dovecot-2.2.33.2/0002755000175000017500000000000013172375614010420 500000000000000dovecot-2.2.33.2/configure0000755000175000017500000332614313172375571012263 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for Dovecot 2.2.33.2. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: dovecot@dovecot.org about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Dovecot' PACKAGE_TARNAME='dovecot' PACKAGE_VERSION='2.2.33.2' PACKAGE_STRING='Dovecot 2.2.33.2' PACKAGE_BUGREPORT='dovecot@dovecot.org' PACKAGE_URL='' ac_unique_file="src" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS abs_top_builddir RUN_TEST VALGRIND NOPLUGIN_LDFLAGS SETTING_FILES HAVE_APPARMOR_FALSE HAVE_APPARMOR_TRUE APPARMOR_LIBS BUILD_LIBICU_FALSE BUILD_LIBICU_TRUE LIBICU_LIBS LIBICU_CFLAGS BUILD_FTS_EXTTEXTCAT_FALSE BUILD_FTS_EXTTEXTCAT_TRUE BUILD_FTS_TEXTCAT_FALSE BUILD_FTS_TEXTCAT_TRUE LIBEXTTEXTCAT_LIBS LIBEXTTEXTCAT_CFLAGS BUILD_FTS_STEMMER_FALSE BUILD_FTS_STEMMER_TRUE CLUCENE_LIBS CLUCENE_CFLAGS BUILD_SOLR_FALSE BUILD_SOLR_TRUE QUOTA_LIBS HAVE_RQUOTA_FALSE HAVE_RQUOTA_TRUE RPCGEN BUILD_ZLIB_PLUGIN_FALSE BUILD_ZLIB_PLUGIN_TRUE COMPRESS_LIBS SQL_PLUGINS_FALSE SQL_PLUGINS_TRUE BUILD_CASSANDRA_FALSE BUILD_CASSANDRA_TRUE BUILD_SQLITE_FALSE BUILD_SQLITE_TRUE BUILD_MYSQL_FALSE BUILD_MYSQL_TRUE BUILD_PGSQL_FALSE BUILD_PGSQL_TRUE sql_drivers LIBDOVECOT_LIBFTS_DEPS LIBDOVECOT_LIBFTS LIBDOVECOT_LDAP LIBDOVECOT_LDA LIBDOVECOT_DSYNC LIBDOVECOT_COMPRESS LIBDOVECOT_SQL LIBDOVECOT_LOGIN LIBDOVECOT_STORAGE_DEPS LIBDOVECOT_STORAGE LIBDOVECOT_DEPS LIBDOVECOT_LA_LIBS LIBDOVECOT mailbox_list_drivers LINKED_STORAGE_LDADD LINKED_STORAGE_LIBS dict_drivers CDB_LIBS DICT_LIBS CASSANDRA_LIBS CASSANDRA_CFLAGS SQLITE_LIBS SQLITE_CFLAGS PGSQL_LIBS PGSQL_CFLAGS MYSQL_LIBS MYSQL_CFLAGS SQL_LIBS SQL_CFLAGS AUTH_LIBS AUTH_CFLAGS MODULE_SUFFIX MODULE_LIBS CRYPT_LIBS MYSQL_CONFIG PG_CONFIG HAVE_LDAP_FALSE HAVE_LDAP_TRUE LDAP_PLUGIN_FALSE LDAP_PLUGIN_TRUE LDAP_LIBS GSSAPI_PLUGIN_FALSE GSSAPI_PLUGIN_TRUE KRB5_CFLAGS KRB5_LIBS KRB5CONFIG BUILD_DCRYPT_OPENSSL_FALSE BUILD_DCRYPT_OPENSSL_TRUE BUILD_OPENSSL_FALSE BUILD_OPENSSL_TRUE SSL_VERSION_GE_102_FALSE SSL_VERSION_GE_102_TRUE SSL_LIBS SSL_CFLAGS DOVECOT_PLUGIN_DEPS_FALSE DOVECOT_PLUGIN_DEPS_TRUE TCPWRAPPERS_FALSE TCPWRAPPERS_TRUE LIBWRAP_LIBS LIBCAP LTLIBICONV LIBICONV CXXCPP LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL EGREP GREP am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE ac_ct_CXX CXXFLAGS CXX CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC BUILD_DOCS_FALSE BUILD_DOCS_TRUE moduledir mail_storages HAVE_SYSTEMD_FALSE HAVE_SYSTEMD_TRUE systemdsystemunitdir statedir rundir ssldir BUILD_LUCENE_FALSE BUILD_LUCENE_TRUE BUILD_SHARED_LIBS_FALSE BUILD_SHARED_LIBS_TRUE ACLOCAL_AMFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_maintainer_mode enable_devel_checks enable_asserts with_shared_libs with_mem_align with_ioloop with_notify with_nss with_shadow with_pam with_bsdauth with_gssapi with_sia with_ldap with_vpopmail with_cdb with_sql with_pgsql with_mysql with_sqlite with_cassandra with_lucene with_stemmer with_textcat with_icu with_solr with_zlib with_bzlib with_lzma with_lz4 with_libcap with_libwrap with_ssl with_ssldir with_rundir with_statedir with_systemdsystemunitdir with_gc with_storages with_moduledir with_docs enable_dependency_tracking enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock enable_rpath with_libiconv_prefix enable_largefile with_apparmor ' ac_precious_vars='build_alias host_alias target_alias PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP CXX CXXFLAGS CCC LT_SYS_LIBRARY_PATH CXXCPP SSL_CFLAGS SSL_LIBS CLUCENE_CFLAGS CLUCENE_LIBS LIBEXTTEXTCAT_CFLAGS LIBEXTTEXTCAT_LIBS LIBICU_CFLAGS LIBICU_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Dovecot 2.2.33.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/dovecot] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Dovecot 2.2.33.2:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-devel-checks Enable some extra expensive checks for developers --enable-asserts Enable asserts (default) --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-rpath do not hardcode runtime library paths --disable-largefile omit support for large files Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-shared-libs Link binaries using shared Dovecot libraries (default) --with-mem-align=BYTES Set the memory alignment (default: 8) --with-ioloop=IOLOOP Specify the I/O loop method to use (epoll, kqueue, poll; best for the fastest available; default is best) --with-notify=NOTIFY Specify the file system notification method to use (inotify, kqueue, none; default is detected in the above order) --with-nss Build with NSS module support (auto) --with-shadow Build with shadow password support (auto) --with-pam Build with PAM support (auto) --with-bsdauth Build with BSD authentication support (auto) --with-gssapi=yes|plugin Build with GSSAPI authentication support --with-sia Build with Tru64 SIA support --with-ldap=yes|plugin Build with LDAP support --with-vpopmail Build with vpopmail support (auto) --with-cdb Build with CDB support --with-sql=yes|plugin Build with generic SQL support --with-pgsql Build with PostgreSQL driver support --with-mysql Build with MySQL driver support --with-sqlite Build with SQLite3 driver support --with-cassandra Build with Cassandra driver support --with-lucene Build with CLucene full text search support --with-stemmer Build with libstemmer support (for FTS) (auto) --with-textcat Build with libtextcat support (for FTS) (auto) --with-icu Build with libicu support (for FTS normalization) (auto) --with-solr Build with Solr full text search support --with-zlib Build with zlib compression support (auto) --with-bzlib Build with bzlib compression support (auto) --with-lzma Build with LZMA compression support (auto) --with-lz4 Build with LZ4 compression support (auto) --with-libcap Build with libcap support (Dropping capabilities) (auto) --with-libwrap Build with libwrap, ie. TCP-wrappers --with-ssl=gnutls|openssl Build with GNUTLS or OpenSSL (default) --with-ssldir=DIR SSL base directory for certificates (/etc/ssl) --with-rundir=DIR Runtime data directory (LOCALSTATEDIR/run/dovecot) --with-statedir=DIR Permanent data directory (LOCALSTATEDIR/lib/dovecot) --with-systemdsystemunitdir=DIR Directory for systemd service files (auto=detect) --with-gc Use Boehm garbage collector --with-storages Build with specified mail storage formats (mdbox sdbox maildir mbox cydir imapc pop3c) --with-moduledir=DIR Base directory for dynamically loadable modules --with-docs Install documentation (default) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib --without-libiconv-prefix don't search for libiconv in includedir and libdir --with-apparmor enable apparmor plugin (default=auto) Some influential environment variables: PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor CXX C++ compiler command CXXFLAGS C++ compiler flags LT_SYS_LIBRARY_PATH User-defined run-time library search path. CXXCPP C++ preprocessor SSL_CFLAGS C compiler flags for SSL, overriding pkg-config SSL_LIBS linker flags for SSL, overriding pkg-config CLUCENE_CFLAGS C compiler flags for CLUCENE, overriding pkg-config CLUCENE_LIBS linker flags for CLUCENE, overriding pkg-config LIBEXTTEXTCAT_CFLAGS C compiler flags for LIBEXTTEXTCAT, overriding pkg-config LIBEXTTEXTCAT_LIBS linker flags for LIBEXTTEXTCAT, overriding pkg-config LIBICU_CFLAGS C compiler flags for LIBICU, overriding pkg-config LIBICU_LIBS linker flags for LIBICU, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Dovecot configure 2.2.33.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------- ## ## Report this to dovecot@dovecot.org ## ## ---------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Dovecot $as_me 2.2.33.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat >>confdefs.h <<_ACEOF #define DOVECOT_ABI_VERSION "2.2.ABIv33($PACKAGE_VERSION)" _ACEOF ac_aux_dir= for ac_dir in . "$srcdir"/.; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='dovecot' VERSION='2.2.33.2' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else USE_MAINTAINER_MODE=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 $as_echo "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi ACLOCAL_AMFLAGS='-I $(top_srcdir)' # Check whether --enable-devel-checks was given. if test "${enable_devel_checks+set}" = set; then : enableval=$enable_devel_checks; if test x$enableval = xyes; then $as_echo "#define DEBUG /**/" >>confdefs.h want_devel_checks=yes fi fi # Check whether --enable-asserts was given. if test "${enable_asserts+set}" = set; then : enableval=$enable_asserts; if test x$enableval = xno; then $as_echo "#define DISABLE_ASSERTS /**/" >>confdefs.h fi fi # Check whether --with-shared-libs was given. if test "${with_shared_libs+set}" = set; then : withval=$with_shared_libs; want_shared_libs=$withval else want_shared_libs=yes fi if test "$want_shared_libs" = "yes"; then BUILD_SHARED_LIBS_TRUE= BUILD_SHARED_LIBS_FALSE='#' else BUILD_SHARED_LIBS_TRUE='#' BUILD_SHARED_LIBS_FALSE= fi # Check whether --with-mem-align was given. if test "${with_mem_align+set}" = set; then : withval=$with_mem_align; mem_align=$withval else mem_align=8 fi # Check whether --with-ioloop was given. if test "${with_ioloop+set}" = set; then : withval=$with_ioloop; ioloop=$withval else ioloop=best fi # Check whether --with-notify was given. if test "${with_notify+set}" = set; then : withval=$with_notify; notify=$withval else notify= fi # Check whether --with-nss was given. if test "${with_nss+set}" = set; then : withval=$with_nss; want=want_`echo nss|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-nss=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-nss=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-nss" "$LINENO" 5 else as_fn_error $? "--with-nss: Unknown value: $withval" "$LINENO" 5 fi else want_nss=auto fi # Check whether --with-shadow was given. if test "${with_shadow+set}" = set; then : withval=$with_shadow; want=want_`echo shadow|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-shadow=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-shadow=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-shadow" "$LINENO" 5 else as_fn_error $? "--with-shadow: Unknown value: $withval" "$LINENO" 5 fi else want_shadow=auto fi # Check whether --with-pam was given. if test "${with_pam+set}" = set; then : withval=$with_pam; want=want_`echo pam|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-pam=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-pam=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-pam" "$LINENO" 5 else as_fn_error $? "--with-pam: Unknown value: $withval" "$LINENO" 5 fi else want_pam=auto fi # Check whether --with-bsdauth was given. if test "${with_bsdauth+set}" = set; then : withval=$with_bsdauth; want=want_`echo bsdauth|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-bsdauth=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-bsdauth=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-bsdauth" "$LINENO" 5 else as_fn_error $? "--with-bsdauth: Unknown value: $withval" "$LINENO" 5 fi else want_bsdauth=auto fi # Check whether --with-gssapi was given. if test "${with_gssapi+set}" = set; then : withval=$with_gssapi; want=want_`echo gssapi|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "plugin" = plugin; then eval $want=plugin else as_fn_error $? "--with-gssapi=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-gssapi=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-gssapi" "$LINENO" 5 else as_fn_error $? "--with-gssapi: Unknown value: $withval" "$LINENO" 5 fi else want_gssapi=no fi # Check whether --with-sia was given. if test "${with_sia+set}" = set; then : withval=$with_sia; want=want_`echo sia|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-sia=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-sia=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-sia" "$LINENO" 5 else as_fn_error $? "--with-sia: Unknown value: $withval" "$LINENO" 5 fi else want_sia=no fi # Check whether --with-ldap was given. if test "${with_ldap+set}" = set; then : withval=$with_ldap; want=want_`echo ldap|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "plugin" = plugin; then eval $want=plugin else as_fn_error $? "--with-ldap=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-ldap=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-ldap" "$LINENO" 5 else as_fn_error $? "--with-ldap: Unknown value: $withval" "$LINENO" 5 fi else want_ldap=no fi # Check whether --with-vpopmail was given. if test "${with_vpopmail+set}" = set; then : withval=$with_vpopmail; if test x$withval = xno; then want_vpopmail=no else if test x$withval = xyes || test x$withval = xauto; then vpopmail_home="`echo ~vpopmail`" want_vpopmail=$withval else vpopmail_home="$withval" want_vpopmail=yes fi fi else want_vpopmail=no fi # Berkeley DB support is more or less broken. Disabled for now. #AC_ARG_WITH(db, #AS_HELP_STRING([--with-db], [Build with Berkeley DB support]), # TEST_WITH(db, $withval), # want_db=no) want_db=no # Check whether --with-cdb was given. if test "${with_cdb+set}" = set; then : withval=$with_cdb; want=want_`echo cdb|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-cdb=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-cdb=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-cdb" "$LINENO" 5 else as_fn_error $? "--with-cdb: Unknown value: $withval" "$LINENO" 5 fi else want_cdb=no fi # Check whether --with-sql was given. if test "${with_sql+set}" = set; then : withval=$with_sql; want=want_`echo sql|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "plugin" = plugin; then eval $want=plugin else as_fn_error $? "--with-sql=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-sql=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-sql" "$LINENO" 5 else as_fn_error $? "--with-sql: Unknown value: $withval" "$LINENO" 5 fi else want_sql=no fi # Check whether --with-pgsql was given. if test "${with_pgsql+set}" = set; then : withval=$with_pgsql; want=want_`echo pgsql|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-pgsql=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-pgsql=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-pgsql" "$LINENO" 5 else as_fn_error $? "--with-pgsql: Unknown value: $withval" "$LINENO" 5 fi else want_pgsql=no fi # Check whether --with-mysql was given. if test "${with_mysql+set}" = set; then : withval=$with_mysql; want=want_`echo mysql|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-mysql=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-mysql=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-mysql" "$LINENO" 5 else as_fn_error $? "--with-mysql: Unknown value: $withval" "$LINENO" 5 fi else want_mysql=no fi # Check whether --with-sqlite was given. if test "${with_sqlite+set}" = set; then : withval=$with_sqlite; want=want_`echo sqlite|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-sqlite=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-sqlite=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-sqlite" "$LINENO" 5 else as_fn_error $? "--with-sqlite: Unknown value: $withval" "$LINENO" 5 fi else want_sqlite=no fi # Check whether --with-cassandra was given. if test "${with_cassandra+set}" = set; then : withval=$with_cassandra; want=want_`echo cassandra|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-cassandra=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-cassandra=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-cassandra" "$LINENO" 5 else as_fn_error $? "--with-cassandra: Unknown value: $withval" "$LINENO" 5 fi else want_cassandra=no fi # Check whether --with-lucene was given. if test "${with_lucene+set}" = set; then : withval=$with_lucene; want=want_`echo lucene|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-lucene=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-lucene=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-lucene" "$LINENO" 5 else as_fn_error $? "--with-lucene: Unknown value: $withval" "$LINENO" 5 fi else want_lucene=no fi if test "$want_lucene" = "yes"; then BUILD_LUCENE_TRUE= BUILD_LUCENE_FALSE='#' else BUILD_LUCENE_TRUE='#' BUILD_LUCENE_FALSE= fi # Check whether --with-stemmer was given. if test "${with_stemmer+set}" = set; then : withval=$with_stemmer; want=want_`echo stemmer|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-stemmer=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-stemmer=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-stemmer" "$LINENO" 5 else as_fn_error $? "--with-stemmer: Unknown value: $withval" "$LINENO" 5 fi else want_stemmer=auto fi # Check whether --with-textcat was given. if test "${with_textcat+set}" = set; then : withval=$with_textcat; want=want_`echo textcat|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-textcat=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-textcat=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-textcat" "$LINENO" 5 else as_fn_error $? "--with-textcat: Unknown value: $withval" "$LINENO" 5 fi else want_textcat=auto fi # Check whether --with-icu was given. if test "${with_icu+set}" = set; then : withval=$with_icu; want_icu=$withval else want_icu=auto fi # Check whether --with-solr was given. if test "${with_solr+set}" = set; then : withval=$with_solr; want=want_`echo solr|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-solr=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-solr=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-solr" "$LINENO" 5 else as_fn_error $? "--with-solr: Unknown value: $withval" "$LINENO" 5 fi else want_solr=no fi # Check whether --with-zlib was given. if test "${with_zlib+set}" = set; then : withval=$with_zlib; want=want_`echo zlib|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-zlib=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-zlib=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-zlib" "$LINENO" 5 else as_fn_error $? "--with-zlib: Unknown value: $withval" "$LINENO" 5 fi else want_zlib=auto fi # Check whether --with-bzlib was given. if test "${with_bzlib+set}" = set; then : withval=$with_bzlib; want=want_`echo bzlib|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-bzlib=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-bzlib=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-bzlib" "$LINENO" 5 else as_fn_error $? "--with-bzlib: Unknown value: $withval" "$LINENO" 5 fi else want_bzlib=auto fi # Check whether --with-lzma was given. if test "${with_lzma+set}" = set; then : withval=$with_lzma; want=want_`echo lzma|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-lzma=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-lzma=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-lzma" "$LINENO" 5 else as_fn_error $? "--with-lzma: Unknown value: $withval" "$LINENO" 5 fi else want_lzma=auto fi # Check whether --with-lz4 was given. if test "${with_lz4+set}" = set; then : withval=$with_lz4; want=want_`echo lz4|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-lz4=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-lz4=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-lz4" "$LINENO" 5 else as_fn_error $? "--with-lz4: Unknown value: $withval" "$LINENO" 5 fi else want_lz4=auto fi # Check whether --with-libcap was given. if test "${with_libcap+set}" = set; then : withval=$with_libcap; want=want_`echo libcap|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-libcap=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-libcap=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-libcap" "$LINENO" 5 else as_fn_error $? "--with-libcap: Unknown value: $withval" "$LINENO" 5 fi else want_libcap=auto fi # Check whether --with-libwrap was given. if test "${with_libwrap+set}" = set; then : withval=$with_libwrap; want=want_`echo libwrap|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-libwrap=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-libwrap=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-libwrap" "$LINENO" 5 else as_fn_error $? "--with-libwrap: Unknown value: $withval" "$LINENO" 5 fi else want_libwrap=no fi # Check whether --with-ssl was given. if test "${with_ssl+set}" = set; then : withval=$with_ssl; if test x$withval = xno; then want_gnutls=no want_openssl=no elif test x$withval = xgnutls; then as_fn_error $? "GNUTLS support is broken currently" "$LINENO" 5 want_gnutls=yes want_openssl=no elif test x$withval = xopenssl; then want_gnutls=no want_openssl=yes elif test x$withval = xyes; then want_gnutls=no want_openssl=yes else as_fn_error $? "--with-ssl: Invalid value: $withval" "$LINENO" 5 fi else want_gnutls=no want_openssl=auto fi # Check whether --with-ssldir was given. if test "${with_ssldir+set}" = set; then : withval=$with_ssldir; ssldir="$withval" else ssldir=/etc/ssl fi # Check whether --with-rundir was given. if test "${with_rundir+set}" = set; then : withval=$with_rundir; rundir="$withval" else rundir=$localstatedir/run/$PACKAGE fi # Check whether --with-statedir was given. if test "${with_statedir+set}" = set; then : withval=$with_statedir; statedir="$withval" else statedir=$localstatedir/lib/$PACKAGE fi # Check whether --with-systemdsystemunitdir was given. if test "${with_systemdsystemunitdir+set}" = set; then : withval=$with_systemdsystemunitdir; if test "$withval" = "auto"; then systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd` elif test "$withval" != "no"; then systemdsystemunitdir=$withval fi fi if test "$systemdsystemunitdir" != ""; then $as_echo "#define HAVE_SYSTEMD /**/" >>confdefs.h fi if test "$systemdsystemunitdir" != ""; then HAVE_SYSTEMD_TRUE= HAVE_SYSTEMD_FALSE='#' else HAVE_SYSTEMD_TRUE='#' HAVE_SYSTEMD_FALSE= fi # Check whether --with-gc was given. if test "${with_gc+set}" = set; then : withval=$with_gc; want=want_`echo gc|sed s/-/_/g` if test $withval = yes || test $withval = no || test $withval = auto; then eval $want=$withval elif test $withval = plugin; then if test "" = plugin; then eval $want=plugin else as_fn_error $? "--with-gc=plugin not supported" "$LINENO" 5 fi elif `echo $withval|grep '^/' >/dev/null`; then as_fn_error $? "--with-gc=path not supported. You may want to use instead: CPPFLAGS=-I$withval/include LDFLAGS=-L$withval/lib ./configure --with-gc" "$LINENO" 5 else as_fn_error $? "--with-gc: Unknown value: $withval" "$LINENO" 5 fi else want_gc=no fi # Check whether --with-storages was given. if test "${with_storages+set}" = set; then : withval=$with_storages; if test "$withval" = "yes" || test "$withval" = "no"; then as_fn_error $? "--with-storages needs storage list as parameter" "$LINENO" 5 fi mail_storages="shared `echo "$withval"|sed 's/,/ /g'`" else mail_storages="shared mdbox sdbox maildir mbox cydir imapc pop3c" fi mail_storages="$mail_storages raw fail" # drop duplicates duplicates=`(for i in $mail_storages; do echo $i; done)|sort|uniq -d|xargs echo` if test "$duplicates" != ""; then as_fn_error $? "Duplicate --with-storages: $duplicates" "$LINENO" 5 fi # Check whether --with-moduledir was given. if test "${with_moduledir+set}" = set; then : withval=$with_moduledir; moduledir="$withval" else moduledir=$libdir/dovecot fi # Check whether --with-docs was given. if test "${with_docs+set}" = set; then : withval=$with_docs; if test x$withval = xno; then want_docs=no else want_docs=yes fi else want_docs=yes fi if test "$want_docs" = "yes"; then BUILD_DOCS_TRUE= BUILD_DOCS_FALSE='#' else BUILD_DOCS_TRUE='#' BUILD_DOCS_FALSE= fi want_passwd=yes want_passwd_file=yes want_checkpassword=yes want_prefetch_userdb=yes DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5 $as_echo_n "checking for library containing strerror... " >&6; } if ${ac_cv_search_strerror+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char strerror (); int main () { return strerror (); ; return 0; } _ACEOF for ac_lib in '' cposix; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_strerror=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_strerror+:} false; then : break fi done if ${ac_cv_search_strerror+:} false; then : else ac_cv_search_strerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strerror" >&5 $as_echo "$ac_cv_search_strerror" >&6; } ac_res=$ac_cv_search_strerror if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi # lucene plugin needs this { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if ${ac_cv_c_inline+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6' macro_revision='2.4.6' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done func_stripname_cnf () { case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; esac } # func_stripname_cnf # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC func_cc_basename $compiler cc_basename=$func_cc_basename_result if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec_CXX='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' $wl-bernotok' allow_undefined_flag_CXX=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='$wl--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" if test yes != "$lt_cv_apple_cc_single_mod"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi else ld_shlibs_CXX=no fi ;; os2*) hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_minus_L_CXX=yes allow_undefined_flag_CXX=unsupported shrext_cmds=.dll archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes_CXX=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='$wl-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='$wl-E' whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then no_undefined_flag_CXX=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='$wl-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='$wl-z,text' allow_undefined_flag_CXX='$wl-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no GCC_CXX=$GXX LD_CXX=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX=$prev$p else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX=$prev$p else postdeps_CXX="${postdeps_CXX} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$predep_objects_CXX"; then predep_objects_CXX=$p else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX=$p else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi lt_prog_compiler_pic_CXX='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static_CXX='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs_CXX=no ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 $as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec_CXX='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test yes = "$hardcode_automatic_CXX"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct_CXX" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && test no != "$hardcode_minus_L_CXX"; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test relink = "$hardcode_action_CXX" || test yes = "$inherit_rpath_CXX"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_commands="$ac_config_commands libtool" # Only expand once: if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which # contains only /bin. Note that ksh looks also at the FPATH variable, # so we have to set that as well for the test. PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ || PATH_SEPARATOR=';' } fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`echo "$ac_prog"| sed 's%\\\\%/%g'` while echo "$ac_prog" | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${acl_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$acl_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${acl_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$acl_cv_prog_gnu_ld" >&6; } with_gnu_ld=$acl_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5 $as_echo_n "checking for shared library run path origin... " >&6; } if ${acl_cv_rpath+:} false; then : $as_echo_n "(cached) " >&6 else CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5 $as_echo "$acl_cv_rpath" >&6; } wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" # Check whether --enable-rpath was given. if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; : else enable_rpath=yes fi acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit host" >&5 $as_echo_n "checking for 64-bit host... " >&6; } if ${gl_cv_solaris_64bit+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef _LP64 sixtyfour bits #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "sixtyfour bits" >/dev/null 2>&1; then : gl_cv_solaris_64bit=yes else gl_cv_solaris_64bit=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_solaris_64bit" >&5 $as_echo "$gl_cv_solaris_64bit" >&6; } if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; */../ | */.. ) # Better ignore directories of this form. They are misleading. ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" use_additional=yes acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" # Check whether --with-libiconv-prefix was given. if test "${with_libiconv_prefix+set}" = set; then : withval=$with_libiconv_prefix; if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" if test "$acl_libdirstem2" != "$acl_libdirstem" \ && ! test -d "$withval/$acl_libdirstem"; then additional_libdir="$withval/$acl_libdirstem2" fi fi fi fi LIBICONV= LTLIBICONV= INCICONV= LIBICONV_PREFIX= HAVE_LIBICONV= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='iconv ' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value" else : fi else found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then dir="$additional_libdir" if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi if test "$acl_hardcode_direct" = yes; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a" else LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name" fi fi additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = 'iconv'; then LIBICONV_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = 'iconv'; then LIBICONV_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INCICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" fi fi fi fi fi if test -n "$found_la"; then save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then haveit= if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) LIBICONV="${LIBICONV}${LIBICONV:+ }$dep" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep" ;; esac done fi else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" else for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then for found_dir in $ltrpathdirs; do LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir" done fi am_save_CPPFLAGS="$CPPFLAGS" for element in $INCICONV; do haveit= for x in $CPPFLAGS; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5 $as_echo_n "checking for iconv... " >&6; } if ${am_cv_func_iconv+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_lib_iconv=yes am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$am_save_LIBS" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5 $as_echo "$am_cv_func_iconv" >&6; } if test "$am_cv_func_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working iconv" >&5 $as_echo_n "checking for working iconv... " >&6; } if ${am_cv_func_iconv_works+:} false; then : $as_echo_n "(cached) " >&6 else am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi am_cv_func_iconv_works=no for ac_iconv_const in '' 'const'; do if test "$cross_compiling" = yes; then : case "$host_os" in aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; *) am_cv_func_iconv_works="guessing yes" ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef ICONV_CONST # define ICONV_CONST $ac_iconv_const #endif int main () { int result = 0; /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 1; iconv_close (cd_utf8_to_88591); } } /* Test against Solaris 10 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); if (cd_ascii_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\263"; char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_ascii_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 2; iconv_close (cd_ascii_to_88591); } } /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ { iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304"; static char buf[2] = { (char)0xDE, (char)0xAD }; ICONV_CONST char *inptr = input; size_t inbytesleft = 1; char *outptr = buf; size_t outbytesleft = 1; size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) result |= 4; iconv_close (cd_88591_to_utf8); } } #if 0 /* This bug could be worked around by the caller. */ /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ { iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) result |= 8; iconv_close (cd_88591_to_utf8); } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ if (/* Try standardized names. */ iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) /* Try IRIX, OSF/1 names. */ && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) /* Try AIX names. */ && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) /* Try HP-UX names. */ && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) result |= 16; return result; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : am_cv_func_iconv_works=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi test "$am_cv_func_iconv_works" = no || break done LIBS="$am_save_LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv_works" >&5 $as_echo "$am_cv_func_iconv_works" >&6; } case "$am_cv_func_iconv_works" in *no) am_func_iconv=no am_cv_lib_iconv=no ;; *) am_func_iconv=yes ;; esac else am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then $as_echo "#define HAVE_ICONV 1" >>confdefs.h fi if test "$am_cv_lib_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5 $as_echo_n "checking how to link with libiconv... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5 $as_echo "$LIBICONV" >&6; } else CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi if test "$am_cv_func_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5 $as_echo_n "checking for iconv declaration... " >&6; } if ${am_cv_proto_iconv+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_cv_proto_iconv_arg1="" else am_cv_proto_iconv_arg1="const" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);" fi am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_proto_iconv" >&5 $as_echo " $am_cv_proto_iconv" >&6; } cat >>confdefs.h <<_ACEOF #define ICONV_CONST $am_cv_proto_iconv_arg1 _ACEOF fi # SIZE_MAX is missing without this CXXFLAGS="$CXXFLAGS -D__STDC_LIMIT_MACROS" cat >>confdefs.h <<_ACEOF #define DOVECOT_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define DOVECOT_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define DOVECOT_VERSION "$PACKAGE_VERSION" _ACEOF $as_echo "#define DOVECOT_VERSION_MAJOR 2" >>confdefs.h $as_echo "#define DOVECOT_VERSION_MINOR 2" >>confdefs.h for ac_header in strings.h stdint.h unistd.h dirent.h malloc.h inttypes.h \ sys/uio.h sys/sysmacros.h sys/resource.h sys/select.h libgen.h \ sys/quota.h sys/fs/ufs_quota.h ufs/ufs/quota.h jfs/quota.h \ quota.h sys/fs/quota_common.h \ mntent.h sys/mnttab.h sys/event.h sys/time.h sys/mkdev.h linux/dqblk_xfs.h \ xfs/xqm.h execinfo.h ucontext.h malloc_np.h sys/utsname.h sys/vmount.h \ sys/utsname.h glob.h linux/falloc.h ucred.h sys/ucred.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done have_clang=no if $CC -dM -E -x c /dev/null | grep __clang__ > /dev/null 2>&1; then have_clang=yes fi if test "x$ac_cv_c_compiler_gnu" = "xyes"; then # -Wcast-qual -Wcast-align -Wconversion -Wunreachable-code # too many warnings # -Wstrict-prototypes -Wredundant-decls # may give warnings in some systems # -Wmissing-format-attribute -Wmissing-noreturn -Wwrite-strings # a couple of warnings CFLAGS="$CFLAGS -Wall -W -Wmissing-prototypes -Wmissing-declarations -Wpointer-arith -Wchar-subscripts -Wformat=2 -Wbad-function-cast" if test "$have_clang" = "yes"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 3) # error new clang #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else # clang 3.3+ unfortunately this gives warnings with hash.h CFLAGS="$CFLAGS -Wno-duplicate-decl-specifier" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else # This is simply to avoid warning when building strftime() wrappers.. CFLAGS="$CFLAGS -fno-builtin-strftime" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if __GNUC__ < 4 # error old gcc #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # gcc4 CFLAGS="$CFLAGS -Wstrict-aliasing=2" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Use std=gnu99 if we have new enough gcc old_cflags=$CFLAGS CFLAGS="-std=gnu99" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : CFLAGS="$CFLAGS $old_cflags" else CFLAGS="$old_cflags" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "$have_clang" = "yes"; then # clang specific options if test "$want_devel_checks" = "yes"; then # FIXME: enable once md[45], sha[12] can be compiled without #CFLAGS="$CFLAGS -fsanitize=integer,undefined -ftrapv" : fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 $as_echo_n "checking for library containing socket... " >&6; } if ${ac_cv_search_socket+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF for ac_lib in '' socket; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_socket=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_socket+:} false; then : break fi done if ${ac_cv_search_socket+:} false; then : else ac_cv_search_socket=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 $as_echo "$ac_cv_search_socket" >&6; } ac_res=$ac_cv_search_socket if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inet_addr" >&5 $as_echo_n "checking for library containing inet_addr... " >&6; } if ${ac_cv_search_inet_addr+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_addr (); int main () { return inet_addr (); ; return 0; } _ACEOF for ac_lib in '' nsl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_inet_addr=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_inet_addr+:} false; then : break fi done if ${ac_cv_search_inet_addr+:} false; then : else ac_cv_search_inet_addr=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_addr" >&5 $as_echo "$ac_cv_search_inet_addr" >&6; } ac_res=$ac_cv_search_inet_addr if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing fdatasync" >&5 $as_echo_n "checking for library containing fdatasync... " >&6; } if ${ac_cv_search_fdatasync+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char fdatasync (); int main () { return fdatasync (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_fdatasync=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_fdatasync+:} false; then : break fi done if ${ac_cv_search_fdatasync+:} false; then : else ac_cv_search_fdatasync=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_fdatasync" >&5 $as_echo "$ac_cv_search_fdatasync" >&6; } ac_res=$ac_cv_search_fdatasync if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_FDATASYNC /**/" >>confdefs.h fi if test $want_libcap != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_init in -lcap" >&5 $as_echo_n "checking for cap_init in -lcap... " >&6; } if ${ac_cv_lib_cap_cap_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cap_init (); int main () { return cap_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cap_cap_init=yes else ac_cv_lib_cap_cap_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_init" >&5 $as_echo "$ac_cv_lib_cap_cap_init" >&6; } if test "x$ac_cv_lib_cap_cap_init" = xyes; then : $as_echo "#define HAVE_LIBCAP /**/" >>confdefs.h LIBCAP="-lcap" else if test "$want_libcap" = "yes"; then as_fn_error $? "Can't build with libcap support: libcap not found" "$LINENO" 5 fi fi fi have_libwrap=no if test $want_libwrap != no; then ac_fn_c_check_header_mongrel "$LINENO" "tcpd.h" "ac_cv_header_tcpd_h" "$ac_includes_default" if test "x$ac_cv_header_tcpd_h" = xyes; then : old_LIBS=$LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we have libwrap" >&5 $as_echo_n "checking whether we have libwrap... " >&6; } if ${i_cv_have_libwrap+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int allow_severity = 0; int deny_severity = 0; int main () { request_init((void *)0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_libwrap=yes else i_cv_have_libwrap=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_libwrap" >&5 $as_echo "$i_cv_have_libwrap" >&6; } if test $i_cv_have_libwrap = yes; then $as_echo "#define HAVE_LIBWRAP /**/" >>confdefs.h LIBWRAP_LIBS=-lwrap have_libwrap=yes else if test "$want_libwrap" = "yes"; then as_fn_error $? "Can't build with libwrap support: libwrap not found" "$LINENO" 5 fi fi LIBS=$old_LIBS else if test "$want_libwrap" = "yes"; then as_fn_error $? "Can't build with libwrap support: tcpd.h not found" "$LINENO" 5 fi fi fi if test "$have_libwrap" = "yes"; then TCPWRAPPERS_TRUE= TCPWRAPPERS_FALSE='#' else TCPWRAPPERS_TRUE='#' TCPWRAPPERS_FALSE= fi $as_echo "#define PACKAGE_WEBPAGE \"http://www.dovecot.org/\"" >>confdefs.h for ac_func in fcntl flock lockf inet_aton sigaction getpagesize madvise \ strcasecmp stricmp vsyslog writev pread uname unsetenv \ setrlimit setproctitle seteuid setreuid setegid setresgid \ getmntinfo setpriority quotactl getmntent kqueue kevent \ backtrace_symbols walkcontext dirfd clearenv \ malloc_usable_size glob fallocate posix_fadvise \ getpeereid getpeerucred inotify_init timegm do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_type "$LINENO" "struct sockpeercred" "ac_cv_type_struct_sockpeercred" " #include #include " if test "x$ac_cv_type_struct_sockpeercred" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_SOCKPEERCRED 1 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_CLOCK_GETTIME /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5 $as_echo_n "checking for typeof... " >&6; } if ${i_cv_have_typeof+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int foo; typeof(foo) bar; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_typeof=yes else i_cv_have_typeof=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_typeof" >&5 $as_echo "$i_cv_have_typeof" >&6; } if test $i_cv_have_typeof = yes; then $as_echo "#define HAVE_TYPEOF /**/" >>confdefs.h fi have_ioloop=no if test "$ioloop" = "best" || test "$ioloop" = "epoll"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can use epoll" >&5 $as_echo_n "checking whether we can use epoll... " >&6; } if ${i_cv_epoll_works+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main() { return epoll_create(5) < 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_epoll_works=yes else i_cv_epoll_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_epoll_works" >&5 $as_echo "$i_cv_epoll_works" >&6; } if test $i_cv_epoll_works = yes; then $as_echo "#define IOLOOP_EPOLL /**/" >>confdefs.h have_ioloop=yes ioloop=epoll else if test "$ioloop" = "epoll" ; then as_fn_error $? "epoll ioloop requested but epoll_create() is not available" "$LINENO" 5 fi fi fi if test "$ioloop" = "best" || test "$ioloop" = "kqueue"; then if test "$ac_cv_func_kqueue" = yes && test "$ac_cv_func_kevent" = yes; then $as_echo "#define IOLOOP_KQUEUE /**/" >>confdefs.h ioloop=kqueue have_ioloop=yes elif test "$ioloop" = "kqueue"; then as_fn_error $? "kqueue ioloop requested but kqueue() is not available" "$LINENO" 5 fi fi if test "$ioloop" = "best" || test "$ioloop" = "poll"; then ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll" if test "x$ac_cv_func_poll" = xyes; then : $as_echo "#define IOLOOP_POLL /**/" >>confdefs.h ioloop=poll have_ioloop=yes fi fi if test "$have_ioloop" = "no"; then $as_echo "#define IOLOOP_SELECT /**/" >>confdefs.h ioloop="select" fi have_notify=none if test "$notify" = "" || test "$notify" = "inotify" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can use inotify" >&5 $as_echo_n "checking whether we can use inotify... " >&6; } if test "$ac_cv_func_inotify_init" = yes; then have_notify=inotify notify=inotify { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"yes\"" >&5 $as_echo "\"yes\"" >&6; } $as_echo "#define IOLOOP_NOTIFY_INOTIFY /**/" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"no\"" >&5 $as_echo "\"no\"" >&6; } if test "$notify" = "inotify"; then as_fn_error $? "inotify requested but not available" "$LINENO" 5 notify="" fi fi fi if (test "$notify" = "" && test "$ioloop" = kqueue) || test "$notify" = "kqueue"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can use BSD kqueue() notify" >&5 $as_echo_n "checking whether we can use BSD kqueue() notify... " >&6; } if test "$ac_cv_func_kqueue" = yes && test "$ac_cv_func_kevent" = yes ; then have_notify=kqueue notify=kqueue { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"yes\"" >&5 $as_echo "\"yes\"" >&6; } $as_echo "#define IOLOOP_NOTIFY_KQUEUE /**/" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"no\"" >&5 $as_echo "\"no\"" >&6; } if test "$notify" = "kqueue" ; then as_fn_error $? "kqueue notify requested but kqueue() is not available" "$LINENO" 5 notify="" fi fi fi if test "$have_notify" = "none"; then $as_echo "#define IOLOOP_NOTIFY_NONE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we have glibc" >&5 $as_echo_n "checking whether we have glibc... " >&6; } if ${i_cv_have_glibc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef __GLIBC__ we have glibc #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_glibc=no else i_cv_have_glibc=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_glibc" >&5 $as_echo "$i_cv_have_glibc" >&6; } if test "$i_cv_have_glibc" = "yes"; then $as_echo "#define PREAD_WRAPPERS /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether posix_fallocate() works" >&5 $as_echo_n "checking whether posix_fallocate() works... " >&6; } if ${i_cv_posix_fallocate_works+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 600 #include #include #include #include #if defined(__GLIBC__) && (__GLIBC__ < 2 || __GLIBC_MINOR__ < 7) possibly broken posix_fallocate #endif int main() { int fd = creat("conftest.temp", 0600); int ret; if (fd == -1) { perror("creat()"); return 2; } ret = posix_fallocate(fd, 1024, 1024) < 0 ? 1 : 0; unlink("conftest.temp"); return ret; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_posix_fallocate_works=yes else i_cv_posix_fallocate_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_posix_fallocate_works" >&5 $as_echo "$i_cv_posix_fallocate_works" >&6; } if test $i_cv_posix_fallocate_works = yes; then $as_echo "#define HAVE_POSIX_FALLOCATE /**/" >>confdefs.h fi _plugin_deps=yes { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OS supports plugin dependencies" >&5 $as_echo_n "checking whether OS supports plugin dependencies... " >&6; } case "$host_os" in darwin*) # OSX loads the plugins twice, which breaks stuff _plugin_deps=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_plugin_deps" >&5 $as_echo "$_plugin_deps" >&6; } if test "x$_plugin_deps" = "xyes"; then DOVECOT_PLUGIN_DEPS_TRUE= DOVECOT_PLUGIN_DEPS_FALSE='#' else DOVECOT_PLUGIN_DEPS_TRUE='#' DOVECOT_PLUGIN_DEPS_FALSE= fi unset _plugin_deps case "$host_os" in hpux*) CFLAGS="$CFLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" # for getting fd_send/fd_recv working: LDFLAGS="$LDFLAGS -Wl,+b,:" LIBS="-lxnet $LIBS" $as_echo "#define PREAD_BROKEN /**/" >>confdefs.h ;; linux*|darwin*) $as_echo "#define PROCTITLE_HACK /**/" >>confdefs.h ;; *) ;; esac # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 $as_echo_n "checking size of int... " >&6; } if ${ac_cv_sizeof_int+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : else if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_int=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 $as_echo "$ac_cv_sizeof_int" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_INT $ac_cv_sizeof_int _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 $as_echo_n "checking size of long... " >&6; } if ${ac_cv_sizeof_long+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 $as_echo "$ac_cv_sizeof_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG $ac_cv_sizeof_long _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 $as_echo_n "checking size of void *... " >&6; } if ${ac_cv_sizeof_void_p+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : else if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void *) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_void_p=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 $as_echo "$ac_cv_sizeof_void_p" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_VOID_P $ac_cv_sizeof_void_p _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 $as_echo_n "checking size of long long... " >&6; } if ${ac_cv_sizeof_long_long+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long long) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5 $as_echo "$ac_cv_sizeof_long_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long _ACEOF # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" if test "x$ac_cv_type__Bool" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE__BOOL 1 _ACEOF fi ac_fn_c_check_type "$LINENO" "uoff_t" "ac_cv_type_uoff_t" "$ac_includes_default" if test "x$ac_cv_type_uoff_t" = xyes; then : have_uoff_t=yes $as_echo "#define HAVE_UOFF_T /**/" >>confdefs.h else have_uoff_t=no fi order="long int long-long" if test "long int long-long" = ""; then order="int long long-long" fi result="" visible="unknown" { $as_echo "$as_me:${as_lineno-$LINENO}: checking type of off_t" >&5 $as_echo_n "checking type of off_t... " >&6; } if ${i_cv_typeof_off_t+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$ac_cv_c_compiler_gnu" = "xyes"; then old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" for type in $order; do case "$type" in int) fmt="%d" ;; unsigned-int) fmt="%u" ;; long) fmt="%ld" ;; unsigned-long) fmt="%lu" ;; long-long) fmt="%lld" ;; unsigned-long-long) fmt="%llu" ;; *) fmt="" ;; esac if test "$fmt" != ""; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { printf("$fmt", (off_t)0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test "$result" != ""; then result="" visible="unknown" break fi result="`echo $type|sed 's/-/ /g'`" visible="$result" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi done CFLAGS="$old_CFLAGS" fi if test "$result" = ""; then for type in $order; do type="`echo $type|sed 's/-/ /g'`" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include typedef $type off_t; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test "$result" != ""; then result="" visible="unknown" break fi result="$type" visible="$type" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done fi if test "$result" = ""; then if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main() { FILE *f=fopen("conftestval", "w"); if (!f) exit(1); fprintf(f, "%d\n", sizeof(off_t)); exit(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : size=`cat conftestval` rm -f conftestval for type in $order; do actype="ac_cv_sizeof_`echo $type|sed 's/-/_/g'`" if test "$size" = "`eval echo \\$$actype`"; then result="`echo $type|sed 's/-/ /g'`" visible="`expr $size \* 8`bit (using $result)" break fi done if test "$result" = ""; then result=unknown visible="`expr $size \* 8`bit (unknown type)" fi fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi i_cv_typeof_off_t=$result/$visible fi typeof_off_t=`echo $i_cv_typeof_off_t | sed s,/.*$,,` visible=`echo $i_cv_typeof_off_t | sed s,^.*/,,` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $visible" >&5 $as_echo "$visible" >&6; } case "$typeof_off_t" in int) offt_max=INT_MAX uofft_fmt="u" if test "$have_uoff_t" != "yes"; then $as_echo "#define UOFF_T_INT /**/" >>confdefs.h fi offt_bits=`expr 8 \* $ac_cv_sizeof_int` ;; long) offt_max=LONG_MAX uofft_fmt="lu" if test "$have_uoff_t" != "yes"; then $as_echo "#define UOFF_T_LONG /**/" >>confdefs.h fi offt_bits=`expr 8 \* $ac_cv_sizeof_long` ;; "long long") offt_max=LLONG_MAX uofft_fmt="llu" if test "$have_uoff_t" != "yes"; then $as_echo "#define UOFF_T_LONG_LONG /**/" >>confdefs.h fi offt_bits=`expr 8 \* $ac_cv_sizeof_long_long` ;; *) as_fn_error $? "Unsupported off_t type" "$LINENO" 5 ;; esac cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct dirent d; d.d_type = DT_DIR; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define HAVE_DIRENT_D_TYPE /**/" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { off_t i = OFF_T_MAX; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : : else cat >>confdefs.h <<_ACEOF #define OFF_T_MAX $offt_max _ACEOF fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat >>confdefs.h <<_ACEOF #define PRIuUOFF_T "$uofft_fmt" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t is signed" >&5 $as_echo_n "checking whether size_t is signed... " >&6; } if ${i_cv_signed_size_t+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main() { /* return 0 if we're signed */ exit((size_t)(int)-1 <= 0 ? 0 : 1); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_signed_size_t=yes echo echo "Your system's size_t is a signed integer, Dovecot isn't designed to" echo "support it. It probably works just fine, but it's less resistant to" echo "buffer overflows. If you're not worried about this and still want to" echo "compile Dovecot, set ignore_signed_size=1 environment." if test "$ignore_signed_size" = ""; then as_fn_error $? "aborting" "$LINENO" 5 fi echo "..ignoring as requested.." else i_cv_signed_size_t=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_signed_size_t" >&5 $as_echo "$i_cv_signed_size_t" >&6; } order="unsigned-int unsigned-long unsigned-long-long" if test "unsigned-int unsigned-long unsigned-long-long" = ""; then order="int long long-long" fi result="" visible="unknown" { $as_echo "$as_me:${as_lineno-$LINENO}: checking type of size_t" >&5 $as_echo_n "checking type of size_t... " >&6; } if ${i_cv_typeof_size_t+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$ac_cv_c_compiler_gnu" = "xyes"; then old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" for type in $order; do case "$type" in int) fmt="%d" ;; unsigned-int) fmt="%u" ;; long) fmt="%ld" ;; unsigned-long) fmt="%lu" ;; long-long) fmt="%lld" ;; unsigned-long-long) fmt="%llu" ;; *) fmt="" ;; esac if test "$fmt" != ""; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { printf("$fmt", (size_t)0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test "$result" != ""; then result="" visible="unknown" break fi result="`echo $type|sed 's/-/ /g'`" visible="$result" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi done CFLAGS="$old_CFLAGS" fi if test "$result" = ""; then for type in $order; do type="`echo $type|sed 's/-/ /g'`" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include typedef $type size_t; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test "$result" != ""; then result="" visible="unknown" break fi result="$type" visible="$type" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done fi if test "$result" = ""; then if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main() { FILE *f=fopen("conftestval", "w"); if (!f) exit(1); fprintf(f, "%d\n", sizeof(size_t)); exit(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : size=`cat conftestval` rm -f conftestval for type in $order; do actype="ac_cv_sizeof_`echo $type|sed 's/-/_/g'`" if test "$size" = "`eval echo \\$$actype`"; then result="`echo $type|sed 's/-/ /g'`" visible="`expr $size \* 8`bit (using $result)" break fi done if test "$result" = ""; then result=unknown visible="`expr $size \* 8`bit (unknown type)" fi fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi i_cv_typeof_size_t=$result/$visible fi typeof_size_t=`echo $i_cv_typeof_size_t | sed s,/.*$,,` visible=`echo $i_cv_typeof_size_t | sed s,^.*/,,` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $visible" >&5 $as_echo "$visible" >&6; } case "$typeof_size_t" in "unsigned long") ssizet_max=LONG_MAX sizet_fmt="lu" ;; "unsigned long long") ssizet_max=LLONG_MAX sizet_fmt="llu" ;; *) ssizet_max=INT_MAX sizet_fmt="u" if test "$typeof_size_t" = ""; then $as_echo "#define size_t unsigned int" >>confdefs.h $as_echo "#define ssize_t int" >>confdefs.h fi ;; esac cat >>confdefs.h <<_ACEOF #define SSIZE_T_MAX $ssizet_max _ACEOF cat >>confdefs.h <<_ACEOF #define PRIuSIZE_T "$sizet_fmt" _ACEOF ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" if test "x$ac_cv_header_stdint_h" = xyes; then : stdint_include="#include " fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uintmax_t" >&5 $as_echo_n "checking for uintmax_t... " >&6; } if ${i_cv_type_uintmax_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include $stdint_include int main () { uintmax_t t; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_type_uintmax_t=yes else i_cv_type_uintmax_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_type_uintmax_t" >&5 $as_echo "$i_cv_type_uintmax_t" >&6; } if test $i_cv_type_uintmax_t = yes; then $as_echo "#define HAVE_UINTMAX_T /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint_fast32_t" >&5 $as_echo_n "checking for uint_fast32_t... " >&6; } if ${i_cv_type_uint_fast32_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include $stdint_include int main () { uint_fast32_t t; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_type_uint_fast32_t=yes else i_cv_type_uint_fast32_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_type_uint_fast32_t" >&5 $as_echo "$i_cv_type_uint_fast32_t" >&6; } if test $i_cv_type_uint_fast32_t = yes; then $as_echo "#define HAVE_UINT_FAST32_T /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t" >&5 $as_echo_n "checking for socklen_t... " >&6; } if ${i_cv_type_socklen_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { socklen_t t; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_type_socklen_t=yes else i_cv_type_socklen_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_type_socklen_t" >&5 $as_echo "$i_cv_type_socklen_t" >&6; } if test $i_cv_type_socklen_t = yes; then $as_echo "#define HAVE_SOCKLEN_T /**/" >>confdefs.h fi cat >>confdefs.h <<_ACEOF #define MEM_ALIGN_SIZE $mem_align _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /dev/urandom" >&5 $as_echo_n "checking for /dev/urandom... " >&6; } if test -c /dev/urandom || test -s /dev/urandom; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define DEV_URANDOM_PATH \"/dev/urandom\"" >>confdefs.h have_random_source=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "$have_random_source" != "yes"; then ac_fn_c_check_header_mongrel "$LINENO" "openssl/rand.h" "ac_cv_header_openssl_rand_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_rand_h" = xyes; then : $as_echo "#define HAVE_OPENSSL_RAND_H /**/" >>confdefs.h LIBS="$LIBS -lcrypto" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tm_gmtoff" >&5 $as_echo_n "checking for tm_gmtoff... " >&6; } if ${i_cv_field_tm_gmtoff+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct tm *tm; return tm->tm_gmtoff; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_field_tm_gmtoff=yes else i_cv_field_tm_gmtoff=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $i_cv_field_tm_gmtoff = yes; then $as_echo "#define HAVE_TM_GMTOFF /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_field_tm_gmtoff" >&5 $as_echo "$i_cv_field_tm_gmtoff" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how large time_t values gmtime() accepts" >&5 $as_echo_n "checking how large time_t values gmtime() accepts... " >&6; } if ${i_cv_gmtime_max_time_t+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main() { FILE *f; int bits; for (bits = 1; bits < sizeof(time_t)*8; bits++) { time_t t = ((time_t)1 << bits) - 1; if (gmtime(&t) == NULL) { bits--; break; } } if (bits > 40) { /* Solaris 9 breaks after 55 bits. Perhaps other systems break earlier. Let's just do the same as Cyrus folks and limit it to 40 bits. */ bits = 40; } f = fopen("conftest.temp", "w"); if (f == NULL) { perror("fopen()"); return 1; } fprintf(f, "%d", bits); fclose(f); return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_gmtime_max_time_t=`cat conftest.temp` rm -f conftest.temp else printf "check failed, assuming " i_cv_gmtime_max_time_t=31 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_gmtime_max_time_t" >&5 $as_echo "$i_cv_gmtime_max_time_t" >&6; } cat >>confdefs.h <<_ACEOF #define TIME_T_MAX_BITS $i_cv_gmtime_max_time_t _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time_t is signed" >&5 $as_echo_n "checking whether time_t is signed... " >&6; } if ${i_cv_signed_time_t+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main() { /* return 0 if we're signed */ exit((time_t)(int)-1 <= 0 ? 0 : 1); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_signed_time_t=yes else i_cv_signed_time_t=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_signed_time_t" >&5 $as_echo "$i_cv_signed_time_t" >&6; } if test $i_cv_signed_time_t = yes; then $as_echo "#define TIME_T_SIGNED /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can use C99 static in array sizes" >&5 $as_echo_n "checking if we can use C99 static in array sizes... " >&6; } if ${i_cv_c99_static_arrays+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void foo(unsigned char [static 20]); int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_c99_static_arrays=yes else i_cv_c99_static_arrays=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_c99_static_arrays" >&5 $as_echo "$i_cv_c99_static_arrays" >&6; } if test $i_cv_c99_static_arrays = yes; then static_value=static else static_value= fi cat >>confdefs.h <<_ACEOF #define STATIC_ARRAY $static_value _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can use C99-like flexible array members" >&5 $as_echo_n "checking if we can use C99-like flexible array members... " >&6; } if ${i_cv_c99_flex_arrays+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ struct foo { int x; char y[]; }; int main () { struct foo foo; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_c99_flex_arrays=yes else i_cv_c99_flex_arrays=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_c99_flex_arrays" >&5 $as_echo "$i_cv_c99_flex_arrays" >&6; } if test $i_cv_c99_flex_arrays = yes; then flexible_value= else flexible_value=1 fi cat >>confdefs.h <<_ACEOF #define FLEXIBLE_ARRAY_MEMBER $flexible_value _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct iovec" >&5 $as_echo_n "checking for struct iovec... " >&6; } if ${i_cv_struct_iovec+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct iovec *iovec; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_struct_iovec=yes else i_cv_struct_iovec=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $i_cv_struct_iovec = yes; then $as_echo "#define HAVE_STRUCT_IOVEC /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_struct_iovec" >&5 $as_echo "$i_cv_struct_iovec" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_t is struct" >&5 $as_echo_n "checking whether dev_t is struct... " >&6; } if ${i_cv_dev_t_struct+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include struct test { dev_t a; }; static struct test t = { 0 }; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_dev_t_struct=no else i_cv_dev_t_struct=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_dev_t_struct" >&5 $as_echo "$i_cv_dev_t_struct" >&6; } if test $i_cv_dev_t_struct = yes; then $as_echo "#define DEV_T_STRUCT /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether RLIMIT_AS exists" >&5 $as_echo_n "checking whether RLIMIT_AS exists... " >&6; } if ${i_cv_have_rlimit_as+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct rlimit r; getrlimit(RLIMIT_AS, &r); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_rlimit_as=yes else i_cv_have_rlimit_as=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_rlimit_as" >&5 $as_echo "$i_cv_have_rlimit_as" >&6; } if test $i_cv_have_rlimit_as = yes; then $as_echo "#define HAVE_RLIMIT_AS /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether RLIMIT_NPROC exists" >&5 $as_echo_n "checking whether RLIMIT_NPROC exists... " >&6; } if ${i_cv_have_rlimit_nproc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct rlimit r; getrlimit(RLIMIT_NPROC, &r); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_rlimit_nproc=yes else i_cv_have_rlimit_nproc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_rlimit_nproc" >&5 $as_echo "$i_cv_have_rlimit_nproc" >&6; } if test $i_cv_have_rlimit_nproc = yes; then $as_echo "#define HAVE_RLIMIT_NPROC /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether RLIMIT_CORE exists" >&5 $as_echo_n "checking whether RLIMIT_CORE exists... " >&6; } if ${i_cv_have_rlimit_core+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct rlimit r; getrlimit(RLIMIT_CORE, &r); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_rlimit_core=yes else i_cv_have_rlimit_core=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_rlimit_core" >&5 $as_echo "$i_cv_have_rlimit_core" >&6; } if test $i_cv_have_rlimit_core = yes; then $as_echo "#define HAVE_RLIMIT_CORE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether PR_SET_DUMPABLE exists" >&5 $as_echo_n "checking whether PR_SET_DUMPABLE exists... " >&6; } if ${i_cv_have_pr_set_dumpable+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_pr_set_dumpable=yes else i_cv_have_pr_set_dumpable=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_pr_set_dumpable" >&5 $as_echo "$i_cv_have_pr_set_dumpable" >&6; } if test $i_cv_have_pr_set_dumpable = yes; then $as_echo "#define HAVE_PR_SET_DUMPABLE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking Linux compatible mremap()" >&5 $as_echo_n "checking Linux compatible mremap()... " >&6; } if ${i_cv_have_linux_mremap+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #define __USE_GNU #include int main () { mremap(0, 0, 0, MREMAP_MAYMOVE); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_linux_mremap=yes else i_cv_have_linux_mremap=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_linux_mremap" >&5 $as_echo "$i_cv_have_linux_mremap" >&6; } if test $i_cv_have_linux_mremap = yes; then $as_echo "#define HAVE_LINUX_MREMAP /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether shared mmaps get updated by write()s" >&5 $as_echo_n "checking whether shared mmaps get updated by write()s... " >&6; } if ${i_cv_mmap_plays_with_write+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include int main() { /* return 0 if we're signed */ int f = open("conftest.mmap", O_RDWR|O_CREAT|O_TRUNC, 0600); void *mem; if (f == -1) { perror("open()"); return 1; } unlink("conftest.mmap"); write(f, "1", 2); mem = mmap(NULL, 2, PROT_READ|PROT_WRITE, MAP_SHARED, f, 0); if (mem == MAP_FAILED) { perror("mmap()"); return 1; } strcpy(mem, "2"); msync(mem, 2, MS_SYNC); lseek(f, 0, SEEK_SET); write(f, "3", 2); return strcmp(mem, "3") == 0 ? 0 : 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_mmap_plays_with_write=yes else i_cv_mmap_plays_with_write=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_mmap_plays_with_write" >&5 $as_echo "$i_cv_mmap_plays_with_write" >&6; } if test $i_cv_mmap_plays_with_write = no; then $as_echo "#define MMAP_CONFLICTS_WRITE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether fd passing works" >&5 $as_echo_n "checking whether fd passing works... " >&6; } if ${i_cv_fd_passing+:} false; then : $as_echo_n "(cached) " >&6 else for i in 1 2; do old_cflags="$CFLAGS" CFLAGS="$CFLAGS -I$srcdir/src/lib $srcdir/src/lib/fdpass.c" if test $i = 2; then CFLAGS="$CFLAGS -DBUGGY_CMSG_MACROS" fi if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include "fdpass.h" int nopen(void) { int i, n; struct stat sb; for (i = n = 0; i < 256; i++) if (fstat(i, &sb) == 0) n++; return n; } int main(void) { int fd[2], send_fd, recv_fd, status, n1, n2; struct stat st, st2; char data; send_fd = creat("conftest.fdpass", 0600); if (send_fd == -1) return 2; unlink("conftest.fdpass"); if (fstat(send_fd, &st) < 0) return 2; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) return 2; n1 = nopen(); switch (fork()) { case -1: return 2; case 0: alarm(1); if (fd_send(fd[0], send_fd, &data, 1) != 1) return 2; return 0; default: alarm(2); if (wait(&status) == -1) return 2; if (status != 0) return status; if (fd_read(fd[1], &data, 1, &recv_fd) != 1) return 1; if (fstat(recv_fd, &st2) < 0) return 2; /* nopen check is for making sure that only a single fd was received */ n2 = nopen(); return st.st_ino == st2.st_ino && n2 == n1 + 1 ? 0 : 1; } } _ACEOF if ac_fn_c_try_run "$LINENO"; then : CFLAGS=$old_cflags if test $i = 2; then i_cv_fd_passing=buggy_cmsg_macros else i_cv_fd_passing=yes fi break else CFLAGS=$old_cflags i_cv_fd_passing=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_fd_passing" >&5 $as_echo "$i_cv_fd_passing" >&6; }; case "$host_os" in darwin[1-9].*) if test "$i_cv_fd_passing" = "yes"; then i_cv_fd_passing=buggy_cmsg_macros fi ;; esac if test $i_cv_fd_passing = buggy_cmsg_macros; then $as_echo "#define BUGGY_CMSG_MACROS /**/" >>confdefs.h fi if test $i_cv_fd_passing = no; then as_fn_error $? "fd passing is required for Dovecot to work" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sendfile in -lsendfile" >&5 $as_echo_n "checking for sendfile in -lsendfile... " >&6; } if ${ac_cv_lib_sendfile_sendfile+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsendfile $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sendfile (); int main () { return sendfile (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_sendfile_sendfile=yes else ac_cv_lib_sendfile_sendfile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sendfile_sendfile" >&5 $as_echo "$ac_cv_lib_sendfile_sendfile" >&6; } if test "x$ac_cv_lib_sendfile_sendfile" = xyes; then : LIBS="$LIBS -lsendfile" $as_echo "#define HAVE_SOLARIS_SENDFILE /**/" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: checking Linux compatible sendfile()" >&5 $as_echo_n "checking Linux compatible sendfile()... " >&6; } if ${i_cv_have_linux_sendfile+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #undef _FILE_OFFSET_BITS #include #include #include int main () { sendfile(0, 0, (void *) 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_linux_sendfile=yes else i_cv_have_linux_sendfile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_linux_sendfile" >&5 $as_echo "$i_cv_have_linux_sendfile" >&6; } if test $i_cv_have_linux_sendfile = yes; then $as_echo "#define HAVE_LINUX_SENDFILE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking FreeBSD compatible sendfile()" >&5 $as_echo_n "checking FreeBSD compatible sendfile()... " >&6; } if ${i_cv_have_freebsd_sendfile+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct sf_hdtr hdtr; sendfile(0, 0, 0, 0, &hdtr, (void *) 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_freebsd_sendfile=yes else i_cv_have_freebsd_sendfile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_freebsd_sendfile" >&5 $as_echo "$i_cv_have_freebsd_sendfile" >&6; } if test $i_cv_have_freebsd_sendfile = yes; then $as_echo "#define HAVE_FREEBSD_SENDFILE /**/" >>confdefs.h fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if unsetenv returns int" >&5 $as_echo_n "checking if unsetenv returns int... " >&6; } if ${i_cv_unsetenv_ret_int+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { if (unsetenv("env") < 0) ; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_unsetenv_ret_int=yes else i_cv_unsetenv_ret_int=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_unsetenv_ret_int" >&5 $as_echo "$i_cv_unsetenv_ret_int" >&6; } if test $i_cv_unsetenv_ret_int = yes; then $as_echo "#define UNSETENV_RET_INT /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we should use _XPG6 macro for crypt()" >&5 $as_echo_n "checking if we should use _XPG6 macro for crypt()... " >&6; } if ${i_cv_use_xpg6_crypt+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 4 #define _XOPEN_SOURCE_EXTENDED 1 #define _XOPEN_VERSION 4 #define _XPG4_2 #define _XPG6 #include int main () { crypt("a", "b"); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_use_xpg6_crypt=yes else i_cv_use_xpg6_crypt=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_use_xpg6_crypt" >&5 $as_echo "$i_cv_use_xpg6_crypt" >&6; } if test $i_cv_use_xpg6_crypt = yes; then $as_echo "#define CRYPT_USE_XPG6 /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if struct stat has st_?tim timespec fields" >&5 $as_echo_n "checking if struct stat has st_?tim timespec fields... " >&6; } if ${i_cv_have_st_tim_timespec+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct stat st; unsigned long x = st.st_mtim.tv_nsec; return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_st_tim_timespec=yes else i_cv_have_st_tim_timespec=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_st_tim_timespec" >&5 $as_echo "$i_cv_have_st_tim_timespec" >&6; } if test $i_cv_have_st_tim_timespec = yes; then $as_echo "#define HAVE_STAT_XTIM /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if struct stat has st_?timespec fields" >&5 $as_echo_n "checking if struct stat has st_?timespec fields... " >&6; } if ${i_cv_have_st_timespec+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct stat st; unsigned long x = st.st_mtimespec.tv_nsec; return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_st_timespec=yes else i_cv_have_st_timespec=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_st_timespec" >&5 $as_echo "$i_cv_have_st_timespec" >&6; } if test $i_cv_have_st_timespec = yes; then $as_echo "#define HAVE_STAT_XTIMESPEC /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if statvfs.f_mntfromname exists" >&5 $as_echo_n "checking if statvfs.f_mntfromname exists... " >&6; } if ${i_cv_have_statvfs_f_mntfromname+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct statvfs buf; char *p = buf.f_mntfromname; statvfs(".", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_statvfs_f_mntfromname=yes else i_cv_have_statvfs_f_mntfromname=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_statvfs_f_mntfromname" >&5 $as_echo "$i_cv_have_statvfs_f_mntfromname" >&6; } if test $i_cv_have_statvfs_f_mntfromname = yes; then $as_echo "#define HAVE_STATVFS_MNTFROMNAME /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if statfs.f_mntfromname exists" >&5 $as_echo_n "checking if statfs.f_mntfromname exists... " >&6; } if ${i_cv_have_statfs_f_mntfromname+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct statfs buf; char *p = buf.f_mntfromname; statfs(".", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_statfs_f_mntfromname=yes else i_cv_have_statfs_f_mntfromname=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_statfs_f_mntfromname" >&5 $as_echo "$i_cv_have_statfs_f_mntfromname" >&6; } if test $i_cv_have_statfs_f_mntfromname = yes; then $as_echo "#define HAVE_STATFS_MNTFROMNAME /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if struct dqblk.dqb_curblocks exists" >&5 $as_echo_n "checking if struct dqblk.dqb_curblocks exists... " >&6; } if ${i_cv_have_dqblk_dqb_curblocks+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include "$srcdir/src/plugins/quota/quota-fs.h" int main () { struct dqblk dqblk; unsigned int x = dqblk.dqb_curblocks; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_dqblk_dqb_curblocks=yes else i_cv_have_dqblk_dqb_curblocks=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_dqblk_dqb_curblocks" >&5 $as_echo "$i_cv_have_dqblk_dqb_curblocks" >&6; } if test $i_cv_have_dqblk_dqb_curblocks = yes; then $as_echo "#define HAVE_STRUCT_DQBLK_CURBLOCKS /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if struct dqblk.dqb_curspace exists" >&5 $as_echo_n "checking if struct dqblk.dqb_curspace exists... " >&6; } if ${i_cv_have_dqblk_dqb_curspace+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include "$srcdir/src/plugins/quota/quota-fs.h" int main () { struct dqblk dqblk; unsigned int x = dqblk.dqb_curspace; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_dqblk_dqb_curspace=yes else i_cv_have_dqblk_dqb_curspace=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_dqblk_dqb_curspace" >&5 $as_echo "$i_cv_have_dqblk_dqb_curspace" >&6; } if test $i_cv_have_dqblk_dqb_curspace = yes; then $as_echo "#define HAVE_STRUCT_DQBLK_CURSPACE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Q_QUOTACTL ioctl exists" >&5 $as_echo_n "checking if Q_QUOTACTL ioctl exists... " >&6; } if ${i_cv_have_ioctl_q_quotactl+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct quotctl ctl; ioctl(0, Q_QUOTACTL, &ctl); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_ioctl_q_quotactl=yes else i_cv_have_ioctl_q_quotactl=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_ioctl_q_quotactl" >&5 $as_echo "$i_cv_have_ioctl_q_quotactl" >&6; } if test $i_cv_have_ioctl_q_quotactl = yes; then $as_echo "#define HAVE_Q_QUOTACTL /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C99 vsnprintf()" >&5 $as_echo_n "checking for C99 vsnprintf()... " >&6; } if ${i_cv_c99_vsnprintf+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include static int f(const char *fmt, ...) { va_list args; char buf[13]; int ret; va_start(args, fmt); ret = vsnprintf(buf, 11, fmt, args) != 12 || buf[11-1] != '\0'; va_end(args); return ret; } int main() { return f("hello %s%d", "world", 1); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_c99_vsnprintf=yes else i_cv_c99_vsnprintf=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_c99_vsnprintf" >&5 $as_echo "$i_cv_c99_vsnprintf" >&6; } if test $i_cv_c99_vsnprintf = no; then $as_echo "#define HAVE_OLD_VSNPRINTF /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an implementation of va_copy()" >&5 $as_echo_n "checking for an implementation of va_copy()... " >&6; } if ${lib_cv_va_copy+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include void f (int i, ...) { va_list args1, args2; va_start (args1, i); va_copy (args2, args1); if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) exit (1); va_end (args1); va_end (args2); } int main() { f (0, 42); return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : lib_cv_va_copy=yes else lib_cv_va_copy=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lib_cv_va_copy" >&5 $as_echo "$lib_cv_va_copy" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an implementation of __va_copy()" >&5 $as_echo_n "checking for an implementation of __va_copy()... " >&6; } if ${lib_cv___va_copy+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include void f (int i, ...) { va_list args1, args2; va_start (args1, i); __va_copy (args2, args1); if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) exit (1); va_end (args1); va_end (args2); } int main() { f (0, 42); return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : lib_cv___va_copy=yes else lib_cv___va_copy=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lib_cv___va_copy" >&5 $as_echo "$lib_cv___va_copy" >&6; } if test "x$lib_cv_va_copy" = "xyes"; then va_copy_func=va_copy else if test "x$lib_cv___va_copy" = "xyes"; then va_copy_func=__va_copy fi fi if test -n "$va_copy_func"; then cat >>confdefs.h <<_ACEOF #define VA_COPY $va_copy_func _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether va_lists can be copied by value" >&5 $as_echo_n "checking whether va_lists can be copied by value... " >&6; } if ${lib_cv_va_val_copy+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include void f (int i, ...) { va_list args1, args2; va_start (args1, i); args2 = args1; if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) exit (1); va_end (args1); va_end (args2); } int main() { f (0, 42); return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : lib_cv_va_val_copy=yes else lib_cv_va_val_copy=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lib_cv_va_val_copy" >&5 $as_echo "$lib_cv_va_val_copy" >&6; } if test "x$lib_cv_va_val_copy" = "xno"; then $as_echo "#define VA_COPY_AS_ARRAY 1" >>confdefs.h fi have_modules=no ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : have_modules=yes MODULE_LIBS="-export-dynamic" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : have_modules=yes MODULE_LIBS="-export-dynamic -ldl" DLLIB=-ldl fi fi if test $ac_cv_header_sys_vmount_h = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for reasonable mntctl buffer size" >&5 $as_echo_n "checking for reasonable mntctl buffer size... " >&6; } if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main() { int size,count; char *m; FILE *f=fopen("conftestval", "w"); if (!f) exit(1); if ((count=mntctl(MCTL_QUERY,sizeof(size),&size))!=0 || !(m=malloc(size)) || (count=mntctl(MCTL_QUERY,size,m))<=0) exit(1); fprintf(f, "%d\n",(size * (count + 5))/count & ~1); /* 5 mounts more */ exit(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : size=`cat conftestval` rm -f conftestval cat >>confdefs.h <<_ACEOF #define STATIC_MTAB_SIZE $size _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: $size" >&5 $as_echo "$size" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: default" >&5 $as_echo "default" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi have_ssl=no build_dcrypt_openssl=no if test $want_openssl != no && test $have_ssl = no; then if test "$PKG_CONFIG" != "" && $PKG_CONFIG --exists openssl 2>/dev/null; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL" >&5 $as_echo_n "checking for SSL... " >&6; } if test -n "$SSL_CFLAGS"; then pkg_cv_SSL_CFLAGS="$SSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SSL_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SSL_LIBS"; then pkg_cv_SSL_LIBS="$SSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SSL_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` else SSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SSL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (openssl) were not met: $SSL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SSL_CFLAGS and SSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SSL_CFLAGS and SSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else SSL_CFLAGS=$pkg_cv_SSL_CFLAGS SSL_LIBS=$pkg_cv_SSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi CFLAGS="$CFLAGS $SSL_CFLAGS" have_openssl=yes else # openssl 0.9.8 wants -ldl and it's required if there's only .a lib { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_read in -lssl" >&5 $as_echo_n "checking for SSL_read in -lssl... " >&6; } if ${ac_cv_lib_ssl_SSL_read+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl -lcrypto $DLLIB $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_read (); int main () { return SSL_read (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_SSL_read=yes else ac_cv_lib_ssl_SSL_read=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_read" >&5 $as_echo "$ac_cv_lib_ssl_SSL_read" >&6; } if test "x$ac_cv_lib_ssl_SSL_read" = xyes; then : for ac_header in openssl/ssl.h openssl/err.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF SSL_LIBS="-lssl -lcrypto $DLLIB" have_openssl=yes else if test $want_openssl = yes; then as_fn_error $? "Can't build with OpenSSL: openssl/ssl.h or openssl/err.h not found" "$LINENO" 5 fi fi done else if test $want_openssl = yes; then as_fn_error $? "Can't build with OpenSSL: libssl not found" "$LINENO" 5 fi fi fi if test "$have_openssl" = "yes"; then $as_echo "#define HAVE_OPENSSL /**/" >>confdefs.h have_ssl="yes (OpenSSL)" build_dcrypt_openssl="no" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if OpenSSL version is 1.0.2 or better" >&5 $as_echo_n "checking if OpenSSL version is 1.0.2 or better... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #if OPENSSL_VERSION_NUMBER < 0x10002000L #error "fail-compile" #endif int main () { return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ssl_version_ge_102=true else ssl_version_ge_102=false fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ssl_version_ge_102" >&5 $as_echo "$ssl_version_ge_102" >&6; } # SSL_clear_options introduced in openssl 0.9.8m but may be backported to # older versions in "enterprise" OS releases; originally implemented as a # macro but as a function in more recent openssl versions { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether SSL_clear_options exists" >&5 $as_echo_n "checking whether SSL_clear_options exists... " >&6; } if ${i_cv_have_ssl_clear_options+:} false; then : $as_echo_n "(cached) " >&6 else old_LIBS=$LIBS LIBS="$LIBS -lssl" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { SSL *ssl; long options; SSL_clear_options(ssl, options); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_ssl_clear_options=yes else i_cv_have_ssl_clear_options=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$old_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_ssl_clear_options" >&5 $as_echo "$i_cv_have_ssl_clear_options" >&6; } if test $i_cv_have_ssl_clear_options = yes; then $as_echo "#define HAVE_SSL_CLEAR_OPTIONS /**/" >>confdefs.h fi # New style mem functions? Should be in v1.1+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CRYPTO_set_mem_functions has new style parameters" >&5 $as_echo_n "checking whether CRYPTO_set_mem_functions has new style parameters... " >&6; } if ${i_cv_have_ssl_new_mem_funcs+:} false; then : $as_echo_n "(cached) " >&6 else old_LIBS=$LIBS LIBS="$LIBS -lssl" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int CRYPTO_set_mem_functions( void *(*m) (size_t, const char *, int), void *(*r) (void *, size_t, const char *, int), void (*f) (void *, const char *, int)); int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_ssl_new_mem_funcs=yes else i_cv_have_ssl_new_mem_funcs=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$old_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_ssl_new_mem_funcs" >&5 $as_echo "$i_cv_have_ssl_new_mem_funcs" >&6; } if test $i_cv_have_ssl_new_mem_funcs = yes; then $as_echo "#define HAVE_SSL_NEW_MEM_FUNCS /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_remove_thread_state in -lssl" >&5 $as_echo_n "checking for ERR_remove_thread_state in -lssl... " >&6; } if ${ac_cv_lib_ssl_ERR_remove_thread_state+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ERR_remove_thread_state (); int main () { return ERR_remove_thread_state (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_ERR_remove_thread_state=yes else ac_cv_lib_ssl_ERR_remove_thread_state=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_ERR_remove_thread_state" >&5 $as_echo "$ac_cv_lib_ssl_ERR_remove_thread_state" >&6; } if test "x$ac_cv_lib_ssl_ERR_remove_thread_state" = xyes; then : $as_echo "#define HAVE_OPENSSL_ERR_REMOVE_THREAD_STATE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL_thread_stop in -lssl" >&5 $as_echo_n "checking for OPENSSL_thread_stop in -lssl... " >&6; } if ${ac_cv_lib_ssl_OPENSSL_thread_stop+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char OPENSSL_thread_stop (); int main () { return OPENSSL_thread_stop (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_OPENSSL_thread_stop=yes else ac_cv_lib_ssl_OPENSSL_thread_stop=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_OPENSSL_thread_stop" >&5 $as_echo "$ac_cv_lib_ssl_OPENSSL_thread_stop" >&6; } if test "x$ac_cv_lib_ssl_OPENSSL_thread_stop" = xyes; then : $as_echo "#define HAVE_OPENSSL_AUTO_THREAD_DEINIT /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL_cleanup in -lssl" >&5 $as_echo_n "checking for OPENSSL_cleanup in -lssl... " >&6; } if ${ac_cv_lib_ssl_OPENSSL_cleanup+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char OPENSSL_cleanup (); int main () { return OPENSSL_cleanup (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_OPENSSL_cleanup=yes else ac_cv_lib_ssl_OPENSSL_cleanup=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_OPENSSL_cleanup" >&5 $as_echo "$ac_cv_lib_ssl_OPENSSL_cleanup" >&6; } if test "x$ac_cv_lib_ssl_OPENSSL_cleanup" = xyes; then : $as_echo "#define HAVE_OPENSSL_CLEANUP /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_get_current_compression in -lssl" >&5 $as_echo_n "checking for SSL_get_current_compression in -lssl... " >&6; } if ${ac_cv_lib_ssl_SSL_get_current_compression+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_get_current_compression (); int main () { return SSL_get_current_compression (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_SSL_get_current_compression=yes else ac_cv_lib_ssl_SSL_get_current_compression=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_get_current_compression" >&5 $as_echo "$ac_cv_lib_ssl_SSL_get_current_compression" >&6; } if test "x$ac_cv_lib_ssl_SSL_get_current_compression" = xyes; then : $as_echo "#define HAVE_SSL_COMPRESSION /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_get_servername in -lssl" >&5 $as_echo_n "checking for SSL_get_servername in -lssl... " >&6; } if ${ac_cv_lib_ssl_SSL_get_servername+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_get_servername (); int main () { return SSL_get_servername (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_SSL_get_servername=yes else ac_cv_lib_ssl_SSL_get_servername=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_get_servername" >&5 $as_echo "$ac_cv_lib_ssl_SSL_get_servername" >&6; } if test "x$ac_cv_lib_ssl_SSL_get_servername" = xyes; then : $as_echo "#define HAVE_SSL_GET_SERVERNAME /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_COMP_free_compression_methods in -lssl" >&5 $as_echo_n "checking for SSL_COMP_free_compression_methods in -lssl... " >&6; } if ${ac_cv_lib_ssl_SSL_COMP_free_compression_methods+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_COMP_free_compression_methods (); int main () { return SSL_COMP_free_compression_methods (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_SSL_COMP_free_compression_methods=yes else ac_cv_lib_ssl_SSL_COMP_free_compression_methods=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_COMP_free_compression_methods" >&5 $as_echo "$ac_cv_lib_ssl_SSL_COMP_free_compression_methods" >&6; } if test "x$ac_cv_lib_ssl_SSL_COMP_free_compression_methods" = xyes; then : $as_echo "#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RSA_generate_key_ex in -lssl" >&5 $as_echo_n "checking for RSA_generate_key_ex in -lssl... " >&6; } if ${ac_cv_lib_ssl_RSA_generate_key_ex+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char RSA_generate_key_ex (); int main () { return RSA_generate_key_ex (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_RSA_generate_key_ex=yes else ac_cv_lib_ssl_RSA_generate_key_ex=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_RSA_generate_key_ex" >&5 $as_echo "$ac_cv_lib_ssl_RSA_generate_key_ex" >&6; } if test "x$ac_cv_lib_ssl_RSA_generate_key_ex" = xyes; then : $as_echo "#define HAVE_RSA_GENERATE_KEY_EX /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ASN1_STRING_get0_data in -lssl" >&5 $as_echo_n "checking for ASN1_STRING_get0_data in -lssl... " >&6; } if ${ac_cv_lib_ssl_ASN1_STRING_get0_data+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ASN1_STRING_get0_data (); int main () { return ASN1_STRING_get0_data (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_ASN1_STRING_get0_data=yes else ac_cv_lib_ssl_ASN1_STRING_get0_data=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_ASN1_STRING_get0_data" >&5 $as_echo "$ac_cv_lib_ssl_ASN1_STRING_get0_data" >&6; } if test "x$ac_cv_lib_ssl_ASN1_STRING_get0_data" = xyes; then : $as_echo "#define HAVE_ASN1_STRING_GET0_DATA /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for HMAC_CTX_new in -lssl" >&5 $as_echo_n "checking for HMAC_CTX_new in -lssl... " >&6; } if ${ac_cv_lib_ssl_HMAC_CTX_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char HMAC_CTX_new (); int main () { return HMAC_CTX_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_HMAC_CTX_new=yes else ac_cv_lib_ssl_HMAC_CTX_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_HMAC_CTX_new" >&5 $as_echo "$ac_cv_lib_ssl_HMAC_CTX_new" >&6; } if test "x$ac_cv_lib_ssl_HMAC_CTX_new" = xyes; then : $as_echo "#define HAVE_HMAC_CTX_NEW /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_MD_CTX_new in -lssl" >&5 $as_echo_n "checking for EVP_MD_CTX_new in -lssl... " >&6; } if ${ac_cv_lib_ssl_EVP_MD_CTX_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char EVP_MD_CTX_new (); int main () { return EVP_MD_CTX_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_EVP_MD_CTX_new=yes else ac_cv_lib_ssl_EVP_MD_CTX_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_EVP_MD_CTX_new" >&5 $as_echo "$ac_cv_lib_ssl_EVP_MD_CTX_new" >&6; } if test "x$ac_cv_lib_ssl_EVP_MD_CTX_new" = xyes; then : $as_echo "#define HAVE_EVP_MD_CTX_NEW /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OBJ_length in -lssl" >&5 $as_echo_n "checking for OBJ_length in -lssl... " >&6; } if ${ac_cv_lib_ssl_OBJ_length+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char OBJ_length (); int main () { return OBJ_length (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_OBJ_length=yes else ac_cv_lib_ssl_OBJ_length=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_OBJ_length" >&5 $as_echo "$ac_cv_lib_ssl_OBJ_length" >&6; } if test "x$ac_cv_lib_ssl_OBJ_length" = xyes; then : $as_echo "#define HAVE_OBJ_LENGTH /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_PKEY_get0_RSA in -lssl" >&5 $as_echo_n "checking for EVP_PKEY_get0_RSA in -lssl... " >&6; } if ${ac_cv_lib_ssl_EVP_PKEY_get0_RSA+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char EVP_PKEY_get0_RSA (); int main () { return EVP_PKEY_get0_RSA (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_EVP_PKEY_get0_RSA=yes else ac_cv_lib_ssl_EVP_PKEY_get0_RSA=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_EVP_PKEY_get0_RSA" >&5 $as_echo "$ac_cv_lib_ssl_EVP_PKEY_get0_RSA" >&6; } if test "x$ac_cv_lib_ssl_EVP_PKEY_get0_RSA" = xyes; then : $as_echo "#define HAVE_EVP_PKEY_get0 /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_PKEY_CTX_new_id in -lssl" >&5 $as_echo_n "checking for EVP_PKEY_CTX_new_id in -lssl... " >&6; } if ${ac_cv_lib_ssl_EVP_PKEY_CTX_new_id+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char EVP_PKEY_CTX_new_id (); int main () { return EVP_PKEY_CTX_new_id (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_EVP_PKEY_CTX_new_id=yes else ac_cv_lib_ssl_EVP_PKEY_CTX_new_id=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_EVP_PKEY_CTX_new_id" >&5 $as_echo "$ac_cv_lib_ssl_EVP_PKEY_CTX_new_id" >&6; } if test "x$ac_cv_lib_ssl_EVP_PKEY_CTX_new_id" = xyes; then : have_evp_pkey_ctx_new_id="yes" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EC_KEY_new in -lssl" >&5 $as_echo_n "checking for EC_KEY_new in -lssl... " >&6; } if ${ac_cv_lib_ssl_EC_KEY_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $SSL_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char EC_KEY_new (); int main () { return EC_KEY_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_EC_KEY_new=yes else ac_cv_lib_ssl_EC_KEY_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_EC_KEY_new" >&5 $as_echo "$ac_cv_lib_ssl_EC_KEY_new" >&6; } if test "x$ac_cv_lib_ssl_EC_KEY_new" = xyes; then : have_ec_key_new="yes" fi if test "$have_evp_pkey_ctx_new_id" = "yes" && test "$have_ec_key_new" = "yes"; then build_dcrypt_openssl="yes" else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No ECC support in OpenSSL - not enabling dcrypt" >&5 $as_echo "$as_me: WARNING: No ECC support in OpenSSL - not enabling dcrypt" >&2;} fi fi fi if test x$ssl_version_ge_102 = xtrue; then SSL_VERSION_GE_102_TRUE= SSL_VERSION_GE_102_FALSE='#' else SSL_VERSION_GE_102_TRUE='#' SSL_VERSION_GE_102_FALSE= fi if test "$have_openssl" = "yes"; then BUILD_OPENSSL_TRUE= BUILD_OPENSSL_FALSE='#' else BUILD_OPENSSL_TRUE='#' BUILD_OPENSSL_FALSE= fi if test "$build_dcrypt_openssl" = "yes"; then BUILD_DCRYPT_OPENSSL_TRUE= BUILD_DCRYPT_OPENSSL_FALSE='#' else BUILD_DCRYPT_OPENSSL_TRUE='#' BUILD_DCRYPT_OPENSSL_FALSE= fi if test $want_gnutls != no && test $have_ssl = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls_global_init in -lgnutls" >&5 $as_echo_n "checking for gnutls_global_init in -lgnutls... " >&6; } if ${ac_cv_lib_gnutls_gnutls_global_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgnutls -lgcrypt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gnutls_global_init (); int main () { return gnutls_global_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gnutls_gnutls_global_init=yes else ac_cv_lib_gnutls_gnutls_global_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_global_init" >&5 $as_echo "$ac_cv_lib_gnutls_gnutls_global_init" >&6; } if test "x$ac_cv_lib_gnutls_gnutls_global_init" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default" if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then : $as_echo "#define HAVE_GNUTLS /**/" >>confdefs.h SSL_LIBS="-lgnutls -lgcrypt" have_ssl="yes (GNUTLS)" have_gnutls=yes else if test $want_gnutls = yes; then as_fn_error $? "Can't build with GNUTLS: gnutls/gnutls.h not found" "$LINENO" 5 fi fi else if test $want_gnutls = yes; then as_fn_error $? "Can't build with GNUTLS: libgnutls not found" "$LINENO" 5 fi fi fi if test "$have_ssl" != "no"; then $as_echo "#define HAVE_SSL /**/" >>confdefs.h fi if test $want_gc != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GC_malloc in -lgc" >&5 $as_echo_n "checking for GC_malloc in -lgc... " >&6; } if ${ac_cv_lib_gc_GC_malloc+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char GC_malloc (); int main () { return GC_malloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gc_GC_malloc=yes else ac_cv_lib_gc_GC_malloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gc_GC_malloc" >&5 $as_echo "$ac_cv_lib_gc_GC_malloc" >&6; } if test "x$ac_cv_lib_gc_GC_malloc" = xyes; then : for ac_header in gc/gc.h gc.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done $as_echo "#define USE_GC /**/" >>confdefs.h LIBS="$LIBS -lgc" else if test $want_gc = yes; then as_fn_error $? "Can't build with GC: libgc not found" "$LINENO" 5 fi fi fi userdb="" passdb="" not_userdb="" not_passdb="" passdb="$passdb static" userdb="$userdb static" if test $want_prefetch_userdb != no; then $as_echo "#define USERDB_PREFETCH /**/" >>confdefs.h userdb="$userdb prefetch" else not_userdb="$not_userdb prefetch" fi if test $want_passwd != no; then $as_echo "#define USERDB_PASSWD /**/" >>confdefs.h $as_echo "#define PASSDB_PASSWD /**/" >>confdefs.h userdb="$userdb passwd" passdb="$passdb passwd" else not_passdb="$not_passdb passwd" not_userdb="$not_userdb passwd" fi if test $want_passwd_file != no; then $as_echo "#define USERDB_PASSWD_FILE /**/" >>confdefs.h $as_echo "#define PASSDB_PASSWD_FILE /**/" >>confdefs.h userdb="$userdb passwd-file" passdb="$passdb passwd-file" else not_passdb="$not_passdb passwd-file" not_userdb="$not_userdb passwd-file" fi have_shadow=no if test $want_shadow != no; then ac_fn_c_check_func "$LINENO" "getspnam" "ac_cv_func_getspnam" if test "x$ac_cv_func_getspnam" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "shadow.h" "ac_cv_header_shadow_h" "$ac_includes_default" if test "x$ac_cv_header_shadow_h" = xyes; then : $as_echo "#define PASSDB_SHADOW /**/" >>confdefs.h have_shadow=yes else if test $want_shadow = yes; then as_fn_error $? "Can't build with shadow support: shadow.h not found" "$LINENO" 5 fi fi else if test $want_shadow = yes; then as_fn_error $? "Can't build with shadow support: getspnam() not found" "$LINENO" 5 fi fi fi if test $have_shadow = no; then not_passdb="$not_passdb shadow" else passdb="$passdb shadow" fi if test $want_pam != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5 $as_echo_n "checking for pam_start in -lpam... " >&6; } if ${ac_cv_lib_pam_pam_start+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpam $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pam_start (); int main () { return pam_start (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pam_pam_start=yes else ac_cv_lib_pam_pam_start=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5 $as_echo "$ac_cv_lib_pam_pam_start" >&6; } if test "x$ac_cv_lib_pam_pam_start" = xyes; then : have_pam=no ac_fn_c_check_header_mongrel "$LINENO" "security/pam_appl.h" "ac_cv_header_security_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_security_pam_appl_h" = xyes; then : $as_echo "#define HAVE_SECURITY_PAM_APPL_H /**/" >>confdefs.h have_pam=yes fi ac_fn_c_check_header_mongrel "$LINENO" "pam/pam_appl.h" "ac_cv_header_pam_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_pam_pam_appl_h" = xyes; then : $as_echo "#define HAVE_PAM_PAM_APPL_H /**/" >>confdefs.h have_pam=yes fi else if test $want_pam = yes; then as_fn_error $? "Can't build with PAM support: libpam not found" "$LINENO" 5 fi fi fi if test "$have_pam" = "yes"; then AUTH_LIBS="$AUTH_LIBS -lpam" $as_echo "#define PASSDB_PAM /**/" >>confdefs.h passdb="$passdb pam" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_setcred in -lpam" >&5 $as_echo_n "checking for pam_setcred in -lpam... " >&6; } if ${ac_cv_lib_pam_pam_setcred+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpam $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pam_setcred (); int main () { return pam_setcred (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pam_pam_setcred=yes else ac_cv_lib_pam_pam_setcred=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_setcred" >&5 $as_echo "$ac_cv_lib_pam_pam_setcred" >&6; } if test "x$ac_cv_lib_pam_pam_setcred" = xyes; then : $as_echo "#define HAVE_PAM_SETCRED /**/" >>confdefs.h fi elif test $want_pam = yes; then as_fn_error $? "Can't build with PAM support: pam_appl.h not found" "$LINENO" 5 else not_passdb="$not_passdb pam" fi if test $want_checkpassword != no; then $as_echo "#define PASSDB_CHECKPASSWORD /**/" >>confdefs.h $as_echo "#define USERDB_CHECKPASSWORD /**/" >>confdefs.h passdb="$passdb checkpassword" userdb="$userdb checkpassword" else not_passdb="$not_passdb checkpassword" not_userdb="$not_userdb checkpassword" fi have_bsdauth=no if test $want_bsdauth != no; then ac_fn_c_check_func "$LINENO" "auth_userokay" "ac_cv_func_auth_userokay" if test "x$ac_cv_func_auth_userokay" = xyes; then : $as_echo "#define PASSDB_BSDAUTH /**/" >>confdefs.h have_bsdauth=yes else if test $want_bsdauth = yes; then as_fn_error $? "Can't build with BSD authentication support: auth_userokay() not found" "$LINENO" 5 fi fi fi if test $have_bsdauth = no; then not_passdb="$not_passdb bsdauth" else passdb="$passdb bsdauth" fi have_gssapi=no if test $want_gssapi != no; then # Extract the first word of "krb5-config", so it can be a program name with args. set dummy krb5-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_KRB5CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$KRB5CONFIG"; then ac_cv_prog_KRB5CONFIG="$KRB5CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_KRB5CONFIG="krb5-config" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_KRB5CONFIG" && ac_cv_prog_KRB5CONFIG="NO" fi fi KRB5CONFIG=$ac_cv_prog_KRB5CONFIG if test -n "$KRB5CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $KRB5CONFIG" >&5 $as_echo "$KRB5CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test $KRB5CONFIG != NO; then if ! $KRB5CONFIG --version gssapi 2>/dev/null > /dev/null; then # krb5-config doesn't support gssapi. KRB5_LIBS="`$KRB5CONFIG --libs`" KRB5_CFLAGS=`$KRB5CONFIG --cflags` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gss_acquire_cred in -lgss" >&5 $as_echo_n "checking for gss_acquire_cred in -lgss... " >&6; } if ${ac_cv_lib_gss_gss_acquire_cred+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgss $KRB5_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gss_acquire_cred (); int main () { return gss_acquire_cred (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gss_gss_acquire_cred=yes else ac_cv_lib_gss_gss_acquire_cred=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gss_gss_acquire_cred" >&5 $as_echo "$ac_cv_lib_gss_gss_acquire_cred" >&6; } if test "x$ac_cv_lib_gss_gss_acquire_cred" = xyes; then : # Solaris KRB5_LIBS="$KRB5_LIBS -lgss" else # failed KRB5_LIBS= fi else KRB5_LIBS=`$KRB5CONFIG --libs gssapi` KRB5_CFLAGS=`$KRB5CONFIG --cflags gssapi` fi if test "$KRB5_LIBS" != ""; then # Although krb5-config exists, all systems still don't # have gssapi.h old_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $KRB5_CFLAGS" ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi.h" "ac_cv_header_gssapi_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_gssapi_h" = xyes; then : $as_echo "#define HAVE_GSSAPI_GSSAPI_H /**/" >>confdefs.h have_gssapi=yes fi ac_fn_c_check_header_mongrel "$LINENO" "gssapi.h" "ac_cv_header_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_h" = xyes; then : $as_echo "#define HAVE_GSSAPI_H /**/" >>confdefs.h have_gssapi=yes fi if test $have_gssapi != no; then if test $want_gssapi = plugin; then have_gssapi=plugin fi $as_echo "#define HAVE_GSSAPI /**/" >>confdefs.h for ac_header in gssapi/gssapi_ext.h gssapi_krb5.h gssapi/gssapi_krb5.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gss_userok in -lgss" >&5 $as_echo_n "checking for __gss_userok in -lgss... " >&6; } if ${ac_cv_lib_gss___gss_userok+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgss $KRB5_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __gss_userok (); int main () { return __gss_userok (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gss___gss_userok=yes else ac_cv_lib_gss___gss_userok=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gss___gss_userok" >&5 $as_echo "$ac_cv_lib_gss___gss_userok" >&6; } if test "x$ac_cv_lib_gss___gss_userok" = xyes; then : $as_echo "#define HAVE___GSS_USEROK /**/" >>confdefs.h KRB5_LIBS="$KRB5_LIBS -lgss" fi # MIT has a #define for Heimdal acceptor_identity, but it's way too # difficult to test for it.. old_LIBS=$LIBS LIBS="$LIBS $KRB5_LIBS" for ac_func in gsskrb5_register_acceptor_identity krb5_gss_register_acceptor_identity do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # does the kerberos library support SPNEGO? { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GSSAPI supports SPNEGO" >&5 $as_echo_n "checking whether GSSAPI supports SPNEGO... " >&6; } if ${i_cv_gssapi_spnego+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_GSSAPI_H # include #else # include #endif #include #include int main(void) { OM_uint32 minor_status; gss_OID_set mech_set; unsigned char spnego_oid[] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 }; unsigned int i; gss_indicate_mechs(&minor_status, &mech_set); for (i = 0; i < mech_set->count; i++) { if (mech_set->elements[i].length == 6 && memcmp(mech_set->elements[i].elements, spnego_oid, 6) == 0) return 0; } return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : i_cv_gssapi_spnego=yes else i_cv_gssapi_spnego=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_gssapi_spnego" >&5 $as_echo "$i_cv_gssapi_spnego" >&6; } if test "$i_cv_gssapi_spnego" = "yes"; then $as_echo "#define HAVE_GSSAPI_SPNEGO /**/" >>confdefs.h fi LIBS=$old_LIBS if test $want_gssapi != plugin; then AUTH_LIBS="$AUTH_LIBS $KRB5_LIBS" AUTH_CFLAGS="$AUTH_CFLAGS $KRB5_CFLAGS" $as_echo "#define BUILTIN_GSSAPI /**/" >>confdefs.h else have_gssapi_plugin=yes fi else if test $want_gssapi != auto; then as_fn_error $? "Can't build with GSSAPI support: gssapi.h not found" "$LINENO" 5 fi fi CFLAGS=$old_CFLAGS fi else if test $want_gssapi != auto; then as_fn_error $? "Can't build with GSSAPI support: krb5-config not found" "$LINENO" 5 fi fi fi if test "$have_gssapi_plugin" = "yes"; then GSSAPI_PLUGIN_TRUE= GSSAPI_PLUGIN_FALSE='#' else GSSAPI_PLUGIN_TRUE='#' GSSAPI_PLUGIN_FALSE= fi have_sia=no if test $want_sia != no; then ac_fn_c_check_func "$LINENO" "sia_validate_user" "ac_cv_func_sia_validate_user" if test "x$ac_cv_func_sia_validate_user" = xyes; then : $as_echo "#define PASSDB_SIA /**/" >>confdefs.h AUTH_LIBS="$AUTH_LIBS -depth_ring_search" have_sia=yes else if test $want_sia = yes; then as_fn_error $? "Can't build with SIA support: sia_validate_user() not found" "$LINENO" 5 fi fi fi if test $have_sia = no; then not_passdb="$not_passdb sia" else passdb="$passdb sia" fi have_ldap=no if test $want_ldap != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_init in -lldap" >&5 $as_echo_n "checking for ldap_init in -lldap... " >&6; } if ${ac_cv_lib_ldap_ldap_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lldap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ldap_init (); int main () { return ldap_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ldap_ldap_init=yes else ac_cv_lib_ldap_ldap_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_init" >&5 $as_echo "$ac_cv_lib_ldap_ldap_init" >&6; } if test "x$ac_cv_lib_ldap_ldap_init" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "ldap.h" "ac_cv_header_ldap_h" "$ac_includes_default" if test "x$ac_cv_header_ldap_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_initialize in -lldap" >&5 $as_echo_n "checking for ldap_initialize in -lldap... " >&6; } if ${ac_cv_lib_ldap_ldap_initialize+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lldap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ldap_initialize (); int main () { return ldap_initialize (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ldap_ldap_initialize=yes else ac_cv_lib_ldap_ldap_initialize=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_initialize" >&5 $as_echo "$ac_cv_lib_ldap_ldap_initialize" >&6; } if test "x$ac_cv_lib_ldap_ldap_initialize" = xyes; then : $as_echo "#define LDAP_HAVE_INITIALIZE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_start_tls_s in -lldap" >&5 $as_echo_n "checking for ldap_start_tls_s in -lldap... " >&6; } if ${ac_cv_lib_ldap_ldap_start_tls_s+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lldap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ldap_start_tls_s (); int main () { return ldap_start_tls_s (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ldap_ldap_start_tls_s=yes else ac_cv_lib_ldap_ldap_start_tls_s=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_start_tls_s" >&5 $as_echo "$ac_cv_lib_ldap_ldap_start_tls_s" >&6; } if test "x$ac_cv_lib_ldap_ldap_start_tls_s" = xyes; then : $as_echo "#define LDAP_HAVE_START_TLS_S /**/" >>confdefs.h fi LDAP_LIBS="-lldap" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_free in -lldap" >&5 $as_echo_n "checking for ber_free in -lldap... " >&6; } if ${ac_cv_lib_ldap_ber_free+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lldap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ber_free (); int main () { return ber_free (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ldap_ber_free=yes else ac_cv_lib_ldap_ber_free=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ber_free" >&5 $as_echo "$ac_cv_lib_ldap_ber_free" >&6; } if test "x$ac_cv_lib_ldap_ber_free" = xyes; then : # do nothing, default is to add -lldap to LIBS : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_free in -llber" >&5 $as_echo_n "checking for ber_free in -llber... " >&6; } if ${ac_cv_lib_lber_ber_free+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llber $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ber_free (); int main () { return ber_free (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lber_ber_free=yes else ac_cv_lib_lber_ber_free=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_free" >&5 $as_echo "$ac_cv_lib_lber_ber_free" >&6; } if test "x$ac_cv_lib_lber_ber_free" = xyes; then : LDAP_LIBS="$LDAP_LIBS -llber" fi fi if test $want_ldap != plugin; then AUTH_LIBS="$AUTH_LIBS $LDAP_LIBS" $as_echo "#define BUILTIN_LDAP /**/" >>confdefs.h fi $as_echo "#define USERDB_LDAP /**/" >>confdefs.h $as_echo "#define PASSDB_LDAP /**/" >>confdefs.h for ac_header in sasl.h sasl/sasl.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done have_ldap=yes else if test $want_ldap != auto; then as_fn_error $? "Can't build with LDAP support: ldap.h not found" "$LINENO" 5 fi fi else if test $want_ldap != auto; then as_fn_error $? "Can't build with LDAP support: libldap not found" "$LINENO" 5 fi fi fi if test $have_ldap = no; then not_passdb="$not_passdb ldap" not_userdb="$not_userdb ldap" else userdb="$userdb ldap" passdb="$passdb ldap" if test $want_ldap = plugin; then have_ldap_plugin=yes userdb="$userdb (plugin)" passdb="$passdb (plugin)" fi fi if test "$have_ldap_plugin" = "yes"; then LDAP_PLUGIN_TRUE= LDAP_PLUGIN_FALSE='#' else LDAP_PLUGIN_TRUE='#' LDAP_PLUGIN_FALSE= fi if test "$want_ldap" != "no"; then HAVE_LDAP_TRUE= HAVE_LDAP_FALSE='#' else HAVE_LDAP_TRUE='#' HAVE_LDAP_FALSE= fi dict_drivers= if test $want_db != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking db_env_create in -ldb" >&5 $as_echo_n "checking db_env_create in -ldb... " >&6; } if ${i_cv_have_db_env_create+:} false; then : $as_echo_n "(cached) " >&6 else old_LIBS=$LIBS LIBS="$LIBS -ldb" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { db_env_create(0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : i_cv_have_db_env_create=yes else i_cv_have_db_env_create=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$old_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_db_env_create" >&5 $as_echo "$i_cv_have_db_env_create" >&6; } if test $i_cv_have_db_env_create = yes; then ac_fn_c_check_header_mongrel "$LINENO" "db.h" "ac_cv_header_db_h" "$ac_includes_default" if test "x$ac_cv_header_db_h" = xyes; then : DICT_LIBS="$DICT_LIBS -ldb" dict_drivers="$dict_drivers db" $as_echo "#define BUILD_DB /**/" >>confdefs.h else if test $want_db = yes; then as_fn_error $? "Can't build with db support: db.h not found" "$LINENO" 5 fi fi else if test $want_db = yes; then as_fn_error $? "Can't build with db support: libdb not found" "$LINENO" 5 fi fi fi if test $want_cdb != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cdb_init in -lcdb" >&5 $as_echo_n "checking for cdb_init in -lcdb... " >&6; } if ${ac_cv_lib_cdb_cdb_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcdb $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cdb_init (); int main () { return cdb_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cdb_cdb_init=yes else ac_cv_lib_cdb_cdb_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cdb_cdb_init" >&5 $as_echo "$ac_cv_lib_cdb_cdb_init" >&6; } if test "x$ac_cv_lib_cdb_cdb_init" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "cdb.h" "ac_cv_header_cdb_h" "$ac_includes_default" if test "x$ac_cv_header_cdb_h" = xyes; then : DICT_LIBS="$DICT_LIBS -lcdb" dict_drivers="$dict_drivers cdb" $as_echo "#define BUILD_CDB /**/" >>confdefs.h else if test $want_cdb = yes; then as_fn_error $? "Can't build with CDB support: cdb.h not found" "$LINENO" 5 fi fi else if test $want_cdb = yes; then as_fn_error $? "Can't build with CDB support: libcdb not found" "$LINENO" 5 fi fi fi if test $want_pgsql != no; then # Extract the first word of "pg_config", so it can be a program name with args. set dummy pg_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_PG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PG_CONFIG"; then ac_cv_prog_PG_CONFIG="$PG_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PG_CONFIG="pg_config" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_PG_CONFIG" && ac_cv_prog_PG_CONFIG="NO" fi fi PG_CONFIG=$ac_cv_prog_PG_CONFIG if test -n "$PG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PG_CONFIG" >&5 $as_echo "$PG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test $PG_CONFIG = NO; then # based on code from PHP for i in /usr /usr/local /usr/local/pgsql; do for j in include include/pgsql include/postgres include/postgresql ""; do if test -r "$i/$j/libpq-fe.h"; then PGSQL_INCLUDE=$i/$j fi done for lib in lib lib64; do for j in $lib $lib/pgsql $lib/postgres $lib/postgresql ""; do if test -f "$i/$j/libpq.so" || test -f "$i/$j/libpq.a"; then PGSQL_LIBDIR=$i/$j fi done done done else PGSQL_INCLUDE="`$PG_CONFIG --includedir`" PGSQL_LIBDIR="`$PG_CONFIG --libdir`" fi old_LIBS=$LIBS if test "$PGSQL_LIBDIR" != ""; then LIBS="$LIBS -L$PGSQL_LIBDIR" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PQconnectdb in -lpq" >&5 $as_echo_n "checking for PQconnectdb in -lpq... " >&6; } if ${ac_cv_lib_pq_PQconnectdb+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpq $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char PQconnectdb (); int main () { return PQconnectdb (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pq_PQconnectdb=yes else ac_cv_lib_pq_PQconnectdb=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pq_PQconnectdb" >&5 $as_echo "$ac_cv_lib_pq_PQconnectdb" >&6; } if test "x$ac_cv_lib_pq_PQconnectdb" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PQescapeStringConn in -lpq" >&5 $as_echo_n "checking for PQescapeStringConn in -lpq... " >&6; } if ${ac_cv_lib_pq_PQescapeStringConn+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpq $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char PQescapeStringConn (); int main () { return PQescapeStringConn (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pq_PQescapeStringConn=yes else ac_cv_lib_pq_PQescapeStringConn=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pq_PQescapeStringConn" >&5 $as_echo "$ac_cv_lib_pq_PQescapeStringConn" >&6; } if test "x$ac_cv_lib_pq_PQescapeStringConn" = xyes; then : $as_echo "#define HAVE_PQESCAPE_STRING_CONN /**/" >>confdefs.h fi old_CPPFLAGS=$CPPFLAGS if test "$PGSQL_INCLUDE" != ""; then CPPFLAGS="$CPPFLAGS -I$PGSQL_INCLUDE" fi ac_fn_c_check_header_mongrel "$LINENO" "libpq-fe.h" "ac_cv_header_libpq_fe_h" "$ac_includes_default" if test "x$ac_cv_header_libpq_fe_h" = xyes; then : if test "$PGSQL_INCLUDE" != ""; then PGSQL_CFLAGS="$PGSQL_CFLAGS -I$PGSQL_INCLUDE" fi if test "$PGSQL_LIBDIR" != ""; then PGSQL_LIBS="$PGSQL_LIBS -L$PGSQL_LIBDIR" fi PGSQL_LIBS="$PGSQL_LIBS -lpq" $as_echo "#define HAVE_PGSQL /**/" >>confdefs.h found_sql_drivers="$found_sql_drivers pgsql" else if test $want_pgsql = yes; then as_fn_error $? "Can't build with PostgreSQL support: libpq-fe.h not found" "$LINENO" 5 fi fi CPPFLAGS=$old_CPPFLAGS else if test $want_pgsql = yes; then as_fn_error $? "Can't build with PostgreSQL support: libpq not found" "$LINENO" 5 fi fi LIBS=$old_LIBS fi have_mysql=no if test $want_mysql != no; then # Extract the first word of "mysql_config", so it can be a program name with args. set dummy mysql_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MYSQL_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MYSQL_CONFIG"; then ac_cv_prog_MYSQL_CONFIG="$MYSQL_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MYSQL_CONFIG="mysql_config" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_MYSQL_CONFIG" && ac_cv_prog_MYSQL_CONFIG="NO" fi fi MYSQL_CONFIG=$ac_cv_prog_MYSQL_CONFIG if test -n "$MYSQL_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MYSQL_CONFIG" >&5 $as_echo "$MYSQL_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test $MYSQL_CONFIG = NO; then # based on code from PHP MYSQL_LIBS="-lmysqlclient -lz -lm" for i in /usr /usr/local /usr/local/mysql; do for j in include include/mysql ""; do if test -r "$i/$j/mysql.h"; then MYSQL_INCLUDE="-I$i/$j" fi done for j in lib lib/mysql lib64 lib64/mysql ""; do if test -f "$i/$j/libmysqlclient.so" || test -f "$i/$j/libmysqlclient.a"; then MYSQL_LIBS="-L$i/$j -lmysqlclient -lz -lm" fi done done else MYSQL_INCLUDE="`$MYSQL_CONFIG --include`" MYSQL_LIBS="`$MYSQL_CONFIG --libs`" fi old_LIBS=$LIBS if test "$MYSQL_LIBS" != ""; then LIBS="$LIBS $MYSQL_LIBS" fi mysql_lib="" LIBS="$LIBS -lz -lm" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mysql_init in -lmysqlclient" >&5 $as_echo_n "checking for mysql_init in -lmysqlclient... " >&6; } if ${ac_cv_lib_mysqlclient_mysql_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lmysqlclient $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char mysql_init (); int main () { return mysql_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_mysqlclient_mysql_init=yes else ac_cv_lib_mysqlclient_mysql_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mysqlclient_mysql_init" >&5 $as_echo "$ac_cv_lib_mysqlclient_mysql_init" >&6; } if test "x$ac_cv_lib_mysqlclient_mysql_init" = xyes; then : old_CPPFLAGS=$CPPFLAGS if test "$MYSQL_INCLUDE" != ""; then CPPFLAGS="$CPPFLAGS $MYSQL_INCLUDE" fi ac_fn_c_check_header_mongrel "$LINENO" "mysql.h" "ac_cv_header_mysql_h" "$ac_includes_default" if test "x$ac_cv_header_mysql_h" = xyes; then : if test "$MYSQL_INCLUDE" != ""; then MYSQL_CFLAGS="$MYSQL_CFLAGS $MYSQL_INCLUDE" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mysql_ssl_set in -lmysqlclient" >&5 $as_echo_n "checking for mysql_ssl_set in -lmysqlclient... " >&6; } if ${ac_cv_lib_mysqlclient_mysql_ssl_set+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lmysqlclient $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char mysql_ssl_set (); int main () { return mysql_ssl_set (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_mysqlclient_mysql_ssl_set=yes else ac_cv_lib_mysqlclient_mysql_ssl_set=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mysqlclient_mysql_ssl_set" >&5 $as_echo "$ac_cv_lib_mysqlclient_mysql_ssl_set" >&6; } if test "x$ac_cv_lib_mysqlclient_mysql_ssl_set" = xyes; then : $as_echo "#define HAVE_MYSQL_SSL /**/" >>confdefs.h if test "x$have_openssl" = "yes"; then ssl_define="#define HAVE_OPENSSL" else ssl_define="" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ssl_define #include int main () { mysql_ssl_set(0, 0, 0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define HAVE_MYSQL_SSL_CIPHER /**/" >>confdefs.h cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ssl_define #include int main () { int i = MYSQL_OPT_SSL_VERIFY_SERVER_CERT; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define HAVE_MYSQL_SSL_VERIFY_SERVER_CERT /**/" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi have_mysql=yes $as_echo "#define HAVE_MYSQL /**/" >>confdefs.h found_sql_drivers="$found_sql_drivers mysql" else if test $want_mysql = yes; then as_fn_error $? "Can't build with MySQL support: mysql.h not found" "$LINENO" 5 fi fi CPPFLAGS=$old_CPPFLAGS else if test $want_mysql = yes; then as_fn_error $? "Can't build with MySQL support: libmysqlclient not found" "$LINENO" 5 fi fi if test $have_mysql != yes; then MYSQL_LIBS= MYSQL_CFLAGS= fi LIBS=$old_LIBS fi if test $want_sqlite != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open in -lsqlite3" >&5 $as_echo_n "checking for sqlite3_open in -lsqlite3... " >&6; } if ${ac_cv_lib_sqlite3_sqlite3_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sqlite3_open (); int main () { return sqlite3_open (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_sqlite3_sqlite3_open=yes else ac_cv_lib_sqlite3_sqlite3_open=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_open" >&5 $as_echo "$ac_cv_lib_sqlite3_sqlite3_open" >&6; } if test "x$ac_cv_lib_sqlite3_sqlite3_open" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default" if test "x$ac_cv_header_sqlite3_h" = xyes; then : SQLITE_LIBS="$SQLITE_LIBS -lsqlite3" $as_echo "#define HAVE_SQLITE /**/" >>confdefs.h found_sql_drivers="$found_sql_drivers sqlite" else if test $want_sqlite = yes; then as_fn_error $? "Can't build with SQLite support: sqlite3.h not found" "$LINENO" 5 fi fi else if test $want_sqlite = yes; then as_fn_error $? "Can't build with SQLite support: libsqlite3 not found" "$LINENO" 5 fi fi fi if test $want_cassandra != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cass_session_new in -lcassandra" >&5 $as_echo_n "checking for cass_session_new in -lcassandra... " >&6; } if ${ac_cv_lib_cassandra_cass_session_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcassandra $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cass_session_new (); int main () { return cass_session_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cassandra_cass_session_new=yes else ac_cv_lib_cassandra_cass_session_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cassandra_cass_session_new" >&5 $as_echo "$ac_cv_lib_cassandra_cass_session_new" >&6; } if test "x$ac_cv_lib_cassandra_cass_session_new" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "cassandra.h" "ac_cv_header_cassandra_h" "$ac_includes_default" if test "x$ac_cv_header_cassandra_h" = xyes; then : CASSANDRA_LIBS="$CASSANDRA_LIBS -lcassandra" $as_echo "#define HAVE_CASSANDRA /**/" >>confdefs.h found_sql_drivers="$found_sql_drivers cassandra" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cass_cluster_set_constant_speculative_execution_policy in -lcassandra" >&5 $as_echo_n "checking for cass_cluster_set_constant_speculative_execution_policy in -lcassandra... " >&6; } if ${ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcassandra $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cass_cluster_set_constant_speculative_execution_policy (); int main () { return cass_cluster_set_constant_speculative_execution_policy (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy=yes else ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy" >&5 $as_echo "$ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy" >&6; } if test "x$ac_cv_lib_cassandra_cass_cluster_set_constant_speculative_execution_policy" = xyes; then : $as_echo "#define HAVE_CASSANDRA_SPECULATIVE_POLICY 1" >>confdefs.h fi else if test $want_cassandra = yes; then as_fn_error $? "Can't build with Cassandra support: cassandra.h not found" "$LINENO" 5 fi fi else if test $want_cassandra = yes; then as_fn_error $? "Can't build with Cassandra support: libcassandra not found" "$LINENO" 5 fi fi fi SQL_CFLAGS="$MYSQL_CFLAGS $PGSQL_CFLAGS $SQLITE_CFLAGS $CASSANDRA_CFLAGS" if test "$want_sql" != "plugin"; then SQL_LIBS="$MYSQL_LIBS $PGSQL_LIBS $SQLITE_LIBS $CASSANDRA_LIBS" else $as_echo "#define SQL_DRIVER_PLUGINS /**/" >>confdefs.h fi sql_drivers= not_sql_drivers= if test "$found_sql_drivers" != "" || test "$want_sql" != "no"; then sql_drivers="$found_sql_drivers" $as_echo "#define PASSDB_SQL /**/" >>confdefs.h $as_echo "#define USERDB_SQL /**/" >>confdefs.h AUTH_LIBS="$AUTH_LIBS $SQL_LIBS" passdb="$passdb sql" userdb="$userdb sql" else not_passdb="$not_passdb sql" not_userdb="$not_userdb sql" fi have_vpopmail=no if test $want_vpopmail != no; then vpop_etc="$vpopmail_home/etc" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vpopmail configuration at $vpop_etc/lib_deps" >&5 $as_echo_n "checking for vpopmail configuration at $vpop_etc/lib_deps... " >&6; } if ! test -f $vpop_etc/lib_deps; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 $as_echo "not found" >&6; } vpop_etc="$vpopmail_home" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vpopmail configuration at $vpop_etc/lib_deps" >&5 $as_echo_n "checking for vpopmail configuration at $vpop_etc/lib_deps... " >&6; } fi if test -f $vpop_etc/lib_deps; then AUTH_CFLAGS="$AUTH_CFLAGS `cat $vpop_etc/inc_deps` $CFLAGS" AUTH_LIBS="$AUTH_LIBS `cat $vpop_etc/lib_deps`" $as_echo "#define USERDB_VPOPMAIL /**/" >>confdefs.h $as_echo "#define PASSDB_VPOPMAIL /**/" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 $as_echo "found" >&6; } have_vpopmail=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 $as_echo "not found" >&6; } if test $want_vpopmail = yes; then as_fn_error $? "Can't build with vpopmail support: $vpop_etc/lib_deps not found" "$LINENO" 5 fi fi fi if test $have_vpopmail = no; then not_passdb="$not_passdb vpopmail" not_userdb="$not_userdb vpopmail" else userdb="$userdb vpopmail" passdb="$passdb vpopmail" fi ac_fn_c_check_func "$LINENO" "crypt" "ac_cv_func_crypt" if test "x$ac_cv_func_crypt" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5 $as_echo_n "checking for crypt in -lcrypt... " >&6; } if ${ac_cv_lib_crypt_crypt+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char crypt (); int main () { return crypt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypt_crypt=yes else ac_cv_lib_crypt_crypt=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt" >&5 $as_echo "$ac_cv_lib_crypt_crypt" >&6; } if test "x$ac_cv_lib_crypt_crypt" = xyes; then : AUTH_LIBS="-lcrypt $AUTH_LIBS" CRYPT_LIBS="-lcrypt" else as_fn_error $? "crypt() wasn't found" "$LINENO" 5 fi fi if test $have_modules = yes; then $as_echo "#define HAVE_MODULES /**/" >>confdefs.h # shrext_cmds comes from libtool.m4 module=yes eval MODULE_SUFFIX=$shrext_cmds if test "$MODULE_SUFFIX" = ""; then # too old libtool? MODULE_SUFFIX=.so fi cat >>confdefs.h <<_ACEOF #define MODULE_SUFFIX "$MODULE_SUFFIX" _ACEOF fi have_nss=no if test $want_nss != no; then if test $have_modules != yes; then if test $want_nss = yes; then as_fn_error $? "Can't build with NSS support: Dynamic modules not supported" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NSS support" >&5 $as_echo_n "checking for NSS support... " >&6; } if ${i_cv_have_nss+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { enum nss_status status = NSS_STATUS_TRYAGAIN; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have_nss=yes else i_cv_have_nss=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have_nss" >&5 $as_echo "$i_cv_have_nss" >&6; } if test $i_cv_have_nss = yes; then $as_echo "#define USERDB_NSS /**/" >>confdefs.h have_nss=yes else if test $want_nss = yes; then as_fn_error $? "Can't build with NSS support: nss.h not found or not usable" "$LINENO" 5 fi fi fi fi if test $have_nss = no; then not_userdb="$not_userdb nss" else userdb="$userdb nss" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if __BIG_ENDIAN__ or __LITTLE_ENDIAN__ is defined" >&5 $as_echo_n "checking if __BIG_ENDIAN__ or __LITTLE_ENDIAN__ is defined... " >&6; } if ${i_cv_have___big_endian__+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if !(__BIG_ENDIAN__ || __LITTLE_ENDIAN__) #error nope #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_have___big_endian__=yes else i_cv_have___big_endian__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_have___big_endian__" >&5 $as_echo "$i_cv_have___big_endian__" >&6; } if test $i_cv_have___big_endian__ = yes; then $as_echo "#define WORDS_BIGENDIAN __BIG_ENDIAN__" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac fi have_ipv6=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for IPv6" >&5 $as_echo_n "checking for IPv6... " >&6; } if ${i_cv_type_in6_addr+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include int main () { struct in6_addr i; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : i_cv_type_in6_addr=yes else i_cv_type_in6_addr=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $i_cv_type_in6_addr = yes; then $as_echo "#define HAVE_IPV6 /**/" >>confdefs.h have_ipv6=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $i_cv_type_in6_addr" >&5 $as_echo "$i_cv_type_in6_addr" >&6; } maildir_libs='$(top_builddir)/src/lib-storage/index/maildir/libstorage_maildir.la' mbox_libs='$(top_builddir)/src/lib-storage/index/mbox/libstorage_mbox.la' dbox_common_libs='$(top_builddir)/src/lib-storage/index/dbox-common/libstorage_dbox_common.la' sdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-single/libstorage_dbox_single.la' mdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-multi/libstorage_dbox_multi.la' cydir_libs='$(top_builddir)/src/lib-storage/index/cydir/libstorage_cydir.la' imapc_libs='$(top_builddir)/src/lib-storage/index/imapc/libstorage_imapc.la $(top_builddir)/src/lib-imap-client/libimap_client.la' pop3c_libs='$(top_builddir)/src/lib-storage/index/pop3c/libstorage_pop3c.la' raw_libs='$(top_builddir)/src/lib-storage/index/raw/libstorage_raw.la' shared_libs='$(top_builddir)/src/lib-storage/index/shared/libstorage_shared.la' CORE_LIBS='$(top_builddir)/src/lib-dovecot/libdovecot.la' STORAGE_LIB='$(top_builddir)/src/lib-storage/libdovecot-storage.la' LINKED_STORAGE_LIBS= mailbox_list_drivers="maildir imapdir fs index none shared" have_sdbox=no have_mdbox=no for storage in $mail_storages; do LINKED_STORAGE_LIBS="$LINKED_STORAGE_LIBS `eval echo \\$${storage}_libs`" if test $storage = sdbox; then have_sdbox=yes fi if test $storage = mdbox; then have_mdbox=yes fi if test $storage = sdbox || test $storage = mdbox; then LINKED_STORAGE_LIBS="$LINKED_STORAGE_LIBS $dbox_common_libs" dbox_common_libs="" fi if test $storage = imapc; then mailbox_list_drivers="$mailbox_list_drivers imapc" fi done LINKED_STORAGE_LDADD= cat >>confdefs.h <<_ACEOF #define MAIL_STORAGES "$mail_storages" _ACEOF if test $have_sdbox = yes; then # create alias for sdbox mail_storages="$mail_storages dbox" fi if test $have_mdbox = yes; then mail_storages="$mail_storages mdbox_deleted" fi LIBDOVECOT_LA_LIBS='$(top_builddir)/src/lib-dict-extra/libdict_extra.la $(top_builddir)/src/lib-oauth2/liboauth2.la $(top_builddir)/src/lib-program-client/libprogram_client.la $(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-stats/libstats.la $(top_builddir)/src/lib-http/libhttp.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-smtp/libsmtp.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-sasl/libsasl.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-dcrypt/libdcrypt.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la' if test "$want_shared_libs" = "yes"; then LIBDOVECOT_DEPS='$(top_builddir)/src/lib-dovecot/libdovecot.la' LIBDOVECOT="$LIBDOVECOT_DEPS \$(MODULE_LIBS)" LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libdovecot-storage.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la' LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la' LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la' else LIBDOVECOT_DEPS="$LIBDOVECOT_LA_LIBS" LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV) \$(MODULE_LIBS)" LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libstorage.la' LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la' LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/liblda.la' fi if test $want_ldap != no; then LIBDOVECOT_LDAP='$(top_builddir)/src/lib-ldap/libdovecot-ldap.la' else LIBDOVECOT_LDAP='' fi LIBDOVECOT_STORAGE="$LIBDOVECOT_STORAGE_DEPS" LIBDOVECOT_DSYNC='$(top_builddir)/src/doveadm/dsync/libdovecot-dsync.la' LIBDOVECOT_SQL='$(top_builddir)/src/lib-sql/libsql.la' LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la' LIBDOVECOT_LIBFTS='$(top_builddir)/src/lib-fts/libfts.la' build_pgsql=no build_mysql=no build_sqlite=no build_cassandra=no for driver in $sql_drivers; do if test "$driver" = "pgsql"; then $as_echo "#define BUILD_PGSQL /**/" >>confdefs.h build_pgsql=yes elif test "$driver" = "mysql"; then $as_echo "#define BUILD_MYSQL /**/" >>confdefs.h build_mysql=yes elif test "$driver" = "sqlite"; then $as_echo "#define BUILD_SQLITE /**/" >>confdefs.h build_sqlite=yes elif test "$driver" = "cassandra"; then $as_echo "#define BUILD_CASSANDRA /**/" >>confdefs.h build_cassandra=yes fi done if test $build_pgsql = no; then not_sql_drivers="$not_sql_drivers pgsql" fi if test $build_mysql = no; then not_sql_drivers="$not_sql_drivers mysql" fi if test $build_sqlite = no; then not_sql_drivers="$not_sql_drivers sqlite" fi if test $build_cassandra = no; then not_sql_drivers="$not_sql_drivers cassandra" fi if test "$build_pgsql" = "yes"; then BUILD_PGSQL_TRUE= BUILD_PGSQL_FALSE='#' else BUILD_PGSQL_TRUE='#' BUILD_PGSQL_FALSE= fi if test "$build_mysql" = "yes"; then BUILD_MYSQL_TRUE= BUILD_MYSQL_FALSE='#' else BUILD_MYSQL_TRUE='#' BUILD_MYSQL_FALSE= fi if test "$build_sqlite" = "yes"; then BUILD_SQLITE_TRUE= BUILD_SQLITE_FALSE='#' else BUILD_SQLITE_TRUE='#' BUILD_SQLITE_FALSE= fi if test "$build_cassandra" = "yes"; then BUILD_CASSANDRA_TRUE= BUILD_CASSANDRA_FALSE='#' else BUILD_CASSANDRA_TRUE='#' BUILD_CASSANDRA_FALSE= fi if test "$want_sql" = "plugin"; then SQL_PLUGINS_TRUE= SQL_PLUGINS_FALSE='#' else SQL_PLUGINS_TRUE='#' SQL_PLUGINS_FALSE= fi COMPRESS_LIBS= if test "$want_zlib" != "no"; then ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = xyes; then : have_zlib=yes have_compress_lib=yes $as_echo "#define HAVE_ZLIB /**/" >>confdefs.h COMPRESS_LIBS="$COMPRESS_LIBS -lz" else if test "$want_zlib" = "yes"; then as_fn_error $? "Can't build with zlib support: zlib.h not found" "$LINENO" 5 fi fi fi if test "$want_bzlib" != "no"; then ac_fn_c_check_header_mongrel "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" if test "x$ac_cv_header_bzlib_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzdopen in -lbz2" >&5 $as_echo_n "checking for BZ2_bzdopen in -lbz2... " >&6; } if ${ac_cv_lib_bz2_BZ2_bzdopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char BZ2_bzdopen (); int main () { return BZ2_bzdopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_bz2_BZ2_bzdopen=yes else ac_cv_lib_bz2_BZ2_bzdopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzdopen" >&5 $as_echo "$ac_cv_lib_bz2_BZ2_bzdopen" >&6; } if test "x$ac_cv_lib_bz2_BZ2_bzdopen" = xyes; then : have_bzlib=yes have_compress_lib=yes $as_echo "#define HAVE_BZLIB /**/" >>confdefs.h COMPRESS_LIBS="$COMPRESS_LIBS -lbz2" else if test "$want_bzlib" = "yes"; then as_fn_error $? "Can't build with bzlib support: libbz2 not found" "$LINENO" 5 fi fi else if test "$want_bzlib" = "yes"; then as_fn_error $? "Can't build with bzlib support: bzlib.h not found" "$LINENO" 5 fi fi fi if test "$want_lzma" != "no"; then ac_fn_c_check_header_mongrel "$LINENO" "lzma.h" "ac_cv_header_lzma_h" "$ac_includes_default" if test "x$ac_cv_header_lzma_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzma_stream_decoder in -llzma" >&5 $as_echo_n "checking for lzma_stream_decoder in -llzma... " >&6; } if ${ac_cv_lib_lzma_lzma_stream_decoder+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llzma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lzma_stream_decoder (); int main () { return lzma_stream_decoder (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lzma_lzma_stream_decoder=yes else ac_cv_lib_lzma_lzma_stream_decoder=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_stream_decoder" >&5 $as_echo "$ac_cv_lib_lzma_lzma_stream_decoder" >&6; } if test "x$ac_cv_lib_lzma_lzma_stream_decoder" = xyes; then : have_lzma=yes have_compress_lib=yes $as_echo "#define HAVE_LZMA /**/" >>confdefs.h COMPRESS_LIBS="$COMPRESS_LIBS -llzma" else if test "$want_lzma" = "yes"; then as_fn_error $? "Can't build with lzma support: liblzma not found" "$LINENO" 5 fi fi else if test "$want_lzma" = "yes"; then as_fn_error $? "Can't build with lzma support: lzma.h not found" "$LINENO" 5 fi fi fi if test "$want_lz4" != "no"; then ac_fn_c_check_header_mongrel "$LINENO" "lz4.h" "ac_cv_header_lz4_h" "$ac_includes_default" if test "x$ac_cv_header_lz4_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LZ4_compress in -llz4" >&5 $as_echo_n "checking for LZ4_compress in -llz4... " >&6; } if ${ac_cv_lib_lz4_LZ4_compress+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llz4 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char LZ4_compress (); int main () { return LZ4_compress (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lz4_LZ4_compress=yes else ac_cv_lib_lz4_LZ4_compress=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lz4_LZ4_compress" >&5 $as_echo "$ac_cv_lib_lz4_LZ4_compress" >&6; } if test "x$ac_cv_lib_lz4_LZ4_compress" = xyes; then : have_lz4=yes have_compress_lib=yes $as_echo "#define HAVE_LZ4 /**/" >>confdefs.h COMPRESS_LIBS="$COMPRESS_LIBS -llz4" else if test "$want_lz4" = "yes"; then as_fn_error $? "Can't build with lz4 support: liblz4 not found" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LZ4_compress_default in -llz4" >&5 $as_echo_n "checking for LZ4_compress_default in -llz4... " >&6; } if ${ac_cv_lib_lz4_LZ4_compress_default+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llz4 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char LZ4_compress_default (); int main () { return LZ4_compress_default (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lz4_LZ4_compress_default=yes else ac_cv_lib_lz4_LZ4_compress_default=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lz4_LZ4_compress_default" >&5 $as_echo "$ac_cv_lib_lz4_LZ4_compress_default" >&6; } if test "x$ac_cv_lib_lz4_LZ4_compress_default" = xyes; then : $as_echo "#define HAVE_LZ4_COMPRESS_DEFAULT /**/" >>confdefs.h fi else if test "$want_lz4" = "yes"; then as_fn_error $? "Can't build with lz4 support: lz4.h not found" "$LINENO" 5 fi fi fi if test "$have_compress_lib" = "yes"; then BUILD_ZLIB_PLUGIN_TRUE= BUILD_ZLIB_PLUGIN_FALSE='#' else BUILD_ZLIB_PLUGIN_TRUE='#' BUILD_ZLIB_PLUGIN_FALSE= fi RPCGEN=${RPCGEN-rpcgen} if ! $RPCGEN -c /dev/null > /dev/null; then RPCGEN= fi have_rquota=no if test -f /usr/include/rpcsvc/rquota.x && test -n "$RPCGEN"; then $as_echo "#define HAVE_RQUOTA /**/" >>confdefs.h have_rquota=yes fi if test "$have_rquota" = "yes"; then HAVE_RQUOTA_TRUE= HAVE_RQUOTA_FALSE='#' else HAVE_RQUOTA_TRUE='#' HAVE_RQUOTA_FALSE= fi QUOTA_LIBS="" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing quota_open" >&5 $as_echo_n "checking for library containing quota_open... " >&6; } if ${ac_cv_search_quota_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char quota_open (); int main () { return quota_open (); ; return 0; } _ACEOF for ac_lib in '' quota; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_quota_open=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_quota_open+:} false; then : break fi done if ${ac_cv_search_quota_open+:} false; then : else ac_cv_search_quota_open=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_quota_open" >&5 $as_echo "$ac_cv_search_quota_open" >&6; } ac_res=$ac_cv_search_quota_open if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_QUOTA_OPEN /**/" >>confdefs.h QUOTA_LIBS="-lquota" fi fts=" squat" not_fts="" have_solr=no if test "$want_solr" != "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XML_Parse in -lexpat" >&5 $as_echo_n "checking for XML_Parse in -lexpat... " >&6; } if ${ac_cv_lib_expat_XML_Parse+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lexpat $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char XML_Parse (); int main () { return XML_Parse (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_expat_XML_Parse=yes else ac_cv_lib_expat_XML_Parse=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_expat_XML_Parse" >&5 $as_echo "$ac_cv_lib_expat_XML_Parse" >&6; } if test "x$ac_cv_lib_expat_XML_Parse" = xyes; then : ac_fn_c_check_header_mongrel "$LINENO" "expat.h" "ac_cv_header_expat_h" "$ac_includes_default" if test "x$ac_cv_header_expat_h" = xyes; then : have_solr=yes fts="$fts solr" else if test $want_solr = yes; then as_fn_error $? "Can't build with Solr support: expat.h not found" "$LINENO" 5 fi fi else if test $want_solr = yes; then as_fn_error $? "Can't build with Solr support: libexpat not found" "$LINENO" 5 fi fi fi if test "$have_solr" = "yes"; then BUILD_SOLR_TRUE= BUILD_SOLR_FALSE='#' else BUILD_SOLR_TRUE='#' BUILD_SOLR_FALSE= fi have_lucene=no if test "$want_lucene" = "yes"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CLUCENE" >&5 $as_echo_n "checking for CLUCENE... " >&6; } if test -n "$CLUCENE_CFLAGS"; then pkg_cv_CLUCENE_CFLAGS="$CLUCENE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libclucene-core\""; } >&5 ($PKG_CONFIG --exists --print-errors "libclucene-core") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CLUCENE_CFLAGS=`$PKG_CONFIG --cflags "libclucene-core" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$CLUCENE_LIBS"; then pkg_cv_CLUCENE_LIBS="$CLUCENE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libclucene-core\""; } >&5 ($PKG_CONFIG --exists --print-errors "libclucene-core") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CLUCENE_LIBS=`$PKG_CONFIG --libs "libclucene-core" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then CLUCENE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libclucene-core" 2>&1` else CLUCENE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libclucene-core" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$CLUCENE_PKG_ERRORS" >&5 # no pkg-config file for clucene. fallback to defaults. # FIXME: we should verify here that this actually works.. CLUCENE_LIBS="-lclucene-shared -lclucene-core" elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } # no pkg-config file for clucene. fallback to defaults. # FIXME: we should verify here that this actually works.. CLUCENE_LIBS="-lclucene-shared -lclucene-core" else CLUCENE_CFLAGS=$pkg_cv_CLUCENE_CFLAGS CLUCENE_LIBS=$pkg_cv_CLUCENE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi have_lucene=yes fts="$fts lucene" fi if test $want_stemmer != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sb_stemmer_new in -lstemmer" >&5 $as_echo_n "checking for sb_stemmer_new in -lstemmer... " >&6; } if ${ac_cv_lib_stemmer_sb_stemmer_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lstemmer $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sb_stemmer_new (); int main () { return sb_stemmer_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_stemmer_sb_stemmer_new=yes else ac_cv_lib_stemmer_sb_stemmer_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_stemmer_sb_stemmer_new" >&5 $as_echo "$ac_cv_lib_stemmer_sb_stemmer_new" >&6; } if test "x$ac_cv_lib_stemmer_sb_stemmer_new" = xyes; then : have_fts_stemmer=yes $as_echo "#define HAVE_FTS_STEMMER /**/" >>confdefs.h else if test $want_stemmer = yes; then as_fn_error $? "Can't build with stemmer support: libstemmer not found" "$LINENO" 5 fi fi fi if test "$have_fts_stemmer" = "yes"; then BUILD_FTS_STEMMER_TRUE= BUILD_FTS_STEMMER_FALSE='#' else BUILD_FTS_STEMMER_TRUE='#' BUILD_FTS_STEMMER_FALSE= fi if test $want_textcat != no; then if test "$PKG_CONFIG" != "" && $PKG_CONFIG --exists libexttextcat 2>/dev/null; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEXTTEXTCAT" >&5 $as_echo_n "checking for LIBEXTTEXTCAT... " >&6; } if test -n "$LIBEXTTEXTCAT_CFLAGS"; then pkg_cv_LIBEXTTEXTCAT_CFLAGS="$LIBEXTTEXTCAT_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libexttextcat\""; } >&5 ($PKG_CONFIG --exists --print-errors "libexttextcat") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEXTTEXTCAT_CFLAGS=`$PKG_CONFIG --cflags "libexttextcat" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEXTTEXTCAT_LIBS"; then pkg_cv_LIBEXTTEXTCAT_LIBS="$LIBEXTTEXTCAT_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libexttextcat\""; } >&5 ($PKG_CONFIG --exists --print-errors "libexttextcat") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEXTTEXTCAT_LIBS=`$PKG_CONFIG --libs "libexttextcat" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEXTTEXTCAT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libexttextcat" 2>&1` else LIBEXTTEXTCAT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libexttextcat" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEXTTEXTCAT_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libexttextcat) were not met: $LIBEXTTEXTCAT_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBEXTTEXTCAT_CFLAGS and LIBEXTTEXTCAT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBEXTTEXTCAT_CFLAGS and LIBEXTTEXTCAT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LIBEXTTEXTCAT_CFLAGS=$pkg_cv_LIBEXTTEXTCAT_CFLAGS LIBEXTTEXTCAT_LIBS=$pkg_cv_LIBEXTTEXTCAT_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi TEXTCAT_DATADIR=`$PKG_CONFIG --variable=pkgdatadir libexttextcat` $as_echo "#define HAVE_FTS_EXTTEXTCAT /**/" >>confdefs.h have_fts_exttextcat=yes # Debian Wheezy workaround - LIBEXTTEXTCAT_CFLAGS include path is wrong: for ac_header in libexttextcat/textcat.h do : ac_fn_c_check_header_mongrel "$LINENO" "libexttextcat/textcat.h" "ac_cv_header_libexttextcat_textcat_h" "$ac_includes_default" if test "x$ac_cv_header_libexttextcat_textcat_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBEXTTEXTCAT_TEXTCAT_H 1 _ACEOF fi done else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special_textcat_Init in -lexttextcat" >&5 $as_echo_n "checking for special_textcat_Init in -lexttextcat... " >&6; } if ${ac_cv_lib_exttextcat_special_textcat_Init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lexttextcat $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char special_textcat_Init (); int main () { return special_textcat_Init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_exttextcat_special_textcat_Init=yes else ac_cv_lib_exttextcat_special_textcat_Init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_exttextcat_special_textcat_Init" >&5 $as_echo "$ac_cv_lib_exttextcat_special_textcat_Init" >&6; } if test "x$ac_cv_lib_exttextcat_special_textcat_Init" = xyes; then : have_fts_exttextcat=yes for ac_header in libexttextcat/textcat.h do : ac_fn_c_check_header_mongrel "$LINENO" "libexttextcat/textcat.h" "ac_cv_header_libexttextcat_textcat_h" "$ac_includes_default" if test "x$ac_cv_header_libexttextcat_textcat_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBEXTTEXTCAT_TEXTCAT_H 1 _ACEOF fi done LIBEXTTEXTCAT_LIBS=-lexttextcat $as_echo "#define HAVE_FTS_EXTTEXTCAT /**/" >>confdefs.h TEXTCAT_DATADIR="/usr/share/libexttextcat" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special_textcat_Init in -ltextcat" >&5 $as_echo_n "checking for special_textcat_Init in -ltextcat... " >&6; } if ${ac_cv_lib_textcat_special_textcat_Init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltextcat $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char special_textcat_Init (); int main () { return special_textcat_Init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_textcat_special_textcat_Init=yes else ac_cv_lib_textcat_special_textcat_Init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_textcat_special_textcat_Init" >&5 $as_echo "$ac_cv_lib_textcat_special_textcat_Init" >&6; } if test "x$ac_cv_lib_textcat_special_textcat_Init" = xyes; then : have_fts_textcat=yes TEXTCAT_DATADIR="/usr/share/libtextcat" for ac_header in libtextcat/textcat.h do : ac_fn_c_check_header_mongrel "$LINENO" "libtextcat/textcat.h" "ac_cv_header_libtextcat_textcat_h" "$ac_includes_default" if test "x$ac_cv_header_libtextcat_textcat_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBTEXTCAT_TEXTCAT_H 1 _ACEOF fi done fi fi if test $want_textcat = yes && test "$have_fts_exttextcat" != yes && test "$have_fts_textcat" != yes; then as_fn_error $? "Can't build with textcat support: libtextcat or libexttextcat not found" "$LINENO" 5 fi fi if test "$have_fts_exttextcat" = yes || test "$have_fts_textcat" = yes; then $as_echo "#define HAVE_FTS_TEXTCAT /**/" >>confdefs.h cat >>confdefs.h <<_ACEOF #define TEXTCAT_DATADIR "$TEXTCAT_DATADIR" _ACEOF fi fi if test "$have_fts_textcat" = "yes"; then BUILD_FTS_TEXTCAT_TRUE= BUILD_FTS_TEXTCAT_FALSE='#' else BUILD_FTS_TEXTCAT_TRUE='#' BUILD_FTS_TEXTCAT_FALSE= fi if test "$have_fts_exttextcat" = "yes"; then BUILD_FTS_EXTTEXTCAT_TRUE= BUILD_FTS_EXTTEXTCAT_FALSE='#' else BUILD_FTS_EXTTEXTCAT_TRUE='#' BUILD_FTS_EXTTEXTCAT_FALSE= fi if test "$want_icu" != "no"; then if test "$PKG_CONFIG" != "" && $PKG_CONFIG --exists icu-i18n 2>/dev/null; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBICU" >&5 $as_echo_n "checking for LIBICU... " >&6; } if test -n "$LIBICU_CFLAGS"; then pkg_cv_LIBICU_CFLAGS="$LIBICU_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"icu-i18n\""; } >&5 ($PKG_CONFIG --exists --print-errors "icu-i18n") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBICU_CFLAGS=`$PKG_CONFIG --cflags "icu-i18n" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBICU_LIBS"; then pkg_cv_LIBICU_LIBS="$LIBICU_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"icu-i18n\""; } >&5 ($PKG_CONFIG --exists --print-errors "icu-i18n") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBICU_LIBS=`$PKG_CONFIG --libs "icu-i18n" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBICU_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "icu-i18n" 2>&1` else LIBICU_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "icu-i18n" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBICU_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (icu-i18n) were not met: $LIBICU_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBICU_CFLAGS and LIBICU_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBICU_CFLAGS and LIBICU_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LIBICU_CFLAGS=$pkg_cv_LIBICU_CFLAGS LIBICU_LIBS=$pkg_cv_LIBICU_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi have_icu=yes $as_echo "#define HAVE_LIBICU /**/" >>confdefs.h elif test "$want_icu" = "yes"; then as_fn_error $? "Can't build with libicu support: libicu-i18n not found" "$LINENO" 5 fi fi if test "$have_icu" = "yes"; then BUILD_LIBICU_TRUE= BUILD_LIBICU_FALSE='#' else BUILD_LIBICU_TRUE='#' BUILD_LIBICU_FALSE= fi want_apparmor=auto # Check whether --with-apparmor was given. if test "${with_apparmor+set}" = set; then : withval=$with_apparmor; want_apparmor=$withval fi have_apparmor=no if test $want_apparmor != no; then ac_fn_c_check_header_mongrel "$LINENO" "sys/apparmor.h" "ac_cv_header_sys_apparmor_h" "$ac_includes_default" if test "x$ac_cv_header_sys_apparmor_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for aa_change_hat in -lapparmor" >&5 $as_echo_n "checking for aa_change_hat in -lapparmor... " >&6; } if ${ac_cv_lib_apparmor_aa_change_hat+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lapparmor $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char aa_change_hat (); int main () { return aa_change_hat (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_apparmor_aa_change_hat=yes else ac_cv_lib_apparmor_aa_change_hat=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_apparmor_aa_change_hat" >&5 $as_echo "$ac_cv_lib_apparmor_aa_change_hat" >&6; } if test "x$ac_cv_lib_apparmor_aa_change_hat" = xyes; then : have_apparmor=yes APPARMOR_LIBS=-lapparmor fi fi fi if test $want_apparmor = yes; then if test $have_apparmor = no; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "apparmor was not found See \`config.log' for more details" "$LINENO" 5; } fi fi if test "$have_apparmor" = "yes"; then HAVE_APPARMOR_TRUE= HAVE_APPARMOR_FALSE='#' else HAVE_APPARMOR_TRUE='#' HAVE_APPARMOR_FALSE= fi want_apparmor=auto # Check whether --with-apparmor was given. if test "${with_apparmor+set}" = set; then : withval=$with_apparmor; want_apparmor=$withval fi have_apparmor=no if test $want_apparmor != no; then ac_fn_c_check_header_mongrel "$LINENO" "sys/apparmor.h" "ac_cv_header_sys_apparmor_h" "$ac_includes_default" if test "x$ac_cv_header_sys_apparmor_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for aa_change_hat in -lapparmor" >&5 $as_echo_n "checking for aa_change_hat in -lapparmor... " >&6; } if ${ac_cv_lib_apparmor_aa_change_hat+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lapparmor $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char aa_change_hat (); int main () { return aa_change_hat (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_apparmor_aa_change_hat=yes else ac_cv_lib_apparmor_aa_change_hat=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_apparmor_aa_change_hat" >&5 $as_echo "$ac_cv_lib_apparmor_aa_change_hat" >&6; } if test "x$ac_cv_lib_apparmor_aa_change_hat" = xyes; then : have_apparmor=yes APPARMOR_LIBS=-lapparmor fi fi fi if test $want_apparmor = yes; then if test $have_apparmor = no; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "apparmor was not found See \`config.log' for more details" "$LINENO" 5; } fi fi if test "$have_apparmor" = "yes"; then HAVE_APPARMOR_TRUE= HAVE_APPARMOR_FALSE='#' else HAVE_APPARMOR_TRUE='#' HAVE_APPARMOR_FALSE= fi if test $have_lucene = no; then not_fts="$not_fts lucene" fi if test $have_solr = no; then not_fts="$not_fts solr" fi FILES1=`find $srcdir/src -name '*settings.[ch]'|grep "$srcdir/src/lib-" | sed 's/^\(.*\)\(.\)$/\2 \1\2/' | grep -v 'lib-master.*c$' | sort -r | sed s/^..//` FILES2=`find $srcdir/src -name '*settings.[ch]'|grep -v "$srcdir/src/lib-" | sed 's/^\(.*\)\(.\)$/\2 \1\2/' | grep -v all-settings | sort -r | sed s/^..//` SETTING_FILES=`echo $FILES1 $FILES2 | sed -e s,$srcdir/src,./src,g -e 's,./src,$(top_srcdir)/src,g'` capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE" capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE" cat >>confdefs.h <<_ACEOF #define CAPABILITY_STRING "$capability" _ACEOF cat >>confdefs.h <<_ACEOF #define CAPABILITY_BANNER_STRING "$capability_banner" _ACEOF CFLAGS="$CFLAGS $EXTRA_CFLAGS" NOPLUGIN_LDFLAGS="-no-undefined" if test "$with_gnu_ld" = yes; then NOPLUGIN_LDFLAGS="$NOPLUGIN_LDFLAGS -Wl,--as-needed" fi LDFLAGS="\$(NOPLUGIN_LDFLAGS) $LDFLAGS" if test "$want_shared_libs" != "yes"; then # want_shared_libs=no is for internal use. the liblib.la check is for plugins if test "$want_shared_libs" = "no" || echo "$LIBDOVECOT" | grep "/liblib.la" > /dev/null; then if test "$with_gnu_ld" = yes; then # libtool can't handle using whole-archive flags, so we need to do this # with a CC wrapper.. shouldn't be much of a problem, since most people # are building with shared libs. cat > cc-wrapper.sh < /dev/null; then # the binary uses plugins. make sure we include everything from .a libs exec $CC -Wl,--whole-archive \$* -Wl,--no-whole-archive else exec $CC \$* fi EOF chmod +x cc-wrapper.sh CC=`pwd`/cc-wrapper.sh fi fi fi if test "$docdir" = ""; then docdir='${datadir}/doc/${PACKAGE_TARNAME}' fi # Extract the first word of "valgrind", so it can be a program name with args. set dummy valgrind; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_VALGRIND+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$VALGRIND"; then ac_cv_prog_VALGRIND="$VALGRIND" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_VALGRIND="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_VALGRIND" && ac_cv_prog_VALGRIND="no" fi fi VALGRIND=$ac_cv_prog_VALGRIND if test -n "$VALGRIND"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $VALGRIND" >&5 $as_echo "$VALGRIND" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test $VALGRIND = yes; then cat > run-test.sh <&2 fi exit \$ret EOF RUN_TEST='$(SHELL) $(top_builddir)/run-test.sh $(top_srcdir)' else RUN_TEST='' fi ac_config_headers="$ac_config_headers config.h" ac_config_files="$ac_config_files Makefile doc/Makefile doc/man/Makefile doc/wiki/Makefile doc/example-config/Makefile doc/example-config/conf.d/Makefile src/Makefile src/lib/Makefile src/lib-sql/Makefile src/lib-auth/Makefile src/lib-charset/Makefile src/lib-compression/Makefile src/lib-dcrypt/Makefile src/lib-dict/Makefile src/lib-dict-extra/Makefile src/lib-dns/Makefile src/lib-fs/Makefile src/lib-fts/Makefile src/lib-http/Makefile src/lib-oauth2/Makefile src/lib-imap/Makefile src/lib-imap-storage/Makefile src/lib-imap-client/Makefile src/lib-imap-urlauth/Makefile src/lib-index/Makefile src/lib-lda/Makefile src/lib-ldap/Makefile src/lib-mail/Makefile src/lib-master/Makefile src/lib-ntlm/Makefile src/lib-program-client/Makefile src/lib-otp/Makefile src/lib-dovecot/Makefile src/lib-sasl/Makefile src/lib-settings/Makefile src/lib-smtp/Makefile src/lib-ssl-iostream/Makefile src/lib-stats/Makefile src/lib-test/Makefile src/lib-storage/Makefile src/lib-storage/list/Makefile src/lib-storage/index/Makefile src/lib-storage/index/imapc/Makefile src/lib-storage/index/pop3c/Makefile src/lib-storage/index/maildir/Makefile src/lib-storage/index/mbox/Makefile src/lib-storage/index/dbox-common/Makefile src/lib-storage/index/dbox-multi/Makefile src/lib-storage/index/dbox-single/Makefile src/lib-storage/index/cydir/Makefile src/lib-storage/index/raw/Makefile src/lib-storage/index/shared/Makefile src/lib-storage/register/Makefile src/anvil/Makefile src/auth/Makefile src/config/Makefile src/doveadm/Makefile src/doveadm/dsync/Makefile src/lda/Makefile src/log/Makefile src/lmtp/Makefile src/dict/Makefile src/director/Makefile src/dns/Makefile src/indexer/Makefile src/ipc/Makefile src/imap/Makefile src/imap-hibernate/Makefile src/imap-login/Makefile src/imap-urlauth/Makefile src/login-common/Makefile src/master/Makefile src/pop3/Makefile src/pop3-login/Makefile src/replication/Makefile src/replication/aggregator/Makefile src/replication/replicator/Makefile src/ssl-params/Makefile src/stats/Makefile src/util/Makefile src/plugins/Makefile src/plugins/acl/Makefile src/plugins/imap-acl/Makefile src/plugins/autocreate/Makefile src/plugins/dict-ldap/Makefile src/plugins/expire/Makefile src/plugins/fs-compress/Makefile src/plugins/fts/Makefile src/plugins/fts-lucene/Makefile src/plugins/fts-solr/Makefile src/plugins/fts-squat/Makefile src/plugins/last-login/Makefile src/plugins/lazy-expunge/Makefile src/plugins/listescape/Makefile src/plugins/mail-filter/Makefile src/plugins/mail-log/Makefile src/plugins/mailbox-alias/Makefile src/plugins/notify/Makefile src/plugins/notify-status/Makefile src/plugins/push-notification/Makefile src/plugins/pop3-migration/Makefile src/plugins/quota/Makefile src/plugins/quota-clone/Makefile src/plugins/imap-quota/Makefile src/plugins/replication/Makefile src/plugins/snarf/Makefile src/plugins/stats/Makefile src/plugins/imap-stats/Makefile src/plugins/trash/Makefile src/plugins/virtual/Makefile src/plugins/welcome/Makefile src/plugins/zlib/Makefile src/plugins/imap-zlib/Makefile src/plugins/mail-crypt/Makefile src/plugins/var-expand-crypt/Makefile src/plugins/apparmor/Makefile stamp.h dovecot-config.in" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_SHARED_LIBS_TRUE}" && test -z "${BUILD_SHARED_LIBS_FALSE}"; then as_fn_error $? "conditional \"BUILD_SHARED_LIBS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_LUCENE_TRUE}" && test -z "${BUILD_LUCENE_FALSE}"; then as_fn_error $? "conditional \"BUILD_LUCENE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_SYSTEMD_TRUE}" && test -z "${HAVE_SYSTEMD_FALSE}"; then as_fn_error $? "conditional \"HAVE_SYSTEMD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_DOCS_TRUE}" && test -z "${BUILD_DOCS_FALSE}"; then as_fn_error $? "conditional \"BUILD_DOCS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${TCPWRAPPERS_TRUE}" && test -z "${TCPWRAPPERS_FALSE}"; then as_fn_error $? "conditional \"TCPWRAPPERS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DOVECOT_PLUGIN_DEPS_TRUE}" && test -z "${DOVECOT_PLUGIN_DEPS_FALSE}"; then as_fn_error $? "conditional \"DOVECOT_PLUGIN_DEPS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SSL_VERSION_GE_102_TRUE}" && test -z "${SSL_VERSION_GE_102_FALSE}"; then as_fn_error $? "conditional \"SSL_VERSION_GE_102\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_OPENSSL_TRUE}" && test -z "${BUILD_OPENSSL_FALSE}"; then as_fn_error $? "conditional \"BUILD_OPENSSL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_DCRYPT_OPENSSL_TRUE}" && test -z "${BUILD_DCRYPT_OPENSSL_FALSE}"; then as_fn_error $? "conditional \"BUILD_DCRYPT_OPENSSL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${GSSAPI_PLUGIN_TRUE}" && test -z "${GSSAPI_PLUGIN_FALSE}"; then as_fn_error $? "conditional \"GSSAPI_PLUGIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LDAP_PLUGIN_TRUE}" && test -z "${LDAP_PLUGIN_FALSE}"; then as_fn_error $? "conditional \"LDAP_PLUGIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_LDAP_TRUE}" && test -z "${HAVE_LDAP_FALSE}"; then as_fn_error $? "conditional \"HAVE_LDAP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_PGSQL_TRUE}" && test -z "${BUILD_PGSQL_FALSE}"; then as_fn_error $? "conditional \"BUILD_PGSQL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_MYSQL_TRUE}" && test -z "${BUILD_MYSQL_FALSE}"; then as_fn_error $? "conditional \"BUILD_MYSQL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_SQLITE_TRUE}" && test -z "${BUILD_SQLITE_FALSE}"; then as_fn_error $? "conditional \"BUILD_SQLITE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_CASSANDRA_TRUE}" && test -z "${BUILD_CASSANDRA_FALSE}"; then as_fn_error $? "conditional \"BUILD_CASSANDRA\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SQL_PLUGINS_TRUE}" && test -z "${SQL_PLUGINS_FALSE}"; then as_fn_error $? "conditional \"SQL_PLUGINS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_ZLIB_PLUGIN_TRUE}" && test -z "${BUILD_ZLIB_PLUGIN_FALSE}"; then as_fn_error $? "conditional \"BUILD_ZLIB_PLUGIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_RQUOTA_TRUE}" && test -z "${HAVE_RQUOTA_FALSE}"; then as_fn_error $? "conditional \"HAVE_RQUOTA\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_SOLR_TRUE}" && test -z "${BUILD_SOLR_FALSE}"; then as_fn_error $? "conditional \"BUILD_SOLR\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_FTS_STEMMER_TRUE}" && test -z "${BUILD_FTS_STEMMER_FALSE}"; then as_fn_error $? "conditional \"BUILD_FTS_STEMMER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_FTS_TEXTCAT_TRUE}" && test -z "${BUILD_FTS_TEXTCAT_FALSE}"; then as_fn_error $? "conditional \"BUILD_FTS_TEXTCAT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_FTS_EXTTEXTCAT_TRUE}" && test -z "${BUILD_FTS_EXTTEXTCAT_FALSE}"; then as_fn_error $? "conditional \"BUILD_FTS_EXTTEXTCAT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_LIBICU_TRUE}" && test -z "${BUILD_LIBICU_FALSE}"; then as_fn_error $? "conditional \"BUILD_LIBICU\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_APPARMOR_TRUE}" && test -z "${HAVE_APPARMOR_FALSE}"; then as_fn_error $? "conditional \"HAVE_APPARMOR\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_APPARMOR_TRUE}" && test -z "${HAVE_APPARMOR_FALSE}"; then as_fn_error $? "conditional \"HAVE_APPARMOR\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Dovecot $as_me 2.2.33.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ Dovecot config.status 2.2.33.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "doc/man/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/Makefile" ;; "doc/wiki/Makefile") CONFIG_FILES="$CONFIG_FILES doc/wiki/Makefile" ;; "doc/example-config/Makefile") CONFIG_FILES="$CONFIG_FILES doc/example-config/Makefile" ;; "doc/example-config/conf.d/Makefile") CONFIG_FILES="$CONFIG_FILES doc/example-config/conf.d/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/lib/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib/Makefile" ;; "src/lib-sql/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-sql/Makefile" ;; "src/lib-auth/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-auth/Makefile" ;; "src/lib-charset/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-charset/Makefile" ;; "src/lib-compression/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-compression/Makefile" ;; "src/lib-dcrypt/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-dcrypt/Makefile" ;; "src/lib-dict/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-dict/Makefile" ;; "src/lib-dict-extra/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-dict-extra/Makefile" ;; "src/lib-dns/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-dns/Makefile" ;; "src/lib-fs/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-fs/Makefile" ;; "src/lib-fts/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-fts/Makefile" ;; "src/lib-http/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-http/Makefile" ;; "src/lib-oauth2/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-oauth2/Makefile" ;; "src/lib-imap/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-imap/Makefile" ;; "src/lib-imap-storage/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-imap-storage/Makefile" ;; "src/lib-imap-client/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-imap-client/Makefile" ;; "src/lib-imap-urlauth/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-imap-urlauth/Makefile" ;; "src/lib-index/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-index/Makefile" ;; "src/lib-lda/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-lda/Makefile" ;; "src/lib-ldap/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-ldap/Makefile" ;; "src/lib-mail/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-mail/Makefile" ;; "src/lib-master/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-master/Makefile" ;; "src/lib-ntlm/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-ntlm/Makefile" ;; "src/lib-program-client/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-program-client/Makefile" ;; "src/lib-otp/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-otp/Makefile" ;; "src/lib-dovecot/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-dovecot/Makefile" ;; "src/lib-sasl/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-sasl/Makefile" ;; "src/lib-settings/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-settings/Makefile" ;; "src/lib-smtp/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-smtp/Makefile" ;; "src/lib-ssl-iostream/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-ssl-iostream/Makefile" ;; "src/lib-stats/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-stats/Makefile" ;; "src/lib-test/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-test/Makefile" ;; "src/lib-storage/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/Makefile" ;; "src/lib-storage/list/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/list/Makefile" ;; "src/lib-storage/index/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/Makefile" ;; "src/lib-storage/index/imapc/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/imapc/Makefile" ;; "src/lib-storage/index/pop3c/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/pop3c/Makefile" ;; "src/lib-storage/index/maildir/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/maildir/Makefile" ;; "src/lib-storage/index/mbox/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/mbox/Makefile" ;; "src/lib-storage/index/dbox-common/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/dbox-common/Makefile" ;; "src/lib-storage/index/dbox-multi/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/dbox-multi/Makefile" ;; "src/lib-storage/index/dbox-single/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/dbox-single/Makefile" ;; "src/lib-storage/index/cydir/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/cydir/Makefile" ;; "src/lib-storage/index/raw/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/raw/Makefile" ;; "src/lib-storage/index/shared/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/index/shared/Makefile" ;; "src/lib-storage/register/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib-storage/register/Makefile" ;; "src/anvil/Makefile") CONFIG_FILES="$CONFIG_FILES src/anvil/Makefile" ;; "src/auth/Makefile") CONFIG_FILES="$CONFIG_FILES src/auth/Makefile" ;; "src/config/Makefile") CONFIG_FILES="$CONFIG_FILES src/config/Makefile" ;; "src/doveadm/Makefile") CONFIG_FILES="$CONFIG_FILES src/doveadm/Makefile" ;; "src/doveadm/dsync/Makefile") CONFIG_FILES="$CONFIG_FILES src/doveadm/dsync/Makefile" ;; "src/lda/Makefile") CONFIG_FILES="$CONFIG_FILES src/lda/Makefile" ;; "src/log/Makefile") CONFIG_FILES="$CONFIG_FILES src/log/Makefile" ;; "src/lmtp/Makefile") CONFIG_FILES="$CONFIG_FILES src/lmtp/Makefile" ;; "src/dict/Makefile") CONFIG_FILES="$CONFIG_FILES src/dict/Makefile" ;; "src/director/Makefile") CONFIG_FILES="$CONFIG_FILES src/director/Makefile" ;; "src/dns/Makefile") CONFIG_FILES="$CONFIG_FILES src/dns/Makefile" ;; "src/indexer/Makefile") CONFIG_FILES="$CONFIG_FILES src/indexer/Makefile" ;; "src/ipc/Makefile") CONFIG_FILES="$CONFIG_FILES src/ipc/Makefile" ;; "src/imap/Makefile") CONFIG_FILES="$CONFIG_FILES src/imap/Makefile" ;; "src/imap-hibernate/Makefile") CONFIG_FILES="$CONFIG_FILES src/imap-hibernate/Makefile" ;; "src/imap-login/Makefile") CONFIG_FILES="$CONFIG_FILES src/imap-login/Makefile" ;; "src/imap-urlauth/Makefile") CONFIG_FILES="$CONFIG_FILES src/imap-urlauth/Makefile" ;; "src/login-common/Makefile") CONFIG_FILES="$CONFIG_FILES src/login-common/Makefile" ;; "src/master/Makefile") CONFIG_FILES="$CONFIG_FILES src/master/Makefile" ;; "src/pop3/Makefile") CONFIG_FILES="$CONFIG_FILES src/pop3/Makefile" ;; "src/pop3-login/Makefile") CONFIG_FILES="$CONFIG_FILES src/pop3-login/Makefile" ;; "src/replication/Makefile") CONFIG_FILES="$CONFIG_FILES src/replication/Makefile" ;; "src/replication/aggregator/Makefile") CONFIG_FILES="$CONFIG_FILES src/replication/aggregator/Makefile" ;; "src/replication/replicator/Makefile") CONFIG_FILES="$CONFIG_FILES src/replication/replicator/Makefile" ;; "src/ssl-params/Makefile") CONFIG_FILES="$CONFIG_FILES src/ssl-params/Makefile" ;; "src/stats/Makefile") CONFIG_FILES="$CONFIG_FILES src/stats/Makefile" ;; "src/util/Makefile") CONFIG_FILES="$CONFIG_FILES src/util/Makefile" ;; "src/plugins/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/Makefile" ;; "src/plugins/acl/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/acl/Makefile" ;; "src/plugins/imap-acl/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/imap-acl/Makefile" ;; "src/plugins/autocreate/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/autocreate/Makefile" ;; "src/plugins/dict-ldap/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/dict-ldap/Makefile" ;; "src/plugins/expire/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/expire/Makefile" ;; "src/plugins/fs-compress/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/fs-compress/Makefile" ;; "src/plugins/fts/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/fts/Makefile" ;; "src/plugins/fts-lucene/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/fts-lucene/Makefile" ;; "src/plugins/fts-solr/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/fts-solr/Makefile" ;; "src/plugins/fts-squat/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/fts-squat/Makefile" ;; "src/plugins/last-login/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/last-login/Makefile" ;; "src/plugins/lazy-expunge/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/lazy-expunge/Makefile" ;; "src/plugins/listescape/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/listescape/Makefile" ;; "src/plugins/mail-filter/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/mail-filter/Makefile" ;; "src/plugins/mail-log/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/mail-log/Makefile" ;; "src/plugins/mailbox-alias/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/mailbox-alias/Makefile" ;; "src/plugins/notify/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/notify/Makefile" ;; "src/plugins/notify-status/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/notify-status/Makefile" ;; "src/plugins/push-notification/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/push-notification/Makefile" ;; "src/plugins/pop3-migration/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/pop3-migration/Makefile" ;; "src/plugins/quota/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/quota/Makefile" ;; "src/plugins/quota-clone/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/quota-clone/Makefile" ;; "src/plugins/imap-quota/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/imap-quota/Makefile" ;; "src/plugins/replication/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/replication/Makefile" ;; "src/plugins/snarf/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/snarf/Makefile" ;; "src/plugins/stats/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/stats/Makefile" ;; "src/plugins/imap-stats/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/imap-stats/Makefile" ;; "src/plugins/trash/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/trash/Makefile" ;; "src/plugins/virtual/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/virtual/Makefile" ;; "src/plugins/welcome/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/welcome/Makefile" ;; "src/plugins/zlib/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/zlib/Makefile" ;; "src/plugins/imap-zlib/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/imap-zlib/Makefile" ;; "src/plugins/mail-crypt/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/mail-crypt/Makefile" ;; "src/plugins/var-expand-crypt/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/var-expand-crypt/Makefile" ;; "src/plugins/apparmor/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/apparmor/Makefile" ;; "stamp.h") CONFIG_FILES="$CONFIG_FILES stamp.h" ;; "dovecot-config.in") CONFIG_FILES="$CONFIG_FILES dovecot-config.in" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='CXX ' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi if test "$want_sql" = "plugin"; then sql_drivers="$sql_drivers (plugins)" fi not_passdb=`echo "$not_passdb"|sed 's/ / -/g'` not_userdb=`echo "$not_userdb"|sed 's/ / -/g'` not_sql_drivers=`echo "$not_sql_drivers"|sed 's/ / -/g'` not_fts=`echo "$not_fts"|sed 's/ / -/g'` echo echo "Install prefix . : $prefix" echo "File offsets ... : ${offt_bits}bit" echo "I/O polling .... : $ioloop" echo "I/O notifys .... : $have_notify" echo "SSL ............ : $have_ssl" echo "GSSAPI ......... : $have_gssapi" echo "passdbs ........ :$passdb" echo "dcrypt ..........: $build_dcrypt_openssl" if test "$not_passdb" != ""; then echo " :$not_passdb" fi echo "userdbs ........ :$userdb" if test "$not_userdb" != ""; then echo " :$not_userdb" fi echo "SQL drivers .... :$sql_drivers" if test "$not_sql_drivers" != ""; then echo " :$not_sql_drivers" fi echo "Full text search :$fts" if test "$not_fts" != ""; then echo " :$not_fts" fi dovecot-2.2.33.2/config.sub0000755000175000017500000010676313144653650012334 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2016 Free Software Foundation, Inc. timestamp='2016-11-04' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: dovecot-2.2.33.2/Makefile.in0000644000175000017500000011064313172375572012413 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @HAVE_SYSTEMD_FALSE@am__append_1 = dovecot.socket dovecot.service.in subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_pkginclude_HEADERS) \ $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = stamp.h dovecot-config.in CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(aclocaldir)" \ "$(DESTDIR)$(systemdsystemunitdir)" \ "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)" DATA = $(aclocal_DATA) $(noinst_DATA) $(systemdsystemunit_DATA) HEADERS = $(dist_pkginclude_HEADERS) $(nodist_pkginclude_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(srcdir)/dovecot-config.in.in $(srcdir)/stamp.h.in AUTHORS \ COPYING ChangeLog INSTALL NEWS README TODO compile \ config.guess config.rpath config.sub depcomp install-sh \ ltmain.sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = -I . -I m4 AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ aclocaldir = $(datadir)/aclocal SUBDIRS = \ . \ src \ doc dist_pkginclude_HEADERS = \ dovecot-version.h EXTRA_DIST = COPYING.LGPL COPYING.MIT ChangeLog update-version.sh \ run-test-valgrind.supp $(conf_DATA) $(am__append_1) noinst_DATA = dovecot-config nodist_pkginclude_HEADERS = config.h aclocal_DATA = dovecot.m4 @HAVE_SYSTEMD_TRUE@systemdsystemunit_DATA = \ @HAVE_SYSTEMD_TRUE@ dovecot.socket \ @HAVE_SYSTEMD_TRUE@ dovecot.service @HAVE_SYSTEMD_TRUE@CLEANFILES = $systedmsystemunit_DATA DISTCLEANFILES = \ $(top_builddir)/dovecot-version.h \ $(top_builddir)/dovecot-config \ $(top_builddir)/run-test.sh all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 stamp.h: $(top_builddir)/config.status $(srcdir)/stamp.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ dovecot-config.in: $(top_builddir)/config.status $(srcdir)/dovecot-config.in.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-aclocalDATA: $(aclocal_DATA) @$(NORMAL_INSTALL) @list='$(aclocal_DATA)'; test -n "$(aclocaldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(aclocaldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(aclocaldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(aclocaldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(aclocaldir)" || exit $$?; \ done uninstall-aclocalDATA: @$(NORMAL_UNINSTALL) @list='$(aclocal_DATA)'; test -n "$(aclocaldir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(aclocaldir)'; $(am__uninstall_files_from_dir) install-systemdsystemunitDATA: $(systemdsystemunit_DATA) @$(NORMAL_INSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \ done uninstall-systemdsystemunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) install-dist_pkgincludeHEADERS: $(dist_pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(dist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-dist_pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(dist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) install-nodist_pkgincludeHEADERS: $(nodist_pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-nodist_pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && $(MAKE) $(AM_MAKEFLAGS) distcheck-hook \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) $(HEADERS) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(aclocaldir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-aclocalDATA install-dist_pkgincludeHEADERS \ install-nodist_pkgincludeHEADERS install-systemdsystemunitDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-aclocalDATA uninstall-dist_pkgincludeHEADERS \ uninstall-nodist_pkgincludeHEADERS \ uninstall-systemdsystemunitDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook .MAKE: $(am__recursive_targets) all install-am install-exec-am \ install-strip uninstall-am .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip distcheck distclean distclean-generic \ distclean-hdr distclean-libtool distclean-tags distcleancheck \ distdir distuninstallcheck dvi dvi-am html html-am info \ info-am install install-aclocalDATA install-am install-data \ install-data-am install-dist_pkgincludeHEADERS install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-html install-html-am install-info install-info-am \ install-man install-nodist_pkgincludeHEADERS install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ install-systemdsystemunitDATA installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-aclocalDATA uninstall-am \ uninstall-dist_pkgincludeHEADERS uninstall-hook \ uninstall-nodist_pkgincludeHEADERS \ uninstall-systemdsystemunitDATA .PRECIOUS: Makefile ChangeLog: git log --name-status --pretty="format:%ai %aN <%aE> (%h)%n%n%w(80,4,4)%s%n%n%b" 5d5c4f2bfb812c767084c0338dae692db476da10^.. > ChangeLog || rm -f ChangeLog dovecot-version.h: noop $(SHELL) $(top_srcdir)/update-version.sh $(top_srcdir) $(top_builddir) noop: dovecot-config: dovecot-config.in Makefile old=`pwd` && cd $(top_builddir) && abs_builddir=`pwd` && cd $$old && \ cd $(top_srcdir) && abs_srcdir=`pwd` && cd $$old && \ (echo "DOVECOT_INSTALLED=no"; cat dovecot-config.in | sed \ -e "s|\$$(top_builddir)|$$abs_builddir|g" \ -e "s|\$$(incdir)|$$abs_srcdir|g" \ -e "s|\$$(LIBICONV)|$(LIBICONV)|g" \ -e "s|\$$(MODULE_LIBS)|$(MODULE_LIBS)|g" \ -e "s|^\(dovecot_pkgincludedir\)=|\1=$(pkgincludedir)|" \ -e "s|^\(dovecot_pkglibdir\)=|\1=$(pkglibdir)|" \ -e "s|^\(dovecot_pkglibexecdir\)=|\1=$(libexecdir)/dovecot|" \ -e "s|^\(dovecot_docdir\)=|\1=$(docdir)|" \ -e "s|^\(dovecot_moduledir\)=|\1=$(moduledir)|" \ -e "s|^\(dovecot_statedir\)=|\1=$(statedir)|" \ ) > dovecot-config @HAVE_SYSTEMD_TRUE@%.service: %.service.in @HAVE_SYSTEMD_TRUE@ $(AM_V_GEN)sed -e 's,@sbindir\@,$(sbindir),g' -e 's,@bindir\@,$(bindir),g' -e 's,@rundir\@,$(rundir),g' $< > $@ install-exec-hook: $(mkdir_p) $(DESTDIR)$(pkglibdir); \ grep -v '^LIBDOVECOT_.*_INCLUDE' dovecot-config | \ grep -v '^LIBDOVECOT.*_DEPS' | sed \ -e "s|^\(DOVECOT_INSTALLED\)=.*$$|\1=yes|" \ -e "s|^\(LIBDOVECOT\)=.*$$|\1='-L$(pkglibdir) -ldovecot'|" \ -e "s|^\(LIBDOVECOT_LOGIN\)=.*$$|\1='-ldovecot-login $(SSL_LIBS)'|" \ -e "s|^\(LIBDOVECOT_SQL\)=.*$$|\1=-ldovecot-sql|" \ -e "s|^\(LIBDOVECOT_COMPRESS\)=.*$$|\1=-ldovecot-compression|" \ -e "s|^\(LIBDOVECOT_DSYNC\)=.*$$|\1=-ldovecot-dsync|" \ -e "s|^\(LIBDOVECOT_LDA\)=.*$$|\1=-ldovecot-lda|" \ -e "s|^\(LIBDOVECOT_LIBFTS\)=.*$$|\1=-ldovecot-fts|" \ -e "s|^\(LIBDOVECOT_STORAGE\)=.*$$|\1='-ldovecot-storage $(LINKED_STORAGE_LDADD)'|" \ -e "s|^\(LIBDOVECOT_INCLUDE\)=.*$$|\1=-I$(pkgincludedir)|" \ > $(DESTDIR)$(pkglibdir)/dovecot-config uninstall-hook: rm $(DESTDIR)$(pkglibdir)/dovecot-config distcheck-hook: if which scan-build > /dev/null; then \ cd $(distdir)/_build; \ scan-build -o scan-reports ../configure --with-ldap=auto --with-pgsql=auto --with-mysql=auto --with-sqlite=auto --with-solr=auto --with-gssapi=auto --with-libwrap=auto; \ rm -rf scan-reports; \ scan-build -o scan-reports make 2>&1 || exit 1; \ if ! rmdir scan-reports 2>/dev/null; then \ exit 1; \ fi; \ make distclean; \ fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/m4/0002755000175000017500000000000013172375610010734 500000000000000dovecot-2.2.33.2/m4/want_apparmor.m40000644000175000017500000000120113147010713013750 00000000000000AC_DEFUN([DOVECOT_WANT_APPARMOR], [ want_apparmor=auto AC_ARG_WITH([apparmor], [AS_HELP_STRING([--with-apparmor], [enable apparmor plugin (default=auto)])], [want_apparmor=$withval]) have_apparmor=no if test $want_apparmor != no; then AC_CHECK_HEADER([sys/apparmor.h], [ AC_CHECK_LIB([apparmor], [aa_change_hat], [ have_apparmor=yes AC_SUBST([APPARMOR_LIBS], [-lapparmor]) ]) ]) fi if test $want_apparmor = yes; then if test $have_apparmor = no; then AC_MSG_FAILURE([apparmor was not found]) fi fi AM_CONDITIONAL(HAVE_APPARMOR, test "$have_apparmor" = "yes") ]) dovecot-2.2.33.2/install-sh0000755000175000017500000003546313144653650012353 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2014-09-12.12; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # $RANDOM is not portable (e.g. dash); use it when possible to # lower collision chance tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # As "mkdir -p" follows symlinks and we work in /tmp possibly; so # create the $tmpdir first (and fail if unsuccessful) to make sure # that nobody tries to guess the $tmpdir name. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: dovecot-2.2.33.2/compile0000755000175000017500000001624513144653650011722 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: dovecot-2.2.33.2/dovecot-config.in.in0000644000175000017500000000512313165463624014203 00000000000000DOVECOT_CFLAGS="@CFLAGS@" DOVECOT_LIBS="@LIBS@" DOVECOT_SSL_LIBS="@SSL_LIBS@" DOVECOT_SQL_LIBS="@SQL_LIBS@" DOVECOT_COMPRESS_LIBS="@COMPRESS_LIBS@" LIBDOVECOT="@LIBDOVECOT@" LIBDOVECOT_LOGIN="@LIBDOVECOT_LOGIN@ @SSL_LIBS@" LIBDOVECOT_SQL="@LIBDOVECOT_SQL@" LIBDOVECOT_COMPRESS="@LIBDOVECOT_COMPRESS@" LIBDOVECOT_LDA="@LIBDOVECOT_LDA@" LIBDOVECOT_STORAGE="@LIBDOVECOT_STORAGE@" LIBDOVECOT_DSYNC="@LIBDOVECOT_DSYNC@" LIBDOVECOT_LIBFTS="@LIBDOVECOT_LIBFTS@" LIBDOVECOT_DEPS="@LIBDOVECOT_DEPS@" LIBDOVECOT_LOGIN_DEPS="@LIBDOVECOT_LOGIN@" LIBDOVECOT_SQL_DEPS="@LIBDOVECOT_SQL@" LIBDOVECOT_COMPRESS_DEPS="@LIBDOVECOT_COMPRESS@" LIBDOVECOT_LDA_DEPS="@LIBDOVECOT_LDA@" LIBDOVECOT_STORAGE_DEPS="@LIBDOVECOT_STORAGE_DEPS@" LIBDOVECOT_DSYNC_DEPS="@LIBDOVECOT_DSYNC@" LIBDOVECOT_LIBFTS_DEPS="@LIBDOVECOT_LIBFTS@" LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-http -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-smtp -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset -I$(incdir)/src/lib-auth -I$(incdir)/src/lib-master -I$(incdir)/src/lib-ssl-iostream -I$(incdir)/src/lib-compression -I$(incdir)/src/lib-settings -I$(incdir)/src/lib-test -I$(incdir)/src/lib-sasl -I$(incdir)/src/lib-stats -I$(incdir)/src/lib-dcrypt -I$(incdir)/src/lib-program-client" LIBDOVECOT_LDA_INCLUDE="-I$(incdir)/src/lib-lda -I$(incdir)/src/lda" LIBDOVECOT_AUTH_INCLUDE="-I$(incdir)/src/auth" LIBDOVECOT_DOVEADM_INCLUDE="-I$(incdir)/src/doveadm" LIBDOVECOT_STORAGE_INCLUDE="-I$(incdir)/src/lib-index -I$(incdir)/src/lib-storage -I$(incdir)/src/lib-storage/list -I$(incdir)/src/lib-storage/index -I$(incdir)/src/lib-storage/index/raw -I$(incdir)/src/lib-imap-storage -I$(incdir)/src/plugins/quota" LIBDOVECOT_DSYNC_INCLUDE="-I$(incdir)/src/doveadm/dsync" LIBDOVECOT_LOGIN_INCLUDE="-I$(incdir)/src/login-common" LIBDOVECOT_SQL_INCLUDE="-I$(incdir)/src/lib-sql" LIBDOVECOT_IMAP_LOGIN_INCLUDE="-I$(incdir)/src/imap-login" LIBDOVECOT_IMAP_INCLUDE="-I$(incdir)/src/imap" LIBDOVECOT_POP3_INCLUDE="-I$(incdir)/src/pop3" LIBDOVECOT_CONFIG_INCLUDE="-I$(incdir)/src/config" LIBDOVECOT_IMAPC_INCLUDE="-I$(incdir)/src/lib-imap-client -I$(incdir)/src/lib-storage/index/imapc" LIBDOVECOT_FTS_INCLUDE="-I$(incdir)/src/plugins/fts" LIBDOVECOT_NOTIFY_INCLUDE="-I$(incdir)/src/plugins/notify" LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE="-I$(incdir)/src/plugins/push-notification" LIBDOVECOT_ACL_INCLUDE="-I$(incdir)/src/plugins/acl" LIBDOVECOT_LIBFTS_INCLUDE="-I$(incdir)/src/lib-fts" dovecot_pkgincludedir= dovecot_pkglibdir= dovecot_pkglibexecdir= dovecot_docdir= dovecot_moduledir= dovecot_statedir= dovecot-2.2.33.2/libtool.m40000644000175000017500000112617113144653645012257 00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS dovecot-2.2.33.2/missing0000755000175000017500000001533013144653650011735 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: dovecot-2.2.33.2/COPYING.LGPL0000644000175000017500000006364213123174404012127 00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! dovecot-2.2.33.2/config.guess0000755000175000017500000012564413147011415012656 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2016 Free Software Foundation, Inc. timestamp='2016-10-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || \ echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "${UNAME_MACHINE_ARCH}" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "${UNAME_MACHINE_ARCH}" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; *:Sortix:*:*) echo ${UNAME_MACHINE}-unknown-sortix exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = hppa2.0w ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; e2k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; k1om:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; mips64el:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac cat >&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: dovecot-2.2.33.2/dovecot.service.in0000644000175000017500000000157313165463624013777 00000000000000# This file is part of Dovecot # # If you want to pass additionally command line options to the dovecot # binary, create the file: # `/etc/systemd/system/dovecot.service.d/service.conf'. [Unit] Description=Dovecot IMAP/POP3 email server Documentation=man:dovecot(1) Documentation=http://wiki2.dovecot.org/ After=local-fs.target network.target [Service] Type=forking ExecStart=@sbindir@/dovecot PIDFile=@rundir@/master.pid ExecReload=@bindir@/doveadm reload ExecStop=@bindir@/doveadm stop PrivateTmp=true NonBlocking=yes # Enable this if your systemd is new enough to support it: #ProtectSystem=full # You can add environment variables with e.g.: #Environment='CORE_OUTOFMEM=1' # If you have trouble with `Too many open files' you may set: #LimitNOFILE=8192 # If you want to allow the Dovecot services to produce core dumps, use: #LimitCORE=infinity [Install] WantedBy=multi-user.target dovecot-2.2.33.2/update-version.sh0000644000175000017500000000336713123174404013636 00000000000000#!/bin/sh SRCDIR="${1:-`pwd`}" BUILDDIR="${2:-`pwd`}" VERSION_H="dovecot-version.h" VERSION_HT="dovecot-version.h.tmp" DOVECOT_BUILD_INFO=${DOVECOT_BUILD_INFO:-DOVECOT_VERSION_FULL} abspath() { #$1 the path #$2 1 -> SRCDIR || 2 -> BUILDDIR old=`pwd` cd "${1}" if [ ${2} -eq 1 ]; then SRCDIR=`pwd` else BUILDDIR=`pwd` fi cd "$old" } abspath "${SRCDIR}" 1 abspath "${BUILDDIR}" 2 # when using a different BUILDDIR just copy from SRCDIR, if there is no .git if [ "${BUILDDIR}" != "${SRCDIR}" ]; then if [ ! -d "${SRCDIR}/.git" ] && [ -f "${SRCDIR}/${VERSION_H}" ]; then cmp -s "${SRCDIR}/${VERSION_H}" "${BUILDDIR}/${VERSION_H}" if [ $? -ne 0 ]; then cp "${SRCDIR}/${VERSION_H}" "${BUILDDIR}/${VERSION_H}" exit 0 fi fi fi # Don't generate dovecot-version.h if the source tree has no .git dir but # a dovecot-version.h. This may be the result of a release/nightly tarball. [ ! -d "${SRCDIR}/.git" ] && [ -f "${BUILDDIR}/${VERSION_H}" ] && exit 0 # Lets generate the dovecot-version.h [ -f "${BUILDDIR}/${VERSION_HT}" ] && rm -f "${BUILDDIR}/${VERSION_HT}" if true; then GITID=`git --git-dir ${SRCDIR}/.git rev-parse --short HEAD` cat > "${BUILDDIR}/${VERSION_HT}" < "${BUILDDIR}/${VERSION_HT}" < LSUB lists -> dsync assert-crashes - replicator: automatically remove users who don't exist - imapc: sync_uid_next handling doesn't seem to be correct, especially with Courier that doesn't send UIDNEXT on SELECT - sdbox: dbox_file_fix() should assume there is only one message.. - pop3: if we can't fetch "order" field for UIDL (but could fetch it initially), the order will be wrong and error is logged. probably just need to read all the UIDLs into memory at startup?.. - fs_list_get_mailbox_flags() is unnecessarily stat()ing files/dirs - doveadm-server: dsync doesn't work through proxying, because the data isn't actually being proxied but handled via doveadm_print() - CATENATE: Allow ~{binary} data but fail if there are any c-t-e: binary parts? or simply silently save it? - master-settings.c warnings aren't logged to log file at startup - dsync: delete foo, rename bar foo -> foo, foo-temp-1 - dsync+imapc: - mailbox list could be synced pretty optimally by ignoring (name, uidvalidity) matches. for the left if uidvalidities are unique and can be matched -> rename mailbox. - GUID-less sync could optionally use just rfc822.size [and internaldate] to match messages. - virtual plugin doesn't verify the index file's data, crashes if broken. - libsasl: use it in pop3c, managesieve-login, doveadm auth - per-msg checksums? per-cache-msg checksums? per-log record checksums? - if transaction log file corruption is noticed, make sure new dovecot.index snapshot gets written and don't mark the whole file corrupted.. rather maybe just rotate and truncate it - mdbox: purging in alt storage could create files back to alt storage - LAYOUT=index: - after doing a lot of changes the list's memory pool keeps growing. do an occasional re-parsing to clear the pool - quota recalc + dict-file [+acl?] assert-crashes in !indexing->syncing - imaptest: add condstore, qresync tests - Track highestmodseq always, just don't keep per-message modseqs unless they're enabled. Then don't return [NOMODSEQ] on select. - URLAUTH: if client tries to access nonexistent user, do a delay in imap-urlauth-client.c (AFTER destroying the worker) - special response in the control connection to make the imap-urlauth master wait before starting a new worker - shared user should get settings from userdb extra fields, especially plugin/quota_rule to get different quota limits for shared mailboxes. the problem is that user doesn't currently have set_parser available, and adding it would probably waste memory.. - auth_debug[_passwords]=yes ability for specific users via doveadm. for both login-common and auth - settings parsing is horribly bloaty - doveadm: if running via doveadm-server and it fails, say something about error being in the log - indexer-worker and maybe others (doveadm?) could support dropping privileges permanently when service_count=1. Note that LMTP can't with multiple RCPT TOs. - after reading whole message text, update has_nul-state to cache - FIFOs maybe should be counted as connections, but unlisten should unlink+reopen it in master? - lmtp client/proxy: Handle multiline replies better - lmtp: support DSN extension (especially ORCPT) - recreate mailbox -> existing sessions log "indexid changed" error - add message/mime limits - imapc: - prefetching to THREAD and SORT - check all imap extensions and see if some don't work (condstore) - per-namespace imapc_* settings? create a way to "copy" a settings struct, so mail_storage_settings are copied to mail_namespace_settings. use the change tracking to figure out what settings are namespace-specific. - doveadm import: add -d parameter to deduplicate mails based on their GUID (or perhaps do it by default?) - sdbox: altmoving is done with mailbox locked. that's not necessary, it could do the copying while unlocked and delete the primary files while locked - passdb, userdb { username_format } that doesn't permanently change the username - mdbox/sdbox index rebuild -> quota rebuild? - solr separate attachments (patch) - sql connection pooling: Count lookup latencies, avoid servers with significantly higher latencies. optionally use the secondary server only as fallback - maildir_storage_sync_force() shouldn't do anything except find the new file, don't go expunging any more stuff or we could get recursively back to where we started, and stuff would break - fuzzy: be fuzzy about date/size - mailbox list index: - with in-memory indexes be sure to refresh it more often - refreshing could refresh only the parts that are actually requested, e.g. % - notify_sync() could have "what changed" struct with old/new flags - maildir: copy dovecot-shared file from parent mailbox, not root. - master passdb preserves userdb_* extra fields. should it preserve non-userdb_* extra fields too? - imap, pop3: if client init fails, wait a second or two before disconnecting client. - doveadm search savedbefore 7d could be optimized in large mailboxes.. - mdbox: storage rebuilding could log about changes it does - mdbox: broken extrefs header keeps causing index rebuilds - sent, drafts: .Sent/dovecot.index: modseq_hdr.log_offset too large - mail_max_lock_timeout error could be reported more nicely, also ones coming from lib-index - sql pool: if async query is pending and sync query is sent and there are no more empty connections, it should flush the async query first - NTLMv1 and LM should be disabled if disable_plaintext_auth=yes - SEARCH SENT*/HEADER/etc. doesn't seem optimized when using with TEXT/BODY - dict sql: support ignoring some search key hierarchies (e.g. acl "anyone") - dsync: avoid sending email when it could be copied from another mailbox. probably requires storage to have guid => { instances } map? that's rather annoying to add. - mdbox - dotlocking: cleanup should delete stale *.lock files - purging seems to be inefficient. run imaptest for a while, get >500 files, start purging, it's slow until there are about 100 files left, then the rest is suddenly fast. - make sure that when reading mdbox mails sequentially the data is being read from disk in n kB blocks and reads cross mail boundaries and when reading the next mail it uses the previously read data in buffer - Add some kind of checksum about data+metadata and use it when checking consistency - figure out a way to efficiently trigger purging when user has too much mail expunged (e.g. keep track of total storage size, trigger purging when it's 2*quota limit) - keep track of total bytes in dbox storage in map header. also if possible keep track of refcount=0 bytes. use these to optimize checks. - save some stuff to map index header so we don't need to keep retrying it. like when saving the lowest file_id which to bother checking. - test crash-fixing - optimize away reading file header? - maildir: out-of-disk-space failures apparently cause all kinds of problems, e.g. "Expunged message reappeared", "Duplicate file entry"? - deliver -r
used as autoreplies' From-address? - istream-seekable is inefficient. it shouldn't be reading the temp file immediately after writing to it - config process is handling requests too slowly. maybe add some caching. - maybe config should return all of the protocol/local/remote overrides when requested? then the caller could do a single lookup at start and merge them later internally. this would really help login processes. - ipv6: auth penalty should begin from /64 and gradually grow to /48 if necessary. and the same could be done for ipv4 as well.. - ldap: fix multiple-gid support somehow - search: use mail_get_parts() only when it's already cached. if it's not, add it to cache afterwards. /* currently non-external transactions can be applied multiple times, causing multiple increments. */ //FIXME:i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0); ^ appears to work now though, probably because of the added syncing stuff.. - use backup index in mail_index_fsck() - proxying: support fallbacking to local (or other?) server if the first one is down - virtual: If last message matching INTHREAD rule gets expunged, the rest of the thread doesn't go away - how do shared mailboxes work with plugins? - lazy-expunge, fts, etc.? - listescape+acl can't handle shared mailboxes with escape chars - dovecot-acl-list: - how does it work with global acls? - update immediately after SETACL: add/remove entries, update timestamps - read the entire file to memory only once and keep it there, stat() later to see if it has changed. if not, perhaps don't even bother stat()ing dovecot-acl files? at least not that often.. - fs quota: getquotaroot inbox vs. other-box should return different quotas if two quotas are defined - auth_log_prefix setting similar to mail_log_prefix - thread indexes: if we expunge a duplicate message-id: and we have a sibling with identical message-id:, we can probably just move the children? (unless there are non-sibling duplicates) - SEARCH INTHREAD requires no thread sorting by date - don't do it - CONDSTORE: use per-flag/per-keyword conflict checking - QRESYNC: Drop expunges from the middle of given seq sets if possible - use universal hash functions? - UIDVALIDITY changed while saving -> sync errors - mbox: copy to Trash, manually delete copied msg, change uidvalidity, set nextuid=1, copy again -> error - recent_uids assert at least with mbox - quota fs: Should values returned by quota be divided by the actual filesystem block size instead of hardcoded DEV_BSIZE? not with AIX.. - squat: - wrong indexid - fts_build_init() assertion failed: (last_uid < last_uid_locked) - is locking done right? it reads header without file being locked? - split after ~8 bytes? - expunges are delayed until more mails are added - test replacement chars (SEARCH / SORT / Squat) - DEBUG: buffer overflow checking code probably doesn't handle a successful t_try_realloc() or pool_alloconly_realloc() properly - ldap: - multiple ldap values could be joined into one field with specified separator (e.g. mail_access_groups=%{ldap:gidNumber:,}) - maildir+pop3 fast updates: - don't update dovecot-uidlist if dovecot.index.cache doesn't exist / there's nothing to cache - if all messages are expunged and there are no unknown extensions in index, unlink dovecot.index and rotate log and add some initial useful info to the log (uidvalidity, nextuid) - maildir - don't allow more than 26 keywords - file_cache: we're growing the mmap in page size blocks, which is horribly slow if mremap() doesn't exist. - keywords: - add some limits to how many there can be - don't return \* in PERMANENTFLAGS when we're full - remove unused keywords? - mail caching - force bits should be used only for nonregistered fields - change envelope parsing not to use get_headers() so imap.envelope can actually be cached without all the headers.. - if there's no other pressure for compression, we should do it when enough temp fields are ready to be dropped - we could try compressing same field values into a single location in cache file. - place some maximum limit of fields to cache file? maybe some soft and hard limits, so when soft limit is reached drop fields that have been used only once. when hard limit is reached drop any fields to get more space. all this to avoid cache file growing infinitely. - mbox - UID renumbering doesn't really work after all? - still problems with CRLF mboxes.. especially with broken Content-Length headers (pointing between CR-LF?) - syncing existing indexes takes 4x longer than creating new one, why? - how well does dirty sync + status work? it reads the last mail every time? not very good.. - always add empty line. make the parser require it too? syncing should make sure there always exists two LFs at end of file. raw-mbox-stream should make sure the last message ends with LF even if it doesn't exist in the file - Quote "From ", unquote ">From " - COPY doesn't work to itself (lock assert crash, for now just disallowed) - index - index file format changes: - split to "old" and "new" indexes and try to avoid loading "old" into memory until needed - pack UIDs to beginning of file with UID ranges - use squat-like compressed uid ranges everywhere - write first extension intros in dovecot.index.log always with names - or better yet, drop the intro concept completely as it is now - login - Digest-MD5: support integrity protection, and maybe crypting. Do it through login process like SSL is done? - auth - with blocking passdb we're not caching lookups if the password was wrong - non-plaintext authentication doesn't support all features: - multiple passdbs don't work, only the first one is used - auth cache's last_success password change check doesn't exist - auth_cache_negative_ttl doesn't check password mismatches - dovecot-auth should limit how fast authentication requests are allowed from login processes. especially if there's one login/connection the speed should be something like once/sec. also limit how fast to accept new connections. - support read-only logins. user could with alternative password get only read-access to mails so mails could be read relatively safely with untrusted computers. Maybe always send [ALERT] about the previous read-only login time with IP? - ssl - add setting: ssl_options = bitmask. by default we enable all openssl workarounds, this could be used to disable some of them - gnutls support isn't working - search - message header search: we should ignore LWSP between two MIME blocks(?) - message_search_init() could accept multiple search keywords so we wouldn't need to call it separately for each one (so we wouldn't need to parse the message multiple times). - Create our own extension: When searching with TEXT/BODY, return the message text surrounding the keywords just like web search engines do. like: SEARCH X-PRINT-MATCHES TEXT "hello" -> * SEARCH 1 "He said: Hello world!" 2 "Hello, I'm ...". This would be especially useful with the above attachment scanning. - general - things break if next_uid gets to 2^32 - lib-http: - Client: - Handle HTTP/1.0 servers properly: -> Transfer-Encoding is not allowed - Implement support for priority/deadline-based scheduling. Much like: https://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html - Allow handling non-idempotent requests specially (no automatic retry, block pipeline) - Implement support for `Range:' requests. - Implement optional round-robin request scheduling for when host has multiple IPs. - Server: - Implement API structure for virtual hosts and resources. This way, multiple services can coexist independently on the same HTTP server. - Implement support for `Range:' requests. - Review compliance with RFC 7230 and RFC 7231 dovecot-2.2.33.2/run-test-valgrind.supp0000644000175000017500000000163113123174404014623 00000000000000{ Memcheck:Leak fun:malloc obj:*/bash } { Memcheck:Cond obj:/lib/x86_64-linux-gnu/liblzma.so.5.* obj:/lib/x86_64-linux-gnu/liblzma.so.5.* obj:/lib/x86_64-linux-gnu/liblzma.so.5.* obj:/lib/x86_64-linux-gnu/liblzma.so.5.* obj:/lib/x86_64-linux-gnu/liblzma.so.5.* fun:lzma_stream_encoder fun:lzma_easy_encoder } { Memcheck:Leak fun:malloc obj:* obj:* obj:* obj:* obj:* obj:* obj:* obj:* obj:* fun:module_dir_init fun:dcrypt_initialize fun:main } { Memcheck:Leak fun:malloc obj:* obj:* obj:* obj:* obj:* obj:* obj:* obj:* fun:module_dir_init fun:dcrypt_initialize fun:main } { Memcheck:Leak fun:malloc ... fun:module_dir_init } dovecot-2.2.33.2/src/0002755000175000017500000000000013172375613011206 500000000000000dovecot-2.2.33.2/src/lib-imap/0002755000175000017500000000000013172375611012676 500000000000000dovecot-2.2.33.2/src/lib-imap/imap-arg.c0000644000175000017500000000527613123174404014460 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-arg.h" bool imap_arg_get_atom(const struct imap_arg *arg, const char **str_r) { if (arg->type != IMAP_ARG_ATOM && arg->type != IMAP_ARG_NIL) return FALSE; *str_r = arg->_data.str; return TRUE; } bool imap_arg_get_quoted(const struct imap_arg *arg, const char **str_r) { if (arg->type != IMAP_ARG_STRING) return FALSE; *str_r = arg->_data.str; return TRUE; } bool imap_arg_get_string(const struct imap_arg *arg, const char **str_r) { if (arg->type != IMAP_ARG_STRING && arg->type != IMAP_ARG_LITERAL) return FALSE; *str_r = arg->_data.str; return TRUE; } bool imap_arg_get_astring(const struct imap_arg *arg, const char **str_r) { /* RFC 3501 4.5. specifies that NIL is the same as "NIL" when reading astring. */ if (!IMAP_ARG_IS_ASTRING(arg) && arg->type != IMAP_ARG_NIL) return FALSE; *str_r = arg->_data.str; return TRUE; } bool imap_arg_get_nstring(const struct imap_arg *arg, const char **str_r) { if (arg->type == IMAP_ARG_NIL) { *str_r = NULL; return TRUE; } return imap_arg_get_astring(arg, str_r); } bool imap_arg_get_literal_size(const struct imap_arg *arg, uoff_t *size_r) { if (arg->type != IMAP_ARG_LITERAL_SIZE && arg->type != IMAP_ARG_LITERAL_SIZE_NONSYNC) return FALSE; *size_r = arg->_data.literal_size; return TRUE; } bool imap_arg_get_list(const struct imap_arg *arg, const struct imap_arg **list_r) { unsigned int count; return imap_arg_get_list_full(arg, list_r, &count); } bool imap_arg_get_list_full(const struct imap_arg *arg, const struct imap_arg **list_r, unsigned int *list_count_r) { unsigned int count; if (arg->type != IMAP_ARG_LIST) return FALSE; *list_r = array_get(&arg->_data.list, &count); /* drop IMAP_ARG_EOL from list size */ i_assert(count > 0); *list_count_r = count - 1; return TRUE; } const char *imap_arg_as_astring(const struct imap_arg *arg) { const char *str; if (!imap_arg_get_astring(arg, &str)) i_unreached(); return str; } const char *imap_arg_as_nstring(const struct imap_arg *arg) { const char *str; if (!imap_arg_get_nstring(arg, &str)) i_unreached(); return str; } uoff_t imap_arg_as_literal_size(const struct imap_arg *arg) { uoff_t size; if (!imap_arg_get_literal_size(arg, &size)) i_unreached(); return size; } const struct imap_arg * imap_arg_as_list(const struct imap_arg *arg) { const struct imap_arg *ret; if (!imap_arg_get_list(arg, &ret)) i_unreached(); return ret; } bool imap_arg_atom_equals(const struct imap_arg *arg, const char *str) { const char *value; if (!imap_arg_get_atom(arg, &value)) return FALSE; return strcasecmp(value, str) == 0; } dovecot-2.2.33.2/src/lib-imap/imap-util.c0000644000175000017500000001127013123174404014653 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "unichar.h" #include "mail-types.h" #include "imap-parser.h" #include "imap-util.h" void imap_write_flags(string_t *dest, enum mail_flags flags, const char *const *keywords) { size_t size; size = str_len(dest); if ((flags & MAIL_ANSWERED) != 0) str_append(dest, "\\Answered "); if ((flags & MAIL_FLAGGED) != 0) str_append(dest, "\\Flagged "); if ((flags & MAIL_DELETED) != 0) str_append(dest, "\\Deleted "); if ((flags & MAIL_SEEN) != 0) str_append(dest, "\\Seen "); if ((flags & MAIL_DRAFT) != 0) str_append(dest, "\\Draft "); if ((flags & MAIL_RECENT) != 0) str_append(dest, "\\Recent "); if (keywords != NULL) { /* we have keywords too */ while (*keywords != NULL) { str_append(dest, *keywords); str_append_c(dest, ' '); keywords++; } } if (str_len(dest) != size) str_truncate(dest, str_len(dest)-1); } enum mail_flags imap_parse_system_flag(const char *str) { if (strcasecmp(str, "\\Answered") == 0) return MAIL_ANSWERED; else if (strcasecmp(str, "\\Flagged") == 0) return MAIL_FLAGGED; else if (strcasecmp(str, "\\Deleted") == 0) return MAIL_DELETED; else if (strcasecmp(str, "\\Seen") == 0) return MAIL_SEEN; else if (strcasecmp(str, "\\Draft") == 0) return MAIL_DRAFT; else if (strcasecmp(str, "\\Recent") == 0) return MAIL_RECENT; else return 0; } void imap_write_seq_range(string_t *dest, const ARRAY_TYPE(seq_range) *array) { const struct seq_range *range; unsigned int i, count; range = array_get(array, &count); for (i = 0; i < count; i++) { if (i > 0) str_append_c(dest, ','); str_printfa(dest, "%u", range[i].seq1); if (range[i].seq1 != range[i].seq2) str_printfa(dest, ":%u", range[i].seq2); } } void imap_write_arg(string_t *dest, const struct imap_arg *arg) { switch (arg->type) { case IMAP_ARG_NIL: str_append(dest, "NIL"); break; case IMAP_ARG_ATOM: str_append(dest, imap_arg_as_astring(arg)); break; case IMAP_ARG_STRING: str_append_c(dest, '"'); str_append(dest, str_escape(imap_arg_as_astring(arg))); str_append_c(dest, '"'); break; case IMAP_ARG_LITERAL: { const char *strarg = imap_arg_as_astring(arg); str_printfa(dest, "{%"PRIuSIZE_T"}\r\n", strlen(strarg)); str_append(dest, strarg); break; } case IMAP_ARG_LIST: str_append_c(dest, '('); imap_write_args(dest, imap_arg_as_list(arg)); str_append_c(dest, ')'); break; case IMAP_ARG_LITERAL_SIZE: case IMAP_ARG_LITERAL_SIZE_NONSYNC: str_printfa(dest, "{%"PRIuUOFF_T"}\r\n", imap_arg_as_literal_size(arg)); str_append(dest, ""); break; case IMAP_ARG_EOL: i_unreached(); } } void imap_write_args(string_t *dest, const struct imap_arg *args) { bool first = TRUE; for (; !IMAP_ARG_IS_EOL(args); args++) { if (first) first = FALSE; else str_append_c(dest, ' '); imap_write_arg(dest, args); } } static void imap_human_args_fix_control_chars(char *str) { size_t i; for (i = 0; str[i] != '\0'; i++) { if (str[i] < 0x20 || str[i] == 0x7f) str[i] = '?'; } } void imap_write_args_for_human(string_t *dest, const struct imap_arg *args) { bool first = TRUE; for (; !IMAP_ARG_IS_EOL(args); args++) { if (first) first = FALSE; else str_append_c(dest, ' '); switch (args->type) { case IMAP_ARG_NIL: str_append(dest, "NIL"); break; case IMAP_ARG_ATOM: /* atom has only printable us-ascii chars */ str_append(dest, imap_arg_as_astring(args)); break; case IMAP_ARG_STRING: case IMAP_ARG_LITERAL: { const char *strarg = imap_arg_as_astring(args); if (strpbrk(strarg, "\r\n") != NULL) { str_printfa(dest, "<%"PRIuSIZE_T" byte multi-line literal>", strlen(strarg)); break; } strarg = str_escape(strarg); str_append_c(dest, '"'); size_t start_pos = str_len(dest); /* append only valid UTF-8 chars */ if (uni_utf8_get_valid_data((const unsigned char *)strarg, strlen(strarg), dest)) str_append(dest, strarg); /* replace all control chars */ imap_human_args_fix_control_chars( str_c_modifiable(dest) + start_pos); str_append_c(dest, '"'); break; } case IMAP_ARG_LIST: str_append_c(dest, '('); imap_write_args_for_human(dest, imap_arg_as_list(args)); str_append_c(dest, ')'); break; case IMAP_ARG_LITERAL_SIZE: case IMAP_ARG_LITERAL_SIZE_NONSYNC: str_printfa(dest, "<%"PRIuUOFF_T" byte literal>", imap_arg_as_literal_size(args)); break; case IMAP_ARG_EOL: i_unreached(); } } } const char *imap_args_to_str(const struct imap_arg *args) { string_t *str; str = t_str_new(128); imap_write_args(str, args); return str_c(str); } dovecot-2.2.33.2/src/lib-imap/imap-bodystructure.c0000644000175000017500000005556113165463624016642 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "message-part-data.h" #include "message-parser.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "imap-parser.h" #include "imap-quote.h" #include "imap-envelope.h" #include "imap-bodystructure.h" #define EMPTY_BODY "(\"text\" \"plain\" " \ "(\"charset\" \""MESSAGE_PART_DEFAULT_CHARSET"\") NIL NIL \"7bit\" 0 0)" #define EMPTY_BODYSTRUCTURE "(\"text\" \"plain\" " \ "(\"charset\" \""MESSAGE_PART_DEFAULT_CHARSET"\") NIL NIL \"7bit\" 0 0 " \ "NIL NIL NIL NIL)" /* * IMAP BODY/BODYSTRUCTURE write */ static void params_write(const struct message_part_param *params, unsigned int params_count, string_t *str, bool default_charset) { unsigned int i; bool seen_charset; if (!default_charset && params_count == 0) { str_append(str, "NIL"); return; } str_append_c(str, '('); seen_charset = FALSE; for (i = 0; i < params_count; i++) { if (i > 0) str_append_c(str, ' '); if (default_charset && strcasecmp(params[i].name, "charset") == 0) seen_charset = TRUE; imap_append_string(str, params[i].name); str_append_c(str, ' '); imap_append_string(str, params[i].value); } if (default_charset && !seen_charset) { if (i > 0) str_append_c(str, ' '); str_append(str, "\"charset\" " "\""MESSAGE_PART_DEFAULT_CHARSET"\""); } str_append_c(str, ')'); } static void part_write_bodystructure_siblings(const struct message_part *part, string_t *dest, bool extended) { for (; part != NULL; part = part->next) { str_append_c(dest, '('); imap_bodystructure_write(part, dest, extended); str_append_c(dest, ')'); } } static void part_write_bodystructure_common(const struct message_part_data *data, string_t *str) { str_append_c(str, ' '); if (data->content_disposition == NULL) str_append(str, "NIL"); else { str_append_c(str, '('); imap_append_string(str, data->content_disposition); str_append_c(str, ' '); params_write(data->content_disposition_params, data->content_disposition_params_count, str, FALSE); str_append_c(str, ')'); } str_append_c(str, ' '); if (data->content_language == NULL) str_append(str, "NIL"); else { const char *const *lang = data->content_language; i_assert(*lang != NULL); str_append_c(str, '('); imap_append_string(str, *lang); lang++; while (*lang != NULL) { str_append_c(str, ' '); imap_append_string(str, *lang); lang++; } str_append_c(str, ')'); } str_append_c(str, ' '); imap_append_nstring_nolf(str, data->content_location); } static void part_write_body_multipart(const struct message_part *part, string_t *str, bool extended) { const struct message_part_data *data = part->data; i_assert(part->data != NULL); if (part->children != NULL) part_write_bodystructure_siblings(part->children, str, extended); else { /* no parts in multipart message, that's not allowed. write a single 0-length text/plain structure */ if (!extended) str_append(str, EMPTY_BODY); else str_append(str, EMPTY_BODYSTRUCTURE); } str_append_c(str, ' '); if (data->content_subtype != NULL) imap_append_string(str, data->content_subtype); else str_append(str, "\"x-unknown\""); if (!extended) return; /* BODYSTRUCTURE data */ str_append_c(str, ' '); params_write(data->content_type_params, data->content_type_params_count, str, FALSE); part_write_bodystructure_common(data, str); } static void part_write_body(const struct message_part *part, string_t *str, bool extended) { const struct message_part_data *data = part->data; bool text; i_assert(part->data != NULL); if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0) { str_append(str, "\"message\" \"rfc822\""); text = FALSE; } else { /* "content type" "subtype" */ if (data->content_type == NULL) { text = TRUE; str_append(str, "\"text\""); } else { text = (strcasecmp(data->content_type, "text") == 0); imap_append_string(str, data->content_type); } str_append_c(str, ' '); if (data->content_subtype != NULL) imap_append_string(str, data->content_subtype); else { if (text) str_append(str, "\"plain\""); else str_append(str, "\"unknown\""); } } /* ("content type param key" "value" ...) */ str_append_c(str, ' '); params_write(data->content_type_params, data->content_type_params_count, str, text); str_append_c(str, ' '); imap_append_nstring_nolf(str, data->content_id); str_append_c(str, ' '); imap_append_nstring_nolf(str, data->content_description); str_append_c(str, ' '); if (data->content_transfer_encoding != NULL) imap_append_string(str, data->content_transfer_encoding); else str_append(str, "\"7bit\""); str_printfa(str, " %"PRIuUOFF_T, part->body_size.virtual_size); if (text) { /* text/.. contains line count */ str_printfa(str, " %u", part->body_size.lines); } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) { /* message/rfc822 contains envelope + body + line count */ const struct message_part_data *child_data; i_assert(part->children != NULL); i_assert(part->children->next == NULL); child_data = part->children->data; str_append(str, " ("); imap_envelope_write(child_data->envelope, str); str_append(str, ") "); part_write_bodystructure_siblings(part->children, str, extended); str_printfa(str, " %u", part->body_size.lines); } if (!extended) return; /* BODYSTRUCTURE data */ /* "md5" ("content disposition" ("disposition" "params")) ("body" "language" "params") "location" */ str_append_c(str, ' '); imap_append_nstring_nolf(str, data->content_md5); part_write_bodystructure_common(data, str); } void imap_bodystructure_write(const struct message_part *part, string_t *dest, bool extended) { if (part->flags & MESSAGE_PART_FLAG_MULTIPART) part_write_body_multipart(part, dest, extended); else part_write_body(part, dest, extended); } /* * IMAP BODYSTRUCTURE parsing */ static int imap_bodystructure_strlist_parse(const struct imap_arg *arg, pool_t pool, const char *const **list_r) { const char *item, **list; const struct imap_arg *list_args; unsigned int list_count, i; if (arg->type == IMAP_ARG_NIL) { *list_r = NULL; return 0; } if (imap_arg_get_nstring(arg, &item)) { list = p_new(pool, const char *, 2); list[0] = p_strdup(pool, item); } else { if (!imap_arg_get_list_full(arg, &list_args, &list_count)) return -1; list = p_new(pool, const char *, list_count+1); for (i = 0; i < list_count; i++) { if (!imap_arg_get_nstring(&list_args[i], &item)) return -1; list[i] = p_strdup(pool, item); } } *list_r = list; return 0; } static int imap_bodystructure_params_parse(const struct imap_arg *arg, pool_t pool, const struct message_part_param **params_r, unsigned int *count_r) { struct message_part_param *params; const struct imap_arg *list_args; unsigned int list_count, params_count, i; if (arg->type == IMAP_ARG_NIL) { *params_r = NULL; return 0; } if (!imap_arg_get_list_full(arg, &list_args, &list_count)) return -1; if ((list_count % 2) != 0) return -1; params_count = list_count/2; params = p_new(pool, struct message_part_param, params_count+1); for (i = 0; i < params_count; i++) { const char *name, *value; if (!imap_arg_get_nstring(&list_args[i*2+0], &name)) return -1; if (!imap_arg_get_nstring(&list_args[i*2+1], &value)) return -1; params[i].name = p_strdup(pool, name); params[i].value = p_strdup(pool, value); } *params_r = params; *count_r = params_count; return 0; } static int imap_bodystructure_parse_args_common(struct message_part *part, pool_t pool, const struct imap_arg *args, const char **error_r) { struct message_part_data *data = part->data; const struct imap_arg *list_args; if (args->type == IMAP_ARG_EOL) return 0; if (args->type == IMAP_ARG_NIL) args++; else if (!imap_arg_get_list(args, &list_args)) { *error_r = "Invalid content-disposition list"; return -1; } else { if (!imap_arg_get_nstring (list_args++, &data->content_disposition)) { *error_r = "Invalid content-disposition"; return -1; } data->content_disposition = p_strdup(pool, data->content_disposition); if (imap_bodystructure_params_parse(list_args, pool, &data->content_disposition_params, &data->content_disposition_params_count) < 0) { *error_r = "Invalid content-disposition params"; return -1; } args++; } if (args->type == IMAP_ARG_EOL) return 0; if (imap_bodystructure_strlist_parse (args++, pool, &data->content_language) < 0) { *error_r = "Invalid content-language"; return -1; } if (args->type == IMAP_ARG_EOL) return 0; if (!imap_arg_get_nstring (args++, &data->content_location)) { *error_r = "Invalid content-location"; return -1; } data->content_location = p_strdup(pool, data->content_location); return 0; } int imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool, struct message_part **_part, const char **error_r) { struct message_part *part = *_part, *child_part;; struct message_part **child_part_p; struct message_part_data *data; const struct imap_arg *list_args; const char *value, *content_type, *subtype, *error; bool multipart, text, message_rfc822, parsing_tree, has_lines; unsigned int lines; uoff_t vsize; if (part != NULL) { /* parsing with pre-existing message_part tree */ parsing_tree = FALSE; } else { /* parsing message_part tree from BODYSTRUCTURE as well */ part = *_part = p_new(pool, struct message_part, 1); parsing_tree = TRUE; } part->data = data = p_new(pool, struct message_part_data, 1); multipart = FALSE; if (!parsing_tree) { if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && part->children == NULL) { struct message_part_data dummy_part_data = { .content_type = "text", .content_subtype = "plain", .content_transfer_encoding = "7bit" }; struct message_part dummy_part = { .parent = part, .data = &dummy_part_data, .flags = MESSAGE_PART_FLAG_TEXT }; struct message_part *dummy_partp = &dummy_part; /* no parts in multipart message, that's not allowed. expect a single 0-length text/plain structure */ if (args->type != IMAP_ARG_LIST || (args+1)->type == IMAP_ARG_LIST) { *error_r = "message_part hierarchy " "doesn't match BODYSTRUCTURE"; return -1; } list_args = imap_arg_as_list(args); if (imap_bodystructure_parse_args(list_args, pool, &dummy_partp, error_r) < 0) return -1; child_part = NULL; multipart = TRUE; args++; } else { child_part = part->children; while (args->type == IMAP_ARG_LIST) { if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 || child_part == NULL) { *error_r = "message_part hierarchy " "doesn't match BODYSTRUCTURE"; return -1; } list_args = imap_arg_as_list(args); if (imap_bodystructure_parse_args(list_args, pool, &child_part, error_r) < 0) return -1; child_part = child_part->next; multipart = TRUE; args++; } } if (multipart) { if (child_part != NULL) { *error_r = "message_part hierarchy " "doesn't match BODYSTRUCTURE"; return -1; } } else if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) { *error_r = "message_part multipart flag " "doesn't match BODYSTRUCTURE"; return -1; } } else { child_part_p = &part->children; while (args->type == IMAP_ARG_LIST) { list_args = imap_arg_as_list(args); if (imap_bodystructure_parse_args(list_args, pool, child_part_p, error_r) < 0) return -1; (*child_part_p)->parent = part; child_part_p = &(*child_part_p)->next; multipart = TRUE; args++; } if (multipart) { part->flags |= MESSAGE_PART_FLAG_MULTIPART; } } if (multipart) { data->content_type = "multipart"; if (!imap_arg_get_nstring(args++, &data->content_subtype)) { *error_r = "Invalid multipart content-type"; return -1; } data->content_subtype = p_strdup(pool, data->content_subtype); if (args->type == IMAP_ARG_EOL) return 0; if (imap_bodystructure_params_parse(args++, pool, &data->content_type_params, &data->content_type_params_count) < 0) { *error_r = "Invalid content params"; return -1; } return imap_bodystructure_parse_args_common (part, pool, args, error_r); } /* "content type" "subtype" */ if (!imap_arg_get_astring(&args[0], &content_type) || !imap_arg_get_astring(&args[1], &subtype)) { *error_r = "Invalid content-type"; return -1; } data->content_type = p_strdup(pool, content_type); data->content_subtype = p_strdup(pool, subtype); args += 2; text = strcasecmp(content_type, "text") == 0; message_rfc822 = strcasecmp(content_type, "message") == 0 && strcasecmp(subtype, "rfc822") == 0; if (!parsing_tree) { #if 0 /* Disabled for now. Earlier Dovecot versions handled broken Content-Type headers by writing them as "text" "plain" to BODYSTRUCTURE reply, but the message_part didn't have MESSAGE_PART_FLAG_TEXT. */ if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) { *error_r = "message_part text flag " "doesn't match BODYSTRUCTURE"; return -1; } #endif if (message_rfc822 != ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)) { *error_r = "message_part message/rfc822 flag " "doesn't match BODYSTRUCTURE"; return -1; } } else { if (text) part->flags |= MESSAGE_PART_FLAG_TEXT; if (message_rfc822) part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822; } /* ("content type param key" "value" ...) | NIL */ if (imap_bodystructure_params_parse(args++, pool, &data->content_type_params, &data->content_type_params_count) < 0) { *error_r = "Invalid content params"; return -1; } /* "content id" "content description" "transfer encoding" size */ if (!imap_arg_get_nstring(args++, &data->content_id)) { *error_r = "Invalid content-id"; return -1; } if (!imap_arg_get_nstring(args++, &data->content_description)) { *error_r = "Invalid content-description"; return -1; } if (!imap_arg_get_nstring(args++, &data->content_transfer_encoding)) { *error_r = "Invalid content-transfer-encoding"; return -1; } data->content_id = p_strdup(pool, data->content_id); data->content_description = p_strdup(pool, data->content_description); data->content_transfer_encoding = p_strdup(pool, data->content_transfer_encoding); if (!imap_arg_get_atom(args++, &value) || str_to_uoff(value, &vsize) < 0) { *error_r = "Invalid size field"; return -1; } if (!parsing_tree) { if (vsize != part->body_size.virtual_size) { *error_r = "message_part virtual_size doesn't match " "size in BODYSTRUCTURE"; return -1; } } else { part->body_size.virtual_size = vsize; } if (text) { /* text/xxx - text lines */ if (!imap_arg_get_atom(args++, &value) || str_to_uint(value, &lines) < 0) { *error_r = "Invalid lines field"; return -1; } i_assert(part->children == NULL); has_lines = TRUE; } else if (message_rfc822) { /* message/rfc822 - envelope + bodystructure + text lines */ if (!parsing_tree) { i_assert(part->children != NULL && part->children->next == NULL); } if (!imap_arg_get_list(&args[1], &list_args)) { *error_r = "Child bodystructure list expected"; return -1; } if (imap_bodystructure_parse_args (list_args, pool, &part->children, error_r) < 0) return -1; if (parsing_tree) { i_assert(part->children != NULL && part->children->next == NULL); part->children->parent = part; } if (!imap_arg_get_list(&args[0], &list_args)) { *error_r = "Envelope list expected"; return -1; } if (!imap_envelope_parse_args(list_args, pool, &part->children->data->envelope, &error)) { *error_r = t_strdup_printf ("Invalid envelope list: %s", error); return -1; } args += 2; if (!imap_arg_get_atom(args++, &value) || str_to_uint(value, &lines) < 0) { *error_r = "Invalid lines field"; return -1; } has_lines = TRUE; } else { i_assert(part->children == NULL); lines = 0; has_lines = FALSE; } if (!parsing_tree) { if (has_lines && lines != part->body_size.lines) { *error_r = "message_part lines " "doesn't match lines in BODYSTRUCTURE"; return -1; } } else { part->body_size.lines = lines; } if (args->type == IMAP_ARG_EOL) return 0; if (!imap_arg_get_nstring(args++, &data->content_md5)) { *error_r = "Invalid content-md5"; return -1; } data->content_md5 = p_strdup(pool, data->content_md5); return imap_bodystructure_parse_args_common (part, pool, args, error_r); } int imap_bodystructure_parse_full(const char *bodystructure, pool_t pool, struct message_part **parts, const char **error_r) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; char *error; int ret; bool fatal; i_assert(*parts == NULL || (*parts)->next == NULL); input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret < 0) { *error_r = t_strdup_printf("IMAP parser failed: %s", imap_parser_get_error(parser, &fatal)); } else if (ret == 0) { *error_r = "Empty bodystructure"; ret = -1; } else { T_BEGIN { ret = imap_bodystructure_parse_args (args, pool, parts, error_r); if (ret < 0) error = i_strdup(*error_r); } T_END; if (ret < 0) { *error_r = t_strdup(error); i_free(error); } } imap_parser_unref(&parser); i_stream_destroy(&input); return ret; } int imap_bodystructure_parse(const char *bodystructure, pool_t pool, struct message_part *parts, const char **error_r) { i_assert(parts != NULL); return imap_bodystructure_parse_full(bodystructure, pool, &parts, error_r); } /* * IMAP BODYSTRUCTURE to BODY conversion */ static bool str_append_nstring(string_t *str, const struct imap_arg *arg) { const char *cstr; if (!imap_arg_get_nstring(arg, &cstr)) return FALSE; switch (arg->type) { case IMAP_ARG_NIL: str_append(str, "NIL"); break; case IMAP_ARG_ATOM: str_append(str, cstr); break; case IMAP_ARG_STRING: str_append_c(str, '"'); /* NOTE: we're parsing with no-unescape flag, so don't double-escape it here */ str_append(str, cstr); str_append_c(str, '"'); break; case IMAP_ARG_LITERAL: { str_printfa(str, "{%"PRIuSIZE_T"}\r\n", strlen(cstr)); str_append(str, cstr); break; } default: i_unreached(); return FALSE; } return TRUE; } static void imap_write_envelope_list(const struct imap_arg *args, string_t *str, bool toplevel) { const struct imap_arg *children; /* don't do any typechecking, just write it out */ while (!IMAP_ARG_IS_EOL(args)) { bool list = FALSE; if (!str_append_nstring(str, args)) { if (!imap_arg_get_list(args, &children)) { /* everything is either nstring or list */ i_unreached(); } str_append_c(str, '('); imap_write_envelope_list(children, str, FALSE); str_append_c(str, ')'); list = TRUE; } args++; if ((toplevel || !list) && !IMAP_ARG_IS_EOL(args)) str_append_c(str, ' '); } } static void imap_write_envelope(const struct imap_arg *args, string_t *str) { imap_write_envelope_list(args, str, TRUE); } static int imap_parse_bodystructure_args(const struct imap_arg *args, string_t *str, const char **error_r) { const struct imap_arg *subargs; const struct imap_arg *list_args; const char *value, *content_type, *subtype; bool multipart, text, message_rfc822; int i; multipart = FALSE; while (args->type == IMAP_ARG_LIST) { str_append_c(str, '('); list_args = imap_arg_as_list(args); if (imap_parse_bodystructure_args(list_args, str, error_r) < 0) return -1; str_append_c(str, ')'); multipart = TRUE; args++; } if (multipart) { /* next is subtype of Content-Type. rest is skipped. */ str_append_c(str, ' '); if (!str_append_nstring(str, args)) { *error_r = "Invalid multipart content-type"; return -1; } return 0; } /* "content type" "subtype" */ if (!imap_arg_get_astring(&args[0], &content_type) || !imap_arg_get_astring(&args[1], &subtype)) { *error_r = "Invalid content-type"; return -1; } if (!str_append_nstring(str, &args[0])) i_unreached(); str_append_c(str, ' '); if (!str_append_nstring(str, &args[1])) i_unreached(); text = strcasecmp(content_type, "text") == 0; message_rfc822 = strcasecmp(content_type, "message") == 0 && strcasecmp(subtype, "rfc822") == 0; args += 2; /* ("content type param key" "value" ...) | NIL */ if (imap_arg_get_list(args, &subargs)) { str_append(str, " ("); while (!IMAP_ARG_IS_EOL(subargs)) { if (!str_append_nstring(str, &subargs[0])) { *error_r = "Invalid content param key"; return -1; } str_append_c(str, ' '); if (!str_append_nstring(str, &subargs[1])) { *error_r = "Invalid content param value"; return -1; } subargs += 2; if (IMAP_ARG_IS_EOL(subargs)) break; str_append_c(str, ' '); } str_append(str, ")"); } else if (args->type == IMAP_ARG_NIL) { str_append(str, " NIL"); } else { *error_r = "list/NIL expected"; return -1; } args++; /* "content id" "content description" "transfer encoding" size */ for (i = 0; i < 4; i++, args++) { str_append_c(str, ' '); if (!str_append_nstring(str, args)) { *error_r = "nstring expected"; return -1; } } if (text) { /* text/xxx - text lines */ if (!imap_arg_get_atom(args, &value)) { *error_r = "Text lines expected"; return -1; } str_append_c(str, ' '); str_append(str, value); } else if (message_rfc822) { /* message/rfc822 - envelope + bodystructure + text lines */ str_append_c(str, ' '); if (!imap_arg_get_list(&args[0], &list_args)) { *error_r = "Envelope list expected"; return -1; } str_append_c(str, '('); imap_write_envelope(list_args, str); str_append(str, ") ("); if (!imap_arg_get_list(&args[1], &list_args)) { *error_r = "Child bodystructure list expected"; return -1; } if (imap_parse_bodystructure_args(list_args, str, error_r) < 0) return -1; str_append(str, ") "); if (!imap_arg_get_atom(&args[2], &value)) { *error_r = "Text lines expected"; return -1; } str_append(str, value); } return 0; } int imap_body_parse_from_bodystructure(const char *bodystructure, string_t *dest, const char **error_r) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; bool fatal; int ret; input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret < 0) { *error_r = t_strdup_printf("IMAP parser failed: %s", imap_parser_get_error(parser, &fatal)); } else if (ret == 0) { *error_r = "Empty bodystructure"; ret = -1; } else { ret = imap_parse_bodystructure_args(args, dest, error_r); } imap_parser_unref(&parser); i_stream_destroy(&input); return ret; } dovecot-2.2.33.2/src/lib-imap/Makefile.in0000644000175000017500000007353313172375573014703 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-imap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libimap_la_LIBADD = am_libimap_la_OBJECTS = imap-arg.lo imap-base-subject.lo \ imap-bodystructure.lo imap-date.lo imap-envelope.lo imap-id.lo \ imap-keepalive.lo imap-match.lo imap-parser.lo imap-quote.lo \ imap-url.lo imap-seqset.lo imap-utf7.lo imap-util.lo libimap_la_OBJECTS = $(am_libimap_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-imap-bodystructure$(EXEEXT) \ test-imap-envelope$(EXEEXT) test-imap-match$(EXEEXT) \ test-imap-parser$(EXEEXT) test-imap-quote$(EXEEXT) \ test-imap-url$(EXEEXT) test-imap-utf7$(EXEEXT) \ test-imap-util$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_imap_bodystructure_OBJECTS = \ test-imap-bodystructure.$(OBJEXT) test_imap_bodystructure_OBJECTS = \ $(am_test_imap_bodystructure_OBJECTS) am_test_imap_envelope_OBJECTS = test-imap-envelope.$(OBJEXT) test_imap_envelope_OBJECTS = $(am_test_imap_envelope_OBJECTS) am_test_imap_match_OBJECTS = test-imap-match.$(OBJEXT) test_imap_match_OBJECTS = $(am_test_imap_match_OBJECTS) am_test_imap_parser_OBJECTS = test-imap-parser.$(OBJEXT) test_imap_parser_OBJECTS = $(am_test_imap_parser_OBJECTS) am_test_imap_quote_OBJECTS = test-imap-quote.$(OBJEXT) test_imap_quote_OBJECTS = $(am_test_imap_quote_OBJECTS) am_test_imap_url_OBJECTS = test-imap-url.$(OBJEXT) test_imap_url_OBJECTS = $(am_test_imap_url_OBJECTS) am_test_imap_utf7_OBJECTS = test-imap-utf7.$(OBJEXT) test_imap_utf7_OBJECTS = $(am_test_imap_utf7_OBJECTS) am_test_imap_util_OBJECTS = test-imap-util.$(OBJEXT) test_imap_util_OBJECTS = $(am_test_imap_util_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libimap_la_SOURCES) $(test_imap_bodystructure_SOURCES) \ $(test_imap_envelope_SOURCES) $(test_imap_match_SOURCES) \ $(test_imap_parser_SOURCES) $(test_imap_quote_SOURCES) \ $(test_imap_url_SOURCES) $(test_imap_utf7_SOURCES) \ $(test_imap_util_SOURCES) DIST_SOURCES = $(libimap_la_SOURCES) \ $(test_imap_bodystructure_SOURCES) \ $(test_imap_envelope_SOURCES) $(test_imap_match_SOURCES) \ $(test_imap_parser_SOURCES) $(test_imap_quote_SOURCES) \ $(test_imap_url_SOURCES) $(test_imap_utf7_SOURCES) \ $(test_imap_util_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libimap.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail libimap_la_SOURCES = \ imap-arg.c \ imap-base-subject.c \ imap-bodystructure.c \ imap-date.c \ imap-envelope.c \ imap-id.c \ imap-keepalive.c \ imap-match.c \ imap-parser.c \ imap-quote.c \ imap-url.c \ imap-seqset.c \ imap-utf7.c \ imap-util.c headers = \ imap-arg.h \ imap-base-subject.h \ imap-bodystructure.h \ imap-date.h \ imap-envelope.h \ imap-id.h \ imap-keepalive.h \ imap-match.h \ imap-parser.h \ imap-resp-code.h \ imap-quote.h \ imap-url.h \ imap-seqset.h \ imap-utf7.h \ imap-util.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-imap-bodystructure \ test-imap-envelope \ test-imap-match \ test-imap-parser \ test-imap-quote \ test-imap-url \ test-imap-utf7 \ test-imap-util test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_imap_bodystructure_SOURCES = test-imap-bodystructure.c test_imap_bodystructure_LDADD = imap-bodystructure.lo imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs) test_imap_bodystructure_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la test_imap_envelope_SOURCES = test-imap-envelope.c test_imap_envelope_LDADD = imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs) test_imap_envelope_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la test_imap_match_SOURCES = test-imap-match.c test_imap_match_LDADD = imap-match.lo $(test_libs) test_imap_match_DEPENDENCIES = $(test_deps) test_imap_parser_SOURCES = test-imap-parser.c test_imap_parser_LDADD = imap-parser.lo imap-arg.lo $(test_libs) test_imap_parser_DEPENDENCIES = $(test_deps) test_imap_quote_SOURCES = test-imap-quote.c test_imap_quote_LDADD = imap-quote.lo $(test_libs) test_imap_quote_DEPENDENCIES = $(test_deps) test_imap_url_SOURCES = test-imap-url.c test_imap_url_LDADD = imap-url.lo $(test_libs) test_imap_url_DEPENDENCIES = $(test_deps) test_imap_utf7_SOURCES = test-imap-utf7.c test_imap_utf7_LDADD = imap-utf7.lo $(test_libs) test_imap_utf7_DEPENDENCIES = $(test_deps) test_imap_util_SOURCES = test-imap-util.c test_imap_util_LDADD = imap-util.lo imap-arg.lo $(test_libs) test_imap_util_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-imap/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-imap/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libimap.la: $(libimap_la_OBJECTS) $(libimap_la_DEPENDENCIES) $(EXTRA_libimap_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libimap_la_OBJECTS) $(libimap_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-imap-bodystructure$(EXEEXT): $(test_imap_bodystructure_OBJECTS) $(test_imap_bodystructure_DEPENDENCIES) $(EXTRA_test_imap_bodystructure_DEPENDENCIES) @rm -f test-imap-bodystructure$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_bodystructure_OBJECTS) $(test_imap_bodystructure_LDADD) $(LIBS) test-imap-envelope$(EXEEXT): $(test_imap_envelope_OBJECTS) $(test_imap_envelope_DEPENDENCIES) $(EXTRA_test_imap_envelope_DEPENDENCIES) @rm -f test-imap-envelope$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_envelope_OBJECTS) $(test_imap_envelope_LDADD) $(LIBS) test-imap-match$(EXEEXT): $(test_imap_match_OBJECTS) $(test_imap_match_DEPENDENCIES) $(EXTRA_test_imap_match_DEPENDENCIES) @rm -f test-imap-match$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_match_OBJECTS) $(test_imap_match_LDADD) $(LIBS) test-imap-parser$(EXEEXT): $(test_imap_parser_OBJECTS) $(test_imap_parser_DEPENDENCIES) $(EXTRA_test_imap_parser_DEPENDENCIES) @rm -f test-imap-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_parser_OBJECTS) $(test_imap_parser_LDADD) $(LIBS) test-imap-quote$(EXEEXT): $(test_imap_quote_OBJECTS) $(test_imap_quote_DEPENDENCIES) $(EXTRA_test_imap_quote_DEPENDENCIES) @rm -f test-imap-quote$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_quote_OBJECTS) $(test_imap_quote_LDADD) $(LIBS) test-imap-url$(EXEEXT): $(test_imap_url_OBJECTS) $(test_imap_url_DEPENDENCIES) $(EXTRA_test_imap_url_DEPENDENCIES) @rm -f test-imap-url$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_url_OBJECTS) $(test_imap_url_LDADD) $(LIBS) test-imap-utf7$(EXEEXT): $(test_imap_utf7_OBJECTS) $(test_imap_utf7_DEPENDENCIES) $(EXTRA_test_imap_utf7_DEPENDENCIES) @rm -f test-imap-utf7$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_utf7_OBJECTS) $(test_imap_utf7_LDADD) $(LIBS) test-imap-util$(EXEEXT): $(test_imap_util_OBJECTS) $(test_imap_util_DEPENDENCIES) $(EXTRA_test_imap_util_DEPENDENCIES) @rm -f test-imap-util$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imap_util_OBJECTS) $(test_imap_util_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-arg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-base-subject.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-bodystructure.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-date.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-envelope.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-id.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-keepalive.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-quote.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-seqset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-url.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-utf7.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-bodystructure.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-envelope.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-match.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-quote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-utf7.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imap-util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-imap/test-imap-bodystructure.c0000644000175000017500000005102313165463624017604 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "str.h" #include "message-part-data.h" #include "message-parser.h" #include "imap-bodystructure.h" #include "test-common.h" struct parse_test { const char *message; const char *body; const char *bodystructure; }; struct parse_test parse_tests[] = { { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "body\n", .bodystructure = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 NIL NIL NIL NIL", .body = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "\n" "body\n" "\n", .bodystructure = "\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"8bit\" 8 2 NIL NIL NIL NIL", .body = "\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"8bit\" 8 2" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2007 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo\n" " bar\"\n" "\n" "--foo bar\n" "Content-Type: text/x-myown; charset=us-ascii\n" "\n" "hello\n" "\n" "--foo bar--\n" "\n", .bodystructure = "(\"text\" \"x-myown\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 7 1 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL", .body = "(\"text\" \"x-myown\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 7 1) \"mixed\"" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "See attached...\n" "\n" "--foo bar\n" "Content-Type: message/rfc822\n" "\n" "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "body\n" "\n" "--foo bar--\n" "\n", .bodystructure = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL NIL NIL)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 NIL NIL NIL NIL) 6 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL", .body = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1) 6) \"mixed\"" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "Content-ID: \n" "Content-Description: Container message\n" "\n" "See attached...\n" "\n" "--foo bar\n" "Content-Type: message/rfc822\n" "Content-ID: \n" "Content-Description: Forwarded\n" "\n" "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "body\n" "\n" "--foo bar--\n", .bodystructure = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") \"\" \"Container message\" \"7bit\" 17 1 NIL NIL NIL NIL)(\"message\" \"rfc822\" NIL \"\" \"Forwarded\" \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 NIL NIL NIL NIL) 6 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL", .body = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") \"\" \"Container message\" \"7bit\" 17 1)(\"message\" \"rfc822\" NIL \"\" \"Forwarded\" \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1) 6) \"mixed\"" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii; format=\"flowed\";\n" " delsp=\"no\"\n" "Content-Language: la\n" "\n" "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo\n" "ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis\n" "parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec,\n" "pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec\n" "pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo,\n" "rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede\n" "mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper\n" "nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu,\n" "consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra\n" "quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet.\n" "Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur\n" "ullamcorper ultricies nisi. Nam eget dui.\n" "\n" "--foo bar\n" "Content-Type: image/png\n" "Content-Transfer-Encoding: base64\n" "Content-Disposition: attachment; filename=\"pigeon.png\"\n" "\n" "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n" "AAAGJwAABicBTVTYxwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAN2SURB\n" "VEiJ7ZfdK7tvHMffZuWpKTlR1my3bTEPU0hJcsABh0QrKYUTJ5KyHCzlyPwBkpSyg1mUpB3ILYna\n" "lEYKcbADm4cDD7NpM2bv75HF1/az8l3fX/1+77pO7vu6rtf1/jx0X3caSeIvSfK3wP/DPykcDsNs\n" "NqOqqgpdXV3Y2NhIGVz6+4PJyUlcXFxAFEV4vV709fVBrVZDpVL9eTo/aGVlhXl5efT5fDw/P2c4\n" "HKbdbmd3dzdTIZDk6+srh4eHqdPpqNfrSZJGo5H7+/sMBoNUKpUpgUsAQCqVQq1WIxgMfgp/dXU1\n" "srKykJ+fD6/X+8ejHiu4wcFB2Gy2uJPq6uqwt7eXOjgAaLXahHCn05laeCKlyvmXVosnrVaLQCCA\n" "4uJiyOVyCIIQGyqVCoIgoKCgIDXwtLQ0HBwc4O3tDR6PB263G263G8fHx1hbW4Pb7cbNzQ1yc3Nj\n" "hxEEAdXV1WhoaEi88cfSf3h4iLXaR01MTFChUFChULCjo4M7OztxW8fn89HlcnF5eZlTU1Nsb29n\n" "W1sb/X5/3PlJwd/19vbG7e1tlpWVcXV19ftGJjk0NESbzRb33aeCy8zMhM/nSxgliUSCxsZGiKKI\n" "ra2tb9N1e3uLSCSC8fFxGAwGDAwMfC7c309TWFjI09PTpFwlis76+jo7OzspCAJNJhN3d3fpcDho\n" "tVqpUCgYCoVIkl8Krr6+Hna7HSUlJd86+yiPx4P5+XlYLBZUVFSgv78fVqsV6enpAIC7uzt4vV5E\n" "IhFIJJL4zp1OJ2traxmJRL51+fLywuXlZba2trK0tJRms5mXl5c8PT3l4uIix8bG2NbWRqVSyfLy\n" "cvb09PDw8DC2Po38eocbGRlBIBDA9PQ0pNKv3Xh2doa5uTksLCwgJycHNTU1yM3NxdHREa6vr6HR\n" "aKDX62NDp9MhIyPjyz5x4SQxOjoKURTR29sLmUyG7Oxs+P1+WCwW7O7uQiKRQKvVQq/Xo7KyMgaS\n" "y+VJpyou/F0nJydYWlqCy+XC1dUV7u/vYTQa8fT0BIfDgaWlpaRBcZVsFc/OznJsbIwkaTKZODMz\n" "k+zShEr6Arm5uYnm5mYAgCiKaGlp+ZnrZJ1Ho1EWFRXx+fmZj4+P1Gg0P3ZNJqj2dxkMhth3PBAI\n" "QCaTIRqNIhQKoamp6cc5/0d4qvXv+mn4z8B/AV1UVu6zi+zUAAAAAElFTkSuQmCC\n" "--foo bar--\n", .bodystructure = "(\"text\" \"plain\" (\"charset\" \"us-ascii\" \"format\" \"flowed\" \"delsp\" \"no\") NIL NIL \"7bit\" 881 12 NIL NIL (\"la\") NIL)(\"image\" \"png\" NIL NIL NIL \"base64\" 1390 NIL (\"attachment\" (\"filename\" \"pigeon.png\")) NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL", .body = "(\"text\" \"plain\" (\"charset\" \"us-ascii\" \"format\" \"flowed\" \"delsp\" \"no\") NIL NIL \"7bit\" 881 12)(\"image\" \"png\" NIL NIL NIL \"base64\" 1390) \"mixed\"" },{ .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2007 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo\n" " bar\"\n" "\n" "Root MIME prologue\n" "\n" "--foo bar\n" "Content-Type: text/x-myown; charset=us-ascii; foo=\"quoted\\\"string\"\n" "Content-ID: \n" "Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==\n" "Content-Disposition: inline; foo=bar\n" "Content-Description: hellodescription\n" "Content-Language: en, fi, se\n" "Content-Location: http://example.com/test.txt\n" "\n" "hello\n" "\n" "--foo bar\n" "Content-Type: message/rfc822\n" "\n" "From: sub@domain.org\n" "To: sub-to1@domain.org, sub-to2@domain.org\n" "Date: Sun, 12 Aug 2012 12:34:56 +0300\n" "Subject: submsg\n" "Content-Type: multipart/alternative; boundary=\"sub1\"\n" "\n" "Sub MIME prologue\n" "--sub1\n" "Content-Type: text/html\n" "Content-Transfer-Encoding: 8bit\n" "\n" "

Hello world

\n" "\n" "--sub1\n" "Content-Type: text/plain\n" "Content-Transfer-Encoding: ?invalid\n" "\n" "Hello another world\n" "\n" "--sub1--\n" "Sub MIME epilogue\n" "\n" "--foo bar--\n" "Root MIME epilogue\n", .bodystructure = "(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"\" \"hellodescription\" \"7bit\" 7 1 \"Q2hlY2sgSW50ZWdyaXR5IQ==\" (\"inline\" (\"foo\" \"bar\")) (\"en\" \"fi\" \"se\") \"http://example.com/test.txt\")(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 412 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub-to1\" \"domain.org\")(NIL NIL \"sub-to2\" \"domain.org\")) NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1 NIL NIL NIL NIL)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"sub1\") NIL NIL NIL) 21 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL", .body = "(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"\" \"hellodescription\" \"7bit\" 7 1)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 412 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub-to1\" \"domain.org\")(NIL NIL \"sub-to2\" \"domain.org\")) NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1) \"alternative\") 21) \"mixed\"" },{ .message = "Content-Type: multipart/mixed; boundary=\"foo\"\n" "\n", .bodystructure = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 0 0 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo\") NIL NIL NIL", .body = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 0 0) \"mixed\"" } }; static const unsigned int parse_tests_count = N_ELEMENTS(parse_tests); struct normalize_test { const char *message; const char *input; const char *output; }; struct normalize_test normalize_tests[] = { { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "body\n", .input = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1", .output = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 NIL NIL NIL NIL", }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "Content-MD5: ae6ba5b4c6eb1efd4a9fac3708046cbe\n" "\n" "body\n", .input = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 \"ae6ba5b4c6eb1efd4a9fac3708046cbe\"", .output = "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 \"ae6ba5b4c6eb1efd4a9fac3708046cbe\" NIL NIL NIL", }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "See attached...\n" "\n" "--foo bar\n" "Content-Type: message/rfc822\n" "\n" "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "body\n" "\n" "--foo bar--\n" "\n", .input = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1) 6) \"mixed\"", .output = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL NIL NIL)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 133 (\"Sat, 24 Mar 2017 23:00:00 +0200\" NIL ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) ((NIL NIL \"user\" \"domain.org\")) NIL NIL NIL NIL NIL) (\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 6 1 NIL NIL NIL NIL) 6 NIL NIL NIL NIL) \"mixed\" NIL NIL NIL NIL" }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "\n" "See attached...\n" "\n" "--foo bar--\n" "\n", .input = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1) \"mixed\" (\"boundary\" \"foo bar\")", .output = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL" }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "Content-MD5: 6537bae18ed07779c9dc25f24635b0f3\n" "\n" "See attached...\n" "\n" "--foo bar--\n" "\n", .input = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 \"6537bae18ed07779c9dc25f24635b0f3\") \"mixed\" (\"boundary\" \"foo bar\")", .output = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 \"6537bae18ed07779c9dc25f24635b0f3\" NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL" }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "Content-Language: en\n" "\n" "See attached...\n" "\n" "--foo bar--\n" "\n", .input = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL \"en\") \"mixed\" (\"boundary\" \"foo bar\")", .output = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL (\"en\") NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL" }, { .message = "From: user@domain.org\n" "Date: Sat, 24 Mar 2017 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "--foo bar\n" "Content-Type: text/plain; charset=us-ascii\n" "Content-Location: http://www.example.com/frop.txt\n" "\n" "See attached...\n" "\n" "--foo bar--\n" "\n", .input = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL NIL \"http://www.example.com/frop.txt\") \"mixed\" (\"boundary\" \"foo bar\")", .output = "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 17 1 NIL NIL NIL \"http://www.example.com/frop.txt\") \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL" } }; static const unsigned int normalize_tests_count = N_ELEMENTS(normalize_tests); static struct message_part * msg_parse(pool_t pool, const char *message, bool parse_bodystructure) { struct message_parser_ctx *parser; struct istream *input; struct message_block block; struct message_part *parts; int ret; input = i_stream_create_from_data(message, strlen(message)); parser = message_parser_init(pool, input, MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { if (parse_bodystructure) { message_part_data_parse_from_header(pool, block.part, block.hdr); } } test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); i_stream_unref(&input); return parts; } static void test_imap_bodystructure_write(void) { struct message_part *parts; unsigned int i; for (i = 0; i < parse_tests_count; i++) T_BEGIN { struct parse_test *test = &parse_tests[i]; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap bodystructure write", 1024); test_begin(t_strdup_printf("imap bodystructure write [%u]", i)); parts = msg_parse(pool, test->message, TRUE); imap_bodystructure_write(parts, str, TRUE); test_assert(strcmp(str_c(str), test->bodystructure) == 0); str_truncate(str, 0); imap_bodystructure_write(parts, str, FALSE); test_assert(strcmp(str_c(str), test->body) == 0); pool_unref(&pool); test_end(); } T_END; } static void test_imap_bodystructure_parse(void) { struct message_part *parts; const char *error; unsigned int i; int ret; for (i = 0; i < parse_tests_count; i++) T_BEGIN { struct parse_test *test = &parse_tests[i]; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024); test_begin(t_strdup_printf("imap bodystructure parser [%u]", i)); parts = msg_parse(pool, test->message, FALSE); test_assert(imap_body_parse_from_bodystructure(test->bodystructure, str, &error) == 0); test_assert(strcmp(str_c(str), test->body) == 0); ret = imap_bodystructure_parse(test->bodystructure, pool, parts, &error); test_assert(ret == 0); if (ret == 0) { str_truncate(str, 0); imap_bodystructure_write(parts, str, TRUE); test_assert(strcmp(str_c(str), test->bodystructure) == 0); } else { i_error("Invalid BODYSTRUCTURE: %s", error); } pool_unref(&pool); test_end(); } T_END; } static void test_imap_bodystructure_parse_full(void) { const char *error; unsigned int i; int ret; for (i = 0; i < parse_tests_count; i++) T_BEGIN { struct parse_test *test = &parse_tests[i]; struct message_part *parts = NULL; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap bodystructure parse full", 1024); test_begin(t_strdup_printf("imap bodystructure parser full [%u]", i)); ret = imap_bodystructure_parse_full(test->bodystructure, pool, &parts, &error); test_assert(ret == 0); if (ret == 0) { str_truncate(str, 0); imap_bodystructure_write(parts, str, TRUE); test_assert(strcmp(str_c(str), test->bodystructure) == 0); } else { i_error("Invalid BODYSTRUCTURE: %s", error); } pool_unref(&pool); test_end(); } T_END; } static void test_imap_bodystructure_normalize(void) { struct message_part *parts; const char *error; unsigned int i; int ret; for (i = 0; i < normalize_tests_count; i++) T_BEGIN { struct normalize_test *test = &normalize_tests[i]; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024); test_begin(t_strdup_printf("imap bodystructure normalize [%u]", i)); parts = msg_parse(pool, test->message, FALSE); ret = imap_bodystructure_parse(test->input, pool, parts, &error); test_assert(ret == 0); if (ret == 0) { str_truncate(str, 0); imap_bodystructure_write(parts, str, TRUE); test_assert(strcmp(str_c(str), test->output) == 0); } else { i_error("Invalid BODYSTRUCTURE: %s", error); } pool_unref(&pool); test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_imap_bodystructure_write, test_imap_bodystructure_parse, test_imap_bodystructure_normalize, test_imap_bodystructure_parse_full, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-keepalive.h0000644000175000017500000000224713123174404015654 00000000000000#ifndef IMAP_KEEPALIVE_H #define IMAP_KEEPALIVE_H /* This function can be used to set IMAP IDLE keepalive notification timeout interval so that the client gets the keepalive notifications at exactly the same time for all the IMAP connections. This helps to reduce battery usage in mobile devices. One problem with this is that we don't really want to send the notifications to everyone at the same time, because it would cause huge peaks of activity. Basing the notifications on the username works well for one account, but basing it on the IP address allows the client to get all of the notifications at the same time for multiple accounts as well (of course assuming Dovecot is running on all the servers :) One potential downside to using IP is that if a proxy hides the client's IP address, the notifications are sent to everyone at the same time. This can be avoided by using a properly configured Dovecot proxy, but we'll also try to avoid this by not doing it for the commonly used intranet IP ranges. */ unsigned int imap_keepalive_interval_msecs(const char *username, const struct ip_addr *ip, unsigned int interval_secs); #endif dovecot-2.2.33.2/src/lib-imap/imap-seqset.h0000644000175000017500000000105013123174404015202 00000000000000#ifndef IMAP_SEQSET_H #define IMAP_SEQSET_H #include "seq-range-array.h" /* Parse IMAP sequence-set and store the result in dest. '*' is stored as (uint32_t)-1. Returns 0 if successful, -1 if input is invalid. */ int imap_seq_set_parse(const char *str, ARRAY_TYPE(seq_range) *dest); /* Like imap_seq_set_parse(), but fail if '*' is used. */ int imap_seq_set_nostar_parse(const char *str, ARRAY_TYPE(seq_range) *dest); /* Parse IMAP seq-number / seq-range. */ int imap_seq_range_parse(const char *str, uint32_t *seq1_r, uint32_t *seq2_r); #endif dovecot-2.2.33.2/src/lib-imap/imap-parser.c0000644000175000017500000005330313165463624015210 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "imap-parser.h" /* We use this macro to read atoms from input. It should probably contain everything some day, but for now we can't handle some input otherwise: ']' is required for parsing section (FETCH BODY[]) '%', '*' and ']' are valid list-chars for LIST patterns '\' is used in flags */ #define IS_ATOM_PARSER_INPUT(c) \ ((c) == '(' || (c) == ')' || (c) == '{' || \ (c) == '"' || (c) <= 32 || (c) == 0x7f) #define is_linebreak(c) \ ((c) == '\r' || (c) == '\n') #define LIST_INIT_COUNT 7 enum arg_parse_type { ARG_PARSE_NONE = 0, ARG_PARSE_ATOM, ARG_PARSE_STRING, ARG_PARSE_LITERAL, ARG_PARSE_LITERAL8, ARG_PARSE_LITERAL_DATA, ARG_PARSE_LITERAL_DATA_FORCED, ARG_PARSE_TEXT }; struct imap_parser { /* permanent */ int refcount; pool_t pool; struct istream *input; struct ostream *output; size_t max_line_size; enum imap_parser_flags flags; /* reset by imap_parser_reset(): */ size_t line_size; ARRAY_TYPE(imap_arg_list) root_list; ARRAY_TYPE(imap_arg_list) *cur_list; struct imap_arg *list_arg; enum arg_parse_type cur_type; size_t cur_pos; /* parser position in input buffer */ bool cur_resp_text; /* we're parsing [resp-text-code] */ int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */ uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */ const char *error; unsigned int literal_skip_crlf:1; unsigned int literal_nonsync:1; unsigned int literal8:1; unsigned int literal_size_return:1; unsigned int eol:1; unsigned int args_added_extra_eol:1; unsigned int fatal_error:1; }; struct imap_parser * imap_parser_create(struct istream *input, struct ostream *output, size_t max_line_size) { struct imap_parser *parser; parser = i_new(struct imap_parser, 1); parser->refcount = 1; parser->pool = pool_alloconly_create(MEMPOOL_GROWING"IMAP parser", 1024); parser->input = input; parser->output = output; parser->max_line_size = max_line_size; p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); parser->cur_list = &parser->root_list; return parser; } void imap_parser_ref(struct imap_parser *parser) { i_assert(parser->refcount > 0); parser->refcount++; } void imap_parser_unref(struct imap_parser **_parser) { struct imap_parser *parser = *_parser; *_parser = NULL; i_assert(parser->refcount > 0); if (--parser->refcount > 0) return; pool_unref(&parser->pool); i_free(parser); } void imap_parser_reset(struct imap_parser *parser) { p_clear(parser->pool); parser->line_size = 0; p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); parser->cur_list = &parser->root_list; parser->list_arg = NULL; parser->cur_type = ARG_PARSE_NONE; parser->cur_pos = 0; parser->cur_resp_text = FALSE; parser->str_first_escape = 0; parser->literal_size = 0; parser->error = NULL; parser->literal_skip_crlf = FALSE; parser->eol = FALSE; parser->args_added_extra_eol = FALSE; parser->literal_size_return = FALSE; } void imap_parser_set_streams(struct imap_parser *parser, struct istream *input, struct ostream *output) { parser->input = input; parser->output = output; } const char *imap_parser_get_error(struct imap_parser *parser, bool *fatal) { *fatal = parser->fatal_error; return parser->error; } /* skip over everything parsed so far, plus the following whitespace */ static int imap_parser_skip_to_next(struct imap_parser *parser, const unsigned char **data, size_t *data_size) { size_t i; for (i = parser->cur_pos; i < *data_size; i++) { if ((*data)[i] != ' ') break; } parser->line_size += i; i_stream_skip(parser->input, i); parser->cur_pos = 0; *data += i; *data_size -= i; return *data_size > 0; } static struct imap_arg *imap_arg_create(struct imap_parser *parser) { struct imap_arg *arg; arg = array_append_space(parser->cur_list); arg->parent = parser->list_arg; return arg; } static void imap_parser_open_list(struct imap_parser *parser) { parser->list_arg = imap_arg_create(parser); parser->list_arg->type = IMAP_ARG_LIST; p_array_init(&parser->list_arg->_data.list, parser->pool, LIST_INIT_COUNT); parser->cur_list = &parser->list_arg->_data.list; parser->cur_type = ARG_PARSE_NONE; } static int imap_parser_close_list(struct imap_parser *parser) { struct imap_arg *arg; if (parser->list_arg == NULL) { /* we're not inside list */ if ((parser->flags & IMAP_PARSE_FLAG_INSIDE_LIST) != 0) { parser->eol = TRUE; parser->cur_type = ARG_PARSE_NONE; return TRUE; } parser->error = "Unexpected ')'"; return FALSE; } arg = imap_arg_create(parser); arg->type = IMAP_ARG_EOL; parser->list_arg = parser->list_arg->parent; if (parser->list_arg == NULL) { parser->cur_list = &parser->root_list; } else { parser->cur_list = &parser->list_arg->_data.list; } parser->cur_type = ARG_PARSE_NONE; return TRUE; } static char * imap_parser_strdup(struct imap_parser *parser, const void *data, size_t len) { char *ret; ret = p_malloc(parser->pool, len + 1); memcpy(ret, data, len); return ret; } static void imap_parser_save_arg(struct imap_parser *parser, const unsigned char *data, size_t size) { struct imap_arg *arg; char *str; arg = imap_arg_create(parser); switch (parser->cur_type) { case ARG_PARSE_ATOM: case ARG_PARSE_TEXT: if (size == 3 && i_memcasecmp(data, "NIL", 3) == 0) { /* NIL argument. it might be an actual NIL, but if we're reading astring, it's an atom and we can't lose its case. */ arg->type = IMAP_ARG_NIL; } else { /* simply save the string */ arg->type = IMAP_ARG_ATOM; } arg->_data.str = imap_parser_strdup(parser, data, size); arg->str_len = size; break; case ARG_PARSE_STRING: /* data is quoted and may contain escapes. */ i_assert(size > 0); arg->type = IMAP_ARG_STRING; str = p_strndup(parser->pool, data+1, size-1); /* remove the escapes */ if (parser->str_first_escape >= 0 && (parser->flags & IMAP_PARSE_FLAG_NO_UNESCAPE) == 0) { /* -1 because we skipped the '"' prefix */ (void)str_unescape(str + parser->str_first_escape-1); } arg->_data.str = str; arg->str_len = strlen(str); break; case ARG_PARSE_LITERAL_DATA: if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) != 0) { /* save literal size */ arg->type = parser->literal_nonsync ? IMAP_ARG_LITERAL_SIZE_NONSYNC : IMAP_ARG_LITERAL_SIZE; arg->_data.literal_size = parser->literal_size; arg->literal8 = parser->literal8; break; } /* fall through */ case ARG_PARSE_LITERAL_DATA_FORCED: if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_TYPE) != 0) arg->type = IMAP_ARG_LITERAL; else arg->type = IMAP_ARG_STRING; arg->_data.str = imap_parser_strdup(parser, data, size); arg->literal8 = parser->literal8; arg->str_len = size; break; default: i_unreached(); } parser->cur_type = ARG_PARSE_NONE; } static int is_valid_atom_char(struct imap_parser *parser, char chr) { const char *error; if (IS_ATOM_PARSER_INPUT((unsigned char)chr)) error = "Invalid characters in atom"; else if ((chr & 0x80) != 0) error = "8bit data in atom"; else return TRUE; if ((parser->flags & IMAP_PARSE_FLAG_ATOM_ALLCHARS) != 0) return TRUE; parser->error = error; return FALSE; } static int imap_parser_read_atom(struct imap_parser *parser, const unsigned char *data, size_t data_size) { size_t i; /* read until we've found space, CR or LF. */ for (i = parser->cur_pos; i < data_size; i++) { if (data[i] == ' ' || is_linebreak(data[i])) { imap_parser_save_arg(parser, data, i); break; } else if (data[i] == ')') { if (parser->list_arg != NULL || (parser->flags & IMAP_PARSE_FLAG_INSIDE_LIST) != 0) { imap_parser_save_arg(parser, data, i); break; } else if ((parser->flags & IMAP_PARSE_FLAG_ATOM_ALLCHARS) == 0) { parser->error = "Unexpected ')'"; return FALSE; } /* assume it's part of the atom */ } else if (!is_valid_atom_char(parser, data[i])) return FALSE; } parser->cur_pos = i; return parser->cur_type == ARG_PARSE_NONE; } static int imap_parser_read_string(struct imap_parser *parser, const unsigned char *data, size_t data_size) { size_t i; /* read until we've found non-escaped ", CR or LF */ for (i = parser->cur_pos; i < data_size; i++) { if (data[i] == '"') { imap_parser_save_arg(parser, data, i); i++; /* skip the trailing '"' too */ break; } if (data[i] == '\\') { if (i+1 == data_size) { /* known data ends with '\' - leave it to next time as well if it happens to be \" */ break; } /* save the first escaped char */ if (parser->str_first_escape < 0) parser->str_first_escape = i; /* skip the escaped char */ i++; } /* check linebreaks here, so escaping CR/LF isn't possible. string always ends with '"', so it's an error if we found a linebreak.. */ if (is_linebreak(data[i]) && (parser->flags & IMAP_PARSE_FLAG_MULTILINE_STR) == 0) { parser->error = "Missing '\"'"; return FALSE; } } parser->cur_pos = i; return parser->cur_type == ARG_PARSE_NONE; } static int imap_parser_literal_end(struct imap_parser *parser) { if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0) { if (parser->line_size >= parser->max_line_size || parser->literal_size > parser->max_line_size - parser->line_size) { /* too long string, abort. */ parser->error = "Literal size too large"; parser->fatal_error = TRUE; return FALSE; } if (parser->output != NULL && !parser->literal_nonsync) { o_stream_nsend(parser->output, "+ OK\r\n", 6); o_stream_nflush(parser->output); } } parser->cur_type = ARG_PARSE_LITERAL_DATA; parser->literal_skip_crlf = TRUE; parser->cur_pos = 0; return TRUE; } static int imap_parser_read_literal(struct imap_parser *parser, const unsigned char *data, size_t data_size) { size_t i, prev_size; /* expecting digits + "}" */ for (i = parser->cur_pos; i < data_size; i++) { if (data[i] == '}') { parser->line_size += i+1; i_stream_skip(parser->input, i+1); return imap_parser_literal_end(parser); } if (parser->literal_nonsync) { parser->error = "Expecting '}' after '+'"; return FALSE; } if (data[i] == '+') { parser->literal_nonsync = TRUE; continue; } if (data[i] < '0' || data[i] > '9') { parser->error = "Invalid literal size"; return FALSE; } prev_size = parser->literal_size; parser->literal_size = parser->literal_size*10 + (data[i]-'0'); if (parser->literal_size < prev_size) { /* wrapped around, abort. */ parser->error = "Literal size too large"; return FALSE; } } parser->cur_pos = i; return FALSE; } static int imap_parser_read_literal_data(struct imap_parser *parser, const unsigned char *data, size_t data_size) { if (parser->literal_skip_crlf) { /* skip \r\n or \n, anything else gives an error */ if (data_size == 0) return FALSE; if (*data == '\r') { parser->line_size++; data++; data_size--; i_stream_skip(parser->input, 1); if (data_size == 0) return FALSE; } if (*data != '\n') { parser->error = "Missing LF after literal size"; return FALSE; } parser->line_size++; data++; data_size--; i_stream_skip(parser->input, 1); parser->literal_skip_crlf = FALSE; i_assert(parser->cur_pos == 0); } if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0 || parser->cur_type == ARG_PARSE_LITERAL_DATA_FORCED) { /* now we just wait until we've read enough data */ if (data_size < parser->literal_size) return FALSE; else { imap_parser_save_arg(parser, data, (size_t)parser->literal_size); parser->cur_pos = (size_t)parser->literal_size; return TRUE; } } else { /* we want to save only literal size, not the literal itself. */ parser->literal_size_return = TRUE; imap_parser_save_arg(parser, &uchar_nul, 0); return FALSE; } } static bool imap_parser_is_next_resp_text(struct imap_parser *parser) { const struct imap_arg *arg; if (parser->cur_list != &parser->root_list || array_count(parser->cur_list) != 1) return FALSE; arg = array_idx(&parser->root_list, 0); if (arg->type != IMAP_ARG_ATOM) return FALSE; return strcasecmp(arg->_data.str, "OK") == 0 || strcasecmp(arg->_data.str, "NO") == 0 || strcasecmp(arg->_data.str, "BAD") == 0 || strcasecmp(arg->_data.str, "BYE") == 0; } static bool imap_parser_is_next_text(struct imap_parser *parser) { const struct imap_arg *arg; size_t len; if (parser->cur_list != &parser->root_list) return FALSE; arg = array_idx(&parser->root_list, array_count(&parser->root_list)-1); if (arg->type != IMAP_ARG_ATOM) return FALSE; len = strlen(arg->_data.str); return len > 0 && arg->_data.str[len-1] == ']'; } static bool imap_parser_read_text(struct imap_parser *parser, const unsigned char *data, size_t data_size) { size_t i; /* read until end of line */ for (i = parser->cur_pos; i < data_size; i++) { if (is_linebreak(data[i])) { imap_parser_save_arg(parser, data, i); break; } } parser->cur_pos = i; return parser->cur_type == ARG_PARSE_NONE; } /* Returns TRUE if argument was fully processed. Also returns TRUE if an argument inside a list was processed. */ static int imap_parser_read_arg(struct imap_parser *parser) { const unsigned char *data; size_t data_size; data = i_stream_get_data(parser->input, &data_size); if (data_size == 0) return FALSE; while (parser->cur_type == ARG_PARSE_NONE) { /* we haven't started parsing yet */ if (!imap_parser_skip_to_next(parser, &data, &data_size)) return FALSE; i_assert(parser->cur_pos == 0); if (parser->cur_resp_text && imap_parser_is_next_text(parser)) { /* we just parsed [resp-text-code] */ parser->cur_type = ARG_PARSE_TEXT; break; } switch (data[0]) { case '\r': if (data_size == 1) { /* wait for LF */ return FALSE; } if (data[1] != '\n') { parser->error = "CR sent without LF"; return FALSE; } /* fall through */ case '\n': /* unexpected end of line */ if ((parser->flags & IMAP_PARSE_FLAG_INSIDE_LIST) != 0) { parser->error = "Missing ')'"; return FALSE; } parser->eol = TRUE; return FALSE; case '"': parser->cur_type = ARG_PARSE_STRING; parser->str_first_escape = -1; break; case '~': /* This could be either literal8 or atom */ if (data_size == 1) { /* wait for the next char */ return FALSE; } if (data[1] != '{') { parser->cur_type = ARG_PARSE_ATOM; break; } if ((parser->flags & IMAP_PARSE_FLAG_LITERAL8) == 0) { parser->error = "literal8 not allowed here"; return FALSE; } parser->cur_type = ARG_PARSE_LITERAL8; parser->literal_size = 0; parser->literal_nonsync = FALSE; parser->literal8 = TRUE; break; case '{': parser->cur_type = ARG_PARSE_LITERAL; parser->literal_size = 0; parser->literal_nonsync = FALSE; parser->literal8 = FALSE; break; case '(': imap_parser_open_list(parser); if ((parser->flags & IMAP_PARSE_FLAG_STOP_AT_LIST) != 0) { i_stream_skip(parser->input, 1); return FALSE; } break; case ')': if (!imap_parser_close_list(parser)) return FALSE; if (parser->list_arg == NULL) { /* end of argument */ parser->cur_pos++; return TRUE; } break; default: if (!is_valid_atom_char(parser, data[0])) return FALSE; parser->cur_type = ARG_PARSE_ATOM; break; } parser->cur_pos++; } i_assert(data_size > 0); switch (parser->cur_type) { case ARG_PARSE_ATOM: if (!imap_parser_read_atom(parser, data, data_size)) return FALSE; if ((parser->flags & IMAP_PARSE_FLAG_SERVER_TEXT) == 0) break; if (imap_parser_is_next_resp_text(parser)) { /* we just parsed OK/NO/BAD/BYE. after parsing the [resp-text-code] the rest of the message can contain pretty much any random text, which we can't parse as if it was valid IMAP input */ parser->cur_resp_text = TRUE; } break; case ARG_PARSE_STRING: if (!imap_parser_read_string(parser, data, data_size)) return FALSE; break; case ARG_PARSE_LITERAL8: if (parser->cur_pos == data_size) return FALSE; if (data[parser->cur_pos] != '{') { parser->error = "Expected '{'"; return FALSE; } parser->cur_type = ARG_PARSE_LITERAL; parser->cur_pos++; /* fall through */ case ARG_PARSE_LITERAL: if (!imap_parser_read_literal(parser, data, data_size)) return FALSE; /* pass through to parsing data. since input->skip was modified, we need to get the data start position again. */ data = i_stream_get_data(parser->input, &data_size); /* fall through */ case ARG_PARSE_LITERAL_DATA: case ARG_PARSE_LITERAL_DATA_FORCED: if (!imap_parser_read_literal_data(parser, data, data_size)) return FALSE; break; case ARG_PARSE_TEXT: if (!imap_parser_read_text(parser, data, data_size)) return FALSE; break; default: i_unreached(); } i_assert(parser->cur_type == ARG_PARSE_NONE); return TRUE; } /* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */ #define IS_UNFINISHED(parser) \ ((parser)->cur_type != ARG_PARSE_NONE || \ ((parser)->cur_list != &parser->root_list && \ ((parser)->flags & IMAP_PARSE_FLAG_STOP_AT_LIST) == 0)) static int finish_line(struct imap_parser *parser, unsigned int count, const struct imap_arg **args_r) { struct imap_arg *arg; int ret = array_count(&parser->root_list); parser->line_size += parser->cur_pos; i_stream_skip(parser->input, parser->cur_pos); parser->cur_pos = 0; parser->cur_resp_text = FALSE; if (parser->list_arg != NULL && !parser->literal_size_return && (parser->flags & IMAP_PARSE_FLAG_STOP_AT_LIST) == 0) { parser->error = "Missing ')'"; *args_r = NULL; return -1; } arg = array_append_space(&parser->root_list); arg->type = IMAP_ARG_EOL; parser->args_added_extra_eol = TRUE; *args_r = array_get(&parser->root_list, &count); return ret; } int imap_parser_read_args(struct imap_parser *parser, unsigned int count, enum imap_parser_flags flags, const struct imap_arg **args_r) { parser->flags = flags; if (parser->args_added_extra_eol) { /* delete EOL */ array_delete(&parser->root_list, array_count(&parser->root_list)-1, 1); parser->args_added_extra_eol = FALSE; parser->literal_size_return = FALSE; } while (!parser->eol && (count == 0 || IS_UNFINISHED(parser) || array_count(&parser->root_list) < count)) { if (!imap_parser_read_arg(parser)) break; if (parser->line_size > parser->max_line_size) { parser->error = "IMAP command line too large"; break; } } if (parser->error != NULL) { /* error, abort */ parser->line_size += parser->cur_pos; i_stream_skip(parser->input, parser->cur_pos); parser->cur_pos = 0; *args_r = NULL; return -1; } else if ((!IS_UNFINISHED(parser) && count > 0 && array_count(&parser->root_list) >= count) || parser->eol || parser->literal_size_return) { /* all arguments read / end of line. */ return finish_line(parser, count, args_r); } else { /* need more data */ *args_r = NULL; return -2; } } static struct imap_arg * imap_parser_get_last_literal_size(struct imap_parser *parser, ARRAY_TYPE(imap_arg_list) **list_r) { ARRAY_TYPE(imap_arg_list) *list; struct imap_arg *args; unsigned int count; list = &parser->root_list; args = array_get_modifiable(&parser->root_list, &count); i_assert(count > 1 && args[count-1].type == IMAP_ARG_EOL); count--; while (args[count-1].type != IMAP_ARG_LITERAL_SIZE && args[count-1].type != IMAP_ARG_LITERAL_SIZE_NONSYNC) { if (args[count-1].type != IMAP_ARG_LIST) return NULL; /* maybe the list ends with literal size */ list = &args[count-1]._data.list; args = array_get_modifiable(list, &count); if (count == 0) return NULL; } *list_r = list; return &args[count-1]; } bool imap_parser_get_literal_size(struct imap_parser *parser, uoff_t *size_r) { ARRAY_TYPE(imap_arg_list) *list; struct imap_arg *last_arg; last_arg = imap_parser_get_last_literal_size(parser, &list); if (last_arg == NULL) return FALSE; return imap_arg_get_literal_size(last_arg, size_r); } void imap_parser_read_last_literal(struct imap_parser *parser) { ARRAY_TYPE(imap_arg_list) *list; struct imap_arg *last_arg; i_assert(parser->literal_size_return); last_arg = imap_parser_get_last_literal_size(parser, &list); i_assert(last_arg != NULL); parser->cur_type = ARG_PARSE_LITERAL_DATA_FORCED; i_assert(parser->literal_size == last_arg->_data.literal_size); /* delete EOL */ array_delete(&parser->root_list, array_count(&parser->root_list)-1, 1); parser->args_added_extra_eol = FALSE; /* delete literal size */ array_delete(list, array_count(list)-1, 1); parser->literal_size_return = FALSE; } int imap_parser_finish_line(struct imap_parser *parser, unsigned int count, enum imap_parser_flags flags, const struct imap_arg **args_r) { const unsigned char *data; size_t data_size; int ret; ret = imap_parser_read_args(parser, count, flags, args_r); if (ret == -1) return -1; if (ret == -2) { /* we should have noticed end of everything except atom */ if (parser->cur_type == ARG_PARSE_ATOM) { data = i_stream_get_data(parser->input, &data_size); imap_parser_save_arg(parser, data, data_size); } } return finish_line(parser, count, args_r); } const char *imap_parser_read_word(struct imap_parser *parser) { const unsigned char *data; size_t i, data_size; data = i_stream_get_data(parser->input, &data_size); for (i = 0; i < data_size; i++) { if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') break; } if (i < data_size) { data_size = i + (data[i] == ' ' ? 1 : 0); parser->line_size += data_size; i_stream_skip(parser->input, data_size); return p_strndup(parser->pool, data, i); } else { return NULL; } } dovecot-2.2.33.2/src/lib-imap/imap-match.c0000644000175000017500000002117113123174404014773 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* imap_match_init() logic originates from Cyrus, but the code is fully rewritten. */ #include "lib.h" #include "array.h" #include "imap-match.h" #include struct imap_match_pattern { const char *pattern; bool inboxcase; }; struct imap_match_glob { pool_t pool; struct imap_match_pattern *patterns; char sep; char patterns_data[FLEXIBLE_ARRAY_MEMBER]; }; struct imap_match_context { const char *inboxcase_end; char sep; bool inboxcase; }; /* name of "INBOX" - must not have repeated substrings */ static const char inbox[] = "INBOX"; #define INBOXLEN (sizeof(inbox) - 1) struct imap_match_glob * imap_match_init(pool_t pool, const char *pattern, bool inboxcase, char separator) { const char *patterns[2]; patterns[0] = pattern; patterns[1] = NULL; return imap_match_init_multiple(pool, patterns, inboxcase, separator); } static const char *pattern_compress(const char *pattern) { char *dest, *ret; dest = ret = t_strdup_noconst(pattern); /* @UNSAFE: compress the pattern */ while (*pattern != '\0') { if (*pattern == '*' || *pattern == '%') { /* remove duplicate hierarchy wildcards */ while (*pattern == '%') pattern++; /* "%*" -> "*" */ if (*pattern == '*') { /* remove duplicate wildcards */ while (*pattern == '*' || *pattern == '%') pattern++; *dest++ = '*'; } else { *dest++ = '%'; } } else { *dest++ = *pattern++; } } *dest = '\0'; return ret; } static bool pattern_is_inboxcase(const char *pattern, char separator) { const char *p = pattern, *inboxp = inbox; /* skip over exact matches */ while (*inboxp == i_toupper(*p) && *p != '\0') { inboxp++; p++; } if (*p != '%') { return *p == '*' || *p == separator || (*inboxp == '\0' && *p == '\0'); } /* handle 'I%B%X' style checks */ for (; *p != '\0' && *p != '*' && *p != separator; p++) { if (*p != '%') { inboxp = strchr(inboxp, i_toupper(*p)); if (inboxp == NULL) return FALSE; if (*++inboxp == '\0') { /* now check that it doesn't end with any invalid chars */ if (*++p == '%') p++; if (*p != '\0' && *p != '*' && *p != separator) return FALSE; break; } } } return TRUE; } static struct imap_match_glob * imap_match_init_multiple_real(pool_t pool, const char *const *patterns, bool inboxcase, char separator) { struct imap_match_glob *glob; struct imap_match_pattern *match_patterns; unsigned int i, patterns_count; size_t len, pos, patterns_data_len = 0; patterns_count = str_array_length(patterns); match_patterns = p_new(pool, struct imap_match_pattern, patterns_count + 1); /* compress the patterns */ for (i = 0; i < patterns_count; i++) { match_patterns[i].pattern = pattern_compress(patterns[i]); match_patterns[i].inboxcase = inboxcase && pattern_is_inboxcase(match_patterns[i].pattern, separator); patterns_data_len += strlen(match_patterns[i].pattern) + 1; } patterns_count = i; /* now we know how much memory we need */ glob = p_malloc(pool, sizeof(struct imap_match_glob) + patterns_data_len); glob->pool = pool; glob->sep = separator; /* copy pattern strings to our allocated memory */ for (i = 0, pos = 0; i < patterns_count; i++) { len = strlen(match_patterns[i].pattern) + 1; i_assert(pos + len <= patterns_data_len); /* @UNSAFE */ memcpy(glob->patterns_data + pos, match_patterns[i].pattern, len); match_patterns[i].pattern = glob->patterns_data + pos; pos += len; } glob->patterns = match_patterns; return glob; } struct imap_match_glob * imap_match_init_multiple(pool_t pool, const char *const *patterns, bool inboxcase, char separator) { struct imap_match_glob *glob; if (pool->datastack_pool) { return imap_match_init_multiple_real(pool, patterns, inboxcase, separator); } T_BEGIN { glob = imap_match_init_multiple_real(pool, patterns, inboxcase, separator); } T_END; return glob; } void imap_match_deinit(struct imap_match_glob **glob) { p_free((*glob)->pool, (*glob)->patterns); p_free((*glob)->pool, *glob); *glob = NULL; } static struct imap_match_glob * imap_match_dup_real(pool_t pool, const struct imap_match_glob *glob) { ARRAY_TYPE(const_string) patterns; const struct imap_match_pattern *p; bool inboxcase = FALSE; t_array_init(&patterns, 8); for (p = glob->patterns; p->pattern != NULL; p++) { if (p->inboxcase) inboxcase = TRUE; array_append(&patterns, &p->pattern, 1); } array_append_zero(&patterns); return imap_match_init_multiple_real(pool, array_idx(&patterns, 0), inboxcase, glob->sep); } struct imap_match_glob * imap_match_dup(pool_t pool, const struct imap_match_glob *glob) { struct imap_match_glob *new_glob; if (pool->datastack_pool) { return imap_match_dup_real(pool, glob); } else { T_BEGIN { new_glob = imap_match_dup_real(pool, glob); } T_END; return new_glob; } } bool imap_match_globs_equal(const struct imap_match_glob *glob1, const struct imap_match_glob *glob2) { const struct imap_match_pattern *p1 = glob1->patterns; const struct imap_match_pattern *p2 = glob2->patterns; if (glob1->sep != glob2->sep) return FALSE; for (; p1->pattern != NULL && p2->pattern != NULL; p1++, p2++) { if (strcmp(p1->pattern, p2->pattern) != 0) return FALSE; if (p1->inboxcase != p2->inboxcase) return FALSE; } return p1->pattern == p2->pattern; } #define CMP_CUR_CHR(ctx, data, pattern) \ (*(data) == *(pattern) || \ (i_toupper(*(data)) == i_toupper(*(pattern)) && \ (data) < (ctx)->inboxcase_end)) static enum imap_match_result match_sub(struct imap_match_context *ctx, const char **data_p, const char **pattern_p) { enum imap_match_result ret, match; unsigned int i; const char *data = *data_p, *pattern = *pattern_p; /* match all non-wildcards */ i = 0; while (pattern[i] != '\0' && pattern[i] != '*' && pattern[i] != '%') { if (!CMP_CUR_CHR(ctx, data+i, pattern+i)) { if (data[i] != '\0') return IMAP_MATCH_NO; if (pattern[i] == ctx->sep) return IMAP_MATCH_CHILDREN; if (i > 0 && pattern[i-1] == ctx->sep) { /* data="foo/" pattern = "foo/bar/%" */ return IMAP_MATCH_CHILDREN; } return IMAP_MATCH_NO; } i++; } data += i; pattern += i; if (*data == '\0' && *data_p != data && data[-1] == ctx->sep && *pattern != '\0') { /* data="/" pattern="/%..." */ match = IMAP_MATCH_CHILDREN; } else { match = IMAP_MATCH_NO; } while (*pattern == '%') { pattern++; if (*pattern == '\0') { /* match, if this is the last hierarchy */ while (*data != '\0' && *data != ctx->sep) data++; break; } /* skip over this hierarchy */ while (*data != '\0') { if (CMP_CUR_CHR(ctx, data, pattern)) { ret = match_sub(ctx, &data, &pattern); if (ret == IMAP_MATCH_YES) break; match |= ret; } if (*data == ctx->sep) break; data++; } } if (*pattern != '*') { if (*data == '\0' && *pattern != '\0') { if (*pattern == ctx->sep) match |= IMAP_MATCH_CHILDREN; return match; } if (*data != '\0') { if (*pattern == '\0' && *data == ctx->sep) match |= IMAP_MATCH_PARENT; return match; } } *data_p = data; *pattern_p = pattern; return IMAP_MATCH_YES; } static enum imap_match_result imap_match_pattern(struct imap_match_context *ctx, const char *data, const char *pattern) { enum imap_match_result ret, match; ctx->inboxcase_end = data; if (ctx->inboxcase && strncasecmp(data, inbox, INBOXLEN) == 0 && (data[INBOXLEN] == '\0' || data[INBOXLEN] == ctx->sep)) { /* data begins with INBOX/, use case-insensitive comparison for it */ ctx->inboxcase_end += INBOXLEN; } if (*pattern != '*') { /* handle the pattern up to the first '*' */ ret = match_sub(ctx, &data, &pattern); if (ret != IMAP_MATCH_YES || *pattern == '\0') return ret; } match = IMAP_MATCH_CHILDREN; while (*pattern == '*') { pattern++; if (*pattern == '\0') return IMAP_MATCH_YES; while (*data != '\0') { if (CMP_CUR_CHR(ctx, data, pattern)) { ret = match_sub(ctx, &data, &pattern); if (ret == IMAP_MATCH_YES) break; match |= ret; } data++; } } return *data == '\0' && *pattern == '\0' ? IMAP_MATCH_YES : match; } enum imap_match_result imap_match(struct imap_match_glob *glob, const char *data) { struct imap_match_context ctx; unsigned int i; enum imap_match_result ret, match; match = IMAP_MATCH_NO; ctx.sep = glob->sep; for (i = 0; glob->patterns[i].pattern != NULL; i++) { ctx.inboxcase = glob->patterns[i].inboxcase; ret = imap_match_pattern(&ctx, data, glob->patterns[i].pattern); if (ret == IMAP_MATCH_YES) return IMAP_MATCH_YES; match |= ret; } return match; } dovecot-2.2.33.2/src/lib-imap/imap-utf7.c0000644000175000017500000001573613123174404014576 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "unichar.h" #include "imap-utf7.h" static const char imap_b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; #define XX 0xff static const unsigned char imap_b64dec[256] = { XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX, 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX }; static void mbase64_encode(string_t *dest, const unsigned char *in, size_t len) { str_append_c(dest, '&'); while (len >= 3) { str_append_c(dest, imap_b64enc[in[0] >> 2]); str_append_c(dest, imap_b64enc[((in[0] & 3) << 4) | (in[1] >> 4)]); str_append_c(dest, imap_b64enc[((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)]); str_append_c(dest, imap_b64enc[in[2] & 0x3f]); in += 3; len -= 3; } if (len > 0) { str_append_c(dest, imap_b64enc[in[0] >> 2]); if (len == 1) str_append_c(dest, imap_b64enc[(in[0] & 0x03) << 4]); else { str_append_c(dest, imap_b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)]); str_append_c(dest, imap_b64enc[(in[1] & 0x0f) << 2]); } } str_append_c(dest, '-'); } static const char *imap_utf8_first_encode_char(const char *str) { const char *p; for (p = str; *p != '\0'; p++) { if (*p == '&' || *p < 0x20 || *p >= 0x7f) return p; } return NULL; } int imap_utf8_to_utf7(const char *src, string_t *dest) { const char *p; unichar_t chr; uint8_t *utf16, *u; uint16_t u16; p = imap_utf8_first_encode_char(src); if (p == NULL) { /* no characters that need to be encoded */ str_append(dest, src); return 0; } /* at least one encoded character */ str_append_n(dest, src, p-src); utf16 = t_malloc0(MALLOC_MULTIPLY(strlen(p), 2)); while (*p != '\0') { if (*p == '&') { str_append(dest, "&-"); p++; continue; } if (*p >= 0x20 && *p < 0x7f) { str_append_c(dest, *p); p++; continue; } u = utf16; while (*p != '\0' && (*p < 0x20 || *p >= 0x7f)) { if (uni_utf8_get_char(p, &chr) <= 0) return -1; /* @UNSAFE */ if (chr < UTF16_SURROGATE_BASE) { *u++ = chr >> 8; *u++ = chr & 0xff; } else { u16 = UTF16_SURROGATE_HIGH(chr); *u++ = u16 >> 8; *u++ = u16 & 0xff; u16 = UTF16_SURROGATE_LOW(chr); *u++ = u16 >> 8; *u++ = u16 & 0xff; } p += uni_utf8_char_bytes(*p); } mbase64_encode(dest, utf16, u-utf16); } return 0; } int t_imap_utf8_to_utf7(const char *src, const char **dest_r) { string_t *str; int ret; if (imap_utf8_first_encode_char(src) == NULL) { *dest_r = src; return 0; } str = t_str_new(64); ret = imap_utf8_to_utf7(src, str); *dest_r = str_c(str); return ret; } static int utf16buf_to_utf8(string_t *dest, const unsigned char output[4], unsigned int *_pos, unsigned int len) { unsigned int pos = *_pos; uint16_t high, low; unichar_t chr; if (len % 2 != 0) return -1; high = (output[pos % 4] << 8) | output[(pos+1) % 4]; if (high < UTF16_SURROGATE_HIGH_FIRST || high > UTF16_SURROGATE_HIGH_MAX) { /* single byte */ size_t oldlen = str_len(dest); if (high == 0) { /* Encoded NUL isn't going to work in Dovecot code, even though it's technically valid. Return failure so the callers don't even get a chance to handle the NUL in the string inconsistently. */ return -1; } uni_ucs4_to_utf8_c(high, dest); if (str_len(dest) - oldlen == 1) { unsigned char last = str_data(dest)[oldlen]; if (last >= 0x20 && last < 0x7f) return -1; } *_pos = (pos + 2) % 4; return 0; } if (high > UTF16_SURROGATE_HIGH_LAST) return -1; if (len != 4) { /* missing the second character */ return -1; } low = (output[(pos+2)%4] << 8) | output[(pos+3) % 4]; if (low < UTF16_SURROGATE_LOW_FIRST || low > UTF16_SURROGATE_LOW_LAST) return -1; chr = UTF16_SURROGATE_BASE + (((high & UTF16_SURROGATE_MASK) << UTF16_SURROGATE_SHIFT) | (low & UTF16_SURROGATE_MASK)); uni_ucs4_to_utf8_c(chr, dest); return 0; } static int mbase64_decode_to_utf8(string_t *dest, const char **_src) { const char *src = *_src; unsigned char input[4], output[4]; unsigned int outstart = 0, outpos = 0; while (*src != '-') { input[0] = imap_b64dec[(uint8_t)src[0]]; if (input[0] == 0xff) return -1; input[1] = imap_b64dec[(uint8_t)src[1]]; if (input[1] == 0xff) return -1; output[outpos % 4] = (input[0] << 2) | (input[1] >> 4); if (++outpos % 4 == outstart) { if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0) return -1; } input[2] = imap_b64dec[(uint8_t)src[2]]; if (input[2] == 0xff) { if (src[2] != '-') return -1; src += 2; break; } output[outpos % 4] = (input[1] << 4) | (input[2] >> 2); if (++outpos % 4 == outstart) { if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0) return -1; } input[3] = imap_b64dec[(uint8_t)src[3]]; if (input[3] == 0xff) { if (src[3] != '-') return -1; src += 3; break; } output[outpos % 4] = ((input[2] << 6) & 0xc0) | input[3]; if (++outpos % 4 == outstart) { if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0) return -1; } src += 4; } if (outstart != outpos % 4) { if (utf16buf_to_utf8(dest, output, &outstart, (4 + outpos - outstart) % 4) < 0) return -1; } /* found ending '-' */ *_src = src + 1; return 0; } int imap_utf7_to_utf8(const char *src, string_t *dest) { const char *p; for (p = src; *p != '\0'; p++) { if (*p < 0x20 || *p >= 0x7f) return -1; if (*p == '&') break; } if (*p == '\0') { /* no IMAP-UTF-7 encoded characters */ str_append(dest, src); return 0; } /* at least one encoded character */ str_append_n(dest, src, p-src); while (*p != '\0') { if (*p == '&') { if (*++p == '-') { str_append_c(dest, '&'); p++; } else { if (mbase64_decode_to_utf8(dest, &p) < 0) return -1; if (p[0] == '&' && p[1] != '-') { /* &...-& */ return -1; } } } else { str_append_c(dest, *p++); } } return 0; } bool imap_utf7_is_valid(const char *src) { const char *p; int ret; for (p = src; *p != '\0'; p++) { if (*p < 0x20 || *p >= 0x7f) return FALSE; if (*p == '&') { /* slow scan */ T_BEGIN { string_t *tmp = t_str_new(128); ret = imap_utf7_to_utf8(p, tmp); } T_END; if (ret < 0) return FALSE; } } return TRUE; } dovecot-2.2.33.2/src/lib-imap/imap-date.c0000644000175000017500000001261513165463624014632 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "utc-offset.h" #include "utc-mktime.h" #include "imap-date.h" #include static const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static int parse_timezone(const char *str) { int offset; /* +|-zone */ if ((*str != '+' && *str != '-') || !i_isdigit(str[1]) || !i_isdigit(str[2]) || !i_isdigit(str[3]) || !i_isdigit(str[4])) return 0; offset = (str[1]-'0') * 10*60 + (str[2]-'0') * 60 + (str[3]-'0') * 10 + (str[4]-'0'); return *str == '+' ? offset : -offset; } static const char *imap_parse_date_internal(const char *str, struct tm *tm) { int i; if (str == NULL || *str == '\0') return NULL; memset(tm, 0, sizeof(struct tm)); /* "dd-mon-yyyy [hh:mi:ss +|-zone]" dd is 1-2 digits and may be prefixed with space or zero. */ if (str[0] == ' ') { /* " d-..." */ str++; } if (!(i_isdigit(str[0]) && (str[1] == '-' || (i_isdigit(str[1]) && str[2] == '-')))) return NULL; tm->tm_mday = (str[0]-'0'); if (str[1] == '-') str += 2; else { tm->tm_mday = (tm->tm_mday * 10) + (str[1]-'0'); str += 3; } /* month name */ for (i = 0; i < 12; i++) { if (strncasecmp(month_names[i], str, 3) == 0) { tm->tm_mon = i; break; } } if (i == 12 || str[3] != '-') return NULL; str += 4; /* yyyy */ if (!i_isdigit(str[0]) || !i_isdigit(str[1]) || !i_isdigit(str[2]) || !i_isdigit(str[3])) return NULL; tm->tm_year = (str[0]-'0') * 1000 + (str[1]-'0') * 100 + (str[2]-'0') * 10 + (str[3]-'0') - 1900; str += 4; return str; } static bool imap_mktime(struct tm *tm, time_t *time_r) { *time_r = utc_mktime(tm); if (*time_r != (time_t)-1) return TRUE; /* the date is outside valid range for time_t. it might still be technically valid though, so try to handle this case. with 64bit time_t the full 0..9999 year range is valid. */ if (tm->tm_year <= 100) { /* too old. time_t can be signed or unsigned, handle both cases. */ *time_r = (time_t)-1 < (int)0 ? INT_MIN : 0; } else { /* too high. return the highest allowed value. we shouldn't get here with 64bit time_t, but handle that anyway. */ #if (TIME_T_MAX_BITS == 32 || TIME_T_MAX_BITS == 64) *time_r = (1UL << (TIME_T_MAX_BITS-1)) - 1; #else *time_r = (1UL << TIME_T_MAX_BITS) - 1; #endif } return FALSE; } bool imap_parse_date(const char *str, time_t *timestamp_r) { struct tm tm; str = imap_parse_date_internal(str, &tm); if (str == NULL || str[0] != '\0') return FALSE; tm.tm_isdst = -1; (void)imap_mktime(&tm, timestamp_r); return TRUE; } bool imap_parse_datetime(const char *str, time_t *timestamp_r, int *timezone_offset_r) { struct tm tm; str = imap_parse_date_internal(str, &tm); if (str == NULL) return FALSE; if (str[0] != ' ') return FALSE; str++; /* hh: */ if (!i_isdigit(str[0]) || !i_isdigit(str[1]) || str[2] != ':') return FALSE; tm.tm_hour = (str[0]-'0') * 10 + (str[1]-'0'); str += 3; /* mm: */ if (!i_isdigit(str[0]) || !i_isdigit(str[1]) || str[2] != ':') return FALSE; tm.tm_min = (str[0]-'0') * 10 + (str[1]-'0'); str += 3; /* ss */ if (!i_isdigit(str[0]) || !i_isdigit(str[1]) || str[2] != ' ') return FALSE; tm.tm_sec = (str[0]-'0') * 10 + (str[1]-'0'); str += 3; /* timezone */ *timezone_offset_r = parse_timezone(str); tm.tm_isdst = -1; if (imap_mktime(&tm, timestamp_r)) *timestamp_r -= *timezone_offset_r * 60; return TRUE; } static void imap_to_date_tm(char buf[11], const struct tm *tm) { int year; /* dd-mon- */ buf[0] = (tm->tm_mday / 10) + '0'; buf[1] = (tm->tm_mday % 10) + '0'; buf[2] = '-'; memcpy(buf+3, month_names[tm->tm_mon], 3); buf[6] = '-'; /* yyyy */ year = tm->tm_year + 1900; buf[7] = (year / 1000) + '0'; buf[8] = ((year / 100) % 10) + '0'; buf[9] = ((year / 10) % 10) + '0'; buf[10] = (year % 10) + '0'; } static const char * imap_to_datetime_tm(const struct tm *tm, int timezone_offset) { char *buf; /* @UNSAFE: but faster than t_strdup_printf() call.. */ buf = t_malloc(27); imap_to_date_tm(buf, tm); buf[11] = ' '; /* hh:mi:ss */ buf[12] = (tm->tm_hour / 10) + '0'; buf[13] = (tm->tm_hour % 10) + '0'; buf[14] = ':'; buf[15] = (tm->tm_min / 10) + '0'; buf[16] = (tm->tm_min % 10) + '0'; buf[17] = ':'; buf[18] = (tm->tm_sec / 10) + '0'; buf[19] = (tm->tm_sec % 10) + '0'; buf[20] = ' '; /* timezone */ if (timezone_offset >= 0) buf[21] = '+'; else { buf[21] = '-'; timezone_offset = -timezone_offset; } buf[22] = (timezone_offset / 600) + '0'; buf[23] = ((timezone_offset / 60) % 10) + '0'; buf[24] = ((timezone_offset % 60) / 10) + '0'; buf[25] = (timezone_offset % 10) + '0'; buf[26] = '\0'; return buf; } const char *imap_to_datetime(time_t timestamp) { struct tm *tm; int timezone_offset; tm = localtime(×tamp); timezone_offset = utc_offset(tm, timestamp); return imap_to_datetime_tm(tm, timezone_offset); } const char *imap_to_datetime_tz(time_t timestamp, int timezone_offset) { const struct tm *tm; time_t adjusted = timestamp + timezone_offset*60; tm = gmtime(&adjusted); return imap_to_datetime_tm(tm, timezone_offset); } bool imap_to_date(time_t timestamp, const char **str_r) { const struct tm *tm; char *buf; tm = localtime(×tamp); buf = t_malloc0(12); imap_to_date_tm(buf, tm); *str_r = buf; return tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0; } dovecot-2.2.33.2/src/lib-imap/imap-util.h0000644000175000017500000000242713123174404014664 00000000000000#ifndef IMAP_UTIL_H #define IMAP_UTIL_H #include "seq-range-array.h" #include "mail-types.h" struct imap_arg; /* Write flags as a space separated string. */ void imap_write_flags(string_t *dest, enum mail_flags flags, const char *const *keywords) ATTR_NULL(3); /* Parse system flag from a string, or return 0 if it's invalid. */ enum mail_flags imap_parse_system_flag(const char *str); /* Write sequence range as IMAP sequence-set */ void imap_write_seq_range(string_t *dest, const ARRAY_TYPE(seq_range) *array); /* Write IMAP arg to the given string. Because IMAP_ARG_LITERAL_SIZE* have no content, they're written as "{size}\r\n". */ void imap_write_arg(string_t *dest, const struct imap_arg *arg); /* Same as imap_write_arg(), but write all the args until EOL. */ void imap_write_args(string_t *dest, const struct imap_arg *args); /* Write IMAP args in a human-readable format to given string (e.g. for logging). The output is a single valid UTF-8 line without control characters. Multi-line literals are replaced with a generic placeholder. */ void imap_write_args_for_human(string_t *dest, const struct imap_arg *args); /* Like imap_write_args(), but return the string allocated from data stack. */ const char *imap_args_to_str(const struct imap_arg *args); #endif dovecot-2.2.33.2/src/lib-imap/test-imap-quote.c0000644000175000017500000001115013165463624016020 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-quote.h" #include "test-common.h" static void test_imap_append_string_for_humans(void) { static struct { const char *input, *output; } tests[] = { { "", "\"\"" }, { " ", "\"\"" }, { " ", "\"\"" }, { "\t", "\"\"" }, { " \t", "\"\"" }, { " \t ", "\"\"" }, { " foo", "{3}\r\nfoo" }, { "\tfoo", "{3}\r\nfoo" }, { "\t \tfoo", "{3}\r\nfoo" }, { " foo ", "{3}\r\nfoo" }, { " foo ", "{3}\r\nfoo" }, { " foo \t \t", "{3}\r\nfoo" }, { "hello\"world", "{11}\r\nhello\"world" }, { "hello\\world", "{11}\r\nhello\\world" }, { "hello\rworld", "{11}\r\nhello world" }, { "hello\nworld", "{11}\r\nhello world" }, { "hello\r\nworld", "{11}\r\nhello world" }, { "hello\r\n world", "{11}\r\nhello world" }, { "hello \r\n world", "{11}\r\nhello world" }, }; string_t *str = t_str_new(128); unsigned int i; test_begin("imap_append_string_for_humans()"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); imap_append_string_for_humans(str, (const void *)tests[i].input, strlen(tests[i].input)); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); } test_end(); } static void test_imap_append_astring(void) { static struct { const char *input, *output; } tests[] = { { "", "\"\"" }, { "NIL", "\"NIL\"" }, { "niL", "\"niL\"" }, { "ni", "ni" }, { "\\", "\"\\\\\"" }, { "\\\\", "\"\\\\\\\\\"" }, { "\\\\\\", "\"\\\\\\\\\\\\\"" }, { "\\\\\\\\", "\"\\\\\\\\\\\\\\\\\"" }, { "\\\\\\\\\\", "{5}\r\n\\\\\\\\\\" }, { "\\\\\\\\\\\\", "{6}\r\n\\\\\\\\\\\\" }, { "\"", "\"\\\"\"" }, { "\"\"", "\"\\\"\\\"\"" }, { "\"\"\"", "\"\\\"\\\"\\\"\"" }, { "\"\"\"\"", "\"\\\"\\\"\\\"\\\"\"" }, { "\"\"\"\"\"", "{5}\r\n\"\"\"\"\"" }, { "\"\"\"\"\"\"", "{6}\r\n\"\"\"\"\"\"" }, { "\r", "{1}\r\n\r" }, { "\n", "{1}\r\n\n" }, { "\r\n", "{2}\r\n\r\n" }, { "\x7f", "\"\x7f\"" }, { "\x80", "{1}\r\n\x80" }, { "\xff", "{1}\r\n\xff" }, }; string_t *str = t_str_new(128); unsigned int i; test_begin("test_imap_append_astring()"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); imap_append_astring(str, tests[i].input); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); } test_end(); } static void test_imap_append_nstring(void) { static struct { const char *input, *output; } tests[] = { { "", "\"\"" }, { NULL, "NIL" }, { "NIL", "\"NIL\"" }, { "\"America N.\"", "\"\\\"America N.\\\"\"" }, { "\"America N.\", \"America S.\"", "\"\\\"America N.\\\", \\\"America S.\\\"\"" }, { "\"America N.\", \"America S.\", \"Africa\"", "{36}\r\n\"America N.\", \"America S.\", \"Africa\"" }, { "Antarctica\n Australia", "{21}\r\nAntarctica\n Australia" }, { "ni", "\"ni\"" } }; string_t *str = t_str_new(128); unsigned int i; test_begin("test_imap_append_nstring()"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); imap_append_nstring(str, tests[i].input); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); } test_end(); } static void test_imap_append_nstring_nolf(void) { static const struct { const char *input, *output; } tests[] = { { "", "\"\"" }, { NULL, "NIL" }, { "NIL", "\"NIL\"" }, { "ni", "\"ni\"" }, { "\"NIL\n foo", "\"\\\"NIL foo\"" }, { "\"America N.\", \"America S.\", \"Africa\"", "{36}\r\n\"America N.\", \"America S.\", \"Africa\"" }, { "foo\nbar", "\"foo bar\"" }, { "foo\r\nbar", "\"foo bar\"" }, { "foo\rbar", "\"foo bar\"" }, { "foo\n bar", "\"foo bar\"" }, { "foo\r\n bar", "\"foo bar\"" }, { "foo\r bar", "\"foo bar\"" }, { "foo\n\tbar", "\"foo\tbar\"" }, { "foo\r\n\tbar", "\"foo\tbar\"" }, { "foo\r\tbar", "\"foo\tbar\"" }, { "foo\n bar", "\"foo bar\"" }, { "foo\r\n bar", "\"foo bar\"" }, { "foo\r bar", "\"foo bar\"" }, { "\nfoo\r bar\r\n", "\" foo bar\"" } }; unsigned int i; test_begin("test_imap_append_nstring_nolf()"); for (i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN { string_t *str = t_str_new(1); string_t *str2 = str_new(default_pool, 1); str_truncate(str, 0); imap_append_nstring_nolf(str, tests[i].input); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); str_truncate(str2, 0); imap_append_nstring_nolf(str2, tests[i].input); test_assert_idx(strcmp(tests[i].output, str_c(str2)) == 0, i); str_free(&str2); } T_END; test_end(); } int main(void) { static void (*test_functions[])(void) = { test_imap_append_string_for_humans, test_imap_append_astring, test_imap_append_nstring, test_imap_append_nstring_nolf, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/test-imap-parser.c0000644000175000017500000000330413165463624016161 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "imap-parser.h" #include "test-common.h" static void test_imap_parser_crlf(void) { static const char *test_input = "foo\r\nx\ry\n"; struct istream *input; struct imap_parser *parser; const struct imap_arg *args; unsigned int i; bool fatal; test_begin("imap parser crlf handling"); input = test_istream_create(test_input); parser = imap_parser_create(input, NULL, 1024); /* must return -2 until LF is read */ for (i = 0; test_input[i] != '\n'; i++) { test_istream_set_size(input, i+1); (void)i_stream_read(input); test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2); } test_istream_set_size(input, i+1); (void)i_stream_read(input); test_assert(imap_parser_read_args(parser, 0, 0, &args) == 1); test_assert(args[0].type == IMAP_ARG_ATOM); test_assert(args[1].type == IMAP_ARG_EOL); /* CR without LF should fail with error */ imap_parser_reset(parser); i_stream_seek(input, ++i); test_istream_set_size(input, ++i); (void)i_stream_read(input); test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2); test_istream_set_size(input, ++i); (void)i_stream_read(input); test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2); test_istream_set_size(input, ++i); (void)i_stream_read(input); test_assert(imap_parser_read_args(parser, 0, 0, &args) == -1); test_assert(strcmp(imap_parser_get_error(parser, &fatal), "CR sent without LF") == 0 && !fatal); imap_parser_unref(&parser); i_stream_destroy(&input); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_imap_parser_crlf, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-base-subject.h0000644000175000017500000000075713123174404016262 00000000000000#ifndef IMAP_BASE_SUBJECT_H #define IMAP_BASE_SUBJECT_H /* Returns the base subject of the given string, according to draft-ietf-imapext-sort-10. String is returned so that it's suitable for strcmp() comparing with another base subject. is_reply_or_forward is set to TRUE if message looks like reply or forward, according to draft-ietf-imapext-thread-12 rules. */ const char *imap_get_base_subject_cased(pool_t pool, const char *subject, bool *is_reply_or_forward_r); #endif dovecot-2.2.33.2/src/lib-imap/imap-id.h0000644000175000017500000000114313123174404014275 00000000000000#ifndef IMAP_ID_H #define IMAP_ID_H struct imap_arg; /* RFC 2971 says keys are max. 30 octets */ #define IMAP_ID_KEY_MAX_LEN 30 /* Return ID reply based on given settings. */ const char *imap_id_reply_generate(const char *settings); /* Return a line that should be logged based on given args and settings. Returns NULL if nothing should be logged. */ const char *imap_id_args_get_log_reply(const struct imap_arg *args, const char *settings); /* Append [, ]key=value to the reply sanitized. */ void imap_id_log_reply_append(string_t *reply, const char *key, const char *value); #endif dovecot-2.2.33.2/src/lib-imap/imap-resp-code.h0000644000175000017500000000200513123174404015560 00000000000000#ifndef IMAP_RESP_CODE_H #define IMAP_RESP_CODE_H /* IMAP response codes (RFC 5530) */ #define IMAP_RESP_CODE_UNAVAILABLE "UNAVAILABLE" #define IMAP_RESP_CODE_AUTHFAILED "AUTHENTICATIONFAILED" #define IMAP_RESP_CODE_AUTHZFAILED "AUTHORIZATIONFAILED" #define IMAP_RESP_CODE_EXPIRED "EXPIRED" #define IMAP_RESP_CODE_PRIVACYREQUIRED "PRIVACYREQUIRED" #define IMAP_RESP_CODE_CONTACTADMIN "CONTACTADMIN" #define IMAP_RESP_CODE_NOPERM "NOPERM" #define IMAP_RESP_CODE_INUSE "INUSE" #define IMAP_RESP_CODE_EXPUNGEISSUED "EXPUNGEISSUED" #define IMAP_RESP_CODE_CORRUPTION "CORRUPTION" #define IMAP_RESP_CODE_SERVERBUG "SERVERBUG" #define IMAP_RESP_CODE_CLIENTBUG "CLIENTBUG" #define IMAP_RESP_CODE_CANNOT "CANNOT" #define IMAP_RESP_CODE_LIMIT "LIMIT" #define IMAP_RESP_CODE_OVERQUOTA "OVERQUOTA" #define IMAP_RESP_CODE_ALREADYEXISTS "ALREADYEXISTS" #define IMAP_RESP_CODE_NONEXISTENT "NONEXISTENT" #define IMAP_RESP_CODE_UNKNOWN_CTE "UNKNOWN-CTE" /* BINARY */ /* IMAP standard (RFC 3501) */ #define IMAP_RESP_CODE_PARSE "PARSE" #endif dovecot-2.2.33.2/src/lib-imap/imap-utf7.h0000644000175000017500000000100513123174404014563 00000000000000#ifndef IMAP_UTF7_H #define IMAP_UTF7_H /* Convert an UTF-8 string to IMAP-UTF-7. Returns 0 if ok, -1 if src isn't valid UTF-8. */ int imap_utf8_to_utf7(const char *src, string_t *dest); int t_imap_utf8_to_utf7(const char *src, const char **dest_r); /* Convert IMAP-UTF-7 string to UTF-8. Returns 0 if ok, -1 if src isn't valid IMAP-UTF-7. */ int imap_utf7_to_utf8(const char *src, string_t *dest); /* Returns TRUE if the string is valid IMAP-UTF-7 string. */ bool imap_utf7_is_valid(const char *src); #endif dovecot-2.2.33.2/src/lib-imap/imap-keepalive.c0000644000175000017500000000253013165463624015655 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "crc32.h" #include "net.h" #include "imap-keepalive.h" #include static bool imap_remote_ip_is_usable(const struct ip_addr *ip) { unsigned int addr; if (ip->family == 0) return FALSE; if (ip->family == AF_INET) { #define IP4(a,b,c,d) ((unsigned)(a)<<24|(unsigned)(b)<<16|(unsigned)(c)<<8|(unsigned)(d)) addr = ip->u.ip4.s_addr; if (addr >= IP4(10,0,0,0) && addr <= IP4(10,255,255,255)) return FALSE; /* 10/8 */ if (addr >= IP4(192,168,0,0) && addr <= IP4(192,168,255,255)) return FALSE; /* 192.168/16 */ if (addr >= IP4(172,16,0,0) && addr <= IP4(172,31,255,255)) return FALSE; /* 172.16/12 */ if (addr >= IP4(127,0,0,0) && addr <= IP4(127,255,255,255)) return FALSE; /* 127/8 */ #undef IP4 } #ifdef HAVE_IPV6 else if (ip->family == AF_INET6) { addr = ip->u.ip6.s6_addr[0]; if (addr == 0xfc || addr == 0xfd) return FALSE; /* fc00::/7 */ } #endif return TRUE; } unsigned int imap_keepalive_interval_msecs(const char *username, const struct ip_addr *ip, unsigned int interval_secs) { unsigned int client_hash; client_hash = ip != NULL && imap_remote_ip_is_usable(ip) ? net_ip_hash(ip) : crc32_str(username); interval_secs -= (time(NULL) + client_hash) % interval_secs; return interval_secs * 1000; } dovecot-2.2.33.2/src/lib-imap/imap-parser.h0000644000175000017500000000761013165463624015215 00000000000000#ifndef IMAP_PARSER_H #define IMAP_PARSER_H #include "imap-arg.h" enum imap_parser_flags { /* Set this flag if you wish to read only size of literal argument and not convert literal into string. Useful when you need to deal with large literal sizes. The literal must be the last read parameter. */ IMAP_PARSE_FLAG_LITERAL_SIZE = 0x01, /* Don't remove '\' chars from string arguments */ IMAP_PARSE_FLAG_NO_UNESCAPE = 0x02, /* Return literals as IMAP_ARG_LITERAL instead of IMAP_ARG_STRING */ IMAP_PARSE_FLAG_LITERAL_TYPE = 0x04, /* Don't check if atom contains invalid characters */ IMAP_PARSE_FLAG_ATOM_ALLCHARS = 0x08, /* Allow strings to contain CRLFs */ IMAP_PARSE_FLAG_MULTILINE_STR = 0x10, /* Parse in list context; ')' parses as EOL */ IMAP_PARSE_FLAG_INSIDE_LIST = 0x20, /* Parse literal8 and set it as flag to imap_arg. */ IMAP_PARSE_FLAG_LITERAL8 = 0x40, /* We're parsing IMAP server replies. Parse the "text" after OK/NO/BAD/BYE replies as a single atom. We assume that the initial "*" or tag was already skipped over. */ IMAP_PARSE_FLAG_SERVER_TEXT = 0x80, /* Parse until '(' and return it as an empty list */ IMAP_PARSE_FLAG_STOP_AT_LIST = 0x100 }; struct imap_parser; /* Create new IMAP argument parser. output is used for sending command continuation requests for literals. max_line_size can be used to approximately limit the maximum amount of memory that gets allocated when parsing a line. Input buffer size limits the maximum size of each parsed token. Usually the largest lines are large only because they have a one huge message set token, so you'll probably want to keep input buffer size the same as max_line_size. That means the maximum memory usage is around 2 * max_line_size. */ struct imap_parser * imap_parser_create(struct istream *input, struct ostream *output, size_t max_line_size) ATTR_NULL(2); void imap_parser_ref(struct imap_parser *parser); void imap_parser_unref(struct imap_parser **parser); /* Reset the parser to initial state. */ void imap_parser_reset(struct imap_parser *parser); /* Change parser's input and output streams */ void imap_parser_set_streams(struct imap_parser *parser, struct istream *input, struct ostream *output) ATTR_NULL(3); /* Return the last error in parser. fatal is set to TRUE if there's no way to continue parsing, currently only if too large non-sync literal size was given. */ const char *imap_parser_get_error(struct imap_parser *parser, bool *fatal); /* Read a number of arguments. This function doesn't call i_stream_read(), you need to do that. Returns number of arguments read (may be less than count in case of EOL), -2 if more data is needed or -1 if error occurred. count-sized array of arguments are stored into args when return value is 0 or larger. If all arguments weren't read, they're set to NIL. count can be set to 0 to read all arguments in the line. Last element in args is always of type IMAP_ARG_EOL. */ int imap_parser_read_args(struct imap_parser *parser, unsigned int count, enum imap_parser_flags flags, const struct imap_arg **args_r); /* If parsing ended with literal size, return it. */ bool imap_parser_get_literal_size(struct imap_parser *parser, uoff_t *size_r); /* IMAP_PARSE_FLAG_LITERAL_SIZE is set and last read argument was a literal. Calling this function causes the literal size to be replaced with the actual literal data when continuing argument parsing. */ void imap_parser_read_last_literal(struct imap_parser *parser); /* just like imap_parser_read_args(), but assume \n at end of data in input stream. */ int imap_parser_finish_line(struct imap_parser *parser, unsigned int count, enum imap_parser_flags flags, const struct imap_arg **args_r); /* Read one word - used for reading tag and command name. Returns NULL if more data is needed. */ const char *imap_parser_read_word(struct imap_parser *parser); #endif dovecot-2.2.33.2/src/lib-imap/imap-bodystructure.h0000644000175000017500000000317213123174404016623 00000000000000#ifndef IMAP_BODYSTRUCTURE_H #define IMAP_BODYSTRUCTURE_H struct message_part; struct message_header_line; struct imap_arg; /* Write a BODY/BODYSTRUCTURE from given message_part. The message_part->data field must be set. part->body_size.virtual_size and .lines are also used for writing it. */ void imap_bodystructure_write(const struct message_part *part, string_t *dest, bool extended); /* Parse BODYSTRUCTURE and save the contents to message_part->data for each message tree node. If the parts argument points to NULL, the message_part tree is created from the BODYSTRUCTURE. Otherwise, existing tree is used. Returns 0 if ok, -1 if bodystructure wasn't valid. */ int imap_bodystructure_parse_full(const char *bodystructure, pool_t pool, struct message_part **parts, const char **error_r); /* Same as imap_bodystructure_parse_full(), but read the input from imap_args instead of a string. */ int imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool, struct message_part **parts, const char **error_r); /* Parse BODYSTRUCTURE and save the contents to message_part->data for each message tree node. The parts argument must point to an existing message_part tree. Returns 0 if ok, -1 if bodystructure wasn't valid. */ int imap_bodystructure_parse(const char *bodystructure, pool_t pool, struct message_part *parts, const char **error_r); /* Get BODY part from BODYSTRUCTURE and write it to dest. Returns 0 if ok, -1 if bodystructure wasn't valid. */ int imap_body_parse_from_bodystructure(const char *bodystructure, string_t *dest, const char **error_r); #endif dovecot-2.2.33.2/src/lib-imap/test-imap-url.c0000644000175000017500000007311113165463624015472 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "imap-url.h" #include "test-common.h" struct valid_imap_url_test { const char *url; enum imap_url_parse_flags flags; struct imap_url url_base; struct imap_url url_parsed; }; /* Valid IMAP URL tests */ static const struct valid_imap_url_test valid_url_tests[] = { { .url = "imap://localhost", .url_parsed = { .host_name = "localhost" } },{ .url = "imap://user@localhost", .url_parsed = { .host_name = "localhost", .userid = "user" } },{ .url = "imap://user;AUTH=PLAIN@localhost", .url_parsed = { .host_name = "localhost", .userid = "user", .auth_type = "PLAIN" } },{ .url = "imap://;AUTH=PLAIN@localhost", .url_parsed = { .host_name = "localhost", .auth_type = "PLAIN" } },{ .url = "imap://%68endri%6B;AUTH=GSS%41PI@%65%78%61%6d%70%6c%65.com", .url_parsed = { .host_name = "example.com", .userid = "hendrik", .auth_type = "GSSAPI" } },{ .url = "imap://user@localhost:993", .url_parsed = { .host_name = "localhost", .userid = "user", .port = 993, .have_port = TRUE } },{ .url = "imap://user@127.0.0.1", .url_parsed = { .host_name = "127.0.0.1", .userid = "user", .have_host_ip = TRUE } #ifdef HAVE_IPV6 },{ .url = "imap://user@[::1]", .url_parsed = { .host_name = "[::1]", .userid = "user", .have_host_ip = TRUE } #endif },{ .url = "imap://user@4example.com:423", .url_parsed = { .host_name = "4example.com", .userid = "user", .port = 423, .have_port = TRUE } },{ .url = "imap://beelzebub@666.4example.com:999", .url_parsed = { .host_name = "666.4example.com", .userid = "beelzebub", .port = 999, .have_port = TRUE } },{ .url = "imap://user@example.com/", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = NULL } },{ .url = "imap://user@example.com/./", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = NULL } },{ .url = "imap://user@example.com/INBOX", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX" } },{ .url = "imap://user@example.com/INBOX/", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX" } },{ .url = "imap://user@example.com//", .url_parsed = { .host_name = "example.com", .userid = "user"} },{ .url = "imap://user@example.com/INBOX/Trash", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash" } },{ .url = "imap://user@example.com/INBOX/Trash/..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX" } },{ .url = "imap://user@example.com/INBOX/Trash/../", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX" } },{ .url = "imap://user@example.com/INBOX/Trash/../..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = NULL } },{ .url = "imap://user@example.com/INBOX.Trash", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX.Trash" } },{ .url = "imap://user@example.com/INBOX%3BTrash", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX;Trash" } },{ .url = "imap://user@example.com/INBOX;UIDVALIDITY=1341", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uidvalidity = 1341 } },{ .url = "imap://user@example.com/INBOX/;UIDVALIDITY=23423", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uidvalidity = 23423 } },{ .url = "imap://user@example.com/INBOX/Drafts;UIDVALIDITY=6567", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 6567 } },{ .url = "imap://user@example.com/INBOX/Drafts;UIDVALIDITY=788/;UID=16", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 788, .uid = 16 } },{ .url = "imap://user@example.com/INBOX/Drafts;UIDVALIDITY=788/;UID=16/..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 788, .uid = 0 } },{ .url = "imap://user@example.com/INBOX/Drafts;UIDVALIDITY=788/;UID=16/../..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uidvalidity = 0, .uid = 0 } },{ .url = "imap://user@example.com/INBOX/Junk;UIDVALIDITY=27667/" ";UID=434/;SECTION=HEADER", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Junk", .uidvalidity = 27667, .uid = 434, .section = "HEADER" } },{ .url = "imap://user@example.com/INBOX/Important/" ";UID=437/;SECTION=1.2.MIME", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 437, .section = "1.2.MIME" } },{ .url = "imap://user@example.com/INBOX/Important/;UID=56/;SECTION=AA/BB", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 56, .section = "AA/BB" } },{ .url = "imap://user@example.com/INBOX/Important/;UID=56/;SECTION=AA/BB/..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 56, .section = "AA/" } },{ .url = "imap://user@example.com/INBOX/Important/;UID=56/" ";SECTION=AA/BB/../..", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 56, .section = NULL } },{ .url = "imap://user@example.com/INBOX/Important/;UID=234/" ";SECTION=HEADER.FIELDS%20(%22To%22%20%22From%22)", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 234, .section = "HEADER.FIELDS (\"To\" \"From\")" } },{ .url = "imap://user@example.com/INBOX/Important/;UID=234/" ";PARTIAL=10.250", .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Important", .uid = 234, .section = NULL, .partial_offset = 10, .partial_size = 250 } },{ .url = "imap://hendrik@example.com/INBOX/Important/;UID=34534/" ";SECTION=1.3.TEXT/;PARTIAL=0.34254", .url_parsed = { .host_name = "example.com", .userid = "hendrik", .mailbox = "INBOX/Important", .uid = 34534, .section = "1.3.TEXT", .partial_offset = 0, .partial_size = 34254 } },{ .url = "imap://hendrik@example.com/INBOX/Sent" ";UIDVALIDITY=534?SUBJECT%20%22Frop?%22", .url_parsed = { .host_name = "example.com", .userid = "hendrik", .mailbox = "INBOX/Sent", .uidvalidity = 534, .search_program = "SUBJECT \"Frop?\"" } },{ .url = "//hendrik@example.org/INBOX/Trash", .url_base = { .host_name = "example.com", .userid = "user" }, .url_parsed = { .host_name = "example.org", .userid = "hendrik", .mailbox = "INBOX/Trash" } },{ .url = "/INBOX/Trash", .url_base = { .host_name = "example.com", .userid = "user" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash" } },{ .url = "user@example.com", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Accounts" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Accounts/user@example.com" } },{ .url = "Drafts", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts" } },{ .url = "../Drafts", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts" } },{ .url = "../Junk", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Junk", .uidvalidity = 0 } },{ .url = "../Junk;UIDVALIDITY=23", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Junk", .uidvalidity = 23 } },{ .url = "../../%23shared;UIDVALIDITY=23452", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 764 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "#shared", .uidvalidity = 23452 } },{ .url = "../../%23news;UIDVALIDITY=546/;UID=456", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "#news", .uidvalidity = 546, .uid = 456 } },{ .url = "", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452 } },{ .url = "", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65 } },{ .url = "", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65, .section = "AA/BB", .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65, .section = "AA/BB", .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 } },{ .url = "", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65, .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65, .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 } },{ .url = ";UID=4767", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 4767 } },{ .url = ";UID=4767", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452}, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 4767 } },{ .url = "../;UID=4767", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uidvalidity = 0, .uid = 4767 } },{ .url = "../;UID=4767/;SECTION=TEXT", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 65, .section = "1.2.3.MIME" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Trash", .uidvalidity = 23452, .uid = 4767, .section = "TEXT" } },{ .url = ";SECTION=TEXT", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "1.2.3.MIME" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "TEXT" } },{ .url = "..", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/BB" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43 } },{ .url = "../;SECTION=CC", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/BB" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "CC" } },{ .url = "CC", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/BB" }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/CC" } },{ .url = ";PARTIAL=1024.1024", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .have_partial = TRUE, .partial_offset = 0, .partial_size = 1024 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 } },{ .url = "../CC/;PARTIAL=0.512", .url_base = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/BB", .have_partial = TRUE, .partial_offset = 1024, .partial_size = 1024 }, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX/Drafts", .uidvalidity = 769, .uid = 43, .section = "AA/CC", .have_partial = TRUE, .partial_offset = 0, .partial_size = 512 } },{ .url = "imap://user@example.com/INBOX/;UID=377;URLAUTH=anonymous", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uid = 377, .uauth_rumpurl = "imap://user@example.com/INBOX/;UID=377" ";URLAUTH=anonymous", .uauth_access_application = "anonymous"} },{ .url = "imap://user@example.com/INBOX/;UID=377" ";URLAUTH=anonymous:internal:4142434445464748494A4B4C4D4E4F5051525354", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uid = 377, .uauth_rumpurl = "imap://user@example.com/INBOX/;UID=377" ";URLAUTH=anonymous", .uauth_access_application = "anonymous", .uauth_mechanism = "internal", .uauth_token = (const unsigned char *)"ABCDEFGHIJKLMNOPQRST", .uauth_token_size = 20} },{ .url = "imap://user@example.com/INBOX/;UID=377" ";EXPIRE=2011-02-12T12:45:14+01:00" ";URLAUTH=user+frop:internal:4142434445464748494A4B4C4D4E4F5051525354", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH, .url_parsed = { .host_name = "example.com", .userid = "user", .mailbox = "INBOX", .uid = 377, .uauth_rumpurl = "imap://user@example.com/INBOX/;UID=377" ";EXPIRE=2011-02-12T12:45:14+01:00;URLAUTH=user+frop", .uauth_access_application = "user", .uauth_access_user = "frop", .uauth_mechanism = "internal", .uauth_token = (const unsigned char *)"ABCDEFGHIJKLMNOPQRST", .uauth_token_size = 20} } }; static const unsigned int valid_url_test_count = N_ELEMENTS(valid_url_tests); static void test_imap_url_valid(void) { unsigned int i; for (i = 0; i < valid_url_test_count; i++) T_BEGIN { const char *url = valid_url_tests[i].url; enum imap_url_parse_flags flags = valid_url_tests[i].flags; const struct imap_url *urlt = &valid_url_tests[i].url_parsed; const struct imap_url *urlb = &valid_url_tests[i].url_base; struct imap_url *urlp; const char *error = NULL; test_begin(t_strdup_printf("imap url valid [%d]", i)); if (urlb->host_name == NULL) urlb = NULL; if (imap_url_parse(url, urlb, flags, &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("imap_url_parse(%s)", valid_url_tests[i].url), urlp != NULL, error); if (urlp != NULL) { if (urlp->host_name == NULL || urlt->host_name == NULL) { test_out_quiet(t_strdup_printf("url->host_name = %s", urlp->host_name), urlp->host_name == urlt->host_name); } else { test_out_quiet(t_strdup_printf("url->host_name = %s", urlp->host_name), strcmp(urlp->host_name, urlt->host_name) == 0); } if (urlp->userid == NULL || urlt->userid == NULL) { test_out_quiet(t_strdup_printf("url->userid = %s", urlp->userid), urlp->userid == urlt->userid); } else { test_out_quiet(t_strdup_printf("url->userid = %s", urlp->userid), strcmp(urlp->userid, urlt->userid) == 0); } if (urlp->auth_type == NULL || urlt->auth_type == NULL) { test_out_quiet(t_strdup_printf("url->auth_type = %s", urlp->auth_type), urlp->auth_type == urlt->auth_type); } else { test_out_quiet(t_strdup_printf("url->auth_type = %s", urlp->auth_type), strcmp(urlp->auth_type, urlt->auth_type) == 0); } if (!urlp->have_port) { test_out_quiet("url->port = (unspecified)", urlp->have_port == urlt->have_port); } else { test_out_quiet(t_strdup_printf("url->port = %u", urlp->port), urlp->have_port == urlt->have_port && urlp->port == urlt->port); } if (!urlp->have_host_ip) { test_out_quiet("url->host_ip = (unspecified)", urlp->have_host_ip == urlt->have_host_ip); } else { test_out_quiet("url->host_ip = (valid)", urlp->have_host_ip == urlt->have_host_ip); } if (urlp->mailbox == NULL || urlt->mailbox == NULL) { test_out_quiet(t_strdup_printf("url->mailbox = %s", urlp->mailbox), urlp->mailbox == urlt->mailbox); } else { test_out_quiet(t_strdup_printf("url->mailbox = %s", urlp->mailbox), strcmp(urlp->mailbox, urlt->mailbox) == 0); } test_out_quiet(t_strdup_printf("url->uidvalidity = %u", urlp->uidvalidity), urlp->uidvalidity == urlt->uidvalidity); test_out_quiet(t_strdup_printf("url->uid = %u", urlp->uid), urlp->uid == urlt->uid); if (urlp->section == NULL || urlt->section == NULL) { test_out_quiet(t_strdup_printf("url->section = %s", urlp->section), urlp->section == urlt->section); } else { test_out_quiet(t_strdup_printf("url->section = %s", urlp->section), strcmp(urlp->section, urlt->section) == 0); } test_out_quiet(t_strdup_printf("url->partial = %"PRIuUOFF_T".%"PRIuUOFF_T, urlp->partial_offset, urlp->partial_size), urlp->partial_offset == urlt->partial_offset && urlp->partial_size == urlt->partial_size); if (urlp->search_program == NULL || urlt->search_program == NULL) { test_out_quiet(t_strdup_printf( "url->search_program = %s", urlp->search_program), urlp->search_program == urlt->search_program); } else { test_out_quiet(t_strdup_printf( "url->search_program = %s", urlp->search_program), strcmp(urlp->search_program, urlt->search_program) == 0); } if (urlt->uauth_rumpurl != NULL) { if (urlp->uauth_rumpurl == NULL) { test_out_quiet(t_strdup_printf( "url->uauth_rumpurl = %s", urlp->uauth_rumpurl), FALSE); } else { test_out_quiet(t_strdup_printf( "url->uauth_rumpurl = %s", urlp->uauth_rumpurl), strcmp(urlp->uauth_rumpurl, urlt->uauth_rumpurl) == 0); } if (urlp->uauth_access_application == NULL || urlt->uauth_access_application == NULL) { test_out_quiet(t_strdup_printf("url->uauth_access_application = %s", urlp->uauth_access_application), urlp->uauth_access_application == urlt->uauth_access_application); } else { test_out_quiet(t_strdup_printf("url->uauth_access_application = %s", urlp->uauth_access_application), strcmp(urlp->uauth_access_application, urlt->uauth_access_application) == 0); } if (urlp->uauth_access_user == NULL || urlt->uauth_access_user == NULL) { test_out_quiet(t_strdup_printf("url->uauth_access_user = %s", urlp->uauth_access_user), urlp->uauth_access_user == urlt->uauth_access_user); } else { test_out_quiet(t_strdup_printf("url->uauth_access_user = %s", urlp->uauth_access_user), strcmp(urlp->uauth_access_user, urlt->uauth_access_user) == 0); } if (urlp->uauth_mechanism == NULL || urlt->uauth_mechanism == NULL) { test_out_quiet(t_strdup_printf( "url->uauth_mechanism = %s", urlp->uauth_mechanism), urlp->uauth_mechanism == urlt->uauth_mechanism); } else { test_out_quiet(t_strdup_printf( "url->uauth_mechanism = %s", urlp->uauth_mechanism), strcmp(urlp->uauth_mechanism, urlt->uauth_mechanism) == 0); } if (urlp->uauth_token == NULL || urlt->uauth_token == NULL) { test_out_quiet(t_strdup_printf( "url->uauth_token = %s", urlp->uauth_token), urlp->uauth_token == urlt->uauth_token); } else { bool equal = urlp->uauth_token_size == urlt->uauth_token_size; size_t i; test_out_quiet(t_strdup_printf( "url->uauth_token_size = %"PRIuSIZE_T, urlp->uauth_token_size), equal); if (equal) { for (i = 0; i < urlp->uauth_token_size; i++) { if (urlp->uauth_token[i] != urlt->uauth_token[i]) { equal = FALSE; break; } } test_out_quiet(t_strdup_printf("url->uauth_token [index=%d]", (int)i), equal); } } } } test_end(); } T_END; } struct invalid_imap_url_test { const char *url; enum imap_url_parse_flags flags; struct imap_url url_base; }; static const struct invalid_imap_url_test invalid_url_tests[] = { { .url = "http://www.dovecot.org" },{ .url = "imap:/INBOX" },{ .url = "imap://user@example.com/INBOX", .flags = IMAP_URL_PARSE_REQUIRE_RELATIVE, .url_base = { .host_name = "example.com", .userid = "user" } },{ .url = "" },{ .url = "/INBOX/;UID=377" },{ .url = "imap://user@example.com/INBOX/;UID=377/;SECTION=TEXT?ALL" },{ .url = "imap://user@example.com/INBOX/?" },{ .url = "imap://user@example.com/INBOX/#Fragment" },{ .url = "imap://user@example.com/INBOX/\"" },{ .url = "imap:///INBOX" },{ .url = "imap://[]/INBOX" },{ .url = "imap://[v08.234:232:234:234:2221]/INBOX" #ifdef HAVE_IPV6 },{ .url = "imap://[1::34a:34:234::6]/INBOX" #endif },{ .url = "imap://example%a.com/INBOX" },{ .url = "imap://example.com%/INBOX" },{ .url = "imap://example%00.com/INBOX" },{ .url = "imap://example.com:65539/INBOX" },{ .url = "imap://user;ATH=frop@example.com" },{ .url = "imap://user;AUTH=frop;friep@example.com" },{ .url = "imap://user;AUTH=@example.com" },{ .url = "imap://user:password@example.com" },{ .url = "imap://user;AUTH=A:B@example.com" },{ .url = "imap://user%@example.com" },{ .url = "imap://user%00@example.com" },{ .url = "imap://user%ar;AUTH=*@example.com" },{ .url = "imap://;AUTH=FR%etD@example.com" },{ .url = "imap://user;AUTH=%@example.com" },{ .url = "imap://user;AUTH=%00@example.com" },{ .url = "imap://example.com/INBOX/%00/" },{ .url = "imap://example.com/INBOX/%0r/" },{ .url = "imap://example.com/INBOX/Trash/%/" },{ .url = "imap://example.com/INBOX;UIDVALIDITY=23423;FROP=friep/" },{ .url = "imap://example.com/INBOX;UIDVALIDITY=0/;UID=377" },{ .url = "imap://example.com/INBOX;UIDVALIDITY=/" },{ .url = "imap://example.com/INBOX;UIDVALIDITY=33a/" },{ .url = "imap://example.com/INBOX;FROP=friep/" },{ .url = "imap://example.com/INBOX/;UID=377;FROP=friep/" },{ .url = "imap://example.com/INBOX/;UID=0/" },{ .url = "imap://example.com/INBOX/;UID=/" },{ .url = "imap://example.com/INBOX/;UID=5e6/" },{ .url = "imap://example.com/INBOX/;UID=35/;SECTION=ALL;FROP=43/" },{ .url = "imap://example.com/INBOX/;UID=35/;SECTION=/" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=0." },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=0.e10" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=.3" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=5t4.3" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=0.0" },{ .url = "imap://example.com/INBOX/;UID=34/;PARTIAL=0.23409823409820938409823" },{ .url = "imap://example.com/INBOX/;UID=377/;FROP=34" },{ .url = "imap://example.com/INBOX/;UID=377;FROP=34" },{ .url = "imap://example.com/INBOX/;UID=377;EXPIRE=2010-02-02T12:00:12Z" },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=anonymous:internal:0ad89fafd79f54afe4523f45aadf2afe" },{ .url = "imap://example.com/INBOX/;UID=377;EXPIRE=2011-15-02T00:00:00Z" ";URLAUTH=anonymous:internal:0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377;EXPIRE=2011-10-02T00:00:00Z", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "/INBOX/;UID=377;EXPIRE=2011-10-02T00:00:00Z" ";URLAUTH=anonymous:internal:0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377;URLAUTH=", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=:internal:0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=user+:internal:0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=+frop:internal:0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377;URLAUTH=anonymous:", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=anonymous::0ad89fafd79f54afe4523f45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377;URLAUTH=anonymous:internal:", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377" ";URLAUTH=anonymous:internal:fd79f54afe4523", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH },{ .url = "imap://example.com/INBOX/;UID=377;EXPIRE=2011-10-02T00:00:00Z" ";URLAUTH=anonymous:internal:0ad89fafd79f54afe4523q45aadf2afe", .flags = IMAP_URL_PARSE_ALLOW_URLAUTH }, }; static const unsigned int invalid_url_test_count = N_ELEMENTS(invalid_url_tests); static void test_imap_url_invalid(void) { unsigned int i; for (i = 0; i < invalid_url_test_count; i++) T_BEGIN { const char *url = invalid_url_tests[i].url; enum imap_url_parse_flags flags = invalid_url_tests[i].flags; const struct imap_url *urlb = &invalid_url_tests[i].url_base; struct imap_url *urlp; const char *error = NULL; if (urlb->host_name == NULL) urlb = NULL; test_begin(t_strdup_printf("imap url invalid [%d]", i)); if (imap_url_parse(url, urlb, flags, &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("parse %s", url), urlp == NULL, error); test_end(); } T_END; } static const char *parse_create_url_tests[] = { "imap://host.example.com/", "imap://10.0.0.1/", #ifdef HAVE_IPV6 "imap://[::1]/", #endif "imap://user@host.example.com/", "imap://user@host.example.com:993/", "imap://su%3auser@host.example.com/", "imap://user;AUTH=PLAIN@host.example.com/", "imap://user;AUTH=PLAIN@host.example.com/INBOX", "imap://user;AUTH=PLAIN@host.example.com/INBOX/;UID=5", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1.14", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=anonymous", "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=user+username", "imap://user;AUTH=PLAIN@host.example.com/INBOX?SUBJECT%20%22Frop?%22", "imap://host.%23example.com/", "imap://user%3ba@host.example.com/", "imap://user%40example.com@host.example.com/", "imap://user%40example.com;AUTH=STR%23ANGE@host.example.com/", "imap://user;AUTH=PLAIN@host.example.com/INBOX/Important%3bWork", "imap://user@host.example.com/%23shared/news", "imap://user@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=HEADER.FIELDS%20(DATE%20FROM)", "imap://user@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=user+user%3bname", }; static const unsigned int parse_create_url_test_count = N_ELEMENTS(parse_create_url_tests); static void test_imap_url_parse_create(void) { unsigned int i; for (i = 0; i < parse_create_url_test_count; i++) T_BEGIN { const char *url = parse_create_url_tests[i]; struct imap_url *urlp; const char *error = NULL; test_begin(t_strdup_printf("imap url parse/create [%d]", i)); if (imap_url_parse (url, NULL, IMAP_URL_PARSE_ALLOW_URLAUTH, &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("parse %s", url), urlp != NULL, error); if (urlp != NULL) { const char *urlnew = imap_url_create(urlp); test_out(t_strdup_printf ("create %s", urlnew), strcmp(url, urlnew) == 0); } test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_imap_url_valid, test_imap_url_invalid, test_imap_url_parse_create, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/test-imap-utf7.c0000644000175000017500000001070313165463624015553 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "unichar.h" #include "imap-utf7.h" #include "test-common.h" static void test_imap_utf7_by_example(void) { static struct test { const char *utf8; const char *mutf7; } tests[] = { { "&&x&&", "&-&-x&-&-" }, { "~peter/mail/\xe5\x8f\xb0\xe5\x8c\x97/\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e", "~peter/mail/&U,BTFw-/&ZeVnLIqe-" }, { "tiet\xc3\xa4j\xc3\xa4", "tiet&AOQ-j&AOQ-" }, { "p\xe4\xe4", NULL }, { NULL, "&" }, { NULL, "&Jjo" }, { NULL, "&Jjo!" }, { NULL, "&U,BTFw-&ZeVnLIqe-" } }; string_t *dest; unsigned int i; dest = t_str_new(256); test_begin("imap mutf7 examples"); for (i = 0; i < N_ELEMENTS(tests); i++) { if (tests[i].utf8 != NULL) { str_truncate(dest, 0); if (imap_utf8_to_utf7(tests[i].utf8, dest) < 0) test_assert_idx(tests[i].mutf7 == NULL, i); else test_assert_idx(null_strcmp(tests[i].mutf7, str_c(dest)) == 0, i); } if (tests[i].mutf7 != NULL) { str_truncate(dest, 0); if (imap_utf7_to_utf8(tests[i].mutf7, dest) < 0) test_assert_idx(tests[i].utf8 == NULL, i); else test_assert_idx(null_strcmp(tests[i].utf8, str_c(dest)) == 0, i); test_assert_idx(imap_utf7_is_valid(tests[i].mutf7) != (tests[i].utf8 == NULL), i); } } test_end(); } static void test_imap_utf7_ucs4_cases(void) { string_t *src, *dest; const char *orig_src; unsigned int i, j; unichar_t chr; src = t_str_new(256); dest = t_str_new(256); test_begin("imap mutf7 ucs4 cases"); for (chr = 0xffff; chr <= 0x10010; chr++) T_BEGIN { for (i = 1; i <= 10; i++) { str_truncate(src, 0); str_truncate(dest, 0); for (j = 0; j < i; j++) { if (j % 3 == 0) str_append_c(src, 'x'); if (j % 5 == 0) str_append_c(src, '&'); uni_ucs4_to_utf8_c(chr, src); } orig_src = t_strdup(str_c(src)); str_truncate(src, 0); test_assert_idx(imap_utf8_to_utf7(orig_src, dest) == 0, chr*100+i); test_assert_idx(imap_utf7_to_utf8(str_c(dest), src) == 0, chr*100+i); test_assert_idx(strcmp(str_c(src), orig_src) == 0, chr+100+i); } } T_END; test_end(); } static const char mb64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; static void test_imap_utf7_non_utf16(void) { char csrc[1+4+1+1]; unsigned int i; test_begin("imap mutf7 non-utf16"); for (i = 0; i <= 255; ++i) { /* Invalid, code a single 8-bit octet */ csrc[2] = '&'; csrc[3] = mb64[i>>2]; csrc[4] = mb64[(i&3) << 4]; csrc[5] = '-'; csrc[6] = '\0'; test_assert_idx(imap_utf7_is_valid(csrc+2) == 0, i); } for (i = 0; i <= 255; ++i) { /* Invalid, U+00E4 followed by a single octet */ csrc[0] = '&'; csrc[1] = mb64[ (0x00 >> 2)]; csrc[2] = mb64[((0x00 & 0x03) << 4) | (0xe4 >> 4)]; csrc[3] = mb64[((0xe4 & 0x0f) << 2) | ( i >> 6)]; csrc[4] = mb64[ i & 0x3f ]; test_assert_idx(imap_utf7_is_valid(csrc) == 0, i); } test_end(); } static void test_imap_utf7_bad_ascii(void) { string_t *dest; char csrc[1+1]; unsigned int i; dest = t_str_new(256); test_begin("imap mutf7 bad ascii"); for (i = 1; i <= 0x7f; ++i) { if (i == ' ') i = 0x7f; csrc[0] = i; csrc[1] = '\0'; test_assert_idx(imap_utf7_is_valid(csrc) == 0, i); str_truncate(dest, 0); test_assert_idx(imap_utf7_to_utf8(csrc, dest) < 0, i); } test_end(); } static void test_imap_utf7_unnecessary(void) { string_t *dest; char csrc[1+3+1+1]; unsigned int i; dest = t_str_new(256); test_begin("imap mutf7 unnecessary"); for (i = 0x20; i < 0x7f; ++i) { /* Create an invalid escaped encoding of a simple char or '&' */ csrc[0] = '&'; csrc[1] = mb64[ (0x00 >> 2)]; csrc[2] = mb64[((0x00 & 0x03) << 4) | ( i >> 4)]; csrc[3] = mb64[(( i & 0x0f) << 2) | 0 ]; csrc[4] = '-'; csrc[5] = '\0'; test_assert_idx(imap_utf7_is_valid(csrc) == 0, i); str_truncate(dest, 0); test_assert_idx(imap_utf7_to_utf8(csrc, dest) < 0, i); /* All self-coding characters must self-code */ if (i == '&') continue; csrc[0] = i; csrc[1] = '\0'; str_truncate(dest, 0); test_assert_idx(imap_utf8_to_utf7(csrc, dest) >= 0, i); test_assert_idx(strcmp(csrc, str_c(dest)) == 0, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_imap_utf7_by_example, test_imap_utf7_ucs4_cases, test_imap_utf7_non_utf16, test_imap_utf7_bad_ascii, test_imap_utf7_unnecessary, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-url.c0000644000175000017500000007163513165463624014526 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strfuncs.h" #include "str-sanitize.h" #include "hex-binary.h" #include "net.h" #include "iso8601-date.h" #include "uri-util.h" #include "imap-url.h" #include /* * IMAP URL parsing */ /* IMAP URL Grammar overview RFC5092 Section 11: imapurl = "imap://" iserver ipath-query ; Defines an absolute IMAP URL iserver = [iuserinfo "@"] host [ ":" port ] ; This is the same as "authority" defined in [URI-GEN]. iuserinfo = enc-user [iauth] / [enc-user] iauth ; conforms to the generic syntax of "userinfo" as ; defined in [URI-GEN]. enc-user = 1*achar ; %-encoded version of [IMAP4] authorization identity or ; "userid". iauth = ";AUTH=" ( "*" / enc-auth-type ) enc-auth-type = 1*achar ; %-encoded version of [IMAP4] "auth-type" ipath-query = ["/" [ icommand ]] ; Corresponds to "path-abempty [ "?" query ]" in ; [URI-GEN] icommand = imessagelist / imessagepart [iurlauth] imessagelist = imailbox-ref [ "?" enc-search ] ; "enc-search" is [URI-GEN] "query". imessagepart = imailbox-ref iuid [isection] [ipartial] imailbox-ref = enc-mailbox [uidvalidity] uidvalidity = ";UIDVALIDITY=" nz-number ; See [IMAP4] for "nz-number" definition iuid = "/" iuid-only iuid-only = ";UID=" nz-number ; See [IMAP4] for "nz-number" definition isection = "/" isection-only isection-only = ";SECTION=" enc-section ipartial = "/" ipartial-only ipartial-only = ";PARTIAL=" partial-range enc-search = 1*bchar ; %-encoded version of [IMAPABNF] ; "search-program". Note that IMAP4 ; literals may not be used in ; a "search-program", i.e., only ; quoted or non-synchronizing ; literals (if the server supports ; LITERAL+ [LITERAL+]) are allowed. enc-mailbox = 1*bchar ; %-encoded version of [IMAP4] "mailbox" enc-section = 1*bchar ; %-encoded version of [IMAP4] "section-spec" partial-range = number ["." nz-number] ; partial FETCH. The first number is ; the offset of the first byte, ; the second number is the length of ; the fragment. bchar = achar / ":" / "@" / "/" achar = uchar / "&" / "=" ;; Same as [URI-GEN] 'unreserved / sub-delims / ;; pct-encoded', but ";" is disallowed. uchar = unreserved / sub-delims-sh / pct-encoded sub-delims-sh = "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," ;; Same as [URI-GEN] sub-delims, ;; but without ";", "&" and "=". The following rules are only used in the presence of the IMAP [URLAUTH] extension: authimapurl = "imap://" iserver "/" imessagepart ; Same as "imapurl" when "[icommand]" is ; "imessagepart" authimapurlfull = authimapurl iurlauth ; Same as "imapurl" when "[icommand]" is ; "imessagepart iurlauth" authimapurlrump = authimapurl iurlauth-rump iurlauth = iurlauth-rump iua-verifier enc-urlauth = 32*HEXDIG iua-verifier = ":" uauth-mechanism ":" enc-urlauth iurlauth-rump = [expire] ";URLAUTH=" access access = ("submit+" enc-user) / ("user+" enc-user) / "authuser" / "anonymous" expire = ";EXPIRE=" date-time ; date-time is defined in [DATETIME] uauth-mechanism = "INTERNAL" / 1*(ALPHA / DIGIT / "-" / ".") ; Case-insensitive. [URI-GEN] RFC3986 Appendix A: Implemented in src/lib/uri-util.c */ /* * Imap URL parser */ struct imap_url_parser { struct uri_parser parser; enum imap_url_parse_flags flags; struct imap_url *url; const struct imap_url *base; unsigned int relative:1; }; static int imap_url_parse_number(struct uri_parser *parser, const char *data, uint32_t *number_r) { /* [IMAP4] RFC3501, Section 9 * * number = 1*DIGIT * ; Unsigned 32-bit integer * ; (0 <= n < 4,294,967,296) */ if (i_isdigit(*data)) { if (str_to_uint32(data, number_r) == 0) return 1; parser->error = "IMAP number is too high"; return -1; } parser->error = t_strdup_printf( "Value '%s' is not a valid IMAP number", data); return -1; } static int imap_url_parse_offset(struct uri_parser *parser, const char *data, uoff_t *number_r) { /* Syntax for big (uoff_t) numbers. Not strictly IMAP syntax, but this is handled similarly for Dovecot IMAP FETCH BODY partial <.> implementation. */ if (i_isdigit(*data)) { if (str_to_uoff(data, number_r) == 0) return 1; parser->error = "IMAP number is too high"; return -1; } parser->error = t_strdup_printf( "Value '%s' is not a valid IMAP number", data); return -1; } static int imap_url_parse_iserver(struct imap_url_parser *url_parser) { struct uri_parser *parser = &url_parser->parser; struct uri_authority auth; struct imap_url *url = url_parser->url; const char *data; int ret = 0; /* imapurl = "imap://" iserver {...} * inetwork-path = "//" iserver {...} * iserver = [iuserinfo "@"] host [":" port] * ; This is the same as "authority" defined * ; in [URI-GEN]. * iuserinfo = enc-user [iauth] / [enc-user] iauth * ; conforms to the generic syntax of "userinfo" as * ; defined in [URI-GEN]. * enc-user = 1*achar * ; %-encoded version of [IMAP4] authorization identity or * ; "userid". * iauth = ";AUTH=" ( "*" / enc-auth-type ) * enc-auth-type = 1*achar * ; %-encoded version of [IMAP4] "auth-type" */ /* "//" iserver */ if ((ret = uri_parse_slashslash_authority(parser, &auth)) <= 0) return ret; if (auth.host_literal == NULL || *auth.host_literal == '\0') { /* This situation is not documented anywhere, but it is not currently useful either and potentially problematic if not handled explicitly everywhere. So, it is denied hier for now. */ parser->error = "IMAP URL does not allow empty host identifier"; return -1; } /* iuserinfo = enc-user [iauth] / [enc-user] iauth */ if (auth.enc_userinfo != NULL) { const char *p, *uend; /* Scan for ";AUTH=" */ for (p = auth.enc_userinfo; *p != '\0'; p++) { if (*p == ';') break; /* check for unallowed userinfo characters */ if (*p == ':') { parser->error = t_strdup_printf( "Stray ':' in userinfo `%s'", auth.enc_userinfo); return -1; } } uend = p; if (*p == ';') { if (strncasecmp(p, ";AUTH=", 6) != 0) { parser->error = t_strdup_printf( "Stray ';' in userinfo `%s'", auth.enc_userinfo); return -1; } for (p += 6; *p != '\0'; p++) { if (*p == ';' || *p == ':') { parser->error = t_strdup_printf( "Stray '%c' in userinfo `%s'", *p, auth.enc_userinfo); return -1; } } } /* enc-user */ if (url != NULL && uend > auth.enc_userinfo) { if (!uri_data_decode(parser, auth.enc_userinfo, uend, &data)) return -1; url->userid = p_strdup(parser->pool, data); } /* ( "*" / enc-auth-type ) */ if (*uend == ';') { p = uend + 6; if (*p == '\0') { parser->error = "Empty auth-type value after ';AUTH='"; return -1; } if (url != NULL) { if (!uri_data_decode(parser, p, NULL, &data)) return -1; url->auth_type = p_strdup(parser->pool, data); } } } if (url != NULL) { url->host_name = auth.host_literal; url->host_ip = auth.host_ip; url->have_host_ip = auth.have_host_ip; url->port = auth.port; url->have_port = auth.have_port; } return 1; } static int imap_url_parse_urlauth(struct imap_url_parser *url_parser, const char *urlext) { struct uri_parser *parser = &url_parser->parser; struct imap_url *url = url_parser->url; const char *p, *q, *data; buffer_t *uauth_token; time_t expire = (time_t)-1; int tz; /* iurlauth = iurlauth-rump iua-verifier * enc-urlauth = 32*HEXDIG * iua-verifier = ":" uauth-mechanism ":" enc-urlauth * iurlauth-rump = [expire] ";URLAUTH=" access * access = ("submit+" enc-user) / ("user+" enc-user) / * "authuser" / "anonymous" * expire = ";EXPIRE=" date-time * ; date-time is defined in [DATETIME] * uauth-mechanism = "INTERNAL" / 1*(ALPHA / DIGIT / "-" / ".") * ; Case-insensitive. */ /* ";EXPIRE=" date-time */ if (strncasecmp(urlext, ";EXPIRE=", 8) == 0) { if ((url_parser->flags & IMAP_URL_PARSE_ALLOW_URLAUTH) == 0) { parser->error = "`;EXPIRE=' is not allowed in this context"; return -1; } if ((p = strchr(urlext+8, ';')) != NULL) { if (!iso8601_date_parse((const unsigned char *)urlext+8, p-urlext-8, &expire, &tz)) { parser->error = "invalid date-time for `;EXPIRE='"; return -1; } urlext = p; } } /* ";URLAUTH=" access */ if (strncasecmp(urlext, ";URLAUTH=", 9) != 0) { if (expire != (time_t)-1) { parser->error = "`;EXPIRE=' without `;URLAUTH='"; return -1; } return 0; } urlext += 9; if (url != NULL) url->uauth_expire = expire; if ((url_parser->flags & IMAP_URL_PARSE_ALLOW_URLAUTH) == 0) { parser->error = "`;URLAUTH=' is not allowed in this context"; return -1; } if (url_parser->relative) { parser->error = "IMAP URLAUTH requires absolute URL"; return -1; } if ((p = strchr(urlext, ':')) == NULL) { size_t len = strlen(urlext); if (len == 0) { parser->error = "Missing URLAUTH access specifier"; return -1; } p = urlext+len; } else if (p == urlext) { parser->error = "Empty URLAUTH access specifier"; return -1; } /* parse access */ if ((q = strchr(urlext, '+')) == NULL) { /* application */ if (url != NULL) { url->uauth_access_application = p_strdup_until(parser->pool, urlext, p); } } else { /* application "+" enc-user */ if (urlext == q) { parser->error = "Empty URLAUTH access application"; return -1; } if (q+1 == p) { parser->error = t_strdup_printf( "Empty URLAUTH access user for `%s' application", t_strdup_until(urlext, q)); return -1; } if (!uri_data_decode(parser, q+1, p, &data)) return -1; if (url != NULL) { url->uauth_access_application = p_strdup_until(parser->pool, urlext, q); url->uauth_access_user = p_strdup(parser->pool, data); } } if (url != NULL) { /* get rump url */ if ((url_parser->flags & IMAP_URL_PARSE_SCHEME_EXTERNAL) == 0) { url->uauth_rumpurl = p_strdup_until(parser->pool, parser->begin, parser->end-strlen(p)); } else { url->uauth_rumpurl = p_strconcat(parser->pool, "imap:", t_strdup_until(parser->begin, parser->end-strlen(p)), NULL); } } if (*p == '\0') { /* rump url; caller should check whether this is appropriate */ return 1; } /* iua-verifier = ":" uauth-mechanism ":" enc-urlauth */ q = p + 1; if (*q == '\0') { parser->error = "Missing URLAUTH verifier"; return -1; } if ((p = strchr(q, ':')) == NULL || p[1] == '\0') { parser->error = "Missing URLAUTH token"; return -1; } if (p == q) { parser->error = "Missing URLAUTH mechanism"; return -1; } if (url != NULL) { /* get mechanism */ url->uauth_mechanism = p_strdup_until(parser->pool, q, p); } /* enc-urlauth = 32*HEXDIG */ q = p+1; if (strlen(q) < 32) { parser->error = "Too short URLAUTH token"; return -1; } uauth_token = buffer_create_dynamic(pool_datastack_create(), 64); if (hex_to_binary(q, uauth_token) < 0) { parser->error = "Invalid URLAUTH token"; return -1; } if (url != NULL) { url->uauth_token = uauth_token->data; url->uauth_token_size = uauth_token->used; } return 1; } static int imap_url_parse_path(struct imap_url_parser *url_parser, const char *const *path, int relative, bool *is_messagelist_r) { struct uri_parser *parser = &url_parser->parser; struct imap_url *url = url_parser->url; const char *const *segment; string_t *mailbox, *section = NULL; uint32_t uid = 0, uidvalidity = 0; uoff_t partial_offset = 0, partial_size = 0; bool have_partial = FALSE; const char *p, *value, *urlext = NULL; bool mailbox_endslash = FALSE, section_endslash = FALSE; int ret; /* icommand = imessagelist / * imessagepart [iurlauth] * imessagelist = imailbox-ref [ "?" enc-search ] * ; "enc-search" is [URI-GEN] "query". * imessagepart = imailbox-ref iuid [isection] [ipartial] * imailbox-ref = enc-mailbox [uidvalidity] * uidvalidity = ";UIDVALIDITY=" nz-number * iuid = "/" iuid-only * iuid-only = ";UID=" nz-number * ; See [IMAP4] for "nz-number" definition * isection = "/" isection-only * isection-only = ";SECTION=" enc-section * ipartial = "/" ipartial-only * ipartial-only = ";PARTIAL=" partial-range * enc-mailbox = 1*bchar * ; %-encoded version of [IMAP4] "mailbox" * enc-section = 1*bchar * ; %-encoded version of [IMAP4] "section-spec" * partial-range = number ["." nz-number] * ; partial FETCH. The first number is * ; the offset of the first byte, * ; the second number is the length of * ; the fragment. */ /* IMAP URL syntax is quite horrible to parse. It relies upon the generic URI path resolution, but the icommand syntax also relies on ';' separators. We use the generic URI path parse functions to adhere to the URI path resolution rules and glue back together path segments when these are part of the same (mailbox or section) value. */ mailbox = t_str_new(256); segment = path; /* Resolve relative URI path; determine what to copy from the base URI */ if (url != NULL && url_parser->base != NULL && relative > 0) { const struct imap_url *base = url_parser->base; int rel = relative; /* /;PARTIAL= */ if (base->have_partial && --rel <= 0) { have_partial = base->have_partial; partial_offset = base->partial_offset; partial_size = base->partial_size; } /* /;SECTION= */ if (base->section != NULL) { p = base->section + strlen(base->section); /* determine what to retain from base section path */ for (; p > base->section && rel > 0; p--) { if (*p =='/' && --rel <= 0) break; } if (--rel <= 0 && p > base->section) { if (p[-1] == '/') section_endslash = TRUE; if (section == NULL) section = t_str_new(256); str_append_n(section, base->section, p-base->section); } } /* /;UID= */ if (base->uid > 0 && --rel <= 0) { uid = base->uid; } /* /mail/box;UIDVALIDITY= */ if (base->mailbox != NULL) { uidvalidity = base->uidvalidity; p = base->mailbox + strlen(base->mailbox); /* mailbox has implicit trailing '/' */ if (p[-1] != '/' && base->uid == 0 && rel > 0) rel--; /* determine what to retain from base mailbox path */ for (; p > base->mailbox && rel > 0; p--) { if (*p =='/') { uidvalidity = 0; if (--rel <= 0) break; } } if (--rel <= 0 && p > base->mailbox) { if (p[-1] == '/') mailbox_endslash = TRUE; str_append_n(mailbox, base->mailbox, p - base->mailbox); } } } /* Scan for last mailbox-ref segment */ if (segment != NULL) { if (relative == 0 || (!have_partial && section == NULL)) { p = NULL; while (*segment != NULL) { /* ';' must be pct-encoded; if it is not, this is either the last mailbox-ref path segment containing ';UIDVALIDITY=' or the subsequent iuid ';UID=' path segment */ if ((p = strchr(*segment, ';')) != NULL) break; if (**segment != '\0') { if (segment > path || (!mailbox_endslash && str_len(mailbox) > 0)) str_append_c(mailbox, '/'); if (!uri_data_decode(parser, *segment, NULL, &value)) return -1; str_append(mailbox, value); mailbox_endslash = FALSE; } segment++; } /* Handle ';' */ if (p != NULL) { /* [uidvalidity] */ if (strncasecmp(p, ";UIDVALIDITY=", 13) == 0) { /* append last bit of mailbox */ if (*segment != p) { if (segment > path || (!mailbox_endslash && str_len(mailbox) > 0)) str_append_c(mailbox, '/'); if (!uri_data_decode(parser, *segment, p, &value)) return -1; str_append(mailbox, value); } /* ";UIDVALIDITY=" nz-number */ if (strchr(p+13, ';') != NULL) { parser->error = "Encountered stray ';' after UIDVALIDITY"; return -1; } /* nz-number */ if (p[13] == '\0') { parser->error = "Empty UIDVALIDITY value"; return -1; } if (imap_url_parse_number(parser, p+13, &uidvalidity) <= 0) return -1; if (uidvalidity == 0) { parser->error = "UIDVALIDITY cannot be zero"; return -1; } segment++; } else if (p != *segment) { parser->error = "Encountered stray ';' in mailbox reference"; return -1; } } /* iuid */ if (*segment != NULL && strncasecmp(*segment, ";UID=", 5) == 0) { /* ";UID=" nz-number */ value = (*segment)+5; if ((p = strchr(value,';')) != NULL) { if (segment[1] != NULL ) { /* not the last segment, so it cannot be extension like iurlauth */ parser->error = "Encountered stray ';' in UID path segment"; return -1; } urlext = p; value = t_strdup_until(value, p); } /* nz-number */ if (*value == '\0') { parser->error = "Empty UID value"; return -1; } if (imap_url_parse_number(parser, value, &uid) <= 0) return -1; if (uid == 0) { parser->error = "UID cannot be zero"; return -1; } segment++; } } /* [isection] [ipartial] */ if (*segment != NULL && uid > 0) { /* [isection] */ if (section != NULL || strncasecmp(*segment, ";SECTION=", 9) == 0) { /* ";SECTION=" enc-section */ if (section == NULL) { section = t_str_new(256); value = (*segment) + 9; } else { value = *segment; } /* enc-section can contain slashes, so we merge path segments until one contains ';' */ while ((p = strchr(value,';')) == NULL) { if (!section_endslash && str_len(section) > 0) str_append_c(section, '/'); if (*value != '\0') { if (!uri_data_decode(parser, value, NULL, &value)) return -1; str_append(section, value); section_endslash = FALSE; } segment++; if (*segment == NULL) break; value = *segment; } if (p != NULL) { /* found ';' */ if (p != value) { /* it is not at the beginning of the path segment */ if (segment[1] != NULL) { /* not the last segment, so it cannot be extension like iurlauth */ parser->error = "Encountered stray ';' in SECTION path segment"; return -1; } urlext = p; value = t_strdup_until(value, p); if (!section_endslash && str_len(section) > 0) str_append_c(section, '/'); if (!uri_data_decode(parser, value, NULL, &value)) return -1; str_append(section, value); segment++; } } if (str_len(section) == 0) { parser->error = "Empty SECTION value"; return -1; } } /* [ipartial] */ if (*segment != NULL && strncasecmp(*segment, ";PARTIAL=", 9) == 0) { have_partial = TRUE; /* ";PARTIAL=" partial-range */ value = (*segment) + 9; if ((p = strchr(value,';')) != NULL) { urlext = p; value = t_strdup_until(value, p); } if (*value == '\0') { parser->error = "Empty PARTIAL value"; return -1; } /* partial-range = number ["." nz-number] */ if ((p = strchr(value,'.')) != NULL) { if (p[1] == '\0') { parser->error = "Empty PARTIAL size"; return -1; } if (imap_url_parse_offset(parser, p+1, &partial_size) <= 0) return -1; if (partial_size == 0) { parser->error = "PARTIAL size cannot be zero"; return -1; } value = t_strdup_until(value, p); if (*value == '\0') { parser->error = "Empty PARTIAL offset"; return -1; } } if (imap_url_parse_offset(parser,value, &partial_offset) <= 0) return -1; segment++; } } if (*segment != NULL) { if (urlext != NULL || **segment != '\0' || *(segment+1) != NULL ) { parser->error = t_strdup_printf( "Unexpected IMAP URL path segment: `%s'", str_sanitize(*segment, 80)); return -1; } } } /* ";" {...} at end of URL */ if (urlext != NULL) { /* [iurlauth] */ if ((ret = imap_url_parse_urlauth(url_parser, urlext)) < 0) return ret; else if (ret == 0) { /* something else */ parser->error = t_strdup_printf( "Unrecognized IMAP URL extension: %s", str_sanitize(urlext, 80)); return -1; } } if (is_messagelist_r != NULL) *is_messagelist_r = (uid == 0); if (url != NULL) { if (str_len(mailbox) > 0) url->mailbox = p_strdup(parser->pool, str_c(mailbox)); url->uidvalidity = uidvalidity; url->uid = uid; if (section != NULL) url->section = p_strdup(parser->pool, str_c(section)); url->have_partial = have_partial; url->partial_offset = partial_offset; url->partial_size = partial_size; } return 1; } static bool imap_url_do_parse(struct imap_url_parser *url_parser) { struct uri_parser *parser = &url_parser->parser; const char *const *path; bool is_messagelist = FALSE; bool have_scheme = FALSE; int relative; const char *query; int ret, sret; /* * imapurl = "imap://" iserver ipath-query * ; Defines an absolute IMAP URL * iserver = [iuserinfo "@"] host [":" port] * ; This is the same as "authority" defined * ; in [URI-GEN]. * ipath-query = ["/" [ icommand ]] * ; Corresponds to "path-abempty [ "?" query ]" in * ; [URI-GEN] * icommand = imessagelist / * imessagepart [iurlauth] * imessagelist = imailbox-ref [ "?" enc-search ] * ; "enc-search" is [URI-GEN] "query". * imessagepart = imailbox-ref iuid [isection] [ipartial] * enc-search = 1*bchar * ; %-encoded version of [IMAPABNF] * ; "search-program". Note that IMAP4 * ; literals may not be used in * ; a "search-program", i.e., only * ; quoted or non-synchronizing * ; literals (if the server supports * ; LITERAL+ [LITERAL+]) are allowed. */ /* "imap:" */ if ((url_parser->flags & IMAP_URL_PARSE_SCHEME_EXTERNAL) == 0) { const char *scheme; if ((ret = uri_parse_scheme(parser, &scheme)) < 0) return FALSE; else if (ret > 0) { if (strcasecmp(scheme, "imap") != 0) { parser->error = "Not an IMAP URL"; return FALSE; } have_scheme = TRUE; } } else { have_scheme = TRUE; } /* "//" iserver */ if ((sret = imap_url_parse_iserver(url_parser)) < 0) return FALSE; if (have_scheme && sret == 0) { parser->error = "Absolute IMAP URL requires `//' after `imap:'"; return FALSE; } if (sret > 0 && (url_parser->flags & IMAP_URL_PARSE_REQUIRE_RELATIVE) != 0) { parser->error = "Relative URL required"; return FALSE; } /* ipath-query = ["/" [ icommand ]] ; excludes `[ "?" enc-search ]` */ if ((ret = uri_parse_path(parser, &relative, &path)) < 0) return FALSE; /* Relative urls are only valid when we have a base url */ if (sret == 0) { if (url_parser->base == NULL) { parser->error = "Relative URL not allowed"; return FALSE; } else if (url_parser->url != NULL) { struct imap_url *url = url_parser->url; const struct imap_url *base = url_parser->base; url->host_name = p_strdup_empty(parser->pool, base->host_name); url->host_ip = base->host_ip; url->have_host_ip = base->have_host_ip; url->have_port = base->have_port; url->port = base->port; url->userid = p_strdup_empty(parser->pool, base->userid); url->auth_type = p_strdup_empty(parser->pool, base->auth_type); } url_parser->relative = TRUE; } /* Parse path, i.e. `[ icommand ]` from `*( "/" segment )` */ if (ret > 0 || url_parser->relative) { if ((ret = imap_url_parse_path(url_parser, path, relative, &is_messagelist)) < 0) return FALSE; } /* [ "?" enc-search ] */ if ((ret = uri_parse_query(parser, &query)) != 0) { if (ret < 0) return FALSE; if (!is_messagelist) { parser->error = "Search query part only valid for messagelist-type IMAP URL"; return FALSE; } else if (*query == '\0') { parser->error = "Empty IMAP URL search query not allowed"; return FALSE; } if (url_parser->url != NULL) { if (!uri_data_decode(parser, query, NULL, &query)) return FALSE; url_parser->url->search_program = p_strdup(parser->pool, query); } } /* IMAP URL has no fragment */ if ((ret = uri_parse_fragment(parser, &query)) != 0) { if (ret == 1) parser->error = "Fragment component not allowed in IMAP URL"; return FALSE; } /* must be at end of URL now */ i_assert(parser->cur == parser->end); return TRUE; } /* Public API */ int imap_url_parse(const char *url, const struct imap_url *base, enum imap_url_parse_flags flags, struct imap_url **url_r, const char **error_r) { struct imap_url_parser url_parser; /* base != NULL indicates whether relative URLs are allowed. However, certain flags may also dictate whether relative URLs are allowed/required. */ i_assert((flags & IMAP_URL_PARSE_REQUIRE_RELATIVE) == 0 || base != NULL); i_assert((flags & IMAP_URL_PARSE_SCHEME_EXTERNAL) == 0 || base == NULL); memset(&url_parser, '\0', sizeof(url_parser)); uri_parser_init(&url_parser.parser, pool_datastack_create(), url); url_parser.url = t_new(struct imap_url, 1); url_parser.url->uauth_expire = (time_t)-1; url_parser.base = base; url_parser.flags = flags; if (!imap_url_do_parse(&url_parser)) { *error_r = url_parser.parser.error; return -1; } *url_r = url_parser.url; return 0; } /* * IMAP URL construction */ static void imap_url_append_mailbox(const struct imap_url *url, string_t *urlstr) { uri_append_path_data(urlstr, ";", url->mailbox); if (url->uidvalidity != 0) str_printfa(urlstr, ";UIDVALIDITY=%u", url->uidvalidity); if (url->uid == 0) { /* message list */ if (url->search_program != NULL) { str_append_c(urlstr, '?'); uri_append_query_data(urlstr, ";", url->search_program); } } else { /* message part */ str_printfa(urlstr, "/;UID=%u", url->uid); if (url->section != NULL) { str_append(urlstr, "/;SECTION="); uri_append_path_data(urlstr, ";", url->section); } if (url->have_partial) { str_append(urlstr, "/;PARTIAL="); if (url->partial_size == 0) { str_printfa(urlstr, "%"PRIuUOFF_T, url->partial_offset); } else { str_printfa(urlstr, "%"PRIuUOFF_T".%"PRIuUOFF_T, url->partial_offset, url->partial_size); } } /* urlauth */ if (url->uauth_access_application != NULL) { if (url->uauth_expire != (time_t)-1) { str_append(urlstr, ";EXPIRE="); str_append(urlstr, iso8601_date_create(url->uauth_expire)); } str_append(urlstr, ";URLAUTH="); str_append(urlstr, url->uauth_access_application); if (url->uauth_access_user != NULL) { str_append_c(urlstr, '+'); uri_append_user_data(urlstr, ";", url->uauth_access_user); } } } } const char *imap_url_create(const struct imap_url *url) { string_t *urlstr = t_str_new(512); /* scheme */ uri_append_scheme(urlstr, "imap"); str_append(urlstr, "//"); /* user */ if (url->userid != NULL || url->auth_type != NULL) { if (url->userid != NULL) uri_append_user_data(urlstr, ";:", url->userid); if (url->auth_type != NULL) { str_append(urlstr, ";AUTH="); uri_append_user_data(urlstr, ";:", url->auth_type); } str_append_c(urlstr, '@'); } /* server */ if (url->host_name != NULL) { /* assume IPv6 literal if starts with '['; avoid encoding */ if (*url->host_name == '[') str_append(urlstr, url->host_name); else uri_append_host_name(urlstr, url->host_name); } else if (url->have_host_ip) { uri_append_host_ip(urlstr, &url->host_ip); } else i_unreached(); if (url->have_port) uri_append_port(urlstr, url->port); /* Older syntax (RFC 2192) requires this slash at all times */ str_append_c(urlstr, '/'); /* mailbox */ if (url->mailbox != NULL) imap_url_append_mailbox(url, urlstr); return str_c(urlstr); } const char * imap_url_add_urlauth(const char *rumpurl, const char *mechanism, const unsigned char *token, size_t token_len) { return t_strconcat(rumpurl, ":", t_str_lcase(mechanism), ":", binary_to_hex(token, token_len), NULL); } dovecot-2.2.33.2/src/lib-imap/imap-quote.c0000644000175000017500000001257213123174404015041 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-arg.h" #include "imap-quote.h" /* If we have quoted-specials (<">, <\>) in a string, the minimum quoted-string overhead is 3 bytes ("\") while the minimum literal overhead is 5 bytes ("{n}\r\n"). But the literal overhead also depends on the string size. If the string length is less than 10, literal catches up to quoted-string after 3 quoted-specials. If the string length is 10..99, it catches up after 4 quoted-specials, and so on. We'll assume that the string lengths are usually in double digits, so we'll switch to literals after seeing 4 quoted-specials. */ #define QUOTED_MAX_ESCAPE_CHARS 4 void imap_append_string(string_t *dest, const char *src) { i_assert(src != NULL); imap_append_nstring(dest, src); } void imap_append_astring(string_t *dest, const char *src) { unsigned int i; i_assert(src != NULL); for (i = 0; src[i] != '\0'; i++) { if (!IS_ASTRING_CHAR(src[i])) { imap_append_string(dest, src); return; } } /* don't mix up NIL and "NIL"! */ if (i == 0 || strcasecmp(src, "NIL") == 0) imap_append_string(dest, src); else str_append(dest, src); } static void imap_append_literal(string_t *dest, const char *src, unsigned int pos) { size_t full_len = pos + strlen(src+pos); str_printfa(dest, "{%"PRIuSIZE_T"}\r\n", full_len); buffer_append(dest, src, full_len); } void imap_append_nstring(string_t *dest, const char *src) { unsigned int escape_count = 0; size_t i; if (src == NULL) { str_append(dest, "NIL"); return; } /* first check if we can (or want to) write this as quoted or as literal. quoted-specials = DQUOTE / "\" QUOTED-CHAR = / "\" quoted-specials TEXT-CHAR = */ for (i = 0; src[i] != '\0'; i++) { switch (src[i]) { case '"': case '\\': if (escape_count++ < QUOTED_MAX_ESCAPE_CHARS) break; /* fall through */ case 13: case 10: imap_append_literal(dest, src, i); return; default: if ((unsigned char)src[i] >= 0x80) { imap_append_literal(dest, src, i); return; } break; } } imap_append_quoted(dest, src); } static void remove_newlines_and_append(string_t *dest, const char *src) { size_t src_len; string_t *src_nolf; src_len = strlen(src); src_nolf = t_str_new(src_len + 1); for (size_t i = 0; i < src_len; ++i) { if (src[i] != '\r' && src[i] != '\n') { str_append_c(src_nolf, src[i]); } else if (src[i+1] != ' ' && src[i+1] != '\t' && src[i+1] != '\r' && src[i+1] != '\n' && src[i+1] != '\0') { /* ensure whitespace between lines if new line doesn't start with whitespace */ str_append_c(src_nolf, ' '); } } imap_append_nstring(dest, str_c(src_nolf)); } void imap_append_nstring_nolf(string_t *dest, const char *src) { if (src == NULL || strpbrk(src, "\r\n") == NULL) imap_append_nstring(dest, src); else if (buffer_get_pool(dest)->datastack_pool) remove_newlines_and_append(dest, src); else T_BEGIN { remove_newlines_and_append(dest, src); } T_END; } void imap_append_quoted(string_t *dest, const char *src) { str_append_c(dest, '"'); for (; *src != '\0'; src++) { switch (*src) { case 13: case 10: /* not allowed */ break; case '"': case '\\': str_append_c(dest, '\\'); str_append_c(dest, *src); break; default: if ((unsigned char)*src >= 0x80) { /* 8bit input not allowed in dquotes */ break; } str_append_c(dest, *src); break; } } str_append_c(dest, '"'); } void imap_append_string_for_humans(string_t *dest, const unsigned char *src, size_t size) { size_t i, pos, remove_count = 0; bool whitespace_prefix = TRUE, last_lwsp = TRUE, modify = FALSE; /* first check if there is anything to change */ for (i = 0; i < size; i++) { switch (src[i]) { case 0: /* convert NUL to #0x80 */ last_lwsp = FALSE; modify = TRUE; break; case 13: case 10: case '\t': modify = TRUE; /* fall through */ case ' ': if (last_lwsp) { modify = TRUE; remove_count++; } last_lwsp = TRUE; break; case '"': case '\\': modify = TRUE; last_lwsp = FALSE; break; default: if ((src[i] & 0x80) != 0) modify = TRUE; last_lwsp = FALSE; break; } if (!last_lwsp) whitespace_prefix = FALSE; } if (last_lwsp && i > 0 && !whitespace_prefix) { modify = TRUE; remove_count++; } if (!modify) { /* fast path: we can simply write it as quoted string without any escaping */ str_append_c(dest, '"'); str_append_n(dest, src, size); str_append_c(dest, '"'); return; } if (size == remove_count) { /* contained only whitespace */ str_append(dest, "\"\""); return; } str_printfa(dest, "{%"PRIuSIZE_T"}\r\n", size - remove_count); pos = str_len(dest); last_lwsp = TRUE; whitespace_prefix = TRUE; for (i = 0; i < size; i++) { switch (src[i]) { case 0: str_append_c(dest, 128); last_lwsp = FALSE; break; case 13: case 10: case '\t': case ' ': if (!last_lwsp) str_append_c(dest, ' '); last_lwsp = TRUE; break; default: last_lwsp = FALSE; str_append_c(dest, src[i]); break; } if (!last_lwsp) whitespace_prefix = FALSE; } if (last_lwsp && i > 0 && !whitespace_prefix) str_truncate(dest, str_len(dest)-1); i_assert(str_len(dest) - pos == size - remove_count); } dovecot-2.2.33.2/src/lib-imap/imap-base-subject.c0000644000175000017500000001316613165463624016266 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* Implementated against draft-ietf-imapext-sort-10 and draft-ietf-imapext-thread-12 */ #include "lib.h" #include "buffer.h" #include "charset-utf8.h" #include "message-header-decode.h" #include "imap-base-subject.h" static void pack_whitespace(buffer_t *buf) { char *data, *dest; bool last_lwsp; data = buffer_get_modifiable_data(buf, NULL); /* check if we need to do anything */ while (*data != '\0') { if (*data == '\t' || *data == '\n' || *data == '\r' || (*data == ' ' && (data[1] == ' ' || data[1] == '\t'))) break; data++; } if (*data == '\0') return; /* @UNSAFE: convert/pack the whitespace */ dest = data; last_lwsp = FALSE; while (*data != '\0') { if (*data == '\t' || *data == ' ' || *data == '\r' || *data == '\n') { if (!last_lwsp) { *dest++ = ' '; last_lwsp = TRUE; } } else { *dest++ = *data; last_lwsp = FALSE; } data++; } *dest = '\0'; data = buffer_get_modifiable_data(buf, NULL); buffer_set_used_size(buf, (size_t) (dest - data)+1); } static void remove_subj_trailers(buffer_t *buf, size_t start_pos, bool *is_reply_or_forward_r) { const char *data; size_t orig_size, size; /* subj-trailer = "(fwd)" / WSP */ data = buffer_get_data(buf, &orig_size); if (orig_size < 1) /* size includes trailing \0 */ return; for (size = orig_size-1; size > start_pos; ) { if (data[size-1] == ' ') size--; else if (size >= 5 && memcmp(data + size - 5, "(FWD)", 5) == 0) { *is_reply_or_forward_r = TRUE; size -= 5; } else { break; } } if (size != orig_size-1) { buffer_set_used_size(buf, size); buffer_append_c(buf, '\0'); } } static bool remove_blob(const char **datap) { const char *data = *datap; if (*data != '[') return FALSE; data++; while (*data != '\0' && *data != '[' && *data != ']') data++; if (*data != ']') return FALSE; data++; if (*data == ' ') data++; *datap = data; return TRUE; } static bool remove_subj_leader(buffer_t *buf, size_t *start_pos, bool *is_reply_or_forward_r) { const char *data, *orig_data; bool ret = FALSE; /* subj-leader = (*subj-blob subj-refwd) / WSP subj-blob = "[" *BLOBCHAR "]" *WSP subj-refwd = ("re" / ("fw" ["d"])) *WSP [subj-blob] ":" BLOBCHAR = %x01-5a / %x5c / %x5e-7f ; any CHAR except '[' and ']' */ orig_data = buffer_get_data(buf, NULL); orig_data += *start_pos; data = orig_data; if (*data == ' ') { /* independent from checks below - always removed */ data++; orig_data++; *start_pos += 1; ret = TRUE; } while (*data == '[') { if (!remove_blob(&data)) return ret; } if (strncmp(data, "RE", 2) == 0) data += 2; else if (strncmp(data, "FWD", 3) == 0) data += 3; else if (strncmp(data, "FW", 2) == 0) data += 2; else return ret; if (*data == ' ') data++; if (*data == '[' && !remove_blob(&data)) return ret; if (*data != ':') return ret; data++; *start_pos += (size_t)(data - orig_data); *is_reply_or_forward_r = TRUE; return TRUE; } static bool remove_blob_when_nonempty(buffer_t *buf, size_t *start_pos) { const char *data, *orig_data; orig_data = buffer_get_data(buf, NULL); orig_data += *start_pos; data = orig_data; if (*data == '[' && remove_blob(&data) && *data != '\0') { *start_pos += (size_t)(data - orig_data); return TRUE; } return FALSE; } static bool remove_subj_fwd_hdr(buffer_t *buf, size_t *start_pos, bool *is_reply_or_forward_r) { const char *data; size_t size; /* subj-fwd = subj-fwd-hdr subject subj-fwd-trl subj-fwd-hdr = "[fwd:" subj-fwd-trl = "]" */ data = buffer_get_data(buf, &size); if (strncmp(data + *start_pos, "[FWD:", 5) != 0) return FALSE; if (data[size-2] != ']') return FALSE; *is_reply_or_forward_r = TRUE; buffer_set_used_size(buf, size-2); buffer_append_c(buf, '\0'); *start_pos += 5; return TRUE; } const char *imap_get_base_subject_cased(pool_t pool, const char *subject, bool *is_reply_or_forward_r) { buffer_t *buf; size_t start_pos, subject_len; bool found; *is_reply_or_forward_r = FALSE; subject_len = strlen(subject); buf = buffer_create_dynamic(pool, subject_len); /* (1) Convert any RFC 2047 encoded-words in the subject to UTF-8. Convert all tabs and continuations to space. Convert all multiple spaces to a single space. */ message_header_decode_utf8((const unsigned char *)subject, subject_len, buf, uni_utf8_to_decomposed_titlecase); buffer_append_c(buf, '\0'); pack_whitespace(buf); start_pos = 0; do { /* (2) Remove all trailing text of the subject that matches the subj-trailer ABNF, repeat until no more matches are possible. */ remove_subj_trailers(buf, start_pos, is_reply_or_forward_r); do { /* (3) Remove all prefix text of the subject that matches the subj-leader ABNF. */ found = remove_subj_leader(buf, &start_pos, is_reply_or_forward_r); /* (4) If there is prefix text of the subject that matches the subj-blob ABNF, and removing that prefix leaves a non-empty subj-base, then remove the prefix text. */ found = remove_blob_when_nonempty(buf, &start_pos) || found; /* (5) Repeat (3) and (4) until no matches remain. */ } while (found); /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and subj-fwd-trl and repeat from step (2). */ } while (remove_subj_fwd_hdr(buf, &start_pos, is_reply_or_forward_r)); /* (7) The resulting text is the "base subject" used in the SORT. */ return (const char *)buf->data + start_pos; } dovecot-2.2.33.2/src/lib-imap/imap-quote.h0000644000175000017500000000160413123174404015040 00000000000000#ifndef IMAP_QUOTE_H #define IMAP_QUOTE_H /* Append "quoted" or literal. */ void imap_append_string(string_t *dest, const char *src); /* Append atom, "quoted" or literal. */ void imap_append_astring(string_t *dest, const char *src); /* Append NIL, "quoted" or literal. */ void imap_append_nstring(string_t *dest, const char *src); /* Append NIL, "quoted" or literal, CRs and LFs skipped. */ void imap_append_nstring_nolf(string_t *dest, const char *src); /* Append "quoted". If src has 8bit chars, skip over them. */ void imap_append_quoted(string_t *dest, const char *src); /* Otherwise the same as imap_append_string(), but cleanup the input data so that it's more readable by humans. This includes converting TABs to spaces, multiple spaces into a single space and NULs to #0x80. */ void imap_append_string_for_humans(string_t *dest, const unsigned char *src, size_t size); #endif dovecot-2.2.33.2/src/lib-imap/test-imap-util.c0000644000175000017500000000175713165463624015654 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-types.h" #include "imap-util.h" #include "test-common.h" static void test_imap_parse_system_flag(void) { test_begin("imap_parse_system_flag"); test_assert(imap_parse_system_flag("\\aNswered") == MAIL_ANSWERED); test_assert(imap_parse_system_flag("\\fLagged") == MAIL_FLAGGED); test_assert(imap_parse_system_flag("\\dEleted") == MAIL_DELETED); test_assert(imap_parse_system_flag("\\sEen") == MAIL_SEEN); test_assert(imap_parse_system_flag("\\dRaft") == MAIL_DRAFT); test_assert(imap_parse_system_flag("\\rEcent") == MAIL_RECENT); test_assert(imap_parse_system_flag("answered") == 0); test_assert(imap_parse_system_flag("\\broken") == 0); test_assert(imap_parse_system_flag("\\") == 0); test_assert(imap_parse_system_flag("") == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_imap_parse_system_flag, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-seqset.c0000644000175000017500000000360313123174404015203 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-seqset.h" static uint32_t get_next_number(const char **str) { uint32_t num; num = 0; while (**str != '\0') { if (**str < '0' || **str > '9') break; num = num*10 + (**str - '0'); (*str)++; } if (num == (uint32_t)-1) { /* FIXME: ugly hack, we're using this number to mean the last existing message. In reality UIDs should never get this high, so we can quite safely just drop this one down. */ num--; } return num; } static int get_next_seq_range(const char **str, uint32_t *seq1_r, uint32_t *seq2_r) { uint32_t seq1, seq2; if (**str == '*') { /* last message */ seq1 = (uint32_t)-1; *str += 1; } else { seq1 = get_next_number(str); if (seq1 == 0) return -1; } if (**str != ':') seq2 = seq1; else { /* first:last range */ *str += 1; if (**str == '*') { seq2 = (uint32_t)-1; *str += 1; } else { seq2 = get_next_number(str); if (seq2 == 0) return -1; } } if (seq1 > seq2) { /* swap, as specified by RFC-3501 */ *seq1_r = seq2; *seq2_r = seq1; } else { *seq1_r = seq1; *seq2_r = seq2; } return 0; } int imap_seq_set_parse(const char *str, ARRAY_TYPE(seq_range) *dest) { uint32_t seq1, seq2; while (*str != '\0') { if (get_next_seq_range(&str, &seq1, &seq2) < 0) return -1; seq_range_array_add_range(dest, seq1, seq2); if (*str == ',') str++; else if (*str != '\0') return -1; } return 0; } int imap_seq_set_nostar_parse(const char *str, ARRAY_TYPE(seq_range) *dest) { if (imap_seq_set_parse(str, dest) < 0) return -1; if (seq_range_exists(dest, (uint32_t)-1)) { /* '*' used */ return -1; } return 0; } int imap_seq_range_parse(const char *str, uint32_t *seq1_r, uint32_t *seq2_r) { if (get_next_seq_range(&str, seq1_r, seq2_r) < 0) return -1; return *str == '\0' ? 0 : -1; } dovecot-2.2.33.2/src/lib-imap/imap-url.h0000644000175000017500000000351113165463624014517 00000000000000#ifndef IMAP_URL_H #define IMAP_URL_H struct imap_url { /* server */ const char *host_name; struct ip_addr host_ip; in_port_t port; /* user */ const char *userid; const char *auth_type; /* mailbox */ const char *mailbox; uint32_t uidvalidity; /* 0 if not set */ /* message part */ uint32_t uid; const char *section; uoff_t partial_offset; uoff_t partial_size; /* 0 if not set */ /* message list (uid == 0) */ const char *search_program; /* urlauth */ const char *uauth_rumpurl; const char *uauth_access_application; const char *uauth_access_user; const char *uauth_mechanism; const unsigned char *uauth_token; size_t uauth_token_size; time_t uauth_expire; /* (time_t)-1 if not set */ unsigned int have_host_ip:1; /* url uses IP address */ unsigned int have_port:1; unsigned int have_partial:1; }; /* * IMAP URL parsing */ enum imap_url_parse_flags { /* Scheme part 'imap:' is already parsed externally. This implies that this is an absolute IMAP URL. */ IMAP_URL_PARSE_SCHEME_EXTERNAL = 0x01, /* Require relative URL (omitting _both_ scheme and authority), e.g. /MAILBOX/;UID=uid or even ;UID=uid. This flag means that an absolute URL makes no sense in this context. Relative URLs are allowed once a base URL is provided to the parser. */ IMAP_URL_PARSE_REQUIRE_RELATIVE = 0x02, /* Allow URLAUTH URL */ IMAP_URL_PARSE_ALLOW_URLAUTH = 0x04 }; /* Parses full IMAP URL. The returned URL is allocated from data stack. */ int imap_url_parse(const char *url, const struct imap_url *base, enum imap_url_parse_flags flags, struct imap_url **url_r, const char **error_r); /* * IMAP URL construction */ const char *imap_url_create(const struct imap_url *url); const char *imap_url_add_urlauth(const char *rumpurl, const char *mechanism, const unsigned char *token, size_t token_len); #endif dovecot-2.2.33.2/src/lib-imap/test-imap-match.c0000644000175000017500000000746713165463624015777 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-match.h" #include "test-common.h" struct test_imap_match { const char *pattern; const char *input; enum imap_match_result result; }; static void test_imap_match(void) { struct test_imap_match test[] = { { "", "", IMAP_MATCH_YES }, { "a", "b", IMAP_MATCH_NO }, { "foo", "foo", IMAP_MATCH_YES }, { "foo", "foo/", IMAP_MATCH_PARENT }, { "%", "", IMAP_MATCH_YES }, { "%", "foo", IMAP_MATCH_YES }, { "%", "foo/", IMAP_MATCH_PARENT }, { "%/", "foo/", IMAP_MATCH_YES }, { "%", "foo/bar", IMAP_MATCH_PARENT }, { "%/%", "foo", IMAP_MATCH_CHILDREN }, { "%/%", "foo/", IMAP_MATCH_YES }, { "foo/bar/%", "foo", IMAP_MATCH_CHILDREN }, { "foo/bar/%", "foo/", IMAP_MATCH_CHILDREN }, { "foo*", "foo", IMAP_MATCH_YES }, { "foo*", "foo/", IMAP_MATCH_YES }, { "foo*", "fobo", IMAP_MATCH_NO }, { "*foo*", "bar/foo/", IMAP_MATCH_YES }, { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, { "foo*bar", "foobar/baz", IMAP_MATCH_CHILDREN | IMAP_MATCH_PARENT }, { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, { "%/%/%", "foo/", IMAP_MATCH_CHILDREN }, { "%/%o/%", "foo/", IMAP_MATCH_CHILDREN }, { "%/%o/%", "foo", IMAP_MATCH_CHILDREN }, { "inbox", "inbox", IMAP_MATCH_YES }, { "inbox", "INBOX", IMAP_MATCH_NO } }; struct test_imap_match inbox_test[] = { { "inbox", "inbox", IMAP_MATCH_YES }, { "inbox", "iNbOx", IMAP_MATCH_YES }, { "i%X", "iNbOx", IMAP_MATCH_YES }, { "%I%N%B%O%X%", "inbox", IMAP_MATCH_YES }, { "i%X/foo", "iNbOx/foo", IMAP_MATCH_YES }, { "%I%N%B%O%X%/foo", "inbox/foo", IMAP_MATCH_YES }, { "i%X/foo", "inbx/foo", IMAP_MATCH_NO } }; struct imap_match_glob *glob, *glob2; unsigned int i; pool_t pool; pool = pool_alloconly_create("imap match", 1024); /* first try tests without inboxcasing */ test_begin("imap match"); for (i = 0; i < N_ELEMENTS(test); i++) { glob = imap_match_init(pool, test[i].pattern, FALSE, '/'); test_assert(imap_match(glob, test[i].input) == test[i].result); glob2 = imap_match_dup(default_pool, glob); test_assert(imap_match_globs_equal(glob, glob2)); p_clear(pool); /* test the dup after clearing first one's memory */ test_assert(imap_match(glob2, test[i].input) == test[i].result); imap_match_deinit(&glob2); } /* inboxcasing tests */ for (i = 0; i < N_ELEMENTS(inbox_test); i++) { glob = imap_match_init(pool, inbox_test[i].pattern, TRUE, '/'); test_assert(imap_match(glob, inbox_test[i].input) == inbox_test[i].result); glob2 = imap_match_dup(default_pool, glob); test_assert(imap_match_globs_equal(glob, glob2)); p_clear(pool); /* test the dup after clearing first one's memory */ test_assert(imap_match(glob2, inbox_test[i].input) == inbox_test[i].result); imap_match_deinit(&glob2); } pool_unref(&pool); test_end(); } static void test_imap_match_globs_equal(void) { struct imap_match_glob *glob; pool_t pool; pool = pool_alloconly_create("imap match globs equal", 1024); test_begin("imap match globs equal"); glob = imap_match_init(pool, "1", FALSE, '/'); test_assert(imap_match_globs_equal(glob, imap_match_init(pool, "1", FALSE, '/'))); test_assert(imap_match_globs_equal(glob, imap_match_init(pool, "1", TRUE, '/'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "1", FALSE, '.'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "11", FALSE, '/'))); glob = imap_match_init(pool, "in%", TRUE, '/'); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "in%", FALSE, '/'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "In%", TRUE, '/'))); pool_unref(&pool); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_imap_match, test_imap_match_globs_equal, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-id.c0000644000175000017500000000741113123174404014274 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "str-sanitize.h" #include "istream.h" #include "imap-parser.h" #include "imap-quote.h" #include "imap-id.h" #ifdef HAVE_SYS_UTSNAME_H # include #endif #ifdef HAVE_UNAME static struct utsname utsname_result; static bool utsname_set = FALSE; static const char *imap_id_get_uname(const char *key) { if (!utsname_set) { utsname_set = TRUE; if (uname(&utsname_result) < 0) { i_error("uname() failed: %m"); i_zero(&utsname_result); } } if (strcasecmp(key, "os") == 0) return utsname_result.sysname; if (strcasecmp(key, "os-version") == 0) return utsname_result.release; return NULL; } #endif static const char *imap_id_get_default(const char *key) { if (strcasecmp(key, "name") == 0) return PACKAGE_NAME; if (strcasecmp(key, "version") == 0) return PACKAGE_VERSION; if (strcasecmp(key, "support-url") == 0) return PACKAGE_WEBPAGE; if (strcasecmp(key, "support-email") == 0) return PACKAGE_BUGREPORT; #ifdef HAVE_UNAME return imap_id_get_uname(key); #endif } static const char * imap_id_reply_generate_from_imap_args(const struct imap_arg *args) { string_t *str; const char *key, *value; if (IMAP_ARG_IS_EOL(args)) return "NIL"; str = t_str_new(256); str_append_c(str, '('); for (; !IMAP_ARG_IS_EOL(args); args++) { if (!imap_arg_get_astring(args, &key)) { /* broken input */ if (IMAP_ARG_IS_EOL(&args[1])) break; args++; } else { /* key */ if (str_len(str) > 1) str_append_c(str, ' '); imap_append_quoted(str, key); str_append_c(str, ' '); /* value */ if (IMAP_ARG_IS_EOL(&args[1])) { str_append(str, "NIL"); break; } args++; if (!imap_arg_get_astring(args, &value)) value = NULL; else { if (strcmp(value, "*") == 0) value = imap_id_get_default(key); } imap_append_nstring(str, value); } } if (str_len(str) == 1) { /* broken */ return "NIL"; } str_append_c(str, ')'); return str_c(str); } const char *imap_id_reply_generate(const char *settings) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; const char *ret; if (settings == NULL) return "NIL"; input = i_stream_create_from_data(settings, strlen(settings)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); if (imap_parser_finish_line(parser, 0, 0, &args) <= 0) ret = "NIL"; else ret = imap_id_reply_generate_from_imap_args(args); imap_parser_unref(&parser); i_stream_destroy(&input); return ret; } void imap_id_log_reply_append(string_t *reply, const char *key, const char *value) { if (str_len(reply) > 0) str_append(reply, ", "); str_append(reply, str_sanitize(key, IMAP_ID_KEY_MAX_LEN)); str_append_c(reply, '='); str_append(reply, value == NULL ? "NIL" : str_sanitize(value, 80)); } const char *imap_id_args_get_log_reply(const struct imap_arg *args, const char *settings) { const char *const *keys, *key, *value; string_t *reply; bool log_all; if (settings == NULL || *settings == '\0') return NULL; if (!imap_arg_get_list(args, &args)) return NULL; log_all = strcmp(settings, "*") == 0; reply = t_str_new(256); keys = t_strsplit_spaces(settings, " "); while (!IMAP_ARG_IS_EOL(&args[0]) && !IMAP_ARG_IS_EOL(&args[1])) { if (!imap_arg_get_string(args, &key)) { /* broken input */ args += 2; continue; } args++; if (strlen(key) > 30) { /* broken: ID spec requires fields to be max. 30 octets */ args++; continue; } if (log_all || str_array_icase_find(keys, key)) { if (!imap_arg_get_nstring(args, &value)) value = ""; imap_id_log_reply_append(reply, key, value); } args++; } return str_len(reply) == 0 ? NULL : str_c(reply); } dovecot-2.2.33.2/src/lib-imap/imap-envelope.h0000644000175000017500000000105613123174404015521 00000000000000#ifndef IMAP_ENVELOPE_H #define IMAP_ENVELOPE_H struct imap_arg; struct message_part_envelope; /* Write envelope to given string */ void imap_envelope_write(struct message_part_envelope *data, string_t *str); /* Parse envelope from arguments */ bool imap_envelope_parse_args(const struct imap_arg *args, pool_t pool, struct message_part_envelope **envlp_r, const char **error_r); /* Parse envelope from string */ bool imap_envelope_parse(const char *envelope, pool_t pool, struct message_part_envelope **envlp_r, const char **error_r); #endif dovecot-2.2.33.2/src/lib-imap/test-imap-envelope.c0000644000175000017500000001315513165463624016507 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "str.h" #include "message-part-data.h" #include "message-parser.h" #include "imap-envelope.h" #include "test-common.h" struct parse_test { const char *message; const char *envelope; }; struct parse_test parse_tests[] = { /* Tests copied from imaptest */ { .message = "Message-ID: \n" "In-Reply-To: \n" "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "Subject: subject header\n" "From: From Real \n" "To: To Real \n" "Cc: Cc Real \n" "Bcc: Bcc Real \n" "Sender: Sender Real \n" "Reply-To: ReplyTo Real \n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" " "\"subject header\" " "((\"From Real\" NIL \"fromuser\" \"fromdomain.org\")) " "((\"Sender Real\" NIL \"senderuser\" \"senderdomain.org\")) " "((\"ReplyTo Real\" NIL \"replytouser\" \"replytodomain.org\")) " "((\"To Real\" NIL \"touser\" \"todomain.org\")) " "((\"Cc Real\" NIL \"ccuser\" \"ccdomain.org\")) " "((\"Bcc Real\" NIL \"bccuser\" \"bccdomain.org\")) " "\"\" \"\"" }, { .message = "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "From: user@domain\n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" NIL " "((NIL NIL \"user\" \"domain\")) " "((NIL NIL \"user\" \"domain\")) " "((NIL NIL \"user\" \"domain\")) NIL NIL NIL NIL NIL" }, { .message = "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "From: user@domain\n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" NIL " "((NIL NIL \"user\" \"domain\")) " "((NIL NIL \"user\" \"domain\")) " "((NIL NIL \"user\" \"domain\")) NIL NIL NIL NIL NIL" }, { .message = "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "From: user@domain (Real Name)\n" "To: group: g1@d1.org, g2@d2.org;, group2: g3@d3.org;\n" "Cc: group:;, group2: (foo) ;\n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" NIL " "((\"Real Name\" NIL \"user\" \"domain\")) " "((\"Real Name\" NIL \"user\" \"domain\")) " "((\"Real Name\" NIL \"user\" \"domain\")) " "((NIL NIL \"group\" NIL)" "(NIL NIL \"g1\" \"d1.org\")" "(NIL NIL \"g2\" \"d2.org\")" "(NIL NIL NIL NIL)" "(NIL NIL \"group2\" NIL)" "(NIL NIL \"g3\" \"d3.org\")" "(NIL NIL NIL NIL)) " "((NIL NIL \"group\" NIL)(NIL NIL NIL NIL)" "(NIL NIL \"group2\" NIL)(NIL NIL NIL NIL)) " "NIL NIL NIL" }, { .message = "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "From: user@domain (Real Name)\n" "Sender: \n" "Reply-To: \n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" NIL " "((\"Real Name\" NIL \"user\" \"domain\")) " "((\"Real Name\" NIL \"user\" \"domain\")) " "((\"Real Name\" NIL \"user\" \"domain\")) " "NIL NIL NIL NIL NIL" }, { .message = "Date: Thu, 15 Feb 2007 01:02:03 +0200\n" "From: <@route:user@domain>\n" "\n" "body\n", .envelope = "\"Thu, 15 Feb 2007 01:02:03 +0200\" NIL " "((NIL \"@route\" \"user\" \"domain\")) " "((NIL \"@route\" \"user\" \"domain\")) " "((NIL \"@route\" \"user\" \"domain\")) " "NIL NIL NIL NIL NIL" } }; static const unsigned int parse_tests_count = N_ELEMENTS(parse_tests); static struct message_part_envelope * msg_parse(pool_t pool, const char *message) { struct message_parser_ctx *parser; struct message_part_envelope *envlp = NULL; struct istream *input; struct message_block block; struct message_part *parts; int ret; input = i_stream_create_from_data(message, strlen(message)); parser = message_parser_init(pool, input, MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { i_assert(block.part->parent == NULL); message_part_envelope_parse_from_header(pool, &envlp, block.hdr); } test_assert(ret < 0); message_parser_deinit(&parser, &parts); i_stream_unref(&input); return envlp; } static void test_imap_envelope_write(void) { struct message_part_envelope *envlp; unsigned int i; for (i = 0; i < parse_tests_count; i++) T_BEGIN { struct parse_test *test = &parse_tests[i]; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap envelope write", 1024); test_begin(t_strdup_printf("imap envelope write [%u]", i)); envlp = msg_parse(pool, test->message); imap_envelope_write(envlp, str); test_assert(strcmp(str_c(str), test->envelope) == 0); pool_unref(&pool); test_end(); } T_END; } static void test_imap_envelope_parse(void) { struct message_part_envelope *envlp; const char *error; unsigned int i; bool ret; for (i = 0; i < parse_tests_count; i++) T_BEGIN { struct parse_test *test = &parse_tests[i]; string_t *str = t_str_new(128); pool_t pool = pool_alloconly_create("imap envelope parse", 1024); test_begin(t_strdup_printf("imap envelope parser [%u]", i)); ret = imap_envelope_parse(test->envelope, pool, &envlp, &error); test_assert(ret); if (ret) { str_truncate(str, 0); imap_envelope_write(envlp, str); test_assert(strcmp(str_c(str), test->envelope) == 0); } else { i_error("Invalid envelope: %s", error); } pool_unref(&pool); test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_imap_envelope_write, test_imap_envelope_parse, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap/imap-match.h0000644000175000017500000000274113123174404015002 00000000000000#ifndef IMAP_MATCH_H #define IMAP_MATCH_H enum imap_match_result { IMAP_MATCH_NO = 0x00, /* definite non-match */ IMAP_MATCH_YES = 0x01, /* match */ /* YES and NO are returned alone, but CHILDREN and PARENT may be returned both (eg. "foo*bar" vs. "foobar/baz") */ /* non-match, but its children could match (eg. "box" vs "box/%") */ IMAP_MATCH_CHILDREN = 0x02, /* non-match, but one of its parents does match. This should often be handled with YES matches, because when listing for "%" and "box/foo" exists but "box" doesn't, you should still list "box" as (Nonexistent HasChildren) mailbox. */ IMAP_MATCH_PARENT = 0x04 }; struct imap_match_glob; /* If inboxcase is TRUE, the "INBOX" string at the beginning of line is compared case-insensitively */ struct imap_match_glob * imap_match_init(pool_t pool, const char *pattern, bool inboxcase, char separator); struct imap_match_glob * imap_match_init_multiple(pool_t pool, const char *const *patterns, bool inboxcase, char separator); void imap_match_deinit(struct imap_match_glob **glob); struct imap_match_glob * imap_match_dup(pool_t pool, const struct imap_match_glob *glob); /* Returns TRUE if two globs were created with same init() parameters (but inboxcase is ignored if no patterns can match INBOX) */ bool imap_match_globs_equal(const struct imap_match_glob *glob1, const struct imap_match_glob *glob2); enum imap_match_result imap_match(struct imap_match_glob *glob, const char *data); #endif dovecot-2.2.33.2/src/lib-imap/imap-date.h0000644000175000017500000000206613123174404014623 00000000000000#ifndef IMAP_DATE_H #define IMAP_DATE_H /* Parses IMAP date/time string and returns TRUE if the string is valid. time_t is filled with UTC date. timezone_offset is filled with parsed timezone. If no timezone is given, local timezone is assumed. If date is too low or too high to fit to time_t, it's set to lowest/highest allowed value. This allows you to use the value directly for comparing timestamps. */ bool imap_parse_date(const char *str, time_t *timestamp_r); bool imap_parse_datetime(const char *str, time_t *timestamp_r, int *timezone_offset_r); /* Returns given UTC timestamp as IMAP date-time string in local timezone. */ const char *imap_to_datetime(time_t timestamp); /* Returns given UTC timestamp as IMAP date-time string in given timezone. */ const char *imap_to_datetime_tz(time_t timestamp, int timezone_offset); /* Returns TRUE if timestamp was successfully converted to a date, FALSE if time would have been required as well (but the string is still returned). */ bool imap_to_date(time_t timestamp, const char **str_r); #endif dovecot-2.2.33.2/src/lib-imap/Makefile.am0000644000175000017500000000504613123174404014646 00000000000000noinst_LTLIBRARIES = libimap.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail libimap_la_SOURCES = \ imap-arg.c \ imap-base-subject.c \ imap-bodystructure.c \ imap-date.c \ imap-envelope.c \ imap-id.c \ imap-keepalive.c \ imap-match.c \ imap-parser.c \ imap-quote.c \ imap-url.c \ imap-seqset.c \ imap-utf7.c \ imap-util.c headers = \ imap-arg.h \ imap-base-subject.h \ imap-bodystructure.h \ imap-date.h \ imap-envelope.h \ imap-id.h \ imap-keepalive.h \ imap-match.h \ imap-parser.h \ imap-resp-code.h \ imap-quote.h \ imap-url.h \ imap-seqset.h \ imap-utf7.h \ imap-util.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-imap-bodystructure \ test-imap-envelope \ test-imap-match \ test-imap-parser \ test-imap-quote \ test-imap-url \ test-imap-utf7 \ test-imap-util noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_imap_bodystructure_SOURCES = test-imap-bodystructure.c test_imap_bodystructure_LDADD = imap-bodystructure.lo imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs) test_imap_bodystructure_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la test_imap_envelope_SOURCES = test-imap-envelope.c test_imap_envelope_LDADD = imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs) test_imap_envelope_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la test_imap_match_SOURCES = test-imap-match.c test_imap_match_LDADD = imap-match.lo $(test_libs) test_imap_match_DEPENDENCIES = $(test_deps) test_imap_parser_SOURCES = test-imap-parser.c test_imap_parser_LDADD = imap-parser.lo imap-arg.lo $(test_libs) test_imap_parser_DEPENDENCIES = $(test_deps) test_imap_quote_SOURCES = test-imap-quote.c test_imap_quote_LDADD = imap-quote.lo $(test_libs) test_imap_quote_DEPENDENCIES = $(test_deps) test_imap_url_SOURCES = test-imap-url.c test_imap_url_LDADD = imap-url.lo $(test_libs) test_imap_url_DEPENDENCIES = $(test_deps) test_imap_utf7_SOURCES = test-imap-utf7.c test_imap_utf7_LDADD = imap-utf7.lo $(test_libs) test_imap_utf7_DEPENDENCIES = $(test_deps) test_imap_util_SOURCES = test-imap-util.c test_imap_util_LDADD = imap-util.lo imap-arg.lo $(test_libs) test_imap_util_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-imap/imap-arg.h0000644000175000017500000000710613165463624014472 00000000000000#ifndef IMAP_ARG_H #define IMAP_ARG_H #include "array.h" /* ABNF: CHAR = %x01-7F CTL = %x00-1F / %x7F SP = %x20 DQUOTE = %x22 */ /* ASTRING-CHAR = ATOM-CHAR / resp-specials */ #define IS_ASTRING_CHAR(c) (IS_ATOM_CHAR(c) || IS_RESP_SPECIAL(c)) /* ATOM-CHAR = */ #define IS_ATOM_CHAR(c) (!IS_ATOM_SPECIAL(c)) /* atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials Since atoms are only 7bit, we'll also optimize a bit by assuming 8bit chars are also atom-specials. */ #define IS_ATOM_SPECIAL(c) \ ((unsigned char)(c) <= 0x20 || (unsigned char)(c) >= 0x7f || \ (c) == '(' || (c) == ')' || (c) == '{' || IS_LIST_WILDCARD(c) || \ IS_QUOTED_SPECIAL(c) || IS_RESP_SPECIAL(c)) /* list-wildcards = "%" / "*" */ #define IS_LIST_WILDCARD(c) ((c) == '%' || (c) == '*') /* quoted-specials = DQUOTE / "\" */ #define IS_QUOTED_SPECIAL(c) ((c) == '\"' || (c) == '\\') /* resp-specials = "]" */ #define IS_RESP_SPECIAL(c) ((c) == ']') enum imap_arg_type { IMAP_ARG_NIL = 0, IMAP_ARG_ATOM, IMAP_ARG_STRING, IMAP_ARG_LIST, /* literals are returned as IMAP_ARG_STRING by default */ IMAP_ARG_LITERAL, IMAP_ARG_LITERAL_SIZE, IMAP_ARG_LITERAL_SIZE_NONSYNC, IMAP_ARG_EOL /* end of argument list */ }; ARRAY_DEFINE_TYPE(imap_arg_list, struct imap_arg); struct imap_arg { enum imap_arg_type type; struct imap_arg *parent; /* always of type IMAP_ARG_LIST */ /* Set when _data.str is set */ size_t str_len; union { const char *str; uoff_t literal_size; ARRAY_TYPE(imap_arg_list) list; } _data; unsigned int literal8:1; /* BINARY literal8 used */ }; /* RFC 3501's astring type. Note that this doesn't return TRUE for IMAP_ARG_NIL, although it should be treated the same as "NIL" string when reading an astring. */ #define IMAP_ARG_TYPE_IS_ASTRING(type) \ ((type) == IMAP_ARG_ATOM || \ (type) == IMAP_ARG_STRING || \ (type) == IMAP_ARG_LITERAL) #define IMAP_ARG_IS_ASTRING(arg) \ IMAP_ARG_TYPE_IS_ASTRING((arg)->type) #define IMAP_ARG_IS_NSTRING(arg) \ (IMAP_ARG_IS_ASTRING(arg) || (arg)->type == IMAP_ARG_NIL) #define IMAP_ARG_IS_EOL(arg) \ ((arg)->type == IMAP_ARG_EOL) bool imap_arg_get_atom(const struct imap_arg *arg, const char **str_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_quoted(const struct imap_arg *arg, const char **str_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_string(const struct imap_arg *arg, const char **str_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_astring(const struct imap_arg *arg, const char **str_r) ATTR_WARN_UNUSED_RESULT; /* str is set to NULL for NIL. */ bool imap_arg_get_nstring(const struct imap_arg *arg, const char **str_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_literal_size(const struct imap_arg *arg, uoff_t *size_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_list(const struct imap_arg *arg, const struct imap_arg **list_r) ATTR_WARN_UNUSED_RESULT; bool imap_arg_get_list_full(const struct imap_arg *arg, const struct imap_arg **list_r, unsigned int *list_count_r) ATTR_WARN_UNUSED_RESULT; /* Similar to above, but assumes that arg is already of correct type. */ const char *imap_arg_as_astring(const struct imap_arg *arg); const char *imap_arg_as_nstring(const struct imap_arg *arg); uoff_t imap_arg_as_literal_size(const struct imap_arg *arg); const struct imap_arg *imap_arg_as_list(const struct imap_arg *arg); /* Returns TRUE if arg is atom and case-insensitively matches str */ bool imap_arg_atom_equals(const struct imap_arg *arg, const char *str); #endif dovecot-2.2.33.2/src/lib-imap/imap-envelope.c0000644000175000017500000001423513165463624015532 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "str.h" #include "message-address.h" #include "message-part-data.h" #include "message-parser.h" #include "imap-parser.h" #include "imap-envelope.h" #include "imap-quote.h" /* * Envelope write */ static void imap_write_address(string_t *str, struct message_address *addr) { if (addr == NULL) { str_append(str, "NIL"); return; } str_append_c(str, '('); while (addr != NULL) { str_append_c(str, '('); if (addr->name == NULL) str_append(str, "NIL"); else { imap_append_string_for_humans(str, (const void *)addr->name, strlen(addr->name)); } str_append_c(str, ' '); imap_append_nstring(str, addr->route); str_append_c(str, ' '); imap_append_nstring(str, addr->mailbox); str_append_c(str, ' '); imap_append_nstring(str, addr->domain); str_append_c(str, ')'); addr = addr->next; } str_append_c(str, ')'); } void imap_envelope_write(struct message_part_envelope *data, string_t *str) { #define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr)) static const char *empty_envelope = "NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL"; if (data == NULL) { str_append(str, empty_envelope); return; } imap_append_nstring_nolf(str, data->date); str_append_c(str, ' '); if (data->subject == NULL) str_append(str, "NIL"); else { imap_append_string_for_humans(str, (const unsigned char *)data->subject, strlen(data->subject)); } str_append_c(str, ' '); imap_write_address(str, data->from); str_append_c(str, ' '); imap_write_address(str, NVL(data->sender, data->from)); str_append_c(str, ' '); imap_write_address(str, NVL(data->reply_to, data->from)); str_append_c(str, ' '); imap_write_address(str, data->to); str_append_c(str, ' '); imap_write_address(str, data->cc); str_append_c(str, ' '); imap_write_address(str, data->bcc); str_append_c(str, ' '); imap_append_nstring_nolf(str, data->in_reply_to); str_append_c(str, ' '); imap_append_nstring_nolf(str, data->message_id); } /* * ENVELOPE parsing */ static bool imap_envelope_parse_address(const struct imap_arg *arg, pool_t pool, struct message_address **addr_r) { struct message_address *addr; const struct imap_arg *list_args; const char *name, *route, *mailbox, *domain; unsigned int list_count; if (!imap_arg_get_list_full(arg, &list_args, &list_count)) return FALSE; /* we require 4 arguments, strings or NILs */ if (list_count < 4) return FALSE; if (!imap_arg_get_nstring(&list_args[0], &name)) return FALSE; if (!imap_arg_get_nstring(&list_args[1], &route)) return FALSE; if (!imap_arg_get_nstring(&list_args[2], &mailbox)) return FALSE; if (!imap_arg_get_nstring(&list_args[3], &domain)) return FALSE; addr = p_new(pool, struct message_address, 1); addr->name = p_strdup(pool, name); addr->route = p_strdup(pool, route); addr->mailbox = p_strdup(pool, mailbox); addr->domain = p_strdup(pool, domain); *addr_r = addr; return TRUE; } static bool imap_envelope_parse_addresses(const struct imap_arg *arg, pool_t pool, struct message_address **addrs_r) { struct message_address *first, *addr, *prev; const struct imap_arg *list_args; if (arg->type == IMAP_ARG_NIL) { *addrs_r = NULL; return TRUE; } if (!imap_arg_get_list(arg, &list_args)) return FALSE; first = addr = prev = NULL; for (; !IMAP_ARG_IS_EOL(list_args); list_args++) { if (!imap_envelope_parse_address (list_args, pool, &addr)) return FALSE; if (first == NULL) first = addr; if (prev != NULL) prev->next = addr; prev = addr; } *addrs_r = first; return TRUE; } bool imap_envelope_parse_args(const struct imap_arg *args, pool_t pool, struct message_part_envelope **envlp_r, const char **error_r) { struct message_part_envelope *envlp; envlp = p_new(pool, struct message_part_envelope, 1); if (!imap_arg_get_nstring(args++, &envlp->date)) { *error_r = "Invalid date field"; return FALSE; } envlp->date = p_strdup(pool, envlp->date); if (!imap_arg_get_nstring(args++, &envlp->subject)) { *error_r = "Invalid subject field"; return FALSE; } envlp->subject = p_strdup(pool, envlp->subject); if (!imap_envelope_parse_addresses(args++, pool, &envlp->from)) { *error_r = "Invalid from field"; return FALSE; } if (!imap_envelope_parse_addresses(args++, pool, &envlp->sender)) { *error_r = "Invalid sender field"; return FALSE; } if (!imap_envelope_parse_addresses(args++, pool, &envlp->reply_to)) { *error_r = "Invalid reply_to field"; return FALSE; } if (!imap_envelope_parse_addresses(args++, pool, &envlp->to)) { *error_r = "Invalid to field"; return FALSE; } if (!imap_envelope_parse_addresses(args++, pool, &envlp->cc)) { *error_r = "Invalid cc field"; return FALSE; } if (!imap_envelope_parse_addresses(args++, pool, &envlp->bcc)) { *error_r = "Invalid bcc field"; return FALSE; } if (!imap_arg_get_nstring(args++, &envlp->in_reply_to)) { *error_r = "Invalid in_reply_to field"; return FALSE; } envlp->in_reply_to = p_strdup(pool, envlp->in_reply_to); if (!imap_arg_get_nstring(args++, &envlp->message_id)) { *error_r = "Invalid message_id field"; return FALSE; } envlp->message_id = p_strdup(pool, envlp->message_id); *envlp_r = envlp; return TRUE; } bool imap_envelope_parse(const char *envelope, pool_t pool, struct message_part_envelope **envlp_r, const char **error_r) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; char *error; int ret; input = i_stream_create_from_data(envelope, strlen(envelope)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret < 0) { *error_r = t_strdup_printf("IMAP parser failed: %s", imap_parser_get_error(parser, NULL)); } else if (ret == 0) { *error_r = "Empty envelope"; ret = -1; } else { T_BEGIN { if (!imap_envelope_parse_args (args, pool, envlp_r, error_r)) { error = i_strdup(*error_r); ret = -1; } } T_END; if (ret < 0) { *error_r = t_strdup(error); i_free(error); } } imap_parser_unref(&parser); i_stream_destroy(&input); return (ret >= 0); } dovecot-2.2.33.2/src/lib-charset/0002755000175000017500000000000013172375610013400 500000000000000dovecot-2.2.33.2/src/lib-charset/charset-utf8.c0000644000175000017500000000477513123174404016006 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "unichar.h" #include "charset-utf8.h" #include bool charset_is_utf8(const char *charset) { return strcasecmp(charset, "us-ascii") == 0 || strcasecmp(charset, "ascii") == 0 || strcasecmp(charset, "UTF-8") == 0 || strcasecmp(charset, "UTF8") == 0; } int charset_to_utf8_str(const char *charset, normalizer_func_t *normalizer, const char *input, string_t *output, enum charset_result *result_r) { struct charset_translation *t; size_t len = strlen(input); if (charset_to_utf8_begin(charset, normalizer, &t) < 0) return -1; *result_r = charset_to_utf8(t, (const unsigned char *)input, &len, output); charset_to_utf8_end(&t); return 0; } struct charset_translation * charset_utf8_to_utf8_begin(normalizer_func_t *normalizer) { struct charset_translation *trans; if (charset_to_utf8_begin("UTF-8", normalizer, &trans) < 0) i_unreached(); return trans; } #ifndef HAVE_ICONV struct charset_translation { normalizer_func_t *normalizer; }; int charset_to_utf8_begin(const char *charset, normalizer_func_t *normalizer, struct charset_translation **t_r) { struct charset_translation *t; if (!charset_is_utf8(charset)) { /* no support for charsets that need translation */ return -1; } t = i_new(struct charset_translation, 1); t->normalizer = normalizer; *t_r = t; return 0; } void charset_to_utf8_end(struct charset_translation **_t) { struct charset_translation *t = *_t; *_t = NULL; i_free(t); } void charset_to_utf8_reset(struct charset_translation *t ATTR_UNUSED) { } enum charset_result charset_to_utf8(struct charset_translation *t, const unsigned char *src, size_t *src_size, buffer_t *dest) { return charset_utf8_to_utf8(t->normalizer, src, src_size, dest); } #endif enum charset_result charset_utf8_to_utf8(normalizer_func_t *normalizer, const unsigned char *src, size_t *src_size, buffer_t *dest) { enum charset_result res = CHARSET_RET_OK; size_t pos; uni_utf8_partial_strlen_n(src, *src_size, &pos); if (pos < *src_size) { i_assert(*src_size - pos <= CHARSET_MAX_PENDING_BUF_SIZE); *src_size = pos; res = CHARSET_RET_INCOMPLETE_INPUT; } if (normalizer != NULL) { if (normalizer(src, *src_size, dest) < 0) return CHARSET_RET_INVALID_INPUT; } else if (!uni_utf8_get_valid_data(src, *src_size, dest)) { return CHARSET_RET_INVALID_INPUT; } else { buffer_append(dest, src, *src_size); } return res; } dovecot-2.2.33.2/src/lib-charset/Makefile.in0000644000175000017500000005661613172375572015410 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-charset ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) am__DEPENDENCIES_1 = libcharset_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libcharset_la_OBJECTS = charset-iconv.lo charset-utf8.lo libcharset_la_OBJECTS = $(am_libcharset_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-charset$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_charset_OBJECTS = test-charset.$(OBJEXT) test_charset_OBJECTS = $(am_test_charset_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcharset_la_SOURCES) $(test_charset_SOURCES) DIST_SOURCES = $(libcharset_la_SOURCES) $(test_charset_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libcharset.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libcharset_la_LIBADD = $(LTLIBICONV) libcharset_la_SOURCES = \ charset-iconv.c \ charset-utf8.c headers = \ charset-utf8.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-charset test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_charset_SOURCES = test-charset.c test_charset_LDADD = libcharset.la $(test_libs) test_charset_DEPENDENCIES = libcharset.la $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-charset/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-charset/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libcharset.la: $(libcharset_la_OBJECTS) $(libcharset_la_DEPENDENCIES) $(EXTRA_libcharset_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libcharset_la_OBJECTS) $(libcharset_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-charset$(EXEEXT): $(test_charset_OBJECTS) $(test_charset_DEPENDENCIES) $(EXTRA_test_charset_DEPENDENCIES) @rm -f test-charset$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_charset_OBJECTS) $(test_charset_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charset-iconv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charset-utf8.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-charset.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-charset/test-charset.c0000644000175000017500000001205013165463624016073 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "test-common.h" #include "charset-utf8.h" static void test_charset_is_utf8(void) { test_begin("charset_is_utf8"); test_assert(charset_is_utf8("AScII")); test_assert(charset_is_utf8("us-AScII")); test_assert(charset_is_utf8("uTF8")); test_assert(charset_is_utf8("uTF-8")); test_end(); } static void test_charset_utf8_common(const char *input_charset) { struct { const char *input; const char *output; enum charset_result result; } tests[] = { { "p\xC3\xA4\xC3", "p\xC3\xA4", CHARSET_RET_INCOMPLETE_INPUT }, { "p\xC3\xA4\xC3""a", "p\xC3\xA4"UNICODE_REPLACEMENT_CHAR_UTF8"a", CHARSET_RET_INVALID_INPUT } }; string_t *src, *str = t_str_new(256); enum charset_result result; unsigned int i; for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); test_assert_idx(charset_to_utf8_str(input_charset, NULL, tests[i].input, str, &result) == 0, i); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); test_assert_idx(result == tests[i].result, i); } /* check that E2BIG handling works. We assume that iconv() is called with 8192 byte buffer (tmpbuf[8192]) */ src = str_new(default_pool, 16384); for (i = 0; i < 8190; i++) str_append_c(src, 'a' + i % ('z'-'a'+1)); for (i = 0; i < 256; i++) { str_truncate(str, 0); str_append_c(src, 'A' + i % ('Z'-'A'+1)); test_assert_idx(charset_to_utf8_str(input_charset, NULL, str_c(src), str, &result) == 0, i); } str_free(&src); } static void test_charset_utf8(void) { test_begin("charset utf8"); test_charset_utf8_common("UTF-8"); test_end(); } #ifdef HAVE_ICONV static void test_charset_iconv(void) { struct { const char *charset; const char *input; const char *output; enum charset_result result; } tests[] = { { "ISO-8859-1", "p\xE4\xE4", "p\xC3\xA4\xC3\xA4", CHARSET_RET_OK }, { "UTF-7", "+AOQA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDk", "\xC3\xA4\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4", CHARSET_RET_OK } }; string_t *str = t_str_new(128); struct charset_translation *trans; enum charset_result result; size_t pos, left, limit, len; unsigned int i; test_begin("charset iconv"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); test_assert_idx(charset_to_utf8_str(tests[i].charset, NULL, tests[i].input, str, &result) == 0, i); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); test_assert_idx(result == tests[i].result, i); str_truncate(str, 0); test_assert_idx(charset_to_utf8_begin(tests[i].charset, NULL, &trans) == 0, i); len = strlen(tests[i].input); for (pos = 0, limit = 1; limit <= len; pos += left, limit++) { left = limit - pos; result = charset_to_utf8(trans, (const void *)(tests[i].input + pos), &left, str); if (result != CHARSET_RET_INCOMPLETE_INPUT && result != CHARSET_RET_OK) break; } test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); test_assert_idx(result == tests[i].result, i); charset_to_utf8_end(&trans); } /* Use //IGNORE just to force handling to be done by iconv instead of our own UTF-8 routines. */ test_charset_utf8_common("UTF-8//TEST"); test_end(); } static void test_charset_iconv_crashes(void) { struct { const char *charset; const char *input; } tests[] = { { "CP932", "\203\334" } }; string_t *str = t_str_new(128); enum charset_result result; unsigned int i; test_begin("charset iconv crashes"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); /* we don't care about checking the result. we only want to verify that there's no crash. */ (void)charset_to_utf8_str(tests[i].charset, NULL, tests[i].input, str, &result); } test_end(); } static void test_charset_iconv_utf7_state(void) { struct charset_translation *trans; string_t *str = t_str_new(32); unsigned char nextbuf[5+CHARSET_MAX_PENDING_BUF_SIZE+1]; size_t size; test_begin("charset iconv utf7 state"); test_assert(charset_to_utf8_begin("UTF-7", NULL, &trans) == 0); size = 2; test_assert(charset_to_utf8(trans, (const void *)"a+", &size, str) == CHARSET_RET_INCOMPLETE_INPUT); test_assert(strcmp(str_c(str), "a") == 0); test_assert(size == 1); memset(nextbuf, '?', sizeof(nextbuf)); memcpy(nextbuf, "+AOQ-", 5); size = sizeof(nextbuf); test_assert(charset_to_utf8(trans, nextbuf, &size, str) == CHARSET_RET_OK); test_assert(strcmp(str_c(str), "a\xC3\xA4???????????") == 0); charset_to_utf8_end(&trans); test_end(); } #endif int main(void) { static void (*test_functions[])(void) = { test_charset_is_utf8, test_charset_utf8, #ifdef HAVE_ICONV test_charset_iconv, test_charset_iconv_crashes, test_charset_iconv_utf7_state, #endif NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-charset/charset-iconv.c0000644000175000017500000000651613123174404016231 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "unichar.h" #include "charset-utf8.h" #ifdef HAVE_ICONV #include #include struct charset_translation { iconv_t cd; normalizer_func_t *normalizer; }; int charset_to_utf8_begin(const char *charset, normalizer_func_t *normalizer, struct charset_translation **t_r) { struct charset_translation *t; iconv_t cd; if (charset_is_utf8(charset)) cd = (iconv_t)-1; else { if (strcmp(charset, "UTF-8//TEST") == 0) charset = "UTF-8"; cd = iconv_open("UTF-8", charset); if (cd == (iconv_t)-1) return -1; } t = i_new(struct charset_translation, 1); t->cd = cd; t->normalizer = normalizer; *t_r = t; return 0; } void charset_to_utf8_end(struct charset_translation **_t) { struct charset_translation *t = *_t; *_t = NULL; if (t->cd != (iconv_t)-1) iconv_close(t->cd); i_free(t); } void charset_to_utf8_reset(struct charset_translation *t) { if (t->cd != (iconv_t)-1) (void)iconv(t->cd, NULL, NULL, NULL, NULL); } static bool charset_to_utf8_try(struct charset_translation *t, const unsigned char *src, size_t *src_size, buffer_t *dest, enum charset_result *result) { ICONV_CONST char *ic_srcbuf; char tmpbuf[8192], *ic_destbuf; size_t srcleft, destleft, tmpbuf_used; bool ret = TRUE; if (t->cd == (iconv_t)-1) { /* input is already supposed to be UTF-8 */ *result = charset_utf8_to_utf8(t->normalizer, src, src_size, dest); return TRUE; } destleft = sizeof(tmpbuf); ic_destbuf = tmpbuf; srcleft = *src_size; ic_srcbuf = (ICONV_CONST char *) src; if (iconv(t->cd, &ic_srcbuf, &srcleft, &ic_destbuf, &destleft) != (size_t)-1) { i_assert(srcleft == 0); *result = CHARSET_RET_OK; } else if (errno == E2BIG) { /* set result just to avoid compiler warning */ *result = CHARSET_RET_INCOMPLETE_INPUT; ret = FALSE; } else if (errno == EINVAL) { i_assert(srcleft <= CHARSET_MAX_PENDING_BUF_SIZE); *result = CHARSET_RET_INCOMPLETE_INPUT; } else { /* should be EILSEQ */ *result = CHARSET_RET_INVALID_INPUT; ret = FALSE; } *src_size -= srcleft; /* we just converted data to UTF-8. it shouldn't be invalid, but Solaris iconv appears to pass invalid data through sometimes (e.g. 8 bit characters with UTF-7) */ tmpbuf_used = sizeof(tmpbuf) - destleft; if (charset_utf8_to_utf8(t->normalizer, (void *)tmpbuf, &tmpbuf_used, dest) != CHARSET_RET_OK) *result = CHARSET_RET_INVALID_INPUT; return ret; } enum charset_result charset_to_utf8(struct charset_translation *t, const unsigned char *src, size_t *src_size, buffer_t *dest) { enum charset_result result; size_t pos, size; size_t prev_invalid_pos = (size_t)-1; bool ret; for (pos = 0;;) { i_assert(pos <= *src_size); size = *src_size - pos; ret = charset_to_utf8_try(t, src + pos, &size, dest, &result); pos += size; if (ret) break; if (result == CHARSET_RET_INVALID_INPUT) { if (prev_invalid_pos != dest->used) { buffer_append(dest, UNICODE_REPLACEMENT_CHAR_UTF8, strlen(UNICODE_REPLACEMENT_CHAR_UTF8)); prev_invalid_pos = dest->used; } if (pos < *src_size) pos++; } } if (prev_invalid_pos != (size_t)-1) result = CHARSET_RET_INVALID_INPUT; i_assert(*src_size - pos <= CHARSET_MAX_PENDING_BUF_SIZE); *src_size = pos; return result; } #endif dovecot-2.2.33.2/src/lib-charset/charset-utf8.h0000644000175000017500000000357113123174404016004 00000000000000#ifndef CHARSET_UTF8_H #define CHARSET_UTF8_H #include "unichar.h" /* Max number of bytes that iconv can require for a single character. UTF-8 takes max 6 bytes per character. Not sure about others, but I'd think 10 is more than enough for everyone.. */ #define CHARSET_MAX_PENDING_BUF_SIZE 10 struct charset_translation; enum charset_result { CHARSET_RET_OK = 1, CHARSET_RET_INCOMPLETE_INPUT = -1, CHARSET_RET_INVALID_INPUT = -2 }; /* Begin translation to UTF-8. Returns -1 if charset is unknown. */ int charset_to_utf8_begin(const char *charset, normalizer_func_t *normalizer, struct charset_translation **t_r) ATTR_NULL(2); /* Translate UTF-8 to UTF-8 while validating the input. */ struct charset_translation * charset_utf8_to_utf8_begin(normalizer_func_t *normalizer); void charset_to_utf8_end(struct charset_translation **t); void charset_to_utf8_reset(struct charset_translation *t); /* Returns TRUE if charset is UTF-8 or ASCII */ bool charset_is_utf8(const char *charset) ATTR_PURE; /* Translate src to UTF-8. src_size is updated to contain the number of characters actually translated from src. The src_size should never shrink more than CHARSET_MAX_PENDING_BUF_SIZE bytes. If src contains invalid input, UNICODE_REPLACEMENT_CHAR is placed in such positions and the invalid input is skipped over. Return value is also CHARSET_RET_INCOMPLETE_INPUT in that case. */ enum charset_result charset_to_utf8(struct charset_translation *t, const unsigned char *src, size_t *src_size, buffer_t *dest); /* Translate a single string to UTF8. */ int charset_to_utf8_str(const char *charset, normalizer_func_t *normalizer, const char *input, string_t *output, enum charset_result *result_r) ATTR_NULL(2); /* INTERNAL: */ enum charset_result charset_utf8_to_utf8(normalizer_func_t *normalizer, const unsigned char *src, size_t *src_size, buffer_t *dest); #endif dovecot-2.2.33.2/src/lib-charset/Makefile.am0000644000175000017500000000136213123174404015346 00000000000000noinst_LTLIBRARIES = libcharset.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libcharset_la_LIBADD = $(LTLIBICONV) libcharset_la_SOURCES = \ charset-iconv.c \ charset-utf8.c headers = \ charset-utf8.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-charset noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_charset_SOURCES = test-charset.c test_charset_LDADD = libcharset.la $(test_libs) test_charset_DEPENDENCIES = libcharset.la $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-compression/0002755000175000017500000000000013172375611014311 500000000000000dovecot-2.2.33.2/src/lib-compression/compression.h0000644000175000017500000000147113123174404016735 00000000000000#ifndef COMPRESSION_H #define COMPRESSION_H struct compression_handler { const char *name; const char *ext; bool (*is_compressed)(struct istream *input); struct istream *(*create_istream)(struct istream *input, bool log_errors); struct ostream *(*create_ostream)(struct ostream *output, int level); }; extern const struct compression_handler compression_handlers[]; /* Lookup handler by its name (gz, bz2) */ const struct compression_handler *compression_lookup_handler(const char *name); /* Detect handler by looking at the first few bytes of the input stream. */ const struct compression_handler * compression_detect_handler(struct istream *input); /* Lookup handler based on filename extension in the path */ const struct compression_handler * compression_lookup_handler_from_ext(const char *path); #endif dovecot-2.2.33.2/src/lib-compression/Makefile.in0000644000175000017500000006562113172375572016314 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-compression ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = libcompression_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libcompression_la_OBJECTS = compression.lo istream-lzma.lo \ istream-lz4.lo istream-zlib.lo istream-bzlib.lo \ ostream-lzma.lo ostream-lz4.lo ostream-zlib.lo \ ostream-bzlib.lo libcompression_la_OBJECTS = $(am_libcompression_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_libdovecot_compression_la_OBJECTS = libdovecot_compression_la_OBJECTS = \ $(am_libdovecot_compression_la_OBJECTS) libdovecot_compression_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_compression_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__EXEEXT_1 = test-compression$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_compression_OBJECTS = test-compression.$(OBJEXT) test_compression_OBJECTS = $(am_test_compression_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcompression_la_SOURCES) \ $(libdovecot_compression_la_SOURCES) \ $(test_compression_SOURCES) DIST_SOURCES = $(libcompression_la_SOURCES) \ $(libdovecot_compression_la_SOURCES) \ $(test_compression_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libcompression.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libcompression_la_SOURCES = \ compression.c \ istream-lzma.c \ istream-lz4.c \ istream-zlib.c \ istream-bzlib.c \ ostream-lzma.c \ ostream-lz4.c \ ostream-zlib.c \ ostream-bzlib.c libcompression_la_LIBADD = \ $(COMPRESS_LIBS) pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ compression.h \ iostream-lz4.h \ istream-zlib.h \ ostream-zlib.h pkglib_LTLIBRARIES = libdovecot-compression.la libdovecot_compression_la_SOURCES = libdovecot_compression_la_LIBADD = libcompression.la ../lib-dovecot/libdovecot.la $(COMPRESS_LIBS) libdovecot_compression_la_DEPENDENCIES = libcompression.la ../lib-dovecot/libdovecot.la libdovecot_compression_la_LDFLAGS = -export-dynamic test_programs = \ test-compression test_libs = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(test_libs) test_compression_SOURCES = test-compression.c test_compression_LDADD = $(test_libs) test_compression_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-compression/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-compression/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libcompression.la: $(libcompression_la_OBJECTS) $(libcompression_la_DEPENDENCIES) $(EXTRA_libcompression_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libcompression_la_OBJECTS) $(libcompression_la_LIBADD) $(LIBS) libdovecot-compression.la: $(libdovecot_compression_la_OBJECTS) $(libdovecot_compression_la_DEPENDENCIES) $(EXTRA_libdovecot_compression_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_compression_la_LINK) -rpath $(pkglibdir) $(libdovecot_compression_la_OBJECTS) $(libdovecot_compression_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-compression$(EXEEXT): $(test_compression_OBJECTS) $(test_compression_DEPENDENCIES) $(EXTRA_test_compression_DEPENDENCIES) @rm -f test-compression$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_compression_OBJECTS) $(test_compression_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compression.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-bzlib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-lz4.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-lzma.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-zlib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-bzlib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-lz4.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-lzma.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-zlib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-compression.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-compression/test-compression.c0000644000175000017500000002116213165463624017717 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "ostream.h" #include "sha1.h" #include "randgen.h" #include "test-common.h" #include "compression.h" #include #include static void test_compression_handler(const struct compression_handler *handler) { const char *path = "test-compression.tmp"; struct istream *file_input, *input; struct ostream *file_output, *output; unsigned char buf[IO_BLOCK_SIZE]; const unsigned char *data; size_t size; struct sha1_ctxt sha1; unsigned char output_sha1[SHA1_RESULTLEN], input_sha1[SHA1_RESULTLEN]; unsigned int i; int fd; ssize_t ret; test_begin(t_strdup_printf("compression handler %s", handler->name)); /* write compressed data */ fd = open(path, O_TRUNC | O_CREAT | O_RDWR, 0600); if (fd == -1) i_fatal("creat(%s) failed: %m", path); file_output = o_stream_create_fd_file(fd, 0, FALSE); output = handler->create_ostream(file_output, 1); sha1_init(&sha1); /* 1) write lots of easily compressible data */ memset(buf, 0, sizeof(buf)); for (i = 0; i < 1024*1024*4 / sizeof(buf); i++) { sha1_loop(&sha1, buf, sizeof(buf)); test_assert(o_stream_send(output, buf, sizeof(buf)) == sizeof(buf)); } /* 2) write uncompressible data */ for (i = 0; i < 1024*128 / sizeof(buf); i++) { random_fill_weak(buf, sizeof(buf)); sha1_loop(&sha1, buf, sizeof(buf)); test_assert(o_stream_send(output, buf, sizeof(buf)) == sizeof(buf)); } /* 3) write semi-compressible data */ for (i = 0; i < sizeof(buf); i++) { if (rand () % 3 == 0) buf[i] = rand() % 4; else buf[i] = i; } for (i = 0; i < 1024*128 / sizeof(buf); i++) { sha1_loop(&sha1, buf, sizeof(buf)); test_assert(o_stream_send(output, buf, sizeof(buf)) == sizeof(buf)); } o_stream_destroy(&output); o_stream_destroy(&file_output); sha1_result(&sha1, output_sha1); /* read and uncompress the data */ sha1_init(&sha1); file_input = i_stream_create_fd(fd, IO_BLOCK_SIZE, FALSE); input = handler->create_istream(file_input, FALSE); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { sha1_loop(&sha1, data, size); i_stream_skip(input, size); } test_assert(ret == -1); i_stream_destroy(&input); i_stream_destroy(&file_input); sha1_result(&sha1, input_sha1); test_assert(memcmp(input_sha1, output_sha1, sizeof(input_sha1)) == 0); i_unlink(path); i_close_fd(&fd); test_end(); } static void test_compression(void) { unsigned int i; for (i = 0; compression_handlers[i].name != NULL; i++) { if (compression_handlers[i].create_istream != NULL) test_compression_handler(&compression_handlers[i]); } } static void test_gz(const char *str1, const char *str2) { const struct compression_handler *gz = compression_lookup_handler("gz"); struct ostream *buf_output, *output; struct istream *test_input, *input; buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 512); if (gz == NULL || gz->create_ostream == NULL) return; /* not compiled in */ /* write concated output */ buf_output = o_stream_create_buffer(buf); output = gz->create_ostream(buf_output, 6); o_stream_nsend_str(output, str1); test_assert(o_stream_nfinish(output) == 0); o_stream_destroy(&output); if (str2[0] != '\0') { output = gz->create_ostream(buf_output, 6); o_stream_nsend_str(output, "world"); test_assert(o_stream_nfinish(output) == 0); o_stream_destroy(&output); } o_stream_destroy(&buf_output); /* read concated input */ const unsigned char *data; size_t size; test_input = test_istream_create_data(buf->data, buf->used); test_istream_set_allow_eof(test_input, FALSE); input = gz->create_istream(test_input, TRUE); for (size_t i = 0; i <= buf->used; i++) { test_istream_set_size(test_input, i); test_assert(i_stream_read(input) >= 0); } test_istream_set_allow_eof(test_input, TRUE); test_assert(i_stream_read(input) == -1); test_assert(input->stream_errno == 0); data = i_stream_get_data(input, &size); test_assert(size == strlen(str1)+strlen(str2) && memcmp(data, str1, strlen(str1)) == 0 && memcmp(data+strlen(str1), str2, strlen(str2)) == 0); i_stream_unref(&input); i_stream_unref(&test_input); } static void test_gz_concat(void) { test_begin("gz concat"); test_gz("hello", "world"); test_end(); } static void test_gz_no_concat(void) { test_begin("gz no concat"); test_gz("hello", ""); test_end(); } static void test_gz_large_header(void) { const struct compression_handler *gz = compression_lookup_handler("gz"); static const unsigned char gz_input[] = { 0x1f, 0x8b, 0x08, 0x08, 'a','a','a','a','a','a','a','a','a','a','a', 0 }; struct istream *file_input, *input; size_t i; if (gz == NULL || gz->create_istream == NULL) return; /* not compiled in */ test_begin("gz large header"); /* max buffer size smaller than gz header */ for (i = 1; i < sizeof(gz_input); i++) { file_input = test_istream_create_data(gz_input, sizeof(gz_input)); test_istream_set_size(file_input, i); test_istream_set_max_buffer_size(file_input, i); input = gz->create_istream(file_input, FALSE); test_assert_idx(i_stream_read(input) == 0, i); test_assert_idx(i_stream_read(input) == -1 && input->stream_errno == EINVAL, i); i_stream_unref(&input); i_stream_unref(&file_input); } /* max buffer size is exactly the gz header */ file_input = test_istream_create_data(gz_input, sizeof(gz_input)); input = gz->create_istream(file_input, FALSE); test_istream_set_size(input, i); test_istream_set_allow_eof(input, FALSE); test_istream_set_max_buffer_size(input, i); test_assert(i_stream_read(input) == 0); i_stream_unref(&input); i_stream_unref(&file_input); test_end(); } static void test_uncompress_file(const char *path) { const struct compression_handler *handler; struct istream *input, *file_input; const unsigned char *data; size_t size; handler = compression_lookup_handler_from_ext(path); if (handler == NULL) i_fatal("Can't detect compression algorithm from path %s", path); if (handler->create_istream == NULL) i_fatal("Support not compiled in for %s", handler->name); file_input = i_stream_create_file(path, IO_BLOCK_SIZE); input = handler->create_istream(file_input, TRUE); while (i_stream_read_more(input, &data, &size) > 0) { if (write(STDOUT_FILENO, data, size) < 0) break; i_stream_skip(input, size); } i_stream_destroy(&input); } static void test_compress_file(const char *in_path, const char *out_path) { const struct compression_handler *handler; struct istream *input, *file_input; struct ostream *output, *file_output; int fd_in, fd_out; struct sha1_ctxt sha1; unsigned char output_sha1[SHA1_RESULTLEN], input_sha1[SHA1_RESULTLEN]; const unsigned char *data; size_t size; ssize_t ret; handler = compression_lookup_handler_from_ext(out_path); if (handler == NULL) i_fatal("Can't detect compression algorithm from path %s", out_path); if (handler->create_ostream == NULL) i_fatal("Support not compiled in for %s", handler->name); /* write the compressed output file */ fd_in = open(in_path, O_RDONLY); if (fd_in == -1) i_fatal("open(%s) failed: %m", in_path); fd_out = open(out_path, O_TRUNC | O_CREAT | O_RDWR, 0600); if (fd_out == -1) i_fatal("creat(%s) failed: %m", out_path); sha1_init(&sha1); file_output = o_stream_create_fd_file(fd_out, 0, FALSE); output = handler->create_ostream(file_output, 1); input = i_stream_create_fd_autoclose(&fd_in, IO_BLOCK_SIZE); while (i_stream_read_data(input, &data, &size, 0) > 0) { sha1_loop(&sha1, data, size); o_stream_nsend(output, data, size); i_stream_skip(input, size); } if (o_stream_nfinish(output) < 0) { i_fatal("write(%s) failed: %s", out_path, o_stream_get_error(output)); } i_stream_destroy(&input); o_stream_destroy(&output); o_stream_destroy(&file_output); sha1_result(&sha1, output_sha1); /* verify that we can read the compressed file */ sha1_init(&sha1); file_input = i_stream_create_fd(fd_out, IO_BLOCK_SIZE, FALSE); input = handler->create_istream(file_input, FALSE); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { sha1_loop(&sha1, data, size); i_stream_skip(input, size); } i_stream_destroy(&input); i_stream_destroy(&file_input); sha1_result(&sha1, input_sha1); if (memcmp(input_sha1, output_sha1, sizeof(input_sha1)) != 0) i_fatal("Decompression couldn't get the original input"); i_close_fd(&fd_out); } int main(int argc, char *argv[]) { static void (*test_functions[])(void) = { test_compression, test_gz_concat, test_gz_no_concat, test_gz_large_header, NULL }; if (argc == 2) { test_uncompress_file(argv[1]); return 0; } if (argc == 3) { test_compress_file(argv[1], argv[2]); return 0; } return test_run(test_functions); } dovecot-2.2.33.2/src/lib-compression/ostream-bzlib.c0000644000175000017500000001221713165463624017154 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_BZLIB #include "ostream-private.h" #include "ostream-zlib.h" #include #define CHUNK_SIZE (1024*64) struct bzlib_ostream { struct ostream_private ostream; bz_stream zs; char outbuf[CHUNK_SIZE]; unsigned int outbuf_offset, outbuf_used; unsigned int flushed:1; }; static void o_stream_bzlib_close(struct iostream_private *stream, bool close_parent) { struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; (void)o_stream_flush(&zstream->ostream.ostream); (void)BZ2_bzCompressEnd(&zstream->zs); if (close_parent) o_stream_close(zstream->ostream.parent); } static int o_stream_zlib_send_outbuf(struct bzlib_ostream *zstream) { ssize_t ret; size_t size; if (zstream->outbuf_used == 0) return 1; size = zstream->outbuf_used - zstream->outbuf_offset; i_assert(size > 0); ret = o_stream_send(zstream->ostream.parent, zstream->outbuf + zstream->outbuf_offset, size); if (ret < 0) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } if ((size_t)ret != size) { zstream->outbuf_offset += ret; return 0; } zstream->outbuf_offset = 0; zstream->outbuf_used = 0; return 1; } static ssize_t o_stream_bzlib_send_chunk(struct bzlib_ostream *zstream, const void *data, size_t size) { bz_stream *zs = &zstream->zs; int ret; i_assert(zstream->outbuf_used == 0); zs->next_in = (void *)data; zs->avail_in = size; while (zs->avail_in > 0) { if (zs->avail_out == 0) { /* previous block was compressed. send it and start compression for a new block. */ zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = sizeof(zstream->outbuf); if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) return -1; if (ret == 0) { /* parent stream's buffer full */ break; } } switch (BZ2_bzCompress(zs, BZ_RUN)) { case BZ_RUN_OK: break; default: i_unreached(); } } size -= zs->avail_in; zstream->flushed = FALSE; return size; } static int o_stream_bzlib_send_flush(struct bzlib_ostream *zstream) { bz_stream *zs = &zstream->zs; size_t len; bool done = FALSE; int ret; if (zs->avail_in != 0) { i_assert(zstream->ostream.ostream.last_failed_errno != 0); zstream->ostream.ostream.stream_errno = zstream->ostream.ostream.last_failed_errno; return -1; } if (zstream->flushed) return 0; if ((ret = o_stream_flush_parent_if_needed(&zstream->ostream)) <= 0) return ret; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; i_assert(zstream->outbuf_used == 0); do { len = sizeof(zstream->outbuf) - zs->avail_out; if (len != 0) { zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = len; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; if (done) break; } ret = BZ2_bzCompress(zs, BZ_FINISH); switch (ret) { case BZ_STREAM_END: done = TRUE; break; case BZ_FINISH_OK: break; default: i_unreached(); } } while (zs->avail_out != sizeof(zstream->outbuf)); zstream->flushed = TRUE; return 0; } static int o_stream_bzlib_flush(struct ostream_private *stream) { struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; int ret; if (o_stream_bzlib_send_flush(zstream) < 0) return -1; ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); return ret; } static ssize_t o_stream_bzlib_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct bzlib_ostream *zstream = (struct bzlib_ostream *)stream; ssize_t ret, bytes = 0; unsigned int i; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ return ret; } for (i = 0; i < iov_count; i++) { ret = o_stream_bzlib_send_chunk(zstream, iov[i].iov_base, iov[i].iov_len); if (ret < 0) return -1; bytes += ret; if ((size_t)ret != iov[i].iov_len) break; } stream->ostream.offset += bytes; /* avail_in!=0 check is used to detect errors. if it's non-zero here it simply means we didn't send all the data */ zstream->zs.avail_in = 0; return bytes; } struct ostream *o_stream_create_bz2(struct ostream *output, int level) { struct bzlib_ostream *zstream; int ret; i_assert(level >= 1 && level <= 9); zstream = i_new(struct bzlib_ostream, 1); zstream->ostream.sendv = o_stream_bzlib_sendv; zstream->ostream.flush = o_stream_bzlib_flush; zstream->ostream.iostream.close = o_stream_bzlib_close; ret = BZ2_bzCompressInit(&zstream->zs, level, 0, 0); switch (ret) { case BZ_OK: break; case BZ_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); case BZ_CONFIG_ERROR: i_fatal("Wrong bzlib library version (broken compilation)"); case BZ_PARAM_ERROR: i_fatal("bzlib: Invalid parameters"); default: i_fatal("BZ2_bzCompressInit() failed with %d", ret); } zstream->zs.next_out = zstream->outbuf; zstream->zs.avail_out = sizeof(zstream->outbuf); return o_stream_create(&zstream->ostream, output, o_stream_get_fd(output)); } #endif dovecot-2.2.33.2/src/lib-compression/istream-lzma.c0000644000175000017500000002251713165463624017013 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_LZMA #include "istream-private.h" #include "istream-zlib.h" #include #define CHUNK_SIZE (1024*64) #define LZMA_MEMORY_LIMIT (1024*1024*80) struct lzma_istream { struct istream_private istream; lzma_stream strm; uoff_t eof_offset, stream_size; size_t high_pos; struct stat last_parent_statbuf; unsigned int log_errors:1; unsigned int marked:1; unsigned int strm_closed:1; }; static void i_stream_lzma_close(struct iostream_private *stream, bool close_parent) { struct lzma_istream *zstream = (struct lzma_istream *)stream; if (!zstream->strm_closed) { lzma_end(&zstream->strm); zstream->strm_closed = TRUE; } if (close_parent) i_stream_close(zstream->istream.parent); } static void lzma_read_error(struct lzma_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "lzma.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, zstream->istream.abs_start_offset + zstream->istream.istream.v_offset); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); } static void lzma_stream_end(struct lzma_istream *zstream) { zstream->eof_offset = zstream->istream.istream.v_offset + (zstream->istream.pos - zstream->istream.skip); zstream->stream_size = zstream->eof_offset; } static ssize_t i_stream_lzma_read(struct istream_private *stream) { struct lzma_istream *zstream = (struct lzma_istream *)stream; const unsigned char *data; uoff_t high_offset; size_t size, out_size; lzma_ret ret; high_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (zstream->eof_offset == high_offset) { i_assert(zstream->high_pos == 0 || zstream->high_pos == stream->pos); stream->istream.eof = TRUE; return -1; } if (stream->pos < zstream->high_pos) { /* we're here because we seeked back within the read buffer. */ ret = zstream->high_pos - stream->pos; stream->pos = zstream->high_pos; zstream->high_pos = 0; if (zstream->eof_offset != (uoff_t)-1) { high_offset = stream->istream.v_offset + (stream->pos - stream->skip); i_assert(zstream->eof_offset == high_offset); stream->istream.eof = TRUE; } return ret; } zstream->high_pos = 0; if (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_size) return -2; /* buffer full */ } } if (i_stream_read_data(stream->parent, &data, &size, 0) < 0) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else { i_assert(stream->parent->eof); lzma_stream_end(zstream); stream->istream.eof = TRUE; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->strm.next_in = data; zstream->strm.avail_in = size; out_size = stream->buffer_size - stream->pos; zstream->strm.next_out = stream->w_buffer + stream->pos; zstream->strm.avail_out = out_size; ret = lzma_code(&zstream->strm, LZMA_RUN); out_size -= zstream->strm.avail_out; stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->strm.avail_in); switch (ret) { case LZMA_OK: break; case LZMA_DATA_ERROR: case LZMA_BUF_ERROR: lzma_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case LZMA_FORMAT_ERROR: lzma_read_error(zstream, "wrong magic in header (not xz file?)"); stream->istream.stream_errno = EINVAL; return -1; case LZMA_OPTIONS_ERROR: lzma_read_error(zstream, "Unsupported xz options"); stream->istream.stream_errno = EIO; return -1; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case LZMA_STREAM_END: lzma_stream_end(zstream); if (out_size == 0) { stream->istream.eof = TRUE; return -1; } break; default: lzma_read_error(zstream, t_strdup_printf( "lzma_code() failed with %d", ret)); stream->istream.stream_errno = EIO; return -1; } if (out_size == 0) { /* read more input */ return i_stream_lzma_read(stream); } return out_size; } static void i_stream_lzma_init(struct lzma_istream *zstream) { lzma_ret ret; ret = lzma_stream_decoder(&zstream->strm, LZMA_MEMORY_LIMIT, LZMA_CONCATENATED); switch (ret) { case LZMA_OK: break; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory"); default: i_fatal("lzma_stream_decoder() failed with ret=%d", ret); } } static void i_stream_lzma_reset(struct lzma_istream *zstream) { struct istream_private *stream = &zstream->istream; i_stream_seek(stream->parent, stream->parent_start_offset); zstream->eof_offset = (uoff_t)-1; zstream->strm.next_in = NULL; zstream->strm.avail_in = 0; stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; zstream->high_pos = 0; lzma_end(&zstream->strm); i_stream_lzma_init(zstream); } static void i_stream_lzma_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct lzma_istream *zstream = (struct lzma_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; if (v_offset < start_offset) { /* have to seek backwards */ i_stream_lzma_reset(zstream); start_offset = 0; } else if (zstream->high_pos != 0) { stream->pos = zstream->high_pos; zstream->high_pos = 0; } if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; stream->istream.v_offset = v_offset; zstream->high_pos = stream->pos; stream->pos = stream->skip; } else { /* read and cache forward */ ssize_t ret; do { size_t avail = stream->pos - stream->skip; if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); ret = -1; break; } i_stream_skip(&stream->istream, avail); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); if (stream->istream.v_offset != v_offset) { /* some failure, we've broken it */ if (stream->istream.stream_errno != 0) { i_error("lzma_istream.seek(%s) failed: %s", i_stream_get_name(&stream->istream), strerror(stream->istream.stream_errno)); i_stream_close(&stream->istream); } else { /* unexpected EOF. allow it since we may just want to check if there's anything.. */ i_assert(stream->istream.eof); } } } if (mark) zstream->marked = TRUE; } static int i_stream_lzma_stat(struct istream_private *stream, bool exact) { struct lzma_istream *zstream = (struct lzma_istream *) stream; const struct stat *st; size_t size; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; /* when exact=FALSE always return the parent stat's size, even if we know the exact value. this is necessary because otherwise e.g. mbox code can see two different values and think that a compressed mbox file keeps changing. */ if (!exact) return 0; if (zstream->stream_size == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; ssize_t ret; do { size = i_stream_get_data_size(&stream->istream); i_stream_skip(&stream->istream, size); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); i_stream_seek(&stream->istream, old_offset); if (zstream->stream_size == (uoff_t)-1) return -1; } stream->statbuf.st_size = zstream->stream_size; return 0; } static void i_stream_lzma_sync(struct istream_private *stream) { struct lzma_istream *zstream = (struct lzma_istream *) stream; const struct stat *st; if (i_stream_stat(stream->parent, FALSE, &st) < 0) { if (memcmp(&zstream->last_parent_statbuf, st, sizeof(*st)) == 0) { /* a compressed file doesn't change unexpectedly, don't clear our caches unnecessarily */ return; } zstream->last_parent_statbuf = *st; } i_stream_lzma_reset(zstream); } struct istream *i_stream_create_lzma(struct istream *input, bool log_errors) { struct lzma_istream *zstream; zstream = i_new(struct lzma_istream, 1); zstream->eof_offset = (uoff_t)-1; zstream->stream_size = (uoff_t)-1; zstream->log_errors = log_errors; i_stream_lzma_init(zstream); zstream->istream.iostream.close = i_stream_lzma_close; zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; zstream->istream.read = i_stream_lzma_read; zstream->istream.seek = i_stream_lzma_seek; zstream->istream.stat = i_stream_lzma_stat; zstream->istream.sync = i_stream_lzma_sync; zstream->istream.istream.readable_fd = FALSE; zstream->istream.istream.blocking = input->blocking; zstream->istream.istream.seekable = input->seekable; return i_stream_create(&zstream->istream, input, i_stream_get_fd(input)); } #endif dovecot-2.2.33.2/src/lib-compression/iostream-lz4.h0000644000175000017500000000171013123174404016722 00000000000000#ifndef IOSTREAM_LZ4_H #define IOSTREAM_LZ4_H /* Dovecot's LZ4 compressed files contain: IOSTREAM_LZ4_HEADER n x (4 byte big-endian: compressed chunk length, compressed chunk) */ #define IOSTREAM_LZ4_MAGIC "Dovecot-LZ4\x0d\x2a\x9b\xc5" #define IOSTREAM_LZ4_MAGIC_LEN (sizeof(IOSTREAM_LZ4_MAGIC)-1) struct iostream_lz4_header { unsigned char magic[IOSTREAM_LZ4_MAGIC_LEN]; /* OSTREAM_LZ4_CHUNK_SIZE in big-endian */ unsigned char max_uncompressed_chunk_size[4]; }; /* How large chunks we're buffering into memory before compressing them */ #define OSTREAM_LZ4_CHUNK_SIZE (1024*64) /* How large chunks we allow in input data before returning a failure. This must be at least OSTREAM_LZ4_CHUNK_SIZE, but for future compatibility should be somewhat higher (but not too high to avoid wasting memory for corrupted files). */ #define ISTREAM_LZ4_CHUNK_SIZE (1024*1024) #define IOSTREAM_LZ4_CHUNK_PREFIX_LEN 4 /* big-endian size of chunk */ #endif dovecot-2.2.33.2/src/lib-compression/istream-lz4.c0000644000175000017500000002251213165463624016554 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_LZ4 #include "buffer.h" #include "istream-private.h" #include "istream-zlib.h" #include "iostream-lz4.h" #include struct lz4_istream { struct istream_private istream; uoff_t stream_size; struct stat last_parent_statbuf; buffer_t *chunk_buf; uint32_t chunk_size, chunk_left, max_uncompressed_chunk_size; unsigned int log_errors:1; unsigned int marked:1; unsigned int header_read:1; }; static void i_stream_lz4_close(struct iostream_private *stream, bool close_parent) { struct lz4_istream *zstream = (struct lz4_istream *)stream; if (zstream->chunk_buf != NULL) buffer_free(&zstream->chunk_buf); if (close_parent) i_stream_close(zstream->istream.parent); } static void lz4_read_error(struct lz4_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "lz4.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, zstream->istream.abs_start_offset + zstream->istream.istream.v_offset); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); } static int i_stream_lz4_read_header(struct lz4_istream *zstream) { const struct iostream_lz4_header *hdr; const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(zstream->istream.parent, &data, &size, sizeof(*hdr)-1); if (ret < 0) { zstream->istream.istream.stream_errno = zstream->istream.parent->stream_errno; return ret; } if (ret == 0 && !zstream->istream.istream.eof) return 0; hdr = (const void *)data; if (ret == 0 || memcmp(hdr->magic, IOSTREAM_LZ4_MAGIC, IOSTREAM_LZ4_MAGIC_LEN) != 0) { lz4_read_error(zstream, "wrong magic in header (not lz4 file?)"); zstream->istream.istream.stream_errno = EINVAL; return -1; } zstream->max_uncompressed_chunk_size = be32_to_cpu_unaligned(hdr->max_uncompressed_chunk_size); if (zstream->max_uncompressed_chunk_size > ISTREAM_LZ4_CHUNK_SIZE) { lz4_read_error(zstream, t_strdup_printf( "lz4 max chunk size too large (%u > %u)", zstream->max_uncompressed_chunk_size, ISTREAM_LZ4_CHUNK_SIZE)); zstream->istream.istream.stream_errno = EINVAL; return -1; } i_stream_skip(zstream->istream.parent, sizeof(*hdr)); return 1; } static ssize_t i_stream_lz4_read(struct istream_private *stream) { struct lz4_istream *zstream = (struct lz4_istream *)stream; const unsigned char *data; size_t size, max_size; int ret; if (!zstream->header_read) { if ((ret = i_stream_lz4_read_header(zstream)) <= 0) return ret; zstream->header_read = TRUE; } if (zstream->chunk_left == 0) { ret = i_stream_read_data(stream->parent, &data, &size, IOSTREAM_LZ4_CHUNK_PREFIX_LEN); if (ret < 0) { stream->istream.stream_errno = stream->parent->stream_errno; if (stream->istream.stream_errno == 0) { stream->istream.eof = TRUE; zstream->stream_size = stream->istream.v_offset + stream->pos - stream->skip; } return ret; } if (ret == 0 && !stream->istream.eof) return 0; zstream->chunk_size = zstream->chunk_left = be32_to_cpu_unaligned(data); if (zstream->chunk_size == 0 || zstream->chunk_size > ISTREAM_LZ4_CHUNK_SIZE) { lz4_read_error(zstream, t_strdup_printf( "invalid lz4 chunk size: %u", zstream->chunk_size)); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, IOSTREAM_LZ4_CHUNK_PREFIX_LEN); buffer_set_used_size(zstream->chunk_buf, 0); } /* read the whole compressed chunk into memory */ while (zstream->chunk_left > 0 && (ret = i_stream_read_data(zstream->istream.parent, &data, &size, 0)) > 0) { if (size > zstream->chunk_left) size = zstream->chunk_left; buffer_append(zstream->chunk_buf, data, size); i_stream_skip(zstream->istream.parent, size); zstream->chunk_left -= size; } if (zstream->chunk_left > 0) { if (ret == -1 && zstream->istream.parent->stream_errno == 0) { lz4_read_error(zstream, "truncated lz4 chunk"); stream->istream.stream_errno = EINVAL; return -1; } zstream->istream.istream.stream_errno = zstream->istream.parent->stream_errno; return ret; } /* if we already have max_buffer_size amount of data, fail here */ i_stream_compress(stream); if (stream->pos >= i_stream_get_max_buffer_size(&stream->istream)) return -2; /* allocate enough space for the old data and the new decompressed chunk. we don't know the original compressed size, so just allocate the max amount of memory. */ max_size = stream->pos + zstream->max_uncompressed_chunk_size; if (stream->buffer_size < max_size) { stream->w_buffer = i_realloc(stream->w_buffer, stream->buffer_size, max_size); stream->buffer_size = max_size; stream->buffer = stream->w_buffer; } ret = LZ4_decompress_safe(zstream->chunk_buf->data, (void *)(stream->w_buffer + stream->pos), zstream->chunk_buf->used, stream->buffer_size - stream->pos); i_assert(ret <= (int)zstream->max_uncompressed_chunk_size); if (ret < 0) { lz4_read_error(zstream, "corrupted lz4 chunk"); stream->istream.stream_errno = EINVAL; return -1; } i_assert(ret > 0); stream->pos += ret; i_assert(stream->pos <= stream->buffer_size); return ret; } static void i_stream_lz4_reset(struct lz4_istream *zstream) { struct istream_private *stream = &zstream->istream; i_stream_seek(stream->parent, stream->parent_start_offset); zstream->header_read = FALSE; zstream->chunk_size = zstream->chunk_left = 0; stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; } static void i_stream_lz4_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct lz4_istream *zstream = (struct lz4_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; if (v_offset < start_offset) { /* have to seek backwards */ i_stream_lz4_reset(zstream); start_offset = 0; } if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; stream->istream.v_offset = v_offset; stream->pos = stream->skip; } else { /* read and cache forward */ ssize_t ret; do { size_t avail = stream->pos - stream->skip; if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); ret = -1; break; } i_stream_skip(&stream->istream, avail); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); if (stream->istream.v_offset != v_offset) { /* some failure, we've broken it */ if (stream->istream.stream_errno != 0) { i_error("lz4_istream.seek(%s) failed: %s", i_stream_get_name(&stream->istream), strerror(stream->istream.stream_errno)); i_stream_close(&stream->istream); } else { /* unexpected EOF. allow it since we may just want to check if there's anything.. */ i_assert(stream->istream.eof); } } } if (mark) zstream->marked = TRUE; } static int i_stream_lz4_stat(struct istream_private *stream, bool exact) { struct lz4_istream *zstream = (struct lz4_istream *) stream; const struct stat *st; size_t size; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; /* when exact=FALSE always return the parent stat's size, even if we know the exact value. this is necessary because otherwise e.g. mbox code can see two different values and think that a compressed mbox file keeps changing. */ if (!exact) return 0; if (zstream->stream_size == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; ssize_t ret; do { size = i_stream_get_data_size(&stream->istream); i_stream_skip(&stream->istream, size); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); i_stream_seek(&stream->istream, old_offset); if (zstream->stream_size == (uoff_t)-1) return -1; } stream->statbuf.st_size = zstream->stream_size; return 0; } static void i_stream_lz4_sync(struct istream_private *stream) { struct lz4_istream *zstream = (struct lz4_istream *) stream; const struct stat *st; if (i_stream_stat(stream->parent, FALSE, &st) < 0) { if (memcmp(&zstream->last_parent_statbuf, st, sizeof(*st)) == 0) { /* a compressed file doesn't change unexpectedly, don't clear our caches unnecessarily */ return; } zstream->last_parent_statbuf = *st; } i_stream_lz4_reset(zstream); } struct istream *i_stream_create_lz4(struct istream *input, bool log_errors) { struct lz4_istream *zstream; zstream = i_new(struct lz4_istream, 1); zstream->stream_size = (uoff_t)-1; zstream->log_errors = log_errors; zstream->istream.iostream.close = i_stream_lz4_close; zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; zstream->istream.read = i_stream_lz4_read; zstream->istream.seek = i_stream_lz4_seek; zstream->istream.stat = i_stream_lz4_stat; zstream->istream.sync = i_stream_lz4_sync; zstream->istream.istream.readable_fd = FALSE; zstream->istream.istream.blocking = input->blocking; zstream->istream.istream.seekable = input->seekable; zstream->chunk_buf = buffer_create_dynamic(default_pool, 1024); return i_stream_create(&zstream->istream, input, i_stream_get_fd(input)); } #endif dovecot-2.2.33.2/src/lib-compression/ostream-lzma.c0000644000175000017500000001303013165463624017007 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_LZMA #include "ostream-private.h" #include "ostream-zlib.h" #include #define CHUNK_SIZE (1024*64) struct lzma_ostream { struct ostream_private ostream; lzma_stream strm; unsigned char outbuf[CHUNK_SIZE]; unsigned int outbuf_offset, outbuf_used; unsigned int flushed:1; }; static void o_stream_lzma_close(struct iostream_private *stream, bool close_parent) { struct lzma_ostream *zstream = (struct lzma_ostream *)stream; (void)o_stream_flush(&zstream->ostream.ostream); lzma_end(&zstream->strm); if (close_parent) o_stream_close(zstream->ostream.parent); } static int o_stream_zlib_send_outbuf(struct lzma_ostream *zstream) { ssize_t ret; size_t size; if (zstream->outbuf_used == 0) return 1; size = zstream->outbuf_used - zstream->outbuf_offset; i_assert(size > 0); ret = o_stream_send(zstream->ostream.parent, zstream->outbuf + zstream->outbuf_offset, size); if (ret < 0) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } if ((size_t)ret != size) { zstream->outbuf_offset += ret; return 0; } zstream->outbuf_offset = 0; zstream->outbuf_used = 0; return 1; } static ssize_t o_stream_lzma_send_chunk(struct lzma_ostream *zstream, const void *data, size_t size) { lzma_stream *zs = &zstream->strm; int ret; i_assert(zstream->outbuf_used == 0); zs->next_in = (void *)data; zs->avail_in = size; while (zs->avail_in > 0) { if (zs->avail_out == 0) { /* previous block was compressed. send it and start compression for a new block. */ zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = sizeof(zstream->outbuf); if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) return -1; if (ret == 0) { /* parent stream's buffer full */ break; } } ret = lzma_code(zs, LZMA_RUN); switch (ret) { case LZMA_OK: break; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma.write(%s): Out of memory", o_stream_get_name(&zstream->ostream.ostream)); default: i_panic("lzma.write(%s) failed with unexpected code %d", o_stream_get_name(&zstream->ostream.ostream), ret); } } size -= zs->avail_in; zstream->flushed = FALSE; return size; } static int o_stream_lzma_send_flush(struct lzma_ostream *zstream) { lzma_stream *zs = &zstream->strm; size_t len; bool done = FALSE; int ret; if (zs->avail_in != 0) { i_assert(zstream->ostream.ostream.last_failed_errno != 0); zstream->ostream.ostream.stream_errno = zstream->ostream.ostream.last_failed_errno; return -1; } if (zstream->flushed) return 0; if ((ret = o_stream_flush_parent_if_needed(&zstream->ostream)) <= 0) return ret; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; i_assert(zstream->outbuf_used == 0); do { ret = lzma_code(zs, LZMA_FINISH); switch (ret) { case LZMA_OK: break; case LZMA_STREAM_END: done = TRUE; break; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma.write(%s): Out of memory", o_stream_get_name(&zstream->ostream.ostream)); default: i_panic("lzma.write(%s) flush failed with unexpected code %d", o_stream_get_name(&zstream->ostream.ostream), ret); } if (zs->avail_out == 0 || done) { len = sizeof(zstream->outbuf) - zs->avail_out; zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = len; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; } } while (!done); zstream->flushed = TRUE; return 0; } static int o_stream_lzma_flush(struct ostream_private *stream) { struct lzma_ostream *zstream = (struct lzma_ostream *)stream; int ret; if (o_stream_lzma_send_flush(zstream) < 0) return -1; ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); return ret; } static ssize_t o_stream_lzma_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct lzma_ostream *zstream = (struct lzma_ostream *)stream; ssize_t ret, bytes = 0; unsigned int i; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ return ret; } for (i = 0; i < iov_count; i++) { ret = o_stream_lzma_send_chunk(zstream, iov[i].iov_base, iov[i].iov_len); if (ret < 0) return -1; bytes += ret; if ((size_t)ret != iov[i].iov_len) break; } stream->ostream.offset += bytes; /* avail_in!=0 check is used to detect errors. if it's non-zero here it simply means we didn't send all the data */ zstream->strm.avail_in = 0; return bytes; } struct ostream *o_stream_create_lzma(struct ostream *output, int level) { struct lzma_ostream *zstream; lzma_ret ret; i_assert(level >= 1 && level <= 9); zstream = i_new(struct lzma_ostream, 1); zstream->ostream.sendv = o_stream_lzma_sendv; zstream->ostream.flush = o_stream_lzma_flush; zstream->ostream.iostream.close = o_stream_lzma_close; ret = lzma_easy_encoder(&zstream->strm, level, LZMA_CHECK_CRC64); switch (ret) { case LZMA_OK: break; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory"); case LZMA_OPTIONS_ERROR: i_fatal("lzma: Invalid level"); default: i_fatal("lzma_easy_encoder() failed with %d", ret); } zstream->strm.next_out = zstream->outbuf; zstream->strm.avail_out = sizeof(zstream->outbuf); return o_stream_create(&zstream->ostream, output, o_stream_get_fd(output)); } #endif dovecot-2.2.33.2/src/lib-compression/istream-bzlib.c0000644000175000017500000002223613165463624017150 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_BZLIB #include "istream-private.h" #include "istream-zlib.h" #include #define CHUNK_SIZE (1024*64) struct bzlib_istream { struct istream_private istream; bz_stream zs; uoff_t eof_offset, stream_size; size_t high_pos; struct stat last_parent_statbuf; unsigned int log_errors:1; unsigned int marked:1; unsigned int zs_closed:1; }; static void i_stream_bzlib_close(struct iostream_private *stream, bool close_parent) { struct bzlib_istream *zstream = (struct bzlib_istream *)stream; if (!zstream->zs_closed) { (void)BZ2_bzDecompressEnd(&zstream->zs); zstream->zs_closed = TRUE; } if (close_parent) i_stream_close(zstream->istream.parent); } static void bzlib_read_error(struct bzlib_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "bzlib.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, zstream->istream.abs_start_offset + zstream->istream.istream.v_offset); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); } static ssize_t i_stream_bzlib_read(struct istream_private *stream) { struct bzlib_istream *zstream = (struct bzlib_istream *)stream; const unsigned char *data; uoff_t high_offset; size_t size, out_size; int ret; high_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (zstream->eof_offset == high_offset) { i_assert(zstream->high_pos == 0 || zstream->high_pos == stream->pos); stream->istream.eof = TRUE; return -1; } if (stream->pos < zstream->high_pos) { /* we're here because we seeked back within the read buffer. */ ret = zstream->high_pos - stream->pos; stream->pos = zstream->high_pos; zstream->high_pos = 0; if (zstream->eof_offset != (uoff_t)-1) { high_offset = stream->istream.v_offset + (stream->pos - stream->skip); i_assert(zstream->eof_offset == high_offset); stream->istream.eof = TRUE; } return ret; } zstream->high_pos = 0; if (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_size) return -2; /* buffer full */ } } if (i_stream_read_data(stream->parent, &data, &size, 0) < 0) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else { i_assert(stream->parent->eof); bzlib_read_error(zstream, "unexpected EOF"); stream->istream.stream_errno = EINVAL; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->zs.next_in = (char *)data; zstream->zs.avail_in = size; out_size = stream->buffer_size - stream->pos; zstream->zs.next_out = (char *)stream->w_buffer + stream->pos; zstream->zs.avail_out = out_size; ret = BZ2_bzDecompress(&zstream->zs); out_size -= zstream->zs.avail_out; stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->zs.avail_in); switch (ret) { case BZ_OK: break; case BZ_PARAM_ERROR: i_unreached(); case BZ_DATA_ERROR: bzlib_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case BZ_DATA_ERROR_MAGIC: bzlib_read_error(zstream, "wrong magic in header (not bz2 file?)"); stream->istream.stream_errno = EINVAL; return -1; case BZ_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case BZ_STREAM_END: zstream->eof_offset = stream->istream.v_offset + (stream->pos - stream->skip); zstream->stream_size = zstream->eof_offset; if (out_size == 0) { stream->istream.eof = TRUE; return -1; } break; default: i_fatal("BZ2_bzDecompress() failed with %d", ret); } if (out_size == 0) { /* read more input */ return i_stream_bzlib_read(stream); } return out_size; } static void i_stream_bzlib_init(struct bzlib_istream *zstream) { int ret; ret = BZ2_bzDecompressInit(&zstream->zs, 0, 0); switch (ret) { case BZ_OK: break; case BZ_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); case BZ_CONFIG_ERROR: i_fatal("Wrong bzlib library version (broken compilation)"); case BZ_PARAM_ERROR: i_fatal("bzlib: Invalid parameters"); default: i_fatal("BZ2_bzDecompressInit() failed with %d", ret); } } static void i_stream_bzlib_reset(struct bzlib_istream *zstream) { struct istream_private *stream = &zstream->istream; i_stream_seek(stream->parent, stream->parent_start_offset); zstream->eof_offset = (uoff_t)-1; zstream->zs.next_in = NULL; zstream->zs.avail_in = 0; stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; zstream->high_pos = 0; (void)BZ2_bzDecompressEnd(&zstream->zs); i_stream_bzlib_init(zstream); } static void i_stream_bzlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct bzlib_istream *zstream = (struct bzlib_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; if (v_offset < start_offset) { /* have to seek backwards */ i_stream_bzlib_reset(zstream); start_offset = 0; } else if (zstream->high_pos != 0) { stream->pos = zstream->high_pos; zstream->high_pos = 0; } if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; stream->istream.v_offset = v_offset; zstream->high_pos = stream->pos; stream->pos = stream->skip; } else { /* read and cache forward */ ssize_t ret; do { size_t avail = stream->pos - stream->skip; if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); ret = -1; break; } i_stream_skip(&stream->istream, avail); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); if (stream->istream.v_offset != v_offset) { /* some failure, we've broken it */ if (stream->istream.stream_errno != 0) { i_error("bzlib_istream.seek(%s) failed: %s", i_stream_get_name(&stream->istream), strerror(stream->istream.stream_errno)); i_stream_close(&stream->istream); } else { /* unexpected EOF. allow it since we may just want to check if there's anything.. */ i_assert(stream->istream.eof); } } } if (mark) zstream->marked = TRUE; } static int i_stream_bzlib_stat(struct istream_private *stream, bool exact) { struct bzlib_istream *zstream = (struct bzlib_istream *) stream; const struct stat *st; size_t size; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; /* when exact=FALSE always return the parent stat's size, even if we know the exact value. this is necessary because otherwise e.g. mbox code can see two different values and think that a compressed mbox file keeps changing. */ if (!exact) return 0; if (zstream->stream_size == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; ssize_t ret; do { size = i_stream_get_data_size(&stream->istream); i_stream_skip(&stream->istream, size); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); i_stream_seek(&stream->istream, old_offset); if (zstream->stream_size == (uoff_t)-1) return -1; } stream->statbuf.st_size = zstream->stream_size; return 0; } static void i_stream_bzlib_sync(struct istream_private *stream) { struct bzlib_istream *zstream = (struct bzlib_istream *) stream; const struct stat *st; if (i_stream_stat(stream->parent, FALSE, &st) < 0) { if (memcmp(&zstream->last_parent_statbuf, st, sizeof(*st)) == 0) { /* a compressed file doesn't change unexpectedly, don't clear our caches unnecessarily */ return; } zstream->last_parent_statbuf = *st; } i_stream_bzlib_reset(zstream); } struct istream *i_stream_create_bz2(struct istream *input, bool log_errors) { struct bzlib_istream *zstream; zstream = i_new(struct bzlib_istream, 1); zstream->eof_offset = (uoff_t)-1; zstream->stream_size = (uoff_t)-1; zstream->log_errors = log_errors; i_stream_bzlib_init(zstream); zstream->istream.iostream.close = i_stream_bzlib_close; zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; zstream->istream.read = i_stream_bzlib_read; zstream->istream.seek = i_stream_bzlib_seek; zstream->istream.stat = i_stream_bzlib_stat; zstream->istream.sync = i_stream_bzlib_sync; zstream->istream.istream.readable_fd = FALSE; zstream->istream.istream.blocking = input->blocking; zstream->istream.istream.seekable = input->seekable; return i_stream_create(&zstream->istream, input, i_stream_get_fd(input)); } #endif dovecot-2.2.33.2/src/lib-compression/istream-zlib.h0000644000175000017500000000067413123174404017002 00000000000000#ifndef ISTREAM_ZLIB_H #define ISTREAM_ZLIB_H struct istream *i_stream_create_gz(struct istream *input, bool log_errors); struct istream *i_stream_create_deflate(struct istream *input, bool log_errors); struct istream *i_stream_create_bz2(struct istream *input, bool log_errors); struct istream *i_stream_create_lzma(struct istream *input, bool log_errors); struct istream *i_stream_create_lz4(struct istream *input, bool log_errors); #endif dovecot-2.2.33.2/src/lib-compression/ostream-lz4.c0000644000175000017500000001316013123174404016546 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_LZ4 #include "ostream-private.h" #include "ostream-zlib.h" #include "iostream-lz4.h" #include #define CHUNK_SIZE OSTREAM_LZ4_CHUNK_SIZE struct lz4_ostream { struct ostream_private ostream; unsigned char compressbuf[CHUNK_SIZE]; unsigned int compressbuf_offset; /* chunk size, followed by compressed data */ unsigned char outbuf[IOSTREAM_LZ4_CHUNK_PREFIX_LEN + LZ4_COMPRESSBOUND(CHUNK_SIZE)]; unsigned int outbuf_offset, outbuf_used; }; static void o_stream_lz4_close(struct iostream_private *stream, bool close_parent) { struct lz4_ostream *zstream = (struct lz4_ostream *)stream; (void)o_stream_flush(&zstream->ostream.ostream); if (close_parent) o_stream_close(zstream->ostream.parent); } static int o_stream_lz4_send_outbuf(struct lz4_ostream *zstream) { ssize_t ret; size_t size; if (zstream->outbuf_used == 0) return 1; size = zstream->outbuf_used - zstream->outbuf_offset; i_assert(size > 0); ret = o_stream_send(zstream->ostream.parent, zstream->outbuf + zstream->outbuf_offset, size); if (ret < 0) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } if ((size_t)ret != size) { zstream->outbuf_offset += ret; return 0; } zstream->outbuf_offset = 0; zstream->outbuf_used = 0; return 1; } static int o_stream_lz4_compress(struct lz4_ostream *zstream) { uint32_t chunk_size; int ret; if (zstream->compressbuf_offset == 0) return 1; if ((ret = o_stream_lz4_send_outbuf(zstream)) <= 0) return ret; i_assert(zstream->outbuf_offset == 0); i_assert(zstream->outbuf_used == 0); #if defined(HAVE_LZ4_COMPRESS_DEFAULT) int max_dest_size = LZ4_compressBound(zstream->compressbuf_offset); i_assert(max_dest_size >= 0); if (max_dest_size == 0) { io_stream_set_error(&zstream->ostream.iostream, "lz4-compress: input size %u too large (> %u)", zstream->compressbuf_offset, LZ4_MAX_INPUT_SIZE); zstream->ostream.ostream.stream_errno = EINVAL; return -1; } ret = LZ4_compress_default((void *)zstream->compressbuf, (void *)(zstream->outbuf + IOSTREAM_LZ4_CHUNK_PREFIX_LEN), zstream->compressbuf_offset, max_dest_size); #else ret = LZ4_compress((void *)zstream->compressbuf, (void *)(zstream->outbuf + IOSTREAM_LZ4_CHUNK_PREFIX_LEN), zstream->compressbuf_offset); #endif /* defined(HAVE_LZ4_COMPRESS_DEFAULT) */ i_assert(ret > 0 && (unsigned int)ret <= sizeof(zstream->outbuf) - IOSTREAM_LZ4_CHUNK_PREFIX_LEN); zstream->outbuf_used = IOSTREAM_LZ4_CHUNK_PREFIX_LEN + ret; chunk_size = zstream->outbuf_used - IOSTREAM_LZ4_CHUNK_PREFIX_LEN; zstream->outbuf[0] = (chunk_size & 0xff000000) >> 24; zstream->outbuf[1] = (chunk_size & 0x00ff0000) >> 16; zstream->outbuf[2] = (chunk_size & 0x0000ff00) >> 8; zstream->outbuf[3] = (chunk_size & 0x000000ff); zstream->compressbuf_offset = 0; return 1; } static ssize_t o_stream_lz4_send_chunk(struct lz4_ostream *zstream, const void *data, size_t size) { size_t max_size; ssize_t added_bytes = 0; int ret; i_assert(zstream->outbuf_used == 0); do { max_size = I_MIN(size, sizeof(zstream->compressbuf) - zstream->compressbuf_offset); memcpy(zstream->compressbuf + zstream->compressbuf_offset, data, max_size); zstream->compressbuf_offset += max_size; data = CONST_PTR_OFFSET(data, max_size); size -= max_size; added_bytes += max_size; if (zstream->compressbuf_offset == sizeof(zstream->compressbuf)) { ret = o_stream_lz4_compress(zstream); if (ret <= 0) return added_bytes != 0 ? added_bytes : ret; } } while (size > 0); return added_bytes; } static int o_stream_lz4_flush(struct ostream_private *stream) { struct lz4_ostream *zstream = (struct lz4_ostream *)stream; int ret; if (o_stream_lz4_compress(zstream) < 0) return -1; if (o_stream_lz4_send_outbuf(zstream) < 0) return -1; ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); return ret; } static ssize_t o_stream_lz4_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct lz4_ostream *zstream = (struct lz4_ostream *)stream; ssize_t ret, bytes = 0; unsigned int i; if ((ret = o_stream_lz4_send_outbuf(zstream)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ return ret; } for (i = 0; i < iov_count; i++) { ret = o_stream_lz4_send_chunk(zstream, iov[i].iov_base, iov[i].iov_len); if (ret < 0) return -1; bytes += ret; if ((size_t)ret != iov[i].iov_len) break; } stream->ostream.offset += bytes; return bytes; } struct ostream *o_stream_create_lz4(struct ostream *output, int level) { struct iostream_lz4_header *hdr; struct lz4_ostream *zstream; i_assert(level >= 1 && level <= 9); zstream = i_new(struct lz4_ostream, 1); zstream->ostream.sendv = o_stream_lz4_sendv; zstream->ostream.flush = o_stream_lz4_flush; zstream->ostream.iostream.close = o_stream_lz4_close; i_assert(sizeof(zstream->outbuf) >= sizeof(*hdr)); hdr = (void *)zstream->outbuf; memcpy(hdr->magic, IOSTREAM_LZ4_MAGIC, sizeof(hdr->magic)); hdr->max_uncompressed_chunk_size[0] = (OSTREAM_LZ4_CHUNK_SIZE & 0xff000000) >> 24; hdr->max_uncompressed_chunk_size[1] = (OSTREAM_LZ4_CHUNK_SIZE & 0x00ff0000) >> 16; hdr->max_uncompressed_chunk_size[2] = (OSTREAM_LZ4_CHUNK_SIZE & 0x0000ff00) >> 8; hdr->max_uncompressed_chunk_size[3] = (OSTREAM_LZ4_CHUNK_SIZE & 0x000000ff); zstream->outbuf_used = sizeof(*hdr); return o_stream_create(&zstream->ostream, output, o_stream_get_fd(output)); } #endif dovecot-2.2.33.2/src/lib-compression/ostream-zlib.h0000644000175000017500000000064313123174404017004 00000000000000#ifndef OSTREAM_ZLIB_H #define OSTREAM_ZLIB_H struct ostream *o_stream_create_gz(struct ostream *output, int level); struct ostream *o_stream_create_deflate(struct ostream *output, int level); struct ostream *o_stream_create_bz2(struct ostream *output, int level); struct ostream *o_stream_create_lzma(struct ostream *output, int level); struct ostream *o_stream_create_lz4(struct ostream *output, int level); #endif dovecot-2.2.33.2/src/lib-compression/istream-zlib.c0000644000175000017500000003342613165463624017011 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_ZLIB #include "crc32.h" #include "istream-private.h" #include "istream-zlib.h" #include #define CHUNK_SIZE (1024*64) #define GZ_HEADER_MIN_SIZE 10 #define GZ_TRAILER_SIZE 8 #define GZ_MAGIC1 0x1f #define GZ_MAGIC2 0x8b #define GZ_FLAG_FHCRC 0x02 #define GZ_FLAG_FEXTRA 0x04 #define GZ_FLAG_FNAME 0x08 #define GZ_FLAG_FCOMMENT 0x10 struct zlib_istream { struct istream_private istream; z_stream zs; uoff_t eof_offset, stream_size; size_t prev_size, high_pos; uint32_t crc32; struct stat last_parent_statbuf; unsigned int gz:1; unsigned int log_errors:1; unsigned int marked:1; unsigned int header_read:1; unsigned int trailer_read:1; unsigned int zs_closed:1; unsigned int starting_concated_output:1; }; static void i_stream_zlib_init(struct zlib_istream *zstream); static void i_stream_zlib_close(struct iostream_private *stream, bool close_parent) { struct zlib_istream *zstream = (struct zlib_istream *)stream; if (!zstream->zs_closed) { (void)inflateEnd(&zstream->zs); zstream->zs_closed = TRUE; } if (close_parent) i_stream_close(zstream->istream.parent); } static void zlib_read_error(struct zlib_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "zlib.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, zstream->istream.abs_start_offset + zstream->istream.istream.v_offset); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); } static int i_stream_zlib_read_header(struct istream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; const unsigned char *data; size_t size; unsigned int pos, fextra_size; int ret; ret = i_stream_read_data(stream->parent, &data, &size, zstream->prev_size); if (size == zstream->prev_size) { stream->istream.stream_errno = stream->parent->stream_errno; if (ret == -1 && stream->istream.stream_errno == 0) { zlib_read_error(zstream, "missing gz header"); stream->istream.stream_errno = EINVAL; } if (ret == -2) { zlib_read_error(zstream, "gz header is too large"); stream->istream.stream_errno = EINVAL; ret = -1; } return ret; } zstream->prev_size = size; if (size < GZ_HEADER_MIN_SIZE) return 0; pos = GZ_HEADER_MIN_SIZE; if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) { /* missing gzip magic header */ zlib_read_error(zstream, "wrong magic in header (not gz file?)"); stream->istream.stream_errno = EINVAL; return -1; } if ((data[3] & GZ_FLAG_FEXTRA) != 0) { if (pos + 2 < size) return 0; fextra_size = le16_to_cpu_unaligned(&data[pos]); pos += 2; if (pos + fextra_size < size) return 0; pos += fextra_size; } if ((data[3] & GZ_FLAG_FNAME) != 0) { do { if (pos == size) return 0; } while (data[pos++] != '\0'); } if ((data[3] & GZ_FLAG_FCOMMENT) != 0) { do { if (pos == size) return 0; } while (data[pos++] != '\0'); } if ((data[3] & GZ_FLAG_FHCRC) != 0) { if (pos + 2 < size) return 0; pos += 2; } i_stream_skip(stream->parent, pos); zstream->prev_size = 0; return 1; } static int i_stream_zlib_read_trailer(struct zlib_istream *zstream) { struct istream_private *stream = &zstream->istream; const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(stream->parent, &data, &size, GZ_TRAILER_SIZE-1); if (size == zstream->prev_size) { stream->istream.stream_errno = stream->parent->stream_errno; if (ret == -1 && stream->istream.stream_errno == 0) { zlib_read_error(zstream, "missing gz trailer"); stream->istream.stream_errno = EINVAL; } return ret; } zstream->prev_size = size; if (size < GZ_TRAILER_SIZE) return 0; if (le32_to_cpu_unaligned(data) != zstream->crc32) { zlib_read_error(zstream, "gz trailer has wrong CRC value"); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, GZ_TRAILER_SIZE); zstream->prev_size = 0; zstream->trailer_read = TRUE; return 1; } static ssize_t i_stream_zlib_read(struct istream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; const unsigned char *data; uoff_t high_offset; size_t size, out_size; int ret; high_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (zstream->eof_offset == high_offset) { i_assert(zstream->high_pos == 0 || zstream->high_pos == stream->pos); if (!zstream->trailer_read) { do { ret = i_stream_zlib_read_trailer(zstream); } while (ret == 0 && stream->istream.blocking); if (ret <= 0) return ret; } if (!zstream->gz || i_stream_is_eof(stream->parent)) { stream->istream.eof = TRUE; return -1; } zstream->starting_concated_output = TRUE; } if (zstream->starting_concated_output) { /* make sure there actually is something in parent stream. we don't want to reset the stream unless we actually see some concated output. */ ret = i_stream_read_more(stream->parent, &data, &size); if (ret <= 0) { if (ret == 0) return 0; if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } stream->istream.eof = TRUE; return -1; } /* gzip file with concatenated content */ zstream->eof_offset = (uoff_t)-1; zstream->stream_size = (uoff_t)-1; zstream->header_read = FALSE; zstream->trailer_read = FALSE; zstream->crc32 = 0; zstream->starting_concated_output = FALSE; (void)inflateEnd(&zstream->zs); i_stream_zlib_init(zstream); } if (!zstream->header_read) { do { ret = i_stream_zlib_read_header(stream); } while (ret == 0 && stream->istream.blocking); if (ret <= 0) return ret; zstream->header_read = TRUE; } if (stream->pos < zstream->high_pos) { /* we're here because we seeked back within the read buffer. */ ret = zstream->high_pos - stream->pos; stream->pos = zstream->high_pos; zstream->high_pos = 0; if (zstream->trailer_read) { high_offset = stream->istream.v_offset + (stream->pos - stream->skip); i_assert(zstream->eof_offset == high_offset); stream->istream.eof = TRUE; } return ret; } zstream->high_pos = 0; if (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_size) return -2; /* buffer full */ } } if (i_stream_read_data(stream->parent, &data, &size, 0) < 0) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else { i_assert(stream->parent->eof); zlib_read_error(zstream, "unexpected EOF"); stream->istream.stream_errno = EPIPE; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->zs.next_in = (void *)data; zstream->zs.avail_in = size; out_size = stream->buffer_size - stream->pos; zstream->zs.next_out = stream->w_buffer + stream->pos; zstream->zs.avail_out = out_size; ret = inflate(&zstream->zs, Z_SYNC_FLUSH); out_size -= zstream->zs.avail_out; zstream->crc32 = crc32_data_more(zstream->crc32, stream->w_buffer + stream->pos, out_size); stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->zs.avail_in); switch (ret) { case Z_OK: break; case Z_NEED_DICT: zlib_read_error(zstream, "can't read file without dict"); stream->istream.stream_errno = EIO; return -1; case Z_DATA_ERROR: zlib_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case Z_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case Z_STREAM_END: zstream->eof_offset = stream->istream.v_offset + (stream->pos - stream->skip); zstream->stream_size = zstream->eof_offset; zstream->zs.avail_in = 0; if (!zstream->trailer_read) { /* try to read and verify the trailer, we might not be called again. */ if (i_stream_zlib_read_trailer(zstream) < 0) return -1; } break; default: i_fatal("inflate() failed with %d", ret); } if (out_size == 0) { /* read more input */ return i_stream_zlib_read(stream); } return out_size; } static void i_stream_zlib_init(struct zlib_istream *zstream) { int ret; ret = inflateInit2(&zstream->zs, -15); switch (ret) { case Z_OK: break; case Z_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory"); case Z_VERSION_ERROR: i_fatal("Wrong zlib library version (broken compilation)"); case Z_STREAM_ERROR: i_fatal("zlib: Invalid parameters"); default: i_fatal("inflateInit() failed with %d", ret); } zstream->header_read = !zstream->gz; zstream->trailer_read = !zstream->gz; } static void i_stream_zlib_reset(struct zlib_istream *zstream) { struct istream_private *stream = &zstream->istream; i_stream_seek(stream->parent, stream->parent_start_offset); zstream->eof_offset = (uoff_t)-1; zstream->crc32 = 0; zstream->zs.next_in = NULL; zstream->zs.avail_in = 0; stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; zstream->high_pos = 0; zstream->prev_size = 0; (void)inflateEnd(&zstream->zs); i_stream_zlib_init(zstream); } static void i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct zlib_istream *zstream = (struct zlib_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; if (v_offset < start_offset) { /* have to seek backwards */ i_stream_zlib_reset(zstream); start_offset = 0; } else if (zstream->high_pos != 0) { stream->pos = zstream->high_pos; zstream->high_pos = 0; } if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; stream->istream.v_offset = v_offset; zstream->high_pos = stream->pos; stream->pos = stream->skip; } else { /* read and cache forward */ ssize_t ret; do { size_t avail = stream->pos - stream->skip; if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); ret = -1; break; } i_stream_skip(&stream->istream, avail); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); if (stream->istream.v_offset != v_offset) { /* some failure, we've broken it */ if (stream->istream.stream_errno != 0) { i_error("zlib_istream.seek(%s) failed: %s", i_stream_get_name(&stream->istream), strerror(stream->istream.stream_errno)); i_stream_close(&stream->istream); } else { /* unexpected EOF. allow it since we may just want to check if there's anything.. */ i_assert(stream->istream.eof); } } } if (mark) zstream->marked = TRUE; } static int i_stream_zlib_stat(struct istream_private *stream, bool exact) { struct zlib_istream *zstream = (struct zlib_istream *) stream; const struct stat *st; size_t size; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; /* when exact=FALSE always return the parent stat's size, even if we know the exact value. this is necessary because otherwise e.g. mbox code can see two different values and think that a compressed mbox file keeps changing. */ if (!exact) return 0; if (zstream->stream_size == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; ssize_t ret; do { size = i_stream_get_data_size(&stream->istream); i_stream_skip(&stream->istream, size); } while ((ret = i_stream_read(&stream->istream)) > 0); i_assert(ret == -1); i_stream_seek(&stream->istream, old_offset); if (zstream->stream_size == (uoff_t)-1) return -1; } stream->statbuf.st_size = zstream->stream_size; return 0; } static void i_stream_zlib_sync(struct istream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *) stream; const struct stat *st; if (i_stream_stat(stream->parent, FALSE, &st) < 0) { if (memcmp(&zstream->last_parent_statbuf, st, sizeof(*st)) == 0) { /* a compressed file doesn't change unexpectedly, don't clear our caches unnecessarily */ return; } zstream->last_parent_statbuf = *st; } i_stream_zlib_reset(zstream); } static struct istream * i_stream_create_zlib(struct istream *input, bool gz, bool log_errors) { struct zlib_istream *zstream; zstream = i_new(struct zlib_istream, 1); zstream->eof_offset = (uoff_t)-1; zstream->stream_size = (uoff_t)-1; zstream->gz = gz; zstream->log_errors = log_errors; i_stream_zlib_init(zstream); zstream->istream.iostream.close = i_stream_zlib_close; zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; zstream->istream.read = i_stream_zlib_read; zstream->istream.seek = i_stream_zlib_seek; zstream->istream.stat = i_stream_zlib_stat; zstream->istream.sync = i_stream_zlib_sync; zstream->istream.istream.readable_fd = FALSE; zstream->istream.istream.blocking = input->blocking; zstream->istream.istream.seekable = input->seekable; return i_stream_create(&zstream->istream, input, i_stream_get_fd(input)); } struct istream *i_stream_create_gz(struct istream *input, bool log_errors) { return i_stream_create_zlib(input, TRUE, log_errors); } struct istream *i_stream_create_deflate(struct istream *input, bool log_errors) { return i_stream_create_zlib(input, FALSE, log_errors); } #endif dovecot-2.2.33.2/src/lib-compression/compression.c0000644000175000017500000000721413165463624016744 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "istream-zlib.h" #include "ostream-zlib.h" #include "iostream-lz4.h" #include "compression.h" #ifndef HAVE_ZLIB # define i_stream_create_gz NULL # define o_stream_create_gz NULL # define i_stream_create_deflate NULL # define o_stream_create_deflate NULL #endif #ifndef HAVE_BZLIB # define i_stream_create_bz2 NULL # define o_stream_create_bz2 NULL #endif #ifndef HAVE_LZMA # define i_stream_create_lzma NULL # define o_stream_create_lzma NULL #endif #ifndef HAVE_LZ4 # define i_stream_create_lz4 NULL # define o_stream_create_lz4 NULL #endif static bool is_compressed_zlib(struct istream *input) { const unsigned char *data; size_t size; /* Peek in to the stream and see if it looks like it's compressed (based on its header). This also means that users can try to exploit security holes in the uncompression library by APPENDing a specially crafted mail. So let's hope zlib is free of holes. */ if (i_stream_read_data(input, &data, &size, 1) <= 0) return FALSE; i_assert(size >= 2); return data[0] == 31 && data[1] == 139; } static bool is_compressed_bzlib(struct istream *input) { const unsigned char *data; size_t size; if (i_stream_read_data(input, &data, &size, 4+6 - 1) <= 0) return FALSE; if (data[0] != 'B' || data[1] != 'Z') return FALSE; if (data[2] != 'h' && data[2] != '0') return FALSE; if (data[3] < '1' || data[3] > '9') return FALSE; return memcmp(data + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0; } static bool is_compressed_xz(struct istream *input) { const unsigned char *data; size_t size; if (i_stream_read_data(input, &data, &size, 6 - 1) <= 0) return FALSE; return memcmp(data, "\xfd\x37\x7a\x58\x5a", 6) == 0; } static bool is_compressed_lz4(struct istream *input) { const unsigned char *data; size_t size; if (i_stream_read_data(input, &data, &size, IOSTREAM_LZ4_MAGIC_LEN - 1) <= 0) return FALSE; /* there is no standard LZ4 header, so we've created our own */ return memcmp(data, IOSTREAM_LZ4_MAGIC, IOSTREAM_LZ4_MAGIC_LEN) == 0; } const struct compression_handler *compression_lookup_handler(const char *name) { unsigned int i; for (i = 0; compression_handlers[i].name != NULL; i++) { if (strcmp(name, compression_handlers[i].name) == 0) return &compression_handlers[i]; } return NULL; } const struct compression_handler * compression_detect_handler(struct istream *input) { unsigned int i; for (i = 0; compression_handlers[i].name != NULL; i++) { if (compression_handlers[i].is_compressed != NULL && compression_handlers[i].is_compressed(input)) return &compression_handlers[i]; } return NULL; } const struct compression_handler * compression_lookup_handler_from_ext(const char *path) { unsigned int i; size_t len, path_len = strlen(path); for (i = 0; compression_handlers[i].name != NULL; i++) { if (compression_handlers[i].ext == NULL) continue; len = strlen(compression_handlers[i].ext); if (path_len > len && strcmp(path + path_len - len, compression_handlers[i].ext) == 0) return &compression_handlers[i]; } return NULL; } const struct compression_handler compression_handlers[] = { { "gz", ".gz", is_compressed_zlib, i_stream_create_gz, o_stream_create_gz }, { "bz2", ".bz2", is_compressed_bzlib, i_stream_create_bz2, o_stream_create_bz2 }, { "deflate", NULL, NULL, i_stream_create_deflate, o_stream_create_deflate }, { "xz", ".xz", is_compressed_xz, i_stream_create_lzma, o_stream_create_lzma }, { "lz4", ".lz4", is_compressed_lz4, i_stream_create_lz4, o_stream_create_lz4 }, { NULL, NULL, NULL, NULL, NULL } }; dovecot-2.2.33.2/src/lib-compression/ostream-zlib.c0000644000175000017500000002023613165463624017012 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef HAVE_ZLIB #include "crc32.h" #include "ostream-private.h" #include "ostream-zlib.h" #include #define CHUNK_SIZE (1024*32) #define ZLIB_OS_CODE 0x03 /* Unix */ struct zlib_ostream { struct ostream_private ostream; z_stream zs; unsigned char gz_header[10]; unsigned char outbuf[CHUNK_SIZE]; unsigned int outbuf_offset, outbuf_used; uint32_t crc, bytes32; unsigned int gz:1; unsigned int header_sent:1; unsigned int flushed:1; }; static void o_stream_zlib_close(struct iostream_private *stream, bool close_parent) { struct zlib_ostream *zstream = (struct zlib_ostream *)stream; (void)o_stream_flush(&zstream->ostream.ostream); (void)deflateEnd(&zstream->zs); if (close_parent) o_stream_close(zstream->ostream.parent); } static int o_stream_zlib_send_gz_header(struct zlib_ostream *zstream) { ssize_t ret; ret = o_stream_send(zstream->ostream.parent, zstream->gz_header, sizeof(zstream->gz_header)); if ((size_t)ret != sizeof(zstream->gz_header)) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } zstream->header_sent = TRUE; return 0; } static int o_stream_zlib_lsb_uint32(struct ostream *output, uint32_t num) { unsigned char buf[sizeof(uint32_t)]; unsigned int i; for (i = 0; i < sizeof(buf); i++) { buf[i] = num & 0xff; num >>= 8; } if (o_stream_send(output, buf, sizeof(buf)) != sizeof(buf)) return -1; return 0; } static int o_stream_zlib_send_gz_trailer(struct zlib_ostream *zstream) { struct ostream *output = zstream->ostream.parent; if (!zstream->gz) return 0; if (o_stream_zlib_lsb_uint32(output, zstream->crc) < 0 || o_stream_zlib_lsb_uint32(output, zstream->bytes32) < 0) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } return 0; } static int o_stream_zlib_send_outbuf(struct zlib_ostream *zstream) { ssize_t ret; size_t size; if (zstream->outbuf_used == 0) return 1; size = zstream->outbuf_used - zstream->outbuf_offset; i_assert(size > 0); ret = o_stream_send(zstream->ostream.parent, zstream->outbuf + zstream->outbuf_offset, size); if (ret < 0) { o_stream_copy_error_from_parent(&zstream->ostream); return -1; } if ((size_t)ret != size) { zstream->outbuf_offset += ret; return 0; } zstream->outbuf_offset = 0; zstream->outbuf_used = 0; return 1; } static ssize_t o_stream_zlib_send_chunk(struct zlib_ostream *zstream, const void *data, size_t size) { z_stream *zs = &zstream->zs; int ret, flush; i_assert(zstream->outbuf_used == 0); flush = zstream->ostream.corked || zstream->gz ? Z_NO_FLUSH : Z_SYNC_FLUSH; if (!zstream->header_sent) { if (o_stream_zlib_send_gz_header(zstream) < 0) return -1; } zs->next_in = (void *)data; zs->avail_in = size; while (zs->avail_in > 0) { if (zs->avail_out == 0) { /* previous block was compressed. send it and start compression for a new block. */ zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = sizeof(zstream->outbuf); if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0) return -1; if (ret == 0) { /* parent stream's buffer full */ break; } } ret = deflate(zs, flush); switch (ret) { case Z_OK: case Z_BUF_ERROR: break; case Z_STREAM_ERROR: i_assert(zstream->gz); i_panic("zlib.write(%s) failed: Can't write more data to .gz after flushing", o_stream_get_name(&zstream->ostream.ostream)); default: i_panic("zlib.write(%s) failed with unexpected code %d", o_stream_get_name(&zstream->ostream.ostream), ret); } } size -= zs->avail_in; zstream->crc = crc32_data_more(zstream->crc, data, size); zstream->bytes32 += size; zstream->flushed = flush == Z_SYNC_FLUSH && zs->avail_in == 0 && zs->avail_out == sizeof(zstream->outbuf); return size; } static int o_stream_zlib_send_flush(struct zlib_ostream *zstream, bool final) { z_stream *zs = &zstream->zs; size_t len; bool done = FALSE; int ret, flush; if (zs->avail_in != 0) { i_assert(zstream->ostream.ostream.last_failed_errno != 0); zstream->ostream.ostream.stream_errno = zstream->ostream.ostream.last_failed_errno; return -1; } if (zstream->flushed) return 0; if ((ret = o_stream_flush_parent_if_needed(&zstream->ostream)) <= 0) return ret; if (!zstream->header_sent) { if (o_stream_zlib_send_gz_header(zstream) < 0) return -1; } if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; flush = !zstream->gz ? Z_SYNC_FLUSH : (final ? Z_FINISH : Z_NO_FLUSH); i_assert(zstream->outbuf_used == 0); do { len = sizeof(zstream->outbuf) - zs->avail_out; if (len != 0) { zs->next_out = zstream->outbuf; zs->avail_out = sizeof(zstream->outbuf); zstream->outbuf_used = len; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) return ret; if (done) break; } switch (deflate(zs, flush)) { case Z_OK: case Z_BUF_ERROR: break; case Z_STREAM_END: done = TRUE; break; default: i_unreached(); } } while (zs->avail_out != sizeof(zstream->outbuf)); if (final) { if (o_stream_zlib_send_gz_trailer(zstream) < 0) return -1; } if (final || flush != Z_NO_FLUSH) zstream->flushed = TRUE; return 0; } static int o_stream_zlib_flush(struct ostream_private *stream) { struct zlib_ostream *zstream = (struct zlib_ostream *)stream; int ret; if (o_stream_zlib_send_flush(zstream, TRUE) < 0) return -1; ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); return ret; } static ssize_t o_stream_zlib_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct zlib_ostream *zstream = (struct zlib_ostream *)stream; ssize_t ret, bytes = 0; unsigned int i; if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ return ret; } for (i = 0; i < iov_count; i++) { ret = o_stream_zlib_send_chunk(zstream, iov[i].iov_base, iov[i].iov_len); if (ret < 0) return -1; bytes += ret; if ((size_t)ret != iov[i].iov_len) break; } stream->ostream.offset += bytes; if (!zstream->ostream.corked && i == iov_count) { if (o_stream_zlib_send_flush(zstream, FALSE) < 0) return -1; } /* avail_in!=0 check is used to detect errors. if it's non-zero here it simply means we didn't send all the data */ zstream->zs.avail_in = 0; return bytes; } static void o_stream_zlib_init_gz_header(struct zlib_ostream *zstream, int level, int strategy) { unsigned char *hdr = zstream->gz_header; hdr[0] = 0x1f; hdr[1] = 0x8b; hdr[2] = Z_DEFLATED; hdr[8] = level == 9 ? 2 : (strategy >= Z_HUFFMAN_ONLY || (level != Z_DEFAULT_COMPRESSION && level < 2) ? 4 : 0); hdr[9] = ZLIB_OS_CODE; i_assert(sizeof(zstream->gz_header) == 10); } static struct ostream * o_stream_create_zlib(struct ostream *output, int level, bool gz) { const int strategy = Z_DEFAULT_STRATEGY; struct zlib_ostream *zstream; int ret; i_assert(level >= 1 && level <= 9); zstream = i_new(struct zlib_ostream, 1); zstream->ostream.sendv = o_stream_zlib_sendv; zstream->ostream.flush = o_stream_zlib_flush; zstream->ostream.iostream.close = o_stream_zlib_close; zstream->crc = 0; zstream->gz = gz; if (!gz) zstream->header_sent = TRUE; o_stream_zlib_init_gz_header(zstream, level, strategy); ret = deflateInit2(&zstream->zs, level, Z_DEFLATED, -15, 8, strategy); switch (ret) { case Z_OK: break; case Z_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "deflateInit(): Out of memory"); case Z_VERSION_ERROR: i_fatal("Wrong zlib library version (broken compilation)"); case Z_STREAM_ERROR: i_fatal("Invalid compression level %d", level); default: i_fatal("deflateInit() failed with %d", ret); } zstream->zs.next_out = zstream->outbuf; zstream->zs.avail_out = sizeof(zstream->outbuf); return o_stream_create(&zstream->ostream, output, o_stream_get_fd(output)); } struct ostream *o_stream_create_gz(struct ostream *output, int level) { return o_stream_create_zlib(output, level, TRUE); } struct ostream *o_stream_create_deflate(struct ostream *output, int level) { return o_stream_create_zlib(output, level, FALSE); } #endif dovecot-2.2.33.2/src/lib-compression/Makefile.am0000644000175000017500000000234013123174404016253 00000000000000noinst_LTLIBRARIES = libcompression.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libcompression_la_SOURCES = \ compression.c \ istream-lzma.c \ istream-lz4.c \ istream-zlib.c \ istream-bzlib.c \ ostream-lzma.c \ ostream-lz4.c \ ostream-zlib.c \ ostream-bzlib.c libcompression_la_LIBADD = \ $(COMPRESS_LIBS) pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ compression.h \ iostream-lz4.h \ istream-zlib.h \ ostream-zlib.h pkglib_LTLIBRARIES = libdovecot-compression.la libdovecot_compression_la_SOURCES = libdovecot_compression_la_LIBADD = libcompression.la ../lib-dovecot/libdovecot.la $(COMPRESS_LIBS) libdovecot_compression_la_DEPENDENCIES = libcompression.la ../lib-dovecot/libdovecot.la libdovecot_compression_la_LDFLAGS = -export-dynamic test_programs = \ test-compression noinst_PROGRAMS = $(test_programs) test_libs = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(test_libs) test_compression_SOURCES = test-compression.c test_compression_LDADD = $(test_libs) test_compression_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-dict-extra/0002755000175000017500000000000013172375611014014 500000000000000dovecot-2.2.33.2/src/lib-dict-extra/Makefile.in0000644000175000017500000004734413172375572016021 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-dict-extra ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libdict_extra_la_LIBADD = am_libdict_extra_la_OBJECTS = dict-fs.lo dict-register.lo libdict_extra_la_OBJECTS = $(am_libdict_extra_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdict_extra_la_SOURCES) DIST_SOURCES = $(libdict_extra_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libdict_extra.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-settings libdict_extra_la_SOURCES = \ dict-fs.c \ dict-register.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-dict-extra/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-dict-extra/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdict_extra.la: $(libdict_extra_la_OBJECTS) $(libdict_extra_la_DEPENDENCIES) $(EXTRA_libdict_extra_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libdict_extra_la_OBJECTS) $(libdict_extra_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-fs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-register.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-dict-extra/dict-fs.c0000644000175000017500000001622213165463624015436 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fs-api.h" #include "istream.h" #include "str.h" #include "dict-transaction-memory.h" #include "dict-private.h" struct fs_dict { struct dict dict; struct fs *fs; char *username; }; struct fs_dict_iterate_context { struct dict_iterate_context ctx; const char **paths; unsigned int path_idx; enum dict_iterate_flags flags; pool_t value_pool; struct fs_iter *fs_iter; bool failed; }; static int fs_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct fs_settings fs_set; struct fs *fs; struct fs_dict *dict; const char *p, *fs_driver, *fs_args; p = strchr(uri, ':'); if (p == NULL) { fs_driver = uri; fs_args = ""; } else { fs_driver = t_strdup_until(uri, p); fs_args = p+1; } i_zero(&fs_set); fs_set.username = set->username; fs_set.base_dir = set->base_dir; if (fs_init(fs_driver, fs_args, &fs_set, &fs, error_r) < 0) return -1; dict = i_new(struct fs_dict, 1); dict->dict = *driver; dict->fs = fs; dict->username = i_strdup(set->username); *dict_r = &dict->dict; return 0; } static void fs_dict_deinit(struct dict *_dict) { struct fs_dict *dict = (struct fs_dict *)_dict; fs_deinit(&dict->fs); i_free(dict->username); i_free(dict); } static const char *fs_dict_get_full_key(struct fs_dict *dict, const char *key) { if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0) return key + strlen(DICT_PATH_SHARED); else if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) { return t_strdup_printf("%s/%s", dict->username, key + strlen(DICT_PATH_PRIVATE)); } else { i_unreached(); } } static int fs_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct fs_dict *dict = (struct fs_dict *)_dict; struct fs_file *file; struct istream *input; const unsigned char *data; size_t size; string_t *str; int ret; file = fs_file_init(dict->fs, fs_dict_get_full_key(dict, key), FS_OPEN_MODE_READONLY); input = fs_read_stream(file, IO_BLOCK_SIZE); (void)i_stream_read(input); str = str_new(pool, i_stream_get_data_size(input)+1); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } i_assert(ret == -1); if (input->stream_errno == 0) { *value_r = str_c(str); ret = 1; } else { *value_r = NULL; if (input->stream_errno == ENOENT) ret = 0; } i_stream_unref(&input); fs_file_deinit(&file); return ret; } static struct dict_iterate_context * fs_dict_iterate_init(struct dict *_dict, const char *const *paths, enum dict_iterate_flags flags) { struct fs_dict *dict = (struct fs_dict *)_dict; struct fs_dict_iterate_context *iter; /* these flags are not supported for now */ i_assert((flags & DICT_ITERATE_FLAG_RECURSE) == 0); i_assert((flags & DICT_ITERATE_FLAG_EXACT_KEY) == 0); i_assert((flags & (DICT_ITERATE_FLAG_SORT_BY_KEY | DICT_ITERATE_FLAG_SORT_BY_VALUE)) == 0); iter = i_new(struct fs_dict_iterate_context, 1); iter->ctx.dict = _dict; iter->paths = p_strarray_dup(default_pool, paths); iter->flags = flags; iter->value_pool = pool_alloconly_create("iterate value pool", 128); iter->fs_iter = fs_iter_init(dict->fs, fs_dict_get_full_key(dict, paths[0]), 0); return &iter->ctx; } static bool fs_dict_iterate(struct dict_iterate_context *ctx, const char **key_r, const char **value_r) { struct fs_dict_iterate_context *iter = (struct fs_dict_iterate_context *)ctx; struct fs_dict *dict = (struct fs_dict *)ctx->dict; const char *path; int ret; *key_r = fs_iter_next(iter->fs_iter); if (*key_r == NULL) { if (fs_iter_deinit(&iter->fs_iter) < 0) { iter->failed = TRUE; return FALSE; } if (iter->paths[++iter->path_idx] == NULL) return FALSE; path = fs_dict_get_full_key(dict, iter->paths[iter->path_idx]); iter->fs_iter = fs_iter_init(dict->fs, path, 0); return fs_dict_iterate(ctx, key_r, value_r); } if ((iter->flags & DICT_ITERATE_FLAG_NO_VALUE) != 0) { *value_r = NULL; return TRUE; } p_clear(iter->value_pool); path = t_strconcat(iter->paths[iter->path_idx], *key_r, NULL); if ((ret = fs_dict_lookup(ctx->dict, iter->value_pool, path, value_r)) < 0) { /* I/O error */ iter->failed = TRUE; return FALSE; } else if (ret == 0) { /* file was just deleted, just skip to next one */ return fs_dict_iterate(ctx, key_r, value_r); } return TRUE; } static int fs_dict_iterate_deinit(struct dict_iterate_context *ctx) { struct fs_dict_iterate_context *iter = (struct fs_dict_iterate_context *)ctx; int ret; if (iter->fs_iter != NULL) { if (fs_iter_deinit(&iter->fs_iter) < 0) iter->failed = TRUE; } ret = iter->failed ? -1 : 0; pool_unref(&iter->value_pool); i_free(iter->paths); i_free(iter); return ret; } static struct dict_transaction_context * fs_dict_transaction_init(struct dict *_dict) { struct dict_transaction_memory_context *ctx; pool_t pool; pool = pool_alloconly_create("file dict transaction", 2048); ctx = p_new(pool, struct dict_transaction_memory_context, 1); dict_transaction_memory_init(ctx, _dict, pool); return &ctx->ctx; } static int fs_dict_write_changes(struct dict_transaction_memory_context *ctx) { struct fs_dict *dict = (struct fs_dict *)ctx->ctx.dict; struct fs_file *file; const struct dict_transaction_memory_change *change; const char *key; int ret = 0; array_foreach(&ctx->changes, change) { key = fs_dict_get_full_key(dict, change->key); switch (change->type) { case DICT_CHANGE_TYPE_SET: file = fs_file_init(dict->fs, key, FS_OPEN_MODE_REPLACE); if (fs_write(file, change->value.str, strlen(change->value.str)) < 0) { i_error("fs_write(%s) failed: %s", key, fs_file_last_error(file)); ret = -1; } fs_file_deinit(&file); break; case DICT_CHANGE_TYPE_UNSET: file = fs_file_init(dict->fs, key, FS_OPEN_MODE_READONLY); if (fs_delete(file) < 0) { i_error("fs_delete(%s) failed: %s", key, fs_file_last_error(file)); ret = -1; } fs_file_deinit(&file); break; case DICT_CHANGE_TYPE_APPEND: case DICT_CHANGE_TYPE_INC: i_unreached(); } if (ret < 0) return -1; } return 0; } static int fs_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async ATTR_UNUSED, dict_transaction_commit_callback_t *callback, void *context) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; int ret; if (fs_dict_write_changes(ctx) < 0) ret = -1; else ret = 1; pool_unref(&ctx->pool); if (callback != NULL) callback(ret, context); return ret; } struct dict dict_driver_fs = { .name = "fs", { .init = fs_dict_init, .deinit = fs_dict_deinit, .lookup = fs_dict_lookup, .iterate_init = fs_dict_iterate_init, .iterate = fs_dict_iterate, .iterate_deinit = fs_dict_iterate_deinit, .transaction_init = fs_dict_transaction_init, .transaction_commit = fs_dict_transaction_commit, .transaction_rollback = dict_transaction_memory_rollback, .set = dict_transaction_memory_set, .unset = dict_transaction_memory_unset, } }; dovecot-2.2.33.2/src/lib-dict-extra/dict-register.c0000644000175000017500000000152513165463547016656 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dict-private.h" static int refcount = 0; void dict_drivers_register_builtin(void) { if (refcount++ > 0) return; dict_driver_register(&dict_driver_client); dict_driver_register(&dict_driver_file); dict_driver_register(&dict_driver_fs); dict_driver_register(&dict_driver_memcached); dict_driver_register(&dict_driver_memcached_ascii); dict_driver_register(&dict_driver_redis); } void dict_drivers_unregister_builtin(void) { if (--refcount > 0) return; dict_driver_unregister(&dict_driver_client); dict_driver_unregister(&dict_driver_file); dict_driver_unregister(&dict_driver_fs); dict_driver_unregister(&dict_driver_memcached); dict_driver_unregister(&dict_driver_memcached_ascii); dict_driver_unregister(&dict_driver_redis); } dovecot-2.2.33.2/src/lib-dict-extra/Makefile.am0000644000175000017500000000044113123174404015756 00000000000000noinst_LTLIBRARIES = libdict_extra.la dict_drivers = @dict_drivers@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-settings libdict_extra_la_SOURCES = \ dict-fs.c \ dict-register.c NOPLUGIN_LDFLAGS = dovecot-2.2.33.2/src/Makefile.in0000644000175000017500000005315713172375572013210 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = lib-test lib lib-settings lib-auth lib-master \ lib-charset lib-ssl-iostream lib-dcrypt lib-dns lib-dict \ lib-sasl lib-stats lib-http lib-fs lib-mail lib-smtp lib-imap \ lib-imap-storage lib-program-client lib-oauth2 lib-dict-extra \ lib-dovecot lib-ldap lib-fts lib-imap-client lib-imap-urlauth \ lib-compression lib-index lib-storage lib-sql lib-ntlm lib-otp \ lib-lda anvil auth dict dns indexer ipc master login-common \ imap-hibernate imap-login imap imap-urlauth pop3-login pop3 \ lda lmtp log config director replication util doveadm \ ssl-params stats plugins am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @HAVE_LDAP_TRUE@LIB_LDAP = lib-ldap LIBDOVECOT_SUBDIRS = \ lib-test \ lib \ lib-settings \ lib-auth \ lib-master \ lib-charset \ lib-ssl-iostream \ lib-dcrypt \ lib-dns \ lib-dict \ lib-sasl \ lib-stats \ lib-http \ lib-fs \ lib-mail \ lib-smtp \ lib-imap \ lib-imap-storage \ lib-program-client \ lib-oauth2 SUBDIRS = \ $(LIBDOVECOT_SUBDIRS) \ lib-dict-extra \ lib-dovecot \ $(LIB_LDAP) \ lib-fts \ lib-imap-client \ lib-imap-urlauth \ lib-compression \ lib-index \ lib-storage \ lib-sql \ lib-ntlm \ lib-otp \ lib-lda \ anvil \ auth \ dict \ dns \ indexer \ ipc \ master \ login-common \ imap-hibernate \ imap-login \ imap \ imap-urlauth \ pop3-login \ pop3 \ lda \ lmtp \ log \ config \ director \ replication \ util \ doveadm \ ssl-params \ stats \ plugins all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-ldap/0002755000175000017500000000000013172375611012670 500000000000000dovecot-2.2.33.2/src/lib-ldap/Makefile.in0000644000175000017500000006124613172375573014673 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-ldap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = am_libdovecot_ldap_la_OBJECTS = ldap-client.lo ldap-connection.lo \ ldap-connection-pool.lo ldap-iterator.lo ldap-search.lo \ ldap-compare.lo ldap-entry.lo libdovecot_ldap_la_OBJECTS = $(am_libdovecot_ldap_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_ldap_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_ldap_la_SOURCES) DIST_SOURCES = $(libdovecot_ldap_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkglib_LTLIBRARIES = libdovecot-ldap.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-ssl-iostream \ $(LDAP_CFLAGS) libdovecot_ldap_la_SOURCES = \ ldap-client.c \ ldap-connection.c \ ldap-connection-pool.c \ ldap-iterator.c \ ldap-search.c \ ldap-compare.c \ ldap-entry.c libdovecot_ldap_la_DEPENDENCIES = ../lib/liblib.la libdovecot_ldap_la_LDFLAGS = -export-dynamic libdovecot_ldap_la_LIBADD = ../lib/liblib.la $(LDAP_LIBS) headers = \ ldap-client.h noinst_HEADERS = \ ldap-connection-pool.h \ ldap-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_libs = \ ../lib-test/libtest.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib/liblib.la all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-ldap/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-ldap/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-ldap.la: $(libdovecot_ldap_la_OBJECTS) $(libdovecot_ldap_la_DEPENDENCIES) $(EXTRA_libdovecot_ldap_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_ldap_la_LINK) -rpath $(pkglibdir) $(libdovecot_ldap_la_OBJECTS) $(libdovecot_ldap_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-compare.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-connection-pool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-entry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-iterator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-search.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibLTLIBRARIES install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS \ uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-ldap/ldap-entry.c0000644000175000017500000000371613123174404015031 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ldap-private.h" int ldap_entry_init(struct ldap_entry *obj, struct ldap_result *result, LDAPMessage *message) { ARRAY_TYPE(const_string) attr_names; struct berval **values; int count; BerElement *bptr; char *tmp; tmp = ldap_get_dn(result->conn->conn, message); obj->dn = p_strdup(result->pool, tmp); obj->result = result; ldap_memfree(tmp); tmp = ldap_first_attribute(result->conn->conn, message, &bptr); p_array_init(&attr_names, result->pool, 8); p_array_init(&(obj->attributes), result->pool, 8); while(tmp != NULL) { struct ldap_attribute *attr = p_new(result->pool, struct ldap_attribute, 1); attr->name = p_strdup(result->pool, tmp); array_append(&attr_names, &(attr->name), 1); values = ldap_get_values_len(result->conn->conn, message, tmp); if (values != NULL) { count = ldap_count_values_len(values); p_array_init(&(attr->values), result->pool, count); for(int i = 0; i < count; i++) { const char *ptr = p_strndup(result->pool, values[i]->bv_val, values[i]->bv_len); array_append(&(attr->values), &ptr, 1); } ldap_value_free_len(values); } array_append_zero(&(attr->values)); ldap_memfree(tmp); array_append(&(obj->attributes), attr, 1); tmp = ldap_next_attribute(result->conn->conn, message, bptr); } ber_free(bptr, 0); array_append_zero(&attr_names); obj->attr_names = array_idx(&attr_names, 0); return 0; } const char *ldap_entry_dn(const struct ldap_entry *entry) { return entry->dn; } const char *const *ldap_entry_get_attributes(const struct ldap_entry *entry) { return entry->attr_names; } const char *const *ldap_entry_get_attribute(const struct ldap_entry *entry, const char *attribute) { const struct ldap_attribute *attr; array_foreach(&(entry->attributes), attr) { if (strcasecmp(attr->name, attribute) == 0) { return array_idx(&(attr->values), 0); } } return NULL; } dovecot-2.2.33.2/src/lib-ldap/ldap-connection-pool.c0000644000175000017500000000520113123174404016765 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "ldap-private.h" #include "ldap-connection-pool.h" struct ldap_connection_pool { struct ldap_connection_list *conn_list; unsigned int conn_count; unsigned int max_connections; }; static void ldap_connection_list_remove(struct ldap_connection_pool *pool, struct ldap_connection_list *list) { DLLIST_REMOVE(&pool->conn_list, list); pool->conn_count--; ldap_connection_deinit(&list->conn); i_free(list); } static void ldap_connection_pool_shrink_to(struct ldap_connection_pool *pool, unsigned int max_count) { struct ldap_connection_list *list, *next; list = pool->conn_list; for (; list != NULL && pool->conn_count > max_count; list = next) { next = list->next; if (list->refcount == 0) ldap_connection_list_remove(pool, list); } } struct ldap_connection_pool * ldap_connection_pool_init(unsigned int max_connections) { struct ldap_connection_pool *pool; pool = i_new(struct ldap_connection_pool, 1); pool->max_connections = max_connections; return pool; } void ldap_connection_pool_deinit(struct ldap_connection_pool **_pool) { struct ldap_connection_pool *pool = *_pool; *_pool = NULL; ldap_connection_pool_shrink_to(pool, 0); i_assert(pool->conn_list == NULL); i_free(pool); } int ldap_connection_pool_get(struct ldap_connection_pool *pool, struct ldap_client *client, const struct ldap_client_settings *set, struct ldap_connection_list **list_r, const char **error_r) { struct ldap_connection_list *list; struct ldap_connection *conn; for (list = pool->conn_list; list != NULL; list = list->next) { if (ldap_connection_have_settings(list->conn, set)) { list->refcount++; *list_r = list; return 0; } } if (ldap_connection_init(client, set, &conn, error_r) < 0) return -1; list = i_new(struct ldap_connection_list, 1); list->conn = conn; list->refcount++; DLLIST_PREPEND(&pool->conn_list, list); pool->conn_count++; ldap_connection_pool_shrink_to(pool, pool->max_connections); *list_r = list; return 0; } void ldap_connection_pool_unref(struct ldap_connection_pool *pool, struct ldap_connection_list **_list) { struct ldap_connection_list *list = *_list; *_list = NULL; i_assert(list->refcount > 0); if (--list->refcount == 0) ldap_connection_pool_shrink_to(pool, pool->max_connections); } bool ldap_connection_pool_have_references(struct ldap_connection_pool *pool) { struct ldap_connection_list *list; for (list = pool->conn_list; list != NULL; list = list->next) { if (list->refcount > 0) return TRUE; } return FALSE; } dovecot-2.2.33.2/src/lib-ldap/ldap-client.c0000644000175000017500000000401713123174404015141 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ldap-connection-pool.h" #include "ldap-private.h" /* Max number of ldap-connections that can be created. For now this is unlimited since we're assuming our callers aren't calling us with many different settings. */ #define LDAP_CONN_POOL_MAX_CONNECTIONS UINT_MAX struct ldap_client { struct ldap_connection_list *list; }; static struct ldap_connection_pool *ldap_conn_pool = NULL; int ldap_client_init(const struct ldap_client_settings *set, struct ldap_client **client_r, const char **error_r) { struct ldap_client *client; if (ldap_conn_pool == NULL) ldap_conn_pool = ldap_connection_pool_init(LDAP_CONN_POOL_MAX_CONNECTIONS); client = i_new(struct ldap_client, 1); if (ldap_connection_pool_get(ldap_conn_pool, client, set, &client->list, error_r) < 0) { i_free(client); return -1; } *client_r = client; return 0; } void ldap_client_deinit(struct ldap_client **_client) { struct ldap_client *client = *_client; *_client = NULL; ldap_connection_pool_unref(ldap_conn_pool, &client->list); i_free(client); } void ldap_client_switch_ioloop(struct ldap_client *client) { ldap_connection_switch_ioloop(client->list->conn); } #undef ldap_search_start void ldap_search_start(struct ldap_client *client, const struct ldap_search_input *input, ldap_result_callback_t *callback, void *context) { /* FIXME: we could support multiple concurrent LDAP connections to the same host. */ ldap_connection_search_start(client->list->conn, input, callback, context); } #undef ldap_compare_start void ldap_compare_start(struct ldap_client *client, const struct ldap_compare_input *input, ldap_result_callback_t *callback, void *context) { ldap_connection_compare_start(client->list->conn, input, callback, context); } void ldap_clients_cleanup(void) { if (ldap_conn_pool != NULL && !ldap_connection_pool_have_references(ldap_conn_pool)) ldap_connection_pool_deinit(&ldap_conn_pool); } dovecot-2.2.33.2/src/lib-ldap/ldap-connection.c0000644000175000017500000004756113165463624016050 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "aqueue.h" #include "ioloop.h" #include "ldap-private.h" static void ldap_connection_read_more(struct ldap_connection *conn); static int ldap_connect_next_message(struct ldap_connection *conn, struct ldap_op_queue_entry *req, bool *finished_r); static void ldap_connection_abort_request(struct ldap_op_queue_entry *req); static void ldap_connection_request_destroy(struct ldap_op_queue_entry **req); static int ldap_connection_connect(struct ldap_connection *conn); static void ldap_connection_send_next(struct ldap_connection *conn); void ldap_connection_deinit(struct ldap_connection **_conn) { struct ldap_connection *conn = *_conn; *_conn = NULL; ldap_connection_kill(conn); unsigned int n = aqueue_count(conn->request_queue); for (unsigned int i = 0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->to_abort != NULL) timeout_remove(&(*reqp)->to_abort); } pool_unref(&conn->pool); } static int ldap_connection_setup(struct ldap_connection *conn, const char **error_r) { int ret, opt; ret = ldap_initialize(&(conn->conn), conn->set.uri); if (ret != LDAP_SUCCESS) { *error_r = t_strdup_printf("ldap_initialize(uri=%s) failed: %s", conn->set.uri, ldap_err2string(ret)); return -1; } if (conn->ssl_set.verify_remote_cert) { opt = LDAP_OPT_X_TLS_HARD; } else { opt = LDAP_OPT_X_TLS_ALLOW; } ldap_set_option(conn->conn, LDAP_OPT_X_TLS, &opt); ldap_set_option(conn->conn, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt); #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN /* refuse to connect to SSLv2 as it's completely insecure */ opt = LDAP_OPT_X_TLS_PROTOCOL_SSL3; ldap_set_option(conn->conn, LDAP_OPT_X_TLS_PROTOCOL_MIN, &opt); #endif opt = conn->set.timeout_secs; /* default timeout */ ldap_set_option(conn->conn, LDAP_OPT_TIMEOUT, &opt); ldap_set_option(conn->conn, LDAP_OPT_NETWORK_TIMEOUT, &opt); /* timelimit */ ldap_set_option(conn->conn, LDAP_OPT_TIMELIMIT, &opt); if (conn->ssl_set.ca_file != NULL) ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CACERTFILE, conn->ssl_set.ca_file); if (conn->ssl_set.ca_dir != NULL) ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CACERTDIR, conn->ssl_set.ca_dir); if (conn->ssl_set.cert != NULL) ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CERTFILE, conn->ssl_set.cert); if (conn->ssl_set.key != NULL) ldap_set_option(conn->conn, LDAP_OPT_X_TLS_KEYFILE, conn->ssl_set.key); opt = conn->set.debug; ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &opt); opt = LDAP_VERSION3; ldap_set_option(conn->conn, LDAP_OPT_PROTOCOL_VERSION, &opt); ldap_set_option(conn->conn, LDAP_OPT_REFERRALS, 0); #ifdef LDAP_OPT_X_TLS_NEWCTX opt = 0; ldap_set_option(conn->conn, LDAP_OPT_X_TLS_NEWCTX, &opt); #endif return 0; } bool ldap_connection_have_settings(struct ldap_connection *conn, const struct ldap_client_settings *set) { const struct ldap_client_settings *conn_set = &conn->set; if (strcmp(conn_set->uri, set->uri) != 0) return FALSE; if (null_strcmp(conn_set->bind_dn, set->bind_dn) != 0) return FALSE; if (null_strcmp(conn_set->password, set->password) != 0) return FALSE; if (conn_set->timeout_secs != set->timeout_secs || conn_set->max_idle_time_secs != set->max_idle_time_secs || conn_set->debug != set->debug || conn_set->require_ssl != set->require_ssl || conn_set->start_tls != set->start_tls) return FALSE; if (set->ssl_set == NULL || !set->start_tls) return TRUE; /* check SSL settings */ if (null_strcmp(conn->ssl_set.protocols, set->ssl_set->protocols) != 0) return FALSE; if (null_strcmp(conn->ssl_set.cipher_list, set->ssl_set->cipher_list) != 0) return FALSE; if (null_strcmp(conn->ssl_set.ca_file, set->ssl_set->ca_file) != 0) return FALSE; if (null_strcmp(conn->ssl_set.cert, set->ssl_set->cert) != 0) return FALSE; if (null_strcmp(conn->ssl_set.key, set->ssl_set->key) != 0) return FALSE; return TRUE; } int ldap_connection_init(struct ldap_client *client, const struct ldap_client_settings *set, struct ldap_connection **conn_r, const char **error_r) { i_assert(set->uri != NULL); if (set->require_ssl && !set->start_tls && strncmp("ldaps://",set->uri,8) != 0) { *error_r = t_strdup_printf("ldap_connection_init(uri=%s) failed: %s", set->uri, "uri does not start with ldaps and ssl required without start TLS"); return -1; } pool_t pool = pool_alloconly_create("ldap connection", 1024); struct ldap_connection *conn = p_new(pool, struct ldap_connection, 1); conn->pool = pool; conn->client = client; conn->set = *set; /* deep copy relevant strings */ conn->set.uri = p_strdup(pool, set->uri); conn->set.bind_dn = p_strdup(pool, set->bind_dn); if (set->password != NULL) { conn->set.password = p_strdup(pool, set->password); ber_str2bv(conn->set.password, strlen(conn->set.password), 0, &(conn->cred)); } /* cannot use these */ conn->ssl_set.ca = NULL; conn->ssl_set.key_password = NULL; conn->ssl_set.cert_username_field = NULL; conn->ssl_set.crypto_device = NULL; if (set->ssl_set != NULL) { /* keep in sync with ldap_connection_have_settings() */ conn->set.ssl_set = &conn->ssl_set; conn->ssl_set.protocols = p_strdup(pool, set->ssl_set->protocols); conn->ssl_set.cipher_list = p_strdup(pool, set->ssl_set->cipher_list); conn->ssl_set.ca_file = p_strdup(pool, set->ssl_set->ca_file); conn->ssl_set.cert = p_strdup(pool, set->ssl_set->cert); conn->ssl_set.key = p_strdup(pool, set->ssl_set->key); } i_assert(ldap_connection_have_settings(conn, set)); if (ldap_connection_setup(conn, error_r) < 0) { ldap_connection_deinit(&conn); return -1; } p_array_init(&(conn->request_array), conn->pool, 10); conn->request_queue = aqueue_init(&(conn->request_array.arr)); *conn_r = conn; return 0; } void ldap_connection_switch_ioloop(struct ldap_connection *conn) { if (conn->io != NULL) conn->io = io_loop_move_io(&conn->io); if (conn->to_disconnect != NULL) conn->to_disconnect = io_loop_move_timeout(&conn->to_disconnect); if (conn->to_reconnect != NULL) conn->to_reconnect = io_loop_move_timeout(&conn->to_reconnect); unsigned int n = aqueue_count(conn->request_queue); for (unsigned int i = 0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->to_abort != NULL) (*reqp)->to_abort = io_loop_move_timeout(&((*reqp)->to_abort)); } } static void ldap_connection_result_failure(struct ldap_connection *conn, struct ldap_op_queue_entry *req, int ret, const char *error) { struct ldap_result res; i_zero(&res); res.conn = conn; res.openldap_ret = ret; res.error_string = error; if (req->result_callback != NULL) req->result_callback(&res, req->result_callback_ctx); else i_error("%s", error); ldap_connection_kill(conn); } static void ldap_connection_result_success(struct ldap_connection *conn, struct ldap_op_queue_entry *req) { struct ldap_result res; i_zero(&res); res.conn = conn; res.openldap_ret = LDAP_SUCCESS; if (req->result_callback != NULL) req->result_callback(&res, req->result_callback_ctx); } static void ldap_connection_send_next(struct ldap_connection *conn) { unsigned int i = 0, n; struct ldap_op_queue_entry *req; if (conn->to_reconnect != NULL) timeout_remove(&(conn->to_reconnect)); if (conn->state == LDAP_STATE_DISCONNECT) { if (ldap_connection_connect(conn) == -1) conn->to_reconnect = timeout_add(1000, ldap_connection_send_next, conn); return; } if (conn->state != LDAP_STATE_CONNECT) { return; } if (conn->pending > 10) return; /* try again later */ req = NULL; /* get next request */ n = aqueue_count(conn->request_queue); for(i=0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->msgid > -1) break; req = *reqp; } i--; /* nothing to actually send */ if (req == NULL) return; i_assert(req->msgid == -1); const char *error; int ret; if ((ret = req->send_request_cb(conn, req, &error)) != LDAP_SUCCESS) { /* did not succeed */ struct ldap_result res; i_zero(&res); res.openldap_ret = ret; res.error_string = error; if (req->result_callback != NULL) req->result_callback(&res, req->result_callback_ctx); ldap_connection_request_destroy(&req); aqueue_delete(conn->request_queue, i); } else conn->pending++; } static void ldap_connection_request_destroy(struct ldap_op_queue_entry **_req) { struct ldap_op_queue_entry *req = *_req; *_req = NULL; if (req->to_abort != NULL) timeout_remove(&req->to_abort); pool_unref(&req->pool); } void ldap_connection_queue_request(struct ldap_connection *conn, struct ldap_op_queue_entry *req) { req->msgid = -1; req->conn = conn; aqueue_append(conn->request_queue, &req); if (req->timeout_secs > 0) req->to_abort = timeout_add(req->timeout_secs * 1000, ldap_connection_abort_request, req); ldap_connection_send_next(conn); } static int ldap_connection_connect_parse(struct ldap_connection *conn, struct ldap_op_queue_entry *req, LDAPMessage *message, bool *finished_r) { int ret, result_err; char *retoid, *result_errmsg; int msgtype = ldap_msgtype(message); *finished_r = TRUE; ret = ldap_parse_result(conn->conn, message, &result_err, NULL, &result_errmsg, NULL, NULL, 0); switch(conn->state) { case LDAP_STATE_TLS: if (msgtype != LDAP_RES_EXTENDED) { *finished_r = FALSE; return LDAP_SUCCESS; } if (ret != 0) { ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_start_tls(uri=%s) failed: %s", conn->set.uri, ldap_err2string(ret))); return ret; } else if (result_err != 0) { if (conn->set.require_ssl) { ldap_connection_result_failure(conn, req, result_err, t_strdup_printf( "ldap_start_tls(uri=%s) failed: %s", conn->set.uri, result_errmsg)); ldap_memfree(result_errmsg); return LDAP_INVALID_CREDENTIALS; /* make sure it disconnects */ } } else { ret = ldap_parse_extended_result(conn->conn, message, &retoid, NULL, 0); /* retoid can be NULL even if ret == 0 */ if (ret == 0) { ret = ldap_install_tls(conn->conn); if (ret != 0) { // if this fails we have to abort ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_start_tls(uri=%s) failed: %s", conn->set.uri, ldap_err2string(ret))); return LDAP_INVALID_CREDENTIALS; } } if (ret != LDAP_SUCCESS) { if (conn->set.require_ssl) { ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_start_tls(uri=%s) failed: %s", conn->set.uri, ldap_err2string(ret))); return LDAP_UNAVAILABLE; } } else { if (conn->set.debug > 0) i_debug("Using TLS connection to remote LDAP server"); } ldap_memfree(retoid); } conn->state = LDAP_STATE_AUTH; return ldap_connect_next_message(conn, req, finished_r); case LDAP_STATE_AUTH: if (ret != LDAP_SUCCESS) { ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_parse_result() failed for connect: %s", ldap_err2string(ret))); return ret; } if (result_err != LDAP_SUCCESS) { const char *error = result_errmsg != NULL ? result_errmsg : ldap_err2string(result_err); ldap_connection_result_failure(conn, req, result_err, t_strdup_printf( "Connect failed: %s", error)); ldap_memfree(result_errmsg); return result_err; } if (msgtype != LDAP_RES_BIND) return 0; ret = ldap_parse_sasl_bind_result(conn->conn, message, &(conn->scred), 0); if (ret != LDAP_SUCCESS) { const char *error = t_strdup_printf( "Cannot bind with server: %s", ldap_err2string(ret)); ldap_connection_result_failure(conn, req, ret, error); return 1; } conn->state = LDAP_STATE_CONNECT; return ldap_connect_next_message(conn, req, finished_r); default: i_unreached(); } return LDAP_SUCCESS; } static void ldap_connection_abort_request(struct ldap_op_queue_entry *req) { struct ldap_result res; /* too bad */ if (req->to_abort != NULL) timeout_remove(&req->to_abort); if (req->msgid > -1) ldap_abandon_ext(req->conn->conn, req->msgid, NULL, NULL); i_zero(&res); res.openldap_ret = LDAP_TIMEOUT; res.error_string = "Aborting LDAP request after timeout"; if (req->result_callback != NULL) req->result_callback(&res, req->result_callback_ctx); unsigned int n = aqueue_count(req->conn->request_queue); for (unsigned int i = 0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(req->conn->request_array), aqueue_idx(req->conn->request_queue, i)); if (req == *reqp) { aqueue_delete(req->conn->request_queue, i); ldap_connection_request_destroy(&req); return; } } i_unreached(); } static void ldap_connection_abort_all_requests(struct ldap_connection *conn) { struct ldap_result res; i_zero(&res); res.openldap_ret = LDAP_TIMEOUT; res.error_string = "Aborting LDAP requests due to failure"; unsigned int n = aqueue_count(conn->request_queue); for (unsigned int i = 0; i < n; i++) { struct ldap_op_queue_entry **reqp = array_idx_modifiable(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->to_abort != NULL) timeout_remove(&(*reqp)->to_abort); if ((*reqp)->result_callback != NULL) (*reqp)->result_callback(&res, (*reqp)->result_callback_ctx); ldap_connection_request_destroy(reqp); } aqueue_clear(conn->request_queue); } static int ldap_connect_next_message(struct ldap_connection *conn, struct ldap_op_queue_entry *req, bool *finished_r) { int ret; *finished_r = TRUE; switch(conn->state) { case LDAP_STATE_DISCONNECT: /* if we should not disable SSL, and the URI is not ldaps:// */ if (!conn->set.start_tls || strstr(conn->set.uri, "ldaps://") == NULL) { ret = ldap_start_tls(conn->conn, NULL, NULL, &(req->msgid)); if (ret != LDAP_SUCCESS) { ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_start_tls(uri=%s) failed: %s", conn->set.uri, ldap_err2string(ret))); return ret; } conn->state = LDAP_STATE_TLS; break; } conn->state = LDAP_STATE_AUTH; /* fall through */ case LDAP_STATE_AUTH: ret = ldap_sasl_bind(conn->conn, conn->set.bind_dn, LDAP_SASL_SIMPLE, &(conn->cred), NULL, NULL, &(req->msgid)); if (ret != LDAP_SUCCESS) { ldap_connection_result_failure(conn, req, ret, t_strdup_printf( "ldap_sasl_bind(uri=%s, dn=%s) failed: %s", conn->set.uri, conn->set.bind_dn, ldap_err2string(ret))); return ret; } break; case LDAP_STATE_CONNECT: ldap_connection_result_success(conn, req); return LDAP_SUCCESS; /* we are done here */ default: i_unreached(); }; req->conn = conn; *finished_r = FALSE; return LDAP_SUCCESS; } static int ldap_connection_connect(struct ldap_connection *conn) { const char *error; int fd; Sockbuf *sb; bool finished; if (conn->conn == NULL) { /* try to reconnect after disconnection */ if (ldap_connection_setup(conn, &error) < 0) i_error("%s", error); } pool_t pool = pool_alloconly_create(MEMPOOL_GROWING "ldap bind", 128); struct ldap_op_queue_entry *req = p_new(pool, struct ldap_op_queue_entry, 1); req->pool = pool; req->internal_response_cb = ldap_connection_connect_parse; req->timeout_secs = conn->set.timeout_secs; if (ldap_connect_next_message(conn, req, &finished) != LDAP_SUCCESS || conn->conn == NULL) { pool_unref(&pool); return -1; } conn->pending++; aqueue_append(conn->request_queue, &req); /* start timeout */ if (req->timeout_secs > 0) req->to_abort = timeout_add(req->timeout_secs * 1000, ldap_connection_abort_request, req); ldap_get_option(conn->conn, LDAP_OPT_SOCKBUF, &sb); ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &fd); conn->io = io_add(fd, IO_READ, ldap_connection_read_more, conn); if (conn->set.max_idle_time_secs > 0) conn->to_disconnect = timeout_add(conn->set.max_idle_time_secs * 1000, ldap_connection_kill, conn); return 0; } void ldap_connection_kill(struct ldap_connection *conn) { if (conn->io != NULL) io_remove_closed(&(conn->io)); if (conn->to_disconnect != NULL) timeout_remove(&(conn->to_disconnect)); if (conn->to_reconnect != NULL) timeout_remove(&(conn->to_reconnect)); if (conn->request_queue) { unsigned int n = aqueue_count(conn->request_queue); for (unsigned int i = 0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->msgid > -1) ldap_abandon_ext(conn->conn, (*reqp)->msgid, NULL, NULL); (*reqp)->msgid = -1; } } if (conn->conn != NULL) { ldap_unbind_ext(conn->conn, NULL, NULL); ldap_memfree(conn->scred); } conn->conn = NULL; conn->state = LDAP_STATE_DISCONNECT; } int ldap_connection_check(struct ldap_connection *conn) { /* it's not connected */ if (conn->state == LDAP_STATE_DISCONNECT) return -1; return 0; } static struct ldap_op_queue_entry * ldap_connection_find_req_by_msgid(struct ldap_connection *conn, int msgid, unsigned int *idx_r) { unsigned int i, n = aqueue_count(conn->request_queue); for (i = 0; i < n; i++) { struct ldap_op_queue_entry *const *reqp = array_idx(&(conn->request_array), aqueue_idx(conn->request_queue, i)); if ((*reqp)->msgid == msgid) { *idx_r = i; return *reqp; } } return NULL; } static int ldap_connection_handle_message(struct ldap_connection *conn, LDAPMessage *message) { struct ldap_op_queue_entry *req; unsigned int i = 0; bool finished = FALSE; int err = LDAP_SUCCESS; /* we need to look at who it was for */ req = ldap_connection_find_req_by_msgid(conn, ldap_msgid(message), &i); if (req != NULL) err = req->internal_response_cb(conn, req, message, &finished); ldap_msgfree(message); switch(err) { case LDAP_SUCCESS: break; case LDAP_SERVER_DOWN: #ifdef LDAP_CONNECT_ERROR case LDAP_CONNECT_ERROR: #endif case LDAP_UNAVAILABLE: case LDAP_OPERATIONS_ERROR: case LDAP_BUSY: /* requeue */ ldap_connection_kill(conn); ldap_connection_send_next(conn); finished = FALSE; break; case LDAP_INVALID_CREDENTIALS: { /* fail everything */ ldap_connection_kill(conn); ldap_connection_abort_all_requests(conn); return 0; } case LDAP_SIZELIMIT_EXCEEDED: case LDAP_TIMELIMIT_EXCEEDED: case LDAP_NO_SUCH_ATTRIBUTE: case LDAP_UNDEFINED_TYPE: case LDAP_INAPPROPRIATE_MATCHING: case LDAP_CONSTRAINT_VIOLATION: case LDAP_TYPE_OR_VALUE_EXISTS: case LDAP_INVALID_SYNTAX: case LDAP_NO_SUCH_OBJECT: case LDAP_ALIAS_PROBLEM: case LDAP_INVALID_DN_SYNTAX: case LDAP_IS_LEAF: case LDAP_ALIAS_DEREF_PROBLEM: case LDAP_FILTER_ERROR: case LDAP_LOCAL_ERROR: finished = TRUE; break; default: /* ignore */ break; } if (finished) { i_assert(req != NULL); ldap_connection_request_destroy(&req); conn->pending--; aqueue_delete(conn->request_queue, i); return 1; } return 0; } static void ldap_connection_read_more(struct ldap_connection *conn) { struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; LDAPMessage *message; int ret; /* try get a message */ ret = ldap_result(conn->conn, LDAP_RES_ANY, 0, &tv, &message); if (ret > 0) ret = ldap_connection_handle_message(conn, message); if (ret == -1) { if (ldap_get_option(conn->conn, LDAP_OPT_RESULT_CODE, &ret) != LDAP_SUCCESS) i_unreached(); if (ret != LDAP_SERVER_DOWN) i_error("ldap_result() failed: %s", ldap_err2string(ret)); else i_error("Connection lost to LDAP server, reconnecting"); /* kill me */ ldap_connection_kill(conn); } else if (ret != 0) { ldap_connection_send_next(conn); } /* reset timeout */ if (conn->to_disconnect != NULL) timeout_reset(conn->to_disconnect); } bool ldap_result_has_failed(struct ldap_result *result) { i_assert((result->openldap_ret == LDAP_SUCCESS) == (result->error_string == NULL)); return result->openldap_ret != LDAP_SUCCESS; } const char *ldap_result_get_error(struct ldap_result *result) { i_assert((result->openldap_ret == LDAP_SUCCESS) == (result->error_string == NULL)); return result->error_string; } dovecot-2.2.33.2/src/lib-ldap/ldap-connection-pool.h0000644000175000017500000000155213123174404016777 00000000000000#ifndef LDAP_CONNECTION_POOL_H #define LDAP_CONNECTION_POOL_H struct ldap_client; struct ldap_client_settings; struct ldap_connection_list { struct ldap_connection_list *prev, *next; struct ldap_connection *conn; int refcount; }; struct ldap_connection_pool * ldap_connection_pool_init(unsigned int max_connections); void ldap_connection_pool_deinit(struct ldap_connection_pool **_pool); /* Returns TRUE if there are connections with refcount>0 */ bool ldap_connection_pool_have_references(struct ldap_connection_pool *pool); int ldap_connection_pool_get(struct ldap_connection_pool *pool, struct ldap_client *client, const struct ldap_client_settings *set, struct ldap_connection_list **list_r, const char **error_r); void ldap_connection_pool_unref(struct ldap_connection_pool *pool, struct ldap_connection_list **list); #endif dovecot-2.2.33.2/src/lib-ldap/ldap-client.h0000644000175000017500000000673313123174404015155 00000000000000#ifndef LDAP_CLIENT_H #define LDAP_CLIENT_H enum ldap_scope { LDAP_SEARCH_SCOPE_BASE = 0x0000, LDAP_SEARCH_SCOPE_ONE = 0x0001, LDAP_SEARCH_SCOPE_SUBTREE = 0x0002 }; struct ldap_client; struct ldap_result; struct ldap_search_iterator; struct ldap_entry; /* Called when the LDAP result has finished. The callback must verify first if the result is valid or not by calling ldap_result_has_failed() or ldap_result_get_error(). The result is freed automatically after this callback finishes. */ typedef void ldap_result_callback_t(struct ldap_result *result, void *context); struct ldap_client_settings { /* NOTE: when adding here, remember to update ldap_connection_have_settings() and ldap_connection_init() */ const char *uri; const char *bind_dn; const char *password; const struct ssl_iostream_settings *ssl_set; unsigned int timeout_secs; unsigned int max_idle_time_secs; unsigned int debug; bool require_ssl; bool start_tls; }; struct ldap_search_input { const char *base_dn; const char *filter; const char *const *attributes; enum ldap_scope scope; unsigned int size_limit; unsigned int timeout_secs; }; struct ldap_compare_input { const char *dn; const char *attr; const char *value; unsigned int timeout_secs; }; /* Initialize LDAP. Returns 0 on success, or -1 and error_r if initialization failed with the given settings. */ int ldap_client_init(const struct ldap_client_settings *set, struct ldap_client **client_r, const char **error_r); void ldap_client_deinit(struct ldap_client **client); void ldap_client_switch_ioloop(struct ldap_client *client); /* Deinitialize all pooled LDAP connections if there are no references left. This allows freeing the memory at deinit, but still allows multiple independent code parts to use lib-ldap and call this function. */ void ldap_clients_cleanup(void); void ldap_search_start(struct ldap_client *client, const struct ldap_search_input *input, ldap_result_callback_t *callback, void *context); #define ldap_search_start(client, input, callback, context) \ ldap_search_start(client, input + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct ldap_result *, typeof(context))), \ (ldap_result_callback_t *)callback, context) /* Returns TRUE if the LDAP query failed and result must not be used further. */ bool ldap_result_has_failed(struct ldap_result *result); /* Returns the error string if the query had failed, or NULL if it hasn't. */ const char *ldap_result_get_error(struct ldap_result *result); struct ldap_search_iterator* ldap_search_iterator_init(struct ldap_result *result); const struct ldap_entry *ldap_search_iterator_next(struct ldap_search_iterator *iter); void ldap_search_iterator_deinit(struct ldap_search_iterator **iter); void ldap_compare_start(struct ldap_client *client, const struct ldap_compare_input *input, ldap_result_callback_t *callback, void *context); #define ldap_compare_start(client, input, callback, context) \ ldap_compare_start(client, input + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct ldap_result *, typeof(context))), \ (ldap_result_callback_t *)callback, context) /* Returns TRUE if the comparison matched, FALSE if not. */ bool ldap_compare_result(struct ldap_result *result); const char *ldap_entry_dn(const struct ldap_entry *entry); const char *const *ldap_entry_get_attributes(const struct ldap_entry *entry); const char *const *ldap_entry_get_attribute(const struct ldap_entry *entry, const char *attribute); #endif dovecot-2.2.33.2/src/lib-ldap/ldap-iterator.c0000644000175000017500000000137513123174404015520 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ldap-private.h" struct ldap_search_iterator* ldap_search_iterator_init(struct ldap_result *result) { struct ldap_search_iterator *iter; i_assert(result->openldap_ret == LDAP_SUCCESS); i_assert(result->error_string == NULL); iter = p_new(result->pool, struct ldap_search_iterator, 1); iter->result = result; return iter; } const struct ldap_entry *ldap_search_iterator_next(struct ldap_search_iterator *iter) { if (iter->idx >= array_count(&(iter->result->entries))) return NULL; return array_idx(&(iter->result->entries), iter->idx++); } void ldap_search_iterator_deinit(struct ldap_search_iterator **iter) { *iter = NULL; } dovecot-2.2.33.2/src/lib-ldap/ldap-private.h0000644000175000017500000000625113123174404015344 00000000000000#ifndef LDAP_PRIVATE_H #define LDAP_PRIVATE_H #include "iostream-ssl.h" #include "ldap-client.h" #include #define DOVE_LDAP_CONTINUE 0 #define DOVE_LDAP_COMPLETE 1 #define DOVE_LDAP_REQUEUE 2 struct ldap_connection; struct ldap_result; struct ldap_op_queue_entry; /* Handle an LDAP response. Returns 0 on success, otherwise the OpenLDAP error number. */ typedef int ldap_response_callback_t(struct ldap_connection *conn, struct ldap_op_queue_entry *entry, LDAPMessage *msg, bool *finished_r); /* Send the request. Returns 0 on success, otherwise the OpenLDAP error number and sets error_r string. */ typedef int ldap_send_request_t(struct ldap_connection *conn, struct ldap_op_queue_entry *entry, const char **error_r); struct ldap_op_queue_entry { pool_t pool; struct ldap_connection *conn; ldap_response_callback_t *internal_response_cb; void *ctx; int msgid; unsigned int timeout_secs; struct timeout *to_abort; ldap_send_request_t *send_request_cb; ldap_result_callback_t *result_callback; void *result_callback_ctx; struct { struct ldap_search_input search; struct ldap_compare_input compare; } input; }; struct ldap_connection { pool_t pool; struct ldap_client *client; LDAP *conn; enum { LDAP_STATE_DISCONNECT, LDAP_STATE_TLS, LDAP_STATE_AUTH, LDAP_STATE_CONNECT } state; BerValue cred; /* needed for SASL */ BerVarray scred; struct ldap_client_settings set; struct ssl_iostream_settings ssl_set; struct aqueue *request_queue; ARRAY(struct ldap_op_queue_entry *) request_array; unsigned int sent; unsigned int pending; struct io *io; struct timeout *to_disconnect; struct timeout *to_reconnect; }; struct ldap_attribute { const char *name; ARRAY_TYPE(const_string) values; }; struct ldap_entry { struct ldap_result *result; char *dn; ARRAY(struct ldap_attribute) attributes; const char *const *attr_names; }; struct ldap_result { pool_t pool; struct ldap_connection *conn; ARRAY(struct ldap_entry) entries; int openldap_ret; bool compare_true; const char *error_string; }; struct ldap_search_iterator { unsigned int idx; struct ldap_result *result; }; int ldap_connection_init(struct ldap_client *client, const struct ldap_client_settings *set, struct ldap_connection **conn_r, const char **error_r); void ldap_connection_deinit(struct ldap_connection **_conn); void ldap_connection_switch_ioloop(struct ldap_connection *conn); bool ldap_connection_have_settings(struct ldap_connection *conn, const struct ldap_client_settings *set); void ldap_connection_search_start(struct ldap_connection *conn, const struct ldap_search_input *input, ldap_result_callback_t *callback, void *context); void ldap_connection_compare_start(struct ldap_connection *conn, const struct ldap_compare_input *input, ldap_result_callback_t *callback, void *context); void ldap_connection_kill(struct ldap_connection *conn); int ldap_connection_check(struct ldap_connection *conn); void ldap_connection_queue_request(struct ldap_connection *conn, struct ldap_op_queue_entry *req); int ldap_entry_init(struct ldap_entry *obj, struct ldap_result *result, LDAPMessage *message); #endif dovecot-2.2.33.2/src/lib-ldap/ldap-search.c0000644000175000017500000001103213123174404015123 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ldap-private.h" #include #include struct ldap_search_ctx { const struct ldap_search_input *input; struct ldap_result res; }; static void ldap_search_result_failure(struct ldap_op_queue_entry *req, int ret, const char *error) { struct ldap_search_ctx *sctx = req->ctx; sctx->res.openldap_ret = ret; sctx->res.error_string = error; req->result_callback(&(sctx->res), req->result_callback_ctx); } static void ldap_search_result_success(struct ldap_op_queue_entry *req) { struct ldap_search_ctx *sctx = req->ctx; sctx->res.openldap_ret = LDAP_SUCCESS; req->result_callback(&(sctx->res), req->result_callback_ctx); } static int ldap_search_callback(struct ldap_connection *conn, struct ldap_op_queue_entry *req, LDAPMessage *message, bool *finished_r) { struct ldap_search_ctx *sctx = req->ctx; int msgtype = ldap_msgtype(message); char *result_errmsg = NULL; int ret, result_err; if (msgtype != LDAP_RES_SEARCH_ENTRY && msgtype != LDAP_RES_SEARCH_RESULT) { *finished_r = FALSE; return LDAP_SUCCESS; } *finished_r = TRUE; ret = ldap_parse_result(conn->conn, message, &result_err, NULL, &result_errmsg, NULL, NULL, 0); if (ret == LDAP_NO_RESULTS_RETURNED) { /*ret = LDAP_SUCCESS;*/ } else if (ret != LDAP_SUCCESS) { ldap_search_result_failure(req, ret, t_strdup_printf( "ldap_parse_result() failed for search: %s", ldap_err2string(ret))); return ret; } else if (result_err != LDAP_SUCCESS) { const struct ldap_search_input *input = &req->input.search; const char *error = result_errmsg != NULL ? result_errmsg : ldap_err2string(result_err); ldap_search_result_failure(req, result_err, t_strdup_printf( "ldap_search_ext(base=%s, scope=%d, filter=%s) failed: %s", input->base_dn, input->scope, input->filter, error)); ldap_memfree(result_errmsg); return result_err; } LDAPMessage *res = ldap_first_entry(conn->conn, message); while(res != NULL) { struct ldap_entry *obj = p_new(req->pool, struct ldap_entry, 1); ldap_entry_init(obj, &(sctx->res), message); array_append(&(sctx->res.entries), obj, 1); res = ldap_next_entry(conn->conn, res); } if (msgtype == LDAP_RES_SEARCH_RESULT) { ldap_search_result_success(req); return LDAP_SUCCESS; } *finished_r = FALSE; return LDAP_SUCCESS; } static int ldap_search_send(struct ldap_connection *conn, struct ldap_op_queue_entry *req, const char **error_r) { const struct ldap_search_input *input = &req->input.search; LDAPControl manageDSAIT = { LDAP_CONTROL_MANAGEDSAIT, {0, 0}, 0 }; /* try to use ManageDSAIT if available */ LDAPControl *sctrls[] = { &manageDSAIT, NULL }; struct timeval tv = { .tv_sec = req->timeout_secs, .tv_usec = 0 }; int ret = ldap_search_ext(conn->conn, input->base_dn, input->scope, input->filter, (char**)input->attributes, 0, sctrls, NULL, &tv, input->size_limit, &(req->msgid)); if (ret != LDAP_SUCCESS) { *error_r = t_strdup_printf( "ldap_search_ext(base=%s, scope=%d, filter=%s) failed: %s", input->base_dn, input->scope, input->filter, ldap_err2string(ret)); } return ret; } void ldap_connection_search_start(struct ldap_connection *conn, const struct ldap_search_input *input, ldap_result_callback_t *callback, void *context) { struct ldap_op_queue_entry *req; pool_t pool = pool_alloconly_create(MEMPOOL_GROWING "ldap search", 128); req = p_new(pool, struct ldap_op_queue_entry, 1); req->pool = pool; struct ldap_search_ctx *sctx = p_new(pool, struct ldap_search_ctx, 1); sctx->res.conn = conn; sctx->res.pool = pool; p_array_init(&(sctx->res.entries), req->pool, 8); req->internal_response_cb = ldap_search_callback; req->result_callback = callback; req->result_callback_ctx = context; req->input.search = *input; /* copy strings */ req->input.search.base_dn = p_strdup(req->pool, input->base_dn); req->input.search.filter = p_strdup(req->pool, input->filter); if (input->attributes != NULL) { ARRAY_TYPE(const_string) arr; p_array_init(&arr, req->pool, 8); for(const char **ptr = (const char**)input->attributes; *ptr != NULL; ptr++) { const char *tmp = p_strdup(req->pool, *ptr); array_append(&arr, &tmp, 1); } array_append_zero(&arr); req->input.search.attributes = array_idx_modifiable(&arr, 0); } req->send_request_cb = ldap_search_send; sctx->input = &req->input.search; req->ctx = sctx; req->timeout_secs = input->timeout_secs; ldap_connection_queue_request(conn, req); } dovecot-2.2.33.2/src/lib-ldap/ldap-compare.c0000644000175000017500000000612013123174404015306 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ldap-private.h" static int ldap_compare_callback(struct ldap_connection *conn, struct ldap_op_queue_entry *req, LDAPMessage *message, bool *finished_r) { int msgtype = ldap_msgtype(message); struct ldap_result res; char *result_errmsg; int ret, result_err; if (msgtype != LDAP_RES_COMPARE) { *finished_r = FALSE; return 0; } *finished_r = TRUE; ret = ldap_parse_result(conn->conn, message, &result_err, NULL, &result_errmsg, NULL, NULL, 0); i_zero(&res); res.openldap_ret = ret; if (ret != LDAP_SUCCESS) { res.error_string = t_strdup_printf( "ldap_parse_result() failed to parse compare: %s", ldap_err2string(ret)); } else if (result_err == LDAP_COMPARE_TRUE) { res.compare_true = TRUE; } else if (result_err == LDAP_COMPARE_FALSE) { res.compare_true = FALSE; } else { const struct ldap_compare_input *input = &req->input.compare; const char *error = result_errmsg != NULL ? result_errmsg : ldap_err2string(result_err); res.openldap_ret = result_err; res.error_string = t_strdup_printf( "ldap_compare_ext(dn=%s, attr=%s) failed: %s", input->dn, input->attr, error); } req->result_callback(&res, req->result_callback_ctx); if (result_errmsg != NULL) ldap_memfree(result_errmsg); return res.openldap_ret; } static int ldap_compare_send(struct ldap_connection *conn, struct ldap_op_queue_entry *req, const char **error_r) { const struct ldap_compare_input *input = &req->input.compare; struct berval bv = { .bv_len = strlen(input->value), .bv_val = (void*)input->value }; LDAPControl manageDSAIT = { LDAP_CONTROL_MANAGEDSAIT, {0, 0}, 0 }; /* try to use ManageDSAIT if available */ LDAPControl *sctrls[] = { &manageDSAIT, NULL }; int ret = ldap_compare_ext(conn->conn, input->dn, input->attr, &bv, sctrls, NULL, &(req->msgid)); if (ret != LDAP_SUCCESS) { *error_r = t_strdup_printf( "ldap_compare_ext(dn=%s, attr=%s) failed: %s", input->dn, input->attr, ldap_err2string(ret)); } return ret; } void ldap_connection_compare_start(struct ldap_connection *conn, const struct ldap_compare_input *input, ldap_result_callback_t *callback, void *context) { struct ldap_op_queue_entry *req; pool_t pool = pool_alloconly_create(MEMPOOL_GROWING "ldap compare", 128); req = p_new(pool, struct ldap_op_queue_entry, 1); req->pool = pool; req->internal_response_cb = ldap_compare_callback; req->input.compare = *input; req->result_callback = callback; req->result_callback_ctx = context; /* copy strings */ req->input.compare.dn = p_strdup(req->pool, input->dn); req->input.compare.attr = p_strdup(req->pool, input->attr); req->input.compare.value = p_strdup(req->pool, input->value); req->send_request_cb = ldap_compare_send; req->timeout_secs = input->timeout_secs; ldap_connection_queue_request(conn, req); } bool ldap_compare_result(struct ldap_result *result) { i_assert(result->openldap_ret == LDAP_SUCCESS); i_assert(result->error_string == NULL); return result->compare_true; } dovecot-2.2.33.2/src/lib-ldap/Makefile.am0000644000175000017500000000167013123174404014637 00000000000000pkglib_LTLIBRARIES = libdovecot-ldap.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-ssl-iostream \ $(LDAP_CFLAGS) libdovecot_ldap_la_SOURCES = \ ldap-client.c \ ldap-connection.c \ ldap-connection-pool.c \ ldap-iterator.c \ ldap-search.c \ ldap-compare.c \ ldap-entry.c libdovecot_ldap_la_DEPENDENCIES = ../lib/liblib.la libdovecot_ldap_la_LDFLAGS = -export-dynamic libdovecot_ldap_la_LIBADD = ../lib/liblib.la $(LDAP_LIBS) headers = \ ldap-client.h noinst_HEADERS = \ ldap-connection-pool.h \ ldap-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_libs = \ ../lib-test/libtest.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib/liblib.la check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/imap-login/0002755000175000017500000000000013172375612013241 500000000000000dovecot-2.2.33.2/src/imap-login/Makefile.in0000644000175000017500000006141013172375572015233 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = imap-login$(EXEEXT) subdir = src/imap-login ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_imap_login_OBJECTS = imap-login-client.$(OBJEXT) \ client-authenticate.$(OBJEXT) imap-login-commands.$(OBJEXT) \ imap-login-settings.$(OBJEXT) imap-proxy.$(OBJEXT) imap_login_OBJECTS = $(am_imap_login_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(imap_login_SOURCES) DIST_SOURCES = $(imap_login_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common imap_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) imap_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT_DEPS) imap_login_SOURCES = \ imap-login-client.c \ client-authenticate.c \ imap-login-commands.c \ imap-login-settings.c \ imap-proxy.c noinst_HEADERS = \ client-authenticate.h \ imap-proxy.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ imap-login-client.h \ imap-login-commands.h \ imap-login-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/imap-login/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/imap-login/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list imap-login$(EXEEXT): $(imap_login_OBJECTS) $(imap_login_DEPENDENCIES) $(EXTRA_imap_login_DEPENDENCIES) @rm -f imap-login$(EXEEXT) $(AM_V_CCLD)$(LINK) $(imap_login_OBJECTS) $(imap_login_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-authenticate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-login-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-login-commands.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-login-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-proxy.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/imap-login/client-authenticate.h0000644000175000017500000000073713123174404017261 00000000000000#ifndef CLIENT_AUTHENTICATE_H #define CLIENT_AUTHENTICATE_H struct imap_arg; void client_authenticate_get_capabilities(struct client *client, string_t *str); void imap_client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text); int cmd_login(struct imap_client *client, const struct imap_arg *args); int cmd_authenticate(struct imap_client *imap_client, bool *parsed_r); #endif dovecot-2.2.33.2/src/imap-login/imap-login-commands.c0000644000175000017500000000350113123174404017145 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "array.h" #include "imap-login-commands.h" static ARRAY(struct imap_login_command *) imap_login_commands; static pool_t imap_login_commands_pool; struct imap_login_command *imap_login_command_lookup(const char *name) { struct imap_login_command *const *cmdp; array_foreach(&imap_login_commands, cmdp) { if (strcasecmp((*cmdp)->name, name) == 0) return *cmdp; } return NULL; } void imap_login_commands_register(const struct imap_login_command *commands, unsigned int count) { struct imap_login_command *cmd; unsigned int i; for (i = 0; i < count; i++) { cmd = p_new(imap_login_commands_pool, struct imap_login_command, 1); cmd->name = p_strdup(imap_login_commands_pool, commands[i].name); cmd->func = commands[i].func; array_append(&imap_login_commands, &cmd, 1); } } static void imap_login_command_unregister(const struct imap_login_command *unreg_cmd) { struct imap_login_command *const *cmdp; array_foreach(&imap_login_commands, cmdp) { if ((*cmdp)->func == unreg_cmd->func && strcmp((*cmdp)->name, unreg_cmd->name) == 0) { array_delete(&imap_login_commands, array_foreach_idx(&imap_login_commands, cmdp), 1); return; } } i_panic("imap_login_command_unregister: Command '%s' not found", unreg_cmd->name); } void imap_login_commands_unregister(const struct imap_login_command *commands, unsigned int count) { unsigned int i; for (i = 0; i < count; i++) imap_login_command_unregister(&commands[i]); } void imap_login_commands_init(void) { imap_login_commands_pool = pool_alloconly_create("imap login commands", 128); p_array_init(&imap_login_commands, imap_login_commands_pool, 8); } void imap_login_commands_deinit(void) { pool_unref(&imap_login_commands_pool); } dovecot-2.2.33.2/src/imap-login/imap-proxy.c0000644000175000017500000003660413165463624015444 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "base64.h" #include "str.h" #include "str-sanitize.h" #include "safe-memset.h" #include "dsasl-client.h" #include "imap-login-client.h" #include "client-authenticate.h" #include "imap-resp-code.h" #include "imap-quote.h" #include "imap-proxy.h" static const char *imap_proxy_sent_state_names[IMAP_PROXY_SENT_STATE_COUNT] = { "id", "starttls", "capability", "authenticate", "auth-continue", "login" }; static const char *imap_proxy_rcvd_state_names[IMAP_PROXY_RCVD_STATE_COUNT] = { "none", "banner", "id", "starttls", "capability", "auth-continue", "login" }; static void proxy_write_id(struct imap_client *client, string_t *str) { i_assert(client->common.proxy_ttl > 1); str_append(str, "I ID ("); if (client->common.client_id != NULL && str_len(client->common.client_id) > 0) { str_append_str(str, client->common.client_id); str_append_c(str, ' '); } str_printfa(str, "\"x-session-id\" \"%s\" " "\"x-originating-ip\" \"%s\" " "\"x-originating-port\" \"%u\" " "\"x-connected-ip\" \"%s\" " "\"x-connected-port\" \"%u\" " "\"x-proxy-ttl\" \"%u\"", client_get_session_id(&client->common), net_ip2addr(&client->common.ip), client->common.remote_port, net_ip2addr(&client->common.local_ip), client->common.local_port, client->common.proxy_ttl - 1); /* append any forward_ variables to request */ for(const char *const *ptr = client->common.auth_passdb_args; *ptr != NULL; ptr++) { if (strncasecmp(*ptr, "forward_", 8) == 0) { const char *key = t_strconcat("x-forward-", t_strcut((*ptr)+8, '='), NULL); const char *val = i_strchr_to_next(*ptr, '='); str_append_c(str, ' '); imap_append_string(str, key); str_append_c(str, ' '); imap_append_nstring(str, val); } } str_append(str, ")\r\n"); } static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } static int proxy_write_starttls(struct imap_client *client, string_t *str) { enum login_proxy_ssl_flags ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy); if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) { if (client->proxy_backend_capability != NULL && !str_array_icase_find(t_strsplit(client->proxy_backend_capability, " "), "STARTTLS")) { client_log_err(&client->common, "proxy: Remote doesn't support STARTTLS"); return -1; } str_append(str, "S STARTTLS\r\n"); client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_STARTTLS; return 1; } return 0; } static int proxy_write_login(struct imap_client *client, string_t *str) { struct dsasl_client_settings sasl_set; const unsigned char *output; unsigned int len; const char *mech_name, *error; /* Send CAPABILITY command if we don't know the capabilities yet. Also as kind of a Dovecot-backend workaround if the client insisted on sending CAPABILITY command (even though our banner already sent it), send the (unnecessary) CAPABILITY command to backend as well to avoid sending the CAPABILITY reply twice (untagged and OK resp code). */ if (!client->proxy_capability_request_sent && (client->proxy_backend_capability == NULL || client->client_ignores_capability_resp_code)) { client->proxy_capability_request_sent = TRUE; client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_CAPABILITY; str_append(str, "C CAPABILITY\r\n"); if (client->common.proxy_nopipelining) { /* authenticate only after receiving C OK reply. */ return 0; } } if (client->common.proxy_mech == NULL) { /* logging in normally - use LOGIN command */ if (client->proxy_logindisabled && login_proxy_get_ssl_flags(client->common.login_proxy) == 0) { client_log_err(&client->common, "proxy: Remote advertised LOGINDISABLED and SSL/TLS not enabled"); return -1; } str_append(str, "L LOGIN "); imap_append_string(str, client->common.proxy_user); str_append_c(str, ' '); imap_append_string(str, client->common.proxy_password); str_append(str, "\r\n"); client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_LOGIN; proxy_free_password(&client->common); return 0; } i_assert(client->common.proxy_sasl_client == NULL); i_zero(&sasl_set); sasl_set.authid = client->common.proxy_master_user != NULL ? client->common.proxy_master_user : client->common.proxy_user; sasl_set.authzid = client->common.proxy_user; sasl_set.password = client->common.proxy_password; client->common.proxy_sasl_client = dsasl_client_new(client->common.proxy_mech, &sasl_set); mech_name = dsasl_client_mech_get_name(client->common.proxy_mech); str_append(str, "L AUTHENTICATE "); str_append(str, mech_name); if (client->proxy_sasl_ir) { if (dsasl_client_output(client->common.proxy_sasl_client, &output, &len, &error) < 0) { client_log_err(&client->common, t_strdup_printf( "proxy: SASL mechanism %s init failed: %s", mech_name, error)); return -1; } str_append_c(str, ' '); if (len == 0) str_append_c(str, '='); else base64_encode(output, len, str); } str_append(str, "\r\n"); proxy_free_password(&client->common); client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_AUTHENTICATE; return 0; } static int proxy_input_banner(struct imap_client *client, struct ostream *output, const char *line) { const char *const *capabilities = NULL; string_t *str; int ret; if (strncmp(line, "* OK ", 5) != 0) { client_log_err(&client->common, t_strdup_printf( "proxy: Remote returned invalid banner: %s", str_sanitize(line, 160))); return -1; } str = t_str_new(128); if (strncmp(line + 5, "[CAPABILITY ", 12) == 0) { capabilities = t_strsplit(t_strcut(line + 5 + 12, ']'), " "); if (str_array_icase_find(capabilities, "SASL-IR")) client->proxy_sasl_ir = TRUE; if (str_array_icase_find(capabilities, "LOGINDISABLED")) client->proxy_logindisabled = TRUE; i_free(client->proxy_backend_capability); client->proxy_backend_capability = i_strdup(t_strcut(line + 5 + 12, ']')); if (str_array_icase_find(capabilities, "ID") && !client->common.proxy_not_trusted) { client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_ID; proxy_write_id(client, str); if (client->common.proxy_nopipelining) { /* write login or starttls after I OK */ o_stream_nsend(output, str_data(str), str_len(str)); return 0; } } } if ((ret = proxy_write_starttls(client, str)) < 0) { return -1; } else if (ret == 0) { if (proxy_write_login(client, str) < 0) return -1; } o_stream_nsend(output, str_data(str), str_len(str)); return 0; } static void client_send_login_reply(struct imap_client *client, string_t *str, const char *line) { const char *capability; bool tagged_capability; capability = client->proxy_backend_capability; tagged_capability = strncasecmp(line, "[CAPABILITY ", 12) == 0; if (tagged_capability) capability = t_strcut(line + 12, ']'); if (client->client_ignores_capability_resp_code && capability != NULL) { /* client has used CAPABILITY command, so it didn't understand the capabilities in the banner. send the backend's untagged CAPABILITY reply and hope that the client understands it */ str_printfa(str, "* CAPABILITY %s\r\n", capability); } str_append(str, client->cmd_tag); str_append(str, " OK "); if (!client->client_ignores_capability_resp_code && !tagged_capability && capability != NULL) { str_printfa(str, "[CAPABILITY %s] ", capability); if (*line == '[') { /* we need to send the capability. skip over this resp-code */ while (*line != ']' && *line != '\0') line++; if (*line == ' ') line++; } } str_append(str, line); str_append(str, "\r\n"); } int imap_proxy_parse_line(struct client *client, const char *line) { struct imap_client *imap_client = (struct imap_client *)client; struct ostream *output; string_t *str; const unsigned char *data; unsigned int data_len; const char *error; int ret; i_assert(!client->destroyed); output = login_proxy_get_ostream(client->login_proxy); if (!imap_client->proxy_seen_banner) { /* this is a banner */ imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_BANNER; imap_client->proxy_seen_banner = TRUE; if (proxy_input_banner(imap_client, output, line) < 0) { client_proxy_failed(client, TRUE); return -1; } return 0; } else if (*line == '+') { /* AUTHENTICATE started. finish it. */ if (client->proxy_sasl_client == NULL) { /* used literals with LOGIN command, just ignore. */ return 0; } imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_AUTHENTICATE; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_AUTH_CONTINUE; str = t_str_new(128); if (line[1] != ' ' || base64_decode(line+2, strlen(line+2), NULL, str) < 0) { client_log_err(client, "proxy: Server sent invalid base64 data in AUTHENTICATE response"); client_proxy_failed(client, TRUE); return -1; } ret = dsasl_client_input(client->proxy_sasl_client, str_data(str), str_len(str), &error); if (ret == 0) { ret = dsasl_client_output(client->proxy_sasl_client, &data, &data_len, &error); } if (ret < 0) { client_log_err(client, t_strdup_printf( "proxy: Server sent invalid authentication data: %s", error)); client_proxy_failed(client, TRUE); return -1; } i_assert(ret == 0); str_truncate(str, 0); base64_encode(data, data_len, str); str_append(str, "\r\n"); imap_client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_AUTH_CONTINUE; o_stream_nsend(output, str_data(str), str_len(str)); return 0; } else if (strncmp(line, "S ", 2) == 0) { imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_STARTTLS; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_STARTTLS; if (strncmp(line, "S OK ", 5) != 0) { /* STARTTLS failed */ client_log_err(client, t_strdup_printf( "proxy: Remote STARTTLS failed: %s", str_sanitize(line + 5, 160))); client_proxy_failed(client, TRUE); return -1; } /* STARTTLS successful, begin TLS negotiation. */ if (login_proxy_starttls(client->login_proxy) < 0) { client_proxy_failed(client, TRUE); return -1; } /* i/ostreams changed. */ output = login_proxy_get_ostream(client->login_proxy); str = t_str_new(128); if (proxy_write_login(imap_client, str) < 0) { client_proxy_failed(client, TRUE); return -1; } o_stream_nsend(output, str_data(str), str_len(str)); return 1; } else if (strncmp(line, "L OK ", 5) == 0) { /* Login successful. Send this line to client. */ imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_LOGIN; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_LOGIN; str = t_str_new(128); client_send_login_reply(imap_client, str, line + 5); o_stream_nsend(client->output, str_data(str), str_len(str)); (void)client_skip_line(imap_client); client_proxy_finish_destroy_client(client); return 1; } else if (strncmp(line, "L ", 2) == 0) { imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_LOGIN; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_LOGIN; line += 2; if (client->set->auth_verbose) { const char *log_line = line; if (strncasecmp(log_line, "NO ", 3) == 0) log_line += 3; client_proxy_log_failure(client, log_line); } #define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]" if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED, strlen(STR_NO_IMAP_RESP_CODE_AUTHFAILED)) == 0) { /* the remote sent a generic "authentication failed" error. replace it with our one, so that in case the remote is sending a different error message an attacker can't find out what users exist in the system. */ client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_AUTHFAILED, AUTH_FAILED_MSG); } else if (strncmp(line, "NO [", 4) == 0) { /* remote sent some other resp-code. forward it. */ client_send_raw(client, t_strconcat( imap_client->cmd_tag, " ", line, "\r\n", NULL)); } else { /* there was no [resp-code], so remote isn't Dovecot v1.2+. we could either forward the line as-is and leak information about what users exist in this system, or we could hide other errors than password failures. since other errors are pretty rare, it's safer to just hide them. they're still available in logs though. */ client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_AUTHFAILED, AUTH_FAILED_MSG); } client->proxy_auth_failed = TRUE; client_proxy_failed(client, FALSE); return -1; } else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) { i_free(imap_client->proxy_backend_capability); imap_client->proxy_backend_capability = i_strdup(line + 13); return 0; } else if (strncmp(line, "C ", 2) == 0) { /* Reply to CAPABILITY command we sent */ imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_CAPABILITY; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_CAPABILITY; if (strncmp(line, "C OK ", 5) == 0 && client->proxy_password != NULL) { /* pipelining was disabled, send the login now. */ str = t_str_new(128); if (proxy_write_login(imap_client, str) < 0) return -1; o_stream_nsend(output, str_data(str), str_len(str)); return 1; } return 0; } else if (strncasecmp(line, "I ", 2) == 0) { /* Reply to ID command we sent, ignore it unless pipelining is disabled, in which case send either STARTTLS or login */ imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_ID; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_ID; if (client->proxy_nopipelining) { str = t_str_new(128); if ((ret = proxy_write_starttls(imap_client, str)) < 0) { return -1; } else if (ret == 0) { if (proxy_write_login(imap_client, str) < 0) return -1; } o_stream_nsend(output, str_data(str), str_len(str)); return 1; } return 0; } else if (strncasecmp(line, "* ID ", 5) == 0) { /* Reply to ID command we sent, ignore it */ return 0; } else if (strncmp(line, "* ", 2) == 0) { /* untagged reply. just forward it. */ client_send_raw(client, t_strconcat(line, "\r\n", NULL)); return 0; } else { /* tagged reply, shouldn't happen. */ client_log_err(client, t_strdup_printf( "proxy: Unexpected input, ignoring: %s", str_sanitize(line, 160))); return 0; } } void imap_proxy_reset(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; imap_client->proxy_sasl_ir = FALSE; imap_client->proxy_logindisabled = FALSE; imap_client->proxy_seen_banner = FALSE; imap_client->proxy_capability_request_sent = FALSE; imap_client->proxy_sent_state = 0; imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_NONE; } void imap_proxy_error(struct client *client, const char *text) { client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_UNAVAILABLE, text); } const char *imap_proxy_get_state(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; string_t *str = t_str_new(128); for (unsigned int i = 0; i < IMAP_PROXY_SENT_STATE_COUNT; i++) { if (str_len(str) > 0) str_append_c(str, '+'); if ((imap_client->proxy_sent_state & (1 << i)) != 0) str_append(str, imap_proxy_sent_state_names[i]); } str_append_c(str, '/'); str_append(str, imap_proxy_rcvd_state_names[imap_client->proxy_rcvd_state]); return str_c(str); } dovecot-2.2.33.2/src/imap-login/imap-login-commands.h0000644000175000017500000000120613123174404017152 00000000000000#ifndef IMAP_LOGIN_COMMANDS_H #define IMAP_LOGIN_COMMANDS_H struct imap_arg; struct imap_client; typedef int imap_login_command_t(struct imap_client *client, const struct imap_arg *args); struct imap_login_command { const char *name; imap_login_command_t *func; }; struct imap_login_command *imap_login_command_lookup(const char *name); void imap_login_commands_register(const struct imap_login_command *commands, unsigned int count); void imap_login_commands_unregister(const struct imap_login_command *commands, unsigned int count); void imap_login_commands_init(void); void imap_login_commands_deinit(void); #endif dovecot-2.2.33.2/src/imap-login/client-authenticate.c0000644000175000017500000001307313165463624017264 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "base64.h" #include "buffer.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" #include "net.h" #include "imap-resp-code.h" #include "imap-parser.h" #include "imap-url.h" #include "auth-client.h" #include "imap-login-client.h" #include "client-authenticate.h" #include "imap-proxy.h" void client_authenticate_get_capabilities(struct client *client, string_t *str) { const struct auth_mech_desc *mech; unsigned int i, count; mech = sasl_server_get_advertised_mechs(client, &count); for (i = 0; i < count; i++) { str_append_c(str, ' '); str_append(str, "AUTH="); str_append(str, mech[i].name); } } void imap_client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text) { struct imap_url url; string_t *referral; switch (result) { case CLIENT_AUTH_RESULT_SUCCESS: /* nothing to be done for IMAP */ break; case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS: case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN: /* IMAP referral [nologin] referral host=.. [port=..] [destuser=..] [reason=..] NO [REFERRAL imap://destuser;AUTH=..@host:port/] Can't login. OK [...] Logged in, but you should use this server instead. .. [REFERRAL ..] (Reason from auth server) */ referral = t_str_new(128); i_zero(&url); url.userid = reply->destuser; url.auth_type = client->auth_mech_name; url.host_name = reply->host; if (reply->port != 143) { url.have_port = TRUE; url.port = reply->port; } str_append(referral, "REFERRAL "); str_append(referral, imap_url_create(&url)); if (result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS) { client_send_reply_code(client, IMAP_CMD_REPLY_OK, str_c(referral), text); } else { client_send_reply_code(client, IMAP_CMD_REPLY_NO, str_c(referral), text); } break; case CLIENT_AUTH_RESULT_ABORTED: client_send_reply(client, IMAP_CMD_REPLY_BAD, text); break; case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: if (text[0] == '[') client_send_reply(client, IMAP_CMD_REPLY_NO, text); else { client_send_reply_code(client, IMAP_CMD_REPLY_NO, "ALERT", text); } break; case CLIENT_AUTH_RESULT_AUTHZFAILED: client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_AUTHZFAILED, text); break; case CLIENT_AUTH_RESULT_TEMPFAIL: client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_UNAVAILABLE, text); break; case CLIENT_AUTH_RESULT_SSL_REQUIRED: client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_PRIVACYREQUIRED, text); break; case CLIENT_AUTH_RESULT_AUTHFAILED: client_send_reply_code(client, IMAP_CMD_REPLY_NO, IMAP_RESP_CODE_AUTHFAILED, text); break; } } static int imap_client_auth_begin(struct imap_client *imap_client, const char *mech_name, const char *init_resp) { char *prefix; prefix = i_strdup_printf("%d%s", imap_client->client_ignores_capability_resp_code, imap_client->cmd_tag); i_free(imap_client->common.master_data_prefix); imap_client->common.master_data_prefix = (void *)prefix; imap_client->common.master_data_prefix_len = strlen(prefix)+1; return client_auth_begin(&imap_client->common, mech_name, init_resp); } int cmd_authenticate(struct imap_client *imap_client, bool *parsed_r) { /* NOTE: This command's input is handled specially because the SASL-IR can be large. */ struct client *client = &imap_client->common; const unsigned char *data; size_t i, size; int ret; *parsed_r = FALSE; /* [] */ if (!imap_client->auth_mech_name_parsed) { data = i_stream_get_data(client->input, &size); for (i = 0; i < size; i++) { if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') break; } if (i == size) return 0; if (i == 0) { /* empty mechanism name */ imap_client->skip_line = TRUE; return -1; } i_free(client->auth_mech_name); client->auth_mech_name = i_strndup(data, i); imap_client->auth_mech_name_parsed = TRUE; if (data[i] == ' ') i++; i_stream_skip(client->input, i); } /* get SASL-IR, if any */ if ((ret = client_auth_read_line(client)) <= 0) return ret; *parsed_r = TRUE; imap_client->auth_mech_name_parsed = FALSE; return imap_client_auth_begin(imap_client, t_strdup(client->auth_mech_name), t_strdup(str_c(client->auth_response))); } int cmd_login(struct imap_client *imap_client, const struct imap_arg *args) { struct client *client = &imap_client->common; const char *user, *pass; string_t *plain_login, *base64; /* two arguments: username and password */ if (!imap_arg_get_astring(&args[0], &user) || !imap_arg_get_astring(&args[1], &pass) || !IMAP_ARG_IS_EOL(&args[2])) return -1; if (!client_check_plaintext_auth(client, TRUE)) { if (client->virtual_user == NULL) client->virtual_user = i_strdup(user); return 1; } /* authorization ID \0 authentication ID \0 pass */ plain_login = buffer_create_dynamic(pool_datastack_create(), 64); buffer_append_c(plain_login, '\0'); buffer_append(plain_login, user, strlen(user)); buffer_append_c(plain_login, '\0'); buffer_append(plain_login, pass, strlen(pass)); base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(plain_login->used)); base64_encode(plain_login->data, plain_login->used, base64); return imap_client_auth_begin(imap_client, "PLAIN", str_c(base64)); } dovecot-2.2.33.2/src/imap-login/imap-login-settings.h0000644000175000017500000000042413165463624017225 00000000000000#ifndef IMAP_LOGIN_SETTINGS_H #define IMAP_LOGIN_SETTINGS_H struct imap_login_settings { const char *imap_capability; const char *imap_id_send; const char *imap_id_log; bool imap_id_retain; }; extern const struct setting_parser_info *imap_login_setting_roots[]; #endif dovecot-2.2.33.2/src/imap-login/imap-proxy.h0000644000175000017500000000043713123174404015431 00000000000000#ifndef IMAP_PROXY_H #define IMAP_PROXY_H void imap_proxy_reset(struct client *client); int imap_proxy_parse_line(struct client *client, const char *line); void imap_proxy_error(struct client *client, const char *text); const char *imap_proxy_get_state(struct client *client); #endif dovecot-2.2.33.2/src/imap-login/imap-login-client.c0000644000175000017500000005116313165463624016644 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "buffer.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "imap-parser.h" #include "imap-id.h" #include "imap-resp-code.h" #include "master-service.h" #include "master-service-ssl-settings.h" #include "master-auth.h" #include "imap-login-client.h" #include "client-authenticate.h" #include "auth-client.h" #include "ssl-proxy.h" #include "imap-proxy.h" #include "imap-quote.h" #include "imap-login-commands.h" #include "imap-login-settings.h" #if LOGIN_MAX_INBUF_SIZE < 1024+2 # error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters #endif /* maximum length for IMAP command line. */ #define MAX_IMAP_LINE 8192 /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 3 static const char *const imap_login_reserved_id_keys[] = { "x-originating-ip", "x-originating-port", "x-connected-ip", "x-connected-port", "x-proxy-ttl", "x-session-id", "x-session-ext-id", NULL }; /* Skip incoming data until newline is found, returns TRUE if newline was found. */ bool client_skip_line(struct imap_client *client) { const unsigned char *data; size_t i, data_size; data = i_stream_get_data(client->common.input, &data_size); for (i = 0; i < data_size; i++) { if (data[i] == '\n') { i_stream_skip(client->common.input, i+1); return TRUE; } } return FALSE; } static bool client_handle_parser_error(struct imap_client *client, struct imap_parser *parser) { const char *msg; bool fatal; msg = imap_parser_get_error(parser, &fatal); if (fatal) { client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, msg); client_destroy(&client->common, t_strconcat("Disconnected: ", msg, NULL)); return FALSE; } client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg); client->cmd_finished = TRUE; client->skip_line = TRUE; return TRUE; } static bool is_login_cmd_disabled(struct client *client) { if (client->secured) { if (auth_client_find_mech(auth_client, "PLAIN") == NULL) { /* no PLAIN authentication, can't use LOGIN command */ return TRUE; } return FALSE; } if (client->set->disable_plaintext_auth) return TRUE; if (strcmp(client->ssl_set->ssl, "required") == 0) return TRUE; return FALSE; } static const char *get_capability(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; string_t *cap_str = t_str_new(256); if (*imap_client->set->imap_capability == '\0') str_append(cap_str, CAPABILITY_BANNER_STRING); else if (*imap_client->set->imap_capability != '+') str_append(cap_str, imap_client->set->imap_capability); else { str_append(cap_str, CAPABILITY_BANNER_STRING); str_append_c(cap_str, ' '); str_append(cap_str, imap_client->set->imap_capability + 1); } if (client_is_tls_enabled(client) && !client->tls) str_append(cap_str, " STARTTLS"); if (is_login_cmd_disabled(client)) str_append(cap_str, " LOGINDISABLED"); client_authenticate_get_capabilities(client, cap_str); return str_c(cap_str); } static int cmd_capability(struct imap_client *imap_client, const struct imap_arg *args ATTR_UNUSED) { struct client *client = &imap_client->common; /* Client is required to send CAPABILITY after STARTTLS, so the capability resp-code workaround checks only pre-STARTTLS CAPABILITY commands. */ if (!client->starttls) imap_client->client_ignores_capability_resp_code = TRUE; client_send_raw(client, t_strconcat( "* CAPABILITY ", get_capability(client), "\r\n", NULL)); client_send_reply(client, IMAP_CMD_REPLY_OK, "Pre-login capabilities listed, post-login capabilities have more."); return 1; } static int cmd_starttls(struct imap_client *client, const struct imap_arg *args ATTR_UNUSED) { client_cmd_starttls(&client->common); return 1; } static void imap_client_notify_starttls(struct client *client, bool success, const char *text) { if (success) client_send_reply(client, IMAP_CMD_REPLY_OK, text); else client_send_reply(client, IMAP_CMD_REPLY_BAD, text); } static bool client_update_info(struct imap_client *client, const char *key, const char *value) { i_assert(value != NULL); /* SYNC WITH imap_login_reserved_id_keys */ if (strcasecmp(key, "x-originating-ip") == 0) { (void)net_addr2ip(value, &client->common.ip); } else if (strcasecmp(key, "x-originating-port") == 0) { (void)net_str2port(value, &client->common.remote_port); } else if (strcasecmp(key, "x-connected-ip") == 0) { (void)net_addr2ip(value, &client->common.local_ip); } else if (strcasecmp(key, "x-connected-port") == 0) { (void)net_str2port(value, &client->common.local_port); } else if (strcasecmp(key, "x-proxy-ttl") == 0) { if (str_to_uint(value, &client->common.proxy_ttl) < 0) { /* nothing */ } } else if (strcasecmp(key, "x-session-id") == 0 || strcasecmp(key, "x-session-ext-id") == 0) { if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { client->common.session_id = p_strdup(client->common.pool, value); } } else if (strncasecmp(key, "x-forward-", 10) == 0) { /* handle extra field */ client_add_forward_field(&client->common, key+10, value); } else { return FALSE; } return TRUE; } static bool client_id_reserved_word(const char *key) { i_assert(key != NULL); return (strncasecmp(key, "x-forward-", 10) == 0 || str_array_icase_find(imap_login_reserved_id_keys, key)); } static void cmd_id_handle_keyvalue(struct imap_client *client, const char *key, const char *value) { bool client_id_str; /* length of key + length of value (NIL for NULL) and two set of quotes and space */ size_t kvlen = strlen(key) + 2 + 1 + (value == NULL ? 3 : strlen(value)) + 2; if (client->common.trusted && !client->id_logged) { if (value == NULL) { /* do not try to process NIL values as client-info, but store them for non-reserved keys */ client_id_str = !client_id_reserved_word(key); } else { client_id_str = !client_update_info(client, key, value); i_assert(client_id_str == !client_id_reserved_word(key)); } } else { client_id_str = !client_id_reserved_word(key); } if (client->set->imap_id_retain && client_id_str && (client->common.client_id == NULL || str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) { if (client->common.client_id == NULL) { client->common.client_id = str_new(client->common.preproxy_pool, 64); } else { str_append_c(client->common.client_id, ' '); } imap_append_quoted(client->common.client_id, key); str_append_c(client->common.client_id, ' '); if (value == NULL) str_append(client->common.client_id, "NIL"); else imap_append_quoted(client->common.client_id, value); } if (client->cmd_id->log_reply != NULL && (client->cmd_id->log_keys == NULL || str_array_icase_find((void *)client->cmd_id->log_keys, key))) imap_id_log_reply_append(client->cmd_id->log_reply, key, value); } static int cmd_id_handle_args(struct imap_client *client, const struct imap_arg *arg) { struct imap_client_cmd_id *id = client->cmd_id; const char *key, *value; switch (id->state) { case IMAP_CLIENT_ID_STATE_LIST: if (arg->type == IMAP_ARG_NIL) return 1; if (arg->type != IMAP_ARG_LIST) return -1; if (client->set->imap_id_log[0] == '\0') { /* no ID logging */ } else if (client->id_logged) { /* already logged the ID reply */ } else { id->log_reply = str_new(default_pool, 64); if (strcmp(client->set->imap_id_log, "*") == 0) { /* log all keys */ } else { /* log only specified keys */ id->log_keys = p_strsplit_spaces(default_pool, client->set->imap_id_log, " "); } } id->state = IMAP_CLIENT_ID_STATE_KEY; break; case IMAP_CLIENT_ID_STATE_KEY: if (!imap_arg_get_string(arg, &key)) return -1; if (i_strocpy(id->key, key, sizeof(id->key)) < 0) return -1; id->state = IMAP_CLIENT_ID_STATE_VALUE; break; case IMAP_CLIENT_ID_STATE_VALUE: if (!imap_arg_get_nstring(arg, &value)) return -1; cmd_id_handle_keyvalue(client, id->key, value); id->state = IMAP_CLIENT_ID_STATE_KEY; break; } return 0; } static void cmd_id_finish(struct imap_client *client) { /* finished handling the parameters */ if (!client->id_logged) { client->id_logged = TRUE; if (client->cmd_id->log_reply != NULL) { client_log(&client->common, t_strdup_printf( "ID sent: %s", str_c(client->cmd_id->log_reply))); } } client_send_raw(&client->common, t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(client->set->imap_id_send))); client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed."); } static void cmd_id_free(struct imap_client *client) { struct imap_client_cmd_id *id = client->cmd_id; if (id->log_reply != NULL) str_free(&id->log_reply); if (id->log_keys != NULL) p_strsplit_free(default_pool, id->log_keys); imap_parser_unref(&id->parser); i_free_and_null(client->cmd_id); client->skip_line = TRUE; } static int cmd_id(struct imap_client *client) { struct imap_client_cmd_id *id; enum imap_parser_flags parser_flags; const struct imap_arg *args; int ret; if (client->common.client_id != NULL) str_truncate(client->common.client_id, 0); if (client->cmd_id == NULL) { client->cmd_id = id = i_new(struct imap_client_cmd_id, 1); id->parser = imap_parser_create(client->common.input, client->common.output, MAX_IMAP_LINE); parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST; } else { id = client->cmd_id; parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; } while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) { i_assert(ret == 1); if ((ret = cmd_id_handle_args(client, args)) < 0) { client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, "Invalid ID parameters"); cmd_id_free(client); return -1; } if (ret > 0) { /* NIL parameter */ ret = 0; break; } imap_parser_reset(id->parser); parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; } if (ret == 0) { /* finished the line */ cmd_id_finish(client); cmd_id_free(client); return 1; } else if (ret == -1) { if (!client_handle_parser_error(client, id->parser)) return 0; cmd_id_free(client); return -1; } else { i_assert(ret == -2); return 0; } } static int cmd_noop(struct imap_client *client, const struct imap_arg *args ATTR_UNUSED) { client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "NOOP completed."); return 1; } static int cmd_logout(struct imap_client *client, const struct imap_arg *args ATTR_UNUSED) { client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out"); client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "Logout completed."); client_destroy(&client->common, "Aborted login"); return 1; } static int cmd_enable(struct imap_client *client, const struct imap_arg *args ATTR_UNUSED) { client_send_raw(&client->common, "* ENABLED\r\n"); client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ENABLE ignored in non-authenticated state."); return 1; } static int client_command_execute(struct imap_client *client, const char *cmd, const struct imap_arg *args) { struct imap_login_command *login_cmd; login_cmd = imap_login_command_lookup(cmd); if (login_cmd == NULL) return -2; return login_cmd->func(client, args); } static bool imap_is_valid_tag(const char *tag) { for (; *tag != '\0'; tag++) { switch (*tag) { case '+': /* atom-specials: */ case '(': case ')': case '{': case '/': case ' ': /* list-wildcards: */ case '%': case '*': /* quoted-specials: */ case '"': case '\\': return FALSE; default: if (*tag < ' ') /* CTL */ return FALSE; break; } } return TRUE; } static int client_parse_command(struct imap_client *client, const struct imap_arg **args_r) { switch (imap_parser_read_args(client->parser, 0, 0, args_r)) { case -1: /* error */ if (!client_handle_parser_error(client, client->parser)) { /* client destroyed */ return 0; } return -1; case -2: /* not enough data */ return 0; default: /* we read the entire line - skip over the CRLF */ if (!client_skip_line(client)) i_unreached(); return 1; } } static bool client_handle_input(struct imap_client *client) { i_assert(!client->common.authenticating); if (client->cmd_finished) { /* clear the previous command from memory. don't do this immediately after handling command since we need the cmd_tag to stay some time after authentication commands. */ client->cmd_tag = NULL; client->cmd_name = NULL; imap_parser_reset(client->parser); /* remove \r\n */ if (client->skip_line) { if (!client_skip_line(client)) return FALSE; client->skip_line = FALSE; } client->cmd_finished = FALSE; } if (client->cmd_tag == NULL) { client->cmd_tag = imap_parser_read_word(client->parser); if (client->cmd_tag == NULL) return FALSE; /* need more data */ if (!imap_is_valid_tag(client->cmd_tag) || strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) { /* the tag is invalid, don't allow it and don't send it back. this attempts to prevent any potentially dangerous replies in case someone tries to access us using HTTP protocol. */ client->cmd_tag = ""; } } if (client->cmd_name == NULL) { client->cmd_name = imap_parser_read_word(client->parser); if (client->cmd_name == NULL) return FALSE; /* need more data */ } return client->common.v.input_next_cmd(&client->common); } static bool imap_client_input_next_cmd(struct client *_client) { struct imap_client *client = (struct imap_client *)_client; const struct imap_arg *args; bool parsed; int ret; if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) { /* SASL-IR may need more space than input buffer's size, so we'll handle it as a special case. */ ret = cmd_authenticate(client, &parsed); if (ret == 0 && !parsed) return FALSE; } else if (strcasecmp(client->cmd_name, "ID") == 0) { /* ID extensions allows max. 30 parameters, each max. 1024 bytes long. that brings us over the input buffer's size, so handle the parameters one at a time */ ret = cmd_id(client); if (ret == 0) return FALSE; if (ret < 0) ret = 1; /* don't send the error reply again */ } else { ret = client_parse_command(client, &args); if (ret < 0) return TRUE; if (ret == 0) return FALSE; ret = *client->cmd_tag == '\0' ? -1 : client_command_execute(client, client->cmd_name, args); } client->cmd_finished = TRUE; if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) { client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, "First parameter in line is IMAP's command tag, " "not the command name. Add that before the command, " "like: a login user pass"); } else if (ret < 0) { if (*client->cmd_tag == '\0') client->cmd_tag = "*"; if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Too many invalid IMAP commands."); client_destroy(&client->common, "Disconnected: Too many invalid commands"); return FALSE; } client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, "Error in IMAP command received by server."); } return ret != 0 && !client->common.destroyed; } static void imap_client_input(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; if (!client_read(client)) return; client_ref(client); o_stream_cork(imap_client->common.output); for (;;) { if (!auth_client_is_connected(auth_client)) { /* we're not currently connected to auth process - don't allow any commands */ client_notify_status(client, FALSE, AUTH_SERVER_WAITING_MSG); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); client->input_blocked = TRUE; break; } else { if (!client_handle_input(imap_client)) break; } } o_stream_uncork(imap_client->common.output); client_unref(&client); } static struct client *imap_client_alloc(pool_t pool) { struct imap_client *imap_client; imap_client = p_new(pool, struct imap_client, 1); return &imap_client->common; } static void imap_client_create(struct client *client, void **other_sets) { struct imap_client *imap_client = (struct imap_client *)client; imap_client->set = other_sets[0]; imap_client->parser = imap_parser_create(imap_client->common.input, imap_client->common.output, MAX_IMAP_LINE); client->io = io_add(client->fd, IO_READ, client_input, client); } static void imap_client_destroy(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; i_free_and_null(imap_client->proxy_backend_capability); imap_parser_unref(&imap_client->parser); } static void imap_client_notify_auth_ready(struct client *client) { string_t *greet; greet = t_str_new(128); str_append(greet, "* OK "); str_printfa(greet, "[CAPABILITY %s] ", get_capability(client)); str_append(greet, client->set->login_greeting); str_append(greet, "\r\n"); client_send_raw(client, str_c(greet)); client->banner_sent = TRUE; } static void imap_client_starttls(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; imap_parser_unref(&imap_client->parser); imap_client->parser = imap_parser_create(imap_client->common.input, imap_client->common.output, MAX_IMAP_LINE); /* CRLF is lost from buffer when streams are reopened. */ imap_client->skip_line = FALSE; } static void ATTR_NULL(3) client_send_reply_raw(struct client *client, const char *prefix, const char *resp_code, const char *text, bool tagged) { struct imap_client *imap_client = (struct imap_client *)client; T_BEGIN { string_t *line = t_str_new(256); if (tagged) str_append(line, imap_client->cmd_tag); else str_append_c(line, '*'); str_append_c(line, ' '); str_append(line, prefix); str_append_c(line, ' '); if (resp_code != NULL) str_printfa(line, "[%s] ", resp_code); str_append(line, text); str_append(line, "\r\n"); client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } void client_send_reply_code(struct client *client, enum imap_cmd_reply reply, const char *resp_code, const char *text) { const char *prefix = "NO"; bool tagged = TRUE; switch (reply) { case IMAP_CMD_REPLY_OK: prefix = "OK"; break; case IMAP_CMD_REPLY_NO: break; case IMAP_CMD_REPLY_BAD: prefix = "BAD"; break; case IMAP_CMD_REPLY_BYE: prefix = "BYE"; tagged = FALSE; break; } client_send_reply_raw(client, prefix, resp_code, text, tagged); } void client_send_reply(struct client *client, enum imap_cmd_reply reply, const char *text) { client_send_reply_code(client, reply, NULL, text); } static void imap_client_notify_status(struct client *client, bool bad, const char *text) { if (bad) client_send_reply_raw(client, "BAD", "ALERT", text, FALSE); else client_send_reply_raw(client, "OK", NULL, text, FALSE); } static void imap_client_notify_disconnect(struct client *client, enum client_disconnect_reason reason, const char *text) { if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) { client_send_reply_code(client, IMAP_CMD_REPLY_BYE, IMAP_RESP_CODE_UNAVAILABLE, text); } else { client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text); } } static void imap_login_preinit(void) { login_set_roots = imap_login_setting_roots; } static const struct imap_login_command imap_login_commands[] = { { "LOGIN", cmd_login }, { "CAPABILITY", cmd_capability }, { "STARTTLS", cmd_starttls }, { "NOOP", cmd_noop }, { "LOGOUT", cmd_logout }, { "ENABLE", cmd_enable } }; static void imap_login_init(void) { imap_login_commands_init(); imap_login_commands_register(imap_login_commands, N_ELEMENTS(imap_login_commands)); } static void imap_login_deinit(void) { clients_destroy_all(); imap_login_commands_deinit(); } static struct client_vfuncs imap_client_vfuncs = { imap_client_alloc, imap_client_create, imap_client_destroy, imap_client_notify_auth_ready, imap_client_notify_disconnect, imap_client_notify_status, imap_client_notify_starttls, imap_client_starttls, imap_client_input, NULL, NULL, imap_client_auth_result, imap_proxy_reset, imap_proxy_parse_line, imap_proxy_error, imap_proxy_get_state, client_common_send_raw_data, imap_client_input_next_cmd, client_common_default_free, }; static const struct login_binary imap_login_binary = { .protocol = "imap", .process_name = "imap-login", .default_port = 143, .default_ssl_port = 993, .client_vfuncs = &imap_client_vfuncs, .preinit = imap_login_preinit, .init = imap_login_init, .deinit = imap_login_deinit, .sasl_support_final_reply = FALSE }; int main(int argc, char *argv[]) { return login_binary_run(&imap_login_binary, argc, argv); } dovecot-2.2.33.2/src/imap-login/imap-login-client.h0000644000175000017500000000431113165463624016642 00000000000000#ifndef IMAP_LOGIN_CLIENT_H #define IMAP_LOGIN_CLIENT_H #include "net.h" #include "imap-id.h" #include "client-common.h" /* Master prefix is: <1|0> */ #define IMAP_TAG_MAX_LEN (LOGIN_MAX_MASTER_PREFIX_LEN-2) enum imap_client_id_state { IMAP_CLIENT_ID_STATE_LIST = 0, IMAP_CLIENT_ID_STATE_KEY, IMAP_CLIENT_ID_STATE_VALUE }; /* Multiple commands can be sent pipelined, so the sent_state is a bitmask */ enum imap_proxy_sent_state { IMAP_PROXY_SENT_STATE_ID = 0x01, IMAP_PROXY_SENT_STATE_STARTTLS = 0x02, IMAP_PROXY_SENT_STATE_CAPABILITY = 0x04, IMAP_PROXY_SENT_STATE_AUTHENTICATE = 0x08, IMAP_PROXY_SENT_STATE_AUTH_CONTINUE = 0x10, IMAP_PROXY_SENT_STATE_LOGIN = 0x20, IMAP_PROXY_SENT_STATE_COUNT = 6 }; enum imap_proxy_rcvd_state { IMAP_PROXY_RCVD_STATE_NONE, IMAP_PROXY_RCVD_STATE_BANNER, IMAP_PROXY_RCVD_STATE_ID, IMAP_PROXY_RCVD_STATE_STARTTLS, IMAP_PROXY_RCVD_STATE_CAPABILITY, IMAP_PROXY_RCVD_STATE_AUTH_CONTINUE, IMAP_PROXY_RCVD_STATE_LOGIN, IMAP_PROXY_RCVD_STATE_COUNT }; struct imap_client_cmd_id { struct imap_parser *parser; enum imap_client_id_state state; char key[IMAP_ID_KEY_MAX_LEN+1]; char **log_keys; string_t *log_reply; }; struct imap_client { struct client common; const struct imap_login_settings *set; struct imap_parser *parser; char *proxy_backend_capability; const char *cmd_tag, *cmd_name; struct imap_client_cmd_id *cmd_id; enum imap_proxy_sent_state proxy_sent_state; enum imap_proxy_rcvd_state proxy_rcvd_state; unsigned int cmd_finished:1; unsigned int proxy_sasl_ir:1; unsigned int proxy_logindisabled:1; unsigned int proxy_seen_banner:1; unsigned int skip_line:1; unsigned int id_logged:1; unsigned int proxy_capability_request_sent:1; unsigned int client_ignores_capability_resp_code:1; unsigned int auth_mech_name_parsed:1; }; bool client_skip_line(struct imap_client *client); enum imap_cmd_reply { IMAP_CMD_REPLY_OK, IMAP_CMD_REPLY_NO, IMAP_CMD_REPLY_BAD, IMAP_CMD_REPLY_BYE }; void client_send_reply(struct client *client, enum imap_cmd_reply reply, const char *text); void client_send_reply_code(struct client *client, enum imap_cmd_reply reply, const char *resp_code, const char *text) ATTR_NULL(3); #endif dovecot-2.2.33.2/src/imap-login/Makefile.am0000644000175000017500000000141713165463624015221 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = imap-login AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common imap_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) imap_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT_DEPS) imap_login_SOURCES = \ imap-login-client.c \ client-authenticate.c \ imap-login-commands.c \ imap-login-settings.c \ imap-proxy.c noinst_HEADERS = \ client-authenticate.h \ imap-proxy.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = \ imap-login-client.h \ imap-login-commands.h \ imap-login-settings.h dovecot-2.2.33.2/src/imap-login/imap-login-settings.c0000644000175000017500000000464713165463624017233 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "login-settings.h" #include "imap-login-settings.h" #include /* */ static struct inet_listener_settings imap_login_inet_listeners_array[] = { { .name = "imap", .address = "", .port = 143 }, { .name = "imaps", .address = "", .port = 993, .ssl = TRUE } }; static struct inet_listener_settings *imap_login_inet_listeners[] = { &imap_login_inet_listeners_array[0], &imap_login_inet_listeners_array[1] }; static buffer_t imap_login_inet_listeners_buf = { imap_login_inet_listeners, sizeof(imap_login_inet_listeners), { NULL, } }; /* */ struct service_settings imap_login_service_settings = { .name = "imap-login", .protocol = "imap", .type = "login", .executable = "imap-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = { { &imap_login_inet_listeners_buf, sizeof(imap_login_inet_listeners[0]) } } }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_login_settings, name), NULL } static const struct setting_define imap_login_setting_defines[] = { DEF(SET_STR, imap_capability), DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), DEF(SET_BOOL, imap_id_retain), SETTING_DEFINE_LIST_END }; static const struct imap_login_settings imap_login_default_settings = { .imap_capability = "", .imap_id_send = "name *", .imap_id_log = "", .imap_id_retain = FALSE, }; static const struct setting_parser_info *imap_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; static const struct setting_parser_info imap_login_setting_parser_info = { .module_name = "imap-login", .defines = imap_login_setting_defines, .defaults = &imap_login_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_login_settings), .parent_offset = (size_t)-1, .dependencies = imap_login_setting_dependencies }; const struct setting_parser_info *imap_login_setting_roots[] = { &login_setting_parser_info, &imap_login_setting_parser_info, NULL }; dovecot-2.2.33.2/src/lib-master/0002755000175000017500000000000013172375610013242 500000000000000dovecot-2.2.33.2/src/lib-master/master-login.c0000644000175000017500000003240513165463624015736 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "ostream.h" #include "fdpass.h" #include "fd-close-on-exec.h" #include "llist.h" #include "str.h" #include "strescape.h" #include "master-service-private.h" #include "master-login.h" #include "master-login-auth.h" #include #include #define master_login_conn_is_closed(conn) \ ((conn)->fd == -1) #define master_login_conn_has_clients(conn) \ ((conn)->refcount > 1) struct master_login_connection { struct master_login_connection *prev, *next; struct master_login *login; int refcount; int fd; struct io *io; struct ostream *output; unsigned int login_success:1; }; struct master_login_postlogin { struct master_login_client *client; int fd; struct io *io; struct timeout *to; string_t *input; char *username; char *socket_path; }; struct master_login { struct master_service *service; master_login_callback_t *callback; master_login_failure_callback_t *failure_callback; struct master_login_connection *conns; struct master_login_auth *auth; char *postlogin_socket_path; unsigned int postlogin_timeout_secs; unsigned int stopping:1; }; static void master_login_conn_close(struct master_login_connection *conn); static void master_login_conn_unref(struct master_login_connection **_conn); struct master_login * master_login_init(struct master_service *service, const struct master_login_settings *set) { struct master_login *login; i_assert(set->postlogin_socket_path == NULL || set->postlogin_timeout_secs > 0); login = i_new(struct master_login, 1); login->service = service; login->callback = set->callback; login->failure_callback = set->failure_callback; login->auth = master_login_auth_init(set->auth_socket_path, set->request_auth_token); login->postlogin_socket_path = i_strdup(set->postlogin_socket_path); login->postlogin_timeout_secs = set->postlogin_timeout_secs; i_assert(service->login == NULL); service->login = login; return login; } void master_login_deinit(struct master_login **_login) { struct master_login *login = *_login; *_login = NULL; i_assert(login->service->login == login); login->service->login = NULL; master_login_auth_deinit(&login->auth); while (login->conns != NULL) { struct master_login_connection *conn = login->conns; master_login_conn_close(conn); master_login_conn_unref(&conn); } i_free(login->postlogin_socket_path); i_free(login); } static int master_login_conn_read_request(struct master_login_connection *conn, struct master_auth_request *req_r, unsigned char data[MASTER_AUTH_MAX_DATA_SIZE], int *client_fd_r) { struct stat st; ssize_t ret; *client_fd_r = -1; ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r); if (ret != sizeof(*req_r)) { if (ret == 0) { /* disconnected */ if (master_login_conn_has_clients(conn)) i_error("Login client disconnected too early"); } else if (ret > 0) { /* request wasn't fully read */ i_error("fd_read() partial input (%d/%d)", (int)ret, (int)sizeof(*req_r)); } else { if (errno == EAGAIN) return 0; i_error("fd_read() failed: %m"); } return -1; } if (req_r->data_size != 0) { if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) { i_error("Too large auth data_size sent"); return -1; } /* @UNSAFE */ ret = read(conn->fd, data, req_r->data_size); if (ret != (ssize_t)req_r->data_size) { if (ret == 0) { /* disconnected */ if (master_login_conn_has_clients(conn)) { i_error("Login client disconnected too early " "(while reading data)"); } } else if (ret > 0) { /* request wasn't fully read */ i_error("Data read partially %d/%u", (int)ret, req_r->data_size); } else { i_error("read(data) failed: %m"); } return -1; } } if (*client_fd_r == -1) { i_error("Auth request missing a file descriptor"); return -1; } if (fstat(*client_fd_r, &st) < 0) { i_error("fstat(fd_read client) failed: %m"); return -1; } if (st.st_ino != req_r->ino) { i_error("Auth request inode mismatch: %s != %s", dec2str(st.st_ino), dec2str(req_r->ino)); return -1; } return 1; } static void master_login_client_free(struct master_login_client **_client) { struct master_login_client *client = *_client; *_client = NULL; if (client->fd != -1) { if (close(client->fd) < 0) i_error("close(fd_read client) failed: %m"); /* this client failed (login callback wasn't called). reset prefix to default. */ i_set_failure_prefix("%s: ", client->conn->login->service->name); } /* FIXME: currently we create a separate connection for each request, so close the connection after we're done with this client */ if (!master_login_conn_is_closed(client->conn)) { i_assert(client->conn->refcount > 1); client->conn->refcount--; } master_login_conn_unref(&client->conn); i_free(client->session_id); i_free(client); } static void master_login_auth_finish(struct master_login_client *client, const char *const *auth_args) { struct master_login *login = client->conn->login; struct master_service *service = login->service; bool close_sockets; close_sockets = service->master_status.available_count == 0 && service->service_count_left == 1; client->conn->login_success = TRUE; login->callback(client, auth_args[0], auth_args+1); if (close_sockets) { /* we're dying as soon as this connection closes. */ i_assert(master_login_auth_request_count(login->auth) == 0); master_login_auth_disconnect(login->auth); master_service_close_config_fd(service); } else if (login->stopping) { /* try stopping again */ master_login_stop(login); } client->fd = -1; master_login_client_free(&client); } static void master_login_postlogin_free(struct master_login_postlogin *pl) { timeout_remove(&pl->to); io_remove(&pl->io); if (close(pl->fd) < 0) i_error("close(postlogin) failed: %m"); str_free(&pl->input); i_free(pl->socket_path); i_free(pl->username); i_free(pl); } static void master_login_postlogin_input(struct master_login_postlogin *pl) { char buf[1024]; const char **auth_args, **p; size_t len; ssize_t ret; int fd = -1; while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) { if (fd != -1) { /* post-login script replaced fd */ if (close(pl->client->fd) < 0) i_error("close(client) failed: %m"); pl->client->fd = fd; } str_append_n(pl->input, buf, ret); } len = str_len(pl->input); if (len > 0 && str_c(pl->input)[len-1] == '\n') { /* finished reading the input */ str_truncate(pl->input, len-1); } else { if (ret < 0) { if (errno == EAGAIN) return; i_error("fd_read(%s) failed: %m", pl->socket_path); } else if (str_len(pl->input) > 0) { i_error("fd_read(%s) failed: disconnected", pl->socket_path); } else { i_info("Post-login script denied access to user %s", pl->username); } master_login_client_free(&pl->client); master_login_postlogin_free(pl); return; } auth_args = t_strsplit_tab(str_c(pl->input)); for (p = auth_args; *p != NULL; p++) *p = str_tabunescape(t_strdup_noconst(*p)); master_login_auth_finish(pl->client, auth_args); master_login_postlogin_free(pl); } static void master_login_postlogin_timeout(struct master_login_postlogin *pl) { i_error("%s: Timeout waiting for post-login script to finish, aborting", pl->socket_path); master_login_client_free(&pl->client); master_login_postlogin_free(pl); } static int master_login_postlogin(struct master_login_client *client, const char *const *auth_args, const char *socket_path) { struct master_login *login = client->conn->login; struct master_login_postlogin *pl; string_t *str; unsigned int i; int fd; ssize_t ret; fd = net_connect_unix_with_retries(socket_path, 1000); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m%s", socket_path, errno != EAGAIN ? "" : " - http://wiki2.dovecot.org/SocketUnavailable"); return -1; } str = t_str_new(256); str_printfa(str, "VERSION\tscript-login\t1\t0\n" "%s\t%s", net_ip2addr(&client->auth_req.local_ip), net_ip2addr(&client->auth_req.remote_ip)); for (i = 0; auth_args[i] != NULL; i++) { str_append_c(str, '\t'); str_append_tabescaped(str, auth_args[i]); } str_append_c(str, '\n'); ret = fd_send(fd, client->fd, str_data(str), str_len(str)); if (ret != (ssize_t)str_len(str)) { if (ret < 0) { i_error("write(%s) failed: %m", socket_path); } else { i_error("write(%s) failed: partial write", socket_path); } i_close_fd(&fd); return -1; } net_set_nonblock(fd, TRUE); pl = i_new(struct master_login_postlogin, 1); pl->client = client; pl->username = i_strdup(auth_args[0]); pl->socket_path = i_strdup(socket_path); pl->fd = fd; pl->io = io_add(fd, IO_READ, master_login_postlogin_input, pl); pl->to = timeout_add(login->postlogin_timeout_secs * 1000, master_login_postlogin_timeout, pl); pl->input = str_new(default_pool, 512); return 0; } static const char * auth_args_find_postlogin_socket(const char *const *auth_args) { for (unsigned int i = 0; auth_args[i] != NULL; i++) { if (strncmp(auth_args[i], "postlogin=", 10) == 0) return auth_args[i]+10; } return NULL; } static void master_login_auth_callback(const char *const *auth_args, const char *errormsg, void *context) { struct master_login_client *client = context; struct master_login_connection *conn = client->conn; struct master_auth_reply reply; const char *postlogin_socket_path; i_zero(&reply); reply.tag = client->auth_req.tag; reply.status = errormsg == NULL ? MASTER_AUTH_STATUS_OK : MASTER_AUTH_STATUS_INTERNAL_ERROR; reply.mail_pid = getpid(); o_stream_nsend(conn->output, &reply, sizeof(reply)); if (errormsg != NULL || auth_args[0] == NULL) { if (auth_args != NULL) { i_error("login client: Username missing from auth reply"); errormsg = MASTER_AUTH_ERRMSG_INTERNAL_FAILURE; } conn->login->failure_callback(client, errormsg); master_login_client_free(&client); return; } i_set_failure_prefix("%s(%s): ", client->conn->login->service->name, auth_args[0]); postlogin_socket_path = auth_args_find_postlogin_socket(auth_args); if (postlogin_socket_path == NULL) postlogin_socket_path = conn->login->postlogin_socket_path; if (postlogin_socket_path == NULL) master_login_auth_finish(client, auth_args); else { /* we've sent the reply. the connection is no longer needed, so disconnect it (before login process disconnects us and logs an error) */ master_login_conn_close(conn); master_login_conn_unref(&conn); /* execute post-login scripts before finishing auth */ if (master_login_postlogin(client, auth_args, postlogin_socket_path) < 0) master_login_client_free(&client); } } static void master_login_conn_input(struct master_login_connection *conn) { struct master_auth_request req; struct master_login_client *client; struct master_login *login = conn->login; unsigned char data[MASTER_AUTH_MAX_DATA_SIZE]; size_t i, session_len = 0; int ret, client_fd; ret = master_login_conn_read_request(conn, &req, data, &client_fd); if (ret <= 0) { if (ret < 0) { master_login_conn_close(conn); master_login_conn_unref(&conn); } if (client_fd != -1) { if (close(client_fd) < 0) i_error("close(fd_read client) failed: %m"); } return; } fd_close_on_exec(client_fd, TRUE); /* extract the session ID from the request data */ for (i = 0; i < req.data_size; i++) { if (data[i] == '\0') { session_len = i++; break; } } /* @UNSAFE: we have a request. do userdb lookup for it. */ req.data_size -= i; client = i_malloc(MALLOC_ADD(sizeof(struct master_login_client), req.data_size)); client->conn = conn; client->fd = client_fd; client->auth_req = req; client->session_id = i_strndup(data, session_len); memcpy(client->data, data+i, req.data_size); conn->refcount++; master_login_auth_request(login->auth, &req, master_login_auth_callback, client); } void master_login_add(struct master_login *login, int fd) { struct master_login_connection *conn; conn = i_new(struct master_login_connection, 1); conn->refcount = 1; conn->login = login; conn->fd = fd; conn->io = io_add(conn->fd, IO_READ, master_login_conn_input, conn); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); DLLIST_PREPEND(&login->conns, conn); /* NOTE: currently there's a separate connection for each request. */ } static void master_login_conn_close(struct master_login_connection *conn) { if (master_login_conn_is_closed(conn)) return; DLLIST_REMOVE(&conn->login->conns, conn); if (conn->io != NULL) io_remove(&conn->io); o_stream_close(conn->output); if (close(conn->fd) < 0) i_error("close(master login) failed: %m"); conn->fd = -1; } static void master_login_conn_unref(struct master_login_connection **_conn) { struct master_login_connection *conn = *_conn; i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; *_conn = NULL; master_login_conn_close(conn); o_stream_unref(&conn->output); if (!conn->login_success) master_service_client_connection_destroyed(conn->login->service); i_free(conn); } void master_login_stop(struct master_login *login) { login->stopping = TRUE; if (master_login_auth_request_count(login->auth) == 0) { master_login_auth_disconnect(login->auth); master_service_close_config_fd(login->service); } } dovecot-2.2.33.2/src/lib-master/master-service-ssl-settings.h0000644000175000017500000000157213165463624020731 00000000000000#ifndef MASTER_SERVICE_SSL_SETTINGS_H #define MASTER_SERVICE_SSL_SETTINGS_H struct master_service; struct master_service_ssl_settings { const char *ssl; const char *ssl_ca; const char *ssl_cert; const char *ssl_alt_cert; const char *ssl_key; const char *ssl_alt_key; const char *ssl_key_password; const char *ssl_cipher_list; const char *ssl_protocols; const char *ssl_cert_username_field; const char *ssl_crypto_device; const char *ssl_options; bool ssl_verify_client_cert; bool ssl_require_crl; bool verbose_ssl; bool ssl_prefer_server_ciphers; /* These are derived from ssl_options, not set directly */ struct { bool compression; bool tickets; } parsed_opts; }; extern const struct setting_parser_info master_service_ssl_setting_parser_info; const struct master_service_ssl_settings * master_service_ssl_settings_get(struct master_service *service); #endif dovecot-2.2.33.2/src/lib-master/anvil-client.c0000644000175000017500000001551413165463624015724 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "array.h" #include "aqueue.h" #include "anvil-client.h" struct anvil_query { anvil_callback_t *callback; void *context; }; struct anvil_client { char *path; int fd; struct istream *input; struct ostream *output; struct io *io; struct timeout *to_query; struct timeout *to_reconnect; time_t last_reconnect; ARRAY(struct anvil_query *) queries_arr; struct aqueue *queries; bool (*reconnect_callback)(void); enum anvil_client_flags flags; }; #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" #define ANVIL_INBUF_SIZE 1024 #define ANVIL_RECONNECT_MIN_SECS 5 #define ANVIL_QUERY_TIMEOUT_MSECS (1000*5) static void anvil_client_disconnect(struct anvil_client *client); struct anvil_client * anvil_client_init(const char *path, bool (*reconnect_callback)(void), enum anvil_client_flags flags) { struct anvil_client *client; client = i_new(struct anvil_client, 1); client->path = i_strdup(path); client->reconnect_callback = reconnect_callback; client->flags = flags; client->fd = -1; i_array_init(&client->queries_arr, 32); client->queries = aqueue_init(&client->queries_arr.arr); return client; } void anvil_client_deinit(struct anvil_client **_client) { struct anvil_client *client = *_client; *_client = NULL; anvil_client_disconnect(client); array_free(&client->queries_arr); aqueue_deinit(&client->queries); i_free(client->path); i_assert(client->to_reconnect == NULL); i_free(client); } static void anvil_reconnect(struct anvil_client *client) { anvil_client_disconnect(client); if (client->reconnect_callback != NULL) { if (!client->reconnect_callback()) { /* no reconnection */ return; } } if (ioloop_time - client->last_reconnect < ANVIL_RECONNECT_MIN_SECS) { if (client->to_reconnect == NULL) { client->to_reconnect = timeout_add(ANVIL_RECONNECT_MIN_SECS*1000, anvil_reconnect, client); } } else { client->last_reconnect = ioloop_time; (void)anvil_client_connect(client, FALSE); } } static void anvil_input(struct anvil_client *client) { struct anvil_query *const *queries; struct anvil_query *query; const char *line; unsigned int count; queries = array_get(&client->queries_arr, &count); while ((line = i_stream_read_next_line(client->input)) != NULL) { if (aqueue_count(client->queries) == 0) { i_error("anvil: Unexpected input: %s", line); continue; } query = queries[aqueue_idx(client->queries, 0)]; if (query->callback != NULL) T_BEGIN { query->callback(line, query->context); } T_END; i_free(query); aqueue_delete_tail(client->queries); } if (client->input->stream_errno != 0) { i_error("read(%s) failed: %s", client->path, i_stream_get_error(client->input)); anvil_reconnect(client); } else if (client->input->eof) { i_error("read(%s) failed: EOF", client->path); anvil_reconnect(client); } else if (client->to_query != NULL) { if (aqueue_count(client->queries) == 0) timeout_remove(&client->to_query); else timeout_reset(client->to_query); } } int anvil_client_connect(struct anvil_client *client, bool retry) { int fd; i_assert(client->fd == -1); fd = retry ? net_connect_unix_with_retries(client->path, 5000) : net_connect_unix(client->path); if (fd == -1) { if (errno != ENOENT || (client->flags & ANVIL_CLIENT_FLAG_HIDE_ENOENT) == 0) { i_error("net_connect_unix(%s) failed: %m", client->path); } return -1; } if (client->to_reconnect != NULL) timeout_remove(&client->to_reconnect); client->fd = fd; client->input = i_stream_create_fd(fd, ANVIL_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); client->io = io_add(fd, IO_READ, anvil_input, client); if (o_stream_send_str(client->output, ANVIL_HANDSHAKE) < 0) { i_error("write(%s) failed: %s", client->path, o_stream_get_error(client->output)); anvil_reconnect(client); return -1; } return 0; } static void anvil_client_cancel_queries(struct anvil_client *client) { struct anvil_query *const *queries, *query; unsigned int count; queries = array_get(&client->queries_arr, &count); while (aqueue_count(client->queries) > 0) { query = queries[aqueue_idx(client->queries, 0)]; if (query->callback != NULL) query->callback(NULL, query->context); i_free(query); aqueue_delete_tail(client->queries); } if (client->to_query != NULL) timeout_remove(&client->to_query); } static void anvil_client_disconnect(struct anvil_client *client) { anvil_client_cancel_queries(client); if (client->fd != -1) { io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); net_disconnect(client->fd); client->fd = -1; } if (client->to_reconnect != NULL) timeout_remove(&client->to_reconnect); } static void anvil_client_timeout(struct anvil_client *client) { i_assert(aqueue_count(client->queries) > 0); i_error("%s: Anvil queries timed out after %u secs - aborting queries", client->path, ANVIL_QUERY_TIMEOUT_MSECS/1000); /* perhaps reconnect helps */ anvil_reconnect(client); } static int anvil_client_send(struct anvil_client *client, const char *cmd) { struct const_iovec iov[2]; if (client->fd == -1) { if (anvil_client_connect(client, FALSE) < 0) return -1; } iov[0].iov_base = cmd; iov[0].iov_len = strlen(cmd); iov[1].iov_base = "\n"; iov[1].iov_len = 1; if (o_stream_sendv(client->output, iov, 2) < 0) { i_error("write(%s) failed: %s", client->path, o_stream_get_error(client->output)); anvil_reconnect(client); return -1; } return 0; } struct anvil_query * anvil_client_query(struct anvil_client *client, const char *query, anvil_callback_t *callback, void *context) { struct anvil_query *anvil_query; if (anvil_client_send(client, query) < 0) { callback(NULL, context); return NULL; } anvil_query = i_new(struct anvil_query, 1); anvil_query->callback = callback; anvil_query->context = context; aqueue_append(client->queries, &anvil_query); if (client->to_query == NULL) { client->to_query = timeout_add(ANVIL_QUERY_TIMEOUT_MSECS, anvil_client_timeout, client); } return anvil_query; } void anvil_client_query_abort(struct anvil_client *client, struct anvil_query **_query) { struct anvil_query *query = *_query; struct anvil_query *const *queries; unsigned int i, count; *_query = NULL; count = aqueue_count(client->queries); queries = array_idx(&client->queries_arr, 0); for (i = 0; i < count; i++) { if (queries[aqueue_idx(client->queries, i)] == query) { query->callback = NULL; return; } } i_panic("anvil query to be aborted doesn't exist"); } void anvil_client_cmd(struct anvil_client *client, const char *cmd) { (void)anvil_client_send(client, cmd); } bool anvil_client_is_connected(struct anvil_client *client) { return client->fd != -1; } dovecot-2.2.33.2/src/lib-master/ipc-server.h0000644000175000017500000000124013123174404015377 00000000000000#ifndef IPC_SERVER_H #define IPC_SERVER_H struct ipc_cmd; /* The callback must eventually free the cmd by calling ip_cmd_success/fail(). line is guaranteed to be non-empty. */ typedef void ipc_command_callback_t(struct ipc_cmd *cmd, const char *line); struct ipc_server * ipc_server_init(const char *ipc_socket_path, const char *name, ipc_command_callback_t *callback); void ipc_server_deinit(struct ipc_server **server); void ipc_cmd_send(struct ipc_cmd *cmd, const char *data); void ipc_cmd_success(struct ipc_cmd **cmd); void ipc_cmd_success_reply(struct ipc_cmd **cmd, const char *data); void ipc_cmd_fail(struct ipc_cmd **cmd, const char *errormsg); #endif dovecot-2.2.33.2/src/lib-master/Makefile.in0000644000175000017500000006401313172375573015241 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-master ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libmaster_la_LIBADD = am_libmaster_la_OBJECTS = anvil-client.lo ipc-client.lo ipc-server.lo \ master-auth.lo master-instance.lo master-login.lo \ master-login-auth.lo master-service.lo \ master-service-haproxy.lo master-service-settings.lo \ master-service-settings-cache.lo master-service-ssl.lo \ master-service-ssl-settings.lo mountpoint-list.lo \ syslog-util.lo libmaster_la_OBJECTS = $(am_libmaster_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-master-service-settings-cache$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_master_service_settings_cache_OBJECTS = \ test-master-service-settings-cache.$(OBJEXT) test_master_service_settings_cache_OBJECTS = \ $(am_test_master_service_settings_cache_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libmaster_la_SOURCES) \ $(test_master_service_settings_cache_SOURCES) DIST_SOURCES = $(libmaster_la_SOURCES) \ $(test_master_service_settings_cache_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkgsysconfdir = $(sysconfdir)/dovecot noinst_LTLIBRARIES = libmaster.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DSYSCONFDIR=\""$(pkgsysconfdir)"\" \ -DBINDIR=\""$(bindir)"\" libmaster_la_SOURCES = \ anvil-client.c \ ipc-client.c \ ipc-server.c \ master-auth.c \ master-instance.c \ master-login.c \ master-login-auth.c \ master-service.c \ master-service-haproxy.c \ master-service-settings.c \ master-service-settings-cache.c \ master-service-ssl.c \ master-service-ssl-settings.c \ mountpoint-list.c \ syslog-util.c headers = \ anvil-client.h \ ipc-client.h \ ipc-server.h \ master-auth.h \ master-instance.h \ master-interface.h \ master-login.h \ master-login-auth.h \ master-service.h \ master-service-private.h \ master-service-settings.h \ master-service-settings-cache.h \ master-service-ssl.h \ master-service-ssl-settings.h \ service-settings.h \ mountpoint-list.h \ syslog-util.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-master-service-settings-cache test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_master_service_settings_cache_SOURCES = test-master-service-settings-cache.c test_master_service_settings_cache_LDADD = master-service-settings-cache.lo ../lib-settings/libsettings.la $(test_libs) test_master_service_settings_cache_DEPENDENCIES = $(test_deps) ../lib-settings/libsettings.la all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-master/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-master/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libmaster.la: $(libmaster_la_OBJECTS) $(libmaster_la_DEPENDENCIES) $(EXTRA_libmaster_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libmaster_la_OBJECTS) $(libmaster_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-master-service-settings-cache$(EXEEXT): $(test_master_service_settings_cache_OBJECTS) $(test_master_service_settings_cache_DEPENDENCIES) $(EXTRA_test_master_service_settings_cache_DEPENDENCIES) @rm -f test-master-service-settings-cache$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_master_service_settings_cache_OBJECTS) $(test_master_service_settings_cache_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/anvil-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-auth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-instance.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-login-auth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-login.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service-haproxy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service-settings-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service-ssl-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service-ssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-service.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountpoint-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-master-service-settings-cache.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-master/mountpoint-list.h0000644000175000017500000000544613165463624016534 00000000000000#ifndef MOUNTPOINT_LIST_H #define MOUNTPOINT_LIST_H #define MOUNTPOINT_LIST_FNAME "mounts" #define MOUNTPOINT_STATE_DEFAULT "online" #define MOUNTPOINT_STATE_IGNORE "ignore" #define MOUNTPOINT_WRONGLY_NOT_MOUNTED(rec) \ (!(rec)->mounted && !(rec)->wildcard && \ strcmp((rec)->state, MOUNTPOINT_STATE_IGNORE) != 0) struct mountpoint_list_rec { const char *mount_path; const char *state; /* _add_missing() skips all mountpoints below this mount_path */ bool wildcard; /* TRUE, if _add_missing() saw this mount */ bool mounted; }; /* A default known good list of mountpoint types that don't contain emails (e.g. proc, tmpfs, etc.) */ extern const char *const mountpoint_list_default_ignore_types[]; /* A default known good list of directories which shouldn't contain emails (e.g. /media) */ extern const char *const mountpoint_list_default_ignore_prefixes[]; struct mountpoint_list * mountpoint_list_init(const char *perm_path, const char *state_path); struct mountpoint_list * mountpoint_list_init_readonly(const char *state_path); void mountpoint_list_deinit(struct mountpoint_list **list); /* Reload the mountpoints if they have changed. Returns 0 if ok, -1 if I/O error. */ int mountpoint_list_refresh(struct mountpoint_list *list); /* Save the current list of mountpoints. Returns 0 if successful, -1 if I/O error. */ int mountpoint_list_save(struct mountpoint_list *list); /* Add a mountpoint. If it already exists, replace the old one. */ void mountpoint_list_add(struct mountpoint_list *list, const struct mountpoint_list_rec *rec); /* Remove a mountpoint. Returns TRUE if mountpoint was found and removed. */ bool mountpoint_list_remove(struct mountpoint_list *list, const char *mount_path); /* Add all currently mounted missing mountpoints to the list and update all mountpoints' mounted state. The mountpoints that match existing wildcards aren't added. Mountpoints with paths under ignore_prefixes aren't added. Mountpoints with type in ignore_types list also aren't added. Returns 0 if we successfully iterated through all mountpoints, -1 if not. */ int mountpoint_list_add_missing(struct mountpoint_list *list, const char *default_state, const char *const *ignore_prefixes, const char *const *ignore_types); /* Update "mounted" status for all mountpoints. */ int mountpoint_list_update_mounted(struct mountpoint_list *list); /* Find a mountpoint record for given path. */ struct mountpoint_list_rec * mountpoint_list_find(struct mountpoint_list *list, const char *path); /* Iterate through all mountpoints in the list. */ struct mountpoint_list_iter * mountpoint_list_iter_init(struct mountpoint_list *list); struct mountpoint_list_rec * mountpoint_list_iter_next(struct mountpoint_list_iter *iter); void mountpoint_list_iter_deinit(struct mountpoint_list_iter **iter); #endif dovecot-2.2.33.2/src/lib-master/master-service.c0000644000175000017500000010606413167445165016273 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "ioloop.h" #include "abspath.h" #include "array.h" #include "strescape.h" #include "env-util.h" #include "home-expand.h" #include "process-title.h" #include "restrict-access.h" #include "fd-close-on-exec.h" #include "settings-parser.h" #include "syslog-util.h" #include "master-instance.h" #include "master-login.h" #include "master-service-ssl.h" #include "master-service-private.h" #include "master-service-settings.h" #include #include #include #define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf" /* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */ #define MASTER_CONFIG_FILE_ENV "CONFIG_FILE" /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */ #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION" /* when we're full of connections, how often to check if login state has changed. we normally notice it immediately because of a signal, so this is just a fallback against race conditions. */ #define MASTER_SERVICE_STATE_CHECK_MSECS 1000 /* If die callback hasn't managed to stop the service for this many seconds, force it. */ #define MASTER_SERVICE_DIE_TIMEOUT_MSECS (30*1000) struct master_service *master_service; static void master_service_io_listeners_close(struct master_service *service); static void master_service_refresh_login_state(struct master_service *service); static void master_status_send(struct master_service *service, bool important_update); const char *master_service_getopt_string(void) { return "c:i:ko:OL"; } static void sig_die(const siginfo_t *si, void *context) { struct master_service *service = context; /* SIGINT comes either from master process or from keyboard. we don't want to log it in either case.*/ if (si->si_signo != SIGINT) { i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)", si->si_signo, dec2str(si->si_pid), dec2str(si->si_uid), lib_signal_code_to_str(si->si_signo, si->si_code)); } else if ((service->flags & MASTER_SERVICE_FLAG_NO_IDLE_DIE) != 0) { /* never die when idling */ return; } else if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { /* SIGINT came from master. die only if we're not handling any clients currently. */ if (service->master_status.available_count != service->total_available_count) return; if (service->idle_die_callback != NULL && !service->idle_die_callback()) { /* we don't want to die - send a notification to master so it doesn't think we're ignoring it completely. */ master_status_send(service, FALSE); return; } } service->killed = TRUE; io_loop_stop(service->ioloop); } static void sig_close_listeners(const siginfo_t *si ATTR_UNUSED, void *context) { struct master_service *service = context; /* We're in a signal handler: Close listeners immediately so master can successfully restart. We can safely close only those listeners that don't have an io, but this shouldn't be a big problem. If there is an active io, the service is unlikely to be unresposive for longer periods of time, so the listener gets closed soon enough via master_status_error(). For extra safety we don't actually close() the fd, but instead replace it with /dev/null. This way it won't be replaced with some other new fd and attempted to be used in unexpected ways. */ for (unsigned int i = 0; i < service->socket_count; i++) { if (service->listeners[i].fd != -1 && service->listeners[i].io == NULL) { if (dup2(dev_null_fd, service->listeners[i].fd) < 0) lib_signals_syscall_error("signal: dup2(/dev/null, listener) failed: "); service->listeners[i].closed = TRUE; } } } static void sig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context) { struct master_service *service = context; master_service_refresh_login_state(service); } static void master_service_verify_version_string(struct master_service *service) { if (service->version_string != NULL && strcmp(service->version_string, PACKAGE_VERSION) != 0) { i_fatal("Dovecot version mismatch: " "Master is v%s, %s is v"PACKAGE_VERSION" " "(if you don't care, set version_ignore=yes)", service->version_string, service->name); } } static void master_service_init_socket_listeners(struct master_service *service) { unsigned int i; const char *value; bool have_ssl_sockets = FALSE; if (service->socket_count == 0) return; service->listeners = i_new(struct master_service_listener, service->socket_count); for (i = 0; i < service->socket_count; i++) { struct master_service_listener *l = &service->listeners[i]; l->service = service; l->fd = MASTER_LISTEN_FD_FIRST + i; value = getenv(t_strdup_printf("SOCKET%u_SETTINGS", i)); if (value != NULL) { const char *const *settings = t_strsplit_tabescaped(value); if (*settings != NULL) { l->name = i_strdup_empty(*settings); settings++; } while (*settings != NULL) { if (strcmp(*settings, "ssl") == 0) { l->ssl = TRUE; have_ssl_sockets = TRUE; } else if (strcmp(*settings, "haproxy") == 0) { l->haproxy = TRUE; } settings++; } } } service->want_ssl_settings = have_ssl_sockets || (service->flags & MASTER_SERVICE_FLAG_USE_SSL_SETTINGS) != 0; } struct master_service * master_service_init(const char *name, enum master_service_flags flags, int *argc, char **argv[], const char *getopt_str) { struct master_service *service; unsigned int count; const char *value; i_assert(name != NULL); #ifdef DEBUG if (getenv("GDB") == NULL && (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { value = getenv("SOCKET_COUNT"); if (value == NULL || str_to_uint(value, &count) < 0) count = 0; fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024); } #endif if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { /* make sure we can dump core, at least until privileges are dropped. (i'm not really sure why this is needed, because doing the same just before exec doesn't help, and exec shouldn't affect this with non-setuid/gid binaries..) */ restrict_access_allow_coredumps(TRUE); } /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ lib_init(); /* Set a logging prefix temporarily. This will be ignored once the log is properly initialized */ i_set_failure_prefix("%s(init): ", name); /* ignore these signals as early as possible */ lib_signals_init(); lib_signals_ignore(SIGPIPE, TRUE); lib_signals_ignore(SIGALRM, FALSE); if (getenv(MASTER_UID_ENV) == NULL) flags |= MASTER_SERVICE_FLAG_STANDALONE; process_title_init(argv); service = i_new(struct master_service, 1); service->argc = *argc; service->argv = *argv; service->name = i_strdup(name); /* keep getopt_str first in case it contains "+" */ service->getopt_str = *getopt_str == '\0' ? i_strdup(master_service_getopt_string()) : i_strconcat(getopt_str, master_service_getopt_string(), NULL); service->flags = flags; service->ioloop = io_loop_create(); service->service_count_left = UINT_MAX; service->config_fd = -1; service->config_path = i_strdup(getenv(MASTER_CONFIG_FILE_ENV)); if (service->config_path == NULL) service->config_path = i_strdup(DEFAULT_CONFIG_FILE_PATH); else service->config_path_from_master = TRUE; if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV); service->socket_count = 1; } else { service->version_string = PACKAGE_VERSION; } /* listener configuration */ value = getenv("SOCKET_COUNT"); if (value != NULL && str_to_uint(value, &service->socket_count) < 0) i_fatal("Invalid SOCKET_COUNT environment"); T_BEGIN { master_service_init_socket_listeners(service); } T_END; /* set up some kind of logging until we know exactly how and where we want to log */ if (getenv("LOG_SERVICE") != NULL) i_set_failure_internal(); if (getenv("USER") != NULL) i_set_failure_prefix("%s(%s): ", name, getenv("USER")); else i_set_failure_prefix("%s: ", name); if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { /* initialize master_status structure */ value = getenv(MASTER_UID_ENV); if (value == NULL || str_to_uint(value, &service->master_status.uid) < 0) i_fatal(MASTER_UID_ENV" missing"); service->master_status.pid = getpid(); /* set the default limit */ value = getenv(MASTER_CLIENT_LIMIT_ENV); if (value == NULL || str_to_uint(value, &count) < 0 || count == 0) i_fatal(MASTER_CLIENT_LIMIT_ENV" missing"); master_service_set_client_limit(service, count); /* seve the process limit */ value = getenv(MASTER_PROCESS_LIMIT_ENV); if (value != NULL && str_to_uint(value, &count) == 0 && count > 0) service->process_limit = count; value = getenv(MASTER_PROCESS_MIN_AVAIL_ENV); if (value != NULL && str_to_uint(value, &count) == 0 && count > 0) service->process_min_avail = count; /* set the default service count */ value = getenv(MASTER_SERVICE_COUNT_ENV); if (value != NULL && str_to_uint(value, &count) == 0 && count > 0) master_service_set_service_count(service, count); /* set the idle kill timeout */ value = getenv(MASTER_SERVICE_IDLE_KILL_ENV); if (value != NULL && str_to_uint(value, &count) == 0) service->idle_kill_secs = count; } else { master_service_set_client_limit(service, 1); master_service_set_service_count(service, 1); } if ((flags & MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN) != 0) { /* since we're going to keep the config socket open anyway, open it now so we can read settings even after privileges are dropped. */ master_service_config_socket_try_open(service); } master_service_verify_version_string(service); return service; } int master_getopt(struct master_service *service) { int c; i_assert(master_getopt_str_is_valid(service->getopt_str)); while ((c = getopt(service->argc, service->argv, service->getopt_str)) > 0) { if (!master_service_parse_option(service, c, optarg)) break; } return c; } bool master_getopt_str_is_valid(const char *str) { unsigned int i, j; /* make sure there are no duplicates. there are few enough characters that this should be fast enough. */ for (i = 0; str[i] != '\0'; i++) { if (str[i] == ':' || str[i] == '+' || str[i] == '-') continue; for (j = i+1; str[j] != '\0'; j++) { if (str[i] == str[j]) return FALSE; } } return TRUE; } static bool master_service_try_init_log(struct master_service *service, const char *prefix) { const char *path, *timestamp; if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 && (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) { timestamp = getenv("LOG_STDERR_TIMESTAMP"); if (timestamp != NULL) i_set_failure_timestamp_format(timestamp); i_set_failure_file("/dev/stderr", ""); return TRUE; } if (getenv("LOG_SERVICE") != NULL && !service->log_directly) { /* logging via log service */ i_set_failure_internal(); i_set_failure_prefix("%s", prefix); return TRUE; } if (service->set == NULL) { i_set_failure_file("/dev/stderr", prefix); /* may be called again after we have settings */ return FALSE; } if (strcmp(service->set->log_path, "syslog") != 0) { /* error logging goes to file or stderr */ path = home_expand(service->set->log_path); i_set_failure_file(path, prefix); } if (strcmp(service->set->log_path, "syslog") == 0 || strcmp(service->set->info_log_path, "syslog") == 0 || strcmp(service->set->debug_log_path, "syslog") == 0) { /* something gets logged to syslog */ int facility; if (!syslog_facility_find(service->set->syslog_facility, &facility)) facility = LOG_MAIL; i_set_failure_syslog("dovecot", LOG_NDELAY, facility); i_set_failure_prefix("%s", prefix); if (strcmp(service->set->log_path, "syslog") != 0) { /* set error handlers back to file */ i_set_fatal_handler(default_fatal_handler); i_set_error_handler(default_error_handler); } } if (*service->set->info_log_path != '\0' && strcmp(service->set->info_log_path, "syslog") != 0) { path = home_expand(service->set->info_log_path); if (*path != '\0') i_set_info_file(path); } if (*service->set->debug_log_path != '\0' && strcmp(service->set->debug_log_path, "syslog") != 0) { path = home_expand(service->set->debug_log_path); if (*path != '\0') i_set_debug_file(path); } i_set_failure_timestamp_format(service->set->log_timestamp); return TRUE; } void master_service_init_log(struct master_service *service, const char *prefix) { if (service->log_initialized) { /* change only the prefix */ i_set_failure_prefix("%s", prefix); return; } if (master_service_try_init_log(service, prefix)) service->log_initialized = TRUE; } void master_service_set_die_with_master(struct master_service *service, bool set) { service->die_with_master = set; } void master_service_set_die_callback(struct master_service *service, void (*callback)(void)) { service->die_callback = callback; } void master_service_set_idle_die_callback(struct master_service *service, bool (*callback)(void)) { service->idle_die_callback = callback; } static bool get_instance_config(const char *name, const char **config_path_r) { struct master_instance_list *list; const struct master_instance *inst; const char *instance_path, *path; /* note that we don't have any settings yet. we're just finding out which dovecot.conf we even want to read! so we must use the hardcoded state_dir path. */ instance_path = t_strconcat(PKG_STATEDIR"/"MASTER_INSTANCE_FNAME, NULL); list = master_instance_list_init(instance_path); inst = master_instance_list_find_by_name(list, name); if (inst != NULL) { path = t_strdup_printf("%s/dovecot.conf", inst->base_dir); if (t_readlink(path, config_path_r) < 0) i_fatal("readlink(%s) failed: %m", path); } master_instance_list_deinit(&list); return inst != NULL; } bool master_service_parse_option(struct master_service *service, int opt, const char *arg) { const char *path; switch (opt) { case 'c': i_free(service->config_path); service->config_path = i_strdup(arg); service->config_path_changed_with_param = TRUE; break; case 'i': if (!get_instance_config(arg, &path)) i_fatal("Unknown instance name: %s", arg); service->config_path = i_strdup(path); service->config_path_changed_with_param = TRUE; break; case 'k': service->keep_environment = TRUE; break; case 'o': if (!array_is_created(&service->config_overrides)) i_array_init(&service->config_overrides, 16); array_append(&service->config_overrides, &arg, 1); break; case 'O': service->flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS; break; case 'L': service->log_directly = TRUE; break; default: return FALSE; } return TRUE; } static void master_service_error(struct master_service *service) { master_service_stop_new_connections(service); if (service->master_status.available_count == service->total_available_count || service->die_with_master) { if (service->die_callback == NULL) master_service_stop(service); else { service->to_die = timeout_add(MASTER_SERVICE_DIE_TIMEOUT_MSECS, master_service_stop, service); service->die_callback(); } } } static void master_status_error(struct master_service *service) { /* status fd is a write-only pipe, so if we're here it means the master wants us to die (or died itself). don't die until all service connections are finished. */ io_remove(&service->io_status_error); /* the log fd may also be closed already, don't die when trying to log later */ i_set_failure_ignore_errors(TRUE); master_service_error(service); } void master_service_init_finish(struct master_service *service) { enum libsig_flags sigint_flags = LIBSIG_FLAG_DELAYED; struct stat st; /* set default signal handlers */ if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) sigint_flags |= LIBSIG_FLAG_RESTART; lib_signals_set_handler(SIGINT, sigint_flags, sig_die, service); lib_signals_set_handler(SIGTERM, LIBSIG_FLAG_DELAYED, sig_die, service); if ((service->flags & MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE) != 0) { lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE, sig_state_changed, service); } if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode)) i_fatal("Must be started by dovecot master process"); /* start listening errors for status fd, it means master died */ service->io_status_error = io_add(MASTER_DEAD_FD, IO_ERROR, master_status_error, service); lib_signals_set_handler(SIGQUIT, 0, sig_close_listeners, service); } master_service_io_listeners_add(service); if (service->want_ssl_settings && (service->flags & MASTER_SERVICE_FLAG_NO_SSL_INIT) == 0) master_service_ssl_ctx_init(service); if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) { /* we already have a connection to be served */ service->master_status.available_count--; } master_status_update(service); } static void master_service_import_environment_real(const char *import_environment) { const char *const *envs, *key, *value; ARRAY_TYPE(const_string) keys; if (*import_environment == '\0') return; t_array_init(&keys, 8); /* preserve existing DOVECOT_PRESERVE_ENVS */ value = getenv(DOVECOT_PRESERVE_ENVS_ENV); if (value != NULL) array_append(&keys, &value, 1); /* add new environments */ envs = t_strsplit_spaces(import_environment, " "); for (; *envs != NULL; envs++) { value = strchr(*envs, '='); if (value == NULL) key = *envs; else { key = t_strdup_until(*envs, value); env_put(*envs); } array_append(&keys, &key, 1); } array_append_zero(&keys); value = t_strarray_join(array_idx(&keys, 0), " "); env_put(t_strconcat(DOVECOT_PRESERVE_ENVS_ENV"=", value, NULL)); } void master_service_import_environment(const char *import_environment) { T_BEGIN { master_service_import_environment_real(import_environment); } T_END; } void master_service_env_clean(void) { const char *value = getenv(DOVECOT_PRESERVE_ENVS_ENV); if (value == NULL || *value == '\0') env_clean(); else T_BEGIN { value = t_strconcat(value, " "DOVECOT_PRESERVE_ENVS_ENV, NULL); env_clean_except(t_strsplit_spaces(value, " ")); } T_END; } void master_service_set_client_limit(struct master_service *service, unsigned int client_limit) { unsigned int used; i_assert(service->master_status.available_count == service->total_available_count); used = service->total_available_count - service->master_status.available_count; i_assert(client_limit >= used); service->total_available_count = client_limit; service->master_status.available_count = client_limit - used; } unsigned int master_service_get_client_limit(struct master_service *service) { return service->total_available_count; } unsigned int master_service_get_process_limit(struct master_service *service) { return service->process_limit; } unsigned int master_service_get_process_min_avail(struct master_service *service) { return service->process_min_avail; } unsigned int master_service_get_idle_kill_secs(struct master_service *service) { return service->idle_kill_secs; } void master_service_set_service_count(struct master_service *service, unsigned int count) { unsigned int used; used = service->total_available_count - service->master_status.available_count; i_assert(count >= used); if (service->total_available_count > count) { service->total_available_count = count; service->master_status.available_count = count - used; } service->service_count_left = count; } unsigned int master_service_get_service_count(struct master_service *service) { return service->service_count_left; } unsigned int master_service_get_socket_count(struct master_service *service) { return service->socket_count; } const char *master_service_get_socket_name(struct master_service *service, int listen_fd) { unsigned int i; i_assert(listen_fd >= MASTER_LISTEN_FD_FIRST); i = listen_fd - MASTER_LISTEN_FD_FIRST; i_assert(i < service->socket_count); return service->listeners[i].name != NULL ? service->listeners[i].name : ""; } void master_service_set_avail_overflow_callback(struct master_service *service, void (*callback)(void)) { service->avail_overflow_callback = callback; } const char *master_service_get_config_path(struct master_service *service) { return service->config_path; } const char *master_service_get_version_string(struct master_service *service) { return service->version_string; } const char *master_service_get_name(struct master_service *service) { return service->name; } void master_service_run(struct master_service *service, master_service_connection_callback_t *callback) { service->callback = callback; io_loop_run(service->ioloop); service->callback = NULL; } void master_service_stop(struct master_service *service) { io_loop_stop(service->ioloop); } void master_service_stop_new_connections(struct master_service *service) { unsigned int current_count; if (service->stopping) return; service->stopping = TRUE; master_service_io_listeners_remove(service); master_service_io_listeners_close(service); /* make sure we stop after servicing current connections */ current_count = service->total_available_count - service->master_status.available_count; service->service_count_left = current_count; service->total_available_count = current_count; if (current_count == 0) master_service_stop(service); else { /* notify master that we're not accepting any more connections */ service->master_status.available_count = 0; master_status_update(service); } if (service->login != NULL) master_login_stop(service->login); } bool master_service_is_killed(struct master_service *service) { return service->killed; } bool master_service_is_master_stopped(struct master_service *service) { return service->io_status_error == NULL && (service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0; } void master_service_anvil_send(struct master_service *service, const char *cmd) { ssize_t ret; if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) return; ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd)); if (ret < 0) { if (errno == EPIPE) { /* anvil process was probably recreated, don't bother logging an error about losing connection to it */ return; } i_error("write(anvil) failed: %m"); } else if (ret == 0) i_error("write(anvil) failed: EOF"); else { i_assert((size_t)ret == strlen(cmd)); } } void master_service_client_connection_created(struct master_service *service) { i_assert(service->master_status.available_count > 0); service->master_status.available_count--; master_status_update(service); } static bool master_service_want_listener(struct master_service *service) { if (service->master_status.available_count > 0) { /* more concurrent clients can still be added */ return TRUE; } if (service->service_count_left == 1) { /* after handling this client, the whole process will stop. */ return FALSE; } if (service->avail_overflow_callback != NULL) { /* overflow callback is set. it's possible that the current existing client may be replaced by a new client, which needs the listener to try to accept new connections. */ return TRUE; } /* the listener isn't needed until the current client is disconnected */ return FALSE; } void master_service_client_connection_handled(struct master_service *service, struct master_service_connection *conn) { if (!conn->accepted) { if (close(conn->fd) < 0) i_error("close(service connection) failed: %m"); master_service_client_connection_destroyed(service); } else if (conn->fifo) { /* reading FIFOs stays open forever, don't count them as real clients */ master_service_client_connection_destroyed(service); } if (!master_service_want_listener(service)) { i_assert(service->listeners != NULL); master_service_io_listeners_remove(service); if (service->service_count_left == 1) { /* we're not going to accept any more connections after this. go ahead and close the connection early. don't do this before calling callback, because it may want to access the listen_fd (e.g. to check socket permissions). */ master_service_io_listeners_close(service); } } } void master_service_client_connection_callback(struct master_service *service, struct master_service_connection *conn) { service->callback(conn); master_service_client_connection_handled(service, conn); } void master_service_client_connection_accept(struct master_service_connection *conn) { conn->accepted = TRUE; } void master_service_client_connection_destroyed(struct master_service *service) { /* we can listen again */ master_service_io_listeners_add(service); i_assert(service->total_available_count > 0); i_assert(service->service_count_left > 0); if (service->service_count_left == service->total_available_count) { service->total_available_count--; service->service_count_left--; } else { if (service->service_count_left != UINT_MAX) service->service_count_left--; i_assert(service->master_status.available_count < service->total_available_count); service->master_status.available_count++; } if (service->service_count_left == 0) { i_assert(service->master_status.available_count == service->total_available_count); master_service_stop(service); } else if ((service->io_status_error == NULL || service->listeners == NULL) && service->master_status.available_count == service->total_available_count) { /* we've finished handling all clients, and a) master has closed the connection b) there are no listeners (std-client?) */ master_service_stop(service); } else { master_status_update(service); } } static void master_service_set_login_state(struct master_service *service, enum master_login_state state) { if (service->to_overflow_state != NULL) timeout_remove(&service->to_overflow_state); switch (state) { case MASTER_LOGIN_STATE_NONFULL: service->call_avail_overflow = FALSE; if (service->master_status.available_count > 0) return; /* some processes should now be able to handle new connections, although we can't. but there may be race conditions, so make sure that we'll check again soon if the state has changed to "full" without our knowledge. */ service->to_overflow_state = timeout_add(MASTER_SERVICE_STATE_CHECK_MSECS, master_service_refresh_login_state, service); return; case MASTER_LOGIN_STATE_FULL: /* make sure we're listening for more connections */ service->call_avail_overflow = TRUE; master_service_io_listeners_add(service); return; } i_error("Invalid master login state: %d", state); } static void master_service_refresh_login_state(struct master_service *service) { int ret; ret = lseek(MASTER_LOGIN_NOTIFY_FD, 0, SEEK_CUR); if (ret < 0) i_error("lseek(login notify fd) failed: %m"); else master_service_set_login_state(service, ret); } void master_service_close_config_fd(struct master_service *service) { if (service->config_fd != -1) { if (close(service->config_fd) < 0) i_error("close(master config fd) failed: %m"); service->config_fd = -1; } } void master_service_deinit(struct master_service **_service) { struct master_service *service = *_service; unsigned int i; *_service = NULL; master_service_haproxy_abort(service); master_service_io_listeners_remove(service); master_service_ssl_ctx_deinit(service); master_service_close_config_fd(service); if (service->to_die != NULL) timeout_remove(&service->to_die); if (service->to_overflow_state != NULL) timeout_remove(&service->to_overflow_state); if (service->to_status != NULL) timeout_remove(&service->to_status); if (service->io_status_error != NULL) io_remove(&service->io_status_error); if (service->io_status_write != NULL) io_remove(&service->io_status_write); if (array_is_created(&service->config_overrides)) array_free(&service->config_overrides); if (service->set_parser != NULL) { settings_parser_deinit(&service->set_parser); pool_unref(&service->set_pool); } lib_signals_deinit(); /* run atexit callbacks before destroying ioloop */ lib_atexit_run(); io_loop_destroy(&service->ioloop); for (i = 0; i < service->socket_count; i++) i_free(service->listeners[i].name); i_free(service->listeners); i_free(service->getopt_str); i_free(service->name); i_free(service->config_path); i_free(service); lib_deinit(); } static void master_service_listen(struct master_service_listener *l) { struct master_service *service = l->service; struct master_service_connection conn; if (service->master_status.available_count == 0) { /* we are full. stop listening for now, unless overflow callback destroys one of the existing connections */ if (service->call_avail_overflow && service->avail_overflow_callback != NULL) service->avail_overflow_callback(); if (service->master_status.available_count == 0) { master_service_io_listeners_remove(service); return; } } i_zero(&conn); conn.listen_fd = l->fd; conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port); if (conn.fd < 0) { struct stat st; int orig_errno = errno; if (conn.fd == -1) return; if (errno == ENOTSOCK) { /* it's not a socket. should be a fifo. */ } else if (errno == EINVAL && (fstat(l->fd, &st) == 0 && S_ISFIFO(st.st_mode))) { /* BSDI fails accept(fifo) with EINVAL. */ } else { errno = orig_errno; i_error("net_accept() failed: %m"); /* try again later after one of the existing connections has died */ master_service_io_listeners_remove(service); return; } /* use the "listener" as the connection fd and stop the listener. */ conn.fd = l->fd; conn.listen_fd = l->fd; conn.fifo = TRUE; io_remove(&l->io); l->fd = -1; } conn.ssl = l->ssl; conn.name = master_service_get_socket_name(service, conn.listen_fd); (void)net_getsockname(conn.fd, &conn.local_ip, &conn.local_port); conn.real_remote_ip = conn.remote_ip; conn.real_remote_port = conn.remote_port; conn.real_local_ip = conn.local_ip; conn.real_local_port = conn.local_port; net_set_nonblock(conn.fd, TRUE); master_service_client_connection_created(service); if (l->haproxy) master_service_haproxy_new(service, &conn); else master_service_client_connection_callback(service, &conn); } void master_service_io_listeners_add(struct master_service *service) { unsigned int i; if (service->stopping) return; for (i = 0; i < service->socket_count; i++) { struct master_service_listener *l = &service->listeners[i]; if (l->io == NULL && l->fd != -1 && !l->closed) { l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ, master_service_listen, l); } } } void master_service_io_listeners_remove(struct master_service *service) { unsigned int i; for (i = 0; i < service->socket_count; i++) { if (service->listeners[i].io != NULL) io_remove(&service->listeners[i].io); } } void master_service_ssl_io_listeners_remove(struct master_service *service) { unsigned int i; for (i = 0; i < service->socket_count; i++) { if (service->listeners[i].io != NULL && service->listeners[i].ssl) io_remove(&service->listeners[i].io); } } static void master_service_io_listeners_close(struct master_service *service) { unsigned int i; /* close via listeners. some fds might be pipes that are currently handled as clients. we don't want to close them. */ for (i = 0; i < service->socket_count; i++) { if (service->listeners[i].fd != -1) { if (close(service->listeners[i].fd) < 0) { i_error("close(listener %d) failed: %m", service->listeners[i].fd); } service->listeners[i].fd = -1; } } } static bool master_status_update_is_important(struct master_service *service) { if (service->master_status.available_count == 0) return TRUE; if (!service->initial_status_sent) return TRUE; return FALSE; } static void master_status_send(struct master_service *service, bool important_update) { ssize_t ret; if (service->to_status != NULL) timeout_remove(&service->to_status); ret = write(MASTER_STATUS_FD, &service->master_status, sizeof(service->master_status)); if (ret == sizeof(service->master_status)) { /* success */ if (service->io_status_write != NULL) { /* delayed important update sent successfully */ io_remove(&service->io_status_write); } service->last_sent_status_time = ioloop_time; service->last_sent_status_avail_count = service->master_status.available_count; service->initial_status_sent = TRUE; } else if (ret >= 0) { /* shouldn't happen? */ i_error("write(master_status_fd) returned %d", (int)ret); service->master_status.pid = 0; } else if (errno != EAGAIN) { /* failure */ if (errno != EPIPE) i_error("write(master_status_fd) failed: %m"); service->master_status.pid = 0; } else if (important_update) { /* reader is busy, but it's important to get this notification through. send it when possible. */ if (service->io_status_write == NULL) { service->io_status_write = io_add(MASTER_STATUS_FD, IO_WRITE, master_status_update, service); } } } void master_status_update(struct master_service *service) { bool important_update; if ((service->flags & MASTER_SERVICE_FLAG_UPDATE_PROCTITLE) != 0 && service->set != NULL && service->set->verbose_proctitle) T_BEGIN { unsigned int used_count = service->total_available_count - service->master_status.available_count; process_title_set(t_strdup_printf("[%u connections]", used_count)); } T_END; important_update = master_status_update_is_important(service); if (service->master_status.pid == 0 || service->master_status.available_count == service->last_sent_status_avail_count) { /* a) closed, b) updating to same state */ if (service->to_status != NULL) timeout_remove(&service->to_status); if (service->io_status_write != NULL) io_remove(&service->io_status_write); return; } if (ioloop_time == service->last_sent_status_time && !important_update) { /* don't spam master */ if (service->to_status != NULL) timeout_reset(service->to_status); else { service->to_status = timeout_add(1000, master_status_update, service); } if (service->io_status_write != NULL) io_remove(&service->io_status_write); return; } master_status_send(service, important_update); } bool version_string_verify(const char *line, const char *service_name, unsigned major_version) { unsigned int minor_version; return version_string_verify_full(line, service_name, major_version, &minor_version); } bool version_string_verify_full(const char *line, const char *service_name, unsigned major_version, unsigned int *minor_version_r) { size_t service_name_len = strlen(service_name); bool ret; if (strncmp(line, "VERSION\t", 8) != 0) return FALSE; line += 8; if (strncmp(line, service_name, service_name_len) != 0 || line[service_name_len] != '\t') return FALSE; line += service_name_len + 1; T_BEGIN { const char *p = strchr(line, '\t'); if (p == NULL) ret = FALSE; else { ret = str_uint_equals(t_strdup_until(line, p), major_version); if (str_to_uint(p+1, minor_version_r) < 0) ret = FALSE; } } T_END; return ret; } dovecot-2.2.33.2/src/lib-master/ipc-client.c0000644000175000017500000000724413165463624015367 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "hostpid.h" #include "master-service.h" #include "ipc-client.h" #include struct ipc_client_cmd { ipc_client_callback_t *callback; void *context; }; struct ipc_client { char *path; ipc_client_callback_t *callback; int fd; struct io *io; struct timeout *to; struct istream *input; struct ostream *output; ARRAY(struct ipc_client_cmd) cmds; }; static void ipc_client_disconnect(struct ipc_client *client); static void ipc_client_input_line(struct ipc_client *client, const char *line) { const struct ipc_client_cmd *cmds; unsigned int count; enum ipc_client_cmd_state state; cmds = array_get(&client->cmds, &count); if (count == 0) { i_error("IPC proxy sent unexpected input: %s", line); return; } switch (*line++) { case ':': state = IPC_CLIENT_CMD_STATE_REPLY; break; case '+': state = IPC_CLIENT_CMD_STATE_OK; break; case '-': state = IPC_CLIENT_CMD_STATE_ERROR; break; default: i_error("IPC proxy sent invalid input: %s", line); line = "Invalid input"; ipc_client_disconnect(client); state = IPC_CLIENT_CMD_STATE_ERROR; break; } cmds[0].callback(state, line, cmds[0].context); if (state != IPC_CLIENT_CMD_STATE_REPLY) array_delete(&client->cmds, 0, 1); } static void ipc_client_input(struct ipc_client *client) { const char *line; if (i_stream_read(client->input) < 0) { ipc_client_disconnect(client); return; } while ((line = i_stream_next_line(client->input)) != NULL) T_BEGIN { ipc_client_input_line(client, line); } T_END; } static int ipc_client_connect(struct ipc_client *client) { if (client->fd != -1) return 0; client->fd = net_connect_unix(client->path); if (client->fd == -1) { i_error("connect(%s) failed: %m", client->path); return -1; } client->io = io_add(client->fd, IO_READ, ipc_client_input, client); client->input = i_stream_create_fd(client->fd, (size_t)-1, FALSE); client->output = o_stream_create_fd(client->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); return 0; } static void ipc_client_disconnect(struct ipc_client *client) { const struct ipc_client_cmd *cmd; if (client->fd == -1) return; array_foreach(&client->cmds, cmd) { cmd->callback(IPC_CLIENT_CMD_STATE_ERROR, "Disconnected", cmd->context); } array_clear(&client->cmds); io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); if (close(client->fd) < 0) i_error("close(%s) failed: %m", client->path); client->fd = -1; } struct ipc_client * ipc_client_init(const char *ipc_socket_path) { struct ipc_client *client; client = i_new(struct ipc_client, 1); client->path = i_strdup(ipc_socket_path); client->fd = -1; i_array_init(&client->cmds, 8); return client; } void ipc_client_deinit(struct ipc_client **_client) { struct ipc_client *client = *_client; *_client = NULL; ipc_client_disconnect(client); array_free(&client->cmds); i_free(client->path); i_free(client); } void ipc_client_cmd(struct ipc_client *client, const char *cmd, ipc_client_callback_t *callback, void *context) { struct ipc_client_cmd *ipc_cmd; struct const_iovec iov[2]; if (ipc_client_connect(client) < 0) { callback(IPC_CLIENT_CMD_STATE_ERROR, "ipc connect failed", context); return; } iov[0].iov_base = cmd; iov[0].iov_len = strlen(cmd); iov[1].iov_base = "\n"; iov[1].iov_len = 1; o_stream_nsendv(client->output, iov, N_ELEMENTS(iov)); ipc_cmd = array_append_space(&client->cmds); ipc_cmd->callback = callback; ipc_cmd->context = context; } dovecot-2.2.33.2/src/lib-master/master-service-ssl-settings.c0000644000175000017500000000757113165463624020731 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "master-service-private.h" #include "master-service-ssl-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct master_service_ssl_settings, name), NULL } static bool master_service_ssl_settings_check(void *_set, pool_t pool, const char **error_r); static const struct setting_define master_service_ssl_setting_defines[] = { DEF(SET_ENUM, ssl), DEF(SET_STR, ssl_ca), DEF(SET_STR, ssl_cert), DEF(SET_STR, ssl_key), DEF(SET_STR, ssl_alt_cert), DEF(SET_STR, ssl_alt_key), DEF(SET_STR, ssl_key_password), DEF(SET_STR, ssl_cipher_list), DEF(SET_STR, ssl_protocols), DEF(SET_STR, ssl_cert_username_field), DEF(SET_STR, ssl_crypto_device), DEF(SET_BOOL, ssl_verify_client_cert), DEF(SET_BOOL, ssl_require_crl), DEF(SET_BOOL, verbose_ssl), DEF(SET_BOOL, ssl_prefer_server_ciphers), DEF(SET_STR, ssl_options), /* parsed as a string to set bools */ SETTING_DEFINE_LIST_END }; static const struct master_service_ssl_settings master_service_ssl_default_settings = { #ifdef HAVE_SSL .ssl = "yes:no:required", #else .ssl = "no:yes:required", #endif .ssl_ca = "", .ssl_cert = "", .ssl_key = "", .ssl_alt_cert = "", .ssl_alt_key = "", .ssl_key_password = "", .ssl_cipher_list = "ALL:!LOW:!SSLv2:!EXP:!aNULL", #ifdef SSL_TXT_SSLV2 .ssl_protocols = "!SSLv2 !SSLv3", #else .ssl_protocols = "!SSLv3", #endif .ssl_cert_username_field = "commonName", .ssl_crypto_device = "", .ssl_verify_client_cert = FALSE, .ssl_require_crl = TRUE, .verbose_ssl = FALSE, .ssl_prefer_server_ciphers = FALSE, .ssl_options = "", }; const struct setting_parser_info master_service_ssl_setting_parser_info = { .module_name = "ssl", .defines = master_service_ssl_setting_defines, .defaults = &master_service_ssl_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct master_service_ssl_settings), .parent_offset = (size_t)-1, .check_func = master_service_ssl_settings_check }; /* */ static bool master_service_ssl_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct master_service_ssl_settings *set = _set; if (strcmp(set->ssl, "no") == 0) { /* disabled */ return TRUE; } #ifndef HAVE_SSL *error_r = t_strdup_printf("SSL support not compiled in but ssl=%s", set->ssl); return FALSE; #else /* we get called from many different tools, possibly with -O parameter, and few of those tools care about SSL settings. so don't check ssl_cert/ssl_key/etc validity here except in doveconf, because it usually is just an extra annoyance. */ #ifdef CONFIG if (*set->ssl_cert == '\0') { *error_r = "ssl enabled, but ssl_cert not set"; return FALSE; } if (*set->ssl_key == '\0') { *error_r = "ssl enabled, but ssl_key not set"; return FALSE; } #endif if (set->ssl_verify_client_cert && *set->ssl_ca == '\0') { *error_r = "ssl_verify_client_cert set, but ssl_ca not"; return FALSE; } /* Now explode the ssl_options string into individual flags */ /* First set them all to defaults */ set->parsed_opts.compression = TRUE; set->parsed_opts.tickets = TRUE; /* Then modify anything specified in the string */ const char **opts = t_strsplit_spaces(set->ssl_options, ", "); const char *opt; while ((opt = *opts++) != NULL) { if (strcasecmp(opt, "no_compression") == 0) { set->parsed_opts.compression = FALSE; } else if (strcasecmp(opt, "no_ticket") == 0) { set->parsed_opts.tickets = FALSE; } else { *error_r = t_strdup_printf("ssl_options: unknown flag: '%s'", opt); return FALSE; } } return TRUE; #endif } /* */ const struct master_service_ssl_settings * master_service_ssl_settings_get(struct master_service *service) { void **sets; sets = settings_parser_get_list(service->set_parser); return sets[1]; } dovecot-2.2.33.2/src/lib-master/ipc-server.c0000644000175000017500000001102613165463624015410 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "hostpid.h" #include "master-service.h" #include "ipc-server.h" #include #define IPC_SERVER_RECONNECT_MSECS (60*1000) #define IPC_SERVER_PROTOCOL_MAJOR_VERSION 1 #define IPC_SERVER_PROTOCOL_MINOR_VERSION 0 #define IPC_SERVER_HANDSHAKE "VERSION\tipc-server\t1\t0\nHANDSHAKE\t%s\t%s\n" struct ipc_cmd { struct ipc_server *server; unsigned int tag; }; struct ipc_server { char *name, *path; ipc_command_callback_t *callback; int ipc_cmd_refcount; int fd; struct io *io; struct timeout *to; struct istream *input; struct ostream *output; unsigned int version_received:1; }; static void ipc_server_disconnect(struct ipc_server *server); static void ipc_server_input_line(struct ipc_server *server, char *line) { struct ipc_cmd *cmd; unsigned int tag = 0; char *p; /* tag cmd */ p = strchr(line, '\t'); if (p != NULL) { *p++ = '\0'; if (str_to_uint(line, &tag) < 0) p = NULL; } if (p == NULL || *p == '\0') { i_error("IPC proxy sent invalid input: %s", line); return; } cmd = i_new(struct ipc_cmd, 1); cmd->server = server; cmd->tag = tag; server->ipc_cmd_refcount++; T_BEGIN { server->callback(cmd, p); } T_END; } static void ipc_server_input(struct ipc_server *server) { char *line; if (i_stream_read(server->input) < 0) { ipc_server_disconnect(server); return; } if (!server->version_received) { if ((line = i_stream_next_line(server->input)) == NULL) return; if (!version_string_verify(line, "ipc-proxy", IPC_SERVER_PROTOCOL_MAJOR_VERSION)) { i_error("IPC proxy not compatible with this server " "(mixed old and new binaries?)"); ipc_server_disconnect(server); return; } server->version_received = TRUE; } while ((line = i_stream_next_line(server->input)) != NULL) ipc_server_input_line(server, line); } static void ipc_server_connect(struct ipc_server *server) { i_assert(server->fd == -1); if (server->to != NULL) timeout_remove(&server->to); server->fd = net_connect_unix(server->path); if (server->fd == -1) { i_error("connect(%s) failed: %m", server->path); server->to = timeout_add(IPC_SERVER_RECONNECT_MSECS, ipc_server_connect, server); return; } server->io = io_add(server->fd, IO_READ, ipc_server_input, server); server->input = i_stream_create_fd(server->fd, (size_t)-1, FALSE); server->output = o_stream_create_fd(server->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(server->output, TRUE); o_stream_nsend_str(server->output, t_strdup_printf(IPC_SERVER_HANDSHAKE, server->name, my_pid)); o_stream_cork(server->output); } static void ipc_server_disconnect(struct ipc_server *server) { if (server->fd == -1) return; io_remove(&server->io); i_stream_destroy(&server->input); o_stream_destroy(&server->output); if (close(server->fd) < 0) i_error("close(%s) failed: %m", server->path); server->fd = -1; } struct ipc_server * ipc_server_init(const char *ipc_socket_path, const char *name, ipc_command_callback_t *callback) { struct ipc_server *server; server = i_new(struct ipc_server, 1); server->name = i_strdup(name); server->path = i_strdup(ipc_socket_path); server->callback = callback; server->fd = -1; ipc_server_connect(server); return server; } void ipc_server_deinit(struct ipc_server **_server) { struct ipc_server *server = *_server; *_server = NULL; i_assert(server->ipc_cmd_refcount == 0); ipc_server_disconnect(server); if (server->to != NULL) timeout_remove(&server->to); i_free(server->name); i_free(server->path); i_free(server); } void ipc_cmd_send(struct ipc_cmd *cmd, const char *data) { o_stream_nsend_str(cmd->server->output, t_strdup_printf("%u\t:%s\n", cmd->tag, data)); } static void ipc_cmd_finish(struct ipc_cmd *cmd, const char *line) { o_stream_nsend_str(cmd->server->output, t_strdup_printf("%u\t%s\n", cmd->tag, line)); o_stream_uncork(cmd->server->output); i_assert(cmd->server->ipc_cmd_refcount > 0); cmd->server->ipc_cmd_refcount--; i_free(cmd); } void ipc_cmd_success(struct ipc_cmd **_cmd) { ipc_cmd_success_reply(_cmd, ""); } void ipc_cmd_success_reply(struct ipc_cmd **_cmd, const char *data) { struct ipc_cmd *cmd = *_cmd; *_cmd = NULL; ipc_cmd_finish(cmd, t_strconcat("+", data, NULL)); } void ipc_cmd_fail(struct ipc_cmd **_cmd, const char *errormsg) { struct ipc_cmd *cmd = *_cmd; i_assert(errormsg != NULL); *_cmd = NULL; ipc_cmd_finish(cmd, t_strconcat("-", errormsg, NULL)); } dovecot-2.2.33.2/src/lib-master/master-auth.h0000644000175000017500000000661513123174404015565 00000000000000#ifndef MASTER_AUTH_H #define MASTER_AUTH_H #include "net.h" struct master_service; /* Major version changes are not backwards compatible, minor version numbers can be ignored. */ #define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1 #define AUTH_MASTER_PROTOCOL_MINOR_VERSION 1 /* Authentication client process's cookie size */ #define MASTER_AUTH_COOKIE_SIZE (128/8) /* LOGIN_MAX_INBUF_SIZE should be based on this. Keep this large enough so that LOGIN_MAX_INBUF_SIZE will be 1024+2 bytes. This is because IMAP ID command's values may be max. 1024 bytes plus 2 for "" quotes. (Although it could be even double of that when value is full of \" quotes, but for now lets not make it too easy to waste memory..) */ #define MASTER_AUTH_MAX_DATA_SIZE (1024 + 128 + 64 + 2) #define MASTER_AUTH_ERRMSG_INTERNAL_FAILURE \ "Internal error occurred. Refer to server log for more information." enum mail_auth_request_flags { /* Connection has TLS compression enabled */ MAIL_AUTH_REQUEST_FLAG_TLS_COMPRESSION = 0x01 }; /* Authentication request. File descriptor may be sent along with the request. */ struct master_auth_request { /* Request tag. Reply is sent back using same tag. */ unsigned int tag; /* Authentication process, authentication ID and auth cookie. */ pid_t auth_pid; unsigned int auth_id; unsigned int client_pid; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; /* Local and remote IPs of the connection. The file descriptor itself may be a local socketpair. */ struct ip_addr local_ip, remote_ip; uint32_t flags; /* request follows this many bytes of client input */ uint32_t data_size; /* inode of the transferred fd. verified just to be sure that the correct fd is mapped to the correct struct. */ ino_t ino; }; enum master_auth_status { MASTER_AUTH_STATUS_OK, MASTER_AUTH_STATUS_INTERNAL_ERROR }; struct master_auth_reply { /* tag=0 are notifications from master */ unsigned int tag; enum master_auth_status status; /* PID of the post-login mail process handling this connection */ pid_t mail_pid; }; struct master_auth_request_params { /* Client fd to transfer to post-login process or -1 if no fd is wanted to be transferred. */ int client_fd; /* Override master_auth->default_path if non-NULL */ const char *socket_path; /* Authentication request that is sent to post-login process. tag is ignored. */ struct master_auth_request request; /* Client input of size request.data_size */ const unsigned char *data; }; /* reply=NULL if the auth lookup was cancelled due to some error */ typedef void master_auth_callback_t(const struct master_auth_reply *reply, void *context); struct master_auth * master_auth_init(struct master_service *service, const char *path); void master_auth_deinit(struct master_auth **auth); /* Send an authentication request. Returns tag which can be used to abort the request (ie. ignore the reply from master). */ void master_auth_request_full(struct master_auth *auth, const struct master_auth_request_params *params, master_auth_callback_t *callback, void *context, unsigned int *tag_r); /* For backwards compatibility: */ void master_auth_request(struct master_auth *auth, int fd, const struct master_auth_request *request, const unsigned char *data, master_auth_callback_t *callback, void *context, unsigned int *tag_r); void master_auth_request_abort(struct master_auth *auth, unsigned int tag); #endif dovecot-2.2.33.2/src/lib-master/master-instance.h0000644000175000017500000000272513123174404016426 00000000000000#ifndef MASTER_INSTANCE_H #define MASTER_INSTANCE_H #define MASTER_INSTANCE_FNAME "instances" struct master_instance_list; struct master_instance { time_t last_used; const char *name; const char *base_dir; const char *config_path; }; struct master_instance_list *master_instance_list_init(const char *path); void master_instance_list_deinit(struct master_instance_list **list); /* Add/update last_used timestamp for an instance. Returns 0 if ok, -1 if I/O error. */ int master_instance_list_update(struct master_instance_list *list, const char *base_dir); /* Set instance's name. Returns 1 if ok, 0 if name was already used for another instance (base_dir) or -1 if I/O error. */ int master_instance_list_set_name(struct master_instance_list *list, const char *base_dir, const char *name); /* Remove instance. Returns 1 if ok, 0 if it didn't exist or -1 if I/O error. */ int master_instance_list_remove(struct master_instance_list *list, const char *base_dir); /* Find instance by its name. */ const struct master_instance * master_instance_list_find_by_name(struct master_instance_list *list, const char *name); /* Iterate through existing instances. */ struct master_instance_list_iter * master_instance_list_iterate_init(struct master_instance_list *list); const struct master_instance * master_instance_iterate_list_next(struct master_instance_list_iter *iter); void master_instance_iterate_list_deinit(struct master_instance_list_iter **iter); #endif dovecot-2.2.33.2/src/lib-master/master-service.h0000644000175000017500000002333613165463624016276 00000000000000#ifndef MASTER_SERVICE_H #define MASTER_SERVICE_H #include "net.h" #include /* for getopt() opt* variables */ #include /* for getopt() opt* variables in Solaris */ enum master_service_flags { /* stdin/stdout already contains a client which we want to serve */ MASTER_SERVICE_FLAG_STD_CLIENT = 0x01, /* this process is currently running standalone without a master */ MASTER_SERVICE_FLAG_STANDALONE = 0x02, /* Log to configured log file instead of stderr. By default when _FLAG_STANDALONE is set, logging is done to stderr. */ MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR = 0x04, /* Service is going to do multiple configuration lookups, keep the connection to config service open. Also opens the config socket before dropping privileges. */ MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN = 0x08, /* Don't read settings, but use whatever is in environment */ MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS = 0x10, /* Use MASTER_LOGIN_NOTIFY_FD to track login overflow state */ MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE = 0x40, /* If master sends SIGINT, don't die even if we don't have clients */ MASTER_SERVICE_FLAG_NO_IDLE_DIE = 0x80, /* Show number of connections in process title (only if verbose_proctitle setting is enabled) */ MASTER_SERVICE_FLAG_UPDATE_PROCTITLE = 0x100, /* SSL settings are always looked up when we have ssl listeners. This flag enables looking up SSL settings even without ssl listeners (i.e. the service does STARTTLS). */ MASTER_SERVICE_FLAG_USE_SSL_SETTINGS = 0x200, /* Don't initialize SSL context automatically. */ MASTER_SERVICE_FLAG_NO_SSL_INIT = 0x400 }; struct master_service_connection { /* fd of the new connection. */ int fd; /* fd of the socket listener. Same as fd for a FIFO. */ int listen_fd; /* listener name as in configuration file, or "" if unnamed. */ const char *name; /* Original client/server IP/port. Both of these may have been changed by the haproxy protocol. */ struct ip_addr remote_ip, local_ip; in_port_t remote_port, local_port; /* The real client/server IP/port, unchanged by haproxy protocol. */ struct ip_addr real_remote_ip, real_local_ip; in_port_t real_remote_port, real_local_port; /* This is a FIFO fd. Only a single "connection" is ever received from a FIFO after the first writer sends something to it. */ unsigned int fifo:1; /* Perform immediate SSL handshake for this connection. Currently this needs to be performed explicitly by each service. */ unsigned int ssl:1; /* Internal: master_service_client_connection_accept() has been called for this connection. */ unsigned int accepted:1; }; typedef void master_service_connection_callback_t(struct master_service_connection *conn); extern struct master_service *master_service; const char *master_service_getopt_string(void); /* Start service initialization. */ struct master_service * master_service_init(const char *name, enum master_service_flags flags, int *argc, char **argv[], const char *getopt_str); /* Call getopt() and handle internal parameters. Return values are the same as getopt()'s. */ int master_getopt(struct master_service *service); /* Returns TRUE if str is a valid getopt_str. Currently this only checks for duplicate args so they aren't accidentally added. */ bool master_getopt_str_is_valid(const char *str); /* Parser command line option. Returns TRUE if processed. */ bool master_service_parse_option(struct master_service *service, int opt, const char *arg); /* Finish service initialization. The caller should drop privileges before calling this. This also notifies the master that the service was successfully started and there shouldn't be any service throttling even if it crashes afterwards, so this should be called after all of the initialization code is finished. */ void master_service_init_finish(struct master_service *service); /* import_environment is a space-separated list of environment keys or key=values. The key=values are immediately added to the environment. All the keys are added to DOVECOT_PRESERVE_ENVS environment so they're preserved by master_service_env_clean(). */ void master_service_import_environment(const char *import_environment); /* Clean environment from everything except the ones listed in DOVECOT_PRESERVE_ENVS environment. */ void master_service_env_clean(void); /* Initialize logging. Only the first call changes the actual logging functions. The following calls change the log prefix. */ void master_service_init_log(struct master_service *service, const char *prefix); /* If set, die immediately when connection to master is lost. Normally all existing clients are handled first. */ void master_service_set_die_with_master(struct master_service *service, bool set); /* Call the given when master connection dies and die_with_master is TRUE. The callback is expected to shut down the service somewhat soon or it's done forcibly. If NULL, the service is stopped immediately. */ void master_service_set_die_callback(struct master_service *service, void (*callback)(void)); /* "idle callback" is called when master thinks we're idling and asks us to die. We'll do it only if the idle callback returns TRUE. This callback isn't even called if the master service code knows that we're handling clients. */ void master_service_set_idle_die_callback(struct master_service *service, bool (*callback)(void)); /* Call the given callback when there are no available connections and master has indicated that it can't create any more processes to handle requests. The callback could decide to kill one of the existing connections. */ void master_service_set_avail_overflow_callback(struct master_service *service, void (*callback)(void)); /* Set maximum number of clients we can handle. Default is given by master. */ void master_service_set_client_limit(struct master_service *service, unsigned int client_limit); /* Returns the maximum number of clients we can handle. */ unsigned int master_service_get_client_limit(struct master_service *service); /* Returns how many processes of this type can be created before reaching the limit. */ unsigned int master_service_get_process_limit(struct master_service *service); /* Returns service { process_min_avail } */ unsigned int master_service_get_process_min_avail(struct master_service *service); /* Returns the service's idle_kill timeout in seconds. Normally master handles sending the kill request when the process has no clients, but some services with permanent client connections may need to handle this themselves. */ unsigned int master_service_get_idle_kill_secs(struct master_service *service); /* Set maximum number of client connections we will handle before shutting down. */ void master_service_set_service_count(struct master_service *service, unsigned int count); /* Returns the number of client connections we will handle before shutting down. The value is decreased only after connection has been closed. */ unsigned int master_service_get_service_count(struct master_service *service); /* Return the number of listener sockets. */ unsigned int master_service_get_socket_count(struct master_service *service); /* Returns the name of the listener socket, or "" if none is specified. */ const char *master_service_get_socket_name(struct master_service *service, int listen_fd); /* Returns configuration file path. */ const char *master_service_get_config_path(struct master_service *service); /* Returns PACKAGE_VERSION or NULL if version_ignore=yes. This function is useful mostly as parameter to module_dir_load(). */ const char *master_service_get_version_string(struct master_service *service); /* Returns name of the service, as given in name parameter to _init(). */ const char *master_service_get_name(struct master_service *service); /* Start the service. Blocks until finished */ void master_service_run(struct master_service *service, master_service_connection_callback_t *callback) ATTR_NULL(2); /* Stop a running service. */ void master_service_stop(struct master_service *service); /* Stop once we're done serving existing new connections, but don't accept any new ones. */ void master_service_stop_new_connections(struct master_service *service); /* Returns TRUE if we've received a SIGINT/SIGTERM and we've decided to stop. */ bool master_service_is_killed(struct master_service *service); /* Returns TRUE if our master process is already stopped. This process may or may not be dying itself. Returns FALSE always if the process was started standalone. */ bool master_service_is_master_stopped(struct master_service *service); /* Send command to anvil process, if we have fd to it. */ void master_service_anvil_send(struct master_service *service, const char *cmd); /* Call to accept the client connection. Otherwise the connection is closed. */ void master_service_client_connection_accept(struct master_service_connection *conn); /* Used to create "extra client connections" outside the common accept() method. */ void master_service_client_connection_created(struct master_service *service); /* Call whenever a client connection is destroyed. */ void master_service_client_connection_destroyed(struct master_service *service); /* Deinitialize the service. */ void master_service_deinit(struct master_service **service); /* Returns TRUE if line contains compatible service name and major version. The line is expected to be in format: VERSION service_name major version minor version */ bool version_string_verify(const char *line, const char *service_name, unsigned major_version); /* Same as version_string_verify(), but return the minor version. */ bool version_string_verify_full(const char *line, const char *service_name, unsigned major_version, unsigned int *minor_version_r); #endif dovecot-2.2.33.2/src/lib-master/master-service-private.h0000644000175000017500000000526113165463624017743 00000000000000#ifndef MASTER_SERVICE_PRIVATE_H #define MASTER_SERVICE_PRIVATE_H #include "master-interface.h" #include "master-service.h" struct master_service_haproxy_conn; struct master_service_listener { struct master_service *service; char *name; /* settings */ bool ssl; bool haproxy; /* state */ bool closed; int fd; struct io *io; }; struct master_service { struct ioloop *ioloop; char *name; char *getopt_str; enum master_service_flags flags; int argc; char **argv; const char *version_string; char *config_path; ARRAY_TYPE(const_string) config_overrides; int config_fd; int syslog_facility; struct master_service_listener *listeners; unsigned int socket_count; struct io *io_status_write, *io_status_error; unsigned int service_count_left; unsigned int total_available_count; unsigned int process_limit; unsigned int process_min_avail; unsigned int idle_kill_secs; struct master_status master_status; unsigned int last_sent_status_avail_count; time_t last_sent_status_time; struct timeout *to_status; bool (*idle_die_callback)(void); void (*die_callback)(void); struct timeout *to_die; void (*avail_overflow_callback)(void); struct timeout *to_overflow_state; struct master_login *login; master_service_connection_callback_t *callback; pool_t set_pool; const struct master_service_settings *set; struct setting_parser_context *set_parser; struct ssl_iostream_context *ssl_ctx; time_t ssl_params_last_refresh; struct master_service_haproxy_conn *haproxy_conns; unsigned int killed:1; unsigned int stopping:1; unsigned int keep_environment:1; unsigned int log_directly:1; unsigned int initial_status_sent:1; unsigned int die_with_master:1; unsigned int call_avail_overflow:1; unsigned int config_path_changed_with_param:1; unsigned int want_ssl_settings:1; unsigned int ssl_ctx_initialized:1; unsigned int config_path_from_master:1; unsigned int log_initialized:1; }; void master_service_io_listeners_add(struct master_service *service); void master_status_update(struct master_service *service); void master_service_close_config_fd(struct master_service *service); void master_service_io_listeners_remove(struct master_service *service); void master_service_ssl_io_listeners_remove(struct master_service *service); void master_service_client_connection_handled(struct master_service *service, struct master_service_connection *conn); void master_service_client_connection_callback(struct master_service *service, struct master_service_connection *conn); void master_service_haproxy_new(struct master_service *service, struct master_service_connection *conn); void master_service_haproxy_abort(struct master_service *service); #endif dovecot-2.2.33.2/src/lib-master/anvil-client.h0000644000175000017500000000266613123174404015722 00000000000000#ifndef ANVIL_CLIENT_H #define ANVIL_CLIENT_H enum anvil_client_flags { /* if connect() fails with ENOENT, hide the error */ ANVIL_CLIENT_FLAG_HIDE_ENOENT = 0x01 }; /* reply=NULL if query failed */ typedef void anvil_callback_t(const char *reply, void *context); /* If reconnect_callback is specified, it's called when connection is lost. If the callback returns FALSE, reconnection isn't attempted. */ struct anvil_client * anvil_client_init(const char *path, bool (*reconnect_callback)(void), enum anvil_client_flags flags) ATTR_NULL(2); void anvil_client_deinit(struct anvil_client **client); /* Connect to anvil. If retry=TRUE, try connecting for a while */ int anvil_client_connect(struct anvil_client *client, bool retry); /* Send a query to anvil, expect a one line reply. The returned pointer can be used to abort the query later. It becomes invalid when callback is called (= the callback must not call it). Returns NULL if the query couldn't be sent. */ struct anvil_query * anvil_client_query(struct anvil_client *client, const char *query, anvil_callback_t *callback, void *context); void anvil_client_query_abort(struct anvil_client *client, struct anvil_query **query); /* Send a command to anvil, don't expect any replies. */ void anvil_client_cmd(struct anvil_client *client, const char *cmd); /* Returns TRUE if anvil is connected to. */ bool anvil_client_is_connected(struct anvil_client *client); #endif dovecot-2.2.33.2/src/lib-master/service-settings.h0000644000175000017500000000333513165463624016640 00000000000000#ifndef SERVICE_SETTINGS_H #define SERVICE_SETTINGS_H #include "net.h" /* */ enum service_user_default { SERVICE_USER_DEFAULT_NONE = 0, SERVICE_USER_DEFAULT_INTERNAL, SERVICE_USER_DEFAULT_LOGIN }; enum service_type { SERVICE_TYPE_UNKNOWN, SERVICE_TYPE_LOG, SERVICE_TYPE_ANVIL, SERVICE_TYPE_CONFIG, SERVICE_TYPE_LOGIN, SERVICE_TYPE_STARTUP }; /* */ struct file_listener_settings { const char *path; unsigned int mode; const char *user; const char *group; }; ARRAY_DEFINE_TYPE(file_listener_settings, struct file_listener_settings *); struct inet_listener_settings { const char *name; const char *address; in_port_t port; bool ssl; bool reuse_port; bool haproxy; }; ARRAY_DEFINE_TYPE(inet_listener_settings, struct inet_listener_settings *); struct service_settings { const char *name; const char *protocol; const char *type; const char *executable; const char *user; const char *group; const char *privileged_group; const char *extra_groups; const char *chroot; bool drop_priv_before_exec; unsigned int process_min_avail; unsigned int process_limit; unsigned int client_limit; unsigned int service_count; unsigned int idle_kill; uoff_t vsz_limit; ARRAY_TYPE(file_listener_settings) unix_listeners; ARRAY_TYPE(file_listener_settings) fifo_listeners; ARRAY_TYPE(inet_listener_settings) inet_listeners; /* internal to master: */ struct master_settings *master_set; enum service_type parsed_type; enum service_user_default user_default; unsigned int login_dump_core:1; /* -- flags that can be set internally -- */ /* process_limit must not be higher than 1 */ unsigned int process_limit_1:1; }; ARRAY_DEFINE_TYPE(service_settings, struct service_settings *); #endif dovecot-2.2.33.2/src/lib-master/syslog-util.c0000644000175000017500000000233313123174404015612 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "syslog-util.h" #include struct syslog_facility_list syslog_facilities[] = { #ifdef LOG_AUTH { "auth", LOG_AUTH }, #endif #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif #ifdef LOG_CRON { "cron", LOG_CRON }, #endif #ifdef LOG_DAEMON { "daemon", LOG_DAEMON }, #endif #ifdef LOG_FTP { "ftp", LOG_FTP }, #endif #ifdef LOG_KERN { "kern", LOG_KERN }, #endif #ifdef LOG_LPR { "lpr", LOG_LPR }, #endif #ifdef LOG_MAIL { "mail", LOG_MAIL }, #endif #ifdef LOG_NEWS { "news", LOG_NEWS }, #endif #ifdef LOG_SYSLOG { "syslog", LOG_SYSLOG }, #endif #ifdef LOG_UUCP { "uucp", LOG_UUCP }, #endif { "user", LOG_USER }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL, 0 } }; bool syslog_facility_find(const char *name, int *facility_r) { int i; for (i = 0; syslog_facilities[i].name != NULL; i++) { if (strcmp(syslog_facilities[i].name, name) == 0) { *facility_r = syslog_facilities[i].facility; return TRUE; } } return FALSE; } dovecot-2.2.33.2/src/lib-master/master-auth.c0000644000175000017500000001437513165463624015575 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "fdpass.h" #include "buffer.h" #include "hash.h" #include "master-service-private.h" #include "master-auth.h" #include #include #define SOCKET_CONNECT_RETRY_MSECS 500 #define MASTER_AUTH_REQUEST_TIMEOUT_MSECS (MASTER_LOGIN_TIMEOUT_SECS/2*1000) struct master_auth_connection { struct master_auth *auth; unsigned int tag; char *path; int fd; struct io *io; struct timeout *to; char buf[sizeof(struct master_auth_reply)]; unsigned int buf_pos; master_auth_callback_t *callback; void *context; }; struct master_auth { struct master_service *service; pool_t pool; const char *default_path; unsigned int tag_counter; HASH_TABLE(void *, struct master_auth_connection *) connections; }; struct master_auth * master_auth_init(struct master_service *service, const char *path) { struct master_auth *auth; pool_t pool; pool = pool_alloconly_create("master auth", 1024); auth = p_new(pool, struct master_auth, 1); auth->pool = pool; auth->service = service; auth->default_path = p_strdup(pool, path); hash_table_create_direct(&auth->connections, pool, 0); return auth; } static void master_auth_connection_deinit(struct master_auth_connection **_conn) { struct master_auth_connection *conn = *_conn; *_conn = NULL; if (conn->tag != 0) hash_table_remove(conn->auth->connections, POINTER_CAST(conn->tag)); if (conn->callback != NULL) conn->callback(NULL, conn->context); if (conn->to != NULL) timeout_remove(&conn->to); if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { if (close(conn->fd) < 0) i_fatal("close(%s) failed: %m", conn->path); conn->fd = -1; } i_free(conn->path); i_free(conn); } void master_auth_deinit(struct master_auth **_auth) { struct master_auth *auth = *_auth; struct hash_iterate_context *iter; void *key; struct master_auth_connection *conn; *_auth = NULL; iter = hash_table_iterate_init(auth->connections); while (hash_table_iterate(iter, auth->connections, &key, &conn)) { conn->tag = 0; master_auth_connection_deinit(&conn); } hash_table_iterate_deinit(&iter); hash_table_destroy(&auth->connections); pool_unref(&auth->pool); } static void master_auth_connection_input(struct master_auth_connection *conn) { const struct master_auth_reply *reply; int ret; ret = read(conn->fd, conn->buf + conn->buf_pos, sizeof(conn->buf) - conn->buf_pos); if (ret <= 0) { if (ret == 0 || errno == ECONNRESET) { i_error("read(%s) failed: Remote closed connection " "(destination service { process_limit } reached?)", conn->path); } else { if (errno == EAGAIN) return; i_error("read(%s) failed: %m", conn->path); } master_auth_connection_deinit(&conn); return; } conn->buf_pos += ret; if (conn->buf_pos < sizeof(conn->buf)) return; /* reply is now read */ reply = (const void *)conn->buf; conn->buf_pos = 0; if (conn->tag != reply->tag) i_error("master(%s): Received reply with unknown tag %u", conn->path, reply->tag); else if (conn->callback == NULL) { /* request aborted */ } else { conn->callback(reply, conn->context); conn->callback = NULL; } master_auth_connection_deinit(&conn); } static void master_auth_connection_timeout(struct master_auth_connection *conn) { i_error("master(%s): Auth request timed out (received %u/%u bytes)", conn->path, conn->buf_pos, (unsigned int)sizeof(conn->buf)); master_auth_connection_deinit(&conn); } void master_auth_request_full(struct master_auth *auth, const struct master_auth_request_params *params, master_auth_callback_t *callback, void *context, unsigned int *tag_r) { struct master_auth_connection *conn; struct master_auth_request req; buffer_t *buf; struct stat st; ssize_t ret; i_assert(params->request.client_pid != 0); i_assert(params->request.auth_pid != 0); conn = i_new(struct master_auth_connection, 1); conn->auth = auth; conn->callback = callback; conn->context = context; conn->path = params->socket_path != NULL ? i_strdup(params->socket_path) : i_strdup(auth->default_path); req = params->request; req.tag = ++auth->tag_counter; if (req.tag == 0) req.tag = ++auth->tag_counter; if (fstat(params->client_fd, &st) < 0) i_fatal("fstat(auth dest fd) failed: %m"); req.ino = st.st_ino; buf = buffer_create_dynamic(pool_datastack_create(), sizeof(req) + req.data_size); buffer_append(buf, &req, sizeof(req)); buffer_append(buf, params->data, req.data_size); conn->fd = net_connect_unix_with_retries(conn->path, SOCKET_CONNECT_RETRY_MSECS); if (conn->fd == -1) { i_error("net_connect_unix(%s) failed: %m%s", conn->path, errno != EAGAIN ? "" : " - http://wiki2.dovecot.org/SocketUnavailable"); master_auth_connection_deinit(&conn); return; } ret = fd_send(conn->fd, params->client_fd, buf->data, buf->used); if (ret < 0) { i_error("fd_send(%s, %d) failed: %m", conn->path, params->client_fd); } else if ((size_t)ret != buf->used) { i_error("fd_send(%s) sent only %d of %d bytes", conn->path, (int)ret, (int)buf->used); ret = -1; } if (ret < 0) { master_auth_connection_deinit(&conn); return; } conn->tag = req.tag; conn->to = timeout_add(MASTER_AUTH_REQUEST_TIMEOUT_MSECS, master_auth_connection_timeout, conn); conn->io = io_add(conn->fd, IO_READ, master_auth_connection_input, conn); i_assert(hash_table_lookup(auth->connections, POINTER_CAST(req.tag)) == NULL); hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn); *tag_r = req.tag; } void master_auth_request(struct master_auth *auth, int fd, const struct master_auth_request *request, const unsigned char *data, master_auth_callback_t *callback, void *context, unsigned int *tag_r) { struct master_auth_request_params params; i_zero(¶ms); params.client_fd = fd; params.request = *request; params.data = data; master_auth_request_full(auth, ¶ms, callback, context, tag_r); } void master_auth_request_abort(struct master_auth *auth, unsigned int tag) { struct master_auth_connection *conn; conn = hash_table_lookup(auth->connections, POINTER_CAST(tag)); if (conn == NULL) i_panic("master_auth_request_abort(): tag %u not found", tag); conn->callback = NULL; } dovecot-2.2.33.2/src/lib-master/mountpoint-list.c0000644000175000017500000002207013165463624016517 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "file-copy.h" #include "safe-mkstemp.h" #include "str.h" #include "write-full.h" #include "mountpoint.h" #include "mountpoint-list.h" #include #include #include struct mountpoint_list { pool_t pool; const char *perm_path, *state_path; ARRAY(struct mountpoint_list_rec *) recs; struct stat load_st; bool load_failed; }; struct mountpoint_list_iter { struct mountpoint_list *list; unsigned int idx; }; const char *const mountpoint_list_default_ignore_types[] = { "proc", /* Linux, Solaris */ "procfs", /* AIX, BSD */ "tmpfs", /* Linux */ "sysfs", /* Linux */ "debugfs", /* Linux */ "securityfs", /* Linux */ "devpts", /* Linux */ "devtmpfs", /* Linux */ "rpc_pipefs", /* Linux */ "fusectl", /* Linux */ "nfsd", /* Linux */ "cgroup", /* Linux */ "binfmt_misc", /* Linux */ "devfs", /* Solaris, OSX, BSD */ "ctfs", /* Solaris */ "mntfs", /* Solaris */ "objfs", /* Solaris */ "sharefs", /* Solaris */ "lofs", /* Solaris */ "fd", /* Solaris */ NULL }; const char *const mountpoint_list_default_ignore_prefixes[] = { "/cdrom", "/media", "/sys", "/proc", "/var/run", "/var/tmp", "/tmp", "/run", #ifdef __APPLE__ "/Volumes", "/private/tmp", #endif NULL }; static struct mountpoint_list * ATTR_NULL(1) mountpoint_list_init_internal(const char *perm_path, const char *state_path) { struct mountpoint_list *list; pool_t pool; pool = pool_alloconly_create("mountpoint list", 1024); list = p_new(pool, struct mountpoint_list, 1); list->pool = pool; list->perm_path = p_strdup(pool, perm_path); list->state_path = p_strdup(pool, state_path); p_array_init(&list->recs, pool, 16); (void)mountpoint_list_refresh(list); return list; } struct mountpoint_list * mountpoint_list_init(const char *perm_path, const char *state_path) { return mountpoint_list_init_internal(perm_path, state_path); } struct mountpoint_list * mountpoint_list_init_readonly(const char *state_path) { return mountpoint_list_init_internal(NULL, state_path); } void mountpoint_list_deinit(struct mountpoint_list **_list) { struct mountpoint_list *list = *_list; *_list = NULL; pool_unref(&list->pool); } static int mountpoint_list_load(struct mountpoint_list *list) { struct mountpoint_list_rec rec; struct istream *input; char *p, *line; unsigned int len; int fd, ret = 0; i_zero(&rec); fd = open(list->state_path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) { i_error("open(%s) failed: %m", list->state_path); return -1; } if (list->perm_path == NULL) { /* we're in read-only mode */ return 0; } if (file_copy(list->perm_path, list->state_path, FALSE) < 0) return -1; fd = open(list->perm_path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* perm_path didn't exist either */ return 0; } i_error("open(%s) failed: %m", list->state_path); return -1; } } if (fstat(fd, &list->load_st) < 0) i_error("fstat(%s) failed: %m", list->state_path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { p = strchr(line, ' '); if (p == NULL) { i_error("Corrupted line in %s: %s", list->state_path, line); ret = -1; break; } *p++ = '\0'; rec.mount_path = p; rec.state = line; len = strlen(p); if (len > 0 && p[len-1] == '*') { p[len-1] = '\0'; rec.wildcard = TRUE; } mountpoint_list_add(list, &rec); } if (input->stream_errno != 0) { i_error("read(%s) failed: %m", list->state_path); ret = -1; } i_stream_destroy(&input); return ret; } int mountpoint_list_refresh(struct mountpoint_list *list) { struct stat st; if (list->load_st.st_mtime != 0) { if (stat(list->state_path, &st) < 0) { if (errno == ENOENT) return 0; i_error("stat(%s) failed: %m", list->state_path); return -1; } if (st.st_mtime == list->load_st.st_mtime && ST_MTIME_NSEC(st) == ST_MTIME_NSEC(list->load_st) && st.st_ino == list->load_st.st_ino) { /* unchanged */ return 0; } } array_clear(&list->recs); return mountpoint_list_load(list); } static int mountpoint_list_save_to(struct mountpoint_list *list, const char *path) { struct mountpoint_list_rec *const *recp; string_t *data, *temp_path = t_str_new(128); int fd; str_append(temp_path, path); str_append(temp_path, ".tmp."); fd = safe_mkstemp(temp_path, 0644, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(temp_path)); return -1; } data = t_str_new(256); array_foreach(&list->recs, recp) { str_append(data, (*recp)->state); str_append_c(data, ' '); str_append(data, (*recp)->mount_path); if ((*recp)->wildcard) str_append_c(data, '*'); str_append_c(data, '\n'); } if (write_full(fd, str_data(data), str_len(data)) < 0) { i_error("write(%s) failed: %m", str_c(temp_path)); i_close_fd(&fd); } else if (fdatasync(fd) < 0) { i_error("fdatasync(%s) failed: %m", str_c(temp_path)); i_close_fd(&fd); } else if (close(fd) < 0) { i_error("close(%s) failed: %m", str_c(temp_path)); } else if (rename(str_c(temp_path), path) < 0) { i_error("rename(%s, %s) failed: %m", str_c(temp_path), path); } else { return 0; } i_unlink(str_c(temp_path)); return -1; } int mountpoint_list_save(struct mountpoint_list *list) { int ret; i_assert(list->perm_path != NULL); if (list->load_failed) return -1; ret = mountpoint_list_save_to(list, list->state_path); if (mountpoint_list_save_to(list, list->perm_path) < 0) ret = -1; return ret; } void mountpoint_list_add(struct mountpoint_list *list, const struct mountpoint_list_rec *rec) { struct mountpoint_list_rec *new_rec; new_rec = mountpoint_list_find(list, rec->mount_path); if (new_rec == NULL) { new_rec = p_new(list->pool, struct mountpoint_list_rec, 1); new_rec->mount_path = p_strdup(list->pool, rec->mount_path); array_append(&list->recs, &new_rec, 1); } new_rec->state = p_strdup(list->pool, rec->state); new_rec->wildcard = rec->wildcard; new_rec->mounted = rec->mounted; } bool mountpoint_list_remove(struct mountpoint_list *list, const char *mount_path) { struct mountpoint_list_rec *const *recs; unsigned int i, count; recs = array_get(&list->recs, &count); for (i = 0; i < count; i++) { if (strcmp(recs[i]->mount_path, mount_path) == 0) { array_delete(&list->recs, i, 1); return TRUE; } } return FALSE; } static bool str_array_find_prefix(const char *const *prefixes, const char *str) { if (prefixes == NULL) return FALSE; for (; *prefixes != NULL; prefixes++) { if (strncmp(*prefixes, str, strlen(*prefixes)) == 0) return TRUE; } return FALSE; } int mountpoint_list_add_missing(struct mountpoint_list *list, const char *default_state, const char *const *ignore_prefixes, const char *const *ignore_types) { struct mountpoint_list_rec new_rec, *rec, *const *recp; struct mountpoint_iter *iter; const struct mountpoint *mnt; i_zero(&new_rec); new_rec.state = default_state; new_rec.mounted = TRUE; array_foreach(&list->recs, recp) (*recp)->mounted = FALSE; /* get a sorted list of all current mountpoints */ iter = mountpoint_iter_init(); while ((mnt = mountpoint_iter_next(iter)) != NULL) { rec = mountpoint_list_find(list, mnt->mount_path); if (rec != NULL) { if (!rec->wildcard) rec->mounted = TRUE; } else if (!str_array_find(ignore_types, mnt->type) && !str_array_find_prefix(ignore_prefixes, mnt->mount_path)) { new_rec.mount_path = mnt->mount_path; mountpoint_list_add(list, &new_rec); } } return mountpoint_iter_deinit(&iter); } int mountpoint_list_update_mounted(struct mountpoint_list *list) { struct mountpoint_list_rec *rec, *const *recp; struct mountpoint_iter *iter; const struct mountpoint *mnt; array_foreach(&list->recs, recp) (*recp)->mounted = FALSE; iter = mountpoint_iter_init(); while ((mnt = mountpoint_iter_next(iter)) != NULL) { rec = mountpoint_list_find(list, mnt->mount_path); if (rec != NULL && !rec->wildcard) rec->mounted = TRUE; } return mountpoint_iter_deinit(&iter); } struct mountpoint_list_rec * mountpoint_list_find(struct mountpoint_list *list, const char *path) { struct mountpoint_list_rec **recp; array_foreach_modifiable(&list->recs, recp) { const char *prefix = (*recp)->mount_path; unsigned int prefix_len = strlen(prefix); if (strncmp(prefix, path, prefix_len) == 0 && (path[prefix_len] == '/' || path[prefix_len] == '\0')) return *recp; } return NULL; } struct mountpoint_list_iter * mountpoint_list_iter_init(struct mountpoint_list *list) { struct mountpoint_list_iter *iter; iter = i_new(struct mountpoint_list_iter, 1); iter->list = list; return iter; } struct mountpoint_list_rec * mountpoint_list_iter_next(struct mountpoint_list_iter *iter) { struct mountpoint_list_rec *const *recp; if (iter->idx == array_count(&iter->list->recs)) return NULL; recp = array_idx(&iter->list->recs, iter->idx++); return *recp; } void mountpoint_list_iter_deinit(struct mountpoint_list_iter **_iter) { struct mountpoint_list_iter *iter = *_iter; *_iter = NULL; i_free(iter); } dovecot-2.2.33.2/src/lib-master/test-master-service-settings-cache.c0000644000175000017500000000611213165463624022136 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "settings-parser.h" #include "master-service-private.h" #include "master-service-settings.h" #include "master-service-settings-cache.h" struct master_service *master_service; static struct master_service test_master_service; static struct master_service_settings set; static struct master_service_settings_input input; static struct master_service_settings_output output; static struct master_service_settings_cache *cache; struct test_service_settings { const char *foo; }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct test_service_settings, name), NULL } static const struct setting_define test_setting_defines[] = { DEF(SET_STR, foo), SETTING_DEFINE_LIST_END }; static const struct test_service_settings test_default_settings = { .foo = "" }; static const struct setting_parser_info test_setting_parser_info = { .module_name = "module", .defines = test_setting_defines, .defaults = &test_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct test_service_settings), .parent_offset = (size_t)-1 }; int master_service_settings_read(struct master_service *service ATTR_UNUSED, const struct master_service_settings_input *input ATTR_UNUSED, struct master_service_settings_output *output_r, const char **error_r ATTR_UNUSED) { *output_r = output; return 0; } const struct master_service_settings * master_service_settings_get(struct master_service *service ATTR_UNUSED) { return &set; } static void test_master_service_settings_cache_once(void) { const struct setting_parser_context *parser; const char *error; output.used_local = output.service_uses_local && rand() % 2; if (output.used_local) { input.local_ip.family = AF_INET; input.local_ip.u.ip4.s_addr = 100 + rand() % 100; } output.used_remote = output.service_uses_remote && rand() % 2; if (output.used_remote) { input.remote_ip.family = AF_INET; input.remote_ip.u.ip4.s_addr = 100 + rand() % 100; } test_assert(master_service_settings_cache_read(cache, &input, NULL, &parser, &error) == 0); } static void test_master_service_settings_cache(void) { int i, j; for (i = 1; i < 4; i++) { cache = master_service_settings_cache_init(master_service, "module", "service_name"); output.service_uses_local = (i & 1) != 0; output.service_uses_remote = (i & 2) != 0; for (j = 0; j < 1000; j++) test_master_service_settings_cache_once(); master_service_settings_cache_deinit(&cache); } } int main(void) { static void (*test_functions[])(void) = { test_master_service_settings_cache, NULL }; pool_t pool; int ret; i_zero(&input); input.module = "module"; input.service = "service_name"; set.config_cache_size = 1024*4; pool = pool_alloconly_create("set pool", 1024); test_master_service.set_parser = settings_parser_init(pool, &test_setting_parser_info, 0); master_service = &test_master_service; ret = test_run(test_functions); settings_parser_deinit(&test_master_service.set_parser); pool_unref(&pool); return ret; } dovecot-2.2.33.2/src/lib-master/ipc-client.h0000644000175000017500000000102013123174404015343 00000000000000#ifndef IPC_CLIENT_H #define IPC_CLIENT_H enum ipc_client_cmd_state { IPC_CLIENT_CMD_STATE_REPLY, IPC_CLIENT_CMD_STATE_OK, IPC_CLIENT_CMD_STATE_ERROR }; typedef void ipc_client_callback_t(enum ipc_client_cmd_state state, const char *data, void *context); struct ipc_client * ipc_client_init(const char *ipc_socket_path); void ipc_client_deinit(struct ipc_client **client); void ipc_client_cmd(struct ipc_client *client, const char *cmd, ipc_client_callback_t *callback, void *context) ATTR_NULL(4); #endif dovecot-2.2.33.2/src/lib-master/master-service-settings.c0000644000175000017500000004247413165463624020133 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "abspath.h" #include "istream.h" #include "write-full.h" #include "str.h" #include "strescape.h" #include "syslog-util.h" #include "eacces-error.h" #include "env-util.h" #include "execv-const.h" #include "settings-parser.h" #include "master-service-private.h" #include "master-service-ssl-settings.h" #include "master-service-settings.h" #include #include #include #include #define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf" #define DOVECOT_CONFIG_SOCKET_PATH PKG_RUNDIR"/config" #define CONFIG_READ_TIMEOUT_SECS 10 #define CONFIG_HANDSHAKE "VERSION\tconfig\t2\t0\n" #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct master_service_settings, name), NULL } static bool master_service_settings_check(void *_set, pool_t pool, const char **error_r); static const struct setting_define master_service_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, state_dir), DEF(SET_STR, log_path), DEF(SET_STR, info_log_path), DEF(SET_STR, debug_log_path), DEF(SET_STR, log_timestamp), DEF(SET_STR, syslog_facility), DEF(SET_STR, import_environment), DEF(SET_SIZE, config_cache_size), DEF(SET_BOOL, version_ignore), DEF(SET_BOOL, shutdown_clients), DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, haproxy_trusted_networks), DEF(SET_TIME, haproxy_timeout), SETTING_DEFINE_LIST_END }; /* */ #ifdef HAVE_SYSTEMD # define ENV_SYSTEMD " LISTEN_PID LISTEN_FDS" #else # define ENV_SYSTEMD "" #endif #ifdef DEBUG # define ENV_GDB " GDB DEBUG_SILENT" #else # define ENV_GDB "" #endif /* */ static const struct master_service_settings master_service_default_settings = { .base_dir = PKG_RUNDIR, .state_dir = PKG_STATEDIR, .log_path = "syslog", .info_log_path = "", .debug_log_path = "", .log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT, .syslog_facility = "mail", .import_environment = "TZ CORE_OUTOFMEM CORE_ERROR" ENV_SYSTEMD ENV_GDB, .config_cache_size = 1024*1024, .version_ignore = FALSE, .shutdown_clients = TRUE, .verbose_proctitle = FALSE, .haproxy_trusted_networks = "", .haproxy_timeout = 3 }; const struct setting_parser_info master_service_setting_parser_info = { .module_name = "master", .defines = master_service_setting_defines, .defaults = &master_service_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct master_service_settings), .parent_offset = (size_t)-1, .check_func = master_service_settings_check }; /* */ static bool master_service_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct master_service_settings *set = _set; int facility; if (*set->log_path == '\0') { /* default to syslog logging */ set->log_path = "syslog"; } if (!syslog_facility_find(set->syslog_facility, &facility)) { *error_r = t_strdup_printf("Unknown syslog_facility: %s", set->syslog_facility); return FALSE; } return TRUE; } /* */ static void ATTR_NORETURN master_service_exec_config(struct master_service *service, const struct master_service_settings_input *input) { const char **conf_argv, *binary_path = service->argv[0]; unsigned int i, argv_max_count; (void)t_binary_abspath(&binary_path); if (!service->keep_environment && !input->preserve_environment) { if (input->preserve_home) master_service_import_environment("HOME"); if (input->preserve_user) master_service_import_environment("USER"); if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) master_service_import_environment("LOG_STDERR_TIMESTAMP"); /* doveconf empties the environment before exec()ing us back if DOVECOT_PRESERVE_ENVS is set, so make sure it is. */ if (getenv(DOVECOT_PRESERVE_ENVS_ENV) == NULL) env_put(DOVECOT_PRESERVE_ENVS_ENV"="); } else { /* make sure doveconf doesn't remove any environment */ env_remove(DOVECOT_PRESERVE_ENVS_ENV); } if (input->use_sysexits) env_put("USE_SYSEXITS=1"); /* @UNSAFE */ i = 0; argv_max_count = 11 + (service->argc + 1) + 1; conf_argv = t_new(const char *, argv_max_count); conf_argv[i++] = DOVECOT_CONFIG_BIN_PATH; if (input->service != NULL) { conf_argv[i++] = "-f"; conf_argv[i++] = t_strconcat("service=", input->service, NULL); } conf_argv[i++] = "-c"; conf_argv[i++] = service->config_path; if (input->module != NULL) { conf_argv[i++] = "-m"; conf_argv[i++] = input->module; if (service->want_ssl_settings) { conf_argv[i++] = "-m"; conf_argv[i++] = "ssl"; } } if (input->parse_full_config) conf_argv[i++] = "-p"; conf_argv[i++] = "-e"; conf_argv[i++] = binary_path; memcpy(conf_argv+i, service->argv + 1, (service->argc) * sizeof(conf_argv[0])); i += service->argc; i_assert(i < argv_max_count); execv_const(conf_argv[0], conf_argv); } static void config_exec_fallback(struct master_service *service, const struct master_service_settings_input *input) { const char *path; struct stat st; int saved_errno = errno; if (input->never_exec) return; path = input->config_path != NULL ? input->config_path : master_service_get_config_path(service); if (stat(path, &st) == 0 && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode)) { /* it's a file, not a socket/pipe */ master_service_exec_config(service, input); } errno = saved_errno; } static int master_service_open_config(struct master_service *service, const struct master_service_settings_input *input, const char **path_r, const char **error_r) { struct stat st; const char *path; int fd; *path_r = path = input->config_path != NULL ? input->config_path : master_service_get_config_path(service); if (service->config_fd != -1 && input->config_path == NULL && !service->config_path_changed_with_param) { /* use the already opened config socket */ fd = service->config_fd; service->config_fd = -1; return fd; } if (!service->config_path_from_master && !service->config_path_changed_with_param && input->config_path == NULL) { /* first try to connect to the default config socket. configuration may contain secrets, so in default config this fails because the socket is 0600. it's useful for developers though. :) */ fd = net_connect_unix(DOVECOT_CONFIG_SOCKET_PATH); if (fd >= 0) { *path_r = DOVECOT_CONFIG_SOCKET_PATH; net_set_nonblock(fd, FALSE); return fd; } /* fallback to executing doveconf */ } if (stat(path, &st) < 0) { *error_r = errno == EACCES ? eacces_error_get("stat", path) : t_strdup_printf("stat(%s) failed: %m", path); return -1; } if (!S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode)) { /* it's not an UNIX socket, don't even try to connect */ fd = -1; errno = ENOTSOCK; } else { fd = net_connect_unix_with_retries(path, 1000); } if (fd < 0) { *error_r = t_strdup_printf("net_connect_unix(%s) failed: %m", path); config_exec_fallback(service, input); return -1; } net_set_nonblock(fd, FALSE); return fd; } static void config_build_request(struct master_service *service, string_t *str, const struct master_service_settings_input *input) { str_append(str, "REQ"); if (input->module != NULL) { str_printfa(str, "\tmodule=%s", input->module); if (service->want_ssl_settings) str_append(str, "\tmodule=ssl"); } if (input->service != NULL) str_printfa(str, "\tservice=%s", input->service); if (input->username != NULL) str_printfa(str, "\tuser=%s", input->username); if (input->local_ip.family != 0) str_printfa(str, "\tlip=%s", net_ip2addr(&input->local_ip)); if (input->remote_ip.family != 0) str_printfa(str, "\trip=%s", net_ip2addr(&input->remote_ip)); if (input->local_name != NULL) str_printfa(str, "\tlname=%s", input->local_name); str_append_c(str, '\n'); } static int config_send_request(struct master_service *service, const struct master_service_settings_input *input, int fd, const char *path, const char **error_r) { int ret; T_BEGIN { string_t *str; str = t_str_new(128); str_append(str, CONFIG_HANDSHAKE); config_build_request(service, str, input); ret = write_full(fd, str_data(str), str_len(str)); } T_END; if (ret < 0) { *error_r = t_strdup_printf("write_full(%s) failed: %m", path); return -1; } return 0; } static int master_service_apply_config_overrides(struct master_service *service, struct setting_parser_context *parser, const char **error_r) { const char *const *overrides; unsigned int i, count; overrides = array_get(&service->config_overrides, &count); for (i = 0; i < count; i++) { if (settings_parse_line(parser, overrides[i]) < 0) { *error_r = t_strdup_printf( "Invalid -o parameter %s: %s", overrides[i], settings_parser_get_error(parser)); return -1; } settings_parse_set_key_expandeded(parser, service->set_pool, t_strcut(overrides[i], '=')); } return 0; } static int config_read_reply_header(struct istream *istream, const char *path, pool_t pool, const struct master_service_settings_input *input, struct master_service_settings_output *output_r, const char **error_r) { const char *line; ssize_t ret; while ((ret = i_stream_read(istream)) > 0) { line = i_stream_next_line(istream); if (line != NULL) break; } if (ret <= 0) { if (ret == 0) return 1; *error_r = istream->stream_errno != 0 ? t_strdup_printf("read(%s) failed: %s", path, i_stream_get_error(istream)) : t_strdup_printf("read(%s) failed: EOF", path); return -1; } T_BEGIN { const char *const *arg = t_strsplit_tabescaped(line); ARRAY_TYPE(const_string) services; p_array_init(&services, pool, 8); for (; *arg != NULL; arg++) { if (strcmp(*arg, "service-uses-local") == 0) output_r->service_uses_local = TRUE; else if (strcmp(*arg, "service-uses-remote") == 0) output_r->service_uses_remote = TRUE; if (strcmp(*arg, "used-local") == 0) output_r->used_local = TRUE; else if (strcmp(*arg, "used-remote") == 0) output_r->used_remote = TRUE; else if (strncmp(*arg, "service=", 8) == 0) { const char *name = p_strdup(pool, *arg + 8); array_append(&services, &name, 1); } } if (input->service == NULL) { array_append_zero(&services); output_r->specific_services = array_idx(&services, 0); } } T_END; return 0; } void master_service_config_socket_try_open(struct master_service *service) { struct master_service_settings_input input; const char *path, *error; int fd; /* we'll get here before command line parameters have been parsed, so -O, -c and -i parameters haven't been handled yet at this point. this means we could end up opening config socket connection unnecessarily, but this isn't a problem. we'll just have to ignore it later on. (unfortunately there isn't a master_service_*() call where this function would be better called.) */ if (getenv("DOVECONF_ENV") != NULL || (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) != 0) return; i_zero(&input); input.never_exec = TRUE; fd = master_service_open_config(service, &input, &path, &error); if (fd != -1) service->config_fd = fd; } int master_service_settings_read(struct master_service *service, const struct master_service_settings_input *input, struct master_service_settings_output *output_r, const char **error_r) { ARRAY(const struct setting_parser_info *) all_roots; const struct setting_parser_info *tmp_root; struct setting_parser_context *parser; struct istream *istream; const char *path = NULL, *error; void **sets; unsigned int i; int ret, fd = -1; time_t now, timeout; bool use_environment, retry; i_zero(output_r); if (getenv("DOVECONF_ENV") == NULL && (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) { retry = service->config_fd != -1; for (;;) { fd = master_service_open_config(service, input, &path, error_r); if (fd == -1) { if (errno == EACCES) output_r->permission_denied = TRUE; return -1; } if (config_send_request(service, input, fd, path, error_r) == 0) break; i_close_fd(&fd); if (!retry) { config_exec_fallback(service, input); return -1; } /* config process died, retry connecting */ retry = FALSE; } } if (service->set_pool != NULL) { if (service->set_parser != NULL) settings_parser_deinit(&service->set_parser); p_clear(service->set_pool); } else { service->set_pool = pool_alloconly_create("master service settings", 16384); } p_array_init(&all_roots, service->set_pool, 8); tmp_root = &master_service_setting_parser_info; array_append(&all_roots, &tmp_root, 1); if (service->want_ssl_settings) { tmp_root = &master_service_ssl_setting_parser_info; array_append(&all_roots, &tmp_root, 1); } if (input->roots != NULL) { for (i = 0; input->roots[i] != NULL; i++) array_append(&all_roots, &input->roots[i], 1); } parser = settings_parser_init_list(service->set_pool, array_idx(&all_roots, 0), array_count(&all_roots), SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); if (fd != -1) { istream = i_stream_create_fd(fd, (size_t)-1, FALSE); now = time(NULL); timeout = now + CONFIG_READ_TIMEOUT_SECS; do { alarm(timeout - now); ret = config_read_reply_header(istream, path, service->set_pool, input, output_r, error_r); if (ret == 0) { ret = settings_parse_stream_read(parser, istream); if (ret < 0) *error_r = settings_parser_get_error(parser); } alarm(0); if (ret <= 0) break; /* most likely timed out, but just in case some other signal was delivered early check if we need to continue */ now = time(NULL); } while (now < timeout); i_stream_unref(&istream); if (ret != 0) { if (ret > 0) { *error_r = t_strdup_printf( "Timeout reading config from %s", path); } i_close_fd(&fd); config_exec_fallback(service, input); settings_parser_deinit(&parser); return -1; } if ((service->flags & MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN) != 0 && service->config_fd == -1 && input->config_path == NULL) service->config_fd = fd; else i_close_fd(&fd); use_environment = FALSE; } else { use_environment = TRUE; } if (use_environment || service->keep_environment) { if (settings_parse_environ(parser) < 0) { *error_r = t_strdup(settings_parser_get_error(parser)); settings_parser_deinit(&parser); return -1; } } if (array_is_created(&service->config_overrides)) { if (master_service_apply_config_overrides(service, parser, error_r) < 0) { settings_parser_deinit(&parser); return -1; } } if (!settings_parser_check(parser, service->set_pool, &error)) { *error_r = t_strdup_printf("Invalid settings: %s", error); settings_parser_deinit(&parser); return -1; } sets = settings_parser_get_list(parser); service->set = sets[0]; service->set_parser = parser; if (service->set->version_ignore && (service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) { /* running standalone. we want to ignore plugin versions. */ service->version_string = NULL; } if (service->set->shutdown_clients) master_service_set_die_with_master(master_service, TRUE); /* if we change any settings afterwards, they're in expanded form. especially all settings from userdb are already expanded. */ settings_parse_set_expanded(service->set_parser, TRUE); return 0; } int master_service_settings_read_simple(struct master_service *service, const struct setting_parser_info **roots, const char **error_r) { struct master_service_settings_input input; struct master_service_settings_output output; i_zero(&input); input.roots = roots; input.module = service->name; return master_service_settings_read(service, &input, &output, error_r); } pool_t master_service_settings_detach(struct master_service *service) { pool_t pool = service->set_pool; settings_parser_deinit(&service->set_parser); service->set_pool = NULL; return pool; } const struct master_service_settings * master_service_settings_get(struct master_service *service) { void **sets; sets = settings_parser_get_list(service->set_parser); return sets[0]; } void **master_service_settings_get_others(struct master_service *service) { return master_service_settings_parser_get_others(service, service->set_parser); } void **master_service_settings_parser_get_others(struct master_service *service, const struct setting_parser_context *set_parser) { return settings_parser_get_list(set_parser) + 1 + (service->want_ssl_settings ? 1 : 0); } struct setting_parser_context * master_service_get_settings_parser(struct master_service *service) { return service->set_parser; } int master_service_set(struct master_service *service, const char *line) { return settings_parse_line(service->set_parser, line); } bool master_service_set_has_config_override(struct master_service *service, const char *key) { const char *const *override, *key_root; bool ret; if (!array_is_created(&service->config_overrides)) return FALSE; key_root = settings_parse_unalias(service->set_parser, key); if (key_root == NULL) key_root = key; array_foreach(&service->config_overrides, override) { T_BEGIN { const char *okey, *okey_root; okey = t_strcut(*override, '='); okey_root = settings_parse_unalias(service->set_parser, okey); if (okey_root == NULL) okey_root = okey; ret = strcmp(okey_root, key_root) == 0; } T_END; if (ret) return TRUE; } return FALSE; } dovecot-2.2.33.2/src/lib-master/master-service-ssl.c0000644000175000017500000001031513165463624017061 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "iostream-ssl.h" #include "master-service-private.h" #include "master-service-settings.h" #include "master-service-ssl-settings.h" #include "master-service-ssl.h" #include /* Check every 30 minutes if parameters file has been updated */ #define SSL_PARAMS_CHECK_INTERVAL (60*30) #define SSL_PARAMETERS_PATH "ssl-params" static int ssl_refresh_parameters(struct master_service *service) { #define BUF_APPEND_SIZE 1024 const char *path; buffer_t *buf; void *data; ssize_t ret; int fd; if (ioloop_time == 0 || service->ssl_params_last_refresh > ioloop_time - SSL_PARAMS_CHECK_INTERVAL) return 0; service->ssl_params_last_refresh = ioloop_time; path = t_strdup_printf("%s/"SSL_PARAMETERS_PATH, service->set->base_dir); fd = net_connect_unix(path); if (fd == -1) { i_error("connect(%s) failed: %m", path); return -1; } net_set_nonblock(fd, FALSE); buf = buffer_create_dynamic(default_pool, BUF_APPEND_SIZE*2); for (;;) { data = buffer_append_space_unsafe(buf, BUF_APPEND_SIZE); ret = read(fd, data, BUF_APPEND_SIZE); buffer_set_used_size(buf, buf->used - BUF_APPEND_SIZE + (ret < 0 ? 0 : ret)); if (ret <= 0) break; } if (ret < 0) i_error("read(%s) failed: %m", path); else if (ssl_iostream_context_import_params(service->ssl_ctx, buf) < 0) { i_error("Corrupted SSL parameters file in state_dir: " "ssl-parameters.dat - disabling SSL %u", (int)buf->used); ret = -1; } i_close_fd(&fd); buffer_free(&buf); return ret < 0 ? -1 : 0; } int master_service_ssl_init(struct master_service *service, struct istream **input, struct ostream **output, struct ssl_iostream **ssl_iostream_r, const char **error_r) { const struct master_service_ssl_settings *set; struct ssl_iostream_settings ssl_set; i_assert(service->ssl_ctx_initialized); if (service->ssl_ctx == NULL) { *error_r = "Failed to initialize SSL context"; return -1; } (void)ssl_refresh_parameters(service); set = master_service_ssl_settings_get(service); i_zero(&ssl_set); ssl_set.verbose = set->verbose_ssl; ssl_set.verify_remote_cert = set->ssl_verify_client_cert; return io_stream_create_ssl_server(service->ssl_ctx, &ssl_set, input, output, ssl_iostream_r, error_r); } bool master_service_ssl_is_enabled(struct master_service *service) { return service->ssl_ctx != NULL; } void master_service_ssl_ctx_init(struct master_service *service) { const struct master_service_ssl_settings *set; struct ssl_iostream_settings ssl_set; const char *error; if (service->ssl_ctx_initialized) return; service->ssl_ctx_initialized = TRUE; /* must be called after master_service_init_finish() so that if initialization fails we can close the SSL listeners */ i_assert(service->listeners != NULL || service->socket_count == 0); set = master_service_ssl_settings_get(service); if (strcmp(set->ssl, "no") == 0) { /* SSL disabled, don't use it */ return; } i_zero(&ssl_set); ssl_set.protocols = set->ssl_protocols; ssl_set.cipher_list = set->ssl_cipher_list; ssl_set.ca = set->ssl_ca; ssl_set.cert = set->ssl_cert; ssl_set.key = set->ssl_key; ssl_set.key_password = set->ssl_key_password; ssl_set.cert_username_field = set->ssl_cert_username_field; ssl_set.crypto_device = set->ssl_crypto_device; ssl_set.verbose = set->verbose_ssl; ssl_set.verify_remote_cert = set->ssl_verify_client_cert; ssl_set.prefer_server_ciphers = set->ssl_prefer_server_ciphers; ssl_set.compression = set->parsed_opts.compression; if (ssl_iostream_context_init_server(&ssl_set, &service->ssl_ctx, &error) < 0) { i_error("SSL context initialization failed, disabling SSL: %s", error); master_service_ssl_io_listeners_remove(service); return; } if (ssl_refresh_parameters(service) < 0) { i_error("Couldn't initialize SSL parameters, disabling SSL"); ssl_iostream_context_deinit(&service->ssl_ctx); master_service_ssl_io_listeners_remove(service); return; } } void master_service_ssl_ctx_deinit(struct master_service *service) { if (service->ssl_ctx != NULL) ssl_iostream_context_deinit(&service->ssl_ctx); service->ssl_ctx_initialized = FALSE; } dovecot-2.2.33.2/src/lib-master/master-login-auth.c0000644000175000017500000003167413165463624016704 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "ioloop.h" #include "hostpid.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "hex-binary.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "master-interface.h" #include "master-service.h" #include "master-auth.h" #include "master-login-auth.h" #define AUTH_MAX_INBUF_SIZE 8192 struct master_login_auth_request { struct master_login_auth_request *prev, *next; unsigned int id; time_t create_stamp; pid_t auth_pid; unsigned int auth_id; unsigned int client_pid; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; master_login_auth_request_callback_t *callback; void *context; unsigned int aborted:1; }; struct master_login_auth { pool_t pool; const char *auth_socket_path; int refcount; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; unsigned int id_counter; HASH_TABLE(void *, struct master_login_auth_request *) requests; /* linked list of requests, ordered by create_stamp */ struct master_login_auth_request *request_head, *request_tail; pid_t auth_server_pid; unsigned int request_auth_token:1; unsigned int version_received:1; unsigned int spid_received:1; }; static void master_login_auth_set_timeout(struct master_login_auth *auth); static void master_login_auth_check_spids(struct master_login_auth *auth); struct master_login_auth * master_login_auth_init(const char *auth_socket_path, bool request_auth_token) { struct master_login_auth *auth; pool_t pool; pool = pool_alloconly_create("master login auth", 1024); auth = p_new(pool, struct master_login_auth, 1); auth->pool = pool; auth->auth_socket_path = p_strdup(pool, auth_socket_path); auth->request_auth_token = request_auth_token; auth->refcount = 1; auth->fd = -1; hash_table_create_direct(&auth->requests, pool, 0); auth->id_counter = (rand() % 32767) * 131072U; return auth; } static void request_internal_failure(struct master_login_auth_request *request, const char *reason) { i_error("%s (client-pid=%u client-id=%u)", reason, request->client_pid, request->auth_id); request->callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE, request->context); } void master_login_auth_disconnect(struct master_login_auth *auth) { struct master_login_auth_request *request; while (auth->request_head != NULL) { request = auth->request_head; DLLIST2_REMOVE(&auth->request_head, &auth->request_tail, request); request_internal_failure(request, "Disconnected from auth server, aborting"); i_free(request); } hash_table_clear(auth->requests, FALSE); if (auth->to != NULL) timeout_remove(&auth->to); if (auth->io != NULL) io_remove(&auth->io); if (auth->fd != -1) { if (auth->input != NULL) i_stream_destroy(&auth->input); if (auth->output != NULL) o_stream_destroy(&auth->output); net_disconnect(auth->fd); auth->fd = -1; } auth->version_received = FALSE; } static void master_login_auth_unref(struct master_login_auth **_auth) { struct master_login_auth *auth = *_auth; *_auth = NULL; i_assert(auth->refcount > 0); if (--auth->refcount > 0) return; hash_table_destroy(&auth->requests); pool_unref(&auth->pool); } void master_login_auth_deinit(struct master_login_auth **_auth) { struct master_login_auth *auth = *_auth; *_auth = NULL; master_login_auth_disconnect(auth); master_login_auth_unref(&auth); } static unsigned int auth_get_next_timeout_secs(struct master_login_auth *auth) { time_t expires; expires = auth->request_head->create_stamp + MASTER_AUTH_LOOKUP_TIMEOUT_SECS; return expires <= ioloop_time ? 0 : expires - ioloop_time; } static void master_login_auth_timeout(struct master_login_auth *auth) { struct master_login_auth_request *request; const char *reason; while (auth->request_head != NULL && auth_get_next_timeout_secs(auth) == 0) { request = auth->request_head; DLLIST2_REMOVE(&auth->request_head, &auth->request_tail, request); hash_table_remove(auth->requests, POINTER_CAST(request->id)); reason = t_strdup_printf( "Auth server request timed out after %u secs", (unsigned int)(ioloop_time - request->create_stamp)); request_internal_failure(request, reason); i_free(request); } timeout_remove(&auth->to); master_login_auth_set_timeout(auth); } static void master_login_auth_set_timeout(struct master_login_auth *auth) { i_assert(auth->to == NULL); if (auth->request_head != NULL) { auth->to = timeout_add(auth_get_next_timeout_secs(auth) * 1000, master_login_auth_timeout, auth); } } static void master_login_auth_request_remove(struct master_login_auth *auth, struct master_login_auth_request *request) { bool update_timeout; update_timeout = request->prev == NULL; hash_table_remove(auth->requests, POINTER_CAST(request->id)); DLLIST2_REMOVE(&auth->request_head, &auth->request_tail, request); if (update_timeout) { timeout_remove(&auth->to); master_login_auth_set_timeout(auth); } } static struct master_login_auth_request * master_login_auth_lookup_request(struct master_login_auth *auth, unsigned int id) { struct master_login_auth_request *request; request = hash_table_lookup(auth->requests, POINTER_CAST(id)); if (request == NULL) { i_error("Auth server sent reply with unknown ID %u", id); return NULL; } master_login_auth_request_remove(auth, request); if (request->aborted) { request->callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE, request->context); i_free(request); return NULL; } return request; } static bool master_login_auth_input_user(struct master_login_auth *auth, const char *args) { struct master_login_auth_request *request; const char *const *list; unsigned int id; /* [..] */ list = t_strsplit_tabescaped(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("Auth server sent corrupted USER line"); return FALSE; } request = master_login_auth_lookup_request(auth, id); if (request != NULL) { request->callback(list + 1, NULL, request->context); i_free(request); } return TRUE; } static bool master_login_auth_input_notfound(struct master_login_auth *auth, const char *args) { struct master_login_auth_request *request; unsigned int id; if (str_to_uint(args, &id) < 0) { i_error("Auth server sent corrupted NOTFOUND line"); return FALSE; } request = master_login_auth_lookup_request(auth, id); if (request != NULL) { const char *reason = t_strdup_printf( "Authenticated user not found from userdb, " "auth lookup id=%u", id); request_internal_failure(request, reason); i_free(request); } return TRUE; } static bool master_login_auth_input_fail(struct master_login_auth *auth, const char *args_line) { struct master_login_auth_request *request; const char *const *args, *error = NULL; unsigned int i, id; args = t_strsplit_tabescaped(args_line); if (args[0] == NULL || str_to_uint(args[0], &id) < 0) { i_error("Auth server sent broken FAIL line"); return FALSE; } for (i = 1; args[i] != NULL; i++) { if (strncmp(args[i], "reason=", 7) == 0) error = args[i] + 7; } request = master_login_auth_lookup_request(auth, id); if (request != NULL) { if (error == NULL) { request_internal_failure(request, "Internal auth failure"); } else { i_error("Internal auth failure: %s " "(client-pid=%u client-id=%u)", error, request->client_pid, request->auth_id); request->callback(NULL, error, request->context); } i_free(request); } return TRUE; } static void master_login_auth_input(struct master_login_auth *auth) { const char *line; bool ret; switch (i_stream_read(auth->input)) { case 0: return; case -1: /* disconnected. stop accepting new connections, because in default configuration we no longer have permissions to connect back to auth-master */ master_service_stop_new_connections(master_service); master_login_auth_disconnect(auth); return; case -2: /* buffer full */ i_error("Auth server sent us too long line"); master_login_auth_disconnect(auth); return; } if (!auth->version_received) { line = i_stream_next_line(auth->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_MASTER_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication server not compatible with " "master process (mixed old and new binaries?)"); master_login_auth_disconnect(auth); return; } auth->version_received = TRUE; } if (!auth->spid_received) { line = i_stream_next_line(auth->input); if (line == NULL) return; if (strncmp(line, "SPID\t", 5) != 0 || str_to_pid(line + 5, &auth->auth_server_pid) < 0) { i_error("Authentication server didn't " "send valid SPID as expected: %s", line); master_login_auth_disconnect(auth); return; } auth->spid_received = TRUE; master_login_auth_check_spids(auth); } auth->refcount++; while ((line = i_stream_next_line(auth->input)) != NULL) { if (strncmp(line, "USER\t", 5) == 0) ret = master_login_auth_input_user(auth, line + 5); else if (strncmp(line, "NOTFOUND\t", 9) == 0) ret = master_login_auth_input_notfound(auth, line + 9); else if (strncmp(line, "FAIL\t", 5) == 0) ret = master_login_auth_input_fail(auth, line + 5); else ret = TRUE; if (!ret || auth->input == NULL) { master_login_auth_disconnect(auth); break; } } master_login_auth_unref(&auth); } static int master_login_auth_connect(struct master_login_auth *auth) { int fd; i_assert(auth->fd == -1); fd = net_connect_unix_with_retries(auth->auth_socket_path, 1000); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m", auth->auth_socket_path); return -1; } auth->fd = fd; auth->input = i_stream_create_fd(fd, AUTH_MAX_INBUF_SIZE, FALSE); auth->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(auth->output, TRUE); auth->io = io_add(fd, IO_READ, master_login_auth_input, auth); return 0; } static bool auth_request_check_spid(struct master_login_auth *auth, struct master_login_auth_request *req) { if (auth->auth_server_pid != req->auth_pid && auth->spid_received) { /* auth server was restarted. don't even attempt a login. */ i_warning("Auth server restarted (pid %u -> %u), aborting auth", (unsigned int)req->auth_pid, (unsigned int)auth->auth_server_pid); return FALSE; } return TRUE; } static void master_login_auth_check_spids(struct master_login_auth *auth) { struct master_login_auth_request *req, *next; for (req = auth->request_head; req != NULL; req = next) { next = req->next; if (!auth_request_check_spid(auth, req)) req->aborted = TRUE; } } static void master_login_auth_send_request(struct master_login_auth *auth, struct master_login_auth_request *req) { string_t *str; if (!auth_request_check_spid(auth, req)) { master_login_auth_request_remove(auth, req); req->callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE, req->context); i_free(req); return; } str = t_str_new(128); str_printfa(str, "REQUEST\t%u\t%u\t%u\t", req->id, req->client_pid, req->auth_id); binary_to_hex_append(str, req->cookie, sizeof(req->cookie)); str_printfa(str, "\tsession_pid=%s", my_pid); if (auth->request_auth_token) str_append(str, "\trequest_auth_token"); str_append_c(str, '\n'); o_stream_nsend(auth->output, str_data(str), str_len(str)); } void master_login_auth_request(struct master_login_auth *auth, const struct master_auth_request *req, master_login_auth_request_callback_t *callback, void *context) { struct master_login_auth_request *login_req; unsigned int id; if (auth->fd == -1) { if (master_login_auth_connect(auth) < 0) { /* we couldn't connect to auth now, so we probably can't in future either. */ master_service_stop_new_connections(master_service); callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE, context); return; } o_stream_nsend_str(auth->output, t_strdup_printf("VERSION\t%u\t%u\n", AUTH_MASTER_PROTOCOL_MAJOR_VERSION, AUTH_MASTER_PROTOCOL_MINOR_VERSION)); } id = ++auth->id_counter; if (id == 0) id++; login_req = i_new(struct master_login_auth_request, 1); login_req->create_stamp = ioloop_time; login_req->id = id; login_req->auth_pid = req->auth_pid; login_req->client_pid = req->client_pid; login_req->auth_id = req->auth_id; memcpy(login_req->cookie, req->cookie, sizeof(login_req->cookie)); login_req->callback = callback; login_req->context = context; i_assert(hash_table_lookup(auth->requests, POINTER_CAST(id)) == NULL); hash_table_insert(auth->requests, POINTER_CAST(id), login_req); DLLIST2_APPEND(&auth->request_head, &auth->request_tail, login_req); if (auth->to == NULL) master_login_auth_set_timeout(auth); master_login_auth_send_request(auth, login_req); } unsigned int master_login_auth_request_count(struct master_login_auth *auth) { return hash_table_count(auth->requests); } dovecot-2.2.33.2/src/lib-master/master-service-settings.h0000644000175000017500000000650113165463624020127 00000000000000#ifndef MASTER_SERVICE_SETTINGS_H #define MASTER_SERVICE_SETTINGS_H #include "net.h" struct setting_parser_info; struct setting_parser_context; struct master_service; struct master_service_settings { const char *base_dir; const char *state_dir; const char *log_path; const char *info_log_path; const char *debug_log_path; const char *log_timestamp; const char *syslog_facility; const char *import_environment; uoff_t config_cache_size; bool version_ignore; bool shutdown_clients; bool verbose_proctitle; const char *haproxy_trusted_networks; unsigned int haproxy_timeout; }; struct master_service_settings_input { const struct setting_parser_info *const *roots; const char *config_path; bool preserve_environment; bool preserve_user; bool preserve_home; bool never_exec; bool use_sysexits; bool parse_full_config; const char *module; const char *service; const char *username; struct ip_addr local_ip, remote_ip; const char *local_name; }; struct master_service_settings_output { /* if service was not given for lookup, this contains names of services that have more specific settings */ const char *const *specific_services; /* some settings for this service (or if service was not given, all services) contain local/remote ip/host specific settings (but this lookup didn't necessarily return any of them). */ unsigned int service_uses_local:1; unsigned int service_uses_remote:1; /* returned settings contain settings specific to given local/remote ip/host */ unsigned int used_local:1; unsigned int used_remote:1; /* Config couldn't be read because we don't have enough permissions. The process probably should be restarted and the settings read before dropping privileges. */ unsigned int permission_denied:1; }; extern const struct setting_parser_info master_service_setting_parser_info; /* Try to open the config socket if it's going to be needed later by master_service_settings_read*() */ void master_service_config_socket_try_open(struct master_service *service); int master_service_settings_read(struct master_service *service, const struct master_service_settings_input *input, struct master_service_settings_output *output_r, const char **error_r); int master_service_settings_read_simple(struct master_service *service, const struct setting_parser_info **roots, const char **error_r) ATTR_NULL(2); /* destroy settings parser and clear service's set_pool, so that master_service_settings_read*() can be called without freeing memory used by existing settings structures. */ pool_t master_service_settings_detach(struct master_service *service); const struct master_service_settings * master_service_settings_get(struct master_service *service); void **master_service_settings_get_others(struct master_service *service); void **master_service_settings_parser_get_others(struct master_service *service, const struct setting_parser_context *set_parser); struct setting_parser_context * master_service_get_settings_parser(struct master_service *service); int master_service_set(struct master_service *service, const char *line); /* Returns TRUE if -o key=value parameter was used. Setting keys in overrides and parameter are unaliased before comparing. */ bool master_service_set_has_config_override(struct master_service *service, const char *key); #endif dovecot-2.2.33.2/src/lib-master/master-service-haproxy.c0000644000175000017500000003126213165463624017756 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "ioloop.h" #include "str-sanitize.h" #include "master-service-private.h" #include "master-service-settings.h" #define HAPROXY_V1_MAX_HEADER_SIZE (108) enum { HAPROXY_CMD_LOCAL = 0x00, HAPROXY_CMD_PROXY = 0x01 }; enum { HAPROXY_AF_UNSPEC = 0x00, HAPROXY_AF_INET = 0x01, HAPROXY_AF_INET6 = 0x02, HAPROXY_AF_UNIX = 0x03 }; enum { HAPROXY_SOCK_UNSPEC = 0x00, HAPROXY_SOCK_STREAM = 0x01, HAPROXY_SOCK_DGRAM = 0x02 }; static const char haproxy_v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; struct haproxy_header_v2 { uint8_t sig[12]; uint8_t ver_cmd; uint8_t fam; uint16_t len; }; struct haproxy_data_v2 { union { struct { /* for TCP/UDP over IPv4, len = 12 */ uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port; } ip4; struct { /* for TCP/UDP over IPv6, len = 36 */ uint8_t src_addr[16]; uint8_t dst_addr[16]; uint16_t src_port; uint16_t dst_port; } ip6; struct { /* for AF_UNIX sockets, len = 216 */ uint8_t src_addr[108]; uint8_t dst_addr[108]; } unx; } addr; }; struct master_service_haproxy_conn { struct master_service_connection conn; struct master_service_haproxy_conn *prev, *next; struct master_service *service; struct io *io; struct timeout *to; }; static void master_service_haproxy_conn_free(struct master_service_haproxy_conn *hpconn) { struct master_service *service = hpconn->service; DLLIST_REMOVE(&service->haproxy_conns, hpconn); if (hpconn->io != NULL) io_remove(&hpconn->io); if (hpconn->to != NULL) timeout_remove(&hpconn->to); i_free(hpconn); } static void master_service_haproxy_conn_failure(struct master_service_haproxy_conn *hpconn) { struct master_service *service = hpconn->service; struct master_service_connection conn = hpconn->conn; master_service_haproxy_conn_free(hpconn); master_service_client_connection_handled(service, &conn); } static void master_service_haproxy_conn_success(struct master_service_haproxy_conn *hpconn) { struct master_service *service = hpconn->service; struct master_service_connection conn = hpconn->conn; master_service_haproxy_conn_free(hpconn); master_service_client_connection_callback(service, &conn); } static void master_service_haproxy_timeout(struct master_service_haproxy_conn *hpconn) { i_error("haproxy: Client timed out (rip=%s)", net_ip2addr(&hpconn->conn.remote_ip)); master_service_haproxy_conn_failure(hpconn); } static int master_service_haproxy_read(struct master_service_haproxy_conn *hpconn) { static union { unsigned char v1_data[HAPROXY_V1_MAX_HEADER_SIZE]; struct { const struct haproxy_header_v2 hdr; const struct haproxy_data_v2 data; } v2; } buf; struct ip_addr *real_remote_ip = &hpconn->conn.remote_ip; int fd = hpconn->conn.fd; struct ip_addr local_ip, remote_ip; in_port_t local_port, remote_port; size_t size; ssize_t ret; /* the protocol specification explicitly states that the protocol header must be sent as one TCP frame, meaning that we will get it in full with the first recv() call. FIXME: still, it would be cleaner to allow reading it incrementally. */ do { ret = recv(fd, &buf, sizeof(buf), MSG_PEEK); } while (ret < 0 && errno == EINTR); if (ret < 0 && errno == EAGAIN) return 0; if (ret <= 0) { i_info("haproxy: Client disconnected (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } /* don't update true connection data until we succeed */ local_ip = hpconn->conn.local_ip; remote_ip = hpconn->conn.remote_ip; local_port = hpconn->conn.local_port; remote_port = hpconn->conn.remote_port; /* protocol version 2 */ if (ret >= (ssize_t)sizeof(buf.v2.hdr) && memcmp(buf.v2.hdr.sig, haproxy_v2sig, sizeof(buf.v2.hdr.sig)) == 0) { const struct haproxy_header_v2 *hdr = &buf.v2.hdr; const struct haproxy_data_v2 *data = &buf.v2.data; size_t hdr_len; if ((hdr->ver_cmd & 0xf0) != 0x20) { i_error("haproxy: Client disconnected: " "Unsupported protocol version (version=%02x, rip=%s)", (hdr->ver_cmd & 0xf0) >> 4, net_ip2addr(real_remote_ip)); return -1; } hdr_len = ntohs(hdr->len); size = sizeof(*hdr) + hdr_len; if (ret < (ssize_t)size) { i_error("haproxy(v2): Client disconnected: " "Protocol payload length does not match header " "(got=%"PRIuSIZE_T", expect=%"PRIuSIZE_T", rip=%s)", (size_t)ret, size, net_ip2addr(real_remote_ip)); return -1; } switch (hdr->ver_cmd & 0x0f) { case HAPROXY_CMD_LOCAL: /* keep local connection address for LOCAL */ /*i_debug("haproxy(v2): Local connection (rip=%s)", net_ip2addr(real_remote_ip));*/ break; case HAPROXY_CMD_PROXY: if ((hdr->fam & 0x0f) != HAPROXY_SOCK_STREAM) { /* UDP makes no sense currently */ i_error("haproxy(v2): Client disconnected: " "Not using TCP (type=%02x, rip=%s)", (hdr->fam & 0x0f), net_ip2addr(real_remote_ip)); return -1; } switch ((hdr->fam & 0xf0) >> 4) { case HAPROXY_AF_INET: /* IPv4 */ if (hdr_len < sizeof(data->addr.ip4)) { i_error("haproxy(v2): Client disconnected: " "IPv4 data is incomplete (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } local_ip.family = AF_INET; local_ip.u.ip4.s_addr = data->addr.ip4.dst_addr; local_port = ntohs(data->addr.ip4.dst_port); remote_ip.family = AF_INET; remote_ip.u.ip4.s_addr = data->addr.ip4.src_addr; remote_port = ntohs(data->addr.ip4.src_port); break; case HAPROXY_AF_INET6: /* IPv6 */ if (hdr_len < sizeof(data->addr.ip6)) { i_error("haproxy(v2): Client disconnected: " "IPv6 data is incomplete (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } local_ip.family = AF_INET6; memcpy(&local_ip.u.ip6.s6_addr, data->addr.ip6.dst_addr, 16); local_port = ntohs(data->addr.ip6.dst_port); remote_ip.family = AF_INET6; memcpy(&remote_ip.u.ip6.s6_addr, data->addr.ip6.src_addr, 16); remote_port = ntohs(data->addr.ip6.src_port); break; case HAPROXY_AF_UNSPEC: case HAPROXY_AF_UNIX: /* unsupported; ignored */ i_error("haproxy(v2): Unsupported address family " "(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4, net_ip2addr(real_remote_ip)); break; default: /* unsupported; error */ i_error("haproxy(v2): Client disconnected: " "Unknown address family " "(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4, net_ip2addr(real_remote_ip)); return -1; } break; default: i_error("haproxy(v2): Client disconnected: " "Invalid command (cmd=%02x, rip=%s)", (hdr->ver_cmd & 0x0f), net_ip2addr(real_remote_ip)); return -1; /* not a supported command */ } // FIXME: TLV vectors are ignored // (useful to see whether proxied client is using SSL) /* protocol version 1 (soon obsolete) */ } else if (ret >= 8 && memcmp(buf.v1_data, "PROXY", 5) == 0) { unsigned char *data = buf.v1_data, *end; const char *const *fields; unsigned int family = 0; /* find end of header line */ end = memchr(data, '\r', ret - 1); if (end == NULL || end[1] != '\n') return -1; *end = '\0'; size = end + 2 - data; /* magic */ fields = t_strsplit((char *)data, " "); i_assert(strcmp(*fields, "PROXY") == 0); fields++; /* protocol */ if (*fields == NULL) { i_error("haproxy(v1): Client disconnected: " "Field for proxied protocol is missing " "(rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (strcmp(*fields, "TCP4") == 0) { family = AF_INET; } else if (strcmp(*fields, "TCP6") == 0) { family = AF_INET6; } else if (strcmp(*fields, "UNKNOWN") == 0) { family = 0; } else { i_error("haproxy(v1): Client disconnected: " "Unknown proxied protocol " "(protocol=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } fields++; if (family != 0) { /* remote address */ if (*fields == NULL) { i_error("haproxy(v1): Client disconnected: " "Field for proxied remote address is missing " "(rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (net_addr2ip(*fields, &remote_ip) < 0 || remote_ip.family != family) { i_error("haproxy(v1): Client disconnected: " "Proxied remote address is invalid " "(address=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } fields++; /* local address */ if (*fields == NULL) { i_error("haproxy(v1): Client disconnected: " "Field for proxied local address is missing " "(rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (net_addr2ip(*fields, &local_ip) < 0 || local_ip.family != family) { i_error("haproxy(v1): Client disconnected: " "Proxied local address is invalid " "(address=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } fields++; /* remote port */ if (*fields == NULL) { i_error("haproxy(v1): Client disconnected: " "Field for proxied local port is missing " "(rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (net_str2port(*fields, &remote_port) < 0) { i_error("haproxy(v1): Client disconnected: " "Proxied remote port is invalid " "(port=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } fields++; /* local port */ if (*fields == NULL) { i_error("haproxy(v1): Client disconnected: " "Field for proxied local port is missing " "(rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (net_str2port(*fields, &local_port) < 0) { i_error("haproxy(v1): Client disconnected: " "Proxied local port is invalid " "(port=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } fields++; if (*fields != NULL) { i_error("haproxy(v1): Client disconnected: " "Header line has spurius extra field " "(field=`%s', rip=%s)", str_sanitize(*fields, 64), net_ip2addr(real_remote_ip)); return -1; } } /* invalid protocol */ } else { i_error("haproxy: Client disconnected: " "No valid proxy header found (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } /* remove proxy protocol header from socket buffer */ i_assert(size <= sizeof(buf)); do { ret = recv(fd, &buf, size, 0); } while (ret == -1 && errno == EINTR); if (ret <= 0) { i_info("haproxy: Client disconnected (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } if (ret != (ssize_t)size) { /* not supposed to happen */ i_error("haproxy: Client disconencted: " "Failed to read full header (rip=%s)", net_ip2addr(real_remote_ip)); return -1; } /* assign data from proxy */ hpconn->conn.local_ip = local_ip; hpconn->conn.remote_ip = remote_ip; hpconn->conn.local_port = local_port; hpconn->conn.remote_port = remote_port; return 1; } static void master_service_haproxy_input(struct master_service_haproxy_conn *hpconn) { int ret; if ((ret = master_service_haproxy_read(hpconn)) <= 0) { if (ret < 0) master_service_haproxy_conn_failure(hpconn); } else { master_service_haproxy_conn_success(hpconn); } } static bool master_service_haproxy_conn_is_trusted(struct master_service *service, struct master_service_connection *conn) { const char *const *net; struct ip_addr net_ip; unsigned int bits; if (service->set->haproxy_trusted_networks == NULL) return FALSE; net = t_strsplit_spaces(service->set->haproxy_trusted_networks, ", "); for (; *net != NULL; net++) { if (net_parse_range(*net, &net_ip, &bits) < 0) { i_error("haproxy_trusted_networks: " "Invalid network '%s'", *net); break; } if (net_is_in_network(&conn->real_remote_ip, &net_ip, bits)) return TRUE; } return FALSE; } void master_service_haproxy_new(struct master_service *service, struct master_service_connection *conn) { struct master_service_haproxy_conn *hpconn; if (!master_service_haproxy_conn_is_trusted(service, conn)) { i_warning("haproxy: Client not trusted (rip=%s)", net_ip2addr(&conn->real_remote_ip)); master_service_client_connection_handled(service, conn); return; } hpconn = i_new(struct master_service_haproxy_conn, 1); hpconn->conn = *conn; hpconn->service = service; DLLIST_PREPEND(&service->haproxy_conns, hpconn); hpconn->io = io_add(conn->fd, IO_READ, master_service_haproxy_input, hpconn); hpconn->to = timeout_add(service->set->haproxy_timeout*1000, master_service_haproxy_timeout, hpconn); } void master_service_haproxy_abort(struct master_service *service) { while (service->haproxy_conns != NULL) { int fd = service->haproxy_conns->conn.fd; master_service_haproxy_conn_free(service->haproxy_conns); i_close_fd(&fd); } } dovecot-2.2.33.2/src/lib-master/master-login.h0000644000175000017500000000213113165463624015734 00000000000000#ifndef MASTER_LOGIN_H #define MASTER_LOGIN_H #include "master-auth.h" #define MASTER_POSTLOGIN_TIMEOUT_DEFAULT 60 struct master_login_client { struct master_login_connection *conn; int fd; struct master_auth_request auth_req; char *session_id; unsigned char data[FLEXIBLE_ARRAY_MEMBER]; }; typedef void master_login_callback_t(const struct master_login_client *client, const char *username, const char *const *extra_fields); typedef void master_login_failure_callback_t(const struct master_login_client *client, const char *errormsg); struct master_login_settings { const char *auth_socket_path; const char *postlogin_socket_path; unsigned int postlogin_timeout_secs; master_login_callback_t *callback; master_login_failure_callback_t *failure_callback; unsigned int request_auth_token:1; }; struct master_login * master_login_init(struct master_service *service, const struct master_login_settings *set); void master_login_deinit(struct master_login **login); void master_login_add(struct master_login *login, int fd); void master_login_stop(struct master_login *login); #endif dovecot-2.2.33.2/src/lib-master/master-service-ssl.h0000644000175000017500000000074413123174404017060 00000000000000#ifndef MASTER_SERVICE_SSL_H #define MASTER_SERVICE_SSL_H struct ssl_iostream; int master_service_ssl_init(struct master_service *service, struct istream **input, struct ostream **output, struct ssl_iostream **ssl_iostream_r, const char **error_r); bool master_service_ssl_is_enabled(struct master_service *service); void master_service_ssl_ctx_init(struct master_service *service); void master_service_ssl_ctx_deinit(struct master_service *service); #endif dovecot-2.2.33.2/src/lib-master/master-interface.h0000644000175000017500000001002313123174404016550 00000000000000#ifndef MASTER_INTERFACE_H #define MASTER_INTERFACE_H /* We are attempting semi-compatibility with Postfix's master process here. Whether this is useful or not remains to be seen. */ /* Child processes should send status updates whenever they accept a new connection (decrease available_count) and when they close existing connection (increase available_count). */ struct master_status { pid_t pid; /* uid is used to check for old/invalid status messages */ unsigned int uid; /* number of new connections process is currently accepting */ unsigned int available_count; }; /* When connecting to log service, send this handshake first */ struct log_service_handshake { /* If magic is invalid, assume the data is already what we want to log */ #define MASTER_LOG_MAGIC 0x02ff03fe unsigned int log_magic; /* Add this prefix to each logged line */ #define MASTER_LOG_PREFIX_NAME "MASTER" unsigned int prefix_len; /* unsigned char prefix[]; */ }; enum master_login_state { MASTER_LOGIN_STATE_NONFULL = 0, MASTER_LOGIN_STATE_FULL }; /* getenv(MASTER_IS_PARENT_ENV) != NULL if process was started by Dovecot master */ #define MASTER_IS_PARENT_ENV "DOVECOT_CHILD_PROCESS" /* getenv(MASTER_UID_ENV) provides master_status.uid value */ #define MASTER_UID_ENV "GENERATION" /* getenv(MASTER_CLIENT_LIMIT_ENV) provides maximum master_status.available_count as specified in configuration file */ #define MASTER_CLIENT_LIMIT_ENV "CLIENT_LIMIT" /* getenv(MASTER_PROCESS_LIMIT_ENV) specifies how many processes of this type can be created before reaching the limit */ #define MASTER_PROCESS_LIMIT_ENV "PROCESS_LIMIT" /* getenv(MASTER_PROCESS_MIN_AVAIL_ENV) specifies how many processes of this type are created at startup and are kept running all the time */ #define MASTER_PROCESS_MIN_AVAIL_ENV "PROCESS_MIN_AVAIL" /* getenv(MASTER_SERVICE_COUNT_ENV) specifies how many client connections the process can finish handling before it should kill itself. */ #define MASTER_SERVICE_COUNT_ENV "SERVICE_COUNT" /* getenv(MASTER_SERVICE_IDLE_KILL_ENV) specifies service's idle_kill timeout in seconds. */ #define MASTER_SERVICE_IDLE_KILL_ENV "IDLE_KILL" /* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */ #define MASTER_CONFIG_FILE_ENV "CONFIG_FILE" /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number (unset if version_ignore=yes) */ #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION" /* getenv(MASTER_SSL_KEY_PASSWORD_ENV) returns manually typed SSL key password, if dovecot was started with -p parameter. */ #define MASTER_SSL_KEY_PASSWORD_ENV "SSL_KEY_PASSWORD" /* getenv(DOVECOT_PRESERVE_ENVS_ENV) returns a space separated list of environments that should be preserved. */ #define DOVECOT_PRESERVE_ENVS_ENV "DOVECOT_PRESERVE_ENVS" /* Write pipe to anvil. */ #define MASTER_ANVIL_FD 3 /* Anvil reads new log fds from this fd */ #define MASTER_ANVIL_LOG_FDPASS_FD 4 /* Master's "all processes full" notification fd for login processes */ #define MASTER_LOGIN_NOTIFY_FD 4 /* Shared pipe to master, used to send master_status reports */ #define MASTER_STATUS_FD 5 /* Pipe to master, used to detect when it dies. (MASTER_STATUS_FD would have been fine for this, except it's inefficient in Linux) */ #define MASTER_DEAD_FD 6 /* First file descriptor where process is expected to be listening. The file descriptor count is given in -s parameter, defaulting to 1. master_status.available_count reports how many accept()s we're still accepting. Once no children are listening, master will do it and create new child processes when needed. */ #define MASTER_LISTEN_FD_FIRST 7 /* Timeouts: base everything on how long we can wait for login clients. */ #define MASTER_LOGIN_TIMEOUT_SECS (3*60) /* auth server should abort auth requests before that happens */ #define MASTER_AUTH_SERVER_TIMEOUT_SECS (MASTER_LOGIN_TIMEOUT_SECS - 30) /* auth clients should abort auth lookups after server was supposed to have done that */ #define MASTER_AUTH_LOOKUP_TIMEOUT_SECS (MASTER_AUTH_SERVER_TIMEOUT_SECS + 5) #endif dovecot-2.2.33.2/src/lib-master/syslog-util.h0000644000175000017500000000041513123174404015616 00000000000000#ifndef SYSLOG_UTIL_H #define SYSLOG_UTIL_H struct syslog_facility_list { const char *name; int facility; }; extern struct syslog_facility_list syslog_facilities[]; /* Returns TRUE if found. */ bool syslog_facility_find(const char *name, int *facility_r); #endif dovecot-2.2.33.2/src/lib-master/master-login-auth.h0000644000175000017500000000160513123174404016665 00000000000000#ifndef MASTER_LOGIN_AUTH_H #define MASTER_LOGIN_AUTH_H struct master_auth_request; typedef void master_login_auth_request_callback_t(const char *const *auth_args, const char *errormsg, void *context); struct master_login_auth * master_login_auth_init(const char *auth_socket_path, bool request_auth_token); void master_login_auth_deinit(struct master_login_auth **auth); void master_login_auth_disconnect(struct master_login_auth *auth); /* req has been sent by login process. this function finishes authentication by performing verifying from auth that req is valid and doing the userdb lookup. */ void master_login_auth_request(struct master_login_auth *auth, const struct master_auth_request *req, master_login_auth_request_callback_t *callback, void *context); unsigned int master_login_auth_request_count(struct master_login_auth *auth); #endif dovecot-2.2.33.2/src/lib-master/master-instance.c0000644000175000017500000002134513165463624016433 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "abspath.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "file-dotlock.h" #include "str.h" #include "strescape.h" #include "master-instance.h" #include #include struct master_instance_list { pool_t pool; const char *path; ARRAY(struct master_instance) instances; unsigned int locked:1; unsigned int config_paths_changed:1; }; struct master_instance_list_iter { struct master_instance_list *list; unsigned int idx; }; static const struct dotlock_settings dotlock_set = { .timeout = 2, .stale_timeout = 60 }; struct master_instance_list *master_instance_list_init(const char *path) { struct master_instance_list *list; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"master instances", 256); list = p_new(pool, struct master_instance_list, 1); list->pool = pool; list->path = p_strdup(pool, path); p_array_init(&list->instances, pool, 8); return list; } void master_instance_list_deinit(struct master_instance_list **_list) { struct master_instance_list *list = *_list; *_list = NULL; pool_unref(&list->pool); } static void master_instance_update_config_path(struct master_instance_list *list, struct master_instance *inst) { const char *path, *config_path; /* update instance's config path if it has changed */ path = t_strconcat(inst->base_dir, "/"PACKAGE".conf", NULL); if (t_readlink(path, &config_path) == 0) { if (null_strcmp(inst->config_path, config_path) != 0) { inst->config_path = p_strdup(list->pool, config_path); list->config_paths_changed = TRUE; } } } static int master_instance_list_add_line(struct master_instance_list *list, const char *line) { struct master_instance *inst; const char *const *args; time_t last_used; /* [] */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < 3) return -1; if (str_to_time(args[0], &last_used) < 0) return -1; inst = array_append_space(&list->instances); inst->last_used = last_used; inst->name = p_strdup(list->pool, args[1]); inst->base_dir = p_strdup(list->pool, args[2]); inst->config_path = p_strdup_empty(list->pool, args[3]); master_instance_update_config_path(list, inst); return 0; } static int master_instance_list_refresh(struct master_instance_list *list) { struct istream *input; const char *line; int fd, ret = 0; array_clear(&list->instances); fd = open(list->path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", list->path); return -1; } input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) T_BEGIN { if (master_instance_list_add_line(list, line) < 0) i_error("Invalid line in %s: %s", list->path, line); } T_END; if (input->stream_errno != 0) { i_error("read(%s) failed: %s", line, i_stream_get_error(input)); ret = -1; } i_stream_destroy(&input); return ret; } static int master_instance_list_write(struct master_instance_list *list, int fd, const char *path) { struct ostream *output; const struct master_instance *inst; string_t *str = t_str_new(128); int ret = 0; output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(output); array_foreach(&list->instances, inst) { str_truncate(str, 0); str_printfa(str, "%ld\t", (long)inst->last_used); str_append_tabescaped(str, inst->name); str_append_c(str, '\t'); str_append_tabescaped(str, inst->base_dir); str_append_c(str, '\t'); if (inst->config_path != NULL) str_append_tabescaped(str, inst->config_path); str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", path, o_stream_get_error(output)); ret = -1; } o_stream_destroy(&output); return ret; } static int master_instance_write_init(struct master_instance_list *list, struct dotlock **dotlock_r) { int fd; i_assert(!list->locked); *dotlock_r = NULL; fd = file_dotlock_open_mode(&dotlock_set, list->path, 0, 0644, (uid_t)-1, (gid_t)-1, dotlock_r); if (fd == -1) { i_error("file_dotlock_open(%s) failed: %m", list->path); return -1; } if (master_instance_list_refresh(list) < 0) { file_dotlock_delete(dotlock_r); return -1; } list->locked = TRUE; return fd; } static int master_instance_write_finish(struct master_instance_list *list, int fd, struct dotlock **dotlock) { const char *lock_path = file_dotlock_get_lock_path(*dotlock); int ret; i_assert(list->locked); T_BEGIN { ret = master_instance_list_write(list, fd, lock_path); } T_END; list->locked = FALSE; if (ret < 0) { file_dotlock_delete(dotlock); return -1; } if (fdatasync(fd) < 0) { i_error("fdatasync(%s) failed: %m", lock_path); file_dotlock_delete(dotlock); return -1; } list->config_paths_changed = FALSE; return file_dotlock_replace(dotlock, 0); } static struct master_instance * master_instance_find(struct master_instance_list *list, const char *base_dir) { struct master_instance *inst; array_foreach_modifiable(&list->instances, inst) { if (strcmp(inst->base_dir, base_dir) == 0) return inst; } return NULL; } int master_instance_list_update(struct master_instance_list *list, const char *base_dir) { struct master_instance *inst; struct dotlock *dotlock; int fd; if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; inst = master_instance_find(list, base_dir); if (inst == NULL) { inst = array_append_space(&list->instances); inst->name = ""; inst->base_dir = p_strdup(list->pool, base_dir); } inst->last_used = time(NULL); master_instance_update_config_path(list, inst); return master_instance_write_finish(list, fd, &dotlock); } int master_instance_list_set_name(struct master_instance_list *list, const char *base_dir, const char *name) { const struct master_instance *orig_inst; struct master_instance *inst; struct dotlock *dotlock; int fd; i_assert(*name != '\0'); if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; orig_inst = master_instance_list_find_by_name(list, name); if (orig_inst != NULL && strcmp(orig_inst->base_dir, base_dir) != 0) { /* name already used */ file_dotlock_delete(&dotlock); list->locked = FALSE; return 0; } inst = master_instance_find(list, base_dir); if (inst == NULL) { inst = array_append_space(&list->instances); inst->base_dir = p_strdup(list->pool, base_dir); } inst->name = p_strdup(list->pool, name); inst->last_used = time(NULL); return master_instance_write_finish(list, fd, &dotlock) < 0 ? -1 : 1; } int master_instance_list_remove(struct master_instance_list *list, const char *base_dir) { struct dotlock *dotlock; const struct master_instance *instances; unsigned int i, count; int fd; if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; instances = array_get(&list->instances, &count); for (i = 0; i < count; i++) { if (strcmp(instances[i].base_dir, base_dir) == 0) { array_delete(&list->instances, i, 1); break; } } if (i == count) { file_dotlock_delete(&dotlock); list->locked = FALSE; return 0; } return master_instance_write_finish(list, fd, &dotlock) < 0 ? -1 : 1; } static int master_instance_list_refresh_and_update(struct master_instance_list *list) { struct dotlock *dotlock; int fd; if (master_instance_list_refresh(list) < 0) return -1; if (list->config_paths_changed && !list->locked) { /* write new config paths */ if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; if (master_instance_write_finish(list, fd, &dotlock) < 0) return -1; } return 0; } const struct master_instance * master_instance_list_find_by_name(struct master_instance_list *list, const char *name) { const struct master_instance *inst; i_assert(*name != '\0'); if (array_count(&list->instances) == 0) (void)master_instance_list_refresh_and_update(list); array_foreach(&list->instances, inst) { if (strcmp(inst->name, name) == 0) return inst; } return NULL; } struct master_instance_list_iter * master_instance_list_iterate_init(struct master_instance_list *list) { struct master_instance_list_iter *iter; iter = i_new(struct master_instance_list_iter, 1); iter->list = list; (void)master_instance_list_refresh_and_update(list); return iter; } const struct master_instance * master_instance_iterate_list_next(struct master_instance_list_iter *iter) { if (iter->idx == array_count(&iter->list->instances)) return NULL; return array_idx(&iter->list->instances, iter->idx++); } void master_instance_iterate_list_deinit(struct master_instance_list_iter **_iter) { struct master_instance_list_iter *iter = *_iter; *_iter = NULL; i_free(iter); } dovecot-2.2.33.2/src/lib-master/master-service-settings-cache.h0000644000175000017500000000122413123174404021152 00000000000000#ifndef MASTER_SERVICE_SETTINGS_CACHE_H #define MASTER_SERVICE_SETTINGS_CACHE_H struct master_service_settings_cache * master_service_settings_cache_init(struct master_service *service, const char *module, const char *service_name); void master_service_settings_cache_deinit(struct master_service_settings_cache **cache); int master_service_settings_cache_read(struct master_service_settings_cache *cache, const struct master_service_settings_input *input, const struct dynamic_settings_parser *dyn_parsers, const struct setting_parser_context **parser_r, const char **error_r) ATTR_NULL(3); #endif dovecot-2.2.33.2/src/lib-master/master-service-settings-cache.c0000644000175000017500000002264213165463624021167 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "llist.h" #include "settings-parser.h" #include "master-service-private.h" #include "master-service-settings.h" #include "master-service-settings-cache.h" /* we start with just a guess. it's updated later. */ #define CACHE_INITIAL_ENTRY_POOL_SIZE (1024*16) #define CACHE_ADD_ENTRY_POOL_SIZE 1024 struct settings_entry { struct settings_entry *prev, *next; pool_t pool; const char *local_name; struct ip_addr local_ip; struct setting_parser_context *parser; }; struct master_service_settings_cache { pool_t pool; struct master_service *service; const char *module; const char *service_name; size_t max_cache_size; /* global settings for this service (after they've been read) */ struct setting_parser_context *global_parser; /* cache for other settings (local_ip/local_name set) */ struct settings_entry *oldest, *newest; /* separate list for entries whose parser=global_parser */ struct settings_entry *oldest_global, *newest_global; /* local_name, local_ip => struct settings_entry */ HASH_TABLE(char *, struct settings_entry *) local_name_hash; HASH_TABLE(struct ip_addr *, struct settings_entry *) local_ip_hash; /* Initial size for new settings entry pools */ size_t approx_entry_pool_size; /* number of bytes malloced by cached settings entries (doesn't count memory used by hash table or global sets) */ size_t cache_malloc_size; unsigned int done_initial_lookup:1; unsigned int service_uses_local:1; unsigned int service_uses_remote:1; }; struct master_service_settings_cache * master_service_settings_cache_init(struct master_service *service, const char *module, const char *service_name) { struct master_service_settings_cache *cache; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"master service settings cache", 1024*12); cache = p_new(pool, struct master_service_settings_cache, 1); cache->pool = pool; cache->service = service; cache->module = p_strdup(pool, module); cache->service_name = p_strdup(pool, service_name); cache->max_cache_size = (size_t)-1; return cache; } void master_service_settings_cache_deinit(struct master_service_settings_cache **_cache) { struct master_service_settings_cache *cache = *_cache; struct settings_entry *entry, *next; /* parsers need to be deinitialized, because they reference the pool */ for (entry = cache->oldest_global; entry != NULL; entry = next) { next = entry->next; i_assert(entry->parser == cache->global_parser); pool_unref(&entry->pool); } for (entry = cache->oldest; entry != NULL; entry = next) { next = entry->next; i_assert(entry->parser != cache->global_parser); settings_parser_deinit(&entry->parser); pool_unref(&entry->pool); } if (hash_table_is_created(cache->local_name_hash)) hash_table_destroy(&cache->local_name_hash); if (hash_table_is_created(cache->local_ip_hash)) hash_table_destroy(&cache->local_ip_hash); if (cache->global_parser != NULL) settings_parser_deinit(&cache->global_parser); pool_unref(&cache->pool); } static bool cache_can_return_global(struct master_service_settings_cache *cache, const struct master_service_settings_input *input) { if (cache->service_uses_local) { if (input->local_name != NULL || input->local_ip.family != 0) return FALSE; } if (cache->service_uses_remote) { if (input->remote_ip.family != 0) return FALSE; } return TRUE; } static bool cache_find(struct master_service_settings_cache *cache, const struct master_service_settings_input *input, const struct setting_parser_context **parser_r) { struct settings_entry *entry = NULL; if (!cache->done_initial_lookup) return FALSE; if (cache_can_return_global(cache, input)) { if (cache->global_parser != NULL) { *parser_r = cache->global_parser; return TRUE; } return FALSE; } if (cache->service_uses_remote) return FALSE; /* see if we have it already in cache. if local_name is specified, don't even try to use local_ip (even though we have it), because there may be different settings specifically for local_name */ if (input->local_name != NULL) { if (hash_table_is_created(cache->local_name_hash)) { entry = hash_table_lookup(cache->local_name_hash, input->local_name); } } else if (hash_table_is_created(cache->local_ip_hash) && input->local_ip.family != 0) { entry = hash_table_lookup(cache->local_ip_hash, &input->local_ip); } if (entry != NULL) { if (entry->parser != cache->global_parser) { DLLIST2_REMOVE(&cache->oldest, &cache->newest, entry); DLLIST2_APPEND(&cache->oldest, &cache->newest, entry); } *parser_r = entry->parser; return TRUE; } return FALSE; } static void setting_entry_detach(struct master_service_settings_cache *cache, struct settings_entry *entry) { DLLIST2_REMOVE(&cache->oldest, &cache->newest, entry); cache->cache_malloc_size -= pool_alloconly_get_total_alloc_size(entry->pool); if (entry->local_name != NULL) hash_table_remove(cache->local_name_hash, entry->local_name); else if (entry->local_ip.family != 0) hash_table_remove(cache->local_ip_hash, &entry->local_ip); settings_parser_deinit(&entry->parser); } static struct setting_parser_context * cache_add(struct master_service_settings_cache *cache, const struct master_service_settings_input *input, const struct master_service_settings_output *output, struct setting_parser_context *parser) { struct settings_entry *entry; pool_t pool; size_t pool_size; char *entry_local_name; if (!output->used_local && !output->used_remote) { /* these are same as global settings */ if (cache->global_parser == NULL) { cache->global_parser = settings_parser_dup(parser, cache->pool); } } if (cache->service_uses_remote) { /* for now we don't try to handle caching remote IPs */ return parser; } if (input->local_name == NULL && input->local_ip.family == 0) return parser; if (!output->used_local) { /* use global settings, but add local_ip/host to hash tables so we'll find them */ pool = pool_alloconly_create("settings global entry", 256); } else if (cache->cache_malloc_size >= cache->max_cache_size) { /* free the oldest and reuse its pool */ pool = cache->oldest->pool; setting_entry_detach(cache, cache->oldest); p_clear(pool); /* note: frees also entry */ } else { pool_size = cache->approx_entry_pool_size != 0 ? cache->approx_entry_pool_size : CACHE_INITIAL_ENTRY_POOL_SIZE; pool = pool_alloconly_create("settings entry", pool_size); } entry = p_new(pool, struct settings_entry, 1); entry->pool = pool; entry_local_name = p_strdup(pool, input->local_name); entry->local_name = entry_local_name; entry->local_ip = input->local_ip; if (!output->used_local) { entry->parser = cache->global_parser; DLLIST2_APPEND(&cache->oldest_global, &cache->newest_global, entry); } else { entry->parser = settings_parser_dup(parser, entry->pool); DLLIST2_APPEND(&cache->oldest, &cache->newest, entry); pool_size = pool_alloconly_get_total_used_size(pool); if (pool_size > cache->approx_entry_pool_size) { cache->approx_entry_pool_size = pool_size + CACHE_ADD_ENTRY_POOL_SIZE; } } cache->cache_malloc_size += pool_alloconly_get_total_alloc_size(pool); if (input->local_name != NULL) { if (!hash_table_is_created(cache->local_name_hash)) { hash_table_create(&cache->local_name_hash, cache->pool, 0, str_hash, strcmp); } i_assert(hash_table_lookup(cache->local_name_hash, entry_local_name) == NULL); hash_table_insert(cache->local_name_hash, entry_local_name, entry); } else if (input->local_ip.family != 0) { if (!hash_table_is_created(cache->local_ip_hash)) { hash_table_create(&cache->local_ip_hash, cache->pool, 0, net_ip_hash, net_ip_cmp); } i_assert(hash_table_lookup(cache->local_ip_hash, &entry->local_ip) == NULL); hash_table_insert(cache->local_ip_hash, &entry->local_ip, entry); } return entry->parser; } int master_service_settings_cache_read(struct master_service_settings_cache *cache, const struct master_service_settings_input *input, const struct dynamic_settings_parser *dyn_parsers, const struct setting_parser_context **parser_r, const char **error_r) { struct master_service_settings_output output; struct master_service_settings_input new_input; const struct master_service_settings *set; i_assert(null_strcmp(input->module, cache->module) == 0); i_assert(null_strcmp(input->service, cache->service_name) == 0); if (cache_find(cache, input, parser_r)) return 0; new_input = *input; if (dyn_parsers != NULL) { settings_parser_dyn_update(cache->pool, &new_input.roots, dyn_parsers); } if (master_service_settings_read(cache->service, &new_input, &output, error_r) < 0) return -1; if (!cache->done_initial_lookup) { cache->done_initial_lookup = TRUE; cache->service_uses_local = output.service_uses_local; cache->service_uses_remote = output.service_uses_remote; set = master_service_settings_get(cache->service); cache->max_cache_size = set->config_cache_size; } if (output.used_local && !cache->service_uses_local) { *error_r = "BUG: config unexpectedly returned local settings"; return -1; } if (output.used_remote && !cache->service_uses_remote) { *error_r = "BUG: config unexpectedly returned remote settings"; return -1; } *parser_r = cache_add(cache, &new_input, &output, cache->service->set_parser); return 0; } dovecot-2.2.33.2/src/lib-master/Makefile.am0000644000175000017500000000341413165463624015223 00000000000000pkgsysconfdir = $(sysconfdir)/dovecot noinst_LTLIBRARIES = libmaster.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DSYSCONFDIR=\""$(pkgsysconfdir)"\" \ -DBINDIR=\""$(bindir)"\" libmaster_la_SOURCES = \ anvil-client.c \ ipc-client.c \ ipc-server.c \ master-auth.c \ master-instance.c \ master-login.c \ master-login-auth.c \ master-service.c \ master-service-haproxy.c \ master-service-settings.c \ master-service-settings-cache.c \ master-service-ssl.c \ master-service-ssl-settings.c \ mountpoint-list.c \ syslog-util.c headers = \ anvil-client.h \ ipc-client.h \ ipc-server.h \ master-auth.h \ master-instance.h \ master-interface.h \ master-login.h \ master-login-auth.h \ master-service.h \ master-service-private.h \ master-service-settings.h \ master-service-settings-cache.h \ master-service-ssl.h \ master-service-ssl-settings.h \ service-settings.h \ mountpoint-list.h \ syslog-util.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-master-service-settings-cache noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_master_service_settings_cache_SOURCES = test-master-service-settings-cache.c test_master_service_settings_cache_LDADD = master-service-settings-cache.lo ../lib-settings/libsettings.la $(test_libs) test_master_service_settings_cache_DEPENDENCIES = $(test_deps) ../lib-settings/libsettings.la check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/ipc/0002755000175000017500000000000013172375612011760 500000000000000dovecot-2.2.33.2/src/ipc/Makefile.in0000644000175000017500000005377413172375572013770 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = ipc$(EXEEXT) subdir = src/ipc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_ipc_OBJECTS = client.$(OBJEXT) main.$(OBJEXT) \ ipc-connection.$(OBJEXT) ipc-group.$(OBJEXT) \ ipc-settings.$(OBJEXT) ipc_OBJECTS = $(am_ipc_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(ipc_SOURCES) DIST_SOURCES = $(ipc_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master ipc_LDADD = $(LIBDOVECOT) ipc_DEPENDENCIES = $(LIBDOVECOT_DEPS) ipc_SOURCES = \ client.c \ main.c \ ipc-connection.c \ ipc-group.c \ ipc-settings.c noinst_HEADERS = \ client.h \ ipc-connection.h \ ipc-group.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/ipc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/ipc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list ipc$(EXEEXT): $(ipc_OBJECTS) $(ipc_DEPENDENCIES) $(EXTRA_ipc_DEPENDENCIES) @rm -f ipc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ipc_OBJECTS) $(ipc_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc-group.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/ipc/ipc-connection.h0000644000175000017500000000166613165463624014773 00000000000000#ifndef IPC_CONNECTION_H #define IPC_CONNECTION_H #include "ipc-group.h" struct ipc_connection_cmd { unsigned int tag; struct ipc_connection *conn; ipc_cmd_callback_t *callback; void *context; }; struct ipc_connection { struct ipc_group *group; /* prev/next within group */ struct ipc_connection *prev, *next; unsigned int id; pid_t pid; int fd; struct io *io; struct istream *input; struct ostream *output; unsigned int cmd_tag_counter; /* running commands */ ARRAY(struct ipc_connection_cmd *) cmds; unsigned int version_received:1; unsigned int handshake_received:1; }; struct ipc_connection *ipc_connection_create(int listen_fd, int fd); void ipc_connection_destroy(struct ipc_connection **conn); struct ipc_connection * ipc_connection_lookup_id(struct ipc_group *group, unsigned int id); void ipc_connection_cmd(struct ipc_connection *conn, const char *cmd, ipc_cmd_callback_t *callback, void *context); #endif dovecot-2.2.33.2/src/ipc/ipc-settings.c0000644000175000017500000000227413123174404014450 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings ipc_unix_listeners_array[] = { { "ipc", 0600, "", "" }, { "login/ipc-proxy", 0600, "$default_login_user", "" } }; static struct file_listener_settings *ipc_unix_listeners[] = { &ipc_unix_listeners_array[0], &ipc_unix_listeners_array[1] }; static buffer_t ipc_unix_listeners_buf = { ipc_unix_listeners, sizeof(ipc_unix_listeners), { NULL, } }; /* */ struct service_settings ipc_service_settings = { .name = "ipc", .protocol = "", .type = "", .executable = "ipc", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &ipc_unix_listeners_buf, sizeof(ipc_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; dovecot-2.2.33.2/src/ipc/ipc-group.c0000644000175000017500000000664013123174404013745 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ipc-connection.h" #include "ipc-group.h" struct ipc_group_cmd { ipc_cmd_callback_t *callback; void *context; int refcount; char *first_error; }; static ARRAY(struct ipc_group *) ipc_groups; struct ipc_group *ipc_group_alloc(int listen_fd) { struct ipc_group *group; i_assert(ipc_group_lookup_listen_fd(listen_fd) == NULL); group = i_new(struct ipc_group, 1); group->listen_fd = listen_fd; array_append(&ipc_groups, &group, 1); return group; } void ipc_group_free(struct ipc_group **_group) { struct ipc_group *const *groups, *group = *_group; unsigned int i, count; i_assert(group->connections == NULL); *_group = NULL; groups = array_get(&ipc_groups, &count); for (i = 0; i < count; i++) { if (groups[i] == group) { array_delete(&ipc_groups, i, 1); break; } } i_free(group->name); i_free(group); } struct ipc_group *ipc_group_lookup_listen_fd(int listen_fd) { struct ipc_group *const *groupp; array_foreach(&ipc_groups, groupp) { if ((*groupp)->listen_fd == listen_fd) return *groupp; } return NULL; } struct ipc_group *ipc_group_lookup_name(const char *name) { struct ipc_group *const *groupp; array_foreach(&ipc_groups, groupp) { if ((*groupp)->name != NULL && strcmp((*groupp)->name, name) == 0) return *groupp; } return NULL; } int ipc_group_update_name(struct ipc_group *group, const char *name) { if (group->name == NULL) group->name = i_strdup(name); else if (strcmp(group->name, name) != 0) return -1; return 0; } static void ipc_group_cmd_callback(enum ipc_cmd_status status, const char *line, void *context) { struct ipc_group_cmd *group_cmd = context; i_assert(group_cmd->refcount > 0); switch (status) { case IPC_CMD_STATUS_REPLY: group_cmd->callback(IPC_CMD_STATUS_REPLY, line, group_cmd->context); break; case IPC_CMD_STATUS_ERROR: if (group_cmd->first_error == NULL) group_cmd->first_error = i_strdup(line); /* fall through */ case IPC_CMD_STATUS_OK: if (--group_cmd->refcount > 0) break; if (group_cmd->first_error == NULL) { group_cmd->callback(IPC_CMD_STATUS_OK, line, group_cmd->context); } else { group_cmd->callback(IPC_CMD_STATUS_ERROR, group_cmd->first_error, group_cmd->context); i_free(group_cmd->first_error); } i_free(group_cmd); break; } } bool ipc_group_cmd(struct ipc_group *group, const char *cmd, ipc_cmd_callback_t *callback, void *context) { struct ipc_connection *conn, *next; struct ipc_group_cmd *group_cmd; if (group->connections == NULL) { callback(IPC_CMD_STATUS_OK, NULL, context); return FALSE; } group_cmd = i_new(struct ipc_group_cmd, 1); group_cmd->callback = callback; group_cmd->context = context; for (conn = group->connections; conn != NULL; conn = next) { next = conn->next; group_cmd->refcount++; ipc_connection_cmd(conn, cmd, ipc_group_cmd_callback, group_cmd); } return TRUE; } void ipc_groups_init(void) { i_array_init(&ipc_groups, 16); } void ipc_groups_deinit(void) { struct ipc_group *const *groupp, *group; while (array_count(&ipc_groups) > 0) { groupp = array_idx(&ipc_groups, 0); group = *groupp; while ((*groupp)->connections != NULL) { struct ipc_connection *conn = (*groupp)->connections; ipc_connection_destroy(&conn); } ipc_group_free(&group); } array_free(&ipc_groups); } dovecot-2.2.33.2/src/ipc/main.c0000644000175000017500000000300013123174404012747 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "ipc-group.h" #include "ipc-connection.h" #include "client.h" static bool ipc_socket_is_client(const char *name) { size_t len; if (strcmp(name, "ipc") == 0) return TRUE; len = strlen(name); if (len > 7 && strcmp(name + len - 7, "-client") == 0) return TRUE; return FALSE; } static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); if (ipc_socket_is_client(conn->name)) (void)client_create(conn->fd); else (void)ipc_connection_create(conn->listen_fd, conn->fd); } int main(int argc, char *argv[]) { const enum master_service_flags service_flags = MASTER_SERVICE_FLAG_UPDATE_PROCTITLE; const char *error; master_service = master_service_init("ipc", service_flags, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, NULL, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "ipc: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); ipc_groups_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); clients_destroy_all(); ipc_groups_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/ipc/ipc-connection.c0000644000175000017500000001335113165463624014760 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "strescape.h" #include "master-service.h" #include "ipc-group.h" #include "ipc-connection.h" #include #define IPC_SERVER_PROTOCOL_MAJOR_VERSION 1 #define IPC_SERVER_PROTOCOL_MINOR_VERSION 0 #define IPC_SERVER_HANDSHAKE "VERSION\tipc-proxy\t1\t0\n" static unsigned int connection_id_counter; static void ipc_connection_cmd_free(struct ipc_connection_cmd **cmd); static void ipc_connection_cmd_free(struct ipc_connection_cmd **_cmd) { struct ipc_connection_cmd *cmd = *_cmd; struct ipc_connection_cmd **cmds; unsigned int i, count; *_cmd = NULL; cmds = array_get_modifiable(&cmd->conn->cmds, &count); for (i = 0; i < count; i++) { if (cmds[i] == cmd) { array_delete(&cmd->conn->cmds, i, 1); break; } } if (cmd->callback != NULL) { cmd->callback(IPC_CMD_STATUS_ERROR, "Connection to server died", cmd->context); } i_free(cmd); } static struct ipc_connection_cmd * ipc_connection_cmd_find(struct ipc_connection *conn, unsigned int tag) { struct ipc_connection_cmd *const *cmdp; array_foreach(&conn->cmds, cmdp) { if ((*cmdp)->tag == tag) return *cmdp; } return NULL; } static int ipc_connection_input_line(struct ipc_connection *conn, char *line) { struct ipc_connection_cmd *cmd; unsigned int tag; enum ipc_cmd_status status; char *data; /* [:+-] */ data = strchr(line, '\t'); if (data == NULL) return -1; *data++ = '\0'; if (str_to_uint(line, &tag) < 0) return -1; switch (data[0]) { case ':': status = IPC_CMD_STATUS_REPLY; break; case '+': status = IPC_CMD_STATUS_OK; break; case '-': status = IPC_CMD_STATUS_ERROR; break; default: return -1; } data++; cmd = ipc_connection_cmd_find(conn, tag); if (cmd == NULL) { i_error("IPC server: Input for unexpected command tag %u", tag); return 0; } cmd->callback(status, data, cmd->context); if (status != IPC_CMD_STATUS_REPLY) { cmd->callback = NULL; ipc_connection_cmd_free(&cmd); } return 0; } static void ipc_connection_input(struct ipc_connection *conn) { const char *const *args; char *line; int ret; if (i_stream_read(conn->input) < 0) { ipc_connection_destroy(&conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, "ipc-server", IPC_SERVER_PROTOCOL_MAJOR_VERSION)) { i_error("IPC server not compatible with this server " "(mixed old and new binaries?)"); ipc_connection_destroy(&conn); return; } conn->version_received = TRUE; } if (!conn->handshake_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; args = t_strsplit_tabescaped(line); if (str_array_length(args) < 3 || strcmp(args[0], "HANDSHAKE") != 0) { i_error("IPC server sent invalid handshake"); ipc_connection_destroy(&conn); return; } if (ipc_group_update_name(conn->group, args[1]) < 0) { i_error("IPC server named itself unexpectedly: %s " "(existing ones were %s)", args[1], conn->group->name); ipc_connection_destroy(&conn); return; } if (str_to_pid(args[2], &conn->pid) < 0) { i_error("IPC server gave broken PID: %s", args[2]); ipc_connection_destroy(&conn); return; } conn->handshake_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = ipc_connection_input_line(conn, line); } T_END; if (ret < 0) { i_error("Invalid input from IPC server '%s': %s", conn->group->name, line); ipc_connection_destroy(&conn); break; } } } struct ipc_connection *ipc_connection_create(int listen_fd, int fd) { struct ipc_connection *conn; conn = i_new(struct ipc_connection, 1); conn->group = ipc_group_lookup_listen_fd(listen_fd); if (conn->group == NULL) conn->group = ipc_group_alloc(listen_fd); conn->id = ++connection_id_counter; if (conn->id == 0) conn->id = ++connection_id_counter; conn->fd = fd; conn->io = io_add(fd, IO_READ, ipc_connection_input, conn); conn->input = i_stream_create_fd(fd, (size_t)-1, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); i_array_init(&conn->cmds, 8); o_stream_nsend_str(conn->output, IPC_SERVER_HANDSHAKE); DLLIST_PREPEND(&conn->group->connections, conn); return conn; } void ipc_connection_destroy(struct ipc_connection **_conn) { struct ipc_connection *conn = *_conn; struct ipc_connection_cmd *const *cmdp, *cmd; *_conn = NULL; DLLIST_REMOVE(&conn->group->connections, conn); while (array_count(&conn->cmds) > 0) { cmdp = array_idx(&conn->cmds, 0); cmd = *cmdp; ipc_connection_cmd_free(&cmd); } array_free(&conn->cmds); io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(ipc connection) failed: %m"); i_free(conn); master_service_client_connection_destroyed(master_service); } struct ipc_connection * ipc_connection_lookup_id(struct ipc_group *group, unsigned int id) { struct ipc_connection *conn; for (conn = group->connections; conn != NULL; conn = conn->next) { if (conn->id == id) return conn; } return NULL; } void ipc_connection_cmd(struct ipc_connection *conn, const char *cmd, ipc_cmd_callback_t *callback, void *context) { struct ipc_connection_cmd *ipc_cmd; ipc_cmd = i_new(struct ipc_connection_cmd, 1); ipc_cmd->tag = ++conn->cmd_tag_counter; ipc_cmd->conn = conn; ipc_cmd->callback = callback; ipc_cmd->context = context; array_append(&conn->cmds, &ipc_cmd, 1); T_BEGIN { o_stream_nsend_str(conn->output, t_strdup_printf("%u\t%s\n", ipc_cmd->tag, cmd)); } T_END; } dovecot-2.2.33.2/src/ipc/client.c0000644000175000017500000000706513165463624013333 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "master-service.h" #include "ipc-group.h" #include "ipc-connection.h" #include "client.h" #include struct client { struct client *prev, *next; int fd; struct io *io; struct istream *input; struct ostream *output; }; static struct client *clients; static void client_input(struct client *client); static void client_cmd_input(enum ipc_cmd_status status, const char *line, void *context) { struct client *client = context; char chr = '\0'; switch (status) { case IPC_CMD_STATUS_REPLY: chr = ':'; break; case IPC_CMD_STATUS_OK: chr = '+'; break; case IPC_CMD_STATUS_ERROR: chr = '-'; break; } T_BEGIN { o_stream_nsend_str(client->output, t_strdup_printf("%c%s\n", chr, line)); } T_END; if (status != IPC_CMD_STATUS_REPLY && client->io == NULL) { client->io = io_add(client->fd, IO_READ, client_input, client); client_input(client); } } static void client_input(struct client *client) { struct ipc_group *group; struct ipc_connection *conn; char *line, *id, *data; unsigned int id_num; bool ret; while ((line = i_stream_read_next_line(client->input)) != NULL) { /* *| */ id = strchr(line, '\t'); if (id == NULL) data = NULL; else { *id++ = '\0'; data = strchr(id, '\t'); } if (data == NULL || data[1] == '\0') { o_stream_nsend_str(client->output, "-Invalid input\n"); continue; } *data++ = '\0'; group = ipc_group_lookup_name(line); ret = FALSE; if (strcmp(id, "*") == 0) { /* send to everyone */ if (group == NULL) { client_cmd_input(IPC_CMD_STATUS_OK, "", client); } else { ret = ipc_group_cmd(group, data, client_cmd_input, client); } } else if (str_to_uint(id, &id_num) < 0) { o_stream_nsend_str(client->output, t_strdup_printf("-Invalid IPC connection id: %s\n", id)); continue; } else if (group == NULL) { o_stream_nsend_str(client->output, t_strdup_printf("-Unknown IPC group: %s\n", line)); } else if ((conn = ipc_connection_lookup_id(group, id_num)) == NULL) { o_stream_nsend_str(client->output, t_strdup_printf("-Unknown IPC connection id: %u\n", id_num)); continue; } else { ipc_connection_cmd(conn, data, client_cmd_input, client); ret = TRUE; } if (ret) { /* we'll handle commands one at a time. stop reading input until this command is finished. */ io_remove(&client->io); break; } } if (client->input->eof || client->input->stream_errno != 0) client_destroy(&client); } struct client *client_create(int fd) { struct client *client; client = i_new(struct client, 1); client->fd = fd; client->io = io_add(fd, IO_READ, client_input, client); client->input = i_stream_create_fd(fd, (size_t)-1, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); DLLIST_PREPEND(&clients, client); return client; } void client_destroy(struct client **_client) { struct client *client = *_client; *_client = NULL; DLLIST_REMOVE(&clients, client); if (client->io != NULL) io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); if (close(client->fd) < 0) i_error("close(client) failed: %m"); i_free(client); master_service_client_connection_destroyed(master_service); } void clients_destroy_all(void) { while (clients != NULL) { struct client *client = clients; client_destroy(&client); } } dovecot-2.2.33.2/src/ipc/client.h0000644000175000017500000000023713123174404013317 00000000000000#ifndef CLIENT_H #define CLIENT_H struct client *client_create(int fd); void client_destroy(struct client **client); void clients_destroy_all(void); #endif dovecot-2.2.33.2/src/ipc/ipc-group.h0000644000175000017500000000245213123174404013747 00000000000000#ifndef IPC_GROUP_H #define IPC_GROUP_H enum ipc_cmd_status { /* Command received a reply line */ IPC_CMD_STATUS_REPLY, /* Command finished successfully */ IPC_CMD_STATUS_OK, /* Command finished with errors */ IPC_CMD_STATUS_ERROR }; struct ipc_group { int listen_fd; char *name; /* connections list also acts as a refcount */ struct ipc_connection *connections; }; /* line is non-NULL only with IPC_CMD_STATUS_REPLY. Each line begins with the connection ID and TAB. */ typedef void ipc_cmd_callback_t(enum ipc_cmd_status status, const char *line, void *context); struct ipc_group *ipc_group_alloc(int listen_fd); void ipc_group_free(struct ipc_group **group); struct ipc_group *ipc_group_lookup_listen_fd(int listen_fd); struct ipc_group *ipc_group_lookup_name(const char *name); /* Returns 0 if name is ok, -1 if name doesn't match the existing one. */ int ipc_group_update_name(struct ipc_group *group, const char *name); /* Send a command to all connections in a group. All connections are expected to answer something. If there are no connections, callback() is called immediately and FALSE is returned. */ bool ipc_group_cmd(struct ipc_group *group, const char *cmd, ipc_cmd_callback_t *callback, void *context); void ipc_groups_init(void); void ipc_groups_deinit(void); #endif dovecot-2.2.33.2/src/ipc/Makefile.am0000644000175000017500000000061613165463624013740 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = ipc AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master ipc_LDADD = $(LIBDOVECOT) ipc_DEPENDENCIES = $(LIBDOVECOT_DEPS) ipc_SOURCES = \ client.c \ main.c \ ipc-connection.c \ ipc-group.c \ ipc-settings.c noinst_HEADERS = \ client.h \ ipc-connection.h \ ipc-group.h dovecot-2.2.33.2/src/lib-fts/0002755000175000017500000000000013172375611012544 500000000000000dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-address.c0000644000175000017500000002433613123174404017056 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "buffer.h" #include "rfc822-parser.h" #include "fts-tokenizer-private.h" #include "fts-tokenizer-common.h" #define IS_DTEXT(c) \ (rfc822_atext_chars[(int)(unsigned char)(c)] == 2) #define FTS_DEFAULT_ADDRESS_MAX_LENGTH 254 enum email_address_parser_state { EMAIL_ADDRESS_PARSER_STATE_NONE = 0, EMAIL_ADDRESS_PARSER_STATE_LOCALPART, EMAIL_ADDRESS_PARSER_STATE_DOMAIN, EMAIL_ADDRESS_PARSER_STATE_COMPLETE }; struct email_address_fts_tokenizer { struct fts_tokenizer tokenizer; enum email_address_parser_state state; string_t *last_word; string_t *parent_data; /* Copy of input data between tokens. */ unsigned int max_length; bool search; }; static int fts_tokenizer_email_address_create(const char *const *settings, struct fts_tokenizer **tokenizer_r, const char **error_r) { struct email_address_fts_tokenizer *tok; bool search = FALSE; unsigned int max_length = FTS_DEFAULT_ADDRESS_MAX_LENGTH; unsigned int i; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "search") == 0) { search = TRUE; } else if (strcmp(key, "maxlen") == 0) { if (str_to_uint(value, &max_length) < 0 || max_length == 0) { *error_r = t_strdup_printf("Invalid maxlen setting: %s", value); return -1; } } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } tok = i_new(struct email_address_fts_tokenizer, 1); tok->tokenizer = *fts_tokenizer_email_address; tok->last_word = str_new(default_pool, 128); tok->parent_data = str_new(default_pool, 128); tok->max_length = max_length; tok->search = search; *tokenizer_r = &tok->tokenizer; return 0; } static void fts_tokenizer_email_address_destroy(struct fts_tokenizer *_tok) { struct email_address_fts_tokenizer *tok = (struct email_address_fts_tokenizer *)_tok; str_free(&tok->last_word); str_free(&tok->parent_data); i_free(tok); } static bool fts_tokenizer_address_current_token(struct email_address_fts_tokenizer *tok, const char **token_r) { const unsigned char *data = tok->last_word->data; size_t len = tok->last_word->used; tok->tokenizer.skip_parents = TRUE; tok->state = EMAIL_ADDRESS_PARSER_STATE_NONE; if (str_len(tok->last_word) > tok->max_length) { str_truncate(tok->last_word, tok->max_length); /* As future proofing, delete partial utf8. IS_DTEXT() does not actually allow utf8 addresses yet though. */ len = tok->last_word->used; fts_tokenizer_delete_trailing_partial_char(data, &len); i_assert(len <= tok->max_length); } if (len > 0) fts_tokenizer_delete_trailing_invalid_char(data, &len); *token_r = len == 0 ? "" : t_strndup(data, len); return len > 0; } static bool fts_tokenizer_address_parent_data(struct email_address_fts_tokenizer *tok, const char **token_r) { if (tok->tokenizer.parent == NULL || str_len(tok->parent_data) == 0) return FALSE; if (tok->search && tok->state >= EMAIL_ADDRESS_PARSER_STATE_DOMAIN) { /* we're searching and we want to find only the full user@domain (not "user" and "domain"). we'll do this by not feeding the last user@domain to parent tokenizer. */ size_t parent_prefix_len = str_len(tok->parent_data) - str_len(tok->last_word); i_assert(str_len(tok->parent_data) >= str_len(tok->last_word) && strcmp(str_c(tok->parent_data) + parent_prefix_len, str_c(tok->last_word)) == 0); str_truncate(tok->parent_data, parent_prefix_len); if (str_len(tok->parent_data) == 0) return FALSE; } *token_r = t_strdup(str_c(tok->parent_data)); str_truncate(tok->parent_data, 0); return TRUE; } /* Used to rewind past characters that can not be the start of a new localpart. Returns size that can be skipped. */ static size_t skip_nonlocal_part(const unsigned char *data, size_t size) { size_t skip = 0; /* Yes, a dot can start an address. De facto before de jure. */ while (skip < size && (!IS_ATEXT(data[skip]) && data[skip] != '.')) skip++; return skip; } static enum email_address_parser_state fts_tokenizer_email_address_parse_local(struct email_address_fts_tokenizer *tok, const unsigned char *data, size_t size, size_t *skip_r) { size_t pos = 0; bool seen_at = FALSE; while (pos < size && (IS_ATEXT(data[pos]) || data[pos] == '@' || data[pos] == '.')) { if (data[pos] == '@') seen_at = TRUE; pos++; if (seen_at) break; } /* localpart and @ */ if (seen_at && (pos > 1 || str_len(tok->last_word) > 0)) { str_append_n(tok->last_word, data, pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_DOMAIN; } /* localpart, @ not included yet */ if (pos > 0 && (IS_ATEXT(data[pos-1]) || data[pos-1] == '.')) { str_append_n(tok->last_word, data, pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_LOCALPART; } /* not a localpart. skip past rest of no-good chars. */ pos += skip_nonlocal_part(data+pos, size - pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_NONE; } static bool domain_is_empty(struct email_address_fts_tokenizer *tok) { const char *p, *str = str_c(tok->last_word); if ((p = strchr(str, '@')) == NULL) return TRUE; return p[1] == '\0'; } static enum email_address_parser_state fts_tokenizer_email_address_parse_domain(struct email_address_fts_tokenizer *tok, const unsigned char *data, size_t size, size_t *skip_r) { size_t pos = 0; while (pos < size && (IS_DTEXT(data[pos]) || data[pos] == '.' || data[pos] == '-')) pos++; /* A complete domain name */ if ((pos > 0 && pos < size) || /* non-atext after atext in this data*/ (pos < size && !domain_is_empty(tok))) { /* non-atext after previous atext */ str_append_n(tok->last_word, data, pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_COMPLETE; } if (pos == size) { /* All good, but possibly not complete. */ str_append_n(tok->last_word, data, pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_DOMAIN; } /* not a domain. skip past no-good chars. */ pos += skip_nonlocal_part(data + pos, size - pos); *skip_r = pos; return EMAIL_ADDRESS_PARSER_STATE_NONE; } /* Buffer raw data for parent. */ static void fts_tokenizer_address_update_parent(struct email_address_fts_tokenizer *tok, const unsigned char *data, size_t size) { if (tok->tokenizer.parent != NULL) str_append_n(tok->parent_data, data, size); } static void fts_tokenizer_email_address_reset(struct fts_tokenizer *_tok) { struct email_address_fts_tokenizer *tok = (struct email_address_fts_tokenizer *)_tok; tok->state = EMAIL_ADDRESS_PARSER_STATE_NONE; str_truncate(tok->last_word, 0); str_truncate(tok->parent_data, 0); } static int fts_tokenizer_email_address_next(struct fts_tokenizer *_tok, const unsigned char *data, size_t size, size_t *skip_r, const char **token_r, const char **error_r ATTR_UNUSED) { struct email_address_fts_tokenizer *tok = (struct email_address_fts_tokenizer *)_tok; size_t pos = 0, local_skip; if (tok->tokenizer.skip_parents == TRUE) tok->tokenizer.skip_parents = FALSE; if (tok->state == EMAIL_ADDRESS_PARSER_STATE_COMPLETE) { *skip_r = pos; if (fts_tokenizer_address_current_token(tok, token_r)) return 1; } /* end of data, output lingering tokens. first the parents data, then possibly our token, if complete enough */ if (size == 0) { if (tok->state == EMAIL_ADDRESS_PARSER_STATE_DOMAIN && domain_is_empty(tok)) { /* user@ without domain - reset state */ str_truncate(tok->last_word, 0); tok->state = EMAIL_ADDRESS_PARSER_STATE_NONE; } if (fts_tokenizer_address_parent_data(tok, token_r)) return 1; if (tok->state == EMAIL_ADDRESS_PARSER_STATE_DOMAIN) { if (fts_tokenizer_address_current_token(tok, token_r)) return 1; } tok->state = EMAIL_ADDRESS_PARSER_STATE_NONE; } /* 1) regular input data OR 2) circle around to return completed address */ while(pos < size || tok->state == EMAIL_ADDRESS_PARSER_STATE_COMPLETE) { switch (tok->state) { case EMAIL_ADDRESS_PARSER_STATE_NONE: /* no part of address found yet. remove possible earlier data */ str_truncate(tok->last_word, 0); /* fall through */ case EMAIL_ADDRESS_PARSER_STATE_LOCALPART: /* last_word is empty or has the beginnings of a valid local-part, but no '@' found yet. continue parsing the beginning of data to see if it contains a full local-part@ */ tok->state = fts_tokenizer_email_address_parse_local(tok, data + pos, size - pos, &local_skip); fts_tokenizer_address_update_parent(tok, data+pos, local_skip); pos += local_skip; break; case EMAIL_ADDRESS_PARSER_STATE_DOMAIN: /* last_word has a local-part@ and maybe the beginning of a domain. continue parsing the beginning of data to see if it contains a valid domain. */ tok->state = fts_tokenizer_email_address_parse_domain(tok, data + pos, size - pos, &local_skip); fts_tokenizer_address_update_parent(tok, data+pos, local_skip); pos += local_skip; break; case EMAIL_ADDRESS_PARSER_STATE_COMPLETE: *skip_r = pos; if (fts_tokenizer_address_parent_data(tok, token_r)) return 1; if (fts_tokenizer_address_current_token(tok, token_r)) return 1; break; default: i_unreached(); } } *skip_r = pos; return 0; } static const struct fts_tokenizer_vfuncs email_address_tokenizer_vfuncs = { fts_tokenizer_email_address_create, fts_tokenizer_email_address_destroy, fts_tokenizer_email_address_reset, fts_tokenizer_email_address_next }; static const struct fts_tokenizer fts_tokenizer_email_address_real = { .name = "email-address", .v = &email_address_tokenizer_vfuncs }; const struct fts_tokenizer *fts_tokenizer_email_address = &fts_tokenizer_email_address_real; dovecot-2.2.33.2/src/lib-fts/WordBreakProperty.txt0000644000175000017500000026714712723567163016677 00000000000000# WordBreakProperty-9.0.0.txt # Date: 2016-06-01, 10:34:38 GMT # © 2016 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see http://www.unicode.org/terms_of_use.html # # Unicode Character Database # For documentation, see http://www.unicode.org/reports/tr44/ # ================================================ # Property: Word_Break # All code points not explicitly listed for Word_Break # have the value Other (XX). # @missing: 0000..10FFFF; Other # ================================================ 0022 ; Double_Quote # Po QUOTATION MARK # Total code points: 1 # ================================================ 0027 ; Single_Quote # Po APOSTROPHE # Total code points: 1 # ================================================ 05D0..05EA ; Hebrew_Letter # Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV 05F0..05F2 ; Hebrew_Letter # Lo [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD FB1D ; Hebrew_Letter # Lo HEBREW LETTER YOD WITH HIRIQ FB1F..FB28 ; Hebrew_Letter # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV FB2A..FB36 ; Hebrew_Letter # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH FB38..FB3C ; Hebrew_Letter # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH FB3E ; Hebrew_Letter # Lo HEBREW LETTER MEM WITH DAGESH FB40..FB41 ; Hebrew_Letter # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH FB43..FB44 ; Hebrew_Letter # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH FB46..FB4F ; Hebrew_Letter # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED # Total code points: 74 # ================================================ 000D ; CR # Cc # Total code points: 1 # ================================================ 000A ; LF # Cc # Total code points: 1 # ================================================ 000B..000C ; Newline # Cc [2] .. 0085 ; Newline # Cc 2028 ; Newline # Zl LINE SEPARATOR 2029 ; Newline # Zp PARAGRAPH SEPARATOR # Total code points: 5 # ================================================ 0300..036F ; Extend # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X 0483..0487 ; Extend # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE 0488..0489 ; Extend # Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN 0591..05BD ; Extend # Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG 05BF ; Extend # Mn HEBREW POINT RAFE 05C1..05C2 ; Extend # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT 05C4..05C5 ; Extend # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT 05C7 ; Extend # Mn HEBREW POINT QAMATS QATAN 0610..061A ; Extend # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA 064B..065F ; Extend # Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW 0670 ; Extend # Mn ARABIC LETTER SUPERSCRIPT ALEF 06D6..06DC ; Extend # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN 06DF..06E4 ; Extend # Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA 06E7..06E8 ; Extend # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON 06EA..06ED ; Extend # Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM 0711 ; Extend # Mn SYRIAC LETTER SUPERSCRIPT ALAPH 0730..074A ; Extend # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH 07A6..07B0 ; Extend # Mn [11] THAANA ABAFILI..THAANA SUKUN 07EB..07F3 ; Extend # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE 0816..0819 ; Extend # Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH 081B..0823 ; Extend # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A 0825..0827 ; Extend # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U 0829..082D ; Extend # Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA 0859..085B ; Extend # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK 08D4..08E1 ; Extend # Mn [14] ARABIC SMALL HIGH WORD AR-RUB..ARABIC SMALL HIGH SIGN SAFHA 08E3..0902 ; Extend # Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA 0903 ; Extend # Mc DEVANAGARI SIGN VISARGA 093A ; Extend # Mn DEVANAGARI VOWEL SIGN OE 093B ; Extend # Mc DEVANAGARI VOWEL SIGN OOE 093C ; Extend # Mn DEVANAGARI SIGN NUKTA 093E..0940 ; Extend # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II 0941..0948 ; Extend # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI 0949..094C ; Extend # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU 094D ; Extend # Mn DEVANAGARI SIGN VIRAMA 094E..094F ; Extend # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW 0951..0957 ; Extend # Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE 0962..0963 ; Extend # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL 0981 ; Extend # Mn BENGALI SIGN CANDRABINDU 0982..0983 ; Extend # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA 09BC ; Extend # Mn BENGALI SIGN NUKTA 09BE..09C0 ; Extend # Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II 09C1..09C4 ; Extend # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR 09C7..09C8 ; Extend # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI 09CB..09CC ; Extend # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU 09CD ; Extend # Mn BENGALI SIGN VIRAMA 09D7 ; Extend # Mc BENGALI AU LENGTH MARK 09E2..09E3 ; Extend # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL 0A01..0A02 ; Extend # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI 0A03 ; Extend # Mc GURMUKHI SIGN VISARGA 0A3C ; Extend # Mn GURMUKHI SIGN NUKTA 0A3E..0A40 ; Extend # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II 0A41..0A42 ; Extend # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU 0A47..0A48 ; Extend # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI 0A4B..0A4D ; Extend # Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA 0A51 ; Extend # Mn GURMUKHI SIGN UDAAT 0A70..0A71 ; Extend # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK 0A75 ; Extend # Mn GURMUKHI SIGN YAKASH 0A81..0A82 ; Extend # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA 0A83 ; Extend # Mc GUJARATI SIGN VISARGA 0ABC ; Extend # Mn GUJARATI SIGN NUKTA 0ABE..0AC0 ; Extend # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II 0AC1..0AC5 ; Extend # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E 0AC7..0AC8 ; Extend # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI 0AC9 ; Extend # Mc GUJARATI VOWEL SIGN CANDRA O 0ACB..0ACC ; Extend # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU 0ACD ; Extend # Mn GUJARATI SIGN VIRAMA 0AE2..0AE3 ; Extend # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL 0B01 ; Extend # Mn ORIYA SIGN CANDRABINDU 0B02..0B03 ; Extend # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA 0B3C ; Extend # Mn ORIYA SIGN NUKTA 0B3E ; Extend # Mc ORIYA VOWEL SIGN AA 0B3F ; Extend # Mn ORIYA VOWEL SIGN I 0B40 ; Extend # Mc ORIYA VOWEL SIGN II 0B41..0B44 ; Extend # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR 0B47..0B48 ; Extend # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI 0B4B..0B4C ; Extend # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU 0B4D ; Extend # Mn ORIYA SIGN VIRAMA 0B56 ; Extend # Mn ORIYA AI LENGTH MARK 0B57 ; Extend # Mc ORIYA AU LENGTH MARK 0B62..0B63 ; Extend # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL 0B82 ; Extend # Mn TAMIL SIGN ANUSVARA 0BBE..0BBF ; Extend # Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I 0BC0 ; Extend # Mn TAMIL VOWEL SIGN II 0BC1..0BC2 ; Extend # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU 0BC6..0BC8 ; Extend # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI 0BCA..0BCC ; Extend # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU 0BCD ; Extend # Mn TAMIL SIGN VIRAMA 0BD7 ; Extend # Mc TAMIL AU LENGTH MARK 0C00 ; Extend # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE 0C01..0C03 ; Extend # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA 0C3E..0C40 ; Extend # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II 0C41..0C44 ; Extend # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR 0C46..0C48 ; Extend # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI 0C4A..0C4D ; Extend # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA 0C55..0C56 ; Extend # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C62..0C63 ; Extend # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C81 ; Extend # Mn KANNADA SIGN CANDRABINDU 0C82..0C83 ; Extend # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA 0CBC ; Extend # Mn KANNADA SIGN NUKTA 0CBE ; Extend # Mc KANNADA VOWEL SIGN AA 0CBF ; Extend # Mn KANNADA VOWEL SIGN I 0CC0..0CC4 ; Extend # Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR 0CC6 ; Extend # Mn KANNADA VOWEL SIGN E 0CC7..0CC8 ; Extend # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI 0CCA..0CCB ; Extend # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC..0CCD ; Extend # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA 0CD5..0CD6 ; Extend # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK 0CE2..0CE3 ; Extend # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0D01 ; Extend # Mn MALAYALAM SIGN CANDRABINDU 0D02..0D03 ; Extend # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA 0D3E..0D40 ; Extend # Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II 0D41..0D44 ; Extend # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR 0D46..0D48 ; Extend # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI 0D4A..0D4C ; Extend # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU 0D4D ; Extend # Mn MALAYALAM SIGN VIRAMA 0D57 ; Extend # Mc MALAYALAM AU LENGTH MARK 0D62..0D63 ; Extend # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL 0D82..0D83 ; Extend # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA 0DCA ; Extend # Mn SINHALA SIGN AL-LAKUNA 0DCF..0DD1 ; Extend # Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA 0DD2..0DD4 ; Extend # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA 0DD6 ; Extend # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA 0DD8..0DDF ; Extend # Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA 0DF2..0DF3 ; Extend # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA 0E31 ; Extend # Mn THAI CHARACTER MAI HAN-AKAT 0E34..0E3A ; Extend # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU 0E47..0E4E ; Extend # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN 0EB1 ; Extend # Mn LAO VOWEL SIGN MAI KAN 0EB4..0EB9 ; Extend # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU 0EBB..0EBC ; Extend # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO 0EC8..0ECD ; Extend # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA 0F18..0F19 ; Extend # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS 0F35 ; Extend # Mn TIBETAN MARK NGAS BZUNG NYI ZLA 0F37 ; Extend # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS 0F39 ; Extend # Mn TIBETAN MARK TSA -PHRU 0F3E..0F3F ; Extend # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES 0F71..0F7E ; Extend # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO 0F7F ; Extend # Mc TIBETAN SIGN RNAM BCAD 0F80..0F84 ; Extend # Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA 0F86..0F87 ; Extend # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS 0F8D..0F97 ; Extend # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA 0F99..0FBC ; Extend # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA 0FC6 ; Extend # Mn TIBETAN SYMBOL PADMA GDAN 102B..102C ; Extend # Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA 102D..1030 ; Extend # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU 1031 ; Extend # Mc MYANMAR VOWEL SIGN E 1032..1037 ; Extend # Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW 1038 ; Extend # Mc MYANMAR SIGN VISARGA 1039..103A ; Extend # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT 103B..103C ; Extend # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA 103D..103E ; Extend # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA 1056..1057 ; Extend # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR 1058..1059 ; Extend # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL 105E..1060 ; Extend # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA 1062..1064 ; Extend # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO 1067..106D ; Extend # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5 1071..1074 ; Extend # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE 1082 ; Extend # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA 1083..1084 ; Extend # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E 1085..1086 ; Extend # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y 1087..108C ; Extend # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3 108D ; Extend # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE 108F ; Extend # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5 109A..109C ; Extend # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A 109D ; Extend # Mn MYANMAR VOWEL SIGN AITON AI 135D..135F ; Extend # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK 1712..1714 ; Extend # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA 1732..1734 ; Extend # Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD 1752..1753 ; Extend # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U 1772..1773 ; Extend # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U 17B4..17B5 ; Extend # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA 17B6 ; Extend # Mc KHMER VOWEL SIGN AA 17B7..17BD ; Extend # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA 17BE..17C5 ; Extend # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU 17C6 ; Extend # Mn KHMER SIGN NIKAHIT 17C7..17C8 ; Extend # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU 17C9..17D3 ; Extend # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT 17DD ; Extend # Mn KHMER SIGN ATTHACAN 180B..180D ; Extend # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE 1885..1886 ; Extend # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA 18A9 ; Extend # Mn MONGOLIAN LETTER ALI GALI DAGALGA 1920..1922 ; Extend # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U 1923..1926 ; Extend # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU 1927..1928 ; Extend # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O 1929..192B ; Extend # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA 1930..1931 ; Extend # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA 1932 ; Extend # Mn LIMBU SMALL LETTER ANUSVARA 1933..1938 ; Extend # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA 1939..193B ; Extend # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I 1A17..1A18 ; Extend # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U 1A19..1A1A ; Extend # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O 1A1B ; Extend # Mn BUGINESE VOWEL SIGN AE 1A55 ; Extend # Mc TAI THAM CONSONANT SIGN MEDIAL RA 1A56 ; Extend # Mn TAI THAM CONSONANT SIGN MEDIAL LA 1A57 ; Extend # Mc TAI THAM CONSONANT SIGN LA TANG LAI 1A58..1A5E ; Extend # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA 1A60 ; Extend # Mn TAI THAM SIGN SAKOT 1A61 ; Extend # Mc TAI THAM VOWEL SIGN A 1A62 ; Extend # Mn TAI THAM VOWEL SIGN MAI SAT 1A63..1A64 ; Extend # Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA 1A65..1A6C ; Extend # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW 1A6D..1A72 ; Extend # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI 1A73..1A7C ; Extend # Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN 1A7F ; Extend # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT 1AB0..1ABD ; Extend # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY 1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04 ; Extend # Mc BALINESE SIGN BISAH 1B34 ; Extend # Mn BALINESE SIGN REREKAN 1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG 1B36..1B3A ; Extend # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA 1B3B ; Extend # Mc BALINESE VOWEL SIGN RA REPA TEDUNG 1B3C ; Extend # Mn BALINESE VOWEL SIGN LA LENGA 1B3D..1B41 ; Extend # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG 1B42 ; Extend # Mn BALINESE VOWEL SIGN PEPET 1B43..1B44 ; Extend # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG 1B6B..1B73 ; Extend # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG 1B80..1B81 ; Extend # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR 1B82 ; Extend # Mc SUNDANESE SIGN PANGWISAD 1BA1 ; Extend # Mc SUNDANESE CONSONANT SIGN PAMINGKAL 1BA2..1BA5 ; Extend # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU 1BA6..1BA7 ; Extend # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG 1BA8..1BA9 ; Extend # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG 1BAA ; Extend # Mc SUNDANESE SIGN PAMAAEH 1BAB..1BAD ; Extend # Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA 1BE6 ; Extend # Mn BATAK SIGN TOMPI 1BE7 ; Extend # Mc BATAK VOWEL SIGN E 1BE8..1BE9 ; Extend # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE 1BEA..1BEC ; Extend # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O 1BED ; Extend # Mn BATAK VOWEL SIGN KARO O 1BEE ; Extend # Mc BATAK VOWEL SIGN U 1BEF..1BF1 ; Extend # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H 1BF2..1BF3 ; Extend # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN 1C24..1C2B ; Extend # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU 1C2C..1C33 ; Extend # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T 1C34..1C35 ; Extend # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG 1C36..1C37 ; Extend # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA 1CD0..1CD2 ; Extend # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA 1CD4..1CE0 ; Extend # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA 1CE1 ; Extend # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA 1CE2..1CE8 ; Extend # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL 1CED ; Extend # Mn VEDIC SIGN TIRYAK 1CF2..1CF3 ; Extend # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA 1CF4 ; Extend # Mn VEDIC TONE CANDRA ABOVE 1CF8..1CF9 ; Extend # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE 1DC0..1DF5 ; Extend # Mn [54] COMBINING DOTTED GRAVE ACCENT..COMBINING UP TACK ABOVE 1DFB..1DFF ; Extend # Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW 200C ; Extend # Cf ZERO WIDTH NON-JOINER 20D0..20DC ; Extend # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE 20DD..20E0 ; Extend # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH 20E1 ; Extend # Mn COMBINING LEFT RIGHT ARROW ABOVE 20E2..20E4 ; Extend # Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE 20E5..20F0 ; Extend # Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE 2CEF..2CF1 ; Extend # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS 2D7F ; Extend # Mn TIFINAGH CONSONANT JOINER 2DE0..2DFF ; Extend # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS 302A..302D ; Extend # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK 302E..302F ; Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK 3099..309A ; Extend # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK A66F ; Extend # Mn COMBINING CYRILLIC VZMET A670..A672 ; Extend # Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN A674..A67D ; Extend # Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK A69E..A69F ; Extend # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E A6F0..A6F1 ; Extend # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS A802 ; Extend # Mn SYLOTI NAGRI SIGN DVISVARA A806 ; Extend # Mn SYLOTI NAGRI SIGN HASANTA A80B ; Extend # Mn SYLOTI NAGRI SIGN ANUSVARA A823..A824 ; Extend # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I A825..A826 ; Extend # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E A827 ; Extend # Mc SYLOTI NAGRI VOWEL SIGN OO A880..A881 ; Extend # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA A8B4..A8C3 ; Extend # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU A8C4..A8C5 ; Extend # Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU A8E0..A8F1 ; Extend # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA A926..A92D ; Extend # Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU A947..A951 ; Extend # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R A952..A953 ; Extend # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA A980..A982 ; Extend # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR A983 ; Extend # Mc JAVANESE SIGN WIGNYAN A9B3 ; Extend # Mn JAVANESE SIGN CECAK TELU A9B4..A9B5 ; Extend # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG A9B6..A9B9 ; Extend # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT A9BA..A9BB ; Extend # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE A9BC ; Extend # Mn JAVANESE VOWEL SIGN PEPET A9BD..A9C0 ; Extend # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON A9E5 ; Extend # Mn MYANMAR SIGN SHAN SAW AA29..AA2E ; Extend # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE AA2F..AA30 ; Extend # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI AA31..AA32 ; Extend # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE AA33..AA34 ; Extend # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA AA35..AA36 ; Extend # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA AA43 ; Extend # Mn CHAM CONSONANT SIGN FINAL NG AA4C ; Extend # Mn CHAM CONSONANT SIGN FINAL M AA4D ; Extend # Mc CHAM CONSONANT SIGN FINAL H AA7B ; Extend # Mc MYANMAR SIGN PAO KAREN TONE AA7C ; Extend # Mn MYANMAR SIGN TAI LAING TONE-2 AA7D ; Extend # Mc MYANMAR SIGN TAI LAING TONE-5 AAB0 ; Extend # Mn TAI VIET MAI KANG AAB2..AAB4 ; Extend # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U AAB7..AAB8 ; Extend # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA AABE..AABF ; Extend # Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK AAC1 ; Extend # Mn TAI VIET TONE MAI THO AAEB ; Extend # Mc MEETEI MAYEK VOWEL SIGN II AAEC..AAED ; Extend # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI AAEE..AAEF ; Extend # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU AAF5 ; Extend # Mc MEETEI MAYEK VOWEL SIGN VISARGA AAF6 ; Extend # Mn MEETEI MAYEK VIRAMA ABE3..ABE4 ; Extend # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP ABE5 ; Extend # Mn MEETEI MAYEK VOWEL SIGN ANAP ABE6..ABE7 ; Extend # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP ABE8 ; Extend # Mn MEETEI MAYEK VOWEL SIGN UNAP ABE9..ABEA ; Extend # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG ABEC ; Extend # Mc MEETEI MAYEK LUM IYEK ABED ; Extend # Mn MEETEI MAYEK APUN IYEK FB1E ; Extend # Mn HEBREW POINT JUDEO-SPANISH VARIKA FE00..FE0F ; Extend # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 FE20..FE2F ; Extend # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK 101FD ; Extend # Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE 102E0 ; Extend # Mn COPTIC EPACT THOUSANDS MARK 10376..1037A ; Extend # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII 10A01..10A03 ; Extend # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R 10A05..10A06 ; Extend # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O 10A0C..10A0F ; Extend # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA 10A38..10A3A ; Extend # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW 10A3F ; Extend # Mn KHAROSHTHI VIRAMA 10AE5..10AE6 ; Extend # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW 11000 ; Extend # Mc BRAHMI SIGN CANDRABINDU 11001 ; Extend # Mn BRAHMI SIGN ANUSVARA 11002 ; Extend # Mc BRAHMI SIGN VISARGA 11038..11046 ; Extend # Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA 1107F..11081 ; Extend # Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA 11082 ; Extend # Mc KAITHI SIGN VISARGA 110B0..110B2 ; Extend # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II 110B3..110B6 ; Extend # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI 110B7..110B8 ; Extend # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU 110B9..110BA ; Extend # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA 11100..11102 ; Extend # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA 11127..1112B ; Extend # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU 1112C ; Extend # Mc CHAKMA VOWEL SIGN E 1112D..11134 ; Extend # Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA 11173 ; Extend # Mn MAHAJANI SIGN NUKTA 11180..11181 ; Extend # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA 11182 ; Extend # Mc SHARADA SIGN VISARGA 111B3..111B5 ; Extend # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II 111B6..111BE ; Extend # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O 111BF..111C0 ; Extend # Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA 111CA..111CC ; Extend # Mn [3] SHARADA SIGN NUKTA..SHARADA EXTRA SHORT VOWEL MARK 1122C..1122E ; Extend # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II 1122F..11231 ; Extend # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI 11232..11233 ; Extend # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU 11234 ; Extend # Mn KHOJKI SIGN ANUSVARA 11235 ; Extend # Mc KHOJKI SIGN VIRAMA 11236..11237 ; Extend # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA 1123E ; Extend # Mn KHOJKI SIGN SUKUN 112DF ; Extend # Mn KHUDAWADI SIGN ANUSVARA 112E0..112E2 ; Extend # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II 112E3..112EA ; Extend # Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA 11300..11301 ; Extend # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU 11302..11303 ; Extend # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA 1133C ; Extend # Mn GRANTHA SIGN NUKTA 1133E..1133F ; Extend # Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I 11340 ; Extend # Mn GRANTHA VOWEL SIGN II 11341..11344 ; Extend # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR 11347..11348 ; Extend # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI 1134B..1134D ; Extend # Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA 11357 ; Extend # Mc GRANTHA AU LENGTH MARK 11362..11363 ; Extend # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL 11366..1136C ; Extend # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX 11370..11374 ; Extend # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA 11435..11437 ; Extend # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II 11438..1143F ; Extend # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI 11440..11441 ; Extend # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU 11442..11444 ; Extend # Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA 11445 ; Extend # Mc NEWA SIGN VISARGA 11446 ; Extend # Mn NEWA SIGN NUKTA 114B0..114B2 ; Extend # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II 114B3..114B8 ; Extend # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL 114B9 ; Extend # Mc TIRHUTA VOWEL SIGN E 114BA ; Extend # Mn TIRHUTA VOWEL SIGN SHORT E 114BB..114BE ; Extend # Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU 114BF..114C0 ; Extend # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA 114C1 ; Extend # Mc TIRHUTA SIGN VISARGA 114C2..114C3 ; Extend # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA 115AF..115B1 ; Extend # Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II 115B2..115B5 ; Extend # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR 115B8..115BB ; Extend # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU 115BC..115BD ; Extend # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA 115BE ; Extend # Mc SIDDHAM SIGN VISARGA 115BF..115C0 ; Extend # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA 115DC..115DD ; Extend # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU 11630..11632 ; Extend # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II 11633..1163A ; Extend # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI 1163B..1163C ; Extend # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU 1163D ; Extend # Mn MODI SIGN ANUSVARA 1163E ; Extend # Mc MODI SIGN VISARGA 1163F..11640 ; Extend # Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA 116AB ; Extend # Mn TAKRI SIGN ANUSVARA 116AC ; Extend # Mc TAKRI SIGN VISARGA 116AD ; Extend # Mn TAKRI VOWEL SIGN AA 116AE..116AF ; Extend # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II 116B0..116B5 ; Extend # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU 116B6 ; Extend # Mc TAKRI SIGN VIRAMA 116B7 ; Extend # Mn TAKRI SIGN NUKTA 1171D..1171F ; Extend # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA 11720..11721 ; Extend # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA 11722..11725 ; Extend # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU 11726 ; Extend # Mc AHOM VOWEL SIGN E 11727..1172B ; Extend # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER 11C2F ; Extend # Mc BHAIKSUKI VOWEL SIGN AA 11C30..11C36 ; Extend # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D ; Extend # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 11C3E ; Extend # Mc BHAIKSUKI SIGN VISARGA 11C3F ; Extend # Mn BHAIKSUKI SIGN VIRAMA 11C92..11CA7 ; Extend # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA 11CA9 ; Extend # Mc MARCHEN SUBJOINED LETTER YA 11CAA..11CB0 ; Extend # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA 11CB1 ; Extend # Mc MARCHEN VOWEL SIGN I 11CB2..11CB3 ; Extend # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E 11CB4 ; Extend # Mc MARCHEN VOWEL SIGN O 11CB5..11CB6 ; Extend # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU 16AF0..16AF4 ; Extend # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE 16B30..16B36 ; Extend # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM 16F51..16F7E ; Extend # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG 16F8F..16F92 ; Extend # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW 1BC9D..1BC9E ; Extend # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK 1D165..1D166 ; Extend # Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM 1D167..1D169 ; Extend # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 1D16D..1D172 ; Extend # Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 1D17B..1D182 ; Extend # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE 1D185..1D18B ; Extend # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE 1D1AA..1D1AD ; Extend # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO 1D242..1D244 ; Extend # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME 1DA00..1DA36 ; Extend # Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN 1DA3B..1DA6C ; Extend # Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT 1DA75 ; Extend # Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS 1DA84 ; Extend # Mn SIGNWRITING LOCATION HEAD NECK 1DA9B..1DA9F ; Extend # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 1DAA1..1DAAF ; Extend # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 1E000..1E006 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE 1E008..1E018 ; Extend # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU 1E01B..1E021 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI 1E023..1E024 ; Extend # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS 1E026..1E02A ; Extend # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA 1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 # Total code points: 2196 # ================================================ 1F1E6..1F1FF ; Regional_Indicator # So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z # Total code points: 26 # ================================================ 00AD ; Format # Cf SOFT HYPHEN 0600..0605 ; Format # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE 061C ; Format # Cf ARABIC LETTER MARK 06DD ; Format # Cf ARABIC END OF AYAH 070F ; Format # Cf SYRIAC ABBREVIATION MARK 08E2 ; Format # Cf ARABIC DISPUTED END OF AYAH 180E ; Format # Cf MONGOLIAN VOWEL SEPARATOR 200E..200F ; Format # Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK 202A..202E ; Format # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE 2060..2064 ; Format # Cf [5] WORD JOINER..INVISIBLE PLUS 2066..206F ; Format # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES FEFF ; Format # Cf ZERO WIDTH NO-BREAK SPACE FFF9..FFFB ; Format # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR 110BD ; Format # Cf KAITHI NUMBER SIGN 1BCA0..1BCA3 ; Format # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP 1D173..1D17A ; Format # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE E0001 ; Format # Cf LANGUAGE TAG # Total code points: 52 # ================================================ 3031..3035 ; Katakana # Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF 309B..309C ; Katakana # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 30A0 ; Katakana # Pd KATAKANA-HIRAGANA DOUBLE HYPHEN 30A1..30FA ; Katakana # Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO 30FC..30FE ; Katakana # Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK 30FF ; Katakana # Lo KATAKANA DIGRAPH KOTO 31F0..31FF ; Katakana # Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO 32D0..32FE ; Katakana # So [47] CIRCLED KATAKANA A..CIRCLED KATAKANA WO 3300..3357 ; Katakana # So [88] SQUARE APAATO..SQUARE WATTO FF66..FF6F ; Katakana # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU FF70 ; Katakana # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N 1B000 ; Katakana # Lo KATAKANA LETTER ARCHAIC E # Total code points: 310 # ================================================ 0041..005A ; ALetter # L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z 0061..007A ; ALetter # L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z 00AA ; ALetter # Lo FEMININE ORDINAL INDICATOR 00B5 ; ALetter # L& MICRO SIGN 00BA ; ALetter # Lo MASCULINE ORDINAL INDICATOR 00C0..00D6 ; ALetter # L& [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS 00D8..00F6 ; ALetter # L& [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS 00F8..01BA ; ALetter # L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL 01BB ; ALetter # Lo LATIN LETTER TWO WITH STROKE 01BC..01BF ; ALetter # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; ALetter # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; ALetter # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL 0294 ; ALetter # Lo LATIN LETTER GLOTTAL STOP 0295..02AF ; ALetter # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; ALetter # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; ALetter # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; ALetter # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP 02EC ; ALetter # Lm MODIFIER LETTER VOICING 02EE ; ALetter # Lm MODIFIER LETTER DOUBLE APOSTROPHE 0370..0373 ; ALetter # L& [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI 0374 ; ALetter # Lm GREEK NUMERAL SIGN 0376..0377 ; ALetter # L& [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA 037A ; ALetter # Lm GREEK YPOGEGRAMMENI 037B..037D ; ALetter # L& [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL 037F ; ALetter # L& GREEK CAPITAL LETTER YOT 0386 ; ALetter # L& GREEK CAPITAL LETTER ALPHA WITH TONOS 0388..038A ; ALetter # L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS 038C ; ALetter # L& GREEK CAPITAL LETTER OMICRON WITH TONOS 038E..03A1 ; ALetter # L& [20] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK CAPITAL LETTER RHO 03A3..03F5 ; ALetter # L& [83] GREEK CAPITAL LETTER SIGMA..GREEK LUNATE EPSILON SYMBOL 03F7..0481 ; ALetter # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA 048A..052F ; ALetter # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER 0531..0556 ; ALetter # L& [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH 0559 ; ALetter # Lm ARMENIAN MODIFIER LETTER LEFT HALF RING 0561..0587 ; ALetter # L& [39] ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LIGATURE ECH YIWN 05F3 ; ALetter # Po HEBREW PUNCTUATION GERESH 0620..063F ; ALetter # Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE 0640 ; ALetter # Lm ARABIC TATWEEL 0641..064A ; ALetter # Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH 066E..066F ; ALetter # Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF 0671..06D3 ; ALetter # Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE 06D5 ; ALetter # Lo ARABIC LETTER AE 06E5..06E6 ; ALetter # Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH 06EE..06EF ; ALetter # Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V 06FA..06FC ; ALetter # Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW 06FF ; ALetter # Lo ARABIC LETTER HEH WITH INVERTED V 0710 ; ALetter # Lo SYRIAC LETTER ALAPH 0712..072F ; ALetter # Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH 074D..07A5 ; ALetter # Lo [89] SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER WAAVU 07B1 ; ALetter # Lo THAANA LETTER NAA 07CA..07EA ; ALetter # Lo [33] NKO LETTER A..NKO LETTER JONA RA 07F4..07F5 ; ALetter # Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE 07FA ; ALetter # Lm NKO LAJANYALAN 0800..0815 ; ALetter # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF 081A ; ALetter # Lm SAMARITAN MODIFIER LETTER EPENTHETIC YUT 0824 ; ALetter # Lm SAMARITAN MODIFIER LETTER SHORT A 0828 ; ALetter # Lm SAMARITAN MODIFIER LETTER I 0840..0858 ; ALetter # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 08A0..08B4 ; ALetter # Lo [21] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER KAF WITH DOT BELOW 08B6..08BD ; ALetter # Lo [8] ARABIC LETTER BEH WITH SMALL MEEM ABOVE..ARABIC LETTER AFRICAN NOON 0904..0939 ; ALetter # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA 093D ; ALetter # Lo DEVANAGARI SIGN AVAGRAHA 0950 ; ALetter # Lo DEVANAGARI OM 0958..0961 ; ALetter # Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL 0971 ; ALetter # Lm DEVANAGARI SIGN HIGH SPACING DOT 0972..0980 ; ALetter # Lo [15] DEVANAGARI LETTER CANDRA A..BENGALI ANJI 0985..098C ; ALetter # Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L 098F..0990 ; ALetter # Lo [2] BENGALI LETTER E..BENGALI LETTER AI 0993..09A8 ; ALetter # Lo [22] BENGALI LETTER O..BENGALI LETTER NA 09AA..09B0 ; ALetter # Lo [7] BENGALI LETTER PA..BENGALI LETTER RA 09B2 ; ALetter # Lo BENGALI LETTER LA 09B6..09B9 ; ALetter # Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA 09BD ; ALetter # Lo BENGALI SIGN AVAGRAHA 09CE ; ALetter # Lo BENGALI LETTER KHANDA TA 09DC..09DD ; ALetter # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA 09DF..09E1 ; ALetter # Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL 09F0..09F1 ; ALetter # Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL 0A05..0A0A ; ALetter # Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU 0A0F..0A10 ; ALetter # Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI 0A13..0A28 ; ALetter # Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA 0A2A..0A30 ; ALetter # Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA 0A32..0A33 ; ALetter # Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA 0A35..0A36 ; ALetter # Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA 0A38..0A39 ; ALetter # Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA 0A59..0A5C ; ALetter # Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA 0A5E ; ALetter # Lo GURMUKHI LETTER FA 0A72..0A74 ; ALetter # Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR 0A85..0A8D ; ALetter # Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E 0A8F..0A91 ; ALetter # Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O 0A93..0AA8 ; ALetter # Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA 0AAA..0AB0 ; ALetter # Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA 0AB2..0AB3 ; ALetter # Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA 0AB5..0AB9 ; ALetter # Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA 0ABD ; ALetter # Lo GUJARATI SIGN AVAGRAHA 0AD0 ; ALetter # Lo GUJARATI OM 0AE0..0AE1 ; ALetter # Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL 0AF9 ; ALetter # Lo GUJARATI LETTER ZHA 0B05..0B0C ; ALetter # Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L 0B0F..0B10 ; ALetter # Lo [2] ORIYA LETTER E..ORIYA LETTER AI 0B13..0B28 ; ALetter # Lo [22] ORIYA LETTER O..ORIYA LETTER NA 0B2A..0B30 ; ALetter # Lo [7] ORIYA LETTER PA..ORIYA LETTER RA 0B32..0B33 ; ALetter # Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA 0B35..0B39 ; ALetter # Lo [5] ORIYA LETTER VA..ORIYA LETTER HA 0B3D ; ALetter # Lo ORIYA SIGN AVAGRAHA 0B5C..0B5D ; ALetter # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA 0B5F..0B61 ; ALetter # Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL 0B71 ; ALetter # Lo ORIYA LETTER WA 0B83 ; ALetter # Lo TAMIL SIGN VISARGA 0B85..0B8A ; ALetter # Lo [6] TAMIL LETTER A..TAMIL LETTER UU 0B8E..0B90 ; ALetter # Lo [3] TAMIL LETTER E..TAMIL LETTER AI 0B92..0B95 ; ALetter # Lo [4] TAMIL LETTER O..TAMIL LETTER KA 0B99..0B9A ; ALetter # Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA 0B9C ; ALetter # Lo TAMIL LETTER JA 0B9E..0B9F ; ALetter # Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA 0BA3..0BA4 ; ALetter # Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA 0BA8..0BAA ; ALetter # Lo [3] TAMIL LETTER NA..TAMIL LETTER PA 0BAE..0BB9 ; ALetter # Lo [12] TAMIL LETTER MA..TAMIL LETTER HA 0BD0 ; ALetter # Lo TAMIL OM 0C05..0C0C ; ALetter # Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L 0C0E..0C10 ; ALetter # Lo [3] TELUGU LETTER E..TELUGU LETTER AI 0C12..0C28 ; ALetter # Lo [23] TELUGU LETTER O..TELUGU LETTER NA 0C2A..0C39 ; ALetter # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA 0C3D ; ALetter # Lo TELUGU SIGN AVAGRAHA 0C58..0C5A ; ALetter # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA 0C60..0C61 ; ALetter # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C80 ; ALetter # Lo KANNADA SIGN SPACING CANDRABINDU 0C85..0C8C ; ALetter # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L 0C8E..0C90 ; ALetter # Lo [3] KANNADA LETTER E..KANNADA LETTER AI 0C92..0CA8 ; ALetter # Lo [23] KANNADA LETTER O..KANNADA LETTER NA 0CAA..0CB3 ; ALetter # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA 0CB5..0CB9 ; ALetter # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA 0CBD ; ALetter # Lo KANNADA SIGN AVAGRAHA 0CDE ; ALetter # Lo KANNADA LETTER FA 0CE0..0CE1 ; ALetter # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CF1..0CF2 ; ALetter # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0D05..0D0C ; ALetter # Lo [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L 0D0E..0D10 ; ALetter # Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI 0D12..0D3A ; ALetter # Lo [41] MALAYALAM LETTER O..MALAYALAM LETTER TTTA 0D3D ; ALetter # Lo MALAYALAM SIGN AVAGRAHA 0D4E ; ALetter # Lo MALAYALAM LETTER DOT REPH 0D54..0D56 ; ALetter # Lo [3] MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL 0D5F..0D61 ; ALetter # Lo [3] MALAYALAM LETTER ARCHAIC II..MALAYALAM LETTER VOCALIC LL 0D7A..0D7F ; ALetter # Lo [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K 0D85..0D96 ; ALetter # Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA 0D9A..0DB1 ; ALetter # Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA 0DB3..0DBB ; ALetter # Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA 0DBD ; ALetter # Lo SINHALA LETTER DANTAJA LAYANNA 0DC0..0DC6 ; ALetter # Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA 0F00 ; ALetter # Lo TIBETAN SYLLABLE OM 0F40..0F47 ; ALetter # Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA 0F49..0F6C ; ALetter # Lo [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA 0F88..0F8C ; ALetter # Lo [5] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN INVERTED MCHU CAN 10A0..10C5 ; ALetter # L& [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE 10C7 ; ALetter # L& GEORGIAN CAPITAL LETTER YN 10CD ; ALetter # L& GEORGIAN CAPITAL LETTER AEN 10D0..10FA ; ALetter # Lo [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN 10FC ; ALetter # Lm MODIFIER LETTER GEORGIAN NAR 10FD..1248 ; ALetter # Lo [332] GEORGIAN LETTER AEN..ETHIOPIC SYLLABLE QWA 124A..124D ; ALetter # Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE 1250..1256 ; ALetter # Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO 1258 ; ALetter # Lo ETHIOPIC SYLLABLE QHWA 125A..125D ; ALetter # Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE 1260..1288 ; ALetter # Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA 128A..128D ; ALetter # Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE 1290..12B0 ; ALetter # Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA 12B2..12B5 ; ALetter # Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE 12B8..12BE ; ALetter # Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO 12C0 ; ALetter # Lo ETHIOPIC SYLLABLE KXWA 12C2..12C5 ; ALetter # Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE 12C8..12D6 ; ALetter # Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O 12D8..1310 ; ALetter # Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA 1312..1315 ; ALetter # Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE 1318..135A ; ALetter # Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA 1380..138F ; ALetter # Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE 13A0..13F5 ; ALetter # L& [86] CHEROKEE LETTER A..CHEROKEE LETTER MV 13F8..13FD ; ALetter # L& [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV 1401..166C ; ALetter # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA 166F..167F ; ALetter # Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W 1681..169A ; ALetter # Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH 16A0..16EA ; ALetter # Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X 16EE..16F0 ; ALetter # Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL 16F1..16F8 ; ALetter # Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC 1700..170C ; ALetter # Lo [13] TAGALOG LETTER A..TAGALOG LETTER YA 170E..1711 ; ALetter # Lo [4] TAGALOG LETTER LA..TAGALOG LETTER HA 1720..1731 ; ALetter # Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA 1740..1751 ; ALetter # Lo [18] BUHID LETTER A..BUHID LETTER HA 1760..176C ; ALetter # Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA 176E..1770 ; ALetter # Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA 1820..1842 ; ALetter # Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI 1843 ; ALetter # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN 1844..1877 ; ALetter # Lo [52] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER MANCHU ZHA 1880..1884 ; ALetter # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA 1887..18A8 ; ALetter # Lo [34] MONGOLIAN LETTER ALI GALI A..MONGOLIAN LETTER MANCHU ALI GALI BHA 18AA ; ALetter # Lo MONGOLIAN LETTER MANCHU ALI GALI LHA 18B0..18F5 ; ALetter # Lo [70] CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S 1900..191E ; ALetter # Lo [31] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER TRA 1A00..1A16 ; ALetter # Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA 1B05..1B33 ; ALetter # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA 1B45..1B4B ; ALetter # Lo [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK 1B83..1BA0 ; ALetter # Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA 1BAE..1BAF ; ALetter # Lo [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA 1BBA..1BE5 ; ALetter # Lo [44] SUNDANESE AVAGRAHA..BATAK LETTER U 1C00..1C23 ; ALetter # Lo [36] LEPCHA LETTER KA..LEPCHA LETTER A 1C4D..1C4F ; ALetter # Lo [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA 1C5A..1C77 ; ALetter # Lo [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH 1C78..1C7D ; ALetter # Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD 1C80..1C88 ; ALetter # L& [9] CYRILLIC SMALL LETTER ROUNDED VE..CYRILLIC SMALL LETTER UNBLENDED UK 1CE9..1CEC ; ALetter # Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL 1CEE..1CF1 ; ALetter # Lo [4] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ANUSVARA UBHAYATO MUKHA 1CF5..1CF6 ; ALetter # Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA 1D00..1D2B ; ALetter # L& [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL 1D2C..1D6A ; ALetter # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI 1D6B..1D77 ; ALetter # L& [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G 1D78 ; ALetter # Lm MODIFIER LETTER CYRILLIC EN 1D79..1D9A ; ALetter # L& [34] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK 1D9B..1DBF ; ALetter # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA 1E00..1F15 ; ALetter # L& [278] LATIN CAPITAL LETTER A WITH RING BELOW..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA 1F18..1F1D ; ALetter # L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA 1F20..1F45 ; ALetter # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA 1F48..1F4D ; ALetter # L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA 1F50..1F57 ; ALetter # L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI 1F59 ; ALetter # L& GREEK CAPITAL LETTER UPSILON WITH DASIA 1F5B ; ALetter # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA 1F5D ; ALetter # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA 1F5F..1F7D ; ALetter # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA 1F80..1FB4 ; ALetter # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI 1FB6..1FBC ; ALetter # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI 1FBE ; ALetter # L& GREEK PROSGEGRAMMENI 1FC2..1FC4 ; ALetter # L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI 1FC6..1FCC ; ALetter # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI 1FD0..1FD3 ; ALetter # L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA 1FD6..1FDB ; ALetter # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA 1FE0..1FEC ; ALetter # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA 1FF2..1FF4 ; ALetter # L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI 1FF6..1FFC ; ALetter # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI 2071 ; ALetter # Lm SUPERSCRIPT LATIN SMALL LETTER I 207F ; ALetter # Lm SUPERSCRIPT LATIN SMALL LETTER N 2090..209C ; ALetter # Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T 2102 ; ALetter # L& DOUBLE-STRUCK CAPITAL C 2107 ; ALetter # L& EULER CONSTANT 210A..2113 ; ALetter # L& [10] SCRIPT SMALL G..SCRIPT SMALL L 2115 ; ALetter # L& DOUBLE-STRUCK CAPITAL N 2119..211D ; ALetter # L& [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R 2124 ; ALetter # L& DOUBLE-STRUCK CAPITAL Z 2126 ; ALetter # L& OHM SIGN 2128 ; ALetter # L& BLACK-LETTER CAPITAL Z 212A..212D ; ALetter # L& [4] KELVIN SIGN..BLACK-LETTER CAPITAL C 212F..2134 ; ALetter # L& [6] SCRIPT SMALL E..SCRIPT SMALL O 2135..2138 ; ALetter # Lo [4] ALEF SYMBOL..DALET SYMBOL 2139 ; ALetter # L& INFORMATION SOURCE 213C..213F ; ALetter # L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI 2145..2149 ; ALetter # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J 214E ; ALetter # L& TURNED SMALL F 2160..2182 ; ALetter # Nl [35] ROMAN NUMERAL ONE..ROMAN NUMERAL TEN THOUSAND 2183..2184 ; ALetter # L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C 2185..2188 ; ALetter # Nl [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND 24B6..24E9 ; ALetter # So [52] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN SMALL LETTER Z 2C00..2C2E ; ALetter # L& [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE 2C30..2C5E ; ALetter # L& [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE 2C60..2C7B ; ALetter # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E 2C7C..2C7D ; ALetter # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V 2C7E..2CE4 ; ALetter # L& [103] LATIN CAPITAL LETTER S WITH SWASH TAIL..COPTIC SYMBOL KAI 2CEB..2CEE ; ALetter # L& [4] COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI..COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA 2CF2..2CF3 ; ALetter # L& [2] COPTIC CAPITAL LETTER BOHAIRIC KHEI..COPTIC SMALL LETTER BOHAIRIC KHEI 2D00..2D25 ; ALetter # L& [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE 2D27 ; ALetter # L& GEORGIAN SMALL LETTER YN 2D2D ; ALetter # L& GEORGIAN SMALL LETTER AEN 2D30..2D67 ; ALetter # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO 2D6F ; ALetter # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK 2D80..2D96 ; ALetter # Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE 2DA0..2DA6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO 2DA8..2DAE ; ALetter # Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO 2DB0..2DB6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO 2DB8..2DBE ; ALetter # Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO 2DC0..2DC6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO 2DC8..2DCE ; ALetter # Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO 2DD0..2DD6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO 2DD8..2DDE ; ALetter # Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO 2E2F ; ALetter # Lm VERTICAL TILDE 3005 ; ALetter # Lm IDEOGRAPHIC ITERATION MARK 303B ; ALetter # Lm VERTICAL IDEOGRAPHIC ITERATION MARK 303C ; ALetter # Lo MASU MARK 3105..312D ; ALetter # Lo [41] BOPOMOFO LETTER B..BOPOMOFO LETTER IH 3131..318E ; ALetter # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE 31A0..31BA ; ALetter # Lo [27] BOPOMOFO LETTER BU..BOPOMOFO LETTER ZY A000..A014 ; ALetter # Lo [21] YI SYLLABLE IT..YI SYLLABLE E A015 ; ALetter # Lm YI SYLLABLE WU A016..A48C ; ALetter # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR A4D0..A4F7 ; ALetter # Lo [40] LISU LETTER BA..LISU LETTER OE A4F8..A4FD ; ALetter # Lm [6] LISU LETTER TONE MYA TI..LISU LETTER TONE MYA JEU A500..A60B ; ALetter # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG A60C ; ALetter # Lm VAI SYLLABLE LENGTHENER A610..A61F ; ALetter # Lo [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG A62A..A62B ; ALetter # Lo [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO A640..A66D ; ALetter # L& [46] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O A66E ; ALetter # Lo CYRILLIC LETTER MULTIOCULAR O A67F ; ALetter # Lm CYRILLIC PAYEROK A680..A69B ; ALetter # L& [28] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER CROSSED O A69C..A69D ; ALetter # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN A6A0..A6E5 ; ALetter # Lo [70] BAMUM LETTER A..BAMUM LETTER KI A6E6..A6EF ; ALetter # Nl [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM A717..A71F ; ALetter # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK A722..A76F ; ALetter # L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON A770 ; ALetter # Lm MODIFIER LETTER US A771..A787 ; ALetter # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T A788 ; ALetter # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; ALetter # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; ALetter # Lo LATIN LETTER SINOLOGICAL DOT A790..A7AE ; ALetter # L& [31] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER SMALL CAPITAL I A7B0..A7B7 ; ALetter # L& [8] LATIN CAPITAL LETTER TURNED K..LATIN SMALL LETTER OMEGA A7F7 ; ALetter # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; ALetter # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A7FA ; ALetter # L& LATIN LETTER SMALL CAPITAL TURNED M A7FB..A801 ; ALetter # Lo [7] LATIN EPIGRAPHIC LETTER REVERSED F..SYLOTI NAGRI LETTER I A803..A805 ; ALetter # Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O A807..A80A ; ALetter # Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO A80C..A822 ; ALetter # Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO A840..A873 ; ALetter # Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU A882..A8B3 ; ALetter # Lo [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA A8F2..A8F7 ; ALetter # Lo [6] DEVANAGARI SIGN SPACING CANDRABINDU..DEVANAGARI SIGN CANDRABINDU AVAGRAHA A8FB ; ALetter # Lo DEVANAGARI HEADSTROKE A8FD ; ALetter # Lo DEVANAGARI JAIN OM A90A..A925 ; ALetter # Lo [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO A930..A946 ; ALetter # Lo [23] REJANG LETTER KA..REJANG LETTER A A960..A97C ; ALetter # Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH A984..A9B2 ; ALetter # Lo [47] JAVANESE LETTER A..JAVANESE LETTER HA A9CF ; ALetter # Lm JAVANESE PANGRANGKEP AA00..AA28 ; ALetter # Lo [41] CHAM LETTER A..CHAM LETTER HA AA40..AA42 ; ALetter # Lo [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG AA44..AA4B ; ALetter # Lo [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS AAE0..AAEA ; ALetter # Lo [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA AAF2 ; ALetter # Lo MEETEI MAYEK ANJI AAF3..AAF4 ; ALetter # Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK AB01..AB06 ; ALetter # Lo [6] ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO AB09..AB0E ; ALetter # Lo [6] ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO AB11..AB16 ; ALetter # Lo [6] ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO AB20..AB26 ; ALetter # Lo [7] ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO AB28..AB2E ; ALetter # Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO AB30..AB5A ; ALetter # L& [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG AB5C..AB5F ; ALetter # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK AB60..AB65 ; ALetter # L& [6] LATIN SMALL LETTER SAKHA YAT..GREEK LETTER SMALL CAPITAL OMEGA AB70..ABBF ; ALetter # L& [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA ABC0..ABE2 ; ALetter # Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM AC00..D7A3 ; ALetter # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH D7B0..D7C6 ; ALetter # Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E D7CB..D7FB ; ALetter # Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH FB00..FB06 ; ALetter # L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST FB13..FB17 ; ALetter # L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH FB50..FBB1 ; ALetter # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM FBD3..FD3D ; ALetter # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM FD50..FD8F ; ALetter # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM FD92..FDC7 ; ALetter # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM FDF0..FDFB ; ALetter # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU FE70..FE74 ; ALetter # Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM FE76..FEFC ; ALetter # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM FF21..FF3A ; ALetter # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z FF41..FF5A ; ALetter # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z FFA0..FFBE ; ALetter # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH FFC2..FFC7 ; ALetter # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E FFCA..FFCF ; ALetter # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE FFD2..FFD7 ; ALetter # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I 10000..1000B ; ALetter # Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE 1000D..10026 ; ALetter # Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO 10028..1003A ; ALetter # Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO 1003C..1003D ; ALetter # Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE 1003F..1004D ; ALetter # Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO 10050..1005D ; ALetter # Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089 10080..100FA ; ALetter # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305 10140..10174 ; ALetter # Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS 10280..1029C ; ALetter # Lo [29] LYCIAN LETTER A..LYCIAN LETTER X 102A0..102D0 ; ALetter # Lo [49] CARIAN LETTER A..CARIAN LETTER UUU3 10300..1031F ; ALetter # Lo [32] OLD ITALIC LETTER A..OLD ITALIC LETTER ESS 10330..10340 ; ALetter # Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA 10341 ; ALetter # Nl GOTHIC LETTER NINETY 10342..10349 ; ALetter # Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL 1034A ; ALetter # Nl GOTHIC LETTER NINE HUNDRED 10350..10375 ; ALetter # Lo [38] OLD PERMIC LETTER AN..OLD PERMIC LETTER IA 10380..1039D ; ALetter # Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU 103A0..103C3 ; ALetter # Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA 103C8..103CF ; ALetter # Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH 103D1..103D5 ; ALetter # Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED 10400..1044F ; ALetter # L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW 10450..1049D ; ALetter # Lo [78] SHAVIAN LETTER PEEP..OSMANYA LETTER OO 104B0..104D3 ; ALetter # L& [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA 104D8..104FB ; ALetter # L& [36] OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA 10500..10527 ; ALetter # Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE 10530..10563 ; ALetter # Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW 10600..10736 ; ALetter # Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664 10740..10755 ; ALetter # Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE 10760..10767 ; ALetter # Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807 10800..10805 ; ALetter # Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA 10808 ; ALetter # Lo CYPRIOT SYLLABLE JO 1080A..10835 ; ALetter # Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO 10837..10838 ; ALetter # Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE 1083C ; ALetter # Lo CYPRIOT SYLLABLE ZA 1083F..10855 ; ALetter # Lo [23] CYPRIOT SYLLABLE ZO..IMPERIAL ARAMAIC LETTER TAW 10860..10876 ; ALetter # Lo [23] PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW 10880..1089E ; ALetter # Lo [31] NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW 108E0..108F2 ; ALetter # Lo [19] HATRAN LETTER ALEPH..HATRAN LETTER QOPH 108F4..108F5 ; ALetter # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; ALetter # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; ALetter # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C 10980..109B7 ; ALetter # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; ALetter # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; ALetter # Lo KHAROSHTHI LETTER A 10A10..10A13 ; ALetter # Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA 10A15..10A17 ; ALetter # Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA 10A19..10A33 ; ALetter # Lo [27] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA 10A60..10A7C ; ALetter # Lo [29] OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH 10A80..10A9C ; ALetter # Lo [29] OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH 10AC0..10AC7 ; ALetter # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW 10AC9..10AE4 ; ALetter # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW 10B00..10B35 ; ALetter # Lo [54] AVESTAN LETTER A..AVESTAN LETTER HE 10B40..10B55 ; ALetter # Lo [22] INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW 10B60..10B72 ; ALetter # Lo [19] INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW 10B80..10B91 ; ALetter # Lo [18] PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW 10C00..10C48 ; ALetter # Lo [73] OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH 10C80..10CB2 ; ALetter # L& [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US 10CC0..10CF2 ; ALetter # L& [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US 11003..11037 ; ALetter # Lo [53] BRAHMI SIGN JIHVAMULIYA..BRAHMI LETTER OLD TAMIL NNNA 11083..110AF ; ALetter # Lo [45] KAITHI LETTER A..KAITHI LETTER HA 110D0..110E8 ; ALetter # Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE 11103..11126 ; ALetter # Lo [36] CHAKMA LETTER AA..CHAKMA LETTER HAA 11150..11172 ; ALetter # Lo [35] MAHAJANI LETTER A..MAHAJANI LETTER RRA 11176 ; ALetter # Lo MAHAJANI LIGATURE SHRI 11183..111B2 ; ALetter # Lo [48] SHARADA LETTER A..SHARADA LETTER HA 111C1..111C4 ; ALetter # Lo [4] SHARADA SIGN AVAGRAHA..SHARADA OM 111DA ; ALetter # Lo SHARADA EKAM 111DC ; ALetter # Lo SHARADA HEADSTROKE 11200..11211 ; ALetter # Lo [18] KHOJKI LETTER A..KHOJKI LETTER JJA 11213..1122B ; ALetter # Lo [25] KHOJKI LETTER NYA..KHOJKI LETTER LLA 11280..11286 ; ALetter # Lo [7] MULTANI LETTER A..MULTANI LETTER GA 11288 ; ALetter # Lo MULTANI LETTER GHA 1128A..1128D ; ALetter # Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA 1128F..1129D ; ALetter # Lo [15] MULTANI LETTER NYA..MULTANI LETTER BA 1129F..112A8 ; ALetter # Lo [10] MULTANI LETTER BHA..MULTANI LETTER RHA 112B0..112DE ; ALetter # Lo [47] KHUDAWADI LETTER A..KHUDAWADI LETTER HA 11305..1130C ; ALetter # Lo [8] GRANTHA LETTER A..GRANTHA LETTER VOCALIC L 1130F..11310 ; ALetter # Lo [2] GRANTHA LETTER EE..GRANTHA LETTER AI 11313..11328 ; ALetter # Lo [22] GRANTHA LETTER OO..GRANTHA LETTER NA 1132A..11330 ; ALetter # Lo [7] GRANTHA LETTER PA..GRANTHA LETTER RA 11332..11333 ; ALetter # Lo [2] GRANTHA LETTER LA..GRANTHA LETTER LLA 11335..11339 ; ALetter # Lo [5] GRANTHA LETTER VA..GRANTHA LETTER HA 1133D ; ALetter # Lo GRANTHA SIGN AVAGRAHA 11350 ; ALetter # Lo GRANTHA OM 1135D..11361 ; ALetter # Lo [5] GRANTHA SIGN PLUTA..GRANTHA LETTER VOCALIC LL 11400..11434 ; ALetter # Lo [53] NEWA LETTER A..NEWA LETTER HA 11447..1144A ; ALetter # Lo [4] NEWA SIGN AVAGRAHA..NEWA SIDDHI 11480..114AF ; ALetter # Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA 114C4..114C5 ; ALetter # Lo [2] TIRHUTA SIGN AVAGRAHA..TIRHUTA GVANG 114C7 ; ALetter # Lo TIRHUTA OM 11580..115AE ; ALetter # Lo [47] SIDDHAM LETTER A..SIDDHAM LETTER HA 115D8..115DB ; ALetter # Lo [4] SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM LETTER ALTERNATE U 11600..1162F ; ALetter # Lo [48] MODI LETTER A..MODI LETTER LLA 11644 ; ALetter # Lo MODI SIGN HUVA 11680..116AA ; ALetter # Lo [43] TAKRI LETTER A..TAKRI LETTER RRA 118A0..118DF ; ALetter # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 118FF ; ALetter # Lo WARANG CITI OM 11AC0..11AF8 ; ALetter # Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL 11C00..11C08 ; ALetter # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L 11C0A..11C2E ; ALetter # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA 11C40 ; ALetter # Lo BHAIKSUKI SIGN AVAGRAHA 11C72..11C8F ; ALetter # Lo [30] MARCHEN LETTER KA..MARCHEN LETTER A 12000..12399 ; ALetter # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U 12400..1246E ; ALetter # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM 12480..12543 ; ALetter # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU 13000..1342E ; ALetter # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032 14400..14646 ; ALetter # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530 16800..16A38 ; ALetter # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ 16A40..16A5E ; ALetter # Lo [31] MRO LETTER TA..MRO LETTER TEK 16AD0..16AED ; ALetter # Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I 16B00..16B2F ; ALetter # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU 16B40..16B43 ; ALetter # Lm [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM 16B63..16B77 ; ALetter # Lo [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS 16B7D..16B8F ; ALetter # Lo [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ 16F00..16F44 ; ALetter # Lo [69] MIAO LETTER PA..MIAO LETTER HHA 16F50 ; ALetter # Lo MIAO LETTER NASALIZATION 16F93..16F9F ; ALetter # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 16FE0 ; ALetter # Lm TANGUT ITERATION MARK 1BC00..1BC6A ; ALetter # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M 1BC70..1BC7C ; ALetter # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK 1BC80..1BC88 ; ALetter # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL 1BC90..1BC99 ; ALetter # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW 1D400..1D454 ; ALetter # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G 1D456..1D49C ; ALetter # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A 1D49E..1D49F ; ALetter # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D 1D4A2 ; ALetter # L& MATHEMATICAL SCRIPT CAPITAL G 1D4A5..1D4A6 ; ALetter # L& [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K 1D4A9..1D4AC ; ALetter # L& [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q 1D4AE..1D4B9 ; ALetter # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D 1D4BB ; ALetter # L& MATHEMATICAL SCRIPT SMALL F 1D4BD..1D4C3 ; ALetter # L& [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N 1D4C5..1D505 ; ALetter # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B 1D507..1D50A ; ALetter # L& [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G 1D50D..1D514 ; ALetter # L& [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q 1D516..1D51C ; ALetter # L& [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y 1D51E..1D539 ; ALetter # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B 1D53B..1D53E ; ALetter # L& [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G 1D540..1D544 ; ALetter # L& [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M 1D546 ; ALetter # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O 1D54A..1D550 ; ALetter # L& [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y 1D552..1D6A5 ; ALetter # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J 1D6A8..1D6C0 ; ALetter # L& [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA 1D6C2..1D6DA ; ALetter # L& [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA 1D6DC..1D6FA ; ALetter # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA 1D6FC..1D714 ; ALetter # L& [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA 1D716..1D734 ; ALetter # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA 1D736..1D74E ; ALetter # L& [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA 1D750..1D76E ; ALetter # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA 1D770..1D788 ; ALetter # L& [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA 1D78A..1D7A8 ; ALetter # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA 1D7AA..1D7C2 ; ALetter # L& [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA 1D7C4..1D7CB ; ALetter # L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA 1E800..1E8C4 ; ALetter # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON 1E900..1E943 ; ALetter # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA 1EE00..1EE03 ; ALetter # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL 1EE05..1EE1F ; ALetter # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF 1EE21..1EE22 ; ALetter # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM 1EE24 ; ALetter # Lo ARABIC MATHEMATICAL INITIAL HEH 1EE27 ; ALetter # Lo ARABIC MATHEMATICAL INITIAL HAH 1EE29..1EE32 ; ALetter # Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF 1EE34..1EE37 ; ALetter # Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH 1EE39 ; ALetter # Lo ARABIC MATHEMATICAL INITIAL DAD 1EE3B ; ALetter # Lo ARABIC MATHEMATICAL INITIAL GHAIN 1EE42 ; ALetter # Lo ARABIC MATHEMATICAL TAILED JEEM 1EE47 ; ALetter # Lo ARABIC MATHEMATICAL TAILED HAH 1EE49 ; ALetter # Lo ARABIC MATHEMATICAL TAILED YEH 1EE4B ; ALetter # Lo ARABIC MATHEMATICAL TAILED LAM 1EE4D..1EE4F ; ALetter # Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN 1EE51..1EE52 ; ALetter # Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF 1EE54 ; ALetter # Lo ARABIC MATHEMATICAL TAILED SHEEN 1EE57 ; ALetter # Lo ARABIC MATHEMATICAL TAILED KHAH 1EE59 ; ALetter # Lo ARABIC MATHEMATICAL TAILED DAD 1EE5B ; ALetter # Lo ARABIC MATHEMATICAL TAILED GHAIN 1EE5D ; ALetter # Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON 1EE5F ; ALetter # Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF 1EE61..1EE62 ; ALetter # Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM 1EE64 ; ALetter # Lo ARABIC MATHEMATICAL STRETCHED HEH 1EE67..1EE6A ; ALetter # Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF 1EE6C..1EE72 ; ALetter # Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF 1EE74..1EE77 ; ALetter # Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH 1EE79..1EE7C ; ALetter # Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH 1EE7E ; ALetter # Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH 1EE80..1EE89 ; ALetter # Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH 1EE8B..1EE9B ; ALetter # Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN 1EEA1..1EEA3 ; ALetter # Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL 1EEA5..1EEA9 ; ALetter # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB ; ALetter # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1F130..1F149 ; ALetter # So [26] SQUARED LATIN CAPITAL LETTER A..SQUARED LATIN CAPITAL LETTER Z 1F150..1F169 ; ALetter # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; ALetter # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z # Total code points: 27992 # ================================================ 003A ; MidLetter # Po COLON 00B7 ; MidLetter # Po MIDDLE DOT 02D7 ; MidLetter # Sk MODIFIER LETTER MINUS SIGN 0387 ; MidLetter # Po GREEK ANO TELEIA 05F4 ; MidLetter # Po HEBREW PUNCTUATION GERSHAYIM 2027 ; MidLetter # Po HYPHENATION POINT FE13 ; MidLetter # Po PRESENTATION FORM FOR VERTICAL COLON FE55 ; MidLetter # Po SMALL COLON FF1A ; MidLetter # Po FULLWIDTH COLON # Total code points: 9 # ================================================ 002C ; MidNum # Po COMMA 003B ; MidNum # Po SEMICOLON 037E ; MidNum # Po GREEK QUESTION MARK 0589 ; MidNum # Po ARMENIAN FULL STOP 060C..060D ; MidNum # Po [2] ARABIC COMMA..ARABIC DATE SEPARATOR 066C ; MidNum # Po ARABIC THOUSANDS SEPARATOR 07F8 ; MidNum # Po NKO COMMA 2044 ; MidNum # Sm FRACTION SLASH FE10 ; MidNum # Po PRESENTATION FORM FOR VERTICAL COMMA FE14 ; MidNum # Po PRESENTATION FORM FOR VERTICAL SEMICOLON FE50 ; MidNum # Po SMALL COMMA FE54 ; MidNum # Po SMALL SEMICOLON FF0C ; MidNum # Po FULLWIDTH COMMA FF1B ; MidNum # Po FULLWIDTH SEMICOLON # Total code points: 15 # ================================================ 002E ; MidNumLet # Po FULL STOP 2018 ; MidNumLet # Pi LEFT SINGLE QUOTATION MARK 2019 ; MidNumLet # Pf RIGHT SINGLE QUOTATION MARK 2024 ; MidNumLet # Po ONE DOT LEADER FE52 ; MidNumLet # Po SMALL FULL STOP FF07 ; MidNumLet # Po FULLWIDTH APOSTROPHE FF0E ; MidNumLet # Po FULLWIDTH FULL STOP # Total code points: 7 # ================================================ 0030..0039 ; Numeric # Nd [10] DIGIT ZERO..DIGIT NINE 0660..0669 ; Numeric # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE 066B ; Numeric # Po ARABIC DECIMAL SEPARATOR 06F0..06F9 ; Numeric # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE 07C0..07C9 ; Numeric # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE 0966..096F ; Numeric # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE 09E6..09EF ; Numeric # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE 0A66..0A6F ; Numeric # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE 0AE6..0AEF ; Numeric # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE 0B66..0B6F ; Numeric # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE 0BE6..0BEF ; Numeric # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE 0C66..0C6F ; Numeric # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE 0CE6..0CEF ; Numeric # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE 0D66..0D6F ; Numeric # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE 0DE6..0DEF ; Numeric # Nd [10] SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE 0E50..0E59 ; Numeric # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE 0ED0..0ED9 ; Numeric # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE 0F20..0F29 ; Numeric # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE 1040..1049 ; Numeric # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE 1090..1099 ; Numeric # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE 17E0..17E9 ; Numeric # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE 1810..1819 ; Numeric # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE 1946..194F ; Numeric # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE 19D0..19D9 ; Numeric # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE 1A80..1A89 ; Numeric # Nd [10] TAI THAM HORA DIGIT ZERO..TAI THAM HORA DIGIT NINE 1A90..1A99 ; Numeric # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE 1B50..1B59 ; Numeric # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE 1BB0..1BB9 ; Numeric # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE 1C40..1C49 ; Numeric # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE 1C50..1C59 ; Numeric # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE A620..A629 ; Numeric # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE A8D0..A8D9 ; Numeric # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE A900..A909 ; Numeric # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE A9D0..A9D9 ; Numeric # Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE A9F0..A9F9 ; Numeric # Nd [10] MYANMAR TAI LAING DIGIT ZERO..MYANMAR TAI LAING DIGIT NINE AA50..AA59 ; Numeric # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE ABF0..ABF9 ; Numeric # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE 104A0..104A9 ; Numeric # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE 11066..1106F ; Numeric # Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE 110F0..110F9 ; Numeric # Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE 11136..1113F ; Numeric # Nd [10] CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE 111D0..111D9 ; Numeric # Nd [10] SHARADA DIGIT ZERO..SHARADA DIGIT NINE 112F0..112F9 ; Numeric # Nd [10] KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE 11450..11459 ; Numeric # Nd [10] NEWA DIGIT ZERO..NEWA DIGIT NINE 114D0..114D9 ; Numeric # Nd [10] TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE 11650..11659 ; Numeric # Nd [10] MODI DIGIT ZERO..MODI DIGIT NINE 116C0..116C9 ; Numeric # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE 11730..11739 ; Numeric # Nd [10] AHOM DIGIT ZERO..AHOM DIGIT NINE 118E0..118E9 ; Numeric # Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE 11C50..11C59 ; Numeric # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE 16A60..16A69 ; Numeric # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE 16B50..16B59 ; Numeric # Nd [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE 1D7CE..1D7FF ; Numeric # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE 1E950..1E959 ; Numeric # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE # Total code points: 571 # ================================================ 005F ; ExtendNumLet # Pc LOW LINE 202F ; ExtendNumLet # Zs NARROW NO-BREAK SPACE 203F..2040 ; ExtendNumLet # Pc [2] UNDERTIE..CHARACTER TIE 2054 ; ExtendNumLet # Pc INVERTED UNDERTIE FE33..FE34 ; ExtendNumLet # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE FE4D..FE4F ; ExtendNumLet # Pc [3] DASHED LOW LINE..WAVY LOW LINE FF3F ; ExtendNumLet # Pc FULLWIDTH LOW LINE # Total code points: 11 # ================================================ 261D ; E_Base # So WHITE UP POINTING INDEX 26F9 ; E_Base # So PERSON WITH BALL 270A..270D ; E_Base # So [4] RAISED FIST..WRITING HAND 1F385 ; E_Base # So FATHER CHRISTMAS 1F3C3..1F3C4 ; E_Base # So [2] RUNNER..SURFER 1F3CA..1F3CB ; E_Base # So [2] SWIMMER..WEIGHT LIFTER 1F442..1F443 ; E_Base # So [2] EAR..NOSE 1F446..1F450 ; E_Base # So [11] WHITE UP POINTING BACKHAND INDEX..OPEN HANDS SIGN 1F46E ; E_Base # So POLICE OFFICER 1F470..1F478 ; E_Base # So [9] BRIDE WITH VEIL..PRINCESS 1F47C ; E_Base # So BABY ANGEL 1F481..1F483 ; E_Base # So [3] INFORMATION DESK PERSON..DANCER 1F485..1F487 ; E_Base # So [3] NAIL POLISH..HAIRCUT 1F4AA ; E_Base # So FLEXED BICEPS 1F575 ; E_Base # So SLEUTH OR SPY 1F57A ; E_Base # So MAN DANCING 1F590 ; E_Base # So RAISED HAND WITH FINGERS SPLAYED 1F595..1F596 ; E_Base # So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS 1F645..1F647 ; E_Base # So [3] FACE WITH NO GOOD GESTURE..PERSON BOWING DEEPLY 1F64B..1F64F ; E_Base # So [5] HAPPY PERSON RAISING ONE HAND..PERSON WITH FOLDED HANDS 1F6A3 ; E_Base # So ROWBOAT 1F6B4..1F6B6 ; E_Base # So [3] BICYCLIST..PEDESTRIAN 1F6C0 ; E_Base # So BATH 1F918..1F91E ; E_Base # So [7] SIGN OF THE HORNS..HAND WITH INDEX AND MIDDLE FINGERS CROSSED 1F926 ; E_Base # So FACE PALM 1F930 ; E_Base # So PREGNANT WOMAN 1F933..1F939 ; E_Base # So [7] SELFIE..JUGGLING 1F93C..1F93E ; E_Base # So [3] WRESTLERS..HANDBALL # Total code points: 79 # ================================================ 1F3FB..1F3FF ; E_Modifier # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 # Total code points: 5 # ================================================ 200D ; ZWJ # Cf ZERO WIDTH JOINER # Total code points: 1 # ================================================ 2764 ; Glue_After_Zwj # So HEAVY BLACK HEART 1F48B ; Glue_After_Zwj # So KISS MARK 1F5E8 ; Glue_After_Zwj # So LEFT SPEECH BUBBLE # Total code points: 3 # ================================================ 1F466..1F469 ; E_Base_GAZ # So [4] BOY..WOMAN # Total code points: 4 # EOF dovecot-2.2.33.2/src/lib-fts/Makefile.in0000644000175000017500000010361613172375573014545 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_3) subdir = src/lib-fts ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_stopwords_DATA) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(stopwordsdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am_libdovecot_fts_la_OBJECTS = libdovecot_fts_la_OBJECTS = $(am_libdovecot_fts_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_fts_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_fts_la_LDFLAGS) $(LDFLAGS) \ -o $@ am__DEPENDENCIES_1 = @BUILD_FTS_EXTTEXTCAT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @BUILD_LIBICU_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) libfts_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_3) am__libfts_la_SOURCES_DIST = fts-filter.c fts-filter-contractions.c \ fts-filter-common.c fts-filter-english-possessive.c \ fts-filter-lowercase.c fts-filter-normalizer-icu.c \ fts-filter-stopwords.c fts-filter-stemmer-snowball.c \ fts-language.c fts-library.c fts-tokenizer.c \ fts-tokenizer-address.c fts-tokenizer-common.c \ fts-tokenizer-generic.c fts-icu.c @BUILD_LIBICU_TRUE@am__objects_1 = fts-icu.lo am_libfts_la_OBJECTS = fts-filter.lo fts-filter-contractions.lo \ fts-filter-common.lo fts-filter-english-possessive.lo \ fts-filter-lowercase.lo fts-filter-normalizer-icu.lo \ fts-filter-stopwords.lo fts-filter-stemmer-snowball.lo \ fts-language.lo fts-library.lo fts-tokenizer.lo \ fts-tokenizer-address.lo fts-tokenizer-common.lo \ fts-tokenizer-generic.lo $(am__objects_1) libfts_la_OBJECTS = $(am_libfts_la_OBJECTS) @BUILD_LIBICU_TRUE@am__EXEEXT_1 = test-fts-icu$(EXEEXT) @BUILD_FTS_EXTTEXTCAT_TRUE@am__EXEEXT_2 = test-fts-language$(EXEEXT) am__EXEEXT_3 = $(am__EXEEXT_1) $(am__EXEEXT_2) \ test-fts-filter$(EXEEXT) test-fts-tokenizer$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_fts_filter_OBJECTS = test-fts-filter.$(OBJEXT) test_fts_filter_OBJECTS = $(am_test_fts_filter_OBJECTS) am_test_fts_icu_OBJECTS = test-fts-icu.$(OBJEXT) test_fts_icu_OBJECTS = $(am_test_fts_icu_OBJECTS) am__test_fts_language_SOURCES_DIST = test-fts-language.c @BUILD_FTS_EXTTEXTCAT_TRUE@am_test_fts_language_OBJECTS = \ @BUILD_FTS_EXTTEXTCAT_TRUE@ test-fts-language.$(OBJEXT) test_fts_language_OBJECTS = $(am_test_fts_language_OBJECTS) am_test_fts_tokenizer_OBJECTS = test-fts-tokenizer.$(OBJEXT) test_fts_tokenizer_OBJECTS = $(am_test_fts_tokenizer_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_fts_la_SOURCES) $(libfts_la_SOURCES) \ $(test_fts_filter_SOURCES) $(test_fts_icu_SOURCES) \ $(test_fts_language_SOURCES) $(test_fts_tokenizer_SOURCES) DIST_SOURCES = $(libdovecot_fts_la_SOURCES) \ $(am__libfts_la_SOURCES_DIST) $(test_fts_filter_SOURCES) \ $(test_fts_icu_SOURCES) $(am__test_fts_language_SOURCES_DIST) \ $(test_fts_tokenizer_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(dist_stopwords_DATA) HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libfts.la # I$(top_srcdir)/src/lib-fts needed to include # word-break-data.c and word-boundary-data.c # in fts-tokenizer-generic.c AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-fts \ $(LIBEXTTEXTCAT_CFLAGS) \ $(LIBICU_CFLAGS) \ -DUDHRDIR=\""$(top_srcdir)/src/lib-fts"\" \ -DDATADIR=\"$(pkgdatadir)\" \ -DTEST_STOPWORDS_DIR=\""$(top_srcdir)/src/lib-fts/stopwords"\" stopwordsdir = $(datadir)/${PACKAGE_TARNAME}/stopwords dist_stopwords_DATA = \ stopwords/stopwords_da.txt \ stopwords/stopwords_de.txt \ stopwords/stopwords_en.txt \ stopwords/stopwords_es.txt \ stopwords/stopwords_fi.txt \ stopwords/stopwords_fr.txt \ stopwords/stopwords_it.txt \ stopwords/stopwords_nl.txt \ stopwords/stopwords_no.txt \ stopwords/stopwords_pt.txt \ stopwords/stopwords_ro.txt \ stopwords/stopwords_ru.txt \ stopwords/stopwords_sv.txt BUILT_SOURCES = word-boundary-data.c word-break-data.c EXTRA_DIST = \ udhr_fra.txt \ PropList.txt \ word-properties.pl \ WordBreakProperty.txt \ word-boundary-data.c \ word-break-data.c \ stopwords/stopwords_malformed.txt @BUILD_FTS_STEMMER_TRUE@STEMMER_LIBS = -lstemmer @BUILD_FTS_EXTTEXTCAT_FALSE@@BUILD_FTS_TEXTCAT_TRUE@TEXTCAT_LIBS = -ltextcat @BUILD_FTS_EXTTEXTCAT_TRUE@TEXTCAT_LIBS = $(LIBEXTTEXTCAT_LIBS) @BUILD_LIBICU_TRUE@ICU_SOURCES = fts-icu.c @BUILD_LIBICU_TRUE@NORMALIZER_LIBS = $(LIBICU_LIBS) @BUILD_LIBICU_TRUE@ICU_TESTS = test-fts-icu libfts_la_LIBADD = \ $(STEMMER_LIBS) \ $(TEXTCAT_LIBS) \ $(NORMALIZER_LIBS) libfts_la_SOURCES = \ fts-filter.c \ fts-filter-contractions.c \ fts-filter-common.c \ fts-filter-english-possessive.c \ fts-filter-lowercase.c \ fts-filter-normalizer-icu.c \ fts-filter-stopwords.c \ fts-filter-stemmer-snowball.c \ fts-language.c \ fts-library.c \ fts-tokenizer.c \ fts-tokenizer-address.c \ fts-tokenizer-common.c \ fts-tokenizer-generic.c \ $(ICU_SOURCES) headers = \ fts-common.h \ fts-filter.h \ fts-filter-common.h \ fts-filter-private.h \ fts-icu.h \ fts-language.h \ fts-library.h \ fts-tokenizer.h \ fts-tokenizer-common.h \ fts-tokenizer-private.h \ fts-tokenizer-generic-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) deps = ../lib-dovecot/libdovecot.la pkglib_LTLIBRARIES = libdovecot-fts.la libdovecot_fts_la_SOURCES = libdovecot_fts_la_LIBADD = libfts.la $(deps) libdovecot_fts_la_DEPENDENCIES = libfts.la $(deps) libdovecot_fts_la_LDFLAGS = -export-dynamic test_programs = \ $(ICU_TESTS) \ $(TEST_FTS_LANGUAGE) \ test-fts-filter \ test-fts-tokenizer test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_fts_icu_SOURCES = test-fts-icu.c test_fts_icu_LDADD = fts-icu.lo $(LIBICU_LIBS) $(test_libs) test_fts_icu_DEPENDENCIES = fts-icu.lo $(test_deps) test_fts_filter_SOURCES = test-fts-filter.c test_fts_filter_LDADD = libfts.la $(test_libs) test_fts_filter_DEPENDENCIES = libfts.la $(test_deps) @BUILD_FTS_EXTTEXTCAT_TRUE@TEST_FTS_LANGUAGE = test-fts-language @BUILD_FTS_EXTTEXTCAT_TRUE@test_fts_language_SOURCES = test-fts-language.c @BUILD_FTS_EXTTEXTCAT_TRUE@test_fts_language_LDADD = fts-language.lo $(test_libs) $(TEXTCAT_LIBS) @BUILD_FTS_EXTTEXTCAT_TRUE@test_fts_language_DEPENDENCIES = $(test_deps) test_fts_tokenizer_SOURCES = test-fts-tokenizer.c test_fts_tokenizer_LDADD = fts-tokenizer.lo fts-tokenizer-generic.lo fts-tokenizer-address.lo fts-tokenizer-common.lo ../lib-mail/libmail.la $(test_libs) test_fts_tokenizer_DEPENDENCIES = ../lib-mail/libmail.la $(test_deps) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-fts/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-fts/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-fts.la: $(libdovecot_fts_la_OBJECTS) $(libdovecot_fts_la_DEPENDENCIES) $(EXTRA_libdovecot_fts_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_fts_la_LINK) -rpath $(pkglibdir) $(libdovecot_fts_la_OBJECTS) $(libdovecot_fts_la_LIBADD) $(LIBS) libfts.la: $(libfts_la_OBJECTS) $(libfts_la_DEPENDENCIES) $(EXTRA_libfts_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libfts_la_OBJECTS) $(libfts_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-fts-filter$(EXEEXT): $(test_fts_filter_OBJECTS) $(test_fts_filter_DEPENDENCIES) $(EXTRA_test_fts_filter_DEPENDENCIES) @rm -f test-fts-filter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_fts_filter_OBJECTS) $(test_fts_filter_LDADD) $(LIBS) test-fts-icu$(EXEEXT): $(test_fts_icu_OBJECTS) $(test_fts_icu_DEPENDENCIES) $(EXTRA_test_fts_icu_DEPENDENCIES) @rm -f test-fts-icu$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_fts_icu_OBJECTS) $(test_fts_icu_LDADD) $(LIBS) test-fts-language$(EXEEXT): $(test_fts_language_OBJECTS) $(test_fts_language_DEPENDENCIES) $(EXTRA_test_fts_language_DEPENDENCIES) @rm -f test-fts-language$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_fts_language_OBJECTS) $(test_fts_language_LDADD) $(LIBS) test-fts-tokenizer$(EXEEXT): $(test_fts_tokenizer_OBJECTS) $(test_fts_tokenizer_DEPENDENCIES) $(EXTRA_test_fts_tokenizer_DEPENDENCIES) @rm -f test-fts-tokenizer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_fts_tokenizer_OBJECTS) $(test_fts_tokenizer_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-contractions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-english-possessive.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-lowercase.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-normalizer-icu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-stemmer-snowball.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter-stopwords.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-filter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-icu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-language.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-library.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-tokenizer-address.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-tokenizer-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-tokenizer-generic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-tokenizer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fts-filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fts-icu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fts-language.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fts-tokenizer.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_stopwordsDATA: $(dist_stopwords_DATA) @$(NORMAL_INSTALL) @list='$(dist_stopwords_DATA)'; test -n "$(stopwordsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(stopwordsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(stopwordsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(stopwordsdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(stopwordsdir)" || exit $$?; \ done uninstall-dist_stopwordsDATA: @$(NORMAL_UNINSTALL) @list='$(dist_stopwords_DATA)'; test -n "$(stopwordsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(stopwordsdir)'; $(am__uninstall_files_from_dir) install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(stopwordsdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dist_stopwordsDATA install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_stopwordsDATA uninstall-pkginc_libHEADERS \ uninstall-pkglibLTLIBRARIES .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_stopwordsDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-dist_stopwordsDATA uninstall-pkginc_libHEADERS \ uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile WordBreakProperty.txt: test -f WordBreakProperty.txt || wget https://dovecot.org/res/WordBreakProperty.txt $(srcdir)/word-boundary-data.c: word-properties.pl WordBreakProperty.txt perl word-properties.pl boundaries WordBreakProperty.txt > $@ PropList.txt: test -f PropList.txt || wget https://dovecot.org/res/PropList.txt $(srcdir)/word-break-data.c: word-properties.pl PropList.txt perl word-properties.pl breaks PropList.txt > $@ check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-fts/test-fts-tokenizer.c0000644000175000017500000004462213165463624016423 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "unichar.h" #include "str.h" #include "test-common.h" #include "fts-tokenizer.h" #include "fts-tokenizer-common.h" #include "fts-tokenizer-private.h" #include "fts-tokenizer-generic-private.h" /*there should be a trailing space ' ' at the end of each string except the last one*/ #define TEST_INPUT_ADDRESS \ "@invalid invalid@ Abc Dfg , " \ "Bar Baz " \ "Foo Bar (comment)foo.bar@host.example.org " \ "foo, foo@domain " \ "abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz@abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.tld " \ "trailing, period@blue.com. " \ "multi-trialing, mul@trail.com..... " \ "m@s " \ "hypen@hypen-hypen.com " \ "hypen@hypen-hypen-sick.com.-" static const char *test_inputs[] = { /* generic things and word truncation: */ "hello world\r\n\nAnd there\twas: text galor\xC3\xA9\xE2\x80\xA7 " "abc@example.com, " "Bar Baz , " "foo@domain " "1234567890123456789012345678\xC3\xA4," "12345678901234567890123456789\xC3\xA4," "123456789012345678901234567890\xC3\xA4," "and longlonglongabcdefghijklmnopqrstuvwxyz more.\n\n " "(\"Hello world\")3.14 3,14 last", "1.", "' ' '' ''' 'quoted text' 'word' 'hlo words' you're bad'''word '''pre post'''", "'1234567890123456789012345678\xC3\xA4," "123456789012345678901234567x'\xC3\xA4," "1234567890123456789012345678x're," "1234567890123456789012345678x'," "1234567890123456789012345678x''," "12345678901234567890123456789x'," "12345678901234567890123456789x''," "123456789012345678901234567890x'," "123456789012345678901234567890x''," /* \xe28099 = U+2019 is a smart quote, sometimes used as an apostrophe */ "\xE2\x80\x99 \xE2\x80\x99 \xE2\x80\x99\xE2\x80\x99 \xE2\x80\x99\xE2\x80\x99\xE2\x80\x99 \xE2\x80\x99quoted text\xE2\x80\x99\xE2\x80\x99word\xE2\x80\x99 \xE2\x80\x99hlo words\xE2\x80\x99 you\xE2\x80\x99re78901234567890123456789012 bad\xE2\x80\x99\xE2\x80\x99\xE2\x80\x99word\xE2\x80\x99\xE2\x80\x99\xE2\x80\x99pre post\xE2\x80\x99\xE2\x80\x99\xE2\x80\x99", "you\xE2\x80\x99re\xE2\x80\x99xyz", /* whitespace: with Unicode(utf8) U+FF01(ef bc 81)(U+2000(e2 80 80) and U+205A(e2 81 9a) and U+205F(e2 81 9f) */ "hello\xEF\xBC\x81world\r\nAnd\xE2\x80\x80there\twas: text " "galore\xE2\x81\x9F""and\xE2\x81\x9Amore.\n\n", /* TR29 MinNumLet U+FF0E at end: u+FF0E is EF BC 8E */ "hello world\xEF\xBC\x8E", /* TR29 WB5a */ "l\xE2\x80\x99homme l\xE2\x80\x99humanit\xC3\xA9 d\xE2\x80\x99immixtions qu\xE2\x80\x99il aujourd'hui que'euq" }; static void test_fts_tokenizer_find(void) { test_begin("fts tokenizer find"); test_assert(fts_tokenizer_find("email-address") == fts_tokenizer_email_address); test_assert(fts_tokenizer_find("generic") == fts_tokenizer_generic); test_end(); } static unsigned int test_tokenizer_inputoutput(struct fts_tokenizer *tok, const char *_input, const char *const *expected_output, unsigned int first_outi) { const unsigned char *input = (const unsigned char *)_input; const char *token, *error; unsigned int i, outi, max, char_len; size_t input_len = strlen(_input); /* test all input at once */ outi = first_outi; while (fts_tokenizer_next(tok, input, input_len, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } while (fts_tokenizer_next(tok, NULL, 0, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } test_assert_idx(expected_output[outi] == NULL, outi); /* test input one byte at a time */ outi = first_outi; for (i = 0; i < input_len; i += char_len) { char_len = uni_utf8_char_bytes(input[i]); while (fts_tokenizer_next(tok, input+i, char_len, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } } while (fts_tokenizer_final(tok, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } test_assert_idx(expected_output[outi] == NULL, outi); /* test input in random chunks */ outi = first_outi; for (i = 0; i < input_len; i += char_len) { max = rand() % (input_len - i) + 1; for (char_len = 0; char_len < max; ) char_len += uni_utf8_char_bytes(input[i+char_len]); while (fts_tokenizer_next(tok, input+i, char_len, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } } while (fts_tokenizer_final(tok, &token, &error) > 0) { test_assert_strcmp(token, expected_output[outi]); outi++; } test_assert_idx(expected_output[outi] == NULL, outi); return outi+1; } static void test_tokenizer_inputs(struct fts_tokenizer *tok, const char *const *expected_output) { unsigned int i, outi = 0; for (i = 0; i < N_ELEMENTS(test_inputs); i++) { outi = test_tokenizer_inputoutput(tok, test_inputs[i], expected_output, outi); } test_assert_idx(expected_output[outi] == NULL, outi); } static void test_fts_tokenizer_generic_only(void) { static const char *const expected_output[] = { "hello", "world", "And", "there", "was", "text", "galor\xC3\xA9", "abc", "example", "com", "Bar", "Baz", "bar", "example", "org", "foo", "domain", "1234567890123456789012345678\xC3\xA4", "12345678901234567890123456789", "123456789012345678901234567890", "and", "longlonglongabcdefghijklmnopqr", "more", "Hello", "world", "3", "14", "3", "14", "last", NULL, "1", NULL, "quoted", "text", "word", "hlo", "words", "you're", "bad", "word", "pre", "post", NULL, "1234567890123456789012345678\xC3\xA4", "123456789012345678901234567x'", "1234567890123456789012345678x'", "1234567890123456789012345678x", "1234567890123456789012345678x", "12345678901234567890123456789x", "12345678901234567890123456789x", "123456789012345678901234567890", "123456789012345678901234567890", "quoted", "text", "word", "hlo", "words", "you're789012345678901234567890", "bad", "word", "pre", "post", NULL, "you're'xyz", NULL, "hello", "world", "And", "there", "was", "text", "galore", "and", "more", NULL, "hello", "world", NULL, "l'homme", "l'humanit\xC3\xA9", "d'immixtions", "qu'il", "aujourd'hui", "que'euq", NULL, NULL }; struct fts_tokenizer *tok; const char *error; test_begin("fts tokenizer generic simple"); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, NULL, &tok, &error) == 0); test_assert(((struct generic_fts_tokenizer *) tok)->algorithm == BOUNDARY_ALGORITHM_SIMPLE); test_tokenizer_inputs(tok, expected_output); fts_tokenizer_unref(&tok); test_end(); } const char *const tr29_settings[] = {"algorithm", "tr29", NULL}; /* TODO: U+206F is in "Format" and therefore currently not word break. This definitely needs to be remapped. */ static void test_fts_tokenizer_generic_tr29_only(void) { static const char *const expected_output[] = { "hello", "world", "And", "there", "was", "text", "galor\xC3\xA9", "abc", "example", "com", "Bar", "Baz", "bar", "example", "org", "foo", "domain", "1234567890123456789012345678\xC3\xA4", "12345678901234567890123456789", "123456789012345678901234567890", "and", "longlonglongabcdefghijklmnopqr", "more", "Hello", "world", "3", "14", "3,14", "last", NULL, "1", NULL, "quoted", "text", "word", "hlo", "words", "you're", "bad", "word", "pre", "post", NULL, "1234567890123456789012345678\xC3\xA4", "123456789012345678901234567x'", "1234567890123456789012345678x'", "1234567890123456789012345678x", "1234567890123456789012345678x", "12345678901234567890123456789x", "12345678901234567890123456789x", "123456789012345678901234567890", "123456789012345678901234567890", "quoted", "text", "word", "hlo", "words", "you're789012345678901234567890", "bad", "word", "pre", "post", NULL, "you're'xyz", NULL, "hello", "world", "And", "there", "was", "text", "galore", "and", "more", NULL, "hello", "world", NULL, "l'homme", "l'humanit\xC3\xA9", "d'immixtions", "qu'il", "aujourd'hui", "que'euq", NULL, NULL }; struct fts_tokenizer *tok; const char *error; test_begin("fts tokenizer generic TR29"); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, tr29_settings, &tok, &error) == 0); test_tokenizer_inputs(tok, expected_output); fts_tokenizer_unref(&tok); test_end(); } const char *const tr29_settings_wb5a[] = {"algorithm", "tr29", "wb5a", "yes", NULL}; /* TODO: U+206F is in "Format" and therefore currently not word break. This definitely needs to be remapped. */ static void test_fts_tokenizer_generic_tr29_wb5a(void) { static const char *const expected_output[] = { "hello", "world", "And", "there", "was", "text", "galor\xC3\xA9", "abc", "example", "com", "Bar", "Baz", "bar", "example", "org", "foo", "domain", "1234567890123456789012345678\xC3\xA4", "12345678901234567890123456789", "123456789012345678901234567890", "and", "longlonglongabcdefghijklmnopqr", "more", "Hello", "world", "3", "14", "3,14", "last", NULL, "1", NULL, "quoted", "text", "word", "hlo", "words", "you're", "bad", "word", "pre", "post", NULL, "1234567890123456789012345678\xC3\xA4", "123456789012345678901234567x'", "1234567890123456789012345678x'", "1234567890123456789012345678x", "1234567890123456789012345678x", "12345678901234567890123456789x", "12345678901234567890123456789x", "123456789012345678901234567890", "123456789012345678901234567890", "quoted", "text", "word", "hlo", "words", "you're789012345678901234567890", "bad", "word", "pre", "post", NULL, "you're'xyz", NULL, "hello", "world", "And", "there", "was", "text", "galore", "and", "more", NULL, "hello", "world", NULL, "l", "homme", "l", "humanit\xC3\xA9", "d", "immixtions", "qu", "il", "aujourd'hui", "que'euq", NULL, NULL }; struct fts_tokenizer *tok; const char *error; test_begin("fts tokenizer generic TR29 with WB5a"); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, tr29_settings_wb5a, &tok, &error) == 0); test_tokenizer_inputs(tok, expected_output); fts_tokenizer_unref(&tok); test_end(); } static void test_fts_tokenizer_address_only(void) { static const char input[] = TEST_INPUT_ADDRESS; static const char *const expected_output[] = { "abc.dfg@example.com", "bar@example.org", "foo.bar@host.example.org", "foo@domain", "abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz@abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstu", "period@blue.com", /*trailing period '.' in email */ "mul@trail.com", "m@s", /*one letter local-part and domain name */ "hypen@hypen-hypen.com", "hypen@hypen-hypen-sick.com", NULL }; struct fts_tokenizer *tok; const char *error; test_begin("fts tokenizer email address only"); test_assert(fts_tokenizer_create(fts_tokenizer_email_address, NULL, NULL, &tok, &error) == 0); test_tokenizer_inputoutput(tok, input, expected_output, 0); fts_tokenizer_unref(&tok); test_end(); } static void test_fts_tokenizer_address_parent(const char *name, const char * const *settings) { static const char input[] = TEST_INPUT_ADDRESS; static const char *const expected_output[] = { "invalid", "invalid", "Abc", "Dfg", "abc", "dfg", "example", "com", "abc.dfg@example.com", "Bar", "Baz", "bar", "example", "org", "bar@example.org", "Foo", "Bar", "comment", "foo", "bar", "host", "example", "org", "foo.bar@host.example.org", "foo", "foo", "domain", "foo@domain", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyzabcde", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz","tld", "abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz@abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstu", "trailing", "period", "blue", "com", "period@blue.com", "multi", "trialing", "mul", "trail", "com", "mul@trail.com", "m", "s", "m@s", "hypen", "hypen", "hypen", "com", "hypen@hypen-hypen.com", "hypen", "hypen", "hypen", "sick", "com", "hypen@hypen-hypen-sick.com", NULL }; struct fts_tokenizer *tok, *gen_tok; const char *error; test_begin(t_strdup_printf("fts tokenizer email address + parent %s", name)); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, settings, &gen_tok, &error) == 0); test_assert(fts_tokenizer_create(fts_tokenizer_email_address, gen_tok, NULL, &tok, &error) == 0); test_tokenizer_inputoutput(tok, input, expected_output, 0); fts_tokenizer_unref(&tok); fts_tokenizer_unref(&gen_tok); test_end(); } const char *const simple_settings[] = {"algorithm", "simple", NULL}; static void test_fts_tokenizer_address_parent_simple(void) { test_fts_tokenizer_address_parent("simple", simple_settings); } static void test_fts_tokenizer_address_parent_tr29(void) { test_fts_tokenizer_address_parent("tr29", tr29_settings); } static void test_fts_tokenizer_address_search(void) { static const char input[] = TEST_INPUT_ADDRESS; static const char *const expected_output[] = { "invalid", "invalid", "Abc", "Dfg", "abc.dfg@example.com", "Bar", "Baz", "bar@example.org", "Foo", "Bar", "comment", "foo.bar@host.example.org", "foo", "foo@domain", "abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz@abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstuvxyz.abcdefghijklmnopqrstu", "trailing", "period@blue.com", "multi", "trialing", "mul@trail.com", "m@s", "hypen@hypen-hypen.com", "hypen@hypen-hypen-sick.com", NULL }; static const char *const settings[] = { "search", "", NULL }; struct fts_tokenizer *tok, *gen_tok; const char *token, *error; test_begin("fts tokenizer search email address + parent"); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, NULL, &gen_tok, &error) == 0); test_assert(fts_tokenizer_create(fts_tokenizer_email_address, gen_tok, settings, &tok, &error) == 0); test_tokenizer_inputoutput(tok, input, expected_output, 0); /* make sure state is forgotten at EOF */ test_assert(fts_tokenizer_next(tok, (const void *)"foo", 3, &token, &error) == 0); test_assert(fts_tokenizer_final(tok, &token, &error) > 0 && strcmp(token, "foo") == 0); test_assert(fts_tokenizer_final(tok, &token, &error) == 0); test_assert(fts_tokenizer_next(tok, (const void *)"bar@baz", 7, &token, &error) == 0); test_assert(fts_tokenizer_final(tok, &token, &error) > 0 && strcmp(token, "bar@baz") == 0); test_assert(fts_tokenizer_final(tok, &token, &error) == 0); test_assert(fts_tokenizer_next(tok, (const void *)"foo@", 4, &token, &error) == 0); test_assert(fts_tokenizer_final(tok, &token, &error) > 0 && strcmp(token, "foo") == 0); test_assert(fts_tokenizer_final(tok, &token, &error) == 0); /* test reset explicitly */ test_assert(fts_tokenizer_next(tok, (const void *)"a", 1, &token, &error) == 0); fts_tokenizer_reset(tok); test_assert(fts_tokenizer_next(tok, (const void *)"b@c", 3, &token, &error) == 0); test_assert(fts_tokenizer_final(tok, &token, &error) > 0 && strcmp(token, "b@c") == 0); test_assert(fts_tokenizer_final(tok, &token, &error) == 0); fts_tokenizer_unref(&tok); fts_tokenizer_unref(&gen_tok); test_end(); } static void test_fts_tokenizer_delete_trailing_partial_char(void) { const struct { const char *str; unsigned int truncated_len; } tests[] = { /* non-truncated */ { "\x7f", 1 }, { "\xC2\x80", 2 }, { "\xE0\x80\x80", 3 }, { "\xF0\x80\x80\x80", 4 }, /* truncated */ { "\xF0\x80\x80", 0 }, { "x\xF0\x80\x80", 1 }, }; unsigned int i; size_t size; test_begin("fts tokenizer delete trailing partial char"); for (i = 0; i < N_ELEMENTS(tests); i++) { size = strlen(tests[i].str); fts_tokenizer_delete_trailing_partial_char((const unsigned char *)tests[i].str, &size); test_assert(size == tests[i].truncated_len); } test_end(); } static void test_fts_tokenizer_address_maxlen(void) { const char *const settings[] = {"maxlen", "5", NULL}; const char *input = "...\357\277\275@a"; struct fts_tokenizer *tok; const char *token, *error; test_begin("fts tokenizer address maxlen"); test_assert(fts_tokenizer_create(fts_tokenizer_email_address, NULL, settings, &tok, &error) == 0); while (fts_tokenizer_next(tok, (const unsigned char *)input, strlen(input), &token, &error) > 0) ; while (fts_tokenizer_final(tok, &token, &error) > 0) ; fts_tokenizer_unref(&tok); test_end(); } static void test_fts_tokenizer_random(void) { const char test_chars[] = { 0, ' ', '.', 'a', 'b', 'c', '-', '@', '\xC3', '\xA4' }; const char *const settings[] = {"algorithm", "simple", NULL}; const char *const email_settings[] = {"maxlen", "9", NULL}; unsigned int i; unsigned char addr[10] = { 0 }; string_t *str = t_str_new(20); struct fts_tokenizer *tok, *gen_tok; const char *token, *error; test_begin("fts tokenizer random"); test_assert(fts_tokenizer_create(fts_tokenizer_generic, NULL, settings, &gen_tok, &error) == 0); test_assert(fts_tokenizer_create(fts_tokenizer_email_address, gen_tok, email_settings, &tok, &error) == 0); for (i = 0; i < 10000; i++) T_BEGIN { for (unsigned int j = 0; j < sizeof(addr); j++) addr[j] = test_chars[rand() % N_ELEMENTS(test_chars)]; str_truncate(str, 0); (void)uni_utf8_get_valid_data(addr, sizeof(addr), str); while (fts_tokenizer_next(tok, str_data(str), str_len(str), &token, &error) > 0) ; while (fts_tokenizer_final(tok, &token, &error) > 0) ; } T_END; fts_tokenizer_unref(&tok); fts_tokenizer_unref(&gen_tok); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_fts_tokenizer_find, test_fts_tokenizer_generic_only, test_fts_tokenizer_generic_tr29_only, test_fts_tokenizer_generic_tr29_wb5a, test_fts_tokenizer_address_only, test_fts_tokenizer_address_parent_simple, test_fts_tokenizer_address_parent_tr29, test_fts_tokenizer_address_maxlen, test_fts_tokenizer_address_search, test_fts_tokenizer_delete_trailing_partial_char, test_fts_tokenizer_random, NULL }; int ret; fts_tokenizers_init(); ret = test_run(test_functions); fts_tokenizers_deinit(); return ret; } dovecot-2.2.33.2/src/lib-fts/fts-language.c0000644000175000017500000002015713165463624015214 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "fts-language.h" #ifdef HAVE_LIBEXTTEXTCAT_TEXTCAT_H # include #elif defined (HAVE_FTS_EXTTEXTCAT) # include #endif #ifndef TEXTCAT_RESULT_UNKNOWN /* old textcat.h has typos */ # ifdef TEXTCAT_RESULT_UNKOWN # define TEXTCAT_RESULT_UNKNOWN TEXTCAT_RESULT_UNKOWN # endif #endif #define DETECT_STR_MAX_LEN 200 struct fts_language_list { pool_t pool; ARRAY_TYPE(fts_language) languages; const char *textcat_config; const char *textcat_datadir; void *textcat_handle; bool textcat_failed; }; pool_t fts_languages_pool; ARRAY_TYPE(fts_language) fts_languages; /* ISO 639-1 alpha 2 codes for languages */ const struct fts_language fts_languages_builtin [] = { { "da" }, /* Danish */ { "de" }, /* German */ { "en" }, /* English */ { "es" }, /* Spanish */ { "fi" }, /* Finnish */ { "fr" }, /* French */ { "it" }, /* Italian */ { "nl" }, /* Dutch */ { "no" }, /* Both Bokmal and Nynorsk are detected as Norwegian */ { "pt" }, /* Portuguese */ { "ro" }, /* Romanian */ { "ru" }, /* Russian */ { "sv" } /* Swedish */ }; const struct fts_language fts_language_data = { "data" }; void fts_languages_init(void) { unsigned int i; const struct fts_language *lp; fts_languages_pool = pool_alloconly_create("fts_language", sizeof(fts_languages_builtin)); p_array_init(&fts_languages, fts_languages_pool, N_ELEMENTS(fts_languages_builtin)); for (i = 0; i < N_ELEMENTS(fts_languages_builtin); i++){ lp = &fts_languages_builtin[i]; array_append(&fts_languages, &lp, 1); } } void fts_languages_deinit(void) { if (fts_languages_pool != NULL) pool_unref(&fts_languages_pool); } void fts_language_register(const char *name) { struct fts_language *lang; if (fts_language_find(name) != NULL) return; lang = p_new(fts_languages_pool, struct fts_language, 1); lang->name = p_strdup(fts_languages_pool, name); array_append(&fts_languages, (const struct fts_language **)&lang, 1); } const struct fts_language *fts_language_find(const char *name) { const struct fts_language *const *langp = NULL; array_foreach(&fts_languages, langp) { if (strcmp((*langp)->name, name) == 0) return *langp; } return NULL; } int fts_language_list_init(const char *const *settings, struct fts_language_list **list_r, const char **error_r) { struct fts_language_list *lp; pool_t pool; unsigned int i; const char *conf = NULL, *data = NULL; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "fts_language_config") == 0) conf = value; else if (strcmp(key, "fts_language_data") == 0) data = value; else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } pool = pool_alloconly_create("fts_language_list", 128); lp = p_new(pool, struct fts_language_list, 1); lp->pool = pool; if (conf != NULL) lp->textcat_config = p_strdup(pool, conf); else lp->textcat_config = NULL; if (data != NULL) lp->textcat_datadir = p_strdup(pool, data); else lp->textcat_datadir = NULL; p_array_init(&lp->languages, pool, 32); *list_r = lp; return 0; } void fts_language_list_deinit(struct fts_language_list **list) { struct fts_language_list *lp = *list; *list = NULL; #ifdef HAVE_FTS_EXTTEXTCAT if (lp->textcat_handle != NULL) textcat_Done(lp->textcat_handle); #endif pool_unref(&lp->pool); } static const struct fts_language * fts_language_list_find(struct fts_language_list *list, const char *name) { const struct fts_language *const *langp; array_foreach(&list->languages, langp) { if (strcmp((*langp)->name, name) == 0) return *langp; } return NULL; } void fts_language_list_add(struct fts_language_list *list, const struct fts_language *lang) { i_assert(fts_language_list_find(list, lang->name) == NULL); array_append(&list->languages, &lang, 1); } bool fts_language_list_add_names(struct fts_language_list *list, const char *names, const char **unknown_name_r) { const char *const *langs; const struct fts_language *lang; for (langs = t_strsplit_spaces(names, ", "); *langs != NULL; langs++) { lang = fts_language_find(*langs); if (lang == NULL) { /* unknown language */ *unknown_name_r = *langs; return FALSE; } if (fts_language_list_find(list, lang->name) == NULL) fts_language_list_add(list, lang); } return TRUE; } const ARRAY_TYPE(fts_language) * fts_language_list_get_all(struct fts_language_list *list) { return &list->languages; } const struct fts_language * fts_language_list_get_first(struct fts_language_list *list) { const struct fts_language *const *langp; langp = array_idx(&list->languages, 0); return *langp; } #ifdef HAVE_FTS_EXTTEXTCAT static bool fts_language_match_lists(struct fts_language_list *list, candidate_t *candp, int candp_len, const struct fts_language **lang_r) { const char *name; for (int i = 0; i < candp_len; i++) { /* name is -- eg, fi--utf8 or pt-PT-utf8 */ name = t_strcut(candp[i].name, '-'); /* For Norwegian we treat both bokmal and nynorsk as "no". */ if (strcmp(name, "nb") == 0 || strcmp(name, "nn") == 0) name = "no"; if ((*lang_r = fts_language_list_find(list, name)) != NULL) return TRUE; } return FALSE; } #endif #ifdef HAVE_FTS_EXTTEXTCAT static int fts_language_textcat_init(struct fts_language_list *list) { const char *config_path; const char *data_dir; if (list->textcat_handle != NULL) return 0; if (list->textcat_failed) return -1; config_path = list->textcat_config != NULL ? list->textcat_config : TEXTCAT_DATADIR"/fpdb.conf"; data_dir = list->textcat_datadir != NULL ? list->textcat_datadir : TEXTCAT_DATADIR"/"; list->textcat_handle = special_textcat_Init(config_path, data_dir); if (list->textcat_handle == NULL) { i_error("special_textcat_Init(%s, %s) failed", config_path, data_dir); list->textcat_failed = TRUE; return -1; } /* The textcat minimum document size could be set here. It currently defaults to 3. UTF8 is enabled by default. */ return 0; } #endif static enum fts_language_result fts_language_detect_textcat(struct fts_language_list *list ATTR_UNUSED, const unsigned char *text ATTR_UNUSED, size_t size ATTR_UNUSED, const struct fts_language **lang_r ATTR_UNUSED) { #ifdef HAVE_FTS_EXTTEXTCAT candidate_t *candp; /* textcat candidate result array pointer */ int cnt; bool match = FALSE; if (fts_language_textcat_init(list) < 0) return FTS_LANGUAGE_RESULT_ERROR; candp = textcat_GetClassifyFullOutput(list->textcat_handle); if (candp == NULL) i_fatal_status(FATAL_OUTOFMEM, "textcat_GetCLassifyFullOutput failed: malloc() returned NULL"); cnt = textcat_ClassifyFull(list->textcat_handle, (const void *)text, I_MIN(size, DETECT_STR_MAX_LEN), candp); if (cnt > 0) { T_BEGIN { match = fts_language_match_lists(list, candp, cnt, lang_r); } T_END; textcat_ReleaseClassifyFullOutput(list->textcat_handle, candp); if (match) return FTS_LANGUAGE_RESULT_OK; else return FTS_LANGUAGE_RESULT_UNKNOWN; } else { textcat_ReleaseClassifyFullOutput(list->textcat_handle, candp); switch (cnt) { case TEXTCAT_RESULT_SHORT: i_assert(size < DETECT_STR_MAX_LEN); return FTS_LANGUAGE_RESULT_SHORT; case TEXTCAT_RESULT_UNKNOWN: return FTS_LANGUAGE_RESULT_UNKNOWN; default: i_unreached(); } } #else return FTS_LANGUAGE_RESULT_UNKNOWN; #endif } enum fts_language_result fts_language_detect(struct fts_language_list *list, const unsigned char *text ATTR_UNUSED, size_t size ATTR_UNUSED, const struct fts_language **lang_r) { i_assert(array_count(&list->languages) > 0); /* if there's only a single wanted language, return it always. */ if (array_count(&list->languages) == 1) { const struct fts_language *const *langp = array_idx(&list->languages, 0); *lang_r = *langp; return FTS_LANGUAGE_RESULT_OK; } return fts_language_detect_textcat(list, text, size, lang_r); } dovecot-2.2.33.2/src/lib-fts/fts-filter.c0000644000175000017500000000616713123174404014710 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "fts-language.h" #include "fts-filter-private.h" #ifdef HAVE_LIBICU # include "fts-icu.h" #endif static ARRAY(const struct fts_filter *) fts_filter_classes; void fts_filters_init(void) { i_array_init(&fts_filter_classes, FTS_FILTER_CLASSES_NR); fts_filter_register(fts_filter_stopwords); fts_filter_register(fts_filter_stemmer_snowball); fts_filter_register(fts_filter_normalizer_icu); fts_filter_register(fts_filter_lowercase); fts_filter_register(fts_filter_english_possessive); fts_filter_register(fts_filter_contractions); } void fts_filters_deinit(void) { #ifdef HAVE_LIBICU fts_icu_deinit(); #endif array_free(&fts_filter_classes); } void fts_filter_register(const struct fts_filter *filter_class) { i_assert(fts_filter_find(filter_class->class_name) == NULL); array_append(&fts_filter_classes, &filter_class, 1); } const struct fts_filter *fts_filter_find(const char *name) { const struct fts_filter *const *fp = NULL; array_foreach(&fts_filter_classes, fp) { if (strcmp((*fp)->class_name, name) == 0) return *fp; } return NULL; } int fts_filter_create(const struct fts_filter *filter_class, struct fts_filter *parent, const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter *fp; const char *empty_settings = NULL; i_assert(settings == NULL || str_array_length(settings) % 2 == 0); if (settings == NULL) settings = &empty_settings; if (filter_class->v.create != NULL) { if (filter_class->v.create(lang, settings, &fp, error_r) < 0) { *filter_r = NULL; return -1; } } else { /* default implementation */ if (settings[0] != NULL) { *error_r = t_strdup_printf("Unknown setting: %s", settings[0]); return -1; } fp = i_new(struct fts_filter, 1); *fp = *filter_class; } fp->refcount = 1; fp->parent = parent; if (parent != NULL) { fts_filter_ref(parent); } *filter_r = fp; return 0; } void fts_filter_ref(struct fts_filter *fp) { i_assert(fp->refcount > 0); fp->refcount++; } void fts_filter_unref(struct fts_filter **_fpp) { struct fts_filter *fp = *_fpp; i_assert(fp->refcount > 0); *_fpp = NULL; if (--fp->refcount > 0) return; if (fp->parent != NULL) fts_filter_unref(&fp->parent); if (fp->v.destroy != NULL) fp->v.destroy(fp); else { /* default destroy implementation */ if (fp->token != NULL) str_free(&fp->token); i_free(fp); } } int fts_filter_filter(struct fts_filter *filter, const char **token, const char **error_r) { int ret = 0; i_assert((*token)[0] != '\0'); /* Recurse to parent. */ if (filter->parent != NULL) ret = fts_filter_filter(filter->parent, token, error_r); /* Parent returned token or no parent. */ if (ret > 0 || filter->parent == NULL) ret = filter->v.filter(filter, token, error_r); if (ret <= 0) *token = NULL; else { i_assert(*token != NULL); i_assert((*token)[0] != '\0'); } return ret; } dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-generic-private.h0000644000175000017500000000257313123174404020521 00000000000000#ifndef FTS_TOKENIZER_GENERIC_PRIVATE_H #define FTS_TOKENIZER_GENERIC_PRIVATE_H extern const struct fts_tokenizer_vfuncs generic_tokenizer_vfuncs_simple; extern const struct fts_tokenizer_vfuncs generic_tokenizer_vfuncs_tr29; /* Word boundary letter type */ enum letter_type { LETTER_TYPE_NONE = 0, LETTER_TYPE_CR, LETTER_TYPE_LF, LETTER_TYPE_NEWLINE, LETTER_TYPE_EXTEND, LETTER_TYPE_REGIONAL_INDICATOR, LETTER_TYPE_FORMAT, LETTER_TYPE_KATAKANA, LETTER_TYPE_HEBREW_LETTER, LETTER_TYPE_ALETTER, LETTER_TYPE_SINGLE_QUOTE, LETTER_TYPE_DOUBLE_QUOTE, LETTER_TYPE_MIDNUMLET, LETTER_TYPE_MIDLETTER, LETTER_TYPE_MIDNUM, LETTER_TYPE_NUMERIC, LETTER_TYPE_EXTENDNUMLET, LETTER_TYPE_SOT, LETTER_TYPE_EOT, LETTER_TYPE_APOSTROPHE, /* Own modification to TR29 */ LETTER_TYPE_OTHER /* WB14 "any" */ }; enum boundary_algorithm { BOUNDARY_ALGORITHM_NONE = 0, BOUNDARY_ALGORITHM_SIMPLE, #define ALGORITHM_SIMPLE_NAME "simple" BOUNDARY_ALGORITHM_TR29 #define ALGORITHM_TR29_NAME "tr29" }; struct generic_fts_tokenizer { struct fts_tokenizer tokenizer; unsigned int max_length; bool wb5a; /* TR29 rule for prefix separation in e.g. French or Italian. */ bool seen_wb5a; unichar_t prev_letter_c; unichar_t letter_c; enum boundary_algorithm algorithm; enum letter_type prev_letter; enum letter_type prev_prev_letter; size_t untruncated_length; buffer_t *token; }; #endif dovecot-2.2.33.2/src/lib-fts/fts-language.h0000644000175000017500000000446513123174404015212 00000000000000#ifndef FTS_LANGUAGE_H #define FTS_LANGUAGE_H struct fts_language_list; enum fts_language_result { /* Provided sample is too short. */ FTS_LANGUAGE_RESULT_SHORT, /* Language is unknown or not in the provided list . */ FTS_LANGUAGE_RESULT_UNKNOWN, FTS_LANGUAGE_RESULT_OK, /* textcat library initialization failed. */ FTS_LANGUAGE_RESULT_ERROR }; struct fts_language { /* Two-letter language name lowercased, e.g. "en" */ const char *name; }; ARRAY_DEFINE_TYPE(fts_language, const struct fts_language *); /* Used for raw data that is indexed. This data shouldn't go through any language-specific filters. */ extern const struct fts_language fts_language_data; /* Language module API. */ void fts_languages_init(void); void fts_languages_deinit(void); /* Add a language to the list of supported languages. */ void fts_language_register(const char *name); /* Find a specified language by name. This finds from the internal list of supported languages. */ const struct fts_language *fts_language_find(const char *name); /* Language list API */ int fts_language_list_init(const char *const *settings, struct fts_language_list **list_r, const char **error_r); void fts_language_list_deinit(struct fts_language_list **list); /* Add a language to the list of wanted languages. */ void fts_language_list_add(struct fts_language_list *list, const struct fts_language *lang); /* Add wanted languages from a space-separated list of language names. Duplicates are ignored. Returns TRUE if ok, FALSE and unknown_name if an unknown language was found from the list. */ bool fts_language_list_add_names(struct fts_language_list *list, const char *names, const char **unknown_name_r); /* Return an array of all wanted languages. */ const ARRAY_TYPE(fts_language) * fts_language_list_get_all(struct fts_language_list *list); /* Returns the first wanted language (default language). */ const struct fts_language * fts_language_list_get_first(struct fts_language_list *list); /* If text was detected to be one of the languages in the list, returns FTS_LANGUAGE_RESULT_OK and (a pointer to) the language (in the list). */ enum fts_language_result fts_language_detect(struct fts_language_list *list, const unsigned char *text, size_t size, const struct fts_language **lang_r); #endif dovecot-2.2.33.2/src/lib-fts/fts-filter-stemmer-snowball.c0000644000175000017500000000700413123174404020170 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fts-language.h" #include "fts-filter-private.h" #ifdef HAVE_FTS_STEMMER #include struct fts_filter_stemmer_snowball { struct fts_filter filter; pool_t pool; struct fts_language *lang; struct sb_stemmer *stemmer; }; static void fts_filter_stemmer_snowball_destroy(struct fts_filter *filter) { struct fts_filter_stemmer_snowball *sp = (struct fts_filter_stemmer_snowball *)filter; if (sp->stemmer != NULL) sb_stemmer_delete(sp->stemmer); pool_unref(&sp->pool); } static int fts_filter_stemmer_snowball_create(const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter_stemmer_snowball *sp; pool_t pp; *filter_r = NULL; if (settings[0] != NULL) { *error_r = t_strdup_printf("Unknown setting: %s", settings[0]); return -1; } pp = pool_alloconly_create(MEMPOOL_GROWING"fts_filter_stemmer_snowball", sizeof(struct fts_filter)); sp = p_new(pp, struct fts_filter_stemmer_snowball, 1); sp->pool = pp; sp->filter = *fts_filter_stemmer_snowball; sp->lang = p_malloc(sp->pool, sizeof(struct fts_language)); sp->lang->name = p_strdup(sp->pool, lang->name); *filter_r = &sp->filter; return 0; } static int fts_filter_stemmer_snowball_create_stemmer(struct fts_filter_stemmer_snowball *sp, const char **error_r) { sp->stemmer = sb_stemmer_new(sp->lang->name, "UTF_8"); if (sp->stemmer == NULL) { *error_r = t_strdup_printf( "Creating a Snowball stemmer for language '%s' failed.", sp->lang->name); fts_filter_stemmer_snowball_destroy(&sp->filter); return -1; } return 0; } static int fts_filter_stemmer_snowball_filter(struct fts_filter *filter, const char **token, const char **error_r) { struct fts_filter_stemmer_snowball *sp = (struct fts_filter_stemmer_snowball *) filter; const sb_symbol *base; if (sp->stemmer == NULL) { if (fts_filter_stemmer_snowball_create_stemmer(sp, error_r) < 0) return -1; } base = sb_stemmer_stem(sp->stemmer, (const unsigned char *)*token, strlen(*token)); if (base == NULL) { /* the only reason why this could fail is because of out of memory. */ i_fatal_status(FATAL_OUTOFMEM, "sb_stemmer_stem(len=%"PRIuSIZE_T") failed: " "Out of memory", strlen(*token)); } *token = t_strndup(base, sb_stemmer_length(sp->stemmer)); return 1; } #else static int fts_filter_stemmer_snowball_create(const struct fts_language *lang ATTR_UNUSED, const char *const *settings ATTR_UNUSED, struct fts_filter **filter_r ATTR_UNUSED, const char **error_r) { *error_r = "Snowball support not built in"; return -1; } static void fts_filter_stemmer_snowball_destroy(struct fts_filter *stemmer ATTR_UNUSED) { } static int fts_filter_stemmer_snowball_filter(struct fts_filter *filter ATTR_UNUSED, const char **token ATTR_UNUSED, const char **error_r ATTR_UNUSED) { return -1; } #endif static const struct fts_filter fts_filter_stemmer_snowball_real = { .class_name = "snowball", .v = { fts_filter_stemmer_snowball_create, fts_filter_stemmer_snowball_filter, fts_filter_stemmer_snowball_destroy } }; const struct fts_filter *fts_filter_stemmer_snowball = &fts_filter_stemmer_snowball_real; dovecot-2.2.33.2/src/lib-fts/word-boundary-data.c0000644000175000017500000106627013144653672016352 00000000000000/* This file is automatically generated by word-properties.pl from WordBreakProperty.txt */ static const uint32_t CR[]= { 0x0000D }; static const uint32_t LF[]= { 0x0000A }; static const uint32_t Newline[]= { 0x0000B, 0x0000C, 0x00085, 0x02028, 0x02029 }; static const uint32_t Extend[]= { 0x00300, 0x00301, 0x00302, 0x00303, 0x00304, 0x00305, 0x00306, 0x00307, 0x00308, 0x00309, 0x0030A, 0x0030B, 0x0030C, 0x0030D, 0x0030E, 0x0030F, 0x00310, 0x00311, 0x00312, 0x00313, 0x00314, 0x00315, 0x00316, 0x00317, 0x00318, 0x00319, 0x0031A, 0x0031B, 0x0031C, 0x0031D, 0x0031E, 0x0031F, 0x00320, 0x00321, 0x00322, 0x00323, 0x00324, 0x00325, 0x00326, 0x00327, 0x00328, 0x00329, 0x0032A, 0x0032B, 0x0032C, 0x0032D, 0x0032E, 0x0032F, 0x00330, 0x00331, 0x00332, 0x00333, 0x00334, 0x00335, 0x00336, 0x00337, 0x00338, 0x00339, 0x0033A, 0x0033B, 0x0033C, 0x0033D, 0x0033E, 0x0033F, 0x00340, 0x00341, 0x00342, 0x00343, 0x00344, 0x00345, 0x00346, 0x00347, 0x00348, 0x00349, 0x0034A, 0x0034B, 0x0034C, 0x0034D, 0x0034E, 0x0034F, 0x00350, 0x00351, 0x00352, 0x00353, 0x00354, 0x00355, 0x00356, 0x00357, 0x00358, 0x00359, 0x0035A, 0x0035B, 0x0035C, 0x0035D, 0x0035E, 0x0035F, 0x00360, 0x00361, 0x00362, 0x00363, 0x00364, 0x00365, 0x00366, 0x00367, 0x00368, 0x00369, 0x0036A, 0x0036B, 0x0036C, 0x0036D, 0x0036E, 0x0036F, 0x00483, 0x00484, 0x00485, 0x00486, 0x00487, 0x00488, 0x00489, 0x00591, 0x00592, 0x00593, 0x00594, 0x00595, 0x00596, 0x00597, 0x00598, 0x00599, 0x0059A, 0x0059B, 0x0059C, 0x0059D, 0x0059E, 0x0059F, 0x005A0, 0x005A1, 0x005A2, 0x005A3, 0x005A4, 0x005A5, 0x005A6, 0x005A7, 0x005A8, 0x005A9, 0x005AA, 0x005AB, 0x005AC, 0x005AD, 0x005AE, 0x005AF, 0x005B0, 0x005B1, 0x005B2, 0x005B3, 0x005B4, 0x005B5, 0x005B6, 0x005B7, 0x005B8, 0x005B9, 0x005BA, 0x005BB, 0x005BC, 0x005BD, 0x005BF, 0x005C1, 0x005C2, 0x005C4, 0x005C5, 0x005C7, 0x00610, 0x00611, 0x00612, 0x00613, 0x00614, 0x00615, 0x00616, 0x00617, 0x00618, 0x00619, 0x0061A, 0x0064B, 0x0064C, 0x0064D, 0x0064E, 0x0064F, 0x00650, 0x00651, 0x00652, 0x00653, 0x00654, 0x00655, 0x00656, 0x00657, 0x00658, 0x00659, 0x0065A, 0x0065B, 0x0065C, 0x0065D, 0x0065E, 0x0065F, 0x00670, 0x006D6, 0x006D7, 0x006D8, 0x006D9, 0x006DA, 0x006DB, 0x006DC, 0x006DF, 0x006E0, 0x006E1, 0x006E2, 0x006E3, 0x006E4, 0x006E7, 0x006E8, 0x006EA, 0x006EB, 0x006EC, 0x006ED, 0x00711, 0x00730, 0x00731, 0x00732, 0x00733, 0x00734, 0x00735, 0x00736, 0x00737, 0x00738, 0x00739, 0x0073A, 0x0073B, 0x0073C, 0x0073D, 0x0073E, 0x0073F, 0x00740, 0x00741, 0x00742, 0x00743, 0x00744, 0x00745, 0x00746, 0x00747, 0x00748, 0x00749, 0x0074A, 0x007A6, 0x007A7, 0x007A8, 0x007A9, 0x007AA, 0x007AB, 0x007AC, 0x007AD, 0x007AE, 0x007AF, 0x007B0, 0x007EB, 0x007EC, 0x007ED, 0x007EE, 0x007EF, 0x007F0, 0x007F1, 0x007F2, 0x007F3, 0x00816, 0x00817, 0x00818, 0x00819, 0x0081B, 0x0081C, 0x0081D, 0x0081E, 0x0081F, 0x00820, 0x00821, 0x00822, 0x00823, 0x00825, 0x00826, 0x00827, 0x00829, 0x0082A, 0x0082B, 0x0082C, 0x0082D, 0x00859, 0x0085A, 0x0085B, 0x008D4, 0x008D5, 0x008D6, 0x008D7, 0x008D8, 0x008D9, 0x008DA, 0x008DB, 0x008DC, 0x008DD, 0x008DE, 0x008DF, 0x008E0, 0x008E1, 0x008E3, 0x008E4, 0x008E5, 0x008E6, 0x008E7, 0x008E8, 0x008E9, 0x008EA, 0x008EB, 0x008EC, 0x008ED, 0x008EE, 0x008EF, 0x008F0, 0x008F1, 0x008F2, 0x008F3, 0x008F4, 0x008F5, 0x008F6, 0x008F7, 0x008F8, 0x008F9, 0x008FA, 0x008FB, 0x008FC, 0x008FD, 0x008FE, 0x008FF, 0x00900, 0x00901, 0x00902, 0x00903, 0x0093A, 0x0093B, 0x0093C, 0x0093E, 0x0093F, 0x00940, 0x00941, 0x00942, 0x00943, 0x00944, 0x00945, 0x00946, 0x00947, 0x00948, 0x00949, 0x0094A, 0x0094B, 0x0094C, 0x0094D, 0x0094E, 0x0094F, 0x00951, 0x00952, 0x00953, 0x00954, 0x00955, 0x00956, 0x00957, 0x00962, 0x00963, 0x00981, 0x00982, 0x00983, 0x009BC, 0x009BE, 0x009BF, 0x009C0, 0x009C1, 0x009C2, 0x009C3, 0x009C4, 0x009C7, 0x009C8, 0x009CB, 0x009CC, 0x009CD, 0x009D7, 0x009E2, 0x009E3, 0x00A01, 0x00A02, 0x00A03, 0x00A3C, 0x00A3E, 0x00A3F, 0x00A40, 0x00A41, 0x00A42, 0x00A47, 0x00A48, 0x00A4B, 0x00A4C, 0x00A4D, 0x00A51, 0x00A70, 0x00A71, 0x00A75, 0x00A81, 0x00A82, 0x00A83, 0x00ABC, 0x00ABE, 0x00ABF, 0x00AC0, 0x00AC1, 0x00AC2, 0x00AC3, 0x00AC4, 0x00AC5, 0x00AC7, 0x00AC8, 0x00AC9, 0x00ACB, 0x00ACC, 0x00ACD, 0x00AE2, 0x00AE3, 0x00B01, 0x00B02, 0x00B03, 0x00B3C, 0x00B3E, 0x00B3F, 0x00B40, 0x00B41, 0x00B42, 0x00B43, 0x00B44, 0x00B47, 0x00B48, 0x00B4B, 0x00B4C, 0x00B4D, 0x00B56, 0x00B57, 0x00B62, 0x00B63, 0x00B82, 0x00BBE, 0x00BBF, 0x00BC0, 0x00BC1, 0x00BC2, 0x00BC6, 0x00BC7, 0x00BC8, 0x00BCA, 0x00BCB, 0x00BCC, 0x00BCD, 0x00BD7, 0x00C00, 0x00C01, 0x00C02, 0x00C03, 0x00C3E, 0x00C3F, 0x00C40, 0x00C41, 0x00C42, 0x00C43, 0x00C44, 0x00C46, 0x00C47, 0x00C48, 0x00C4A, 0x00C4B, 0x00C4C, 0x00C4D, 0x00C55, 0x00C56, 0x00C62, 0x00C63, 0x00C81, 0x00C82, 0x00C83, 0x00CBC, 0x00CBE, 0x00CBF, 0x00CC0, 0x00CC1, 0x00CC2, 0x00CC3, 0x00CC4, 0x00CC6, 0x00CC7, 0x00CC8, 0x00CCA, 0x00CCB, 0x00CCC, 0x00CCD, 0x00CD5, 0x00CD6, 0x00CE2, 0x00CE3, 0x00D01, 0x00D02, 0x00D03, 0x00D3E, 0x00D3F, 0x00D40, 0x00D41, 0x00D42, 0x00D43, 0x00D44, 0x00D46, 0x00D47, 0x00D48, 0x00D4A, 0x00D4B, 0x00D4C, 0x00D4D, 0x00D57, 0x00D62, 0x00D63, 0x00D82, 0x00D83, 0x00DCA, 0x00DCF, 0x00DD0, 0x00DD1, 0x00DD2, 0x00DD3, 0x00DD4, 0x00DD6, 0x00DD8, 0x00DD9, 0x00DDA, 0x00DDB, 0x00DDC, 0x00DDD, 0x00DDE, 0x00DDF, 0x00DF2, 0x00DF3, 0x00E31, 0x00E34, 0x00E35, 0x00E36, 0x00E37, 0x00E38, 0x00E39, 0x00E3A, 0x00E47, 0x00E48, 0x00E49, 0x00E4A, 0x00E4B, 0x00E4C, 0x00E4D, 0x00E4E, 0x00EB1, 0x00EB4, 0x00EB5, 0x00EB6, 0x00EB7, 0x00EB8, 0x00EB9, 0x00EBB, 0x00EBC, 0x00EC8, 0x00EC9, 0x00ECA, 0x00ECB, 0x00ECC, 0x00ECD, 0x00F18, 0x00F19, 0x00F35, 0x00F37, 0x00F39, 0x00F3E, 0x00F3F, 0x00F71, 0x00F72, 0x00F73, 0x00F74, 0x00F75, 0x00F76, 0x00F77, 0x00F78, 0x00F79, 0x00F7A, 0x00F7B, 0x00F7C, 0x00F7D, 0x00F7E, 0x00F7F, 0x00F80, 0x00F81, 0x00F82, 0x00F83, 0x00F84, 0x00F86, 0x00F87, 0x00F8D, 0x00F8E, 0x00F8F, 0x00F90, 0x00F91, 0x00F92, 0x00F93, 0x00F94, 0x00F95, 0x00F96, 0x00F97, 0x00F99, 0x00F9A, 0x00F9B, 0x00F9C, 0x00F9D, 0x00F9E, 0x00F9F, 0x00FA0, 0x00FA1, 0x00FA2, 0x00FA3, 0x00FA4, 0x00FA5, 0x00FA6, 0x00FA7, 0x00FA8, 0x00FA9, 0x00FAA, 0x00FAB, 0x00FAC, 0x00FAD, 0x00FAE, 0x00FAF, 0x00FB0, 0x00FB1, 0x00FB2, 0x00FB3, 0x00FB4, 0x00FB5, 0x00FB6, 0x00FB7, 0x00FB8, 0x00FB9, 0x00FBA, 0x00FBB, 0x00FBC, 0x00FC6, 0x0102B, 0x0102C, 0x0102D, 0x0102E, 0x0102F, 0x01030, 0x01031, 0x01032, 0x01033, 0x01034, 0x01035, 0x01036, 0x01037, 0x01038, 0x01039, 0x0103A, 0x0103B, 0x0103C, 0x0103D, 0x0103E, 0x01056, 0x01057, 0x01058, 0x01059, 0x0105E, 0x0105F, 0x01060, 0x01062, 0x01063, 0x01064, 0x01067, 0x01068, 0x01069, 0x0106A, 0x0106B, 0x0106C, 0x0106D, 0x01071, 0x01072, 0x01073, 0x01074, 0x01082, 0x01083, 0x01084, 0x01085, 0x01086, 0x01087, 0x01088, 0x01089, 0x0108A, 0x0108B, 0x0108C, 0x0108D, 0x0108F, 0x0109A, 0x0109B, 0x0109C, 0x0109D, 0x0135D, 0x0135E, 0x0135F, 0x01712, 0x01713, 0x01714, 0x01732, 0x01733, 0x01734, 0x01752, 0x01753, 0x01772, 0x01773, 0x017B4, 0x017B5, 0x017B6, 0x017B7, 0x017B8, 0x017B9, 0x017BA, 0x017BB, 0x017BC, 0x017BD, 0x017BE, 0x017BF, 0x017C0, 0x017C1, 0x017C2, 0x017C3, 0x017C4, 0x017C5, 0x017C6, 0x017C7, 0x017C8, 0x017C9, 0x017CA, 0x017CB, 0x017CC, 0x017CD, 0x017CE, 0x017CF, 0x017D0, 0x017D1, 0x017D2, 0x017D3, 0x017DD, 0x0180B, 0x0180C, 0x0180D, 0x01885, 0x01886, 0x018A9, 0x01920, 0x01921, 0x01922, 0x01923, 0x01924, 0x01925, 0x01926, 0x01927, 0x01928, 0x01929, 0x0192A, 0x0192B, 0x01930, 0x01931, 0x01932, 0x01933, 0x01934, 0x01935, 0x01936, 0x01937, 0x01938, 0x01939, 0x0193A, 0x0193B, 0x01A17, 0x01A18, 0x01A19, 0x01A1A, 0x01A1B, 0x01A55, 0x01A56, 0x01A57, 0x01A58, 0x01A59, 0x01A5A, 0x01A5B, 0x01A5C, 0x01A5D, 0x01A5E, 0x01A60, 0x01A61, 0x01A62, 0x01A63, 0x01A64, 0x01A65, 0x01A66, 0x01A67, 0x01A68, 0x01A69, 0x01A6A, 0x01A6B, 0x01A6C, 0x01A6D, 0x01A6E, 0x01A6F, 0x01A70, 0x01A71, 0x01A72, 0x01A73, 0x01A74, 0x01A75, 0x01A76, 0x01A77, 0x01A78, 0x01A79, 0x01A7A, 0x01A7B, 0x01A7C, 0x01A7F, 0x01AB0, 0x01AB1, 0x01AB2, 0x01AB3, 0x01AB4, 0x01AB5, 0x01AB6, 0x01AB7, 0x01AB8, 0x01AB9, 0x01ABA, 0x01ABB, 0x01ABC, 0x01ABD, 0x01ABE, 0x01B00, 0x01B01, 0x01B02, 0x01B03, 0x01B04, 0x01B34, 0x01B35, 0x01B36, 0x01B37, 0x01B38, 0x01B39, 0x01B3A, 0x01B3B, 0x01B3C, 0x01B3D, 0x01B3E, 0x01B3F, 0x01B40, 0x01B41, 0x01B42, 0x01B43, 0x01B44, 0x01B6B, 0x01B6C, 0x01B6D, 0x01B6E, 0x01B6F, 0x01B70, 0x01B71, 0x01B72, 0x01B73, 0x01B80, 0x01B81, 0x01B82, 0x01BA1, 0x01BA2, 0x01BA3, 0x01BA4, 0x01BA5, 0x01BA6, 0x01BA7, 0x01BA8, 0x01BA9, 0x01BAA, 0x01BAB, 0x01BAC, 0x01BAD, 0x01BE6, 0x01BE7, 0x01BE8, 0x01BE9, 0x01BEA, 0x01BEB, 0x01BEC, 0x01BED, 0x01BEE, 0x01BEF, 0x01BF0, 0x01BF1, 0x01BF2, 0x01BF3, 0x01C24, 0x01C25, 0x01C26, 0x01C27, 0x01C28, 0x01C29, 0x01C2A, 0x01C2B, 0x01C2C, 0x01C2D, 0x01C2E, 0x01C2F, 0x01C30, 0x01C31, 0x01C32, 0x01C33, 0x01C34, 0x01C35, 0x01C36, 0x01C37, 0x01CD0, 0x01CD1, 0x01CD2, 0x01CD4, 0x01CD5, 0x01CD6, 0x01CD7, 0x01CD8, 0x01CD9, 0x01CDA, 0x01CDB, 0x01CDC, 0x01CDD, 0x01CDE, 0x01CDF, 0x01CE0, 0x01CE1, 0x01CE2, 0x01CE3, 0x01CE4, 0x01CE5, 0x01CE6, 0x01CE7, 0x01CE8, 0x01CED, 0x01CF2, 0x01CF3, 0x01CF4, 0x01CF8, 0x01CF9, 0x01DC0, 0x01DC1, 0x01DC2, 0x01DC3, 0x01DC4, 0x01DC5, 0x01DC6, 0x01DC7, 0x01DC8, 0x01DC9, 0x01DCA, 0x01DCB, 0x01DCC, 0x01DCD, 0x01DCE, 0x01DCF, 0x01DD0, 0x01DD1, 0x01DD2, 0x01DD3, 0x01DD4, 0x01DD5, 0x01DD6, 0x01DD7, 0x01DD8, 0x01DD9, 0x01DDA, 0x01DDB, 0x01DDC, 0x01DDD, 0x01DDE, 0x01DDF, 0x01DE0, 0x01DE1, 0x01DE2, 0x01DE3, 0x01DE4, 0x01DE5, 0x01DE6, 0x01DE7, 0x01DE8, 0x01DE9, 0x01DEA, 0x01DEB, 0x01DEC, 0x01DED, 0x01DEE, 0x01DEF, 0x01DF0, 0x01DF1, 0x01DF2, 0x01DF3, 0x01DF4, 0x01DF5, 0x01DFB, 0x01DFC, 0x01DFD, 0x01DFE, 0x01DFF, 0x0200C, 0x020D0, 0x020D1, 0x020D2, 0x020D3, 0x020D4, 0x020D5, 0x020D6, 0x020D7, 0x020D8, 0x020D9, 0x020DA, 0x020DB, 0x020DC, 0x020DD, 0x020DE, 0x020DF, 0x020E0, 0x020E1, 0x020E2, 0x020E3, 0x020E4, 0x020E5, 0x020E6, 0x020E7, 0x020E8, 0x020E9, 0x020EA, 0x020EB, 0x020EC, 0x020ED, 0x020EE, 0x020EF, 0x020F0, 0x02CEF, 0x02CF0, 0x02CF1, 0x02D7F, 0x02DE0, 0x02DE1, 0x02DE2, 0x02DE3, 0x02DE4, 0x02DE5, 0x02DE6, 0x02DE7, 0x02DE8, 0x02DE9, 0x02DEA, 0x02DEB, 0x02DEC, 0x02DED, 0x02DEE, 0x02DEF, 0x02DF0, 0x02DF1, 0x02DF2, 0x02DF3, 0x02DF4, 0x02DF5, 0x02DF6, 0x02DF7, 0x02DF8, 0x02DF9, 0x02DFA, 0x02DFB, 0x02DFC, 0x02DFD, 0x02DFE, 0x02DFF, 0x0302A, 0x0302B, 0x0302C, 0x0302D, 0x0302E, 0x0302F, 0x03099, 0x0309A, 0x0A66F, 0x0A670, 0x0A671, 0x0A672, 0x0A674, 0x0A675, 0x0A676, 0x0A677, 0x0A678, 0x0A679, 0x0A67A, 0x0A67B, 0x0A67C, 0x0A67D, 0x0A69E, 0x0A69F, 0x0A6F0, 0x0A6F1, 0x0A802, 0x0A806, 0x0A80B, 0x0A823, 0x0A824, 0x0A825, 0x0A826, 0x0A827, 0x0A880, 0x0A881, 0x0A8B4, 0x0A8B5, 0x0A8B6, 0x0A8B7, 0x0A8B8, 0x0A8B9, 0x0A8BA, 0x0A8BB, 0x0A8BC, 0x0A8BD, 0x0A8BE, 0x0A8BF, 0x0A8C0, 0x0A8C1, 0x0A8C2, 0x0A8C3, 0x0A8C4, 0x0A8C5, 0x0A8E0, 0x0A8E1, 0x0A8E2, 0x0A8E3, 0x0A8E4, 0x0A8E5, 0x0A8E6, 0x0A8E7, 0x0A8E8, 0x0A8E9, 0x0A8EA, 0x0A8EB, 0x0A8EC, 0x0A8ED, 0x0A8EE, 0x0A8EF, 0x0A8F0, 0x0A8F1, 0x0A926, 0x0A927, 0x0A928, 0x0A929, 0x0A92A, 0x0A92B, 0x0A92C, 0x0A92D, 0x0A947, 0x0A948, 0x0A949, 0x0A94A, 0x0A94B, 0x0A94C, 0x0A94D, 0x0A94E, 0x0A94F, 0x0A950, 0x0A951, 0x0A952, 0x0A953, 0x0A980, 0x0A981, 0x0A982, 0x0A983, 0x0A9B3, 0x0A9B4, 0x0A9B5, 0x0A9B6, 0x0A9B7, 0x0A9B8, 0x0A9B9, 0x0A9BA, 0x0A9BB, 0x0A9BC, 0x0A9BD, 0x0A9BE, 0x0A9BF, 0x0A9C0, 0x0A9E5, 0x0AA29, 0x0AA2A, 0x0AA2B, 0x0AA2C, 0x0AA2D, 0x0AA2E, 0x0AA2F, 0x0AA30, 0x0AA31, 0x0AA32, 0x0AA33, 0x0AA34, 0x0AA35, 0x0AA36, 0x0AA43, 0x0AA4C, 0x0AA4D, 0x0AA7B, 0x0AA7C, 0x0AA7D, 0x0AAB0, 0x0AAB2, 0x0AAB3, 0x0AAB4, 0x0AAB7, 0x0AAB8, 0x0AABE, 0x0AABF, 0x0AAC1, 0x0AAEB, 0x0AAEC, 0x0AAED, 0x0AAEE, 0x0AAEF, 0x0AAF5, 0x0AAF6, 0x0ABE3, 0x0ABE4, 0x0ABE5, 0x0ABE6, 0x0ABE7, 0x0ABE8, 0x0ABE9, 0x0ABEA, 0x0ABEC, 0x0ABED, 0x0FB1E, 0x0FE00, 0x0FE01, 0x0FE02, 0x0FE03, 0x0FE04, 0x0FE05, 0x0FE06, 0x0FE07, 0x0FE08, 0x0FE09, 0x0FE0A, 0x0FE0B, 0x0FE0C, 0x0FE0D, 0x0FE0E, 0x0FE0F, 0x0FE20, 0x0FE21, 0x0FE22, 0x0FE23, 0x0FE24, 0x0FE25, 0x0FE26, 0x0FE27, 0x0FE28, 0x0FE29, 0x0FE2A, 0x0FE2B, 0x0FE2C, 0x0FE2D, 0x0FE2E, 0x0FE2F, 0x0FF9E, 0x0FF9F, 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379, 0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D, 0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6, 0x11000, 0x11001, 0x11002, 0x11038, 0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, 0x11040, 0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x1107F, 0x11080, 0x11081, 0x11082, 0x110B0, 0x110B1, 0x110B2, 0x110B3, 0x110B4, 0x110B5, 0x110B6, 0x110B7, 0x110B8, 0x110B9, 0x110BA, 0x11100, 0x11101, 0x11102, 0x11127, 0x11128, 0x11129, 0x1112A, 0x1112B, 0x1112C, 0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, 0x11173, 0x11180, 0x11181, 0x11182, 0x111B3, 0x111B4, 0x111B5, 0x111B6, 0x111B7, 0x111B8, 0x111B9, 0x111BA, 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111BF, 0x111C0, 0x111CA, 0x111CB, 0x111CC, 0x1122C, 0x1122D, 0x1122E, 0x1122F, 0x11230, 0x11231, 0x11232, 0x11233, 0x11234, 0x11235, 0x11236, 0x11237, 0x1123E, 0x112DF, 0x112E0, 0x112E1, 0x112E2, 0x112E3, 0x112E4, 0x112E5, 0x112E6, 0x112E7, 0x112E8, 0x112E9, 0x112EA, 0x11300, 0x11301, 0x11302, 0x11303, 0x1133C, 0x1133E, 0x1133F, 0x11340, 0x11341, 0x11342, 0x11343, 0x11344, 0x11347, 0x11348, 0x1134B, 0x1134C, 0x1134D, 0x11357, 0x11362, 0x11363, 0x11366, 0x11367, 0x11368, 0x11369, 0x1136A, 0x1136B, 0x1136C, 0x11370, 0x11371, 0x11372, 0x11373, 0x11374, 0x11435, 0x11436, 0x11437, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E, 0x1143F, 0x11440, 0x11441, 0x11442, 0x11443, 0x11444, 0x11445, 0x11446, 0x114B0, 0x114B1, 0x114B2, 0x114B3, 0x114B4, 0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114B9, 0x114BA, 0x114BB, 0x114BC, 0x114BD, 0x114BE, 0x114BF, 0x114C0, 0x114C1, 0x114C2, 0x114C3, 0x115AF, 0x115B0, 0x115B1, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115B8, 0x115B9, 0x115BA, 0x115BB, 0x115BC, 0x115BD, 0x115BE, 0x115BF, 0x115C0, 0x115DC, 0x115DD, 0x11630, 0x11631, 0x11632, 0x11633, 0x11634, 0x11635, 0x11636, 0x11637, 0x11638, 0x11639, 0x1163A, 0x1163B, 0x1163C, 0x1163D, 0x1163E, 0x1163F, 0x11640, 0x116AB, 0x116AC, 0x116AD, 0x116AE, 0x116AF, 0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B6, 0x116B7, 0x1171D, 0x1171E, 0x1171F, 0x11720, 0x11721, 0x11722, 0x11723, 0x11724, 0x11725, 0x11726, 0x11727, 0x11728, 0x11729, 0x1172A, 0x1172B, 0x11C2F, 0x11C30, 0x11C31, 0x11C32, 0x11C33, 0x11C34, 0x11C35, 0x11C36, 0x11C38, 0x11C39, 0x11C3A, 0x11C3B, 0x11C3C, 0x11C3D, 0x11C3E, 0x11C3F, 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96, 0x11C97, 0x11C98, 0x11C99, 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E, 0x11C9F, 0x11CA0, 0x11CA1, 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, 0x11CA6, 0x11CA7, 0x11CA9, 0x11CAA, 0x11CAB, 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0, 0x11CB1, 0x11CB2, 0x11CB3, 0x11CB4, 0x11CB5, 0x11CB6, 0x16AF0, 0x16AF1, 0x16AF2, 0x16AF3, 0x16AF4, 0x16B30, 0x16B31, 0x16B32, 0x16B33, 0x16B34, 0x16B35, 0x16B36, 0x16F51, 0x16F52, 0x16F53, 0x16F54, 0x16F55, 0x16F56, 0x16F57, 0x16F58, 0x16F59, 0x16F5A, 0x16F5B, 0x16F5C, 0x16F5D, 0x16F5E, 0x16F5F, 0x16F60, 0x16F61, 0x16F62, 0x16F63, 0x16F64, 0x16F65, 0x16F66, 0x16F67, 0x16F68, 0x16F69, 0x16F6A, 0x16F6B, 0x16F6C, 0x16F6D, 0x16F6E, 0x16F6F, 0x16F70, 0x16F71, 0x16F72, 0x16F73, 0x16F74, 0x16F75, 0x16F76, 0x16F77, 0x16F78, 0x16F79, 0x16F7A, 0x16F7B, 0x16F7C, 0x16F7D, 0x16F7E, 0x16F8F, 0x16F90, 0x16F91, 0x16F92, 0x1BC9D, 0x1BC9E, 0x1D165, 0x1D166, 0x1D167, 0x1D168, 0x1D169, 0x1D16D, 0x1D16E, 0x1D16F, 0x1D170, 0x1D171, 0x1D172, 0x1D17B, 0x1D17C, 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182, 0x1D185, 0x1D186, 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B, 0x1D1AA, 0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01, 0x1DA02, 0x1DA03, 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09, 0x1DA0A, 0x1DA0B, 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, 0x1DA11, 0x1DA12, 0x1DA13, 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19, 0x1DA1A, 0x1DA1B, 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21, 0x1DA22, 0x1DA23, 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29, 0x1DA2A, 0x1DA2B, 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31, 0x1DA32, 0x1DA33, 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C, 0x1DA3D, 0x1DA3E, 0x1DA3F, 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45, 0x1DA46, 0x1DA47, 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D, 0x1DA4E, 0x1DA4F, 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55, 0x1DA56, 0x1DA57, 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D, 0x1DA5E, 0x1DA5F, 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64, 0x1DA65, 0x1DA66, 0x1DA67, 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75, 0x1DA84, 0x1DA9B, 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2, 0x1DAA3, 0x1DAA4, 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, 0x1DAAA, 0x1DAAB, 0x1DAAC, 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002, 0x1E003, 0x1E004, 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A, 0x1E00B, 0x1E00C, 0x1E00D, 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013, 0x1E014, 0x1E015, 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D, 0x1E01E, 0x1E01F, 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E027, 0x1E028, 0x1E029, 0x1E02A, 0x1E8D0, 0x1E8D1, 0x1E8D2, 0x1E8D3, 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946, 0x1E947, 0x1E948, 0x1E949, 0x1E94A, 0xE0020, 0xE0021, 0xE0022, 0xE0023, 0xE0024, 0xE0025, 0xE0026, 0xE0027, 0xE0028, 0xE0029, 0xE002A, 0xE002B, 0xE002C, 0xE002D, 0xE002E, 0xE002F, 0xE0030, 0xE0031, 0xE0032, 0xE0033, 0xE0034, 0xE0035, 0xE0036, 0xE0037, 0xE0038, 0xE0039, 0xE003A, 0xE003B, 0xE003C, 0xE003D, 0xE003E, 0xE003F, 0xE0040, 0xE0041, 0xE0042, 0xE0043, 0xE0044, 0xE0045, 0xE0046, 0xE0047, 0xE0048, 0xE0049, 0xE004A, 0xE004B, 0xE004C, 0xE004D, 0xE004E, 0xE004F, 0xE0050, 0xE0051, 0xE0052, 0xE0053, 0xE0054, 0xE0055, 0xE0056, 0xE0057, 0xE0058, 0xE0059, 0xE005A, 0xE005B, 0xE005C, 0xE005D, 0xE005E, 0xE005F, 0xE0060, 0xE0061, 0xE0062, 0xE0063, 0xE0064, 0xE0065, 0xE0066, 0xE0067, 0xE0068, 0xE0069, 0xE006A, 0xE006B, 0xE006C, 0xE006D, 0xE006E, 0xE006F, 0xE0070, 0xE0071, 0xE0072, 0xE0073, 0xE0074, 0xE0075, 0xE0076, 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F, 0xE0100, 0xE0101, 0xE0102, 0xE0103, 0xE0104, 0xE0105, 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B, 0xE010C, 0xE010D, 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113, 0xE0114, 0xE0115, 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, 0xE011B, 0xE011C, 0xE011D, 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123, 0xE0124, 0xE0125, 0xE0126, 0xE0127, 0xE0128, 0xE0129, 0xE012A, 0xE012B, 0xE012C, 0xE012D, 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133, 0xE0134, 0xE0135, 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B, 0xE013C, 0xE013D, 0xE013E, 0xE013F, 0xE0140, 0xE0141, 0xE0142, 0xE0143, 0xE0144, 0xE0145, 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B, 0xE014C, 0xE014D, 0xE014E, 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153, 0xE0154, 0xE0155, 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B, 0xE015C, 0xE015D, 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163, 0xE0164, 0xE0165, 0xE0166, 0xE0167, 0xE0168, 0xE0169, 0xE016A, 0xE016B, 0xE016C, 0xE016D, 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173, 0xE0174, 0xE0175, 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B, 0xE017C, 0xE017D, 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, 0xE0183, 0xE0184, 0xE0185, 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B, 0xE018C, 0xE018D, 0xE018E, 0xE018F, 0xE0190, 0xE0191, 0xE0192, 0xE0193, 0xE0194, 0xE0195, 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B, 0xE019C, 0xE019D, 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3, 0xE01A4, 0xE01A5, 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, 0xE01AA, 0xE01AB, 0xE01AC, 0xE01AD, 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3, 0xE01B4, 0xE01B5, 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB, 0xE01BC, 0xE01BD, 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3, 0xE01C4, 0xE01C5, 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB, 0xE01CC, 0xE01CD, 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1, 0xE01D2, 0xE01D3, 0xE01D4, 0xE01D5, 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB, 0xE01DC, 0xE01DD, 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3, 0xE01E4, 0xE01E5, 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, 0xE01EB, 0xE01EC, 0xE01ED, 0xE01EE, 0xE01EF }; static const uint32_t Regional_Indicator[]= { 0x1F1E6, 0x1F1E7, 0x1F1E8, 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1ED, 0x1F1EE, 0x1F1EF, 0x1F1F0, 0x1F1F1, 0x1F1F2, 0x1F1F3, 0x1F1F4, 0x1F1F5, 0x1F1F6, 0x1F1F7, 0x1F1F8, 0x1F1F9, 0x1F1FA, 0x1F1FB, 0x1F1FC, 0x1F1FD, 0x1F1FE, 0x1F1FF }; static const uint32_t Format[]= { 0x000AD, 0x00600, 0x00601, 0x00602, 0x00603, 0x00604, 0x00605, 0x0061C, 0x006DD, 0x0070F, 0x008E2, 0x0180E, 0x0200E, 0x0200F, 0x0202A, 0x0202B, 0x0202C, 0x0202D, 0x0202E, 0x02060, 0x02061, 0x02062, 0x02063, 0x02064, 0x02066, 0x02067, 0x02068, 0x02069, 0x0206A, 0x0206B, 0x0206C, 0x0206D, 0x0206E, 0x0206F, 0x0FEFF, 0x0FFF9, 0x0FFFA, 0x0FFFB, 0x110BD, 0x1BCA0, 0x1BCA1, 0x1BCA2, 0x1BCA3, 0x1D173, 0x1D174, 0x1D175, 0x1D176, 0x1D177, 0x1D178, 0x1D179, 0x1D17A, 0xE0001 }; static const uint32_t Katakana[]= { 0x03031, 0x03032, 0x03033, 0x03034, 0x03035, 0x0309B, 0x0309C, 0x030A0, 0x030A1, 0x030A2, 0x030A3, 0x030A4, 0x030A5, 0x030A6, 0x030A7, 0x030A8, 0x030A9, 0x030AA, 0x030AB, 0x030AC, 0x030AD, 0x030AE, 0x030AF, 0x030B0, 0x030B1, 0x030B2, 0x030B3, 0x030B4, 0x030B5, 0x030B6, 0x030B7, 0x030B8, 0x030B9, 0x030BA, 0x030BB, 0x030BC, 0x030BD, 0x030BE, 0x030BF, 0x030C0, 0x030C1, 0x030C2, 0x030C3, 0x030C4, 0x030C5, 0x030C6, 0x030C7, 0x030C8, 0x030C9, 0x030CA, 0x030CB, 0x030CC, 0x030CD, 0x030CE, 0x030CF, 0x030D0, 0x030D1, 0x030D2, 0x030D3, 0x030D4, 0x030D5, 0x030D6, 0x030D7, 0x030D8, 0x030D9, 0x030DA, 0x030DB, 0x030DC, 0x030DD, 0x030DE, 0x030DF, 0x030E0, 0x030E1, 0x030E2, 0x030E3, 0x030E4, 0x030E5, 0x030E6, 0x030E7, 0x030E8, 0x030E9, 0x030EA, 0x030EB, 0x030EC, 0x030ED, 0x030EE, 0x030EF, 0x030F0, 0x030F1, 0x030F2, 0x030F3, 0x030F4, 0x030F5, 0x030F6, 0x030F7, 0x030F8, 0x030F9, 0x030FA, 0x030FC, 0x030FD, 0x030FE, 0x030FF, 0x031F0, 0x031F1, 0x031F2, 0x031F3, 0x031F4, 0x031F5, 0x031F6, 0x031F7, 0x031F8, 0x031F9, 0x031FA, 0x031FB, 0x031FC, 0x031FD, 0x031FE, 0x031FF, 0x032D0, 0x032D1, 0x032D2, 0x032D3, 0x032D4, 0x032D5, 0x032D6, 0x032D7, 0x032D8, 0x032D9, 0x032DA, 0x032DB, 0x032DC, 0x032DD, 0x032DE, 0x032DF, 0x032E0, 0x032E1, 0x032E2, 0x032E3, 0x032E4, 0x032E5, 0x032E6, 0x032E7, 0x032E8, 0x032E9, 0x032EA, 0x032EB, 0x032EC, 0x032ED, 0x032EE, 0x032EF, 0x032F0, 0x032F1, 0x032F2, 0x032F3, 0x032F4, 0x032F5, 0x032F6, 0x032F7, 0x032F8, 0x032F9, 0x032FA, 0x032FB, 0x032FC, 0x032FD, 0x032FE, 0x03300, 0x03301, 0x03302, 0x03303, 0x03304, 0x03305, 0x03306, 0x03307, 0x03308, 0x03309, 0x0330A, 0x0330B, 0x0330C, 0x0330D, 0x0330E, 0x0330F, 0x03310, 0x03311, 0x03312, 0x03313, 0x03314, 0x03315, 0x03316, 0x03317, 0x03318, 0x03319, 0x0331A, 0x0331B, 0x0331C, 0x0331D, 0x0331E, 0x0331F, 0x03320, 0x03321, 0x03322, 0x03323, 0x03324, 0x03325, 0x03326, 0x03327, 0x03328, 0x03329, 0x0332A, 0x0332B, 0x0332C, 0x0332D, 0x0332E, 0x0332F, 0x03330, 0x03331, 0x03332, 0x03333, 0x03334, 0x03335, 0x03336, 0x03337, 0x03338, 0x03339, 0x0333A, 0x0333B, 0x0333C, 0x0333D, 0x0333E, 0x0333F, 0x03340, 0x03341, 0x03342, 0x03343, 0x03344, 0x03345, 0x03346, 0x03347, 0x03348, 0x03349, 0x0334A, 0x0334B, 0x0334C, 0x0334D, 0x0334E, 0x0334F, 0x03350, 0x03351, 0x03352, 0x03353, 0x03354, 0x03355, 0x03356, 0x03357, 0x0FF66, 0x0FF67, 0x0FF68, 0x0FF69, 0x0FF6A, 0x0FF6B, 0x0FF6C, 0x0FF6D, 0x0FF6E, 0x0FF6F, 0x0FF70, 0x0FF71, 0x0FF72, 0x0FF73, 0x0FF74, 0x0FF75, 0x0FF76, 0x0FF77, 0x0FF78, 0x0FF79, 0x0FF7A, 0x0FF7B, 0x0FF7C, 0x0FF7D, 0x0FF7E, 0x0FF7F, 0x0FF80, 0x0FF81, 0x0FF82, 0x0FF83, 0x0FF84, 0x0FF85, 0x0FF86, 0x0FF87, 0x0FF88, 0x0FF89, 0x0FF8A, 0x0FF8B, 0x0FF8C, 0x0FF8D, 0x0FF8E, 0x0FF8F, 0x0FF90, 0x0FF91, 0x0FF92, 0x0FF93, 0x0FF94, 0x0FF95, 0x0FF96, 0x0FF97, 0x0FF98, 0x0FF99, 0x0FF9A, 0x0FF9B, 0x0FF9C, 0x0FF9D, 0x1B000 }; static const uint32_t Hebrew_Letter[]= { 0x005D0, 0x005D1, 0x005D2, 0x005D3, 0x005D4, 0x005D5, 0x005D6, 0x005D7, 0x005D8, 0x005D9, 0x005DA, 0x005DB, 0x005DC, 0x005DD, 0x005DE, 0x005DF, 0x005E0, 0x005E1, 0x005E2, 0x005E3, 0x005E4, 0x005E5, 0x005E6, 0x005E7, 0x005E8, 0x005E9, 0x005EA, 0x005F0, 0x005F1, 0x005F2, 0x0FB1D, 0x0FB1F, 0x0FB20, 0x0FB21, 0x0FB22, 0x0FB23, 0x0FB24, 0x0FB25, 0x0FB26, 0x0FB27, 0x0FB28, 0x0FB2A, 0x0FB2B, 0x0FB2C, 0x0FB2D, 0x0FB2E, 0x0FB2F, 0x0FB30, 0x0FB31, 0x0FB32, 0x0FB33, 0x0FB34, 0x0FB35, 0x0FB36, 0x0FB38, 0x0FB39, 0x0FB3A, 0x0FB3B, 0x0FB3C, 0x0FB3E, 0x0FB40, 0x0FB41, 0x0FB43, 0x0FB44, 0x0FB46, 0x0FB47, 0x0FB48, 0x0FB49, 0x0FB4A, 0x0FB4B, 0x0FB4C, 0x0FB4D, 0x0FB4E, 0x0FB4F }; static const uint32_t ALetter[]= { 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004A, 0x0004B, 0x0004C, 0x0004D, 0x0004E, 0x0004F, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005A, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006A, 0x0006B, 0x0006C, 0x0006D, 0x0006E, 0x0006F, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007A, 0x000AA, 0x000B5, 0x000BA, 0x000C0, 0x000C1, 0x000C2, 0x000C3, 0x000C4, 0x000C5, 0x000C6, 0x000C7, 0x000C8, 0x000C9, 0x000CA, 0x000CB, 0x000CC, 0x000CD, 0x000CE, 0x000CF, 0x000D0, 0x000D1, 0x000D2, 0x000D3, 0x000D4, 0x000D5, 0x000D6, 0x000D8, 0x000D9, 0x000DA, 0x000DB, 0x000DC, 0x000DD, 0x000DE, 0x000DF, 0x000E0, 0x000E1, 0x000E2, 0x000E3, 0x000E4, 0x000E5, 0x000E6, 0x000E7, 0x000E8, 0x000E9, 0x000EA, 0x000EB, 0x000EC, 0x000ED, 0x000EE, 0x000EF, 0x000F0, 0x000F1, 0x000F2, 0x000F3, 0x000F4, 0x000F5, 0x000F6, 0x000F8, 0x000F9, 0x000FA, 0x000FB, 0x000FC, 0x000FD, 0x000FE, 0x000FF, 0x00100, 0x00101, 0x00102, 0x00103, 0x00104, 0x00105, 0x00106, 0x00107, 0x00108, 0x00109, 0x0010A, 0x0010B, 0x0010C, 0x0010D, 0x0010E, 0x0010F, 0x00110, 0x00111, 0x00112, 0x00113, 0x00114, 0x00115, 0x00116, 0x00117, 0x00118, 0x00119, 0x0011A, 0x0011B, 0x0011C, 0x0011D, 0x0011E, 0x0011F, 0x00120, 0x00121, 0x00122, 0x00123, 0x00124, 0x00125, 0x00126, 0x00127, 0x00128, 0x00129, 0x0012A, 0x0012B, 0x0012C, 0x0012D, 0x0012E, 0x0012F, 0x00130, 0x00131, 0x00132, 0x00133, 0x00134, 0x00135, 0x00136, 0x00137, 0x00138, 0x00139, 0x0013A, 0x0013B, 0x0013C, 0x0013D, 0x0013E, 0x0013F, 0x00140, 0x00141, 0x00142, 0x00143, 0x00144, 0x00145, 0x00146, 0x00147, 0x00148, 0x00149, 0x0014A, 0x0014B, 0x0014C, 0x0014D, 0x0014E, 0x0014F, 0x00150, 0x00151, 0x00152, 0x00153, 0x00154, 0x00155, 0x00156, 0x00157, 0x00158, 0x00159, 0x0015A, 0x0015B, 0x0015C, 0x0015D, 0x0015E, 0x0015F, 0x00160, 0x00161, 0x00162, 0x00163, 0x00164, 0x00165, 0x00166, 0x00167, 0x00168, 0x00169, 0x0016A, 0x0016B, 0x0016C, 0x0016D, 0x0016E, 0x0016F, 0x00170, 0x00171, 0x00172, 0x00173, 0x00174, 0x00175, 0x00176, 0x00177, 0x00178, 0x00179, 0x0017A, 0x0017B, 0x0017C, 0x0017D, 0x0017E, 0x0017F, 0x00180, 0x00181, 0x00182, 0x00183, 0x00184, 0x00185, 0x00186, 0x00187, 0x00188, 0x00189, 0x0018A, 0x0018B, 0x0018C, 0x0018D, 0x0018E, 0x0018F, 0x00190, 0x00191, 0x00192, 0x00193, 0x00194, 0x00195, 0x00196, 0x00197, 0x00198, 0x00199, 0x0019A, 0x0019B, 0x0019C, 0x0019D, 0x0019E, 0x0019F, 0x001A0, 0x001A1, 0x001A2, 0x001A3, 0x001A4, 0x001A5, 0x001A6, 0x001A7, 0x001A8, 0x001A9, 0x001AA, 0x001AB, 0x001AC, 0x001AD, 0x001AE, 0x001AF, 0x001B0, 0x001B1, 0x001B2, 0x001B3, 0x001B4, 0x001B5, 0x001B6, 0x001B7, 0x001B8, 0x001B9, 0x001BA, 0x001BB, 0x001BC, 0x001BD, 0x001BE, 0x001BF, 0x001C0, 0x001C1, 0x001C2, 0x001C3, 0x001C4, 0x001C5, 0x001C6, 0x001C7, 0x001C8, 0x001C9, 0x001CA, 0x001CB, 0x001CC, 0x001CD, 0x001CE, 0x001CF, 0x001D0, 0x001D1, 0x001D2, 0x001D3, 0x001D4, 0x001D5, 0x001D6, 0x001D7, 0x001D8, 0x001D9, 0x001DA, 0x001DB, 0x001DC, 0x001DD, 0x001DE, 0x001DF, 0x001E0, 0x001E1, 0x001E2, 0x001E3, 0x001E4, 0x001E5, 0x001E6, 0x001E7, 0x001E8, 0x001E9, 0x001EA, 0x001EB, 0x001EC, 0x001ED, 0x001EE, 0x001EF, 0x001F0, 0x001F1, 0x001F2, 0x001F3, 0x001F4, 0x001F5, 0x001F6, 0x001F7, 0x001F8, 0x001F9, 0x001FA, 0x001FB, 0x001FC, 0x001FD, 0x001FE, 0x001FF, 0x00200, 0x00201, 0x00202, 0x00203, 0x00204, 0x00205, 0x00206, 0x00207, 0x00208, 0x00209, 0x0020A, 0x0020B, 0x0020C, 0x0020D, 0x0020E, 0x0020F, 0x00210, 0x00211, 0x00212, 0x00213, 0x00214, 0x00215, 0x00216, 0x00217, 0x00218, 0x00219, 0x0021A, 0x0021B, 0x0021C, 0x0021D, 0x0021E, 0x0021F, 0x00220, 0x00221, 0x00222, 0x00223, 0x00224, 0x00225, 0x00226, 0x00227, 0x00228, 0x00229, 0x0022A, 0x0022B, 0x0022C, 0x0022D, 0x0022E, 0x0022F, 0x00230, 0x00231, 0x00232, 0x00233, 0x00234, 0x00235, 0x00236, 0x00237, 0x00238, 0x00239, 0x0023A, 0x0023B, 0x0023C, 0x0023D, 0x0023E, 0x0023F, 0x00240, 0x00241, 0x00242, 0x00243, 0x00244, 0x00245, 0x00246, 0x00247, 0x00248, 0x00249, 0x0024A, 0x0024B, 0x0024C, 0x0024D, 0x0024E, 0x0024F, 0x00250, 0x00251, 0x00252, 0x00253, 0x00254, 0x00255, 0x00256, 0x00257, 0x00258, 0x00259, 0x0025A, 0x0025B, 0x0025C, 0x0025D, 0x0025E, 0x0025F, 0x00260, 0x00261, 0x00262, 0x00263, 0x00264, 0x00265, 0x00266, 0x00267, 0x00268, 0x00269, 0x0026A, 0x0026B, 0x0026C, 0x0026D, 0x0026E, 0x0026F, 0x00270, 0x00271, 0x00272, 0x00273, 0x00274, 0x00275, 0x00276, 0x00277, 0x00278, 0x00279, 0x0027A, 0x0027B, 0x0027C, 0x0027D, 0x0027E, 0x0027F, 0x00280, 0x00281, 0x00282, 0x00283, 0x00284, 0x00285, 0x00286, 0x00287, 0x00288, 0x00289, 0x0028A, 0x0028B, 0x0028C, 0x0028D, 0x0028E, 0x0028F, 0x00290, 0x00291, 0x00292, 0x00293, 0x00294, 0x00295, 0x00296, 0x00297, 0x00298, 0x00299, 0x0029A, 0x0029B, 0x0029C, 0x0029D, 0x0029E, 0x0029F, 0x002A0, 0x002A1, 0x002A2, 0x002A3, 0x002A4, 0x002A5, 0x002A6, 0x002A7, 0x002A8, 0x002A9, 0x002AA, 0x002AB, 0x002AC, 0x002AD, 0x002AE, 0x002AF, 0x002B0, 0x002B1, 0x002B2, 0x002B3, 0x002B4, 0x002B5, 0x002B6, 0x002B7, 0x002B8, 0x002B9, 0x002BA, 0x002BB, 0x002BC, 0x002BD, 0x002BE, 0x002BF, 0x002C0, 0x002C1, 0x002C6, 0x002C7, 0x002C8, 0x002C9, 0x002CA, 0x002CB, 0x002CC, 0x002CD, 0x002CE, 0x002CF, 0x002D0, 0x002D1, 0x002E0, 0x002E1, 0x002E2, 0x002E3, 0x002E4, 0x002EC, 0x002EE, 0x00370, 0x00371, 0x00372, 0x00373, 0x00374, 0x00376, 0x00377, 0x0037A, 0x0037B, 0x0037C, 0x0037D, 0x0037F, 0x00386, 0x00388, 0x00389, 0x0038A, 0x0038C, 0x0038E, 0x0038F, 0x00390, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039A, 0x0039B, 0x0039C, 0x0039D, 0x0039E, 0x0039F, 0x003A0, 0x003A1, 0x003A3, 0x003A4, 0x003A5, 0x003A6, 0x003A7, 0x003A8, 0x003A9, 0x003AA, 0x003AB, 0x003AC, 0x003AD, 0x003AE, 0x003AF, 0x003B0, 0x003B1, 0x003B2, 0x003B3, 0x003B4, 0x003B5, 0x003B6, 0x003B7, 0x003B8, 0x003B9, 0x003BA, 0x003BB, 0x003BC, 0x003BD, 0x003BE, 0x003BF, 0x003C0, 0x003C1, 0x003C2, 0x003C3, 0x003C4, 0x003C5, 0x003C6, 0x003C7, 0x003C8, 0x003C9, 0x003CA, 0x003CB, 0x003CC, 0x003CD, 0x003CE, 0x003CF, 0x003D0, 0x003D1, 0x003D2, 0x003D3, 0x003D4, 0x003D5, 0x003D6, 0x003D7, 0x003D8, 0x003D9, 0x003DA, 0x003DB, 0x003DC, 0x003DD, 0x003DE, 0x003DF, 0x003E0, 0x003E1, 0x003E2, 0x003E3, 0x003E4, 0x003E5, 0x003E6, 0x003E7, 0x003E8, 0x003E9, 0x003EA, 0x003EB, 0x003EC, 0x003ED, 0x003EE, 0x003EF, 0x003F0, 0x003F1, 0x003F2, 0x003F3, 0x003F4, 0x003F5, 0x003F7, 0x003F8, 0x003F9, 0x003FA, 0x003FB, 0x003FC, 0x003FD, 0x003FE, 0x003FF, 0x00400, 0x00401, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407, 0x00408, 0x00409, 0x0040A, 0x0040B, 0x0040C, 0x0040D, 0x0040E, 0x0040F, 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417, 0x00418, 0x00419, 0x0041A, 0x0041B, 0x0041C, 0x0041D, 0x0041E, 0x0041F, 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427, 0x00428, 0x00429, 0x0042A, 0x0042B, 0x0042C, 0x0042D, 0x0042E, 0x0042F, 0x00430, 0x00431, 0x00432, 0x00433, 0x00434, 0x00435, 0x00436, 0x00437, 0x00438, 0x00439, 0x0043A, 0x0043B, 0x0043C, 0x0043D, 0x0043E, 0x0043F, 0x00440, 0x00441, 0x00442, 0x00443, 0x00444, 0x00445, 0x00446, 0x00447, 0x00448, 0x00449, 0x0044A, 0x0044B, 0x0044C, 0x0044D, 0x0044E, 0x0044F, 0x00450, 0x00451, 0x00452, 0x00453, 0x00454, 0x00455, 0x00456, 0x00457, 0x00458, 0x00459, 0x0045A, 0x0045B, 0x0045C, 0x0045D, 0x0045E, 0x0045F, 0x00460, 0x00461, 0x00462, 0x00463, 0x00464, 0x00465, 0x00466, 0x00467, 0x00468, 0x00469, 0x0046A, 0x0046B, 0x0046C, 0x0046D, 0x0046E, 0x0046F, 0x00470, 0x00471, 0x00472, 0x00473, 0x00474, 0x00475, 0x00476, 0x00477, 0x00478, 0x00479, 0x0047A, 0x0047B, 0x0047C, 0x0047D, 0x0047E, 0x0047F, 0x00480, 0x00481, 0x0048A, 0x0048B, 0x0048C, 0x0048D, 0x0048E, 0x0048F, 0x00490, 0x00491, 0x00492, 0x00493, 0x00494, 0x00495, 0x00496, 0x00497, 0x00498, 0x00499, 0x0049A, 0x0049B, 0x0049C, 0x0049D, 0x0049E, 0x0049F, 0x004A0, 0x004A1, 0x004A2, 0x004A3, 0x004A4, 0x004A5, 0x004A6, 0x004A7, 0x004A8, 0x004A9, 0x004AA, 0x004AB, 0x004AC, 0x004AD, 0x004AE, 0x004AF, 0x004B0, 0x004B1, 0x004B2, 0x004B3, 0x004B4, 0x004B5, 0x004B6, 0x004B7, 0x004B8, 0x004B9, 0x004BA, 0x004BB, 0x004BC, 0x004BD, 0x004BE, 0x004BF, 0x004C0, 0x004C1, 0x004C2, 0x004C3, 0x004C4, 0x004C5, 0x004C6, 0x004C7, 0x004C8, 0x004C9, 0x004CA, 0x004CB, 0x004CC, 0x004CD, 0x004CE, 0x004CF, 0x004D0, 0x004D1, 0x004D2, 0x004D3, 0x004D4, 0x004D5, 0x004D6, 0x004D7, 0x004D8, 0x004D9, 0x004DA, 0x004DB, 0x004DC, 0x004DD, 0x004DE, 0x004DF, 0x004E0, 0x004E1, 0x004E2, 0x004E3, 0x004E4, 0x004E5, 0x004E6, 0x004E7, 0x004E8, 0x004E9, 0x004EA, 0x004EB, 0x004EC, 0x004ED, 0x004EE, 0x004EF, 0x004F0, 0x004F1, 0x004F2, 0x004F3, 0x004F4, 0x004F5, 0x004F6, 0x004F7, 0x004F8, 0x004F9, 0x004FA, 0x004FB, 0x004FC, 0x004FD, 0x004FE, 0x004FF, 0x00500, 0x00501, 0x00502, 0x00503, 0x00504, 0x00505, 0x00506, 0x00507, 0x00508, 0x00509, 0x0050A, 0x0050B, 0x0050C, 0x0050D, 0x0050E, 0x0050F, 0x00510, 0x00511, 0x00512, 0x00513, 0x00514, 0x00515, 0x00516, 0x00517, 0x00518, 0x00519, 0x0051A, 0x0051B, 0x0051C, 0x0051D, 0x0051E, 0x0051F, 0x00520, 0x00521, 0x00522, 0x00523, 0x00524, 0x00525, 0x00526, 0x00527, 0x00528, 0x00529, 0x0052A, 0x0052B, 0x0052C, 0x0052D, 0x0052E, 0x0052F, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537, 0x00538, 0x00539, 0x0053A, 0x0053B, 0x0053C, 0x0053D, 0x0053E, 0x0053F, 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547, 0x00548, 0x00549, 0x0054A, 0x0054B, 0x0054C, 0x0054D, 0x0054E, 0x0054F, 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00559, 0x00561, 0x00562, 0x00563, 0x00564, 0x00565, 0x00566, 0x00567, 0x00568, 0x00569, 0x0056A, 0x0056B, 0x0056C, 0x0056D, 0x0056E, 0x0056F, 0x00570, 0x00571, 0x00572, 0x00573, 0x00574, 0x00575, 0x00576, 0x00577, 0x00578, 0x00579, 0x0057A, 0x0057B, 0x0057C, 0x0057D, 0x0057E, 0x0057F, 0x00580, 0x00581, 0x00582, 0x00583, 0x00584, 0x00585, 0x00586, 0x00587, 0x005F3, 0x00620, 0x00621, 0x00622, 0x00623, 0x00624, 0x00625, 0x00626, 0x00627, 0x00628, 0x00629, 0x0062A, 0x0062B, 0x0062C, 0x0062D, 0x0062E, 0x0062F, 0x00630, 0x00631, 0x00632, 0x00633, 0x00634, 0x00635, 0x00636, 0x00637, 0x00638, 0x00639, 0x0063A, 0x0063B, 0x0063C, 0x0063D, 0x0063E, 0x0063F, 0x00640, 0x00641, 0x00642, 0x00643, 0x00644, 0x00645, 0x00646, 0x00647, 0x00648, 0x00649, 0x0064A, 0x0066E, 0x0066F, 0x00671, 0x00672, 0x00673, 0x00674, 0x00675, 0x00676, 0x00677, 0x00678, 0x00679, 0x0067A, 0x0067B, 0x0067C, 0x0067D, 0x0067E, 0x0067F, 0x00680, 0x00681, 0x00682, 0x00683, 0x00684, 0x00685, 0x00686, 0x00687, 0x00688, 0x00689, 0x0068A, 0x0068B, 0x0068C, 0x0068D, 0x0068E, 0x0068F, 0x00690, 0x00691, 0x00692, 0x00693, 0x00694, 0x00695, 0x00696, 0x00697, 0x00698, 0x00699, 0x0069A, 0x0069B, 0x0069C, 0x0069D, 0x0069E, 0x0069F, 0x006A0, 0x006A1, 0x006A2, 0x006A3, 0x006A4, 0x006A5, 0x006A6, 0x006A7, 0x006A8, 0x006A9, 0x006AA, 0x006AB, 0x006AC, 0x006AD, 0x006AE, 0x006AF, 0x006B0, 0x006B1, 0x006B2, 0x006B3, 0x006B4, 0x006B5, 0x006B6, 0x006B7, 0x006B8, 0x006B9, 0x006BA, 0x006BB, 0x006BC, 0x006BD, 0x006BE, 0x006BF, 0x006C0, 0x006C1, 0x006C2, 0x006C3, 0x006C4, 0x006C5, 0x006C6, 0x006C7, 0x006C8, 0x006C9, 0x006CA, 0x006CB, 0x006CC, 0x006CD, 0x006CE, 0x006CF, 0x006D0, 0x006D1, 0x006D2, 0x006D3, 0x006D5, 0x006E5, 0x006E6, 0x006EE, 0x006EF, 0x006FA, 0x006FB, 0x006FC, 0x006FF, 0x00710, 0x00712, 0x00713, 0x00714, 0x00715, 0x00716, 0x00717, 0x00718, 0x00719, 0x0071A, 0x0071B, 0x0071C, 0x0071D, 0x0071E, 0x0071F, 0x00720, 0x00721, 0x00722, 0x00723, 0x00724, 0x00725, 0x00726, 0x00727, 0x00728, 0x00729, 0x0072A, 0x0072B, 0x0072C, 0x0072D, 0x0072E, 0x0072F, 0x0074D, 0x0074E, 0x0074F, 0x00750, 0x00751, 0x00752, 0x00753, 0x00754, 0x00755, 0x00756, 0x00757, 0x00758, 0x00759, 0x0075A, 0x0075B, 0x0075C, 0x0075D, 0x0075E, 0x0075F, 0x00760, 0x00761, 0x00762, 0x00763, 0x00764, 0x00765, 0x00766, 0x00767, 0x00768, 0x00769, 0x0076A, 0x0076B, 0x0076C, 0x0076D, 0x0076E, 0x0076F, 0x00770, 0x00771, 0x00772, 0x00773, 0x00774, 0x00775, 0x00776, 0x00777, 0x00778, 0x00779, 0x0077A, 0x0077B, 0x0077C, 0x0077D, 0x0077E, 0x0077F, 0x00780, 0x00781, 0x00782, 0x00783, 0x00784, 0x00785, 0x00786, 0x00787, 0x00788, 0x00789, 0x0078A, 0x0078B, 0x0078C, 0x0078D, 0x0078E, 0x0078F, 0x00790, 0x00791, 0x00792, 0x00793, 0x00794, 0x00795, 0x00796, 0x00797, 0x00798, 0x00799, 0x0079A, 0x0079B, 0x0079C, 0x0079D, 0x0079E, 0x0079F, 0x007A0, 0x007A1, 0x007A2, 0x007A3, 0x007A4, 0x007A5, 0x007B1, 0x007CA, 0x007CB, 0x007CC, 0x007CD, 0x007CE, 0x007CF, 0x007D0, 0x007D1, 0x007D2, 0x007D3, 0x007D4, 0x007D5, 0x007D6, 0x007D7, 0x007D8, 0x007D9, 0x007DA, 0x007DB, 0x007DC, 0x007DD, 0x007DE, 0x007DF, 0x007E0, 0x007E1, 0x007E2, 0x007E3, 0x007E4, 0x007E5, 0x007E6, 0x007E7, 0x007E8, 0x007E9, 0x007EA, 0x007F4, 0x007F5, 0x007FA, 0x00800, 0x00801, 0x00802, 0x00803, 0x00804, 0x00805, 0x00806, 0x00807, 0x00808, 0x00809, 0x0080A, 0x0080B, 0x0080C, 0x0080D, 0x0080E, 0x0080F, 0x00810, 0x00811, 0x00812, 0x00813, 0x00814, 0x00815, 0x0081A, 0x00824, 0x00828, 0x00840, 0x00841, 0x00842, 0x00843, 0x00844, 0x00845, 0x00846, 0x00847, 0x00848, 0x00849, 0x0084A, 0x0084B, 0x0084C, 0x0084D, 0x0084E, 0x0084F, 0x00850, 0x00851, 0x00852, 0x00853, 0x00854, 0x00855, 0x00856, 0x00857, 0x00858, 0x008A0, 0x008A1, 0x008A2, 0x008A3, 0x008A4, 0x008A5, 0x008A6, 0x008A7, 0x008A8, 0x008A9, 0x008AA, 0x008AB, 0x008AC, 0x008AD, 0x008AE, 0x008AF, 0x008B0, 0x008B1, 0x008B2, 0x008B3, 0x008B4, 0x008B6, 0x008B7, 0x008B8, 0x008B9, 0x008BA, 0x008BB, 0x008BC, 0x008BD, 0x00904, 0x00905, 0x00906, 0x00907, 0x00908, 0x00909, 0x0090A, 0x0090B, 0x0090C, 0x0090D, 0x0090E, 0x0090F, 0x00910, 0x00911, 0x00912, 0x00913, 0x00914, 0x00915, 0x00916, 0x00917, 0x00918, 0x00919, 0x0091A, 0x0091B, 0x0091C, 0x0091D, 0x0091E, 0x0091F, 0x00920, 0x00921, 0x00922, 0x00923, 0x00924, 0x00925, 0x00926, 0x00927, 0x00928, 0x00929, 0x0092A, 0x0092B, 0x0092C, 0x0092D, 0x0092E, 0x0092F, 0x00930, 0x00931, 0x00932, 0x00933, 0x00934, 0x00935, 0x00936, 0x00937, 0x00938, 0x00939, 0x0093D, 0x00950, 0x00958, 0x00959, 0x0095A, 0x0095B, 0x0095C, 0x0095D, 0x0095E, 0x0095F, 0x00960, 0x00961, 0x00971, 0x00972, 0x00973, 0x00974, 0x00975, 0x00976, 0x00977, 0x00978, 0x00979, 0x0097A, 0x0097B, 0x0097C, 0x0097D, 0x0097E, 0x0097F, 0x00980, 0x00985, 0x00986, 0x00987, 0x00988, 0x00989, 0x0098A, 0x0098B, 0x0098C, 0x0098F, 0x00990, 0x00993, 0x00994, 0x00995, 0x00996, 0x00997, 0x00998, 0x00999, 0x0099A, 0x0099B, 0x0099C, 0x0099D, 0x0099E, 0x0099F, 0x009A0, 0x009A1, 0x009A2, 0x009A3, 0x009A4, 0x009A5, 0x009A6, 0x009A7, 0x009A8, 0x009AA, 0x009AB, 0x009AC, 0x009AD, 0x009AE, 0x009AF, 0x009B0, 0x009B2, 0x009B6, 0x009B7, 0x009B8, 0x009B9, 0x009BD, 0x009CE, 0x009DC, 0x009DD, 0x009DF, 0x009E0, 0x009E1, 0x009F0, 0x009F1, 0x00A05, 0x00A06, 0x00A07, 0x00A08, 0x00A09, 0x00A0A, 0x00A0F, 0x00A10, 0x00A13, 0x00A14, 0x00A15, 0x00A16, 0x00A17, 0x00A18, 0x00A19, 0x00A1A, 0x00A1B, 0x00A1C, 0x00A1D, 0x00A1E, 0x00A1F, 0x00A20, 0x00A21, 0x00A22, 0x00A23, 0x00A24, 0x00A25, 0x00A26, 0x00A27, 0x00A28, 0x00A2A, 0x00A2B, 0x00A2C, 0x00A2D, 0x00A2E, 0x00A2F, 0x00A30, 0x00A32, 0x00A33, 0x00A35, 0x00A36, 0x00A38, 0x00A39, 0x00A59, 0x00A5A, 0x00A5B, 0x00A5C, 0x00A5E, 0x00A72, 0x00A73, 0x00A74, 0x00A85, 0x00A86, 0x00A87, 0x00A88, 0x00A89, 0x00A8A, 0x00A8B, 0x00A8C, 0x00A8D, 0x00A8F, 0x00A90, 0x00A91, 0x00A93, 0x00A94, 0x00A95, 0x00A96, 0x00A97, 0x00A98, 0x00A99, 0x00A9A, 0x00A9B, 0x00A9C, 0x00A9D, 0x00A9E, 0x00A9F, 0x00AA0, 0x00AA1, 0x00AA2, 0x00AA3, 0x00AA4, 0x00AA5, 0x00AA6, 0x00AA7, 0x00AA8, 0x00AAA, 0x00AAB, 0x00AAC, 0x00AAD, 0x00AAE, 0x00AAF, 0x00AB0, 0x00AB2, 0x00AB3, 0x00AB5, 0x00AB6, 0x00AB7, 0x00AB8, 0x00AB9, 0x00ABD, 0x00AD0, 0x00AE0, 0x00AE1, 0x00AF9, 0x00B05, 0x00B06, 0x00B07, 0x00B08, 0x00B09, 0x00B0A, 0x00B0B, 0x00B0C, 0x00B0F, 0x00B10, 0x00B13, 0x00B14, 0x00B15, 0x00B16, 0x00B17, 0x00B18, 0x00B19, 0x00B1A, 0x00B1B, 0x00B1C, 0x00B1D, 0x00B1E, 0x00B1F, 0x00B20, 0x00B21, 0x00B22, 0x00B23, 0x00B24, 0x00B25, 0x00B26, 0x00B27, 0x00B28, 0x00B2A, 0x00B2B, 0x00B2C, 0x00B2D, 0x00B2E, 0x00B2F, 0x00B30, 0x00B32, 0x00B33, 0x00B35, 0x00B36, 0x00B37, 0x00B38, 0x00B39, 0x00B3D, 0x00B5C, 0x00B5D, 0x00B5F, 0x00B60, 0x00B61, 0x00B71, 0x00B83, 0x00B85, 0x00B86, 0x00B87, 0x00B88, 0x00B89, 0x00B8A, 0x00B8E, 0x00B8F, 0x00B90, 0x00B92, 0x00B93, 0x00B94, 0x00B95, 0x00B99, 0x00B9A, 0x00B9C, 0x00B9E, 0x00B9F, 0x00BA3, 0x00BA4, 0x00BA8, 0x00BA9, 0x00BAA, 0x00BAE, 0x00BAF, 0x00BB0, 0x00BB1, 0x00BB2, 0x00BB3, 0x00BB4, 0x00BB5, 0x00BB6, 0x00BB7, 0x00BB8, 0x00BB9, 0x00BD0, 0x00C05, 0x00C06, 0x00C07, 0x00C08, 0x00C09, 0x00C0A, 0x00C0B, 0x00C0C, 0x00C0E, 0x00C0F, 0x00C10, 0x00C12, 0x00C13, 0x00C14, 0x00C15, 0x00C16, 0x00C17, 0x00C18, 0x00C19, 0x00C1A, 0x00C1B, 0x00C1C, 0x00C1D, 0x00C1E, 0x00C1F, 0x00C20, 0x00C21, 0x00C22, 0x00C23, 0x00C24, 0x00C25, 0x00C26, 0x00C27, 0x00C28, 0x00C2A, 0x00C2B, 0x00C2C, 0x00C2D, 0x00C2E, 0x00C2F, 0x00C30, 0x00C31, 0x00C32, 0x00C33, 0x00C34, 0x00C35, 0x00C36, 0x00C37, 0x00C38, 0x00C39, 0x00C3D, 0x00C58, 0x00C59, 0x00C5A, 0x00C60, 0x00C61, 0x00C80, 0x00C85, 0x00C86, 0x00C87, 0x00C88, 0x00C89, 0x00C8A, 0x00C8B, 0x00C8C, 0x00C8E, 0x00C8F, 0x00C90, 0x00C92, 0x00C93, 0x00C94, 0x00C95, 0x00C96, 0x00C97, 0x00C98, 0x00C99, 0x00C9A, 0x00C9B, 0x00C9C, 0x00C9D, 0x00C9E, 0x00C9F, 0x00CA0, 0x00CA1, 0x00CA2, 0x00CA3, 0x00CA4, 0x00CA5, 0x00CA6, 0x00CA7, 0x00CA8, 0x00CAA, 0x00CAB, 0x00CAC, 0x00CAD, 0x00CAE, 0x00CAF, 0x00CB0, 0x00CB1, 0x00CB2, 0x00CB3, 0x00CB5, 0x00CB6, 0x00CB7, 0x00CB8, 0x00CB9, 0x00CBD, 0x00CDE, 0x00CE0, 0x00CE1, 0x00CF1, 0x00CF2, 0x00D05, 0x00D06, 0x00D07, 0x00D08, 0x00D09, 0x00D0A, 0x00D0B, 0x00D0C, 0x00D0E, 0x00D0F, 0x00D10, 0x00D12, 0x00D13, 0x00D14, 0x00D15, 0x00D16, 0x00D17, 0x00D18, 0x00D19, 0x00D1A, 0x00D1B, 0x00D1C, 0x00D1D, 0x00D1E, 0x00D1F, 0x00D20, 0x00D21, 0x00D22, 0x00D23, 0x00D24, 0x00D25, 0x00D26, 0x00D27, 0x00D28, 0x00D29, 0x00D2A, 0x00D2B, 0x00D2C, 0x00D2D, 0x00D2E, 0x00D2F, 0x00D30, 0x00D31, 0x00D32, 0x00D33, 0x00D34, 0x00D35, 0x00D36, 0x00D37, 0x00D38, 0x00D39, 0x00D3A, 0x00D3D, 0x00D4E, 0x00D54, 0x00D55, 0x00D56, 0x00D5F, 0x00D60, 0x00D61, 0x00D7A, 0x00D7B, 0x00D7C, 0x00D7D, 0x00D7E, 0x00D7F, 0x00D85, 0x00D86, 0x00D87, 0x00D88, 0x00D89, 0x00D8A, 0x00D8B, 0x00D8C, 0x00D8D, 0x00D8E, 0x00D8F, 0x00D90, 0x00D91, 0x00D92, 0x00D93, 0x00D94, 0x00D95, 0x00D96, 0x00D9A, 0x00D9B, 0x00D9C, 0x00D9D, 0x00D9E, 0x00D9F, 0x00DA0, 0x00DA1, 0x00DA2, 0x00DA3, 0x00DA4, 0x00DA5, 0x00DA6, 0x00DA7, 0x00DA8, 0x00DA9, 0x00DAA, 0x00DAB, 0x00DAC, 0x00DAD, 0x00DAE, 0x00DAF, 0x00DB0, 0x00DB1, 0x00DB3, 0x00DB4, 0x00DB5, 0x00DB6, 0x00DB7, 0x00DB8, 0x00DB9, 0x00DBA, 0x00DBB, 0x00DBD, 0x00DC0, 0x00DC1, 0x00DC2, 0x00DC3, 0x00DC4, 0x00DC5, 0x00DC6, 0x00F00, 0x00F40, 0x00F41, 0x00F42, 0x00F43, 0x00F44, 0x00F45, 0x00F46, 0x00F47, 0x00F49, 0x00F4A, 0x00F4B, 0x00F4C, 0x00F4D, 0x00F4E, 0x00F4F, 0x00F50, 0x00F51, 0x00F52, 0x00F53, 0x00F54, 0x00F55, 0x00F56, 0x00F57, 0x00F58, 0x00F59, 0x00F5A, 0x00F5B, 0x00F5C, 0x00F5D, 0x00F5E, 0x00F5F, 0x00F60, 0x00F61, 0x00F62, 0x00F63, 0x00F64, 0x00F65, 0x00F66, 0x00F67, 0x00F68, 0x00F69, 0x00F6A, 0x00F6B, 0x00F6C, 0x00F88, 0x00F89, 0x00F8A, 0x00F8B, 0x00F8C, 0x010A0, 0x010A1, 0x010A2, 0x010A3, 0x010A4, 0x010A5, 0x010A6, 0x010A7, 0x010A8, 0x010A9, 0x010AA, 0x010AB, 0x010AC, 0x010AD, 0x010AE, 0x010AF, 0x010B0, 0x010B1, 0x010B2, 0x010B3, 0x010B4, 0x010B5, 0x010B6, 0x010B7, 0x010B8, 0x010B9, 0x010BA, 0x010BB, 0x010BC, 0x010BD, 0x010BE, 0x010BF, 0x010C0, 0x010C1, 0x010C2, 0x010C3, 0x010C4, 0x010C5, 0x010C7, 0x010CD, 0x010D0, 0x010D1, 0x010D2, 0x010D3, 0x010D4, 0x010D5, 0x010D6, 0x010D7, 0x010D8, 0x010D9, 0x010DA, 0x010DB, 0x010DC, 0x010DD, 0x010DE, 0x010DF, 0x010E0, 0x010E1, 0x010E2, 0x010E3, 0x010E4, 0x010E5, 0x010E6, 0x010E7, 0x010E8, 0x010E9, 0x010EA, 0x010EB, 0x010EC, 0x010ED, 0x010EE, 0x010EF, 0x010F0, 0x010F1, 0x010F2, 0x010F3, 0x010F4, 0x010F5, 0x010F6, 0x010F7, 0x010F8, 0x010F9, 0x010FA, 0x010FC, 0x010FD, 0x010FE, 0x010FF, 0x01100, 0x01101, 0x01102, 0x01103, 0x01104, 0x01105, 0x01106, 0x01107, 0x01108, 0x01109, 0x0110A, 0x0110B, 0x0110C, 0x0110D, 0x0110E, 0x0110F, 0x01110, 0x01111, 0x01112, 0x01113, 0x01114, 0x01115, 0x01116, 0x01117, 0x01118, 0x01119, 0x0111A, 0x0111B, 0x0111C, 0x0111D, 0x0111E, 0x0111F, 0x01120, 0x01121, 0x01122, 0x01123, 0x01124, 0x01125, 0x01126, 0x01127, 0x01128, 0x01129, 0x0112A, 0x0112B, 0x0112C, 0x0112D, 0x0112E, 0x0112F, 0x01130, 0x01131, 0x01132, 0x01133, 0x01134, 0x01135, 0x01136, 0x01137, 0x01138, 0x01139, 0x0113A, 0x0113B, 0x0113C, 0x0113D, 0x0113E, 0x0113F, 0x01140, 0x01141, 0x01142, 0x01143, 0x01144, 0x01145, 0x01146, 0x01147, 0x01148, 0x01149, 0x0114A, 0x0114B, 0x0114C, 0x0114D, 0x0114E, 0x0114F, 0x01150, 0x01151, 0x01152, 0x01153, 0x01154, 0x01155, 0x01156, 0x01157, 0x01158, 0x01159, 0x0115A, 0x0115B, 0x0115C, 0x0115D, 0x0115E, 0x0115F, 0x01160, 0x01161, 0x01162, 0x01163, 0x01164, 0x01165, 0x01166, 0x01167, 0x01168, 0x01169, 0x0116A, 0x0116B, 0x0116C, 0x0116D, 0x0116E, 0x0116F, 0x01170, 0x01171, 0x01172, 0x01173, 0x01174, 0x01175, 0x01176, 0x01177, 0x01178, 0x01179, 0x0117A, 0x0117B, 0x0117C, 0x0117D, 0x0117E, 0x0117F, 0x01180, 0x01181, 0x01182, 0x01183, 0x01184, 0x01185, 0x01186, 0x01187, 0x01188, 0x01189, 0x0118A, 0x0118B, 0x0118C, 0x0118D, 0x0118E, 0x0118F, 0x01190, 0x01191, 0x01192, 0x01193, 0x01194, 0x01195, 0x01196, 0x01197, 0x01198, 0x01199, 0x0119A, 0x0119B, 0x0119C, 0x0119D, 0x0119E, 0x0119F, 0x011A0, 0x011A1, 0x011A2, 0x011A3, 0x011A4, 0x011A5, 0x011A6, 0x011A7, 0x011A8, 0x011A9, 0x011AA, 0x011AB, 0x011AC, 0x011AD, 0x011AE, 0x011AF, 0x011B0, 0x011B1, 0x011B2, 0x011B3, 0x011B4, 0x011B5, 0x011B6, 0x011B7, 0x011B8, 0x011B9, 0x011BA, 0x011BB, 0x011BC, 0x011BD, 0x011BE, 0x011BF, 0x011C0, 0x011C1, 0x011C2, 0x011C3, 0x011C4, 0x011C5, 0x011C6, 0x011C7, 0x011C8, 0x011C9, 0x011CA, 0x011CB, 0x011CC, 0x011CD, 0x011CE, 0x011CF, 0x011D0, 0x011D1, 0x011D2, 0x011D3, 0x011D4, 0x011D5, 0x011D6, 0x011D7, 0x011D8, 0x011D9, 0x011DA, 0x011DB, 0x011DC, 0x011DD, 0x011DE, 0x011DF, 0x011E0, 0x011E1, 0x011E2, 0x011E3, 0x011E4, 0x011E5, 0x011E6, 0x011E7, 0x011E8, 0x011E9, 0x011EA, 0x011EB, 0x011EC, 0x011ED, 0x011EE, 0x011EF, 0x011F0, 0x011F1, 0x011F2, 0x011F3, 0x011F4, 0x011F5, 0x011F6, 0x011F7, 0x011F8, 0x011F9, 0x011FA, 0x011FB, 0x011FC, 0x011FD, 0x011FE, 0x011FF, 0x01200, 0x01201, 0x01202, 0x01203, 0x01204, 0x01205, 0x01206, 0x01207, 0x01208, 0x01209, 0x0120A, 0x0120B, 0x0120C, 0x0120D, 0x0120E, 0x0120F, 0x01210, 0x01211, 0x01212, 0x01213, 0x01214, 0x01215, 0x01216, 0x01217, 0x01218, 0x01219, 0x0121A, 0x0121B, 0x0121C, 0x0121D, 0x0121E, 0x0121F, 0x01220, 0x01221, 0x01222, 0x01223, 0x01224, 0x01225, 0x01226, 0x01227, 0x01228, 0x01229, 0x0122A, 0x0122B, 0x0122C, 0x0122D, 0x0122E, 0x0122F, 0x01230, 0x01231, 0x01232, 0x01233, 0x01234, 0x01235, 0x01236, 0x01237, 0x01238, 0x01239, 0x0123A, 0x0123B, 0x0123C, 0x0123D, 0x0123E, 0x0123F, 0x01240, 0x01241, 0x01242, 0x01243, 0x01244, 0x01245, 0x01246, 0x01247, 0x01248, 0x0124A, 0x0124B, 0x0124C, 0x0124D, 0x01250, 0x01251, 0x01252, 0x01253, 0x01254, 0x01255, 0x01256, 0x01258, 0x0125A, 0x0125B, 0x0125C, 0x0125D, 0x01260, 0x01261, 0x01262, 0x01263, 0x01264, 0x01265, 0x01266, 0x01267, 0x01268, 0x01269, 0x0126A, 0x0126B, 0x0126C, 0x0126D, 0x0126E, 0x0126F, 0x01270, 0x01271, 0x01272, 0x01273, 0x01274, 0x01275, 0x01276, 0x01277, 0x01278, 0x01279, 0x0127A, 0x0127B, 0x0127C, 0x0127D, 0x0127E, 0x0127F, 0x01280, 0x01281, 0x01282, 0x01283, 0x01284, 0x01285, 0x01286, 0x01287, 0x01288, 0x0128A, 0x0128B, 0x0128C, 0x0128D, 0x01290, 0x01291, 0x01292, 0x01293, 0x01294, 0x01295, 0x01296, 0x01297, 0x01298, 0x01299, 0x0129A, 0x0129B, 0x0129C, 0x0129D, 0x0129E, 0x0129F, 0x012A0, 0x012A1, 0x012A2, 0x012A3, 0x012A4, 0x012A5, 0x012A6, 0x012A7, 0x012A8, 0x012A9, 0x012AA, 0x012AB, 0x012AC, 0x012AD, 0x012AE, 0x012AF, 0x012B0, 0x012B2, 0x012B3, 0x012B4, 0x012B5, 0x012B8, 0x012B9, 0x012BA, 0x012BB, 0x012BC, 0x012BD, 0x012BE, 0x012C0, 0x012C2, 0x012C3, 0x012C4, 0x012C5, 0x012C8, 0x012C9, 0x012CA, 0x012CB, 0x012CC, 0x012CD, 0x012CE, 0x012CF, 0x012D0, 0x012D1, 0x012D2, 0x012D3, 0x012D4, 0x012D5, 0x012D6, 0x012D8, 0x012D9, 0x012DA, 0x012DB, 0x012DC, 0x012DD, 0x012DE, 0x012DF, 0x012E0, 0x012E1, 0x012E2, 0x012E3, 0x012E4, 0x012E5, 0x012E6, 0x012E7, 0x012E8, 0x012E9, 0x012EA, 0x012EB, 0x012EC, 0x012ED, 0x012EE, 0x012EF, 0x012F0, 0x012F1, 0x012F2, 0x012F3, 0x012F4, 0x012F5, 0x012F6, 0x012F7, 0x012F8, 0x012F9, 0x012FA, 0x012FB, 0x012FC, 0x012FD, 0x012FE, 0x012FF, 0x01300, 0x01301, 0x01302, 0x01303, 0x01304, 0x01305, 0x01306, 0x01307, 0x01308, 0x01309, 0x0130A, 0x0130B, 0x0130C, 0x0130D, 0x0130E, 0x0130F, 0x01310, 0x01312, 0x01313, 0x01314, 0x01315, 0x01318, 0x01319, 0x0131A, 0x0131B, 0x0131C, 0x0131D, 0x0131E, 0x0131F, 0x01320, 0x01321, 0x01322, 0x01323, 0x01324, 0x01325, 0x01326, 0x01327, 0x01328, 0x01329, 0x0132A, 0x0132B, 0x0132C, 0x0132D, 0x0132E, 0x0132F, 0x01330, 0x01331, 0x01332, 0x01333, 0x01334, 0x01335, 0x01336, 0x01337, 0x01338, 0x01339, 0x0133A, 0x0133B, 0x0133C, 0x0133D, 0x0133E, 0x0133F, 0x01340, 0x01341, 0x01342, 0x01343, 0x01344, 0x01345, 0x01346, 0x01347, 0x01348, 0x01349, 0x0134A, 0x0134B, 0x0134C, 0x0134D, 0x0134E, 0x0134F, 0x01350, 0x01351, 0x01352, 0x01353, 0x01354, 0x01355, 0x01356, 0x01357, 0x01358, 0x01359, 0x0135A, 0x01380, 0x01381, 0x01382, 0x01383, 0x01384, 0x01385, 0x01386, 0x01387, 0x01388, 0x01389, 0x0138A, 0x0138B, 0x0138C, 0x0138D, 0x0138E, 0x0138F, 0x013A0, 0x013A1, 0x013A2, 0x013A3, 0x013A4, 0x013A5, 0x013A6, 0x013A7, 0x013A8, 0x013A9, 0x013AA, 0x013AB, 0x013AC, 0x013AD, 0x013AE, 0x013AF, 0x013B0, 0x013B1, 0x013B2, 0x013B3, 0x013B4, 0x013B5, 0x013B6, 0x013B7, 0x013B8, 0x013B9, 0x013BA, 0x013BB, 0x013BC, 0x013BD, 0x013BE, 0x013BF, 0x013C0, 0x013C1, 0x013C2, 0x013C3, 0x013C4, 0x013C5, 0x013C6, 0x013C7, 0x013C8, 0x013C9, 0x013CA, 0x013CB, 0x013CC, 0x013CD, 0x013CE, 0x013CF, 0x013D0, 0x013D1, 0x013D2, 0x013D3, 0x013D4, 0x013D5, 0x013D6, 0x013D7, 0x013D8, 0x013D9, 0x013DA, 0x013DB, 0x013DC, 0x013DD, 0x013DE, 0x013DF, 0x013E0, 0x013E1, 0x013E2, 0x013E3, 0x013E4, 0x013E5, 0x013E6, 0x013E7, 0x013E8, 0x013E9, 0x013EA, 0x013EB, 0x013EC, 0x013ED, 0x013EE, 0x013EF, 0x013F0, 0x013F1, 0x013F2, 0x013F3, 0x013F4, 0x013F5, 0x013F8, 0x013F9, 0x013FA, 0x013FB, 0x013FC, 0x013FD, 0x01401, 0x01402, 0x01403, 0x01404, 0x01405, 0x01406, 0x01407, 0x01408, 0x01409, 0x0140A, 0x0140B, 0x0140C, 0x0140D, 0x0140E, 0x0140F, 0x01410, 0x01411, 0x01412, 0x01413, 0x01414, 0x01415, 0x01416, 0x01417, 0x01418, 0x01419, 0x0141A, 0x0141B, 0x0141C, 0x0141D, 0x0141E, 0x0141F, 0x01420, 0x01421, 0x01422, 0x01423, 0x01424, 0x01425, 0x01426, 0x01427, 0x01428, 0x01429, 0x0142A, 0x0142B, 0x0142C, 0x0142D, 0x0142E, 0x0142F, 0x01430, 0x01431, 0x01432, 0x01433, 0x01434, 0x01435, 0x01436, 0x01437, 0x01438, 0x01439, 0x0143A, 0x0143B, 0x0143C, 0x0143D, 0x0143E, 0x0143F, 0x01440, 0x01441, 0x01442, 0x01443, 0x01444, 0x01445, 0x01446, 0x01447, 0x01448, 0x01449, 0x0144A, 0x0144B, 0x0144C, 0x0144D, 0x0144E, 0x0144F, 0x01450, 0x01451, 0x01452, 0x01453, 0x01454, 0x01455, 0x01456, 0x01457, 0x01458, 0x01459, 0x0145A, 0x0145B, 0x0145C, 0x0145D, 0x0145E, 0x0145F, 0x01460, 0x01461, 0x01462, 0x01463, 0x01464, 0x01465, 0x01466, 0x01467, 0x01468, 0x01469, 0x0146A, 0x0146B, 0x0146C, 0x0146D, 0x0146E, 0x0146F, 0x01470, 0x01471, 0x01472, 0x01473, 0x01474, 0x01475, 0x01476, 0x01477, 0x01478, 0x01479, 0x0147A, 0x0147B, 0x0147C, 0x0147D, 0x0147E, 0x0147F, 0x01480, 0x01481, 0x01482, 0x01483, 0x01484, 0x01485, 0x01486, 0x01487, 0x01488, 0x01489, 0x0148A, 0x0148B, 0x0148C, 0x0148D, 0x0148E, 0x0148F, 0x01490, 0x01491, 0x01492, 0x01493, 0x01494, 0x01495, 0x01496, 0x01497, 0x01498, 0x01499, 0x0149A, 0x0149B, 0x0149C, 0x0149D, 0x0149E, 0x0149F, 0x014A0, 0x014A1, 0x014A2, 0x014A3, 0x014A4, 0x014A5, 0x014A6, 0x014A7, 0x014A8, 0x014A9, 0x014AA, 0x014AB, 0x014AC, 0x014AD, 0x014AE, 0x014AF, 0x014B0, 0x014B1, 0x014B2, 0x014B3, 0x014B4, 0x014B5, 0x014B6, 0x014B7, 0x014B8, 0x014B9, 0x014BA, 0x014BB, 0x014BC, 0x014BD, 0x014BE, 0x014BF, 0x014C0, 0x014C1, 0x014C2, 0x014C3, 0x014C4, 0x014C5, 0x014C6, 0x014C7, 0x014C8, 0x014C9, 0x014CA, 0x014CB, 0x014CC, 0x014CD, 0x014CE, 0x014CF, 0x014D0, 0x014D1, 0x014D2, 0x014D3, 0x014D4, 0x014D5, 0x014D6, 0x014D7, 0x014D8, 0x014D9, 0x014DA, 0x014DB, 0x014DC, 0x014DD, 0x014DE, 0x014DF, 0x014E0, 0x014E1, 0x014E2, 0x014E3, 0x014E4, 0x014E5, 0x014E6, 0x014E7, 0x014E8, 0x014E9, 0x014EA, 0x014EB, 0x014EC, 0x014ED, 0x014EE, 0x014EF, 0x014F0, 0x014F1, 0x014F2, 0x014F3, 0x014F4, 0x014F5, 0x014F6, 0x014F7, 0x014F8, 0x014F9, 0x014FA, 0x014FB, 0x014FC, 0x014FD, 0x014FE, 0x014FF, 0x01500, 0x01501, 0x01502, 0x01503, 0x01504, 0x01505, 0x01506, 0x01507, 0x01508, 0x01509, 0x0150A, 0x0150B, 0x0150C, 0x0150D, 0x0150E, 0x0150F, 0x01510, 0x01511, 0x01512, 0x01513, 0x01514, 0x01515, 0x01516, 0x01517, 0x01518, 0x01519, 0x0151A, 0x0151B, 0x0151C, 0x0151D, 0x0151E, 0x0151F, 0x01520, 0x01521, 0x01522, 0x01523, 0x01524, 0x01525, 0x01526, 0x01527, 0x01528, 0x01529, 0x0152A, 0x0152B, 0x0152C, 0x0152D, 0x0152E, 0x0152F, 0x01530, 0x01531, 0x01532, 0x01533, 0x01534, 0x01535, 0x01536, 0x01537, 0x01538, 0x01539, 0x0153A, 0x0153B, 0x0153C, 0x0153D, 0x0153E, 0x0153F, 0x01540, 0x01541, 0x01542, 0x01543, 0x01544, 0x01545, 0x01546, 0x01547, 0x01548, 0x01549, 0x0154A, 0x0154B, 0x0154C, 0x0154D, 0x0154E, 0x0154F, 0x01550, 0x01551, 0x01552, 0x01553, 0x01554, 0x01555, 0x01556, 0x01557, 0x01558, 0x01559, 0x0155A, 0x0155B, 0x0155C, 0x0155D, 0x0155E, 0x0155F, 0x01560, 0x01561, 0x01562, 0x01563, 0x01564, 0x01565, 0x01566, 0x01567, 0x01568, 0x01569, 0x0156A, 0x0156B, 0x0156C, 0x0156D, 0x0156E, 0x0156F, 0x01570, 0x01571, 0x01572, 0x01573, 0x01574, 0x01575, 0x01576, 0x01577, 0x01578, 0x01579, 0x0157A, 0x0157B, 0x0157C, 0x0157D, 0x0157E, 0x0157F, 0x01580, 0x01581, 0x01582, 0x01583, 0x01584, 0x01585, 0x01586, 0x01587, 0x01588, 0x01589, 0x0158A, 0x0158B, 0x0158C, 0x0158D, 0x0158E, 0x0158F, 0x01590, 0x01591, 0x01592, 0x01593, 0x01594, 0x01595, 0x01596, 0x01597, 0x01598, 0x01599, 0x0159A, 0x0159B, 0x0159C, 0x0159D, 0x0159E, 0x0159F, 0x015A0, 0x015A1, 0x015A2, 0x015A3, 0x015A4, 0x015A5, 0x015A6, 0x015A7, 0x015A8, 0x015A9, 0x015AA, 0x015AB, 0x015AC, 0x015AD, 0x015AE, 0x015AF, 0x015B0, 0x015B1, 0x015B2, 0x015B3, 0x015B4, 0x015B5, 0x015B6, 0x015B7, 0x015B8, 0x015B9, 0x015BA, 0x015BB, 0x015BC, 0x015BD, 0x015BE, 0x015BF, 0x015C0, 0x015C1, 0x015C2, 0x015C3, 0x015C4, 0x015C5, 0x015C6, 0x015C7, 0x015C8, 0x015C9, 0x015CA, 0x015CB, 0x015CC, 0x015CD, 0x015CE, 0x015CF, 0x015D0, 0x015D1, 0x015D2, 0x015D3, 0x015D4, 0x015D5, 0x015D6, 0x015D7, 0x015D8, 0x015D9, 0x015DA, 0x015DB, 0x015DC, 0x015DD, 0x015DE, 0x015DF, 0x015E0, 0x015E1, 0x015E2, 0x015E3, 0x015E4, 0x015E5, 0x015E6, 0x015E7, 0x015E8, 0x015E9, 0x015EA, 0x015EB, 0x015EC, 0x015ED, 0x015EE, 0x015EF, 0x015F0, 0x015F1, 0x015F2, 0x015F3, 0x015F4, 0x015F5, 0x015F6, 0x015F7, 0x015F8, 0x015F9, 0x015FA, 0x015FB, 0x015FC, 0x015FD, 0x015FE, 0x015FF, 0x01600, 0x01601, 0x01602, 0x01603, 0x01604, 0x01605, 0x01606, 0x01607, 0x01608, 0x01609, 0x0160A, 0x0160B, 0x0160C, 0x0160D, 0x0160E, 0x0160F, 0x01610, 0x01611, 0x01612, 0x01613, 0x01614, 0x01615, 0x01616, 0x01617, 0x01618, 0x01619, 0x0161A, 0x0161B, 0x0161C, 0x0161D, 0x0161E, 0x0161F, 0x01620, 0x01621, 0x01622, 0x01623, 0x01624, 0x01625, 0x01626, 0x01627, 0x01628, 0x01629, 0x0162A, 0x0162B, 0x0162C, 0x0162D, 0x0162E, 0x0162F, 0x01630, 0x01631, 0x01632, 0x01633, 0x01634, 0x01635, 0x01636, 0x01637, 0x01638, 0x01639, 0x0163A, 0x0163B, 0x0163C, 0x0163D, 0x0163E, 0x0163F, 0x01640, 0x01641, 0x01642, 0x01643, 0x01644, 0x01645, 0x01646, 0x01647, 0x01648, 0x01649, 0x0164A, 0x0164B, 0x0164C, 0x0164D, 0x0164E, 0x0164F, 0x01650, 0x01651, 0x01652, 0x01653, 0x01654, 0x01655, 0x01656, 0x01657, 0x01658, 0x01659, 0x0165A, 0x0165B, 0x0165C, 0x0165D, 0x0165E, 0x0165F, 0x01660, 0x01661, 0x01662, 0x01663, 0x01664, 0x01665, 0x01666, 0x01667, 0x01668, 0x01669, 0x0166A, 0x0166B, 0x0166C, 0x0166F, 0x01670, 0x01671, 0x01672, 0x01673, 0x01674, 0x01675, 0x01676, 0x01677, 0x01678, 0x01679, 0x0167A, 0x0167B, 0x0167C, 0x0167D, 0x0167E, 0x0167F, 0x01681, 0x01682, 0x01683, 0x01684, 0x01685, 0x01686, 0x01687, 0x01688, 0x01689, 0x0168A, 0x0168B, 0x0168C, 0x0168D, 0x0168E, 0x0168F, 0x01690, 0x01691, 0x01692, 0x01693, 0x01694, 0x01695, 0x01696, 0x01697, 0x01698, 0x01699, 0x0169A, 0x016A0, 0x016A1, 0x016A2, 0x016A3, 0x016A4, 0x016A5, 0x016A6, 0x016A7, 0x016A8, 0x016A9, 0x016AA, 0x016AB, 0x016AC, 0x016AD, 0x016AE, 0x016AF, 0x016B0, 0x016B1, 0x016B2, 0x016B3, 0x016B4, 0x016B5, 0x016B6, 0x016B7, 0x016B8, 0x016B9, 0x016BA, 0x016BB, 0x016BC, 0x016BD, 0x016BE, 0x016BF, 0x016C0, 0x016C1, 0x016C2, 0x016C3, 0x016C4, 0x016C5, 0x016C6, 0x016C7, 0x016C8, 0x016C9, 0x016CA, 0x016CB, 0x016CC, 0x016CD, 0x016CE, 0x016CF, 0x016D0, 0x016D1, 0x016D2, 0x016D3, 0x016D4, 0x016D5, 0x016D6, 0x016D7, 0x016D8, 0x016D9, 0x016DA, 0x016DB, 0x016DC, 0x016DD, 0x016DE, 0x016DF, 0x016E0, 0x016E1, 0x016E2, 0x016E3, 0x016E4, 0x016E5, 0x016E6, 0x016E7, 0x016E8, 0x016E9, 0x016EA, 0x016EE, 0x016EF, 0x016F0, 0x016F1, 0x016F2, 0x016F3, 0x016F4, 0x016F5, 0x016F6, 0x016F7, 0x016F8, 0x01700, 0x01701, 0x01702, 0x01703, 0x01704, 0x01705, 0x01706, 0x01707, 0x01708, 0x01709, 0x0170A, 0x0170B, 0x0170C, 0x0170E, 0x0170F, 0x01710, 0x01711, 0x01720, 0x01721, 0x01722, 0x01723, 0x01724, 0x01725, 0x01726, 0x01727, 0x01728, 0x01729, 0x0172A, 0x0172B, 0x0172C, 0x0172D, 0x0172E, 0x0172F, 0x01730, 0x01731, 0x01740, 0x01741, 0x01742, 0x01743, 0x01744, 0x01745, 0x01746, 0x01747, 0x01748, 0x01749, 0x0174A, 0x0174B, 0x0174C, 0x0174D, 0x0174E, 0x0174F, 0x01750, 0x01751, 0x01760, 0x01761, 0x01762, 0x01763, 0x01764, 0x01765, 0x01766, 0x01767, 0x01768, 0x01769, 0x0176A, 0x0176B, 0x0176C, 0x0176E, 0x0176F, 0x01770, 0x01820, 0x01821, 0x01822, 0x01823, 0x01824, 0x01825, 0x01826, 0x01827, 0x01828, 0x01829, 0x0182A, 0x0182B, 0x0182C, 0x0182D, 0x0182E, 0x0182F, 0x01830, 0x01831, 0x01832, 0x01833, 0x01834, 0x01835, 0x01836, 0x01837, 0x01838, 0x01839, 0x0183A, 0x0183B, 0x0183C, 0x0183D, 0x0183E, 0x0183F, 0x01840, 0x01841, 0x01842, 0x01843, 0x01844, 0x01845, 0x01846, 0x01847, 0x01848, 0x01849, 0x0184A, 0x0184B, 0x0184C, 0x0184D, 0x0184E, 0x0184F, 0x01850, 0x01851, 0x01852, 0x01853, 0x01854, 0x01855, 0x01856, 0x01857, 0x01858, 0x01859, 0x0185A, 0x0185B, 0x0185C, 0x0185D, 0x0185E, 0x0185F, 0x01860, 0x01861, 0x01862, 0x01863, 0x01864, 0x01865, 0x01866, 0x01867, 0x01868, 0x01869, 0x0186A, 0x0186B, 0x0186C, 0x0186D, 0x0186E, 0x0186F, 0x01870, 0x01871, 0x01872, 0x01873, 0x01874, 0x01875, 0x01876, 0x01877, 0x01880, 0x01881, 0x01882, 0x01883, 0x01884, 0x01887, 0x01888, 0x01889, 0x0188A, 0x0188B, 0x0188C, 0x0188D, 0x0188E, 0x0188F, 0x01890, 0x01891, 0x01892, 0x01893, 0x01894, 0x01895, 0x01896, 0x01897, 0x01898, 0x01899, 0x0189A, 0x0189B, 0x0189C, 0x0189D, 0x0189E, 0x0189F, 0x018A0, 0x018A1, 0x018A2, 0x018A3, 0x018A4, 0x018A5, 0x018A6, 0x018A7, 0x018A8, 0x018AA, 0x018B0, 0x018B1, 0x018B2, 0x018B3, 0x018B4, 0x018B5, 0x018B6, 0x018B7, 0x018B8, 0x018B9, 0x018BA, 0x018BB, 0x018BC, 0x018BD, 0x018BE, 0x018BF, 0x018C0, 0x018C1, 0x018C2, 0x018C3, 0x018C4, 0x018C5, 0x018C6, 0x018C7, 0x018C8, 0x018C9, 0x018CA, 0x018CB, 0x018CC, 0x018CD, 0x018CE, 0x018CF, 0x018D0, 0x018D1, 0x018D2, 0x018D3, 0x018D4, 0x018D5, 0x018D6, 0x018D7, 0x018D8, 0x018D9, 0x018DA, 0x018DB, 0x018DC, 0x018DD, 0x018DE, 0x018DF, 0x018E0, 0x018E1, 0x018E2, 0x018E3, 0x018E4, 0x018E5, 0x018E6, 0x018E7, 0x018E8, 0x018E9, 0x018EA, 0x018EB, 0x018EC, 0x018ED, 0x018EE, 0x018EF, 0x018F0, 0x018F1, 0x018F2, 0x018F3, 0x018F4, 0x018F5, 0x01900, 0x01901, 0x01902, 0x01903, 0x01904, 0x01905, 0x01906, 0x01907, 0x01908, 0x01909, 0x0190A, 0x0190B, 0x0190C, 0x0190D, 0x0190E, 0x0190F, 0x01910, 0x01911, 0x01912, 0x01913, 0x01914, 0x01915, 0x01916, 0x01917, 0x01918, 0x01919, 0x0191A, 0x0191B, 0x0191C, 0x0191D, 0x0191E, 0x01A00, 0x01A01, 0x01A02, 0x01A03, 0x01A04, 0x01A05, 0x01A06, 0x01A07, 0x01A08, 0x01A09, 0x01A0A, 0x01A0B, 0x01A0C, 0x01A0D, 0x01A0E, 0x01A0F, 0x01A10, 0x01A11, 0x01A12, 0x01A13, 0x01A14, 0x01A15, 0x01A16, 0x01B05, 0x01B06, 0x01B07, 0x01B08, 0x01B09, 0x01B0A, 0x01B0B, 0x01B0C, 0x01B0D, 0x01B0E, 0x01B0F, 0x01B10, 0x01B11, 0x01B12, 0x01B13, 0x01B14, 0x01B15, 0x01B16, 0x01B17, 0x01B18, 0x01B19, 0x01B1A, 0x01B1B, 0x01B1C, 0x01B1D, 0x01B1E, 0x01B1F, 0x01B20, 0x01B21, 0x01B22, 0x01B23, 0x01B24, 0x01B25, 0x01B26, 0x01B27, 0x01B28, 0x01B29, 0x01B2A, 0x01B2B, 0x01B2C, 0x01B2D, 0x01B2E, 0x01B2F, 0x01B30, 0x01B31, 0x01B32, 0x01B33, 0x01B45, 0x01B46, 0x01B47, 0x01B48, 0x01B49, 0x01B4A, 0x01B4B, 0x01B83, 0x01B84, 0x01B85, 0x01B86, 0x01B87, 0x01B88, 0x01B89, 0x01B8A, 0x01B8B, 0x01B8C, 0x01B8D, 0x01B8E, 0x01B8F, 0x01B90, 0x01B91, 0x01B92, 0x01B93, 0x01B94, 0x01B95, 0x01B96, 0x01B97, 0x01B98, 0x01B99, 0x01B9A, 0x01B9B, 0x01B9C, 0x01B9D, 0x01B9E, 0x01B9F, 0x01BA0, 0x01BAE, 0x01BAF, 0x01BBA, 0x01BBB, 0x01BBC, 0x01BBD, 0x01BBE, 0x01BBF, 0x01BC0, 0x01BC1, 0x01BC2, 0x01BC3, 0x01BC4, 0x01BC5, 0x01BC6, 0x01BC7, 0x01BC8, 0x01BC9, 0x01BCA, 0x01BCB, 0x01BCC, 0x01BCD, 0x01BCE, 0x01BCF, 0x01BD0, 0x01BD1, 0x01BD2, 0x01BD3, 0x01BD4, 0x01BD5, 0x01BD6, 0x01BD7, 0x01BD8, 0x01BD9, 0x01BDA, 0x01BDB, 0x01BDC, 0x01BDD, 0x01BDE, 0x01BDF, 0x01BE0, 0x01BE1, 0x01BE2, 0x01BE3, 0x01BE4, 0x01BE5, 0x01C00, 0x01C01, 0x01C02, 0x01C03, 0x01C04, 0x01C05, 0x01C06, 0x01C07, 0x01C08, 0x01C09, 0x01C0A, 0x01C0B, 0x01C0C, 0x01C0D, 0x01C0E, 0x01C0F, 0x01C10, 0x01C11, 0x01C12, 0x01C13, 0x01C14, 0x01C15, 0x01C16, 0x01C17, 0x01C18, 0x01C19, 0x01C1A, 0x01C1B, 0x01C1C, 0x01C1D, 0x01C1E, 0x01C1F, 0x01C20, 0x01C21, 0x01C22, 0x01C23, 0x01C4D, 0x01C4E, 0x01C4F, 0x01C5A, 0x01C5B, 0x01C5C, 0x01C5D, 0x01C5E, 0x01C5F, 0x01C60, 0x01C61, 0x01C62, 0x01C63, 0x01C64, 0x01C65, 0x01C66, 0x01C67, 0x01C68, 0x01C69, 0x01C6A, 0x01C6B, 0x01C6C, 0x01C6D, 0x01C6E, 0x01C6F, 0x01C70, 0x01C71, 0x01C72, 0x01C73, 0x01C74, 0x01C75, 0x01C76, 0x01C77, 0x01C78, 0x01C79, 0x01C7A, 0x01C7B, 0x01C7C, 0x01C7D, 0x01C80, 0x01C81, 0x01C82, 0x01C83, 0x01C84, 0x01C85, 0x01C86, 0x01C87, 0x01C88, 0x01CE9, 0x01CEA, 0x01CEB, 0x01CEC, 0x01CEE, 0x01CEF, 0x01CF0, 0x01CF1, 0x01CF5, 0x01CF6, 0x01D00, 0x01D01, 0x01D02, 0x01D03, 0x01D04, 0x01D05, 0x01D06, 0x01D07, 0x01D08, 0x01D09, 0x01D0A, 0x01D0B, 0x01D0C, 0x01D0D, 0x01D0E, 0x01D0F, 0x01D10, 0x01D11, 0x01D12, 0x01D13, 0x01D14, 0x01D15, 0x01D16, 0x01D17, 0x01D18, 0x01D19, 0x01D1A, 0x01D1B, 0x01D1C, 0x01D1D, 0x01D1E, 0x01D1F, 0x01D20, 0x01D21, 0x01D22, 0x01D23, 0x01D24, 0x01D25, 0x01D26, 0x01D27, 0x01D28, 0x01D29, 0x01D2A, 0x01D2B, 0x01D2C, 0x01D2D, 0x01D2E, 0x01D2F, 0x01D30, 0x01D31, 0x01D32, 0x01D33, 0x01D34, 0x01D35, 0x01D36, 0x01D37, 0x01D38, 0x01D39, 0x01D3A, 0x01D3B, 0x01D3C, 0x01D3D, 0x01D3E, 0x01D3F, 0x01D40, 0x01D41, 0x01D42, 0x01D43, 0x01D44, 0x01D45, 0x01D46, 0x01D47, 0x01D48, 0x01D49, 0x01D4A, 0x01D4B, 0x01D4C, 0x01D4D, 0x01D4E, 0x01D4F, 0x01D50, 0x01D51, 0x01D52, 0x01D53, 0x01D54, 0x01D55, 0x01D56, 0x01D57, 0x01D58, 0x01D59, 0x01D5A, 0x01D5B, 0x01D5C, 0x01D5D, 0x01D5E, 0x01D5F, 0x01D60, 0x01D61, 0x01D62, 0x01D63, 0x01D64, 0x01D65, 0x01D66, 0x01D67, 0x01D68, 0x01D69, 0x01D6A, 0x01D6B, 0x01D6C, 0x01D6D, 0x01D6E, 0x01D6F, 0x01D70, 0x01D71, 0x01D72, 0x01D73, 0x01D74, 0x01D75, 0x01D76, 0x01D77, 0x01D78, 0x01D79, 0x01D7A, 0x01D7B, 0x01D7C, 0x01D7D, 0x01D7E, 0x01D7F, 0x01D80, 0x01D81, 0x01D82, 0x01D83, 0x01D84, 0x01D85, 0x01D86, 0x01D87, 0x01D88, 0x01D89, 0x01D8A, 0x01D8B, 0x01D8C, 0x01D8D, 0x01D8E, 0x01D8F, 0x01D90, 0x01D91, 0x01D92, 0x01D93, 0x01D94, 0x01D95, 0x01D96, 0x01D97, 0x01D98, 0x01D99, 0x01D9A, 0x01D9B, 0x01D9C, 0x01D9D, 0x01D9E, 0x01D9F, 0x01DA0, 0x01DA1, 0x01DA2, 0x01DA3, 0x01DA4, 0x01DA5, 0x01DA6, 0x01DA7, 0x01DA8, 0x01DA9, 0x01DAA, 0x01DAB, 0x01DAC, 0x01DAD, 0x01DAE, 0x01DAF, 0x01DB0, 0x01DB1, 0x01DB2, 0x01DB3, 0x01DB4, 0x01DB5, 0x01DB6, 0x01DB7, 0x01DB8, 0x01DB9, 0x01DBA, 0x01DBB, 0x01DBC, 0x01DBD, 0x01DBE, 0x01DBF, 0x01E00, 0x01E01, 0x01E02, 0x01E03, 0x01E04, 0x01E05, 0x01E06, 0x01E07, 0x01E08, 0x01E09, 0x01E0A, 0x01E0B, 0x01E0C, 0x01E0D, 0x01E0E, 0x01E0F, 0x01E10, 0x01E11, 0x01E12, 0x01E13, 0x01E14, 0x01E15, 0x01E16, 0x01E17, 0x01E18, 0x01E19, 0x01E1A, 0x01E1B, 0x01E1C, 0x01E1D, 0x01E1E, 0x01E1F, 0x01E20, 0x01E21, 0x01E22, 0x01E23, 0x01E24, 0x01E25, 0x01E26, 0x01E27, 0x01E28, 0x01E29, 0x01E2A, 0x01E2B, 0x01E2C, 0x01E2D, 0x01E2E, 0x01E2F, 0x01E30, 0x01E31, 0x01E32, 0x01E33, 0x01E34, 0x01E35, 0x01E36, 0x01E37, 0x01E38, 0x01E39, 0x01E3A, 0x01E3B, 0x01E3C, 0x01E3D, 0x01E3E, 0x01E3F, 0x01E40, 0x01E41, 0x01E42, 0x01E43, 0x01E44, 0x01E45, 0x01E46, 0x01E47, 0x01E48, 0x01E49, 0x01E4A, 0x01E4B, 0x01E4C, 0x01E4D, 0x01E4E, 0x01E4F, 0x01E50, 0x01E51, 0x01E52, 0x01E53, 0x01E54, 0x01E55, 0x01E56, 0x01E57, 0x01E58, 0x01E59, 0x01E5A, 0x01E5B, 0x01E5C, 0x01E5D, 0x01E5E, 0x01E5F, 0x01E60, 0x01E61, 0x01E62, 0x01E63, 0x01E64, 0x01E65, 0x01E66, 0x01E67, 0x01E68, 0x01E69, 0x01E6A, 0x01E6B, 0x01E6C, 0x01E6D, 0x01E6E, 0x01E6F, 0x01E70, 0x01E71, 0x01E72, 0x01E73, 0x01E74, 0x01E75, 0x01E76, 0x01E77, 0x01E78, 0x01E79, 0x01E7A, 0x01E7B, 0x01E7C, 0x01E7D, 0x01E7E, 0x01E7F, 0x01E80, 0x01E81, 0x01E82, 0x01E83, 0x01E84, 0x01E85, 0x01E86, 0x01E87, 0x01E88, 0x01E89, 0x01E8A, 0x01E8B, 0x01E8C, 0x01E8D, 0x01E8E, 0x01E8F, 0x01E90, 0x01E91, 0x01E92, 0x01E93, 0x01E94, 0x01E95, 0x01E96, 0x01E97, 0x01E98, 0x01E99, 0x01E9A, 0x01E9B, 0x01E9C, 0x01E9D, 0x01E9E, 0x01E9F, 0x01EA0, 0x01EA1, 0x01EA2, 0x01EA3, 0x01EA4, 0x01EA5, 0x01EA6, 0x01EA7, 0x01EA8, 0x01EA9, 0x01EAA, 0x01EAB, 0x01EAC, 0x01EAD, 0x01EAE, 0x01EAF, 0x01EB0, 0x01EB1, 0x01EB2, 0x01EB3, 0x01EB4, 0x01EB5, 0x01EB6, 0x01EB7, 0x01EB8, 0x01EB9, 0x01EBA, 0x01EBB, 0x01EBC, 0x01EBD, 0x01EBE, 0x01EBF, 0x01EC0, 0x01EC1, 0x01EC2, 0x01EC3, 0x01EC4, 0x01EC5, 0x01EC6, 0x01EC7, 0x01EC8, 0x01EC9, 0x01ECA, 0x01ECB, 0x01ECC, 0x01ECD, 0x01ECE, 0x01ECF, 0x01ED0, 0x01ED1, 0x01ED2, 0x01ED3, 0x01ED4, 0x01ED5, 0x01ED6, 0x01ED7, 0x01ED8, 0x01ED9, 0x01EDA, 0x01EDB, 0x01EDC, 0x01EDD, 0x01EDE, 0x01EDF, 0x01EE0, 0x01EE1, 0x01EE2, 0x01EE3, 0x01EE4, 0x01EE5, 0x01EE6, 0x01EE7, 0x01EE8, 0x01EE9, 0x01EEA, 0x01EEB, 0x01EEC, 0x01EED, 0x01EEE, 0x01EEF, 0x01EF0, 0x01EF1, 0x01EF2, 0x01EF3, 0x01EF4, 0x01EF5, 0x01EF6, 0x01EF7, 0x01EF8, 0x01EF9, 0x01EFA, 0x01EFB, 0x01EFC, 0x01EFD, 0x01EFE, 0x01EFF, 0x01F00, 0x01F01, 0x01F02, 0x01F03, 0x01F04, 0x01F05, 0x01F06, 0x01F07, 0x01F08, 0x01F09, 0x01F0A, 0x01F0B, 0x01F0C, 0x01F0D, 0x01F0E, 0x01F0F, 0x01F10, 0x01F11, 0x01F12, 0x01F13, 0x01F14, 0x01F15, 0x01F18, 0x01F19, 0x01F1A, 0x01F1B, 0x01F1C, 0x01F1D, 0x01F20, 0x01F21, 0x01F22, 0x01F23, 0x01F24, 0x01F25, 0x01F26, 0x01F27, 0x01F28, 0x01F29, 0x01F2A, 0x01F2B, 0x01F2C, 0x01F2D, 0x01F2E, 0x01F2F, 0x01F30, 0x01F31, 0x01F32, 0x01F33, 0x01F34, 0x01F35, 0x01F36, 0x01F37, 0x01F38, 0x01F39, 0x01F3A, 0x01F3B, 0x01F3C, 0x01F3D, 0x01F3E, 0x01F3F, 0x01F40, 0x01F41, 0x01F42, 0x01F43, 0x01F44, 0x01F45, 0x01F48, 0x01F49, 0x01F4A, 0x01F4B, 0x01F4C, 0x01F4D, 0x01F50, 0x01F51, 0x01F52, 0x01F53, 0x01F54, 0x01F55, 0x01F56, 0x01F57, 0x01F59, 0x01F5B, 0x01F5D, 0x01F5F, 0x01F60, 0x01F61, 0x01F62, 0x01F63, 0x01F64, 0x01F65, 0x01F66, 0x01F67, 0x01F68, 0x01F69, 0x01F6A, 0x01F6B, 0x01F6C, 0x01F6D, 0x01F6E, 0x01F6F, 0x01F70, 0x01F71, 0x01F72, 0x01F73, 0x01F74, 0x01F75, 0x01F76, 0x01F77, 0x01F78, 0x01F79, 0x01F7A, 0x01F7B, 0x01F7C, 0x01F7D, 0x01F80, 0x01F81, 0x01F82, 0x01F83, 0x01F84, 0x01F85, 0x01F86, 0x01F87, 0x01F88, 0x01F89, 0x01F8A, 0x01F8B, 0x01F8C, 0x01F8D, 0x01F8E, 0x01F8F, 0x01F90, 0x01F91, 0x01F92, 0x01F93, 0x01F94, 0x01F95, 0x01F96, 0x01F97, 0x01F98, 0x01F99, 0x01F9A, 0x01F9B, 0x01F9C, 0x01F9D, 0x01F9E, 0x01F9F, 0x01FA0, 0x01FA1, 0x01FA2, 0x01FA3, 0x01FA4, 0x01FA5, 0x01FA6, 0x01FA7, 0x01FA8, 0x01FA9, 0x01FAA, 0x01FAB, 0x01FAC, 0x01FAD, 0x01FAE, 0x01FAF, 0x01FB0, 0x01FB1, 0x01FB2, 0x01FB3, 0x01FB4, 0x01FB6, 0x01FB7, 0x01FB8, 0x01FB9, 0x01FBA, 0x01FBB, 0x01FBC, 0x01FBE, 0x01FC2, 0x01FC3, 0x01FC4, 0x01FC6, 0x01FC7, 0x01FC8, 0x01FC9, 0x01FCA, 0x01FCB, 0x01FCC, 0x01FD0, 0x01FD1, 0x01FD2, 0x01FD3, 0x01FD6, 0x01FD7, 0x01FD8, 0x01FD9, 0x01FDA, 0x01FDB, 0x01FE0, 0x01FE1, 0x01FE2, 0x01FE3, 0x01FE4, 0x01FE5, 0x01FE6, 0x01FE7, 0x01FE8, 0x01FE9, 0x01FEA, 0x01FEB, 0x01FEC, 0x01FF2, 0x01FF3, 0x01FF4, 0x01FF6, 0x01FF7, 0x01FF8, 0x01FF9, 0x01FFA, 0x01FFB, 0x01FFC, 0x02071, 0x0207F, 0x02090, 0x02091, 0x02092, 0x02093, 0x02094, 0x02095, 0x02096, 0x02097, 0x02098, 0x02099, 0x0209A, 0x0209B, 0x0209C, 0x02102, 0x02107, 0x0210A, 0x0210B, 0x0210C, 0x0210D, 0x0210E, 0x0210F, 0x02110, 0x02111, 0x02112, 0x02113, 0x02115, 0x02119, 0x0211A, 0x0211B, 0x0211C, 0x0211D, 0x02124, 0x02126, 0x02128, 0x0212A, 0x0212B, 0x0212C, 0x0212D, 0x0212F, 0x02130, 0x02131, 0x02132, 0x02133, 0x02134, 0x02135, 0x02136, 0x02137, 0x02138, 0x02139, 0x0213C, 0x0213D, 0x0213E, 0x0213F, 0x02145, 0x02146, 0x02147, 0x02148, 0x02149, 0x0214E, 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167, 0x02168, 0x02169, 0x0216A, 0x0216B, 0x0216C, 0x0216D, 0x0216E, 0x0216F, 0x02170, 0x02171, 0x02172, 0x02173, 0x02174, 0x02175, 0x02176, 0x02177, 0x02178, 0x02179, 0x0217A, 0x0217B, 0x0217C, 0x0217D, 0x0217E, 0x0217F, 0x02180, 0x02181, 0x02182, 0x02183, 0x02184, 0x02185, 0x02186, 0x02187, 0x02188, 0x024B6, 0x024B7, 0x024B8, 0x024B9, 0x024BA, 0x024BB, 0x024BC, 0x024BD, 0x024BE, 0x024BF, 0x024C0, 0x024C1, 0x024C2, 0x024C3, 0x024C4, 0x024C5, 0x024C6, 0x024C7, 0x024C8, 0x024C9, 0x024CA, 0x024CB, 0x024CC, 0x024CD, 0x024CE, 0x024CF, 0x024D0, 0x024D1, 0x024D2, 0x024D3, 0x024D4, 0x024D5, 0x024D6, 0x024D7, 0x024D8, 0x024D9, 0x024DA, 0x024DB, 0x024DC, 0x024DD, 0x024DE, 0x024DF, 0x024E0, 0x024E1, 0x024E2, 0x024E3, 0x024E4, 0x024E5, 0x024E6, 0x024E7, 0x024E8, 0x024E9, 0x02C00, 0x02C01, 0x02C02, 0x02C03, 0x02C04, 0x02C05, 0x02C06, 0x02C07, 0x02C08, 0x02C09, 0x02C0A, 0x02C0B, 0x02C0C, 0x02C0D, 0x02C0E, 0x02C0F, 0x02C10, 0x02C11, 0x02C12, 0x02C13, 0x02C14, 0x02C15, 0x02C16, 0x02C17, 0x02C18, 0x02C19, 0x02C1A, 0x02C1B, 0x02C1C, 0x02C1D, 0x02C1E, 0x02C1F, 0x02C20, 0x02C21, 0x02C22, 0x02C23, 0x02C24, 0x02C25, 0x02C26, 0x02C27, 0x02C28, 0x02C29, 0x02C2A, 0x02C2B, 0x02C2C, 0x02C2D, 0x02C2E, 0x02C30, 0x02C31, 0x02C32, 0x02C33, 0x02C34, 0x02C35, 0x02C36, 0x02C37, 0x02C38, 0x02C39, 0x02C3A, 0x02C3B, 0x02C3C, 0x02C3D, 0x02C3E, 0x02C3F, 0x02C40, 0x02C41, 0x02C42, 0x02C43, 0x02C44, 0x02C45, 0x02C46, 0x02C47, 0x02C48, 0x02C49, 0x02C4A, 0x02C4B, 0x02C4C, 0x02C4D, 0x02C4E, 0x02C4F, 0x02C50, 0x02C51, 0x02C52, 0x02C53, 0x02C54, 0x02C55, 0x02C56, 0x02C57, 0x02C58, 0x02C59, 0x02C5A, 0x02C5B, 0x02C5C, 0x02C5D, 0x02C5E, 0x02C60, 0x02C61, 0x02C62, 0x02C63, 0x02C64, 0x02C65, 0x02C66, 0x02C67, 0x02C68, 0x02C69, 0x02C6A, 0x02C6B, 0x02C6C, 0x02C6D, 0x02C6E, 0x02C6F, 0x02C70, 0x02C71, 0x02C72, 0x02C73, 0x02C74, 0x02C75, 0x02C76, 0x02C77, 0x02C78, 0x02C79, 0x02C7A, 0x02C7B, 0x02C7C, 0x02C7D, 0x02C7E, 0x02C7F, 0x02C80, 0x02C81, 0x02C82, 0x02C83, 0x02C84, 0x02C85, 0x02C86, 0x02C87, 0x02C88, 0x02C89, 0x02C8A, 0x02C8B, 0x02C8C, 0x02C8D, 0x02C8E, 0x02C8F, 0x02C90, 0x02C91, 0x02C92, 0x02C93, 0x02C94, 0x02C95, 0x02C96, 0x02C97, 0x02C98, 0x02C99, 0x02C9A, 0x02C9B, 0x02C9C, 0x02C9D, 0x02C9E, 0x02C9F, 0x02CA0, 0x02CA1, 0x02CA2, 0x02CA3, 0x02CA4, 0x02CA5, 0x02CA6, 0x02CA7, 0x02CA8, 0x02CA9, 0x02CAA, 0x02CAB, 0x02CAC, 0x02CAD, 0x02CAE, 0x02CAF, 0x02CB0, 0x02CB1, 0x02CB2, 0x02CB3, 0x02CB4, 0x02CB5, 0x02CB6, 0x02CB7, 0x02CB8, 0x02CB9, 0x02CBA, 0x02CBB, 0x02CBC, 0x02CBD, 0x02CBE, 0x02CBF, 0x02CC0, 0x02CC1, 0x02CC2, 0x02CC3, 0x02CC4, 0x02CC5, 0x02CC6, 0x02CC7, 0x02CC8, 0x02CC9, 0x02CCA, 0x02CCB, 0x02CCC, 0x02CCD, 0x02CCE, 0x02CCF, 0x02CD0, 0x02CD1, 0x02CD2, 0x02CD3, 0x02CD4, 0x02CD5, 0x02CD6, 0x02CD7, 0x02CD8, 0x02CD9, 0x02CDA, 0x02CDB, 0x02CDC, 0x02CDD, 0x02CDE, 0x02CDF, 0x02CE0, 0x02CE1, 0x02CE2, 0x02CE3, 0x02CE4, 0x02CEB, 0x02CEC, 0x02CED, 0x02CEE, 0x02CF2, 0x02CF3, 0x02D00, 0x02D01, 0x02D02, 0x02D03, 0x02D04, 0x02D05, 0x02D06, 0x02D07, 0x02D08, 0x02D09, 0x02D0A, 0x02D0B, 0x02D0C, 0x02D0D, 0x02D0E, 0x02D0F, 0x02D10, 0x02D11, 0x02D12, 0x02D13, 0x02D14, 0x02D15, 0x02D16, 0x02D17, 0x02D18, 0x02D19, 0x02D1A, 0x02D1B, 0x02D1C, 0x02D1D, 0x02D1E, 0x02D1F, 0x02D20, 0x02D21, 0x02D22, 0x02D23, 0x02D24, 0x02D25, 0x02D27, 0x02D2D, 0x02D30, 0x02D31, 0x02D32, 0x02D33, 0x02D34, 0x02D35, 0x02D36, 0x02D37, 0x02D38, 0x02D39, 0x02D3A, 0x02D3B, 0x02D3C, 0x02D3D, 0x02D3E, 0x02D3F, 0x02D40, 0x02D41, 0x02D42, 0x02D43, 0x02D44, 0x02D45, 0x02D46, 0x02D47, 0x02D48, 0x02D49, 0x02D4A, 0x02D4B, 0x02D4C, 0x02D4D, 0x02D4E, 0x02D4F, 0x02D50, 0x02D51, 0x02D52, 0x02D53, 0x02D54, 0x02D55, 0x02D56, 0x02D57, 0x02D58, 0x02D59, 0x02D5A, 0x02D5B, 0x02D5C, 0x02D5D, 0x02D5E, 0x02D5F, 0x02D60, 0x02D61, 0x02D62, 0x02D63, 0x02D64, 0x02D65, 0x02D66, 0x02D67, 0x02D6F, 0x02D80, 0x02D81, 0x02D82, 0x02D83, 0x02D84, 0x02D85, 0x02D86, 0x02D87, 0x02D88, 0x02D89, 0x02D8A, 0x02D8B, 0x02D8C, 0x02D8D, 0x02D8E, 0x02D8F, 0x02D90, 0x02D91, 0x02D92, 0x02D93, 0x02D94, 0x02D95, 0x02D96, 0x02DA0, 0x02DA1, 0x02DA2, 0x02DA3, 0x02DA4, 0x02DA5, 0x02DA6, 0x02DA8, 0x02DA9, 0x02DAA, 0x02DAB, 0x02DAC, 0x02DAD, 0x02DAE, 0x02DB0, 0x02DB1, 0x02DB2, 0x02DB3, 0x02DB4, 0x02DB5, 0x02DB6, 0x02DB8, 0x02DB9, 0x02DBA, 0x02DBB, 0x02DBC, 0x02DBD, 0x02DBE, 0x02DC0, 0x02DC1, 0x02DC2, 0x02DC3, 0x02DC4, 0x02DC5, 0x02DC6, 0x02DC8, 0x02DC9, 0x02DCA, 0x02DCB, 0x02DCC, 0x02DCD, 0x02DCE, 0x02DD0, 0x02DD1, 0x02DD2, 0x02DD3, 0x02DD4, 0x02DD5, 0x02DD6, 0x02DD8, 0x02DD9, 0x02DDA, 0x02DDB, 0x02DDC, 0x02DDD, 0x02DDE, 0x02E2F, 0x03005, 0x0303B, 0x0303C, 0x03105, 0x03106, 0x03107, 0x03108, 0x03109, 0x0310A, 0x0310B, 0x0310C, 0x0310D, 0x0310E, 0x0310F, 0x03110, 0x03111, 0x03112, 0x03113, 0x03114, 0x03115, 0x03116, 0x03117, 0x03118, 0x03119, 0x0311A, 0x0311B, 0x0311C, 0x0311D, 0x0311E, 0x0311F, 0x03120, 0x03121, 0x03122, 0x03123, 0x03124, 0x03125, 0x03126, 0x03127, 0x03128, 0x03129, 0x0312A, 0x0312B, 0x0312C, 0x0312D, 0x03131, 0x03132, 0x03133, 0x03134, 0x03135, 0x03136, 0x03137, 0x03138, 0x03139, 0x0313A, 0x0313B, 0x0313C, 0x0313D, 0x0313E, 0x0313F, 0x03140, 0x03141, 0x03142, 0x03143, 0x03144, 0x03145, 0x03146, 0x03147, 0x03148, 0x03149, 0x0314A, 0x0314B, 0x0314C, 0x0314D, 0x0314E, 0x0314F, 0x03150, 0x03151, 0x03152, 0x03153, 0x03154, 0x03155, 0x03156, 0x03157, 0x03158, 0x03159, 0x0315A, 0x0315B, 0x0315C, 0x0315D, 0x0315E, 0x0315F, 0x03160, 0x03161, 0x03162, 0x03163, 0x03164, 0x03165, 0x03166, 0x03167, 0x03168, 0x03169, 0x0316A, 0x0316B, 0x0316C, 0x0316D, 0x0316E, 0x0316F, 0x03170, 0x03171, 0x03172, 0x03173, 0x03174, 0x03175, 0x03176, 0x03177, 0x03178, 0x03179, 0x0317A, 0x0317B, 0x0317C, 0x0317D, 0x0317E, 0x0317F, 0x03180, 0x03181, 0x03182, 0x03183, 0x03184, 0x03185, 0x03186, 0x03187, 0x03188, 0x03189, 0x0318A, 0x0318B, 0x0318C, 0x0318D, 0x0318E, 0x031A0, 0x031A1, 0x031A2, 0x031A3, 0x031A4, 0x031A5, 0x031A6, 0x031A7, 0x031A8, 0x031A9, 0x031AA, 0x031AB, 0x031AC, 0x031AD, 0x031AE, 0x031AF, 0x031B0, 0x031B1, 0x031B2, 0x031B3, 0x031B4, 0x031B5, 0x031B6, 0x031B7, 0x031B8, 0x031B9, 0x031BA, 0x0A000, 0x0A001, 0x0A002, 0x0A003, 0x0A004, 0x0A005, 0x0A006, 0x0A007, 0x0A008, 0x0A009, 0x0A00A, 0x0A00B, 0x0A00C, 0x0A00D, 0x0A00E, 0x0A00F, 0x0A010, 0x0A011, 0x0A012, 0x0A013, 0x0A014, 0x0A015, 0x0A016, 0x0A017, 0x0A018, 0x0A019, 0x0A01A, 0x0A01B, 0x0A01C, 0x0A01D, 0x0A01E, 0x0A01F, 0x0A020, 0x0A021, 0x0A022, 0x0A023, 0x0A024, 0x0A025, 0x0A026, 0x0A027, 0x0A028, 0x0A029, 0x0A02A, 0x0A02B, 0x0A02C, 0x0A02D, 0x0A02E, 0x0A02F, 0x0A030, 0x0A031, 0x0A032, 0x0A033, 0x0A034, 0x0A035, 0x0A036, 0x0A037, 0x0A038, 0x0A039, 0x0A03A, 0x0A03B, 0x0A03C, 0x0A03D, 0x0A03E, 0x0A03F, 0x0A040, 0x0A041, 0x0A042, 0x0A043, 0x0A044, 0x0A045, 0x0A046, 0x0A047, 0x0A048, 0x0A049, 0x0A04A, 0x0A04B, 0x0A04C, 0x0A04D, 0x0A04E, 0x0A04F, 0x0A050, 0x0A051, 0x0A052, 0x0A053, 0x0A054, 0x0A055, 0x0A056, 0x0A057, 0x0A058, 0x0A059, 0x0A05A, 0x0A05B, 0x0A05C, 0x0A05D, 0x0A05E, 0x0A05F, 0x0A060, 0x0A061, 0x0A062, 0x0A063, 0x0A064, 0x0A065, 0x0A066, 0x0A067, 0x0A068, 0x0A069, 0x0A06A, 0x0A06B, 0x0A06C, 0x0A06D, 0x0A06E, 0x0A06F, 0x0A070, 0x0A071, 0x0A072, 0x0A073, 0x0A074, 0x0A075, 0x0A076, 0x0A077, 0x0A078, 0x0A079, 0x0A07A, 0x0A07B, 0x0A07C, 0x0A07D, 0x0A07E, 0x0A07F, 0x0A080, 0x0A081, 0x0A082, 0x0A083, 0x0A084, 0x0A085, 0x0A086, 0x0A087, 0x0A088, 0x0A089, 0x0A08A, 0x0A08B, 0x0A08C, 0x0A08D, 0x0A08E, 0x0A08F, 0x0A090, 0x0A091, 0x0A092, 0x0A093, 0x0A094, 0x0A095, 0x0A096, 0x0A097, 0x0A098, 0x0A099, 0x0A09A, 0x0A09B, 0x0A09C, 0x0A09D, 0x0A09E, 0x0A09F, 0x0A0A0, 0x0A0A1, 0x0A0A2, 0x0A0A3, 0x0A0A4, 0x0A0A5, 0x0A0A6, 0x0A0A7, 0x0A0A8, 0x0A0A9, 0x0A0AA, 0x0A0AB, 0x0A0AC, 0x0A0AD, 0x0A0AE, 0x0A0AF, 0x0A0B0, 0x0A0B1, 0x0A0B2, 0x0A0B3, 0x0A0B4, 0x0A0B5, 0x0A0B6, 0x0A0B7, 0x0A0B8, 0x0A0B9, 0x0A0BA, 0x0A0BB, 0x0A0BC, 0x0A0BD, 0x0A0BE, 0x0A0BF, 0x0A0C0, 0x0A0C1, 0x0A0C2, 0x0A0C3, 0x0A0C4, 0x0A0C5, 0x0A0C6, 0x0A0C7, 0x0A0C8, 0x0A0C9, 0x0A0CA, 0x0A0CB, 0x0A0CC, 0x0A0CD, 0x0A0CE, 0x0A0CF, 0x0A0D0, 0x0A0D1, 0x0A0D2, 0x0A0D3, 0x0A0D4, 0x0A0D5, 0x0A0D6, 0x0A0D7, 0x0A0D8, 0x0A0D9, 0x0A0DA, 0x0A0DB, 0x0A0DC, 0x0A0DD, 0x0A0DE, 0x0A0DF, 0x0A0E0, 0x0A0E1, 0x0A0E2, 0x0A0E3, 0x0A0E4, 0x0A0E5, 0x0A0E6, 0x0A0E7, 0x0A0E8, 0x0A0E9, 0x0A0EA, 0x0A0EB, 0x0A0EC, 0x0A0ED, 0x0A0EE, 0x0A0EF, 0x0A0F0, 0x0A0F1, 0x0A0F2, 0x0A0F3, 0x0A0F4, 0x0A0F5, 0x0A0F6, 0x0A0F7, 0x0A0F8, 0x0A0F9, 0x0A0FA, 0x0A0FB, 0x0A0FC, 0x0A0FD, 0x0A0FE, 0x0A0FF, 0x0A100, 0x0A101, 0x0A102, 0x0A103, 0x0A104, 0x0A105, 0x0A106, 0x0A107, 0x0A108, 0x0A109, 0x0A10A, 0x0A10B, 0x0A10C, 0x0A10D, 0x0A10E, 0x0A10F, 0x0A110, 0x0A111, 0x0A112, 0x0A113, 0x0A114, 0x0A115, 0x0A116, 0x0A117, 0x0A118, 0x0A119, 0x0A11A, 0x0A11B, 0x0A11C, 0x0A11D, 0x0A11E, 0x0A11F, 0x0A120, 0x0A121, 0x0A122, 0x0A123, 0x0A124, 0x0A125, 0x0A126, 0x0A127, 0x0A128, 0x0A129, 0x0A12A, 0x0A12B, 0x0A12C, 0x0A12D, 0x0A12E, 0x0A12F, 0x0A130, 0x0A131, 0x0A132, 0x0A133, 0x0A134, 0x0A135, 0x0A136, 0x0A137, 0x0A138, 0x0A139, 0x0A13A, 0x0A13B, 0x0A13C, 0x0A13D, 0x0A13E, 0x0A13F, 0x0A140, 0x0A141, 0x0A142, 0x0A143, 0x0A144, 0x0A145, 0x0A146, 0x0A147, 0x0A148, 0x0A149, 0x0A14A, 0x0A14B, 0x0A14C, 0x0A14D, 0x0A14E, 0x0A14F, 0x0A150, 0x0A151, 0x0A152, 0x0A153, 0x0A154, 0x0A155, 0x0A156, 0x0A157, 0x0A158, 0x0A159, 0x0A15A, 0x0A15B, 0x0A15C, 0x0A15D, 0x0A15E, 0x0A15F, 0x0A160, 0x0A161, 0x0A162, 0x0A163, 0x0A164, 0x0A165, 0x0A166, 0x0A167, 0x0A168, 0x0A169, 0x0A16A, 0x0A16B, 0x0A16C, 0x0A16D, 0x0A16E, 0x0A16F, 0x0A170, 0x0A171, 0x0A172, 0x0A173, 0x0A174, 0x0A175, 0x0A176, 0x0A177, 0x0A178, 0x0A179, 0x0A17A, 0x0A17B, 0x0A17C, 0x0A17D, 0x0A17E, 0x0A17F, 0x0A180, 0x0A181, 0x0A182, 0x0A183, 0x0A184, 0x0A185, 0x0A186, 0x0A187, 0x0A188, 0x0A189, 0x0A18A, 0x0A18B, 0x0A18C, 0x0A18D, 0x0A18E, 0x0A18F, 0x0A190, 0x0A191, 0x0A192, 0x0A193, 0x0A194, 0x0A195, 0x0A196, 0x0A197, 0x0A198, 0x0A199, 0x0A19A, 0x0A19B, 0x0A19C, 0x0A19D, 0x0A19E, 0x0A19F, 0x0A1A0, 0x0A1A1, 0x0A1A2, 0x0A1A3, 0x0A1A4, 0x0A1A5, 0x0A1A6, 0x0A1A7, 0x0A1A8, 0x0A1A9, 0x0A1AA, 0x0A1AB, 0x0A1AC, 0x0A1AD, 0x0A1AE, 0x0A1AF, 0x0A1B0, 0x0A1B1, 0x0A1B2, 0x0A1B3, 0x0A1B4, 0x0A1B5, 0x0A1B6, 0x0A1B7, 0x0A1B8, 0x0A1B9, 0x0A1BA, 0x0A1BB, 0x0A1BC, 0x0A1BD, 0x0A1BE, 0x0A1BF, 0x0A1C0, 0x0A1C1, 0x0A1C2, 0x0A1C3, 0x0A1C4, 0x0A1C5, 0x0A1C6, 0x0A1C7, 0x0A1C8, 0x0A1C9, 0x0A1CA, 0x0A1CB, 0x0A1CC, 0x0A1CD, 0x0A1CE, 0x0A1CF, 0x0A1D0, 0x0A1D1, 0x0A1D2, 0x0A1D3, 0x0A1D4, 0x0A1D5, 0x0A1D6, 0x0A1D7, 0x0A1D8, 0x0A1D9, 0x0A1DA, 0x0A1DB, 0x0A1DC, 0x0A1DD, 0x0A1DE, 0x0A1DF, 0x0A1E0, 0x0A1E1, 0x0A1E2, 0x0A1E3, 0x0A1E4, 0x0A1E5, 0x0A1E6, 0x0A1E7, 0x0A1E8, 0x0A1E9, 0x0A1EA, 0x0A1EB, 0x0A1EC, 0x0A1ED, 0x0A1EE, 0x0A1EF, 0x0A1F0, 0x0A1F1, 0x0A1F2, 0x0A1F3, 0x0A1F4, 0x0A1F5, 0x0A1F6, 0x0A1F7, 0x0A1F8, 0x0A1F9, 0x0A1FA, 0x0A1FB, 0x0A1FC, 0x0A1FD, 0x0A1FE, 0x0A1FF, 0x0A200, 0x0A201, 0x0A202, 0x0A203, 0x0A204, 0x0A205, 0x0A206, 0x0A207, 0x0A208, 0x0A209, 0x0A20A, 0x0A20B, 0x0A20C, 0x0A20D, 0x0A20E, 0x0A20F, 0x0A210, 0x0A211, 0x0A212, 0x0A213, 0x0A214, 0x0A215, 0x0A216, 0x0A217, 0x0A218, 0x0A219, 0x0A21A, 0x0A21B, 0x0A21C, 0x0A21D, 0x0A21E, 0x0A21F, 0x0A220, 0x0A221, 0x0A222, 0x0A223, 0x0A224, 0x0A225, 0x0A226, 0x0A227, 0x0A228, 0x0A229, 0x0A22A, 0x0A22B, 0x0A22C, 0x0A22D, 0x0A22E, 0x0A22F, 0x0A230, 0x0A231, 0x0A232, 0x0A233, 0x0A234, 0x0A235, 0x0A236, 0x0A237, 0x0A238, 0x0A239, 0x0A23A, 0x0A23B, 0x0A23C, 0x0A23D, 0x0A23E, 0x0A23F, 0x0A240, 0x0A241, 0x0A242, 0x0A243, 0x0A244, 0x0A245, 0x0A246, 0x0A247, 0x0A248, 0x0A249, 0x0A24A, 0x0A24B, 0x0A24C, 0x0A24D, 0x0A24E, 0x0A24F, 0x0A250, 0x0A251, 0x0A252, 0x0A253, 0x0A254, 0x0A255, 0x0A256, 0x0A257, 0x0A258, 0x0A259, 0x0A25A, 0x0A25B, 0x0A25C, 0x0A25D, 0x0A25E, 0x0A25F, 0x0A260, 0x0A261, 0x0A262, 0x0A263, 0x0A264, 0x0A265, 0x0A266, 0x0A267, 0x0A268, 0x0A269, 0x0A26A, 0x0A26B, 0x0A26C, 0x0A26D, 0x0A26E, 0x0A26F, 0x0A270, 0x0A271, 0x0A272, 0x0A273, 0x0A274, 0x0A275, 0x0A276, 0x0A277, 0x0A278, 0x0A279, 0x0A27A, 0x0A27B, 0x0A27C, 0x0A27D, 0x0A27E, 0x0A27F, 0x0A280, 0x0A281, 0x0A282, 0x0A283, 0x0A284, 0x0A285, 0x0A286, 0x0A287, 0x0A288, 0x0A289, 0x0A28A, 0x0A28B, 0x0A28C, 0x0A28D, 0x0A28E, 0x0A28F, 0x0A290, 0x0A291, 0x0A292, 0x0A293, 0x0A294, 0x0A295, 0x0A296, 0x0A297, 0x0A298, 0x0A299, 0x0A29A, 0x0A29B, 0x0A29C, 0x0A29D, 0x0A29E, 0x0A29F, 0x0A2A0, 0x0A2A1, 0x0A2A2, 0x0A2A3, 0x0A2A4, 0x0A2A5, 0x0A2A6, 0x0A2A7, 0x0A2A8, 0x0A2A9, 0x0A2AA, 0x0A2AB, 0x0A2AC, 0x0A2AD, 0x0A2AE, 0x0A2AF, 0x0A2B0, 0x0A2B1, 0x0A2B2, 0x0A2B3, 0x0A2B4, 0x0A2B5, 0x0A2B6, 0x0A2B7, 0x0A2B8, 0x0A2B9, 0x0A2BA, 0x0A2BB, 0x0A2BC, 0x0A2BD, 0x0A2BE, 0x0A2BF, 0x0A2C0, 0x0A2C1, 0x0A2C2, 0x0A2C3, 0x0A2C4, 0x0A2C5, 0x0A2C6, 0x0A2C7, 0x0A2C8, 0x0A2C9, 0x0A2CA, 0x0A2CB, 0x0A2CC, 0x0A2CD, 0x0A2CE, 0x0A2CF, 0x0A2D0, 0x0A2D1, 0x0A2D2, 0x0A2D3, 0x0A2D4, 0x0A2D5, 0x0A2D6, 0x0A2D7, 0x0A2D8, 0x0A2D9, 0x0A2DA, 0x0A2DB, 0x0A2DC, 0x0A2DD, 0x0A2DE, 0x0A2DF, 0x0A2E0, 0x0A2E1, 0x0A2E2, 0x0A2E3, 0x0A2E4, 0x0A2E5, 0x0A2E6, 0x0A2E7, 0x0A2E8, 0x0A2E9, 0x0A2EA, 0x0A2EB, 0x0A2EC, 0x0A2ED, 0x0A2EE, 0x0A2EF, 0x0A2F0, 0x0A2F1, 0x0A2F2, 0x0A2F3, 0x0A2F4, 0x0A2F5, 0x0A2F6, 0x0A2F7, 0x0A2F8, 0x0A2F9, 0x0A2FA, 0x0A2FB, 0x0A2FC, 0x0A2FD, 0x0A2FE, 0x0A2FF, 0x0A300, 0x0A301, 0x0A302, 0x0A303, 0x0A304, 0x0A305, 0x0A306, 0x0A307, 0x0A308, 0x0A309, 0x0A30A, 0x0A30B, 0x0A30C, 0x0A30D, 0x0A30E, 0x0A30F, 0x0A310, 0x0A311, 0x0A312, 0x0A313, 0x0A314, 0x0A315, 0x0A316, 0x0A317, 0x0A318, 0x0A319, 0x0A31A, 0x0A31B, 0x0A31C, 0x0A31D, 0x0A31E, 0x0A31F, 0x0A320, 0x0A321, 0x0A322, 0x0A323, 0x0A324, 0x0A325, 0x0A326, 0x0A327, 0x0A328, 0x0A329, 0x0A32A, 0x0A32B, 0x0A32C, 0x0A32D, 0x0A32E, 0x0A32F, 0x0A330, 0x0A331, 0x0A332, 0x0A333, 0x0A334, 0x0A335, 0x0A336, 0x0A337, 0x0A338, 0x0A339, 0x0A33A, 0x0A33B, 0x0A33C, 0x0A33D, 0x0A33E, 0x0A33F, 0x0A340, 0x0A341, 0x0A342, 0x0A343, 0x0A344, 0x0A345, 0x0A346, 0x0A347, 0x0A348, 0x0A349, 0x0A34A, 0x0A34B, 0x0A34C, 0x0A34D, 0x0A34E, 0x0A34F, 0x0A350, 0x0A351, 0x0A352, 0x0A353, 0x0A354, 0x0A355, 0x0A356, 0x0A357, 0x0A358, 0x0A359, 0x0A35A, 0x0A35B, 0x0A35C, 0x0A35D, 0x0A35E, 0x0A35F, 0x0A360, 0x0A361, 0x0A362, 0x0A363, 0x0A364, 0x0A365, 0x0A366, 0x0A367, 0x0A368, 0x0A369, 0x0A36A, 0x0A36B, 0x0A36C, 0x0A36D, 0x0A36E, 0x0A36F, 0x0A370, 0x0A371, 0x0A372, 0x0A373, 0x0A374, 0x0A375, 0x0A376, 0x0A377, 0x0A378, 0x0A379, 0x0A37A, 0x0A37B, 0x0A37C, 0x0A37D, 0x0A37E, 0x0A37F, 0x0A380, 0x0A381, 0x0A382, 0x0A383, 0x0A384, 0x0A385, 0x0A386, 0x0A387, 0x0A388, 0x0A389, 0x0A38A, 0x0A38B, 0x0A38C, 0x0A38D, 0x0A38E, 0x0A38F, 0x0A390, 0x0A391, 0x0A392, 0x0A393, 0x0A394, 0x0A395, 0x0A396, 0x0A397, 0x0A398, 0x0A399, 0x0A39A, 0x0A39B, 0x0A39C, 0x0A39D, 0x0A39E, 0x0A39F, 0x0A3A0, 0x0A3A1, 0x0A3A2, 0x0A3A3, 0x0A3A4, 0x0A3A5, 0x0A3A6, 0x0A3A7, 0x0A3A8, 0x0A3A9, 0x0A3AA, 0x0A3AB, 0x0A3AC, 0x0A3AD, 0x0A3AE, 0x0A3AF, 0x0A3B0, 0x0A3B1, 0x0A3B2, 0x0A3B3, 0x0A3B4, 0x0A3B5, 0x0A3B6, 0x0A3B7, 0x0A3B8, 0x0A3B9, 0x0A3BA, 0x0A3BB, 0x0A3BC, 0x0A3BD, 0x0A3BE, 0x0A3BF, 0x0A3C0, 0x0A3C1, 0x0A3C2, 0x0A3C3, 0x0A3C4, 0x0A3C5, 0x0A3C6, 0x0A3C7, 0x0A3C8, 0x0A3C9, 0x0A3CA, 0x0A3CB, 0x0A3CC, 0x0A3CD, 0x0A3CE, 0x0A3CF, 0x0A3D0, 0x0A3D1, 0x0A3D2, 0x0A3D3, 0x0A3D4, 0x0A3D5, 0x0A3D6, 0x0A3D7, 0x0A3D8, 0x0A3D9, 0x0A3DA, 0x0A3DB, 0x0A3DC, 0x0A3DD, 0x0A3DE, 0x0A3DF, 0x0A3E0, 0x0A3E1, 0x0A3E2, 0x0A3E3, 0x0A3E4, 0x0A3E5, 0x0A3E6, 0x0A3E7, 0x0A3E8, 0x0A3E9, 0x0A3EA, 0x0A3EB, 0x0A3EC, 0x0A3ED, 0x0A3EE, 0x0A3EF, 0x0A3F0, 0x0A3F1, 0x0A3F2, 0x0A3F3, 0x0A3F4, 0x0A3F5, 0x0A3F6, 0x0A3F7, 0x0A3F8, 0x0A3F9, 0x0A3FA, 0x0A3FB, 0x0A3FC, 0x0A3FD, 0x0A3FE, 0x0A3FF, 0x0A400, 0x0A401, 0x0A402, 0x0A403, 0x0A404, 0x0A405, 0x0A406, 0x0A407, 0x0A408, 0x0A409, 0x0A40A, 0x0A40B, 0x0A40C, 0x0A40D, 0x0A40E, 0x0A40F, 0x0A410, 0x0A411, 0x0A412, 0x0A413, 0x0A414, 0x0A415, 0x0A416, 0x0A417, 0x0A418, 0x0A419, 0x0A41A, 0x0A41B, 0x0A41C, 0x0A41D, 0x0A41E, 0x0A41F, 0x0A420, 0x0A421, 0x0A422, 0x0A423, 0x0A424, 0x0A425, 0x0A426, 0x0A427, 0x0A428, 0x0A429, 0x0A42A, 0x0A42B, 0x0A42C, 0x0A42D, 0x0A42E, 0x0A42F, 0x0A430, 0x0A431, 0x0A432, 0x0A433, 0x0A434, 0x0A435, 0x0A436, 0x0A437, 0x0A438, 0x0A439, 0x0A43A, 0x0A43B, 0x0A43C, 0x0A43D, 0x0A43E, 0x0A43F, 0x0A440, 0x0A441, 0x0A442, 0x0A443, 0x0A444, 0x0A445, 0x0A446, 0x0A447, 0x0A448, 0x0A449, 0x0A44A, 0x0A44B, 0x0A44C, 0x0A44D, 0x0A44E, 0x0A44F, 0x0A450, 0x0A451, 0x0A452, 0x0A453, 0x0A454, 0x0A455, 0x0A456, 0x0A457, 0x0A458, 0x0A459, 0x0A45A, 0x0A45B, 0x0A45C, 0x0A45D, 0x0A45E, 0x0A45F, 0x0A460, 0x0A461, 0x0A462, 0x0A463, 0x0A464, 0x0A465, 0x0A466, 0x0A467, 0x0A468, 0x0A469, 0x0A46A, 0x0A46B, 0x0A46C, 0x0A46D, 0x0A46E, 0x0A46F, 0x0A470, 0x0A471, 0x0A472, 0x0A473, 0x0A474, 0x0A475, 0x0A476, 0x0A477, 0x0A478, 0x0A479, 0x0A47A, 0x0A47B, 0x0A47C, 0x0A47D, 0x0A47E, 0x0A47F, 0x0A480, 0x0A481, 0x0A482, 0x0A483, 0x0A484, 0x0A485, 0x0A486, 0x0A487, 0x0A488, 0x0A489, 0x0A48A, 0x0A48B, 0x0A48C, 0x0A4D0, 0x0A4D1, 0x0A4D2, 0x0A4D3, 0x0A4D4, 0x0A4D5, 0x0A4D6, 0x0A4D7, 0x0A4D8, 0x0A4D9, 0x0A4DA, 0x0A4DB, 0x0A4DC, 0x0A4DD, 0x0A4DE, 0x0A4DF, 0x0A4E0, 0x0A4E1, 0x0A4E2, 0x0A4E3, 0x0A4E4, 0x0A4E5, 0x0A4E6, 0x0A4E7, 0x0A4E8, 0x0A4E9, 0x0A4EA, 0x0A4EB, 0x0A4EC, 0x0A4ED, 0x0A4EE, 0x0A4EF, 0x0A4F0, 0x0A4F1, 0x0A4F2, 0x0A4F3, 0x0A4F4, 0x0A4F5, 0x0A4F6, 0x0A4F7, 0x0A4F8, 0x0A4F9, 0x0A4FA, 0x0A4FB, 0x0A4FC, 0x0A4FD, 0x0A500, 0x0A501, 0x0A502, 0x0A503, 0x0A504, 0x0A505, 0x0A506, 0x0A507, 0x0A508, 0x0A509, 0x0A50A, 0x0A50B, 0x0A50C, 0x0A50D, 0x0A50E, 0x0A50F, 0x0A510, 0x0A511, 0x0A512, 0x0A513, 0x0A514, 0x0A515, 0x0A516, 0x0A517, 0x0A518, 0x0A519, 0x0A51A, 0x0A51B, 0x0A51C, 0x0A51D, 0x0A51E, 0x0A51F, 0x0A520, 0x0A521, 0x0A522, 0x0A523, 0x0A524, 0x0A525, 0x0A526, 0x0A527, 0x0A528, 0x0A529, 0x0A52A, 0x0A52B, 0x0A52C, 0x0A52D, 0x0A52E, 0x0A52F, 0x0A530, 0x0A531, 0x0A532, 0x0A533, 0x0A534, 0x0A535, 0x0A536, 0x0A537, 0x0A538, 0x0A539, 0x0A53A, 0x0A53B, 0x0A53C, 0x0A53D, 0x0A53E, 0x0A53F, 0x0A540, 0x0A541, 0x0A542, 0x0A543, 0x0A544, 0x0A545, 0x0A546, 0x0A547, 0x0A548, 0x0A549, 0x0A54A, 0x0A54B, 0x0A54C, 0x0A54D, 0x0A54E, 0x0A54F, 0x0A550, 0x0A551, 0x0A552, 0x0A553, 0x0A554, 0x0A555, 0x0A556, 0x0A557, 0x0A558, 0x0A559, 0x0A55A, 0x0A55B, 0x0A55C, 0x0A55D, 0x0A55E, 0x0A55F, 0x0A560, 0x0A561, 0x0A562, 0x0A563, 0x0A564, 0x0A565, 0x0A566, 0x0A567, 0x0A568, 0x0A569, 0x0A56A, 0x0A56B, 0x0A56C, 0x0A56D, 0x0A56E, 0x0A56F, 0x0A570, 0x0A571, 0x0A572, 0x0A573, 0x0A574, 0x0A575, 0x0A576, 0x0A577, 0x0A578, 0x0A579, 0x0A57A, 0x0A57B, 0x0A57C, 0x0A57D, 0x0A57E, 0x0A57F, 0x0A580, 0x0A581, 0x0A582, 0x0A583, 0x0A584, 0x0A585, 0x0A586, 0x0A587, 0x0A588, 0x0A589, 0x0A58A, 0x0A58B, 0x0A58C, 0x0A58D, 0x0A58E, 0x0A58F, 0x0A590, 0x0A591, 0x0A592, 0x0A593, 0x0A594, 0x0A595, 0x0A596, 0x0A597, 0x0A598, 0x0A599, 0x0A59A, 0x0A59B, 0x0A59C, 0x0A59D, 0x0A59E, 0x0A59F, 0x0A5A0, 0x0A5A1, 0x0A5A2, 0x0A5A3, 0x0A5A4, 0x0A5A5, 0x0A5A6, 0x0A5A7, 0x0A5A8, 0x0A5A9, 0x0A5AA, 0x0A5AB, 0x0A5AC, 0x0A5AD, 0x0A5AE, 0x0A5AF, 0x0A5B0, 0x0A5B1, 0x0A5B2, 0x0A5B3, 0x0A5B4, 0x0A5B5, 0x0A5B6, 0x0A5B7, 0x0A5B8, 0x0A5B9, 0x0A5BA, 0x0A5BB, 0x0A5BC, 0x0A5BD, 0x0A5BE, 0x0A5BF, 0x0A5C0, 0x0A5C1, 0x0A5C2, 0x0A5C3, 0x0A5C4, 0x0A5C5, 0x0A5C6, 0x0A5C7, 0x0A5C8, 0x0A5C9, 0x0A5CA, 0x0A5CB, 0x0A5CC, 0x0A5CD, 0x0A5CE, 0x0A5CF, 0x0A5D0, 0x0A5D1, 0x0A5D2, 0x0A5D3, 0x0A5D4, 0x0A5D5, 0x0A5D6, 0x0A5D7, 0x0A5D8, 0x0A5D9, 0x0A5DA, 0x0A5DB, 0x0A5DC, 0x0A5DD, 0x0A5DE, 0x0A5DF, 0x0A5E0, 0x0A5E1, 0x0A5E2, 0x0A5E3, 0x0A5E4, 0x0A5E5, 0x0A5E6, 0x0A5E7, 0x0A5E8, 0x0A5E9, 0x0A5EA, 0x0A5EB, 0x0A5EC, 0x0A5ED, 0x0A5EE, 0x0A5EF, 0x0A5F0, 0x0A5F1, 0x0A5F2, 0x0A5F3, 0x0A5F4, 0x0A5F5, 0x0A5F6, 0x0A5F7, 0x0A5F8, 0x0A5F9, 0x0A5FA, 0x0A5FB, 0x0A5FC, 0x0A5FD, 0x0A5FE, 0x0A5FF, 0x0A600, 0x0A601, 0x0A602, 0x0A603, 0x0A604, 0x0A605, 0x0A606, 0x0A607, 0x0A608, 0x0A609, 0x0A60A, 0x0A60B, 0x0A60C, 0x0A610, 0x0A611, 0x0A612, 0x0A613, 0x0A614, 0x0A615, 0x0A616, 0x0A617, 0x0A618, 0x0A619, 0x0A61A, 0x0A61B, 0x0A61C, 0x0A61D, 0x0A61E, 0x0A61F, 0x0A62A, 0x0A62B, 0x0A640, 0x0A641, 0x0A642, 0x0A643, 0x0A644, 0x0A645, 0x0A646, 0x0A647, 0x0A648, 0x0A649, 0x0A64A, 0x0A64B, 0x0A64C, 0x0A64D, 0x0A64E, 0x0A64F, 0x0A650, 0x0A651, 0x0A652, 0x0A653, 0x0A654, 0x0A655, 0x0A656, 0x0A657, 0x0A658, 0x0A659, 0x0A65A, 0x0A65B, 0x0A65C, 0x0A65D, 0x0A65E, 0x0A65F, 0x0A660, 0x0A661, 0x0A662, 0x0A663, 0x0A664, 0x0A665, 0x0A666, 0x0A667, 0x0A668, 0x0A669, 0x0A66A, 0x0A66B, 0x0A66C, 0x0A66D, 0x0A66E, 0x0A67F, 0x0A680, 0x0A681, 0x0A682, 0x0A683, 0x0A684, 0x0A685, 0x0A686, 0x0A687, 0x0A688, 0x0A689, 0x0A68A, 0x0A68B, 0x0A68C, 0x0A68D, 0x0A68E, 0x0A68F, 0x0A690, 0x0A691, 0x0A692, 0x0A693, 0x0A694, 0x0A695, 0x0A696, 0x0A697, 0x0A698, 0x0A699, 0x0A69A, 0x0A69B, 0x0A69C, 0x0A69D, 0x0A6A0, 0x0A6A1, 0x0A6A2, 0x0A6A3, 0x0A6A4, 0x0A6A5, 0x0A6A6, 0x0A6A7, 0x0A6A8, 0x0A6A9, 0x0A6AA, 0x0A6AB, 0x0A6AC, 0x0A6AD, 0x0A6AE, 0x0A6AF, 0x0A6B0, 0x0A6B1, 0x0A6B2, 0x0A6B3, 0x0A6B4, 0x0A6B5, 0x0A6B6, 0x0A6B7, 0x0A6B8, 0x0A6B9, 0x0A6BA, 0x0A6BB, 0x0A6BC, 0x0A6BD, 0x0A6BE, 0x0A6BF, 0x0A6C0, 0x0A6C1, 0x0A6C2, 0x0A6C3, 0x0A6C4, 0x0A6C5, 0x0A6C6, 0x0A6C7, 0x0A6C8, 0x0A6C9, 0x0A6CA, 0x0A6CB, 0x0A6CC, 0x0A6CD, 0x0A6CE, 0x0A6CF, 0x0A6D0, 0x0A6D1, 0x0A6D2, 0x0A6D3, 0x0A6D4, 0x0A6D5, 0x0A6D6, 0x0A6D7, 0x0A6D8, 0x0A6D9, 0x0A6DA, 0x0A6DB, 0x0A6DC, 0x0A6DD, 0x0A6DE, 0x0A6DF, 0x0A6E0, 0x0A6E1, 0x0A6E2, 0x0A6E3, 0x0A6E4, 0x0A6E5, 0x0A6E6, 0x0A6E7, 0x0A6E8, 0x0A6E9, 0x0A6EA, 0x0A6EB, 0x0A6EC, 0x0A6ED, 0x0A6EE, 0x0A6EF, 0x0A717, 0x0A718, 0x0A719, 0x0A71A, 0x0A71B, 0x0A71C, 0x0A71D, 0x0A71E, 0x0A71F, 0x0A722, 0x0A723, 0x0A724, 0x0A725, 0x0A726, 0x0A727, 0x0A728, 0x0A729, 0x0A72A, 0x0A72B, 0x0A72C, 0x0A72D, 0x0A72E, 0x0A72F, 0x0A730, 0x0A731, 0x0A732, 0x0A733, 0x0A734, 0x0A735, 0x0A736, 0x0A737, 0x0A738, 0x0A739, 0x0A73A, 0x0A73B, 0x0A73C, 0x0A73D, 0x0A73E, 0x0A73F, 0x0A740, 0x0A741, 0x0A742, 0x0A743, 0x0A744, 0x0A745, 0x0A746, 0x0A747, 0x0A748, 0x0A749, 0x0A74A, 0x0A74B, 0x0A74C, 0x0A74D, 0x0A74E, 0x0A74F, 0x0A750, 0x0A751, 0x0A752, 0x0A753, 0x0A754, 0x0A755, 0x0A756, 0x0A757, 0x0A758, 0x0A759, 0x0A75A, 0x0A75B, 0x0A75C, 0x0A75D, 0x0A75E, 0x0A75F, 0x0A760, 0x0A761, 0x0A762, 0x0A763, 0x0A764, 0x0A765, 0x0A766, 0x0A767, 0x0A768, 0x0A769, 0x0A76A, 0x0A76B, 0x0A76C, 0x0A76D, 0x0A76E, 0x0A76F, 0x0A770, 0x0A771, 0x0A772, 0x0A773, 0x0A774, 0x0A775, 0x0A776, 0x0A777, 0x0A778, 0x0A779, 0x0A77A, 0x0A77B, 0x0A77C, 0x0A77D, 0x0A77E, 0x0A77F, 0x0A780, 0x0A781, 0x0A782, 0x0A783, 0x0A784, 0x0A785, 0x0A786, 0x0A787, 0x0A788, 0x0A78B, 0x0A78C, 0x0A78D, 0x0A78E, 0x0A78F, 0x0A790, 0x0A791, 0x0A792, 0x0A793, 0x0A794, 0x0A795, 0x0A796, 0x0A797, 0x0A798, 0x0A799, 0x0A79A, 0x0A79B, 0x0A79C, 0x0A79D, 0x0A79E, 0x0A79F, 0x0A7A0, 0x0A7A1, 0x0A7A2, 0x0A7A3, 0x0A7A4, 0x0A7A5, 0x0A7A6, 0x0A7A7, 0x0A7A8, 0x0A7A9, 0x0A7AA, 0x0A7AB, 0x0A7AC, 0x0A7AD, 0x0A7AE, 0x0A7B0, 0x0A7B1, 0x0A7B2, 0x0A7B3, 0x0A7B4, 0x0A7B5, 0x0A7B6, 0x0A7B7, 0x0A7F7, 0x0A7F8, 0x0A7F9, 0x0A7FA, 0x0A7FB, 0x0A7FC, 0x0A7FD, 0x0A7FE, 0x0A7FF, 0x0A800, 0x0A801, 0x0A803, 0x0A804, 0x0A805, 0x0A807, 0x0A808, 0x0A809, 0x0A80A, 0x0A80C, 0x0A80D, 0x0A80E, 0x0A80F, 0x0A810, 0x0A811, 0x0A812, 0x0A813, 0x0A814, 0x0A815, 0x0A816, 0x0A817, 0x0A818, 0x0A819, 0x0A81A, 0x0A81B, 0x0A81C, 0x0A81D, 0x0A81E, 0x0A81F, 0x0A820, 0x0A821, 0x0A822, 0x0A840, 0x0A841, 0x0A842, 0x0A843, 0x0A844, 0x0A845, 0x0A846, 0x0A847, 0x0A848, 0x0A849, 0x0A84A, 0x0A84B, 0x0A84C, 0x0A84D, 0x0A84E, 0x0A84F, 0x0A850, 0x0A851, 0x0A852, 0x0A853, 0x0A854, 0x0A855, 0x0A856, 0x0A857, 0x0A858, 0x0A859, 0x0A85A, 0x0A85B, 0x0A85C, 0x0A85D, 0x0A85E, 0x0A85F, 0x0A860, 0x0A861, 0x0A862, 0x0A863, 0x0A864, 0x0A865, 0x0A866, 0x0A867, 0x0A868, 0x0A869, 0x0A86A, 0x0A86B, 0x0A86C, 0x0A86D, 0x0A86E, 0x0A86F, 0x0A870, 0x0A871, 0x0A872, 0x0A873, 0x0A882, 0x0A883, 0x0A884, 0x0A885, 0x0A886, 0x0A887, 0x0A888, 0x0A889, 0x0A88A, 0x0A88B, 0x0A88C, 0x0A88D, 0x0A88E, 0x0A88F, 0x0A890, 0x0A891, 0x0A892, 0x0A893, 0x0A894, 0x0A895, 0x0A896, 0x0A897, 0x0A898, 0x0A899, 0x0A89A, 0x0A89B, 0x0A89C, 0x0A89D, 0x0A89E, 0x0A89F, 0x0A8A0, 0x0A8A1, 0x0A8A2, 0x0A8A3, 0x0A8A4, 0x0A8A5, 0x0A8A6, 0x0A8A7, 0x0A8A8, 0x0A8A9, 0x0A8AA, 0x0A8AB, 0x0A8AC, 0x0A8AD, 0x0A8AE, 0x0A8AF, 0x0A8B0, 0x0A8B1, 0x0A8B2, 0x0A8B3, 0x0A8F2, 0x0A8F3, 0x0A8F4, 0x0A8F5, 0x0A8F6, 0x0A8F7, 0x0A8FB, 0x0A8FD, 0x0A90A, 0x0A90B, 0x0A90C, 0x0A90D, 0x0A90E, 0x0A90F, 0x0A910, 0x0A911, 0x0A912, 0x0A913, 0x0A914, 0x0A915, 0x0A916, 0x0A917, 0x0A918, 0x0A919, 0x0A91A, 0x0A91B, 0x0A91C, 0x0A91D, 0x0A91E, 0x0A91F, 0x0A920, 0x0A921, 0x0A922, 0x0A923, 0x0A924, 0x0A925, 0x0A930, 0x0A931, 0x0A932, 0x0A933, 0x0A934, 0x0A935, 0x0A936, 0x0A937, 0x0A938, 0x0A939, 0x0A93A, 0x0A93B, 0x0A93C, 0x0A93D, 0x0A93E, 0x0A93F, 0x0A940, 0x0A941, 0x0A942, 0x0A943, 0x0A944, 0x0A945, 0x0A946, 0x0A960, 0x0A961, 0x0A962, 0x0A963, 0x0A964, 0x0A965, 0x0A966, 0x0A967, 0x0A968, 0x0A969, 0x0A96A, 0x0A96B, 0x0A96C, 0x0A96D, 0x0A96E, 0x0A96F, 0x0A970, 0x0A971, 0x0A972, 0x0A973, 0x0A974, 0x0A975, 0x0A976, 0x0A977, 0x0A978, 0x0A979, 0x0A97A, 0x0A97B, 0x0A97C, 0x0A984, 0x0A985, 0x0A986, 0x0A987, 0x0A988, 0x0A989, 0x0A98A, 0x0A98B, 0x0A98C, 0x0A98D, 0x0A98E, 0x0A98F, 0x0A990, 0x0A991, 0x0A992, 0x0A993, 0x0A994, 0x0A995, 0x0A996, 0x0A997, 0x0A998, 0x0A999, 0x0A99A, 0x0A99B, 0x0A99C, 0x0A99D, 0x0A99E, 0x0A99F, 0x0A9A0, 0x0A9A1, 0x0A9A2, 0x0A9A3, 0x0A9A4, 0x0A9A5, 0x0A9A6, 0x0A9A7, 0x0A9A8, 0x0A9A9, 0x0A9AA, 0x0A9AB, 0x0A9AC, 0x0A9AD, 0x0A9AE, 0x0A9AF, 0x0A9B0, 0x0A9B1, 0x0A9B2, 0x0A9CF, 0x0AA00, 0x0AA01, 0x0AA02, 0x0AA03, 0x0AA04, 0x0AA05, 0x0AA06, 0x0AA07, 0x0AA08, 0x0AA09, 0x0AA0A, 0x0AA0B, 0x0AA0C, 0x0AA0D, 0x0AA0E, 0x0AA0F, 0x0AA10, 0x0AA11, 0x0AA12, 0x0AA13, 0x0AA14, 0x0AA15, 0x0AA16, 0x0AA17, 0x0AA18, 0x0AA19, 0x0AA1A, 0x0AA1B, 0x0AA1C, 0x0AA1D, 0x0AA1E, 0x0AA1F, 0x0AA20, 0x0AA21, 0x0AA22, 0x0AA23, 0x0AA24, 0x0AA25, 0x0AA26, 0x0AA27, 0x0AA28, 0x0AA40, 0x0AA41, 0x0AA42, 0x0AA44, 0x0AA45, 0x0AA46, 0x0AA47, 0x0AA48, 0x0AA49, 0x0AA4A, 0x0AA4B, 0x0AAE0, 0x0AAE1, 0x0AAE2, 0x0AAE3, 0x0AAE4, 0x0AAE5, 0x0AAE6, 0x0AAE7, 0x0AAE8, 0x0AAE9, 0x0AAEA, 0x0AAF2, 0x0AAF3, 0x0AAF4, 0x0AB01, 0x0AB02, 0x0AB03, 0x0AB04, 0x0AB05, 0x0AB06, 0x0AB09, 0x0AB0A, 0x0AB0B, 0x0AB0C, 0x0AB0D, 0x0AB0E, 0x0AB11, 0x0AB12, 0x0AB13, 0x0AB14, 0x0AB15, 0x0AB16, 0x0AB20, 0x0AB21, 0x0AB22, 0x0AB23, 0x0AB24, 0x0AB25, 0x0AB26, 0x0AB28, 0x0AB29, 0x0AB2A, 0x0AB2B, 0x0AB2C, 0x0AB2D, 0x0AB2E, 0x0AB30, 0x0AB31, 0x0AB32, 0x0AB33, 0x0AB34, 0x0AB35, 0x0AB36, 0x0AB37, 0x0AB38, 0x0AB39, 0x0AB3A, 0x0AB3B, 0x0AB3C, 0x0AB3D, 0x0AB3E, 0x0AB3F, 0x0AB40, 0x0AB41, 0x0AB42, 0x0AB43, 0x0AB44, 0x0AB45, 0x0AB46, 0x0AB47, 0x0AB48, 0x0AB49, 0x0AB4A, 0x0AB4B, 0x0AB4C, 0x0AB4D, 0x0AB4E, 0x0AB4F, 0x0AB50, 0x0AB51, 0x0AB52, 0x0AB53, 0x0AB54, 0x0AB55, 0x0AB56, 0x0AB57, 0x0AB58, 0x0AB59, 0x0AB5A, 0x0AB5C, 0x0AB5D, 0x0AB5E, 0x0AB5F, 0x0AB60, 0x0AB61, 0x0AB62, 0x0AB63, 0x0AB64, 0x0AB65, 0x0AB70, 0x0AB71, 0x0AB72, 0x0AB73, 0x0AB74, 0x0AB75, 0x0AB76, 0x0AB77, 0x0AB78, 0x0AB79, 0x0AB7A, 0x0AB7B, 0x0AB7C, 0x0AB7D, 0x0AB7E, 0x0AB7F, 0x0AB80, 0x0AB81, 0x0AB82, 0x0AB83, 0x0AB84, 0x0AB85, 0x0AB86, 0x0AB87, 0x0AB88, 0x0AB89, 0x0AB8A, 0x0AB8B, 0x0AB8C, 0x0AB8D, 0x0AB8E, 0x0AB8F, 0x0AB90, 0x0AB91, 0x0AB92, 0x0AB93, 0x0AB94, 0x0AB95, 0x0AB96, 0x0AB97, 0x0AB98, 0x0AB99, 0x0AB9A, 0x0AB9B, 0x0AB9C, 0x0AB9D, 0x0AB9E, 0x0AB9F, 0x0ABA0, 0x0ABA1, 0x0ABA2, 0x0ABA3, 0x0ABA4, 0x0ABA5, 0x0ABA6, 0x0ABA7, 0x0ABA8, 0x0ABA9, 0x0ABAA, 0x0ABAB, 0x0ABAC, 0x0ABAD, 0x0ABAE, 0x0ABAF, 0x0ABB0, 0x0ABB1, 0x0ABB2, 0x0ABB3, 0x0ABB4, 0x0ABB5, 0x0ABB6, 0x0ABB7, 0x0ABB8, 0x0ABB9, 0x0ABBA, 0x0ABBB, 0x0ABBC, 0x0ABBD, 0x0ABBE, 0x0ABBF, 0x0ABC0, 0x0ABC1, 0x0ABC2, 0x0ABC3, 0x0ABC4, 0x0ABC5, 0x0ABC6, 0x0ABC7, 0x0ABC8, 0x0ABC9, 0x0ABCA, 0x0ABCB, 0x0ABCC, 0x0ABCD, 0x0ABCE, 0x0ABCF, 0x0ABD0, 0x0ABD1, 0x0ABD2, 0x0ABD3, 0x0ABD4, 0x0ABD5, 0x0ABD6, 0x0ABD7, 0x0ABD8, 0x0ABD9, 0x0ABDA, 0x0ABDB, 0x0ABDC, 0x0ABDD, 0x0ABDE, 0x0ABDF, 0x0ABE0, 0x0ABE1, 0x0ABE2, 0x0AC00, 0x0AC01, 0x0AC02, 0x0AC03, 0x0AC04, 0x0AC05, 0x0AC06, 0x0AC07, 0x0AC08, 0x0AC09, 0x0AC0A, 0x0AC0B, 0x0AC0C, 0x0AC0D, 0x0AC0E, 0x0AC0F, 0x0AC10, 0x0AC11, 0x0AC12, 0x0AC13, 0x0AC14, 0x0AC15, 0x0AC16, 0x0AC17, 0x0AC18, 0x0AC19, 0x0AC1A, 0x0AC1B, 0x0AC1C, 0x0AC1D, 0x0AC1E, 0x0AC1F, 0x0AC20, 0x0AC21, 0x0AC22, 0x0AC23, 0x0AC24, 0x0AC25, 0x0AC26, 0x0AC27, 0x0AC28, 0x0AC29, 0x0AC2A, 0x0AC2B, 0x0AC2C, 0x0AC2D, 0x0AC2E, 0x0AC2F, 0x0AC30, 0x0AC31, 0x0AC32, 0x0AC33, 0x0AC34, 0x0AC35, 0x0AC36, 0x0AC37, 0x0AC38, 0x0AC39, 0x0AC3A, 0x0AC3B, 0x0AC3C, 0x0AC3D, 0x0AC3E, 0x0AC3F, 0x0AC40, 0x0AC41, 0x0AC42, 0x0AC43, 0x0AC44, 0x0AC45, 0x0AC46, 0x0AC47, 0x0AC48, 0x0AC49, 0x0AC4A, 0x0AC4B, 0x0AC4C, 0x0AC4D, 0x0AC4E, 0x0AC4F, 0x0AC50, 0x0AC51, 0x0AC52, 0x0AC53, 0x0AC54, 0x0AC55, 0x0AC56, 0x0AC57, 0x0AC58, 0x0AC59, 0x0AC5A, 0x0AC5B, 0x0AC5C, 0x0AC5D, 0x0AC5E, 0x0AC5F, 0x0AC60, 0x0AC61, 0x0AC62, 0x0AC63, 0x0AC64, 0x0AC65, 0x0AC66, 0x0AC67, 0x0AC68, 0x0AC69, 0x0AC6A, 0x0AC6B, 0x0AC6C, 0x0AC6D, 0x0AC6E, 0x0AC6F, 0x0AC70, 0x0AC71, 0x0AC72, 0x0AC73, 0x0AC74, 0x0AC75, 0x0AC76, 0x0AC77, 0x0AC78, 0x0AC79, 0x0AC7A, 0x0AC7B, 0x0AC7C, 0x0AC7D, 0x0AC7E, 0x0AC7F, 0x0AC80, 0x0AC81, 0x0AC82, 0x0AC83, 0x0AC84, 0x0AC85, 0x0AC86, 0x0AC87, 0x0AC88, 0x0AC89, 0x0AC8A, 0x0AC8B, 0x0AC8C, 0x0AC8D, 0x0AC8E, 0x0AC8F, 0x0AC90, 0x0AC91, 0x0AC92, 0x0AC93, 0x0AC94, 0x0AC95, 0x0AC96, 0x0AC97, 0x0AC98, 0x0AC99, 0x0AC9A, 0x0AC9B, 0x0AC9C, 0x0AC9D, 0x0AC9E, 0x0AC9F, 0x0ACA0, 0x0ACA1, 0x0ACA2, 0x0ACA3, 0x0ACA4, 0x0ACA5, 0x0ACA6, 0x0ACA7, 0x0ACA8, 0x0ACA9, 0x0ACAA, 0x0ACAB, 0x0ACAC, 0x0ACAD, 0x0ACAE, 0x0ACAF, 0x0ACB0, 0x0ACB1, 0x0ACB2, 0x0ACB3, 0x0ACB4, 0x0ACB5, 0x0ACB6, 0x0ACB7, 0x0ACB8, 0x0ACB9, 0x0ACBA, 0x0ACBB, 0x0ACBC, 0x0ACBD, 0x0ACBE, 0x0ACBF, 0x0ACC0, 0x0ACC1, 0x0ACC2, 0x0ACC3, 0x0ACC4, 0x0ACC5, 0x0ACC6, 0x0ACC7, 0x0ACC8, 0x0ACC9, 0x0ACCA, 0x0ACCB, 0x0ACCC, 0x0ACCD, 0x0ACCE, 0x0ACCF, 0x0ACD0, 0x0ACD1, 0x0ACD2, 0x0ACD3, 0x0ACD4, 0x0ACD5, 0x0ACD6, 0x0ACD7, 0x0ACD8, 0x0ACD9, 0x0ACDA, 0x0ACDB, 0x0ACDC, 0x0ACDD, 0x0ACDE, 0x0ACDF, 0x0ACE0, 0x0ACE1, 0x0ACE2, 0x0ACE3, 0x0ACE4, 0x0ACE5, 0x0ACE6, 0x0ACE7, 0x0ACE8, 0x0ACE9, 0x0ACEA, 0x0ACEB, 0x0ACEC, 0x0ACED, 0x0ACEE, 0x0ACEF, 0x0ACF0, 0x0ACF1, 0x0ACF2, 0x0ACF3, 0x0ACF4, 0x0ACF5, 0x0ACF6, 0x0ACF7, 0x0ACF8, 0x0ACF9, 0x0ACFA, 0x0ACFB, 0x0ACFC, 0x0ACFD, 0x0ACFE, 0x0ACFF, 0x0AD00, 0x0AD01, 0x0AD02, 0x0AD03, 0x0AD04, 0x0AD05, 0x0AD06, 0x0AD07, 0x0AD08, 0x0AD09, 0x0AD0A, 0x0AD0B, 0x0AD0C, 0x0AD0D, 0x0AD0E, 0x0AD0F, 0x0AD10, 0x0AD11, 0x0AD12, 0x0AD13, 0x0AD14, 0x0AD15, 0x0AD16, 0x0AD17, 0x0AD18, 0x0AD19, 0x0AD1A, 0x0AD1B, 0x0AD1C, 0x0AD1D, 0x0AD1E, 0x0AD1F, 0x0AD20, 0x0AD21, 0x0AD22, 0x0AD23, 0x0AD24, 0x0AD25, 0x0AD26, 0x0AD27, 0x0AD28, 0x0AD29, 0x0AD2A, 0x0AD2B, 0x0AD2C, 0x0AD2D, 0x0AD2E, 0x0AD2F, 0x0AD30, 0x0AD31, 0x0AD32, 0x0AD33, 0x0AD34, 0x0AD35, 0x0AD36, 0x0AD37, 0x0AD38, 0x0AD39, 0x0AD3A, 0x0AD3B, 0x0AD3C, 0x0AD3D, 0x0AD3E, 0x0AD3F, 0x0AD40, 0x0AD41, 0x0AD42, 0x0AD43, 0x0AD44, 0x0AD45, 0x0AD46, 0x0AD47, 0x0AD48, 0x0AD49, 0x0AD4A, 0x0AD4B, 0x0AD4C, 0x0AD4D, 0x0AD4E, 0x0AD4F, 0x0AD50, 0x0AD51, 0x0AD52, 0x0AD53, 0x0AD54, 0x0AD55, 0x0AD56, 0x0AD57, 0x0AD58, 0x0AD59, 0x0AD5A, 0x0AD5B, 0x0AD5C, 0x0AD5D, 0x0AD5E, 0x0AD5F, 0x0AD60, 0x0AD61, 0x0AD62, 0x0AD63, 0x0AD64, 0x0AD65, 0x0AD66, 0x0AD67, 0x0AD68, 0x0AD69, 0x0AD6A, 0x0AD6B, 0x0AD6C, 0x0AD6D, 0x0AD6E, 0x0AD6F, 0x0AD70, 0x0AD71, 0x0AD72, 0x0AD73, 0x0AD74, 0x0AD75, 0x0AD76, 0x0AD77, 0x0AD78, 0x0AD79, 0x0AD7A, 0x0AD7B, 0x0AD7C, 0x0AD7D, 0x0AD7E, 0x0AD7F, 0x0AD80, 0x0AD81, 0x0AD82, 0x0AD83, 0x0AD84, 0x0AD85, 0x0AD86, 0x0AD87, 0x0AD88, 0x0AD89, 0x0AD8A, 0x0AD8B, 0x0AD8C, 0x0AD8D, 0x0AD8E, 0x0AD8F, 0x0AD90, 0x0AD91, 0x0AD92, 0x0AD93, 0x0AD94, 0x0AD95, 0x0AD96, 0x0AD97, 0x0AD98, 0x0AD99, 0x0AD9A, 0x0AD9B, 0x0AD9C, 0x0AD9D, 0x0AD9E, 0x0AD9F, 0x0ADA0, 0x0ADA1, 0x0ADA2, 0x0ADA3, 0x0ADA4, 0x0ADA5, 0x0ADA6, 0x0ADA7, 0x0ADA8, 0x0ADA9, 0x0ADAA, 0x0ADAB, 0x0ADAC, 0x0ADAD, 0x0ADAE, 0x0ADAF, 0x0ADB0, 0x0ADB1, 0x0ADB2, 0x0ADB3, 0x0ADB4, 0x0ADB5, 0x0ADB6, 0x0ADB7, 0x0ADB8, 0x0ADB9, 0x0ADBA, 0x0ADBB, 0x0ADBC, 0x0ADBD, 0x0ADBE, 0x0ADBF, 0x0ADC0, 0x0ADC1, 0x0ADC2, 0x0ADC3, 0x0ADC4, 0x0ADC5, 0x0ADC6, 0x0ADC7, 0x0ADC8, 0x0ADC9, 0x0ADCA, 0x0ADCB, 0x0ADCC, 0x0ADCD, 0x0ADCE, 0x0ADCF, 0x0ADD0, 0x0ADD1, 0x0ADD2, 0x0ADD3, 0x0ADD4, 0x0ADD5, 0x0ADD6, 0x0ADD7, 0x0ADD8, 0x0ADD9, 0x0ADDA, 0x0ADDB, 0x0ADDC, 0x0ADDD, 0x0ADDE, 0x0ADDF, 0x0ADE0, 0x0ADE1, 0x0ADE2, 0x0ADE3, 0x0ADE4, 0x0ADE5, 0x0ADE6, 0x0ADE7, 0x0ADE8, 0x0ADE9, 0x0ADEA, 0x0ADEB, 0x0ADEC, 0x0ADED, 0x0ADEE, 0x0ADEF, 0x0ADF0, 0x0ADF1, 0x0ADF2, 0x0ADF3, 0x0ADF4, 0x0ADF5, 0x0ADF6, 0x0ADF7, 0x0ADF8, 0x0ADF9, 0x0ADFA, 0x0ADFB, 0x0ADFC, 0x0ADFD, 0x0ADFE, 0x0ADFF, 0x0AE00, 0x0AE01, 0x0AE02, 0x0AE03, 0x0AE04, 0x0AE05, 0x0AE06, 0x0AE07, 0x0AE08, 0x0AE09, 0x0AE0A, 0x0AE0B, 0x0AE0C, 0x0AE0D, 0x0AE0E, 0x0AE0F, 0x0AE10, 0x0AE11, 0x0AE12, 0x0AE13, 0x0AE14, 0x0AE15, 0x0AE16, 0x0AE17, 0x0AE18, 0x0AE19, 0x0AE1A, 0x0AE1B, 0x0AE1C, 0x0AE1D, 0x0AE1E, 0x0AE1F, 0x0AE20, 0x0AE21, 0x0AE22, 0x0AE23, 0x0AE24, 0x0AE25, 0x0AE26, 0x0AE27, 0x0AE28, 0x0AE29, 0x0AE2A, 0x0AE2B, 0x0AE2C, 0x0AE2D, 0x0AE2E, 0x0AE2F, 0x0AE30, 0x0AE31, 0x0AE32, 0x0AE33, 0x0AE34, 0x0AE35, 0x0AE36, 0x0AE37, 0x0AE38, 0x0AE39, 0x0AE3A, 0x0AE3B, 0x0AE3C, 0x0AE3D, 0x0AE3E, 0x0AE3F, 0x0AE40, 0x0AE41, 0x0AE42, 0x0AE43, 0x0AE44, 0x0AE45, 0x0AE46, 0x0AE47, 0x0AE48, 0x0AE49, 0x0AE4A, 0x0AE4B, 0x0AE4C, 0x0AE4D, 0x0AE4E, 0x0AE4F, 0x0AE50, 0x0AE51, 0x0AE52, 0x0AE53, 0x0AE54, 0x0AE55, 0x0AE56, 0x0AE57, 0x0AE58, 0x0AE59, 0x0AE5A, 0x0AE5B, 0x0AE5C, 0x0AE5D, 0x0AE5E, 0x0AE5F, 0x0AE60, 0x0AE61, 0x0AE62, 0x0AE63, 0x0AE64, 0x0AE65, 0x0AE66, 0x0AE67, 0x0AE68, 0x0AE69, 0x0AE6A, 0x0AE6B, 0x0AE6C, 0x0AE6D, 0x0AE6E, 0x0AE6F, 0x0AE70, 0x0AE71, 0x0AE72, 0x0AE73, 0x0AE74, 0x0AE75, 0x0AE76, 0x0AE77, 0x0AE78, 0x0AE79, 0x0AE7A, 0x0AE7B, 0x0AE7C, 0x0AE7D, 0x0AE7E, 0x0AE7F, 0x0AE80, 0x0AE81, 0x0AE82, 0x0AE83, 0x0AE84, 0x0AE85, 0x0AE86, 0x0AE87, 0x0AE88, 0x0AE89, 0x0AE8A, 0x0AE8B, 0x0AE8C, 0x0AE8D, 0x0AE8E, 0x0AE8F, 0x0AE90, 0x0AE91, 0x0AE92, 0x0AE93, 0x0AE94, 0x0AE95, 0x0AE96, 0x0AE97, 0x0AE98, 0x0AE99, 0x0AE9A, 0x0AE9B, 0x0AE9C, 0x0AE9D, 0x0AE9E, 0x0AE9F, 0x0AEA0, 0x0AEA1, 0x0AEA2, 0x0AEA3, 0x0AEA4, 0x0AEA5, 0x0AEA6, 0x0AEA7, 0x0AEA8, 0x0AEA9, 0x0AEAA, 0x0AEAB, 0x0AEAC, 0x0AEAD, 0x0AEAE, 0x0AEAF, 0x0AEB0, 0x0AEB1, 0x0AEB2, 0x0AEB3, 0x0AEB4, 0x0AEB5, 0x0AEB6, 0x0AEB7, 0x0AEB8, 0x0AEB9, 0x0AEBA, 0x0AEBB, 0x0AEBC, 0x0AEBD, 0x0AEBE, 0x0AEBF, 0x0AEC0, 0x0AEC1, 0x0AEC2, 0x0AEC3, 0x0AEC4, 0x0AEC5, 0x0AEC6, 0x0AEC7, 0x0AEC8, 0x0AEC9, 0x0AECA, 0x0AECB, 0x0AECC, 0x0AECD, 0x0AECE, 0x0AECF, 0x0AED0, 0x0AED1, 0x0AED2, 0x0AED3, 0x0AED4, 0x0AED5, 0x0AED6, 0x0AED7, 0x0AED8, 0x0AED9, 0x0AEDA, 0x0AEDB, 0x0AEDC, 0x0AEDD, 0x0AEDE, 0x0AEDF, 0x0AEE0, 0x0AEE1, 0x0AEE2, 0x0AEE3, 0x0AEE4, 0x0AEE5, 0x0AEE6, 0x0AEE7, 0x0AEE8, 0x0AEE9, 0x0AEEA, 0x0AEEB, 0x0AEEC, 0x0AEED, 0x0AEEE, 0x0AEEF, 0x0AEF0, 0x0AEF1, 0x0AEF2, 0x0AEF3, 0x0AEF4, 0x0AEF5, 0x0AEF6, 0x0AEF7, 0x0AEF8, 0x0AEF9, 0x0AEFA, 0x0AEFB, 0x0AEFC, 0x0AEFD, 0x0AEFE, 0x0AEFF, 0x0AF00, 0x0AF01, 0x0AF02, 0x0AF03, 0x0AF04, 0x0AF05, 0x0AF06, 0x0AF07, 0x0AF08, 0x0AF09, 0x0AF0A, 0x0AF0B, 0x0AF0C, 0x0AF0D, 0x0AF0E, 0x0AF0F, 0x0AF10, 0x0AF11, 0x0AF12, 0x0AF13, 0x0AF14, 0x0AF15, 0x0AF16, 0x0AF17, 0x0AF18, 0x0AF19, 0x0AF1A, 0x0AF1B, 0x0AF1C, 0x0AF1D, 0x0AF1E, 0x0AF1F, 0x0AF20, 0x0AF21, 0x0AF22, 0x0AF23, 0x0AF24, 0x0AF25, 0x0AF26, 0x0AF27, 0x0AF28, 0x0AF29, 0x0AF2A, 0x0AF2B, 0x0AF2C, 0x0AF2D, 0x0AF2E, 0x0AF2F, 0x0AF30, 0x0AF31, 0x0AF32, 0x0AF33, 0x0AF34, 0x0AF35, 0x0AF36, 0x0AF37, 0x0AF38, 0x0AF39, 0x0AF3A, 0x0AF3B, 0x0AF3C, 0x0AF3D, 0x0AF3E, 0x0AF3F, 0x0AF40, 0x0AF41, 0x0AF42, 0x0AF43, 0x0AF44, 0x0AF45, 0x0AF46, 0x0AF47, 0x0AF48, 0x0AF49, 0x0AF4A, 0x0AF4B, 0x0AF4C, 0x0AF4D, 0x0AF4E, 0x0AF4F, 0x0AF50, 0x0AF51, 0x0AF52, 0x0AF53, 0x0AF54, 0x0AF55, 0x0AF56, 0x0AF57, 0x0AF58, 0x0AF59, 0x0AF5A, 0x0AF5B, 0x0AF5C, 0x0AF5D, 0x0AF5E, 0x0AF5F, 0x0AF60, 0x0AF61, 0x0AF62, 0x0AF63, 0x0AF64, 0x0AF65, 0x0AF66, 0x0AF67, 0x0AF68, 0x0AF69, 0x0AF6A, 0x0AF6B, 0x0AF6C, 0x0AF6D, 0x0AF6E, 0x0AF6F, 0x0AF70, 0x0AF71, 0x0AF72, 0x0AF73, 0x0AF74, 0x0AF75, 0x0AF76, 0x0AF77, 0x0AF78, 0x0AF79, 0x0AF7A, 0x0AF7B, 0x0AF7C, 0x0AF7D, 0x0AF7E, 0x0AF7F, 0x0AF80, 0x0AF81, 0x0AF82, 0x0AF83, 0x0AF84, 0x0AF85, 0x0AF86, 0x0AF87, 0x0AF88, 0x0AF89, 0x0AF8A, 0x0AF8B, 0x0AF8C, 0x0AF8D, 0x0AF8E, 0x0AF8F, 0x0AF90, 0x0AF91, 0x0AF92, 0x0AF93, 0x0AF94, 0x0AF95, 0x0AF96, 0x0AF97, 0x0AF98, 0x0AF99, 0x0AF9A, 0x0AF9B, 0x0AF9C, 0x0AF9D, 0x0AF9E, 0x0AF9F, 0x0AFA0, 0x0AFA1, 0x0AFA2, 0x0AFA3, 0x0AFA4, 0x0AFA5, 0x0AFA6, 0x0AFA7, 0x0AFA8, 0x0AFA9, 0x0AFAA, 0x0AFAB, 0x0AFAC, 0x0AFAD, 0x0AFAE, 0x0AFAF, 0x0AFB0, 0x0AFB1, 0x0AFB2, 0x0AFB3, 0x0AFB4, 0x0AFB5, 0x0AFB6, 0x0AFB7, 0x0AFB8, 0x0AFB9, 0x0AFBA, 0x0AFBB, 0x0AFBC, 0x0AFBD, 0x0AFBE, 0x0AFBF, 0x0AFC0, 0x0AFC1, 0x0AFC2, 0x0AFC3, 0x0AFC4, 0x0AFC5, 0x0AFC6, 0x0AFC7, 0x0AFC8, 0x0AFC9, 0x0AFCA, 0x0AFCB, 0x0AFCC, 0x0AFCD, 0x0AFCE, 0x0AFCF, 0x0AFD0, 0x0AFD1, 0x0AFD2, 0x0AFD3, 0x0AFD4, 0x0AFD5, 0x0AFD6, 0x0AFD7, 0x0AFD8, 0x0AFD9, 0x0AFDA, 0x0AFDB, 0x0AFDC, 0x0AFDD, 0x0AFDE, 0x0AFDF, 0x0AFE0, 0x0AFE1, 0x0AFE2, 0x0AFE3, 0x0AFE4, 0x0AFE5, 0x0AFE6, 0x0AFE7, 0x0AFE8, 0x0AFE9, 0x0AFEA, 0x0AFEB, 0x0AFEC, 0x0AFED, 0x0AFEE, 0x0AFEF, 0x0AFF0, 0x0AFF1, 0x0AFF2, 0x0AFF3, 0x0AFF4, 0x0AFF5, 0x0AFF6, 0x0AFF7, 0x0AFF8, 0x0AFF9, 0x0AFFA, 0x0AFFB, 0x0AFFC, 0x0AFFD, 0x0AFFE, 0x0AFFF, 0x0B000, 0x0B001, 0x0B002, 0x0B003, 0x0B004, 0x0B005, 0x0B006, 0x0B007, 0x0B008, 0x0B009, 0x0B00A, 0x0B00B, 0x0B00C, 0x0B00D, 0x0B00E, 0x0B00F, 0x0B010, 0x0B011, 0x0B012, 0x0B013, 0x0B014, 0x0B015, 0x0B016, 0x0B017, 0x0B018, 0x0B019, 0x0B01A, 0x0B01B, 0x0B01C, 0x0B01D, 0x0B01E, 0x0B01F, 0x0B020, 0x0B021, 0x0B022, 0x0B023, 0x0B024, 0x0B025, 0x0B026, 0x0B027, 0x0B028, 0x0B029, 0x0B02A, 0x0B02B, 0x0B02C, 0x0B02D, 0x0B02E, 0x0B02F, 0x0B030, 0x0B031, 0x0B032, 0x0B033, 0x0B034, 0x0B035, 0x0B036, 0x0B037, 0x0B038, 0x0B039, 0x0B03A, 0x0B03B, 0x0B03C, 0x0B03D, 0x0B03E, 0x0B03F, 0x0B040, 0x0B041, 0x0B042, 0x0B043, 0x0B044, 0x0B045, 0x0B046, 0x0B047, 0x0B048, 0x0B049, 0x0B04A, 0x0B04B, 0x0B04C, 0x0B04D, 0x0B04E, 0x0B04F, 0x0B050, 0x0B051, 0x0B052, 0x0B053, 0x0B054, 0x0B055, 0x0B056, 0x0B057, 0x0B058, 0x0B059, 0x0B05A, 0x0B05B, 0x0B05C, 0x0B05D, 0x0B05E, 0x0B05F, 0x0B060, 0x0B061, 0x0B062, 0x0B063, 0x0B064, 0x0B065, 0x0B066, 0x0B067, 0x0B068, 0x0B069, 0x0B06A, 0x0B06B, 0x0B06C, 0x0B06D, 0x0B06E, 0x0B06F, 0x0B070, 0x0B071, 0x0B072, 0x0B073, 0x0B074, 0x0B075, 0x0B076, 0x0B077, 0x0B078, 0x0B079, 0x0B07A, 0x0B07B, 0x0B07C, 0x0B07D, 0x0B07E, 0x0B07F, 0x0B080, 0x0B081, 0x0B082, 0x0B083, 0x0B084, 0x0B085, 0x0B086, 0x0B087, 0x0B088, 0x0B089, 0x0B08A, 0x0B08B, 0x0B08C, 0x0B08D, 0x0B08E, 0x0B08F, 0x0B090, 0x0B091, 0x0B092, 0x0B093, 0x0B094, 0x0B095, 0x0B096, 0x0B097, 0x0B098, 0x0B099, 0x0B09A, 0x0B09B, 0x0B09C, 0x0B09D, 0x0B09E, 0x0B09F, 0x0B0A0, 0x0B0A1, 0x0B0A2, 0x0B0A3, 0x0B0A4, 0x0B0A5, 0x0B0A6, 0x0B0A7, 0x0B0A8, 0x0B0A9, 0x0B0AA, 0x0B0AB, 0x0B0AC, 0x0B0AD, 0x0B0AE, 0x0B0AF, 0x0B0B0, 0x0B0B1, 0x0B0B2, 0x0B0B3, 0x0B0B4, 0x0B0B5, 0x0B0B6, 0x0B0B7, 0x0B0B8, 0x0B0B9, 0x0B0BA, 0x0B0BB, 0x0B0BC, 0x0B0BD, 0x0B0BE, 0x0B0BF, 0x0B0C0, 0x0B0C1, 0x0B0C2, 0x0B0C3, 0x0B0C4, 0x0B0C5, 0x0B0C6, 0x0B0C7, 0x0B0C8, 0x0B0C9, 0x0B0CA, 0x0B0CB, 0x0B0CC, 0x0B0CD, 0x0B0CE, 0x0B0CF, 0x0B0D0, 0x0B0D1, 0x0B0D2, 0x0B0D3, 0x0B0D4, 0x0B0D5, 0x0B0D6, 0x0B0D7, 0x0B0D8, 0x0B0D9, 0x0B0DA, 0x0B0DB, 0x0B0DC, 0x0B0DD, 0x0B0DE, 0x0B0DF, 0x0B0E0, 0x0B0E1, 0x0B0E2, 0x0B0E3, 0x0B0E4, 0x0B0E5, 0x0B0E6, 0x0B0E7, 0x0B0E8, 0x0B0E9, 0x0B0EA, 0x0B0EB, 0x0B0EC, 0x0B0ED, 0x0B0EE, 0x0B0EF, 0x0B0F0, 0x0B0F1, 0x0B0F2, 0x0B0F3, 0x0B0F4, 0x0B0F5, 0x0B0F6, 0x0B0F7, 0x0B0F8, 0x0B0F9, 0x0B0FA, 0x0B0FB, 0x0B0FC, 0x0B0FD, 0x0B0FE, 0x0B0FF, 0x0B100, 0x0B101, 0x0B102, 0x0B103, 0x0B104, 0x0B105, 0x0B106, 0x0B107, 0x0B108, 0x0B109, 0x0B10A, 0x0B10B, 0x0B10C, 0x0B10D, 0x0B10E, 0x0B10F, 0x0B110, 0x0B111, 0x0B112, 0x0B113, 0x0B114, 0x0B115, 0x0B116, 0x0B117, 0x0B118, 0x0B119, 0x0B11A, 0x0B11B, 0x0B11C, 0x0B11D, 0x0B11E, 0x0B11F, 0x0B120, 0x0B121, 0x0B122, 0x0B123, 0x0B124, 0x0B125, 0x0B126, 0x0B127, 0x0B128, 0x0B129, 0x0B12A, 0x0B12B, 0x0B12C, 0x0B12D, 0x0B12E, 0x0B12F, 0x0B130, 0x0B131, 0x0B132, 0x0B133, 0x0B134, 0x0B135, 0x0B136, 0x0B137, 0x0B138, 0x0B139, 0x0B13A, 0x0B13B, 0x0B13C, 0x0B13D, 0x0B13E, 0x0B13F, 0x0B140, 0x0B141, 0x0B142, 0x0B143, 0x0B144, 0x0B145, 0x0B146, 0x0B147, 0x0B148, 0x0B149, 0x0B14A, 0x0B14B, 0x0B14C, 0x0B14D, 0x0B14E, 0x0B14F, 0x0B150, 0x0B151, 0x0B152, 0x0B153, 0x0B154, 0x0B155, 0x0B156, 0x0B157, 0x0B158, 0x0B159, 0x0B15A, 0x0B15B, 0x0B15C, 0x0B15D, 0x0B15E, 0x0B15F, 0x0B160, 0x0B161, 0x0B162, 0x0B163, 0x0B164, 0x0B165, 0x0B166, 0x0B167, 0x0B168, 0x0B169, 0x0B16A, 0x0B16B, 0x0B16C, 0x0B16D, 0x0B16E, 0x0B16F, 0x0B170, 0x0B171, 0x0B172, 0x0B173, 0x0B174, 0x0B175, 0x0B176, 0x0B177, 0x0B178, 0x0B179, 0x0B17A, 0x0B17B, 0x0B17C, 0x0B17D, 0x0B17E, 0x0B17F, 0x0B180, 0x0B181, 0x0B182, 0x0B183, 0x0B184, 0x0B185, 0x0B186, 0x0B187, 0x0B188, 0x0B189, 0x0B18A, 0x0B18B, 0x0B18C, 0x0B18D, 0x0B18E, 0x0B18F, 0x0B190, 0x0B191, 0x0B192, 0x0B193, 0x0B194, 0x0B195, 0x0B196, 0x0B197, 0x0B198, 0x0B199, 0x0B19A, 0x0B19B, 0x0B19C, 0x0B19D, 0x0B19E, 0x0B19F, 0x0B1A0, 0x0B1A1, 0x0B1A2, 0x0B1A3, 0x0B1A4, 0x0B1A5, 0x0B1A6, 0x0B1A7, 0x0B1A8, 0x0B1A9, 0x0B1AA, 0x0B1AB, 0x0B1AC, 0x0B1AD, 0x0B1AE, 0x0B1AF, 0x0B1B0, 0x0B1B1, 0x0B1B2, 0x0B1B3, 0x0B1B4, 0x0B1B5, 0x0B1B6, 0x0B1B7, 0x0B1B8, 0x0B1B9, 0x0B1BA, 0x0B1BB, 0x0B1BC, 0x0B1BD, 0x0B1BE, 0x0B1BF, 0x0B1C0, 0x0B1C1, 0x0B1C2, 0x0B1C3, 0x0B1C4, 0x0B1C5, 0x0B1C6, 0x0B1C7, 0x0B1C8, 0x0B1C9, 0x0B1CA, 0x0B1CB, 0x0B1CC, 0x0B1CD, 0x0B1CE, 0x0B1CF, 0x0B1D0, 0x0B1D1, 0x0B1D2, 0x0B1D3, 0x0B1D4, 0x0B1D5, 0x0B1D6, 0x0B1D7, 0x0B1D8, 0x0B1D9, 0x0B1DA, 0x0B1DB, 0x0B1DC, 0x0B1DD, 0x0B1DE, 0x0B1DF, 0x0B1E0, 0x0B1E1, 0x0B1E2, 0x0B1E3, 0x0B1E4, 0x0B1E5, 0x0B1E6, 0x0B1E7, 0x0B1E8, 0x0B1E9, 0x0B1EA, 0x0B1EB, 0x0B1EC, 0x0B1ED, 0x0B1EE, 0x0B1EF, 0x0B1F0, 0x0B1F1, 0x0B1F2, 0x0B1F3, 0x0B1F4, 0x0B1F5, 0x0B1F6, 0x0B1F7, 0x0B1F8, 0x0B1F9, 0x0B1FA, 0x0B1FB, 0x0B1FC, 0x0B1FD, 0x0B1FE, 0x0B1FF, 0x0B200, 0x0B201, 0x0B202, 0x0B203, 0x0B204, 0x0B205, 0x0B206, 0x0B207, 0x0B208, 0x0B209, 0x0B20A, 0x0B20B, 0x0B20C, 0x0B20D, 0x0B20E, 0x0B20F, 0x0B210, 0x0B211, 0x0B212, 0x0B213, 0x0B214, 0x0B215, 0x0B216, 0x0B217, 0x0B218, 0x0B219, 0x0B21A, 0x0B21B, 0x0B21C, 0x0B21D, 0x0B21E, 0x0B21F, 0x0B220, 0x0B221, 0x0B222, 0x0B223, 0x0B224, 0x0B225, 0x0B226, 0x0B227, 0x0B228, 0x0B229, 0x0B22A, 0x0B22B, 0x0B22C, 0x0B22D, 0x0B22E, 0x0B22F, 0x0B230, 0x0B231, 0x0B232, 0x0B233, 0x0B234, 0x0B235, 0x0B236, 0x0B237, 0x0B238, 0x0B239, 0x0B23A, 0x0B23B, 0x0B23C, 0x0B23D, 0x0B23E, 0x0B23F, 0x0B240, 0x0B241, 0x0B242, 0x0B243, 0x0B244, 0x0B245, 0x0B246, 0x0B247, 0x0B248, 0x0B249, 0x0B24A, 0x0B24B, 0x0B24C, 0x0B24D, 0x0B24E, 0x0B24F, 0x0B250, 0x0B251, 0x0B252, 0x0B253, 0x0B254, 0x0B255, 0x0B256, 0x0B257, 0x0B258, 0x0B259, 0x0B25A, 0x0B25B, 0x0B25C, 0x0B25D, 0x0B25E, 0x0B25F, 0x0B260, 0x0B261, 0x0B262, 0x0B263, 0x0B264, 0x0B265, 0x0B266, 0x0B267, 0x0B268, 0x0B269, 0x0B26A, 0x0B26B, 0x0B26C, 0x0B26D, 0x0B26E, 0x0B26F, 0x0B270, 0x0B271, 0x0B272, 0x0B273, 0x0B274, 0x0B275, 0x0B276, 0x0B277, 0x0B278, 0x0B279, 0x0B27A, 0x0B27B, 0x0B27C, 0x0B27D, 0x0B27E, 0x0B27F, 0x0B280, 0x0B281, 0x0B282, 0x0B283, 0x0B284, 0x0B285, 0x0B286, 0x0B287, 0x0B288, 0x0B289, 0x0B28A, 0x0B28B, 0x0B28C, 0x0B28D, 0x0B28E, 0x0B28F, 0x0B290, 0x0B291, 0x0B292, 0x0B293, 0x0B294, 0x0B295, 0x0B296, 0x0B297, 0x0B298, 0x0B299, 0x0B29A, 0x0B29B, 0x0B29C, 0x0B29D, 0x0B29E, 0x0B29F, 0x0B2A0, 0x0B2A1, 0x0B2A2, 0x0B2A3, 0x0B2A4, 0x0B2A5, 0x0B2A6, 0x0B2A7, 0x0B2A8, 0x0B2A9, 0x0B2AA, 0x0B2AB, 0x0B2AC, 0x0B2AD, 0x0B2AE, 0x0B2AF, 0x0B2B0, 0x0B2B1, 0x0B2B2, 0x0B2B3, 0x0B2B4, 0x0B2B5, 0x0B2B6, 0x0B2B7, 0x0B2B8, 0x0B2B9, 0x0B2BA, 0x0B2BB, 0x0B2BC, 0x0B2BD, 0x0B2BE, 0x0B2BF, 0x0B2C0, 0x0B2C1, 0x0B2C2, 0x0B2C3, 0x0B2C4, 0x0B2C5, 0x0B2C6, 0x0B2C7, 0x0B2C8, 0x0B2C9, 0x0B2CA, 0x0B2CB, 0x0B2CC, 0x0B2CD, 0x0B2CE, 0x0B2CF, 0x0B2D0, 0x0B2D1, 0x0B2D2, 0x0B2D3, 0x0B2D4, 0x0B2D5, 0x0B2D6, 0x0B2D7, 0x0B2D8, 0x0B2D9, 0x0B2DA, 0x0B2DB, 0x0B2DC, 0x0B2DD, 0x0B2DE, 0x0B2DF, 0x0B2E0, 0x0B2E1, 0x0B2E2, 0x0B2E3, 0x0B2E4, 0x0B2E5, 0x0B2E6, 0x0B2E7, 0x0B2E8, 0x0B2E9, 0x0B2EA, 0x0B2EB, 0x0B2EC, 0x0B2ED, 0x0B2EE, 0x0B2EF, 0x0B2F0, 0x0B2F1, 0x0B2F2, 0x0B2F3, 0x0B2F4, 0x0B2F5, 0x0B2F6, 0x0B2F7, 0x0B2F8, 0x0B2F9, 0x0B2FA, 0x0B2FB, 0x0B2FC, 0x0B2FD, 0x0B2FE, 0x0B2FF, 0x0B300, 0x0B301, 0x0B302, 0x0B303, 0x0B304, 0x0B305, 0x0B306, 0x0B307, 0x0B308, 0x0B309, 0x0B30A, 0x0B30B, 0x0B30C, 0x0B30D, 0x0B30E, 0x0B30F, 0x0B310, 0x0B311, 0x0B312, 0x0B313, 0x0B314, 0x0B315, 0x0B316, 0x0B317, 0x0B318, 0x0B319, 0x0B31A, 0x0B31B, 0x0B31C, 0x0B31D, 0x0B31E, 0x0B31F, 0x0B320, 0x0B321, 0x0B322, 0x0B323, 0x0B324, 0x0B325, 0x0B326, 0x0B327, 0x0B328, 0x0B329, 0x0B32A, 0x0B32B, 0x0B32C, 0x0B32D, 0x0B32E, 0x0B32F, 0x0B330, 0x0B331, 0x0B332, 0x0B333, 0x0B334, 0x0B335, 0x0B336, 0x0B337, 0x0B338, 0x0B339, 0x0B33A, 0x0B33B, 0x0B33C, 0x0B33D, 0x0B33E, 0x0B33F, 0x0B340, 0x0B341, 0x0B342, 0x0B343, 0x0B344, 0x0B345, 0x0B346, 0x0B347, 0x0B348, 0x0B349, 0x0B34A, 0x0B34B, 0x0B34C, 0x0B34D, 0x0B34E, 0x0B34F, 0x0B350, 0x0B351, 0x0B352, 0x0B353, 0x0B354, 0x0B355, 0x0B356, 0x0B357, 0x0B358, 0x0B359, 0x0B35A, 0x0B35B, 0x0B35C, 0x0B35D, 0x0B35E, 0x0B35F, 0x0B360, 0x0B361, 0x0B362, 0x0B363, 0x0B364, 0x0B365, 0x0B366, 0x0B367, 0x0B368, 0x0B369, 0x0B36A, 0x0B36B, 0x0B36C, 0x0B36D, 0x0B36E, 0x0B36F, 0x0B370, 0x0B371, 0x0B372, 0x0B373, 0x0B374, 0x0B375, 0x0B376, 0x0B377, 0x0B378, 0x0B379, 0x0B37A, 0x0B37B, 0x0B37C, 0x0B37D, 0x0B37E, 0x0B37F, 0x0B380, 0x0B381, 0x0B382, 0x0B383, 0x0B384, 0x0B385, 0x0B386, 0x0B387, 0x0B388, 0x0B389, 0x0B38A, 0x0B38B, 0x0B38C, 0x0B38D, 0x0B38E, 0x0B38F, 0x0B390, 0x0B391, 0x0B392, 0x0B393, 0x0B394, 0x0B395, 0x0B396, 0x0B397, 0x0B398, 0x0B399, 0x0B39A, 0x0B39B, 0x0B39C, 0x0B39D, 0x0B39E, 0x0B39F, 0x0B3A0, 0x0B3A1, 0x0B3A2, 0x0B3A3, 0x0B3A4, 0x0B3A5, 0x0B3A6, 0x0B3A7, 0x0B3A8, 0x0B3A9, 0x0B3AA, 0x0B3AB, 0x0B3AC, 0x0B3AD, 0x0B3AE, 0x0B3AF, 0x0B3B0, 0x0B3B1, 0x0B3B2, 0x0B3B3, 0x0B3B4, 0x0B3B5, 0x0B3B6, 0x0B3B7, 0x0B3B8, 0x0B3B9, 0x0B3BA, 0x0B3BB, 0x0B3BC, 0x0B3BD, 0x0B3BE, 0x0B3BF, 0x0B3C0, 0x0B3C1, 0x0B3C2, 0x0B3C3, 0x0B3C4, 0x0B3C5, 0x0B3C6, 0x0B3C7, 0x0B3C8, 0x0B3C9, 0x0B3CA, 0x0B3CB, 0x0B3CC, 0x0B3CD, 0x0B3CE, 0x0B3CF, 0x0B3D0, 0x0B3D1, 0x0B3D2, 0x0B3D3, 0x0B3D4, 0x0B3D5, 0x0B3D6, 0x0B3D7, 0x0B3D8, 0x0B3D9, 0x0B3DA, 0x0B3DB, 0x0B3DC, 0x0B3DD, 0x0B3DE, 0x0B3DF, 0x0B3E0, 0x0B3E1, 0x0B3E2, 0x0B3E3, 0x0B3E4, 0x0B3E5, 0x0B3E6, 0x0B3E7, 0x0B3E8, 0x0B3E9, 0x0B3EA, 0x0B3EB, 0x0B3EC, 0x0B3ED, 0x0B3EE, 0x0B3EF, 0x0B3F0, 0x0B3F1, 0x0B3F2, 0x0B3F3, 0x0B3F4, 0x0B3F5, 0x0B3F6, 0x0B3F7, 0x0B3F8, 0x0B3F9, 0x0B3FA, 0x0B3FB, 0x0B3FC, 0x0B3FD, 0x0B3FE, 0x0B3FF, 0x0B400, 0x0B401, 0x0B402, 0x0B403, 0x0B404, 0x0B405, 0x0B406, 0x0B407, 0x0B408, 0x0B409, 0x0B40A, 0x0B40B, 0x0B40C, 0x0B40D, 0x0B40E, 0x0B40F, 0x0B410, 0x0B411, 0x0B412, 0x0B413, 0x0B414, 0x0B415, 0x0B416, 0x0B417, 0x0B418, 0x0B419, 0x0B41A, 0x0B41B, 0x0B41C, 0x0B41D, 0x0B41E, 0x0B41F, 0x0B420, 0x0B421, 0x0B422, 0x0B423, 0x0B424, 0x0B425, 0x0B426, 0x0B427, 0x0B428, 0x0B429, 0x0B42A, 0x0B42B, 0x0B42C, 0x0B42D, 0x0B42E, 0x0B42F, 0x0B430, 0x0B431, 0x0B432, 0x0B433, 0x0B434, 0x0B435, 0x0B436, 0x0B437, 0x0B438, 0x0B439, 0x0B43A, 0x0B43B, 0x0B43C, 0x0B43D, 0x0B43E, 0x0B43F, 0x0B440, 0x0B441, 0x0B442, 0x0B443, 0x0B444, 0x0B445, 0x0B446, 0x0B447, 0x0B448, 0x0B449, 0x0B44A, 0x0B44B, 0x0B44C, 0x0B44D, 0x0B44E, 0x0B44F, 0x0B450, 0x0B451, 0x0B452, 0x0B453, 0x0B454, 0x0B455, 0x0B456, 0x0B457, 0x0B458, 0x0B459, 0x0B45A, 0x0B45B, 0x0B45C, 0x0B45D, 0x0B45E, 0x0B45F, 0x0B460, 0x0B461, 0x0B462, 0x0B463, 0x0B464, 0x0B465, 0x0B466, 0x0B467, 0x0B468, 0x0B469, 0x0B46A, 0x0B46B, 0x0B46C, 0x0B46D, 0x0B46E, 0x0B46F, 0x0B470, 0x0B471, 0x0B472, 0x0B473, 0x0B474, 0x0B475, 0x0B476, 0x0B477, 0x0B478, 0x0B479, 0x0B47A, 0x0B47B, 0x0B47C, 0x0B47D, 0x0B47E, 0x0B47F, 0x0B480, 0x0B481, 0x0B482, 0x0B483, 0x0B484, 0x0B485, 0x0B486, 0x0B487, 0x0B488, 0x0B489, 0x0B48A, 0x0B48B, 0x0B48C, 0x0B48D, 0x0B48E, 0x0B48F, 0x0B490, 0x0B491, 0x0B492, 0x0B493, 0x0B494, 0x0B495, 0x0B496, 0x0B497, 0x0B498, 0x0B499, 0x0B49A, 0x0B49B, 0x0B49C, 0x0B49D, 0x0B49E, 0x0B49F, 0x0B4A0, 0x0B4A1, 0x0B4A2, 0x0B4A3, 0x0B4A4, 0x0B4A5, 0x0B4A6, 0x0B4A7, 0x0B4A8, 0x0B4A9, 0x0B4AA, 0x0B4AB, 0x0B4AC, 0x0B4AD, 0x0B4AE, 0x0B4AF, 0x0B4B0, 0x0B4B1, 0x0B4B2, 0x0B4B3, 0x0B4B4, 0x0B4B5, 0x0B4B6, 0x0B4B7, 0x0B4B8, 0x0B4B9, 0x0B4BA, 0x0B4BB, 0x0B4BC, 0x0B4BD, 0x0B4BE, 0x0B4BF, 0x0B4C0, 0x0B4C1, 0x0B4C2, 0x0B4C3, 0x0B4C4, 0x0B4C5, 0x0B4C6, 0x0B4C7, 0x0B4C8, 0x0B4C9, 0x0B4CA, 0x0B4CB, 0x0B4CC, 0x0B4CD, 0x0B4CE, 0x0B4CF, 0x0B4D0, 0x0B4D1, 0x0B4D2, 0x0B4D3, 0x0B4D4, 0x0B4D5, 0x0B4D6, 0x0B4D7, 0x0B4D8, 0x0B4D9, 0x0B4DA, 0x0B4DB, 0x0B4DC, 0x0B4DD, 0x0B4DE, 0x0B4DF, 0x0B4E0, 0x0B4E1, 0x0B4E2, 0x0B4E3, 0x0B4E4, 0x0B4E5, 0x0B4E6, 0x0B4E7, 0x0B4E8, 0x0B4E9, 0x0B4EA, 0x0B4EB, 0x0B4EC, 0x0B4ED, 0x0B4EE, 0x0B4EF, 0x0B4F0, 0x0B4F1, 0x0B4F2, 0x0B4F3, 0x0B4F4, 0x0B4F5, 0x0B4F6, 0x0B4F7, 0x0B4F8, 0x0B4F9, 0x0B4FA, 0x0B4FB, 0x0B4FC, 0x0B4FD, 0x0B4FE, 0x0B4FF, 0x0B500, 0x0B501, 0x0B502, 0x0B503, 0x0B504, 0x0B505, 0x0B506, 0x0B507, 0x0B508, 0x0B509, 0x0B50A, 0x0B50B, 0x0B50C, 0x0B50D, 0x0B50E, 0x0B50F, 0x0B510, 0x0B511, 0x0B512, 0x0B513, 0x0B514, 0x0B515, 0x0B516, 0x0B517, 0x0B518, 0x0B519, 0x0B51A, 0x0B51B, 0x0B51C, 0x0B51D, 0x0B51E, 0x0B51F, 0x0B520, 0x0B521, 0x0B522, 0x0B523, 0x0B524, 0x0B525, 0x0B526, 0x0B527, 0x0B528, 0x0B529, 0x0B52A, 0x0B52B, 0x0B52C, 0x0B52D, 0x0B52E, 0x0B52F, 0x0B530, 0x0B531, 0x0B532, 0x0B533, 0x0B534, 0x0B535, 0x0B536, 0x0B537, 0x0B538, 0x0B539, 0x0B53A, 0x0B53B, 0x0B53C, 0x0B53D, 0x0B53E, 0x0B53F, 0x0B540, 0x0B541, 0x0B542, 0x0B543, 0x0B544, 0x0B545, 0x0B546, 0x0B547, 0x0B548, 0x0B549, 0x0B54A, 0x0B54B, 0x0B54C, 0x0B54D, 0x0B54E, 0x0B54F, 0x0B550, 0x0B551, 0x0B552, 0x0B553, 0x0B554, 0x0B555, 0x0B556, 0x0B557, 0x0B558, 0x0B559, 0x0B55A, 0x0B55B, 0x0B55C, 0x0B55D, 0x0B55E, 0x0B55F, 0x0B560, 0x0B561, 0x0B562, 0x0B563, 0x0B564, 0x0B565, 0x0B566, 0x0B567, 0x0B568, 0x0B569, 0x0B56A, 0x0B56B, 0x0B56C, 0x0B56D, 0x0B56E, 0x0B56F, 0x0B570, 0x0B571, 0x0B572, 0x0B573, 0x0B574, 0x0B575, 0x0B576, 0x0B577, 0x0B578, 0x0B579, 0x0B57A, 0x0B57B, 0x0B57C, 0x0B57D, 0x0B57E, 0x0B57F, 0x0B580, 0x0B581, 0x0B582, 0x0B583, 0x0B584, 0x0B585, 0x0B586, 0x0B587, 0x0B588, 0x0B589, 0x0B58A, 0x0B58B, 0x0B58C, 0x0B58D, 0x0B58E, 0x0B58F, 0x0B590, 0x0B591, 0x0B592, 0x0B593, 0x0B594, 0x0B595, 0x0B596, 0x0B597, 0x0B598, 0x0B599, 0x0B59A, 0x0B59B, 0x0B59C, 0x0B59D, 0x0B59E, 0x0B59F, 0x0B5A0, 0x0B5A1, 0x0B5A2, 0x0B5A3, 0x0B5A4, 0x0B5A5, 0x0B5A6, 0x0B5A7, 0x0B5A8, 0x0B5A9, 0x0B5AA, 0x0B5AB, 0x0B5AC, 0x0B5AD, 0x0B5AE, 0x0B5AF, 0x0B5B0, 0x0B5B1, 0x0B5B2, 0x0B5B3, 0x0B5B4, 0x0B5B5, 0x0B5B6, 0x0B5B7, 0x0B5B8, 0x0B5B9, 0x0B5BA, 0x0B5BB, 0x0B5BC, 0x0B5BD, 0x0B5BE, 0x0B5BF, 0x0B5C0, 0x0B5C1, 0x0B5C2, 0x0B5C3, 0x0B5C4, 0x0B5C5, 0x0B5C6, 0x0B5C7, 0x0B5C8, 0x0B5C9, 0x0B5CA, 0x0B5CB, 0x0B5CC, 0x0B5CD, 0x0B5CE, 0x0B5CF, 0x0B5D0, 0x0B5D1, 0x0B5D2, 0x0B5D3, 0x0B5D4, 0x0B5D5, 0x0B5D6, 0x0B5D7, 0x0B5D8, 0x0B5D9, 0x0B5DA, 0x0B5DB, 0x0B5DC, 0x0B5DD, 0x0B5DE, 0x0B5DF, 0x0B5E0, 0x0B5E1, 0x0B5E2, 0x0B5E3, 0x0B5E4, 0x0B5E5, 0x0B5E6, 0x0B5E7, 0x0B5E8, 0x0B5E9, 0x0B5EA, 0x0B5EB, 0x0B5EC, 0x0B5ED, 0x0B5EE, 0x0B5EF, 0x0B5F0, 0x0B5F1, 0x0B5F2, 0x0B5F3, 0x0B5F4, 0x0B5F5, 0x0B5F6, 0x0B5F7, 0x0B5F8, 0x0B5F9, 0x0B5FA, 0x0B5FB, 0x0B5FC, 0x0B5FD, 0x0B5FE, 0x0B5FF, 0x0B600, 0x0B601, 0x0B602, 0x0B603, 0x0B604, 0x0B605, 0x0B606, 0x0B607, 0x0B608, 0x0B609, 0x0B60A, 0x0B60B, 0x0B60C, 0x0B60D, 0x0B60E, 0x0B60F, 0x0B610, 0x0B611, 0x0B612, 0x0B613, 0x0B614, 0x0B615, 0x0B616, 0x0B617, 0x0B618, 0x0B619, 0x0B61A, 0x0B61B, 0x0B61C, 0x0B61D, 0x0B61E, 0x0B61F, 0x0B620, 0x0B621, 0x0B622, 0x0B623, 0x0B624, 0x0B625, 0x0B626, 0x0B627, 0x0B628, 0x0B629, 0x0B62A, 0x0B62B, 0x0B62C, 0x0B62D, 0x0B62E, 0x0B62F, 0x0B630, 0x0B631, 0x0B632, 0x0B633, 0x0B634, 0x0B635, 0x0B636, 0x0B637, 0x0B638, 0x0B639, 0x0B63A, 0x0B63B, 0x0B63C, 0x0B63D, 0x0B63E, 0x0B63F, 0x0B640, 0x0B641, 0x0B642, 0x0B643, 0x0B644, 0x0B645, 0x0B646, 0x0B647, 0x0B648, 0x0B649, 0x0B64A, 0x0B64B, 0x0B64C, 0x0B64D, 0x0B64E, 0x0B64F, 0x0B650, 0x0B651, 0x0B652, 0x0B653, 0x0B654, 0x0B655, 0x0B656, 0x0B657, 0x0B658, 0x0B659, 0x0B65A, 0x0B65B, 0x0B65C, 0x0B65D, 0x0B65E, 0x0B65F, 0x0B660, 0x0B661, 0x0B662, 0x0B663, 0x0B664, 0x0B665, 0x0B666, 0x0B667, 0x0B668, 0x0B669, 0x0B66A, 0x0B66B, 0x0B66C, 0x0B66D, 0x0B66E, 0x0B66F, 0x0B670, 0x0B671, 0x0B672, 0x0B673, 0x0B674, 0x0B675, 0x0B676, 0x0B677, 0x0B678, 0x0B679, 0x0B67A, 0x0B67B, 0x0B67C, 0x0B67D, 0x0B67E, 0x0B67F, 0x0B680, 0x0B681, 0x0B682, 0x0B683, 0x0B684, 0x0B685, 0x0B686, 0x0B687, 0x0B688, 0x0B689, 0x0B68A, 0x0B68B, 0x0B68C, 0x0B68D, 0x0B68E, 0x0B68F, 0x0B690, 0x0B691, 0x0B692, 0x0B693, 0x0B694, 0x0B695, 0x0B696, 0x0B697, 0x0B698, 0x0B699, 0x0B69A, 0x0B69B, 0x0B69C, 0x0B69D, 0x0B69E, 0x0B69F, 0x0B6A0, 0x0B6A1, 0x0B6A2, 0x0B6A3, 0x0B6A4, 0x0B6A5, 0x0B6A6, 0x0B6A7, 0x0B6A8, 0x0B6A9, 0x0B6AA, 0x0B6AB, 0x0B6AC, 0x0B6AD, 0x0B6AE, 0x0B6AF, 0x0B6B0, 0x0B6B1, 0x0B6B2, 0x0B6B3, 0x0B6B4, 0x0B6B5, 0x0B6B6, 0x0B6B7, 0x0B6B8, 0x0B6B9, 0x0B6BA, 0x0B6BB, 0x0B6BC, 0x0B6BD, 0x0B6BE, 0x0B6BF, 0x0B6C0, 0x0B6C1, 0x0B6C2, 0x0B6C3, 0x0B6C4, 0x0B6C5, 0x0B6C6, 0x0B6C7, 0x0B6C8, 0x0B6C9, 0x0B6CA, 0x0B6CB, 0x0B6CC, 0x0B6CD, 0x0B6CE, 0x0B6CF, 0x0B6D0, 0x0B6D1, 0x0B6D2, 0x0B6D3, 0x0B6D4, 0x0B6D5, 0x0B6D6, 0x0B6D7, 0x0B6D8, 0x0B6D9, 0x0B6DA, 0x0B6DB, 0x0B6DC, 0x0B6DD, 0x0B6DE, 0x0B6DF, 0x0B6E0, 0x0B6E1, 0x0B6E2, 0x0B6E3, 0x0B6E4, 0x0B6E5, 0x0B6E6, 0x0B6E7, 0x0B6E8, 0x0B6E9, 0x0B6EA, 0x0B6EB, 0x0B6EC, 0x0B6ED, 0x0B6EE, 0x0B6EF, 0x0B6F0, 0x0B6F1, 0x0B6F2, 0x0B6F3, 0x0B6F4, 0x0B6F5, 0x0B6F6, 0x0B6F7, 0x0B6F8, 0x0B6F9, 0x0B6FA, 0x0B6FB, 0x0B6FC, 0x0B6FD, 0x0B6FE, 0x0B6FF, 0x0B700, 0x0B701, 0x0B702, 0x0B703, 0x0B704, 0x0B705, 0x0B706, 0x0B707, 0x0B708, 0x0B709, 0x0B70A, 0x0B70B, 0x0B70C, 0x0B70D, 0x0B70E, 0x0B70F, 0x0B710, 0x0B711, 0x0B712, 0x0B713, 0x0B714, 0x0B715, 0x0B716, 0x0B717, 0x0B718, 0x0B719, 0x0B71A, 0x0B71B, 0x0B71C, 0x0B71D, 0x0B71E, 0x0B71F, 0x0B720, 0x0B721, 0x0B722, 0x0B723, 0x0B724, 0x0B725, 0x0B726, 0x0B727, 0x0B728, 0x0B729, 0x0B72A, 0x0B72B, 0x0B72C, 0x0B72D, 0x0B72E, 0x0B72F, 0x0B730, 0x0B731, 0x0B732, 0x0B733, 0x0B734, 0x0B735, 0x0B736, 0x0B737, 0x0B738, 0x0B739, 0x0B73A, 0x0B73B, 0x0B73C, 0x0B73D, 0x0B73E, 0x0B73F, 0x0B740, 0x0B741, 0x0B742, 0x0B743, 0x0B744, 0x0B745, 0x0B746, 0x0B747, 0x0B748, 0x0B749, 0x0B74A, 0x0B74B, 0x0B74C, 0x0B74D, 0x0B74E, 0x0B74F, 0x0B750, 0x0B751, 0x0B752, 0x0B753, 0x0B754, 0x0B755, 0x0B756, 0x0B757, 0x0B758, 0x0B759, 0x0B75A, 0x0B75B, 0x0B75C, 0x0B75D, 0x0B75E, 0x0B75F, 0x0B760, 0x0B761, 0x0B762, 0x0B763, 0x0B764, 0x0B765, 0x0B766, 0x0B767, 0x0B768, 0x0B769, 0x0B76A, 0x0B76B, 0x0B76C, 0x0B76D, 0x0B76E, 0x0B76F, 0x0B770, 0x0B771, 0x0B772, 0x0B773, 0x0B774, 0x0B775, 0x0B776, 0x0B777, 0x0B778, 0x0B779, 0x0B77A, 0x0B77B, 0x0B77C, 0x0B77D, 0x0B77E, 0x0B77F, 0x0B780, 0x0B781, 0x0B782, 0x0B783, 0x0B784, 0x0B785, 0x0B786, 0x0B787, 0x0B788, 0x0B789, 0x0B78A, 0x0B78B, 0x0B78C, 0x0B78D, 0x0B78E, 0x0B78F, 0x0B790, 0x0B791, 0x0B792, 0x0B793, 0x0B794, 0x0B795, 0x0B796, 0x0B797, 0x0B798, 0x0B799, 0x0B79A, 0x0B79B, 0x0B79C, 0x0B79D, 0x0B79E, 0x0B79F, 0x0B7A0, 0x0B7A1, 0x0B7A2, 0x0B7A3, 0x0B7A4, 0x0B7A5, 0x0B7A6, 0x0B7A7, 0x0B7A8, 0x0B7A9, 0x0B7AA, 0x0B7AB, 0x0B7AC, 0x0B7AD, 0x0B7AE, 0x0B7AF, 0x0B7B0, 0x0B7B1, 0x0B7B2, 0x0B7B3, 0x0B7B4, 0x0B7B5, 0x0B7B6, 0x0B7B7, 0x0B7B8, 0x0B7B9, 0x0B7BA, 0x0B7BB, 0x0B7BC, 0x0B7BD, 0x0B7BE, 0x0B7BF, 0x0B7C0, 0x0B7C1, 0x0B7C2, 0x0B7C3, 0x0B7C4, 0x0B7C5, 0x0B7C6, 0x0B7C7, 0x0B7C8, 0x0B7C9, 0x0B7CA, 0x0B7CB, 0x0B7CC, 0x0B7CD, 0x0B7CE, 0x0B7CF, 0x0B7D0, 0x0B7D1, 0x0B7D2, 0x0B7D3, 0x0B7D4, 0x0B7D5, 0x0B7D6, 0x0B7D7, 0x0B7D8, 0x0B7D9, 0x0B7DA, 0x0B7DB, 0x0B7DC, 0x0B7DD, 0x0B7DE, 0x0B7DF, 0x0B7E0, 0x0B7E1, 0x0B7E2, 0x0B7E3, 0x0B7E4, 0x0B7E5, 0x0B7E6, 0x0B7E7, 0x0B7E8, 0x0B7E9, 0x0B7EA, 0x0B7EB, 0x0B7EC, 0x0B7ED, 0x0B7EE, 0x0B7EF, 0x0B7F0, 0x0B7F1, 0x0B7F2, 0x0B7F3, 0x0B7F4, 0x0B7F5, 0x0B7F6, 0x0B7F7, 0x0B7F8, 0x0B7F9, 0x0B7FA, 0x0B7FB, 0x0B7FC, 0x0B7FD, 0x0B7FE, 0x0B7FF, 0x0B800, 0x0B801, 0x0B802, 0x0B803, 0x0B804, 0x0B805, 0x0B806, 0x0B807, 0x0B808, 0x0B809, 0x0B80A, 0x0B80B, 0x0B80C, 0x0B80D, 0x0B80E, 0x0B80F, 0x0B810, 0x0B811, 0x0B812, 0x0B813, 0x0B814, 0x0B815, 0x0B816, 0x0B817, 0x0B818, 0x0B819, 0x0B81A, 0x0B81B, 0x0B81C, 0x0B81D, 0x0B81E, 0x0B81F, 0x0B820, 0x0B821, 0x0B822, 0x0B823, 0x0B824, 0x0B825, 0x0B826, 0x0B827, 0x0B828, 0x0B829, 0x0B82A, 0x0B82B, 0x0B82C, 0x0B82D, 0x0B82E, 0x0B82F, 0x0B830, 0x0B831, 0x0B832, 0x0B833, 0x0B834, 0x0B835, 0x0B836, 0x0B837, 0x0B838, 0x0B839, 0x0B83A, 0x0B83B, 0x0B83C, 0x0B83D, 0x0B83E, 0x0B83F, 0x0B840, 0x0B841, 0x0B842, 0x0B843, 0x0B844, 0x0B845, 0x0B846, 0x0B847, 0x0B848, 0x0B849, 0x0B84A, 0x0B84B, 0x0B84C, 0x0B84D, 0x0B84E, 0x0B84F, 0x0B850, 0x0B851, 0x0B852, 0x0B853, 0x0B854, 0x0B855, 0x0B856, 0x0B857, 0x0B858, 0x0B859, 0x0B85A, 0x0B85B, 0x0B85C, 0x0B85D, 0x0B85E, 0x0B85F, 0x0B860, 0x0B861, 0x0B862, 0x0B863, 0x0B864, 0x0B865, 0x0B866, 0x0B867, 0x0B868, 0x0B869, 0x0B86A, 0x0B86B, 0x0B86C, 0x0B86D, 0x0B86E, 0x0B86F, 0x0B870, 0x0B871, 0x0B872, 0x0B873, 0x0B874, 0x0B875, 0x0B876, 0x0B877, 0x0B878, 0x0B879, 0x0B87A, 0x0B87B, 0x0B87C, 0x0B87D, 0x0B87E, 0x0B87F, 0x0B880, 0x0B881, 0x0B882, 0x0B883, 0x0B884, 0x0B885, 0x0B886, 0x0B887, 0x0B888, 0x0B889, 0x0B88A, 0x0B88B, 0x0B88C, 0x0B88D, 0x0B88E, 0x0B88F, 0x0B890, 0x0B891, 0x0B892, 0x0B893, 0x0B894, 0x0B895, 0x0B896, 0x0B897, 0x0B898, 0x0B899, 0x0B89A, 0x0B89B, 0x0B89C, 0x0B89D, 0x0B89E, 0x0B89F, 0x0B8A0, 0x0B8A1, 0x0B8A2, 0x0B8A3, 0x0B8A4, 0x0B8A5, 0x0B8A6, 0x0B8A7, 0x0B8A8, 0x0B8A9, 0x0B8AA, 0x0B8AB, 0x0B8AC, 0x0B8AD, 0x0B8AE, 0x0B8AF, 0x0B8B0, 0x0B8B1, 0x0B8B2, 0x0B8B3, 0x0B8B4, 0x0B8B5, 0x0B8B6, 0x0B8B7, 0x0B8B8, 0x0B8B9, 0x0B8BA, 0x0B8BB, 0x0B8BC, 0x0B8BD, 0x0B8BE, 0x0B8BF, 0x0B8C0, 0x0B8C1, 0x0B8C2, 0x0B8C3, 0x0B8C4, 0x0B8C5, 0x0B8C6, 0x0B8C7, 0x0B8C8, 0x0B8C9, 0x0B8CA, 0x0B8CB, 0x0B8CC, 0x0B8CD, 0x0B8CE, 0x0B8CF, 0x0B8D0, 0x0B8D1, 0x0B8D2, 0x0B8D3, 0x0B8D4, 0x0B8D5, 0x0B8D6, 0x0B8D7, 0x0B8D8, 0x0B8D9, 0x0B8DA, 0x0B8DB, 0x0B8DC, 0x0B8DD, 0x0B8DE, 0x0B8DF, 0x0B8E0, 0x0B8E1, 0x0B8E2, 0x0B8E3, 0x0B8E4, 0x0B8E5, 0x0B8E6, 0x0B8E7, 0x0B8E8, 0x0B8E9, 0x0B8EA, 0x0B8EB, 0x0B8EC, 0x0B8ED, 0x0B8EE, 0x0B8EF, 0x0B8F0, 0x0B8F1, 0x0B8F2, 0x0B8F3, 0x0B8F4, 0x0B8F5, 0x0B8F6, 0x0B8F7, 0x0B8F8, 0x0B8F9, 0x0B8FA, 0x0B8FB, 0x0B8FC, 0x0B8FD, 0x0B8FE, 0x0B8FF, 0x0B900, 0x0B901, 0x0B902, 0x0B903, 0x0B904, 0x0B905, 0x0B906, 0x0B907, 0x0B908, 0x0B909, 0x0B90A, 0x0B90B, 0x0B90C, 0x0B90D, 0x0B90E, 0x0B90F, 0x0B910, 0x0B911, 0x0B912, 0x0B913, 0x0B914, 0x0B915, 0x0B916, 0x0B917, 0x0B918, 0x0B919, 0x0B91A, 0x0B91B, 0x0B91C, 0x0B91D, 0x0B91E, 0x0B91F, 0x0B920, 0x0B921, 0x0B922, 0x0B923, 0x0B924, 0x0B925, 0x0B926, 0x0B927, 0x0B928, 0x0B929, 0x0B92A, 0x0B92B, 0x0B92C, 0x0B92D, 0x0B92E, 0x0B92F, 0x0B930, 0x0B931, 0x0B932, 0x0B933, 0x0B934, 0x0B935, 0x0B936, 0x0B937, 0x0B938, 0x0B939, 0x0B93A, 0x0B93B, 0x0B93C, 0x0B93D, 0x0B93E, 0x0B93F, 0x0B940, 0x0B941, 0x0B942, 0x0B943, 0x0B944, 0x0B945, 0x0B946, 0x0B947, 0x0B948, 0x0B949, 0x0B94A, 0x0B94B, 0x0B94C, 0x0B94D, 0x0B94E, 0x0B94F, 0x0B950, 0x0B951, 0x0B952, 0x0B953, 0x0B954, 0x0B955, 0x0B956, 0x0B957, 0x0B958, 0x0B959, 0x0B95A, 0x0B95B, 0x0B95C, 0x0B95D, 0x0B95E, 0x0B95F, 0x0B960, 0x0B961, 0x0B962, 0x0B963, 0x0B964, 0x0B965, 0x0B966, 0x0B967, 0x0B968, 0x0B969, 0x0B96A, 0x0B96B, 0x0B96C, 0x0B96D, 0x0B96E, 0x0B96F, 0x0B970, 0x0B971, 0x0B972, 0x0B973, 0x0B974, 0x0B975, 0x0B976, 0x0B977, 0x0B978, 0x0B979, 0x0B97A, 0x0B97B, 0x0B97C, 0x0B97D, 0x0B97E, 0x0B97F, 0x0B980, 0x0B981, 0x0B982, 0x0B983, 0x0B984, 0x0B985, 0x0B986, 0x0B987, 0x0B988, 0x0B989, 0x0B98A, 0x0B98B, 0x0B98C, 0x0B98D, 0x0B98E, 0x0B98F, 0x0B990, 0x0B991, 0x0B992, 0x0B993, 0x0B994, 0x0B995, 0x0B996, 0x0B997, 0x0B998, 0x0B999, 0x0B99A, 0x0B99B, 0x0B99C, 0x0B99D, 0x0B99E, 0x0B99F, 0x0B9A0, 0x0B9A1, 0x0B9A2, 0x0B9A3, 0x0B9A4, 0x0B9A5, 0x0B9A6, 0x0B9A7, 0x0B9A8, 0x0B9A9, 0x0B9AA, 0x0B9AB, 0x0B9AC, 0x0B9AD, 0x0B9AE, 0x0B9AF, 0x0B9B0, 0x0B9B1, 0x0B9B2, 0x0B9B3, 0x0B9B4, 0x0B9B5, 0x0B9B6, 0x0B9B7, 0x0B9B8, 0x0B9B9, 0x0B9BA, 0x0B9BB, 0x0B9BC, 0x0B9BD, 0x0B9BE, 0x0B9BF, 0x0B9C0, 0x0B9C1, 0x0B9C2, 0x0B9C3, 0x0B9C4, 0x0B9C5, 0x0B9C6, 0x0B9C7, 0x0B9C8, 0x0B9C9, 0x0B9CA, 0x0B9CB, 0x0B9CC, 0x0B9CD, 0x0B9CE, 0x0B9CF, 0x0B9D0, 0x0B9D1, 0x0B9D2, 0x0B9D3, 0x0B9D4, 0x0B9D5, 0x0B9D6, 0x0B9D7, 0x0B9D8, 0x0B9D9, 0x0B9DA, 0x0B9DB, 0x0B9DC, 0x0B9DD, 0x0B9DE, 0x0B9DF, 0x0B9E0, 0x0B9E1, 0x0B9E2, 0x0B9E3, 0x0B9E4, 0x0B9E5, 0x0B9E6, 0x0B9E7, 0x0B9E8, 0x0B9E9, 0x0B9EA, 0x0B9EB, 0x0B9EC, 0x0B9ED, 0x0B9EE, 0x0B9EF, 0x0B9F0, 0x0B9F1, 0x0B9F2, 0x0B9F3, 0x0B9F4, 0x0B9F5, 0x0B9F6, 0x0B9F7, 0x0B9F8, 0x0B9F9, 0x0B9FA, 0x0B9FB, 0x0B9FC, 0x0B9FD, 0x0B9FE, 0x0B9FF, 0x0BA00, 0x0BA01, 0x0BA02, 0x0BA03, 0x0BA04, 0x0BA05, 0x0BA06, 0x0BA07, 0x0BA08, 0x0BA09, 0x0BA0A, 0x0BA0B, 0x0BA0C, 0x0BA0D, 0x0BA0E, 0x0BA0F, 0x0BA10, 0x0BA11, 0x0BA12, 0x0BA13, 0x0BA14, 0x0BA15, 0x0BA16, 0x0BA17, 0x0BA18, 0x0BA19, 0x0BA1A, 0x0BA1B, 0x0BA1C, 0x0BA1D, 0x0BA1E, 0x0BA1F, 0x0BA20, 0x0BA21, 0x0BA22, 0x0BA23, 0x0BA24, 0x0BA25, 0x0BA26, 0x0BA27, 0x0BA28, 0x0BA29, 0x0BA2A, 0x0BA2B, 0x0BA2C, 0x0BA2D, 0x0BA2E, 0x0BA2F, 0x0BA30, 0x0BA31, 0x0BA32, 0x0BA33, 0x0BA34, 0x0BA35, 0x0BA36, 0x0BA37, 0x0BA38, 0x0BA39, 0x0BA3A, 0x0BA3B, 0x0BA3C, 0x0BA3D, 0x0BA3E, 0x0BA3F, 0x0BA40, 0x0BA41, 0x0BA42, 0x0BA43, 0x0BA44, 0x0BA45, 0x0BA46, 0x0BA47, 0x0BA48, 0x0BA49, 0x0BA4A, 0x0BA4B, 0x0BA4C, 0x0BA4D, 0x0BA4E, 0x0BA4F, 0x0BA50, 0x0BA51, 0x0BA52, 0x0BA53, 0x0BA54, 0x0BA55, 0x0BA56, 0x0BA57, 0x0BA58, 0x0BA59, 0x0BA5A, 0x0BA5B, 0x0BA5C, 0x0BA5D, 0x0BA5E, 0x0BA5F, 0x0BA60, 0x0BA61, 0x0BA62, 0x0BA63, 0x0BA64, 0x0BA65, 0x0BA66, 0x0BA67, 0x0BA68, 0x0BA69, 0x0BA6A, 0x0BA6B, 0x0BA6C, 0x0BA6D, 0x0BA6E, 0x0BA6F, 0x0BA70, 0x0BA71, 0x0BA72, 0x0BA73, 0x0BA74, 0x0BA75, 0x0BA76, 0x0BA77, 0x0BA78, 0x0BA79, 0x0BA7A, 0x0BA7B, 0x0BA7C, 0x0BA7D, 0x0BA7E, 0x0BA7F, 0x0BA80, 0x0BA81, 0x0BA82, 0x0BA83, 0x0BA84, 0x0BA85, 0x0BA86, 0x0BA87, 0x0BA88, 0x0BA89, 0x0BA8A, 0x0BA8B, 0x0BA8C, 0x0BA8D, 0x0BA8E, 0x0BA8F, 0x0BA90, 0x0BA91, 0x0BA92, 0x0BA93, 0x0BA94, 0x0BA95, 0x0BA96, 0x0BA97, 0x0BA98, 0x0BA99, 0x0BA9A, 0x0BA9B, 0x0BA9C, 0x0BA9D, 0x0BA9E, 0x0BA9F, 0x0BAA0, 0x0BAA1, 0x0BAA2, 0x0BAA3, 0x0BAA4, 0x0BAA5, 0x0BAA6, 0x0BAA7, 0x0BAA8, 0x0BAA9, 0x0BAAA, 0x0BAAB, 0x0BAAC, 0x0BAAD, 0x0BAAE, 0x0BAAF, 0x0BAB0, 0x0BAB1, 0x0BAB2, 0x0BAB3, 0x0BAB4, 0x0BAB5, 0x0BAB6, 0x0BAB7, 0x0BAB8, 0x0BAB9, 0x0BABA, 0x0BABB, 0x0BABC, 0x0BABD, 0x0BABE, 0x0BABF, 0x0BAC0, 0x0BAC1, 0x0BAC2, 0x0BAC3, 0x0BAC4, 0x0BAC5, 0x0BAC6, 0x0BAC7, 0x0BAC8, 0x0BAC9, 0x0BACA, 0x0BACB, 0x0BACC, 0x0BACD, 0x0BACE, 0x0BACF, 0x0BAD0, 0x0BAD1, 0x0BAD2, 0x0BAD3, 0x0BAD4, 0x0BAD5, 0x0BAD6, 0x0BAD7, 0x0BAD8, 0x0BAD9, 0x0BADA, 0x0BADB, 0x0BADC, 0x0BADD, 0x0BADE, 0x0BADF, 0x0BAE0, 0x0BAE1, 0x0BAE2, 0x0BAE3, 0x0BAE4, 0x0BAE5, 0x0BAE6, 0x0BAE7, 0x0BAE8, 0x0BAE9, 0x0BAEA, 0x0BAEB, 0x0BAEC, 0x0BAED, 0x0BAEE, 0x0BAEF, 0x0BAF0, 0x0BAF1, 0x0BAF2, 0x0BAF3, 0x0BAF4, 0x0BAF5, 0x0BAF6, 0x0BAF7, 0x0BAF8, 0x0BAF9, 0x0BAFA, 0x0BAFB, 0x0BAFC, 0x0BAFD, 0x0BAFE, 0x0BAFF, 0x0BB00, 0x0BB01, 0x0BB02, 0x0BB03, 0x0BB04, 0x0BB05, 0x0BB06, 0x0BB07, 0x0BB08, 0x0BB09, 0x0BB0A, 0x0BB0B, 0x0BB0C, 0x0BB0D, 0x0BB0E, 0x0BB0F, 0x0BB10, 0x0BB11, 0x0BB12, 0x0BB13, 0x0BB14, 0x0BB15, 0x0BB16, 0x0BB17, 0x0BB18, 0x0BB19, 0x0BB1A, 0x0BB1B, 0x0BB1C, 0x0BB1D, 0x0BB1E, 0x0BB1F, 0x0BB20, 0x0BB21, 0x0BB22, 0x0BB23, 0x0BB24, 0x0BB25, 0x0BB26, 0x0BB27, 0x0BB28, 0x0BB29, 0x0BB2A, 0x0BB2B, 0x0BB2C, 0x0BB2D, 0x0BB2E, 0x0BB2F, 0x0BB30, 0x0BB31, 0x0BB32, 0x0BB33, 0x0BB34, 0x0BB35, 0x0BB36, 0x0BB37, 0x0BB38, 0x0BB39, 0x0BB3A, 0x0BB3B, 0x0BB3C, 0x0BB3D, 0x0BB3E, 0x0BB3F, 0x0BB40, 0x0BB41, 0x0BB42, 0x0BB43, 0x0BB44, 0x0BB45, 0x0BB46, 0x0BB47, 0x0BB48, 0x0BB49, 0x0BB4A, 0x0BB4B, 0x0BB4C, 0x0BB4D, 0x0BB4E, 0x0BB4F, 0x0BB50, 0x0BB51, 0x0BB52, 0x0BB53, 0x0BB54, 0x0BB55, 0x0BB56, 0x0BB57, 0x0BB58, 0x0BB59, 0x0BB5A, 0x0BB5B, 0x0BB5C, 0x0BB5D, 0x0BB5E, 0x0BB5F, 0x0BB60, 0x0BB61, 0x0BB62, 0x0BB63, 0x0BB64, 0x0BB65, 0x0BB66, 0x0BB67, 0x0BB68, 0x0BB69, 0x0BB6A, 0x0BB6B, 0x0BB6C, 0x0BB6D, 0x0BB6E, 0x0BB6F, 0x0BB70, 0x0BB71, 0x0BB72, 0x0BB73, 0x0BB74, 0x0BB75, 0x0BB76, 0x0BB77, 0x0BB78, 0x0BB79, 0x0BB7A, 0x0BB7B, 0x0BB7C, 0x0BB7D, 0x0BB7E, 0x0BB7F, 0x0BB80, 0x0BB81, 0x0BB82, 0x0BB83, 0x0BB84, 0x0BB85, 0x0BB86, 0x0BB87, 0x0BB88, 0x0BB89, 0x0BB8A, 0x0BB8B, 0x0BB8C, 0x0BB8D, 0x0BB8E, 0x0BB8F, 0x0BB90, 0x0BB91, 0x0BB92, 0x0BB93, 0x0BB94, 0x0BB95, 0x0BB96, 0x0BB97, 0x0BB98, 0x0BB99, 0x0BB9A, 0x0BB9B, 0x0BB9C, 0x0BB9D, 0x0BB9E, 0x0BB9F, 0x0BBA0, 0x0BBA1, 0x0BBA2, 0x0BBA3, 0x0BBA4, 0x0BBA5, 0x0BBA6, 0x0BBA7, 0x0BBA8, 0x0BBA9, 0x0BBAA, 0x0BBAB, 0x0BBAC, 0x0BBAD, 0x0BBAE, 0x0BBAF, 0x0BBB0, 0x0BBB1, 0x0BBB2, 0x0BBB3, 0x0BBB4, 0x0BBB5, 0x0BBB6, 0x0BBB7, 0x0BBB8, 0x0BBB9, 0x0BBBA, 0x0BBBB, 0x0BBBC, 0x0BBBD, 0x0BBBE, 0x0BBBF, 0x0BBC0, 0x0BBC1, 0x0BBC2, 0x0BBC3, 0x0BBC4, 0x0BBC5, 0x0BBC6, 0x0BBC7, 0x0BBC8, 0x0BBC9, 0x0BBCA, 0x0BBCB, 0x0BBCC, 0x0BBCD, 0x0BBCE, 0x0BBCF, 0x0BBD0, 0x0BBD1, 0x0BBD2, 0x0BBD3, 0x0BBD4, 0x0BBD5, 0x0BBD6, 0x0BBD7, 0x0BBD8, 0x0BBD9, 0x0BBDA, 0x0BBDB, 0x0BBDC, 0x0BBDD, 0x0BBDE, 0x0BBDF, 0x0BBE0, 0x0BBE1, 0x0BBE2, 0x0BBE3, 0x0BBE4, 0x0BBE5, 0x0BBE6, 0x0BBE7, 0x0BBE8, 0x0BBE9, 0x0BBEA, 0x0BBEB, 0x0BBEC, 0x0BBED, 0x0BBEE, 0x0BBEF, 0x0BBF0, 0x0BBF1, 0x0BBF2, 0x0BBF3, 0x0BBF4, 0x0BBF5, 0x0BBF6, 0x0BBF7, 0x0BBF8, 0x0BBF9, 0x0BBFA, 0x0BBFB, 0x0BBFC, 0x0BBFD, 0x0BBFE, 0x0BBFF, 0x0BC00, 0x0BC01, 0x0BC02, 0x0BC03, 0x0BC04, 0x0BC05, 0x0BC06, 0x0BC07, 0x0BC08, 0x0BC09, 0x0BC0A, 0x0BC0B, 0x0BC0C, 0x0BC0D, 0x0BC0E, 0x0BC0F, 0x0BC10, 0x0BC11, 0x0BC12, 0x0BC13, 0x0BC14, 0x0BC15, 0x0BC16, 0x0BC17, 0x0BC18, 0x0BC19, 0x0BC1A, 0x0BC1B, 0x0BC1C, 0x0BC1D, 0x0BC1E, 0x0BC1F, 0x0BC20, 0x0BC21, 0x0BC22, 0x0BC23, 0x0BC24, 0x0BC25, 0x0BC26, 0x0BC27, 0x0BC28, 0x0BC29, 0x0BC2A, 0x0BC2B, 0x0BC2C, 0x0BC2D, 0x0BC2E, 0x0BC2F, 0x0BC30, 0x0BC31, 0x0BC32, 0x0BC33, 0x0BC34, 0x0BC35, 0x0BC36, 0x0BC37, 0x0BC38, 0x0BC39, 0x0BC3A, 0x0BC3B, 0x0BC3C, 0x0BC3D, 0x0BC3E, 0x0BC3F, 0x0BC40, 0x0BC41, 0x0BC42, 0x0BC43, 0x0BC44, 0x0BC45, 0x0BC46, 0x0BC47, 0x0BC48, 0x0BC49, 0x0BC4A, 0x0BC4B, 0x0BC4C, 0x0BC4D, 0x0BC4E, 0x0BC4F, 0x0BC50, 0x0BC51, 0x0BC52, 0x0BC53, 0x0BC54, 0x0BC55, 0x0BC56, 0x0BC57, 0x0BC58, 0x0BC59, 0x0BC5A, 0x0BC5B, 0x0BC5C, 0x0BC5D, 0x0BC5E, 0x0BC5F, 0x0BC60, 0x0BC61, 0x0BC62, 0x0BC63, 0x0BC64, 0x0BC65, 0x0BC66, 0x0BC67, 0x0BC68, 0x0BC69, 0x0BC6A, 0x0BC6B, 0x0BC6C, 0x0BC6D, 0x0BC6E, 0x0BC6F, 0x0BC70, 0x0BC71, 0x0BC72, 0x0BC73, 0x0BC74, 0x0BC75, 0x0BC76, 0x0BC77, 0x0BC78, 0x0BC79, 0x0BC7A, 0x0BC7B, 0x0BC7C, 0x0BC7D, 0x0BC7E, 0x0BC7F, 0x0BC80, 0x0BC81, 0x0BC82, 0x0BC83, 0x0BC84, 0x0BC85, 0x0BC86, 0x0BC87, 0x0BC88, 0x0BC89, 0x0BC8A, 0x0BC8B, 0x0BC8C, 0x0BC8D, 0x0BC8E, 0x0BC8F, 0x0BC90, 0x0BC91, 0x0BC92, 0x0BC93, 0x0BC94, 0x0BC95, 0x0BC96, 0x0BC97, 0x0BC98, 0x0BC99, 0x0BC9A, 0x0BC9B, 0x0BC9C, 0x0BC9D, 0x0BC9E, 0x0BC9F, 0x0BCA0, 0x0BCA1, 0x0BCA2, 0x0BCA3, 0x0BCA4, 0x0BCA5, 0x0BCA6, 0x0BCA7, 0x0BCA8, 0x0BCA9, 0x0BCAA, 0x0BCAB, 0x0BCAC, 0x0BCAD, 0x0BCAE, 0x0BCAF, 0x0BCB0, 0x0BCB1, 0x0BCB2, 0x0BCB3, 0x0BCB4, 0x0BCB5, 0x0BCB6, 0x0BCB7, 0x0BCB8, 0x0BCB9, 0x0BCBA, 0x0BCBB, 0x0BCBC, 0x0BCBD, 0x0BCBE, 0x0BCBF, 0x0BCC0, 0x0BCC1, 0x0BCC2, 0x0BCC3, 0x0BCC4, 0x0BCC5, 0x0BCC6, 0x0BCC7, 0x0BCC8, 0x0BCC9, 0x0BCCA, 0x0BCCB, 0x0BCCC, 0x0BCCD, 0x0BCCE, 0x0BCCF, 0x0BCD0, 0x0BCD1, 0x0BCD2, 0x0BCD3, 0x0BCD4, 0x0BCD5, 0x0BCD6, 0x0BCD7, 0x0BCD8, 0x0BCD9, 0x0BCDA, 0x0BCDB, 0x0BCDC, 0x0BCDD, 0x0BCDE, 0x0BCDF, 0x0BCE0, 0x0BCE1, 0x0BCE2, 0x0BCE3, 0x0BCE4, 0x0BCE5, 0x0BCE6, 0x0BCE7, 0x0BCE8, 0x0BCE9, 0x0BCEA, 0x0BCEB, 0x0BCEC, 0x0BCED, 0x0BCEE, 0x0BCEF, 0x0BCF0, 0x0BCF1, 0x0BCF2, 0x0BCF3, 0x0BCF4, 0x0BCF5, 0x0BCF6, 0x0BCF7, 0x0BCF8, 0x0BCF9, 0x0BCFA, 0x0BCFB, 0x0BCFC, 0x0BCFD, 0x0BCFE, 0x0BCFF, 0x0BD00, 0x0BD01, 0x0BD02, 0x0BD03, 0x0BD04, 0x0BD05, 0x0BD06, 0x0BD07, 0x0BD08, 0x0BD09, 0x0BD0A, 0x0BD0B, 0x0BD0C, 0x0BD0D, 0x0BD0E, 0x0BD0F, 0x0BD10, 0x0BD11, 0x0BD12, 0x0BD13, 0x0BD14, 0x0BD15, 0x0BD16, 0x0BD17, 0x0BD18, 0x0BD19, 0x0BD1A, 0x0BD1B, 0x0BD1C, 0x0BD1D, 0x0BD1E, 0x0BD1F, 0x0BD20, 0x0BD21, 0x0BD22, 0x0BD23, 0x0BD24, 0x0BD25, 0x0BD26, 0x0BD27, 0x0BD28, 0x0BD29, 0x0BD2A, 0x0BD2B, 0x0BD2C, 0x0BD2D, 0x0BD2E, 0x0BD2F, 0x0BD30, 0x0BD31, 0x0BD32, 0x0BD33, 0x0BD34, 0x0BD35, 0x0BD36, 0x0BD37, 0x0BD38, 0x0BD39, 0x0BD3A, 0x0BD3B, 0x0BD3C, 0x0BD3D, 0x0BD3E, 0x0BD3F, 0x0BD40, 0x0BD41, 0x0BD42, 0x0BD43, 0x0BD44, 0x0BD45, 0x0BD46, 0x0BD47, 0x0BD48, 0x0BD49, 0x0BD4A, 0x0BD4B, 0x0BD4C, 0x0BD4D, 0x0BD4E, 0x0BD4F, 0x0BD50, 0x0BD51, 0x0BD52, 0x0BD53, 0x0BD54, 0x0BD55, 0x0BD56, 0x0BD57, 0x0BD58, 0x0BD59, 0x0BD5A, 0x0BD5B, 0x0BD5C, 0x0BD5D, 0x0BD5E, 0x0BD5F, 0x0BD60, 0x0BD61, 0x0BD62, 0x0BD63, 0x0BD64, 0x0BD65, 0x0BD66, 0x0BD67, 0x0BD68, 0x0BD69, 0x0BD6A, 0x0BD6B, 0x0BD6C, 0x0BD6D, 0x0BD6E, 0x0BD6F, 0x0BD70, 0x0BD71, 0x0BD72, 0x0BD73, 0x0BD74, 0x0BD75, 0x0BD76, 0x0BD77, 0x0BD78, 0x0BD79, 0x0BD7A, 0x0BD7B, 0x0BD7C, 0x0BD7D, 0x0BD7E, 0x0BD7F, 0x0BD80, 0x0BD81, 0x0BD82, 0x0BD83, 0x0BD84, 0x0BD85, 0x0BD86, 0x0BD87, 0x0BD88, 0x0BD89, 0x0BD8A, 0x0BD8B, 0x0BD8C, 0x0BD8D, 0x0BD8E, 0x0BD8F, 0x0BD90, 0x0BD91, 0x0BD92, 0x0BD93, 0x0BD94, 0x0BD95, 0x0BD96, 0x0BD97, 0x0BD98, 0x0BD99, 0x0BD9A, 0x0BD9B, 0x0BD9C, 0x0BD9D, 0x0BD9E, 0x0BD9F, 0x0BDA0, 0x0BDA1, 0x0BDA2, 0x0BDA3, 0x0BDA4, 0x0BDA5, 0x0BDA6, 0x0BDA7, 0x0BDA8, 0x0BDA9, 0x0BDAA, 0x0BDAB, 0x0BDAC, 0x0BDAD, 0x0BDAE, 0x0BDAF, 0x0BDB0, 0x0BDB1, 0x0BDB2, 0x0BDB3, 0x0BDB4, 0x0BDB5, 0x0BDB6, 0x0BDB7, 0x0BDB8, 0x0BDB9, 0x0BDBA, 0x0BDBB, 0x0BDBC, 0x0BDBD, 0x0BDBE, 0x0BDBF, 0x0BDC0, 0x0BDC1, 0x0BDC2, 0x0BDC3, 0x0BDC4, 0x0BDC5, 0x0BDC6, 0x0BDC7, 0x0BDC8, 0x0BDC9, 0x0BDCA, 0x0BDCB, 0x0BDCC, 0x0BDCD, 0x0BDCE, 0x0BDCF, 0x0BDD0, 0x0BDD1, 0x0BDD2, 0x0BDD3, 0x0BDD4, 0x0BDD5, 0x0BDD6, 0x0BDD7, 0x0BDD8, 0x0BDD9, 0x0BDDA, 0x0BDDB, 0x0BDDC, 0x0BDDD, 0x0BDDE, 0x0BDDF, 0x0BDE0, 0x0BDE1, 0x0BDE2, 0x0BDE3, 0x0BDE4, 0x0BDE5, 0x0BDE6, 0x0BDE7, 0x0BDE8, 0x0BDE9, 0x0BDEA, 0x0BDEB, 0x0BDEC, 0x0BDED, 0x0BDEE, 0x0BDEF, 0x0BDF0, 0x0BDF1, 0x0BDF2, 0x0BDF3, 0x0BDF4, 0x0BDF5, 0x0BDF6, 0x0BDF7, 0x0BDF8, 0x0BDF9, 0x0BDFA, 0x0BDFB, 0x0BDFC, 0x0BDFD, 0x0BDFE, 0x0BDFF, 0x0BE00, 0x0BE01, 0x0BE02, 0x0BE03, 0x0BE04, 0x0BE05, 0x0BE06, 0x0BE07, 0x0BE08, 0x0BE09, 0x0BE0A, 0x0BE0B, 0x0BE0C, 0x0BE0D, 0x0BE0E, 0x0BE0F, 0x0BE10, 0x0BE11, 0x0BE12, 0x0BE13, 0x0BE14, 0x0BE15, 0x0BE16, 0x0BE17, 0x0BE18, 0x0BE19, 0x0BE1A, 0x0BE1B, 0x0BE1C, 0x0BE1D, 0x0BE1E, 0x0BE1F, 0x0BE20, 0x0BE21, 0x0BE22, 0x0BE23, 0x0BE24, 0x0BE25, 0x0BE26, 0x0BE27, 0x0BE28, 0x0BE29, 0x0BE2A, 0x0BE2B, 0x0BE2C, 0x0BE2D, 0x0BE2E, 0x0BE2F, 0x0BE30, 0x0BE31, 0x0BE32, 0x0BE33, 0x0BE34, 0x0BE35, 0x0BE36, 0x0BE37, 0x0BE38, 0x0BE39, 0x0BE3A, 0x0BE3B, 0x0BE3C, 0x0BE3D, 0x0BE3E, 0x0BE3F, 0x0BE40, 0x0BE41, 0x0BE42, 0x0BE43, 0x0BE44, 0x0BE45, 0x0BE46, 0x0BE47, 0x0BE48, 0x0BE49, 0x0BE4A, 0x0BE4B, 0x0BE4C, 0x0BE4D, 0x0BE4E, 0x0BE4F, 0x0BE50, 0x0BE51, 0x0BE52, 0x0BE53, 0x0BE54, 0x0BE55, 0x0BE56, 0x0BE57, 0x0BE58, 0x0BE59, 0x0BE5A, 0x0BE5B, 0x0BE5C, 0x0BE5D, 0x0BE5E, 0x0BE5F, 0x0BE60, 0x0BE61, 0x0BE62, 0x0BE63, 0x0BE64, 0x0BE65, 0x0BE66, 0x0BE67, 0x0BE68, 0x0BE69, 0x0BE6A, 0x0BE6B, 0x0BE6C, 0x0BE6D, 0x0BE6E, 0x0BE6F, 0x0BE70, 0x0BE71, 0x0BE72, 0x0BE73, 0x0BE74, 0x0BE75, 0x0BE76, 0x0BE77, 0x0BE78, 0x0BE79, 0x0BE7A, 0x0BE7B, 0x0BE7C, 0x0BE7D, 0x0BE7E, 0x0BE7F, 0x0BE80, 0x0BE81, 0x0BE82, 0x0BE83, 0x0BE84, 0x0BE85, 0x0BE86, 0x0BE87, 0x0BE88, 0x0BE89, 0x0BE8A, 0x0BE8B, 0x0BE8C, 0x0BE8D, 0x0BE8E, 0x0BE8F, 0x0BE90, 0x0BE91, 0x0BE92, 0x0BE93, 0x0BE94, 0x0BE95, 0x0BE96, 0x0BE97, 0x0BE98, 0x0BE99, 0x0BE9A, 0x0BE9B, 0x0BE9C, 0x0BE9D, 0x0BE9E, 0x0BE9F, 0x0BEA0, 0x0BEA1, 0x0BEA2, 0x0BEA3, 0x0BEA4, 0x0BEA5, 0x0BEA6, 0x0BEA7, 0x0BEA8, 0x0BEA9, 0x0BEAA, 0x0BEAB, 0x0BEAC, 0x0BEAD, 0x0BEAE, 0x0BEAF, 0x0BEB0, 0x0BEB1, 0x0BEB2, 0x0BEB3, 0x0BEB4, 0x0BEB5, 0x0BEB6, 0x0BEB7, 0x0BEB8, 0x0BEB9, 0x0BEBA, 0x0BEBB, 0x0BEBC, 0x0BEBD, 0x0BEBE, 0x0BEBF, 0x0BEC0, 0x0BEC1, 0x0BEC2, 0x0BEC3, 0x0BEC4, 0x0BEC5, 0x0BEC6, 0x0BEC7, 0x0BEC8, 0x0BEC9, 0x0BECA, 0x0BECB, 0x0BECC, 0x0BECD, 0x0BECE, 0x0BECF, 0x0BED0, 0x0BED1, 0x0BED2, 0x0BED3, 0x0BED4, 0x0BED5, 0x0BED6, 0x0BED7, 0x0BED8, 0x0BED9, 0x0BEDA, 0x0BEDB, 0x0BEDC, 0x0BEDD, 0x0BEDE, 0x0BEDF, 0x0BEE0, 0x0BEE1, 0x0BEE2, 0x0BEE3, 0x0BEE4, 0x0BEE5, 0x0BEE6, 0x0BEE7, 0x0BEE8, 0x0BEE9, 0x0BEEA, 0x0BEEB, 0x0BEEC, 0x0BEED, 0x0BEEE, 0x0BEEF, 0x0BEF0, 0x0BEF1, 0x0BEF2, 0x0BEF3, 0x0BEF4, 0x0BEF5, 0x0BEF6, 0x0BEF7, 0x0BEF8, 0x0BEF9, 0x0BEFA, 0x0BEFB, 0x0BEFC, 0x0BEFD, 0x0BEFE, 0x0BEFF, 0x0BF00, 0x0BF01, 0x0BF02, 0x0BF03, 0x0BF04, 0x0BF05, 0x0BF06, 0x0BF07, 0x0BF08, 0x0BF09, 0x0BF0A, 0x0BF0B, 0x0BF0C, 0x0BF0D, 0x0BF0E, 0x0BF0F, 0x0BF10, 0x0BF11, 0x0BF12, 0x0BF13, 0x0BF14, 0x0BF15, 0x0BF16, 0x0BF17, 0x0BF18, 0x0BF19, 0x0BF1A, 0x0BF1B, 0x0BF1C, 0x0BF1D, 0x0BF1E, 0x0BF1F, 0x0BF20, 0x0BF21, 0x0BF22, 0x0BF23, 0x0BF24, 0x0BF25, 0x0BF26, 0x0BF27, 0x0BF28, 0x0BF29, 0x0BF2A, 0x0BF2B, 0x0BF2C, 0x0BF2D, 0x0BF2E, 0x0BF2F, 0x0BF30, 0x0BF31, 0x0BF32, 0x0BF33, 0x0BF34, 0x0BF35, 0x0BF36, 0x0BF37, 0x0BF38, 0x0BF39, 0x0BF3A, 0x0BF3B, 0x0BF3C, 0x0BF3D, 0x0BF3E, 0x0BF3F, 0x0BF40, 0x0BF41, 0x0BF42, 0x0BF43, 0x0BF44, 0x0BF45, 0x0BF46, 0x0BF47, 0x0BF48, 0x0BF49, 0x0BF4A, 0x0BF4B, 0x0BF4C, 0x0BF4D, 0x0BF4E, 0x0BF4F, 0x0BF50, 0x0BF51, 0x0BF52, 0x0BF53, 0x0BF54, 0x0BF55, 0x0BF56, 0x0BF57, 0x0BF58, 0x0BF59, 0x0BF5A, 0x0BF5B, 0x0BF5C, 0x0BF5D, 0x0BF5E, 0x0BF5F, 0x0BF60, 0x0BF61, 0x0BF62, 0x0BF63, 0x0BF64, 0x0BF65, 0x0BF66, 0x0BF67, 0x0BF68, 0x0BF69, 0x0BF6A, 0x0BF6B, 0x0BF6C, 0x0BF6D, 0x0BF6E, 0x0BF6F, 0x0BF70, 0x0BF71, 0x0BF72, 0x0BF73, 0x0BF74, 0x0BF75, 0x0BF76, 0x0BF77, 0x0BF78, 0x0BF79, 0x0BF7A, 0x0BF7B, 0x0BF7C, 0x0BF7D, 0x0BF7E, 0x0BF7F, 0x0BF80, 0x0BF81, 0x0BF82, 0x0BF83, 0x0BF84, 0x0BF85, 0x0BF86, 0x0BF87, 0x0BF88, 0x0BF89, 0x0BF8A, 0x0BF8B, 0x0BF8C, 0x0BF8D, 0x0BF8E, 0x0BF8F, 0x0BF90, 0x0BF91, 0x0BF92, 0x0BF93, 0x0BF94, 0x0BF95, 0x0BF96, 0x0BF97, 0x0BF98, 0x0BF99, 0x0BF9A, 0x0BF9B, 0x0BF9C, 0x0BF9D, 0x0BF9E, 0x0BF9F, 0x0BFA0, 0x0BFA1, 0x0BFA2, 0x0BFA3, 0x0BFA4, 0x0BFA5, 0x0BFA6, 0x0BFA7, 0x0BFA8, 0x0BFA9, 0x0BFAA, 0x0BFAB, 0x0BFAC, 0x0BFAD, 0x0BFAE, 0x0BFAF, 0x0BFB0, 0x0BFB1, 0x0BFB2, 0x0BFB3, 0x0BFB4, 0x0BFB5, 0x0BFB6, 0x0BFB7, 0x0BFB8, 0x0BFB9, 0x0BFBA, 0x0BFBB, 0x0BFBC, 0x0BFBD, 0x0BFBE, 0x0BFBF, 0x0BFC0, 0x0BFC1, 0x0BFC2, 0x0BFC3, 0x0BFC4, 0x0BFC5, 0x0BFC6, 0x0BFC7, 0x0BFC8, 0x0BFC9, 0x0BFCA, 0x0BFCB, 0x0BFCC, 0x0BFCD, 0x0BFCE, 0x0BFCF, 0x0BFD0, 0x0BFD1, 0x0BFD2, 0x0BFD3, 0x0BFD4, 0x0BFD5, 0x0BFD6, 0x0BFD7, 0x0BFD8, 0x0BFD9, 0x0BFDA, 0x0BFDB, 0x0BFDC, 0x0BFDD, 0x0BFDE, 0x0BFDF, 0x0BFE0, 0x0BFE1, 0x0BFE2, 0x0BFE3, 0x0BFE4, 0x0BFE5, 0x0BFE6, 0x0BFE7, 0x0BFE8, 0x0BFE9, 0x0BFEA, 0x0BFEB, 0x0BFEC, 0x0BFED, 0x0BFEE, 0x0BFEF, 0x0BFF0, 0x0BFF1, 0x0BFF2, 0x0BFF3, 0x0BFF4, 0x0BFF5, 0x0BFF6, 0x0BFF7, 0x0BFF8, 0x0BFF9, 0x0BFFA, 0x0BFFB, 0x0BFFC, 0x0BFFD, 0x0BFFE, 0x0BFFF, 0x0C000, 0x0C001, 0x0C002, 0x0C003, 0x0C004, 0x0C005, 0x0C006, 0x0C007, 0x0C008, 0x0C009, 0x0C00A, 0x0C00B, 0x0C00C, 0x0C00D, 0x0C00E, 0x0C00F, 0x0C010, 0x0C011, 0x0C012, 0x0C013, 0x0C014, 0x0C015, 0x0C016, 0x0C017, 0x0C018, 0x0C019, 0x0C01A, 0x0C01B, 0x0C01C, 0x0C01D, 0x0C01E, 0x0C01F, 0x0C020, 0x0C021, 0x0C022, 0x0C023, 0x0C024, 0x0C025, 0x0C026, 0x0C027, 0x0C028, 0x0C029, 0x0C02A, 0x0C02B, 0x0C02C, 0x0C02D, 0x0C02E, 0x0C02F, 0x0C030, 0x0C031, 0x0C032, 0x0C033, 0x0C034, 0x0C035, 0x0C036, 0x0C037, 0x0C038, 0x0C039, 0x0C03A, 0x0C03B, 0x0C03C, 0x0C03D, 0x0C03E, 0x0C03F, 0x0C040, 0x0C041, 0x0C042, 0x0C043, 0x0C044, 0x0C045, 0x0C046, 0x0C047, 0x0C048, 0x0C049, 0x0C04A, 0x0C04B, 0x0C04C, 0x0C04D, 0x0C04E, 0x0C04F, 0x0C050, 0x0C051, 0x0C052, 0x0C053, 0x0C054, 0x0C055, 0x0C056, 0x0C057, 0x0C058, 0x0C059, 0x0C05A, 0x0C05B, 0x0C05C, 0x0C05D, 0x0C05E, 0x0C05F, 0x0C060, 0x0C061, 0x0C062, 0x0C063, 0x0C064, 0x0C065, 0x0C066, 0x0C067, 0x0C068, 0x0C069, 0x0C06A, 0x0C06B, 0x0C06C, 0x0C06D, 0x0C06E, 0x0C06F, 0x0C070, 0x0C071, 0x0C072, 0x0C073, 0x0C074, 0x0C075, 0x0C076, 0x0C077, 0x0C078, 0x0C079, 0x0C07A, 0x0C07B, 0x0C07C, 0x0C07D, 0x0C07E, 0x0C07F, 0x0C080, 0x0C081, 0x0C082, 0x0C083, 0x0C084, 0x0C085, 0x0C086, 0x0C087, 0x0C088, 0x0C089, 0x0C08A, 0x0C08B, 0x0C08C, 0x0C08D, 0x0C08E, 0x0C08F, 0x0C090, 0x0C091, 0x0C092, 0x0C093, 0x0C094, 0x0C095, 0x0C096, 0x0C097, 0x0C098, 0x0C099, 0x0C09A, 0x0C09B, 0x0C09C, 0x0C09D, 0x0C09E, 0x0C09F, 0x0C0A0, 0x0C0A1, 0x0C0A2, 0x0C0A3, 0x0C0A4, 0x0C0A5, 0x0C0A6, 0x0C0A7, 0x0C0A8, 0x0C0A9, 0x0C0AA, 0x0C0AB, 0x0C0AC, 0x0C0AD, 0x0C0AE, 0x0C0AF, 0x0C0B0, 0x0C0B1, 0x0C0B2, 0x0C0B3, 0x0C0B4, 0x0C0B5, 0x0C0B6, 0x0C0B7, 0x0C0B8, 0x0C0B9, 0x0C0BA, 0x0C0BB, 0x0C0BC, 0x0C0BD, 0x0C0BE, 0x0C0BF, 0x0C0C0, 0x0C0C1, 0x0C0C2, 0x0C0C3, 0x0C0C4, 0x0C0C5, 0x0C0C6, 0x0C0C7, 0x0C0C8, 0x0C0C9, 0x0C0CA, 0x0C0CB, 0x0C0CC, 0x0C0CD, 0x0C0CE, 0x0C0CF, 0x0C0D0, 0x0C0D1, 0x0C0D2, 0x0C0D3, 0x0C0D4, 0x0C0D5, 0x0C0D6, 0x0C0D7, 0x0C0D8, 0x0C0D9, 0x0C0DA, 0x0C0DB, 0x0C0DC, 0x0C0DD, 0x0C0DE, 0x0C0DF, 0x0C0E0, 0x0C0E1, 0x0C0E2, 0x0C0E3, 0x0C0E4, 0x0C0E5, 0x0C0E6, 0x0C0E7, 0x0C0E8, 0x0C0E9, 0x0C0EA, 0x0C0EB, 0x0C0EC, 0x0C0ED, 0x0C0EE, 0x0C0EF, 0x0C0F0, 0x0C0F1, 0x0C0F2, 0x0C0F3, 0x0C0F4, 0x0C0F5, 0x0C0F6, 0x0C0F7, 0x0C0F8, 0x0C0F9, 0x0C0FA, 0x0C0FB, 0x0C0FC, 0x0C0FD, 0x0C0FE, 0x0C0FF, 0x0C100, 0x0C101, 0x0C102, 0x0C103, 0x0C104, 0x0C105, 0x0C106, 0x0C107, 0x0C108, 0x0C109, 0x0C10A, 0x0C10B, 0x0C10C, 0x0C10D, 0x0C10E, 0x0C10F, 0x0C110, 0x0C111, 0x0C112, 0x0C113, 0x0C114, 0x0C115, 0x0C116, 0x0C117, 0x0C118, 0x0C119, 0x0C11A, 0x0C11B, 0x0C11C, 0x0C11D, 0x0C11E, 0x0C11F, 0x0C120, 0x0C121, 0x0C122, 0x0C123, 0x0C124, 0x0C125, 0x0C126, 0x0C127, 0x0C128, 0x0C129, 0x0C12A, 0x0C12B, 0x0C12C, 0x0C12D, 0x0C12E, 0x0C12F, 0x0C130, 0x0C131, 0x0C132, 0x0C133, 0x0C134, 0x0C135, 0x0C136, 0x0C137, 0x0C138, 0x0C139, 0x0C13A, 0x0C13B, 0x0C13C, 0x0C13D, 0x0C13E, 0x0C13F, 0x0C140, 0x0C141, 0x0C142, 0x0C143, 0x0C144, 0x0C145, 0x0C146, 0x0C147, 0x0C148, 0x0C149, 0x0C14A, 0x0C14B, 0x0C14C, 0x0C14D, 0x0C14E, 0x0C14F, 0x0C150, 0x0C151, 0x0C152, 0x0C153, 0x0C154, 0x0C155, 0x0C156, 0x0C157, 0x0C158, 0x0C159, 0x0C15A, 0x0C15B, 0x0C15C, 0x0C15D, 0x0C15E, 0x0C15F, 0x0C160, 0x0C161, 0x0C162, 0x0C163, 0x0C164, 0x0C165, 0x0C166, 0x0C167, 0x0C168, 0x0C169, 0x0C16A, 0x0C16B, 0x0C16C, 0x0C16D, 0x0C16E, 0x0C16F, 0x0C170, 0x0C171, 0x0C172, 0x0C173, 0x0C174, 0x0C175, 0x0C176, 0x0C177, 0x0C178, 0x0C179, 0x0C17A, 0x0C17B, 0x0C17C, 0x0C17D, 0x0C17E, 0x0C17F, 0x0C180, 0x0C181, 0x0C182, 0x0C183, 0x0C184, 0x0C185, 0x0C186, 0x0C187, 0x0C188, 0x0C189, 0x0C18A, 0x0C18B, 0x0C18C, 0x0C18D, 0x0C18E, 0x0C18F, 0x0C190, 0x0C191, 0x0C192, 0x0C193, 0x0C194, 0x0C195, 0x0C196, 0x0C197, 0x0C198, 0x0C199, 0x0C19A, 0x0C19B, 0x0C19C, 0x0C19D, 0x0C19E, 0x0C19F, 0x0C1A0, 0x0C1A1, 0x0C1A2, 0x0C1A3, 0x0C1A4, 0x0C1A5, 0x0C1A6, 0x0C1A7, 0x0C1A8, 0x0C1A9, 0x0C1AA, 0x0C1AB, 0x0C1AC, 0x0C1AD, 0x0C1AE, 0x0C1AF, 0x0C1B0, 0x0C1B1, 0x0C1B2, 0x0C1B3, 0x0C1B4, 0x0C1B5, 0x0C1B6, 0x0C1B7, 0x0C1B8, 0x0C1B9, 0x0C1BA, 0x0C1BB, 0x0C1BC, 0x0C1BD, 0x0C1BE, 0x0C1BF, 0x0C1C0, 0x0C1C1, 0x0C1C2, 0x0C1C3, 0x0C1C4, 0x0C1C5, 0x0C1C6, 0x0C1C7, 0x0C1C8, 0x0C1C9, 0x0C1CA, 0x0C1CB, 0x0C1CC, 0x0C1CD, 0x0C1CE, 0x0C1CF, 0x0C1D0, 0x0C1D1, 0x0C1D2, 0x0C1D3, 0x0C1D4, 0x0C1D5, 0x0C1D6, 0x0C1D7, 0x0C1D8, 0x0C1D9, 0x0C1DA, 0x0C1DB, 0x0C1DC, 0x0C1DD, 0x0C1DE, 0x0C1DF, 0x0C1E0, 0x0C1E1, 0x0C1E2, 0x0C1E3, 0x0C1E4, 0x0C1E5, 0x0C1E6, 0x0C1E7, 0x0C1E8, 0x0C1E9, 0x0C1EA, 0x0C1EB, 0x0C1EC, 0x0C1ED, 0x0C1EE, 0x0C1EF, 0x0C1F0, 0x0C1F1, 0x0C1F2, 0x0C1F3, 0x0C1F4, 0x0C1F5, 0x0C1F6, 0x0C1F7, 0x0C1F8, 0x0C1F9, 0x0C1FA, 0x0C1FB, 0x0C1FC, 0x0C1FD, 0x0C1FE, 0x0C1FF, 0x0C200, 0x0C201, 0x0C202, 0x0C203, 0x0C204, 0x0C205, 0x0C206, 0x0C207, 0x0C208, 0x0C209, 0x0C20A, 0x0C20B, 0x0C20C, 0x0C20D, 0x0C20E, 0x0C20F, 0x0C210, 0x0C211, 0x0C212, 0x0C213, 0x0C214, 0x0C215, 0x0C216, 0x0C217, 0x0C218, 0x0C219, 0x0C21A, 0x0C21B, 0x0C21C, 0x0C21D, 0x0C21E, 0x0C21F, 0x0C220, 0x0C221, 0x0C222, 0x0C223, 0x0C224, 0x0C225, 0x0C226, 0x0C227, 0x0C228, 0x0C229, 0x0C22A, 0x0C22B, 0x0C22C, 0x0C22D, 0x0C22E, 0x0C22F, 0x0C230, 0x0C231, 0x0C232, 0x0C233, 0x0C234, 0x0C235, 0x0C236, 0x0C237, 0x0C238, 0x0C239, 0x0C23A, 0x0C23B, 0x0C23C, 0x0C23D, 0x0C23E, 0x0C23F, 0x0C240, 0x0C241, 0x0C242, 0x0C243, 0x0C244, 0x0C245, 0x0C246, 0x0C247, 0x0C248, 0x0C249, 0x0C24A, 0x0C24B, 0x0C24C, 0x0C24D, 0x0C24E, 0x0C24F, 0x0C250, 0x0C251, 0x0C252, 0x0C253, 0x0C254, 0x0C255, 0x0C256, 0x0C257, 0x0C258, 0x0C259, 0x0C25A, 0x0C25B, 0x0C25C, 0x0C25D, 0x0C25E, 0x0C25F, 0x0C260, 0x0C261, 0x0C262, 0x0C263, 0x0C264, 0x0C265, 0x0C266, 0x0C267, 0x0C268, 0x0C269, 0x0C26A, 0x0C26B, 0x0C26C, 0x0C26D, 0x0C26E, 0x0C26F, 0x0C270, 0x0C271, 0x0C272, 0x0C273, 0x0C274, 0x0C275, 0x0C276, 0x0C277, 0x0C278, 0x0C279, 0x0C27A, 0x0C27B, 0x0C27C, 0x0C27D, 0x0C27E, 0x0C27F, 0x0C280, 0x0C281, 0x0C282, 0x0C283, 0x0C284, 0x0C285, 0x0C286, 0x0C287, 0x0C288, 0x0C289, 0x0C28A, 0x0C28B, 0x0C28C, 0x0C28D, 0x0C28E, 0x0C28F, 0x0C290, 0x0C291, 0x0C292, 0x0C293, 0x0C294, 0x0C295, 0x0C296, 0x0C297, 0x0C298, 0x0C299, 0x0C29A, 0x0C29B, 0x0C29C, 0x0C29D, 0x0C29E, 0x0C29F, 0x0C2A0, 0x0C2A1, 0x0C2A2, 0x0C2A3, 0x0C2A4, 0x0C2A5, 0x0C2A6, 0x0C2A7, 0x0C2A8, 0x0C2A9, 0x0C2AA, 0x0C2AB, 0x0C2AC, 0x0C2AD, 0x0C2AE, 0x0C2AF, 0x0C2B0, 0x0C2B1, 0x0C2B2, 0x0C2B3, 0x0C2B4, 0x0C2B5, 0x0C2B6, 0x0C2B7, 0x0C2B8, 0x0C2B9, 0x0C2BA, 0x0C2BB, 0x0C2BC, 0x0C2BD, 0x0C2BE, 0x0C2BF, 0x0C2C0, 0x0C2C1, 0x0C2C2, 0x0C2C3, 0x0C2C4, 0x0C2C5, 0x0C2C6, 0x0C2C7, 0x0C2C8, 0x0C2C9, 0x0C2CA, 0x0C2CB, 0x0C2CC, 0x0C2CD, 0x0C2CE, 0x0C2CF, 0x0C2D0, 0x0C2D1, 0x0C2D2, 0x0C2D3, 0x0C2D4, 0x0C2D5, 0x0C2D6, 0x0C2D7, 0x0C2D8, 0x0C2D9, 0x0C2DA, 0x0C2DB, 0x0C2DC, 0x0C2DD, 0x0C2DE, 0x0C2DF, 0x0C2E0, 0x0C2E1, 0x0C2E2, 0x0C2E3, 0x0C2E4, 0x0C2E5, 0x0C2E6, 0x0C2E7, 0x0C2E8, 0x0C2E9, 0x0C2EA, 0x0C2EB, 0x0C2EC, 0x0C2ED, 0x0C2EE, 0x0C2EF, 0x0C2F0, 0x0C2F1, 0x0C2F2, 0x0C2F3, 0x0C2F4, 0x0C2F5, 0x0C2F6, 0x0C2F7, 0x0C2F8, 0x0C2F9, 0x0C2FA, 0x0C2FB, 0x0C2FC, 0x0C2FD, 0x0C2FE, 0x0C2FF, 0x0C300, 0x0C301, 0x0C302, 0x0C303, 0x0C304, 0x0C305, 0x0C306, 0x0C307, 0x0C308, 0x0C309, 0x0C30A, 0x0C30B, 0x0C30C, 0x0C30D, 0x0C30E, 0x0C30F, 0x0C310, 0x0C311, 0x0C312, 0x0C313, 0x0C314, 0x0C315, 0x0C316, 0x0C317, 0x0C318, 0x0C319, 0x0C31A, 0x0C31B, 0x0C31C, 0x0C31D, 0x0C31E, 0x0C31F, 0x0C320, 0x0C321, 0x0C322, 0x0C323, 0x0C324, 0x0C325, 0x0C326, 0x0C327, 0x0C328, 0x0C329, 0x0C32A, 0x0C32B, 0x0C32C, 0x0C32D, 0x0C32E, 0x0C32F, 0x0C330, 0x0C331, 0x0C332, 0x0C333, 0x0C334, 0x0C335, 0x0C336, 0x0C337, 0x0C338, 0x0C339, 0x0C33A, 0x0C33B, 0x0C33C, 0x0C33D, 0x0C33E, 0x0C33F, 0x0C340, 0x0C341, 0x0C342, 0x0C343, 0x0C344, 0x0C345, 0x0C346, 0x0C347, 0x0C348, 0x0C349, 0x0C34A, 0x0C34B, 0x0C34C, 0x0C34D, 0x0C34E, 0x0C34F, 0x0C350, 0x0C351, 0x0C352, 0x0C353, 0x0C354, 0x0C355, 0x0C356, 0x0C357, 0x0C358, 0x0C359, 0x0C35A, 0x0C35B, 0x0C35C, 0x0C35D, 0x0C35E, 0x0C35F, 0x0C360, 0x0C361, 0x0C362, 0x0C363, 0x0C364, 0x0C365, 0x0C366, 0x0C367, 0x0C368, 0x0C369, 0x0C36A, 0x0C36B, 0x0C36C, 0x0C36D, 0x0C36E, 0x0C36F, 0x0C370, 0x0C371, 0x0C372, 0x0C373, 0x0C374, 0x0C375, 0x0C376, 0x0C377, 0x0C378, 0x0C379, 0x0C37A, 0x0C37B, 0x0C37C, 0x0C37D, 0x0C37E, 0x0C37F, 0x0C380, 0x0C381, 0x0C382, 0x0C383, 0x0C384, 0x0C385, 0x0C386, 0x0C387, 0x0C388, 0x0C389, 0x0C38A, 0x0C38B, 0x0C38C, 0x0C38D, 0x0C38E, 0x0C38F, 0x0C390, 0x0C391, 0x0C392, 0x0C393, 0x0C394, 0x0C395, 0x0C396, 0x0C397, 0x0C398, 0x0C399, 0x0C39A, 0x0C39B, 0x0C39C, 0x0C39D, 0x0C39E, 0x0C39F, 0x0C3A0, 0x0C3A1, 0x0C3A2, 0x0C3A3, 0x0C3A4, 0x0C3A5, 0x0C3A6, 0x0C3A7, 0x0C3A8, 0x0C3A9, 0x0C3AA, 0x0C3AB, 0x0C3AC, 0x0C3AD, 0x0C3AE, 0x0C3AF, 0x0C3B0, 0x0C3B1, 0x0C3B2, 0x0C3B3, 0x0C3B4, 0x0C3B5, 0x0C3B6, 0x0C3B7, 0x0C3B8, 0x0C3B9, 0x0C3BA, 0x0C3BB, 0x0C3BC, 0x0C3BD, 0x0C3BE, 0x0C3BF, 0x0C3C0, 0x0C3C1, 0x0C3C2, 0x0C3C3, 0x0C3C4, 0x0C3C5, 0x0C3C6, 0x0C3C7, 0x0C3C8, 0x0C3C9, 0x0C3CA, 0x0C3CB, 0x0C3CC, 0x0C3CD, 0x0C3CE, 0x0C3CF, 0x0C3D0, 0x0C3D1, 0x0C3D2, 0x0C3D3, 0x0C3D4, 0x0C3D5, 0x0C3D6, 0x0C3D7, 0x0C3D8, 0x0C3D9, 0x0C3DA, 0x0C3DB, 0x0C3DC, 0x0C3DD, 0x0C3DE, 0x0C3DF, 0x0C3E0, 0x0C3E1, 0x0C3E2, 0x0C3E3, 0x0C3E4, 0x0C3E5, 0x0C3E6, 0x0C3E7, 0x0C3E8, 0x0C3E9, 0x0C3EA, 0x0C3EB, 0x0C3EC, 0x0C3ED, 0x0C3EE, 0x0C3EF, 0x0C3F0, 0x0C3F1, 0x0C3F2, 0x0C3F3, 0x0C3F4, 0x0C3F5, 0x0C3F6, 0x0C3F7, 0x0C3F8, 0x0C3F9, 0x0C3FA, 0x0C3FB, 0x0C3FC, 0x0C3FD, 0x0C3FE, 0x0C3FF, 0x0C400, 0x0C401, 0x0C402, 0x0C403, 0x0C404, 0x0C405, 0x0C406, 0x0C407, 0x0C408, 0x0C409, 0x0C40A, 0x0C40B, 0x0C40C, 0x0C40D, 0x0C40E, 0x0C40F, 0x0C410, 0x0C411, 0x0C412, 0x0C413, 0x0C414, 0x0C415, 0x0C416, 0x0C417, 0x0C418, 0x0C419, 0x0C41A, 0x0C41B, 0x0C41C, 0x0C41D, 0x0C41E, 0x0C41F, 0x0C420, 0x0C421, 0x0C422, 0x0C423, 0x0C424, 0x0C425, 0x0C426, 0x0C427, 0x0C428, 0x0C429, 0x0C42A, 0x0C42B, 0x0C42C, 0x0C42D, 0x0C42E, 0x0C42F, 0x0C430, 0x0C431, 0x0C432, 0x0C433, 0x0C434, 0x0C435, 0x0C436, 0x0C437, 0x0C438, 0x0C439, 0x0C43A, 0x0C43B, 0x0C43C, 0x0C43D, 0x0C43E, 0x0C43F, 0x0C440, 0x0C441, 0x0C442, 0x0C443, 0x0C444, 0x0C445, 0x0C446, 0x0C447, 0x0C448, 0x0C449, 0x0C44A, 0x0C44B, 0x0C44C, 0x0C44D, 0x0C44E, 0x0C44F, 0x0C450, 0x0C451, 0x0C452, 0x0C453, 0x0C454, 0x0C455, 0x0C456, 0x0C457, 0x0C458, 0x0C459, 0x0C45A, 0x0C45B, 0x0C45C, 0x0C45D, 0x0C45E, 0x0C45F, 0x0C460, 0x0C461, 0x0C462, 0x0C463, 0x0C464, 0x0C465, 0x0C466, 0x0C467, 0x0C468, 0x0C469, 0x0C46A, 0x0C46B, 0x0C46C, 0x0C46D, 0x0C46E, 0x0C46F, 0x0C470, 0x0C471, 0x0C472, 0x0C473, 0x0C474, 0x0C475, 0x0C476, 0x0C477, 0x0C478, 0x0C479, 0x0C47A, 0x0C47B, 0x0C47C, 0x0C47D, 0x0C47E, 0x0C47F, 0x0C480, 0x0C481, 0x0C482, 0x0C483, 0x0C484, 0x0C485, 0x0C486, 0x0C487, 0x0C488, 0x0C489, 0x0C48A, 0x0C48B, 0x0C48C, 0x0C48D, 0x0C48E, 0x0C48F, 0x0C490, 0x0C491, 0x0C492, 0x0C493, 0x0C494, 0x0C495, 0x0C496, 0x0C497, 0x0C498, 0x0C499, 0x0C49A, 0x0C49B, 0x0C49C, 0x0C49D, 0x0C49E, 0x0C49F, 0x0C4A0, 0x0C4A1, 0x0C4A2, 0x0C4A3, 0x0C4A4, 0x0C4A5, 0x0C4A6, 0x0C4A7, 0x0C4A8, 0x0C4A9, 0x0C4AA, 0x0C4AB, 0x0C4AC, 0x0C4AD, 0x0C4AE, 0x0C4AF, 0x0C4B0, 0x0C4B1, 0x0C4B2, 0x0C4B3, 0x0C4B4, 0x0C4B5, 0x0C4B6, 0x0C4B7, 0x0C4B8, 0x0C4B9, 0x0C4BA, 0x0C4BB, 0x0C4BC, 0x0C4BD, 0x0C4BE, 0x0C4BF, 0x0C4C0, 0x0C4C1, 0x0C4C2, 0x0C4C3, 0x0C4C4, 0x0C4C5, 0x0C4C6, 0x0C4C7, 0x0C4C8, 0x0C4C9, 0x0C4CA, 0x0C4CB, 0x0C4CC, 0x0C4CD, 0x0C4CE, 0x0C4CF, 0x0C4D0, 0x0C4D1, 0x0C4D2, 0x0C4D3, 0x0C4D4, 0x0C4D5, 0x0C4D6, 0x0C4D7, 0x0C4D8, 0x0C4D9, 0x0C4DA, 0x0C4DB, 0x0C4DC, 0x0C4DD, 0x0C4DE, 0x0C4DF, 0x0C4E0, 0x0C4E1, 0x0C4E2, 0x0C4E3, 0x0C4E4, 0x0C4E5, 0x0C4E6, 0x0C4E7, 0x0C4E8, 0x0C4E9, 0x0C4EA, 0x0C4EB, 0x0C4EC, 0x0C4ED, 0x0C4EE, 0x0C4EF, 0x0C4F0, 0x0C4F1, 0x0C4F2, 0x0C4F3, 0x0C4F4, 0x0C4F5, 0x0C4F6, 0x0C4F7, 0x0C4F8, 0x0C4F9, 0x0C4FA, 0x0C4FB, 0x0C4FC, 0x0C4FD, 0x0C4FE, 0x0C4FF, 0x0C500, 0x0C501, 0x0C502, 0x0C503, 0x0C504, 0x0C505, 0x0C506, 0x0C507, 0x0C508, 0x0C509, 0x0C50A, 0x0C50B, 0x0C50C, 0x0C50D, 0x0C50E, 0x0C50F, 0x0C510, 0x0C511, 0x0C512, 0x0C513, 0x0C514, 0x0C515, 0x0C516, 0x0C517, 0x0C518, 0x0C519, 0x0C51A, 0x0C51B, 0x0C51C, 0x0C51D, 0x0C51E, 0x0C51F, 0x0C520, 0x0C521, 0x0C522, 0x0C523, 0x0C524, 0x0C525, 0x0C526, 0x0C527, 0x0C528, 0x0C529, 0x0C52A, 0x0C52B, 0x0C52C, 0x0C52D, 0x0C52E, 0x0C52F, 0x0C530, 0x0C531, 0x0C532, 0x0C533, 0x0C534, 0x0C535, 0x0C536, 0x0C537, 0x0C538, 0x0C539, 0x0C53A, 0x0C53B, 0x0C53C, 0x0C53D, 0x0C53E, 0x0C53F, 0x0C540, 0x0C541, 0x0C542, 0x0C543, 0x0C544, 0x0C545, 0x0C546, 0x0C547, 0x0C548, 0x0C549, 0x0C54A, 0x0C54B, 0x0C54C, 0x0C54D, 0x0C54E, 0x0C54F, 0x0C550, 0x0C551, 0x0C552, 0x0C553, 0x0C554, 0x0C555, 0x0C556, 0x0C557, 0x0C558, 0x0C559, 0x0C55A, 0x0C55B, 0x0C55C, 0x0C55D, 0x0C55E, 0x0C55F, 0x0C560, 0x0C561, 0x0C562, 0x0C563, 0x0C564, 0x0C565, 0x0C566, 0x0C567, 0x0C568, 0x0C569, 0x0C56A, 0x0C56B, 0x0C56C, 0x0C56D, 0x0C56E, 0x0C56F, 0x0C570, 0x0C571, 0x0C572, 0x0C573, 0x0C574, 0x0C575, 0x0C576, 0x0C577, 0x0C578, 0x0C579, 0x0C57A, 0x0C57B, 0x0C57C, 0x0C57D, 0x0C57E, 0x0C57F, 0x0C580, 0x0C581, 0x0C582, 0x0C583, 0x0C584, 0x0C585, 0x0C586, 0x0C587, 0x0C588, 0x0C589, 0x0C58A, 0x0C58B, 0x0C58C, 0x0C58D, 0x0C58E, 0x0C58F, 0x0C590, 0x0C591, 0x0C592, 0x0C593, 0x0C594, 0x0C595, 0x0C596, 0x0C597, 0x0C598, 0x0C599, 0x0C59A, 0x0C59B, 0x0C59C, 0x0C59D, 0x0C59E, 0x0C59F, 0x0C5A0, 0x0C5A1, 0x0C5A2, 0x0C5A3, 0x0C5A4, 0x0C5A5, 0x0C5A6, 0x0C5A7, 0x0C5A8, 0x0C5A9, 0x0C5AA, 0x0C5AB, 0x0C5AC, 0x0C5AD, 0x0C5AE, 0x0C5AF, 0x0C5B0, 0x0C5B1, 0x0C5B2, 0x0C5B3, 0x0C5B4, 0x0C5B5, 0x0C5B6, 0x0C5B7, 0x0C5B8, 0x0C5B9, 0x0C5BA, 0x0C5BB, 0x0C5BC, 0x0C5BD, 0x0C5BE, 0x0C5BF, 0x0C5C0, 0x0C5C1, 0x0C5C2, 0x0C5C3, 0x0C5C4, 0x0C5C5, 0x0C5C6, 0x0C5C7, 0x0C5C8, 0x0C5C9, 0x0C5CA, 0x0C5CB, 0x0C5CC, 0x0C5CD, 0x0C5CE, 0x0C5CF, 0x0C5D0, 0x0C5D1, 0x0C5D2, 0x0C5D3, 0x0C5D4, 0x0C5D5, 0x0C5D6, 0x0C5D7, 0x0C5D8, 0x0C5D9, 0x0C5DA, 0x0C5DB, 0x0C5DC, 0x0C5DD, 0x0C5DE, 0x0C5DF, 0x0C5E0, 0x0C5E1, 0x0C5E2, 0x0C5E3, 0x0C5E4, 0x0C5E5, 0x0C5E6, 0x0C5E7, 0x0C5E8, 0x0C5E9, 0x0C5EA, 0x0C5EB, 0x0C5EC, 0x0C5ED, 0x0C5EE, 0x0C5EF, 0x0C5F0, 0x0C5F1, 0x0C5F2, 0x0C5F3, 0x0C5F4, 0x0C5F5, 0x0C5F6, 0x0C5F7, 0x0C5F8, 0x0C5F9, 0x0C5FA, 0x0C5FB, 0x0C5FC, 0x0C5FD, 0x0C5FE, 0x0C5FF, 0x0C600, 0x0C601, 0x0C602, 0x0C603, 0x0C604, 0x0C605, 0x0C606, 0x0C607, 0x0C608, 0x0C609, 0x0C60A, 0x0C60B, 0x0C60C, 0x0C60D, 0x0C60E, 0x0C60F, 0x0C610, 0x0C611, 0x0C612, 0x0C613, 0x0C614, 0x0C615, 0x0C616, 0x0C617, 0x0C618, 0x0C619, 0x0C61A, 0x0C61B, 0x0C61C, 0x0C61D, 0x0C61E, 0x0C61F, 0x0C620, 0x0C621, 0x0C622, 0x0C623, 0x0C624, 0x0C625, 0x0C626, 0x0C627, 0x0C628, 0x0C629, 0x0C62A, 0x0C62B, 0x0C62C, 0x0C62D, 0x0C62E, 0x0C62F, 0x0C630, 0x0C631, 0x0C632, 0x0C633, 0x0C634, 0x0C635, 0x0C636, 0x0C637, 0x0C638, 0x0C639, 0x0C63A, 0x0C63B, 0x0C63C, 0x0C63D, 0x0C63E, 0x0C63F, 0x0C640, 0x0C641, 0x0C642, 0x0C643, 0x0C644, 0x0C645, 0x0C646, 0x0C647, 0x0C648, 0x0C649, 0x0C64A, 0x0C64B, 0x0C64C, 0x0C64D, 0x0C64E, 0x0C64F, 0x0C650, 0x0C651, 0x0C652, 0x0C653, 0x0C654, 0x0C655, 0x0C656, 0x0C657, 0x0C658, 0x0C659, 0x0C65A, 0x0C65B, 0x0C65C, 0x0C65D, 0x0C65E, 0x0C65F, 0x0C660, 0x0C661, 0x0C662, 0x0C663, 0x0C664, 0x0C665, 0x0C666, 0x0C667, 0x0C668, 0x0C669, 0x0C66A, 0x0C66B, 0x0C66C, 0x0C66D, 0x0C66E, 0x0C66F, 0x0C670, 0x0C671, 0x0C672, 0x0C673, 0x0C674, 0x0C675, 0x0C676, 0x0C677, 0x0C678, 0x0C679, 0x0C67A, 0x0C67B, 0x0C67C, 0x0C67D, 0x0C67E, 0x0C67F, 0x0C680, 0x0C681, 0x0C682, 0x0C683, 0x0C684, 0x0C685, 0x0C686, 0x0C687, 0x0C688, 0x0C689, 0x0C68A, 0x0C68B, 0x0C68C, 0x0C68D, 0x0C68E, 0x0C68F, 0x0C690, 0x0C691, 0x0C692, 0x0C693, 0x0C694, 0x0C695, 0x0C696, 0x0C697, 0x0C698, 0x0C699, 0x0C69A, 0x0C69B, 0x0C69C, 0x0C69D, 0x0C69E, 0x0C69F, 0x0C6A0, 0x0C6A1, 0x0C6A2, 0x0C6A3, 0x0C6A4, 0x0C6A5, 0x0C6A6, 0x0C6A7, 0x0C6A8, 0x0C6A9, 0x0C6AA, 0x0C6AB, 0x0C6AC, 0x0C6AD, 0x0C6AE, 0x0C6AF, 0x0C6B0, 0x0C6B1, 0x0C6B2, 0x0C6B3, 0x0C6B4, 0x0C6B5, 0x0C6B6, 0x0C6B7, 0x0C6B8, 0x0C6B9, 0x0C6BA, 0x0C6BB, 0x0C6BC, 0x0C6BD, 0x0C6BE, 0x0C6BF, 0x0C6C0, 0x0C6C1, 0x0C6C2, 0x0C6C3, 0x0C6C4, 0x0C6C5, 0x0C6C6, 0x0C6C7, 0x0C6C8, 0x0C6C9, 0x0C6CA, 0x0C6CB, 0x0C6CC, 0x0C6CD, 0x0C6CE, 0x0C6CF, 0x0C6D0, 0x0C6D1, 0x0C6D2, 0x0C6D3, 0x0C6D4, 0x0C6D5, 0x0C6D6, 0x0C6D7, 0x0C6D8, 0x0C6D9, 0x0C6DA, 0x0C6DB, 0x0C6DC, 0x0C6DD, 0x0C6DE, 0x0C6DF, 0x0C6E0, 0x0C6E1, 0x0C6E2, 0x0C6E3, 0x0C6E4, 0x0C6E5, 0x0C6E6, 0x0C6E7, 0x0C6E8, 0x0C6E9, 0x0C6EA, 0x0C6EB, 0x0C6EC, 0x0C6ED, 0x0C6EE, 0x0C6EF, 0x0C6F0, 0x0C6F1, 0x0C6F2, 0x0C6F3, 0x0C6F4, 0x0C6F5, 0x0C6F6, 0x0C6F7, 0x0C6F8, 0x0C6F9, 0x0C6FA, 0x0C6FB, 0x0C6FC, 0x0C6FD, 0x0C6FE, 0x0C6FF, 0x0C700, 0x0C701, 0x0C702, 0x0C703, 0x0C704, 0x0C705, 0x0C706, 0x0C707, 0x0C708, 0x0C709, 0x0C70A, 0x0C70B, 0x0C70C, 0x0C70D, 0x0C70E, 0x0C70F, 0x0C710, 0x0C711, 0x0C712, 0x0C713, 0x0C714, 0x0C715, 0x0C716, 0x0C717, 0x0C718, 0x0C719, 0x0C71A, 0x0C71B, 0x0C71C, 0x0C71D, 0x0C71E, 0x0C71F, 0x0C720, 0x0C721, 0x0C722, 0x0C723, 0x0C724, 0x0C725, 0x0C726, 0x0C727, 0x0C728, 0x0C729, 0x0C72A, 0x0C72B, 0x0C72C, 0x0C72D, 0x0C72E, 0x0C72F, 0x0C730, 0x0C731, 0x0C732, 0x0C733, 0x0C734, 0x0C735, 0x0C736, 0x0C737, 0x0C738, 0x0C739, 0x0C73A, 0x0C73B, 0x0C73C, 0x0C73D, 0x0C73E, 0x0C73F, 0x0C740, 0x0C741, 0x0C742, 0x0C743, 0x0C744, 0x0C745, 0x0C746, 0x0C747, 0x0C748, 0x0C749, 0x0C74A, 0x0C74B, 0x0C74C, 0x0C74D, 0x0C74E, 0x0C74F, 0x0C750, 0x0C751, 0x0C752, 0x0C753, 0x0C754, 0x0C755, 0x0C756, 0x0C757, 0x0C758, 0x0C759, 0x0C75A, 0x0C75B, 0x0C75C, 0x0C75D, 0x0C75E, 0x0C75F, 0x0C760, 0x0C761, 0x0C762, 0x0C763, 0x0C764, 0x0C765, 0x0C766, 0x0C767, 0x0C768, 0x0C769, 0x0C76A, 0x0C76B, 0x0C76C, 0x0C76D, 0x0C76E, 0x0C76F, 0x0C770, 0x0C771, 0x0C772, 0x0C773, 0x0C774, 0x0C775, 0x0C776, 0x0C777, 0x0C778, 0x0C779, 0x0C77A, 0x0C77B, 0x0C77C, 0x0C77D, 0x0C77E, 0x0C77F, 0x0C780, 0x0C781, 0x0C782, 0x0C783, 0x0C784, 0x0C785, 0x0C786, 0x0C787, 0x0C788, 0x0C789, 0x0C78A, 0x0C78B, 0x0C78C, 0x0C78D, 0x0C78E, 0x0C78F, 0x0C790, 0x0C791, 0x0C792, 0x0C793, 0x0C794, 0x0C795, 0x0C796, 0x0C797, 0x0C798, 0x0C799, 0x0C79A, 0x0C79B, 0x0C79C, 0x0C79D, 0x0C79E, 0x0C79F, 0x0C7A0, 0x0C7A1, 0x0C7A2, 0x0C7A3, 0x0C7A4, 0x0C7A5, 0x0C7A6, 0x0C7A7, 0x0C7A8, 0x0C7A9, 0x0C7AA, 0x0C7AB, 0x0C7AC, 0x0C7AD, 0x0C7AE, 0x0C7AF, 0x0C7B0, 0x0C7B1, 0x0C7B2, 0x0C7B3, 0x0C7B4, 0x0C7B5, 0x0C7B6, 0x0C7B7, 0x0C7B8, 0x0C7B9, 0x0C7BA, 0x0C7BB, 0x0C7BC, 0x0C7BD, 0x0C7BE, 0x0C7BF, 0x0C7C0, 0x0C7C1, 0x0C7C2, 0x0C7C3, 0x0C7C4, 0x0C7C5, 0x0C7C6, 0x0C7C7, 0x0C7C8, 0x0C7C9, 0x0C7CA, 0x0C7CB, 0x0C7CC, 0x0C7CD, 0x0C7CE, 0x0C7CF, 0x0C7D0, 0x0C7D1, 0x0C7D2, 0x0C7D3, 0x0C7D4, 0x0C7D5, 0x0C7D6, 0x0C7D7, 0x0C7D8, 0x0C7D9, 0x0C7DA, 0x0C7DB, 0x0C7DC, 0x0C7DD, 0x0C7DE, 0x0C7DF, 0x0C7E0, 0x0C7E1, 0x0C7E2, 0x0C7E3, 0x0C7E4, 0x0C7E5, 0x0C7E6, 0x0C7E7, 0x0C7E8, 0x0C7E9, 0x0C7EA, 0x0C7EB, 0x0C7EC, 0x0C7ED, 0x0C7EE, 0x0C7EF, 0x0C7F0, 0x0C7F1, 0x0C7F2, 0x0C7F3, 0x0C7F4, 0x0C7F5, 0x0C7F6, 0x0C7F7, 0x0C7F8, 0x0C7F9, 0x0C7FA, 0x0C7FB, 0x0C7FC, 0x0C7FD, 0x0C7FE, 0x0C7FF, 0x0C800, 0x0C801, 0x0C802, 0x0C803, 0x0C804, 0x0C805, 0x0C806, 0x0C807, 0x0C808, 0x0C809, 0x0C80A, 0x0C80B, 0x0C80C, 0x0C80D, 0x0C80E, 0x0C80F, 0x0C810, 0x0C811, 0x0C812, 0x0C813, 0x0C814, 0x0C815, 0x0C816, 0x0C817, 0x0C818, 0x0C819, 0x0C81A, 0x0C81B, 0x0C81C, 0x0C81D, 0x0C81E, 0x0C81F, 0x0C820, 0x0C821, 0x0C822, 0x0C823, 0x0C824, 0x0C825, 0x0C826, 0x0C827, 0x0C828, 0x0C829, 0x0C82A, 0x0C82B, 0x0C82C, 0x0C82D, 0x0C82E, 0x0C82F, 0x0C830, 0x0C831, 0x0C832, 0x0C833, 0x0C834, 0x0C835, 0x0C836, 0x0C837, 0x0C838, 0x0C839, 0x0C83A, 0x0C83B, 0x0C83C, 0x0C83D, 0x0C83E, 0x0C83F, 0x0C840, 0x0C841, 0x0C842, 0x0C843, 0x0C844, 0x0C845, 0x0C846, 0x0C847, 0x0C848, 0x0C849, 0x0C84A, 0x0C84B, 0x0C84C, 0x0C84D, 0x0C84E, 0x0C84F, 0x0C850, 0x0C851, 0x0C852, 0x0C853, 0x0C854, 0x0C855, 0x0C856, 0x0C857, 0x0C858, 0x0C859, 0x0C85A, 0x0C85B, 0x0C85C, 0x0C85D, 0x0C85E, 0x0C85F, 0x0C860, 0x0C861, 0x0C862, 0x0C863, 0x0C864, 0x0C865, 0x0C866, 0x0C867, 0x0C868, 0x0C869, 0x0C86A, 0x0C86B, 0x0C86C, 0x0C86D, 0x0C86E, 0x0C86F, 0x0C870, 0x0C871, 0x0C872, 0x0C873, 0x0C874, 0x0C875, 0x0C876, 0x0C877, 0x0C878, 0x0C879, 0x0C87A, 0x0C87B, 0x0C87C, 0x0C87D, 0x0C87E, 0x0C87F, 0x0C880, 0x0C881, 0x0C882, 0x0C883, 0x0C884, 0x0C885, 0x0C886, 0x0C887, 0x0C888, 0x0C889, 0x0C88A, 0x0C88B, 0x0C88C, 0x0C88D, 0x0C88E, 0x0C88F, 0x0C890, 0x0C891, 0x0C892, 0x0C893, 0x0C894, 0x0C895, 0x0C896, 0x0C897, 0x0C898, 0x0C899, 0x0C89A, 0x0C89B, 0x0C89C, 0x0C89D, 0x0C89E, 0x0C89F, 0x0C8A0, 0x0C8A1, 0x0C8A2, 0x0C8A3, 0x0C8A4, 0x0C8A5, 0x0C8A6, 0x0C8A7, 0x0C8A8, 0x0C8A9, 0x0C8AA, 0x0C8AB, 0x0C8AC, 0x0C8AD, 0x0C8AE, 0x0C8AF, 0x0C8B0, 0x0C8B1, 0x0C8B2, 0x0C8B3, 0x0C8B4, 0x0C8B5, 0x0C8B6, 0x0C8B7, 0x0C8B8, 0x0C8B9, 0x0C8BA, 0x0C8BB, 0x0C8BC, 0x0C8BD, 0x0C8BE, 0x0C8BF, 0x0C8C0, 0x0C8C1, 0x0C8C2, 0x0C8C3, 0x0C8C4, 0x0C8C5, 0x0C8C6, 0x0C8C7, 0x0C8C8, 0x0C8C9, 0x0C8CA, 0x0C8CB, 0x0C8CC, 0x0C8CD, 0x0C8CE, 0x0C8CF, 0x0C8D0, 0x0C8D1, 0x0C8D2, 0x0C8D3, 0x0C8D4, 0x0C8D5, 0x0C8D6, 0x0C8D7, 0x0C8D8, 0x0C8D9, 0x0C8DA, 0x0C8DB, 0x0C8DC, 0x0C8DD, 0x0C8DE, 0x0C8DF, 0x0C8E0, 0x0C8E1, 0x0C8E2, 0x0C8E3, 0x0C8E4, 0x0C8E5, 0x0C8E6, 0x0C8E7, 0x0C8E8, 0x0C8E9, 0x0C8EA, 0x0C8EB, 0x0C8EC, 0x0C8ED, 0x0C8EE, 0x0C8EF, 0x0C8F0, 0x0C8F1, 0x0C8F2, 0x0C8F3, 0x0C8F4, 0x0C8F5, 0x0C8F6, 0x0C8F7, 0x0C8F8, 0x0C8F9, 0x0C8FA, 0x0C8FB, 0x0C8FC, 0x0C8FD, 0x0C8FE, 0x0C8FF, 0x0C900, 0x0C901, 0x0C902, 0x0C903, 0x0C904, 0x0C905, 0x0C906, 0x0C907, 0x0C908, 0x0C909, 0x0C90A, 0x0C90B, 0x0C90C, 0x0C90D, 0x0C90E, 0x0C90F, 0x0C910, 0x0C911, 0x0C912, 0x0C913, 0x0C914, 0x0C915, 0x0C916, 0x0C917, 0x0C918, 0x0C919, 0x0C91A, 0x0C91B, 0x0C91C, 0x0C91D, 0x0C91E, 0x0C91F, 0x0C920, 0x0C921, 0x0C922, 0x0C923, 0x0C924, 0x0C925, 0x0C926, 0x0C927, 0x0C928, 0x0C929, 0x0C92A, 0x0C92B, 0x0C92C, 0x0C92D, 0x0C92E, 0x0C92F, 0x0C930, 0x0C931, 0x0C932, 0x0C933, 0x0C934, 0x0C935, 0x0C936, 0x0C937, 0x0C938, 0x0C939, 0x0C93A, 0x0C93B, 0x0C93C, 0x0C93D, 0x0C93E, 0x0C93F, 0x0C940, 0x0C941, 0x0C942, 0x0C943, 0x0C944, 0x0C945, 0x0C946, 0x0C947, 0x0C948, 0x0C949, 0x0C94A, 0x0C94B, 0x0C94C, 0x0C94D, 0x0C94E, 0x0C94F, 0x0C950, 0x0C951, 0x0C952, 0x0C953, 0x0C954, 0x0C955, 0x0C956, 0x0C957, 0x0C958, 0x0C959, 0x0C95A, 0x0C95B, 0x0C95C, 0x0C95D, 0x0C95E, 0x0C95F, 0x0C960, 0x0C961, 0x0C962, 0x0C963, 0x0C964, 0x0C965, 0x0C966, 0x0C967, 0x0C968, 0x0C969, 0x0C96A, 0x0C96B, 0x0C96C, 0x0C96D, 0x0C96E, 0x0C96F, 0x0C970, 0x0C971, 0x0C972, 0x0C973, 0x0C974, 0x0C975, 0x0C976, 0x0C977, 0x0C978, 0x0C979, 0x0C97A, 0x0C97B, 0x0C97C, 0x0C97D, 0x0C97E, 0x0C97F, 0x0C980, 0x0C981, 0x0C982, 0x0C983, 0x0C984, 0x0C985, 0x0C986, 0x0C987, 0x0C988, 0x0C989, 0x0C98A, 0x0C98B, 0x0C98C, 0x0C98D, 0x0C98E, 0x0C98F, 0x0C990, 0x0C991, 0x0C992, 0x0C993, 0x0C994, 0x0C995, 0x0C996, 0x0C997, 0x0C998, 0x0C999, 0x0C99A, 0x0C99B, 0x0C99C, 0x0C99D, 0x0C99E, 0x0C99F, 0x0C9A0, 0x0C9A1, 0x0C9A2, 0x0C9A3, 0x0C9A4, 0x0C9A5, 0x0C9A6, 0x0C9A7, 0x0C9A8, 0x0C9A9, 0x0C9AA, 0x0C9AB, 0x0C9AC, 0x0C9AD, 0x0C9AE, 0x0C9AF, 0x0C9B0, 0x0C9B1, 0x0C9B2, 0x0C9B3, 0x0C9B4, 0x0C9B5, 0x0C9B6, 0x0C9B7, 0x0C9B8, 0x0C9B9, 0x0C9BA, 0x0C9BB, 0x0C9BC, 0x0C9BD, 0x0C9BE, 0x0C9BF, 0x0C9C0, 0x0C9C1, 0x0C9C2, 0x0C9C3, 0x0C9C4, 0x0C9C5, 0x0C9C6, 0x0C9C7, 0x0C9C8, 0x0C9C9, 0x0C9CA, 0x0C9CB, 0x0C9CC, 0x0C9CD, 0x0C9CE, 0x0C9CF, 0x0C9D0, 0x0C9D1, 0x0C9D2, 0x0C9D3, 0x0C9D4, 0x0C9D5, 0x0C9D6, 0x0C9D7, 0x0C9D8, 0x0C9D9, 0x0C9DA, 0x0C9DB, 0x0C9DC, 0x0C9DD, 0x0C9DE, 0x0C9DF, 0x0C9E0, 0x0C9E1, 0x0C9E2, 0x0C9E3, 0x0C9E4, 0x0C9E5, 0x0C9E6, 0x0C9E7, 0x0C9E8, 0x0C9E9, 0x0C9EA, 0x0C9EB, 0x0C9EC, 0x0C9ED, 0x0C9EE, 0x0C9EF, 0x0C9F0, 0x0C9F1, 0x0C9F2, 0x0C9F3, 0x0C9F4, 0x0C9F5, 0x0C9F6, 0x0C9F7, 0x0C9F8, 0x0C9F9, 0x0C9FA, 0x0C9FB, 0x0C9FC, 0x0C9FD, 0x0C9FE, 0x0C9FF, 0x0CA00, 0x0CA01, 0x0CA02, 0x0CA03, 0x0CA04, 0x0CA05, 0x0CA06, 0x0CA07, 0x0CA08, 0x0CA09, 0x0CA0A, 0x0CA0B, 0x0CA0C, 0x0CA0D, 0x0CA0E, 0x0CA0F, 0x0CA10, 0x0CA11, 0x0CA12, 0x0CA13, 0x0CA14, 0x0CA15, 0x0CA16, 0x0CA17, 0x0CA18, 0x0CA19, 0x0CA1A, 0x0CA1B, 0x0CA1C, 0x0CA1D, 0x0CA1E, 0x0CA1F, 0x0CA20, 0x0CA21, 0x0CA22, 0x0CA23, 0x0CA24, 0x0CA25, 0x0CA26, 0x0CA27, 0x0CA28, 0x0CA29, 0x0CA2A, 0x0CA2B, 0x0CA2C, 0x0CA2D, 0x0CA2E, 0x0CA2F, 0x0CA30, 0x0CA31, 0x0CA32, 0x0CA33, 0x0CA34, 0x0CA35, 0x0CA36, 0x0CA37, 0x0CA38, 0x0CA39, 0x0CA3A, 0x0CA3B, 0x0CA3C, 0x0CA3D, 0x0CA3E, 0x0CA3F, 0x0CA40, 0x0CA41, 0x0CA42, 0x0CA43, 0x0CA44, 0x0CA45, 0x0CA46, 0x0CA47, 0x0CA48, 0x0CA49, 0x0CA4A, 0x0CA4B, 0x0CA4C, 0x0CA4D, 0x0CA4E, 0x0CA4F, 0x0CA50, 0x0CA51, 0x0CA52, 0x0CA53, 0x0CA54, 0x0CA55, 0x0CA56, 0x0CA57, 0x0CA58, 0x0CA59, 0x0CA5A, 0x0CA5B, 0x0CA5C, 0x0CA5D, 0x0CA5E, 0x0CA5F, 0x0CA60, 0x0CA61, 0x0CA62, 0x0CA63, 0x0CA64, 0x0CA65, 0x0CA66, 0x0CA67, 0x0CA68, 0x0CA69, 0x0CA6A, 0x0CA6B, 0x0CA6C, 0x0CA6D, 0x0CA6E, 0x0CA6F, 0x0CA70, 0x0CA71, 0x0CA72, 0x0CA73, 0x0CA74, 0x0CA75, 0x0CA76, 0x0CA77, 0x0CA78, 0x0CA79, 0x0CA7A, 0x0CA7B, 0x0CA7C, 0x0CA7D, 0x0CA7E, 0x0CA7F, 0x0CA80, 0x0CA81, 0x0CA82, 0x0CA83, 0x0CA84, 0x0CA85, 0x0CA86, 0x0CA87, 0x0CA88, 0x0CA89, 0x0CA8A, 0x0CA8B, 0x0CA8C, 0x0CA8D, 0x0CA8E, 0x0CA8F, 0x0CA90, 0x0CA91, 0x0CA92, 0x0CA93, 0x0CA94, 0x0CA95, 0x0CA96, 0x0CA97, 0x0CA98, 0x0CA99, 0x0CA9A, 0x0CA9B, 0x0CA9C, 0x0CA9D, 0x0CA9E, 0x0CA9F, 0x0CAA0, 0x0CAA1, 0x0CAA2, 0x0CAA3, 0x0CAA4, 0x0CAA5, 0x0CAA6, 0x0CAA7, 0x0CAA8, 0x0CAA9, 0x0CAAA, 0x0CAAB, 0x0CAAC, 0x0CAAD, 0x0CAAE, 0x0CAAF, 0x0CAB0, 0x0CAB1, 0x0CAB2, 0x0CAB3, 0x0CAB4, 0x0CAB5, 0x0CAB6, 0x0CAB7, 0x0CAB8, 0x0CAB9, 0x0CABA, 0x0CABB, 0x0CABC, 0x0CABD, 0x0CABE, 0x0CABF, 0x0CAC0, 0x0CAC1, 0x0CAC2, 0x0CAC3, 0x0CAC4, 0x0CAC5, 0x0CAC6, 0x0CAC7, 0x0CAC8, 0x0CAC9, 0x0CACA, 0x0CACB, 0x0CACC, 0x0CACD, 0x0CACE, 0x0CACF, 0x0CAD0, 0x0CAD1, 0x0CAD2, 0x0CAD3, 0x0CAD4, 0x0CAD5, 0x0CAD6, 0x0CAD7, 0x0CAD8, 0x0CAD9, 0x0CADA, 0x0CADB, 0x0CADC, 0x0CADD, 0x0CADE, 0x0CADF, 0x0CAE0, 0x0CAE1, 0x0CAE2, 0x0CAE3, 0x0CAE4, 0x0CAE5, 0x0CAE6, 0x0CAE7, 0x0CAE8, 0x0CAE9, 0x0CAEA, 0x0CAEB, 0x0CAEC, 0x0CAED, 0x0CAEE, 0x0CAEF, 0x0CAF0, 0x0CAF1, 0x0CAF2, 0x0CAF3, 0x0CAF4, 0x0CAF5, 0x0CAF6, 0x0CAF7, 0x0CAF8, 0x0CAF9, 0x0CAFA, 0x0CAFB, 0x0CAFC, 0x0CAFD, 0x0CAFE, 0x0CAFF, 0x0CB00, 0x0CB01, 0x0CB02, 0x0CB03, 0x0CB04, 0x0CB05, 0x0CB06, 0x0CB07, 0x0CB08, 0x0CB09, 0x0CB0A, 0x0CB0B, 0x0CB0C, 0x0CB0D, 0x0CB0E, 0x0CB0F, 0x0CB10, 0x0CB11, 0x0CB12, 0x0CB13, 0x0CB14, 0x0CB15, 0x0CB16, 0x0CB17, 0x0CB18, 0x0CB19, 0x0CB1A, 0x0CB1B, 0x0CB1C, 0x0CB1D, 0x0CB1E, 0x0CB1F, 0x0CB20, 0x0CB21, 0x0CB22, 0x0CB23, 0x0CB24, 0x0CB25, 0x0CB26, 0x0CB27, 0x0CB28, 0x0CB29, 0x0CB2A, 0x0CB2B, 0x0CB2C, 0x0CB2D, 0x0CB2E, 0x0CB2F, 0x0CB30, 0x0CB31, 0x0CB32, 0x0CB33, 0x0CB34, 0x0CB35, 0x0CB36, 0x0CB37, 0x0CB38, 0x0CB39, 0x0CB3A, 0x0CB3B, 0x0CB3C, 0x0CB3D, 0x0CB3E, 0x0CB3F, 0x0CB40, 0x0CB41, 0x0CB42, 0x0CB43, 0x0CB44, 0x0CB45, 0x0CB46, 0x0CB47, 0x0CB48, 0x0CB49, 0x0CB4A, 0x0CB4B, 0x0CB4C, 0x0CB4D, 0x0CB4E, 0x0CB4F, 0x0CB50, 0x0CB51, 0x0CB52, 0x0CB53, 0x0CB54, 0x0CB55, 0x0CB56, 0x0CB57, 0x0CB58, 0x0CB59, 0x0CB5A, 0x0CB5B, 0x0CB5C, 0x0CB5D, 0x0CB5E, 0x0CB5F, 0x0CB60, 0x0CB61, 0x0CB62, 0x0CB63, 0x0CB64, 0x0CB65, 0x0CB66, 0x0CB67, 0x0CB68, 0x0CB69, 0x0CB6A, 0x0CB6B, 0x0CB6C, 0x0CB6D, 0x0CB6E, 0x0CB6F, 0x0CB70, 0x0CB71, 0x0CB72, 0x0CB73, 0x0CB74, 0x0CB75, 0x0CB76, 0x0CB77, 0x0CB78, 0x0CB79, 0x0CB7A, 0x0CB7B, 0x0CB7C, 0x0CB7D, 0x0CB7E, 0x0CB7F, 0x0CB80, 0x0CB81, 0x0CB82, 0x0CB83, 0x0CB84, 0x0CB85, 0x0CB86, 0x0CB87, 0x0CB88, 0x0CB89, 0x0CB8A, 0x0CB8B, 0x0CB8C, 0x0CB8D, 0x0CB8E, 0x0CB8F, 0x0CB90, 0x0CB91, 0x0CB92, 0x0CB93, 0x0CB94, 0x0CB95, 0x0CB96, 0x0CB97, 0x0CB98, 0x0CB99, 0x0CB9A, 0x0CB9B, 0x0CB9C, 0x0CB9D, 0x0CB9E, 0x0CB9F, 0x0CBA0, 0x0CBA1, 0x0CBA2, 0x0CBA3, 0x0CBA4, 0x0CBA5, 0x0CBA6, 0x0CBA7, 0x0CBA8, 0x0CBA9, 0x0CBAA, 0x0CBAB, 0x0CBAC, 0x0CBAD, 0x0CBAE, 0x0CBAF, 0x0CBB0, 0x0CBB1, 0x0CBB2, 0x0CBB3, 0x0CBB4, 0x0CBB5, 0x0CBB6, 0x0CBB7, 0x0CBB8, 0x0CBB9, 0x0CBBA, 0x0CBBB, 0x0CBBC, 0x0CBBD, 0x0CBBE, 0x0CBBF, 0x0CBC0, 0x0CBC1, 0x0CBC2, 0x0CBC3, 0x0CBC4, 0x0CBC5, 0x0CBC6, 0x0CBC7, 0x0CBC8, 0x0CBC9, 0x0CBCA, 0x0CBCB, 0x0CBCC, 0x0CBCD, 0x0CBCE, 0x0CBCF, 0x0CBD0, 0x0CBD1, 0x0CBD2, 0x0CBD3, 0x0CBD4, 0x0CBD5, 0x0CBD6, 0x0CBD7, 0x0CBD8, 0x0CBD9, 0x0CBDA, 0x0CBDB, 0x0CBDC, 0x0CBDD, 0x0CBDE, 0x0CBDF, 0x0CBE0, 0x0CBE1, 0x0CBE2, 0x0CBE3, 0x0CBE4, 0x0CBE5, 0x0CBE6, 0x0CBE7, 0x0CBE8, 0x0CBE9, 0x0CBEA, 0x0CBEB, 0x0CBEC, 0x0CBED, 0x0CBEE, 0x0CBEF, 0x0CBF0, 0x0CBF1, 0x0CBF2, 0x0CBF3, 0x0CBF4, 0x0CBF5, 0x0CBF6, 0x0CBF7, 0x0CBF8, 0x0CBF9, 0x0CBFA, 0x0CBFB, 0x0CBFC, 0x0CBFD, 0x0CBFE, 0x0CBFF, 0x0CC00, 0x0CC01, 0x0CC02, 0x0CC03, 0x0CC04, 0x0CC05, 0x0CC06, 0x0CC07, 0x0CC08, 0x0CC09, 0x0CC0A, 0x0CC0B, 0x0CC0C, 0x0CC0D, 0x0CC0E, 0x0CC0F, 0x0CC10, 0x0CC11, 0x0CC12, 0x0CC13, 0x0CC14, 0x0CC15, 0x0CC16, 0x0CC17, 0x0CC18, 0x0CC19, 0x0CC1A, 0x0CC1B, 0x0CC1C, 0x0CC1D, 0x0CC1E, 0x0CC1F, 0x0CC20, 0x0CC21, 0x0CC22, 0x0CC23, 0x0CC24, 0x0CC25, 0x0CC26, 0x0CC27, 0x0CC28, 0x0CC29, 0x0CC2A, 0x0CC2B, 0x0CC2C, 0x0CC2D, 0x0CC2E, 0x0CC2F, 0x0CC30, 0x0CC31, 0x0CC32, 0x0CC33, 0x0CC34, 0x0CC35, 0x0CC36, 0x0CC37, 0x0CC38, 0x0CC39, 0x0CC3A, 0x0CC3B, 0x0CC3C, 0x0CC3D, 0x0CC3E, 0x0CC3F, 0x0CC40, 0x0CC41, 0x0CC42, 0x0CC43, 0x0CC44, 0x0CC45, 0x0CC46, 0x0CC47, 0x0CC48, 0x0CC49, 0x0CC4A, 0x0CC4B, 0x0CC4C, 0x0CC4D, 0x0CC4E, 0x0CC4F, 0x0CC50, 0x0CC51, 0x0CC52, 0x0CC53, 0x0CC54, 0x0CC55, 0x0CC56, 0x0CC57, 0x0CC58, 0x0CC59, 0x0CC5A, 0x0CC5B, 0x0CC5C, 0x0CC5D, 0x0CC5E, 0x0CC5F, 0x0CC60, 0x0CC61, 0x0CC62, 0x0CC63, 0x0CC64, 0x0CC65, 0x0CC66, 0x0CC67, 0x0CC68, 0x0CC69, 0x0CC6A, 0x0CC6B, 0x0CC6C, 0x0CC6D, 0x0CC6E, 0x0CC6F, 0x0CC70, 0x0CC71, 0x0CC72, 0x0CC73, 0x0CC74, 0x0CC75, 0x0CC76, 0x0CC77, 0x0CC78, 0x0CC79, 0x0CC7A, 0x0CC7B, 0x0CC7C, 0x0CC7D, 0x0CC7E, 0x0CC7F, 0x0CC80, 0x0CC81, 0x0CC82, 0x0CC83, 0x0CC84, 0x0CC85, 0x0CC86, 0x0CC87, 0x0CC88, 0x0CC89, 0x0CC8A, 0x0CC8B, 0x0CC8C, 0x0CC8D, 0x0CC8E, 0x0CC8F, 0x0CC90, 0x0CC91, 0x0CC92, 0x0CC93, 0x0CC94, 0x0CC95, 0x0CC96, 0x0CC97, 0x0CC98, 0x0CC99, 0x0CC9A, 0x0CC9B, 0x0CC9C, 0x0CC9D, 0x0CC9E, 0x0CC9F, 0x0CCA0, 0x0CCA1, 0x0CCA2, 0x0CCA3, 0x0CCA4, 0x0CCA5, 0x0CCA6, 0x0CCA7, 0x0CCA8, 0x0CCA9, 0x0CCAA, 0x0CCAB, 0x0CCAC, 0x0CCAD, 0x0CCAE, 0x0CCAF, 0x0CCB0, 0x0CCB1, 0x0CCB2, 0x0CCB3, 0x0CCB4, 0x0CCB5, 0x0CCB6, 0x0CCB7, 0x0CCB8, 0x0CCB9, 0x0CCBA, 0x0CCBB, 0x0CCBC, 0x0CCBD, 0x0CCBE, 0x0CCBF, 0x0CCC0, 0x0CCC1, 0x0CCC2, 0x0CCC3, 0x0CCC4, 0x0CCC5, 0x0CCC6, 0x0CCC7, 0x0CCC8, 0x0CCC9, 0x0CCCA, 0x0CCCB, 0x0CCCC, 0x0CCCD, 0x0CCCE, 0x0CCCF, 0x0CCD0, 0x0CCD1, 0x0CCD2, 0x0CCD3, 0x0CCD4, 0x0CCD5, 0x0CCD6, 0x0CCD7, 0x0CCD8, 0x0CCD9, 0x0CCDA, 0x0CCDB, 0x0CCDC, 0x0CCDD, 0x0CCDE, 0x0CCDF, 0x0CCE0, 0x0CCE1, 0x0CCE2, 0x0CCE3, 0x0CCE4, 0x0CCE5, 0x0CCE6, 0x0CCE7, 0x0CCE8, 0x0CCE9, 0x0CCEA, 0x0CCEB, 0x0CCEC, 0x0CCED, 0x0CCEE, 0x0CCEF, 0x0CCF0, 0x0CCF1, 0x0CCF2, 0x0CCF3, 0x0CCF4, 0x0CCF5, 0x0CCF6, 0x0CCF7, 0x0CCF8, 0x0CCF9, 0x0CCFA, 0x0CCFB, 0x0CCFC, 0x0CCFD, 0x0CCFE, 0x0CCFF, 0x0CD00, 0x0CD01, 0x0CD02, 0x0CD03, 0x0CD04, 0x0CD05, 0x0CD06, 0x0CD07, 0x0CD08, 0x0CD09, 0x0CD0A, 0x0CD0B, 0x0CD0C, 0x0CD0D, 0x0CD0E, 0x0CD0F, 0x0CD10, 0x0CD11, 0x0CD12, 0x0CD13, 0x0CD14, 0x0CD15, 0x0CD16, 0x0CD17, 0x0CD18, 0x0CD19, 0x0CD1A, 0x0CD1B, 0x0CD1C, 0x0CD1D, 0x0CD1E, 0x0CD1F, 0x0CD20, 0x0CD21, 0x0CD22, 0x0CD23, 0x0CD24, 0x0CD25, 0x0CD26, 0x0CD27, 0x0CD28, 0x0CD29, 0x0CD2A, 0x0CD2B, 0x0CD2C, 0x0CD2D, 0x0CD2E, 0x0CD2F, 0x0CD30, 0x0CD31, 0x0CD32, 0x0CD33, 0x0CD34, 0x0CD35, 0x0CD36, 0x0CD37, 0x0CD38, 0x0CD39, 0x0CD3A, 0x0CD3B, 0x0CD3C, 0x0CD3D, 0x0CD3E, 0x0CD3F, 0x0CD40, 0x0CD41, 0x0CD42, 0x0CD43, 0x0CD44, 0x0CD45, 0x0CD46, 0x0CD47, 0x0CD48, 0x0CD49, 0x0CD4A, 0x0CD4B, 0x0CD4C, 0x0CD4D, 0x0CD4E, 0x0CD4F, 0x0CD50, 0x0CD51, 0x0CD52, 0x0CD53, 0x0CD54, 0x0CD55, 0x0CD56, 0x0CD57, 0x0CD58, 0x0CD59, 0x0CD5A, 0x0CD5B, 0x0CD5C, 0x0CD5D, 0x0CD5E, 0x0CD5F, 0x0CD60, 0x0CD61, 0x0CD62, 0x0CD63, 0x0CD64, 0x0CD65, 0x0CD66, 0x0CD67, 0x0CD68, 0x0CD69, 0x0CD6A, 0x0CD6B, 0x0CD6C, 0x0CD6D, 0x0CD6E, 0x0CD6F, 0x0CD70, 0x0CD71, 0x0CD72, 0x0CD73, 0x0CD74, 0x0CD75, 0x0CD76, 0x0CD77, 0x0CD78, 0x0CD79, 0x0CD7A, 0x0CD7B, 0x0CD7C, 0x0CD7D, 0x0CD7E, 0x0CD7F, 0x0CD80, 0x0CD81, 0x0CD82, 0x0CD83, 0x0CD84, 0x0CD85, 0x0CD86, 0x0CD87, 0x0CD88, 0x0CD89, 0x0CD8A, 0x0CD8B, 0x0CD8C, 0x0CD8D, 0x0CD8E, 0x0CD8F, 0x0CD90, 0x0CD91, 0x0CD92, 0x0CD93, 0x0CD94, 0x0CD95, 0x0CD96, 0x0CD97, 0x0CD98, 0x0CD99, 0x0CD9A, 0x0CD9B, 0x0CD9C, 0x0CD9D, 0x0CD9E, 0x0CD9F, 0x0CDA0, 0x0CDA1, 0x0CDA2, 0x0CDA3, 0x0CDA4, 0x0CDA5, 0x0CDA6, 0x0CDA7, 0x0CDA8, 0x0CDA9, 0x0CDAA, 0x0CDAB, 0x0CDAC, 0x0CDAD, 0x0CDAE, 0x0CDAF, 0x0CDB0, 0x0CDB1, 0x0CDB2, 0x0CDB3, 0x0CDB4, 0x0CDB5, 0x0CDB6, 0x0CDB7, 0x0CDB8, 0x0CDB9, 0x0CDBA, 0x0CDBB, 0x0CDBC, 0x0CDBD, 0x0CDBE, 0x0CDBF, 0x0CDC0, 0x0CDC1, 0x0CDC2, 0x0CDC3, 0x0CDC4, 0x0CDC5, 0x0CDC6, 0x0CDC7, 0x0CDC8, 0x0CDC9, 0x0CDCA, 0x0CDCB, 0x0CDCC, 0x0CDCD, 0x0CDCE, 0x0CDCF, 0x0CDD0, 0x0CDD1, 0x0CDD2, 0x0CDD3, 0x0CDD4, 0x0CDD5, 0x0CDD6, 0x0CDD7, 0x0CDD8, 0x0CDD9, 0x0CDDA, 0x0CDDB, 0x0CDDC, 0x0CDDD, 0x0CDDE, 0x0CDDF, 0x0CDE0, 0x0CDE1, 0x0CDE2, 0x0CDE3, 0x0CDE4, 0x0CDE5, 0x0CDE6, 0x0CDE7, 0x0CDE8, 0x0CDE9, 0x0CDEA, 0x0CDEB, 0x0CDEC, 0x0CDED, 0x0CDEE, 0x0CDEF, 0x0CDF0, 0x0CDF1, 0x0CDF2, 0x0CDF3, 0x0CDF4, 0x0CDF5, 0x0CDF6, 0x0CDF7, 0x0CDF8, 0x0CDF9, 0x0CDFA, 0x0CDFB, 0x0CDFC, 0x0CDFD, 0x0CDFE, 0x0CDFF, 0x0CE00, 0x0CE01, 0x0CE02, 0x0CE03, 0x0CE04, 0x0CE05, 0x0CE06, 0x0CE07, 0x0CE08, 0x0CE09, 0x0CE0A, 0x0CE0B, 0x0CE0C, 0x0CE0D, 0x0CE0E, 0x0CE0F, 0x0CE10, 0x0CE11, 0x0CE12, 0x0CE13, 0x0CE14, 0x0CE15, 0x0CE16, 0x0CE17, 0x0CE18, 0x0CE19, 0x0CE1A, 0x0CE1B, 0x0CE1C, 0x0CE1D, 0x0CE1E, 0x0CE1F, 0x0CE20, 0x0CE21, 0x0CE22, 0x0CE23, 0x0CE24, 0x0CE25, 0x0CE26, 0x0CE27, 0x0CE28, 0x0CE29, 0x0CE2A, 0x0CE2B, 0x0CE2C, 0x0CE2D, 0x0CE2E, 0x0CE2F, 0x0CE30, 0x0CE31, 0x0CE32, 0x0CE33, 0x0CE34, 0x0CE35, 0x0CE36, 0x0CE37, 0x0CE38, 0x0CE39, 0x0CE3A, 0x0CE3B, 0x0CE3C, 0x0CE3D, 0x0CE3E, 0x0CE3F, 0x0CE40, 0x0CE41, 0x0CE42, 0x0CE43, 0x0CE44, 0x0CE45, 0x0CE46, 0x0CE47, 0x0CE48, 0x0CE49, 0x0CE4A, 0x0CE4B, 0x0CE4C, 0x0CE4D, 0x0CE4E, 0x0CE4F, 0x0CE50, 0x0CE51, 0x0CE52, 0x0CE53, 0x0CE54, 0x0CE55, 0x0CE56, 0x0CE57, 0x0CE58, 0x0CE59, 0x0CE5A, 0x0CE5B, 0x0CE5C, 0x0CE5D, 0x0CE5E, 0x0CE5F, 0x0CE60, 0x0CE61, 0x0CE62, 0x0CE63, 0x0CE64, 0x0CE65, 0x0CE66, 0x0CE67, 0x0CE68, 0x0CE69, 0x0CE6A, 0x0CE6B, 0x0CE6C, 0x0CE6D, 0x0CE6E, 0x0CE6F, 0x0CE70, 0x0CE71, 0x0CE72, 0x0CE73, 0x0CE74, 0x0CE75, 0x0CE76, 0x0CE77, 0x0CE78, 0x0CE79, 0x0CE7A, 0x0CE7B, 0x0CE7C, 0x0CE7D, 0x0CE7E, 0x0CE7F, 0x0CE80, 0x0CE81, 0x0CE82, 0x0CE83, 0x0CE84, 0x0CE85, 0x0CE86, 0x0CE87, 0x0CE88, 0x0CE89, 0x0CE8A, 0x0CE8B, 0x0CE8C, 0x0CE8D, 0x0CE8E, 0x0CE8F, 0x0CE90, 0x0CE91, 0x0CE92, 0x0CE93, 0x0CE94, 0x0CE95, 0x0CE96, 0x0CE97, 0x0CE98, 0x0CE99, 0x0CE9A, 0x0CE9B, 0x0CE9C, 0x0CE9D, 0x0CE9E, 0x0CE9F, 0x0CEA0, 0x0CEA1, 0x0CEA2, 0x0CEA3, 0x0CEA4, 0x0CEA5, 0x0CEA6, 0x0CEA7, 0x0CEA8, 0x0CEA9, 0x0CEAA, 0x0CEAB, 0x0CEAC, 0x0CEAD, 0x0CEAE, 0x0CEAF, 0x0CEB0, 0x0CEB1, 0x0CEB2, 0x0CEB3, 0x0CEB4, 0x0CEB5, 0x0CEB6, 0x0CEB7, 0x0CEB8, 0x0CEB9, 0x0CEBA, 0x0CEBB, 0x0CEBC, 0x0CEBD, 0x0CEBE, 0x0CEBF, 0x0CEC0, 0x0CEC1, 0x0CEC2, 0x0CEC3, 0x0CEC4, 0x0CEC5, 0x0CEC6, 0x0CEC7, 0x0CEC8, 0x0CEC9, 0x0CECA, 0x0CECB, 0x0CECC, 0x0CECD, 0x0CECE, 0x0CECF, 0x0CED0, 0x0CED1, 0x0CED2, 0x0CED3, 0x0CED4, 0x0CED5, 0x0CED6, 0x0CED7, 0x0CED8, 0x0CED9, 0x0CEDA, 0x0CEDB, 0x0CEDC, 0x0CEDD, 0x0CEDE, 0x0CEDF, 0x0CEE0, 0x0CEE1, 0x0CEE2, 0x0CEE3, 0x0CEE4, 0x0CEE5, 0x0CEE6, 0x0CEE7, 0x0CEE8, 0x0CEE9, 0x0CEEA, 0x0CEEB, 0x0CEEC, 0x0CEED, 0x0CEEE, 0x0CEEF, 0x0CEF0, 0x0CEF1, 0x0CEF2, 0x0CEF3, 0x0CEF4, 0x0CEF5, 0x0CEF6, 0x0CEF7, 0x0CEF8, 0x0CEF9, 0x0CEFA, 0x0CEFB, 0x0CEFC, 0x0CEFD, 0x0CEFE, 0x0CEFF, 0x0CF00, 0x0CF01, 0x0CF02, 0x0CF03, 0x0CF04, 0x0CF05, 0x0CF06, 0x0CF07, 0x0CF08, 0x0CF09, 0x0CF0A, 0x0CF0B, 0x0CF0C, 0x0CF0D, 0x0CF0E, 0x0CF0F, 0x0CF10, 0x0CF11, 0x0CF12, 0x0CF13, 0x0CF14, 0x0CF15, 0x0CF16, 0x0CF17, 0x0CF18, 0x0CF19, 0x0CF1A, 0x0CF1B, 0x0CF1C, 0x0CF1D, 0x0CF1E, 0x0CF1F, 0x0CF20, 0x0CF21, 0x0CF22, 0x0CF23, 0x0CF24, 0x0CF25, 0x0CF26, 0x0CF27, 0x0CF28, 0x0CF29, 0x0CF2A, 0x0CF2B, 0x0CF2C, 0x0CF2D, 0x0CF2E, 0x0CF2F, 0x0CF30, 0x0CF31, 0x0CF32, 0x0CF33, 0x0CF34, 0x0CF35, 0x0CF36, 0x0CF37, 0x0CF38, 0x0CF39, 0x0CF3A, 0x0CF3B, 0x0CF3C, 0x0CF3D, 0x0CF3E, 0x0CF3F, 0x0CF40, 0x0CF41, 0x0CF42, 0x0CF43, 0x0CF44, 0x0CF45, 0x0CF46, 0x0CF47, 0x0CF48, 0x0CF49, 0x0CF4A, 0x0CF4B, 0x0CF4C, 0x0CF4D, 0x0CF4E, 0x0CF4F, 0x0CF50, 0x0CF51, 0x0CF52, 0x0CF53, 0x0CF54, 0x0CF55, 0x0CF56, 0x0CF57, 0x0CF58, 0x0CF59, 0x0CF5A, 0x0CF5B, 0x0CF5C, 0x0CF5D, 0x0CF5E, 0x0CF5F, 0x0CF60, 0x0CF61, 0x0CF62, 0x0CF63, 0x0CF64, 0x0CF65, 0x0CF66, 0x0CF67, 0x0CF68, 0x0CF69, 0x0CF6A, 0x0CF6B, 0x0CF6C, 0x0CF6D, 0x0CF6E, 0x0CF6F, 0x0CF70, 0x0CF71, 0x0CF72, 0x0CF73, 0x0CF74, 0x0CF75, 0x0CF76, 0x0CF77, 0x0CF78, 0x0CF79, 0x0CF7A, 0x0CF7B, 0x0CF7C, 0x0CF7D, 0x0CF7E, 0x0CF7F, 0x0CF80, 0x0CF81, 0x0CF82, 0x0CF83, 0x0CF84, 0x0CF85, 0x0CF86, 0x0CF87, 0x0CF88, 0x0CF89, 0x0CF8A, 0x0CF8B, 0x0CF8C, 0x0CF8D, 0x0CF8E, 0x0CF8F, 0x0CF90, 0x0CF91, 0x0CF92, 0x0CF93, 0x0CF94, 0x0CF95, 0x0CF96, 0x0CF97, 0x0CF98, 0x0CF99, 0x0CF9A, 0x0CF9B, 0x0CF9C, 0x0CF9D, 0x0CF9E, 0x0CF9F, 0x0CFA0, 0x0CFA1, 0x0CFA2, 0x0CFA3, 0x0CFA4, 0x0CFA5, 0x0CFA6, 0x0CFA7, 0x0CFA8, 0x0CFA9, 0x0CFAA, 0x0CFAB, 0x0CFAC, 0x0CFAD, 0x0CFAE, 0x0CFAF, 0x0CFB0, 0x0CFB1, 0x0CFB2, 0x0CFB3, 0x0CFB4, 0x0CFB5, 0x0CFB6, 0x0CFB7, 0x0CFB8, 0x0CFB9, 0x0CFBA, 0x0CFBB, 0x0CFBC, 0x0CFBD, 0x0CFBE, 0x0CFBF, 0x0CFC0, 0x0CFC1, 0x0CFC2, 0x0CFC3, 0x0CFC4, 0x0CFC5, 0x0CFC6, 0x0CFC7, 0x0CFC8, 0x0CFC9, 0x0CFCA, 0x0CFCB, 0x0CFCC, 0x0CFCD, 0x0CFCE, 0x0CFCF, 0x0CFD0, 0x0CFD1, 0x0CFD2, 0x0CFD3, 0x0CFD4, 0x0CFD5, 0x0CFD6, 0x0CFD7, 0x0CFD8, 0x0CFD9, 0x0CFDA, 0x0CFDB, 0x0CFDC, 0x0CFDD, 0x0CFDE, 0x0CFDF, 0x0CFE0, 0x0CFE1, 0x0CFE2, 0x0CFE3, 0x0CFE4, 0x0CFE5, 0x0CFE6, 0x0CFE7, 0x0CFE8, 0x0CFE9, 0x0CFEA, 0x0CFEB, 0x0CFEC, 0x0CFED, 0x0CFEE, 0x0CFEF, 0x0CFF0, 0x0CFF1, 0x0CFF2, 0x0CFF3, 0x0CFF4, 0x0CFF5, 0x0CFF6, 0x0CFF7, 0x0CFF8, 0x0CFF9, 0x0CFFA, 0x0CFFB, 0x0CFFC, 0x0CFFD, 0x0CFFE, 0x0CFFF, 0x0D000, 0x0D001, 0x0D002, 0x0D003, 0x0D004, 0x0D005, 0x0D006, 0x0D007, 0x0D008, 0x0D009, 0x0D00A, 0x0D00B, 0x0D00C, 0x0D00D, 0x0D00E, 0x0D00F, 0x0D010, 0x0D011, 0x0D012, 0x0D013, 0x0D014, 0x0D015, 0x0D016, 0x0D017, 0x0D018, 0x0D019, 0x0D01A, 0x0D01B, 0x0D01C, 0x0D01D, 0x0D01E, 0x0D01F, 0x0D020, 0x0D021, 0x0D022, 0x0D023, 0x0D024, 0x0D025, 0x0D026, 0x0D027, 0x0D028, 0x0D029, 0x0D02A, 0x0D02B, 0x0D02C, 0x0D02D, 0x0D02E, 0x0D02F, 0x0D030, 0x0D031, 0x0D032, 0x0D033, 0x0D034, 0x0D035, 0x0D036, 0x0D037, 0x0D038, 0x0D039, 0x0D03A, 0x0D03B, 0x0D03C, 0x0D03D, 0x0D03E, 0x0D03F, 0x0D040, 0x0D041, 0x0D042, 0x0D043, 0x0D044, 0x0D045, 0x0D046, 0x0D047, 0x0D048, 0x0D049, 0x0D04A, 0x0D04B, 0x0D04C, 0x0D04D, 0x0D04E, 0x0D04F, 0x0D050, 0x0D051, 0x0D052, 0x0D053, 0x0D054, 0x0D055, 0x0D056, 0x0D057, 0x0D058, 0x0D059, 0x0D05A, 0x0D05B, 0x0D05C, 0x0D05D, 0x0D05E, 0x0D05F, 0x0D060, 0x0D061, 0x0D062, 0x0D063, 0x0D064, 0x0D065, 0x0D066, 0x0D067, 0x0D068, 0x0D069, 0x0D06A, 0x0D06B, 0x0D06C, 0x0D06D, 0x0D06E, 0x0D06F, 0x0D070, 0x0D071, 0x0D072, 0x0D073, 0x0D074, 0x0D075, 0x0D076, 0x0D077, 0x0D078, 0x0D079, 0x0D07A, 0x0D07B, 0x0D07C, 0x0D07D, 0x0D07E, 0x0D07F, 0x0D080, 0x0D081, 0x0D082, 0x0D083, 0x0D084, 0x0D085, 0x0D086, 0x0D087, 0x0D088, 0x0D089, 0x0D08A, 0x0D08B, 0x0D08C, 0x0D08D, 0x0D08E, 0x0D08F, 0x0D090, 0x0D091, 0x0D092, 0x0D093, 0x0D094, 0x0D095, 0x0D096, 0x0D097, 0x0D098, 0x0D099, 0x0D09A, 0x0D09B, 0x0D09C, 0x0D09D, 0x0D09E, 0x0D09F, 0x0D0A0, 0x0D0A1, 0x0D0A2, 0x0D0A3, 0x0D0A4, 0x0D0A5, 0x0D0A6, 0x0D0A7, 0x0D0A8, 0x0D0A9, 0x0D0AA, 0x0D0AB, 0x0D0AC, 0x0D0AD, 0x0D0AE, 0x0D0AF, 0x0D0B0, 0x0D0B1, 0x0D0B2, 0x0D0B3, 0x0D0B4, 0x0D0B5, 0x0D0B6, 0x0D0B7, 0x0D0B8, 0x0D0B9, 0x0D0BA, 0x0D0BB, 0x0D0BC, 0x0D0BD, 0x0D0BE, 0x0D0BF, 0x0D0C0, 0x0D0C1, 0x0D0C2, 0x0D0C3, 0x0D0C4, 0x0D0C5, 0x0D0C6, 0x0D0C7, 0x0D0C8, 0x0D0C9, 0x0D0CA, 0x0D0CB, 0x0D0CC, 0x0D0CD, 0x0D0CE, 0x0D0CF, 0x0D0D0, 0x0D0D1, 0x0D0D2, 0x0D0D3, 0x0D0D4, 0x0D0D5, 0x0D0D6, 0x0D0D7, 0x0D0D8, 0x0D0D9, 0x0D0DA, 0x0D0DB, 0x0D0DC, 0x0D0DD, 0x0D0DE, 0x0D0DF, 0x0D0E0, 0x0D0E1, 0x0D0E2, 0x0D0E3, 0x0D0E4, 0x0D0E5, 0x0D0E6, 0x0D0E7, 0x0D0E8, 0x0D0E9, 0x0D0EA, 0x0D0EB, 0x0D0EC, 0x0D0ED, 0x0D0EE, 0x0D0EF, 0x0D0F0, 0x0D0F1, 0x0D0F2, 0x0D0F3, 0x0D0F4, 0x0D0F5, 0x0D0F6, 0x0D0F7, 0x0D0F8, 0x0D0F9, 0x0D0FA, 0x0D0FB, 0x0D0FC, 0x0D0FD, 0x0D0FE, 0x0D0FF, 0x0D100, 0x0D101, 0x0D102, 0x0D103, 0x0D104, 0x0D105, 0x0D106, 0x0D107, 0x0D108, 0x0D109, 0x0D10A, 0x0D10B, 0x0D10C, 0x0D10D, 0x0D10E, 0x0D10F, 0x0D110, 0x0D111, 0x0D112, 0x0D113, 0x0D114, 0x0D115, 0x0D116, 0x0D117, 0x0D118, 0x0D119, 0x0D11A, 0x0D11B, 0x0D11C, 0x0D11D, 0x0D11E, 0x0D11F, 0x0D120, 0x0D121, 0x0D122, 0x0D123, 0x0D124, 0x0D125, 0x0D126, 0x0D127, 0x0D128, 0x0D129, 0x0D12A, 0x0D12B, 0x0D12C, 0x0D12D, 0x0D12E, 0x0D12F, 0x0D130, 0x0D131, 0x0D132, 0x0D133, 0x0D134, 0x0D135, 0x0D136, 0x0D137, 0x0D138, 0x0D139, 0x0D13A, 0x0D13B, 0x0D13C, 0x0D13D, 0x0D13E, 0x0D13F, 0x0D140, 0x0D141, 0x0D142, 0x0D143, 0x0D144, 0x0D145, 0x0D146, 0x0D147, 0x0D148, 0x0D149, 0x0D14A, 0x0D14B, 0x0D14C, 0x0D14D, 0x0D14E, 0x0D14F, 0x0D150, 0x0D151, 0x0D152, 0x0D153, 0x0D154, 0x0D155, 0x0D156, 0x0D157, 0x0D158, 0x0D159, 0x0D15A, 0x0D15B, 0x0D15C, 0x0D15D, 0x0D15E, 0x0D15F, 0x0D160, 0x0D161, 0x0D162, 0x0D163, 0x0D164, 0x0D165, 0x0D166, 0x0D167, 0x0D168, 0x0D169, 0x0D16A, 0x0D16B, 0x0D16C, 0x0D16D, 0x0D16E, 0x0D16F, 0x0D170, 0x0D171, 0x0D172, 0x0D173, 0x0D174, 0x0D175, 0x0D176, 0x0D177, 0x0D178, 0x0D179, 0x0D17A, 0x0D17B, 0x0D17C, 0x0D17D, 0x0D17E, 0x0D17F, 0x0D180, 0x0D181, 0x0D182, 0x0D183, 0x0D184, 0x0D185, 0x0D186, 0x0D187, 0x0D188, 0x0D189, 0x0D18A, 0x0D18B, 0x0D18C, 0x0D18D, 0x0D18E, 0x0D18F, 0x0D190, 0x0D191, 0x0D192, 0x0D193, 0x0D194, 0x0D195, 0x0D196, 0x0D197, 0x0D198, 0x0D199, 0x0D19A, 0x0D19B, 0x0D19C, 0x0D19D, 0x0D19E, 0x0D19F, 0x0D1A0, 0x0D1A1, 0x0D1A2, 0x0D1A3, 0x0D1A4, 0x0D1A5, 0x0D1A6, 0x0D1A7, 0x0D1A8, 0x0D1A9, 0x0D1AA, 0x0D1AB, 0x0D1AC, 0x0D1AD, 0x0D1AE, 0x0D1AF, 0x0D1B0, 0x0D1B1, 0x0D1B2, 0x0D1B3, 0x0D1B4, 0x0D1B5, 0x0D1B6, 0x0D1B7, 0x0D1B8, 0x0D1B9, 0x0D1BA, 0x0D1BB, 0x0D1BC, 0x0D1BD, 0x0D1BE, 0x0D1BF, 0x0D1C0, 0x0D1C1, 0x0D1C2, 0x0D1C3, 0x0D1C4, 0x0D1C5, 0x0D1C6, 0x0D1C7, 0x0D1C8, 0x0D1C9, 0x0D1CA, 0x0D1CB, 0x0D1CC, 0x0D1CD, 0x0D1CE, 0x0D1CF, 0x0D1D0, 0x0D1D1, 0x0D1D2, 0x0D1D3, 0x0D1D4, 0x0D1D5, 0x0D1D6, 0x0D1D7, 0x0D1D8, 0x0D1D9, 0x0D1DA, 0x0D1DB, 0x0D1DC, 0x0D1DD, 0x0D1DE, 0x0D1DF, 0x0D1E0, 0x0D1E1, 0x0D1E2, 0x0D1E3, 0x0D1E4, 0x0D1E5, 0x0D1E6, 0x0D1E7, 0x0D1E8, 0x0D1E9, 0x0D1EA, 0x0D1EB, 0x0D1EC, 0x0D1ED, 0x0D1EE, 0x0D1EF, 0x0D1F0, 0x0D1F1, 0x0D1F2, 0x0D1F3, 0x0D1F4, 0x0D1F5, 0x0D1F6, 0x0D1F7, 0x0D1F8, 0x0D1F9, 0x0D1FA, 0x0D1FB, 0x0D1FC, 0x0D1FD, 0x0D1FE, 0x0D1FF, 0x0D200, 0x0D201, 0x0D202, 0x0D203, 0x0D204, 0x0D205, 0x0D206, 0x0D207, 0x0D208, 0x0D209, 0x0D20A, 0x0D20B, 0x0D20C, 0x0D20D, 0x0D20E, 0x0D20F, 0x0D210, 0x0D211, 0x0D212, 0x0D213, 0x0D214, 0x0D215, 0x0D216, 0x0D217, 0x0D218, 0x0D219, 0x0D21A, 0x0D21B, 0x0D21C, 0x0D21D, 0x0D21E, 0x0D21F, 0x0D220, 0x0D221, 0x0D222, 0x0D223, 0x0D224, 0x0D225, 0x0D226, 0x0D227, 0x0D228, 0x0D229, 0x0D22A, 0x0D22B, 0x0D22C, 0x0D22D, 0x0D22E, 0x0D22F, 0x0D230, 0x0D231, 0x0D232, 0x0D233, 0x0D234, 0x0D235, 0x0D236, 0x0D237, 0x0D238, 0x0D239, 0x0D23A, 0x0D23B, 0x0D23C, 0x0D23D, 0x0D23E, 0x0D23F, 0x0D240, 0x0D241, 0x0D242, 0x0D243, 0x0D244, 0x0D245, 0x0D246, 0x0D247, 0x0D248, 0x0D249, 0x0D24A, 0x0D24B, 0x0D24C, 0x0D24D, 0x0D24E, 0x0D24F, 0x0D250, 0x0D251, 0x0D252, 0x0D253, 0x0D254, 0x0D255, 0x0D256, 0x0D257, 0x0D258, 0x0D259, 0x0D25A, 0x0D25B, 0x0D25C, 0x0D25D, 0x0D25E, 0x0D25F, 0x0D260, 0x0D261, 0x0D262, 0x0D263, 0x0D264, 0x0D265, 0x0D266, 0x0D267, 0x0D268, 0x0D269, 0x0D26A, 0x0D26B, 0x0D26C, 0x0D26D, 0x0D26E, 0x0D26F, 0x0D270, 0x0D271, 0x0D272, 0x0D273, 0x0D274, 0x0D275, 0x0D276, 0x0D277, 0x0D278, 0x0D279, 0x0D27A, 0x0D27B, 0x0D27C, 0x0D27D, 0x0D27E, 0x0D27F, 0x0D280, 0x0D281, 0x0D282, 0x0D283, 0x0D284, 0x0D285, 0x0D286, 0x0D287, 0x0D288, 0x0D289, 0x0D28A, 0x0D28B, 0x0D28C, 0x0D28D, 0x0D28E, 0x0D28F, 0x0D290, 0x0D291, 0x0D292, 0x0D293, 0x0D294, 0x0D295, 0x0D296, 0x0D297, 0x0D298, 0x0D299, 0x0D29A, 0x0D29B, 0x0D29C, 0x0D29D, 0x0D29E, 0x0D29F, 0x0D2A0, 0x0D2A1, 0x0D2A2, 0x0D2A3, 0x0D2A4, 0x0D2A5, 0x0D2A6, 0x0D2A7, 0x0D2A8, 0x0D2A9, 0x0D2AA, 0x0D2AB, 0x0D2AC, 0x0D2AD, 0x0D2AE, 0x0D2AF, 0x0D2B0, 0x0D2B1, 0x0D2B2, 0x0D2B3, 0x0D2B4, 0x0D2B5, 0x0D2B6, 0x0D2B7, 0x0D2B8, 0x0D2B9, 0x0D2BA, 0x0D2BB, 0x0D2BC, 0x0D2BD, 0x0D2BE, 0x0D2BF, 0x0D2C0, 0x0D2C1, 0x0D2C2, 0x0D2C3, 0x0D2C4, 0x0D2C5, 0x0D2C6, 0x0D2C7, 0x0D2C8, 0x0D2C9, 0x0D2CA, 0x0D2CB, 0x0D2CC, 0x0D2CD, 0x0D2CE, 0x0D2CF, 0x0D2D0, 0x0D2D1, 0x0D2D2, 0x0D2D3, 0x0D2D4, 0x0D2D5, 0x0D2D6, 0x0D2D7, 0x0D2D8, 0x0D2D9, 0x0D2DA, 0x0D2DB, 0x0D2DC, 0x0D2DD, 0x0D2DE, 0x0D2DF, 0x0D2E0, 0x0D2E1, 0x0D2E2, 0x0D2E3, 0x0D2E4, 0x0D2E5, 0x0D2E6, 0x0D2E7, 0x0D2E8, 0x0D2E9, 0x0D2EA, 0x0D2EB, 0x0D2EC, 0x0D2ED, 0x0D2EE, 0x0D2EF, 0x0D2F0, 0x0D2F1, 0x0D2F2, 0x0D2F3, 0x0D2F4, 0x0D2F5, 0x0D2F6, 0x0D2F7, 0x0D2F8, 0x0D2F9, 0x0D2FA, 0x0D2FB, 0x0D2FC, 0x0D2FD, 0x0D2FE, 0x0D2FF, 0x0D300, 0x0D301, 0x0D302, 0x0D303, 0x0D304, 0x0D305, 0x0D306, 0x0D307, 0x0D308, 0x0D309, 0x0D30A, 0x0D30B, 0x0D30C, 0x0D30D, 0x0D30E, 0x0D30F, 0x0D310, 0x0D311, 0x0D312, 0x0D313, 0x0D314, 0x0D315, 0x0D316, 0x0D317, 0x0D318, 0x0D319, 0x0D31A, 0x0D31B, 0x0D31C, 0x0D31D, 0x0D31E, 0x0D31F, 0x0D320, 0x0D321, 0x0D322, 0x0D323, 0x0D324, 0x0D325, 0x0D326, 0x0D327, 0x0D328, 0x0D329, 0x0D32A, 0x0D32B, 0x0D32C, 0x0D32D, 0x0D32E, 0x0D32F, 0x0D330, 0x0D331, 0x0D332, 0x0D333, 0x0D334, 0x0D335, 0x0D336, 0x0D337, 0x0D338, 0x0D339, 0x0D33A, 0x0D33B, 0x0D33C, 0x0D33D, 0x0D33E, 0x0D33F, 0x0D340, 0x0D341, 0x0D342, 0x0D343, 0x0D344, 0x0D345, 0x0D346, 0x0D347, 0x0D348, 0x0D349, 0x0D34A, 0x0D34B, 0x0D34C, 0x0D34D, 0x0D34E, 0x0D34F, 0x0D350, 0x0D351, 0x0D352, 0x0D353, 0x0D354, 0x0D355, 0x0D356, 0x0D357, 0x0D358, 0x0D359, 0x0D35A, 0x0D35B, 0x0D35C, 0x0D35D, 0x0D35E, 0x0D35F, 0x0D360, 0x0D361, 0x0D362, 0x0D363, 0x0D364, 0x0D365, 0x0D366, 0x0D367, 0x0D368, 0x0D369, 0x0D36A, 0x0D36B, 0x0D36C, 0x0D36D, 0x0D36E, 0x0D36F, 0x0D370, 0x0D371, 0x0D372, 0x0D373, 0x0D374, 0x0D375, 0x0D376, 0x0D377, 0x0D378, 0x0D379, 0x0D37A, 0x0D37B, 0x0D37C, 0x0D37D, 0x0D37E, 0x0D37F, 0x0D380, 0x0D381, 0x0D382, 0x0D383, 0x0D384, 0x0D385, 0x0D386, 0x0D387, 0x0D388, 0x0D389, 0x0D38A, 0x0D38B, 0x0D38C, 0x0D38D, 0x0D38E, 0x0D38F, 0x0D390, 0x0D391, 0x0D392, 0x0D393, 0x0D394, 0x0D395, 0x0D396, 0x0D397, 0x0D398, 0x0D399, 0x0D39A, 0x0D39B, 0x0D39C, 0x0D39D, 0x0D39E, 0x0D39F, 0x0D3A0, 0x0D3A1, 0x0D3A2, 0x0D3A3, 0x0D3A4, 0x0D3A5, 0x0D3A6, 0x0D3A7, 0x0D3A8, 0x0D3A9, 0x0D3AA, 0x0D3AB, 0x0D3AC, 0x0D3AD, 0x0D3AE, 0x0D3AF, 0x0D3B0, 0x0D3B1, 0x0D3B2, 0x0D3B3, 0x0D3B4, 0x0D3B5, 0x0D3B6, 0x0D3B7, 0x0D3B8, 0x0D3B9, 0x0D3BA, 0x0D3BB, 0x0D3BC, 0x0D3BD, 0x0D3BE, 0x0D3BF, 0x0D3C0, 0x0D3C1, 0x0D3C2, 0x0D3C3, 0x0D3C4, 0x0D3C5, 0x0D3C6, 0x0D3C7, 0x0D3C8, 0x0D3C9, 0x0D3CA, 0x0D3CB, 0x0D3CC, 0x0D3CD, 0x0D3CE, 0x0D3CF, 0x0D3D0, 0x0D3D1, 0x0D3D2, 0x0D3D3, 0x0D3D4, 0x0D3D5, 0x0D3D6, 0x0D3D7, 0x0D3D8, 0x0D3D9, 0x0D3DA, 0x0D3DB, 0x0D3DC, 0x0D3DD, 0x0D3DE, 0x0D3DF, 0x0D3E0, 0x0D3E1, 0x0D3E2, 0x0D3E3, 0x0D3E4, 0x0D3E5, 0x0D3E6, 0x0D3E7, 0x0D3E8, 0x0D3E9, 0x0D3EA, 0x0D3EB, 0x0D3EC, 0x0D3ED, 0x0D3EE, 0x0D3EF, 0x0D3F0, 0x0D3F1, 0x0D3F2, 0x0D3F3, 0x0D3F4, 0x0D3F5, 0x0D3F6, 0x0D3F7, 0x0D3F8, 0x0D3F9, 0x0D3FA, 0x0D3FB, 0x0D3FC, 0x0D3FD, 0x0D3FE, 0x0D3FF, 0x0D400, 0x0D401, 0x0D402, 0x0D403, 0x0D404, 0x0D405, 0x0D406, 0x0D407, 0x0D408, 0x0D409, 0x0D40A, 0x0D40B, 0x0D40C, 0x0D40D, 0x0D40E, 0x0D40F, 0x0D410, 0x0D411, 0x0D412, 0x0D413, 0x0D414, 0x0D415, 0x0D416, 0x0D417, 0x0D418, 0x0D419, 0x0D41A, 0x0D41B, 0x0D41C, 0x0D41D, 0x0D41E, 0x0D41F, 0x0D420, 0x0D421, 0x0D422, 0x0D423, 0x0D424, 0x0D425, 0x0D426, 0x0D427, 0x0D428, 0x0D429, 0x0D42A, 0x0D42B, 0x0D42C, 0x0D42D, 0x0D42E, 0x0D42F, 0x0D430, 0x0D431, 0x0D432, 0x0D433, 0x0D434, 0x0D435, 0x0D436, 0x0D437, 0x0D438, 0x0D439, 0x0D43A, 0x0D43B, 0x0D43C, 0x0D43D, 0x0D43E, 0x0D43F, 0x0D440, 0x0D441, 0x0D442, 0x0D443, 0x0D444, 0x0D445, 0x0D446, 0x0D447, 0x0D448, 0x0D449, 0x0D44A, 0x0D44B, 0x0D44C, 0x0D44D, 0x0D44E, 0x0D44F, 0x0D450, 0x0D451, 0x0D452, 0x0D453, 0x0D454, 0x0D455, 0x0D456, 0x0D457, 0x0D458, 0x0D459, 0x0D45A, 0x0D45B, 0x0D45C, 0x0D45D, 0x0D45E, 0x0D45F, 0x0D460, 0x0D461, 0x0D462, 0x0D463, 0x0D464, 0x0D465, 0x0D466, 0x0D467, 0x0D468, 0x0D469, 0x0D46A, 0x0D46B, 0x0D46C, 0x0D46D, 0x0D46E, 0x0D46F, 0x0D470, 0x0D471, 0x0D472, 0x0D473, 0x0D474, 0x0D475, 0x0D476, 0x0D477, 0x0D478, 0x0D479, 0x0D47A, 0x0D47B, 0x0D47C, 0x0D47D, 0x0D47E, 0x0D47F, 0x0D480, 0x0D481, 0x0D482, 0x0D483, 0x0D484, 0x0D485, 0x0D486, 0x0D487, 0x0D488, 0x0D489, 0x0D48A, 0x0D48B, 0x0D48C, 0x0D48D, 0x0D48E, 0x0D48F, 0x0D490, 0x0D491, 0x0D492, 0x0D493, 0x0D494, 0x0D495, 0x0D496, 0x0D497, 0x0D498, 0x0D499, 0x0D49A, 0x0D49B, 0x0D49C, 0x0D49D, 0x0D49E, 0x0D49F, 0x0D4A0, 0x0D4A1, 0x0D4A2, 0x0D4A3, 0x0D4A4, 0x0D4A5, 0x0D4A6, 0x0D4A7, 0x0D4A8, 0x0D4A9, 0x0D4AA, 0x0D4AB, 0x0D4AC, 0x0D4AD, 0x0D4AE, 0x0D4AF, 0x0D4B0, 0x0D4B1, 0x0D4B2, 0x0D4B3, 0x0D4B4, 0x0D4B5, 0x0D4B6, 0x0D4B7, 0x0D4B8, 0x0D4B9, 0x0D4BA, 0x0D4BB, 0x0D4BC, 0x0D4BD, 0x0D4BE, 0x0D4BF, 0x0D4C0, 0x0D4C1, 0x0D4C2, 0x0D4C3, 0x0D4C4, 0x0D4C5, 0x0D4C6, 0x0D4C7, 0x0D4C8, 0x0D4C9, 0x0D4CA, 0x0D4CB, 0x0D4CC, 0x0D4CD, 0x0D4CE, 0x0D4CF, 0x0D4D0, 0x0D4D1, 0x0D4D2, 0x0D4D3, 0x0D4D4, 0x0D4D5, 0x0D4D6, 0x0D4D7, 0x0D4D8, 0x0D4D9, 0x0D4DA, 0x0D4DB, 0x0D4DC, 0x0D4DD, 0x0D4DE, 0x0D4DF, 0x0D4E0, 0x0D4E1, 0x0D4E2, 0x0D4E3, 0x0D4E4, 0x0D4E5, 0x0D4E6, 0x0D4E7, 0x0D4E8, 0x0D4E9, 0x0D4EA, 0x0D4EB, 0x0D4EC, 0x0D4ED, 0x0D4EE, 0x0D4EF, 0x0D4F0, 0x0D4F1, 0x0D4F2, 0x0D4F3, 0x0D4F4, 0x0D4F5, 0x0D4F6, 0x0D4F7, 0x0D4F8, 0x0D4F9, 0x0D4FA, 0x0D4FB, 0x0D4FC, 0x0D4FD, 0x0D4FE, 0x0D4FF, 0x0D500, 0x0D501, 0x0D502, 0x0D503, 0x0D504, 0x0D505, 0x0D506, 0x0D507, 0x0D508, 0x0D509, 0x0D50A, 0x0D50B, 0x0D50C, 0x0D50D, 0x0D50E, 0x0D50F, 0x0D510, 0x0D511, 0x0D512, 0x0D513, 0x0D514, 0x0D515, 0x0D516, 0x0D517, 0x0D518, 0x0D519, 0x0D51A, 0x0D51B, 0x0D51C, 0x0D51D, 0x0D51E, 0x0D51F, 0x0D520, 0x0D521, 0x0D522, 0x0D523, 0x0D524, 0x0D525, 0x0D526, 0x0D527, 0x0D528, 0x0D529, 0x0D52A, 0x0D52B, 0x0D52C, 0x0D52D, 0x0D52E, 0x0D52F, 0x0D530, 0x0D531, 0x0D532, 0x0D533, 0x0D534, 0x0D535, 0x0D536, 0x0D537, 0x0D538, 0x0D539, 0x0D53A, 0x0D53B, 0x0D53C, 0x0D53D, 0x0D53E, 0x0D53F, 0x0D540, 0x0D541, 0x0D542, 0x0D543, 0x0D544, 0x0D545, 0x0D546, 0x0D547, 0x0D548, 0x0D549, 0x0D54A, 0x0D54B, 0x0D54C, 0x0D54D, 0x0D54E, 0x0D54F, 0x0D550, 0x0D551, 0x0D552, 0x0D553, 0x0D554, 0x0D555, 0x0D556, 0x0D557, 0x0D558, 0x0D559, 0x0D55A, 0x0D55B, 0x0D55C, 0x0D55D, 0x0D55E, 0x0D55F, 0x0D560, 0x0D561, 0x0D562, 0x0D563, 0x0D564, 0x0D565, 0x0D566, 0x0D567, 0x0D568, 0x0D569, 0x0D56A, 0x0D56B, 0x0D56C, 0x0D56D, 0x0D56E, 0x0D56F, 0x0D570, 0x0D571, 0x0D572, 0x0D573, 0x0D574, 0x0D575, 0x0D576, 0x0D577, 0x0D578, 0x0D579, 0x0D57A, 0x0D57B, 0x0D57C, 0x0D57D, 0x0D57E, 0x0D57F, 0x0D580, 0x0D581, 0x0D582, 0x0D583, 0x0D584, 0x0D585, 0x0D586, 0x0D587, 0x0D588, 0x0D589, 0x0D58A, 0x0D58B, 0x0D58C, 0x0D58D, 0x0D58E, 0x0D58F, 0x0D590, 0x0D591, 0x0D592, 0x0D593, 0x0D594, 0x0D595, 0x0D596, 0x0D597, 0x0D598, 0x0D599, 0x0D59A, 0x0D59B, 0x0D59C, 0x0D59D, 0x0D59E, 0x0D59F, 0x0D5A0, 0x0D5A1, 0x0D5A2, 0x0D5A3, 0x0D5A4, 0x0D5A5, 0x0D5A6, 0x0D5A7, 0x0D5A8, 0x0D5A9, 0x0D5AA, 0x0D5AB, 0x0D5AC, 0x0D5AD, 0x0D5AE, 0x0D5AF, 0x0D5B0, 0x0D5B1, 0x0D5B2, 0x0D5B3, 0x0D5B4, 0x0D5B5, 0x0D5B6, 0x0D5B7, 0x0D5B8, 0x0D5B9, 0x0D5BA, 0x0D5BB, 0x0D5BC, 0x0D5BD, 0x0D5BE, 0x0D5BF, 0x0D5C0, 0x0D5C1, 0x0D5C2, 0x0D5C3, 0x0D5C4, 0x0D5C5, 0x0D5C6, 0x0D5C7, 0x0D5C8, 0x0D5C9, 0x0D5CA, 0x0D5CB, 0x0D5CC, 0x0D5CD, 0x0D5CE, 0x0D5CF, 0x0D5D0, 0x0D5D1, 0x0D5D2, 0x0D5D3, 0x0D5D4, 0x0D5D5, 0x0D5D6, 0x0D5D7, 0x0D5D8, 0x0D5D9, 0x0D5DA, 0x0D5DB, 0x0D5DC, 0x0D5DD, 0x0D5DE, 0x0D5DF, 0x0D5E0, 0x0D5E1, 0x0D5E2, 0x0D5E3, 0x0D5E4, 0x0D5E5, 0x0D5E6, 0x0D5E7, 0x0D5E8, 0x0D5E9, 0x0D5EA, 0x0D5EB, 0x0D5EC, 0x0D5ED, 0x0D5EE, 0x0D5EF, 0x0D5F0, 0x0D5F1, 0x0D5F2, 0x0D5F3, 0x0D5F4, 0x0D5F5, 0x0D5F6, 0x0D5F7, 0x0D5F8, 0x0D5F9, 0x0D5FA, 0x0D5FB, 0x0D5FC, 0x0D5FD, 0x0D5FE, 0x0D5FF, 0x0D600, 0x0D601, 0x0D602, 0x0D603, 0x0D604, 0x0D605, 0x0D606, 0x0D607, 0x0D608, 0x0D609, 0x0D60A, 0x0D60B, 0x0D60C, 0x0D60D, 0x0D60E, 0x0D60F, 0x0D610, 0x0D611, 0x0D612, 0x0D613, 0x0D614, 0x0D615, 0x0D616, 0x0D617, 0x0D618, 0x0D619, 0x0D61A, 0x0D61B, 0x0D61C, 0x0D61D, 0x0D61E, 0x0D61F, 0x0D620, 0x0D621, 0x0D622, 0x0D623, 0x0D624, 0x0D625, 0x0D626, 0x0D627, 0x0D628, 0x0D629, 0x0D62A, 0x0D62B, 0x0D62C, 0x0D62D, 0x0D62E, 0x0D62F, 0x0D630, 0x0D631, 0x0D632, 0x0D633, 0x0D634, 0x0D635, 0x0D636, 0x0D637, 0x0D638, 0x0D639, 0x0D63A, 0x0D63B, 0x0D63C, 0x0D63D, 0x0D63E, 0x0D63F, 0x0D640, 0x0D641, 0x0D642, 0x0D643, 0x0D644, 0x0D645, 0x0D646, 0x0D647, 0x0D648, 0x0D649, 0x0D64A, 0x0D64B, 0x0D64C, 0x0D64D, 0x0D64E, 0x0D64F, 0x0D650, 0x0D651, 0x0D652, 0x0D653, 0x0D654, 0x0D655, 0x0D656, 0x0D657, 0x0D658, 0x0D659, 0x0D65A, 0x0D65B, 0x0D65C, 0x0D65D, 0x0D65E, 0x0D65F, 0x0D660, 0x0D661, 0x0D662, 0x0D663, 0x0D664, 0x0D665, 0x0D666, 0x0D667, 0x0D668, 0x0D669, 0x0D66A, 0x0D66B, 0x0D66C, 0x0D66D, 0x0D66E, 0x0D66F, 0x0D670, 0x0D671, 0x0D672, 0x0D673, 0x0D674, 0x0D675, 0x0D676, 0x0D677, 0x0D678, 0x0D679, 0x0D67A, 0x0D67B, 0x0D67C, 0x0D67D, 0x0D67E, 0x0D67F, 0x0D680, 0x0D681, 0x0D682, 0x0D683, 0x0D684, 0x0D685, 0x0D686, 0x0D687, 0x0D688, 0x0D689, 0x0D68A, 0x0D68B, 0x0D68C, 0x0D68D, 0x0D68E, 0x0D68F, 0x0D690, 0x0D691, 0x0D692, 0x0D693, 0x0D694, 0x0D695, 0x0D696, 0x0D697, 0x0D698, 0x0D699, 0x0D69A, 0x0D69B, 0x0D69C, 0x0D69D, 0x0D69E, 0x0D69F, 0x0D6A0, 0x0D6A1, 0x0D6A2, 0x0D6A3, 0x0D6A4, 0x0D6A5, 0x0D6A6, 0x0D6A7, 0x0D6A8, 0x0D6A9, 0x0D6AA, 0x0D6AB, 0x0D6AC, 0x0D6AD, 0x0D6AE, 0x0D6AF, 0x0D6B0, 0x0D6B1, 0x0D6B2, 0x0D6B3, 0x0D6B4, 0x0D6B5, 0x0D6B6, 0x0D6B7, 0x0D6B8, 0x0D6B9, 0x0D6BA, 0x0D6BB, 0x0D6BC, 0x0D6BD, 0x0D6BE, 0x0D6BF, 0x0D6C0, 0x0D6C1, 0x0D6C2, 0x0D6C3, 0x0D6C4, 0x0D6C5, 0x0D6C6, 0x0D6C7, 0x0D6C8, 0x0D6C9, 0x0D6CA, 0x0D6CB, 0x0D6CC, 0x0D6CD, 0x0D6CE, 0x0D6CF, 0x0D6D0, 0x0D6D1, 0x0D6D2, 0x0D6D3, 0x0D6D4, 0x0D6D5, 0x0D6D6, 0x0D6D7, 0x0D6D8, 0x0D6D9, 0x0D6DA, 0x0D6DB, 0x0D6DC, 0x0D6DD, 0x0D6DE, 0x0D6DF, 0x0D6E0, 0x0D6E1, 0x0D6E2, 0x0D6E3, 0x0D6E4, 0x0D6E5, 0x0D6E6, 0x0D6E7, 0x0D6E8, 0x0D6E9, 0x0D6EA, 0x0D6EB, 0x0D6EC, 0x0D6ED, 0x0D6EE, 0x0D6EF, 0x0D6F0, 0x0D6F1, 0x0D6F2, 0x0D6F3, 0x0D6F4, 0x0D6F5, 0x0D6F6, 0x0D6F7, 0x0D6F8, 0x0D6F9, 0x0D6FA, 0x0D6FB, 0x0D6FC, 0x0D6FD, 0x0D6FE, 0x0D6FF, 0x0D700, 0x0D701, 0x0D702, 0x0D703, 0x0D704, 0x0D705, 0x0D706, 0x0D707, 0x0D708, 0x0D709, 0x0D70A, 0x0D70B, 0x0D70C, 0x0D70D, 0x0D70E, 0x0D70F, 0x0D710, 0x0D711, 0x0D712, 0x0D713, 0x0D714, 0x0D715, 0x0D716, 0x0D717, 0x0D718, 0x0D719, 0x0D71A, 0x0D71B, 0x0D71C, 0x0D71D, 0x0D71E, 0x0D71F, 0x0D720, 0x0D721, 0x0D722, 0x0D723, 0x0D724, 0x0D725, 0x0D726, 0x0D727, 0x0D728, 0x0D729, 0x0D72A, 0x0D72B, 0x0D72C, 0x0D72D, 0x0D72E, 0x0D72F, 0x0D730, 0x0D731, 0x0D732, 0x0D733, 0x0D734, 0x0D735, 0x0D736, 0x0D737, 0x0D738, 0x0D739, 0x0D73A, 0x0D73B, 0x0D73C, 0x0D73D, 0x0D73E, 0x0D73F, 0x0D740, 0x0D741, 0x0D742, 0x0D743, 0x0D744, 0x0D745, 0x0D746, 0x0D747, 0x0D748, 0x0D749, 0x0D74A, 0x0D74B, 0x0D74C, 0x0D74D, 0x0D74E, 0x0D74F, 0x0D750, 0x0D751, 0x0D752, 0x0D753, 0x0D754, 0x0D755, 0x0D756, 0x0D757, 0x0D758, 0x0D759, 0x0D75A, 0x0D75B, 0x0D75C, 0x0D75D, 0x0D75E, 0x0D75F, 0x0D760, 0x0D761, 0x0D762, 0x0D763, 0x0D764, 0x0D765, 0x0D766, 0x0D767, 0x0D768, 0x0D769, 0x0D76A, 0x0D76B, 0x0D76C, 0x0D76D, 0x0D76E, 0x0D76F, 0x0D770, 0x0D771, 0x0D772, 0x0D773, 0x0D774, 0x0D775, 0x0D776, 0x0D777, 0x0D778, 0x0D779, 0x0D77A, 0x0D77B, 0x0D77C, 0x0D77D, 0x0D77E, 0x0D77F, 0x0D780, 0x0D781, 0x0D782, 0x0D783, 0x0D784, 0x0D785, 0x0D786, 0x0D787, 0x0D788, 0x0D789, 0x0D78A, 0x0D78B, 0x0D78C, 0x0D78D, 0x0D78E, 0x0D78F, 0x0D790, 0x0D791, 0x0D792, 0x0D793, 0x0D794, 0x0D795, 0x0D796, 0x0D797, 0x0D798, 0x0D799, 0x0D79A, 0x0D79B, 0x0D79C, 0x0D79D, 0x0D79E, 0x0D79F, 0x0D7A0, 0x0D7A1, 0x0D7A2, 0x0D7A3, 0x0D7B0, 0x0D7B1, 0x0D7B2, 0x0D7B3, 0x0D7B4, 0x0D7B5, 0x0D7B6, 0x0D7B7, 0x0D7B8, 0x0D7B9, 0x0D7BA, 0x0D7BB, 0x0D7BC, 0x0D7BD, 0x0D7BE, 0x0D7BF, 0x0D7C0, 0x0D7C1, 0x0D7C2, 0x0D7C3, 0x0D7C4, 0x0D7C5, 0x0D7C6, 0x0D7CB, 0x0D7CC, 0x0D7CD, 0x0D7CE, 0x0D7CF, 0x0D7D0, 0x0D7D1, 0x0D7D2, 0x0D7D3, 0x0D7D4, 0x0D7D5, 0x0D7D6, 0x0D7D7, 0x0D7D8, 0x0D7D9, 0x0D7DA, 0x0D7DB, 0x0D7DC, 0x0D7DD, 0x0D7DE, 0x0D7DF, 0x0D7E0, 0x0D7E1, 0x0D7E2, 0x0D7E3, 0x0D7E4, 0x0D7E5, 0x0D7E6, 0x0D7E7, 0x0D7E8, 0x0D7E9, 0x0D7EA, 0x0D7EB, 0x0D7EC, 0x0D7ED, 0x0D7EE, 0x0D7EF, 0x0D7F0, 0x0D7F1, 0x0D7F2, 0x0D7F3, 0x0D7F4, 0x0D7F5, 0x0D7F6, 0x0D7F7, 0x0D7F8, 0x0D7F9, 0x0D7FA, 0x0D7FB, 0x0FB00, 0x0FB01, 0x0FB02, 0x0FB03, 0x0FB04, 0x0FB05, 0x0FB06, 0x0FB13, 0x0FB14, 0x0FB15, 0x0FB16, 0x0FB17, 0x0FB50, 0x0FB51, 0x0FB52, 0x0FB53, 0x0FB54, 0x0FB55, 0x0FB56, 0x0FB57, 0x0FB58, 0x0FB59, 0x0FB5A, 0x0FB5B, 0x0FB5C, 0x0FB5D, 0x0FB5E, 0x0FB5F, 0x0FB60, 0x0FB61, 0x0FB62, 0x0FB63, 0x0FB64, 0x0FB65, 0x0FB66, 0x0FB67, 0x0FB68, 0x0FB69, 0x0FB6A, 0x0FB6B, 0x0FB6C, 0x0FB6D, 0x0FB6E, 0x0FB6F, 0x0FB70, 0x0FB71, 0x0FB72, 0x0FB73, 0x0FB74, 0x0FB75, 0x0FB76, 0x0FB77, 0x0FB78, 0x0FB79, 0x0FB7A, 0x0FB7B, 0x0FB7C, 0x0FB7D, 0x0FB7E, 0x0FB7F, 0x0FB80, 0x0FB81, 0x0FB82, 0x0FB83, 0x0FB84, 0x0FB85, 0x0FB86, 0x0FB87, 0x0FB88, 0x0FB89, 0x0FB8A, 0x0FB8B, 0x0FB8C, 0x0FB8D, 0x0FB8E, 0x0FB8F, 0x0FB90, 0x0FB91, 0x0FB92, 0x0FB93, 0x0FB94, 0x0FB95, 0x0FB96, 0x0FB97, 0x0FB98, 0x0FB99, 0x0FB9A, 0x0FB9B, 0x0FB9C, 0x0FB9D, 0x0FB9E, 0x0FB9F, 0x0FBA0, 0x0FBA1, 0x0FBA2, 0x0FBA3, 0x0FBA4, 0x0FBA5, 0x0FBA6, 0x0FBA7, 0x0FBA8, 0x0FBA9, 0x0FBAA, 0x0FBAB, 0x0FBAC, 0x0FBAD, 0x0FBAE, 0x0FBAF, 0x0FBB0, 0x0FBB1, 0x0FBD3, 0x0FBD4, 0x0FBD5, 0x0FBD6, 0x0FBD7, 0x0FBD8, 0x0FBD9, 0x0FBDA, 0x0FBDB, 0x0FBDC, 0x0FBDD, 0x0FBDE, 0x0FBDF, 0x0FBE0, 0x0FBE1, 0x0FBE2, 0x0FBE3, 0x0FBE4, 0x0FBE5, 0x0FBE6, 0x0FBE7, 0x0FBE8, 0x0FBE9, 0x0FBEA, 0x0FBEB, 0x0FBEC, 0x0FBED, 0x0FBEE, 0x0FBEF, 0x0FBF0, 0x0FBF1, 0x0FBF2, 0x0FBF3, 0x0FBF4, 0x0FBF5, 0x0FBF6, 0x0FBF7, 0x0FBF8, 0x0FBF9, 0x0FBFA, 0x0FBFB, 0x0FBFC, 0x0FBFD, 0x0FBFE, 0x0FBFF, 0x0FC00, 0x0FC01, 0x0FC02, 0x0FC03, 0x0FC04, 0x0FC05, 0x0FC06, 0x0FC07, 0x0FC08, 0x0FC09, 0x0FC0A, 0x0FC0B, 0x0FC0C, 0x0FC0D, 0x0FC0E, 0x0FC0F, 0x0FC10, 0x0FC11, 0x0FC12, 0x0FC13, 0x0FC14, 0x0FC15, 0x0FC16, 0x0FC17, 0x0FC18, 0x0FC19, 0x0FC1A, 0x0FC1B, 0x0FC1C, 0x0FC1D, 0x0FC1E, 0x0FC1F, 0x0FC20, 0x0FC21, 0x0FC22, 0x0FC23, 0x0FC24, 0x0FC25, 0x0FC26, 0x0FC27, 0x0FC28, 0x0FC29, 0x0FC2A, 0x0FC2B, 0x0FC2C, 0x0FC2D, 0x0FC2E, 0x0FC2F, 0x0FC30, 0x0FC31, 0x0FC32, 0x0FC33, 0x0FC34, 0x0FC35, 0x0FC36, 0x0FC37, 0x0FC38, 0x0FC39, 0x0FC3A, 0x0FC3B, 0x0FC3C, 0x0FC3D, 0x0FC3E, 0x0FC3F, 0x0FC40, 0x0FC41, 0x0FC42, 0x0FC43, 0x0FC44, 0x0FC45, 0x0FC46, 0x0FC47, 0x0FC48, 0x0FC49, 0x0FC4A, 0x0FC4B, 0x0FC4C, 0x0FC4D, 0x0FC4E, 0x0FC4F, 0x0FC50, 0x0FC51, 0x0FC52, 0x0FC53, 0x0FC54, 0x0FC55, 0x0FC56, 0x0FC57, 0x0FC58, 0x0FC59, 0x0FC5A, 0x0FC5B, 0x0FC5C, 0x0FC5D, 0x0FC5E, 0x0FC5F, 0x0FC60, 0x0FC61, 0x0FC62, 0x0FC63, 0x0FC64, 0x0FC65, 0x0FC66, 0x0FC67, 0x0FC68, 0x0FC69, 0x0FC6A, 0x0FC6B, 0x0FC6C, 0x0FC6D, 0x0FC6E, 0x0FC6F, 0x0FC70, 0x0FC71, 0x0FC72, 0x0FC73, 0x0FC74, 0x0FC75, 0x0FC76, 0x0FC77, 0x0FC78, 0x0FC79, 0x0FC7A, 0x0FC7B, 0x0FC7C, 0x0FC7D, 0x0FC7E, 0x0FC7F, 0x0FC80, 0x0FC81, 0x0FC82, 0x0FC83, 0x0FC84, 0x0FC85, 0x0FC86, 0x0FC87, 0x0FC88, 0x0FC89, 0x0FC8A, 0x0FC8B, 0x0FC8C, 0x0FC8D, 0x0FC8E, 0x0FC8F, 0x0FC90, 0x0FC91, 0x0FC92, 0x0FC93, 0x0FC94, 0x0FC95, 0x0FC96, 0x0FC97, 0x0FC98, 0x0FC99, 0x0FC9A, 0x0FC9B, 0x0FC9C, 0x0FC9D, 0x0FC9E, 0x0FC9F, 0x0FCA0, 0x0FCA1, 0x0FCA2, 0x0FCA3, 0x0FCA4, 0x0FCA5, 0x0FCA6, 0x0FCA7, 0x0FCA8, 0x0FCA9, 0x0FCAA, 0x0FCAB, 0x0FCAC, 0x0FCAD, 0x0FCAE, 0x0FCAF, 0x0FCB0, 0x0FCB1, 0x0FCB2, 0x0FCB3, 0x0FCB4, 0x0FCB5, 0x0FCB6, 0x0FCB7, 0x0FCB8, 0x0FCB9, 0x0FCBA, 0x0FCBB, 0x0FCBC, 0x0FCBD, 0x0FCBE, 0x0FCBF, 0x0FCC0, 0x0FCC1, 0x0FCC2, 0x0FCC3, 0x0FCC4, 0x0FCC5, 0x0FCC6, 0x0FCC7, 0x0FCC8, 0x0FCC9, 0x0FCCA, 0x0FCCB, 0x0FCCC, 0x0FCCD, 0x0FCCE, 0x0FCCF, 0x0FCD0, 0x0FCD1, 0x0FCD2, 0x0FCD3, 0x0FCD4, 0x0FCD5, 0x0FCD6, 0x0FCD7, 0x0FCD8, 0x0FCD9, 0x0FCDA, 0x0FCDB, 0x0FCDC, 0x0FCDD, 0x0FCDE, 0x0FCDF, 0x0FCE0, 0x0FCE1, 0x0FCE2, 0x0FCE3, 0x0FCE4, 0x0FCE5, 0x0FCE6, 0x0FCE7, 0x0FCE8, 0x0FCE9, 0x0FCEA, 0x0FCEB, 0x0FCEC, 0x0FCED, 0x0FCEE, 0x0FCEF, 0x0FCF0, 0x0FCF1, 0x0FCF2, 0x0FCF3, 0x0FCF4, 0x0FCF5, 0x0FCF6, 0x0FCF7, 0x0FCF8, 0x0FCF9, 0x0FCFA, 0x0FCFB, 0x0FCFC, 0x0FCFD, 0x0FCFE, 0x0FCFF, 0x0FD00, 0x0FD01, 0x0FD02, 0x0FD03, 0x0FD04, 0x0FD05, 0x0FD06, 0x0FD07, 0x0FD08, 0x0FD09, 0x0FD0A, 0x0FD0B, 0x0FD0C, 0x0FD0D, 0x0FD0E, 0x0FD0F, 0x0FD10, 0x0FD11, 0x0FD12, 0x0FD13, 0x0FD14, 0x0FD15, 0x0FD16, 0x0FD17, 0x0FD18, 0x0FD19, 0x0FD1A, 0x0FD1B, 0x0FD1C, 0x0FD1D, 0x0FD1E, 0x0FD1F, 0x0FD20, 0x0FD21, 0x0FD22, 0x0FD23, 0x0FD24, 0x0FD25, 0x0FD26, 0x0FD27, 0x0FD28, 0x0FD29, 0x0FD2A, 0x0FD2B, 0x0FD2C, 0x0FD2D, 0x0FD2E, 0x0FD2F, 0x0FD30, 0x0FD31, 0x0FD32, 0x0FD33, 0x0FD34, 0x0FD35, 0x0FD36, 0x0FD37, 0x0FD38, 0x0FD39, 0x0FD3A, 0x0FD3B, 0x0FD3C, 0x0FD3D, 0x0FD50, 0x0FD51, 0x0FD52, 0x0FD53, 0x0FD54, 0x0FD55, 0x0FD56, 0x0FD57, 0x0FD58, 0x0FD59, 0x0FD5A, 0x0FD5B, 0x0FD5C, 0x0FD5D, 0x0FD5E, 0x0FD5F, 0x0FD60, 0x0FD61, 0x0FD62, 0x0FD63, 0x0FD64, 0x0FD65, 0x0FD66, 0x0FD67, 0x0FD68, 0x0FD69, 0x0FD6A, 0x0FD6B, 0x0FD6C, 0x0FD6D, 0x0FD6E, 0x0FD6F, 0x0FD70, 0x0FD71, 0x0FD72, 0x0FD73, 0x0FD74, 0x0FD75, 0x0FD76, 0x0FD77, 0x0FD78, 0x0FD79, 0x0FD7A, 0x0FD7B, 0x0FD7C, 0x0FD7D, 0x0FD7E, 0x0FD7F, 0x0FD80, 0x0FD81, 0x0FD82, 0x0FD83, 0x0FD84, 0x0FD85, 0x0FD86, 0x0FD87, 0x0FD88, 0x0FD89, 0x0FD8A, 0x0FD8B, 0x0FD8C, 0x0FD8D, 0x0FD8E, 0x0FD8F, 0x0FD92, 0x0FD93, 0x0FD94, 0x0FD95, 0x0FD96, 0x0FD97, 0x0FD98, 0x0FD99, 0x0FD9A, 0x0FD9B, 0x0FD9C, 0x0FD9D, 0x0FD9E, 0x0FD9F, 0x0FDA0, 0x0FDA1, 0x0FDA2, 0x0FDA3, 0x0FDA4, 0x0FDA5, 0x0FDA6, 0x0FDA7, 0x0FDA8, 0x0FDA9, 0x0FDAA, 0x0FDAB, 0x0FDAC, 0x0FDAD, 0x0FDAE, 0x0FDAF, 0x0FDB0, 0x0FDB1, 0x0FDB2, 0x0FDB3, 0x0FDB4, 0x0FDB5, 0x0FDB6, 0x0FDB7, 0x0FDB8, 0x0FDB9, 0x0FDBA, 0x0FDBB, 0x0FDBC, 0x0FDBD, 0x0FDBE, 0x0FDBF, 0x0FDC0, 0x0FDC1, 0x0FDC2, 0x0FDC3, 0x0FDC4, 0x0FDC5, 0x0FDC6, 0x0FDC7, 0x0FDF0, 0x0FDF1, 0x0FDF2, 0x0FDF3, 0x0FDF4, 0x0FDF5, 0x0FDF6, 0x0FDF7, 0x0FDF8, 0x0FDF9, 0x0FDFA, 0x0FDFB, 0x0FE70, 0x0FE71, 0x0FE72, 0x0FE73, 0x0FE74, 0x0FE76, 0x0FE77, 0x0FE78, 0x0FE79, 0x0FE7A, 0x0FE7B, 0x0FE7C, 0x0FE7D, 0x0FE7E, 0x0FE7F, 0x0FE80, 0x0FE81, 0x0FE82, 0x0FE83, 0x0FE84, 0x0FE85, 0x0FE86, 0x0FE87, 0x0FE88, 0x0FE89, 0x0FE8A, 0x0FE8B, 0x0FE8C, 0x0FE8D, 0x0FE8E, 0x0FE8F, 0x0FE90, 0x0FE91, 0x0FE92, 0x0FE93, 0x0FE94, 0x0FE95, 0x0FE96, 0x0FE97, 0x0FE98, 0x0FE99, 0x0FE9A, 0x0FE9B, 0x0FE9C, 0x0FE9D, 0x0FE9E, 0x0FE9F, 0x0FEA0, 0x0FEA1, 0x0FEA2, 0x0FEA3, 0x0FEA4, 0x0FEA5, 0x0FEA6, 0x0FEA7, 0x0FEA8, 0x0FEA9, 0x0FEAA, 0x0FEAB, 0x0FEAC, 0x0FEAD, 0x0FEAE, 0x0FEAF, 0x0FEB0, 0x0FEB1, 0x0FEB2, 0x0FEB3, 0x0FEB4, 0x0FEB5, 0x0FEB6, 0x0FEB7, 0x0FEB8, 0x0FEB9, 0x0FEBA, 0x0FEBB, 0x0FEBC, 0x0FEBD, 0x0FEBE, 0x0FEBF, 0x0FEC0, 0x0FEC1, 0x0FEC2, 0x0FEC3, 0x0FEC4, 0x0FEC5, 0x0FEC6, 0x0FEC7, 0x0FEC8, 0x0FEC9, 0x0FECA, 0x0FECB, 0x0FECC, 0x0FECD, 0x0FECE, 0x0FECF, 0x0FED0, 0x0FED1, 0x0FED2, 0x0FED3, 0x0FED4, 0x0FED5, 0x0FED6, 0x0FED7, 0x0FED8, 0x0FED9, 0x0FEDA, 0x0FEDB, 0x0FEDC, 0x0FEDD, 0x0FEDE, 0x0FEDF, 0x0FEE0, 0x0FEE1, 0x0FEE2, 0x0FEE3, 0x0FEE4, 0x0FEE5, 0x0FEE6, 0x0FEE7, 0x0FEE8, 0x0FEE9, 0x0FEEA, 0x0FEEB, 0x0FEEC, 0x0FEED, 0x0FEEE, 0x0FEEF, 0x0FEF0, 0x0FEF1, 0x0FEF2, 0x0FEF3, 0x0FEF4, 0x0FEF5, 0x0FEF6, 0x0FEF7, 0x0FEF8, 0x0FEF9, 0x0FEFA, 0x0FEFB, 0x0FEFC, 0x0FF21, 0x0FF22, 0x0FF23, 0x0FF24, 0x0FF25, 0x0FF26, 0x0FF27, 0x0FF28, 0x0FF29, 0x0FF2A, 0x0FF2B, 0x0FF2C, 0x0FF2D, 0x0FF2E, 0x0FF2F, 0x0FF30, 0x0FF31, 0x0FF32, 0x0FF33, 0x0FF34, 0x0FF35, 0x0FF36, 0x0FF37, 0x0FF38, 0x0FF39, 0x0FF3A, 0x0FF41, 0x0FF42, 0x0FF43, 0x0FF44, 0x0FF45, 0x0FF46, 0x0FF47, 0x0FF48, 0x0FF49, 0x0FF4A, 0x0FF4B, 0x0FF4C, 0x0FF4D, 0x0FF4E, 0x0FF4F, 0x0FF50, 0x0FF51, 0x0FF52, 0x0FF53, 0x0FF54, 0x0FF55, 0x0FF56, 0x0FF57, 0x0FF58, 0x0FF59, 0x0FF5A, 0x0FFA0, 0x0FFA1, 0x0FFA2, 0x0FFA3, 0x0FFA4, 0x0FFA5, 0x0FFA6, 0x0FFA7, 0x0FFA8, 0x0FFA9, 0x0FFAA, 0x0FFAB, 0x0FFAC, 0x0FFAD, 0x0FFAE, 0x0FFAF, 0x0FFB0, 0x0FFB1, 0x0FFB2, 0x0FFB3, 0x0FFB4, 0x0FFB5, 0x0FFB6, 0x0FFB7, 0x0FFB8, 0x0FFB9, 0x0FFBA, 0x0FFBB, 0x0FFBC, 0x0FFBD, 0x0FFBE, 0x0FFC2, 0x0FFC3, 0x0FFC4, 0x0FFC5, 0x0FFC6, 0x0FFC7, 0x0FFCA, 0x0FFCB, 0x0FFCC, 0x0FFCD, 0x0FFCE, 0x0FFCF, 0x0FFD2, 0x0FFD3, 0x0FFD4, 0x0FFD5, 0x0FFD6, 0x0FFD7, 0x0FFDA, 0x0FFDB, 0x0FFDC, 0x10000, 0x10001, 0x10002, 0x10003, 0x10004, 0x10005, 0x10006, 0x10007, 0x10008, 0x10009, 0x1000A, 0x1000B, 0x1000D, 0x1000E, 0x1000F, 0x10010, 0x10011, 0x10012, 0x10013, 0x10014, 0x10015, 0x10016, 0x10017, 0x10018, 0x10019, 0x1001A, 0x1001B, 0x1001C, 0x1001D, 0x1001E, 0x1001F, 0x10020, 0x10021, 0x10022, 0x10023, 0x10024, 0x10025, 0x10026, 0x10028, 0x10029, 0x1002A, 0x1002B, 0x1002C, 0x1002D, 0x1002E, 0x1002F, 0x10030, 0x10031, 0x10032, 0x10033, 0x10034, 0x10035, 0x10036, 0x10037, 0x10038, 0x10039, 0x1003A, 0x1003C, 0x1003D, 0x1003F, 0x10040, 0x10041, 0x10042, 0x10043, 0x10044, 0x10045, 0x10046, 0x10047, 0x10048, 0x10049, 0x1004A, 0x1004B, 0x1004C, 0x1004D, 0x10050, 0x10051, 0x10052, 0x10053, 0x10054, 0x10055, 0x10056, 0x10057, 0x10058, 0x10059, 0x1005A, 0x1005B, 0x1005C, 0x1005D, 0x10080, 0x10081, 0x10082, 0x10083, 0x10084, 0x10085, 0x10086, 0x10087, 0x10088, 0x10089, 0x1008A, 0x1008B, 0x1008C, 0x1008D, 0x1008E, 0x1008F, 0x10090, 0x10091, 0x10092, 0x10093, 0x10094, 0x10095, 0x10096, 0x10097, 0x10098, 0x10099, 0x1009A, 0x1009B, 0x1009C, 0x1009D, 0x1009E, 0x1009F, 0x100A0, 0x100A1, 0x100A2, 0x100A3, 0x100A4, 0x100A5, 0x100A6, 0x100A7, 0x100A8, 0x100A9, 0x100AA, 0x100AB, 0x100AC, 0x100AD, 0x100AE, 0x100AF, 0x100B0, 0x100B1, 0x100B2, 0x100B3, 0x100B4, 0x100B5, 0x100B6, 0x100B7, 0x100B8, 0x100B9, 0x100BA, 0x100BB, 0x100BC, 0x100BD, 0x100BE, 0x100BF, 0x100C0, 0x100C1, 0x100C2, 0x100C3, 0x100C4, 0x100C5, 0x100C6, 0x100C7, 0x100C8, 0x100C9, 0x100CA, 0x100CB, 0x100CC, 0x100CD, 0x100CE, 0x100CF, 0x100D0, 0x100D1, 0x100D2, 0x100D3, 0x100D4, 0x100D5, 0x100D6, 0x100D7, 0x100D8, 0x100D9, 0x100DA, 0x100DB, 0x100DC, 0x100DD, 0x100DE, 0x100DF, 0x100E0, 0x100E1, 0x100E2, 0x100E3, 0x100E4, 0x100E5, 0x100E6, 0x100E7, 0x100E8, 0x100E9, 0x100EA, 0x100EB, 0x100EC, 0x100ED, 0x100EE, 0x100EF, 0x100F0, 0x100F1, 0x100F2, 0x100F3, 0x100F4, 0x100F5, 0x100F6, 0x100F7, 0x100F8, 0x100F9, 0x100FA, 0x10140, 0x10141, 0x10142, 0x10143, 0x10144, 0x10145, 0x10146, 0x10147, 0x10148, 0x10149, 0x1014A, 0x1014B, 0x1014C, 0x1014D, 0x1014E, 0x1014F, 0x10150, 0x10151, 0x10152, 0x10153, 0x10154, 0x10155, 0x10156, 0x10157, 0x10158, 0x10159, 0x1015A, 0x1015B, 0x1015C, 0x1015D, 0x1015E, 0x1015F, 0x10160, 0x10161, 0x10162, 0x10163, 0x10164, 0x10165, 0x10166, 0x10167, 0x10168, 0x10169, 0x1016A, 0x1016B, 0x1016C, 0x1016D, 0x1016E, 0x1016F, 0x10170, 0x10171, 0x10172, 0x10173, 0x10174, 0x10280, 0x10281, 0x10282, 0x10283, 0x10284, 0x10285, 0x10286, 0x10287, 0x10288, 0x10289, 0x1028A, 0x1028B, 0x1028C, 0x1028D, 0x1028E, 0x1028F, 0x10290, 0x10291, 0x10292, 0x10293, 0x10294, 0x10295, 0x10296, 0x10297, 0x10298, 0x10299, 0x1029A, 0x1029B, 0x1029C, 0x102A0, 0x102A1, 0x102A2, 0x102A3, 0x102A4, 0x102A5, 0x102A6, 0x102A7, 0x102A8, 0x102A9, 0x102AA, 0x102AB, 0x102AC, 0x102AD, 0x102AE, 0x102AF, 0x102B0, 0x102B1, 0x102B2, 0x102B3, 0x102B4, 0x102B5, 0x102B6, 0x102B7, 0x102B8, 0x102B9, 0x102BA, 0x102BB, 0x102BC, 0x102BD, 0x102BE, 0x102BF, 0x102C0, 0x102C1, 0x102C2, 0x102C3, 0x102C4, 0x102C5, 0x102C6, 0x102C7, 0x102C8, 0x102C9, 0x102CA, 0x102CB, 0x102CC, 0x102CD, 0x102CE, 0x102CF, 0x102D0, 0x10300, 0x10301, 0x10302, 0x10303, 0x10304, 0x10305, 0x10306, 0x10307, 0x10308, 0x10309, 0x1030A, 0x1030B, 0x1030C, 0x1030D, 0x1030E, 0x1030F, 0x10310, 0x10311, 0x10312, 0x10313, 0x10314, 0x10315, 0x10316, 0x10317, 0x10318, 0x10319, 0x1031A, 0x1031B, 0x1031C, 0x1031D, 0x1031E, 0x1031F, 0x10330, 0x10331, 0x10332, 0x10333, 0x10334, 0x10335, 0x10336, 0x10337, 0x10338, 0x10339, 0x1033A, 0x1033B, 0x1033C, 0x1033D, 0x1033E, 0x1033F, 0x10340, 0x10341, 0x10342, 0x10343, 0x10344, 0x10345, 0x10346, 0x10347, 0x10348, 0x10349, 0x1034A, 0x10350, 0x10351, 0x10352, 0x10353, 0x10354, 0x10355, 0x10356, 0x10357, 0x10358, 0x10359, 0x1035A, 0x1035B, 0x1035C, 0x1035D, 0x1035E, 0x1035F, 0x10360, 0x10361, 0x10362, 0x10363, 0x10364, 0x10365, 0x10366, 0x10367, 0x10368, 0x10369, 0x1036A, 0x1036B, 0x1036C, 0x1036D, 0x1036E, 0x1036F, 0x10370, 0x10371, 0x10372, 0x10373, 0x10374, 0x10375, 0x10380, 0x10381, 0x10382, 0x10383, 0x10384, 0x10385, 0x10386, 0x10387, 0x10388, 0x10389, 0x1038A, 0x1038B, 0x1038C, 0x1038D, 0x1038E, 0x1038F, 0x10390, 0x10391, 0x10392, 0x10393, 0x10394, 0x10395, 0x10396, 0x10397, 0x10398, 0x10399, 0x1039A, 0x1039B, 0x1039C, 0x1039D, 0x103A0, 0x103A1, 0x103A2, 0x103A3, 0x103A4, 0x103A5, 0x103A6, 0x103A7, 0x103A8, 0x103A9, 0x103AA, 0x103AB, 0x103AC, 0x103AD, 0x103AE, 0x103AF, 0x103B0, 0x103B1, 0x103B2, 0x103B3, 0x103B4, 0x103B5, 0x103B6, 0x103B7, 0x103B8, 0x103B9, 0x103BA, 0x103BB, 0x103BC, 0x103BD, 0x103BE, 0x103BF, 0x103C0, 0x103C1, 0x103C2, 0x103C3, 0x103C8, 0x103C9, 0x103CA, 0x103CB, 0x103CC, 0x103CD, 0x103CE, 0x103CF, 0x103D1, 0x103D2, 0x103D3, 0x103D4, 0x103D5, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040A, 0x1040B, 0x1040C, 0x1040D, 0x1040E, 0x1040F, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041A, 0x1041B, 0x1041C, 0x1041D, 0x1041E, 0x1041F, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x10428, 0x10429, 0x1042A, 0x1042B, 0x1042C, 0x1042D, 0x1042E, 0x1042F, 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043A, 0x1043B, 0x1043C, 0x1043D, 0x1043E, 0x1043F, 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044A, 0x1044B, 0x1044C, 0x1044D, 0x1044E, 0x1044F, 0x10450, 0x10451, 0x10452, 0x10453, 0x10454, 0x10455, 0x10456, 0x10457, 0x10458, 0x10459, 0x1045A, 0x1045B, 0x1045C, 0x1045D, 0x1045E, 0x1045F, 0x10460, 0x10461, 0x10462, 0x10463, 0x10464, 0x10465, 0x10466, 0x10467, 0x10468, 0x10469, 0x1046A, 0x1046B, 0x1046C, 0x1046D, 0x1046E, 0x1046F, 0x10470, 0x10471, 0x10472, 0x10473, 0x10474, 0x10475, 0x10476, 0x10477, 0x10478, 0x10479, 0x1047A, 0x1047B, 0x1047C, 0x1047D, 0x1047E, 0x1047F, 0x10480, 0x10481, 0x10482, 0x10483, 0x10484, 0x10485, 0x10486, 0x10487, 0x10488, 0x10489, 0x1048A, 0x1048B, 0x1048C, 0x1048D, 0x1048E, 0x1048F, 0x10490, 0x10491, 0x10492, 0x10493, 0x10494, 0x10495, 0x10496, 0x10497, 0x10498, 0x10499, 0x1049A, 0x1049B, 0x1049C, 0x1049D, 0x104B0, 0x104B1, 0x104B2, 0x104B3, 0x104B4, 0x104B5, 0x104B6, 0x104B7, 0x104B8, 0x104B9, 0x104BA, 0x104BB, 0x104BC, 0x104BD, 0x104BE, 0x104BF, 0x104C0, 0x104C1, 0x104C2, 0x104C3, 0x104C4, 0x104C5, 0x104C6, 0x104C7, 0x104C8, 0x104C9, 0x104CA, 0x104CB, 0x104CC, 0x104CD, 0x104CE, 0x104CF, 0x104D0, 0x104D1, 0x104D2, 0x104D3, 0x104D8, 0x104D9, 0x104DA, 0x104DB, 0x104DC, 0x104DD, 0x104DE, 0x104DF, 0x104E0, 0x104E1, 0x104E2, 0x104E3, 0x104E4, 0x104E5, 0x104E6, 0x104E7, 0x104E8, 0x104E9, 0x104EA, 0x104EB, 0x104EC, 0x104ED, 0x104EE, 0x104EF, 0x104F0, 0x104F1, 0x104F2, 0x104F3, 0x104F4, 0x104F5, 0x104F6, 0x104F7, 0x104F8, 0x104F9, 0x104FA, 0x104FB, 0x10500, 0x10501, 0x10502, 0x10503, 0x10504, 0x10505, 0x10506, 0x10507, 0x10508, 0x10509, 0x1050A, 0x1050B, 0x1050C, 0x1050D, 0x1050E, 0x1050F, 0x10510, 0x10511, 0x10512, 0x10513, 0x10514, 0x10515, 0x10516, 0x10517, 0x10518, 0x10519, 0x1051A, 0x1051B, 0x1051C, 0x1051D, 0x1051E, 0x1051F, 0x10520, 0x10521, 0x10522, 0x10523, 0x10524, 0x10525, 0x10526, 0x10527, 0x10530, 0x10531, 0x10532, 0x10533, 0x10534, 0x10535, 0x10536, 0x10537, 0x10538, 0x10539, 0x1053A, 0x1053B, 0x1053C, 0x1053D, 0x1053E, 0x1053F, 0x10540, 0x10541, 0x10542, 0x10543, 0x10544, 0x10545, 0x10546, 0x10547, 0x10548, 0x10549, 0x1054A, 0x1054B, 0x1054C, 0x1054D, 0x1054E, 0x1054F, 0x10550, 0x10551, 0x10552, 0x10553, 0x10554, 0x10555, 0x10556, 0x10557, 0x10558, 0x10559, 0x1055A, 0x1055B, 0x1055C, 0x1055D, 0x1055E, 0x1055F, 0x10560, 0x10561, 0x10562, 0x10563, 0x10600, 0x10601, 0x10602, 0x10603, 0x10604, 0x10605, 0x10606, 0x10607, 0x10608, 0x10609, 0x1060A, 0x1060B, 0x1060C, 0x1060D, 0x1060E, 0x1060F, 0x10610, 0x10611, 0x10612, 0x10613, 0x10614, 0x10615, 0x10616, 0x10617, 0x10618, 0x10619, 0x1061A, 0x1061B, 0x1061C, 0x1061D, 0x1061E, 0x1061F, 0x10620, 0x10621, 0x10622, 0x10623, 0x10624, 0x10625, 0x10626, 0x10627, 0x10628, 0x10629, 0x1062A, 0x1062B, 0x1062C, 0x1062D, 0x1062E, 0x1062F, 0x10630, 0x10631, 0x10632, 0x10633, 0x10634, 0x10635, 0x10636, 0x10637, 0x10638, 0x10639, 0x1063A, 0x1063B, 0x1063C, 0x1063D, 0x1063E, 0x1063F, 0x10640, 0x10641, 0x10642, 0x10643, 0x10644, 0x10645, 0x10646, 0x10647, 0x10648, 0x10649, 0x1064A, 0x1064B, 0x1064C, 0x1064D, 0x1064E, 0x1064F, 0x10650, 0x10651, 0x10652, 0x10653, 0x10654, 0x10655, 0x10656, 0x10657, 0x10658, 0x10659, 0x1065A, 0x1065B, 0x1065C, 0x1065D, 0x1065E, 0x1065F, 0x10660, 0x10661, 0x10662, 0x10663, 0x10664, 0x10665, 0x10666, 0x10667, 0x10668, 0x10669, 0x1066A, 0x1066B, 0x1066C, 0x1066D, 0x1066E, 0x1066F, 0x10670, 0x10671, 0x10672, 0x10673, 0x10674, 0x10675, 0x10676, 0x10677, 0x10678, 0x10679, 0x1067A, 0x1067B, 0x1067C, 0x1067D, 0x1067E, 0x1067F, 0x10680, 0x10681, 0x10682, 0x10683, 0x10684, 0x10685, 0x10686, 0x10687, 0x10688, 0x10689, 0x1068A, 0x1068B, 0x1068C, 0x1068D, 0x1068E, 0x1068F, 0x10690, 0x10691, 0x10692, 0x10693, 0x10694, 0x10695, 0x10696, 0x10697, 0x10698, 0x10699, 0x1069A, 0x1069B, 0x1069C, 0x1069D, 0x1069E, 0x1069F, 0x106A0, 0x106A1, 0x106A2, 0x106A3, 0x106A4, 0x106A5, 0x106A6, 0x106A7, 0x106A8, 0x106A9, 0x106AA, 0x106AB, 0x106AC, 0x106AD, 0x106AE, 0x106AF, 0x106B0, 0x106B1, 0x106B2, 0x106B3, 0x106B4, 0x106B5, 0x106B6, 0x106B7, 0x106B8, 0x106B9, 0x106BA, 0x106BB, 0x106BC, 0x106BD, 0x106BE, 0x106BF, 0x106C0, 0x106C1, 0x106C2, 0x106C3, 0x106C4, 0x106C5, 0x106C6, 0x106C7, 0x106C8, 0x106C9, 0x106CA, 0x106CB, 0x106CC, 0x106CD, 0x106CE, 0x106CF, 0x106D0, 0x106D1, 0x106D2, 0x106D3, 0x106D4, 0x106D5, 0x106D6, 0x106D7, 0x106D8, 0x106D9, 0x106DA, 0x106DB, 0x106DC, 0x106DD, 0x106DE, 0x106DF, 0x106E0, 0x106E1, 0x106E2, 0x106E3, 0x106E4, 0x106E5, 0x106E6, 0x106E7, 0x106E8, 0x106E9, 0x106EA, 0x106EB, 0x106EC, 0x106ED, 0x106EE, 0x106EF, 0x106F0, 0x106F1, 0x106F2, 0x106F3, 0x106F4, 0x106F5, 0x106F6, 0x106F7, 0x106F8, 0x106F9, 0x106FA, 0x106FB, 0x106FC, 0x106FD, 0x106FE, 0x106FF, 0x10700, 0x10701, 0x10702, 0x10703, 0x10704, 0x10705, 0x10706, 0x10707, 0x10708, 0x10709, 0x1070A, 0x1070B, 0x1070C, 0x1070D, 0x1070E, 0x1070F, 0x10710, 0x10711, 0x10712, 0x10713, 0x10714, 0x10715, 0x10716, 0x10717, 0x10718, 0x10719, 0x1071A, 0x1071B, 0x1071C, 0x1071D, 0x1071E, 0x1071F, 0x10720, 0x10721, 0x10722, 0x10723, 0x10724, 0x10725, 0x10726, 0x10727, 0x10728, 0x10729, 0x1072A, 0x1072B, 0x1072C, 0x1072D, 0x1072E, 0x1072F, 0x10730, 0x10731, 0x10732, 0x10733, 0x10734, 0x10735, 0x10736, 0x10740, 0x10741, 0x10742, 0x10743, 0x10744, 0x10745, 0x10746, 0x10747, 0x10748, 0x10749, 0x1074A, 0x1074B, 0x1074C, 0x1074D, 0x1074E, 0x1074F, 0x10750, 0x10751, 0x10752, 0x10753, 0x10754, 0x10755, 0x10760, 0x10761, 0x10762, 0x10763, 0x10764, 0x10765, 0x10766, 0x10767, 0x10800, 0x10801, 0x10802, 0x10803, 0x10804, 0x10805, 0x10808, 0x1080A, 0x1080B, 0x1080C, 0x1080D, 0x1080E, 0x1080F, 0x10810, 0x10811, 0x10812, 0x10813, 0x10814, 0x10815, 0x10816, 0x10817, 0x10818, 0x10819, 0x1081A, 0x1081B, 0x1081C, 0x1081D, 0x1081E, 0x1081F, 0x10820, 0x10821, 0x10822, 0x10823, 0x10824, 0x10825, 0x10826, 0x10827, 0x10828, 0x10829, 0x1082A, 0x1082B, 0x1082C, 0x1082D, 0x1082E, 0x1082F, 0x10830, 0x10831, 0x10832, 0x10833, 0x10834, 0x10835, 0x10837, 0x10838, 0x1083C, 0x1083F, 0x10840, 0x10841, 0x10842, 0x10843, 0x10844, 0x10845, 0x10846, 0x10847, 0x10848, 0x10849, 0x1084A, 0x1084B, 0x1084C, 0x1084D, 0x1084E, 0x1084F, 0x10850, 0x10851, 0x10852, 0x10853, 0x10854, 0x10855, 0x10860, 0x10861, 0x10862, 0x10863, 0x10864, 0x10865, 0x10866, 0x10867, 0x10868, 0x10869, 0x1086A, 0x1086B, 0x1086C, 0x1086D, 0x1086E, 0x1086F, 0x10870, 0x10871, 0x10872, 0x10873, 0x10874, 0x10875, 0x10876, 0x10880, 0x10881, 0x10882, 0x10883, 0x10884, 0x10885, 0x10886, 0x10887, 0x10888, 0x10889, 0x1088A, 0x1088B, 0x1088C, 0x1088D, 0x1088E, 0x1088F, 0x10890, 0x10891, 0x10892, 0x10893, 0x10894, 0x10895, 0x10896, 0x10897, 0x10898, 0x10899, 0x1089A, 0x1089B, 0x1089C, 0x1089D, 0x1089E, 0x108E0, 0x108E1, 0x108E2, 0x108E3, 0x108E4, 0x108E5, 0x108E6, 0x108E7, 0x108E8, 0x108E9, 0x108EA, 0x108EB, 0x108EC, 0x108ED, 0x108EE, 0x108EF, 0x108F0, 0x108F1, 0x108F2, 0x108F4, 0x108F5, 0x10900, 0x10901, 0x10902, 0x10903, 0x10904, 0x10905, 0x10906, 0x10907, 0x10908, 0x10909, 0x1090A, 0x1090B, 0x1090C, 0x1090D, 0x1090E, 0x1090F, 0x10910, 0x10911, 0x10912, 0x10913, 0x10914, 0x10915, 0x10920, 0x10921, 0x10922, 0x10923, 0x10924, 0x10925, 0x10926, 0x10927, 0x10928, 0x10929, 0x1092A, 0x1092B, 0x1092C, 0x1092D, 0x1092E, 0x1092F, 0x10930, 0x10931, 0x10932, 0x10933, 0x10934, 0x10935, 0x10936, 0x10937, 0x10938, 0x10939, 0x10980, 0x10981, 0x10982, 0x10983, 0x10984, 0x10985, 0x10986, 0x10987, 0x10988, 0x10989, 0x1098A, 0x1098B, 0x1098C, 0x1098D, 0x1098E, 0x1098F, 0x10990, 0x10991, 0x10992, 0x10993, 0x10994, 0x10995, 0x10996, 0x10997, 0x10998, 0x10999, 0x1099A, 0x1099B, 0x1099C, 0x1099D, 0x1099E, 0x1099F, 0x109A0, 0x109A1, 0x109A2, 0x109A3, 0x109A4, 0x109A5, 0x109A6, 0x109A7, 0x109A8, 0x109A9, 0x109AA, 0x109AB, 0x109AC, 0x109AD, 0x109AE, 0x109AF, 0x109B0, 0x109B1, 0x109B2, 0x109B3, 0x109B4, 0x109B5, 0x109B6, 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A10, 0x10A11, 0x10A12, 0x10A13, 0x10A15, 0x10A16, 0x10A17, 0x10A19, 0x10A1A, 0x10A1B, 0x10A1C, 0x10A1D, 0x10A1E, 0x10A1F, 0x10A20, 0x10A21, 0x10A22, 0x10A23, 0x10A24, 0x10A25, 0x10A26, 0x10A27, 0x10A28, 0x10A29, 0x10A2A, 0x10A2B, 0x10A2C, 0x10A2D, 0x10A2E, 0x10A2F, 0x10A30, 0x10A31, 0x10A32, 0x10A33, 0x10A60, 0x10A61, 0x10A62, 0x10A63, 0x10A64, 0x10A65, 0x10A66, 0x10A67, 0x10A68, 0x10A69, 0x10A6A, 0x10A6B, 0x10A6C, 0x10A6D, 0x10A6E, 0x10A6F, 0x10A70, 0x10A71, 0x10A72, 0x10A73, 0x10A74, 0x10A75, 0x10A76, 0x10A77, 0x10A78, 0x10A79, 0x10A7A, 0x10A7B, 0x10A7C, 0x10A80, 0x10A81, 0x10A82, 0x10A83, 0x10A84, 0x10A85, 0x10A86, 0x10A87, 0x10A88, 0x10A89, 0x10A8A, 0x10A8B, 0x10A8C, 0x10A8D, 0x10A8E, 0x10A8F, 0x10A90, 0x10A91, 0x10A92, 0x10A93, 0x10A94, 0x10A95, 0x10A96, 0x10A97, 0x10A98, 0x10A99, 0x10A9A, 0x10A9B, 0x10A9C, 0x10AC0, 0x10AC1, 0x10AC2, 0x10AC3, 0x10AC4, 0x10AC5, 0x10AC6, 0x10AC7, 0x10AC9, 0x10ACA, 0x10ACB, 0x10ACC, 0x10ACD, 0x10ACE, 0x10ACF, 0x10AD0, 0x10AD1, 0x10AD2, 0x10AD3, 0x10AD4, 0x10AD5, 0x10AD6, 0x10AD7, 0x10AD8, 0x10AD9, 0x10ADA, 0x10ADB, 0x10ADC, 0x10ADD, 0x10ADE, 0x10ADF, 0x10AE0, 0x10AE1, 0x10AE2, 0x10AE3, 0x10AE4, 0x10B00, 0x10B01, 0x10B02, 0x10B03, 0x10B04, 0x10B05, 0x10B06, 0x10B07, 0x10B08, 0x10B09, 0x10B0A, 0x10B0B, 0x10B0C, 0x10B0D, 0x10B0E, 0x10B0F, 0x10B10, 0x10B11, 0x10B12, 0x10B13, 0x10B14, 0x10B15, 0x10B16, 0x10B17, 0x10B18, 0x10B19, 0x10B1A, 0x10B1B, 0x10B1C, 0x10B1D, 0x10B1E, 0x10B1F, 0x10B20, 0x10B21, 0x10B22, 0x10B23, 0x10B24, 0x10B25, 0x10B26, 0x10B27, 0x10B28, 0x10B29, 0x10B2A, 0x10B2B, 0x10B2C, 0x10B2D, 0x10B2E, 0x10B2F, 0x10B30, 0x10B31, 0x10B32, 0x10B33, 0x10B34, 0x10B35, 0x10B40, 0x10B41, 0x10B42, 0x10B43, 0x10B44, 0x10B45, 0x10B46, 0x10B47, 0x10B48, 0x10B49, 0x10B4A, 0x10B4B, 0x10B4C, 0x10B4D, 0x10B4E, 0x10B4F, 0x10B50, 0x10B51, 0x10B52, 0x10B53, 0x10B54, 0x10B55, 0x10B60, 0x10B61, 0x10B62, 0x10B63, 0x10B64, 0x10B65, 0x10B66, 0x10B67, 0x10B68, 0x10B69, 0x10B6A, 0x10B6B, 0x10B6C, 0x10B6D, 0x10B6E, 0x10B6F, 0x10B70, 0x10B71, 0x10B72, 0x10B80, 0x10B81, 0x10B82, 0x10B83, 0x10B84, 0x10B85, 0x10B86, 0x10B87, 0x10B88, 0x10B89, 0x10B8A, 0x10B8B, 0x10B8C, 0x10B8D, 0x10B8E, 0x10B8F, 0x10B90, 0x10B91, 0x10C00, 0x10C01, 0x10C02, 0x10C03, 0x10C04, 0x10C05, 0x10C06, 0x10C07, 0x10C08, 0x10C09, 0x10C0A, 0x10C0B, 0x10C0C, 0x10C0D, 0x10C0E, 0x10C0F, 0x10C10, 0x10C11, 0x10C12, 0x10C13, 0x10C14, 0x10C15, 0x10C16, 0x10C17, 0x10C18, 0x10C19, 0x10C1A, 0x10C1B, 0x10C1C, 0x10C1D, 0x10C1E, 0x10C1F, 0x10C20, 0x10C21, 0x10C22, 0x10C23, 0x10C24, 0x10C25, 0x10C26, 0x10C27, 0x10C28, 0x10C29, 0x10C2A, 0x10C2B, 0x10C2C, 0x10C2D, 0x10C2E, 0x10C2F, 0x10C30, 0x10C31, 0x10C32, 0x10C33, 0x10C34, 0x10C35, 0x10C36, 0x10C37, 0x10C38, 0x10C39, 0x10C3A, 0x10C3B, 0x10C3C, 0x10C3D, 0x10C3E, 0x10C3F, 0x10C40, 0x10C41, 0x10C42, 0x10C43, 0x10C44, 0x10C45, 0x10C46, 0x10C47, 0x10C48, 0x10C80, 0x10C81, 0x10C82, 0x10C83, 0x10C84, 0x10C85, 0x10C86, 0x10C87, 0x10C88, 0x10C89, 0x10C8A, 0x10C8B, 0x10C8C, 0x10C8D, 0x10C8E, 0x10C8F, 0x10C90, 0x10C91, 0x10C92, 0x10C93, 0x10C94, 0x10C95, 0x10C96, 0x10C97, 0x10C98, 0x10C99, 0x10C9A, 0x10C9B, 0x10C9C, 0x10C9D, 0x10C9E, 0x10C9F, 0x10CA0, 0x10CA1, 0x10CA2, 0x10CA3, 0x10CA4, 0x10CA5, 0x10CA6, 0x10CA7, 0x10CA8, 0x10CA9, 0x10CAA, 0x10CAB, 0x10CAC, 0x10CAD, 0x10CAE, 0x10CAF, 0x10CB0, 0x10CB1, 0x10CB2, 0x10CC0, 0x10CC1, 0x10CC2, 0x10CC3, 0x10CC4, 0x10CC5, 0x10CC6, 0x10CC7, 0x10CC8, 0x10CC9, 0x10CCA, 0x10CCB, 0x10CCC, 0x10CCD, 0x10CCE, 0x10CCF, 0x10CD0, 0x10CD1, 0x10CD2, 0x10CD3, 0x10CD4, 0x10CD5, 0x10CD6, 0x10CD7, 0x10CD8, 0x10CD9, 0x10CDA, 0x10CDB, 0x10CDC, 0x10CDD, 0x10CDE, 0x10CDF, 0x10CE0, 0x10CE1, 0x10CE2, 0x10CE3, 0x10CE4, 0x10CE5, 0x10CE6, 0x10CE7, 0x10CE8, 0x10CE9, 0x10CEA, 0x10CEB, 0x10CEC, 0x10CED, 0x10CEE, 0x10CEF, 0x10CF0, 0x10CF1, 0x10CF2, 0x11003, 0x11004, 0x11005, 0x11006, 0x11007, 0x11008, 0x11009, 0x1100A, 0x1100B, 0x1100C, 0x1100D, 0x1100E, 0x1100F, 0x11010, 0x11011, 0x11012, 0x11013, 0x11014, 0x11015, 0x11016, 0x11017, 0x11018, 0x11019, 0x1101A, 0x1101B, 0x1101C, 0x1101D, 0x1101E, 0x1101F, 0x11020, 0x11021, 0x11022, 0x11023, 0x11024, 0x11025, 0x11026, 0x11027, 0x11028, 0x11029, 0x1102A, 0x1102B, 0x1102C, 0x1102D, 0x1102E, 0x1102F, 0x11030, 0x11031, 0x11032, 0x11033, 0x11034, 0x11035, 0x11036, 0x11037, 0x11083, 0x11084, 0x11085, 0x11086, 0x11087, 0x11088, 0x11089, 0x1108A, 0x1108B, 0x1108C, 0x1108D, 0x1108E, 0x1108F, 0x11090, 0x11091, 0x11092, 0x11093, 0x11094, 0x11095, 0x11096, 0x11097, 0x11098, 0x11099, 0x1109A, 0x1109B, 0x1109C, 0x1109D, 0x1109E, 0x1109F, 0x110A0, 0x110A1, 0x110A2, 0x110A3, 0x110A4, 0x110A5, 0x110A6, 0x110A7, 0x110A8, 0x110A9, 0x110AA, 0x110AB, 0x110AC, 0x110AD, 0x110AE, 0x110AF, 0x110D0, 0x110D1, 0x110D2, 0x110D3, 0x110D4, 0x110D5, 0x110D6, 0x110D7, 0x110D8, 0x110D9, 0x110DA, 0x110DB, 0x110DC, 0x110DD, 0x110DE, 0x110DF, 0x110E0, 0x110E1, 0x110E2, 0x110E3, 0x110E4, 0x110E5, 0x110E6, 0x110E7, 0x110E8, 0x11103, 0x11104, 0x11105, 0x11106, 0x11107, 0x11108, 0x11109, 0x1110A, 0x1110B, 0x1110C, 0x1110D, 0x1110E, 0x1110F, 0x11110, 0x11111, 0x11112, 0x11113, 0x11114, 0x11115, 0x11116, 0x11117, 0x11118, 0x11119, 0x1111A, 0x1111B, 0x1111C, 0x1111D, 0x1111E, 0x1111F, 0x11120, 0x11121, 0x11122, 0x11123, 0x11124, 0x11125, 0x11126, 0x11150, 0x11151, 0x11152, 0x11153, 0x11154, 0x11155, 0x11156, 0x11157, 0x11158, 0x11159, 0x1115A, 0x1115B, 0x1115C, 0x1115D, 0x1115E, 0x1115F, 0x11160, 0x11161, 0x11162, 0x11163, 0x11164, 0x11165, 0x11166, 0x11167, 0x11168, 0x11169, 0x1116A, 0x1116B, 0x1116C, 0x1116D, 0x1116E, 0x1116F, 0x11170, 0x11171, 0x11172, 0x11176, 0x11183, 0x11184, 0x11185, 0x11186, 0x11187, 0x11188, 0x11189, 0x1118A, 0x1118B, 0x1118C, 0x1118D, 0x1118E, 0x1118F, 0x11190, 0x11191, 0x11192, 0x11193, 0x11194, 0x11195, 0x11196, 0x11197, 0x11198, 0x11199, 0x1119A, 0x1119B, 0x1119C, 0x1119D, 0x1119E, 0x1119F, 0x111A0, 0x111A1, 0x111A2, 0x111A3, 0x111A4, 0x111A5, 0x111A6, 0x111A7, 0x111A8, 0x111A9, 0x111AA, 0x111AB, 0x111AC, 0x111AD, 0x111AE, 0x111AF, 0x111B0, 0x111B1, 0x111B2, 0x111C1, 0x111C2, 0x111C3, 0x111C4, 0x111DA, 0x111DC, 0x11200, 0x11201, 0x11202, 0x11203, 0x11204, 0x11205, 0x11206, 0x11207, 0x11208, 0x11209, 0x1120A, 0x1120B, 0x1120C, 0x1120D, 0x1120E, 0x1120F, 0x11210, 0x11211, 0x11213, 0x11214, 0x11215, 0x11216, 0x11217, 0x11218, 0x11219, 0x1121A, 0x1121B, 0x1121C, 0x1121D, 0x1121E, 0x1121F, 0x11220, 0x11221, 0x11222, 0x11223, 0x11224, 0x11225, 0x11226, 0x11227, 0x11228, 0x11229, 0x1122A, 0x1122B, 0x11280, 0x11281, 0x11282, 0x11283, 0x11284, 0x11285, 0x11286, 0x11288, 0x1128A, 0x1128B, 0x1128C, 0x1128D, 0x1128F, 0x11290, 0x11291, 0x11292, 0x11293, 0x11294, 0x11295, 0x11296, 0x11297, 0x11298, 0x11299, 0x1129A, 0x1129B, 0x1129C, 0x1129D, 0x1129F, 0x112A0, 0x112A1, 0x112A2, 0x112A3, 0x112A4, 0x112A5, 0x112A6, 0x112A7, 0x112A8, 0x112B0, 0x112B1, 0x112B2, 0x112B3, 0x112B4, 0x112B5, 0x112B6, 0x112B7, 0x112B8, 0x112B9, 0x112BA, 0x112BB, 0x112BC, 0x112BD, 0x112BE, 0x112BF, 0x112C0, 0x112C1, 0x112C2, 0x112C3, 0x112C4, 0x112C5, 0x112C6, 0x112C7, 0x112C8, 0x112C9, 0x112CA, 0x112CB, 0x112CC, 0x112CD, 0x112CE, 0x112CF, 0x112D0, 0x112D1, 0x112D2, 0x112D3, 0x112D4, 0x112D5, 0x112D6, 0x112D7, 0x112D8, 0x112D9, 0x112DA, 0x112DB, 0x112DC, 0x112DD, 0x112DE, 0x11305, 0x11306, 0x11307, 0x11308, 0x11309, 0x1130A, 0x1130B, 0x1130C, 0x1130F, 0x11310, 0x11313, 0x11314, 0x11315, 0x11316, 0x11317, 0x11318, 0x11319, 0x1131A, 0x1131B, 0x1131C, 0x1131D, 0x1131E, 0x1131F, 0x11320, 0x11321, 0x11322, 0x11323, 0x11324, 0x11325, 0x11326, 0x11327, 0x11328, 0x1132A, 0x1132B, 0x1132C, 0x1132D, 0x1132E, 0x1132F, 0x11330, 0x11332, 0x11333, 0x11335, 0x11336, 0x11337, 0x11338, 0x11339, 0x1133D, 0x11350, 0x1135D, 0x1135E, 0x1135F, 0x11360, 0x11361, 0x11400, 0x11401, 0x11402, 0x11403, 0x11404, 0x11405, 0x11406, 0x11407, 0x11408, 0x11409, 0x1140A, 0x1140B, 0x1140C, 0x1140D, 0x1140E, 0x1140F, 0x11410, 0x11411, 0x11412, 0x11413, 0x11414, 0x11415, 0x11416, 0x11417, 0x11418, 0x11419, 0x1141A, 0x1141B, 0x1141C, 0x1141D, 0x1141E, 0x1141F, 0x11420, 0x11421, 0x11422, 0x11423, 0x11424, 0x11425, 0x11426, 0x11427, 0x11428, 0x11429, 0x1142A, 0x1142B, 0x1142C, 0x1142D, 0x1142E, 0x1142F, 0x11430, 0x11431, 0x11432, 0x11433, 0x11434, 0x11447, 0x11448, 0x11449, 0x1144A, 0x11480, 0x11481, 0x11482, 0x11483, 0x11484, 0x11485, 0x11486, 0x11487, 0x11488, 0x11489, 0x1148A, 0x1148B, 0x1148C, 0x1148D, 0x1148E, 0x1148F, 0x11490, 0x11491, 0x11492, 0x11493, 0x11494, 0x11495, 0x11496, 0x11497, 0x11498, 0x11499, 0x1149A, 0x1149B, 0x1149C, 0x1149D, 0x1149E, 0x1149F, 0x114A0, 0x114A1, 0x114A2, 0x114A3, 0x114A4, 0x114A5, 0x114A6, 0x114A7, 0x114A8, 0x114A9, 0x114AA, 0x114AB, 0x114AC, 0x114AD, 0x114AE, 0x114AF, 0x114C4, 0x114C5, 0x114C7, 0x11580, 0x11581, 0x11582, 0x11583, 0x11584, 0x11585, 0x11586, 0x11587, 0x11588, 0x11589, 0x1158A, 0x1158B, 0x1158C, 0x1158D, 0x1158E, 0x1158F, 0x11590, 0x11591, 0x11592, 0x11593, 0x11594, 0x11595, 0x11596, 0x11597, 0x11598, 0x11599, 0x1159A, 0x1159B, 0x1159C, 0x1159D, 0x1159E, 0x1159F, 0x115A0, 0x115A1, 0x115A2, 0x115A3, 0x115A4, 0x115A5, 0x115A6, 0x115A7, 0x115A8, 0x115A9, 0x115AA, 0x115AB, 0x115AC, 0x115AD, 0x115AE, 0x115D8, 0x115D9, 0x115DA, 0x115DB, 0x11600, 0x11601, 0x11602, 0x11603, 0x11604, 0x11605, 0x11606, 0x11607, 0x11608, 0x11609, 0x1160A, 0x1160B, 0x1160C, 0x1160D, 0x1160E, 0x1160F, 0x11610, 0x11611, 0x11612, 0x11613, 0x11614, 0x11615, 0x11616, 0x11617, 0x11618, 0x11619, 0x1161A, 0x1161B, 0x1161C, 0x1161D, 0x1161E, 0x1161F, 0x11620, 0x11621, 0x11622, 0x11623, 0x11624, 0x11625, 0x11626, 0x11627, 0x11628, 0x11629, 0x1162A, 0x1162B, 0x1162C, 0x1162D, 0x1162E, 0x1162F, 0x11644, 0x11680, 0x11681, 0x11682, 0x11683, 0x11684, 0x11685, 0x11686, 0x11687, 0x11688, 0x11689, 0x1168A, 0x1168B, 0x1168C, 0x1168D, 0x1168E, 0x1168F, 0x11690, 0x11691, 0x11692, 0x11693, 0x11694, 0x11695, 0x11696, 0x11697, 0x11698, 0x11699, 0x1169A, 0x1169B, 0x1169C, 0x1169D, 0x1169E, 0x1169F, 0x116A0, 0x116A1, 0x116A2, 0x116A3, 0x116A4, 0x116A5, 0x116A6, 0x116A7, 0x116A8, 0x116A9, 0x116AA, 0x118A0, 0x118A1, 0x118A2, 0x118A3, 0x118A4, 0x118A5, 0x118A6, 0x118A7, 0x118A8, 0x118A9, 0x118AA, 0x118AB, 0x118AC, 0x118AD, 0x118AE, 0x118AF, 0x118B0, 0x118B1, 0x118B2, 0x118B3, 0x118B4, 0x118B5, 0x118B6, 0x118B7, 0x118B8, 0x118B9, 0x118BA, 0x118BB, 0x118BC, 0x118BD, 0x118BE, 0x118BF, 0x118C0, 0x118C1, 0x118C2, 0x118C3, 0x118C4, 0x118C5, 0x118C6, 0x118C7, 0x118C8, 0x118C9, 0x118CA, 0x118CB, 0x118CC, 0x118CD, 0x118CE, 0x118CF, 0x118D0, 0x118D1, 0x118D2, 0x118D3, 0x118D4, 0x118D5, 0x118D6, 0x118D7, 0x118D8, 0x118D9, 0x118DA, 0x118DB, 0x118DC, 0x118DD, 0x118DE, 0x118DF, 0x118FF, 0x11AC0, 0x11AC1, 0x11AC2, 0x11AC3, 0x11AC4, 0x11AC5, 0x11AC6, 0x11AC7, 0x11AC8, 0x11AC9, 0x11ACA, 0x11ACB, 0x11ACC, 0x11ACD, 0x11ACE, 0x11ACF, 0x11AD0, 0x11AD1, 0x11AD2, 0x11AD3, 0x11AD4, 0x11AD5, 0x11AD6, 0x11AD7, 0x11AD8, 0x11AD9, 0x11ADA, 0x11ADB, 0x11ADC, 0x11ADD, 0x11ADE, 0x11ADF, 0x11AE0, 0x11AE1, 0x11AE2, 0x11AE3, 0x11AE4, 0x11AE5, 0x11AE6, 0x11AE7, 0x11AE8, 0x11AE9, 0x11AEA, 0x11AEB, 0x11AEC, 0x11AED, 0x11AEE, 0x11AEF, 0x11AF0, 0x11AF1, 0x11AF2, 0x11AF3, 0x11AF4, 0x11AF5, 0x11AF6, 0x11AF7, 0x11AF8, 0x11C00, 0x11C01, 0x11C02, 0x11C03, 0x11C04, 0x11C05, 0x11C06, 0x11C07, 0x11C08, 0x11C0A, 0x11C0B, 0x11C0C, 0x11C0D, 0x11C0E, 0x11C0F, 0x11C10, 0x11C11, 0x11C12, 0x11C13, 0x11C14, 0x11C15, 0x11C16, 0x11C17, 0x11C18, 0x11C19, 0x11C1A, 0x11C1B, 0x11C1C, 0x11C1D, 0x11C1E, 0x11C1F, 0x11C20, 0x11C21, 0x11C22, 0x11C23, 0x11C24, 0x11C25, 0x11C26, 0x11C27, 0x11C28, 0x11C29, 0x11C2A, 0x11C2B, 0x11C2C, 0x11C2D, 0x11C2E, 0x11C40, 0x11C72, 0x11C73, 0x11C74, 0x11C75, 0x11C76, 0x11C77, 0x11C78, 0x11C79, 0x11C7A, 0x11C7B, 0x11C7C, 0x11C7D, 0x11C7E, 0x11C7F, 0x11C80, 0x11C81, 0x11C82, 0x11C83, 0x11C84, 0x11C85, 0x11C86, 0x11C87, 0x11C88, 0x11C89, 0x11C8A, 0x11C8B, 0x11C8C, 0x11C8D, 0x11C8E, 0x11C8F, 0x12000, 0x12001, 0x12002, 0x12003, 0x12004, 0x12005, 0x12006, 0x12007, 0x12008, 0x12009, 0x1200A, 0x1200B, 0x1200C, 0x1200D, 0x1200E, 0x1200F, 0x12010, 0x12011, 0x12012, 0x12013, 0x12014, 0x12015, 0x12016, 0x12017, 0x12018, 0x12019, 0x1201A, 0x1201B, 0x1201C, 0x1201D, 0x1201E, 0x1201F, 0x12020, 0x12021, 0x12022, 0x12023, 0x12024, 0x12025, 0x12026, 0x12027, 0x12028, 0x12029, 0x1202A, 0x1202B, 0x1202C, 0x1202D, 0x1202E, 0x1202F, 0x12030, 0x12031, 0x12032, 0x12033, 0x12034, 0x12035, 0x12036, 0x12037, 0x12038, 0x12039, 0x1203A, 0x1203B, 0x1203C, 0x1203D, 0x1203E, 0x1203F, 0x12040, 0x12041, 0x12042, 0x12043, 0x12044, 0x12045, 0x12046, 0x12047, 0x12048, 0x12049, 0x1204A, 0x1204B, 0x1204C, 0x1204D, 0x1204E, 0x1204F, 0x12050, 0x12051, 0x12052, 0x12053, 0x12054, 0x12055, 0x12056, 0x12057, 0x12058, 0x12059, 0x1205A, 0x1205B, 0x1205C, 0x1205D, 0x1205E, 0x1205F, 0x12060, 0x12061, 0x12062, 0x12063, 0x12064, 0x12065, 0x12066, 0x12067, 0x12068, 0x12069, 0x1206A, 0x1206B, 0x1206C, 0x1206D, 0x1206E, 0x1206F, 0x12070, 0x12071, 0x12072, 0x12073, 0x12074, 0x12075, 0x12076, 0x12077, 0x12078, 0x12079, 0x1207A, 0x1207B, 0x1207C, 0x1207D, 0x1207E, 0x1207F, 0x12080, 0x12081, 0x12082, 0x12083, 0x12084, 0x12085, 0x12086, 0x12087, 0x12088, 0x12089, 0x1208A, 0x1208B, 0x1208C, 0x1208D, 0x1208E, 0x1208F, 0x12090, 0x12091, 0x12092, 0x12093, 0x12094, 0x12095, 0x12096, 0x12097, 0x12098, 0x12099, 0x1209A, 0x1209B, 0x1209C, 0x1209D, 0x1209E, 0x1209F, 0x120A0, 0x120A1, 0x120A2, 0x120A3, 0x120A4, 0x120A5, 0x120A6, 0x120A7, 0x120A8, 0x120A9, 0x120AA, 0x120AB, 0x120AC, 0x120AD, 0x120AE, 0x120AF, 0x120B0, 0x120B1, 0x120B2, 0x120B3, 0x120B4, 0x120B5, 0x120B6, 0x120B7, 0x120B8, 0x120B9, 0x120BA, 0x120BB, 0x120BC, 0x120BD, 0x120BE, 0x120BF, 0x120C0, 0x120C1, 0x120C2, 0x120C3, 0x120C4, 0x120C5, 0x120C6, 0x120C7, 0x120C8, 0x120C9, 0x120CA, 0x120CB, 0x120CC, 0x120CD, 0x120CE, 0x120CF, 0x120D0, 0x120D1, 0x120D2, 0x120D3, 0x120D4, 0x120D5, 0x120D6, 0x120D7, 0x120D8, 0x120D9, 0x120DA, 0x120DB, 0x120DC, 0x120DD, 0x120DE, 0x120DF, 0x120E0, 0x120E1, 0x120E2, 0x120E3, 0x120E4, 0x120E5, 0x120E6, 0x120E7, 0x120E8, 0x120E9, 0x120EA, 0x120EB, 0x120EC, 0x120ED, 0x120EE, 0x120EF, 0x120F0, 0x120F1, 0x120F2, 0x120F3, 0x120F4, 0x120F5, 0x120F6, 0x120F7, 0x120F8, 0x120F9, 0x120FA, 0x120FB, 0x120FC, 0x120FD, 0x120FE, 0x120FF, 0x12100, 0x12101, 0x12102, 0x12103, 0x12104, 0x12105, 0x12106, 0x12107, 0x12108, 0x12109, 0x1210A, 0x1210B, 0x1210C, 0x1210D, 0x1210E, 0x1210F, 0x12110, 0x12111, 0x12112, 0x12113, 0x12114, 0x12115, 0x12116, 0x12117, 0x12118, 0x12119, 0x1211A, 0x1211B, 0x1211C, 0x1211D, 0x1211E, 0x1211F, 0x12120, 0x12121, 0x12122, 0x12123, 0x12124, 0x12125, 0x12126, 0x12127, 0x12128, 0x12129, 0x1212A, 0x1212B, 0x1212C, 0x1212D, 0x1212E, 0x1212F, 0x12130, 0x12131, 0x12132, 0x12133, 0x12134, 0x12135, 0x12136, 0x12137, 0x12138, 0x12139, 0x1213A, 0x1213B, 0x1213C, 0x1213D, 0x1213E, 0x1213F, 0x12140, 0x12141, 0x12142, 0x12143, 0x12144, 0x12145, 0x12146, 0x12147, 0x12148, 0x12149, 0x1214A, 0x1214B, 0x1214C, 0x1214D, 0x1214E, 0x1214F, 0x12150, 0x12151, 0x12152, 0x12153, 0x12154, 0x12155, 0x12156, 0x12157, 0x12158, 0x12159, 0x1215A, 0x1215B, 0x1215C, 0x1215D, 0x1215E, 0x1215F, 0x12160, 0x12161, 0x12162, 0x12163, 0x12164, 0x12165, 0x12166, 0x12167, 0x12168, 0x12169, 0x1216A, 0x1216B, 0x1216C, 0x1216D, 0x1216E, 0x1216F, 0x12170, 0x12171, 0x12172, 0x12173, 0x12174, 0x12175, 0x12176, 0x12177, 0x12178, 0x12179, 0x1217A, 0x1217B, 0x1217C, 0x1217D, 0x1217E, 0x1217F, 0x12180, 0x12181, 0x12182, 0x12183, 0x12184, 0x12185, 0x12186, 0x12187, 0x12188, 0x12189, 0x1218A, 0x1218B, 0x1218C, 0x1218D, 0x1218E, 0x1218F, 0x12190, 0x12191, 0x12192, 0x12193, 0x12194, 0x12195, 0x12196, 0x12197, 0x12198, 0x12199, 0x1219A, 0x1219B, 0x1219C, 0x1219D, 0x1219E, 0x1219F, 0x121A0, 0x121A1, 0x121A2, 0x121A3, 0x121A4, 0x121A5, 0x121A6, 0x121A7, 0x121A8, 0x121A9, 0x121AA, 0x121AB, 0x121AC, 0x121AD, 0x121AE, 0x121AF, 0x121B0, 0x121B1, 0x121B2, 0x121B3, 0x121B4, 0x121B5, 0x121B6, 0x121B7, 0x121B8, 0x121B9, 0x121BA, 0x121BB, 0x121BC, 0x121BD, 0x121BE, 0x121BF, 0x121C0, 0x121C1, 0x121C2, 0x121C3, 0x121C4, 0x121C5, 0x121C6, 0x121C7, 0x121C8, 0x121C9, 0x121CA, 0x121CB, 0x121CC, 0x121CD, 0x121CE, 0x121CF, 0x121D0, 0x121D1, 0x121D2, 0x121D3, 0x121D4, 0x121D5, 0x121D6, 0x121D7, 0x121D8, 0x121D9, 0x121DA, 0x121DB, 0x121DC, 0x121DD, 0x121DE, 0x121DF, 0x121E0, 0x121E1, 0x121E2, 0x121E3, 0x121E4, 0x121E5, 0x121E6, 0x121E7, 0x121E8, 0x121E9, 0x121EA, 0x121EB, 0x121EC, 0x121ED, 0x121EE, 0x121EF, 0x121F0, 0x121F1, 0x121F2, 0x121F3, 0x121F4, 0x121F5, 0x121F6, 0x121F7, 0x121F8, 0x121F9, 0x121FA, 0x121FB, 0x121FC, 0x121FD, 0x121FE, 0x121FF, 0x12200, 0x12201, 0x12202, 0x12203, 0x12204, 0x12205, 0x12206, 0x12207, 0x12208, 0x12209, 0x1220A, 0x1220B, 0x1220C, 0x1220D, 0x1220E, 0x1220F, 0x12210, 0x12211, 0x12212, 0x12213, 0x12214, 0x12215, 0x12216, 0x12217, 0x12218, 0x12219, 0x1221A, 0x1221B, 0x1221C, 0x1221D, 0x1221E, 0x1221F, 0x12220, 0x12221, 0x12222, 0x12223, 0x12224, 0x12225, 0x12226, 0x12227, 0x12228, 0x12229, 0x1222A, 0x1222B, 0x1222C, 0x1222D, 0x1222E, 0x1222F, 0x12230, 0x12231, 0x12232, 0x12233, 0x12234, 0x12235, 0x12236, 0x12237, 0x12238, 0x12239, 0x1223A, 0x1223B, 0x1223C, 0x1223D, 0x1223E, 0x1223F, 0x12240, 0x12241, 0x12242, 0x12243, 0x12244, 0x12245, 0x12246, 0x12247, 0x12248, 0x12249, 0x1224A, 0x1224B, 0x1224C, 0x1224D, 0x1224E, 0x1224F, 0x12250, 0x12251, 0x12252, 0x12253, 0x12254, 0x12255, 0x12256, 0x12257, 0x12258, 0x12259, 0x1225A, 0x1225B, 0x1225C, 0x1225D, 0x1225E, 0x1225F, 0x12260, 0x12261, 0x12262, 0x12263, 0x12264, 0x12265, 0x12266, 0x12267, 0x12268, 0x12269, 0x1226A, 0x1226B, 0x1226C, 0x1226D, 0x1226E, 0x1226F, 0x12270, 0x12271, 0x12272, 0x12273, 0x12274, 0x12275, 0x12276, 0x12277, 0x12278, 0x12279, 0x1227A, 0x1227B, 0x1227C, 0x1227D, 0x1227E, 0x1227F, 0x12280, 0x12281, 0x12282, 0x12283, 0x12284, 0x12285, 0x12286, 0x12287, 0x12288, 0x12289, 0x1228A, 0x1228B, 0x1228C, 0x1228D, 0x1228E, 0x1228F, 0x12290, 0x12291, 0x12292, 0x12293, 0x12294, 0x12295, 0x12296, 0x12297, 0x12298, 0x12299, 0x1229A, 0x1229B, 0x1229C, 0x1229D, 0x1229E, 0x1229F, 0x122A0, 0x122A1, 0x122A2, 0x122A3, 0x122A4, 0x122A5, 0x122A6, 0x122A7, 0x122A8, 0x122A9, 0x122AA, 0x122AB, 0x122AC, 0x122AD, 0x122AE, 0x122AF, 0x122B0, 0x122B1, 0x122B2, 0x122B3, 0x122B4, 0x122B5, 0x122B6, 0x122B7, 0x122B8, 0x122B9, 0x122BA, 0x122BB, 0x122BC, 0x122BD, 0x122BE, 0x122BF, 0x122C0, 0x122C1, 0x122C2, 0x122C3, 0x122C4, 0x122C5, 0x122C6, 0x122C7, 0x122C8, 0x122C9, 0x122CA, 0x122CB, 0x122CC, 0x122CD, 0x122CE, 0x122CF, 0x122D0, 0x122D1, 0x122D2, 0x122D3, 0x122D4, 0x122D5, 0x122D6, 0x122D7, 0x122D8, 0x122D9, 0x122DA, 0x122DB, 0x122DC, 0x122DD, 0x122DE, 0x122DF, 0x122E0, 0x122E1, 0x122E2, 0x122E3, 0x122E4, 0x122E5, 0x122E6, 0x122E7, 0x122E8, 0x122E9, 0x122EA, 0x122EB, 0x122EC, 0x122ED, 0x122EE, 0x122EF, 0x122F0, 0x122F1, 0x122F2, 0x122F3, 0x122F4, 0x122F5, 0x122F6, 0x122F7, 0x122F8, 0x122F9, 0x122FA, 0x122FB, 0x122FC, 0x122FD, 0x122FE, 0x122FF, 0x12300, 0x12301, 0x12302, 0x12303, 0x12304, 0x12305, 0x12306, 0x12307, 0x12308, 0x12309, 0x1230A, 0x1230B, 0x1230C, 0x1230D, 0x1230E, 0x1230F, 0x12310, 0x12311, 0x12312, 0x12313, 0x12314, 0x12315, 0x12316, 0x12317, 0x12318, 0x12319, 0x1231A, 0x1231B, 0x1231C, 0x1231D, 0x1231E, 0x1231F, 0x12320, 0x12321, 0x12322, 0x12323, 0x12324, 0x12325, 0x12326, 0x12327, 0x12328, 0x12329, 0x1232A, 0x1232B, 0x1232C, 0x1232D, 0x1232E, 0x1232F, 0x12330, 0x12331, 0x12332, 0x12333, 0x12334, 0x12335, 0x12336, 0x12337, 0x12338, 0x12339, 0x1233A, 0x1233B, 0x1233C, 0x1233D, 0x1233E, 0x1233F, 0x12340, 0x12341, 0x12342, 0x12343, 0x12344, 0x12345, 0x12346, 0x12347, 0x12348, 0x12349, 0x1234A, 0x1234B, 0x1234C, 0x1234D, 0x1234E, 0x1234F, 0x12350, 0x12351, 0x12352, 0x12353, 0x12354, 0x12355, 0x12356, 0x12357, 0x12358, 0x12359, 0x1235A, 0x1235B, 0x1235C, 0x1235D, 0x1235E, 0x1235F, 0x12360, 0x12361, 0x12362, 0x12363, 0x12364, 0x12365, 0x12366, 0x12367, 0x12368, 0x12369, 0x1236A, 0x1236B, 0x1236C, 0x1236D, 0x1236E, 0x1236F, 0x12370, 0x12371, 0x12372, 0x12373, 0x12374, 0x12375, 0x12376, 0x12377, 0x12378, 0x12379, 0x1237A, 0x1237B, 0x1237C, 0x1237D, 0x1237E, 0x1237F, 0x12380, 0x12381, 0x12382, 0x12383, 0x12384, 0x12385, 0x12386, 0x12387, 0x12388, 0x12389, 0x1238A, 0x1238B, 0x1238C, 0x1238D, 0x1238E, 0x1238F, 0x12390, 0x12391, 0x12392, 0x12393, 0x12394, 0x12395, 0x12396, 0x12397, 0x12398, 0x12399, 0x12400, 0x12401, 0x12402, 0x12403, 0x12404, 0x12405, 0x12406, 0x12407, 0x12408, 0x12409, 0x1240A, 0x1240B, 0x1240C, 0x1240D, 0x1240E, 0x1240F, 0x12410, 0x12411, 0x12412, 0x12413, 0x12414, 0x12415, 0x12416, 0x12417, 0x12418, 0x12419, 0x1241A, 0x1241B, 0x1241C, 0x1241D, 0x1241E, 0x1241F, 0x12420, 0x12421, 0x12422, 0x12423, 0x12424, 0x12425, 0x12426, 0x12427, 0x12428, 0x12429, 0x1242A, 0x1242B, 0x1242C, 0x1242D, 0x1242E, 0x1242F, 0x12430, 0x12431, 0x12432, 0x12433, 0x12434, 0x12435, 0x12436, 0x12437, 0x12438, 0x12439, 0x1243A, 0x1243B, 0x1243C, 0x1243D, 0x1243E, 0x1243F, 0x12440, 0x12441, 0x12442, 0x12443, 0x12444, 0x12445, 0x12446, 0x12447, 0x12448, 0x12449, 0x1244A, 0x1244B, 0x1244C, 0x1244D, 0x1244E, 0x1244F, 0x12450, 0x12451, 0x12452, 0x12453, 0x12454, 0x12455, 0x12456, 0x12457, 0x12458, 0x12459, 0x1245A, 0x1245B, 0x1245C, 0x1245D, 0x1245E, 0x1245F, 0x12460, 0x12461, 0x12462, 0x12463, 0x12464, 0x12465, 0x12466, 0x12467, 0x12468, 0x12469, 0x1246A, 0x1246B, 0x1246C, 0x1246D, 0x1246E, 0x12480, 0x12481, 0x12482, 0x12483, 0x12484, 0x12485, 0x12486, 0x12487, 0x12488, 0x12489, 0x1248A, 0x1248B, 0x1248C, 0x1248D, 0x1248E, 0x1248F, 0x12490, 0x12491, 0x12492, 0x12493, 0x12494, 0x12495, 0x12496, 0x12497, 0x12498, 0x12499, 0x1249A, 0x1249B, 0x1249C, 0x1249D, 0x1249E, 0x1249F, 0x124A0, 0x124A1, 0x124A2, 0x124A3, 0x124A4, 0x124A5, 0x124A6, 0x124A7, 0x124A8, 0x124A9, 0x124AA, 0x124AB, 0x124AC, 0x124AD, 0x124AE, 0x124AF, 0x124B0, 0x124B1, 0x124B2, 0x124B3, 0x124B4, 0x124B5, 0x124B6, 0x124B7, 0x124B8, 0x124B9, 0x124BA, 0x124BB, 0x124BC, 0x124BD, 0x124BE, 0x124BF, 0x124C0, 0x124C1, 0x124C2, 0x124C3, 0x124C4, 0x124C5, 0x124C6, 0x124C7, 0x124C8, 0x124C9, 0x124CA, 0x124CB, 0x124CC, 0x124CD, 0x124CE, 0x124CF, 0x124D0, 0x124D1, 0x124D2, 0x124D3, 0x124D4, 0x124D5, 0x124D6, 0x124D7, 0x124D8, 0x124D9, 0x124DA, 0x124DB, 0x124DC, 0x124DD, 0x124DE, 0x124DF, 0x124E0, 0x124E1, 0x124E2, 0x124E3, 0x124E4, 0x124E5, 0x124E6, 0x124E7, 0x124E8, 0x124E9, 0x124EA, 0x124EB, 0x124EC, 0x124ED, 0x124EE, 0x124EF, 0x124F0, 0x124F1, 0x124F2, 0x124F3, 0x124F4, 0x124F5, 0x124F6, 0x124F7, 0x124F8, 0x124F9, 0x124FA, 0x124FB, 0x124FC, 0x124FD, 0x124FE, 0x124FF, 0x12500, 0x12501, 0x12502, 0x12503, 0x12504, 0x12505, 0x12506, 0x12507, 0x12508, 0x12509, 0x1250A, 0x1250B, 0x1250C, 0x1250D, 0x1250E, 0x1250F, 0x12510, 0x12511, 0x12512, 0x12513, 0x12514, 0x12515, 0x12516, 0x12517, 0x12518, 0x12519, 0x1251A, 0x1251B, 0x1251C, 0x1251D, 0x1251E, 0x1251F, 0x12520, 0x12521, 0x12522, 0x12523, 0x12524, 0x12525, 0x12526, 0x12527, 0x12528, 0x12529, 0x1252A, 0x1252B, 0x1252C, 0x1252D, 0x1252E, 0x1252F, 0x12530, 0x12531, 0x12532, 0x12533, 0x12534, 0x12535, 0x12536, 0x12537, 0x12538, 0x12539, 0x1253A, 0x1253B, 0x1253C, 0x1253D, 0x1253E, 0x1253F, 0x12540, 0x12541, 0x12542, 0x12543, 0x13000, 0x13001, 0x13002, 0x13003, 0x13004, 0x13005, 0x13006, 0x13007, 0x13008, 0x13009, 0x1300A, 0x1300B, 0x1300C, 0x1300D, 0x1300E, 0x1300F, 0x13010, 0x13011, 0x13012, 0x13013, 0x13014, 0x13015, 0x13016, 0x13017, 0x13018, 0x13019, 0x1301A, 0x1301B, 0x1301C, 0x1301D, 0x1301E, 0x1301F, 0x13020, 0x13021, 0x13022, 0x13023, 0x13024, 0x13025, 0x13026, 0x13027, 0x13028, 0x13029, 0x1302A, 0x1302B, 0x1302C, 0x1302D, 0x1302E, 0x1302F, 0x13030, 0x13031, 0x13032, 0x13033, 0x13034, 0x13035, 0x13036, 0x13037, 0x13038, 0x13039, 0x1303A, 0x1303B, 0x1303C, 0x1303D, 0x1303E, 0x1303F, 0x13040, 0x13041, 0x13042, 0x13043, 0x13044, 0x13045, 0x13046, 0x13047, 0x13048, 0x13049, 0x1304A, 0x1304B, 0x1304C, 0x1304D, 0x1304E, 0x1304F, 0x13050, 0x13051, 0x13052, 0x13053, 0x13054, 0x13055, 0x13056, 0x13057, 0x13058, 0x13059, 0x1305A, 0x1305B, 0x1305C, 0x1305D, 0x1305E, 0x1305F, 0x13060, 0x13061, 0x13062, 0x13063, 0x13064, 0x13065, 0x13066, 0x13067, 0x13068, 0x13069, 0x1306A, 0x1306B, 0x1306C, 0x1306D, 0x1306E, 0x1306F, 0x13070, 0x13071, 0x13072, 0x13073, 0x13074, 0x13075, 0x13076, 0x13077, 0x13078, 0x13079, 0x1307A, 0x1307B, 0x1307C, 0x1307D, 0x1307E, 0x1307F, 0x13080, 0x13081, 0x13082, 0x13083, 0x13084, 0x13085, 0x13086, 0x13087, 0x13088, 0x13089, 0x1308A, 0x1308B, 0x1308C, 0x1308D, 0x1308E, 0x1308F, 0x13090, 0x13091, 0x13092, 0x13093, 0x13094, 0x13095, 0x13096, 0x13097, 0x13098, 0x13099, 0x1309A, 0x1309B, 0x1309C, 0x1309D, 0x1309E, 0x1309F, 0x130A0, 0x130A1, 0x130A2, 0x130A3, 0x130A4, 0x130A5, 0x130A6, 0x130A7, 0x130A8, 0x130A9, 0x130AA, 0x130AB, 0x130AC, 0x130AD, 0x130AE, 0x130AF, 0x130B0, 0x130B1, 0x130B2, 0x130B3, 0x130B4, 0x130B5, 0x130B6, 0x130B7, 0x130B8, 0x130B9, 0x130BA, 0x130BB, 0x130BC, 0x130BD, 0x130BE, 0x130BF, 0x130C0, 0x130C1, 0x130C2, 0x130C3, 0x130C4, 0x130C5, 0x130C6, 0x130C7, 0x130C8, 0x130C9, 0x130CA, 0x130CB, 0x130CC, 0x130CD, 0x130CE, 0x130CF, 0x130D0, 0x130D1, 0x130D2, 0x130D3, 0x130D4, 0x130D5, 0x130D6, 0x130D7, 0x130D8, 0x130D9, 0x130DA, 0x130DB, 0x130DC, 0x130DD, 0x130DE, 0x130DF, 0x130E0, 0x130E1, 0x130E2, 0x130E3, 0x130E4, 0x130E5, 0x130E6, 0x130E7, 0x130E8, 0x130E9, 0x130EA, 0x130EB, 0x130EC, 0x130ED, 0x130EE, 0x130EF, 0x130F0, 0x130F1, 0x130F2, 0x130F3, 0x130F4, 0x130F5, 0x130F6, 0x130F7, 0x130F8, 0x130F9, 0x130FA, 0x130FB, 0x130FC, 0x130FD, 0x130FE, 0x130FF, 0x13100, 0x13101, 0x13102, 0x13103, 0x13104, 0x13105, 0x13106, 0x13107, 0x13108, 0x13109, 0x1310A, 0x1310B, 0x1310C, 0x1310D, 0x1310E, 0x1310F, 0x13110, 0x13111, 0x13112, 0x13113, 0x13114, 0x13115, 0x13116, 0x13117, 0x13118, 0x13119, 0x1311A, 0x1311B, 0x1311C, 0x1311D, 0x1311E, 0x1311F, 0x13120, 0x13121, 0x13122, 0x13123, 0x13124, 0x13125, 0x13126, 0x13127, 0x13128, 0x13129, 0x1312A, 0x1312B, 0x1312C, 0x1312D, 0x1312E, 0x1312F, 0x13130, 0x13131, 0x13132, 0x13133, 0x13134, 0x13135, 0x13136, 0x13137, 0x13138, 0x13139, 0x1313A, 0x1313B, 0x1313C, 0x1313D, 0x1313E, 0x1313F, 0x13140, 0x13141, 0x13142, 0x13143, 0x13144, 0x13145, 0x13146, 0x13147, 0x13148, 0x13149, 0x1314A, 0x1314B, 0x1314C, 0x1314D, 0x1314E, 0x1314F, 0x13150, 0x13151, 0x13152, 0x13153, 0x13154, 0x13155, 0x13156, 0x13157, 0x13158, 0x13159, 0x1315A, 0x1315B, 0x1315C, 0x1315D, 0x1315E, 0x1315F, 0x13160, 0x13161, 0x13162, 0x13163, 0x13164, 0x13165, 0x13166, 0x13167, 0x13168, 0x13169, 0x1316A, 0x1316B, 0x1316C, 0x1316D, 0x1316E, 0x1316F, 0x13170, 0x13171, 0x13172, 0x13173, 0x13174, 0x13175, 0x13176, 0x13177, 0x13178, 0x13179, 0x1317A, 0x1317B, 0x1317C, 0x1317D, 0x1317E, 0x1317F, 0x13180, 0x13181, 0x13182, 0x13183, 0x13184, 0x13185, 0x13186, 0x13187, 0x13188, 0x13189, 0x1318A, 0x1318B, 0x1318C, 0x1318D, 0x1318E, 0x1318F, 0x13190, 0x13191, 0x13192, 0x13193, 0x13194, 0x13195, 0x13196, 0x13197, 0x13198, 0x13199, 0x1319A, 0x1319B, 0x1319C, 0x1319D, 0x1319E, 0x1319F, 0x131A0, 0x131A1, 0x131A2, 0x131A3, 0x131A4, 0x131A5, 0x131A6, 0x131A7, 0x131A8, 0x131A9, 0x131AA, 0x131AB, 0x131AC, 0x131AD, 0x131AE, 0x131AF, 0x131B0, 0x131B1, 0x131B2, 0x131B3, 0x131B4, 0x131B5, 0x131B6, 0x131B7, 0x131B8, 0x131B9, 0x131BA, 0x131BB, 0x131BC, 0x131BD, 0x131BE, 0x131BF, 0x131C0, 0x131C1, 0x131C2, 0x131C3, 0x131C4, 0x131C5, 0x131C6, 0x131C7, 0x131C8, 0x131C9, 0x131CA, 0x131CB, 0x131CC, 0x131CD, 0x131CE, 0x131CF, 0x131D0, 0x131D1, 0x131D2, 0x131D3, 0x131D4, 0x131D5, 0x131D6, 0x131D7, 0x131D8, 0x131D9, 0x131DA, 0x131DB, 0x131DC, 0x131DD, 0x131DE, 0x131DF, 0x131E0, 0x131E1, 0x131E2, 0x131E3, 0x131E4, 0x131E5, 0x131E6, 0x131E7, 0x131E8, 0x131E9, 0x131EA, 0x131EB, 0x131EC, 0x131ED, 0x131EE, 0x131EF, 0x131F0, 0x131F1, 0x131F2, 0x131F3, 0x131F4, 0x131F5, 0x131F6, 0x131F7, 0x131F8, 0x131F9, 0x131FA, 0x131FB, 0x131FC, 0x131FD, 0x131FE, 0x131FF, 0x13200, 0x13201, 0x13202, 0x13203, 0x13204, 0x13205, 0x13206, 0x13207, 0x13208, 0x13209, 0x1320A, 0x1320B, 0x1320C, 0x1320D, 0x1320E, 0x1320F, 0x13210, 0x13211, 0x13212, 0x13213, 0x13214, 0x13215, 0x13216, 0x13217, 0x13218, 0x13219, 0x1321A, 0x1321B, 0x1321C, 0x1321D, 0x1321E, 0x1321F, 0x13220, 0x13221, 0x13222, 0x13223, 0x13224, 0x13225, 0x13226, 0x13227, 0x13228, 0x13229, 0x1322A, 0x1322B, 0x1322C, 0x1322D, 0x1322E, 0x1322F, 0x13230, 0x13231, 0x13232, 0x13233, 0x13234, 0x13235, 0x13236, 0x13237, 0x13238, 0x13239, 0x1323A, 0x1323B, 0x1323C, 0x1323D, 0x1323E, 0x1323F, 0x13240, 0x13241, 0x13242, 0x13243, 0x13244, 0x13245, 0x13246, 0x13247, 0x13248, 0x13249, 0x1324A, 0x1324B, 0x1324C, 0x1324D, 0x1324E, 0x1324F, 0x13250, 0x13251, 0x13252, 0x13253, 0x13254, 0x13255, 0x13256, 0x13257, 0x13258, 0x13259, 0x1325A, 0x1325B, 0x1325C, 0x1325D, 0x1325E, 0x1325F, 0x13260, 0x13261, 0x13262, 0x13263, 0x13264, 0x13265, 0x13266, 0x13267, 0x13268, 0x13269, 0x1326A, 0x1326B, 0x1326C, 0x1326D, 0x1326E, 0x1326F, 0x13270, 0x13271, 0x13272, 0x13273, 0x13274, 0x13275, 0x13276, 0x13277, 0x13278, 0x13279, 0x1327A, 0x1327B, 0x1327C, 0x1327D, 0x1327E, 0x1327F, 0x13280, 0x13281, 0x13282, 0x13283, 0x13284, 0x13285, 0x13286, 0x13287, 0x13288, 0x13289, 0x1328A, 0x1328B, 0x1328C, 0x1328D, 0x1328E, 0x1328F, 0x13290, 0x13291, 0x13292, 0x13293, 0x13294, 0x13295, 0x13296, 0x13297, 0x13298, 0x13299, 0x1329A, 0x1329B, 0x1329C, 0x1329D, 0x1329E, 0x1329F, 0x132A0, 0x132A1, 0x132A2, 0x132A3, 0x132A4, 0x132A5, 0x132A6, 0x132A7, 0x132A8, 0x132A9, 0x132AA, 0x132AB, 0x132AC, 0x132AD, 0x132AE, 0x132AF, 0x132B0, 0x132B1, 0x132B2, 0x132B3, 0x132B4, 0x132B5, 0x132B6, 0x132B7, 0x132B8, 0x132B9, 0x132BA, 0x132BB, 0x132BC, 0x132BD, 0x132BE, 0x132BF, 0x132C0, 0x132C1, 0x132C2, 0x132C3, 0x132C4, 0x132C5, 0x132C6, 0x132C7, 0x132C8, 0x132C9, 0x132CA, 0x132CB, 0x132CC, 0x132CD, 0x132CE, 0x132CF, 0x132D0, 0x132D1, 0x132D2, 0x132D3, 0x132D4, 0x132D5, 0x132D6, 0x132D7, 0x132D8, 0x132D9, 0x132DA, 0x132DB, 0x132DC, 0x132DD, 0x132DE, 0x132DF, 0x132E0, 0x132E1, 0x132E2, 0x132E3, 0x132E4, 0x132E5, 0x132E6, 0x132E7, 0x132E8, 0x132E9, 0x132EA, 0x132EB, 0x132EC, 0x132ED, 0x132EE, 0x132EF, 0x132F0, 0x132F1, 0x132F2, 0x132F3, 0x132F4, 0x132F5, 0x132F6, 0x132F7, 0x132F8, 0x132F9, 0x132FA, 0x132FB, 0x132FC, 0x132FD, 0x132FE, 0x132FF, 0x13300, 0x13301, 0x13302, 0x13303, 0x13304, 0x13305, 0x13306, 0x13307, 0x13308, 0x13309, 0x1330A, 0x1330B, 0x1330C, 0x1330D, 0x1330E, 0x1330F, 0x13310, 0x13311, 0x13312, 0x13313, 0x13314, 0x13315, 0x13316, 0x13317, 0x13318, 0x13319, 0x1331A, 0x1331B, 0x1331C, 0x1331D, 0x1331E, 0x1331F, 0x13320, 0x13321, 0x13322, 0x13323, 0x13324, 0x13325, 0x13326, 0x13327, 0x13328, 0x13329, 0x1332A, 0x1332B, 0x1332C, 0x1332D, 0x1332E, 0x1332F, 0x13330, 0x13331, 0x13332, 0x13333, 0x13334, 0x13335, 0x13336, 0x13337, 0x13338, 0x13339, 0x1333A, 0x1333B, 0x1333C, 0x1333D, 0x1333E, 0x1333F, 0x13340, 0x13341, 0x13342, 0x13343, 0x13344, 0x13345, 0x13346, 0x13347, 0x13348, 0x13349, 0x1334A, 0x1334B, 0x1334C, 0x1334D, 0x1334E, 0x1334F, 0x13350, 0x13351, 0x13352, 0x13353, 0x13354, 0x13355, 0x13356, 0x13357, 0x13358, 0x13359, 0x1335A, 0x1335B, 0x1335C, 0x1335D, 0x1335E, 0x1335F, 0x13360, 0x13361, 0x13362, 0x13363, 0x13364, 0x13365, 0x13366, 0x13367, 0x13368, 0x13369, 0x1336A, 0x1336B, 0x1336C, 0x1336D, 0x1336E, 0x1336F, 0x13370, 0x13371, 0x13372, 0x13373, 0x13374, 0x13375, 0x13376, 0x13377, 0x13378, 0x13379, 0x1337A, 0x1337B, 0x1337C, 0x1337D, 0x1337E, 0x1337F, 0x13380, 0x13381, 0x13382, 0x13383, 0x13384, 0x13385, 0x13386, 0x13387, 0x13388, 0x13389, 0x1338A, 0x1338B, 0x1338C, 0x1338D, 0x1338E, 0x1338F, 0x13390, 0x13391, 0x13392, 0x13393, 0x13394, 0x13395, 0x13396, 0x13397, 0x13398, 0x13399, 0x1339A, 0x1339B, 0x1339C, 0x1339D, 0x1339E, 0x1339F, 0x133A0, 0x133A1, 0x133A2, 0x133A3, 0x133A4, 0x133A5, 0x133A6, 0x133A7, 0x133A8, 0x133A9, 0x133AA, 0x133AB, 0x133AC, 0x133AD, 0x133AE, 0x133AF, 0x133B0, 0x133B1, 0x133B2, 0x133B3, 0x133B4, 0x133B5, 0x133B6, 0x133B7, 0x133B8, 0x133B9, 0x133BA, 0x133BB, 0x133BC, 0x133BD, 0x133BE, 0x133BF, 0x133C0, 0x133C1, 0x133C2, 0x133C3, 0x133C4, 0x133C5, 0x133C6, 0x133C7, 0x133C8, 0x133C9, 0x133CA, 0x133CB, 0x133CC, 0x133CD, 0x133CE, 0x133CF, 0x133D0, 0x133D1, 0x133D2, 0x133D3, 0x133D4, 0x133D5, 0x133D6, 0x133D7, 0x133D8, 0x133D9, 0x133DA, 0x133DB, 0x133DC, 0x133DD, 0x133DE, 0x133DF, 0x133E0, 0x133E1, 0x133E2, 0x133E3, 0x133E4, 0x133E5, 0x133E6, 0x133E7, 0x133E8, 0x133E9, 0x133EA, 0x133EB, 0x133EC, 0x133ED, 0x133EE, 0x133EF, 0x133F0, 0x133F1, 0x133F2, 0x133F3, 0x133F4, 0x133F5, 0x133F6, 0x133F7, 0x133F8, 0x133F9, 0x133FA, 0x133FB, 0x133FC, 0x133FD, 0x133FE, 0x133FF, 0x13400, 0x13401, 0x13402, 0x13403, 0x13404, 0x13405, 0x13406, 0x13407, 0x13408, 0x13409, 0x1340A, 0x1340B, 0x1340C, 0x1340D, 0x1340E, 0x1340F, 0x13410, 0x13411, 0x13412, 0x13413, 0x13414, 0x13415, 0x13416, 0x13417, 0x13418, 0x13419, 0x1341A, 0x1341B, 0x1341C, 0x1341D, 0x1341E, 0x1341F, 0x13420, 0x13421, 0x13422, 0x13423, 0x13424, 0x13425, 0x13426, 0x13427, 0x13428, 0x13429, 0x1342A, 0x1342B, 0x1342C, 0x1342D, 0x1342E, 0x14400, 0x14401, 0x14402, 0x14403, 0x14404, 0x14405, 0x14406, 0x14407, 0x14408, 0x14409, 0x1440A, 0x1440B, 0x1440C, 0x1440D, 0x1440E, 0x1440F, 0x14410, 0x14411, 0x14412, 0x14413, 0x14414, 0x14415, 0x14416, 0x14417, 0x14418, 0x14419, 0x1441A, 0x1441B, 0x1441C, 0x1441D, 0x1441E, 0x1441F, 0x14420, 0x14421, 0x14422, 0x14423, 0x14424, 0x14425, 0x14426, 0x14427, 0x14428, 0x14429, 0x1442A, 0x1442B, 0x1442C, 0x1442D, 0x1442E, 0x1442F, 0x14430, 0x14431, 0x14432, 0x14433, 0x14434, 0x14435, 0x14436, 0x14437, 0x14438, 0x14439, 0x1443A, 0x1443B, 0x1443C, 0x1443D, 0x1443E, 0x1443F, 0x14440, 0x14441, 0x14442, 0x14443, 0x14444, 0x14445, 0x14446, 0x14447, 0x14448, 0x14449, 0x1444A, 0x1444B, 0x1444C, 0x1444D, 0x1444E, 0x1444F, 0x14450, 0x14451, 0x14452, 0x14453, 0x14454, 0x14455, 0x14456, 0x14457, 0x14458, 0x14459, 0x1445A, 0x1445B, 0x1445C, 0x1445D, 0x1445E, 0x1445F, 0x14460, 0x14461, 0x14462, 0x14463, 0x14464, 0x14465, 0x14466, 0x14467, 0x14468, 0x14469, 0x1446A, 0x1446B, 0x1446C, 0x1446D, 0x1446E, 0x1446F, 0x14470, 0x14471, 0x14472, 0x14473, 0x14474, 0x14475, 0x14476, 0x14477, 0x14478, 0x14479, 0x1447A, 0x1447B, 0x1447C, 0x1447D, 0x1447E, 0x1447F, 0x14480, 0x14481, 0x14482, 0x14483, 0x14484, 0x14485, 0x14486, 0x14487, 0x14488, 0x14489, 0x1448A, 0x1448B, 0x1448C, 0x1448D, 0x1448E, 0x1448F, 0x14490, 0x14491, 0x14492, 0x14493, 0x14494, 0x14495, 0x14496, 0x14497, 0x14498, 0x14499, 0x1449A, 0x1449B, 0x1449C, 0x1449D, 0x1449E, 0x1449F, 0x144A0, 0x144A1, 0x144A2, 0x144A3, 0x144A4, 0x144A5, 0x144A6, 0x144A7, 0x144A8, 0x144A9, 0x144AA, 0x144AB, 0x144AC, 0x144AD, 0x144AE, 0x144AF, 0x144B0, 0x144B1, 0x144B2, 0x144B3, 0x144B4, 0x144B5, 0x144B6, 0x144B7, 0x144B8, 0x144B9, 0x144BA, 0x144BB, 0x144BC, 0x144BD, 0x144BE, 0x144BF, 0x144C0, 0x144C1, 0x144C2, 0x144C3, 0x144C4, 0x144C5, 0x144C6, 0x144C7, 0x144C8, 0x144C9, 0x144CA, 0x144CB, 0x144CC, 0x144CD, 0x144CE, 0x144CF, 0x144D0, 0x144D1, 0x144D2, 0x144D3, 0x144D4, 0x144D5, 0x144D6, 0x144D7, 0x144D8, 0x144D9, 0x144DA, 0x144DB, 0x144DC, 0x144DD, 0x144DE, 0x144DF, 0x144E0, 0x144E1, 0x144E2, 0x144E3, 0x144E4, 0x144E5, 0x144E6, 0x144E7, 0x144E8, 0x144E9, 0x144EA, 0x144EB, 0x144EC, 0x144ED, 0x144EE, 0x144EF, 0x144F0, 0x144F1, 0x144F2, 0x144F3, 0x144F4, 0x144F5, 0x144F6, 0x144F7, 0x144F8, 0x144F9, 0x144FA, 0x144FB, 0x144FC, 0x144FD, 0x144FE, 0x144FF, 0x14500, 0x14501, 0x14502, 0x14503, 0x14504, 0x14505, 0x14506, 0x14507, 0x14508, 0x14509, 0x1450A, 0x1450B, 0x1450C, 0x1450D, 0x1450E, 0x1450F, 0x14510, 0x14511, 0x14512, 0x14513, 0x14514, 0x14515, 0x14516, 0x14517, 0x14518, 0x14519, 0x1451A, 0x1451B, 0x1451C, 0x1451D, 0x1451E, 0x1451F, 0x14520, 0x14521, 0x14522, 0x14523, 0x14524, 0x14525, 0x14526, 0x14527, 0x14528, 0x14529, 0x1452A, 0x1452B, 0x1452C, 0x1452D, 0x1452E, 0x1452F, 0x14530, 0x14531, 0x14532, 0x14533, 0x14534, 0x14535, 0x14536, 0x14537, 0x14538, 0x14539, 0x1453A, 0x1453B, 0x1453C, 0x1453D, 0x1453E, 0x1453F, 0x14540, 0x14541, 0x14542, 0x14543, 0x14544, 0x14545, 0x14546, 0x14547, 0x14548, 0x14549, 0x1454A, 0x1454B, 0x1454C, 0x1454D, 0x1454E, 0x1454F, 0x14550, 0x14551, 0x14552, 0x14553, 0x14554, 0x14555, 0x14556, 0x14557, 0x14558, 0x14559, 0x1455A, 0x1455B, 0x1455C, 0x1455D, 0x1455E, 0x1455F, 0x14560, 0x14561, 0x14562, 0x14563, 0x14564, 0x14565, 0x14566, 0x14567, 0x14568, 0x14569, 0x1456A, 0x1456B, 0x1456C, 0x1456D, 0x1456E, 0x1456F, 0x14570, 0x14571, 0x14572, 0x14573, 0x14574, 0x14575, 0x14576, 0x14577, 0x14578, 0x14579, 0x1457A, 0x1457B, 0x1457C, 0x1457D, 0x1457E, 0x1457F, 0x14580, 0x14581, 0x14582, 0x14583, 0x14584, 0x14585, 0x14586, 0x14587, 0x14588, 0x14589, 0x1458A, 0x1458B, 0x1458C, 0x1458D, 0x1458E, 0x1458F, 0x14590, 0x14591, 0x14592, 0x14593, 0x14594, 0x14595, 0x14596, 0x14597, 0x14598, 0x14599, 0x1459A, 0x1459B, 0x1459C, 0x1459D, 0x1459E, 0x1459F, 0x145A0, 0x145A1, 0x145A2, 0x145A3, 0x145A4, 0x145A5, 0x145A6, 0x145A7, 0x145A8, 0x145A9, 0x145AA, 0x145AB, 0x145AC, 0x145AD, 0x145AE, 0x145AF, 0x145B0, 0x145B1, 0x145B2, 0x145B3, 0x145B4, 0x145B5, 0x145B6, 0x145B7, 0x145B8, 0x145B9, 0x145BA, 0x145BB, 0x145BC, 0x145BD, 0x145BE, 0x145BF, 0x145C0, 0x145C1, 0x145C2, 0x145C3, 0x145C4, 0x145C5, 0x145C6, 0x145C7, 0x145C8, 0x145C9, 0x145CA, 0x145CB, 0x145CC, 0x145CD, 0x145CE, 0x145CF, 0x145D0, 0x145D1, 0x145D2, 0x145D3, 0x145D4, 0x145D5, 0x145D6, 0x145D7, 0x145D8, 0x145D9, 0x145DA, 0x145DB, 0x145DC, 0x145DD, 0x145DE, 0x145DF, 0x145E0, 0x145E1, 0x145E2, 0x145E3, 0x145E4, 0x145E5, 0x145E6, 0x145E7, 0x145E8, 0x145E9, 0x145EA, 0x145EB, 0x145EC, 0x145ED, 0x145EE, 0x145EF, 0x145F0, 0x145F1, 0x145F2, 0x145F3, 0x145F4, 0x145F5, 0x145F6, 0x145F7, 0x145F8, 0x145F9, 0x145FA, 0x145FB, 0x145FC, 0x145FD, 0x145FE, 0x145FF, 0x14600, 0x14601, 0x14602, 0x14603, 0x14604, 0x14605, 0x14606, 0x14607, 0x14608, 0x14609, 0x1460A, 0x1460B, 0x1460C, 0x1460D, 0x1460E, 0x1460F, 0x14610, 0x14611, 0x14612, 0x14613, 0x14614, 0x14615, 0x14616, 0x14617, 0x14618, 0x14619, 0x1461A, 0x1461B, 0x1461C, 0x1461D, 0x1461E, 0x1461F, 0x14620, 0x14621, 0x14622, 0x14623, 0x14624, 0x14625, 0x14626, 0x14627, 0x14628, 0x14629, 0x1462A, 0x1462B, 0x1462C, 0x1462D, 0x1462E, 0x1462F, 0x14630, 0x14631, 0x14632, 0x14633, 0x14634, 0x14635, 0x14636, 0x14637, 0x14638, 0x14639, 0x1463A, 0x1463B, 0x1463C, 0x1463D, 0x1463E, 0x1463F, 0x14640, 0x14641, 0x14642, 0x14643, 0x14644, 0x14645, 0x14646, 0x16800, 0x16801, 0x16802, 0x16803, 0x16804, 0x16805, 0x16806, 0x16807, 0x16808, 0x16809, 0x1680A, 0x1680B, 0x1680C, 0x1680D, 0x1680E, 0x1680F, 0x16810, 0x16811, 0x16812, 0x16813, 0x16814, 0x16815, 0x16816, 0x16817, 0x16818, 0x16819, 0x1681A, 0x1681B, 0x1681C, 0x1681D, 0x1681E, 0x1681F, 0x16820, 0x16821, 0x16822, 0x16823, 0x16824, 0x16825, 0x16826, 0x16827, 0x16828, 0x16829, 0x1682A, 0x1682B, 0x1682C, 0x1682D, 0x1682E, 0x1682F, 0x16830, 0x16831, 0x16832, 0x16833, 0x16834, 0x16835, 0x16836, 0x16837, 0x16838, 0x16839, 0x1683A, 0x1683B, 0x1683C, 0x1683D, 0x1683E, 0x1683F, 0x16840, 0x16841, 0x16842, 0x16843, 0x16844, 0x16845, 0x16846, 0x16847, 0x16848, 0x16849, 0x1684A, 0x1684B, 0x1684C, 0x1684D, 0x1684E, 0x1684F, 0x16850, 0x16851, 0x16852, 0x16853, 0x16854, 0x16855, 0x16856, 0x16857, 0x16858, 0x16859, 0x1685A, 0x1685B, 0x1685C, 0x1685D, 0x1685E, 0x1685F, 0x16860, 0x16861, 0x16862, 0x16863, 0x16864, 0x16865, 0x16866, 0x16867, 0x16868, 0x16869, 0x1686A, 0x1686B, 0x1686C, 0x1686D, 0x1686E, 0x1686F, 0x16870, 0x16871, 0x16872, 0x16873, 0x16874, 0x16875, 0x16876, 0x16877, 0x16878, 0x16879, 0x1687A, 0x1687B, 0x1687C, 0x1687D, 0x1687E, 0x1687F, 0x16880, 0x16881, 0x16882, 0x16883, 0x16884, 0x16885, 0x16886, 0x16887, 0x16888, 0x16889, 0x1688A, 0x1688B, 0x1688C, 0x1688D, 0x1688E, 0x1688F, 0x16890, 0x16891, 0x16892, 0x16893, 0x16894, 0x16895, 0x16896, 0x16897, 0x16898, 0x16899, 0x1689A, 0x1689B, 0x1689C, 0x1689D, 0x1689E, 0x1689F, 0x168A0, 0x168A1, 0x168A2, 0x168A3, 0x168A4, 0x168A5, 0x168A6, 0x168A7, 0x168A8, 0x168A9, 0x168AA, 0x168AB, 0x168AC, 0x168AD, 0x168AE, 0x168AF, 0x168B0, 0x168B1, 0x168B2, 0x168B3, 0x168B4, 0x168B5, 0x168B6, 0x168B7, 0x168B8, 0x168B9, 0x168BA, 0x168BB, 0x168BC, 0x168BD, 0x168BE, 0x168BF, 0x168C0, 0x168C1, 0x168C2, 0x168C3, 0x168C4, 0x168C5, 0x168C6, 0x168C7, 0x168C8, 0x168C9, 0x168CA, 0x168CB, 0x168CC, 0x168CD, 0x168CE, 0x168CF, 0x168D0, 0x168D1, 0x168D2, 0x168D3, 0x168D4, 0x168D5, 0x168D6, 0x168D7, 0x168D8, 0x168D9, 0x168DA, 0x168DB, 0x168DC, 0x168DD, 0x168DE, 0x168DF, 0x168E0, 0x168E1, 0x168E2, 0x168E3, 0x168E4, 0x168E5, 0x168E6, 0x168E7, 0x168E8, 0x168E9, 0x168EA, 0x168EB, 0x168EC, 0x168ED, 0x168EE, 0x168EF, 0x168F0, 0x168F1, 0x168F2, 0x168F3, 0x168F4, 0x168F5, 0x168F6, 0x168F7, 0x168F8, 0x168F9, 0x168FA, 0x168FB, 0x168FC, 0x168FD, 0x168FE, 0x168FF, 0x16900, 0x16901, 0x16902, 0x16903, 0x16904, 0x16905, 0x16906, 0x16907, 0x16908, 0x16909, 0x1690A, 0x1690B, 0x1690C, 0x1690D, 0x1690E, 0x1690F, 0x16910, 0x16911, 0x16912, 0x16913, 0x16914, 0x16915, 0x16916, 0x16917, 0x16918, 0x16919, 0x1691A, 0x1691B, 0x1691C, 0x1691D, 0x1691E, 0x1691F, 0x16920, 0x16921, 0x16922, 0x16923, 0x16924, 0x16925, 0x16926, 0x16927, 0x16928, 0x16929, 0x1692A, 0x1692B, 0x1692C, 0x1692D, 0x1692E, 0x1692F, 0x16930, 0x16931, 0x16932, 0x16933, 0x16934, 0x16935, 0x16936, 0x16937, 0x16938, 0x16939, 0x1693A, 0x1693B, 0x1693C, 0x1693D, 0x1693E, 0x1693F, 0x16940, 0x16941, 0x16942, 0x16943, 0x16944, 0x16945, 0x16946, 0x16947, 0x16948, 0x16949, 0x1694A, 0x1694B, 0x1694C, 0x1694D, 0x1694E, 0x1694F, 0x16950, 0x16951, 0x16952, 0x16953, 0x16954, 0x16955, 0x16956, 0x16957, 0x16958, 0x16959, 0x1695A, 0x1695B, 0x1695C, 0x1695D, 0x1695E, 0x1695F, 0x16960, 0x16961, 0x16962, 0x16963, 0x16964, 0x16965, 0x16966, 0x16967, 0x16968, 0x16969, 0x1696A, 0x1696B, 0x1696C, 0x1696D, 0x1696E, 0x1696F, 0x16970, 0x16971, 0x16972, 0x16973, 0x16974, 0x16975, 0x16976, 0x16977, 0x16978, 0x16979, 0x1697A, 0x1697B, 0x1697C, 0x1697D, 0x1697E, 0x1697F, 0x16980, 0x16981, 0x16982, 0x16983, 0x16984, 0x16985, 0x16986, 0x16987, 0x16988, 0x16989, 0x1698A, 0x1698B, 0x1698C, 0x1698D, 0x1698E, 0x1698F, 0x16990, 0x16991, 0x16992, 0x16993, 0x16994, 0x16995, 0x16996, 0x16997, 0x16998, 0x16999, 0x1699A, 0x1699B, 0x1699C, 0x1699D, 0x1699E, 0x1699F, 0x169A0, 0x169A1, 0x169A2, 0x169A3, 0x169A4, 0x169A5, 0x169A6, 0x169A7, 0x169A8, 0x169A9, 0x169AA, 0x169AB, 0x169AC, 0x169AD, 0x169AE, 0x169AF, 0x169B0, 0x169B1, 0x169B2, 0x169B3, 0x169B4, 0x169B5, 0x169B6, 0x169B7, 0x169B8, 0x169B9, 0x169BA, 0x169BB, 0x169BC, 0x169BD, 0x169BE, 0x169BF, 0x169C0, 0x169C1, 0x169C2, 0x169C3, 0x169C4, 0x169C5, 0x169C6, 0x169C7, 0x169C8, 0x169C9, 0x169CA, 0x169CB, 0x169CC, 0x169CD, 0x169CE, 0x169CF, 0x169D0, 0x169D1, 0x169D2, 0x169D3, 0x169D4, 0x169D5, 0x169D6, 0x169D7, 0x169D8, 0x169D9, 0x169DA, 0x169DB, 0x169DC, 0x169DD, 0x169DE, 0x169DF, 0x169E0, 0x169E1, 0x169E2, 0x169E3, 0x169E4, 0x169E5, 0x169E6, 0x169E7, 0x169E8, 0x169E9, 0x169EA, 0x169EB, 0x169EC, 0x169ED, 0x169EE, 0x169EF, 0x169F0, 0x169F1, 0x169F2, 0x169F3, 0x169F4, 0x169F5, 0x169F6, 0x169F7, 0x169F8, 0x169F9, 0x169FA, 0x169FB, 0x169FC, 0x169FD, 0x169FE, 0x169FF, 0x16A00, 0x16A01, 0x16A02, 0x16A03, 0x16A04, 0x16A05, 0x16A06, 0x16A07, 0x16A08, 0x16A09, 0x16A0A, 0x16A0B, 0x16A0C, 0x16A0D, 0x16A0E, 0x16A0F, 0x16A10, 0x16A11, 0x16A12, 0x16A13, 0x16A14, 0x16A15, 0x16A16, 0x16A17, 0x16A18, 0x16A19, 0x16A1A, 0x16A1B, 0x16A1C, 0x16A1D, 0x16A1E, 0x16A1F, 0x16A20, 0x16A21, 0x16A22, 0x16A23, 0x16A24, 0x16A25, 0x16A26, 0x16A27, 0x16A28, 0x16A29, 0x16A2A, 0x16A2B, 0x16A2C, 0x16A2D, 0x16A2E, 0x16A2F, 0x16A30, 0x16A31, 0x16A32, 0x16A33, 0x16A34, 0x16A35, 0x16A36, 0x16A37, 0x16A38, 0x16A40, 0x16A41, 0x16A42, 0x16A43, 0x16A44, 0x16A45, 0x16A46, 0x16A47, 0x16A48, 0x16A49, 0x16A4A, 0x16A4B, 0x16A4C, 0x16A4D, 0x16A4E, 0x16A4F, 0x16A50, 0x16A51, 0x16A52, 0x16A53, 0x16A54, 0x16A55, 0x16A56, 0x16A57, 0x16A58, 0x16A59, 0x16A5A, 0x16A5B, 0x16A5C, 0x16A5D, 0x16A5E, 0x16AD0, 0x16AD1, 0x16AD2, 0x16AD3, 0x16AD4, 0x16AD5, 0x16AD6, 0x16AD7, 0x16AD8, 0x16AD9, 0x16ADA, 0x16ADB, 0x16ADC, 0x16ADD, 0x16ADE, 0x16ADF, 0x16AE0, 0x16AE1, 0x16AE2, 0x16AE3, 0x16AE4, 0x16AE5, 0x16AE6, 0x16AE7, 0x16AE8, 0x16AE9, 0x16AEA, 0x16AEB, 0x16AEC, 0x16AED, 0x16B00, 0x16B01, 0x16B02, 0x16B03, 0x16B04, 0x16B05, 0x16B06, 0x16B07, 0x16B08, 0x16B09, 0x16B0A, 0x16B0B, 0x16B0C, 0x16B0D, 0x16B0E, 0x16B0F, 0x16B10, 0x16B11, 0x16B12, 0x16B13, 0x16B14, 0x16B15, 0x16B16, 0x16B17, 0x16B18, 0x16B19, 0x16B1A, 0x16B1B, 0x16B1C, 0x16B1D, 0x16B1E, 0x16B1F, 0x16B20, 0x16B21, 0x16B22, 0x16B23, 0x16B24, 0x16B25, 0x16B26, 0x16B27, 0x16B28, 0x16B29, 0x16B2A, 0x16B2B, 0x16B2C, 0x16B2D, 0x16B2E, 0x16B2F, 0x16B40, 0x16B41, 0x16B42, 0x16B43, 0x16B63, 0x16B64, 0x16B65, 0x16B66, 0x16B67, 0x16B68, 0x16B69, 0x16B6A, 0x16B6B, 0x16B6C, 0x16B6D, 0x16B6E, 0x16B6F, 0x16B70, 0x16B71, 0x16B72, 0x16B73, 0x16B74, 0x16B75, 0x16B76, 0x16B77, 0x16B7D, 0x16B7E, 0x16B7F, 0x16B80, 0x16B81, 0x16B82, 0x16B83, 0x16B84, 0x16B85, 0x16B86, 0x16B87, 0x16B88, 0x16B89, 0x16B8A, 0x16B8B, 0x16B8C, 0x16B8D, 0x16B8E, 0x16B8F, 0x16F00, 0x16F01, 0x16F02, 0x16F03, 0x16F04, 0x16F05, 0x16F06, 0x16F07, 0x16F08, 0x16F09, 0x16F0A, 0x16F0B, 0x16F0C, 0x16F0D, 0x16F0E, 0x16F0F, 0x16F10, 0x16F11, 0x16F12, 0x16F13, 0x16F14, 0x16F15, 0x16F16, 0x16F17, 0x16F18, 0x16F19, 0x16F1A, 0x16F1B, 0x16F1C, 0x16F1D, 0x16F1E, 0x16F1F, 0x16F20, 0x16F21, 0x16F22, 0x16F23, 0x16F24, 0x16F25, 0x16F26, 0x16F27, 0x16F28, 0x16F29, 0x16F2A, 0x16F2B, 0x16F2C, 0x16F2D, 0x16F2E, 0x16F2F, 0x16F30, 0x16F31, 0x16F32, 0x16F33, 0x16F34, 0x16F35, 0x16F36, 0x16F37, 0x16F38, 0x16F39, 0x16F3A, 0x16F3B, 0x16F3C, 0x16F3D, 0x16F3E, 0x16F3F, 0x16F40, 0x16F41, 0x16F42, 0x16F43, 0x16F44, 0x16F50, 0x16F93, 0x16F94, 0x16F95, 0x16F96, 0x16F97, 0x16F98, 0x16F99, 0x16F9A, 0x16F9B, 0x16F9C, 0x16F9D, 0x16F9E, 0x16F9F, 0x16FE0, 0x1BC00, 0x1BC01, 0x1BC02, 0x1BC03, 0x1BC04, 0x1BC05, 0x1BC06, 0x1BC07, 0x1BC08, 0x1BC09, 0x1BC0A, 0x1BC0B, 0x1BC0C, 0x1BC0D, 0x1BC0E, 0x1BC0F, 0x1BC10, 0x1BC11, 0x1BC12, 0x1BC13, 0x1BC14, 0x1BC15, 0x1BC16, 0x1BC17, 0x1BC18, 0x1BC19, 0x1BC1A, 0x1BC1B, 0x1BC1C, 0x1BC1D, 0x1BC1E, 0x1BC1F, 0x1BC20, 0x1BC21, 0x1BC22, 0x1BC23, 0x1BC24, 0x1BC25, 0x1BC26, 0x1BC27, 0x1BC28, 0x1BC29, 0x1BC2A, 0x1BC2B, 0x1BC2C, 0x1BC2D, 0x1BC2E, 0x1BC2F, 0x1BC30, 0x1BC31, 0x1BC32, 0x1BC33, 0x1BC34, 0x1BC35, 0x1BC36, 0x1BC37, 0x1BC38, 0x1BC39, 0x1BC3A, 0x1BC3B, 0x1BC3C, 0x1BC3D, 0x1BC3E, 0x1BC3F, 0x1BC40, 0x1BC41, 0x1BC42, 0x1BC43, 0x1BC44, 0x1BC45, 0x1BC46, 0x1BC47, 0x1BC48, 0x1BC49, 0x1BC4A, 0x1BC4B, 0x1BC4C, 0x1BC4D, 0x1BC4E, 0x1BC4F, 0x1BC50, 0x1BC51, 0x1BC52, 0x1BC53, 0x1BC54, 0x1BC55, 0x1BC56, 0x1BC57, 0x1BC58, 0x1BC59, 0x1BC5A, 0x1BC5B, 0x1BC5C, 0x1BC5D, 0x1BC5E, 0x1BC5F, 0x1BC60, 0x1BC61, 0x1BC62, 0x1BC63, 0x1BC64, 0x1BC65, 0x1BC66, 0x1BC67, 0x1BC68, 0x1BC69, 0x1BC6A, 0x1BC70, 0x1BC71, 0x1BC72, 0x1BC73, 0x1BC74, 0x1BC75, 0x1BC76, 0x1BC77, 0x1BC78, 0x1BC79, 0x1BC7A, 0x1BC7B, 0x1BC7C, 0x1BC80, 0x1BC81, 0x1BC82, 0x1BC83, 0x1BC84, 0x1BC85, 0x1BC86, 0x1BC87, 0x1BC88, 0x1BC90, 0x1BC91, 0x1BC92, 0x1BC93, 0x1BC94, 0x1BC95, 0x1BC96, 0x1BC97, 0x1BC98, 0x1BC99, 0x1D400, 0x1D401, 0x1D402, 0x1D403, 0x1D404, 0x1D405, 0x1D406, 0x1D407, 0x1D408, 0x1D409, 0x1D40A, 0x1D40B, 0x1D40C, 0x1D40D, 0x1D40E, 0x1D40F, 0x1D410, 0x1D411, 0x1D412, 0x1D413, 0x1D414, 0x1D415, 0x1D416, 0x1D417, 0x1D418, 0x1D419, 0x1D41A, 0x1D41B, 0x1D41C, 0x1D41D, 0x1D41E, 0x1D41F, 0x1D420, 0x1D421, 0x1D422, 0x1D423, 0x1D424, 0x1D425, 0x1D426, 0x1D427, 0x1D428, 0x1D429, 0x1D42A, 0x1D42B, 0x1D42C, 0x1D42D, 0x1D42E, 0x1D42F, 0x1D430, 0x1D431, 0x1D432, 0x1D433, 0x1D434, 0x1D435, 0x1D436, 0x1D437, 0x1D438, 0x1D439, 0x1D43A, 0x1D43B, 0x1D43C, 0x1D43D, 0x1D43E, 0x1D43F, 0x1D440, 0x1D441, 0x1D442, 0x1D443, 0x1D444, 0x1D445, 0x1D446, 0x1D447, 0x1D448, 0x1D449, 0x1D44A, 0x1D44B, 0x1D44C, 0x1D44D, 0x1D44E, 0x1D44F, 0x1D450, 0x1D451, 0x1D452, 0x1D453, 0x1D454, 0x1D456, 0x1D457, 0x1D458, 0x1D459, 0x1D45A, 0x1D45B, 0x1D45C, 0x1D45D, 0x1D45E, 0x1D45F, 0x1D460, 0x1D461, 0x1D462, 0x1D463, 0x1D464, 0x1D465, 0x1D466, 0x1D467, 0x1D468, 0x1D469, 0x1D46A, 0x1D46B, 0x1D46C, 0x1D46D, 0x1D46E, 0x1D46F, 0x1D470, 0x1D471, 0x1D472, 0x1D473, 0x1D474, 0x1D475, 0x1D476, 0x1D477, 0x1D478, 0x1D479, 0x1D47A, 0x1D47B, 0x1D47C, 0x1D47D, 0x1D47E, 0x1D47F, 0x1D480, 0x1D481, 0x1D482, 0x1D483, 0x1D484, 0x1D485, 0x1D486, 0x1D487, 0x1D488, 0x1D489, 0x1D48A, 0x1D48B, 0x1D48C, 0x1D48D, 0x1D48E, 0x1D48F, 0x1D490, 0x1D491, 0x1D492, 0x1D493, 0x1D494, 0x1D495, 0x1D496, 0x1D497, 0x1D498, 0x1D499, 0x1D49A, 0x1D49B, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AA, 0x1D4AB, 0x1D4AC, 0x1D4AE, 0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2, 0x1D4B3, 0x1D4B4, 0x1D4B5, 0x1D4B6, 0x1D4B7, 0x1D4B8, 0x1D4B9, 0x1D4BB, 0x1D4BD, 0x1D4BE, 0x1D4BF, 0x1D4C0, 0x1D4C1, 0x1D4C2, 0x1D4C3, 0x1D4C5, 0x1D4C6, 0x1D4C7, 0x1D4C8, 0x1D4C9, 0x1D4CA, 0x1D4CB, 0x1D4CC, 0x1D4CD, 0x1D4CE, 0x1D4CF, 0x1D4D0, 0x1D4D1, 0x1D4D2, 0x1D4D3, 0x1D4D4, 0x1D4D5, 0x1D4D6, 0x1D4D7, 0x1D4D8, 0x1D4D9, 0x1D4DA, 0x1D4DB, 0x1D4DC, 0x1D4DD, 0x1D4DE, 0x1D4DF, 0x1D4E0, 0x1D4E1, 0x1D4E2, 0x1D4E3, 0x1D4E4, 0x1D4E5, 0x1D4E6, 0x1D4E7, 0x1D4E8, 0x1D4E9, 0x1D4EA, 0x1D4EB, 0x1D4EC, 0x1D4ED, 0x1D4EE, 0x1D4EF, 0x1D4F0, 0x1D4F1, 0x1D4F2, 0x1D4F3, 0x1D4F4, 0x1D4F5, 0x1D4F6, 0x1D4F7, 0x1D4F8, 0x1D4F9, 0x1D4FA, 0x1D4FB, 0x1D4FC, 0x1D4FD, 0x1D4FE, 0x1D4FF, 0x1D500, 0x1D501, 0x1D502, 0x1D503, 0x1D504, 0x1D505, 0x1D507, 0x1D508, 0x1D509, 0x1D50A, 0x1D50D, 0x1D50E, 0x1D50F, 0x1D510, 0x1D511, 0x1D512, 0x1D513, 0x1D514, 0x1D516, 0x1D517, 0x1D518, 0x1D519, 0x1D51A, 0x1D51B, 0x1D51C, 0x1D51E, 0x1D51F, 0x1D520, 0x1D521, 0x1D522, 0x1D523, 0x1D524, 0x1D525, 0x1D526, 0x1D527, 0x1D528, 0x1D529, 0x1D52A, 0x1D52B, 0x1D52C, 0x1D52D, 0x1D52E, 0x1D52F, 0x1D530, 0x1D531, 0x1D532, 0x1D533, 0x1D534, 0x1D535, 0x1D536, 0x1D537, 0x1D538, 0x1D539, 0x1D53B, 0x1D53C, 0x1D53D, 0x1D53E, 0x1D540, 0x1D541, 0x1D542, 0x1D543, 0x1D544, 0x1D546, 0x1D54A, 0x1D54B, 0x1D54C, 0x1D54D, 0x1D54E, 0x1D54F, 0x1D550, 0x1D552, 0x1D553, 0x1D554, 0x1D555, 0x1D556, 0x1D557, 0x1D558, 0x1D559, 0x1D55A, 0x1D55B, 0x1D55C, 0x1D55D, 0x1D55E, 0x1D55F, 0x1D560, 0x1D561, 0x1D562, 0x1D563, 0x1D564, 0x1D565, 0x1D566, 0x1D567, 0x1D568, 0x1D569, 0x1D56A, 0x1D56B, 0x1D56C, 0x1D56D, 0x1D56E, 0x1D56F, 0x1D570, 0x1D571, 0x1D572, 0x1D573, 0x1D574, 0x1D575, 0x1D576, 0x1D577, 0x1D578, 0x1D579, 0x1D57A, 0x1D57B, 0x1D57C, 0x1D57D, 0x1D57E, 0x1D57F, 0x1D580, 0x1D581, 0x1D582, 0x1D583, 0x1D584, 0x1D585, 0x1D586, 0x1D587, 0x1D588, 0x1D589, 0x1D58A, 0x1D58B, 0x1D58C, 0x1D58D, 0x1D58E, 0x1D58F, 0x1D590, 0x1D591, 0x1D592, 0x1D593, 0x1D594, 0x1D595, 0x1D596, 0x1D597, 0x1D598, 0x1D599, 0x1D59A, 0x1D59B, 0x1D59C, 0x1D59D, 0x1D59E, 0x1D59F, 0x1D5A0, 0x1D5A1, 0x1D5A2, 0x1D5A3, 0x1D5A4, 0x1D5A5, 0x1D5A6, 0x1D5A7, 0x1D5A8, 0x1D5A9, 0x1D5AA, 0x1D5AB, 0x1D5AC, 0x1D5AD, 0x1D5AE, 0x1D5AF, 0x1D5B0, 0x1D5B1, 0x1D5B2, 0x1D5B3, 0x1D5B4, 0x1D5B5, 0x1D5B6, 0x1D5B7, 0x1D5B8, 0x1D5B9, 0x1D5BA, 0x1D5BB, 0x1D5BC, 0x1D5BD, 0x1D5BE, 0x1D5BF, 0x1D5C0, 0x1D5C1, 0x1D5C2, 0x1D5C3, 0x1D5C4, 0x1D5C5, 0x1D5C6, 0x1D5C7, 0x1D5C8, 0x1D5C9, 0x1D5CA, 0x1D5CB, 0x1D5CC, 0x1D5CD, 0x1D5CE, 0x1D5CF, 0x1D5D0, 0x1D5D1, 0x1D5D2, 0x1D5D3, 0x1D5D4, 0x1D5D5, 0x1D5D6, 0x1D5D7, 0x1D5D8, 0x1D5D9, 0x1D5DA, 0x1D5DB, 0x1D5DC, 0x1D5DD, 0x1D5DE, 0x1D5DF, 0x1D5E0, 0x1D5E1, 0x1D5E2, 0x1D5E3, 0x1D5E4, 0x1D5E5, 0x1D5E6, 0x1D5E7, 0x1D5E8, 0x1D5E9, 0x1D5EA, 0x1D5EB, 0x1D5EC, 0x1D5ED, 0x1D5EE, 0x1D5EF, 0x1D5F0, 0x1D5F1, 0x1D5F2, 0x1D5F3, 0x1D5F4, 0x1D5F5, 0x1D5F6, 0x1D5F7, 0x1D5F8, 0x1D5F9, 0x1D5FA, 0x1D5FB, 0x1D5FC, 0x1D5FD, 0x1D5FE, 0x1D5FF, 0x1D600, 0x1D601, 0x1D602, 0x1D603, 0x1D604, 0x1D605, 0x1D606, 0x1D607, 0x1D608, 0x1D609, 0x1D60A, 0x1D60B, 0x1D60C, 0x1D60D, 0x1D60E, 0x1D60F, 0x1D610, 0x1D611, 0x1D612, 0x1D613, 0x1D614, 0x1D615, 0x1D616, 0x1D617, 0x1D618, 0x1D619, 0x1D61A, 0x1D61B, 0x1D61C, 0x1D61D, 0x1D61E, 0x1D61F, 0x1D620, 0x1D621, 0x1D622, 0x1D623, 0x1D624, 0x1D625, 0x1D626, 0x1D627, 0x1D628, 0x1D629, 0x1D62A, 0x1D62B, 0x1D62C, 0x1D62D, 0x1D62E, 0x1D62F, 0x1D630, 0x1D631, 0x1D632, 0x1D633, 0x1D634, 0x1D635, 0x1D636, 0x1D637, 0x1D638, 0x1D639, 0x1D63A, 0x1D63B, 0x1D63C, 0x1D63D, 0x1D63E, 0x1D63F, 0x1D640, 0x1D641, 0x1D642, 0x1D643, 0x1D644, 0x1D645, 0x1D646, 0x1D647, 0x1D648, 0x1D649, 0x1D64A, 0x1D64B, 0x1D64C, 0x1D64D, 0x1D64E, 0x1D64F, 0x1D650, 0x1D651, 0x1D652, 0x1D653, 0x1D654, 0x1D655, 0x1D656, 0x1D657, 0x1D658, 0x1D659, 0x1D65A, 0x1D65B, 0x1D65C, 0x1D65D, 0x1D65E, 0x1D65F, 0x1D660, 0x1D661, 0x1D662, 0x1D663, 0x1D664, 0x1D665, 0x1D666, 0x1D667, 0x1D668, 0x1D669, 0x1D66A, 0x1D66B, 0x1D66C, 0x1D66D, 0x1D66E, 0x1D66F, 0x1D670, 0x1D671, 0x1D672, 0x1D673, 0x1D674, 0x1D675, 0x1D676, 0x1D677, 0x1D678, 0x1D679, 0x1D67A, 0x1D67B, 0x1D67C, 0x1D67D, 0x1D67E, 0x1D67F, 0x1D680, 0x1D681, 0x1D682, 0x1D683, 0x1D684, 0x1D685, 0x1D686, 0x1D687, 0x1D688, 0x1D689, 0x1D68A, 0x1D68B, 0x1D68C, 0x1D68D, 0x1D68E, 0x1D68F, 0x1D690, 0x1D691, 0x1D692, 0x1D693, 0x1D694, 0x1D695, 0x1D696, 0x1D697, 0x1D698, 0x1D699, 0x1D69A, 0x1D69B, 0x1D69C, 0x1D69D, 0x1D69E, 0x1D69F, 0x1D6A0, 0x1D6A1, 0x1D6A2, 0x1D6A3, 0x1D6A4, 0x1D6A5, 0x1D6A8, 0x1D6A9, 0x1D6AA, 0x1D6AB, 0x1D6AC, 0x1D6AD, 0x1D6AE, 0x1D6AF, 0x1D6B0, 0x1D6B1, 0x1D6B2, 0x1D6B3, 0x1D6B4, 0x1D6B5, 0x1D6B6, 0x1D6B7, 0x1D6B8, 0x1D6B9, 0x1D6BA, 0x1D6BB, 0x1D6BC, 0x1D6BD, 0x1D6BE, 0x1D6BF, 0x1D6C0, 0x1D6C2, 0x1D6C3, 0x1D6C4, 0x1D6C5, 0x1D6C6, 0x1D6C7, 0x1D6C8, 0x1D6C9, 0x1D6CA, 0x1D6CB, 0x1D6CC, 0x1D6CD, 0x1D6CE, 0x1D6CF, 0x1D6D0, 0x1D6D1, 0x1D6D2, 0x1D6D3, 0x1D6D4, 0x1D6D5, 0x1D6D6, 0x1D6D7, 0x1D6D8, 0x1D6D9, 0x1D6DA, 0x1D6DC, 0x1D6DD, 0x1D6DE, 0x1D6DF, 0x1D6E0, 0x1D6E1, 0x1D6E2, 0x1D6E3, 0x1D6E4, 0x1D6E5, 0x1D6E6, 0x1D6E7, 0x1D6E8, 0x1D6E9, 0x1D6EA, 0x1D6EB, 0x1D6EC, 0x1D6ED, 0x1D6EE, 0x1D6EF, 0x1D6F0, 0x1D6F1, 0x1D6F2, 0x1D6F3, 0x1D6F4, 0x1D6F5, 0x1D6F6, 0x1D6F7, 0x1D6F8, 0x1D6F9, 0x1D6FA, 0x1D6FC, 0x1D6FD, 0x1D6FE, 0x1D6FF, 0x1D700, 0x1D701, 0x1D702, 0x1D703, 0x1D704, 0x1D705, 0x1D706, 0x1D707, 0x1D708, 0x1D709, 0x1D70A, 0x1D70B, 0x1D70C, 0x1D70D, 0x1D70E, 0x1D70F, 0x1D710, 0x1D711, 0x1D712, 0x1D713, 0x1D714, 0x1D716, 0x1D717, 0x1D718, 0x1D719, 0x1D71A, 0x1D71B, 0x1D71C, 0x1D71D, 0x1D71E, 0x1D71F, 0x1D720, 0x1D721, 0x1D722, 0x1D723, 0x1D724, 0x1D725, 0x1D726, 0x1D727, 0x1D728, 0x1D729, 0x1D72A, 0x1D72B, 0x1D72C, 0x1D72D, 0x1D72E, 0x1D72F, 0x1D730, 0x1D731, 0x1D732, 0x1D733, 0x1D734, 0x1D736, 0x1D737, 0x1D738, 0x1D739, 0x1D73A, 0x1D73B, 0x1D73C, 0x1D73D, 0x1D73E, 0x1D73F, 0x1D740, 0x1D741, 0x1D742, 0x1D743, 0x1D744, 0x1D745, 0x1D746, 0x1D747, 0x1D748, 0x1D749, 0x1D74A, 0x1D74B, 0x1D74C, 0x1D74D, 0x1D74E, 0x1D750, 0x1D751, 0x1D752, 0x1D753, 0x1D754, 0x1D755, 0x1D756, 0x1D757, 0x1D758, 0x1D759, 0x1D75A, 0x1D75B, 0x1D75C, 0x1D75D, 0x1D75E, 0x1D75F, 0x1D760, 0x1D761, 0x1D762, 0x1D763, 0x1D764, 0x1D765, 0x1D766, 0x1D767, 0x1D768, 0x1D769, 0x1D76A, 0x1D76B, 0x1D76C, 0x1D76D, 0x1D76E, 0x1D770, 0x1D771, 0x1D772, 0x1D773, 0x1D774, 0x1D775, 0x1D776, 0x1D777, 0x1D778, 0x1D779, 0x1D77A, 0x1D77B, 0x1D77C, 0x1D77D, 0x1D77E, 0x1D77F, 0x1D780, 0x1D781, 0x1D782, 0x1D783, 0x1D784, 0x1D785, 0x1D786, 0x1D787, 0x1D788, 0x1D78A, 0x1D78B, 0x1D78C, 0x1D78D, 0x1D78E, 0x1D78F, 0x1D790, 0x1D791, 0x1D792, 0x1D793, 0x1D794, 0x1D795, 0x1D796, 0x1D797, 0x1D798, 0x1D799, 0x1D79A, 0x1D79B, 0x1D79C, 0x1D79D, 0x1D79E, 0x1D79F, 0x1D7A0, 0x1D7A1, 0x1D7A2, 0x1D7A3, 0x1D7A4, 0x1D7A5, 0x1D7A6, 0x1D7A7, 0x1D7A8, 0x1D7AA, 0x1D7AB, 0x1D7AC, 0x1D7AD, 0x1D7AE, 0x1D7AF, 0x1D7B0, 0x1D7B1, 0x1D7B2, 0x1D7B3, 0x1D7B4, 0x1D7B5, 0x1D7B6, 0x1D7B7, 0x1D7B8, 0x1D7B9, 0x1D7BA, 0x1D7BB, 0x1D7BC, 0x1D7BD, 0x1D7BE, 0x1D7BF, 0x1D7C0, 0x1D7C1, 0x1D7C2, 0x1D7C4, 0x1D7C5, 0x1D7C6, 0x1D7C7, 0x1D7C8, 0x1D7C9, 0x1D7CA, 0x1D7CB, 0x1E800, 0x1E801, 0x1E802, 0x1E803, 0x1E804, 0x1E805, 0x1E806, 0x1E807, 0x1E808, 0x1E809, 0x1E80A, 0x1E80B, 0x1E80C, 0x1E80D, 0x1E80E, 0x1E80F, 0x1E810, 0x1E811, 0x1E812, 0x1E813, 0x1E814, 0x1E815, 0x1E816, 0x1E817, 0x1E818, 0x1E819, 0x1E81A, 0x1E81B, 0x1E81C, 0x1E81D, 0x1E81E, 0x1E81F, 0x1E820, 0x1E821, 0x1E822, 0x1E823, 0x1E824, 0x1E825, 0x1E826, 0x1E827, 0x1E828, 0x1E829, 0x1E82A, 0x1E82B, 0x1E82C, 0x1E82D, 0x1E82E, 0x1E82F, 0x1E830, 0x1E831, 0x1E832, 0x1E833, 0x1E834, 0x1E835, 0x1E836, 0x1E837, 0x1E838, 0x1E839, 0x1E83A, 0x1E83B, 0x1E83C, 0x1E83D, 0x1E83E, 0x1E83F, 0x1E840, 0x1E841, 0x1E842, 0x1E843, 0x1E844, 0x1E845, 0x1E846, 0x1E847, 0x1E848, 0x1E849, 0x1E84A, 0x1E84B, 0x1E84C, 0x1E84D, 0x1E84E, 0x1E84F, 0x1E850, 0x1E851, 0x1E852, 0x1E853, 0x1E854, 0x1E855, 0x1E856, 0x1E857, 0x1E858, 0x1E859, 0x1E85A, 0x1E85B, 0x1E85C, 0x1E85D, 0x1E85E, 0x1E85F, 0x1E860, 0x1E861, 0x1E862, 0x1E863, 0x1E864, 0x1E865, 0x1E866, 0x1E867, 0x1E868, 0x1E869, 0x1E86A, 0x1E86B, 0x1E86C, 0x1E86D, 0x1E86E, 0x1E86F, 0x1E870, 0x1E871, 0x1E872, 0x1E873, 0x1E874, 0x1E875, 0x1E876, 0x1E877, 0x1E878, 0x1E879, 0x1E87A, 0x1E87B, 0x1E87C, 0x1E87D, 0x1E87E, 0x1E87F, 0x1E880, 0x1E881, 0x1E882, 0x1E883, 0x1E884, 0x1E885, 0x1E886, 0x1E887, 0x1E888, 0x1E889, 0x1E88A, 0x1E88B, 0x1E88C, 0x1E88D, 0x1E88E, 0x1E88F, 0x1E890, 0x1E891, 0x1E892, 0x1E893, 0x1E894, 0x1E895, 0x1E896, 0x1E897, 0x1E898, 0x1E899, 0x1E89A, 0x1E89B, 0x1E89C, 0x1E89D, 0x1E89E, 0x1E89F, 0x1E8A0, 0x1E8A1, 0x1E8A2, 0x1E8A3, 0x1E8A4, 0x1E8A5, 0x1E8A6, 0x1E8A7, 0x1E8A8, 0x1E8A9, 0x1E8AA, 0x1E8AB, 0x1E8AC, 0x1E8AD, 0x1E8AE, 0x1E8AF, 0x1E8B0, 0x1E8B1, 0x1E8B2, 0x1E8B3, 0x1E8B4, 0x1E8B5, 0x1E8B6, 0x1E8B7, 0x1E8B8, 0x1E8B9, 0x1E8BA, 0x1E8BB, 0x1E8BC, 0x1E8BD, 0x1E8BE, 0x1E8BF, 0x1E8C0, 0x1E8C1, 0x1E8C2, 0x1E8C3, 0x1E8C4, 0x1E900, 0x1E901, 0x1E902, 0x1E903, 0x1E904, 0x1E905, 0x1E906, 0x1E907, 0x1E908, 0x1E909, 0x1E90A, 0x1E90B, 0x1E90C, 0x1E90D, 0x1E90E, 0x1E90F, 0x1E910, 0x1E911, 0x1E912, 0x1E913, 0x1E914, 0x1E915, 0x1E916, 0x1E917, 0x1E918, 0x1E919, 0x1E91A, 0x1E91B, 0x1E91C, 0x1E91D, 0x1E91E, 0x1E91F, 0x1E920, 0x1E921, 0x1E922, 0x1E923, 0x1E924, 0x1E925, 0x1E926, 0x1E927, 0x1E928, 0x1E929, 0x1E92A, 0x1E92B, 0x1E92C, 0x1E92D, 0x1E92E, 0x1E92F, 0x1E930, 0x1E931, 0x1E932, 0x1E933, 0x1E934, 0x1E935, 0x1E936, 0x1E937, 0x1E938, 0x1E939, 0x1E93A, 0x1E93B, 0x1E93C, 0x1E93D, 0x1E93E, 0x1E93F, 0x1E940, 0x1E941, 0x1E942, 0x1E943, 0x1EE00, 0x1EE01, 0x1EE02, 0x1EE03, 0x1EE05, 0x1EE06, 0x1EE07, 0x1EE08, 0x1EE09, 0x1EE0A, 0x1EE0B, 0x1EE0C, 0x1EE0D, 0x1EE0E, 0x1EE0F, 0x1EE10, 0x1EE11, 0x1EE12, 0x1EE13, 0x1EE14, 0x1EE15, 0x1EE16, 0x1EE17, 0x1EE18, 0x1EE19, 0x1EE1A, 0x1EE1B, 0x1EE1C, 0x1EE1D, 0x1EE1E, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE27, 0x1EE29, 0x1EE2A, 0x1EE2B, 0x1EE2C, 0x1EE2D, 0x1EE2E, 0x1EE2F, 0x1EE30, 0x1EE31, 0x1EE32, 0x1EE34, 0x1EE35, 0x1EE36, 0x1EE37, 0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE4D, 0x1EE4E, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE67, 0x1EE68, 0x1EE69, 0x1EE6A, 0x1EE6C, 0x1EE6D, 0x1EE6E, 0x1EE6F, 0x1EE70, 0x1EE71, 0x1EE72, 0x1EE74, 0x1EE75, 0x1EE76, 0x1EE77, 0x1EE79, 0x1EE7A, 0x1EE7B, 0x1EE7C, 0x1EE7E, 0x1EE80, 0x1EE81, 0x1EE82, 0x1EE83, 0x1EE84, 0x1EE85, 0x1EE86, 0x1EE87, 0x1EE88, 0x1EE89, 0x1EE8B, 0x1EE8C, 0x1EE8D, 0x1EE8E, 0x1EE8F, 0x1EE90, 0x1EE91, 0x1EE92, 0x1EE93, 0x1EE94, 0x1EE95, 0x1EE96, 0x1EE97, 0x1EE98, 0x1EE99, 0x1EE9A, 0x1EE9B, 0x1EEA1, 0x1EEA2, 0x1EEA3, 0x1EEA5, 0x1EEA6, 0x1EEA7, 0x1EEA8, 0x1EEA9, 0x1EEAB, 0x1EEAC, 0x1EEAD, 0x1EEAE, 0x1EEAF, 0x1EEB0, 0x1EEB1, 0x1EEB2, 0x1EEB3, 0x1EEB4, 0x1EEB5, 0x1EEB6, 0x1EEB7, 0x1EEB8, 0x1EEB9, 0x1EEBA, 0x1EEBB, 0x1F130, 0x1F131, 0x1F132, 0x1F133, 0x1F134, 0x1F135, 0x1F136, 0x1F137, 0x1F138, 0x1F139, 0x1F13A, 0x1F13B, 0x1F13C, 0x1F13D, 0x1F13E, 0x1F13F, 0x1F140, 0x1F141, 0x1F142, 0x1F143, 0x1F144, 0x1F145, 0x1F146, 0x1F147, 0x1F148, 0x1F149, 0x1F150, 0x1F151, 0x1F152, 0x1F153, 0x1F154, 0x1F155, 0x1F156, 0x1F157, 0x1F158, 0x1F159, 0x1F15A, 0x1F15B, 0x1F15C, 0x1F15D, 0x1F15E, 0x1F15F, 0x1F160, 0x1F161, 0x1F162, 0x1F163, 0x1F164, 0x1F165, 0x1F166, 0x1F167, 0x1F168, 0x1F169, 0x1F170, 0x1F171, 0x1F172, 0x1F173, 0x1F174, 0x1F175, 0x1F176, 0x1F177, 0x1F178, 0x1F179, 0x1F17A, 0x1F17B, 0x1F17C, 0x1F17D, 0x1F17E, 0x1F17F, 0x1F180, 0x1F181, 0x1F182, 0x1F183, 0x1F184, 0x1F185, 0x1F186, 0x1F187, 0x1F188, 0x1F189 }; static const uint32_t Single_Quote[]= { 0x00027 }; static const uint32_t Double_Quote[]= { 0x00022 }; static const uint32_t MidNumLet[]= { 0x0002E, 0x02018, 0x02019, 0x02024, 0x0FE52, 0x0FF07, 0x0FF0E }; static const uint32_t MidLetter[]= { 0x0003A, 0x000B7, 0x002D7, 0x00387, 0x005F4, 0x02027, 0x0FE13, 0x0FE55, 0x0FF1A }; static const uint32_t MidNum[]= { 0x0002C, 0x0003B, 0x0037E, 0x00589, 0x0060C, 0x0060D, 0x0066C, 0x007F8, 0x02044, 0x0FE10, 0x0FE14, 0x0FE50, 0x0FE54, 0x0FF0C, 0x0FF1B }; static const uint32_t Numeric[]= { 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00660, 0x00661, 0x00662, 0x00663, 0x00664, 0x00665, 0x00666, 0x00667, 0x00668, 0x00669, 0x0066B, 0x006F0, 0x006F1, 0x006F2, 0x006F3, 0x006F4, 0x006F5, 0x006F6, 0x006F7, 0x006F8, 0x006F9, 0x007C0, 0x007C1, 0x007C2, 0x007C3, 0x007C4, 0x007C5, 0x007C6, 0x007C7, 0x007C8, 0x007C9, 0x00966, 0x00967, 0x00968, 0x00969, 0x0096A, 0x0096B, 0x0096C, 0x0096D, 0x0096E, 0x0096F, 0x009E6, 0x009E7, 0x009E8, 0x009E9, 0x009EA, 0x009EB, 0x009EC, 0x009ED, 0x009EE, 0x009EF, 0x00A66, 0x00A67, 0x00A68, 0x00A69, 0x00A6A, 0x00A6B, 0x00A6C, 0x00A6D, 0x00A6E, 0x00A6F, 0x00AE6, 0x00AE7, 0x00AE8, 0x00AE9, 0x00AEA, 0x00AEB, 0x00AEC, 0x00AED, 0x00AEE, 0x00AEF, 0x00B66, 0x00B67, 0x00B68, 0x00B69, 0x00B6A, 0x00B6B, 0x00B6C, 0x00B6D, 0x00B6E, 0x00B6F, 0x00BE6, 0x00BE7, 0x00BE8, 0x00BE9, 0x00BEA, 0x00BEB, 0x00BEC, 0x00BED, 0x00BEE, 0x00BEF, 0x00C66, 0x00C67, 0x00C68, 0x00C69, 0x00C6A, 0x00C6B, 0x00C6C, 0x00C6D, 0x00C6E, 0x00C6F, 0x00CE6, 0x00CE7, 0x00CE8, 0x00CE9, 0x00CEA, 0x00CEB, 0x00CEC, 0x00CED, 0x00CEE, 0x00CEF, 0x00D66, 0x00D67, 0x00D68, 0x00D69, 0x00D6A, 0x00D6B, 0x00D6C, 0x00D6D, 0x00D6E, 0x00D6F, 0x00DE6, 0x00DE7, 0x00DE8, 0x00DE9, 0x00DEA, 0x00DEB, 0x00DEC, 0x00DED, 0x00DEE, 0x00DEF, 0x00E50, 0x00E51, 0x00E52, 0x00E53, 0x00E54, 0x00E55, 0x00E56, 0x00E57, 0x00E58, 0x00E59, 0x00ED0, 0x00ED1, 0x00ED2, 0x00ED3, 0x00ED4, 0x00ED5, 0x00ED6, 0x00ED7, 0x00ED8, 0x00ED9, 0x00F20, 0x00F21, 0x00F22, 0x00F23, 0x00F24, 0x00F25, 0x00F26, 0x00F27, 0x00F28, 0x00F29, 0x01040, 0x01041, 0x01042, 0x01043, 0x01044, 0x01045, 0x01046, 0x01047, 0x01048, 0x01049, 0x01090, 0x01091, 0x01092, 0x01093, 0x01094, 0x01095, 0x01096, 0x01097, 0x01098, 0x01099, 0x017E0, 0x017E1, 0x017E2, 0x017E3, 0x017E4, 0x017E5, 0x017E6, 0x017E7, 0x017E8, 0x017E9, 0x01810, 0x01811, 0x01812, 0x01813, 0x01814, 0x01815, 0x01816, 0x01817, 0x01818, 0x01819, 0x01946, 0x01947, 0x01948, 0x01949, 0x0194A, 0x0194B, 0x0194C, 0x0194D, 0x0194E, 0x0194F, 0x019D0, 0x019D1, 0x019D2, 0x019D3, 0x019D4, 0x019D5, 0x019D6, 0x019D7, 0x019D8, 0x019D9, 0x01A80, 0x01A81, 0x01A82, 0x01A83, 0x01A84, 0x01A85, 0x01A86, 0x01A87, 0x01A88, 0x01A89, 0x01A90, 0x01A91, 0x01A92, 0x01A93, 0x01A94, 0x01A95, 0x01A96, 0x01A97, 0x01A98, 0x01A99, 0x01B50, 0x01B51, 0x01B52, 0x01B53, 0x01B54, 0x01B55, 0x01B56, 0x01B57, 0x01B58, 0x01B59, 0x01BB0, 0x01BB1, 0x01BB2, 0x01BB3, 0x01BB4, 0x01BB5, 0x01BB6, 0x01BB7, 0x01BB8, 0x01BB9, 0x01C40, 0x01C41, 0x01C42, 0x01C43, 0x01C44, 0x01C45, 0x01C46, 0x01C47, 0x01C48, 0x01C49, 0x01C50, 0x01C51, 0x01C52, 0x01C53, 0x01C54, 0x01C55, 0x01C56, 0x01C57, 0x01C58, 0x01C59, 0x0A620, 0x0A621, 0x0A622, 0x0A623, 0x0A624, 0x0A625, 0x0A626, 0x0A627, 0x0A628, 0x0A629, 0x0A8D0, 0x0A8D1, 0x0A8D2, 0x0A8D3, 0x0A8D4, 0x0A8D5, 0x0A8D6, 0x0A8D7, 0x0A8D8, 0x0A8D9, 0x0A900, 0x0A901, 0x0A902, 0x0A903, 0x0A904, 0x0A905, 0x0A906, 0x0A907, 0x0A908, 0x0A909, 0x0A9D0, 0x0A9D1, 0x0A9D2, 0x0A9D3, 0x0A9D4, 0x0A9D5, 0x0A9D6, 0x0A9D7, 0x0A9D8, 0x0A9D9, 0x0A9F0, 0x0A9F1, 0x0A9F2, 0x0A9F3, 0x0A9F4, 0x0A9F5, 0x0A9F6, 0x0A9F7, 0x0A9F8, 0x0A9F9, 0x0AA50, 0x0AA51, 0x0AA52, 0x0AA53, 0x0AA54, 0x0AA55, 0x0AA56, 0x0AA57, 0x0AA58, 0x0AA59, 0x0ABF0, 0x0ABF1, 0x0ABF2, 0x0ABF3, 0x0ABF4, 0x0ABF5, 0x0ABF6, 0x0ABF7, 0x0ABF8, 0x0ABF9, 0x104A0, 0x104A1, 0x104A2, 0x104A3, 0x104A4, 0x104A5, 0x104A6, 0x104A7, 0x104A8, 0x104A9, 0x11066, 0x11067, 0x11068, 0x11069, 0x1106A, 0x1106B, 0x1106C, 0x1106D, 0x1106E, 0x1106F, 0x110F0, 0x110F1, 0x110F2, 0x110F3, 0x110F4, 0x110F5, 0x110F6, 0x110F7, 0x110F8, 0x110F9, 0x11136, 0x11137, 0x11138, 0x11139, 0x1113A, 0x1113B, 0x1113C, 0x1113D, 0x1113E, 0x1113F, 0x111D0, 0x111D1, 0x111D2, 0x111D3, 0x111D4, 0x111D5, 0x111D6, 0x111D7, 0x111D8, 0x111D9, 0x112F0, 0x112F1, 0x112F2, 0x112F3, 0x112F4, 0x112F5, 0x112F6, 0x112F7, 0x112F8, 0x112F9, 0x11450, 0x11451, 0x11452, 0x11453, 0x11454, 0x11455, 0x11456, 0x11457, 0x11458, 0x11459, 0x114D0, 0x114D1, 0x114D2, 0x114D3, 0x114D4, 0x114D5, 0x114D6, 0x114D7, 0x114D8, 0x114D9, 0x11650, 0x11651, 0x11652, 0x11653, 0x11654, 0x11655, 0x11656, 0x11657, 0x11658, 0x11659, 0x116C0, 0x116C1, 0x116C2, 0x116C3, 0x116C4, 0x116C5, 0x116C6, 0x116C7, 0x116C8, 0x116C9, 0x11730, 0x11731, 0x11732, 0x11733, 0x11734, 0x11735, 0x11736, 0x11737, 0x11738, 0x11739, 0x118E0, 0x118E1, 0x118E2, 0x118E3, 0x118E4, 0x118E5, 0x118E6, 0x118E7, 0x118E8, 0x118E9, 0x11C50, 0x11C51, 0x11C52, 0x11C53, 0x11C54, 0x11C55, 0x11C56, 0x11C57, 0x11C58, 0x11C59, 0x16A60, 0x16A61, 0x16A62, 0x16A63, 0x16A64, 0x16A65, 0x16A66, 0x16A67, 0x16A68, 0x16A69, 0x16B50, 0x16B51, 0x16B52, 0x16B53, 0x16B54, 0x16B55, 0x16B56, 0x16B57, 0x16B58, 0x16B59, 0x1D7CE, 0x1D7CF, 0x1D7D0, 0x1D7D1, 0x1D7D2, 0x1D7D3, 0x1D7D4, 0x1D7D5, 0x1D7D6, 0x1D7D7, 0x1D7D8, 0x1D7D9, 0x1D7DA, 0x1D7DB, 0x1D7DC, 0x1D7DD, 0x1D7DE, 0x1D7DF, 0x1D7E0, 0x1D7E1, 0x1D7E2, 0x1D7E3, 0x1D7E4, 0x1D7E5, 0x1D7E6, 0x1D7E7, 0x1D7E8, 0x1D7E9, 0x1D7EA, 0x1D7EB, 0x1D7EC, 0x1D7ED, 0x1D7EE, 0x1D7EF, 0x1D7F0, 0x1D7F1, 0x1D7F2, 0x1D7F3, 0x1D7F4, 0x1D7F5, 0x1D7F6, 0x1D7F7, 0x1D7F8, 0x1D7F9, 0x1D7FA, 0x1D7FB, 0x1D7FC, 0x1D7FD, 0x1D7FE, 0x1D7FF, 0x1E950, 0x1E951, 0x1E952, 0x1E953, 0x1E954, 0x1E955, 0x1E956, 0x1E957, 0x1E958, 0x1E959 }; static const uint32_t ExtendNumLet[]= { 0x0005F, 0x0202F, 0x0203F, 0x02040, 0x02054, 0x0FE33, 0x0FE34, 0x0FE4D, 0x0FE4E, 0x0FE4F, 0x0FF3F }; dovecot-2.2.33.2/src/lib-fts/fts-tokenizer.c0000644000175000017500000001255513165463624015446 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "str.h" #include "strfuncs.h" #include "fts-tokenizer.h" #include "fts-tokenizer-private.h" static ARRAY(const struct fts_tokenizer *) fts_tokenizer_classes; void fts_tokenizers_init(void) { if (!array_is_created(&fts_tokenizer_classes)) { fts_tokenizer_register(fts_tokenizer_generic); fts_tokenizer_register(fts_tokenizer_email_address); } } void fts_tokenizers_deinit(void) { if (array_is_created(&fts_tokenizer_classes)) array_free(&fts_tokenizer_classes); } /* private */ void fts_tokenizer_register(const struct fts_tokenizer *tok_class) { if (!array_is_created(&fts_tokenizer_classes)) i_array_init(&fts_tokenizer_classes, FTS_TOKENIZER_CLASSES_NR); array_append(&fts_tokenizer_classes, &tok_class, 1); } /* private */ void fts_tokenizer_unregister(const struct fts_tokenizer *tok_class) { const struct fts_tokenizer *const *tp; unsigned int idx; array_foreach(&fts_tokenizer_classes, tp) { if (strcmp((*tp)->name, tok_class->name) == 0) { idx = array_foreach_idx(&fts_tokenizer_classes, tp); array_delete(&fts_tokenizer_classes, idx, 1); if (array_count(&fts_tokenizer_classes) == 0) array_free(&fts_tokenizer_classes); return; } } i_unreached(); } const struct fts_tokenizer *fts_tokenizer_find(const char *name) { const struct fts_tokenizer *const *tp; array_foreach(&fts_tokenizer_classes, tp) { if (strcmp((*tp)->name, name) == 0) return *tp; } return NULL; } const char *fts_tokenizer_name(const struct fts_tokenizer *tok) { return tok->name; } static void fts_tokenizer_self_reset(struct fts_tokenizer *tok) { tok->prev_data = NULL; tok->prev_size = 0; tok->prev_skip = 0; tok->prev_reply_finished = TRUE; } int fts_tokenizer_create(const struct fts_tokenizer *tok_class, struct fts_tokenizer *parent, const char *const *settings, struct fts_tokenizer **tokenizer_r, const char **error_r) { struct fts_tokenizer *tok; const char *empty_settings = NULL; i_assert(settings == NULL || str_array_length(settings) % 2 == 0); if (settings == NULL) settings = &empty_settings; if (tok_class->v->create(settings, &tok, error_r) < 0) { *tokenizer_r = NULL; return -1; } tok->refcount = 1; fts_tokenizer_self_reset(tok); if (parent != NULL) { fts_tokenizer_ref(parent); tok->parent = parent; tok->parent_input = buffer_create_dynamic(default_pool, 128); } *tokenizer_r = tok; return 0; } void fts_tokenizer_ref(struct fts_tokenizer *tok) { i_assert(tok->refcount > 0); tok->refcount++; } void fts_tokenizer_unref(struct fts_tokenizer **_tok) { struct fts_tokenizer *tok = *_tok; i_assert(tok->refcount > 0); *_tok = NULL; if (--tok->refcount > 0) return; if (tok->parent_input != NULL) buffer_free(&tok->parent_input); if (tok->parent != NULL) fts_tokenizer_unref(&tok->parent); tok->v->destroy(tok); } static int fts_tokenizer_next_self(struct fts_tokenizer *tok, const unsigned char *data, size_t size, const char **token_r, const char **error_r) { int ret = 0; size_t skip = 0; i_assert(tok->prev_reply_finished || (data == tok->prev_data && size == tok->prev_size)); if (tok->prev_reply_finished) { /* whole new data */ ret = tok->v->next(tok, data, size, &skip, token_r, error_r); } else { /* continuing previous data */ i_assert(tok->prev_skip <= size); ret = tok->v->next(tok, data + tok->prev_skip, size - tok->prev_skip, &skip, token_r, error_r); } if (ret > 0) { i_assert(skip <= size - tok->prev_skip); tok->prev_data = data; tok->prev_size = size; tok->prev_skip = tok->prev_skip + skip; tok->prev_reply_finished = FALSE; } else if (ret == 0) { /* we need a new data block */ fts_tokenizer_self_reset(tok); } return ret; } void fts_tokenizer_reset(struct fts_tokenizer *tok) { tok->v->reset(tok); fts_tokenizer_self_reset(tok); } int fts_tokenizer_next(struct fts_tokenizer *tok, const unsigned char *data, size_t size, const char **token_r, const char **error_r) { int ret; switch (tok->parent_state) { case FTS_TOKENIZER_PARENT_STATE_ADD_DATA: ret = fts_tokenizer_next_self(tok, data, size, token_r, error_r); if (ret <= 0 || tok->parent == NULL || tok->skip_parents) break; buffer_set_used_size(tok->parent_input, 0); buffer_append(tok->parent_input, *token_r, strlen(*token_r)); tok->parent_state++; /* fall through */ case FTS_TOKENIZER_PARENT_STATE_NEXT_OUTPUT: ret = fts_tokenizer_next(tok->parent, tok->parent_input->data, tok->parent_input->used, token_r, error_r); if (ret != 0) break; tok->parent_state++; /* fall through */ case FTS_TOKENIZER_PARENT_STATE_FINALIZE: ret = fts_tokenizer_next(tok->parent, NULL, 0, token_r, error_r); if (ret != 0) break; /* we're finished sending this token to parent tokenizer. see if our own tokenizer has more tokens available */ tok->parent_state = FTS_TOKENIZER_PARENT_STATE_ADD_DATA; return fts_tokenizer_next(tok, data, size, token_r, error_r); default: i_unreached(); } /* we must not be returning empty tokens */ i_assert(ret <= 0 || (*token_r)[0] != '\0'); return ret; } int fts_tokenizer_final(struct fts_tokenizer *tok, const char **token_r, const char **error_r) { return fts_tokenizer_next(tok, NULL, 0, token_r, error_r); } dovecot-2.2.33.2/src/lib-fts/test-fts-language.c0000644000175000017500000003075713165463624016200 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "fts-language.h" /* TODO: These checks will not work without proper libtextcat configuration. As such, they are not really unit test to be coupled with the build. */ const char *const settings[] = {"fts_language_config", TEXTCAT_DATADIR"/fpdb.conf", "fts_language_data", TEXTCAT_DATADIR"/", NULL}; /* Detect Finnish. fi--utf8 */ static void test_fts_language_detect_finnish(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char finnish[] = "Yhdistyneiden kansakuntien kolmas yleiskokous hyv\xC3\xA4ksyi "\ "ja julkisti ihmisoikeuksien yleismaailmallisen julistuksen "\ "joulukuun 10. p\xC3\xA4iv\xC3\xA4n\xC3\xA4 1948. Julistuksen "\ "hyv\xC3\xA4ksymisen puolesta \xC3\xA4\xC3\xA4nesti 48 maata. "\ "Mik\xC3\xA4\xC3\xA4n maa ei \xC3\xA4\xC3\xA4nest\xC3\xA4nyt "\ "vastaan. Kahdeksan maata pid\xC3\xA4ttyi "\ "\xC3\xA4\xC3\xA4nest\xC3\xA4m\xC3\xA4st\xC3\xA4."; const char names[] = "de, fi, en"; const char *unknown, *error; test_begin("fts language detect Finnish"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, finnish, sizeof(finnish)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "fi") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect English */ static void test_fts_language_detect_english(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char english[] = "Whereas recognition of the inherent dignity and"\ " of the equal and inalienable rights of all members of the human"\ "family is the foundation of freedom, justice and peace in the "\ "world,\n Whereas disregard and contempt for human rights have "\ "resulted in barbarous acts which have outraged the conscience"\ "of mankind, and the advent of a world in which human beings"\ "shall enjoy freedom of speech and belief and freedom from "\ "fear and want has been proclaimed as the highest aspiration"\ "of the common people, "; const char names[] = "fi, de, fr, en"; const char *unknown, *error; test_begin("fts language detect English"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, english, sizeof(english)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "en") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect French */ static void test_fts_language_detect_french(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char french[] = "D\xC3\xA9""claration universelle des droits de l\xE2\x80\x99" "homme Pr\xC3\xA9""ambule Consid\xC3\xA9rant que la "\ "reconnaissance de la dignit\xC3\xA9 inh\xC3\xA9rente \xC3\xA0"\ " tous les membres de la famille humaine et de leurs droits "\ "\xC3\xA9gaux et inali\xC3\xA9nables constitue le fondement de"\ " la libert\xC3\xA9, de la justice et de la paix dans le monde,"\ " Consid\xC3\xA9rant que la m\xC3\xA9""connaissance et le "\ "m\xC3\xA9pris des droits de l\xE2\x80\x99homme ont conduit "\ "\xC3\xA0 des actes de barbarie qui r\xC3\xA9voltent la "\ "conscience de l\xE2\x80\x99humanit\xC3\xA9 et que "\ "l\xE2\x80\x99""av\xC3\xA8nement d\xE2\x80\x99un monde o\xC3\xB9"\ " les \xC3\xAAtres humains seront libres de parler et de "\ "croire, lib\xC3\xA9r\xC3\xA9s de la terreur et de la "\ "mis\xC3\xA8re, a \xC3\xA9t\xC3\xA9 proclam\xC3\xA9 comme la "\ "plus haute aspiration de l\xE2\x80\x99homme,"; const char names[] = "de, fi, fr, en"; const char *unknown, *error; test_begin("fts language detect French"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, french, sizeof(french)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "fr") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect German */ static void test_fts_language_detect_german(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char german[] = "Artikel 1"\ "Alle Menschen sind frei und gleich an W\xC3\xBCrde und Rechten "\ "geboren. Sie sind mit Vernunft und Gewissen begabt und sollen "\ "einander im Geist der Br\xC3\xBC""derlichkeit begegnen." \ "Artikel 2 Jeder hat Anspruch auf die in dieser "\ "Erkl\xC3\xA4rung verk\xC3\xBCndeten Rechte und Freiheiten ohne"\ "irgendeinen Unterschied, etwa nach Rasse, Hautfarbe, "\ "Geschlecht, Sprache, Religion, politischer oder sonstiger "\ "\xC3\x9C""berzeugung, nationaler oder sozialer Herkunft, "\ "Verm\xC3\xB6gen, Geburt oder sonstigem Stand. Des weiteren "\ "darf kein Unterschied gemacht werden auf Grund der "\ "politischen, rechtlichen oder internationalen Stellung des "\ "Landes oder Gebiets, dem eine Person angeh\xC3\xB6rt, "\ "gleichg\xC3\xBCltig, ob dieses unabh\xC3\xA4ngig ist, unter "\ "Treuhandschaft steht, keine Selbstregierung besitzt oder "\ "sonst in seiner Souver\xC3\xA4nit\xC3\xA4t "\ "eingeschr\xC3\xA4nkt ist."; const char names[] = "fi, de, fr, en"; const char *unknown, *error; test_begin("fts language detect German"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, german, sizeof(german)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "de") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect Swedish */ static void test_fts_language_detect_swedish(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char swedish[] = "Artikel 1."\ "Alla m\xC3\xA4nniskor \xC3\xA4ro f\xC3\xB6""dda fria och lika"\ " i v\xC3\xA4rde och r\xC3\xA4ttigheter. De \xC3\xA4ro "\ "utrustade med f\xC3\xB6rnuft och samvete och b\xC3\xB6ra "\ "handla gentemot varandra i en anda av broderskap."; const char names[] = "fi, de, sv, fr, en"; const char *unknown, *error; test_begin("fts language detect Swedish"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, swedish, sizeof(swedish)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "sv") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect Bokmal */ static void test_fts_language_detect_bokmal(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char bokmal[] = "Artikkel 1.\n"\ "Alle mennesker er f\xC3\xB8""dt frie og med samme menneskeverd"\ " og menneskerettigheter. De er utstyrt med fornuft og "\ "samvittighet og b\xC3\xB8r handle mot hverandre i "\ "brorskapets \xC3\xA5nd"; const char names[] = "fi, de, sv, no, fr, en"; const char *unknown, *error; test_begin("fts language detect Bokmal as Norwegian"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, bokmal, sizeof(bokmal)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "no") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect Nynorsk */ static void test_fts_language_detect_nynorsk(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char nynorsk[] = "Artikkel 1.\n"\ "Alle menneske er f\xC3\xB8""dde til fridom og med same "\ "menneskeverd og menneskerettar. Dei har f\xC3\xA5tt fornuft "\ "og samvit og skal leve med kvarandre som br\xC3\xB8r."; const char names[] = "fi, de, sv, no, fr, en"; const char *unknown, *error; test_begin("fts language detect Nynorsk as Norwegian"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, nynorsk, sizeof(nynorsk)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "no") == 0); fts_language_list_deinit(&lp); test_end(); } /* Detect Finnish as English */ static void test_fts_language_detect_finnish_as_english(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char finnish[] = "Yhdistyneiden kansakuntien kolmas yleiskokous hyv\xC3\xA4ksyi "\ "ja julkisti ihmisoikeuksien yleismaailmallisen julistuksen "\ "joulukuun 10. p\xC3\xA4iv\xC3\xA4n\xC3\xA4 1948. Julistuksen "\ "hyv\xC3\xA4ksymisen puolesta \xC3\xA4\xC3\xA4nesti 48 maata. "\ "Mik\xC3\xA4\xC3\xA4n maa ei \xC3\xA4\xC3\xA4nest\xC3\xA4nyt "\ "vastaan. Kahdeksan maata pid\xC3\xA4ttyi "\ "\xC3\xA4\xC3\xA4nest\xC3\xA4m\xC3\xA4st\xC3\xA4."; const char names[] = "en"; const char *unknown, *error; test_begin("fts language detect Finnish as English"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, finnish, sizeof(finnish)-1, &lang_r) == FTS_LANGUAGE_RESULT_OK); test_assert(strcmp(lang_r->name, "en") == 0); fts_language_list_deinit(&lp); test_end(); } /* Successfully avoid detecting English, when en is not in language list. */ static void test_fts_language_detect_na(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char english[] = "Whereas recognition of the inherent dignity and"\ " of the equal and inalienable rights of all members of the human"\ "family is the foundation of freedom, justice and peace in the "\ "world,\n Whereas disregard and contempt for human rights have "\ "resulted in barbarous acts which have outraged the conscience"\ "of mankind, and the advent of a world in which human beings"\ "shall enjoy freedom of speech and belief and freedom from "\ "fear and want has been proclaimed as the highest aspiration"\ "of the common people, "; const char names[] = "fi, de, fr"; const char *unknown, *error; test_begin("fts language detect not available"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, english, sizeof(english)-1, &lang_r) == FTS_LANGUAGE_RESULT_UNKNOWN); fts_language_list_deinit(&lp); test_end(); } /* Successfully detect, that Klingon is unknown. */ static void test_fts_language_detect_unknown(void) { struct fts_language_list *lp = NULL; const struct fts_language *lang_r = NULL; const unsigned char klingon[] = "nobwI''a'pu'qoqvam'e' "\ "nuHegh'eghrupqa'moHlaHbe'law'lI'neS "\ "SeH'eghtaHghach'a'na'chajmo'."; const char names[] = "fi, de, fr"; const char *unknown, *error; test_begin("fts language detect unknown"); test_assert(fts_language_list_init(settings, &lp, &error) == 0); test_assert(fts_language_list_add_names(lp, names, &unknown) == TRUE); test_assert(fts_language_detect(lp, klingon, sizeof(klingon), &lang_r) == FTS_LANGUAGE_RESULT_UNKNOWN); fts_language_list_deinit(&lp); test_end(); } static void test_fts_language_find_builtin(void) { const struct fts_language *lp; test_begin("fts language find built-in"); lp = fts_language_find("en"); i_assert(lp != NULL); test_assert(strcmp(lp->name, "en") == 0); test_end(); } static void test_fts_language_register(void) { const struct fts_language *lp; test_begin("fts language register"); fts_language_register("jp"); lp = fts_language_find("jp"); i_assert(lp != NULL); test_assert(strcmp(lp->name, "jp") == 0); test_end(); } int main(void) { int ret; static void (*test_functions[])(void) = { test_fts_language_detect_finnish, test_fts_language_detect_english, test_fts_language_detect_french, test_fts_language_detect_german, test_fts_language_detect_swedish, test_fts_language_detect_bokmal, test_fts_language_detect_nynorsk, test_fts_language_detect_finnish_as_english, test_fts_language_detect_na, test_fts_language_detect_unknown, test_fts_language_find_builtin, test_fts_language_register, NULL }; fts_languages_init(); ret = test_run(test_functions); fts_languages_deinit(); return ret; } dovecot-2.2.33.2/src/lib-fts/fts-icu.h0000644000175000017500000000175313165463624014217 00000000000000#ifndef HAVE_FTS_ICU_H #define HAVE_FTS_ICU_H #include #include /* Convert UTF-8 input to UTF-16 output. The dest_utf16 contains UChars. */ void fts_icu_utf8_to_utf16(buffer_t *dest_utf16, const char *src_utf8); /* Convert UTF-16 input to UTF-8 output. */ void fts_icu_utf16_to_utf8(string_t *dest_utf8, const UChar *src_utf16, unsigned int src_len); /* Run ICU translation for the string. Returns 0 on success, -1 on error. */ int fts_icu_translate(buffer_t *dest_utf16, const UChar *src_utf16, unsigned int src_len, UTransliterator *transliterator, const char **error_r); /* Lowercase the given UTF-8 string. */ void fts_icu_lcase(string_t *dest_utf8, const char *src_utf8); /* Free all the memory used by ICU functions. */ void fts_icu_deinit(void); int fts_icu_transliterator_create(const char *id, UTransliterator **transliterator_r, const char **error_r) ; #endif dovecot-2.2.33.2/src/lib-fts/word-break-data.c0000644000175000017500000000663213144653672015606 00000000000000/* This file is automatically generated by word-properties.pl from PropList.txt */ static const uint32_t White_Space[]= { 0x00009, 0x0000A, 0x0000B, 0x0000C, 0x0000D, 0x00020, 0x00085, 0x000A0, 0x01680, 0x02000, 0x02001, 0x02002, 0x02003, 0x02004, 0x02005, 0x02006, 0x02007, 0x02008, 0x02009, 0x0200A, 0x02028, 0x02029, 0x0202F, 0x0205F, 0x03000 }; static const uint32_t Dash[]= { 0x0002D, 0x0058A, 0x005BE, 0x01400, 0x01806, 0x02010, 0x02011, 0x02012, 0x02013, 0x02014, 0x02015, 0x02053, 0x0207B, 0x0208B, 0x02212, 0x02E17, 0x02E1A, 0x02E3A, 0x02E3B, 0x02E40, 0x0301C, 0x03030, 0x030A0, 0x0FE31, 0x0FE32, 0x0FE58, 0x0FE63, 0x0FF0D }; static const uint32_t Quotation_Mark[]= { 0x00022, 0x00027, 0x000AB, 0x000BB, 0x02018, 0x02019, 0x0201A, 0x0201B, 0x0201C, 0x0201D, 0x0201E, 0x0201F, 0x02039, 0x0203A, 0x02E42, 0x0300C, 0x0300D, 0x0300E, 0x0300F, 0x0301D, 0x0301E, 0x0301F, 0x0FE41, 0x0FE42, 0x0FE43, 0x0FE44, 0x0FF02, 0x0FF07, 0x0FF62, 0x0FF63 }; static const uint32_t Terminal_Punctuation[]= { 0x00021, 0x0002C, 0x0002E, 0x0003A, 0x0003B, 0x0003F, 0x0037E, 0x00387, 0x00589, 0x005C3, 0x0060C, 0x0061B, 0x0061F, 0x006D4, 0x00700, 0x00701, 0x00702, 0x00703, 0x00704, 0x00705, 0x00706, 0x00707, 0x00708, 0x00709, 0x0070A, 0x0070C, 0x007F8, 0x007F9, 0x00830, 0x00831, 0x00832, 0x00833, 0x00834, 0x00835, 0x00836, 0x00837, 0x00838, 0x00839, 0x0083A, 0x0083B, 0x0083C, 0x0083D, 0x0083E, 0x0085E, 0x00964, 0x00965, 0x00E5A, 0x00E5B, 0x00F08, 0x00F0D, 0x00F0E, 0x00F0F, 0x00F10, 0x00F11, 0x00F12, 0x0104A, 0x0104B, 0x01361, 0x01362, 0x01363, 0x01364, 0x01365, 0x01366, 0x01367, 0x01368, 0x0166D, 0x0166E, 0x016EB, 0x016EC, 0x016ED, 0x01735, 0x01736, 0x017D4, 0x017D5, 0x017D6, 0x017DA, 0x01802, 0x01803, 0x01804, 0x01805, 0x01808, 0x01809, 0x01944, 0x01945, 0x01AA8, 0x01AA9, 0x01AAA, 0x01AAB, 0x01B5A, 0x01B5B, 0x01B5D, 0x01B5E, 0x01B5F, 0x01C3B, 0x01C3C, 0x01C3D, 0x01C3E, 0x01C3F, 0x01C7E, 0x01C7F, 0x0203C, 0x0203D, 0x02047, 0x02048, 0x02049, 0x02E2E, 0x02E3C, 0x02E41, 0x03001, 0x03002, 0x0A4FE, 0x0A4FF, 0x0A60D, 0x0A60E, 0x0A60F, 0x0A6F3, 0x0A6F4, 0x0A6F5, 0x0A6F6, 0x0A6F7, 0x0A876, 0x0A877, 0x0A8CE, 0x0A8CF, 0x0A92F, 0x0A9C7, 0x0A9C8, 0x0A9C9, 0x0AA5D, 0x0AA5E, 0x0AA5F, 0x0AADF, 0x0AAF0, 0x0AAF1, 0x0ABEB, 0x0FE50, 0x0FE51, 0x0FE52, 0x0FE54, 0x0FE55, 0x0FE56, 0x0FE57, 0x0FF01, 0x0FF0C, 0x0FF0E, 0x0FF1A, 0x0FF1B, 0x0FF1F, 0x0FF61, 0x0FF64, 0x1039F, 0x103D0, 0x10857, 0x1091F, 0x10A56, 0x10A57, 0x10AF0, 0x10AF1, 0x10AF2, 0x10AF3, 0x10AF4, 0x10AF5, 0x10B3A, 0x10B3B, 0x10B3C, 0x10B3D, 0x10B3E, 0x10B3F, 0x10B99, 0x10B9A, 0x10B9B, 0x10B9C, 0x11047, 0x11048, 0x11049, 0x1104A, 0x1104B, 0x1104C, 0x1104D, 0x110BE, 0x110BF, 0x110C0, 0x110C1, 0x11141, 0x11142, 0x11143, 0x111C5, 0x111C6, 0x111CD, 0x111DE, 0x111DF, 0x11238, 0x11239, 0x1123A, 0x1123B, 0x1123C, 0x112A9, 0x1144B, 0x1144C, 0x1144D, 0x1145B, 0x115C2, 0x115C3, 0x115C4, 0x115C5, 0x115C9, 0x115CA, 0x115CB, 0x115CC, 0x115CD, 0x115CE, 0x115CF, 0x115D0, 0x115D1, 0x115D2, 0x115D3, 0x115D4, 0x115D5, 0x115D6, 0x115D7, 0x11641, 0x11642, 0x1173C, 0x1173D, 0x1173E, 0x11C41, 0x11C42, 0x11C43, 0x11C71, 0x12470, 0x12471, 0x12472, 0x12473, 0x12474, 0x16A6E, 0x16A6F, 0x16AF5, 0x16B37, 0x16B38, 0x16B39, 0x16B44, 0x1BC9F, 0x1DA87, 0x1DA88, 0x1DA89, 0x1DA8A }; static const uint32_t STerm[]= { }; static const uint32_t Pattern_White_Space[]= { 0x00009, 0x0000A, 0x0000B, 0x0000C, 0x0000D, 0x00020, 0x00085, 0x0200E, 0x0200F, 0x02028, 0x02029 }; dovecot-2.2.33.2/src/lib-fts/fts-filter-lowercase.c0000644000175000017500000000347013123174404016664 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "fts-language.h" #include "fts-filter-private.h" #ifdef HAVE_LIBICU # include "fts-icu.h" # include "fts-filter-common.h" #endif static int fts_filter_lowercase_create(const struct fts_language *lang ATTR_UNUSED, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter *filter; unsigned int i, max_length = 250; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "maxlen") == 0) { if (str_to_uint(value, &max_length) < 0 || max_length == 0) { *error_r = t_strdup_printf("Invalid lowercase filter maxlen setting: %s", value); return -1; } } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } filter = i_new(struct fts_filter, 1); *filter = *fts_filter_lowercase; filter->token = str_new(default_pool, 64); filter->max_length = max_length; *filter_r = filter; return 0; } static int fts_filter_lowercase_filter(struct fts_filter *filter ATTR_UNUSED, const char **token, const char **error_r ATTR_UNUSED) { #ifdef HAVE_LIBICU str_truncate(filter->token, 0); fts_icu_lcase(filter->token, *token); fts_filter_truncate_token(filter->token, filter->max_length); *token = str_c(filter->token); #else *token = t_str_lcase(*token); #endif return 1; } static const struct fts_filter fts_filter_lowercase_real = { .class_name = "lowercase", .v = { fts_filter_lowercase_create, fts_filter_lowercase_filter, NULL } }; const struct fts_filter *fts_filter_lowercase = &fts_filter_lowercase_real; dovecot-2.2.33.2/src/lib-fts/test-fts-filter.c0000644000175000017500000007632413165463624015702 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "sha2.h" #include "str.h" #include "unichar.h" #include "test-common.h" #include "fts-language.h" #include "fts-filter.h" #include static const char *const stopword_settings[] = {"stopwords_dir", TEST_STOPWORDS_DIR, NULL}; static struct fts_language english_language = { .name = "en" }; static struct fts_language french_language = { .name = "fr" }; static struct fts_language norwegian_language = { .name = "no" }; #if defined(HAVE_LIBICU) && defined(HAVE_FTS_STEMMER) static struct fts_language swedish_language = { .name = "sv" }; #endif static void test_fts_filter_find(void) { test_begin("fts filter find"); test_assert(fts_filter_find("stopwords") == fts_filter_stopwords); test_assert(fts_filter_find("snowball") == fts_filter_stemmer_snowball); test_assert(fts_filter_find("normalizer-icu") == fts_filter_normalizer_icu); test_assert(fts_filter_find("lowercase") == fts_filter_lowercase); test_assert(fts_filter_find("contractions") == fts_filter_contractions); test_end(); } static void test_fts_filter_contractions_fail(void) { struct fts_filter *filter; const char *error; test_begin("fts filter contractions, unsupported language"); test_assert(fts_filter_create(fts_filter_contractions, NULL, &english_language, NULL, &filter, &error) != 0); test_assert(error != NULL); test_end(); } static void test_fts_filter_contractions_fr(void) { struct { const char *input; const char *output; } tests[] = { { "foo", "foo" }, { "you're", "you're" }, { "l'homme", "homme" }, { "l\xE2\x80\x99homme", "homme" }, { "aujourd'hui", "aujourd'hui" }, { "qu\xE2\x80\x99il", "il" }, { "qu'il", "il" }, { "du'il", "du'il" }, { "que", "que" }, { "'foobar'", "'foobar'" }, { "foo'bar", "foo'bar" }, { "a'foo", "a'foo" }, { "cu'", "cu'" }, { "qu", "qu" }, { "d", "d" }, { "qu'", NULL }, { "j'adore", "adore" }, { "quelqu'un", "quelqu'un" }, { "l'esprit", "esprit" } }; struct fts_filter *filter; const char *error; const char *token; unsigned int i; int ret; test_begin("fts filter contractions, French"); test_assert(fts_filter_create(fts_filter_contractions, NULL, &french_language, NULL, &filter, &error) == 0); for (i = 0; i < N_ELEMENTS(tests); i++) { token = tests[i].input; ret = fts_filter_filter(filter, &token, &error); test_assert(ret >= 0); if (ret > 0) test_assert_idx(strcmp(token, tests[i].output) == 0, i); else if (ret == 0) test_assert_idx(token == NULL && tests[i].output == NULL, i); } fts_filter_unref(&filter); test_end(); } static void test_fts_filter_lowercase(void) { struct { const char *input; const char *output; } tests[] = { { "foo", "foo" }, { "FOO", "foo" }, { "fOo", "foo" } }; struct fts_filter *filter; const char *error; const char *token; unsigned int i; test_begin("fts filter lowercase"); test_assert(fts_filter_create(fts_filter_lowercase, NULL, &english_language, NULL, &filter, &error) == 0); for (i = 0; i < N_ELEMENTS(tests); i++) { token = tests[i].input; test_assert_idx(fts_filter_filter(filter, &token, &error) > 0 && strcmp(token, tests[i].output) == 0, 0); } fts_filter_unref(&filter); test_end(); } #ifdef HAVE_LIBICU static void test_fts_filter_lowercase_utf8(void) { struct { const char *input; const char *output; } tests[] = { { "f\xC3\x85\xC3\x85", "f\xC3\xA5\xC3\xA5" }, { "F\xC3\x85\xC3\x85", "f\xC3\xA5\xC3\xA5" }, { "F\xC3\x85\xC3\xA5", "f\xC3\xA5\xC3\xA5" } }; struct fts_filter *filter; const char *error; const char *token; unsigned int i; test_begin("fts filter lowercase, UTF8"); test_assert(fts_filter_create(fts_filter_lowercase, NULL, &english_language, NULL, &filter, &error) == 0); for (i = 0; i < N_ELEMENTS(tests); i++) { token = tests[i].input; test_assert_idx(fts_filter_filter(filter, &token, &error) > 0 && strcmp(token, tests[i].output) == 0, 0); } fts_filter_unref(&filter); test_end(); } static void test_fts_filter_lowercase_too_long_utf8(void) { struct { const char *input; const char *output; } tests[] = { { "f\xC3\x85\xC3\x85", "f\xC3\xA5\xC3\xA5" }, { "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxy" }, { "abc\xC3\x85""defghijklmnopqrstuvwxyz", "abc\xC3\xA5""defghijklmnopqrstuvw" }, { "abcdefghijklmnopqrstuvwx\xC3\x85", "abcdefghijklmnopqrstuvwx" } }; struct fts_filter *filter; const char *error; const char *token; const char * const settings[] = {"maxlen", "25", NULL}; unsigned int i; test_begin("fts filter lowercase, too long UTF8"); test_assert(fts_filter_create(fts_filter_lowercase, NULL, &english_language, settings, &filter, &error) == 0); for (i = 0; i < N_ELEMENTS(tests); i++) { token = tests[i].input; test_assert_idx(fts_filter_filter(filter, &token, &error) > 0 && strcmp(token, tests[i].output) == 0, 0); } fts_filter_unref(&filter); test_end(); } #endif static void test_fts_filter_stopwords_eng(void) { struct fts_filter *filter; const char *error; int ret; const char *input[] = {"an", "elephant", "and", "a", "bear", "drive", "by", "for", "no", "reason", "they", "will", "not", "sing", NULL}; const char *output[] = {NULL, "elephant", NULL, NULL, "bear", "drive", NULL, NULL, NULL, "reason", NULL, NULL, NULL, "sing"}; const char **ip, **op; const char *token; test_begin("fts filter stopwords, English"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &english_language, stopword_settings, &filter, &error) == 0); ip = input; op = output; while (*ip != NULL) { token = *ip; ret = fts_filter_filter(filter, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*op == NULL); } else { test_assert(*op != NULL); test_assert(strcmp(*ip, token) == 0); } op++; ip++; } fts_filter_unref(&filter); test_assert(filter == NULL); test_end(); } static void test_fts_filter_stopwords_fin(void) { const struct fts_language finnish = { .name = "fi" }; struct fts_filter *filter; const char *error; int ret; const char *input[] = {"olla", "vaiko", "eik\xC3\xB6", "olla", "kenest\xC3\xA4", "ja", "joista", "jonka", "testi", NULL}; const char *output[] = {NULL, "vaiko", "eik\xC3\xB6", NULL, NULL, NULL, NULL, NULL, "testi"}; const char *input2[] = {"kuka", "kenet", "keneen", "testi", "eiv\xC3\xA4t", NULL}; const char *output2[] = {NULL, NULL, NULL, "testi", NULL}; const char **ip, **op; const char *token; test_begin("fts filter stopwords, Finnish"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &finnish, stopword_settings, &filter, &error) == 0); ip = input; op = output; while (*ip != NULL) { token = *ip; ret = fts_filter_filter(filter, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*op == NULL); } else { test_assert(*op != NULL); test_assert(strcmp(*ip, token) == 0); } op++; ip++; } fts_filter_unref(&filter); test_assert(filter == NULL); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &finnish, stopword_settings, &filter, &error) == 0); ip = input2; op = output2; while (*ip != NULL) { token = *ip; ret = fts_filter_filter(filter, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*op == NULL); } else { test_assert(*op != NULL); test_assert(strcmp(*ip, token) == 0); } op++; ip++; } fts_filter_unref(&filter); test_assert(filter == NULL); test_end(); } static void test_fts_filter_stopwords_fra(void) { struct fts_filter *filter; const char *error; int ret; const char *input[] = {"e\xC3\xBBt", "soyez", "soi", "peut", "que", "quelconque", "\xC3\xA9t\xC3\xA9", "l\xE2\x80\x99""av\xC3\xA8nement", NULL}; const char *output[] = {NULL, NULL, NULL, "peut", NULL, "quelconque", NULL, "l\xE2\x80\x99""av\xC3\xA8nement",}; const char **ip, **op; const char *token; test_begin("fts filter stopwords, French"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &french_language, stopword_settings, &filter, &error) == 0); ip = input; op = output; while (*ip != NULL) { token = *ip; ret = fts_filter_filter(filter, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*op == NULL); } else { test_assert(*op != NULL); test_assert(strcmp(*ip, token) == 0); } op++; ip++; } fts_filter_unref(&filter); test_assert(filter == NULL); test_end(); } static void test_fts_filter_stopwords_no(void) { struct fts_filter *filter; const char *error; int ret; const char *input[] = {"og", "d\xC3\xA5", "medlemsstatane", "har", "bunde", "seg", "til", "\xC3\xA5", "fremje", "allmenn", "v\xC3\xB8rdnad", "for", "pakta", "og", "halde", "seg", "etter", "menneskerettane", "og", "den", "grunnleggjande", "fridomen", "i", "samarbeid", "med", "Dei", "Sameinte", "Nasjonane", NULL}; const char *output[] = {NULL, NULL, "medlemsstatane", NULL, "bunde", NULL, NULL, NULL, "fremje", "allmenn", "v\xC3\xB8rdnad", NULL, "pakta", NULL, "halde", NULL, NULL, "menneskerettane", NULL, NULL, "grunnleggjande", "fridomen", NULL, "samarbeid", NULL, "Dei", "Sameinte", "Nasjonane"}; const char **ip, **op; const char *token; test_begin("fts filter stopwords, Norwegian"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &norwegian_language, stopword_settings, &filter, &error) == 0); ip = input; op = output; while (*ip != NULL) { token = *ip; ret = fts_filter_filter(filter, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*op == NULL); } else { test_assert(*op != NULL); test_assert(strcmp(*ip, token) == 0); } op++; ip++; } fts_filter_unref(&filter); test_assert(filter == NULL); test_end(); } static void test_fts_filter_stopwords_fail_lazy_init(void) { const struct fts_language unknown = { .name = "bebobidoop" }; struct fts_filter *filter = NULL; const char *error = NULL, *token = "foobar"; test_begin("fts filter stopwords, fail filter() (lazy init)"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &unknown, stopword_settings, &filter, &error) == 0); test_assert(filter != NULL && error == NULL); test_assert(fts_filter_filter(filter, &token, &error) < 0 && error != NULL); fts_filter_unref(&filter); test_end(); } static void test_fts_filter_stopwords_malformed(void) { const struct fts_language malformed = { .name = "malformed" }; struct fts_filter *filter = NULL; const char *error = NULL, *token = "foobar"; test_begin("fts filter stopwords, malformed list"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &malformed, stopword_settings, &filter, &error) == 0); test_expect_error_string("seems empty. Is the file correctly formatted?"); test_assert(fts_filter_filter(filter, &token, &error) > 0); test_expect_no_more_errors(); fts_filter_unref(&filter); test_end(); } #ifdef HAVE_FTS_STEMMER static void test_fts_filter_stemmer_snowball_stem_english(void) { struct fts_filter *stemmer; const char *error; const char *token = NULL; const char * const tokens[] = { "dries" ,"friendlies", "All", "human", "beings", "are", "born", "free", "and", "equal", "in", "dignity", "and", "rights", "They", "are", "endowed", "with", "reason", "and", "conscience", "and", "should", "act", "towards", "one", "another", "in", "a", "spirit", "of", "brotherhood", NULL}; const char * const bases[] = { "dri" ,"friend", "All", "human", "be", "are", "born", "free", "and", "equal", "in", "digniti", "and", "right", "They", "are", "endow", "with", "reason", "and", "conscienc", "and", "should", "act", "toward", "one", "anoth", "in", "a", "spirit", "of", "brotherhood", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filter stem English"); test_assert(fts_filter_create(fts_filter_stemmer_snowball, NULL, &english_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp=tokens; *tpp != NULL; tpp++) { token = *tpp; test_assert(fts_filter_filter(stemmer, &token, &error) > 0); test_assert(token != NULL); test_assert(null_strcmp(token, *bpp) == 0); bpp++; } fts_filter_unref(&stemmer); test_assert(stemmer == NULL); test_end(); } static void test_fts_filter_stemmer_snowball_stem_french(void) { struct fts_filter *stemmer; const char *error; const char *token = NULL; const char * const tokens[] = { "Tous", "les", "\xC3\xAAtres", "humains", "naissent", "libres", "et", "\xC3\xA9gaux", "en", "dignit\xC3\xA9", "et", "en", "droits", NULL}; const char * const bases[] = { "Tous" ,"le", "\xC3\xAAtre", "humain", "naissent", "libr", "et", "\xC3\xA9gal", "en", "dignit", "et", "en", "droit", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filter stem French"); test_assert(fts_filter_create(fts_filter_stemmer_snowball, NULL, &french_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp=tokens; *tpp != NULL; tpp++) { token = *tpp; test_assert(fts_filter_filter(stemmer, &token, &error) > 0); test_assert(token != NULL); test_assert(null_strcmp(token, *bpp) == 0); bpp++; } fts_filter_unref(&stemmer); test_assert(stemmer == NULL); test_end(); } static void test_fts_filter_stopwords_stemmer_eng(void) { int ret; struct fts_filter *stemmer; struct fts_filter *filter; const char *error; const char *token = NULL; const char * const tokens[] = { "dries" ,"friendlies", "All", "human", "beings", "are", "born", "free", "and", "equal", "in", "dignity", "and", "rights", "They", "are", "endowed", "with", "reason", "and", "conscience", "and", "should", "act", "towards", "one", "another", "in", "a", "spirit", "of", "brotherhood", NULL}; const char * const bases[] = { "dri" ,"friend", "All", "human", "be", NULL, "born", "free", NULL, "equal", NULL, "digniti", NULL, "right", "They", NULL, "endow", NULL, "reason", NULL, "conscienc", NULL, "should", "act", "toward", "one", "anoth", NULL, NULL, "spirit", NULL, "brotherhood", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filters stopwords and stemming chained, English"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &english_language, stopword_settings, &filter, &error) == 0); test_assert(fts_filter_create(fts_filter_stemmer_snowball, filter, &english_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp=tokens; *tpp != NULL; tpp++) { token = *tpp; ret = fts_filter_filter(stemmer, &token, &error); test_assert(ret >= 0); if (ret == 0) test_assert(*bpp == NULL); else { test_assert(*bpp != NULL); test_assert(null_strcmp(*bpp, token) == 0); } bpp++; } fts_filter_unref(&stemmer); fts_filter_unref(&filter); test_assert(stemmer == NULL); test_assert(filter == NULL); test_end(); } #endif #ifdef HAVE_LIBICU static void test_fts_filter_normalizer_swedish_short(void) { struct fts_filter *norm = NULL; const char *input[] = { "Vem", "\xC3\x85", "\xC3\x85\xC3\x84\xC3\x96", "Vem kan segla f\xC3\xB6rutan vind?\n" "\xC3\x85\xC3\x84\xC3\x96\xC3\xB6\xC3\xA4\xC3\xA5" }; const char *expected_output[] = { "vem", "a", "aao", "vem kan segla forutan vind?\naaooaa" }; const char * const settings[] = {"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC", NULL}; const char *error = NULL; const char *token = NULL; unsigned int i; test_begin("fts filter normalizer Swedish short text"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); for (i = 0; i < N_ELEMENTS(input); i++) { token = input[i]; test_assert_idx(fts_filter_filter(norm, &token, &error) == 1, i); test_assert_idx(null_strcmp(token, expected_output[i]) == 0, i); } fts_filter_unref(&norm); test_assert(norm == NULL); test_end(); } static void test_fts_filter_normalizer_swedish_short_default_id(void) { struct fts_filter *norm = NULL; const char *input[] = { "Vem", "\xC3\x85", "\xC3\x85\xC3\x84\xC3\x96", "Vem kan segla f\xC3\xB6rutan vind?\n" "\xC3\x85\xC3\x84\xC3\x96\xC3\xB6\xC3\xA4\xC3\xA5" }; const char *expected_output[] = { "vem", "a", "aao", "vemkanseglaforutanvind?\naaooaa" }; const char *error = NULL; const char *token = NULL; unsigned int i; test_begin("fts filter normalizer Swedish short text using default ID"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, NULL, &norm, &error) == 0); for (i = 0; i < N_ELEMENTS(input); i++) { token = input[i]; test_assert_idx(fts_filter_filter(norm, &token, &error) == 1, i); test_assert_idx(null_strcmp(token, expected_output[i]) == 0, i); } fts_filter_unref(&norm); test_assert(norm == NULL); test_end(); } /* UDHRDIR comes from Automake AM_CPPFLAGS */ #define UDHR_FRA_NAME "/udhr_fra.txt" static void test_fts_filter_normalizer_french(void) { struct fts_filter *norm = NULL; FILE *input; const char * const settings[] = {"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove", NULL}; char buf[250] = {0}; const char *error = NULL; const char *tokens; unsigned char sha512_digest[SHA512_RESULTLEN]; struct sha512_ctx ctx; const unsigned char correct_digest[] = { 0x06, 0x80, 0xf1, 0x81, 0xf2, 0xed, 0xfb, 0x6d, 0xcd, 0x7d, 0xcb, 0xbd, 0xc4, 0x87, 0xc3, 0xf6, 0xb8, 0x6a, 0x01, 0x82, 0xdf, 0x0a, 0xb5, 0x92, 0x6b, 0x9b, 0x7b, 0x21, 0x5e, 0x62, 0x40, 0xbd, 0xbf, 0x15, 0xb9, 0x7b, 0x75, 0x9c, 0x4e, 0xc9, 0xe8, 0x48, 0xaa, 0x08, 0x63, 0xf2, 0xa0, 0x6c, 0x20, 0x4c, 0x01, 0xe3, 0xb3, 0x4f, 0x15, 0xc6, 0x8c, 0xd6, 0x7a, 0xb7, 0xc5, 0xc6, 0x85, 0x00}; const char *udhr_path; test_begin("fts filter normalizer French UDHR"); udhr_path = t_strconcat(UDHRDIR, UDHR_FRA_NAME, NULL); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); input = fopen(udhr_path, "r"); test_assert(input != NULL); sha512_init(&ctx); while (NULL != fgets(buf, sizeof(buf), input)) { tokens = buf; if (fts_filter_filter(norm, &tokens, &error) != 1){ break; } sha512_loop(&ctx, tokens, strlen(tokens)); } fclose(input); sha512_result(&ctx, sha512_digest); test_assert(memcmp(sha512_digest, correct_digest, sizeof(sha512_digest)) == 0); fts_filter_unref(&norm); test_assert(norm == NULL); test_end(); } static void test_fts_filter_normalizer_empty(void) { /* test just a couple of these */ static const char *empty_tokens[] = { "\xC2\xAF", /* U+00AF */ "\xCC\x80", /* U+0300 */ "\xF3\xA0\x87\xAF", /* U+E01EF */ "\xCC\x80\xF3\xA0\x87\xAF" /* U+0300 U+E01EF */ }; const char * const settings[] = {"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; [\\x20] Remove", NULL}; struct fts_filter *norm; const char *error; unsigned int i; test_begin("fts filter normalizer empty tokens"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); for (i = 0; i < N_ELEMENTS(empty_tokens); i++) { const char *token = empty_tokens[i]; test_assert_idx(fts_filter_filter(norm, &token, &error) == 0, i); } fts_filter_unref(&norm); test_end(); } static void test_fts_filter_normalizer_baddata(void) { const char * const settings[] = {"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove", NULL}; struct fts_filter *norm; const char *token, *error; string_t *str; unsigned int i; test_begin("fts filter normalizer bad data"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); str = t_str_new(128); for (i = 1; i < 0x1ffff; i++) { str_truncate(str, 0); uni_ucs4_to_utf8_c(i, str); token = str_c(str); T_BEGIN { test_assert_idx(fts_filter_filter(norm, &token, &error) >= 0, i); } T_END; } str_truncate(str, 0); uni_ucs4_to_utf8_c(0x7fffffff, str); token = str_c(str); test_assert(fts_filter_filter(norm, &token, &error) >= 0); fts_filter_unref(&norm); test_end(); } static void test_fts_filter_normalizer_invalid_id(void) { struct fts_filter *norm = NULL; const char *settings[] = {"id", "Any-One-Out-There; DKFN; [: Nonspacing Mark :] Remove", NULL}; const char *error = NULL, *token = "foo"; test_begin("fts filter normalizer invalid id"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); test_assert(error == NULL); test_assert(fts_filter_filter(norm, &token, &error) < 0 && error != NULL); fts_filter_unref(&norm); test_end(); } static void test_fts_filter_normalizer_oversized(void) { struct fts_filter *norm = NULL; const char *settings[] = {"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove", "maxlen", "250", NULL}; const char *error = NULL; const char *token = "\xe4\x95\x91\x25\xe2\x94\xad\xe1\x90\xad\xee\x94\x81\xe2\x8e\x9e" "\xe7\x9a\xb7\xea\xbf\x97\xe3\xb2\x8f\xe4\x9c\xbe\xee\xb4\x98\xe1" "\x8d\x99\xe2\x91\x83\xe3\xb1\xb8\xef\xbf\xbd\xe8\xbb\x9c\xef\xbf" "\xbd\xea\xbb\x98\xea\xb5\xac\xe4\x87\xae\xe4\x88\x93\xe9\x86\x8f" "\xe9\x86\x83\xe6\x8f\x8d\xe7\xa3\x9d\xed\x89\x96\xe2\x89\x85\xe6" "\x8c\x82\xec\x80\x98\xee\x91\x96\xe7\xa8\x8a\xec\xbc\x85\xeb\x9c" "\xbd\xeb\x97\x95\xe3\xa4\x9d\xd7\xb1\xea\xa7\x94\xe0\xbb\xac\xee" "\x95\x87\xd5\x9d\xe8\xba\x87\xee\x8b\xae\xe5\xb8\x80\xe9\x8d\x82" "\xe7\xb6\x8c\xe7\x9b\xa0\xef\x82\x9f\xed\x96\xa4\xe3\x8d\xbc\xe1" "\x81\xbd\xe9\x81\xb2\xea\xac\xac\xec\x9b\x98\xe7\x84\xb2\xee\xaf" "\xbc\xeb\xa2\x9d\xe9\x86\xb3\xe0\xb0\x89\xeb\x80\xb6\xe3\x8c\x9d" "\xe9\x8f\x9e\xe2\xae\x8a\xee\x9e\x9a\xef\xbf\xbd\xe7\xa3\x9b\xe4" "\xa3\x8b\xe4\x82\xb9\xeb\x8e\x93\xec\xb5\x82\xe5\xa7\x81\xe2\x8c" "\x97\xea\xbb\xb4\xe5\x85\xb7\xeb\x96\xbe\xe7\x97\x91\xea\xbb\x98" "\xe6\xae\xb4\xe9\x8a\x85\xc4\xb9\xe4\x90\xb2\xe9\x96\xad\xef\x90" "\x9c\xe5\xa6\xae\xe9\x93\x91\xe8\x87\xa1"; test_begin("fts filter normalizer over-sized token"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); test_assert(error == NULL); test_assert(fts_filter_filter(norm, &token, &error) >= 0); test_assert(strlen(token) <= 250); fts_filter_unref(&norm); test_end(); } static void test_fts_filter_normalizer_truncation(void) { struct fts_filter *norm = NULL; const char *settings[] = {"id", "Any-Lower;", "maxlen", "10", NULL}; const char *error = NULL; const char *token = "abcdefghi\xC3\x85"; test_begin("fts filter normalizer token truncated mid letter"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0); test_assert(error == NULL); test_assert(fts_filter_filter(norm, &token, &error) >= 0); test_assert(strcmp(token, "abcdefghi") == 0); fts_filter_unref(&norm); test_end(); } #ifdef HAVE_FTS_STEMMER static void test_fts_filter_normalizer_stopwords_stemmer_eng(void) { int ret; struct fts_filter *normalizer; struct fts_filter *stemmer; struct fts_filter *filter; const char *error; const char * const id_settings[] = //{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC", NULL}; {"id", "Lower", NULL}; const char *token = NULL; const char * const tokens[] = { "dries" ,"friendlies", "All", "human", "beings", "are", "born", "free", "and", "equal", "in", "dignity", "and", "rights", "They", "are", "endowed", "with", "reason", "and", "conscience", "and", "should", "act", "towards", "one", "another", "in", "a", "spirit", "of", "brotherhood", "ABCFoo", NULL}; const char * const bases[] = { "dri" ,"friend", "all", "human", "be", NULL, "born", "free", NULL, "equal", NULL, "digniti", NULL, "right", NULL, NULL, "endow", NULL, "reason", NULL, "conscienc", NULL, "should", "act", "toward", "one", "anoth", NULL, NULL, "spirit", NULL, "brotherhood", "abcfoo", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filters normalizer, stopwords and stemming chained, English"); test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, id_settings, &normalizer, &error) == 0); test_assert(fts_filter_create(fts_filter_stopwords, normalizer, &english_language, stopword_settings, &filter, &error) == 0); test_assert(fts_filter_create(fts_filter_stemmer_snowball, filter, &english_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp = tokens; *tpp != NULL; tpp++) { token = *tpp; ret = fts_filter_filter(stemmer, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*bpp == NULL); } else { test_assert(*bpp != NULL); test_assert(strcmp(*bpp, token) == 0); } bpp++; } fts_filter_unref(&stemmer); fts_filter_unref(&filter); fts_filter_unref(&normalizer); test_assert(stemmer == NULL); test_assert(filter == NULL); test_assert(normalizer == NULL); test_end(); } static void test_fts_filter_stopwords_normalizer_stemmer_no(void) { int ret; struct fts_filter *normalizer; struct fts_filter *stemmer; struct fts_filter *filter; const char *error; const char *token = NULL; const char * const tokens[] = { /* Nynorsk*/ "Alle", "har", "plikter", "andsynes", "samfunnet", "d\xC3\xA5", "personlegdomen", "til", "den", "einskilde", "einast", "der", "kan", "f\xC3\xA5", "frie", "og", "fullgode", "voksterk\xC3\xA5r", /* Bokmal */ "Alle", "mennesker", "er", "f\xC3\xB8""dt", "frie", "og", "med", "samme", "menneskeverd", "og", "menneskerettigheter", "De", "er", "utstyrt", "med", "fornuft", "og", "samvittighet", "og", "b\xC3\xB8r", "handle", "mot", "hverandre", "i", "brorskapets", "\xC3\xA5nd", NULL}; const char * const bases[] = { /* Nynorsk*/ "all", NULL, "plikt", "andsyn", "samfunn", NULL, "personlegdom", NULL, NULL, "einskild", "ein", NULL, NULL, "fa", "frie", NULL, "fullgod", "voksterk", /* Bokmal */ "all", "mennesk", NULL, "f\xC3\xB8""dt", "frie", NULL, NULL, NULL, "menneskeverd", NULL, "menneskerett", "de", NULL, "utstyrt", NULL, "fornuft", NULL, "samvitt", NULL, "b\xC3\xB8r", "handl", NULL, "hverandr", NULL, "brorskap", "and", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filters with stopwords, default normalizer and stemming chained, Norwegian"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &norwegian_language, stopword_settings, &filter, &error) == 0); test_assert(fts_filter_create(fts_filter_normalizer_icu, filter, NULL, NULL, &normalizer, &error) == 0); test_assert(fts_filter_create(fts_filter_stemmer_snowball, normalizer, &norwegian_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp = tokens; *tpp != NULL; tpp++) { token = *tpp; ret = fts_filter_filter(stemmer, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*bpp == NULL); } else { test_assert(*bpp != NULL); test_assert(null_strcmp(*bpp, token) == 0); } bpp++; } fts_filter_unref(&stemmer); fts_filter_unref(&normalizer); fts_filter_unref(&filter); test_assert(stemmer == NULL); test_assert(filter == NULL); test_assert(normalizer == NULL); test_end(); } static void test_fts_filter_stopwords_normalizer_stemmer_sv(void) { int ret; struct fts_filter *normalizer; struct fts_filter *stemmer; struct fts_filter *filter; const char *error; const char *token = NULL; const char * const tokens[] = { "Enär", "erkännandet", "av", "det", "inneboende", "värdet", "hos", "alla", "medlemmar", "av", "människosläktet", "och", "av", "deras", "lika", "och", "oförytterliga", "rättigheter", "är", "grundvalen", "för", "frihet", "rättvisa", "och", "fred", "i", "världen", NULL}; const char * const bases[] = { "enar", "erkan", NULL, NULL, "inneboend", "vardet", "hos", NULL, "medlemm", NULL, "manniskoslaktet", NULL, NULL, NULL, "lik", NULL, "oforytter", "ratt", NULL, "grundval", NULL, "frihet", "rattvis", NULL, "fred", NULL, "varld", NULL}; const char * const *tpp; const char * const *bpp; test_begin("fts filters with stopwords, default normalizer and stemming chained, Swedish"); test_assert(fts_filter_create(fts_filter_stopwords, NULL, &swedish_language, stopword_settings, &filter, &error) == 0); test_assert(fts_filter_create(fts_filter_normalizer_icu, filter, NULL, NULL, &normalizer, &error) == 0); test_assert(fts_filter_create(fts_filter_stemmer_snowball, normalizer, &swedish_language, NULL, &stemmer, &error) == 0); bpp = bases; for (tpp = tokens; *tpp != NULL; tpp++) { token = *tpp; ret = fts_filter_filter(stemmer, &token, &error); if (ret <= 0) { test_assert(ret == 0); test_assert(*bpp == NULL); } else { test_assert(*bpp != NULL); test_assert(null_strcmp(*bpp, token) == 0); } bpp++; } fts_filter_unref(&stemmer); fts_filter_unref(&normalizer); fts_filter_unref(&filter); test_assert(stemmer == NULL); test_assert(filter == NULL); test_assert(normalizer == NULL); test_end(); } #endif #endif static void test_fts_filter_english_possessive(void) { struct fts_filter *norm = NULL; const char *input[] = { "foo'", "foo's", "foo\xC3\xA4's", "foo'S", "foos'S", "foo's's", "foo'ss", "foo\xE2\x80\x99s", "foo\xC3\xA4\xE2\x80\x99s", "foo\xE2\x80\x99S", "foos\xE2\x80\x99S", "foo\xE2\x80\x99s\xE2\x80\x99s", "foo\xE2\x80\x99ss" }; const char *expected_output[] = { "foo'", "foo", "foo\xC3\xA4", "foo", "foos", "foo's", "foo'ss", "foo", "foo\xC3\xA4", "foo", "foos", "foo\xE2\x80\x99s", "foo\xE2\x80\x99ss" }; const char *error = NULL; const char *token = NULL; unsigned int i; test_begin("fts filter english possessive"); test_assert(fts_filter_create(fts_filter_english_possessive, NULL, NULL, NULL, &norm, &error) == 0); for (i = 0; i < N_ELEMENTS(input); i++) { token = input[i]; test_assert_idx(fts_filter_filter(norm, &token, &error) == 1, i); test_assert_idx(null_strcmp(token, expected_output[i]) == 0, i); } fts_filter_unref(&norm); test_assert(norm == NULL); test_end(); } /* TODO: Functions to test 1. ref-unref pairs 2. multiple registers + an unregister + find */ int main(void) { static void (*test_functions[])(void) = { test_fts_filter_find, test_fts_filter_contractions_fail, test_fts_filter_contractions_fr, test_fts_filter_lowercase, #ifdef HAVE_LIBICU test_fts_filter_lowercase_utf8, test_fts_filter_lowercase_too_long_utf8, #endif test_fts_filter_stopwords_eng, test_fts_filter_stopwords_fin, test_fts_filter_stopwords_fra, test_fts_filter_stopwords_no, test_fts_filter_stopwords_fail_lazy_init, test_fts_filter_stopwords_malformed, #ifdef HAVE_FTS_STEMMER test_fts_filter_stemmer_snowball_stem_english, test_fts_filter_stemmer_snowball_stem_french, test_fts_filter_stopwords_stemmer_eng, #endif #ifdef HAVE_LIBICU test_fts_filter_normalizer_swedish_short, test_fts_filter_normalizer_swedish_short_default_id, test_fts_filter_normalizer_french, test_fts_filter_normalizer_empty, test_fts_filter_normalizer_baddata, test_fts_filter_normalizer_invalid_id, test_fts_filter_normalizer_oversized, test_fts_filter_normalizer_truncation, #ifdef HAVE_FTS_STEMMER test_fts_filter_normalizer_stopwords_stemmer_eng, test_fts_filter_stopwords_normalizer_stemmer_no, test_fts_filter_stopwords_normalizer_stemmer_sv, #endif #endif test_fts_filter_english_possessive, NULL }; int ret; fts_filters_init(); ret = test_run(test_functions); fts_filters_deinit(); return ret; } dovecot-2.2.33.2/src/lib-fts/fts-library.h0000644000175000017500000000016113123174404015060 00000000000000#ifndef FTS_LIBRARY_H #define FTS_LIBRARY_H void fts_library_init(void); void fts_library_deinit(void); #endif dovecot-2.2.33.2/src/lib-fts/fts-filter-common.h0000644000175000017500000000020513123174404016166 00000000000000#ifndef FTS_FILTER_COMMON_H #define FTS_FILTER_COMMON_H void fts_filter_truncate_token(string_t *token, size_t max_length); #endif dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-generic.c0000644000175000017500000005272013165463624017056 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "unichar.h" #include "bsearch-insert-pos.h" #include "fts-common.h" #include "fts-tokenizer-private.h" #include "fts-tokenizer-generic-private.h" #include "fts-tokenizer-common.h" #include "word-boundary-data.c" #include "word-break-data.c" #define FTS_DEFAULT_TOKEN_MAX_LENGTH 30 #define FTS_WB5A_PREFIX_MAX_LENGTH 3 /* Including apostrophe */ static unsigned char fts_ascii_word_breaks[128] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0-15 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 16-31 */ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 32-47: !"#$%&()*+,-./ */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 48-63: :;<=>? */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64-79: @ */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 80-95: [\]^ */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96-111: ` */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 /* 112-127: {|}~ */ }; static int fts_tokenizer_generic_create(const char *const *settings, struct fts_tokenizer **tokenizer_r, const char **error_r) { struct generic_fts_tokenizer *tok; unsigned int max_length = FTS_DEFAULT_TOKEN_MAX_LENGTH; enum boundary_algorithm algo = BOUNDARY_ALGORITHM_SIMPLE; bool wb5a = FALSE; unsigned int i; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "maxlen") == 0) { if (str_to_uint(value, &max_length) < 0 || max_length == 0) { *error_r = t_strdup_printf( "Invalid maxlen setting: %s", value); return -1; } } else if (strcmp(key, "algorithm") == 0) { if (strcmp(value, ALGORITHM_TR29_NAME) == 0) algo = BOUNDARY_ALGORITHM_TR29; else if (strcmp(value, ALGORITHM_SIMPLE_NAME) == 0) ; else { *error_r = t_strdup_printf( "Invalid algorithm: %s", value); return -1; } } else if (strcmp(key, "search") == 0) { /* tokenizing a search string - makes no difference to us */ } else if (strcasecmp(key, "wb5a") == 0) { if (strcasecmp(value, "no") == 0) wb5a = FALSE; else wb5a = TRUE; } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } if (wb5a && algo != BOUNDARY_ALGORITHM_TR29) { *error_r = "Can not use WB5a for algorithms other than TR29."; return -1; } tok = i_new(struct generic_fts_tokenizer, 1); if (algo == BOUNDARY_ALGORITHM_TR29) tok->tokenizer.v = &generic_tokenizer_vfuncs_tr29; else tok->tokenizer.v = &generic_tokenizer_vfuncs_simple; tok->max_length = max_length; tok->algorithm = algo; tok->wb5a = wb5a; tok->token = buffer_create_dynamic(default_pool, 64); *tokenizer_r = &tok->tokenizer; return 0; } static void fts_tokenizer_generic_destroy(struct fts_tokenizer *_tok) { struct generic_fts_tokenizer *tok = (struct generic_fts_tokenizer *)_tok; buffer_free(&tok->token); i_free(tok); } static bool fts_tokenizer_generic_simple_current_token(struct generic_fts_tokenizer *tok, const char **token_r) { const unsigned char *data = tok->token->data; size_t len = tok->token->used; if (tok->untruncated_length <= tok->max_length) { /* Remove the trailing apostrophe - it was made into U+0027 earlier. There can be only a single such apostrophe, because otherwise the token would have already been split. We also want to remove the trailing apostrophe only if it's the the last character in the nontruncated token - a truncated token may end with apostrophe. */ if (len > 0 && data[len-1] == '\'') { len--; i_assert(len > 0 && data[len-1] != '\''); } } else { fts_tokenizer_delete_trailing_partial_char(data, &len); } i_assert(len <= tok->max_length); *token_r = len == 0 ? "" : t_strndup(tok->token->data, len); buffer_set_used_size(tok->token, 0); tok->untruncated_length = 0; tok->prev_letter = LETTER_TYPE_NONE; return len > 0; } static bool uint32_find(const uint32_t *data, unsigned int count, uint32_t value, unsigned int *idx_r) { BINARY_NUMBER_SEARCH(data, count, value, idx_r); } static bool fts_uni_word_break(unichar_t c) { unsigned int idx; /* Unicode General Punctuation, including deprecated characters. */ if (c >= 0x2000 && c <= 0x206f) return TRUE; /* From word-break-data.c, which is generated from PropList.txt. */ if (uint32_find(White_Space, N_ELEMENTS(White_Space), c, &idx)) return TRUE; if (uint32_find(Dash, N_ELEMENTS(Dash), c, &idx)) return TRUE; if (uint32_find(Quotation_Mark, N_ELEMENTS(Quotation_Mark), c, &idx)) return TRUE; if (uint32_find(Terminal_Punctuation, N_ELEMENTS(Terminal_Punctuation), c, &idx)) return TRUE; if (uint32_find(STerm, N_ELEMENTS(STerm), c, &idx)) return TRUE; if (uint32_find(Pattern_White_Space, N_ELEMENTS(Pattern_White_Space), c, &idx)) return TRUE; return FALSE; } static inline bool fts_simple_is_word_break(struct generic_fts_tokenizer *tok, unichar_t c, bool apostrophe) { if (apostrophe) return tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE; else if (c < 0x80) return fts_ascii_word_breaks[c] != 0; else return fts_uni_word_break(c); } static void fts_tokenizer_generic_reset(struct fts_tokenizer *_tok) { struct generic_fts_tokenizer *tok = (struct generic_fts_tokenizer *)_tok; tok->prev_letter = LETTER_TYPE_NONE; tok->prev_prev_letter = LETTER_TYPE_NONE; tok->untruncated_length = 0; buffer_set_used_size(tok->token, 0); } static void tok_append_truncated(struct generic_fts_tokenizer *tok, const unsigned char *data, size_t size) { buffer_append(tok->token, data, I_MIN(size, tok->max_length - tok->token->used)); tok->untruncated_length += size; } static int fts_tokenizer_generic_simple_next(struct fts_tokenizer *_tok, const unsigned char *data, size_t size, size_t *skip_r, const char **token_r, const char **error_r ATTR_UNUSED) { struct generic_fts_tokenizer *tok = (struct generic_fts_tokenizer *)_tok; size_t i, start = 0; int char_size; unichar_t c; bool apostrophe; for (i = 0; i < size; i += char_size) { char_size = uni_utf8_get_char_n(data + i, size - i, &c); i_assert(char_size > 0); apostrophe = IS_APOSTROPHE(c); if (fts_simple_is_word_break(tok, c, apostrophe)) { tok_append_truncated(tok, data + start, i - start); if (fts_tokenizer_generic_simple_current_token(tok, token_r)) { *skip_r = i + char_size; return 1; } start = i + char_size; /* it doesn't actually matter at this point how whether subsequent apostrophes are handled by prefix skipping or by ignoring empty tokens - they will be dropped in any case. */ tok->prev_letter = LETTER_TYPE_NONE; } else if (apostrophe) { /* all apostrophes require special handling */ const unsigned char apostrophe_char = '\''; tok_append_truncated(tok, data + start, i - start); if (tok->token->used > 0) tok_append_truncated(tok, &apostrophe_char, 1); start = i + char_size; tok->prev_letter = LETTER_TYPE_SINGLE_QUOTE; } else { tok->prev_letter = LETTER_TYPE_NONE; } } /* word boundary not found yet */ tok_append_truncated(tok, data + start, i - start); *skip_r = i; /* return the last token */ if (size == 0) { if (fts_tokenizer_generic_simple_current_token(tok, token_r)) return 1; } return 0; } /* TODO: Arrange array searches roughly in order of likelyhood of a match. TODO: Make some array of the arrays, so this can be a foreach loop. TODO: Check for Hangul. TODO: Add Hyphens U+002D HYPHEN-MINUS, U+2010 HYPHEN, possibly also U+058A ( ֊ ) ARMENIAN HYPHEN, and U+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN. TODO */ static enum letter_type letter_type(unichar_t c) { unsigned int idx; if (IS_APOSTROPHE(c)) return LETTER_TYPE_APOSTROPHE; if (uint32_find(CR, N_ELEMENTS(CR), c, &idx)) return LETTER_TYPE_CR; if (uint32_find(LF, N_ELEMENTS(LF), c, &idx)) return LETTER_TYPE_LF; if (uint32_find(Newline, N_ELEMENTS(Newline), c, &idx)) return LETTER_TYPE_NEWLINE; if (uint32_find(Extend, N_ELEMENTS(Extend), c, &idx)) return LETTER_TYPE_EXTEND; if (uint32_find(Regional_Indicator, N_ELEMENTS(Regional_Indicator), c, &idx)) return LETTER_TYPE_REGIONAL_INDICATOR; if (uint32_find(Format, N_ELEMENTS(Format), c, &idx)) return LETTER_TYPE_FORMAT; if (uint32_find(Katakana, N_ELEMENTS(Katakana), c, &idx)) return LETTER_TYPE_KATAKANA; if (uint32_find(Hebrew_Letter, N_ELEMENTS(Hebrew_Letter), c, &idx)) return LETTER_TYPE_HEBREW_LETTER; if (uint32_find(ALetter, N_ELEMENTS(ALetter), c, &idx)) return LETTER_TYPE_ALETTER; if (uint32_find(Single_Quote, N_ELEMENTS(Single_Quote), c, &idx)) return LETTER_TYPE_SINGLE_QUOTE; if (uint32_find(Double_Quote, N_ELEMENTS(Double_Quote), c, &idx)) return LETTER_TYPE_DOUBLE_QUOTE; if (uint32_find(MidNumLet, N_ELEMENTS(MidNumLet), c, &idx)) return LETTER_TYPE_MIDNUMLET; if (uint32_find(MidLetter, N_ELEMENTS(MidLetter), c, &idx)) return LETTER_TYPE_MIDLETTER; if (uint32_find(MidNum, N_ELEMENTS(MidNum), c, &idx)) return LETTER_TYPE_MIDNUM; if (uint32_find(Numeric, N_ELEMENTS(Numeric), c, &idx)) return LETTER_TYPE_NUMERIC; if (uint32_find(ExtendNumLet, N_ELEMENTS(ExtendNumLet), c, &idx)) return LETTER_TYPE_EXTENDNUMLET; return LETTER_TYPE_OTHER; } static bool letter_panic(struct generic_fts_tokenizer *tok ATTR_UNUSED) { i_panic("Letter type should not be used."); } /* WB3, WB3a and WB3b, but really different since we try to eat whitespace between words. */ static bool letter_cr_lf_newline(struct generic_fts_tokenizer *tok ATTR_UNUSED) { return TRUE; } static bool letter_extend_format(struct generic_fts_tokenizer *tok ATTR_UNUSED) { /* WB4 */ return FALSE; } static bool letter_regional_indicator(struct generic_fts_tokenizer *tok) { /* WB13c */ if (tok->prev_letter == LETTER_TYPE_REGIONAL_INDICATOR) return FALSE; return TRUE; /* Any / Any */ } static bool letter_katakana(struct generic_fts_tokenizer *tok) { /* WB13 */ if (tok->prev_letter == LETTER_TYPE_KATAKANA) return FALSE; /* WB13b */ if (tok->prev_letter == LETTER_TYPE_EXTENDNUMLET) return FALSE; return TRUE; /* Any / Any */ } static bool letter_hebrew(struct generic_fts_tokenizer *tok) { /* WB5 */ if (tok->prev_letter == LETTER_TYPE_HEBREW_LETTER) return FALSE; /* WB7 WB7c, except MidNumLet */ if (tok->prev_prev_letter == LETTER_TYPE_HEBREW_LETTER && (tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE || tok->prev_letter == LETTER_TYPE_APOSTROPHE || tok->prev_letter == LETTER_TYPE_MIDLETTER || tok->prev_letter == LETTER_TYPE_DOUBLE_QUOTE)) return FALSE; /* WB10 */ if (tok->prev_letter == LETTER_TYPE_NUMERIC) return FALSE; /* WB13b */ if (tok->prev_letter == LETTER_TYPE_EXTENDNUMLET) return FALSE; return TRUE; /* Any / Any */ } static bool letter_aletter(struct generic_fts_tokenizer *tok) { /* WB5a */ if (tok->wb5a && tok->token->used <= FTS_WB5A_PREFIX_MAX_LENGTH) if (IS_WB5A_APOSTROPHE(tok->prev_letter_c) && IS_VOWEL(tok->letter_c)) { tok->seen_wb5a = TRUE; return TRUE; } /* WB5 */ if (tok->prev_letter == LETTER_TYPE_ALETTER) return FALSE; /* WB7, except MidNumLet */ if (tok->prev_prev_letter == LETTER_TYPE_ALETTER && (tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE || tok->prev_letter == LETTER_TYPE_APOSTROPHE || tok->prev_letter == LETTER_TYPE_MIDLETTER)) return FALSE; /* WB10 */ if (tok->prev_letter == LETTER_TYPE_NUMERIC) return FALSE; /* WB13b */ if (tok->prev_letter == LETTER_TYPE_EXTENDNUMLET) return FALSE; return TRUE; /* Any / Any */ } static bool letter_single_quote(struct generic_fts_tokenizer *tok) { /* WB6 */ if (tok->prev_letter == LETTER_TYPE_ALETTER || tok->prev_letter == LETTER_TYPE_HEBREW_LETTER) return FALSE; /* WB12 */ if (tok->prev_letter == LETTER_TYPE_NUMERIC) return FALSE; return TRUE; /* Any / Any */ } static bool letter_double_quote(struct generic_fts_tokenizer *tok) { if (tok->prev_letter == LETTER_TYPE_DOUBLE_QUOTE) return FALSE; return TRUE; /* Any / Any */ } static bool letter_midnumlet(struct generic_fts_tokenizer *tok ATTR_UNUSED) { /* Break at MidNumLet, non-conformant with WB6/WB7 */ return TRUE; } static bool letter_midletter(struct generic_fts_tokenizer *tok) { /* WB6 */ if (tok->prev_letter == LETTER_TYPE_ALETTER || tok->prev_letter == LETTER_TYPE_HEBREW_LETTER) return FALSE; return TRUE; /* Any / Any */ } static bool letter_midnum(struct generic_fts_tokenizer *tok) { /* WB12 */ if (tok->prev_letter == LETTER_TYPE_NUMERIC) return FALSE; return TRUE; /* Any / Any */ } static bool letter_numeric(struct generic_fts_tokenizer *tok) { /* WB8 */ if (tok->prev_letter == LETTER_TYPE_NUMERIC) return FALSE; /* WB9 */ if (tok->prev_letter == LETTER_TYPE_ALETTER || tok->prev_letter == LETTER_TYPE_HEBREW_LETTER) return FALSE; /* WB11 */ if(tok->prev_prev_letter == LETTER_TYPE_NUMERIC && (tok->prev_letter == LETTER_TYPE_MIDNUM || tok->prev_letter == LETTER_TYPE_MIDNUMLET || tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE)) return FALSE; /* WB13b */ if (tok->prev_letter == LETTER_TYPE_EXTENDNUMLET) return FALSE; return TRUE; /* Any / Any */ } static bool letter_extendnumlet(struct generic_fts_tokenizer *tok) { /* WB13a */ if (tok->prev_letter == LETTER_TYPE_ALETTER || tok->prev_letter == LETTER_TYPE_HEBREW_LETTER || tok->prev_letter == LETTER_TYPE_NUMERIC || tok->prev_letter == LETTER_TYPE_KATAKANA || tok->prev_letter == LETTER_TYPE_EXTENDNUMLET) return FALSE; return TRUE; /* Any / Any */ } static bool letter_apostrophe(struct generic_fts_tokenizer *tok) { if (tok->prev_letter == LETTER_TYPE_ALETTER || tok->prev_letter == LETTER_TYPE_HEBREW_LETTER) return FALSE; return TRUE; /* Any / Any */ } static bool letter_other(struct generic_fts_tokenizer *tok ATTR_UNUSED) { return TRUE; /* Any / Any */ } static void add_prev_letter(struct generic_fts_tokenizer *tok, enum letter_type lt) { if(tok->prev_letter != LETTER_TYPE_NONE) tok->prev_prev_letter = tok->prev_letter; tok->prev_letter = lt; } static void add_letter_c(struct generic_fts_tokenizer *tok, unichar_t c) { if(tok->letter_c != 0) tok->prev_letter_c = tok->letter_c; tok->letter_c = c; } /* TODO: Define what to skip between words. TODO: Include double quotation marks? Messes up parsing? TODO: Does this "reverse approach" include too much in "whitespace"? TODO: Possibly use is_word_break()? */ static bool is_nontoken(enum letter_type lt) { if (lt == LETTER_TYPE_REGIONAL_INDICATOR || lt == LETTER_TYPE_KATAKANA || lt == LETTER_TYPE_HEBREW_LETTER || lt == LETTER_TYPE_ALETTER || lt == LETTER_TYPE_NUMERIC) return FALSE; return TRUE; } /* The way things are done WB6/7 and WB11/12 "false positives" can leave trailing unwanted chars. They are searched for here. This is very kludgy and should be coded into the rules themselves somehow. */ static bool is_one_past_end(struct generic_fts_tokenizer *tok) { /* WB6/7 false positive detected at one past end. */ if (tok->prev_letter == LETTER_TYPE_MIDLETTER || tok->prev_letter == LETTER_TYPE_MIDNUMLET || tok->prev_letter == LETTER_TYPE_APOSTROPHE || tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE ) return TRUE; /* WB11/12 false positive detected at one past end. */ if (tok->prev_letter == LETTER_TYPE_MIDNUM || tok->prev_letter == LETTER_TYPE_MIDNUMLET || tok->prev_letter == LETTER_TYPE_APOSTROPHE || tok->prev_letter == LETTER_TYPE_SINGLE_QUOTE) return TRUE; return FALSE; } static void fts_tokenizer_generic_tr29_current_token(struct generic_fts_tokenizer *tok, const char **token_r) { const unsigned char *data = tok->token->data; size_t len = tok->token->used; if (is_one_past_end(tok) && tok->untruncated_length <= tok->max_length) { /* delete the last character */ while (!UTF8_IS_START_SEQ(data[len-1])) len--; i_assert(len > 0); len--; } else if (tok->untruncated_length > tok->max_length) { fts_tokenizer_delete_trailing_partial_char(data, &len); } /* we're skipping all non-token chars at the beginning of the word, so by this point we must have something here - even if we just deleted the last character */ i_assert(len > 0); i_assert(len <= tok->max_length); tok->prev_prev_letter = LETTER_TYPE_NONE; tok->prev_letter = LETTER_TYPE_NONE; *token_r = t_strndup(data, len); buffer_set_used_size(tok->token, 0); tok->untruncated_length = 0; } static void wb5a_reinsert(struct generic_fts_tokenizer *tok) { string_t *utf8_str = t_str_new(6); uni_ucs4_to_utf8_c(tok->letter_c, utf8_str); buffer_insert(tok->token, 0, str_data(utf8_str), str_len(utf8_str)); tok->prev_letter = letter_type(tok->letter_c); tok->letter_c = 0; tok->prev_letter_c = 0; tok->seen_wb5a = FALSE; } struct letter_fn { bool (*fn)(struct generic_fts_tokenizer *tok); }; static struct letter_fn letter_fns[] = { {letter_panic}, {letter_cr_lf_newline}, {letter_cr_lf_newline}, {letter_cr_lf_newline}, {letter_extend_format}, {letter_regional_indicator}, {letter_extend_format}, {letter_katakana}, {letter_hebrew}, {letter_aletter}, {letter_single_quote}, {letter_double_quote}, {letter_midnumlet}, {letter_midletter}, {letter_midnum}, {letter_numeric}, {letter_extendnumlet}, {letter_panic}, {letter_panic}, {letter_apostrophe}, {letter_other} }; /* Find word boundaries in input text. Based on Unicode standard annex #29, but tailored for FTS purposes. http://www.unicode.org/reports/tr29/ Note: The text of tr29 is a living standard, so it keeps changing. In newer specs some characters are combined, like AHLetter (ALetter | Hebrew_Letter) and MidNumLetQ (MidNumLet | Single_Quote). Adaptions: * Added optional WB5a as a configurable option. The cut of prefix is max FTS_WB5A_PREFIX chars. * No word boundary at Start-Of-Text or End-of-Text (Wb1 and WB2). * Break just once, not before and after. * Break at MidNumLet, except apostrophes (diverging from WB6/WB7). * Other things also (e.g. is_nontoken(), not really pure tr29. Meant to assist in finding individual words. */ static bool uni_found_word_boundary(struct generic_fts_tokenizer *tok, enum letter_type lt) { /* No rule knows what to do with just one char, except the linebreaks we eat away (above) anyway. */ if (tok->prev_letter != LETTER_TYPE_NONE) { if (letter_fns[lt].fn(tok)) return TRUE; } if (lt == LETTER_TYPE_EXTEND || lt == LETTER_TYPE_FORMAT) { /* These types are completely ignored. */ } else { add_prev_letter(tok,lt); } return FALSE; } static int fts_tokenizer_generic_tr29_next(struct fts_tokenizer *_tok, const unsigned char *data, size_t size, size_t *skip_r, const char **token_r, const char **error_r ATTR_UNUSED) { struct generic_fts_tokenizer *tok = (struct generic_fts_tokenizer *)_tok; unichar_t c; size_t i, char_start_i, start_pos = 0; enum letter_type lt; int char_size; for (i = 0; i < size; ) { char_start_i = i; char_size = uni_utf8_get_char_n(data + i, size - i, &c); i_assert(char_size > 0); i += char_size; lt = letter_type(c); /* The WB5a break is detected only when the "after break" char is inspected. That char needs to be reinserted as the "previous char". */ if (tok->seen_wb5a) wb5a_reinsert(tok); if (tok->prev_letter == LETTER_TYPE_NONE && is_nontoken(lt)) { /* Skip non-token chars at the beginning of token */ i_assert(tok->token->used == 0); start_pos = i; continue; } if (tok->wb5a && tok->token->used <= FTS_WB5A_PREFIX_MAX_LENGTH) add_letter_c(tok, c); if (uni_found_word_boundary(tok, lt)) { i_assert(char_start_i >= start_pos && size >= start_pos); tok_append_truncated(tok, data + start_pos, char_start_i - start_pos); *skip_r = i; fts_tokenizer_generic_tr29_current_token(tok, token_r); return 1; } else if (lt == LETTER_TYPE_APOSTROPHE || lt == LETTER_TYPE_SINGLE_QUOTE) { /* all apostrophes require special handling */ const unsigned char apostrophe_char = '\''; tok_append_truncated(tok, data + start_pos, char_start_i - start_pos); tok_append_truncated(tok, &apostrophe_char, 1); start_pos = i; } } i_assert(i >= start_pos && size >= start_pos); tok_append_truncated(tok, data + start_pos, i - start_pos); *skip_r = i; if (size == 0 && tok->token->used > 0) { /* return the last token */ *skip_r = 0; fts_tokenizer_generic_tr29_current_token(tok, token_r); return 1; } return 0; } static int fts_tokenizer_generic_next(struct fts_tokenizer *_tok ATTR_UNUSED, const unsigned char *data ATTR_UNUSED, size_t size ATTR_UNUSED, size_t *skip_r ATTR_UNUSED, const char **token_r ATTR_UNUSED, const char **error_r ATTR_UNUSED) { i_unreached(); } static const struct fts_tokenizer_vfuncs generic_tokenizer_vfuncs = { fts_tokenizer_generic_create, fts_tokenizer_generic_destroy, fts_tokenizer_generic_reset, fts_tokenizer_generic_next }; static const struct fts_tokenizer fts_tokenizer_generic_real = { .name = "generic", .v = &generic_tokenizer_vfuncs }; const struct fts_tokenizer *fts_tokenizer_generic = &fts_tokenizer_generic_real; const struct fts_tokenizer_vfuncs generic_tokenizer_vfuncs_simple = { fts_tokenizer_generic_create, fts_tokenizer_generic_destroy, fts_tokenizer_generic_reset, fts_tokenizer_generic_simple_next }; const struct fts_tokenizer_vfuncs generic_tokenizer_vfuncs_tr29 = { fts_tokenizer_generic_create, fts_tokenizer_generic_destroy, fts_tokenizer_generic_reset, fts_tokenizer_generic_tr29_next }; dovecot-2.2.33.2/src/lib-fts/PropList.txt0000644000175000017500000034253412723566636015024 00000000000000# PropList-9.0.0.txt # Date: 2016-06-01, 10:34:30 GMT # © 2016 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see http://www.unicode.org/terms_of_use.html # # Unicode Character Database # For documentation, see http://www.unicode.org/reports/tr44/ # ================================================ 0009..000D ; White_Space # Cc [5] .. 0020 ; White_Space # Zs SPACE 0085 ; White_Space # Cc 00A0 ; White_Space # Zs NO-BREAK SPACE 1680 ; White_Space # Zs OGHAM SPACE MARK 2000..200A ; White_Space # Zs [11] EN QUAD..HAIR SPACE 2028 ; White_Space # Zl LINE SEPARATOR 2029 ; White_Space # Zp PARAGRAPH SEPARATOR 202F ; White_Space # Zs NARROW NO-BREAK SPACE 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE 3000 ; White_Space # Zs IDEOGRAPHIC SPACE # Total code points: 25 # ================================================ 061C ; Bidi_Control # Cf ARABIC LETTER MARK 200E..200F ; Bidi_Control # Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK 202A..202E ; Bidi_Control # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE 2066..2069 ; Bidi_Control # Cf [4] LEFT-TO-RIGHT ISOLATE..POP DIRECTIONAL ISOLATE # Total code points: 12 # ================================================ 200C..200D ; Join_Control # Cf [2] ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER # Total code points: 2 # ================================================ 002D ; Dash # Pd HYPHEN-MINUS 058A ; Dash # Pd ARMENIAN HYPHEN 05BE ; Dash # Pd HEBREW PUNCTUATION MAQAF 1400 ; Dash # Pd CANADIAN SYLLABICS HYPHEN 1806 ; Dash # Pd MONGOLIAN TODO SOFT HYPHEN 2010..2015 ; Dash # Pd [6] HYPHEN..HORIZONTAL BAR 2053 ; Dash # Po SWUNG DASH 207B ; Dash # Sm SUPERSCRIPT MINUS 208B ; Dash # Sm SUBSCRIPT MINUS 2212 ; Dash # Sm MINUS SIGN 2E17 ; Dash # Pd DOUBLE OBLIQUE HYPHEN 2E1A ; Dash # Pd HYPHEN WITH DIAERESIS 2E3A..2E3B ; Dash # Pd [2] TWO-EM DASH..THREE-EM DASH 2E40 ; Dash # Pd DOUBLE HYPHEN 301C ; Dash # Pd WAVE DASH 3030 ; Dash # Pd WAVY DASH 30A0 ; Dash # Pd KATAKANA-HIRAGANA DOUBLE HYPHEN FE31..FE32 ; Dash # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH FE58 ; Dash # Pd SMALL EM DASH FE63 ; Dash # Pd SMALL HYPHEN-MINUS FF0D ; Dash # Pd FULLWIDTH HYPHEN-MINUS # Total code points: 28 # ================================================ 002D ; Hyphen # Pd HYPHEN-MINUS 00AD ; Hyphen # Cf SOFT HYPHEN 058A ; Hyphen # Pd ARMENIAN HYPHEN 1806 ; Hyphen # Pd MONGOLIAN TODO SOFT HYPHEN 2010..2011 ; Hyphen # Pd [2] HYPHEN..NON-BREAKING HYPHEN 2E17 ; Hyphen # Pd DOUBLE OBLIQUE HYPHEN 30FB ; Hyphen # Po KATAKANA MIDDLE DOT FE63 ; Hyphen # Pd SMALL HYPHEN-MINUS FF0D ; Hyphen # Pd FULLWIDTH HYPHEN-MINUS FF65 ; Hyphen # Po HALFWIDTH KATAKANA MIDDLE DOT # Total code points: 11 # ================================================ 0022 ; Quotation_Mark # Po QUOTATION MARK 0027 ; Quotation_Mark # Po APOSTROPHE 00AB ; Quotation_Mark # Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 00BB ; Quotation_Mark # Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 2018 ; Quotation_Mark # Pi LEFT SINGLE QUOTATION MARK 2019 ; Quotation_Mark # Pf RIGHT SINGLE QUOTATION MARK 201A ; Quotation_Mark # Ps SINGLE LOW-9 QUOTATION MARK 201B..201C ; Quotation_Mark # Pi [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK 201D ; Quotation_Mark # Pf RIGHT DOUBLE QUOTATION MARK 201E ; Quotation_Mark # Ps DOUBLE LOW-9 QUOTATION MARK 201F ; Quotation_Mark # Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK 2039 ; Quotation_Mark # Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK 203A ; Quotation_Mark # Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 2E42 ; Quotation_Mark # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK 300C ; Quotation_Mark # Ps LEFT CORNER BRACKET 300D ; Quotation_Mark # Pe RIGHT CORNER BRACKET 300E ; Quotation_Mark # Ps LEFT WHITE CORNER BRACKET 300F ; Quotation_Mark # Pe RIGHT WHITE CORNER BRACKET 301D ; Quotation_Mark # Ps REVERSED DOUBLE PRIME QUOTATION MARK 301E..301F ; Quotation_Mark # Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK FE41 ; Quotation_Mark # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET FE42 ; Quotation_Mark # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET FE43 ; Quotation_Mark # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET FE44 ; Quotation_Mark # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET FF02 ; Quotation_Mark # Po FULLWIDTH QUOTATION MARK FF07 ; Quotation_Mark # Po FULLWIDTH APOSTROPHE FF62 ; Quotation_Mark # Ps HALFWIDTH LEFT CORNER BRACKET FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET # Total code points: 30 # ================================================ 0021 ; Terminal_Punctuation # Po EXCLAMATION MARK 002C ; Terminal_Punctuation # Po COMMA 002E ; Terminal_Punctuation # Po FULL STOP 003A..003B ; Terminal_Punctuation # Po [2] COLON..SEMICOLON 003F ; Terminal_Punctuation # Po QUESTION MARK 037E ; Terminal_Punctuation # Po GREEK QUESTION MARK 0387 ; Terminal_Punctuation # Po GREEK ANO TELEIA 0589 ; Terminal_Punctuation # Po ARMENIAN FULL STOP 05C3 ; Terminal_Punctuation # Po HEBREW PUNCTUATION SOF PASUQ 060C ; Terminal_Punctuation # Po ARABIC COMMA 061B ; Terminal_Punctuation # Po ARABIC SEMICOLON 061F ; Terminal_Punctuation # Po ARABIC QUESTION MARK 06D4 ; Terminal_Punctuation # Po ARABIC FULL STOP 0700..070A ; Terminal_Punctuation # Po [11] SYRIAC END OF PARAGRAPH..SYRIAC CONTRACTION 070C ; Terminal_Punctuation # Po SYRIAC HARKLEAN METOBELUS 07F8..07F9 ; Terminal_Punctuation # Po [2] NKO COMMA..NKO EXCLAMATION MARK 0830..083E ; Terminal_Punctuation # Po [15] SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU 085E ; Terminal_Punctuation # Po MANDAIC PUNCTUATION 0964..0965 ; Terminal_Punctuation # Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA 0E5A..0E5B ; Terminal_Punctuation # Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT 0F08 ; Terminal_Punctuation # Po TIBETAN MARK SBRUL SHAD 0F0D..0F12 ; Terminal_Punctuation # Po [6] TIBETAN MARK SHAD..TIBETAN MARK RGYA GRAM SHAD 104A..104B ; Terminal_Punctuation # Po [2] MYANMAR SIGN LITTLE SECTION..MYANMAR SIGN SECTION 1361..1368 ; Terminal_Punctuation # Po [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR 166D..166E ; Terminal_Punctuation # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP 16EB..16ED ; Terminal_Punctuation # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION 1735..1736 ; Terminal_Punctuation # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION 17D4..17D6 ; Terminal_Punctuation # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH 17DA ; Terminal_Punctuation # Po KHMER SIGN KOOMUUT 1802..1805 ; Terminal_Punctuation # Po [4] MONGOLIAN COMMA..MONGOLIAN FOUR DOTS 1808..1809 ; Terminal_Punctuation # Po [2] MONGOLIAN MANCHU COMMA..MONGOLIAN MANCHU FULL STOP 1944..1945 ; Terminal_Punctuation # Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK 1AA8..1AAB ; Terminal_Punctuation # Po [4] TAI THAM SIGN KAAN..TAI THAM SIGN SATKAANKUU 1B5A..1B5B ; Terminal_Punctuation # Po [2] BALINESE PANTI..BALINESE PAMADA 1B5D..1B5F ; Terminal_Punctuation # Po [3] BALINESE CARIK PAMUNGKAH..BALINESE CARIK PAREREN 1C3B..1C3F ; Terminal_Punctuation # Po [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK 1C7E..1C7F ; Terminal_Punctuation # Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD 203C..203D ; Terminal_Punctuation # Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG 2047..2049 ; Terminal_Punctuation # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK 2E2E ; Terminal_Punctuation # Po REVERSED QUESTION MARK 2E3C ; Terminal_Punctuation # Po STENOGRAPHIC FULL STOP 2E41 ; Terminal_Punctuation # Po REVERSED COMMA 3001..3002 ; Terminal_Punctuation # Po [2] IDEOGRAPHIC COMMA..IDEOGRAPHIC FULL STOP A4FE..A4FF ; Terminal_Punctuation # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP A60D..A60F ; Terminal_Punctuation # Po [3] VAI COMMA..VAI QUESTION MARK A6F3..A6F7 ; Terminal_Punctuation # Po [5] BAMUM FULL STOP..BAMUM QUESTION MARK A876..A877 ; Terminal_Punctuation # Po [2] PHAGS-PA MARK SHAD..PHAGS-PA MARK DOUBLE SHAD A8CE..A8CF ; Terminal_Punctuation # Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA A92F ; Terminal_Punctuation # Po KAYAH LI SIGN SHYA A9C7..A9C9 ; Terminal_Punctuation # Po [3] JAVANESE PADA PANGKAT..JAVANESE PADA LUNGSI AA5D..AA5F ; Terminal_Punctuation # Po [3] CHAM PUNCTUATION DANDA..CHAM PUNCTUATION TRIPLE DANDA AADF ; Terminal_Punctuation # Po TAI VIET SYMBOL KOI KOI AAF0..AAF1 ; Terminal_Punctuation # Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM ABEB ; Terminal_Punctuation # Po MEETEI MAYEK CHEIKHEI FE50..FE52 ; Terminal_Punctuation # Po [3] SMALL COMMA..SMALL FULL STOP FE54..FE57 ; Terminal_Punctuation # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK FF01 ; Terminal_Punctuation # Po FULLWIDTH EXCLAMATION MARK FF0C ; Terminal_Punctuation # Po FULLWIDTH COMMA FF0E ; Terminal_Punctuation # Po FULLWIDTH FULL STOP FF1A..FF1B ; Terminal_Punctuation # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON FF1F ; Terminal_Punctuation # Po FULLWIDTH QUESTION MARK FF61 ; Terminal_Punctuation # Po HALFWIDTH IDEOGRAPHIC FULL STOP FF64 ; Terminal_Punctuation # Po HALFWIDTH IDEOGRAPHIC COMMA 1039F ; Terminal_Punctuation # Po UGARITIC WORD DIVIDER 103D0 ; Terminal_Punctuation # Po OLD PERSIAN WORD DIVIDER 10857 ; Terminal_Punctuation # Po IMPERIAL ARAMAIC SECTION SIGN 1091F ; Terminal_Punctuation # Po PHOENICIAN WORD SEPARATOR 10A56..10A57 ; Terminal_Punctuation # Po [2] KHAROSHTHI PUNCTUATION DANDA..KHAROSHTHI PUNCTUATION DOUBLE DANDA 10AF0..10AF5 ; Terminal_Punctuation # Po [6] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION TWO DOTS 10B3A..10B3F ; Terminal_Punctuation # Po [6] TINY TWO DOTS OVER ONE DOT PUNCTUATION..LARGE ONE RING OVER TWO RINGS PUNCTUATION 10B99..10B9C ; Terminal_Punctuation # Po [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT 11047..1104D ; Terminal_Punctuation # Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS 110BE..110C1 ; Terminal_Punctuation # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA 11141..11143 ; Terminal_Punctuation # Po [3] CHAKMA DANDA..CHAKMA QUESTION MARK 111C5..111C6 ; Terminal_Punctuation # Po [2] SHARADA DANDA..SHARADA DOUBLE DANDA 111CD ; Terminal_Punctuation # Po SHARADA SUTRA MARK 111DE..111DF ; Terminal_Punctuation # Po [2] SHARADA SECTION MARK-1..SHARADA SECTION MARK-2 11238..1123C ; Terminal_Punctuation # Po [5] KHOJKI DANDA..KHOJKI DOUBLE SECTION MARK 112A9 ; Terminal_Punctuation # Po MULTANI SECTION MARK 1144B..1144D ; Terminal_Punctuation # Po [3] NEWA DANDA..NEWA COMMA 1145B ; Terminal_Punctuation # Po NEWA PLACEHOLDER MARK 115C2..115C5 ; Terminal_Punctuation # Po [4] SIDDHAM DANDA..SIDDHAM SEPARATOR BAR 115C9..115D7 ; Terminal_Punctuation # Po [15] SIDDHAM END OF TEXT MARK..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES 11641..11642 ; Terminal_Punctuation # Po [2] MODI DANDA..MODI DOUBLE DANDA 1173C..1173E ; Terminal_Punctuation # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI 11C41..11C43 ; Terminal_Punctuation # Po [3] BHAIKSUKI DANDA..BHAIKSUKI WORD SEPARATOR 11C71 ; Terminal_Punctuation # Po MARCHEN MARK SHAD 12470..12474 ; Terminal_Punctuation # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON 16A6E..16A6F ; Terminal_Punctuation # Po [2] MRO DANDA..MRO DOUBLE DANDA 16AF5 ; Terminal_Punctuation # Po BASSA VAH FULL STOP 16B37..16B39 ; Terminal_Punctuation # Po [3] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN CIM CHEEM 16B44 ; Terminal_Punctuation # Po PAHAWH HMONG SIGN XAUS 1BC9F ; Terminal_Punctuation # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP 1DA87..1DA8A ; Terminal_Punctuation # Po [4] SIGNWRITING COMMA..SIGNWRITING COLON # Total code points: 246 # ================================================ 005E ; Other_Math # Sk CIRCUMFLEX ACCENT 03D0..03D2 ; Other_Math # L& [3] GREEK BETA SYMBOL..GREEK UPSILON WITH HOOK SYMBOL 03D5 ; Other_Math # L& GREEK PHI SYMBOL 03F0..03F1 ; Other_Math # L& [2] GREEK KAPPA SYMBOL..GREEK RHO SYMBOL 03F4..03F5 ; Other_Math # L& [2] GREEK CAPITAL THETA SYMBOL..GREEK LUNATE EPSILON SYMBOL 2016 ; Other_Math # Po DOUBLE VERTICAL LINE 2032..2034 ; Other_Math # Po [3] PRIME..TRIPLE PRIME 2040 ; Other_Math # Pc CHARACTER TIE 2061..2064 ; Other_Math # Cf [4] FUNCTION APPLICATION..INVISIBLE PLUS 207D ; Other_Math # Ps SUPERSCRIPT LEFT PARENTHESIS 207E ; Other_Math # Pe SUPERSCRIPT RIGHT PARENTHESIS 208D ; Other_Math # Ps SUBSCRIPT LEFT PARENTHESIS 208E ; Other_Math # Pe SUBSCRIPT RIGHT PARENTHESIS 20D0..20DC ; Other_Math # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE 20E1 ; Other_Math # Mn COMBINING LEFT RIGHT ARROW ABOVE 20E5..20E6 ; Other_Math # Mn [2] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING DOUBLE VERTICAL STROKE OVERLAY 20EB..20EF ; Other_Math # Mn [5] COMBINING LONG DOUBLE SOLIDUS OVERLAY..COMBINING RIGHT ARROW BELOW 2102 ; Other_Math # L& DOUBLE-STRUCK CAPITAL C 2107 ; Other_Math # L& EULER CONSTANT 210A..2113 ; Other_Math # L& [10] SCRIPT SMALL G..SCRIPT SMALL L 2115 ; Other_Math # L& DOUBLE-STRUCK CAPITAL N 2119..211D ; Other_Math # L& [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R 2124 ; Other_Math # L& DOUBLE-STRUCK CAPITAL Z 2128 ; Other_Math # L& BLACK-LETTER CAPITAL Z 2129 ; Other_Math # So TURNED GREEK SMALL LETTER IOTA 212C..212D ; Other_Math # L& [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C 212F..2131 ; Other_Math # L& [3] SCRIPT SMALL E..SCRIPT CAPITAL F 2133..2134 ; Other_Math # L& [2] SCRIPT CAPITAL M..SCRIPT SMALL O 2135..2138 ; Other_Math # Lo [4] ALEF SYMBOL..DALET SYMBOL 213C..213F ; Other_Math # L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI 2145..2149 ; Other_Math # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J 2195..2199 ; Other_Math # So [5] UP DOWN ARROW..SOUTH WEST ARROW 219C..219F ; Other_Math # So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW 21A1..21A2 ; Other_Math # So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL 21A4..21A5 ; Other_Math # So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR 21A7 ; Other_Math # So DOWNWARDS ARROW FROM BAR 21A9..21AD ; Other_Math # So [5] LEFTWARDS ARROW WITH HOOK..LEFT RIGHT WAVE ARROW 21B0..21B1 ; Other_Math # So [2] UPWARDS ARROW WITH TIP LEFTWARDS..UPWARDS ARROW WITH TIP RIGHTWARDS 21B6..21B7 ; Other_Math # So [2] ANTICLOCKWISE TOP SEMICIRCLE ARROW..CLOCKWISE TOP SEMICIRCLE ARROW 21BC..21CD ; Other_Math # So [18] LEFTWARDS HARPOON WITH BARB UPWARDS..LEFTWARDS DOUBLE ARROW WITH STROKE 21D0..21D1 ; Other_Math # So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW 21D3 ; Other_Math # So DOWNWARDS DOUBLE ARROW 21D5..21DB ; Other_Math # So [7] UP DOWN DOUBLE ARROW..RIGHTWARDS TRIPLE ARROW 21DD ; Other_Math # So RIGHTWARDS SQUIGGLE ARROW 21E4..21E5 ; Other_Math # So [2] LEFTWARDS ARROW TO BAR..RIGHTWARDS ARROW TO BAR 2308 ; Other_Math # Ps LEFT CEILING 2309 ; Other_Math # Pe RIGHT CEILING 230A ; Other_Math # Ps LEFT FLOOR 230B ; Other_Math # Pe RIGHT FLOOR 23B4..23B5 ; Other_Math # So [2] TOP SQUARE BRACKET..BOTTOM SQUARE BRACKET 23B7 ; Other_Math # So RADICAL SYMBOL BOTTOM 23D0 ; Other_Math # So VERTICAL LINE EXTENSION 23E2 ; Other_Math # So WHITE TRAPEZIUM 25A0..25A1 ; Other_Math # So [2] BLACK SQUARE..WHITE SQUARE 25AE..25B6 ; Other_Math # So [9] BLACK VERTICAL RECTANGLE..BLACK RIGHT-POINTING TRIANGLE 25BC..25C0 ; Other_Math # So [5] BLACK DOWN-POINTING TRIANGLE..BLACK LEFT-POINTING TRIANGLE 25C6..25C7 ; Other_Math # So [2] BLACK DIAMOND..WHITE DIAMOND 25CA..25CB ; Other_Math # So [2] LOZENGE..WHITE CIRCLE 25CF..25D3 ; Other_Math # So [5] BLACK CIRCLE..CIRCLE WITH UPPER HALF BLACK 25E2 ; Other_Math # So BLACK LOWER RIGHT TRIANGLE 25E4 ; Other_Math # So BLACK UPPER LEFT TRIANGLE 25E7..25EC ; Other_Math # So [6] SQUARE WITH LEFT HALF BLACK..WHITE UP-POINTING TRIANGLE WITH DOT 2605..2606 ; Other_Math # So [2] BLACK STAR..WHITE STAR 2640 ; Other_Math # So FEMALE SIGN 2642 ; Other_Math # So MALE SIGN 2660..2663 ; Other_Math # So [4] BLACK SPADE SUIT..BLACK CLUB SUIT 266D..266E ; Other_Math # So [2] MUSIC FLAT SIGN..MUSIC NATURAL SIGN 27C5 ; Other_Math # Ps LEFT S-SHAPED BAG DELIMITER 27C6 ; Other_Math # Pe RIGHT S-SHAPED BAG DELIMITER 27E6 ; Other_Math # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET 27E7 ; Other_Math # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET 27E8 ; Other_Math # Ps MATHEMATICAL LEFT ANGLE BRACKET 27E9 ; Other_Math # Pe MATHEMATICAL RIGHT ANGLE BRACKET 27EA ; Other_Math # Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET 27EB ; Other_Math # Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET 27EC ; Other_Math # Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET 27ED ; Other_Math # Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET 27EE ; Other_Math # Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS 27EF ; Other_Math # Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS 2983 ; Other_Math # Ps LEFT WHITE CURLY BRACKET 2984 ; Other_Math # Pe RIGHT WHITE CURLY BRACKET 2985 ; Other_Math # Ps LEFT WHITE PARENTHESIS 2986 ; Other_Math # Pe RIGHT WHITE PARENTHESIS 2987 ; Other_Math # Ps Z NOTATION LEFT IMAGE BRACKET 2988 ; Other_Math # Pe Z NOTATION RIGHT IMAGE BRACKET 2989 ; Other_Math # Ps Z NOTATION LEFT BINDING BRACKET 298A ; Other_Math # Pe Z NOTATION RIGHT BINDING BRACKET 298B ; Other_Math # Ps LEFT SQUARE BRACKET WITH UNDERBAR 298C ; Other_Math # Pe RIGHT SQUARE BRACKET WITH UNDERBAR 298D ; Other_Math # Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER 298E ; Other_Math # Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 298F ; Other_Math # Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 2990 ; Other_Math # Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER 2991 ; Other_Math # Ps LEFT ANGLE BRACKET WITH DOT 2992 ; Other_Math # Pe RIGHT ANGLE BRACKET WITH DOT 2993 ; Other_Math # Ps LEFT ARC LESS-THAN BRACKET 2994 ; Other_Math # Pe RIGHT ARC GREATER-THAN BRACKET 2995 ; Other_Math # Ps DOUBLE LEFT ARC GREATER-THAN BRACKET 2996 ; Other_Math # Pe DOUBLE RIGHT ARC LESS-THAN BRACKET 2997 ; Other_Math # Ps LEFT BLACK TORTOISE SHELL BRACKET 2998 ; Other_Math # Pe RIGHT BLACK TORTOISE SHELL BRACKET 29D8 ; Other_Math # Ps LEFT WIGGLY FENCE 29D9 ; Other_Math # Pe RIGHT WIGGLY FENCE 29DA ; Other_Math # Ps LEFT DOUBLE WIGGLY FENCE 29DB ; Other_Math # Pe RIGHT DOUBLE WIGGLY FENCE 29FC ; Other_Math # Ps LEFT-POINTING CURVED ANGLE BRACKET 29FD ; Other_Math # Pe RIGHT-POINTING CURVED ANGLE BRACKET FE61 ; Other_Math # Po SMALL ASTERISK FE63 ; Other_Math # Pd SMALL HYPHEN-MINUS FE68 ; Other_Math # Po SMALL REVERSE SOLIDUS FF3C ; Other_Math # Po FULLWIDTH REVERSE SOLIDUS FF3E ; Other_Math # Sk FULLWIDTH CIRCUMFLEX ACCENT 1D400..1D454 ; Other_Math # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G 1D456..1D49C ; Other_Math # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A 1D49E..1D49F ; Other_Math # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D 1D4A2 ; Other_Math # L& MATHEMATICAL SCRIPT CAPITAL G 1D4A5..1D4A6 ; Other_Math # L& [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K 1D4A9..1D4AC ; Other_Math # L& [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q 1D4AE..1D4B9 ; Other_Math # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D 1D4BB ; Other_Math # L& MATHEMATICAL SCRIPT SMALL F 1D4BD..1D4C3 ; Other_Math # L& [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N 1D4C5..1D505 ; Other_Math # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B 1D507..1D50A ; Other_Math # L& [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G 1D50D..1D514 ; Other_Math # L& [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q 1D516..1D51C ; Other_Math # L& [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y 1D51E..1D539 ; Other_Math # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B 1D53B..1D53E ; Other_Math # L& [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G 1D540..1D544 ; Other_Math # L& [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M 1D546 ; Other_Math # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O 1D54A..1D550 ; Other_Math # L& [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y 1D552..1D6A5 ; Other_Math # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J 1D6A8..1D6C0 ; Other_Math # L& [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA 1D6C2..1D6DA ; Other_Math # L& [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA 1D6DC..1D6FA ; Other_Math # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA 1D6FC..1D714 ; Other_Math # L& [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA 1D716..1D734 ; Other_Math # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA 1D736..1D74E ; Other_Math # L& [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA 1D750..1D76E ; Other_Math # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA 1D770..1D788 ; Other_Math # L& [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA 1D78A..1D7A8 ; Other_Math # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA 1D7AA..1D7C2 ; Other_Math # L& [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA 1D7C4..1D7CB ; Other_Math # L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA 1D7CE..1D7FF ; Other_Math # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE 1EE00..1EE03 ; Other_Math # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL 1EE05..1EE1F ; Other_Math # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF 1EE21..1EE22 ; Other_Math # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM 1EE24 ; Other_Math # Lo ARABIC MATHEMATICAL INITIAL HEH 1EE27 ; Other_Math # Lo ARABIC MATHEMATICAL INITIAL HAH 1EE29..1EE32 ; Other_Math # Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF 1EE34..1EE37 ; Other_Math # Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH 1EE39 ; Other_Math # Lo ARABIC MATHEMATICAL INITIAL DAD 1EE3B ; Other_Math # Lo ARABIC MATHEMATICAL INITIAL GHAIN 1EE42 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED JEEM 1EE47 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED HAH 1EE49 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED YEH 1EE4B ; Other_Math # Lo ARABIC MATHEMATICAL TAILED LAM 1EE4D..1EE4F ; Other_Math # Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN 1EE51..1EE52 ; Other_Math # Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF 1EE54 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED SHEEN 1EE57 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED KHAH 1EE59 ; Other_Math # Lo ARABIC MATHEMATICAL TAILED DAD 1EE5B ; Other_Math # Lo ARABIC MATHEMATICAL TAILED GHAIN 1EE5D ; Other_Math # Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON 1EE5F ; Other_Math # Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF 1EE61..1EE62 ; Other_Math # Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM 1EE64 ; Other_Math # Lo ARABIC MATHEMATICAL STRETCHED HEH 1EE67..1EE6A ; Other_Math # Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF 1EE6C..1EE72 ; Other_Math # Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF 1EE74..1EE77 ; Other_Math # Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH 1EE79..1EE7C ; Other_Math # Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH 1EE7E ; Other_Math # Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH 1EE80..1EE89 ; Other_Math # Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH 1EE8B..1EE9B ; Other_Math # Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN 1EEA1..1EEA3 ; Other_Math # Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL 1EEA5..1EEA9 ; Other_Math # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB ; Other_Math # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN # Total code points: 1362 # ================================================ 0030..0039 ; Hex_Digit # Nd [10] DIGIT ZERO..DIGIT NINE 0041..0046 ; Hex_Digit # L& [6] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER F 0061..0066 ; Hex_Digit # L& [6] LATIN SMALL LETTER A..LATIN SMALL LETTER F FF10..FF19 ; Hex_Digit # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE FF21..FF26 ; Hex_Digit # L& [6] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER F FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER F # Total code points: 44 # ================================================ 0030..0039 ; ASCII_Hex_Digit # Nd [10] DIGIT ZERO..DIGIT NINE 0041..0046 ; ASCII_Hex_Digit # L& [6] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER F 0061..0066 ; ASCII_Hex_Digit # L& [6] LATIN SMALL LETTER A..LATIN SMALL LETTER F # Total code points: 22 # ================================================ 0345 ; Other_Alphabetic # Mn COMBINING GREEK YPOGEGRAMMENI 05B0..05BD ; Other_Alphabetic # Mn [14] HEBREW POINT SHEVA..HEBREW POINT METEG 05BF ; Other_Alphabetic # Mn HEBREW POINT RAFE 05C1..05C2 ; Other_Alphabetic # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT 05C4..05C5 ; Other_Alphabetic # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT 05C7 ; Other_Alphabetic # Mn HEBREW POINT QAMATS QATAN 0610..061A ; Other_Alphabetic # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA 064B..0657 ; Other_Alphabetic # Mn [13] ARABIC FATHATAN..ARABIC INVERTED DAMMA 0659..065F ; Other_Alphabetic # Mn [7] ARABIC ZWARAKAY..ARABIC WAVY HAMZA BELOW 0670 ; Other_Alphabetic # Mn ARABIC LETTER SUPERSCRIPT ALEF 06D6..06DC ; Other_Alphabetic # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN 06E1..06E4 ; Other_Alphabetic # Mn [4] ARABIC SMALL HIGH DOTLESS HEAD OF KHAH..ARABIC SMALL HIGH MADDA 06E7..06E8 ; Other_Alphabetic # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON 06ED ; Other_Alphabetic # Mn ARABIC SMALL LOW MEEM 0711 ; Other_Alphabetic # Mn SYRIAC LETTER SUPERSCRIPT ALAPH 0730..073F ; Other_Alphabetic # Mn [16] SYRIAC PTHAHA ABOVE..SYRIAC RWAHA 07A6..07B0 ; Other_Alphabetic # Mn [11] THAANA ABAFILI..THAANA SUKUN 0816..0817 ; Other_Alphabetic # Mn [2] SAMARITAN MARK IN..SAMARITAN MARK IN-ALAF 081B..0823 ; Other_Alphabetic # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A 0825..0827 ; Other_Alphabetic # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U 0829..082C ; Other_Alphabetic # Mn [4] SAMARITAN VOWEL SIGN LONG I..SAMARITAN VOWEL SIGN SUKUN 08D4..08DF ; Other_Alphabetic # Mn [12] ARABIC SMALL HIGH WORD AR-RUB..ARABIC SMALL HIGH WORD WAQFA 08E3..08E9 ; Other_Alphabetic # Mn [7] ARABIC TURNED DAMMA BELOW..ARABIC CURLY KASRATAN 08F0..0902 ; Other_Alphabetic # Mn [19] ARABIC OPEN FATHATAN..DEVANAGARI SIGN ANUSVARA 0903 ; Other_Alphabetic # Mc DEVANAGARI SIGN VISARGA 093A ; Other_Alphabetic # Mn DEVANAGARI VOWEL SIGN OE 093B ; Other_Alphabetic # Mc DEVANAGARI VOWEL SIGN OOE 093E..0940 ; Other_Alphabetic # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II 0941..0948 ; Other_Alphabetic # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI 0949..094C ; Other_Alphabetic # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU 094E..094F ; Other_Alphabetic # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW 0955..0957 ; Other_Alphabetic # Mn [3] DEVANAGARI VOWEL SIGN CANDRA LONG E..DEVANAGARI VOWEL SIGN UUE 0962..0963 ; Other_Alphabetic # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL 0981 ; Other_Alphabetic # Mn BENGALI SIGN CANDRABINDU 0982..0983 ; Other_Alphabetic # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA 09BE..09C0 ; Other_Alphabetic # Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II 09C1..09C4 ; Other_Alphabetic # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR 09C7..09C8 ; Other_Alphabetic # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI 09CB..09CC ; Other_Alphabetic # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU 09D7 ; Other_Alphabetic # Mc BENGALI AU LENGTH MARK 09E2..09E3 ; Other_Alphabetic # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL 0A01..0A02 ; Other_Alphabetic # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI 0A03 ; Other_Alphabetic # Mc GURMUKHI SIGN VISARGA 0A3E..0A40 ; Other_Alphabetic # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II 0A41..0A42 ; Other_Alphabetic # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU 0A47..0A48 ; Other_Alphabetic # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI 0A4B..0A4C ; Other_Alphabetic # Mn [2] GURMUKHI VOWEL SIGN OO..GURMUKHI VOWEL SIGN AU 0A51 ; Other_Alphabetic # Mn GURMUKHI SIGN UDAAT 0A70..0A71 ; Other_Alphabetic # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK 0A75 ; Other_Alphabetic # Mn GURMUKHI SIGN YAKASH 0A81..0A82 ; Other_Alphabetic # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA 0A83 ; Other_Alphabetic # Mc GUJARATI SIGN VISARGA 0ABE..0AC0 ; Other_Alphabetic # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II 0AC1..0AC5 ; Other_Alphabetic # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E 0AC7..0AC8 ; Other_Alphabetic # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI 0AC9 ; Other_Alphabetic # Mc GUJARATI VOWEL SIGN CANDRA O 0ACB..0ACC ; Other_Alphabetic # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU 0AE2..0AE3 ; Other_Alphabetic # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL 0B01 ; Other_Alphabetic # Mn ORIYA SIGN CANDRABINDU 0B02..0B03 ; Other_Alphabetic # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA 0B3E ; Other_Alphabetic # Mc ORIYA VOWEL SIGN AA 0B3F ; Other_Alphabetic # Mn ORIYA VOWEL SIGN I 0B40 ; Other_Alphabetic # Mc ORIYA VOWEL SIGN II 0B41..0B44 ; Other_Alphabetic # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR 0B47..0B48 ; Other_Alphabetic # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI 0B4B..0B4C ; Other_Alphabetic # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU 0B56 ; Other_Alphabetic # Mn ORIYA AI LENGTH MARK 0B57 ; Other_Alphabetic # Mc ORIYA AU LENGTH MARK 0B62..0B63 ; Other_Alphabetic # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL 0B82 ; Other_Alphabetic # Mn TAMIL SIGN ANUSVARA 0BBE..0BBF ; Other_Alphabetic # Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I 0BC0 ; Other_Alphabetic # Mn TAMIL VOWEL SIGN II 0BC1..0BC2 ; Other_Alphabetic # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU 0BC6..0BC8 ; Other_Alphabetic # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI 0BCA..0BCC ; Other_Alphabetic # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU 0BD7 ; Other_Alphabetic # Mc TAMIL AU LENGTH MARK 0C00 ; Other_Alphabetic # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE 0C01..0C03 ; Other_Alphabetic # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA 0C3E..0C40 ; Other_Alphabetic # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II 0C41..0C44 ; Other_Alphabetic # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR 0C46..0C48 ; Other_Alphabetic # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI 0C4A..0C4C ; Other_Alphabetic # Mn [3] TELUGU VOWEL SIGN O..TELUGU VOWEL SIGN AU 0C55..0C56 ; Other_Alphabetic # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C62..0C63 ; Other_Alphabetic # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C81 ; Other_Alphabetic # Mn KANNADA SIGN CANDRABINDU 0C82..0C83 ; Other_Alphabetic # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA 0CBE ; Other_Alphabetic # Mc KANNADA VOWEL SIGN AA 0CBF ; Other_Alphabetic # Mn KANNADA VOWEL SIGN I 0CC0..0CC4 ; Other_Alphabetic # Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR 0CC6 ; Other_Alphabetic # Mn KANNADA VOWEL SIGN E 0CC7..0CC8 ; Other_Alphabetic # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI 0CCA..0CCB ; Other_Alphabetic # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC ; Other_Alphabetic # Mn KANNADA VOWEL SIGN AU 0CD5..0CD6 ; Other_Alphabetic # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK 0CE2..0CE3 ; Other_Alphabetic # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0D01 ; Other_Alphabetic # Mn MALAYALAM SIGN CANDRABINDU 0D02..0D03 ; Other_Alphabetic # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA 0D3E..0D40 ; Other_Alphabetic # Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II 0D41..0D44 ; Other_Alphabetic # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR 0D46..0D48 ; Other_Alphabetic # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI 0D4A..0D4C ; Other_Alphabetic # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU 0D57 ; Other_Alphabetic # Mc MALAYALAM AU LENGTH MARK 0D62..0D63 ; Other_Alphabetic # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL 0D82..0D83 ; Other_Alphabetic # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA 0DCF..0DD1 ; Other_Alphabetic # Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA 0DD2..0DD4 ; Other_Alphabetic # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA 0DD6 ; Other_Alphabetic # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA 0DD8..0DDF ; Other_Alphabetic # Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA 0DF2..0DF3 ; Other_Alphabetic # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA 0E31 ; Other_Alphabetic # Mn THAI CHARACTER MAI HAN-AKAT 0E34..0E3A ; Other_Alphabetic # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU 0E4D ; Other_Alphabetic # Mn THAI CHARACTER NIKHAHIT 0EB1 ; Other_Alphabetic # Mn LAO VOWEL SIGN MAI KAN 0EB4..0EB9 ; Other_Alphabetic # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU 0EBB..0EBC ; Other_Alphabetic # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO 0ECD ; Other_Alphabetic # Mn LAO NIGGAHITA 0F71..0F7E ; Other_Alphabetic # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO 0F7F ; Other_Alphabetic # Mc TIBETAN SIGN RNAM BCAD 0F80..0F81 ; Other_Alphabetic # Mn [2] TIBETAN VOWEL SIGN REVERSED I..TIBETAN VOWEL SIGN REVERSED II 0F8D..0F97 ; Other_Alphabetic # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA 0F99..0FBC ; Other_Alphabetic # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA 102B..102C ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA 102D..1030 ; Other_Alphabetic # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU 1031 ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN E 1032..1036 ; Other_Alphabetic # Mn [5] MYANMAR VOWEL SIGN AI..MYANMAR SIGN ANUSVARA 1038 ; Other_Alphabetic # Mc MYANMAR SIGN VISARGA 103B..103C ; Other_Alphabetic # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA 103D..103E ; Other_Alphabetic # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA 1056..1057 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR 1058..1059 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL 105E..1060 ; Other_Alphabetic # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA 1062 ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN SGAW KAREN EU 1067..1068 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR VOWEL SIGN WESTERN PWO KAREN UE 1071..1074 ; Other_Alphabetic # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE 1082 ; Other_Alphabetic # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA 1083..1084 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E 1085..1086 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y 109C ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN AITON A 109D ; Other_Alphabetic # Mn MYANMAR VOWEL SIGN AITON AI 135F ; Other_Alphabetic # Mn ETHIOPIC COMBINING GEMINATION MARK 1712..1713 ; Other_Alphabetic # Mn [2] TAGALOG VOWEL SIGN I..TAGALOG VOWEL SIGN U 1732..1733 ; Other_Alphabetic # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U 1752..1753 ; Other_Alphabetic # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U 1772..1773 ; Other_Alphabetic # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U 17B6 ; Other_Alphabetic # Mc KHMER VOWEL SIGN AA 17B7..17BD ; Other_Alphabetic # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA 17BE..17C5 ; Other_Alphabetic # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU 17C6 ; Other_Alphabetic # Mn KHMER SIGN NIKAHIT 17C7..17C8 ; Other_Alphabetic # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU 1885..1886 ; Other_Alphabetic # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA 18A9 ; Other_Alphabetic # Mn MONGOLIAN LETTER ALI GALI DAGALGA 1920..1922 ; Other_Alphabetic # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U 1923..1926 ; Other_Alphabetic # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU 1927..1928 ; Other_Alphabetic # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O 1929..192B ; Other_Alphabetic # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA 1930..1931 ; Other_Alphabetic # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA 1932 ; Other_Alphabetic # Mn LIMBU SMALL LETTER ANUSVARA 1933..1938 ; Other_Alphabetic # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA 1A17..1A18 ; Other_Alphabetic # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U 1A19..1A1A ; Other_Alphabetic # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O 1A1B ; Other_Alphabetic # Mn BUGINESE VOWEL SIGN AE 1A55 ; Other_Alphabetic # Mc TAI THAM CONSONANT SIGN MEDIAL RA 1A56 ; Other_Alphabetic # Mn TAI THAM CONSONANT SIGN MEDIAL LA 1A57 ; Other_Alphabetic # Mc TAI THAM CONSONANT SIGN LA TANG LAI 1A58..1A5E ; Other_Alphabetic # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA 1A61 ; Other_Alphabetic # Mc TAI THAM VOWEL SIGN A 1A62 ; Other_Alphabetic # Mn TAI THAM VOWEL SIGN MAI SAT 1A63..1A64 ; Other_Alphabetic # Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA 1A65..1A6C ; Other_Alphabetic # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW 1A6D..1A72 ; Other_Alphabetic # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI 1A73..1A74 ; Other_Alphabetic # Mn [2] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN MAI KANG 1B00..1B03 ; Other_Alphabetic # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04 ; Other_Alphabetic # Mc BALINESE SIGN BISAH 1B35 ; Other_Alphabetic # Mc BALINESE VOWEL SIGN TEDUNG 1B36..1B3A ; Other_Alphabetic # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA 1B3B ; Other_Alphabetic # Mc BALINESE VOWEL SIGN RA REPA TEDUNG 1B3C ; Other_Alphabetic # Mn BALINESE VOWEL SIGN LA LENGA 1B3D..1B41 ; Other_Alphabetic # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG 1B42 ; Other_Alphabetic # Mn BALINESE VOWEL SIGN PEPET 1B43 ; Other_Alphabetic # Mc BALINESE VOWEL SIGN PEPET TEDUNG 1B80..1B81 ; Other_Alphabetic # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR 1B82 ; Other_Alphabetic # Mc SUNDANESE SIGN PANGWISAD 1BA1 ; Other_Alphabetic # Mc SUNDANESE CONSONANT SIGN PAMINGKAL 1BA2..1BA5 ; Other_Alphabetic # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU 1BA6..1BA7 ; Other_Alphabetic # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG 1BA8..1BA9 ; Other_Alphabetic # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG 1BAC..1BAD ; Other_Alphabetic # Mn [2] SUNDANESE CONSONANT SIGN PASANGAN MA..SUNDANESE CONSONANT SIGN PASANGAN WA 1BE7 ; Other_Alphabetic # Mc BATAK VOWEL SIGN E 1BE8..1BE9 ; Other_Alphabetic # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE 1BEA..1BEC ; Other_Alphabetic # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O 1BED ; Other_Alphabetic # Mn BATAK VOWEL SIGN KARO O 1BEE ; Other_Alphabetic # Mc BATAK VOWEL SIGN U 1BEF..1BF1 ; Other_Alphabetic # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H 1C24..1C2B ; Other_Alphabetic # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU 1C2C..1C33 ; Other_Alphabetic # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T 1C34..1C35 ; Other_Alphabetic # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG 1CF2..1CF3 ; Other_Alphabetic # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA 1DE7..1DF4 ; Other_Alphabetic # Mn [14] COMBINING LATIN SMALL LETTER ALPHA..COMBINING LATIN SMALL LETTER U WITH DIAERESIS 24B6..24E9 ; Other_Alphabetic # So [52] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN SMALL LETTER Z 2DE0..2DFF ; Other_Alphabetic # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS A674..A67B ; Other_Alphabetic # Mn [8] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC LETTER OMEGA A69E..A69F ; Other_Alphabetic # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E A823..A824 ; Other_Alphabetic # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I A825..A826 ; Other_Alphabetic # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E A827 ; Other_Alphabetic # Mc SYLOTI NAGRI VOWEL SIGN OO A880..A881 ; Other_Alphabetic # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA A8B4..A8C3 ; Other_Alphabetic # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU A8C5 ; Other_Alphabetic # Mn SAURASHTRA SIGN CANDRABINDU A926..A92A ; Other_Alphabetic # Mn [5] KAYAH LI VOWEL UE..KAYAH LI VOWEL O A947..A951 ; Other_Alphabetic # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R A952 ; Other_Alphabetic # Mc REJANG CONSONANT SIGN H A980..A982 ; Other_Alphabetic # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR A983 ; Other_Alphabetic # Mc JAVANESE SIGN WIGNYAN A9B4..A9B5 ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG A9B6..A9B9 ; Other_Alphabetic # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT A9BA..A9BB ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE A9BC ; Other_Alphabetic # Mn JAVANESE VOWEL SIGN PEPET A9BD..A9BF ; Other_Alphabetic # Mc [3] JAVANESE CONSONANT SIGN KERET..JAVANESE CONSONANT SIGN CAKRA AA29..AA2E ; Other_Alphabetic # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE AA2F..AA30 ; Other_Alphabetic # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI AA31..AA32 ; Other_Alphabetic # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE AA33..AA34 ; Other_Alphabetic # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA AA35..AA36 ; Other_Alphabetic # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA AA43 ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL NG AA4C ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL M AA4D ; Other_Alphabetic # Mc CHAM CONSONANT SIGN FINAL H AAB0 ; Other_Alphabetic # Mn TAI VIET MAI KANG AAB2..AAB4 ; Other_Alphabetic # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U AAB7..AAB8 ; Other_Alphabetic # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA AABE ; Other_Alphabetic # Mn TAI VIET VOWEL AM AAEB ; Other_Alphabetic # Mc MEETEI MAYEK VOWEL SIGN II AAEC..AAED ; Other_Alphabetic # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI AAEE..AAEF ; Other_Alphabetic # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU AAF5 ; Other_Alphabetic # Mc MEETEI MAYEK VOWEL SIGN VISARGA ABE3..ABE4 ; Other_Alphabetic # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP ABE5 ; Other_Alphabetic # Mn MEETEI MAYEK VOWEL SIGN ANAP ABE6..ABE7 ; Other_Alphabetic # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP ABE8 ; Other_Alphabetic # Mn MEETEI MAYEK VOWEL SIGN UNAP ABE9..ABEA ; Other_Alphabetic # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA 10376..1037A ; Other_Alphabetic # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII 10A01..10A03 ; Other_Alphabetic # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R 10A05..10A06 ; Other_Alphabetic # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O 10A0C..10A0F ; Other_Alphabetic # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA 11000 ; Other_Alphabetic # Mc BRAHMI SIGN CANDRABINDU 11001 ; Other_Alphabetic # Mn BRAHMI SIGN ANUSVARA 11002 ; Other_Alphabetic # Mc BRAHMI SIGN VISARGA 11038..11045 ; Other_Alphabetic # Mn [14] BRAHMI VOWEL SIGN AA..BRAHMI VOWEL SIGN AU 11082 ; Other_Alphabetic # Mc KAITHI SIGN VISARGA 110B0..110B2 ; Other_Alphabetic # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II 110B3..110B6 ; Other_Alphabetic # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI 110B7..110B8 ; Other_Alphabetic # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU 11100..11102 ; Other_Alphabetic # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA 11127..1112B ; Other_Alphabetic # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU 1112C ; Other_Alphabetic # Mc CHAKMA VOWEL SIGN E 1112D..11132 ; Other_Alphabetic # Mn [6] CHAKMA VOWEL SIGN AI..CHAKMA AU MARK 11180..11181 ; Other_Alphabetic # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA 11182 ; Other_Alphabetic # Mc SHARADA SIGN VISARGA 111B3..111B5 ; Other_Alphabetic # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II 111B6..111BE ; Other_Alphabetic # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O 111BF ; Other_Alphabetic # Mc SHARADA VOWEL SIGN AU 1122C..1122E ; Other_Alphabetic # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II 1122F..11231 ; Other_Alphabetic # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI 11232..11233 ; Other_Alphabetic # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU 11234 ; Other_Alphabetic # Mn KHOJKI SIGN ANUSVARA 11237 ; Other_Alphabetic # Mn KHOJKI SIGN SHADDA 1123E ; Other_Alphabetic # Mn KHOJKI SIGN SUKUN 112DF ; Other_Alphabetic # Mn KHUDAWADI SIGN ANUSVARA 112E0..112E2 ; Other_Alphabetic # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II 112E3..112E8 ; Other_Alphabetic # Mn [6] KHUDAWADI VOWEL SIGN U..KHUDAWADI VOWEL SIGN AU 11300..11301 ; Other_Alphabetic # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU 11302..11303 ; Other_Alphabetic # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA 1133E..1133F ; Other_Alphabetic # Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I 11340 ; Other_Alphabetic # Mn GRANTHA VOWEL SIGN II 11341..11344 ; Other_Alphabetic # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR 11347..11348 ; Other_Alphabetic # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI 1134B..1134C ; Other_Alphabetic # Mc [2] GRANTHA VOWEL SIGN OO..GRANTHA VOWEL SIGN AU 11357 ; Other_Alphabetic # Mc GRANTHA AU LENGTH MARK 11362..11363 ; Other_Alphabetic # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL 11435..11437 ; Other_Alphabetic # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II 11438..1143F ; Other_Alphabetic # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI 11440..11441 ; Other_Alphabetic # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU 11443..11444 ; Other_Alphabetic # Mn [2] NEWA SIGN CANDRABINDU..NEWA SIGN ANUSVARA 11445 ; Other_Alphabetic # Mc NEWA SIGN VISARGA 114B0..114B2 ; Other_Alphabetic # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II 114B3..114B8 ; Other_Alphabetic # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL 114B9 ; Other_Alphabetic # Mc TIRHUTA VOWEL SIGN E 114BA ; Other_Alphabetic # Mn TIRHUTA VOWEL SIGN SHORT E 114BB..114BE ; Other_Alphabetic # Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU 114BF..114C0 ; Other_Alphabetic # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA 114C1 ; Other_Alphabetic # Mc TIRHUTA SIGN VISARGA 115AF..115B1 ; Other_Alphabetic # Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II 115B2..115B5 ; Other_Alphabetic # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR 115B8..115BB ; Other_Alphabetic # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU 115BC..115BD ; Other_Alphabetic # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA 115BE ; Other_Alphabetic # Mc SIDDHAM SIGN VISARGA 115DC..115DD ; Other_Alphabetic # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU 11630..11632 ; Other_Alphabetic # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II 11633..1163A ; Other_Alphabetic # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI 1163B..1163C ; Other_Alphabetic # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU 1163D ; Other_Alphabetic # Mn MODI SIGN ANUSVARA 1163E ; Other_Alphabetic # Mc MODI SIGN VISARGA 11640 ; Other_Alphabetic # Mn MODI SIGN ARDHACANDRA 116AB ; Other_Alphabetic # Mn TAKRI SIGN ANUSVARA 116AC ; Other_Alphabetic # Mc TAKRI SIGN VISARGA 116AD ; Other_Alphabetic # Mn TAKRI VOWEL SIGN AA 116AE..116AF ; Other_Alphabetic # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II 116B0..116B5 ; Other_Alphabetic # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU 1171D..1171F ; Other_Alphabetic # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA 11720..11721 ; Other_Alphabetic # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA 11722..11725 ; Other_Alphabetic # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU 11726 ; Other_Alphabetic # Mc AHOM VOWEL SIGN E 11727..1172A ; Other_Alphabetic # Mn [4] AHOM VOWEL SIGN AW..AHOM VOWEL SIGN AM 11C2F ; Other_Alphabetic # Mc BHAIKSUKI VOWEL SIGN AA 11C30..11C36 ; Other_Alphabetic # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D ; Other_Alphabetic # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 11C3E ; Other_Alphabetic # Mc BHAIKSUKI SIGN VISARGA 11C92..11CA7 ; Other_Alphabetic # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA 11CA9 ; Other_Alphabetic # Mc MARCHEN SUBJOINED LETTER YA 11CAA..11CB0 ; Other_Alphabetic # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA 11CB1 ; Other_Alphabetic # Mc MARCHEN VOWEL SIGN I 11CB2..11CB3 ; Other_Alphabetic # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E 11CB4 ; Other_Alphabetic # Mc MARCHEN VOWEL SIGN O 11CB5..11CB6 ; Other_Alphabetic # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU 16B30..16B36 ; Other_Alphabetic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM 16F51..16F7E ; Other_Alphabetic # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG 1BC9E ; Other_Alphabetic # Mn DUPLOYAN DOUBLE MARK 1E000..1E006 ; Other_Alphabetic # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE 1E008..1E018 ; Other_Alphabetic # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU 1E01B..1E021 ; Other_Alphabetic # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI 1E023..1E024 ; Other_Alphabetic # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS 1E026..1E02A ; Other_Alphabetic # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA 1E947 ; Other_Alphabetic # Mn ADLAM HAMZA 1F130..1F149 ; Other_Alphabetic # So [26] SQUARED LATIN CAPITAL LETTER A..SQUARED LATIN CAPITAL LETTER Z 1F150..1F169 ; Other_Alphabetic # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; Other_Alphabetic # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z # Total code points: 1238 # ================================================ 3006 ; Ideographic # Lo IDEOGRAPHIC CLOSING MARK 3007 ; Ideographic # Nl IDEOGRAPHIC NUMBER ZERO 3021..3029 ; Ideographic # Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE 3038..303A ; Ideographic # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY 3400..4DB5 ; Ideographic # Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5 4E00..9FD5 ; Ideographic # Lo [20950] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FD5 F900..FA6D ; Ideographic # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9 17000..187EC ; Ideographic # Lo [6125] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187EC 18800..18AF2 ; Ideographic # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755 20000..2A6D6 ; Ideographic # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6 2A700..2B734 ; Ideographic # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734 2B740..2B81D ; Ideographic # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D 2B820..2CEA1 ; Ideographic # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 2F800..2FA1D ; Ideographic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D # Total code points: 88284 # ================================================ 005E ; Diacritic # Sk CIRCUMFLEX ACCENT 0060 ; Diacritic # Sk GRAVE ACCENT 00A8 ; Diacritic # Sk DIAERESIS 00AF ; Diacritic # Sk MACRON 00B4 ; Diacritic # Sk ACUTE ACCENT 00B7 ; Diacritic # Po MIDDLE DOT 00B8 ; Diacritic # Sk CEDILLA 02B0..02C1 ; Diacritic # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C2..02C5 ; Diacritic # Sk [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD 02C6..02D1 ; Diacritic # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02D2..02DF ; Diacritic # Sk [14] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER CROSS ACCENT 02E0..02E4 ; Diacritic # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP 02E5..02EB ; Diacritic # Sk [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK 02EC ; Diacritic # Lm MODIFIER LETTER VOICING 02ED ; Diacritic # Sk MODIFIER LETTER UNASPIRATED 02EE ; Diacritic # Lm MODIFIER LETTER DOUBLE APOSTROPHE 02EF..02FF ; Diacritic # Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW 0300..034E ; Diacritic # Mn [79] COMBINING GRAVE ACCENT..COMBINING UPWARDS ARROW BELOW 0350..0357 ; Diacritic # Mn [8] COMBINING RIGHT ARROWHEAD ABOVE..COMBINING RIGHT HALF RING ABOVE 035D..0362 ; Diacritic # Mn [6] COMBINING DOUBLE BREVE..COMBINING DOUBLE RIGHTWARDS ARROW BELOW 0374 ; Diacritic # Lm GREEK NUMERAL SIGN 0375 ; Diacritic # Sk GREEK LOWER NUMERAL SIGN 037A ; Diacritic # Lm GREEK YPOGEGRAMMENI 0384..0385 ; Diacritic # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS 0483..0487 ; Diacritic # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE 0559 ; Diacritic # Lm ARMENIAN MODIFIER LETTER LEFT HALF RING 0591..05A1 ; Diacritic # Mn [17] HEBREW ACCENT ETNAHTA..HEBREW ACCENT PAZER 05A3..05BD ; Diacritic # Mn [27] HEBREW ACCENT MUNAH..HEBREW POINT METEG 05BF ; Diacritic # Mn HEBREW POINT RAFE 05C1..05C2 ; Diacritic # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT 05C4 ; Diacritic # Mn HEBREW MARK UPPER DOT 064B..0652 ; Diacritic # Mn [8] ARABIC FATHATAN..ARABIC SUKUN 0657..0658 ; Diacritic # Mn [2] ARABIC INVERTED DAMMA..ARABIC MARK NOON GHUNNA 06DF..06E0 ; Diacritic # Mn [2] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO 06E5..06E6 ; Diacritic # Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH 06EA..06EC ; Diacritic # Mn [3] ARABIC EMPTY CENTRE LOW STOP..ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE 0730..074A ; Diacritic # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH 07A6..07B0 ; Diacritic # Mn [11] THAANA ABAFILI..THAANA SUKUN 07EB..07F3 ; Diacritic # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE 07F4..07F5 ; Diacritic # Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE 0818..0819 ; Diacritic # Mn [2] SAMARITAN MARK OCCLUSION..SAMARITAN MARK DAGESH 08E3..08FE ; Diacritic # Mn [28] ARABIC TURNED DAMMA BELOW..ARABIC DAMMA WITH DOT 093C ; Diacritic # Mn DEVANAGARI SIGN NUKTA 094D ; Diacritic # Mn DEVANAGARI SIGN VIRAMA 0951..0954 ; Diacritic # Mn [4] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI ACUTE ACCENT 0971 ; Diacritic # Lm DEVANAGARI SIGN HIGH SPACING DOT 09BC ; Diacritic # Mn BENGALI SIGN NUKTA 09CD ; Diacritic # Mn BENGALI SIGN VIRAMA 0A3C ; Diacritic # Mn GURMUKHI SIGN NUKTA 0A4D ; Diacritic # Mn GURMUKHI SIGN VIRAMA 0ABC ; Diacritic # Mn GUJARATI SIGN NUKTA 0ACD ; Diacritic # Mn GUJARATI SIGN VIRAMA 0B3C ; Diacritic # Mn ORIYA SIGN NUKTA 0B4D ; Diacritic # Mn ORIYA SIGN VIRAMA 0BCD ; Diacritic # Mn TAMIL SIGN VIRAMA 0C4D ; Diacritic # Mn TELUGU SIGN VIRAMA 0CBC ; Diacritic # Mn KANNADA SIGN NUKTA 0CCD ; Diacritic # Mn KANNADA SIGN VIRAMA 0D4D ; Diacritic # Mn MALAYALAM SIGN VIRAMA 0DCA ; Diacritic # Mn SINHALA SIGN AL-LAKUNA 0E47..0E4C ; Diacritic # Mn [6] THAI CHARACTER MAITAIKHU..THAI CHARACTER THANTHAKHAT 0E4E ; Diacritic # Mn THAI CHARACTER YAMAKKAN 0EC8..0ECC ; Diacritic # Mn [5] LAO TONE MAI EK..LAO CANCELLATION MARK 0F18..0F19 ; Diacritic # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS 0F35 ; Diacritic # Mn TIBETAN MARK NGAS BZUNG NYI ZLA 0F37 ; Diacritic # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS 0F39 ; Diacritic # Mn TIBETAN MARK TSA -PHRU 0F3E..0F3F ; Diacritic # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES 0F82..0F84 ; Diacritic # Mn [3] TIBETAN SIGN NYI ZLA NAA DA..TIBETAN MARK HALANTA 0F86..0F87 ; Diacritic # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS 0FC6 ; Diacritic # Mn TIBETAN SYMBOL PADMA GDAN 1037 ; Diacritic # Mn MYANMAR SIGN DOT BELOW 1039..103A ; Diacritic # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT 1087..108C ; Diacritic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3 108D ; Diacritic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE 108F ; Diacritic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5 109A..109B ; Diacritic # Mc [2] MYANMAR SIGN KHAMTI TONE-1..MYANMAR SIGN KHAMTI TONE-3 17C9..17D3 ; Diacritic # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT 17DD ; Diacritic # Mn KHMER SIGN ATTHACAN 1939..193B ; Diacritic # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I 1A75..1A7C ; Diacritic # Mn [8] TAI THAM SIGN TONE-1..TAI THAM SIGN KHUEN-LUE KARAN 1A7F ; Diacritic # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT 1AB0..1ABD ; Diacritic # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1B34 ; Diacritic # Mn BALINESE SIGN REREKAN 1B44 ; Diacritic # Mc BALINESE ADEG ADEG 1B6B..1B73 ; Diacritic # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG 1BAA ; Diacritic # Mc SUNDANESE SIGN PAMAAEH 1BAB ; Diacritic # Mn SUNDANESE SIGN VIRAMA 1C36..1C37 ; Diacritic # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA 1C78..1C7D ; Diacritic # Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD 1CD0..1CD2 ; Diacritic # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA 1CD3 ; Diacritic # Po VEDIC SIGN NIHSHVASA 1CD4..1CE0 ; Diacritic # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA 1CE1 ; Diacritic # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA 1CE2..1CE8 ; Diacritic # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL 1CED ; Diacritic # Mn VEDIC SIGN TIRYAK 1CF4 ; Diacritic # Mn VEDIC TONE CANDRA ABOVE 1CF8..1CF9 ; Diacritic # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE 1D2C..1D6A ; Diacritic # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI 1DC4..1DCF ; Diacritic # Mn [12] COMBINING MACRON-ACUTE..COMBINING ZIGZAG BELOW 1DF5 ; Diacritic # Mn COMBINING UP TACK ABOVE 1DFD..1DFF ; Diacritic # Mn [3] COMBINING ALMOST EQUAL TO BELOW..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW 1FBD ; Diacritic # Sk GREEK KORONIS 1FBF..1FC1 ; Diacritic # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI 1FCD..1FCF ; Diacritic # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI 1FDD..1FDF ; Diacritic # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI 1FED..1FEF ; Diacritic # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA 1FFD..1FFE ; Diacritic # Sk [2] GREEK OXIA..GREEK DASIA 2CEF..2CF1 ; Diacritic # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS 2E2F ; Diacritic # Lm VERTICAL TILDE 302A..302D ; Diacritic # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK 302E..302F ; Diacritic # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK 3099..309A ; Diacritic # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 309B..309C ; Diacritic # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 30FC ; Diacritic # Lm KATAKANA-HIRAGANA PROLONGED SOUND MARK A66F ; Diacritic # Mn COMBINING CYRILLIC VZMET A67C..A67D ; Diacritic # Mn [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK A67F ; Diacritic # Lm CYRILLIC PAYEROK A69C..A69D ; Diacritic # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN A6F0..A6F1 ; Diacritic # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS A717..A71F ; Diacritic # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK A720..A721 ; Diacritic # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE A788 ; Diacritic # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A7F8..A7F9 ; Diacritic # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A8C4 ; Diacritic # Mn SAURASHTRA SIGN VIRAMA A8E0..A8F1 ; Diacritic # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA A92B..A92D ; Diacritic # Mn [3] KAYAH LI TONE PLOPHU..KAYAH LI TONE CALYA PLOPHU A92E ; Diacritic # Po KAYAH LI SIGN CWI A953 ; Diacritic # Mc REJANG VIRAMA A9B3 ; Diacritic # Mn JAVANESE SIGN CECAK TELU A9C0 ; Diacritic # Mc JAVANESE PANGKON A9E5 ; Diacritic # Mn MYANMAR SIGN SHAN SAW AA7B ; Diacritic # Mc MYANMAR SIGN PAO KAREN TONE AA7C ; Diacritic # Mn MYANMAR SIGN TAI LAING TONE-2 AA7D ; Diacritic # Mc MYANMAR SIGN TAI LAING TONE-5 AABF ; Diacritic # Mn TAI VIET TONE MAI EK AAC0 ; Diacritic # Lo TAI VIET TONE MAI NUENG AAC1 ; Diacritic # Mn TAI VIET TONE MAI THO AAC2 ; Diacritic # Lo TAI VIET TONE MAI SONG AAF6 ; Diacritic # Mn MEETEI MAYEK VIRAMA AB5B ; Diacritic # Sk MODIFIER BREVE WITH INVERTED BREVE AB5C..AB5F ; Diacritic # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK ABEC ; Diacritic # Mc MEETEI MAYEK LUM IYEK ABED ; Diacritic # Mn MEETEI MAYEK APUN IYEK FB1E ; Diacritic # Mn HEBREW POINT JUDEO-SPANISH VARIKA FE20..FE2F ; Diacritic # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF FF3E ; Diacritic # Sk FULLWIDTH CIRCUMFLEX ACCENT FF40 ; Diacritic # Sk FULLWIDTH GRAVE ACCENT FF70 ; Diacritic # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK FF9E..FF9F ; Diacritic # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK FFE3 ; Diacritic # Sk FULLWIDTH MACRON 102E0 ; Diacritic # Mn COPTIC EPACT THOUSANDS MARK 10AE5..10AE6 ; Diacritic # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW 110B9..110BA ; Diacritic # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA 11133..11134 ; Diacritic # Mn [2] CHAKMA VIRAMA..CHAKMA MAAYYAA 11173 ; Diacritic # Mn MAHAJANI SIGN NUKTA 111C0 ; Diacritic # Mc SHARADA SIGN VIRAMA 111CA..111CC ; Diacritic # Mn [3] SHARADA SIGN NUKTA..SHARADA EXTRA SHORT VOWEL MARK 11235 ; Diacritic # Mc KHOJKI SIGN VIRAMA 11236 ; Diacritic # Mn KHOJKI SIGN NUKTA 112E9..112EA ; Diacritic # Mn [2] KHUDAWADI SIGN NUKTA..KHUDAWADI SIGN VIRAMA 1133C ; Diacritic # Mn GRANTHA SIGN NUKTA 1134D ; Diacritic # Mc GRANTHA SIGN VIRAMA 11366..1136C ; Diacritic # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX 11370..11374 ; Diacritic # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA 11442 ; Diacritic # Mn NEWA SIGN VIRAMA 11446 ; Diacritic # Mn NEWA SIGN NUKTA 114C2..114C3 ; Diacritic # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA 115BF..115C0 ; Diacritic # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA 1163F ; Diacritic # Mn MODI SIGN VIRAMA 116B6 ; Diacritic # Mc TAKRI SIGN VIRAMA 116B7 ; Diacritic # Mn TAKRI SIGN NUKTA 1172B ; Diacritic # Mn AHOM SIGN KILLER 11C3F ; Diacritic # Mn BHAIKSUKI SIGN VIRAMA 16AF0..16AF4 ; Diacritic # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE 16F8F..16F92 ; Diacritic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW 16F93..16F9F ; Diacritic # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 1D167..1D169 ; Diacritic # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 1D16D..1D172 ; Diacritic # Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 1D17B..1D182 ; Diacritic # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE 1D185..1D18B ; Diacritic # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE 1D1AA..1D1AD ; Diacritic # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO 1E8D0..1E8D6 ; Diacritic # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E944..1E946 ; Diacritic # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK 1E948..1E94A ; Diacritic # Mn [3] ADLAM CONSONANT MODIFIER..ADLAM NUKTA # Total code points: 782 # ================================================ 00B7 ; Extender # Po MIDDLE DOT 02D0..02D1 ; Extender # Lm [2] MODIFIER LETTER TRIANGULAR COLON..MODIFIER LETTER HALF TRIANGULAR COLON 0640 ; Extender # Lm ARABIC TATWEEL 07FA ; Extender # Lm NKO LAJANYALAN 0E46 ; Extender # Lm THAI CHARACTER MAIYAMOK 0EC6 ; Extender # Lm LAO KO LA 180A ; Extender # Po MONGOLIAN NIRUGU 1843 ; Extender # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN 1AA7 ; Extender # Lm TAI THAM SIGN MAI YAMOK 1C36 ; Extender # Mn LEPCHA SIGN RAN 1C7B ; Extender # Lm OL CHIKI RELAA 3005 ; Extender # Lm IDEOGRAPHIC ITERATION MARK 3031..3035 ; Extender # Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF 309D..309E ; Extender # Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK 30FC..30FE ; Extender # Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK A015 ; Extender # Lm YI SYLLABLE WU A60C ; Extender # Lm VAI SYLLABLE LENGTHENER A9CF ; Extender # Lm JAVANESE PANGRANGKEP A9E6 ; Extender # Lm MYANMAR MODIFIER LETTER SHAN REDUPLICATION AA70 ; Extender # Lm MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION AADD ; Extender # Lm TAI VIET SYMBOL SAM AAF3..AAF4 ; Extender # Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK FF70 ; Extender # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK 1135D ; Extender # Lo GRANTHA SIGN PLUTA 115C6..115C8 ; Extender # Po [3] SIDDHAM REPETITION MARK-1..SIDDHAM REPETITION MARK-3 16B42..16B43 ; Extender # Lm [2] PAHAWH HMONG SIGN VOS NRUA..PAHAWH HMONG SIGN IB YAM 16FE0 ; Extender # Lm TANGUT ITERATION MARK 1E944..1E946 ; Extender # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK # Total code points: 42 # ================================================ 00AA ; Other_Lowercase # Lo FEMININE ORDINAL INDICATOR 00BA ; Other_Lowercase # Lo MASCULINE ORDINAL INDICATOR 02B0..02B8 ; Other_Lowercase # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y 02C0..02C1 ; Other_Lowercase # Lm [2] MODIFIER LETTER GLOTTAL STOP..MODIFIER LETTER REVERSED GLOTTAL STOP 02E0..02E4 ; Other_Lowercase # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP 0345 ; Other_Lowercase # Mn COMBINING GREEK YPOGEGRAMMENI 037A ; Other_Lowercase # Lm GREEK YPOGEGRAMMENI 1D2C..1D6A ; Other_Lowercase # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI 1D78 ; Other_Lowercase # Lm MODIFIER LETTER CYRILLIC EN 1D9B..1DBF ; Other_Lowercase # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA 2071 ; Other_Lowercase # Lm SUPERSCRIPT LATIN SMALL LETTER I 207F ; Other_Lowercase # Lm SUPERSCRIPT LATIN SMALL LETTER N 2090..209C ; Other_Lowercase # Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T 2170..217F ; Other_Lowercase # Nl [16] SMALL ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL ONE THOUSAND 24D0..24E9 ; Other_Lowercase # So [26] CIRCLED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z 2C7C..2C7D ; Other_Lowercase # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V A69C..A69D ; Other_Lowercase # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN A770 ; Other_Lowercase # Lm MODIFIER LETTER US A7F8..A7F9 ; Other_Lowercase # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE AB5C..AB5F ; Other_Lowercase # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK # Total code points: 189 # ================================================ 2160..216F ; Other_Uppercase # Nl [16] ROMAN NUMERAL ONE..ROMAN NUMERAL ONE THOUSAND 24B6..24CF ; Other_Uppercase # So [26] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN CAPITAL LETTER Z 1F130..1F149 ; Other_Uppercase # So [26] SQUARED LATIN CAPITAL LETTER A..SQUARED LATIN CAPITAL LETTER Z 1F150..1F169 ; Other_Uppercase # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; Other_Uppercase # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z # Total code points: 120 # ================================================ FDD0..FDEF ; Noncharacter_Code_Point # Cn [32] .. FFFE..FFFF ; Noncharacter_Code_Point # Cn [2] .. 1FFFE..1FFFF ; Noncharacter_Code_Point # Cn [2] .. 2FFFE..2FFFF ; Noncharacter_Code_Point # Cn [2] .. 3FFFE..3FFFF ; Noncharacter_Code_Point # Cn [2] .. 4FFFE..4FFFF ; Noncharacter_Code_Point # Cn [2] .. 5FFFE..5FFFF ; Noncharacter_Code_Point # Cn [2] .. 6FFFE..6FFFF ; Noncharacter_Code_Point # Cn [2] .. 7FFFE..7FFFF ; Noncharacter_Code_Point # Cn [2] .. 8FFFE..8FFFF ; Noncharacter_Code_Point # Cn [2] .. 9FFFE..9FFFF ; Noncharacter_Code_Point # Cn [2] .. AFFFE..AFFFF ; Noncharacter_Code_Point # Cn [2] .. BFFFE..BFFFF ; Noncharacter_Code_Point # Cn [2] .. CFFFE..CFFFF ; Noncharacter_Code_Point # Cn [2] .. DFFFE..DFFFF ; Noncharacter_Code_Point # Cn [2] .. EFFFE..EFFFF ; Noncharacter_Code_Point # Cn [2] .. FFFFE..FFFFF ; Noncharacter_Code_Point # Cn [2] .. 10FFFE..10FFFF; Noncharacter_Code_Point # Cn [2] .. # Total code points: 66 # ================================================ 09BE ; Other_Grapheme_Extend # Mc BENGALI VOWEL SIGN AA 09D7 ; Other_Grapheme_Extend # Mc BENGALI AU LENGTH MARK 0B3E ; Other_Grapheme_Extend # Mc ORIYA VOWEL SIGN AA 0B57 ; Other_Grapheme_Extend # Mc ORIYA AU LENGTH MARK 0BBE ; Other_Grapheme_Extend # Mc TAMIL VOWEL SIGN AA 0BD7 ; Other_Grapheme_Extend # Mc TAMIL AU LENGTH MARK 0CC2 ; Other_Grapheme_Extend # Mc KANNADA VOWEL SIGN UU 0CD5..0CD6 ; Other_Grapheme_Extend # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK 0D3E ; Other_Grapheme_Extend # Mc MALAYALAM VOWEL SIGN AA 0D57 ; Other_Grapheme_Extend # Mc MALAYALAM AU LENGTH MARK 0DCF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN AELA-PILLA 0DDF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN GAYANUKITTA 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER 302E..302F ; Other_Grapheme_Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK 1133E ; Other_Grapheme_Extend # Mc GRANTHA VOWEL SIGN AA 11357 ; Other_Grapheme_Extend # Mc GRANTHA AU LENGTH MARK 114B0 ; Other_Grapheme_Extend # Mc TIRHUTA VOWEL SIGN AA 114BD ; Other_Grapheme_Extend # Mc TIRHUTA VOWEL SIGN SHORT O 115AF ; Other_Grapheme_Extend # Mc SIDDHAM VOWEL SIGN AA 1D165 ; Other_Grapheme_Extend # Mc MUSICAL SYMBOL COMBINING STEM 1D16E..1D172 ; Other_Grapheme_Extend # Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG # Total code points: 125 # ================================================ 2FF0..2FF1 ; IDS_Binary_Operator # So [2] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW 2FF4..2FFB ; IDS_Binary_Operator # So [8] IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID # Total code points: 10 # ================================================ 2FF2..2FF3 ; IDS_Trinary_Operator # So [2] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW # Total code points: 2 # ================================================ 2E80..2E99 ; Radical # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP 2E9B..2EF3 ; Radical # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE 2F00..2FD5 ; Radical # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE # Total code points: 329 # ================================================ 3400..4DB5 ; Unified_Ideograph # Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5 4E00..9FD5 ; Unified_Ideograph # Lo [20950] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FD5 FA0E..FA0F ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA0E..CJK COMPATIBILITY IDEOGRAPH-FA0F FA11 ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA11 FA13..FA14 ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA13..CJK COMPATIBILITY IDEOGRAPH-FA14 FA1F ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA1F FA21 ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA21 FA23..FA24 ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA23..CJK COMPATIBILITY IDEOGRAPH-FA24 FA27..FA29 ; Unified_Ideograph # Lo [3] CJK COMPATIBILITY IDEOGRAPH-FA27..CJK COMPATIBILITY IDEOGRAPH-FA29 20000..2A6D6 ; Unified_Ideograph # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6 2A700..2B734 ; Unified_Ideograph # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734 2B740..2B81D ; Unified_Ideograph # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D 2B820..2CEA1 ; Unified_Ideograph # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 # Total code points: 80388 # ================================================ 034F ; Other_Default_Ignorable_Code_Point # Mn COMBINING GRAPHEME JOINER 115F..1160 ; Other_Default_Ignorable_Code_Point # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER 17B4..17B5 ; Other_Default_Ignorable_Code_Point # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA 2065 ; Other_Default_Ignorable_Code_Point # Cn 3164 ; Other_Default_Ignorable_Code_Point # Lo HANGUL FILLER FFA0 ; Other_Default_Ignorable_Code_Point # Lo HALFWIDTH HANGUL FILLER FFF0..FFF8 ; Other_Default_Ignorable_Code_Point # Cn [9] .. E0000 ; Other_Default_Ignorable_Code_Point # Cn E0002..E001F ; Other_Default_Ignorable_Code_Point # Cn [30] .. E0080..E00FF ; Other_Default_Ignorable_Code_Point # Cn [128] .. E01F0..E0FFF ; Other_Default_Ignorable_Code_Point # Cn [3600] .. # Total code points: 3776 # ================================================ 0149 ; Deprecated # L& LATIN SMALL LETTER N PRECEDED BY APOSTROPHE 0673 ; Deprecated # Lo ARABIC LETTER ALEF WITH WAVY HAMZA BELOW 0F77 ; Deprecated # Mn TIBETAN VOWEL SIGN VOCALIC RR 0F79 ; Deprecated # Mn TIBETAN VOWEL SIGN VOCALIC LL 17A3..17A4 ; Deprecated # Lo [2] KHMER INDEPENDENT VOWEL QAQ..KHMER INDEPENDENT VOWEL QAA 206A..206F ; Deprecated # Cf [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES 2329 ; Deprecated # Ps LEFT-POINTING ANGLE BRACKET 232A ; Deprecated # Pe RIGHT-POINTING ANGLE BRACKET E0001 ; Deprecated # Cf LANGUAGE TAG # Total code points: 15 # ================================================ 0069..006A ; Soft_Dotted # L& [2] LATIN SMALL LETTER I..LATIN SMALL LETTER J 012F ; Soft_Dotted # L& LATIN SMALL LETTER I WITH OGONEK 0249 ; Soft_Dotted # L& LATIN SMALL LETTER J WITH STROKE 0268 ; Soft_Dotted # L& LATIN SMALL LETTER I WITH STROKE 029D ; Soft_Dotted # L& LATIN SMALL LETTER J WITH CROSSED-TAIL 02B2 ; Soft_Dotted # Lm MODIFIER LETTER SMALL J 03F3 ; Soft_Dotted # L& GREEK LETTER YOT 0456 ; Soft_Dotted # L& CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I 0458 ; Soft_Dotted # L& CYRILLIC SMALL LETTER JE 1D62 ; Soft_Dotted # Lm LATIN SUBSCRIPT SMALL LETTER I 1D96 ; Soft_Dotted # L& LATIN SMALL LETTER I WITH RETROFLEX HOOK 1DA4 ; Soft_Dotted # Lm MODIFIER LETTER SMALL I WITH STROKE 1DA8 ; Soft_Dotted # Lm MODIFIER LETTER SMALL J WITH CROSSED-TAIL 1E2D ; Soft_Dotted # L& LATIN SMALL LETTER I WITH TILDE BELOW 1ECB ; Soft_Dotted # L& LATIN SMALL LETTER I WITH DOT BELOW 2071 ; Soft_Dotted # Lm SUPERSCRIPT LATIN SMALL LETTER I 2148..2149 ; Soft_Dotted # L& [2] DOUBLE-STRUCK ITALIC SMALL I..DOUBLE-STRUCK ITALIC SMALL J 2C7C ; Soft_Dotted # Lm LATIN SUBSCRIPT SMALL LETTER J 1D422..1D423 ; Soft_Dotted # L& [2] MATHEMATICAL BOLD SMALL I..MATHEMATICAL BOLD SMALL J 1D456..1D457 ; Soft_Dotted # L& [2] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL J 1D48A..1D48B ; Soft_Dotted # L& [2] MATHEMATICAL BOLD ITALIC SMALL I..MATHEMATICAL BOLD ITALIC SMALL J 1D4BE..1D4BF ; Soft_Dotted # L& [2] MATHEMATICAL SCRIPT SMALL I..MATHEMATICAL SCRIPT SMALL J 1D4F2..1D4F3 ; Soft_Dotted # L& [2] MATHEMATICAL BOLD SCRIPT SMALL I..MATHEMATICAL BOLD SCRIPT SMALL J 1D526..1D527 ; Soft_Dotted # L& [2] MATHEMATICAL FRAKTUR SMALL I..MATHEMATICAL FRAKTUR SMALL J 1D55A..1D55B ; Soft_Dotted # L& [2] MATHEMATICAL DOUBLE-STRUCK SMALL I..MATHEMATICAL DOUBLE-STRUCK SMALL J 1D58E..1D58F ; Soft_Dotted # L& [2] MATHEMATICAL BOLD FRAKTUR SMALL I..MATHEMATICAL BOLD FRAKTUR SMALL J 1D5C2..1D5C3 ; Soft_Dotted # L& [2] MATHEMATICAL SANS-SERIF SMALL I..MATHEMATICAL SANS-SERIF SMALL J 1D5F6..1D5F7 ; Soft_Dotted # L& [2] MATHEMATICAL SANS-SERIF BOLD SMALL I..MATHEMATICAL SANS-SERIF BOLD SMALL J 1D62A..1D62B ; Soft_Dotted # L& [2] MATHEMATICAL SANS-SERIF ITALIC SMALL I..MATHEMATICAL SANS-SERIF ITALIC SMALL J 1D65E..1D65F ; Soft_Dotted # L& [2] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J 1D692..1D693 ; Soft_Dotted # L& [2] MATHEMATICAL MONOSPACE SMALL I..MATHEMATICAL MONOSPACE SMALL J # Total code points: 46 # ================================================ 0E40..0E44 ; Logical_Order_Exception # Lo [5] THAI CHARACTER SARA E..THAI CHARACTER SARA AI MAIMALAI 0EC0..0EC4 ; Logical_Order_Exception # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI 19B5..19B7 ; Logical_Order_Exception # Lo [3] NEW TAI LUE VOWEL SIGN E..NEW TAI LUE VOWEL SIGN O 19BA ; Logical_Order_Exception # Lo NEW TAI LUE VOWEL SIGN AY AAB5..AAB6 ; Logical_Order_Exception # Lo [2] TAI VIET VOWEL E..TAI VIET VOWEL O AAB9 ; Logical_Order_Exception # Lo TAI VIET VOWEL UEA AABB..AABC ; Logical_Order_Exception # Lo [2] TAI VIET VOWEL AUE..TAI VIET VOWEL AY # Total code points: 19 # ================================================ 1885..1886 ; Other_ID_Start # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA 2118 ; Other_ID_Start # Sm SCRIPT CAPITAL P 212E ; Other_ID_Start # So ESTIMATED SYMBOL 309B..309C ; Other_ID_Start # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK # Total code points: 6 # ================================================ 00B7 ; Other_ID_Continue # Po MIDDLE DOT 0387 ; Other_ID_Continue # Po GREEK ANO TELEIA 1369..1371 ; Other_ID_Continue # No [9] ETHIOPIC DIGIT ONE..ETHIOPIC DIGIT NINE 19DA ; Other_ID_Continue # No NEW TAI LUE THAM DIGIT ONE # Total code points: 12 # ================================================ 0021 ; Sentence_Terminal # Po EXCLAMATION MARK 002E ; Sentence_Terminal # Po FULL STOP 003F ; Sentence_Terminal # Po QUESTION MARK 0589 ; Sentence_Terminal # Po ARMENIAN FULL STOP 061F ; Sentence_Terminal # Po ARABIC QUESTION MARK 06D4 ; Sentence_Terminal # Po ARABIC FULL STOP 0700..0702 ; Sentence_Terminal # Po [3] SYRIAC END OF PARAGRAPH..SYRIAC SUBLINEAR FULL STOP 07F9 ; Sentence_Terminal # Po NKO EXCLAMATION MARK 0964..0965 ; Sentence_Terminal # Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA 104A..104B ; Sentence_Terminal # Po [2] MYANMAR SIGN LITTLE SECTION..MYANMAR SIGN SECTION 1362 ; Sentence_Terminal # Po ETHIOPIC FULL STOP 1367..1368 ; Sentence_Terminal # Po [2] ETHIOPIC QUESTION MARK..ETHIOPIC PARAGRAPH SEPARATOR 166E ; Sentence_Terminal # Po CANADIAN SYLLABICS FULL STOP 1735..1736 ; Sentence_Terminal # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION 1803 ; Sentence_Terminal # Po MONGOLIAN FULL STOP 1809 ; Sentence_Terminal # Po MONGOLIAN MANCHU FULL STOP 1944..1945 ; Sentence_Terminal # Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK 1AA8..1AAB ; Sentence_Terminal # Po [4] TAI THAM SIGN KAAN..TAI THAM SIGN SATKAANKUU 1B5A..1B5B ; Sentence_Terminal # Po [2] BALINESE PANTI..BALINESE PAMADA 1B5E..1B5F ; Sentence_Terminal # Po [2] BALINESE CARIK SIKI..BALINESE CARIK PAREREN 1C3B..1C3C ; Sentence_Terminal # Po [2] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION NYET THYOOM TA-ROL 1C7E..1C7F ; Sentence_Terminal # Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD 203C..203D ; Sentence_Terminal # Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG 2047..2049 ; Sentence_Terminal # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK 2E2E ; Sentence_Terminal # Po REVERSED QUESTION MARK 2E3C ; Sentence_Terminal # Po STENOGRAPHIC FULL STOP 3002 ; Sentence_Terminal # Po IDEOGRAPHIC FULL STOP A4FF ; Sentence_Terminal # Po LISU PUNCTUATION FULL STOP A60E..A60F ; Sentence_Terminal # Po [2] VAI FULL STOP..VAI QUESTION MARK A6F3 ; Sentence_Terminal # Po BAMUM FULL STOP A6F7 ; Sentence_Terminal # Po BAMUM QUESTION MARK A876..A877 ; Sentence_Terminal # Po [2] PHAGS-PA MARK SHAD..PHAGS-PA MARK DOUBLE SHAD A8CE..A8CF ; Sentence_Terminal # Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA A92F ; Sentence_Terminal # Po KAYAH LI SIGN SHYA A9C8..A9C9 ; Sentence_Terminal # Po [2] JAVANESE PADA LINGSA..JAVANESE PADA LUNGSI AA5D..AA5F ; Sentence_Terminal # Po [3] CHAM PUNCTUATION DANDA..CHAM PUNCTUATION TRIPLE DANDA AAF0..AAF1 ; Sentence_Terminal # Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM ABEB ; Sentence_Terminal # Po MEETEI MAYEK CHEIKHEI FE52 ; Sentence_Terminal # Po SMALL FULL STOP FE56..FE57 ; Sentence_Terminal # Po [2] SMALL QUESTION MARK..SMALL EXCLAMATION MARK FF01 ; Sentence_Terminal # Po FULLWIDTH EXCLAMATION MARK FF0E ; Sentence_Terminal # Po FULLWIDTH FULL STOP FF1F ; Sentence_Terminal # Po FULLWIDTH QUESTION MARK FF61 ; Sentence_Terminal # Po HALFWIDTH IDEOGRAPHIC FULL STOP 10A56..10A57 ; Sentence_Terminal # Po [2] KHAROSHTHI PUNCTUATION DANDA..KHAROSHTHI PUNCTUATION DOUBLE DANDA 11047..11048 ; Sentence_Terminal # Po [2] BRAHMI DANDA..BRAHMI DOUBLE DANDA 110BE..110C1 ; Sentence_Terminal # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA 11141..11143 ; Sentence_Terminal # Po [3] CHAKMA DANDA..CHAKMA QUESTION MARK 111C5..111C6 ; Sentence_Terminal # Po [2] SHARADA DANDA..SHARADA DOUBLE DANDA 111CD ; Sentence_Terminal # Po SHARADA SUTRA MARK 111DE..111DF ; Sentence_Terminal # Po [2] SHARADA SECTION MARK-1..SHARADA SECTION MARK-2 11238..11239 ; Sentence_Terminal # Po [2] KHOJKI DANDA..KHOJKI DOUBLE DANDA 1123B..1123C ; Sentence_Terminal # Po [2] KHOJKI SECTION MARK..KHOJKI DOUBLE SECTION MARK 112A9 ; Sentence_Terminal # Po MULTANI SECTION MARK 1144B..1144C ; Sentence_Terminal # Po [2] NEWA DANDA..NEWA DOUBLE DANDA 115C2..115C3 ; Sentence_Terminal # Po [2] SIDDHAM DANDA..SIDDHAM DOUBLE DANDA 115C9..115D7 ; Sentence_Terminal # Po [15] SIDDHAM END OF TEXT MARK..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES 11641..11642 ; Sentence_Terminal # Po [2] MODI DANDA..MODI DOUBLE DANDA 1173C..1173E ; Sentence_Terminal # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI 11C41..11C42 ; Sentence_Terminal # Po [2] BHAIKSUKI DANDA..BHAIKSUKI DOUBLE DANDA 16A6E..16A6F ; Sentence_Terminal # Po [2] MRO DANDA..MRO DOUBLE DANDA 16AF5 ; Sentence_Terminal # Po BASSA VAH FULL STOP 16B37..16B38 ; Sentence_Terminal # Po [2] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN VOS TSHAB CEEB 16B44 ; Sentence_Terminal # Po PAHAWH HMONG SIGN XAUS 1BC9F ; Sentence_Terminal # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP 1DA88 ; Sentence_Terminal # Po SIGNWRITING FULL STOP # Total code points: 124 # ================================================ 180B..180D ; Variation_Selector # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE FE00..FE0F ; Variation_Selector # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 # Total code points: 259 # ================================================ 0009..000D ; Pattern_White_Space # Cc [5] .. 0020 ; Pattern_White_Space # Zs SPACE 0085 ; Pattern_White_Space # Cc 200E..200F ; Pattern_White_Space # Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK 2028 ; Pattern_White_Space # Zl LINE SEPARATOR 2029 ; Pattern_White_Space # Zp PARAGRAPH SEPARATOR # Total code points: 11 # ================================================ 0021..0023 ; Pattern_Syntax # Po [3] EXCLAMATION MARK..NUMBER SIGN 0024 ; Pattern_Syntax # Sc DOLLAR SIGN 0025..0027 ; Pattern_Syntax # Po [3] PERCENT SIGN..APOSTROPHE 0028 ; Pattern_Syntax # Ps LEFT PARENTHESIS 0029 ; Pattern_Syntax # Pe RIGHT PARENTHESIS 002A ; Pattern_Syntax # Po ASTERISK 002B ; Pattern_Syntax # Sm PLUS SIGN 002C ; Pattern_Syntax # Po COMMA 002D ; Pattern_Syntax # Pd HYPHEN-MINUS 002E..002F ; Pattern_Syntax # Po [2] FULL STOP..SOLIDUS 003A..003B ; Pattern_Syntax # Po [2] COLON..SEMICOLON 003C..003E ; Pattern_Syntax # Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN 003F..0040 ; Pattern_Syntax # Po [2] QUESTION MARK..COMMERCIAL AT 005B ; Pattern_Syntax # Ps LEFT SQUARE BRACKET 005C ; Pattern_Syntax # Po REVERSE SOLIDUS 005D ; Pattern_Syntax # Pe RIGHT SQUARE BRACKET 005E ; Pattern_Syntax # Sk CIRCUMFLEX ACCENT 0060 ; Pattern_Syntax # Sk GRAVE ACCENT 007B ; Pattern_Syntax # Ps LEFT CURLY BRACKET 007C ; Pattern_Syntax # Sm VERTICAL LINE 007D ; Pattern_Syntax # Pe RIGHT CURLY BRACKET 007E ; Pattern_Syntax # Sm TILDE 00A1 ; Pattern_Syntax # Po INVERTED EXCLAMATION MARK 00A2..00A5 ; Pattern_Syntax # Sc [4] CENT SIGN..YEN SIGN 00A6 ; Pattern_Syntax # So BROKEN BAR 00A7 ; Pattern_Syntax # Po SECTION SIGN 00A9 ; Pattern_Syntax # So COPYRIGHT SIGN 00AB ; Pattern_Syntax # Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 00AC ; Pattern_Syntax # Sm NOT SIGN 00AE ; Pattern_Syntax # So REGISTERED SIGN 00B0 ; Pattern_Syntax # So DEGREE SIGN 00B1 ; Pattern_Syntax # Sm PLUS-MINUS SIGN 00B6 ; Pattern_Syntax # Po PILCROW SIGN 00BB ; Pattern_Syntax # Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 00BF ; Pattern_Syntax # Po INVERTED QUESTION MARK 00D7 ; Pattern_Syntax # Sm MULTIPLICATION SIGN 00F7 ; Pattern_Syntax # Sm DIVISION SIGN 2010..2015 ; Pattern_Syntax # Pd [6] HYPHEN..HORIZONTAL BAR 2016..2017 ; Pattern_Syntax # Po [2] DOUBLE VERTICAL LINE..DOUBLE LOW LINE 2018 ; Pattern_Syntax # Pi LEFT SINGLE QUOTATION MARK 2019 ; Pattern_Syntax # Pf RIGHT SINGLE QUOTATION MARK 201A ; Pattern_Syntax # Ps SINGLE LOW-9 QUOTATION MARK 201B..201C ; Pattern_Syntax # Pi [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK 201D ; Pattern_Syntax # Pf RIGHT DOUBLE QUOTATION MARK 201E ; Pattern_Syntax # Ps DOUBLE LOW-9 QUOTATION MARK 201F ; Pattern_Syntax # Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK 2020..2027 ; Pattern_Syntax # Po [8] DAGGER..HYPHENATION POINT 2030..2038 ; Pattern_Syntax # Po [9] PER MILLE SIGN..CARET 2039 ; Pattern_Syntax # Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK 203A ; Pattern_Syntax # Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 203B..203E ; Pattern_Syntax # Po [4] REFERENCE MARK..OVERLINE 2041..2043 ; Pattern_Syntax # Po [3] CARET INSERTION POINT..HYPHEN BULLET 2044 ; Pattern_Syntax # Sm FRACTION SLASH 2045 ; Pattern_Syntax # Ps LEFT SQUARE BRACKET WITH QUILL 2046 ; Pattern_Syntax # Pe RIGHT SQUARE BRACKET WITH QUILL 2047..2051 ; Pattern_Syntax # Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY 2052 ; Pattern_Syntax # Sm COMMERCIAL MINUS SIGN 2053 ; Pattern_Syntax # Po SWUNG DASH 2055..205E ; Pattern_Syntax # Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS 2190..2194 ; Pattern_Syntax # Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW 2195..2199 ; Pattern_Syntax # So [5] UP DOWN ARROW..SOUTH WEST ARROW 219A..219B ; Pattern_Syntax # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE 219C..219F ; Pattern_Syntax # So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW 21A0 ; Pattern_Syntax # Sm RIGHTWARDS TWO HEADED ARROW 21A1..21A2 ; Pattern_Syntax # So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL 21A3 ; Pattern_Syntax # Sm RIGHTWARDS ARROW WITH TAIL 21A4..21A5 ; Pattern_Syntax # So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR 21A6 ; Pattern_Syntax # Sm RIGHTWARDS ARROW FROM BAR 21A7..21AD ; Pattern_Syntax # So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW 21AE ; Pattern_Syntax # Sm LEFT RIGHT ARROW WITH STROKE 21AF..21CD ; Pattern_Syntax # So [31] DOWNWARDS ZIGZAG ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE 21CE..21CF ; Pattern_Syntax # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE 21D0..21D1 ; Pattern_Syntax # So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW 21D2 ; Pattern_Syntax # Sm RIGHTWARDS DOUBLE ARROW 21D3 ; Pattern_Syntax # So DOWNWARDS DOUBLE ARROW 21D4 ; Pattern_Syntax # Sm LEFT RIGHT DOUBLE ARROW 21D5..21F3 ; Pattern_Syntax # So [31] UP DOWN DOUBLE ARROW..UP DOWN WHITE ARROW 21F4..22FF ; Pattern_Syntax # Sm [268] RIGHT ARROW WITH SMALL CIRCLE..Z NOTATION BAG MEMBERSHIP 2300..2307 ; Pattern_Syntax # So [8] DIAMETER SIGN..WAVY LINE 2308 ; Pattern_Syntax # Ps LEFT CEILING 2309 ; Pattern_Syntax # Pe RIGHT CEILING 230A ; Pattern_Syntax # Ps LEFT FLOOR 230B ; Pattern_Syntax # Pe RIGHT FLOOR 230C..231F ; Pattern_Syntax # So [20] BOTTOM RIGHT CROP..BOTTOM RIGHT CORNER 2320..2321 ; Pattern_Syntax # Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL 2322..2328 ; Pattern_Syntax # So [7] FROWN..KEYBOARD 2329 ; Pattern_Syntax # Ps LEFT-POINTING ANGLE BRACKET 232A ; Pattern_Syntax # Pe RIGHT-POINTING ANGLE BRACKET 232B..237B ; Pattern_Syntax # So [81] ERASE TO THE LEFT..NOT CHECK MARK 237C ; Pattern_Syntax # Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW 237D..239A ; Pattern_Syntax # So [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL 239B..23B3 ; Pattern_Syntax # Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM 23B4..23DB ; Pattern_Syntax # So [40] TOP SQUARE BRACKET..FUSE 23DC..23E1 ; Pattern_Syntax # Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET 23E2..23FE ; Pattern_Syntax # So [29] WHITE TRAPEZIUM..POWER SLEEP SYMBOL 23FF ; Pattern_Syntax # Cn 2400..2426 ; Pattern_Syntax # So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO 2427..243F ; Pattern_Syntax # Cn [25] .. 2440..244A ; Pattern_Syntax # So [11] OCR HOOK..OCR DOUBLE BACKSLASH 244B..245F ; Pattern_Syntax # Cn [21] .. 2500..25B6 ; Pattern_Syntax # So [183] BOX DRAWINGS LIGHT HORIZONTAL..BLACK RIGHT-POINTING TRIANGLE 25B7 ; Pattern_Syntax # Sm WHITE RIGHT-POINTING TRIANGLE 25B8..25C0 ; Pattern_Syntax # So [9] BLACK RIGHT-POINTING SMALL TRIANGLE..BLACK LEFT-POINTING TRIANGLE 25C1 ; Pattern_Syntax # Sm WHITE LEFT-POINTING TRIANGLE 25C2..25F7 ; Pattern_Syntax # So [54] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE CIRCLE WITH UPPER RIGHT QUADRANT 25F8..25FF ; Pattern_Syntax # Sm [8] UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE 2600..266E ; Pattern_Syntax # So [111] BLACK SUN WITH RAYS..MUSIC NATURAL SIGN 266F ; Pattern_Syntax # Sm MUSIC SHARP SIGN 2670..2767 ; Pattern_Syntax # So [248] WEST SYRIAC CROSS..ROTATED FLORAL HEART BULLET 2768 ; Pattern_Syntax # Ps MEDIUM LEFT PARENTHESIS ORNAMENT 2769 ; Pattern_Syntax # Pe MEDIUM RIGHT PARENTHESIS ORNAMENT 276A ; Pattern_Syntax # Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT 276B ; Pattern_Syntax # Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT 276C ; Pattern_Syntax # Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT 276D ; Pattern_Syntax # Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT 276E ; Pattern_Syntax # Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT 276F ; Pattern_Syntax # Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT 2770 ; Pattern_Syntax # Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT 2771 ; Pattern_Syntax # Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT 2772 ; Pattern_Syntax # Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT 2773 ; Pattern_Syntax # Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT 2774 ; Pattern_Syntax # Ps MEDIUM LEFT CURLY BRACKET ORNAMENT 2775 ; Pattern_Syntax # Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT 2794..27BF ; Pattern_Syntax # So [44] HEAVY WIDE-HEADED RIGHTWARDS ARROW..DOUBLE CURLY LOOP 27C0..27C4 ; Pattern_Syntax # Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET 27C5 ; Pattern_Syntax # Ps LEFT S-SHAPED BAG DELIMITER 27C6 ; Pattern_Syntax # Pe RIGHT S-SHAPED BAG DELIMITER 27C7..27E5 ; Pattern_Syntax # Sm [31] OR WITH DOT INSIDE..WHITE SQUARE WITH RIGHTWARDS TICK 27E6 ; Pattern_Syntax # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET 27E7 ; Pattern_Syntax # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET 27E8 ; Pattern_Syntax # Ps MATHEMATICAL LEFT ANGLE BRACKET 27E9 ; Pattern_Syntax # Pe MATHEMATICAL RIGHT ANGLE BRACKET 27EA ; Pattern_Syntax # Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET 27EB ; Pattern_Syntax # Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET 27EC ; Pattern_Syntax # Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET 27ED ; Pattern_Syntax # Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET 27EE ; Pattern_Syntax # Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS 27EF ; Pattern_Syntax # Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS 27F0..27FF ; Pattern_Syntax # Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW 2800..28FF ; Pattern_Syntax # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678 2900..2982 ; Pattern_Syntax # Sm [131] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..Z NOTATION TYPE COLON 2983 ; Pattern_Syntax # Ps LEFT WHITE CURLY BRACKET 2984 ; Pattern_Syntax # Pe RIGHT WHITE CURLY BRACKET 2985 ; Pattern_Syntax # Ps LEFT WHITE PARENTHESIS 2986 ; Pattern_Syntax # Pe RIGHT WHITE PARENTHESIS 2987 ; Pattern_Syntax # Ps Z NOTATION LEFT IMAGE BRACKET 2988 ; Pattern_Syntax # Pe Z NOTATION RIGHT IMAGE BRACKET 2989 ; Pattern_Syntax # Ps Z NOTATION LEFT BINDING BRACKET 298A ; Pattern_Syntax # Pe Z NOTATION RIGHT BINDING BRACKET 298B ; Pattern_Syntax # Ps LEFT SQUARE BRACKET WITH UNDERBAR 298C ; Pattern_Syntax # Pe RIGHT SQUARE BRACKET WITH UNDERBAR 298D ; Pattern_Syntax # Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER 298E ; Pattern_Syntax # Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 298F ; Pattern_Syntax # Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 2990 ; Pattern_Syntax # Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER 2991 ; Pattern_Syntax # Ps LEFT ANGLE BRACKET WITH DOT 2992 ; Pattern_Syntax # Pe RIGHT ANGLE BRACKET WITH DOT 2993 ; Pattern_Syntax # Ps LEFT ARC LESS-THAN BRACKET 2994 ; Pattern_Syntax # Pe RIGHT ARC GREATER-THAN BRACKET 2995 ; Pattern_Syntax # Ps DOUBLE LEFT ARC GREATER-THAN BRACKET 2996 ; Pattern_Syntax # Pe DOUBLE RIGHT ARC LESS-THAN BRACKET 2997 ; Pattern_Syntax # Ps LEFT BLACK TORTOISE SHELL BRACKET 2998 ; Pattern_Syntax # Pe RIGHT BLACK TORTOISE SHELL BRACKET 2999..29D7 ; Pattern_Syntax # Sm [63] DOTTED FENCE..BLACK HOURGLASS 29D8 ; Pattern_Syntax # Ps LEFT WIGGLY FENCE 29D9 ; Pattern_Syntax # Pe RIGHT WIGGLY FENCE 29DA ; Pattern_Syntax # Ps LEFT DOUBLE WIGGLY FENCE 29DB ; Pattern_Syntax # Pe RIGHT DOUBLE WIGGLY FENCE 29DC..29FB ; Pattern_Syntax # Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS 29FC ; Pattern_Syntax # Ps LEFT-POINTING CURVED ANGLE BRACKET 29FD ; Pattern_Syntax # Pe RIGHT-POINTING CURVED ANGLE BRACKET 29FE..2AFF ; Pattern_Syntax # Sm [258] TINY..N-ARY WHITE VERTICAL BAR 2B00..2B2F ; Pattern_Syntax # So [48] NORTH EAST WHITE ARROW..WHITE VERTICAL ELLIPSE 2B30..2B44 ; Pattern_Syntax # Sm [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET 2B45..2B46 ; Pattern_Syntax # So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW 2B47..2B4C ; Pattern_Syntax # Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR 2B4D..2B73 ; Pattern_Syntax # So [39] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR 2B74..2B75 ; Pattern_Syntax # Cn [2] .. 2B76..2B95 ; Pattern_Syntax # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW 2B96..2B97 ; Pattern_Syntax # Cn [2] .. 2B98..2BB9 ; Pattern_Syntax # So [34] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..UP ARROWHEAD IN A RECTANGLE BOX 2BBA..2BBC ; Pattern_Syntax # Cn [3] .. 2BBD..2BC8 ; Pattern_Syntax # So [12] BALLOT BOX WITH LIGHT X..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED 2BC9 ; Pattern_Syntax # Cn 2BCA..2BD1 ; Pattern_Syntax # So [8] TOP HALF BLACK CIRCLE..UNCERTAINTY SIGN 2BD2..2BEB ; Pattern_Syntax # Cn [26] .. 2BEC..2BEF ; Pattern_Syntax # So [4] LEFTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS..DOWNWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS 2BF0..2BFF ; Pattern_Syntax # Cn [16] .. 2E00..2E01 ; Pattern_Syntax # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER 2E02 ; Pattern_Syntax # Pi LEFT SUBSTITUTION BRACKET 2E03 ; Pattern_Syntax # Pf RIGHT SUBSTITUTION BRACKET 2E04 ; Pattern_Syntax # Pi LEFT DOTTED SUBSTITUTION BRACKET 2E05 ; Pattern_Syntax # Pf RIGHT DOTTED SUBSTITUTION BRACKET 2E06..2E08 ; Pattern_Syntax # Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER 2E09 ; Pattern_Syntax # Pi LEFT TRANSPOSITION BRACKET 2E0A ; Pattern_Syntax # Pf RIGHT TRANSPOSITION BRACKET 2E0B ; Pattern_Syntax # Po RAISED SQUARE 2E0C ; Pattern_Syntax # Pi LEFT RAISED OMISSION BRACKET 2E0D ; Pattern_Syntax # Pf RIGHT RAISED OMISSION BRACKET 2E0E..2E16 ; Pattern_Syntax # Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE 2E17 ; Pattern_Syntax # Pd DOUBLE OBLIQUE HYPHEN 2E18..2E19 ; Pattern_Syntax # Po [2] INVERTED INTERROBANG..PALM BRANCH 2E1A ; Pattern_Syntax # Pd HYPHEN WITH DIAERESIS 2E1B ; Pattern_Syntax # Po TILDE WITH RING ABOVE 2E1C ; Pattern_Syntax # Pi LEFT LOW PARAPHRASE BRACKET 2E1D ; Pattern_Syntax # Pf RIGHT LOW PARAPHRASE BRACKET 2E1E..2E1F ; Pattern_Syntax # Po [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW 2E20 ; Pattern_Syntax # Pi LEFT VERTICAL BAR WITH QUILL 2E21 ; Pattern_Syntax # Pf RIGHT VERTICAL BAR WITH QUILL 2E22 ; Pattern_Syntax # Ps TOP LEFT HALF BRACKET 2E23 ; Pattern_Syntax # Pe TOP RIGHT HALF BRACKET 2E24 ; Pattern_Syntax # Ps BOTTOM LEFT HALF BRACKET 2E25 ; Pattern_Syntax # Pe BOTTOM RIGHT HALF BRACKET 2E26 ; Pattern_Syntax # Ps LEFT SIDEWAYS U BRACKET 2E27 ; Pattern_Syntax # Pe RIGHT SIDEWAYS U BRACKET 2E28 ; Pattern_Syntax # Ps LEFT DOUBLE PARENTHESIS 2E29 ; Pattern_Syntax # Pe RIGHT DOUBLE PARENTHESIS 2E2A..2E2E ; Pattern_Syntax # Po [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK 2E2F ; Pattern_Syntax # Lm VERTICAL TILDE 2E30..2E39 ; Pattern_Syntax # Po [10] RING POINT..TOP HALF SECTION SIGN 2E3A..2E3B ; Pattern_Syntax # Pd [2] TWO-EM DASH..THREE-EM DASH 2E3C..2E3F ; Pattern_Syntax # Po [4] STENOGRAPHIC FULL STOP..CAPITULUM 2E40 ; Pattern_Syntax # Pd DOUBLE HYPHEN 2E41 ; Pattern_Syntax # Po REVERSED COMMA 2E42 ; Pattern_Syntax # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK 2E43..2E44 ; Pattern_Syntax # Po [2] DASH WITH LEFT UPTURN..DOUBLE SUSPENSION MARK 2E45..2E7F ; Pattern_Syntax # Cn [59] .. 3001..3003 ; Pattern_Syntax # Po [3] IDEOGRAPHIC COMMA..DITTO MARK 3008 ; Pattern_Syntax # Ps LEFT ANGLE BRACKET 3009 ; Pattern_Syntax # Pe RIGHT ANGLE BRACKET 300A ; Pattern_Syntax # Ps LEFT DOUBLE ANGLE BRACKET 300B ; Pattern_Syntax # Pe RIGHT DOUBLE ANGLE BRACKET 300C ; Pattern_Syntax # Ps LEFT CORNER BRACKET 300D ; Pattern_Syntax # Pe RIGHT CORNER BRACKET 300E ; Pattern_Syntax # Ps LEFT WHITE CORNER BRACKET 300F ; Pattern_Syntax # Pe RIGHT WHITE CORNER BRACKET 3010 ; Pattern_Syntax # Ps LEFT BLACK LENTICULAR BRACKET 3011 ; Pattern_Syntax # Pe RIGHT BLACK LENTICULAR BRACKET 3012..3013 ; Pattern_Syntax # So [2] POSTAL MARK..GETA MARK 3014 ; Pattern_Syntax # Ps LEFT TORTOISE SHELL BRACKET 3015 ; Pattern_Syntax # Pe RIGHT TORTOISE SHELL BRACKET 3016 ; Pattern_Syntax # Ps LEFT WHITE LENTICULAR BRACKET 3017 ; Pattern_Syntax # Pe RIGHT WHITE LENTICULAR BRACKET 3018 ; Pattern_Syntax # Ps LEFT WHITE TORTOISE SHELL BRACKET 3019 ; Pattern_Syntax # Pe RIGHT WHITE TORTOISE SHELL BRACKET 301A ; Pattern_Syntax # Ps LEFT WHITE SQUARE BRACKET 301B ; Pattern_Syntax # Pe RIGHT WHITE SQUARE BRACKET 301C ; Pattern_Syntax # Pd WAVE DASH 301D ; Pattern_Syntax # Ps REVERSED DOUBLE PRIME QUOTATION MARK 301E..301F ; Pattern_Syntax # Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK 3020 ; Pattern_Syntax # So POSTAL MARK FACE 3030 ; Pattern_Syntax # Pd WAVY DASH FD3E ; Pattern_Syntax # Pe ORNATE LEFT PARENTHESIS FD3F ; Pattern_Syntax # Ps ORNATE RIGHT PARENTHESIS FE45..FE46 ; Pattern_Syntax # Po [2] SESAME DOT..WHITE SESAME DOT # Total code points: 2760 # ================================================ 0600..0605 ; Prepended_Concatenation_Mark # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE 06DD ; Prepended_Concatenation_Mark # Cf ARABIC END OF AYAH 070F ; Prepended_Concatenation_Mark # Cf SYRIAC ABBREVIATION MARK 08E2 ; Prepended_Concatenation_Mark # Cf ARABIC DISPUTED END OF AYAH 110BD ; Prepended_Concatenation_Mark # Cf KAITHI NUMBER SIGN # Total code points: 10 # EOF dovecot-2.2.33.2/src/lib-fts/stopwords/0002755000175000017500000000000013172375611014610 500000000000000dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_en.txt0000644000175000017500000000210613165463547020024 00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # a couple of test stopwords to test that the words are really being # configured from this file: stopworda stopwordb # Standard english stop words taken from Lucene's StopAnalyzer a an and are as at be but by for if in into is it no not of on or such that the their then there these they this to was will with dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_malformed.txt0000644000175000017500000052221113123174404021355 00000000000000 lucene-solr/stopwords_de.txt at master · apache/lucene-solr · GitHub Skip to content
Find file
be4680a
295 lines (243 sloc) 4.47 KB
| From svn.tartarus.org/snowball/trunk/website/algorithms/german/stop.txt
| This file is distributed under the BSD License.
| See http://snowball.tartarus.org/license.php
| Also see http://www.opensource.org/licenses/bsd-license.html
| - Encoding was converted to UTF-8.
| - This notice was added.
|
| NOTE: To use this file with StopFilterFactory, you must specify format="snowball"
| A German stop word list. Comments begin with vertical bar. Each stop
| word is at the start of a line.
| The number of forms in this list is reduced significantly by passing it
| through the German stemmer.
aber | but
alle | all
allem
allen
aller
alles
als | than, as
also | so
am | an + dem
an | at
ander | other
andere
anderem
anderen
anderer
anderes
anderm
andern
anderr
anders
auch | also
auf | on
aus | out of
bei | by
bin | am
bis | until
bist | art
da | there
damit | with it
dann | then
der | the
den
des
dem
die
das
daß | that
derselbe | the same
derselben
denselben
desselben
demselben
dieselbe
dieselben
dasselbe
dazu | to that
dein | thy
deine
deinem
deinen
deiner
deines
denn | because
derer | of those
dessen | of him
dich | thee
dir | to thee
du | thou
dies | this
diese
diesem
diesen
dieser
dieses
doch | (several meanings)
dort | (over) there
durch | through
ein | a
eine
einem
einen
einer
eines
einig | some
einige
einigem
einigen
einiger
einiges
einmal | once
er | he
ihn | him
ihm | to him
es | it
etwas | something
euer | your
eure
eurem
euren
eurer
eures
für | for
gegen | towards
gewesen | p.p. of sein
hab | have
habe | have
haben | have
hat | has
hatte | had
hatten | had
hier | here
hin | there
hinter | behind
ich | I
mich | me
mir | to me
ihr | you, to her
ihre
ihrem
ihren
ihrer
ihres
euch | to you
im | in + dem
in | in
indem | while
ins | in + das
ist | is
jede | each, every
jedem
jeden
jeder
jedes
jene | that
jenem
jenen
jener
jenes
jetzt | now
kann | can
kein | no
keine
keinem
keinen
keiner
keines
können | can
könnte | could
machen | do
man | one
manche | some, many a
manchem
manchen
mancher
manches
mein | my
meine
meinem
meinen
meiner
meines
mit | with
muss | must
musste | had to
nach | to(wards)
nicht | not
nichts | nothing
noch | still, yet
nun | now
nur | only
ob | whether
oder | or
ohne | without
sehr | very
sein | his
seine
seinem
seinen
seiner
seines
selbst | self
sich | herself
sie | they, she
ihnen | to them
sind | are
so | so
solche | such
solchem
solchen
solcher
solches
soll | shall
sollte | should
sondern | but
sonst | else
über | over
um | about, around
und | and
uns | us
unse
unsem
unsen
unser
unses
unter | under
viel | much
vom | von + dem
von | from
vor | before
während | while
war | was
waren | were
warst | wast
was | what
weg | away, off
weil | because
weiter | further
welche | which
welchem
welchen
welcher
welches
wenn | when
werde | will
werden | will
wie | how
wieder | again
will | want
wir | we
wird | will
wirst | willst
wo | where
wollen | want
wollte | wanted
würde | would
würden | would
zu | to
zum | zu + dem
zur | zu + der
zwar | indeed
zwischen | between
Something went wrong with that request. Please try again.
dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_fi.txt0000644000175000017500000000743513123174404020013 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/finnish/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | forms of BE olla olen olet on olemme olette ovat ole | negative form oli olisi olisit olisin olisimme olisitte olisivat olit olin olimme olitte olivat ollut olleet en | negation et ei emme ette eivät |Words below in the following cases: |Nom Gen Acc Part Iness Elat Illat Adess Ablat Allat Ess Trans minä | I minun | I minut | I minua | I minussa | I minusta | I minuun | I minulla | I minulta | I minulle | I sinä | you sinun | you sinut | you sinua | you sinussa | you sinusta | you sinuun | you sinulla | you sinulta | you sinulle | you hän | he she hänen | he she hänet | he she häntä | he she hänessä | he she hänestä | he she häneen | he she hänellä | he she häneltä | he she hänelle | he she me | we meidän | we meidät | we meitä | we meissä | we meistä | we meihin | we meillä | we meiltä | we meille | we te | you teidän | you teidät | you teitä | you teissä | you teistä | you teihin | you teillä | you teiltä | you teille | you he | they heidän | they heidät | they heitä | they heissä | they heistä | they heihin | they heillä | they heiltä | they heille | they tämä | this tämän | this tätä | this tässä | this tästä | this tähän | this tallä | this tältä | this tälle | this tänä | this täksi | this tuo | that tuon | that tuotä | that tuossa | that tuosta | that tuohon | that tuolla | that tuolta | that tuolle | that tuona | that tuoksi | that se | it sen | it sitä | it siinä | it siitä | it siihen | it sillä | it siltä | it sille | it sinä | it siksi | it nämä | these näiden | these näitä | these näissä | these näistä | these näihin | these näillä | these näiltä | these näille | these näinä | these näiksi | these nuo | those noiden | those noita | those noissa | those noista | those noihin | those noilla | those noilta | those noille | those noina | those noiksi | those ne | they niiden | they niitä | they niissä | they niistä | they niihin | they niillä | they niiltä | they niille | they niinä | they niiksi | they kuka | who kenen | who kenet | who ketä | who kenessä | who kenestä | who keneen | who kenellä | who keneltä | who kenelle | who kenenä | who keneksi | who ketkä | who (pl) keiden | who (pl) ketkä | who (pl) keitä | who (pl) keissä | who (pl) keistä | who (pl) keihin | who (pl) keillä | who (pl) keiltä | who (pl) keille | who (pl) keinä | who (pl) keiksi | who (pl) mikä | which what minkä | which what minkä | which what mitä | which what missä | which what mistä | which what mihin | which what millä | which what miltä | which what mille | which what minä | which what miksi | which what mitkä | which what (pl) joka | who which jonka | who which jota | who which jossa | who which josta | who which johon | who which jolla | who which jolta | who which jolle | who which jona | who which joksi | who which jotka | who which (pl) joiden | who which (pl) joita | who which (pl) joissa | who which (pl) joista | who which (pl) joihin | who which (pl) joilla | who which (pl) joilta | who which (pl) joille | who which (pl) joina | who which (pl) joiksi | who which (pl) | conjunctions että | that ja | and jos | if koska | because kuin | than mutta | but niin | so sekä | and sillä | for tai | or vaan | but vai | or vaikka | although | prepositions kanssa | with mukaan | according to noin | about poikki | across yli | over, across | other kun | when niin | so nyt | now itse | self dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_da.txt0000644000175000017500000000652113123174404017774 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/danish/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | A Danish stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | This is a ranked list (commonest to rarest) of stopwords derived from | a large text sample. og | and i | in jeg | I det | that (dem. pronoun)/it (pers. pronoun) at | that (in front of a sentence)/to (with infinitive) en | a/an den | it (pers. pronoun)/that (dem. pronoun) til | to/at/for/until/against/by/of/into, more er | present tense of "to be" som | who, as på | on/upon/in/on/at/to/after/of/with/for, on de | they med | with/by/in, along han | he af | of/by/from/off/for/in/with/on, off for | at/for/to/from/by/of/ago, in front/before, because ikke | not der | who/which, there/those var | past tense of "to be" mig | me/myself sig | oneself/himself/herself/itself/themselves men | but et | a/an/one, one (number), someone/somebody/one har | present tense of "to have" om | round/about/for/in/a, about/around/down, if vi | we min | my havde | past tense of "to have" ham | him hun | she nu | now over | over/above/across/by/beyond/past/on/about, over/past da | then, when/as/since fra | from/off/since, off, since du | you ud | out sin | his/her/its/one's dem | them os | us/ourselves op | up man | you/one hans | his hvor | where eller | or hvad | what skal | must/shall etc. selv | myself/youself/herself/ourselves etc., even her | here alle | all/everyone/everybody etc. vil | will (verb) blev | past tense of "to stay/to remain/to get/to become" kunne | could ind | in når | when være | present tense of "to be" dog | however/yet/after all noget | something ville | would jo | you know/you see (adv), yes deres | their/theirs efter | after/behind/according to/for/by/from, later/afterwards ned | down skulle | should denne | this end | than dette | this mit | my/mine også | also under | under/beneath/below/during, below/underneath have | have dig | you anden | other hende | her mine | my alt | everything meget | much/very, plenty of sit | his, her, its, one's sine | his, her, its, one's vor | our mod | against disse | these hvis | if din | your/yours nogle | some hos | by/at blive | be/become mange | many ad | by/through bliver | present tense of "to be/to become" hendes | her/hers været | be thi | for (conj) jer | you sådan | such, like this/like that dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_fr.txt0000644000175000017500000000603713165463547020040 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/french/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | A French stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. au | a + le aux | a + les avec | with ce | this ces | these dans | with de | of des | de + les du | de + le elle | she en | `of them' etc et | and eux | them il | he je | I la | the le | the leur | their lui | him ma | my (fem) mais | but me | me même | same; as in moi-même (myself) etc mes | me (pl) moi | me mon | my (masc) ne | not nos | our (pl) notre | our nous | we on | one ou | where par | by pas | not pour | for qu | que before vowel que | that qui | who sa | his, her (fem) se | oneself ses | his (pl) son | his, her (masc) sur | on ta | thy (fem) te | thee tes | thy (pl) toi | thee ton | thy (masc) tu | thou un | a une | a vos | your (pl) votre | your vous | you | single letter forms c | c' d | d' j | j' l | l' à | to, at m | m' n | n' s | s' t | t' y | there | forms of être (not including the infinitive): été étée étées étés étant suis es est sommes êtes sont serai seras sera serons serez seront serais serait serions seriez seraient étais était étions étiez étaient fus fut fûmes fûtes furent sois soit soyons soyez soient fusse fusses fût fussions fussiez fussent | forms of avoir (not including the infinitive): ayant eu eue eues eus ai as avons avez ont aurai auras aura aurons aurez auront aurais aurait aurions auriez auraient avais avait avions aviez avaient eut eûmes eûtes eurent aie aies ait ayons ayez aient eusse eusses eût eussions eussiez eussent | Later additions (from Jean-Christophe Deschamps) ceci | this cela | that celà | that cet | this cette | this ici | here ils | they les | the (pl) leurs | their (pl) quel | which quels | which quelle | which quelles | which sans | without soi | oneself dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_sv.txt0000644000175000017500000000652313165463547020061 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/swedish/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | A Swedish stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | This is a ranked list (commonest to rarest) of stopwords derived from | a large text sample. | Swedish stop words occasionally exhibit homonym clashes. For example | så = so, but also seed. These are indicated clearly below. och | and det | it, this/that att | to (with infinitive) i | in, at en | a jag | I hon | she som | who, that han | he på | on den | it, this/that med | with var | where, each sig | him(self) etc för | for så | so (also: seed) till | to är | is men | but ett | a om | if; around, about hade | had de | they, these/those av | of icke | not, no mig | me du | you henne | her då | then, when sin | his nu | now har | have inte | inte någon = no one hans | his honom | him skulle | 'sake' hennes | her där | there min | my man | one (pronoun) ej | nor vid | at, by, on (also: vast) kunde | could något | some etc från | from, off ut | out när | when efter | after, behind upp | up vi | we dem | them vara | be vad | what över | over än | than dig | you kan | can sina | his här | here ha | have mot | towards alla | all under | under (also: wonder) någon | some etc eller | or (else) allt | all mycket | much sedan | since ju | why denna | this/that själv | myself, yourself etc detta | this/that åt | to utan | without varit | was hur | how ingen | no mitt | my ni | you bli | to be, become blev | from bli oss | us din | thy dessa | these/those några | some etc deras | their blir | from bli mina | my samma | (the) same vilken | who, that er | you, your sådan | such a vår | our blivit | from bli dess | its inom | within mellan | between sådant | such a varför | why varje | each vilka | who, that ditt | thy vem | who vilket | who, that sitta | his sådana | such a vart | each dina | thy vars | whose vårt | our våra | our ert | your era | your vilkas | whose dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_nl.txt0000644000175000017500000001066013123174404020020 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/dutch/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | A Dutch stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | This is a ranked list (commonest to rarest) of stopwords derived from | a large sample of Dutch text. | Dutch stop words frequently exhibit homonym clashes. These are indicated | clearly below. de | the en | and van | of, from ik | I, the ego te | (1) chez, at etc, (2) to, (3) too dat | that, which die | that, those, who, which in | in, inside een | a, an, one hij | he het | the, it niet | not, nothing, naught zijn | (1) to be, being, (2) his, one's, its is | is was | (1) was, past tense of all persons sing. of 'zijn' (to be) (2) wax, (3) the washing, (4) rise of river op | on, upon, at, in, up, used up aan | on, upon, to (as dative) met | with, by als | like, such as, when voor | (1) before, in front of, (2) furrow had | had, past tense all persons sing. of 'hebben' (have) er | there maar | but, only om | round, about, for etc hem | him dan | then zou | should/would, past tense all persons sing. of 'zullen' of | or, whether, if wat | what, something, anything mijn | possessive and noun 'mine' men | people, 'one' dit | this zo | so, thus, in this way door | through by over | over, across ze | she, her, they, them zich | oneself bij | (1) a bee, (2) by, near, at ook | also, too tot | till, until je | you mij | me uit | out of, from der | Old Dutch form of 'van der' still found in surnames daar | (1) there, (2) because haar | (1) her, their, them, (2) hair naar | (1) unpleasant, unwell etc, (2) towards, (3) as heb | present first person sing. of 'to have' hoe | how, why heeft | present third person sing. of 'to have' hebben | 'to have' and various parts thereof deze | this u | you want | (1) for, (2) mitten, (3) rigging nog | yet, still zal | 'shall', first and third person sing. of verb 'zullen' (will) me | me zij | she, they nu | now ge | 'thou', still used in Belgium and south Netherlands geen | none omdat | because iets | something, somewhat worden | to become, grow, get toch | yet, still al | all, every, each waren | (1) 'were' (2) to wander, (3) wares, (3) veel | much, many meer | (1) more, (2) lake doen | to do, to make toen | then, when moet | noun 'spot/mote' and present form of 'to must' ben | (1) am, (2) 'are' in interrogative second person singular of 'to be' zonder | without kan | noun 'can' and present form of 'to be able' hun | their, them dus | so, consequently alles | all, everything, anything onder | under, beneath ja | yes, of course eens | once, one day hier | here wie | who werd | imperfect third person sing. of 'become' altijd | always doch | yet, but etc wordt | present third person sing. of 'become' wezen | (1) to be, (2) 'been' as in 'been fishing', (3) orphans kunnen | to be able ons | us/our zelf | self tegen | against, towards, at na | after, near reeds | already wil | (1) present tense of 'want', (2) 'will', noun, (3) fender kon | could; past tense of 'to be able' niets | nothing uw | your iemand | somebody geweest | been; past participle of 'be' andere | other dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_es.txt0000644000175000017500000001276713123174404020030 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/spanish/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | A Spanish stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | The following is a ranked list (commonest to rarest) of stopwords | deriving from a large sample of text. | Extra words have been added at the end. de | from, of la | the, her que | who, that el | the en | in y | and a | to los | the, them del | de + el se | himself, from him etc las | the, them por | for, by, etc un | a para | for con | with no | no una | a su | his, her al | a + el | es from SER lo | him como | how más | more pero | pero sus | su plural le | to him, her ya | already o | or | fue from SER este | this | ha from HABER sí | himself etc porque | because esta | this | son from SER entre | between | está from ESTAR cuando | when muy | very sin | without sobre | on | ser from SER | tiene from TENER también | also me | me hasta | until hay | there is/are donde | where | han from HABER quien | whom, that | están from ESTAR | estado from ESTAR desde | from todo | all nos | us durante | during | estados from ESTAR todos | all uno | a les | to them ni | nor contra | against otros | other | fueron from SER ese | that eso | that | había from HABER ante | before ellos | they e | and (variant of y) esto | this mí | me antes | before algunos | some qué | what? unos | a yo | I otro | other otras | other otra | other él | he tanto | so much, many esa | that estos | these mucho | much, many quienes | who nada | nothing muchos | many cual | who | sea from SER poco | few ella | she estar | to be | haber from HABER estas | these | estaba from ESTAR | estamos from ESTAR algunas | some algo | something nosotros | we | other forms mi | me mis | mi plural tú | thou te | thee ti | thee tu | thy tus | tu plural ellas | they nosotras | we vosotros | you vosotras | you os | you mío | mine mía | míos | mías | tuyo | thine tuya | tuyos | tuyas | suyo | his, hers, theirs suya | suyos | suyas | nuestro | ours nuestra | nuestros | nuestras | vuestro | yours vuestra | vuestros | vuestras | esos | those esas | those | forms of estar, to be (not including the infinitive): estoy estás está estamos estáis están esté estés estemos estéis estén estaré estarás estará estaremos estaréis estarán estaría estarías estaríamos estaríais estarían estaba estabas estábamos estabais estaban estuve estuviste estuvo estuvimos estuvisteis estuvieron estuviera estuvieras estuviéramos estuvierais estuvieran estuviese estuvieses estuviésemos estuvieseis estuviesen estando estado estada estados estadas estad | forms of haber, to have (not including the infinitive): he has ha hemos habéis han haya hayas hayamos hayáis hayan habré habrás habrá habremos habréis habrán habría habrías habríamos habríais habrían había habías habíamos habíais habían hube hubiste hubo hubimos hubisteis hubieron hubiera hubieras hubiéramos hubierais hubieran hubiese hubieses hubiésemos hubieseis hubiesen habiendo habido habida habidos habidas | forms of ser, to be (not including the infinitive): soy eres es somos sois son sea seas seamos seáis sean seré serás será seremos seréis serán sería serías seríamos seríais serían era eras éramos erais eran fui fuiste fue fuimos fuisteis fueron fuera fueras fuéramos fuerais fueran fuese fueses fuésemos fueseis fuesen siendo sido | sed also means 'thirst' | forms of tener, to have (not including the infinitive): tengo tienes tiene tenemos tenéis tienen tenga tengas tengamos tengáis tengan tendré tendrás tendrá tendremos tendréis tendrán tendría tendrías tendríamos tendríais tendrían tenía tenías teníamos teníais tenían tuve tuviste tuvo tuvimos tuvisteis tuvieron tuviera tuvieras tuviéramos tuvierais tuvieran tuviese tuvieses tuviésemos tuvieseis tuviesen teniendo tenido tenida tenidos tenidas tened dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_it.txt0000644000175000017500000001134713123174404020026 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/italian/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | An Italian stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. ad | a (to) before vowel al | a + il allo | a + lo ai | a + i agli | a + gli all | a + l' agl | a + gl' alla | a + la alle | a + le con | with col | con + il coi | con + i (forms collo, cogli etc are now very rare) da | from dal | da + il dallo | da + lo dai | da + i dagli | da + gli dall | da + l' dagl | da + gll' dalla | da + la dalle | da + le di | of del | di + il dello | di + lo dei | di + i degli | di + gli dell | di + l' degl | di + gl' della | di + la delle | di + le in | in nel | in + el nello | in + lo nei | in + i negli | in + gli nell | in + l' negl | in + gl' nella | in + la nelle | in + le su | on sul | su + il sullo | su + lo sui | su + i sugli | su + gli sull | su + l' sugl | su + gl' sulla | su + la sulle | su + le per | through, by tra | among contro | against io | I tu | thou lui | he lei | she noi | we voi | you loro | they mio | my mia | miei | mie | tuo | tua | tuoi | thy tue | suo | sua | suoi | his, her sue | nostro | our nostra | nostri | nostre | vostro | your vostra | vostri | vostre | mi | me ti | thee ci | us, there vi | you, there lo | him, the la | her, the li | them le | them, the gli | to him, the ne | from there etc il | the un | a uno | a una | a ma | but ed | and se | if perché | why, because anche | also come | how dov | where (as dov') dove | where che | who, that chi | who cui | whom non | not più | more quale | who, that quanto | how much quanti | quanta | quante | quello | that quelli | quella | quelle | questo | this questi | questa | queste | si | yes tutto | all tutti | all | single letter forms: a | at c | as c' for ce or ci e | and i | the l | as l' o | or | forms of avere, to have (not including the infinitive): ho hai ha abbiamo avete hanno abbia abbiate abbiano avrò avrai avrà avremo avrete avranno avrei avresti avrebbe avremmo avreste avrebbero avevo avevi aveva avevamo avevate avevano ebbi avesti ebbe avemmo aveste ebbero avessi avesse avessimo avessero avendo avuto avuta avuti avute | forms of essere, to be (not including the infinitive): sono sei è siamo siete sia siate siano sarò sarai sarà saremo sarete saranno sarei saresti sarebbe saremmo sareste sarebbero ero eri era eravamo eravate erano fui fosti fu fummo foste furono fossi fosse fossimo fossero essendo | forms of fare, to do (not including the infinitive, fa, fat-): faccio fai facciamo fanno faccia facciate facciano farò farai farà faremo farete faranno farei faresti farebbe faremmo fareste farebbero facevo facevi faceva facevamo facevate facevano feci facesti fece facemmo faceste fecero facessi facesse facessimo facessero facendo | forms of stare, to be (not including the infinitive): sto stai sta stiamo stanno stia stiate stiano starò starai starà staremo starete staranno starei staresti starebbe staremmo stareste starebbero stavo stavi stava stavamo stavate stavano stetti stesti stette stemmo steste stettero stessi stesse stessimo stessero stando dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_pt.txt0000644000175000017500000001065513123174404020036 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/portuguese/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | A Portuguese stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | The following is a ranked list (commonest to rarest) of stopwords | deriving from a large sample of text. | Extra words have been added at the end. de | of, from a | the; to, at; her o | the; him que | who, that e | and do | de + o da | de + a em | in um | a para | for | é from SER com | with não | not, no uma | a os | the; them no | em + o se | himself etc na | em + a por | for mais | more as | the; them dos | de + os como | as, like mas | but | foi from SER ao | a + o ele | he das | de + as | tem from TER à | a + a seu | his sua | her ou | or | ser from SER quando | when muito | much | há from HAV nos | em + os; us já | already, now | está from EST eu | I também | also só | only, just pelo | per + o pela | per + a até | up to isso | that ela | he entre | between | era from SER depois | after sem | without mesmo | same aos | a + os | ter from TER seus | his quem | whom nas | em + as me | me esse | that eles | they | estão from EST você | you | tinha from TER | foram from SER essa | that num | em + um nem | nor suas | her meu | my às | a + as minha | my | têm from TER numa | em + uma pelos | per + os elas | they | havia from HAV | seja from SER qual | which | será from SER nós | we | tenho from TER lhe | to him, her deles | of them essas | those esses | those pelas | per + as este | this | fosse from SER dele | of him | other words. There are many contractions such as naquele = em+aquele, | mo = me+o, but they are rare. | Indefinite article plural forms are also rare. tu | thou te | thee vocês | you (plural) vos | you lhes | to them meus | my minhas teu | thy tua teus tuas nosso | our nossa nossos nossas dela | of her delas | of them esta | this estes | these estas | these aquele | that aquela | that aqueles | those aquelas | those isto | this aquilo | that | forms of estar, to be (not including the infinitive): estou está estamos estão estive esteve estivemos estiveram estava estávamos estavam estivera estivéramos esteja estejamos estejam estivesse estivéssemos estivessem estiver estivermos estiverem | forms of haver, to have (not including the infinitive): hei há havemos hão houve houvemos houveram houvera houvéramos haja hajamos hajam houvesse houvéssemos houvessem houver houvermos houverem houverei houverá houveremos houverão houveria houveríamos houveriam | forms of ser, to be (not including the infinitive): sou somos são era éramos eram fui foi fomos foram fora fôramos seja sejamos sejam fosse fôssemos fossem for formos forem serei será seremos serão seria seríamos seriam | forms of ter, to have (not including the infinitive): tenho tem temos tém tinha tínhamos tinham tive teve tivemos tiveram tivera tivéramos tenha tenhamos tenham tivesse tivéssemos tivessem tiver tivermos tiverem terei terá teremos terão teria teríamos teriam dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_de.txt0000644000175000017500000001062013123174404017773 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/german/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | A German stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | The number of forms in this list is reduced significantly by passing it | through the German stemmer. aber | but alle | all allem allen aller alles als | than, as also | so am | an + dem an | at ander | other andere anderem anderen anderer anderes anderm andern anderr anders auch | also auf | on aus | out of bei | by bin | am bis | until bist | art da | there damit | with it dann | then der | the den des dem die das daß | that derselbe | the same derselben denselben desselben demselben dieselbe dieselben dasselbe dazu | to that dein | thy deine deinem deinen deiner deines denn | because derer | of those dessen | of him dich | thee dir | to thee du | thou dies | this diese diesem diesen dieser dieses doch | (several meanings) dort | (over) there durch | through ein | a eine einem einen einer eines einig | some einige einigem einigen einiger einiges einmal | once er | he ihn | him ihm | to him es | it etwas | something euer | your eure eurem euren eurer eures für | for gegen | towards gewesen | p.p. of sein hab | have habe | have haben | have hat | has hatte | had hatten | had hier | here hin | there hinter | behind ich | I mich | me mir | to me ihr | you, to her ihre ihrem ihren ihrer ihres euch | to you im | in + dem in | in indem | while ins | in + das ist | is jede | each, every jedem jeden jeder jedes jene | that jenem jenen jener jenes jetzt | now kann | can kein | no keine keinem keinen keiner keines können | can könnte | could machen | do man | one manche | some, many a manchem manchen mancher manches mein | my meine meinem meinen meiner meines mit | with muss | must musste | had to nach | to(wards) nicht | not nichts | nothing noch | still, yet nun | now nur | only ob | whether oder | or ohne | without sehr | very sein | his seine seinem seinen seiner seines selbst | self sich | herself sie | they, she ihnen | to them sind | are so | so solche | such solchem solchen solcher solches soll | shall sollte | should sondern | but sonst | else über | over um | about, around und | and uns | us unse unsem unsen unser unses unter | under viel | much vom | von + dem von | from vor | before während | while war | was waren | were warst | wast was | what weg | away, off weil | because weiter | further welche | which welchem welchen welcher welches wenn | when werde | will werden | will wie | how wieder | again will | want wir | we wird | will wirst | willst wo | where wollen | want wollte | wanted würde | would würden | would zu | to zum | zu + dem zur | zu + der zwar | indeed zwischen | between dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_ro.txt0000644000175000017500000000300713123174404020024 00000000000000# This file was created by Jacques Savoy and is distributed under the BSD license. # See http://members.unine.ch/jacques.savoy/clef/index.html. # Also see http://www.opensource.org/licenses/bsd-license.html acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acum ai aia aibă aici al ăla ale alea ălea altceva altcineva am ar are aş aşadar asemenea asta ăsta astăzi astea ăstea ăştia asupra aţi au avea avem aveţi azi bine bucur bună ca că căci când care cărei căror cărui cât câte câţi către câtva ce cel ceva chiar cînd cine cineva cît cîte cîţi cîtva contra cu cum cumva curând curînd da dă dacă dar datorită de deci deja deoarece departe deşi din dinaintea dintr dintre drept după ea ei el ele eram este eşti eu face fără fi fie fiecare fii fim fiţi iar ieri îi îl îmi împotriva în înainte înaintea încât încît încotro între întrucât întrucît îţi la lângă le li lîngă lor lui mă mâine mea mei mele mereu meu mi mine mult multă mulţi ne nicăieri nici nimeni nişte noastră noastre noi noştri nostru nu ori oricând oricare oricât orice oricînd oricine oricît oricum oriunde până pe pentru peste pînă poate pot prea prima primul prin printr sa să săi sale sau său se şi sînt sîntem sînteţi spre sub sunt suntem sunteţi ta tăi tale tău te ţi ţie tine toată toate tot toţi totuşi tu un una unde undeva unei unele uneori unor vă vi voastră voastre voi voştri vostru vouă vreo vreun dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_no.txt0000644000175000017500000001126113165463547020040 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/norwegian/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | A Norwegian stop word list. Comments begin with vertical bar. Each stop | word is at the start of a line. | This stop word list is for the dominant bokmål dialect. Words unique | to nynorsk are marked *. | Revised by Jan Bruusgaard , Jan 2005 og | and i | in jeg | I det | it/this/that at | to (w. inf.) en | a/an et | a/an den | it/this/that til | to er | is/am/are som | who/that på | on de | they / you(formal) med | with han | he av | of ikke | not ikkje | not * der | there så | so var | was/were meg | me seg | you men | but ett | one har | have om | about vi | we min | my mitt | my ha | have hadde | had hun | she nå | now over | over da | when/as ved | by/know fra | from du | you ut | out sin | your dem | them oss | us opp | up man | you/one kan | can hans | his hvor | where eller | or hva | what skal | shall/must selv | self (reflective) sjøl | self (reflective) her | here alle | all vil | will bli | become ble | became blei | became * blitt | have become kunne | could inn | in når | when være | be kom | come noen | some noe | some ville | would dere | you som | who/which/that deres | their/theirs kun | only/just ja | yes etter | after ned | down skulle | should denne | this for | for/because deg | you si | hers/his sine | hers/his sitt | hers/his mot | against å | to meget | much hvorfor | why dette | this disse | these/those uten | without hvordan | how ingen | none din | your ditt | your blir | become samme | same hvilken | which hvilke | which (plural) sånn | such a inni | inside/within mellom | between vår | our hver | each hvem | who vors | us/ours hvis | whose både | both bare | only/just enn | than fordi | as/because før | before mange | many også | also slik | just vært | been være | to be båe | both * begge | both siden | since dykk | your * dykkar | yours * dei | they * deira | them * deires | theirs * deim | them * di | your (fem.) * då | as/when * eg | I * ein | a/an * eit | a/an * eitt | a/an * elles | or * honom | he * hjå | at * ho | she * hoe | she * henne | her hennar | her/hers hennes | hers hoss | how * hossen | how * ikkje | not * ingi | noone * inkje | noone * korleis | how * korso | how * kva | what/which * kvar | where * kvarhelst | where * kven | who/whom * kvi | why * kvifor | why * me | we * medan | while * mi | my * mine | my * mykje | much * no | now * nokon | some (masc./neut.) * noka | some (fem.) * nokor | some * noko | some * nokre | some * si | his/hers * sia | since * sidan | since * so | so * somt | some * somme | some * um | about* upp | up * vere | be * vore | was * verte | become * vort | become * varte | became * vart | became * dovecot-2.2.33.2/src/lib-fts/stopwords/stopwords_ru.txt0000644000175000017500000001715113123174404020037 00000000000000 | From svn.tartarus.org/snowball/trunk/website/algorithms/russian/stop.txt | This file is distributed under the BSD License. | See http://snowball.tartarus.org/license.php | Also see http://www.opensource.org/licenses/bsd-license.html | - Encoding was converted to UTF-8. | - This notice was added. | | a russian stop word list. comments begin with vertical bar. each stop | word is at the start of a line. | this is a ranked list (commonest to rarest) of stopwords derived from | a large text sample. | letter `ё' is translated to `е'. и | and в | in/into во | alternative form не | not что | what/that он | he на | on/onto я | i с | from со | alternative form как | how а | milder form of `no' (but) то | conjunction and form of `that' все | all она | she так | so, thus его | him но | but да | yes/and ты | thou к | towards, by у | around, chez же | intensifier particle вы | you за | beyond, behind бы | conditional/subj. particle по | up to, along только | only ее | her мне | to me было | it was вот | here is/are, particle от | away from меня | me еще | still, yet, more нет | no, there isnt/arent о | about из | out of ему | to him теперь | now когда | when даже | even ну | so, well вдруг | suddenly ли | interrogative particle если | if уже | already, but homonym of `narrower' или | or ни | neither быть | to be был | he was него | prepositional form of его до | up to вас | you accusative нибудь | indef. suffix preceded by hyphen опять | again уж | already, but homonym of `adder' вам | to you сказал | he said ведь | particle `after all' там | there потом | then себя | oneself ничего | nothing ей | to her может | usually with `быть' as `maybe' они | they тут | here где | where есть | there is/are надо | got to, must ней | prepositional form of ей для | for мы | we тебя | thee их | them, their чем | than была | she was сам | self чтоб | in order to без | without будто | as if человек | man, person, one чего | genitive form of `what' раз | once тоже | also себе | to oneself под | beneath жизнь | life будет | will be ж | short form of intensifer particle `же' тогда | then кто | who этот | this говорил | was saying того | genitive form of `that' потому | for that reason этого | genitive form of `this' какой | which совсем | altogether ним | prepositional form of `его', `они' здесь | here этом | prepositional form of `этот' один | one почти | almost мой | my тем | instrumental/dative plural of `тот', `то' чтобы | full form of `in order that' нее | her (acc.) кажется | it seems сейчас | now были | they were куда | where to зачем | why сказать | to say всех | all (acc., gen. preposn. plural) никогда | never сегодня | today можно | possible, one can при | by наконец | finally два | two об | alternative form of `о', about другой | another хоть | even после | after над | above больше | more тот | that one (masc.) через | across, in эти | these нас | us про | about всего | in all, only, of all них | prepositional form of `они' (they) какая | which, feminine много | lots разве | interrogative particle сказала | she said три | three эту | this, acc. fem. sing. моя | my, feminine впрочем | moreover, besides хорошо | good свою | ones own, acc. fem. sing. этой | oblique form of `эта', fem. `this' перед | in front of иногда | sometimes лучше | better чуть | a little том | preposn. form of `that one' нельзя | one must not такой | such a one им | to them более | more всегда | always конечно | of course всю | acc. fem. sing of `all' между | between | b: some paradigms | | personal pronouns | | я меня мне мной [мною] | ты тебя тебе тобой [тобою] | он его ему им [него, нему, ним] | она ее эи ею [нее, нэи, нею] | оно его ему им [него, нему, ним] | | мы нас нам нами | вы вас вам вами | они их им ими [них, ним, ними] | | себя себе собой [собою] | | demonstrative pronouns: этот (this), тот (that) | | этот эта это эти | этого эты это эти | этого этой этого этих | этому этой этому этим | этим этой этим [этою] этими | этом этой этом этих | | тот та то те | того ту то те | того той того тех | тому той тому тем | тем той тем [тою] теми | том той том тех | | determinative pronouns | | (a) весь (all) | | весь вся все все | всего всю все все | всего всей всего всех | всему всей всему всем | всем всей всем [всею] всеми | всем всей всем всех | | (b) сам (himself etc) | | сам сама само сами | самого саму само самих | самого самой самого самих | самому самой самому самим | самим самой самим [самою] самими | самом самой самом самих | | stems of verbs `to be', `to have', `to do' and modal | | быть бы буд быв есть суть | име | дел | мог мож мочь | уме | хоч хот | долж | можн | нужн | нельзя dovecot-2.2.33.2/src/lib-fts/fts-filter-english-possessive.c0000644000175000017500000000212713123174404020530 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "unichar.h" #include "fts-common.h" #include "fts-filter-private.h" static unichar_t get_ending_utf8_char(const char *str, size_t *end_pos) { unichar_t c; while (!UTF8_IS_START_SEQ(str[*end_pos])) { i_assert(*end_pos > 0); *end_pos -= 1; } if (uni_utf8_get_char(str + *end_pos, &c) <= 0) i_unreached(); return c; } static int fts_filter_english_possessive_filter(struct fts_filter *filter ATTR_UNUSED, const char **token, const char **error_r ATTR_UNUSED) { size_t len = strlen(*token); unichar_t c; if (len > 1 && ((*token)[len-1] == 's' || (*token)[len-1] == 'S')) { len -= 2; c = get_ending_utf8_char(*token, &len); if (IS_APOSTROPHE(c)) *token = t_strndup(*token, len); } return 1; } static const struct fts_filter fts_filter_english_possessive_real = { .class_name = "english-possessive", .v = { NULL, fts_filter_english_possessive_filter, NULL } }; const struct fts_filter *fts_filter_english_possessive = &fts_filter_english_possessive_real; dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-common.c0000644000175000017500000000155413123174404016716 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "unichar.h" #include "fts-tokenizer-common.h" void fts_tokenizer_delete_trailing_partial_char(const unsigned char *data, size_t *len) { size_t pos; unsigned int char_bytes; /* the token is truncated - make sure the last character exists entirely in the token */ for (pos = *len-1; pos > 0; pos--) { if (UTF8_IS_START_SEQ(data[pos])) break; } char_bytes = uni_utf8_char_bytes(data[pos]); if (char_bytes != *len-pos) { i_assert(char_bytes > *len-pos); *len = pos; } } void fts_tokenizer_delete_trailing_invalid_char(const unsigned char *data, size_t *len) { size_t pos = *len; /* the token may contain '.' in the end - remove all of them. */ while (pos > 0 && (data[pos-1] == '.' || data[pos-1] == '-')) pos--; *len = pos; } dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-private.h0000644000175000017500000000225113123174404017100 00000000000000#ifndef FTS_TOKENIZER_PRIVATE_H #define FTS_TOKENIZER_PRIVATE_H #include "fts-tokenizer.h" #define FTS_TOKENIZER_CLASSES_NR 2 struct fts_tokenizer_vfuncs { int (*create)(const char *const *settings, struct fts_tokenizer **tokenizer_r, const char **error_r); void (*destroy)(struct fts_tokenizer *tok); void (*reset)(struct fts_tokenizer *tok); int (*next)(struct fts_tokenizer *tok, const unsigned char *data, size_t size, size_t *skip_r, const char **token_r, const char **error_r); }; enum fts_tokenizer_parent_state { FTS_TOKENIZER_PARENT_STATE_ADD_DATA = 0, FTS_TOKENIZER_PARENT_STATE_NEXT_OUTPUT, FTS_TOKENIZER_PARENT_STATE_FINALIZE }; struct fts_tokenizer { const char *name; const struct fts_tokenizer_vfuncs *v; int refcount; struct fts_tokenizer *parent; buffer_t *parent_input; enum fts_tokenizer_parent_state parent_state; const unsigned char *prev_data; size_t prev_size; size_t prev_skip; bool prev_reply_finished; bool skip_parents; /* Return token as is, do not hand to parents. */ }; void fts_tokenizer_register(const struct fts_tokenizer *tok_class); void fts_tokenizer_unregister(const struct fts_tokenizer *tok_class); #endif dovecot-2.2.33.2/src/lib-fts/fts-filter-stopwords.c0000644000175000017500000000703713165463624016762 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "strfuncs.h" #include "hash.h" #include "unichar.h" #include "fts-language.h" #include "fts-filter-private.h" #define STOPWORDS_FILE_FORMAT "%s/stopwords_%s.txt" #define STOPWORDS_CUTCHARS "|#\t " #define STOPWORDS_DISALLOWED_CHARS "/\\<>.,\":()\t\n\r" struct fts_filter_stopwords { struct fts_filter filter; struct fts_language *lang; pool_t pool; HASH_TABLE(const char *, const char *) stopwords; const char *stopwords_dir; }; static int fts_filter_stopwords_read_list(struct fts_filter_stopwords *filter, const char **error_r) { struct istream *input; const char *line, *word, *path; int ret = 0; size_t len; path = t_strdup_printf(STOPWORDS_FILE_FORMAT, filter->stopwords_dir, filter->lang->name); input = i_stream_create_file(path, IO_BLOCK_SIZE); while ((line = i_stream_read_next_line(input)) != NULL) { len = strcspn(line, STOPWORDS_CUTCHARS); if (len == 0) continue; if (strcspn(line, STOPWORDS_DISALLOWED_CHARS) < len) continue; word = p_strndup(filter->pool, line, len); hash_table_insert(filter->stopwords, word, word); } if (input->stream_errno != 0) { *error_r = t_strdup_printf("Failed to read stopword list %s: %s", path, i_stream_get_error(input)); ret = -1; } if (ret == 0 && hash_table_count(filter->stopwords) == 0) i_warning("Stopwords list \"%s\" seems empty. Is the file correctly formatted?", path); i_stream_destroy(&input); return ret; } static void fts_filter_stopwords_destroy(struct fts_filter *filter) { struct fts_filter_stopwords *sp = (struct fts_filter_stopwords *)filter; if (hash_table_is_created(sp->stopwords)) hash_table_destroy(&sp->stopwords); pool_unref(&sp->pool); } static int fts_filter_stopwords_create(const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter_stopwords *sp; pool_t pp; const char *dir = NULL; unsigned int i; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "stopwords_dir") == 0) { dir = value; } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } pp = pool_alloconly_create(MEMPOOL_GROWING"fts_filter_stopwords", sizeof(struct fts_filter)); sp = p_new(pp, struct fts_filter_stopwords, 1); sp->filter = *fts_filter_stopwords; sp->pool = pp; sp->lang = p_malloc(sp->pool, sizeof(struct fts_language)); sp->lang->name = p_strdup(sp->pool, lang->name); if (dir != NULL) sp->stopwords_dir = p_strdup(pp, dir); else sp->stopwords_dir = DATADIR"/stopwords"; *filter_r = &sp->filter; return 0; } static int fts_filter_stopwords_filter(struct fts_filter *filter, const char **token, const char **error_r) { struct fts_filter_stopwords *sp = (struct fts_filter_stopwords *) filter; if (!hash_table_is_created(sp->stopwords)) { hash_table_create(&sp->stopwords, sp->pool, 0, str_hash, strcmp); if (fts_filter_stopwords_read_list(sp, error_r) < 0) return -1; } return hash_table_lookup(sp->stopwords, *token) == NULL ? 1 : 0; } const struct fts_filter fts_filter_stopwords_real = { .class_name = "stopwords", .v = { fts_filter_stopwords_create, fts_filter_stopwords_filter, fts_filter_stopwords_destroy } }; const struct fts_filter *fts_filter_stopwords = &fts_filter_stopwords_real; dovecot-2.2.33.2/src/lib-fts/fts-tokenizer-common.h0000644000175000017500000000044713123174404016723 00000000000000#ifndef FTS_TOKENIZER_COMMON_H #define FTS_TOKENIZER_COMMON_H void fts_tokenizer_delete_trailing_partial_char(const unsigned char *data, size_t *len); void fts_tokenizer_delete_trailing_invalid_char(const unsigned char *data, size_t *len); #endif dovecot-2.2.33.2/src/lib-fts/fts-filter-contractions.c0000644000175000017500000000346313165463543017423 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "fts-language.h" #include "fts-filter-private.h" #include "fts-common.h" #include "unichar.h" static int fts_filter_contractions_create(const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter *filter; if (settings[0] != NULL) { *error_r = t_strdup_printf("Unknown setting: %s", settings[0]); return -1; } if (strcmp(lang->name, "fr") != 0) { *error_r = t_strdup_printf("Unsupported language: %s", lang->name); return -1; } filter = i_new(struct fts_filter, 1); *filter = *fts_filter_contractions; filter->token = str_new(default_pool, 64); *filter_r = filter; return 0; } static int fts_filter_contractions_filter(struct fts_filter *filter ATTR_UNUSED, const char **_token, const char **error_r ATTR_UNUSED) { int char_size, pos = 0; unichar_t apostrophe; const char *token = *_token; switch (token[pos]) { case 'q': pos++; if (token[pos] == '\0' || token[pos] != 'u') break; /* fall through */ case 'c': case 'd': case 'j': case 'l': case 'm': case 'n': case 's': case 't': pos++; if (token[pos] == '\0') break; char_size = uni_utf8_get_char(token + pos, &apostrophe); if (IS_APOSTROPHE(apostrophe)) { pos += char_size; *_token = token + pos; } if (token[pos] == '\0') /* nothing left */ return 0; break; default: /* do nothing */ break; } return 1; } static const struct fts_filter fts_filter_contractions_real = { .class_name = "contractions", .v = { fts_filter_contractions_create, fts_filter_contractions_filter, NULL } }; const struct fts_filter *fts_filter_contractions = &fts_filter_contractions_real; dovecot-2.2.33.2/src/lib-fts/fts-filter.h0000644000175000017500000000451513123174404014710 00000000000000#ifndef FTS_FILTER_H #define FTS_FILTER_H struct fts_language; struct fts_filter; /* Settings are given in the form of a const char * const *settings = {"key, "value", "key2", "value2", NULL} array of string pairs. The array has to be NULL terminated. */ /* Settings: "stopwords_dir", path to the directory containing stopword files. Stopword files are looked up in ""/stopwords_.txt */ extern const struct fts_filter *fts_filter_stopwords; /* Settings: "lang", language of the stemmed language. */ extern const struct fts_filter *fts_filter_stemmer_snowball; /* Settings: "id", description of the normalizing/translitterating rules to use. See http://userguide.icu-project.org/transforms/general#TOC-Transliterator-Identifiers for syntax. Defaults to "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC" "maxlen", maximum length of tokens that ICU normalizer will output. Defaults to 250. */ extern const struct fts_filter *fts_filter_normalizer_icu; /* Lowecases the input. Supports UTF8, if libicu is available. */ extern const struct fts_filter *fts_filter_lowercase; /* Removes <'s> suffix from words. */ extern const struct fts_filter *fts_filter_english_possessive; /* Removes prefixing contractions from words. */ extern const struct fts_filter *fts_filter_contractions; /* Register all built-in filters. */ void fts_filters_init(void); void fts_filters_deinit(void); /* Register a new class explicitly. Built-in classes are automatically registered. */ void fts_filter_register(const struct fts_filter *filter_class); /* Filtering workflow, find --> create --> filter --> destroy. */ const struct fts_filter *fts_filter_find(const char *name); int fts_filter_create(const struct fts_filter *filter_class, struct fts_filter *parent, const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r); void fts_filter_ref(struct fts_filter *filter); void fts_filter_unref(struct fts_filter **filter); /* Returns 1 if token is returned in *token, 0 if token was filtered out (*token is also set to NULL) and -1 on error. Input is also given via *token. */ int fts_filter_filter(struct fts_filter *filter, const char **token, const char **error_r); #endif dovecot-2.2.33.2/src/lib-fts/fts-filter-normalizer-icu.c0000644000175000017500000000771013165463624017654 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "unichar.h" /* unicode replacement char */ #include "fts-filter-common.h" #include "fts-filter-private.h" #include "fts-language.h" #ifdef HAVE_LIBICU #include "fts-icu.h" struct fts_filter_normalizer_icu { struct fts_filter filter; pool_t pool; const char *transliterator_id; UTransliterator *transliterator; buffer_t *utf16_token, *trans_token; string_t *utf8_token; }; static void fts_filter_normalizer_icu_destroy(struct fts_filter *filter) { struct fts_filter_normalizer_icu *np = (struct fts_filter_normalizer_icu *)filter; if (np->transliterator != NULL) utrans_close(np->transliterator); pool_unref(&np->pool); } static int fts_filter_normalizer_icu_create(const struct fts_language *lang ATTR_UNUSED, const char *const *settings, struct fts_filter **filter_r, const char **error_r) { struct fts_filter_normalizer_icu *np; pool_t pp; unsigned int i, max_length = 250; const char *id = "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC; [\\x20] Remove"; for (i = 0; settings[i] != NULL; i += 2) { const char *key = settings[i], *value = settings[i+1]; if (strcmp(key, "id") == 0) { id = value; } else if (strcmp(key, "maxlen") == 0) { if (str_to_uint(value, &max_length) < 0 || max_length == 0) { *error_r = t_strdup_printf("Invalid icu maxlen setting: %s", value); return -1; } } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } } pp = pool_alloconly_create(MEMPOOL_GROWING"fts_filter_normalizer_icu", sizeof(struct fts_filter_normalizer_icu)); np = p_new(pp, struct fts_filter_normalizer_icu, 1); np->pool = pp; np->filter = *fts_filter_normalizer_icu; np->transliterator_id = p_strdup(pp, id); np->utf16_token = buffer_create_dynamic(pp, 128); np->trans_token = buffer_create_dynamic(pp, 128); np->utf8_token = buffer_create_dynamic(pp, 128); np->filter.max_length = max_length; *filter_r = &np->filter; return 0; } static int fts_filter_normalizer_icu_filter(struct fts_filter *filter, const char **token, const char **error_r) { struct fts_filter_normalizer_icu *np = (struct fts_filter_normalizer_icu *)filter; if (np->transliterator == NULL) if (fts_icu_transliterator_create(np->transliterator_id, &np->transliterator, error_r) < 0) return -1; fts_icu_utf8_to_utf16(np->utf16_token, *token); buffer_append_zero(np->utf16_token, 2); buffer_set_used_size(np->utf16_token, np->utf16_token->used-2); buffer_set_used_size(np->trans_token, 0); if (fts_icu_translate(np->trans_token, np->utf16_token->data, np->utf16_token->used / sizeof(UChar), np->transliterator, error_r) < 0) return -1; if (np->trans_token->used == 0) return 0; fts_icu_utf16_to_utf8(np->utf8_token, np->trans_token->data, np->trans_token->used / sizeof(UChar)); fts_filter_truncate_token(np->utf8_token, np->filter.max_length); *token = str_c(np->utf8_token); return 1; } #else static int fts_filter_normalizer_icu_create(const struct fts_language *lang ATTR_UNUSED, const char *const *settings ATTR_UNUSED, struct fts_filter **filter_r ATTR_UNUSED, const char **error_r) { *error_r = "libicu support not built in"; return -1; } static int fts_filter_normalizer_icu_filter(struct fts_filter *filter ATTR_UNUSED, const char **token ATTR_UNUSED, const char **error_r ATTR_UNUSED) { return -1; } static void fts_filter_normalizer_icu_destroy(struct fts_filter *normalizer ATTR_UNUSED) { } #endif static const struct fts_filter fts_filter_normalizer_icu_real = { .class_name = "normalizer-icu", .v = { fts_filter_normalizer_icu_create, fts_filter_normalizer_icu_filter, fts_filter_normalizer_icu_destroy } }; const struct fts_filter *fts_filter_normalizer_icu = &fts_filter_normalizer_icu_real; dovecot-2.2.33.2/src/lib-fts/fts-library.c0000644000175000017500000000062713123174404015062 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fts-language.h" #include "fts-tokenizer.h" #include "fts-filter.h" #include "fts-library.h" void fts_library_init(void) { fts_languages_init(); fts_tokenizers_init(); fts_filters_init(); } void fts_library_deinit(void) { fts_languages_deinit(); fts_tokenizers_deinit(); fts_filters_deinit(); } dovecot-2.2.33.2/src/lib-fts/fts-tokenizer.h0000644000175000017500000000724613165463624015454 00000000000000#ifndef FTS_TOKENIZER_H #define FTS_TOKENIZER_H /* Settings are given in the form of a const char * const *settings = {"key, "value", "key2", "value2", NULL} array of string pairs. Some keys, like "no_parent" and "search" are a sort of boolean and the value does not matter, just mentioning the key enables the functionality. The array has to be NULL terminated. */ /* Email address header tokenizer that returns "user@domain.org" input as "user@domain.org" token as well as passing it through to the parent (generic) tokenizer, which also returns "user", "domain" and "org". This allows searching the mails with their individual components, but also allows doing an explicit "user@domain" search, which returns only mails matching that exact address (instead of e.g. a mail with both user@domain2 and user2@domain words). */ /* Settings: "no_parent", Return only our tokens, no data for parent to process. Defaults to disabled. Should normally not be needed. "search" Remove addresses from parent data stream, so they are not processed further. Defaults to disabled. Enable by defining the keyword (and any value). */ extern const struct fts_tokenizer *fts_tokenizer_email_address; /* Generic email content tokenizer. Cuts text into tokens. */ /* Settings: "maxlen" Maximum length of token, before an arbitary cut off is made. Defaults to FTS_DEFAULT_TOKEN_MAX_LENGTH. "algorithm", accepted values are "simple" or "tr29". Defines the method for looking for word boundaries. Simple is faster and will work for many texts, especially those using latin alphabets, but leaves corner cases. The tr29 implements a version of Unicode technical report 29 word boundary lookup. It might work better with e.g. texts containing Katakana or hebrew characters, but it is not possible to use a single algorithm for all existing languages. It is also significantly slower than simple. The algorithms also differ in some details, e.g. simple will cut "a.b" and tr29 will not. The default is "simple" */ extern const struct fts_tokenizer *fts_tokenizer_generic; /* Tokenizing workflow, find --> create --> filter --> destroy. Do init before first use and deinit after all done. */ /* Register all built-in tokenizers. */ void fts_tokenizers_init(void); void fts_tokenizers_deinit(void); const struct fts_tokenizer *fts_tokenizer_find(const char *name); /* Create a new tokenizer. The settings are described above. */ int fts_tokenizer_create(const struct fts_tokenizer *tok_class, struct fts_tokenizer *parent, const char *const *settings, struct fts_tokenizer **tokenizer_r, const char **error_r); void fts_tokenizer_ref(struct fts_tokenizer *tok); void fts_tokenizer_unref(struct fts_tokenizer **tok); /* Reset FTS tokenizer state */ void fts_tokenizer_reset(struct fts_tokenizer *tok); /* Returns 1 if *token_r was returned, 0 if more data is needed, -1 on error. This function should be called with the same data+size until it returns 0. After that fts_tokenizer_final() should be called until it returns 0 to flush out the final token(s). data must contain only valid complete UTF-8 sequences, but otherwise it may be broken into however small pieces. (Input to this function typically comes from message-decoder, which returns only complete UTF-8 sequences.) */ int fts_tokenizer_next(struct fts_tokenizer *tok, const unsigned char *data, size_t size, const char **token_r, const char **error_r); /* Returns same as fts_tokenizer_next(). */ int fts_tokenizer_final(struct fts_tokenizer *tok, const char **token_r, const char **error_r); const char *fts_tokenizer_name(const struct fts_tokenizer *tok); #endif dovecot-2.2.33.2/src/lib-fts/fts-common.h0000644000175000017500000000411713123174404014711 00000000000000#ifndef FTS_COMMON_H #define FTS_COMMON_H /* Some might consider 0x02BB an apostrophe also. */ #define IS_NONASCII_APOSTROPHE(c) \ ((c) == 0x2019 || (c) == 0xFF07) #define IS_APOSTROPHE(c) \ ((c) == 0x0027 || IS_NONASCII_APOSTROPHE(c)) #define IS_WB5A_APOSTROPHE(c) \ ((c) == 0x0027 || (c) == 0x2019) /* The h letters are included because it is an exception in French. A, E, H, I, O, U, Y, a, e, h, i, o, u, y */ #define IS_ASCII_VOWEL(c) \ ((c) == 0x0041 || (c) == 0x0045 || (c) == 0x0048 || (c) == 0x0049 || \ (c) == 0x004F || (c) == 0x0055 || (c) == 0x0059 || (c) == 0x0061 || \ (c) == 0x0065 || (c) == 0x0068 || (c) == 0x0069 || (c) == 0x006F || \ (c) == 0x0075 || (c) == 0x0079) #define IS_NONASCII_VOWEL(c) \ /*latin capital letter a with grave, acute and circumflex*/ \ ((c) == 0x00C0 || (c) == 0x00C1 || (c) == 0x00C2 || \ /* latin capital letter e with grave, acute and circumflex */ \ (c) == 0x00C8 || (c) == 0x00C9 || (c) == 0x00CA || \ /* latin capital letter i with grave, acute and circumflex */ \ (c) == 0x00CC || (c) == 0x00CD || (c) == 0x00CE || \ /* latin capital letter o with grave, acute and circumflex */ \ (c) == 0x00D2 || (c) == 0x00D3 || (c) == 0x00D4 || \ /* latin capital letter u with grave, acute and circumflex */ \ (c) == 0x00D9 || (c) == 0x00DA || (c) == 0x00DB || \ /* latin capital letter y with acute */ \ (c) == 0x00DD || \ /* latin small letter a with grave, acute and circumflex */ \ (c) == 0x00E0 || (c) == 0x00E1 || (c) == 0x00E2 || \ /* latin small letter e with grave, acute and circumflex */ \ (c) == 0x00E8 || (c) == 0x00E9 || (c) == 0x00EA || \ /* latin small letter i with grave, acute and circumflex */ \ (c) == 0x00EC || (c) == 0x00ED || (c) == 0x00EE || \ /* latin small letter o with grave, acute and circumflex */ \ (c) == 0x00F2 || (c) == 0x00F3 || (c) == 0x00F4 || \ /* latin small letter u with grave, acute and circumflex */ \ (c) == 0x00F9 || (c) == 0x00FA || (c) == 0x00FB || \ /* latin small letter y with acute */ \ (c) == 0x00FD ) #define IS_VOWEL(c) \ (IS_ASCII_VOWEL(c) || IS_NONASCII_VOWEL(c)) #endif dovecot-2.2.33.2/src/lib-fts/udhr_fra.txt0000644000175000017500000003233613123174404015015 00000000000000Universal Declaration of Human Rights - French © 1996 – 2009 The Office of the High Commissioner for Human Rights This plain text version prepared by the “UDHR in Unicode” project, http://www.unicode.org/udhr. --- Déclaration universelle des droits de l’homme Préambule Considérant que la reconnaissance de la dignité inhérente à tous les membres de la famille humaine et de leurs droits égaux et inaliénables constitue le fondement de la liberté, de la justice et de la paix dans le monde, Considérant que la méconnaissance et le mépris des droits de l’homme ont conduit à des actes de barbarie qui révoltent la conscience de l’humanité et que l’avènement d’un monde où les êtres humains seront libres de parler et de croire, libérés de la terreur et de la misère, a été proclamé comme la plus haute aspiration de l’homme, Considérant qu’il est essentiel que les droits de l’homme soient protégés par un régime de droit pour que l’homme ne soit pas contraint, en suprême recours, à la révolte contre la tyrannie et l’oppression, Considérant qu’il est essentiel d’encourager le développement de relations amicales entre nations, Considérant que dans la Charte les peuples des Nations Unies ont proclamé à nouveau leur foi dans les droits fondamentaux de l’homme, dans la dignité et la valeur de la personne humaine, dans l’égalité des droits des hommes et des femmes, et qu’ils se sont déclarés résolus à favoriser le progrès social et à instaurer de meilleures conditions de vie dans une liberté plus grande, Considérant que les États Membres se sont engagés à assurer, en coopération avec l’Organisation des Nations Unies, le respect universel et effectif des droits de l’homme et des libertés fondamentales, Considérant qu’une conception commune de ces droits et libertés est de la plus haute importance pour remplir pleinement cet engagement, L’Assemblée générale Proclame la présente Déclaration universelle des droits de l’homme comme l’idéal commun à atteindre par tous les peuples et toutes les nations afin que tous les individus et tous les organes de la société, ayant cette Déclaration constamment à l’esprit, s’efforcent, par l’enseignement et l’éducation, de développer le respect de ces droits et libertés et d’en assurer, par des mesures progressives d’ordre national et international, la reconnaissance et l’application universelles et effectives, tant parmi les populations des États Membres eux‐mêmes que parmi celles des territoires placés sous leur juridiction. Article premier Tous les êtres humains naissent libres et égaux en dignité et en droits. Ils sont doués de raison et de conscience et doivent agir les uns envers les autres dans un esprit de fraternité. Article 2 Chacun peut se prévaloir de tous les droits et de toutes les libertés proclamés dans la présente Déclaration, sans distinction aucune, notamment de race, de couleur, de sexe, de langue, de religion, d’opinion politique ou de toute autre opinion, d’origine nationale ou sociale, de fortune, de naissance ou de toute autre situation. De plus, il ne sera fait aucune distinction fondée sur le statut politique, juridique ou international du pays ou du territoire dont une personne est ressortissante, que ce pays ou territoire soit indépendant, sous tutelle, non autonome ou soumis à une limitation quelconque de souveraineté. Article 3 Tout individu a droit à la vie, à la liberté et à la sûreté de sa personne. Article 4 Nul ne sera tenu en esclavage ni en servitude ; l’esclavage et la traite des esclaves sont interdits sous toutes leurs formes. Article 5 Nul ne sera soumis à la torture, ni à des peines ou traitements cruels, inhumains ou dégradants. Article 6 Chacun a le droit à la reconnaissance en tous lieux de sa personnalité juridique. Article 7 Tous sont égaux devant la loi et ont droit sans distinction à une égale protection de la loi. Tous ont droit à une protection égale contre toute discrimination qui violerait la présente Déclaration et contre toute provocation à une telle discrimination. Article 8 Toute personne a droit à un recours effectif devant les juridictions nationales compétentes contre les actes violant les droits fondamentaux qui lui sont reconnus par la constitution ou par la loi. Article 9 Nul ne peut être arbitrairement arrêté, détenu ni exilé. Article 10 Toute personne a droit, en pleine égalité, à ce que sa cause soit entendue équitablement et publiquement par un tribunal indépendant et impartial, qui décidera, soit de ses droits et obligations, soit du bien fondé de toute accusation en matière pénale dirigée contre elle. Article 11 Toute personne accusée d’un acte délictueux est présumée innocente jusqu’à ce que sa culpabilité ait été légalement établie au cours d’un procès public où toutes les garanties nécessaires à sa défense lui auront été assurées. Nul ne sera condamné pour des actions ou omissions qui, au moment où elles ont été commises, ne constituaient pas un acte délictueux d’après le droit national ou international. De même, il ne sera infligé aucune peine plus forte que celle qui était applicable au moment où l’acte délictueux a été commis. Article 12 Nul ne sera l’objet d’immixtions arbitraires dans sa vie privée, sa famille, son domicile ou sa correspondance, ni d’atteintes à son honneur et à sa réputation. Toute personne a droit à la protection de la loi contre de telles immixtions ou de telles atteintes. Article 13 Toute personne a le droit de circuler librement et de choisir sa résidence à l’intérieur d’un État. Toute personne a le droit de quitter tout pays, y compris le sien, et de revenir dans son pays. Article 14 Devant la persécution, toute personne a le droit de chercher asile et de bénéficier de l’asile en d’autres pays. Ce droit ne peut être invoqué dans le cas de poursuites réellement fondées sur un crime de droit commun ou sur des agissements contraires aux buts et aux principes des Nations Unies. Article 15 Tout individu a droit à une nationalité. Nul ne peut être arbitrairement privé de sa nationalité, ni du droit de changer de nationalité. Article 16 A partir de l’âge nubile, l’homme et la femme, sans aucune restriction quant à la race, la nationalité ou la religion, ont le droit de se marier et de fonder une famille. Ils ont des droits égaux au regard du mariage, durant le mariage et lors de sa dissolution. Le mariage ne peut être conclu qu’avec le libre et plein consentement des futurs époux. La famille est l’élément naturel et fondamental de la société et a droit à la protection de la société et de l’État. Article 17 Toute personne, aussi bien seule qu’en collectivité, a droit à la propriété. Nul ne peut être arbitrairement privé de sa propriété. Article 18 Toute personne a droit à la liberté de pensée, de conscience et de religion ; ce droit implique la liberté de changer de religion ou de conviction ainsi que la liberté de manifester sa religion ou sa conviction, seule ou en commun, tant en public qu’en privé, par l’enseignement, les pratiques, le culte et l’accomplissement des rites. Article 19 Tout individu a droit à la liberté d’opinion et d’expression, ce qui implique le droit de ne pas être inquiété pour ses opinions et celui de chercher, de recevoir et de répandre, sans considérations de frontières, les informations et les idées par quelque moyen d’expression que ce soit. Article 20 Toute personne a droit à la liberté de réunion et d’association pacifiques. Nul ne peut être obligé de faire partie d’une association. Article 21 Toute personne a le droit de prendre part à la direction des affaires publiques de son pays, soit directement, soit par l’intermédiaire de représentants librement choisis. Toute personne a droit à accéder, dans des conditions d’égalité, aux fonctions publiques de son pays. La volonté du peuple est le fondement de l’autorité des pouvoirs publics ; cette volonté doit s’exprimer par des élections honnêtes qui doivent avoir lieu périodiquement, au suffrage universel égal et au vote secret ou suivant une procédure équivalente assurant la liberté du vote. Article 22 Toute personne, en tant que membre de la société, a droit à la sécurité sociale ; elle est fondée à obtenir la satisfaction des droits économiques, sociaux et culturels indispensables à sa dignité et au libre développement de sa personnalité, grâce à l’effort national et à la coopération internationale, compte tenu de l’organisation et des ressources de chaque pays. Article 23 Toute personne a droit au travail, au libre choix de son travail, à des conditions équitables et satisfaisantes de travail et à la protection contre le chômage. Tous ont droit, sans aucune discrimination, à un salaire égal pour un travail égal. Quiconque travaille a droit à une rémunération équitable et satisfaisante lui assurant ainsi qu’à sa famille une existence conforme à la dignité humaine et complétée, s’il y a lieu, par tous autres moyens de protection sociale. Toute personne a le droit de fonder avec d’autres des syndicats et de s’affilier à des syndicats pour la défense de ses intérêts. Article 24 Toute personne a droit au repos et aux loisirs et notamment à une limitation raisonnable de la durée du travail et à des congés payés périodiques. Article 25 Toute personne a droit à un niveau de vie suffisant pour assurer sa santé, son bien‐être et ceux de sa famille, notamment pour l’alimentation, l’habillement, le logement, les soins médicaux ainsi que pour les services sociaux nécessaires ; elle a droit à la sécurité en cas de chômage, de maladie, d’invalidité, de veuvage, de vieillesse ou dans les autres cas de perte de ses moyens de subsistance par suite de circonstances indépendantes de sa volonté. La maternité et l’enfance ont droit à une aide et à une assistance spéciales. Tous les enfants, qu’ils soient nés dans le mariage ou hors mariage, jouissent de la même protection sociale. Article 26 Toute personne a droit à l’éducation. L’éducation doit être gratuite, au moins en ce qui concerne l’enseignement élémentaire et fondamental. L’enseignement élémentaire est obligatoire. L’enseignement technique et professionnel doit être généralisé ; l’accès aux études supérieures doit être ouvert en pleine égalité à tous en fonction de leur mérite. L’éducation doit viser au plein épanouissement de la personnalité humaine et au renforcement du respect des droits de l’homme et des libertés fondamentales. Elle doit favoriser la compréhension, la tolérance et l’amitié entre toutes les nations et tous les groupes raciaux ou religieux, ainsi que le développement des activités des Nations Unies pour le maintien de la paix. Les parents ont, par priorité, le droit de choisir le genre d’éducation à donner à leurs enfants. Article 27 Toute personne a le droit de prendre part librement à la vie culturelle de la communauté, de jouir des arts et de participer au progrès scientifique et aux bienfaits qui en résultent. Chacun a droit à la protection des intérêts moraux et matériels découlant de toute production scientifique, littéraire ou artistique dont il est l’auteur. Article 28 Toute personne a droit à ce que règne, sur le plan social et sur le plan international, un ordre tel que les droits et libertés énoncés dans la présente Déclaration puissent y trouver plein effet. Article 29 L’individu a des devoirs envers la communauté dans laquelle seul le libre et plein développement de sa personnalité est possible. Dans l’exercice de ses droits et dans la jouissance de ses libertés, chacun n’est soumis qu’aux limitations établies par la loi exclusivement en vue d’assurer la reconnaissance et le respect des droits et libertés d’autrui et afin de satisfaire aux justes exigences de la morale, de l’ordre public et du bien‐être général dans une société démocratique. Ces droits et libertés ne pourront, en aucun cas, s’exercer contrairement aux buts et aux principes des Nations Unies. Article 30 Aucune disposition de la présente Déclaration ne peut être interprétée comme impliquant pour un État, un groupement ou un individu un droit quelconque de se livrer à une activité ou d’accomplir un acte visant à la destruction des droits et libertés qui y sont énoncés. dovecot-2.2.33.2/src/lib-fts/fts-filter-common.c0000644000175000017500000000100313123174404016156 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "buffer.h" #include "unichar.h" #include "fts-filter-private.h" #include "fts-filter-common.h" #include "fts-tokenizer-common.h" void fts_filter_truncate_token(string_t *token, size_t max_length) { if (str_len(token) <= max_length) return; size_t len = max_length; fts_tokenizer_delete_trailing_partial_char(token->data, &len); str_truncate(token, len); i_assert(len <= max_length); } dovecot-2.2.33.2/src/lib-fts/fts-icu.c0000644000175000017500000001413213165463624014205 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mempool.h" #include "buffer.h" #include "str.h" #include "unichar.h" #include "fts-icu.h" #include #include #include static struct UCaseMap *icu_csm = NULL; static struct UCaseMap *fts_icu_csm(void) { UErrorCode err = U_ZERO_ERROR; if (icu_csm != NULL) return icu_csm; icu_csm = ucasemap_open(NULL, U_FOLD_CASE_DEFAULT, &err); if (U_FAILURE(err)) { i_fatal("LibICU ucasemap_open() failed: %s", u_errorName(err)); } return icu_csm; } void fts_icu_utf8_to_utf16(buffer_t *dest_utf16, const char *src_utf8) { UErrorCode err = U_ZERO_ERROR; size_t src_bytes = strlen(src_utf8); int32_t utf16_len; UChar *dest_data, *retp = NULL; int32_t avail_uchars = 0; /* try to encode with the current buffer size */ avail_uchars = buffer_get_writable_size(dest_utf16) / sizeof(UChar); dest_data = buffer_get_space_unsafe(dest_utf16, 0, buffer_get_writable_size(dest_utf16)); retp = u_strFromUTF8Lenient(dest_data, avail_uchars, &utf16_len, src_utf8, src_bytes, &err); if (err == U_BUFFER_OVERFLOW_ERROR) { /* try again with a larger buffer */ dest_data = buffer_get_space_unsafe(dest_utf16, 0, utf16_len * sizeof(UChar)); err = U_ZERO_ERROR; retp = u_strFromUTF8Lenient(dest_data, utf16_len, &utf16_len, src_utf8, src_bytes, &err); } if (U_FAILURE(err)) { i_panic("LibICU u_strFromUTF8Lenient() failed: %s", u_errorName(err)); } buffer_set_used_size(dest_utf16, utf16_len * sizeof(UChar)); i_assert(retp == dest_data); } void fts_icu_utf16_to_utf8(string_t *dest_utf8, const UChar *src_utf16, unsigned int src_len) { int32_t dest_len = 0; int32_t sub_num = 0; char *dest_data, *retp = NULL; UErrorCode err = U_ZERO_ERROR; /* try to encode with the current buffer size */ dest_data = buffer_get_space_unsafe(dest_utf8, 0, buffer_get_writable_size(dest_utf8)); retp = u_strToUTF8WithSub(dest_data, buffer_get_writable_size(dest_utf8), &dest_len, src_utf16, src_len, UNICODE_REPLACEMENT_CHAR, &sub_num, &err); if (err == U_BUFFER_OVERFLOW_ERROR) { /* try again with a larger buffer */ dest_data = buffer_get_space_unsafe(dest_utf8, 0, dest_len); err = U_ZERO_ERROR; retp = u_strToUTF8WithSub(dest_data, buffer_get_writable_size(dest_utf8), &dest_len, src_utf16, src_len, UNICODE_REPLACEMENT_CHAR, &sub_num, &err); } if (U_FAILURE(err)) { i_panic("LibICU u_strToUTF8WithSub() failed: %s", u_errorName(err)); } buffer_set_used_size(dest_utf8, dest_len); i_assert(retp == dest_data); } int fts_icu_translate(buffer_t *dest_utf16, const UChar *src_utf16, unsigned int src_len, UTransliterator *transliterator, const char **error_r) { UErrorCode err = U_ZERO_ERROR; int32_t utf16_len = src_len; UChar *dest_data; int32_t avail_uchars, limit = src_len; size_t dest_pos = dest_utf16->used; /* translation is done in-place in the buffer. try first with the current buffer size. */ buffer_append(dest_utf16, src_utf16, src_len*sizeof(UChar)); avail_uchars = (buffer_get_writable_size(dest_utf16)-dest_pos) / sizeof(UChar); dest_data = buffer_get_space_unsafe(dest_utf16, dest_pos, buffer_get_writable_size(dest_utf16)-dest_pos); utrans_transUChars(transliterator, dest_data, &utf16_len, avail_uchars, 0, &limit, &err); if (err == U_BUFFER_OVERFLOW_ERROR) { /* try again with a larger buffer */ err = U_ZERO_ERROR; avail_uchars = utf16_len; limit = utf16_len = src_len; buffer_write(dest_utf16, dest_pos, src_utf16, src_len*sizeof(UChar)); dest_data = buffer_get_space_unsafe(dest_utf16, dest_pos, avail_uchars * sizeof(UChar)); utrans_transUChars(transliterator, dest_data, &utf16_len, avail_uchars, 0, &limit, &err); i_assert(err != U_BUFFER_OVERFLOW_ERROR); } if (U_FAILURE(err)) { *error_r = t_strdup_printf("LibICU utrans_transUChars() failed: %s", u_errorName(err)); buffer_set_used_size(dest_utf16, dest_pos); return -1; } buffer_set_used_size(dest_utf16, utf16_len * sizeof(UChar)); return 0; } void fts_icu_lcase(string_t *dest_utf8, const char *src_utf8) { struct UCaseMap *csm = fts_icu_csm(); size_t avail_bytes, dest_pos = dest_utf8->used; char *dest_data; int dest_full_len; UErrorCode err = U_ZERO_ERROR; avail_bytes = buffer_get_writable_size(dest_utf8) - dest_pos; dest_data = buffer_get_space_unsafe(dest_utf8, dest_pos, avail_bytes); /* ucasemap_utf8ToLower() may need to be called multiple times, because the first return value may not be large enough. */ for (unsigned int i = 0;; i++) { dest_full_len = ucasemap_utf8ToLower(csm, dest_data, avail_bytes, src_utf8, -1, &err); if (err != U_BUFFER_OVERFLOW_ERROR || i == 2) break; err = U_ZERO_ERROR; dest_data = buffer_get_space_unsafe(dest_utf8, dest_pos, dest_full_len); avail_bytes = dest_full_len; } if (U_FAILURE(err)) { i_fatal("LibICU ucasemap_utf8ToLower() failed: %s", u_errorName(err)); } buffer_set_used_size(dest_utf8, dest_full_len); } void fts_icu_deinit(void) { if (icu_csm != NULL) { ucasemap_close(icu_csm); icu_csm = NULL; } u_cleanup(); } int fts_icu_transliterator_create(const char *id, UTransliterator **transliterator_r, const char **error_r) { UErrorCode err = U_ZERO_ERROR; UParseError perr; buffer_t *id_utf16_buf = buffer_create_dynamic(pool_datastack_create(), 2 * strlen(id)); UChar *id_utf16; i_zero(&perr); fts_icu_utf8_to_utf16(id_utf16_buf, id); id_utf16 = (UChar *)str_c(id_utf16_buf); *transliterator_r = utrans_openU(id_utf16, id_utf16_buf->used / sizeof(UChar), UTRANS_FORWARD, NULL, 0, &perr, &err); if (U_FAILURE(err)) { string_t *str = t_str_new(128); str_printfa(str, "Failed to open transliterator for id '%s': %s", id, u_errorName(err)); if (perr.line >= 1) { /* we have only one line in our ID */ str_printfa(str, " (parse error on offset %u)", perr.offset); } *error_r = str_c(str); return -1; } return 0; } dovecot-2.2.33.2/src/lib-fts/fts-filter-private.h0000644000175000017500000000166513123174404016363 00000000000000#ifndef FTS_FILTER_PRIVATE_H #define FTS_FILTER_PRIVATE_H #include "fts-filter.h" #define FTS_FILTER_CLASSES_NR 6 /* API that stemming providers (classes) must provide: The create() function is called to get an instance of a registered filter class. The filter() function is called with tokens for the specific filter. The destroy function is called to destroy an instance of a filter. */ struct fts_filter_vfuncs { int (*create)(const struct fts_language *lang, const char *const *settings, struct fts_filter **filter_r, const char **error_r); int (*filter)(struct fts_filter *filter, const char **token, const char **error_r); void (*destroy)(struct fts_filter *filter); }; struct fts_filter { const char *class_name; /* name of the class this is based on */ struct fts_filter_vfuncs v; struct fts_filter *parent; string_t *token; size_t max_length; int refcount; }; #endif dovecot-2.2.33.2/src/lib-fts/test-fts-icu.c0000644000175000017500000001164213165463624015165 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "unichar.h" #include "test-common.h" #include "fts-icu.h" #include static void test_fts_icu_utf8_to_utf16_ascii_resize(void) { buffer_t *dest = buffer_create_dynamic(pool_datastack_create(), 4); test_begin("fts_icu_utf8_to_utf16 ascii resize"); test_assert(buffer_get_writable_size(dest) == 4); fts_icu_utf8_to_utf16(dest, "12"); test_assert(dest->used == 4); test_assert(buffer_get_writable_size(dest) == 4); fts_icu_utf8_to_utf16(dest, "123"); test_assert(dest->used == 6); test_assert(buffer_get_writable_size(dest) == 7); fts_icu_utf8_to_utf16(dest, "12345"); test_assert(dest->used == 10); test_end(); } static void test_fts_icu_utf8_to_utf16_32bit_resize(void) { buffer_t *dest; unsigned int i; test_begin("fts_icu_utf8_to_utf16 32bit resize"); for (i = 2; i <= 5; i++) { dest = buffer_create_dynamic(pool_datastack_create(), i); test_assert(buffer_get_writable_size(dest) == i); fts_icu_utf8_to_utf16(dest, "\xF0\x90\x90\x80"); /* 0x10400 */ test_assert(dest->used == 4); } test_end(); } static void test_fts_icu_utf16_to_utf8(void) { string_t *dest = t_str_new(64); const UChar src[] = { 0xbd, 'b', 'c' }; unsigned int i; test_begin("fts_icu_utf16_to_utf8"); for (i = N_ELEMENTS(src); i > 0; i--) { fts_icu_utf16_to_utf8(dest, src, i); test_assert(dest->used == i+1); } test_end(); } static void test_fts_icu_utf16_to_utf8_resize(void) { string_t *dest; const UChar src = UNICODE_REPLACEMENT_CHAR; unsigned int i; test_begin("fts_icu_utf16_to_utf8 resize"); for (i = 2; i <= 6; i++) { dest = t_str_new(i); test_assert(buffer_get_writable_size(dest) == i); fts_icu_utf16_to_utf8(dest, &src, 1); test_assert(dest->used == 3); test_assert(strcmp(str_c(dest), UNICODE_REPLACEMENT_CHAR_UTF8) == 0); } test_end(); } static UTransliterator *get_translit(const char *id) { UTransliterator *translit; buffer_t *id_utf16; UErrorCode err = U_ZERO_ERROR; UParseError perr; id_utf16 = buffer_create_dynamic(pool_datastack_create(), 16); fts_icu_utf8_to_utf16(id_utf16, id); translit = utrans_openU(id_utf16->data, id_utf16->used/sizeof(UChar), UTRANS_FORWARD, NULL, 0, &perr, &err); test_assert(!U_FAILURE(err)); return translit; } static void test_fts_icu_translate(void) { const char *translit_id = "Any-Lower"; UTransliterator *translit; buffer_t *dest = buffer_create_dynamic(pool_datastack_create(), 64); const UChar src[] = { 0xbd, 'B', 'C' }; const char *error; unsigned int i; test_begin("fts_icu_translate"); translit = get_translit(translit_id); for (i = N_ELEMENTS(src); i > 0; i--) { buffer_set_used_size(dest, 0); test_assert(fts_icu_translate(dest, src, i, translit, &error) == 0); test_assert(dest->used == i * sizeof(UChar)); } utrans_close(translit); test_end(); } static void test_fts_icu_translate_resize(void) { const char *translit_id = "Any-Hex"; const char *src_utf8 = "FOO"; buffer_t *dest, *src_utf16; UTransliterator *translit; const char *error; unsigned int i; test_begin("fts_icu_translate_resize resize"); src_utf16 = buffer_create_dynamic(pool_datastack_create(), 16); translit = get_translit(translit_id); for (i = 2; i <= 20; i++) { buffer_set_used_size(src_utf16, 0); fts_icu_utf8_to_utf16(src_utf16, src_utf8); dest = buffer_create_dynamic(pool_datastack_create(), i); test_assert(buffer_get_writable_size(dest) == i); test_assert(fts_icu_translate(dest, src_utf16->data, src_utf16->used/sizeof(UChar), translit, &error) == 0); } utrans_close(translit); test_end(); } static void test_fts_icu_lcase(void) { const char *src = "aBcD\xC3\x84\xC3\xA4"; string_t *dest = t_str_new(64); test_begin("fts_icu_lcase"); fts_icu_lcase(dest, src); test_assert(strcmp(str_c(dest), "abcd\xC3\xA4\xC3\xA4") == 0); test_end(); } static void test_fts_icu_lcase_resize(void) { const char *src = "a\xC3\x84"; string_t *dest; unsigned int i; test_begin("fts_icu_lcase resize"); for (i = 1; i <= 3; i++) { dest = t_str_new(i); test_assert(buffer_get_writable_size(dest) == i); fts_icu_lcase(dest, src); test_assert(strcmp(str_c(dest), "a\xC3\xA4") == 0); test_assert(buffer_get_writable_size(dest) == 3); } test_end(); } static void test_fts_icu_lcase_resize_invalid_utf8(void) { string_t *dest; test_begin("fts_icu_lcase resize invalid utf8"); dest = t_str_new(1); fts_icu_lcase(dest, ".\x80."); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_fts_icu_utf8_to_utf16_ascii_resize, test_fts_icu_utf8_to_utf16_32bit_resize, test_fts_icu_utf16_to_utf8, test_fts_icu_utf16_to_utf8_resize, test_fts_icu_translate, test_fts_icu_translate_resize, test_fts_icu_lcase, test_fts_icu_lcase_resize, test_fts_icu_lcase_resize_invalid_utf8, NULL }; int ret = test_run(test_functions); fts_icu_deinit(); return ret; } dovecot-2.2.33.2/src/lib-fts/word-properties.pl0000644000175000017500000000214613123174404016160 00000000000000#!/usr/bin/env perl use strict; use warnings; my @categories; my $which = shift(@ARGV); if ($which eq 'boundaries') { @categories = qw(CR LF Newline Extend Regional_Indicator Format Katakana Hebrew_Letter ALetter Single_Quote Double_Quote MidNumLet MidLetter MidNum Numeric ExtendNumLet); } elsif ($which eq 'breaks') { @categories = qw(White_Space Dash Quotation_Mark Terminal_Punctuation STerm Pattern_White_Space); } else { die "specify 'boundaries' or 'breaks'"; } my $catregexp=join('|', @categories); my %catlists = map { $_ => []; } (@categories); while(<>) { next if (m/^#/ or m/^\s*$/); push(@{$catlists{$3}}, defined($2) ? (hex($1)..hex($2)) : hex($1)) if (m/([[:xdigit:]]+)(?:\.\.([[:xdigit:]]+))?\s+; ($catregexp) #/) } print "/* This file is automatically generated by word-properties.pl from $ARGV */\n"; foreach(@categories) { my $arref=$catlists{$_}; print "static const uint32_t ${_}[]= {\n"; while(scalar(@$arref)) { print("\t", join(", ", map { sprintf("0x%05X", $_); } splice(@$arref, 0, 8))); print(scalar(@$arref) ? ", \n" : "\n"); } print("};\n"); } dovecot-2.2.33.2/src/lib-fts/Makefile.am0000644000175000017500000001001213165463624014514 00000000000000noinst_LTLIBRARIES = libfts.la # I$(top_srcdir)/src/lib-fts needed to include # word-break-data.c and word-boundary-data.c # in fts-tokenizer-generic.c AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-fts \ $(LIBEXTTEXTCAT_CFLAGS) \ $(LIBICU_CFLAGS) \ -DUDHRDIR=\""$(top_srcdir)/src/lib-fts"\" \ -DDATADIR=\"$(pkgdatadir)\" \ -DTEST_STOPWORDS_DIR=\""$(top_srcdir)/src/lib-fts/stopwords"\" stopwordsdir = $(datadir)/${PACKAGE_TARNAME}/stopwords dist_stopwords_DATA = \ stopwords/stopwords_da.txt \ stopwords/stopwords_de.txt \ stopwords/stopwords_en.txt \ stopwords/stopwords_es.txt \ stopwords/stopwords_fi.txt \ stopwords/stopwords_fr.txt \ stopwords/stopwords_it.txt \ stopwords/stopwords_nl.txt \ stopwords/stopwords_no.txt \ stopwords/stopwords_pt.txt \ stopwords/stopwords_ro.txt \ stopwords/stopwords_ru.txt \ stopwords/stopwords_sv.txt BUILT_SOURCES = word-boundary-data.c word-break-data.c EXTRA_DIST = \ udhr_fra.txt \ PropList.txt \ word-properties.pl \ WordBreakProperty.txt \ word-boundary-data.c \ word-break-data.c \ stopwords/stopwords_malformed.txt WordBreakProperty.txt: test -f WordBreakProperty.txt || wget https://dovecot.org/res/WordBreakProperty.txt $(srcdir)/word-boundary-data.c: word-properties.pl WordBreakProperty.txt perl word-properties.pl boundaries WordBreakProperty.txt > $@ PropList.txt: test -f PropList.txt || wget https://dovecot.org/res/PropList.txt $(srcdir)/word-break-data.c: word-properties.pl PropList.txt perl word-properties.pl breaks PropList.txt > $@ if BUILD_FTS_STEMMER STEMMER_LIBS = -lstemmer endif if BUILD_FTS_EXTTEXTCAT TEXTCAT_LIBS = $(LIBEXTTEXTCAT_LIBS) else if BUILD_FTS_TEXTCAT TEXTCAT_LIBS = -ltextcat endif endif if BUILD_LIBICU ICU_SOURCES = fts-icu.c NORMALIZER_LIBS = $(LIBICU_LIBS) ICU_TESTS = test-fts-icu endif libfts_la_LIBADD = \ $(STEMMER_LIBS) \ $(TEXTCAT_LIBS) \ $(NORMALIZER_LIBS) libfts_la_SOURCES = \ fts-filter.c \ fts-filter-contractions.c \ fts-filter-common.c \ fts-filter-english-possessive.c \ fts-filter-lowercase.c \ fts-filter-normalizer-icu.c \ fts-filter-stopwords.c \ fts-filter-stemmer-snowball.c \ fts-language.c \ fts-library.c \ fts-tokenizer.c \ fts-tokenizer-address.c \ fts-tokenizer-common.c \ fts-tokenizer-generic.c \ $(ICU_SOURCES) headers = \ fts-common.h \ fts-filter.h \ fts-filter-common.h \ fts-filter-private.h \ fts-icu.h \ fts-language.h \ fts-library.h \ fts-tokenizer.h \ fts-tokenizer-common.h \ fts-tokenizer-private.h \ fts-tokenizer-generic-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) deps=../lib-dovecot/libdovecot.la pkglib_LTLIBRARIES = libdovecot-fts.la libdovecot_fts_la_SOURCES = libdovecot_fts_la_LIBADD = libfts.la $(deps) libdovecot_fts_la_DEPENDENCIES = libfts.la $(deps) libdovecot_fts_la_LDFLAGS = -export-dynamic test_programs = \ $(ICU_TESTS) \ $(TEST_FTS_LANGUAGE) \ test-fts-filter \ test-fts-tokenizer noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_fts_icu_SOURCES = test-fts-icu.c test_fts_icu_LDADD = fts-icu.lo $(LIBICU_LIBS) $(test_libs) test_fts_icu_DEPENDENCIES = fts-icu.lo $(test_deps) test_fts_filter_SOURCES = test-fts-filter.c test_fts_filter_LDADD = libfts.la $(test_libs) test_fts_filter_DEPENDENCIES = libfts.la $(test_deps) if BUILD_FTS_EXTTEXTCAT TEST_FTS_LANGUAGE = test-fts-language test_fts_language_SOURCES = test-fts-language.c test_fts_language_LDADD = fts-language.lo $(test_libs) $(TEXTCAT_LIBS) test_fts_language_DEPENDENCIES = $(test_deps) endif test_fts_tokenizer_SOURCES = test-fts-tokenizer.c test_fts_tokenizer_LDADD = fts-tokenizer.lo fts-tokenizer-generic.lo fts-tokenizer-address.lo fts-tokenizer-common.lo ../lib-mail/libmail.la $(test_libs) test_fts_tokenizer_DEPENDENCIES = ../lib-mail/libmail.la $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-http/0002755000175000017500000000000013172375610012726 500000000000000dovecot-2.2.33.2/src/lib-http/test-http-request-parser.c0000644000175000017500000003700613165463624017737 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "str.h" #include "str-sanitize.h" #include "istream.h" #include "ostream.h" #include "test-common.h" #include "http-url.h" #include "http-request-parser.h" #include /* * Test: valid requests */ struct http_request_valid_parse_test { const char *request; const char *method; const char *target_raw; struct { enum http_request_target_format format; struct http_url url; } target; unsigned char version_major; unsigned char version_minor; uoff_t content_length; const char *payload; bool connection_close; bool expect_100_continue; enum http_request_parse_flags flags; }; static const struct http_request_valid_parse_test valid_request_parse_tests[] = { { .request = "GET / HTTP/1.1\r\n" "Host: example.com\r\n" "\r\n", .method = "GET", .target_raw = "/", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN, .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 1, },{ .request = "OPTIONS * HTTP/1.0\r\n" "Host: example.com\r\n" "Connection: Keep-Alive\r\n" "\r\n", .method = "OPTIONS", .target_raw = "*", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ASTERISK, .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 0, },{ .request = "CONNECT example.com:443 HTTP/1.2\r\n" "Host: example.com:443\r\n" "\r\n", .method = "CONNECT", .target_raw = "example.com:443", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_AUTHORITY, .url = { .host_name = "example.com", .have_port = TRUE, .port = 443 } }, .version_major = 1, .version_minor = 2, },{ .request = "GET https://www.example.com:443 HTTP/1.1\r\n" "Host: www.example.com:80\r\n" "\r\n", .method = "GET", .target_raw = "https://www.example.com:443", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, .url = { .host_name = "www.example.com", .have_port = TRUE, .port = 443, .have_ssl = TRUE } }, .version_major = 1, .version_minor = 1, },{ .request = "POST http://api.example.com:8080/commit?user=dirk HTTP/1.1\r\n" "Host: api.example.com:8080\r\n" "Content-Length: 10\r\n" "\r\n" "Content!\r\n", .method = "POST", .target_raw = "http://api.example.com:8080/commit?user=dirk", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, .url = { .host_name = "api.example.com", .have_port = TRUE, .port = 8080 } }, .version_major = 1, .version_minor = 1, .payload = "Content!\r\n" },{ .request = "GET http://www.example.com/index.php?seq=1 HTTP/1.1\r\n" "Host: www.example.com\r\n" "Connection: close\r\n" "\r\n", .method = "GET", .target_raw = "http://www.example.com/index.php?seq=1", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, .url = { .host_name = "www.example.com" } }, .version_major = 1, .version_minor = 1, .connection_close = TRUE },{ .request = "GET http://www.example.com/index.html HTTP/1.0\r\n" "Host: www.example.com\r\n" "\r\n", .method = "GET", .target_raw = "http://www.example.com/index.html", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, .url = { .host_name = "www.example.com" } }, .version_major = 1, .version_minor = 0, .connection_close = TRUE },{ .request = "GET http://www.example.com/index.html HTTP/1.1\r\n" "Host: www.example.com\r\n" "Expect: 100-continue\r\n" "\r\n", .method = "GET", .target_raw = "http://www.example.com/index.html", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, .url = { .host_name = "www.example.com" } }, .version_major = 1, .version_minor = 1, .expect_100_continue = TRUE },{ .request = "GET / HTTP/1.1\r\n" "Date: Mon, 09 Kul 2018 02:24:29 GMT\r\n" "Host: example.com\r\n" "\r\n", .method = "GET", .target_raw = "/", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN, .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 1, },{ .request = "GET / HTTP/1.1\r\n" "Date: Sun, 07 Oct 2012 19:52:03 GMT\r\n" "Host: example.com\r\n" "Date: Sun, 13 Oct 2013 13:13:13 GMT\r\n" "\r\n", .method = "GET", .target_raw = "/", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN, .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 1, } }; static const unsigned int valid_request_parse_test_count = N_ELEMENTS(valid_request_parse_tests); static const char * _request_target_format(enum http_request_target_format target_format) { switch (target_format) { case HTTP_REQUEST_TARGET_FORMAT_ORIGIN: return "origin"; case HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE: return "absolute"; case HTTP_REQUEST_TARGET_FORMAT_AUTHORITY: return "authority"; case HTTP_REQUEST_TARGET_FORMAT_ASTERISK: return "asterisk"; } return t_strdup_printf("<>", target_format); } static void test_http_request_parse_valid(void) { unsigned int i; buffer_t *payload_buffer = buffer_create_dynamic(default_pool, 1024); for (i = 0; i < valid_request_parse_test_count; i++) T_BEGIN { struct istream *input; struct ostream *output; const struct http_request_valid_parse_test *test; struct http_request_parser *parser; struct http_request request, request_parsed; enum http_request_parse_error error_code; const char *request_text, *payload, *error; unsigned int pos, request_text_len; int ret = 0; test = &valid_request_parse_tests[i]; request_text = test->request; request_text_len = strlen(request_text); input = test_istream_create_data(request_text, request_text_len); parser = http_request_parser_init(input, NULL, test->flags); test_begin(t_strdup_printf("http request valid [%d]", i)); payload = NULL; for (pos = 0; pos <= request_text_len && ret == 0; pos++) { test_istream_set_size(input, pos); ret = http_request_parse_next (parser, NULL, &request_parsed, &error_code, &error); } test_istream_set_size(input, request_text_len); i_stream_unref(&input); request = request_parsed; while (ret > 0) { if (request.payload != NULL) { buffer_set_used_size(payload_buffer, 0); output = o_stream_create_buffer(payload_buffer); test_out("payload receive", o_stream_send_istream(output, request.payload)); o_stream_destroy(&output); payload = str_c(payload_buffer); } else { payload = NULL; } ret = http_request_parse_next (parser, NULL, &request_parsed, &error_code, &error); } test_out_reason("parse success", ret == 0, error); if (ret == 0) { /* verify last request only */ if (request.method == NULL || test->method == NULL) { test_out(t_strdup_printf("request->method = %s", request.method), request.method == test->method); } else { test_out(t_strdup_printf("request->method = %s", request.method), strcmp(request.method, test->method) == 0); } if (request.target_raw == NULL || test->target_raw == NULL) { test_out(t_strdup_printf ("request->target_raw = %s", request.target_raw), request.target_raw == test->target_raw); } else { test_out(t_strdup_printf ("request->target_raw = %s", request.target_raw), strcmp(request.target_raw, test->target_raw) == 0); } if (request.target.url == NULL) { test_out("request->target.url = (null)", test->target.url.host_name == NULL && !test->target.url.have_port); } else { if (request.target.url->host_name == NULL || test->target.url.host_name == NULL) { test_out(t_strdup_printf("request->target.url->host_name = %s", request.target.url->host_name), request.target.url->host_name == test->target.url.host_name); } else { test_out(t_strdup_printf("request->target.url->host_name = %s", request.target.url->host_name), strcmp(request.target.url->host_name, test->target.url.host_name) == 0); } if (!request.target.url->have_port) { test_out("request->target.url->port = (unspecified)", request.target.url->have_port == test->target.url.have_port); } else { test_out(t_strdup_printf ("request->target.url->port = %u", request.target.url->port), request.target.url->have_port == test->target.url.have_port && request.target.url->port == test->target.url.port); } test_out(t_strdup_printf("request->target.url->have_ssl = %s", (request.target.url->have_ssl ? "yes" : "no")), request.target.url->have_ssl == test->target.url.have_ssl); } test_out(t_strdup_printf("request->target_format = %s", _request_target_format(request.target.format)), request.target.format == test->target.format); test_out(t_strdup_printf("request->version = %u.%u", request.version_major, request.version_minor), request.version_major == test->version_major && request.version_minor == test->version_minor); test_out(t_strdup_printf("request->connection_close = %s", (request.connection_close ? "yes" : "no")), request.connection_close == test->connection_close); test_out(t_strdup_printf("request->expect_100_continue = %s", (request.expect_100_continue ? "yes" : "no")), request.expect_100_continue == test->expect_100_continue); if (payload == NULL || test->payload == NULL) { test_out(t_strdup_printf("request->payload = %s", str_sanitize(payload, 80)), payload == test->payload); } else { test_out(t_strdup_printf("request->payload = %s", str_sanitize(payload, 80)), strcmp(payload, test->payload) == 0); } } test_end(); http_request_parser_deinit(&parser); } T_END; buffer_free(&payload_buffer); } /* * Test: invalid requests */ struct http_request_invalid_parse_test { const char *request; enum http_request_parse_flags flags; enum http_request_parse_error error_code; }; static const struct http_request_invalid_parse_test invalid_request_parse_tests[] = { { .request = "GET: / HTTP/1.1\r\n" "Host: example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST },{ .request = "GET % HTTP/1.1\r\n" "Host: example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST },{ .request = "GET /frop\" HTTP/1.1\r\n" "Host: example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST },{ .request = "GET / HTCPCP/1.0\r\n" "Host: example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST },{ .request = "GET / HTTP/1.0.1\r\n" "Host: example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST },{ .request = "GET / HTTP/1.1\r\n" "Host: \"example.com\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST },{ .request = "GET / HTTP/1.1\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST },{ .request = "GET / HTTP/1.1\r\n" "Host: www.example.com\r\n" "Transfer-Encoding: gzip\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST },{ .request = "GET / HTTP/1.1\r\n" "Host: www.example.com\r\n" "Expect: payment\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED },{ .request = "GET / HTTP/1.1\r\n" "Host: www.example.com\r\n" "Transfer-Encoding: cuneiform, chunked\r\n" "\r\n", .error_code = HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED },{ .request = "GET / HTTP/1.1\r\n" "Date: Mon, 09 Kul 2018 02:24:29 GMT\r\n" "Host: example.com\r\n" "\r\n", .flags = HTTP_REQUEST_PARSE_FLAG_STRICT, .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST },{ .request = "GET / HTTP/1.1\r\n" "Date: Sun, 07 Oct 2012 19:52:03 GMT\r\n" "Host: example.com\r\n" "Date: Sun, 13 Oct 2013 13:13:13 GMT\r\n" "\r\n", .flags = HTTP_REQUEST_PARSE_FLAG_STRICT, .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST } // FIXME: test request limits }; static unsigned int invalid_request_parse_test_count = N_ELEMENTS(invalid_request_parse_tests); static const char * _request_parse_error(enum http_request_parse_error error) { switch (error) { case HTTP_REQUEST_PARSE_ERROR_NONE: return "none?!"; case HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM: return "broken stream"; case HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST: return "broken request"; case HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST: return "bad request"; case HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED: return "not implemented"; case HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED: return "expectation failed"; case HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG: return "method too long"; case HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG: return "target too long"; case HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE: return "payload too large"; } return t_strdup_printf("<>", error); } static void test_http_request_parse_invalid(void) { const struct http_request_invalid_parse_test *test; struct http_request_parser *parser; struct http_request request; enum http_request_parse_error error_code; const char *request_text, *error; struct istream *input; int ret; unsigned int i; for (i = 0; i < invalid_request_parse_test_count; i++) T_BEGIN { test = &invalid_request_parse_tests[i]; request_text = test->request; input = i_stream_create_from_data(request_text, strlen(request_text)); parser = http_request_parser_init(input, NULL, test->flags); i_stream_unref(&input); test_begin(t_strdup_printf("http request invalid [%d]", i)); while ((ret=http_request_parse_next (parser, NULL, &request, &error_code, &error)) > 0); test_out_reason("parse failure", ret < 0, error); if (ret < 0) { test_out(t_strdup_printf("parse error code = %s", _request_parse_error(error_code)), error_code == test->error_code); } test_end(); http_request_parser_deinit(&parser); } T_END; } /* * Bad request tests */ static const unsigned char bad_request_with_nuls[] = "GET / HTTP/1.1\r\n" "Host: example.com\r\n" "User-Agent: text\0client\r\n" "\r\n"; static void test_http_request_parse_bad(void) { struct http_request_parser *parser; struct http_request request; enum http_request_parse_error error_code; const char *header, *error; struct istream *input; int ret; /* parse failure guarantees http_request_header.size equals strlen(http_request_header.value) */ test_begin("http request with NULs (strict)"); input = i_stream_create_from_data(bad_request_with_nuls, sizeof(bad_request_with_nuls)-1); parser = http_request_parser_init(input, NULL, HTTP_REQUEST_PARSE_FLAG_STRICT); i_stream_unref(&input); while ((ret=http_request_parse_next (parser, NULL, &request, &error_code, &error)) > 0); test_assert(ret < 0); http_request_parser_deinit(&parser); test_end(); /* even when lenient, bad characters like NUL must not be returned */ test_begin("http request with NULs (lenient)"); input = i_stream_create_from_data(bad_request_with_nuls, sizeof(bad_request_with_nuls)-1); parser = http_request_parser_init(input, NULL, 0); i_stream_unref(&input); ret = http_request_parse_next (parser, NULL, &request, &error_code, &error); test_out("parse success", ret > 0); header = http_request_header_get(&request, "user-agent"); test_out("header present", header != NULL); if (header != NULL) { test_out(t_strdup_printf("header User-Agent: %s", header), strcmp(header, "textclient") == 0); } ret = http_request_parse_next (parser, NULL, &request, &error_code, &error); test_out("parse end", ret == 0); http_request_parser_deinit(&parser); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_http_request_parse_valid, test_http_request_parse_invalid, test_http_request_parse_bad, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-header.h0000644000175000017500000000236413165463624015234 00000000000000#ifndef HTTP_HEADER_H #define HTTP_HEADER_H struct http_header; struct http_header_limits { uoff_t max_size; uoff_t max_field_size; unsigned int max_fields; }; struct http_header_field { const char *key; /* FIXME: rename to 'name' for v2.3 */ const char *value; size_t size; }; ARRAY_DEFINE_TYPE(http_header_field, struct http_header_field); static inline bool http_header_field_is(const struct http_header_field *hfield, const char *name) { return (strcasecmp(hfield->key, name) == 0); } struct http_header * http_header_create(pool_t pool, unsigned int init_count); const struct http_header_field * http_header_field_add(struct http_header *header, const char *name, const unsigned char *data, size_t size); void http_header_field_delete(struct http_header *header, const char *name); const ARRAY_TYPE(http_header_field) * http_header_get_fields(const struct http_header *header) ATTR_PURE; const struct http_header_field * http_header_field_find(const struct http_header *header, const char *name) ATTR_PURE; const char * http_header_field_get(const struct http_header *header, const char *name) ATTR_PURE; int http_header_field_find_unique(const struct http_header *header, const char *name, const struct http_header_field **hfield_r); #endif dovecot-2.2.33.2/src/lib-http/http-header-parser.h0000644000175000017500000000130313147010711016477 00000000000000#ifndef HTTP_HEADER_PARSER_H #define HTTP_HEADER_PARSER_H struct http_header_limits; struct http_header_parser; enum http_header_parse_flags { /* Strictly adhere to the HTTP protocol specification */ HTTP_HEADER_PARSE_FLAG_STRICT = BIT(0) }; struct http_header_parser * http_header_parser_init(struct istream *input, const struct http_header_limits *limits, enum http_header_parse_flags flags); void http_header_parser_deinit(struct http_header_parser **_parser); void http_header_parser_reset(struct http_header_parser *parser); int http_header_parse_next_field(struct http_header_parser *parser, const char **name_r, const unsigned char **data_r, size_t *size_r, const char **error_r); #endif dovecot-2.2.33.2/src/lib-http/Makefile.in0000644000175000017500000011144513172375573014727 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) subdir = src/lib-http ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libhttp_la_LIBADD = am_libhttp_la_OBJECTS = http-date.lo http-url.lo http-parser.lo \ http-header.lo http-header-parser.lo http-transfer-chunked.lo \ http-auth.lo http-message-parser.lo http-request.lo \ http-request-parser.lo http-response.lo \ http-response-parser.lo http-client-request.lo \ http-client-connection.lo http-client-peer.lo \ http-client-queue.lo http-client-host.lo http-client.lo \ http-server-response.lo http-server-request.lo \ http-server-connection.lo http-server.lo libhttp_la_OBJECTS = $(am_libhttp_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-http-date$(EXEEXT) test-http-url$(EXEEXT) \ test-http-header-parser$(EXEEXT) test-http-transfer$(EXEEXT) \ test-http-auth$(EXEEXT) test-http-response-parser$(EXEEXT) \ test-http-request-parser$(EXEEXT) am__EXEEXT_2 = test-http-payload$(EXEEXT) test-http-client$(EXEEXT) \ test-http-client-errors$(EXEEXT) test-http-server$(EXEEXT) \ test-http-server-errors$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_http_auth_OBJECTS = test-http-auth.$(OBJEXT) test_http_auth_OBJECTS = $(am_test_http_auth_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = ../lib-test/libtest.la ../lib/liblib.la \ $(am__DEPENDENCIES_1) am_test_http_client_OBJECTS = test-http-client.$(OBJEXT) test_http_client_OBJECTS = $(am_test_http_client_OBJECTS) am__DEPENDENCIES_3 = libhttp.la ../lib-dns/libdns.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-master/libmaster.la ../lib-settings/libsettings.la \ $(am__DEPENDENCIES_2) test_http_client_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(test_http_client_LDFLAGS) $(LDFLAGS) \ -o $@ am_test_http_client_errors_OBJECTS = \ test-http-client-errors.$(OBJEXT) test_http_client_errors_OBJECTS = \ $(am_test_http_client_errors_OBJECTS) test_http_client_errors_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(test_http_client_errors_LDFLAGS) \ $(LDFLAGS) -o $@ am_test_http_date_OBJECTS = test-http-date.$(OBJEXT) test_http_date_OBJECTS = $(am_test_http_date_OBJECTS) am_test_http_header_parser_OBJECTS = \ test-http-header-parser.$(OBJEXT) test_http_header_parser_OBJECTS = \ $(am_test_http_header_parser_OBJECTS) am_test_http_payload_OBJECTS = test-http-payload.$(OBJEXT) test_http_payload_OBJECTS = $(am_test_http_payload_OBJECTS) test_http_payload_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(test_http_payload_LDFLAGS) $(LDFLAGS) \ -o $@ am_test_http_request_parser_OBJECTS = \ test-http-request-parser.$(OBJEXT) test_http_request_parser_OBJECTS = \ $(am_test_http_request_parser_OBJECTS) am_test_http_response_parser_OBJECTS = \ test-http-response-parser.$(OBJEXT) test_http_response_parser_OBJECTS = \ $(am_test_http_response_parser_OBJECTS) am_test_http_server_OBJECTS = test-http-server.$(OBJEXT) test_http_server_OBJECTS = $(am_test_http_server_OBJECTS) test_http_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(test_http_server_LDFLAGS) $(LDFLAGS) \ -o $@ am_test_http_server_errors_OBJECTS = \ test-http-server-errors.$(OBJEXT) test_http_server_errors_OBJECTS = \ $(am_test_http_server_errors_OBJECTS) test_http_server_errors_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(test_http_server_errors_LDFLAGS) \ $(LDFLAGS) -o $@ am_test_http_transfer_OBJECTS = test-http-transfer.$(OBJEXT) test_http_transfer_OBJECTS = $(am_test_http_transfer_OBJECTS) am_test_http_url_OBJECTS = test-http-url.$(OBJEXT) test_http_url_OBJECTS = $(am_test_http_url_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libhttp_la_SOURCES) $(test_http_auth_SOURCES) \ $(test_http_client_SOURCES) $(test_http_client_errors_SOURCES) \ $(test_http_date_SOURCES) $(test_http_header_parser_SOURCES) \ $(test_http_payload_SOURCES) \ $(test_http_request_parser_SOURCES) \ $(test_http_response_parser_SOURCES) \ $(test_http_server_SOURCES) $(test_http_server_errors_SOURCES) \ $(test_http_transfer_SOURCES) $(test_http_url_SOURCES) DIST_SOURCES = $(libhttp_la_SOURCES) $(test_http_auth_SOURCES) \ $(test_http_client_SOURCES) $(test_http_client_errors_SOURCES) \ $(test_http_date_SOURCES) $(test_http_header_parser_SOURCES) \ $(test_http_payload_SOURCES) \ $(test_http_request_parser_SOURCES) \ $(test_http_response_parser_SOURCES) \ $(test_http_server_SOURCES) $(test_http_server_errors_SOURCES) \ $(test_http_transfer_SOURCES) $(test_http_url_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libhttp.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master libhttp_la_SOURCES = \ http-date.c \ http-url.c \ http-parser.c \ http-header.c \ http-header-parser.c \ http-transfer-chunked.c \ http-auth.c \ http-message-parser.c \ http-request.c \ http-request-parser.c \ http-response.c \ http-response-parser.c \ http-client-request.c \ http-client-connection.c \ http-client-peer.c \ http-client-queue.c \ http-client-host.c \ http-client.c \ http-server-response.c \ http-server-request.c \ http-server-connection.c \ http-server.c headers = \ http-date.h \ http-url.h \ http-parser.h \ http-header.h \ http-header-parser.h \ http-transfer.h \ http-auth.h \ http-message-parser.h \ http-request.h \ http-request-parser.h \ http-response.h \ http-response-parser.h \ http-client-private.h \ http-client.h \ http-server-private.h \ http-server.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-http-date \ test-http-url \ test-http-header-parser \ test-http-transfer \ test-http-auth \ test-http-response-parser \ test-http-request-parser test_nocheck_programs = \ test-http-payload \ test-http-client \ test-http-client-errors \ test-http-server \ test-http-server-errors test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la \ $(MODULE_LIBS) test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_http_url_SOURCES = test-http-url.c test_http_url_LDADD = http-url.lo http-header.lo $(test_libs) test_http_url_DEPENDENCIES = $(test_deps) test_http_date_SOURCES = test-http-date.c test_http_date_LDADD = http-date.lo $(test_libs) test_http_date_DEPENDENCIES = $(test_deps) test_http_header_parser_SOURCES = test-http-header-parser.c test_http_header_parser_LDADD = http-parser.lo http-header-parser.lo http-header.lo $(test_libs) test_http_header_parser_DEPENDENCIES = $(test_deps) test_http_transfer_SOURCES = test-http-transfer.c test_http_transfer_LDADD = \ http-parser.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-header.lo \ $(test_libs) test_http_transfer_DEPENDENCIES = $(test_deps) test_http_auth_SOURCES = test-http-auth.c test_http_auth_LDADD = \ http-auth.lo \ http-parser.lo \ $(test_libs) test_http_auth_DEPENDENCIES = $(test_deps) test_http_response_parser_SOURCES = test-http-response-parser.c test_http_response_parser_LDADD = \ http-date.lo \ http-parser.lo \ http-header.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-message-parser.lo \ http-response-parser.lo \ $(test_libs) test_http_response_parser_DEPENDENCIES = $(test_deps) test_http_request_parser_SOURCES = test-http-request-parser.c test_http_request_parser_LDADD = \ http-date.lo \ http-parser.lo \ http-url.lo \ http-header.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-message-parser.lo \ http-request-parser.lo \ $(test_libs) test_http_request_parser_DEPENDENCIES = $(test_deps) test_http_libs = \ libhttp.la \ ../lib-dns/libdns.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-master/libmaster.la \ ../lib-settings/libsettings.la \ $(test_libs) test_http_deps = \ libhttp.la \ ../lib-dns/libdns.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-master/libmaster.la \ ../lib-settings/libsettings.la \ $(test_deps) test_http_payload_SOURCES = test-http-payload.c test_http_payload_LDFLAGS = -export-dynamic test_http_payload_LDADD = \ $(test_http_libs) test_http_payload_DEPENDENCIES = \ $(test_http_deps) test_http_client_SOURCES = test-http-client.c test_http_client_LDFLAGS = -export-dynamic test_http_client_LDADD = \ $(test_http_libs) \ ../lib-ssl-iostream/libssl_iostream_openssl.la test_http_client_DEPENDENCIES = \ $(test_http_deps) test_http_client_errors_SOURCES = test-http-client-errors.c test_http_client_errors_LDFLAGS = -export-dynamic test_http_client_errors_LDADD = \ $(test_http_libs) test_http_client_errors_DEPENDENCIES = \ $(test_http_deps) test_http_server_SOURCES = test-http-server.c test_http_server_LDFLAGS = -export-dynamic test_http_server_LDADD = \ $(test_http_libs) test_http_server_DEPENDENCIES = \ $(test_http_deps) test_http_server_errors_SOURCES = test-http-server-errors.c test_http_server_errors_LDFLAGS = -export-dynamic test_http_server_errors_LDADD = \ $(test_http_libs) test_http_server_errors_DEPENDENCIES = \ $(test_http_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-http/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-http/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libhttp.la: $(libhttp_la_OBJECTS) $(libhttp_la_DEPENDENCIES) $(EXTRA_libhttp_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libhttp_la_OBJECTS) $(libhttp_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-http-auth$(EXEEXT): $(test_http_auth_OBJECTS) $(test_http_auth_DEPENDENCIES) $(EXTRA_test_http_auth_DEPENDENCIES) @rm -f test-http-auth$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_auth_OBJECTS) $(test_http_auth_LDADD) $(LIBS) test-http-client$(EXEEXT): $(test_http_client_OBJECTS) $(test_http_client_DEPENDENCIES) $(EXTRA_test_http_client_DEPENDENCIES) @rm -f test-http-client$(EXEEXT) $(AM_V_CCLD)$(test_http_client_LINK) $(test_http_client_OBJECTS) $(test_http_client_LDADD) $(LIBS) test-http-client-errors$(EXEEXT): $(test_http_client_errors_OBJECTS) $(test_http_client_errors_DEPENDENCIES) $(EXTRA_test_http_client_errors_DEPENDENCIES) @rm -f test-http-client-errors$(EXEEXT) $(AM_V_CCLD)$(test_http_client_errors_LINK) $(test_http_client_errors_OBJECTS) $(test_http_client_errors_LDADD) $(LIBS) test-http-date$(EXEEXT): $(test_http_date_OBJECTS) $(test_http_date_DEPENDENCIES) $(EXTRA_test_http_date_DEPENDENCIES) @rm -f test-http-date$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_date_OBJECTS) $(test_http_date_LDADD) $(LIBS) test-http-header-parser$(EXEEXT): $(test_http_header_parser_OBJECTS) $(test_http_header_parser_DEPENDENCIES) $(EXTRA_test_http_header_parser_DEPENDENCIES) @rm -f test-http-header-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_header_parser_OBJECTS) $(test_http_header_parser_LDADD) $(LIBS) test-http-payload$(EXEEXT): $(test_http_payload_OBJECTS) $(test_http_payload_DEPENDENCIES) $(EXTRA_test_http_payload_DEPENDENCIES) @rm -f test-http-payload$(EXEEXT) $(AM_V_CCLD)$(test_http_payload_LINK) $(test_http_payload_OBJECTS) $(test_http_payload_LDADD) $(LIBS) test-http-request-parser$(EXEEXT): $(test_http_request_parser_OBJECTS) $(test_http_request_parser_DEPENDENCIES) $(EXTRA_test_http_request_parser_DEPENDENCIES) @rm -f test-http-request-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_request_parser_OBJECTS) $(test_http_request_parser_LDADD) $(LIBS) test-http-response-parser$(EXEEXT): $(test_http_response_parser_OBJECTS) $(test_http_response_parser_DEPENDENCIES) $(EXTRA_test_http_response_parser_DEPENDENCIES) @rm -f test-http-response-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_response_parser_OBJECTS) $(test_http_response_parser_LDADD) $(LIBS) test-http-server$(EXEEXT): $(test_http_server_OBJECTS) $(test_http_server_DEPENDENCIES) $(EXTRA_test_http_server_DEPENDENCIES) @rm -f test-http-server$(EXEEXT) $(AM_V_CCLD)$(test_http_server_LINK) $(test_http_server_OBJECTS) $(test_http_server_LDADD) $(LIBS) test-http-server-errors$(EXEEXT): $(test_http_server_errors_OBJECTS) $(test_http_server_errors_DEPENDENCIES) $(EXTRA_test_http_server_errors_DEPENDENCIES) @rm -f test-http-server-errors$(EXEEXT) $(AM_V_CCLD)$(test_http_server_errors_LINK) $(test_http_server_errors_OBJECTS) $(test_http_server_errors_LDADD) $(LIBS) test-http-transfer$(EXEEXT): $(test_http_transfer_OBJECTS) $(test_http_transfer_DEPENDENCIES) $(EXTRA_test_http_transfer_DEPENDENCIES) @rm -f test-http-transfer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_transfer_OBJECTS) $(test_http_transfer_LDADD) $(LIBS) test-http-url$(EXEEXT): $(test_http_url_OBJECTS) $(test_http_url_DEPENDENCIES) $(EXTRA_test_http_url_DEPENDENCIES) @rm -f test-http-url$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_http_url_OBJECTS) $(test_http_url_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-auth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client-host.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client-peer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client-queue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client-request.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-date.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-header-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-header.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-message-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-request-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-request.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-response-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-response.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-server-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-server-request.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-server-response.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-transfer-chunked.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-url.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-auth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-client-errors.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-date.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-header-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-payload.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-request-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-response-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-server-errors.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-transfer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-http-url.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-http/http-server-response.c0000644000175000017500000005406113165463624017142 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "array.h" #include "istream.h" #include "ostream-private.h" #include "http-date.h" #include "http-transfer.h" #include "http-server-private.h" struct http_server_response_payload { struct http_server_response *resp; struct const_iovec *iov; unsigned int iov_count, iov_idx; size_t iov_pos; }; /* * Logging */ static inline void http_server_response_debug(struct http_server_response *resp, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_response_debug(struct http_server_response *resp, const char *format, ...) { va_list args; if (resp->request->server->set.debug) { va_start(args, format); i_debug("http-server: request %s; %u response: %s", http_server_request_label(resp->request), resp->status, t_strdup_vprintf(format, args)); va_end(args); } } /* * Response */ struct http_server_response * http_server_response_create(struct http_server_request *req, unsigned int status, const char *reason) { struct http_server_response *resp; if (req->response == NULL) { resp = req->response = p_new (req->pool, struct http_server_response, 1); } else { /* was already composing a response, but decided to start a new one (would usually be a failure response) */ resp = req->response; i_assert(!resp->submitted); http_server_response_free(resp); i_zero(resp); } resp->request = req; resp->status = status; resp->reason = p_strdup(req->pool, reason); resp->headers = str_new(default_pool, 256); resp->date = (time_t)-1; return resp; } void http_server_response_free(struct http_server_response *resp) { http_server_response_debug(resp, "Destroy"); i_assert(!resp->payload_blocking); if (resp->payload_input != NULL) i_stream_unref(&resp->payload_input); if (resp->payload_output != NULL) o_stream_unref(&resp->payload_output); str_free(&resp->headers); } void http_server_response_add_header(struct http_server_response *resp, const char *key, const char *value) { i_assert(!resp->submitted); i_assert(strchr(key, '\r') == NULL && strchr(key, '\n') == NULL); i_assert(strchr(value, '\r') == NULL && strchr(value, '\n') == NULL); /* mark presence of special headers */ switch (key[0]) { case 'c': case 'C': if (strcasecmp(key, "Connection") == 0) resp->have_hdr_connection = TRUE; else if (strcasecmp(key, "Content-Length") == 0) resp->have_hdr_body_spec = TRUE; break; case 'd': case 'D': if (strcasecmp(key, "Date") == 0) resp->have_hdr_date = TRUE; break; case 't': case 'T': if (strcasecmp(key, "Transfer-Encoding") == 0) resp->have_hdr_body_spec = TRUE; break; } str_printfa(resp->headers, "%s: %s\r\n", key, value); } void http_server_response_update_status(struct http_server_response *resp, unsigned int status, const char *reason) { i_assert(!resp->submitted); resp->status = status; /* free not called because pool is alloconly */ resp->reason = p_strdup(resp->request->pool, reason); } void http_server_response_set_date(struct http_server_response *resp, time_t date) { i_assert(!resp->submitted); resp->date = date; } void http_server_response_set_payload(struct http_server_response *resp, struct istream *input) { int ret; i_assert(!resp->submitted); i_assert(resp->blocking_output == NULL); i_assert(resp->payload_input == NULL); i_stream_ref(input); resp->payload_input = input; if ((ret = i_stream_get_size(input, TRUE, &resp->payload_size)) <= 0) { if (ret < 0) { i_error("i_stream_get_size(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); } resp->payload_size = 0; resp->payload_chunked = TRUE; } resp->payload_offset = input->v_offset; } void http_server_response_set_payload_data(struct http_server_response *resp, const unsigned char *data, size_t size) { struct istream *input; unsigned char *payload_data; if (size == 0) return; payload_data = p_malloc(resp->request->pool, size); memcpy(payload_data, data, size); input = i_stream_create_from_data(payload_data, size); http_server_response_set_payload(resp, input); i_stream_unref(&input); } void http_server_response_add_auth( struct http_server_response *resp, const struct http_auth_challenge *chlng) { struct http_auth_challenge *new; pool_t pool = resp->request->pool; if (!array_is_created(&resp->auth_challenges)) p_array_init(&resp->auth_challenges, pool, 4); new = array_append_space(&resp->auth_challenges); http_auth_challenge_copy(pool, new, chlng); } void http_server_response_add_auth_basic( struct http_server_response *resp, const char *realm) { struct http_auth_challenge chlng; http_auth_basic_challenge_init(&chlng, realm); http_server_response_add_auth(resp, &chlng); } static void http_server_response_do_submit(struct http_server_response *resp, bool close) { if (resp->date == (time_t)-1) resp->date = ioloop_time; resp->close = close; resp->submitted = TRUE; http_server_request_submit_response(resp->request); } void http_server_response_submit(struct http_server_response *resp) { i_assert(!resp->submitted); http_server_response_debug(resp, "Submitted"); http_server_response_do_submit(resp, FALSE); } void http_server_response_submit_close(struct http_server_response *resp) { i_assert(!resp->submitted); http_server_response_debug(resp, "Submitted"); http_server_response_do_submit(resp, TRUE); } void http_server_response_submit_tunnel(struct http_server_response *resp, http_server_tunnel_callback_t callback, void *context) { i_assert(!resp->submitted); http_server_response_debug(resp, "Started tunnelling"); resp->tunnel_callback = callback; resp->tunnel_context = context; http_server_response_do_submit(resp, TRUE); } static void http_server_response_finish_payload_out(struct http_server_response *resp) { struct http_server_connection *conn = resp->request->conn; if (resp->payload_output != NULL) { o_stream_unref(&resp->payload_output); resp->payload_output = NULL; } http_server_response_debug(resp, "Finished sending payload"); conn->output_locked = FALSE; if (resp->payload_corked) o_stream_uncork(conn->conn.output); o_stream_set_flush_callback(conn->conn.output, http_server_connection_output, conn); http_server_request_finished(resp->request); } static int http_server_response_output_direct(struct http_server_response_payload *rpay) { struct http_server_response *resp = rpay->resp; struct http_server_connection *conn = resp->request->conn; struct http_server *server = resp->request->server; struct ostream *output = resp->payload_output; struct const_iovec *iov; unsigned int iov_count, i; size_t bytes_left, block_len; ssize_t ret; if (http_server_connection_flush(conn) < 0) return -1; iov = &rpay->iov[rpay->iov_idx]; iov_count = rpay->iov_count - rpay->iov_idx; if ((ret=o_stream_sendv(output, iov, iov_count)) < 0) { const char *error = NULL; if (output->stream_errno != EPIPE && output->stream_errno != ECONNRESET) { error = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } http_server_connection_write_failed(conn, error); return -1; } if (ret > 0) { bytes_left = ret; for (i = 0; i < iov_count && bytes_left > 0; i++) { block_len = iov[i].iov_len <= bytes_left ? iov[i].iov_len : bytes_left; bytes_left -= block_len; } rpay->iov_idx += i; if (i < iov_count) { i_assert(iov[i].iov_len > bytes_left); iov[i].iov_base = PTR_OFFSET (iov[i].iov_base, iov[i].iov_len - bytes_left); iov[i].iov_len = bytes_left; } else { i_assert(rpay->iov_idx == rpay->iov_count); i_assert(server->ioloop != NULL); io_loop_stop(server->ioloop); } } return 1; } static int http_server_response_output_payload( struct http_server_response **_resp, const struct const_iovec *iov, unsigned int iov_count) { struct ioloop *prev_ioloop = current_ioloop; struct http_server_response *resp = *_resp; struct http_server_request *req = resp->request; struct http_server *server = req->server; struct http_server_connection *conn = req->conn; struct http_server_response_payload rpay; int ret; i_assert(req->state < HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE || req->state == HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT); i_assert(resp->payload_input == NULL); /* Discard any remaining incoming payload */ if (http_server_connection_discard_payload(conn) < 0) return -1; req->req.payload = NULL; http_server_connection_ref(conn); http_server_request_ref(req); resp->payload_blocking = TRUE; i_zero(&rpay); rpay.resp = resp; if (iov == NULL) { resp->payload_direct = FALSE; if (req->state == HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT) http_server_response_finish_payload_out(resp); } else { resp->payload_direct = TRUE; rpay.iov = i_new(struct const_iovec, iov_count); memcpy(rpay.iov, iov, sizeof(*iov)*iov_count); rpay.iov_count = iov_count; } resp->payload_size = 0; resp->payload_chunked = TRUE; if (req->state < HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE) http_server_response_submit(resp); if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) { /* Wait for payload data to be written */ i_assert(server->ioloop == NULL); server->ioloop = io_loop_create(); http_server_connection_switch_ioloop(conn); do { if (req->state < HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT) { http_server_response_debug(resp, "Preparing to send blocking payload"); http_server_connection_trigger_responses(conn); } else if (resp->payload_output != NULL) { http_server_response_debug(resp, "Sending blocking payload"); o_stream_unset_flush_callback(conn->conn.output); o_stream_set_flush_callback(resp->payload_output, http_server_response_output_direct, &rpay); o_stream_set_flush_pending(resp->payload_output, TRUE); } else { http_server_response_finish_payload_out(resp); i_assert(req->state >= HTTP_SERVER_REQUEST_STATE_FINISHED); break; } io_loop_run(server->ioloop); if (rpay.iov_count > 0 && rpay.iov_idx >= rpay.iov_count) break; } while (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED); io_loop_set_current(prev_ioloop); http_server_connection_switch_ioloop(conn); io_loop_set_current(server->ioloop); io_loop_destroy(&server->ioloop); } switch (req->state) { case HTTP_SERVER_REQUEST_STATE_FINISHED: ret = 1; break; case HTTP_SERVER_REQUEST_STATE_ABORTED: http_server_response_debug(resp, "Request aborted while sending blocking payload"); ret = -1; break; default: ret = 0; break; } resp->payload_blocking = FALSE; resp->payload_direct = FALSE; /* callback may have messed with our pointer, so unref using local variable */ if (!http_server_request_unref(&req)) *_resp = NULL; http_server_connection_unref(&conn); i_free(rpay.iov); /* Return status */ return ret; } int http_server_response_send_payload(struct http_server_response **_resp, const unsigned char *data, size_t size) { struct http_server_response *resp = *_resp; struct const_iovec iov; int ret; i_assert(resp->blocking_output == NULL); resp->payload_corked = TRUE; i_assert(data != NULL); i_zero(&iov); iov.iov_base = data; iov.iov_len = size; ret = http_server_response_output_payload(&resp, &iov, 1); if (ret < 0) *_resp = NULL; else { i_assert(ret == 0); i_assert(resp != NULL); } return ret; } int http_server_response_finish_payload(struct http_server_response **_resp) { struct http_server_response *resp = *_resp; int ret; i_assert(resp->blocking_output == NULL); *_resp = NULL; ret = http_server_response_output_payload(&resp, NULL, 0); i_assert(ret != 0); return ret < 0 ? -1 : 0; } void http_server_response_abort_payload(struct http_server_response **_resp) { struct http_server_response *resp = *_resp; struct http_server_request *req = resp->request; *_resp = NULL; http_server_request_abort(&req, "Aborted sending response payload"); } static void http_server_response_payload_input(struct http_server_response *resp) { struct http_server_connection *conn = resp->request->conn; if (conn->io_resp_payload != NULL) io_remove(&conn->io_resp_payload); (void)http_server_connection_output(conn); } int http_server_response_send_more(struct http_server_response *resp, const char **error_r) { struct http_server_connection *conn = resp->request->conn; struct ostream *output = resp->payload_output; off_t ret; *error_r = NULL; i_assert(!resp->payload_blocking); i_assert(resp->payload_input != NULL); i_assert(resp->payload_output != NULL); if (conn->io_resp_payload != NULL) io_remove(&conn->io_resp_payload); /* chunked ostream needs to write to the parent stream's buffer */ o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE); ret = o_stream_send_istream(output, resp->payload_input); o_stream_set_max_buffer_size(output, (size_t)-1); if (resp->payload_input->stream_errno != 0) { /* we're in the middle of sending a response, so the connection will also have to be aborted */ *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(resp->payload_input), i_stream_get_error(resp->payload_input)); ret = -1; } else if (output->stream_errno != 0) { /* failed to send response */ if (output->stream_errno != EPIPE && output->stream_errno != ECONNRESET) { *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } ret = -1; } else { i_assert(ret >= 0); } if (ret < 0 || i_stream_is_eof(resp->payload_input)) { /* finished sending */ if (ret >= 0 && !resp->payload_chunked && resp->payload_input->v_offset - resp->payload_offset != resp->payload_size) { *error_r = t_strdup_printf( "Input stream %s size changed unexpectedly", i_stream_get_name(resp->payload_input)); ret = -1; } /* finished sending payload */ http_server_response_finish_payload_out(resp); } else if (i_stream_have_bytes_left(resp->payload_input)) { /* output is blocking */ conn->output_locked = TRUE; o_stream_set_flush_pending(output, TRUE); //http_server_response_debug(resp, "Partially sent payload"); } else { /* input is blocking */ conn->output_locked = TRUE; conn->io_resp_payload = io_add_istream(resp->payload_input, http_server_response_payload_input, resp); } return ret < 0 ? -1 : 0; } static int http_server_response_send_real(struct http_server_response *resp, const char **error_r) { struct http_server_request *req = resp->request; struct http_server_connection *conn = req->conn; struct http_server *server = req->server; struct ostream *output = conn->conn.output; string_t *rtext = t_str_new(256); struct const_iovec iov[3]; bool is_head = http_request_method_is(&req->req, "HEAD"); int ret = 0; *error_r = NULL; i_assert(!conn->output_locked); /* create status line */ str_append(rtext, "HTTP/1.1 "); str_printfa(rtext, "%u", resp->status); str_append(rtext, " "); str_append(rtext, resp->reason); /* create special headers implicitly if not set explicitly using http_server_response_add_header() */ if (!resp->have_hdr_date) { str_append(rtext, "\r\nDate: "); str_append(rtext, http_date_create(resp->date)); str_append(rtext, "\r\n"); } if (array_is_created(&resp->auth_challenges)) { str_append(rtext, "WWW-Authenticate: "); http_auth_create_challenges(rtext, &resp->auth_challenges); str_append(rtext, "\r\n"); } if (resp->payload_input != NULL || resp->payload_direct) { i_assert(resp->tunnel_callback == NULL && resp->status / 100 != 1 && resp->status != 204 && resp->status != 304); if (resp->payload_chunked) { if (http_server_request_version_equals(req, 1, 0)) { if (!is_head) { /* cannot use Transfer-Encoding */ resp->payload_output = output; o_stream_ref(output); /* connection close marks end of payload */ resp->close = TRUE; } } else { if (!resp->have_hdr_body_spec) str_append(rtext, "Transfer-Encoding: chunked\r\n"); if (!is_head) { resp->payload_output = http_transfer_chunked_ostream_create(output); } } } else { /* send Content-Length if we have specified a payload, even if it's 0 bytes. */ if (!resp->have_hdr_body_spec) { str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n", resp->payload_size); } if (!is_head) { resp->payload_output = output; o_stream_ref(output); } } } else if (resp->tunnel_callback == NULL && resp->status / 100 != 1 && resp->status != 204 && resp->status != 304 && !is_head) { /* RFC 7230, Section 3.3: Message Body Responses to the HEAD request method (Section 4.3.2 of [RFC7231]) never include a message body because the associated response header fields (e.g., Transfer-Encoding, Content-Length, etc.), if present, indicate only what their values would have been if the request method had been GET (Section 4.3.1 of [RFC7231]). 2xx (Successful) responses to a CONNECT request method (Section 4.3.6 of [RFC7231]) switch to tunnel mode instead of having a message body. All 1xx (Informational), 204 (No Content), and 304 (Not Modified) responses do not include a message body. All other responses do include a message body, although the body might be of zero length. RFC 7230, Section 3.3.2: Content-Length A server MUST NOT send a Content-Length header field in any 2xx (Successful) response to a CONNECT request (Section 4.3.6 of [RFC7231]). -> Create empty body if it is missing. */ if (!resp->have_hdr_body_spec) str_append(rtext, "Content-Length: 0\r\n"); } if (!resp->have_hdr_connection) { if (resp->close && resp->tunnel_callback == NULL) str_append(rtext, "Connection: close\r\n"); else if (http_server_request_version_equals(req, 1, 0)) str_append(rtext, "Connection: Keep-Alive\r\n"); } /* status line + implicit headers */ iov[0].iov_base = str_data(rtext); iov[0].iov_len = str_len(rtext); /* explicit headers */ iov[1].iov_base = str_data(resp->headers); iov[1].iov_len = str_len(resp->headers); /* end of header */ iov[2].iov_base = "\r\n"; iov[2].iov_len = 2; req->state = HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT; o_stream_ref(output); o_stream_cork(output); if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) { if (output->stream_errno != EPIPE && output->stream_errno != ECONNRESET) { *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } ret = -1; } if (ret >= 0) { http_server_response_debug(resp, "Sent header"); if (resp->payload_blocking) { /* blocking payload */ conn->output_locked = TRUE; if (server->ioloop != NULL) io_loop_stop(server->ioloop); } else if (resp->payload_output != NULL) { /* non-blocking payload */ if (http_server_response_send_more(resp, error_r) < 0) ret = -1; } else { /* no payload to send */ conn->output_locked = FALSE; http_server_response_finish_payload_out(resp); } } if (!resp->payload_corked) o_stream_uncork(output); o_stream_unref(&output); return ret; } int http_server_response_send(struct http_server_response *resp, const char **error_r) { char *errstr = NULL; int ret; T_BEGIN { ret = http_server_response_send_real(resp, error_r); if (ret < 0) errstr = i_strdup(*error_r); } T_END; *error_r = t_strdup(errstr); i_free(errstr); return ret; } /* * Payload output stream */ struct http_server_ostream { struct ostream_private ostream; struct http_server_response *resp; }; static ssize_t http_server_ostream_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct http_server_ostream *hsostream = (struct http_server_ostream *)stream; unsigned int i; ssize_t ret; if (http_server_response_output_payload (&hsostream->resp, iov, iov_count) < 0) { if (stream->parent->stream_errno != 0) { o_stream_copy_error_from_parent(stream); } else { io_stream_set_error(&stream->iostream, "HTTP connection broke while sending payload"); stream->ostream.stream_errno = EIO; } return -1; } ret = 0; for (i = 0; i < iov_count; i++) ret += iov[i].iov_len; stream->ostream.offset += ret; return ret; } static void http_server_ostream_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct http_server_ostream *hsostream = (struct http_server_ostream *)stream; struct ostream_private *ostream = &hsostream->ostream; if (hsostream->resp == NULL) return; hsostream->resp->blocking_output = NULL; if (http_server_response_output_payload (&hsostream->resp, NULL, 0) < 0) { if (ostream->parent->stream_errno != 0) { o_stream_copy_error_from_parent(ostream); } else { io_stream_set_error(&ostream->iostream, "HTTP connection broke while sending payload"); ostream->ostream.stream_errno = EIO; } } hsostream->resp = NULL; } static void http_server_ostream_destroy(struct iostream_private *stream) { struct http_server_ostream *hsostream = (struct http_server_ostream *)stream; if (hsostream->resp != NULL) { hsostream->resp->blocking_output = NULL; http_server_response_abort_payload(&hsostream->resp); } } struct ostream * http_server_response_get_payload_output(struct http_server_response *resp, bool blocking) { struct http_server_connection *conn = resp->request->conn; struct http_server_ostream *hsostream; i_assert(resp->payload_input == NULL); i_assert(resp->blocking_output == NULL); i_assert(blocking == TRUE); // FIXME: support non-blocking hsostream = i_new(struct http_server_ostream, 1); hsostream->ostream.sendv = http_server_ostream_sendv; hsostream->ostream.iostream.close = http_server_ostream_close; hsostream->ostream.iostream.destroy = http_server_ostream_destroy; hsostream->resp = resp; resp->blocking_output = o_stream_create(&hsostream->ostream, conn->conn.output, -1); return resp->blocking_output; } void http_server_response_get_status(struct http_server_response *resp, int *status_r, const char **reason_r) { i_assert(resp != NULL); *status_r = resp->status; *reason_r = resp->reason; } uoff_t http_server_response_get_total_size(struct http_server_response *resp) { i_assert(resp != NULL); return resp->payload_size + str_len(resp->headers); } dovecot-2.2.33.2/src/lib-http/http-request.h0000644000175000017500000000400713165463624015470 00000000000000#ifndef HTTP_REQUEST_H #define HTTP_REQUEST_H #include "http-header.h" struct http_url; #define HTTP_REQUEST_DEFAULT_MAX_TARGET_LENGTH (8 * 1024) #define HTTP_REQUEST_DEFAULT_MAX_HEADER_SIZE (200 * 1024) #define HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELD_SIZE (8 * 1024) #define HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELDS 50 #define HTTP_REQUEST_DEFAULT_MAX_PAYLOAD_SIZE (1 * 1024 * 1024) struct http_request_limits { uoff_t max_target_length; uoff_t max_payload_size; struct http_header_limits header; }; enum http_request_target_format { HTTP_REQUEST_TARGET_FORMAT_ORIGIN = 0, HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, HTTP_REQUEST_TARGET_FORMAT_AUTHORITY, HTTP_REQUEST_TARGET_FORMAT_ASTERISK }; struct http_request_target { enum http_request_target_format format; struct http_url *url; }; struct http_request { const char *method; const char *target_raw; struct http_request_target target; unsigned char version_major; unsigned char version_minor; time_t date; const struct http_header *header; struct istream *payload; ARRAY_TYPE(const_string) connection_options; unsigned int connection_close:1; unsigned int expect_100_continue:1; }; static inline bool http_request_method_is(const struct http_request *req, const char *method) { if (req->method == NULL) return FALSE; return (strcmp(req->method, method) == 0); } static inline const struct http_header_field * http_request_header_find(const struct http_request *req, const char *name) { return http_header_field_find(req->header, name); } static inline const char * http_request_header_get(const struct http_request *req, const char *name) { return http_header_field_get(req->header, name); } static inline const ARRAY_TYPE(http_header_field) * http_request_header_get_fields(const struct http_request *req) { return http_header_get_fields(req->header); } bool http_request_has_connection_option(const struct http_request *req, const char *option); int http_request_get_payload_size(const struct http_request *req, uoff_t *size_r); #endif dovecot-2.2.33.2/src/lib-http/http-auth.h0000644000175000017500000000350413123174404014727 00000000000000#ifndef HTTP_AUTH_H #define HTTP_AUTH_H #include "array-decl.h" struct http_auth_param; struct http_auth_challenge; struct http_auth_credentials; ARRAY_DEFINE_TYPE(http_auth_param, struct http_auth_param); ARRAY_DEFINE_TYPE(http_auth_challenge, struct http_auth_challenge); struct http_auth_param { const char *name; const char *value; }; struct http_auth_challenge { const char *scheme; const char *data; ARRAY_TYPE(http_auth_param) params; }; struct http_auth_credentials { const char *scheme; const char *data; ARRAY_TYPE(http_auth_param) params; }; /* * Parsing */ int http_auth_parse_challenges(const unsigned char *data, size_t size, ARRAY_TYPE(http_auth_challenge) *chlngs); int http_auth_parse_credentials(const unsigned char *data, size_t size, struct http_auth_credentials *crdts); /* * Construction */ void http_auth_create_challenge(string_t *out, const struct http_auth_challenge *chlng); void http_auth_create_challenges(string_t *out, const ARRAY_TYPE(http_auth_challenge) *chlngs); void http_auth_create_credentials(string_t *out, const struct http_auth_credentials *crdts); /* * Manipulation */ void http_auth_challenge_copy(pool_t pool, struct http_auth_challenge *dst, const struct http_auth_challenge *src); struct http_auth_challenge * http_auth_challenge_clone(pool_t pool, const struct http_auth_challenge *src); void http_auth_credentials_copy(pool_t pool, struct http_auth_credentials *dst, const struct http_auth_credentials *src); struct http_auth_credentials * http_auth_credentials_clone(pool_t pool, const struct http_auth_credentials *src); /* * Simple schemes */ void http_auth_basic_challenge_init(struct http_auth_challenge *chlng, const char *realm); void http_auth_basic_credentials_init(struct http_auth_credentials *crdts, const char *username, const char *password); #endif dovecot-2.2.33.2/src/lib-http/http-client-host.c0000644000175000017500000002201613165463624016224 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "llist.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "dns-lookup.h" #include "http-response-parser.h" #include "http-client-private.h" #define HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS 100 /* * Logging */ static inline void http_client_host_debug(struct http_client_host *host, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_host_debug(struct http_client_host *host, const char *format, ...) { va_list args; if (host->client->set.debug) { va_start(args, format); i_debug("http-client: host %s: %s", host->name, t_strdup_vprintf(format, args)); va_end(args); } } /* * Host */ static void http_client_host_lookup_failure(struct http_client_host *host, const char *error) { struct http_client_queue *const *queue_idx; error = t_strdup_printf("Failed to lookup host %s: %s", host->name, error); array_foreach_modifiable(&host->queues, queue_idx) { http_client_queue_host_lookup_failure(*queue_idx, error); } http_client_host_check_idle(host); } static void http_client_host_dns_callback(const struct dns_lookup_result *result, struct http_client_host *host) { struct http_client *client = host->client; struct http_client_queue *const *queue_idx; unsigned int requests = 0; host->dns_lookup = NULL; if (result->ret != 0) { /* lookup failed */ http_client_host_lookup_failure(host, result->error); return; } http_client_host_debug(host, "DNS lookup successful; got %d IPs", result->ips_count); i_assert(result->ips_count > 0); i_free(host->ips); host->ips_count = result->ips_count; host->ips = i_new(struct ip_addr, host->ips_count); memcpy(host->ips, result->ips, sizeof(*host->ips) * host->ips_count); host->ips_timeout = ioloop_timeval; timeval_add_msecs(&host->ips_timeout, client->set.dns_ttl_msecs); /* make connections to requested ports */ array_foreach_modifiable(&host->queues, queue_idx) { requests += http_client_queue_host_lookup_done(*queue_idx); } if (requests == 0 && host->client->ioloop != NULL) io_loop_stop(host->client->ioloop); } static void http_client_host_lookup (struct http_client_host *host) { struct http_client *client = host->client; struct dns_lookup_settings dns_set; struct ip_addr *ips; int ret; i_assert(!host->explicit_ip); i_assert(host->dns_lookup == NULL); if (client->set.dns_client != NULL) { http_client_host_debug(host, "Performing asynchronous DNS lookup"); (void)dns_client_lookup(client->set.dns_client, host->name, http_client_host_dns_callback, host, &host->dns_lookup); } else if (client->set.dns_client_socket_path != NULL) { http_client_host_debug(host, "Performing asynchronous DNS lookup"); i_zero(&dns_set); dns_set.dns_client_socket_path = client->set.dns_client_socket_path; if (client->set.connect_timeout_msecs > 0) dns_set.timeout_msecs = client->set.connect_timeout_msecs; else if (client->set.request_timeout_msecs > 0) dns_set.timeout_msecs = client->set.request_timeout_msecs; else { dns_set.timeout_msecs = HTTP_CLIENT_DEFAULT_DNS_LOOKUP_TIMEOUT_MSECS; } (void)dns_lookup(host->name, &dns_set, http_client_host_dns_callback, host, &host->dns_lookup); } else { unsigned int ips_count; ret = net_gethostbyname(host->name, &ips, &ips_count); if (ret != 0) { http_client_host_lookup_failure(host, net_gethosterror(ret)); return; } http_client_host_debug(host, "DNS lookup successful; got %d IPs", ips_count); i_free(host->ips); host->ips_count = ips_count; host->ips = i_new(struct ip_addr, ips_count); memcpy(host->ips, ips, ips_count * sizeof(*ips)); host->ips_timeout = ioloop_timeval; timeval_add_msecs(&host->ips_timeout, client->set.dns_ttl_msecs); } } int http_client_host_refresh(struct http_client_host *host) { if (host->unix_local) return 0; if (host->explicit_ip) return 0; if (host->dns_lookup != NULL) return -1; if (host->ips_count > 0 && timeval_cmp(&host->ips_timeout, &ioloop_timeval) > 0) return 0; if (host->to_idle != NULL) return 0; http_client_host_debug(host, "IPs have expired; need to refresh DNS lookup"); http_client_host_lookup(host); if (host->dns_lookup != NULL) return -1; return (host->ips_count > 0 ? 1 : -1); } static struct http_client_host *http_client_host_create (struct http_client *client) { struct http_client_host *host; // FIXME: limit the maximum number of inactive cached hosts host = i_new(struct http_client_host, 1); host->client = client; i_array_init(&host->queues, 4); DLLIST_PREPEND(&client->hosts_list, host); return host; } struct http_client_host *http_client_host_get (struct http_client *client, const struct http_url *host_url) { struct http_client_host *host; if (host_url == NULL) { host = client->unix_host; if (host == NULL) { host = http_client_host_create(client); host->name = i_strdup("[unix]"); host->unix_local = TRUE; client->unix_host = host; http_client_host_debug(host, "Unix host created"); } } else { const char *hostname = host_url->host_name; struct ip_addr ip = host_url->host_ip; host = hash_table_lookup(client->hosts, hostname); if (host == NULL) { host = http_client_host_create(client); host->name = i_strdup(hostname); hostname = host->name; hash_table_insert(client->hosts, hostname, host); if (ip.family != 0 || net_addr2ip(host->name, &ip) == 0) { host->ips_count = 1; host->ips = i_new(struct ip_addr, host->ips_count); host->ips[0] = ip; host->explicit_ip = TRUE; } http_client_host_debug(host, "Host created"); } } return host; } void http_client_host_submit_request(struct http_client_host *host, struct http_client_request *req) { struct http_client_queue *queue; struct http_client_peer_addr addr; const char *error; req->host = host; http_client_request_get_peer_addr(req, &addr); if (http_client_peer_addr_is_https(&addr) && host->client->ssl_ctx == NULL) { if (http_client_init_ssl_ctx(host->client, &error) < 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, error); return; } } /* add request to queue (grouped by tcp port) */ queue = http_client_queue_create(host, &addr); http_client_queue_submit_request(queue, req); /* cancel host idle timeout */ if (host->to_idle != NULL) timeout_remove(&host->to_idle); if (host->unix_local) { http_client_queue_connection_setup(queue); return; } /* start DNS lookup if necessary */ if (host->ips_count == 0 && host->dns_lookup == NULL) http_client_host_lookup(host); /* make a connection if we have an IP already */ if (host->ips_count > 0) http_client_queue_connection_setup(queue); } void http_client_host_free(struct http_client_host **_host) { struct http_client_host *host = *_host; struct http_client_queue *const *queue_idx; ARRAY_TYPE(http_client_queue) queues; const char *hostname = host->name; http_client_host_debug(host, "Host destroy"); if (host->to_idle != NULL) timeout_remove(&host->to_idle); DLLIST_REMOVE(&host->client->hosts_list, host); if (host == host->client->unix_host) host->client->unix_host = NULL; else hash_table_remove(host->client->hosts, hostname); if (host->dns_lookup != NULL) dns_lookup_abort(&host->dns_lookup); /* drop request queues */ t_array_init(&queues, array_count(&host->queues)); array_copy(&queues.arr, 0, &host->queues.arr, 0, array_count(&host->queues)); array_clear(&host->queues); array_foreach(&queues, queue_idx) { http_client_queue_free(*queue_idx); } array_free(&host->queues); i_free(host->ips); i_free(host->name); i_free(host); } static void http_client_host_idle_timeout(struct http_client_host *host) { http_client_host_debug(host, "Idle host timed out"); http_client_host_free(&host); } void http_client_host_check_idle(struct http_client_host *host) { struct http_client_queue *const *queue_idx; unsigned int requests = 0; int timeout = 0; if (host->to_idle != NULL) return; array_foreach(&host->queues, queue_idx) { requests += http_client_queue_requests_active(*queue_idx); } if (requests > 0) return; if (!host->unix_local && !host->explicit_ip && host->ips_timeout.tv_sec > 0) { timeout = timeval_diff_msecs (&host->ips_timeout, &ioloop_timeval); } if (timeout <= HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS) timeout = HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS; host->to_idle = timeout_add_short(timeout, http_client_host_idle_timeout, host); http_client_host_debug(host, "Host is idle (timeout = %u msecs)", timeout); } void http_client_host_switch_ioloop(struct http_client_host *host) { struct http_client_queue *const *queue_idx; if (host->dns_lookup != NULL && host->client->set.dns_client == NULL) dns_lookup_switch_ioloop(host->dns_lookup); array_foreach(&host->queues, queue_idx) http_client_queue_switch_ioloop(*queue_idx); if (host->to_idle != NULL) host->to_idle = io_loop_move_timeout(&host->to_idle); } dovecot-2.2.33.2/src/lib-http/test-http-client.c0000644000175000017500000003172013165463624016230 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "safe-memset.h" #include "ioloop.h" #include "istream.h" #include "write-full.h" #include "http-url.h" #include "http-client.h" #include "dns-lookup.h" #include "iostream-ssl.h" #include "iostream-openssl.h" struct http_test_request { struct io *io; struct istream *payload; bool write_output; }; static void payload_input(struct http_test_request *req) { const unsigned char *data; size_t size; int ret; /* read payload */ while ((ret=i_stream_read_data(req->payload, &data, &size, 0)) > 0) { i_info("DEBUG: got data (size=%d)", (int)size); if (req->write_output) if (write_full(1, data, size) < 0) i_error("REQUEST PAYLOAD WRITE ERROR: %m"); i_stream_skip(req->payload, size); } if (ret == 0) { i_info("DEBUG: REQUEST: NEED MORE DATA"); /* we will be called again for this request */ } else { if (req->payload->stream_errno != 0) { i_error("REQUEST PAYLOAD READ ERROR: %s", i_stream_get_error(req->payload)); } else i_info("DEBUG: REQUEST: Finished"); io_remove(&req->io); i_stream_unref(&req->payload); i_free(req); } } static void got_request_response(const struct http_response *response, struct http_test_request *req) { if (response->status / 100 != 2) { i_error("HTTP Request failed: %s", response->reason); i_free(req); /* payload (if any) is skipped implicitly */ return; } i_info("DEBUG: REQUEST SUCCEEDED: %s", response->reason); if (response->payload == NULL) { i_free(req); return; } i_info("DEBUG: REQUEST: Got payload"); i_stream_ref(response->payload); req->payload = response->payload; req->io = io_add_istream(response->payload, payload_input, req); payload_input(req); } static const char *test_query1 = "data=Frop&submit=Submit"; static const char *test_query2 = "data=This%20is%20a%20test&submit=Submit"; static const char *test_query3 = "foo=bar"; static void run_tests(struct http_client *http_client) { struct http_client_request *http_req; struct http_test_request *test_req; struct istream *post_payload; // JigSAW is useful for testing: http://jigsaw.w3.org/HTTP/ test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/download.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "jigsaw.w3.org", "/HTTP/300/301.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/frop.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "jigsaw.w3.org", "/HTTP/300/307.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/documentation.html", got_request_response, test_req); http_client_request_set_urgent(http_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "jigsaw.w3.org", "/HTTP/300/302.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "test.dovecot.org", "/http/post/index.php", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query1, strlen(test_query1)); http_client_request_set_payload(http_req, post_payload, FALSE); i_stream_unref(&post_payload); http_client_request_add_header(http_req, "Content-Type", "application/x-www-form-urlencoded"); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "test.dovecot.org", "/http/post/index.php", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query2, strlen(test_query2)); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_add_header(http_req, "Content-Type", "application/x-www-form-urlencoded"); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/", got_request_response, test_req); http_client_request_set_port(http_req, 81); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "HEAD", "pigeonhole.dovecot.org", "/download.html", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/", got_request_response, test_req); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/download.html", got_request_response, test_req); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "pigeonhole.dovecot.org", "/documentation.html", got_request_response, test_req); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); http_client_request_abort(&http_req); i_free(test_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "posttestserver.com", "/post.php", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query1, strlen(test_query1)); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "posttestserver.com", "/post.php", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query1, strlen(test_query1)); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "posttestserver.com", "/post.php", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query1, strlen(test_query1)); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_set_ssl(http_req, TRUE); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "wiki2.dovecot.org", "/Pigeonhole", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "jigsaw.w3.org", "/HTTP/ChunkedScript", got_request_response, test_req); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "jigsaw.w3.org", "/HTTP/300/Go_307", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query3, strlen(test_query3)); http_client_request_set_payload(http_req, post_payload, FALSE); i_stream_unref(&post_payload); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "jigsaw.w3.org", "/HTTP/300/Go_307", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query3, strlen(test_query3)); http_client_request_set_payload(http_req, post_payload, FALSE); i_stream_unref(&post_payload); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "POST", "jigsaw.w3.org", "/HTTP/300/Go_307", got_request_response, test_req); post_payload = i_stream_create_from_data ((unsigned char *)test_query3, strlen(test_query3)); http_client_request_set_payload(http_req, post_payload, FALSE); i_stream_unref(&post_payload); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "GET", "jigsaw.w3.org", "/HTTP/Basic/", got_request_response, test_req); http_client_request_set_auth_simple (http_req, "guest", "guest"); http_client_request_submit(http_req); test_req = i_new(struct http_test_request, 1); http_req = http_client_request(http_client, "PUT", "test.dovecot.org", "/http/put/put.php", got_request_response, test_req); post_payload = i_stream_create_file("Makefile.am", 10); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_submit(http_req); } static void test_http_request_init(struct http_client *http_client, const char *method, const char *url_str, struct http_client_request **http_req_r, struct http_test_request **test_req_r) { struct http_client_request *http_req; struct http_test_request *test_req; struct http_url *url; const char *error; if (http_url_parse(url_str, NULL, 0, pool_datastack_create(), &url, &error) < 0) i_fatal("Invalid URL %s: %s", url_str, error); test_req = i_new(struct http_test_request, 1); test_req->write_output = TRUE; http_req = http_client_request(http_client, method, url->host_name, t_strconcat("/", url->path, url->enc_query, NULL), got_request_response, test_req); if (url->have_port) http_client_request_set_port(http_req, url->port); if (url->have_ssl) http_client_request_set_ssl(http_req, TRUE); *http_req_r = http_req; *test_req_r = test_req; } static void run_http_get(struct http_client *http_client, const char *url_str) { struct http_client_request *http_req; struct http_test_request *test_req; test_http_request_init(http_client, "GET", url_str, &http_req, &test_req); http_client_request_submit(http_req); } static void run_http_post(struct http_client *http_client, const char *url_str, const char *path) { struct http_client_request *http_req; struct http_test_request *test_req; struct istream *input; test_http_request_init(http_client, "POST", url_str, &http_req, &test_req); input = i_stream_create_file(path, IO_BLOCK_SIZE); http_client_request_set_payload(http_req, input, FALSE); i_stream_unref(&input); http_client_request_submit(http_req); } int main(int argc, char *argv[]) { struct dns_client *dns_client; struct dns_lookup_settings dns_set; struct http_client_settings http_set; struct http_client *http_client; const char *error; struct ioloop *ioloop; lib_init(); ssl_iostream_openssl_init(); ioloop = io_loop_create(); io_loop_set_running(ioloop); /* kludge: use safe_memset() here since otherwise it's not included in the binary in all systems (but is in others! so linking safe-memset.lo directly causes them to fail.) If safe_memset() isn't included, libssl-iostream plugin loading fails. */ i_zero_safe(&dns_set); dns_set.dns_client_socket_path = "/var/run/dovecot/dns-client"; dns_set.timeout_msecs = 30*1000; dns_set.idle_timeout_msecs = UINT_MAX; dns_client = dns_client_init(&dns_set); if (dns_client_connect(dns_client, &error) < 0) i_fatal("Couldn't initialize DNS client: %s", error); i_zero(&http_set); http_set.dns_client = dns_client; http_set.ssl_allow_invalid_cert = TRUE; http_set.ssl_ca_dir = "/etc/ssl/certs"; /* debian */ http_set.ssl_ca_file = "/etc/pki/tls/cert.pem"; /* redhat */ http_set.max_idle_time_msecs = 5*1000; http_set.max_parallel_connections = 4; http_set.max_pipelined_requests = 4; http_set.max_redirects = 2; http_set.request_timeout_msecs = 10*1000; http_set.max_attempts = 1; http_set.debug = TRUE; http_set.rawlog_dir = "/tmp/http-test"; http_client = http_client_init(&http_set); switch (argc) { case 1: run_tests(http_client); break; case 2: run_http_get(http_client, argv[1]); break; case 3: run_http_post(http_client, argv[1], argv[2]); break; default: i_fatal("Too many parameters"); } http_client_wait(http_client); http_client_deinit(&http_client); dns_client_deinit(&dns_client); io_loop_destroy(&ioloop); ssl_iostream_openssl_deinit(); lib_deinit(); } dovecot-2.2.33.2/src/lib-http/http-parser.c0000644000175000017500000001357213123174404015263 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "strescape.h" #include "http-url.h" #include "http-parser.h" /* Character definitions: tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA ; any VCHAR, except special special = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / DQUOTE / "/" / "[" / "]" / "?" / "=" / "{" / "}" qdtext = OWS / %x21 / %x23-5B / %x5D-7E / obs-text qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text ctext = OWS / %x21-27 / %x2A-5B / %x5D-7E / obs-text obs-text = %x80-FF OWS = *( SP / HTAB ) VCHAR = %x21-7E 't68char' = ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" 'text' = ( HTAB / SP / VCHAR / obs-text ) Character bit mappings: (1<<0) => ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" (1<<1) => "!" / "#" / "$" / "%" / "&" / "'" / "*" / "^" / "`" / "|" (1<<2) => special (1<<3) => %x21 / %x2A-5B / %x5D-7E (1<<4) => %x23-29 (1<<5) => %x22-27 (1<<6) => HTAB / SP / obs-text (1<<7) => "/" */ const unsigned char _http_token_char_mask = (1<<0)|(1<<1); const unsigned char _http_value_char_mask = (1<<0)|(1<<1)|(1<<2); const unsigned char _http_text_char_mask = (1<<0)|(1<<1)|(1<<2)|(1<<6); const unsigned char _http_qdtext_char_mask = (1<<3)|(1<<4)|(1<<6); const unsigned char _http_ctext_char_mask = (1<<3)|(1<<5)|(1<<6); const unsigned char _http_token68_char_mask = (1<<0)|(1<<7); const unsigned char _http_char_lookup[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, // 00 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 64, 10, 36, 50, 50, 50, 50, 50, 20, 20, 10, 9, 12, 9, 9, 140, // 20 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 12, 12, 12, 12, 12, 12, // 30 12, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 40 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 12, 4, 12, 10, 9, // 50 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 60 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 12, 10, 12, 9, 0, // 70 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 80 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 90 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // A0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // B0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // C0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // D0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // E0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // F0 }; /* * HTTP value parsing */ void http_parser_init(struct http_parser *parser, const unsigned char *data, size_t size) { i_zero(parser); parser->begin = data; parser->cur = data; parser->end = data + size; } void http_parse_ows(struct http_parser *parser) { /* OWS = *( SP / HTAB ) */ if (parser->cur >= parser->end) return; while (parser->cur < parser->end && (parser->cur[0] == ' ' || parser->cur[0] == '\t')) { parser->cur++; } } int http_parser_skip_token(struct http_parser *parser) { /* token = 1*tchar */ if (parser->cur >= parser->end || !http_char_is_token(*parser->cur)) return 0; parser->cur++; while (parser->cur < parser->end && http_char_is_token(*parser->cur)) parser->cur++; return 1; } int http_parse_token(struct http_parser *parser, const char **token_r) { const unsigned char *first = parser->cur; int ret; if ((ret=http_parser_skip_token(parser)) <= 0) return ret; *token_r = t_strndup(first, parser->cur - first); return 1; } int http_parse_token_list_next(struct http_parser *parser, const char **token_r) { /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21; Appendix B: For compatibility with legacy list rules, recipients SHOULD accept empty list elements. In other words, consumers would follow the list productions: #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ] 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] ) */ for (;;) { if (http_parse_token(parser, token_r) > 0) break; http_parse_ows(parser); if (parser->cur >= parser->end || parser->cur[0] != ',') return 0; parser->cur++; http_parse_ows(parser); } return 1; } int http_parse_quoted_string(struct http_parser *parser, const char **str_r) { string_t *str; /* quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) obs-text = %x80-FF */ /* DQUOTE */ if (parser->cur >= parser->end || parser->cur[0] != '"') return 0; parser->cur++; /* *( qdtext / quoted-pair ) */ str = t_str_new(256); for (;;) { const unsigned char *first; /* *qdtext */ first = parser->cur; while (parser->cur < parser->end && http_char_is_qdtext(*parser->cur)) parser->cur++; if (parser->cur >= parser->end) return -1; str_append_n(str, first, parser->cur - first); /* DQUOTE */ if (*parser->cur == '"') { parser->cur++; break; /* "\" */ } else if (*parser->cur == '\\') { parser->cur++; if (parser->cur >= parser->end || !http_char_is_text(*parser->cur)) return -1; str_append_c(str, *parser->cur); parser->cur++; /* ERROR */ } else { return -1; } } *str_r = str_c(str); return 1; } int http_parse_token_or_qstring(struct http_parser *parser, const char **word_r) { if (parser->cur >= parser->end) return 0; if (parser->cur[0] == '"') return http_parse_quoted_string(parser, word_r); return http_parse_token(parser, word_r); } dovecot-2.2.33.2/src/lib-http/http-message-parser.h0000644000175000017500000000445113165463624016721 00000000000000#ifndef HTTP_MESSAGE_PARSER_H #define HTTP_MESSAGE_PARSER_H #include "http-response.h" #include "http-transfer.h" #include "http-header.h" enum http_message_parse_error { HTTP_MESSAGE_PARSE_ERROR_NONE = 0, /* no error */ HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM, /* stream error */ HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE, /* unrecoverable generic error */ HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE, /* recoverable generic error */ HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature (recoverable) */ HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE /* message payload is too large (fatal) */ }; enum http_message_parse_flags { /* Strictly adhere to the HTTP protocol specification */ HTTP_MESSAGE_PARSE_FLAG_STRICT = BIT(0) }; struct http_message { pool_t pool; unsigned int version_major; unsigned int version_minor; struct http_header *header; time_t date; uoff_t content_length; const char *location; ARRAY_TYPE(http_transfer_coding) transfer_encoding; ARRAY_TYPE(const_string) connection_options; unsigned int connection_close:1; unsigned int have_content_length:1; }; struct http_message_parser { struct istream *input; struct http_header_limits header_limits; uoff_t max_payload_size; enum http_message_parse_flags flags; const unsigned char *cur, *end; const char *error; enum http_message_parse_error error_code; struct http_header_parser *header_parser; struct istream *payload; pool_t msg_pool; struct http_message msg; }; void http_message_parser_init(struct http_message_parser *parser, struct istream *input, const struct http_header_limits *hdr_limits, uoff_t max_payload_size, enum http_message_parse_flags flags) ATTR_NULL(3); void http_message_parser_deinit(struct http_message_parser *parser); void http_message_parser_restart(struct http_message_parser *parser, pool_t pool); pool_t http_message_parser_get_pool(struct http_message_parser *parser); int http_message_parse_finish_payload(struct http_message_parser *parser); int http_message_parse_version(struct http_message_parser *parser); int http_message_parse_headers(struct http_message_parser *parser); int http_message_parse_body(struct http_message_parser *parser, bool request); #endif dovecot-2.2.33.2/src/lib-http/http-response.h0000644000175000017500000000360613165463624015642 00000000000000#ifndef HTTP_RESPONSE_H #define HTTP_RESPONSE_H #include "array.h" #include "http-header.h" #define http_response_header http_header_field /* FIXME: remove in v2.3 */ #define HTTP_RESPONSE_STATUS_INTERNAL 9000 enum http_response_payload_type { HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT, HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL }; struct http_response { unsigned char version_major; unsigned char version_minor; unsigned int status; const char *reason; const char *location; time_t date, retry_after; const struct http_header *header; struct istream *payload; /* FIXME: remove in v2.3 */ ARRAY_TYPE(http_header_field) headers; ARRAY_TYPE(const_string) connection_options; unsigned int connection_close:1; }; void http_response_init(struct http_response *resp, unsigned int status, const char *reason); static inline const struct http_header_field * http_response_header_find(const struct http_response *resp, const char *name) { if (resp->header == NULL) return NULL; return http_header_field_find(resp->header, name); } static inline const char * http_response_header_get(const struct http_response *resp, const char *name) { if (resp->header == NULL) return NULL; return http_header_field_get(resp->header, name); } static inline const ARRAY_TYPE(http_header_field) * http_response_header_get_fields(const struct http_response *resp) { if (resp->header == NULL) return NULL; return http_header_get_fields(resp->header); } static inline const char * http_response_get_message(const struct http_response *resp) { if (resp->status >= HTTP_RESPONSE_STATUS_INTERNAL) return resp->reason; return t_strdup_printf("%u %s", resp->status, resp->reason); } bool http_response_has_connection_option(const struct http_response *resp, const char *option); int http_response_get_payload_size(const struct http_response *resp, uoff_t *size_r); #endif dovecot-2.2.33.2/src/lib-http/http-client.h0000644000175000017500000004146513165463624015267 00000000000000#ifndef HTTP_CLIENT_H #define HTTP_CLIENT_H #include "net.h" #include "http-response.h" struct timeval; struct http_response; struct http_client; struct http_client_request; /* * Client settings */ struct http_client_settings { /* a) If dns_client is set, all lookups are done via it. b) If dns_client_socket_path is set, each DNS lookup does its own dns-lookup UNIX socket connection. c) Otherwise, blocking gethostbyname() lookups are used. */ struct dns_client *dns_client; const char *dns_client_socket_path; unsigned int dns_ttl_msecs; /* ssl configuration */ const char *ssl_ca_dir, *ssl_ca_file, *ssl_ca; const char *ssl_crypto_device; bool ssl_allow_invalid_cert; /* user cert */ const char *ssl_cert, *ssl_key, *ssl_key_password; /* User-Agent: header (default: none) */ const char *user_agent; /* proxy on unix socket */ const char *proxy_socket_path; /* URL for normal proxy (ignored if proxy_socket_path is set) */ const struct http_url *proxy_url; /* credentials for proxy */ const char *proxy_username; const char *proxy_password; /* directory for writing raw log data for debugging purposes */ const char *rawlog_dir; /* maximum time a connection will idle. if parallel connections are idle, the duplicates will end earlier based on how many idle connections exist to that same service */ unsigned int max_idle_time_msecs; /* maximum number of parallel connections per peer (default = 1) */ unsigned int max_parallel_connections; /* maximum number of pipelined requests per connection (default = 1) */ unsigned int max_pipelined_requests; /* don't automatically act upon redirect responses */ bool no_auto_redirect; /* never automatically retry requests */ bool no_auto_retry; /* if we use a proxy, delegate SSL negotiation to proxy, rather than creating a CONNECT tunnel through the proxy for the SSL link */ bool no_ssl_tunnel; /* maximum number of redirects for a request (default = 0; redirects refused) */ unsigned int max_redirects; /* maximum number of attempts for a request */ unsigned int max_attempts; /* maximum number of connection attempts to a host before all associated requests fail. if > 1, the maximum will be enforced across all IPs for that host, meaning that IPs may be tried more than once eventually if the number of IPs is smaller than the specified maximum attempts. If the number of IPs is higher than the maximum attempts, not all IPs are tried. If <= 1, all IPs are tried at most once. */ unsigned int max_connect_attempts; /* Initial backoff time; doubled at each connection failure */ unsigned int connect_backoff_time_msecs; /* Maximum backoff time */ unsigned int connect_backoff_max_time_msecs; /* response header limits */ struct http_header_limits response_hdr_limits; /* max total time to wait for HTTP request to finish this can be overridden/reset for individual requests using http_client_request_set_timeout() and friends. (default is no timeout) */ unsigned int request_absolute_timeout_msecs; /* max time to wait for HTTP request to finish before retrying (default = unlimited) */ unsigned int request_timeout_msecs; /* max time to wait for connect() (and SSL handshake) to finish before retrying (default = request_timeout_msecs) */ unsigned int connect_timeout_msecs; /* time to wait for connect() (and SSL handshake) to finish for the first connection before trying the next IP in parallel (default = 0; wait until current connection attempt finishes) */ unsigned int soft_connect_timeout_msecs; /* maximum acceptable delay in seconds for automatically retrying/redirecting requests. if a server sends a response with a Retry-After header that causes a delay longer than this, the request is not automatically retried and the response is returned */ unsigned int max_auto_retry_delay; /* the kernel send/receive buffer sizes used for the connection sockets. Configuring this is mainly useful for the test suite. The kernel defaults are used when these settings are 0. */ size_t socket_send_buffer_size; size_t socket_recv_buffer_size; /* enable logging debug messages */ bool debug; }; /* * Request */ enum http_client_request_error { /* The request was aborted */ HTTP_CLIENT_REQUEST_ERROR_ABORTED = HTTP_RESPONSE_STATUS_INTERNAL, /* Failed to parse HTTP target url */ HTTP_CLIENT_REQUEST_ERROR_INVALID_URL, /* Failed to perform DNS lookup for the host */ HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED, /* Failed to setup any connection for the host and client settings allowed no more attempts */ HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, /* Service returned an invalid redirect response for this request */ HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, /* The connection was lost unexpectedly while handling the request and client settings allowed no more attempts */ HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, /* The input stream passed to the request using http_client_request_set_payload() returned an error while sending the request. */ HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD, /* The service returned a bad response */ HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, /* The request timed out (either this was the last attempt or the absolute timeout was hit) */ HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT, }; enum http_request_state { /* New request; not yet submitted */ HTTP_REQUEST_STATE_NEW = 0, /* Request is queued; waiting for a connection */ HTTP_REQUEST_STATE_QUEUED, /* Request header is sent; still sending request payload to server */ HTTP_REQUEST_STATE_PAYLOAD_OUT, /* Request is fully sent; waiting for response */ HTTP_REQUEST_STATE_WAITING, /* Response header is received for the request */ HTTP_REQUEST_STATE_GOT_RESPONSE, /* Reading response payload; response handler still needs to read more payload. */ HTTP_REQUEST_STATE_PAYLOAD_IN, /* Request is finished; still lingering due to references */ HTTP_REQUEST_STATE_FINISHED, /* Request is aborted; still lingering due to references */ HTTP_REQUEST_STATE_ABORTED }; extern const char *http_request_state_names[]; struct http_client_tunnel { int fd_in, fd_out; struct istream *input; struct ostream *output; }; struct http_client_request_stats { /* Total elapsed time since message was submitted */ unsigned int total_msecs; /* Elapsed time since message was first sent */ unsigned int first_sent_msecs; /* Elapsed time since message was last sent */ unsigned int last_sent_msecs; /* Time spent in other ioloops */ unsigned int other_ioloop_msecs; /* Time spent in the http-client's own ioloop */ unsigned int http_ioloop_msecs; /* Total time spent on waiting for file locks */ unsigned int lock_msecs; /* Number of attempts for this request */ unsigned int attempts; }; typedef void http_client_request_callback_t(const struct http_response *response, void *context); /* create new HTTP request */ struct http_client_request * http_client_request(struct http_client *client, const char *method, const char *host, const char *target, http_client_request_callback_t *callback, void *context); #define http_client_request(client, method, host, target, callback, context) \ http_client_request(client, method, host, target + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) /* create net HTTP request using provided URL. This implicitly sets port, ssl, and username:password if provided. */ struct http_client_request * http_client_request_url(struct http_client *client, const char *method, const struct http_url *target_url, http_client_request_callback_t *callback, void *context); #define http_client_request_url(client, method, target_url, callback, context) \ http_client_request_url(client, method, target_url + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) struct http_client_request * http_client_request_url_str(struct http_client *client, const char *method, const char *url_str, http_client_request_callback_t *callback, void *context); #define http_client_request_url_str(client, method, url_str, callback, context) \ http_client_request_url_str(client, method, url_str + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) /* create new HTTP CONNECT request. If this HTTP is configured to use a proxy, a CONNECT request will be submitted at that proxy, otherwise the connection is created directly. Call http_client_request_start_tunnel() to to take over the connection. */ struct http_client_request * http_client_request_connect(struct http_client *client, const char *host, in_port_t port, http_client_request_callback_t *callback, void *context); #define http_client_request_connect(client, host, port, callback, context) \ http_client_request_connect(client, host, port + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) /* same as http_client_request_connect, but uses an IP rather than a host name. */ struct http_client_request * http_client_request_connect_ip(struct http_client *client, const struct ip_addr *ip, in_port_t port, http_client_request_callback_t *callback, void *context); #define http_client_request_connect_ip(client, ip, port, callback, context) \ http_client_request_connect_ip(client, ip, port + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) /* set the port for the service the request is directed at */ void http_client_request_set_port(struct http_client_request *req, in_port_t port); /* indicate whether service the request is directed at uses ssl */ void http_client_request_set_ssl(struct http_client_request *req, bool ssl); /* set the urgent flag: this means that this request will get priority over non-urgent request. Also, if no idle connection is available, a new connection is created. Urgent requests are never pipelined. */ void http_client_request_set_urgent(struct http_client_request *req); void http_client_request_set_preserve_exact_reason(struct http_client_request *req); /* add a custom header to the request. This can override headers that are otherwise created implicitly. */ void http_client_request_add_header(struct http_client_request *req, const char *key, const char *value); /* remove a header added earlier. This has no influence on implicitly created headers. */ void http_client_request_remove_header(struct http_client_request *req, const char *key); /* set the value of the "Date" header for the request using a time_t value. Use this instead of setting it directly using http_client_request_add_header() */ void http_client_request_set_date(struct http_client_request *req, time_t date); /* assign an input stream for the outgoing payload of this request. The input stream is read asynchronously while the request is sent to the server. when sync=TRUE a "100 Continue" response is requested from the service. The client will then postpone sending the payload until a provisional response with code 100 is received. This way, an error response can be sent by the service before any potentially big payload is transmitted. Use this only for payload that can be large. */ void http_client_request_set_payload(struct http_client_request *req, struct istream *input, bool sync); /* assign payload data to the request. The data is copied to the request pool. If your data is already durably allocated during the existence of the request, you should consider using http_client_request_set_payload() with a data input stream instead. This will avoid copying the data unnecessarily. */ void http_client_request_set_payload_data(struct http_client_request *req, const unsigned char *data, size_t size); /* set an absolute timeout for this request specifically, overriding the default client-wide absolute request timeout */ void http_client_request_set_timeout_msecs(struct http_client_request *req, unsigned int msecs); void http_client_request_set_timeout(struct http_client_request *req, const struct timeval *time); /* Override http_client_settings.request_timeout_msecs */ void http_client_request_set_attempt_timeout_msecs(struct http_client_request *req, unsigned int msecs); /* Override http_client_settings.max_attempts */ void http_client_request_set_max_attempts(struct http_client_request *req, unsigned int max_attempts); /* set the username:password credentials for this request for simple authentication. This function is meant for simple schemes that use a password. More complex schemes will need to be handled manually. This currently only supports the "basic" authentication scheme. */ void http_client_request_set_auth_simple(struct http_client_request *req, const char *username, const char *password); /* Assign a proxy to use for this particular request. This overrides any proxy defined in the client settings. */ void http_client_request_set_proxy_url(struct http_client_request *req, const struct http_url *proxy_url); /* Like http_client_request_set_proxy_url(), but the proxy is behind a unix socket. */ void http_client_request_set_proxy_socket(struct http_client_request *req, const char *proxy_socket); /* delay handling of this request to a later time. This way, a request can be submitted that is held for some time until a certain time period has passed. */ void http_client_request_delay_until(struct http_client_request *req, time_t time); void http_client_request_delay(struct http_client_request *req, time_t seconds); void http_client_request_delay_msecs(struct http_client_request *req, unsigned int msecs); /* return the HTTP method for the request */ const char * http_client_request_get_method(const struct http_client_request *req) ATTR_PURE; /* return the HTTP target for the request */ const char * http_client_request_get_target(const struct http_client_request *req) ATTR_PURE; /* return the request state */ enum http_request_state http_client_request_get_state(const struct http_client_request *req) ATTR_PURE; /* get statistics for the request */ void http_client_request_get_stats(struct http_client_request *req, struct http_client_request_stats *stats); /* append text with request statistics to provided string buffer */ void http_client_request_append_stats_text(struct http_client_request *req, string_t *str); /* submit the request. It is queued for transmission to the service */ void http_client_request_submit(struct http_client_request *req); /* attempt to retry the request. This function is called within the request callback. It returns false if the request cannot be retried */ bool http_client_request_try_retry(struct http_client_request *req); /* abort the request immediately. It may still linger for a while when it is already sent to the service, but the callback will not be called anymore. */ void http_client_request_abort(struct http_client_request **req); /* call the specified callback when HTTP request is destroyed. */ void http_client_request_set_destroy_callback(struct http_client_request *req, void (*callback)(void *), void *context); /* submits request and blocks until the provided payload is sent. Multiple calls are allowed; payload transmission is ended with http_client_request_finish_payload(). If the sending fails, returns -1 and sets req=NULL to indicate that the request was freed, otherwise returns 0 and req is unchanged. */ int http_client_request_send_payload(struct http_client_request **req, const unsigned char *data, size_t size); /* finish sending the payload. Always frees req and sets it to NULL. Returns 0 on success, -1 on error. */ int http_client_request_finish_payload(struct http_client_request **req); /* take over the connection this request was sent over for use as a HTTP CONNECT tunnel. This only applies to requests that were created using http_client_request_connect() or http_client_request_connect_ip(). */ void http_client_request_start_tunnel(struct http_client_request *req, struct http_client_tunnel *tunnel); /* * Client */ struct http_client *http_client_init(const struct http_client_settings *set); void http_client_deinit(struct http_client **_client); /* switch this client to the current ioloop */ void http_client_switch_ioloop(struct http_client *client); /* blocks until all currently submitted requests are handled */ void http_client_wait(struct http_client *client); /* Returns the total number of pending HTTP requests. */ unsigned int http_client_get_pending_request_count(struct http_client *client); #endif dovecot-2.2.33.2/src/lib-http/http-client-peer.c0000644000175000017500000006017613165463624016213 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "time-util.h" #include "str.h" #include "hash.h" #include "array.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "iostream-ssl.h" #include "http-response-parser.h" #include "http-client-private.h" /* * Logging */ static inline void http_client_peer_debug(struct http_client_peer *peer, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_peer_debug(struct http_client_peer *peer, const char *format, ...) { va_list args; if (peer->client->set.debug) { va_start(args, format); i_debug("http-client: peer %s: %s", http_client_peer_label(peer), t_strdup_vprintf(format, args)); va_end(args); } } /* * Peer address */ unsigned int http_client_peer_addr_hash (const struct http_client_peer_addr *peer) { unsigned int hash = (unsigned int)peer->type; switch (peer->type) { case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: if (peer->a.tcp.https_name != NULL) hash += str_hash(peer->a.tcp.https_name); /* fall through */ case HTTP_CLIENT_PEER_ADDR_RAW: case HTTP_CLIENT_PEER_ADDR_HTTP: if (peer->a.tcp.ip.family != 0) hash += net_ip_hash(&peer->a.tcp.ip); hash += peer->a.tcp.port; break; case HTTP_CLIENT_PEER_ADDR_UNIX: hash += str_hash(peer->a.un.path); break; } return hash; } int http_client_peer_addr_cmp (const struct http_client_peer_addr *peer1, const struct http_client_peer_addr *peer2) { int ret; if (peer1->type != peer2->type) return (peer1->type > peer2->type ? 1 : -1); switch (peer1->type) { case HTTP_CLIENT_PEER_ADDR_RAW: case HTTP_CLIENT_PEER_ADDR_HTTP: case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: /* Queues are created with peer addresses that have an uninitialized IP value, because that is assigned later when the host lookup completes. In all other other contexts, the IP is always initialized, so we do not compare IPs when one of them is unassigned. */ if (peer1->a.tcp.ip.family != 0 && peer2->a.tcp.ip.family != 0 && (ret=net_ip_cmp(&peer1->a.tcp.ip, &peer2->a.tcp.ip)) != 0) return ret; if (peer1->a.tcp.port != peer2->a.tcp.port) return (peer1->a.tcp.port > peer2->a.tcp.port ? 1 : -1); if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS && peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) return 0; return null_strcmp (peer1->a.tcp.https_name, peer2->a.tcp.https_name); case HTTP_CLIENT_PEER_ADDR_UNIX: return null_strcmp(peer1->a.un.path, peer2->a.un.path); } i_unreached(); return 0; } /* * Peer */ static void http_client_peer_drop(struct http_client_peer **_peer); const char * http_client_peer_label(struct http_client_peer *peer) { if (peer->label == NULL) { switch (peer->addr.type) { case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: peer->label = i_strconcat (http_client_peer_addr2str(&peer->addr), " (tunnel)", NULL); break; default: peer->label = i_strdup (http_client_peer_addr2str(&peer->addr)); } } return peer->label; } static void http_client_peer_do_connect(struct http_client_peer *peer, unsigned int count) { unsigned int i; for (i = 0; i < count; i++) { http_client_peer_debug(peer, "Making new connection %u of %u", i+1, count); (void)http_client_connection_create(peer); } } static void http_client_peer_connect_backoff(struct http_client_peer *peer) { i_assert(peer->to_backoff != NULL); http_client_peer_debug(peer, "Backoff timer expired"); timeout_remove(&peer->to_backoff); if (array_count(&peer->queues) == 0) { http_client_peer_close(&peer); return; } http_client_peer_do_connect(peer, 1); } static bool http_client_peer_start_backoff_timer(struct http_client_peer *peer) { if (peer->to_backoff != NULL) return TRUE; if (peer->last_failure.tv_sec > 0) { int backoff_time_spent = timeval_diff_msecs(&ioloop_timeval, &peer->last_failure); if (backoff_time_spent < (int)peer->backoff_time_msecs) { http_client_peer_debug(peer, "Starting backoff timer for %d msecs", peer->backoff_time_msecs - backoff_time_spent); peer->to_backoff = timeout_add ((unsigned int)(peer->backoff_time_msecs - backoff_time_spent), http_client_peer_connect_backoff, peer); return TRUE; } http_client_peer_debug(peer, "Backoff time already exceeded by %d msecs", backoff_time_spent - peer->backoff_time_msecs); } return FALSE; } static void http_client_peer_increase_backoff_timer(struct http_client_peer *peer) { const struct http_client_settings *set = &peer->client->set; if (peer->backoff_time_msecs == 0) peer->backoff_time_msecs = set->connect_backoff_time_msecs; else peer->backoff_time_msecs *= 2; if (peer->backoff_time_msecs > set->connect_backoff_max_time_msecs) peer->backoff_time_msecs = set->connect_backoff_max_time_msecs; } static void http_client_peer_reset_backoff_timer(struct http_client_peer *peer) { peer->backoff_time_msecs = 0; if (peer->to_backoff != NULL) timeout_remove(&peer->to_backoff); } static void http_client_peer_connect(struct http_client_peer *peer, unsigned int count) { if (http_client_peer_start_backoff_timer(peer)) return; http_client_peer_do_connect(peer, count); } bool http_client_peer_is_connected(struct http_client_peer *peer) { struct http_client_connection *const *conn_idx; array_foreach(&peer->conns, conn_idx) { if ((*conn_idx)->connected) return TRUE; } return FALSE; } static void http_client_peer_cancel(struct http_client_peer *peer) { struct http_client_connection **conn; ARRAY_TYPE(http_client_connection) conns; http_client_peer_debug(peer, "Peer cancel"); /* make a copy of the connection array; freed connections modify it */ t_array_init(&conns, array_count(&peer->conns)); array_copy(&conns.arr, 0, &peer->conns.arr, 0, array_count(&peer->conns)); array_foreach_modifiable(&conns, conn) { if (!http_client_connection_is_active(*conn)) http_client_connection_close(conn); } } static unsigned int http_client_peer_requests_pending(struct http_client_peer *peer, unsigned int *num_urgent_r) { struct http_client_queue *const *queue; unsigned int num_requests = 0, num_urgent = 0, requests, urgent; array_foreach(&peer->queues, queue) { requests = http_client_queue_requests_pending(*queue, &urgent); num_requests += requests; num_urgent += urgent; } *num_urgent_r = num_urgent; return num_requests; } static void http_client_peer_check_idle(struct http_client_peer *peer) { struct http_client_connection *const *conn_idx; unsigned int num_urgent = 0; if (array_count(&peer->conns) == 0 && http_client_peer_requests_pending(peer, &num_urgent) == 0) { /* no connections or pending requests; disconnect immediately */ http_client_peer_drop(&peer); return; } /* check all connections for idle status */ array_foreach(&peer->conns, conn_idx) { http_client_connection_check_idle(*conn_idx); } } static void http_client_peer_handle_requests_real(struct http_client_peer *peer) { struct _conn_available { struct http_client_connection *conn; unsigned int pending_requests; }; struct http_client_connection *const *conn_idx; ARRAY(struct _conn_available) conns_avail; struct _conn_available *conn_avail_idx; unsigned int connecting, closing, idle; unsigned int num_pending, num_urgent, new_connections, working_conn_count; struct http_client_peer *tmp_peer; bool statistics_dirty = TRUE; /* FIXME: limit the number of requests handled in one run to prevent I/O starvation. */ /* disconnect pending connections if we're not linked to any queue anymore */ if (array_count(&peer->queues) == 0) { http_client_peer_debug(peer, "Peer no longer used; will now cancel pending connections " "(%u connections exist)", array_count(&peer->conns)); http_client_peer_cancel(peer); return; } /* don't do anything unless we have pending requests */ num_pending = http_client_peer_requests_pending(peer, &num_urgent); if (num_pending == 0) { http_client_peer_debug(peer, "No requests to service for this peer " "(%u connections exist)", array_count(&peer->conns)); http_client_peer_check_idle(peer); return; } http_client_peer_ref(peer); peer->handling_requests = TRUE; t_array_init(&conns_avail, array_count(&peer->conns)); do { bool conn_lost = FALSE; array_clear(&conns_avail); connecting = closing = idle = 0; /* gather connection statistics */ array_foreach(&peer->conns, conn_idx) { struct http_client_connection *conn = *conn_idx; int ret; if ((ret=http_client_connection_check_ready(conn)) < 0) { conn_lost = TRUE; break; } else if (ret > 0) { struct _conn_available *conn_avail; unsigned int insert_idx, pending_requests; /* compile sorted availability list */ pending_requests = http_client_connection_count_pending(conn); if (array_count(&conns_avail) == 0) { insert_idx = 0; } else { insert_idx = array_count(&conns_avail); array_foreach_modifiable(&conns_avail, conn_avail_idx) { if (conn_avail_idx->pending_requests > pending_requests) { insert_idx = array_foreach_idx(&conns_avail, conn_avail_idx); break; } } } conn_avail = array_insert_space(&conns_avail, insert_idx); conn_avail->conn = conn; conn_avail->pending_requests = pending_requests; if (pending_requests == 0) idle++; } /* count the number of connecting and closing connections */ if (conn->closing) closing++; else if (!conn->connected) connecting++; } if (conn_lost) { /* connection array changed while iterating; retry */ continue; } working_conn_count = array_count(&peer->conns) - closing; statistics_dirty = FALSE; /* use idle connections right away */ if (idle > 0) { http_client_peer_debug(peer, "Using %u idle connections to handle %u requests " "(%u total connections ready)", idle, num_pending > idle ? idle : num_pending, array_count(&conns_avail)); array_foreach_modifiable(&conns_avail, conn_avail_idx) { if (num_pending == 0 || conn_avail_idx->pending_requests > 0) break; idle--; if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) { /* no longer available (probably connection error/closed) */ statistics_dirty = TRUE; conn_avail_idx->conn = NULL; } else { /* update statistics */ conn_avail_idx->pending_requests++; if (num_urgent > 0) num_urgent--; num_pending--; } } } /* don't continue unless we have more pending requests */ num_pending = http_client_peer_requests_pending(peer, &num_urgent); if (num_pending == 0) { http_client_peer_debug(peer, "No more requests to service for this peer " "(%u connections exist)", array_count(&peer->conns)); http_client_peer_check_idle(peer); break; } } while (statistics_dirty); tmp_peer = peer; if (!http_client_peer_unref(&tmp_peer)) return; peer->handling_requests = FALSE; if (num_pending == 0) return; i_assert(idle == 0); /* determine how many new connections we can set up */ if (peer->last_failure.tv_sec > 0 && working_conn_count > 0 && working_conn_count == connecting) { /* don't create new connections until the existing ones have finished connecting successfully. */ new_connections = 0; } else { if (working_conn_count - connecting + num_urgent >= peer->client->set.max_parallel_connections) { /* only create connections for urgent requests */ new_connections = (num_urgent > connecting ? num_urgent - connecting : 0); } else if (num_pending <= connecting) { /* there are already enough connections being made */ new_connections = 0; } else if (working_conn_count == connecting) { /* no connections succeeded so far, don't hammer the server with more than one connection attempt unless its urgent */ if (num_urgent > 0) { new_connections = (num_urgent > connecting ? num_urgent - connecting : 0); } else { new_connections = (connecting == 0 ? 1 : 0); } } else if (num_pending - connecting > peer->client->set.max_parallel_connections - working_conn_count) { /* create maximum allowed connections */ new_connections = peer->client->set.max_parallel_connections - working_conn_count; } else { /* create as many connections as we need */ new_connections = num_pending - connecting; } } /* create connections */ if (new_connections > 0) { http_client_peer_debug(peer, "Creating %u new connections to handle requests " "(already %u usable, connecting to %u, closing %u)", new_connections, working_conn_count - connecting, connecting, closing); http_client_peer_connect(peer, new_connections); return; } /* cannot create new connections for normal request; attempt pipelining */ if (working_conn_count - connecting >= peer->client->set.max_parallel_connections) { unsigned int pipeline_level = 0, total_handled = 0, handled; if (!peer->allows_pipelining) { http_client_peer_debug(peer, "Will not pipeline until peer has shown support"); return; } /* fill pipelines */ do { handled = 0; /* fill smallest pipelines first, until all pipelines are filled to the same level */ array_foreach_modifiable(&conns_avail, conn_avail_idx) { if (conn_avail_idx->conn == NULL) continue; if (pipeline_level == 0) { pipeline_level = conn_avail_idx->pending_requests; } else if (conn_avail_idx->pending_requests > pipeline_level) { pipeline_level = conn_avail_idx->pending_requests; break; /* restart from least busy connection */ } /* pipeline it */ if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) { /* connection now unavailable */ conn_avail_idx->conn = NULL; } else { /* successfully pipelined */ conn_avail_idx->pending_requests++; num_pending--; handled++; } } total_handled += handled; } while (num_pending > num_urgent && handled > 0); http_client_peer_debug(peer, "Pipelined %u requests (filled pipelines up to %u requests)", total_handled, pipeline_level); return; } /* still waiting for connections to finish */ http_client_peer_debug(peer, "No request handled; waiting for new connections"); return; } static void http_client_peer_handle_requests(struct http_client_peer *peer) { if (peer->to_req_handling != NULL) timeout_remove(&peer->to_req_handling); T_BEGIN { http_client_peer_handle_requests_real(peer); } T_END; } void http_client_peer_trigger_request_handler(struct http_client_peer *peer) { /* trigger request handling through timeout */ if (peer->to_req_handling == NULL) { peer->to_req_handling = timeout_add_short(0, http_client_peer_handle_requests, peer); } } static struct http_client_peer * http_client_peer_create(struct http_client *client, const struct http_client_peer_addr *addr) { struct http_client_peer *peer; peer = i_new(struct http_client_peer, 1); peer->refcount = 1; peer->client = client; peer->addr = *addr; switch (addr->type) { case HTTP_CLIENT_PEER_ADDR_RAW: case HTTP_CLIENT_PEER_ADDR_HTTP: i_assert(peer->addr.a.tcp.ip.family != 0); break; case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: i_assert(peer->addr.a.tcp.ip.family != 0); i_assert(client->ssl_ctx != NULL); peer->addr_name = i_strdup(addr->a.tcp.https_name); peer->addr.a.tcp.https_name = peer->addr_name; break; case HTTP_CLIENT_PEER_ADDR_UNIX: peer->addr_name = i_strdup(addr->a.un.path); peer->addr.a.un.path = peer->addr_name; break; default: break; } i_array_init(&peer->queues, 16); i_array_init(&peer->conns, 16); hash_table_insert (client->peers, (const struct http_client_peer_addr *)&peer->addr, peer); DLLIST_PREPEND(&client->peers_list, peer); http_client_peer_debug(peer, "Peer created"); return peer; } static void http_client_peer_disconnect(struct http_client_peer *peer) { struct http_client_connection **conn; ARRAY_TYPE(http_client_connection) conns; struct http_client_queue *const *queue; if (peer->disconnected) return; peer->disconnected = TRUE; http_client_peer_debug(peer, "Peer disconnect"); /* make a copy of the connection array; freed connections modify it */ t_array_init(&conns, array_count(&peer->conns)); array_copy(&conns.arr, 0, &peer->conns.arr, 0, array_count(&peer->conns)); array_foreach_modifiable(&conns, conn) { http_client_connection_peer_closed(conn); } i_assert(array_count(&peer->conns) == 0); if (peer->to_req_handling != NULL) timeout_remove(&peer->to_req_handling); if (peer->to_backoff != NULL) timeout_remove(&peer->to_backoff); /* unlist in client */ hash_table_remove (peer->client->peers, (const struct http_client_peer_addr *)&peer->addr); DLLIST_REMOVE(&peer->client->peers_list, peer); /* unlink all queues */ array_foreach(&peer->queues, queue) http_client_queue_peer_disconnected(*queue, peer); array_clear(&peer->queues); } void http_client_peer_ref(struct http_client_peer *peer) { peer->refcount++; } bool http_client_peer_unref(struct http_client_peer **_peer) { struct http_client_peer *peer = *_peer; i_assert(peer->refcount > 0); *_peer = NULL; if (--peer->refcount > 0) return TRUE; http_client_peer_debug(peer, "Peer destroy"); http_client_peer_disconnect(peer); i_assert(array_count(&peer->queues) == 0); array_free(&peer->conns); array_free(&peer->queues); i_free(peer->addr_name); i_free(peer->label); i_free(peer); return FALSE; } void http_client_peer_close(struct http_client_peer **_peer) { struct http_client_peer *peer = *_peer; http_client_peer_debug(peer, "Peer close"); http_client_peer_disconnect(peer); (void)http_client_peer_unref(_peer); } static void http_client_peer_drop(struct http_client_peer **_peer) { struct http_client_peer *peer = *_peer; unsigned int conns_active = http_client_peer_active_connections(peer); if (conns_active > 0) { http_client_peer_debug(peer, "Not dropping peer (%d connections active)", conns_active); return; } if (peer->to_backoff != NULL) return; if (http_client_peer_start_backoff_timer(peer)) { http_client_peer_debug(peer, "Dropping peer (waiting for backof timeout)"); /* will disconnect any pending connections */ http_client_peer_trigger_request_handler(peer); } else { http_client_peer_debug(peer, "Dropping peer now"); /* drop peer immediately */ http_client_peer_close(_peer); } } struct http_client_peer * http_client_peer_get(struct http_client *client, const struct http_client_peer_addr *addr) { struct http_client_peer *peer; peer = hash_table_lookup(client->peers, addr); if (peer == NULL) peer = http_client_peer_create(client, addr); return peer; } bool http_client_peer_have_queue(struct http_client_peer *peer, struct http_client_queue *queue) { struct http_client_queue *const *queue_idx; array_foreach(&peer->queues, queue_idx) { if (*queue_idx == queue) return TRUE; } return FALSE; } void http_client_peer_link_queue(struct http_client_peer *peer, struct http_client_queue *queue) { if (!http_client_peer_have_queue(peer, queue)) { array_append(&peer->queues, &queue, 1); http_client_peer_debug(peer, "Linked queue %s (%d queues linked)", queue->name, array_count(&peer->queues)); } } void http_client_peer_unlink_queue(struct http_client_peer *peer, struct http_client_queue *queue) { struct http_client_queue *const *queue_idx; array_foreach(&peer->queues, queue_idx) { if (*queue_idx == queue) { array_delete(&peer->queues, array_foreach_idx(&peer->queues, queue_idx), 1); http_client_peer_debug(peer, "Unlinked queue %s (%d queues linked)", queue->name, array_count(&peer->queues)); if (array_count(&peer->queues) == 0) http_client_peer_check_idle(peer); return; } } } struct http_client_request * http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent) { struct http_client_queue *const *queue_idx; struct http_client_request *req; array_foreach(&peer->queues, queue_idx) { if ((req=http_client_queue_claim_request (*queue_idx, &peer->addr, no_urgent)) != NULL) { req->peer = peer; return req; } } return NULL; } void http_client_peer_connection_success(struct http_client_peer *peer) { struct http_client_queue *const *queue; http_client_peer_debug(peer, "Successfully connected (connections=%u)", array_count(&peer->conns)); peer->last_failure.tv_sec = peer->last_failure.tv_usec = 0; http_client_peer_reset_backoff_timer(peer); array_foreach(&peer->queues, queue) { http_client_queue_connection_success(*queue, &peer->addr); } http_client_peer_trigger_request_handler(peer); } void http_client_peer_connection_failure(struct http_client_peer *peer, const char *reason) { struct http_client_queue *const *queue; unsigned int pending; peer->last_failure = ioloop_timeval; /* count number of pending connections */ pending = http_client_peer_pending_connections(peer); i_assert(pending > 0); http_client_peer_debug(peer, "Failed to make connection " "(connections=%u, connecting=%u)", array_count(&peer->conns), pending); /* manage backoff timer only when this was the only attempt */ if (pending == 1) http_client_peer_increase_backoff_timer(peer); if (pending > 1) { /* if there are other connections attempting to connect, wait for them before failing the requests. remember that we had trouble with connecting so in future we don't try to create more than one connection until connects work again. */ } else { /* this was the only/last connection and connecting to it failed. a second connect will probably also fail, so just try another IP for the hosts(s) or abort all requests if this was the only/last option. */ array_foreach(&peer->queues, queue) { http_client_queue_connection_failure(*queue, &peer->addr, reason); } } } void http_client_peer_connection_lost(struct http_client_peer *peer, bool premature) { unsigned int num_pending, num_urgent; /* we get here when an already connected connection fails. if the connect itself fails, http_client_peer_connection_failure() is called instead. */ if (peer->disconnected) return; num_pending = http_client_peer_requests_pending(peer, &num_urgent); http_client_peer_debug(peer, "Lost a connection%s (%u queues linked, %u connections left, " "%u requests pending, %u requests urgent)", (premature ? " prematurely" : ""), array_count(&peer->queues), array_count(&peer->conns), num_pending, num_urgent); /* update backoff timer if the connection was lost prematurely. this prevents reconnecting immediately to a server that is misbehaving by disconnecting before sending a response. */ if (premature) { peer->last_failure = ioloop_timeval; http_client_peer_increase_backoff_timer(peer); } if (peer->handling_requests) { /* we got here from the request handler loop */ http_client_peer_debug(peer, "Lost a connection while handling requests"); return; } /* if there are pending requests for this peer, create a new connection for them. if not, this peer will wind itself down. */ http_client_peer_trigger_request_handler(peer); } unsigned int http_client_peer_idle_connections(struct http_client_peer *peer) { struct http_client_connection *const *conn_idx; unsigned int idle = 0; /* find idle connections */ array_foreach(&peer->conns, conn_idx) { if (http_client_connection_is_idle(*conn_idx)) idle++; } return idle; } unsigned int http_client_peer_active_connections(struct http_client_peer *peer) { struct http_client_connection *const *conn_idx; unsigned int active = 0; /* find idle connections */ array_foreach(&peer->conns, conn_idx) { if (http_client_connection_is_active(*conn_idx)) active++; } return active; } unsigned int http_client_peer_pending_connections(struct http_client_peer *peer) { struct http_client_connection *const *conn_idx; unsigned int pending = 0; /* find idle connections */ array_foreach(&peer->conns, conn_idx) { if (!(*conn_idx)->closing && !(*conn_idx)->connected) pending++; } return pending; } void http_client_peer_switch_ioloop(struct http_client_peer *peer) { if (peer->to_req_handling != NULL) { peer->to_req_handling = io_loop_move_timeout(&peer->to_req_handling); } if (peer->to_backoff != NULL) { peer->to_backoff = io_loop_move_timeout(&peer->to_backoff); } } dovecot-2.2.33.2/src/lib-http/http-auth.c0000644000175000017500000002432113123174404014722 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "base64.h" #include "array.h" #include "http-parser.h" #include "http-auth.h" /* RFC 7235, Section 2.1: challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ] credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ] auth-scheme = token auth-param = token BWS "=" BWS ( token / quoted-string ) token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" OWS = *( SP / HTAB ) ; optional whitespace BWS = OWS ; "bad" whitespace */ /* * Parsing */ static int http_parse_token68(struct http_parser *parser, const char **token68_r) { const unsigned char *first; /* token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" */ /* 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) */ if (parser->cur >= parser->end || !http_char_is_token68(*parser->cur)) return 0; first = parser->cur++; while (parser->cur < parser->end && http_char_is_token68(*parser->cur)) parser->cur++; /* *"=" */ while (parser->cur < parser->end && *parser->cur == '=') parser->cur++; *token68_r = t_strndup(first, parser->cur - first); return 1; } static int http_parse_auth_param(struct http_parser *parser, const char **param_r, const char **value_r) { const unsigned char *first = parser->cur, *end_token; int ret; /* auth-param = token BWS "=" BWS ( token / quoted-string ) */ /* token */ if ((ret=http_parser_skip_token(parser)) <= 0) { parser->cur = first; return ret; } end_token = parser->cur; /* BWS "=" BWS */ http_parse_ows(parser); if (parser->cur >= parser->end || *parser->cur != '=') { parser->cur = first; return 0; } parser->cur++; http_parse_ows(parser); /* ( token / quoted-string ) */ if ((ret=http_parse_token_or_qstring(parser, value_r)) <= 0) { parser->cur = first; return ret; } *param_r = t_strndup(first, end_token - first); return 1; } static int http_parse_auth_params(struct http_parser *parser, ARRAY_TYPE(http_auth_param) *params) { const unsigned char *last = parser->cur; struct http_auth_param param; unsigned int count = 0; int ret; i_zero(¶m); while ((ret=http_parse_auth_param (parser, ¶m.name, ¶m.value)) > 0) { if (!array_is_created(params)) t_array_init(params, 4); array_append(params, ¶m, 1); count++; last = parser->cur; /* OWS "," OWS --> also allow empty elements */ for (;;) { http_parse_ows(parser); if (parser->cur >= parser->end || *parser->cur != ',') break; parser->cur++; } } parser->cur = last; if (ret < 0) return -1; return (count > 0 ? 1 : 0); } int http_auth_parse_challenges(const unsigned char *data, size_t size, ARRAY_TYPE(http_auth_challenge) *chlngs) { struct http_parser parser; int ret; http_parser_init(&parser, data, size); /* WWW-Authenticate = 1#challenge Proxy-Authenticate = 1#challenge challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ] auth-scheme = token */ /* 1#element => *( "," OWS ) ... ; RFC 7230, Section 7 */ for (;;) { if (parser.cur >= parser.end || *parser.cur != ',') break; parser.cur++; http_parse_ows(&parser); } for (;;) { struct http_auth_challenge chlng; i_zero(&chlng); /* auth-scheme */ if ((ret=http_parse_token(&parser, &chlng.scheme)) <= 0) { if (ret < 0) return -1; break; } /* [ 1*SP ... ] */ if (parser.cur >= parser.end || *parser.cur != ' ') return 1; parser.cur++; while (parser.cur < parser.end && *parser.cur == ' ') parser.cur++; /* ( token68 / #auth-param ) */ if ((ret=http_parse_auth_params(&parser, &chlng.params)) <= 0) { if (ret < 0) return -1; if (http_parse_token68(&parser, &chlng.data) < 0) return -1; } if (!array_is_created(chlngs)) t_array_init(chlngs, 4); array_append(chlngs, &chlng, 1); /* OWS "," OWS --> also allow empty elements */ for (;;) { http_parse_ows(&parser); if (parser.cur >= parser.end || *parser.cur != ',') break; parser.cur++; } } if (parser.cur != parser.end) return -1; return 1; } int http_auth_parse_credentials(const unsigned char *data, size_t size, struct http_auth_credentials *crdts) { struct http_parser parser; int ret; http_parser_init(&parser, data, size); /* Authorization = credentials Proxy-Authorization = credentials credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ] auth-scheme = token */ i_zero(crdts); /* auth-scheme */ if (http_parse_token(&parser, &crdts->scheme) <= 0) return -1; /* [ 1*SP ... ] */ if (parser.cur >= parser.end || *parser.cur != ' ') return 1; parser.cur++; while (parser.cur < parser.end && *parser.cur == ' ') parser.cur++; /* ( token68 / #auth-param ) */ if ((ret=http_parse_auth_params(&parser, &crdts->params)) <= 0) { if (ret < 0) return -1; if (http_parse_token68(&parser, &crdts->data) < 0) return -1; } if (parser.cur != parser.end) return -1; return 1; } /* * Construction */ static void http_auth_create_param(string_t *out, const struct http_auth_param *param) { const char *p, *first; /* auth-param = token BWS "=" BWS ( token / quoted-string ) */ str_append(out, param->name); str_append_c(out, '='); for (p = param->value; *p != '\0' && http_char_is_token(*p); p++); if ( *p != '\0' ) { str_append_c(out, '"'); p = first = param->value; while (*p != '\0') { if (*p == '\\' || *p == '"') { str_append_n(out, first, p-first); str_append_c(out, '\\'); first = p; } p++; } str_append_n(out, first, p-first); str_append_c(out, '"'); } else { str_append(out, param->value); } } static void http_auth_create_params(string_t *out, const ARRAY_TYPE(http_auth_param) *params) { const struct http_auth_param *prms; unsigned int count, i; if (!array_is_created(params)) return; prms = array_get(params, &count); for (i = 0; i < count; i++) { if (i > 0) str_append(out, ", "); http_auth_create_param(out, &prms[i]); } } static void http_auth_check_token68(const char *data) { const char *p = data; /* Make sure we're not working with nonsense. */ i_assert(http_char_is_token68(*p)); for (p++; *p != '\0' && *p != '='; p++) i_assert(http_char_is_token68(*p)); for (; *p != '\0'; p++) i_assert(*p == '='); } void http_auth_create_challenge(string_t *out, const struct http_auth_challenge *chlng) { /* challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ] auth-scheme = token */ /* auth-scheme */ str_append(out, chlng->scheme); if (chlng->data != NULL) { /* SP token68 */ http_auth_check_token68(chlng->data); str_append_c(out, ' '); str_append(out, chlng->data); } else { /* SP #auth-param */ str_append_c(out, ' '); http_auth_create_params(out, &chlng->params); } } void http_auth_create_challenges(string_t *out, const ARRAY_TYPE(http_auth_challenge) *chlngs) { const struct http_auth_challenge *chlgs; unsigned int count, i; /* WWW-Authenticate = 1#challenge Proxy-Authenticate = 1#challenge */ chlgs = array_get(chlngs, &count); for (i = 0; i < count; i++) { if (i > 0) str_append(out, ", "); http_auth_create_challenge(out, &chlgs[i]); } } void http_auth_create_credentials(string_t *out, const struct http_auth_credentials *crdts) { /* Authorization = credentials Proxy-Authorization = credentials credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ] auth-scheme = token */ /* auth-scheme */ str_append(out, crdts->scheme); if (crdts->data != NULL) { /* SP token68 */ http_auth_check_token68(crdts->data); str_append_c(out, ' '); str_append(out, crdts->data); } else { /* SP #auth-param */ str_append_c(out, ' '); http_auth_create_params(out, &crdts->params); } } /* * Manipulation */ static void http_auth_params_clone(pool_t pool, ARRAY_TYPE(http_auth_param) *dst, const ARRAY_TYPE(http_auth_param) *src) { const struct http_auth_param *sparam; if (!array_is_created(src)) return; p_array_init(dst, pool, 4); array_foreach(src, sparam) { struct http_auth_param nparam; i_zero(&nparam); nparam.name = p_strdup(pool, sparam->name); nparam.value = p_strdup(pool, sparam->value); array_append(dst, &nparam, 1); } } void http_auth_challenge_copy(pool_t pool, struct http_auth_challenge *dst, const struct http_auth_challenge *src) { dst->scheme = p_strdup(pool, src->scheme); if (src->data != NULL) dst->data = p_strdup(pool, src->data); else http_auth_params_clone(pool, &dst->params, &src->params); } struct http_auth_challenge * http_auth_challenge_clone(pool_t pool, const struct http_auth_challenge *src) { struct http_auth_challenge *new; new = p_new(pool, struct http_auth_challenge, 1); http_auth_challenge_copy(pool, new, src); return new; } void http_auth_credentials_copy(pool_t pool, struct http_auth_credentials *dst, const struct http_auth_credentials *src) { dst->scheme = p_strdup(pool, src->scheme); if (src->data != NULL) dst->data = p_strdup(pool, src->data); else http_auth_params_clone(pool, &dst->params, &src->params); } struct http_auth_credentials * http_auth_credentials_clone(pool_t pool, const struct http_auth_credentials *src) { struct http_auth_credentials *new; new = p_new(pool, struct http_auth_credentials, 1); http_auth_credentials_copy(pool, new, src); return new; } /* * Simple schemes */ void http_auth_basic_challenge_init(struct http_auth_challenge *chlng, const char *realm) { i_zero(chlng); chlng->scheme = "Basic"; if (realm != NULL) { struct http_auth_param param; i_zero(¶m); param.name = "realm"; param.value = t_strdup(realm); t_array_init(&chlng->params, 1); array_append(&chlng->params, ¶m, 1); } } void http_auth_basic_credentials_init(struct http_auth_credentials *crdts, const char *username, const char *password) { const char *auth; string_t *data; i_assert(username != NULL && *username != '\0'); i_assert(strchr(username, ':') == NULL); data = t_str_new(64); auth = t_strconcat(username, ":", password, NULL); base64_encode(auth, strlen(auth), data); i_zero(crdts); crdts->scheme = "Basic"; crdts->data = str_c(data); } dovecot-2.2.33.2/src/lib-http/http-server-private.h0000644000175000017500000001621513165463624016762 00000000000000#ifndef HTTP_SERVER_PRIVATE_H #define HTTP_SERVER_PRIVATE_H #include "connection.h" #include "http-server.h" #include "llist.h" #define HTTP_SERVER_REQUEST_MAX_TARGET_LENGTH 4096 struct http_server_request; struct http_server_connection; enum http_server_request_state { /* New request; request header is still being parsed. */ HTTP_SERVER_REQUEST_STATE_NEW = 0, /* Queued request; callback to request handler executing. */ HTTP_SERVER_REQUEST_STATE_QUEUED, /* Reading request payload; request handler still needs to read more payload. */ HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN, /* This request is being processed; request payload is fully read, but no response is yet submitted */ HTTP_SERVER_REQUEST_STATE_PROCESSING, /* A response is submitted for this request. If not all request payload was read by the handler, it is first skipped on the input. */ HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE, /* Request is ready for response; a response is submitted and the request payload is fully read */ HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND, /* The response for the request is sent (apart from payload) */ HTTP_SERVER_REQUEST_STATE_SENT_RESPONSE, /* Sending response payload to client */ HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT, /* Request is finished; still lingering due to references */ HTTP_SERVER_REQUEST_STATE_FINISHED, /* Request is aborted; still lingering due to references */ HTTP_SERVER_REQUEST_STATE_ABORTED }; struct http_server_response { struct http_server_request *request; unsigned int status; const char *reason; string_t *headers; time_t date; ARRAY_TYPE(http_auth_challenge) auth_challenges; struct istream *payload_input; uoff_t payload_size, payload_offset; struct ostream *payload_output; struct ostream *blocking_output; http_server_tunnel_callback_t tunnel_callback; void *tunnel_context; unsigned int have_hdr_connection:1; unsigned int have_hdr_date:1; unsigned int have_hdr_body_spec:1; unsigned int payload_chunked:1; unsigned int payload_blocking:1; unsigned int payload_direct:1; unsigned int payload_corked:1; unsigned int close:1; unsigned int submitted:1; }; struct http_server_request { struct http_request req; pool_t pool; unsigned int refcount; unsigned int id; enum http_server_request_state state; struct http_server_request *prev, *next; struct http_server *server; struct http_server_connection *conn; struct istream *payload_input; struct http_server_response *response; void (*destroy_callback)(void *); void *destroy_context; unsigned int payload_halted:1; unsigned int sent_100_continue:1; unsigned int delay_destroy:1; unsigned int destroy_pending:1; unsigned int failed:1; }; struct http_server_connection { struct connection conn; struct http_server *server; unsigned int refcount; const struct http_server_callbacks *callbacks; void *context; unsigned int id; // DEBUG struct timeout *to_input, *to_idle; struct ssl_iostream *ssl_iostream; struct http_request_parser *http_parser; struct http_server_request *request_queue_head, *request_queue_tail; unsigned int request_queue_count; struct istream *incoming_payload; struct io *io_resp_payload; char *disconnect_reason; struct http_server_stats stats; unsigned int ssl:1; unsigned int closed:1; unsigned int close_indicated:1; unsigned int input_broken:1; unsigned int output_locked:1; unsigned int in_req_callback:1; /* performing request callback (busy) */ unsigned int switching_ioloop:1; /* in the middle of switching ioloop */ }; struct http_server { pool_t pool; struct http_server_settings set; struct ioloop *ioloop; struct ssl_iostream_context *ssl_ctx; struct connection_list *conn_list; unsigned int shutting_down:1; /* shutting down server */ }; static inline const char * http_server_request_label(struct http_server_request *req) { if (req->req.method == NULL) { if (req->req.target_raw == NULL) return t_strdup_printf("[Req%u: ]", req->id); return t_strdup_printf("[Req%u: %s ]", req->id, req->req.method); } return t_strdup_printf("[Req%u: %s %s]", req->id, req->req.method, req->req.target_raw); } static inline const char * http_server_connection_label(struct http_server_connection *conn) { return conn->conn.name; } bool http_server_connection_pending_payload(struct http_server_connection *conn); /* response */ void http_server_response_free(struct http_server_response *resp); int http_server_response_send(struct http_server_response *resp, const char **error_r); int http_server_response_send_more(struct http_server_response *resp, const char **error_r); /* request */ struct http_server_request * http_server_request_new(struct http_server_connection *conn); void http_server_request_destroy(struct http_server_request **_req); void http_server_request_abort(struct http_server_request **_req, const char *reason) ATTR_NULL(2); void http_server_request_halt_payload(struct http_server_request *req); void http_server_request_continue_payload(struct http_server_request *req); void http_server_request_submit_response(struct http_server_request *req); void http_server_request_ready_to_respond(struct http_server_request *req); void http_server_request_finished(struct http_server_request *req); static inline bool http_server_request_is_new(struct http_server_request *req) { return (req->state == HTTP_SERVER_REQUEST_STATE_NEW); } static inline bool http_server_request_is_complete(struct http_server_request *req) { return (req->failed || req->conn->input_broken || (req->next != NULL && !http_server_request_is_new(req->next)) || !http_server_connection_pending_payload(req->conn)); } static inline bool http_server_request_version_equals(struct http_server_request *req, unsigned int major, unsigned int minor) { return (req->req.version_major == major && req->req.version_minor == minor); } /* connection */ struct connection_list *http_server_connection_list_init(void); bool http_server_connection_shut_down(struct http_server_connection *conn); void http_server_connection_switch_ioloop(struct http_server_connection *conn); void http_server_connection_write_failed(struct http_server_connection *conn, const char *error); void http_server_connection_trigger_responses( struct http_server_connection *conn); int http_server_connection_flush(struct http_server_connection *conn); int http_server_connection_output(struct http_server_connection *conn); void http_server_connection_tunnel(struct http_server_connection **_conn, http_server_tunnel_callback_t callback, void *context); int http_server_connection_discard_payload( struct http_server_connection *conn); bool http_server_connection_pending_payload(struct http_server_connection *conn); static inline void http_server_connection_add_request(struct http_server_connection *conn, struct http_server_request *sreq) { DLLIST2_APPEND(&conn->request_queue_head, &conn->request_queue_tail, sreq); conn->request_queue_count++; } static inline void http_server_connection_remove_request(struct http_server_connection *conn, struct http_server_request *sreq) { DLLIST2_REMOVE(&conn->request_queue_head, &conn->request_queue_tail, sreq); conn->request_queue_count--; } #endif dovecot-2.2.33.2/src/lib-http/http-date.h0000644000175000017500000000104713123174404014703 00000000000000#ifndef HTTP_DATE_H #define HTTP_DATE_H /* Parses HTTP-date string into time_t timestamp. */ bool http_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r); /* Equal to http_date_parse, but writes uncompensated timestamp to tm_r. */ bool http_date_parse_tm(const unsigned char *data, size_t size, struct tm *tm_r); /* Create HTTP-date string from given time struct. */ const char *http_date_create_tm(struct tm *tm); /* Create HTTP-date string from given time. */ const char *http_date_create(time_t timestamp); #endif dovecot-2.2.33.2/src/lib-http/test-http-header-parser.c0000644000175000017500000002643413165463624017502 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str-sanitize.h" #include "istream.h" #include "test-common.h" #include "http-response.h" #include "http-header-parser.h" #include struct http_header_parse_result { const char *name; const char *value; }; struct http_header_parse_test { const char *header; struct http_header_limits limits; enum http_header_parse_flags flags; const struct http_header_parse_result *fields; }; /* Valid header tests */ static const struct http_header_parse_result valid_header_parse_result1[] = { { "Date", "Sat, 06 Oct 2012 16:01:44 GMT" }, { "Server", "Apache/2.2.16 (Debian)" }, { "Last-Modified", "Mon, 30 Jul 2012 11:09:28 GMT" }, { "Etag", "\"3d24677-3261-4c60a1863aa00\"" }, { "Accept-Ranges", "bytes" }, { "Vary", "Accept-Encoding" }, { "Content-Encoding", "gzip" }, { "Content-Length", "4092" }, { "Keep-Alive", "timeout=15, max=100" }, { "Connection", "Keep-Alive" }, { "Content-Type", "text/html" }, { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result2[] = { { "Host", "p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com" }, { "User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)" }, { "Accept", "image/png,image/*;q=0.8,*/*;q=0.5" }, { "Accept-Language", "en-us,en;q=0.5" }, { "Accept-Encoding", "gzip, deflate" }, { "DNT", "1" }, { "Connection", "keep-alive" }, { "Referer", "http://www.example.nl/" }, { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result3[] = { { "Date", "Sat, 06 Oct 2012 17:12:37 GMT" }, { "Server", "Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with" " Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6" " mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1" }, { "WWW-Authenticate", "Basic realm=\"Munin\"" }, { "Vary", "Accept-Encoding" }, { "Content-Encoding", "gzip" }, { "Content-Length", "445" }, { "Keep-Alive", "timeout=15, max=98" }, { "Connection", "Keep-Alive" }, { "Content-Type", "text/html; charset=iso-8859-1" }, { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result4[] = { { "Age", "58" }, { "Date", "Sun, 04 Aug 2013 09:33:09 GMT" }, { "Expires", "Sun, 04 Aug 2013 09:34:08 GMT" }, { "Cache-Control", "max-age=60" }, { "Content-Length", "17336" }, { "Connection", "Keep-Alive" }, { "Via", "NS-CACHE-9.3" }, { "Server", "Apache" }, { "Vary", "Host" }, { "Last-Modified", "Sun, 04 Aug 2013 09:33:07 GMT" }, { "Content-Type", "text/html; charset=utf-8" }, { "Content-Encoding", "gzip" }, { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result5[] = { { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result6[] = { { "X-Frop", "This text\x80 contains obs-text\x81 characters" }, { NULL, NULL } }; static const struct http_header_parse_result valid_header_parse_result7[] = { { "X-Frop", "This text contains invalid characters" }, { NULL, NULL } }; static const struct http_header_parse_test valid_header_parse_tests[] = { { .header = "Date: Sat, 06 Oct 2012 16:01:44 GMT\r\n" "Server: Apache/2.2.16 (Debian)\r\n" "Last-Modified: Mon, 30 Jul 2012 11:09:28 GMT\r\n" "Etag: \"3d24677-3261-4c60a1863aa00\"\r\n" "Accept-Ranges: bytes\r\n" "Vary: Accept-Encoding\r\n" "Content-Encoding: gzip\r\n" "Content-Length: 4092\r\n" "Keep-Alive: timeout=15, max=100\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/html\r\n" "\r\n", .fields = valid_header_parse_result1 },{ .header = "Host: p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com\n" "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)\n" "Accept:\t\timage/png,image/*;q=0.8,*/*;q=0.5\n" "Accept-Language:\ten-us,en;q=0.5\n" "Accept-Encoding: \t\tgzip, deflate\n" "DNT: 1\n" "Connection: \t\tkeep-alive\n" "Referer: http://www.example.nl/\n" "\n", .fields = valid_header_parse_result2 },{ .header = "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with\r\n" " Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n" " mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1\r\n" "WWW-Authenticate: Basic realm=\"Munin\"\r\n" "Vary: Accept-Encoding\r\n" "Content-Encoding: gzip\r\n" "Content-Length: 445\r\n" "Keep-Alive: timeout=15, max=98\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "\r\n", .fields = valid_header_parse_result3 },{ .header = "Age: 58 \r\n" "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" "Cache-Control: max-age=60 \r\n" "Content-Length: 17336 \r\n" "Connection: Keep-Alive\r\n" "Via: NS-CACHE-9.3\r\n" "Server: Apache\r\n" "Vary: Host\r\n" "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Encoding: gzip\r\n" "\r\n", .fields = valid_header_parse_result4, .limits = { .max_size = 340, .max_field_size = 46, .max_fields = 12 } },{ .header = "\r\n", .fields = valid_header_parse_result5 },{ .header = "X-Frop: This text\x80 contains obs-text\x81 characters\r\n" "\r\n", .fields = valid_header_parse_result6 },{ .header = "X-Frop: This text\x01 contains invalid\x7f characters\r\n" "\r\n", .fields = valid_header_parse_result7 } }; static const unsigned int valid_header_parse_test_count = N_ELEMENTS(valid_header_parse_tests); static void test_http_header_parse_valid(void) { unsigned int i; for (i = 0; i < valid_header_parse_test_count; i++) T_BEGIN { struct istream *input; struct http_header_parser *parser; const struct http_header_limits *limits; const char *header, *field_name, *error = NULL; const unsigned char *field_data; size_t field_size; int ret; unsigned int j, pos, header_len; header = valid_header_parse_tests[i].header; header_len = strlen(header); limits = &valid_header_parse_tests[i].limits; input = test_istream_create_data(header, header_len); parser = http_header_parser_init(input, limits, valid_header_parse_tests[i].flags); test_begin(t_strdup_printf("http header valid [%d]", i)); j = 0; pos = 0; test_istream_set_size(input, 0); while ((ret=http_header_parse_next_field (parser, &field_name, &field_data, &field_size, &error)) >= 0) { const struct http_header_parse_result *result; const char *field_value; if (ret == 0) { if (pos == header_len) break; test_istream_set_size(input, ++pos); continue; } if (field_name == NULL) break; result = &valid_header_parse_tests[i].fields[j]; field_value = t_strndup(field_data, field_size); if (result->name == NULL) { test_out_reason("valid", FALSE, t_strdup_printf ("%s: %s", field_name, str_sanitize(field_value, 100))); break; } test_out_reason("valid", strcmp(result->name, field_name) == 0 && strcmp(result->value, field_value) == 0, t_strdup_printf("%s: %s", field_name, str_sanitize(field_value, 100))); j++; } test_out_reason("parse success", ret > 0, error); test_end(); i_stream_unref(&input); http_header_parser_deinit(&parser); } T_END; } static const struct http_header_parse_test invalid_header_parse_tests[] = { { .header = "Date: Sat, 06 Oct 2012 16:01:44 GMT\r\n" "Server : Apache/2.2.16 (Debian)\r\n" "Last-Modified: Mon, 30 Jul 2012 11:09:28 GMT\r\n" "\r\n" },{ .header = "Date: Sat, 06 Oct 2012 17:18:22 GMT\r\n" "Server: Apache/2.2.3 (CentOS)\r\n" "X Powered By: PHP/5.3.6\r\n" "\r\n" },{ .header = "Host: www.example.com\n\r" "Accept: image/png,image/*;q=0.8,*/*;q=0.5\n\r" "Accept-Language: en-us,en;q=0.5\n\r" "Accept-Encoding: gzip, deflate\n\r" "\n\r" },{ .header = "Host: p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com\n" "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)\n" "Accept:\t\timage/png,image/*;q=0.8,*/\1;q=0.5\n" "\n", .flags = HTTP_HEADER_PARSE_FLAG_STRICT },{ .header = "Date: Sat, 06 Oct 2012 17:18:22 GMT\r\n" "Server: Apache/2.2.3\177 (CentOS)\r\n" "\r\n", .flags = HTTP_HEADER_PARSE_FLAG_STRICT },{ .header = "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with\r\n" "Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n" "mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1\r\n" "\r\n" },{ .header = "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" },{ .header = "Age: 58 \r\n" "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" "Cache-Control: max-age=60 \r\n" "Content-Length: 17336 \r\n" "Connection: Keep-Alive\r\n" "Via: NS-CACHE-9.3\r\n" "Server: Apache\r\n" "Vary: Host\r\n" "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Encoding: gzip\r\n" "\r\n", .limits = { .max_size = 339 } },{ .header = "Age: 58 \r\n" "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" "Cache-Control: max-age=60 \r\n" "Content-Length: 17336 \r\n" "Connection: Keep-Alive\r\n" "Via: NS-CACHE-9.3\r\n" "Server: Apache\r\n" "Vary: Host\r\n" "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Encoding: gzip\r\n" "\r\n", .fields = valid_header_parse_result4, .limits = { .max_field_size = 45 } },{ .header = "Age: 58 \r\n" "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" "Cache-Control: max-age=60 \r\n" "Content-Length: 17336 \r\n" "Connection: Keep-Alive\r\n" "Via: NS-CACHE-9.3\r\n" "Server: Apache\r\n" "Vary: Host\r\n" "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Encoding: gzip\r\n" "\r\n", .fields = valid_header_parse_result4, .limits = { .max_fields = 11 } } }; static const unsigned int invalid_header_parse_test_count = N_ELEMENTS(invalid_header_parse_tests); static void test_http_header_parse_invalid(void) { unsigned int i; for (i = 0; i < invalid_header_parse_test_count; i++) T_BEGIN { struct istream *input; struct http_header_parser *parser; const struct http_header_limits *limits; const char *header, *field_name, *error = NULL; const unsigned char *field_data; size_t field_size; int ret; header = invalid_header_parse_tests[i].header; limits = &invalid_header_parse_tests[i].limits; input = i_stream_create_from_data(header, strlen(header)); parser = http_header_parser_init(input, limits, invalid_header_parse_tests[i].flags); test_begin(t_strdup_printf("http header invalid [%d]", i)); while ((ret=http_header_parse_next_field (parser, &field_name, &field_data, &field_size, &error)) > 0) { if (field_name == NULL) break; } test_out_reason("parse failure", ret < 0, error); test_end(); i_stream_unref(&input); http_header_parser_deinit(&parser); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_http_header_parse_valid, test_http_header_parse_invalid, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-response-parser.c0000644000175000017500000002675513165463624017141 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "http-parser.h" #include "http-date.h" #include "http-message-parser.h" #include "http-response-parser.h" #include enum http_response_parser_state { HTTP_RESPONSE_PARSE_STATE_INIT = 0, HTTP_RESPONSE_PARSE_STATE_VERSION, HTTP_RESPONSE_PARSE_STATE_SP1, HTTP_RESPONSE_PARSE_STATE_STATUS, HTTP_RESPONSE_PARSE_STATE_SP2, HTTP_RESPONSE_PARSE_STATE_REASON, HTTP_RESPONSE_PARSE_STATE_CR, HTTP_RESPONSE_PARSE_STATE_LF, HTTP_RESPONSE_PARSE_STATE_HEADER }; struct http_response_parser { struct http_message_parser parser; enum http_response_parser_state state; unsigned int response_status; const char *response_reason; }; struct http_response_parser * http_response_parser_init(struct istream *input, const struct http_header_limits *hdr_limits, enum http_response_parse_flags flags) { struct http_response_parser *parser; enum http_message_parse_flags msg_flags = 0; /* FIXME: implement status line limit */ if ((flags & HTTP_RESPONSE_PARSE_FLAG_STRICT) != 0) msg_flags |= HTTP_MESSAGE_PARSE_FLAG_STRICT; parser = i_new(struct http_response_parser, 1); http_message_parser_init(&parser->parser, input, hdr_limits, 0, msg_flags); return parser; } void http_response_parser_deinit(struct http_response_parser **_parser) { struct http_response_parser *parser = *_parser; *_parser = NULL; http_message_parser_deinit(&parser->parser); i_free(parser); } static void http_response_parser_restart(struct http_response_parser *parser) { http_message_parser_restart(&parser->parser, NULL); parser->response_status = 0; parser->response_reason = NULL; } static int http_response_parse_status(struct http_response_parser *parser) { const unsigned char *p = parser->parser.cur; const size_t size = parser->parser.end - parser->parser.cur; /* status-code = 3DIGIT */ if (size < 3) return 0; if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2])) return -1; parser->response_status = (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0'); if (parser->response_status < 100 || parser->response_status >= 600) return -1; parser->parser.cur += 3; return 1; } static int http_response_parse_reason(struct http_response_parser *parser) { const unsigned char *p = parser->parser.cur; pool_t pool; /* reason-phrase = *( HTAB / SP / VCHAR / obs-text ) */ // FIXME: limit length while (p < parser->parser.end && http_char_is_text(*p)) p++; if (p == parser->parser.end) return 0; pool = http_message_parser_get_pool(&parser->parser); parser->response_reason = p_strdup_until(pool, parser->parser.cur, p); parser->parser.cur = p; return 1; } static const char *_reply_sanitize(struct http_message_parser *parser) { string_t *str = t_str_new(32); const unsigned char *p; unsigned int i; bool quote_open = FALSE; i_assert(parser->cur < parser->end); for (p = parser->cur, i = 0; p < parser->end && i < 20; p++, i++) { if (*p >= 0x20 && *p < 0x7F) { if (!quote_open) { str_append_c(str, '`'); quote_open = TRUE; } str_append_c(str, *p); } else { if (quote_open) { str_append_c(str, '\''); quote_open = FALSE; } if (*p == 0x0a) str_append(str, ""); else if (*p == 0x0d) str_append(str, ""); else str_printfa(str, "<0x%02x>", *p); } } if (quote_open) str_append_c(str, '\''); return str_c(str); } static int http_response_parse(struct http_response_parser *parser) { struct http_message_parser *_parser = &parser->parser; int ret; /* RFC 7230, Section 3.1.2: Status Line status-line = HTTP-version SP status-code SP reason-phrase CRLF status-code = 3DIGIT reason-phrase = *( HTAB / SP / VCHAR / obs-text ) */ switch (parser->state) { case HTTP_RESPONSE_PARSE_STATE_INIT: parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_VERSION: if ((ret=http_message_parse_version(_parser)) <= 0) { if (ret < 0) _parser->error = t_strdup_printf( "Invalid HTTP version in response: %s", _reply_sanitize(_parser)); return ret; } parser->state = HTTP_RESPONSE_PARSE_STATE_SP1; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_SP1: if (*_parser->cur != ' ') { _parser->error = t_strdup_printf ("Expected ' ' after response version, but found %s", _reply_sanitize(_parser)); return -1; } _parser->cur++; parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS; if (_parser->cur >= _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_STATUS: if ((ret=http_response_parse_status(parser)) <= 0) { if (ret < 0) _parser->error = "Invalid HTTP status code in response"; return ret; } parser->state = HTTP_RESPONSE_PARSE_STATE_SP2; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_SP2: if (*_parser->cur != ' ') { _parser->error = t_strdup_printf ("Expected ' ' after response status code, but found %s", _reply_sanitize(_parser)); return -1; } _parser->cur++; parser->state = HTTP_RESPONSE_PARSE_STATE_REASON; if (_parser->cur >= _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_REASON: if ((ret=http_response_parse_reason(parser)) <= 0) { i_assert(ret == 0); return 0; } parser->state = HTTP_RESPONSE_PARSE_STATE_CR; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_CR: if (*_parser->cur == '\r') _parser->cur++; parser->state = HTTP_RESPONSE_PARSE_STATE_LF; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_RESPONSE_PARSE_STATE_LF: if (*_parser->cur != '\n') { _parser->error = t_strdup_printf ("Expected line end after response, but found %s", _reply_sanitize(_parser)); return -1; } _parser->cur++; parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER; return 1; case HTTP_RESPONSE_PARSE_STATE_HEADER: default: break; } i_unreached(); return -1; } static int http_response_parse_status_line(struct http_response_parser *parser) { struct http_message_parser *_parser = &parser->parser; const unsigned char *begin; size_t size, old_bytes = 0; int ret; while ((ret = i_stream_read_data(_parser->input, &begin, &size, old_bytes)) > 0) { _parser->cur = begin; _parser->end = _parser->cur + size; if ((ret = http_response_parse(parser)) < 0) return -1; i_stream_skip(_parser->input, _parser->cur - begin); if (ret > 0) return 1; old_bytes = i_stream_get_data_size(_parser->input); } if (ret == -2) { _parser->error = "HTTP status line is too long"; return -1; } if (ret < 0) { if (_parser->input->eof && parser->state == HTTP_RESPONSE_PARSE_STATE_INIT) return 0; _parser->error = "Stream error"; return -1; } return 0; } static int http_response_parse_retry_after(const char *hdrval, time_t resp_time, time_t *retry_after_r) { time_t delta; /* RFC 7231, Section 7.1.3: Retry-After The value of this field can be either an HTTP-date or a number of seconds to delay after the response is received. Retry-After = HTTP-date / delta-seconds A delay-seconds value is a non-negative decimal integer, representing time in seconds. delta-seconds = 1*DIGIT */ if (str_to_time(hdrval, &delta) >= 0) { if (resp_time == (time_t)-1) { return -1; } *retry_after_r = resp_time + delta; return 0; } return (http_date_parse ((unsigned char *)hdrval, strlen(hdrval), retry_after_r) ? 0 : -1); } int http_response_parse_next(struct http_response_parser *parser, enum http_response_payload_type payload_type, struct http_response *response, const char **error_r) { const char *hdrval; time_t retry_after = (time_t)-1; int ret; i_zero(response); /* make sure we finished streaming payload from previous response before we continue. */ if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) { *error_r = parser->parser.error; return ret; } if (parser->state == HTTP_RESPONSE_PARSE_STATE_INIT) http_response_parser_restart(parser); /* RFC 7230, Section 3: HTTP-message = start-line *( header-field CRLF ) CRLF [ message-body ] */ if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) { if ((ret = http_response_parse_status_line(parser)) <= 0) { *error_r = parser->parser.error; return ret; } } if ((ret = http_message_parse_headers(&parser->parser)) <= 0) { *error_r = parser->parser.error; return ret; } /* RFC 7230, Section 3.3.2: Content-Length A server MUST NOT send a Content-Length header field in any response with a status code of 1xx (Informational) or 204 (No Content). */ if ((parser->response_status / 100 == 1 || parser->response_status == 204) && parser->parser.msg.content_length > 0) { *error_r = t_strdup_printf( "Unexpected Content-Length header field for %u response " "(length=%"PRIuUOFF_T")", parser->response_status, parser->parser.msg.content_length); return -1; } /* RFC 7230, Section 3.3.3: Message Body Length 1. Any response to a HEAD request and any response with a 1xx (Informational), 204 (No Content), or 304 (Not Modified) status code is always terminated by the first empty line after the header fields, regardless of the header fields present in the message, and thus cannot contain a message body. */ if (parser->response_status / 100 == 1 || parser->response_status == 204 || parser->response_status == 304) { // HEAD is handled in caller payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT; } if ((payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED) || (payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL && parser->response_status / 100 != 2)) { /* [ message-body ] */ if (http_message_parse_body(&parser->parser, FALSE) < 0) { *error_r = parser->parser.error; return -1; } } /* RFC 7231, Section 7.1.3: Retry-After Servers send the "Retry-After" header field to indicate how long the user agent ought to wait before making a follow-up request. When sent with a 503 (Service Unavailable) response, Retry-After indicates how long the service is expected to be unavailable to the client. When sent with any 3xx (Redirection) response, Retry-After indicates the minimum time that the user agent is asked to wait before issuing the redirected request. */ if (parser->response_status == 503 || (parser->response_status / 100) == 3) { hdrval = http_header_field_get(parser->parser.msg.header, "Retry-After"); if (hdrval != NULL) { (void)http_response_parse_retry_after (hdrval, parser->parser.msg.date, &retry_after); /* broken Retry-After header is ignored */ } } parser->state = HTTP_RESPONSE_PARSE_STATE_INIT; response->status = parser->response_status; response->reason = parser->response_reason; response->version_major = parser->parser.msg.version_major; response->version_minor = parser->parser.msg.version_minor; response->location = parser->parser.msg.location; response->date = parser->parser.msg.date; response->retry_after = retry_after; response->payload = parser->parser.payload; response->header = parser->parser.msg.header; response->headers = *http_header_get_fields(response->header); /* FIXME: remove in v2.3 */ response->connection_options = parser->parser.msg.connection_options; response->connection_close = parser->parser.msg.connection_close; return 1; } dovecot-2.2.33.2/src/lib-http/test-http-payload.c0000644000175000017500000011214213165463624016401 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "llist.h" #include "abspath.h" #include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "iostream-temp.h" #include "connection.h" #include "test-common.h" #include "http-url.h" #include "http-request.h" #include "http-server.h" #include "http-client.h" #include #include #include #include #include #include #include static bool debug = FALSE; static bool blocking = FALSE; static bool request_100_continue = FALSE; static size_t read_server_partial = 0; static size_t read_client_partial = 0; static unsigned int test_max_pending = 200; static unsigned int client_ioloop_nesting = 0; static struct ip_addr bind_ip; static in_port_t bind_port = 0; static int fd_listen = -1; static pid_t server_pid = (pid_t)-1; static struct ioloop *ioloop_nested = NULL; static unsigned ioloop_nested_first = 0; static unsigned ioloop_nested_last = 0; /* * Test files */ static ARRAY_TYPE(const_string) files; static pool_t files_pool; static void test_files_read_dir(const char *path) { DIR *dirp; /* open the directory */ if ((dirp = opendir(path)) == NULL) { if (errno == ENOENT || errno == EACCES) return; i_fatal("test files: " "failed to open directory %s: %m", path); } /* read entries */ for (;;) { const char *file; struct dirent *dp; struct stat st; errno = 0; if ((dp=readdir(dirp)) == NULL) break; if (*dp->d_name == '.') continue; file = t_abspath_to(dp->d_name, path); if (stat(file, &st) == 0) { if (S_ISREG(st.st_mode)) { file += 2; /* skip "./" */ file = p_strdup(files_pool, file); array_append(&files, &file, 1); } else { test_files_read_dir(file); } } } if (errno != 0) i_fatal("test files: " "failed to read directory %s: %m", path); /* Close the directory */ if (closedir(dirp) < 0) i_error("test files: " "failed to close directory %s: %m", path); } static void test_files_init(void) { /* initialize file array */ files_pool = pool_alloconly_create (MEMPOOL_GROWING"http_server_request", 4096); p_array_init(&files, files_pool, 512); /* obtain all filenames */ test_files_read_dir("."); } static void test_files_deinit(void) { pool_unref(&files_pool); } static struct istream * test_file_open(const char *path, unsigned int *status_r, const char **reason_r) ATTR_NULL(2, 3) { int fd; if (status_r != NULL) *status_r = 200; if (reason_r != NULL) *reason_r = "OK"; fd = open(path, O_RDONLY); if (fd < 0) { if (debug) { i_debug("test files: " "open(%s) failed: %m", path); } switch (errno) { case EFAULT: case ENOENT: if (status_r != NULL) *status_r = 404; if (reason_r != NULL) *reason_r = "Not Found"; break; case EISDIR: case EACCES: if (status_r != NULL) *status_r = 403; if (reason_r != NULL) *reason_r = "Forbidden"; break; default: if (status_r != NULL) *status_r = 500; if (reason_r != NULL) *reason_r = "Internal Server Error"; } return NULL; } return i_stream_create_fd(fd, 40960, TRUE); } /* * Test server */ struct client { pool_t pool; struct client *prev, *next; struct http_server_connection *http_conn; }; struct client_request { struct client *client; struct http_server_request *server_req; const char *path; struct istream *payload_input; struct ostream *payload_output; struct io *io; }; static const struct http_server_callbacks http_callbacks; static struct http_server *http_server; static struct io *io_listen; static struct client *clients; /* location: /succes */ static void client_handle_success_request(struct client_request *creq) { struct http_server_request *req = creq->server_req; const struct http_request *hreq = http_server_request_get(req); struct http_server_response *resp; if (strcmp(hreq->method, "GET") != 0) { http_server_request_fail(req, 405, "Method Not Allowed"); return; } resp = http_server_response_create(req, 200, "OK"); http_server_response_submit(resp); } /* location: /download/... */ static void client_handle_download_request( struct client_request *creq, const char *path) { struct http_server_request *req = creq->server_req; const struct http_request *hreq = http_server_request_get(req); struct http_server_response *resp; const char *fpath, *reason; struct istream *fstream; struct ostream *output; unsigned int status; int ret; if (strcmp(hreq->method, "GET") != 0) { http_server_request_fail(req, 405, "Method Not Allowed"); return; } fpath = t_strconcat(".", path, NULL); if (debug) { i_debug("test server: download: " "sending payload for %s", fpath); } fstream = test_file_open(fpath, &status, &reason); if (fstream == NULL) { http_server_request_fail(req, status, reason); return; } resp = http_server_response_create(req, 200, "OK"); http_server_response_add_header(resp, "Content-Type", "text/plain"); if (blocking) { output = http_server_response_get_payload_output(resp, TRUE); while ((ret=o_stream_send_istream (output, fstream)) > 0); if (ret < 0) { i_fatal("test server: download: " "failed to send blocking file payload"); } if (debug) { i_debug("test server: download: " "finished sending blocking payload for %s" "(%"PRIuUOFF_T":%"PRIuUOFF_T")", fpath, fstream->v_offset, output->offset); } o_stream_close(output); o_stream_unref(&output); } else { http_server_response_set_payload(resp, fstream); http_server_response_submit(resp); } i_stream_unref(&fstream); } /* location: /echo */ static void client_request_read_echo_more(struct client_request *creq) { struct http_server_response *resp; struct istream *payload_input; off_t ret; o_stream_set_max_buffer_size(creq->payload_output, IO_BLOCK_SIZE); ret = o_stream_send_istream(creq->payload_output, creq->payload_input); o_stream_set_max_buffer_size(creq->payload_output, (size_t)-1); if (ret < 0) { if (creq->payload_output->stream_errno != 0) { i_fatal("test server: echo: " "Failed to write all echo payload [%s]", creq->path); } if (creq->payload_input->stream_errno != 0) { i_fatal("test server: echo: " "Failed to read all echo payload [%s]", creq->path); } i_unreached(); } if (i_stream_have_bytes_left(creq->payload_input)) return; io_remove(&creq->io); i_stream_unref(&creq->payload_input); if (debug) { i_debug("test server: echo: " "finished receiving payload for %s", creq->path); } payload_input = iostream_temp_finish(&creq->payload_output, 4096); resp = http_server_response_create (creq->server_req, 200, "OK"); http_server_response_add_header(resp, "Content-Type", "text/plain"); http_server_response_set_payload(resp, payload_input); http_server_response_submit(resp); i_stream_unref(&payload_input); } static void client_handle_echo_request(struct client_request *creq, const char *path) { struct http_server_request *req = creq->server_req; const struct http_request *hreq = http_server_request_get(req); struct http_server_response *resp; struct ostream *payload_output; uoff_t size; int ret; creq->path = p_strdup (http_server_request_get_pool(req), path); if (strcmp(hreq->method, "PUT") != 0) { http_server_request_fail(req, 405, "Method Not Allowed"); return; } size = 0; (void)http_request_get_payload_size(hreq, &size); if (size == 0) { resp = http_server_response_create (creq->server_req, 200, "OK"); http_server_response_add_header(resp, "Content-Type", "text/plain"); http_server_response_submit(resp); return; } payload_output = iostream_temp_create ("/tmp/test-http-server", 0); if (blocking) { struct istream *payload_input; payload_input = http_server_request_get_payload_input(req, TRUE); if (read_server_partial > 0) { struct istream *partial = i_stream_create_limit(payload_input, read_server_partial); i_stream_unref(&payload_input); payload_input = partial; } while ((ret=o_stream_send_istream (payload_output, payload_input)) > 0); if (ret < 0) { i_fatal("test server: echo: " "failed to receive blocking echo payload"); } i_stream_unref(&payload_input); payload_input = iostream_temp_finish(&payload_output, 4096); if (debug) { i_debug("test server: echo: " "finished receiving blocking payload for %s", path); } resp = http_server_response_create(req, 200, "OK"); http_server_response_add_header(resp, "Content-Type", "text/plain"); payload_output = http_server_response_get_payload_output(resp, TRUE); while ((ret=o_stream_send_istream (payload_output, payload_input)) > 0); if (ret < 0) { i_fatal("test server: echo: " "failed to send blocking echo payload"); } if (debug) { i_debug("test server: echo: " "finished sending blocking payload for %s", path); } i_stream_unref(&payload_input); o_stream_close(payload_output); o_stream_unref(&payload_output); } else { creq->payload_output = payload_output; creq->payload_input = http_server_request_get_payload_input(req, FALSE); if (read_server_partial > 0) { struct istream *partial = i_stream_create_limit(creq->payload_input, read_server_partial); i_stream_unref(&creq->payload_input); creq->payload_input = partial; } creq->io = io_add_istream(creq->payload_input, client_request_read_echo_more, creq); client_request_read_echo_more(creq); } } /* request */ static void http_server_request_destroyed(void *context); static struct client_request * client_request_init(struct client *client, struct http_server_request *req) { struct client_request *creq; pool_t pool = http_server_request_get_pool(req); http_server_request_ref(req); creq = p_new(pool, struct client_request, 1); creq->client = client; creq->server_req = req; http_server_request_set_destroy_callback(req, http_server_request_destroyed, creq); return creq; } static void client_request_deinit(struct client_request **_creq) { struct client_request *creq = *_creq; struct http_server_request *req = creq->server_req; *_creq = NULL; if (creq->io != NULL) { i_stream_unref(&creq->payload_input); io_remove(&creq->io); } http_server_request_unref(&req); } static void http_server_request_destroyed(void *context) { struct client_request *creq = (struct client_request *)context; client_request_deinit(&creq); } static void client_handle_request(void *context, struct http_server_request *req) { const struct http_request *hreq = http_server_request_get(req); const char *path = hreq->target.url->path, *p; struct client *client = (struct client *)context; struct client_request *creq; if (debug) { i_debug("test server: " "request method=`%s' path=`%s'", hreq->method, path); } creq = client_request_init(client, req); if (strcmp(path, "/success") == 0) { client_handle_success_request(creq); return; } if ((p=strchr(path+1, '/')) == NULL) { http_server_request_fail(req, 404, "Not found"); return; } if (strncmp(path, "/download", p-path) == 0) { client_handle_download_request(creq, p); return; } if (strncmp(path, "/echo", p-path) == 0) { client_handle_echo_request(creq, p); return; } http_server_request_fail(req, 404, "Not found"); return; } /* client connection */ static void client_connection_destroy(void *context, const char *reason); static const struct http_server_callbacks http_callbacks = { .connection_destroy = client_connection_destroy, .handle_request = client_handle_request }; static void client_init(int fd) { struct client *client; pool_t pool; net_set_nonblock(fd, TRUE); pool = pool_alloconly_create("client", 256); client = p_new(pool, struct client, 1); client->pool = pool; client->http_conn = http_server_connection_create(http_server, fd, fd, FALSE, &http_callbacks, client); DLLIST_PREPEND(&clients, client); } static void client_deinit(struct client **_client) { struct client *client = *_client; *_client = NULL; DLLIST_REMOVE(&clients, client); if (client->http_conn != NULL) http_server_connection_close(&client->http_conn, "deinit"); pool_unref(&client->pool); } static void client_connection_destroy(void *context, const char *reason ATTR_UNUSED) { struct client *client = context; client->http_conn = NULL; client_deinit(&client); } static void client_accept(void *context ATTR_UNUSED) { int fd; /* accept new client */ fd = net_accept(fd_listen, NULL, NULL); if (fd == -1) return; if (fd == -2) { i_fatal("test server: accept() failed: %m"); } client_init(fd); } /* */ static void test_server_init(const struct http_server_settings *server_set) { /* open server socket */ io_listen = io_add(fd_listen, IO_READ, client_accept, (void *)NULL); http_server = http_server_init(server_set); } static void test_server_deinit(void) { /* close server socket */ io_remove(&io_listen); /* deinitialize */ http_server_deinit(&http_server); } /* * Test client */ struct test_client_request { struct test_client_request *prev, *next; struct http_client_request *hreq; struct io *io; struct istream *payload; struct istream *file; unsigned int files_idx; }; static struct http_client *http_client; static struct test_client_request *client_requests; static unsigned int client_files_first, client_files_last; static struct test_client_request * test_client_request_new(void) { struct test_client_request *tcreq; tcreq = i_new(struct test_client_request, 1); DLLIST_PREPEND(&client_requests, tcreq); return tcreq; } static void test_client_request_destroy(void *context) { struct test_client_request *tcreq = (struct test_client_request *)context; if (tcreq->io != NULL) io_remove(&tcreq->io); if (tcreq->payload != NULL) i_stream_unref(&tcreq->payload); if (tcreq->file != NULL) i_stream_unref(&tcreq->file); DLLIST_REMOVE(&client_requests, tcreq); i_free(tcreq); } static void test_client_requests_switch_ioloop(void) { struct test_client_request *tcreq; for (tcreq = client_requests; tcreq != NULL; tcreq = tcreq->next) { if (tcreq->io != NULL) tcreq->io = io_loop_move_io(&tcreq->io); if (tcreq->payload != NULL) i_stream_switch_ioloop(tcreq->payload); } } /* download */ static void test_client_download_continue(void); static void test_client_download_finished(unsigned int files_idx) { const char **paths; unsigned int count; paths = array_get_modifiable(&files, &count); i_assert(files_idx < count); i_assert(client_files_first < count); i_assert(paths[files_idx] != NULL); paths[files_idx] = NULL; test_client_download_continue(); } static void test_client_download_payload_input(struct test_client_request *tcreq) { struct istream *payload = tcreq->payload; const unsigned char *pdata, *fdata; size_t psize, fsize, pleft; unsigned int files_idx = tcreq->files_idx; off_t ret; /* read payload */ while ((ret=i_stream_read_data (payload, &pdata, &psize, 0)) > 0) { if (debug) { i_debug("test client: download: " "got data for [%u] (size=%d)", tcreq->files_idx, (int)psize); } /* compare with file on disk */ pleft = psize; while ((ret=i_stream_read_data (tcreq->file, &fdata, &fsize, 0)) > 0 && pleft > 0) { fsize = (fsize > pleft ? pleft : fsize); if (memcmp(pdata, fdata, fsize) != 0) { i_fatal("test client: download: " "received data does not match file " "(%"PRIuUOFF_T":%"PRIuUOFF_T")", payload->v_offset, tcreq->file->v_offset); } i_stream_skip(tcreq->file, fsize); pleft -= fsize; pdata += fsize; } if (ret < 0 && tcreq->file->stream_errno != 0) { i_fatal("test client: download: " "failed to read file: %s", i_stream_get_error(tcreq->file)); } i_stream_skip(payload, psize); } if (ret == 0) { if (debug) { i_debug("test client: download: " "need more data for [%u]", tcreq->files_idx); } /* we will be called again for this request */ } else { (void)i_stream_read(tcreq->file); if (payload->stream_errno != 0) { i_fatal("test client: download: " "failed to read request payload: %s", i_stream_get_error(payload)); } if (i_stream_have_bytes_left(tcreq->file)) { if (i_stream_read_data(tcreq->file, &fdata, &fsize, 0) <= 0) fsize = 0; i_fatal("test client: download: " "payload ended prematurely " "(at least %"PRIuSIZE_T" bytes left)", fsize); } else if (debug) { i_debug("test client: download: " "finished request for [%u]", tcreq->files_idx); } /* dereference payload stream; finishes the request */ tcreq->payload = NULL; io_remove(&tcreq->io); /* holds a reference too */ i_stream_unref(&payload); /* finished */ test_client_download_finished(files_idx); } } static void test_client_download_response(const struct http_response *resp, struct test_client_request *tcreq) { const char **paths; const char *path; unsigned int count, status; struct istream *fstream; const char *reason; if (debug) { i_debug("test client: download: " "got response for [%u]", tcreq->files_idx); } paths = array_get_modifiable(&files, &count); i_assert(tcreq->files_idx < count); i_assert(client_files_first < count); path = paths[tcreq->files_idx]; i_assert(path != NULL); if (debug) { i_debug("test client: download: " "path for [%u]: %s", tcreq->files_idx, path); } fstream = test_file_open(path, &status, &reason); i_assert(fstream != NULL); if (status != resp->status) { i_fatal("test client: download: " "got wrong response for %s: %u %s (expected: %u %s)", path, resp->status, resp->reason, status, reason); } if (resp->status / 100 != 2) { if (debug) { i_debug("test client: download: " "HTTP request for %s failed: %u %s", path, resp->status, resp->reason); } i_stream_unref(&fstream); test_client_download_finished(tcreq->files_idx); return; } if (resp->payload == NULL) { if (debug) { i_debug("test client: download: " "no payload for %s [%u]", path, tcreq->files_idx); } i_stream_unref(&fstream); test_client_download_finished(tcreq->files_idx); return; } i_assert(fstream != NULL); if (read_client_partial == 0) { i_stream_ref(resp->payload); tcreq->payload = resp->payload; tcreq->file = fstream; } else { struct istream *payload = resp->payload; tcreq->payload = i_stream_create_limit (payload, read_client_partial); tcreq->file = i_stream_create_limit (fstream, read_client_partial); i_stream_unref(&fstream); } tcreq->io = io_add_istream(tcreq->payload, test_client_download_payload_input, tcreq); test_client_download_payload_input(tcreq); } static void test_client_download_continue(void) { struct test_client_request *tcreq; struct http_client_request *hreq; const char *const *paths; unsigned int count; paths = array_get(&files, &count); i_assert(client_files_first <= count); i_assert(client_files_last <= count); i_assert(client_files_first <= client_files_last); for (; client_files_first < client_files_last && paths[client_files_first] == NULL; client_files_first++) if (debug) { i_debug("test client: download: " "received until [%u]", client_files_first-1); } if (client_files_first >= count) { io_loop_stop(current_ioloop); return; } for (; client_files_last < count && (client_files_last - client_files_first) < test_max_pending; client_files_last++) { const char *path = paths[client_files_last]; tcreq = test_client_request_new(); tcreq->files_idx = client_files_last; if (debug) { i_debug("test client: download: " "retrieving %s [%u]", path, tcreq->files_idx); } hreq = tcreq->hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), t_strconcat("/download/", path, NULL), test_client_download_response, tcreq); http_client_request_set_port(hreq, bind_port); http_client_request_set_destroy_callback(hreq, test_client_request_destroy, tcreq); http_client_request_submit(hreq); } } static void test_client_download(const struct http_client_settings *client_set) { /* create client */ http_client = http_client_init(client_set); /* start querying server */ client_files_first = client_files_last = 0; test_client_download_continue(); } /* echo */ static void test_client_echo_continue(void); static void test_client_echo_finished(unsigned int files_idx) { const char **paths; unsigned int count; paths = array_get_modifiable(&files, &count); i_assert(files_idx < count); i_assert(client_files_first < count); i_assert(paths[files_idx] != NULL); paths[files_idx] = NULL; test_client_echo_continue(); } static void test_client_echo_payload_input(struct test_client_request *tcreq) { struct istream *payload = tcreq->payload; const unsigned char *pdata, *fdata; size_t psize, fsize, pleft; unsigned int files_idx = tcreq->files_idx; off_t ret; /* read payload */ while ((ret=i_stream_read_data (payload, &pdata, &psize, 0)) > 0) { if (debug) { i_debug("test client: echo: " "got data for [%u] (size=%d)", tcreq->files_idx, (int)psize); } /* compare with file on disk */ pleft = psize; while ((ret=i_stream_read_data (tcreq->file, &fdata, &fsize, 0)) > 0 && pleft > 0) { fsize = (fsize > pleft ? pleft : fsize); if (memcmp(pdata, fdata, fsize) != 0) { i_fatal("test client: echo: " "received data does not match file " "(%"PRIuUOFF_T":%"PRIuUOFF_T")", payload->v_offset, tcreq->file->v_offset); } i_stream_skip(tcreq->file, fsize); pleft -= fsize; pdata += fsize; } if (ret < 0 && tcreq->file->stream_errno != 0) { i_fatal("test client: echo: " "failed to read file: %s", i_stream_get_error(tcreq->file)); } i_stream_skip(payload, psize); } if (ret == 0) { if (debug) { i_debug("test client: echo: " "need more data for [%u]", tcreq->files_idx); } /* we will be called again for this request */ } else { (void)i_stream_read(tcreq->file); if (payload->stream_errno != 0) { i_fatal("test client: echo: " "failed to read request payload: %s", i_stream_get_error(payload)); } if (i_stream_have_bytes_left(tcreq->file)) { if (i_stream_read_data(tcreq->file, &fdata, &fsize, 0) <= 0) fsize = 0; i_fatal("test client: echo: " "payload ended prematurely " "(at least %"PRIuSIZE_T" bytes left)", fsize); } else if (debug) { i_debug("test client: echo: " "finished request for [%u]", tcreq->files_idx); } /* dereference payload stream; finishes the request */ tcreq->payload = NULL; io_remove(&tcreq->io); /* holds a reference too */ i_stream_unref(&payload); /* finished */ test_client_echo_finished(files_idx); } } static void test_client_echo_response(const struct http_response *resp, struct test_client_request *tcreq) { const char **paths; const char *path; unsigned int count, status; struct istream *fstream; if (debug) { i_debug("test client: echo: " "got response for [%u]", tcreq->files_idx); } paths = array_get_modifiable(&files, &count); i_assert(tcreq->files_idx < count); i_assert(client_files_first < count); path = paths[tcreq->files_idx]; i_assert(path != NULL); if (debug) { i_debug("test client: echo: " "path for [%u]: %s", tcreq->files_idx, path); } if (resp->status / 100 != 2) { i_fatal("test client: echo: " "HTTP request for %s failed: %u %s", path, resp->status, resp->reason); } fstream = test_file_open(path, &status, NULL); if (fstream == NULL) { i_fatal("test client: echo: " "failed to open %s", path); } if (read_server_partial > 0) { struct istream *partial = i_stream_create_limit (fstream, read_server_partial); i_stream_unref(&fstream); fstream = partial; } if (resp->payload == NULL) { // FIXME: check file is empty if (debug) { i_debug("test client: echo: " "no payload for %s [%u]", path, tcreq->files_idx); } i_stream_unref(&fstream); test_client_echo_finished(tcreq->files_idx); return; } i_assert(fstream != NULL); tcreq->file = fstream; i_stream_ref(resp->payload); tcreq->payload = resp->payload; tcreq->io = io_add_istream(resp->payload, test_client_echo_payload_input, tcreq); test_client_echo_payload_input(tcreq); } static void test_client_echo_continue(void) { struct test_client_request *tcreq; struct http_client_request *hreq; const char **paths; unsigned int count, first_submitted; paths = array_get_modifiable(&files, &count); i_assert(client_files_first <= count); i_assert(client_files_last <= count); i_assert(client_files_first <= client_files_last); for (; client_files_first < client_files_last && paths[client_files_first] == NULL; client_files_first++); if (debug) { i_debug("test client: echo: " "received until [%u/%u]", client_files_first-1, count); } if (debug && client_files_first < count) { const char *path = paths[client_files_first]; i_debug("test client: echo: " "next blocking: %s [%d]", (path == NULL ? "none" : path), client_files_first); } if (client_files_first >= count) { io_loop_stop(current_ioloop); return; } first_submitted = client_files_last; for (; client_files_last < count && (client_files_last - client_files_first) < test_max_pending; client_files_last++) { struct istream *fstream; const char *path = paths[client_files_last]; fstream = test_file_open(path, NULL, NULL); if (fstream == NULL) { paths[client_files_last] = NULL; if (debug) { i_debug("test client: echo: " "skipping %s [%u]", path, client_files_last); } continue; } if (debug) { i_debug("test client: echo: " "retrieving %s [%u]", path, client_files_last); } tcreq = test_client_request_new(); tcreq->files_idx = client_files_last; hreq = tcreq->hreq = http_client_request(http_client, "PUT", net_ip2addr(&bind_ip), t_strconcat("/echo/", path, NULL), test_client_echo_response, tcreq); http_client_request_set_port(hreq, bind_port); http_client_request_set_payload (hreq, fstream, request_100_continue); http_client_request_set_destroy_callback(hreq, test_client_request_destroy, tcreq); http_client_request_submit(hreq); i_stream_unref(&fstream); } /* run nested ioloop (if requested) if new requests cross a nesting boundary */ if (ioloop_nested != NULL) { unsigned int i; i_assert(ioloop_nested_first <= count); i_assert(ioloop_nested_last <= count); for (i = ioloop_nested_first; i < ioloop_nested_last; i++) { if (paths[i] != NULL) { if (debug) { i_debug("test client: " "not leaving ioloop [%u]", i); } break; } } if (i == ioloop_nested_last) io_loop_stop(ioloop_nested); } else if (client_ioloop_nesting > 0 && ((client_files_last / client_ioloop_nesting) != (first_submitted / client_ioloop_nesting)) ) { struct ioloop *prev_ioloop = current_ioloop; ioloop_nested_first = first_submitted; ioloop_nested_last = first_submitted + client_ioloop_nesting; if (ioloop_nested_last > client_files_last) ioloop_nested_last = client_files_last; if (debug) { i_debug("test client: echo: entering ioloop for %u...%u", ioloop_nested_first, ioloop_nested_last); } ioloop_nested = io_loop_create(); http_client_switch_ioloop(http_client); test_client_requests_switch_ioloop(); io_loop_run(ioloop_nested); io_loop_set_current(prev_ioloop); http_client_switch_ioloop(http_client); test_client_requests_switch_ioloop(); io_loop_set_current(ioloop_nested); io_loop_destroy(&ioloop_nested); ioloop_nested = NULL; if (debug) { i_debug("test client: echo: leaving ioloop for %u...%u", ioloop_nested_first, ioloop_nested_last); } ioloop_nested_first = ioloop_nested_last = 0; } } static void test_client_echo(const struct http_client_settings *client_set) { /* create client */ http_client = http_client_init(client_set); /* start querying server */ client_files_first = client_files_last = 0; test_client_echo_continue(); } /* cleanup */ static void test_client_deinit(void) { http_client_deinit(&http_client); } /* * Tests */ static void test_open_server_fd(void) { if (fd_listen != -1) i_close_fd(&fd_listen); fd_listen = net_listen(&bind_ip, &bind_port, 128); if (fd_listen == -1) { i_fatal("listen(%s:%u) failed: %m", net_ip2addr(&bind_ip), bind_port); } } static void test_server_kill(void) { if (server_pid != (pid_t)-1) { (void)kill(server_pid, SIGKILL); (void)waitpid(server_pid, NULL, 0); } server_pid = (pid_t)-1; } static void test_run_client_server( const struct http_client_settings *client_set, const struct http_server_settings *server_set, void (*client_init)(const struct http_client_settings *client_set)) { struct ioloop *ioloop; test_open_server_fd(); if ((server_pid = fork()) == (pid_t)-1) i_fatal("fork() failed: %m"); if (server_pid == 0) { server_pid = (pid_t)-1; hostpid_init(); if (debug) i_debug("server: PID=%s", my_pid); /* child: server */ ioloop_nested = FALSE; ioloop = io_loop_create(); test_server_init(server_set); io_loop_run(ioloop); test_server_deinit(); io_loop_destroy(&ioloop); i_close_fd(&fd_listen); } else { if (debug) i_debug("client: PID=%s", my_pid); i_close_fd(&fd_listen); /* parent: client */ ioloop_nested = FALSE; ioloop = io_loop_create(); client_init(client_set); io_loop_run(ioloop); test_client_deinit(); io_loop_destroy(&ioloop); test_server_kill(); } } static void test_run_sequential( void (*client_init)(const struct http_client_settings *client_set)) { struct http_server_settings http_server_set; struct http_client_settings http_client_set; /* download files from blocking server */ /* server settings */ i_zero(&http_server_set); http_server_set.max_pipelined_requests = 0; http_server_set.debug = debug; http_server_set.request_limits.max_payload_size = (uoff_t)-1; /* client settings */ i_zero(&http_client_set); http_client_set.max_idle_time_msecs = 5*1000; http_client_set.max_parallel_connections = 1; http_client_set.max_pipelined_requests = 1; http_client_set.max_redirects = 0; http_client_set.max_attempts = 1; http_client_set.debug = debug; test_files_init(); test_run_client_server (&http_client_set, &http_server_set, client_init); test_files_deinit(); test_out("sequential", TRUE); } static void test_run_pipeline( void (*client_init)(const struct http_client_settings *client_set)) { struct http_server_settings http_server_set; struct http_client_settings http_client_set; /* download files from blocking server */ /* server settings */ i_zero(&http_server_set); http_server_set.max_pipelined_requests = 4; http_server_set.debug = debug; http_server_set.request_limits.max_payload_size = (uoff_t)-1; /* client settings */ i_zero(&http_client_set); http_client_set.max_idle_time_msecs = 5*1000; http_client_set.max_parallel_connections = 1; http_client_set.max_pipelined_requests = 8; http_client_set.max_redirects = 0; http_client_set.max_attempts = 1; http_client_set.debug = debug; test_files_init(); test_run_client_server (&http_client_set, &http_server_set, client_init); test_files_deinit(); test_out("pipeline", TRUE); } static void test_run_parallel( void (*client_init)(const struct http_client_settings *client_set)) { struct http_server_settings http_server_set; struct http_client_settings http_client_set; /* download files from blocking server */ /* server settings */ i_zero(&http_server_set); http_server_set.max_pipelined_requests = 4; http_server_set.debug = debug; http_server_set.request_limits.max_payload_size = (uoff_t)-1; /* client settings */ i_zero(&http_client_set); http_client_set.max_idle_time_msecs = 5*1000; http_client_set.max_parallel_connections = 40; http_client_set.max_pipelined_requests = 8; http_client_set.max_redirects = 0; http_client_set.max_attempts = 1; http_client_set.debug = debug; test_files_init(); test_run_client_server (&http_client_set, &http_server_set, client_init); test_files_deinit(); test_out("parallel", TRUE); } static void test_download_server_nonblocking(void) { test_begin("http payload download (server non-blocking)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_download); test_run_pipeline(test_client_download); test_run_parallel(test_client_download); test_end(); } static void test_download_server_blocking(void) { test_begin("http payload download (server blocking)"); blocking = TRUE; request_100_continue = FALSE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_download); test_run_pipeline(test_client_download); test_run_parallel(test_client_download); test_end(); } static void test_echo_server_nonblocking(void) { test_begin("http payload echo (server non-blocking)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_echo_server_blocking(void) { test_begin("http payload echo (server blocking)"); blocking = TRUE; request_100_continue = FALSE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_echo_server_nonblocking_sync(void) { test_begin("http payload echo (server non-blocking; 100-continue)"); blocking = FALSE; request_100_continue = TRUE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_echo_server_blocking_sync(void) { test_begin("http payload echo (server blocking; 100-continue)"); blocking = TRUE; request_100_continue = TRUE; read_server_partial = 0; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_echo_server_nonblocking_partial(void) { test_begin("http payload echo (server non-blocking; partial short)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 1024; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); test_begin("http payload echo (server non-blocking; partial long)"); read_server_partial = IO_BLOCK_SIZE + 1024; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_echo_server_blocking_partial(void) { test_begin("http payload echo (server blocking; partial short)"); blocking = TRUE; request_100_continue = FALSE; read_server_partial = 1024; client_ioloop_nesting = 0; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); test_begin("http payload echo (server blocking; partial long)"); read_server_partial = IO_BLOCK_SIZE + 1024; test_run_sequential(test_client_echo); test_run_pipeline(test_client_echo); test_run_parallel(test_client_echo); test_end(); } static void test_download_client_partial(void) { test_begin("http payload download (client partial)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 0; read_client_partial = 1024; client_ioloop_nesting = 0; test_run_sequential(test_client_download); test_run_pipeline(test_client_download); test_run_parallel(test_client_download); test_end(); test_begin("http payload download (client partial long)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 0; read_client_partial = IO_BLOCK_SIZE + 1024; test_run_sequential(test_client_download); test_run_pipeline(test_client_download); test_run_parallel(test_client_download); test_end(); } static void test_download_client_nested_ioloop(void) { test_begin("http payload echo (client nested ioloop)"); blocking = FALSE; request_100_continue = FALSE; read_server_partial = 0; read_client_partial = 0; client_ioloop_nesting = 10; test_run_parallel(test_client_echo); test_end(); } static void (*test_functions[])(void) = { test_download_server_nonblocking, test_download_server_blocking, test_echo_server_nonblocking, test_echo_server_blocking, test_echo_server_nonblocking_sync, test_echo_server_blocking_sync, test_echo_server_nonblocking_partial, test_echo_server_blocking_partial, test_download_client_partial, test_download_client_nested_ioloop, NULL }; /* * Main */ volatile sig_atomic_t terminating = 0; static void test_signal_handler(int signo) { if (terminating) raise(signo); terminating = 1; /* make sure we don't leave any pesky children alive */ test_server_kill(); (void)signal(signo, SIG_DFL); raise(signo); } static void test_atexit(void) { test_server_kill(); } int main(int argc, char *argv[]) { int c; atexit(test_atexit); (void)signal(SIGCHLD, SIG_IGN); (void)signal(SIGTERM, test_signal_handler); (void)signal(SIGQUIT, test_signal_handler); (void)signal(SIGINT, test_signal_handler); (void)signal(SIGSEGV, test_signal_handler); (void)signal(SIGABRT, test_signal_handler); while ((c = getopt(argc, argv, "D")) > 0) { switch (c) { case 'D': debug = TRUE; break; default: i_fatal("Usage: %s [-D]", argv[0]); } } /* listen on localhost */ i_zero(&bind_ip); bind_ip.family = AF_INET; bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK); test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-url.h0000644000175000017500000000433513165463624014606 00000000000000#ifndef HTTP_URL_H #define HTTP_URL_H #include "net.h" struct http_request_target; struct http_url { /* server */ const char *host_name; struct ip_addr host_ip; in_port_t port; /* userinfo (not parsed by default) */ const char *user; const char *password; /* path */ const char *path; /* ?query (still encoded) */ const char *enc_query; /* #fragment (still encoded) */ const char *enc_fragment; unsigned int have_host_ip:1; /* URL uses IP address */ unsigned int have_port:1; unsigned int have_ssl:1; }; /* * HTTP URL parsing */ enum http_url_parse_flags { /* Scheme part 'http:' is already parsed externally. This implies that this is an absolute HTTP URL. */ HTTP_URL_PARSE_SCHEME_EXTERNAL = 0x01, /* Allow '#fragment' part in HTTP URL */ HTTP_URL_ALLOW_FRAGMENT_PART = 0x02, /* Allow 'user:password@' part in HTTP URL */ HTTP_URL_ALLOW_USERINFO_PART = 0x04, /* Allow URL to contain %00 */ HTTP_URL_ALLOW_PCT_NUL = 0x08, }; int http_url_parse(const char *url, struct http_url *base, enum http_url_parse_flags flags, pool_t pool, struct http_url **url_r, const char **error_r); int http_url_request_target_parse(const char *request_target, const char *host_header, pool_t pool, struct http_request_target *target, const char **error_r); /* * HTTP URL manipulation */ void http_url_copy_authority(pool_t pool, struct http_url *dest, const struct http_url *src); struct http_url *http_url_clone_authority(pool_t pool, const struct http_url *src); void http_url_copy(pool_t pool, struct http_url *dest, const struct http_url *src); void http_url_copy_with_userinfo(pool_t pool, struct http_url *dest, const struct http_url *src); struct http_url *http_url_clone(pool_t pool,const struct http_url *src); struct http_url *http_url_clone_with_userinfo(pool_t pool, const struct http_url *src); /* * HTTP URL construction */ const char *http_url_create(const struct http_url *url); const char *http_url_create_host(const struct http_url *url); const char *http_url_create_authority(const struct http_url *url); const char *http_url_create_target(const struct http_url *url); void http_url_escape_path(string_t *out, const char *data); void http_url_escape_param(string_t *out, const char *data); #endif dovecot-2.2.33.2/src/lib-http/http-transfer-chunked.c0000644000175000017500000004431013165463624017237 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "ostream-private.h" #include "http-parser.h" #include "http-header-parser.h" #include "http-transfer.h" #define MIN_CHUNK_SIZE_WITH_EXTRA 6 /* * Chunked input stream */ enum http_transfer_chunked_parse_state { HTTP_CHUNKED_PARSE_STATE_INIT, HTTP_CHUNKED_PARSE_STATE_SIZE, HTTP_CHUNKED_PARSE_STATE_EXT, HTTP_CHUNKED_PARSE_STATE_EXT_NAME, HTTP_CHUNKED_PARSE_STATE_EXT_EQ, HTTP_CHUNKED_PARSE_STATE_EXT_VALUE, HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING, HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE, HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN, HTTP_CHUNKED_PARSE_STATE_CR, HTTP_CHUNKED_PARSE_STATE_LF, HTTP_CHUNKED_PARSE_STATE_DATA, HTTP_CHUNKED_PARSE_STATE_DATA_READY, HTTP_CHUNKED_PARSE_STATE_DATA_CR, HTTP_CHUNKED_PARSE_STATE_DATA_LF, HTTP_CHUNKED_PARSE_STATE_TRAILER, HTTP_CHUNKED_PARSE_STATE_FINISHED, }; struct http_transfer_chunked_istream { struct istream_private istream; struct stat statbuf; const unsigned char *begin, *cur, *end; enum http_transfer_chunked_parse_state state; unsigned int parsed_chars; uoff_t chunk_size, chunk_v_offset, chunk_pos; uoff_t size, max_size; struct http_header_parser *header_parser; unsigned int finished:1; }; /* Chunk parser */ static inline const char *_chr_sanitize(unsigned char c) { if (c >= 0x20 && c < 0x7F) return t_strdup_printf("'%c'", c); return t_strdup_printf("0x%02x", c); } static int http_transfer_chunked_parse_size (struct http_transfer_chunked_istream *tcstream) { uoff_t size = 0, prev; /* chunk-size = 1*HEXDIG */ while (tcstream->cur < tcstream->end) { prev = tcstream->chunk_size; if (*tcstream->cur >= '0' && *tcstream->cur <= '9') size = *tcstream->cur-'0'; else if (*tcstream->cur >= 'A' && *tcstream->cur <= 'F') size = *tcstream->cur-'A' + 10; else if (*tcstream->cur >= 'a' && *tcstream->cur <= 'f') size = *tcstream->cur-'a' + 10; else { if (tcstream->parsed_chars == 0) { io_stream_set_error(&tcstream->istream.iostream, "Expected chunk size digit, but found %s", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->parsed_chars = 0; return 1; } tcstream->chunk_size <<= 4; tcstream->chunk_size += size; if (tcstream->chunk_size < prev) { io_stream_set_error(&tcstream->istream.iostream, "Chunk size exceeds integer limit"); return -1; } tcstream->parsed_chars++; tcstream->cur++; } return 0; } static int http_transfer_chunked_skip_token (struct http_transfer_chunked_istream *tcstream) { const unsigned char *first = tcstream->cur; /* token = 1*tchar */ while (tcstream->cur < tcstream->end && http_char_is_token(*tcstream->cur)) tcstream->cur++; tcstream->parsed_chars += (tcstream->cur-first); if (tcstream->cur == tcstream->end) return 0; if (tcstream->parsed_chars == 0) return -1; return 1; } static int http_transfer_chunked_skip_qdtext (struct http_transfer_chunked_istream *tcstream) { /* qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text */ while (tcstream->cur < tcstream->end && http_char_is_qdtext(*tcstream->cur)) tcstream->cur++; if (tcstream->cur == tcstream->end) return 0; return 1; } static int http_transfer_chunked_parse(struct http_transfer_chunked_istream *tcstream) { int ret; /* RFC 7230, Section 4.1: Chunked Transfer Encoding chunked-body = *chunk last-chunk trailer-part CRLF chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF chunk-size = 1*HEXDIG last-chunk = 1*("0") [ chunk-ext ] CRLF chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) chunk-ext-name = token chunk-ext-val = token / quoted-string chunk-data = 1*OCTET ; a sequence of chunk-size octets trailer-part = *( header-field CRLF ) */ for (;;) { switch (tcstream->state) { case HTTP_CHUNKED_PARSE_STATE_INIT: tcstream->chunk_size = 0; tcstream->chunk_pos = 0; tcstream->parsed_chars = 0; tcstream->state = HTTP_CHUNKED_PARSE_STATE_SIZE; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_SIZE: if ((ret=http_transfer_chunked_parse_size(tcstream)) <= 0) return ret; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT: if (*tcstream->cur != ';') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_CR; break; } /* chunk-ext */ tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_NAME; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_NAME: /* chunk-ext-name = token */ if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) { if (ret < 0) { io_stream_set_error(&tcstream->istream.iostream, "Invalid chunked extension name"); } return ret; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_EQ; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_EQ: if (*tcstream->cur != '=') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; break; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE: /* chunk-ext-val = token / quoted-string */ if (*tcstream->cur != '"') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN; break; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING: if (*tcstream->cur == '"') { tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; if (tcstream->cur >= tcstream->end) return 0; } else if ((ret=http_transfer_chunked_skip_qdtext(tcstream)) <= 0) { if (ret < 0) { io_stream_set_error(&tcstream->istream.iostream, "Invalid chunked extension value"); } return ret; } else if (*tcstream->cur == '\\') { tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE; if (tcstream->cur >= tcstream->end) return 0; } else { io_stream_set_error(&tcstream->istream.iostream, "Invalid character %s in chunked extension value string", _chr_sanitize(*tcstream->cur)); return -1; } break; case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE: /* ( HTAB / SP / VCHAR / obs-text ) */ if (!http_char_is_text(*tcstream->cur)) { io_stream_set_error(&tcstream->istream.iostream, "Escaped invalid character %s in chunked extension value string", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING; if (tcstream->cur >= tcstream->end) return 0; break; case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN: if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) { if (ret < 0) { io_stream_set_error(&tcstream->istream.iostream, "Invalid chunked extension value"); } return ret; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; break; case HTTP_CHUNKED_PARSE_STATE_CR: tcstream->state = HTTP_CHUNKED_PARSE_STATE_LF; if (*tcstream->cur == '\r') { tcstream->cur++; if (tcstream->cur >= tcstream->end) return 0; } /* fall through */ case HTTP_CHUNKED_PARSE_STATE_LF: if (*tcstream->cur != '\n') { io_stream_set_error(&tcstream->istream.iostream, "Expected new line after chunk size, but found %s", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->cur++; if (tcstream->chunk_size > 0) tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA; else tcstream->state = HTTP_CHUNKED_PARSE_STATE_TRAILER; return 1; case HTTP_CHUNKED_PARSE_STATE_DATA_READY: /* fall through */ case HTTP_CHUNKED_PARSE_STATE_DATA_CR: tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_LF; if (*tcstream->cur == '\r') { tcstream->cur++; if (tcstream->cur >= tcstream->end) return 0; } /* fall through */ case HTTP_CHUNKED_PARSE_STATE_DATA_LF: if (*tcstream->cur != '\n') { io_stream_set_error(&tcstream->istream.iostream, "Expected new line after chunk data, but found %s", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_INIT; break; default: i_unreached(); } } i_unreached(); return -1; } static int http_transfer_chunked_parse_next( struct http_transfer_chunked_istream *tcstream) { struct istream_private *stream = &tcstream->istream; struct istream *input = tcstream->istream.parent; size_t size; int ret; while ((ret=i_stream_read_data (input, &tcstream->begin, &size, 0)) > 0) { tcstream->cur = tcstream->begin; tcstream->end = tcstream->cur + size; if ((ret=http_transfer_chunked_parse(tcstream)) < 0) { stream->istream.stream_errno = EIO; return -1; } i_stream_skip(input, tcstream->cur - tcstream->begin); if (ret > 0) { if (tcstream->state == HTTP_CHUNKED_PARSE_STATE_DATA) { tcstream->chunk_v_offset = input->v_offset; tcstream->size += tcstream->chunk_size; if (tcstream->max_size > 0 && tcstream->size > tcstream->max_size) { io_stream_set_error(&tcstream->istream.iostream, "Total chunked payload size exceeds maximum"); stream->istream.stream_errno = EMSGSIZE; return -1; } } return ret; } } i_assert(ret != -2); if (ret < 0) { if ( stream->parent->eof && stream->parent->stream_errno == 0 ) { /* unexpected EOF */ io_stream_set_error(&tcstream->istream.iostream, "Unexpected end of payload"); stream->istream.stream_errno = EIO; } else { /* parent stream error */ stream->istream.stream_errno = stream->parent->stream_errno; } } return ret; } /* Input stream */ static ssize_t http_transfer_chunked_istream_read_data( struct http_transfer_chunked_istream *tcstream) { struct istream_private *stream = &tcstream->istream; const unsigned char *data; size_t size, avail; ssize_t ret = 0; if (tcstream->chunk_pos >= tcstream->chunk_size) { tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY; return 0; } // FIXME: is this even necessary? i_stream_seek(stream->parent, tcstream->chunk_v_offset + tcstream->chunk_pos); /* read from parent if necessary */ data = i_stream_get_data(stream->parent, &size); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if ( stream->parent->eof && stream->parent->stream_errno == 0 ) { /* unexpected EOF */ io_stream_set_error(&tcstream->istream.iostream, "Unexpected end of payload"); stream->istream.stream_errno = EIO; } else { /* parent stream error */ stream->istream.stream_errno = stream->parent->stream_errno; } return ret; } data = i_stream_get_data(stream->parent, &size); i_assert(size != 0); } size = size > (tcstream->chunk_size - tcstream->chunk_pos) ? (tcstream->chunk_size - tcstream->chunk_pos) : size; /* Allocate buffer space */ if (!i_stream_try_alloc(stream, size, &avail)) return -2; /* Copy payload */ size = size > avail ? avail : size; memcpy(&stream->w_buffer[stream->pos], data, size); i_stream_skip(stream->parent, size); tcstream->chunk_pos += size; if (tcstream->chunk_pos >= tcstream->chunk_size) tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY; if ( ret < 0 ) { stream->pos = stream->pos+size; return ret; } ret = size; stream->pos = stream->pos+size; return ret; } static int http_transfer_chunked_parse_trailer( struct http_transfer_chunked_istream *tcstream) { struct istream_private *stream = &tcstream->istream; const char *field_name, *error; const unsigned char *field_data; size_t field_size; int ret; if (tcstream->header_parser == NULL) { /* NOTE: trailer is currently ignored */ /* FIXME: limit trailer size */ tcstream->header_parser = http_header_parser_init(tcstream->istream.parent, NULL, 0); } while ((ret=http_header_parse_next_field(tcstream->header_parser, &field_name, &field_data, &field_size, &error)) > 0) { if (field_name == NULL) break; } if (ret <= 0) { if (ret < 0) { io_stream_set_error(&stream->iostream, "Failed to parse chunked trailer: %s", error); stream->istream.stream_errno = EIO; } return ret; } return 1; } static ssize_t http_transfer_chunked_istream_read(struct istream_private *stream) { struct http_transfer_chunked_istream *tcstream = (struct http_transfer_chunked_istream *)stream; ssize_t ret = 0; for (;;) { switch (tcstream->state) { case HTTP_CHUNKED_PARSE_STATE_FINISHED: tcstream->istream.istream.eof = TRUE; return -1; case HTTP_CHUNKED_PARSE_STATE_DATA: if ((ret=http_transfer_chunked_istream_read_data(tcstream)) != 0) return ret; if (tcstream->state != HTTP_CHUNKED_PARSE_STATE_DATA_READY) return 0; break; case HTTP_CHUNKED_PARSE_STATE_TRAILER: if ((ret=http_transfer_chunked_parse_trailer(tcstream)) <= 0) return ret; tcstream->state = HTTP_CHUNKED_PARSE_STATE_FINISHED; tcstream->istream.istream.eof = TRUE; return -1; default: if ((ret=http_transfer_chunked_parse_next(tcstream)) <= 0) return ret; } } return -1; } static void http_transfer_chunked_istream_destroy(struct iostream_private *stream) { struct http_transfer_chunked_istream *tcstream = (struct http_transfer_chunked_istream *)stream; if (tcstream->header_parser != NULL) http_header_parser_deinit(&tcstream->header_parser); // FIXME: copied from istream.c; there's got to be a better way. i_free(tcstream->istream.w_buffer); if (tcstream->istream.parent != NULL) i_stream_unref(&tcstream->istream.parent); } struct istream * http_transfer_chunked_istream_create(struct istream *input, uoff_t max_size) { struct http_transfer_chunked_istream *tcstream; tcstream = i_new(struct http_transfer_chunked_istream, 1); tcstream->max_size = max_size; tcstream->istream.max_buffer_size = input->real_stream->max_buffer_size; tcstream->istream.iostream.destroy = http_transfer_chunked_istream_destroy; tcstream->istream.read = http_transfer_chunked_istream_read; tcstream->istream.istream.readable_fd = FALSE; tcstream->istream.istream.blocking = input->blocking; tcstream->istream.istream.seekable = FALSE; return i_stream_create(&tcstream->istream, input, i_stream_get_fd(input)); } /* * Chunked output stream */ // FIXME: provide support for corking the stream. This means that we'll have // to buffer sent data here rather than in the parent steam; we need to know // the size of the chunks before we can send them. #define DEFAULT_MAX_BUFFER_SIZE (1024*32) struct http_transfer_chunked_ostream { struct ostream_private ostream; size_t chunk_size, chunk_pos; unsigned int chunk_active:1; }; static size_t _log16(size_t in) { size_t res = 0; while (in > 0) { in >>= 4; res++; } return res; } static size_t _max_chunk_size(size_t avail) { size_t chunk_extra = 2*2; /* Make sure we have room for both chunk data and overhead chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF chunk-size = 1*HEXDIG */ chunk_extra += _log16(avail); return avail < chunk_extra ? 0 : avail - chunk_extra; } static void http_transfer_chunked_ostream_close(struct iostream_private *stream, bool close_parent) { struct http_transfer_chunked_ostream *tcstream = (struct http_transfer_chunked_ostream *)stream; (void)o_stream_send(tcstream->ostream.parent, "0\r\n\r\n", 5); (void)o_stream_flush(&tcstream->ostream.ostream); if (close_parent) o_stream_close(tcstream->ostream.parent); } static ssize_t http_transfer_chunked_ostream_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct http_transfer_chunked_ostream *tcstream = (struct http_transfer_chunked_ostream *)stream; struct const_iovec *iov_new; unsigned int iov_count_new, i; size_t bytes = 0, max_bytes; ssize_t ret; const char *prefix; i_assert(stream->parent->real_stream->max_buffer_size >= MIN_CHUNK_SIZE_WITH_EXTRA); if ((ret=o_stream_flush(stream->parent)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ o_stream_copy_error_from_parent(stream); return ret; } /* check how many bytes we want to send */ bytes = 0; for (i = 0; i < iov_count; i++) { bytes += iov[i].iov_len; } /* check if we have room to send at least one byte */ max_bytes = o_stream_get_buffer_avail_size(stream->parent); max_bytes = _max_chunk_size(max_bytes); if (max_bytes < MIN_CHUNK_SIZE_WITH_EXTRA) return 0; tcstream->chunk_size = bytes > max_bytes ? max_bytes : bytes; /* determine what to send */ bytes = tcstream->chunk_size; iov_count_new = 1; for (i = 0; i < iov_count && bytes > 0; i++) { if (bytes <= iov[i].iov_len) break; bytes -= iov[i].iov_len; iov_count_new++; } /* create new iovec */ prefix = t_strdup_printf("%llx\r\n", (unsigned long long)tcstream->chunk_size); iov_count = iov_count_new + 2; iov_new = t_malloc(sizeof(struct const_iovec) * iov_count); iov_new[0].iov_base = prefix; iov_new[0].iov_len = strlen(prefix); memcpy(&iov_new[1], iov, sizeof(struct const_iovec) * iov_count_new); iov_new[iov_count-2].iov_len = bytes; iov_new[iov_count-1].iov_base = "\r\n"; iov_new[iov_count-1].iov_len = 2; /* send */ if ((ret=o_stream_sendv(stream->parent, iov_new, iov_count)) <= 0) { i_assert(ret < 0); o_stream_copy_error_from_parent(stream); return -1; } /* all must be sent */ i_assert((size_t)ret == (tcstream->chunk_size + iov_new[0].iov_len + iov_new[iov_count-1].iov_len)); stream->ostream.offset += tcstream->chunk_size; return tcstream->chunk_size; } struct ostream * http_transfer_chunked_ostream_create(struct ostream *output) { struct http_transfer_chunked_ostream *tcstream; size_t max_size; tcstream = i_new(struct http_transfer_chunked_ostream, 1); tcstream->ostream.sendv = http_transfer_chunked_ostream_sendv; tcstream->ostream.iostream.close = http_transfer_chunked_ostream_close; if (output->real_stream->max_buffer_size > 0) max_size = output->real_stream->max_buffer_size; else max_size = DEFAULT_MAX_BUFFER_SIZE; tcstream->ostream.max_buffer_size = _max_chunk_size(max_size); return o_stream_create(&tcstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib-http/http-server.h0000644000175000017500000002141613165463624015311 00000000000000#ifndef HTTP_SERVER_H #define HTTP_SERVER_H #include "http-auth.h" #include "http-request.h" struct istream; struct ostream; struct http_request; struct http_server; struct http_server_request; struct http_server_response; struct http_server_settings { const char *rawlog_dir; unsigned int max_client_idle_time_msecs; /* maximum number of pipelined requests per connection (default = 1) */ unsigned int max_pipelined_requests; /* request limits */ struct http_request_limits request_limits; /* the kernel send/receive buffer sizes used for the connection sockets. Configuring this is mainly useful for the test suite. The kernel defaults are used when these settings are 0. */ size_t socket_send_buffer_size; size_t socket_recv_buffer_size; bool debug; }; struct http_server_stats { unsigned int request_count, response_count; uoff_t input, output; }; struct http_server_tunnel { int fd_in, fd_out; struct istream *input; struct ostream *output; }; struct http_server_callbacks { /* Handle the server request. All requests must be sent back a response. The response is sent either with http_server_request_fail*() or http_server_response_submit*(). For simple requests you can send the response back immediately. If you can't do that, you'll need to reference the request. Then the code flow usually goes like this: - http_server_request_set_destroy_callback(destroy_callback) - http_server_request_ref() - - http_server_response_create() - http_server_response_set_payload() can be used especially with istream-callback to create a large response without temp files. - http_server_response_submit() triggers the destroy_callback after it has finished sending the response and its payload. - In destroy_callback: http_server_request_unref() and any other necessary cleanup - the request handling is now fully finished. */ void (*handle_request)(void *context, struct http_server_request *req); void (*handle_connect_request)(void *context, struct http_server_request *req, struct http_url *target); void (*connection_destroy)(void *context, const char *reason); }; typedef void (*http_server_tunnel_callback_t)(void *context, const struct http_server_tunnel *tunnel); struct http_server *http_server_init(const struct http_server_settings *set); void http_server_deinit(struct http_server **_server); /* shut down the server; accept no new requests and drop connections once they become idle */ void http_server_shut_down(struct http_server *server); struct http_server_connection * http_server_connection_create(struct http_server *server, int fd_in, int fd_out, bool ssl, const struct http_server_callbacks *callbacks, void *context); void http_server_connection_ref(struct http_server_connection *conn); /* Returns FALSE if unrefing destroyed the connection entirely */ bool http_server_connection_unref(struct http_server_connection **_conn); void http_server_connection_close(struct http_server_connection **_conn, const char *reason); const struct http_server_stats * http_server_connection_get_stats(struct http_server_connection *conn); const struct http_request * http_server_request_get(struct http_server_request *req); pool_t http_server_request_get_pool(struct http_server_request *req); /* Returns the response created for the request with http_server_response_create(), or NULL if none. */ struct http_server_response * http_server_request_get_response(struct http_server_request *req); /* Returns TRUE if request is finished either because a response was sent or because the request was aborted. */ bool http_server_request_is_finished(struct http_server_request *req); /* Return input stream for the request's payload. Optionally, this stream can be made blocking. Do *NOT* meddle with the FD of the http_request payload to achieve the same, because protocol violations will result. */ struct istream * http_server_request_get_payload_input(struct http_server_request *req, bool blocking); /* Get the authentication credentials provided in this request. Returns 0 if the Authorization header is absent, returns -1 when that header cannot be parsed, and returns 1 otherwise */ int http_server_request_get_auth(struct http_server_request *req, struct http_auth_credentials *credentials); /* Send a failure response to the request with given status/reason. */ void http_server_request_fail(struct http_server_request *req, unsigned int status, const char *reason); /* Send a failure response to the request with given status/reason and close the connection. */ void http_server_request_fail_close(struct http_server_request *req, unsigned int status, const char *reason); /* Send an authentication failure response to the request with given reason. The provided challenge is set in the WWW-Authenticate header of the response. */ void http_server_request_fail_auth(struct http_server_request *req, const char *reason, const struct http_auth_challenge *chlng) ATTR_NULL(2); /* Send a authentication failure response to the request with given reason. The provided realm is used to construct an Basic challenge in the WWW-Authenticate header of the response. */ void http_server_request_fail_auth_basic(struct http_server_request *req, const char *reason, const char *realm) ATTR_NULL(2); /* Call the specified callback when HTTP request is destroyed. This happens after one of the following: a) Response and its payload is fully sent b) Response was submitted, but it couldn't be sent due to disconnection. c) http_server_deinit() was called and the request was aborted Note client disconnection before response is submitted isn't visible to this. The request payload reading is the responsibility of the caller, which also must handle the read errors by submitting a failure response. */ void http_server_request_set_destroy_callback(struct http_server_request *req, void (*callback)(void *), void *context); /* Reference a server request */ void http_server_request_ref(struct http_server_request *req); /* Unreference a server request. Returns TRUE if there are still more references, FALSE if not. */ bool http_server_request_unref(struct http_server_request **_req); /* Start creating the response for the request. This function can be called only once for each request. */ struct http_server_response * http_server_response_create(struct http_server_request *req, unsigned int status, const char *reason); void http_server_response_add_header(struct http_server_response *resp, const char *key, const char *value); /* Change the response code and text, cannot be used after submission */ void http_server_response_update_status(struct http_server_response *resp, unsigned int status, const char *reason); void http_server_response_set_date(struct http_server_response *resp, time_t date); void http_server_response_set_payload(struct http_server_response *resp, struct istream *input); void http_server_response_set_payload_data(struct http_server_response *resp, const unsigned char *data, size_t size); struct ostream * http_server_response_get_payload_output(struct http_server_response *resp, bool blocking); /* get some information about response */ void http_server_response_get_status(struct http_server_response *resp, int *status_r, const char **reason_r); uoff_t http_server_response_get_total_size(struct http_server_response *resp); void http_server_response_add_auth( struct http_server_response *resp, const struct http_auth_challenge *chlng); void http_server_response_add_auth_basic( struct http_server_response *resp, const char *realm); void http_server_response_submit(struct http_server_response *resp); /* Submit response and close the connection. */ void http_server_response_submit_close(struct http_server_response *resp); void http_server_response_submit_tunnel(struct http_server_response *resp, http_server_tunnel_callback_t callback, void *context); void http_server_switch_ioloop(struct http_server *server); /* submits response and blocks until provided payload is sent. Multiple calls are allowed; payload transmission is finished with http_server_response_finish_payload(). If the sending fails, returns -1 and sets resp=NULL to indicate that the response was freed, otherwise returns 0 and resp is unchanged. */ int http_server_response_send_payload(struct http_server_response **resp, const unsigned char *data, size_t size); /* Finish sending the payload. Always frees resp and sets it to NULL. Returns 0 on success, -1 on error. */ int http_server_response_finish_payload(struct http_server_response **resp); /* abort response payload transmission prematurely. this closes the associated connection */ void http_server_response_abort_payload(struct http_server_response **resp); #endif dovecot-2.2.33.2/src/lib-http/http-date.c0000644000175000017500000002655513165463543014724 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "utc-mktime.h" #include "http-date.h" #include /* RFC 7231, Section 7.1.1.1: Date/Time Formats The defined syntax is as follows: HTTP-date = IMF-fixdate / obs-date Preferred format: IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT ; fixed length/zone/capitalization subset of the format ; see Section 3.3 of [RFC5322] day-name = %x4D.6F.6E ; "Mon", case-sensitive / %x54.75.65 ; "Tue", case-sensitive / %x57.65.64 ; "Wed", case-sensitive / %x54.68.75 ; "Thu", case-sensitive / %x46.72.69 ; "Fri", case-sensitive / %x53.61.74 ; "Sat", case-sensitive / %x53.75.6E ; "Sun", case-sensitive date1 = day SP month SP year ; e.g., 02 Jun 1982 day = 2DIGIT month = %x4A.61.6E ; "Jan", case-sensitive / %x46.65.62 ; "Feb", case-sensitive / %x4D.61.72 ; "Mar", case-sensitive / %x41.70.72 ; "Apr", case-sensitive / %x4D.61.79 ; "May", case-sensitive / %x4A.75.6E ; "Jun", case-sensitive / %x4A.75.6C ; "Jul", case-sensitive / %x41.75.67 ; "Aug", case-sensitive / %x53.65.70 ; "Sep", case-sensitive / %x4F.63.74 ; "Oct", case-sensitive / %x4E.6F.76 ; "Nov", case-sensitive / %x44.65.63 ; "Dec", case-sensitive year = 4DIGIT GMT = %x47.4D.54 ; "GMT", case-sensitive time-of-day = hour ":" minute ":" second ; 00:00:00 - 23:59:60 (leap second) hour = 2DIGIT minute = 2DIGIT second = 2DIGIT Obsolete formats: obs-date = rfc850-date / asctime-date rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT date2 = day "-" month "-" 2DIGIT ; e.g., 02-Jun-82 day-name-l = %x4D.6F.6E.64.61.79 ; "Monday", case-sensitive / %x54.75.65.73.64.61.79 ; "Tuesday", case-sensitive / %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive / %x54.68.75.72.73.64.61.79 ; "Thursday", case-sensitive / %x46.72.69.64.61.79 ; "Friday", case-sensitive / %x53.61.74.75.72.64.61.79 ; "Saturday", case-sensitive / %x53.75.6E.64.61.79 ; "Sunday", case-sensitive asctime-date = day-name SP date3 SP time-of-day SP year date3 = month SP ( 2DIGIT / ( SP 1DIGIT )) ; e.g., Jun 2 */ static const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char *weekday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *weekday_names_long[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; struct http_date_parser { const unsigned char *cur, *end; struct tm tm; int timezone_offset; }; static inline int http_date_parse_sp(struct http_date_parser *parser) { if (parser->cur >= parser->end) return -1; if (parser->cur[0] != ' ') return 0; parser->cur++; return 1; } static inline int http_date_parse_number(struct http_date_parser *parser, int digits, int *number_r) { int i; if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) return 0; *number_r = parser->cur[0] - '0'; parser->cur++; for (i=0; i < digits-1; i++) { if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) return -1; *number_r = ((*number_r) * 10) + parser->cur[0] - '0'; parser->cur++; } return 1; } static inline int http_date_parse_word(struct http_date_parser *parser, int maxchars, string_t **word_r) { string_t *word; int i; if (parser->cur >= parser->end || !i_isalpha(parser->cur[0])) return 0; word = t_str_new(maxchars); str_append_c(word, parser->cur[0]); parser->cur++; for (i=0; i < maxchars-1; i++) { if (parser->cur >= parser->end || !i_isalpha(parser->cur[0])) break; str_append_c(word, parser->cur[0]); parser->cur++; } if (parser->cur < parser->end && i_isalpha(parser->cur[0])) return -1; *word_r = word; return 1; } static inline int http_date_parse_year(struct http_date_parser *parser) { /* year = 4DIGIT */ if (http_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0) return -1; if (parser->tm.tm_year < 1900) return -1; parser->tm.tm_year -= 1900; return 1; } static inline int http_date_parse_month(struct http_date_parser *parser) { string_t *month; int i; if (http_date_parse_word(parser, 3, &month) <= 0 || str_len(month) != 3) return -1; for (i = 0; i < 12; i++) { if (strcmp(month_names[i], str_c(month)) == 0) { break; } } if (i >= 12) return -1; parser->tm.tm_mon = i; return 1; } static inline int http_date_parse_day(struct http_date_parser *parser) { /* day = 2DIGIT */ if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0) return -1; return 1; } static int http_date_parse_time_of_day(struct http_date_parser *parser) { /* time-of-day = hour ":" minute ":" second ; 00:00:00 - 23:59:59 hour = 2DIGIT minute = 2DIGIT second = 2DIGIT */ /* hour = 2DIGIT */ if (http_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0) return -1; /* ":" */ if (parser->cur >= parser->end || parser->cur[0] != ':') return -1; parser->cur++; /* minute = 2DIGIT */ if (http_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0) return -1; /* ":" */ if (parser->cur >= parser->end || parser->cur[0] != ':') return -1; parser->cur++; /* second = 2DIGIT */ if (http_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0) return -1; return 1; } static inline int http_date_parse_time_gmt(struct http_date_parser *parser) { string_t *gmt; /* Remaining: {...} SP time-of-day SP GMT */ /* SP time-of-day */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_time_of_day(parser) <= 0) return -1; /* SP GMT */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_word(parser, 3, &gmt) <= 0 || strcmp("GMT", str_c(gmt)) != 0) return -1; return 1; } static int http_date_parse_format_imf_fixdate(struct http_date_parser *parser) { /* IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT ; fixed length/zone/capitalization subset of the format ; see Section 3.3 of [RFC5322] date1 = day SP month SP year ; e.g., 02 Jun 1982 Remaining: {...} SP day SP month SP year SP time-of-day SP GMT */ /* SP day */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_day(parser) <= 0) return -1; /* SP month */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_month(parser) <= 0) return -1; /* SP year */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_year(parser) <= 0) return -1; /* SP time-of-day SP GMT */ return http_date_parse_time_gmt(parser); } static int http_date_parse_format_rfc850(struct http_date_parser *parser) { /* rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT date2 = day "-" month "-" 2DIGIT ; day-month-year (e.g., 02-Jun-82) Remaining: "," SP day "-" month "-" 2DIGIT SP time-of-day SP GMT */ /* "," SP */ if (parser->cur >= parser->end || parser->cur[0] != ',') return -1; parser->cur++; if (http_date_parse_sp(parser) <= 0) return -1; /* day */ if (http_date_parse_day(parser) <= 0) return -1; /* "-" */ if (parser->cur >= parser->end || parser->cur[0] != '-') return -1; parser->cur++; /* month */ if (http_date_parse_month(parser) <= 0) return -1; /* "-" */ if (parser->cur >= parser->end || parser->cur[0] != '-') return -1; parser->cur++; /* 2DIGIT */ if (http_date_parse_number(parser, 2, &parser->tm.tm_year) <= 0) return -1; if (parser->tm.tm_year < 70) parser->tm.tm_year += 100; /* SP time-of-day SP GMT */ return http_date_parse_time_gmt(parser); } static int http_date_parse_format_asctime(struct http_date_parser *parser) { int ret; /* asctime-date = day-name SP date3 SP time-of-day SP year date3 = month SP ( 2DIGIT / ( SP 1DIGIT )) ; month day (e.g., Jun 2) Remaining: {...} month SP ( 2DIGIT / ( SP 1DIGIT )) SP time-of-day SP year */ /* month */ if (http_date_parse_month(parser) <= 0) return -1; /* SP */ if (http_date_parse_sp(parser) <= 0) return -1; /* SP 1DIGIT / 2DIGIT */ if ((ret=http_date_parse_sp(parser)) < 0) return -1; if (ret == 0) { if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0) return -1; } else { if (http_date_parse_number(parser, 1, &parser->tm.tm_mday) <= 0) return -1; } /* SP time-of-day */ if (http_date_parse_sp(parser) <= 0) return -1; if (http_date_parse_time_of_day(parser) <= 0) return -1; /* SP year */ if (http_date_parse_sp(parser) <= 0) return -1; return http_date_parse_year(parser); } static int http_date_parse_format_any(struct http_date_parser *parser) { string_t *dayname; int i; /* HTTP-date = IMF-fixdate / obs-date IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT ; fixed length/zone/capitalization subset of the format ; see Section 3.3 of [RFC5322] obs-date = rfc850-date / asctime-date rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT asctime-date = day-name SP date3 SP time-of-day SP year */ if (http_date_parse_word(parser, 9, &dayname) <= 0) return -1; if (str_len(dayname) > 3) { /* rfc850-date */ for (i = 0; i < 7; i++) { if (strcmp(weekday_names_long[i], str_c(dayname)) == 0) { break; } } if (i >= 7) return -1; return http_date_parse_format_rfc850(parser); } /* IMF-fixdate / asctime-date */ for (i = 0; i < 7; i++) { if (strcmp(weekday_names[i], str_c(dayname)) == 0) { break; } } if (i >= 7 || parser->cur >= parser->end) return -1; if (parser->cur[0] == ' ') { /* asctime-date */ parser->cur++; return http_date_parse_format_asctime(parser); } if (parser->cur[0] != ',') return -1; /* IMF-fixdate */ parser->cur++; return http_date_parse_format_imf_fixdate(parser); } bool http_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r) { struct http_date_parser parser; time_t timestamp; i_zero(&parser); parser.cur = data; parser.end = data + size; if (http_date_parse_format_any(&parser) <= 0) return FALSE; if (parser.cur != parser.end) return FALSE; parser.tm.tm_isdst = -1; timestamp = utc_mktime(&parser.tm); if (timestamp == (time_t)-1) return FALSE; *timestamp_r = timestamp; return TRUE; } bool http_date_parse_tm(const unsigned char *data, size_t size, struct tm *tm_r) { time_t timestamp; struct tm *tm; if (!http_date_parse(data, size, ×tamp)) return FALSE; tm = gmtime(×tamp); *tm_r = *tm; return TRUE; } const char *http_date_create_tm(struct tm *tm) { return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d GMT", weekday_names[tm->tm_wday], tm->tm_mday, month_names[tm->tm_mon], tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec); } const char *http_date_create(time_t timestamp) { struct tm *tm; tm = gmtime(×tamp); return http_date_create_tm(tm); } dovecot-2.2.33.2/src/lib-http/http-transfer.h0000644000175000017500000000116413123174404015612 00000000000000#ifndef HTTP_TRANSFER_H #define HTTP_TRANSFER_H struct http_transfer_param { const char *attribute; const char *value; }; ARRAY_DEFINE_TYPE(http_transfer_param, struct http_transfer_param); struct http_transfer_coding { const char *name; ARRAY_TYPE(http_transfer_param) parameters; }; ARRAY_DEFINE_TYPE(http_transfer_coding, struct http_transfer_coding); // FIXME: we currently lack a means to get error strings from the input stream struct istream * http_transfer_chunked_istream_create(struct istream *input, uoff_t max_size); struct ostream * http_transfer_chunked_ostream_create(struct ostream *output); #endif dovecot-2.2.33.2/src/lib-http/test-http-url.c0000644000175000017500000004057213165463624015561 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "http-url.h" #include "test-common.h" struct valid_http_url_test { const char *url; enum http_url_parse_flags flags; struct http_url url_base; struct http_url url_parsed; }; /* Valid HTTP URL tests */ static struct valid_http_url_test valid_url_tests[] = { /* Generic tests */ { .url = "http://localhost", .url_parsed = { .host_name = "localhost" } },{ .url = "http://www.%65%78%61%6d%70%6c%65.com", .url_parsed = { .host_name = "www.example.com" } },{ .url = "http://www.dovecot.org:8080", .url_parsed = { .host_name = "www.dovecot.org", .port = 8080, .have_port = TRUE } },{ .url = "http://127.0.0.1", .url_parsed = { .host_name = "127.0.0.1", .have_host_ip = TRUE } #ifdef HAVE_IPV6 },{ .url = "http://[::1]", .url_parsed = { .host_name = "[::1]", .have_host_ip = TRUE } },{ .url = "http://[::1]:8080", .url_parsed = { .host_name = "[::1]", .have_host_ip = TRUE, .port = 8080, .have_port = TRUE } #endif },{ .url = "http://user@api.dovecot.org", .flags = HTTP_URL_ALLOW_USERINFO_PART, .url_parsed = { .host_name = "api.dovecot.org", .user = "user" } },{ .url = "http://userid:secret@api.dovecot.org", .flags = HTTP_URL_ALLOW_USERINFO_PART, .url_parsed = { .host_name = "api.dovecot.org", .user = "userid", .password = "secret" } },{ .url = "http://su%3auserid:secret@api.dovecot.org", .flags = HTTP_URL_ALLOW_USERINFO_PART, .url_parsed = { .host_name = "api.dovecot.org", .user = "su:userid", .password = "secret" } },{ .url = "http://www.example.com/" "?question=What%20are%20you%20doing%3f&answer=Nothing.", .url_parsed = { .path = "/", .host_name = "www.example.com", .enc_query = "question=What%20are%20you%20doing%3f&answer=Nothing." } },{ /* These next 2 URLs don't follow the recommendations in http://tools.ietf.org/html/rfc1034#section-3.5 and http://tools.ietf.org/html/rfc3696 However they satisfy the grammar in http://tools.ietf.org/html/rfc1123#section-2 and http://tools.ietf.org/html/rfc952 so we should parse them. */ .url = "http://256.0.0.1/that/reverts/to/DNS", .url_parsed = { .path = "/that/reverts/to/DNS", .host_name = "256.0.0.1" } },{ .url = "http://127.0.0.284/this/also/reverts/to/DNS", .url_parsed = { .path = "/this/also/reverts/to/DNS", .host_name = "127.0.0.284" } },{ .url = "http://www.example.com/#Status%20of%20development", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_parsed = { .path = "/", .host_name = "www.example.com", .enc_fragment = "Status%20of%20development" } /* RFC 3986, Section 5.4. Reference Resolution Examples * * Within a representation with a well defined base URI of * * http://a/b/c/d;p?q * * a relative reference is transformed to its target URI as follows. * * 5.4.1. Normal Examples */ },{ // "g" = "http://a/b/c/g" .url = "g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g" } },{ // "./g" = "http://a/b/c/g" .url = "./g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g" } },{ // "g/" = "http://a/b/c/g/" .url = "g/", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g/" } },{ // "/g" = "http://a/g" .url = "/g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } },{ // "//g" = "http://g" .url = "//g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "g" } },{ // "?y" = "http://a/b/c/d;p?y" .url = "?y", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "y" } },{ // "g?y" = "http://a/b/c/g?y" .url = "g?y", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_query = "y" } },{ // "#s" = "http://a/b/c/d;p?q#s" .url = "#s", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q", .enc_fragment = "s" } },{ // "g#s" = "http://a/b/c/g#s" .url = "g#s", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_fragment = "s" } },{ // "g?y#s" = "http://a/b/c/g?y#s" .url = "g?y#s", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_query = "y", .enc_fragment = "s" } },{ // ";x" = "http://a/b/c/;x" .url = ";x", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/;x" } },{ // "g;x" = "http://a/b/c/g;x" .url = "g;x", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g;x" } },{ // "g;x?y#s" = "http://a/b/c/g;x?y#s" .url = "g;x?y#s", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g;x", .enc_query = "y", .enc_fragment = "s" } },{ // "" = "http://a/b/c/d;p?q" .url = "", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" } },{ // "." = "http://a/b/c/" .url = ".", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/" } },{ // "./" = "http://a/b/c/" .url = "./", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/" } },{ // ".." = "http://a/b/" .url = "..", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/" } },{ // "../" = "http://a/b/" .url = "../", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/" } },{ // "../g" = "http://a/b/g" .url = "../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/g" } },{ // "../.." = "http://a/" .url = "../..", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/" } },{ // "../../" = "http://a/" .url = "../../", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/" } },{ // "../../g" = "http://a/g" .url = "../../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } /* 5.4.2. Abnormal Examples */ },{ // "../../../g" = "http://a/g" .url = "../../../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } },{ // "../../../../g" = "http://a/g" .url = "../../../../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } },{ // "/./g" = "http://a/g" .url = "/./g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } },{ // "/../g" = "http://a/g" .url = "/../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/g" } },{ // "g." = "http://a/b/c/g." .url = "g.", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g." } },{ // ".g" = "http://a/b/c/.g" .url = ".g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/.g" } },{ // "g.." = "http://a/b/c/g.." .url = "g..", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g.." } },{ // "..g" = "http://a/b/c/..g" .url = "..g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/..g" } },{ // "./../g" = "http://a/b/g" .url = "./../g", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/g" } },{ // "./g/." = "http://a/b/c/g/" .url = "./g/.", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g/" } },{ // "g/./h" = "http://a/b/c/g/h" .url = "g/./h", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g/h" } },{ // "g/../h" = "http://a/b/c/h" .url = "g/../h", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/h" } },{ // "g;x=1/./y" = "http://a/b/c/g;x=1/y" .url = "g;x=1/./y", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g;x=1/y" } },{ // "g;x=1/../y" = "http://a/b/c/y" .url = "g;x=1/../y", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/y" } },{ // "g?y/./x" = "http://a/b/c/g?y/./x" .url = "g?y/./x", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_query = "y/./x" } },{ // "g?y/../x" = "http://a/b/c/g?y/../x" .url = "g?y/../x", .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_query = "y/../x" } },{ // "g#s/./x" = "http://a/b/c/g#s/./x" .url = "g#s/./x", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_fragment = "s/./x" } },{ // "g#s/../x" = "http://a/b/c/g#s/../x" .url = "g#s/../x", .flags = HTTP_URL_ALLOW_FRAGMENT_PART, .url_base = { .host_name = "a", .path = "/b/c/d;p", .enc_query = "q" }, .url_parsed = { .host_name = "a", .path = "/b/c/g", .enc_fragment = "s/../x" } } }; static unsigned int valid_url_test_count = N_ELEMENTS(valid_url_tests); static void test_http_url_valid(void) { unsigned int i; for (i = 0; i < valid_url_test_count; i++) T_BEGIN { const char *url = valid_url_tests[i].url; enum http_url_parse_flags flags = valid_url_tests[i].flags; struct http_url *urlt = &valid_url_tests[i].url_parsed; struct http_url *urlb = &valid_url_tests[i].url_base; struct http_url *urlp; const char *error = NULL; test_begin(t_strdup_printf("http url valid [%d]", i)); if (urlb->host_name == NULL) urlb = NULL; if (http_url_parse(url, urlb, flags, pool_datastack_create(), &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("http_url_parse(%s)", valid_url_tests[i].url), urlp != NULL, error); if (urlp != NULL) { if (urlp->host_name == NULL || urlt->host_name == NULL) { test_assert(urlp->host_name == urlt->host_name); } else { test_assert(strcmp(urlp->host_name, urlt->host_name) == 0); } if (!urlp->have_port) { test_assert(urlp->have_port == urlt->have_port); } else { test_assert(urlp->have_port == urlt->have_port && urlp->port == urlt->port); } if (!urlp->have_host_ip) { test_assert(urlp->have_host_ip == urlt->have_host_ip); } else { test_assert(urlp->have_host_ip == urlt->have_host_ip); } if (urlp->user == NULL || urlt->user == NULL) { test_assert(urlp->user == urlt->user); } else { test_assert(strcmp(urlp->user, urlt->user) == 0); } if (urlp->password == NULL || urlt->password == NULL) { test_assert(urlp->password == urlt->password); } else { test_assert(strcmp(urlp->password, urlt->password) == 0); } if (urlp->path == NULL || urlt->path == NULL) { test_assert(urlp->path == urlt->path); } else { test_assert(strcmp(urlp->path, urlt->path) == 0); } if (urlp->enc_query == NULL || urlt->enc_query == NULL) { test_assert(urlp->enc_query == urlt->enc_query); } else { test_assert(strcmp(urlp->enc_query, urlt->enc_query) == 0); } if (urlp->enc_fragment == NULL || urlt->enc_fragment == NULL) { test_assert(urlp->enc_fragment == urlt->enc_fragment); } else { test_assert(strcmp(urlp->enc_fragment, urlt->enc_fragment) == 0); } } test_end(); } T_END; } struct invalid_http_url_test { const char *url; enum http_url_parse_flags flags; struct http_url url_base; }; static struct invalid_http_url_test invalid_url_tests[] = { { .url = "imap://example.com/INBOX" },{ .url = "http:/www.example.com" },{ .url = "" },{ .url = "/index.html" },{ .url = "http://www.example.com/index.html\"" },{ .url = "http:///dovecot.org" },{ .url = "http://[]/index.html" },{ .url = "http://[v08.234:232:234:234:2221]/index.html" #ifdef HAVE_IPV6 },{ .url = "http://[1::34a:34:234::6]/index.html" #endif },{ .url = "http://example%a.com/index.html" },{ .url = "http://example.com%/index.html" },{ .url = "http://example%00.com/index.html" },{ .url = "http://example.com:65536/index.html" },{ .url = "http://example.com:72817/index.html" },{ .url = "http://example.com/settings/%00/" },{ .url = "http://example.com/settings/%0r/" },{ .url = "http://example.com/settings/misc/%/" },{ .url = "http://example.com/?%00" },{ .url = "http://www.example.com/network.html#IMAP_Server" },{ .url = "http://example.com/#%00", .flags = HTTP_URL_ALLOW_FRAGMENT_PART } }; static unsigned int invalid_url_test_count = N_ELEMENTS(invalid_url_tests); static void test_http_url_invalid(void) { unsigned int i; for (i = 0; i < invalid_url_test_count; i++) T_BEGIN { const char *url = invalid_url_tests[i].url; enum http_url_parse_flags flags = invalid_url_tests[i].flags; struct http_url *urlb = &invalid_url_tests[i].url_base; struct http_url *urlp; const char *error = NULL; if (urlb->host_name == NULL) urlb = NULL; test_begin(t_strdup_printf("http url invalid [%d]", i)); if (http_url_parse(url, urlb, flags, pool_datastack_create(), &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("parse %s", url), urlp == NULL, error); test_end(); } T_END; } static const char *parse_create_url_tests[] = { "http://www.example.com/", "http://10.0.0.1/", #ifdef HAVE_IPV6 "http://[::1]/", #endif "http://www.example.com:993/", "http://www.example.com/index.html", "http://www.example.com/settings/index.html", "http://ww.%23example.com/", "http://www.example.com/%23shared/news", "http://www.example.com/query.php?name=Hendrik%20Visser", "http://www.example.com/network.html#IMAP%20Server", }; static unsigned int parse_create_url_test_count = N_ELEMENTS(parse_create_url_tests); static void test_http_url_parse_create(void) { unsigned int i; for (i = 0; i < parse_create_url_test_count; i++) T_BEGIN { const char *url = parse_create_url_tests[i]; struct http_url *urlp; const char *error = NULL; test_begin(t_strdup_printf("http url parse/create [%d]", i)); if (http_url_parse (url, NULL, HTTP_URL_ALLOW_FRAGMENT_PART, pool_datastack_create(), &urlp, &error) < 0) urlp = NULL; test_out_reason(t_strdup_printf("parse %s", url), urlp != NULL, error); if (urlp != NULL) { const char *urlnew = http_url_create(urlp); test_out(t_strdup_printf ("create %s", urlnew), strcmp(url, urlnew) == 0); } test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_http_url_valid, test_http_url_invalid, test_http_url_parse_create, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-client.c0000644000175000017500000002552313165463624015257 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "connection.h" #include "dns-lookup.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "http-url.h" #include "http-client-private.h" #define HTTP_DEFAULT_PORT 80 #define HTTPS_DEFAULT_PORT 443 /* Structure: http-client: Acts much like a browser; it is not dedicated to a single host. Client can accept requests to different hosts, which can be served at different IPs. Redirects are handled in the background by making a new connection. Connections to new hosts are created once needed for servicing a request. http-client-request: The request semantics are similar to imapc commands. Create a request, optionally modify some aspects of it and finally submit it. Once finished, a callback is called with the returned response. http-client-host: We maintain a 'cache' of hosts for which we have looked up IPs. One host can have multiple IPs. http-client-queue: Requests are queued in a queue object. These queues are maintained for each host:port target and listed in the host object. The queue object is responsible for starting connection attempts to TCP port at the various IPs known for the host. http-client-peer: The peer object groups multiple connections to the same ip/port (== peer_addr). http-client-connection: This is an actual connection to a server. Once a connection is ready to handle requests, it claims a request from a queue object. One connection can service multiple hosts and one host can have multiple associated connections, possibly to different ips and ports. */ /* * Logging */ static inline void http_client_debug(struct http_client *client, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_debug(struct http_client *client, const char *format, ...) { va_list args; va_start(args, format); if (client->set.debug) i_debug("http-client: %s", t_strdup_vprintf(format, args)); va_end(args); } /* * Client */ struct http_client *http_client_init(const struct http_client_settings *set) { struct http_client *client; pool_t pool; pool = pool_alloconly_create("http client", 1024); client = p_new(pool, struct http_client, 1); client->pool = pool; client->set.dns_client = set->dns_client; client->set.dns_client_socket_path = p_strdup_empty(pool, set->dns_client_socket_path); client->set.dns_ttl_msecs = (set->dns_ttl_msecs == 0 ? HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS : set->dns_ttl_msecs); client->set.user_agent = p_strdup_empty(pool, set->user_agent); client->set.rawlog_dir = p_strdup_empty(pool, set->rawlog_dir); client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir); client->set.ssl_ca_file = p_strdup(pool, set->ssl_ca_file); client->set.ssl_ca = p_strdup(pool, set->ssl_ca); client->set.ssl_crypto_device = p_strdup(pool, set->ssl_crypto_device); client->set.ssl_allow_invalid_cert = set->ssl_allow_invalid_cert; client->set.ssl_cert = p_strdup(pool, set->ssl_cert); client->set.ssl_key = p_strdup(pool, set->ssl_key); client->set.ssl_key_password = p_strdup(pool, set->ssl_key_password); if (set->proxy_socket_path != NULL && *set->proxy_socket_path != '\0') { client->set.proxy_socket_path = p_strdup(pool, set->proxy_socket_path); } else if (set->proxy_url != NULL) { client->set.proxy_url = http_url_clone(pool, set->proxy_url); } if (set->proxy_username != NULL && *set->proxy_username != '\0') { client->set.proxy_username = p_strdup_empty(pool, set->proxy_username); client->set.proxy_password = p_strdup(pool, set->proxy_password); } else if (set->proxy_url != NULL) { client->set.proxy_username = p_strdup_empty(pool, set->proxy_url->user); client->set.proxy_password = p_strdup(pool, set->proxy_url->password); } client->set.max_idle_time_msecs = set->max_idle_time_msecs; client->set.max_parallel_connections = (set->max_parallel_connections > 0 ? set->max_parallel_connections : 1); client->set.max_pipelined_requests = (set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1); client->set.max_attempts = set->max_attempts; client->set.max_connect_attempts = set->max_connect_attempts; client->set.connect_backoff_time_msecs = set->connect_backoff_time_msecs == 0 ? HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS : set->connect_backoff_time_msecs; client->set.connect_backoff_max_time_msecs = set->connect_backoff_max_time_msecs == 0 ? HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS : set->connect_backoff_max_time_msecs; client->set.no_auto_redirect = set->no_auto_redirect; client->set.no_auto_retry = set->no_auto_retry; client->set.no_ssl_tunnel = set->no_ssl_tunnel; client->set.max_redirects = set->max_redirects; client->set.response_hdr_limits = set->response_hdr_limits; client->set.request_absolute_timeout_msecs = set->request_absolute_timeout_msecs; client->set.request_timeout_msecs = set->request_timeout_msecs == 0 ? HTTP_CLIENT_DEFAULT_REQUEST_TIMEOUT_MSECS : set->request_timeout_msecs; client->set.connect_timeout_msecs = set->connect_timeout_msecs; client->set.soft_connect_timeout_msecs = set->soft_connect_timeout_msecs; client->set.max_auto_retry_delay = set->max_auto_retry_delay; client->set.socket_send_buffer_size = set->socket_send_buffer_size; client->set.socket_recv_buffer_size = set->socket_recv_buffer_size; client->set.debug = set->debug; i_array_init(&client->delayed_failing_requests, 1); client->conn_list = http_client_connection_list_init(); hash_table_create(&client->hosts, default_pool, 0, str_hash, strcmp); hash_table_create(&client->peers, default_pool, 0, http_client_peer_addr_hash, http_client_peer_addr_cmp); return client; } void http_client_deinit(struct http_client **_client) { struct http_client *client = *_client; struct http_client_request *req; struct http_client_host *host; struct http_client_peer *peer; *_client = NULL; /* destroy requests without calling callbacks */ req = client->requests_list; while (req != NULL) { struct http_client_request *next_req = req->next; http_client_request_destroy(&req); req = next_req; } i_assert(client->requests_count == 0); /* free peers */ while (client->peers_list != NULL) { peer = client->peers_list; http_client_peer_close(&peer); } hash_table_destroy(&client->peers); /* free hosts */ while (client->hosts_list != NULL) { host = client->hosts_list; http_client_host_free(&host); } hash_table_destroy(&client->hosts); array_free(&client->delayed_failing_requests); if (client->to_failing_requests != NULL) timeout_remove(&client->to_failing_requests); connection_list_deinit(&client->conn_list); if (client->ssl_ctx != NULL) ssl_iostream_context_deinit(&client->ssl_ctx); pool_unref(&client->pool); } void http_client_switch_ioloop(struct http_client *client) { struct connection *_conn = client->conn_list->connections; struct http_client_host *host; struct http_client_peer *peer; /* move connections */ /* FIXME: we wouldn't necessarily need to switch all of them immediately, only those that have requests now. but also connections that get new requests before ioloop is switched again.. */ for (; _conn != NULL; _conn = _conn->next) { struct http_client_connection *conn = (struct http_client_connection *)_conn; http_client_connection_switch_ioloop(conn); } /* move peers */ for (peer = client->peers_list; peer != NULL; peer = peer->next) http_client_peer_switch_ioloop(peer); /* move dns lookups and delayed requests */ for (host = client->hosts_list; host != NULL; host = host->next) http_client_host_switch_ioloop(host); /* move timeouts */ if (client->to_failing_requests != NULL) { client->to_failing_requests = io_loop_move_timeout(&client->to_failing_requests); } } void http_client_wait(struct http_client *client) { struct ioloop *prev_ioloop = current_ioloop; i_assert(client->ioloop == NULL); if (client->requests_count == 0) return; client->ioloop = io_loop_create(); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); /* either we're waiting for network I/O or we're getting out of a callback using timeout_add_short(0) */ i_assert(io_loop_have_ios(client->ioloop) || io_loop_have_immediate_timeouts(client->ioloop)); do { http_client_debug(client, "Waiting for %d requests to finish", client->requests_count); io_loop_run(client->ioloop); } while (client->requests_count > 0); http_client_debug(client, "All requests finished"); io_loop_set_current(prev_ioloop); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); io_loop_set_current(client->ioloop); io_loop_destroy(&client->ioloop); } unsigned int http_client_get_pending_request_count(struct http_client *client) { return client->requests_count; } int http_client_init_ssl_ctx(struct http_client *client, const char **error_r) { struct ssl_iostream_settings ssl_set; const char *error; if (client->ssl_ctx != NULL) return 0; i_zero(&ssl_set); ssl_set.ca_dir = client->set.ssl_ca_dir; ssl_set.ca_file = client->set.ssl_ca_file; ssl_set.ca = client->set.ssl_ca; ssl_set.verify_remote_cert = TRUE; ssl_set.crypto_device = client->set.ssl_crypto_device; ssl_set.cert = client->set.ssl_cert; ssl_set.key = client->set.ssl_key; ssl_set.key_password = client->set.ssl_key_password; ssl_set.verbose = client->set.debug; ssl_set.verbose_invalid_cert = client->set.debug; if (ssl_iostream_context_init_client(&ssl_set, &client->ssl_ctx, &error) < 0) { *error_r = t_strdup_printf("Couldn't initialize SSL context: %s", error); return -1; } return 0; } /* * Delayed request errors */ static void http_client_handle_request_errors(struct http_client *client) { struct http_client_request *const *req_idx; timeout_remove(&client->to_failing_requests); array_foreach(&client->delayed_failing_requests, req_idx) { struct http_client_request *req = *req_idx; i_assert(req->refcount == 1); http_client_request_error_delayed(&req); } array_clear(&client->delayed_failing_requests); } void http_client_delay_request_error(struct http_client *client, struct http_client_request *req) { if (client->to_failing_requests == NULL) { client->to_failing_requests = timeout_add_short(0, http_client_handle_request_errors, client); } array_append(&client->delayed_failing_requests, &req, 1); } void http_client_remove_request_error(struct http_client *client, struct http_client_request *req) { struct http_client_request *const *reqs; unsigned int i, count; reqs = array_get(&client->delayed_failing_requests, &count); for (i = 0; i < count; i++) { if (reqs[i] == req) { array_delete(&client->delayed_failing_requests, i, 1); return; } } } dovecot-2.2.33.2/src/lib-http/test-http-server-errors.c0000644000175000017500000004000513165463624017566 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "connection.h" #include "test-common.h" #include "http-url.h" #include "http-request.h" #include "http-server.h" #include #include #include #include #define SERVER_MAX_TIMEOUT_MSECS 10*1000 /* * Types */ struct client_connection { struct connection conn; pool_t pool; }; typedef void (*test_server_init_t) (const struct http_server_settings *server_set); typedef void (*test_client_init_t)(unsigned int index); /* * State */ /* common */ static struct ip_addr bind_ip; static in_port_t bind_port = 0; static struct ioloop *ioloop; static bool debug = FALSE; /* server */ static struct http_server *http_server = NULL; static struct io *io_listen; static int fd_listen = -1; static void (*test_server_request)(struct http_server_request *req); /* client */ static pid_t *client_pids = NULL; static struct connection_list *client_conn_list; static unsigned int client_pids_count = 0; static unsigned int client_index; static void (*test_client_connected)(struct client_connection *conn); static void (*test_client_input)(struct client_connection *conn); /* * Forward declarations */ /* server */ static void test_server_defaults(struct http_server_settings *http_set); static void test_server_run(const struct http_server_settings *http_set); /* client */ static void test_client_run(unsigned int index); /* test*/ static void test_run_client_server( const struct http_server_settings *server_set, test_server_init_t server_test, test_client_init_t client_test, unsigned int client_tests_count) ATTR_NULL(3); /* * Slow request */ /* client */ static void test_slow_request_input(struct client_connection *conn ATTR_UNUSED) { /* do nothing */ } static void test_slow_request_connected(struct client_connection *conn) { (void)o_stream_send_str(conn->conn.output, "GET / HTTP/1.1\r\n" "Host: example.com\r\n" "\r\n"); } static void test_client_slow_request(unsigned int index) { test_client_input = test_slow_request_input; test_client_connected = test_slow_request_connected; test_client_run(index); } /* server */ struct _slow_request { struct http_server_request *req; struct timeout *to_delay; unsigned int serviced:1; }; static void test_server_slow_request_destroyed(void *context) { struct _slow_request *ctx = (struct _slow_request *)context; test_assert(ctx->serviced); if (ctx->to_delay != NULL) timeout_remove(&ctx->to_delay); i_free(ctx); io_loop_stop(ioloop); } static void test_server_slow_request_delayed(struct _slow_request *ctx) { struct http_server_response *resp; struct http_server_request *req = ctx->req; resp = http_server_response_create(req, 200, "OK"); http_server_response_submit(resp); ctx->serviced = TRUE; http_server_request_unref(&req); } static void test_server_slow_request_request( struct http_server_request *req) { const struct http_request *hreq = http_server_request_get(req); struct _slow_request *ctx; if (debug) { i_debug("REQUEST: %s %s HTTP/%u.%u", hreq->method, hreq->target_raw, hreq->version_major, hreq->version_minor); } ctx = i_new(struct _slow_request, 1); ctx->req = req; http_server_request_set_destroy_callback(req, test_server_slow_request_destroyed, ctx); http_server_request_ref(req); ctx->to_delay = timeout_add (4000, test_server_slow_request_delayed, ctx); } static void test_server_slow_request (const struct http_server_settings *server_set) { test_server_request = test_server_slow_request_request; test_server_run(server_set); } /* test */ static void test_slow_request(void) { struct http_server_settings http_server_set; test_server_defaults(&http_server_set); http_server_set.max_client_idle_time_msecs = 1000; test_begin("slow request"); test_run_client_server(&http_server_set, test_server_slow_request, test_client_slow_request, 1); test_end(); } /* * Hanging request payload */ /* client */ static void test_hanging_request_payload_connected(struct client_connection *conn) { (void)o_stream_send_str(conn->conn.output, "GET / HTTP/1.1\r\n" "Host: example.com\r\n" "Content-Length: 1000\r\n" "\r\n" "To be continued... or not"); } static void test_client_hanging_request_payload(unsigned int index) { test_client_connected = test_hanging_request_payload_connected; test_client_run(index); } /* server */ struct _hanging_request_payload { struct http_server_request *req; struct istream *payload_input; struct io *io; unsigned int serviced:1; }; static void test_server_hanging_request_payload_destroyed(void *context) { struct _hanging_request_payload *ctx = (struct _hanging_request_payload *)context; test_assert(!ctx->serviced); if (ctx->io != NULL) io_remove(&ctx->io); i_free(ctx); io_loop_stop(ioloop); } static void test_server_hanging_request_payload_input(struct _hanging_request_payload *ctx) { struct http_server_response *resp; struct http_server_request *req = ctx->req; const unsigned char *data; size_t size; int ret; if (debug) i_debug("test server: got more payload"); while ((ret=i_stream_read_data (ctx->payload_input, &data, &size, 0)) > 0) { i_stream_skip(ctx->payload_input, size); } if (ret == 0) return; if (ctx->payload_input->stream_errno != 0) { if (debug) { i_debug("test server: failed to read payload: %s", i_stream_get_error(ctx->payload_input)); } i_stream_unref(&ctx->payload_input); io_remove(&ctx->io); http_server_request_fail_close(req, 400, "Bad request"); http_server_request_unref(&req); return; } i_assert(i_stream_is_eof(ctx->payload_input)); resp = http_server_response_create(req, 200, "OK"); http_server_response_submit(resp); ctx->serviced = TRUE; i_stream_unref(&ctx->payload_input); http_server_request_unref(&req); } static void test_server_hanging_request_payload_request( struct http_server_request *req) { const struct http_request *hreq = http_server_request_get(req); struct _hanging_request_payload *ctx; if (debug) { i_debug("REQUEST: %s %s HTTP/%u.%u", hreq->method, hreq->target_raw, hreq->version_major, hreq->version_minor); } ctx = i_new(struct _hanging_request_payload, 1); ctx->req = req; http_server_request_set_destroy_callback(req, test_server_hanging_request_payload_destroyed, ctx); ctx->payload_input = http_server_request_get_payload_input(req, FALSE); http_server_request_ref(req); ctx->io = io_add_istream(ctx->payload_input, test_server_hanging_request_payload_input, ctx); test_server_hanging_request_payload_input(ctx); } static void test_server_hanging_request_payload (const struct http_server_settings *server_set) { test_server_request = test_server_hanging_request_payload_request; test_server_run(server_set); } /* test */ static void test_hanging_request_payload(void) { struct http_server_settings http_server_set; test_server_defaults(&http_server_set); http_server_set.max_client_idle_time_msecs = 1000; test_begin("hanging request payload"); test_run_client_server(&http_server_set, test_server_hanging_request_payload, test_client_hanging_request_payload, 1); test_end(); } /* * Hanging response payload */ /* client */ static void test_hanging_response_payload_connected(struct client_connection *conn) { (void)o_stream_send_str(conn->conn.output, "GET / HTTP/1.1\r\n" "Host: example.com\r\n" "Content-Length: 18\r\n" "\r\n" "Complete payload\r\n"); } static void test_client_hanging_response_payload(unsigned int index) { test_client_connected = test_hanging_response_payload_connected; test_client_run(index); } /* server */ struct _hanging_response_payload { struct http_server_request *req; struct istream *payload_input; struct io *io; unsigned int serviced:1; }; static void test_server_hanging_response_payload_destroyed(void *context) { struct _hanging_response_payload *ctx = (struct _hanging_response_payload *)context; test_assert(!ctx->serviced); if (ctx->io != NULL) io_remove(&ctx->io); i_free(ctx); io_loop_stop(ioloop); } static void test_server_hanging_response_payload_request( struct http_server_request *req) { const struct http_request *hreq = http_server_request_get(req); struct http_server_response *resp; struct _hanging_response_payload *ctx; string_t *payload; unsigned int i; if (debug) { i_debug("REQUEST: %s %s HTTP/%u.%u", hreq->method, hreq->target_raw, hreq->version_major, hreq->version_minor); } ctx = i_new(struct _hanging_response_payload, 1); ctx->req = req; http_server_request_set_destroy_callback(req, test_server_hanging_response_payload_destroyed, ctx); resp = http_server_response_create(req, 200, "OK"); T_BEGIN { payload = t_str_new(204800); for (i = 0; i < 3200; i++) { str_append(payload, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"); } http_server_response_set_payload_data (resp, str_data(payload), str_len(payload)); } T_END; http_server_response_submit(resp); } static void test_server_hanging_response_payload (const struct http_server_settings *server_set) { test_server_request = test_server_hanging_response_payload_request; test_server_run(server_set); } /* test */ static void test_hanging_response_payload(void) { struct http_server_settings http_server_set; test_server_defaults(&http_server_set); http_server_set.socket_send_buffer_size = 4096; http_server_set.max_client_idle_time_msecs = 1000; test_begin("hanging response payload"); test_run_client_server(&http_server_set, test_server_hanging_response_payload, test_client_hanging_response_payload, 1); test_end(); } /* * All tests */ static void (*test_functions[])(void) = { test_slow_request, test_hanging_request_payload, test_hanging_response_payload, NULL }; /* * Test client */ /* client connection */ static void client_connection_input(struct connection *_conn) { struct client_connection *conn = (struct client_connection *)_conn; if (test_client_input != NULL) test_client_input(conn); } static void client_connection_connected(struct connection *_conn, bool success) { struct client_connection *conn = (struct client_connection *)_conn; if (success && test_client_connected != NULL) test_client_connected(conn); } static void client_connection_init(const struct ip_addr *ip, in_port_t port) { struct client_connection *conn; pool_t pool; pool = pool_alloconly_create("client connection", 256); conn = p_new(pool, struct client_connection, 1); conn->pool = pool; connection_init_client_ip(client_conn_list, &conn->conn, ip, port); (void)connection_client_connect(&conn->conn); } static void server_connection_deinit(struct client_connection **_conn) { struct client_connection *conn = *_conn; *_conn = NULL; connection_deinit(&conn->conn); pool_unref(&conn->pool); } static void client_connection_destroy(struct connection *_conn) { struct client_connection *conn = (struct client_connection *)_conn; server_connection_deinit(&conn); } /* */ static struct connection_settings client_connection_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE }; static const struct connection_vfuncs client_connection_vfuncs = { .destroy = client_connection_destroy, .client_connected = client_connection_connected, .input = client_connection_input }; static void test_client_run(unsigned int index) { client_index = index; if (debug) i_debug("client connecting to %u", bind_port); client_conn_list = connection_list_init (&client_connection_set, &client_connection_vfuncs); client_connection_init(&bind_ip, bind_port); io_loop_run(ioloop); /* close server socket */ io_remove(&io_listen); connection_list_deinit(&client_conn_list); } /* * Test server */ static void test_server_defaults(struct http_server_settings *http_set) { /* server settings */ i_zero(http_set); http_set->max_client_idle_time_msecs = 5*1000; http_set->max_pipelined_requests = 1; http_set->debug = debug; } /* client connection */ static void server_handle_request(void *context ATTR_UNUSED, struct http_server_request *req) { test_server_request(req); } struct http_server_callbacks http_server_callbacks = { .handle_request = server_handle_request }; static void server_connection_accept(void *context ATTR_UNUSED) { int fd; /* accept new client */ fd = net_accept(fd_listen, NULL, NULL); if (fd == -1) return; if (fd == -2) { i_fatal("test server: accept() failed: %m"); } (void)http_server_connection_create(http_server, fd, fd, FALSE, &http_server_callbacks, NULL); } /* */ static void test_server_timeout(void *context ATTR_UNUSED) { i_fatal("Server timed out"); io_loop_stop(ioloop); } static void test_server_run(const struct http_server_settings *http_set) { struct timeout *to; to = timeout_add(SERVER_MAX_TIMEOUT_MSECS, test_server_timeout, NULL); /* open server socket */ io_listen = io_add(fd_listen, IO_READ, server_connection_accept, (void *)NULL); http_server = http_server_init(http_set); io_loop_run(ioloop); /* close server socket */ io_remove(&io_listen); timeout_remove(&to); http_server_deinit(&http_server); } /* * Tests */ static int test_open_server_fd(void) { int fd = net_listen(&bind_ip, &bind_port, 128); if (debug) i_debug("server listening on %u", bind_port); if (fd == -1) { i_fatal("listen(%s:%u) failed: %m", net_ip2addr(&bind_ip), bind_port); } return fd; } static void test_clients_kill_all(void) { unsigned int i; if (client_pids_count > 0) { for (i = 0; i < client_pids_count; i++) { if (client_pids[i] != (pid_t)-1) { (void)kill(client_pids[i], SIGKILL); (void)waitpid(client_pids[i], NULL, 0); client_pids[i] = -1; } } } client_pids_count = 0; } static void test_run_client_server( const struct http_server_settings *server_set, test_server_init_t server_test, test_client_init_t client_test, unsigned int client_tests_count) { unsigned int i; client_pids = NULL; client_pids_count = 0; fd_listen = test_open_server_fd(); if (client_tests_count > 0) { client_pids = i_new(pid_t, client_tests_count); for (i = 0; i < client_tests_count; i++) client_pids[i] = (pid_t)-1; client_pids_count = client_tests_count; for (i = 0; i < client_tests_count; i++) { if ((client_pids[i] = fork()) == (pid_t)-1) i_fatal("fork() failed: %m"); if (client_pids[i] == 0) { client_pids[i] = (pid_t)-1; client_pids_count = 0; hostpid_init(); if (debug) i_debug("client[%d]: PID=%s", i+1, my_pid); /* child: client */ usleep(100000); /* wait a little for server setup */ i_close_fd(&fd_listen); ioloop = io_loop_create(); client_test(i); io_loop_destroy(&ioloop); i_free(client_pids); /* wait for it to be killed; this way, valgrind will not object to this process going away inelegantly. */ sleep(60); exit(1); } } if (debug) i_debug("server: PID=%s", my_pid); } /* parent: server */ ioloop = io_loop_create(); server_test(server_set); io_loop_destroy(&ioloop); i_close_fd(&fd_listen); test_clients_kill_all(); i_free(client_pids); } /* * Main */ volatile sig_atomic_t terminating = 0; static void test_signal_handler(int signo) { if (terminating != 0) raise(signo); terminating = 1; /* make sure we don't leave any pesky children alive */ test_clients_kill_all(); (void)signal(signo, SIG_DFL); raise(signo); } static void test_atexit(void) { test_clients_kill_all(); } int main(int argc, char *argv[]) { int c; atexit(test_atexit); (void)signal(SIGCHLD, SIG_IGN); (void)signal(SIGTERM, test_signal_handler); (void)signal(SIGQUIT, test_signal_handler); (void)signal(SIGINT, test_signal_handler); (void)signal(SIGSEGV, test_signal_handler); (void)signal(SIGABRT, test_signal_handler); while ((c = getopt(argc, argv, "D")) > 0) { switch (c) { case 'D': debug = TRUE; break; default: i_fatal("Usage: %s [-D]", argv[0]); } } /* listen on localhost */ i_zero(&bind_ip); bind_ip.family = AF_INET; bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK); test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/test-http-date.c0000644000175000017500000001433613165463624015673 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "test-common.h" #include "http-date.h" #include struct http_date_test { const char *date_in; const char *date_out; struct tm tm; }; /* Valid date tests */ static const struct http_date_test valid_date_tests[] = { /* Preferred format: */ { .date_in = "Sun, 11 Nov 2007 09:42:43 GMT", .tm = { .tm_year = 107, .tm_mon = 10, .tm_mday = 11, .tm_hour = 9, .tm_min = 42, .tm_sec = 43 }, },{ .date_in = "Mon, 17 Aug 1992 13:06:27 GMT", .tm = { .tm_year = 92, .tm_mon = 7, .tm_mday = 17, .tm_hour = 13, .tm_min = 06, .tm_sec = 27 }, },{ .date_in = "Tue, 03 Sep 1974 04:38:08 GMT", .tm = { .tm_year = 74, .tm_mon = 8, .tm_mday = 3, .tm_hour = 4, .tm_min = 38, .tm_sec = 8 }, },{ .date_in = "Wed, 07 May 1980 06:20:42 GMT", .tm = { .tm_year = 80, .tm_mon = 4, .tm_mday = 7, .tm_hour = 6, .tm_min = 20, .tm_sec = 42 }, },{ .date_in = "Thu, 15 Oct 1987 18:30:14 GMT", .tm = { .tm_year = 87, .tm_mon = 9, .tm_mday = 15, .tm_hour = 18, .tm_min = 30, .tm_sec = 14 }, },{ .date_in = "Fri, 20 Dec 1996 00:20:07 GMT", .tm = { .tm_year = 96, .tm_mon = 11, .tm_mday = 20, .tm_hour = 0, .tm_min = 20, .tm_sec = 7 }, },{ .date_in = "Sat, 19 Jan 2036 19:52:18 GMT", .tm = { .tm_year = 136, .tm_mon = 0, .tm_mday = 19, .tm_hour = 19, .tm_min = 52, .tm_sec = 18 }, },{ .date_in = "Mon, 17 Apr 2006 14:41:45 GMT", .tm = { .tm_year = 106, .tm_mon = 3, .tm_mday = 17, .tm_hour = 14, .tm_min = 41, .tm_sec = 45 }, },{ .date_in = "Sun, 06 Mar 2011 16:18:41 GMT", .tm = { .tm_year = 111, .tm_mon = 2, .tm_mday = 6, .tm_hour = 16, .tm_min = 18, .tm_sec = 41 }, },{ .date_in = "Sat, 14 Jun 1975 16:09:30 GMT", .tm = { .tm_year = 75, .tm_mon = 5, .tm_mday = 14, .tm_hour = 16, .tm_min = 9, .tm_sec = 30 }, },{ .date_in = "Fri, 05 Feb 2027 06:53:58 GMT", .tm = { .tm_year = 127, .tm_mon = 1, .tm_mday = 5, .tm_hour = 6, .tm_min = 53, .tm_sec = 58 }, },{ .date_in = "Mon, 09 Jul 2018 02:24:29 GMT", .tm = { .tm_year = 118, .tm_mon = 6, .tm_mday = 9, .tm_hour = 2, .tm_min = 24, .tm_sec = 29 }, /* Obsolete formats: */ },{ .date_in = "Wednesday, 02-Jun-82 16:06:23 GMT", .date_out = "Wed, 02 Jun 1982 16:06:23 GMT", .tm = { .tm_year = 82, .tm_mon = 5, .tm_mday = 2, .tm_hour = 16, .tm_min = 6, .tm_sec = 23 }, },{ .date_in = "Thursday, 23-May-02 12:16:24 GMT", .date_out = "Thu, 23 May 2002 12:16:24 GMT", .tm = { .tm_year = 102, .tm_mon = 4, .tm_mday = 23, .tm_hour = 12, .tm_min = 16, .tm_sec = 24 }, },{ .date_in = "Sun Nov 6 08:49:37 1994", .date_out = "Sun, 06 Nov 1994 08:49:37 GMT", .tm = { .tm_year = 94, .tm_mon = 10, .tm_mday = 6, .tm_hour = 8, .tm_min = 49, .tm_sec = 37 }, },{ .date_in = "Mon Apr 30 02:45:01 2012", .date_out = "Mon, 30 Apr 2012 02:45:01 GMT", .tm = { .tm_year = 112, .tm_mon = 3, .tm_mday = 30, .tm_hour = 2, .tm_min = 45, .tm_sec = 01 }, } }; static const unsigned int valid_date_test_count = N_ELEMENTS(valid_date_tests); static void test_http_date_valid(void) { unsigned int i; for (i = 0; i < valid_date_test_count; i++) T_BEGIN { const char *date_in, *date_out, *pdate_out; const struct tm *tm = &valid_date_tests[i].tm; struct tm ptm; bool result; date_in = valid_date_tests[i].date_in; date_out = valid_date_tests[i].date_out == NULL ? date_in : valid_date_tests[i].date_out; test_begin(t_strdup_printf("http date valid [%d]", i)); result = http_date_parse_tm ((const unsigned char *)date_in, strlen(date_in), &ptm); test_out(t_strdup_printf("parse %s", date_in), result); if (result) { bool equal = tm->tm_year == ptm.tm_year && tm->tm_mon == ptm.tm_mon && tm->tm_mday == ptm.tm_mday && tm->tm_hour == ptm.tm_hour && tm->tm_min == ptm.tm_min && tm->tm_sec == ptm.tm_sec; test_out("valid timestamp", equal); pdate_out = http_date_create_tm(&ptm); test_out_reason("valid create", strcmp(date_out, pdate_out) == 0, pdate_out); } test_end(); } T_END; } /* Invalid date tests */ static const char *invalid_date_tests[] = { "Mom, 09 Jul 2018 02:24:29 GMT", "Mon; 09 Jul 2018 02:24:29 GMT", "Mon, 09 Jul 2018 02:24:29 GMT", "Mon, 90 Jul 2018 02:24:29 GMT", "Mon, 090 Jul 2018 02:24:29 GMT", "Mon, 09 Jul 2018 02:24:29 GMT", "Mon, 09 Lul 2018 02:24:29 GMT", "Mon, 09 July 2018 02:24:29 GMT", "Mon, 09 Jul 2018 02:24:29 GMT", "Mon, 09 Jul 22018 02:24:29 GMT", "Mon, 09 Jul 2018 02:24:29 GMT", "Mon, 09 Jul 2018 032:24:29 GMT", "Mon, 09 Jul 2018 02:224:29 GMT", "Mon, 09 Jul 2018 02:24:239 GMT", "Mon, 09 Jul 2018 02;24:29 GMT", "Mon, 09 Jul 2018 02:24;29 GMT", "Mon, 09 Jul 2018 45:24:29 GMT", "Mon, 09 Jul 2018 02:90:29 GMT", "Mon, 09 Jul 2018 02:24:84 GMT", "Mon, 09 Jul 2018 02:24:29 GMT", "Mon, 09 Jul 2018 02:24:29 UTC", "Mon, 09 Jul 2018 02:24:29 GM", "Mon, 09 Jul 2018 02:24:29 GMTREE", "Thu, 23-May-02 12:16:24 GMT", "Thursday; 23-May-02 12:16:24 GMT", "Thursday, 223-May-02 12:16:24 GMT", "Thursday, 23-Mays-02 12:16:24 GMT", "Thursday, 23-May-2002 12:16:24 GMT", "Thursday, 23-May-02 122:16:24 GMT", "Thursday, 23-May-02 12:164:24 GMT", "Thursday, 23-May-02 12:16:244 GMT", "Thursday, 23-May-02 12:16:24 EET", "Sunday Nov 6 08:49:37 1994", "Sun Nov 6 08:49:37 1994", "Sun November 6 08:49:37 1994", "Sun Nov 6 08:49:37 1994", "Sun Nov 16 08:49:37 1994", "Sun Nov 16 08:49:37 1994", "Sun Nov 6 082:49:37 1994", "Sun Nov 6 08:492:37 1994", "Sun Nov 6 08:49:137 1994", "Sun Nov 6 08:49:37 19914", "Sun Nov 6 08:49:37 0000", }; static const unsigned int invalid_date_test_count = N_ELEMENTS(invalid_date_tests); static void test_http_date_invalid(void) { unsigned int i; for (i = 0; i < invalid_date_test_count; i++) T_BEGIN { const char *date_in; struct tm tm; bool result; date_in = invalid_date_tests[i]; test_begin(t_strdup_printf("http date invalid [%d]", i)); result = http_date_parse_tm ((const unsigned char *)date_in, strlen(date_in), &tm); test_out(t_strdup_printf("parse %s", date_in), !result); test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_http_date_valid, test_http_date_invalid, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-url.c0000644000175000017500000003671513165463624014610 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strfuncs.h" #include "net.h" #include "uri-util.h" #include "http-url.h" #include "http-request.h" /* * HTTP URL parser */ struct http_url_parser { struct uri_parser parser; enum http_url_parse_flags flags; struct http_url *url; struct http_url *base; enum http_request_target_format req_format; unsigned int relative:1; unsigned int request_target:1; }; static bool http_url_parse_authority(struct http_url_parser *url_parser) { struct uri_parser *parser = &url_parser->parser; struct http_url *url = url_parser->url; struct uri_authority auth; const char *user = NULL, *password = NULL; int ret; if ((ret = uri_parse_authority(parser, &auth)) < 0) return FALSE; if (auth.host_literal == NULL || *auth.host_literal == '\0') { /* RFC 7230, Section 2.7.1: http URI Scheme A sender MUST NOT generate an "http" URI with an empty host identifier. A recipient that processes such a URI reference MUST reject it as invalid. */ parser->error = "HTTP URL does not allow empty host identifier"; return FALSE; } if (ret > 0) { if (auth.enc_userinfo != NULL) { const char *p; if ((url_parser->flags & HTTP_URL_ALLOW_USERINFO_PART) == 0) { /* RFC 7230, Section 2.7.1: http URI Scheme A sender MUST NOT generate the userinfo subcomponent (and its "@" delimiter) when an "http" URI reference is generated within a message as a request target or header field value. Before making use of an "http" URI reference received from an untrusted source, a recipient SHOULD parse for userinfo and treat its presence as an error; it is likely being used to obscure the authority for the sake of phishing attacks. */ parser->error = "HTTP URL does not allow `userinfo@' part"; return FALSE; } p = strchr(auth.enc_userinfo, ':'); if (p == NULL) { if (!uri_data_decode(parser, auth.enc_userinfo, NULL, &user)) return FALSE; } else { if (!uri_data_decode(parser, auth.enc_userinfo, p, &user)) return FALSE; if (!uri_data_decode(parser, p+1, NULL, &password)) return FALSE; } } } if (url != NULL) { url->host_name = p_strdup(parser->pool, auth.host_literal); url->host_ip = auth.host_ip; url->have_host_ip = auth.have_host_ip; url->port = auth.port; url->have_port = auth.have_port; url->user = p_strdup(parser->pool, user); url->password = p_strdup(parser->pool, password); } return TRUE; } static bool http_url_parse_authority_form(struct http_url_parser *url_parser) { struct uri_parser *parser = &url_parser->parser; if (!http_url_parse_authority(url_parser)) return FALSE; if (parser->cur != parser->end) return FALSE; url_parser->req_format = HTTP_REQUEST_TARGET_FORMAT_AUTHORITY; return TRUE; } static bool http_url_do_parse(struct http_url_parser *url_parser) { struct uri_parser *parser = &url_parser->parser; struct http_url *url = url_parser->url, *base = url_parser->base; const char *const *path; bool relative = TRUE, have_scheme = FALSE, have_authority = FALSE, have_path = FALSE; int path_relative; const char *part; int ret; /* RFC 7230, Appendix B: http-URI = "http://" authority path-abempty [ "?" query ] [ "#" fragment ] https-URI = "https://" authority path-abempty [ "?" query ] [ "#" fragment ] partial-URI = relative-part [ "?" query ] request-target = origin-form / absolute-form / authority-form / asterisk-form origin-form = absolute-path [ "?" query ] absolute-form = absolute-URI authority-form = authority asterisk-form = "*" ; Not parsed here absolute-path = 1*( "/" segment ) RFC 3986, Appendix A: (implemented in uri-util.h) absolute-URI = scheme ":" hier-part [ "?" query ] hier-part = "//" authority path-abempty / path-absolute / path-rootless / path-empty relative-part = "//" authority path-abempty / path-absolute / path-noscheme / path-empty authority = [ userinfo "@" ] host [ ":" port ] path-abempty = *( "/" segment ) path-absolute = "/" [ segment-nz *( "/" segment ) ] path-noscheme = segment-nz-nc *( "/" segment ) path-rootless = segment-nz *( "/" segment ) path-empty = 0 segment = *pchar segment-nz = 1*pchar segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) ; non-zero-length segment without any colon ":" query = *( pchar / "/" / "?" ) fragment = *( pchar / "/" / "?" ) */ /* "http:" / "https:" */ if ((url_parser->flags & HTTP_URL_PARSE_SCHEME_EXTERNAL) == 0) { const char *scheme; if ((ret = uri_parse_scheme(parser, &scheme)) < 0) return FALSE; else if (ret > 0) { if (strcasecmp(scheme, "https") == 0) { if (url != NULL) url->have_ssl = TRUE; } else if (strcasecmp(scheme, "http") != 0) { if (url_parser->request_target) { /* valid as non-HTTP scheme, but also try to parse as authority */ parser->cur = parser->begin; if (!http_url_parse_authority_form(url_parser)) { url_parser->url = NULL; /* indicate non-http-url */ url_parser->req_format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE; } return TRUE; } parser->error = "Not an HTTP URL"; return FALSE; } relative = FALSE; have_scheme = TRUE; } } else { relative = FALSE; have_scheme = TRUE; } /* "//" authority ; or * ["//"] authority ; when parsing a request target */ if (parser->cur < parser->end && parser->cur[0] == '/') { if (parser->cur+1 < parser->end && parser->cur[1] == '/') { parser->cur += 2; relative = FALSE; have_authority = TRUE; } else { /* start of absolute-path */ } } else if (url_parser->request_target && !have_scheme) { if (!http_url_parse_authority_form(url_parser)) { /* not non-HTTP scheme and invalid as authority-form */ parser->error = "Request target is invalid"; return FALSE; } return TRUE; } if (have_scheme && !have_authority) { parser->error = "Absolute HTTP URL requires `//' after `http:'"; return FALSE; } if (have_authority) { if (!http_url_parse_authority(url_parser)) return FALSE; } /* path-abempty / path-absolute / path-noscheme / path-empty */ if ((ret = uri_parse_path(parser, &path_relative, &path)) < 0) return FALSE; /* Relative URLs are only valid when we have a base URL */ if (relative) { if (base == NULL) { parser->error = "Relative HTTP URL not allowed"; return FALSE; } else if (!have_authority && url != NULL) { url->host_name = p_strdup_empty(parser->pool, base->host_name); url->host_ip = base->host_ip; url->have_host_ip = base->have_host_ip; url->port = base->port; url->have_port = base->have_port; url->have_ssl = base->have_ssl; url->user = p_strdup_empty(parser->pool, base->user); url->password = p_strdup_empty(parser->pool, base->password); } url_parser->relative = TRUE; } /* Resolve path */ if (ret > 0) { string_t *fullpath = NULL; have_path = TRUE; if (url != NULL) fullpath = t_str_new(256); if (relative && path_relative > 0 && base->path != NULL) { const char *pbegin = base->path; const char *pend = base->path + strlen(base->path); const char *p = pend - 1; i_assert(*pbegin == '/'); /* discard trailing segments of base path based on how many effective leading '..' segments were found in the relative path. */ while (path_relative > 0 && p > pbegin) { while (p > pbegin && *p != '/') p--; if (p >= pbegin) { pend = p; path_relative--; } if (p > pbegin) p--; } if (url != NULL && pend > pbegin) str_append_n(fullpath, pbegin, pend-pbegin); } /* append relative path */ while (*path != NULL) { if (!uri_data_decode(parser, *path, NULL, &part)) return FALSE; if (url != NULL) { str_append_c(fullpath, '/'); str_append(fullpath, part); } path++; } if (url != NULL) url->path = p_strdup(parser->pool, str_c(fullpath)); } else if (relative && url != NULL) { url->path = p_strdup(parser->pool, base->path); } /* [ "?" query ] */ if ((ret = uri_parse_query(parser, &part)) < 0) return FALSE; if (ret > 0) { if (url != NULL) url->enc_query = p_strdup(parser->pool, part); } else if (relative && !have_path && url != NULL) { url->enc_query = p_strdup(parser->pool, base->enc_query); } /* [ "#" fragment ] */ if ((ret = uri_parse_fragment(parser, &part)) < 0) return FALSE; if (ret > 0) { if ((url_parser->flags & HTTP_URL_ALLOW_FRAGMENT_PART) == 0) { parser->error = "URL fragment not allowed for HTTP URL in this context"; return FALSE; } if (url != NULL) url->enc_fragment = p_strdup(parser->pool, part); } else if (relative && !have_path && url != NULL) { url->enc_fragment = p_strdup(parser->pool, base->enc_fragment); } /* must be at end of URL now */ i_assert(parser->cur == parser->end); if (have_scheme) url_parser->req_format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE; return TRUE; } /* Public API */ int http_url_parse(const char *url, struct http_url *base, enum http_url_parse_flags flags, pool_t pool, struct http_url **url_r, const char **error_r) { struct http_url_parser url_parser; /* base != NULL indicates whether relative URLs are allowed. However, certain flags may also dictate whether relative URLs are allowed/required. */ i_assert((flags & HTTP_URL_PARSE_SCHEME_EXTERNAL) == 0 || base == NULL); memset(&url_parser, '\0', sizeof(url_parser)); uri_parser_init(&url_parser.parser, pool, url); url_parser.parser.allow_pct_nul = (flags & HTTP_URL_ALLOW_PCT_NUL) != 0; url_parser.url = p_new(pool, struct http_url, 1); url_parser.base = base; url_parser.flags = flags; if (!http_url_do_parse(&url_parser)) { *error_r = url_parser.parser.error; return -1; } *url_r = url_parser.url; return 0; } int http_url_request_target_parse(const char *request_target, const char *host_header, pool_t pool, struct http_request_target *target, const char **error_r) { struct http_url_parser url_parser; struct uri_parser *parser; struct uri_authority host; struct http_url base; memset(&url_parser, '\0', sizeof(url_parser)); parser = &url_parser.parser; uri_parser_init(parser, pool, host_header); if (uri_parse_authority(parser, &host) <= 0) { *error_r = t_strdup_printf("Invalid Host header: %s", parser->error); return -1; } if (parser->cur != parser->end || host.enc_userinfo != NULL) { *error_r = "Invalid Host header: Contains invalid character"; return -1; } if (request_target[0] == '*' && request_target[1] == '\0') { struct http_url *url = p_new(pool, struct http_url, 1); url->host_name = p_strdup(pool, host.host_literal); url->host_ip = host.host_ip; url->port = host.port; url->have_host_ip = host.have_host_ip; url->have_port = host.have_port; target->url = url; target->format = HTTP_REQUEST_TARGET_FORMAT_ASTERISK; return 0; } i_zero(&base); base.host_name = host.host_literal; base.host_ip = host.host_ip; base.port = host.port; base.have_host_ip = host.have_host_ip; base.have_port = host.have_port; memset(parser, '\0', sizeof(*parser)); uri_parser_init(parser, pool, request_target); url_parser.url = p_new(pool, struct http_url, 1); url_parser.request_target = TRUE; url_parser.req_format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN; url_parser.base = &base; url_parser.flags = 0; if (!http_url_do_parse(&url_parser)) { *error_r = url_parser.parser.error; return -1; } target->url = url_parser.url; target->format = url_parser.req_format; return 0; } /* * HTTP URL manipulation */ void http_url_copy_authority(pool_t pool, struct http_url *dest, const struct http_url *src) { i_zero(dest); dest->host_name = p_strdup(pool, src->host_name); if (src->have_host_ip) { dest->host_ip = src->host_ip; dest->have_host_ip = TRUE; } if (src->have_port) { dest->port = src->port; dest->have_port = TRUE; } dest->have_ssl = src->have_ssl; } struct http_url *http_url_clone_authority(pool_t pool, const struct http_url *src) { struct http_url *new_url; new_url = p_new(pool, struct http_url, 1); http_url_copy_authority(pool, new_url, src); return new_url; } void http_url_copy(pool_t pool, struct http_url *dest, const struct http_url *src) { http_url_copy_authority(pool, dest, src); dest->path = p_strdup(pool, src->path); dest->enc_query = p_strdup(pool, src->enc_query); dest->enc_fragment = p_strdup(pool, src->enc_fragment); } void http_url_copy_with_userinfo(pool_t pool, struct http_url *dest, const struct http_url *src) { http_url_copy(pool, dest, src); dest->user = p_strdup(pool, src->user); dest->password = p_strdup(pool, src->password); } struct http_url *http_url_clone(pool_t pool, const struct http_url *src) { struct http_url *new_url; new_url = p_new(pool, struct http_url, 1); http_url_copy(pool, new_url, src); return new_url; } struct http_url *http_url_clone_with_userinfo(pool_t pool, const struct http_url *src) { struct http_url *new_url; new_url = p_new(pool, struct http_url, 1); http_url_copy_with_userinfo(pool, new_url, src); return new_url; } /* * HTTP URL construction */ static void http_url_add_scheme(string_t *urlstr, const struct http_url *url) { /* scheme */ if (!url->have_ssl) uri_append_scheme(urlstr, "http"); else uri_append_scheme(urlstr, "https"); str_append(urlstr, "//"); } static void http_url_add_authority(string_t *urlstr, const struct http_url *url) { /* host:port */ if (url->host_name != NULL) { /* assume IPv6 literal if starts with '['; avoid encoding */ if (*url->host_name == '[') str_append(urlstr, url->host_name); else uri_append_host_name(urlstr, url->host_name); } else if (url->have_host_ip) { uri_append_host_ip(urlstr, &url->host_ip); } else i_unreached(); if (url->have_port) uri_append_port(urlstr, url->port); } static void http_url_add_target(string_t *urlstr, const struct http_url *url) { if (url->path == NULL || *url->path == '\0') { /* Older syntax of RFC 2616 requires this slash at all times for an absolute URL */ str_append_c(urlstr, '/'); } else { uri_append_path_data(urlstr, "", url->path); } /* query (pre-encoded) */ if (url->enc_query != NULL) { str_append_c(urlstr, '?'); str_append(urlstr, url->enc_query); } } const char *http_url_create(const struct http_url *url) { string_t *urlstr = t_str_new(512); http_url_add_scheme(urlstr, url); http_url_add_authority(urlstr, url); http_url_add_target(urlstr, url); /* fragment */ if (url->enc_fragment != NULL) { str_append_c(urlstr, '#'); str_append(urlstr, url->enc_fragment); } return str_c(urlstr); } const char *http_url_create_host(const struct http_url *url) { string_t *urlstr = t_str_new(512); http_url_add_scheme(urlstr, url); http_url_add_authority(urlstr, url); return str_c(urlstr); } const char *http_url_create_authority(const struct http_url *url) { string_t *urlstr = t_str_new(256); http_url_add_authority(urlstr, url); return str_c(urlstr); } const char *http_url_create_target(const struct http_url *url) { string_t *urlstr = t_str_new(256); http_url_add_target(urlstr, url); return str_c(urlstr); } void http_url_escape_path(string_t *out, const char *data) { uri_append_query_data(out, "&;?=+", data); } void http_url_escape_param(string_t *out, const char *data) { uri_append_query_data(out, "&;/?=+", data); } dovecot-2.2.33.2/src/lib-http/http-client-private.h0000644000175000017500000004174613165463624016741 00000000000000#ifndef HTTP_CLIENT_PRIVATE_H #define HTTP_CLIENT_PRIVATE_H #include "connection.h" #include "http-url.h" #include "http-client.h" /* * Defaults */ #define HTTP_DEFAULT_PORT 80 #define HTTPS_DEFAULT_PORT 443 #define HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS (1000*2) #define HTTP_CLIENT_DEFAULT_REQUEST_TIMEOUT_MSECS (1000*60*1) #define HTTP_CLIENT_DEFAULT_DNS_LOOKUP_TIMEOUT_MSECS (1000*10) #define HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS (100) #define HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS (1000*60) #define HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS (1000*60*30) /* * Types */ struct http_client_host; struct http_client_queue; struct http_client_peer; struct http_client_connection; ARRAY_DEFINE_TYPE(http_client_host, struct http_client_host *); ARRAY_DEFINE_TYPE(http_client_queue, struct http_client_queue *); ARRAY_DEFINE_TYPE(http_client_peer, struct http_client_peer *); ARRAY_DEFINE_TYPE(http_client_connection, struct http_client_connection *); ARRAY_DEFINE_TYPE(http_client_request, struct http_client_request *); HASH_TABLE_DEFINE_TYPE(http_client_host, const char *, struct http_client_host *); HASH_TABLE_DEFINE_TYPE(http_client_peer, const struct http_client_peer_addr *, struct http_client_peer *); enum http_client_peer_addr_type { HTTP_CLIENT_PEER_ADDR_HTTP = 0, HTTP_CLIENT_PEER_ADDR_HTTPS, HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL, HTTP_CLIENT_PEER_ADDR_RAW, HTTP_CLIENT_PEER_ADDR_UNIX, }; struct http_client_peer_addr { enum http_client_peer_addr_type type; union { struct { const char *https_name; /* TLS SNI */ struct ip_addr ip; in_port_t port; } tcp; struct { const char *path; } un; } a; }; /* * Objects */ struct http_client_request { pool_t pool; unsigned int refcount; const char *label; unsigned int id; struct http_client_request *prev, *next; const char *method, *target; struct http_url origin_url; const char *username, *password; const char *host_socket; const struct http_url *host_url; const char *authority; struct http_client *client; struct http_client_host *host; struct http_client_queue *queue; struct http_client_peer *peer; struct http_client_connection *conn; string_t *headers; time_t date; struct istream *payload_input; uoff_t payload_size, payload_offset; struct ostream *payload_output; struct timeval release_time; struct timeval submit_time; struct timeval first_sent_time; struct timeval sent_time; struct timeval response_time; struct timeval timeout_time; unsigned int timeout_msecs; unsigned int attempt_timeout_msecs; unsigned int max_attempts; unsigned int attempts; unsigned int redirects; uint64_t sent_global_ioloop_usecs; uint64_t sent_http_ioloop_usecs; uint64_t sent_lock_usecs; unsigned int delayed_error_status; const char *delayed_error; http_client_request_callback_t *callback; void *context; void (*destroy_callback)(void *); void *destroy_context; enum http_request_state state; unsigned int have_hdr_authorization:1; unsigned int have_hdr_body_spec:1; unsigned int have_hdr_connection:1; unsigned int have_hdr_date:1; unsigned int have_hdr_expect:1; unsigned int have_hdr_host:1; unsigned int have_hdr_user_agent:1; unsigned int payload_sync:1; unsigned int payload_sync_continue:1; unsigned int payload_chunked:1; unsigned int payload_wait:1; unsigned int urgent:1; unsigned int submitted:1; unsigned int listed:1; unsigned int connect_tunnel:1; unsigned int connect_direct:1; unsigned int ssl_tunnel:1; unsigned int preserve_exact_reason:1; }; struct http_client_connection { struct connection conn; struct http_client_peer *peer; struct http_client *client; unsigned int refcount; char *label; unsigned int id; // DEBUG: identify parallel connections int connect_errno; struct timeval connect_start_timestamp; struct timeval connected_timestamp; struct http_client_request *connect_request; struct ssl_iostream *ssl_iostream; struct http_response_parser *http_parser; struct timeout *to_connect, *to_input, *to_idle, *to_response; struct timeout *to_requests; struct http_client_request *pending_request; struct istream *incoming_payload; struct io *io_req_payload; struct ioloop *last_ioloop; struct io_wait_timer *io_wait_timer; /* requests that have been sent, waiting for response */ ARRAY_TYPE(http_client_request) request_wait_list; unsigned int connected:1; /* connection is connected */ unsigned int tunneling:1; /* last sent request turns this connection into tunnel */ unsigned int connect_initialized:1; /* connection was initialized */ unsigned int connect_succeeded:1; /* connection succeeded including SSL */ unsigned int connect_failed:1; /* connection failed */ unsigned int lost_prematurely:1; /* lost connection before receiving any data */ unsigned int closing:1; unsigned int disconnected:1; unsigned int close_indicated:1; unsigned int output_locked:1; /* output is locked; no pipelining */ unsigned int output_broken:1; /* output is broken; no more requests */ unsigned int in_req_callback:1; /* performin request callback (busy) */ }; struct http_client_peer { unsigned int refcount; struct http_client_peer_addr addr; char *addr_name; char *label; struct http_client *client; struct http_client_peer *prev, *next; /* queues using this peer */ ARRAY_TYPE(http_client_queue) queues; /* active connections to this peer */ ARRAY_TYPE(http_client_connection) conns; /* zero time-out for consolidating request handling */ struct timeout *to_req_handling; /* connection retry */ struct timeval last_failure; struct timeout *to_backoff; unsigned int backoff_time_msecs; unsigned int disconnected:1; /* peer is already disconnected */ unsigned int no_payload_sync:1; /* expect: 100-continue failed before */ unsigned int seen_100_response:1;/* expect: 100-continue succeeded before */ unsigned int allows_pipelining:1;/* peer is known to allow persistent connections */ unsigned int handling_requests:1;/* currently running request handler */ }; struct http_client_queue { struct http_client *client; struct http_client_host *host; char *name; struct http_client_peer_addr addr; char *addr_name; /* current index in host->ips */ unsigned int ips_connect_idx; /* the first IP that started the current round of connection attempts. initially 0, and later set to the ip index of the last successful connected IP */ unsigned int ips_connect_start_idx; struct timeval first_connect_time; unsigned int connect_attempts; /* peers we are trying to connect to; this can be more than one when soft connect timeouts are enabled */ ARRAY_TYPE(http_client_peer) pending_peers; /* currently active peer */ struct http_client_peer *cur_peer; /* all requests associated to this queue (ordered by earliest timeout first) */ ARRAY_TYPE(http_client_request) requests; /* delayed requests waiting to be released after delay */ ARRAY_TYPE(http_client_request) delayed_requests; /* requests pending in queue to be picked up by connections */ ARRAY_TYPE(http_client_request) queued_requests, queued_urgent_requests; struct timeout *to_connect, *to_request, *to_delayed; }; struct http_client_host { struct http_client_host *prev, *next; struct http_client *client; char *name; /* the ip addresses DNS returned for this host */ unsigned int ips_count; struct ip_addr *ips; struct timeval ips_timeout; /* requests are managed on a per-port basis */ ARRAY_TYPE(http_client_queue) queues; /* active DNS lookup */ struct dns_lookup *dns_lookup; /* timeouts */ struct timeout *to_idle; bool unix_local:1; bool explicit_ip:1; }; struct http_client { pool_t pool; struct http_client_settings set; struct ioloop *ioloop; struct ssl_iostream_context *ssl_ctx; /* list of failed requests that are waiting for ioloop */ ARRAY(struct http_client_request *) delayed_failing_requests; struct timeout *to_failing_requests; struct connection_list *conn_list; HASH_TABLE_TYPE(http_client_host) hosts; struct http_client_host *unix_host; struct http_client_host *hosts_list; HASH_TABLE_TYPE(http_client_peer) peers; struct http_client_peer *peers_list; struct http_client_request *requests_list; unsigned int requests_count; }; /* * Peer address */ static inline bool http_client_peer_addr_is_https(const struct http_client_peer_addr *addr) { switch (addr->type) { case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: return TRUE; default: break; } return FALSE; } static inline const char * http_client_peer_addr_get_https_name(const struct http_client_peer_addr *addr) { switch (addr->type) { case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: return addr->a.tcp.https_name; default: break; } return NULL; } static inline const char * http_client_peer_addr2str(const struct http_client_peer_addr *addr) { switch (addr->type) { case HTTP_CLIENT_PEER_ADDR_HTTP: case HTTP_CLIENT_PEER_ADDR_HTTPS: case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: case HTTP_CLIENT_PEER_ADDR_RAW: if (addr->a.tcp.ip.family == AF_INET6) { return t_strdup_printf("[%s]:%u", net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port); } return t_strdup_printf("%s:%u", net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port); case HTTP_CLIENT_PEER_ADDR_UNIX: return t_strdup_printf("unix:%s", addr->a.un.path); default: break; } i_unreached(); return ""; } /* * Request */ static inline bool http_client_request_to_proxy(const struct http_client_request *req) { return (req->host_url != &req->origin_url); } const char * http_client_request_label(struct http_client_request *req); void http_client_request_ref(struct http_client_request *req); /* Returns FALSE if unrefing destroyed the request entirely */ bool http_client_request_unref(struct http_client_request **_req); void http_client_request_destroy(struct http_client_request **_req); int http_client_request_delay_from_response(struct http_client_request *req, const struct http_response *response); void http_client_request_get_peer_addr(const struct http_client_request *req, struct http_client_peer_addr *addr); enum http_response_payload_type http_client_request_get_payload_type(struct http_client_request *req); int http_client_request_send(struct http_client_request *req, bool pipelined, const char **error_r); int http_client_request_send_more(struct http_client_request *req, bool pipelined, const char **error_r); bool http_client_request_callback(struct http_client_request *req, struct http_response *response); void http_client_request_connect_callback(struct http_client_request *req, const struct http_client_tunnel *tunnel, struct http_response *response); void http_client_request_resubmit(struct http_client_request *req); void http_client_request_retry(struct http_client_request *req, unsigned int status, const char *error); void http_client_request_error_delayed(struct http_client_request **_req); void http_client_request_error(struct http_client_request **req, unsigned int status, const char *error); void http_client_request_redirect(struct http_client_request *req, unsigned int status, const char *location); void http_client_request_finish(struct http_client_request *req); /* * Connection */ struct connection_list *http_client_connection_list_init(void); struct http_client_connection * http_client_connection_create(struct http_client_peer *peer); void http_client_connection_ref(struct http_client_connection *conn); /* Returns FALSE if unrefing destroyed the connection entirely */ bool http_client_connection_unref(struct http_client_connection **_conn); void http_client_connection_close(struct http_client_connection **_conn); void http_client_connection_peer_closed(struct http_client_connection **_conn); void http_client_connection_request_destroyed( struct http_client_connection *conn, struct http_client_request *req); int http_client_connection_output(struct http_client_connection *conn); void http_client_connection_start_request_timeout( struct http_client_connection *conn); void http_client_connection_reset_request_timeout( struct http_client_connection *conn); void http_client_connection_stop_request_timeout( struct http_client_connection *conn); unsigned int http_client_connection_count_pending(struct http_client_connection *conn); int http_client_connection_check_ready(struct http_client_connection *conn); bool http_client_connection_is_idle(struct http_client_connection *conn); bool http_client_connection_is_active(struct http_client_connection *conn); int http_client_connection_next_request(struct http_client_connection *conn); void http_client_connection_check_idle(struct http_client_connection *conn); void http_client_connection_switch_ioloop(struct http_client_connection *conn); void http_client_connection_start_tunnel(struct http_client_connection **_conn, struct http_client_tunnel *tunnel); /* * Peer */ unsigned int http_client_peer_addr_hash (const struct http_client_peer_addr *peer) ATTR_PURE; int http_client_peer_addr_cmp (const struct http_client_peer_addr *peer1, const struct http_client_peer_addr *peer2) ATTR_PURE; const char * http_client_peer_label(struct http_client_peer *peer); struct http_client_peer * http_client_peer_get(struct http_client *client, const struct http_client_peer_addr *addr); void http_client_peer_ref(struct http_client_peer *peer); bool http_client_peer_unref(struct http_client_peer **_peer); void http_client_peer_close(struct http_client_peer **_peer); bool http_client_peer_have_queue(struct http_client_peer *peer, struct http_client_queue *queue); void http_client_peer_link_queue(struct http_client_peer *peer, struct http_client_queue *queue); void http_client_peer_unlink_queue(struct http_client_peer *peer, struct http_client_queue *queue); struct http_client_request * http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent); void http_client_peer_trigger_request_handler(struct http_client_peer *peer); void http_client_peer_connection_success(struct http_client_peer *peer); void http_client_peer_connection_failure(struct http_client_peer *peer, const char *reason); void http_client_peer_connection_lost(struct http_client_peer *peer, bool premature); bool http_client_peer_is_connected(struct http_client_peer *peer); unsigned int http_client_peer_idle_connections(struct http_client_peer *peer); unsigned int http_client_peer_active_connections(struct http_client_peer *peer); unsigned int http_client_peer_pending_connections(struct http_client_peer *peer); void http_client_peer_switch_ioloop(struct http_client_peer *peer); /* * Queue */ struct http_client_queue * http_client_queue_create(struct http_client_host *host, const struct http_client_peer_addr *addr); void http_client_queue_free(struct http_client_queue *queue); void http_client_queue_connection_setup(struct http_client_queue *queue); unsigned int http_client_queue_host_lookup_done(struct http_client_queue *queue); void http_client_queue_host_lookup_failure( struct http_client_queue *queue, const char *error); void http_client_queue_submit_request(struct http_client_queue *queue, struct http_client_request *req); void http_client_queue_drop_request(struct http_client_queue *queue, struct http_client_request *req); struct http_client_request * http_client_queue_claim_request(struct http_client_queue *queue, const struct http_client_peer_addr *addr, bool no_urgent); unsigned int http_client_queue_requests_pending(struct http_client_queue *queue, unsigned int *num_urgent_r) ATTR_NULL(2); unsigned int http_client_queue_requests_active(struct http_client_queue *queue); void http_client_queue_connection_success(struct http_client_queue *queue, const struct http_client_peer_addr *addr); void http_client_queue_connection_failure(struct http_client_queue *queue, const struct http_client_peer_addr *addr, const char *reason); void http_client_queue_peer_disconnected(struct http_client_queue *queue, struct http_client_peer *peer); void http_client_queue_switch_ioloop(struct http_client_queue *queue); /* * Host */ static inline bool http_client_host_get_ip_idx(struct http_client_host *host, const struct ip_addr *ip, unsigned int *idx_r) { unsigned int i; for (i = 0; i < host->ips_count; i++) { if (net_ip_compare(&host->ips[i], ip)) { *idx_r = i; return TRUE; } } return FALSE; } struct http_client_host * http_client_host_get(struct http_client *client, const struct http_url *host_url); void http_client_host_free(struct http_client_host **_host); void http_client_host_submit_request(struct http_client_host *host, struct http_client_request *req); void http_client_host_switch_ioloop(struct http_client_host *host); void http_client_host_check_idle(struct http_client_host *host); int http_client_host_refresh(struct http_client_host *host); /* * Client */ int http_client_init_ssl_ctx(struct http_client *client, const char **error_r); void http_client_delay_request_error(struct http_client *client, struct http_client_request *req); void http_client_remove_request_error(struct http_client *client, struct http_client_request *req); #endif dovecot-2.2.33.2/src/lib-http/http-request-parser.c0000644000175000017500000004251513165463624016763 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "http-url.h" #include "http-parser.h" #include "http-message-parser.h" #include "http-request-parser.h" #define HTTP_REQUEST_PARSER_MAX_METHOD_LENGTH 32 enum http_request_parser_state { HTTP_REQUEST_PARSE_STATE_INIT = 0, HTTP_REQUEST_PARSE_STATE_SKIP_LINE, HTTP_REQUEST_PARSE_STATE_METHOD, HTTP_REQUEST_PARSE_STATE_SP1, HTTP_REQUEST_PARSE_STATE_TARGET, HTTP_REQUEST_PARSE_STATE_SP2, HTTP_REQUEST_PARSE_STATE_VERSION, HTTP_REQUEST_PARSE_STATE_CR, HTTP_REQUEST_PARSE_STATE_LF, HTTP_REQUEST_PARSE_STATE_HEADER }; struct http_request_parser { struct http_message_parser parser; enum http_request_parser_state state; uoff_t max_target_length; enum http_request_parse_error error_code; const char *request_method; const char *request_target; unsigned int skipping_line:1; }; struct http_request_parser * http_request_parser_init(struct istream *input, const struct http_request_limits *limits, enum http_request_parse_flags flags) { struct http_request_parser *parser; struct http_header_limits hdr_limits; uoff_t max_payload_size; enum http_message_parse_flags msg_flags = 0; parser = i_new(struct http_request_parser, 1); if (limits != NULL) { hdr_limits = limits->header; max_payload_size = limits->max_payload_size; } else { i_zero(&hdr_limits); max_payload_size = 0; } /* substitute default limits */ if (parser->max_target_length == 0) parser->max_target_length = HTTP_REQUEST_DEFAULT_MAX_TARGET_LENGTH; if (hdr_limits.max_size == 0) hdr_limits.max_size = HTTP_REQUEST_DEFAULT_MAX_HEADER_SIZE; if (hdr_limits.max_field_size == 0) hdr_limits.max_field_size = HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELD_SIZE; if (hdr_limits.max_fields == 0) hdr_limits.max_fields = HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELDS; if (max_payload_size == 0) max_payload_size = HTTP_REQUEST_DEFAULT_MAX_PAYLOAD_SIZE; if ((flags & HTTP_REQUEST_PARSE_FLAG_STRICT) != 0) msg_flags |= HTTP_MESSAGE_PARSE_FLAG_STRICT; http_message_parser_init(&parser->parser, input, &hdr_limits, max_payload_size, msg_flags); return parser; } void http_request_parser_deinit(struct http_request_parser **_parser) { struct http_request_parser *parser = *_parser; *_parser = NULL; http_message_parser_deinit(&parser->parser); i_free(parser); } static void http_request_parser_restart(struct http_request_parser *parser, pool_t pool) { http_message_parser_restart(&parser->parser, pool); parser->request_method = NULL; parser->request_target = NULL; } static int http_request_parse_method(struct http_request_parser *parser) { const unsigned char *p = parser->parser.cur; pool_t pool; /* method = token */ while (p < parser->parser.end && http_char_is_token(*p)) p++; if ((p - parser->parser.cur) > HTTP_REQUEST_PARSER_MAX_METHOD_LENGTH) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG; parser->parser.error = "HTTP request method is too long"; return -1; } if (p == parser->parser.end) return 0; pool = http_message_parser_get_pool(&parser->parser); parser->request_method = p_strdup_until(pool, parser->parser.cur, p); parser->parser.cur = p; return 1; } static int http_request_parse_target(struct http_request_parser *parser) { struct http_message_parser *_parser = &parser->parser; const unsigned char *p = parser->parser.cur; pool_t pool; /* We'll just parse anything up to the first SP or a control char. We could also implement workarounds for buggy HTTP clients and parse anything up to the HTTP-version and return 301 with the target properly encoded (FIXME). */ while (p < _parser->end && *p > ' ') p++; /* target is too long when explicit limit is exceeded or when input buffer runs out of space */ /* FIXME: put limit on full request line rather than target and method separately */ /* FIXME: is it wise to keep target in stream buffer? It can become very large for some applications, increasing the stream buffer size */ if ((uoff_t)(p - _parser->cur) > parser->max_target_length || (p == _parser->end && ((uoff_t)(p - _parser->cur) >= i_stream_get_max_buffer_size(_parser->input)))) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG; parser->parser.error = "HTTP request target is too long"; return -1; } if (p == _parser->end) return 0; pool = http_message_parser_get_pool(_parser); parser->request_target = p_strdup_until(pool, _parser->cur, p); parser->parser.cur = p; return 1; } static inline const char *_chr_sanitize(unsigned char c) { if (c >= 0x20 && c < 0x7F) return t_strdup_printf("`%c'", c); if (c == 0x0a) return ""; if (c == 0x0d) return ""; return t_strdup_printf("<0x%02x>", c); } static int http_request_parse(struct http_request_parser *parser, pool_t pool) { struct http_message_parser *_parser = &parser->parser; int ret; /* RFC 7230, Section 3.1.1: Request Line request-line = method SP request-target SP HTTP-version CRLF method = token */ for (;;) { switch (parser->state) { case HTTP_REQUEST_PARSE_STATE_INIT: http_request_parser_restart(parser, pool); parser->state = HTTP_REQUEST_PARSE_STATE_SKIP_LINE; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_SKIP_LINE: if (*_parser->cur == '\r' || *_parser->cur == '\n') { if (parser->skipping_line) { /* second extra CRLF; not allowed */ parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = "Empty request line"; return -1; } /* HTTP/1.0 client sent one extra CRLF after body. ignore it. */ parser->skipping_line = TRUE; parser->state = HTTP_REQUEST_PARSE_STATE_CR; break; } parser->state = HTTP_REQUEST_PARSE_STATE_METHOD; parser->skipping_line = FALSE; /* fall through */ case HTTP_REQUEST_PARSE_STATE_METHOD: if ((ret=http_request_parse_method(parser)) <= 0) return ret; parser->state = HTTP_REQUEST_PARSE_STATE_SP1; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_SP1: if (*_parser->cur != ' ') { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = t_strdup_printf ("Unexpected character %s in request method", _chr_sanitize(*_parser->cur)); return -1; } _parser->cur++; parser->state = HTTP_REQUEST_PARSE_STATE_TARGET; if (_parser->cur >= _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_TARGET: if ((ret=http_request_parse_target(parser)) <= 0) return ret; parser->state = HTTP_REQUEST_PARSE_STATE_SP2; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_SP2: if (*_parser->cur != ' ') { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = t_strdup_printf ("Unexpected character %s in request target", _chr_sanitize(*_parser->cur)); return -1; } _parser->cur++; parser->state = HTTP_REQUEST_PARSE_STATE_VERSION; if (_parser->cur >= _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_VERSION: if ((ret=http_message_parse_version(&parser->parser)) <= 0) { if (ret < 0) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = "Invalid HTTP version in request"; } return ret; } parser->state = HTTP_REQUEST_PARSE_STATE_CR; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_CR: if (*_parser->cur == '\r') _parser->cur++; parser->state = HTTP_REQUEST_PARSE_STATE_LF; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_LF: if (*_parser->cur != '\n') { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = t_strdup_printf ("Unexpected character %s at end of request line", _chr_sanitize(*_parser->cur)); return -1; } _parser->cur++; if (!parser->skipping_line) { parser->state = HTTP_REQUEST_PARSE_STATE_HEADER; return 1; } parser->state = HTTP_REQUEST_PARSE_STATE_INIT; break; case HTTP_REQUEST_PARSE_STATE_HEADER: default: i_unreached(); } } i_unreached(); return -1; } static int http_request_parse_request_line(struct http_request_parser *parser, pool_t pool) { struct http_message_parser *_parser = &parser->parser; const unsigned char *begin; size_t size, old_bytes = 0; int ret; while ((ret = i_stream_read_data(_parser->input, &begin, &size, old_bytes)) > 0) { _parser->cur = begin; _parser->end = _parser->cur + size; if ((ret = http_request_parse(parser, pool)) < 0) return -1; i_stream_skip(_parser->input, _parser->cur - begin); if (ret > 0) return 1; old_bytes = i_stream_get_data_size(_parser->input); } if (ret == -2) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; _parser->error = "HTTP request line is too long"; return -1; } if (ret < 0) { if (_parser->input->eof && parser->state == HTTP_REQUEST_PARSE_STATE_INIT) return 0; parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM; _parser->error = "Broken stream"; return -1; } return 0; } static inline enum http_request_parse_error http_request_parser_message_error(struct http_request_parser *parser) { switch (parser->parser.error_code) { case HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM: return HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM; case HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE: return HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; case HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED: return HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED; case HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE: return HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE; case HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE: return HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; default: break; } i_unreached(); return HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; } bool http_request_parser_pending_payload( struct http_request_parser *parser) { if (parser->parser.payload == NULL) return FALSE; return i_stream_have_bytes_left(parser->parser.payload); } static int http_request_parse_expect_header(struct http_request_parser *parser, struct http_request *request, const struct http_header_field *hdr) { struct http_message_parser *_parser = &parser->parser; struct http_parser hparser; bool parse_error = FALSE; unsigned int num_expectations = 0; /* RFC 7231, Section 5.1.1: Expect = "100-continue" */ // FIXME: simplify; RFC 7231 discarded Expect extension mechanism http_parser_init(&hparser, (const unsigned char *)hdr->value, hdr->size); while (!parse_error) { const char *expect_name, *expect_value; /* expect-name */ if (http_parse_token(&hparser, &expect_name) > 0) { num_expectations++; if (strcasecmp(expect_name, "100-continue") == 0) { request->expect_100_continue = TRUE; } else { if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; _parser->error = t_strdup_printf ("Unknown Expectation `%s'", expect_name); } } /* BWS "=" BWS */ http_parse_ows(&hparser); if (hparser.cur >= hparser.end) break; if (*hparser.cur == '=') { hparser.cur++; http_parse_ows(&hparser); /* value */ if (http_parse_token_or_qstring(&hparser, &expect_value) <= 0) { parse_error = TRUE; break; } if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; _parser->error = t_strdup_printf ("Expectation `%s' has unexpected value", expect_name); } } /* *( OWS ";" [ OWS expect-param ] ) */ while (!parse_error) { const char *attribute, *value; /* OWS ";" */ http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != ';') break; hparser.cur++; http_parse_ows(&hparser); /* expect-param */ if (http_parse_token(&hparser, &attribute) <= 0) { parse_error = TRUE; break; } /* BWS "=" BWS */ http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != '=') { parse_error = TRUE; break; } hparser.cur++; http_parse_ows(&hparser); /* value */ if (http_parse_token_or_qstring(&hparser, &value) <= 0) { parse_error = TRUE; break; } if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; _parser->error = t_strdup_printf ("Expectation `%s' has unknown parameter `'%s'", expect_name, attribute); } } if (parse_error) break; } http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != ',') break; hparser.cur++; http_parse_ows(&hparser); } if (parse_error || hparser.cur < hparser.end) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; _parser->error = "Invalid Expect header"; return -1; } if (parser->error_code != HTTP_REQUEST_PARSE_ERROR_NONE) return -1; if (num_expectations == 0) { parser->error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; _parser->error = "Empty Expect header"; return -1; } return 0; } static int http_request_parse_headers(struct http_request_parser *parser, struct http_request *request) { const ARRAY_TYPE(http_header_field) *hdrs; const struct http_header_field *hdr; hdrs = http_header_get_fields(parser->parser.msg.header); array_foreach(hdrs, hdr) { int ret = 0; /* Expect: */ if (http_header_field_is(hdr, "Expect")) ret = http_request_parse_expect_header(parser, request, hdr); if (ret < 0) return -1; } return 0; } int http_request_parse_finish_payload( struct http_request_parser *parser, enum http_request_parse_error *error_code_r, const char **error_r) { int ret; *error_code_r = parser->error_code = HTTP_REQUEST_PARSE_ERROR_NONE; *error_r = parser->parser.error = NULL; /* make sure we finished streaming payload from previous request before we continue. */ if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) { if (ret < 0) { *error_code_r = http_request_parser_message_error(parser); *error_r = parser->parser.error; } } return ret; } int http_request_parse_next(struct http_request_parser *parser, pool_t pool, struct http_request *request, enum http_request_parse_error *error_code_r, const char **error_r) { const struct http_header_field *hdr; const char *error; int ret; /* initialize and get rid of any payload of previous request */ if ((ret=http_request_parse_finish_payload (parser, error_code_r, error_r)) <= 0) return ret; /* RFC 7230, Section 3: HTTP-message = start-line *( header-field CRLF ) CRLF [ message-body ] */ if (parser->state != HTTP_REQUEST_PARSE_STATE_HEADER) { ret = http_request_parse_request_line(parser, pool); /* assign early for error reporting */ request->method = parser->request_method; request->target_raw = parser->request_target; request->version_major = parser->parser.msg.version_major; request->version_minor = parser->parser.msg.version_minor; if (ret <= 0) { if (ret < 0) { *error_code_r = parser->error_code; *error_r = parser->parser.error; } return ret; } } if ((ret = http_message_parse_headers(&parser->parser)) <= 0) { if (ret < 0) { *error_code_r = http_request_parser_message_error(parser); *error_r = parser->parser.error; } return ret; } if (http_message_parse_body(&parser->parser, TRUE) < 0) { *error_code_r = http_request_parser_message_error(parser); *error_r = parser->parser.error; return -1; } parser->state = HTTP_REQUEST_PARSE_STATE_INIT; /* RFC 7230, Section 5.4: Host A server MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message that lacks a Host header field and to any request message that contains more than one Host header field or a Host header field with an invalid field-value. */ if ((ret=http_header_field_find_unique (parser->parser.msg.header, "Host", &hdr)) <= 0) { *error_code_r = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; if (ret == 0) *error_r = "Missing Host header"; else *error_r = "Duplicate Host header"; return -1; } i_zero(request); pool = http_message_parser_get_pool(&parser->parser); if (http_url_request_target_parse(parser->request_target, hdr->value, pool, &request->target, &error) < 0) { *error_code_r = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; *error_r = t_strdup_printf("Bad request target `%s': %s", parser->request_target, error); return -1; } /* parse request-specific headers */ if (http_request_parse_headers(parser, request) < 0) { *error_code_r = parser->error_code; *error_r = parser->parser.error; return -1; } request->method = parser->request_method; request->target_raw = parser->request_target; request->version_major = parser->parser.msg.version_major; request->version_minor = parser->parser.msg.version_minor; request->date = parser->parser.msg.date; request->payload = parser->parser.payload; request->header = parser->parser.msg.header; request->connection_options = parser->parser.msg.connection_options; request->connection_close = parser->parser.msg.connection_close; /* reset this state early */ parser->request_method = NULL; parser->request_target = NULL; return 1; } dovecot-2.2.33.2/src/lib-http/http-header.c0000644000175000017500000000444413165463624015230 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "http-header.h" struct http_header { ARRAY_TYPE(http_header_field) fields; /* FIXME: ARRAY(struct http_header_field *) *btree; */ }; struct http_header * http_header_create(pool_t pool, unsigned int init_count) { struct http_header *header; header = p_new(pool, struct http_header, 1); p_array_init(&header->fields, pool, init_count); return header; } const struct http_header_field * http_header_field_add(struct http_header *header, const char *name, const unsigned char *data, size_t size) { struct http_header_field *hfield; pool_t pool = array_get_pool(&header->fields); void *value; hfield = array_append_space(&header->fields); hfield->key = p_strdup(pool, name); hfield->size = size; value = p_malloc(pool, size+1); memcpy(value, data, size); hfield->value = (const char *)value; return hfield; } void http_header_field_delete(struct http_header *header, const char *name) { ARRAY_TYPE(http_header_field) *hfields = &header->fields; const struct http_header_field *hfield; array_foreach(hfields, hfield) { if (http_header_field_is(hfield, name)) { array_delete(hfields, array_foreach_idx(hfields, hfield), 1); } } } const ARRAY_TYPE(http_header_field) * http_header_get_fields(const struct http_header *header) { return &header->fields; } const struct http_header_field * http_header_field_find(const struct http_header *header, const char *name) { const struct http_header_field *hfield; array_foreach(&header->fields, hfield) { if (http_header_field_is(hfield, name)) return hfield; } return NULL; } const char * http_header_field_get(const struct http_header *header, const char *name) { const struct http_header_field *hfield = http_header_field_find(header, name); return (hfield == NULL ? NULL : hfield->value); } int http_header_field_find_unique(const struct http_header *header, const char *name, const struct http_header_field **hfield_r) { const struct http_header_field *hfield, *hfield_found = NULL; array_foreach(&header->fields, hfield) { if (http_header_field_is(hfield, name)) { if (hfield_found != NULL) return -1; hfield_found = hfield; } } *hfield_r = hfield_found; return (hfield_found == NULL ? 0 : 1); } dovecot-2.2.33.2/src/lib-http/http-server-connection.c0000644000175000017500000010433113165463624017437 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "array.h" #include "str.h" #include "ioloop.h" #include "istream.h" #include "istream-timeout.h" #include "ostream.h" #include "connection.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "master-service.h" #include "master-service-ssl.h" #include "http-date.h" #include "http-request-parser.h" #include "http-server-private.h" static void http_server_connection_disconnect(struct http_server_connection *conn, const char *reason); static bool http_server_connection_unref_is_closed(struct http_server_connection *conn); /* * Logging */ static inline void http_server_connection_debug(struct http_server_connection *conn, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_connection_debug(struct http_server_connection *conn, const char *format, ...) { va_list args; if (conn->server->set.debug) { va_start(args, format); i_debug("http-server: conn %s: %s", http_server_connection_label(conn), t_strdup_vprintf(format, args)); va_end(args); } } static inline void http_server_connection_error(struct http_server_connection *conn, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_connection_error(struct http_server_connection *conn, const char *format, ...) { va_list args; va_start(args, format); i_error("http-server: conn %s: %s", http_server_connection_label(conn), t_strdup_vprintf(format, args)); va_end(args); } static inline void http_server_connection_client_error(struct http_server_connection *conn, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_connection_client_error(struct http_server_connection *conn, const char *format, ...) { va_list args; va_start(args, format); i_info("http-server: conn %s: %s", http_server_connection_label(conn), t_strdup_vprintf(format, args)); va_end(args); } /* * Connection */ static void http_server_connection_input(struct connection *_conn); static void http_server_connection_update_stats(struct http_server_connection *conn) { if (conn->conn.input != NULL) conn->stats.input = conn->conn.input->v_offset; if (conn->conn.output != NULL) conn->stats.output = conn->conn.output->offset; } const struct http_server_stats * http_server_connection_get_stats(struct http_server_connection *conn) { http_server_connection_update_stats(conn); return &conn->stats; } static void http_server_connection_input_halt(struct http_server_connection *conn) { if (conn->conn.io != NULL) io_remove(&conn->conn.io); } static void http_server_connection_input_resume(struct http_server_connection *conn) { if (conn->conn.io == NULL && !conn->closed && !conn->input_broken && !conn->close_indicated && !conn->in_req_callback && conn->incoming_payload == NULL) { conn->conn.io = io_add_istream(conn->conn.input, http_server_connection_input, &conn->conn); } } static void http_server_connection_idle_timeout(struct http_server_connection *conn) { http_server_connection_client_error(conn, "Disconnected for inactivity"); http_server_connection_close(&conn, "Disconnected for inactivity"); } static void http_server_connection_timeout_stop(struct http_server_connection *conn) { if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); } static void http_server_connection_timeout_start(struct http_server_connection *conn) { if (conn->to_idle == NULL && conn->server->set.max_client_idle_time_msecs > 0) { conn->to_idle = timeout_add(conn->server->set.max_client_idle_time_msecs, http_server_connection_idle_timeout, conn); } } static void http_server_connection_timeout_reset(struct http_server_connection *conn) { if (conn->to_idle != NULL) timeout_reset(conn->to_idle); } bool http_server_connection_shut_down(struct http_server_connection *conn) { if (conn->request_queue_head == NULL || conn->request_queue_head->state == HTTP_SERVER_REQUEST_STATE_NEW) { http_server_connection_close(&conn, "Server shutting down"); return TRUE; } return FALSE; } static void http_server_connection_ready(struct http_server_connection *conn) { struct stat st; if (conn->server->set.rawlog_dir != NULL && stat(conn->server->set.rawlog_dir, &st) == 0) { iostream_rawlog_create(conn->server->set.rawlog_dir, &conn->conn.input, &conn->conn.output); } conn->http_parser = http_request_parser_init (conn->conn.input, &conn->server->set.request_limits, HTTP_REQUEST_PARSE_FLAG_STRICT); o_stream_set_flush_callback(conn->conn.output, http_server_connection_output, conn); } static void http_server_connection_destroy(struct connection *_conn) { struct http_server_connection *conn = (struct http_server_connection *)_conn; http_server_connection_disconnect(conn, NULL); http_server_connection_unref(&conn); } static void http_server_payload_finished(struct http_server_connection *conn) { timeout_remove(&conn->to_input); http_server_connection_input_resume(conn); } static void http_server_payload_destroyed_timeout(struct http_server_connection *conn) { http_server_connection_input(&conn->conn); } static void http_server_payload_destroyed(struct http_server_request *req) { struct http_server_connection *conn = req->conn; int stream_errno; i_assert(conn != NULL); i_assert(conn->request_queue_tail == req || req->state >= HTTP_SERVER_REQUEST_STATE_FINISHED); i_assert(conn->conn.io == NULL); http_server_connection_debug(conn, "Request payload stream destroyed"); /* caller is allowed to change the socket fd to blocking while reading the payload. make sure here that it's switched back. */ net_set_nonblock(conn->conn.fd_in, TRUE); stream_errno = conn->incoming_payload->stream_errno; conn->incoming_payload = NULL; /* handle errors in transfer stream */ if (req->response == NULL && stream_errno != 0 && conn->conn.input->stream_errno == 0) { switch (stream_errno) { case EMSGSIZE: conn->input_broken = TRUE; http_server_connection_client_error(conn, "Client sent excessively large request"); http_server_request_fail_close(req, 413, "Payload Too Large"); return; case EIO: conn->input_broken = TRUE; http_server_connection_client_error(conn, "Client sent invalid request payload"); http_server_request_fail_close(req, 400, "Bad Request"); return; default: break; } } /* resource stopped reading payload; update state */ switch (req->state) { case HTTP_SERVER_REQUEST_STATE_QUEUED: case HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN: /* finished reading request */ req->state = HTTP_SERVER_REQUEST_STATE_PROCESSING; http_server_connection_timeout_stop(conn); if (req->response != NULL && req->response->submitted) http_server_request_submit_response(req); break; case HTTP_SERVER_REQUEST_STATE_PROCESSING: /* no response submitted yet */ break; case HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE: /* response submitted, but not all payload is necessarily read */ if (http_server_request_is_complete(req)) http_server_request_ready_to_respond(req); break; case HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND: case HTTP_SERVER_REQUEST_STATE_FINISHED: case HTTP_SERVER_REQUEST_STATE_ABORTED: /* nothing to do */ break; default: i_unreached(); } /* input stream may have pending input. make sure input handler gets called (but don't do it directly, since we get get here somewhere from the API user's code, which we can't really know what state it is in). this call also triggers sending the next response if necessary. */ if (!conn->input_broken && !conn->in_req_callback) { conn->to_input = timeout_add_short(0, http_server_payload_destroyed_timeout, conn); } } static void http_server_connection_request_callback( struct http_server_connection *conn, struct http_server_request *req) { unsigned int old_refcount = req->refcount; /* CONNECT method */ if (strcmp(req->req.method, "CONNECT") == 0) { if (conn->callbacks->handle_connect_request == NULL) { http_server_request_fail(req, 505, "Not Implemented"); return; } if (req->req.target.format != HTTP_REQUEST_TARGET_FORMAT_AUTHORITY) { http_server_request_fail(req, 400, "Bad Request"); return; } conn->callbacks->handle_connect_request (conn->context, req, req->req.target.url); /* other methods */ } else { if (conn->callbacks->handle_request == NULL) { http_server_request_fail(req, 505, "Not Implemented"); return; } conn->callbacks->handle_request(conn->context, req); } i_assert((req->response != NULL && req->response->submitted) || req->refcount > old_refcount); } static bool http_server_connection_handle_request(struct http_server_connection *conn, struct http_server_request *req) { const struct http_server_settings *set = &conn->server->set; struct istream *payload; i_assert(!conn->in_req_callback); i_assert(conn->incoming_payload == NULL); if (req->req.version_major != 1) { http_server_request_fail(req, 505, "HTTP Version Not Supported"); return TRUE; } req->state = HTTP_SERVER_REQUEST_STATE_QUEUED; if (req->req.payload != NULL) { /* wrap the stream to capture the destroy event without destroying the actual payload stream. */ conn->incoming_payload = req->req.payload = i_stream_create_timeout(req->req.payload, set->max_client_idle_time_msecs); /* we've received the request itself, and we can't reset the timeout during the payload reading. */ http_server_connection_timeout_stop(conn); } else { conn->incoming_payload = req->req.payload = i_stream_create_from_data("", 0); } i_stream_add_destroy_callback(req->req.payload, http_server_payload_destroyed, req); /* the callback may add its own I/O, so we need to remove our one before calling it */ http_server_connection_input_halt(conn); conn->in_req_callback = TRUE; http_server_connection_request_callback(conn, req); if (conn->closed) { /* the callback managed to get this connection destroyed/closed */ return FALSE; } conn->in_req_callback = FALSE; if (req->req.payload != NULL) { /* send 100 Continue when appropriate */ if (req->req.expect_100_continue && !req->payload_halted && req->response == NULL) { http_server_connection_trigger_responses(conn); } /* delegate payload handling to request handler */ if (req->state < HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN) req->state = HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN; payload = req->req.payload; req->req.payload = NULL; i_stream_unref(&payload); if (conn->to_input != NULL) { /* already finished reading the payload */ http_server_payload_finished(conn); } } if (req->state < HTTP_SERVER_REQUEST_STATE_PROCESSING && (conn->incoming_payload == NULL || !i_stream_have_bytes_left(conn->incoming_payload))) { /* finished reading request */ req->state = HTTP_SERVER_REQUEST_STATE_PROCESSING; if (req->response != NULL && req->response->submitted) http_server_request_submit_response(req); } if (conn->incoming_payload == NULL) { if (conn->conn.io == NULL && conn->to_input == NULL) http_server_connection_input_resume(conn); return TRUE; } /* Request payload is still being uploaded by the client */ return FALSE; } static int http_server_connection_ssl_init(struct http_server_connection *conn) { const char *error; if (conn->server->set.debug) http_server_connection_debug(conn, "Starting SSL handshake"); if (master_service_ssl_init(master_service, &conn->conn.input, &conn->conn.output, &conn->ssl_iostream, &error) < 0) { http_server_connection_error(conn, "Couldn't initialize SSL server for %s: %s", conn->conn.name, error); return -1; } if (ssl_iostream_handshake(conn->ssl_iostream) < 0) { http_server_connection_error(conn,"SSL handshake failed: %s", ssl_iostream_get_last_error(conn->ssl_iostream)); return -1; } http_server_connection_ready(conn); return 0; } static bool http_server_connection_pipeline_is_full(struct http_server_connection *conn) { return (conn->request_queue_count >= conn->server->set.max_pipelined_requests || conn->server->shutting_down); } static void http_server_connection_pipeline_handle_full( struct http_server_connection *conn) { if (conn->server->shutting_down) { http_server_connection_debug(conn, "Pipeline full (%u requests pending; server shutting down)", conn->request_queue_count); } else { http_server_connection_debug(conn, "Pipeline full (%u requests pending; %u maximum)", conn->request_queue_count, conn->server->set.max_pipelined_requests); } http_server_connection_input_halt(conn); } static bool http_server_connection_check_input(struct http_server_connection *conn) { struct istream *input = conn->conn.input; int stream_errno; if (input == NULL) return FALSE; stream_errno = input->stream_errno; if (input->eof || stream_errno != 0) { /* connection input broken; output may still be intact */ if (stream_errno != 0 && stream_errno != EPIPE && stream_errno != ECONNRESET) { http_server_connection_client_error(conn, "Connection lost: read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); http_server_connection_close(&conn, "Read failure"); } else { http_server_connection_debug(conn, "Connection lost: Remote disconnected"); if (conn->request_queue_head == NULL) { /* no pending requests; close */ http_server_connection_close(&conn, "Remote closed connection"); } else if (conn->request_queue_head->state < HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE) { /* unfinished request; close */ http_server_connection_close(&conn, "Remote closed connection unexpectedly"); } else { /* a request is still processing; only drop input io for now. the other end may only have shutdown one direction */ conn->input_broken = TRUE; http_server_connection_input_halt(conn); } } return FALSE; } return TRUE; } static bool http_server_connection_finish_request(struct http_server_connection *conn) { struct http_server_request *req; enum http_request_parse_error error_code; const char *error; int ret; req = conn->request_queue_tail; if (req != NULL && req->state == HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE) { http_server_connection_debug(conn, "Finish receiving request"); ret = http_request_parse_finish_payload (conn->http_parser, &error_code, &error); if (ret <= 0 && !http_server_connection_check_input(conn)) return FALSE; if (ret < 0) { http_server_connection_ref(conn); http_server_connection_client_error(conn, "Client sent invalid request: %s", error); switch (error_code) { case HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE: conn->input_broken = TRUE; http_server_request_fail_close(req, 413, "Payload Too Large"); break; default: i_unreached(); } http_server_connection_unref(&conn); return FALSE; } if (ret == 0) return FALSE; http_server_request_ready_to_respond(req); } return TRUE; } static void http_server_connection_input(struct connection *_conn) { struct http_server_connection *conn = (struct http_server_connection *)_conn; struct http_server_request *req; enum http_request_parse_error error_code; const char *error; bool cont; int ret; if (conn->server->shutting_down) { if (!http_server_connection_shut_down(conn)) http_server_connection_pipeline_handle_full(conn); return; } i_assert(!conn->in_req_callback); i_assert(!conn->input_broken && conn->incoming_payload == NULL); i_assert(!conn->close_indicated); http_server_connection_timeout_reset(conn); if (conn->ssl && conn->ssl_iostream == NULL) { if (http_server_connection_ssl_init(conn) < 0) { /* ssl failed */ http_server_connection_close(&conn, "SSL Initialization failed"); return; } } if (conn->to_input != NULL) { /* We came here from a timeout added by http_server_payload_destroyed(). The IO couldn't be added back immediately in there, because the HTTP API user may still have had its own IO pointed to the same fd. It should be removed by now, so we can add it back. */ http_server_payload_finished(conn); } /* finish up pending request */ if (!http_server_connection_finish_request(conn)) return; /* create request object if none was created already */ if (conn->request_queue_tail != NULL && conn->request_queue_tail->state == HTTP_SERVER_REQUEST_STATE_NEW) { if (conn->request_queue_count > conn->server->set.max_pipelined_requests) { /* pipeline full */ http_server_connection_pipeline_handle_full(conn); return; } /* continue last unfinished request*/ req = conn->request_queue_tail; } else { if (conn->request_queue_count >= conn->server->set.max_pipelined_requests) { /* pipeline full */ http_server_connection_pipeline_handle_full(conn); return; } /* start new request */ req = http_server_request_new(conn); } /* parse requests */ ret = 1; while (!conn->close_indicated && ret != 0) { http_server_connection_ref(conn); while ((ret = http_request_parse_next (conn->http_parser, req->pool, &req->req, &error_code, &error)) > 0) { conn->stats.request_count++; http_server_connection_debug(conn, "Received new request %s " "(%u requests pending; %u maximum)", http_server_request_label(req), conn->request_queue_count, conn->server->set.max_pipelined_requests); http_server_request_ref(req); i_assert(!req->delay_destroy); req->delay_destroy = TRUE; T_BEGIN { cont = http_server_connection_handle_request(conn, req); } T_END; req->delay_destroy = FALSE; if (!cont) { /* connection closed or request body not read yet. the request may be destroyed now. */ if (req->destroy_pending) http_server_request_destroy(&req); else http_server_request_unref(&req); http_server_connection_unref(&conn); return; } if (req->req.connection_close) conn->close_indicated = TRUE; if (req->destroy_pending) http_server_request_destroy(&req); else http_server_request_unref(&req); if (conn->closed) { /* connection got closed in destroy callback */ break; } if (conn->close_indicated) { /* client indicated it will close after this request; stop trying to read more. */ break; } /* finish up pending request if possible */ if (!http_server_connection_finish_request(conn)) { http_server_connection_unref(&conn); return; } if (http_server_connection_pipeline_is_full(conn)) { /* pipeline full */ http_server_connection_pipeline_handle_full(conn); http_server_connection_unref(&conn); return; } /* start new request */ req = http_server_request_new(conn); } if (http_server_connection_unref_is_closed(conn)) { /* connection got closed */ return; } if (ret <= 0 && !http_server_connection_check_input(conn)) return; if (ret < 0) { http_server_connection_ref(conn); http_server_connection_client_error(conn, "Client sent invalid request: %s", error); switch (error_code) { case HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST: conn->input_broken = TRUE; /* fall through */ case HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST: http_server_request_fail(req, 400, "Bad Request"); break; case HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG: conn->input_broken = TRUE; /* fall through */ case HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED: http_server_request_fail(req, 501, "Not Implemented"); break; case HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG: conn->input_broken = TRUE; http_server_request_fail_close(req, 414, "URI Too Long"); break; case HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED: http_server_request_fail(req, 417, "Expectation Failed"); break; case HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE: conn->input_broken = TRUE; http_server_request_fail_close(req, 413, "Payload Too Large"); break; default: i_unreached(); } if (http_server_connection_unref_is_closed(conn)) { /* connection got closed */ return; } } if (conn->input_broken || conn->close_indicated) { http_server_connection_input_halt(conn); return; } } } static void http_server_connection_discard_input(struct http_server_connection *conn) { struct http_server *server = conn->server; enum http_request_parse_error error_code; const char *error; int ret = 0; i_assert(server->ioloop != NULL); ret = http_request_parse_finish_payload (conn->http_parser, &error_code, &error); if (ret <= 0 && !http_server_connection_check_input(conn)) { io_loop_stop(server->ioloop); return; } if (ret < 0) { http_server_connection_ref(conn); http_server_connection_client_error(conn, "Client sent invalid request: %s", error); switch (error_code) { case HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE: conn->input_broken = TRUE; http_server_request_fail_close(conn->request_queue_head, 413, "Payload Too Large"); break; default: i_unreached(); } http_server_connection_unref(&conn); io_loop_stop(server->ioloop); return; } if (ret > 0) io_loop_stop(server->ioloop); } int http_server_connection_discard_payload( struct http_server_connection *conn) { struct http_server *server = conn->server; struct ioloop *prev_ioloop = current_ioloop; i_assert(conn->conn.io == NULL); i_assert(server->ioloop == NULL); /* destroy payload wrapper early to advance state */ if (conn->incoming_payload != NULL) { i_stream_unref(&conn->incoming_payload); conn->incoming_payload = NULL; } /* finish reading payload from the parser */ if (http_request_parser_pending_payload (conn->http_parser)) { http_server_connection_debug(conn, "Discarding remaining incoming payload"); server->ioloop = io_loop_create(); http_server_connection_switch_ioloop(conn); io_loop_set_running(server->ioloop); conn->conn.io = io_add_istream(conn->conn.input, http_server_connection_discard_input, conn); http_server_connection_discard_input(conn); if (io_loop_is_running(server->ioloop)) io_loop_run(server->ioloop); io_remove(&conn->conn.io); io_loop_set_current(prev_ioloop); http_server_connection_switch_ioloop(conn); io_loop_set_current(server->ioloop); io_loop_destroy(&server->ioloop); } else { http_server_connection_debug(conn, "No remaining incoming payload"); } /* check whether connection is still viable */ http_server_connection_ref(conn); (void)http_server_connection_check_input(conn); return http_server_connection_unref_is_closed(conn) ? -1 : 0; } void http_server_connection_write_failed(struct http_server_connection *conn, const char *error) { if (conn->closed) return; if (error != NULL) { http_server_connection_error(conn, "Connection lost: %s", error); http_server_connection_close(&conn, "Write failure"); } else { http_server_connection_debug(conn, "Connection lost: Remote disconnected"); http_server_connection_close(&conn, "Remote closed connection unexpectedly"); } } static bool http_server_connection_next_response(struct http_server_connection *conn) { struct http_server_request *req; const char *error = NULL; int ret; if (conn->output_locked) return FALSE; req = conn->request_queue_head; if (req == NULL) { /* no requests pending */ http_server_connection_debug(conn, "No more requests pending"); http_server_connection_timeout_start(conn); return FALSE; } if (req->state < HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND) { if (req->state == HTTP_SERVER_REQUEST_STATE_PROCESSING) { /* server is causing idle time */ http_server_connection_debug(conn, "Not ready to respond: Server is processing"); http_server_connection_timeout_stop(conn); } else { /* client is causing idle time */ http_server_connection_debug(conn, "Not ready to respond: Waiting for client"); http_server_connection_timeout_start(conn); } /* send 100 Continue if appropriate */ if (req->state >= HTTP_SERVER_REQUEST_STATE_QUEUED && conn->incoming_payload != NULL && req->response == NULL && req->req.version_minor >= 1 && req->req.expect_100_continue && !req->payload_halted && !req->sent_100_continue) { static const char *response = "HTTP/1.1 100 Continue\r\n\r\n"; struct ostream *output = conn->conn.output; if (o_stream_send(output, response, strlen(response)) < 0) { if (output->stream_errno != EPIPE && output->stream_errno != ECONNRESET) { error = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } http_server_connection_write_failed(conn, error); return FALSE; } http_server_connection_debug(conn, "Sent 100 Continue"); req->sent_100_continue = TRUE; } return FALSE; } i_assert(req->state == HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND && req->response != NULL); http_server_connection_debug(conn, "Sending response"); http_server_connection_timeout_start(conn); http_server_request_ref(req); ret = http_server_response_send(req->response, &error); http_server_request_unref(&req); if (ret < 0) { http_server_connection_write_failed(conn, error); return FALSE; } http_server_connection_timeout_reset(conn); return TRUE; } static int http_server_connection_send_responses( struct http_server_connection *conn) { http_server_connection_ref(conn); /* send more responses until no more responses remain, the output blocks again, or the connection is closed */ while (!conn->closed && http_server_connection_next_response(conn)); if (http_server_connection_unref_is_closed(conn)) return -1; /* accept more requests if possible */ if (conn->incoming_payload == NULL && conn->request_queue_count < conn->server->set.max_pipelined_requests && !conn->server->shutting_down) { http_server_connection_input_resume(conn); return 1; } return 0; } int http_server_connection_flush(struct http_server_connection *conn) { struct ostream *output = conn->conn.output; int ret; if ((ret = o_stream_flush(output)) <= 0) { if (ret < 0) { const char *error = NULL; if (output->stream_errno != EPIPE && output->stream_errno != ECONNRESET) { error = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } http_server_connection_write_failed(conn, error); } return -1; } http_server_connection_timeout_reset(conn); return 0; } int http_server_connection_output(struct http_server_connection *conn) { bool pipeline_was_full = http_server_connection_pipeline_is_full(conn); int ret; if (http_server_connection_flush(conn) < 0) return -1; if (!conn->output_locked) { if (http_server_connection_send_responses(conn) < 0) return -1; } else if (conn->request_queue_head != NULL) { struct http_server_request *req = conn->request_queue_head; struct http_server_response *resp = req->response; const char *error = NULL; http_server_connection_ref(conn); i_assert(resp != NULL); ret = http_server_response_send_more(resp, &error); if (http_server_connection_unref_is_closed(conn)) return -1; if (ret < 0) { http_server_connection_write_failed(conn, error); return -1; } if (!conn->output_locked) { /* room for more responses */ if (http_server_connection_send_responses(conn) < 0) return -1; } else if (conn->io_resp_payload != NULL) { /* server is causing idle time */ http_server_connection_debug(conn, "Not ready to continue response: " "Server is producing response"); http_server_connection_timeout_stop(conn); } else { /* client is causing idle time */ http_server_connection_debug(conn, "Not ready to continue response: " "Waiting for client"); http_server_connection_timeout_start(conn); } } if (conn->server->shutting_down && http_server_connection_shut_down(conn)) return 1; if (!http_server_connection_pipeline_is_full(conn)) { http_server_connection_input_resume(conn); if (pipeline_was_full && conn->conn.io != NULL) i_stream_set_input_pending(conn->conn.input, TRUE); } return 1; } void http_server_connection_trigger_responses( struct http_server_connection *conn) { o_stream_set_flush_pending(conn->conn.output, TRUE); } bool http_server_connection_pending_payload(struct http_server_connection *conn) { return http_request_parser_pending_payload(conn->http_parser); } static struct connection_settings http_server_connection_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs http_server_connection_vfuncs = { .destroy = http_server_connection_destroy, .input = http_server_connection_input }; struct connection_list * http_server_connection_list_init(void) { return connection_list_init (&http_server_connection_set, &http_server_connection_vfuncs); } struct http_server_connection * http_server_connection_create(struct http_server *server, int fd_in, int fd_out, bool ssl, const struct http_server_callbacks *callbacks, void *context) { const struct http_server_settings *set = &server->set; struct http_server_connection *conn; static unsigned int id = 0; struct ip_addr addr; in_port_t port; const char *name; i_assert(!server->shutting_down); conn = i_new(struct http_server_connection, 1); conn->refcount = 1; conn->id = id++; conn->server = server; conn->ssl = ssl; conn->callbacks = callbacks; conn->context = context; net_set_nonblock(fd_in, TRUE); if (fd_in != fd_out) net_set_nonblock(fd_out, TRUE); if (set->socket_send_buffer_size > 0) { if (net_set_send_buffer_size(fd_out, set->socket_send_buffer_size) < 0) i_error("net_set_send_buffer_size(%"PRIuSIZE_T") failed: %m", set->socket_send_buffer_size); } if (set->socket_recv_buffer_size > 0) { if (net_set_recv_buffer_size(fd_in, set->socket_recv_buffer_size) < 0) i_error("net_set_recv_buffer_size(%"PRIuSIZE_T") failed: %m", set->socket_recv_buffer_size); } (void)net_set_tcp_nodelay(fd_out, TRUE); /* get a name for this connection */ if (fd_in != fd_out || net_getpeername(fd_in, &addr, &port) < 0) { name = t_strdup_printf("[%u]", id); } else { if (addr.family == 0) { struct net_unix_cred cred; if (net_getunixcred(fd_in, &cred) < 0) { name = t_strdup_printf("[%u]", id); } else if (cred.pid == (pid_t)-1) { name = t_strdup_printf("unix:uid=%ld [%u]", (long)cred.uid, id); } else { name = t_strdup_printf ("unix:pid=%ld,uid=%ld [%u]", (long)cred.pid, (long)cred.uid, id); } } else if (addr.family == AF_INET6) { name = t_strdup_printf("[%s]:%u [%u]", net_ip2addr(&addr), port, id); } else { name = t_strdup_printf("%s:%u [%u]", net_ip2addr(&addr), port, id); } } connection_init_server (server->conn_list, &conn->conn, name, fd_in, fd_out); if (!ssl) http_server_connection_ready(conn); http_server_connection_timeout_start(conn); http_server_connection_debug(conn, "Connection created"); return conn; } void http_server_connection_ref(struct http_server_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } static void http_server_connection_disconnect(struct http_server_connection *conn, const char *reason) { struct http_server_request *req, *req_next; if (conn->closed) return; if (reason == NULL) reason = "Connection closed"; http_server_connection_debug(conn, "Disconnected: %s", reason); conn->disconnect_reason = i_strdup(reason); conn->closed = TRUE; /* preserve statistics */ http_server_connection_update_stats(conn); if (conn->incoming_payload != NULL) { /* the stream is still accessed by lib-http caller. */ i_stream_remove_destroy_callback(conn->incoming_payload, http_server_payload_destroyed); conn->incoming_payload = NULL; } /* drop all requests before connection is closed */ req = conn->request_queue_head; while (req != NULL) { req_next = req->next; http_server_request_abort(&req, NULL); req = req_next; } if (conn->to_input != NULL) timeout_remove(&conn->to_input); http_server_connection_timeout_stop(conn); if (conn->io_resp_payload != NULL) io_remove(&conn->io_resp_payload); if (conn->conn.output != NULL) { o_stream_nflush(conn->conn.output); o_stream_uncork(conn->conn.output); } if (conn->http_parser != NULL) http_request_parser_deinit(&conn->http_parser); connection_disconnect(&conn->conn); } bool http_server_connection_unref(struct http_server_connection **_conn) { struct http_server_connection *conn = *_conn; i_assert(conn->refcount > 0); *_conn = NULL; if (--conn->refcount > 0) return TRUE; http_server_connection_disconnect(conn, NULL); http_server_connection_debug(conn, "Connection destroy"); if (conn->ssl_iostream != NULL) ssl_iostream_unref(&conn->ssl_iostream); connection_deinit(&conn->conn); if (conn->callbacks != NULL && conn->callbacks->connection_destroy != NULL) T_BEGIN { conn->callbacks->connection_destroy (conn->context, conn->disconnect_reason); } T_END; i_free(conn->disconnect_reason); i_free(conn); return FALSE; } static bool http_server_connection_unref_is_closed(struct http_server_connection *conn) { bool closed = conn->closed; if (!http_server_connection_unref(&conn)) closed = TRUE; return closed; } void http_server_connection_close(struct http_server_connection **_conn, const char *reason) { struct http_server_connection *conn = *_conn; http_server_connection_disconnect(conn, reason); http_server_connection_unref(_conn); } void http_server_connection_tunnel(struct http_server_connection **_conn, http_server_tunnel_callback_t callback, void *context) { struct http_server_connection *conn = *_conn; struct http_server_tunnel tunnel; /* preserve statistics */ http_server_connection_update_stats(conn); i_zero(&tunnel); tunnel.input = conn->conn.input; tunnel.output = conn->conn.output; tunnel.fd_in = conn->conn.fd_in; tunnel.fd_out = conn->conn.fd_out; conn->conn.input = NULL; conn->conn.output = NULL; conn->conn.fd_in = conn->conn.fd_out = -1; http_server_connection_close(_conn, "Tunnel initiated"); callback(context, &tunnel); } void http_server_connection_switch_ioloop(struct http_server_connection *conn) { if (conn->switching_ioloop) return; conn->switching_ioloop = TRUE; if (conn->to_input != NULL) conn->to_input = io_loop_move_timeout(&conn->to_input); if (conn->to_idle != NULL) conn->to_idle = io_loop_move_timeout(&conn->to_idle); if (conn->io_resp_payload != NULL) conn->io_resp_payload = io_loop_move_io(&conn->io_resp_payload); if (conn->incoming_payload != NULL) i_stream_switch_ioloop(conn->incoming_payload); connection_switch_ioloop(&conn->conn); conn->switching_ioloop = FALSE; } dovecot-2.2.33.2/src/lib-http/http-request.c0000644000175000017500000000125613123174404015453 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "http-request.h" bool http_request_has_connection_option(const struct http_request *req, const char *option) { const char *const *opt_idx; if (!array_is_created(&req->connection_options)) return FALSE; array_foreach(&req->connection_options, opt_idx) { if (strcasecmp(*opt_idx, option) == 0) return TRUE; } return FALSE; } int http_request_get_payload_size(const struct http_request *req, uoff_t *size_r) { if (req->payload == NULL) { *size_r = 0; return 1; } return i_stream_get_size(req->payload, TRUE, size_r); } dovecot-2.2.33.2/src/lib-http/http-response.c0000644000175000017500000000170213123174404015615 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "http-response.h" void http_response_init(struct http_response *resp, unsigned int status, const char *reason) { i_zero(resp); resp->version_major = 1; resp->version_minor = 1; resp->date = ioloop_time; resp->status = status; resp->reason = reason; } bool http_response_has_connection_option(const struct http_response *resp, const char *option) { const char *const *opt_idx; if (!array_is_created(&resp->connection_options)) return FALSE; array_foreach(&resp->connection_options, opt_idx) { if (strcasecmp(*opt_idx, option) == 0) return TRUE; } return FALSE; } int http_response_get_payload_size(const struct http_response *resp, uoff_t *size_r) { if (resp->payload == NULL) { *size_r = 0; return 1; } return i_stream_get_size(resp->payload, TRUE, size_r); } dovecot-2.2.33.2/src/lib-http/http-response-parser.h0000644000175000017500000000133013147010711017105 00000000000000#ifndef HTTP_RESPONSE_PARSER_H #define HTTP_RESPONSE_PARSER_H #include "http-response.h" struct http_header_limits; struct http_response_parser; enum http_response_parse_flags { /* Strictly adhere to the HTTP protocol specification */ HTTP_RESPONSE_PARSE_FLAG_STRICT = BIT(0) }; struct http_response_parser * http_response_parser_init(struct istream *input, const struct http_header_limits *hdr_limits, enum http_response_parse_flags flags) ATTR_NULL(2); void http_response_parser_deinit(struct http_response_parser **_parser); int http_response_parse_next(struct http_response_parser *parser, enum http_response_payload_type payload_type, struct http_response *response, const char **error_r); #endif dovecot-2.2.33.2/src/lib-http/http-message-parser.c0000644000175000017500000004155413165463624016721 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "iostream.h" #include "istream-sized.h" #include "http-parser.h" #include "http-header.h" #include "http-header-parser.h" #include "http-date.h" #include "http-transfer.h" #include "http-message-parser.h" #include void http_message_parser_init(struct http_message_parser *parser, struct istream *input, const struct http_header_limits *hdr_limits, uoff_t max_payload_size, enum http_message_parse_flags flags) { i_zero(parser); parser->input = input; i_stream_ref(parser->input); if (hdr_limits != NULL) parser->header_limits = *hdr_limits; parser->max_payload_size = max_payload_size; parser->flags = flags; } void http_message_parser_deinit(struct http_message_parser *parser) { if (parser->header_parser != NULL) http_header_parser_deinit(&parser->header_parser); if (parser->msg.pool != NULL) pool_unref(&parser->msg.pool); if (parser->payload != NULL) i_stream_unref(&parser->payload); if (parser->input != NULL) i_stream_unref(&parser->input); } void http_message_parser_restart(struct http_message_parser *parser, pool_t pool) { i_assert(parser->payload == NULL); if (parser->header_parser == NULL) { enum http_header_parse_flags hdr_flags = 0; if ((parser->flags & HTTP_MESSAGE_PARSE_FLAG_STRICT) != 0) hdr_flags |= HTTP_HEADER_PARSE_FLAG_STRICT; parser->header_parser = http_header_parser_init (parser->input, &parser->header_limits, hdr_flags); } else { http_header_parser_reset(parser->header_parser); } if (parser->msg.pool != NULL) pool_unref(&parser->msg.pool); i_zero(&parser->msg); if (pool != NULL) { parser->msg.pool = pool; pool_ref(pool); } parser->msg.date = (time_t)-1; } pool_t http_message_parser_get_pool(struct http_message_parser *parser) { if (parser->msg.pool == NULL) parser->msg.pool = pool_alloconly_create("http_message", 4096); return parser->msg.pool; } int http_message_parse_version(struct http_message_parser *parser) { const unsigned char *p = parser->cur; const size_t size = parser->end - parser->cur; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; parser->error = NULL; /* RFC 7230, Section 2.6: Protocol Versioning HTTP-version = HTTP-name "/" DIGIT "." DIGIT HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive */ if (size < 8) return 0; if (memcmp(p, "HTTP/", 5) != 0 || !i_isdigit(p[5]) || p[6] != '.' || !i_isdigit(p[7])) { parser->error = "Bad HTTP version"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } parser->msg.version_major = p[5] - '0'; parser->msg.version_minor = p[7] - '0'; parser->cur += 8; return 1; } int http_message_parse_finish_payload(struct http_message_parser *parser) { const unsigned char *data; size_t size; int ret; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; parser->error = NULL; if (parser->payload == NULL) return 1; while ((ret = i_stream_read_data(parser->payload, &data, &size, 0)) > 0) i_stream_skip(parser->payload, size); if (ret == 0 || parser->payload->stream_errno != 0) { if (ret < 0) { if (parser->payload->stream_errno == EMSGSIZE) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE; parser->error = "Payload is too large"; } else if (parser->payload->stream_errno == EIO) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; parser->error = "Invalid payload"; } else { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM; parser->error = "Stream error while skipping payload"; } } return ret; } i_stream_destroy(&parser->payload); return 1; } static int http_message_parse_header(struct http_message_parser *parser, const char *name, const unsigned char *data, size_t size) { const struct http_header_field *hdr; struct http_parser hparser; pool_t pool; pool = http_message_parser_get_pool(parser); if (parser->msg.header == NULL) parser->msg.header = http_header_create(pool, 32); hdr = http_header_field_add(parser->msg.header, name, data, size); /* RFC 7230, Section 3.2.2: Field Order A sender MUST NOT generate multiple header fields with the same field name in a message unless either the entire field value for that header field is defined as a comma-separated list [i.e., #(values)] or the header field is a well-known exception. */ switch (name[0]) { case 'C': case 'c': /* Connection: */ if (strcasecmp(name, "Connection") == 0) { const char **opt_idx; const char *option; unsigned int num_tokens = 0; /* RFC 7230, Section 6.1: Connection Connection = 1#connection-option connection-option = token */ /* Multiple Connection headers are allowed and combined into one */ http_parser_init(&hparser, data, size); for (;;) { if (http_parse_token_list_next(&hparser, &option) <= 0) break; num_tokens++; if (strcasecmp(option, "close") == 0) parser->msg.connection_close = TRUE; if (!array_is_created(&parser->msg.connection_options)) p_array_init(&parser->msg.connection_options, pool, 4); opt_idx = array_append_space(&parser->msg.connection_options); *opt_idx = p_strdup(pool, option); } if (hparser.cur < hparser.end || num_tokens == 0) { parser->error = "Invalid Connection header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } return 0; } /* Content-Length: */ if (strcasecmp(name, "Content-Length") == 0) { if (parser->msg.have_content_length) { /* There is no acceptable way to allow duplicates for this header. */ parser->error = "Duplicate Content-Length header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } /* RFC 7230, Section 3.3.2: Content-Length Content-Length = 1*DIGIT */ if (str_to_uoff(hdr->value, &parser->msg.content_length) < 0) { parser->error= "Invalid Content-Length header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } parser->msg.have_content_length = TRUE; return 0; } break; case 'D': case 'd': /* Date: */ if (strcasecmp(name, "Date") == 0) { if (parser->msg.date != (time_t)-1) { if ((parser->flags & HTTP_MESSAGE_PARSE_FLAG_STRICT) != 0) { parser->error = "Duplicate Date header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } /* Allow the duplicate; last instance is used */ } /* RFC 7231, Section 7.1.1.2: Date Date = HTTP-date */ if (!http_date_parse(data, size, &parser->msg.date) && (parser->flags & HTTP_MESSAGE_PARSE_FLAG_STRICT) != 0) { parser->error = "Invalid Date header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } return 0; } break; case 'L': case 'l': /* Location: */ if (strcasecmp(name, "Location") == 0) { /* RFC 7231, Section 7.1.2: Location Location = URI-reference -> not parsed here */ /* FIXME: move this to response parser */ parser->msg.location = hdr->value; return 0; } break; case 'T': case 't': /* Transfer-Encoding: */ if (strcasecmp(name, "Transfer-Encoding") == 0) { const char *trenc = NULL; /* Multiple Transfer-Encoding headers are allowed and combined into one */ if (!array_is_created(&parser->msg.transfer_encoding)) p_array_init(&parser->msg.transfer_encoding, pool, 4); /* RFC 7230, Section 3.3.1: Transfer-Encoding Transfer-Encoding = 1#transfer-coding RFC 7230, Section 4: Transfer Codings transfer-coding = "chunked" ; RFC 7230, Section 4.1 / "compress" ; RFC 7230, Section 4.2.1 / "deflate" ; RFC 7230, Section 4.2.2 / "gzip" ; RFC 7230, Section 4.2.3 / transfer-extension transfer-extension = token *( OWS ";" OWS transfer-parameter ) transfer-parameter = token BWS "=" BWS ( token / quoted-string ) */ http_parser_init(&hparser, data, size); for (;;) { /* transfer-coding */ if (http_parse_token(&hparser, &trenc) > 0) { struct http_transfer_coding *coding; bool parse_error; coding = array_append_space(&parser->msg.transfer_encoding); coding->name = p_strdup(pool, trenc); /* *( OWS ";" OWS transfer-parameter ) */ parse_error = FALSE; for (;;) { struct http_transfer_param *param; const char *attribute, *value; /* OWS ";" OWS */ http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != ';') break; hparser.cur++; http_parse_ows(&hparser); /* attribute */ if (http_parse_token(&hparser, &attribute) <= 0) { parse_error = TRUE; break; } /* BWS "=" BWS */ http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != '=') { parse_error = TRUE; break; } hparser.cur++; http_parse_ows(&hparser); /* token / quoted-string */ if (http_parse_token_or_qstring(&hparser, &value) <= 0) { parse_error = TRUE; break; } if (!array_is_created(&coding->parameters)) p_array_init(&coding->parameters, pool, 2); param = array_append_space(&coding->parameters); param->attribute = p_strdup(pool, attribute); param->value = p_strdup(pool, value); } if (parse_error) break; } else { /* RFC 7230, Section 7: ABNF List Extension: #rule For compatibility with legacy list rules, a recipient MUST parse and ignore a reasonable number of empty list elements: enough to handle common mistakes by senders that merge values, but not so much that they could be used as a denial-of-service mechanism. */ // FIXME: limit allowed number of empty list elements // FIXME: handle invalid transfer encoding } http_parse_ows(&hparser); if (hparser.cur >= hparser.end || *hparser.cur != ',') break; hparser.cur++; http_parse_ows(&hparser); } if (hparser.cur < hparser.end || array_count(&parser->msg.transfer_encoding) == 0) { parser->error = "Invalid Transfer-Encoding header"; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } return 0; } break; default: break; } return 0; } int http_message_parse_headers(struct http_message_parser *parser) { const unsigned char *field_data; struct http_message *msg = &parser->msg; const char *field_name, *error; size_t field_size; int ret; parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; parser->error = NULL; /* *( header-field CRLF ) CRLF */ while ((ret=http_header_parse_next_field(parser->header_parser, &field_name, &field_data, &field_size, &error)) > 0) { if (field_name == NULL) { pool_t pool; /* EOH */ /* Create empty header if there is none */ pool = http_message_parser_get_pool(parser); if (parser->msg.header == NULL) parser->msg.header = http_header_create(pool, 1); /* handle HTTP/1.0 persistence */ if (msg->version_major == 1 && msg->version_minor == 0 && !msg->connection_close) { const char *const *option; msg->connection_close = TRUE; if (array_is_created(&parser->msg.connection_options)) { array_foreach(&msg->connection_options, option) { if (strcasecmp(*option, "Keep-Alive") == 0) { msg->connection_close = FALSE; break; } } } } return 1; } if (http_message_parse_header(parser, field_name, field_data, field_size) < 0) return -1; } if (ret < 0) { if (parser->input->eof || parser->input->stream_errno != 0) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM; parser->error = "Broken stream"; } else { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; parser->error = t_strdup_printf("Failed to parse header: %s", error); } } return ret; } static const char * http_istream_error_callback(const struct istream_sized_error_data *data, struct istream *input) { i_assert(data->eof); i_assert(data->v_offset + data->new_bytes < data->wanted_size); return t_strdup_printf("Disconnected while reading response payload at offset %"PRIuUOFF_T " (wanted %"PRIuUOFF_T"): %s", data->v_offset + data->new_bytes, data->wanted_size, io_stream_get_disconnect_reason(input, NULL)); } int http_message_parse_body(struct http_message_parser *parser, bool request) { struct istream *input; i_assert(parser->payload == NULL); parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; parser->error = NULL; if (array_is_created(&parser->msg.transfer_encoding)) { const struct http_transfer_coding *coding; bool chunked_last = FALSE; array_foreach(&parser->msg.transfer_encoding, coding) { if (strcasecmp(coding->name, "chunked") == 0) { chunked_last = TRUE; if ((parser->error_code == HTTP_MESSAGE_PARSE_ERROR_NONE) && array_is_created(&coding->parameters) && array_count(&coding->parameters) > 0) { const struct http_transfer_param *param = array_idx(&coding->parameters, 0); parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE; parser->error = t_strdup_printf( "Unexpected parameter `%s' specified" "for the `%s' transfer coding", param->attribute, coding->name); /* recoverable */ } } else if (chunked_last) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; parser->error = "Chunked Transfer-Encoding must be last"; return -1; } else if (parser->error_code == HTTP_MESSAGE_PARSE_ERROR_NONE) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED; parser->error = t_strdup_printf( "Unknown transfer coding `%s'", coding->name); /* recoverable */ } } if (chunked_last) { parser->payload = http_transfer_chunked_istream_create (parser->input, parser->max_payload_size); } else if (!request) { /* RFC 7230, Section 3.3.3: Message Body Length If a Transfer-Encoding header field is present in a response and the chunked transfer coding is not the final encoding, the message body length is determined by reading the connection until it is closed by the server. */ /* FIXME: enforce max payload size (relevant to http-client only) */ parser->payload = i_stream_create_limit(parser->input, (size_t)-1); } else { /* RFC 7230, Section 3.3.3: Message Body Length If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the final encoding, the message body length cannot be determined reliably; the server MUST respond with the 400 (Bad Request) status code and then close the connection. */ parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; parser->error = "Final Transfer-Encoding in request is not chunked"; return -1; } /* RFC 7230, Section 3.3.3: Message Body Length If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 9.5 of [RFC7230]) or response splitting (Section 9.4 of [RFC7230]) and ought to be handled as an error. A sender MUST remove the received Content-Length field prior to forwarding such a message downstream. */ // FIXME: make this an error? if (parser->msg.have_content_length) http_header_field_delete(parser->msg.header, "Content-Length"); } else if (parser->msg.content_length > 0) { if (parser->max_payload_size > 0 && parser->msg.content_length > parser->max_payload_size) { parser->error_code = HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE; parser->error = "Payload is too large"; return -1; } /* Got explicit message size from Content-Length: header */ input = i_stream_create_limit(parser->input, parser->msg.content_length); /* Make sure we return failure if HTTP connection closes before we've finished reading the full input. */ parser->payload = i_stream_create_sized_with_callback(input, parser->msg.content_length, http_istream_error_callback, input); i_stream_unref(&input); } else if (!parser->msg.have_content_length && !request) { /* RFC 7230, Section 3.3.3: Message Body Length 6. If this is a request message and none of the above are true, then the message body length is zero (no message body is present). 7. Otherwise, this is a response message without a declared message body length, so the message body length is determined by the number of octets received prior to the server closing the connection */ // FIXME: enforce max payload size (relevant to http-client only) // FIXME: handle request case correctly. parser->payload = i_stream_create_limit(parser->input, (size_t)-1); } if (parser->error_code != HTTP_MESSAGE_PARSE_ERROR_NONE) return -1; return 0; } dovecot-2.2.33.2/src/lib-http/http-client-queue.c0000644000175000017500000007121413165463624016377 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "bsearch-insert-pos.h" #include "llist.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "dns-lookup.h" #include "http-response-parser.h" #include "http-client-private.h" #define TIMEOUT_CMP_MARGIN_USECS 2000 static void http_client_queue_fail(struct http_client_queue *queue, unsigned int status, const char *error); static void http_client_queue_set_delay_timer(struct http_client_queue *queue, struct timeval time); static void http_client_queue_set_request_timer(struct http_client_queue *queue, const struct timeval *time); /* * Logging */ static inline void http_client_queue_debug(struct http_client_queue *queue, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_queue_debug(struct http_client_queue *queue, const char *format, ...) { va_list args; if (queue->client->set.debug) { va_start(args, format); i_debug("http-client: queue %s: %s", queue->name, t_strdup_vprintf(format, args)); va_end(args); } } /* * Queue object */ static struct http_client_queue * http_client_queue_find(struct http_client_host *host, const struct http_client_peer_addr *addr) { struct http_client_queue *const *queue_idx; array_foreach_modifiable(&host->queues, queue_idx) { struct http_client_queue *queue = *queue_idx; if (http_client_peer_addr_cmp(&queue->addr, addr) == 0) return queue; } return NULL; } struct http_client_queue * http_client_queue_create(struct http_client_host *host, const struct http_client_peer_addr *addr) { struct http_client_queue *queue; queue = http_client_queue_find(host, addr); if (queue == NULL) { queue = i_new(struct http_client_queue, 1); queue->client = host->client; queue->host = host; queue->addr = *addr; switch (addr->type) { case HTTP_CLIENT_PEER_ADDR_RAW: queue->name = i_strdup_printf("raw://%s:%u", host->name, addr->a.tcp.port); queue->addr.a.tcp.https_name = NULL; break; case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: case HTTP_CLIENT_PEER_ADDR_HTTPS: queue->name = i_strdup_printf("https://%s:%u", host->name, addr->a.tcp.port); queue->addr_name = i_strdup(addr->a.tcp.https_name); queue->addr.a.tcp.https_name = queue->addr_name; break; case HTTP_CLIENT_PEER_ADDR_HTTP: queue->name = i_strdup_printf("http://%s:%u", host->name, addr->a.tcp.port); queue->addr.a.tcp.https_name = NULL; break; case HTTP_CLIENT_PEER_ADDR_UNIX: queue->name = i_strdup_printf("unix:%s", addr->a.un.path); queue->addr_name = i_strdup(addr->a.un.path); queue->addr.a.un.path = queue->addr_name; break; default: i_unreached(); } queue->ips_connect_idx = 0; i_array_init(&queue->pending_peers, 8); i_array_init(&queue->requests, 16); i_array_init(&queue->queued_requests, 16); i_array_init(&queue->queued_urgent_requests, 16); i_array_init(&queue->delayed_requests, 4); array_append(&host->queues, &queue, 1); } return queue; } void http_client_queue_free(struct http_client_queue *queue) { struct http_client_peer *const *peer_idx; ARRAY_TYPE(http_client_peer) peers; http_client_queue_debug(queue, "Destroy"); /* unlink all peers */ if (queue->cur_peer != NULL) { struct http_client_peer *peer = queue->cur_peer; queue->cur_peer = NULL; http_client_peer_unlink_queue(peer, queue); } t_array_init(&peers, array_count(&queue->pending_peers)); array_copy(&peers.arr, 0, &queue->pending_peers.arr, 0, array_count(&queue->pending_peers)); array_foreach(&peers, peer_idx) http_client_peer_unlink_queue(*peer_idx, queue); array_free(&queue->pending_peers); /* abort all requests */ http_client_queue_fail (queue, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborted"); array_free(&queue->requests); array_free(&queue->queued_requests); array_free(&queue->queued_urgent_requests); array_free(&queue->delayed_requests); /* cancel timeouts */ if (queue->to_connect != NULL) timeout_remove(&queue->to_connect); if (queue->to_delayed != NULL) timeout_remove(&queue->to_delayed); /* free */ i_free(queue->addr_name); i_free(queue->name); i_free(queue); } /* * Error handling */ static void http_client_queue_fail_full(struct http_client_queue *queue, unsigned int status, const char *error, bool queued_only) { ARRAY_TYPE(http_client_request) *req_arr, treqs; struct http_client_request **req_idx; unsigned int retained = 0; /* abort requests */ req_arr = &queue->requests; t_array_init(&treqs, array_count(req_arr)); array_copy(&treqs.arr, 0, &req_arr->arr, 0, array_count(req_arr)); array_foreach_modifiable(&treqs, req_idx) { struct http_client_request *req = *req_idx; i_assert(req->state >= HTTP_REQUEST_STATE_QUEUED); if (queued_only && req->state != HTTP_REQUEST_STATE_QUEUED) retained++; else http_client_request_error(&req, status, error); } /* all queues should be empty now... unless new requests were submitted from the callback. this invariant captures it all: */ i_assert((retained + array_count(&queue->delayed_requests) + array_count(&queue->queued_requests) + array_count(&queue->queued_urgent_requests)) == array_count(&queue->requests)); } static void http_client_queue_fail(struct http_client_queue *queue, unsigned int status, const char *error) { http_client_queue_fail_full(queue, status, error, FALSE); } /* * Connection management */ static bool http_client_queue_is_last_connect_ip(struct http_client_queue *queue) { const struct http_client_settings *set = &queue->client->set; struct http_client_host *host = queue->host; i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX); i_assert(queue->ips_connect_idx < host->ips_count); i_assert(queue->ips_connect_start_idx < host->ips_count); /* if a maximum connect attempts > 1 is set, enforce it directly */ if (set->max_connect_attempts > 1 && queue->connect_attempts >= set->max_connect_attempts) return TRUE; /* otherwise, we'll always go through all the IPs. we don't necessarily start connecting from the first IP, so we'll need to treat the IPs as a ring buffer where we automatically wrap back to the first IP when necessary. */ return (queue->ips_connect_idx + 1) % host->ips_count == queue->ips_connect_start_idx; } static void http_client_queue_recover_from_lookup(struct http_client_queue *queue) { struct http_client_host *host = queue->host; struct http_client_peer_addr new_addr = queue->addr; unsigned int i; i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX); if (queue->cur_peer == NULL) { queue->ips_connect_idx = queue->ips_connect_start_idx = 0; return; } /* try to find current peer amongst new IPs */ for (i = 0; i < host->ips_count; i++) { new_addr.a.tcp.ip = host->ips[i]; if (http_client_peer_addr_cmp (&new_addr, &queue->cur_peer->addr) == 0) break; } if (i < host->ips_count) { /* continue with current peer */ queue->ips_connect_idx = queue->ips_connect_start_idx = i; } else { /* reset connect attempts */ queue->ips_connect_idx = queue->ips_connect_start_idx = 0; } } static void http_client_queue_soft_connect_timeout(struct http_client_queue *queue) { struct http_client_host *host = queue->host; const struct http_client_peer_addr *addr = &queue->addr; const char *https_name; i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX); if (queue->to_connect != NULL) timeout_remove(&queue->to_connect); if (http_client_queue_is_last_connect_ip(queue)) { /* no more IPs to try */ return; } /* if our our previous connection attempt takes longer than the soft_connect_timeout, we start a connection attempt to the next IP in parallel */ https_name = http_client_peer_addr_get_https_name(addr); http_client_queue_debug(queue, "Connection to %s%s is taking a long time; " "starting parallel connection attempt to next IP", http_client_peer_addr2str(addr), (https_name == NULL ? "" : t_strdup_printf(" (SSL=%s)", https_name))); /* next IP */ queue->ips_connect_idx = (queue->ips_connect_idx + 1) % host->ips_count; /* setup connection to new peer (can start new soft timeout) */ http_client_queue_connection_setup(queue); } static struct http_client_peer * http_client_queue_connection_attempt(struct http_client_queue *queue) { struct http_client_host *host = queue->host; struct http_client_peer *peer; struct http_client_peer_addr *addr = &queue->addr; unsigned int num_requests = array_count(&queue->queued_requests) + array_count(&queue->queued_urgent_requests); const char *ssl = ""; int ret; if (num_requests == 0) return NULL; /* check whether host IPs are still up-to-date */ if ((ret=http_client_host_refresh(host)) < 0) { /* performing asynchronous lookup */ if (queue->to_connect != NULL) timeout_remove(&queue->to_connect); return NULL; } if (ret > 0) { /* new lookup performed */ http_client_queue_recover_from_lookup(queue); } /* update our peer address */ if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) { i_assert(queue->ips_connect_idx < host->ips_count); queue->addr.a.tcp.ip = host->ips[queue->ips_connect_idx]; ssl = http_client_peer_addr_get_https_name(addr); ssl = (ssl == NULL ? "" : t_strdup_printf(" (SSL=%s)", ssl)); } /* already got a peer? */ peer = NULL; if (queue->cur_peer != NULL) { i_assert(array_count(&queue->pending_peers) == 0); /* is it still the one we want? */ if (http_client_peer_addr_cmp(addr, &queue->cur_peer->addr) == 0) { /* is it still connected? */ if (http_client_peer_is_connected(queue->cur_peer)) { /* yes */ http_client_queue_debug(queue, "Using existing connection to %s%s " "(%u requests pending)", http_client_peer_addr2str(addr), ssl, num_requests); /* handle requests; */ http_client_peer_trigger_request_handler(queue->cur_peer); return queue->cur_peer; } /* no */ peer = queue->cur_peer; } else { /* peer is not relevant to this queue anymore */ http_client_peer_unlink_queue(queue->cur_peer, queue); } queue->cur_peer = NULL; } if (peer == NULL) peer = http_client_peer_get(queue->client, addr); http_client_queue_debug(queue, "Setting up connection to %s%s " "(%u requests pending)", http_client_peer_addr2str(addr), ssl, num_requests); /* create provisional link between queue and peer */ http_client_peer_link_queue(peer, queue); /* handle requests; creates new connections when needed/possible */ http_client_peer_trigger_request_handler(peer); if (http_client_peer_is_connected(peer)) { /* drop any pending peers */ if (array_count(&queue->pending_peers) > 0) { struct http_client_peer *const *peer_idx; array_foreach(&queue->pending_peers, peer_idx) { i_assert(http_client_peer_addr_cmp(&(*peer_idx)->addr, addr) != 0); http_client_peer_unlink_queue(*peer_idx, queue); } array_clear(&queue->pending_peers); } queue->cur_peer = peer; } else { struct http_client_peer *const *peer_idx; unsigned int msecs; bool new_peer = TRUE; /* not already connected, wait for connections */ /* we may be waiting for this peer already */ array_foreach(&queue->pending_peers, peer_idx) { if (http_client_peer_addr_cmp(&(*peer_idx)->addr, addr) == 0) { i_assert(*peer_idx == peer); new_peer = FALSE; break; } } if (new_peer) { http_client_queue_debug(queue, "Started new connection to %s%s", http_client_peer_addr2str(addr), ssl); array_append(&queue->pending_peers, &peer, 1); if (queue->connect_attempts++ == 0) queue->first_connect_time = ioloop_timeval; } /* start soft connect time-out (but only if we have another IP left) */ if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) { msecs = host->client->set.soft_connect_timeout_msecs; if (!http_client_queue_is_last_connect_ip(queue) && msecs > 0 && queue->to_connect == NULL) { queue->to_connect = timeout_add(msecs, http_client_queue_soft_connect_timeout, queue); } } } return peer; } void http_client_queue_connection_setup(struct http_client_queue *queue) { (void)http_client_queue_connection_attempt(queue); } unsigned int http_client_queue_host_lookup_done(struct http_client_queue *queue) { unsigned int reqs_pending = http_client_queue_requests_pending(queue, NULL); http_client_queue_recover_from_lookup(queue); if (reqs_pending > 0) http_client_queue_connection_setup(queue); return reqs_pending; } void http_client_queue_host_lookup_failure( struct http_client_queue *queue, const char *error) { http_client_queue_fail_full(queue, HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED, error, TRUE); } void http_client_queue_connection_success(struct http_client_queue *queue, const struct http_client_peer_addr *addr) { if (queue->host->dns_lookup == NULL && queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) { /* we achieved at least one connection the the addr->ip */ if (!http_client_host_get_ip_idx(queue->host, &addr->a.tcp.ip, &queue->ips_connect_start_idx)) { /* list of IPs changed during connect */ queue->ips_connect_start_idx = 0; } } /* reset attempt counter */ queue->connect_attempts = 0; /* stop soft connect time-out */ if (queue->to_connect != NULL) timeout_remove(&queue->to_connect); /* drop all other attempts to the hport. note that we get here whenever a connection is successfully created, so pending_peers array may be empty. */ if (array_count(&queue->pending_peers) > 0) { struct http_client_peer *const *peer_idx; array_foreach(&queue->pending_peers, peer_idx) { if (http_client_peer_addr_cmp(&(*peer_idx)->addr, addr) == 0) { /* don't drop any connections to the successfully connected peer, even if some of the connections are pending. they may be intended for urgent requests. */ i_assert(queue->cur_peer == NULL); queue->cur_peer = *peer_idx; continue; } /* unlink this queue from the peer; if this was the last/only queue, the peer will be freed, closing all connections. */ http_client_peer_unlink_queue(*peer_idx, queue); } array_clear(&queue->pending_peers); i_assert(queue->cur_peer != NULL); } } void http_client_queue_connection_failure(struct http_client_queue *queue, const struct http_client_peer_addr *addr, const char *reason) { const struct http_client_settings *set = &queue->client->set; const char *https_name = http_client_peer_addr_get_https_name(addr); struct http_client_host *host = queue->host; struct http_client_peer *failed_peer; struct http_client_peer *const *peer_idx; http_client_queue_debug(queue, "Failed to set up connection to %s%s: %s " "(%u peers pending, %u requests pending)", http_client_peer_addr2str(addr), (https_name == NULL ? "" : t_strdup_printf(" (SSL=%s)", https_name)), reason, array_count(&queue->pending_peers), array_count(&queue->requests)); if (queue->cur_peer != NULL) { if (http_client_peer_is_connected(queue->cur_peer)) { /* The peer still has some working connections, which means that pending requests wait until they're picked up by those connections or the remaining connections fail as well. In the latter case, connecting to different peer can resolve the situation, but only if there is more than one IP. In any other case, the requests will eventually fail. In the future we could start connections to the next IP at this point already, but that is no small change. */ i_assert(array_count(&queue->pending_peers) == 0); return; } failed_peer = queue->cur_peer; http_client_peer_unlink_queue(queue->cur_peer, queue); queue->cur_peer = NULL; } else { /* we're still doing the initial connections to this hport. if we're also doing parallel connections with soft timeouts (pending_peer_count>1), wait for them to finish first. */ failed_peer = NULL; array_foreach(&queue->pending_peers, peer_idx) { if (http_client_peer_addr_cmp(&(*peer_idx)->addr, addr) == 0) { failed_peer = *peer_idx; array_delete(&queue->pending_peers, array_foreach_idx(&queue->pending_peers, peer_idx), 1); break; } } i_assert(failed_peer != NULL); if (array_count(&queue->pending_peers) > 0) { http_client_queue_debug(queue, "Waiting for remaining pending peers."); http_client_peer_unlink_queue(failed_peer, queue); return; } } /* one of the connections failed. if we're not using soft timeouts, we need to try to connect to the next IP. if we are using soft timeouts, we've already tried all of the IPs by now. */ if (queue->to_connect != NULL) timeout_remove(&queue->to_connect); if (queue->addr.type == HTTP_CLIENT_PEER_ADDR_UNIX) { http_client_queue_fail(queue, HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason); return; } if (http_client_queue_is_last_connect_ip(queue)) { /* all IPs failed, but retry all of them again if we have more connect attempts left or on the next request. */ queue->ips_connect_idx = queue->ips_connect_start_idx = (queue->ips_connect_idx + 1) % host->ips_count; if (set->max_connect_attempts == 0 || queue->connect_attempts >= set->max_connect_attempts) { http_client_queue_debug(queue, "Failed to set up any connection; failing all queued requests"); if (queue->connect_attempts > 1) { unsigned int total_msecs = timeval_diff_msecs(&ioloop_timeval, &queue->first_connect_time); reason = t_strdup_printf("%s (%u attempts in %u.%03u secs)", reason, queue->connect_attempts, total_msecs/1000, total_msecs%1000); } queue->connect_attempts = 0; http_client_peer_unlink_queue(failed_peer, queue); http_client_queue_fail(queue, HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason); return; } } else { queue->ips_connect_idx = (queue->ips_connect_idx + 1) % host->ips_count; } if (http_client_queue_connection_attempt(queue) != failed_peer) http_client_peer_unlink_queue(failed_peer, queue); return; } void http_client_queue_peer_disconnected(struct http_client_queue *queue, struct http_client_peer *peer) { struct http_client_peer *const *peer_idx; if (queue->cur_peer == peer) { queue->cur_peer = NULL; return; } array_foreach(&queue->pending_peers, peer_idx) { if (*peer_idx == peer) { array_delete(&queue->pending_peers, array_foreach_idx(&queue->pending_peers, peer_idx), 1); break; } } } /* * Main request queue */ void http_client_queue_drop_request(struct http_client_queue *queue, struct http_client_request *req) { struct http_client_request **reqs; unsigned int count, i; http_client_queue_debug(queue, "Dropping request %s", http_client_request_label(req)); /* drop from queue */ if (req->urgent) { reqs = array_get_modifiable(&queue->queued_urgent_requests, &count); for (i = 0; i < count; i++) { if (reqs[i] == req) { array_delete(&queue->queued_urgent_requests, i, 1); break; } } } else { reqs = array_get_modifiable(&queue->queued_requests, &count); for (i = 0; i < count; i++) { if (reqs[i] == req) { array_delete(&queue->queued_requests, i, 1); break; } } } /* drop from delay queue */ if (req->release_time.tv_sec > 0) { reqs = array_get_modifiable(&queue->delayed_requests, &count); for (i = 0; i < count; i++) { if (reqs[i] == req) break; } if (i < count) { if (i == 0) { if (queue->to_delayed != NULL) { timeout_remove(&queue->to_delayed); if (count > 1) { i_assert(reqs[1]->release_time.tv_sec > 0); http_client_queue_set_request_timer(queue, &reqs[1]->release_time); } } } array_delete(&queue->delayed_requests, i, 1); } } /* drop from main request list */ reqs = array_get_modifiable(&queue->requests, &count); for (i = 0; i < count; i++) { if (reqs[i] == req) break; } i_assert(i < count); if (i == 0) { if (queue->to_request != NULL) { timeout_remove(&queue->to_request); if (count > 1 && reqs[1]->timeout_time.tv_sec > 0) http_client_queue_set_request_timer(queue, &reqs[1]->timeout_time); } } req->queue = NULL; array_delete(&queue->requests, i, 1); if (array_count(&queue->requests) == 0) http_client_host_check_idle(queue->host); return; } static void http_client_queue_request_timeout(struct http_client_queue *queue) { struct http_client_request *const *reqs; ARRAY_TYPE(http_client_request) failed_requests; struct timeval new_to = { 0, 0 }; unsigned int count, i; http_client_queue_debug(queue, "Timeout (now: %s.%03lu)", t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_timeval.tv_sec), ((unsigned long)ioloop_timeval.tv_usec)/1000); if (queue->to_request != NULL) timeout_remove(&queue->to_request); /* collect failed requests */ reqs = array_get(&queue->requests, &count); i_assert(count > 0); t_array_init(&failed_requests, count); for (i = 0; i < count; i++) { if (reqs[i]->timeout_time.tv_sec > 0 && timeval_cmp_margin(&reqs[i]->timeout_time, &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) { break; } array_append(&failed_requests, &reqs[i], 1); } /* update timout */ if (i < count) new_to = reqs[i]->timeout_time; /* abort all failed request */ reqs = array_get(&failed_requests, &count); i_assert(count > 0); /* at least one request timed out */ for (i = 0; i < count; i++) { struct http_client_request *req = reqs[i]; http_client_queue_debug(queue, "Request %s timed out", http_client_request_label(req)); http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT, "Timed out"); } if (new_to.tv_sec > 0) { http_client_queue_debug(queue, "New timeout"); http_client_queue_set_request_timer(queue, &new_to); } } static void http_client_queue_set_request_timer(struct http_client_queue *queue, const struct timeval *time) { i_assert(time->tv_sec > 0); if (queue->to_request != NULL) timeout_remove(&queue->to_request); if (queue->client->set.debug) { http_client_queue_debug(queue, "Set request timeout to %s.%03lu (now: %s.%03lu)", t_strflocaltime("%Y-%m-%d %H:%M:%S", time->tv_sec), ((unsigned long)time->tv_usec)/1000, t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_timeval.tv_sec), ((unsigned long)ioloop_timeval.tv_usec)/1000); } /* set timer */ queue->to_request = timeout_add_absolute (time, http_client_queue_request_timeout, queue); } static int http_client_queue_request_timeout_cmp(struct http_client_request *const *req1, struct http_client_request *const *req2) { int ret; /* 0 means no timeout */ if ((*req1)->timeout_time.tv_sec == 0) { if ((*req2)->timeout_time.tv_sec == 0) { /* sort by age */ if ((ret=timeval_cmp(&(*req1)->submit_time, &(*req2)->submit_time)) != 0) return ret; } else { return 1; } } else if ((*req2)->timeout_time.tv_sec == 0) { return -1; /* sort by timeout */ } else if ((ret=timeval_cmp(&(*req1)->timeout_time, &(*req2)->timeout_time)) != 0) { return ret; } /* sort by minumum attempts for fairness */ return ((*req2)->attempts - (*req1)->attempts); } static void http_client_queue_submit_now(struct http_client_queue *queue, struct http_client_request *req) { ARRAY_TYPE(http_client_request) *req_queue; req->release_time.tv_sec = 0; req->release_time.tv_usec = 0; if (req->urgent) req_queue = &queue->queued_urgent_requests; else req_queue = &queue->queued_requests; /* enqueue */ if (req->timeout_time.tv_sec == 0) { /* no timeout; enqueue at end */ array_append(req_queue, &req, 1); } else if (timeval_diff_msecs(&req->timeout_time, &ioloop_timeval) <= 1) { /* pretty much already timed out; don't bother */ } else { unsigned int insert_idx; /* keep transmission queue sorted earliest timeout first */ (void)array_bsearch_insert_pos(req_queue, &req, http_client_queue_request_timeout_cmp, &insert_idx); array_insert(req_queue, insert_idx, &req, 1); } } /* * Delayed request queue */ static void http_client_queue_delay_timeout(struct http_client_queue *queue) { struct http_client_request *const *reqs; unsigned int count, i, finished; timeout_remove(&queue->to_delayed); io_loop_time_refresh(); finished = 0; reqs = array_get(&queue->delayed_requests, &count); for (i = 0; i < count; i++) { if (timeval_cmp_margin(&reqs[i]->release_time, &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) { break; } http_client_queue_debug(queue, "Activated delayed request %s%s", http_client_request_label(reqs[i]), (reqs[i]->urgent ? " (urgent)" : "")); http_client_queue_submit_now(queue, reqs[i]); finished++; } if (i < count) { http_client_queue_set_delay_timer(queue, reqs[i]->release_time); } array_delete(&queue->delayed_requests, 0, finished); http_client_queue_connection_setup(queue); } static void http_client_queue_set_delay_timer(struct http_client_queue *queue, struct timeval time) { int usecs = timeval_diff_usecs(&time, &ioloop_timeval); int msecs; /* round up to nearest microsecond */ msecs = (usecs + 999) / 1000; /* set timer */ if (queue->to_delayed != NULL) timeout_remove(&queue->to_delayed); queue->to_delayed = timeout_add (msecs, http_client_queue_delay_timeout, queue); } static int http_client_queue_delayed_cmp(struct http_client_request *const *req1, struct http_client_request *const *req2) { return timeval_cmp(&(*req1)->release_time, &(*req2)->release_time); } /* * Request submission */ void http_client_queue_submit_request(struct http_client_queue *queue, struct http_client_request *req) { unsigned int insert_idx; if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); req->queue = queue; /* check delay vs timeout */ if (req->release_time.tv_sec > 0 && req->timeout_time.tv_sec > 0 && timeval_cmp_margin(&req->release_time, &req->timeout_time, TIMEOUT_CMP_MARGIN_USECS) >= 0) { /* release time is later than absolute timeout */ req->release_time.tv_sec = 0; req->release_time.tv_usec = 0; /* timeout rightaway */ req->timeout_time = ioloop_timeval; http_client_queue_debug(queue, "Delayed request %s%s already timed out", http_client_request_label(req), (req->urgent ? " (urgent)" : "")); } /* add to main request list */ if (req->timeout_time.tv_sec == 0) { /* no timeout; just append */ array_append(&queue->requests, &req, 1); } else { unsigned int insert_idx; /* keep main request list sorted earliest timeout first */ (void)array_bsearch_insert_pos(&queue->requests, &req, http_client_queue_request_timeout_cmp, &insert_idx); array_insert(&queue->requests, insert_idx, &req, 1); /* now first in queue; update timer */ if (insert_idx == 0) http_client_queue_set_request_timer(queue, &req->timeout_time); } /* handle delay */ if (req->release_time.tv_sec > 0) { io_loop_time_refresh(); if (timeval_cmp_margin(&req->release_time, &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) { http_client_queue_debug(queue, "Delayed request %s%s submitted (time remaining: %d msecs)", http_client_request_label(req), (req->urgent ? " (urgent)" : ""), timeval_diff_msecs(&req->release_time, &ioloop_timeval)); (void)array_bsearch_insert_pos(&queue->delayed_requests, &req, http_client_queue_delayed_cmp, &insert_idx); array_insert(&queue->delayed_requests, insert_idx, &req, 1); if (insert_idx == 0) http_client_queue_set_delay_timer(queue, req->release_time); return; } } http_client_queue_submit_now(queue, req); } /* * Request retrieval */ struct http_client_request * http_client_queue_claim_request(struct http_client_queue *queue, const struct http_client_peer_addr *addr, bool no_urgent) { struct http_client_request *const *requests; struct http_client_request *req; unsigned int i, count; count = 0; if (!no_urgent) requests = array_get(&queue->queued_urgent_requests, &count); if (count == 0) requests = array_get(&queue->queued_requests, &count); if (count == 0) return NULL; i = 0; req = requests[i]; if (req->urgent) array_delete(&queue->queued_urgent_requests, i, 1); else array_delete(&queue->queued_requests, i, 1); http_client_queue_debug(queue, "Connection to peer %s claimed request %s %s", http_client_peer_addr2str(addr), http_client_request_label(req), (req->urgent ? "(urgent)" : "")); return req; } unsigned int http_client_queue_requests_pending(struct http_client_queue *queue, unsigned int *num_urgent_r) { unsigned int urg_count = array_count(&queue->queued_urgent_requests); if (num_urgent_r != NULL) *num_urgent_r = urg_count; return array_count(&queue->queued_requests) + urg_count; } unsigned int http_client_queue_requests_active(struct http_client_queue *queue) { return array_count(&queue->requests); } /* * ioloop */ void http_client_queue_switch_ioloop(struct http_client_queue *queue) { if (queue->to_connect != NULL) queue->to_connect = io_loop_move_timeout(&queue->to_connect); if (queue->to_request != NULL) queue->to_request = io_loop_move_timeout(&queue->to_request); if (queue->to_delayed != NULL) queue->to_delayed = io_loop_move_timeout(&queue->to_delayed); } dovecot-2.2.33.2/src/lib-http/test-http-auth.c0000644000175000017500000001711213165463624015712 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "test-common.h" #include "array.h" #include "str-sanitize.h" #include "http-auth.h" struct http_auth_challenge_test { const char *scheme; const char *data; struct http_auth_param *params; }; struct http_auth_challenges_test { const char *challenges_in; struct http_auth_challenge_test *challenges; }; /* Valid auth challenges tests */ static const struct http_auth_challenges_test valid_auth_challenges_tests[] = { { .challenges_in = "Basic realm=\"WallyWorld\"", .challenges = (struct http_auth_challenge_test []) { { .scheme = "Basic", .data = NULL, .params = (struct http_auth_param []) { { "realm", "WallyWorld" }, { NULL, NULL } } },{ .scheme = NULL } } },{ .challenges_in = "Digest " "realm=\"testrealm@host.com\", " "qop=\"auth,auth-int\", " "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", " "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", .challenges = (struct http_auth_challenge_test []) { { .scheme = "Digest", .data = NULL, .params = (struct http_auth_param []) { { "realm", "testrealm@host.com" }, { "qop", "auth,auth-int" }, { "nonce", "dcd98b7102dd2f0e8b11d0f600bfb0c093" }, { "opaque", "5ccc069c403ebaf9f0171e9517f40e41" }, { NULL, NULL } } },{ .scheme = NULL } } },{ .challenges_in = "Newauth realm=\"apps\", type=1, " "title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"", .challenges = (struct http_auth_challenge_test []) { { .scheme = "Newauth", .data = NULL, .params = (struct http_auth_param []) { { "realm", "apps" }, { "type", "1" }, { "title", "Login to \"apps\"" }, { NULL, NULL } } },{ .scheme = "Basic", .data = NULL, .params = (struct http_auth_param []) { { "realm", "simple" }, { NULL, NULL } } },{ .scheme = NULL } } } }; static const unsigned int valid_auth_challenges_test_count = N_ELEMENTS(valid_auth_challenges_tests); static void test_http_auth_challenges_valid(void) { unsigned int i; for (i = 0; i < valid_auth_challenges_test_count; i++) T_BEGIN { const char *challenges_in; ARRAY_TYPE(http_auth_challenge) out; const struct http_auth_challenges_test *test; bool result; test = &valid_auth_challenges_tests[i]; challenges_in = test->challenges_in; test_begin(t_strdup_printf("http auth challenges valid [%d]", i)); i_zero(&out); result = (http_auth_parse_challenges ((const unsigned char *)challenges_in, strlen(challenges_in), &out) > 0); test_out(t_strdup_printf("parse `%s'", challenges_in), result); if (result) { const struct http_auth_challenge *chalo; const struct http_auth_challenge_test *chalt; unsigned int index; index = 0; chalt = test->challenges; array_foreach(&out, chalo) { const struct http_auth_param *paramo, *paramt; unsigned int pindex; if (chalt != NULL && chalt->scheme != NULL) { i_assert(chalo->scheme != NULL); test_out(t_strdup_printf("[%d]->scheme = %s", index, str_sanitize(chalo->scheme, 80)), strcmp(chalo->scheme, chalt->scheme) == 0); if (chalo->data == NULL || chalt->data == NULL) { test_out(t_strdup_printf("[%d]->data = %s", index, str_sanitize(chalo->data, 80)), chalo->data == chalt->data); } else { test_out(t_strdup_printf("[%d]->data = %s", index, str_sanitize(chalo->data, 80)), strcmp(chalo->data, chalt->data) == 0); } paramt = chalt->params; pindex = 0; array_foreach(&chalo->params, paramo) { if (paramt->name == NULL) { test_out(t_strdup_printf("[%d]->params[%d]: %s = %s", index, pindex, str_sanitize(paramo->name, 80), str_sanitize(paramo->value, 80)), FALSE); break; } else { test_out(t_strdup_printf("[%d]->params[%d]: %s = %s", index, pindex, str_sanitize(paramo->name, 80), str_sanitize(paramo->value, 80)), strcmp(paramo->name, paramt->name) == 0 && strcmp(paramo->value, paramt->value) == 0); paramt++; } pindex++; } chalt++; } index++; } } test_end(); } T_END; } struct http_auth_credentials_test { const char *credentials_in; const char *scheme; const char *data; struct http_auth_param *params; }; /* Valid auth credentials tests */ static const struct http_auth_credentials_test valid_auth_credentials_tests[] = { { .credentials_in = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", .scheme = "Basic", .data = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==", .params = NULL },{ .credentials_in = "Digest username=\"Mufasa\", " "realm=\"testrealm@host.com\", " "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", " "uri=\"/dir/index.html\", " "qop=auth, " "nc=00000001, " "cnonce=\"0a4f113b\", " "response=\"6629fae49393a05397450978507c4ef1\", " "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", .scheme = "Digest", .data = NULL, .params = (struct http_auth_param []) { { "username", "Mufasa" }, { "realm", "testrealm@host.com" }, { "nonce", "dcd98b7102dd2f0e8b11d0f600bfb0c093" }, { "uri", "/dir/index.html" }, { "qop", "auth" }, { "nc", "00000001" }, { "cnonce", "0a4f113b" }, { "response", "6629fae49393a05397450978507c4ef1" }, { "opaque", "5ccc069c403ebaf9f0171e9517f40e41" }, { NULL, NULL } } } }; static const unsigned int valid_auth_credentials_test_count = N_ELEMENTS(valid_auth_credentials_tests); static void test_http_auth_credentials_valid(void) { unsigned int i; for (i = 0; i < valid_auth_credentials_test_count; i++) T_BEGIN { const char *credentials_in; struct http_auth_credentials out; const struct http_auth_credentials_test *test; bool result; test = &valid_auth_credentials_tests[i]; credentials_in = test->credentials_in; test_begin(t_strdup_printf("http auth credentials valid [%d]", i)); result = (http_auth_parse_credentials ((const unsigned char *)credentials_in, strlen(credentials_in), &out) > 0); test_out(t_strdup_printf("parse `%s'", credentials_in), result); if (result) { const struct http_auth_param *paramo, *paramt; unsigned int index; i_assert(out.scheme != NULL); test_out(t_strdup_printf("->scheme = %s", str_sanitize(out.scheme, 80)), strcmp(out.scheme, test->scheme) == 0); if (out.data == NULL || test->data == NULL) { test_out(t_strdup_printf("->data = %s", str_sanitize(out.data, 80)), out.data == test->data); } else { test_out(t_strdup_printf("->data = %s", str_sanitize(out.data, 80)), strcmp(out.data, test->data) == 0); } paramt = test->params; index = 0; if (array_is_created(&out.params)) { array_foreach(&out.params, paramo) { if (paramt == NULL || paramt->name == NULL) { test_out(t_strdup_printf("->params[%d]: %s = %s", index++, str_sanitize(paramo->name, 80), str_sanitize(paramo->value, 80)), FALSE); break; } else { test_out(t_strdup_printf("->params[%d]: %s = %s", index++, str_sanitize(paramo->name, 80), str_sanitize(paramo->value, 80)), strcmp(paramo->name, paramt->name) == 0 && strcmp(paramo->value, paramt->value) == 0); paramt++; } } } } test_end(); } T_END; } int main(void) { static void (*test_functions[])(void) = { test_http_auth_challenges_valid, test_http_auth_credentials_valid, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-client-connection.c0000644000175000017500000013710713165463624017416 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "llist.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "istream-timeout.h" #include "ostream.h" #include "time-util.h" #include "file-lock.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "http-response-parser.h" #include "http-client-private.h" /* * Logging */ static inline void http_client_connection_debug(struct http_client_connection *conn, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_connection_debug(struct http_client_connection *conn, const char *format, ...) { va_list args; if (conn->client->set.debug) { va_start(args, format); i_debug("http-client: conn %s: %s", conn->label, t_strdup_vprintf(format, args)); va_end(args); } } /* * Connection */ static void http_client_connection_ready(struct http_client_connection *conn); static void http_client_connection_input(struct connection *_conn); static inline void http_client_connection_ref_request(struct http_client_connection *conn, struct http_client_request *req) { i_assert(req->conn == NULL); req->conn = conn; http_client_request_ref(req); } static inline bool http_client_connection_unref_request(struct http_client_connection *conn, struct http_client_request **_req) { struct http_client_request *req = *_req; i_assert(req->conn == conn); req->conn = NULL; return http_client_request_unref(_req); } static inline void http_client_connection_failure(struct http_client_connection *conn, const char *reason) { struct http_client_peer *peer = conn->peer; conn->connect_failed = TRUE; http_client_peer_connection_failure(peer, reason); } unsigned int http_client_connection_count_pending(struct http_client_connection *conn) { unsigned int pending_count = array_count(&conn->request_wait_list); if (conn->in_req_callback || conn->pending_request != NULL) pending_count++; return pending_count; } bool http_client_connection_is_idle(struct http_client_connection *conn) { return (conn->to_idle != NULL); } bool http_client_connection_is_active(struct http_client_connection *conn) { if (!conn->connected) return FALSE; if (conn->in_req_callback || conn->pending_request != NULL) return TRUE; return (array_is_created(&conn->request_wait_list) && array_count(&conn->request_wait_list) > 0); } static void http_client_connection_retry_requests(struct http_client_connection *conn, unsigned int status, const char *error) { const struct http_client_settings *set = &conn->client->set; struct http_client_request *req, **req_idx; if (!array_is_created(&conn->request_wait_list)) return; if (set->no_auto_retry) { http_client_connection_debug(conn, "Aborting pending requests with error"); } else { http_client_connection_debug(conn, "Retrying pending requests"); } array_foreach_modifiable(&conn->request_wait_list, req_idx) { req = *req_idx; /* drop reference from connection */ if (!http_client_connection_unref_request(conn, req_idx)) continue; /* retry the request, which may drop it */ if (req->state < HTTP_REQUEST_STATE_FINISHED) { if (set->no_auto_retry) http_client_request_error(&req, status, error); else http_client_request_retry(req, status, error); } } array_clear(&conn->request_wait_list); } static void http_client_connection_server_close(struct http_client_connection **_conn) { struct http_client_connection *conn = *_conn; struct http_client_request *req, **req_idx; http_client_connection_debug(conn, "Server explicitly closed connection"); array_foreach_modifiable(&conn->request_wait_list, req_idx) { req = *req_idx; /* drop reference from connection */ if (!http_client_connection_unref_request(conn, req_idx)) continue; /* resubmit the request, which may drop it */ if (req->state < HTTP_REQUEST_STATE_FINISHED) http_client_request_resubmit(req); } array_clear(&conn->request_wait_list); if (conn->client->ioloop != NULL) io_loop_stop(conn->client->ioloop); http_client_connection_close(_conn); } static void http_client_connection_abort_error(struct http_client_connection **_conn, unsigned int status, const char *error) { struct http_client_connection *conn = *_conn; struct http_client_request *req, **req_idx; http_client_connection_debug(conn, "Aborting connection: %s", error); array_foreach_modifiable(&conn->request_wait_list, req_idx) { req = *req_idx; i_assert(req->submitted); /* drop reference from connection */ if (!http_client_connection_unref_request(conn, req_idx)) continue; /* drop request if not already aborted */ http_client_request_error(&req, status, error); } array_clear(&conn->request_wait_list); http_client_connection_close(_conn); } static void http_client_connection_abort_any_requests(struct http_client_connection *conn) { struct http_client_request *req, **req_idx; if (array_is_created(&conn->request_wait_list)) { array_foreach_modifiable(&conn->request_wait_list, req_idx) { req = *req_idx; i_assert(req->submitted); /* drop reference from connection */ if (!http_client_connection_unref_request(conn, req_idx)) continue; /* drop request if not already aborted */ http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborting"); } array_clear(&conn->request_wait_list); } if (conn->pending_request != NULL) { req = conn->pending_request; /* drop reference from connection */ if (http_client_connection_unref_request(conn, &conn->pending_request)) { /* drop request if not already aborted */ http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborting"); } } } static const char * http_client_connection_get_timing_info(struct http_client_connection *conn) { struct http_client_request *const *requestp; unsigned int connected_msecs; string_t *str = t_str_new(64); if (array_count(&conn->request_wait_list) > 0) { requestp = array_idx(&conn->request_wait_list, 0); str_append(str, "Request "); http_client_request_append_stats_text(*requestp, str); } else { str_append(str, "No requests"); if (conn->conn.last_input != 0) { str_printfa(str, ", last input %d secs ago", (int)(ioloop_time - conn->conn.last_input)); } } connected_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_timestamp); str_printfa(str, ", connected %u.%03u secs ago", connected_msecs/1000, connected_msecs%1000); return str_c(str); } static void http_client_connection_abort_temp_error(struct http_client_connection **_conn, unsigned int status, const char *error) { struct http_client_connection *conn = *_conn; error = t_strdup_printf("%s (%s)", error, http_client_connection_get_timing_info(conn)); http_client_connection_debug(conn, "Aborting connection with temporary error: %s", error); http_client_connection_retry_requests(conn, status, error); http_client_connection_close(_conn); } static void http_client_connection_lost(struct http_client_connection **_conn, const char *error) ATTR_NULL(2) { struct http_client_connection *conn = *_conn; const char *sslerr; if (error == NULL) error = "Connection lost"; else error = t_strdup_printf("Connection lost: %s", error); if (conn->ssl_iostream != NULL) { sslerr = ssl_iostream_get_last_error(conn->ssl_iostream); if (sslerr != NULL) { error = t_strdup_printf("%s (last SSL error: %s)", error, sslerr); } if (ssl_iostream_has_handshake_failed(conn->ssl_iostream)) { /* this isn't really a "connection lost", but that we don't trust the remote's SSL certificate. don't retry. */ http_client_connection_abort_error(_conn, HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, error); return; } } conn->lost_prematurely = (conn->conn.input != NULL && conn->conn.input->v_offset == 0 && i_stream_get_data_size(conn->conn.input) == 0); http_client_connection_abort_temp_error(_conn, HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, error); } int http_client_connection_check_ready(struct http_client_connection *conn) { int ret; if (conn->in_req_callback) { /* this can happen when a nested ioloop is created inside request callback. we currently don't reuse connections that are occupied this way, but theoretically we could, although that would add quite a bit of complexity. */ return 0; } if (!conn->connected || conn->output_locked || conn->output_broken || conn->close_indicated || conn->tunneling || http_client_connection_count_pending(conn) >= conn->client->set.max_pipelined_requests) return 0; if (conn->last_ioloop != NULL && conn->last_ioloop != current_ioloop) { conn->last_ioloop = current_ioloop; /* Active ioloop is different from what we saw earlier; we may have missed a disconnection event on this connection. Verify status by reading from connection. */ if ((ret=i_stream_read(conn->conn.input)) == -1) { int stream_errno = conn->conn.input->stream_errno; i_assert(conn->conn.input->stream_errno != 0 || conn->conn.input->eof); http_client_connection_lost(&conn, t_strdup_printf("read(%s) failed: %s", i_stream_get_name(conn->conn.input), stream_errno != 0 ? i_stream_get_error(conn->conn.input) : "EOF")); return -1; } /* we may have read some data */ if (i_stream_get_data_size(conn->conn.input) > 0) i_stream_set_input_pending(conn->conn.input, TRUE); } return 1; } static void http_client_connection_idle_timeout(struct http_client_connection *conn) { http_client_connection_debug(conn, "Idle connection timed out"); /* cannot get here unless connection was established at some point */ i_assert(conn->connect_succeeded); http_client_connection_close(&conn); } void http_client_connection_check_idle(struct http_client_connection *conn) { unsigned int timeout, count; if (conn->connected && array_is_created(&conn->request_wait_list) && array_count(&conn->request_wait_list) == 0 && !conn->in_req_callback && conn->incoming_payload == NULL && conn->client->set.max_idle_time_msecs > 0) { if (conn->to_idle != NULL) { /* timeout already set */ return; } if (conn->client->ioloop != NULL) io_loop_stop(conn->client->ioloop); count = array_count(&conn->peer->conns); i_assert(count > 0); /* set timeout for this connection */ if (count > conn->client->set.max_parallel_connections) { /* instant death for (urgent) connections above limit */ timeout = 0; } else { unsigned int idle_count = http_client_peer_idle_connections(conn->peer); /* kill duplicate connections quicker; linearly based on the number of connections */ i_assert(count >= idle_count + 1); timeout = (conn->client->set.max_parallel_connections - idle_count) * (conn->client->set.max_idle_time_msecs / conn->client->set.max_parallel_connections); } http_client_connection_debug(conn, "No more requests queued; going idle (timeout = %u msecs)", timeout); conn->to_idle = timeout_add(timeout, http_client_connection_idle_timeout, conn); } else { /* there should be no idle timeout */ i_assert(conn->to_idle == NULL); } } static void http_client_connection_request_timeout(struct http_client_connection *conn) { conn->conn.input->stream_errno = ETIMEDOUT; http_client_connection_abort_temp_error(&conn, HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT, "Request timed out"); } void http_client_connection_start_request_timeout( struct http_client_connection *conn) { unsigned int timeout_msecs = conn->client->set.request_timeout_msecs; if (conn->pending_request != NULL) return; i_assert(array_is_created(&conn->request_wait_list)); if (array_count(&conn->request_wait_list) > 0) { struct http_client_request *const *requestp; requestp = array_idx(&conn->request_wait_list, 0); timeout_msecs = (*requestp)->attempt_timeout_msecs; } if (timeout_msecs == 0) ; else if (conn->to_requests != NULL) timeout_reset(conn->to_requests); else { conn->to_requests = timeout_add(timeout_msecs, http_client_connection_request_timeout, conn); } } void http_client_connection_reset_request_timeout( struct http_client_connection *conn) { if (conn->to_requests != NULL) timeout_reset(conn->to_requests); } void http_client_connection_stop_request_timeout( struct http_client_connection *conn) { if (conn->to_requests != NULL) timeout_remove(&conn->to_requests); } static void http_client_connection_continue_timeout(struct http_client_connection *conn) { struct http_client_request *const *wait_reqs; struct http_client_request *req; unsigned int wait_count; const char *error; i_assert(conn->pending_request == NULL); if (conn->to_response != NULL) timeout_remove(&conn->to_response); conn->peer->no_payload_sync = TRUE; http_client_connection_debug(conn, "Expected 100-continue response timed out; sending payload anyway"); wait_reqs = array_get(&conn->request_wait_list, &wait_count); i_assert(wait_count == 1); req = wait_reqs[wait_count-1]; req->payload_sync_continue = TRUE; if (http_client_request_send_more(req, FALSE, &error) < 0) { http_client_connection_lost(&conn, t_strdup_printf("Failed to send request: %s", error)); } } int http_client_connection_next_request(struct http_client_connection *conn) { struct http_client_request *req = NULL; const char *error; bool pipelined; int ret; if ((ret=http_client_connection_check_ready(conn)) <= 0) { if (ret == 0) { http_client_connection_debug(conn, "Not ready for next request"); } return ret; } /* claim request, but no urgent request can be second in line */ pipelined = array_count(&conn->request_wait_list) > 0 || conn->pending_request != NULL; req = http_client_peer_claim_request(conn->peer, pipelined); if (req == NULL) return 0; i_assert(req->state == HTTP_REQUEST_STATE_QUEUED); if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); req->payload_sync_continue = FALSE; if (conn->peer->no_payload_sync) req->payload_sync = FALSE; /* add request to wait list and add a reference */ array_append(&conn->request_wait_list, &req, 1); http_client_connection_ref_request(conn, req); http_client_connection_debug(conn, "Claimed request %s", http_client_request_label(req)); if (http_client_request_send(req, pipelined, &error) < 0) { http_client_connection_lost(&conn, t_strdup_printf("Failed to send request: %s", error)); return -1; } if (req->connect_tunnel) conn->tunneling = TRUE; /* RFC 7231, Section 5.1.1: Expect o A client that sends a 100-continue expectation is not required to wait for any specific length of time; such a client MAY proceed to send the message body even if it has not yet received a response. Furthermore, since 100 (Continue) responses cannot be sent through an HTTP/1.0 intermediary, such a client SHOULD NOT wait for an indefinite period before sending the message body. */ if (req->payload_sync && !conn->peer->seen_100_response) { i_assert(!pipelined); i_assert(req->payload_chunked || req->payload_size > 0); i_assert(conn->to_response == NULL); conn->to_response = timeout_add(HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS, http_client_connection_continue_timeout, conn); } return 1; } static void http_client_connection_destroy(struct connection *_conn) { struct http_client_connection *conn = (struct http_client_connection *)_conn; const char *error; unsigned int msecs; switch (_conn->disconnect_reason) { case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: if (conn->connected_timestamp.tv_sec == 0) { msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connect_start_timestamp); error = t_strdup_printf( "connect(%s) failed: Connection timed out in %u.%03u secs", _conn->name, msecs/1000, msecs%1000); } else { msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_timestamp); error = t_strdup_printf( "SSL handshaking with %s failed: Connection timed out in %u.%03u secs", _conn->name, msecs/1000, msecs%1000); } http_client_connection_debug(conn, "%s", error); http_client_connection_failure(conn, error); break; case CONNECTION_DISCONNECT_CONN_CLOSED: if (conn->connect_failed) { i_assert(!array_is_created(&conn->request_wait_list) || array_count(&conn->request_wait_list) == 0); break; } http_client_connection_lost(&conn, (_conn->input == NULL ? NULL : i_stream_get_error(_conn->input))); return; default: break; } http_client_connection_close(&conn); } static void http_client_payload_finished(struct http_client_connection *conn) { timeout_remove(&conn->to_input); conn->conn.io = io_add_istream(conn->conn.input, http_client_connection_input, &conn->conn); if (array_count(&conn->request_wait_list) > 0) http_client_connection_start_request_timeout(conn); } static void http_client_payload_destroyed_timeout(struct http_client_connection *conn) { if (conn->close_indicated) { http_client_connection_server_close(&conn); return; } http_client_connection_input(&conn->conn); } static void http_client_payload_destroyed(struct http_client_request *req) { struct http_client_connection *conn = req->conn; i_assert(conn != NULL); i_assert(conn->pending_request == req); i_assert(conn->incoming_payload != NULL); i_assert(conn->conn.io == NULL); http_client_connection_debug(conn, "Response payload stream destroyed (%u ms after initial response)", timeval_diff_msecs(&ioloop_timeval, &req->response_time)); /* caller is allowed to change the socket fd to blocking while reading the payload. make sure here that it's switched back. */ net_set_nonblock(conn->conn.fd_in, TRUE); /* drop reference from connection */ if (http_client_connection_unref_request(conn, &conn->pending_request)) { /* finish request if not already aborted */ http_client_request_finish(req); } conn->incoming_payload = NULL; /* input stream may have pending input. make sure input handler gets called (but don't do it directly, since we get get here somewhere from the API user's code, which we can't really know what state it is in). this call also triggers sending a new request if necessary. */ if (!conn->disconnected) { conn->to_input = timeout_add_short (0, http_client_payload_destroyed_timeout, conn); } /* room for new requests */ if (http_client_connection_check_ready(conn) > 0) http_client_peer_trigger_request_handler(conn->peer); } void http_client_connection_request_destroyed( struct http_client_connection *conn, struct http_client_request *req) { struct istream *payload; i_assert(req->conn == conn); if (conn->pending_request != req) return; http_client_connection_debug(conn, "Pending request destroyed prematurely"); payload = conn->incoming_payload; if (payload == NULL) { /* payload already gone */ return; } /* destroy the payload, so that the timeout istream is closed */ i_stream_ref(payload); i_stream_destroy(&payload); payload = conn->incoming_payload; if (payload == NULL) { /* not going to happen, but check for it anyway */ return; } /* the application still holds a reference to the payload stream, but it is closed and we don't care about it anymore, so act as though it is destroyed. */ i_stream_remove_destroy_callback(payload, http_client_payload_destroyed); http_client_payload_destroyed(req); } static bool http_client_connection_return_response( struct http_client_connection *conn, struct http_client_request *req, struct http_response *response) { struct istream *payload; bool retrying; i_assert(!conn->in_req_callback); i_assert(conn->incoming_payload == NULL); i_assert(conn->pending_request == NULL); http_client_connection_ref(conn); http_client_connection_ref_request(conn, req); req->state = HTTP_REQUEST_STATE_GOT_RESPONSE; if (response->payload != NULL) { /* wrap the stream to capture the destroy event without destroying the actual payload stream. */ conn->incoming_payload = response->payload = i_stream_create_timeout(response->payload, req->attempt_timeout_msecs); i_stream_add_destroy_callback(response->payload, http_client_payload_destroyed, req); /* the callback may add its own I/O, so we need to remove our one before calling it */ io_remove(&conn->conn.io); /* we've received the request itself, and we can't reset the timeout during the payload reading. */ http_client_connection_stop_request_timeout(conn); } conn->in_req_callback = TRUE; retrying = !http_client_request_callback(req, response); if (conn->disconnected) { /* the callback managed to get this connection disconnected */ if (!retrying) http_client_request_finish(req); http_client_connection_unref_request(conn, &req); http_client_connection_unref(&conn); return FALSE; } conn->in_req_callback = FALSE; if (retrying) { /* retrying, don't destroy the request */ if (response->payload != NULL) { i_stream_remove_destroy_callback(conn->incoming_payload, http_client_payload_destroyed); i_stream_unref(&conn->incoming_payload); conn->conn.io = io_add_istream(conn->conn.input, http_client_connection_input, &conn->conn); } http_client_connection_unref_request(conn, &req); return http_client_connection_unref(&conn); } if (response->payload != NULL) { req->state = HTTP_REQUEST_STATE_PAYLOAD_IN; payload = response->payload; response->payload = NULL; /* maintain request reference while payload is pending */ conn->pending_request = req; /* request is dereferenced in payload destroy callback */ i_stream_unref(&payload); if (conn->to_input != NULL && conn->conn.input != NULL) { /* already finished reading the payload */ http_client_payload_finished(conn); } } else { http_client_request_finish(req); http_client_connection_unref_request(conn, &req); } if (conn->incoming_payload == NULL && conn->conn.input != NULL) { i_assert(conn->conn.io != NULL || conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW); return http_client_connection_unref(&conn); } http_client_connection_unref(&conn); return FALSE; } static void http_client_connection_input(struct connection *_conn) { struct http_client_connection *conn = (struct http_client_connection *)_conn; struct http_response response; struct http_client_request *const *reqs; struct http_client_request *req = NULL, *req_ref; enum http_response_payload_type payload_type; unsigned int count; int finished = 0, ret; const char *error; i_assert(conn->incoming_payload == NULL); _conn->last_input = ioloop_time; if (conn->ssl_iostream != NULL && !ssl_iostream_is_handshaked(conn->ssl_iostream)) { /* finish SSL negotiation by reading from input stream */ while ((ret=i_stream_read(conn->conn.input)) > 0 || ret == -2) { if (ssl_iostream_is_handshaked(conn->ssl_iostream)) break; } if (ret < 0) { int stream_errno = conn->conn.input->stream_errno; /* failed somehow */ i_assert(ret != -2); error = t_strdup_printf( "SSL handshaking with %s failed: " "read(%s) failed: %s", _conn->name, i_stream_get_name(conn->conn.input), stream_errno != 0 ? i_stream_get_error(conn->conn.input) : "EOF"); http_client_peer_connection_failure(conn->peer, error); http_client_connection_debug(conn, "%s", error); http_client_connection_close(&conn); return; } if (!ssl_iostream_is_handshaked(conn->ssl_iostream)) { /* not finished */ i_assert(ret == 0); return; } /* ready for first request */ http_client_connection_ready(conn); } if (conn->to_input != NULL) { /* We came here from a timeout added by http_client_payload_destroyed(). The IO couldn't be added back immediately in there, because the HTTP API user may still have had its own IO pointed to the same fd. It should be removed by now, so we can add it back. */ http_client_payload_finished(conn); finished++; } /* we've seen activity from the server; reset request timeout */ http_client_connection_reset_request_timeout(conn); /* get first waiting request */ reqs = array_get(&conn->request_wait_list, &count); if (count > 0) { req = reqs[0]; /* determine whether to expect a response payload */ payload_type = http_client_request_get_payload_type(req); } else { req = NULL; payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED; } /* drop connection with broken output if last possible input was received */ if (conn->output_broken && (count == 0 || (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) { http_client_connection_server_close(&conn); return; } while ((ret=http_response_parse_next (conn->http_parser, payload_type, &response, &error)) > 0) { bool aborted, early = FALSE; if (req == NULL) { /* server sent response without any requests in the wait list */ if (response.status == 408) { http_client_connection_debug(conn, "Server explicitly closed connection: 408 %s", response.reason); } else { http_client_connection_debug(conn, "Got unexpected input from server: %u %s", response.status, response.reason); } http_client_connection_close(&conn); return; } req->response_time = ioloop_timeval; /* Got some response; cancel response timeout */ if (conn->to_response != NULL) timeout_remove(&conn->to_response); /* RFC 7231, Section 6.2: A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses. */ if (req->payload_sync && response.status == 100) { if (req->payload_sync_continue) { http_client_connection_debug(conn, "Got 100-continue response after timeout"); continue; } conn->peer->no_payload_sync = FALSE; conn->peer->seen_100_response = TRUE; req->payload_sync_continue = TRUE; http_client_connection_debug(conn, "Got expected 100-continue response"); if (req->state == HTTP_REQUEST_STATE_ABORTED) { http_client_connection_debug(conn, "Request aborted before sending payload was complete."); http_client_connection_close(&conn); return; } if (http_client_request_send_more(req, FALSE, &error) < 0) { http_client_connection_lost(&conn, t_strdup_printf("Failed to send request: %s", error)); } return; } else if (response.status / 100 == 1) { /* ignore other 1xx for now */ http_client_connection_debug(conn, "Got unexpected %u response; ignoring", response.status); continue; } else if (!req->payload_sync && req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) { /* got early response from server while we're still sending request payload. we cannot recover from this reliably, so we stop sending payload and close the connection once the response is processed */ http_client_connection_debug(conn, "Got early input from server; " "request payload not completely sent (will close connection)"); o_stream_unset_flush_callback(conn->conn.output); conn->output_broken = early = TRUE; } http_client_connection_debug(conn, "Got %u response for request %s (took %u ms + %u ms in queue)", response.status, http_client_request_label(req), timeval_diff_msecs(&req->response_time, &req->sent_time), timeval_diff_msecs(&req->sent_time, &req->submit_time)); /* make sure connection output is unlocked if 100-continue failed */ if (req->payload_sync && !req->payload_sync_continue) { http_client_connection_debug(conn, "Unlocked output"); conn->output_locked = FALSE; } /* remove request from queue */ array_delete(&conn->request_wait_list, 0, 1); aborted = (req->state == HTTP_REQUEST_STATE_ABORTED); req_ref = req; if (!http_client_connection_unref_request(conn, &req_ref)) { i_assert(aborted); req = NULL; } conn->close_indicated = response.connection_close; if (!aborted) { bool handled = FALSE; /* response cannot be 2xx if request payload was not completely sent */ if (early && response.status / 100 == 2) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, "Server responded with success response " "before all payload was sent"); http_client_connection_close(&conn); return; } /* don't redirect/retry if we're sending data in small blocks via http_client_request_send_payload() and we're not waiting for 100 continue */ if (!req->payload_wait || (req->payload_sync && !req->payload_sync_continue)) { /* failed Expect: */ if (response.status == 417 && req->payload_sync) { /* drop Expect: continue */ req->payload_sync = FALSE; conn->output_locked = FALSE; conn->peer->no_payload_sync = TRUE; if (http_client_request_try_retry(req)) handled = TRUE; /* redirection */ } else if (!req->client->set.no_auto_redirect && response.status / 100 == 3 && response.status != 304 && response.location != NULL) { /* redirect (possibly after delay) */ if (http_client_request_delay_from_response(req, &response) >= 0) { http_client_request_redirect (req, response.status, response.location); handled = TRUE; } /* service unavailable */ } else if (response.status == 503) { /* automatically retry after delay if indicated */ if ( response.retry_after != (time_t)-1 && http_client_request_delay_from_response(req, &response) > 0 && http_client_request_try_retry(req)) handled = TRUE; /* request timeout (by server) */ } else if (response.status == 408) { /* automatically retry */ if (http_client_request_try_retry(req)) handled = TRUE; /* connection close is implicit, although server should indicate that explicitly */ conn->close_indicated = TRUE; } } if (!handled) { /* response for application */ if (!http_client_connection_return_response (conn, req, &response)) return; } } finished++; /* server closing connection? */ if (conn->close_indicated) { http_client_connection_server_close(&conn); return; } /* get next waiting request */ reqs = array_get(&conn->request_wait_list, &count); if (count > 0) { req = reqs[0]; /* determine whether to expect a response payload */ payload_type = http_client_request_get_payload_type(req); } else { /* no more requests waiting for the connection */ req = NULL; payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED; } /* drop connection with broken output if last possible input was received */ if (conn->output_broken && (count == 0 || (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) { http_client_connection_server_close(&conn); return; } } if (ret <= 0 && (conn->conn.input->eof || conn->conn.input->stream_errno != 0)) { int stream_errno = conn->conn.input->stream_errno; http_client_connection_lost(&conn, t_strdup_printf("read(%s) failed: %s", i_stream_get_name(conn->conn.input), stream_errno != 0 ? i_stream_get_error(conn->conn.input) : "EOF")); return; } if (ret < 0) { http_client_connection_abort_error(&conn, HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, error); return; } if (finished > 0) { /* connection still alive after (at least one) request; we can pipeline -> mark for subsequent connections */ conn->peer->allows_pipelining = TRUE; /* room for new requests */ if (http_client_connection_check_ready(conn) > 0) http_client_peer_trigger_request_handler(conn->peer); } } int http_client_connection_output(struct http_client_connection *conn) { struct http_client_request *const *reqs; struct ostream *output = conn->conn.output; unsigned int count; const char *error; int ret; /* we've seen activity from the server; reset request timeout */ http_client_connection_reset_request_timeout(conn); if ((ret = o_stream_flush(output)) <= 0) { if (ret < 0) { http_client_connection_lost(&conn, t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output))); } return ret; } i_assert(!conn->output_broken); if (conn->ssl_iostream != NULL && !ssl_iostream_is_handshaked(conn->ssl_iostream)) return 1; reqs = array_get(&conn->request_wait_list, &count); if (count > 0 && conn->output_locked) { struct http_client_request *req = reqs[count-1]; bool pipelined = (count > 1 || conn->pending_request != NULL); if (req->state == HTTP_REQUEST_STATE_ABORTED) { http_client_connection_debug(conn, "Request aborted before sending payload was complete."); if (count == 1) { http_client_connection_close(&conn); } else { o_stream_unset_flush_callback(output); conn->output_broken = TRUE; } return 1; } if (!req->payload_sync || req->payload_sync_continue) { if (http_client_request_send_more(req, pipelined, &error) < 0) { http_client_connection_lost(&conn, error); return -1; } if (!conn->output_locked) { /* room for new requests */ if (http_client_connection_check_ready(conn) > 0) http_client_peer_trigger_request_handler(conn->peer); } } } return 1; } void http_client_connection_start_tunnel(struct http_client_connection **_conn, struct http_client_tunnel *tunnel) { struct http_client_connection *conn = *_conn; i_assert(conn->tunneling); /* claim connection streams */ i_zero(tunnel); tunnel->input = conn->conn.input; tunnel->output = conn->conn.output; tunnel->fd_in = conn->conn.fd_in; tunnel->fd_out = conn->conn.fd_out; /* detach from connection */ conn->conn.input = NULL; conn->conn.output = NULL; conn->conn.fd_in = -1; conn->conn.fd_out = -1; conn->closing = TRUE; conn->connected = FALSE; connection_disconnect(&conn->conn); http_client_connection_unref(_conn); } static void http_client_connection_ready(struct http_client_connection *conn) { http_client_connection_debug(conn, "Ready for requests"); /* connected */ conn->connected = TRUE; conn->last_ioloop = current_ioloop; if (conn->to_connect != NULL) timeout_remove(&conn->to_connect); /* indicate connection success */ conn->connect_succeeded = TRUE; http_client_peer_connection_success(conn->peer); /* start raw log */ if (conn->client->set.rawlog_dir != NULL) { iostream_rawlog_create(conn->client->set.rawlog_dir, &conn->conn.input, &conn->conn.output); } /* direct tunneling connections handle connect requests just by providing a raw connection */ if (conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW) { struct http_client_request *req; req = http_client_peer_claim_request(conn->peer, FALSE); if (req != NULL) { struct http_response response; conn->tunneling = TRUE; i_zero(&response); response.status = 200; response.reason = "OK"; (void)http_client_connection_return_response(conn, req, &response); return; } http_client_connection_debug(conn, "No raw connect requests pending; closing useless connection"); http_client_connection_close(&conn); return; } /* start protocol I/O */ conn->http_parser = http_response_parser_init (conn->conn.input, &conn->client->set.response_hdr_limits, 0); o_stream_set_flush_callback(conn->conn.output, http_client_connection_output, conn); } static int http_client_connection_ssl_handshaked(const char **error_r, void *context) { struct http_client_connection *conn = context; const char *error, *host = conn->peer->addr.a.tcp.https_name; if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, &error) == 0) http_client_connection_debug(conn, "SSL handshake successful"); else if (conn->client->set.ssl_allow_invalid_cert) { http_client_connection_debug(conn, "SSL handshake successful, " "ignoring invalid certificate: %s", error); } else { *error_r = error; return -1; } return 0; } static int http_client_connection_ssl_init(struct http_client_connection *conn, const char **error_r) { struct ssl_iostream_settings ssl_set; const char *error; i_assert(conn->client->ssl_ctx != NULL); i_zero(&ssl_set); if (!conn->client->set.ssl_allow_invalid_cert) { ssl_set.verbose_invalid_cert = TRUE; ssl_set.verify_remote_cert = TRUE; ssl_set.require_valid_cert = TRUE; } if (conn->client->set.debug) http_client_connection_debug(conn, "Starting SSL handshake"); if (io_stream_create_ssl_client(conn->client->ssl_ctx, conn->peer->addr.a.tcp.https_name, &ssl_set, &conn->conn.input, &conn->conn.output, &conn->ssl_iostream, &error) < 0) { *error_r = t_strdup_printf( "Couldn't initialize SSL client for %s: %s", conn->conn.name, error); return -1; } ssl_iostream_set_handshake_callback(conn->ssl_iostream, http_client_connection_ssl_handshaked, conn); if (ssl_iostream_handshake(conn->ssl_iostream) < 0) { *error_r = t_strdup_printf("SSL handshake to %s failed: %s", conn->conn.name, ssl_iostream_get_last_error(conn->ssl_iostream)); return -1; } if (ssl_iostream_is_handshaked(conn->ssl_iostream)) { http_client_connection_ready(conn); } else { /* wait for handshake to complete; connection input handler does the rest by reading from the input stream */ o_stream_set_flush_callback(conn->conn.output, http_client_connection_output, conn); } return 0; } static void http_client_connection_connected(struct connection *_conn, bool success) { struct http_client_connection *conn = (struct http_client_connection *)_conn; const struct http_client_settings *set = &conn->client->set; const char *error; if (!success) { http_client_connection_failure(conn, t_strdup_printf( "connect(%s) failed: %m", _conn->name)); } else { conn->connected_timestamp = ioloop_timeval; http_client_connection_debug(conn, "Connected"); (void)net_set_tcp_nodelay(_conn->fd_out, TRUE); if (set->socket_send_buffer_size > 0) { if (net_set_send_buffer_size(_conn->fd_out, set->socket_send_buffer_size) < 0) i_error("net_set_send_buffer_size(%"PRIuSIZE_T") failed: %m", set->socket_send_buffer_size); } if (set->socket_recv_buffer_size > 0) { if (net_set_recv_buffer_size(_conn->fd_in, set->socket_recv_buffer_size) < 0) i_error("net_set_recv_buffer_size(%"PRIuSIZE_T") failed: %m", set->socket_recv_buffer_size); } if (http_client_peer_addr_is_https(&conn->peer->addr)) { if (http_client_connection_ssl_init(conn, &error) < 0) { http_client_connection_debug(conn, "%s", error); http_client_connection_failure(conn, error); http_client_connection_close(&conn); } return; } http_client_connection_ready(conn); } } static const struct connection_settings http_client_connection_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE, .delayed_unix_client_connected_callback = TRUE }; static const struct connection_vfuncs http_client_connection_vfuncs = { .destroy = http_client_connection_destroy, .input = http_client_connection_input, .client_connected = http_client_connection_connected }; struct connection_list * http_client_connection_list_init(void) { return connection_list_init (&http_client_connection_set, &http_client_connection_vfuncs); } static void http_client_connection_delayed_connect_error(struct http_client_connection *conn) { timeout_remove(&conn->to_input); errno = conn->connect_errno; http_client_connection_connected(&conn->conn, FALSE); http_client_connection_close(&conn); } static void http_client_connect_timeout(struct http_client_connection *conn) { conn->conn.disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT; http_client_connection_destroy(&conn->conn); } static void http_client_connection_connect(struct http_client_connection *conn) { unsigned int msecs; conn->connect_start_timestamp = ioloop_timeval; if (connection_client_connect(&conn->conn) < 0) { conn->connect_errno = errno; http_client_connection_debug(conn, "Connect failed: %m"); conn->to_input = timeout_add_short(0, http_client_connection_delayed_connect_error, conn); return; } /* don't use connection.h timeout because we want this timeout to include also the SSL handshake */ msecs = conn->client->set.connect_timeout_msecs; if (msecs == 0) msecs = conn->client->set.request_timeout_msecs; if (msecs > 0) { conn->to_connect = timeout_add(msecs, http_client_connect_timeout, conn); } } static void http_client_connect_tunnel_timeout(struct http_client_connection *conn) { const char *error, *name = http_client_peer_addr2str(&conn->peer->addr); unsigned int msecs; msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connect_start_timestamp); error = t_strdup_printf( "Tunnel connect(%s) failed: " "Connection timed out in %u.%03u secs", name, msecs/1000, msecs%1000); http_client_connection_debug(conn, "%s", error); http_client_peer_connection_failure(conn->peer, error); http_client_connection_close(&conn); } static void http_client_connection_tunnel_response(const struct http_response *response, struct http_client_connection *conn) { struct http_client_tunnel tunnel; const char *name = http_client_peer_addr2str(&conn->peer->addr); struct http_client_request *req = conn->connect_request; conn->connect_request = NULL; if (response->status != 200) { http_client_connection_failure(conn, t_strdup_printf( "Tunnel connect(%s) failed: %s", name, http_response_get_message(response))); return; } http_client_request_start_tunnel(req, &tunnel); connection_init_from_streams (conn->client->conn_list, &conn->conn, name, tunnel.input, tunnel.output); i_stream_unref(&tunnel.input); o_stream_unref(&tunnel.output); conn->connect_initialized = TRUE; } static void http_client_connection_connect_tunnel(struct http_client_connection *conn, const struct ip_addr *ip, in_port_t port) { unsigned int msecs; conn->connect_start_timestamp = ioloop_timeval; conn->connect_request = http_client_request_connect_ip (conn->client, ip, port, http_client_connection_tunnel_response, conn); http_client_request_set_urgent(conn->connect_request); http_client_request_submit(conn->connect_request); /* don't use connection.h timeout because we want this timeout to include also the SSL handshake */ msecs = conn->client->set.connect_timeout_msecs; if (msecs == 0) msecs = conn->client->set.request_timeout_msecs; if (msecs > 0) { conn->to_connect = timeout_add(msecs, http_client_connect_tunnel_timeout, conn); } } struct http_client_connection * http_client_connection_create(struct http_client_peer *peer) { struct http_client_connection *conn; static unsigned int id = 0; const struct http_client_peer_addr *addr = &peer->addr; const char *conn_type = "UNKNOWN"; switch (peer->addr.type) { case HTTP_CLIENT_PEER_ADDR_HTTP: conn_type = "HTTP"; break; case HTTP_CLIENT_PEER_ADDR_HTTPS: conn_type = "HTTPS"; break; case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: conn_type = "Tunneled HTTPS"; break; case HTTP_CLIENT_PEER_ADDR_RAW: conn_type = "Raw"; break; case HTTP_CLIENT_PEER_ADDR_UNIX: conn_type = "Unix"; break; } conn = i_new(struct http_client_connection, 1); conn->refcount = 1; conn->client = peer->client; conn->id = id++; conn->peer = peer; if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW) i_array_init(&conn->request_wait_list, 16); conn->io_wait_timer = io_wait_timer_add(); conn->label = i_strdup_printf("%s [%d]", http_client_peer_label(peer), conn->id); switch (peer->addr.type) { case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL: http_client_connection_connect_tunnel (conn, &addr->a.tcp.ip, addr->a.tcp.port); break; case HTTP_CLIENT_PEER_ADDR_UNIX: connection_init_client_unix(peer->client->conn_list, &conn->conn, addr->a.un.path); conn->connect_initialized = TRUE; http_client_connection_connect(conn); break; default: connection_init_client_ip(peer->client->conn_list, &conn->conn, &addr->a.tcp.ip, addr->a.tcp.port); conn->connect_initialized = TRUE; http_client_connection_connect(conn); } array_append(&peer->conns, &conn, 1); http_client_connection_debug(conn, "%s connection created (%d parallel connections exist)%s", conn_type, array_count(&peer->conns), (conn->to_input == NULL ? "" : " [broken]")); return conn; } void http_client_connection_ref(struct http_client_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } static void http_client_connection_disconnect(struct http_client_connection *conn) { struct http_client_peer *peer = conn->peer; ARRAY_TYPE(http_client_connection) *conn_arr; struct http_client_connection *const *conn_idx; if (conn->disconnected) return; conn->disconnected = TRUE; http_client_connection_debug(conn, "Connection disconnect"); conn->closing = TRUE; conn->connected = FALSE; if (conn->connect_request != NULL) http_client_request_abort(&conn->connect_request); if (conn->incoming_payload != NULL) { /* the stream is still accessed by lib-http caller. */ i_stream_remove_destroy_callback(conn->incoming_payload, http_client_payload_destroyed); conn->incoming_payload = NULL; } http_client_connection_abort_any_requests(conn); if (conn->http_parser != NULL) http_response_parser_deinit(&conn->http_parser); if (conn->connect_initialized) connection_disconnect(&conn->conn); if (conn->io_req_payload != NULL) io_remove(&conn->io_req_payload); if (conn->to_requests != NULL) timeout_remove(&conn->to_requests); if (conn->to_connect != NULL) timeout_remove(&conn->to_connect); if (conn->to_input != NULL) timeout_remove(&conn->to_input); if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); if (conn->to_response != NULL) timeout_remove(&conn->to_response); /* remove this connection from the list */ conn_arr = &peer->conns; array_foreach(conn_arr, conn_idx) { if (*conn_idx == conn) { array_delete(conn_arr, array_foreach_idx(conn_arr, conn_idx), 1); break; } } if (conn->connect_succeeded) http_client_peer_connection_lost(peer, conn->lost_prematurely); } bool http_client_connection_unref(struct http_client_connection **_conn) { struct http_client_connection *conn = *_conn; i_assert(conn->refcount > 0); *_conn = NULL; if (--conn->refcount > 0) return TRUE; http_client_connection_debug(conn, "Connection destroy"); http_client_connection_disconnect(conn); i_assert(conn->io_req_payload == NULL); i_assert(conn->to_requests == NULL); i_assert(conn->to_connect == NULL); i_assert(conn->to_input == NULL); i_assert(conn->to_idle == NULL); i_assert(conn->to_response == NULL); if (array_is_created(&conn->request_wait_list)) array_free(&conn->request_wait_list); if (conn->ssl_iostream != NULL) ssl_iostream_unref(&conn->ssl_iostream); if (conn->connect_initialized) connection_deinit(&conn->conn); io_wait_timer_remove(&conn->io_wait_timer); i_free(conn->label); i_free(conn); return FALSE; } void http_client_connection_close(struct http_client_connection **_conn) { struct http_client_connection *conn = *_conn; http_client_connection_debug(conn, "Connection close"); http_client_connection_disconnect(conn); http_client_connection_unref(_conn); } void http_client_connection_peer_closed(struct http_client_connection **_conn) { struct http_client_connection *conn = *_conn; http_client_connection_debug(conn, "Peer closed"); http_client_connection_disconnect(conn); if (http_client_connection_unref(_conn)) conn->peer = NULL; } void http_client_connection_switch_ioloop(struct http_client_connection *conn) { if (conn->io_req_payload != NULL) conn->io_req_payload = io_loop_move_io(&conn->io_req_payload); if (conn->to_requests != NULL) conn->to_requests = io_loop_move_timeout(&conn->to_requests); if (conn->to_connect != NULL) conn->to_connect = io_loop_move_timeout(&conn->to_connect); if (conn->to_input != NULL) conn->to_input = io_loop_move_timeout(&conn->to_input); if (conn->to_idle != NULL) conn->to_idle = io_loop_move_timeout(&conn->to_idle); if (conn->to_response != NULL) conn->to_response = io_loop_move_timeout(&conn->to_response); if (conn->incoming_payload != NULL) i_stream_switch_ioloop(conn->incoming_payload); conn->io_wait_timer = io_wait_timer_move(&conn->io_wait_timer); connection_switch_ioloop(&conn->conn); } dovecot-2.2.33.2/src/lib-http/http-server.c0000644000175000017500000000455613123174404015277 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "connection.h" #include "dns-lookup.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "http-url.h" #include "http-server-private.h" /* * Server */ struct http_server *http_server_init(const struct http_server_settings *set) { struct http_server *server; pool_t pool; pool = pool_alloconly_create("http server", 1024); server = p_new(pool, struct http_server, 1); server->pool = pool; if (set->rawlog_dir != NULL && *set->rawlog_dir != '\0') server->set.rawlog_dir = p_strdup(pool, set->rawlog_dir); server->set.max_client_idle_time_msecs = set->max_client_idle_time_msecs; server->set.max_pipelined_requests = (set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1); server->set.request_limits = set->request_limits; server->set.socket_send_buffer_size = set->socket_send_buffer_size; server->set.socket_recv_buffer_size = set->socket_recv_buffer_size; server->set.debug = set->debug; server->conn_list = http_server_connection_list_init(); return server; } void http_server_deinit(struct http_server **_server) { struct http_server *server = *_server; *_server = NULL; connection_list_deinit(&server->conn_list); if (server->ssl_ctx != NULL) ssl_iostream_context_deinit(&server->ssl_ctx); pool_unref(&server->pool); } void http_server_switch_ioloop(struct http_server *server) { struct connection *_conn = server->conn_list->connections; /* move connections */ /* FIXME: we wouldn't necessarily need to switch all of them immediately, only those that have requests now. but also connections that get new requests before ioloop is switched again.. */ for (; _conn != NULL; _conn = _conn->next) { struct http_server_connection *conn = (struct http_server_connection *)_conn; http_server_connection_switch_ioloop(conn); } } void http_server_shut_down(struct http_server *server) { struct connection *_conn, *_next; server->shutting_down = TRUE; for (_conn = server->conn_list->connections; _conn != NULL; _conn = _next) { struct http_server_connection *conn = (struct http_server_connection *)_conn; _next = _conn->next; (void)http_server_connection_shut_down(conn); } } dovecot-2.2.33.2/src/lib-http/http-header-parser.c0000644000175000017500000002261413165463624016521 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "str-sanitize.h" #include "http-parser.h" #include "http-header.h" #include "http-header-parser.h" enum http_header_parse_state { HTTP_HEADER_PARSE_STATE_INIT = 0, HTTP_HEADER_PARSE_STATE_NAME, HTTP_HEADER_PARSE_STATE_COLON, HTTP_HEADER_PARSE_STATE_OWS, HTTP_HEADER_PARSE_STATE_CONTENT, HTTP_HEADER_PARSE_STATE_CR, HTTP_HEADER_PARSE_STATE_LF, HTTP_HEADER_PARSE_STATE_NEW_LINE, HTTP_HEADER_PARSE_STATE_EOH }; struct http_header_parser { struct istream *input; struct http_header_limits limits; enum http_header_parse_flags flags; uoff_t size, field_size; unsigned int field_count; const unsigned char *begin, *cur, *end; const char *error; string_t *name; buffer_t *value_buf; enum http_header_parse_state state; }; struct http_header_parser * http_header_parser_init(struct istream *input, const struct http_header_limits *limits, enum http_header_parse_flags flags) { struct http_header_parser *parser; parser = i_new(struct http_header_parser, 1); parser->input = input; if (limits != NULL) parser->limits = *limits; if (parser->limits.max_size == 0) parser->limits.max_size = (uoff_t)-1; if (parser->limits.max_field_size == 0) parser->limits.max_field_size = (uoff_t)-1; if (parser->limits.max_fields == 0) parser->limits.max_fields = (unsigned int)-1; parser->flags = flags; parser->name = str_new(default_pool, 128); parser->value_buf = buffer_create_dynamic(default_pool, 4096); return parser; } void http_header_parser_deinit(struct http_header_parser **_parser) { struct http_header_parser *parser = *_parser; *_parser = NULL; //i_stream_skip(ctx->input, ctx->skip); buffer_free(&parser->value_buf); str_free(&parser->name); i_free(parser); } void http_header_parser_reset(struct http_header_parser *parser) { parser->state = HTTP_HEADER_PARSE_STATE_INIT; parser->size = 0; parser->field_size = 0; parser->field_count = 0; } static int http_header_parse_name(struct http_header_parser *parser) { const unsigned char *first = parser->cur; /* field-name = token token = 1*tchar */ while (parser->cur < parser->end && http_char_is_token(*parser->cur)) parser->cur++; str_append_n(parser->name, first, parser->cur-first); if (parser->cur == parser->end) return 0; if (str_len(parser->name) == 0) { parser->error = "Empty header field name"; return -1; } return 1; } static int http_header_parse_ows(struct http_header_parser *parser) { /* OWS = *( SP / HTAB ) ; "optional" whitespace */ while (parser->cur < parser->end && (*parser->cur == ' ' || *parser->cur == '\t')) parser->cur++; return (parser->cur == parser->end ? 0 : 1); } static int http_header_parse_content(struct http_header_parser *parser) { const unsigned char *first; /* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] field-vchar = VCHAR / obs-text */ do { first = parser->cur; while (parser->cur < parser->end && http_char_is_text(*parser->cur)) { parser->cur++; } buffer_append(parser->value_buf, first, parser->cur-first); if ((parser->flags & HTTP_HEADER_PARSE_FLAG_STRICT) != 0) break; /* We'll be lenient here to accommodate for some bad servers. We just drop offending characters */ while (parser->cur < parser->end && !http_char_is_text(*parser->cur) && (*parser->cur != '\r' && *parser->cur != '\n')) parser->cur++; } while (parser->cur < parser->end && (*parser->cur != '\r' && *parser->cur != '\n')); if (parser->cur == parser->end) return 0; return 1; } static inline const char *_chr_sanitize(unsigned char c) { if (c >= 0x20 && c < 0x7F) return t_strdup_printf("'%c'", c); return t_strdup_printf("0x%02x", c); } static int http_header_parse(struct http_header_parser *parser) { int ret; /* RFC 7230, Section 3.2: Header Fields 'header' = *( header-field CRLF ) CRLF ; Actually part of HTTP-message syntax header-field = field-name ":" OWS field-value OWS field-name = token field-value = *( field-content / obs-fold ) field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] field-vchar = VCHAR / obs-text obs-fold = CRLF 1*( SP / HTAB ) ; obsolete line folding ; see Section 3.2.4 */ for (;;) { switch (parser->state) { case HTTP_HEADER_PARSE_STATE_INIT: buffer_set_used_size(parser->value_buf, 0); str_truncate(parser->name, 0); if (*parser->cur == '\r') { /* last CRLF */ parser->cur++; parser->state = HTTP_HEADER_PARSE_STATE_EOH; if (parser->cur == parser->end) return 0; break; } else if (*parser->cur == '\n') { /* last LF */ parser->state = HTTP_HEADER_PARSE_STATE_EOH; break; } /* next line */ parser->state = HTTP_HEADER_PARSE_STATE_NAME; /* fall through */ case HTTP_HEADER_PARSE_STATE_NAME: if ((ret=http_header_parse_name(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_COLON; /* fall through */ case HTTP_HEADER_PARSE_STATE_COLON: if (*parser->cur != ':') { parser->error = t_strdup_printf ("Expected ':' after header field name '%s', but found %s", str_sanitize(str_c(parser->name),64), _chr_sanitize(*parser->cur)); return -1; } parser->cur++; if (str_len(parser->name) == 0) { parser->error = "Empty header field name"; return -1; } if (++parser->field_count > parser->limits.max_fields) { parser->error = "Excessive number of header fields"; return -1; } parser->state = HTTP_HEADER_PARSE_STATE_OWS; /* fall through */ case HTTP_HEADER_PARSE_STATE_OWS: if ((ret=http_header_parse_ows(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_CONTENT; /* fall through */ case HTTP_HEADER_PARSE_STATE_CONTENT: if ((ret=http_header_parse_content(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_CR; /* fall through */ case HTTP_HEADER_PARSE_STATE_CR: if (*parser->cur == '\r') { parser->cur++; } else if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Invalid character %s in content of header field '%s'", _chr_sanitize(*parser->cur), str_sanitize(str_c(parser->name),64)); return -1; } parser->state = HTTP_HEADER_PARSE_STATE_LF; if (parser->cur == parser->end) return 0; /* fall through */ case HTTP_HEADER_PARSE_STATE_LF: if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Expected LF after CR at end of header field '%s', but found %s", str_sanitize(str_c(parser->name),64), _chr_sanitize(*parser->cur)); return -1; } parser->cur++; parser->state = HTTP_HEADER_PARSE_STATE_NEW_LINE; if (parser->cur == parser->end) return 0; /* fall through */ case HTTP_HEADER_PARSE_STATE_NEW_LINE: if (*parser->cur == ' ' || *parser->cur == '\t') { /* obs-fold */ buffer_append_c(parser->value_buf, ' '); parser->state = HTTP_HEADER_PARSE_STATE_OWS; break; } /* next header line */ parser->state = HTTP_HEADER_PARSE_STATE_INIT; return 1; case HTTP_HEADER_PARSE_STATE_EOH: if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Encountered stray CR at beginning of header line, followed by %s", _chr_sanitize(*parser->cur)); return -1; } /* header fully parsed */ parser->cur++; return 1; default: i_unreached(); } } i_unreached(); return -1; } int http_header_parse_next_field(struct http_header_parser *parser, const char **name_r, const unsigned char **data_r, size_t *size_r, const char **error_r) { const uoff_t max_size = parser->limits.max_size; const uoff_t max_field_size = parser->limits.max_field_size; const unsigned char *data; size_t size; int ret; *error_r = NULL; while ((ret=i_stream_read_data (parser->input, &parser->begin, &size, 0)) > 0) { /* check header size limits */ if (parser->size >= max_size) { *error_r = "Excessive header size"; return -1; } if (parser->field_size > max_field_size) { *error_r = "Excessive header field size"; return -1; } /* don't parse beyond header size limits */ if (size > (max_size - parser->size)) size = max_size - parser->size; if (size > (max_field_size - parser->field_size)) { size = max_field_size - parser->field_size; size = (size == 0 ? 1 : size); /* need to parse one more byte */ } parser->cur = parser->begin; parser->end = parser->cur + size; if ((ret=http_header_parse(parser)) < 0) { *error_r = parser->error; return -1; } i_stream_skip(parser->input, parser->cur - parser->begin); parser->size += parser->cur - parser->begin; parser->field_size += parser->cur - parser->begin; if (ret == 1) { parser->field_size = 0; if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) { data = buffer_get_data(parser->value_buf, &size); /* trim trailing OWS */ while (size > 0 && (data[size-1] == ' ' || data[size-1] == '\t')) size--; *name_r = str_c(parser->name); *data_r = data; *size_r = size; parser->state = HTTP_HEADER_PARSE_STATE_INIT; } else { *name_r = NULL; *data_r = NULL; } return 1; } } i_assert(ret != -2); if (ret < 0) { if (i_stream_is_eof(parser->input)) *error_r = "Premature end of input"; else *error_r = "Stream error"; } return ret; } dovecot-2.2.33.2/src/lib-http/http-parser.h0000644000175000017500000000340513123174404015262 00000000000000#ifndef HTTP_PARSER_H #define HTTP_PARSER_H /* * Character definitions */ extern const unsigned char _http_token_char_mask; extern const unsigned char _http_value_char_mask; extern const unsigned char _http_text_char_mask; extern const unsigned char _http_qdtext_char_mask; extern const unsigned char _http_ctext_char_mask; extern const unsigned char _http_token68_char_mask; extern const unsigned char _http_char_lookup[256]; static inline bool http_char_is_token(unsigned char ch) { return (_http_char_lookup[ch] & _http_token_char_mask) != 0; } static inline bool http_char_is_value(unsigned char ch) { return (_http_char_lookup[ch] & _http_value_char_mask) != 0; } static inline bool http_char_is_text(unsigned char ch) { return (_http_char_lookup[ch] & _http_text_char_mask) != 0; } static inline bool http_char_is_qdtext(unsigned char ch) { return (_http_char_lookup[ch] & _http_qdtext_char_mask) != 0; } static inline bool http_char_is_ctext(unsigned char ch) { return (_http_char_lookup[ch] & _http_ctext_char_mask) != 0; } static inline bool http_char_is_token68(unsigned char ch) { return (_http_char_lookup[ch] & _http_token68_char_mask) != 0; } /* * HTTP value parsing */ struct http_parser { const unsigned char *begin, *cur, *end; }; void http_parser_init(struct http_parser *parser, const unsigned char *data, size_t size); void http_parse_ows(struct http_parser *parser); int http_parser_skip_token(struct http_parser *parser); int http_parse_token(struct http_parser *parser, const char **token_r); int http_parse_token_list_next(struct http_parser *parser, const char **token_r); int http_parse_quoted_string(struct http_parser *parser, const char **str_r); int http_parse_token_or_qstring(struct http_parser *parser, const char **word_r); #endif dovecot-2.2.33.2/src/lib-http/test-http-transfer.c0000644000175000017500000002243613165463624016602 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "str.h" #include "strfuncs.h" #include "str-sanitize.h" #include "istream.h" #include "ostream.h" #include "test-common.h" #include "http-transfer.h" #include struct http_transfer_chunked_input_test { const char *in; const char *out; }; /* Valid transfer_chunked input tests */ static struct http_transfer_chunked_input_test valid_transfer_chunked_input_tests[] = { { .in = "1E\r\n" "This is a simple test payload." "\r\n" "0\r\n" "\r\n", .out = "This is a simple test payload." }, { .in = "20\r\n" "This is a longer test payload..." "\r\n" "23\r\n" "...spread over two separate chunks." "\r\n" "0\r\n" "\r\n", .out = "This is a longer test payload..." "...spread over two separate chunks." }, { .in = "26\r\n" "This is an even longer test payload..." "\r\n" "27\r\n" "...spread over three separate chunks..." "\r\n" "1F\r\n" "...and also includes a trailer." "\r\n" "0\r\n" "Checksum: adgfef3fdaf3daf3dfaf3ff3fdag\r\n" "X-Dovecot: Whatever\r\n" "\r\n", .out = "This is an even longer test payload..." "...spread over three separate chunks..." "...and also includes a trailer." }, { .in = "26\n" "This is an even longer test payload..." "\n" "27\n" "...spread over three separate chunks..." "\n" "1F\n" "...and also includes a trailer." "\n" "0\n" "Checksum: adgfef3fdaf3daf3dfaf3ff3fdag\n" "X-Dovecot: Whatever\n" "\n", .out = "This is an even longer test payload..." "...spread over three separate chunks..." "...and also includes a trailer." } }; static unsigned int valid_transfer_chunked_input_test_count = N_ELEMENTS(valid_transfer_chunked_input_tests); static void test_http_transfer_chunked_input_valid(void) { struct istream *input, *chunked; struct ostream *output; buffer_t *payload_buffer; unsigned int i; payload_buffer = buffer_create_dynamic(default_pool, 1024); for (i = 0; i < valid_transfer_chunked_input_test_count; i++) T_BEGIN { const char *in, *out, *stream_out; in = valid_transfer_chunked_input_tests[i].in; out = valid_transfer_chunked_input_tests[i].out; test_begin(t_strdup_printf("http transfer_chunked input valid [%d]", i)); input = i_stream_create_from_data(in, strlen(in)); chunked = http_transfer_chunked_istream_create(input, 0); i_stream_unref(&input); buffer_set_used_size(payload_buffer, 0); output = o_stream_create_buffer(payload_buffer); test_out("payload read", o_stream_send_istream(output, chunked) > 0 && chunked->stream_errno == 0); o_stream_destroy(&output); i_stream_unref(&chunked); stream_out = str_c(payload_buffer); test_out(t_strdup_printf("response->payload = %s", str_sanitize(stream_out, 80)), strcmp(stream_out, out) == 0); test_end(); } T_END; buffer_free(&payload_buffer); } /* Invalid transfer_chunked input tests */ static const char * invalid_transfer_chunked_input_tests[] = { // invalid size "1X\r\n" "This is a simple test payload." "\r\n" "0\r\n" "\r\n", // invalid end "1E\r\n" "This is a simple test payload." "\r\n" "0\r\n" "ah\r\n", // invalid size "20\r\n" "This is a longer test payload..." "\r\n" "2q\r\n" "...spread over two separate chunks." "\r\n" "0\r\n" "\r\n", // invalid end "20\r\n" "This is a longer test payload..." "\r\n" "23\r\n" "...spread over two separate chunks." "\r\n" "0\r\n", // invalid last chunk "20\r\n" "This is a longer test payload..." "\r\n" "23\r\n" "...spread over two separate chunks." "\r\n" "4\r\n" "\r\n", // invalid trailer "26\r\n" "This is an even longer test payload..." "\r\n" "27\r\n" "...spread over three separate chunks..." "\r\n" "1F\r\n" "...and also includes a trailer." "\r\n" "0\r\n" "Checksum adgfef3fdaf3daf3dfaf3ff3fdag\r\n" "\r\n" }; static unsigned int invalid_transfer_chunked_input_test_count = N_ELEMENTS(invalid_transfer_chunked_input_tests); static void test_http_transfer_chunked_input_invalid(void) { struct istream *input, *chunked; struct ostream *output; buffer_t *payload_buffer; unsigned int i; payload_buffer = buffer_create_dynamic(default_pool, 1024); for (i = 0; i < invalid_transfer_chunked_input_test_count; i++) T_BEGIN { const char *in; in = invalid_transfer_chunked_input_tests[i]; test_begin(t_strdup_printf("http transfer_chunked input invalid [%d]", i)); input = i_stream_create_from_data(in, strlen(in)); chunked = http_transfer_chunked_istream_create(input, 0); i_stream_unref(&input); buffer_set_used_size(payload_buffer, 0); output = o_stream_create_buffer(payload_buffer); (void)o_stream_send_istream(output, chunked); test_out("payload read failure", chunked->stream_errno != 0); i_stream_unref(&chunked); o_stream_destroy(&output); test_end(); } T_END; buffer_free(&payload_buffer); } /* Valid transfer_chunked output tests */ static const char *valid_transfer_chunked_output_tests[] = { /* The maximum chunk size is set to 16. These tests are tuned to some border cases */ "A small payload", // 15 bytes "A longer payload", // 16 bytes "A lengthy payload", // 17 bytes /* Others */ "This is a test payload with lots of nonsense.", "Yet another payload.", "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " "This a very long repetitive payload. This a very long repetitive payload. " }; static unsigned int valid_transfer_chunked_output_test_count = N_ELEMENTS(valid_transfer_chunked_output_tests); static void test_http_transfer_chunked_output_valid(void) { struct istream *input, *ichunked; struct ostream *output, *ochunked; buffer_t *chunked_buffer, *plain_buffer; unsigned int i; chunked_buffer = buffer_create_dynamic(default_pool, 1024); plain_buffer = buffer_create_dynamic(default_pool, 1024); for (i = 0; i < valid_transfer_chunked_output_test_count; i++) T_BEGIN { const char *data, *stream_out; const unsigned char *rdata; size_t rsize; ssize_t ret; data = valid_transfer_chunked_output_tests[i]; test_begin(t_strdup_printf("http transfer_chunked output valid [%d]", i)); /* create input stream */ input = i_stream_create_from_data(data, strlen(data)); /* create buffer output stream */ buffer_set_used_size(chunked_buffer, 0); output = o_stream_create_buffer(chunked_buffer); /* create chunked output stream */ ochunked = http_transfer_chunked_ostream_create(output); /* send input through chunked stream; chunk size is limited */ for (;;) { ret = i_stream_read_data(input, &rdata, &rsize, 0); if (ret < 0) { if (input->eof) ret = 1; break; } if (rsize == 0) break; if (rsize > 16) rsize = 16; ret = o_stream_send(ochunked, rdata, rsize); if (ret < 0) break; if ((size_t)ret != rsize) { ret = -1; break; } i_stream_skip(input, ret); } /* cleanup streams */ test_out("payload chunk", ret > 0); o_stream_destroy(&ochunked); o_stream_destroy(&output); i_stream_destroy(&input); /* create chunked input stream */ input = i_stream_create_from_data (chunked_buffer->data, chunked_buffer->used); ichunked = http_transfer_chunked_istream_create(input, 0); /* read back chunk */ buffer_set_used_size(plain_buffer, 0); output = o_stream_create_buffer(plain_buffer); ret = o_stream_send_istream(output, ichunked); test_out("payload unchunk", ret >= 0 && ichunked->stream_errno == 0); o_stream_destroy(&output); i_stream_destroy(&ichunked); i_stream_destroy(&input); /* test output */ stream_out = str_c(plain_buffer); test_out(t_strdup_printf("response->payload = %s", str_sanitize(stream_out, 80)), strcmp(stream_out, data) == 0); test_end(); } T_END; buffer_free(&chunked_buffer); buffer_free(&plain_buffer); } int main(void) { static void (*test_functions[])(void) = { test_http_transfer_chunked_input_valid, test_http_transfer_chunked_input_invalid, test_http_transfer_chunked_output_valid, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/test-http-response-parser.c0000644000175000017500000002606213165463624020105 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "str.h" #include "str-sanitize.h" #include "istream.h" #include "ostream.h" #include "test-common.h" #include "http-response-parser.h" #include struct valid_parse_test_response { unsigned char version_major; unsigned char version_minor; unsigned int status; uoff_t content_length; const char *payload; }; struct valid_parse_test { const char *input; enum http_response_parse_flags flags; const struct valid_parse_test_response *responses; unsigned int responses_count; }; /* Valid response tests */ static const struct valid_parse_test_response valid_responses1[] = { { .status = 200, .payload = "This is a piece of stupid text.\r\n" } }; static const struct valid_parse_test_response valid_responses2[] = { { .status = 200, .payload = "This is a piece of stupid text.\r\n" },{ .status = 200, .payload = "This is a piece of even more stupid text.\r\n" } }; static const struct valid_parse_test_response valid_responses3[] = { { .status = 401, .payload = "Frop!" } }; static const struct valid_parse_test_response valid_responses4[] = { { .status = 200, .payload = "Invalid date header" } }; static const struct valid_parse_test_response valid_responses5[] = { { .status = 200, .payload = "Duplicate headers" } }; static const struct valid_parse_test valid_response_parse_tests[] = { { .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Oct 2012 13:02:27 GMT\r\n" "Server: Apache/2.2.16 (Debian)\r\n" "Last-Modified: Tue, 18 Sep 2012 19:31:41 GMT\r\n" "Etag: \"2a8400c-10751f-4c9fef0858140\"\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: 33\r\n" "Keep-Alive: timeout=15, max=100\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/plain\r\n" "\r\n" "This is a piece of stupid text.\r\n", .responses = valid_responses1, .responses_count = N_ELEMENTS(valid_responses1) },{ .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Oct 2012 13:02:27 GMT\r\n" "Server: Apache/2.2.16 (Debian)\r\n" "Last-Modified: Tue, 18 Sep 2012 19:31:41 GMT\r\n" "Etag: \"2a8400c-10751f-4c9fef0858140\"\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: 33\r\n" "Keep-Alive: timeout=15, max=100\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/plain\r\n" "\r\n" "This is a piece of stupid text.\r\n" "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Oct 2012 13:02:27 GMT\r\n" "Server: Apache/2.2.16 (Debian)\r\n" "Last-Modified: Tue, 18 Sep 2012 19:31:41 GMT\r\n" "Etag: \"2a8400c-10751f-4c9fef0858140\"\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: 43\r\n" "Keep-Alive: timeout=15, max=100\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/plain\r\n" "\r\n" "This is a piece of even more stupid text.\r\n", .responses = valid_responses2, .responses_count = N_ELEMENTS(valid_responses2) },{ .input = "HTTP/1.1 401 Authorization Required\r\n" "Date: Sun, 07 Oct 2012 19:52:03 GMT\r\n" "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14\r\n" "WWW-Authenticate: Basic realm=\"Munin\"\r\n" "Vary: Accept-Encoding\r\n" "Content-Encoding: gzip\r\n" "Content-Length: 5\r\n" "Keep-Alive: timeout=15, max=99\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "\r\n" "Frop!", .responses = valid_responses3, .responses_count = N_ELEMENTS(valid_responses3) },{ .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Ocu 2012 19:52:03 GMT\r\n" "Content-Length: 19\r\n" "Keep-Alive: timeout=15, max=99\r\n" "Connection: Keep-Alive\r\n" "Date: Sun, 13 Oct 2013 13:13:13 GMT\r\n" "\r\n" "Invalid date header", .responses = valid_responses4, .responses_count = N_ELEMENTS(valid_responses4) },{ .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Oct 2012 19:52:03 GMT\r\n" "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14\r\n" "Content-Length: 17\r\n" "Keep-Alive: timeout=15, max=99\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "Date: Sun, 13 Oct 2013 13:13:13 GMT\r\n" "\r\n" "Duplicate headers", .responses = valid_responses5, .responses_count = N_ELEMENTS(valid_responses5) } }; static const unsigned int valid_response_parse_test_count = N_ELEMENTS(valid_response_parse_tests); static void test_http_response_parse_valid(void) { unsigned int i; buffer_t *payload_buffer = buffer_create_dynamic(default_pool, 1024); for (i = 0; i < valid_response_parse_test_count; i++) T_BEGIN { struct istream *input; struct ostream *output; const struct valid_parse_test *test; const struct valid_parse_test_response *tresponse; struct http_response_parser *parser; struct http_response presponse; const char *input_text, *payload, *error; unsigned int j, pos, input_text_len; int ret = 0; i_zero(&presponse); test = &valid_response_parse_tests[i]; input_text = test->input; input_text_len = strlen(input_text); input = test_istream_create_data(input_text, input_text_len); parser = http_response_parser_init(input, NULL, valid_response_parse_tests[i].flags); test_begin(t_strdup_printf("http response valid [%d]", i)); payload = NULL; for (pos = 0; pos < input_text_len && ret == 0; pos++) { test_istream_set_size(input, pos); ret = http_response_parse_next(parser, HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, &presponse, &error); } test_istream_set_size(input, input_text_len); i_stream_unref(&input); j = 0; test_out("parse success", ret > 0); while (ret > 0) { if (presponse.payload != NULL) { buffer_set_used_size(payload_buffer, 0); output = o_stream_create_buffer(payload_buffer); test_out("payload receive", o_stream_send_istream(output, presponse.payload)); o_stream_destroy(&output); payload = str_c(payload_buffer); } else { payload = NULL; } test_assert(j < test->responses_count); if (j >= test->responses_count) break; tresponse = &test->responses[j]; /* verify last response only */ test_out(t_strdup_printf("response->status = %d", tresponse->status), presponse.status == tresponse->status); if (payload == NULL || tresponse->payload == NULL) { test_out(t_strdup_printf("response->payload = %s", str_sanitize(payload, 80)), payload == tresponse->payload); } else { test_out(t_strdup_printf("response->payload = %s", str_sanitize(payload, 80)), strcmp(payload, tresponse->payload) == 0); } ret = http_response_parse_next(parser, HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, &presponse, &error); if (++j == test->responses_count) test_out("parse end", ret == 0); else test_out("parse success", ret > 0); } test_assert(ret == 0); test_end(); http_response_parser_deinit(&parser); } T_END; buffer_free(&payload_buffer); } /* * Invalid response tests */ struct invalid_parse_test { const char *input; enum http_response_parse_flags flags; }; static struct invalid_parse_test invalid_response_parse_tests[] = { { .input = "XMPP/1.0 302 Found\r\n" "Location: http://www.example.nl/\r\n" "Cache-Control: private\r\n" },{ .input = "HTTP/1.1 302 Found\r\n" "Location: http://www.example.nl/\r\n" "Cache-Control: private\r\n" },{ .input = "HTTP/1.1 ABC Found\r\n" "Location: http://www.example.nl/\r\n" "Cache-Control: private\r\n" },{ .input = "HTTP/1.1 302 \177\r\n" "Location: http://www.example.nl/\r\n" "Cache-Control: private\r\n" },{ .input = "HTTP/1.1 302 Found\n\r" "Location: http://www.example.nl/\n\r" "Cache-Control: private\n\r" },{ .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Ocu 2012 19:52:03 GMT\r\n" "Content-Length: 19\r\n" "Keep-Alive: timeout=15, max=99\r\n" "Connection: Keep-Alive\r\n" "\r\n" "Invalid date header", .flags = HTTP_RESPONSE_PARSE_FLAG_STRICT },{ .input = "HTTP/1.1 200 OK\r\n" "Date: Sun, 07 Oct 2012 19:52:03 GMT\r\n" "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14\r\n" "Content-Length: 17\r\n" "Keep-Alive: timeout=15, max=99\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "Date: Sun, 13 Oct 2013 13:13:13 GMT\r\n" "\r\n" "Duplicate headers", .flags = HTTP_RESPONSE_PARSE_FLAG_STRICT } }; static const unsigned int invalid_response_parse_test_count = N_ELEMENTS(invalid_response_parse_tests); static void test_http_response_parse_invalid(void) { struct http_response_parser *parser; struct http_response response; const char *response_text, *error; struct istream *input; int ret; unsigned int i; for (i = 0; i < invalid_response_parse_test_count; i++) T_BEGIN { const char *test; test = invalid_response_parse_tests[i].input; response_text = test; input = i_stream_create_from_data(response_text, strlen(response_text)); parser = http_response_parser_init(input, NULL, invalid_response_parse_tests[i].flags); i_stream_unref(&input); test_begin(t_strdup_printf("http response invalid [%d]", i)); while ((ret=http_response_parse_next(parser, FALSE, &response, &error)) > 0); test_out_reason("parse failure", ret < 0, error); test_end(); http_response_parser_deinit(&parser); } T_END; } /* * Bad response tests */ static const unsigned char bad_response_with_nuls[] = "HTTP/1.1 200 OK\r\n" "Server: text\0server\r\n" "\r\n"; static void test_http_response_parse_bad(void) { struct http_response_parser *parser; struct http_response response; const char *header, *error; struct istream *input; int ret; /* parse failure guarantees http_response_header.size equals strlen(http_response_header.value) */ test_begin("http response with NULs (strict)"); input = i_stream_create_from_data(bad_response_with_nuls, sizeof(bad_response_with_nuls)-1); parser = http_response_parser_init(input, NULL, HTTP_RESPONSE_PARSE_FLAG_STRICT); i_stream_unref(&input); while ((ret=http_response_parse_next(parser, HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, &response, &error)) > 0); test_assert(ret < 0); http_response_parser_deinit(&parser); test_end(); /* even when lenient, bad characters like NUL must not be returned */ test_begin("http response with NULs (lenient)"); input = i_stream_create_from_data(bad_response_with_nuls, sizeof(bad_response_with_nuls)-1); parser = http_response_parser_init(input, NULL, 0); i_stream_unref(&input); ret = http_response_parse_next(parser, HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, &response, &error); test_out("parse success", ret > 0); header = http_response_header_get(&response, "server"); test_out("header present", header != NULL); if (header != NULL) { test_out(t_strdup_printf("header Server: %s", header), strcmp(header, "textserver") == 0); } ret = http_response_parse_next(parser, HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED, &response, &error); test_out("parse end", ret == 0); http_response_parser_deinit(&parser); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_http_response_parse_valid, test_http_response_parse_invalid, test_http_response_parse_bad, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/test-http-client-errors.c0000644000175000017500000022210013165463624017534 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "istream-chain.h" #include "ostream.h" #include "time-util.h" #include "connection.h" #include "test-common.h" #include "http-url.h" #include "http-request.h" #include "http-client.h" #include #include #include #include /* * Types */ struct server_connection { struct connection conn; pool_t pool; }; typedef void (*test_server_init_t)(unsigned int index); typedef bool (*test_client_init_t) (const struct http_client_settings *client_set); typedef void (*test_dns_init_t)(void); /* * State */ /* common */ static struct ip_addr bind_ip; static in_port_t *bind_ports = 0; static struct ioloop *ioloop; static bool debug = FALSE; /* dns */ static pid_t dns_pid = (pid_t)-1; /* server */ static struct io *io_listen; static int fd_listen = -1; static pid_t *server_pids = NULL; static unsigned int server_pids_count = 0; static struct connection_list *server_conn_list; static size_t server_read_max = 0; static unsigned int server_index; static void (*test_server_input)(struct server_connection *conn); /* client */ static struct http_client *http_client = NULL; /* * Forward declarations */ /* server */ static void test_server_run(unsigned int index); static void server_connection_deinit(struct server_connection **_conn); /* client */ static void test_client_defaults(struct http_client_settings *http_set); static void test_client_deinit(void); /* test*/ static void test_run_client_server( const struct http_client_settings *client_set, test_client_init_t client_test, test_server_init_t server_test, unsigned int server_tests_count, test_dns_init_t dns_test) ATTR_NULL(3); /* * Unconfigured SSL */ /* client */ struct _unconfigured_ssl { unsigned int count; }; static void test_client_unconfigured_ssl_response( const struct http_response *resp, struct _unconfigured_ssl *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_unconfigured_ssl(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _unconfigured_ssl *ctx; ctx = i_new(struct _unconfigured_ssl, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "127.0.0.1", "/unconfigured-ssl.txt", test_client_unconfigured_ssl_response, ctx); http_client_request_set_ssl(hreq, TRUE); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "127.0.0.1", "/unconfigured-ssl2.txt", test_client_unconfigured_ssl_response, ctx); http_client_request_set_ssl(hreq, TRUE); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_unconfigured_ssl(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("unconfigured ssl"); test_run_client_server(&http_client_set, test_client_unconfigured_ssl, NULL, 0, NULL); test_end(); } /* * Unconfigured SSL abort */ /* client */ struct _unconfigured_ssl_abort { unsigned int count; }; static void test_client_unconfigured_ssl_abort_response1( const struct http_response *resp, struct _unconfigured_ssl_abort *ctx ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_out_quiet("inappropriate callback", FALSE); } static void test_client_unconfigured_ssl_abort_response2( const struct http_response *resp, struct _unconfigured_ssl_abort *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); i_free(ctx); io_loop_stop(ioloop); } static bool test_client_unconfigured_ssl_abort(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _unconfigured_ssl_abort *ctx; ctx = i_new(struct _unconfigured_ssl_abort, 1); ctx->count = 1; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "127.0.0.1", "/unconfigured-ssl.txt", test_client_unconfigured_ssl_abort_response1, ctx); http_client_request_set_ssl(hreq, TRUE); http_client_request_submit(hreq); http_client_request_abort(&hreq); hreq = http_client_request(http_client, "GET", "127.0.0.1", "/unconfigured-ssl2.txt", test_client_unconfigured_ssl_abort_response2, ctx); http_client_request_set_ssl(hreq, TRUE); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_unconfigured_ssl_abort(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("unconfigured ssl abort"); test_run_client_server(&http_client_set, test_client_unconfigured_ssl_abort, NULL, 0, NULL); test_end(); } /* * Invalid URL */ /* client */ struct _invalid_url { unsigned int count; }; static void test_client_invalid_url_response( const struct http_response *resp, struct _invalid_url *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_INVALID_URL); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_invalid_url(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _invalid_url *ctx; ctx = i_new(struct _invalid_url, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request_url_str(http_client, "GET", "imap://example.com/INBOX", test_client_invalid_url_response, ctx); http_client_request_submit(hreq); hreq = http_client_request_url_str(http_client, "GET", "http:/www.example.com", test_client_invalid_url_response, ctx); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_invalid_url(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("invalid url"); test_run_client_server(&http_client_set, test_client_invalid_url, NULL, 0, NULL); test_end(); } /* * Host lookup failed */ /* client */ struct _host_lookup_failed { unsigned int count; }; static void test_client_host_lookup_failed_response( const struct http_response *resp, struct _host_lookup_failed *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_host_lookup_failed(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _host_lookup_failed *ctx; ctx = i_new(struct _host_lookup_failed, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "host.in-addr.arpa", "/host-lookup-failed.txt", test_client_host_lookup_failed_response, ctx); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "host.in-addr.arpa", "/host-lookup-failed2.txt", test_client_host_lookup_failed_response, ctx); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_host_lookup_failed(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("host lookup failed"); test_run_client_server(&http_client_set, test_client_host_lookup_failed, NULL, 0, NULL); test_end(); } /* * Connection refused */ /* server */ static void test_server_connection_refused(unsigned int index ATTR_UNUSED) { i_close_fd(&fd_listen); } /* client */ struct _connection_refused { unsigned int count; struct timeout *to; }; static void test_client_connection_refused_response( const struct http_response *resp, struct _connection_refused *ctx) { test_assert(ctx->to == NULL); if (ctx->to != NULL) timeout_remove(&ctx->to); if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static void test_client_connection_refused_timeout(struct _connection_refused *ctx) { if (debug) i_debug("TIMEOUT (ok)"); timeout_remove(&ctx->to); } static bool test_client_connection_refused(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _connection_refused *ctx; ctx = i_new(struct _connection_refused, 1); ctx->count = 2; if (client_set->max_connect_attempts > 0) { ctx->to = timeout_add_short(250, test_client_connection_refused_timeout, ctx); } http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-refused.txt", test_client_connection_refused_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-refused2.txt", test_client_connection_refused_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_connection_refused(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("connection refused"); test_run_client_server(&http_client_set, test_client_connection_refused, test_server_connection_refused, 1, NULL); test_end(); http_client_set.max_connect_attempts = 3; test_begin("connection refused backoff"); test_run_client_server(&http_client_set, test_client_connection_refused, test_server_connection_refused, 1, NULL); test_end(); } /* * Connection lost prematurely */ /* server */ static void test_server_connection_lost_prematurely_input(struct server_connection *conn) { server_connection_deinit(&conn); } static void test_server_connection_lost_prematurely(unsigned int index) { test_server_input = test_server_connection_lost_prematurely_input; test_server_run(index); } /* client */ struct _connection_lost_prematurely { unsigned int count; struct timeout *to; }; static void test_client_connection_lost_prematurely_response( const struct http_response *resp, struct _connection_lost_prematurely *ctx) { test_assert(ctx->to == NULL); if (ctx->to != NULL) timeout_remove(&ctx->to); if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static void test_client_connection_lost_prematurely_timeout( struct _connection_lost_prematurely *ctx) { if (debug) i_debug("TIMEOUT (ok)"); timeout_remove(&ctx->to); } static bool test_client_connection_lost_prematurely(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _connection_lost_prematurely *ctx; ctx = i_new(struct _connection_lost_prematurely, 1); ctx->count = 2; ctx->to = timeout_add_short(250, test_client_connection_lost_prematurely_timeout, ctx); http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-refused-retry.txt", test_client_connection_lost_prematurely_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-refused-retry2.txt", test_client_connection_lost_prematurely_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_connection_lost_prematurely(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.max_connect_attempts = 3; http_client_set.max_attempts = 3; test_begin("connection lost prematurely"); test_run_client_server(&http_client_set, test_client_connection_lost_prematurely, test_server_connection_lost_prematurely, 1, NULL); test_end(); } /* * Connection timed out */ /* client */ struct _connection_timed_out { unsigned int count; }; static void test_client_connection_timed_out_response( const struct http_response *resp, struct _connection_timed_out *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_connection_timed_out( const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _connection_timed_out *ctx; ctx = i_new(struct _connection_timed_out, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "192.168.0.0", "/connection-timed-out.txt", test_client_connection_timed_out_response, ctx); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "192.168.0.0", "/connection-timed-out2.txt", test_client_connection_timed_out_response, ctx); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_connection_timed_out(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.connect_timeout_msecs = 1000; http_client_set.max_attempts = 1; test_begin("connection timed out"); test_run_client_server(&http_client_set, test_client_connection_timed_out, NULL, 0, NULL); test_end(); } /* * Invalid redirect */ /* server */ /* -> not accepted */ static void test_invalid_redirect_input1(struct server_connection *conn) { static const char *resp = "HTTP/1.1 302 Redirect\r\n" "Location: http://localhost:4444\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_invalid_redirect1(unsigned int index) { test_server_input = test_invalid_redirect_input1; test_server_run(index); } /* -> bad location */ static void test_invalid_redirect_input2(struct server_connection *conn) { static const char *resp = "HTTP/1.1 302 Redirect\r\n" "Location: unix:/var/run/dovecot/auth-master\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_invalid_redirect2(unsigned int index) { test_server_input = test_invalid_redirect_input2; test_server_run(index); } /* -> too many */ static void test_invalid_redirect_input3(struct server_connection *conn) { string_t *resp; resp = t_str_new(512); str_printfa(resp, "HTTP/1.1 302 Redirect\r\n" "Location: http://%s:%u/friep.txt\r\n" "\r\n", net_ip2addr(&bind_ip), bind_ports[server_index+1]); o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp)); server_connection_deinit(&conn); } static void test_server_invalid_redirect3(unsigned int index) { test_server_input = test_invalid_redirect_input3; test_server_run(index); } /* client */ static void test_client_invalid_redirect_response( const struct http_response *resp, void *context ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT); test_assert(resp->reason != NULL && *resp->reason != '\0'); io_loop_stop(ioloop); } static bool test_client_invalid_redirect(const struct http_client_settings *client_set) { struct http_client_request *hreq; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/invalid-redirect.txt", test_client_invalid_redirect_response, NULL); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_invalid_redirect(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("invalid redirect: not accepted"); http_client_set.max_redirects = 0; test_run_client_server(&http_client_set, test_client_invalid_redirect, test_server_invalid_redirect1, 1, NULL); test_end(); test_begin("invalid redirect: bad location"); http_client_set.max_redirects = 1; test_run_client_server(&http_client_set, test_client_invalid_redirect, test_server_invalid_redirect2, 1, NULL); test_end(); test_begin("invalid redirect: too many"); http_client_set.max_redirects = 1; test_run_client_server(&http_client_set, test_client_invalid_redirect, test_server_invalid_redirect3, 3, NULL); test_end(); } /* * Unseekable redirect */ /* server */ static void test_unseekable_redirect_input(struct server_connection *conn) { string_t *resp; resp = t_str_new(512); str_printfa(resp, "HTTP/1.1 302 Redirect\r\n" "Location: http://%s:%u/frml.txt\r\n" "\r\n", net_ip2addr(&bind_ip), bind_ports[server_index+1]); o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp)); server_connection_deinit(&conn); } static void test_server_unseekable_redirect(unsigned int index) { test_server_input = test_unseekable_redirect_input; test_server_run(index); } /* client */ static void test_client_unseekable_redirect_response( const struct http_response *resp, void *context ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_ABORTED); test_assert(resp->reason != NULL && *resp->reason != '\0'); io_loop_stop(ioloop); } static bool test_client_unseekable_redirect(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct istream *input; http_client = http_client_init(client_set); input = i_stream_create_from_data("FROP", 4); input->seekable = FALSE; hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/unseekable-redirect.txt", test_client_unseekable_redirect_response, NULL); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, FALSE); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_unseekable_redirect(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.max_redirects = 1; test_begin("unseekable redirect"); test_run_client_server(&http_client_set, test_client_unseekable_redirect, test_server_unseekable_redirect, 2, NULL); test_end(); } /* * Unseekable retry */ /* server */ static void test_unseekable_retry_input(struct server_connection *conn) { server_connection_deinit(&conn); } static void test_server_unseekable_retry(unsigned int index) { test_server_input = test_unseekable_retry_input; test_server_run(index); } /* client */ static void test_client_unseekable_retry_response( const struct http_response *resp, void *context ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_ABORTED); test_assert(resp->reason != NULL && *resp->reason != '\0'); io_loop_stop(ioloop); } static bool test_client_unseekable_retry(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct istream *input; http_client = http_client_init(client_set); input = i_stream_create_from_data("FROP", 4); input->seekable = FALSE; hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/unseekable-retry.txt", test_client_unseekable_retry_response, NULL); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, FALSE); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_unseekable_retry(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.max_attempts = 3; test_begin("unseekable retry"); test_run_client_server(&http_client_set, test_client_unseekable_retry, test_server_unseekable_retry, 2, NULL); test_end(); } /* * Broken payload */ /* server */ static void test_broken_payload_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 200 OK\r\n" "Content-Length: 18\r\n" "\r\n" "Everything is OK\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_broken_payload(unsigned int index) { test_server_input = test_broken_payload_input; test_server_run(index); } /* client */ static void test_client_broken_payload_response( const struct http_response *resp, void *context ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD); test_assert(resp->reason != NULL && *resp->reason != '\0'); io_loop_stop(ioloop); } static bool test_client_broken_payload(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct istream *input; test_expect_errors(1); http_client = http_client_init(client_set); input = i_stream_create_error_str(EIO, "Moehahahaha!!"); i_stream_set_name(input, "PURE EVIL"); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/broken-payload.txt", test_client_broken_payload_response, NULL); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, FALSE); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_broken_payload(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("broken payload"); test_run_client_server(&http_client_set, test_client_broken_payload, test_server_broken_payload, 1, NULL); test_end(); } /* * Connection lost */ /* server */ static void test_connection_lost_input(struct server_connection *conn) { ssize_t ret; if (server_read_max == 0) { server_connection_deinit(&conn); return; } i_stream_set_max_buffer_size(conn->conn.input, server_read_max); ret = i_stream_read(conn->conn.input); if (ret == -2) { server_connection_deinit(&conn); return; } if (ret < 0) { if (i_stream_is_eof(conn->conn.input)) i_fatal("server: Client stream ended prematurely"); else i_fatal("server: Streem error: %s", i_stream_get_error(conn->conn.input)); } } static void test_server_connection_lost(unsigned int index) { test_server_input = test_connection_lost_input; test_server_run(index); } /* client */ struct _connection_lost_ctx { unsigned int count; }; struct _connection_lost_request_ctx { struct _connection_lost_ctx *ctx; struct http_client_request *req; }; static void test_client_connection_lost_response( const struct http_response *resp, struct _connection_lost_request_ctx *rctx) { struct _connection_lost_ctx *ctx = rctx->ctx; if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (http_client_request_try_retry(rctx->req)) { if (debug) i_debug("retrying"); return; } i_free(rctx); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_connection_lost(const struct http_client_settings *client_set) { static const char payload[] = "This is a useless payload that only serves as a means to give the " "server the opportunity to close the connection before the payload " "is finished."; struct _connection_lost_ctx *ctx; struct _connection_lost_request_ctx *rctx; struct http_client_request *hreq; struct istream *input; ctx = i_new(struct _connection_lost_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); input = i_stream_create_from_data(payload, sizeof(payload)-1); rctx = i_new(struct _connection_lost_request_ctx, 1); rctx->ctx = ctx; rctx->req = hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost.txt", test_client_connection_lost_response, rctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, FALSE); http_client_request_submit(hreq); rctx = i_new(struct _connection_lost_request_ctx, 1); rctx->ctx = ctx; rctx->req = hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost2.txt", test_client_connection_lost_response, rctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_connection_lost(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); server_read_max = 0; test_begin("connection lost: one attempt"); http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost, test_server_connection_lost, 1, NULL); test_end(); test_begin("connection lost: two attempts"); http_client_set.max_attempts = 2; test_run_client_server(&http_client_set, test_client_connection_lost, test_server_connection_lost, 1, NULL); test_end(); test_begin("connection lost: three attempts"); http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_connection_lost, test_server_connection_lost, 1, NULL); test_end(); test_begin("connection lost: manual retry"); http_client_set.max_attempts = 3; http_client_set.no_auto_retry = TRUE; test_run_client_server(&http_client_set, test_client_connection_lost, test_server_connection_lost, 1, NULL); test_end(); } /* * Connection lost after 100-continue */ /* server */ static void test_connection_lost_100_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 100 Continue\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_connection_lost_100(unsigned int index) { test_server_input = test_connection_lost_100_input; test_server_run(index); } /* client */ struct _connection_lost_100_ctx { unsigned int count; }; static void test_client_connection_lost_100_response( const struct http_response *resp, struct _connection_lost_100_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_connection_lost_100( const struct http_client_settings *client_set) { static const char payload[] = "This is a useless payload that only serves as a means to give the " "server the opportunity to close the connection before the payload " "is finished."; struct _connection_lost_100_ctx *ctx; struct http_client_request *hreq; struct istream *input; ctx = i_new(struct _connection_lost_100_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); input = i_stream_create_from_data(payload, sizeof(payload)-1); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost.txt", test_client_connection_lost_100_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, TRUE); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost2.txt", test_client_connection_lost_100_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, TRUE); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_connection_lost_100(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); server_read_max = 0; test_begin("connection lost after 100-continue"); http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost_100, test_server_connection_lost_100, 1, NULL); test_end(); } /* * Connection lost in sub-ioloop */ /* server */ static void test_connection_lost_sub_ioloop_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_connection_lost_sub_ioloop(unsigned int index) { test_server_input = test_connection_lost_sub_ioloop_input; test_server_run(index); } /* client */ struct _connection_lost_sub_ioloop_ctx { unsigned int count; }; static void test_client_connection_lost_sub_ioloop_response2( const struct http_response *resp, struct ioloop *sub_ioloop) { if (debug) i_debug("SUB-RESPONSE: %u %s", resp->status, resp->reason); io_loop_stop(sub_ioloop); } static void test_client_connection_lost_sub_ioloop_response( const struct http_response *resp, struct _connection_lost_sub_ioloop_ctx *ctx) { struct http_client_request *hreq; struct ioloop *sub_ioloop; if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == 200); test_assert(resp->reason != NULL && *resp->reason != '\0'); sub_ioloop = io_loop_create(); http_client_switch_ioloop(http_client); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost-sub-ioloop3.txt", test_client_connection_lost_sub_ioloop_response2, sub_ioloop); http_client_request_set_port(hreq, bind_ports[1]); http_client_request_submit(hreq); io_loop_run(sub_ioloop); io_loop_set_current(ioloop); http_client_switch_ioloop(http_client); io_loop_set_current(sub_ioloop); io_loop_destroy(&sub_ioloop); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_connection_lost_sub_ioloop( const struct http_client_settings *client_set) { static const char payload[] = "This is a useless payload that only serves as a means to give the " "server the opportunity to close the connection before the payload " "is finished."; struct _connection_lost_sub_ioloop_ctx *ctx; struct http_client_request *hreq; struct istream *input; ctx = i_new(struct _connection_lost_sub_ioloop_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); input = i_stream_create_from_data(payload, sizeof(payload)-1); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost-sub-ioloop.txt", test_client_connection_lost_sub_ioloop_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, TRUE); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/connection-lost-sub-ioloop2.txt", test_client_connection_lost_sub_ioloop_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_payload(hreq, input, TRUE); http_client_request_submit(hreq); i_stream_unref(&input); return TRUE; } /* test */ static void test_connection_lost_sub_ioloop(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); server_read_max = 0; test_begin("connection lost while running sub-ioloop"); http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost_sub_ioloop, test_server_connection_lost_sub_ioloop, 2, NULL); test_end(); } /* * Early success */ /* server */ static void test_early_success_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 200 OK\r\n" "Content-Length: 18\r\n" "\r\n" "Everything is OK\r\n"; usleep(200000); o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_early_success(unsigned int index) { test_server_input = test_early_success_input; test_server_run(index); } /* client */ struct _early_success_ctx { unsigned int count; }; static void test_client_early_success_response( const struct http_response *resp, struct _early_success_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); if (ctx->count == 2) test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE); else test_assert(resp->status == 200); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { io_loop_stop(ioloop); i_free(ctx); } } static bool test_client_early_success(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _early_success_ctx *ctx; string_t *payload; unsigned int i; ctx = i_new(struct _early_success_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/early-success.txt", test_client_early_success_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); T_BEGIN { struct istream_chain *chain; struct istream *input, *chain_input; payload = t_str_new(64*3000); for (i = 0; i < 3000; i++) { str_append(payload, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"); } chain_input = i_stream_create_chain(&chain); input = i_stream_create_copy_from_data (str_data(payload), str_len(payload)); i_stream_chain_append(chain, input); i_stream_unref(&input); http_client_request_set_payload(hreq, chain_input, FALSE); i_stream_unref(&chain_input); } T_END; http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/early-success2.txt", test_client_early_success_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_early_success(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.socket_send_buffer_size = 4096; test_begin("early succes"); test_run_client_server(&http_client_set, test_client_early_success, test_server_early_success, 1, NULL); test_end(); } /* * Bad response */ /* server */ static void test_bad_response_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 666 Really bad response\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_bad_response(unsigned int index) { test_server_input = test_bad_response_input; test_server_run(index); } /* client */ struct _bad_response_ctx { unsigned int count; }; static void test_client_bad_response_response( const struct http_response *resp, struct _bad_response_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_bad_response(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _bad_response_ctx *ctx; ctx = i_new(struct _bad_response_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/bad-response.txt", test_client_bad_response_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/bad-response2.txt", test_client_bad_response_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_bad_response(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("bad response"); test_run_client_server(&http_client_set, test_client_bad_response, test_server_bad_response, 1, NULL); test_end(); } /* * Request timed out */ /* server */ static void test_request_timed_out_input(struct server_connection *conn ATTR_UNUSED) { /* do nothing */ } static void test_server_request_timed_out(unsigned int index) { test_server_input = test_request_timed_out_input; test_server_run(index); } /* client */ struct _request_timed_out1_ctx { unsigned int count; }; static void test_client_request_timed_out1_response( const struct http_response *resp, struct _request_timed_out1_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_request_timed_out1(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _request_timed_out1_ctx *ctx; ctx = i_new(struct _request_timed_out1_ctx, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-timed-out1-1.txt", test_client_request_timed_out1_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-timed-out1-2.txt", test_client_request_timed_out1_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } struct _request_timed_out2_ctx { struct timeout *to; unsigned int count; unsigned int max_parallel_connections; }; static void test_client_request_timed_out2_timeout( struct _request_timed_out2_ctx *ctx) { if (ctx->to != NULL) timeout_remove(&ctx->to); i_debug("TIMEOUT"); } static void test_client_request_timed_out2_response( const struct http_response *resp, struct _request_timed_out2_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT); test_assert(resp->reason != NULL && *resp->reason != '\0'); test_assert(ctx->to != NULL); if (--ctx->count > 0) { if (ctx->to != NULL && ctx->max_parallel_connections <= 1) timeout_reset(ctx->to); } else { if (ctx->to != NULL) timeout_remove(&ctx->to); i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_request_timed_out2(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _request_timed_out2_ctx *ctx; ctx = i_new(struct _request_timed_out2_ctx, 1); ctx->count = 2; ctx->max_parallel_connections = client_set->max_parallel_connections; ctx->to = timeout_add(2000, test_client_request_timed_out2_timeout, ctx); http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-timed-out2-1.txt", test_client_request_timed_out2_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_attempt_timeout_msecs(hreq, 1000); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-timed-out2-2.txt", test_client_request_timed_out2_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_set_attempt_timeout_msecs(hreq, 1000); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_request_timed_out(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("request timed out: one attempt"); http_client_set.request_timeout_msecs = 1000; http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_request_timed_out1, test_server_request_timed_out, 1, NULL); test_end(); test_begin("request timed out: two attempts"); http_client_set.request_timeout_msecs = 1000; http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_request_timed_out1, test_server_request_timed_out, 1, NULL); test_end(); test_begin("request absolutely timed out"); http_client_set.request_timeout_msecs = 0; http_client_set.request_absolute_timeout_msecs = 2000; http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_request_timed_out1, test_server_request_timed_out, 1, NULL); test_end(); test_begin("request double timed out"); http_client_set.request_timeout_msecs = 500; http_client_set.request_absolute_timeout_msecs = 2000; http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_request_timed_out1, test_server_request_timed_out, 1, NULL); test_end(); test_begin("request timed out: specific timeout"); http_client_set.request_timeout_msecs = 3000; http_client_set.request_absolute_timeout_msecs = 0; http_client_set.max_attempts = 1; http_client_set.max_parallel_connections = 1; test_run_client_server(&http_client_set, test_client_request_timed_out2, test_server_request_timed_out, 1, NULL); test_end(); test_begin("request timed out: specific timeout (parallel)"); http_client_set.request_timeout_msecs = 3000; http_client_set.request_absolute_timeout_msecs = 0; http_client_set.max_attempts = 1; http_client_set.max_parallel_connections = 4; test_run_client_server(&http_client_set, test_client_request_timed_out2, test_server_request_timed_out, 1, NULL); test_end(); } /* * Request aborted early */ /* server */ static void test_request_aborted_early_input(struct server_connection *conn ATTR_UNUSED) { static const char *resp = "HTTP/1.1 404 Not Found\r\n" "\r\n"; /* wait one second to respon */ sleep(1); /* respond */ o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_request_aborted_early(unsigned int index) { test_server_input = test_request_aborted_early_input; test_server_run(index); } /* client */ struct _request_aborted_early_ctx { struct http_client_request *req1, *req2; struct timeout *to; }; static void test_client_request_aborted_early_response( const struct http_response *resp, struct _request_aborted_early_ctx *ctx ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); /* abort does not trigger callback */ test_assert(FALSE); } static void test_client_request_aborted_early_timeout( struct _request_aborted_early_ctx *ctx) { timeout_remove(&ctx->to); if (ctx->req1 != NULL) { /* abort early */ http_client_request_abort(&ctx->req1); /* sent */ http_client_request_abort(&ctx->req2); /* only queued */ /* wait a little for server to actually respond to an already aborted request */ ctx->to = timeout_add_short(1000, test_client_request_aborted_early_timeout, ctx); } else { /* all done */ i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_request_aborted_early( const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _request_aborted_early_ctx *ctx; ctx = i_new(struct _request_aborted_early_ctx, 1); http_client = http_client_init(client_set); hreq = ctx->req1 = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-aborted-early.txt", test_client_request_aborted_early_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = ctx->req2 = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-aborted-early2.txt", test_client_request_aborted_early_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); ctx->to = timeout_add_short(500, test_client_request_aborted_early_timeout, ctx); return TRUE; } /* test */ static void test_request_aborted_early(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("request aborted early"); test_run_client_server(&http_client_set, test_client_request_aborted_early, test_server_request_aborted_early, 1, NULL); test_end(); } /* * Request failed blocking */ /* server */ static void test_request_failed_blocking_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 500 Internal Server Error\r\n" "\r\n"; /* respond */ o_stream_nsend_str(conn->conn.output, resp); sleep(10); server_connection_deinit(&conn); } static void test_server_request_failed_blocking(unsigned int index) { test_server_input = test_request_failed_blocking_input; test_server_run(index); } /* client */ struct _request_failed_blocking_ctx { struct http_client_request *req; }; static void test_client_request_failed_blocking_response( const struct http_response *resp, struct _request_failed_blocking_ctx *ctx ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); i_assert(resp->status == 500); } static bool test_client_request_failed_blocking( const struct http_client_settings *client_set) { static const char *payload = "This a test payload!"; struct http_client_request *hreq; struct _request_failed_blocking_ctx *ctx; unsigned int n; string_t *data; data = str_new(default_pool, 1000000); for (n = 0; n < 50000; n++) str_append(data, payload); ctx = i_new(struct _request_failed_blocking_ctx, 1); http_client = http_client_init(client_set); hreq = ctx->req = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/request-failed-blocking.txt", test_client_request_failed_blocking_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); test_assert(http_client_request_send_payload(&hreq, str_data(data), str_len(data)) < 0); i_assert(hreq == NULL); str_free(&data); i_free(ctx); return FALSE; } /* test */ static void test_request_failed_blocking(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.socket_send_buffer_size = 4096; test_begin("request failed blocking"); test_run_client_server(&http_client_set, test_client_request_failed_blocking, test_server_request_failed_blocking, 1, NULL); test_end(); } /* * Client deinit early */ /* server */ static void test_client_deinit_early_input(struct server_connection *conn ATTR_UNUSED) { static const char *resp = "HTTP/1.1 404 Not Found\r\n" "\r\n"; /* wait one second to respon */ sleep(1); /* respond */ o_stream_nsend_str(conn->conn.output, resp); server_connection_deinit(&conn); } static void test_server_client_deinit_early(unsigned int index) { test_server_input = test_client_deinit_early_input; test_server_run(index); } /* client */ struct _client_deinit_early_ctx { struct timeout *to; }; static void test_client_client_deinit_early_response( const struct http_response *resp, struct _client_deinit_early_ctx *ctx ATTR_UNUSED) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); /* abort does not trigger callback */ test_assert(FALSE); } static void test_client_client_deinit_early_timeout( struct _client_deinit_early_ctx *ctx) { timeout_remove(&ctx->to); /* deinit early */ http_client_deinit(&http_client); /* all done */ i_free(ctx); io_loop_stop(ioloop); } static bool test_client_client_deinit_early(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _client_deinit_early_ctx *ctx; ctx = i_new(struct _client_deinit_early_ctx, 1); http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/client-deinit-early.txt", test_client_client_deinit_early_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/client-deinit-early2.txt", test_client_client_deinit_early_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); ctx->to = timeout_add_short(500, test_client_client_deinit_early_timeout, ctx); return TRUE; } /* test */ static void test_client_deinit_early(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); test_begin("client deinit early"); test_run_client_server(&http_client_set, test_client_client_deinit_early, test_server_client_deinit_early, 1, NULL); test_end(); } /* * Retry with delay */ /* server */ static void test_retry_with_delay_input(struct server_connection *conn) { string_t *resp; resp = t_str_new(512); str_printfa(resp, "HTTP/1.1 500 Internal Server Error\r\n" "\r\n"); o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp)); server_connection_deinit(&conn); } static void test_server_retry_with_delay(unsigned int index) { test_server_input = test_retry_with_delay_input; test_server_run(index); } /* client */ struct _client_retry_with_delay_ctx { struct http_client_request *req; unsigned int retries; struct timeval time; }; static void test_client_retry_with_delay_response( const struct http_response *resp, struct _client_retry_with_delay_ctx *ctx) { int real_delay, exp_delay; if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == 500); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (ctx->retries > 0) { /* check delay */ real_delay = timeval_diff_msecs(&ioloop_timeval, &ctx->time); exp_delay = (1 << (ctx->retries-1)) * 50; if (real_delay < exp_delay-2) { i_fatal("Retry delay is too short %d < %d", real_delay, exp_delay); } } http_client_request_delay_msecs(ctx->req, (1 << ctx->retries) * 50); ctx->time = ioloop_timeval; if (http_client_request_try_retry(ctx->req)) { ctx->retries++; if (debug) i_debug("retrying"); return; } i_free(ctx); io_loop_stop(ioloop); } static bool test_client_retry_with_delay(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _client_retry_with_delay_ctx *ctx; ctx = i_new(struct _client_retry_with_delay_ctx, 1); ctx->time = ioloop_timeval; http_client = http_client_init(client_set); ctx->req = hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/retry-with-delay.txt", test_client_retry_with_delay_response, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_retry_with_delay(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.max_attempts = 3; test_begin("retry with delay"); test_run_client_server(&http_client_set, test_client_retry_with_delay, test_server_retry_with_delay, 1, NULL); test_end(); } /* * DNS service failure */ /* client */ struct _dns_service_failure { unsigned int count; }; static void test_client_dns_service_failure_response( const struct http_response *resp, struct _dns_service_failure *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_dns_service_failure(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _dns_service_failure *ctx; ctx = i_new(struct _dns_service_failure, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "example.com", "/dns-service-failure.txt", test_client_dns_service_failure_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "example.com", "/dns-service-failure2.txt", test_client_dns_service_failure_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_dns_service_failure(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.dns_client_socket_path = "./frop"; test_begin("dns service failure"); test_run_client_server(&http_client_set, test_client_dns_service_failure, NULL, 0, NULL); test_end(); } /* * DNS timeout */ /* dns */ static void test_dns_timeout_input(struct server_connection *conn ATTR_UNUSED) { /* hang */ sleep(100); server_connection_deinit(&conn); } static void test_dns_dns_timeout(void) { test_server_input = test_dns_timeout_input; test_server_run(0); } /* client */ struct _dns_timeout { unsigned int count; }; static void test_client_dns_timeout_response( const struct http_response *resp, struct _dns_timeout *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_dns_timeout(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _dns_timeout *ctx; ctx = i_new(struct _dns_timeout, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "example.com", "/dns-timeout.txt", test_client_dns_timeout_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "example.com", "/dns-timeout2.txt", test_client_dns_timeout_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_dns_timeout(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.request_timeout_msecs = 2000; http_client_set.connect_timeout_msecs = 2000; http_client_set.dns_client_socket_path = "./dns-test"; test_begin("dns timeout"); test_run_client_server(&http_client_set, test_client_dns_timeout, NULL, 0, test_dns_dns_timeout); test_end(); } /* * DNS lookup failure */ /* dns */ static void test_dns_lookup_failure_input(struct server_connection *conn) { o_stream_nsend_str(conn->conn.output, t_strdup_printf("%d\n", EAI_FAIL)); server_connection_deinit(&conn); } static void test_dns_dns_lookup_failure(void) { test_server_input = test_dns_lookup_failure_input; test_server_run(0); } /* client */ struct _dns_lookup_failure { unsigned int count; }; static void test_client_dns_lookup_failure_response( const struct http_response *resp, struct _dns_lookup_failure *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static bool test_client_dns_lookup_failure(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _dns_lookup_failure *ctx; ctx = i_new(struct _dns_lookup_failure, 1); ctx->count = 2; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "example.com", "/dns-lookup-failure.txt", test_client_dns_lookup_failure_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", "example.com", "/dns-lookup-failure2.txt", test_client_dns_lookup_failure_response, ctx); http_client_request_set_port(hreq, 80); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_dns_lookup_failure(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.dns_client_socket_path = "./dns-test"; test_begin("dns lookup failure"); test_run_client_server(&http_client_set, test_client_dns_lookup_failure, NULL, 0, test_dns_dns_lookup_failure); test_end(); } /* * DNS lookup ttl */ /* dns */ static void test_dns_lookup_ttl_input(struct server_connection *conn) { static unsigned int count = 0; const char *line; while ((line=i_stream_read_next_line(conn->conn.input)) != NULL) { if (debug) i_debug("DNS REQUEST %u: %s", count, line); if (count == 0) { o_stream_nsend_str(conn->conn.output, "0 1\n127.0.0.1\n"); } else { o_stream_nsend_str(conn->conn.output, t_strdup_printf("%d\n", EAI_FAIL)); if (count > 4) { server_connection_deinit(&conn); return; } } count++; } } static void test_dns_dns_lookup_ttl(void) { test_server_input = test_dns_lookup_ttl_input; test_server_run(0); } /* server */ static void test_server_dns_lookup_ttl_input(struct server_connection *conn) { string_t *resp; resp = t_str_new(512); str_printfa(resp, "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "\r\n"); o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp)); server_connection_deinit(&conn); } static void test_server_dns_lookup_ttl(unsigned int index) { test_server_input = test_server_dns_lookup_ttl_input; test_server_run(index); } /* client */ struct _dns_lookup_ttl { struct http_client *client; unsigned int count; struct timeout *to; }; static void test_client_dns_lookup_ttl_response_stage2( const struct http_response *resp, struct _dns_lookup_ttl *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); if (--ctx->count == 0) { i_free(ctx); io_loop_stop(ioloop); } } static void test_client_dns_lookup_ttl_stage2_start(struct _dns_lookup_ttl *ctx) { struct http_client_request *hreq; timeout_remove(&ctx->to); ctx->count = 2; hreq = http_client_request(ctx->client, "GET", "example.com", "/dns-lookup-ttl-stage2.txt", test_client_dns_lookup_ttl_response_stage2, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(ctx->client, "GET", "example.com", "/dns-lookup-ttl2-stage2.txt", test_client_dns_lookup_ttl_response_stage2, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); } static void test_client_dns_lookup_ttl_response_stage1( const struct http_response *resp, struct _dns_lookup_ttl *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == 200); if (--ctx->count == 0) { ctx->to = timeout_add(2000, test_client_dns_lookup_ttl_stage2_start, ctx); } } static bool test_client_dns_lookup_ttl(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _dns_lookup_ttl *ctx; ctx = i_new(struct _dns_lookup_ttl, 1); ctx->count = 2; ctx->client = http_client = http_client_init(client_set); hreq = http_client_request(ctx->client, "GET", "example.com", "/dns-lookup-ttl-stage1.txt", test_client_dns_lookup_ttl_response_stage1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(ctx->client, "GET", "example.com", "/dns-lookup-ttl2-stage1.txt", test_client_dns_lookup_ttl_response_stage1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_dns_lookup_ttl(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.dns_client_socket_path = "./dns-test"; http_client_set.dns_ttl_msecs = 1000; test_begin("dns lookup ttl"); test_run_client_server(&http_client_set, test_client_dns_lookup_ttl, test_server_dns_lookup_ttl, 1, test_dns_dns_lookup_ttl); test_end(); } /* * Peer reuse failure */ /* server */ static void test_peer_reuse_failure_input(struct server_connection *conn) { static unsigned int seq = 0; static const char *resp = "HTTP/1.1 200 OK\r\n" "\r\n"; o_stream_nsend_str(conn->conn.output, resp); if (seq++ > 2) { server_connection_deinit(&conn); io_loop_stop(current_ioloop); } } static void test_server_peer_reuse_failure(unsigned int index) { test_server_input = test_peer_reuse_failure_input; test_server_run(index); } /* client */ struct _peer_reuse_failure { struct timeout *to; bool first:1; }; static void test_client_peer_reuse_failure_response2( const struct http_response *resp, struct _peer_reuse_failure *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); i_free(ctx); io_loop_stop(ioloop); } static void test_client_peer_reuse_failure_next(struct _peer_reuse_failure *ctx) { struct http_client_request *hreq; timeout_remove(&ctx->to); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse-next.txt", test_client_peer_reuse_failure_response2, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); } static void test_client_peer_reuse_failure_response1( const struct http_response *resp, struct _peer_reuse_failure *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); if (ctx->first) { test_assert(resp->status == 200); ctx->first = FALSE; ctx->to = timeout_add_short(500, test_client_peer_reuse_failure_next, ctx); } else { test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); } test_assert(resp->reason != NULL && *resp->reason != '\0'); } static bool test_client_peer_reuse_failure(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _peer_reuse_failure *ctx; ctx = i_new(struct _peer_reuse_failure, 1); ctx->first = TRUE; http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt", test_client_peer_reuse_failure_response1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt", test_client_peer_reuse_failure_response1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); hreq = http_client_request(http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt", test_client_peer_reuse_failure_response1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_peer_reuse_failure(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.max_connect_attempts = 1; http_client_set.max_idle_time_msecs = 500; test_begin("peer reuse failure"); test_run_client_server(&http_client_set, test_client_peer_reuse_failure, test_server_peer_reuse_failure, 1, NULL); test_end(); } /* * Reconnect failure */ /* dns */ static void test_dns_reconnect_failure_input(struct server_connection *conn) { static unsigned int count = 0; const char *line; while ((line=i_stream_read_next_line(conn->conn.input)) != NULL) { if (debug) i_debug("DNS REQUEST %u: %s", count, line); if (count == 0) { o_stream_nsend_str(conn->conn.output, "0 1\n127.0.0.1\n"); } else { o_stream_nsend_str(conn->conn.output, t_strdup_printf("%d\n", EAI_FAIL)); if (count > 4) { server_connection_deinit(&conn); return; } } count++; } } static void test_dns_reconnect_failure(void) { test_server_input = test_dns_reconnect_failure_input; test_server_run(0); } /* server */ static void test_reconnect_failure_input(struct server_connection *conn) { static const char *resp = "HTTP/1.1 200 OK\r\n" "Content-Length: 18\r\n" "\r\n" "Everything is OK\r\n"; o_stream_nsend_str(conn->conn.output, resp); i_close_fd(&fd_listen); sleep(500); } static void test_server_reconnect_failure(unsigned int index) { test_server_input = test_reconnect_failure_input; test_server_run(index); } /* client */ struct _reconnect_failure_ctx { struct timeout *to; }; static void test_client_reconnect_failure_response2( const struct http_response *resp, struct _reconnect_failure_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED); test_assert(resp->reason != NULL && *resp->reason != '\0'); io_loop_stop(ioloop); i_free(ctx); } static void test_client_reconnect_failure_next( struct _reconnect_failure_ctx *ctx) { struct http_client_request *hreq; if (debug) i_debug("NEXT REQUEST"); timeout_remove(&ctx->to); hreq = http_client_request(http_client, "GET", "example.com", "/reconnect-failure-2.txt", test_client_reconnect_failure_response2, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); } static void test_client_reconnect_failure_response1( const struct http_response *resp, struct _reconnect_failure_ctx *ctx) { if (debug) i_debug("RESPONSE: %u %s", resp->status, resp->reason); test_assert(resp->status == 200); test_assert(resp->reason != NULL && *resp->reason != '\0'); ctx->to = timeout_add_short(999, test_client_reconnect_failure_next, ctx); } static bool test_client_reconnect_failure(const struct http_client_settings *client_set) { struct http_client_request *hreq; struct _reconnect_failure_ctx *ctx; ctx = i_new(struct _reconnect_failure_ctx, 1); http_client = http_client_init(client_set); hreq = http_client_request(http_client, "GET", "example.com", "/reconnect-failure-1.txt", test_client_reconnect_failure_response1, ctx); http_client_request_set_port(hreq, bind_ports[0]); http_client_request_submit(hreq); return TRUE; } /* test */ static void test_reconnect_failure(void) { struct http_client_settings http_client_set; test_client_defaults(&http_client_set); http_client_set.dns_client_socket_path = "./dns-test"; http_client_set.dns_ttl_msecs = 2000; http_client_set.max_idle_time_msecs = 1000; http_client_set.max_attempts = 1; http_client_set.request_timeout_msecs = 1000; test_begin("reconnect failure"); test_run_client_server(&http_client_set, test_client_reconnect_failure, test_server_reconnect_failure, 1, test_dns_reconnect_failure); test_end(); } /* * All tests */ static void (*test_functions[])(void) = { test_unconfigured_ssl, test_unconfigured_ssl_abort, test_invalid_url, test_host_lookup_failed, test_connection_refused, test_connection_lost_prematurely, test_connection_timed_out, test_invalid_redirect, test_unseekable_redirect, test_unseekable_retry, test_broken_payload, test_connection_lost, test_connection_lost_100, test_connection_lost_sub_ioloop, test_early_success, test_bad_response, test_request_timed_out, test_request_aborted_early, test_request_failed_blocking, test_client_deinit_early, test_retry_with_delay, test_dns_service_failure, test_dns_timeout, test_dns_lookup_failure, test_dns_lookup_ttl, test_peer_reuse_failure, test_reconnect_failure, NULL }; /* * Test client */ static void test_client_defaults(struct http_client_settings *http_set) { /* client settings */ i_zero(http_set); http_set->max_idle_time_msecs = 5*1000; http_set->max_parallel_connections = 1; http_set->max_pipelined_requests = 1; http_set->max_redirects = 0; http_set->max_attempts = 1; http_set->debug = debug; } static void test_client_deinit(void) { if (http_client != NULL) http_client_deinit(&http_client); http_client = NULL; } /* * Test server */ /* client connection */ static void server_connection_input(struct connection *_conn) { struct server_connection *conn = (struct server_connection *)_conn; test_server_input(conn); } static void server_connection_init(int fd) { struct server_connection *conn; pool_t pool; net_set_nonblock(fd, TRUE); pool = pool_alloconly_create("server connection", 256); conn = p_new(pool, struct server_connection, 1); conn->pool = pool; connection_init_server (server_conn_list, &conn->conn, "server connection", fd, fd); } static void server_connection_deinit(struct server_connection **_conn) { struct server_connection *conn = *_conn; *_conn = NULL; connection_deinit(&conn->conn); pool_unref(&conn->pool); } static void server_connection_destroy(struct connection *_conn) { struct server_connection *conn = (struct server_connection *)_conn; server_connection_deinit(&conn); } static void server_connection_accept(void *context ATTR_UNUSED) { int fd; /* accept new client */ fd = net_accept(fd_listen, NULL, NULL); if (fd == -1) return; if (fd == -2) { i_fatal("test server: accept() failed: %m"); } server_connection_init(fd); } /* */ static struct connection_settings server_connection_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs server_connection_vfuncs = { .destroy = server_connection_destroy, .input = server_connection_input }; static void test_server_run(unsigned int index) { server_index = index; /* open server socket */ io_listen = io_add(fd_listen, IO_READ, server_connection_accept, (void *)NULL); server_conn_list = connection_list_init (&server_connection_set, &server_connection_vfuncs); io_loop_run(ioloop); /* close server socket */ io_remove(&io_listen); connection_list_deinit(&server_conn_list); } /* * Tests */ static int test_open_server_fd(in_port_t *bind_port) { int fd = net_listen(&bind_ip, bind_port, 128); if (debug) i_debug("server listening on %u", *bind_port); if (fd == -1) { i_fatal("listen(%s:%u) failed: %m", net_ip2addr(&bind_ip), *bind_port); } return fd; } static void test_servers_kill_all(void) { unsigned int i; if (server_pids_count > 0) { for (i = 0; i < server_pids_count; i++) { if (server_pids[i] != (pid_t)-1) { (void)kill(server_pids[i], SIGKILL); (void)waitpid(server_pids[i], NULL, 0); server_pids[i] = -1; } } } server_pids_count = 0; if (dns_pid != (pid_t)-1) { (void)kill(dns_pid, SIGKILL); (void)waitpid(dns_pid, NULL, 0); dns_pid = (pid_t)-1; } } static void test_run_client_server( const struct http_client_settings *client_set, test_client_init_t client_test, test_server_init_t server_test, unsigned int server_tests_count, test_dns_init_t dns_test) { unsigned int i; server_pids = NULL; server_pids_count = 0; if (server_tests_count > 0) { int fds[server_tests_count]; bind_ports = i_new(in_port_t, server_tests_count); server_pids = i_new(pid_t, server_tests_count); for (i = 0; i < server_tests_count; i++) server_pids[i] = (pid_t)-1; server_pids_count = server_tests_count; for (i = 0; i < server_tests_count; i++) fds[i] = test_open_server_fd(&bind_ports[i]); for (i = 0; i < server_tests_count; i++) { fd_listen = fds[i]; if ((server_pids[i] = fork()) == (pid_t)-1) i_fatal("fork() failed: %m"); if (server_pids[i] == 0) { server_pids[i] = (pid_t)-1; server_pids_count = 0; hostpid_init(); if (debug) i_debug("server[%d]: PID=%s", i+1, my_pid); /* child: server */ ioloop = io_loop_create(); server_test(i); io_loop_destroy(&ioloop); if (fd_listen != -1) i_close_fd(&fd_listen); i_free(bind_ports); i_free(server_pids); /* wait for it to be killed; this way, valgrind will not object to this process going away inelegantly. */ sleep(60); exit(1); } if (fd_listen != -1) i_close_fd(&fd_listen); } if (debug) i_debug("client: PID=%s", my_pid); } if (dns_test != NULL) { int fd; i_unlink_if_exists("./dns-test"); fd = net_listen_unix("./dns-test", 128); if (fd == -1) { i_fatal("listen(./dns-test) failed: %m"); } fd_listen = fd; if ((dns_pid = fork()) == (pid_t)-1) i_fatal("fork() failed: %m"); if (dns_pid == 0) { dns_pid = (pid_t)-1; hostpid_init(); if (debug) i_debug("dns server: PID=%s", my_pid); /* child: server */ ioloop = io_loop_create(); dns_test(); io_loop_destroy(&ioloop); if (fd_listen != -1) i_close_fd(&fd_listen); /* wait for it to be killed; this way, valgrind will not object to this process going away inelegantly. */ sleep(60); exit(1); } if (fd_listen != -1) i_close_fd(&fd_listen); } /* parent: client */ usleep(100000); /* wait a little for server setup */ ioloop = io_loop_create(); if (client_test(client_set)) io_loop_run(ioloop); test_client_deinit(); io_loop_destroy(&ioloop); test_servers_kill_all(); i_free(server_pids); i_free(bind_ports); i_unlink_if_exists("./dns-test"); } /* * Main */ volatile sig_atomic_t terminating = 0; static void test_signal_handler(int signo) { if (terminating) raise(signo); terminating = 1; /* make sure we don't leave any pesky children alive */ test_servers_kill_all(); (void)signal(signo, SIG_DFL); raise(signo); } static void test_atexit(void) { test_servers_kill_all(); } int main(int argc, char *argv[]) { int c; atexit(test_atexit); (void)signal(SIGCHLD, SIG_IGN); (void)signal(SIGTERM, test_signal_handler); (void)signal(SIGQUIT, test_signal_handler); (void)signal(SIGINT, test_signal_handler); (void)signal(SIGSEGV, test_signal_handler); (void)signal(SIGABRT, test_signal_handler); while ((c = getopt(argc, argv, "D")) > 0) { switch (c) { case 'D': debug = TRUE; break; default: i_fatal("Usage: %s [-D]", argv[0]); } } /* listen on localhost */ i_zero(&bind_ip); bind_ip.family = AF_INET; bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK); test_run(test_functions); } dovecot-2.2.33.2/src/lib-http/http-request-parser.h0000644000175000017500000000330113147010711016737 00000000000000#ifndef HTTP_REQUEST_PARSER_H #define HTTP_REQUEST_PARSER_H #include "http-request.h" enum http_request_parse_error { HTTP_REQUEST_PARSE_ERROR_NONE = 0, /* no error */ HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM, /* stream error */ HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST, /* unrecoverable generic error */ HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST, /* recoverable generic error */ HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature (recoverable) */ HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED, /* unknown item in Expect: header (recoverable) */ HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG, /* method too long (fatal) */ HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG, /* target too long (fatal) */ HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE /* payload too large (fatal) */ }; enum http_request_parse_flags { /* Strictly adhere to the HTTP protocol specification */ HTTP_REQUEST_PARSE_FLAG_STRICT = BIT(0) }; struct http_request_parser * http_request_parser_init(struct istream *input, const struct http_request_limits *limits, enum http_request_parse_flags flags) ATTR_NULL(2); void http_request_parser_deinit(struct http_request_parser **_parser); int http_request_parse_finish_payload( struct http_request_parser *parser, enum http_request_parse_error *error_code_r, const char **error_r); int http_request_parse_next(struct http_request_parser *parser, pool_t pool, struct http_request *request, enum http_request_parse_error *error_code_r, const char **error_r); bool http_request_parser_pending_payload(struct http_request_parser *parser); #endif dovecot-2.2.33.2/src/lib-http/http-server-request.c0000644000175000017500000003217213165463624016773 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "ostream.h" #include "istream-private.h" #include "http-server-private.h" /* * Logging */ static inline void http_server_request_debug(struct http_server_request *req, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_request_debug(struct http_server_request *req, const char *format, ...) { struct http_server *server = req->server; va_list args; if (server->set.debug) { va_start(args, format); i_debug("http-server: request %s: %s", http_server_request_label(req), t_strdup_vprintf(format, args)); va_end(args); } } /* * Request */ struct http_server_request * http_server_request_new(struct http_server_connection *conn) { static unsigned int id_counter = 0; pool_t pool; struct http_server_request *req; pool = pool_alloconly_create(MEMPOOL_GROWING"http_server_request", 4096); req = p_new(pool, struct http_server_request, 1); req->pool = pool; req->refcount = 1; req->conn = conn; req->server = conn->server; req->id = ++id_counter; http_server_connection_add_request(conn, req); return req; } void http_server_request_ref(struct http_server_request *req) { i_assert(req->refcount > 0); req->refcount++; } bool http_server_request_unref(struct http_server_request **_req) { struct http_server_request *req = *_req; struct http_server_connection *conn = req->conn; i_assert(req->refcount > 0); *_req = NULL; if (--req->refcount > 0) return TRUE; http_server_request_debug(req, "Free"); if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) { req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; http_server_connection_remove_request(conn, req); } if (req->destroy_callback != NULL) { req->destroy_callback(req->destroy_context); req->destroy_callback = NULL; } if (req->response != NULL) http_server_response_free(req->response); pool_unref(&req->pool); return FALSE; } void http_server_request_destroy(struct http_server_request **_req) { struct http_server_request *req = *_req; struct http_server *server = req->server; http_server_request_debug(req, "Destroy"); /* just make sure the request ends in a proper state */ if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; if (server->ioloop) io_loop_stop(server->ioloop); if (req->delay_destroy) { req->destroy_pending = TRUE; } else if (req->destroy_callback != NULL) { void (*callback)(void *) = req->destroy_callback; req->destroy_callback = NULL; callback(req->destroy_context); } http_server_request_unref(_req); } void http_server_request_set_destroy_callback(struct http_server_request *req, void (*callback)(void *), void *context) { req->destroy_callback = callback; req->destroy_context = context; } void http_server_request_abort(struct http_server_request **_req, const char *reason) { struct http_server_request *req = *_req; struct http_server_connection *conn = req->conn; if (req->state >= HTTP_SERVER_REQUEST_STATE_FINISHED) return; http_server_request_debug(req, "Abort"); req->conn = NULL; if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) { if (conn != NULL) { http_server_connection_remove_request(conn, req); if (!conn->closed) { /* send best-effort response if appropriate */ if (!conn->output_locked && req->state >= HTTP_SERVER_REQUEST_STATE_PROCESSING && req->state < HTTP_SERVER_REQUEST_STATE_SENT_RESPONSE) { static const char *response = "HTTP/1.1 500 Internal Server Error\r\n" "Content-Length: 0\r\n" "\r\n"; (void)o_stream_send(conn->conn.output, response, strlen(response)); } /* close the connection */ http_server_connection_close(&conn, reason); } } req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; } if (req->response != NULL && !req->response->payload_blocking) { http_server_response_free(req->response); req->response = NULL; } http_server_request_destroy(_req); } const struct http_request * http_server_request_get(struct http_server_request *req) { return &req->req; } pool_t http_server_request_get_pool(struct http_server_request *req) { return req->pool; } struct http_server_response * http_server_request_get_response(struct http_server_request *req) { return req->response; } int http_server_request_get_auth(struct http_server_request *req, struct http_auth_credentials *credentials) { const char *auth; auth = http_request_header_get(&req->req, "Authorization"); if (auth == NULL) return 0; if (http_auth_parse_credentials ((const unsigned char *)auth, strlen(auth), credentials) < 0) return -1; return 1; } bool http_server_request_is_finished(struct http_server_request *req) { return req->response != NULL || req->state == HTTP_SERVER_REQUEST_STATE_ABORTED; } void http_server_request_halt_payload(struct http_server_request *req) { i_assert(req->state <= HTTP_SERVER_REQUEST_STATE_QUEUED); req->payload_halted = TRUE; } void http_server_request_continue_payload(struct http_server_request *req) { i_assert(req->state <= HTTP_SERVER_REQUEST_STATE_QUEUED); req->payload_halted = FALSE; if (req->req.expect_100_continue && !req->sent_100_continue) http_server_connection_trigger_responses(req->conn); } void http_server_request_ready_to_respond(struct http_server_request *req) { http_server_request_debug(req, "Ready to respond"); req->state = HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND; http_server_connection_trigger_responses(req->conn); } void http_server_request_submit_response(struct http_server_request *req) { struct http_server_connection *conn = req->conn; i_assert(conn != NULL && req->response != NULL && req->response->submitted); switch (req->state) { case HTTP_SERVER_REQUEST_STATE_NEW: case HTTP_SERVER_REQUEST_STATE_QUEUED: case HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN: case HTTP_SERVER_REQUEST_STATE_PROCESSING: if (!http_server_request_is_complete(req)) { http_server_request_debug(req, "Not ready to respond"); req->state = HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE; break; } http_server_request_ready_to_respond(req); break; case HTTP_SERVER_REQUEST_STATE_ABORTED: break; default: i_unreached(); } } void http_server_request_finished(struct http_server_request *req) { struct http_server_connection *conn = req->conn; struct http_server_response *resp = req->response; http_server_tunnel_callback_t tunnel_callback = resp->tunnel_callback; void *tunnel_context = resp->tunnel_context; http_server_request_debug(req, "Finished"); i_assert(req->state < HTTP_SERVER_REQUEST_STATE_FINISHED); req->state = HTTP_SERVER_REQUEST_STATE_FINISHED; http_server_connection_remove_request(conn, req); conn->stats.response_count++; if (tunnel_callback == NULL && (req->req.connection_close || resp->close)) { if (resp->close) { http_server_connection_close(&conn, t_strdup_printf("Server closed connection: %u %s", resp->status, resp->reason)); } else { http_server_connection_close(&conn, "Client requested connection close"); } http_server_request_destroy(&req); return; } http_server_request_destroy(&req); if (tunnel_callback != NULL) { http_server_connection_tunnel(&conn, tunnel_callback, tunnel_context); return; } http_server_connection_trigger_responses(conn); } static struct http_server_response * http_server_request_create_fail_response(struct http_server_request *req, unsigned int status, const char *reason) { struct http_server_response *resp; req->failed = TRUE; i_assert(status / 100 != 1 && status != 204 && status != 304); resp = http_server_response_create(req, status, reason); if (!http_request_method_is(&req->req, "HEAD")) { http_server_response_add_header (resp, "Content-Type", "text/plain; charset=utf-8"); reason = t_strconcat(reason, "\r\n", NULL); http_server_response_set_payload_data (resp, (const unsigned char *)reason, strlen(reason)); } return resp; } static void http_server_request_fail_full(struct http_server_request *req, unsigned int status, const char *reason, bool close) { struct http_server_response *resp; req->failed = TRUE; resp = http_server_request_create_fail_response(req, status, reason); if (close) http_server_response_submit_close(resp); else http_server_response_submit(resp); } void http_server_request_fail(struct http_server_request *req, unsigned int status, const char *reason) { http_server_request_fail_full(req, status, reason, req->conn->input_broken); } void http_server_request_fail_close(struct http_server_request *req, unsigned int status, const char *reason) { http_server_request_fail_full(req, status, reason, TRUE); } void http_server_request_fail_auth(struct http_server_request *req, const char *reason, const struct http_auth_challenge *chlng) { struct http_server_response *resp; req->failed = TRUE; if (reason == NULL) reason = "Unauthenticated"; resp = http_server_request_create_fail_response(req, 401, reason); http_server_response_add_auth(resp, chlng); http_server_response_submit(resp); } void http_server_request_fail_auth_basic(struct http_server_request *req, const char *reason, const char *realm) { struct http_auth_challenge chlng; http_auth_basic_challenge_init(&chlng, realm); http_server_request_fail_auth(req, reason, &chlng); } /* * Payload input stream */ struct http_server_istream { struct istream_private istream; struct http_server_request *req; ssize_t read_status; }; static void http_server_istream_switch_ioloop(struct istream_private *stream) { struct http_server_istream *hsristream = (struct http_server_istream *)stream; if (hsristream->istream.istream.blocking) return; http_server_connection_switch_ioloop(hsristream->req->conn); } static void http_server_istream_read_any(struct http_server_istream *hsristream) { struct istream_private *stream = &hsristream->istream; struct http_server *server = hsristream->req->server; ssize_t ret; if ((ret=i_stream_read_copy_from_parent (&stream->istream)) > 0) { hsristream->read_status = ret; io_loop_stop(server->ioloop); } } static ssize_t http_server_istream_read(struct istream_private *stream) { struct http_server_istream *hsristream = (struct http_server_istream *)stream; struct http_server_request *req = hsristream->req; struct http_server *server; struct http_server_connection *conn; bool blocking = stream->istream.blocking; ssize_t ret; if (req == NULL) { /* request already gone (we shouldn't get here) */ stream->istream.stream_errno = EINVAL; return -1; } i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); server = hsristream->req->server; conn = hsristream->req->conn; ret = i_stream_read_copy_from_parent(&stream->istream); if (ret == 0 && blocking) { struct ioloop *prev_ioloop = current_ioloop; struct io *io; http_server_connection_ref(conn); http_server_request_ref(req); i_assert(server->ioloop == NULL); server->ioloop = io_loop_create(); http_server_connection_switch_ioloop(conn); if (blocking && req->req.expect_100_continue && !req->sent_100_continue) http_server_connection_trigger_responses(conn); hsristream->read_status = 0; io = io_add_istream(&stream->istream, http_server_istream_read_any, hsristream); while (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED && hsristream->read_status == 0) { io_loop_run(server->ioloop); } io_remove(&io); io_loop_set_current(prev_ioloop); http_server_connection_switch_ioloop(conn); io_loop_set_current(server->ioloop); io_loop_destroy(&server->ioloop); ret = hsristream->read_status; if (!http_server_request_unref(&req)) hsristream->req = NULL; http_server_connection_unref(&conn); } return ret; } static void http_server_istream_destroy(struct iostream_private *stream) { struct http_server_istream *hsristream = (struct http_server_istream *)stream; uoff_t v_offset; v_offset = hsristream->istream.parent_start_offset + hsristream->istream.istream.v_offset; if (hsristream->istream.parent->seekable || v_offset > hsristream->istream.parent->v_offset) { /* get to same position in parent stream */ i_stream_seek(hsristream->istream.parent, v_offset); } i_stream_unref(&hsristream->istream.parent); } struct istream * http_server_request_get_payload_input(struct http_server_request *req, bool blocking) { struct http_server_istream *hsristream; struct istream *payload = req->req.payload; i_assert(req->payload_input == NULL); hsristream = i_new(struct http_server_istream, 1); hsristream->req = req; hsristream->istream.max_buffer_size = payload->real_stream->max_buffer_size; hsristream->istream.stream_size_passthrough = TRUE; hsristream->istream.read = http_server_istream_read; hsristream->istream.switch_ioloop = http_server_istream_switch_ioloop; hsristream->istream.iostream.destroy = http_server_istream_destroy; hsristream->istream.istream.readable_fd = FALSE; hsristream->istream.istream.blocking = blocking; hsristream->istream.istream.seekable = FALSE; req->payload_input = i_stream_create (&hsristream->istream, payload, i_stream_get_fd(payload)); i_stream_unref(&req->req.payload); return req->payload_input; } dovecot-2.2.33.2/src/lib-http/Makefile.am0000644000175000017500000001121013123174404014665 00000000000000noinst_LTLIBRARIES = libhttp.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master libhttp_la_SOURCES = \ http-date.c \ http-url.c \ http-parser.c \ http-header.c \ http-header-parser.c \ http-transfer-chunked.c \ http-auth.c \ http-message-parser.c \ http-request.c \ http-request-parser.c \ http-response.c \ http-response-parser.c \ http-client-request.c \ http-client-connection.c \ http-client-peer.c \ http-client-queue.c \ http-client-host.c \ http-client.c \ http-server-response.c \ http-server-request.c \ http-server-connection.c \ http-server.c headers = \ http-date.h \ http-url.h \ http-parser.h \ http-header.h \ http-header-parser.h \ http-transfer.h \ http-auth.h \ http-message-parser.h \ http-request.h \ http-request-parser.h \ http-response.h \ http-response-parser.h \ http-client-private.h \ http-client.h \ http-server-private.h \ http-server.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-http-date \ test-http-url \ test-http-header-parser \ test-http-transfer \ test-http-auth \ test-http-response-parser \ test-http-request-parser test_nocheck_programs = \ test-http-payload \ test-http-client \ test-http-client-errors \ test-http-server \ test-http-server-errors noinst_PROGRAMS = $(test_programs) $(test_nocheck_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la \ $(MODULE_LIBS) test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_http_url_SOURCES = test-http-url.c test_http_url_LDADD = http-url.lo http-header.lo $(test_libs) test_http_url_DEPENDENCIES = $(test_deps) test_http_date_SOURCES = test-http-date.c test_http_date_LDADD = http-date.lo $(test_libs) test_http_date_DEPENDENCIES = $(test_deps) test_http_header_parser_SOURCES = test-http-header-parser.c test_http_header_parser_LDADD = http-parser.lo http-header-parser.lo http-header.lo $(test_libs) test_http_header_parser_DEPENDENCIES = $(test_deps) test_http_transfer_SOURCES = test-http-transfer.c test_http_transfer_LDADD = \ http-parser.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-header.lo \ $(test_libs) test_http_transfer_DEPENDENCIES = $(test_deps) test_http_auth_SOURCES = test-http-auth.c test_http_auth_LDADD = \ http-auth.lo \ http-parser.lo \ $(test_libs) test_http_auth_DEPENDENCIES = $(test_deps) test_http_response_parser_SOURCES = test-http-response-parser.c test_http_response_parser_LDADD = \ http-date.lo \ http-parser.lo \ http-header.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-message-parser.lo \ http-response-parser.lo \ $(test_libs) test_http_response_parser_DEPENDENCIES = $(test_deps) test_http_request_parser_SOURCES = test-http-request-parser.c test_http_request_parser_LDADD = \ http-date.lo \ http-parser.lo \ http-url.lo \ http-header.lo \ http-header-parser.lo \ http-transfer-chunked.lo \ http-message-parser.lo \ http-request-parser.lo \ $(test_libs) test_http_request_parser_DEPENDENCIES = $(test_deps) test_http_libs = \ libhttp.la \ ../lib-dns/libdns.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-master/libmaster.la \ ../lib-settings/libsettings.la \ $(test_libs) test_http_deps = \ libhttp.la \ ../lib-dns/libdns.la \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-master/libmaster.la \ ../lib-settings/libsettings.la \ $(test_deps) test_http_payload_SOURCES = test-http-payload.c test_http_payload_LDFLAGS = -export-dynamic test_http_payload_LDADD = \ $(test_http_libs) test_http_payload_DEPENDENCIES = \ $(test_http_deps) test_http_client_SOURCES = test-http-client.c test_http_client_LDFLAGS = -export-dynamic test_http_client_LDADD = \ $(test_http_libs) \ ../lib-ssl-iostream/libssl_iostream_openssl.la test_http_client_DEPENDENCIES = \ $(test_http_deps) test_http_client_errors_SOURCES = test-http-client-errors.c test_http_client_errors_LDFLAGS = -export-dynamic test_http_client_errors_LDADD = \ $(test_http_libs) test_http_client_errors_DEPENDENCIES = \ $(test_http_deps) test_http_server_SOURCES = test-http-server.c test_http_server_LDFLAGS = -export-dynamic test_http_server_LDADD = \ $(test_http_libs) test_http_server_DEPENDENCIES = \ $(test_http_deps) test_http_server_errors_SOURCES = test-http-server-errors.c test_http_server_errors_LDFLAGS = -export-dynamic test_http_server_errors_LDADD = \ $(test_http_libs) test_http_server_errors_DEPENDENCIES = \ $(test_http_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-http/http-client-request.c0000644000175000017500000013300013165463624016733 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "llist.h" #include "time-util.h" #include "istream.h" #include "ostream.h" #include "file-lock.h" #include "dns-lookup.h" #include "http-url.h" #include "http-date.h" #include "http-auth.h" #include "http-response-parser.h" #include "http-transfer.h" #include "http-client-private.h" const char *http_request_state_names[] = { "new", "queued", "payload_out", "waiting", "got_response", "payload_in", "finished", "aborted" }; /* * Logging */ static inline void http_client_request_debug(struct http_client_request *req, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_request_debug(struct http_client_request *req, const char *format, ...) { va_list args; if (req->client->set.debug) { va_start(args, format); i_debug("http-client: request %s: %s", http_client_request_label(req), t_strdup_vprintf(format, args)); va_end(args); } } /* * Request */ static bool http_client_request_send_error(struct http_client_request *req, unsigned int status, const char *error); const char * http_client_request_label(struct http_client_request *req) { if (req->label == NULL) { req->label = p_strdup_printf(req->pool, "[Req%u: %s %s%s]", req->id, req->method, http_url_create_host(&req->origin_url), req->target); } return req->label; } static struct http_client_request * http_client_request_new(struct http_client *client, const char *method, http_client_request_callback_t *callback, void *context) { static unsigned int id_counter = 0; pool_t pool; struct http_client_request *req; pool = pool_alloconly_create("http client request", 2048); req = p_new(pool, struct http_client_request, 1); req->pool = pool; req->refcount = 1; req->client = client; req->id = ++id_counter; req->method = p_strdup(pool, method); req->callback = callback; req->context = context; req->date = (time_t)-1; /* default to client-wide settings: */ req->max_attempts = client->set.max_attempts; req->attempt_timeout_msecs = client->set.request_timeout_msecs; req->state = HTTP_REQUEST_STATE_NEW; return req; } #undef http_client_request struct http_client_request * http_client_request(struct http_client *client, const char *method, const char *host, const char *target, http_client_request_callback_t *callback, void *context) { struct http_client_request *req; req = http_client_request_new(client, method, callback, context); req->origin_url.host_name = p_strdup(req->pool, host); req->target = (target == NULL ? "/" : p_strdup(req->pool, target)); return req; } #undef http_client_request_url struct http_client_request * http_client_request_url(struct http_client *client, const char *method, const struct http_url *target_url, http_client_request_callback_t *callback, void *context) { struct http_client_request *req; req = http_client_request_new(client, method, callback, context); http_url_copy_authority(req->pool, &req->origin_url, target_url); req->target = p_strdup(req->pool, http_url_create_target(target_url)); if (target_url->user != NULL && *target_url->user != '\0' && target_url->password != NULL) { req->username = p_strdup(req->pool, target_url->user); req->password = p_strdup(req->pool, target_url->password); } return req; } #undef http_client_request_url_str struct http_client_request * http_client_request_url_str(struct http_client *client, const char *method, const char *url_str, http_client_request_callback_t *callback, void *context) { struct http_client_request *req, *tmpreq; struct http_url *target_url; const char *error; req = tmpreq = http_client_request_new (client, method, callback, context); if (http_url_parse(url_str, NULL, HTTP_URL_ALLOW_USERINFO_PART, req->pool, &target_url, &error) < 0) { req->label = p_strdup_printf(req->pool, "[Req%u: %s %s]", req->id, req->method, url_str); http_client_request_error(&tmpreq, HTTP_CLIENT_REQUEST_ERROR_INVALID_URL, t_strdup_printf("Invalid HTTP URL: %s", error)); return req; } req->origin_url = *target_url; req->target = p_strdup(req->pool, http_url_create_target(target_url)); if (target_url->user != NULL && *target_url->user != '\0' && target_url->password != NULL) { req->username = p_strdup(req->pool, target_url->user); req->password = p_strdup(req->pool, target_url->password); } return req; } #undef http_client_request_connect struct http_client_request * http_client_request_connect(struct http_client *client, const char *host, in_port_t port, http_client_request_callback_t *callback, void *context) { struct http_client_request *req; req = http_client_request_new(client, "CONNECT", callback, context); req->origin_url.host_name = p_strdup(req->pool, host); req->origin_url.port = port; req->origin_url.have_port = TRUE; req->connect_tunnel = TRUE; req->target = req->origin_url.host_name; return req; } #undef http_client_request_connect_ip struct http_client_request * http_client_request_connect_ip(struct http_client *client, const struct ip_addr *ip, in_port_t port, http_client_request_callback_t *callback, void *context) { struct http_client_request *req; const char *hostname = net_ip2addr(ip); req = http_client_request_connect (client, hostname, port, callback, context); req->origin_url.host_ip = *ip; req->origin_url.have_host_ip = TRUE; return req; } static void http_client_request_add(struct http_client_request *req) { struct http_client *client = req->client; DLLIST_PREPEND(&client->requests_list, req); client->requests_count++; req->listed = TRUE; } static void http_client_request_remove(struct http_client_request *req) { struct http_client *client = req->client; if (req->listed) { /* only decrease pending request counter if this request was submitted */ DLLIST_REMOVE(&client->requests_list, req); client->requests_count--; } req->listed = FALSE; if (client->requests_count == 0 && client->ioloop != NULL) io_loop_stop(client->ioloop); } void http_client_request_ref(struct http_client_request *req) { i_assert(req->refcount > 0); req->refcount++; } bool http_client_request_unref(struct http_client_request **_req) { struct http_client_request *req = *_req; struct http_client *client = req->client; i_assert(req->refcount > 0); *_req = NULL; if (--req->refcount > 0) return TRUE; http_client_request_debug(req, "Free (requests left=%d)", client->requests_count); /* cannot be destroyed while it is still pending */ i_assert(req->conn == NULL); if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (req->destroy_callback != NULL) { req->destroy_callback(req->destroy_context); req->destroy_callback = NULL; } http_client_request_remove(req); if (client->requests_count == 0 && client->ioloop != NULL) io_loop_stop(client->ioloop); if (req->delayed_error != NULL) http_client_remove_request_error(req->client, req); if (req->payload_input != NULL) i_stream_unref(&req->payload_input); if (req->payload_output != NULL) o_stream_unref(&req->payload_output); if (req->headers != NULL) str_free(&req->headers); pool_unref(&req->pool); return FALSE; } void http_client_request_destroy(struct http_client_request **_req) { struct http_client_request *req = *_req; struct http_client *client = req->client; *_req = NULL; http_client_request_debug(req, "Destroy (requests left=%d)", client->requests_count); if (req->state < HTTP_REQUEST_STATE_FINISHED) req->state = HTTP_REQUEST_STATE_ABORTED; req->callback = NULL; if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (req->delayed_error != NULL) http_client_remove_request_error(req->client, req); if (req->destroy_callback != NULL) { void (*callback)(void *) = req->destroy_callback; req->destroy_callback = NULL; callback(req->destroy_context); } if (req->conn != NULL) http_client_connection_request_destroyed(req->conn, req); http_client_request_remove(req); http_client_request_unref(&req); } void http_client_request_set_port(struct http_client_request *req, in_port_t port) { i_assert(req->state == HTTP_REQUEST_STATE_NEW); req->origin_url.port = port; req->origin_url.have_port = TRUE; } void http_client_request_set_ssl(struct http_client_request *req, bool ssl) { i_assert(req->state == HTTP_REQUEST_STATE_NEW); req->origin_url.have_ssl = ssl; } void http_client_request_set_urgent(struct http_client_request *req) { i_assert(req->state == HTTP_REQUEST_STATE_NEW); req->urgent = TRUE; } void http_client_request_set_preserve_exact_reason(struct http_client_request *req) { req->preserve_exact_reason = TRUE; } void http_client_request_add_header(struct http_client_request *req, const char *key, const char *value) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || /* allow calling for retries */ req->state == HTTP_REQUEST_STATE_GOT_RESPONSE || req->state == HTTP_REQUEST_STATE_ABORTED); /* make sure key or value can't break HTTP headers entirely */ i_assert(strpbrk(key, ":\r\n") == NULL); i_assert(strpbrk(value, "\r\n") == NULL); /* mark presence of special headers */ switch (key[0]) { case 'a': case 'A': if (strcasecmp(key, "Authorization") == 0) req->have_hdr_authorization = TRUE; break; case 'c': case 'C': if (strcasecmp(key, "Connection") == 0) req->have_hdr_connection = TRUE; else if (strcasecmp(key, "Content-Length") == 0) req->have_hdr_body_spec = TRUE; break; case 'd': case 'D': if (strcasecmp(key, "Date") == 0) req->have_hdr_date = TRUE; break; case 'e': case 'E': if (strcasecmp(key, "Expect") == 0) req->have_hdr_expect = TRUE; break; case 'h': case 'H': if (strcasecmp(key, "Host") == 0) req->have_hdr_host = TRUE; break; case 'p': case 'P': i_assert(strcasecmp(key, "Proxy-Authorization") != 0); break; case 't': case 'T': if (strcasecmp(key, "Transfer-Encoding") == 0) req->have_hdr_body_spec = TRUE; break; case 'u': case 'U': if (strcasecmp(key, "User-Agent") == 0) req->have_hdr_user_agent = TRUE; break; } if (req->headers == NULL) req->headers = str_new(default_pool, 256); str_printfa(req->headers, "%s: %s\r\n", key, value); } void http_client_request_remove_header(struct http_client_request *req, const char *key) { const unsigned char *data, *p; size_t size, line_len, line_start_pos; size_t key_len = strlen(key); i_assert(req->state == HTTP_REQUEST_STATE_NEW || /* allow calling for retries */ req->state == HTTP_REQUEST_STATE_GOT_RESPONSE || req->state == HTTP_REQUEST_STATE_ABORTED); data = str_data(req->headers); size = str_len(req->headers); while ((p = memchr(data, '\n', size)) != NULL) { line_len = (p+1) - data; if (size > key_len && i_memcasecmp(data, key, key_len) == 0 && data[key_len] == ':' && data[key_len+1] == ' ') { /* key was found from header, replace its value */ line_start_pos = str_len(req->headers) - size; str_delete(req->headers, line_start_pos, line_len); break; } size -= line_len; data += line_len; } } void http_client_request_set_date(struct http_client_request *req, time_t date) { i_assert(req->state == HTTP_REQUEST_STATE_NEW); req->date = date; } void http_client_request_set_payload(struct http_client_request *req, struct istream *input, bool sync) { int ret; i_assert(req->state == HTTP_REQUEST_STATE_NEW); i_assert(req->payload_input == NULL); i_stream_ref(input); req->payload_input = input; if ((ret = i_stream_get_size(input, TRUE, &req->payload_size)) <= 0) { if (ret < 0) { i_error("i_stream_get_size(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); } req->payload_size = 0; req->payload_chunked = TRUE; } req->payload_offset = input->v_offset; /* prepare request payload sync using 100 Continue response from server */ if ((req->payload_chunked || req->payload_size > 0) && sync) req->payload_sync = TRUE; } void http_client_request_set_payload_data(struct http_client_request *req, const unsigned char *data, size_t size) { struct istream *input; unsigned char *payload_data; if (size == 0) return; payload_data = p_malloc(req->pool, size); memcpy(payload_data, data, size); input = i_stream_create_from_data(payload_data, size); http_client_request_set_payload(req, input, FALSE); i_stream_unref(&input); } void http_client_request_set_timeout_msecs(struct http_client_request *req, unsigned int msecs) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->timeout_msecs = msecs; } void http_client_request_set_timeout(struct http_client_request *req, const struct timeval *time) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->timeout_time = *time; req->timeout_msecs = 0; } void http_client_request_set_attempt_timeout_msecs(struct http_client_request *req, unsigned int msecs) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->attempt_timeout_msecs = msecs; } void http_client_request_set_max_attempts(struct http_client_request *req, unsigned int max_attempts) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->max_attempts = max_attempts; } void http_client_request_set_auth_simple(struct http_client_request *req, const char *username, const char *password) { req->username = p_strdup(req->pool, username); req->password = p_strdup(req->pool, password); } void http_client_request_set_proxy_url(struct http_client_request *req, const struct http_url *proxy_url) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->host_url = http_url_clone_authority(req->pool, proxy_url); req->host_socket = NULL; } void http_client_request_set_proxy_socket(struct http_client_request *req, const char *proxy_socket) { i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); req->host_socket = p_strdup(req->pool, proxy_socket); req->host_url = NULL; } void http_client_request_delay_until(struct http_client_request *req, time_t time) { req->release_time.tv_sec = time; req->release_time.tv_usec = 0; } void http_client_request_delay(struct http_client_request *req, time_t seconds) { req->release_time = ioloop_timeval; req->release_time.tv_sec += seconds; } void http_client_request_delay_msecs(struct http_client_request *req, unsigned int msecs) { req->release_time = ioloop_timeval; timeval_add_msecs(&req->release_time, msecs); } int http_client_request_delay_from_response(struct http_client_request *req, const struct http_response *response) { time_t retry_after = response->retry_after; unsigned int max; if (retry_after == (time_t)-1) return 0; /* no delay */ if (retry_after < ioloop_time) return 0; /* delay already expired */ max = (req->client->set.max_auto_retry_delay == 0 ? req->attempt_timeout_msecs / 1000 : req->client->set.max_auto_retry_delay); if ((unsigned int)(retry_after - ioloop_time) > max) return -1; /* delay too long */ req->release_time.tv_sec = retry_after; req->release_time.tv_usec = 0; return 1; /* valid delay */ } const char * http_client_request_get_method(const struct http_client_request *req) { return req->method; } const char * http_client_request_get_target(const struct http_client_request *req) { return req->target; } enum http_request_state http_client_request_get_state(const struct http_client_request *req) { return req->state; } void http_client_request_get_stats(struct http_client_request *req, struct http_client_request_stats *stats_r) { struct http_client *client = req->client; int diff_msecs; uint64_t wait_usecs; i_zero(stats_r); if (!req->submitted) return; /* total elapsed time since message was submitted */ diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->submit_time); stats_r->total_msecs = (unsigned int)I_MAX(diff_msecs, 0); /* elapsed time since message was first sent */ if (req->first_sent_time.tv_sec > 0) { diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->first_sent_time); stats_r->first_sent_msecs = (unsigned int)I_MAX(diff_msecs, 0); } /* elapsed time since message was last sent */ if (req->sent_time.tv_sec > 0) { diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->sent_time); stats_r->last_sent_msecs = (unsigned int)I_MAX(diff_msecs, 0); } if (req->conn != NULL) { /* time spent in other ioloops */ i_assert(ioloop_global_wait_usecs >= req->sent_global_ioloop_usecs); stats_r->other_ioloop_msecs = (unsigned int) (ioloop_global_wait_usecs - req->sent_global_ioloop_usecs + 999) / 1000; /* time spent in the http-client's own ioloop */ if (client->ioloop != NULL) { wait_usecs = io_wait_timer_get_usecs(req->conn->io_wait_timer); i_assert(wait_usecs >= req->sent_http_ioloop_usecs); stats_r->http_ioloop_msecs = (unsigned int) (wait_usecs - req->sent_http_ioloop_usecs + 999) / 1000; i_assert(stats_r->other_ioloop_msecs >= stats_r->http_ioloop_msecs); stats_r->other_ioloop_msecs -= stats_r->http_ioloop_msecs; } } /* total time spent on waiting for file locks */ wait_usecs = file_lock_wait_get_total_usecs(); i_assert(wait_usecs >= req->sent_lock_usecs); stats_r->lock_msecs = (unsigned int) (wait_usecs - req->sent_lock_usecs + 999) / 1000; /* number of attempts for this request */ stats_r->attempts = req->attempts; } void http_client_request_append_stats_text(struct http_client_request *req, string_t *str) { struct http_client_request_stats stats; if (!req->submitted) { str_append(str, "not yet submitted"); return; } http_client_request_get_stats(req, &stats); str_printfa(str, "queued %u.%03u secs ago", stats.total_msecs/1000, stats.total_msecs%1000); if (stats.first_sent_msecs == 0) str_append(str, ", not yet sent"); else { str_printfa(str, ", %u attempts in %u.%03u secs", stats.attempts + 1, stats.first_sent_msecs/1000, stats.first_sent_msecs%1000); if (stats.attempts > 0) { str_printfa(str, ", %u.%03u in last attempt", stats.last_sent_msecs/1000, stats.last_sent_msecs%1000); } } if (stats.http_ioloop_msecs > 0) { str_printfa(str, ", %u.%03u in http ioloop", stats.http_ioloop_msecs/1000, stats.http_ioloop_msecs%1000); } str_printfa(str, ", %u.%03u in other ioloops", stats.other_ioloop_msecs/1000, stats.other_ioloop_msecs%1000); if (stats.lock_msecs > 0) { str_printfa(str, ", %u.%03u in locks", stats.lock_msecs/1000, stats.lock_msecs%1000); } } enum http_response_payload_type http_client_request_get_payload_type(struct http_client_request *req) { /* RFC 7230, Section 3.3: The presence of a message body in a response depends on both the request method to which it is responding and the response status code (Section 3.1.2 of [RFC7230]). Responses to the HEAD request method (Section 4.3.2 of [RFC7231]) never include a message body because the associated response header fields (e.g., Transfer-Encoding, Content-Length, etc.), if present, indicate only what their values would have been if the request method had been GET (Section 4.3.1 of [RFC7231]). 2xx (Successful) responses to a CONNECT request method (Section 4.3.6 of [RFC7231]) switch to tunnel mode instead of having a message body. */ if (strcmp(req->method, "HEAD") == 0) return HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT; if (strcmp(req->method, "CONNECT") == 0) return HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL; return HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED; } static void http_client_request_do_submit(struct http_client_request *req) { struct http_client *client = req->client; struct http_client_host *host; const char *proxy_socket_path = client->set.proxy_socket_path; const struct http_url *proxy_url = client->set.proxy_url; bool have_proxy = (proxy_socket_path != NULL) || (proxy_url != NULL) || (req->host_socket != NULL) || (req->host_url != NULL); const char *authority, *target; if (req->state == HTTP_REQUEST_STATE_ABORTED) return; i_assert(req->state == HTTP_REQUEST_STATE_NEW); authority = http_url_create_authority(&req->origin_url); if (req->connect_tunnel) { /* connect requests require authority form for request target */ target = authority; } else { /* absolute target url */ target = t_strconcat (http_url_create_host(&req->origin_url), req->target, NULL); } /* determine what host to contact to submit this request */ if (have_proxy) { if (req->host_socket != NULL) { /* specific socket proxy */ req->host_url = NULL; } else if (req->host_url != NULL) { /* specific normal proxy */ req->host_socket = NULL; } else if (req->origin_url.have_ssl && !client->set.no_ssl_tunnel && !req->connect_tunnel) { req->host_url = &req->origin_url; /* tunnel to origin server */ req->ssl_tunnel = TRUE; } else if (proxy_socket_path != NULL) { req->host_socket = proxy_socket_path; /* proxy on unix socket */ req->host_url = NULL; } else { req->host_url = proxy_url; /* normal proxy server */ req->host_socket = NULL; } } else { req->host_url = &req->origin_url; /* origin server */ } /* use submission date if no date is set explicitly */ if (req->date == (time_t)-1) req->date = ioloop_time; /* prepare value for Host header */ req->authority = p_strdup(req->pool, authority); /* debug label */ req->label = p_strdup_printf(req->pool, "[Req%u: %s %s]", req->id, req->method, target); /* update request target */ if (req->connect_tunnel || have_proxy) req->target = p_strdup(req->pool, target); if (!have_proxy) { /* if we don't have a proxy, CONNECT requests are handled by creating the requested connection directly */ req->connect_direct = req->connect_tunnel; if (req->connect_direct) req->urgent = TRUE; } if (req->timeout_time.tv_sec == 0) { if (req->timeout_msecs > 0) { req->timeout_time = ioloop_timeval; timeval_add_msecs(&req->timeout_time, req->timeout_msecs); } else if ( client->set.request_absolute_timeout_msecs > 0) { req->timeout_time = ioloop_timeval; timeval_add_msecs(&req->timeout_time, client->set.request_absolute_timeout_msecs); } } host = http_client_host_get(req->client, req->host_url); req->state = HTTP_REQUEST_STATE_QUEUED; http_client_host_submit_request(host, req); } void http_client_request_submit(struct http_client_request *req) { req->submit_time = ioloop_timeval; http_client_request_do_submit(req); http_client_request_debug(req, "Submitted"); req->submitted = TRUE; http_client_request_add(req); } void http_client_request_get_peer_addr(const struct http_client_request *req, struct http_client_peer_addr *addr) { const char *host_socket = req->host_socket; const struct http_url *host_url = req->host_url; /* the IP address may be unassigned in the returned peer address, since that is only available at this stage when the target URL has an explicit IP address. */ i_zero(addr); if (host_socket != NULL) { addr->type = HTTP_CLIENT_PEER_ADDR_UNIX; addr->a.un.path = host_socket; } else if (req->connect_direct) { addr->type = HTTP_CLIENT_PEER_ADDR_RAW; if (host_url->have_host_ip) addr->a.tcp.ip = host_url->host_ip; addr->a.tcp.port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT); } else if (host_url->have_ssl) { if (req->ssl_tunnel) addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL; else addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS; if (host_url->have_host_ip) addr->a.tcp.ip = host_url->host_ip; addr->a.tcp.https_name = host_url->host_name; addr->a.tcp.port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT); } else { addr->type = HTTP_CLIENT_PEER_ADDR_HTTP; if (host_url->have_host_ip) addr->a.tcp.ip = host_url->host_ip; addr->a.tcp.port = (host_url->have_port ? host_url->port : HTTP_DEFAULT_PORT); } } static void http_client_request_finish_payload_out(struct http_client_request *req) { i_assert(req->conn != NULL); /* drop payload output stream */ if (req->payload_output != NULL) { o_stream_unref(&req->payload_output); req->payload_output = NULL; } /* advance state only when request didn't get aborted in the mean time */ if (req->state != HTTP_REQUEST_STATE_ABORTED) { i_assert(req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT); /* we're now waiting for a response from the server */ req->state = HTTP_REQUEST_STATE_WAITING; http_client_connection_start_request_timeout(req->conn); } /* release connection */ req->conn->output_locked = FALSE; http_client_request_debug(req, "Finished sending%s payload", (req->state == HTTP_REQUEST_STATE_ABORTED ? " aborted" : "")); } static int http_client_request_continue_payload(struct http_client_request **_req, const unsigned char *data, size_t size) { struct ioloop *prev_ioloop = current_ioloop; struct http_client_request *req = *_req; struct http_client_connection *conn = req->conn; struct http_client *client = req->client; int ret; i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT); i_assert(req->payload_input == NULL); if (conn != NULL) http_client_connection_ref(conn); http_client_request_ref(req); req->payload_wait = TRUE; if (data == NULL) { req->payload_input = NULL; if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) http_client_request_finish_payload_out(req); } else { req->payload_input = i_stream_create_from_data(data, size); i_stream_set_name(req->payload_input, ""); } req->payload_size = 0; req->payload_chunked = TRUE; if (req->state == HTTP_REQUEST_STATE_NEW) http_client_request_submit(req); if (req->state == HTTP_REQUEST_STATE_ABORTED) { /* Request already failed */ if (req->delayed_error != NULL) { struct http_client_request *tmpreq = req; /* Handle delayed error outside ioloop; the caller expects callbacks occurring, so there is no need for delay. Also, it is very important that any error triggers a callback before http_client_request_send_payload() finishes, since its return value is not always checked. */ http_client_remove_request_error(req->client, req); http_client_request_error_delayed(&tmpreq); } } else { /* Wait for payload data to be written */ i_assert(client->ioloop == NULL); client->ioloop = io_loop_create(); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); while (req->state < HTTP_REQUEST_STATE_PAYLOAD_IN) { http_client_request_debug(req, "Waiting for request to finish"); if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) o_stream_set_flush_pending(req->payload_output, TRUE); io_loop_run(client->ioloop); if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT && req->payload_input->eof) { i_stream_unref(&req->payload_input); req->payload_input = NULL; break; } } io_loop_set_current(prev_ioloop); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); io_loop_set_current(client->ioloop); io_loop_destroy(&client->ioloop); } switch (req->state) { case HTTP_REQUEST_STATE_PAYLOAD_IN: case HTTP_REQUEST_STATE_FINISHED: ret = 1; break; case HTTP_REQUEST_STATE_ABORTED: ret = -1; break; default: ret = 0; break; } req->payload_wait = FALSE; /* callback may have messed with our pointer, so unref using local variable */ if (!http_client_request_unref(&req)) *_req = NULL; if (conn != NULL) http_client_connection_unref(&conn); /* Return status */ return ret; } int http_client_request_send_payload(struct http_client_request **_req, const unsigned char *data, size_t size) { struct http_client_request *req = *_req; int ret; i_assert(data != NULL); ret = http_client_request_continue_payload(&req, data, size); if (ret < 0) { /* failed to send payload */ *_req = NULL; } else if (ret > 0) { /* premature end of request; server sent error before all payload could be sent */ ret = -1; *_req = NULL; } else { /* not finished sending payload */ i_assert(req != NULL); } return ret; } int http_client_request_finish_payload(struct http_client_request **_req) { struct http_client_request *req = *_req; int ret; *_req = NULL; ret = http_client_request_continue_payload(&req, NULL, 0); i_assert(ret != 0); return ret < 0 ? -1 : 0; } static void http_client_request_payload_input(struct http_client_request *req) { struct http_client_connection *conn = req->conn; if (conn->io_req_payload != NULL) io_remove(&conn->io_req_payload); (void)http_client_connection_output(conn); } int http_client_request_send_more(struct http_client_request *req, bool pipelined, const char **error_r) { struct http_client_connection *conn = req->conn; struct ostream *output = req->payload_output; off_t ret; i_assert(req->payload_input != NULL); i_assert(req->payload_output != NULL); if (conn->io_req_payload != NULL) io_remove(&conn->io_req_payload); /* chunked ostream needs to write to the parent stream's buffer */ o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE); ret = o_stream_send_istream(output, req->payload_input); o_stream_set_max_buffer_size(output, (size_t)-1); if (req->payload_input->stream_errno != 0) { /* we're in the middle of sending a request, so the connection will also have to be aborted */ errno = req->payload_input->stream_errno; *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(req->payload_input), i_stream_get_error(req->payload_input)); /* the payload stream assigned to this request is broken, fail this the request immediately */ http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD, "Broken payload stream"); return -1; } else if (output->stream_errno != 0) { /* failed to send request */ errno = output->stream_errno; *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); return -1; } i_assert(ret >= 0); if (i_stream_is_eof(req->payload_input)) { /* finished sending */ if (!req->payload_chunked && req->payload_input->v_offset - req->payload_offset != req->payload_size) { *error_r = t_strdup_printf("BUG: stream '%s' input size changed: " "%"PRIuUOFF_T"-%"PRIuUOFF_T" != %"PRIuUOFF_T, i_stream_get_name(req->payload_input), req->payload_input->v_offset, req->payload_offset, req->payload_size); i_error("%s", *error_r); //FIXME: remove? return -1; } if (req->payload_wait) { /* this chunk of input is finished (client needs to act; disable timeout) */ i_assert(!pipelined); conn->output_locked = TRUE; http_client_connection_stop_request_timeout(conn); if (req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); } else { /* finished sending payload */ http_client_request_finish_payload_out(req); } } else if (i_stream_have_bytes_left(req->payload_input)) { /* output is blocking (server needs to act; enable timeout) */ conn->output_locked = TRUE; if (!pipelined) http_client_connection_start_request_timeout(conn); o_stream_set_flush_pending(output, TRUE); http_client_request_debug(req, "Partially sent payload"); } else { /* input is blocking (client needs to act; disable timeout) */ conn->output_locked = TRUE; if (!pipelined) http_client_connection_stop_request_timeout(conn); conn->io_req_payload = io_add_istream(req->payload_input, http_client_request_payload_input, req); } return 0; } static int http_client_request_send_real(struct http_client_request *req, bool pipelined, const char **error_r) { const struct http_client_settings *set = &req->client->set; struct http_client_connection *conn = req->conn; struct ostream *output = conn->conn.output; string_t *rtext = t_str_new(256); struct const_iovec iov[3]; int ret = 0; i_assert(!req->conn->output_locked); i_assert(req->payload_output == NULL); /* create request line */ str_append(rtext, req->method); str_append(rtext, " "); str_append(rtext, req->target); str_append(rtext, " HTTP/1.1\r\n"); /* create special headers implicitly if not set explicitly using http_client_request_add_header() */ if (!req->have_hdr_host) { str_append(rtext, "Host: "); str_append(rtext, req->authority); str_append(rtext, "\r\n"); } if (!req->have_hdr_date) { str_append(rtext, "Date: "); str_append(rtext, http_date_create(req->date)); str_append(rtext, "\r\n"); } if (!req->have_hdr_authorization && req->username != NULL && req->password != NULL) { struct http_auth_credentials auth_creds; http_auth_basic_credentials_init(&auth_creds, req->username, req->password); str_append(rtext, "Authorization: "); http_auth_create_credentials(rtext, &auth_creds); str_append(rtext, "\r\n"); } if (http_client_request_to_proxy(req) && set->proxy_username != NULL && set->proxy_password != NULL) { struct http_auth_credentials auth_creds; http_auth_basic_credentials_init(&auth_creds, set->proxy_username, set->proxy_password); str_append(rtext, "Proxy-Authorization: "); http_auth_create_credentials(rtext, &auth_creds); str_append(rtext, "\r\n"); } if (!req->have_hdr_user_agent && req->client->set.user_agent != NULL) { str_printfa(rtext, "User-Agent: %s\r\n", req->client->set.user_agent); } if (!req->have_hdr_expect && req->payload_sync) { str_append(rtext, "Expect: 100-continue\r\n"); } if (req->payload_input != NULL) { if (req->payload_chunked) { // FIXME: can't do this for a HTTP/1.0 server if (!req->have_hdr_body_spec) str_append(rtext, "Transfer-Encoding: chunked\r\n"); req->payload_output = http_transfer_chunked_ostream_create(output); } else { /* send Content-Length if we have specified a payload, even if it's 0 bytes. */ if (!req->have_hdr_body_spec) { str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n", req->payload_size); } req->payload_output = output; o_stream_ref(output); } } if (!req->have_hdr_connection && !http_client_request_to_proxy(req)) { /* https://tools.ietf.org/html/rfc2068 Section 19.7.1: A client MUST NOT send the Keep-Alive connection token to a proxy server as HTTP/1.0 proxy servers do not obey the rules of HTTP/1.1 for parsing the Connection header field. */ str_append(rtext, "Connection: Keep-Alive\r\n"); } /* request line + implicit headers */ iov[0].iov_base = str_data(rtext); iov[0].iov_len = str_len(rtext); /* explicit headers */ if (req->headers != NULL) { iov[1].iov_base = str_data(req->headers); iov[1].iov_len = str_len(req->headers); } else { iov[1].iov_base = ""; iov[1].iov_len = 0; } /* end of header */ iov[2].iov_base = "\r\n"; iov[2].iov_len = 2; req->state = HTTP_REQUEST_STATE_PAYLOAD_OUT; if (req->first_sent_time.tv_sec == 0) req->first_sent_time = ioloop_timeval; req->sent_time = ioloop_timeval; req->sent_lock_usecs = file_lock_wait_get_total_usecs(); req->sent_global_ioloop_usecs = ioloop_global_wait_usecs; req->sent_http_ioloop_usecs = io_wait_timer_get_usecs(req->conn->io_wait_timer); o_stream_cork(output); if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) { *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); ret = -1; } else { http_client_request_debug(req, "Sent header"); if (req->payload_output != NULL) { if (!req->payload_sync) { if (http_client_request_send_more (req, pipelined, error_r) < 0) ret = -1; } else { http_client_request_debug(req, "Waiting for 100-continue"); conn->output_locked = TRUE; } } else { req->state = HTTP_REQUEST_STATE_WAITING; if (!pipelined) http_client_connection_start_request_timeout(req->conn); conn->output_locked = FALSE; } if (ret >= 0 && o_stream_flush(output) < 0) { *error_r = t_strdup_printf("flush(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); ret = -1; } } o_stream_uncork(output); return ret; } int http_client_request_send(struct http_client_request *req, bool pipelined, const char **error_r) { char *errstr = NULL; int ret; T_BEGIN { ret = http_client_request_send_real(req, pipelined, error_r); if (ret < 0) errstr = i_strdup(*error_r); } T_END; *error_r = t_strdup(errstr); i_free(errstr); return ret; } bool http_client_request_callback(struct http_client_request *req, struct http_response *response) { http_client_request_callback_t *callback = req->callback; unsigned int orig_attempts = req->attempts; req->state = HTTP_REQUEST_STATE_GOT_RESPONSE; req->callback = NULL; if (callback != NULL) { struct http_response response_copy = *response; if (req->attempts > 0 && !req->preserve_exact_reason) { unsigned int total_msecs = timeval_diff_msecs(&ioloop_timeval, &req->submit_time); response_copy.reason = t_strdup_printf( "%s (%u retries in %u.%03u secs)", response_copy.reason, req->attempts, total_msecs/1000, total_msecs%1000); } callback(&response_copy, req->context); if (req->attempts != orig_attempts) { /* retrying */ req->callback = callback; http_client_request_resubmit(req); return FALSE; } else { /* release payload early (prevents server/client deadlock in proxy) */ if (req->payload_input != NULL) i_stream_unref(&req->payload_input); } } return TRUE; } static bool http_client_request_send_error(struct http_client_request *req, unsigned int status, const char *error) { http_client_request_callback_t *callback; bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT); unsigned int orig_attempts = req->attempts; req->state = HTTP_REQUEST_STATE_ABORTED; callback = req->callback; req->callback = NULL; if (callback != NULL) { struct http_response response; http_response_init(&response, status, error); (void)callback(&response, req->context); if (req->attempts != orig_attempts) { /* retrying */ req->callback = callback; http_client_request_resubmit(req); return FALSE; } else { /* release payload early (prevents server/client deadlock in proxy) */ if (!sending && req->payload_input != NULL) i_stream_unref(&req->payload_input); } } if (req->payload_wait && req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); return TRUE; } void http_client_request_error_delayed(struct http_client_request **_req) { struct http_client_request *req = *_req; const char *error = req->delayed_error; unsigned int status = req->delayed_error_status; bool destroy; i_assert(req->state == HTTP_REQUEST_STATE_ABORTED); *_req = NULL; req->delayed_error = NULL; req->delayed_error_status = 0; i_assert(error != NULL && status != 0); destroy = http_client_request_send_error(req, status, error); if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (destroy) http_client_request_destroy(&req); } void http_client_request_error(struct http_client_request **_req, unsigned int status, const char *error) { struct http_client_request *req = *_req; *_req = NULL; i_assert(req->delayed_error_status == 0); i_assert(req->state < HTTP_REQUEST_STATE_FINISHED); req->state = HTTP_REQUEST_STATE_ABORTED; if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (!req->submitted || req->state == HTTP_REQUEST_STATE_GOT_RESPONSE) { /* we're still in http_client_request_submit() or in the callback during a retry attempt. delay reporting the error, so the caller doesn't have to handle immediate or nested callbacks. */ req->delayed_error = p_strdup(req->pool, error); req->delayed_error_status = status; http_client_delay_request_error(req->client, req); } else { if (http_client_request_send_error(req, status, error)) http_client_request_destroy(&req); } } void http_client_request_abort(struct http_client_request **_req) { struct http_client_request *req = *_req; bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT); *_req = NULL; if (req->state >= HTTP_REQUEST_STATE_FINISHED && req->delayed_error_status == 0) return; req->callback = NULL; req->state = HTTP_REQUEST_STATE_ABORTED; /* release payload early (prevents server/client deadlock in proxy) */ if (!sending && req->payload_input != NULL) i_stream_unref(&req->payload_input); if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (req->payload_wait && req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); http_client_request_destroy(&req); } void http_client_request_finish(struct http_client_request *req) { if (req->state >= HTTP_REQUEST_STATE_FINISHED) return; i_assert(req->refcount > 0); http_client_request_debug(req, "Finished"); req->callback = NULL; req->state = HTTP_REQUEST_STATE_FINISHED; if (req->queue != NULL) http_client_queue_drop_request(req->queue, req); if (req->payload_wait && req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); http_client_request_unref(&req); } void http_client_request_redirect(struct http_client_request *req, unsigned int status, const char *location) { struct http_url *url; const char *error, *target, *origin_url; i_assert(!req->payload_wait); /* parse URL */ if (http_url_parse(location, NULL, 0, pool_datastack_create(), &url, &error) < 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, t_strdup_printf("Invalid redirect location: %s", error)); return; } if (++req->redirects > req->client->set.max_redirects) { if (req->client->set.max_redirects > 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, t_strdup_printf("Redirected more than %d times", req->client->set.max_redirects)); } else { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, "Redirect refused"); } return; } /* rewind payload stream */ if (req->payload_input != NULL && req->payload_size > 0 && status != 303) { if (req->payload_input->v_offset != req->payload_offset && !req->payload_input->seekable) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Redirect failed: Cannot resend payload; stream is not seekable"); return; } else { i_stream_seek(req->payload_input, req->payload_offset); } } /* drop payload output stream from previous attempt */ if (req->payload_output != NULL) o_stream_unref(&req->payload_output); target = http_url_create_target(url); http_url_copy(req->pool, &req->origin_url, url); req->target = p_strdup(req->pool, target); req->host = NULL; origin_url = http_url_create(&req->origin_url); http_client_request_debug(req, "Redirecting to %s%s", origin_url, target); req->label = p_strdup_printf(req->pool, "[%s %s%s]", req->method, origin_url, req->target); /* RFC 7231, Section 6.4.4: -> A 303 `See Other' redirect status response is handled a bit differently. Basically, the response content is located elsewhere, but the original (POST) request is handled already. */ if (status == 303 && strcasecmp(req->method, "HEAD") != 0 && strcasecmp(req->method, "GET") != 0) { // FIXME: should we provide the means to skip this step? The original // request was already handled at this point. req->method = p_strdup(req->pool, "GET"); /* drop payload */ if (req->payload_input != NULL) i_stream_unref(&req->payload_input); req->payload_size = 0; req->payload_offset = 0; } /* resubmit */ req->state = HTTP_REQUEST_STATE_NEW; http_client_request_do_submit(req); } void http_client_request_resubmit(struct http_client_request *req) { i_assert(!req->payload_wait); http_client_request_debug(req, "Resubmitting request"); /* rewind payload stream */ if (req->payload_input != NULL && req->payload_size > 0) { if (req->payload_input->v_offset != req->payload_offset && !req->payload_input->seekable) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Resubmission failed: Cannot resend payload; stream is not seekable"); return; } else { i_stream_seek(req->payload_input, req->payload_offset); } } /* drop payload output stream from previous attempt */ if (req->payload_output != NULL) o_stream_unref(&req->payload_output); req->peer = NULL; req->state = HTTP_REQUEST_STATE_QUEUED; http_client_host_submit_request(req->host, req); } void http_client_request_retry(struct http_client_request *req, unsigned int status, const char *error) { if (!http_client_request_try_retry(req)) http_client_request_error(&req, status, error); } bool http_client_request_try_retry(struct http_client_request *req) { /* don't ever retry if we're sending data in small blocks via http_client_request_send_payload() and we're not waiting for a 100 continue (there's no way to rewind the payload for a retry) */ if (req->payload_wait && (!req->payload_sync || req->payload_sync_continue)) return FALSE; /* limit the number of attempts for each request */ if (req->attempts+1 >= req->max_attempts) return FALSE; req->attempts++; http_client_request_debug(req, "Retrying (attempts=%d)", req->attempts); if (req->callback != NULL) http_client_request_resubmit(req); return TRUE; } void http_client_request_set_destroy_callback(struct http_client_request *req, void (*callback)(void *), void *context) { req->destroy_callback = callback; req->destroy_context = context; } void http_client_request_start_tunnel(struct http_client_request *req, struct http_client_tunnel *tunnel) { struct http_client_connection *conn = req->conn; i_assert(req->state == HTTP_REQUEST_STATE_GOT_RESPONSE); http_client_connection_start_tunnel(&conn, tunnel); } dovecot-2.2.33.2/src/lib-http/test-http-server.c0000644000175000017500000001474513165463624016270 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "llist.h" #include "str.h" #include "ioloop.h" #include "ostream.h" #include "connection.h" #include "http-url.h" #include "http-server.h" #include static int fd_listen; static struct ioloop *ioloop; static struct io *io_listen; static struct http_server *http_server; static bool shut_down = FALSE; static struct client *clients_head = NULL, *clients_tail = NULL; struct client { struct client *prev, *next; struct ip_addr server_ip, ip; in_port_t server_port, port; struct http_server_connection *http_conn; }; static void client_destroy(struct client **_client, const char *reason) { struct client *client = *_client; if (client->http_conn != NULL) { /* We're not in the lib-http/server's connection destroy callback. If at all possible, avoid destroying client objects directly. */ http_server_connection_close(&client->http_conn, reason); } DLLIST2_REMOVE(&clients_head, &clients_tail, client); i_free(client); if (clients_head == NULL) io_loop_stop(ioloop); } /* This function just serves as an illustration of what to do when client objects are destroyed by some actor other than lib-http/server. The best way to close all clients is to drop the whole http-server, which will close all connections, which in turn calls the connection_destroy() callbacks. Using a function like this just complicates matters. */ static void clients_destroy_all(void) { while (clients_head != NULL) { struct client *client = clients_head; client_destroy(&client, "Shutting down server"); } } static void client_http_handle_request(void *context, struct http_server_request *req) { struct client *client = (struct client *)context; const struct http_request *http_req = http_server_request_get(req); struct http_server_response *http_resp; const char *ipport; string_t *content; if (strcmp(http_req->method, "GET") != 0) { /* Unsupported method */ http_resp = http_server_response_create(req, 501, "Not Implemented"); http_server_response_add_header(http_resp, "Allow", "GET"); http_server_response_submit(http_resp); return; } /* Compose response payload */ content = t_str_new(1024); (void)net_ipport2str(&client->server_ip, client->server_port, &ipport); str_printfa(content, "Server: %s\r\n", ipport); (void)net_ipport2str(&client->ip, client->port, &ipport); str_printfa(content, "Client: %s\r\n", ipport); str_printfa(content, "Host: %s", http_req->target.url->host_name); if (http_req->target.url->port != 0) str_printfa(content, ":%u", http_req->target.url->port); str_append(content, "\r\n"); switch (http_req->target.format) { case HTTP_REQUEST_TARGET_FORMAT_ORIGIN: case HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE: str_printfa(content, "Target: %s\r\n", http_url_create(http_req->target.url)); break; case HTTP_REQUEST_TARGET_FORMAT_AUTHORITY: str_printfa(content, "Target: %s\r\n", http_url_create_authority(http_req->target.url)); break; case HTTP_REQUEST_TARGET_FORMAT_ASTERISK: str_append(content, "Target: *\r\n"); break; } /* Just respond with the request target */ http_resp = http_server_response_create(req, 200, "OK"); http_server_response_add_header(http_resp, "Content-Type", "text/plain"); http_server_response_set_payload_data(http_resp, str_data(content), str_len(content)); http_server_response_submit(http_resp); } static void client_http_connection_destroy(void *context, const char *reason) { struct client *client = (struct client *)context; if (client->http_conn == NULL) { /* already destroying client directly */ return; } /* HTTP connection is destroyed already now */ client->http_conn = NULL; /* destroy the client itself */ client_destroy(&client, reason); } static const struct http_server_callbacks server_callbacks = { .handle_request = client_http_handle_request, .connection_destroy = client_http_connection_destroy }; static void client_init(int fd, const struct ip_addr *ip, in_port_t port) { struct client *client; struct http_request_limits req_limits; i_zero(&req_limits); req_limits.max_target_length = 4096; client = i_new(struct client, 1); client->ip = *ip; client->port = port; (void)net_getsockname(fd, &client->server_ip, &client->server_port); client->http_conn = http_server_connection_create(http_server, fd, fd, FALSE, &server_callbacks, client); DLLIST2_APPEND(&clients_head, &clients_tail, client); } static void client_accept(void *context ATTR_UNUSED) { struct ip_addr client_ip; in_port_t client_port; int fd; fd = net_accept(fd_listen, &client_ip, &client_port); if (fd == -1) return; if (fd == -2) i_fatal("accept() failed: %m"); client_init(fd, &client_ip, client_port); } static void sig_die(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { if (shut_down) { i_info("Received SIGINT again - stopping immediately"); io_loop_stop(current_ioloop); return; } i_info("Received SIGINT - shutting down gracefully"); shut_down = TRUE; http_server_shut_down(http_server); if (clients_head == NULL) io_loop_stop(ioloop); } int main(int argc, char *argv[]) { struct http_server_settings http_set; bool debug = FALSE; struct ip_addr my_ip; in_port_t port; int c; lib_init(); while ((c = getopt(argc, argv, "D")) > 0) { switch (c) { case 'D': debug = TRUE; break; default: i_fatal("Usage: %s [-D] []", argv[0]); } } argc -= optind; argv += optind; if (argc < 1 || net_str2port(argv[0], &port) < 0) i_fatal("Port parameter missing"); if (argc < 2) net_get_ip_any4(&my_ip); else if (net_addr2ip(argv[2], &my_ip) < 0) i_fatal("Invalid IP parameter"); i_zero(&http_set); http_set.max_client_idle_time_msecs = 20*1000; /* defaults to indefinite! */ http_set.max_pipelined_requests = 4; http_set.debug = debug; ioloop = io_loop_create(); http_server = http_server_init(&http_set); lib_signals_init(); lib_signals_ignore(SIGPIPE, TRUE); lib_signals_set_handler(SIGTERM, LIBSIG_FLAG_DELAYED, sig_die, NULL); lib_signals_set_handler(SIGINT, LIBSIG_FLAG_DELAYED, sig_die, NULL); fd_listen = net_listen(&my_ip, &port, 128); if (fd_listen == -1) i_fatal("listen(port=%u) failed: %m", port); io_listen = io_add(fd_listen, IO_READ, client_accept, (void *)NULL); io_loop_run(ioloop); io_remove(&io_listen); i_close_fd(&fd_listen); clients_destroy_all(); /* just an example; avoid doing this */ http_server_deinit(&http_server); lib_signals_deinit(); io_loop_destroy(&ioloop); lib_deinit(); } dovecot-2.2.33.2/src/imap/0002755000175000017500000000000013172375612012133 500000000000000dovecot-2.2.33.2/src/imap/imap-sync.h0000644000175000017500000000142513123174404014114 00000000000000#ifndef IMAP_SYNC_H #define IMAP_SYNC_H enum imap_sync_flags { IMAP_SYNC_FLAG_SEND_UID = 0x01, IMAP_SYNC_FLAG_SAFE = 0x02 }; struct client; struct imap_sync_context * imap_sync_init(struct client *client, struct mailbox *box, enum imap_sync_flags imap_flags, enum mailbox_sync_flags flags); int imap_sync_deinit(struct imap_sync_context *ctx, struct client_command_context *sync_cmd); int imap_sync_more(struct imap_sync_context *ctx); /* Returns TRUE if syncing would be allowed currently. */ bool imap_sync_is_allowed(struct client *client); bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags, enum imap_sync_flags imap_flags, const char *tagline); bool cmd_sync_delayed(struct client *client) ATTR_NOWARN_UNUSED_RESULT; #endif dovecot-2.2.33.2/src/imap/cmd-urlfetch.c0000644000175000017500000002472713165463624014611 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "strfuncs.h" #include "str.h" #include "array.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "imap-url.h" #include "imap-quote.h" #include "imap-common.h" #include "imap-commands.h" #include "imap-urlauth.h" #include "imap-urlauth-fetch.h" struct cmd_urlfetch_context { struct imap_urlauth_fetch *ufetch; struct istream *input; uoff_t size; unsigned int failed:1; unsigned int finished:1; unsigned int extended:1; unsigned int in_io_handler:1; }; struct cmd_urlfetch_url { const char *url; enum imap_urlauth_fetch_flags flags; }; static void cmd_urlfetch_finish(struct client_command_context *cmd) { struct cmd_urlfetch_context *ctx = (struct cmd_urlfetch_context *)cmd->context; if (ctx->finished) return; ctx->finished = TRUE; if (ctx->input != NULL) i_stream_unref(&ctx->input); if (ctx->ufetch != NULL) imap_urlauth_fetch_deinit(&ctx->ufetch); if (ctx->failed) { if (cmd->client->output_cmd_lock == cmd) { /* failed in the middle of a literal. we need to disconnect. */ cmd->client->output_cmd_lock = NULL; client_disconnect(cmd->client, "URLFETCH failed"); } else { client_send_internal_error(cmd); } return; } client_send_tagline(cmd, "OK URLFETCH completed."); } static bool cmd_urlfetch_cancel(struct client_command_context *cmd) { struct cmd_urlfetch_context *ctx = (struct cmd_urlfetch_context *)cmd->context; if (!cmd->cancel) return FALSE; if (ctx->ufetch != NULL) { if (cmd->client->user->mail_debug) { i_debug("URLFETCH: Canceling command; " "aborting URLAUTH fetch requests prematurely"); } imap_urlauth_fetch_deinit(&ctx->ufetch); } return TRUE; } static int cmd_urlfetch_transfer_literal(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_urlfetch_context *ctx = (struct cmd_urlfetch_context *)cmd->context; int ret; /* are we in the middle of an urlfetch literal? */ if (ctx->input == NULL) return 1; /* flush output to client if buffer is filled above optimum */ if (o_stream_get_buffer_used_size(client->output) >= CLIENT_OUTPUT_OPTIMAL_SIZE) { if ((ret = o_stream_flush(client->output)) <= 0) return ret; } /* transfer literal to client */ o_stream_set_max_buffer_size(client->output, 0); (void)o_stream_send_istream(client->output, ctx->input); o_stream_set_max_buffer_size(client->output, (size_t)-1); if (ctx->input->v_offset == ctx->size) { /* finished successfully */ i_stream_unref(&ctx->input); return 1; } if (client->output->closed) { /* client disconnected */ return -1; } if (ctx->input->stream_errno != 0) { errno = ctx->input->stream_errno; i_error("read(%s) failed: %s (URLFETCH)", i_stream_get_name(ctx->input), i_stream_get_error(ctx->input)); client_disconnect(client, "URLFETCH failed"); return -1; } if (i_stream_have_bytes_left(ctx->input)) { o_stream_set_flush_pending(client->output, TRUE); return 0; } i_error("URLFETCH got too little data: %"PRIuUOFF_T" vs %"PRIuUOFF_T, ctx->input->v_offset, ctx->size); client_disconnect(client, "FETCH failed"); return -1; } static bool cmd_urlfetch_continue(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_urlfetch_context *ctx = (struct cmd_urlfetch_context *)cmd->context; bool urls_pending; int ret = 1; if (cmd->cancel) return cmd_urlfetch_cancel(cmd); i_assert(client->output_cmd_lock == NULL || client->output_cmd_lock == cmd); /* finish a pending literal transfer */ ret = cmd_urlfetch_transfer_literal(cmd); if (ret < 0) { ctx->failed = TRUE; cmd_urlfetch_finish(cmd); return TRUE; } if (ret == 0) { /* not finished; apparently output blocked again */ return FALSE; } if (ctx->extended) client_send_line(client, ")"); else client_send_line(client, ""); client->output_cmd_lock = NULL; ctx->in_io_handler = TRUE; urls_pending = imap_urlauth_fetch_continue(ctx->ufetch); ctx->in_io_handler = FALSE; if (urls_pending) { /* waiting for imap urlauth service */ cmd->state = CLIENT_COMMAND_STATE_WAIT_EXTERNAL; cmd->func = cmd_urlfetch_cancel; /* retrieve next url */ return FALSE; } /* finished */ cmd_urlfetch_finish(cmd); return TRUE; } static int cmd_urlfetch_url_sucess(struct client_command_context *cmd, struct imap_urlauth_fetch_reply *reply) { struct cmd_urlfetch_context *ctx = cmd->context; string_t *response = t_str_new(256); int ret; str_append(response, "* URLFETCH "); imap_append_astring(response, reply->url); if ((reply->flags & IMAP_URLAUTH_FETCH_FLAG_EXTENDED) == 0) { /* simple */ ctx->extended = FALSE; str_printfa(response, " {%"PRIuUOFF_T"}", reply->size); client_send_line(cmd->client, str_c(response)); i_assert(reply->size == 0 || reply->input != NULL); } else { bool metadata = FALSE; /* extended */ ctx->extended = TRUE; str_append(response, " ("); if ((reply->flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0 && reply->bodypartstruct != NULL) { str_append(response, "BODYPARTSTRUCTURE ("); str_append(response, reply->bodypartstruct); str_append_c(response, ')'); metadata = TRUE; } if ((reply->flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 || (reply->flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) { if (metadata) str_append_c(response, ' '); if ((reply->flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0) { str_append(response, "BODY "); } else { str_append(response, "BINARY "); if (reply->binary_has_nuls) str_append_c(response, '~'); } str_printfa(response, "{%"PRIuUOFF_T"}", reply->size); i_assert(reply->size == 0 || reply->input != NULL); } else { str_append_c(response, ')'); ctx->extended = FALSE; } client_send_line(cmd->client, str_c(response)); } if (reply->input != NULL) { ctx->input = reply->input; ctx->size = reply->size; i_stream_ref(ctx->input); ret = cmd_urlfetch_transfer_literal(cmd); if (ret < 0) { ctx->failed = TRUE; return -1; } if (ret == 0) { /* not finished; apparently output blocked */ cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; cmd->func = cmd_urlfetch_continue; cmd->client->output_cmd_lock = cmd; return 0; } if (ctx->extended) client_send_line(cmd->client, ")"); else client_send_line(cmd->client, ""); } return 1; } static int cmd_urlfetch_url_callback(struct imap_urlauth_fetch_reply *reply, bool last, void *context) { struct client_command_context *cmd = context; struct client *client = cmd->client; struct cmd_urlfetch_context *ctx = cmd->context; bool in_io_handler = ctx->in_io_handler; int ret; if (!in_io_handler) o_stream_cork(client->output); if (reply == NULL) { /* fatal failure */ ctx->failed = TRUE; ret = -1; } else if (reply->succeeded) { /* URL fetch succeeded */ ret = cmd_urlfetch_url_sucess(cmd, reply); } else { /* URL fetch failed */ string_t *response = t_str_new(128); str_append(response, "* URLFETCH "); imap_append_astring(response, reply->url); str_append(response, " NIL"); client_send_line(client, str_c(response)); if (reply->error != NULL) { client_send_line(client, t_strdup_printf( "* NO %s.", reply->error)); } ret = 1; } if ((last && cmd->state == CLIENT_COMMAND_STATE_WAIT_EXTERNAL) || ret < 0) { cmd_urlfetch_finish(cmd); client_command_free(&cmd); } if (!in_io_handler) o_stream_uncork(client->output); return ret; } static int cmd_urlfetch_parse_arg(struct client_command_context *cmd, const struct imap_arg *arg, struct cmd_urlfetch_url *ufurl_r) { enum imap_urlauth_fetch_flags url_flags = 0; const struct imap_arg *params; const char *url_text; if (imap_arg_get_list(arg, ¶ms)) url_flags |= IMAP_URLAUTH_FETCH_FLAG_EXTENDED; else params = arg; /* read url */ if (!imap_arg_get_astring(params++, &url_text)) { client_send_command_error(cmd, "Invalid arguments."); return -1; } ufurl_r->url = t_strdup(url_text); if (url_flags == 0) return 0; while (!IMAP_ARG_IS_EOL(params)) { const char *fetch_param; if (!imap_arg_get_atom(params++, &fetch_param)) { client_send_command_error(cmd, "Invalid URL fetch parameter."); return -1; } if (strcasecmp(fetch_param, "BODY") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODY; else if (strcasecmp(fetch_param, "BINARY") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BINARY; else if (strcasecmp(fetch_param, "BODYPARTSTRUCTURE") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE; else { client_send_command_error(cmd, t_strdup_printf("Unknown URL fetch parameter: %s", fetch_param)); return -1; } } if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) { client_send_command_error(cmd, "URL cannot have both BODY and BINARY fetch parameters."); return -1; } if (url_flags == IMAP_URLAUTH_FETCH_FLAG_EXTENDED) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODY; ufurl_r->flags = url_flags; return 0; } bool cmd_urlfetch(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_urlfetch_context *ctx; ARRAY(struct cmd_urlfetch_url) urls; const struct cmd_urlfetch_url *url; const struct imap_arg *args; struct cmd_urlfetch_url *ufurl; if (client->urlauth_ctx == NULL) { client_send_command_error(cmd, "URLAUTH disabled."); return TRUE; } if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } t_array_init(&urls, 32); /* parse url arguments and group them per userid */ for (; !IMAP_ARG_IS_EOL(args); args++) { ufurl = array_append_space(&urls); if (cmd_urlfetch_parse_arg(cmd, args, ufurl) < 0) return TRUE; } cmd->context = ctx = p_new(cmd->pool, struct cmd_urlfetch_context, 1); cmd->func = cmd_urlfetch_cancel; cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; ctx->ufetch = imap_urlauth_fetch_init(client->urlauth_ctx, cmd_urlfetch_url_callback, cmd); ctx->in_io_handler = TRUE; array_foreach(&urls, url) { if (imap_urlauth_fetch_url(ctx->ufetch, url->url, url->flags) < 0) { /* fatal error */ ctx->failed = TRUE; break; } } ctx->in_io_handler = FALSE; if ((ctx->failed || !imap_urlauth_fetch_is_pending(ctx->ufetch)) && cmd->client->output_cmd_lock != cmd) { /* finished */ cmd_urlfetch_finish(cmd); return TRUE; } if (cmd->client->output_cmd_lock != cmd) cmd->state = CLIENT_COMMAND_STATE_WAIT_EXTERNAL; return FALSE; } dovecot-2.2.33.2/src/imap/cmd-setmetadata.c0000644000175000017500000002210413165463624015254 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ioloop.h" #include "istream.h" #include "istream-seekable.h" #include "ostream.h" #include "str.h" #include "imap-metadata.h" #define METADATA_MAX_INMEM_SIZE (1024*128) struct imap_setmetadata_context { struct client_command_context *cmd; struct imap_parser *parser; struct mailbox *box; struct imap_metadata_transaction *trans; char *entry_name; uoff_t entry_value_len; struct istream *input; bool failed; bool cmd_error_sent; bool storage_failure; }; static void cmd_setmetadata_deinit(struct imap_setmetadata_context *ctx) { o_stream_set_flush_callback(ctx->cmd->client->output, client_output, ctx->cmd->client); ctx->cmd->client->input_lock = NULL; imap_parser_unref(&ctx->parser); if (ctx->trans != NULL) imap_metadata_transaction_rollback(&ctx->trans); if (ctx->box != NULL && ctx->box != ctx->cmd->client->mailbox) mailbox_free(&ctx->box); i_free(ctx->entry_name); } static int cmd_setmetadata_parse_entryvalue(struct imap_setmetadata_context *ctx, const char **entry_r, const struct imap_arg **value_r) { const struct imap_arg *args; const char *name, *error; int ret; bool fatal; /* parse the entry name */ ret = imap_parser_read_args(ctx->parser, 1, IMAP_PARSE_FLAG_INSIDE_LIST, &args); if (ret >= 0) { if (ret == 0) { /* ')' found */ *entry_r = NULL; return 1; } if (!imap_arg_get_astring(args, &name)) { client_send_command_error(ctx->cmd, "Entry name isn't astring"); return -1; } ret = imap_parser_read_args(ctx->parser, 2, IMAP_PARSE_FLAG_INSIDE_LIST | IMAP_PARSE_FLAG_LITERAL_SIZE | IMAP_PARSE_FLAG_LITERAL8, &args); } if (ret < 0) { if (ret == -2) return 0; error = imap_parser_get_error(ctx->parser, &fatal); if (fatal) { client_disconnect_with_error(ctx->cmd->client, error); return -1; } client_send_command_error(ctx->cmd, error); return -1; } if (args[1].type == IMAP_ARG_EOL) { client_send_command_error(ctx->cmd, "Entry value missing"); return -1; } if (args[1].type == IMAP_ARG_LIST) { client_send_command_error(ctx->cmd, "Entry value can't be a list"); return -1; } if (!ctx->cmd_error_sent && !imap_metadata_verify_entry_name(name, &error)) { client_send_command_error(ctx->cmd, error); ctx->cmd_error_sent = TRUE; } if (ctx->cmd_error_sent) { ctx->cmd->param_error = FALSE; ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; ctx->failed = TRUE; if (args[1].type == IMAP_ARG_LITERAL_SIZE) { /* client won't see "+ OK", so we can abort immediately */ ctx->cmd->client->input_skip_line = FALSE; return -1; } } /* entry names are case-insensitive. handle this by using only lowercase names. */ *entry_r = t_str_lcase(name); *value_r = &args[1]; return 1; } static int cmd_setmetadata_entry_read_stream(struct imap_setmetadata_context *ctx) { const unsigned char *data; size_t size; struct mail_attribute_value value; int ret; while ((ret = i_stream_read_data(ctx->input, &data, &size, 0)) > 0) i_stream_skip(ctx->input, size); if (ctx->input->v_offset == ctx->entry_value_len) { /* finished reading the value */ i_stream_seek(ctx->input, 0); if (ctx->failed) { i_stream_unref(&ctx->input); return 1; } i_zero(&value); value.value_stream = ctx->input; if (imap_metadata_set(ctx->trans, ctx->entry_name, &value) < 0) { /* delay reporting the failure so we'll finish reading the command input */ ctx->storage_failure = TRUE; ctx->failed = TRUE; } i_stream_unref(&ctx->input); return 1; } if (ctx->input->eof) { /* client disconnected */ return -1; } return 0; } static int cmd_setmetadata_entry(struct imap_setmetadata_context *ctx, const char *entry_name, const struct imap_arg *entry_value) { struct istream *inputs[2]; struct mail_attribute_value value; string_t *path; int ret; switch (entry_value->type) { case IMAP_ARG_NIL: case IMAP_ARG_ATOM: case IMAP_ARG_STRING: /* we have the value already */ if (ctx->failed) return 1; i_zero(&value); value.value = imap_arg_as_nstring(entry_value); ret = imap_metadata_set(ctx->trans, entry_name, &value); if (ret < 0) { /* delay reporting the failure so we'll finish reading the command input */ ctx->storage_failure = TRUE; ctx->failed = TRUE; } return 1; case IMAP_ARG_LITERAL_SIZE: o_stream_nsend(ctx->cmd->client->output, "+ OK\r\n", 6); o_stream_nflush(ctx->cmd->client->output); o_stream_uncork(ctx->cmd->client->output); o_stream_cork(ctx->cmd->client->output); /* fall through */ case IMAP_ARG_LITERAL_SIZE_NONSYNC: i_free(ctx->entry_name); ctx->entry_name = i_strdup(entry_name); ctx->entry_value_len = imap_arg_as_literal_size(entry_value); inputs[0] = i_stream_create_limit(ctx->cmd->client->input, ctx->entry_value_len); inputs[1] = NULL; path = t_str_new(128); mail_user_set_get_temp_prefix(path, ctx->cmd->client->user->set); ctx->input = i_stream_create_seekable_path(inputs, METADATA_MAX_INMEM_SIZE, str_c(path)); i_stream_set_name(ctx->input, i_stream_get_name(inputs[0])); i_stream_unref(&inputs[0]); return cmd_setmetadata_entry_read_stream(ctx); case IMAP_ARG_LITERAL: case IMAP_ARG_LIST: case IMAP_ARG_EOL: break; } i_unreached(); } static bool cmd_setmetadata_continue(struct client_command_context *cmd) { struct imap_setmetadata_context *ctx = cmd->context; const char *entry, *error_string; enum mail_error error; const struct imap_arg *value; int ret; if (cmd->cancel) { cmd_setmetadata_deinit(ctx); return TRUE; } if (ctx->input != NULL) { if ((ret = cmd_setmetadata_entry_read_stream(ctx)) == 0) return FALSE; if (ret < 0) { cmd_setmetadata_deinit(ctx); return TRUE; } } while ((ret = cmd_setmetadata_parse_entryvalue(ctx, &entry, &value)) > 0 && entry != NULL) { ret = ctx->failed ? 1 : cmd_setmetadata_entry(ctx, entry, value); imap_parser_reset(ctx->parser); if (ret <= 0) break; } if (ret == 0) return 0; if (ret < 0 || ctx->cmd_error_sent) { /* already sent the error to client */ ; } else if (ctx->storage_failure) { if (ctx->box == NULL) client_disconnect_if_inconsistent(cmd->client); error_string = imap_metadata_transaction_get_last_error (ctx->trans, &error); client_send_tagline(cmd, imap_get_error_string(cmd, error_string, error)); } else if (imap_metadata_transaction_commit(&ctx->trans, &error, &error_string) < 0) { if (ctx->box == NULL) client_disconnect_if_inconsistent(cmd->client); client_send_tagline(cmd, imap_get_error_string(cmd, error_string, error)); } else { client_send_tagline(cmd, "OK Setmetadata completed."); } cmd_setmetadata_deinit(ctx); return TRUE; } static bool cmd_setmetadata_start(struct imap_setmetadata_context *ctx) { struct client_command_context *cmd = ctx->cmd; struct client *client = cmd->client; /* we support large literals, so read the values from client asynchronously the same way as APPEND does. */ client->input_lock = cmd; ctx->parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); o_stream_unset_flush_callback(client->output); cmd->func = cmd_setmetadata_continue; cmd->context = ctx; return cmd_setmetadata_continue(cmd); } static bool cmd_setmetadata_server(struct imap_setmetadata_context *ctx) { ctx->trans = imap_metadata_transaction_begin_server(ctx->cmd->client->user); return cmd_setmetadata_start(ctx); } static bool cmd_setmetadata_mailbox(struct imap_setmetadata_context *ctx, const char *mailbox) { struct client_command_context *cmd = ctx->cmd; struct client *client = cmd->client; struct mail_namespace *ns; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; if (client->mailbox != NULL && !client->mailbox_examined && mailbox_equals(client->mailbox, ns, mailbox)) ctx->box = client->mailbox; else { ctx->box = mailbox_alloc(ns->list, mailbox, 0); mailbox_set_reason(ctx->box, "SETMETADATA"); if (mailbox_open(ctx->box) < 0) { client_send_box_error(cmd, ctx->box); mailbox_free(&ctx->box); return TRUE; } } ctx->trans = imap_metadata_transaction_begin(ctx->box); return cmd_setmetadata_start(ctx); } bool cmd_setmetadata(struct client_command_context *cmd) { struct imap_setmetadata_context *ctx; const struct imap_arg *args; const char *mailbox; int ret; ret = imap_parser_read_args(cmd->parser, 2, IMAP_PARSE_FLAG_STOP_AT_LIST, &args); if (ret == -1) { client_send_command_error(cmd, NULL); return TRUE; } if (ret == -2) return FALSE; if (!imap_arg_get_astring(&args[0], &mailbox) || args[1].type != IMAP_ARG_LIST) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (!cmd->client->imap_metadata_enabled) { client_send_command_error(cmd, "METADATA disabled."); return TRUE; } ctx = p_new(cmd->pool, struct imap_setmetadata_context, 1); ctx->cmd = cmd; ctx->cmd->context = ctx; if (mailbox[0] == '\0') { /* server attribute */ return cmd_setmetadata_server(ctx); } return cmd_setmetadata_mailbox(ctx, mailbox); } dovecot-2.2.33.2/src/imap/cmd-cancelupdate.c0000644000175000017500000000211013123174404015370 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-search.h" #include "imap-commands.h" static bool client_search_update_cancel(struct client *client, const char *tag) { struct imap_search_update *update; unsigned int idx; update = client_search_update_lookup(client, tag, &idx); if (update == NULL) return FALSE; imap_search_update_free(update); array_delete(&client->search_updates, idx, 1); return TRUE; } bool cmd_cancelupdate(struct client_command_context *cmd) { const struct imap_arg *args; const char *tag; unsigned int i; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; for (i = 0; args[i].type == IMAP_ARG_STRING; i++) ; if (!IMAP_ARG_IS_EOL(&args[i]) || i == 0) { client_send_command_error(cmd, "Invalid parameters."); return TRUE; } while (imap_arg_get_quoted(args, &tag)) { if (!client_search_update_cancel(cmd->client, tag)) { client_send_tagline(cmd, "NO Unknown tag."); return TRUE; } args++; } client_send_tagline(cmd, "OK Updates cancelled."); return TRUE; } dovecot-2.2.33.2/src/imap/Makefile.in0000644000175000017500000007564413172375572014143 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = imap$(EXEEXT) subdir = src/imap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am__objects_1 = cmd-append.$(OBJEXT) cmd-capability.$(OBJEXT) \ cmd-cancelupdate.$(OBJEXT) cmd-check.$(OBJEXT) \ cmd-close.$(OBJEXT) cmd-copy.$(OBJEXT) cmd-create.$(OBJEXT) \ cmd-delete.$(OBJEXT) cmd-enable.$(OBJEXT) \ cmd-examine.$(OBJEXT) cmd-expunge.$(OBJEXT) \ cmd-fetch.$(OBJEXT) cmd-genurlauth.$(OBJEXT) \ cmd-getmetadata.$(OBJEXT) cmd-id.$(OBJEXT) cmd-idle.$(OBJEXT) \ cmd-list.$(OBJEXT) cmd-logout.$(OBJEXT) cmd-lsub.$(OBJEXT) \ cmd-namespace.$(OBJEXT) cmd-noop.$(OBJEXT) \ cmd-notify.$(OBJEXT) cmd-rename.$(OBJEXT) \ cmd-resetkey.$(OBJEXT) cmd-search.$(OBJEXT) \ cmd-select.$(OBJEXT) cmd-setmetadata.$(OBJEXT) \ cmd-sort.$(OBJEXT) cmd-status.$(OBJEXT) cmd-store.$(OBJEXT) \ cmd-subscribe.$(OBJEXT) cmd-thread.$(OBJEXT) \ cmd-unselect.$(OBJEXT) cmd-unsubscribe.$(OBJEXT) \ cmd-urlfetch.$(OBJEXT) cmd-x-cancel.$(OBJEXT) \ cmd-x-state.$(OBJEXT) am_imap_OBJECTS = $(am__objects_1) imap-client.$(OBJEXT) \ imap-client-hibernate.$(OBJEXT) imap-commands.$(OBJEXT) \ imap-commands-util.$(OBJEXT) imap-expunge.$(OBJEXT) \ imap-fetch.$(OBJEXT) imap-fetch-body.$(OBJEXT) \ imap-list.$(OBJEXT) imap-master-client.$(OBJEXT) \ imap-notify.$(OBJEXT) imap-search.$(OBJEXT) \ imap-search-args.$(OBJEXT) imap-settings.$(OBJEXT) \ imap-status.$(OBJEXT) imap-state.$(OBJEXT) imap-sync.$(OBJEXT) \ mail-storage-callbacks.$(OBJEXT) main.$(OBJEXT) imap_OBJECTS = $(am_imap_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = imap_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(imap_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(imap_SOURCES) DIST_SOURCES = $(imap_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-urlauth \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage imap_LDFLAGS = -export-dynamic imap_LDADD = \ ../lib-imap-urlauth/libimap-urlauth.la \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) imap_DEPENDENCIES = \ ../lib-imap-urlauth/libimap-urlauth.la \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) cmds = \ cmd-append.c \ cmd-capability.c \ cmd-cancelupdate.c \ cmd-check.c \ cmd-close.c \ cmd-copy.c \ cmd-create.c \ cmd-delete.c \ cmd-enable.c \ cmd-examine.c \ cmd-expunge.c \ cmd-fetch.c \ cmd-genurlauth.c \ cmd-getmetadata.c \ cmd-id.c \ cmd-idle.c \ cmd-list.c \ cmd-logout.c \ cmd-lsub.c \ cmd-namespace.c \ cmd-noop.c \ cmd-notify.c \ cmd-rename.c \ cmd-resetkey.c \ cmd-search.c \ cmd-select.c \ cmd-setmetadata.c \ cmd-sort.c \ cmd-status.c \ cmd-store.c \ cmd-subscribe.c \ cmd-thread.c \ cmd-unselect.c \ cmd-unsubscribe.c \ cmd-urlfetch.c \ cmd-x-cancel.c \ cmd-x-state.c imap_SOURCES = \ $(cmds) \ imap-client.c \ imap-client-hibernate.c \ imap-commands.c \ imap-commands-util.c \ imap-expunge.c \ imap-fetch.c \ imap-fetch-body.c \ imap-list.c \ imap-master-client.c \ imap-notify.c \ imap-search.c \ imap-search-args.c \ imap-settings.c \ imap-status.c \ imap-state.c \ imap-sync.c \ mail-storage-callbacks.c \ main.c headers = \ imap-client.h \ imap-commands.h \ imap-commands-util.h \ imap-common.h \ imap-expunge.h \ imap-fetch.h \ imap-list.h \ imap-master-client.h \ imap-notify.h \ imap-search.h \ imap-search-args.h \ imap-settings.h \ imap-status.h \ imap-state.h \ imap-sync.h \ imap-sync-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/imap/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/imap/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list imap$(EXEEXT): $(imap_OBJECTS) $(imap_DEPENDENCIES) $(EXTRA_imap_DEPENDENCIES) @rm -f imap$(EXEEXT) $(AM_V_CCLD)$(imap_LINK) $(imap_OBJECTS) $(imap_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-append.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-cancelupdate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-capability.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-check.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-close.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-copy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-create.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-delete.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-enable.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-examine.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-expunge.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-fetch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-genurlauth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-getmetadata.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-id.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-idle.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-logout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-lsub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-namespace.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-noop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-notify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-rename.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-resetkey.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-search.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-setmetadata.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-sort.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-store.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-subscribe.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-thread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-unselect.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-unsubscribe.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-urlfetch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-x-cancel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-x-state.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-client-hibernate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-commands-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-commands.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-expunge.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-fetch-body.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-fetch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-master-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-notify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-search-args.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-search.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-state.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-sync.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage-callbacks.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/imap/imap-search.c0000644000175000017500000004354713165463624014426 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ostream.h" #include "str.h" #include "seq-range-array.h" #include "time-util.h" #include "imap-resp-code.h" #include "imap-quote.h" #include "imap-seqset.h" #include "imap-util.h" #include "mail-search-build.h" #include "imap-fetch.h" #include "imap-commands.h" #include "imap-search-args.h" #include "imap-search.h" static int imap_search_deinit(struct imap_search_context *ctx); static int imap_partial_range_parse(struct imap_search_context *ctx, const char *str) { ctx->partial1 = 0; ctx->partial2 = 0; for (; *str >= '0' && *str <= '9'; str++) ctx->partial1 = ctx->partial1 * 10 + *str-'0'; if (*str != ':' || ctx->partial1 == 0) return -1; for (str++; *str >= '0' && *str <= '9'; str++) ctx->partial2 = ctx->partial2 * 10 + *str-'0'; if (*str != '\0' || ctx->partial2 == 0) return -1; if (ctx->partial1 > ctx->partial2) { uint32_t temp = ctx->partial2; ctx->partial2 = ctx->partial1; ctx->partial1 = temp; } return 0; } static bool search_parse_fetch_att(struct imap_search_context *ctx, const struct imap_arg *update_args) { const char *error; ctx->fetch_pool = pool_alloconly_create("search update fetch", 512); if (imap_fetch_att_list_parse(ctx->cmd->client, ctx->fetch_pool, update_args, &ctx->fetch_ctx, &error) < 0) { client_send_command_error(ctx->cmd, t_strconcat( "SEARCH UPDATE fetch-att: ", error, NULL)); pool_unref(&ctx->fetch_pool); return FALSE; } return TRUE; } static bool search_parse_return_options(struct imap_search_context *ctx, const struct imap_arg *args) { struct client_command_context *cmd = ctx->cmd; const struct imap_arg *update_args; const char *name, *str; unsigned int idx; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &name)) { client_send_command_error(cmd, "SEARCH return options contain non-atoms."); return FALSE; } name = t_str_ucase(name); args++; if (strcmp(name, "MIN") == 0) ctx->return_options |= SEARCH_RETURN_MIN; else if (strcmp(name, "MAX") == 0) ctx->return_options |= SEARCH_RETURN_MAX; else if (strcmp(name, "ALL") == 0) ctx->return_options |= SEARCH_RETURN_ALL; else if (strcmp(name, "COUNT") == 0) ctx->return_options |= SEARCH_RETURN_COUNT; else if (strcmp(name, "SAVE") == 0) ctx->return_options |= SEARCH_RETURN_SAVE; else if (strcmp(name, "CONTEXT") == 0) { /* no-op */ } else if (strcmp(name, "UPDATE") == 0) { if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0) { client_send_command_error(cmd, "SEARCH return options have duplicate UPDATE."); return FALSE; } ctx->return_options |= SEARCH_RETURN_UPDATE; if (imap_arg_get_list(args, &update_args)) { if (!search_parse_fetch_att(ctx, update_args)) return FALSE; args++; } } else if (strcmp(name, "RELEVANCY") == 0) ctx->return_options |= SEARCH_RETURN_RELEVANCY; else if (strcmp(name, "PARTIAL") == 0) { if (ctx->partial1 != 0) { client_send_command_error(cmd, "PARTIAL can be used only once."); return FALSE; } ctx->return_options |= SEARCH_RETURN_PARTIAL; if (!imap_arg_get_atom(args, &str)) { client_send_command_error(cmd, "PARTIAL range missing."); return FALSE; } if (imap_partial_range_parse(ctx, str) < 0) { client_send_command_error(cmd, "PARTIAL range broken."); return FALSE; } args++; } else { client_send_command_error(cmd, "Unknown SEARCH return option"); return FALSE; } } if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0 && client_search_update_lookup(cmd->client, cmd->tag, &idx) != NULL) { client_send_command_error(cmd, "Duplicate search update tag"); return FALSE; } if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0 && (ctx->return_options & SEARCH_RETURN_ALL) != 0) { client_send_command_error(cmd, "PARTIAL conflicts with ALL"); return FALSE; } if (ctx->return_options == 0) ctx->return_options = SEARCH_RETURN_ALL; ctx->return_options |= SEARCH_RETURN_ESEARCH; return TRUE; } static void imap_search_args_check(struct imap_search_context *ctx, const struct mail_search_arg *sargs) { for (; sargs != NULL; sargs = sargs->next) { switch (sargs->type) { case SEARCH_SEQSET: ctx->have_seqsets = TRUE; break; case SEARCH_MODSEQ: ctx->have_modseqs = TRUE; break; case SEARCH_OR: case SEARCH_SUB: imap_search_args_check(ctx, sargs->value.subargs); break; default: break; } } } static void imap_search_result_save(struct imap_search_context *ctx) { struct client *client = ctx->cmd->client; struct mail_search_result *result; struct imap_search_update *update; if (!array_is_created(&client->search_updates)) i_array_init(&client->search_updates, 32); else if (array_count(&client->search_updates) >= CLIENT_MAX_SEARCH_UPDATES) { /* too many updates */ string_t *str = t_str_new(256); str_append(str, "* NO [NOUPDATE "); imap_append_quoted(str, ctx->cmd->tag); str_append_c(str, ']'); client_send_line(client, str_c(str)); ctx->return_options &= ~SEARCH_RETURN_UPDATE; imap_search_context_free(ctx); return; } result = mailbox_search_result_save(ctx->search_ctx, MAILBOX_SEARCH_RESULT_FLAG_UPDATE | MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC); update = array_append_space(&client->search_updates); update->tag = i_strdup(ctx->cmd->tag); update->result = result; update->return_uids = ctx->cmd->uid; update->fetch_pool = ctx->fetch_pool; update->fetch_ctx = ctx->fetch_ctx; ctx->fetch_pool = NULL; ctx->fetch_ctx = NULL; } static void imap_search_send_result_standard(struct imap_search_context *ctx) { const struct seq_range *range; string_t *str; uint32_t seq; str = t_str_new(1024); str_append(str, ctx->sorting ? "* SORT" : "* SEARCH"); array_foreach(&ctx->result, range) { for (seq = range->seq1; seq <= range->seq2; seq++) str_printfa(str, " %u", seq); if (str_len(str) >= 1024-32) { o_stream_nsend(ctx->cmd->client->output, str_data(str), str_len(str)); str_truncate(str, 0); } } if (ctx->highest_seen_modseq != 0) { str_printfa(str, " (MODSEQ %llu)", (unsigned long long)ctx->highest_seen_modseq); } str_append(str, "\r\n"); o_stream_nsend(ctx->cmd->client->output, str_data(str), str_len(str)); } static void imap_search_send_partial(struct imap_search_context *ctx, string_t *str) { struct seq_range *range; uint32_t n, diff; unsigned int i, count, delete_count; str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2); ctx->partial1--; ctx->partial2--; /* we need to be able to handle non-sorted seq ranges (for SORT replies), so do this ourself instead of using seq_range_array_*() functions. */ range = array_get_modifiable(&ctx->result, &count); /* delete everything up to partial1 */ delete_count = 0; for (i = n = 0; i < count; i++) { diff = range[i].seq2 - range[i].seq1; if (n + diff >= ctx->partial1) { range[i].seq1 += ctx->partial1 - n; delete_count = i; break; } n += diff + 1; } if (i == count) { /* partial1 points past the result */ array_clear(&ctx->result); } else { /* delete everything after partial2 */ for (n = ctx->partial1; i < count; i++) { diff = range[i].seq2 - range[i].seq1; if (n + diff >= ctx->partial2) { range[i].seq2 = range[i].seq1 + (ctx->partial2 - n); array_delete(&ctx->result, i + 1, count-(i+1)); break; } n += diff + 1; } array_delete(&ctx->result, 0, delete_count); } if (array_count(&ctx->result) == 0) { /* no results (in range) */ str_append(str, "NIL"); } else { imap_write_seq_range(str, &ctx->result); } str_append_c(str, ')'); } static void imap_search_send_relevancy(struct imap_search_context *ctx, string_t *dest) { const float *scores; unsigned int i, count; float diff, imap_score; scores = array_get(&ctx->relevancy_scores, &count); if (count == 0) return; /* we'll need to convert float scores to numbers 1..100 FIXME: would be a good idea to try to detect non-linear score mappings and convert them better.. */ diff = ctx->max_relevancy - ctx->min_relevancy; if (diff == 0) diff = 1.0; for (i = 0; i < count; i++) { if (i > 0) str_append_c(dest, ' '); imap_score = (scores[i] - ctx->min_relevancy) / diff * 100.0; if (imap_score < 1) str_append(dest, "1"); else str_printfa(dest, "%u", (unsigned int)imap_score); } } static void imap_search_send_result(struct imap_search_context *ctx) { struct client *client = ctx->cmd->client; const struct seq_range *range; unsigned int count; string_t *str; if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) { imap_search_send_result_standard(ctx); return; } if (ctx->return_options == (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_SAVE)) { /* we only wanted to save the result, don't return ESEARCH result. */ return; } str = str_new(default_pool, 1024); str_append(str, "* ESEARCH (TAG "); imap_append_string(str, ctx->cmd->tag); str_append_c(str, ')'); if (ctx->cmd->uid) str_append(str, " UID"); range = array_get(&ctx->result, &count); if (count > 0) { if ((ctx->return_options & SEARCH_RETURN_MIN) != 0) str_printfa(str, " MIN %u", range[0].seq1); if ((ctx->return_options & SEARCH_RETURN_MAX) != 0) str_printfa(str, " MAX %u", range[count-1].seq2); if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) { str_append(str, " ALL "); imap_write_seq_range(str, &ctx->result); } } if ((ctx->return_options & SEARCH_RETURN_RELEVANCY) != 0) { str_append(str, " RELEVANCY ("); imap_search_send_relevancy(ctx, str); str_append_c(str, ')'); } if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0) imap_search_send_partial(ctx, str); if ((ctx->return_options & SEARCH_RETURN_COUNT) != 0) str_printfa(str, " COUNT %u", ctx->result_count); if (ctx->highest_seen_modseq != 0) { str_printfa(str, " MODSEQ %llu", (unsigned long long)ctx->highest_seen_modseq); } str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); str_free(&str); } static void search_update_mail(struct imap_search_context *ctx, struct mail *mail) { uint64_t modseq; if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) { modseq = mail_get_modseq(mail); if (ctx->highest_seen_modseq < modseq) ctx->highest_seen_modseq = modseq; } if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) { seq_range_array_add(&ctx->cmd->client->search_saved_uidset, mail->uid); } if ((ctx->return_options & SEARCH_RETURN_RELEVANCY) != 0) { const char *str; float score; if (mail_get_special(mail, MAIL_FETCH_SEARCH_RELEVANCY, &str) < 0) score = 0; else score = strtod(str, NULL); array_append(&ctx->relevancy_scores, &score, 1); if (ctx->min_relevancy > score) ctx->min_relevancy = score; if (ctx->max_relevancy < score) ctx->max_relevancy = score; } } static void search_add_result_id(struct imap_search_context *ctx, uint32_t id) { struct seq_range *range; unsigned int count; /* only append the data. this is especially important when we're returning a sort result. */ range = array_get_modifiable(&ctx->result, &count); if (count > 0 && id == range[count-1].seq2 + 1) { range[count-1].seq2++; } else { range = array_append_space(&ctx->result); range->seq1 = range->seq2 = id; } } static bool cmd_search_more(struct client_command_context *cmd) { struct imap_search_context *ctx = cmd->context; enum search_return_options opts = ctx->return_options; struct mail *mail; enum mailbox_sync_flags sync_flags; const struct seq_range *range; unsigned int count; uint32_t id, id_min, id_max; const char *ok_reply; bool tryagain, minmax, lost_data; if (cmd->cancel) { (void)imap_search_deinit(ctx); return TRUE; } range = array_get(&ctx->result, &count); if (count == 0) { id_min = 0; id_max = 0; } else { id_min = range[0].seq1; id_max = range[count-1].seq2; } minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 && (opts & ~(SEARCH_RETURN_NORESULTS | SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0; while (mailbox_search_next_nonblock(ctx->search_ctx, &mail, &tryagain)) { id = cmd->uid ? mail->uid : mail->seq; ctx->result_count++; if (minmax) { /* we only care about min/max */ if (id_min == 0 && (opts & SEARCH_RETURN_MIN) != 0) id_min = id; if ((opts & SEARCH_RETURN_MAX) != 0) id_max = id; if (id == id_min || id == id_max) { /* return option updates are delayed until we know the actual min/max values */ search_add_result_id(ctx, id); } continue; } search_update_mail(ctx, mail); if ((opts & ~(SEARCH_RETURN_NORESULTS | SEARCH_RETURN_COUNT)) == 0) { /* we only want to count (and get modseqs) */ continue; } search_add_result_id(ctx, id); } if (tryagain) return FALSE; if (minmax && array_count(&ctx->result) > 0 && (opts & (SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE)) != 0) { /* handle MIN/MAX modseq/save updates */ mail = mail_alloc(ctx->trans, 0, NULL); if ((opts & SEARCH_RETURN_MIN) != 0) { i_assert(id_min != 0); if (cmd->uid) { if (!mail_set_uid(mail, id_min)) i_unreached(); } else { mail_set_seq(mail, id_min); } search_update_mail(ctx, mail); } if ((opts & SEARCH_RETURN_MAX) != 0) { i_assert(id_max != 0); if (cmd->uid) { if (!mail_set_uid(mail, id_max)) i_unreached(); } else { mail_set_seq(mail, id_max); } search_update_mail(ctx, mail); } mail_free(&mail); } lost_data = mailbox_search_seen_lost_data(ctx->search_ctx); if (imap_search_deinit(ctx) < 0) { client_send_box_error(cmd, cmd->client->mailbox); return TRUE; } sync_flags = MAILBOX_SYNC_FLAG_FAST; if (!cmd->uid || ctx->have_seqsets) sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES; ok_reply = t_strdup_printf("OK %s%s completed", lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : "", !ctx->sorting ? "Search" : "Sort"); return cmd_sync(cmd, sync_flags, 0, ok_reply); } static void cmd_search_more_callback(struct client_command_context *cmd) { struct client *client = cmd->client; bool finished; o_stream_cork(client->output); finished = command_exec(cmd); o_stream_uncork(client->output); if (!finished) (void)client_handle_unfinished_cmd(cmd); else client_command_free(&cmd); cmd_sync_delayed(client); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); } int cmd_search_parse_return_if_found(struct imap_search_context *ctx, const struct imap_arg **_args) { const struct imap_arg *list_args, *args = *_args; struct client_command_context *cmd = ctx->cmd; if (!imap_arg_atom_equals(&args[0], "RETURN") || !imap_arg_get_list(&args[1], &list_args)) { ctx->return_options = SEARCH_RETURN_ALL; return 1; } if (!search_parse_return_options(ctx, list_args)) { imap_search_context_free(ctx); return -1; } if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) { /* wait if there is another SEARCH SAVE command running. */ if (client_handle_search_save_ambiguity(cmd)) { imap_search_context_free(ctx); return 0; } /* make sure the search result gets cleared if SEARCH fails */ if (array_is_created(&cmd->client->search_saved_uidset)) array_clear(&cmd->client->search_saved_uidset); else i_array_init(&cmd->client->search_saved_uidset, 128); cmd->search_save_result = TRUE; } *_args = args + 2; return 1; } bool imap_search_start(struct imap_search_context *ctx, struct mail_search_args *sargs, const enum mail_sort_type *sort_program) { struct client_command_context *cmd = ctx->cmd; imap_search_args_check(ctx, sargs->args); if (ctx->have_modseqs) { ctx->return_options |= SEARCH_RETURN_MODSEQ; (void)client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE); } ctx->box = cmd->client->mailbox; ctx->trans = mailbox_transaction_begin(ctx->box, 0); imap_transaction_set_cmd_reason(ctx->trans, cmd); ctx->sargs = sargs; ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, sort_program, 0, NULL); ctx->sorting = sort_program != NULL; i_array_init(&ctx->result, 128); if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0) imap_search_result_save(ctx); else { i_assert(ctx->fetch_ctx == NULL); } if ((ctx->return_options & SEARCH_RETURN_RELEVANCY) != 0) i_array_init(&ctx->relevancy_scores, 128); cmd->func = cmd_search_more; cmd->context = ctx; if (cmd_search_more(cmd)) return TRUE; /* we may have moved onto syncing by now */ if (cmd->func == cmd_search_more) { ctx->to = timeout_add(0, cmd_search_more_callback, cmd); cmd->state = CLIENT_COMMAND_STATE_WAIT_EXTERNAL; } return FALSE; } static int imap_search_deinit(struct imap_search_context *ctx) { int ret = 0; if (mailbox_search_deinit(&ctx->search_ctx) < 0) ret = -1; /* Send the result also after failing. It might have something useful, even though it didn't fully succeed. The client should be able to realize that there was some failure because NO is returned. */ if (!ctx->cmd->cancel && (ret == 0 || array_count(&ctx->result) > 0)) imap_search_send_result(ctx); if (ret < 0 || ctx->cmd->cancel) { /* search failed */ if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) array_clear(&ctx->cmd->client->search_saved_uidset); } (void)mailbox_transaction_commit(&ctx->trans); if (ctx->to != NULL) timeout_remove(&ctx->to); if (array_is_created(&ctx->relevancy_scores)) array_free(&ctx->relevancy_scores); array_free(&ctx->result); mail_search_args_deinit(ctx->sargs); mail_search_args_unref(&ctx->sargs); imap_search_context_free(ctx); ctx->cmd->context = NULL; return ret; } void imap_search_context_free(struct imap_search_context *ctx) { if (ctx->fetch_ctx != NULL) { imap_fetch_free(&ctx->fetch_ctx); pool_unref(&ctx->fetch_pool); } } void imap_search_update_free(struct imap_search_update *update) { if (update->fetch_ctx != NULL) { imap_fetch_free(&update->fetch_ctx); pool_unref(&update->fetch_pool); } mailbox_search_result_free(&update->result); i_free(update->tag); } dovecot-2.2.33.2/src/imap/cmd-unselect.c0000644000175000017500000000066313123174404014575 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_unselect(struct client_command_context *cmd) { struct client *client = cmd->client; if (!client_verify_open_mailbox(cmd)) return TRUE; i_assert(client->mailbox_change_lock == NULL); imap_client_close_mailbox(client); client_send_tagline(cmd, "OK Unselect completed."); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-sort.c0000644000175000017500000000653313123174404013744 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "buffer.h" #include "imap-commands.h" #include "imap-search-args.h" #include "imap-search.h" struct sort_name { enum mail_sort_type type; const char *name; }; static struct sort_name sort_names[] = { { MAIL_SORT_ARRIVAL, "arrival" }, { MAIL_SORT_CC, "cc" }, { MAIL_SORT_DATE, "date" }, { MAIL_SORT_FROM, "from" }, { MAIL_SORT_SIZE, "size" }, { MAIL_SORT_SUBJECT, "subject" }, { MAIL_SORT_TO, "to" }, { MAIL_SORT_RELEVANCY, "x-score" }, /* FIXME: obsolete */ { MAIL_SORT_RELEVANCY, "relevancy" }, { MAIL_SORT_DISPLAYFROM, "displayfrom" }, { MAIL_SORT_DISPLAYTO, "displayto" }, { MAIL_SORT_END, NULL } }; static int get_sort_program(struct client_command_context *cmd, const struct imap_arg *args, enum mail_sort_type program[MAX_SORT_PROGRAM_SIZE]) { enum mail_sort_type mask = 0; const char *arg; unsigned int i, pos; bool reverse, last_reverse; if (IMAP_ARG_IS_EOL(args)) { /* empyty list */ client_send_command_error(cmd, "Empty sort program."); return -1; } pos = 0; reverse = last_reverse = FALSE; for (; imap_arg_get_astring(args, &arg); args++) { last_reverse = strcasecmp(arg, "reverse") == 0; if (last_reverse) { reverse = !reverse; continue; } for (i = 0; sort_names[i].type != MAIL_SORT_END; i++) { if (strcasecmp(arg, sort_names[i].name) == 0) break; } if (sort_names[i].type == MAIL_SORT_END) { client_send_command_error(cmd, t_strconcat( "Unknown sort argument: ", arg, NULL)); return -1; } if ((mask & sort_names[i].type) != 0) continue; mask |= sort_names[i].type; /* @UNSAFE: mask check should prevent us from ever overflowing */ i_assert(pos < MAX_SORT_PROGRAM_SIZE-1); program[pos++] = sort_names[i].type | (reverse ? MAIL_SORT_FLAG_REVERSE : 0); reverse = FALSE; } if (last_reverse) { client_send_command_error(cmd, "Sort list ends with REVERSE."); return -1; } program[pos] = MAIL_SORT_END; if (!IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Invalid sort list argument."); return -1; } return 0; } bool cmd_sort(struct client_command_context *cmd) { struct imap_search_context *ctx; struct mail_search_args *sargs; enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE]; const struct imap_arg *args, *list_args; const char *charset; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ctx = p_new(cmd->pool, struct imap_search_context, 1); ctx->cmd = cmd; if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) { /* error / waiting for unambiguity */ return ret < 0; } /* sort program */ if (!imap_arg_get_list(args, &list_args)) { client_send_command_error(cmd, "Invalid sort argument."); imap_search_context_free(ctx); return TRUE; } if (get_sort_program(cmd, list_args, sort_program) < 0) { imap_search_context_free(ctx); return TRUE; } args++; /* charset */ if (!imap_arg_get_astring(args, &charset)) { client_send_command_error(cmd, "Invalid charset argument."); imap_search_context_free(ctx); return TRUE; } args++; ret = imap_search_args_build(cmd, args, charset, &sargs); if (ret <= 0) { imap_search_context_free(ctx); return ret < 0; } return imap_search_start(ctx, sargs, sort_program); } dovecot-2.2.33.2/src/imap/cmd-unsubscribe.c0000644000175000017500000000035013123174404015270 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_unsubscribe(struct client_command_context *cmd) { return cmd_subscribe_full(cmd, FALSE); } dovecot-2.2.33.2/src/imap/cmd-close.c0000644000175000017500000000204313123174404014052 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" #include "imap-expunge.h" bool cmd_close(struct client_command_context *cmd) { struct client *client = cmd->client; struct mailbox *mailbox = client->mailbox; struct mail_storage *storage; const char *errstr, *tagged_reply = "OK Close completed."; enum mail_error error = MAIL_ERROR_NONE; if (!client_verify_open_mailbox(cmd)) return TRUE; i_assert(client->mailbox_change_lock == NULL); storage = mailbox_get_storage(mailbox); if (imap_expunge(mailbox, NULL, &client->expunged_count) < 0) { errstr = mailbox_get_last_error(mailbox, &error); if (error != MAIL_ERROR_PERM) client_send_untagged_storage_error(client, storage); else { tagged_reply = t_strdup_printf( "OK Closed without expunging: %s", errstr); } } if (mailbox_sync(mailbox, 0) < 0) client_send_untagged_storage_error(client, storage); imap_client_close_mailbox(client); client_send_tagline(cmd, tagged_reply); return TRUE; } dovecot-2.2.33.2/src/imap/imap-notify.c0000644000175000017500000003531113165463624014457 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "imap-quote.h" #include "mailbox-list-iter.h" #include "mailbox-list-notify.h" #include "mail-search.h" #include "mail-search-build.h" #include "imap-commands.h" #include "imap-fetch.h" #include "imap-list.h" #include "imap-status.h" #include "imap-notify.h" #define IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS 1000 static int imap_notify_list(struct imap_notify_namespace *notify_ns, const struct mailbox_list_notify_rec *rec, enum mailbox_info_flags flags) { string_t *str = t_str_new(128); char ns_sep = mail_namespace_get_sep(notify_ns->ns); str_append(str, "* LIST ("); imap_mailbox_flags2str(str, flags); str_append(str, ") \""); if (ns_sep == '\\') str_append_c(str, '\\'); str_append_c(str, ns_sep); str_append(str, "\" "); imap_append_astring(str, rec->vname); if (rec->old_vname != NULL) { str_append(str, " (\"OLDNAME\" ("); imap_append_astring(str, rec->old_vname); str_append(str, "))"); } return client_send_line_next(notify_ns->ctx->client, str_c(str)); } static int imap_notify_status(struct imap_notify_namespace *notify_ns, const struct mailbox_list_notify_rec *rec) { struct client *client = notify_ns->ctx->client; struct mailbox *box; struct imap_status_items items; struct imap_status_result result; enum mail_error error; int ret = 1; i_zero(&items); if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0) items.status |= STATUS_HIGHESTMODSEQ; box = mailbox_alloc(notify_ns->ns->list, rec->vname, 0); mailbox_set_reason(box, "NOTIFY STATUS"); if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) { items.status |= STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN; } if ((rec->events & (MAILBOX_LIST_NOTIFY_APPENDS | MAILBOX_LIST_NOTIFY_EXPUNGES)) != 0) items.status |= STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN; if ((rec->events & MAILBOX_LIST_NOTIFY_SEEN_CHANGES) != 0) items.status |= STATUS_UNSEEN; if ((rec->events & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) != 0) { /* if HIGHESTMODSEQ isn't being sent, don't send anything */ } if (items.status == 0) { /* don't send anything */ } else if (mailbox_get_status(box, items.status, &result.status) < 0) { /* hide permission errors from client. we don't want to leak information about existence of mailboxes where user doesn't have access to */ (void)mailbox_get_last_error(box, &error); if (error != MAIL_ERROR_PERM) ret = -1; } else { ret = imap_status_send(client, rec->vname, &items, &result); } mailbox_free(&box); return ret; } static int imap_notify_next(struct imap_notify_namespace *notify_ns, const struct mailbox_list_notify_rec *rec) { enum mailbox_info_flags mailbox_flags; int ret; if ((rec->events & MAILBOX_LIST_NOTIFY_CREATE) != 0) { if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name, &mailbox_flags) < 0) mailbox_flags = 0; if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) <= 0) return ret; } if ((rec->events & MAILBOX_LIST_NOTIFY_DELETE) != 0) { if ((ret = imap_notify_list(notify_ns, rec, MAILBOX_NONEXISTENT)) < 0) return ret; } if ((rec->events & MAILBOX_LIST_NOTIFY_RENAME) != 0) { if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name, &mailbox_flags) < 0) mailbox_flags = 0; if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0) return ret; } if ((rec->events & MAILBOX_LIST_NOTIFY_SUBSCRIBE) != 0) { if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name, &mailbox_flags) < 0) mailbox_flags = 0; if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags | MAILBOX_SUBSCRIBED)) < 0) return ret; } if ((rec->events & MAILBOX_LIST_NOTIFY_UNSUBSCRIBE) != 0) { if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name, &mailbox_flags) < 0) mailbox_flags = 0; if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0) return ret; } if ((rec->events & (MAILBOX_LIST_NOTIFY_UIDVALIDITY | MAILBOX_LIST_NOTIFY_APPENDS | MAILBOX_LIST_NOTIFY_EXPUNGES | MAILBOX_LIST_NOTIFY_SEEN_CHANGES | MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0) { if ((ret = imap_notify_status(notify_ns, rec)) < 0) return ret; } return 1; } static bool imap_notify_match_event(struct imap_notify_namespace *notify_ns, const struct imap_notify_mailboxes *notify_boxes, const struct mailbox_list_notify_rec *rec) { enum imap_notify_event wanted_events = notify_boxes->events; struct mailbox *box; /* check for mailbox list events first */ if ((wanted_events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) { if ((rec->events & (MAILBOX_LIST_NOTIFY_CREATE | MAILBOX_LIST_NOTIFY_DELETE | MAILBOX_LIST_NOTIFY_RENAME)) != 0) return TRUE; } if ((wanted_events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) { if ((rec->events & (MAILBOX_LIST_NOTIFY_SUBSCRIBE | MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0) return TRUE; } /* if this is an event for the selected mailbox, ignore it */ box = notify_ns->ctx->client->mailbox; if (box != NULL && mailbox_equals(box, notify_ns->ns, rec->vname)) return FALSE; if ((wanted_events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW | IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE | IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0) { if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) return TRUE; } if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) { if ((rec->events & MAILBOX_LIST_NOTIFY_APPENDS) != 0) return TRUE; } if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) { if ((rec->events & MAILBOX_LIST_NOTIFY_EXPUNGES) != 0) return TRUE; } if ((wanted_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) { if ((rec->events & (MAILBOX_LIST_NOTIFY_SEEN_CHANGES | MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0) return TRUE; } return FALSE; } bool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns, const struct imap_notify_mailboxes *notify_boxes, const char *vname) { struct mailbox *box; const char *const *namep; size_t name_len; char ns_sep; bool ret; switch (notify_boxes->type) { case IMAP_NOTIFY_TYPE_SUBSCRIBED: box = mailbox_alloc(notify_ns->ns->list, vname, 0); mailbox_set_reason(box, "NOTIFY is subscribed"); ret = mailbox_is_subscribed(box); mailbox_free(&box); return ret; case IMAP_NOTIFY_TYPE_SUBTREE: ns_sep = mail_namespace_get_sep(notify_ns->ns); array_foreach(¬ify_boxes->names, namep) { name_len = strlen(*namep); if (name_len == 0) { /* everything under root. NOTIFY spec itself doesn't define this, but we use it for implementing "personal" */ return TRUE; } if (strncmp(*namep, vname, name_len) == 0 && (vname[name_len] == '\0' || vname[name_len] == ns_sep)) return TRUE; } break; case IMAP_NOTIFY_TYPE_MAILBOX: array_foreach(¬ify_boxes->names, namep) { if (strcmp(*namep, vname) == 0) return TRUE; } break; } return FALSE; } static bool imap_notify_match(struct imap_notify_namespace *notify_ns, const struct mailbox_list_notify_rec *rec) { const struct imap_notify_mailboxes *notify_boxes; array_foreach(¬ify_ns->mailboxes, notify_boxes) { if (imap_notify_match_event(notify_ns, notify_boxes, rec) && imap_notify_match_mailbox(notify_ns, notify_boxes, rec->vname)) return TRUE; } return FALSE; } static int imap_client_notify_ns(struct imap_notify_namespace *notify_ns) { const struct mailbox_list_notify_rec *rec; int ret, ret2 = 1; if (notify_ns->notify == NULL) return 0; /* notifications not supported in this namespace */ while ((ret = mailbox_list_notify_next(notify_ns->notify, &rec)) > 0) { if (imap_notify_match(notify_ns, rec)) T_BEGIN { ret2 = imap_notify_next(notify_ns, rec); } T_END; if (ret2 <= 0) break; } if (ret < 0) { /* failed to get some notifications */ return -1; } return ret2; } static int imap_client_notify_selected(struct client *client) { struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx; int ret; if (!fetch_ctx->state.fetching) return 1; if ((ret = imap_fetch_more_no_lock_update(fetch_ctx)) <= 0) return ret; /* finished the FETCH */ if (imap_fetch_end(fetch_ctx) < 0) return -1; return 1; } static int imap_client_notify_more(struct client *client) { struct imap_notify_namespace *notify_ns; int ret = 1; /* send notifications for selected mailbox first. note that it may leave the client's output stream in the middle of a FETCH reply. */ if (client->notify_ctx->fetch_ctx != NULL) { if ((ret = imap_client_notify_selected(client)) < 0) { client->notify_ctx->fetch_ctx->state.failed = FALSE; ret = -1; } } /* send notifications for non-selected mailboxes */ array_foreach_modifiable(&client->notify_ctx->namespaces, notify_ns) { if (ret == 0) break; if (imap_client_notify_ns(notify_ns) < 0) ret = -1; } if (ret < 0) { client_send_line(notify_ns->ctx->client, "* NO NOTIFY error, some events may have got lost"); } return ret; } int imap_client_notify_newmails(struct client *client) { struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx; struct mailbox_status status; struct mail_search_args *search_args; struct mail_search_arg *arg; i_assert(client->mailbox != NULL); if (fetch_ctx == NULL) { /* FETCH notifications not enabled in this session */ return 1; } if (client->notify_ctx->notifying) return imap_client_notify_more(client); client->notify_ctx->notifying = TRUE; i_assert(!fetch_ctx->state.fetching); mailbox_get_open_status(client->mailbox, STATUS_UIDNEXT, &status); search_args = mail_search_build_init(); arg = mail_search_build_add(search_args, SEARCH_UIDSET); p_array_init(&arg->value.seqset, search_args->pool, 1); seq_range_array_add_range(&arg->value.seqset, client->notify_uidnext, status.uidnext-1); client->notify_uidnext = status.uidnext; imap_fetch_begin(fetch_ctx, client->mailbox, search_args); mail_search_args_unref(&search_args); return imap_client_notify_more(client); } void imap_client_notify_finished(struct client *client) { if (client->notify_ctx != NULL) client->notify_ctx->notifying = FALSE; } static void notify_callback(struct imap_notify_namespace *notify_ns) { o_stream_cork(notify_ns->ctx->client->output); imap_client_notify_ns(notify_ns); o_stream_uncork(notify_ns->ctx->client->output); } static enum mailbox_list_notify_event imap_events_to_notify(enum imap_notify_event events) { enum mailbox_list_notify_event ret = 0; if ((events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) { ret |= MAILBOX_LIST_NOTIFY_APPENDS | MAILBOX_LIST_NOTIFY_UIDVALIDITY; } if ((events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) { ret |= MAILBOX_LIST_NOTIFY_EXPUNGES | MAILBOX_LIST_NOTIFY_UIDVALIDITY; } if ((events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) { ret |= MAILBOX_LIST_NOTIFY_SEEN_CHANGES | MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES | MAILBOX_LIST_NOTIFY_UIDVALIDITY; } if ((events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) { ret |= MAILBOX_LIST_NOTIFY_CREATE | MAILBOX_LIST_NOTIFY_DELETE | MAILBOX_LIST_NOTIFY_RENAME; } if ((events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) { ret |= MAILBOX_LIST_NOTIFY_SUBSCRIBE | MAILBOX_LIST_NOTIFY_UNSUBSCRIBE; } return ret; } static void imap_notify_callback(struct mailbox *box, struct client *client) { struct client_command_context *cmd; enum mailbox_sync_flags sync_flags = 0; i_assert(client->command_queue_size == 0); i_assert(box == client->mailbox); /* create a fake command to handle this */ cmd = client_command_alloc(client); cmd->tag = "*"; cmd->name = "NOTIFY-CALLBACK"; if (!client->notify_ctx->selected_immediate_expunges) sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES; if (cmd_sync(cmd, sync_flags, 0, NULL)) i_unreached(); (void)cmd_sync_delayed(client); } static void imap_notify_watch_selected_mailbox(struct client *client) { i_assert(client->command_queue_size == 0); if (client->mailbox == NULL) { /* mailbox not selected */ return; } if (client->notify_ctx == NULL || !client->notify_ctx->selected_set) { /* client doesn't want selected mailbox notifications */ return; } mailbox_notify_changes(client->mailbox, imap_notify_callback, client); client->notify_ctx->watching_mailbox = TRUE; } static void imap_notify_watch_timeout(struct client *client) { timeout_remove(&client->notify_ctx->to_watch); imap_notify_watch_selected_mailbox(client); } void imap_client_notify_command_freed(struct client *client) { struct imap_notify_context *ctx = client->notify_ctx; if (ctx == NULL) return; if (client->command_queue_size > 0) { /* don't add it until all commands are finished */ i_assert(ctx->to_watch == NULL); return; } /* add mailbox watch back after a small delay. if another command is started this timeout is aborted. */ ctx->to_watch = timeout_add(IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS, imap_notify_watch_timeout, client); } void imap_client_notify_command_allocated(struct client *client) { struct imap_notify_context *ctx = client->notify_ctx; if (ctx == NULL) return; /* remove mailbox watcher before starting any commands */ if (ctx->watching_mailbox) { mailbox_notify_changes_stop(client->mailbox); ctx->watching_mailbox = FALSE; } if (ctx->to_watch != NULL) timeout_remove(&ctx->to_watch); } int imap_notify_begin(struct imap_notify_context *ctx) { struct imap_notify_namespace *notify_ns; const struct imap_notify_mailboxes *notify_boxes; enum mailbox_list_notify_event notify_events; int ret = -1; array_foreach_modifiable(&ctx->namespaces, notify_ns) { notify_events = 0; array_foreach(¬ify_ns->mailboxes, notify_boxes) { notify_events |= imap_events_to_notify(notify_boxes->events); } if (mailbox_list_notify_init(notify_ns->ns->list, notify_events, ¬ify_ns->notify) < 0) { /* notifications not supported */ } else { ret = 0; mailbox_list_notify_wait(notify_ns->notify, notify_callback, notify_ns); } } /* enable NOTIFY as long as even one namespace supports it, ignore the rest */ return ret; } void imap_notify_deinit(struct imap_notify_context **_ctx) { struct imap_notify_context *ctx = *_ctx; struct imap_notify_namespace *notify_ns; *_ctx = NULL; array_foreach_modifiable(&ctx->namespaces, notify_ns) { if (notify_ns->notify != NULL) mailbox_list_notify_deinit(¬ify_ns->notify); } if (ctx->to_watch != NULL) timeout_remove(&ctx->to_watch); if (ctx->fetch_ctx != NULL) imap_fetch_free(&ctx->fetch_ctx); pool_unref(&ctx->pool); } void imap_notify_flush(struct imap_notify_context *ctx) { struct imap_notify_namespace *notify_ns; array_foreach_modifiable(&ctx->namespaces, notify_ns) { if (notify_ns->notify != NULL) mailbox_list_notify_flush(notify_ns->notify); } } dovecot-2.2.33.2/src/imap/imap-client-hibernate.c0000644000175000017500000001622213165463624016364 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "fdpass.h" #include "net.h" #include "ostream.h" #include "write-full.h" #include "base64.h" #include "str.h" #include "strescape.h" #include "master-service.h" #include "mailbox-watch.h" #include "imap-state.h" #include "imap-client.h" #include #define IMAP_HIBERNATE_SOCKET_NAME "imap-hibernate" #define IMAP_HIBERNATE_SEND_TIMEOUT_SECS 10 #define IMAP_HIBERNATE_HANDSHAKE "VERSION\timap-hibernate\t1\t0\n" static int imap_hibernate_handshake(int fd, const char *path) { char buf[1024]; ssize_t ret; if (write_full(fd, IMAP_HIBERNATE_HANDSHAKE, strlen(IMAP_HIBERNATE_HANDSHAKE)) < 0) { i_error("write(%s) failed: %m", path); return -1; } else if ((ret = read(fd, buf, sizeof(buf)-1)) < 0) { i_error("read(%s) failed: %m", path); return -1; } else if (ret > 0 && buf[ret-1] == '\n') { buf[ret-1] = '\0'; if (version_string_verify(buf, "imap-hibernate", 1)) return 0; } i_error("%s sent invalid VERSION handshake: %s", path, buf); return -1; } static void imap_hibernate_write_cmd(struct client *client, string_t *cmd, const buffer_t *state, int fd_notify) { struct stat peer_st; const char *tag; tag = client->command_queue == NULL ? NULL : client->command_queue->tag; str_append_tabescaped(cmd, client->user->username); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, client->user->set->mail_log_prefix); str_printfa(cmd, "\tidle_notify_interval=%u", client->set->imap_idle_notify_interval); if (fstat(client->fd_in, &peer_st) == 0) { str_printfa(cmd, "\tpeer_dev_major=%lu\tpeer_dev_minor=%lu\tpeer_ino=%llu", (unsigned long)major(peer_st.st_dev), (unsigned long)minor(peer_st.st_dev), (unsigned long long)peer_st.st_ino); } if (client->session_id != NULL) { str_append(cmd, "\tsession="); str_append_tabescaped(cmd, client->session_id); } if (client->user->session_create_time != 0) { str_printfa(cmd, "\tsession_created=%s", dec2str(client->user->session_create_time)); } if (client->user->local_ip != NULL) str_printfa(cmd, "\tlip=%s", net_ip2addr(client->user->local_ip)); if (client->user->remote_ip != NULL) str_printfa(cmd, "\trip=%s", net_ip2addr(client->user->remote_ip)); if (client->userdb_fields != NULL) { string_t *userdb_fields = t_str_new(256); unsigned int i; for (i = 0; client->userdb_fields[i] != NULL; i++) { if (i > 0) str_append_c(userdb_fields, '\t'); str_append_tabescaped(userdb_fields, client->userdb_fields[i]); } str_append(cmd, "\tuserdb_fields="); str_append_tabescaped(cmd, str_c(userdb_fields)); } if (client->user->uid != (uid_t)-1) str_printfa(cmd, "\tuid=%s", dec2str(client->user->uid)); if (client->user->gid != (gid_t)-1) str_printfa(cmd, "\tgid=%s", dec2str(client->user->gid)); if (tag != NULL) str_printfa(cmd, "\ttag=%s", tag); str_append(cmd, "\tstats="); str_append_tabescaped(cmd, client_stats(client)); if (client->command_queue != NULL && strcasecmp(client->command_queue->name, "IDLE") == 0) str_append(cmd, "\tidle-cmd"); if (fd_notify != -1) str_append(cmd, "\tnotify_fd"); str_append(cmd, "\tstate="); base64_encode(state->data, state->used, cmd); str_append_c(cmd, '\n'); } static int imap_hibernate_process_send_cmd(int fd_socket, const char *path, const string_t *cmd, int fd_client) { ssize_t ret; i_assert(fd_socket != -1); i_assert(str_len(cmd) > 1); if (imap_hibernate_handshake(fd_socket, path) < 0) return -1; if ((ret = fd_send(fd_socket, fd_client, str_data(cmd), 1)) < 0) { i_error("fd_send(%s) failed: %m", path); return -1; } if ((ret = write_full(fd_socket, str_data(cmd)+1, str_len(cmd)-1)) < 0) { i_error("write(%s) failed: %m", path); return -1; } return 0; } static int imap_hibernate_process_read(int fd, const char *path) { char buf[1024]; ssize_t ret; if ((ret = read(fd, buf, sizeof(buf)-1)) < 0) { i_error("read(%s) failed: %m", path); return -1; } else if (ret == 0) { i_error("%s disconnected", path); return -1; } else if (buf[0] != '+') { buf[ret] = '\0'; i_error("%s returned failure: %s", path, ret > 0 && buf[0] == '-' ? buf+1 : buf); return -1; } else { return 0; } } static int imap_hibernate_process_send(struct client *client, const buffer_t *state, int fd_notify, int *fd_r) { string_t *cmd = t_str_new(512); const char *path; ssize_t ret = 0; int fd; i_assert(state->used > 0); *fd_r = -1; path = t_strconcat(client->user->set->base_dir, "/"IMAP_HIBERNATE_SOCKET_NAME, NULL); fd = net_connect_unix_with_retries(path, 1000); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m", path); return -1; } net_set_nonblock(fd, FALSE); imap_hibernate_write_cmd(client, cmd, state, fd_notify); alarm(IMAP_HIBERNATE_SEND_TIMEOUT_SECS); if (imap_hibernate_process_send_cmd(fd, path, cmd, client->fd_in) < 0 || imap_hibernate_process_read(fd, path) < 0) ret = -1; else if (fd_notify != -1) { if ((ret = fd_send(fd, fd_notify, "\n", 1)) < 0) i_error("fd_send(%s) failed: %m", path); else ret = imap_hibernate_process_read(fd, path); } alarm(0); if (ret < 0) { net_disconnect(fd); return -1; } *fd_r = fd; return 0; } bool imap_client_hibernate(struct client **_client) { struct client *client = *_client; buffer_t *state; const char *error; int ret, fd_notify = -1, fd_hibernate = -1; if (client->fd_in != client->fd_out) { /* we won't try to hibernate stdio clients */ return FALSE; } if (o_stream_get_buffer_used_size(client->output) > 0) { /* wait until we've sent the pending output to client */ return FALSE; } state = buffer_create_dynamic(default_pool, 1024); ret = imap_state_export_internal(client, state, &error); if (ret < 0) { i_error("Couldn't hibernate imap client: " "Couldn't export state: %s (mailbox=%s)", error, client->mailbox == NULL ? "" : mailbox_get_vname(client->mailbox)); } else if (ret == 0 && client->user->mail_debug) { i_debug("Couldn't hibernate imap client: " "Couldn't export state: %s (mailbox=%s)", error, client->mailbox == NULL ? "" : mailbox_get_vname(client->mailbox)); } if (ret > 0 && client->mailbox != NULL) { fd_notify = mailbox_watch_extract_notify_fd(client->mailbox, &error); if (fd_notify == -1) { if (client->user->mail_debug) { i_debug("Couldn't hibernate imap client: " "Couldn't extract notifications fd: %s", error); } ret = -1; } } if (ret > 0) { if (imap_hibernate_process_send(client, state, fd_notify, &fd_hibernate) < 0) ret = -1; } if (fd_notify != -1) i_close_fd(&fd_notify); if (ret > 0) { /* hide the disconnect log message, because the client didn't actually log out */ client->disconnected = TRUE; client->hibernated = TRUE; client_destroy(client, NULL); *_client = NULL; } /* notify imap-hibernate that we're done by closing the connection. do this only after client is destroyed. this way imap-hibernate won't try to launch another imap process too early and cause problems (like sending duplicate session ID to stats process) */ if (fd_hibernate != -1) net_disconnect(fd_hibernate); buffer_free(&state); return ret > 0; } dovecot-2.2.33.2/src/imap/cmd-lsub.c0000644000175000017500000000033313123174404013712 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_lsub(struct client_command_context *cmd) { return cmd_list_full(cmd, TRUE); } dovecot-2.2.33.2/src/imap/cmd-id.c0000644000175000017500000000126513123174404013346 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-id.h" bool cmd_id(struct client_command_context *cmd) { const struct imap_settings *set = cmd->client->set; const struct imap_arg *args; const char *value; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!cmd->client->id_logged) { cmd->client->id_logged = TRUE; value = imap_id_args_get_log_reply(args, set->imap_id_log); if (value != NULL) i_info("ID sent: %s", value); } client_send_line(cmd->client, t_strdup_printf( "* ID %s", imap_id_reply_generate(set->imap_id_send))); client_send_tagline(cmd, "OK ID completed."); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-fetch.c0000644000175000017500000002260513165463624014057 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ostream.h" #include "imap-resp-code.h" #include "imap-commands.h" #include "imap-fetch.h" #include "imap-search-args.h" #include "mail-search.h" static const char *all_macro[] = { "FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE", NULL }; static const char *fast_macro[] = { "FLAGS", "INTERNALDATE", "RFC822.SIZE", NULL }; static const char *full_macro[] = { "FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE", "BODY", NULL }; static bool imap_fetch_cmd_init_handler(struct imap_fetch_context *ctx, struct client_command_context *cmd, const char *name, const struct imap_arg **args) { struct imap_fetch_init_context init_ctx; i_zero(&init_ctx); init_ctx.fetch_ctx = ctx; init_ctx.pool = ctx->ctx_pool; init_ctx.name = name; init_ctx.args = *args; if (!imap_fetch_init_handler(&init_ctx)) { i_assert(init_ctx.error != NULL); client_send_command_error(cmd, init_ctx.error); return FALSE; } *args = init_ctx.args; return TRUE; } static bool fetch_parse_args(struct imap_fetch_context *ctx, struct client_command_context *cmd, const struct imap_arg *arg, const struct imap_arg **next_arg_r) { const char *str, *const *macro; if (cmd->uid) { if (!imap_fetch_cmd_init_handler(ctx, cmd, "UID", &arg)) return FALSE; } if (imap_arg_get_atom(arg, &str)) { str = t_str_ucase(str); arg++; /* handle macros first */ if (strcmp(str, "ALL") == 0) macro = all_macro; else if (strcmp(str, "FAST") == 0) macro = fast_macro; else if (strcmp(str, "FULL") == 0) macro = full_macro; else { macro = NULL; if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (macro != NULL) { while (*macro != NULL) { if (!imap_fetch_cmd_init_handler(ctx, cmd, *macro, &arg)) return FALSE; macro++; } } *next_arg_r = arg; } else { *next_arg_r = arg + 1; arg = imap_arg_as_list(arg); if (IMAP_ARG_IS_EOL(arg)) { client_send_command_error(cmd, "FETCH list is empty."); return FALSE; } while (imap_arg_get_atom(arg, &str)) { str = t_str_ucase(str); arg++; if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (!IMAP_ARG_IS_EOL(arg)) { client_send_command_error(cmd, "FETCH list contains non-atoms."); return FALSE; } } return TRUE; } static bool fetch_parse_modifier(struct imap_fetch_context *ctx, struct client_command_context *cmd, struct mail_search_args *search_args, const char *name, const struct imap_arg **args, bool *send_vanished) { const char *str; uint64_t modseq; if (strcmp(name, "CHANGEDSINCE") == 0) { if (cmd->client->nonpermanent_modseqs) { client_send_command_error(cmd, "FETCH CHANGEDSINCE can't be used with non-permanent modseqs"); return FALSE; } if (!imap_arg_get_atom(*args, &str) || str_to_uint64(str, &modseq) < 0) { client_send_command_error(cmd, "Invalid CHANGEDSINCE modseq."); return FALSE; } *args += 1; imap_search_add_changed_since(search_args, modseq); imap_fetch_init_nofail_handler(ctx, imap_fetch_modseq_init); return TRUE; } if (strcmp(name, "VANISHED") == 0 && cmd->uid) { if ((ctx->client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) { client_send_command_error(cmd, "QRESYNC not enabled"); return FALSE; } *send_vanished = TRUE; return TRUE; } client_send_command_error(cmd, "Unknown FETCH modifier"); return FALSE; } static bool fetch_parse_modifiers(struct imap_fetch_context *ctx, struct client_command_context *cmd, struct mail_search_args *search_args, const struct imap_arg *args, bool *send_vanished_r) { const char *name; *send_vanished_r = FALSE; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &name)) { client_send_command_error(cmd, "FETCH modifiers contain non-atoms."); return FALSE; } args++; if (!fetch_parse_modifier(ctx, cmd, search_args, t_str_ucase(name), &args, send_vanished_r)) return FALSE; } if (*send_vanished_r && (search_args->args->next == NULL || search_args->args->next->type != SEARCH_MODSEQ)) { client_send_command_error(cmd, "VANISHED used without CHANGEDSINCE"); return FALSE; } return TRUE; } static bool cmd_fetch_finished(struct client_command_context *cmd ATTR_UNUSED) { return TRUE; } static bool imap_fetch_is_failed_retry(struct imap_fetch_context *ctx) { if (!array_is_created(&ctx->client->fetch_failed_uids) || !array_is_created(&ctx->fetch_failed_uids)) return FALSE; return seq_range_array_have_common(&ctx->client->fetch_failed_uids, &ctx->fetch_failed_uids); } static void imap_fetch_add_failed_uids(struct imap_fetch_context *ctx) { if (!array_is_created(&ctx->fetch_failed_uids)) return; if (!array_is_created(&ctx->client->fetch_failed_uids)) { p_array_init(&ctx->client->fetch_failed_uids, ctx->client->pool, array_count(&ctx->fetch_failed_uids)); } seq_range_array_merge(&ctx->client->fetch_failed_uids, &ctx->fetch_failed_uids); } static bool cmd_fetch_finish(struct imap_fetch_context *ctx, struct client_command_context *cmd) { static const char *ok_message = "OK Fetch completed."; const char *tagged_reply = ok_message; enum mail_error error; bool seen_flags_changed = ctx->state.seen_flags_changed; if (ctx->state.skipped_expunged_msgs) { tagged_reply = "OK ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some messages were already expunged."; } if (imap_fetch_end(ctx) < 0) { const char *errstr; if (cmd->client->output->closed) { /* If we're canceling we need to finish this command or we'll assert crash. But normally we want to return FALSE so that the disconnect message logs about this fetch command and that these latest output bytes are included in it (which wouldn't happen if we called client_disconnect() here directly). */ cmd->func = cmd_fetch_finished; imap_fetch_free(&ctx); return cmd->cancel; } if (ctx->error == MAIL_ERROR_NONE) errstr = mailbox_get_last_error(cmd->client->mailbox, &error); else { errstr = ctx->errstr; error = ctx->error; } if (error == MAIL_ERROR_CONVERSION || error == MAIL_ERROR_INVALIDDATA) { /* a) BINARY found unsupported Content-Transfer-Encoding b) Content was invalid */ tagged_reply = t_strdup_printf( "NO ["IMAP_RESP_CODE_UNKNOWN_CTE"] %s", errstr); } else if (cmd->client->set->parsed_fetch_failure != IMAP_CLIENT_FETCH_FAILURE_NO_AFTER || imap_fetch_is_failed_retry(ctx)) { /* By default we never want to reply NO to FETCH requests, because many IMAP clients become confused about what they should on NO. A disconnection causes less confusion. */ client_disconnect_with_error(cmd->client, t_strconcat("FETCH failed: ", errstr, NULL)); imap_fetch_free(&ctx); return TRUE; } else { /* Use a tagged NO to FETCH failure, but only if client hasn't repeated the FETCH to the same email (so that we avoid infinitely retries from client.) */ imap_fetch_add_failed_uids(ctx); tagged_reply = t_strdup_printf( "NO ["IMAP_RESP_CODE_SERVERBUG"] %s", errstr); } } imap_fetch_free(&ctx); return cmd_sync(cmd, (seen_flags_changed ? 0 : MAILBOX_SYNC_FLAG_FAST) | (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), 0, tagged_reply); } static bool cmd_fetch_continue(struct client_command_context *cmd) { struct imap_fetch_context *ctx = cmd->context; if (imap_fetch_more(ctx, cmd) == 0) { /* unfinished */ return FALSE; } return cmd_fetch_finish(ctx, cmd); } bool cmd_fetch(struct client_command_context *cmd) { struct client *client = cmd->client; struct imap_fetch_context *ctx; const struct imap_arg *args, *next_arg, *list_arg; struct mail_search_args *search_args; struct imap_fetch_qresync_args qresync_args; const char *messageset; bool send_vanished = FALSE; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; /* [(modifiers)] */ if (!imap_arg_get_atom(&args[0], &messageset) || (args[1].type != IMAP_ARG_LIST && args[1].type != IMAP_ARG_ATOM) || (!IMAP_ARG_IS_EOL(&args[2]) && args[2].type != IMAP_ARG_LIST)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } /* UID FETCH VANISHED needs the uidset, so convert it to sequence set later */ ret = imap_search_get_anyset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; ctx = imap_fetch_alloc(client, cmd->pool, imap_client_command_get_reason(cmd)); if (!fetch_parse_args(ctx, cmd, &args[1], &next_arg) || (imap_arg_get_list(next_arg, &list_arg) && !fetch_parse_modifiers(ctx, cmd, search_args, list_arg, &send_vanished))) { imap_fetch_free(&ctx); mail_search_args_unref(&search_args); return TRUE; } if (send_vanished) { i_zero(&qresync_args); if (imap_fetch_send_vanished(client, client->mailbox, search_args, &qresync_args) < 0) { mail_search_args_unref(&search_args); return cmd_fetch_finish(ctx, cmd); } } imap_fetch_begin(ctx, client->mailbox, search_args); mail_search_args_unref(&search_args); if (imap_fetch_more(ctx, cmd) == 0) { /* unfinished */ cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; cmd->func = cmd_fetch_continue; cmd->context = ctx; return FALSE; } return cmd_fetch_finish(ctx, cmd); } dovecot-2.2.33.2/src/imap/cmd-expunge.c0000644000175000017500000000332013123174404014417 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" #include "imap-search-args.h" #include "imap-expunge.h" static bool ATTR_NULL(2) cmd_expunge_finish(struct client_command_context *cmd, struct mail_search_args *search_args) { struct client *client = cmd->client; const char *errstr; enum mail_error error = MAIL_ERROR_NONE; int ret; ret = imap_expunge(client->mailbox, search_args == NULL ? NULL : search_args->args, &client->expunged_count); if (search_args != NULL) mail_search_args_unref(&search_args); if (ret < 0) { errstr = mailbox_get_last_error(client->mailbox, &error); if (error != MAIL_ERROR_PERM) { client_send_box_error(cmd, client->mailbox); return TRUE; } else { return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, t_strdup_printf("OK Expunge ignored: %s.", errstr)); } } client->sync_seen_deletes = FALSE; return cmd_sync(cmd, MAILBOX_SYNC_FLAG_EXPUNGE, IMAP_SYNC_FLAG_SAFE, "OK Expunge completed."); } bool cmd_uid_expunge(struct client_command_context *cmd) { const struct imap_arg *args; struct mail_search_args *search_args; const char *uidset; int ret; if (!client_read_args(cmd, 1, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; if (!imap_arg_get_astring(&args[0], &uidset)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } ret = imap_search_get_seqset(cmd, uidset, TRUE, &search_args); if (ret <= 0) return ret < 0; return cmd_expunge_finish(cmd, search_args); } bool cmd_expunge(struct client_command_context *cmd) { if (!client_verify_open_mailbox(cmd)) return TRUE; return cmd_expunge_finish(cmd, NULL); } dovecot-2.2.33.2/src/imap/imap-notify.h0000644000175000017500000000414413165463624014464 00000000000000#ifndef IMAP_NOTIFY_H #define IMAP_NOTIFY_H enum imap_notify_type { IMAP_NOTIFY_TYPE_SUBSCRIBED, IMAP_NOTIFY_TYPE_SUBTREE, IMAP_NOTIFY_TYPE_MAILBOX }; enum imap_notify_event { IMAP_NOTIFY_EVENT_MESSAGE_NEW = 0x01, IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE = 0x02, IMAP_NOTIFY_EVENT_FLAG_CHANGE = 0x04, IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE = 0x08, IMAP_NOTIFY_EVENT_MAILBOX_NAME = 0x10, IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE = 0x20, IMAP_NOTIFY_EVENT_MAILBOX_METADATA_CHANGE = 0x40, IMAP_NOTIFY_EVENT_SERVER_METADATA_CHANGE = 0x80 }; #define UNSUPPORTED_EVENTS \ (IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE | \ IMAP_NOTIFY_EVENT_MAILBOX_METADATA_CHANGE | \ IMAP_NOTIFY_EVENT_SERVER_METADATA_CHANGE) struct imap_notify_mailboxes { enum imap_notify_event events; enum imap_notify_type type; ARRAY_TYPE(const_string) names; }; struct imap_notify_namespace { struct imap_notify_context *ctx; struct mail_namespace *ns; struct mailbox_list_notify *notify; ARRAY(struct imap_notify_mailboxes) mailboxes; }; struct imap_notify_context { pool_t pool; struct client *client; const char *error; ARRAY(struct imap_notify_namespace) namespaces; enum imap_notify_event selected_events; enum imap_notify_event global_used_events; unsigned int global_max_mailbox_names; struct imap_fetch_context *fetch_ctx; struct timeout *to_watch; unsigned int have_subscriptions:1; unsigned int selected_set:1; unsigned int selected_immediate_expunges:1; unsigned int send_immediate_status:1; unsigned int watching_mailbox:1; unsigned int notifying:1; }; bool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns, const struct imap_notify_mailboxes *notify_boxes, const char *vname); int imap_client_notify_newmails(struct client *client); void imap_client_notify_finished(struct client *client); void imap_client_notify_command_allocated(struct client *client); void imap_client_notify_command_freed(struct client *client); int imap_notify_begin(struct imap_notify_context *ctx); void imap_notify_deinit(struct imap_notify_context **ctx); void imap_notify_flush(struct imap_notify_context *ctx); #endif dovecot-2.2.33.2/src/imap/imap-list.h0000644000175000017500000000026413123174404014113 00000000000000#ifndef IMAP_LIST_H #define IMAP_LIST_H /* Returns TRUE if anything was added to the string. */ bool imap_mailbox_flags2str(string_t *str, enum mailbox_info_flags flags); #endif dovecot-2.2.33.2/src/imap/cmd-subscribe.c0000644000175000017500000000426413123174404014735 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-utf7.h" #include "imap-commands.h" #include "mail-namespace.h" static bool subscribe_is_valid_name(struct client_command_context *cmd, struct mailbox *box) { enum mailbox_existence existence; int ret; if ((ret = mailbox_exists(box, TRUE, &existence)) < 0) { client_send_box_error(cmd, box); return FALSE; } if (existence == MAILBOX_EXISTENCE_NONE) { client_send_tagline(cmd, t_strdup_printf( "NO "MAIL_ERRSTR_MAILBOX_NOT_FOUND, mailbox_get_vname(box))); return FALSE; } return TRUE; } static bool str_ends_with_char(const char *str, char c) { size_t len = strlen(str); return len > 0 && str[len-1] == c; } bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe) { struct mail_namespace *ns; struct mailbox *box, *box2; const char *mailbox, *orig_mailbox; bool unsubscribed_mailbox2; char sep; /* */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; box = mailbox_alloc(ns->list, mailbox, 0); mailbox_set_reason(box, subscribe ? "SUBSCRIBE" : "UNSUBSCRIBE"); if (subscribe) { if (!subscribe_is_valid_name(cmd, box)) { mailbox_free(&box); return TRUE; } } sep = mail_namespace_get_sep(ns); unsubscribed_mailbox2 = FALSE; if (!subscribe && str_ends_with_char(orig_mailbox, sep) && !str_ends_with_char(mailbox, sep)) { /* try to unsubscribe both "box" and "box/" */ const char *name2 = t_strdup_printf("%s%c", mailbox, sep); box2 = mailbox_alloc(ns->list, name2, 0); mailbox_set_reason(box2, "UNSUBSCRIBE"); if (mailbox_set_subscribed(box2, FALSE) == 0) unsubscribed_mailbox2 = TRUE; mailbox_free(&box2); } if (mailbox_set_subscribed(box, subscribe) < 0 && !unsubscribed_mailbox2) { client_send_box_error(cmd, box); } else { client_send_tagline(cmd, subscribe ? "OK Subscribe completed." : "OK Unsubscribe completed."); } mailbox_free(&box); return TRUE; } bool cmd_subscribe(struct client_command_context *cmd) { return cmd_subscribe_full(cmd, TRUE); } dovecot-2.2.33.2/src/imap/mail-storage-callbacks.c0000644000175000017500000000200613123174404016504 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ostream.h" #include "mail-storage.h" #include "imap-commands-util.h" static void notify_ok(struct mailbox *mailbox ATTR_UNUSED, const char *text, void *context) { struct client *client = context; if (o_stream_get_buffer_used_size(client->output) != 0) return; T_BEGIN { const char *str; str = t_strconcat("* OK ", text, "\r\n", NULL); o_stream_nsend_str(client->output, str); (void)o_stream_flush(client->output); } T_END; } static void notify_no(struct mailbox *mailbox ATTR_UNUSED, const char *text, void *context) { struct client *client = context; if (o_stream_get_buffer_used_size(client->output) != 0) return; T_BEGIN { const char *str; str = t_strconcat("* NO ", text, "\r\n", NULL); o_stream_nsend_str(client->output, str); (void)o_stream_flush(client->output); } T_END; } struct mail_storage_callbacks mail_storage_callbacks = { notify_ok, notify_no }; dovecot-2.2.33.2/src/imap/imap-client.h0000644000175000017500000002710513165463624014434 00000000000000#ifndef IMAP_CLIENT_H #define IMAP_CLIENT_H #include "imap-commands.h" #include "message-size.h" #define CLIENT_COMMAND_QUEUE_MAX_SIZE 4 /* Maximum number of CONTEXT=SEARCH UPDATEs. Clients probably won't need more than a few, so this is mainly to avoid more or less accidental pointless resource usage. */ #define CLIENT_MAX_SEARCH_UPDATES 10 struct client; struct mail_storage; struct mail_storage_service_ctx; struct lda_settings; struct imap_parser; struct imap_arg; struct imap_urlauth_context; struct mailbox_keywords { /* All keyword names. The array itself exists in mail_index. Keywords are currently only appended, they're never removed. */ const ARRAY_TYPE(keywords) *names; /* Number of keywords announced to client via FLAGS/PERMANENTFLAGS. This relies on keywords not being removed while mailbox is selected. */ unsigned int announce_count; }; struct imap_search_update { char *tag; struct mail_search_result *result; bool return_uids; pool_t fetch_pool; struct imap_fetch_context *fetch_ctx; }; enum client_command_state { /* Waiting for more input */ CLIENT_COMMAND_STATE_WAIT_INPUT, /* Waiting to be able to send more output */ CLIENT_COMMAND_STATE_WAIT_OUTPUT, /* Waiting for external interaction */ CLIENT_COMMAND_STATE_WAIT_EXTERNAL, /* Wait for other commands to finish execution */ CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY, /* Waiting for other commands to finish so we can sync */ CLIENT_COMMAND_STATE_WAIT_SYNC, /* Command is finished */ CLIENT_COMMAND_STATE_DONE }; struct client_command_stats { /* time when command handling was started - typically this is after reading all the parameters. */ struct timeval start_time; /* time when command handling was last finished. this is before mailbox syncing is done. */ struct timeval last_run_timeval; /* io_loop_get_wait_usecs()'s value when the command was started */ uint64_t start_ioloop_wait_usecs; /* how many usecs this command itself has spent running */ uint64_t running_usecs; /* how many usecs this command itself has spent waiting for locks */ uint64_t lock_wait_usecs; /* how many bytes of client input/output command has used */ uint64_t bytes_in, bytes_out; }; struct client_command_stats_start { struct timeval timeval; uint64_t lock_wait_usecs; uint64_t bytes_in, bytes_out; }; struct client_command_context { struct client_command_context *prev, *next; struct client *client; pool_t pool; /* IMAP command tag */ const char *tag; /* Name of this command */ const char *name; /* Parameters for this command. These are generated from parsed IMAP arguments, so they may not be exactly the same as how client sent them. */ const char *args; /* Parameters for this command generated with imap_write_args_for_human(), so it's suitable for logging. */ const char *human_args; enum command_flags cmd_flags; const char *tagline_reply; command_func_t *func; void *context; /* Module-specific contexts. */ ARRAY(union imap_module_context *) module_contexts; struct imap_parser *parser; enum client_command_state state; struct client_command_stats stats; struct client_command_stats_start stats_start; struct imap_client_sync_context *sync; unsigned int uid:1; /* used UID command */ unsigned int cancel:1; /* command is wanted to be cancelled */ unsigned int param_error:1; unsigned int search_save_result:1; /* search result is being updated */ unsigned int search_save_result_used:1; /* command uses search save */ unsigned int temp_executed:1; /* temporary execution state tracking */ unsigned int tagline_sent:1; unsigned int executing:1; }; struct imap_client_vfuncs { /* Export client state into buffer. Returns 1 if ok, 0 if some state couldn't be preserved, -1 if temporary internal error occurred. */ int (*state_export)(struct client *client, bool internal, buffer_t *dest, const char **error_r); /* Import a single block of client state from the given data. Returns number of bytes successfully imported from the block, or 0 if state is corrupted or contains unknown data (e.g. some plugin is no longer loaded), -1 if temporary internal error occurred. */ ssize_t (*state_import)(struct client *client, bool internal, const unsigned char *data, size_t size, const char **error_r); void (*destroy)(struct client *client, const char *reason); void (*send_tagline)(struct client_command_context *cmd, const char *data); /* Run "mailbox syncing". This can send any unsolicited untagged replies. Returns 1 = done, 0 = wait for more space in output buffer, -1 = failed. */ int (*sync_notify_more)(struct imap_sync_context *ctx); }; struct client { struct client *prev, *next; struct imap_client_vfuncs v; const char *session_id; const char *const *userdb_fields; /* for internal session saving/restoring */ int fd_in, fd_out; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_idle, *to_idle_output, *to_delayed_input; pool_t pool; struct mail_storage_service_user *service_user; const struct imap_settings *set; const struct lda_settings *lda_set; string_t *capability_string; const char *disconnect_reason; struct mail_user *user; struct mailbox *mailbox; struct mailbox_keywords keywords; unsigned int sync_counter; uint32_t messages_count, recent_count, uidvalidity; enum mailbox_feature enabled_features; time_t last_input, last_output; unsigned int bad_counter; /* one parser is kept here to be used for new commands */ struct imap_parser *free_parser; /* command_pool is cleared when the command queue gets empty */ pool_t command_pool; /* New commands are always prepended to the queue */ struct client_command_context *command_queue; unsigned int command_queue_size; char *last_cmd_name; struct client_command_stats last_cmd_stats; uint64_t sync_last_full_modseq; uint64_t highest_fetch_modseq; ARRAY_TYPE(seq_range) fetch_failed_uids; /* For imap_logout_format statistics: */ unsigned int fetch_hdr_count, fetch_body_count; uint64_t fetch_hdr_bytes, fetch_body_bytes; unsigned int deleted_count, expunged_count, trashed_count; unsigned int autoexpunged_count, append_count; /* SEARCHRES extension: Last saved SEARCH result */ ARRAY_TYPE(seq_range) search_saved_uidset; /* SEARCH=CONTEXT extension: Searches that get updated */ ARRAY(struct imap_search_update) search_updates; /* NOTIFY extension */ struct imap_notify_context *notify_ctx; uint32_t notify_uidnext; /* client input/output is locked by this command */ struct client_command_context *input_lock; struct client_command_context *output_cmd_lock; /* command changing the mailbox */ struct client_command_context *mailbox_change_lock; /* IMAP URLAUTH context (RFC4467) */ struct imap_urlauth_context *urlauth_ctx; /* Module-specific contexts. */ ARRAY(union imap_module_context *) module_contexts; /* syncing marks this TRUE when it sees \Deleted flags. this is by EXPUNGE for Outlook-workaround. */ unsigned int sync_seen_deletes:1; unsigned int logged_out:1; unsigned int disconnected:1; unsigned int hibernated:1; unsigned int destroyed:1; unsigned int handling_input:1; unsigned int syncing:1; unsigned int id_logged:1; unsigned int mailbox_examined:1; unsigned int anvil_sent:1; unsigned int tls_compression:1; unsigned int input_skip_line:1; /* skip all the data until we've found a new line */ unsigned int modseqs_sent_since_sync:1; unsigned int notify_immediate_expunges:1; unsigned int notify_count_changes:1; unsigned int notify_flag_changes:1; unsigned int imap_metadata_enabled:1; unsigned int nonpermanent_modseqs:1; unsigned int state_import_bad_idle_done:1; unsigned int state_import_idle_continue:1; }; struct imap_module_register { unsigned int id; }; union imap_module_context { struct imap_client_vfuncs super; struct imap_module_register *reg; }; extern struct imap_module_register imap_module_register; extern struct client *imap_clients; extern unsigned int imap_client_count; /* Create new client with specified input/output handles. socket specifies if the handle is a socket. */ struct client *client_create(int fd_in, int fd_out, const char *session_id, struct mail_user *user, struct mail_storage_service_user *service_user, const struct imap_settings *set, const struct lda_settings *lda_set); void client_destroy(struct client *client, const char *reason) ATTR_NULL(2); /* Disconnect client connection */ void client_disconnect(struct client *client, const char *reason); void client_disconnect_with_error(struct client *client, const char *msg); /* Add the given capability to the CAPABILITY reply. If imap_capability setting has an explicit capability, nothing is changed. */ void client_add_capability(struct client *client, const char *capability); /* Send a line of data to client. */ void client_send_line(struct client *client, const char *data); /* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full, -1 if error. This should be used when you're (potentially) sending a lot of lines to client. */ int client_send_line_next(struct client *client, const char *data); /* Send line of data to client, prefixed with client->tag. You need to prefix the data with "OK ", "NO " or "BAD ". */ void client_send_tagline(struct client_command_context *cmd, const char *data); /* Send a BAD command reply to client via client_send_tagline(). If there have been too many command errors, the client is disconnected. msg may be NULL, in which case the error is looked up from imap_parser. */ void client_send_command_error(struct client_command_context *cmd, const char *msg); /* Send a NO command reply with the default internal error message to client via client_send_tagline(). */ void client_send_internal_error(struct client_command_context *cmd); /* Read a number of arguments. Returns TRUE if everything was read or FALSE if either needs more data or error occurred. */ bool client_read_args(struct client_command_context *cmd, unsigned int count, unsigned int flags, const struct imap_arg **args_r); /* Reads a number of string arguments. ... is a list of pointers where to store the arguments. */ bool client_read_string_args(struct client_command_context *cmd, unsigned int count, ...); /* SEARCHRES extension: Call if $ is being used/updated, returns TRUE if we have to wait for an existing SEARCH SAVE to finish. */ bool client_handle_search_save_ambiguity(struct client_command_context *cmd); int client_enable(struct client *client, enum mailbox_feature features); /* Send client processing to imap-idle process. If successful, returns TRUE and destroys the client. */ bool imap_client_hibernate(struct client **client); struct imap_search_update * client_search_update_lookup(struct client *client, const char *tag, unsigned int *idx_r); void client_search_updates_free(struct client *client); struct client_command_context *client_command_alloc(struct client *client); void client_command_cancel(struct client_command_context **cmd); void client_command_free(struct client_command_context **cmd); bool client_handle_unfinished_cmd(struct client_command_context *cmd); /* Handle any pending command input. This must be run at the end of all I/O callbacks after they've (potentially) finished some commands. */ void client_continue_pending_input(struct client *client); void client_add_missing_io(struct client *client); const char *client_stats(struct client *client); void client_input(struct client *client); bool client_handle_input(struct client *client); int client_output(struct client *client); void clients_destroy_all(struct mail_storage_service_ctx *storage_service); #endif dovecot-2.2.33.2/src/imap/cmd-rename.c0000644000175000017500000000264613123174404014225 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "mail-namespace.h" #include "imap-commands.h" bool cmd_rename(struct client_command_context *cmd) { struct mail_namespace *old_ns, *new_ns; struct mailbox *old_box, *new_box; const char *oldname, *newname; size_t oldlen; /* */ if (!client_read_string_args(cmd, 2, &oldname, &newname)) return FALSE; old_ns = client_find_namespace(cmd, &oldname); if (old_ns == NULL) return TRUE; new_ns = client_find_namespace(cmd, &newname); if (new_ns == NULL) return TRUE; if (old_ns == new_ns) { /* disallow box -> box/child, because it may break clients and there's really no point in doing it anyway. */ old_ns = mailbox_list_get_namespace(old_ns->list); oldlen = strlen(oldname); if (strncmp(oldname, newname, oldlen) == 0 && newname[oldlen] == mail_namespace_get_sep(old_ns)) { client_send_tagline(cmd, "NO Can't rename mailbox under its own child."); return TRUE; } } old_box = mailbox_alloc(old_ns->list, oldname, 0); new_box = mailbox_alloc(new_ns->list, newname, 0); mailbox_set_reason(old_box, "RENAME from"); mailbox_set_reason(new_box, "RENAME to"); if (mailbox_rename(old_box, new_box) < 0) client_send_box_error(cmd, old_box); else client_send_tagline(cmd, "OK Rename completed."); mailbox_free(&old_box); mailbox_free(&new_box); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-select.c0000644000175000017500000002710113165463624014241 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "seq-range-array.h" #include "time-util.h" #include "imap-commands.h" #include "mail-search-build.h" #include "imap-search-args.h" #include "imap-seqset.h" #include "imap-fetch.h" #include "imap-sync.h" struct imap_select_context { struct client_command_context *cmd; struct mail_namespace *ns; struct mailbox *box; struct imap_fetch_context *fetch_ctx; uint32_t qresync_uid_validity; uint64_t qresync_modseq; ARRAY_TYPE(seq_range) qresync_known_uids; ARRAY_TYPE(uint32_t) qresync_sample_seqset; ARRAY_TYPE(uint32_t) qresync_sample_uidset; unsigned int condstore:1; }; static int select_qresync_get_uids(struct imap_select_context *ctx, const ARRAY_TYPE(seq_range) *seqset, const ARRAY_TYPE(seq_range) *uidset) { const struct seq_range *uid_range; struct seq_range_iter seq_iter; unsigned int i, uid_count, diff, n = 0; uint32_t seq; /* change all n:m ranges to n,m and store the results */ uid_range = array_get(uidset, &uid_count); seq_range_array_iter_init(&seq_iter, seqset); i_array_init(&ctx->qresync_sample_uidset, uid_count); i_array_init(&ctx->qresync_sample_seqset, uid_count); for (i = 0; i < uid_count; i++) { if (!seq_range_array_iter_nth(&seq_iter, n++, &seq)) return -1; array_append(&ctx->qresync_sample_uidset, &uid_range[i].seq1, 1); array_append(&ctx->qresync_sample_seqset, &seq, 1); diff = uid_range[i].seq2 - uid_range[i].seq1; if (diff > 0) { n += diff - 1; if (!seq_range_array_iter_nth(&seq_iter, n++, &seq)) return -1; array_append(&ctx->qresync_sample_uidset, &uid_range[i].seq2, 1); array_append(&ctx->qresync_sample_seqset, &seq, 1); } } if (seq_range_array_iter_nth(&seq_iter, n, &seq)) return -1; return 0; } static bool select_parse_qresync_known_set(struct imap_select_context *ctx, const struct imap_arg *args) { ARRAY_TYPE(seq_range) seqset, uidset; const char *str; t_array_init(&seqset, 32); if (!imap_arg_get_atom(args, &str) || imap_seq_set_nostar_parse(str, &seqset) < 0) { client_send_command_error(ctx->cmd, "Invalid QRESYNC known-sequence-set"); return FALSE; } args++; t_array_init(&uidset, 32); if (!imap_arg_get_atom(args, &str) || imap_seq_set_nostar_parse(str, &uidset) < 0) { client_send_command_error(ctx->cmd, "Invalid QRESYNC known-uid-set"); return FALSE; } args++; if (select_qresync_get_uids(ctx, &seqset, &uidset) < 0) { client_send_command_error(ctx->cmd, "Invalid QRESYNC sets"); return FALSE; } if (!IMAP_ARG_IS_EOL(args)) { client_send_command_error(ctx->cmd, "Too many parameters to QRESYNC known set"); return FALSE; } return TRUE; } static bool select_parse_qresync(struct imap_select_context *ctx, const struct imap_arg *args) { const struct imap_arg *list_args; const char *str; unsigned int count; if ((ctx->cmd->client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) { client_send_command_error(ctx->cmd, "QRESYNC not enabled"); return FALSE; } if (!imap_arg_get_list_full(args, &args, &count)) { client_send_command_error(ctx->cmd, "QRESYNC parameters missing"); return FALSE; } if (!imap_arg_get_atom(&args[0], &str) || str_to_uint32(str, &ctx->qresync_uid_validity) < 0 || !imap_arg_get_atom(&args[1], &str) || str_to_uint64(str, &ctx->qresync_modseq) < 0) { client_send_command_error(ctx->cmd, "Invalid QRESYNC parameters"); return FALSE; } args += 2; i_array_init(&ctx->qresync_known_uids, 64); if (imap_arg_get_atom(args, &str)) { if (imap_seq_set_nostar_parse(str, &ctx->qresync_known_uids) < 0) { client_send_command_error(ctx->cmd, "Invalid QRESYNC known-uids"); return FALSE; } args++; } else { seq_range_array_add_range(&ctx->qresync_known_uids, 1, (uint32_t)-1); } if (imap_arg_get_list(args, &list_args)) { if (!select_parse_qresync_known_set(ctx, list_args)) return FALSE; args++; } if (!IMAP_ARG_IS_EOL(args)) { client_send_command_error(ctx->cmd, "Invalid QRESYNC parameters"); return FALSE; } return TRUE; } static bool select_parse_options(struct imap_select_context *ctx, const struct imap_arg *args) { const char *name; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &name)) { client_send_command_error(ctx->cmd, "SELECT options contain non-atoms."); return FALSE; } name = t_str_ucase(name); args++; if (strcmp(name, "CONDSTORE") == 0) ctx->condstore = TRUE; else if (strcmp(name, "QRESYNC") == 0) { if (!select_parse_qresync(ctx, args)) return FALSE; args++; } else { client_send_command_error(ctx->cmd, "Unknown FETCH modifier"); return FALSE; } } return TRUE; } static void select_context_free(struct imap_select_context *ctx) { if (array_is_created(&ctx->qresync_known_uids)) array_free(&ctx->qresync_known_uids); if (array_is_created(&ctx->qresync_sample_seqset)) array_free(&ctx->qresync_sample_seqset); if (array_is_created(&ctx->qresync_sample_uidset)) array_free(&ctx->qresync_sample_uidset); } static void cmd_select_finish(struct imap_select_context *ctx, int ret) { const char *resp_code; if (ret < 0) { if (ctx->box != NULL) mailbox_free(&ctx->box); ctx->cmd->client->mailbox = NULL; } else { resp_code = mailbox_is_readonly(ctx->box) ? "READ-ONLY" : "READ-WRITE"; client_send_tagline(ctx->cmd, t_strdup_printf( "OK [%s] %s completed", resp_code, ctx->cmd->client->mailbox_examined ? "Examine" : "Select")); } select_context_free(ctx); } static bool cmd_select_continue(struct client_command_context *cmd) { struct imap_select_context *ctx = cmd->context; int ret; if (imap_fetch_more(ctx->fetch_ctx, cmd) == 0) { /* unfinished */ return FALSE; } ret = imap_fetch_end(ctx->fetch_ctx); if (ret < 0) client_send_box_error(ctx->cmd, ctx->box); imap_fetch_free(&ctx->fetch_ctx); cmd_select_finish(ctx, ret); return TRUE; } static int select_qresync(struct imap_select_context *ctx) { struct imap_fetch_context *fetch_ctx; struct mail_search_args *search_args; struct imap_fetch_qresync_args qresync_args; int ret; search_args = mail_search_build_init(); search_args->args = p_new(search_args->pool, struct mail_search_arg, 1); search_args->args->type = SEARCH_UIDSET; search_args->args->value.seqset = ctx->qresync_known_uids; imap_search_add_changed_since(search_args, ctx->qresync_modseq); i_zero(&qresync_args); qresync_args.qresync_sample_seqset = &ctx->qresync_sample_seqset; qresync_args.qresync_sample_uidset = &ctx->qresync_sample_uidset; if (imap_fetch_send_vanished(ctx->cmd->client, ctx->box, search_args, &qresync_args) < 0) { mail_search_args_unref(&search_args); return -1; } fetch_ctx = imap_fetch_alloc(ctx->cmd->client, ctx->cmd->pool, t_strdup_printf("%s %s", ctx->cmd->name, ctx->cmd->args)); imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init); imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init); imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init); imap_fetch_begin(fetch_ctx, ctx->box, search_args); mail_search_args_unref(&search_args); if (imap_fetch_more(fetch_ctx, ctx->cmd) == 0) { /* unfinished */ ctx->fetch_ctx = fetch_ctx; ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; ctx->cmd->func = cmd_select_continue; ctx->cmd->context = ctx; return 0; } ret = imap_fetch_end(fetch_ctx); imap_fetch_free(&fetch_ctx); return ret < 0 ? -1 : 1; } static int select_open(struct imap_select_context *ctx, const char *mailbox, bool readonly) { struct client *client = ctx->cmd->client; struct mailbox_status status; enum mailbox_flags flags = 0; int ret = 0; if (readonly) flags |= MAILBOX_FLAG_READONLY; else flags |= MAILBOX_FLAG_DROP_RECENT; ctx->box = mailbox_alloc(ctx->ns->list, mailbox, flags); mailbox_set_reason(ctx->box, readonly ? "EXAMINE" : "SELECT"); if (mailbox_open(ctx->box) < 0) { client_send_box_error(ctx->cmd, ctx->box); mailbox_free(&ctx->box); return -1; } if (client->enabled_features != 0) ret = mailbox_enable(ctx->box, client->enabled_features); if (ret < 0 || mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { client_send_box_error(ctx->cmd, ctx->box); return -1; } mailbox_get_open_status(ctx->box, STATUS_MESSAGES | STATUS_RECENT | STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_KEYWORDS | STATUS_HIGHESTMODSEQ, &status); client->mailbox = ctx->box; client->mailbox_examined = readonly; client->messages_count = status.messages; client->recent_count = status.recent; client->uidvalidity = status.uidvalidity; client->notify_uidnext = status.uidnext; client_update_mailbox_flags(client, status.keywords); client_send_mailbox_flags(client, TRUE); client_send_line(client, t_strdup_printf("* %u EXISTS", status.messages)); client_send_line(client, t_strdup_printf("* %u RECENT", status.recent)); if (status.first_unseen_seq != 0) { client_send_line(client, t_strdup_printf("* OK [UNSEEN %u] First unseen.", status.first_unseen_seq)); } client_send_line(client, t_strdup_printf("* OK [UIDVALIDITY %u] UIDs valid", status.uidvalidity)); client_send_line(client, t_strdup_printf("* OK [UIDNEXT %u] Predicted next UID", status.uidnext)); client->nonpermanent_modseqs = status.nonpermanent_modseqs; if (status.nonpermanent_modseqs) { client_send_line(client, "* OK [NOMODSEQ] No permanent modsequences"); } else if (!status.no_modseq_tracking) { client_send_line(client, t_strdup_printf("* OK [HIGHESTMODSEQ %llu] Highest", (unsigned long long)status.highest_modseq)); client->sync_last_full_modseq = status.highest_modseq; } if (ctx->qresync_uid_validity == status.uidvalidity && status.uidvalidity != 0 && !client->nonpermanent_modseqs) { if ((ret = select_qresync(ctx)) < 0) { client_send_box_error(ctx->cmd, ctx->box); return -1; } } else { ret = 1; } return ret; } static void close_selected_mailbox(struct client *client) { if (client->mailbox == NULL) return; imap_client_close_mailbox(client); /* CLOSED response is required by QRESYNC */ client_send_line(client, "* OK [CLOSED] Previous mailbox closed."); } bool cmd_select_full(struct client_command_context *cmd, bool readonly) { struct client *client = cmd->client; struct imap_select_context *ctx; const struct imap_arg *args, *list_args; const char *mailbox, *error; int ret; /* [(optional parameters)] */ if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!imap_arg_get_astring(args, &mailbox)) { close_selected_mailbox(client); client_send_command_error(cmd, "Invalid arguments."); return FALSE; } ctx = p_new(cmd->pool, struct imap_select_context, 1); ctx->cmd = cmd; ctx->ns = client_find_namespace_full(cmd->client, &mailbox, &error); if (ctx->ns == NULL) { close_selected_mailbox(client); client_send_tagline(cmd, error); return TRUE; } if (imap_arg_get_list(&args[1], &list_args)) { if (!select_parse_options(ctx, list_args)) { select_context_free(ctx); close_selected_mailbox(client); return TRUE; } } i_assert(client->mailbox_change_lock == NULL); client->mailbox_change_lock = cmd; close_selected_mailbox(client); if (ctx->condstore) { /* Enable while no mailbox is opened to avoid sending HIGHESTMODSEQ for previously opened mailbox */ (void)client_enable(client, MAILBOX_FEATURE_CONDSTORE); } ret = select_open(ctx, mailbox, readonly); if (ret == 0) return FALSE; cmd_select_finish(ctx, ret); return TRUE; } bool cmd_select(struct client_command_context *cmd) { return cmd_select_full(cmd, FALSE); } dovecot-2.2.33.2/src/imap/cmd-create.c0000644000175000017500000000240313123174404014210 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-resp-code.h" #include "mail-namespace.h" #include "imap-commands.h" bool cmd_create(struct client_command_context *cmd) { struct mail_namespace *ns; const char *mailbox, *orig_mailbox; struct mailbox *box; bool directory; size_t len; /* */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; len = strlen(orig_mailbox); if (len == 0 || orig_mailbox[len-1] != mail_namespace_get_sep(ns)) directory = FALSE; else { /* name ends with hierarchy separator - client is just informing us that it wants to create children under this mailbox. */ directory = TRUE; /* drop separator from mailbox. it's already dropped when WORKAROUND_TB_EXTRA_MAILBOX_SEP is enabled */ if (len == strlen(mailbox)) mailbox = t_strndup(mailbox, len-1); } box = mailbox_alloc(ns->list, mailbox, 0); mailbox_set_reason(box, "CREATE"); if (mailbox_create(box, NULL, directory) < 0) client_send_box_error(cmd, box); else client_send_tagline(cmd, "OK Create completed."); mailbox_free(&box); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-store.c0000644000175000017500000001566213165463624014127 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "seq-range-array.h" #include "str.h" #include "imap-commands.h" #include "imap-search-args.h" #include "imap-util.h" struct imap_store_context { struct client_command_context *cmd; uint64_t max_modseq; enum mail_flags flags; struct mail_keywords *keywords; enum modify_type modify_type; bool silent; }; static bool get_modify_type(struct imap_store_context *ctx, const char *type) { if (*type == '+') { ctx->modify_type = MODIFY_ADD; type++; } else if (*type == '-') { ctx->modify_type = MODIFY_REMOVE; type++; } else { ctx->modify_type = MODIFY_REPLACE; } if (strncasecmp(type, "FLAGS", 5) != 0) return FALSE; ctx->silent = strcasecmp(type+5, ".SILENT") == 0; if (!ctx->silent && type[5] != '\0') return FALSE; return TRUE; } static bool store_parse_modifiers(struct imap_store_context *ctx, const struct imap_arg *args) { const char *name, *value; for (; !IMAP_ARG_IS_EOL(args); args += 2) { if (!imap_arg_get_atom(&args[0], &name) || !imap_arg_get_atom(&args[1], &value)) { client_send_command_error(ctx->cmd, "Invalid STORE modifiers."); return FALSE; } if (strcasecmp(name, "UNCHANGEDSINCE") == 0) { if (ctx->cmd->client->nonpermanent_modseqs) { client_send_command_error(ctx->cmd, "STORE UNCHANGEDSINCE can't be used with non-permanent modseqs"); return FALSE; } if (str_to_uint64(value, &ctx->max_modseq) < 0) { client_send_command_error(ctx->cmd, "Invalid modseq"); return FALSE; } (void)client_enable(ctx->cmd->client, MAILBOX_FEATURE_CONDSTORE); } else { client_send_command_error(ctx->cmd, "Unknown STORE modifier"); return FALSE; } } return TRUE; } static bool store_parse_args(struct imap_store_context *ctx, const struct imap_arg *args) { struct client_command_context *cmd = ctx->cmd; const struct imap_arg *list_args; const char *type; const char *const *keywords_list = NULL; ctx->max_modseq = (uint64_t)-1; if (imap_arg_get_list(args, &list_args)) { if (!store_parse_modifiers(ctx, list_args)) return FALSE; args++; } if (!imap_arg_get_astring(args, &type) || !get_modify_type(ctx, type)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } args++; if (imap_arg_get_list(args, &list_args)) { if (!client_parse_mail_flags(cmd, list_args, &ctx->flags, &keywords_list)) return FALSE; } else { if (!client_parse_mail_flags(cmd, args, &ctx->flags, &keywords_list)) return FALSE; } if (keywords_list != NULL || ctx->modify_type == MODIFY_REPLACE) { if (mailbox_keywords_create(cmd->client->mailbox, keywords_list, &ctx->keywords) < 0) { /* invalid keywords */ client_send_box_error(cmd, cmd->client->mailbox); return FALSE; } } return TRUE; } bool cmd_store(struct client_command_context *cmd) { struct client *client = cmd->client; const struct imap_arg *args; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mailbox_transaction_context *t; struct mail *mail; struct imap_store_context ctx; ARRAY_TYPE(seq_range) modified_set, uids; enum mailbox_transaction_flags flags = 0; enum imap_sync_flags imap_sync_flags = 0; const char *set, *reply, *tagged_reply; string_t *str; int ret; bool update_deletes; unsigned int deleted_count; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; if (!imap_arg_get_atom(args, &set)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } ret = imap_search_get_seqset(cmd, set, cmd->uid, &search_args); if (ret <= 0) return ret < 0; i_zero(&ctx); ctx.cmd = cmd; if (!store_parse_args(&ctx, ++args)) { mail_search_args_unref(&search_args); return TRUE; } if (client->mailbox_examined) { mail_search_args_unref(&search_args); if (ctx.max_modseq < (uint64_t)-1) reply = "NO CONDSTORE failed: Mailbox is read-only."; else reply = "OK Store ignored with read-only mailbox."; return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), 0, reply); } if (ctx.silent) flags |= MAILBOX_TRANSACTION_FLAG_HIDE; if (ctx.max_modseq < (uint64_t)-1) { /* update modseqs so we can check them early */ flags |= MAILBOX_TRANSACTION_FLAG_REFRESH; } t = mailbox_transaction_begin(client->mailbox, flags); imap_transaction_set_cmd_reason(t, cmd); search_ctx = mailbox_search_init(t, search_args, NULL, MAIL_FETCH_FLAGS, NULL); mail_search_args_unref(&search_args); i_array_init(&modified_set, 64); if (ctx.max_modseq < (uint64_t)-1) { /* STORE UNCHANGEDSINCE is being used */ mailbox_transaction_set_max_modseq(t, ctx.max_modseq, &modified_set); } update_deletes = (ctx.flags & MAIL_DELETED) != 0 && ctx.modify_type != MODIFY_REMOVE; deleted_count = 0; while (mailbox_search_next(search_ctx, &mail)) { if (ctx.max_modseq < (uint64_t)-1) { /* check early so there's less work for transaction commit if something has to be cancelled */ if (mail_get_modseq(mail) > ctx.max_modseq) { seq_range_array_add(&modified_set, mail->seq); continue; } } if (update_deletes) { if ((mail_get_flags(mail) & MAIL_DELETED) == 0) deleted_count++; } if (ctx.modify_type == MODIFY_REPLACE || ctx.flags != 0) mail_update_flags(mail, ctx.modify_type, ctx.flags); if (ctx.modify_type == MODIFY_REPLACE || ctx.keywords != NULL) { mail_update_keywords(mail, ctx.modify_type, ctx.keywords); } } if (ctx.keywords != NULL) mailbox_keywords_unref(&ctx.keywords); ret = mailbox_search_deinit(&search_ctx); if (ret < 0) mailbox_transaction_rollback(&t); else ret = mailbox_transaction_commit(&t); if (ret < 0) { array_free(&modified_set); client_send_box_error(cmd, client->mailbox); return TRUE; } client->deleted_count += deleted_count; if (array_count(&modified_set) == 0) tagged_reply = "OK Store completed."; else { if (cmd->uid) { i_array_init(&uids, array_count(&modified_set)*2); mailbox_get_uid_range(client->mailbox, &modified_set, &uids); array_free(&modified_set); modified_set = uids; } str = str_new(cmd->pool, 256); str_append(str, "OK [MODIFIED "); imap_write_seq_range(str, &modified_set); str_append(str, "] Conditional store failed."); tagged_reply = str_c(str); } array_free(&modified_set); /* With UID STORE we have to return UID for the flags as well. Unfortunately we don't have the ability to separate those flag changes that were caused by UID STORE and those that came externally, so we'll just send the UID for all flag changes that we see. */ if (cmd->uid && (!ctx.silent || (client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)) imap_sync_flags |= IMAP_SYNC_FLAG_SEND_UID; return cmd_sync(cmd, (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), imap_sync_flags, tagged_reply); } dovecot-2.2.33.2/src/imap/main.c0000644000175000017500000003376113165463624013156 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "abspath.h" #include "str.h" #include "base64.h" #include "process-title.h" #include "randgen.h" #include "restrict-access.h" #include "fd-close-on-exec.h" #include "write-full.h" #include "settings-parser.h" #include "master-interface.h" #include "master-service.h" #include "master-login.h" #include "mail-user.h" #include "mail-storage-service.h" #include "lda-settings.h" #include "imap-master-client.h" #include "imap-resp-code.h" #include "imap-commands.h" #include "imap-fetch.h" #include #include #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) #define IMAP_DIE_IDLE_SECS 10 static bool verbose_proctitle = FALSE; static struct mail_storage_service_ctx *storage_service; static struct master_login *master_login = NULL; imap_client_created_func_t *hook_client_created = NULL; bool imap_debug = FALSE; imap_client_created_func_t * imap_client_created_hook_set(imap_client_created_func_t *new_hook) { imap_client_created_func_t *old_hook = hook_client_created; hook_client_created = new_hook; return old_hook; } void imap_refresh_proctitle(void) { #define IMAP_PROCTITLE_PREFERRED_LEN 80 struct client *client; struct client_command_context *cmd; string_t *title = t_str_new(128); bool wait_output; if (!verbose_proctitle) return; str_append_c(title, '['); switch (imap_client_count) { case 0: str_append(title, "idling"); break; case 1: client = imap_clients; str_append(title, client->user->username); if (client->user->remote_ip != NULL) { str_append_c(title, ' '); str_append(title, net_ip2addr(client->user->remote_ip)); } wait_output = FALSE; for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { if (cmd->name == NULL) continue; if (str_len(title) < IMAP_PROCTITLE_PREFERRED_LEN) { str_append_c(title, ' '); str_append(title, cmd->name); } if (cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT) wait_output = TRUE; } if (wait_output) { str_printfa(title, " - %"PRIuSIZE_T" bytes waiting", o_stream_get_buffer_used_size(client->output)); if (o_stream_is_corked(client->output)) str_append(title, " corked"); } if (client->destroyed) str_append(title, " (deinit)"); break; default: str_printfa(title, "%u connections", imap_client_count); break; } str_append_c(title, ']'); process_title_set(str_c(title)); } static void client_kill_idle(struct client *client) { if (client->output_cmd_lock != NULL) return; client_send_line(client, "* BYE Server shutting down."); mail_storage_service_io_activate_user(client->service_user); client_destroy(client, "Server shutting down."); mail_storage_service_io_deactivate(storage_service); } static void imap_die(void) { struct client *client, *next; time_t last_io, now = time(NULL); time_t stop_timestamp = now - IMAP_DIE_IDLE_SECS; unsigned int stop_msecs; for (client = imap_clients; client != NULL; client = next) { next = client->next; last_io = I_MAX(client->last_input, client->last_output); if (last_io <= stop_timestamp) client_kill_idle(client); else { timeout_remove(&client->to_idle); stop_msecs = (last_io - stop_timestamp) * 1000; client->to_idle = timeout_add(stop_msecs, client_kill_idle, client); } } } struct client_input { const char *tag; const unsigned char *input; size_t input_size; bool send_untagged_capability; }; static void client_parse_input(const unsigned char *data, size_t len, struct client_input *input_r) { size_t taglen; i_assert(len > 0); i_zero(input_r); if (data[0] == '1') input_r->send_untagged_capability = TRUE; data++; len--; input_r->tag = t_strndup(data, len); taglen = strlen(input_r->tag) + 1; if (len > taglen) { input_r->input = data + taglen; input_r->input_size = len - taglen; } } static void client_add_input_capability(struct client *client, const unsigned char *client_input, size_t client_input_size) { struct ostream *output; struct client_input input; if (client_input_size > 0) { client_parse_input(client_input, client_input_size, &input); if (input.input_size > 0 && !i_stream_add_data(client->input, input.input, input.input_size)) i_panic("Couldn't add client input to stream"); } else { /* IMAPLOGINTAG environment is compatible with mailfront */ i_zero(&input); input.tag = getenv("IMAPLOGINTAG"); } /* cork/uncork around the OK reply to minimize latency */ output = client->output; o_stream_ref(output); o_stream_cork(output); if (input.tag == NULL) { client_send_line(client, t_strconcat( "* PREAUTH [CAPABILITY ", str_c(client->capability_string), "] " "Logged in as ", client->user->username, NULL)); } else if (input.send_untagged_capability) { /* client doesn't seem to understand tagged capabilities. send untagged instead and hope that it works. */ client_send_line(client, t_strconcat("* CAPABILITY ", str_c(client->capability_string), NULL)); client_send_line(client, t_strconcat(input.tag, " OK Logged in", NULL)); } else { client_send_line(client, t_strconcat( input.tag, " OK [CAPABILITY ", str_c(client->capability_string), "] Logged in", NULL)); } o_stream_uncork(output); o_stream_unref(&output); } static void client_add_input_finalize(struct client *client) { struct ostream *output; /* try to condense any responses into as few packets as possible */ output = client->output; o_stream_ref(output); o_stream_cork(output); (void)client_handle_input(client); o_stream_uncork(output); o_stream_unref(&output); /* we could have already handled LOGOUT, or we might need to continue pending ambigious commands. */ if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); } static void client_add_input(struct client *client, const unsigned char *client_input, size_t client_input_size) { client_add_input_capability(client, client_input, client_input_size); client_add_input_finalize(client); } int client_create_from_input(const struct mail_storage_service_input *input, int fd_in, int fd_out, struct client **client_r, const char **error_r) { struct mail_storage_service_user *user; struct mail_user *mail_user; struct client *client; struct imap_settings *imap_set; struct lda_settings *lda_set; if (mail_storage_service_lookup_next(storage_service, input, &user, &mail_user, error_r) <= 0) return -1; restrict_access_allow_coredumps(TRUE); imap_set = mail_storage_service_user_get_set(user)[1]; if (imap_set->verbose_proctitle) verbose_proctitle = TRUE; lda_set = mail_storage_service_user_get_set(user)[2]; settings_var_expand(&imap_setting_parser_info, imap_set, mail_user->pool, mail_user_var_expand_table(mail_user)); settings_var_expand(&lda_setting_parser_info, lda_set, mail_user->pool, mail_user_var_expand_table(mail_user)); client = client_create(fd_in, fd_out, input->session_id, mail_user, user, imap_set, lda_set); client->userdb_fields = input->userdb_fields == NULL ? NULL : p_strarray_dup(client->pool, input->userdb_fields); *client_r = client; return 0; } static void main_stdio_run(const char *username) { struct client *client; struct mail_storage_service_input input; const char *value, *error, *input_base64; i_zero(&input); input.module = input.service = "imap"; input.username = username != NULL ? username : getenv("USER"); if (input.username == NULL && IS_STANDALONE()) input.username = getlogin(); if (input.username == NULL) i_fatal("USER environment missing"); if ((value = getenv("IP")) != NULL) (void)net_addr2ip(value, &input.remote_ip); if ((value = getenv("LOCAL_IP")) != NULL) (void)net_addr2ip(value, &input.local_ip); if (client_create_from_input(&input, STDIN_FILENO, STDOUT_FILENO, &client, &error) < 0) i_fatal("%s", error); /* TODO: the following could make use of client_add_input_{capability,finalize} */ input_base64 = getenv("CLIENT_INPUT"); if (input_base64 == NULL) client_add_input(client, NULL, 0); else { const buffer_t *input_buf = t_base64_decode_str(input_base64); client_add_input(client, input_buf->data, input_buf->used); } /* client may be destroyed now */ } static void login_client_connected(const struct master_login_client *login_client, const char *username, const char *const *extra_fields) { #define MSG_BYE_INTERNAL_ERROR "* BYE "MAIL_ERRSTR_CRITICAL_MSG"\r\n" struct mail_storage_service_input input; struct client *client; enum mail_auth_request_flags flags; const char *error; i_zero(&input); input.module = input.service = "imap"; input.local_ip = login_client->auth_req.local_ip; input.remote_ip = login_client->auth_req.remote_ip; input.username = username; input.userdb_fields = extra_fields; input.session_id = login_client->session_id; if (client_create_from_input(&input, login_client->fd, login_client->fd, &client, &error) < 0) { int fd = login_client->fd; if (write(fd, MSG_BYE_INTERNAL_ERROR, strlen(MSG_BYE_INTERNAL_ERROR)) < 0) { if (errno != EAGAIN && errno != EPIPE) i_error("write(client) failed: %m"); } i_error("%s", error); i_close_fd(&fd); master_service_client_connection_destroyed(master_service); return; } flags = login_client->auth_req.flags; if ((flags & MAIL_AUTH_REQUEST_FLAG_TLS_COMPRESSION) != 0) client->tls_compression = TRUE; client_add_input_capability(client, login_client->data, login_client->auth_req.data_size); /* finish initializing the user (see comment in main()) */ if (mail_namespaces_init(client->user, &error) < 0) { if (write_full(login_client->fd, MSG_BYE_INTERNAL_ERROR, strlen(MSG_BYE_INTERNAL_ERROR)) < 0) if (errno != EAGAIN && errno != EPIPE) i_error("write_full(client) failed: %m"); i_error("%s", error); client_destroy(client, error); return; } client_add_input_finalize(client); /* client may be destroyed now */ } static void login_client_failed(const struct master_login_client *client, const char *errormsg) { struct client_input input; const char *msg; client_parse_input(client->data, client->auth_req.data_size, &input); msg = t_strdup_printf("%s NO ["IMAP_RESP_CODE_UNAVAILABLE"] %s\r\n", input.tag, errormsg); if (write(client->fd, msg, strlen(msg)) < 0) { /* ignored */ } } static void client_connected(struct master_service_connection *conn) { /* when running standalone, we shouldn't even get here */ i_assert(master_login != NULL); master_service_client_connection_accept(conn); if (strcmp(conn->name, "imap-master") == 0) { /* restoring existing IMAP connection (e.g. from imap-idle) */ imap_master_client_create(conn->fd); } else { master_login_add(master_login, conn->fd); } } int main(int argc, char *argv[]) { static const struct setting_parser_info *set_roots[] = { &imap_setting_parser_info, &lda_setting_parser_info, NULL }; struct master_login_settings login_set; enum master_service_flags service_flags = 0; enum mail_storage_service_flags storage_service_flags = 0; const char *username = NULL, *auth_socket_path = "auth-master"; int c; i_zero(&login_set); login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT; login_set.request_auth_token = TRUE; if (IS_STANDALONE() && getuid() == 0 && net_getpeername(1, NULL, NULL) == 0) { printf("* BAD [ALERT] imap binary must not be started from " "inetd, use imap-login instead.\n"); return 1; } if (IS_STANDALONE()) { service_flags |= MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_STD_CLIENT; } else { service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT | MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; /* * We include MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES so * that the mail_user initialization is fast and we can * quickly send back the OK response to LOGIN/AUTHENTICATE. * Otherwise we risk a very slow namespace initialization to * cause client timeouts on login. */ } master_service = master_service_init("imap", service_flags, &argc, &argv, "a:Dt:u:"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; case 't': if (str_to_uint(optarg, &login_set.postlogin_timeout_secs) < 0 || login_set.postlogin_timeout_secs == 0) i_fatal("Invalid -t parameter: %s", optarg); break; case 'u': storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; username = optarg; break; case 'D': imap_debug = TRUE; break; default: return FATAL_DEFAULT; } } master_service_set_die_callback(master_service, imap_die); /* plugins may want to add commands, so this needs to be called early */ commands_init(); imap_fetch_handlers_init(); imap_master_clients_init(); random_init(); storage_service = mail_storage_service_init(master_service, set_roots, storage_service_flags); master_service_init_finish(master_service); /* fake that we're running, so we know if client was destroyed while handling its initial input */ io_loop_set_running(current_ioloop); if (IS_STANDALONE()) { T_BEGIN { main_stdio_run(username); } T_END; } else T_BEGIN { login_set.auth_socket_path = t_abspath(auth_socket_path); if (argv[optind] != NULL) { login_set.postlogin_socket_path = t_abspath(argv[optind]); } login_set.callback = login_client_connected; login_set.failure_callback = login_client_failed; master_login = master_login_init(master_service, &login_set); io_loop_set_running(current_ioloop); } T_END; if (io_loop_is_running(current_ioloop)) master_service_run(master_service, client_connected); clients_destroy_all(storage_service); if (master_login != NULL) master_login_deinit(&master_login); mail_storage_service_deinit(&storage_service); imap_fetch_handlers_deinit(); commands_deinit(); imap_master_clients_deinit(); random_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/imap/cmd-getmetadata.c0000644000175000017500000003414513165463624015250 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "mailbox-list-iter.h" #include "imap-utf7.h" #include "imap-quote.h" #include "imap-metadata.h" struct imap_getmetadata_context { struct client_command_context *cmd; struct mailbox *box; struct imap_metadata_transaction *trans; struct mailbox_list_iterate_context *list_iter; ARRAY_TYPE(const_string) entries; uint32_t maxsize; uoff_t largest_seen_size; unsigned int depth; struct istream *cur_stream; uoff_t cur_stream_offset, cur_stream_size; struct imap_metadata_iter *iter; string_t *iter_entry_prefix; string_t *delayed_errors; unsigned int entry_idx; bool first_entry_sent; bool failed; }; static bool cmd_getmetadata_mailbox_iter_next(struct imap_getmetadata_context *ctx); static bool cmd_getmetadata_parse_options(struct imap_getmetadata_context *ctx, const struct imap_arg *options) { const char *value; while (!IMAP_ARG_IS_EOL(options)) { if (imap_arg_atom_equals(options, "MAXSIZE")) { options++; if (!imap_arg_get_atom(options, &value) || str_to_uint32(value, &ctx->maxsize) < 0) { client_send_command_error(ctx->cmd, "Invalid value for MAXSIZE option"); return FALSE; } } else if (imap_arg_atom_equals(options, "DEPTH")) { options++; if (!imap_arg_get_atom(options, &value)) { client_send_command_error(ctx->cmd, "Invalid value for DEPTH option"); return FALSE; } if (strcmp(value, "0") == 0) ctx->depth = 0; else if (strcmp(value, "1") == 0) ctx->depth = 1; else if (strcmp(value, "infinity") == 0) ctx->depth = UINT_MAX; else { client_send_command_error(ctx->cmd, "Invalid value for DEPTH option"); return FALSE; } } else { client_send_command_error(ctx->cmd, "Unknown option"); return FALSE; } options++; } return TRUE; } static bool imap_metadata_parse_entry_names(struct imap_getmetadata_context *ctx, const struct imap_arg *entries) { const char *value, *error; p_array_init(&ctx->entries, ctx->cmd->pool, 4); for (; !IMAP_ARG_IS_EOL(entries); entries++) { if (!imap_arg_get_astring(entries, &value)) { client_send_command_error(ctx->cmd, "Entry isn't astring"); return FALSE; } if (!imap_metadata_verify_entry_name(value, &error)) { client_send_command_error(ctx->cmd, error); return FALSE; } /* names are case-insensitive so we'll always lowercase them */ value = p_strdup(ctx->cmd->pool, t_str_lcase(value)); array_append(&ctx->entries, &value, 1); } return TRUE; } static string_t * metadata_add_entry(struct imap_getmetadata_context *ctx, const char *entry) { string_t *str; str = t_str_new(64); if (!ctx->first_entry_sent) { string_t *mailbox_mutf7 = t_str_new(64); ctx->first_entry_sent = TRUE; str_append(str, "* METADATA "); if (ctx->box == NULL) { /* server metadata reply */ str_append(str, "\"\""); } else { if (imap_utf8_to_utf7(mailbox_get_vname(ctx->box), mailbox_mutf7) < 0) i_unreached(); imap_append_astring(str, str_c(mailbox_mutf7)); } str_append(str, " ("); /* nothing can be sent until untagged METADATA is finished */ ctx->cmd->client->output_cmd_lock = ctx->cmd; } else { str_append_c(str, ' '); } imap_append_astring(str, entry); return str; } static void cmd_getmetadata_send_nil_reply(struct imap_getmetadata_context *ctx, const char *entry) { string_t *str; /* client requested a specific entry that didn't exist. we must return it as NIL. */ str = metadata_add_entry(ctx, entry); str_append(str, " NIL"); o_stream_nsend(ctx->cmd->client->output, str_data(str), str_len(str)); } static void cmd_getmetadata_send_entry(struct imap_getmetadata_context *ctx, const char *entry, bool require_reply) { struct client *client = ctx->cmd->client; struct mail_attribute_value value; const char *error_string; enum mail_error error; uoff_t value_len; string_t *str; if (imap_metadata_get_stream(ctx->trans, entry, &value) < 0) { error_string = imap_metadata_transaction_get_last_error( ctx->trans, &error); if (error != MAIL_ERROR_NOTFOUND && error != MAIL_ERROR_PERM) { str_printfa(ctx->delayed_errors, "* NO %s\r\n", error_string); ctx->failed = TRUE; return; } } if (value.value != NULL) value_len = strlen(value.value); else if (value.value_stream != NULL) { if (i_stream_get_size(value.value_stream, TRUE, &value_len) < 0) { i_error("GETMETADATA %s: i_stream_get_size(%s) failed: %s", entry, i_stream_get_name(value.value_stream), i_stream_get_error(value.value_stream)); i_stream_unref(&value.value_stream); ctx->failed = TRUE; return; } } else { /* skip nonexistent entries */ if (require_reply) cmd_getmetadata_send_nil_reply(ctx, entry); return; } if (value_len > ctx->maxsize) { /* value length is larger than specified MAXSIZE, skip this entry */ if (ctx->largest_seen_size < value_len) ctx->largest_seen_size = value_len; if (value.value_stream != NULL) i_stream_unref(&value.value_stream); return; } str = metadata_add_entry(ctx, entry); if (value.value != NULL) { str_printfa(str, " {%"PRIuUOFF_T"}\r\n%s", value_len, value.value); o_stream_nsend(client->output, str_data(str), str_len(str)); } else { str_printfa(str, " ~{%"PRIuUOFF_T"}\r\n", value_len); o_stream_nsend(client->output, str_data(str), str_len(str)); ctx->cur_stream_offset = 0; ctx->cur_stream_size = value_len; ctx->cur_stream = value.value_stream; } } static bool cmd_getmetadata_stream_continue(struct imap_getmetadata_context *ctx) { off_t ret; o_stream_set_max_buffer_size(ctx->cmd->client->output, 0); ret = o_stream_send_istream(ctx->cmd->client->output, ctx->cur_stream); o_stream_set_max_buffer_size(ctx->cmd->client->output, (size_t)-1); if (ret > 0) ctx->cur_stream_offset += ret; if (ctx->cur_stream_offset == ctx->cur_stream_size) { /* finished */ return TRUE; } if (ctx->cur_stream->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(ctx->cur_stream), i_stream_get_error(ctx->cur_stream)); client_disconnect(ctx->cmd->client, "Internal GETMETADATA failure"); return TRUE; } if (!i_stream_have_bytes_left(ctx->cur_stream)) { /* Input stream gave less data than expected */ i_error("read(%s): GETMETADATA stream had less data than expected", i_stream_get_name(ctx->cur_stream)); client_disconnect(ctx->cmd->client, "Internal GETMETADATA failure"); return TRUE; } o_stream_set_flush_pending(ctx->cmd->client->output, TRUE); return FALSE; } static int cmd_getmetadata_send_entry_tree(struct imap_getmetadata_context *ctx, const char *entry) { struct client *client = ctx->cmd->client; if (o_stream_get_buffer_used_size(client->output) >= CLIENT_OUTPUT_OPTIMAL_SIZE) { if (o_stream_flush(client->output) <= 0) { o_stream_set_flush_pending(client->output, TRUE); return 0; } } if (ctx->iter != NULL) { const char *subentry; /* DEPTH iteration */ do { subentry = imap_metadata_iter_next(ctx->iter); if (subentry == NULL) { /* iteration finished, get to the next entry */ if (imap_metadata_iter_deinit(&ctx->iter) < 0) { enum mail_error error; str_printfa(ctx->delayed_errors, "* NO %s\r\n", imap_metadata_transaction_get_last_error(ctx->trans, &error)); ctx->failed = TRUE; } return -1; } } while (ctx->depth == 1 && strchr(subentry, '/') != NULL); entry = t_strconcat(str_c(ctx->iter_entry_prefix), subentry, NULL); } cmd_getmetadata_send_entry(ctx, entry, ctx->iter == NULL); if (ctx->cur_stream != NULL) { if (!cmd_getmetadata_stream_continue(ctx)) return 0; i_stream_unref(&ctx->cur_stream); } if (ctx->iter != NULL) { /* already iterating the entry */ return 1; } else if (ctx->depth == 0) { /* no iteration for the entry */ return -1; } else { /* we just sent the entry root. iterate its children. */ str_truncate(ctx->iter_entry_prefix, 0); str_append(ctx->iter_entry_prefix, entry); str_append_c(ctx->iter_entry_prefix, '/'); ctx->iter = imap_metadata_iter_init(ctx->trans, entry); return 1; } } static void cmd_getmetadata_iter_deinit(struct imap_getmetadata_context *ctx) { if (ctx->iter != NULL) (void)imap_metadata_iter_deinit(&ctx->iter); if (ctx->trans != NULL) (void)imap_metadata_transaction_commit(&ctx->trans, NULL, NULL); if (ctx->box != NULL) mailbox_free(&ctx->box); ctx->first_entry_sent = FALSE; ctx->entry_idx = 0; } static void cmd_getmetadata_deinit(struct imap_getmetadata_context *ctx) { struct client_command_context *cmd = ctx->cmd; cmd_getmetadata_iter_deinit(ctx); cmd->client->output_cmd_lock = NULL; if (ctx->list_iter != NULL && mailbox_list_iter_deinit(&ctx->list_iter) < 0) client_send_list_error(cmd, cmd->client->user->namespaces->list); else if (ctx->failed) { client_send_tagline(cmd, "NO Getmetadata failed to send some entries"); } else if (ctx->largest_seen_size != 0) { client_send_tagline(cmd, t_strdup_printf( "OK [METADATA LONGENTRIES %"PRIuUOFF_T"] " "Getmetadata completed.", ctx->largest_seen_size)); } else { client_send_tagline(cmd, "OK Getmetadata completed."); } } static bool cmd_getmetadata_continue(struct client_command_context *cmd) { struct imap_getmetadata_context *ctx = cmd->context; const char *const *entries; unsigned int count; int ret; if (cmd->cancel) { cmd_getmetadata_deinit(ctx); return TRUE; } if (ctx->cur_stream != NULL) { if (!cmd_getmetadata_stream_continue(ctx)) return FALSE; i_stream_unref(&ctx->cur_stream); } entries = array_get(&ctx->entries, &count); for (; ctx->entry_idx < count; ctx->entry_idx++) { do { T_BEGIN { ret = cmd_getmetadata_send_entry_tree(ctx, entries[ctx->entry_idx]); } T_END; if (ret == 0) return FALSE; } while (ret > 0); } if (ctx->first_entry_sent) o_stream_nsend_str(cmd->client->output, ")\r\n"); if (str_len(ctx->delayed_errors) > 0) { o_stream_nsend(cmd->client->output, str_data(ctx->delayed_errors), str_len(ctx->delayed_errors)); str_truncate(ctx->delayed_errors, 0); } cmd_getmetadata_iter_deinit(ctx); if (ctx->list_iter != NULL) return cmd_getmetadata_mailbox_iter_next(ctx); cmd_getmetadata_deinit(ctx); return TRUE; } static bool cmd_getmetadata_start(struct imap_getmetadata_context *ctx) { struct client_command_context *cmd = ctx->cmd; if (ctx->depth > 0) ctx->iter_entry_prefix = str_new(cmd->pool, 128); if (!cmd_getmetadata_continue(cmd)) { cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; cmd->func = cmd_getmetadata_continue; return FALSE; } return TRUE; } static bool cmd_getmetadata_server(struct imap_getmetadata_context *ctx) { ctx->trans = imap_metadata_transaction_begin_server(ctx->cmd->client->user); return cmd_getmetadata_start(ctx); } static int cmd_getmetadata_try_mailbox(struct imap_getmetadata_context *ctx, struct mail_namespace *ns, const char *mailbox) { ctx->box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY); mailbox_set_reason(ctx->box, "GETMETADATA"); if (mailbox_open(ctx->box) < 0) return -1; ctx->trans = imap_metadata_transaction_begin(ctx->box); return cmd_getmetadata_start(ctx) ? 1 : 0; } static bool cmd_getmetadata_mailbox(struct imap_getmetadata_context *ctx, struct mail_namespace *ns, const char *mailbox) { int ret; ret = cmd_getmetadata_try_mailbox(ctx, ns, mailbox); if (ret < 0) { client_send_box_error(ctx->cmd, ctx->box); mailbox_free(&ctx->box); } return ret != 0; } static bool cmd_getmetadata_mailbox_iter_next(struct imap_getmetadata_context *ctx) { const struct mailbox_info *info; int ret; while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) { if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) continue; ret = cmd_getmetadata_try_mailbox(ctx, info->ns, info->vname); if (ret > 0) { /* we'll already recursively went through all the mailboxes (FIXME: ugly and potentially stack consuming) */ return TRUE; } else if (ret == 0) { /* need to send more data later */ return FALSE; } T_BEGIN { client_send_line(ctx->cmd->client, t_strdup_printf( "* NO Failed to open mailbox %s: %s", info->vname, mailbox_get_last_error(ctx->box, NULL))); } T_END; mailbox_free(&ctx->box); } cmd_getmetadata_deinit(ctx); return TRUE; } bool cmd_getmetadata(struct client_command_context *cmd) { struct imap_getmetadata_context *ctx; struct mail_namespace *ns; const struct imap_arg *args, *options, *entries; const char *mailbox, *entry_name; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!cmd->client->imap_metadata_enabled) { client_send_command_error(cmd, "METADATA disabled."); return TRUE; } ctx = p_new(cmd->pool, struct imap_getmetadata_context, 1); ctx->cmd = cmd; ctx->maxsize = (uint32_t)-1; ctx->cmd->context = ctx; ctx->delayed_errors = str_new(cmd->pool, 128); if (imap_arg_get_list(&args[0], &options)) { if (!cmd_getmetadata_parse_options(ctx, options)) return TRUE; args++; } if (!imap_arg_get_astring(&args[0], &mailbox)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (!imap_arg_get_list(&args[1], &entries)) { if (!imap_arg_get_astring(&args[1], &entry_name) || !IMAP_ARG_IS_EOL(&args[2])) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } entries = args+1; } if (!imap_metadata_parse_entry_names(ctx, entries)) return TRUE; if (mailbox[0] == '\0') { /* server attribute */ return cmd_getmetadata_server(ctx); } else if (strchr(mailbox, '*') == NULL && strchr(mailbox, '%') == NULL) { /* mailbox attribute */ ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; return cmd_getmetadata_mailbox(ctx, ns, mailbox); } else { /* wildcards in mailbox name. this isn't supported by RFC 5464, but it was in the earlier drafts and is already used by some software (Horde). */ const char *patterns[2]; patterns[0] = mailbox; patterns[1] = NULL; ctx->list_iter = mailbox_list_iter_init_namespaces( cmd->client->user->namespaces, patterns, MAIL_NAMESPACE_TYPE_MASK_ALL, 0); return cmd_getmetadata_mailbox_iter_next(ctx); } } dovecot-2.2.33.2/src/imap/imap-expunge.c0000644000175000017500000000256613165463624014630 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "mail-storage.h" #include "mail-search-build.h" #include "imap-expunge.h" int imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg, unsigned int *expunged_count) { struct mail_search_context *ctx; struct mailbox_transaction_context *t; struct mail *mail; struct mail_search_args *search_args; bool expunges = FALSE; if (mailbox_is_readonly(box)) { /* silently ignore */ return 0; } search_args = mail_search_build_init(); search_args->args = p_new(search_args->pool, struct mail_search_arg, 1); search_args->args->type = SEARCH_FLAGS; search_args->args->value.flags = MAIL_DELETED; search_args->args->next = next_search_arg; /* Refresh the flags so we'll expunge all messages marked as \Deleted by any session. */ t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_REFRESH); mailbox_transaction_set_reason(t, "EXPUNGE"); ctx = mailbox_search_init(t, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { *expunged_count += 1; mail_expunge(mail); expunges = TRUE; } if (mailbox_search_deinit(&ctx) < 0) { mailbox_transaction_rollback(&t); return -1; } else { if (mailbox_transaction_commit(&t) < 0) return -1; } return expunges ? 1 : 0; } dovecot-2.2.33.2/src/imap/imap-search-args.c0000644000175000017500000001353513123174404015337 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "mail-storage.h" #include "mail-search-parser.h" #include "mail-search-build.h" #include "imap-search-args.h" #include "imap-parser.h" #include "imap-seqset.h" struct search_build_data { pool_t pool; struct mailbox *box; const char *error; }; static bool search_args_have_searchres(struct mail_search_arg *sargs) { for (; sargs != NULL; sargs = sargs->next) { switch (sargs->type) { case SEARCH_UIDSET: if (strcmp(sargs->value.str, "$") == 0) return TRUE; break; case SEARCH_SUB: case SEARCH_OR: if (search_args_have_searchres(sargs->value.subargs)) return TRUE; break; default: break; } } return FALSE; } int imap_search_args_build(struct client_command_context *cmd, const struct imap_arg *args, const char *charset, struct mail_search_args **search_args_r) { struct mail_search_parser *parser; struct mail_search_args *sargs; const char *error; int ret; if (IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Missing search parameters"); return -1; } parser = mail_search_parser_init_imap(args); ret = mail_search_build(mail_search_register_get_imap(), parser, &charset, &sargs, &error); mail_search_parser_deinit(&parser); if (ret < 0) { if (charset == NULL) { client_send_tagline(cmd, t_strconcat( "BAD [BADCHARSET] ", error, NULL)); } else { client_send_command_error(cmd, error); } return -1; } if (search_args_have_searchres(sargs->args)) { if (client_handle_search_save_ambiguity(cmd)) return 0; } mail_search_args_init(sargs, cmd->client->mailbox, TRUE, &cmd->client->search_saved_uidset); *search_args_r = sargs; return 1; } static bool msgset_is_valid(ARRAY_TYPE(seq_range) *seqset, uint32_t messages_count) { const struct seq_range *range; unsigned int count; /* when there are no messages, all messagesets are invalid. if there's at least one message: - * gives seq1 = seq2 = (uint32_t)-1 - n:* should work if n <= messages_count - n:m or m should work if m <= messages_count */ range = array_get(seqset, &count); if (count == 0 || messages_count == 0) return FALSE; if (range[count-1].seq2 == (uint32_t)-1) { if (range[count-1].seq1 > messages_count && range[count-1].seq1 != (uint32_t)-1) return FALSE; } else { if (range[count-1].seq2 > messages_count) return FALSE; } return TRUE; } static int imap_search_get_msgset_arg(struct client_command_context *cmd, const char *messageset, struct mail_search_args **args_r, const char **error_r) { struct mail_search_args *args; args = mail_search_build_init(); args->args = p_new(args->pool, struct mail_search_arg, 1); args->args->type = SEARCH_SEQSET; p_array_init(&args->args->value.seqset, args->pool, 16); if (imap_seq_set_parse(messageset, &args->args->value.seqset) < 0 || !msgset_is_valid(&args->args->value.seqset, cmd->client->messages_count)) { *error_r = "Invalid messageset"; mail_search_args_unref(&args); return -1; } *args_r = args; return 0; } static int imap_search_get_uidset_arg(const char *uidset, struct mail_search_args **args_r, const char **error_r) { struct mail_search_args *args; args = mail_search_build_init(); args->args = p_new(args->pool, struct mail_search_arg, 1); args->args->type = SEARCH_UIDSET; p_array_init(&args->args->value.seqset, args->pool, 16); if (imap_seq_set_parse(uidset, &args->args->value.seqset) < 0) { *error_r = "Invalid uidset"; mail_search_args_unref(&args); return -1; } *args_r = args; return 0; } int imap_search_get_seqset(struct client_command_context *cmd, const char *set, bool uid, struct mail_search_args **search_args_r) { int ret; ret = imap_search_get_anyset(cmd, set, uid, search_args_r); if (ret > 0) { mail_search_args_init(*search_args_r, cmd->client->mailbox, TRUE, &cmd->client->search_saved_uidset); } return ret; } static int imap_search_get_searchres(struct client_command_context *cmd, struct mail_search_args **search_args_r) { struct mail_search_args *search_args; if (client_handle_search_save_ambiguity(cmd)) return 0; search_args = mail_search_build_init(); search_args->args = p_new(search_args->pool, struct mail_search_arg, 1); if (array_is_created(&cmd->client->search_saved_uidset)) { search_args->args->type = SEARCH_UIDSET; p_array_init(&search_args->args->value.seqset, search_args->pool, array_count(&cmd->client->search_saved_uidset)); array_append_array(&search_args->args->value.seqset, &cmd->client->search_saved_uidset); } else { /* $ not set yet, match nothing */ search_args->args->type = SEARCH_ALL; search_args->args->match_not = TRUE; } *search_args_r = search_args; return 1; } int imap_search_get_anyset(struct client_command_context *cmd, const char *set, bool uid, struct mail_search_args **search_args_r) { const char *error = NULL; int ret; if (strcmp(set, "$") == 0) { /* SEARCHRES extension: replace $ with the last saved search result */ return imap_search_get_searchres(cmd, search_args_r); } if (!uid) { ret = imap_search_get_msgset_arg(cmd, set, search_args_r, &error); } else { ret = imap_search_get_uidset_arg(set, search_args_r, &error); } if (ret < 0) { client_send_command_error(cmd, error); return -1; } return 1; } void imap_search_add_changed_since(struct mail_search_args *search_args, uint64_t modseq) { struct mail_search_arg *search_arg; search_arg = p_new(search_args->pool, struct mail_search_arg, 1); search_arg->type = SEARCH_MODSEQ; search_arg->value.modseq = p_new(search_args->pool, struct mail_search_modseq, 1); search_arg->value.modseq->modseq = modseq + 1; search_arg->next = search_args->args->next; search_args->args->next = search_arg; } dovecot-2.2.33.2/src/imap/cmd-x-cancel.c0000644000175000017500000000131313123174404014436 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_x_cancel(struct client_command_context *cmd) { struct client_command_context *cancel_cmd; const char *tag; /* */ if (!client_read_string_args(cmd, 1, &tag)) return FALSE; cancel_cmd = cmd->client->command_queue; for (; cancel_cmd != NULL; cancel_cmd = cancel_cmd->next) { if (cancel_cmd->tag != NULL && cancel_cmd != cmd && strcmp(cancel_cmd->tag, tag) == 0) { client_command_cancel(&cancel_cmd); client_send_tagline(cmd, "OK Command cancelled."); return TRUE; } } client_send_tagline(cmd, "NO Command tag not found."); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-x-state.c0000644000175000017500000000376113123174404014342 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "base64.h" #include "str.h" #include "imap-commands.h" #include "imap-state.h" bool cmd_x_state(struct client_command_context *cmd) { /* FIXME: state importing can cause unnecessarily large memory usage by specifying an old modseq, because the EXPUNGE/FETCH replies aren't currently sent asynchronously. so this command is disabled for now. */ #if 0 const struct imap_arg *args; const char *str, *error; buffer_t *state, *state_encoded; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; state = buffer_create_dynamic(cmd->pool, 256); if (imap_arg_get_astring(&args[0], &str)) { if (cmd->client->mailbox != NULL) { client_send_tagline(cmd, "BAD Can't be used in SELECTED state"); return TRUE; } if (base64_decode(str, strlen(str), NULL, state) < 0) ret = 0; else { ret = imap_state_import_external(cmd->client, state->data, state->used, &error); } if (ret < 0) { client_send_tagline(cmd, t_strdup_printf( "NO Failed to restore state: %s", error)); } else if (ret == 0) { client_send_tagline(cmd, t_strdup_printf( "BAD Broken state: %s", error)); } else { client_send_tagline(cmd, "OK State imported."); } return TRUE; } else if (args[0].type == IMAP_ARG_EOL) { if (!imap_state_export_external(cmd->client, state, &error)) { client_send_tagline(cmd, t_strdup_printf( "NO Can't save state: %s", error)); return TRUE; } state_encoded = buffer_create_dynamic(cmd->pool, MAX_BASE64_ENCODED_SIZE(state->used)+10); str_append(state_encoded, "* STATE "); base64_encode(state->data, state->used, state_encoded); client_send_line(cmd->client, str_c(state_encoded)); client_send_tagline(cmd, "OK State exported."); return TRUE; } else { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } #else client_send_command_error(cmd, "Command is disabled for now."); return TRUE; #endif } dovecot-2.2.33.2/src/imap/imap-search-args.h0000644000175000017500000000212013123174404015330 00000000000000#ifndef IMAP_SEARCH_ARGS_H #define IMAP_SEARCH_ARGS_H #include "mail-search.h" struct imap_arg; struct mailbox; struct client_command_context; /* Builds search arguments based on IMAP arguments. Returns -1 if search arguments are invalid, 0 if we have to wait for unambiguity, 1 if we can continue. */ int imap_search_args_build(struct client_command_context *cmd, const struct imap_arg *args, const char *charset, struct mail_search_args **search_args_r); /* Returns -1 if set is invalid, 0 if we have to wait for unambiguity, 1 if we were successful. search_args_r is set to contain either a seqset or uidset. */ int imap_search_get_anyset(struct client_command_context *cmd, const char *set, bool uid, struct mail_search_args **search_args_r); /* Like imap_search_get_anyset(), but always returns a seqset. */ int imap_search_get_seqset(struct client_command_context *cmd, const char *set, bool uid, struct mail_search_args **search_args_r); void imap_search_add_changed_since(struct mail_search_args *search_args, uint64_t modseq); #endif dovecot-2.2.33.2/src/imap/imap-common.h0000644000175000017500000000226313123174404014431 00000000000000#ifndef IMAP_COMMON_H #define IMAP_COMMON_H /* Disconnect client after idling this many milliseconds */ #define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000) /* If we can't send anything to client for this long, disconnect the client */ #define CLIENT_OUTPUT_TIMEOUT_MSECS (5*60*1000) /* Stop buffering more data into output stream after this many bytes */ #define CLIENT_OUTPUT_OPTIMAL_SIZE 2048 /* Disconnect client when it sends too many bad commands in a row */ #define CLIENT_MAX_BAD_COMMANDS 20 #include "lib.h" #include "imap-client.h" #include "imap-settings.h" struct mail_storage_service_input; typedef void imap_client_created_func_t(struct client **client); extern imap_client_created_func_t *hook_client_created; extern bool imap_debug; /* Sets the hook_client_created and returns the previous hook, which the new_hook should call if it's non-NULL. */ imap_client_created_func_t * ATTR_NOWARN_UNUSED_RESULT imap_client_created_hook_set(imap_client_created_func_t *new_hook); void imap_refresh_proctitle(void); int client_create_from_input(const struct mail_storage_service_input *input, int fd_in, int fd_out, struct client **client_r, const char **error_r); #endif dovecot-2.2.33.2/src/imap/imap-status.h0000644000175000017500000000150413123174404014461 00000000000000#ifndef IMAP_STATUS_H #define IMAP_STATUS_H struct imap_status_items { enum mailbox_status_items status; enum mailbox_metadata_items metadata; }; struct imap_status_result { struct mailbox_status status; struct mailbox_metadata metadata; enum mail_error error; const char *errstr; }; int imap_status_parse_items(struct client_command_context *cmd, const struct imap_arg *args, struct imap_status_items *items_r); int imap_status_get(struct client_command_context *cmd, struct mail_namespace *ns, const char *mailbox, const struct imap_status_items *items, struct imap_status_result *result_r); int imap_status_send(struct client *client, const char *mailbox_mutf7, const struct imap_status_items *items, const struct imap_status_result *result) ATTR_NOWARN_UNUSED_RESULT; #endif dovecot-2.2.33.2/src/imap/imap-fetch-body.c0000644000175000017500000003633713165463624015204 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "buffer.h" #include "str.h" #include "strescape.h" #include "istream.h" #include "ostream.h" #include "istream-header-filter.h" #include "message-parser.h" #include "mail-storage-private.h" #include "imap-quote.h" #include "imap-parser.h" #include "imap-msgpart.h" #include "imap-fetch.h" #include #include struct imap_fetch_body_data { const char *section; /* NOTE: always uppercased */ struct imap_msgpart *msgpart; unsigned int partial:1; unsigned int binary:1; unsigned int binary_size:1; }; static void fetch_read_error(struct imap_fetch_context *ctx, const char **disconnect_reason_r) { struct imap_fetch_state *state = &ctx->state; if (state->cur_input->stream_errno == ENOENT) { if (state->cur_mail->expunged) { *disconnect_reason_r = "Mail expunged while it was being FETCHed"; return; } } mail_storage_set_critical(state->cur_mail->box->storage, "read(%s) failed: %s (FETCH %s for mailbox %s UID %u)", i_stream_get_name(state->cur_input), i_stream_get_error(state->cur_input), state->cur_human_name, mailbox_get_vname(state->cur_mail->box), state->cur_mail->uid); *disconnect_reason_r = "FETCH read() failed"; } static const char *get_body_name(const struct imap_fetch_body_data *body) { string_t *str; str = t_str_new(128); if (body->binary_size) str_append(str, "BINARY.SIZE"); else if (body->binary) str_append(str, "BINARY"); else str_append(str, "BODY"); str_printfa(str, "[%s]", body->section); if (body->partial) { str_printfa(str, "<%"PRIuUOFF_T">", imap_msgpart_get_partial_offset(body->msgpart)); } return str_c(str); } static string_t *get_prefix(struct imap_fetch_state *state, const struct imap_fetch_body_data *body, uoff_t size, bool has_nuls) { string_t *str; str = t_str_new(128); if (state->cur_first) state->cur_first = FALSE; else str_append_c(str, ' '); str_append(str, get_body_name(body)); if (size == (uoff_t)-1) str_append(str, " NIL"); else if (has_nuls && body->binary) str_printfa(str, " ~{%"PRIuUOFF_T"}\r\n", size); else str_printfa(str, " {%"PRIuUOFF_T"}\r\n", size); return str; } static int fetch_stream_continue(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; const char *disconnect_reason; off_t ret; o_stream_set_max_buffer_size(ctx->client->output, 0); ret = o_stream_send_istream(ctx->client->output, state->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ret > 0) { state->cur_offset += ret; if (ctx->state.cur_stats_sizep != NULL) *ctx->state.cur_stats_sizep += ret; } if (state->cur_offset != state->cur_size) { /* unfinished */ if (state->cur_input->stream_errno != 0) { fetch_read_error(ctx, &disconnect_reason); client_disconnect(ctx->client, disconnect_reason); return -1; } if (!i_stream_have_bytes_left(state->cur_input)) { /* Input stream gave less data than expected */ mail_set_cache_corrupted_reason(state->cur_mail, state->cur_size_field, t_strdup_printf( "read(%s): FETCH %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, i_stream_get_name(state->cur_input), state->cur_human_name, state->cur_offset, state->cur_size)); client_disconnect(ctx->client, "FETCH failed"); return -1; } if (ret < 0) { /* client probably disconnected */ return -1; } o_stream_set_flush_pending(ctx->client->output, TRUE); return 0; } return 1; } static const char * get_body_human_name(pool_t pool, struct imap_fetch_body_data *body) { string_t *str; uoff_t partial_offset, partial_size; str = t_str_new(64); if (body->binary) str_append(str, "BINARY["); else str_append(str, "BODY["); str_append(str, body->section); str_append_c(str, ']'); partial_offset = imap_msgpart_get_partial_offset(body->msgpart); partial_size = imap_msgpart_get_partial_size(body->msgpart); if (partial_offset != 0 || partial_size != (uoff_t)-1) { str_printfa(str, "<%"PRIuUOFF_T, partial_offset); if (partial_size != (uoff_t)-1) str_printfa(str, ".%"PRIuUOFF_T, partial_size); str_append_c(str, '>'); } return p_strdup(pool, str_c(str)); } static void fetch_state_update_stats(struct imap_fetch_context *ctx, const struct imap_msgpart *msgpart) { if (!imap_msgpart_contains_body(msgpart)) { ctx->client->fetch_hdr_count++; ctx->state.cur_stats_sizep = &ctx->client->fetch_hdr_bytes; } else { ctx->client->fetch_body_count++; ctx->state.cur_stats_sizep = &ctx->client->fetch_body_bytes; } } static int fetch_body_msgpart(struct imap_fetch_context *ctx, struct mail *mail, struct imap_fetch_body_data *body) { struct imap_msgpart_open_result result; string_t *str; if (mail == NULL) { imap_msgpart_free(&body->msgpart); return 1; } if (imap_msgpart_open(mail, body->msgpart, &result) < 0) return -1; ctx->state.cur_input = result.input; ctx->state.cur_size = result.size; ctx->state.cur_size_field = result.size_field; ctx->state.cur_human_name = get_body_human_name(ctx->ctx_pool, body); fetch_state_update_stats(ctx, body->msgpart); str = get_prefix(&ctx->state, body, ctx->state.cur_size, result.binary_decoded_input_has_nuls); o_stream_nsend(ctx->client->output, str_data(str), str_len(str)); ctx->state.cont_handler = fetch_stream_continue; return ctx->state.cont_handler(ctx); } static int fetch_binary_size(struct imap_fetch_context *ctx, struct mail *mail, struct imap_fetch_body_data *body) { string_t *str; uoff_t size; if (mail == NULL) { imap_msgpart_free(&body->msgpart); return 1; } if (imap_msgpart_size(mail, body->msgpart, &size) < 0) return -1; str = t_str_new(128); if (ctx->state.cur_first) ctx->state.cur_first = FALSE; else str_append_c(str, ' '); str_printfa(str, "%s %"PRIuUOFF_T, get_body_name(body), size); if (o_stream_send(ctx->client->output, str_data(str), str_len(str)) < 0) return -1; return 1; } /* Parse next digits in string into integer. Returns -1 if the integer becomes too big and wraps. */ static int read_uoff_t(const char **p, uoff_t *value) { return str_parse_uoff(*p, value, p); } static int body_header_fields_parse(struct imap_fetch_init_context *ctx, struct imap_fetch_body_data *body, const char *prefix, const struct imap_arg *args, unsigned int args_count) { string_t *str; const char *value; size_t i; str = str_new(ctx->pool, 128); str_append(str, prefix); str_append(str, " ("); for (i = 0; i < args_count; i++) { if (!imap_arg_get_astring(&args[i], &value)) { ctx->error = "Invalid BODY[..] parameter: " "Header list contains non-strings"; return -1; } value = t_str_ucase(value); if (i != 0) str_append_c(str, ' '); if (args[i].type == IMAP_ARG_ATOM) str_append(str, value); else imap_append_quoted(str, value); } str_append_c(str, ')'); body->section = str_c(str); return 0; } static int body_parse_partial(struct imap_fetch_body_data *body, const char *p, const char **error_r) { uoff_t offset, size = (uoff_t)-1; if (*p == '\0') return 0; /* */ if (*p != '<') { *error_r = "Unexpected data after ']'"; return -1; } p++; body->partial = TRUE; if (read_uoff_t(&p, &offset) < 0 || offset > OFF_T_MAX) { /* wrapped */ *error_r = "Too big partial start"; return -1; } if (*p == '.') { p++; if (read_uoff_t(&p, &size) < 0 || size > OFF_T_MAX) { /* wrapped */ *error_r = "Too big partial end"; return -1; } } if (*p != '>') { *error_r = "Missing '>' in partial"; return -1; } if (p[1] != '\0') { *error_r = "Unexpected data after '>'"; return -1; } imap_msgpart_set_partial(body->msgpart, offset, size); return 0; } bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx) { struct imap_fetch_body_data *body; const struct imap_arg *list_args; unsigned int list_count; const char *str, *p, *error; i_assert(strncmp(ctx->name, "BODY", 4) == 0); p = ctx->name + 4; body = p_new(ctx->pool, struct imap_fetch_body_data, 1); if (strncmp(p, ".PEEK", 5) == 0) p += 5; else ctx->fetch_ctx->flags_update_seen = TRUE; if (*p != '[') { ctx->error = "Invalid BODY[..] parameter: Missing '['"; return FALSE; } if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) { /* BODY[HEADER.FIELDS.. (headers list)] */ if (!imap_arg_get_atom(&ctx->args[1], &str) || str[0] != ']') { ctx->error = "Invalid BODY[..] parameter: Missing ']'"; return FALSE; } if (body_header_fields_parse(ctx, body, p+1, list_args, list_count) < 0) return FALSE; p = str+1; ctx->args += 2; } else { /* no headers list */ body->section = p+1; p = strchr(body->section, ']'); if (p == NULL) { ctx->error = "Invalid BODY[..] parameter: Missing ']'"; return FALSE; } body->section = p_strdup_until(ctx->pool, body->section, p); p++; } if (imap_msgpart_parse(body->section, &body->msgpart) < 0) { ctx->error = "Invalid BODY[..] section"; return FALSE; } ctx->fetch_ctx->fetch_data |= imap_msgpart_get_fetch_data(body->msgpart); imap_msgpart_get_wanted_headers(body->msgpart, &ctx->fetch_ctx->all_headers); if (body_parse_partial(body, p, &error) < 0) { ctx->error = p_strdup_printf(ctx->pool, "Invalid BODY[..] parameter: %s", error); return FALSE; } /* update the section name for the imap_fetch_add_handler() */ ctx->name = p_strdup(ctx->pool, get_body_name(body)); imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "NIL", fetch_body_msgpart, body); return TRUE; } bool imap_fetch_binary_init(struct imap_fetch_init_context *ctx) { struct imap_fetch_body_data *body; const struct imap_arg *list_args; unsigned int list_count; const char *str, *p, *error; i_assert(strncmp(ctx->name, "BINARY", 6) == 0); p = ctx->name + 6; body = p_new(ctx->pool, struct imap_fetch_body_data, 1); body->binary = TRUE; if (strncmp(p, ".SIZE", 5) == 0) { /* fetch decoded size of the section */ p += 5; body->binary_size = TRUE; } else if (strncmp(p, ".PEEK", 5) == 0) { p += 5; } else { ctx->fetch_ctx->flags_update_seen = TRUE; } if (*p != '[') { ctx->error = "Invalid BINARY[..] parameter: Missing '['"; return FALSE; } if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) { /* BINARY[HEADER.FIELDS.. (headers list)] */ if (!imap_arg_get_atom(&ctx->args[1], &str) || str[0] != ']') { ctx->error = "Invalid BINARY[..] parameter: Missing ']'"; return FALSE; } if (body_header_fields_parse(ctx, body, p+1, list_args, list_count) < 0) return FALSE; p = str+1; ctx->args += 2; } else { /* no headers list */ body->section = p+1; p = strchr(body->section, ']'); if (p == NULL) { ctx->error = "Invalid BINARY[..] parameter: Missing ']'"; return FALSE; } body->section = p_strdup_until(ctx->pool, body->section, p); p++; } if (imap_msgpart_parse(body->section, &body->msgpart) < 0) { ctx->error = "Invalid BINARY[..] section"; return FALSE; } imap_msgpart_set_decode_to_binary(body->msgpart); ctx->fetch_ctx->fetch_data |= imap_msgpart_get_fetch_data(body->msgpart); if (!body->binary_size) { if (body_parse_partial(body, p, &error) < 0) { ctx->error = p_strdup_printf(ctx->pool, "Invalid BINARY[..] parameter: %s", error); return FALSE; } } /* update the section name for the imap_fetch_add_handler() */ ctx->name = p_strdup(ctx->pool, get_body_name(body)); if (body->binary_size) { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "0", fetch_binary_size, body); } else { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "NIL", fetch_body_msgpart, body); } return TRUE; } static int ATTR_NULL(3) fetch_rfc822_size(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { uoff_t size; if (mail_get_virtual_size(mail, &size) < 0) return -1; str_printfa(ctx->state.cur_str, "RFC822.SIZE %"PRIuUOFF_T" ", size); return 1; } static int fetch_and_free_msgpart(struct imap_fetch_context *ctx, struct mail *mail, struct imap_msgpart **_msgpart) { struct imap_msgpart_open_result result; int ret; ret = imap_msgpart_open(mail, *_msgpart, &result); imap_msgpart_free(_msgpart); if (ret < 0) return -1; ctx->state.cur_input = result.input; ctx->state.cur_size = result.size; ctx->state.cur_size_field = result.size_field; ctx->state.cont_handler = fetch_stream_continue; return 0; } static int ATTR_NULL(3) fetch_rfc822(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { struct imap_msgpart *msgpart; const char *str; msgpart = imap_msgpart_full(); fetch_state_update_stats(ctx, msgpart); if (fetch_and_free_msgpart(ctx, mail, &msgpart) < 0) return -1; str = t_strdup_printf(" RFC822 {%"PRIuUOFF_T"}\r\n", ctx->state.cur_size); if (ctx->state.cur_first) { str++; ctx->state.cur_first = FALSE; } o_stream_nsend_str(ctx->client->output, str); ctx->state.cur_human_name = "RFC822"; return ctx->state.cont_handler(ctx); } static int ATTR_NULL(3) fetch_rfc822_header(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { struct imap_msgpart *msgpart; const char *str; msgpart = imap_msgpart_header(); fetch_state_update_stats(ctx, msgpart); if (fetch_and_free_msgpart(ctx, mail, &msgpart) < 0) return -1; str = t_strdup_printf(" RFC822.HEADER {%"PRIuUOFF_T"}\r\n", ctx->state.cur_size); if (ctx->state.cur_first) { str++; ctx->state.cur_first = FALSE; } o_stream_nsend_str(ctx->client->output, str); ctx->state.cur_human_name = "RFC822.HEADER"; return ctx->state.cont_handler(ctx); } static int ATTR_NULL(3) fetch_rfc822_text(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { struct imap_msgpart *msgpart; const char *str; msgpart = imap_msgpart_body(); fetch_state_update_stats(ctx, msgpart); if (fetch_and_free_msgpart(ctx, mail, &msgpart) < 0) return -1; str = t_strdup_printf(" RFC822.TEXT {%"PRIuUOFF_T"}\r\n", ctx->state.cur_size); if (ctx->state.cur_first) { str++; ctx->state.cur_first = FALSE; } o_stream_nsend_str(ctx->client->output, str); ctx->state.cur_human_name = "RFC822.TEXT"; return ctx->state.cont_handler(ctx); } bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx) { const char *name = ctx->name; if (name[6] == '\0') { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY; ctx->fetch_ctx->flags_update_seen = TRUE; imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822, (void *)NULL); return TRUE; } if (strcmp(name+6, ".SIZE") == 0) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE; imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "0", fetch_rfc822_size, (void *)NULL); return TRUE; } if (strcmp(name+6, ".HEADER") == 0) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER; imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822_header, (void *)NULL); return TRUE; } if (strcmp(name+6, ".TEXT") == 0) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY; ctx->fetch_ctx->flags_update_seen = TRUE; imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822_text, (void *)NULL); return TRUE; } ctx->error = t_strconcat("Unknown parameter ", name, NULL); return FALSE; } dovecot-2.2.33.2/src/imap/cmd-namespace.c0000644000175000017500000000454513123174404014712 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "imap-utf7.h" #include "imap-quote.h" #include "imap-commands.h" #include "mail-namespace.h" struct namespace_order { int secondary_order; struct mail_namespace *ns; }; static int namespace_order_cmp(const struct namespace_order *no1, const struct namespace_order *no2) { if (no1->ns->set->order < no2->ns->set->order) return -1; if (no1->ns->set->order > no2->ns->set->order) return 1; if (no1->secondary_order < no2->secondary_order) return -1; if (no1->secondary_order > no2->secondary_order) return 1; return 0; } static void list_namespaces(struct mail_namespace *ns, enum mail_namespace_type type, string_t *str) { ARRAY(struct namespace_order) ns_order; struct namespace_order *no; unsigned int count = 0; string_t *mutf7_prefix; char ns_sep; t_array_init(&ns_order, 4); while (ns != NULL) { if (ns->type == type && (ns->flags & NAMESPACE_FLAG_HIDDEN) == 0) { no = array_append_space(&ns_order); no->ns = ns; no->secondary_order = ++count; } ns = ns->next; } if (array_count(&ns_order) == 0) { str_append(str, "NIL"); return; } array_sort(&ns_order, namespace_order_cmp); mutf7_prefix = t_str_new(64); str_append_c(str, '('); array_foreach_modifiable(&ns_order, no) { ns_sep = mail_namespace_get_sep(no->ns); str_append_c(str, '('); str_truncate(mutf7_prefix, 0); if (imap_utf8_to_utf7(no->ns->prefix, mutf7_prefix) < 0) { i_panic("LIST: Namespace prefix not UTF-8: %s", no->ns->prefix); } imap_append_string(str, str_c(mutf7_prefix)); str_append(str, " \""); if (ns_sep == '\\') str_append_c(str, '\\'); str_append_c(str, ns_sep); str_append(str, "\")"); } str_append_c(str, ')'); } bool cmd_namespace(struct client_command_context *cmd) { struct client *client = cmd->client; string_t *str; str = t_str_new(256); str_append(str, "* NAMESPACE "); list_namespaces(client->user->namespaces, MAIL_NAMESPACE_TYPE_PRIVATE, str); str_append_c(str, ' '); list_namespaces(client->user->namespaces, MAIL_NAMESPACE_TYPE_SHARED, str); str_append_c(str, ' '); list_namespaces(client->user->namespaces, MAIL_NAMESPACE_TYPE_PUBLIC, str); client_send_line(client, str_c(str)); client_send_tagline(cmd, "OK Namespace completed."); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-examine.c0000644000175000017500000000034013123174404014371 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_examine(struct client_command_context *cmd) { return cmd_select_full(cmd, TRUE); } dovecot-2.2.33.2/src/imap/imap-fetch.c0000644000175000017500000006720213165463624014244 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "array.h" #include "buffer.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "message-size.h" #include "imap-date.h" #include "imap-utf7.h" #include "mail-search-build.h" #include "imap-commands.h" #include "imap-quote.h" #include "imap-fetch.h" #include "imap-util.h" #include #define BODY_NIL_REPLY \ "\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0" #define ENVELOPE_NIL_REPLY \ "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)" static ARRAY(struct imap_fetch_handler) fetch_handlers; static int imap_fetch_handler_cmp(const struct imap_fetch_handler *h1, const struct imap_fetch_handler *h2) { return strcmp(h1->name, h2->name); } void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers, size_t count) { array_append(&fetch_handlers, handlers, count); array_sort(&fetch_handlers, imap_fetch_handler_cmp); } void imap_fetch_handler_unregister(const char *name) { const struct imap_fetch_handler *handler, *first_handler; first_handler = array_idx(&fetch_handlers, 0); handler = imap_fetch_handler_lookup(name); i_assert(handler != NULL); array_delete(&fetch_handlers, handler - first_handler, 1); } static int imap_fetch_handler_bsearch(const char *name, const struct imap_fetch_handler *h) { return strcmp(name, h->name); } const struct imap_fetch_handler *imap_fetch_handler_lookup(const char *name) { return array_bsearch(&fetch_handlers, name, imap_fetch_handler_bsearch); } bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx) { const struct imap_fetch_handler *handler; const char *lookup_name, *p; for (p = init_ctx->name; i_isalnum(*p) || *p == '-'; p++) ; lookup_name = t_strdup_until(init_ctx->name, p); handler = array_bsearch(&fetch_handlers, lookup_name, imap_fetch_handler_bsearch); if (handler == NULL) { init_ctx->error = t_strdup_printf("Unknown parameter: %s", init_ctx->name); return FALSE; } if (!handler->init(init_ctx)) { i_assert(init_ctx->error != NULL); return FALSE; } return TRUE; } void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx, bool (*init)(struct imap_fetch_init_context *)) { struct imap_fetch_init_context init_ctx; i_zero(&init_ctx); init_ctx.fetch_ctx = ctx; init_ctx.pool = ctx->ctx_pool; if (!init(&init_ctx)) i_unreached(); } int imap_fetch_att_list_parse(struct client *client, pool_t pool, const struct imap_arg *list, struct imap_fetch_context **fetch_ctx_r, const char **error_r) { struct imap_fetch_init_context init_ctx; const char *str; i_zero(&init_ctx); init_ctx.fetch_ctx = imap_fetch_alloc(client, pool, "NOTIFY"); init_ctx.pool = pool; init_ctx.args = list; while (imap_arg_get_atom(init_ctx.args, &str)) { init_ctx.name = t_str_ucase(str); init_ctx.args++; if (!imap_fetch_init_handler(&init_ctx)) { *error_r = t_strconcat("Invalid fetch-att list: ", init_ctx.error, NULL); imap_fetch_free(&init_ctx.fetch_ctx); return -1; } } if (!IMAP_ARG_IS_EOL(init_ctx.args)) { *error_r = "fetch-att list contains non-atoms."; imap_fetch_free(&init_ctx.fetch_ctx); return -1; } *fetch_ctx_r = init_ctx.fetch_ctx; return 0; } struct imap_fetch_context * imap_fetch_alloc(struct client *client, pool_t pool, const char *reason) { struct imap_fetch_context *ctx; ctx = p_new(pool, struct imap_fetch_context, 1); ctx->client = client; ctx->ctx_pool = pool; ctx->reason = p_strdup(pool, reason); pool_ref(pool); p_array_init(&ctx->all_headers, pool, 64); p_array_init(&ctx->handlers, pool, 16); p_array_init(&ctx->tmp_keywords, pool, client->keywords.announce_count + 8); return ctx; } #undef imap_fetch_add_handler void imap_fetch_add_handler(struct imap_fetch_init_context *ctx, enum imap_fetch_handler_flags flags, const char *nil_reply, imap_fetch_handler_t *handler, void *context) { /* partially because of broken clients, but also partially because it potentially can make client implementations faster, we have a buffered parameter which basically means that the handler promises to write the output in fetch_ctx->state.cur_str. The cur_str is then sent to client before calling any non-buffered handlers. We try to keep the handler registration order the same as the client requested them. This is especially useful to get UID returned first, which some clients rely on.. */ const struct imap_fetch_context_handler *ctx_handler; struct imap_fetch_context_handler h; if (context == NULL) { /* don't allow duplicate handlers */ array_foreach(&ctx->fetch_ctx->handlers, ctx_handler) { if (ctx_handler->handler == handler && ctx_handler->context == NULL) return; } } i_zero(&h); h.handler = handler; h.context = context; h.buffered = (flags & IMAP_FETCH_HANDLER_FLAG_BUFFERED) != 0; h.want_deinit = (flags & IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT) != 0; h.name = p_strdup(ctx->pool, ctx->name); h.nil_reply = p_strdup(ctx->pool, nil_reply); if (!h.buffered) array_append(&ctx->fetch_ctx->handlers, &h, 1); else { array_insert(&ctx->fetch_ctx->handlers, ctx->fetch_ctx->buffered_handlers_count, &h, 1); ctx->fetch_ctx->buffered_handlers_count++; } } static void expunges_drop_known(struct mailbox *box, const struct imap_fetch_qresync_args *qresync_args, struct mailbox_transaction_context *trans, ARRAY_TYPE(seq_range) *expunged_uids) { struct mailbox_status status; struct mail *mail; const uint32_t *seqs, *uids; unsigned int i, count; seqs = array_get(qresync_args->qresync_sample_seqset, &count); uids = array_idx(qresync_args->qresync_sample_uidset, 0); i_assert(array_count(qresync_args->qresync_sample_uidset) == count); i_assert(count > 0); mailbox_get_open_status(box, STATUS_MESSAGES, &status); mail = mail_alloc(trans, 0, NULL); /* FIXME: we could do removals from the middle as well */ for (i = 0; i < count && seqs[i] <= status.messages; i++) { mail_set_seq(mail, seqs[i]); if (uids[i] != mail->uid) break; } if (i > 0) seq_range_array_remove_range(expunged_uids, 1, uids[i-1]); mail_free(&mail); } static int get_expunges_fallback(struct mailbox *box, const struct imap_fetch_qresync_args *qresync_args, const ARRAY_TYPE(seq_range) *uid_filter_arr, ARRAY_TYPE(seq_range) *expunged_uids) { struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mail *mail; const struct seq_range *uid_filter; struct mailbox_status status; unsigned int i, count; uint32_t next_uid; int ret = 0; uid_filter = array_get(uid_filter_arr, &count); i_assert(count > 0); i = 0; next_uid = uid_filter[0].seq1; /* search UIDs only in given range */ search_args = mail_search_build_init(); search_args->args = p_new(search_args->pool, struct mail_search_arg, 1); search_args->args->type = SEARCH_UIDSET; i_array_init(&search_args->args->value.seqset, count); array_append_array(&search_args->args->value.seqset, uid_filter_arr); trans = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(trans, "FETCH send VANISHED"); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { if (mail->uid == next_uid) { if (next_uid < uid_filter[i].seq2) next_uid++; else if (++i < count) next_uid = uid_filter[i].seq1; else break; } else { /* next_uid .. mail->uid-1 are expunged */ i_assert(mail->uid > next_uid); while (mail->uid > uid_filter[i].seq2) { seq_range_array_add_range(expunged_uids, next_uid, uid_filter[i].seq2); i++; i_assert(i < count); next_uid = uid_filter[i].seq1; } if (next_uid != mail->uid) { seq_range_array_add_range(expunged_uids, next_uid, mail->uid - 1); } if (uid_filter[i].seq2 != mail->uid) next_uid = mail->uid + 1; else if (++i < count) next_uid = uid_filter[i].seq1; else break; } } if (i < count) { i_assert(next_uid <= uid_filter[i].seq2); seq_range_array_add_range(expunged_uids, next_uid, uid_filter[i].seq2); i++; } for (; i < count; i++) { seq_range_array_add_range(expunged_uids, uid_filter[i].seq1, uid_filter[i].seq2); } mailbox_get_open_status(box, STATUS_UIDNEXT, &status); seq_range_array_remove_range(expunged_uids, status.uidnext, (uint32_t)-1); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; if (ret == 0 && qresync_args->qresync_sample_seqset != NULL && array_is_created(qresync_args->qresync_sample_seqset)) expunges_drop_known(box, qresync_args, trans, expunged_uids); (void)mailbox_transaction_commit(&trans); return ret; } int imap_fetch_send_vanished(struct client *client, struct mailbox *box, const struct mail_search_args *search_args, const struct imap_fetch_qresync_args *qresync_args) { const struct mail_search_arg *uidarg = search_args->args; const struct mail_search_arg *modseqarg = uidarg->next; const ARRAY_TYPE(seq_range) *uid_filter; uint64_t modseq; ARRAY_TYPE(seq_range) expunged_uids_range; string_t *str; int ret = 0; i_assert(uidarg->type == SEARCH_UIDSET); i_assert(modseqarg->type == SEARCH_MODSEQ); uid_filter = &uidarg->value.seqset; modseq = modseqarg->value.modseq->modseq - 1; i_array_init(&expunged_uids_range, array_count(uid_filter)); if (!mailbox_get_expunged_uids(box, modseq, uid_filter, &expunged_uids_range)) { /* return all expunged UIDs */ if (get_expunges_fallback(box, qresync_args, uid_filter, &expunged_uids_range) < 0) { array_clear(&expunged_uids_range); ret = -1; } } if (array_count(&expunged_uids_range) > 0) { str = str_new(default_pool, 128); str_append(str, "* VANISHED (EARLIER) "); imap_write_seq_range(str, &expunged_uids_range); str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); str_free(&str); } array_free(&expunged_uids_range); return ret; } static void imap_fetch_init(struct imap_fetch_context *ctx) { if (ctx->initialized) return; ctx->initialized = TRUE; if (ctx->flags_update_seen && !ctx->flags_have_handler) { ctx->flags_show_only_seen_changes = TRUE; imap_fetch_init_nofail_handler(ctx, imap_fetch_flags_init); } if ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0) ctx->fetch_data |= MAIL_FETCH_NUL_STATE; } void imap_fetch_begin(struct imap_fetch_context *ctx, struct mailbox *box, struct mail_search_args *search_args) { enum mailbox_transaction_flags trans_flags = MAILBOX_TRANSACTION_FLAG_REFRESH; struct mailbox_header_lookup_ctx *wanted_headers = NULL; const char *const *headers; i_assert(!ctx->state.fetching); imap_fetch_init(ctx); i_zero(&ctx->state); if (array_count(&ctx->all_headers) > 0 && ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) == 0)) { array_append_zero(&ctx->all_headers); headers = array_idx(&ctx->all_headers, 0); wanted_headers = mailbox_header_lookup_init(box, headers); array_delete(&ctx->all_headers, array_count(&ctx->all_headers)-1, 1); } if (ctx->flags_update_seen) { /* Hide the implicit \Seen flag addition. Otherwise a separate untagged FETCH FLAGS (\Seen) would be sent on top of the one FLAGS (\Seen) already added in the main FETCH reply. We don't set this always, because some plugins might want to do their own flag changes which we don't want hidden. (Of course this isn't perfect since if implicit \Seen flags are added, other flag changes are also hidden.) */ trans_flags |= MAILBOX_TRANSACTION_FLAG_HIDE; } ctx->state.trans = mailbox_transaction_begin(box, trans_flags); mailbox_transaction_set_reason(ctx->state.trans, ctx->reason); mail_search_args_init(search_args, box, TRUE, &ctx->client->search_saved_uidset); ctx->state.search_ctx = mailbox_search_init(ctx->state.trans, search_args, NULL, ctx->fetch_data, wanted_headers); ctx->state.cur_str = str_new(default_pool, 8192); ctx->state.fetching = TRUE; if (wanted_headers != NULL) mailbox_header_lookup_unref(&wanted_headers); } static int imap_fetch_flush_buffer(struct imap_fetch_context *ctx) { const unsigned char *data; size_t len; data = str_data(ctx->state.cur_str); len = str_len(ctx->state.cur_str); if (len == 0) return 0; /* there's an extra space at the end if we added any fetch items to buffer */ if (data[len-1] == ' ') { len--; ctx->state.cur_first = FALSE; } ctx->state.line_partial = TRUE; if (o_stream_send(ctx->client->output, data, len) < 0) return -1; str_truncate(ctx->state.cur_str, 0); return 0; } static int imap_fetch_send_nil_reply(struct imap_fetch_context *ctx) { const struct imap_fetch_context_handler *handler; if (!ctx->state.cur_first) str_append_c(ctx->state.cur_str, ' '); handler = array_idx(&ctx->handlers, ctx->state.cur_handler); str_printfa(ctx->state.cur_str, "%s %s ", handler->name, handler->nil_reply); if (!handler->buffered) { if (imap_fetch_flush_buffer(ctx) < 0) return -1; } return 0; } static void imap_fetch_fix_empty_reply(struct imap_fetch_context *ctx) { if (ctx->state.line_partial && ctx->state.cur_first) { /* we've flushed an empty "FETCH (" reply so far. we can't take it back, but RFC 3501 doesn't allow returning empty "FETCH ()" either, so just add the current message's UID there. */ str_printfa(ctx->state.cur_str, "UID %u ", ctx->state.cur_mail->uid); } } static bool imap_fetch_cur_failed(struct imap_fetch_context *ctx) { ctx->failures = TRUE; if (ctx->client->set->parsed_fetch_failure == IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY) return FALSE; if (!array_is_created(&ctx->fetch_failed_uids)) p_array_init(&ctx->fetch_failed_uids, ctx->ctx_pool, 8); seq_range_array_add(&ctx->fetch_failed_uids, ctx->state.cur_mail->uid); if (ctx->error == MAIL_ERROR_NONE) { /* preserve the first error, since it may change in storage. */ ctx->errstr = p_strdup(ctx->ctx_pool, mailbox_get_last_error(ctx->state.cur_mail->box, &ctx->error)); } return TRUE; } static int imap_fetch_more_int(struct imap_fetch_context *ctx, bool cancel) { struct imap_fetch_state *state = &ctx->state; struct client *client = ctx->client; const struct imap_fetch_context_handler *handlers; unsigned int count; int ret; if (state->cont_handler != NULL) { ret = state->cont_handler(ctx); if (ret == 0) return 0; if (ret < 0) { if (client->output->closed) return -1; if (state->cur_mail->expunged) { /* not an error, just lost it. */ state->skipped_expunged_msgs = TRUE; if (imap_fetch_send_nil_reply(ctx) < 0) return -1; } else { if (!imap_fetch_cur_failed(ctx)) return -1; } } state->cont_handler = NULL; state->cur_offset = 0; state->cur_handler++; if (state->cur_input != NULL) i_stream_unref(&state->cur_input); } handlers = array_get(&ctx->handlers, &count); for (;;) { if (o_stream_get_buffer_used_size(client->output) >= CLIENT_OUTPUT_OPTIMAL_SIZE) { ret = o_stream_flush(client->output); if (ret <= 0) return ret; } if (state->cur_mail == NULL) { if (cancel) return 1; if (!mailbox_search_next(state->search_ctx, &state->cur_mail)) break; str_printfa(state->cur_str, "* %u FETCH (", state->cur_mail->seq); state->cur_first = TRUE; state->cur_str_prefix_size = str_len(state->cur_str); i_assert(!state->line_partial); } for (; state->cur_handler < count; state->cur_handler++) { if (str_len(state->cur_str) > 0 && !handlers[state->cur_handler].buffered) { /* first non-buffered handler. flush the buffer. */ if (imap_fetch_flush_buffer(ctx) < 0) return -1; } i_assert(state->cur_input == NULL); T_BEGIN { const struct imap_fetch_context_handler *h = &handlers[state->cur_handler]; ret = h->handler(ctx, state->cur_mail, h->context); } T_END; if (ret == 0) return 0; if (ret < 0) { if (state->cur_mail->expunged) { /* not an error, just lost it. */ state->skipped_expunged_msgs = TRUE; if (imap_fetch_send_nil_reply(ctx) < 0) return -1; } else { i_assert(ret < 0 || state->cont_handler != NULL); if (!imap_fetch_cur_failed(ctx)) return -1; } } state->cont_handler = NULL; state->cur_offset = 0; if (state->cur_input != NULL) i_stream_unref(&state->cur_input); } imap_fetch_fix_empty_reply(ctx); if (str_len(state->cur_str) > 0 && (state->line_partial || str_len(state->cur_str) != state->cur_str_prefix_size)) { /* no non-buffered handlers */ if (imap_fetch_flush_buffer(ctx) < 0) return -1; } if (state->line_partial) o_stream_nsend(client->output, ")\r\n", 3); client->last_output = ioloop_time; state->cur_mail = NULL; state->cur_handler = 0; state->line_partial = FALSE; } return ctx->failures ? -1 : 1; } int imap_fetch_more(struct imap_fetch_context *ctx, struct client_command_context *cmd) { int ret; i_assert(ctx->client->output_cmd_lock == NULL || ctx->client->output_cmd_lock == cmd); ret = imap_fetch_more_int(ctx, cmd->cancel); if (ret < 0) ctx->state.failed = TRUE; if (ctx->state.line_partial) { /* nothing can be sent until FETCH is finished */ ctx->client->output_cmd_lock = cmd; } if (cmd->cancel && ctx->client->output_cmd_lock != NULL) { /* canceling didn't really work. we must not output anything anymore. */ if (!ctx->client->destroyed) client_disconnect(ctx->client, "Failed to cancel FETCH"); ctx->client->output_cmd_lock = NULL; } return ret; } int imap_fetch_more_no_lock_update(struct imap_fetch_context *ctx) { int ret; ret = imap_fetch_more_int(ctx, FALSE); if (ret < 0) { ctx->state.failed = TRUE; if (ctx->state.line_partial) { /* we can't send any more replies to client, because the FETCH reply wasn't fully sent. */ client_disconnect(ctx->client, "NOTIFY failed in the middle of FETCH reply"); } } return ret; } int imap_fetch_end(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; if (ctx->state.fetching) { ctx->state.fetching = FALSE; if (state->line_partial) { imap_fetch_fix_empty_reply(ctx); if (imap_fetch_flush_buffer(ctx) < 0) state->failed = TRUE; if (o_stream_send(ctx->client->output, ")\r\n", 3) < 0) state->failed = TRUE; } } ctx->client->output_cmd_lock = NULL; if (state->cur_str != NULL) str_free(&state->cur_str); if (state->cur_input != NULL) i_stream_unref(&state->cur_input); if (state->search_ctx != NULL) { if (mailbox_search_deinit(&state->search_ctx) < 0) state->failed = TRUE; } if (state->trans != NULL) { /* even if something failed, we want to commit changes to cache, as well as possible \Seen flag changes for FETCH replies we returned so far. */ if (mailbox_transaction_commit(&state->trans) < 0) state->failed = TRUE; } return state->failed ? -1 : 0; } void imap_fetch_free(struct imap_fetch_context **_ctx) { struct imap_fetch_context *ctx = *_ctx; const struct imap_fetch_context_handler *handler; *_ctx = NULL; (void)imap_fetch_end(ctx); array_foreach(&ctx->handlers, handler) { if (handler->want_deinit) handler->handler(ctx, NULL, handler->context); } pool_unref(&ctx->ctx_pool); } static int fetch_body(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { const char *body; if (mail_get_special(mail, MAIL_FETCH_IMAP_BODY, &body) < 0) return -1; if (ctx->state.cur_first) ctx->state.cur_first = FALSE; else { if (o_stream_send(ctx->client->output, " ", 1) < 0) return -1; } if (o_stream_send(ctx->client->output, "BODY (", 6) < 0 || o_stream_send_str(ctx->client->output, body) < 0 || o_stream_send(ctx->client->output, ")", 1) < 0) return -1; return 1; } static bool fetch_body_init(struct imap_fetch_init_context *ctx) { if (ctx->name[4] == '\0') { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODY; imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY")", fetch_body, NULL); return TRUE; } return imap_fetch_body_section_init(ctx); } static int fetch_bodystructure(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { const char *bodystructure; if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &bodystructure) < 0) return -1; if (ctx->state.cur_first) ctx->state.cur_first = FALSE; else { if (o_stream_send(ctx->client->output, " ", 1) < 0) return -1; } if (o_stream_send(ctx->client->output, "BODYSTRUCTURE (", 15) < 0 || o_stream_send_str(ctx->client->output, bodystructure) < 0 || o_stream_send(ctx->client->output, ")", 1) < 0) return -1; return 1; } static bool fetch_bodystructure_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE; imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY" NIL NIL NIL NIL)", fetch_bodystructure, NULL); return TRUE; } static int fetch_envelope(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { const char *envelope; if (mail_get_special(mail, MAIL_FETCH_IMAP_ENVELOPE, &envelope) < 0) return -1; if (ctx->state.cur_first) ctx->state.cur_first = FALSE; else { if (o_stream_send(ctx->client->output, " ", 1) < 0) return -1; } if (o_stream_send(ctx->client->output, "ENVELOPE (", 10) < 0 || o_stream_send_str(ctx->client->output, envelope) < 0 || o_stream_send(ctx->client->output, ")", 1) < 0) return -1; return 1; } static bool fetch_envelope_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE; imap_fetch_add_handler(ctx, 0, ENVELOPE_NIL_REPLY, fetch_envelope, NULL); return TRUE; } static int fetch_flags(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { enum mail_flags flags; const char *const *keywords; flags = mail_get_flags(mail); if (ctx->flags_update_seen && (flags & MAIL_SEEN) == 0 && !mailbox_is_readonly(mail->box)) { /* Add \Seen flag */ ctx->state.seen_flags_changed = TRUE; flags |= MAIL_SEEN; mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN); } else if (ctx->flags_show_only_seen_changes) { return 1; } keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords, mail_get_keyword_indexes(mail)); str_append(ctx->state.cur_str, "FLAGS ("); imap_write_flags(ctx->state.cur_str, flags, keywords); str_append(ctx->state.cur_str, ") "); return 1; } bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->flags_have_handler = TRUE; ctx->fetch_ctx->fetch_data |= MAIL_FETCH_FLAGS; imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "()", fetch_flags, NULL); return TRUE; } static int fetch_internaldate(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { time_t date; if (mail_get_received_date(mail, &date) < 0) return -1; str_printfa(ctx->state.cur_str, "INTERNALDATE \"%s\" ", imap_to_datetime(date)); return 1; } static bool fetch_internaldate_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE; imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "\"01-Jan-1970 00:00:00 +0000\"", fetch_internaldate, NULL); return TRUE; } static int fetch_modseq(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { uint64_t modseq; modseq = mail_get_modseq(mail); if (ctx->client->highest_fetch_modseq < modseq) ctx->client->highest_fetch_modseq = modseq; str_printfa(ctx->state.cur_str, "MODSEQ (%llu) ", (unsigned long long)modseq); return 1; } bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx) { if (ctx->fetch_ctx->client->nonpermanent_modseqs) { ctx->error = "FETCH MODSEQ can't be used with non-permanent modseqs"; return FALSE; } (void)client_enable(ctx->fetch_ctx->client, MAILBOX_FEATURE_CONDSTORE); imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, NULL, fetch_modseq, NULL); return TRUE; } static int fetch_uid(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { str_printfa(ctx->state.cur_str, "UID %u ", mail->uid); return 1; } bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx) { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, NULL, fetch_uid, NULL); return TRUE; } static int fetch_guid(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { const char *value; if (mail_get_special(mail, MAIL_FETCH_GUID, &value) < 0) return -1; str_append(ctx->state.cur_str, "X-GUID "); imap_append_astring(ctx->state.cur_str, value); str_append_c(ctx->state.cur_str, ' '); return 1; } static bool fetch_guid_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_GUID; imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "", fetch_guid, NULL); return TRUE; } static int fetch_x_mailbox(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { const char *name; string_t *mutf7_name; if (mail_get_special(mail, MAIL_FETCH_MAILBOX_NAME, &name) < 0) { /* This can happen with virtual mailbox if the backend mail is expunged. */ return -1; } mutf7_name = t_str_new(strlen(name)*2); if (imap_utf8_to_utf7(name, mutf7_name) < 0) i_panic("FETCH: Mailbox name not UTF-8: %s", name); str_append(ctx->state.cur_str, "X-MAILBOX "); imap_append_astring(ctx->state.cur_str, str_c(mutf7_name)); str_append_c(ctx->state.cur_str, ' '); return 1; } static bool fetch_x_mailbox_init(struct imap_fetch_init_context *ctx) { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, NULL, fetch_x_mailbox, NULL); return TRUE; } static int fetch_x_real_uid(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { struct mail *real_mail; if (mail_get_backend_mail(mail, &real_mail) < 0) return -1; str_printfa(ctx->state.cur_str, "X-REAL-UID %u ", real_mail->uid); return 1; } static bool fetch_x_real_uid_init(struct imap_fetch_init_context *ctx) { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, NULL, fetch_x_real_uid, NULL); return TRUE; } static int fetch_x_savedate(struct imap_fetch_context *ctx, struct mail *mail, void *context ATTR_UNUSED) { time_t date; if (mail_get_save_date(mail, &date) < 0) return -1; str_printfa(ctx->state.cur_str, "X-SAVEDATE \"%s\" ", imap_to_datetime(date)); return 1; } static bool fetch_x_savedate_init(struct imap_fetch_init_context *ctx) { ctx->fetch_ctx->fetch_data |= MAIL_FETCH_SAVE_DATE; imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "\"01-Jan-1970 00:00:00 +0000\"", fetch_x_savedate, NULL); return TRUE; } static const struct imap_fetch_handler imap_fetch_default_handlers[] = { { "BINARY", imap_fetch_binary_init }, { "BODY", fetch_body_init }, { "BODYSTRUCTURE", fetch_bodystructure_init }, { "ENVELOPE", fetch_envelope_init }, { "FLAGS", imap_fetch_flags_init }, { "INTERNALDATE", fetch_internaldate_init }, { "MODSEQ", imap_fetch_modseq_init }, { "RFC822", imap_fetch_rfc822_init }, { "UID", imap_fetch_uid_init }, { "X-GUID", fetch_guid_init }, { "X-MAILBOX", fetch_x_mailbox_init }, { "X-REAL-UID", fetch_x_real_uid_init }, { "X-SAVEDATE", fetch_x_savedate_init } }; void imap_fetch_handlers_init(void) { i_array_init(&fetch_handlers, 32); imap_fetch_handlers_register(imap_fetch_default_handlers, N_ELEMENTS(imap_fetch_default_handlers)); } void imap_fetch_handlers_deinit(void) { array_free(&fetch_handlers); } dovecot-2.2.33.2/src/imap/cmd-check.c0000644000175000017500000000056613123174404014032 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_check(struct client_command_context *cmd) { if (!client_verify_open_mailbox(cmd)) return TRUE; return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FULL_READ | MAILBOX_SYNC_FLAG_FULL_WRITE, IMAP_SYNC_FLAG_SAFE, "OK Check completed."); } dovecot-2.2.33.2/src/imap/cmd-genurlauth.c0000644000175000017500000000237413123174404015132 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "imap-commands.h" #include "imap-quote.h" #include "imap-urlauth.h" bool cmd_genurlauth(struct client_command_context *cmd) { const struct imap_arg *args; string_t *response; int ret; if (cmd->client->urlauth_ctx == NULL) { client_send_command_error(cmd, "URLAUTH disabled."); return TRUE; } if (!client_read_args(cmd, 0, 0, &args)) return FALSE; response = t_str_new(1024); str_append(response, "* GENURLAUTH"); for (;;) { const char *url_rump, *mechanism, *url, *error; if (IMAP_ARG_IS_EOL(args)) break; if (!imap_arg_get_astring(args++, &url_rump) || !imap_arg_get_atom(args++, &mechanism)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } ret = imap_urlauth_generate(cmd->client->urlauth_ctx, mechanism, url_rump, &url, &error); if (ret <= 0) { if (ret < 0) client_send_internal_error(cmd); else client_send_command_error(cmd, error); return TRUE; } str_append_c(response, ' '); imap_append_astring(response, url); } client_send_line(cmd->client, str_c(response)); client_send_tagline(cmd, "OK GENURLAUTH completed."); return TRUE; } dovecot-2.2.33.2/src/imap/imap-state.c0000644000175000017500000006261513165463624014276 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "crc32.h" #include "numpack.h" #include "net.h" #include "ostream.h" #include "str.h" #include "str-sanitize.h" #include "imap-util.h" #include "mail-search-build.h" #include "mail-storage-private.h" #include "mailbox-recent-flags.h" #include "imap-client.h" #include "imap-fetch.h" #include "imap-search-args.h" #include "imap-state.h" enum imap_state_type_public { IMAP_STATE_TYPE_MAILBOX = 'B', IMAP_STATE_TYPE_ENABLED_FEATURES = 'F', IMAP_STATE_TYPE_SEARCHRES = '1', }; enum imap_state_type_internal { IMAP_STATE_TYPE_ID_LOGGED = 'I', IMAP_STATE_TYPE_TLS_COMPRESSION = 'C', }; enum imap_state_feature { IMAP_STATE_FEATURE_CONDSTORE = 'C', IMAP_STATE_FEATURE_QRESYNC = 'Q' }; struct mailbox_import_state { const char *vname; guid_128_t mailbox_guid; bool examined; uint32_t keywords_count, keywords_crc32, uids_crc32; uint32_t uidvalidity, uidnext, messages; uint64_t highest_modseq; ARRAY_TYPE(seq_range) recent_uids; }; static void export_seq_range(buffer_t *dest, const ARRAY_TYPE(seq_range) *range) { const struct seq_range *uids; unsigned int i, count; uint32_t next_uid; uids = array_get(range, &count); numpack_encode(dest, count); next_uid = 1; for (i = 0; i < count; i++) { i_assert(uids[i].seq1 >= next_uid); if (uids[i].seq1 == uids[i].seq2) { numpack_encode(dest, (uids[i].seq1 - next_uid) << 1); } else { numpack_encode(dest, 1 | ((uids[i].seq1 - next_uid) << 1)); numpack_encode(dest, uids[i].seq2 - uids[i].seq1 - 1); } next_uid = uids[i].seq2 + 1; } } static int import_seq_range(const unsigned char **data, const unsigned char *end, ARRAY_TYPE(seq_range) *range) { uint32_t i, count, next_uid, num, uid1, uid2; if (numpack_decode32(data, end, &count) < 0) return -1; next_uid = 1; for (i = 0; i < count; i++) { if (numpack_decode32(data, end, &num) < 0) return -1; uid1 = next_uid + (num >> 1); if ((num & 1) == 0) { uid2 = uid1; seq_range_array_add(range, uid1); } else { if (numpack_decode32(data, end, &num) < 0) return -1; uid2 = uid1 + num + 1; seq_range_array_add_range(range, uid1, uid2); } next_uid = uid2 + 1; } return 0; } int imap_state_export_internal(struct client *client, buffer_t *dest, const char **error_r) { /* the only IMAP command we allow running is IDLE or X-STATE */ if (client->command_queue_size > 1) { *error_r = "Multiple commands in progress"; return 0; } if (client->command_queue == NULL || strcasecmp(client->command_queue->name, "IDLE") != 0) { /* this would require saving the seq <-> uid mapping and restore it on import. quite a lot of trouble if messages have been expunged in the mean time. */ *error_r = "Non-IDLE connections not supported currently"; return 0; } return client->v.state_export(client, TRUE, dest, error_r); } int imap_state_export_external(struct client *client, buffer_t *dest, const char **error_r) { if (client->command_queue_size > 1) { *error_r = "Multiple commands in progress"; return 0; } i_assert(client->command_queue_size == 1); i_assert(strcmp(client->command_queue->name, "X-STATE") == 0); return client->v.state_export(client, FALSE, dest, error_r); } static int imap_state_import(struct client *client, bool internal, const unsigned char *data, size_t size, const char **error_r) { ssize_t ret; while (size > 0) { ret = client->v.state_import(client, internal, data, size, error_r); if (ret <= 0) { i_assert(*error_r != NULL); return ret < 0 ? -1 : 0; } i_assert((size_t)ret <= size); data += ret; size -= ret; } return 1; } int imap_state_import_internal(struct client *client, const unsigned char *data, size_t size, const char **error_r) { return imap_state_import(client, TRUE, data, size, error_r); } int imap_state_import_external(struct client *client, const unsigned char *data, size_t size, const char **error_r) { return imap_state_import(client, FALSE, data, size, error_r); } static int imap_state_export_mailbox_mails(buffer_t *dest, struct mailbox *box, const char **error_r) { struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mail *mail; ARRAY_TYPE(seq_range) recent_uids; uint32_t crc = 0; int ret = 1; search_args = mail_search_build_init(); mail_search_build_add_all(search_args); trans = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(trans, "unhibernate"); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); t_array_init(&recent_uids, 8); while (mailbox_search_next(search_ctx, &mail)) { crc = crc32_data_more(crc, &mail->uid, sizeof(mail->uid)); if ((mail_get_flags(mail) & MAIL_RECENT) != 0) seq_range_array_add(&recent_uids, mail->uid); } if (mailbox_search_deinit(&search_ctx) < 0) { *error_r = mailbox_get_last_internal_error(box, NULL); ret = -1; } (void)mailbox_transaction_commit(&trans); numpack_encode(dest, crc); export_seq_range(dest, &recent_uids); return ret; } static uint32_t mailbox_status_keywords_crc32(const struct mailbox_status *status) { const char *const *strp; uint32_t crc = 0; array_foreach(status->keywords, strp) crc = crc32_str(*strp); return crc; } static int imap_state_export_mailbox(buffer_t *dest, struct client *client, struct mailbox *box, const char **error_r) { struct mailbox_status status; struct mailbox_metadata metadata; const char *vname = mailbox_get_vname(box); enum mail_error mail_error; mailbox_get_open_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_HIGHESTMODSEQ | STATUS_KEYWORDS, &status); if (status.nonpermanent_modseqs) { *error_r = "Nonpermanent modseqs"; return 0; } if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { *error_r = mailbox_get_last_internal_error(box, &mail_error); /* if the selected mailbox can't have a GUID, fail silently */ return mail_error == MAIL_ERROR_NOTPOSSIBLE ? 0 : -1; } buffer_append_c(dest, IMAP_STATE_TYPE_MAILBOX); buffer_append(dest, vname, strlen(vname)+1); buffer_append(dest, metadata.guid, sizeof(metadata.guid)); buffer_append_c(dest, client->mailbox_examined ? 1 : 0); numpack_encode(dest, status.uidvalidity); numpack_encode(dest, status.uidnext); numpack_encode(dest, status.messages); if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 && !client->nonpermanent_modseqs) numpack_encode(dest, client->sync_last_full_modseq); else numpack_encode(dest, status.highest_modseq); /* keywords count + CRC32 should be enough to figure out if it needs to be resent */ numpack_encode(dest, array_count(status.keywords)); numpack_encode(dest, mailbox_status_keywords_crc32(&status)); /* we're now basically done, but just in case there's a bug add a checksum of the currently existing UIDs and verify it when importing. this also writes the list of recent UIDs. */ return imap_state_export_mailbox_mails(dest, box, error_r); } int imap_state_export_base(struct client *client, bool internal, buffer_t *dest, const char **error_r) { int ret; str_append(dest, "base\n"); if (array_is_created(&client->search_updates) && array_count(&client->search_updates) > 0) { /* these could be tricky */ *error_r = "CONTEXT=SEARCH updates not supported currently"; return 0; } if (client->notify_ctx != NULL) { /* FIXME: this really should be supported. also IDLE wouldn't be needed if NOTIFY allows sending EXPUNGEs to selected mailbox. */ *error_r = "NOTIFY not supported currently"; return 0; } if (client->mailbox != NULL) { ret = imap_state_export_mailbox(dest, client, client->mailbox, error_r); if (ret <= 0) return ret; } /* IMAP features */ if (client->enabled_features != 0) { i_assert((client->enabled_features & ~(MAILBOX_FEATURE_CONDSTORE | MAILBOX_FEATURE_QRESYNC)) == 0); buffer_append_c(dest, IMAP_STATE_TYPE_ENABLED_FEATURES); if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0) buffer_append_c(dest, IMAP_STATE_FEATURE_CONDSTORE); if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) buffer_append_c(dest, IMAP_STATE_FEATURE_QRESYNC); buffer_append_c(dest, '\0'); } if (internal) { if (client->id_logged) buffer_append_c(dest, IMAP_STATE_TYPE_ID_LOGGED); if (client->tls_compression) buffer_append_c(dest, IMAP_STATE_TYPE_TLS_COMPRESSION); } /* IMAP SEARCHRES extension */ if (array_is_created(&client->search_saved_uidset) && array_count(&client->search_saved_uidset) > 0) { buffer_append_c(dest, IMAP_STATE_TYPE_SEARCHRES); export_seq_range(dest, &client->search_saved_uidset); } return 1; } static int import_string(const unsigned char **data, const unsigned char *end, const char **str_r) { const unsigned char *p; p = memchr(*data, '\0', end - *data); if (p == NULL) return -1; *str_r = (const void *)*data; *data = p + 1; return 0; } static int import_send_expunges(struct client *client, const struct mailbox_import_state *state, unsigned int *expunge_count_r, const char **error_r) { struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mail *mail; uint32_t crc = 0, seq, expunged_uid; ARRAY_TYPE(seq_range) uids_filter, expunged_uids; ARRAY_TYPE(uint32_t) expunged_seqs; struct seq_range_iter iter; const uint32_t *seqs; unsigned int i, expunge_count, n = 0; string_t *str; int ret = 0; *expunge_count_r = 0; if (state->messages == 0) { /* the mailbox was empty originally - there couldn't be any pending expunges. */ return 0; } if (state->uidnext <= 1) { *error_r = "Invalid UIDNEXT"; return -1; } /* get all the message UIDs expunged since the last known modseq */ t_array_init(&uids_filter, 1); t_array_init(&expunged_uids, 128); seq_range_array_add_range(&uids_filter, 1, state->uidnext-1); if (!mailbox_get_expunged_uids(client->mailbox, state->highest_modseq, &uids_filter, &expunged_uids)) { *error_r = t_strdup_printf( "Couldn't get recently expunged UIDs " "(uidnext=%u highest_modseq=%llu)", state->uidnext, (unsigned long long)state->highest_modseq); return -1; } seq_range_array_iter_init(&iter, &expunged_uids); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); trans = mailbox_transaction_begin(client->mailbox, 0); mailbox_transaction_set_reason(trans, "unhibernate"); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); /* find sequence numbers for the expunged UIDs */ t_array_init(&expunged_seqs, array_count(&expunged_uids)+1); seq = 0; while (mailbox_search_next(search_ctx, &mail)) { while (seq_range_array_iter_nth(&iter, n, &expunged_uid) && expunged_uid < mail->uid && seq < state->messages) { seq++; n++; array_append(&expunged_seqs, &seq, 1); crc = crc32_data_more(crc, &expunged_uid, sizeof(expunged_uid)); } if (seq == state->messages) break; crc = crc32_data_more(crc, &mail->uid, sizeof(mail->uid)); if (++seq == state->messages) break; } while (seq_range_array_iter_nth(&iter, n, &expunged_uid) && seq < state->messages) { seq++; n++; array_append(&expunged_seqs, &seq, 1); crc = crc32_data_more(crc, &expunged_uid, sizeof(expunged_uid)); } if (mailbox_search_deinit(&search_ctx) < 0) { *error_r = mailbox_get_last_internal_error(client->mailbox, NULL); ret = -1; } else if (seq != state->messages) { *error_r = t_strdup_printf("Message count mismatch after " "handling expunges (%u != %u)", seq, state->messages); ret = -1; } (void)mailbox_transaction_commit(&trans); if (ret < 0) return -1; seqs = array_get(&expunged_seqs, &expunge_count); if (client->messages_count + expunge_count < state->messages) { *error_r = t_strdup_printf("Message count too low after " "handling expunges (%u < %u)", client->messages_count + expunge_count, state->messages); return -1; } if (crc != state->uids_crc32) { *error_r = t_strdup_printf("Message UIDs CRC32 mismatch (%u != %u)", crc, state->uids_crc32); return -1; } if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) { str = t_str_new(32); for (i = expunge_count; i > 0; i--) { str_truncate(str, 0); str_printfa(str, "* %u EXPUNGE", seqs[i-1]); client_send_line(client, str_c(str)); } } else { str = str_new(default_pool, 128); str_append(str, "* VANISHED "); imap_write_seq_range(str, &expunged_uids); str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); str_free(&str); } *expunge_count_r = expunge_count; return 0; } static int import_send_flag_changes(struct client *client, const struct mailbox_import_state *state) { struct imap_fetch_context *fetch_ctx; struct mail_search_args *search_args; ARRAY_TYPE(seq_range) old_uids; pool_t pool; int ret; if (state->messages == 0) return 0; t_array_init(&old_uids, 1); seq_range_array_add_range(&old_uids, 1, state->uidnext-1); search_args = mail_search_build_init(); search_args->args = p_new(search_args->pool, struct mail_search_arg, 1); search_args->args->type = SEARCH_UIDSET; search_args->args->value.seqset = old_uids; imap_search_add_changed_since(search_args, state->highest_modseq); pool = pool_alloconly_create("imap state flag changes", 1024); fetch_ctx = imap_fetch_alloc(client, pool, "unhibernate"); pool_unref(&pool); imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init); if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) { imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init); imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init); } imap_fetch_begin(fetch_ctx, client->mailbox, search_args); mail_search_args_unref(&search_args); /* FIXME: ideally do this asynchronously.. */ while (imap_fetch_more_no_lock_update(fetch_ctx) == 0) ; ret = imap_fetch_end(fetch_ctx); imap_fetch_free(&fetch_ctx); return ret; } static ssize_t import_state_mailbox_struct(const unsigned char *data, size_t size, struct mailbox_import_state *state_r, const char **error_r) { const unsigned char *p = data, *end = data + size; i_zero(state_r); t_array_init(&state_r->recent_uids, 8); /* vname */ if (import_string(&p, end, &state_r->vname) < 0) { *error_r = "Mailbox state truncated at name"; return 0; } /* GUID */ if (end-p < (int)sizeof(state_r->mailbox_guid)) { *error_r = "Mailbox state truncated at GUID"; return 0; } memcpy(state_r->mailbox_guid, p, sizeof(state_r->mailbox_guid)); p += sizeof(state_r->mailbox_guid); if (guid_128_is_empty(state_r->mailbox_guid)) { *error_r = "Empty GUID"; return 0; } /* EXAMINEd vs SELECTed */ if (p == end) { *error_r = "Mailbox state truncated at examined-flag"; return 0; } state_r->examined = p[0] != 0; p++; /* mailbox state */ if (numpack_decode32(&p, end, &state_r->uidvalidity) < 0 || numpack_decode32(&p, end, &state_r->uidnext) < 0 || numpack_decode32(&p, end, &state_r->messages) < 0 || numpack_decode(&p, end, &state_r->highest_modseq) < 0 || numpack_decode32(&p, end, &state_r->keywords_count) < 0 || numpack_decode32(&p, end, &state_r->keywords_crc32) < 0 || numpack_decode32(&p, end, &state_r->uids_crc32) < 0 || import_seq_range(&p, end, &state_r->recent_uids) < 0) { *error_r = "Mailbox state truncated"; return 0; } if (state_r->uidvalidity == 0) { *error_r = "Empty UIDVALIDITY"; return 0; } if (state_r->uidnext == 0) { *error_r = "Empty UIDNEXT"; return 0; } return p - data; } static int import_state_mailbox_open(struct client *client, const struct mailbox_import_state *state, const char **error_r) { struct mail_namespace *ns; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_status status; const struct seq_range *range; enum mailbox_flags flags = 0; unsigned int expunge_count; uint32_t uid; int ret = 0; ns = mail_namespace_find(client->user->namespaces, state->vname); if (ns == NULL) { *error_r = "Namespace not found for mailbox"; return -1; } if (state->examined) flags |= MAILBOX_FLAG_READONLY; else flags |= MAILBOX_FLAG_DROP_RECENT; box = mailbox_alloc(ns->list, state->vname, flags); mailbox_set_reason(box, "unhibernate"); if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("Couldn't open mailbox: %s", mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return -1; } if (client->enabled_features != 0) ret = mailbox_enable(box, client->enabled_features); if (ret < 0 || mailbox_sync(box, 0) < 0) { *error_r = t_strdup_printf("Couldn't sync mailbox: %s", mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return -1; } /* verify that this still looks like the same mailbox */ if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { *error_r = mailbox_get_last_internal_error(box, NULL); mailbox_free(&box); return -1; } if (!guid_128_equals(metadata.guid, state->mailbox_guid)) { *error_r = t_strdup_printf("Mailbox GUID has changed %s->%s", guid_128_to_string(state->mailbox_guid), guid_128_to_string(metadata.guid)); mailbox_free(&box); return -1; } mailbox_get_open_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ | STATUS_RECENT | STATUS_KEYWORDS, &status); if (status.uidvalidity != state->uidvalidity) { *error_r = t_strdup_printf("Mailbox UIDVALIDITY has changed %u->%u", state->uidvalidity, status.uidvalidity); mailbox_free(&box); return -1; } if (status.uidnext < state->uidnext) { *error_r = t_strdup_printf("Mailbox UIDNEXT shrank %u -> %u", state->uidnext, status.uidnext); mailbox_free(&box); return -1; } if (status.highest_modseq < state->highest_modseq) { *error_r = t_strdup_printf("Mailbox HIGHESTMODSEQ shrank %llu -> %llu", (unsigned long long)state->highest_modseq, (unsigned long long)status.highest_modseq); mailbox_free(&box); return -1; } client->mailbox = box; client->mailbox_examined = state->examined; client->messages_count = status.messages; client->uidvalidity = status.uidvalidity; client->notify_uidnext = status.uidnext; if (import_send_expunges(client, state, &expunge_count, error_r) < 0) return -1; i_assert(expunge_count <= state->messages); if (state->messages - expunge_count > client->messages_count) { *error_r = t_strdup_printf("Mailbox message count shrank %u -> %u", client->messages_count, state->messages - expunge_count); return -1; } client_update_mailbox_flags(client, status.keywords); array_foreach(&state->recent_uids, range) { for (uid = range->seq1; uid <= range->seq2; uid++) { uint32_t seq; if (mail_index_lookup_seq(box->view, uid, &seq)) mailbox_recent_flags_set_uid_forced(box, uid); } } client->recent_count = mailbox_recent_flags_count(box); if (state->messages - expunge_count < client->messages_count) { /* new messages arrived */ client_send_line(client, t_strdup_printf("* %u EXISTS", client->messages_count)); client_send_line(client, t_strdup_printf("* %u RECENT", client->recent_count)); } if (array_count(status.keywords) == state->keywords_count && mailbox_status_keywords_crc32(&status) == state->keywords_crc32) { /* no changes to keywords */ client->keywords.announce_count = state->keywords_count; } else { client_send_mailbox_flags(client, TRUE); } if (import_send_flag_changes(client, state) < 0) { *error_r = "Couldn't send flag changes"; return -1; } if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 && !client->nonpermanent_modseqs && status.highest_modseq != state->highest_modseq) { client_send_line(client, t_strdup_printf( "* OK [HIGHESTMODSEQ %llu] Highest", (unsigned long long)status.highest_modseq)); client->sync_last_full_modseq = status.highest_modseq; } return 0; } static ssize_t import_state_mailbox(struct client *client, const unsigned char *data, size_t size, const char **error_r) { struct mailbox_import_state state; ssize_t ret; if (client->mailbox != NULL) { *error_r = "Duplicate mailbox state"; return 0; } ret = import_state_mailbox_struct(data, size, &state, error_r); if (ret <= 0) { i_assert(*error_r != NULL); return ret; } if (import_state_mailbox_open(client, &state, error_r) < 0) { *error_r = t_strdup_printf("Mailbox %s: %s", state.vname, *error_r); return -1; } return ret; } static ssize_t import_state_enabled_features(struct client *client, const unsigned char *data, size_t size, const char **error_r) { enum imap_state_feature feature; size_t i = 0; for (i = 0; i < size; i++) { if (data[i] == '\0') return i+1; feature = data[i]; switch (feature) { case IMAP_STATE_FEATURE_CONDSTORE: client->enabled_features |= MAILBOX_FEATURE_CONDSTORE; break; case IMAP_STATE_FEATURE_QRESYNC: client->enabled_features |= MAILBOX_FEATURE_QRESYNC; break; default: *error_r = t_strdup_printf( "Unknown feature '%c'", feature); return 0; } } *error_r = "Non-terminated features list"; return 0; } static ssize_t import_state_searchres(struct client *client, const unsigned char *data, size_t size, const char **error_r) { const unsigned char *p = data; i_array_init(&client->search_saved_uidset, 128); if (import_seq_range(&p, data+size, &client->search_saved_uidset) < 0) { *error_r = "Invalid SEARCHRES seq-range"; return 0; } return p - data; } static ssize_t import_state_id_logged(struct client *client, const unsigned char *data ATTR_UNUSED, size_t size ATTR_UNUSED, const char **error_r ATTR_UNUSED) { client->id_logged = TRUE; return 0; } static ssize_t import_state_tls_compression(struct client *client, const unsigned char *data ATTR_UNUSED, size_t size ATTR_UNUSED, const char **error_r ATTR_UNUSED) { client->tls_compression = TRUE; return 0; } void imap_state_import_idle_cmd_tag(struct client *client, const char *tag) { if (client->state_import_idle_continue) { /* IDLE command continues */ struct client_command_context *cmd; struct command *command; cmd = client_command_alloc(client); cmd->tag = p_strdup(cmd->pool, tag); cmd->name = "IDLE"; command = command_find("IDLE"); i_assert(command != NULL); cmd->func = command->func; cmd->cmd_flags = command->flags; if (command_exec(cmd)) { /* IDLE terminated because of an external change, but DONE was already buffered */ client_command_free(&cmd); client_add_missing_io(client); } else { i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT || cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT); } } else { /* we're finishing IDLE command */ client_send_line(client, t_strdup_printf( "%s %s Idle completed.", tag, client->state_import_bad_idle_done ? "BAD" : "OK")); } } static struct { enum imap_state_type_public type; ssize_t (*import)(struct client *client, const unsigned char *data, size_t size, const char **error_r); } imap_states_public[] = { { IMAP_STATE_TYPE_MAILBOX, import_state_mailbox }, { IMAP_STATE_TYPE_ENABLED_FEATURES, import_state_enabled_features }, { IMAP_STATE_TYPE_SEARCHRES, import_state_searchres } }; static struct { enum imap_state_type_internal type; ssize_t (*import)(struct client *client, const unsigned char *data, size_t size, const char **error_r); } imap_states_internal[] = { { IMAP_STATE_TYPE_ID_LOGGED, import_state_id_logged }, { IMAP_STATE_TYPE_TLS_COMPRESSION, import_state_tls_compression } }; static ssize_t imap_state_try_import_public(struct client *client, const unsigned char *data, size_t size, const char **error_r) { unsigned int i; ssize_t ret; i_assert(size > 0); for (i = 0; i < N_ELEMENTS(imap_states_public); i++) { if (imap_states_public[i].type == data[0]) { ret = imap_states_public[i]. import(client, data+1, size-1, error_r); return ret < 0 ? -1 : ret+1; } } return -2; } static ssize_t imap_state_try_import_internal(struct client *client, const unsigned char *data, size_t size, const char **error_r) { unsigned int i; ssize_t ret; i_assert(size > 0); for (i = 0; i < N_ELEMENTS(imap_states_internal); i++) { if (imap_states_internal[i].type == data[0]) { ret = imap_states_internal[i]. import(client, data+1, size-1, error_r); return ret < 0 ? -1 : ret+1; } } return -2; } ssize_t imap_state_import_base(struct client *client, bool internal, const unsigned char *data, size_t size, const char **error_r) { const unsigned char *p; ssize_t ret; size_t pos; i_assert(client->mailbox == NULL); *error_r = NULL; if (size < 5 || memcmp(data, "base\n", 5) != 0) { p = memchr(data, '\n', size); if (p == NULL) p = data + I_MIN(size, 20); *error_r = t_strdup_printf("Unknown state block '%s'", str_sanitize(t_strdup_until(data, p), 20)); return 0; } pos = 5; while (pos < size) { ret = imap_state_try_import_public(client, data+pos, size-pos, error_r); if (ret == -2 && internal) { ret = imap_state_try_import_internal(client, data+pos, size-pos, error_r); } if (ret < 0 || *error_r != NULL) { if (ret == -2) { *error_r = t_strdup_printf("Unknown type '%c'", data[pos]); } i_assert(*error_r != NULL); return ret < 0 ? -1 : 0; } i_assert(size - pos >= (size_t)ret); pos += ret; } return pos; } dovecot-2.2.33.2/src/imap/imap-settings.h0000644000175000017500000000215313165463624015012 00000000000000#ifndef IMAP_SETTINGS_H #define IMAP_SETTINGS_H #include "net.h" struct mail_user_settings; /* */ enum imap_client_workarounds { WORKAROUND_DELAY_NEWMAIL = 0x01, WORKAROUND_TB_EXTRA_MAILBOX_SEP = 0x08, WORKAROUND_TB_LSUB_FLAGS = 0x10 }; enum imap_client_fetch_failure { IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY, IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER, IMAP_CLIENT_FETCH_FAILURE_NO_AFTER, }; /* */ struct imap_settings { bool verbose_proctitle; const char *rawlog_dir; /* imap: */ uoff_t imap_max_line_length; unsigned int imap_idle_notify_interval; const char *imap_capability; const char *imap_client_workarounds; const char *imap_logout_format; const char *imap_id_send; const char *imap_id_log; const char *imap_fetch_failure; bool imap_metadata; unsigned int imap_hibernate_timeout; /* imap urlauth: */ const char *imap_urlauth_host; in_port_t imap_urlauth_port; enum imap_client_workarounds parsed_workarounds; enum imap_client_fetch_failure parsed_fetch_failure; }; extern const struct setting_parser_info imap_setting_parser_info; #endif dovecot-2.2.33.2/src/imap/cmd-capability.c0000644000175000017500000000060413123174404015067 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" #include "str.h" bool cmd_capability(struct client_command_context *cmd) { client_send_line(cmd->client, t_strconcat( "* CAPABILITY ", str_c(cmd->client->capability_string), NULL)); client_send_tagline(cmd, "OK Capability completed."); return TRUE; } dovecot-2.2.33.2/src/imap/imap-sync.c0000644000175000017500000005611013165463624014123 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "mail-user.h" #include "mail-storage.h" #include "mail-search-build.h" #include "imap-quote.h" #include "imap-util.h" #include "imap-fetch.h" #include "imap-notify.h" #include "imap-commands.h" #include "imap-sync-private.h" static void uids_to_seqs(struct mailbox *box, ARRAY_TYPE(seq_range) *uids) { T_BEGIN { ARRAY_TYPE(seq_range) seqs; const struct seq_range *range; uint32_t seq1, seq2; t_array_init(&seqs, array_count(uids)); array_foreach(uids, range) { mailbox_get_seq_range(box, range->seq1, range->seq2, &seq1, &seq2); /* since we have to notify about expunged messages, we expect that all the referenced UIDs exist */ i_assert(seq1 != 0); i_assert(seq2 - seq1 == range->seq2 - range->seq1); seq_range_array_add_range(&seqs, seq1, seq2); } /* replace uids with seqs */ array_clear(uids); array_append_array(uids, &seqs); } T_END; } static int search_update_fetch_more(const struct imap_search_update *update) { int ret; if ((ret = imap_fetch_more_no_lock_update(update->fetch_ctx)) <= 0) return ret; /* finished the FETCH */ if (imap_fetch_end(update->fetch_ctx) < 0) return -1; return 1; } static int imap_sync_send_fetch_to_search_update(struct imap_sync_context *ctx, const struct imap_search_update *update) { struct mail_search_args *search_args; struct mail_search_arg *arg; ARRAY_TYPE(seq_range) seqs; if (ctx->search_update_notifying) return search_update_fetch_more(update); i_assert(!update->fetch_ctx->state.fetching); if (array_count(&ctx->search_adds) == 0 || !ctx->have_new_mails) return 1; search_args = mail_search_build_init(); arg = mail_search_build_add(search_args, SEARCH_UIDSET); p_array_init(&arg->value.seqset, search_args->pool, 1); /* find the newly appended messages: ctx->messages_count is the message count before new messages found by sync, client->messages_count is the number of messages after. */ t_array_init(&seqs, 1); seq_range_array_add_range(&seqs, ctx->messages_count+1, ctx->client->messages_count); mailbox_get_uid_range(ctx->client->mailbox, &seqs, &arg->value.seqset); /* remove messages not in the search_adds list */ seq_range_array_intersect(&arg->value.seqset, &ctx->search_adds); imap_fetch_begin(update->fetch_ctx, ctx->client->mailbox, search_args); mail_search_args_unref(&search_args); return search_update_fetch_more(update); } static int imap_sync_send_search_update(struct imap_sync_context *ctx, const struct imap_search_update *update, bool removes_only) { string_t *cmd; int ret = 1; if (!ctx->search_update_notifying) { mailbox_search_result_sync(update->result, &ctx->search_removes, &ctx->search_adds); } if (array_count(&ctx->search_adds) == 0 && array_count(&ctx->search_removes) == 0) return 1; i_assert(array_count(&ctx->search_adds) == 0 || !removes_only); if (update->fetch_ctx != NULL) { ret = imap_sync_send_fetch_to_search_update(ctx, update); if (ret == 0) { ctx->search_update_notifying = TRUE; return 0; } } ctx->search_update_notifying = FALSE; cmd = t_str_new(256); str_append(cmd, "* ESEARCH (TAG "); imap_append_string(cmd, update->tag); str_append_c(cmd, ')'); if (update->return_uids) str_append(cmd, " UID"); else { /* convert to sequences */ uids_to_seqs(ctx->client->mailbox, &ctx->search_removes); uids_to_seqs(ctx->client->mailbox, &ctx->search_adds); } if (array_count(&ctx->search_removes) != 0) { str_printfa(cmd, " REMOVEFROM (0 "); imap_write_seq_range(cmd, &ctx->search_removes); str_append_c(cmd, ')'); } if (array_count(&ctx->search_adds) != 0) { str_printfa(cmd, " ADDTO (0 "); imap_write_seq_range(cmd, &ctx->search_adds); str_append_c(cmd, ')'); } str_append(cmd, "\r\n"); o_stream_nsend(ctx->client->output, str_data(cmd), str_len(cmd)); return ret; } static int imap_sync_send_search_updates(struct imap_sync_context *ctx, bool removes_only) { const struct imap_search_update *updates; unsigned int i, count; int ret = 1; if (!array_is_created(&ctx->client->search_updates)) return 1; if (!array_is_created(&ctx->search_removes)) { i_array_init(&ctx->search_removes, 64); i_array_init(&ctx->search_adds, 128); } updates = array_get(&ctx->client->search_updates, &count); for (i = ctx->search_update_idx; i < count; i++) { T_BEGIN { ret = imap_sync_send_search_update(ctx, &updates[i], removes_only); } T_END; if (ret <= 0) break; } ctx->search_update_idx = i; return ret; } struct imap_sync_context * imap_sync_init(struct client *client, struct mailbox *box, enum imap_sync_flags imap_flags, enum mailbox_sync_flags flags) { struct imap_sync_context *ctx; i_assert(client->mailbox == box); if (client->notify_immediate_expunges) { /* NOTIFY enabled without SELECTED-DELAYED */ flags &= ~MAILBOX_SYNC_FLAG_NO_EXPUNGES; } ctx = i_new(struct imap_sync_context, 1); ctx->client = client; ctx->box = box; ctx->imap_flags = imap_flags; i_array_init(&ctx->module_contexts, 5); /* make sure user can't DoS the system by causing Dovecot to create tons of useless namespaces. */ mail_user_drop_useless_namespaces(client->user); ctx->sync_ctx = mailbox_sync_init(box, flags); ctx->t = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(ctx->t, "Mailbox sync"); ctx->mail = mail_alloc(ctx->t, MAIL_FETCH_FLAGS, NULL); ctx->messages_count = client->messages_count; i_array_init(&ctx->tmp_keywords, client->keywords.announce_count + 8); if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) { i_array_init(&ctx->expunges, 128); /* always send UIDs in FETCH replies */ ctx->imap_flags |= IMAP_SYNC_FLAG_SEND_UID; } client_send_mailbox_flags(client, FALSE); /* send search updates the first time after sync is initialized. it now contains expunged messages that must be sent before EXPUNGE replies. */ if (imap_sync_send_search_updates(ctx, TRUE) == 0) i_unreached(); ctx->search_update_idx = 0; return ctx; } static void imap_sync_send_highestmodseq(struct imap_sync_context *ctx, struct client_command_context *sync_cmd) { struct client *client = ctx->client; uint64_t send_modseq = 0; if (ctx->sync_status.sync_delayed_expunges && client->highest_fetch_modseq > client->sync_last_full_modseq) { /* if client updates highest-modseq using returned MODSEQs it loses expunges. try to avoid this by sending it a lower pre-expunge HIGHESTMODSEQ reply. */ send_modseq = client->sync_last_full_modseq; } else if (!ctx->sync_status.sync_delayed_expunges && ctx->status.highest_modseq > client->sync_last_full_modseq && ctx->status.highest_modseq > client->highest_fetch_modseq) { /* we've probably sent some VANISHED or EXISTS replies which increased the highest-modseq. notify the client about this. */ send_modseq = ctx->status.highest_modseq; } if (send_modseq == 0) { /* no sending */ } else if (sync_cmd->sync != NULL && /* IDLE doesn't have ->sync */ sync_cmd->sync->tagline != NULL && /* NOTIFY doesn't have tagline */ strncmp(sync_cmd->sync->tagline, "OK ", 3) == 0 && sync_cmd->sync->tagline[3] != '[') { /* modify the tagged reply directly */ sync_cmd->sync->tagline = p_strdup_printf(sync_cmd->pool, "OK [HIGHESTMODSEQ %llu] %s", (unsigned long long)send_modseq, sync_cmd->sync->tagline + 3); } else { /* send an untagged OK reply */ client_send_line(client, t_strdup_printf( "* OK [HIGHESTMODSEQ %llu] Highest", (unsigned long long)send_modseq)); } if (!ctx->sync_status.sync_delayed_expunges) { /* no delayed expunges, remember this for future */ client->sync_last_full_modseq = ctx->status.highest_modseq; } client->highest_fetch_modseq = 0; } static int imap_sync_finish(struct imap_sync_context *ctx, bool aborting) { struct client *client = ctx->client; int ret = ctx->failed ? -1 : 0; if (ctx->finished) return ret; ctx->finished = TRUE; mail_free(&ctx->mail); /* the transaction is used only for fetching modseqs/flags. it can't really fail.. */ (void)mailbox_transaction_commit(&ctx->t); if (array_is_created(&ctx->expunges)) array_free(&ctx->expunges); if (mailbox_sync_deinit(&ctx->sync_ctx, &ctx->sync_status) < 0 || ctx->failed) { ctx->failed = TRUE; ret = -1; } mailbox_get_open_status(ctx->box, STATUS_UIDVALIDITY | STATUS_MESSAGES | STATUS_RECENT | STATUS_HIGHESTMODSEQ, &ctx->status); if (ctx->status.uidvalidity != client->uidvalidity) { /* most clients would get confused by this. disconnect them. */ client_disconnect_with_error(client, "Mailbox UIDVALIDITY changed"); } if (mailbox_is_inconsistent(ctx->box)) { client_disconnect_with_error(client, "IMAP session state is inconsistent, please relogin."); /* we can't trust status information anymore, so don't try to sync message counts. */ return -1; } if (!ctx->no_newmail && !aborting) { if (ctx->status.messages < ctx->messages_count) i_panic("Message count decreased"); if (ctx->status.messages != ctx->messages_count && client->notify_count_changes) { client_send_line(client, t_strdup_printf("* %u EXISTS", ctx->status.messages)); ctx->have_new_mails = TRUE; } if (ctx->status.recent != client->recent_count && client->notify_count_changes) { client_send_line(client, t_strdup_printf("* %u RECENT", ctx->status.recent)); } client->messages_count = ctx->status.messages; client->recent_count = ctx->status.recent; } return ret; } static int imap_sync_notify_more(struct imap_sync_context *ctx) { int ret = 1; if (ctx->have_new_mails && ctx->client->notify_ctx != NULL) { /* send FETCH replies for the new mails */ if ((ret = imap_client_notify_newmails(ctx->client)) == 0) return 0; if (ret < 0) ctx->failed = TRUE; } /* send search updates the second time after syncing in done. now it contains added/removed messages. */ if ((ret = imap_sync_send_search_updates(ctx, FALSE)) < 0) ctx->failed = TRUE; if (ret > 0) ret = ctx->client->v.sync_notify_more(ctx); return ret; } int imap_sync_deinit(struct imap_sync_context *ctx, struct client_command_context *sync_cmd) { int ret; ret = imap_sync_finish(ctx, TRUE); imap_client_notify_finished(ctx->client); if ((ctx->client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 && !ctx->client->nonpermanent_modseqs) imap_sync_send_highestmodseq(ctx, sync_cmd); if (array_is_created(&ctx->search_removes)) { array_free(&ctx->search_removes); array_free(&ctx->search_adds); } array_free(&ctx->tmp_keywords); array_free(&ctx->module_contexts); i_free(ctx); return ret; } static void imap_sync_add_modseq(struct imap_sync_context *ctx, string_t *str) { uint64_t modseq; modseq = mail_get_modseq(ctx->mail); if (ctx->client->highest_fetch_modseq < modseq) ctx->client->highest_fetch_modseq = modseq; str_printfa(str, "MODSEQ (%llu)", (unsigned long long)modseq); } static int imap_sync_send_flags(struct imap_sync_context *ctx, string_t *str) { enum mail_flags flags; const char *const *keywords; mail_set_seq(ctx->mail, ctx->seq); flags = mail_get_flags(ctx->mail); keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords, mail_get_keyword_indexes(ctx->mail)); if ((flags & MAIL_DELETED) != 0) ctx->client->sync_seen_deletes = TRUE; str_truncate(str, 0); str_printfa(str, "* %u FETCH (", ctx->seq); if (ctx->imap_flags & IMAP_SYNC_FLAG_SEND_UID) str_printfa(str, "UID %u ", ctx->mail->uid); if ((ctx->client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0 && !ctx->client->nonpermanent_modseqs) { imap_sync_add_modseq(ctx, str); str_append_c(str, ' '); } str_append(str, "FLAGS ("); imap_write_flags(str, flags, keywords); str_append(str, "))"); return client_send_line_next(ctx->client, str_c(str)); } static int imap_sync_send_modseq(struct imap_sync_context *ctx, string_t *str) { mail_set_seq(ctx->mail, ctx->seq); str_truncate(str, 0); str_printfa(str, "* %u FETCH (", ctx->seq); if (ctx->imap_flags & IMAP_SYNC_FLAG_SEND_UID) str_printfa(str, "UID %u ", ctx->mail->uid); imap_sync_add_modseq(ctx, str); str_append_c(str, ')'); return client_send_line_next(ctx->client, str_c(str)); } static void imap_sync_vanished(struct imap_sync_context *ctx) { const struct seq_range *seqs; unsigned int i, count; string_t *line; uint32_t seq, prev_uid, start_uid; bool comma = FALSE; /* Convert expunge sequences to UIDs and send them in VANISHED line. */ seqs = array_get(&ctx->expunges, &count); if (count == 0) return; line = t_str_new(256); str_append(line, "* VANISHED "); for (i = 0; i < count; i++) { start_uid = 0; prev_uid = (uint32_t)-1; for (seq = seqs[i].seq1; seq <= seqs[i].seq2; seq++) { mail_set_seq(ctx->mail, seq); if (prev_uid + 1 != ctx->mail->uid) { if (start_uid != 0) { if (!comma) comma = TRUE; else str_append_c(line, ','); str_printfa(line, "%u", start_uid); if (start_uid != prev_uid) { str_printfa(line, ":%u", prev_uid); } } start_uid = ctx->mail->uid; } prev_uid = ctx->mail->uid; } if (!comma) comma = TRUE; else str_append_c(line, ','); str_printfa(line, "%u", start_uid); if (start_uid != prev_uid) str_printfa(line, ":%u", prev_uid); } str_append(line, "\r\n"); o_stream_nsend(ctx->client->output, str_data(line), str_len(line)); } static int imap_sync_send_expunges(struct imap_sync_context *ctx, string_t *str) { int ret = 1; if (!ctx->client->notify_count_changes) { /* NOTIFY: MessageEvent not specified for selected mailbox */ return 1; } if (array_is_created(&ctx->expunges)) { /* Use a single VANISHED line */ seq_range_array_add_range(&ctx->expunges, ctx->sync_rec.seq1, ctx->sync_rec.seq2); return 1; } if (ctx->seq == 0) ctx->seq = ctx->sync_rec.seq2; for (; ctx->seq >= ctx->sync_rec.seq1; ctx->seq--) { if (ret == 0) { /* buffer full, continue later */ return 0; } str_truncate(str, 0); str_printfa(str, "* %u EXPUNGE", ctx->seq); ret = client_send_line_next(ctx->client, str_c(str)); } return 1; } int imap_sync_more(struct imap_sync_context *ctx) { string_t *str; int ret = 1; if (ctx->finished) return imap_sync_notify_more(ctx); /* finish syncing even when client has disconnected. otherwise our internal state (ctx->messages_count) can get messed up and unless we immediately stop handling all commands and syncs we could end up assert-crashing. */ str = t_str_new(256); for (;;) { if (ctx->seq == 0) { /* get next one */ if (!mailbox_sync_next(ctx->sync_ctx, &ctx->sync_rec)) { /* finished */ ret = 1; break; } } if (ctx->sync_rec.seq2 > ctx->messages_count) { /* don't send change notifications of messages we haven't even announced to client yet */ if (ctx->sync_rec.seq1 > ctx->messages_count) { ctx->seq = 0; continue; } ctx->sync_rec.seq2 = ctx->messages_count; } /* EXPUNGEs must come last */ i_assert(!array_is_created(&ctx->expunges) || array_count(&ctx->expunges) == 0 || ctx->sync_rec.type == MAILBOX_SYNC_TYPE_EXPUNGE); switch (ctx->sync_rec.type) { case MAILBOX_SYNC_TYPE_FLAGS: if (!ctx->client->notify_flag_changes) { /* NOTIFY: FlagChange not specified for selected mailbox */ break; } if (ctx->seq == 0) ctx->seq = ctx->sync_rec.seq1; ret = 1; for (; ctx->seq <= ctx->sync_rec.seq2; ctx->seq++) { if (ret == 0) break; ret = imap_sync_send_flags(ctx, str); } break; case MAILBOX_SYNC_TYPE_EXPUNGE: ret = imap_sync_send_expunges(ctx, str); if (ret > 0) { /* update only after we're finished, so that the seq2 > messages_count check above doesn't break */ ctx->messages_count -= ctx->sync_rec.seq2 - ctx->sync_rec.seq1 + 1; } break; case MAILBOX_SYNC_TYPE_MODSEQ: if ((ctx->client->enabled_features & MAILBOX_FEATURE_CONDSTORE) == 0) break; if (!ctx->client->notify_flag_changes) { /* NOTIFY: FlagChange not specified for selected mailbox. The RFC doesn't explicitly specify MODSEQ changes, but they're close enough to flag changes. */ break; } if (ctx->seq == 0) ctx->seq = ctx->sync_rec.seq1; ret = 1; for (; ctx->seq <= ctx->sync_rec.seq2; ctx->seq++) { if (ret == 0) break; ret = imap_sync_send_modseq(ctx, str); } break; } if (ret == 0) { /* buffer full */ break; } ctx->seq = 0; } if (ret > 0) { if (array_is_created(&ctx->expunges)) imap_sync_vanished(ctx); if (imap_sync_finish(ctx, FALSE) < 0) return -1; return imap_sync_more(ctx); } return ret; } bool imap_sync_is_allowed(struct client *client) { if (client->syncing) return FALSE; if (client->mailbox != NULL && mailbox_transaction_get_count(client->mailbox) > 0) return FALSE; return TRUE; } static bool cmd_finish_sync(struct client_command_context *cmd) { if (cmd->sync->tagline != NULL) client_send_tagline(cmd, cmd->sync->tagline); return TRUE; } static bool cmd_sync_continue(struct client_command_context *sync_cmd) { struct client_command_context *cmd, *prev; struct client *client = sync_cmd->client; struct imap_sync_context *ctx = sync_cmd->context; int ret; i_assert(ctx->client == client); if ((ret = imap_sync_more(ctx)) == 0) return FALSE; if (ret < 0) ctx->failed = TRUE; client->syncing = FALSE; if (imap_sync_deinit(ctx, sync_cmd) < 0) { client_send_untagged_storage_error(client, mailbox_get_storage(client->mailbox)); } sync_cmd->context = NULL; /* Finish all commands that waited for this sync. Go through the queue backwards, so that tagged replies are sent in the same order as they were received. This fixes problems with clients that rely on this (Apple Mail 3.2) */ for (cmd = client->command_queue; cmd->next != NULL; cmd = cmd->next) ; for (; cmd != NULL; cmd = prev) { prev = cmd->prev; if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC && cmd != sync_cmd && cmd->sync->counter+1 == client->sync_counter) { cmd_finish_sync(cmd); client_command_free(&cmd); } } cmd_finish_sync(sync_cmd); return TRUE; } static void get_common_sync_flags(struct client *client, enum mailbox_sync_flags *flags_r, enum imap_sync_flags *imap_flags_r) { struct client_command_context *cmd; unsigned int count = 0, fast_count = 0, noexpunges_count = 0; *flags_r = 0; *imap_flags_r = 0; for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { if (cmd->sync != NULL && cmd->sync->counter == client->sync_counter) { if ((cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0) fast_count++; if (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) noexpunges_count++; *flags_r |= cmd->sync->flags; *imap_flags_r |= cmd->sync->imap_flags; count++; } } i_assert(noexpunges_count == 0 || noexpunges_count == count); if (fast_count != count) *flags_r &= ~MAILBOX_SYNC_FLAG_FAST; i_assert((*flags_r & MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) == 0); } static bool cmd_sync_client(struct client_command_context *sync_cmd) { struct client *client = sync_cmd->client; struct imap_sync_context *ctx; enum mailbox_sync_flags flags; enum imap_sync_flags imap_flags; bool no_newmail; /* there may be multiple commands waiting. use their combined flags */ get_common_sync_flags(client, &flags, &imap_flags); client->sync_counter++; no_newmail = (client->set->parsed_workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 && client->notify_ctx == NULL && /* always disabled with NOTIFY */ (imap_flags & IMAP_SYNC_FLAG_SAFE) == 0; if (no_newmail) { /* expunges might break the client just as badly as new mail notifications. */ flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES; } client->syncing = TRUE; ctx = imap_sync_init(client, client->mailbox, imap_flags, flags); ctx->no_newmail = no_newmail; /* handle the syncing using sync_cmd. it doesn't actually matter which one of the pending commands it is. */ sync_cmd->func = cmd_sync_continue; sync_cmd->context = ctx; sync_cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; if (!cmd_sync_continue(sync_cmd)) { o_stream_set_flush_pending(client->output, TRUE); return FALSE; } client_command_free(&sync_cmd); cmd_sync_delayed(client); return TRUE; } bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags, enum imap_sync_flags imap_flags, const char *tagline) { struct client *client = cmd->client; i_assert(client->output_cmd_lock == NULL); if (cmd->cancel) return TRUE; cmd->stats.last_run_timeval = ioloop_timeval; if (client->mailbox == NULL) { /* no mailbox selected, no point in delaying the sync */ if (tagline != NULL) client_send_tagline(cmd, tagline); return TRUE; } cmd->tagline_reply = p_strdup(cmd->pool, tagline); cmd->sync = p_new(cmd->pool, struct imap_client_sync_context, 1); cmd->sync->counter = client->sync_counter; cmd->sync->flags = flags; cmd->sync->imap_flags = imap_flags; cmd->sync->tagline = cmd->tagline_reply; cmd->state = CLIENT_COMMAND_STATE_WAIT_SYNC; cmd->func = NULL; cmd->context = NULL; if (client->input_lock == cmd) client->input_lock = NULL; return FALSE; } static bool cmd_sync_drop_fast(struct client *client) { struct client_command_context *cmd, *prev; bool ret = FALSE; if (client->command_queue == NULL) return FALSE; for (cmd = client->command_queue; cmd->next != NULL; cmd = cmd->next) ; for (; cmd != NULL; cmd = prev) { prev = cmd->next; if (cmd->state != CLIENT_COMMAND_STATE_WAIT_SYNC) continue; i_assert(cmd->sync != NULL); if ((cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0) { cmd_finish_sync(cmd); client_command_free(&cmd); ret = TRUE; } } return ret; } static bool cmd_sync_delayed_real(struct client *client) { struct client_command_context *cmd, *first_expunge, *first_nonexpunge; if (client->output_cmd_lock != NULL) { /* wait until we can send output to client */ return FALSE; } if (!imap_sync_is_allowed(client)) { /* wait until mailbox can be synced */ return cmd_sync_drop_fast(client); } /* separate syncs that can send expunges from those that can't */ first_expunge = first_nonexpunge = NULL; for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { if (cmd->sync != NULL && cmd->sync->counter == client->sync_counter) { if (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) { if (first_nonexpunge == NULL) first_nonexpunge = cmd; } else { if (first_expunge == NULL) first_expunge = cmd; } } } if (first_expunge != NULL && first_nonexpunge != NULL) { /* sync expunges after nonexpunges */ for (cmd = first_expunge; cmd != NULL; cmd = cmd->next) { if (cmd->sync != NULL && cmd->sync->counter == client->sync_counter && (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0) cmd->sync->counter++; } first_expunge = NULL; } cmd = first_nonexpunge != NULL ? first_nonexpunge : first_expunge; if (cmd == NULL) return cmd_sync_drop_fast(client); i_assert(client->mailbox != NULL); return cmd_sync_client(cmd); } bool cmd_sync_delayed(struct client *client) { bool ret; T_BEGIN { ret = cmd_sync_delayed_real(client); } T_END; return ret; } dovecot-2.2.33.2/src/imap/cmd-resetkey.c0000644000175000017500000000503313123174404014602 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-resp-code.h" #include "imap-commands.h" #include "imap-urlauth.h" static bool cmd_resetkey_all(struct client_command_context *cmd) { if (imap_urlauth_reset_all_keys(cmd->client->urlauth_ctx) < 0) { client_send_internal_error(cmd); return TRUE; } client_send_tagline(cmd, "OK All keys removed."); return TRUE; } static bool cmd_resetkey_mailbox(struct client_command_context *cmd, const char *mailbox, const struct imap_arg *mech_args) { struct mail_namespace *ns; enum mailbox_flags flags = MAILBOX_FLAG_READONLY; struct mailbox *box; /* check mechanism arguments (we support only INTERNAL mechanism) */ while (!IMAP_ARG_IS_EOL(mech_args)) { const char *mechanism; if (imap_arg_get_astring(mech_args, &mechanism)) { if (strcasecmp(mechanism, "INTERNAL") != 0) { client_send_tagline(cmd, "NO Unsupported URLAUTH mechanism."); return TRUE; } } else { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } mech_args++; } /* find mailbox namespace */ ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; /* open mailbox */ box = mailbox_alloc(ns->list, mailbox, flags); mailbox_set_reason(box, "RESETKEY"); if (mailbox_open(box) < 0) { client_send_box_error(cmd, box); mailbox_free(&box); return TRUE; } /* check urlauth environment and reset requested key */ if (imap_urlauth_reset_mailbox_key(cmd->client->urlauth_ctx, box) < 0) { client_send_internal_error(cmd); mailbox_free(&box); return TRUE; } /* confirm success */ /* FIXME: RFC Says: `Any current IMAP session logged in as the user that has the mailbox selected will receive an untagged OK response with the URLMECH status response code'. We currently don't do that at all. We could probably do it by communicating via mailbox list index. */ client_send_tagline(cmd, "OK [URLMECH INTERNAL] Key removed."); mailbox_free(&box); return TRUE; } bool cmd_resetkey(struct client_command_context *cmd) { const struct imap_arg *args; const char *mailbox; if (cmd->client->urlauth_ctx == NULL) { client_send_command_error(cmd, "URLAUTH disabled."); return TRUE; } if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (IMAP_ARG_IS_EOL(&args[0])) return cmd_resetkey_all(cmd); else if (imap_arg_get_astring(&args[0], &mailbox)) return cmd_resetkey_mailbox(cmd, mailbox, &args[1]); client_send_command_error(cmd, "Invalid arguments."); return TRUE; } dovecot-2.2.33.2/src/imap/imap-client.c0000644000175000017500000012046313165463624014430 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ioloop.h" #include "llist.h" #include "str.h" #include "hostpid.h" #include "net.h" #include "iostream.h" #include "iostream-rawlog.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "var-expand.h" #include "master-service.h" #include "imap-resp-code.h" #include "imap-util.h" #include "imap-urlauth.h" #include "mail-error.h" #include "mail-namespace.h" #include "mail-storage-service.h" #include "mail-autoexpunge.h" #include "imap-state.h" #include "imap-search.h" #include "imap-notify.h" #include "imap-commands.h" #include /* If the last command took longer than this to run, log statistics on where the time was spent. */ #define IMAP_CLIENT_DISCONNECT_LOG_STATS_CMD_MIN_RUNNING_MSECS 1000 extern struct mail_storage_callbacks mail_storage_callbacks; extern struct imap_client_vfuncs imap_client_vfuncs; struct imap_module_register imap_module_register = { 0 }; struct client *imap_clients = NULL; unsigned int imap_client_count = 0; static const char *client_command_state_names[CLIENT_COMMAND_STATE_DONE+1] = { "wait-input", "wait-output", "wait-external", "wait-unambiguity", "wait-sync", "done" }; static void client_idle_timeout(struct client *client) { if (client->output_cmd_lock == NULL) client_send_line(client, "* BYE Disconnected for inactivity."); client_destroy(client, "Disconnected for inactivity"); } static void client_init_urlauth(struct client *client) { struct imap_urlauth_config config; i_zero(&config); config.url_host = client->set->imap_urlauth_host; config.url_port = client->set->imap_urlauth_port; config.socket_path = t_strconcat(client->user->set->base_dir, "/"IMAP_URLAUTH_SOCKET_NAME, NULL); config.session_id = client->session_id; config.access_anonymous = client->user->anonymous; config.access_user = client->user->username; client->urlauth_ctx = imap_urlauth_init(client->user, &config); } static bool user_has_special_use_mailboxes(struct mail_user *user) { struct mail_namespace *ns; for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->special_use_mailboxes) return TRUE; } return FALSE; } struct client *client_create(int fd_in, int fd_out, const char *session_id, struct mail_user *user, struct mail_storage_service_user *service_user, const struct imap_settings *set, const struct lda_settings *lda_set) { const struct mail_storage_settings *mail_set; struct client *client; const char *ident; pool_t pool; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); pool = pool_alloconly_create("imap client", 2048); client = p_new(pool, struct client, 1); client->pool = pool; client->v = imap_client_vfuncs; client->set = set; client->lda_set = lda_set; client->service_user = service_user; client->session_id = p_strdup(pool, session_id); client->fd_in = fd_in; client->fd_out = fd_out; client->input = i_stream_create_fd(fd_in, set->imap_max_line_length, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); i_stream_set_name(client->input, ""); o_stream_set_name(client->output, ""); o_stream_set_flush_callback(client->output, client_output, client); p_array_init(&client->module_contexts, client->pool, 5); client->io = io_add_istream(client->input, client_input, client); client->last_input = ioloop_time; client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, client_idle_timeout, client); client->command_pool = pool_alloconly_create(MEMPOOL_GROWING"client command", 1024*2); client->user = user; client->notify_count_changes = TRUE; client->notify_flag_changes = TRUE; if (set->rawlog_dir[0] != '\0') { (void)iostream_rawlog_create(set->rawlog_dir, &client->input, &client->output); } mail_namespaces_set_storage_callbacks(user->namespaces, &mail_storage_callbacks, client); client->capability_string = str_new(client->pool, sizeof(CAPABILITY_STRING)+64); if (*set->imap_capability == '\0') str_append(client->capability_string, CAPABILITY_STRING); else if (*set->imap_capability != '+') { str_append(client->capability_string, set->imap_capability); } else { str_append(client->capability_string, CAPABILITY_STRING); str_append_c(client->capability_string, ' '); str_append(client->capability_string, set->imap_capability + 1); } if (user->fuzzy_search) { /* Enable FUZZY capability only when it actually has a chance of working */ client_add_capability(client, "SEARCH=FUZZY"); } mail_set = mail_user_set_get_storage_set(user); if (mail_set->mailbox_list_index) { /* NOTIFY is enabled only when mailbox list indexes are enabled, although even that doesn't necessarily guarantee it always */ client_add_capability(client, "NOTIFY"); } if (*set->imap_urlauth_host != '\0' && *mail_set->mail_attribute_dict != '\0') { /* Enable URLAUTH capability only when dict is configured correctly */ client_init_urlauth(client); client_add_capability(client, "URLAUTH"); client_add_capability(client, "URLAUTH=BINARY"); } if (set->imap_metadata && *mail_set->mail_attribute_dict != '\0') { client->imap_metadata_enabled = TRUE; client_add_capability(client, "METADATA"); } if (user_has_special_use_mailboxes(user)) { /* Advertise SPECIAL-USE only if there are actually some SPECIAL-USE flags in mailbox configuration. */ client_add_capability(client, "SPECIAL-USE"); } ident = mail_user_get_anvil_userip_ident(client->user); if (ident != NULL) { master_service_anvil_send(master_service, t_strconcat( "CONNECT\t", my_pid, "\timap/", ident, "\n", NULL)); client->anvil_sent = TRUE; } imap_client_count++; DLLIST_PREPEND(&imap_clients, client); if (hook_client_created != NULL) hook_client_created(&client); imap_refresh_proctitle(); return client; } void client_command_cancel(struct client_command_context **_cmd) { struct client_command_context *cmd = *_cmd; bool cmd_ret; switch (cmd->state) { case CLIENT_COMMAND_STATE_WAIT_INPUT: /* a bit kludgy check: cancel command only if it has context set. currently only append command matches this check. all other commands haven't even started the processing yet. */ if (cmd->context == NULL) break; /* fall through */ case CLIENT_COMMAND_STATE_WAIT_EXTERNAL: case CLIENT_COMMAND_STATE_WAIT_OUTPUT: cmd->cancel = TRUE; break; case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY: case CLIENT_COMMAND_STATE_WAIT_SYNC: /* commands haven't started yet */ break; case CLIENT_COMMAND_STATE_DONE: i_unreached(); } cmd_ret = !cmd->cancel || cmd->func == NULL ? TRUE : command_exec(cmd); if (!cmd_ret) { if (cmd->client->output->closed) i_panic("command didn't cancel itself: %s", cmd->name); } else { client_command_free(*_cmd != NULL ? _cmd : &cmd); } } const char *client_stats(struct client *client) { static struct var_expand_table static_tab[] = { { 'i', NULL, "input" }, { 'o', NULL, "output" }, { '\0', NULL, "session" }, { '\0', NULL, "fetch_hdr_count" }, { '\0', NULL, "fetch_hdr_bytes" }, { '\0', NULL, "fetch_body_count" }, { '\0', NULL, "fetch_body_bytes" }, { '\0', NULL, "deleted" }, { '\0', NULL, "expunged" }, { '\0', NULL, "trashed" }, { '\0', NULL, "autoexpunged" }, { '\0', NULL, "appended" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; string_t *str; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = dec2str(i_stream_get_absolute_offset(client->input)); tab[1].value = dec2str(client->output->offset); tab[2].value = client->session_id; tab[3].value = dec2str(client->fetch_hdr_count); tab[4].value = dec2str(client->fetch_hdr_bytes); tab[5].value = dec2str(client->fetch_body_count); tab[6].value = dec2str(client->fetch_body_bytes); tab[7].value = dec2str(client->deleted_count); tab[8].value = dec2str(client->expunged_count); tab[9].value = dec2str(client->trashed_count); tab[10].value = dec2str(client->autoexpunged_count); tab[11].value = dec2str(client->append_count); str = t_str_new(128); var_expand(str, client->set->imap_logout_format, tab); return str_c(str); } void client_destroy(struct client *client, const char *reason) { client->v.destroy(client, reason); } static void client_command_stats_append(string_t *str, const struct client_command_stats *stats, const char *wait_condition, size_t buffered_size) { uint64_t ioloop_wait_usecs; unsigned int msecs_in_ioloop; ioloop_wait_usecs = io_loop_get_wait_usecs(current_ioloop); msecs_in_ioloop = (ioloop_wait_usecs - stats->start_ioloop_wait_usecs + 999) / 1000; str_printfa(str, "running for %d.%03d + waiting ", (int)((stats->running_usecs+999)/1000 / 1000), (int)((stats->running_usecs+999)/1000 % 1000)); if (wait_condition[0] != '\0') str_printfa(str, "%s ", wait_condition); str_printfa(str, "for %d.%03d secs", msecs_in_ioloop / 1000, msecs_in_ioloop % 1000); if (stats->lock_wait_usecs > 0) { int lock_wait_msecs = (stats->lock_wait_usecs+999)/1000; str_printfa(str, ", %d.%03d in locks", lock_wait_msecs/1000, lock_wait_msecs%1000); } str_printfa(str, ", %llu B in + %llu", (unsigned long long)stats->bytes_in, (unsigned long long)stats->bytes_out); if (buffered_size > 0) str_printfa(str, "+%"PRIuSIZE_T, buffered_size); str_append(str, " B out"); } static const char *client_get_last_command_status(struct client *client) { if (client->logged_out) return ""; if (client->last_cmd_name == NULL) return " (No commands sent)"; /* client disconnected without sending LOGOUT. if the last command took over 1 second to run, log it. */ const struct client_command_stats *stats = &client->last_cmd_stats; string_t *str = t_str_new(128); int last_run_secs = timeval_diff_msecs(&ioloop_timeval, &stats->last_run_timeval); str_printfa(str, " (%s finished %d.%03d secs ago", client->last_cmd_name, last_run_secs/1000, last_run_secs%1000); if (timeval_diff_msecs(&stats->last_run_timeval, &stats->start_time) >= IMAP_CLIENT_DISCONNECT_LOG_STATS_CMD_MIN_RUNNING_MSECS) { str_append(str, " - "); client_command_stats_append(str, stats, "", 0); } str_append_c(str, ')'); return str_c(str); } static const char *client_get_commands_status(struct client *client) { struct client_command_context *cmd, *last_cmd = NULL; struct client_command_stats all_stats; string_t *str; enum io_condition cond; const char *cond_str; if (client->command_queue == NULL) return client_get_last_command_status(client); i_zero(&all_stats); str = t_str_new(128); str_append(str, " ("); for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { if (cmd->name == NULL) { /* (parts of a) tag were received, but not yet the command name */ continue; } str_append(str, cmd->name); if (cmd->next != NULL) str_append_c(str, ','); all_stats.running_usecs += cmd->stats.running_usecs; all_stats.lock_wait_usecs += cmd->stats.lock_wait_usecs; all_stats.bytes_in += cmd->stats.bytes_in; all_stats.bytes_out += cmd->stats.bytes_out; last_cmd = cmd; } if (last_cmd == NULL) return client_get_last_command_status(client); cond = io_loop_find_fd_conditions(current_ioloop, client->fd_out); if ((cond & (IO_READ | IO_WRITE)) == (IO_READ | IO_WRITE)) cond_str = "input/output"; else if ((cond & IO_READ) != 0) cond_str = "input"; else if ((cond & IO_WRITE) != 0) cond_str = "output"; else cond_str = "nothing"; all_stats.start_ioloop_wait_usecs = last_cmd->stats.start_ioloop_wait_usecs; str_append_c(str, ' '); client_command_stats_append(str, &all_stats, cond_str, o_stream_get_buffer_used_size(client->output)); str_printfa(str, ", state=%s)", client_command_state_names[last_cmd->state]); return str_c(str); } static void client_log_disconnect(struct client *client, const char *reason) { i_info("%s %s", reason, client_stats(client)); } static void client_default_destroy(struct client *client, const char *reason) { struct client_command_context *cmd; i_assert(!client->destroyed); client->destroyed = TRUE; client->disconnected = TRUE; if (client->disconnect_reason != NULL) reason = client->disconnect_reason; if (reason == NULL) reason = t_strconcat( io_stream_get_disconnect_reason(client->input, client->output), client_get_commands_status(client), NULL); i_stream_close(client->input); o_stream_close(client->output); /* finish off all the queued commands. */ if (client->output_cmd_lock != NULL) client_command_cancel(&client->output_cmd_lock); while (client->command_queue != NULL) { cmd = client->command_queue; client_command_cancel(&cmd); } /* handle the input_lock command last. it might have been waiting on other queued commands (although we probably should just drop the command at that point since it hasn't started running. but this may change in future). */ if (client->input_lock != NULL) client_command_cancel(&client->input_lock); if (client->mailbox != NULL) imap_client_close_mailbox(client); if (client->notify_ctx != NULL) imap_notify_deinit(&client->notify_ctx); if (client->urlauth_ctx != NULL) imap_urlauth_deinit(&client->urlauth_ctx); if (client->anvil_sent) { master_service_anvil_send(master_service, t_strconcat( "DISCONNECT\t", my_pid, "\timap/", mail_user_get_anvil_userip_ident(client->user), "\n", NULL)); } if (client->free_parser != NULL) imap_parser_unref(&client->free_parser); if (client->io != NULL) io_remove(&client->io); if (client->to_idle_output != NULL) timeout_remove(&client->to_idle_output); if (client->to_delayed_input != NULL) timeout_remove(&client->to_delayed_input); timeout_remove(&client->to_idle); /* i/ostreams are already closed at this stage, so fd can be closed */ fd_close_maybe_stdio(&client->fd_in, &client->fd_out); /* Autoexpunging might run for a long time. Disconnect the client before it starts, and refresh proctitle so it's clear that it's doing autoexpunging. We've also sent DISCONNECT to anvil already, because this is background work and shouldn't really be counted as an active IMAP session for the user. Don't autoexpunge if the client is hibernated - it shouldn't be any different from the non-hibernating IDLE case. For frequent hibernations it could also be doing unnecessarily much work. */ imap_refresh_proctitle(); if (!client->hibernated) { client->autoexpunged_count = mail_user_autoexpunge(client->user); client_log_disconnect(client, reason); } mail_user_unref(&client->user); /* free the i/ostreams after mail_user_unref(), which could trigger mail_storage_callbacks notifications that write to the ostream. */ i_stream_destroy(&client->input); o_stream_destroy(&client->output); if (array_is_created(&client->search_saved_uidset)) array_free(&client->search_saved_uidset); if (array_is_created(&client->search_updates)) array_free(&client->search_updates); pool_unref(&client->command_pool); mail_storage_service_user_unref(&client->service_user); imap_client_count--; DLLIST_REMOVE(&imap_clients, client); i_free(client->last_cmd_name); pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); imap_refresh_proctitle(); } static void client_destroy_timeout(struct client *client) { client_destroy(client, NULL); } void client_disconnect(struct client *client, const char *reason) { if (client->disconnected) return; client->disconnected = TRUE; client->disconnect_reason = p_strdup(client->pool, reason); o_stream_nflush(client->output); o_stream_uncork(client->output); i_stream_close(client->input); o_stream_close(client->output); if (client->to_idle != NULL) timeout_remove(&client->to_idle); client->to_idle = timeout_add(0, client_destroy_timeout, client); } void client_disconnect_with_error(struct client *client, const char *msg) { client_send_line(client, t_strconcat("* BYE ", msg, NULL)); client_disconnect(client, msg); } void client_add_capability(struct client *client, const char *capability) { /* require a single capability at a time (feels cleaner) */ i_assert(strchr(capability, ' ') == NULL); if (client->set->imap_capability[0] != '\0' && client->set->imap_capability[0] != '+') { /* explicit capability - don't change it */ return; } str_append_c(client->capability_string, ' '); str_append(client->capability_string, capability); } void client_send_line(struct client *client, const char *data) { (void)client_send_line_next(client, data); } int client_send_line_next(struct client *client, const char *data) { struct const_iovec iov[2]; if (client->output->closed) return -1; iov[0].iov_base = data; iov[0].iov_len = strlen(data); iov[1].iov_base = "\r\n"; iov[1].iov_len = 2; if (o_stream_sendv(client->output, iov, 2) < 0) return -1; client->last_output = ioloop_time; if (o_stream_get_buffer_used_size(client->output) >= CLIENT_OUTPUT_OPTIMAL_SIZE) { /* buffer full, try flushing */ return o_stream_flush(client->output); } return 1; } static void client_cmd_append_timing_stats(struct client_command_context *cmd, string_t *str) { unsigned int msecs_in_cmd, msecs_in_ioloop; uint64_t ioloop_wait_usecs; unsigned int msecs_since_cmd; if (cmd->stats.start_time.tv_sec == 0) return; command_stats_flush(cmd); ioloop_wait_usecs = io_loop_get_wait_usecs(current_ioloop); msecs_in_cmd = (cmd->stats.running_usecs + 999) / 1000; msecs_in_ioloop = (ioloop_wait_usecs - cmd->stats.start_ioloop_wait_usecs + 999) / 1000; msecs_since_cmd = timeval_diff_msecs(&ioloop_timeval, &cmd->stats.last_run_timeval); if (str_data(str)[str_len(str)-1] == '.') str_truncate(str, str_len(str)-1); str_printfa(str, " (%d.%03d + %d.%03d ", msecs_in_cmd / 1000, msecs_in_cmd % 1000, msecs_in_ioloop / 1000, msecs_in_ioloop % 1000); if (msecs_since_cmd > 0) { str_printfa(str, "+ %d.%03d ", msecs_since_cmd / 1000, msecs_since_cmd % 1000); } str_append(str, "secs)."); } void client_send_tagline(struct client_command_context *cmd, const char *data) { cmd->client->v.send_tagline(cmd, data); } static void client_default_send_tagline(struct client_command_context *cmd, const char *data) { struct client *client = cmd->client; const char *tag = cmd->tag; if (client->output->closed || cmd->cancel) return; i_assert(!cmd->tagline_sent); cmd->tagline_sent = TRUE; cmd->tagline_reply = p_strdup(cmd->pool, data); if (tag == NULL || *tag == '\0') tag = "*"; T_BEGIN { string_t *str = t_str_new(256); str_printfa(str, "%s %s", tag, data); client_cmd_append_timing_stats(cmd, str); str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); } T_END; client->last_output = ioloop_time; } static int client_default_sync_notify_more(struct imap_sync_context *ctx ATTR_UNUSED) { return 1; } void client_send_command_error(struct client_command_context *cmd, const char *msg) { struct client *client = cmd->client; const char *error, *cmd_name; bool fatal; if (msg == NULL) { msg = imap_parser_get_error(cmd->parser, &fatal); if (fatal) { client_disconnect_with_error(client, msg); return; } } if (cmd->tag == NULL) error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL); else if (cmd->name == NULL) error = t_strconcat("BAD Error in IMAP command: ", msg, NULL); else { cmd_name = t_str_ucase(cmd->name); error = t_strconcat("BAD Error in IMAP command ", cmd_name, ": ", msg, NULL); } client_send_tagline(cmd, error); if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_disconnect_with_error(client, "Too many invalid IMAP commands."); } cmd->param_error = TRUE; /* client_read_args() failures rely on this being set, so that the command processing is stopped even while command function returns FALSE. */ cmd->state = CLIENT_COMMAND_STATE_DONE; } void client_send_internal_error(struct client_command_context *cmd) { client_send_tagline(cmd, t_strflocaltime("NO "MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time)); } bool client_read_args(struct client_command_context *cmd, unsigned int count, unsigned int flags, const struct imap_arg **args_r) { string_t *str; int ret; i_assert(count <= INT_MAX); ret = imap_parser_read_args(cmd->parser, count, flags, args_r); if (ret >= (int)count) { /* all parameters read successfully */ i_assert(cmd->client->input_lock == NULL || cmd->client->input_lock == cmd); str = t_str_new(256); imap_write_args(str, *args_r); cmd->args = p_strdup(cmd->pool, str_c(str)); str_truncate(str, 0); imap_write_args_for_human(str, *args_r); cmd->human_args = p_strdup(cmd->pool, str_c(str)); cmd->client->input_lock = NULL; return TRUE; } else if (ret == -2) { /* need more data */ if (cmd->client->input->closed) { /* disconnected */ cmd->state = CLIENT_COMMAND_STATE_DONE; } return FALSE; } else { /* error, or missing arguments */ client_send_command_error(cmd, ret < 0 ? NULL : "Missing arguments"); return FALSE; } } bool client_read_string_args(struct client_command_context *cmd, unsigned int count, ...) { const struct imap_arg *imap_args; va_list va; const char *str; unsigned int i; if (!client_read_args(cmd, count, 0, &imap_args)) return FALSE; va_start(va, count); for (i = 0; i < count; i++) { const char **ret = va_arg(va, const char **); if (IMAP_ARG_IS_EOL(&imap_args[i])) { client_send_command_error(cmd, "Missing arguments."); break; } if (!imap_arg_get_astring(&imap_args[i], &str)) { client_send_command_error(cmd, "Invalid arguments."); break; } if (ret != NULL) *ret = str; } va_end(va); return i == count; } static struct client_command_context * client_command_find_with_flags(struct client_command_context *new_cmd, enum command_flags flags, enum client_command_state max_state) { struct client_command_context *cmd; cmd = new_cmd->client->command_queue; for (; cmd != NULL; cmd = cmd->next) { if (cmd->state <= max_state && cmd != new_cmd && (cmd->cmd_flags & flags) != 0) return cmd; } return NULL; } static bool client_command_is_ambiguous(struct client_command_context *cmd) { enum command_flags flags; enum client_command_state max_state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; bool broken_client = FALSE; if ((cmd->cmd_flags & COMMAND_FLAG_REQUIRES_SYNC) != 0 && !imap_sync_is_allowed(cmd->client)) return TRUE; if (cmd->search_save_result_used) { /* if there are pending commands that update the search save result, wait */ struct client_command_context *old_cmd = cmd->next; for (; old_cmd != NULL; old_cmd = old_cmd->next) { if (old_cmd->search_save_result) return TRUE; } } if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_MAILBOX) == COMMAND_FLAG_BREAKS_MAILBOX) { /* there must be no other command running that uses the selected mailbox */ flags = COMMAND_FLAG_USES_MAILBOX; max_state = CLIENT_COMMAND_STATE_DONE; } else if ((cmd->cmd_flags & COMMAND_FLAG_USES_SEQS) != 0) { /* no existing command must be breaking sequences */ flags = COMMAND_FLAG_BREAKS_SEQS; broken_client = TRUE; } else if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_SEQS) != 0) { /* if existing command uses sequences, we'll have to block */ flags = COMMAND_FLAG_USES_SEQS; } else { return FALSE; } if (client_command_find_with_flags(cmd, flags, max_state) == NULL) { if (cmd->client->syncing) { /* don't do anything until syncing is finished */ return TRUE; } if (cmd->client->mailbox_change_lock != NULL && cmd->client->mailbox_change_lock != cmd) { /* don't do anything until mailbox is fully opened/closed */ return TRUE; } return FALSE; } if (broken_client) { client_send_line(cmd->client, "* BAD ["IMAP_RESP_CODE_CLIENTBUG"] " "Command pipelining results in ambiguity."); } return TRUE; } struct client_command_context *client_command_alloc(struct client *client) { struct client_command_context *cmd; cmd = p_new(client->command_pool, struct client_command_context, 1); cmd->client = client; cmd->pool = client->command_pool; cmd->stats.start_time = ioloop_timeval; cmd->stats.last_run_timeval = ioloop_timeval; cmd->stats.start_ioloop_wait_usecs = io_loop_get_wait_usecs(current_ioloop); p_array_init(&cmd->module_contexts, cmd->pool, 5); DLLIST_PREPEND(&client->command_queue, cmd); client->command_queue_size++; imap_client_notify_command_allocated(client); return cmd; } static struct client_command_context * client_command_new(struct client *client) { struct client_command_context *cmd; cmd = client_command_alloc(client); if (client->free_parser != NULL) { cmd->parser = client->free_parser; client->free_parser = NULL; } else { cmd->parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); } return cmd; } void client_add_missing_io(struct client *client) { if (client->io == NULL && !client->disconnected) client->io = io_add_istream(client->input, client_input, client); } void client_command_free(struct client_command_context **_cmd) { struct client_command_context *cmd = *_cmd; struct client *client = cmd->client; enum client_command_state state = cmd->state; *_cmd = NULL; i_assert(!cmd->executing); i_assert(client->output_cmd_lock == NULL); /* reset input idle time because command output might have taken a long time and we don't want to disconnect client immediately then */ client->last_input = ioloop_time; timeout_reset(client->to_idle); if (cmd->cancel) { cmd->cancel = FALSE; client_send_tagline(cmd, "NO Command cancelled."); } i_free(client->last_cmd_name); client->last_cmd_name = i_strdup(cmd->name); client->last_cmd_stats = cmd->stats; if (!cmd->param_error) client->bad_counter = 0; if (client->input_lock == cmd) client->input_lock = NULL; if (client->mailbox_change_lock == cmd) client->mailbox_change_lock = NULL; if (cmd->parser != NULL) { if (client->free_parser == NULL) { imap_parser_reset(cmd->parser); client->free_parser = cmd->parser; } else { imap_parser_unref(&cmd->parser); } } client->command_queue_size--; DLLIST_REMOVE(&client->command_queue, cmd); cmd = NULL; if (client->command_queue == NULL) { /* no commands left in the queue, we can clear the pool */ p_clear(client->command_pool); if (client->to_idle_output != NULL) timeout_remove(&client->to_idle_output); } imap_client_notify_command_freed(client); imap_refresh_proctitle(); /* if command finished from external event, check input for more unhandled commands since we may not be executing from client_input or client_output. */ if (state == CLIENT_COMMAND_STATE_WAIT_EXTERNAL && !client->disconnected) { client_add_missing_io(client); if (client->to_delayed_input == NULL) { client->to_delayed_input = timeout_add(0, client_input, client); } } } static void client_check_command_hangs(struct client *client) { struct client_command_context *cmd; unsigned int unfinished_count = 0; bool have_wait_unfinished = FALSE; for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { switch (cmd->state) { case CLIENT_COMMAND_STATE_WAIT_INPUT: /* We need to be reading input for this command. However, if there is already an output lock for another command we'll wait for it to finish first. This is needed because if there are any literals we'd need to send "+ OK" responses. */ i_assert(client->io != NULL || (client->output_cmd_lock != NULL && client->output_cmd_lock != client->input_lock)); unfinished_count++; break; case CLIENT_COMMAND_STATE_WAIT_OUTPUT: i_assert((io_loop_find_fd_conditions(current_ioloop, client->fd_out) & IO_WRITE) != 0); unfinished_count++; break; case CLIENT_COMMAND_STATE_WAIT_EXTERNAL: unfinished_count++; break; case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY: have_wait_unfinished = TRUE; break; case CLIENT_COMMAND_STATE_WAIT_SYNC: if ((io_loop_find_fd_conditions(current_ioloop, client->fd_out) & IO_WRITE) == 0) have_wait_unfinished = TRUE; else { /* we have an output callback, which will be called soon and it'll run cmd_sync_delayed(). FIXME: is this actually wanted? */ } break; case CLIENT_COMMAND_STATE_DONE: i_unreached(); } } i_assert(!have_wait_unfinished || unfinished_count > 0); } static bool client_remove_pending_unambiguity(struct client *client) { if (client->input_lock != NULL) { /* there's a command that has locked the input */ struct client_command_context *cmd = client->input_lock; if (cmd->state != CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) return TRUE; /* the command is waiting for existing ambiguity causing commands to finish. */ if (client_command_is_ambiguous(cmd)) { /* we could be waiting for existing sync to finish */ if (!cmd_sync_delayed(client)) return FALSE; if (client_command_is_ambiguous(cmd)) return FALSE; } cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; } return TRUE; } void client_continue_pending_input(struct client *client) { i_assert(!client->handling_input); /* this function is called at the end of I/O callbacks (and only there). fix up the command states and verify that they're correct. */ while (client_remove_pending_unambiguity(client)) { client_add_missing_io(client); /* if there's unread data in buffer, handle it. */ if (i_stream_get_data_size(client->input) == 0 || client->disconnected) break; if (!client_handle_input(client)) break; } if (!client->input->closed && !client->output->closed) client_check_command_hangs(client); } /* Skip incoming data until newline is found, returns TRUE if newline was found. */ static bool client_skip_line(struct client *client) { const unsigned char *data; size_t i, data_size; data = i_stream_get_data(client->input, &data_size); for (i = 0; i < data_size; i++) { if (data[i] == '\n') { client->input_skip_line = FALSE; i++; break; } } i_stream_skip(client->input, i); return !client->input_skip_line; } static void client_idle_output_timeout(struct client *client) { client_destroy(client, "Disconnected for inactivity in reading our output"); } bool client_handle_unfinished_cmd(struct client_command_context *cmd) { if (cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT) { /* need more input */ return FALSE; } if (cmd->state != CLIENT_COMMAND_STATE_WAIT_OUTPUT) { /* waiting for something */ if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) { /* this is mainly for APPEND. */ client_add_missing_io(cmd->client); } return TRUE; } /* output is blocking, we can execute more commands */ o_stream_set_flush_pending(cmd->client->output, TRUE); if (cmd->client->to_idle_output == NULL) { /* disconnect sooner if client isn't reading our output */ cmd->client->to_idle_output = timeout_add(CLIENT_OUTPUT_TIMEOUT_MSECS, client_idle_output_timeout, cmd->client); } return TRUE; } static bool client_command_input(struct client_command_context *cmd) { struct client *client = cmd->client; struct command *command; if (cmd->func != NULL) { /* command is being executed - continue it */ if (command_exec(cmd)) { /* command execution was finished */ client_command_free(&cmd); client_add_missing_io(client); return TRUE; } return client_handle_unfinished_cmd(cmd); } if (cmd->tag == NULL) { cmd->tag = imap_parser_read_word(cmd->parser); if (cmd->tag == NULL) return FALSE; /* need more data */ cmd->tag = p_strdup(cmd->pool, cmd->tag); } if (cmd->name == NULL) { cmd->name = imap_parser_read_word(cmd->parser); if (cmd->name == NULL) return FALSE; /* need more data */ /* UID commands are a special case. better to handle them here. */ if (!cmd->uid && strcasecmp(cmd->name, "UID") == 0) { cmd->uid = TRUE; cmd->name = imap_parser_read_word(cmd->parser); if (cmd->name == NULL) return FALSE; /* need more data */ } cmd->name = !cmd->uid ? p_strdup(cmd->pool, cmd->name) : p_strconcat(cmd->pool, "UID ", cmd->name, NULL); imap_refresh_proctitle(); } client->input_skip_line = TRUE; if (cmd->name[0] == '\0') { /* command not given - cmd->func is already NULL. */ } else if ((command = command_find(cmd->name)) != NULL) { cmd->func = command->func; cmd->cmd_flags = command->flags; if (client_command_is_ambiguous(cmd)) { /* do nothing until existing commands are finished */ i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; io_remove(&client->io); return FALSE; } } if (cmd->func == NULL) { /* unknown command */ client_send_command_error(cmd, "Unknown command."); cmd->param_error = TRUE; client_command_free(&cmd); return TRUE; } else { i_assert(!client->disconnected); return client_command_input(cmd); } } static bool client_handle_next_command(struct client *client, bool *remove_io_r) { *remove_io_r = FALSE; if (client->input_lock != NULL) { if (client->input_lock->state == CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY || /* we can't send literal "+ OK" replies if output is locked by another command. */ (client->output_cmd_lock != NULL && client->output_cmd_lock != client->input_lock)) { *remove_io_r = TRUE; return FALSE; } return client_command_input(client->input_lock); } if (client->input_skip_line) { /* first eat the previous command line */ if (!client_skip_line(client)) return FALSE; client->input_skip_line = FALSE; } /* don't bother creating a new client command before there's at least some input */ if (i_stream_get_data_size(client->input) == 0) return FALSE; /* beginning a new command */ if (client->command_queue_size >= CLIENT_COMMAND_QUEUE_MAX_SIZE || client->output_cmd_lock != NULL) { /* wait for some of the commands to finish */ *remove_io_r = TRUE; return FALSE; } client->input_lock = client_command_new(client); return client_command_input(client->input_lock); } bool client_handle_input(struct client *client) { bool ret, remove_io, handled_commands = FALSE; i_assert(!client->disconnected); client->handling_input = TRUE; do { T_BEGIN { ret = client_handle_next_command(client, &remove_io); } T_END; if (ret) handled_commands = TRUE; } while (ret && !client->disconnected && client->io != NULL); client->handling_input = FALSE; if (remove_io) io_remove(&client->io); else client_add_missing_io(client); if (!handled_commands) return FALSE; if (client->input_lock == NULL) { /* finished handling all commands. sync them all at once now. */ cmd_sync_delayed(client); } else if (client->input_lock->state == CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) { /* the command may be waiting for previous command to sync. */ cmd_sync_delayed(client); } return TRUE; } void client_input(struct client *client) { struct client_command_context *cmd; struct ostream *output = client->output; ssize_t bytes; i_assert(client->io != NULL); client->last_input = ioloop_time; timeout_reset(client->to_idle); if (client->to_delayed_input != NULL) timeout_remove(&client->to_delayed_input); bytes = i_stream_read(client->input); if (bytes == -1) { /* disconnected */ client_destroy(client, NULL); return; } o_stream_ref(output); o_stream_cork(output); if (!client_handle_input(client) && bytes == -2) { /* parameter word is longer than max. input buffer size. this is most likely an error, so skip the new data until newline is found. */ client->input_skip_line = TRUE; cmd = client->input_lock != NULL ? client->input_lock : client_command_new(client); cmd->param_error = TRUE; client_send_command_error(cmd, "Too long argument."); client_command_free(&cmd); } o_stream_uncork(output); o_stream_unref(&output); imap_refresh_proctitle(); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); } static void client_output_cmd(struct client_command_context *cmd) { bool finished; /* continue processing command */ finished = command_exec(cmd); if (!finished) (void)client_handle_unfinished_cmd(cmd); else { /* command execution was finished */ client_command_free(&cmd); } } static void client_output_commands(struct client *client) { struct client_command_context *cmd; /* mark all commands non-executed */ for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) cmd->temp_executed = FALSE; if (client->output_cmd_lock != NULL) { client->output_cmd_lock->temp_executed = TRUE; client_output_cmd(client->output_cmd_lock); } while (client->output_cmd_lock == NULL) { /* go through the entire commands list every time in case multiple commands were freed. temp_executed keeps track of which messages we've called so far */ cmd = client->command_queue; for (; cmd != NULL; cmd = cmd->next) { if (!cmd->temp_executed && cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT) { cmd->temp_executed = TRUE; client_output_cmd(cmd); break; } } if (cmd == NULL) { /* all commands executed */ break; } } } int client_output(struct client *client) { int ret; i_assert(!client->destroyed); client->last_output = ioloop_time; timeout_reset(client->to_idle); if (client->to_idle_output != NULL) timeout_reset(client->to_idle_output); o_stream_cork(client->output); if ((ret = o_stream_flush(client->output)) < 0) { client_destroy(client, NULL); return 1; } client_output_commands(client); (void)cmd_sync_delayed(client); o_stream_uncork(client->output); imap_refresh_proctitle(); if (client->output->closed) client_destroy(client, NULL); else client_continue_pending_input(client); return ret; } bool client_handle_search_save_ambiguity(struct client_command_context *cmd) { struct client_command_context *old_cmd = cmd->next; /* search only commands that were added before this command (commands are prepended to the queue, so they're after ourself) */ for (; old_cmd != NULL; old_cmd = old_cmd->next) { if (old_cmd->search_save_result) break; } if (old_cmd == NULL) return FALSE; /* ambiguity, wait until it's over */ i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); cmd->client->input_lock = cmd; cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; cmd->search_save_result_used = TRUE; io_remove(&cmd->client->io); return TRUE; } int client_enable(struct client *client, enum mailbox_feature features) { struct mailbox_status status; int ret; if ((client->enabled_features & features) == features) return 0; client->enabled_features |= features; if (client->mailbox == NULL) return 0; ret = mailbox_enable(client->mailbox, features); if ((features & MAILBOX_FEATURE_CONDSTORE) != 0 && ret == 0) { /* CONDSTORE being enabled while mailbox is selected. Notify client of the latest HIGHESTMODSEQ. */ ret = mailbox_get_status(client->mailbox, STATUS_HIGHESTMODSEQ, &status); if (ret == 0) { client_send_line(client, t_strdup_printf( "* OK [HIGHESTMODSEQ %llu] Highest", (unsigned long long)status.highest_modseq)); } } if (ret < 0) { client_send_untagged_storage_error(client, mailbox_get_storage(client->mailbox)); } return ret; } struct imap_search_update * client_search_update_lookup(struct client *client, const char *tag, unsigned int *idx_r) { struct imap_search_update *updates; unsigned int i, count; if (!array_is_created(&client->search_updates)) return NULL; updates = array_get_modifiable(&client->search_updates, &count); for (i = 0; i < count; i++) { if (strcmp(updates[i].tag, tag) == 0) { *idx_r = i; return &updates[i]; } } return NULL; } void client_search_updates_free(struct client *client) { struct imap_search_update *update; if (!array_is_created(&client->search_updates)) return; array_foreach_modifiable(&client->search_updates, update) imap_search_update_free(update); array_clear(&client->search_updates); } void clients_destroy_all(struct mail_storage_service_ctx *storage_service) { while (imap_clients != NULL) { client_send_line(imap_clients, "* BYE Server shutting down."); mail_storage_service_io_activate_user(imap_clients->service_user); client_destroy(imap_clients, "Server shutting down."); } mail_storage_service_io_deactivate(storage_service); } struct imap_client_vfuncs imap_client_vfuncs = { imap_state_export_base, imap_state_import_base, client_default_destroy, client_default_send_tagline, client_default_sync_notify_more, }; dovecot-2.2.33.2/src/imap/cmd-enable.c0000644000175000017500000000176213123174404014202 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" bool cmd_enable(struct client_command_context *cmd) { const struct imap_arg *args; const char *str; string_t *reply; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; reply = t_str_new(64); str_append(reply, "* ENABLED"); for (; !IMAP_ARG_IS_EOL(args); args++) { if (!imap_arg_get_atom(args, &str)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } str = t_str_ucase(str); if (strcmp(str, "CONDSTORE") == 0) { if (client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE) == 0) str_append(reply, " CONDSTORE"); } else if (strcmp(str, "QRESYNC") == 0) { if (client_enable(cmd->client, MAILBOX_FEATURE_QRESYNC | MAILBOX_FEATURE_CONDSTORE) == 0) str_append(reply, " QRESYNC"); } } if (str_len(reply) > 9) client_send_line(cmd->client, str_c(reply)); client_send_tagline(cmd, "OK Enabled."); return TRUE; } dovecot-2.2.33.2/src/imap/cmd-noop.c0000644000175000017500000000070513123174404013723 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-notify.h" #include "imap-commands.h" bool cmd_noop(struct client_command_context *cmd) { if (cmd->client->notify_ctx != NULL) { /* flush any delayed notifications now. this is mainly useful for testing. */ imap_notify_flush(cmd->client->notify_ctx); } return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, "OK NOOP completed."); } dovecot-2.2.33.2/src/imap/imap-fetch.h0000644000175000017500000001212513165463624014243 00000000000000#ifndef IMAP_FETCH_H #define IMAP_FETCH_H struct imap_fetch_context; enum imap_fetch_handler_flags { IMAP_FETCH_HANDLER_FLAG_BUFFERED = 0x01, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT = 0x02 }; /* Returns 1 = ok, 0 = client output buffer full, call again, -1 = error. mail = NULL for deinit. */ typedef int imap_fetch_handler_t(struct imap_fetch_context *ctx, struct mail *mail, void *context); struct imap_fetch_init_context { struct imap_fetch_context *fetch_ctx; pool_t pool; const char *name; const struct imap_arg *args; const char *error; }; struct imap_fetch_handler { const char *name; /* Returns FALSE and sets ctx->error if arg is invalid */ bool (*init)(struct imap_fetch_init_context *ctx); }; struct imap_fetch_context_handler { imap_fetch_handler_t *handler; void *context; const char *name; const char *nil_reply; unsigned int buffered:1; unsigned int want_deinit:1; }; struct imap_fetch_qresync_args { const ARRAY_TYPE(uint32_t) *qresync_sample_seqset; const ARRAY_TYPE(uint32_t) *qresync_sample_uidset; }; struct imap_fetch_state { struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail *cur_mail; unsigned int cur_handler; const char *cur_human_name; uoff_t cur_size, cur_offset; enum mail_fetch_field cur_size_field; string_t *cur_str; size_t cur_str_prefix_size; struct istream *cur_input; bool skip_cr; int (*cont_handler)(struct imap_fetch_context *ctx); uint64_t *cur_stats_sizep; unsigned int fetching:1; unsigned int seen_flags_changed:1; /* TRUE if the first FETCH parameter result hasn't yet been sent to the IMAP client. Note that this doesn't affect buffered content in cur_str until it gets flushed out. */ unsigned int cur_first:1; /* TRUE if the cur_str prefix has been flushed. More data may still be added to it. */ unsigned int line_partial:1; unsigned int skipped_expunged_msgs:1; unsigned int failed:1; }; struct imap_fetch_context { struct client *client; pool_t ctx_pool; const char *reason; enum mail_fetch_field fetch_data; ARRAY_TYPE(const_string) all_headers; ARRAY(struct imap_fetch_context_handler) handlers; unsigned int buffered_handlers_count; ARRAY_TYPE(keywords) tmp_keywords; struct imap_fetch_state state; ARRAY_TYPE(seq_range) fetch_failed_uids; enum mail_error error; const char *errstr; unsigned int initialized:1; unsigned int failures:1; unsigned int flags_have_handler:1; unsigned int flags_update_seen:1; unsigned int flags_show_only_seen_changes:1; }; void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers, size_t count); void imap_fetch_handler_unregister(const char *name); void imap_fetch_add_handler(struct imap_fetch_init_context *ctx, enum imap_fetch_handler_flags flags, const char *nil_reply, imap_fetch_handler_t *handler, void *context) ATTR_NULL(3, 5); #define imap_fetch_add_handler(ctx, flags, nil_reply, handler, context) \ imap_fetch_add_handler(ctx, flags, nil_reply + \ CALLBACK_TYPECHECK(handler, int (*)( \ struct imap_fetch_context *, struct mail *, \ typeof(context))), \ (imap_fetch_handler_t *)handler, context) int imap_fetch_att_list_parse(struct client *client, pool_t pool, const struct imap_arg *list, struct imap_fetch_context **fetch_ctx_r, const char **error_r); struct imap_fetch_context * imap_fetch_alloc(struct client *client, pool_t pool, const char *reason); void imap_fetch_free(struct imap_fetch_context **ctx); bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx); void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx, bool (*init)(struct imap_fetch_init_context *)); const struct imap_fetch_handler *imap_fetch_handler_lookup(const char *name); void imap_fetch_begin(struct imap_fetch_context *ctx, struct mailbox *box, struct mail_search_args *search_args); int imap_fetch_send_vanished(struct client *client, struct mailbox *box, const struct mail_search_args *search_args, const struct imap_fetch_qresync_args *qresync_args); /* Returns 1 if finished, 0 if more data is needed, -1 if error. When 0 is returned, line_partial=TRUE if literal is open and must be finished before anything else to client. */ int imap_fetch_more(struct imap_fetch_context *ctx, struct client_command_context *cmd); /* Like imap_fetch_more(), but don't check/update output_lock. The caller must handle this itself. */ int imap_fetch_more_no_lock_update(struct imap_fetch_context *ctx); int imap_fetch_end(struct imap_fetch_context *ctx); int imap_fetch_more(struct imap_fetch_context *ctx, struct client_command_context *cmd); bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx); bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx); bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx); bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx); bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx); bool imap_fetch_binary_init(struct imap_fetch_init_context *ctx); void imap_fetch_handlers_init(void); void imap_fetch_handlers_deinit(void); #endif dovecot-2.2.33.2/src/imap/imap-settings.c0000644000175000017500000001214213165463624015004 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "lda-settings.h" #include "imap-settings.h" #include #include static bool imap_settings_verify(void *_set, pool_t pool, const char **error_r); /* */ static struct file_listener_settings imap_unix_listeners_array[] = { { "login/imap", 0666, "", "" }, { "imap-master", 0600, "", "" } }; static struct file_listener_settings *imap_unix_listeners[] = { &imap_unix_listeners_array[0], &imap_unix_listeners_array[1] }; static buffer_t imap_unix_listeners_buf = { imap_unix_listeners, sizeof(imap_unix_listeners), { NULL, } }; /* */ struct service_settings imap_service_settings = { .name = "imap", .protocol = "imap", .type = "", .executable = "imap", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_unix_listeners_buf, sizeof(imap_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct imap_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct imap_settings, field), defines } static const struct setting_define imap_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR_VARS, rawlog_dir), DEF(SET_SIZE, imap_max_line_length), DEF(SET_TIME, imap_idle_notify_interval), DEF(SET_STR, imap_capability), DEF(SET_STR, imap_client_workarounds), DEF(SET_STR, imap_logout_format), DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), DEF(SET_ENUM, imap_fetch_failure), DEF(SET_BOOL, imap_metadata), DEF(SET_TIME, imap_hibernate_timeout), DEF(SET_STR, imap_urlauth_host), DEF(SET_IN_PORT, imap_urlauth_port), SETTING_DEFINE_LIST_END }; static const struct imap_settings imap_default_settings = { .verbose_proctitle = FALSE, .rawlog_dir = "", /* RFC-2683 recommends at least 8000 bytes. Some clients however don't break large message sets to multiple commands, so we're pretty liberal by default. */ .imap_max_line_length = 64*1024, .imap_idle_notify_interval = 2*60, .imap_capability = "", .imap_client_workarounds = "", .imap_logout_format = "in=%i out=%o", .imap_id_send = "name *", .imap_id_log = "", .imap_fetch_failure = "disconnect-immediately:disconnect-after:no-after", .imap_metadata = FALSE, .imap_hibernate_timeout = 0, .imap_urlauth_host = "", .imap_urlauth_port = 143 }; static const struct setting_parser_info *imap_setting_dependencies[] = { &mail_user_setting_parser_info, &lda_setting_parser_info, NULL }; const struct setting_parser_info imap_setting_parser_info = { .module_name = "imap", .defines = imap_setting_defines, .defaults = &imap_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_settings), .parent_offset = (size_t)-1, .check_func = imap_settings_verify, .dependencies = imap_setting_dependencies }; /* */ struct imap_client_workaround_list { const char *name; enum imap_client_workarounds num; }; static const struct imap_client_workaround_list imap_client_workaround_list[] = { { "delay-newmail", WORKAROUND_DELAY_NEWMAIL }, { "tb-extra-mailbox-sep", WORKAROUND_TB_EXTRA_MAILBOX_SEP }, { "tb-lsub-flags", WORKAROUND_TB_LSUB_FLAGS }, { NULL, 0 } }; static int imap_settings_parse_workarounds(struct imap_settings *set, const char **error_r) { enum imap_client_workarounds client_workarounds = 0; const struct imap_client_workaround_list *list; const char *const *str; str = t_strsplit_spaces(set->imap_client_workarounds, " ,"); for (; *str != NULL; str++) { list = imap_client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { client_workarounds |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("imap_client_workarounds: " "Unknown workaround: %s", *str); return -1; } } set->parsed_workarounds = client_workarounds; return 0; } static bool imap_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct imap_settings *set = _set; if (imap_settings_parse_workarounds(set, error_r) < 0) return FALSE; if (strcmp(set->imap_fetch_failure, "disconnect-immediately") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY; else if (strcmp(set->imap_fetch_failure, "disconnect-after") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER; else if (strcmp(set->imap_fetch_failure, "no-after") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_NO_AFTER; else { *error_r = t_strdup_printf("Unknown imap_fetch_failure: %s", set->imap_fetch_failure); return FALSE; } return TRUE; } /* */ dovecot-2.2.33.2/src/imap/imap-search.h0000644000175000017500000000314313165463624014417 00000000000000#ifndef IMAP_SEARCH_H #define IMAP_SEARCH_H #include enum search_return_options { SEARCH_RETURN_ESEARCH = 0x0001, SEARCH_RETURN_MIN = 0x0002, SEARCH_RETURN_MAX = 0x0004, SEARCH_RETURN_ALL = 0x0008, SEARCH_RETURN_COUNT = 0x0010, SEARCH_RETURN_MODSEQ = 0x0020, SEARCH_RETURN_SAVE = 0x0040, SEARCH_RETURN_UPDATE = 0x0080, SEARCH_RETURN_PARTIAL = 0x0100, SEARCH_RETURN_RELEVANCY = 0x0200 /* Options that don't return any seq/uid results */ #define SEARCH_RETURN_NORESULTS \ (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE | \ SEARCH_RETURN_UPDATE) }; struct imap_search_context { struct client_command_context *cmd; struct mailbox *box; struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; pool_t fetch_pool; struct imap_fetch_context *fetch_ctx; struct mail_search_args *sargs; enum search_return_options return_options; uint32_t partial1, partial2; struct timeout *to; ARRAY_TYPE(seq_range) result; unsigned int result_count; ARRAY(float) relevancy_scores; float min_relevancy, max_relevancy; uint64_t highest_seen_modseq; unsigned int have_seqsets:1; unsigned int have_modseqs:1; unsigned int sorting:1; }; int cmd_search_parse_return_if_found(struct imap_search_context *ctx, const struct imap_arg **args); void imap_search_context_free(struct imap_search_context *ctx); bool imap_search_start(struct imap_search_context *ctx, struct mail_search_args *sargs, const enum mail_sort_type *sort_program) ATTR_NULL(3); void imap_search_update_free(struct imap_search_update *update); #endif dovecot-2.2.33.2/src/imap/imap-state.h0000644000175000017500000000252313123174404014260 00000000000000#ifndef IMAP_STATE_H #define IMAP_STATE_H /* Export the IMAP client state to the given buffer. Returns 1 if ok, 0 if state couldn't be exported, -1 if temporary internal error error. */ int imap_state_export_internal(struct client *client, buffer_t *dest, const char **error_r); int imap_state_export_external(struct client *client, buffer_t *dest, const char **error_r); /* Returns 1 if ok, 0 if state was corrupted, -1 if other error. Internal state comes from another Dovecot component, which can override IP addresses, session IDs, etc. */ int imap_state_import_internal(struct client *client, const unsigned char *data, size_t size, const char **error_r); int imap_state_import_external(struct client *client, const unsigned char *data, size_t size, const char **error_r); /* INTERNAL API: Note that the "internal" flag specifies whether we're doing the import/export from/to another Dovecot component or an untrusted IMAP client. */ int imap_state_export_base(struct client *client, bool internal, buffer_t *dest, const char **error_r); ssize_t imap_state_import_base(struct client *client, bool internal, const unsigned char *data, size_t size, const char **error_r); void imap_state_import_idle_cmd_tag(struct client *client, const char *tag); #endif dovecot-2.2.33.2/src/imap/imap-commands.h0000644000175000017500000001165313165463624014760 00000000000000#ifndef IMAP_COMMANDS_H #define IMAP_COMMANDS_H struct client_command_context; #include "mail-storage.h" #include "mail-namespace.h" #include "imap-parser.h" #include "imap-sync.h" #include "imap-commands-util.h" typedef bool command_func_t(struct client_command_context *cmd); typedef void command_hook_callback_t(struct client_command_context *ctx); enum command_flags { /* Command uses sequences as its input parameters */ COMMAND_FLAG_USES_SEQS = 0x01, /* Command may reply with EXPUNGE, causing sequences to break */ COMMAND_FLAG_BREAKS_SEQS = 0x02, /* Command changes the mailbox */ COMMAND_FLAG_BREAKS_MAILBOX = 0x04 | COMMAND_FLAG_BREAKS_SEQS, /* Command uses selected mailbox */ COMMAND_FLAG_USES_MAILBOX = COMMAND_FLAG_BREAKS_MAILBOX | COMMAND_FLAG_USES_SEQS, /* Command requires mailbox syncing before it can do its job. */ COMMAND_FLAG_REQUIRES_SYNC = 0x08, /* Command allows replying with [NONEXISTENT] imap resp code. Dovecot internally returns it for all kinds of commands, but unfortunately RFC 5530 specifies it only for "delete something" operations. */ COMMAND_FLAG_USE_NONEXISTENT = 0x10 }; struct command { const char *name; command_func_t *func; enum command_flags flags; }; ARRAY_DEFINE_TYPE(command, struct command); extern ARRAY_TYPE(command) imap_commands; /* Register command. Given name parameter must be permanently stored until command is unregistered. */ void command_register(const char *name, command_func_t *func, enum command_flags flags); void command_unregister(const char *name); /* Register array of commands. */ void command_register_array(const struct command *cmdarr, unsigned int count); void command_unregister_array(const struct command *cmdarr, unsigned int count); /* Register hook callbacks that are called before and after all commands */ void command_hook_register(command_hook_callback_t *pre, command_hook_callback_t *post); void command_hook_unregister(command_hook_callback_t *pre, command_hook_callback_t *post); /* Execute command and hooks */ bool command_exec(struct client_command_context *cmd); /* Finish counting command statistics. This is called automatically when command_exec() returns, but it should be called explicitly if the stats are needed during command_exec(). */ void command_stats_flush(struct client_command_context *cmd); struct command *command_find(const char *name); void commands_init(void); void commands_deinit(void); /* IMAP4rev1 commands: */ /* Non-Authenticated State */ bool cmd_logout(struct client_command_context *cmd); bool cmd_capability(struct client_command_context *cmd); bool cmd_noop(struct client_command_context *cmd); /* Authenticated State */ bool cmd_select(struct client_command_context *cmd); bool cmd_examine(struct client_command_context *cmd); bool cmd_create(struct client_command_context *cmd); bool cmd_delete(struct client_command_context *cmd); bool cmd_rename(struct client_command_context *cmd); bool cmd_subscribe(struct client_command_context *cmd); bool cmd_unsubscribe(struct client_command_context *cmd); bool cmd_list(struct client_command_context *cmd); bool cmd_lsub(struct client_command_context *cmd); bool cmd_status(struct client_command_context *cmd); bool cmd_append(struct client_command_context *cmd); /* Selected state */ bool cmd_check(struct client_command_context *cmd); bool cmd_close(struct client_command_context *cmd); bool cmd_expunge(struct client_command_context *cmd); bool cmd_search(struct client_command_context *cmd); bool cmd_fetch(struct client_command_context *cmd); bool cmd_store(struct client_command_context *cmd); bool cmd_copy(struct client_command_context *cmd); bool cmd_uid(struct client_command_context *cmd); /* IMAP extensions: */ bool cmd_cancelupdate(struct client_command_context *cmd); bool cmd_enable(struct client_command_context *cmd); bool cmd_id(struct client_command_context *cmd); bool cmd_idle(struct client_command_context *cmd); bool cmd_namespace(struct client_command_context *cmd); bool cmd_getmetadata(struct client_command_context *cmd); bool cmd_setmetadata(struct client_command_context *cmd); bool cmd_notify(struct client_command_context *cmd); bool cmd_sort(struct client_command_context *cmd); bool cmd_thread(struct client_command_context *cmd); bool cmd_uid_expunge(struct client_command_context *cmd); bool cmd_move(struct client_command_context *cmd); bool cmd_unselect(struct client_command_context *cmd); bool cmd_x_cancel(struct client_command_context *cmd); bool cmd_x_state(struct client_command_context *cmd); /* IMAP URLAUTH (RFC4467): */ bool cmd_genurlauth(struct client_command_context *cmd); bool cmd_resetkey(struct client_command_context *cmd); bool cmd_urlfetch(struct client_command_context *cmd); /* private: */ bool cmd_list_full(struct client_command_context *cmd, bool lsub); bool cmd_select_full(struct client_command_context *cmd, bool readonly); bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe); #endif dovecot-2.2.33.2/src/imap/imap-master-client.h0000644000175000017500000000027013123174404015704 00000000000000#ifndef IMAP_MASTER_CLIENT_H #define IMAP_MASTER_CLIENT_H void imap_master_client_create(int fd); void imap_master_clients_init(void); void imap_master_clients_deinit(void); #endif dovecot-2.2.33.2/src/imap/imap-commands-util.c0000644000175000017500000002455213165463624015730 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "array.h" #include "buffer.h" #include "str.h" #include "str-sanitize.h" #include "imap-resp-code.h" #include "imap-parser.h" #include "imap-sync.h" #include "imap-utf7.h" #include "imap-util.h" #include "mail-storage.h" #include "mail-namespace.h" #include "imap-commands-util.h" struct mail_namespace * client_find_namespace_full(struct client *client, const char **mailbox, const char **error_r) { struct mail_namespace *namespaces = client->user->namespaces; struct mail_namespace *ns; string_t *utf8_name; utf8_name = t_str_new(64); if (imap_utf7_to_utf8(*mailbox, utf8_name) < 0) { *error_r = "NO Mailbox name is not valid mUTF-7"; return NULL; } ns = mail_namespace_find(namespaces, str_c(utf8_name)); if ((ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0 && ns->prefix_len == 0) { /* this matched only the autocreated prefix="" namespace. give a nice human-readable error message */ *error_r = t_strdup_printf( "NO Client tried to access nonexistent namespace. " "(Mailbox name should probably be prefixed with: %s)", mail_namespace_find_inbox(namespaces)->prefix); return NULL; } if ((client->set->parsed_workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 && str_len(utf8_name) > 0 && str_c(utf8_name)[str_len(utf8_name)-1] == mail_namespace_get_sep(ns)) { /* drop the extra trailing hierarchy separator */ str_truncate(utf8_name, str_len(utf8_name)-1); } *mailbox = str_c(utf8_name); return ns; } struct mail_namespace * client_find_namespace(struct client_command_context *cmd, const char **mailbox) { struct mail_namespace *ns; const char *error; ns = client_find_namespace_full(cmd->client, mailbox, &error); if (ns == NULL) client_send_tagline(cmd, error); return ns; } bool client_verify_open_mailbox(struct client_command_context *cmd) { if (cmd->client->mailbox != NULL) return TRUE; else { client_send_tagline(cmd, "BAD No mailbox selected."); return FALSE; } } void imap_client_close_mailbox(struct client *client) { struct mailbox *box; i_assert(client->mailbox != NULL); if (array_is_created(&client->fetch_failed_uids)) array_clear(&client->fetch_failed_uids); client_search_updates_free(client); box = client->mailbox; client->mailbox = NULL; mailbox_free(&box); client_update_mailbox_flags(client, NULL); } int client_open_save_dest_box(struct client_command_context *cmd, const char *name, struct mailbox **destbox_r) { struct mail_namespace *ns; struct mailbox *box; const char *error_string; enum mail_error error; ns = client_find_namespace(cmd, &name); if (ns == NULL) return -1; if (cmd->client->mailbox != NULL && mailbox_equals(cmd->client->mailbox, ns, name)) { *destbox_r = cmd->client->mailbox; return 0; } box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_SAVEONLY); mailbox_set_reason(box, cmd->name); if (mailbox_open(box) < 0) { error_string = mailbox_get_last_error(box, &error); if (error == MAIL_ERROR_NOTFOUND) { client_send_tagline(cmd, t_strdup_printf( "NO [TRYCREATE] %s", error_string)); } else { client_send_box_error(cmd, box); } mailbox_free(&box); return -1; } if (cmd->client->enabled_features != 0) { if (mailbox_enable(box, cmd->client->enabled_features) < 0) { client_send_box_error(cmd, box); mailbox_free(&box); return -1; } } *destbox_r = box; return 0; } const char *imap_client_command_get_reason(struct client_command_context *cmd) { return cmd->args[0] == '\0' ? cmd->name : t_strdup_printf("%s %s", cmd->name, cmd->human_args); } void imap_transaction_set_cmd_reason(struct mailbox_transaction_context *trans, struct client_command_context *cmd) { mailbox_transaction_set_reason(trans, imap_client_command_get_reason(cmd)); } const char * imap_get_error_string(struct client_command_context *cmd, const char *error_string, enum mail_error error) { const char *resp_code = NULL; switch (error) { case MAIL_ERROR_NONE: break; case MAIL_ERROR_TEMP: case MAIL_ERROR_LOOKUP_ABORTED: /* BUG: shouldn't be visible here */ resp_code = IMAP_RESP_CODE_SERVERBUG; break; case MAIL_ERROR_UNAVAILABLE: resp_code = IMAP_RESP_CODE_UNAVAILABLE; break; case MAIL_ERROR_NOTPOSSIBLE: case MAIL_ERROR_PARAMS: resp_code = IMAP_RESP_CODE_CANNOT; break; case MAIL_ERROR_PERM: resp_code = IMAP_RESP_CODE_NOPERM; break; case MAIL_ERROR_NOQUOTA: resp_code = IMAP_RESP_CODE_OVERQUOTA; break; case MAIL_ERROR_NOTFOUND: if ((cmd->cmd_flags & COMMAND_FLAG_USE_NONEXISTENT) != 0) resp_code = IMAP_RESP_CODE_NONEXISTENT; break; case MAIL_ERROR_EXISTS: resp_code = IMAP_RESP_CODE_ALREADYEXISTS; break; case MAIL_ERROR_EXPUNGED: resp_code = IMAP_RESP_CODE_EXPUNGEISSUED; break; case MAIL_ERROR_INUSE: resp_code = IMAP_RESP_CODE_INUSE; break; case MAIL_ERROR_CONVERSION: case MAIL_ERROR_INVALIDDATA: break; case MAIL_ERROR_LIMIT: resp_code = IMAP_RESP_CODE_LIMIT; break; } if (resp_code == NULL || *error_string == '[') return t_strconcat("NO ", error_string, NULL); else return t_strdup_printf("NO [%s] %s", resp_code, error_string); } void client_send_list_error(struct client_command_context *cmd, struct mailbox_list *list) { const char *error_string; enum mail_error error; error_string = mailbox_list_get_last_error(list, &error); client_send_tagline(cmd, imap_get_error_string(cmd, error_string, error)); } void client_disconnect_if_inconsistent(struct client *client) { if (client->mailbox != NULL && mailbox_is_inconsistent(client->mailbox)) { /* we can't do forced CLOSE, so have to disconnect */ client_disconnect_with_error(client, "IMAP session state is inconsistent, please relogin."); } } void client_send_box_error(struct client_command_context *cmd, struct mailbox *box) { client_send_storage_error(cmd, mailbox_get_storage(box)); } void client_send_storage_error(struct client_command_context *cmd, struct mail_storage *storage) { const char *error_string; enum mail_error error; error_string = mail_storage_get_last_error(storage, &error); client_send_tagline(cmd, imap_get_error_string(cmd, error_string, error)); client_disconnect_if_inconsistent(cmd->client); } void client_send_untagged_storage_error(struct client *client, struct mail_storage *storage) { const char *error_string; enum mail_error error; error_string = mail_storage_get_last_error(storage, &error); client_send_line(client, t_strconcat("* NO ", error_string, NULL)); client_disconnect_if_inconsistent(client); } bool client_parse_mail_flags(struct client_command_context *cmd, const struct imap_arg *args, enum mail_flags *flags_r, const char *const **keywords_r) { const char *atom; enum mail_flags flag; ARRAY(const char *) keywords; *flags_r = 0; *keywords_r = NULL; p_array_init(&keywords, cmd->pool, 16); while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &atom)) { client_send_command_error(cmd, "Flags list contains non-atoms."); return FALSE; } if (*atom == '\\') { /* system flag */ atom = t_str_ucase(atom); flag = imap_parse_system_flag(atom); if (flag != 0 && flag != MAIL_RECENT) *flags_r |= flag; else { client_send_command_error(cmd, t_strconcat( "Invalid system flag ", atom, NULL)); return FALSE; } } else { /* keyword validity checks are done by lib-storage */ array_append(&keywords, &atom, 1); } args++; } if (array_count(&keywords) == 0) *keywords_r = NULL; else { array_append_zero(&keywords); /* NULL-terminate */ *keywords_r = array_idx(&keywords, 0); } return TRUE; } void client_send_mailbox_flags(struct client *client, bool selecting) { struct mailbox_status status; unsigned int count = array_count(client->keywords.names); const char *const *keywords; string_t *str; if (!selecting && count == client->keywords.announce_count) { /* no changes to keywords and we're not selecting a mailbox */ return; } client->keywords.announce_count = count; mailbox_get_open_status(client->mailbox, STATUS_PERMANENT_FLAGS, &status); keywords = count == 0 ? NULL : array_idx(client->keywords.names, 0); str = t_str_new(128); str_append(str, "* FLAGS ("); imap_write_flags(str, status.flags, keywords); str_append_c(str, ')'); client_send_line(client, str_c(str)); if (!status.permanent_keywords) keywords = NULL; str_truncate(str, 0); str_append(str, "* OK [PERMANENTFLAGS ("); imap_write_flags(str, status.permanent_flags, keywords); if (status.allow_new_keywords) { if (status.permanent_flags != 0 || keywords != NULL) str_append_c(str, ' '); str_append(str, "\\*"); } str_append(str, ")] "); if (mailbox_is_readonly(client->mailbox)) str_append(str, "Read-only mailbox."); else str_append(str, "Flags permitted."); client_send_line(client, str_c(str)); } void client_update_mailbox_flags(struct client *client, const ARRAY_TYPE(keywords) *keywords) { client->keywords.names = keywords; client->keywords.announce_count = 0; } const char *const * client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest, const ARRAY_TYPE(keyword_indexes) *src) { const unsigned int *kw_indexes; const char *const *all_names; unsigned int all_count; client_send_mailbox_flags(client, FALSE); /* convert indexes to names */ all_names = array_get(client->keywords.names, &all_count); array_clear(dest); array_foreach(src, kw_indexes) { unsigned int kw_index = *kw_indexes; i_assert(kw_index < all_count); array_append(dest, &all_names[kw_index], 1); } array_append_zero(dest); return array_idx(dest, 0); } void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str) { i_zero(ctx); ctx->str = str; ctx->last_uid = (uint32_t)-1; } void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid) { i_assert(uid > 0); if (uid-1 != ctx->last_uid) { if (ctx->first_uid == 0) ; else if (ctx->first_uid == ctx->last_uid) str_printfa(ctx->str, "%u,", ctx->first_uid); else { str_printfa(ctx->str, "%u:%u,", ctx->first_uid, ctx->last_uid); } ctx->first_uid = uid; } ctx->last_uid = uid; } void msgset_generator_finish(struct msgset_generator_context *ctx) { if (ctx->first_uid == ctx->last_uid) str_printfa(ctx->str, "%u", ctx->first_uid); else str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid); } dovecot-2.2.33.2/src/imap/imap-commands.c0000644000175000017500000001627113165463624014754 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "array.h" #include "buffer.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "imap-commands.h" struct command_hook { command_hook_callback_t *pre; command_hook_callback_t *post; }; static const struct command imap4rev1_commands[] = { { "CAPABILITY", cmd_capability, 0 }, { "LOGOUT", cmd_logout, COMMAND_FLAG_BREAKS_MAILBOX }, { "NOOP", cmd_noop, COMMAND_FLAG_BREAKS_SEQS }, { "APPEND", cmd_append, COMMAND_FLAG_BREAKS_SEQS | /* finish syncing and sending all tagged commands before we wait for APPEND input */ COMMAND_FLAG_BREAKS_MAILBOX }, { "EXAMINE", cmd_examine, COMMAND_FLAG_BREAKS_MAILBOX }, { "CREATE", cmd_create, 0 }, { "DELETE", cmd_delete, COMMAND_FLAG_BREAKS_MAILBOX | COMMAND_FLAG_USE_NONEXISTENT }, { "RENAME", cmd_rename, COMMAND_FLAG_USE_NONEXISTENT }, { "LIST", cmd_list, 0 }, { "LSUB", cmd_lsub, 0 }, { "SELECT", cmd_select, COMMAND_FLAG_BREAKS_MAILBOX }, { "STATUS", cmd_status, 0 }, { "SUBSCRIBE", cmd_subscribe, 0 }, { "UNSUBSCRIBE", cmd_unsubscribe, COMMAND_FLAG_USE_NONEXISTENT }, { "CHECK", cmd_check, COMMAND_FLAG_BREAKS_SEQS }, { "CLOSE", cmd_close, COMMAND_FLAG_BREAKS_MAILBOX }, { "COPY", cmd_copy, COMMAND_FLAG_USES_SEQS | COMMAND_FLAG_BREAKS_SEQS }, { "EXPUNGE", cmd_expunge, COMMAND_FLAG_BREAKS_SEQS }, { "FETCH", cmd_fetch, COMMAND_FLAG_USES_SEQS }, { "SEARCH", cmd_search, COMMAND_FLAG_USES_SEQS }, { "STORE", cmd_store, COMMAND_FLAG_USES_SEQS }, { "UID COPY", cmd_copy, COMMAND_FLAG_BREAKS_SEQS }, { "UID FETCH", cmd_fetch, COMMAND_FLAG_BREAKS_SEQS }, { "UID SEARCH", cmd_search, COMMAND_FLAG_BREAKS_SEQS }, { "UID STORE", cmd_store, COMMAND_FLAG_BREAKS_SEQS } }; #define IMAP4REV1_COMMANDS_COUNT N_ELEMENTS(imap4rev1_commands) static const struct command imap_ext_commands[] = { /* IMAP extensions: */ { "CANCELUPDATE", cmd_cancelupdate,0 }, { "ENABLE", cmd_enable, 0 }, { "ID", cmd_id, 0 }, { "IDLE", cmd_idle, COMMAND_FLAG_BREAKS_SEQS | COMMAND_FLAG_REQUIRES_SYNC | /* finish syncing and sending all tagged commands before IDLE is started */ COMMAND_FLAG_BREAKS_MAILBOX }, { "GETMETADATA", cmd_getmetadata, 0 }, { "SETMETADATA", cmd_setmetadata, 0 }, { "NAMESPACE", cmd_namespace, 0 }, { "NOTIFY", cmd_notify, COMMAND_FLAG_BREAKS_SEQS }, { "SORT", cmd_sort, COMMAND_FLAG_USES_SEQS }, { "THREAD", cmd_thread, COMMAND_FLAG_USES_SEQS }, { "UID EXPUNGE", cmd_uid_expunge, COMMAND_FLAG_BREAKS_SEQS }, { "MOVE", cmd_move, COMMAND_FLAG_USES_SEQS | COMMAND_FLAG_BREAKS_SEQS }, { "UID MOVE", cmd_move, COMMAND_FLAG_BREAKS_SEQS }, { "UID SORT", cmd_sort, COMMAND_FLAG_BREAKS_SEQS }, { "UID THREAD", cmd_thread, COMMAND_FLAG_BREAKS_SEQS }, { "UNSELECT", cmd_unselect, COMMAND_FLAG_BREAKS_MAILBOX }, { "X-CANCEL", cmd_x_cancel, 0 }, { "X-STATE", cmd_x_state, COMMAND_FLAG_REQUIRES_SYNC }, { "XLIST", cmd_list, 0 }, /* IMAP URLAUTH (RFC4467): */ { "GENURLAUTH", cmd_genurlauth, 0 }, { "RESETKEY", cmd_resetkey, 0 }, { "URLFETCH", cmd_urlfetch, 0 } }; #define IMAP_EXT_COMMANDS_COUNT N_ELEMENTS(imap_ext_commands) ARRAY_TYPE(command) imap_commands; static bool commands_unsorted; static ARRAY(struct command_hook) command_hooks; void command_register(const char *name, command_func_t *func, enum command_flags flags) { struct command cmd; i_zero(&cmd); cmd.name = name; cmd.func = func; cmd.flags = flags; array_append(&imap_commands, &cmd, 1); commands_unsorted = TRUE; } void command_unregister(const char *name) { const struct command *cmd; unsigned int i, count; cmd = array_get(&imap_commands, &count); for (i = 0; i < count; i++) { if (strcasecmp(cmd[i].name, name) == 0) { array_delete(&imap_commands, i, 1); return; } } i_error("Trying to unregister unknown command '%s'", name); } void command_register_array(const struct command *cmdarr, unsigned int count) { commands_unsorted = TRUE; array_append(&imap_commands, cmdarr, count); } void command_unregister_array(const struct command *cmdarr, unsigned int count) { while (count > 0) { command_unregister(cmdarr->name); count--; cmdarr++; } } void command_hook_register(command_hook_callback_t *pre, command_hook_callback_t *post) { struct command_hook hook; hook.pre = pre; hook.post = post; array_append(&command_hooks, &hook, 1); } void command_hook_unregister(command_hook_callback_t *pre, command_hook_callback_t *post) { const struct command_hook *hooks; unsigned int i, count; hooks = array_get(&command_hooks, &count); for (i = 0; i < count; i++) { if (hooks[i].pre == pre && hooks[i].post == post) { array_delete(&command_hooks, i, 1); return; } } i_panic("command_hook_unregister(): hook not registered"); } static void command_stats_start(struct client_command_context *cmd) { cmd->stats_start.timeval = ioloop_timeval; cmd->stats_start.lock_wait_usecs = file_lock_wait_get_total_usecs(); cmd->stats_start.bytes_in = i_stream_get_absolute_offset(cmd->client->input); cmd->stats_start.bytes_out = cmd->client->output->offset; } void command_stats_flush(struct client_command_context *cmd) { io_loop_time_refresh(); cmd->stats.running_usecs += timeval_diff_usecs(&ioloop_timeval, &cmd->stats_start.timeval); cmd->stats.lock_wait_usecs += file_lock_wait_get_total_usecs() - cmd->stats_start.lock_wait_usecs; cmd->stats.bytes_in += i_stream_get_absolute_offset(cmd->client->input) - cmd->stats_start.bytes_in; cmd->stats.bytes_out += cmd->client->output->offset - cmd->stats_start.bytes_out; /* allow flushing multiple times */ command_stats_start(cmd); } bool command_exec(struct client_command_context *cmd) { const struct command_hook *hook; bool finished; i_assert(!cmd->executing); io_loop_time_refresh(); command_stats_start(cmd); cmd->executing = TRUE; array_foreach(&command_hooks, hook) hook->pre(cmd); finished = cmd->func(cmd); array_foreach(&command_hooks, hook) hook->post(cmd); cmd->executing = FALSE; if (cmd->state == CLIENT_COMMAND_STATE_DONE) finished = TRUE; command_stats_flush(cmd); return finished; } static int command_cmp(const struct command *c1, const struct command *c2) { return strcasecmp(c1->name, c2->name); } static int command_bsearch(const char *name, const struct command *cmd) { return strcasecmp(name, cmd->name); } struct command *command_find(const char *name) { if (commands_unsorted) { array_sort(&imap_commands, command_cmp); commands_unsorted = FALSE; } return array_bsearch(&imap_commands, name, command_bsearch); } void commands_init(void) { i_array_init(&imap_commands, 64); i_array_init(&command_hooks, 4); commands_unsorted = FALSE; command_register_array(imap4rev1_commands, IMAP4REV1_COMMANDS_COUNT); command_register_array(imap_ext_commands, IMAP_EXT_COMMANDS_COUNT); } void commands_deinit(void) { array_free(&imap_commands); array_free(&command_hooks); } dovecot-2.2.33.2/src/imap/cmd-copy.c0000644000175000017500000001452213165463624013737 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "imap-resp-code.h" #include "imap-util.h" #include "imap-commands.h" #include "imap-search-args.h" #include #define COPY_CHECK_INTERVAL 100 static void client_send_sendalive_if_needed(struct client *client) { time_t now, last_io; if (o_stream_get_buffer_used_size(client->output) != 0) return; now = time(NULL); last_io = I_MAX(client->last_input, client->last_output); if (now - last_io > MAIL_STORAGE_STAYALIVE_SECS) { o_stream_nsend_str(client->output, "* OK Hang in there..\r\n"); o_stream_nflush(client->output); client->last_output = now; } } static int fetch_and_copy(struct client_command_context *cmd, bool move, struct mailbox_transaction_context *t, struct mailbox_transaction_context **src_trans_r, struct mail_search_args *search_args, const char **src_uidset_r, unsigned int *copy_count_r) { struct client *client = cmd->client; struct mail_search_context *search_ctx; struct mailbox_transaction_context *src_trans; struct mail_save_context *save_ctx; struct mail *mail; unsigned int copy_count = 0; struct msgset_generator_context srcset_ctx; string_t *src_uidset; int ret; src_uidset = t_str_new(256); msgset_generator_init(&srcset_ctx, src_uidset); src_trans = mailbox_transaction_begin(client->mailbox, 0); imap_transaction_set_cmd_reason(src_trans, cmd); search_ctx = mailbox_search_init(src_trans, search_args, NULL, 0, NULL); ret = 1; while (mailbox_search_next(search_ctx, &mail) && ret > 0) { if (mail->expunged) { ret = 0; break; } if ((++copy_count % COPY_CHECK_INTERVAL) == 0) client_send_sendalive_if_needed(client); save_ctx = mailbox_save_alloc(t); mailbox_save_copy_flags(save_ctx, mail); if (move) { if (mailbox_move(&save_ctx, mail) < 0) ret = -1; } else { if (mailbox_copy(&save_ctx, mail) < 0) ret = -1; } if (ret < 0 && mail->expunged) ret = 0; msgset_generator_next(&srcset_ctx, mail->uid); } msgset_generator_finish(&srcset_ctx); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; *src_trans_r = src_trans; *src_uidset_r = str_c(src_uidset); *copy_count_r = copy_count; return ret; } static void copy_update_trashed(struct client *client, struct mailbox *box, unsigned int count) { const struct mailbox_settings *set; set = mailbox_settings_find(mailbox_get_namespace(box), mailbox_get_vname(box)); if (set != NULL && set->special_use[0] != '\0' && str_array_icase_find(t_strsplit_spaces(set->special_use, " "), "\\Trash")) client->trashed_count += count; } static bool cmd_copy_full(struct client_command_context *cmd, bool move) { struct client *client = cmd->client; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t, *src_trans; struct mail_search_args *search_args; const char *messageset, *mailbox, *src_uidset; enum mailbox_sync_flags sync_flags = 0; enum imap_sync_flags imap_flags = 0; struct mail_transaction_commit_changes changes; unsigned int copy_count; string_t *msg; int ret; /* */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0) { mail_search_args_unref(&search_args); return TRUE; } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); imap_transaction_set_cmd_reason(t, cmd); ret = fetch_and_copy(cmd, move, t, &src_trans, search_args, &src_uidset, ©_count); mail_search_args_unref(&search_args); msg = t_str_new(256); if (ret <= 0) mailbox_transaction_rollback(&t); else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0) { if (mailbox_get_last_mail_error(destbox) == MAIL_ERROR_EXPUNGED) { /* storage backend didn't notice the expunge until at commit time. */ ret = 0; } else { ret = -1; } } else if (copy_count == 0) { str_append(msg, "OK No messages found."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0 || changes.no_read_perm) { /* not supported by backend (virtual) or no read permissions for mailbox */ str_append(msg, move ? "OK Move completed." : "OK Copy completed."); pool_unref(&changes.pool); } else if (move) { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "* OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Moved UIDs."); client_send_line(client, str_c(msg)); str_truncate(msg, 0); str_append(msg, "OK Move completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Copy completed."); pool_unref(&changes.pool); } if (ret <= 0 && move) { /* move failed, don't expunge anything */ mailbox_transaction_rollback(&src_trans); } else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { if (move) sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; else sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } else if (move) { sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; imap_flags |= IMAP_SYNC_FLAG_SAFE; } if (ret > 0) return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, "NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some of the requested messages no longer exist."); } else { client_send_storage_error(cmd, dest_storage); return TRUE; } } bool cmd_copy(struct client_command_context *cmd) { return cmd_copy_full(cmd, FALSE); } bool cmd_move(struct client_command_context *cmd) { return cmd_copy_full(cmd, TRUE); } dovecot-2.2.33.2/src/imap/imap-expunge.h0000644000175000017500000000031713123174404014612 00000000000000#ifndef IMAP_EXPUNGE_H #define IMAP_EXPUNGE_H struct mail_search_arg; int imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg, unsigned int *expunged_count) ATTR_NULL(2); #endif dovecot-2.2.33.2/src/imap/cmd-list.c0000644000175000017500000003306113165463624013737 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "array.h" #include "str.h" #include "strescape.h" #include "mailbox-list-iter.h" #include "imap-utf7.h" #include "imap-quote.h" #include "imap-match.h" #include "imap-status.h" #include "imap-commands.h" #include "imap-list.h" struct cmd_list_context { struct client_command_context *cmd; struct mail_user *user; enum mailbox_list_iter_flags list_flags; struct imap_status_items status_items; struct mailbox_list_iterate_context *list_iter; unsigned int lsub:1; unsigned int lsub_no_unsubscribed:1; unsigned int used_listext:1; unsigned int used_status:1; }; static void mailbox_flags2str(struct cmd_list_context *ctx, string_t *str, const char *special_use, enum mailbox_info_flags flags) { size_t orig_len = str_len(str); if ((flags & MAILBOX_NONEXISTENT) != 0 && !ctx->used_listext) { flags |= MAILBOX_NOSELECT; flags &= ~MAILBOX_NONEXISTENT; } if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN); if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 && (flags & MAILBOX_SUBSCRIBED) == 0 && !ctx->used_listext) { /* LSUB uses \Noselect for this */ flags |= MAILBOX_NOSELECT; } else if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) == 0) flags &= ~MAILBOX_SUBSCRIBED; imap_mailbox_flags2str(str, flags); if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0 && special_use != NULL) { if (str_len(str) != orig_len) str_append_c(str, ' '); str_append(str, special_use); } } static void mailbox_childinfo2str(struct cmd_list_context *ctx, string_t *str, enum mailbox_info_flags flags) { if (!ctx->used_listext) return; if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 && (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0) str_append(str, " (CHILDINFO (\"SUBSCRIBED\"))"); if ((flags & MAILBOX_CHILD_SPECIALUSE) != 0 && (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0) str_append(str, " (CHILDINFO (\"SPECIAL-USE\"))"); } static bool parse_select_flags(struct cmd_list_context *ctx, const struct imap_arg *args) { enum mailbox_list_iter_flags list_flags = 0; const char *str; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &str)) { client_send_command_error(ctx->cmd, "List options contains non-atoms."); return FALSE; } if (strcasecmp(str, "SUBSCRIBED") == 0) { list_flags |= MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED; } else if (strcasecmp(str, "RECURSIVEMATCH") == 0) list_flags |= MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH; else if (strcasecmp(str, "SPECIAL-USE") == 0) { list_flags |= MAILBOX_LIST_ITER_SELECT_SPECIALUSE | MAILBOX_LIST_ITER_RETURN_SPECIALUSE; } else if (strcasecmp(str, "REMOTE") == 0) { /* not supported, ignore */ } else { /* skip also optional list value */ client_send_command_error(ctx->cmd, "Unknown select options"); return FALSE; } args++; } if ((list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 && (list_flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_SELECT_SPECIALUSE)) == 0) { client_send_command_error(ctx->cmd, "RECURSIVEMATCH must not be the only selection."); return FALSE; } ctx->list_flags = list_flags; return TRUE; } static bool parse_return_flags(struct cmd_list_context *ctx, const struct imap_arg *args) { enum mailbox_list_iter_flags list_flags = 0; const struct imap_arg *list_args; const char *str; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &str)) { client_send_command_error(ctx->cmd, "List options contains non-atoms."); return FALSE; } if (strcasecmp(str, "SUBSCRIBED") == 0) list_flags |= MAILBOX_LIST_ITER_RETURN_SUBSCRIBED; else if (strcasecmp(str, "CHILDREN") == 0) list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN; else if (strcasecmp(str, "SPECIAL-USE") == 0) list_flags |= MAILBOX_LIST_ITER_RETURN_SPECIALUSE; else if (strcasecmp(str, "STATUS") == 0 && imap_arg_get_list(&args[1], &list_args)) { if (imap_status_parse_items(ctx->cmd, list_args, &ctx->status_items) < 0) return FALSE; ctx->used_status = TRUE; args++; } else { /* skip also optional list value */ client_send_command_error(ctx->cmd, "Unknown return options"); return FALSE; } args++; } ctx->list_flags |= list_flags; return TRUE; } static const char *ns_prefix_mutf7(struct mail_namespace *ns) { string_t *str; if (*ns->prefix == '\0') return ""; str = t_str_new(64); if (imap_utf8_to_utf7(ns->prefix, str) < 0) i_panic("Namespace prefix not UTF-8: %s", ns->prefix); return str_c(str); } static void list_reply_append_ns_sep_param(string_t *str, char sep) { str_append_c(str, '"'); if (sep == '\\') str_append(str, "\\\\"); else str_append_c(str, sep); str_append_c(str, '"'); } static void list_send_status(struct cmd_list_context *ctx, const char *name, const char *mutf7_name, enum mailbox_info_flags flags) { struct imap_status_result result; struct mail_namespace *ns; if ((flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) { /* doesn't exist, don't even try to get STATUS */ return; } if ((flags & MAILBOX_SUBSCRIBED) == 0 && (flags & MAILBOX_CHILD_SUBSCRIBED) != 0) { /* listing subscriptions, but only child is subscribed */ return; } /* if we're listing subscriptions and there are subscriptions=no namespaces, ctx->ns may not point to correct one */ ns = mail_namespace_find(ctx->user->namespaces, name); if (imap_status_get(ctx->cmd, ns, name, &ctx->status_items, &result) < 0) { client_send_line(ctx->cmd->client, t_strconcat("* ", result.errstr, NULL)); return; } imap_status_send(ctx->cmd->client, mutf7_name, &ctx->status_items, &result); } static bool cmd_list_continue(struct client_command_context *cmd) { struct cmd_list_context *ctx = cmd->context; const struct mailbox_info *info; enum mailbox_info_flags flags; string_t *str, *mutf7_name; const char *name; int ret = 0; if (cmd->cancel) { if (ctx->list_iter != NULL) (void)mailbox_list_iter_deinit(&ctx->list_iter); return TRUE; } str = t_str_new(256); mutf7_name = t_str_new(128); while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) { name = info->vname; flags = info->flags; if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 && (flags & MAILBOX_SUBSCRIBED) == 0 && ctx->lsub_no_unsubscribed) { /* mask doesn't end with %. we don't want to show any extra mailboxes. */ continue; } str_truncate(mutf7_name, 0); if (imap_utf8_to_utf7(name, mutf7_name) < 0) i_panic("LIST: Mailbox name not UTF-8: %s", name); str_truncate(str, 0); str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST"); mailbox_flags2str(ctx, str, info->special_use, flags); str_append(str, ") "); list_reply_append_ns_sep_param(str, mail_namespace_get_sep(info->ns)); str_append_c(str, ' '); imap_append_astring(str, str_c(mutf7_name)); mailbox_childinfo2str(ctx, str, flags); ret = client_send_line_next(ctx->cmd->client, str_c(str)); if (ctx->used_status) T_BEGIN { list_send_status(ctx, name, str_c(mutf7_name), flags); } T_END; if (ret == 0) { /* buffer is full, continue later */ return FALSE; } } if (mailbox_list_iter_deinit(&ctx->list_iter) < 0) { client_send_list_error(cmd, ctx->user->namespaces->list); return TRUE; } client_send_tagline(cmd, !ctx->lsub ? "OK List completed." : "OK Lsub completed."); return TRUE; } static const char *const * list_get_ref_patterns(struct cmd_list_context *ctx, const char *ref, const char *const *patterns) { struct mail_namespace *ns; const char *const *pat, *pattern; ARRAY(const char *) full_patterns; if (*ref == '\0') return patterns; ns = mail_namespace_find(ctx->user->namespaces, ref); t_array_init(&full_patterns, 16); for (pat = patterns; *pat != NULL; pat++) { pattern = mailbox_list_join_refpattern(ns->list, ref, *pat); array_append(&full_patterns, &pattern, 1); } array_append_zero(&full_patterns); /* NULL-terminate */ return array_idx(&full_patterns, 0); } static void cmd_list_init(struct cmd_list_context *ctx, const char *const *patterns) { enum mail_namespace_type type_mask = MAIL_NAMESPACE_TYPE_MASK_ALL; ctx->list_iter = mailbox_list_iter_init_namespaces(ctx->user->namespaces, patterns, type_mask, ctx->list_flags); } static void cmd_list_ref_root(struct client *client, const char *ref) { struct mail_namespace *ns; const char *ns_prefix; char ns_sep; string_t *str; /* Special request to return the hierarchy delimiter and mailbox root name. If namespace has a prefix, it's returned as the mailbox root. Otherwise we'll emulate UW-IMAP behavior. */ ns = mail_namespace_find_visible(client->user->namespaces, ref); if (ns != NULL) { ns_prefix = ns_prefix_mutf7(ns); ns_sep = mail_namespace_get_sep(ns); } else { ns_prefix = ""; ns_sep = mail_namespaces_get_root_sep(client->user->namespaces); } str = t_str_new(64); str_append(str, "* LIST (\\Noselect) \""); if (ns_sep == '\\' || ns_sep == '"') str_append_c(str, '\\'); str_printfa(str, "%c\" ", ns_sep); if (*ns_prefix != '\0') { /* non-hidden namespace, use it as the root name */ imap_append_astring(str, ns_prefix); } else { /* Hidden namespace or empty namespace prefix. We could just return an empty root name, but it's safer to emulate what UW-IMAP does. With full filesystem access this might even matter (root of "~user/mail/" is "~user/", not "") */ const char *p = strchr(ref, ns_sep); if (p == NULL) str_append(str, "\"\""); else imap_append_astring(str, t_strdup_until(ref, p + 1)); } client_send_line(client, str_c(str)); } bool cmd_list_full(struct client_command_context *cmd, bool lsub) { struct client *client = cmd->client; const struct imap_arg *args, *list_args; unsigned int arg_count; struct cmd_list_context *ctx; ARRAY(const char *) patterns = ARRAY_INIT; const char *ref, *pattern, *const *patterns_strarr; string_t *str; /* [()] |() [RETURN ()] */ if (!client_read_args(cmd, 0, 0, &args)) return FALSE; ctx = p_new(cmd->pool, struct cmd_list_context, 1); ctx->cmd = cmd; ctx->lsub = lsub; ctx->user = client->user; cmd->context = ctx; if (!lsub && imap_arg_get_list(&args[0], &list_args)) { /* LIST-EXTENDED selection options */ ctx->used_listext = TRUE; if (!parse_select_flags(ctx, list_args)) return TRUE; args++; } if (!imap_arg_get_astring(&args[0], &ref)) { client_send_command_error(cmd, "Invalid reference."); return TRUE; } str = t_str_new(64); if (imap_utf7_to_utf8(ref, str) == 0) ref = p_strdup(cmd->pool, str_c(str)); str_truncate(str, 0); if (imap_arg_get_list_full(&args[1], &list_args, &arg_count)) { ctx->used_listext = TRUE; /* convert pattern list to string array */ p_array_init(&patterns, cmd->pool, arg_count); for (; !IMAP_ARG_IS_EOL(list_args); list_args++) { if (!imap_arg_get_astring(list_args, &pattern)) { client_send_command_error(cmd, "Invalid pattern list."); return TRUE; } if (imap_utf7_to_utf8(pattern, str) == 0) pattern = p_strdup(cmd->pool, str_c(str)); array_append(&patterns, &pattern, 1); str_truncate(str, 0); } args += 2; } else { if (!imap_arg_get_astring(&args[1], &pattern)) { client_send_command_error(cmd, "Invalid pattern."); return TRUE; } if (imap_utf7_to_utf8(pattern, str) == 0) pattern = p_strdup(cmd->pool, str_c(str)); p_array_init(&patterns, cmd->pool, 1); array_append(&patterns, &pattern, 1); args += 2; if (lsub) { size_t len = strlen(pattern); ctx->lsub_no_unsubscribed = len == 0 || pattern[len-1] != '%'; } } if (imap_arg_atom_equals(&args[0], "RETURN") && imap_arg_get_list(&args[1], &list_args)) { /* LIST-EXTENDED return options */ ctx->used_listext = TRUE; if (!parse_return_flags(ctx, list_args)) return TRUE; args += 2; } if (lsub) { /* LSUB - we don't care about flags except if tb-lsub-flags workaround is explicitly set */ ctx->list_flags |= MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH; /* Return SPECIAL-USE flags for LSUB anyway. Outlook 2013 does this and since it's not expensive for us to return them, it's not worth the trouble of adding an explicit workaround setting. */ ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_SPECIALUSE; if ((cmd->client->set->parsed_workarounds & WORKAROUND_TB_LSUB_FLAGS) == 0) ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_NO_FLAGS; } else if (!ctx->used_listext) { /* non-extended LIST: use default flags */ ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN | MAILBOX_LIST_ITER_RETURN_SPECIALUSE; } if (!IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Extra arguments."); return TRUE; } array_append_zero(&patterns); /* NULL-terminate */ patterns_strarr = array_idx(&patterns, 0); if (!ctx->used_listext && !lsub && *patterns_strarr[0] == '\0') { /* Only LIST ref "" gets us here */ cmd_list_ref_root(client, ref); client_send_tagline(cmd, "OK List completed."); } else { patterns_strarr = list_get_ref_patterns(ctx, ref, patterns_strarr); cmd_list_init(ctx, patterns_strarr); if (!cmd_list_continue(cmd)) { /* unfinished */ cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; cmd->func = cmd_list_continue; return FALSE; } cmd->context = NULL; return TRUE; } return TRUE; } bool cmd_list(struct client_command_context *cmd) { return cmd_list_full(cmd, FALSE); } dovecot-2.2.33.2/src/imap/cmd-notify.c0000644000175000017500000004155013144653606014274 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "mailbox-list-iter.h" #include "imap-quote.h" #include "imap-commands.h" #include "imap-fetch.h" #include "imap-list.h" #include "imap-status.h" #include "imap-notify.h" #define IMAP_NOTIFY_MAX_NAMES_PER_NS 100 static const char *imap_notify_event_names[] = { "MessageNew", "MessageExpunge", "FlagChange", "AnnotationChange", "MailboxName", "SubscriptionChange", "MailboxMetadataChange", "ServerMetadataChange" }; static int cmd_notify_parse_event(const struct imap_arg *arg, enum imap_notify_event *event_r) { const char *str; unsigned int i; if (!imap_arg_get_atom(arg, &str)) return -1; for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) { if (strcasecmp(str, imap_notify_event_names[i]) == 0) { *event_r = (enum imap_notify_event)(1 << i); return 0; } } return -1; } static int cmd_notify_parse_fetch(struct imap_notify_context *ctx, const struct imap_arg *list) { if (list->type == IMAP_ARG_EOL) return -1; /* at least one attribute must be set */ return imap_fetch_att_list_parse(ctx->client, ctx->pool, list, &ctx->fetch_ctx, &ctx->error); } static int cmd_notify_set_selected(struct imap_notify_context *ctx, const struct imap_arg *events) { #define EV_NEW_OR_EXPUNGE \ (IMAP_NOTIFY_EVENT_MESSAGE_NEW | IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) const struct imap_arg *list, *fetch_att_list; const char *str; enum imap_notify_event event; if (imap_arg_get_atom(events, &str) && strcasecmp(str, "NONE") == 0) { /* no events for selected mailbox. this is also the default when NOTIFY command doesn't specify it explicitly */ if (events[1].type != IMAP_ARG_EOL) return -1; /* no extra parameters */ return 0; } if (!imap_arg_get_list(events, &list)) return -1; if (events[1].type != IMAP_ARG_EOL) return -1; /* no extra parameters */ if (list->type == IMAP_ARG_EOL) return -1; /* at least one event */ for (; list->type != IMAP_ARG_EOL; list++) { if (cmd_notify_parse_event(list, &event) < 0) return -1; ctx->selected_events |= event; ctx->global_used_events |= event; if (event == IMAP_NOTIFY_EVENT_MESSAGE_NEW && imap_arg_get_list(&list[1], &fetch_att_list)) { /* MessageNew: list of fetch-att */ if (cmd_notify_parse_fetch(ctx, fetch_att_list) < 0) return -1; list++; } } /* if MessageNew or MessageExpunge is specified, both of them must */ if ((ctx->selected_events & EV_NEW_OR_EXPUNGE) != 0 && (ctx->selected_events & EV_NEW_OR_EXPUNGE) != EV_NEW_OR_EXPUNGE) { ctx->error = "MessageNew and MessageExpunge must be together"; return -1; } /* if FlagChange or AnnotationChange is specified, MessageNew and MessageExpunge must also be specified */ if ((ctx->selected_events & (IMAP_NOTIFY_EVENT_FLAG_CHANGE | IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0 && (ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) == 0) { ctx->error = "FlagChange requires MessageNew and MessageExpunge"; return -1; } return 0; } static struct imap_notify_namespace * imap_notify_namespace_get(struct imap_notify_context *ctx, struct mail_namespace *ns) { struct imap_notify_namespace *notify_ns; array_foreach_modifiable(&ctx->namespaces, notify_ns) { if (notify_ns->ns == ns) return notify_ns; } notify_ns = array_append_space(&ctx->namespaces); notify_ns->ctx = ctx; notify_ns->ns = ns; p_array_init(¬ify_ns->mailboxes, ctx->pool, 4); return notify_ns; } static struct imap_notify_mailboxes * imap_notify_mailboxes_get(struct imap_notify_namespace *notify_ns, enum imap_notify_type type, enum imap_notify_event events) { struct imap_notify_mailboxes *notify_boxes; array_foreach_modifiable(¬ify_ns->mailboxes, notify_boxes) { if (notify_boxes->type == type && notify_boxes->events == events) return notify_boxes; } notify_boxes = array_append_space(¬ify_ns->mailboxes); notify_boxes->type = type; notify_boxes->events = events; p_array_init(¬ify_boxes->names, notify_ns->ctx->pool, 4); return notify_boxes; } static void cmd_notify_add_mailbox(struct imap_notify_context *ctx, struct mail_namespace *ns, const char *name, enum imap_notify_type type, enum imap_notify_event events) { struct imap_notify_namespace *notify_ns; struct imap_notify_mailboxes *notify_boxes; const char *const *names; unsigned int i, count; size_t cur_len, name_len = strlen(name); char ns_sep = mail_namespace_get_sep(ns); if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && strncmp(name, "INBOX", 5) != 0 && strncasecmp(name, "INBOX", 5) == 0 && (name[5] == '\0' || name[5] == ns_sep)) { /* we'll do only case-sensitive comparisons later, so sanitize INBOX to be uppercase */ name = t_strconcat("INBOX", name + 5, NULL); } notify_ns = imap_notify_namespace_get(ctx, ns); notify_boxes = imap_notify_mailboxes_get(notify_ns, type, events); names = array_get(¬ify_boxes->names, &count); for (i = 0; i < count; ) { if (strcmp(names[i], name) == 0) { /* exact duplicate, already added */ return; } if (type != IMAP_NOTIFY_TYPE_SUBTREE) i++; else { /* see if one is a subtree of the other */ cur_len = strlen(names[i]); if (strncmp(names[i], name, cur_len) == 0 && names[i][cur_len] == ns_sep) { /* already matched in this subtree */ return; } if (strncmp(names[i], name, name_len) == 0 && names[i][name_len] == ns_sep) { /* we're adding a parent, remove the child */ array_delete(¬ify_boxes->names, i, 1); names = array_get(¬ify_boxes->names, &count); } else { i++; } } } name = p_strdup(ctx->pool, name); array_append(¬ify_boxes->names, &name, 1); ctx->global_max_mailbox_names = I_MAX(ctx->global_max_mailbox_names, array_count(¬ify_boxes->names)); } static void cmd_notify_add_personal(struct imap_notify_context *ctx, enum imap_notify_event events) { struct mail_namespace *ns; for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) { if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) { cmd_notify_add_mailbox(ctx, ns, "", IMAP_NOTIFY_TYPE_SUBTREE, events); } } } static int imap_notify_refresh_subscriptions(struct client_command_context *cmd, struct imap_notify_context *ctx) { struct mailbox_list_iterate_context *iter; struct mail_namespace *ns; if (!ctx->have_subscriptions) return 0; /* make sure subscriptions are refreshed at least once */ for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) { iter = mailbox_list_iter_init(ns->list, "*", MAILBOX_LIST_ITER_SELECT_SUBSCRIBED); (void)mailbox_list_iter_next(iter); if (mailbox_list_iter_deinit(&iter) < 0) { client_send_list_error(cmd, ns->list); return -1; } } return 0; } static void cmd_notify_add_subscribed(struct imap_notify_context *ctx, enum imap_notify_event events) { struct mail_namespace *ns; ctx->have_subscriptions = TRUE; for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) { cmd_notify_add_mailbox(ctx, ns, "", IMAP_NOTIFY_TYPE_SUBSCRIBED, events); } } static void cmd_notify_add_mailbox_namespaces(struct imap_notify_context *ctx, const char *name, enum imap_notify_type type, enum imap_notify_event events) { struct mail_namespace *ns; ns = mail_namespace_find(ctx->client->user->namespaces, name); cmd_notify_add_mailbox(ctx, ns, name, type, events); } static int cmd_notify_add_mailboxes(struct imap_notify_context *ctx, const struct imap_arg *arg, enum imap_notify_type type, enum imap_notify_event events) { const struct imap_arg *list; const char *name; if (imap_arg_get_astring(arg, &name)) { cmd_notify_add_mailbox_namespaces(ctx, name, type, events); return 0; } if (!imap_arg_get_list(arg, &list)) return -1; for (; list->type != IMAP_ARG_EOL; list++) { if (!imap_arg_get_astring(list, &name)) return -1; cmd_notify_add_mailbox_namespaces(ctx, name, type, events); } return 0; } static int cmd_notify_set(struct imap_notify_context *ctx, const struct imap_arg *args) { const struct imap_arg *event_group, *mailboxes, *list; const char *str, *filter_mailboxes; enum imap_notify_event event, event_mask; if (imap_arg_get_atom(args, &str) && strcasecmp(str, "STATUS") == 0) { /* send STATUS replies for all matched mailboxes before NOTIFY's OK reply */ ctx->send_immediate_status = TRUE; args++; } for (; args->type != IMAP_ARG_EOL; args++) { if (!imap_arg_get_list(args, &event_group)) return -1; /* filter-mailboxes */ if (!imap_arg_get_atom(event_group, &filter_mailboxes)) return -1; event_group++; if (strcasecmp(filter_mailboxes, "selected") == 0 || strcasecmp(filter_mailboxes, "selected-delayed") == 0) { /* setting events for selected mailbox. handle specially. */ if (ctx->selected_set) { ctx->error = "Duplicate selected filter"; return -1; } ctx->selected_set = TRUE; if (strcasecmp(filter_mailboxes, "selected") == 0) ctx->selected_immediate_expunges = TRUE; if (cmd_notify_set_selected(ctx, event_group) < 0) return -1; continue; } if (strcasecmp(filter_mailboxes, "subtree") == 0 || strcasecmp(filter_mailboxes, "mailboxes") == 0) { if (event_group->type == IMAP_ARG_EOL) return -1; mailboxes = event_group++; /* check that the mailboxes parameter is valid */ if (IMAP_ARG_IS_ASTRING(mailboxes)) ; else if (!imap_arg_get_list(mailboxes, &list)) return -1; else if (list->type == IMAP_ARG_EOL) { /* should have at least one mailbox */ return -1; } } else { mailboxes = NULL; } /* parse events */ if (imap_arg_get_atom(event_group, &str) && strcasecmp(str, "NONE") == 0) { /* NONE is the default, ignore this */ continue; } if (!imap_arg_get_list(event_group, &list) || list[0].type == IMAP_ARG_EOL) return -1; event_mask = 0; for (; list->type != IMAP_ARG_EOL; list++) { if (cmd_notify_parse_event(list, &event) < 0) return -1; event_mask |= event; ctx->global_used_events |= event; } /* we can't currently know inboxes, so treat it the same as personal */ if (strcasecmp(filter_mailboxes, "inboxes") == 0 || strcasecmp(filter_mailboxes, "personal") == 0) cmd_notify_add_personal(ctx, event_mask); else if (strcasecmp(filter_mailboxes, "subscribed") == 0) cmd_notify_add_subscribed(ctx, event_mask); else if (strcasecmp(filter_mailboxes, "subtree") == 0) { if (cmd_notify_add_mailboxes(ctx, mailboxes, IMAP_NOTIFY_TYPE_SUBTREE, event_mask) < 0) return -1; } else if (strcasecmp(filter_mailboxes, "mailboxes") == 0) { if (cmd_notify_add_mailboxes(ctx, mailboxes, IMAP_NOTIFY_TYPE_MAILBOX, event_mask) < 0) return -1; } else { return -1; } } return 0; } static void imap_notify_box_list_noperm(struct client *client, struct mailbox *box) { string_t *str = t_str_new(128); char ns_sep = mail_namespace_get_sep(mailbox_get_namespace(box)); enum mailbox_info_flags mailbox_flags; if (mailbox_list_mailbox(mailbox_get_namespace(box)->list, mailbox_get_name(box), &mailbox_flags) < 0) mailbox_flags = 0; str_append(str, "* LIST ("); if (imap_mailbox_flags2str(str, mailbox_flags)) str_append_c(str, ' '); str_append(str, "\\NoAccess) \""); if (ns_sep == '\\') str_append_c(str, '\\'); str_append_c(str, ns_sep); str_append(str, "\" "); imap_append_astring(str, mailbox_get_vname(box)); client_send_line(client, str_c(str)); } static void imap_notify_box_send_status(struct client_command_context *cmd, struct imap_notify_context *ctx, const struct mailbox_info *info) { struct mailbox *box; struct imap_status_items items; struct imap_status_result result; if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) return; /* don't send STATUS to selected mailbox */ if (cmd->client->mailbox != NULL && mailbox_equals(cmd->client->mailbox, info->ns, info->vname)) return; i_zero(&items); i_zero(&result); items.status = STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN; if ((ctx->global_used_events & (IMAP_NOTIFY_EVENT_FLAG_CHANGE | IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0) items.status |= STATUS_HIGHESTMODSEQ; box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY); mailbox_set_reason(box, "NOTIFY send STATUS"); if (ctx->client->enabled_features != 0) (void)mailbox_enable(box, ctx->client->enabled_features); if (imap_status_get(cmd, info->ns, info->vname, &items, &result) < 0) { if (result.error == MAIL_ERROR_PERM) imap_notify_box_list_noperm(ctx->client, box); else if (result.error != MAIL_ERROR_NOTFOUND) { client_send_line(ctx->client, t_strconcat("* ", result.errstr, NULL)); } } else { imap_status_send(ctx->client, info->vname, &items, &result); } mailbox_free(&box); } static bool imap_notify_ns_want_status(struct imap_notify_namespace *notify_ns) { const struct imap_notify_mailboxes *notify_boxes; array_foreach(¬ify_ns->mailboxes, notify_boxes) { if ((notify_boxes->events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW | IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE | IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE | IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0) return TRUE; } return FALSE; } static void imap_notify_ns_send_status(struct client_command_context *cmd, struct imap_notify_context *ctx, struct imap_notify_namespace *notify_ns) { struct mailbox_list_iterate_context *iter; const struct imap_notify_mailboxes *notify_boxes; const struct mailbox_info *info; if (!imap_notify_ns_want_status(notify_ns)) return; /* set _RETURN_SUBSCRIBED flag just in case IMAP_NOTIFY_TYPE_SUBSCRIBED is used, which requires refreshing subscriptions */ iter = mailbox_list_iter_init(notify_ns->ns->list, "*", MAILBOX_LIST_ITER_RETURN_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { array_foreach(¬ify_ns->mailboxes, notify_boxes) { if (imap_notify_match_mailbox(notify_ns, notify_boxes, info->vname)) { imap_notify_box_send_status(cmd, ctx, info); break; } } } if (mailbox_list_iter_deinit(&iter) < 0) { client_send_line(notify_ns->ctx->client, "* NO Mailbox listing failed"); } } static void cmd_notify_send_status(struct client_command_context *cmd, struct imap_notify_context *ctx) { struct imap_notify_namespace *notify_ns; array_foreach_modifiable(&ctx->namespaces, notify_ns) imap_notify_ns_send_status(cmd, ctx, notify_ns); } bool cmd_notify(struct client_command_context *cmd) { struct imap_notify_context *ctx; const struct imap_arg *args; const char *str; int ret = 0; pool_t pool; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; pool = pool_alloconly_create("imap notify context", 1024); ctx = p_new(pool, struct imap_notify_context, 1); ctx->pool = pool; ctx->client = cmd->client; p_array_init(&ctx->namespaces, pool, 4); if (!imap_arg_get_atom(&args[0], &str)) ret = -1; else if (strcasecmp(str, "NONE") == 0) ; else if (strcasecmp(str, "SET") == 0) ret = cmd_notify_set(ctx, args+1); else ret = -1; if (ret < 0) { client_send_command_error(cmd, ctx->error != NULL ? ctx->error : "Invalid arguments."); pool_unref(&pool); return TRUE; } if ((ctx->global_used_events & UNSUPPORTED_EVENTS) != 0) { string_t *str = t_str_new(128); unsigned int i; str_append(str, "NO [BADEVENT"); for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) { if ((ctx->global_used_events & (1 << i)) != 0 && ((1 << i) & UNSUPPORTED_EVENTS) != 0) { str_append_c(str, ' '); str_append(str, imap_notify_event_names[i]); } } str_append(str, "] Unsupported NOTIFY events."); client_send_tagline(cmd, str_c(str)); pool_unref(&pool); return TRUE; } if (array_count(&ctx->namespaces) == 0) { /* selected mailbox only */ } else if (ctx->global_max_mailbox_names > IMAP_NOTIFY_MAX_NAMES_PER_NS) { client_send_tagline(cmd, "NO [NOTIFICATIONOVERFLOW] Too many mailbox names"); pool_unref(&pool); return TRUE; } else if (imap_notify_refresh_subscriptions(cmd, ctx) < 0) { /* tagline already sent */ pool_unref(&pool); return TRUE; } else if (imap_notify_begin(ctx) < 0) { client_send_tagline(cmd, "NO [NOTIFICATIONOVERFLOW] NOTIFY not supported for these mailboxes."); pool_unref(&pool); return TRUE; } if (cmd->client->notify_ctx != NULL) imap_notify_deinit(&cmd->client->notify_ctx); if (ctx->send_immediate_status) cmd_notify_send_status(cmd, ctx); cmd->client->notify_immediate_expunges = ctx->selected_immediate_expunges; cmd->client->notify_count_changes = (ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0; cmd->client->notify_flag_changes = (ctx->selected_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0; cmd->client->notify_ctx = ctx; return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, "OK NOTIFY completed."); } dovecot-2.2.33.2/src/imap/cmd-idle.c0000644000175000017500000002006213165463624013676 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "istream.h" #include "ostream.h" #include "crc32.h" #include "mail-storage-settings.h" #include "imap-commands.h" #include "imap-keepalive.h" #include "imap-sync.h" struct cmd_idle_context { struct client *client; struct client_command_context *cmd; struct imap_sync_context *sync_ctx; struct timeout *keepalive_to, *to_hibernate; unsigned int manual_cork:1; unsigned int sync_pending:1; }; static void idle_add_keepalive_timeout(struct cmd_idle_context *ctx); static bool cmd_idle_continue(struct client_command_context *cmd); static void idle_finish(struct cmd_idle_context *ctx, bool done_ok, bool free_cmd) { struct client *client = ctx->client; if (ctx->keepalive_to != NULL) timeout_remove(&ctx->keepalive_to); if (ctx->to_hibernate != NULL) timeout_remove(&ctx->to_hibernate); if (ctx->sync_ctx != NULL) { /* we're here only in connection failure cases */ (void)imap_sync_deinit(ctx->sync_ctx, ctx->cmd); } o_stream_cork(client->output); if (client->io != NULL) io_remove(&client->io); if (client->mailbox != NULL) mailbox_notify_changes_stop(client->mailbox); if (done_ok) client_send_tagline(ctx->cmd, "OK Idle completed."); else client_send_tagline(ctx->cmd, "BAD Expected DONE."); o_stream_uncork(client->output); if (free_cmd) client_command_free(&ctx->cmd); } static bool idle_client_handle_input(struct cmd_idle_context *ctx, bool free_cmd) { const char *line; while ((line = i_stream_next_line(ctx->client->input)) != NULL) { if (ctx->client->input_skip_line) ctx->client->input_skip_line = FALSE; else { idle_finish(ctx, strcasecmp(line, "DONE") == 0, free_cmd); return TRUE; } } return FALSE; } static bool idle_client_input_more(struct cmd_idle_context *ctx) { struct client *client = ctx->client; client->last_input = ioloop_time; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -1: /* disconnected */ client_disconnect(client, NULL); return TRUE; case -2: client->input_skip_line = TRUE; idle_finish(ctx, FALSE, TRUE); return TRUE; } if (ctx->sync_ctx != NULL) { /* we're still sending output to client. wait until it's all sent so we don't lose any changes. */ io_remove(&client->io); return FALSE; } return idle_client_handle_input(ctx, TRUE); } static void idle_client_input(struct cmd_idle_context *ctx) { struct client *client = ctx->client; if (idle_client_input_more(ctx)) { if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); } } static void keepalive_timeout(struct cmd_idle_context *ctx) { if (ctx->client->output_cmd_lock != NULL) { /* it's busy sending output */ return; } if (o_stream_get_buffer_used_size(ctx->client->output) == 0) { /* Sending this keeps NATs/stateful firewalls alive. Sending this also catches dead connections. Don't send anything if there is already data waiting in output buffer. */ o_stream_cork(ctx->client->output); client_send_line(ctx->client, "* OK Still here"); o_stream_uncork(ctx->client->output); } /* Make sure idling connections don't get disconnected. There are several clients that really want to IDLE forever and there's not much harm in letting them do so. */ timeout_reset(ctx->client->to_idle); /* recalculate time for the next keepalive timeout */ idle_add_keepalive_timeout(ctx); } static bool idle_sync_now(struct mailbox *box, struct cmd_idle_context *ctx) { i_assert(ctx->sync_ctx == NULL); if (ctx->to_hibernate != NULL) { /* hibernation can't happen while sync is running. the timeout is added back afterwards. */ timeout_remove(&ctx->to_hibernate); } ctx->sync_pending = FALSE; ctx->sync_ctx = imap_sync_init(ctx->client, box, 0, 0); return cmd_idle_continue(ctx->cmd); } static void idle_callback(struct mailbox *box, struct cmd_idle_context *ctx) { struct client *client = ctx->client; if (ctx->sync_ctx != NULL) ctx->sync_pending = TRUE; else { ctx->manual_cork = TRUE; (void)idle_sync_now(box, ctx); if (client->disconnected) client_destroy(client, NULL); } } static void idle_add_keepalive_timeout(struct cmd_idle_context *ctx) { unsigned int interval = ctx->client->set->imap_idle_notify_interval; if (interval == 0) return; interval = imap_keepalive_interval_msecs(ctx->client->user->username, ctx->client->user->remote_ip, interval); if (ctx->keepalive_to != NULL) timeout_remove(&ctx->keepalive_to); ctx->keepalive_to = timeout_add(interval, keepalive_timeout, ctx); } static void idle_hibernate_timeout(struct cmd_idle_context *ctx) { struct client *client = ctx->client; i_assert(ctx->sync_ctx == NULL); i_assert(!ctx->sync_pending); if (imap_client_hibernate(&client)) { /* client may be destroyed now */ } else { /* failed - don't bother retrying */ timeout_remove(&ctx->to_hibernate); } } static void idle_add_hibernate_timeout(struct cmd_idle_context *ctx) { unsigned int secs = ctx->client->set->imap_hibernate_timeout; i_assert(ctx->to_hibernate == NULL); if (secs == 0) return; ctx->to_hibernate = timeout_add(secs * 1000, idle_hibernate_timeout, ctx); } static bool cmd_idle_continue(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_idle_context *ctx = cmd->context; uoff_t orig_offset = client->output->offset; if (cmd->cancel) { idle_finish(ctx, FALSE, FALSE); return TRUE; } if (ctx->to_hibernate != NULL) timeout_reset(ctx->to_hibernate); if (ctx->manual_cork) { /* we're coming from idle_callback instead of a normal I/O handler, so we'll have to do corking manually */ o_stream_cork(client->output); } if (ctx->sync_ctx != NULL) { if (imap_sync_more(ctx->sync_ctx) == 0) { /* unfinished */ if (ctx->manual_cork) { ctx->manual_cork = FALSE; o_stream_uncork(client->output); } cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; return FALSE; } if (imap_sync_deinit(ctx->sync_ctx, ctx->cmd) < 0) { client_send_untagged_storage_error(client, mailbox_get_storage(client->mailbox)); mailbox_notify_changes_stop(client->mailbox); } ctx->sync_ctx = NULL; } if (client->output->offset != orig_offset && ctx->keepalive_to != NULL) idle_add_keepalive_timeout(ctx); if (ctx->sync_pending) { /* more changes occurred while we were sending changes to client. NOTE: this recurses back to this function, so we return here instead of doing everything twice. */ return idle_sync_now(client->mailbox, ctx); } if (ctx->to_hibernate == NULL) idle_add_hibernate_timeout(ctx); cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; if (ctx->manual_cork) { ctx->manual_cork = FALSE; o_stream_uncork(client->output); } if (client->output->closed) { idle_finish(ctx, FALSE, FALSE); return TRUE; } if (client->io == NULL) { /* input is pending. add the io back and mark the input as pending. we can't safely read more input immediately here. */ client->io = io_add_istream(client->input, idle_client_input, ctx); i_stream_set_input_pending(client->input, TRUE); } return FALSE; } bool cmd_idle(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_idle_context *ctx; ctx = p_new(cmd->pool, struct cmd_idle_context, 1); ctx->cmd = cmd; ctx->client = client; idle_add_keepalive_timeout(ctx); idle_add_hibernate_timeout(ctx); if (client->mailbox != NULL) mailbox_notify_changes(client->mailbox, idle_callback, ctx); if (!client->state_import_idle_continue) client_send_line(client, "+ idling"); else { /* continuing an IDLE after hibernation */ client->state_import_idle_continue = FALSE; } io_remove(&client->io); client->io = io_add_istream(client->input, idle_client_input, ctx); cmd->func = cmd_idle_continue; cmd->context = ctx; /* check immediately if there are changes. if they came before we added mailbox-notifier, we wouldn't see them otherwise. */ if (client->mailbox != NULL) idle_sync_now(client->mailbox, ctx); return idle_client_handle_input(ctx, FALSE); } dovecot-2.2.33.2/src/imap/imap-commands-util.h0000644000175000017500000000622213123174404015714 00000000000000#ifndef IMAP_COMMANDS_UTIL_H #define IMAP_COMMANDS_UTIL_H struct msgset_generator_context { string_t *str; uint32_t first_uid, last_uid; }; struct mail_full_flags; struct mailbox_keywords; /* Finds namespace for given mailbox from namespaces. If namespace isn't found or mailbox name is invalid, sends a tagged NO reply to client. */ struct mail_namespace * client_find_namespace(struct client_command_context *cmd, const char **mailbox); struct mail_namespace * client_find_namespace_full(struct client *client, const char **mailbox, const char **error_r); /* Returns TRUE if mailbox is selected. If not, sends "No mailbox selected" error message to client. */ bool client_verify_open_mailbox(struct client_command_context *cmd); /* Close the selected mailbox. */ void imap_client_close_mailbox(struct client *client); /* Open APPEND/COPY destination mailbox. */ int client_open_save_dest_box(struct client_command_context *cmd, const char *name, struct mailbox **destbox_r); /* Returns string based in IMAP command name and parameters. */ const char *imap_client_command_get_reason(struct client_command_context *cmd); /* Set transaction's reason to the IMAP command name and parameters. */ void imap_transaction_set_cmd_reason(struct mailbox_transaction_context *trans, struct client_command_context *cmd); const char * imap_get_error_string(struct client_command_context *cmd, const char *error_string, enum mail_error error); void client_disconnect_if_inconsistent(struct client *client); /* Send last mailbox list error message to client. */ void client_send_list_error(struct client_command_context *cmd, struct mailbox_list *list); /* Send last mail storage error message to client. */ void client_send_storage_error(struct client_command_context *cmd, struct mail_storage *storage); /* Send last mailbox's storage error message to client. */ void client_send_box_error(struct client_command_context *cmd, struct mailbox *box); /* Send untagged error message to client. */ void client_send_untagged_storage_error(struct client *client, struct mail_storage *storage); /* Parse flags. Returns TRUE if successful, if not sends an error message to client. */ bool client_parse_mail_flags(struct client_command_context *cmd, const struct imap_arg *args, enum mail_flags *flags_r, const char *const **keywords_r); /* Send FLAGS + PERMANENTFLAGS to client if they have changed, or if selecting=TRUE. */ void client_send_mailbox_flags(struct client *client, bool selecting); /* Update client->keywords array. Use keywords=NULL when unselecting. */ void client_update_mailbox_flags(struct client *client, const ARRAY_TYPE(keywords) *keywords) ATTR_NULL(2); /* Convert keyword indexes to keyword names in selected mailbox. */ const char *const * client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest, const ARRAY_TYPE(keyword_indexes) *src); void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str); void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid); void msgset_generator_finish(struct msgset_generator_context *ctx); #endif dovecot-2.2.33.2/src/imap/cmd-status.c0000644000175000017500000000267313123174404014301 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-resp-code.h" #include "imap-commands.h" #include "imap-sync.h" #include "imap-status.h" bool cmd_status(struct client_command_context *cmd) { struct client *client = cmd->client; const struct imap_arg *args, *list_args; struct imap_status_items items; struct imap_status_result result; struct mail_namespace *ns; const char *mailbox, *orig_mailbox; bool selected_mailbox; /* */ if (!client_read_args(cmd, 2, 0, &args)) return FALSE; if (!imap_arg_get_astring(&args[0], &mailbox) || !imap_arg_get_list(&args[1], &list_args)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } /* get the items client wants */ if (imap_status_parse_items(cmd, list_args, &items) < 0) return TRUE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; selected_mailbox = client->mailbox != NULL && mailbox_equals(client->mailbox, ns, mailbox); if (imap_status_get(cmd, ns, mailbox, &items, &result) < 0) { client_send_tagline(cmd, result.errstr); return TRUE; } imap_status_send(client, orig_mailbox, &items, &result); if (!selected_mailbox) client_send_tagline(cmd, "OK Status completed."); else { client_send_tagline(cmd, "OK ["IMAP_RESP_CODE_CLIENTBUG"] " "Status on selected mailbox completed."); } return TRUE; } dovecot-2.2.33.2/src/imap/cmd-logout.c0000644000175000017500000000123013123174404014253 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ostream.h" #include "imap-commands.h" bool cmd_logout(struct client_command_context *cmd) { struct client *client = cmd->client; client->logged_out = TRUE; client_send_line(client, "* BYE Logging out"); if (client->mailbox != NULL) { /* this could be done at client_disconnect() as well, but eg. mbox rewrite takes a while so the waiting is better to happen before "OK" message. */ imap_client_close_mailbox(client); } client_send_tagline(cmd, "OK Logout completed."); client_disconnect(client, "Logged out"); return TRUE; } dovecot-2.2.33.2/src/imap/imap-status.c0000644000175000017500000001031113123174404014450 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "hex-binary.h" #include "str.h" #include "imap-quote.h" #include "imap-status.h" int imap_status_parse_items(struct client_command_context *cmd, const struct imap_arg *args, struct imap_status_items *items_r) { const char *item; enum mailbox_status_items status = 0; enum mailbox_metadata_items metadata = 0; if (IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Empty status list."); return -1; } i_zero(items_r); for (; !IMAP_ARG_IS_EOL(args); args++) { if (!imap_arg_get_atom(args, &item)) { /* list may contain only atoms */ client_send_command_error(cmd, "Status list contains non-atoms."); return -1; } item = t_str_ucase(item); if (strcmp(item, "MESSAGES") == 0) status |= STATUS_MESSAGES; else if (strcmp(item, "RECENT") == 0) status |= STATUS_RECENT; else if (strcmp(item, "UIDNEXT") == 0) status |= STATUS_UIDNEXT; else if (strcmp(item, "UIDVALIDITY") == 0) status |= STATUS_UIDVALIDITY; else if (strcmp(item, "UNSEEN") == 0) status |= STATUS_UNSEEN; else if (strcmp(item, "HIGHESTMODSEQ") == 0) status |= STATUS_HIGHESTMODSEQ; else if (strcmp(item, "X-SIZE") == 0) metadata |= MAILBOX_METADATA_VIRTUAL_SIZE; else if (strcmp(item, "X-GUID") == 0) metadata |= MAILBOX_METADATA_GUID; else { client_send_command_error(cmd, t_strconcat( "Invalid status item ", item, NULL)); return -1; } } items_r->status = status; items_r->metadata = metadata; return 0; } int imap_status_get(struct client_command_context *cmd, struct mail_namespace *ns, const char *mailbox, const struct imap_status_items *items, struct imap_status_result *result_r) { struct client *client = cmd->client; struct mailbox *box; const char *errstr; int ret = 0; if (client->mailbox != NULL && mailbox_equals(client->mailbox, ns, mailbox)) { /* this mailbox is selected */ box = client->mailbox; } else { /* open the mailbox */ box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY); mailbox_set_reason(box, "STATUS"); if (client->enabled_features != 0) (void)mailbox_enable(box, client->enabled_features); } if ((items->status & STATUS_HIGHESTMODSEQ) != 0) (void)client_enable(client, MAILBOX_FEATURE_CONDSTORE); ret = mailbox_get_status(box, items->status, &result_r->status); if (items->metadata != 0 && ret == 0) { ret = mailbox_get_metadata(box, items->metadata, &result_r->metadata); } if (ret < 0) { errstr = mailbox_get_last_error(box, &result_r->error); result_r->errstr = imap_get_error_string(cmd, errstr, result_r->error); } if (box != client->mailbox) mailbox_free(&box); return ret; } int imap_status_send(struct client *client, const char *mailbox_mutf7, const struct imap_status_items *items, const struct imap_status_result *result) { const struct mailbox_status *status = &result->status; string_t *str; size_t prefix_len; str = t_str_new(128); str_append(str, "* STATUS "); imap_append_astring(str, mailbox_mutf7); str_append(str, " ("); prefix_len = str_len(str); if ((items->status & STATUS_MESSAGES) != 0) str_printfa(str, "MESSAGES %u ", status->messages); if ((items->status & STATUS_RECENT) != 0) str_printfa(str, "RECENT %u ", status->recent); if ((items->status & STATUS_UIDNEXT) != 0) str_printfa(str, "UIDNEXT %u ", status->uidnext); if ((items->status & STATUS_UIDVALIDITY) != 0) str_printfa(str, "UIDVALIDITY %u ", status->uidvalidity); if ((items->status & STATUS_UNSEEN) != 0) str_printfa(str, "UNSEEN %u ", status->unseen); if ((items->status & STATUS_HIGHESTMODSEQ) != 0) { str_printfa(str, "HIGHESTMODSEQ %llu ", (unsigned long long)status->highest_modseq); } if ((items->metadata & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) { str_printfa(str, "X-SIZE %llu ", (unsigned long long)result->metadata.virtual_size); } if ((items->metadata & MAILBOX_METADATA_GUID) != 0) { str_printfa(str, "X-GUID %s ", guid_128_to_string(result->metadata.guid)); } if (str_len(str) != prefix_len) str_truncate(str, str_len(str)-1); str_append_c(str, ')'); return client_send_line_next(client, str_c(str)); } dovecot-2.2.33.2/src/imap/imap-master-client.c0000644000175000017500000002504613165463624015722 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "connection.h" #include "istream.h" #include "istream-unix.h" #include "ostream.h" #include "base64.h" #include "strescape.h" #include "master-service.h" #include "mail-storage-service.h" #include "imap-client.h" #include "imap-state.h" #include "imap-master-client.h" struct imap_master_client { struct connection conn; bool imap_client_created; }; struct imap_master_input { /* input we've already read from the IMAP client. */ buffer_t *client_input; /* output that imap-hibernate was supposed to send to IMAP client, but couldn't send it yet. */ buffer_t *client_output; /* IMAP connection state */ buffer_t *state; /* command tag */ const char *tag; dev_t peer_dev; ino_t peer_ino; bool state_import_bad_idle_done; bool state_import_idle_continue; }; static struct connection_list *master_clients = NULL; static void imap_master_client_destroy(struct connection *conn) { struct imap_master_client *client = (struct imap_master_client *)conn; if (!client->imap_client_created) master_service_client_connection_destroyed(master_service); connection_deinit(conn); i_free(conn); } static int imap_master_client_parse_input(const char *const *args, pool_t pool, struct mail_storage_service_input *input_r, struct imap_master_input *master_input_r, const char **error_r) { const char *key, *value; unsigned int peer_dev_major = 0, peer_dev_minor = 0; i_zero(input_r); i_zero(master_input_r); master_input_r->client_input = buffer_create_dynamic(pool, 64); master_input_r->client_output = buffer_create_dynamic(pool, 16); master_input_r->state = buffer_create_dynamic(pool, 512); input_r->module = input_r->service = "imap"; /* we never want to do userdb lookup again when restoring the client. we have the userdb_fields cached already. */ input_r->flags_override_remove = MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; if (args[0] == NULL) { *error_r = "Missing username in input"; return -1; } input_r->username = args[0]; for (args++; *args != NULL; args++) { value = strchr(*args, '='); if (value != NULL) key = t_strdup_until(*args, value++); else { key = *args; value = ""; } if (strcmp(key, "lip") == 0) { if (net_addr2ip(value, &input_r->local_ip) < 0) { *error_r = t_strdup_printf( "Invalid lip value: %s", value); return -1; } } else if (strcmp(key, "rip") == 0) { if (net_addr2ip(value, &input_r->remote_ip) < 0) { *error_r = t_strdup_printf( "Invalid rip value: %s", value); return -1; } } else if (strcmp(key, "peer_dev_major") == 0) { if (str_to_uint(value, &peer_dev_major) < 0) { *error_r = t_strdup_printf( "Invalid peer_dev_major value: %s", value); return -1; } } else if (strcmp(key, "peer_dev_minor") == 0) { if (str_to_uint(value, &peer_dev_minor) < 0) { *error_r = t_strdup_printf( "Invalid peer_dev_minor value: %s", value); return -1; } } else if (strcmp(key, "peer_ino") == 0) { if (str_to_ino(value, &master_input_r->peer_ino) < 0) { *error_r = t_strdup_printf( "Invalid peer_ino value: %s", value); return -1; } } else if (strcmp(key, "session") == 0) { input_r->session_id = value; } else if (strcmp(key, "session_created") == 0) { if (str_to_time(value, &input_r->session_create_time) < 0) { *error_r = t_strdup_printf( "Invalid session_created value: %s", value); return -1; } } else if (strcmp(key, "userdb_fields") == 0) { input_r->userdb_fields = t_strsplit_tabescaped(value); } else if (strcmp(key, "client_input") == 0) { if (base64_decode(value, strlen(value), NULL, master_input_r->client_input) < 0) { *error_r = t_strdup_printf( "Invalid client_input base64 value: %s", value); return -1; } } else if (strcmp(key, "client_output") == 0) { if (base64_decode(value, strlen(value), NULL, master_input_r->client_output) < 0) { *error_r = t_strdup_printf( "Invalid client_output base64 value: %s", value); return -1; } } else if (strcmp(key, "state") == 0) { if (base64_decode(value, strlen(value), NULL, master_input_r->state) < 0) { *error_r = t_strdup_printf( "Invalid state base64 value: %s", value); return -1; } } else if (strcmp(key, "tag") == 0) { master_input_r->tag = t_strdup(value); } else if (strcmp(key, "bad-done") == 0) { master_input_r->state_import_bad_idle_done = TRUE; } else if (strcmp(key, "idle-continue") == 0) { master_input_r->state_import_idle_continue = TRUE; } } if (peer_dev_major != 0 || peer_dev_minor != 0) { master_input_r->peer_dev = makedev(peer_dev_major, peer_dev_minor); } return 0; } static int imap_master_client_verify(const struct imap_master_input *master_input, int fd_client, const char **error_r) { struct stat peer_st; if (master_input->peer_ino == 0) return 0; /* make sure we have the right fd */ if (fstat(fd_client, &peer_st) < 0) { *error_r = t_strdup_printf("fstat(peer) failed: %m"); return -1; } if (peer_st.st_ino != master_input->peer_ino || !CMP_DEV_T(peer_st.st_dev, master_input->peer_dev)) { *error_r = t_strdup_printf( "BUG: Expected peer device=%lu,%lu inode=%s doesn't match " "client fd's actual device=%lu,%lu inode=%s", (unsigned long)major(peer_st.st_dev), (unsigned long)minor(peer_st.st_dev), dec2str(peer_st.st_ino), (unsigned long)major(master_input->peer_dev), (unsigned long)minor(master_input->peer_dev), dec2str(master_input->peer_ino)); return -1; } return 0; } static int imap_master_client_input_args(struct connection *conn, const char *const *args, int fd_client, pool_t pool) { struct imap_master_client *client = (struct imap_master_client *)conn; struct client *imap_client; struct mail_storage_service_input input; struct imap_master_input master_input; const char *error; int ret; if (imap_master_client_parse_input(args, pool, &input, &master_input, &error) < 0) { i_error("imap-master: Failed to parse client input: %s", error); o_stream_nsend_str(conn->output, t_strdup_printf( "-Failed to parse client input: %s\n", error)); i_close_fd(&fd_client); return -1; } if (imap_master_client_verify(&master_input, fd_client, &error) < 0) { i_error("imap-master: Failed to verify client input: %s", error); o_stream_nsend_str(conn->output, t_strdup_printf( "-Failed to verify client input: %s\n", error)); i_close_fd(&fd_client); return -1; } /* Send a success notification before we start anything that lasts potentially a long time. imap-hibernate process is waiting for us to answer. Even if we fail later, we log the error anyway. */ o_stream_nsend_str(conn->output, "+\n"); (void)o_stream_flush(conn->output); /* NOTE: before client_create_from_input() on failures we need to close fd_client, but afterward it gets closed by client_destroy() */ ret = client_create_from_input(&input, fd_client, fd_client, &imap_client, &error); if (ret < 0) { i_error("imap-master(%s): Failed to create client: %s", input.username, error); i_close_fd(&fd_client); return -1; } client->imap_client_created = TRUE; if (mail_namespaces_init(imap_client->user, &error) < 0) { i_error("imap-master(%s): mail_namespaces_init() failed: %s", input.username, error); client_destroy(imap_client, error); return -1; } /* log prefix is set at this point, so we don't need to add the username anymore to the log messages */ o_stream_nsend(imap_client->output, master_input.client_output->data, master_input.client_output->used); if (master_input.client_input->used > 0 && !i_stream_add_data(imap_client->input, master_input.client_input->data, master_input.client_input->used)) { i_error("imap-master: Couldn't add %"PRIuSIZE_T " bytes to client's input stream", master_input.client_input->used); client_destroy(imap_client, "Client initialization failed"); return -1; } imap_client->state_import_bad_idle_done = master_input.state_import_bad_idle_done; imap_client->state_import_idle_continue = master_input.state_import_idle_continue; ret = imap_state_import_internal(imap_client, master_input.state->data, master_input.state->used, &error); if (ret <= 0) { i_error("imap-master: Failed to import client state: %s", error); client_destroy(imap_client, "Client state initialization failed"); return -1; } if (master_input.tag != NULL) imap_state_import_idle_cmd_tag(imap_client, master_input.tag); /* make sure all pending input gets handled */ i_assert(imap_client->to_delayed_input == NULL); if (master_input.client_input->used > 0) { imap_client->to_delayed_input = timeout_add(0, client_input, imap_client); } imap_refresh_proctitle(); /* we'll always disconnect the client afterwards */ return -1; } static int imap_master_client_input_line(struct connection *conn, const char *line) { char *const *args; pool_t pool; int fd_client, ret; if (!conn->version_received) { if (connection_verify_version(conn, t_strsplit_tabescaped(line)) < 0) return -1; conn->version_received = TRUE; return 1; } fd_client = i_stream_unix_get_read_fd(conn->input); if (fd_client == -1) { i_error("imap-master: IMAP client fd not received"); return -1; } if (imap_debug) i_debug("imap-master: Client input: %s", line); pool = pool_alloconly_create("imap master client cmd", 1024); args = p_strsplit_tabescaped(pool, line); ret = imap_master_client_input_args(conn, (void *)args, fd_client, pool); pool_unref(&pool); return ret; } void imap_master_client_create(int fd) { struct imap_master_client *client; client = i_new(struct imap_master_client, 1); connection_init_server(master_clients, &client->conn, "imap-master", fd, fd); i_assert(client->conn.input == NULL); client->conn.input = i_stream_create_unix(fd, (size_t)-1); /* read the first file descriptor that we can */ i_stream_unix_set_read_fd(client->conn.input); } static struct connection_settings client_set = { .service_name_in = "imap-master", .service_name_out = "imap-master", .major_version = 1, .minor_version = 0, .input_max_size = 0, /* don't auto-create istream */ .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs client_vfuncs = { .destroy = imap_master_client_destroy, .input_line = imap_master_client_input_line }; void imap_master_clients_init(void) { master_clients = connection_list_init(&client_set, &client_vfuncs); } void imap_master_clients_deinit(void) { connection_list_deinit(&master_clients); } dovecot-2.2.33.2/src/imap/cmd-append.c0000644000175000017500000006140413165463624014235 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ioloop.h" #include "istream.h" #include "istream-chain.h" #include "ostream.h" #include "str.h" #include "imap-resp-code.h" #include "istream-binary-converter.h" #include "mail-storage-private.h" #include "imap-parser.h" #include "imap-date.h" #include "imap-util.h" #include "imap-commands.h" #include "imap-msgpart-url.h" #include /* Don't allow internaldates to be too far in the future. At least with Maildir they can cause problems with incremental backups since internaldate is stored in file's mtime. But perhaps there are also some other reasons why it might not be wanted. */ #define INTERNALDATE_MAX_FUTURE_SECS (2*3600) struct cmd_append_context { struct client *client; struct client_command_context *cmd; struct mailbox *box; struct mailbox_transaction_context *t; time_t started; struct istream_chain *catchain; uoff_t cat_msg_size; struct istream *input; struct istream *litinput; uoff_t literal_size; struct imap_parser *save_parser; struct mail_save_context *save_ctx; unsigned int count; unsigned int message_input:1; unsigned int binary_input:1; unsigned int catenate:1; unsigned int failed:1; }; static void cmd_append_finish(struct cmd_append_context *ctx); static bool cmd_append_continue_message(struct client_command_context *cmd); static bool cmd_append_parse_new_msg(struct client_command_context *cmd); static const char * get_disconnect_reason(struct cmd_append_context *ctx, uoff_t lit_offset) { string_t *str = t_str_new(128); unsigned int secs = ioloop_time - ctx->started; str_printfa(str, "Disconnected in APPEND (%u msgs, %u secs", ctx->count, secs); if (ctx->literal_size > 0) { str_printfa(str, ", %"PRIuUOFF_T"/%"PRIuUOFF_T" bytes", lit_offset, ctx->literal_size); } str_append_c(str, ')'); return str_c(str); } static void client_input_append(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; struct client *client = cmd->client; const char *reason; bool finished; uoff_t lit_offset; i_assert(!client->destroyed); client->last_input = ioloop_time; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -1: /* disconnected */ lit_offset = ctx->litinput == NULL ? 0 : ctx->litinput->v_offset; reason = get_disconnect_reason(ctx, lit_offset); cmd_append_finish(cmd->context); /* Reset command so that client_destroy() doesn't try to call cmd_append_continue_message() anymore. */ client_command_free(&cmd); client_destroy(client, reason); return; case -2: if (ctx->message_input) { /* message data, this is handled internally by mailbox_save_continue() */ break; } cmd_append_finish(cmd->context); /* parameter word is longer than max. input buffer size. this is most likely an error, so skip the new data until newline is found. */ client->input_skip_line = TRUE; if (!ctx->failed) client_send_command_error(cmd, "Too long argument."); cmd->param_error = TRUE; client_command_free(&cmd); return; } o_stream_cork(client->output); finished = command_exec(cmd); if (!finished) (void)client_handle_unfinished_cmd(cmd); else client_command_free(&cmd); cmd_sync_delayed(client); o_stream_uncork(client->output); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); } static void cmd_append_finish(struct cmd_append_context *ctx) { if (ctx->save_parser != NULL) imap_parser_unref(&ctx->save_parser); i_assert(ctx->client->input_lock == ctx->cmd); if (ctx->client->io != NULL) io_remove(&ctx->client->io); /* we must put back the original flush callback before beginning to sync (the command is still unfinished at that point) */ o_stream_set_flush_callback(ctx->client->output, client_output, ctx->client); if (ctx->litinput != NULL) i_stream_unref(&ctx->litinput); if (ctx->input != NULL) i_stream_unref(&ctx->input); if (ctx->save_ctx != NULL) mailbox_save_cancel(&ctx->save_ctx); if (ctx->t != NULL) mailbox_transaction_rollback(&ctx->t); if (ctx->box != ctx->cmd->client->mailbox && ctx->box != NULL) mailbox_free(&ctx->box); } static bool cmd_append_send_literal_continue(struct cmd_append_context *ctx) { if (ctx->failed) { /* tagline was already sent, we can abort here */ return FALSE; } o_stream_nsend(ctx->client->output, "+ OK\r\n", 6); o_stream_nflush(ctx->client->output); o_stream_uncork(ctx->client->output); o_stream_cork(ctx->client->output); return TRUE; } static int cmd_append_catenate_mpurl(struct client_command_context *cmd, const char *caturl, struct imap_msgpart_url *mpurl) { struct cmd_append_context *ctx = cmd->context; struct imap_msgpart_open_result mpresult; uoff_t newsize; const char *error; int ret; /* catenate URL */ ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error); if (ret < 0) { client_send_box_error(cmd, ctx->box); return -1; } if (ret == 0) { /* invalid url, abort */ client_send_tagline(cmd, t_strdup_printf("NO [BADURL %s] %s.", caturl, error)); return -1; } if (mpresult.size == 0) { /* empty input */ return 0; } newsize = ctx->cat_msg_size + mpresult.size; if (newsize < ctx->cat_msg_size) { client_send_tagline(cmd, "NO [TOOBIG] Composed message grows too big."); return -1; } ctx->cat_msg_size = newsize; /* add this input stream to chain */ i_stream_chain_append(ctx->catchain, mpresult.input); /* save by reading the chain stream */ do { ret = i_stream_read(mpresult.input); i_assert(ret != 0); /* we can handle only blocking input here */ } while (mailbox_save_continue(ctx->save_ctx) == 0 && ret != -1); if (mpresult.input->stream_errno != 0) { errno = mpresult.input->stream_errno; mail_storage_set_critical(ctx->box->storage, "read(%s) failed: %s (for CATENATE URL %s)", i_stream_get_name(mpresult.input), i_stream_get_error(mpresult.input), caturl); client_send_box_error(cmd, ctx->box); ret = -1; } else if (!mpresult.input->eof) { /* save failed */ client_send_box_error(cmd, ctx->box); ret = -1; } else { /* all the input must be consumed, so istream-chain's read() unreferences the stream and we can free its parent mail */ i_assert(!i_stream_have_bytes_left(mpresult.input)); ret = 0; } return ret; } static int cmd_append_catenate_url(struct client_command_context *cmd, const char *caturl) { struct cmd_append_context *ctx = cmd->context; struct imap_msgpart_url *mpurl; const char *error; int ret; if (ctx->failed) return -1; ret = imap_msgpart_url_parse(cmd->client->user, cmd->client->mailbox, caturl, &mpurl, &error); if (ret < 0) { client_send_box_error(cmd, ctx->box); return -1; } if (ret == 0) { /* invalid url, abort */ client_send_tagline(cmd, t_strdup_printf("NO [BADURL %s] %s.", caturl, error)); return -1; } ret = cmd_append_catenate_mpurl(cmd, caturl, mpurl); imap_msgpart_url_free(&mpurl); return ret; } static void cmd_append_catenate_text(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; if (ctx->literal_size > (uoff_t)-1 - ctx->cat_msg_size && !ctx->failed) { client_send_tagline(cmd, "NO [TOOBIG] Composed message grows too big."); ctx->failed = TRUE; } /* save the mail */ ctx->cat_msg_size += ctx->literal_size; if (ctx->literal_size == 0) { /* zero length literal. RFC doesn't explicitly specify what should be done with this, so we'll simply handle it by skipping the empty text part. */ ctx->litinput = i_stream_create_from_data("", 0); ctx->litinput->eof = TRUE; } else { ctx->litinput = i_stream_create_limit(cmd->client->input, ctx->literal_size); i_stream_chain_append(ctx->catchain, ctx->litinput); } } static int cmd_append_catenate(struct client_command_context *cmd, const struct imap_arg *args, bool *nonsync_r) { struct cmd_append_context *ctx = cmd->context; const char *catpart; *nonsync_r = FALSE; /* Handle URLs until a TEXT literal is encountered */ while (imap_arg_get_atom(args, &catpart)) { const char *caturl; if (strcasecmp(catpart, "URL") == 0 ) { /* URL */ args++; if (!imap_arg_get_astring(args, &caturl)) break; if (cmd_append_catenate_url(cmd, caturl) < 0) { /* delay failure until we can stop parsing input */ ctx->failed = TRUE; } } else if (strcasecmp(catpart, "TEXT") == 0) { /* TEXT */ args++; if (!imap_arg_get_literal_size(args, &ctx->literal_size)) break; if (args->literal8 && !ctx->binary_input && !ctx->failed) { client_send_tagline(cmd, "NO ["IMAP_RESP_CODE_UNKNOWN_CTE"] " "Binary input allowed only when the first part is binary."); ctx->failed = TRUE; } *nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC; cmd_append_catenate_text(cmd); return 1; } else { break; } args++; } if (IMAP_ARG_IS_EOL(args)) { /* ")" */ return 0; } if (!ctx->failed) client_send_command_error(cmd, "Invalid arguments."); return -1; } static void cmd_append_finish_catenate(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; i_stream_chain_append_eof(ctx->catchain); i_stream_unref(&ctx->input); ctx->catenate = FALSE; ctx->catchain = NULL; if (ctx->failed) { /* APPEND has already failed */ if (ctx->save_ctx != NULL) mailbox_save_cancel(&ctx->save_ctx); } else { if (mailbox_save_finish(&ctx->save_ctx) < 0) { client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } } } static bool catenate_args_can_stop(struct cmd_append_context *ctx, const struct imap_arg *args) { /* eat away literal_sizes from URLs */ while (args->type != IMAP_ARG_EOL) { if (imap_arg_atom_equals(args, "TEXT")) return TRUE; if (!imap_arg_atom_equals(args, "URL")) { /* error - handle it later */ return TRUE; } args++; if (args->type == IMAP_ARG_LITERAL_SIZE || args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC) { if (args->type == IMAP_ARG_LITERAL_SIZE) { if (!cmd_append_send_literal_continue(ctx)) return TRUE; } imap_parser_read_last_literal(ctx->save_parser); return FALSE; } args++; } return TRUE; } static bool cmd_append_continue_catenate(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; const struct imap_arg *args; const char *msg; bool fatal, nonsync = FALSE; int ret; if (cmd->cancel) { /* cancel the command immediately (disconnection) */ cmd_append_finish(ctx); return TRUE; } /* we're parsing inside CATENATE (..) list after handling a TEXT part. it's fine that this would need to fully fit into input buffer (although clients attempting to DoS could simply insert an extra {1+} between the URLs) */ do { ret = imap_parser_read_args(ctx->save_parser, 0, IMAP_PARSE_FLAG_LITERAL_SIZE | IMAP_PARSE_FLAG_LITERAL8 | IMAP_PARSE_FLAG_INSIDE_LIST, &args); } while (ret > 0 && !catenate_args_can_stop(ctx, args)); if (ret == -1) { msg = imap_parser_get_error(ctx->save_parser, &fatal); if (fatal) client_disconnect_with_error(client, msg); else if (!ctx->failed) client_send_command_error(cmd, msg); client->input_skip_line = TRUE; cmd_append_finish(ctx); return TRUE; } if (ret < 0) { /* need more data */ return FALSE; } if ((ret = cmd_append_catenate(cmd, args, &nonsync)) < 0) { /* invalid parameters, abort immediately */ cmd_append_finish(ctx); return TRUE; } if (ret == 0) { /* ")" */ cmd_append_finish_catenate(cmd); /* last catenate part */ imap_parser_reset(ctx->save_parser); cmd->func = cmd_append_parse_new_msg; return cmd_append_parse_new_msg(cmd); } /* TEXT */ if (!nonsync) { if (!cmd_append_send_literal_continue(ctx)) { cmd_append_finish(ctx); return TRUE; } } i_assert(ctx->litinput != NULL); ctx->message_input = TRUE; cmd->func = cmd_append_continue_message; return cmd_append_continue_message(cmd); } static int cmd_append_handle_args(struct client_command_context *cmd, const struct imap_arg *args, bool *nonsync_r) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; const struct imap_arg *flags_list; const struct imap_arg *cat_list = NULL; enum mail_flags flags; const char *const *keywords_list; struct mail_keywords *keywords; struct istream *input; const char *internal_date_str; time_t internal_date; int ret, timezone_offset; bool valid; /* [] */ if (!imap_arg_get_list(args, &flags_list)) flags_list = NULL; else args++; /* [] */ if (args->type != IMAP_ARG_STRING) internal_date_str = NULL; else { internal_date_str = imap_arg_as_astring(args); args++; } /* | CATENATE (..) */ valid = FALSE; *nonsync_r = FALSE; ctx->catenate = FALSE; if (imap_arg_get_literal_size(args, &ctx->literal_size)) { *nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC; ctx->binary_input = args->literal8; valid = TRUE; } else if (!imap_arg_atom_equals(args, "CATENATE")) { /* invalid */ } else if (!imap_arg_get_list(++args, &cat_list)) { /* invalid */ } else { valid = TRUE; ctx->catenate = TRUE; /* We'll do BINARY conversion only if the CATENATE's first part is a literal8. If it doesn't and a literal8 is seen later we'll abort the append with UNKNOWN-CTE. */ ctx->binary_input = imap_arg_atom_equals(&cat_list[0], "TEXT") && cat_list[1].literal8; } if (!IMAP_ARG_IS_EOL(&args[1])) valid = FALSE; if (!valid) { client->input_skip_line = TRUE; if (!ctx->failed) client_send_command_error(cmd, "Invalid arguments."); return -1; } if (flags_list == NULL || ctx->failed) { flags = 0; keywords = NULL; } else { if (!client_parse_mail_flags(cmd, flags_list, &flags, &keywords_list)) return -1; if (keywords_list == NULL) keywords = NULL; else if (mailbox_keywords_create(ctx->box, keywords_list, &keywords) < 0) { /* invalid keywords - delay failure */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; keywords = NULL; } } if (internal_date_str == NULL || ctx->failed) { /* no time given, default to now. */ internal_date = (time_t)-1; timezone_offset = 0; } else if (!imap_parse_datetime(internal_date_str, &internal_date, &timezone_offset)) { client_send_command_error(cmd, "Invalid internal date."); if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } if (internal_date != (time_t)-1 && internal_date > ioloop_time + INTERNALDATE_MAX_FUTURE_SECS) { /* the client specified a time in the future, set it to now. */ internal_date = (time_t)-1; timezone_offset = 0; } if (cat_list != NULL) { ctx->cat_msg_size = 0; ctx->input = i_stream_create_chain(&ctx->catchain); } else { if (ctx->literal_size == 0) { /* no message data, abort */ if (!ctx->failed) { client_send_tagline(cmd, "NO Can't save a zero byte message."); ctx->failed = TRUE; } if (!*nonsync_r) { if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } /* {0+} used. although there isn't any point in using MULTIAPPEND here and adding more messages, it is technically valid so we'll continue parsing.. */ } ctx->litinput = i_stream_create_limit(client->input, ctx->literal_size); ctx->input = ctx->litinput; i_stream_ref(ctx->input); } if (ctx->binary_input) { input = i_stream_create_binary_converter(ctx->input); i_stream_unref(&ctx->input); ctx->input = input; } if (!ctx->failed) { /* save the mail */ ctx->save_ctx = mailbox_save_alloc(ctx->t); mailbox_save_set_flags(ctx->save_ctx, flags, keywords); mailbox_save_set_received_date(ctx->save_ctx, internal_date, timezone_offset); if (mailbox_save_begin(&ctx->save_ctx, ctx->input) < 0) { /* save initialization failed */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } } if (keywords != NULL) mailbox_keywords_unref(&keywords); ctx->count++; if (cat_list == NULL) { /* normal APPEND */ return 1; } else if (cat_list->type == IMAP_ARG_EOL) { /* zero parts */ if (!ctx->failed) client_send_command_error(cmd, "Empty CATENATE list."); client->input_skip_line = TRUE; return -1; } else if ((ret = cmd_append_catenate(cmd, cat_list, nonsync_r)) < 0) { /* invalid parameters, abort immediately */ return -1; } else if (ret == 0) { /* CATENATE consisted only of URLs */ return 0; } else { /* TEXT part found from CATENATE */ return 1; } } static bool cmd_append_finish_parsing(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; enum mailbox_sync_flags sync_flags; enum imap_sync_flags imap_flags; struct mail_transaction_commit_changes changes; unsigned int save_count; string_t *msg; int ret; /* eat away the trailing CRLF */ cmd->client->input_skip_line = TRUE; if (ctx->failed) { /* we failed earlier, error message is sent */ cmd_append_finish(ctx); return TRUE; } if (ctx->count == 0) { client_send_command_error(cmd, "Missing message size."); cmd_append_finish(ctx); return TRUE; } ret = mailbox_transaction_commit_get_changes(&ctx->t, &changes); if (ret < 0) { client_send_box_error(cmd, ctx->box); cmd_append_finish(ctx); return TRUE; } msg = t_str_new(256); save_count = seq_range_count(&changes.saved_uids); if (save_count == 0 || changes.no_read_perm) { /* not supported by backend (virtual) */ str_append(msg, "OK Append completed."); } else { i_assert(ctx->count == save_count); str_printfa(msg, "OK [APPENDUID %u ", changes.uid_validity); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Append completed."); } ctx->client->append_count += save_count; pool_unref(&changes.pool); if (ctx->box == cmd->client->mailbox) { sync_flags = 0; imap_flags = IMAP_SYNC_FLAG_SAFE; } else { sync_flags = MAILBOX_SYNC_FLAG_FAST; imap_flags = 0; } cmd_append_finish(ctx); return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); } static bool cmd_append_args_can_stop(struct cmd_append_context *ctx, const struct imap_arg *args, bool *last_literal_r) { const struct imap_arg *cat_list; *last_literal_r = FALSE; if (args->type == IMAP_ARG_EOL) return TRUE; /* [(flags)] ["internal date"] | CATENATE (..) */ if (args->type == IMAP_ARG_LIST) args++; if (args->type == IMAP_ARG_STRING) args++; if (args->type == IMAP_ARG_LITERAL_SIZE || args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC) return TRUE; if (imap_arg_atom_equals(args, "CATENATE") && imap_arg_get_list(&args[1], &cat_list)) { if (catenate_args_can_stop(ctx, cat_list)) return TRUE; *last_literal_r = TRUE; } return FALSE; } static bool cmd_append_parse_new_msg(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; const struct imap_arg *args; const char *msg; unsigned int arg_min_count; bool fatal, nonsync, last_literal; int ret; /* this function gets called 1) after parsing APPEND and 2) with MULTIAPPEND extension after already saving one or more mails. */ if (cmd->cancel) { /* cancel the command immediately (disconnection) */ cmd_append_finish(ctx); return TRUE; } /* if error occurs, the CRLF is already read. */ client->input_skip_line = FALSE; /* parse the entire line up to the first message literal, or in case the input buffer is full of MULTIAPPEND CATENATE URLs, parse at least until the beginning of the next message */ arg_min_count = 0; last_literal = FALSE; do { if (!last_literal) arg_min_count++; else { /* we only read the literal size. now we read the literal itself. */ } ret = imap_parser_read_args(ctx->save_parser, arg_min_count, IMAP_PARSE_FLAG_LITERAL_SIZE | IMAP_PARSE_FLAG_LITERAL8, &args); } while (ret >= (int)arg_min_count && !cmd_append_args_can_stop(ctx, args, &last_literal)); if (ret == -1) { if (!ctx->failed) { msg = imap_parser_get_error(ctx->save_parser, &fatal); if (fatal) client_disconnect_with_error(client, msg); else client_send_command_error(cmd, msg); } cmd_append_finish(ctx); return TRUE; } if (ret < 0) { /* need more data */ return FALSE; } if (IMAP_ARG_IS_EOL(args)) { /* last message */ return cmd_append_finish_parsing(cmd); } ret = cmd_append_handle_args(cmd, args, &nonsync); if (ret < 0) { /* invalid parameters, abort immediately */ cmd_append_finish(ctx); return TRUE; } if (ret == 0) { /* CATENATE contained only URLs. Finish it and see if there are more messsages. */ cmd_append_finish_catenate(cmd); imap_parser_reset(ctx->save_parser); return cmd_append_parse_new_msg(cmd); } if (!nonsync) { if (!cmd_append_send_literal_continue(ctx)) { cmd_append_finish(ctx); return TRUE; } } i_assert(ctx->litinput != NULL); ctx->message_input = TRUE; cmd->func = cmd_append_continue_message; return cmd_append_continue_message(cmd); } static bool cmd_append_continue_message(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; int ret = 0; if (cmd->cancel) { /* cancel the command immediately (disconnection) */ cmd_append_finish(ctx); return TRUE; } if (ctx->save_ctx != NULL) { while (ctx->litinput->v_offset != ctx->literal_size) { ret = i_stream_read(ctx->litinput); if (mailbox_save_continue(ctx->save_ctx) < 0) { /* we still have to finish reading the message from client */ mailbox_save_cancel(&ctx->save_ctx); break; } if (ret == -1 || ret == 0) break; } } if (ctx->save_ctx == NULL) { /* saving has already failed, we're just eating away the literal */ (void)i_stream_read(ctx->litinput); i_stream_skip(ctx->litinput, i_stream_get_data_size(ctx->litinput)); } if (ctx->litinput->eof || client->input->closed) { uoff_t lit_offset = ctx->litinput->v_offset; /* finished - do one more read, to make sure istream-chain unreferences its stream, which is needed for litinput's unreferencing to seek the client->input to correct position. the seek is needed to avoid trying to seek backwards in the ctx->input's parent stream. */ i_stream_seek(ctx->input, ctx->input->v_offset); (void)i_stream_read(ctx->input); i_stream_unref(&ctx->litinput); if (ctx->failed) { if (ctx->save_ctx != NULL) mailbox_save_cancel(&ctx->save_ctx); } else if (ctx->save_ctx == NULL) { /* failed above */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } else if (lit_offset != ctx->literal_size) { /* client disconnected before it finished sending the whole message. */ ctx->failed = TRUE; mailbox_save_cancel(&ctx->save_ctx); client_disconnect(client, get_disconnect_reason(ctx, lit_offset)); } else if (ctx->catenate) { /* CATENATE isn't finished yet */ } else if (mailbox_save_finish(&ctx->save_ctx) < 0) { client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } if (client->input->closed) { cmd_append_finish(ctx); return TRUE; } /* prepare for the next message (or its part with catenate) */ ctx->message_input = FALSE; imap_parser_reset(ctx->save_parser); if (ctx->catenate) { cmd->func = cmd_append_continue_catenate; return cmd_append_continue_catenate(cmd); } i_stream_unref(&ctx->input); cmd->func = cmd_append_parse_new_msg; return cmd_append_parse_new_msg(cmd); } return FALSE; } bool cmd_append(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_append_context *ctx; const char *mailbox; if (client->syncing) { /* if transaction is created while its view is synced, appends aren't allowed for it. */ cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; return FALSE; } /* */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; /* we keep the input locked all the time */ client->input_lock = cmd; ctx = p_new(cmd->pool, struct cmd_append_context, 1); ctx->cmd = cmd; ctx->client = client; ctx->started = ioloop_time; if (client_open_save_dest_box(cmd, mailbox, &ctx->box) < 0) ctx->failed = TRUE; else { ctx->t = mailbox_transaction_begin(ctx->box, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); imap_transaction_set_cmd_reason(ctx->t, cmd); } io_remove(&client->io); client->io = io_add_istream(client->input, client_input_append, cmd); /* append is special because we're only waiting on client input, not client output, so disable the standard output handler until we're finished */ o_stream_unset_flush_callback(client->output); ctx->save_parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); cmd->func = cmd_append_parse_new_msg; cmd->context = ctx; return cmd_append_parse_new_msg(cmd); } dovecot-2.2.33.2/src/imap/cmd-thread.c0000644000175000017500000001661313165463624014237 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "imap-base-subject.h" #include "imap-commands.h" #include "imap-search-args.h" #include "mail-thread.h" static int imap_thread_write(struct mail_thread_iterate_context *iter, string_t *str, bool root) { const struct mail_thread_child_node *node; struct mail_thread_iterate_context *child_iter; unsigned int count; int ret = 0; count = mail_thread_iterate_count(iter); if (count == 0) return 0; if (count == 1 && !root) { /* only one child - special case to avoid extra paranthesis */ node = mail_thread_iterate_next(iter, &child_iter); str_printfa(str, "%u", node->uid); if (child_iter != NULL) { str_append_c(str, ' '); T_BEGIN { ret = imap_thread_write(child_iter, str, FALSE); } T_END; if (mail_thread_iterate_deinit(&child_iter) < 0) return -1; } return ret; } while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) { if (child_iter == NULL) { /* no children */ str_printfa(str, "(%u)", node->uid); } else { /* node with children */ str_append_c(str, '('); if (node->uid != 0) str_printfa(str, "%u ", node->uid); T_BEGIN { ret = imap_thread_write(child_iter, str, FALSE); } T_END; if (mail_thread_iterate_deinit(&child_iter) < 0 || ret < 0) return -1; str_append_c(str, ')'); } } return 0; } static int imap_thread_write_reply(struct mail_thread_context *ctx, string_t *str, enum mail_thread_type thread_type, bool write_seqs) { struct mail_thread_iterate_context *iter; int ret; iter = mail_thread_iterate_init(ctx, thread_type, write_seqs); str_append(str, "* THREAD "); T_BEGIN { ret = imap_thread_write(iter, str, TRUE); } T_END; if (mail_thread_iterate_deinit(&iter) < 0) ret = -1; str_append(str, "\r\n"); return ret; } static int imap_thread(struct client_command_context *cmd, struct mail_search_args *search_args, enum mail_thread_type thread_type) { struct mail_thread_context *ctx; string_t *str; int ret; i_assert(thread_type == MAIL_THREAD_REFERENCES || thread_type == MAIL_THREAD_REFS); str = str_new(default_pool, 1024); ret = mail_thread_init(cmd->client->mailbox, search_args, &ctx); if (ret == 0) { ret = imap_thread_write_reply(ctx, str, thread_type, !cmd->uid); mail_thread_deinit(&ctx); } if (ret == 0) o_stream_nsend(cmd->client->output, str_data(str), str_len(str)); str_free(&str); return ret; } struct orderedsubject_thread { time_t timestamp; ARRAY_TYPE(uint32_t) msgs; }; static int orderedsubject_thread_cmp(const struct orderedsubject_thread *t1, const struct orderedsubject_thread *t2) { const uint32_t *m1, *m2; if (t1->timestamp < t2->timestamp) return -1; if (t1->timestamp > t2->timestamp) return 1; m1 = array_idx(&t1->msgs, 0); m2 = array_idx(&t2->msgs, 0); if (*m1 < *m2) return -1; if (*m1 > *m2) return 1; i_unreached(); } static void imap_orderedsubject_thread_write(struct ostream *output, string_t *reply, const struct orderedsubject_thread *thread) { const uint32_t *msgs; unsigned int i, count; if (str_len(reply) > 128-10) { o_stream_nsend(output, str_data(reply), str_len(reply)); str_truncate(reply, 0); } msgs = array_get(&thread->msgs, &count); switch (count) { case 1: str_printfa(reply, "(%u)", msgs[0]); break; case 2: str_printfa(reply, "(%u %u)", msgs[0], msgs[1]); break; default: /* (1 (2)(3)) */ str_printfa(reply, "(%u ", msgs[0]); for (i = 1; i < count; i++) { if (str_len(reply) > 128-10) { o_stream_nsend(output, str_data(reply), str_len(reply)); str_truncate(reply, 0); } str_printfa(reply, "(%u)", msgs[i]); } str_append_c(reply, ')'); } } static int imap_thread_orderedsubject(struct client_command_context *cmd, struct mail_search_args *search_args) { static const enum mail_sort_type sort_program[] = { MAIL_SORT_SUBJECT, MAIL_SORT_DATE, 0 }; struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail *mail; string_t *prev_subject, *reply; const char *subject, *base_subject; pool_t pool; ARRAY(struct orderedsubject_thread) threads; const struct orderedsubject_thread *thread; struct orderedsubject_thread *cur_thread = NULL; uint32_t num; bool reply_or_fw; int ret, tz; prev_subject = str_new(default_pool, 128); /* first read all of the threads into memory */ pool = pool_alloconly_create("orderedsubject thread", 1024); i_array_init(&threads, 128); trans = mailbox_transaction_begin(cmd->client->mailbox, 0); imap_transaction_set_cmd_reason(trans, cmd); search_ctx = mailbox_search_init(trans, search_args, sort_program, 0, NULL); while (mailbox_search_next(search_ctx, &mail)) { if (mail_get_first_header(mail, "Subject", &subject) <= 0) subject = ""; T_BEGIN { base_subject = imap_get_base_subject_cased( pool_datastack_create(), subject, &reply_or_fw); if (strcmp(str_c(prev_subject), base_subject) != 0) { /* thread changed */ cur_thread = NULL; } str_truncate(prev_subject, 0); str_append(prev_subject, base_subject); } T_END; if (cur_thread == NULL) { /* starting a new thread. get the first message's date */ cur_thread = array_append_space(&threads); if (mail_get_date(mail, &cur_thread->timestamp, &tz) == 0 && cur_thread->timestamp == 0) { (void)mail_get_received_date(mail, &cur_thread->timestamp); } p_array_init(&cur_thread->msgs, pool, 4); } num = cmd->uid ? mail->uid : mail->seq; array_append(&cur_thread->msgs, &num, 1); } str_free(&prev_subject); ret = mailbox_search_deinit(&search_ctx); (void)mailbox_transaction_commit(&trans); if (ret < 0) { array_free(&threads); pool_unref(&pool); return -1; } /* sort the threads by their first message's timestamp */ array_sort(&threads, orderedsubject_thread_cmp); /* write the threads to client */ reply = t_str_new(128); str_append(reply, "* THREAD "); array_foreach(&threads, thread) { imap_orderedsubject_thread_write(cmd->client->output, reply, thread); } str_append(reply, "\r\n"); o_stream_nsend(cmd->client->output, str_data(reply), str_len(reply)); array_free(&threads); pool_unref(&pool); return 0; } bool cmd_thread(struct client_command_context *cmd) { struct client *client = cmd->client; enum mail_thread_type thread_type; struct mail_search_args *sargs; const struct imap_arg *args; const char *charset, *str; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; if (!imap_arg_get_astring(&args[0], &str) || !imap_arg_get_astring(&args[1], &charset)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } args += 2; if (!mail_thread_type_parse(str, &thread_type)) { client_send_command_error(cmd, "Unknown thread algorithm."); return TRUE; } ret = imap_search_args_build(cmd, args, charset, &sargs); if (ret <= 0) return ret < 0; if (thread_type != MAIL_THREAD_ORDEREDSUBJECT) ret = imap_thread(cmd, sargs, thread_type); else ret = imap_thread_orderedsubject(cmd, sargs); mail_search_args_unref(&sargs); if (ret < 0) { client_send_box_error(cmd, client->mailbox); return TRUE; } return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), 0, "OK Thread completed."); } dovecot-2.2.33.2/src/imap/imap-list.c0000644000175000017500000000170513123174404014107 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "imap-list.h" bool imap_mailbox_flags2str(string_t *str, enum mailbox_info_flags flags) { size_t orig_len = str_len(str); if ((flags & MAILBOX_SUBSCRIBED) != 0) str_append(str, "\\Subscribed "); if ((flags & MAILBOX_NOSELECT) != 0) str_append(str, "\\Noselect "); if ((flags & MAILBOX_NONEXISTENT) != 0) str_append(str, "\\NonExistent "); if ((flags & MAILBOX_CHILDREN) != 0) str_append(str, "\\HasChildren "); else if ((flags & MAILBOX_NOINFERIORS) != 0) str_append(str, "\\NoInferiors "); else if ((flags & MAILBOX_NOCHILDREN) != 0) str_append(str, "\\HasNoChildren "); if ((flags & MAILBOX_MARKED) != 0) str_append(str, "\\Marked "); if ((flags & MAILBOX_UNMARKED) != 0) str_append(str, "\\UnMarked "); if (str_len(str) == orig_len) return FALSE; str_truncate(str, str_len(str)-1); return TRUE; } dovecot-2.2.33.2/src/imap/Makefile.am0000644000175000017500000000407113165463624014112 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = imap AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-urlauth \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage imap_LDFLAGS = -export-dynamic imap_LDADD = \ ../lib-imap-urlauth/libimap-urlauth.la \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) imap_DEPENDENCIES = \ ../lib-imap-urlauth/libimap-urlauth.la \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) cmds = \ cmd-append.c \ cmd-capability.c \ cmd-cancelupdate.c \ cmd-check.c \ cmd-close.c \ cmd-copy.c \ cmd-create.c \ cmd-delete.c \ cmd-enable.c \ cmd-examine.c \ cmd-expunge.c \ cmd-fetch.c \ cmd-genurlauth.c \ cmd-getmetadata.c \ cmd-id.c \ cmd-idle.c \ cmd-list.c \ cmd-logout.c \ cmd-lsub.c \ cmd-namespace.c \ cmd-noop.c \ cmd-notify.c \ cmd-rename.c \ cmd-resetkey.c \ cmd-search.c \ cmd-select.c \ cmd-setmetadata.c \ cmd-sort.c \ cmd-status.c \ cmd-store.c \ cmd-subscribe.c \ cmd-thread.c \ cmd-unselect.c \ cmd-unsubscribe.c \ cmd-urlfetch.c \ cmd-x-cancel.c \ cmd-x-state.c imap_SOURCES = \ $(cmds) \ imap-client.c \ imap-client-hibernate.c \ imap-commands.c \ imap-commands-util.c \ imap-expunge.c \ imap-fetch.c \ imap-fetch-body.c \ imap-list.c \ imap-master-client.c \ imap-notify.c \ imap-search.c \ imap-search-args.c \ imap-settings.c \ imap-status.c \ imap-state.c \ imap-sync.c \ mail-storage-callbacks.c \ main.c headers = \ imap-client.h \ imap-commands.h \ imap-commands-util.h \ imap-common.h \ imap-expunge.h \ imap-fetch.h \ imap-list.h \ imap-master-client.h \ imap-notify.h \ imap-search.h \ imap-search-args.h \ imap-settings.h \ imap-status.h \ imap-state.h \ imap-sync.h \ imap-sync-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/imap/cmd-delete.c0000644000175000017500000000274213123174404014215 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-commands.h" bool cmd_delete(struct client_command_context *cmd) { struct client *client = cmd->client; struct mail_namespace *ns; struct mailbox *box; const char *name, *errstr; enum mail_error error; bool disconnect = FALSE; /* */ if (!client_read_string_args(cmd, 1, &name)) return FALSE; ns = client_find_namespace(cmd, &name); if (ns == NULL) return TRUE; box = mailbox_alloc(ns->list, name, 0); mailbox_set_reason(box, "DELETE"); if (mailbox_is_any_inbox(box)) { /* IMAP protocol allows this, but I think it's safer to not allow it. */ mailbox_free(&box); client_send_tagline(cmd, "NO INBOX can't be deleted."); return TRUE; } if (client->mailbox != NULL && mailbox_backends_equal(box, client->mailbox)) { /* deleting selected mailbox. close it first */ client_search_updates_free(client); mailbox_free(&client->mailbox); disconnect = TRUE; } if (mailbox_delete(box) == 0) client_send_tagline(cmd, "OK Delete completed."); else { errstr = mailbox_get_last_error(box, &error); if (error != MAIL_ERROR_EXISTS) client_send_box_error(cmd, box); else { /* mailbox has children */ client_send_tagline(cmd, t_strdup_printf("NO %s", errstr)); } } mailbox_free(&box); if (disconnect) { client_disconnect_with_error(cmd->client, "Selected mailbox was deleted, have to disconnect."); } return TRUE; } dovecot-2.2.33.2/src/imap/imap-sync-private.h0000644000175000017500000000213513165463624015576 00000000000000#ifndef IMAP_SYNC_PRIVATE_H #define IMAP_SYNC_PRIVATE_H #include "imap-sync.h" struct imap_client_sync_context { /* if multiple commands are in progress, we may need to wait for them to finish before syncing mailbox. */ unsigned int counter; enum mailbox_sync_flags flags; enum imap_sync_flags imap_flags; const char *tagline; }; struct imap_sync_context { struct client *client; struct mailbox *box; enum imap_sync_flags imap_flags; struct mailbox_transaction_context *t; struct mailbox_sync_context *sync_ctx; struct mail *mail; struct mailbox_status status; struct mailbox_sync_status sync_status; struct mailbox_sync_rec sync_rec; ARRAY_TYPE(keywords) tmp_keywords; ARRAY_TYPE(seq_range) expunges; uint32_t seq; ARRAY_TYPE(seq_range) search_adds, search_removes; unsigned int search_update_idx; unsigned int messages_count; /* Module-specific contexts. */ ARRAY(union imap_module_context *) module_contexts; unsigned int failed:1; unsigned int finished:1; unsigned int no_newmail:1; unsigned int have_new_mails:1; unsigned int search_update_notifying:1; }; #endif dovecot-2.2.33.2/src/imap/cmd-search.c0000644000175000017500000000215213123174404014213 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "imap-search-args.h" #include "imap-search.h" bool cmd_search(struct client_command_context *cmd) { struct imap_search_context *ctx; struct mail_search_args *sargs; const struct imap_arg *args; const char *charset; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ctx = p_new(cmd->pool, struct imap_search_context, 1); ctx->cmd = cmd; if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) { /* error / waiting for unambiguity */ return ret < 0; } if (imap_arg_atom_equals(args, "CHARSET")) { /* CHARSET specified */ if (!imap_arg_get_astring(&args[1], &charset)) { client_send_command_error(cmd, "Invalid charset argument."); imap_search_context_free(ctx); return TRUE; } args += 2; } else { charset = "UTF-8"; } ret = imap_search_args_build(cmd, args, charset, &sargs); if (ret <= 0) { imap_search_context_free(ctx); return ret < 0; } return imap_search_start(ctx, sargs, NULL); } dovecot-2.2.33.2/src/lib-index/0002755000175000017500000000000013172375611013057 500000000000000dovecot-2.2.33.2/src/lib-index/test-mail-index-transaction-update.c0000644000175000017500000005110713165463624021760 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-index-transaction-private.h" #include static struct mail_index_header hdr; static struct mail_index_record rec; const struct mail_index_header * mail_index_get_header(struct mail_index_view *view ATTR_UNUSED) { return &hdr; } const struct mail_index_record * mail_index_lookup(struct mail_index_view *view ATTR_UNUSED, uint32_t seq ATTR_UNUSED) { return &rec; } void mail_index_lookup_keywords(struct mail_index_view *view ATTR_UNUSED, uint32_t seq ATTR_UNUSED, ARRAY_TYPE(keyword_indexes) *keyword_idx ATTR_UNUSED) { array_clear(keyword_idx); } bool mail_index_map_get_ext_idx(struct mail_index_map *map ATTR_UNUSED, uint32_t ext_id ATTR_UNUSED, uint32_t *idx_r ATTR_UNUSED) { return FALSE; } uint32_t mail_index_view_get_messages_count(struct mail_index_view *view ATTR_UNUSED) { return hdr.messages_count; } void mail_index_transaction_lookup_latest_keywords(struct mail_index_transaction *t ATTR_UNUSED, uint32_t seq ATTR_UNUSED, ARRAY_TYPE(keyword_indexes) *keywords ATTR_UNUSED) { } struct mail_keywords * mail_index_keywords_create_from_indexes(struct mail_index *index ATTR_UNUSED, const ARRAY_TYPE(keyword_indexes) *keyword_indexes ATTR_UNUSED) { return NULL; } void mail_index_keywords_unref(struct mail_keywords **keywords ATTR_UNUSED) { } static struct mail_index_transaction * mail_index_transaction_new(void) { struct mail_index_transaction *t; t = t_new(struct mail_index_transaction, 1); t->first_new_seq = hdr.messages_count + 1; return t; } static void mail_index_transaction_cleanup(struct mail_index_transaction *t) { if (array_is_created(&t->appends)) array_free(&t->appends); if (array_is_created(&t->updates)) array_free(&t->updates); if (array_is_created(&t->modseq_updates)) array_free(&t->modseq_updates); if (array_is_created(&t->expunges)) array_free(&t->expunges); } static void test_mail_index_append(void) { struct mail_index_transaction *t; const struct mail_index_record *appends; ARRAY_TYPE(seq_range) saved_uids_arr; const struct seq_range *saved_uids; unsigned int count; uint32_t seq; hdr.messages_count = 4; t = mail_index_transaction_new(); test_begin("mail index append"); mail_index_append(t, 0, &seq); test_assert(t->log_updates); test_assert(seq == 5); mail_index_append(t, 0, &seq); test_assert(seq == 6); test_assert(!t->appends_nonsorted); t_array_init(&saved_uids_arr, 128); mail_index_append_finish_uids(t, 123, &saved_uids_arr); saved_uids = array_get(&saved_uids_arr, &count); test_assert(count == 1); test_assert(saved_uids[0].seq1 == 123 && saved_uids[0].seq2 == 124); appends = array_get(&t->appends, &count); test_assert(appends[0].uid == 123); test_assert(appends[0].flags == 0); test_assert(appends[1].uid == 124); test_assert(appends[1].flags == 0); test_end(); mail_index_transaction_cleanup(t); /* test with some uids */ t = mail_index_transaction_new(); test_begin("mail index append with uids"); mail_index_append(t, 0, &seq); test_assert(seq == 5); mail_index_append(t, 126, &seq); test_assert(seq == 6); test_assert(!t->appends_nonsorted); mail_index_append(t, 124, &seq); test_assert(seq == 7); test_assert(t->appends_nonsorted); mail_index_append(t, 0, &seq); test_assert(seq == 8); mail_index_append(t, 128, &seq); test_assert(seq == 9); test_assert(t->highest_append_uid == 128); mail_index_append_finish_uids(t, 125, &saved_uids_arr); saved_uids = array_get(&saved_uids_arr, &count); test_assert(count == 4); test_assert(saved_uids[0].seq1 == 129 && saved_uids[0].seq2 == 129); test_assert(saved_uids[1].seq1 == 126 && saved_uids[1].seq2 == 126); test_assert(saved_uids[2].seq1 == 130 && saved_uids[2].seq2 == 131); test_assert(saved_uids[3].seq1 == 128 && saved_uids[3].seq2 == 128); appends = array_get(&t->appends, &count); test_assert(count == 5); test_assert(appends[0].uid == 129); test_assert(appends[1].uid == 126); test_assert(appends[2].uid == 130); test_assert(appends[3].uid == 131); test_assert(appends[4].uid == 128); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_flag_update_fastpath(void) { struct mail_index_transaction *t; const struct mail_index_flag_update *updates; unsigned int count; hdr.messages_count = 20; t = mail_index_transaction_new(); test_begin("mail index flag update fast paths"); mail_index_update_flags_range(t, 13, 14, MODIFY_REPLACE, MAIL_DELETED); test_assert(t->last_update_idx == 0); test_assert(array_count(&t->updates) == 1); mail_index_update_flags_range(t, 15, 15, MODIFY_REPLACE, MAIL_DELETED); test_assert(t->last_update_idx == 0); test_assert(array_count(&t->updates) == 1); mail_index_update_flags_range(t, 16, 16, MODIFY_ADD, MAIL_DELETED); test_assert(t->last_update_idx == 1); test_assert(array_count(&t->updates) == 2); updates = array_get(&t->updates, &count); test_assert(updates[0].uid1 == 13); test_assert(updates[0].uid2 == 15); test_assert(updates[0].add_flags == MAIL_DELETED); test_assert(updates[0].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_SEEN | MAIL_DRAFT)); test_assert(updates[1].uid1 == 16); test_assert(updates[1].uid2 == 16); test_assert(updates[1].add_flags == MAIL_DELETED); test_assert(updates[1].remove_flags == 0); test_assert(!t->log_updates); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_flag_update_simple_merges(void) { struct mail_index_transaction *t; const struct mail_index_flag_update *updates; unsigned int count; hdr.messages_count = 20; t = mail_index_transaction_new(); test_begin("mail index flag update simple merges"); mail_index_update_flags_range(t, 6, 8, MODIFY_ADD, MAIL_FLAGGED); test_assert(t->last_update_idx == 0); mail_index_update_flags_range(t, 5, 6, MODIFY_ADD, MAIL_FLAGGED); test_assert(t->last_update_idx == 0); mail_index_update_flags_range(t, 4, 4, MODIFY_ADD, MAIL_FLAGGED); test_assert(t->last_update_idx == 0); mail_index_update_flags_range(t, 7, 9, MODIFY_ADD, MAIL_FLAGGED); test_assert(t->last_update_idx == 0); mail_index_update_flags_range(t, 10, 10, MODIFY_ADD, MAIL_FLAGGED); updates = array_get(&t->updates, &count); test_assert(count == 1); test_assert(updates[0].uid1 == 4); test_assert(updates[0].uid2 == 10); test_assert(updates[0].add_flags == MAIL_FLAGGED); test_assert(updates[0].remove_flags == 0); mail_index_update_flags_range(t, 12, 12, MODIFY_ADD, MAIL_FLAGGED); mail_index_update_flags_range(t, 11, 11, MODIFY_ADD, MAIL_FLAGGED); updates = array_get(&t->updates, &count); test_assert(count == 1); test_assert(updates[0].uid1 == 4); test_assert(updates[0].uid2 == 12); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_flag_update_complex_merges(void) { struct mail_index_transaction *t; const struct mail_index_flag_update *updates; unsigned int count; hdr.messages_count = 20; t = mail_index_transaction_new(); test_begin("mail index flag update complex merges"); mail_index_update_flags_range(t, 6, 8, MODIFY_REPLACE, MAIL_SEEN); mail_index_update_flags_range(t, 3, 6, MODIFY_ADD, MAIL_FLAGGED); mail_index_update_flags_range(t, 5, 7, MODIFY_ADD, MAIL_DRAFT); mail_index_update_flags_range(t, 6, 6, MODIFY_REPLACE, MAIL_SEEN | MAIL_ANSWERED); mail_index_update_flags_range(t, 5, 10, MODIFY_REMOVE, MAIL_ANSWERED); mail_index_update_flags_range(t, 7, 12, MODIFY_ADD, MAIL_DELETED); updates = array_get(&t->updates, &count); test_assert(count == 7); test_assert(updates[0].uid1 == 3); test_assert(updates[0].uid2 == 4); test_assert(updates[0].add_flags == MAIL_FLAGGED); test_assert(updates[0].remove_flags == 0); test_assert(updates[1].uid1 == 5); test_assert(updates[1].uid2 == 5); test_assert(updates[1].add_flags == (MAIL_DRAFT | MAIL_FLAGGED)); test_assert(updates[1].remove_flags == MAIL_ANSWERED); test_assert(updates[2].uid1 == 6); test_assert(updates[2].uid2 == 6); test_assert(updates[2].add_flags == MAIL_SEEN); test_assert(updates[2].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DELETED | MAIL_DRAFT)); test_assert(updates[3].uid1 == 7); test_assert(updates[3].uid2 == 7); test_assert(updates[3].add_flags == (MAIL_SEEN | MAIL_DRAFT | MAIL_DELETED)); test_assert(updates[3].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED)); test_assert(updates[4].uid1 == 8); test_assert(updates[4].uid2 == 8); test_assert(updates[4].add_flags == (MAIL_SEEN | MAIL_DELETED)); test_assert(updates[4].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DRAFT)); test_assert(updates[5].uid1 == 9); test_assert(updates[5].uid2 == 10); test_assert(updates[5].add_flags == MAIL_DELETED); test_assert(updates[5].remove_flags == MAIL_ANSWERED); test_assert(updates[6].uid1 == 11); test_assert(updates[6].uid2 == 12); test_assert(updates[6].add_flags == MAIL_DELETED); test_assert(updates[6].remove_flags == 0); test_end(); mail_index_transaction_cleanup(t); } static void flags_array_check(struct mail_index_transaction *t, const enum mail_flags *flags, unsigned int msg_count) { const struct mail_index_flag_update *updates; unsigned int i, count, seq; if (array_is_created(&t->updates)) updates = array_get(&t->updates, &count); else { updates = NULL; count = 0; } for (seq = 1, i = 0; i < count; i++) { if (i > 0) { test_assert(updates[i-1].uid2 < updates[i].uid1); test_assert(updates[i-1].uid2 + 1 != updates[i].uid1 || updates[i-1].add_flags != updates[i].add_flags || updates[i-1].remove_flags != updates[i].remove_flags); } for (; seq != updates[i].uid1; seq++) test_assert(flags[seq] == 0); for (; seq <= updates[i].uid2; seq++) test_assert(flags[seq] == updates[i].add_flags); } for (; seq <= msg_count; seq++) test_assert(flags[seq] == 0); } static void test_mail_index_flag_update_random(void) { struct mail_index_transaction *t; unsigned int r, seq1, seq2, seq; enum mail_flags *flags, change; enum modify_type modify_type; hdr.messages_count = 20; t = mail_index_transaction_new(); test_begin("mail index flag update random"); flags = t_new(enum mail_flags, hdr.messages_count + 1); for (r = 0; r < 1000; r++) { change = rand() % (MAIL_FLAGS_NONRECENT+1); seq1 = (rand() % hdr.messages_count) + 1; seq2 = seq1 == hdr.messages_count ? seq1 : (rand() % (hdr.messages_count - seq1)) + seq1; switch (rand() % 3) { case 0: modify_type = MODIFY_ADD; for (seq = seq1; seq <= seq2; seq++) flags[seq] |= change; break; case 1: modify_type = MODIFY_REMOVE; for (seq = seq1; seq <= seq2; seq++) flags[seq] &= ~change; break; case 2: modify_type = MODIFY_REPLACE; for (seq = seq1; seq <= seq2; seq++) flags[seq] = change; break; default: i_unreached(); } mail_index_update_flags_range(t, seq1, seq2, modify_type, change); flags_array_check(t, flags, hdr.messages_count); } test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_cancel_flag_updates(void) { struct mail_index_transaction *t; const struct mail_index_flag_update *updates; unsigned int count; hdr.messages_count = 20; t = mail_index_transaction_new(); test_begin("mail index cancel flag updates"); mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0); updates = array_get(&t->updates, &count); test_assert(count == 1); test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 7); test_assert(mail_index_cancel_flag_updates(t, 5)); test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 7); test_assert(mail_index_cancel_flag_updates(t, 7)); test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 6); test_assert(mail_index_cancel_flag_updates(t, 6)); test_assert(!array_is_created(&t->updates)); mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0); test_assert(mail_index_cancel_flag_updates(t, 6)); updates = array_get(&t->updates, &count); test_assert(count == 2); test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 5); test_assert(updates[1].uid1 == 7 && updates[1].uid2 == 7); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_flag_update_appends(void) { struct mail_index_transaction *t; const struct mail_index_record *appends; const struct mail_index_flag_update *updates; unsigned int count; uint32_t seq; hdr.messages_count = 4; t = mail_index_transaction_new(); test_begin("mail index flag update appends"); mail_index_append(t, 0, &seq); test_assert(seq == 5); mail_index_append(t, 0, &seq); test_assert(seq == 6); mail_index_append(t, 0, &seq); test_assert(seq == 7); mail_index_update_flags_range(t, 5, 6, MODIFY_REPLACE, MAIL_SEEN | MAIL_FLAGGED); mail_index_update_flags_range(t, 6, 7, MODIFY_ADD, MAIL_DRAFT | MAIL_FLAGGED); mail_index_update_flags_range(t, 5, 7, MODIFY_REMOVE, MAIL_FLAGGED); appends = array_get(&t->appends, &count); test_assert(count == 3); test_assert(appends[0].flags == MAIL_SEEN); test_assert(appends[1].flags == (MAIL_SEEN | MAIL_DRAFT)); test_assert(appends[2].flags == MAIL_DRAFT); /* mixed existing/appends */ mail_index_update_flags_range(t, 4, 5, MODIFY_ADD, MAIL_ANSWERED); test_assert(appends[0].flags == (MAIL_SEEN | MAIL_ANSWERED)); updates = array_get(&t->updates, &count); test_assert(count == 1); test_assert(updates[0].uid1 == 4); test_assert(updates[0].uid2 == 4); test_assert(updates[0].add_flags == MAIL_ANSWERED); test_end(); mail_index_transaction_cleanup(t); } static bool test_flag_update_pos(struct mail_index_transaction *t, uint32_t seq, unsigned int idx) { unsigned int i, j, count; count = array_count(&t->updates); for (i = 0; i < idx; i++) { for (j = idx + 1; j <= count; j++) { if (mail_index_transaction_get_flag_update_pos(t, i, j, seq) != idx) { test_assert(FALSE); return FALSE; } } } return TRUE; } static void test_mail_index_transaction_get_flag_update_pos(void) { struct mail_index_transaction *t; test_begin("mail index transaction get flag update pos"); hdr.messages_count = 10; t = mail_index_transaction_new(); mail_index_update_flags_range(t, 1, 1, MODIFY_REPLACE, 0); mail_index_update_flags_range(t, 3, 4, MODIFY_REPLACE, 0); mail_index_update_flags_range(t, 6, 7, MODIFY_REPLACE, 0); mail_index_update_flags_range(t, 9, 10, MODIFY_REPLACE, 0); test_assert(test_flag_update_pos(t, 1, 0)); test_assert(test_flag_update_pos(t, 2, 1)); test_assert(test_flag_update_pos(t, 3, 1)); test_assert(test_flag_update_pos(t, 4, 1)); test_assert(test_flag_update_pos(t, 5, 2)); test_assert(test_flag_update_pos(t, 6, 2)); test_assert(test_flag_update_pos(t, 7, 2)); test_assert(test_flag_update_pos(t, 8, 3)); test_assert(test_flag_update_pos(t, 9, 3)); test_assert(test_flag_update_pos(t, 10, 3)); test_assert(test_flag_update_pos(t, 11, 4)); test_assert(test_flag_update_pos(t, 12, 4)); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_modseq_update(void) { struct mail_index_transaction *t; const struct mail_transaction_modseq_update *ups; unsigned int count; test_begin("mail index modseq update"); hdr.messages_count = 10; t = mail_index_transaction_new(); mail_index_update_modseq(t, 4, 0x8234fefa02747429ULL); mail_index_update_modseq(t, 6, 0x1234567890abcdefULL); mail_index_update_modseq(t, 2, 0xfeed); mail_index_update_modseq(t, 4, 2); /* modseq=1 updates are ignored: */ mail_index_update_modseq(t, 5, 1); mail_index_update_modseq(t, 6, 1); ups = array_get(&t->modseq_updates, &count); test_assert(count == 4); test_assert(ups[0].uid == 4 && ups[0].modseq_high32 == 0x8234fefa && ups[0].modseq_low32 == 0x02747429); test_assert(ups[1].uid == 6 && ups[1].modseq_high32 == 0x12345678 && ups[1].modseq_low32 == 0x90abcdef); test_assert(ups[2].uid == 2 && ups[2].modseq_high32 == 0 && ups[2].modseq_low32 == 0xfeed); test_assert(ups[3].uid == 4 && ups[3].modseq_high32 == 0 && ups[3].modseq_low32 == 2); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_expunge(void) { static guid_128_t empty_guid = { 0, }; struct mail_index_transaction *t; const struct mail_transaction_expunge_guid *expunges; guid_128_t guid2, guid3, guid4; unsigned int i, count; test_begin("mail index expunge"); hdr.messages_count = 10; t = mail_index_transaction_new(); for (i = 0; i < sizeof(guid2); i++) { guid2[i] = i + 1; guid3[i] = i ^ 0xff; guid4[i] = i + 0x80; } mail_index_expunge_guid(t, 4, guid4); test_assert(!t->expunges_nonsorted); mail_index_expunge_guid(t, 2, guid2); test_assert(t->expunges_nonsorted); mail_index_expunge_guid(t, 3, guid3); mail_index_expunge(t, 1); mail_index_expunge(t, 5); expunges = array_get(&t->expunges, &count); test_assert(count == 5); test_assert(expunges[0].uid == 4); test_assert(memcmp(expunges[0].guid_128, guid4, sizeof(guid4)) == 0); test_assert(expunges[1].uid == 2); test_assert(memcmp(expunges[1].guid_128, guid2, sizeof(guid2)) == 0); test_assert(expunges[2].uid == 3); test_assert(memcmp(expunges[2].guid_128, guid3, sizeof(guid3)) == 0); test_assert(expunges[3].uid == 1); test_assert(memcmp(expunges[3].guid_128, empty_guid, sizeof(empty_guid)) == 0); test_assert(expunges[4].uid == 5); test_assert(memcmp(expunges[4].guid_128, empty_guid, sizeof(empty_guid)) == 0); test_end(); mail_index_transaction_cleanup(t); } static void test_mail_index_update_day_first_uid(void) { struct { uint32_t now; uint32_t old_day_stamp; uint32_t new_day_stamp; uint32_t new_day_first_uid[8]; } tests[] = { /* 1487116800 = 2017-02-15 00:00:00 UTC */ { 1487116800, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } }, /* still same day */ { 1487116800+3600*24-1, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } }, /* one day earlier */ { 1487116800-1, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } }, /* next day */ { 1487116800+3600*24, 1487116800, 1487116800+3600*24, { 9, 8, 7, 6, 5, 4, 3, 2 } }, { 1487116800+3600*24*2-1, 1487116800, 1487116800+3600*24, { 9, 8, 7, 6, 5, 4, 3, 2 } }, /* 2 days */ { 1487116800+3600*24*2, 1487116800, 1487116800+3600*24*2, { 9, 8, 8, 7, 6, 5, 4, 3 } }, /* 3 days */ { 1487116800+3600*24*3, 1487116800, 1487116800+3600*24*3, { 9, 8, 8, 8, 7, 6, 5, 4 } }, /* 4 days */ { 1487116800+3600*24*4, 1487116800, 1487116800+3600*24*4, { 9, 8, 8, 8, 8, 7, 6, 5 } }, /* 5 days */ { 1487116800+3600*24*5, 1487116800, 1487116800+3600*24*5, { 9, 8, 8, 8, 8, 8, 7, 6 } }, /* 6 days */ { 1487116800+3600*24*6, 1487116800, 1487116800+3600*24*6, { 9, 8, 8, 8, 8, 8, 8, 7 } }, /* 7 days */ { 1487116800+3600*24*7, 1487116800, 1487116800+3600*24*7, { 9, 8, 8, 8, 8, 8, 8, 8 } }, /* 8 days */ { 1487116800+3600*24*8, 1487116800, 1487116800+3600*24*8, { 9, 8, 8, 8, 8, 8, 8, 8 } }, /* 366 days */ { 1487116800+3600*24*366, 1487116800, 1487116800+3600*24*366, { 9, 8, 8, 8, 8, 8, 8, 8 } }, }; struct mail_index_transaction *t; struct mail_index_record *rec; unsigned int i, j; test_begin("mail index update day first uid"); /* daylight savings times were confusing these tests, so we'll now just assume that TZ=UTC */ test_assert(timezone == 0); hdr.messages_count = 10; t = mail_index_transaction_new(); t->view = t_new(struct mail_index_view, 1); t->view->map = t_new(struct mail_index_map, 1); t_array_init(&t->appends, 1); rec = array_append_space(&t->appends); rec->uid = 9; for (i = 0; i < N_ELEMENTS(tests); i++) { i_zero(&hdr); for (j = 0; j < N_ELEMENTS(hdr.day_first_uid); j++) hdr.day_first_uid[j] = 8-j; hdr.day_stamp = tests[i].old_day_stamp + timezone; memcpy(t->post_hdr_change, &hdr, sizeof(hdr)); mail_index_update_day_headers(t, tests[i].now + timezone); struct mail_index_header new_hdr; memcpy(&new_hdr, t->post_hdr_change, sizeof(new_hdr)); test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp + timezone, i); test_assert_idx(memcmp(new_hdr.day_first_uid, tests[i].new_day_first_uid, sizeof(uint32_t) * 8) == 0, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_index_append, test_mail_index_flag_update_fastpath, test_mail_index_flag_update_simple_merges, test_mail_index_flag_update_complex_merges, test_mail_index_flag_update_random, test_mail_index_flag_update_appends, test_mail_index_cancel_flag_updates, test_mail_index_transaction_get_flag_update_pos, test_mail_index_modseq_update, test_mail_index_expunge, test_mail_index_update_day_first_uid, NULL }; /* daylight saving time confuses things */ putenv("TZ=UTC"); tzset(); return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-export.c0000644000175000017500000004477313165463624021055 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mail-transaction-log-private.h" #include "mail-index-transaction-private.h" struct mail_index_export_context { struct mail_index_transaction *trans; struct mail_transaction_log_append_ctx *append_ctx; }; static void log_append_buffer(struct mail_index_export_context *ctx, const buffer_t *buf, enum mail_transaction_type type) { mail_transaction_log_append_add(ctx->append_ctx, type, buf->data, buf->used); } static void log_append_flag_updates(struct mail_index_export_context *ctx, struct mail_index_transaction *t) { ARRAY(struct mail_transaction_flag_update) log_updates; const struct mail_index_flag_update *updates; struct mail_transaction_flag_update *log_update; unsigned int i, count; updates = array_get(&t->updates, &count); if (count == 0) return; i_array_init(&log_updates, count); for (i = 0; i < count; i++) { log_update = array_append_space(&log_updates); log_update->uid1 = updates[i].uid1; log_update->uid2 = updates[i].uid2; log_update->add_flags = updates[i].add_flags & 0xff; log_update->remove_flags = updates[i].remove_flags & 0xff; if ((updates[i].add_flags & MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ) != 0) log_update->modseq_inc_flag = 1; } log_append_buffer(ctx, log_updates.arr.buffer, MAIL_TRANSACTION_FLAG_UPDATE); array_free(&log_updates); } static const buffer_t * log_get_hdr_update_buffer(struct mail_index_transaction *t, bool prepend) { buffer_t *buf; const unsigned char *data, *mask; struct mail_transaction_header_update u; uint16_t offset; int state = 0; i_zero(&u); data = prepend ? t->pre_hdr_change : t->post_hdr_change; mask = prepend ? t->pre_hdr_mask : t->post_hdr_mask; buf = buffer_create_dynamic(pool_datastack_create(), 256); for (offset = 0; offset <= sizeof(t->pre_hdr_change); offset++) { if (offset < sizeof(t->pre_hdr_change) && mask[offset]) { if (state == 0) { u.offset = offset; state++; } } else { if (state > 0) { u.size = offset - u.offset; buffer_append(buf, &u, sizeof(u)); buffer_append(buf, data + u.offset, u.size); state = 0; } } } return buf; } static unsigned int ext_hdr_update_get_size(const struct mail_index_transaction_ext_hdr_update *hu) { unsigned int i; for (i = hu->alloc_size; i > 0; i--) { if (hu->mask[i-1] != 0) return i; } return 0; } static void log_append_ext_intro(struct mail_index_export_context *ctx, uint32_t ext_id, uint32_t reset_id, unsigned int *hdr_size_r) { struct mail_index_transaction *t = ctx->trans; const struct mail_index_registered_ext *rext; const struct mail_index_ext *ext; struct mail_transaction_ext_intro *intro, *resizes; buffer_t *buf; uint32_t idx; unsigned int count; i_assert(ext_id != (uint32_t)-1); if (t->reset || !mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) { /* new extension */ idx = (uint32_t)-1; } rext = array_idx(&t->view->index->extensions, ext_id); if (!array_is_created(&t->ext_resizes)) { resizes = NULL; count = 0; } else { resizes = array_get_modifiable(&t->ext_resizes, &count); } buf = buffer_create_dynamic(pool_datastack_create(), 128); if (ext_id < count && resizes[ext_id].name_size != 0) { /* we're resizing the extension. use the resize struct. */ intro = &resizes[ext_id]; if (idx != (uint32_t)-1) { intro->ext_id = idx; intro->name_size = 0; } else { intro->ext_id = (uint32_t)-1; intro->name_size = strlen(rext->name); } buffer_append(buf, intro, sizeof(*intro)); } else { /* generate a new intro structure */ intro = buffer_append_space_unsafe(buf, sizeof(*intro)); intro->ext_id = idx; intro->record_size = rext->record_size; intro->record_align = rext->record_align; if (idx == (uint32_t)-1) { intro->hdr_size = rext->hdr_size; intro->name_size = strlen(rext->name); } else { ext = array_idx(&t->view->index->map->extensions, idx); intro->hdr_size = ext->hdr_size; intro->name_size = 0; } intro->flags = MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK; /* handle increasing header size automatically */ if (array_is_created(&t->ext_hdr_updates) && ext_id < array_count(&t->ext_hdr_updates)) { const struct mail_index_transaction_ext_hdr_update *hu; unsigned int hdr_update_size; hu = array_idx(&t->ext_hdr_updates, ext_id); hdr_update_size = ext_hdr_update_get_size(hu); if (intro->hdr_size < hdr_update_size) intro->hdr_size = hdr_update_size; } } i_assert(intro->record_size != 0 || intro->hdr_size != 0); if (reset_id != 0) { /* we're going to reset this extension in this transaction */ intro->reset_id = reset_id; } else if (idx != (uint32_t)-1) { /* use the existing reset_id */ const struct mail_index_ext *map_ext = array_idx(&t->view->index->map->extensions, idx); intro->reset_id = map_ext->reset_id; } else { /* new extension, reset_id defaults to 0 */ } buffer_append(buf, rext->name, intro->name_size); if ((buf->used % 4) != 0) buffer_append_zero(buf, 4 - (buf->used % 4)); if (ctx->append_ctx->new_highest_modseq == 0 && strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) { /* modseq tracking started */ ctx->append_ctx->new_highest_modseq = 1; } log_append_buffer(ctx, buf, MAIL_TRANSACTION_EXT_INTRO); *hdr_size_r = intro->hdr_size; } static void log_append_ext_hdr_update(struct mail_index_export_context *ctx, const struct mail_index_transaction_ext_hdr_update *hdr, unsigned int ext_hdr_size) { buffer_t *buf; const unsigned char *data, *mask; struct mail_transaction_ext_hdr_update u; struct mail_transaction_ext_hdr_update32 u32; size_t offset; bool started = FALSE, use_32 = hdr->alloc_size >= 65536; i_zero(&u); i_zero(&u32); data = hdr->data; mask = hdr->mask; buf = buffer_create_dynamic(pool_datastack_create(), 256); for (offset = 0; offset <= hdr->alloc_size; offset++) { if (offset < hdr->alloc_size && mask[offset] != 0) { if (!started) { u32.offset = offset; started = TRUE; } } else { if (started) { u32.size = offset - u32.offset; if (use_32) buffer_append(buf, &u32, sizeof(u32)); else { u.offset = u32.offset; u.size = u32.size; buffer_append(buf, &u, sizeof(u)); } i_assert(u32.offset + u32.size <= ext_hdr_size); buffer_append(buf, data + u32.offset, u32.size); started = FALSE; } } } if (buf->used % 4 != 0) buffer_append_zero(buf, 4 - buf->used % 4); log_append_buffer(ctx, buf, use_32 ? MAIL_TRANSACTION_EXT_HDR_UPDATE32 : MAIL_TRANSACTION_EXT_HDR_UPDATE); } static void mail_transaction_log_append_ext_intros(struct mail_index_export_context *ctx) { struct mail_index_transaction *t = ctx->trans; const struct mail_transaction_ext_intro *resize; const struct mail_index_transaction_ext_hdr_update *hdrs; struct mail_transaction_ext_reset ext_reset; unsigned int resize_count, ext_count = 0; unsigned int hdrs_count, reset_id_count, reset_count, hdr_size; uint32_t ext_id, reset_id; const struct mail_transaction_ext_reset *reset; const uint32_t *reset_ids; buffer_t reset_buf; if (!array_is_created(&t->ext_resizes)) { resize = NULL; resize_count = 0; } else { resize = array_get(&t->ext_resizes, &resize_count); if (ext_count < resize_count) ext_count = resize_count; } if (!array_is_created(&t->ext_reset_ids)) { reset_ids = NULL; reset_id_count = 0; } else { reset_ids = array_get(&t->ext_reset_ids, &reset_id_count); } if (!array_is_created(&t->ext_resets)) { reset = NULL; reset_count = 0; } else { reset = array_get(&t->ext_resets, &reset_count); if (ext_count < reset_count) ext_count = reset_count; } if (!array_is_created(&t->ext_hdr_updates)) { hdrs = NULL; hdrs_count = 0; } else { hdrs = array_get(&t->ext_hdr_updates, &hdrs_count); if (ext_count < hdrs_count) ext_count = hdrs_count; } i_zero(&ext_reset); buffer_create_from_data(&reset_buf, &ext_reset, sizeof(ext_reset)); buffer_set_used_size(&reset_buf, sizeof(ext_reset)); for (ext_id = 0; ext_id < ext_count; ext_id++) { if (ext_id < reset_count) ext_reset = reset[ext_id]; else ext_reset.new_reset_id = 0; if ((ext_id < resize_count && resize[ext_id].name_size) || ext_reset.new_reset_id != 0 || (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) { if (ext_reset.new_reset_id != 0) { /* we're going to reset this extension immediately after the intro */ reset_id = 0; } else { reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0; } log_append_ext_intro(ctx, ext_id, reset_id, &hdr_size); } else { hdr_size = 0; } if (ext_reset.new_reset_id != 0) { i_assert(ext_id < reset_id_count && ext_reset.new_reset_id == reset_ids[ext_id]); log_append_buffer(ctx, &reset_buf, MAIL_TRANSACTION_EXT_RESET); } if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) { T_BEGIN { log_append_ext_hdr_update(ctx, &hdrs[ext_id], hdr_size); } T_END; } } } static void log_append_ext_recs(struct mail_index_export_context *ctx, const ARRAY_TYPE(seq_array_array) *arr, enum mail_transaction_type type) { struct mail_index_transaction *t = ctx->trans; const ARRAY_TYPE(seq_array) *updates; const uint32_t *reset_ids; unsigned int ext_id, count, reset_id_count, hdr_size; uint32_t reset_id; if (!array_is_created(&t->ext_reset_ids)) { reset_ids = NULL; reset_id_count = 0; } else { reset_ids = array_get_modifiable(&t->ext_reset_ids, &reset_id_count); } updates = array_get(arr, &count); for (ext_id = 0; ext_id < count; ext_id++) { if (!array_is_created(&updates[ext_id])) continue; reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0; log_append_ext_intro(ctx, ext_id, reset_id, &hdr_size); log_append_buffer(ctx, updates[ext_id].arr.buffer, type); } } static void log_append_keyword_update(struct mail_index_export_context *ctx, buffer_t *tmp_buf, enum modify_type modify_type, const char *keyword, const buffer_t *uid_buffer) { struct mail_transaction_keyword_update kt_hdr; i_assert(uid_buffer->used > 0); i_zero(&kt_hdr); kt_hdr.modify_type = modify_type; kt_hdr.name_size = strlen(keyword); buffer_set_used_size(tmp_buf, 0); buffer_append(tmp_buf, &kt_hdr, sizeof(kt_hdr)); buffer_append(tmp_buf, keyword, kt_hdr.name_size); if ((tmp_buf->used % 4) != 0) buffer_append_zero(tmp_buf, 4 - (tmp_buf->used % 4)); buffer_append(tmp_buf, uid_buffer->data, uid_buffer->used); log_append_buffer(ctx, tmp_buf, MAIL_TRANSACTION_KEYWORD_UPDATE); } static enum mail_index_fsync_mask log_append_keyword_updates(struct mail_index_export_context *ctx) { const struct mail_index_transaction_keyword_update *updates; const char *const *keywords; buffer_t *tmp_buf; enum mail_index_fsync_mask change_mask = 0; unsigned int i, count, keywords_count; tmp_buf = buffer_create_dynamic(pool_datastack_create(), 64); keywords = array_get_modifiable(&ctx->trans->view->index->keywords, &keywords_count); updates = array_get_modifiable(&ctx->trans->keyword_updates, &count); i_assert(count <= keywords_count); for (i = 0; i < count; i++) { if (array_is_created(&updates[i].add_seq) && array_count(&updates[i].add_seq) > 0) { change_mask |= MAIL_INDEX_FSYNC_MASK_KEYWORDS; log_append_keyword_update(ctx, tmp_buf, MODIFY_ADD, keywords[i], updates[i].add_seq.arr.buffer); } if (array_is_created(&updates[i].remove_seq) && array_count(&updates[i].remove_seq) > 0) { change_mask |= MAIL_INDEX_FSYNC_MASK_KEYWORDS; log_append_keyword_update(ctx, tmp_buf, MODIFY_REMOVE, keywords[i], updates[i].remove_seq.arr.buffer); } } return change_mask; } void mail_index_transaction_export(struct mail_index_transaction *t, struct mail_transaction_log_append_ctx *append_ctx) { static uint8_t null4[4] = { 0, 0, 0, 0 }; enum mail_index_fsync_mask change_mask = 0; struct mail_index_export_context ctx; i_zero(&ctx); ctx.trans = t; ctx.append_ctx = append_ctx; if (t->index_undeleted) { i_assert(!t->index_deleted); mail_transaction_log_append_add(ctx.append_ctx, MAIL_TRANSACTION_INDEX_UNDELETED, &null4, 4); } /* send all extension introductions and resizes before appends to avoid resize overhead as much as possible */ mail_transaction_log_append_ext_intros(&ctx); if (t->pre_hdr_changed) { log_append_buffer(&ctx, log_get_hdr_update_buffer(t, TRUE), MAIL_TRANSACTION_HEADER_UPDATE); } if (t->attribute_updates != NULL) { buffer_append_c(t->attribute_updates, '\0'); /* need to have 32bit alignment */ if (t->attribute_updates->used % 4 != 0) { buffer_append_zero(t->attribute_updates, 4 - t->attribute_updates->used % 4); } /* append the timestamp and value lengths */ buffer_append(t->attribute_updates, t->attribute_updates_suffix->data, t->attribute_updates_suffix->used); i_assert(t->attribute_updates->used % 4 == 0); log_append_buffer(&ctx, t->attribute_updates, MAIL_TRANSACTION_ATTRIBUTE_UPDATE); } if (array_is_created(&t->appends)) { change_mask |= MAIL_INDEX_FSYNC_MASK_APPENDS; log_append_buffer(&ctx, t->appends.arr.buffer, MAIL_TRANSACTION_APPEND); } if (array_is_created(&t->updates)) { change_mask |= MAIL_INDEX_FSYNC_MASK_FLAGS; log_append_flag_updates(&ctx, t); } if (array_is_created(&t->ext_rec_updates)) { log_append_ext_recs(&ctx, &t->ext_rec_updates, MAIL_TRANSACTION_EXT_REC_UPDATE); } if (array_is_created(&t->ext_rec_atomics)) { log_append_ext_recs(&ctx, &t->ext_rec_atomics, MAIL_TRANSACTION_EXT_ATOMIC_INC); } if (array_is_created(&t->keyword_updates)) change_mask |= log_append_keyword_updates(&ctx); /* keep modseq updates almost last */ if (array_is_created(&t->modseq_updates)) { log_append_buffer(&ctx, t->modseq_updates.arr.buffer, MAIL_TRANSACTION_MODSEQ_UPDATE); } if (array_is_created(&t->expunges)) { /* non-external expunges are only requests, ignore them when checking fsync_mask */ if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0) change_mask |= MAIL_INDEX_FSYNC_MASK_EXPUNGES; log_append_buffer(&ctx, t->expunges.arr.buffer, MAIL_TRANSACTION_EXPUNGE_GUID); } if (t->post_hdr_changed) { log_append_buffer(&ctx, log_get_hdr_update_buffer(t, FALSE), MAIL_TRANSACTION_HEADER_UPDATE); } if (t->index_deleted) { i_assert(!t->index_undeleted); mail_transaction_log_append_add(ctx.append_ctx, MAIL_TRANSACTION_INDEX_DELETED, &null4, 4); } append_ctx->index_sync_transaction = t->sync_transaction; append_ctx->tail_offset_changed = t->tail_offset_changed; append_ctx->want_fsync = (t->view->index->fsync_mask & change_mask) != 0 || (t->flags & MAIL_INDEX_TRANSACTION_FLAG_FSYNC) != 0; } static unsigned int count_modseq_incs_with(struct mail_index_transaction *t, ARRAY_TYPE(seq_range) *tmp_seqs, const ARRAY_TYPE(seq_range) *orig_seqs) { if (!array_is_created(orig_seqs)) return 0; array_clear(tmp_seqs); array_append_array(tmp_seqs, orig_seqs); mail_index_transaction_seq_range_to_uid(t, tmp_seqs); return array_count(tmp_seqs) > 0 ? 1 : 0; } static unsigned int mail_index_transaction_keywords_count_modseq_incs(struct mail_index_transaction *t) { const struct mail_index_transaction_keyword_update *update; ARRAY_TYPE(seq_range) tmp_seqs; unsigned int count = 0; i_array_init(&tmp_seqs, 64); array_foreach_modifiable(&t->keyword_updates, update) { count += count_modseq_incs_with(t, &tmp_seqs, &update->add_seq); count += count_modseq_incs_with(t, &tmp_seqs, &update->remove_seq); } array_free(&tmp_seqs); return count; } static bool transaction_flag_updates_have_non_internal(struct mail_index_transaction *t) { struct mail_transaction_log_file *file = t->view->index->log->head; const uint8_t internal_flags = MAIL_INDEX_MAIL_FLAG_BACKEND | MAIL_INDEX_MAIL_FLAG_DIRTY; const struct mail_index_flag_update *u; const unsigned int hdr_version = MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr); if (!MAIL_TRANSACTION_LOG_VERSION_HAVE(hdr_version, HIDE_INTERNAL_MODSEQS)) { /* this check can be a bit racy if the call isn't done while transaction log is locked. practically it won't matter now though. */ return array_count(&t->updates) > 0; } array_foreach(&t->updates, u) { uint8_t changed_flags = u->add_flags | u->remove_flags; if ((changed_flags & ~internal_flags) != 0) return TRUE; } return FALSE; } uint64_t mail_index_transaction_get_highest_modseq(struct mail_index_transaction *t) { struct mail_transaction_log_file *file = t->view->index->log->head; uint64_t new_highest_modseq = file->sync_highest_modseq; i_assert(file->locked); if (new_highest_modseq == 0) { /* highest-modseq tracking isn't enabled in this transaction log file. This shouldn't happen with logs created since v2.2.26+, because initial_modseq is always set. We don't also bother checking if this transaction itself enables the highest-modseq tracking, because it's always done as a standalone transaction in mail_index_modseq_enable(), which doesn't care about this function. */ i_warning("%s: Requested highest-modseq for transaction, " "but modseq tracking isn't enabled for the file " "(this shouldn't happen)", file->filepath); return 0; } /* finish everything that can affect highest-modseq */ mail_index_transaction_finish_so_far(t); /* NOTE: keep in sync with mail_transaction_update_modseq() */ if (array_is_created(&t->appends) && array_count(&t->appends) > 0) { /* sorting may change the order of keyword_updates, */ new_highest_modseq++; } if (array_is_created(&t->updates) && transaction_flag_updates_have_non_internal(t) > 0) new_highest_modseq++; if (array_is_created(&t->keyword_updates)) { new_highest_modseq += mail_index_transaction_keywords_count_modseq_incs(t); } if (t->attribute_updates != NULL) new_highest_modseq++; /* NOTE: the order of modseq_updates and everything following it must match mail_index_transaction_export(). */ if (array_is_created(&t->modseq_updates)) { const struct mail_transaction_modseq_update *mu; /* mail_index_update_highest_modseq() is handled here also, as a special case of uid==0. */ array_foreach(&t->modseq_updates, mu) { uint64_t modseq = ((uint64_t)mu->modseq_high32 << 32) | mu->modseq_low32; if (new_highest_modseq < modseq) new_highest_modseq = modseq; } } if (array_is_created(&t->expunges) && array_count(&t->expunges) > 0 && (t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0) new_highest_modseq++; return new_highest_modseq; } dovecot-2.2.33.2/src/lib-index/mail-index-sync-private.h0000644000175000017500000000672713165463624017637 00000000000000#ifndef MAIL_INDEX_SYNC_PRIVATE_H #define MAIL_INDEX_SYNC_PRIVATE_H #include "mail-index-private.h" #include "mail-transaction-log.h" struct uid_range { uint32_t uid1, uid2; }; ARRAY_DEFINE_TYPE(uid_range, struct uid_range); struct mail_index_sync_list { const ARRAY_TYPE(uid_range) *array; unsigned int idx; unsigned int keyword_idx:31; unsigned int keyword_remove:1; }; struct mail_index_expunge_handler { mail_index_expunge_handler_t *handler; void *context; void **sync_context; uint32_t record_offset; }; struct mail_index_sync_map_ctx { struct mail_index_view *view; struct mail_index_modseq_sync *modseq_ctx; uint32_t cur_ext_map_idx; uint32_t cur_ext_record_size; uint32_t ext_intro_seq; uoff_t ext_intro_offset, ext_intro_end_offset; ARRAY(struct mail_index_expunge_handler) expunge_handlers; ARRAY(void *) extra_contexts; buffer_t *unknown_extensions; enum mail_index_sync_handler_type type; unsigned int sync_handlers_initialized:1; unsigned int expunge_handlers_set:1; unsigned int expunge_handlers_used:1; unsigned int cur_ext_ignore:1; unsigned int internal_update:1; /* used by keywords for ext_intro */ unsigned int errors:1; }; extern struct mail_transaction_map_functions mail_index_map_sync_funcs; void mail_index_sync_map_init(struct mail_index_sync_map_ctx *sync_map_ctx, struct mail_index_view *view, enum mail_index_sync_handler_type type); void mail_index_sync_map_deinit(struct mail_index_sync_map_ctx *sync_map_ctx); int mail_index_sync_map(struct mail_index_map **map, enum mail_index_sync_handler_type type, bool force, const char *sync_reason); int mail_index_sync_record(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const void *data); struct mail_index_map * mail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx); void mail_index_sync_init_expunge_handlers(struct mail_index_sync_map_ctx *ctx); void mail_index_sync_deinit_expunge_handlers(struct mail_index_sync_map_ctx *ctx); void mail_index_sync_init_handlers(struct mail_index_sync_map_ctx *ctx); void mail_index_sync_deinit_handlers(struct mail_index_sync_map_ctx *ctx); void mail_index_sync_ext_init(struct mail_index_sync_map_ctx *ctx, const char *name, bool fix_size, uint32_t *ext_map_idx_r); int mail_index_sync_ext_intro(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_intro *u); int mail_index_sync_ext_reset(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_reset *u); int mail_index_sync_ext_hdr_update(struct mail_index_sync_map_ctx *ctx, uint32_t offset, uint32_t size, const void *data); int mail_index_sync_ext_rec_update(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_rec_update *u); int mail_index_sync_ext_atomic_inc(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_atomic_inc *u); int mail_index_sync_keywords(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const struct mail_transaction_keyword_update *rec); int mail_index_sync_keywords_reset(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const struct mail_transaction_keyword_reset *r); void mail_index_sync_set_corrupted(struct mail_index_sync_map_ctx *ctx, const char *fmt, ...) ATTR_FORMAT(2, 3); #ifdef DEBUG void mail_index_map_check(struct mail_index_map *map); #endif #endif dovecot-2.2.33.2/src/lib-index/Makefile.in0000644000175000017500000010655013172375573015060 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-index ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libindex_la_LIBADD = am_libindex_la_OBJECTS = mail-cache.lo mail-cache-compress.lo \ mail-cache-decisions.lo mail-cache-fields.lo \ mail-cache-lookup.lo mail-cache-transaction.lo \ mail-cache-sync-update.lo mail-index.lo \ mail-index-alloc-cache.lo mail-index-dummy-view.lo \ mail-index-fsck.lo mail-index-lock.lo mail-index-map.lo \ mail-index-map-hdr.lo mail-index-map-read.lo \ mail-index-modseq.lo mail-index-transaction.lo \ mail-index-transaction-export.lo \ mail-index-transaction-finish.lo \ mail-index-transaction-sort-appends.lo \ mail-index-transaction-update.lo \ mail-index-transaction-view.lo mail-index-strmap.lo \ mail-index-sync.lo mail-index-sync-ext.lo \ mail-index-sync-keywords.lo mail-index-sync-update.lo \ mail-index-util.lo mail-index-view.lo mail-index-view-sync.lo \ mail-index-write.lo mail-transaction-log.lo \ mail-transaction-log-append.lo mail-transaction-log-file.lo \ mail-transaction-log-view.lo mailbox-log.lo libindex_la_OBJECTS = $(am_libindex_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-mail-index-map$(EXEEXT) \ test-mail-index-modseq$(EXEEXT) \ test-mail-index-sync-ext$(EXEEXT) \ test-mail-index-transaction-finish$(EXEEXT) \ test-mail-index-transaction-update$(EXEEXT) \ test-mail-transaction-log-append$(EXEEXT) \ test-mail-transaction-log-file$(EXEEXT) \ test-mail-transaction-log-view$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_mail_index_map_OBJECTS = test-mail-index-map.$(OBJEXT) test_mail_index_map_OBJECTS = $(am_test_mail_index_map_OBJECTS) am_test_mail_index_modseq_OBJECTS = test-mail-index-modseq.$(OBJEXT) test_mail_index_modseq_OBJECTS = $(am_test_mail_index_modseq_OBJECTS) am_test_mail_index_sync_ext_OBJECTS = \ test-mail-index-sync-ext.$(OBJEXT) test_mail_index_sync_ext_OBJECTS = \ $(am_test_mail_index_sync_ext_OBJECTS) am_test_mail_index_transaction_finish_OBJECTS = \ test-mail-index-transaction-finish.$(OBJEXT) test_mail_index_transaction_finish_OBJECTS = \ $(am_test_mail_index_transaction_finish_OBJECTS) am_test_mail_index_transaction_update_OBJECTS = \ test-mail-index-transaction-update.$(OBJEXT) test_mail_index_transaction_update_OBJECTS = \ $(am_test_mail_index_transaction_update_OBJECTS) am_test_mail_transaction_log_append_OBJECTS = \ test-mail-transaction-log-append.$(OBJEXT) test_mail_transaction_log_append_OBJECTS = \ $(am_test_mail_transaction_log_append_OBJECTS) am_test_mail_transaction_log_file_OBJECTS = \ test-mail-transaction-log-file.$(OBJEXT) test_mail_transaction_log_file_OBJECTS = \ $(am_test_mail_transaction_log_file_OBJECTS) am_test_mail_transaction_log_view_OBJECTS = \ test-mail-transaction-log-view.$(OBJEXT) test_mail_transaction_log_view_OBJECTS = \ $(am_test_mail_transaction_log_view_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libindex_la_SOURCES) $(test_mail_index_map_SOURCES) \ $(test_mail_index_modseq_SOURCES) \ $(test_mail_index_sync_ext_SOURCES) \ $(test_mail_index_transaction_finish_SOURCES) \ $(test_mail_index_transaction_update_SOURCES) \ $(test_mail_transaction_log_append_SOURCES) \ $(test_mail_transaction_log_file_SOURCES) \ $(test_mail_transaction_log_view_SOURCES) DIST_SOURCES = $(libindex_la_SOURCES) $(test_mail_index_map_SOURCES) \ $(test_mail_index_modseq_SOURCES) \ $(test_mail_index_sync_ext_SOURCES) \ $(test_mail_index_transaction_finish_SOURCES) \ $(test_mail_index_transaction_update_SOURCES) \ $(test_mail_transaction_log_append_SOURCES) \ $(test_mail_transaction_log_file_SOURCES) \ $(test_mail_transaction_log_view_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libindex.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail libindex_la_SOURCES = \ mail-cache.c \ mail-cache-compress.c \ mail-cache-decisions.c \ mail-cache-fields.c \ mail-cache-lookup.c \ mail-cache-transaction.c \ mail-cache-sync-update.c \ mail-index.c \ mail-index-alloc-cache.c \ mail-index-dummy-view.c \ mail-index-fsck.c \ mail-index-lock.c \ mail-index-map.c \ mail-index-map-hdr.c \ mail-index-map-read.c \ mail-index-modseq.c \ mail-index-transaction.c \ mail-index-transaction-export.c \ mail-index-transaction-finish.c \ mail-index-transaction-sort-appends.c \ mail-index-transaction-update.c \ mail-index-transaction-view.c \ mail-index-strmap.c \ mail-index-sync.c \ mail-index-sync-ext.c \ mail-index-sync-keywords.c \ mail-index-sync-update.c \ mail-index-util.c \ mail-index-view.c \ mail-index-view-sync.c \ mail-index-write.c \ mail-transaction-log.c \ mail-transaction-log-append.c \ mail-transaction-log-file.c \ mail-transaction-log-view.c \ mailbox-log.c headers = \ mail-cache.h \ mail-cache-private.h \ mail-index.h \ mail-index-alloc-cache.h \ mail-index-modseq.h \ mail-index-private.h \ mail-index-strmap.h \ mail-index-sync-private.h \ mail-index-transaction-private.h \ mail-index-util.h \ mail-index-view-private.h \ mail-transaction-log.h \ mail-transaction-log-private.h \ mail-transaction-log-view-private.h \ mailbox-log.h test_programs = \ test-mail-index-map \ test-mail-index-modseq \ test-mail-index-sync-ext \ test-mail-index-transaction-finish \ test-mail-index-transaction-update \ test-mail-transaction-log-append \ test-mail-transaction-log-file \ test-mail-transaction-log-view test_libs = \ mail-index-util.lo \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_map_SOURCES = test-mail-index-map.c test_mail_index_map_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_map_DEPENDENCIES = $(test_deps) test_mail_index_modseq_SOURCES = test-mail-index-modseq.c test_mail_index_modseq_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_modseq_DEPENDENCIES = $(test_deps) test_mail_index_sync_ext_SOURCES = test-mail-index-sync-ext.c test_mail_index_sync_ext_LDADD = mail-index-sync-ext.lo $(test_libs) test_mail_index_sync_ext_DEPENDENCIES = $(test_deps) test_mail_index_transaction_finish_SOURCES = test-mail-index-transaction-finish.c test_mail_index_transaction_finish_LDADD = mail-index-transaction-finish.lo $(test_libs) test_mail_index_transaction_finish_DEPENDENCIES = $(test_deps) test_mail_index_transaction_update_SOURCES = test-mail-index-transaction-update.c test_mail_index_transaction_update_LDADD = mail-index-transaction-update.lo $(test_libs) test_mail_index_transaction_update_DEPENDENCIES = $(test_deps) test_mail_transaction_log_append_SOURCES = test-mail-transaction-log-append.c test_mail_transaction_log_append_LDADD = mail-transaction-log-append.lo $(test_libs) test_mail_transaction_log_append_DEPENDENCIES = $(test_deps) test_mail_transaction_log_file_SOURCES = test-mail-transaction-log-file.c test_mail_transaction_log_file_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_transaction_log_file_DEPENDENCIES = $(test_deps) test_mail_transaction_log_view_SOURCES = test-mail-transaction-log-view.c test_mail_transaction_log_view_LDADD = mail-transaction-log-view.lo $(test_libs) test_mail_transaction_log_view_DEPENDENCIES = $(test_deps) pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-index/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-index/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libindex.la: $(libindex_la_OBJECTS) $(libindex_la_DEPENDENCIES) $(EXTRA_libindex_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libindex_la_OBJECTS) $(libindex_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-mail-index-map$(EXEEXT): $(test_mail_index_map_OBJECTS) $(test_mail_index_map_DEPENDENCIES) $(EXTRA_test_mail_index_map_DEPENDENCIES) @rm -f test-mail-index-map$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_index_map_OBJECTS) $(test_mail_index_map_LDADD) $(LIBS) test-mail-index-modseq$(EXEEXT): $(test_mail_index_modseq_OBJECTS) $(test_mail_index_modseq_DEPENDENCIES) $(EXTRA_test_mail_index_modseq_DEPENDENCIES) @rm -f test-mail-index-modseq$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_index_modseq_OBJECTS) $(test_mail_index_modseq_LDADD) $(LIBS) test-mail-index-sync-ext$(EXEEXT): $(test_mail_index_sync_ext_OBJECTS) $(test_mail_index_sync_ext_DEPENDENCIES) $(EXTRA_test_mail_index_sync_ext_DEPENDENCIES) @rm -f test-mail-index-sync-ext$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_index_sync_ext_OBJECTS) $(test_mail_index_sync_ext_LDADD) $(LIBS) test-mail-index-transaction-finish$(EXEEXT): $(test_mail_index_transaction_finish_OBJECTS) $(test_mail_index_transaction_finish_DEPENDENCIES) $(EXTRA_test_mail_index_transaction_finish_DEPENDENCIES) @rm -f test-mail-index-transaction-finish$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_index_transaction_finish_OBJECTS) $(test_mail_index_transaction_finish_LDADD) $(LIBS) test-mail-index-transaction-update$(EXEEXT): $(test_mail_index_transaction_update_OBJECTS) $(test_mail_index_transaction_update_DEPENDENCIES) $(EXTRA_test_mail_index_transaction_update_DEPENDENCIES) @rm -f test-mail-index-transaction-update$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_index_transaction_update_OBJECTS) $(test_mail_index_transaction_update_LDADD) $(LIBS) test-mail-transaction-log-append$(EXEEXT): $(test_mail_transaction_log_append_OBJECTS) $(test_mail_transaction_log_append_DEPENDENCIES) $(EXTRA_test_mail_transaction_log_append_DEPENDENCIES) @rm -f test-mail-transaction-log-append$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_transaction_log_append_OBJECTS) $(test_mail_transaction_log_append_LDADD) $(LIBS) test-mail-transaction-log-file$(EXEEXT): $(test_mail_transaction_log_file_OBJECTS) $(test_mail_transaction_log_file_DEPENDENCIES) $(EXTRA_test_mail_transaction_log_file_DEPENDENCIES) @rm -f test-mail-transaction-log-file$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_transaction_log_file_OBJECTS) $(test_mail_transaction_log_file_LDADD) $(LIBS) test-mail-transaction-log-view$(EXEEXT): $(test_mail_transaction_log_view_OBJECTS) $(test_mail_transaction_log_view_DEPENDENCIES) $(EXTRA_test_mail_transaction_log_view_DEPENDENCIES) @rm -f test-mail-transaction-log-view$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_transaction_log_view_OBJECTS) $(test_mail_transaction_log_view_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-compress.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-decisions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-fields.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-lookup.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-sync-update.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache-transaction.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-alloc-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-dummy-view.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-fsck.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-lock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-map-hdr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-map-read.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-map.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-modseq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-strmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-sync-ext.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-sync-keywords.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-sync-update.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction-export.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction-finish.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction-sort-appends.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction-update.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction-view.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-transaction.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-view-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-view.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index-write.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-index.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-transaction-log-append.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-transaction-log-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-transaction-log-view.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-transaction-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-index-map.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-index-modseq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-index-sync-ext.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-index-transaction-finish.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-index-transaction-update.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-transaction-log-append.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-transaction-log-file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-transaction-log-view.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-index/mail-cache-lookup.c0000644000175000017500000004434513165463624016451 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "str.h" #include "mail-cache-private.h" #define CACHE_PREFETCH IO_BLOCK_SIZE int mail_cache_get_record(struct mail_cache *cache, uint32_t offset, const struct mail_cache_record **rec_r) { const struct mail_cache_record *rec; const void *data; int ret; i_assert(offset != 0); if (offset % sizeof(uint32_t) != 0) { /* records are always 32-bit aligned */ mail_cache_set_corrupted(cache, "invalid record offset"); return -1; } /* we don't know yet how large the record is, so just guess */ if (mail_cache_map(cache, offset, sizeof(*rec) + CACHE_PREFETCH, &data) < 0) return -1; if (offset + sizeof(*rec) > cache->mmap_length) { mail_cache_set_corrupted(cache, "record points outside file"); return -1; } rec = data; if (rec->size < sizeof(*rec)) { mail_cache_set_corrupted(cache, "invalid record size"); return -1; } if (rec->size > CACHE_PREFETCH) { /* larger than we guessed. map the rest of the record. */ if ((ret = mail_cache_map(cache, offset, rec->size, &data)) < 0) return -1; if (ret == 0) { mail_cache_set_corrupted(cache, "record points outside file"); return -1; } rec = data; } *rec_r = rec; return 0; } uint32_t mail_cache_lookup_cur_offset(struct mail_index_view *view, uint32_t seq, uint32_t *reset_id_r) { struct mail_cache *cache = mail_index_view_get_index(view)->cache; struct mail_index_map *map; const void *data; uint32_t offset; mail_index_lookup_ext_full(view, seq, cache->ext_id, &map, &data, NULL); if (data == NULL) { /* no cache offsets */ return 0; } offset = *((const uint32_t *)data); if (offset == 0) return 0; if (!mail_index_ext_get_reset_id(view, map, cache->ext_id, reset_id_r)) i_unreached(); return offset; } static int mail_cache_lookup_offset(struct mail_cache *cache, struct mail_index_view *view, uint32_t seq, uint32_t *offset_r) { uint32_t offset, reset_id; int i, ret; offset = mail_cache_lookup_cur_offset(view, seq, &reset_id); if (offset == 0) return 0; /* reset_id must match file_seq or the offset is for a different cache file. if this happens, try if reopening the cache helps. if not, it was probably for an old cache file that's already lost by now. */ i = 0; while (cache->hdr->file_seq != reset_id) { if (++i == 2 || reset_id < cache->hdr->file_seq) return 0; if (cache->locked) { /* we're probably compressing */ return 0; } if ((ret = mail_cache_reopen(cache)) <= 0) { /* error / we already have the latest file open */ return ret; } } *offset_r = offset; return 1; } bool mail_cache_track_loops(struct mail_cache_loop_track *loop_track, uoff_t offset, uoff_t size) { i_assert(offset != 0); i_assert(size != 0); /* looping happens only in rare error conditions, so it's enough if we just catch it eventually. we do this by checking if we've seen more record data than possible in the accessed file area. */ if (loop_track->size_sum == 0) { /* first call */ loop_track->min_offset = offset; loop_track->max_offset = offset + size; } else { if (loop_track->min_offset > offset) loop_track->min_offset = offset; if (loop_track->max_offset < offset + size) loop_track->max_offset = offset + size; } loop_track->size_sum += size; return loop_track->size_sum > (loop_track->max_offset - loop_track->min_offset); } void mail_cache_lookup_iter_init(struct mail_cache_view *view, uint32_t seq, struct mail_cache_lookup_iterate_ctx *ctx_r) { struct mail_cache_lookup_iterate_ctx *ctx = ctx_r; int ret; if (!view->cache->opened) (void)mail_cache_open_and_verify(view->cache); i_zero(ctx); ctx->view = view; ctx->seq = seq; if (!MAIL_CACHE_IS_UNUSABLE(view->cache)) { /* look up the first offset */ ret = mail_cache_lookup_offset(view->cache, view->view, seq, &ctx->offset); if (ret <= 0) { ctx->stop = TRUE; ctx->failed = ret < 0; } } ctx->remap_counter = view->cache->remap_counter; i_zero(&view->loop_track); } static bool mail_cache_lookup_iter_transaction(struct mail_cache_lookup_iterate_ctx *ctx) { ctx->rec = mail_cache_transaction_lookup_rec(ctx->view->transaction, ctx->seq, &ctx->trans_next_idx); if (ctx->rec == NULL) return FALSE; ctx->remap_counter = ctx->view->cache->remap_counter; ctx->pos = sizeof(*ctx->rec); ctx->rec_size = ctx->rec->size; return TRUE; } static int mail_cache_lookup_iter_next_record(struct mail_cache_lookup_iterate_ctx *ctx) { struct mail_cache_view *view = ctx->view; if (ctx->failed) return -1; if (ctx->rec != NULL) ctx->offset = ctx->rec->prev_offset; if (ctx->offset == 0) { /* end of this record list. check newly appended data. */ if (view->trans_seq1 > ctx->seq || view->trans_seq2 < ctx->seq) return 0; /* check data still in memory. this works for recent mails even with INDEX=MEMORY */ if (!ctx->memory_appends_checked) { if (mail_cache_lookup_iter_transaction(ctx)) return 1; ctx->memory_appends_checked = TRUE; } if (MAIL_CACHE_IS_UNUSABLE(view->cache) || ctx->stop) return 0; /* check data already written to cache file */ if (ctx->disk_appends_checked || mail_cache_lookup_offset(view->cache, view->trans_view, ctx->seq, &ctx->offset) <= 0) return 0; ctx->disk_appends_checked = TRUE; ctx->remap_counter = view->cache->remap_counter; i_zero(&view->loop_track); } if (ctx->stop) return 0; /* look up the next record */ if (mail_cache_get_record(view->cache, ctx->offset, &ctx->rec) < 0) return -1; if (mail_cache_track_loops(&view->loop_track, ctx->offset, ctx->rec->size)) { mail_cache_set_corrupted(view->cache, "record list is circular"); return -1; } ctx->remap_counter = view->cache->remap_counter; ctx->pos = sizeof(*ctx->rec); ctx->rec_size = ctx->rec->size; return 1; } int mail_cache_lookup_iter_next(struct mail_cache_lookup_iterate_ctx *ctx, struct mail_cache_iterate_field *field_r) { struct mail_cache *cache = ctx->view->cache; unsigned int field_idx; unsigned int data_size; uint32_t file_field; int ret; i_assert(ctx->remap_counter == cache->remap_counter); if (ctx->pos + sizeof(uint32_t) > ctx->rec_size) { if (ctx->pos != ctx->rec_size) { mail_cache_set_corrupted(cache, "record has invalid size"); return -1; } if ((ret = mail_cache_lookup_iter_next_record(ctx)) <= 0) return ret; } /* return the next field */ file_field = *((const uint32_t *)CONST_PTR_OFFSET(ctx->rec, ctx->pos)); ctx->pos += sizeof(uint32_t); if (file_field >= cache->file_fields_count) { /* new field, have to re-read fields header to figure out its size. don't do this if we're compressing. */ if (!cache->locked) { if (mail_cache_header_fields_read(cache) < 0) return -1; } if (file_field >= cache->file_fields_count) { mail_cache_set_corrupted(cache, "field index too large (%u >= %u)", file_field, cache->file_fields_count); return -1; } /* field reading might have re-mmaped the file and caused rec pointer to break. need to get it again. */ if (mail_cache_get_record(cache, ctx->offset, &ctx->rec) < 0) return -1; ctx->remap_counter = cache->remap_counter; } field_idx = cache->file_field_map[file_field]; data_size = cache->fields[field_idx].field.field_size; if (data_size == UINT_MAX && ctx->pos + sizeof(uint32_t) <= ctx->rec->size) { /* variable size field. get its size from the file. */ data_size = *((const uint32_t *) CONST_PTR_OFFSET(ctx->rec, ctx->pos)); ctx->pos += sizeof(uint32_t); } if (ctx->rec->size - ctx->pos < data_size) { mail_cache_set_corrupted(cache, "record continues outside its allocated size"); return -1; } field_r->field_idx = field_idx; field_r->data = CONST_PTR_OFFSET(ctx->rec, ctx->pos); field_r->size = data_size; field_r->offset = ctx->offset + ctx->pos; /* each record begins from 32bit aligned position */ ctx->pos += (data_size + sizeof(uint32_t)-1) & ~(sizeof(uint32_t)-1); return 1; } static int mail_cache_seq(struct mail_cache_view *view, uint32_t seq) { struct mail_cache_lookup_iterate_ctx iter; struct mail_cache_iterate_field field; int ret; if (++view->cached_exists_value == 0) { /* wrapped, we'll have to clear the buffer */ buffer_reset(view->cached_exists_buf); view->cached_exists_value++; } view->cached_exists_seq = seq; mail_cache_lookup_iter_init(view, seq, &iter); while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) { buffer_write(view->cached_exists_buf, field.field_idx, &view->cached_exists_value, 1); } return ret; } static bool mail_cache_file_has_field(struct mail_cache *cache, unsigned int field) { i_assert(field < cache->fields_count); return cache->field_file_map[field] != (uint32_t)-1; } int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq, unsigned int field) { const uint8_t *data; i_assert(seq > 0); if (!view->cache->opened) (void)mail_cache_open_and_verify(view->cache); if (!mail_cache_file_has_field(view->cache, field)) return 0; /* FIXME: we should discard the cache if view has been synced */ if (view->cached_exists_seq != seq) { if (mail_cache_seq(view, seq) < 0) return -1; } data = view->cached_exists_buf->data; return (field < view->cached_exists_buf->used && data[field] == view->cached_exists_value) ? 1 : 0; } bool mail_cache_field_exists_any(struct mail_cache_view *view, uint32_t seq) { uint32_t reset_id; return mail_cache_lookup_cur_offset(view->view, seq, &reset_id) != 0; } enum mail_cache_decision_type mail_cache_field_get_decision(struct mail_cache *cache, unsigned int field_idx) { i_assert(field_idx < cache->fields_count); return cache->fields[field_idx].field.decision; } static int mail_cache_lookup_bitmask(struct mail_cache_lookup_iterate_ctx *iter, unsigned int field_idx, unsigned int field_size, buffer_t *dest_buf) { struct mail_cache_iterate_field field; const unsigned char *src; unsigned char *dest; unsigned int i; bool found = FALSE; int ret; /* make sure all bits are cleared first */ buffer_write_zero(dest_buf, 0, field_size); while ((ret = mail_cache_lookup_iter_next(iter, &field)) > 0) { if (field.field_idx != field_idx) continue; /* merge all bits */ src = field.data; dest = buffer_get_space_unsafe(dest_buf, 0, field.size); for (i = 0; i < field.size; i++) dest[i] |= src[i]; found = TRUE; } return ret < 0 ? -1 : (found ? 1 : 0); } int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf, uint32_t seq, unsigned int field_idx) { const struct mail_cache_field *field_def; struct mail_cache_lookup_iterate_ctx iter; struct mail_cache_iterate_field field; int ret; ret = mail_cache_field_exists(view, seq, field_idx); mail_cache_decision_state_update(view, seq, field_idx); if (ret <= 0) return ret; /* the field should exist */ mail_cache_lookup_iter_init(view, seq, &iter); field_def = &view->cache->fields[field_idx].field; if (field_def->type == MAIL_CACHE_FIELD_BITMASK) { return mail_cache_lookup_bitmask(&iter, field_idx, field_def->field_size, dest_buf); } /* return the first one that's found. if there are multiple they're all identical. */ while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) { if (field.field_idx == field_idx) { buffer_append(dest_buf, field.data, field.size); break; } } return ret; } struct header_lookup_data { uint32_t data_size; const unsigned char *data; }; struct header_lookup_line { uint32_t line_num; struct header_lookup_data *data; }; struct header_lookup_context { struct mail_cache_view *view; pool_t pool; ARRAY(struct header_lookup_line) lines; }; enum { HDR_FIELD_STATE_DONTWANT = 0, HDR_FIELD_STATE_WANT, HDR_FIELD_STATE_SEEN }; static void header_lines_save(struct header_lookup_context *ctx, const struct mail_cache_iterate_field *field) { const uint32_t *lines = field->data; uint32_t data_size = field->size; struct header_lookup_line hdr_line; struct header_lookup_data *hdr_data; void *data_dup; unsigned int i, lines_count, pos; /* data = { line_nums[], 0, "headers" } */ for (i = 0; data_size >= sizeof(uint32_t); i++) { data_size -= sizeof(uint32_t); if (lines[i] == 0) break; } lines_count = i; pos = (lines_count+1) * sizeof(uint32_t); hdr_data = p_new(ctx->pool, struct header_lookup_data, 1); hdr_data->data_size = data_size; if (data_size > 0) { hdr_data->data = data_dup = p_malloc(ctx->pool, data_size); memcpy(data_dup, CONST_PTR_OFFSET(field->data, pos), data_size); } for (i = 0; i < lines_count; i++) { hdr_line.line_num = lines[i]; hdr_line.data = hdr_data; array_append(&ctx->lines, &hdr_line, 1); } } static int header_lookup_line_cmp(const struct header_lookup_line *l1, const struct header_lookup_line *l2) { return (int)l1->line_num - (int)l2->line_num; } static int mail_cache_lookup_headers_real(struct mail_cache_view *view, string_t *dest, uint32_t seq, unsigned int field_idxs[], unsigned int fields_count, pool_t *pool_r) { struct mail_cache *cache = view->cache; struct mail_cache_lookup_iterate_ctx iter; struct mail_cache_iterate_field field; struct header_lookup_context ctx; struct header_lookup_line *lines; const unsigned char *p, *start, *end; uint8_t *field_state; unsigned int i, count, max_field = 0; size_t hdr_size; uint8_t want = HDR_FIELD_STATE_WANT; buffer_t *buf; int ret; *pool_r = NULL; if (fields_count == 0) return 1; if (!view->cache->opened) (void)mail_cache_open_and_verify(view->cache); /* update the decision state regardless of whether the fields actually exist or not. */ for (i = 0; i < fields_count; i++) mail_cache_decision_state_update(view, seq, field_idxs[i]); /* mark all the fields we want to find. */ buf = buffer_create_dynamic(pool_datastack_create(), 32); for (i = 0; i < fields_count; i++) { if (!mail_cache_file_has_field(cache, field_idxs[i])) return 0; if (field_idxs[i] > max_field) max_field = field_idxs[i]; buffer_write(buf, field_idxs[i], &want, 1); } field_state = buffer_get_modifiable_data(buf, NULL); /* lookup the fields */ i_zero(&ctx); ctx.view = view; ctx.pool = *pool_r = pool_alloconly_create(MEMPOOL_GROWING"mail cache headers", 1024); t_array_init(&ctx.lines, 32); mail_cache_lookup_iter_init(view, seq, &iter); while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) { if (field.field_idx > max_field || field_state[field.field_idx] != HDR_FIELD_STATE_WANT) { /* a) don't want it, b) duplicate */ } else { field_state[field.field_idx] = HDR_FIELD_STATE_SEEN; header_lines_save(&ctx, &field); } } if (ret < 0) return -1; /* check that all fields were found */ for (i = 0; i <= max_field; i++) { if (field_state[i] == HDR_FIELD_STATE_WANT) return 0; } /* we need to return headers in the order they existed originally. we can do this by sorting the messages by their line numbers. */ array_sort(&ctx.lines, header_lookup_line_cmp); lines = array_get_modifiable(&ctx.lines, &count); /* then start filling dest buffer from the headers */ for (i = 0; i < count; i++) { start = lines[i].data->data; end = start + lines[i].data->data_size; /* find the end of the (multiline) header */ for (p = start; p != end; p++) { if (*p == '\n' && (p+1 == end || (p[1] != ' ' && p[1] != '\t'))) { p++; break; } } hdr_size = (size_t)(p - start); buffer_append(dest, start, hdr_size); /* if there are more lines for this header, the following lines continue after this one. so skip this line. */ lines[i].data->data += hdr_size; lines[i].data->data_size -= hdr_size; } return 1; } int mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest, uint32_t seq, unsigned int field_idxs[], unsigned int fields_count) { pool_t pool; int ret; T_BEGIN { ret = mail_cache_lookup_headers_real(view, dest, seq, field_idxs, fields_count, &pool); if (pool != NULL) pool_unref(&pool); } T_END; return ret; } static uint32_t mail_cache_get_highest_seq_with_cache(struct mail_cache_view *view, uint32_t below_seq, uint32_t *reset_id_r) { struct mail_cache_missing_reason_cache *rc = &view->reason_cache; uint32_t seq = below_seq-1, highest_checked_seq = 0; /* find the newest mail that has anything in cache */ if (rc->log_file_head_offset == view->view->log_file_head_offset && rc->log_file_head_seq == view->view->log_file_head_seq) { /* reason_cache matches the current view - we can use it */ highest_checked_seq = rc->highest_checked_seq; } else { rc->log_file_head_offset = view->view->log_file_head_offset; rc->log_file_head_seq = view->view->log_file_head_seq; } rc->highest_checked_seq = below_seq; /* first check anything not already in reason_cache */ for (; seq > highest_checked_seq; seq--) { if (mail_cache_lookup_cur_offset(view->view, seq, reset_id_r) != 0) { rc->highest_seq_with_cache = seq; rc->reset_id = *reset_id_r; return seq; } } if (seq == 0) return 0; /* then return the result from cache */ *reset_id_r = rc->reset_id; return rc->highest_seq_with_cache; } const char * mail_cache_get_missing_reason(struct mail_cache_view *view, uint32_t seq) { uint32_t offset, reset_id; if (MAIL_CACHE_IS_UNUSABLE(view->cache)) return "Cache file is unusable"; offset = mail_cache_lookup_cur_offset(view->view, seq, &reset_id); if (offset != 0) { if (view->cache->hdr->file_seq != reset_id) { return t_strdup_printf( "Index reset_id=%u doesn't match cache reset_id=%u", reset_id, view->cache->hdr->file_seq); } return t_strdup_printf( "Mail has other cached fields, reset_id=%u", reset_id); } seq = mail_cache_get_highest_seq_with_cache(view, seq, &reset_id); if (seq == 0) { return t_strdup_printf("Cache file is empty, reset_id=%u", view->cache->hdr->file_seq); } uint32_t uid; mail_index_lookup_uid(view->view, seq, &uid); if (view->cache->hdr->file_seq != reset_id) { return t_strdup_printf( "Mail not cached, highest cached seq=%u uid=%u: " "Index reset_id=%u doesn't match cache reset_id=%u", seq, uid, reset_id, view->cache->hdr->file_seq); } return t_strdup_printf( "Mail not cached, highest cached seq=%u uid=%u: reset_id=%u", seq, uid, reset_id); } dovecot-2.2.33.2/src/lib-index/mail-index-map-hdr.c0000644000175000017500000002402013144653606016516 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-private.h" int mail_index_map_parse_extensions(struct mail_index_map *map) { struct mail_index *index = map->index; const struct mail_index_ext_header *ext_hdr; unsigned int i, old_count, offset; const char *name, *error; uint32_t ext_id, ext_map_idx, ext_offset; /* extension headers always start from 64bit offsets, so if base header doesn't happen to be 64bit aligned we'll skip some bytes */ offset = MAIL_INDEX_HEADER_SIZE_ALIGN(map->hdr.base_header_size); if (offset >= map->hdr.header_size && map->extension_pool == NULL) { /* nothing to do, skip allocatations and all */ return 0; } old_count = array_count(&index->extensions); mail_index_map_init_extbufs(map, old_count + 5); ext_id = (uint32_t)-1; for (i = 0; i < old_count; i++) array_append(&map->ext_id_map, &ext_id, 1); for (i = 0; offset < map->hdr.header_size; i++) { ext_offset = offset; if (mail_index_map_ext_get_next(map, &offset, &ext_hdr, &name) < 0) { mail_index_set_error(index, "Corrupted index file %s: " "Header extension #%d (%s) goes outside header", index->filepath, i, name); return -1; } if (mail_index_map_ext_hdr_check(&map->hdr, ext_hdr, name, &error) < 0) { mail_index_set_error(index, "Corrupted index file %s: " "Broken extension #%d (%s): %s", index->filepath, i, name, error); return -1; } if (mail_index_map_lookup_ext(map, name, &ext_map_idx)) { mail_index_set_error(index, "Corrupted index file %s: " "Duplicate header extension %s", index->filepath, name); return -1; } (void)mail_index_map_register_ext(map, name, ext_offset, ext_hdr); } return 0; } int mail_index_map_parse_keywords(struct mail_index_map *map) { struct mail_index *index = map->index; const struct mail_index_ext *ext; const struct mail_index_keyword_header *kw_hdr; const struct mail_index_keyword_header_rec *kw_rec; const char *name; unsigned int i, name_area_end_offset, old_count; uint32_t idx; if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &idx)) { if (array_is_created(&map->keyword_idx_map)) array_clear(&map->keyword_idx_map); return 0; } ext = array_idx(&map->extensions, idx); /* Extension header contains: - struct mail_index_keyword_header - struct mail_index_keyword_header_rec * keywords_count - const char names[] * keywords_count */ i_assert(ext->hdr_offset < map->hdr.header_size); kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); kw_rec = (const void *)(kw_hdr + 1); name = (const char *)(kw_rec + kw_hdr->keywords_count); old_count = !array_is_created(&map->keyword_idx_map) ? 0 : array_count(&map->keyword_idx_map); /* Keywords can only be added into same mapping. Removing requires a new mapping (recreating the index file) */ if (kw_hdr->keywords_count == old_count) { /* nothing changed */ return 0; } /* make sure the header is valid */ if (kw_hdr->keywords_count < old_count) { mail_index_set_error(index, "Corrupted index file %s: " "Keywords removed unexpectedly", index->filepath); return -1; } if ((size_t)(name - (const char *)kw_hdr) > ext->hdr_size) { mail_index_set_error(index, "Corrupted index file %s: " "keywords_count larger than header size", index->filepath); return -1; } name_area_end_offset = (const char *)kw_hdr + ext->hdr_size - name; for (i = 0; i < kw_hdr->keywords_count; i++) { if (kw_rec[i].name_offset > name_area_end_offset) { mail_index_set_error(index, "Corrupted index file %s: " "name_offset points outside allocated header", index->filepath); return -1; } } if (name[name_area_end_offset-1] != '\0') { mail_index_set_error(index, "Corrupted index file %s: " "Keyword header doesn't end with NUL", index->filepath); return -1; } /* create file -> index mapping */ if (!array_is_created(&map->keyword_idx_map)) i_array_init(&map->keyword_idx_map, kw_hdr->keywords_count); #ifdef DEBUG /* Check that existing headers are still the same. It's behind DEBUG since it's pretty useless waste of CPU normally. */ for (i = 0; i < array_count(&map->keyword_idx_map); i++) { const char *keyword = name + kw_rec[i].name_offset; const unsigned int *old_idx; unsigned int kw_idx; old_idx = array_idx(&map->keyword_idx_map, i); if (!mail_index_keyword_lookup(index, keyword, &kw_idx) || kw_idx != *old_idx) { mail_index_set_error(index, "Corrupted index file %s: " "Keywords changed unexpectedly", index->filepath); return -1; } } #endif /* Register the newly seen keywords */ i = array_count(&map->keyword_idx_map); for (; i < kw_hdr->keywords_count; i++) { const char *keyword = name + kw_rec[i].name_offset; unsigned int kw_idx; if (*keyword == '\0') { mail_index_set_error(index, "Corrupted index file %s: " "Empty keyword name in header", index->filepath); return -1; } mail_index_keyword_lookup_or_create(index, keyword, &kw_idx); array_append(&map->keyword_idx_map, &kw_idx, 1); } return 0; } bool mail_index_check_header_compat(struct mail_index *index, const struct mail_index_header *hdr, uoff_t file_size, const char **error_r) { enum mail_index_header_compat_flags compat_flags = 0; #if !WORDS_BIGENDIAN compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #endif if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) { /* major version change */ *error_r = t_strdup_printf("Major version changed (%u != %u)", hdr->major_version, MAIL_INDEX_MAJOR_VERSION); return FALSE; } if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) { /* we've already complained about it */ *error_r = "Header's corrupted flag is set"; return FALSE; } if (hdr->compat_flags != compat_flags) { /* architecture change */ *error_r = "CPU architecture changed"; return FALSE; } if (hdr->base_header_size < MAIL_INDEX_HEADER_MIN_SIZE || hdr->header_size < hdr->base_header_size) { *error_r = t_strdup_printf( "Corrupted header sizes (base %u, full %u)", hdr->base_header_size, hdr->header_size); return FALSE; } if (hdr->header_size > file_size) { *error_r = t_strdup_printf( "Header size is larger than file (%u > %"PRIuUOFF_T")", hdr->header_size, file_size); return FALSE; } if (hdr->indexid != index->indexid) { if (index->indexid != 0) { mail_index_set_error(index, "Index file %s: " "indexid changed: %u -> %u", index->filepath, index->indexid, hdr->indexid); } index->indexid = hdr->indexid; mail_transaction_log_indexid_changed(index->log); } return TRUE; } static void mail_index_map_clear_recent_flags(struct mail_index_map *map) { struct mail_index_record *rec; uint32_t seq; for (seq = 1; seq <= map->hdr.messages_count; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(map, seq); rec->flags &= ~MAIL_RECENT; } } int mail_index_map_check_header(struct mail_index_map *map, const char **error_r) { struct mail_index *index = map->index; const struct mail_index_header *hdr = &map->hdr; if (!mail_index_check_header_compat(index, hdr, (uoff_t)-1, error_r)) return 0; /* following some extra checks that only take a bit of CPU */ if (hdr->record_size < sizeof(struct mail_index_record)) { *error_r = t_strdup_printf( "record_size too small (%u < %"PRIuSIZE_T")", hdr->record_size, sizeof(struct mail_index_record)); return -1; } if (hdr->uid_validity == 0 && hdr->next_uid != 1) { *error_r = t_strdup_printf( "uidvalidity=0, but next_uid=%u", hdr->next_uid); return 0; } if (hdr->next_uid == 0) { *error_r = "next_uid=0"; return 0; } if (hdr->messages_count > map->rec_map->records_count) { *error_r = t_strdup_printf( "messages_count is higher in header than record map (%u > %u)", hdr->messages_count, map->rec_map->records_count); return 0; } if (hdr->seen_messages_count > hdr->messages_count) { *error_r = t_strdup_printf( "seen_messages_count %u > messages_count %u", hdr->seen_messages_count, hdr->messages_count); return 0; } if (hdr->deleted_messages_count > hdr->messages_count) { *error_r = t_strdup_printf( "deleted_messages_count %u > messages_count %u", hdr->deleted_messages_count, hdr->messages_count); return 0; } switch (hdr->minor_version) { case 0: /* upgrade silently from v1.0 */ map->hdr.unused_old_recent_messages_count = 0; if (hdr->first_recent_uid == 0) map->hdr.first_recent_uid = 1; index->need_recreate = TRUE; /* fall through */ case 1: /* pre-v1.1.rc6: make sure the \Recent flags are gone */ mail_index_map_clear_recent_flags(map); map->hdr.minor_version = MAIL_INDEX_MINOR_VERSION; /* fall through */ case 2: /* pre-v2.2 (although should have been done in v2.1 already): make sure the old unused fields are cleared */ map->hdr.unused_old_sync_size_part1 = 0; map->hdr.log2_rotate_time = 0; map->hdr.last_temp_file_scan = 0; } if (hdr->first_recent_uid == 0) { *error_r = "first_recent_uid=0"; return 0; } if (hdr->first_recent_uid > hdr->next_uid) { *error_r = t_strdup_printf( "first_recent_uid %u > next_uid %u", hdr->first_recent_uid, hdr->next_uid); return 0; } if (hdr->first_unseen_uid_lowwater > hdr->next_uid) { *error_r = t_strdup_printf( "first_unseen_uid_lowwater %u > next_uid %u", hdr->first_unseen_uid_lowwater, hdr->next_uid); return 0; } if (hdr->first_deleted_uid_lowwater > hdr->next_uid) { *error_r = t_strdup_printf( "first_deleted_uid_lowwater %u > next_uid %u", hdr->first_deleted_uid_lowwater, hdr->next_uid); return 0; } if (hdr->messages_count > 0) { /* last message's UID must be smaller than next_uid. also make sure it's not zero. */ const struct mail_index_record *rec; rec = MAIL_INDEX_REC_AT_SEQ(map, hdr->messages_count); if (rec->uid == 0) { *error_r = "last message has uid=0"; return -1; } if (rec->uid >= hdr->next_uid) { *error_r = t_strdup_printf( "last message uid %u >= next_uid %u", rec->uid, hdr->next_uid); return 0; } } return 1; } dovecot-2.2.33.2/src/lib-index/mail-index-sync-update.c0000644000175000017500000007574313165463624017446 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "mmap-util.h" #include "mail-index-modseq.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-transaction-log.h" #include "mail-transaction-log-private.h" /* If we have less than this many bytes to sync from log file, don't bother reading the main index */ #define MAIL_INDEX_SYNC_MIN_READ_INDEX_SIZE 2048 static void mail_index_sync_update_log_offset(struct mail_index_sync_map_ctx *ctx, struct mail_index_map *map, bool eol) { uint32_t prev_seq; uoff_t prev_offset; mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &prev_seq, &prev_offset); if (prev_seq == 0) { /* handling lost changes in view syncing */ return; } if (!eol) { if (prev_offset == ctx->ext_intro_end_offset && prev_seq == ctx->ext_intro_seq) { /* previous transaction was an extension introduction. we probably came here from mail_index_sync_ext_reset(). if there are any more views which want to continue syncing it needs the intro. so back up a bit more. don't do this in case the last transaction in the log is the extension intro, so we don't keep trying to sync it over and over again. */ prev_offset = ctx->ext_intro_offset; } map->hdr.log_file_seq = prev_seq; } else { i_assert(ctx->view->index->log->head->hdr.file_seq == prev_seq); if (map->hdr.log_file_seq != prev_seq) { map->hdr.log_file_seq = prev_seq; map->hdr.log_file_tail_offset = 0; } } map->hdr.log_file_head_offset = prev_offset; } static void mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx, struct mail_index_map *map) { struct mail_index_view *view = ctx->view; i_assert(view->map != map); mail_index_sync_update_log_offset(ctx, view->map, FALSE); mail_index_unmap(&view->map); view->map = map; if (ctx->type != MAIL_INDEX_SYNC_HANDLER_VIEW) view->index->map = map; mail_index_modseq_sync_map_replaced(ctx->modseq_ctx); } static struct mail_index_map * mail_index_sync_move_to_private_memory(struct mail_index_sync_map_ctx *ctx) { struct mail_index_map *map = ctx->view->map; if (map->refcount > 1) { map = mail_index_map_clone(map); mail_index_sync_replace_map(ctx, map); } if (!MAIL_INDEX_MAP_IS_IN_MEMORY(ctx->view->map)) mail_index_map_move_to_memory(ctx->view->map); mail_index_modseq_sync_map_replaced(ctx->modseq_ctx); return map; } struct mail_index_map * mail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx) { (void)mail_index_sync_move_to_private_memory(ctx); mail_index_record_map_move_to_private(ctx->view->map); mail_index_modseq_sync_map_replaced(ctx->modseq_ctx); return ctx->view->map; } static int mail_index_header_update_counts(struct mail_index_header *hdr, uint8_t old_flags, uint8_t new_flags, const char **error_r) { if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) { /* different seen-flag */ if ((old_flags & MAIL_SEEN) != 0) { if (hdr->seen_messages_count == 0) { *error_r = "Seen counter wrong"; return -1; } hdr->seen_messages_count--; } else { if (hdr->seen_messages_count >= hdr->messages_count) { *error_r = "Seen counter wrong"; return -1; } if (++hdr->seen_messages_count == hdr->messages_count) hdr->first_unseen_uid_lowwater = hdr->next_uid; } } if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) { /* different deleted-flag */ if ((old_flags & MAIL_DELETED) == 0) { hdr->deleted_messages_count++; if (hdr->deleted_messages_count > hdr->messages_count) { *error_r = "Deleted counter wrong"; return -1; } } else { if (hdr->deleted_messages_count == 0 || hdr->deleted_messages_count > hdr->messages_count) { *error_r = "Deleted counter wrong"; return -1; } if (--hdr->deleted_messages_count == 0) hdr->first_deleted_uid_lowwater = hdr->next_uid; } } return 0; } static void mail_index_sync_header_update_counts_all(struct mail_index_sync_map_ctx *ctx, uint32_t uid, uint8_t old_flags, uint8_t new_flags) { struct mail_index_map *const *maps; const char *error; unsigned int i, count; maps = array_get(&ctx->view->map->rec_map->maps, &count); for (i = 0; i < count; i++) { if (uid >= maps[i]->hdr.next_uid) continue; if (mail_index_header_update_counts(&maps[i]->hdr, old_flags, new_flags, &error) < 0) mail_index_sync_set_corrupted(ctx, "%s", error); } } static void mail_index_sync_header_update_counts(struct mail_index_sync_map_ctx *ctx, uint32_t uid, uint8_t old_flags, uint8_t new_flags) { const char *error; if (uid >= ctx->view->map->hdr.next_uid) { mail_index_sync_set_corrupted(ctx, "uid %u >= next_uid %u", uid, ctx->view->map->hdr.next_uid); } else { if (mail_index_header_update_counts(&ctx->view->map->hdr, old_flags, new_flags, &error) < 0) mail_index_sync_set_corrupted(ctx, "%s", error); } } static void mail_index_header_update_lowwaters(struct mail_index_sync_map_ctx *ctx, uint32_t uid, enum mail_flags flags) { struct mail_index_map *const *maps; unsigned int i, count; maps = array_get(&ctx->view->map->rec_map->maps, &count); for (i = 0; i < count; i++) { if ((flags & MAIL_SEEN) == 0 && uid < maps[i]->hdr.first_unseen_uid_lowwater) maps[i]->hdr.first_unseen_uid_lowwater = uid; if ((flags & MAIL_DELETED) != 0 && uid < maps[i]->hdr.first_deleted_uid_lowwater) maps[i]->hdr.first_deleted_uid_lowwater = uid; } } static void sync_expunge_call_handlers(struct mail_index_sync_map_ctx *ctx, uint32_t seq1, uint32_t seq2) { const struct mail_index_expunge_handler *eh; struct mail_index_record *rec; uint32_t seq; array_foreach(&ctx->expunge_handlers, eh) { for (seq = seq1; seq <= seq2; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(ctx->view->map, seq); /* FIXME: does expunge handler's return value matter? we probably shouldn't disallow expunges if the handler returns failure.. should it be just changed to return void? */ (void)eh->handler(ctx, seq, PTR_OFFSET(rec, eh->record_offset), eh->sync_context, eh->context); } } } static bool sync_expunge_handlers_init(struct mail_index_sync_map_ctx *ctx) { /* call expunge handlers only when syncing index file */ if (ctx->type != MAIL_INDEX_SYNC_HANDLER_FILE) return FALSE; if (!ctx->expunge_handlers_set) mail_index_sync_init_expunge_handlers(ctx); if (!array_is_created(&ctx->expunge_handlers)) return FALSE; return TRUE; } static void sync_expunge_range(struct mail_index_sync_map_ctx *ctx, const ARRAY_TYPE(seq_range) *seqs) { struct mail_index_map *map; const struct seq_range *range; unsigned int i, count; uint32_t dest_seq1, prev_seq2, orig_rec_count; range = array_get(seqs, &count); if (count == 0) return; map = mail_index_sync_get_atomic_map(ctx); /* call the expunge handlers first */ if (sync_expunge_handlers_init(ctx)) { for (i = 0; i < count; i++) { sync_expunge_call_handlers(ctx, range[i].seq1, range[i].seq2); } } prev_seq2 = 0; dest_seq1 = 1; orig_rec_count = map->rec_map->records_count; for (i = 0; i < count; i++) { uint32_t seq1 = range[i].seq1; uint32_t seq2 = range[i].seq2; struct mail_index_record *rec; uint32_t seq_count, seq; i_assert(seq1 > prev_seq2); for (seq = seq1; seq <= seq2; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(map, seq); mail_index_sync_header_update_counts(ctx, rec->uid, rec->flags, 0); } if (prev_seq2+1 <= seq1-1) { /* @UNSAFE: move (prev_seq2+1) .. (seq1-1) to its final location in the map if necessary */ uint32_t move_count = (seq1-1) - (prev_seq2+1) + 1; if (prev_seq2+1-1 != dest_seq1-1) memmove(MAIL_INDEX_REC_AT_SEQ(map, dest_seq1), MAIL_INDEX_REC_AT_SEQ(map, prev_seq2+1), move_count * map->hdr.record_size); dest_seq1 += move_count; } seq_count = seq2 - seq1 + 1; map->rec_map->records_count -= seq_count; map->hdr.messages_count -= seq_count; mail_index_modseq_expunge(ctx->modseq_ctx, seq1, seq2); prev_seq2 = seq2; } /* Final stragglers */ if (orig_rec_count > prev_seq2) { uint32_t final_move_count = orig_rec_count - prev_seq2; memmove(MAIL_INDEX_REC_AT_SEQ(map, dest_seq1), MAIL_INDEX_REC_AT_SEQ(map, prev_seq2+1), final_move_count * map->hdr.record_size); } } static void *sync_append_record(struct mail_index_map *map) { size_t append_pos; void *ret; append_pos = map->rec_map->records_count * map->hdr.record_size; ret = buffer_get_space_unsafe(map->rec_map->buffer, append_pos, map->hdr.record_size); map->rec_map->records = buffer_get_modifiable_data(map->rec_map->buffer, NULL); return ret; } static bool sync_update_ignored_change(struct mail_index_sync_map_ctx *ctx) { struct mail_index_transaction_commit_result *result = ctx->view->index->sync_commit_result; uint32_t prev_log_seq; uoff_t prev_log_offset, trans_start_offset, trans_end_offset; if (result == NULL) return FALSE; /* we'll return TRUE if this modseq change was written within the transaction that was just committed */ mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &prev_log_seq, &prev_log_offset); if (prev_log_seq != result->log_file_seq) return FALSE; trans_end_offset = result->log_file_offset; trans_start_offset = trans_end_offset - result->commit_size; if (prev_log_offset < trans_start_offset || prev_log_offset >= trans_end_offset) return FALSE; return TRUE; } static int sync_modseq_update(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_modseq_update *u, unsigned int size) { struct mail_index_view *view = ctx->view; const struct mail_transaction_modseq_update *end; uint32_t seq; uint64_t min_modseq; int ret; end = CONST_PTR_OFFSET(u, size); for (; u < end; u++) { if (u->uid == 0) seq = 0; else if (!mail_index_lookup_seq(view, u->uid, &seq)) continue; min_modseq = ((uint64_t)u->modseq_high32 << 32) | u->modseq_low32; ret = seq == 0 ? 1 : mail_index_modseq_set(view, seq, min_modseq); if (ret < 0) { mail_index_sync_set_corrupted(ctx, "modseqs updated before they were enabled"); return -1; } if (ret == 0 && sync_update_ignored_change(ctx)) view->index->sync_commit_result->ignored_modseq_changes++; } return 1; } static int sync_append(const struct mail_index_record *rec, struct mail_index_sync_map_ctx *ctx) { struct mail_index_view *view = ctx->view; struct mail_index_map *map = view->map; const struct mail_index_record *old_rec; enum mail_flags new_flags; void *dest; if (rec->uid < map->hdr.next_uid) { mail_index_sync_set_corrupted(ctx, "Append with UID %u, but next_uid = %u", rec->uid, map->hdr.next_uid); return -1; } /* move to memory. the mapping is written when unlocking so we don't waste time re-mmap()ing multiple times or waste space growing index file too large */ map = mail_index_sync_move_to_private_memory(ctx); if (rec->uid <= map->rec_map->last_appended_uid) { i_assert(map->hdr.messages_count < map->rec_map->records_count); /* the flags may have changed since it was added to map. use the updated flags already, so flag counters won't get broken. */ old_rec = MAIL_INDEX_MAP_IDX(map, map->hdr.messages_count); i_assert(old_rec->uid == rec->uid); new_flags = old_rec->flags; } else { /* don't rely on buffer->used being at the correct position. at least expunges can move it */ dest = sync_append_record(map); memcpy(dest, rec, sizeof(*rec)); memset(PTR_OFFSET(dest, sizeof(*rec)), 0, map->hdr.record_size - sizeof(*rec)); map->rec_map->records_count++; map->rec_map->last_appended_uid = rec->uid; new_flags = rec->flags; mail_index_modseq_append(ctx->modseq_ctx, map->rec_map->records_count); } map->hdr.messages_count++; map->hdr.next_uid = rec->uid+1; if ((new_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0 && (view->index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) == 0) map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY; mail_index_header_update_lowwaters(ctx, rec->uid, new_flags); mail_index_sync_header_update_counts(ctx, rec->uid, 0, new_flags); return 1; } static int sync_flag_update(const struct mail_transaction_flag_update *u, struct mail_index_sync_map_ctx *ctx) { struct mail_index_view *view = ctx->view; struct mail_index_record *rec; uint8_t flag_mask, old_flags; uint32_t seq, seq1, seq2; if (!mail_index_lookup_seq_range(view, u->uid1, u->uid2, &seq1, &seq2)) return 1; if (!MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(u)) { mail_index_modseq_update_flags(ctx->modseq_ctx, u->add_flags | u->remove_flags, seq1, seq2); } if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0 && (view->index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) == 0) view->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY; flag_mask = ~u->remove_flags; if (((u->add_flags | u->remove_flags) & (MAIL_SEEN | MAIL_DELETED)) == 0) { /* we're not modifying any counted/lowwatered flags */ for (seq = seq1; seq <= seq2; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); rec->flags = (rec->flags & flag_mask) | u->add_flags; } } else { for (seq = seq1; seq <= seq2; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); old_flags = rec->flags; rec->flags = (rec->flags & flag_mask) | u->add_flags; mail_index_header_update_lowwaters(ctx, rec->uid, rec->flags); mail_index_sync_header_update_counts_all(ctx, rec->uid, old_flags, rec->flags); } } return 1; } static int sync_header_update(const struct mail_transaction_header_update *u, struct mail_index_sync_map_ctx *ctx) { #define MAIL_INDEX_HEADER_UPDATE_FIELD_IN_RANGE(u, field) \ ((u)->offset <= offsetof(struct mail_index_header, field) && \ (u)->offset + (u)->size > offsetof(struct mail_index_header, field)) struct mail_index_map *map = ctx->view->map; uint32_t orig_log_file_tail_offset = map->hdr.log_file_tail_offset; uint32_t orig_next_uid = map->hdr.next_uid; if (u->offset >= map->hdr.base_header_size || u->offset + u->size > map->hdr.base_header_size) { mail_index_sync_set_corrupted(ctx, "Header update outside range: %u + %u > %u", u->offset, u->size, map->hdr.base_header_size); return -1; } buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); /* @UNSAFE */ if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) { memcpy(PTR_OFFSET(&map->hdr, u->offset), u + 1, u->size); } else if (u->offset < sizeof(map->hdr)) { memcpy(PTR_OFFSET(&map->hdr, u->offset), u + 1, sizeof(map->hdr) - u->offset); } if (map->hdr.next_uid < orig_next_uid) { /* next_uid update tried to shrink its value. this can happen in some race conditions with e.g. with dsync, so just silently ignore it. */ map->hdr.next_uid = orig_next_uid; } /* the tail offset updates are intended for internal transaction log handling. we'll update the offset in the header only when the sync is finished. */ map->hdr.log_file_tail_offset = orig_log_file_tail_offset; return 1; } static int mail_index_sync_record_real(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const void *data) { int ret = 0; switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_APPEND: { const struct mail_index_record *rec, *end; end = CONST_PTR_OFFSET(data, hdr->size); for (rec = data; rec < end; rec++) { ret = sync_append(rec, ctx); if (ret <= 0) break; } break; } case MAIL_TRANSACTION_EXPUNGE: case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: { const struct mail_transaction_expunge *rec = data, *end; ARRAY_TYPE(seq_range) seqs; uint32_t seq1, seq2; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* this is simply a request for expunge */ break; } t_array_init(&seqs, 64); end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { if (mail_index_lookup_seq_range(ctx->view, rec->uid1, rec->uid2, &seq1, &seq2)) seq_range_array_add_range(&seqs, seq1, seq2); } sync_expunge_range(ctx, &seqs); break; } case MAIL_TRANSACTION_EXPUNGE_GUID: case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: { const struct mail_transaction_expunge_guid *rec = data, *end; ARRAY_TYPE(seq_range) seqs; uint32_t seq; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* this is simply a request for expunge */ break; } t_array_init(&seqs, 64); end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { i_assert(rec->uid != 0); if (mail_index_lookup_seq(ctx->view, rec->uid, &seq)) seq_range_array_add(&seqs, seq); } sync_expunge_range(ctx, &seqs); break; } case MAIL_TRANSACTION_FLAG_UPDATE: { const struct mail_transaction_flag_update *rec, *end; end = CONST_PTR_OFFSET(data, hdr->size); for (rec = data; rec < end; rec++) { ret = sync_flag_update(rec, ctx); if (ret <= 0) break; } break; } case MAIL_TRANSACTION_HEADER_UPDATE: { const struct mail_transaction_header_update *rec; unsigned int i; for (i = 0; i < hdr->size; ) { rec = CONST_PTR_OFFSET(data, i); ret = sync_header_update(rec, ctx); if (ret <= 0) break; i += sizeof(*rec) + rec->size; if ((i % 4) != 0) i += 4 - (i % 4); } break; } case MAIL_TRANSACTION_EXT_INTRO: { const struct mail_transaction_ext_intro *rec = data; unsigned int i; uint32_t prev_seq; uoff_t prev_offset; mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &prev_seq, &prev_offset); ctx->ext_intro_seq = prev_seq; ctx->ext_intro_offset = prev_offset; ctx->ext_intro_end_offset = prev_offset + hdr->size + sizeof(*hdr); for (i = 0; i < hdr->size; ) { if (i + sizeof(*rec) > hdr->size) { /* should be just extra padding */ break; } rec = CONST_PTR_OFFSET(data, i); /* name_size checked by _log_view_next() */ i_assert(i + sizeof(*rec) + rec->name_size <= hdr->size); ret = mail_index_sync_ext_intro(ctx, rec); if (ret <= 0) break; i += sizeof(*rec) + rec->name_size; if ((i % 4) != 0) i += 4 - (i % 4); } break; } case MAIL_TRANSACTION_EXT_RESET: { struct mail_transaction_ext_reset rec; /* old versions have only new_reset_id */ if (hdr->size < sizeof(uint32_t)) { mail_index_sync_set_corrupted(ctx, "ext reset: invalid record size"); ret = -1; break; } i_zero(&rec); memcpy(&rec, data, I_MIN(hdr->size, sizeof(rec))); ret = mail_index_sync_ext_reset(ctx, &rec); break; } case MAIL_TRANSACTION_EXT_HDR_UPDATE: { const struct mail_transaction_ext_hdr_update *rec; unsigned int i; for (i = 0; i < hdr->size; ) { rec = CONST_PTR_OFFSET(data, i); if (i + sizeof(*rec) > hdr->size || i + sizeof(*rec) + rec->size > hdr->size) { mail_index_sync_set_corrupted(ctx, "ext hdr update: invalid record size"); ret = -1; break; } ret = mail_index_sync_ext_hdr_update(ctx, rec->offset, rec->size, rec + 1); if (ret <= 0) break; i += sizeof(*rec) + rec->size; if ((i % 4) != 0) i += 4 - (i % 4); } break; } case MAIL_TRANSACTION_EXT_HDR_UPDATE32: { const struct mail_transaction_ext_hdr_update32 *rec; unsigned int i; for (i = 0; i < hdr->size; ) { rec = CONST_PTR_OFFSET(data, i); if (i + sizeof(*rec) > hdr->size || i + sizeof(*rec) + rec->size > hdr->size) { mail_index_sync_set_corrupted(ctx, "ext hdr update: invalid record size"); ret = -1; break; } ret = mail_index_sync_ext_hdr_update(ctx, rec->offset, rec->size, rec + 1); if (ret <= 0) break; i += sizeof(*rec) + rec->size; if ((i % 4) != 0) i += 4 - (i % 4); } break; } case MAIL_TRANSACTION_EXT_REC_UPDATE: { const struct mail_transaction_ext_rec_update *rec; unsigned int i, record_size; if (ctx->cur_ext_map_idx == (uint32_t)-1) { mail_index_sync_set_corrupted(ctx, "Extension record updated " "without intro prefix"); ret = -1; break; } if (ctx->cur_ext_ignore) { ret = 1; break; } /* the record is padded to 32bits in the transaction log */ record_size = (sizeof(*rec) + ctx->cur_ext_record_size + 3) & ~3; for (i = 0; i < hdr->size; i += record_size) { rec = CONST_PTR_OFFSET(data, i); if (i + record_size > hdr->size) { mail_index_sync_set_corrupted(ctx, "ext rec update: invalid record size"); ret = -1; break; } ret = mail_index_sync_ext_rec_update(ctx, rec); if (ret <= 0) break; } break; } case MAIL_TRANSACTION_EXT_ATOMIC_INC: { const struct mail_transaction_ext_atomic_inc *rec, *end; if (ctx->cur_ext_map_idx == (uint32_t)-1) { mail_index_sync_set_corrupted(ctx, "Extension record updated " "without intro prefix"); ret = -1; break; } if (ctx->cur_ext_ignore) { ret = 1; break; } end = CONST_PTR_OFFSET(data, hdr->size); for (rec = data; rec < end; rec++) { ret = mail_index_sync_ext_atomic_inc(ctx, rec); if (ret <= 0) break; } break; } case MAIL_TRANSACTION_KEYWORD_UPDATE: { const struct mail_transaction_keyword_update *rec = data; ret = mail_index_sync_keywords(ctx, hdr, rec); break; } case MAIL_TRANSACTION_KEYWORD_RESET: { const struct mail_transaction_keyword_reset *rec = data; ret = mail_index_sync_keywords_reset(ctx, hdr, rec); break; } case MAIL_TRANSACTION_MODSEQ_UPDATE: { const struct mail_transaction_modseq_update *rec = data; ret = sync_modseq_update(ctx, rec, hdr->size); break; } case MAIL_TRANSACTION_INDEX_DELETED: if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* next sync finishes the deletion */ ctx->view->index->index_delete_requested = TRUE; } else { /* transaction log reading handles this */ } break; case MAIL_TRANSACTION_INDEX_UNDELETED: ctx->view->index->index_delete_requested = FALSE; break; case MAIL_TRANSACTION_BOUNDARY: break; case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: break; default: mail_index_sync_set_corrupted(ctx, "Unknown transaction record type 0x%x", (hdr->type & MAIL_TRANSACTION_TYPE_MASK)); ret = -1; break; } return ret; } int mail_index_sync_record(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const void *data) { int ret; T_BEGIN { ret = mail_index_sync_record_real(ctx, hdr, data); } T_END; return ret; } void mail_index_sync_map_init(struct mail_index_sync_map_ctx *sync_map_ctx, struct mail_index_view *view, enum mail_index_sync_handler_type type) { i_zero(sync_map_ctx); sync_map_ctx->view = view; sync_map_ctx->cur_ext_map_idx = (uint32_t)-1; sync_map_ctx->type = type; sync_map_ctx->modseq_ctx = mail_index_modseq_sync_begin(sync_map_ctx); mail_index_sync_init_handlers(sync_map_ctx); } void mail_index_sync_map_deinit(struct mail_index_sync_map_ctx *sync_map_ctx) { i_assert(sync_map_ctx->modseq_ctx == NULL); if (sync_map_ctx->unknown_extensions != NULL) buffer_free(&sync_map_ctx->unknown_extensions); if (sync_map_ctx->expunge_handlers_used) mail_index_sync_deinit_expunge_handlers(sync_map_ctx); mail_index_sync_deinit_handlers(sync_map_ctx); } static void mail_index_sync_update_hdr_dirty_flag(struct mail_index_map *map) { const struct mail_index_record *rec; uint32_t seq; if ((map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 || (map->index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) != 0) return; /* do we have dirty flags anymore? */ for (seq = 1; seq <= map->rec_map->records_count; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(map, seq); if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) { map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY; break; } } } #ifdef DEBUG void mail_index_map_check(struct mail_index_map *map) { const struct mail_index_header *hdr = &map->hdr; unsigned int del = 0, seen = 0; uint32_t seq, prev_uid = 0; i_assert(hdr->messages_count <= map->rec_map->records_count); for (seq = 1; seq <= hdr->messages_count; seq++) { const struct mail_index_record *rec; rec = MAIL_INDEX_REC_AT_SEQ(map, seq); i_assert(rec->uid > prev_uid); prev_uid = rec->uid; if (rec->flags & MAIL_DELETED) { i_assert(rec->uid >= hdr->first_deleted_uid_lowwater); del++; } if (rec->flags & MAIL_SEEN) seen++; else i_assert(rec->uid >= hdr->first_unseen_uid_lowwater); } i_assert(del == hdr->deleted_messages_count); i_assert(seen == hdr->seen_messages_count); } #endif int mail_index_sync_map(struct mail_index_map **_map, enum mail_index_sync_handler_type type, bool force, const char *sync_reason) { struct mail_index_map *map = *_map; struct mail_index *index = map->index; struct mail_index_view *view; struct mail_index_sync_map_ctx sync_map_ctx; const struct mail_transaction_header *thdr; const void *tdata; uint32_t prev_seq; uoff_t start_offset, prev_offset; const char *reason, *error; int ret; bool had_dirty, reset; i_assert(index->map == map || type == MAIL_INDEX_SYNC_HANDLER_VIEW); if (index->log->head == NULL) { i_assert(!force); return 0; } start_offset = type == MAIL_INDEX_SYNC_HANDLER_FILE ? map->hdr.log_file_tail_offset : map->hdr.log_file_head_offset; if (!force && (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0) { /* see if we'd prefer to reopen the index file instead of syncing the current map from the transaction log. don't check this if mmap is disabled, because reopening index causes sync to get lost. */ uoff_t log_size, index_size; if (index->fd == -1 && index->log->head->hdr.prev_file_seq != 0) { /* we don't know the index's size, so use the smallest index size we're willing to read */ index_size = MAIL_INDEX_SYNC_MIN_READ_INDEX_SIZE; } else { index_size = map->hdr.header_size + map->rec_map->records_count * map->hdr.record_size; } /* this isn't necessary correct currently, but it should be close enough */ log_size = index->log->head->last_size; if (log_size > start_offset && log_size - start_offset > index_size) return 0; } view = mail_index_view_open_with_map(index, map); ret = mail_transaction_log_view_set(view->log_view, map->hdr.log_file_seq, start_offset, (uint32_t)-1, (uoff_t)-1, &reset, &reason); if (ret <= 0) { mail_index_view_close(&view); if (force && ret < 0) { /* if we failed because of a syscall error, make sure we return a failure. */ return -1; } if (force && ret == 0) { /* the seq/offset is probably broken */ mail_index_set_error(index, "Index %s: Lost log for " "seq=%u offset=%"PRIuUOFF_T": %s " "(initial_mapped=%d, reason=%s)", index->filepath, map->hdr.log_file_seq, start_offset, reason, index->initial_mapped ? 1 : 0, sync_reason); (void)mail_index_fsck(index); } /* can't use it. sync by re-reading index. */ return 0; } mail_transaction_log_get_head(index->log, &prev_seq, &prev_offset); if (prev_seq != map->hdr.log_file_seq || prev_offset - map->hdr.log_file_tail_offset > MAIL_INDEX_MIN_WRITE_BYTES) { /* we're reading more from log than we would have preferred. remember that we probably want to rewrite index soon. */ index->index_min_write = TRUE; } /* view referenced the map. avoid unnecessary map cloning by unreferencing the map while view exists. */ map->refcount--; had_dirty = (map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0; if (had_dirty) map->hdr.flags &= ~MAIL_INDEX_HDR_FLAG_HAVE_DIRTY; if (map->hdr_base != map->hdr_copy_buf->data) { /* if syncing updates the header, it updates hdr_copy_buf and updates hdr_base to hdr_copy_buf. so the buffer must initially contain a valid header or we'll break it when writing it. */ buffer_reset(map->hdr_copy_buf); buffer_append(map->hdr_copy_buf, map->hdr_base, map->hdr.header_size); map->hdr_base = map->hdr_copy_buf->data; } mail_transaction_log_view_get_prev_pos(view->log_view, &prev_seq, &prev_offset); mail_index_sync_map_init(&sync_map_ctx, view, type); if (reset) { /* Reset the entire index. Leave only indexid and log_file_seq. */ mail_transaction_log_view_get_prev_pos(view->log_view, &prev_seq, &prev_offset); map = mail_index_map_alloc(index); if ((index->map->hdr.flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0) map->hdr.flags |= MAIL_INDEX_HDR_FLAG_FSCKD; map->hdr.log_file_seq = prev_seq; map->hdr.log_file_tail_offset = 0; mail_index_sync_replace_map(&sync_map_ctx, map); } map = NULL; /* FIXME: when transaction sync lock is removed, we'll need to handle the case when a transaction is committed while mailbox is being synced ([synced transactions][new transaction][ext transaction]). this means int_offset contains [synced] and ext_offset contains all */ while ((ret = mail_transaction_log_view_next(view->log_view, &thdr, &tdata)) > 0) { mail_transaction_log_view_get_prev_pos(view->log_view, &prev_seq, &prev_offset); if (LOG_IS_BEFORE(prev_seq, prev_offset, view->map->hdr.log_file_seq, view->map->hdr.log_file_head_offset)) { /* this has been synced already. we're here only to call expunge handlers and extension update handlers. */ i_assert(type == MAIL_INDEX_SYNC_HANDLER_FILE); if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) != 0) continue; if ((thdr->type & MAIL_TRANSACTION_EXT_MASK) == 0) continue; } /* we'll just skip over broken entries */ (void)mail_index_sync_record(&sync_map_ctx, thdr, tdata); } map = view->map; if (had_dirty) mail_index_sync_update_hdr_dirty_flag(map); mail_index_modseq_sync_end(&sync_map_ctx.modseq_ctx); mail_index_sync_update_log_offset(&sync_map_ctx, view->map, TRUE); #ifdef DEBUG mail_index_map_check(map); #endif i_assert(map->hdr.indexid == index->indexid || map->hdr.indexid == 0); /* transaction log tracks internally the current tail offset. besides using header updates, it also updates the offset to skip over following external transactions to avoid extra unneeded log reading. */ i_assert(map->hdr.log_file_seq == index->log->head->hdr.file_seq); if (map->hdr.log_file_tail_offset < index->log->head->max_tail_offset) { map->hdr.log_file_tail_offset = index->log->head->max_tail_offset; } buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr)); if (!MAIL_INDEX_MAP_IS_IN_MEMORY(map)) { memcpy(map->rec_map->mmap_base, map->hdr_copy_buf->data, map->hdr_copy_buf->used); } /* restore refcount before closing the view. this is necessary also if map got cloned, because view closing would otherwise destroy it */ map->refcount++; mail_index_sync_map_deinit(&sync_map_ctx); mail_index_view_close(&view); i_assert(index->map == map || type == MAIL_INDEX_SYNC_HANDLER_VIEW); if (mail_index_map_check_header(map, &error) <= 0) { mail_index_set_error(index, "Synchronization corrupted index header %s: %s", index->filepath, error); (void)mail_index_fsck(index); map = index->map; } else if (sync_map_ctx.errors) { /* make sure the index looks valid now */ (void)mail_index_fsck(index); map = index->map; } *_map = map; return ret < 0 ? -1 : 1; } dovecot-2.2.33.2/src/lib-index/mail-cache.c0000644000175000017500000005571513165463624015145 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "hash.h" #include "nfs-workarounds.h" #include "file-cache.h" #include "mmap-util.h" #include "read-full.h" #include "write-full.h" #include "mail-cache-private.h" #include "ioloop.h" #include #define MAIL_CACHE_MIN_HEADER_READ_SIZE 4096 void mail_cache_set_syscall_error(struct mail_cache *cache, const char *function) { mail_index_file_set_syscall_error(cache->index, cache->filepath, function); } static void mail_cache_unlink(struct mail_cache *cache) { if (!cache->index->readonly && !MAIL_INDEX_IS_IN_MEMORY(cache->index)) i_unlink_if_exists(cache->filepath); } void mail_cache_reset(struct mail_cache *cache) { mail_cache_unlink(cache); /* mark the cache as unusable */ cache->hdr = NULL; } void mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...) { va_list va; mail_cache_reset(cache); va_start(va, fmt); T_BEGIN { mail_index_set_error(cache->index, "Corrupted index cache file %s: %s", cache->filepath, t_strdup_vprintf(fmt, va)); } T_END; va_end(va); } void mail_cache_set_seq_corrupted_reason(struct mail_cache_view *cache_view, uint32_t seq, const char *reason) { uint32_t empty = 0; struct mail_cache *cache = cache_view->cache; struct mail_index_view *view = cache_view->view; mail_index_set_error(cache->index, "Corrupted record in index cache file %s: %s", cache->filepath, reason); /* drop cache pointer */ struct mail_index_transaction *t = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_ext(t, seq, cache->ext_id, &empty, NULL); if (mail_index_transaction_commit(&t) < 0) mail_cache_reset(cache); else mail_cache_expunge_count(cache, 1); } void mail_cache_file_close(struct mail_cache *cache) { if (cache->mmap_base != NULL) { if (munmap(cache->mmap_base, cache->mmap_length) < 0) mail_cache_set_syscall_error(cache, "munmap()"); } if (cache->file_cache != NULL) file_cache_set_fd(cache->file_cache, -1); if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); cache->mmap_base = NULL; cache->hdr = NULL; cache->mmap_length = 0; cache->last_field_header_offset = 0; if (cache->file_lock != NULL) file_lock_free(&cache->file_lock); cache->locked = FALSE; if (cache->fd != -1) { if (close(cache->fd) < 0) mail_cache_set_syscall_error(cache, "close()"); cache->fd = -1; } cache->opened = FALSE; } static void mail_cache_init_file_cache(struct mail_cache *cache) { struct stat st; if (cache->file_cache != NULL) file_cache_set_fd(cache->file_cache, cache->fd); if (fstat(cache->fd, &st) == 0) { if (cache->file_cache != NULL) (void)file_cache_set_size(cache->file_cache, st.st_size); } else if (!ESTALE_FSTAT(errno)) { mail_cache_set_syscall_error(cache, "fstat()"); } cache->st_ino = st.st_ino; cache->st_dev = st.st_dev; } static int mail_cache_try_open(struct mail_cache *cache) { const void *data; i_assert(!cache->opened); cache->opened = TRUE; if (MAIL_INDEX_IS_IN_MEMORY(cache->index)) return 0; i_assert(cache->fd == -1); cache->fd = nfs_safe_open(cache->filepath, cache->index->readonly ? O_RDONLY : O_RDWR); if (cache->fd == -1) { mail_cache_file_close(cache); if (errno == ENOENT) { cache->need_compress_file_seq = 0; return 0; } mail_cache_set_syscall_error(cache, "open()"); return -1; } mail_cache_init_file_cache(cache); if (mail_cache_map(cache, 0, 0, &data) < 0) { mail_cache_file_close(cache); return -1; } return 1; } static bool mail_cache_need_reopen(struct mail_cache *cache) { struct stat st; if (MAIL_CACHE_IS_UNUSABLE(cache)) { if (cache->need_compress_file_seq != 0) { /* we're waiting for compression */ return FALSE; } if (MAIL_INDEX_IS_IN_MEMORY(cache->index)) { /* disabled */ return FALSE; } } if (cache->fd == -1) return TRUE; /* see if the file has changed */ if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) { i_assert(!cache->locked); nfs_flush_file_handle_cache(cache->filepath); } if (nfs_safe_stat(cache->filepath, &st) < 0) { mail_cache_set_syscall_error(cache, "stat()"); return TRUE; } if (st.st_ino != cache->st_ino || !CMP_DEV_T(st.st_dev, cache->st_dev)) { /* file changed */ return TRUE; } if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) { /* if the old file has been deleted, the new file may have the same inode as the old one. we'll catch this here by checking if fstat() fails with ESTALE */ if (fstat(cache->fd, &st) < 0) { if (ESTALE_FSTAT(errno)) return TRUE; mail_cache_set_syscall_error(cache, "fstat()"); return FALSE; } } return FALSE; } static int mail_cache_reopen_now(struct mail_cache *cache) { struct mail_index_view *view; const struct mail_index_ext *ext; mail_cache_file_close(cache); if (mail_cache_try_open(cache) <= 0) return -1; if (mail_cache_header_fields_read(cache) < 0) return -1; view = mail_index_view_open(cache->index); ext = mail_index_view_get_ext(view, cache->ext_id); if (ext == NULL || cache->hdr->file_seq != ext->reset_id) { /* still different - maybe a race condition or maybe the file_seq really is corrupted. either way, this shouldn't happen often so we'll just mark cache to be compressed later which fixes this. */ cache->need_compress_file_seq = cache->hdr->file_seq; mail_index_view_close(&view); return 0; } mail_index_view_close(&view); i_assert(!MAIL_CACHE_IS_UNUSABLE(cache)); return 1; } int mail_cache_reopen(struct mail_cache *cache) { i_assert(!cache->locked); if (!mail_cache_need_reopen(cache)) { /* reopening does no good */ return 0; } return mail_cache_reopen_now(cache); } static void mail_cache_update_need_compress(struct mail_cache *cache) { const struct mail_cache_header *hdr = cache->hdr; struct stat st; unsigned int msg_count; unsigned int records_count, cont_percentage, delete_percentage; bool want_compress = FALSE; if (hdr->minor_version == 0) { /* compress to get ourself into the new header version */ cache->need_compress_file_seq = hdr->file_seq; return; } msg_count = cache->index->map->rec_map->records_count; if (msg_count == 0) records_count = 1; else if (hdr->record_count == 0 || hdr->record_count > msg_count*2) { /* probably not the real record_count, but hole offset that Dovecot <=v2.1 versions used to use in this position. we already checked that minor_version>0, but this could happen if old Dovecot was used to access mailbox after it had been updated. */ records_count = I_MAX(msg_count, 1); cache->hdr_copy.record_count = msg_count; cache->hdr_modified = TRUE; } else { records_count = hdr->record_count; } cont_percentage = hdr->continued_record_count * 100 / records_count; if (cont_percentage >= MAIL_CACHE_COMPRESS_CONTINUED_PERCENTAGE) { /* too many continued rows, compress */ want_compress = TRUE; } delete_percentage = hdr->deleted_record_count * 100 / (records_count + hdr->deleted_record_count); if (delete_percentage >= MAIL_CACHE_COMPRESS_DELETE_PERCENTAGE) { /* too many deleted records, compress */ want_compress = TRUE; } if (want_compress) { if (fstat(cache->fd, &st) < 0) { if (!ESTALE_FSTAT(errno)) mail_cache_set_syscall_error(cache, "fstat()"); return; } if (st.st_size >= MAIL_CACHE_COMPRESS_MIN_SIZE) cache->need_compress_file_seq = hdr->file_seq; } } static bool mail_cache_verify_header(struct mail_cache *cache, const struct mail_cache_header *hdr) { /* check that the header is still ok */ if (cache->mmap_length < sizeof(struct mail_cache_header)) { mail_cache_set_corrupted(cache, "File too small"); return FALSE; } if (hdr->major_version != MAIL_CACHE_MAJOR_VERSION) { /* version changed - upgrade silently */ mail_cache_unlink(cache); return FALSE; } if (hdr->compat_sizeof_uoff_t != sizeof(uoff_t)) { /* architecture change - handle silently(?) */ mail_cache_unlink(cache); return FALSE; } if (hdr->indexid != cache->index->indexid) { /* index id changed - handle silently */ mail_cache_unlink(cache); return FALSE; } if (hdr->file_seq == 0) { mail_cache_set_corrupted(cache, "file_seq is 0"); return FALSE; } return TRUE; } static int mail_cache_map_finish(struct mail_cache *cache, uoff_t offset, size_t size, const void *hdr_data, bool copy_hdr) { const struct mail_cache_header *hdr = hdr_data; if (offset == 0) { /* verify the header validity only with offset=0. this way we won't waste time re-verifying it all the time */ if (!mail_cache_verify_header(cache, hdr)) { cache->need_compress_file_seq = !MAIL_CACHE_IS_UNUSABLE(cache) && cache->hdr->file_seq != 0 ? cache->hdr->file_seq : 0; cache->hdr = NULL; return -1; } } if (hdr_data != NULL) { if (!copy_hdr) cache->hdr = hdr; else { memcpy(&cache->hdr_ro_copy, hdr, sizeof(cache->hdr_ro_copy)); cache->hdr = &cache->hdr_ro_copy; } mail_cache_update_need_compress(cache); } else { i_assert(cache->hdr != NULL); } i_assert(cache->hdr->file_seq != 0); if (offset + size > cache->mmap_length) return 0; return 1; } static int mail_cache_map_with_read(struct mail_cache *cache, size_t offset, size_t size, const void **data_r) { const void *hdr_data; void *data; ssize_t ret; if (cache->read_buf == NULL) { cache->read_buf = buffer_create_dynamic(default_pool, size); } else if (cache->read_offset <= offset && cache->read_offset + cache->read_buf->used >= offset+size) { /* already mapped */ *data_r = CONST_PTR_OFFSET(cache->read_buf->data, offset - cache->read_offset); hdr_data = offset == 0 ? *data_r : NULL; return mail_cache_map_finish(cache, offset, size, hdr_data, TRUE); } else { buffer_set_used_size(cache->read_buf, 0); } if (offset == 0 && size < MAIL_CACHE_MIN_HEADER_READ_SIZE) { /* we can usually read the fields header after the cache header. we need them both, so try to read them all with one pread() call. */ size = MAIL_CACHE_MIN_HEADER_READ_SIZE; } data = buffer_append_space_unsafe(cache->read_buf, size); ret = pread(cache->fd, data, size, offset); if (ret < 0) { if (errno != ESTALE) mail_cache_set_syscall_error(cache, "read()"); buffer_set_used_size(cache->read_buf, 0); cache->hdr = NULL; cache->mmap_length = 0; return -1; } buffer_set_used_size(cache->read_buf, ret); cache->read_offset = offset; cache->mmap_length = offset + cache->read_buf->used; *data_r = data; hdr_data = offset == 0 ? *data_r : NULL; return mail_cache_map_finish(cache, offset, cache->read_buf->used, hdr_data, TRUE); } int mail_cache_map(struct mail_cache *cache, size_t offset, size_t size, const void **data_r) { struct stat st; const void *data; ssize_t ret; if (size == 0) size = sizeof(struct mail_cache_header); /* verify offset + size before trying to allocate a huge amount of memory due to them. note that we may be prefetching more than we actually need, so don't fail too early. */ if ((size > cache->mmap_length || offset + size > cache->mmap_length) && (offset > 0 || size > sizeof(struct mail_cache_header))) { if (fstat(cache->fd, &st) < 0) { i_error("fstat(%s) failed: %m", cache->filepath); return -1; } if (offset >= (uoff_t)st.st_size) { *data_r = NULL; return 0; } if (offset + size > (uoff_t)st.st_size) size = st.st_size - offset; } cache->remap_counter++; if (cache->map_with_read) return mail_cache_map_with_read(cache, offset, size, data_r); if (cache->file_cache != NULL) { ret = file_cache_read(cache->file_cache, offset, size); if (ret < 0) { /* In case of ESTALE we'll simply fail without error messages. The caller will then just have to fallback to generating the value itself. We can't simply reopen the cache flie, because using it requires also having updated file offsets. */ if (errno != ESTALE) mail_cache_set_syscall_error(cache, "read()"); cache->hdr = NULL; return -1; } data = file_cache_get_map(cache->file_cache, &cache->mmap_length); *data_r = offset > cache->mmap_length ? NULL : CONST_PTR_OFFSET(data, offset); return mail_cache_map_finish(cache, offset, size, offset == 0 ? data : NULL, TRUE); } if (offset < cache->mmap_length && size <= cache->mmap_length - offset) { /* already mapped */ i_assert(cache->mmap_base != NULL); *data_r = CONST_PTR_OFFSET(cache->mmap_base, offset); return 1; } if (cache->mmap_base != NULL) { if (munmap(cache->mmap_base, cache->mmap_length) < 0) mail_cache_set_syscall_error(cache, "munmap()"); } else { if (cache->fd == -1) { /* unusable, waiting for compression or index is in memory */ i_assert(cache->need_compress_file_seq != 0 || MAIL_INDEX_IS_IN_MEMORY(cache->index)); return -1; } } /* map the whole file */ cache->hdr = NULL; cache->mmap_length = 0; if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); cache->mmap_base = mmap_ro_file(cache->fd, &cache->mmap_length); if (cache->mmap_base == MAP_FAILED) { cache->mmap_base = NULL; if (ioloop_time != cache->last_mmap_error_time) { cache->last_mmap_error_time = ioloop_time; mail_cache_set_syscall_error(cache, t_strdup_printf( "mmap(size=%"PRIuSIZE_T")", cache->mmap_length)); } cache->mmap_length = 0; return -1; } *data_r = offset > cache->mmap_length ? NULL : CONST_PTR_OFFSET(cache->mmap_base, offset); return mail_cache_map_finish(cache, offset, size, cache->mmap_base, FALSE); } int mail_cache_open_and_verify(struct mail_cache *cache) { int ret; if (cache->opened) return 0; ret = mail_cache_try_open(cache); if (ret > 0) ret = mail_cache_header_fields_read(cache); if (ret < 0) { /* failed for some reason - doesn't really matter, it's disabled for now. */ mail_cache_file_close(cache); } return ret; } static struct mail_cache *mail_cache_alloc(struct mail_index *index) { struct mail_cache *cache; cache = i_new(struct mail_cache, 1); cache->index = index; cache->fd = -1; cache->filepath = i_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL); cache->field_pool = pool_alloconly_create("Cache fields", 2048); hash_table_create(&cache->field_name_hash, cache->field_pool, 0, strcase_hash, strcasecmp); cache->dotlock_settings.use_excl_lock = (index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0; cache->dotlock_settings.nfs_flush = (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0; cache->dotlock_settings.timeout = I_MIN(MAIL_CACHE_LOCK_TIMEOUT, index->max_lock_timeout_secs); cache->dotlock_settings.stale_timeout = MAIL_CACHE_LOCK_CHANGE_TIMEOUT; if (!MAIL_INDEX_IS_IN_MEMORY(index) && (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0) cache->file_cache = file_cache_new_path(-1, cache->filepath); cache->map_with_read = (cache->index->flags & MAIL_INDEX_OPEN_FLAG_SAVEONLY) != 0; cache->ext_id = mail_index_ext_register(index, "cache", 0, sizeof(uint32_t), sizeof(uint32_t)); mail_index_register_expunge_handler(index, cache->ext_id, FALSE, mail_cache_expunge_handler, cache); return cache; } struct mail_cache *mail_cache_open_or_create(struct mail_index *index) { struct mail_cache *cache; cache = mail_cache_alloc(index); return cache; } void mail_cache_free(struct mail_cache **_cache) { struct mail_cache *cache = *_cache; *_cache = NULL; if (cache->file_cache != NULL) file_cache_free(&cache->file_cache); mail_index_unregister_expunge_handler(cache->index, cache->ext_id); mail_cache_file_close(cache); if (cache->read_buf != NULL) buffer_free(&cache->read_buf); hash_table_destroy(&cache->field_name_hash); pool_unref(&cache->field_pool); i_free(cache->field_file_map); i_free(cache->file_field_map); i_free(cache->fields); i_free(cache->filepath); i_free(cache); } static int mail_cache_lock_file(struct mail_cache *cache, bool nonblock) { unsigned int timeout_secs; int ret; if (cache->last_lock_failed) { /* previous locking failed. don't waste time waiting on it again, just try once to see if it's available now. */ nonblock = TRUE; } if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) { i_assert(cache->file_lock == NULL); timeout_secs = I_MIN(MAIL_CACHE_LOCK_TIMEOUT, cache->index->max_lock_timeout_secs); ret = mail_index_lock_fd(cache->index, cache->filepath, cache->fd, F_WRLCK, nonblock ? 0 : timeout_secs, &cache->file_lock); } else { enum dotlock_create_flags flags = nonblock ? DOTLOCK_CREATE_FLAG_NONBLOCK : 0; i_assert(cache->dotlock == NULL); ret = file_dotlock_create(&cache->dotlock_settings, cache->filepath, flags, &cache->dotlock); if (ret < 0) { mail_cache_set_syscall_error(cache, "file_dotlock_create()"); } } cache->last_lock_failed = ret <= 0; /* don't bother warning if locking failed due to a timeout. since cache updating isn't all that important we're using a very short timeout so it can be triggered sometimes on heavy load */ if (ret <= 0) return ret; mail_index_flush_read_cache(cache->index, cache->filepath, cache->fd, TRUE); return 1; } static void mail_cache_unlock_file(struct mail_cache *cache) { if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) file_unlock(&cache->file_lock); else file_dotlock_delete(&cache->dotlock); } static int mail_cache_lock_full(struct mail_cache *cache, bool nonblock) { const struct mail_index_ext *ext; const void *data; struct mail_index_view *iview; uint32_t reset_id; int i; i_assert(!cache->locked); if (!cache->opened) (void)mail_cache_open_and_verify(cache); if (MAIL_CACHE_IS_UNUSABLE(cache) || MAIL_INDEX_IS_IN_MEMORY(cache->index) || cache->index->readonly) return 0; for (;;) { if (mail_cache_lock_file(cache, nonblock) <= 0) return -1; i_assert(!MAIL_CACHE_IS_UNUSABLE(cache)); if (!mail_cache_need_reopen(cache)) { /* locked the latest file */ break; } if (mail_cache_reopen_now(cache) <= 0) { i_assert(cache->file_lock == NULL); return -1; } i_assert(cache->file_lock == NULL); /* okay, so it was just compressed. try again. */ } /* now verify that the index reset_id matches the cache's file_seq */ for (i = 0; ; i++) { iview = mail_index_view_open(cache->index); ext = mail_index_view_get_ext(iview, cache->ext_id); reset_id = ext == NULL ? 0 : ext->reset_id; mail_index_view_close(&iview); if (cache->hdr->file_seq == reset_id) break; /* mismatch. try refreshing index once. if that doesn't help, we can't use the cache. */ if (i > 0 || cache->index->mapping) { mail_cache_unlock_file(cache); return 0; } if (mail_index_refresh(cache->index) < 0) { mail_cache_unlock_file(cache); return -1; } } /* successfully locked - make sure our header is up to date */ cache->locked = TRUE; cache->hdr_modified = FALSE; if (cache->file_cache != NULL) { file_cache_invalidate(cache->file_cache, 0, sizeof(struct mail_cache_header)); } if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); if (mail_cache_map(cache, 0, 0, &data) <= 0) { (void)mail_cache_unlock(cache); return -1; } cache->hdr_copy = *cache->hdr; return 1; } int mail_cache_lock(struct mail_cache *cache) { return mail_cache_lock_full(cache, FALSE); } int mail_cache_try_lock(struct mail_cache *cache) { return mail_cache_lock_full(cache, TRUE); } int mail_cache_unlock(struct mail_cache *cache) { int ret = 0; i_assert(cache->locked); if (cache->field_header_write_pending) ret = mail_cache_header_fields_update(cache); if (MAIL_CACHE_IS_UNUSABLE(cache)) { /* we found it to be broken during the lock. just clean up. */ cache->hdr_modified = FALSE; cache->locked = FALSE; return -1; } if (cache->hdr_modified) { cache->hdr_modified = FALSE; if (mail_cache_write(cache, &cache->hdr_copy, sizeof(cache->hdr_copy), 0) < 0) ret = -1; cache->hdr_ro_copy = cache->hdr_copy; mail_cache_update_need_compress(cache); } if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) { if (fdatasync(cache->fd) < 0) mail_cache_set_syscall_error(cache, "fdatasync()"); } cache->locked = FALSE; mail_cache_unlock_file(cache); return ret; } int mail_cache_write(struct mail_cache *cache, const void *data, size_t size, uoff_t offset) { i_assert(cache->locked); if (pwrite_full(cache->fd, data, size, offset) < 0) { mail_cache_set_syscall_error(cache, "pwrite_full()"); return -1; } if (cache->file_cache != NULL) file_cache_write(cache->file_cache, data, size, offset); if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); return 0; } int mail_cache_append(struct mail_cache *cache, const void *data, size_t size, uint32_t *offset) { struct stat st; if (*offset == 0) { if (fstat(cache->fd, &st) < 0) { if (!ESTALE_FSTAT(errno)) mail_cache_set_syscall_error(cache, "fstat()"); return -1; } if (st.st_size > (uint32_t)-1) { mail_cache_set_corrupted(cache, "Cache file too large"); return -1; } *offset = st.st_size; } if ((uint32_t)-1 - *offset < size) { mail_cache_set_corrupted(cache, "Cache file too large"); return -1; } if (mail_cache_write(cache, data, size, *offset) < 0) return -1; /* FIXME: this is updated only so that older Dovecot versions (<=v2.1) can read this file. we can remove this later. */ cache->hdr_modified = TRUE; cache->hdr_copy.backwards_compat_used_file_size = *offset + size; return 0; } bool mail_cache_exists(struct mail_cache *cache) { return !MAIL_CACHE_IS_UNUSABLE(cache); } struct mail_cache_view * mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview) { struct mail_cache_view *view; view = i_new(struct mail_cache_view, 1); view->cache = cache; view->view = iview; view->cached_exists_buf = buffer_create_dynamic(default_pool, cache->file_fields_count + 10); return view; } void mail_cache_view_close(struct mail_cache_view **_view) { struct mail_cache_view *view = *_view; i_assert(view->trans_view == NULL); *_view = NULL; if (view->cache->field_header_write_pending && !view->cache->compressing) (void)mail_cache_header_fields_update(view->cache); buffer_free(&view->cached_exists_buf); i_free(view); } void mail_cache_view_update_cache_decisions(struct mail_cache_view *view, bool update) { view->no_decision_updates = !update; } uint32_t mail_cache_get_first_new_seq(struct mail_index_view *view) { const struct mail_index_header *idx_hdr; uint32_t first_new_seq, message_count; idx_hdr = mail_index_get_header(view); if (idx_hdr->day_first_uid[7] == 0) return 1; if (!mail_index_lookup_seq_range(view, idx_hdr->day_first_uid[7], (uint32_t)-1, &first_new_seq, &message_count)) { /* all messages are too old */ return message_count+1; } return first_new_seq; } dovecot-2.2.33.2/src/lib-index/mail-index-util.h0000644000175000017500000000141613123174404016143 00000000000000#ifndef MAIL_INDEX_UTIL_H #define MAIL_INDEX_UTIL_H ARRAY_DEFINE_TYPE(seq_array, uint32_t); uint32_t mail_index_uint32_to_offset(uint32_t offset); uint32_t mail_index_offset_to_uint32(uint32_t offset); #define MAIL_INDEX_PACK_MAX_SIZE ((sizeof(uint32_t) * 8 + 7) / 7) void mail_index_pack_num(uint8_t **p, uint32_t num); int mail_index_unpack_num(const uint8_t **p, const uint8_t *end, uint32_t *num_r); bool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array, uint32_t seq, unsigned int *idx_r); void mail_index_seq_array_alloc(ARRAY_TYPE(seq_array) *array, size_t record_size); bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq, const void *record, size_t record_size, void *old_record) ATTR_NULL(5); #endif dovecot-2.2.33.2/src/lib-index/mail-cache-transaction.c0000644000175000017500000006064213165463624017463 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "module-context.h" #include "file-cache.h" #include "file-set-size.h" #include "read-full.h" #include "write-full.h" #include "mail-cache-private.h" #include "mail-index-transaction-private.h" #include #include #define MAIL_CACHE_INIT_WRITE_BUFFER (1024*16) #define MAIL_CACHE_MAX_WRITE_BUFFER (1024*256) #define CACHE_TRANS_CONTEXT(obj) \ MODULE_CONTEXT(obj, cache_mail_index_transaction_module) struct mail_cache_transaction_rec { uint32_t seq; uint32_t cache_data_pos; }; struct mail_cache_transaction_ctx { union mail_index_transaction_module_context module_ctx; struct mail_index_transaction_vfuncs super; struct mail_cache *cache; struct mail_cache_view *view; struct mail_index_transaction *trans; uint32_t cache_file_seq; uint32_t first_new_seq; buffer_t *cache_data; ARRAY(struct mail_cache_transaction_rec) cache_data_seq; ARRAY_TYPE(seq_range) cache_data_wanted_seqs; uint32_t prev_seq, min_seq; size_t last_rec_pos; unsigned int records_written; unsigned int tried_compression:1; unsigned int changes:1; }; static MODULE_CONTEXT_DEFINE_INIT(cache_mail_index_transaction_module, &mail_index_module_register); static int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx); static size_t mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx); static void mail_index_transaction_cache_reset(struct mail_index_transaction *t) { struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT(t); struct mail_index_transaction_vfuncs super = ctx->super; mail_cache_transaction_reset(ctx); super.reset(t); } static int mail_index_transaction_cache_commit(struct mail_index_transaction *t, struct mail_index_transaction_commit_result *result_r) { struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT(t); struct mail_index_transaction_vfuncs super = ctx->super; /* a failed cache commit isn't important enough to fail the entire index transaction, so we'll just ignore it */ (void)mail_cache_transaction_commit(&ctx); return super.commit(t, result_r); } static void mail_index_transaction_cache_rollback(struct mail_index_transaction *t) { struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT(t); struct mail_index_transaction_vfuncs super = ctx->super; mail_cache_transaction_rollback(&ctx); super.rollback(t); } struct mail_cache_transaction_ctx * mail_cache_get_transaction(struct mail_cache_view *view, struct mail_index_transaction *t) { struct mail_cache_transaction_ctx *ctx; ctx = !cache_mail_index_transaction_module.id.module_id_set ? NULL : CACHE_TRANS_CONTEXT(t); if (ctx != NULL) return ctx; ctx = i_new(struct mail_cache_transaction_ctx, 1); ctx->cache = view->cache; ctx->view = view; ctx->trans = t; i_assert(view->transaction == NULL); view->transaction = ctx; view->trans_view = mail_index_transaction_open_updated_view(t); ctx->super = t->v; t->v.reset = mail_index_transaction_cache_reset; t->v.commit = mail_index_transaction_cache_commit; t->v.rollback = mail_index_transaction_cache_rollback; MODULE_CONTEXT_SET(t, cache_mail_index_transaction_module, ctx); return ctx; } void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx) { ctx->cache_file_seq = MAIL_CACHE_IS_UNUSABLE(ctx->cache) ? 0 : ctx->cache->hdr->file_seq; mail_index_ext_set_reset_id(ctx->trans, ctx->cache->ext_id, ctx->cache_file_seq); if (ctx->cache_data != NULL) buffer_set_used_size(ctx->cache_data, 0); if (array_is_created(&ctx->cache_data_seq)) array_clear(&ctx->cache_data_seq); ctx->prev_seq = 0; ctx->last_rec_pos = 0; ctx->changes = FALSE; } void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx) { struct mail_cache_transaction_ctx *ctx = *_ctx; *_ctx = NULL; if (ctx->records_written > 0) { /* we already wrote to the cache file. we can't (or don't want to) delete that data, so just mark it as deleted space */ if (mail_cache_transaction_lock(ctx) > 0) { ctx->cache->hdr_copy.deleted_record_count += ctx->records_written; (void)mail_cache_unlock(ctx->cache); } } MODULE_CONTEXT_UNSET(ctx->trans, cache_mail_index_transaction_module); ctx->view->transaction = NULL; ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0; mail_index_view_close(&ctx->view->trans_view); if (ctx->cache_data != NULL) buffer_free(&ctx->cache_data); if (array_is_created(&ctx->cache_data_seq)) array_free(&ctx->cache_data_seq); if (array_is_created(&ctx->cache_data_wanted_seqs)) array_free(&ctx->cache_data_wanted_seqs); i_free(ctx); } static int mail_cache_transaction_compress(struct mail_cache_transaction_ctx *ctx) { struct mail_cache *cache = ctx->cache; struct mail_index_view *view; struct mail_index_transaction *trans; struct mail_cache_compress_lock *lock; int ret; ctx->tried_compression = TRUE; cache->need_compress_file_seq = MAIL_CACHE_IS_UNUSABLE(cache) ? 0 : cache->hdr->file_seq; view = mail_index_view_open(cache->index); trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); if (mail_cache_compress(cache, trans, &lock) < 0) { mail_index_transaction_rollback(&trans); ret = -1; } else { ret = mail_index_transaction_commit(&trans); if (lock != NULL) mail_cache_compress_unlock(&lock); } mail_index_view_close(&view); mail_cache_transaction_reset(ctx); return ret; } static void mail_cache_transaction_open_if_needed(struct mail_cache_transaction_ctx *ctx) { struct mail_cache *cache = ctx->cache; const struct mail_index_ext *ext; uint32_t idx; int i; if (!cache->opened) { (void)mail_cache_open_and_verify(cache); return; } /* see if we should try to reopen the cache file */ for (i = 0;; i++) { if (MAIL_CACHE_IS_UNUSABLE(cache)) return; if (!mail_index_map_get_ext_idx(cache->index->map, cache->ext_id, &idx)) { /* index doesn't have a cache extension, but the cache file exists (corrupted indexes fixed?). fix it. */ if (i == 2) break; } else { ext = array_idx(&cache->index->map->extensions, idx); if (ext->reset_id == cache->hdr->file_seq || i == 2) break; /* index offsets don't match the cache file */ if (ext->reset_id > cache->hdr->file_seq) { /* the cache file appears to be too old. reopening should help. */ if (mail_cache_reopen(cache) != 0) break; } } /* cache file sequence might be broken. it's also possible that it was just compressed and we just haven't yet seen the changes in index. try if refreshing index helps. if not, compress the cache file. */ if (i == 0) { if (ctx->tried_compression) break; /* get the latest reset ID */ if (mail_index_refresh(ctx->cache->index) < 0) return; } else { i_assert(i == 1); (void)mail_cache_transaction_compress(ctx); } } } static int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx) { struct mail_cache *cache = ctx->cache; int ret; mail_cache_transaction_open_if_needed(ctx); if ((ret = mail_cache_lock(cache)) <= 0) { if (ret < 0) return -1; if (!ctx->tried_compression && MAIL_CACHE_IS_UNUSABLE(cache)) { if (mail_cache_transaction_compress(ctx) < 0) return -1; return mail_cache_transaction_lock(ctx); } else { return 0; } } i_assert(!MAIL_CACHE_IS_UNUSABLE(cache)); if (ctx->cache_file_seq == 0) { i_assert(ctx->cache_data == NULL || ctx->cache_data->used == 0); ctx->cache_file_seq = cache->hdr->file_seq; } else if (ctx->cache_file_seq != cache->hdr->file_seq) { if (mail_cache_unlock(cache) < 0) return -1; mail_cache_transaction_reset(ctx); return 0; } return 1; } const struct mail_cache_record * mail_cache_transaction_lookup_rec(struct mail_cache_transaction_ctx *ctx, unsigned int seq, unsigned int *trans_next_idx) { const struct mail_cache_transaction_rec *recs; unsigned int i, count; if (!MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index) && (MAIL_CACHE_IS_UNUSABLE(ctx->cache) || ctx->cache_file_seq != ctx->cache->hdr->file_seq)) { /* Cache was compressed during this transaction. We can't safely use the data anymore, since its fields won't match cache->file_fields_map. */ return NULL; } recs = array_get(&ctx->cache_data_seq, &count); for (i = *trans_next_idx; i < count; i++) { if (recs[i].seq == seq) { *trans_next_idx = i + 1; return CONST_PTR_OFFSET(ctx->cache_data->data, recs[i].cache_data_pos); } } *trans_next_idx = i + 1; if (seq == ctx->prev_seq && i == count) { /* update the unfinished record's (temporary) size and return it */ mail_cache_transaction_update_last_rec_size(ctx); return CONST_PTR_OFFSET(ctx->cache_data->data, ctx->last_rec_pos); } return NULL; } static int mail_cache_transaction_update_index(struct mail_cache_transaction_ctx *ctx, uint32_t write_offset) { struct mail_cache *cache = ctx->cache; const struct mail_cache_record *rec = ctx->cache_data->data; const struct mail_cache_transaction_rec *recs; uint32_t i, seq_count; mail_index_ext_using_reset_id(ctx->trans, ctx->cache->ext_id, ctx->cache_file_seq); /* write the cache_offsets to index file. records' prev_offset is updated to point to old cache record when index is being synced. */ recs = array_get(&ctx->cache_data_seq, &seq_count); for (i = 0; i < seq_count; i++) { mail_index_update_ext(ctx->trans, recs[i].seq, cache->ext_id, &write_offset, NULL); write_offset += rec->size; rec = CONST_PTR_OFFSET(rec, rec->size); } return 0; } static int mail_cache_link_records(struct mail_cache_transaction_ctx *ctx, uint32_t write_offset) { struct mail_index_map *map; struct mail_cache_record *rec; const struct mail_cache_transaction_rec *recs; const uint32_t *prev_offsetp; ARRAY_TYPE(uint32_t) seq_offsets; uint32_t i, seq_count, reset_id, prev_offset, *offsetp; const void *data; i_assert(ctx->min_seq != 0); i_array_init(&seq_offsets, 64); recs = array_get(&ctx->cache_data_seq, &seq_count); rec = buffer_get_modifiable_data(ctx->cache_data, NULL); for (i = 0; i < seq_count; i++) { offsetp = array_idx_modifiable(&seq_offsets, recs[i].seq - ctx->min_seq); if (*offsetp != 0) prev_offset = *offsetp; else { mail_index_lookup_ext_full(ctx->view->trans_view, recs[i].seq, ctx->cache->ext_id, &map, &data, NULL); prev_offsetp = data; if (prev_offsetp == NULL || *prev_offsetp == 0) prev_offset = 0; else if (mail_index_ext_get_reset_id(ctx->view->trans_view, map, ctx->cache->ext_id, &reset_id) && reset_id == ctx->cache_file_seq) prev_offset = *prev_offsetp; else prev_offset = 0; if (prev_offset >= write_offset) { mail_cache_set_corrupted(ctx->cache, "Cache record offset points outside existing file"); array_free(&seq_offsets); return -1; } } if (prev_offset != 0) { /* link this record to previous one */ rec->prev_offset = prev_offset; ctx->cache->hdr_copy.continued_record_count++; } else { ctx->cache->hdr_copy.record_count++; } *offsetp = write_offset; write_offset += rec->size; rec = PTR_OFFSET(rec, rec->size); } array_free(&seq_offsets); ctx->cache->hdr_modified = TRUE; return 0; } static int mail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx) { struct stat st; uint32_t write_offset = 0; int ret = 0; i_assert(!ctx->cache->locked); if (array_count(&ctx->cache_data_seq) == 0) { /* we had done some changes, but they were aborted. */ i_assert(ctx->last_rec_pos == 0); ctx->min_seq = 0; return 0; } if (mail_cache_transaction_lock(ctx) <= 0) return -1; i_assert(ctx->cache_data != NULL); i_assert(ctx->last_rec_pos <= ctx->cache_data->used); /* we need to get the final write offset for linking records */ if (fstat(ctx->cache->fd, &st) < 0) { if (!ESTALE_FSTAT(errno)) mail_cache_set_syscall_error(ctx->cache, "fstat()"); ret = -1; } else if ((uint32_t)-1 < st.st_size + ctx->last_rec_pos) { mail_cache_set_corrupted(ctx->cache, "Cache file too large"); ret = -1; } else { write_offset = st.st_size; if (mail_cache_link_records(ctx, write_offset) < 0) ret = -1; } /* write to cache file */ if (ret < 0 || mail_cache_append(ctx->cache, ctx->cache_data->data, ctx->last_rec_pos, &write_offset) < 0) ret = -1; else { /* update records' cache offsets to index */ ctx->records_written++; ret = mail_cache_transaction_update_index(ctx, write_offset); } if (mail_cache_unlock(ctx->cache) < 0) ret = -1; /* drop the written data from buffer */ buffer_copy(ctx->cache_data, 0, ctx->cache_data, ctx->last_rec_pos, (size_t)-1); buffer_set_used_size(ctx->cache_data, ctx->cache_data->used - ctx->last_rec_pos); ctx->last_rec_pos = 0; ctx->min_seq = 0; array_clear(&ctx->cache_data_seq); array_clear(&ctx->cache_data_wanted_seqs); return ret; } static void mail_cache_transaction_drop_unwanted(struct mail_cache_transaction_ctx *ctx, size_t space_needed) { struct mail_cache_transaction_rec *recs; unsigned int i, count; recs = array_get_modifiable(&ctx->cache_data_seq, &count); /* find out how many records to delete. delete all unwanted sequences, and if that's not enough delete some more. */ for (i = 0; i < count; i++) { if (seq_range_exists(&ctx->cache_data_wanted_seqs, recs[i].seq)) { if (recs[i].cache_data_pos >= space_needed) break; /* we're going to forcibly delete it - remove it also from the array since it's no longer useful there */ seq_range_array_remove(&ctx->cache_data_wanted_seqs, recs[i].seq); } } unsigned int deleted_count = i; size_t deleted_space = i < count ? recs[i].cache_data_pos : ctx->last_rec_pos; for (; i < count; i++) recs[i].cache_data_pos -= deleted_space; ctx->last_rec_pos -= deleted_space; array_delete(&ctx->cache_data_seq, 0, deleted_count); buffer_delete(ctx->cache_data, 0, deleted_space); } static size_t mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx) { struct mail_cache_record *rec; void *data; size_t size; data = buffer_get_modifiable_data(ctx->cache_data, &size); rec = PTR_OFFSET(data, ctx->last_rec_pos); rec->size = size - ctx->last_rec_pos; i_assert(rec->size > sizeof(*rec)); return rec->size; } static void mail_cache_transaction_update_last_rec(struct mail_cache_transaction_ctx *ctx) { struct mail_cache_transaction_rec *trans_rec; size_t size; size = mail_cache_transaction_update_last_rec_size(ctx); if (size > MAIL_CACHE_RECORD_MAX_SIZE) { buffer_set_used_size(ctx->cache_data, ctx->last_rec_pos); return; } if (ctx->min_seq > ctx->prev_seq || ctx->min_seq == 0) ctx->min_seq = ctx->prev_seq; trans_rec = array_append_space(&ctx->cache_data_seq); trans_rec->seq = ctx->prev_seq; trans_rec->cache_data_pos = ctx->last_rec_pos; ctx->last_rec_pos = ctx->cache_data->used; } static void mail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx) { struct mail_cache_record new_rec; if (ctx->prev_seq != 0) { /* update previously added cache record's size */ mail_cache_transaction_update_last_rec(ctx); } else if (ctx->cache_data == NULL) { ctx->cache_data = buffer_create_dynamic(default_pool, MAIL_CACHE_INIT_WRITE_BUFFER); i_array_init(&ctx->cache_data_seq, 64); i_array_init(&ctx->cache_data_wanted_seqs, 32); } i_zero(&new_rec); buffer_append(ctx->cache_data, &new_rec, sizeof(new_rec)); ctx->prev_seq = 0; ctx->changes = TRUE; } int mail_cache_transaction_commit(struct mail_cache_transaction_ctx **_ctx) { struct mail_cache_transaction_ctx *ctx = *_ctx; int ret = 0; if (ctx->changes) { if (ctx->prev_seq != 0) mail_cache_transaction_update_last_rec(ctx); if (mail_cache_transaction_flush(ctx) < 0) ret = -1; else { /* successfully wrote everything */ ctx->records_written = 0; } /* Here would be a good place to do fdatasync() to make sure everything is written before offsets are updated to index. However it slows down I/O unneededly and we're pretty good at catching and fixing cache corruption, so we no longer do it. */ } mail_cache_transaction_rollback(_ctx); return ret; } static int mail_cache_header_fields_write(struct mail_cache_transaction_ctx *ctx, const buffer_t *buffer) { struct mail_cache *cache = ctx->cache; uint32_t offset, hdr_offset; i_assert(cache->locked); offset = 0; if (mail_cache_append(cache, buffer->data, buffer->used, &offset) < 0) return -1; if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) { if (fdatasync(cache->fd) < 0) { mail_cache_set_syscall_error(cache, "fdatasync()"); return -1; } } /* find offset to the previous header's "next_offset" field */ if (mail_cache_header_fields_get_next_offset(cache, &hdr_offset) < 0) return -1; /* update the next_offset offset, so our new header will be found */ offset = mail_index_uint32_to_offset(offset); if (mail_cache_write(cache, &offset, sizeof(offset), hdr_offset) < 0) return -1; if (hdr_offset == offsetof(struct mail_cache_header, field_header_offset)) { /* we're adding the first field. hdr_copy needs to be kept in sync so unlocking won't overwrite it. */ cache->hdr_copy.field_header_offset = hdr_offset; cache->hdr_ro_copy.field_header_offset = hdr_offset; } return 0; } static void mail_cache_mark_adding(struct mail_cache *cache, bool set) { unsigned int i; /* we want to avoid adding all the fields one by one to the cache file, so just add all of them at once in here. the unused ones get dropped later when compressing. */ for (i = 0; i < cache->fields_count; i++) { if (set) cache->fields[i].used = TRUE; cache->fields[i].adding = set; } } static int mail_cache_header_add_field(struct mail_cache_transaction_ctx *ctx, unsigned int field_idx) { struct mail_cache *cache = ctx->cache; int ret; if (MAIL_INDEX_IS_IN_MEMORY(cache->index)) { if (cache->file_fields_count <= field_idx) { cache->file_field_map = i_realloc_type(cache->file_field_map, unsigned int, cache->file_fields_count, field_idx+1); cache->file_fields_count = field_idx+1; } cache->file_field_map[field_idx] = field_idx; cache->field_file_map[field_idx] = field_idx; return 0; } if (mail_cache_transaction_lock(ctx) <= 0) { if (MAIL_CACHE_IS_UNUSABLE(cache)) return -1; /* if we compressed the cache, the field should be there now. it's however possible that someone else just compressed it and we only reopened the cache file. */ if (cache->field_file_map[field_idx] != (uint32_t)-1) return 0; /* need to add it */ if (mail_cache_transaction_lock(ctx) <= 0) return -1; } /* re-read header to make sure we don't lose any fields. */ if (mail_cache_header_fields_read(cache) < 0) { (void)mail_cache_unlock(cache); return -1; } if (cache->field_file_map[field_idx] != (uint32_t)-1) { /* it was already added */ if (mail_cache_unlock(cache) < 0) return -1; return 0; } T_BEGIN { buffer_t *buffer; buffer = buffer_create_dynamic(pool_datastack_create(), 256); mail_cache_header_fields_get(cache, buffer); ret = mail_cache_header_fields_write(ctx, buffer); } T_END; if (ret == 0) { /* we wrote all the headers, so there are no pending changes */ cache->field_header_write_pending = FALSE; ret = mail_cache_header_fields_read(cache); } if (ret == 0 && cache->field_file_map[field_idx] == (uint32_t)-1) { mail_index_set_error(cache->index, "Cache file %s: Newly added field got " "lost unexpectedly", cache->filepath); ret = -1; } if (mail_cache_unlock(cache) < 0) ret = -1; return ret; } void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx, const void *data, size_t data_size) { uint32_t file_field, data_size32; unsigned int fixed_size; size_t full_size; int ret; i_assert(field_idx < ctx->cache->fields_count); i_assert(data_size < (uint32_t)-1); if (ctx->cache->fields[field_idx].field.decision == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) return; if (ctx->cache_file_seq == 0) { mail_cache_transaction_open_if_needed(ctx); if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache)) ctx->cache_file_seq = ctx->cache->hdr->file_seq; } else if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache) && ctx->cache_file_seq != ctx->cache->hdr->file_seq) { /* cache was compressed within this transaction */ mail_cache_transaction_reset(ctx); } file_field = ctx->cache->field_file_map[field_idx]; if (MAIL_CACHE_IS_UNUSABLE(ctx->cache) || file_field == (uint32_t)-1) { /* we'll have to add this field to headers */ mail_cache_mark_adding(ctx->cache, TRUE); ret = mail_cache_header_add_field(ctx, field_idx); mail_cache_mark_adding(ctx->cache, FALSE); if (ret < 0) return; if (ctx->cache_file_seq == 0) { if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index)) ctx->cache_file_seq = 1; else ctx->cache_file_seq = ctx->cache->hdr->file_seq; } file_field = ctx->cache->field_file_map[field_idx]; i_assert(file_field != (uint32_t)-1); } i_assert(ctx->cache_file_seq != 0); mail_cache_decision_add(ctx->view, seq, field_idx); fixed_size = ctx->cache->fields[field_idx].field.field_size; i_assert(fixed_size == UINT_MAX || fixed_size == data_size); data_size32 = (uint32_t)data_size; if (ctx->prev_seq != seq) { mail_cache_transaction_switch_seq(ctx); ctx->prev_seq = seq; seq_range_array_add(&ctx->cache_data_wanted_seqs, seq); /* remember roughly what we have modified, so cache lookups can look into transactions to see changes. */ if (seq < ctx->view->trans_seq1 || ctx->view->trans_seq1 == 0) ctx->view->trans_seq1 = seq; if (seq > ctx->view->trans_seq2) ctx->view->trans_seq2 = seq; } /* remember that this value exists, in case we try to look it up */ buffer_write(ctx->view->cached_exists_buf, field_idx, &ctx->view->cached_exists_value, 1); full_size = (data_size + 3) & ~3; if (fixed_size == UINT_MAX) full_size += sizeof(data_size32); if (ctx->cache_data->used + full_size > MAIL_CACHE_MAX_WRITE_BUFFER && ctx->last_rec_pos > 0) { /* time to flush our buffer. if flushing fails because the cache file had been compressed and was reopened, return without adding the cached data since cache_data buffer doesn't contain the cache_rec anymore. */ if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index)) { /* just drop the old data to free up memory */ size_t space_needed = ctx->cache_data->used + full_size - MAIL_CACHE_MAX_WRITE_BUFFER; mail_cache_transaction_drop_unwanted(ctx, space_needed); } else if (mail_cache_transaction_flush(ctx) < 0) { /* make sure the transaction is reset, so we don't constantly try to flush for each call to this function */ mail_cache_transaction_reset(ctx); return; } } buffer_append(ctx->cache_data, &file_field, sizeof(file_field)); if (fixed_size == UINT_MAX) { buffer_append(ctx->cache_data, &data_size32, sizeof(data_size32)); } buffer_append(ctx->cache_data, data, data_size); if ((data_size & 3) != 0) buffer_append_zero(ctx->cache_data, 4 - (data_size & 3)); } bool mail_cache_field_want_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx) { enum mail_cache_decision_type decision; mail_cache_transaction_open_if_needed(ctx); decision = mail_cache_field_get_decision(ctx->view->cache, field_idx); decision &= ~MAIL_CACHE_DECISION_FORCED; switch (decision) { case MAIL_CACHE_DECISION_NO: return FALSE; case MAIL_CACHE_DECISION_TEMP: /* add it only if it's newer than what we would drop when compressing */ if (ctx->first_new_seq == 0) { ctx->first_new_seq = mail_cache_get_first_new_seq(ctx->view->view); } if (seq < ctx->first_new_seq) return FALSE; break; default: break; } return mail_cache_field_exists(ctx->view, seq, field_idx) == 0; } bool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx) { enum mail_cache_decision_type decision; mail_cache_transaction_open_if_needed(ctx); decision = mail_cache_field_get_decision(ctx->view->cache, field_idx); if (decision == (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_NO)) return FALSE; return mail_cache_field_exists(ctx->view, seq, field_idx) == 0; } void mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx, uint32_t seq) { if (array_is_created(&ctx->cache_data_wanted_seqs)) seq_range_array_remove(&ctx->cache_data_wanted_seqs, seq); } dovecot-2.2.33.2/src/lib-index/mailbox-log.c0000644000175000017500000001617113165463624015365 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "eacces-error.h" #include "mailbox-log.h" #include #include #include #include /* How often to reopen the log file to make sure that the changes are written to the latest file. The main problem here is if the value is too high the changes could be written to a file that was already rotated and deleted. That wouldn't happen in any real world situations though, since the file rotation time is probably measured in months or years. Still, each session rarely writes anything here, so the value can just as well be a pretty small one without any performance problems. */ #define MAILBOX_LOG_REOPEN_SECS (60) #define MAILBOX_LOG_ROTATE_SIZE (1024*4) struct mailbox_log { char *filepath, *filepath2; int fd; time_t open_timestamp; mode_t mode; gid_t gid; char *gid_origin; }; struct mailbox_log_iter { struct mailbox_log *log; int fd; const char *filepath; struct mailbox_log_record buf[128]; unsigned int idx, count; uoff_t offset; bool failed; }; static void mailbox_log_close(struct mailbox_log *log); struct mailbox_log *mailbox_log_alloc(const char *path) { struct mailbox_log *log; log = i_new(struct mailbox_log, 1); log->filepath = i_strdup(path); log->filepath2 = i_strconcat(path, ".2", NULL); log->mode = 0644; log->gid = (gid_t)-1; log->fd = -1; return log; } void mailbox_log_free(struct mailbox_log **_log) { struct mailbox_log *log = *_log; *_log = NULL; mailbox_log_close(log); i_free(log->gid_origin); i_free(log->filepath); i_free(log->filepath2); i_free(log); } static void mailbox_log_close(struct mailbox_log *log) { if (log->fd != -1) { if (close(log->fd) < 0) i_error("close(%s) failed: %m", log->filepath); log->fd = -1; } } void mailbox_log_set_permissions(struct mailbox_log *log, mode_t mode, gid_t gid, const char *gid_origin) { log->mode = mode; log->gid = gid; i_free(log->gid_origin); log->gid_origin = i_strdup(gid_origin); } static int mailbox_log_open(struct mailbox_log *log) { mode_t old_mode; i_assert(log->fd == -1); log->open_timestamp = ioloop_time; log->fd = open(log->filepath, O_RDWR | O_APPEND); if (log->fd != -1) return 0; /* try to create it */ old_mode = umask(0666 ^ log->mode); log->fd = open(log->filepath, O_RDWR | O_APPEND | O_CREAT, 0666); umask(old_mode); if (log->fd == -1) { if (errno != EACCES) i_error("creat(%s) failed: %m", log->filepath); else i_error("%s", eacces_error_get("creat", log->filepath)); return -1; } if (fchown(log->fd, (uid_t)-1, log->gid) < 0) { if (errno != EPERM) i_error("fchown(%s) failed: %m", log->filepath); else { i_error("%s", eperm_error_get_chgrp("fchown", log->filepath, log->gid, log->gid_origin)); } } return 0; } static int mailbox_log_rotate_if_needed(struct mailbox_log *log) { struct stat st; if (fstat(log->fd, &st) < 0) { i_error("fstat(%s) failed: %m", log->filepath); return -1; } if (st.st_size < MAILBOX_LOG_ROTATE_SIZE) return 0; if (rename(log->filepath, log->filepath2) < 0 && errno != ENOENT) { i_error("rename(%s, %s) failed: %m", log->filepath, log->filepath2); return -1; } return 0; } void mailbox_log_record_set_timestamp(struct mailbox_log_record *rec, time_t stamp) { cpu32_to_be_unaligned(stamp, rec->timestamp); } time_t mailbox_log_record_get_timestamp(const struct mailbox_log_record *rec) { return (time_t) be32_to_cpu_unaligned(rec->timestamp); } int mailbox_log_append(struct mailbox_log *log, const struct mailbox_log_record *rec) { struct stat st; ssize_t ret; /* we don't have to be too strict about appending to the latest log file. the records' ordering doesn't matter and iteration goes through both logs anyway. still, if there's a long running session it shouldn't keep writing to a rotated log forever. */ if (log->open_timestamp/MAILBOX_LOG_REOPEN_SECS != ioloop_time/MAILBOX_LOG_REOPEN_SECS) mailbox_log_close(log); if (log->fd == -1) { if (mailbox_log_open(log) < 0) return -1; i_assert(log->fd != -1); } /* We don't bother with locking, atomic appends will protect us. If they don't (NFS), the worst that can happen is that a few records get overwritten (because they're all the same size). This whole log isn't supposed to be super-reliable anyway. */ ret = write(log->fd, rec, sizeof(*rec)); if (ret < 0) { i_error("write(%s) failed: %m", log->filepath); return -1; } else if (ret != sizeof(*rec)) { i_error("write(%s) wrote %d/%u bytes", log->filepath, (int)ret, (unsigned int)sizeof(*rec)); if (fstat(log->fd, &st) == 0) { if (ftruncate(log->fd, st.st_size - ret) < 0) { i_error("ftruncate(%s) failed: %m", log->filepath); } } return -1; } (void)mailbox_log_rotate_if_needed(log); return 0; } static bool mailbox_log_iter_open_next(struct mailbox_log_iter *iter) { if (iter->fd != -1) { if (close(iter->fd) < 0) i_error("close(%s) failed: %m", iter->filepath); iter->fd = -1; } if (iter->filepath == NULL) iter->filepath = iter->log->filepath2; else if (iter->filepath == iter->log->filepath2) iter->filepath = iter->log->filepath; else return FALSE; iter->fd = open(iter->filepath, O_RDONLY | O_APPEND); if (iter->fd != -1) return TRUE; else if (errno == ENOENT) { if (iter->filepath == iter->log->filepath2) return mailbox_log_iter_open_next(iter); } else { i_error("open(%s) failed: %m", iter->filepath); iter->failed = TRUE; } return FALSE; } struct mailbox_log_iter *mailbox_log_iter_init(struct mailbox_log *log) { struct mailbox_log_iter *iter; iter = i_new(struct mailbox_log_iter, 1); iter->log = log; iter->fd = -1; (void)mailbox_log_iter_open_next(iter); return iter; } const struct mailbox_log_record * mailbox_log_iter_next(struct mailbox_log_iter *iter) { const struct mailbox_log_record *rec; uoff_t offset; ssize_t ret; if (iter->idx == iter->count) { if (iter->fd == -1) return NULL; ret = pread(iter->fd, iter->buf, sizeof(iter->buf), iter->offset); if (ret < 0) { i_error("pread(%s) failed: %m", iter->filepath); iter->failed = TRUE; return NULL; } if (ret == 0) { if (!mailbox_log_iter_open_next(iter)) return NULL; iter->idx = iter->count = 0; iter->offset = 0; return mailbox_log_iter_next(iter); } iter->idx = 0; iter->count = ret / sizeof(iter->buf[0]); iter->offset += iter->count * sizeof(iter->buf[0]); } rec = &iter->buf[iter->idx++]; if (rec->type < MAILBOX_LOG_RECORD_DELETE_MAILBOX || rec->type > MAILBOX_LOG_RECORD_UNSUBSCRIBE) { offset = iter->offset - (iter->count - iter->idx) * sizeof(iter->buf[0]); i_error("Corrupted mailbox log %s at offset %"PRIuUOFF_T": " "type=%d", iter->filepath, offset, rec->type); i_unlink(iter->filepath); return NULL; } return rec; } int mailbox_log_iter_deinit(struct mailbox_log_iter **_iter) { struct mailbox_log_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; if (iter->fd != -1) { if (close(iter->fd) < 0) i_error("close(%s) failed: %m", iter->filepath); } i_free(iter); return ret; } dovecot-2.2.33.2/src/lib-index/mail-transaction-log-view-private.h0000644000175000017500000000156513165463624021625 00000000000000#ifndef MAIL_TRANSACTION_LOG_VIEW_PRIVATE_H #define MAIL_TRANSACTION_LOG_VIEW_PRIVATE_H #include "mail-transaction-log-private.h" struct mail_transaction_log_view { struct mail_transaction_log *log; struct mail_transaction_log_view *next; uint32_t min_file_seq, max_file_seq; uoff_t min_file_offset, max_file_offset; struct mail_transaction_header tmp_hdr; /* a list of log files we've referenced. we have to keep this list explicitly because more files may be added into the linked list at any time. */ ARRAY(struct mail_transaction_log_file *) file_refs; struct mail_transaction_log_file *cur, *head, *tail; uoff_t cur_offset; uint64_t prev_modseq; uint32_t prev_file_seq; uoff_t prev_file_offset; struct mail_transaction_log_file *mark_file; uoff_t mark_offset, mark_next_offset; uint64_t mark_modseq; unsigned int broken:1; }; #endif dovecot-2.2.33.2/src/lib-index/test-mail-transaction-log-view.c0000644000175000017500000001626513165463624021130 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-transaction-log-view-private.h" static struct mail_transaction_log *log; static struct mail_transaction_log_view *view; void mail_index_set_error(struct mail_index *index ATTR_UNUSED, const char *fmt ATTR_UNUSED, ...) { } void mail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file ATTR_UNUSED, const char *fmt ATTR_UNUSED, ...) { } void mail_transaction_logs_clean(struct mail_transaction_log *log ATTR_UNUSED) { } int mail_transaction_log_find_file(struct mail_transaction_log *log, uint32_t file_seq, bool nfs_flush ATTR_UNUSED, struct mail_transaction_log_file **file_r, const char **reason_r) { struct mail_transaction_log_file *file; for (file = log->files; file != NULL; file = file->next) { if (file->hdr.file_seq == file_seq) { *file_r = file; return 1; } } *reason_r = "not found"; return 0; } int mail_transaction_log_file_map(struct mail_transaction_log_file *file ATTR_UNUSED, uoff_t start_offset ATTR_UNUSED, uoff_t end_offset ATTR_UNUSED, const char **reason_r ATTR_UNUSED) { return 1; } int mail_transaction_log_file_get_highest_modseq_at( struct mail_transaction_log_file *file ATTR_UNUSED, uoff_t offset ATTR_UNUSED, uint64_t *highest_modseq_r, const char **error_r ATTR_UNUSED) { *highest_modseq_r = 0; return 0; } void mail_transaction_update_modseq(const struct mail_transaction_header *hdr ATTR_UNUSED, const void *data ATTR_UNUSED, uint64_t *cur_modseq, unsigned int version ATTR_UNUSED) { *cur_modseq += 1; } static void test_transaction_log_file_add(uint32_t file_seq) { struct mail_transaction_log_file **p, *file; file = i_new(struct mail_transaction_log_file, 1); file->hdr.file_seq = file_seq; file->hdr.hdr_size = file->sync_offset = sizeof(file->hdr); file->hdr.prev_file_seq = file_seq - 1; file->hdr.prev_file_offset = (uint32_t)-1; file->log = log; file->fd = -1; file->buffer = buffer_create_dynamic(default_pool, 256); file->buffer_offset = file->hdr.hdr_size; /* files must be sorted by file_seq */ for (p = &log->files; *p != NULL; p = &(*p)->next) { if ((*p)->hdr.file_seq > file->hdr.file_seq) { file->next = *p; break; } } *p = file; log->head = file; } static bool view_is_file_refed(uint32_t file_seq) { struct mail_transaction_log_file *const *files; unsigned int i, count; bool ret = FALSE; files = array_get(&view->file_refs, &count); for (i = 0; i < count; i++) { if (files[i]->hdr.file_seq == file_seq) { i_assert(!ret); /* could be a test too.. */ ret = TRUE; } } return ret; } static size_t add_append_record(struct mail_transaction_log_file *file, const struct mail_index_record *rec) { struct mail_transaction_header hdr; size_t size; i_zero(&hdr); hdr.type = MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL; hdr.size = mail_index_uint32_to_offset(sizeof(hdr) + sizeof(*rec)); buffer_append(file->buffer, &hdr, sizeof(hdr)); buffer_append(file->buffer, rec, sizeof(*rec)); size = sizeof(hdr) + sizeof(*rec); file->sync_offset += size; return size; } static void test_mail_transaction_log_view(void) { const struct mail_transaction_header *hdr; const struct mail_index_record *rec; struct mail_index_record append_rec; const void *data; void *oldfile; uint32_t seq; uoff_t offset, last_log_size; const char *reason; bool reset; test_begin("init"); log = i_new(struct mail_transaction_log, 1); log->index = i_new(struct mail_index, 1); log->index->log = log; log->index->log_sync_locked = TRUE; test_transaction_log_file_add(1); test_transaction_log_file_add(2); test_transaction_log_file_add(3); /* add an append record to the 3rd log file */ i_zero(&append_rec); append_rec.uid = 1; last_log_size = sizeof(struct mail_transaction_log_header) + add_append_record(log->head, &append_rec); view = mail_transaction_log_view_open(log); i_assert(view != NULL); test_assert(log->views == view && !view_is_file_refed(1) && !view_is_file_refed(2) && view_is_file_refed(3)); test_end(); /* we have files 1-3 opened */ test_begin("set all"); test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset, &reason) == 1 && reset && view_is_file_refed(1) && view_is_file_refed(2) && view_is_file_refed(3) && !mail_transaction_log_view_is_corrupted(view)); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 1); test_assert(hdr->type == (MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL)); rec = data; test_assert(memcmp(rec, &append_rec, sizeof(*rec)) == 0); test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); test_assert(mail_transaction_log_view_is_last(view)); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 3 && offset == last_log_size); test_end(); test_begin("set first"); test_assert(mail_transaction_log_view_set(view, 0, 0, 0, 0, &reset, &reason) == 1); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); test_end(); test_begin("set end"); test_assert(mail_transaction_log_view_set(view, 3, last_log_size, (uint32_t)-1, (uoff_t)-1, &reset, &reason) == 1); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 3 && offset == last_log_size); test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); mail_transaction_log_view_get_prev_pos(view, &seq, &offset); test_assert(seq == 3 && offset == last_log_size); test_end(); test_begin("log clear"); mail_transaction_log_view_clear(view, 2); test_assert(!view_is_file_refed(1) && view_is_file_refed(2) && view_is_file_refed(3)); oldfile = log->files; buffer_free(&log->files->buffer); log->files = log->files->next; i_free(oldfile); test_assert(log->files->hdr.file_seq == 2); test_end(); /* --- first file has been removed --- */ test_begin("set 2-3"); test_assert(mail_transaction_log_view_set(view, 2, 0, (uint32_t)-1, (uoff_t)-1, &reset, &reason) == 1); test_end(); test_begin("missing log handing"); test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset, &reason) == 0); test_end(); test_begin("closed log handling"); view->log = NULL; test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset, &reason) == -1); view->log = log; test_end(); mail_transaction_log_view_close(&view); i_free(log->index); while (log->files != NULL) { oldfile = log->files; buffer_free(&log->files->buffer); log->files = log->files->next; i_free(oldfile); } i_free(log); } int main(void) { static void (*test_functions[])(void) = { test_mail_transaction_log_view, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-transaction.c0000644000175000017500000002451413123174404017512 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "hook-build.h" #include "bsearch-insert-pos.h" #include "llist.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include "mail-index-transaction-private.h" static ARRAY(hook_mail_index_transaction_created_t *) hook_mail_index_transaction_created; void mail_index_transaction_hook_register(hook_mail_index_transaction_created_t *hook) { if (!array_is_created(&hook_mail_index_transaction_created)) i_array_init(&hook_mail_index_transaction_created, 8); array_append(&hook_mail_index_transaction_created, &hook, 1); } void mail_index_transaction_hook_unregister(hook_mail_index_transaction_created_t *hook) { unsigned int idx; bool found = FALSE; i_assert(array_is_created(&hook_mail_index_transaction_created)); for(idx = 0; idx < array_count(&hook_mail_index_transaction_created); idx++) { hook_mail_index_transaction_created_t *const *hook_ptr = array_idx(&hook_mail_index_transaction_created, idx); if (*hook_ptr == hook) { array_delete(&hook_mail_index_transaction_created, idx, 1); found = TRUE; break; } } i_assert(found == TRUE); if (array_count(&hook_mail_index_transaction_created) == 0) array_free(&hook_mail_index_transaction_created); } struct mail_index_view * mail_index_transaction_get_view(struct mail_index_transaction *t) { return t->view; } bool mail_index_transaction_is_expunged(struct mail_index_transaction *t, uint32_t seq) { struct mail_transaction_expunge_guid key; if (!array_is_created(&t->expunges)) return FALSE; if (t->expunges_nonsorted) mail_index_transaction_sort_expunges(t); key.uid = seq; return array_bsearch(&t->expunges, &key, mail_transaction_expunge_guid_cmp) != NULL; } void mail_index_transaction_ref(struct mail_index_transaction *t) { t->refcount++; } void mail_index_transaction_unref(struct mail_index_transaction **_t) { struct mail_index_transaction *t = *_t; *_t = NULL; if (--t->refcount > 0) return; mail_index_transaction_reset_v(t); DLLIST_REMOVE(&t->view->transactions_list, t); array_free(&t->module_contexts); mail_index_view_transaction_unref(t->view); if (t->latest_view != NULL) mail_index_view_close(&t->latest_view); mail_index_view_close(&t->view); i_free(t); } uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t) { const struct mail_index_header *head_hdr, *hdr; unsigned int offset; uint32_t next_uid; head_hdr = &t->view->index->map->hdr; hdr = &t->view->map->hdr; next_uid = t->reset || head_hdr->uid_validity != hdr->uid_validity ? 1 : hdr->next_uid; if (array_is_created(&t->appends) && t->highest_append_uid != 0) { /* get next_uid from appends if they have UIDs. it's possible that some appends have too low UIDs, they'll be caught later. */ if (next_uid <= t->highest_append_uid) next_uid = t->highest_append_uid + 1; } /* see if it's been updated in pre/post header changes */ offset = offsetof(struct mail_index_header, next_uid); if (t->post_hdr_mask[offset] != 0) { hdr = (const void *)t->post_hdr_change; if (hdr->next_uid > next_uid) next_uid = hdr->next_uid; } if (t->pre_hdr_mask[offset] != 0) { hdr = (const void *)t->pre_hdr_change; if (hdr->next_uid > next_uid) next_uid = hdr->next_uid; } return next_uid; } void mail_index_transaction_lookup_latest_keywords(struct mail_index_transaction *t, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keywords) { uint32_t uid, latest_seq; /* seq points to the transaction's primary view */ mail_index_lookup_uid(t->view, seq, &uid); /* get the latest keywords from the updated index, or fallback to the primary view if the message is already expunged */ if (t->latest_view == NULL) { mail_index_refresh(t->view->index); t->latest_view = mail_index_view_open(t->view->index); } if (mail_index_lookup_seq(t->latest_view, uid, &latest_seq)) mail_index_lookup_keywords(t->latest_view, latest_seq, keywords); else mail_index_lookup_keywords(t->view, seq, keywords); } static int mail_transaction_log_file_refresh(struct mail_index_transaction *t, struct mail_transaction_log_append_ctx *ctx) { struct mail_transaction_log_file *file; if (t->reset) { /* Reset the whole index, preserving only indexid. Begin by rotating the log. We don't care if we skip some non-synced transactions. */ if (mail_transaction_log_rotate(t->view->index->log, TRUE) < 0) return -1; if (!MAIL_INDEX_TRANSACTION_HAS_CHANGES(t)) { /* we only wanted to reset */ return 0; } } file = t->view->index->log->head; /* make sure we have everything mapped */ if (mail_index_map(t->view->index, MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0) return -1; i_assert(file->sync_offset >= file->buffer_offset); ctx->new_highest_modseq = file->sync_highest_modseq; return 1; } static int mail_index_transaction_commit_real(struct mail_index_transaction *t, uoff_t *commit_size_r) { struct mail_transaction_log *log = t->view->index->log; struct mail_transaction_log_append_ctx *ctx; enum mail_transaction_type trans_flags = 0; uint32_t log_seq1, log_seq2; uoff_t log_offset1, log_offset2; int ret; if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0) trans_flags |= MAIL_TRANSACTION_EXTERNAL; if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_SYNC) != 0) trans_flags |= MAIL_TRANSACTION_SYNC; if (mail_transaction_log_append_begin(log->index, trans_flags, &ctx) < 0) return -1; ret = mail_transaction_log_file_refresh(t, ctx); #ifdef DEBUG uint64_t expected_highest_modseq = mail_index_transaction_get_highest_modseq(t); #endif if (ret > 0) T_BEGIN { mail_index_transaction_finish(t); mail_index_transaction_export(t, ctx); } T_END; mail_transaction_log_get_head(log, &log_seq1, &log_offset1); if (mail_transaction_log_append_commit(&ctx) < 0 || ret < 0) return -1; mail_transaction_log_get_head(log, &log_seq2, &log_offset2); i_assert(log_seq1 == log_seq2); #ifdef DEBUG i_assert(expected_highest_modseq == log->head->sync_highest_modseq); #endif if (t->reset) { /* get rid of the old index. it might just confuse readers, especially if it's broken. */ i_unlink_if_exists(log->index->filepath); } *commit_size_r = log_offset2 - log_offset1; if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_HIDE) != 0 && log_offset1 != log_offset2) { /* mark the area covered by this transaction hidden */ mail_index_view_add_hidden_transaction(t->view, log_seq1, log_offset1, log_offset2 - log_offset1); } return 0; } static int mail_index_transaction_commit_v(struct mail_index_transaction *t, struct mail_index_transaction_commit_result *result_r) { struct mail_index *index = t->view->index; bool changed; int ret; i_assert(t->first_new_seq > mail_index_view_get_messages_count(t->view)); changed = MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) || t->reset; ret = !changed ? 0 : mail_index_transaction_commit_real(t, &result_r->commit_size); mail_transaction_log_get_head(index->log, &result_r->log_file_seq, &result_r->log_file_offset); if (ret == 0 && !index->syncing && changed) { /* if we're committing a normal transaction, we want to have those changes in the index mapping immediately. this is especially important when committing cache offset updates. however if we're syncing the index now, the mapping must be done later as MAIL_INDEX_SYNC_HANDLER_FILE so that expunge handlers get run for the newly expunged messages (and sync handlers that require HANDLER_FILE as well). */ index->sync_commit_result = result_r; mail_index_refresh(index); index->sync_commit_result = NULL; } mail_index_transaction_unref(&t); return ret; } static void mail_index_transaction_rollback_v(struct mail_index_transaction *t) { mail_index_transaction_unref(&t); } int mail_index_transaction_commit(struct mail_index_transaction **t) { struct mail_index_transaction_commit_result result; return mail_index_transaction_commit_full(t, &result); } int mail_index_transaction_commit_full(struct mail_index_transaction **_t, struct mail_index_transaction_commit_result *result_r) { struct mail_index_transaction *t = *_t; struct mail_index *index = t->view->index; bool index_undeleted = t->index_undeleted; if (mail_index_view_is_inconsistent(t->view)) { mail_index_transaction_rollback(_t); return -1; } if (!index_undeleted && !t->commit_deleted_index) { if (t->view->index->index_deleted || (t->view->index->index_delete_requested && !t->view->index->syncing)) { /* no further changes allowed */ mail_index_transaction_rollback(_t); return -1; } } *_t = NULL; i_zero(result_r); if (t->v.commit(t, result_r) < 0) return -1; if (index_undeleted) { index->index_deleted = FALSE; index->index_delete_requested = FALSE; } return 0; } void mail_index_transaction_rollback(struct mail_index_transaction **_t) { struct mail_index_transaction *t = *_t; *_t = NULL; t->v.rollback(t); } static struct mail_index_transaction_vfuncs trans_vfuncs = { mail_index_transaction_reset_v, mail_index_transaction_commit_v, mail_index_transaction_rollback_v }; struct mail_index_transaction * mail_index_transaction_begin(struct mail_index_view *view, enum mail_index_transaction_flags flags) { struct mail_index_transaction *t; /* don't allow syncing view while there's ongoing transactions */ mail_index_view_transaction_ref(view); mail_index_view_ref(view); t = i_new(struct mail_index_transaction, 1); t->refcount = 1; t->v = trans_vfuncs; t->view = view; t->flags = flags; if (view->syncing) { /* transaction view cannot work if new records are being added in two places. make sure it doesn't happen. */ t->no_appends = TRUE; t->first_new_seq = (uint32_t)-1; } else { t->first_new_seq = mail_index_view_get_messages_count(t->view) + 1; } i_array_init(&t->module_contexts, I_MIN(5, mail_index_module_register.id)); DLLIST_PREPEND(&view->transactions_list, t); if (array_is_created(&hook_mail_index_transaction_created)) { struct hook_build_context *ctx = hook_build_init((void *)&t->v, sizeof(t->v)); hook_mail_index_transaction_created_t *const *ptr; array_foreach(&hook_mail_index_transaction_created, ptr) { (*ptr)(t); hook_build_update(ctx, t->vlast); } t->vlast = NULL; hook_build_deinit(&ctx); } return t; } dovecot-2.2.33.2/src/lib-index/mail-index-sync.c0000644000175000017500000007134613165463624016161 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-index-transaction-private.h" #include "mail-transaction-log-private.h" #include "mail-cache-private.h" #include struct mail_index_sync_ctx { struct mail_index *index; struct mail_index_view *view; struct mail_index_transaction *sync_trans, *ext_trans; struct mail_index_transaction_commit_result *sync_commit_result; enum mail_index_sync_flags flags; char *reason; const struct mail_transaction_header *hdr; const void *data; ARRAY(struct mail_index_sync_list) sync_list; uint32_t next_uid; unsigned int no_warning:1; unsigned int seen_nonexternal_transactions:1; unsigned int fully_synced:1; }; static void mail_index_sync_add_expunge(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_expunge *e = ctx->data; size_t i, size = ctx->hdr->size / sizeof(*e); uint32_t uid; for (i = 0; i < size; i++) { for (uid = e[i].uid1; uid <= e[i].uid2; uid++) mail_index_expunge(ctx->sync_trans, uid); } } static void mail_index_sync_add_expunge_guid(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_expunge_guid *e = ctx->data; size_t i, size = ctx->hdr->size / sizeof(*e); for (i = 0; i < size; i++) { mail_index_expunge_guid(ctx->sync_trans, e[i].uid, e[i].guid_128); } } static void mail_index_sync_add_flag_update(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_flag_update *u = ctx->data; size_t i, size = ctx->hdr->size / sizeof(*u); for (i = 0; i < size; i++) { if (u[i].add_flags != 0) { mail_index_update_flags_range(ctx->sync_trans, u[i].uid1, u[i].uid2, MODIFY_ADD, u[i].add_flags); } if (u[i].remove_flags != 0) { mail_index_update_flags_range(ctx->sync_trans, u[i].uid1, u[i].uid2, MODIFY_REMOVE, u[i].remove_flags); } } } static void mail_index_sync_add_keyword_update(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_keyword_update *u = ctx->data; const char *keyword_names[2]; struct mail_keywords *keywords; const uint32_t *uids; uint32_t uid; size_t uidset_offset, i, size; i_assert(u->name_size > 0); uidset_offset = sizeof(*u) + u->name_size; if ((uidset_offset % 4) != 0) uidset_offset += 4 - (uidset_offset % 4); uids = CONST_PTR_OFFSET(u, uidset_offset); keyword_names[0] = t_strndup(u + 1, u->name_size); keyword_names[1] = NULL; keywords = mail_index_keywords_create(ctx->index, keyword_names); size = (ctx->hdr->size - uidset_offset) / sizeof(uint32_t); for (i = 0; i < size; i += 2) { /* FIXME: mail_index_update_keywords_range() */ for (uid = uids[i]; uid <= uids[i+1]; uid++) { mail_index_update_keywords(ctx->sync_trans, uid, u->modify_type, keywords); } } mail_index_keywords_unref(&keywords); } static void mail_index_sync_add_keyword_reset(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_keyword_reset *u = ctx->data; size_t i, size = ctx->hdr->size / sizeof(*u); struct mail_keywords *keywords; uint32_t uid; keywords = mail_index_keywords_create(ctx->index, NULL); for (i = 0; i < size; i++) { for (uid = u[i].uid1; uid <= u[i].uid2; uid++) { mail_index_update_keywords(ctx->sync_trans, uid, MODIFY_REPLACE, keywords); } } mail_index_keywords_unref(&keywords); } static bool mail_index_sync_add_transaction(struct mail_index_sync_ctx *ctx) { switch (ctx->hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: mail_index_sync_add_expunge(ctx); break; case MAIL_TRANSACTION_EXPUNGE_GUID: mail_index_sync_add_expunge_guid(ctx); break; case MAIL_TRANSACTION_FLAG_UPDATE: mail_index_sync_add_flag_update(ctx); break; case MAIL_TRANSACTION_KEYWORD_UPDATE: mail_index_sync_add_keyword_update(ctx); break; case MAIL_TRANSACTION_KEYWORD_RESET: mail_index_sync_add_keyword_reset(ctx); break; default: return FALSE; } return TRUE; } static void mail_index_sync_add_dirty_updates(struct mail_index_sync_ctx *ctx) { struct mail_transaction_flag_update update; const struct mail_index_record *rec; uint32_t seq, messages_count; i_zero(&update); messages_count = mail_index_view_get_messages_count(ctx->view); for (seq = 1; seq <= messages_count; seq++) { rec = mail_index_lookup(ctx->view, seq); if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0) continue; mail_index_update_flags(ctx->sync_trans, rec->uid, MODIFY_REPLACE, rec->flags); } } static int mail_index_sync_read_and_sort(struct mail_index_sync_ctx *ctx) { struct mail_index_transaction *sync_trans = ctx->sync_trans; struct mail_index_sync_list *synclist; const struct mail_index_transaction_keyword_update *keyword_updates; unsigned int i, keyword_count; int ret; if ((ctx->view->map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 && (ctx->flags & MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY) != 0 && (ctx->view->index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) == 0) { /* show dirty flags as flag updates */ mail_index_sync_add_dirty_updates(ctx); } /* read all transactions from log into a transaction in memory. skip the external ones, they're already synced to mailbox and included in our view */ while ((ret = mail_transaction_log_view_next(ctx->view->log_view, &ctx->hdr, &ctx->data)) > 0) { if ((ctx->hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0) continue; T_BEGIN { if (mail_index_sync_add_transaction(ctx)) { /* update tail_offset if needed */ ctx->seen_nonexternal_transactions = TRUE; } else { /* this is an internal change. we don't necessarily need to update tail_offset, so avoid the extra write caused by it. */ } } T_END; } /* create an array containing all expunge, flag and keyword update arrays so we can easily go through all of the changes. */ keyword_count = !array_is_created(&sync_trans->keyword_updates) ? 0 : array_count(&sync_trans->keyword_updates); i_array_init(&ctx->sync_list, keyword_count + 2); if (array_is_created(&sync_trans->expunges)) { mail_index_transaction_sort_expunges(sync_trans); synclist = array_append_space(&ctx->sync_list); synclist->array = (void *)&sync_trans->expunges; } if (array_is_created(&sync_trans->updates)) { synclist = array_append_space(&ctx->sync_list); synclist->array = (void *)&sync_trans->updates; } keyword_updates = keyword_count == 0 ? NULL : array_idx(&sync_trans->keyword_updates, 0); for (i = 0; i < keyword_count; i++) { if (array_is_created(&keyword_updates[i].add_seq)) { synclist = array_append_space(&ctx->sync_list); synclist->array = (const void *)&keyword_updates[i].add_seq; synclist->keyword_idx = i; } if (array_is_created(&keyword_updates[i].remove_seq)) { synclist = array_append_space(&ctx->sync_list); synclist->array = (const void *)&keyword_updates[i].remove_seq; synclist->keyword_idx = i; synclist->keyword_remove = TRUE; } } return ret; } static bool mail_index_need_sync(struct mail_index *index, enum mail_index_sync_flags flags, uint32_t log_file_seq, uoff_t log_file_offset) { const struct mail_index_header *hdr = &index->map->hdr; if ((flags & MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES) == 0) return TRUE; /* sync only if there's something to do */ if (hdr->first_recent_uid < hdr->next_uid && (flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0) return TRUE; if ((hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 && (flags & MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY) != 0 && (index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) == 0) return TRUE; if (log_file_seq == (uint32_t)-1) { /* we want to sync up to transaction log's head */ mail_transaction_log_get_head(index->log, &log_file_seq, &log_file_offset); } if ((hdr->log_file_tail_offset < log_file_offset && hdr->log_file_seq == log_file_seq) || hdr->log_file_seq < log_file_seq) return TRUE; if (index->need_recreate) return TRUE; /* already synced */ return mail_cache_need_compress(index->cache); } static int mail_index_sync_set_log_view(struct mail_index_view *view, uint32_t start_file_seq, uoff_t start_file_offset) { uint32_t log_seq; uoff_t log_offset; const char *reason; bool reset; int ret; mail_transaction_log_get_head(view->index->log, &log_seq, &log_offset); ret = mail_transaction_log_view_set(view->log_view, start_file_seq, start_file_offset, log_seq, log_offset, &reset, &reason); if (ret < 0) return -1; if (ret == 0) { /* either corrupted or the file was deleted for some reason. either way, we can't go forward */ mail_index_set_error(view->index, "Unexpected transaction log desync with index %s: %s", view->index->filepath, reason); return 0; } return 1; } int mail_index_sync_begin(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, enum mail_index_sync_flags flags) { int ret; ret = mail_index_sync_begin_to(index, ctx_r, view_r, trans_r, (uint32_t)-1, (uoff_t)-1, flags); i_assert(ret != 0 || (flags & MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES) != 0); return ret; } static int mail_index_sync_begin_init(struct mail_index *index, enum mail_index_sync_flags flags, uint32_t log_file_seq, uoff_t log_file_offset) { const struct mail_index_header *hdr; uint32_t seq; uoff_t offset; bool locked = FALSE; int ret; /* if we require changes, don't lock transaction log yet. first check if there's anything to sync. */ if ((flags & MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES) == 0) { if (mail_transaction_log_sync_lock(index->log, "syncing", &seq, &offset) < 0) return -1; locked = TRUE; } /* The view must contain what we expect the mailbox to look like currently. That allows the backend to update external flag changes (etc.) if the view doesn't match the mailbox. We'll update the view to contain everything that exist in the transaction log except for expunges. They're synced in mail_index_sync_commit(). */ if ((ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD)) <= 0) { if (ret == 0) { if (locked) mail_transaction_log_sync_unlock(index->log, "sync init failure"); return -1; } /* let's try again */ if (mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0) { if (locked) mail_transaction_log_sync_unlock(index->log, "sync init failure"); return -1; } } if (!mail_index_need_sync(index, flags, log_file_seq, log_file_offset) && !index->index_deleted && !index->need_recreate) { if (locked) mail_transaction_log_sync_unlock(index->log, "syncing determined unnecessary"); return 0; } if (!locked) { /* it looks like we have something to sync. lock the file and check again. */ flags &= ~MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; return mail_index_sync_begin_init(index, flags, log_file_seq, log_file_offset); } if (index->index_deleted && (flags & MAIL_INDEX_SYNC_FLAG_DELETING_INDEX) == 0) { /* index is already deleted. we can't sync. */ if (locked) mail_transaction_log_sync_unlock(index->log, "syncing detected deleted index"); return -1; } hdr = &index->map->hdr; if (hdr->log_file_tail_offset > hdr->log_file_head_offset || hdr->log_file_seq > seq || (hdr->log_file_seq == seq && hdr->log_file_tail_offset > offset)) { /* broken sync positions. fix them. */ mail_index_set_error(index, "broken sync positions in index file %s", index->filepath); mail_index_fsck_locked(index); } return 1; } static int mail_index_sync_begin_to2(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, uint32_t log_file_seq, uoff_t log_file_offset, enum mail_index_sync_flags flags, bool *retry_r) { const struct mail_index_header *hdr; struct mail_index_sync_ctx *ctx; struct mail_index_view *sync_view; enum mail_index_transaction_flags trans_flags; int ret; i_assert(!index->syncing); *retry_r = FALSE; if (index->map != NULL && (index->map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) { /* index is corrupted and need to be reopened */ return -1; } if (log_file_seq != (uint32_t)-1) flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; ret = mail_index_sync_begin_init(index, flags, log_file_seq, log_file_offset); if (ret <= 0) return ret; hdr = &index->map->hdr; ctx = i_new(struct mail_index_sync_ctx, 1); ctx->index = index; ctx->flags = flags; ctx->view = mail_index_view_open(index); sync_view = mail_index_dummy_view_open(index); ctx->sync_trans = mail_index_transaction_begin(sync_view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_view_close(&sync_view); /* set before any rollbacks are called */ index->syncing = TRUE; /* we wish to see all the changes from last mailbox sync position to the end of the transaction log */ ret = mail_index_sync_set_log_view(ctx->view, hdr->log_file_seq, hdr->log_file_tail_offset); if (ret < 0) { mail_index_sync_rollback(&ctx); return -1; } if (ret == 0) { /* if a log file is missing, there's nothing we can do except to skip over it. fix the problem with fsck and try again. */ mail_index_fsck_locked(index); mail_index_sync_rollback(&ctx); *retry_r = TRUE; return 0; } /* we need to have all the transactions sorted to optimize caller's mailbox access patterns */ if (mail_index_sync_read_and_sort(ctx) < 0) { mail_index_sync_rollback(&ctx); return -1; } ctx->view->index_sync_view = TRUE; /* create the transaction after the view has been updated with external transactions and marked as sync view */ trans_flags = MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; if ((ctx->flags & MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES) != 0) trans_flags |= MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES; if ((ctx->flags & MAIL_INDEX_SYNC_FLAG_FSYNC) != 0) trans_flags |= MAIL_INDEX_TRANSACTION_FLAG_FSYNC; ctx->ext_trans = mail_index_transaction_begin(ctx->view, trans_flags); ctx->ext_trans->sync_transaction = TRUE; ctx->ext_trans->commit_deleted_index = (flags & (MAIL_INDEX_SYNC_FLAG_DELETING_INDEX | MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX)) != 0; *ctx_r = ctx; *view_r = ctx->view; *trans_r = ctx->ext_trans; return 1; } int mail_index_sync_begin_to(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, uint32_t log_file_seq, uoff_t log_file_offset, enum mail_index_sync_flags flags) { bool retry; int ret; i_assert(index->open_count > 0); ret = mail_index_sync_begin_to2(index, ctx_r, view_r, trans_r, log_file_seq, log_file_offset, flags, &retry); if (retry) { ret = mail_index_sync_begin_to2(index, ctx_r, view_r, trans_r, log_file_seq, log_file_offset, flags, &retry); } return ret; } bool mail_index_sync_has_expunges(struct mail_index_sync_ctx *ctx) { return array_is_created(&ctx->sync_trans->expunges) && array_count(&ctx->sync_trans->expunges) > 0; } static bool mail_index_sync_view_have_any(struct mail_index_view *view, enum mail_index_sync_flags flags, bool expunges_only) { const struct mail_transaction_header *hdr; const void *data; uint32_t log_seq; uoff_t log_offset; const char *reason; bool reset; int ret; if (view->map->hdr.first_recent_uid < view->map->hdr.next_uid && (flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0) return TRUE; if ((view->map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 && (flags & MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY) != 0 && (view->index->flags & MAIL_INDEX_OPEN_FLAG_NO_DIRTY) == 0) return TRUE; mail_transaction_log_get_head(view->index->log, &log_seq, &log_offset); if (mail_transaction_log_view_set(view->log_view, view->map->hdr.log_file_seq, view->map->hdr.log_file_tail_offset, log_seq, log_offset, &reset, &reason) <= 0) { /* let the actual syncing handle the error */ return TRUE; } while ((ret = mail_transaction_log_view_next(view->log_view, &hdr, &data)) > 0) { if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0) continue; switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: case MAIL_TRANSACTION_EXPUNGE_GUID: return TRUE; case MAIL_TRANSACTION_EXT_REC_UPDATE: case MAIL_TRANSACTION_EXT_ATOMIC_INC: /* extension record updates aren't exactly needed to be synced, but cache syncing relies on tail offsets being updated. */ case MAIL_TRANSACTION_FLAG_UPDATE: case MAIL_TRANSACTION_KEYWORD_UPDATE: case MAIL_TRANSACTION_KEYWORD_RESET: case MAIL_TRANSACTION_INDEX_DELETED: case MAIL_TRANSACTION_INDEX_UNDELETED: if (!expunges_only) return TRUE; break; default: break; } } return ret < 0; } bool mail_index_sync_have_any(struct mail_index *index, enum mail_index_sync_flags flags) { struct mail_index_view *view; bool ret; view = mail_index_view_open(index); ret = mail_index_sync_view_have_any(view, flags, FALSE); mail_index_view_close(&view); return ret; } bool mail_index_sync_have_any_expunges(struct mail_index *index) { struct mail_index_view *view; bool ret; view = mail_index_view_open(index); ret = mail_index_sync_view_have_any(view, 0, TRUE); mail_index_view_close(&view); return ret; } void mail_index_sync_get_offsets(struct mail_index_sync_ctx *ctx, uint32_t *seq1_r, uoff_t *offset1_r, uint32_t *seq2_r, uoff_t *offset2_r) { *seq1_r = ctx->view->map->hdr.log_file_seq; *offset1_r = ctx->view->map->hdr.log_file_tail_offset != 0 ? ctx->view->map->hdr.log_file_tail_offset : ctx->view->index->log->head->hdr.hdr_size; mail_transaction_log_get_head(ctx->view->index->log, seq2_r, offset2_r); } static void mail_index_sync_get_expunge(struct mail_index_sync_rec *rec, const struct mail_transaction_expunge_guid *exp) { rec->type = MAIL_INDEX_SYNC_TYPE_EXPUNGE; rec->uid1 = exp->uid; rec->uid2 = exp->uid; memcpy(rec->guid_128, exp->guid_128, sizeof(rec->guid_128)); } static void mail_index_sync_get_update(struct mail_index_sync_rec *rec, const struct mail_index_flag_update *update) { rec->type = MAIL_INDEX_SYNC_TYPE_FLAGS; rec->uid1 = update->uid1; rec->uid2 = update->uid2; rec->add_flags = update->add_flags; rec->remove_flags = update->remove_flags; } static void mail_index_sync_get_keyword_update(struct mail_index_sync_rec *rec, const struct uid_range *range, struct mail_index_sync_list *sync_list) { rec->type = !sync_list->keyword_remove ? MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD : MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE; rec->uid1 = range->uid1; rec->uid2 = range->uid2; rec->keyword_idx = sync_list->keyword_idx; } bool mail_index_sync_next(struct mail_index_sync_ctx *ctx, struct mail_index_sync_rec *sync_rec) { struct mail_index_transaction *sync_trans = ctx->sync_trans; struct mail_index_sync_list *sync_list; const struct uid_range *uid_range = NULL; unsigned int i, count, next_i; uint32_t next_found_uid; next_i = UINT_MAX; next_found_uid = (uint32_t)-1; /* FIXME: replace with a priority queue so we don't have to go through the whole list constantly. and remember to make sure that keyword resets are sent before adds! */ /* FIXME: pretty ugly to do this for expunges, which isn't even a seq_range. */ sync_list = array_get_modifiable(&ctx->sync_list, &count); for (i = 0; i < count; i++) { if (!array_is_created(sync_list[i].array) || sync_list[i].idx == array_count(sync_list[i].array)) continue; uid_range = array_idx(sync_list[i].array, sync_list[i].idx); if (uid_range->uid1 == ctx->next_uid) { /* use this one. */ break; } if (uid_range->uid1 < next_found_uid) { next_i = i; next_found_uid = uid_range->uid1; } } if (i == count) { if (next_i == UINT_MAX) { /* nothing left in sync_list */ ctx->fully_synced = TRUE; return FALSE; } ctx->next_uid = next_found_uid; i = next_i; uid_range = array_idx(sync_list[i].array, sync_list[i].idx); } if (sync_list[i].array == (void *)&sync_trans->expunges) { mail_index_sync_get_expunge(sync_rec, (const struct mail_transaction_expunge_guid *)uid_range); } else if (sync_list[i].array == (void *)&sync_trans->updates) { mail_index_sync_get_update(sync_rec, (const struct mail_index_flag_update *)uid_range); } else { mail_index_sync_get_keyword_update(sync_rec, uid_range, &sync_list[i]); } sync_list[i].idx++; return TRUE; } bool mail_index_sync_have_more(struct mail_index_sync_ctx *ctx) { const struct mail_index_sync_list *sync_list; array_foreach(&ctx->sync_list, sync_list) { if (array_is_created(sync_list->array) && sync_list->idx != array_count(sync_list->array)) return TRUE; } return FALSE; } void mail_index_sync_set_commit_result(struct mail_index_sync_ctx *ctx, struct mail_index_transaction_commit_result *result) { ctx->sync_commit_result = result; } void mail_index_sync_reset(struct mail_index_sync_ctx *ctx) { struct mail_index_sync_list *sync_list; ctx->next_uid = 0; array_foreach_modifiable(&ctx->sync_list, sync_list) sync_list->idx = 0; } void mail_index_sync_no_warning(struct mail_index_sync_ctx *ctx) { ctx->no_warning = TRUE; } void mail_index_sync_set_reason(struct mail_index_sync_ctx *ctx, const char *reason) { i_free(ctx->reason); ctx->reason = i_strdup(reason); } static void mail_index_sync_end(struct mail_index_sync_ctx **_ctx) { struct mail_index_sync_ctx *ctx = *_ctx; const char *lock_reason; i_assert(ctx->index->syncing); *_ctx = NULL; ctx->index->syncing = FALSE; if (ctx->no_warning) lock_reason = NULL; else if (ctx->reason != NULL) lock_reason = ctx->reason; else lock_reason = "Mailbox was synchronized"; mail_transaction_log_sync_unlock(ctx->index->log, lock_reason); mail_index_view_close(&ctx->view); mail_index_transaction_rollback(&ctx->sync_trans); if (array_is_created(&ctx->sync_list)) array_free(&ctx->sync_list); i_free(ctx->reason); i_free(ctx); } static void mail_index_sync_update_mailbox_offset(struct mail_index_sync_ctx *ctx) { const struct mail_index_header *hdr = &ctx->index->map->hdr; uint32_t seq; uoff_t offset; if (!ctx->fully_synced) { /* Everything wasn't synced. This usually means that syncing was used for locking and nothing was synced. Don't update tail offset. */ mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &seq, &offset); return; } /* synced everything, but we might also have committed new transactions. include them also here. */ mail_transaction_log_get_head(ctx->index->log, &seq, &offset); mail_transaction_log_set_mailbox_sync_pos(ctx->index->log, seq, offset); /* If tail offset has changed, make sure it gets written to transaction log. do this only if we're required to make changes. avoid writing a new tail offset if all the transactions were external, because that wouldn't change effective the tail offset. except e.g. mdbox map requires this to happen, so do it optionally. */ if ((hdr->log_file_seq != seq || hdr->log_file_tail_offset < offset) && (ctx->seen_nonexternal_transactions || (ctx->flags & MAIL_INDEX_SYNC_FLAG_UPDATE_TAIL_OFFSET) != 0)) { ctx->ext_trans->log_updates = TRUE; ctx->ext_trans->tail_offset_changed = TRUE; } } static bool mail_index_sync_want_index_write(struct mail_index *index) { uint32_t log_diff; if (index->last_read_log_file_seq != 0 && index->last_read_log_file_seq != index->map->hdr.log_file_seq) { /* dovecot.index points to an old .log file. we were supposed to rewrite the dovecot.index when rotating the log, so we shouldn't usually get here. */ return TRUE; } log_diff = index->map->hdr.log_file_tail_offset - index->last_read_log_file_tail_offset; if (log_diff > MAIL_INDEX_MAX_WRITE_BYTES || (index->index_min_write && log_diff > MAIL_INDEX_MIN_WRITE_BYTES)) return TRUE; if (index->need_recreate) return TRUE; return FALSE; } int mail_index_sync_commit(struct mail_index_sync_ctx **_ctx) { struct mail_index_sync_ctx *ctx = *_ctx; struct mail_index *index = ctx->index; struct mail_cache_compress_lock *cache_lock = NULL; uint32_t next_uid; bool want_rotate, index_undeleted, delete_index; int ret = 0, ret2; index_undeleted = ctx->ext_trans->index_undeleted; delete_index = index->index_delete_requested && !index_undeleted && (ctx->flags & (MAIL_INDEX_SYNC_FLAG_DELETING_INDEX | MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX)) != 0; if (delete_index) { /* finish this sync by marking the index deleted */ mail_index_set_deleted(ctx->ext_trans); } else if (index->index_deleted && !index_undeleted && (ctx->flags & MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX) == 0) { /* another process just marked the index deleted. finish the sync, but return error. */ ret = -1; } mail_index_sync_update_mailbox_offset(ctx); if (mail_cache_need_compress(index->cache)) { /* if cache compression fails, we don't really care. the cache offsets are updated only if the compression was successful. */ (void)mail_cache_compress(index->cache, ctx->ext_trans, &cache_lock); } if ((ctx->flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0) { next_uid = mail_index_transaction_get_next_uid(ctx->ext_trans); if (index->map->hdr.first_recent_uid < next_uid) { mail_index_update_header(ctx->ext_trans, offsetof(struct mail_index_header, first_recent_uid), &next_uid, sizeof(next_uid), FALSE); } } if (index->pending_log2_rotate_time != 0) { uint32_t log2_rotate_time = index->pending_log2_rotate_time; mail_index_update_header(ctx->ext_trans, offsetof(struct mail_index_header, log2_rotate_time), &log2_rotate_time, sizeof(log2_rotate_time), TRUE); index->pending_log2_rotate_time = 0; } ret2 = mail_index_transaction_commit(&ctx->ext_trans); if (cache_lock != NULL) mail_cache_compress_unlock(&cache_lock); if (ret2 < 0) { mail_index_sync_end(&ctx); return -1; } if (delete_index) index->index_deleted = TRUE; else if (index_undeleted) { index->index_deleted = FALSE; index->index_delete_requested = FALSE; } /* refresh the mapping with newly committed external transactions and the synced expunges. sync using file handler here so that the expunge handlers get called. */ index->sync_commit_result = ctx->sync_commit_result; if (mail_index_map(ctx->index, MAIL_INDEX_SYNC_HANDLER_FILE) <= 0) ret = -1; index->sync_commit_result = NULL; want_rotate = mail_transaction_log_want_rotate(index->log); if (ret == 0 && (want_rotate || mail_index_sync_want_index_write(index))) { index->need_recreate = FALSE; index->index_min_write = FALSE; mail_index_write(index, want_rotate); } mail_index_sync_end(_ctx); return ret; } void mail_index_sync_rollback(struct mail_index_sync_ctx **ctx) { if ((*ctx)->ext_trans != NULL) mail_index_transaction_rollback(&(*ctx)->ext_trans); mail_index_sync_end(ctx); } void mail_index_sync_flags_apply(const struct mail_index_sync_rec *sync_rec, uint8_t *flags) { i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS); *flags = (*flags & ~sync_rec->remove_flags) | sync_rec->add_flags; } bool mail_index_sync_keywords_apply(const struct mail_index_sync_rec *sync_rec, ARRAY_TYPE(keyword_indexes) *keywords) { const unsigned int *keyword_indexes; unsigned int idx = sync_rec->keyword_idx; unsigned int i, count; keyword_indexes = array_get(keywords, &count); switch (sync_rec->type) { case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: for (i = 0; i < count; i++) { if (keyword_indexes[i] == idx) return FALSE; } array_append(keywords, &idx, 1); return TRUE; case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: for (i = 0; i < count; i++) { if (keyword_indexes[i] == idx) { array_delete(keywords, i, 1); return TRUE; } } return FALSE; default: i_unreached(); return FALSE; } } void mail_index_sync_set_corrupted(struct mail_index_sync_map_ctx *ctx, const char *fmt, ...) { va_list va; uint32_t seq; uoff_t offset; ctx->errors = TRUE; /* make sure we don't get to this same error again by updating the dovecot.index */ ctx->view->index->need_recreate = TRUE; mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &seq, &offset); if (seq < ctx->view->index->fsck_log_head_file_seq || (seq == ctx->view->index->fsck_log_head_file_seq && offset < ctx->view->index->fsck_log_head_file_offset)) { /* be silent */ return; } va_start(va, fmt); T_BEGIN { mail_index_set_error(ctx->view->index, "Log synchronization error at " "seq=%u,offset=%"PRIuUOFF_T" for %s: %s", seq, offset, ctx->view->index->filepath, t_strdup_vprintf(fmt, va)); } T_END; va_end(va); } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-view.c0000644000175000017500000003643213165463624020477 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "seq-range-array.h" #include "mail-index-private.h" #include "mail-index-view-private.h" #include "mail-index-transaction-private.h" struct mail_index_view_transaction { struct mail_index_view view; struct mail_index_view_vfuncs *super; struct mail_index_transaction *t; struct mail_index_map *lookup_map; struct mail_index_header hdr; buffer_t *lookup_return_data; uint32_t lookup_prev_seq; unsigned int record_size; unsigned int recs_count; void *recs; ARRAY(void *) all_recs; }; static void tview_close(struct mail_index_view *view) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; struct mail_index_transaction *t = tview->t; void **recs; unsigned int i, count; if (tview->lookup_map != NULL) mail_index_unmap(&tview->lookup_map); if (tview->lookup_return_data != NULL) buffer_free(&tview->lookup_return_data); if (array_is_created(&tview->all_recs)) { recs = array_get_modifiable(&tview->all_recs, &count); for (i = 0; i < count; i++) i_free(recs[i]); array_free(&tview->all_recs); } tview->super->close(view); mail_index_transaction_unref(&t); } static uint32_t tview_get_message_count(struct mail_index_view *view) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; return view->map->hdr.messages_count + (tview->t->last_new_seq == 0 ? 0 : tview->t->last_new_seq - tview->t->first_new_seq + 1); } static const struct mail_index_header * tview_get_header(struct mail_index_view *view) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; const struct mail_index_header *hdr; uint32_t next_uid; /* FIXME: header counters may not be correct */ hdr = tview->super->get_header(view); next_uid = mail_index_transaction_get_next_uid(tview->t); if (next_uid != hdr->next_uid) { tview->hdr = *hdr; tview->hdr.next_uid = next_uid; hdr = &tview->hdr; } return hdr; } static const struct mail_index_record * tview_apply_flag_updates(struct mail_index_view_transaction *tview, struct mail_index_map *map, const struct mail_index_record *rec, uint32_t seq) { struct mail_index_transaction *t = tview->t; const struct mail_index_flag_update *updates; struct mail_index_record *trec; unsigned int idx, count; /* see if there are any flag updates */ if (seq < t->min_flagupdate_seq || seq > t->max_flagupdate_seq || !array_is_created(&t->updates)) return rec; updates = array_get(&t->updates, &count); idx = mail_index_transaction_get_flag_update_pos(t, 0, count, seq); if (seq < updates[idx].uid1 || seq > updates[idx].uid2) return rec; /* yes, we have flag updates. since we can't modify rec directly and we want to be able to handle multiple mail_index_lookup() calls without the second one overriding the first one's data, we'll create a records array and return data from there. it's also possible that the record size increases, so we potentially have to create multiple arrays. they all get eventually freed when the view gets freed. */ if (map->hdr.record_size > tview->record_size) { if (!array_is_created(&tview->all_recs)) i_array_init(&tview->all_recs, 4); tview->recs_count = t->first_new_seq; tview->record_size = I_MAX(map->hdr.record_size, tview->view.map->hdr.record_size); tview->recs = i_malloc(MALLOC_MULTIPLY(tview->record_size, tview->recs_count)); array_append(&tview->all_recs, &tview->recs, 1); } i_assert(tview->recs_count == t->first_new_seq); i_assert(seq > 0 && seq <= tview->recs_count); trec = PTR_OFFSET(tview->recs, (seq-1) * tview->record_size); memcpy(trec, rec, map->hdr.record_size); trec->flags |= updates[idx].add_flags; trec->flags &= ~updates[idx].remove_flags; return trec; } static const struct mail_index_record * tview_lookup_full(struct mail_index_view *view, uint32_t seq, struct mail_index_map **map_r, bool *expunged_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; const struct mail_index_record *rec; if (seq >= tview->t->first_new_seq) { /* FIXME: is this right to return index map..? it's not there yet. */ *map_r = view->index->map; if (expunged_r != NULL) *expunged_r = FALSE; return mail_index_transaction_lookup(tview->t, seq); } rec = tview->super->lookup_full(view, seq, map_r, expunged_r); rec = tview_apply_flag_updates(tview, *map_r, rec, seq); if (expunged_r != NULL && mail_index_transaction_is_expunged(tview->t, seq)) *expunged_r = TRUE; return rec; } static void tview_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; if (seq >= tview->t->first_new_seq) *uid_r = mail_index_transaction_lookup(tview->t, seq)->uid; else tview->super->lookup_uid(view, seq, uid_r); } static void tview_lookup_seq_range(struct mail_index_view *view, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; const struct mail_index_record *rec; uint32_t seq; if (!tview->t->reset) { tview->super->lookup_seq_range(view, first_uid, last_uid, first_seq_r, last_seq_r); } else { /* index is being reset. we never want to return old sequences. */ *first_seq_r = *last_seq_r = 0; } if (tview->t->last_new_seq == 0) { /* no new messages, the results are final. */ return; } rec = mail_index_transaction_lookup(tview->t, tview->t->first_new_seq); if (rec->uid == 0) { /* new messages don't have UIDs */ return; } if (last_uid < rec->uid) { /* all wanted messages were existing */ return; } /* at least some of the wanted messages are newly created */ if (*first_seq_r == 0) { seq = tview->t->first_new_seq; for (; seq <= tview->t->last_new_seq; seq++) { rec = mail_index_transaction_lookup(tview->t, seq); if (first_uid <= rec->uid) break; } if (seq > tview->t->last_new_seq || rec->uid > last_uid) { /* no messages in range */ return; } *first_seq_r = seq; if (rec->uid == last_uid) { /* one seq in range */ *last_seq_r = seq; return; } } seq = tview->t->last_new_seq; for (; seq >= tview->t->first_new_seq; seq--) { rec = mail_index_transaction_lookup(tview->t, seq); if (rec->uid <= last_uid) { *last_seq_r = seq; break; } } i_assert(seq >= tview->t->first_new_seq); } static void tview_lookup_first(struct mail_index_view *view, enum mail_flags flags, uint8_t flags_mask, uint32_t *seq_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; const struct mail_index_record *rec; unsigned int append_count; uint32_t seq, message_count; if (!tview->t->reset) { tview->super->lookup_first(view, flags, flags_mask, seq_r); if (*seq_r != 0) return; } else { *seq_r = 0; } rec = array_get(&tview->t->appends, &append_count); seq = tview->t->first_new_seq; message_count = tview->t->last_new_seq; i_assert(append_count == message_count - seq + 1); for (; seq <= message_count; seq++, rec++) { if ((rec->flags & flags_mask) == (uint8_t)flags) { *seq_r = seq; break; } } } static void keyword_index_add(ARRAY_TYPE(keyword_indexes) *keywords, unsigned int idx) { const unsigned int *indexes; unsigned int i, count; indexes = array_get(keywords, &count); for (i = 0; i < count; i++) { if (indexes[i] == idx) return; } array_append(keywords, &idx, 1); } static void keyword_index_remove(ARRAY_TYPE(keyword_indexes) *keywords, unsigned int idx) { const unsigned int *indexes; unsigned int i, count; indexes = array_get(keywords, &count); for (i = 0; i < count; i++) { if (indexes[i] == idx) { array_delete(keywords, i, 1); break; } } } static void tview_lookup_keywords(struct mail_index_view *view, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; struct mail_index_transaction *t = tview->t; const struct mail_index_transaction_keyword_update *updates; unsigned int i, count; tview->super->lookup_keywords(view, seq, keyword_idx); if (seq < t->min_flagupdate_seq || seq > t->max_flagupdate_seq) { /* no keyword updates for this sequence */ return; } if (array_is_created(&t->keyword_updates)) updates = array_get(&t->keyword_updates, &count); else { updates = NULL; count = 0; } for (i = 0; i < count; i++) { if (array_is_created(&updates[i].add_seq) && seq_range_exists(&updates[i].add_seq, seq)) keyword_index_add(keyword_idx, i); else if (array_is_created(&updates[i].remove_seq) && seq_range_exists(&updates[i].remove_seq, seq)) keyword_index_remove(keyword_idx, i); } } static const void * tview_return_updated_ext(struct mail_index_view_transaction *tview, uint32_t seq, const void *data, uint32_t ext_id) { const struct mail_index_ext *ext; const struct mail_index_registered_ext *rext; const struct mail_transaction_ext_intro *intro; unsigned int record_align, record_size; uint32_t ext_idx; size_t pos; /* data begins with a 32bit sequence, followed by the actual extension data */ data = CONST_PTR_OFFSET(data, sizeof(uint32_t)); if (!mail_index_map_get_ext_idx(tview->lookup_map, ext_id, &ext_idx)) { /* we're adding the extension now. */ rext = array_idx(&tview->view.index->extensions, ext_id); record_align = rext->record_align; record_size = rext->record_size; } else { ext = array_idx(&tview->lookup_map->extensions, ext_idx); record_align = ext->record_align; record_size = ext->record_size; } /* see if the extension has been resized within this transaction */ if (array_is_created(&tview->t->ext_resizes) && ext_id < array_count(&tview->t->ext_resizes)) { intro = array_idx(&tview->t->ext_resizes, ext_id); if (intro[ext_id].name_size != 0) { record_align = intro->record_align; record_size = intro->record_size; } } if (record_align <= sizeof(uint32_t)) { /* data is 32bit aligned already */ return data; } else { /* assume we want 64bit alignment - copy the data to temporary buffer and return it */ if (tview->lookup_return_data == NULL) { tview->lookup_return_data = buffer_create_dynamic(default_pool, record_size + 64); } else if (seq != tview->lookup_prev_seq) { /* clear the buffer between lookups for different messages */ buffer_set_used_size(tview->lookup_return_data, 0); } tview->lookup_prev_seq = seq; pos = tview->lookup_return_data->used; buffer_append(tview->lookup_return_data, data, record_size); return CONST_PTR_OFFSET(tview->lookup_return_data->data, pos); } } static bool tview_is_ext_reset(struct mail_index_view_transaction *tview, uint32_t ext_id) { const struct mail_transaction_ext_reset *resets; unsigned int count; if (!array_is_created(&tview->t->ext_resets)) return FALSE; resets = array_get(&tview->t->ext_resets, &count); return ext_id < count && resets[ext_id].new_reset_id != 0; } static bool tview_lookup_ext_update(struct mail_index_view_transaction *tview, uint32_t seq, uint32_t ext_id, struct mail_index_map **map_r, const void **data_r) { const ARRAY_TYPE(seq_array) *ext_buf; const void *data; unsigned int idx; uint32_t map_ext_idx; ext_buf = array_idx(&tview->t->ext_rec_updates, ext_id); if (!array_is_created(ext_buf) || !mail_index_seq_array_lookup(ext_buf, seq, &idx)) return FALSE; if (tview->lookup_map == NULL) { tview->lookup_map = mail_index_map_clone(tview->view.index->map); } if (!mail_index_map_get_ext_idx(tview->lookup_map, ext_id, &map_ext_idx)) { /* extension doesn't yet exist in the map. add it there with the preliminary information (mainly its size) so if caller looks it up, it's going to be found. */ const struct mail_index_registered_ext *rext = array_idx(&tview->view.index->extensions, ext_id); struct mail_index_ext_header ext_hdr; i_zero(&ext_hdr); ext_hdr.hdr_size = rext->hdr_size; ext_hdr.record_size = ext_buf->arr.element_size - sizeof(uint32_t); ext_hdr.record_align = rext->record_align; mail_index_map_register_ext(tview->lookup_map, rext->name, (uint32_t)-1, &ext_hdr); } data = array_idx(ext_buf, idx); *map_r = tview->lookup_map; *data_r = tview_return_updated_ext(tview, seq, data, ext_id); return TRUE; } static void tview_lookup_ext_full(struct mail_index_view *view, uint32_t seq, uint32_t ext_id, struct mail_index_map **map_r, const void **data_r, bool *expunged_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; i_assert(ext_id < array_count(&view->index->extensions)); if (expunged_r != NULL) *expunged_r = FALSE; if (array_is_created(&tview->t->ext_rec_updates) && ext_id < array_count(&tview->t->ext_rec_updates)) { /* there are some ext updates in transaction. see if there's any for this sequence. */ if (tview_lookup_ext_update(tview, seq, ext_id, map_r, data_r)) return; } /* not updated, return the existing value, unless ext was already reset */ if (seq < tview->t->first_new_seq && !tview_is_ext_reset(tview, ext_id)) { tview->super->lookup_ext_full(view, seq, ext_id, map_r, data_r, expunged_r); } else { *map_r = view->index->map; *data_r = NULL; } } static void tview_get_header_ext(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, const void **data_r, size_t *data_size_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; /* FIXME: check updates */ tview->super->get_header_ext(view, map, ext_id, data_r, data_size_r); } static bool tview_ext_get_reset_id(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, uint32_t *reset_id_r) { struct mail_index_view_transaction *tview = (struct mail_index_view_transaction *)view; const uint32_t *reset_id_p; if (array_is_created(&tview->t->ext_reset_ids) && ext_id < array_count(&tview->t->ext_reset_ids) && map == tview->lookup_map) { reset_id_p = array_idx(&tview->t->ext_reset_ids, ext_id); *reset_id_r = *reset_id_p; return TRUE; } return tview->super->ext_get_reset_id(view, map, ext_id, reset_id_r); } static struct mail_index_view_vfuncs trans_view_vfuncs = { tview_close, tview_get_message_count, tview_get_header, tview_lookup_full, tview_lookup_uid, tview_lookup_seq_range, tview_lookup_first, tview_lookup_keywords, tview_lookup_ext_full, tview_get_header_ext, tview_ext_get_reset_id }; struct mail_index_view * mail_index_transaction_open_updated_view(struct mail_index_transaction *t) { struct mail_index_view_transaction *tview; if (t->view->syncing) { /* transaction view is being synced. while it's done, it's not possible to add new messages, but the view itself might change. so we can't make a copy of the view. */ mail_index_view_ref(t->view); return t->view; } tview = i_new(struct mail_index_view_transaction, 1); mail_index_view_clone(&tview->view, t->view); tview->view.v = trans_view_vfuncs; tview->super = &t->view->v; tview->t = t; mail_index_transaction_ref(t); return &tview->view; } dovecot-2.2.33.2/src/lib-index/mail-transaction-log-view.c0000644000175000017500000006053113165463624020146 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "mail-index-private.h" #include "mail-transaction-log-view-private.h" struct mail_transaction_log_view * mail_transaction_log_view_open(struct mail_transaction_log *log) { struct mail_transaction_log_view *view; view = i_new(struct mail_transaction_log_view, 1); view->log = log; view->broken = TRUE; i_assert(view->log->head != NULL); view->head = view->tail = view->log->head; view->head->refcount++; i_array_init(&view->file_refs, 8); array_append(&view->file_refs, &view->head, 1); view->next = log->views; log->views = view; return view; } static void mail_transaction_log_view_unref_all(struct mail_transaction_log_view *view) { struct mail_transaction_log_file *const *files; unsigned int i, count; files = array_get(&view->file_refs, &count); for (i = 0; i < count; i++) files[i]->refcount--; array_clear(&view->file_refs); } void mail_transaction_log_view_close(struct mail_transaction_log_view **_view) { struct mail_transaction_log_view *view = *_view; struct mail_transaction_log_view **p; *_view = NULL; for (p = &view->log->views; *p != NULL; p = &(*p)->next) { if (*p == view) { *p = view->next; break; } } mail_transaction_log_view_unref_all(view); mail_transaction_logs_clean(view->log); array_free(&view->file_refs); i_free(view); } static const char * mail_transaction_log_get_file_seqs(struct mail_transaction_log *log) { struct mail_transaction_log_file *file; string_t *str = t_str_new(32); if (log->files == NULL) return ""; for (file = log->files; file != NULL; file = file->next) str_printfa(str, ",%u", file->hdr.file_seq); return str_c(str) + 1; } int mail_transaction_log_view_set(struct mail_transaction_log_view *view, uint32_t min_file_seq, uoff_t min_file_offset, uint32_t max_file_seq, uoff_t max_file_offset, bool *reset_r, const char **reason_r) { struct mail_transaction_log_file *file, *const *files; uoff_t start_offset, end_offset; unsigned int i; uint32_t seq; int ret; *reset_r = FALSE; *reason_r = NULL; if (view->log == NULL) { /* transaction log is closed already. this log view shouldn't be used anymore. */ *reason_r = "Log already closed"; return -1; } if (min_file_seq == 0) { /* index file doesn't exist yet. this transaction log should start from the beginning */ if (view->log->files->hdr.prev_file_seq != 0) { /* but it doesn't */ *reason_r = t_strdup_printf( "Wanted log beginning, but found prev_file_seq=%u", view->log->files->hdr.prev_file_seq); return 0; } min_file_seq = view->log->files->hdr.file_seq; min_file_offset = 0; if (max_file_seq == 0) { max_file_seq = min_file_seq; max_file_offset = min_file_offset; } } for (file = view->log->files; file != NULL; file = file->next) { if (file->hdr.prev_file_seq == min_file_seq) break; } if (file != NULL && min_file_offset == file->hdr.prev_file_offset) { /* we can (and sometimes must) skip to the next file */ min_file_seq = file->hdr.file_seq; min_file_offset = file->hdr.hdr_size; } for (file = view->log->files; file != NULL; file = file->next) { if (file->hdr.prev_file_seq == max_file_seq) break; } if (file != NULL && max_file_offset == file->hdr.prev_file_offset) { /* we can skip to the next file. we've delayed checking for min_file_seq <= max_file_seq until now, because it's not really an error to specify the same position twice (even if in "wrong" order) */ i_assert(min_file_seq <= max_file_seq || min_file_seq <= file->hdr.file_seq); max_file_seq = file->hdr.file_seq; max_file_offset = file->hdr.hdr_size; } else { i_assert(min_file_seq <= max_file_seq); } if (min_file_seq == max_file_seq && min_file_offset > max_file_offset) { /* log file offset is probably corrupted in the index file. */ *reason_r = t_strdup_printf( "Invalid offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T ") > max_file_offset (%"PRIuUOFF_T")", min_file_seq, min_file_offset, max_file_offset); mail_transaction_log_view_set_corrupted(view, "%s", *reason_r); return -1; } view->tail = view->head = file = NULL; for (seq = min_file_seq; seq <= max_file_seq; seq++) { const char *reason = NULL; if (file == NULL || file->hdr.file_seq != seq) { /* see if we could find the missing file. if we know the max. file sequence or we don't have the the min. file, make sure NFS attribute cache gets flushed if necessary. */ bool nfs_flush = seq == min_file_seq || max_file_seq != (uint32_t)-1; ret = mail_transaction_log_find_file(view->log, seq, nfs_flush, &file, &reason); if (ret <= 0) { if (ret < 0) { *reason_r = t_strdup_printf( "Failed to find file seq=%u: %s", seq, reason); return -1; } /* not found / corrupted */ file = NULL; } } if (file == NULL || file->hdr.file_seq != seq) { i_assert(reason != NULL); if (file == NULL && max_file_seq == (uint32_t)-1 && view->head == view->log->head) { /* we just wanted to sync everything */ i_assert(max_file_offset == (uoff_t)-1); max_file_seq = seq-1; break; } /* if any of the found files reset the index, ignore any missing files up to it */ file = view->tail != NULL ? view->tail : view->log->files; for (;; file = file->next) { if (file == NULL || file->hdr.file_seq > max_file_seq) { /* missing files in the middle */ *reason_r = t_strdup_printf( "Missing middle file seq=%u (between %u..%u, we have seqs %s): %s", seq, min_file_seq, max_file_seq, mail_transaction_log_get_file_seqs(view->log), reason); return 0; } if (file->hdr.file_seq >= seq && file->hdr.prev_file_seq == 0) { /* we can ignore the missing file */ break; } } seq = file->hdr.file_seq; view->tail = NULL; } if (view->tail == NULL) view->tail = file; view->head = file; file = file->next; } i_assert(view->tail != NULL); if (min_file_offset == 0) { /* beginning of the file */ min_file_offset = view->tail->hdr.hdr_size; if (min_file_offset > max_file_offset && min_file_seq == max_file_seq) { /* we don't actually want to show anything */ max_file_offset = min_file_offset; } } if (min_file_offset < view->tail->hdr.hdr_size) { /* log file offset is probably corrupted in the index file. */ *reason_r = t_strdup_printf( "Invalid min_file_offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T ") < hdr_size (%u)", min_file_seq, min_file_offset, view->tail->hdr.hdr_size); mail_transaction_log_view_set_corrupted(view, "%s", *reason_r); return -1; } if (max_file_offset < view->head->hdr.hdr_size) { /* log file offset is probably corrupted in the index file. */ *reason_r = t_strdup_printf( "Invalid max_file_offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T ") < hdr_size (%u)", max_file_seq, max_file_offset, view->head->hdr.hdr_size); mail_transaction_log_view_set_corrupted(view, "%s", *reason_r); return -1; } /* we have all of them. update refcounts. */ mail_transaction_log_view_unref_all(view); /* Reference all used files. */ for (file = view->tail;; file = file->next) { array_append(&view->file_refs, &file, 1); file->refcount++; if (file == view->head) break; } view->cur = view->tail; view->cur_offset = view->cur->hdr.file_seq == min_file_seq ? min_file_offset : view->cur->hdr.hdr_size; /* Map the files only after we've found them all. Otherwise if we map one file and then another file just happens to get rotated, we could include both files in the view but skip the last transactions from the first file. We're mapping the files in reverse order so that _log_file_map() can verify that prev_file_offset matches how far it actually managed to sync the file. */ files = array_idx(&view->file_refs, 0); for (i = array_count(&view->file_refs); i > 0; i--) { file = files[i-1]; start_offset = file->hdr.file_seq == min_file_seq ? min_file_offset : file->hdr.hdr_size; end_offset = file->hdr.file_seq == max_file_seq ? max_file_offset : (uoff_t)-1; ret = mail_transaction_log_file_map(file, start_offset, end_offset, reason_r); if (ret <= 0) { *reason_r = t_strdup_printf( "Failed to map file seq=%u " "offset=%"PRIuUOFF_T"..%"PRIuUOFF_T" (ret=%d): %s", file->hdr.file_seq, start_offset, end_offset, ret, *reason_r); return ret; } if (file->hdr.prev_file_seq == 0) { /* this file resets the index. don't bother reading the others. */ if (view->cur != file || view->cur_offset == file->hdr.hdr_size) { view->cur = file; view->cur_offset = file->hdr.hdr_size; *reset_r = TRUE; break; } i_assert(i == 1); } } if (min_file_seq == view->head->hdr.file_seq && min_file_offset > view->head->sync_offset) { /* log file offset is probably corrupted in the index file. */ *reason_r = t_strdup_printf( "Invalid offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T ") > sync_offset (%"PRIuUOFF_T")", min_file_seq, min_file_offset, view->head->sync_offset); mail_transaction_log_view_set_corrupted(view, "%s", *reason_r); return -1; } i_assert(max_file_seq == (uint32_t)-1 || max_file_seq == view->head->hdr.file_seq); i_assert(max_file_offset == (uoff_t)-1 || max_file_offset <= view->head->sync_offset); i_assert(min_file_seq != max_file_seq || max_file_seq != view->head->hdr.file_seq || max_file_offset != (uoff_t)-1 || min_file_offset <= view->head->sync_offset); view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->cur_offset; view->min_file_seq = min_file_seq; view->min_file_offset = min_file_offset; view->max_file_seq = max_file_seq; view->max_file_offset = I_MIN(max_file_offset, view->head->sync_offset); view->broken = FALSE; if (mail_transaction_log_file_get_highest_modseq_at(view->cur, view->cur_offset, &view->prev_modseq, reason_r) < 0) return -1; i_assert(view->cur_offset <= view->cur->sync_offset); return 1; } int mail_transaction_log_view_set_all(struct mail_transaction_log_view *view) { struct mail_transaction_log_file *file, *first; const char *reason = NULL; int ret; /* make sure .log.2 file is opened */ (void)mail_transaction_log_find_file(view->log, 1, FALSE, &file, &reason); first = view->log->files; i_assert(first != NULL); for (file = view->log->files; file != NULL; file = file->next) { ret = mail_transaction_log_file_map(file, file->hdr.hdr_size, (uoff_t)-1, &reason); if (ret < 0) { first = NULL; break; } if (ret == 0) { /* corrupted */ first = NULL; } else if (file->hdr.prev_file_seq == 0) { /* this file resets the index. skip the old ones. */ first = file; } } if (first == NULL) { /* index wasn't reset after corruption was found */ i_assert(reason != NULL); mail_index_set_error(view->log->index, "Failed to map transaction log %s for all-view: %s", view->log->filepath, reason); return -1; } mail_transaction_log_view_unref_all(view); for (file = first; file != NULL; file = file->next) { array_append(&view->file_refs, &file, 1); file->refcount++; } view->tail = first; view->cur = view->tail; view->cur_offset = view->tail->hdr.hdr_size; view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->cur_offset; view->min_file_seq = view->cur->hdr.file_seq; view->min_file_offset = view->cur_offset; view->max_file_seq = view->head->hdr.file_seq; view->max_file_offset = view->head->sync_offset; view->broken = FALSE; if (mail_transaction_log_file_get_highest_modseq_at(view->cur, view->cur_offset, &view->prev_modseq, &reason) < 0) { mail_index_set_error(view->log->index, "Failed to get modseq in %s for all-view: %s", view->log->filepath, reason); return -1; } return 0; } void mail_transaction_log_view_clear(struct mail_transaction_log_view *view, uint32_t oldest_file_seq) { struct mail_transaction_log_file *file; const char *reason; mail_transaction_log_view_unref_all(view); if (oldest_file_seq != 0 && mail_transaction_log_find_file(view->log, oldest_file_seq, FALSE, &file, &reason) > 0) { for (; file != NULL; file = file->next) { array_append(&view->file_refs, &file, 1); file->refcount++; } } view->cur = view->head = view->tail = NULL; view->mark_file = NULL; view->mark_offset = 0; view->mark_modseq = 0; view->min_file_seq = view->max_file_seq = 0; view->min_file_offset = view->max_file_offset = 0; view->cur_offset = 0; view->prev_file_seq = 0; view->prev_file_offset = 0; view->prev_modseq = 0; } void mail_transaction_log_view_get_prev_pos(struct mail_transaction_log_view *view, uint32_t *file_seq_r, uoff_t *file_offset_r) { *file_seq_r = view->prev_file_seq; *file_offset_r = view->prev_file_offset; } uint64_t mail_transaction_log_view_get_prev_modseq(struct mail_transaction_log_view *view) { return view->prev_modseq; } static bool mail_transaction_log_view_get_last(struct mail_transaction_log_view *view, struct mail_transaction_log_file **last_r, uoff_t *last_offset_r) { struct mail_transaction_log_file *cur = view->cur; uoff_t cur_offset = view->cur_offset; bool last = FALSE; if (cur == NULL) { *last_r = NULL; return TRUE; } for (;;) { if (cur->hdr.file_seq == view->max_file_seq) { /* last file */ if (cur_offset == view->max_file_offset || cur_offset == cur->sync_offset) { /* we're all finished */ last = TRUE; } } else if (cur_offset == cur->sync_offset) { /* end of file, go to next one */ if (cur->next == NULL) { last = TRUE; } else { cur = cur->next; cur_offset = cur->hdr.hdr_size; continue; } } /* not EOF */ break; } *last_r = cur; *last_offset_r = cur_offset; return last; } bool mail_transaction_log_view_is_last(struct mail_transaction_log_view *view) { struct mail_transaction_log_file *cur; uoff_t cur_offset; return mail_transaction_log_view_get_last(view, &cur, &cur_offset); } void mail_transaction_log_view_set_corrupted(struct mail_transaction_log_view *view, const char *fmt, ...) { va_list va; view->broken = TRUE; va_start(va, fmt); T_BEGIN { mail_transaction_log_file_set_corrupted(view->log->head, "%s", t_strdup_vprintf(fmt, va)); } T_END; va_end(va); } bool mail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view) { return view->broken; } static bool log_view_is_uid_range_valid(struct mail_transaction_log_file *file, enum mail_transaction_type rec_type, const ARRAY_TYPE(seq_range) *uids) { const struct seq_range *rec, *prev = NULL; unsigned int i, count = array_count(uids); if ((uids->arr.buffer->used % uids->arr.element_size) != 0) { mail_transaction_log_file_set_corrupted(file, "Invalid record size (type=0x%x)", rec_type); return FALSE; } else if (count == 0) { mail_transaction_log_file_set_corrupted(file, "No UID ranges (type=0x%x)", rec_type); return FALSE; } for (i = 0; i < count; i++, prev = rec) { rec = array_idx(uids, i); if (rec->seq1 > rec->seq2 || rec->seq1 == 0) { mail_transaction_log_file_set_corrupted(file, "Invalid UID range (%u .. %u, type=0x%x)", rec->seq1, rec->seq2, rec_type); return FALSE; } if (prev != NULL && rec->seq1 <= prev->seq2) { mail_transaction_log_file_set_corrupted(file, "Non-sorted UID ranges (type=0x%x)", rec_type); return FALSE; } } return TRUE; } static bool log_view_is_record_valid(struct mail_transaction_log_file *file, const struct mail_transaction_header *hdr, const void *data) { enum mail_transaction_type rec_type; ARRAY_TYPE(seq_range) uids = ARRAY_INIT; buffer_t uid_buf; uint32_t rec_size; rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK; rec_size = mail_index_offset_to_uint32(hdr->size) - sizeof(*hdr); /* we want to be extra careful with expunges */ if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { if (rec_type != (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT)) { mail_transaction_log_file_set_corrupted(file, "expunge record missing protection mask"); return FALSE; } rec_type &= ~MAIL_TRANSACTION_EXPUNGE_PROT; } if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { if (rec_type != (MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT)) { mail_transaction_log_file_set_corrupted(file, "expunge guid record missing protection mask"); return FALSE; } rec_type &= ~MAIL_TRANSACTION_EXPUNGE_PROT; } if (rec_size == 0) { mail_transaction_log_file_set_corrupted(file, "Empty record contents (type=0x%x)", rec_type); return FALSE; } /* records that are exported by syncing and view syncing will be checked here so that we don't have to implement the same validation multiple times. other records are checked internally by mail_index_sync_record(). */ switch (rec_type) { case MAIL_TRANSACTION_APPEND: if ((rec_size % sizeof(struct mail_index_record)) != 0) { mail_transaction_log_file_set_corrupted(file, "Invalid append record size"); return FALSE; } break; case MAIL_TRANSACTION_EXPUNGE: buffer_create_from_const_data(&uid_buf, data, rec_size); array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_expunge)); break; case MAIL_TRANSACTION_EXPUNGE_GUID: { const struct mail_transaction_expunge_guid *recs = data; unsigned int i, count; if ((rec_size % sizeof(*recs)) != 0) { mail_transaction_log_file_set_corrupted(file, "Invalid expunge guid record size"); return FALSE; } count = rec_size / sizeof(*recs); for (i = 0; i < count; i++) { if (recs[i].uid == 0) { mail_transaction_log_file_set_corrupted(file, "Expunge guid record with uid=0"); return FALSE; } } break; } case MAIL_TRANSACTION_FLAG_UPDATE: buffer_create_from_const_data(&uid_buf, data, rec_size); array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_flag_update)); break; case MAIL_TRANSACTION_KEYWORD_UPDATE: { const struct mail_transaction_keyword_update *rec = data; unsigned int seqset_offset; seqset_offset = sizeof(*rec) + rec->name_size; if ((seqset_offset % 4) != 0) seqset_offset += 4 - (seqset_offset % 4); if (rec->name_size == 0) { mail_transaction_log_file_set_corrupted(file, "Trying to use empty keyword"); return FALSE; } if (seqset_offset > rec_size) { mail_transaction_log_file_set_corrupted(file, "Invalid keyword update record size"); return FALSE; } buffer_create_from_const_data(&uid_buf, CONST_PTR_OFFSET(data, seqset_offset), rec_size - seqset_offset); array_create_from_buffer(&uids, &uid_buf, sizeof(uint32_t)*2); break; } case MAIL_TRANSACTION_KEYWORD_RESET: buffer_create_from_const_data(&uid_buf, data, rec_size); array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_keyword_reset)); break; case MAIL_TRANSACTION_EXT_INTRO: { const struct mail_transaction_ext_intro *rec; unsigned int i; for (i = 0; i < rec_size; ) { if (i + sizeof(*rec) > rec_size) { /* should be just extra padding */ break; } rec = CONST_PTR_OFFSET(data, i); if (i + sizeof(*rec) + rec->name_size > rec_size) { mail_transaction_log_file_set_corrupted(file, "ext intro: name_size too large"); return FALSE; } i += sizeof(*rec) + rec->name_size; if ((i % 4) != 0) i += 4 - (i % 4); } break; } case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: { const char *attr_changes = data; unsigned int i; for (i = 0; i+2 < rec_size && attr_changes[i] != '\0'; ) { if (attr_changes[i] != '+' && attr_changes[i] != '-') { mail_transaction_log_file_set_corrupted(file, "attribute update: Invalid prefix 0x%02x", attr_changes[i]); return FALSE; } i++; if (attr_changes[i] != 'p' && attr_changes[i] != 's') { mail_transaction_log_file_set_corrupted(file, "attribute update: Invalid type 0x%02x", attr_changes[i]); return FALSE; } i++; if (attr_changes[i] == '\0') { mail_transaction_log_file_set_corrupted(file, "attribute update: Empty key"); return FALSE; } i += strlen(attr_changes+i) + 1; } if (i == 0 || (i < rec_size && attr_changes[i] != '\0')) { mail_transaction_log_file_set_corrupted(file, "attribute update doesn't end with NUL"); return FALSE; } break; } default: break; } if (array_is_created(&uids)) { if (!log_view_is_uid_range_valid(file, rec_type, &uids)) return FALSE; } return TRUE; } static int log_view_get_next(struct mail_transaction_log_view *view, const struct mail_transaction_header **hdr_r, const void **data_r) { const struct mail_transaction_header *hdr; struct mail_transaction_log_file *file; const void *data; enum mail_transaction_type rec_type; uint32_t full_size; size_t file_size; int ret; if (view->cur == NULL) return 0; /* prev_file_offset should point to beginning of previous log record. when we reach EOF, it should be left there, not to beginning of the next file that's not included inside the view. */ if (mail_transaction_log_view_get_last(view, &view->cur, &view->cur_offset)) { /* if the last file was the beginning of a file, we want to move prev pointers there */ view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->cur_offset; view->cur = NULL; return 0; } view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->cur_offset; file = view->cur; data = buffer_get_data(file->buffer, &file_size); file_size += file->buffer_offset; if (view->cur_offset + sizeof(*hdr) > file_size) { mail_transaction_log_file_set_corrupted(file, "offset points outside file " "(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")", view->cur_offset, sizeof(*hdr), file_size); return -1; } i_assert(view->cur_offset >= file->buffer_offset); hdr = CONST_PTR_OFFSET(data, view->cur_offset - file->buffer_offset); data = CONST_PTR_OFFSET(hdr, sizeof(*hdr)); rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK; full_size = mail_index_offset_to_uint32(hdr->size); if (full_size < sizeof(*hdr)) { mail_transaction_log_file_set_corrupted(file, "record size too small (type=0x%x, " "offset=%"PRIuUOFF_T", size=%u)", rec_type, view->cur_offset, full_size); return -1; } if (file_size - view->cur_offset < full_size) { mail_transaction_log_file_set_corrupted(file, "record size too large (type=0x%x, " "offset=%"PRIuUOFF_T", size=%u, end=%"PRIuSIZE_T")", rec_type, view->cur_offset, full_size, file_size); return -1; } T_BEGIN { ret = log_view_is_record_valid(file, hdr, data) ? 1 : -1; } T_END; if (ret > 0) { mail_transaction_update_modseq(hdr, data, &view->prev_modseq, MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr)); *hdr_r = hdr; *data_r = data; view->cur_offset += full_size; } return ret; } int mail_transaction_log_view_next(struct mail_transaction_log_view *view, const struct mail_transaction_header **hdr_r, const void **data_r) { const struct mail_transaction_header *hdr; const void *data; int ret = 0; if (view->broken) return -1; ret = log_view_get_next(view, &hdr, &data); if (ret <= 0) { if (ret < 0) view->cur_offset = view->cur->sync_offset; return ret; } /* drop expunge protection */ if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) == (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT) || (hdr->type & MAIL_TRANSACTION_TYPE_MASK) == (MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT)) view->tmp_hdr.type = hdr->type & ~MAIL_TRANSACTION_EXPUNGE_PROT; else view->tmp_hdr.type = hdr->type; /* return record's size */ view->tmp_hdr.size = mail_index_offset_to_uint32(hdr->size); i_assert(view->tmp_hdr.size > sizeof(*hdr)); view->tmp_hdr.size -= sizeof(*hdr); *hdr_r = &view->tmp_hdr; *data_r = data; return 1; } void mail_transaction_log_view_mark(struct mail_transaction_log_view *view) { i_assert(view->cur->hdr.file_seq == view->prev_file_seq); view->mark_file = view->cur; view->mark_offset = view->prev_file_offset; view->mark_next_offset = view->cur_offset; view->mark_modseq = view->prev_modseq; } void mail_transaction_log_view_rewind(struct mail_transaction_log_view *view) { i_assert(view->mark_file != NULL); view->cur = view->mark_file; view->cur_offset = view->mark_next_offset; view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->mark_offset; view->prev_modseq = view->mark_modseq; } dovecot-2.2.33.2/src/lib-index/mail-index-fsck.c0000644000175000017500000003353113123174404016112 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" static void mail_index_fsck_error(struct mail_index *index, const char *fmt, ...) ATTR_FORMAT(2, 3); static void mail_index_fsck_error(struct mail_index *index, const char *fmt, ...) { va_list va; va_start(va, fmt); mail_index_set_error(index, "Fixed index file %s: %s", index->filepath, t_strdup_vprintf(fmt, va)); va_end(va); } #define CHECK(field, oper) \ if (hdr->field oper map->hdr.field) { \ mail_index_fsck_error(index, #field" %u -> %u", \ map->hdr.field, hdr->field); \ } static void mail_index_fsck_log_pos(struct mail_index *index, struct mail_index_map *map, struct mail_index_header *hdr) { uint32_t file_seq; uoff_t file_offset; mail_transaction_log_get_head(index->log, &file_seq, &file_offset); if (hdr->log_file_seq < file_seq) { /* index's log_file_seq is too old. move it to log head. */ hdr->log_file_head_offset = hdr->log_file_tail_offset = sizeof(struct mail_transaction_log_header); } else if (hdr->log_file_seq == file_seq) { /* index's log_file_seq matches the current log. make sure the offsets are valid. */ if (hdr->log_file_head_offset > file_offset) hdr->log_file_head_offset = file_offset; else if (hdr->log_file_head_offset < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) hdr->log_file_head_offset = MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE; if (hdr->log_file_tail_offset > hdr->log_file_head_offset) hdr->log_file_tail_offset = hdr->log_file_head_offset; else if (hdr->log_file_tail_offset < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) hdr->log_file_tail_offset = MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE; } else { /* index's log_file_seq is newer than exists. move it to end of the current log head. */ hdr->log_file_head_offset = hdr->log_file_tail_offset = file_offset; } hdr->log_file_seq = file_seq; CHECK(log_file_seq, !=); if (hdr->log_file_seq == map->hdr.log_file_seq) { /* don't bother complaining about these if file changed too */ CHECK(log_file_head_offset, !=); CHECK(log_file_tail_offset, !=); } } static void mail_index_fsck_header(struct mail_index *index, struct mail_index_map *map, struct mail_index_header *hdr) { /* mail_index_map_check_header() has already checked that the index isn't completely broken. */ if (hdr->uid_validity == 0 && hdr->next_uid != 1) hdr->uid_validity = ioloop_time; if (index->log->head != NULL) mail_index_fsck_log_pos(index, map, hdr); } static bool array_has_name(const ARRAY_TYPE(const_string) *names, const char *name) { const char *const *namep; array_foreach(names, namep) { if (strcmp(*namep, name) == 0) return TRUE; } return FALSE; } static unsigned int mail_index_fsck_find_keyword_count(struct mail_index_map *map, const struct mail_index_ext_header *ext_hdr) { const struct mail_index_record *rec; const uint8_t *kw; unsigned int r, i, j, cur, max = 0, kw_pos, kw_size; kw_pos = ext_hdr->record_offset; kw_size = ext_hdr->record_size; rec = map->rec_map->records; for (r = 0; r < map->rec_map->records_count; r++) { kw = CONST_PTR_OFFSET(rec, kw_pos); for (i = cur = 0; i < kw_size; i++) { if (kw[i] != 0) { for (j = 0; j < 8; j++) { if ((kw[i] & (1 << j)) != 0) cur = i * 8 + j + 1; } } } if (cur > max) { max = cur; if (max == kw_size*8) return max; } rec = CONST_PTR_OFFSET(rec, map->hdr.record_size); } return max; } static bool keyword_name_is_valid(const char *buffer, unsigned int pos, unsigned int size) { for (; pos < size; pos++) { if (buffer[pos] == '\0') return TRUE; if (((unsigned char)buffer[pos] & 0x7f) < 32) { /* control characters aren't valid */ return FALSE; } } return FALSE; } static void mail_index_fsck_keywords(struct mail_index *index, struct mail_index_map *map, struct mail_index_header *hdr, const struct mail_index_ext_header *ext_hdr, unsigned int ext_offset, unsigned int *offset_p) { const struct mail_index_keyword_header *kw_hdr; struct mail_index_keyword_header *new_kw_hdr; const struct mail_index_keyword_header_rec *kw_rec; struct mail_index_keyword_header_rec new_kw_rec; const char *name, *name_buffer, **name_array; unsigned int i, j, name_pos, name_size, rec_pos, hdr_offset, diff; unsigned int changed_count, keywords_count, name_base_pos; ARRAY_TYPE(const_string) names; buffer_t *dest; bool changed = FALSE; hdr_offset = ext_offset + mail_index_map_ext_hdr_offset(sizeof(MAIL_INDEX_EXT_KEYWORDS)-1); kw_hdr = CONST_PTR_OFFSET(map->hdr_base, hdr_offset); keywords_count = kw_hdr->keywords_count; kw_rec = (const void *)(kw_hdr + 1); name_buffer = (const char *)(kw_rec + keywords_count); name_pos = (size_t)(name_buffer - (const char *)kw_hdr); if (name_pos > ext_hdr->hdr_size) { /* the header is completely broken */ keywords_count = mail_index_fsck_find_keyword_count(map, ext_hdr); mail_index_fsck_error(index, "Assuming keywords_count = %u", keywords_count); kw_rec = NULL; name_size = 0; changed = TRUE; } else { name_size = ext_hdr->hdr_size - name_pos; } /* create keyword name array. invalid keywords are added as empty strings */ t_array_init(&names, keywords_count); for (i = 0; i < keywords_count; i++) { if (name_size == 0 || !keyword_name_is_valid(name_buffer, kw_rec[i].name_offset, name_size)) name = ""; else name = name_buffer + kw_rec[i].name_offset; if (*name != '\0' && array_has_name(&names, name)) { /* duplicate */ name = ""; } array_append(&names, &name, 1); } /* give new names to invalid keywords */ changed_count = 0; name_array = array_idx_modifiable(&names, 0); for (i = j = 0; i < keywords_count; i++) { while (name_array[i][0] == '\0') { name = t_strdup_printf("unknown-%d", j++); if (!array_has_name(&names, name)) { name_array[i] = name; changed = TRUE; changed_count++; } } } if (!changed) { /* nothing was broken */ return; } mail_index_fsck_error(index, "Renamed %u keywords to unknown-*", changed_count); dest = buffer_create_dynamic(default_pool, I_MAX(ext_hdr->hdr_size, 128)); new_kw_hdr = buffer_append_space_unsafe(dest, sizeof(*new_kw_hdr)); new_kw_hdr->keywords_count = keywords_count; /* add keyword records so we can start appending names directly */ rec_pos = dest->used; i_zero(&new_kw_rec); (void)buffer_append_space_unsafe(dest, keywords_count * sizeof(*kw_rec)); /* write the actual records and names */ name_base_pos = dest->used; for (i = 0; i < keywords_count; i++) { new_kw_rec.name_offset = dest->used - name_base_pos; buffer_write(dest, rec_pos, &new_kw_rec, sizeof(new_kw_rec)); rec_pos += sizeof(*kw_rec); buffer_append(dest, name_array[i], strlen(name_array[i]) + 1); } /* keep the header size at least the same size as before */ if (dest->used < ext_hdr->hdr_size) buffer_append_zero(dest, ext_hdr->hdr_size - dest->used); if (dest->used > ext_hdr->hdr_size) { /* need to resize the header */ struct mail_index_ext_header new_ext_hdr; diff = dest->used - ext_hdr->hdr_size; buffer_copy(map->hdr_copy_buf, hdr_offset + diff, map->hdr_copy_buf, hdr_offset, (size_t)-1); map->hdr_base = map->hdr_copy_buf->data; hdr->header_size += diff; *offset_p += diff; new_ext_hdr = *ext_hdr; new_ext_hdr.hdr_size += diff; buffer_write(map->hdr_copy_buf, ext_offset, &new_ext_hdr, sizeof(new_ext_hdr)); } i_assert(hdr_offset + dest->used <= map->hdr_copy_buf->used); buffer_write(map->hdr_copy_buf, hdr_offset, dest->data, dest->used); /* keywords changed unexpectedly, so all views are broken now */ index->inconsistency_id++; buffer_free(&dest); } static void mail_index_fsck_extensions(struct mail_index *index, struct mail_index_map *map, struct mail_index_header *hdr) { const struct mail_index_ext_header *ext_hdr; ARRAY_TYPE(const_string) names; const char *name, *error; unsigned int offset, next_offset, i; t_array_init(&names, 64); offset = MAIL_INDEX_HEADER_SIZE_ALIGN(hdr->base_header_size); for (i = 0; offset < hdr->header_size; i++) { /* mail_index_map_ext_get_next() uses map->hdr, so make sure it's up-to-date */ map->hdr = *hdr; next_offset = offset; if (mail_index_map_ext_get_next(map, &next_offset, &ext_hdr, &name) < 0) { /* the extension continued outside header, drop it */ mail_index_fsck_error(index, "Dropped extension #%d (%s) " "with invalid header size", i, name); hdr->header_size = offset; buffer_set_used_size(map->hdr_copy_buf, hdr->header_size); break; } if (mail_index_map_ext_hdr_check(hdr, ext_hdr, name, &error) < 0) { mail_index_fsck_error(index, "Dropped broken extension #%d (%s)", i, name); } else if (array_has_name(&names, name)) { mail_index_fsck_error(index, "Dropped duplicate extension %s", name); } else { /* name may change if header buffer is changed */ name = t_strdup(name); if (strcmp(name, MAIL_INDEX_EXT_KEYWORDS) == 0) { mail_index_fsck_keywords(index, map, hdr, ext_hdr, offset, &next_offset); } array_append(&names, &name, 1); offset = next_offset; continue; } /* drop the field */ hdr->header_size -= next_offset - offset; buffer_copy(map->hdr_copy_buf, offset, map->hdr_copy_buf, next_offset, (size_t)-1); buffer_set_used_size(map->hdr_copy_buf, hdr->header_size); map->hdr_base = map->hdr_copy_buf->data; } } static void mail_index_fsck_records(struct mail_index *index, struct mail_index_map *map, struct mail_index_header *hdr) { struct mail_index_record *rec, *next_rec; uint32_t i, last_uid; bool logged_unordered_uids = FALSE, logged_zero_uids = FALSE; bool records_dropped = FALSE; hdr->messages_count = 0; hdr->seen_messages_count = 0; hdr->deleted_messages_count = 0; hdr->first_unseen_uid_lowwater = 0; hdr->first_deleted_uid_lowwater = 0; rec = map->rec_map->records; last_uid = 0; for (i = 0; i < map->rec_map->records_count; ) { next_rec = PTR_OFFSET(rec, hdr->record_size); if (rec->uid <= last_uid) { /* log an error once, and skip this record */ if (rec->uid == 0) { if (!logged_zero_uids) { mail_index_fsck_error(index, "Record UIDs have zeroes"); logged_zero_uids = TRUE; } } else { if (!logged_unordered_uids) { mail_index_fsck_error(index, "Record UIDs unordered"); logged_unordered_uids = TRUE; } } /* not the fastest way when we're skipping lots of records, but this should happen rarely so don't bother optimizing. */ memmove(rec, next_rec, hdr->record_size * (map->rec_map->records_count - i - 1)); map->rec_map->records_count--; records_dropped = TRUE; continue; } hdr->messages_count++; if ((rec->flags & MAIL_SEEN) != 0) hdr->seen_messages_count++; if ((rec->flags & MAIL_DELETED) != 0) hdr->deleted_messages_count++; if ((rec->flags & MAIL_SEEN) == 0 && hdr->first_unseen_uid_lowwater == 0) hdr->first_unseen_uid_lowwater = rec->uid; if ((rec->flags & MAIL_DELETED) != 0 && hdr->first_deleted_uid_lowwater == 0) hdr->first_deleted_uid_lowwater = rec->uid; last_uid = rec->uid; rec = next_rec; i++; } if (records_dropped) { /* all existing views are broken now */ index->inconsistency_id++; } if (hdr->next_uid <= last_uid) { mail_index_fsck_error(index, "next_uid %u -> %u", hdr->next_uid, last_uid+1); hdr->next_uid = last_uid+1; } if (hdr->first_unseen_uid_lowwater == 0) hdr->first_unseen_uid_lowwater = hdr->next_uid; if (hdr->first_deleted_uid_lowwater == 0) hdr->first_deleted_uid_lowwater = hdr->next_uid; if (hdr->first_recent_uid > hdr->next_uid) hdr->first_recent_uid = hdr->next_uid; if (hdr->first_recent_uid == 0) hdr->first_recent_uid = 1; CHECK(uid_validity, !=); CHECK(messages_count, !=); CHECK(seen_messages_count, !=); CHECK(deleted_messages_count, !=); CHECK(first_unseen_uid_lowwater, <); CHECK(first_deleted_uid_lowwater, <); CHECK(first_recent_uid, !=); } static void mail_index_fsck_map(struct mail_index *index, struct mail_index_map *map) { struct mail_index_header hdr; if (index->log->head != NULL) { /* Remember the log head position. If we go back in the index's head offset, ignore errors in the log up to this offset. */ mail_transaction_log_get_head(index->log, &index->fsck_log_head_file_seq, &index->fsck_log_head_file_offset); } hdr = map->hdr; mail_index_fsck_header(index, map, &hdr); mail_index_fsck_extensions(index, map, &hdr); mail_index_fsck_records(index, map, &hdr); hdr.flags |= MAIL_INDEX_HDR_FLAG_FSCKD; map->hdr = hdr; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); } int mail_index_fsck(struct mail_index *index) { bool orig_locked = index->log_sync_locked; struct mail_index_map *map; uint32_t file_seq; uoff_t file_offset; i_warning("fscking index file %s", index->filepath); index->fscked = TRUE; if (index->log->head == NULL) { /* we're trying to open the index files, but there wasn't any .log file. */ if (mail_transaction_log_create(index->log, FALSE) < 0) return -1; } if (!orig_locked) { if (mail_transaction_log_sync_lock(index->log, "fscking", &file_seq, &file_offset) < 0) return -1; } map = mail_index_map_clone(index->map); mail_index_unmap(&index->map); index->map = map; T_BEGIN { mail_index_fsck_map(index, map); } T_END; mail_index_write(index, FALSE); if (!orig_locked) mail_transaction_log_sync_unlock(index->log, "fscking"); return 0; } void mail_index_fsck_locked(struct mail_index *index) { int ret; i_assert(index->log_sync_locked); ret = mail_index_fsck(index); i_assert(ret == 0); } bool mail_index_reset_fscked(struct mail_index *index) { bool ret = index->fscked; index->fscked = FALSE; return ret; } dovecot-2.2.33.2/src/lib-index/mail-index-alloc-cache.c0000644000175000017500000001710013123174404017311 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "module-context.h" #include "eacces-error.h" #include "mail-index-private.h" #include "mail-index-alloc-cache.h" #define MAIL_INDEX_ALLOC_CACHE_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_index_alloc_cache_index_module) /* How many seconds to keep index opened for reuse after it's been closed */ #define INDEX_CACHE_TIMEOUT 10 /* How many closed indexes to keep */ #define INDEX_CACHE_MAX 3 struct mail_index_alloc_cache_list { union mail_index_module_context module_ctx; struct mail_index_alloc_cache_list *next; struct mail_index *index; char *mailbox_path; int refcount; bool referenced; dev_t index_dir_dev; ino_t index_dir_ino; time_t destroy_time; }; static MODULE_CONTEXT_DEFINE_INIT(mail_index_alloc_cache_index_module, &mail_index_module_register); static struct mail_index_alloc_cache_list *indexes = NULL; static unsigned int indexes_cache_references_count = 0; static struct timeout *to_index = NULL; static struct mail_index_alloc_cache_list * mail_index_alloc_cache_add(struct mail_index *index, const char *mailbox_path, struct stat *st) { struct mail_index_alloc_cache_list *list; list = i_new(struct mail_index_alloc_cache_list, 1); list->refcount = 1; list->index = index; list->mailbox_path = i_strdup(mailbox_path); list->index_dir_dev = st->st_dev; list->index_dir_ino = st->st_ino; list->next = indexes; indexes = list; MODULE_CONTEXT_SET(index, mail_index_alloc_cache_index_module, list); return list; } static void mail_index_alloc_cache_list_unref(struct mail_index_alloc_cache_list *list) { i_assert(list->referenced); i_assert(indexes_cache_references_count > 0); indexes_cache_references_count--; mail_index_close(list->index); list->referenced = FALSE; } static void mail_index_alloc_cache_list_free(struct mail_index_alloc_cache_list *list) { i_assert(list->refcount == 0); if (list->referenced) mail_index_alloc_cache_list_unref(list); mail_index_free(&list->index); i_free(list->mailbox_path); i_free(list); } static struct mail_index_alloc_cache_list * mail_index_alloc_cache_find(const char *mailbox_path, const char *index_dir, const struct stat *index_st) { struct mail_index_alloc_cache_list **indexp, *rec, *match; unsigned int destroy_count; struct stat st; destroy_count = 0; match = NULL; for (indexp = &indexes; *indexp != NULL;) { rec = *indexp; if (match != NULL) { /* already found the index. we're just going through the rest of them to drop 0 refcounts */ } else if (rec->refcount == 0 && rec->index->open_count == 0) { /* index is already closed. don't even try to reuse it. */ } else if (index_dir != NULL && rec->index_dir_ino != 0) { if (index_st->st_ino == rec->index_dir_ino && CMP_DEV_T(index_st->st_dev, rec->index_dir_dev)) { /* make sure the directory still exists. it might have been renamed and we're trying to access it via its new path now. */ if (stat(rec->index->dir, &st) < 0 || st.st_ino != index_st->st_ino || !CMP_DEV_T(st.st_dev, index_st->st_dev)) rec->destroy_time = 0; else match = rec; } } else if (mailbox_path != NULL && rec->mailbox_path != NULL && index_dir == NULL && rec->index_dir_ino == 0) { if (strcmp(mailbox_path, rec->mailbox_path) == 0) match = rec; } if (rec->refcount == 0 && rec != match) { if (rec->destroy_time <= ioloop_time || destroy_count >= INDEX_CACHE_MAX) { *indexp = rec->next; mail_index_alloc_cache_list_free(rec); continue; } else { destroy_count++; } } indexp = &(*indexp)->next; } return match; } struct mail_index * mail_index_alloc_cache_get(const char *mailbox_path, const char *index_dir, const char *prefix) { struct mail_index_alloc_cache_list *match; struct stat st; /* compare index_dir inodes so we don't break even with symlinks. if index_dir doesn't exist yet or if using in-memory indexes, just compare mailbox paths */ i_zero(&st); if (index_dir == NULL) { /* in-memory indexes */ } else if (stat(index_dir, &st) < 0) { if (errno == ENOENT) { /* it'll be created later */ } else if (errno == EACCES) { i_error("%s", eacces_error_get("stat", index_dir)); } else { i_error("stat(%s) failed: %m", index_dir); } } match = mail_index_alloc_cache_find(mailbox_path, index_dir, &st); if (match == NULL) { struct mail_index *index = mail_index_alloc(index_dir, prefix); match = mail_index_alloc_cache_add(index, mailbox_path, &st); } else { match->refcount++; } i_assert(match->index != NULL); return match->index; } static bool destroy_unrefed(unsigned int min_destroy_count) { struct mail_index_alloc_cache_list **list, *rec; bool destroyed = FALSE; bool seen_ref0 = FALSE; for (list = &indexes; *list != NULL;) { rec = *list; if (rec->refcount == 0 && (min_destroy_count > 0 || rec->destroy_time <= ioloop_time)) { *list = rec->next; destroyed = TRUE; mail_index_alloc_cache_list_free(rec); if (min_destroy_count > 0) min_destroy_count--; } else { if (rec->refcount == 0) seen_ref0 = TRUE; if (min_destroy_count > 0 && rec->index->open_count == 1 && rec->referenced) { /* we're the only one keeping this index open. we might be here, because the caller is deleting this mailbox and wants its indexes to be closed. so close it. */ destroyed = TRUE; mail_index_alloc_cache_list_unref(rec); } list = &(*list)->next; } } if (!seen_ref0 && to_index != NULL) timeout_remove(&to_index); return destroyed; } static void ATTR_NULL(1) index_removal_timeout(void *context ATTR_UNUSED) { destroy_unrefed(0); } void mail_index_alloc_cache_unref(struct mail_index **_index) { struct mail_index *index = *_index; struct mail_index_alloc_cache_list *list, **listp; *_index = NULL; list = NULL; for (listp = &indexes; *listp != NULL; listp = &(*listp)->next) { if ((*listp)->index == index) { list = *listp; break; } } i_assert(list != NULL); i_assert(list->refcount > 0); list->refcount--; list->destroy_time = ioloop_time + INDEX_CACHE_TIMEOUT; if (list->refcount == 0 && index->open_count == 0) { /* index was already closed. don't even try to cache it. */ *listp = list->next; mail_index_alloc_cache_list_free(list); } else if (to_index == NULL) { to_index = timeout_add(INDEX_CACHE_TIMEOUT*1000/2, index_removal_timeout, (void *)NULL); } } void mail_index_alloc_cache_destroy_unrefed(void) { destroy_unrefed(UINT_MAX); } void mail_index_alloc_cache_index_opened(struct mail_index *index) { struct mail_index_alloc_cache_list *list = MAIL_INDEX_ALLOC_CACHE_CONTEXT(index); struct stat st; if (list != NULL && list->index_dir_ino == 0 && !MAIL_INDEX_IS_IN_MEMORY(index)) { /* newly created index directory. update its stat. */ if (stat(index->dir, &st) == 0) { list->index_dir_ino = st.st_ino; list->index_dir_dev = st.st_dev; } } } void mail_index_alloc_cache_index_closing(struct mail_index *index) { struct mail_index_alloc_cache_list *list = MAIL_INDEX_ALLOC_CACHE_CONTEXT(index); i_assert(index->open_count > 0); if (index->open_count > 1 || list == NULL) return; if (list->referenced) { /* we're closing our referenced index */ return; } while (indexes_cache_references_count > INDEX_CACHE_MAX) { if (!destroy_unrefed(1)) { /* our cache is full already, don't keep more */ return; } } /* keep the index referenced for caching */ indexes_cache_references_count++; list->referenced = TRUE; index->open_count++; } dovecot-2.2.33.2/src/lib-index/mail-index-strmap.h0000644000175000017500000000613113123174404016473 00000000000000#ifndef MAIL_INDEX_STRMAP_H #define MAIL_INDEX_STRMAP_H #include "hash2.h" struct mail_index; struct mail_index_view; struct mail_index_strmap_header { #define MAIL_INDEX_STRMAP_VERSION 1 uint8_t version; uint8_t unused[3]; uint32_t uid_validity; }; struct mail_index_strmap_rec { uint32_t uid; uint32_t ref_index; /* unique index number for the string */ uint32_t str_idx; }; ARRAY_DEFINE_TYPE(mail_index_strmap_rec, struct mail_index_strmap_rec); typedef bool mail_index_strmap_key_cmp_t(const char *key, const struct mail_index_strmap_rec *rec, void *context); /* Returns 1 if matches, 0 if not, -1 if one of the records is expunged and the result can't be determined */ typedef int mail_index_strmap_rec_cmp_t(const struct mail_index_strmap_rec *rec1, const struct mail_index_strmap_rec *rec2, void *context); /* called when string indexes are renumbered. idx_map[old_idx] = new_idx. if new_idx is 0, the record was expunged. As a special case if count=0, the strmap was reset. */ typedef void mail_index_strmap_remap_t(const uint32_t *idx_map, unsigned int old_count, unsigned int new_count, void *context); struct mail_index_strmap * mail_index_strmap_init(struct mail_index *index, const char *suffix); void mail_index_strmap_deinit(struct mail_index_strmap **strmap); /* Returns strmap records and hash that can be used for read-only access. The records array always teminates with a record containing zeros (but it's not counted in the array count). */ struct mail_index_strmap_view * mail_index_strmap_view_open(struct mail_index_strmap *strmap, struct mail_index_view *idx_view, mail_index_strmap_key_cmp_t *key_compare_cb, mail_index_strmap_rec_cmp_t *rec_compare_cb, mail_index_strmap_remap_t *remap_cb, void *context, const ARRAY_TYPE(mail_index_strmap_rec) **recs_r, const struct hash2_table **hash_r); void mail_index_strmap_view_close(struct mail_index_strmap_view **view); void mail_index_strmap_view_set_corrupted(struct mail_index_strmap_view *view); /* Return the highest used string index. */ uint32_t mail_index_strmap_view_get_highest_idx(struct mail_index_strmap_view *view); /* Synchronize strmap: Caller adds missing entries, expunged messages may be removed internally and the changes are written to disk. Note that the strmap recs/hash shouldn't be used until _sync_commit() is called, because the string indexes may be renumbered if another process had already written the same changes as us. */ struct mail_index_strmap_view_sync * mail_index_strmap_view_sync_init(struct mail_index_strmap_view *view, uint32_t *last_uid_r); void mail_index_strmap_view_sync_add(struct mail_index_strmap_view_sync *sync, uint32_t uid, uint32_t ref_index, const char *key); void mail_index_strmap_view_sync_add_unique(struct mail_index_strmap_view_sync *sync, uint32_t uid, uint32_t ref_index); void mail_index_strmap_view_sync_commit(struct mail_index_strmap_view_sync **sync); void mail_index_strmap_view_sync_rollback(struct mail_index_strmap_view_sync **sync); #endif dovecot-2.2.33.2/src/lib-index/mail-index-map.c0000644000175000017500000003777413165463624015771 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str-sanitize.h" #include "mmap-util.h" #include "mail-index-private.h" #include "mail-index-modseq.h" void mail_index_map_init_extbufs(struct mail_index_map *map, unsigned int initial_count) { #define EXTENSION_NAME_APPROX_LEN 20 #define EXT_GLOBAL_ALLOC_SIZE \ ((sizeof(map->extensions) + sizeof(buffer_t)) * 2) #define EXT_PER_ALLOC_SIZE \ (EXTENSION_NAME_APPROX_LEN + \ sizeof(struct mail_index_ext) + sizeof(uint32_t)) size_t size; if (map->extension_pool == NULL) { size = EXT_GLOBAL_ALLOC_SIZE + initial_count * EXT_PER_ALLOC_SIZE; map->extension_pool = pool_alloconly_create(MEMPOOL_GROWING"map extensions", nearest_power(size)); } else { p_clear(map->extension_pool); /* try to use the existing pool's size for initial_count so we don't grow it unneededly */ size = p_get_max_easy_alloc_size(map->extension_pool); if (size > EXT_GLOBAL_ALLOC_SIZE + EXT_PER_ALLOC_SIZE) { initial_count = (size - EXT_GLOBAL_ALLOC_SIZE) / EXT_PER_ALLOC_SIZE; } } p_array_init(&map->extensions, map->extension_pool, initial_count); p_array_init(&map->ext_id_map, map->extension_pool, initial_count); } bool mail_index_map_lookup_ext(struct mail_index_map *map, const char *name, uint32_t *idx_r) { const struct mail_index_ext *ext; if (!array_is_created(&map->extensions)) return FALSE; array_foreach(&map->extensions, ext) { if (strcmp(ext->name, name) == 0) { *idx_r = array_foreach_idx(&map->extensions, ext); return TRUE; } } return FALSE; } unsigned int mail_index_map_ext_hdr_offset(unsigned int name_len) { size_t size = sizeof(struct mail_index_ext_header) + name_len; return MAIL_INDEX_HEADER_SIZE_ALIGN(size); } uint32_t mail_index_map_register_ext(struct mail_index_map *map, const char *name, uint32_t ext_offset, const struct mail_index_ext_header *ext_hdr) { struct mail_index_ext *ext; uint32_t idx, ext_map_idx, empty_idx = (uint32_t)-1; if (!array_is_created(&map->extensions)) { mail_index_map_init_extbufs(map, 5); idx = 0; } else { idx = array_count(&map->extensions); } i_assert(!mail_index_map_lookup_ext(map, name, &ext_map_idx)); ext = array_append_space(&map->extensions); ext->name = p_strdup(map->extension_pool, name); ext->ext_offset = ext_offset; ext->hdr_offset = ext_offset == (uint32_t)-1 ? (uint32_t)-1 : ext_offset + mail_index_map_ext_hdr_offset(strlen(name)); ext->hdr_size = ext_hdr->hdr_size; ext->record_offset = ext_hdr->record_offset; ext->record_size = ext_hdr->record_size; ext->record_align = ext_hdr->record_align; ext->reset_id = ext_hdr->reset_id; ext->index_idx = mail_index_ext_register(map->index, name, ext_hdr->hdr_size, ext_hdr->record_size, ext_hdr->record_align); /* Update index ext_id -> map ext_id mapping. Fill non-used ext_ids with (uint32_t)-1 */ while (array_count(&map->ext_id_map) < ext->index_idx) array_append(&map->ext_id_map, &empty_idx, 1); array_idx_set(&map->ext_id_map, ext->index_idx, &idx); return idx; } int mail_index_map_ext_get_next(struct mail_index_map *map, unsigned int *offset_p, const struct mail_index_ext_header **ext_hdr_r, const char **name_r) { const struct mail_index_ext_header *ext_hdr; unsigned int offset, name_offset; offset = *offset_p; *name_r = ""; /* Extension header contains: - struct mail_index_ext_header - name (not 0-terminated) - 64bit alignment padding - extension header contents - 64bit alignment padding */ name_offset = offset + sizeof(*ext_hdr); ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset); if (offset + sizeof(*ext_hdr) >= map->hdr.header_size) return -1; offset += mail_index_map_ext_hdr_offset(ext_hdr->name_size); if (offset > map->hdr.header_size) return -1; *name_r = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset), ext_hdr->name_size); if (strcmp(*name_r, str_sanitize(*name_r, -1)) != 0) { /* we allow only plain ASCII names, so this extension is most likely broken */ *name_r = ""; } /* finally make sure that the hdr_size is small enough. do this last so that we could return a usable name. */ offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size); if (offset > map->hdr.header_size) return -1; *offset_p = offset; *ext_hdr_r = ext_hdr; return 0; } static int mail_index_map_ext_hdr_check_record(const struct mail_index_header *hdr, const struct mail_index_ext_header *ext_hdr, const char **error_r) { if (ext_hdr->record_align == 0) { *error_r = "Record field alignment is zero"; return -1; } /* until we get 128 bit CPUs having a larger alignment is pointless */ if (ext_hdr->record_align > sizeof(uint64_t)) { *error_r = "Record alignment is too large"; return -1; } /* a large record size is most likely a bug somewhere. the maximum record size is limited to 64k anyway, so try to fail earlier. */ if (ext_hdr->record_size >= 32768) { *error_r = "Record size is too large"; return -1; } if (ext_hdr->record_offset == 0) { /* if we get here from extension introduction, record_offset=0 and hdr->record_size hasn't been updated yet */ return 0; } if (ext_hdr->record_offset + ext_hdr->record_size > hdr->record_size) { *error_r = t_strdup_printf("Record field points " "outside record size (%u+%u > %u)", ext_hdr->record_offset, ext_hdr->record_size, hdr->record_size); return -1; } if ((ext_hdr->record_offset % ext_hdr->record_align) != 0) { *error_r = t_strdup_printf("Record field alignment %u " "not used", ext_hdr->record_align); return -1; } if ((hdr->record_size % ext_hdr->record_align) != 0) { *error_r = t_strdup_printf("Record size not aligned by %u " "as required by extension", ext_hdr->record_align); return -1; } return 0; } int mail_index_map_ext_hdr_check(const struct mail_index_header *hdr, const struct mail_index_ext_header *ext_hdr, const char *name, const char **error_r) { if (ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) { *error_r = "Invalid field values"; return -1; } if (*name == '\0') { *error_r = "Broken name"; return -1; } if (ext_hdr->record_size != 0) { if (mail_index_map_ext_hdr_check_record(hdr, ext_hdr, error_r) < 0) return -1; } if (ext_hdr->hdr_size > MAIL_INDEX_EXT_HEADER_MAX_SIZE) { *error_r = t_strdup_printf("Headersize too large (%u)", ext_hdr->hdr_size); return -1; } return 0; } static void mail_index_header_init(struct mail_index *index, struct mail_index_header *hdr) { i_assert((sizeof(*hdr) % sizeof(uint64_t)) == 0); i_zero(hdr); hdr->major_version = MAIL_INDEX_MAJOR_VERSION; hdr->minor_version = MAIL_INDEX_MINOR_VERSION; hdr->base_header_size = sizeof(*hdr); hdr->header_size = sizeof(*hdr); hdr->record_size = sizeof(struct mail_index_record); #if !WORDS_BIGENDIAN hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #endif hdr->indexid = index->indexid; hdr->log_file_seq = 1; hdr->next_uid = 1; hdr->first_recent_uid = 1; } struct mail_index_map *mail_index_map_alloc(struct mail_index *index) { struct mail_index_map tmp_map; i_zero(&tmp_map); mail_index_header_init(index, &tmp_map.hdr); tmp_map.index = index; tmp_map.hdr_base = &tmp_map.hdr; /* a bit kludgy way to do this, but it initializes everything nicely and correctly */ return mail_index_map_clone(&tmp_map); } static void mail_index_record_map_free(struct mail_index_map *map, struct mail_index_record_map *rec_map) { if (rec_map->buffer != NULL) { i_assert(rec_map->mmap_base == NULL); buffer_free(&rec_map->buffer); } else if (rec_map->mmap_base != NULL) { i_assert(rec_map->buffer == NULL); if (munmap(rec_map->mmap_base, rec_map->mmap_size) < 0) mail_index_set_syscall_error(map->index, "munmap()"); rec_map->mmap_base = NULL; } array_free(&rec_map->maps); if (rec_map->modseq != NULL) mail_index_map_modseq_free(&rec_map->modseq); i_free(rec_map); } static void mail_index_record_map_unlink(struct mail_index_map *map) { struct mail_index_map *const *maps; unsigned int idx = UINT_MAX; array_foreach(&map->rec_map->maps, maps) { if (*maps == map) { idx = array_foreach_idx(&map->rec_map->maps, maps); break; } } i_assert(idx != UINT_MAX); array_delete(&map->rec_map->maps, idx, 1); if (array_count(&map->rec_map->maps) == 0) { mail_index_record_map_free(map, map->rec_map); map->rec_map = NULL; } } void mail_index_unmap(struct mail_index_map **_map) { struct mail_index_map *map = *_map; *_map = NULL; if (--map->refcount > 0) return; i_assert(map->refcount == 0); mail_index_record_map_unlink(map); if (map->extension_pool != NULL) pool_unref(&map->extension_pool); if (array_is_created(&map->keyword_idx_map)) array_free(&map->keyword_idx_map); buffer_free(&map->hdr_copy_buf); i_free(map); } static void mail_index_map_copy_records(struct mail_index_record_map *dest, const struct mail_index_record_map *src, unsigned int record_size) { size_t size; size = src->records_count * record_size; /* +1% so we have a bit of space to grow. useful for huge mailboxes. */ dest->buffer = buffer_create_dynamic(default_pool, size + I_MAX(size/100, 1024)); buffer_append(dest->buffer, src->records, size); dest->records = buffer_get_modifiable_data(dest->buffer, NULL); dest->records_count = src->records_count; } static void mail_index_map_copy_header(struct mail_index_map *dest, const struct mail_index_map *src) { /* use src->hdr copy directly, because if we got here from syncing it has the latest changes. */ if (src != dest) dest->hdr = src->hdr; if (dest->hdr_copy_buf != NULL) { if (src == dest) return; buffer_set_used_size(dest->hdr_copy_buf, 0); } else { dest->hdr_copy_buf = buffer_create_dynamic(default_pool, dest->hdr.header_size); } buffer_append(dest->hdr_copy_buf, &dest->hdr, I_MIN(sizeof(dest->hdr), src->hdr.base_header_size)); if (src != dest) { buffer_write(dest->hdr_copy_buf, src->hdr.base_header_size, CONST_PTR_OFFSET(src->hdr_base, src->hdr.base_header_size), src->hdr.header_size - src->hdr.base_header_size); } dest->hdr_base = buffer_get_modifiable_data(dest->hdr_copy_buf, NULL); i_assert(dest->hdr_copy_buf->used == dest->hdr.header_size); } static struct mail_index_record_map * mail_index_record_map_alloc(struct mail_index_map *map) { struct mail_index_record_map *rec_map; rec_map = i_new(struct mail_index_record_map, 1); i_array_init(&rec_map->maps, 4); array_append(&rec_map->maps, &map, 1); return rec_map; } struct mail_index_map *mail_index_map_clone(const struct mail_index_map *map) { struct mail_index_map *mem_map; struct mail_index_ext *ext; unsigned int count; mem_map = i_new(struct mail_index_map, 1); mem_map->index = map->index; mem_map->refcount = 1; if (map->rec_map == NULL) { mem_map->rec_map = mail_index_record_map_alloc(mem_map); mem_map->rec_map->buffer = buffer_create_dynamic(default_pool, 1024); } else { mem_map->rec_map = map->rec_map; array_append(&mem_map->rec_map->maps, &mem_map, 1); } mail_index_map_copy_header(mem_map, map); /* copy extensions */ if (array_is_created(&map->ext_id_map)) { count = array_count(&map->ext_id_map); mail_index_map_init_extbufs(mem_map, count + 2); array_append_array(&mem_map->extensions, &map->extensions); array_append_array(&mem_map->ext_id_map, &map->ext_id_map); /* fix the name pointers to use our own pool */ array_foreach_modifiable(&mem_map->extensions, ext) { i_assert(ext->record_offset + ext->record_size <= mem_map->hdr.record_size); ext->name = p_strdup(mem_map->extension_pool, ext->name); } } /* copy keyword map */ if (array_is_created(&map->keyword_idx_map)) { i_array_init(&mem_map->keyword_idx_map, array_count(&map->keyword_idx_map) + 4); array_append_array(&mem_map->keyword_idx_map, &map->keyword_idx_map); } return mem_map; } void mail_index_record_map_move_to_private(struct mail_index_map *map) { struct mail_index_record_map *new_map; const struct mail_index_record *rec; if (array_count(&map->rec_map->maps) > 1) { new_map = mail_index_record_map_alloc(map); mail_index_map_copy_records(new_map, map->rec_map, map->hdr.record_size); mail_index_record_map_unlink(map); map->rec_map = new_map; if (map->rec_map->modseq != NULL) new_map->modseq = mail_index_map_modseq_clone(map->rec_map->modseq); } else { new_map = map->rec_map; } if (new_map->records_count != map->hdr.messages_count) { new_map->records_count = map->hdr.messages_count; if (new_map->records_count == 0) new_map->last_appended_uid = 0; else { rec = MAIL_INDEX_REC_AT_SEQ(map, new_map->records_count); new_map->last_appended_uid = rec->uid; } buffer_set_used_size(new_map->buffer, new_map->records_count * map->hdr.record_size); } } void mail_index_map_move_to_memory(struct mail_index_map *map) { struct mail_index_record_map *new_map; if (map->rec_map->mmap_base == NULL) return; if (array_count(&map->rec_map->maps) == 1) new_map = map->rec_map; else { new_map = mail_index_record_map_alloc(map); new_map->modseq = map->rec_map->modseq == NULL ? NULL : mail_index_map_modseq_clone(map->rec_map->modseq); } mail_index_map_copy_records(new_map, map->rec_map, map->hdr.record_size); mail_index_map_copy_header(map, map); if (new_map != map->rec_map) { mail_index_record_map_unlink(map); map->rec_map = new_map; } else { if (munmap(new_map->mmap_base, new_map->mmap_size) < 0) mail_index_set_syscall_error(map->index, "munmap()"); new_map->mmap_base = NULL; } } bool mail_index_map_get_ext_idx(struct mail_index_map *map, uint32_t ext_id, uint32_t *idx_r) { const uint32_t *id; if (!array_is_created(&map->ext_id_map) || ext_id >= array_count(&map->ext_id_map)) return FALSE; id = array_idx(&map->ext_id_map, ext_id); *idx_r = *id; return *idx_r != (uint32_t)-1; } static uint32_t mail_index_bsearch_uid(struct mail_index_map *map, uint32_t uid, uint32_t left_idx, int nearest_side) { const struct mail_index_record *rec_base, *rec; uint32_t idx, right_idx, record_size; i_assert(map->hdr.messages_count <= map->rec_map->records_count); rec_base = map->rec_map->records; record_size = map->hdr.record_size; idx = left_idx; right_idx = I_MIN(map->hdr.messages_count, uid); i_assert(right_idx < INT_MAX); while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; rec = CONST_PTR_OFFSET(rec_base, idx * record_size); if (rec->uid < uid) left_idx = idx+1; else if (rec->uid > uid) right_idx = idx; else break; } i_assert(idx < map->hdr.messages_count); rec = CONST_PTR_OFFSET(rec_base, idx * record_size); if (rec->uid != uid) { if (nearest_side > 0) { /* we want uid or larger */ return rec->uid > uid ? idx+1 : (idx == map->hdr.messages_count-1 ? 0 : idx+2); } else { /* we want uid or smaller */ return rec->uid < uid ? idx + 1 : idx; } } return idx+1; } void mail_index_map_lookup_seq_range(struct mail_index_map *map, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r) { i_assert(first_uid > 0); i_assert(first_uid <= last_uid); if (map->hdr.messages_count == 0) { *first_seq_r = *last_seq_r = 0; return; } *first_seq_r = mail_index_bsearch_uid(map, first_uid, 0, 1); if (*first_seq_r == 0 || MAIL_INDEX_REC_AT_SEQ(map, *first_seq_r)->uid > last_uid) { *first_seq_r = *last_seq_r = 0; return; } if (last_uid >= map->hdr.next_uid-1) { /* we want the last message */ last_uid = map->hdr.next_uid-1; if (first_uid > last_uid) { *first_seq_r = *last_seq_r = 0; return; } *last_seq_r = map->hdr.messages_count; return; } if (first_uid == last_uid) *last_seq_r = *first_seq_r; else { /* optimization - binary lookup only from right side: */ *last_seq_r = mail_index_bsearch_uid(map, last_uid, *first_seq_r - 1, -1); } i_assert(*last_seq_r >= *first_seq_r); } dovecot-2.2.33.2/src/lib-index/mailbox-log.h0000644000175000017500000000241713123174404015355 00000000000000#ifndef MAILBOX_LOG_H #define MAILBOX_LOG_H #include "guid.h" enum mailbox_log_record_type { MAILBOX_LOG_RECORD_DELETE_MAILBOX = 1, MAILBOX_LOG_RECORD_DELETE_DIR, MAILBOX_LOG_RECORD_RENAME, MAILBOX_LOG_RECORD_SUBSCRIBE, MAILBOX_LOG_RECORD_UNSUBSCRIBE, MAILBOX_LOG_RECORD_CREATE_DIR }; struct mailbox_log_record { uint8_t type; uint8_t padding[3]; guid_128_t mailbox_guid; uint8_t timestamp[4]; }; struct mailbox_log *mailbox_log_alloc(const char *path); void mailbox_log_free(struct mailbox_log **log); void mailbox_log_set_permissions(struct mailbox_log *log, mode_t mode, gid_t gid, const char *gid_origin); void mailbox_log_record_set_timestamp(struct mailbox_log_record *rec, time_t stamp); time_t mailbox_log_record_get_timestamp(const struct mailbox_log_record *rec); /* Append a new record to mailbox log. Returns 0 if ok, -1 if error. */ int mailbox_log_append(struct mailbox_log *log, const struct mailbox_log_record *rec); /* Iterate through all records in mailbox log. */ struct mailbox_log_iter *mailbox_log_iter_init(struct mailbox_log *log); const struct mailbox_log_record * mailbox_log_iter_next(struct mailbox_log_iter *iter); /* Returns 0 if ok, -1 if I/O error. */ int mailbox_log_iter_deinit(struct mailbox_log_iter **iter); #endif dovecot-2.2.33.2/src/lib-index/test-mail-index-sync-ext.c0000644000175000017500000000645313165463624017731 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-sync-private.h" #include "mail-index-modseq.h" void mail_index_sync_set_corrupted(struct mail_index_sync_map_ctx *ctx ATTR_UNUSED, const char *fmt ATTR_UNUSED, ...) {} struct mail_index_map * mail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx) { return ctx->view->map; } uint32_t mail_index_map_register_ext(struct mail_index_map *map ATTR_UNUSED, const char *name ATTR_UNUSED, uint32_t ext_offset ATTR_UNUSED, const struct mail_index_ext_header *ext_hdr ATTR_UNUSED) { return 0; } bool mail_index_ext_lookup(struct mail_index *index ATTR_UNUSED, const char *name ATTR_UNUSED, uint32_t *ext_id_r ATTR_UNUSED) { return FALSE; } bool mail_index_map_lookup_ext(struct mail_index_map *map ATTR_UNUSED, const char *name ATTR_UNUSED, uint32_t *idx_r ATTR_UNUSED) { return FALSE; } int mail_index_map_ext_hdr_check(const struct mail_index_header *hdr ATTR_UNUSED, const struct mail_index_ext_header *ext_hdr ATTR_UNUSED, const char *name ATTR_UNUSED, const char **error_r ATTR_UNUSED) { return -1; } void mail_index_modseq_hdr_update(struct mail_index_modseq_sync *ctx ATTR_UNUSED) {} bool mail_index_lookup_seq(struct mail_index_view *view ATTR_UNUSED, uint32_t uid, uint32_t *seq_r) { *seq_r = uid; return TRUE; } static void test_mail_index_sync_ext_atomic_inc(void) { struct mail_index_sync_map_ctx ctx; struct mail_transaction_ext_atomic_inc u; struct mail_index_ext *ext; void *ptr; test_begin("mail index sync ext atomic inc"); i_zero(&ctx); ctx.view = t_new(struct mail_index_view, 1); ctx.view->map = t_new(struct mail_index_map, 1); ctx.view->map->hdr.next_uid = 2; ctx.view->map->hdr.record_size = sizeof(struct mail_index_record) + 16; ctx.view->map->rec_map = t_new(struct mail_index_record_map, 1); ctx.view->map->rec_map->records = t_malloc(ctx.view->map->hdr.record_size); t_array_init(&ctx.view->map->extensions, 4); ext = array_append_space(&ctx.view->map->extensions); ext->record_offset = sizeof(struct mail_index_record); ptr = PTR_OFFSET(ctx.view->map->rec_map->records, ext->record_offset); i_zero(&u); test_assert(mail_index_sync_ext_atomic_inc(&ctx, &u) == -1); u.uid = 2; test_assert(mail_index_sync_ext_atomic_inc(&ctx, &u) == -1); u.uid = 1; #define TEST_ATOMIC(_type, _value, _diff, _ret) \ { _type *n = ptr; *n = _value; } \ ctx.cur_ext_record_size = sizeof(_type); \ u.diff = _diff; \ test_assert(mail_index_sync_ext_atomic_inc(&ctx, &u) == _ret); #define TEST_ATOMIC_BLOCK(_type, _max) \ TEST_ATOMIC(_type, 1, -1, 1); \ TEST_ATOMIC(_type, 1, -2, -1); \ TEST_ATOMIC(_type, 0, -1, -1); \ TEST_ATOMIC(_type, 0, _max, 1); \ TEST_ATOMIC(_type, 1, _max, -1); \ TEST_ATOMIC(_type, 0, (_max+1), -1); \ TEST_ATOMIC(_type, _max, 1, -1); \ TEST_ATOMIC(_type, _max, -_max, 1); \ TEST_ATOMIC(_type, _max, -(_max+1), -1); TEST_ATOMIC_BLOCK(uint8_t, 255); TEST_ATOMIC_BLOCK(uint16_t, 65535); ctx.cur_ext_record_size = 5; u.diff = 0; test_assert(mail_index_sync_ext_atomic_inc(&ctx, &u) == -1); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_index_sync_ext_atomic_inc, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index.h0000644000175000017500000007506413165463624015215 00000000000000#ifndef MAIL_INDEX_H #define MAIL_INDEX_H #include "file-lock.h" #include "fsync-mode.h" #include "guid.h" #include "mail-types.h" #include "seq-range-array.h" #define MAIL_INDEX_MAJOR_VERSION 7 #define MAIL_INDEX_MINOR_VERSION 3 #define MAIL_INDEX_HEADER_MIN_SIZE 120 /* Log a warning when transaction log has been locked for this many seconds. This lock is held also between mail_index_sync_begin()..commit(). */ #define MAIL_TRANSACTION_LOG_LOCK_WARN_SECS 30 enum mail_index_open_flags { /* Create index if it doesn't exist */ MAIL_INDEX_OPEN_FLAG_CREATE = 0x01, /* Don't try to mmap() index files */ MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE = 0x04, /* Rely on O_EXCL when creating dotlocks */ MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL = 0x10, /* Flush NFS attr/data/write cache when necessary */ MAIL_INDEX_OPEN_FLAG_NFS_FLUSH = 0x40, /* Open the index read-only */ MAIL_INDEX_OPEN_FLAG_READONLY = 0x80, /* Create backups of dovecot.index files once in a while */ MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS = 0x100, /* If we run out of disk space, fail modifications instead of moving indexes to memory. */ MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY = 0x200, /* We're only going to save new messages to the index. Avoid unnecessary reads. */ MAIL_INDEX_OPEN_FLAG_SAVEONLY = 0x400, /* Enable debug logging */ MAIL_INDEX_OPEN_FLAG_DEBUG = 0x800, /* MAIL_INDEX_MAIL_FLAG_DIRTY can be used as a backend-specific flag. All special handling of the flag is disabled by this. */ MAIL_INDEX_OPEN_FLAG_NO_DIRTY = 0x1000, }; enum mail_index_header_compat_flags { MAIL_INDEX_COMPAT_LITTLE_ENDIAN = 0x01 }; enum mail_index_header_flag { /* Index file is corrupted, reopen or recreate it. */ MAIL_INDEX_HDR_FLAG_CORRUPTED = 0x0001, MAIL_INDEX_HDR_FLAG_HAVE_DIRTY = 0x0002, /* Index has been fsck'd. The caller may want to resync the index to make sure it's valid and drop this flag. */ MAIL_INDEX_HDR_FLAG_FSCKD = 0x0004, }; enum mail_index_mail_flags { /* For private use by backend. Replacing flags doesn't change this. */ MAIL_INDEX_MAIL_FLAG_BACKEND = 0x40, /* Message flags haven't been written to backend. If MAIL_INDEX_OPEN_FLAG_NO_DIRTY is set, this is treated as a backend-specific flag with no special internal handling. */ MAIL_INDEX_MAIL_FLAG_DIRTY = 0x80, /* Force updating this message's modseq via a flag update record */ MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ = 0x100 }; #define MAIL_INDEX_FLAGS_MASK \ (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DELETED | MAIL_SEEN | MAIL_DRAFT) struct mail_index_header { /* major version is increased only when you can't have backwards compatibility. minor version is increased when header size is increased to contain new non-critical fields. */ uint8_t major_version; uint8_t minor_version; uint16_t base_header_size; uint32_t header_size; /* base + extended header size */ uint32_t record_size; uint8_t compat_flags; /* enum mail_index_header_compat_flags */ uint8_t unused[3]; uint32_t indexid; uint32_t flags; uint32_t uid_validity; uint32_t next_uid; uint32_t messages_count; uint32_t unused_old_recent_messages_count; uint32_t seen_messages_count; uint32_t deleted_messages_count; uint32_t first_recent_uid; /* these UIDs may not exist and may not even be unseen/deleted */ uint32_t first_unseen_uid_lowwater; uint32_t first_deleted_uid_lowwater; uint32_t log_file_seq; /* non-external records between tail..head haven't been committed to mailbox yet. */ uint32_t log_file_tail_offset; uint32_t log_file_head_offset; uint32_t unused_old_sync_size_part1; /* Timestamp of when .log was rotated into .log.2. This can be used to optimize checking when it's time to unlink it without stat()ing it. 0 = unknown, -1 = .log.2 doesn't exists. */ uint32_t log2_rotate_time; uint32_t last_temp_file_scan; /* daily first UIDs that have been added to index. */ uint32_t day_stamp; uint32_t day_first_uid[8]; }; #define MAIL_INDEX_RECORD_MIN_SIZE (sizeof(uint32_t) + sizeof(uint8_t)) struct mail_index_record { uint32_t uid; uint8_t flags; /* enum mail_flags | enum mail_index_mail_flags */ }; struct mail_keywords { struct mail_index *index; unsigned int count; int refcount; /* variable sized list of keyword indexes */ unsigned int idx[1]; }; enum mail_index_transaction_flags { /* If transaction is marked as hidden, the changes are marked with hidden=TRUE when the view is synchronized. */ MAIL_INDEX_TRANSACTION_FLAG_HIDE = 0x01, /* External transactions describe changes to mailbox that have already happened. */ MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL = 0x02, /* Don't add flag updates unless they actually change something. This is reliable only when syncing, otherwise someone else might have already committed a transaction that had changed the flags. */ MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES = 0x04, /* fsync() this transaction (unless fsyncs are disabled) */ MAIL_INDEX_TRANSACTION_FLAG_FSYNC = 0x08, /* Sync transaction describes changes to mailbox that already happened to another mailbox with whom we're syncing with (dsync) */ MAIL_INDEX_TRANSACTION_FLAG_SYNC = 0x10 }; enum mail_index_sync_type { MAIL_INDEX_SYNC_TYPE_EXPUNGE = 0x02, MAIL_INDEX_SYNC_TYPE_FLAGS = 0x04, MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD = 0x08, MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE = 0x10 }; enum mail_index_fsync_mask { MAIL_INDEX_FSYNC_MASK_APPENDS = 0x01, MAIL_INDEX_FSYNC_MASK_EXPUNGES = 0x02, MAIL_INDEX_FSYNC_MASK_FLAGS = 0x04, MAIL_INDEX_FSYNC_MASK_KEYWORDS = 0x08 }; enum mail_index_sync_flags { /* Resync all dirty messages' flags. */ MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY = 0x01, /* Drop recent flags from all messages */ MAIL_INDEX_SYNC_FLAG_DROP_RECENT = 0x02, /* Create the transaction with AVOID_FLAG_UPDATES flag */ MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES = 0x04, /* If there are no new transactions and nothing else to do, return 0 in mail_index_sync_begin() */ MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES = 0x08, /* Create the transaction with FSYNC flag */ MAIL_INDEX_SYNC_FLAG_FSYNC = 0x10, /* If we see "delete index" request transaction, finish it. This flag also allows committing more changes to a deleted index. */ MAIL_INDEX_SYNC_FLAG_DELETING_INDEX = 0x20, /* Same as MAIL_INDEX_SYNC_FLAG_DELETING_INDEX, but finish index deletion only once and fail the rest (= avoid race conditions when multiple processes try to mark the index deleted) */ MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX = 0x40, /* Update header's tail_offset to head_offset, even if it's the only thing we do and there's no strict need for it. */ MAIL_INDEX_SYNC_FLAG_UPDATE_TAIL_OFFSET = 0x80 }; enum mail_index_view_sync_flags { /* Don't sync expunges */ MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES = 0x01, /* Make sure view isn't inconsistent after syncing. This also means that you don't care about view_sync_next()'s output, so it won't return anything. */ MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT = 0x02 }; struct mail_index_sync_rec { uint32_t uid1, uid2; enum mail_index_sync_type type; /* MAIL_INDEX_SYNC_TYPE_FLAGS: */ uint8_t add_flags; uint8_t remove_flags; /* MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD, .._REMOVE: */ unsigned int keyword_idx; /* MAIL_INDEX_SYNC_TYPE_EXPUNGE: */ guid_128_t guid_128; }; enum mail_index_view_sync_type { /* Flags or keywords changed */ MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS = 0x01, MAIL_INDEX_VIEW_SYNC_TYPE_MODSEQ = 0x02 }; struct mail_index_view_sync_rec { uint32_t uid1, uid2; enum mail_index_view_sync_type type; /* TRUE if this was a hidden transaction. */ unsigned int hidden:1; }; struct mail_index_transaction_commit_result { /* seq/offset points to end of transaction */ uint32_t log_file_seq; uoff_t log_file_offset; /* number of bytes in the written transaction. all of it was written to the same file. */ uoff_t commit_size; unsigned int ignored_modseq_changes; }; struct mail_index; struct mail_index_map; struct mail_index_view; struct mail_index_transaction; struct mail_index_sync_ctx; struct mail_index_view_sync_ctx; struct mail_index *mail_index_alloc(const char *dir, const char *prefix); void mail_index_free(struct mail_index **index); /* Specify how often to do fsyncs. If mode is FSYNC_MODE_OPTIMIZED, the mask can be used to specify which transaction types to fsync. */ void mail_index_set_fsync_mode(struct mail_index *index, enum fsync_mode mode, enum mail_index_fsync_mask mask); /* Try to set the index's permissions based on its index directory. Returns TRUE if successful (directory existed), FALSE if mail_index_set_permissions() should be called. */ bool mail_index_use_existing_permissions(struct mail_index *index); void mail_index_set_permissions(struct mail_index *index, mode_t mode, gid_t gid, const char *gid_origin); /* Set locking method and maximum time to wait for a lock (UINT_MAX = default). */ void mail_index_set_lock_method(struct mail_index *index, enum file_lock_method lock_method, unsigned int max_timeout_secs); /* Rotate transaction log after it's a) min_size or larger and it was created at least min_created_ago_secs or b) larger than max_size. Delete .log.2 when it's older than log2_stale_secs. The defaults are min_size=32kB, max_size=1M, min_created_ago_secs=5min, log2_stale_secs=2d. */ void mail_index_set_log_rotation(struct mail_index *index, uoff_t min_size, uoff_t max_size, unsigned int min_created_ago_secs, unsigned int log2_stale_secs); /* When creating a new index file or reseting an existing one, add the given extension header data immediately to it. */ void mail_index_set_ext_init_data(struct mail_index *index, uint32_t ext_id, const void *data, size_t size); /* Open index. Returns 1 if ok, 0 if index doesn't exist and CREATE flags wasn't given, -1 if error. */ int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags); /* Open or create index. Returns 0 if ok, -1 if error. */ int mail_index_open_or_create(struct mail_index *index, enum mail_index_open_flags flags); void mail_index_close(struct mail_index *index); /* unlink() all the index files. */ int mail_index_unlink(struct mail_index *index); /* Returns TRUE if index is currently in memory. */ bool mail_index_is_in_memory(struct mail_index *index); /* Move the index into memory. Returns 0 if ok, -1 if error occurred. */ int mail_index_move_to_memory(struct mail_index *index); struct mail_cache *mail_index_get_cache(struct mail_index *index); /* Refresh index so mail_index_lookup*() will return latest values. Note that immediately after this call there may already be changes, so if you need to rely on validity of the returned values, use some external locking for it. */ int ATTR_NOWARN_UNUSED_RESULT mail_index_refresh(struct mail_index *index); /* View can be used to look into index. Sequence numbers inside view change only when you synchronize it. The view acquires required locks automatically, but you'll have to drop them manually. */ struct mail_index_view *mail_index_view_open(struct mail_index *index); void mail_index_view_close(struct mail_index_view **view); /* Returns the index for given view. */ struct mail_index *mail_index_view_get_index(struct mail_index_view *view); /* Returns number of mails in view. */ uint32_t mail_index_view_get_messages_count(struct mail_index_view *view); /* Returns TRUE if we lost track of changes for some reason. */ bool mail_index_view_is_inconsistent(struct mail_index_view *view); /* Returns number of transactions open for the view. */ unsigned int mail_index_view_get_transaction_count(struct mail_index_view *view); /* Transaction has to be opened to be able to modify index. You can have multiple transactions open simultaneously. Committed transactions won't show up until you've synchronized the view. Expunges won't show up until you've synchronized the mailbox (mail_index_sync_begin). */ struct mail_index_transaction * mail_index_transaction_begin(struct mail_index_view *view, enum mail_index_transaction_flags flags); int mail_index_transaction_commit(struct mail_index_transaction **t); int mail_index_transaction_commit_full(struct mail_index_transaction **t, struct mail_index_transaction_commit_result *result_r); void mail_index_transaction_rollback(struct mail_index_transaction **t); /* Discard all changes in the transaction. */ void mail_index_transaction_reset(struct mail_index_transaction *t); /* When committing transaction, drop flag/keyword updates for messages whose mdoseq is larger than max_modseq. Save those messages' sequences to the given array. */ void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t, uint64_t max_modseq, ARRAY_TYPE(seq_range) *seqs); /* Returns the resulting highest-modseq after this commit. This can be called only if transaction log is locked, which normally means only during mail index syncing. If there are any appends, they all must have been assigned UIDs before calling this. */ uint64_t mail_index_transaction_get_highest_modseq(struct mail_index_transaction *t); /* Returns the view transaction was created for. */ struct mail_index_view * mail_index_transaction_get_view(struct mail_index_transaction *t); /* Returns TRUE if the given sequence is being expunged in this transaction. */ bool mail_index_transaction_is_expunged(struct mail_index_transaction *t, uint32_t seq); /* Returns a view containing the mailbox state after changes in transaction are applied. The view can still be used after transaction has been committed. */ struct mail_index_view * mail_index_transaction_open_updated_view(struct mail_index_transaction *t); /* Begin synchronizing mailbox with index file. Returns 1 if ok, 0 if MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES is set and there's nothing to sync, -1 if error. mail_index_sync_next() returns all changes from previously committed transactions which haven't yet been committed to the actual mailbox. They're returned in ascending order and they never overlap (if we add more sync types, then they might). You must go through all of them and update the mailbox accordingly. Changes done to the returned transaction are expected to describe the mailbox's current state. The returned view already contains all the changes (except expunge requests). After applying sync records on top of backend flags they should match flags in the view. If they don't, there have been external changes. Returned expunges are treated as expunge requests. They're not really removed from the index until you mark them expunged to the returned transaction. If it's not possible to expunge the message (e.g. permission denied), simply don't mark them expunged. Returned sequence numbers describe the mailbox state at the beginning of synchronization, ie. expunges don't affect them. */ int mail_index_sync_begin(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, enum mail_index_sync_flags flags); /* Like mail_index_sync_begin(), but returns 1 if OK and if index is already synchronized up to the given log_file_seq+offset, the synchronization isn't started and this function returns 0. This should be done when you wish to sync your committed transaction instead of doing a full mailbox synchronization. */ int mail_index_sync_begin_to(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, uint32_t log_file_seq, uoff_t log_file_offset, enum mail_index_sync_flags flags); /* Returns TRUE if it currently looks like syncing would return changes. */ bool mail_index_sync_have_any(struct mail_index *index, enum mail_index_sync_flags flags); /* Returns TRUE if it currently looks like syncing would return expunges. */ bool mail_index_sync_have_any_expunges(struct mail_index *index); /* Returns the log file seq+offsets for the area which this sync is handling. */ void mail_index_sync_get_offsets(struct mail_index_sync_ctx *ctx, uint32_t *seq1_r, uoff_t *offset1_r, uint32_t *seq2_r, uoff_t *offset2_r); /* Returns -1 if error, 0 if sync is finished, 1 if record was filled. */ bool mail_index_sync_next(struct mail_index_sync_ctx *ctx, struct mail_index_sync_rec *sync_rec); /* Returns TRUE if there's more to sync. */ bool mail_index_sync_have_more(struct mail_index_sync_ctx *ctx); /* Returns TRUE if sync has any expunges to handle. */ bool mail_index_sync_has_expunges(struct mail_index_sync_ctx *ctx); /* Reset syncing to initial state after mail_index_sync_begin(), so you can go through all the sync records again with mail_index_sync_next(). */ void mail_index_sync_reset(struct mail_index_sync_ctx *ctx); /* Update result when refreshing index at the end of sync. */ void mail_index_sync_set_commit_result(struct mail_index_sync_ctx *ctx, struct mail_index_transaction_commit_result *result); /* Don't log a warning even if syncing took over MAIL_TRANSACTION_LOG_LOCK_WARN_SECS seconds. Usually this is called because the caller itself already logged a warning about it. */ void mail_index_sync_no_warning(struct mail_index_sync_ctx *ctx); /* If a warning is logged because syncing took over MAIL_TRANSACTION_LOG_LOCK_WARN_SECS seconds, log this as the reason for the syncing. */ void mail_index_sync_set_reason(struct mail_index_sync_ctx *ctx, const char *reason); /* Commit synchronization by writing all changes to mail index file. */ int mail_index_sync_commit(struct mail_index_sync_ctx **ctx); /* Rollback synchronization - none of the changes listed by sync_next() are actually written to index file. */ void mail_index_sync_rollback(struct mail_index_sync_ctx **ctx); /* Mark index file corrupted. Invalidates all views. */ void mail_index_mark_corrupted(struct mail_index *index); /* Check and fix any found problems. Returns -1 if we couldn't lock for sync, 0 if everything went ok. */ int mail_index_fsck(struct mail_index *index); /* Returns TRUE if mail_index_fsck() has been called since the last mail_index_reset_fscked() call. */ bool mail_index_reset_fscked(struct mail_index *index); /* Synchronize changes in view. You have to go through all records, or view will be marked inconsistent. Only sync_mask type records are synchronized. */ struct mail_index_view_sync_ctx * mail_index_view_sync_begin(struct mail_index_view *view, enum mail_index_view_sync_flags flags); bool mail_index_view_sync_next(struct mail_index_view_sync_ctx *ctx, struct mail_index_view_sync_rec *sync_rec); void mail_index_view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx, const ARRAY_TYPE(seq_range) **expunges_r); int mail_index_view_sync_commit(struct mail_index_view_sync_ctx **ctx, bool *delayed_expunges_r); /* Returns the index header. */ const struct mail_index_header * mail_index_get_header(struct mail_index_view *view); /* Returns the wanted message record. */ const struct mail_index_record * mail_index_lookup(struct mail_index_view *view, uint32_t seq); const struct mail_index_record * mail_index_lookup_full(struct mail_index_view *view, uint32_t seq, struct mail_index_map **map_r); /* Returns TRUE if the given message has already been expunged from index. */ bool mail_index_is_expunged(struct mail_index_view *view, uint32_t seq); /* Note that returned keyword indexes aren't sorted. */ void mail_index_lookup_keywords(struct mail_index_view *view, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx); /* Return keywords from given map. */ void mail_index_map_lookup_keywords(struct mail_index_map *map, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx); /* mail_index_lookup[_keywords]() returns the latest flag changes. This function instead attempts to return the flags and keywords done by the last view sync. */ void mail_index_lookup_view_flags(struct mail_index_view *view, uint32_t seq, enum mail_flags *flags_r, ARRAY_TYPE(keyword_indexes) *keyword_idx); /* Returns the UID for given message. May be slightly faster than mail_index_lookup()->uid. */ void mail_index_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r); /* Convert UID range to sequence range. If no UIDs are found, returns FALSE and sequences are set to 0. Note that any of the returned sequences may have been expunged already. */ bool mail_index_lookup_seq_range(struct mail_index_view *view, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r); bool mail_index_lookup_seq(struct mail_index_view *view, uint32_t uid, uint32_t *seq_r); /* Find first mail with (mail->flags & flags_mask) == flags. Useful mostly for taking advantage of lowwater-fields in headers. */ void mail_index_lookup_first(struct mail_index_view *view, enum mail_flags flags, uint8_t flags_mask, uint32_t *seq_r); /* Append a new record to index. */ void mail_index_append(struct mail_index_transaction *t, uint32_t uid, uint32_t *seq_r); /* Assign UIDs for mails with uid=0 or uidlog->index, file->filepath, function); } static void mail_transaction_log_mark_corrupted(struct mail_transaction_log_file *file) { unsigned int offset = offsetof(struct mail_transaction_log_header, indexid); int flags; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file) || file->log->index->readonly) return; /* indexid=0 marks the log file as corrupted. we opened the file with O_APPEND, and now we need to drop it for pwrite() to work (at least in Linux) */ flags = fcntl(file->fd, F_GETFL, 0); if (flags < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "fcntl(F_GETFL)"); return; } if (fcntl(file->fd, F_SETFL, flags & ~O_APPEND) < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "fcntl(F_SETFL)"); return; } if (pwrite_full(file->fd, &file->hdr.indexid, sizeof(file->hdr.indexid), offset) < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "pwrite()"); } } void mail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file, const char *fmt, ...) { va_list va; file->corrupted = TRUE; file->hdr.indexid = 0; mail_transaction_log_mark_corrupted(file); va_start(va, fmt); T_BEGIN { mail_index_set_error(file->log->index, "Corrupted transaction log file %s seq %u: %s " "(sync_offset=%"PRIuUOFF_T")", file->filepath, file->hdr.file_seq, t_strdup_vprintf(fmt, va), file->sync_offset); } T_END; va_end(va); } struct mail_transaction_log_file * mail_transaction_log_file_alloc(struct mail_transaction_log *log, const char *path) { struct mail_transaction_log_file *file; file = i_new(struct mail_transaction_log_file, 1); file->log = log; file->filepath = i_strdup(path); file->fd = -1; return file; } void mail_transaction_log_file_free(struct mail_transaction_log_file **_file) { struct mail_transaction_log_file *file = *_file; struct mail_transaction_log_file **p; int old_errno = errno; *_file = NULL; i_assert(!file->locked); for (p = &file->log->files; *p != NULL; p = &(*p)->next) { if (*p == file) { *p = file->next; break; } } if (file == file->log->head) file->log->head = NULL; if (file->buffer != NULL) buffer_free(&file->buffer); if (file->mmap_base != NULL) { if (munmap(file->mmap_base, file->mmap_size) < 0) log_file_set_syscall_error(file, "munmap()"); } if (file->fd != -1) { if (close(file->fd) < 0) log_file_set_syscall_error(file, "close()"); } i_free(file->filepath); i_free(file); errno = old_errno; } static void mail_transaction_log_file_skip_to_head(struct mail_transaction_log_file *file) { struct mail_transaction_log *log = file->log; struct mail_index_map *map = log->index->map; const struct mail_index_modseq_header *modseq_hdr; uoff_t head_offset; if (map == NULL || file->hdr.file_seq != map->hdr.log_file_seq || map->hdr.log_file_head_offset == 0) return; /* we can get a valid log offset from index file. initialize sync_offset from it so we don't have to read the whole log file from beginning. */ head_offset = map->hdr.log_file_head_offset; modseq_hdr = mail_index_map_get_modseq_header(map); if (head_offset < file->hdr.hdr_size) { mail_index_set_error(log->index, "%s: log_file_head_offset too small", log->index->filepath); file->sync_offset = file->hdr.hdr_size; file->sync_highest_modseq = file->hdr.initial_modseq; } else if (modseq_hdr == NULL && file->hdr.initial_modseq == 0) { /* modseqs not used yet */ file->sync_offset = head_offset; file->sync_highest_modseq = 0; } else if (modseq_hdr == NULL || modseq_hdr->log_seq != file->hdr.file_seq) { /* highest_modseq not synced, start from beginning */ file->sync_offset = file->hdr.hdr_size; file->sync_highest_modseq = file->hdr.initial_modseq; } else if (modseq_hdr->log_offset > head_offset) { mail_index_set_error(log->index, "%s: modseq_hdr.log_offset too large", log->index->filepath); file->sync_offset = file->hdr.hdr_size; file->sync_highest_modseq = file->hdr.initial_modseq; } else { /* start from where we last stopped tracking modseqs */ file->sync_offset = modseq_hdr->log_offset; file->sync_highest_modseq = modseq_hdr->highest_modseq; } if (file->hdr.file_seq == log->index->map->hdr.log_file_seq) { file->saved_tail_offset = log->index->map->hdr.log_file_tail_offset; file->saved_tail_sync_offset = file->saved_tail_offset; } if (file->saved_tail_offset > file->max_tail_offset) file->max_tail_offset = file->saved_tail_offset; } static void mail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file) { struct mail_transaction_log_file **p; const char *reason; bool retry; file->sync_offset = file->hdr.hdr_size; file->sync_highest_modseq = file->hdr.initial_modseq; mail_transaction_log_file_skip_to_head(file); /* insert it to correct position */ for (p = &file->log->files; *p != NULL; p = &(*p)->next) { if ((*p)->hdr.file_seq > file->hdr.file_seq) break; i_assert((*p)->hdr.file_seq < file->hdr.file_seq); } file->next = *p; *p = file; if (file->buffer != NULL) { /* if we read any unfinished data, make sure the buffer gets truncated. */ (void)mail_transaction_log_file_sync(file, &retry, &reason); buffer_set_used_size(file->buffer, file->sync_offset - file->buffer_offset); } } static int mail_transaction_log_init_hdr(struct mail_transaction_log *log, struct mail_transaction_log_header *hdr) { struct mail_index *index = log->index; struct mail_transaction_log_file *file; i_assert(index->indexid != 0); i_zero(hdr); hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION; hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION; hdr->hdr_size = sizeof(struct mail_transaction_log_header); hdr->indexid = log->index->indexid; hdr->create_stamp = ioloop_time; #if !WORDS_BIGENDIAN hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #endif if (index->fd != -1) { /* not creating index - make sure we have latest header */ if (!index->mapping) { if (mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0) return -1; } else { /* if we got here from mapping, the .log file is corrupted. use whatever values we got from index file */ } } if (index->map != NULL) { hdr->prev_file_seq = index->map->hdr.log_file_seq; hdr->prev_file_offset = index->map->hdr.log_file_head_offset; hdr->file_seq = index->map->hdr.log_file_seq + 1; hdr->initial_modseq = mail_index_map_modseq_get_highest(index->map); } else { hdr->file_seq = 1; } if (hdr->initial_modseq == 0) { /* modseq tracking in log files is required for many reasons nowadays, even if per-message modseqs aren't enabled in dovecot.index. */ hdr->initial_modseq = 1; } if (log->head != NULL) { /* make sure the sequence always increases to avoid crashes later. this catches the buggy case where two processes happen to replace the same log file. */ for (file = log->head->next; file != NULL; file = file->next) { if (hdr->file_seq <= file->hdr.file_seq) hdr->file_seq = file->hdr.file_seq + 1; } if (hdr->file_seq <= log->head->hdr.file_seq) { /* make sure the sequence grows */ hdr->file_seq = log->head->hdr.file_seq+1; } if (hdr->initial_modseq < log->head->sync_highest_modseq) { /* this should be always up-to-date */ hdr->initial_modseq = log->head->sync_highest_modseq; } } return 0; } struct mail_transaction_log_file * mail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log) { struct mail_transaction_log_file *file; file = mail_transaction_log_file_alloc(log, MEMORY_LOG_NAME); if (mail_transaction_log_init_hdr(log, &file->hdr) < 0) { i_free(file); return NULL; } file->buffer = buffer_create_dynamic(default_pool, 4096); file->buffer_offset = sizeof(file->hdr); mail_transaction_log_file_add_to_list(file); return file; } static int mail_transaction_log_file_dotlock(struct mail_transaction_log_file *file) { struct dotlock_settings dotlock_set; int ret; if (file->log->dotlock_count > 0) ret = 1; else { mail_transaction_log_get_dotlock_set(file->log, &dotlock_set); ret = file_dotlock_create(&dotlock_set, file->filepath, 0, &file->log->dotlock); } if (ret > 0) { file->log->dotlock_count++; file->locked = TRUE; file->lock_created = time(NULL); return 0; } if (ret < 0) { log_file_set_syscall_error(file, "file_dotlock_create()"); return -1; } mail_index_set_error(file->log->index, "Timeout (%us) while waiting for " "dotlock for transaction log file %s", dotlock_set.timeout, file->filepath); file->log->index->index_lock_timeout = TRUE; return -1; } static int mail_transaction_log_file_undotlock(struct mail_transaction_log_file *file) { int ret; if (--file->log->dotlock_count > 0) return 0; ret = file_dotlock_delete(&file->log->dotlock); if (ret < 0) { log_file_set_syscall_error(file, "file_dotlock_delete()"); return -1; } if (ret == 0) { mail_index_set_error(file->log->index, "Dotlock was lost for transaction log file %s", file->filepath); return -1; } return 0; } int mail_transaction_log_file_lock(struct mail_transaction_log_file *file) { unsigned int lock_timeout_secs; int ret; if (file->locked) return 0; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) { file->locked = TRUE; return 0; } if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) return mail_transaction_log_file_dotlock(file); if (file->log->index->readonly) { mail_index_set_error(file->log->index, "Index is read-only, can't write-lock %s", file->filepath); return -1; } i_assert(file->file_lock == NULL); lock_timeout_secs = I_MIN(MAIL_TRANSACTION_LOG_LOCK_TIMEOUT, file->log->index->max_lock_timeout_secs); ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd, F_WRLCK, lock_timeout_secs, &file->file_lock); if (ret > 0) { file->locked = TRUE; file->lock_created = time(NULL); return 0; } if (ret < 0) { log_file_set_syscall_error(file, "mail_index_wait_lock_fd()"); return -1; } mail_index_set_error(file->log->index, "Timeout (%us) while waiting for lock for " "transaction log file %s%s", lock_timeout_secs, file->filepath, file_lock_find(file->fd, file->log->index->lock_method, F_WRLCK)); file->log->index->index_lock_timeout = TRUE; return -1; } void mail_transaction_log_file_unlock(struct mail_transaction_log_file *file, const char *lock_reason) { unsigned int lock_time; if (!file->locked) return; file->locked = FALSE; file->locked_sync_offset_updated = FALSE; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) return; lock_time = time(NULL) - file->lock_created; if (lock_time >= MAIL_TRANSACTION_LOG_LOCK_WARN_SECS && lock_reason != NULL) { i_warning("Transaction log file %s was locked for %u seconds (%s)", file->filepath, lock_time, lock_reason); } if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) { (void)mail_transaction_log_file_undotlock(file); return; } file_unlock(&file->file_lock); } static ssize_t mail_transaction_log_file_read_header(struct mail_transaction_log_file *file) { void *dest; size_t pos, dest_size; ssize_t ret; i_assert(file->buffer == NULL && file->mmap_base == NULL); i_zero(&file->hdr); if (file->last_size < mmap_get_page_size() && file->last_size > 0) { /* just read the entire transaction log to memory. note that if some of the data hasn't been fully committed yet (hdr.size=0), the buffer must be truncated later */ file->buffer = buffer_create_dynamic(default_pool, 4096); file->buffer_offset = 0; dest_size = file->last_size; dest = buffer_append_space_unsafe(file->buffer, dest_size); } else { /* read only the header */ dest = &file->hdr; dest_size = sizeof(file->hdr); } /* it's not necessarily an error to read less than wanted header size, since older versions of the log format used smaller headers. */ pos = 0; do { ret = pread(file->fd, PTR_OFFSET(dest, pos), dest_size - pos, pos); if (ret > 0) pos += ret; } while (ret > 0 && pos < dest_size); if (file->buffer != NULL) { buffer_set_used_size(file->buffer, pos); memcpy(&file->hdr, file->buffer->data, I_MIN(pos, sizeof(file->hdr))); } return ret < 0 ? -1 : (ssize_t)pos; } static int mail_transaction_log_file_fail_dupe(struct mail_transaction_log_file *file) { int ret; /* mark the old file corrupted. we can't safely remove it from the list however, so return failure. */ file->hdr.indexid = 0; if (strcmp(file->filepath, file->log->head->filepath) != 0) { /* only mark .2 corrupted, just to make sure we don't lose any changes from .log in case we're somehow wrong */ mail_transaction_log_mark_corrupted(file); ret = 0; } else { ret = -1; } if (!file->corrupted) { file->corrupted = TRUE; mail_index_set_error(file->log->index, "Transaction log %s: " "duplicate transaction log sequence (%u)", file->filepath, file->hdr.file_seq); } return ret; } static int mail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file, bool ignore_estale) { struct mail_transaction_log_file *f; int ret; i_assert(!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)); if (file->corrupted) return 0; ret = mail_transaction_log_file_read_header(file); if (ret < 0) { if (errno != ESTALE || !ignore_estale) log_file_set_syscall_error(file, "pread()"); return -1; } if (file->hdr.major_version != MAIL_TRANSACTION_LOG_MAJOR_VERSION) { /* incompatible version - fix silently */ return 0; } if (ret < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) { mail_transaction_log_file_set_corrupted(file, "unexpected end of file while reading header"); return 0; } const unsigned int hdr_version = MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr); if (MAIL_TRANSACTION_LOG_VERSION_HAVE(hdr_version, COMPAT_FLAGS)) { /* we have compatibility flags */ enum mail_index_header_compat_flags compat_flags = 0; #if !WORDS_BIGENDIAN compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #endif if (file->hdr.compat_flags != compat_flags) { /* architecture change */ mail_index_set_error(file->log->index, "Rebuilding index file %s: " "CPU architecture changed", file->log->index->filepath); return 0; } } if (file->hdr.hdr_size < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) { mail_transaction_log_file_set_corrupted(file, "Header size too small"); return 0; } if (file->hdr.hdr_size < sizeof(file->hdr)) { /* @UNSAFE: smaller than we expected - zero out the fields we shouldn't have filled */ memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0, sizeof(file->hdr) - file->hdr.hdr_size); } if (file->hdr.indexid == 0) { /* corrupted */ file->corrupted = TRUE; mail_index_set_error(file->log->index, "Transaction log file %s: marked corrupted", file->filepath); return 0; } if (file->hdr.indexid != file->log->index->indexid) { if (file->log->index->indexid != 0 && !file->log->index->initial_create) { /* index file was probably just rebuilt and we don't know about it yet */ mail_transaction_log_file_set_corrupted(file, "indexid changed %u -> %u", file->log->index->indexid, file->hdr.indexid); return 0; } /* creating index file. since transaction log is created first, use the indexid in it to create the main index to avoid races. */ file->log->index->indexid = file->hdr.indexid; } /* make sure we already don't have a file with the same sequence opened. it shouldn't happen unless the old log file was corrupted. */ for (f = file->log->files; f != NULL; f = f->next) { if (f->hdr.file_seq == file->hdr.file_seq) { if (strcmp(f->filepath, f->log->head->filepath) != 0) { /* old "f" is the .log.2 */ return mail_transaction_log_file_fail_dupe(f); } else { /* new "file" is probably the .log.2 */ return mail_transaction_log_file_fail_dupe(file); } } } file->sync_highest_modseq = file->hdr.initial_modseq; return 1; } static int mail_transaction_log_file_stat(struct mail_transaction_log_file *file, bool ignore_estale) { struct stat st; if (fstat(file->fd, &st) < 0) { if (!ESTALE_FSTAT(errno) || !ignore_estale) log_file_set_syscall_error(file, "fstat()"); return -1; } file->st_dev = st.st_dev; file->st_ino = st.st_ino; file->last_mtime = st.st_mtime; file->last_size = st.st_size; return 0; } static bool mail_transaction_log_file_is_dupe(struct mail_transaction_log_file *file) { struct mail_transaction_log_file *tmp; for (tmp = file->log->files; tmp != NULL; tmp = tmp->next) { if (tmp->st_ino == file->st_ino && CMP_DEV_T(tmp->st_dev, file->st_dev)) return TRUE; } return FALSE; } static void log_write_ext_hdr_init_data(struct mail_index *index, buffer_t *buf) { const struct mail_index_registered_ext *rext; struct mail_transaction_header *hdr; struct mail_transaction_ext_intro *intro; struct mail_transaction_ext_hdr_update *ext_hdr; unsigned int hdr_offset; rext = array_idx(&index->extensions, index->ext_hdr_init_id); /* introduce the extension */ hdr_offset = buf->used; hdr = buffer_append_space_unsafe(buf, sizeof(*hdr)); hdr->type = MAIL_TRANSACTION_EXT_INTRO; intro = buffer_append_space_unsafe(buf, sizeof(*intro)); intro->ext_id = (uint32_t)-1; intro->hdr_size = rext->hdr_size; intro->record_size = rext->record_size; intro->record_align = rext->record_align; intro->name_size = strlen(rext->name); buffer_append(buf, rext->name, intro->name_size); if (buf->used % 4 != 0) buffer_append_zero(buf, 4 - buf->used % 4); hdr = buffer_get_space_unsafe(buf, hdr_offset, sizeof(*hdr)); hdr->size = mail_index_uint32_to_offset(buf->used - hdr_offset); /* add the extension header data */ hdr_offset = buf->used; hdr = buffer_append_space_unsafe(buf, sizeof(*hdr)); hdr->type = MAIL_TRANSACTION_EXT_HDR_UPDATE; ext_hdr = buffer_append_space_unsafe(buf, sizeof(*ext_hdr)); ext_hdr->size = rext->hdr_size; buffer_append(buf, index->ext_hdr_init_data, rext->hdr_size); hdr = buffer_get_space_unsafe(buf, hdr_offset, sizeof(*hdr)); hdr->size = mail_index_uint32_to_offset(buf->used - hdr_offset); } static int mail_transaction_log_file_create2(struct mail_transaction_log_file *file, int new_fd, bool reset, struct dotlock **dotlock) { struct mail_index *index = file->log->index; struct stat st; const char *path2; buffer_t *writebuf; int fd, ret; bool rename_existing, need_lock; need_lock = file->log->head != NULL && file->log->head->locked; if (fcntl(new_fd, F_SETFL, O_APPEND) < 0) { log_file_set_syscall_error(file, "fcntl(O_APPEND)"); return -1; } if (file->log->nfs_flush) { /* although we check also mtime and file size below, it's done only to fix broken log files. we don't bother flushing attribute cache just for that. */ nfs_flush_file_handle_cache(file->filepath); } /* log creation is locked now - see if someone already created it. note that if we're rotating, we need to keep the log locked until the file has been rewritten. and because fcntl() locks are stupid, if we go and open()+close() the file and we had it already opened, its locks are lost. so we use stat() to check if the file has been recreated, although it almost never is. */ if (reset) rename_existing = FALSE; else if (nfs_safe_stat(file->filepath, &st) < 0) { if (errno != ENOENT) { log_file_set_syscall_error(file, "stat()"); return -1; } rename_existing = FALSE; } else if (st.st_ino == file->st_ino && CMP_DEV_T(st.st_dev, file->st_dev) && /* inode/dev checks are enough when we're rotating the file, but not when we're replacing a broken log file */ st.st_mtime == file->last_mtime && (uoff_t)st.st_size == file->last_size) { /* no-one else recreated the file */ rename_existing = TRUE; } else { /* recreated. use the file if its header is ok */ fd = nfs_safe_open(file->filepath, O_RDWR | O_APPEND); if (fd == -1) { if (errno != ENOENT) { log_file_set_syscall_error(file, "open()"); return -1; } } else { file->fd = fd; file->last_size = 0; if (mail_transaction_log_file_read_hdr(file, FALSE) > 0 && mail_transaction_log_file_stat(file, FALSE) == 0) { /* yes, it was ok */ file_dotlock_delete(dotlock); mail_transaction_log_file_add_to_list(file); return 0; } file->fd = -1; if (close(fd) < 0) log_file_set_syscall_error(file, "close()"); } rename_existing = FALSE; } if (index->fd == -1 && !rename_existing) { /* creating the initial index */ reset = TRUE; } if (mail_transaction_log_init_hdr(file->log, &file->hdr) < 0) return -1; if (reset) { /* don't reset modseqs. if we're reseting due to rebuilding indexes we'll probably want to keep uidvalidity and in such cases we really don't want to shrink modseqs. */ file->hdr.prev_file_seq = 0; file->hdr.prev_file_offset = 0; } writebuf = buffer_create_dynamic(pool_datastack_create(), 128); buffer_append(writebuf, &file->hdr, sizeof(file->hdr)); if (index->ext_hdr_init_data != NULL && reset) log_write_ext_hdr_init_data(index, writebuf); if (write_full(new_fd, writebuf->data, writebuf->used) < 0) { log_file_set_syscall_error(file, "write_full()"); return -1; } if (file->log->index->fsync_mode == FSYNC_MODE_ALWAYS) { /* the header isn't important, so don't bother calling fdatasync() unless it's required */ if (fdatasync(new_fd) < 0) { log_file_set_syscall_error(file, "fdatasync()"); return -1; } } file->fd = new_fd; ret = mail_transaction_log_file_stat(file, FALSE); if (need_lock && ret == 0) { /* we'll need to preserve the lock */ if (mail_transaction_log_file_lock(file) < 0) ret = -1; } /* if we return -1 the dotlock deletion code closes the fd */ file->fd = -1; if (ret < 0) return -1; /* keep two log files */ if (rename_existing) { /* rename() would be nice and easy way to do this, except then there's a race condition between the rename and file_dotlock_replace(). during that time the log file doesn't exist, which could cause problems. */ path2 = t_strconcat(file->filepath, ".2", NULL); if (i_unlink_if_exists(path2) < 0) { /* try to link() anyway */ } if (nfs_safe_link(file->filepath, path2, FALSE) < 0 && errno != ENOENT && errno != EEXIST) { mail_index_set_error(index, "link(%s, %s) failed: %m", file->filepath, path2); /* ignore the error. we don't care that much about the second log file and we're going to overwrite this first one. */ } /* NOTE: here's a race condition where both .log and .log.2 point to the same file. our reading code should ignore that though by comparing the inodes. */ } if (file_dotlock_replace(dotlock, DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0) { /* need to unlock to avoid assert-crash in mail_transaction_log_file_free() */ mail_transaction_log_file_unlock(file, "creation failed"); return -1; } /* success */ file->fd = new_fd; mail_transaction_log_file_add_to_list(file); i_assert(!need_lock || file->locked); return 1; } int mail_transaction_log_file_create(struct mail_transaction_log_file *file, bool reset) { struct mail_index *index = file->log->index; struct dotlock_settings new_dotlock_set; struct dotlock *dotlock; mode_t old_mask; int fd, ret; i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); if (file->log->index->readonly) { mail_index_set_error(index, "Can't create log file %s: Index is read-only", file->filepath); return -1; } if (index->indexid == 0) { mail_index_set_error(index, "Can't create log file %s: Index is marked corrupted", file->filepath); return -1; } mail_transaction_log_get_dotlock_set(file->log, &new_dotlock_set); new_dotlock_set.lock_suffix = LOG_NEW_DOTLOCK_SUFFIX; /* With dotlocking we might already have path.lock created, so this filename has to be different. */ old_mask = umask(index->mode ^ 0666); fd = file_dotlock_open(&new_dotlock_set, file->filepath, 0, &dotlock); umask(old_mask); if (fd == -1) { log_file_set_syscall_error(file, "file_dotlock_open()"); return -1; } mail_index_fchown(index, fd, file_dotlock_get_lock_path(dotlock)); /* either fd gets used or the dotlock gets deleted and returned fd is for the existing file */ ret = mail_transaction_log_file_create2(file, fd, reset, &dotlock); if (ret < 0) { if (dotlock != NULL) file_dotlock_delete(&dotlock); return -1; } return ret; } int mail_transaction_log_file_open(struct mail_transaction_log_file *file, const char **reason_r) { struct mail_index *index = file->log->index; unsigned int i; bool ignore_estale; int ret; for (i = 0;; i++) { if (!index->readonly) { file->fd = nfs_safe_open(file->filepath, O_RDWR | O_APPEND); } else { file->fd = nfs_safe_open(file->filepath, O_RDONLY); } if (file->fd == -1 && errno == EACCES) { file->fd = nfs_safe_open(file->filepath, O_RDONLY); index->readonly = TRUE; } if (file->fd == -1) { if (errno == ENOENT) { *reason_r = "File doesn't exist"; return 0; } log_file_set_syscall_error(file, "open()"); *reason_r = t_strdup_printf("open() failed: %m"); return -1; } ignore_estale = i < MAIL_INDEX_ESTALE_RETRY_COUNT; if (mail_transaction_log_file_stat(file, ignore_estale) < 0) ret = -1; else if (mail_transaction_log_file_is_dupe(file)) { /* probably our already opened .log file has been renamed to .log.2 and we're trying to reopen it. also possible that hit a race condition where .log and .log.2 are linked. */ *reason_r = "File is already open"; return 0; } else { ret = mail_transaction_log_file_read_hdr(file, ignore_estale); } if (ret > 0) { /* success */ break; } if (ret == 0) { /* corrupted */ if (index->readonly) { /* don't delete */ } else { i_unlink_if_exists(file->filepath); } *reason_r = "File is corrupted"; return 0; } if (errno != ESTALE || i == MAIL_INDEX_ESTALE_RETRY_COUNT) { /* syscall error */ *reason_r = t_strdup_printf("fstat() failed: %m"); return -1; } /* ESTALE - try again */ if (file->buffer != NULL) buffer_free(&file->buffer); } mail_transaction_log_file_add_to_list(file); return 1; } static int log_file_track_mailbox_sync_offset_hdr(struct mail_transaction_log_file *file, const void *data, unsigned int trans_size, const char **error_r) { const struct mail_transaction_header_update *u = data; const struct mail_index_header *ihdr; const unsigned int size = trans_size - sizeof(struct mail_transaction_header); const unsigned int offset_pos = offsetof(struct mail_index_header, log_file_tail_offset); const unsigned int offset_size = sizeof(ihdr->log_file_tail_offset); uint32_t tail_offset; i_assert(offset_size == sizeof(tail_offset)); if (size < sizeof(*u) || size < sizeof(*u) + u->size) { *error_r = "header update extends beyond record size"; mail_transaction_log_file_set_corrupted(file, "%s", *error_r); return -1; } if (u->offset <= offset_pos && u->offset + u->size >= offset_pos + offset_size) { memcpy(&tail_offset, CONST_PTR_OFFSET(u + 1, offset_pos - u->offset), sizeof(tail_offset)); if (tail_offset < file->saved_tail_offset) { /* ignore shrinking tail offsets */ return 1; } else if (tail_offset > file->sync_offset + trans_size) { mail_transaction_log_file_set_corrupted(file, "log_file_tail_offset %u goes past sync offset %"PRIuUOFF_T, tail_offset, file->sync_offset + trans_size); } else { file->saved_tail_offset = tail_offset; if (tail_offset > file->max_tail_offset) file->max_tail_offset = tail_offset; return 1; } } return 0; } static bool flag_updates_have_non_internal(const struct mail_transaction_flag_update *u, unsigned int count, unsigned int version) { /* Hide internal flags from modseqs if the log file's version is new enough. This allows upgrading without the modseqs suddenly shrinking. */ if (!MAIL_TRANSACTION_LOG_VERSION_HAVE(version, HIDE_INTERNAL_MODSEQS)) return TRUE; for (unsigned int i = 0; i < count; i++) { if (!MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(&u[i])) return TRUE; } return FALSE; } void mail_transaction_update_modseq(const struct mail_transaction_header *hdr, const void *data, uint64_t *cur_modseq, unsigned int version) { uint32_t trans_size; trans_size = mail_index_offset_to_uint32(hdr->size); i_assert(trans_size != 0); if (*cur_modseq != 0) { /* tracking modseqs */ } else if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) == MAIL_TRANSACTION_EXT_INTRO) { /* modseqs not tracked yet. see if this is a modseq extension introduction. */ const struct mail_transaction_ext_intro *intro = data; const unsigned int modseq_ext_len = strlen(MAIL_INDEX_MODSEQ_EXT_NAME); if (intro->name_size == modseq_ext_len && memcmp(intro + 1, MAIL_INDEX_MODSEQ_EXT_NAME, modseq_ext_len) == 0) { /* modseq tracking started */ *cur_modseq += 1; } return; } else { /* not tracking modseqs */ return; } /* NOTE: keep in sync with mail_index_transaction_get_highest_modseq() */ switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT: case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT: if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* ignore expunge requests */ break; } /* fall through */ case MAIL_TRANSACTION_APPEND: case MAIL_TRANSACTION_KEYWORD_UPDATE: case MAIL_TRANSACTION_KEYWORD_RESET: case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: /* these changes increase modseq */ *cur_modseq += 1; break; case MAIL_TRANSACTION_FLAG_UPDATE: { const struct mail_transaction_flag_update *rec = data; unsigned int count; count = (trans_size - sizeof(*hdr)) / sizeof(*rec); if (flag_updates_have_non_internal(rec, count, version)) *cur_modseq += 1; break; } case MAIL_TRANSACTION_MODSEQ_UPDATE: { const struct mail_transaction_modseq_update *rec, *end; end = CONST_PTR_OFFSET(data, trans_size - sizeof(*hdr)); for (rec = data; rec < end; rec++) { uint64_t modseq = ((uint64_t)rec->modseq_high32 << 32) | rec->modseq_low32; if (*cur_modseq < modseq) *cur_modseq = modseq; } } } } static struct modseq_cache * modseq_cache_hit(struct mail_transaction_log_file *file, unsigned int idx) { struct modseq_cache cache; if (idx > 0) { /* @UNSAFE: move it to top */ cache = file->modseq_cache[idx]; memmove(file->modseq_cache + 1, file->modseq_cache, sizeof(*file->modseq_cache) * idx); file->modseq_cache[0] = cache; } return &file->modseq_cache[0]; } static struct modseq_cache * modseq_cache_get_offset(struct mail_transaction_log_file *file, uoff_t offset) { unsigned int i, best = UINT_MAX; for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) { if (offset < file->modseq_cache[i].offset) continue; if (file->modseq_cache[i].offset == 0) return NULL; if (offset == file->modseq_cache[i].offset) { /* exact cache hit */ return modseq_cache_hit(file, i); } if (best == UINT_MAX || file->modseq_cache[i].offset < file->modseq_cache[best].offset) best = i; } if (best == UINT_MAX) return NULL; return &file->modseq_cache[best]; } static struct modseq_cache * modseq_cache_get_modseq(struct mail_transaction_log_file *file, uint64_t modseq) { unsigned int i, best = UINT_MAX; for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) { if (modseq < file->modseq_cache[i].highest_modseq) continue; if (file->modseq_cache[i].offset == 0) return NULL; if (modseq == file->modseq_cache[i].highest_modseq) { /* exact cache hit */ return modseq_cache_hit(file, i); } if (best == UINT_MAX || file->modseq_cache[i].highest_modseq < file->modseq_cache[best].highest_modseq) best = i; } if (best == UINT_MAX) return NULL; return &file->modseq_cache[best]; } static int log_get_synced_record(struct mail_transaction_log_file *file, uoff_t *offset, const struct mail_transaction_header **hdr_r, const char **error_r) { const struct mail_transaction_header *hdr; uint32_t trans_size; hdr = CONST_PTR_OFFSET(file->buffer->data, *offset - file->buffer_offset); /* we've already synced this record at some point. it should be valid. */ trans_size = mail_index_offset_to_uint32(hdr->size); if (trans_size < sizeof(*hdr) || *offset - file->buffer_offset + trans_size > file->buffer->used) { *error_r = t_strdup_printf( "Transaction log corrupted unexpectedly at " "%"PRIuUOFF_T": Invalid size %u (type=%x)", *offset, trans_size, hdr->type); mail_transaction_log_file_set_corrupted(file, "%s", *error_r); return -1; } *offset += trans_size; *hdr_r = hdr; return 0; } int mail_transaction_log_file_get_highest_modseq_at( struct mail_transaction_log_file *file, uoff_t offset, uint64_t *highest_modseq_r, const char **error_r) { const struct mail_transaction_header *hdr; struct modseq_cache *cache; uoff_t cur_offset; uint64_t cur_modseq; const char *reason; int ret; i_assert(offset <= file->sync_offset); if (offset == file->sync_offset) { *highest_modseq_r = file->sync_highest_modseq; return 0; } cache = modseq_cache_get_offset(file, offset); if (cache == NULL) { /* nothing usable in cache - scan from beginning */ cur_offset = file->hdr.hdr_size; cur_modseq = file->hdr.initial_modseq; } else if (cache->offset == offset) { /* exact cache hit */ *highest_modseq_r = cache->highest_modseq; return 0; } else { /* use cache to skip over some records */ cur_offset = cache->offset; cur_modseq = cache->highest_modseq; } ret = mail_transaction_log_file_map(file, cur_offset, offset, &reason); if (ret <= 0) { *error_r = t_strdup_printf( "Failed to map transaction log %s for getting modseq " "at offset=%"PRIuUOFF_T" with start_offset=%"PRIuUOFF_T": %s", file->filepath, offset, cur_offset, reason); return -1; } i_assert(cur_offset >= file->buffer_offset); i_assert(cur_offset + file->buffer->used >= offset); while (cur_offset < offset) { if (log_get_synced_record(file, &cur_offset, &hdr, error_r) < 0) return- 1; mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq, MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr)); } /* @UNSAFE: cache the value */ memmove(file->modseq_cache + 1, file->modseq_cache, sizeof(*file->modseq_cache) * (N_ELEMENTS(file->modseq_cache) - 1)); file->modseq_cache[0].offset = cur_offset; file->modseq_cache[0].highest_modseq = cur_modseq; *highest_modseq_r = cur_modseq; return 0; } static int get_modseq_next_offset_at(struct mail_transaction_log_file *file, uint64_t modseq, bool use_highest, uoff_t *cur_offset, uint64_t *cur_modseq, uoff_t *next_offset_r) { const struct mail_transaction_header *hdr; const char *reason; int ret; /* make sure we've read until end of file. this is especially important with non-head logs which might only have been opened without being synced. */ ret = mail_transaction_log_file_map(file, *cur_offset, (uoff_t)-1, &reason); if (ret <= 0) { mail_index_set_error(file->log->index, "Failed to map transaction log %s for getting offset " "for modseq=%llu with start_offset=%"PRIuUOFF_T": %s", file->filepath, (unsigned long long)modseq, *cur_offset, reason); return -1; } /* check sync_highest_modseq again in case sync_offset was updated */ if (modseq >= file->sync_highest_modseq && use_highest) { *next_offset_r = file->sync_offset; return 0; } i_assert(*cur_offset >= file->buffer_offset); while (*cur_offset < file->sync_offset) { if (log_get_synced_record(file, cur_offset, &hdr, &reason) < 0) { mail_index_set_error(file->log->index, "%s: %s", file->filepath, reason); return -1; } mail_transaction_update_modseq(hdr, hdr + 1, cur_modseq, MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr)); if (*cur_modseq >= modseq) break; } return 1; } int mail_transaction_log_file_get_modseq_next_offset( struct mail_transaction_log_file *file, uint64_t modseq, uoff_t *next_offset_r) { struct modseq_cache *cache; uoff_t cur_offset; uint64_t cur_modseq; int ret; if (modseq == file->sync_highest_modseq) { *next_offset_r = file->sync_offset; return 0; } if (modseq == file->hdr.initial_modseq) { *next_offset_r = file->hdr.hdr_size; return 0; } cache = modseq_cache_get_modseq(file, modseq); if (cache == NULL) { /* nothing usable in cache - scan from beginning */ cur_offset = file->hdr.hdr_size; cur_modseq = file->hdr.initial_modseq; } else if (cache->highest_modseq == modseq) { /* exact cache hit */ *next_offset_r = cache->offset; return 0; } else { /* use cache to skip over some records */ cur_offset = cache->offset; cur_modseq = cache->highest_modseq; } if ((ret = get_modseq_next_offset_at(file, modseq, TRUE, &cur_offset, &cur_modseq, next_offset_r)) <= 0) return ret; if (cur_offset == file->sync_offset) { /* if we got to sync_offset, cur_modseq should be sync_highest_modseq */ mail_index_set_error(file->log->index, "%s: Transaction log modseq tracking is corrupted - fixing", file->filepath); /* retry getting the offset by reading from the beginning of the file */ cur_offset = file->hdr.hdr_size; cur_modseq = file->hdr.initial_modseq; ret = get_modseq_next_offset_at(file, modseq, FALSE, &cur_offset, &cur_modseq, next_offset_r); if (ret < 0) return -1; i_assert(ret != 0); /* get it fixed on the next sync */ file->log->index->need_recreate = TRUE; file->need_rotate = TRUE; /* clear cache, since it's unreliable */ memset(file->modseq_cache, 0, sizeof(file->modseq_cache)); } /* @UNSAFE: cache the value */ memmove(file->modseq_cache + 1, file->modseq_cache, sizeof(*file->modseq_cache) * (N_ELEMENTS(file->modseq_cache) - 1)); file->modseq_cache[0].offset = cur_offset; file->modseq_cache[0].highest_modseq = cur_modseq; *next_offset_r = cur_offset; return 0; } static int log_file_track_sync(struct mail_transaction_log_file *file, const struct mail_transaction_header *hdr, unsigned int trans_size, const char **error_r) { const void *data = hdr + 1; int ret; mail_transaction_update_modseq(hdr, hdr + 1, &file->sync_highest_modseq, MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr)); if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) return 1; /* external transactions: */ switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_HEADER_UPDATE: /* see if this updates mailbox_sync_offset */ ret = log_file_track_mailbox_sync_offset_hdr(file, data, trans_size, error_r); if (ret != 0) return ret < 0 ? -1 : 1; break; case MAIL_TRANSACTION_INDEX_DELETED: if (file->sync_offset < file->index_undeleted_offset) break; file->log->index->index_deleted = TRUE; file->log->index->index_delete_requested = FALSE; file->index_deleted_offset = file->sync_offset + trans_size; break; case MAIL_TRANSACTION_INDEX_UNDELETED: if (file->sync_offset < file->index_deleted_offset) break; file->log->index->index_deleted = FALSE; file->log->index->index_delete_requested = FALSE; file->index_undeleted_offset = file->sync_offset + trans_size; break; case MAIL_TRANSACTION_BOUNDARY: { const struct mail_transaction_boundary *boundary = (const void *)(hdr + 1); size_t wanted_buffer_size; wanted_buffer_size = file->sync_offset - file->buffer_offset + boundary->size; if (wanted_buffer_size > file->buffer->used) { /* the full transaction hasn't been written yet */ return 0; } break; } } if (file->max_tail_offset == file->sync_offset) { /* external transactions aren't synced to mailbox. we can update mailbox sync offset to skip this transaction to avoid re-reading it at the next sync. */ file->max_tail_offset += trans_size; } return 1; } static int mail_transaction_log_file_sync(struct mail_transaction_log_file *file, bool *retry_r, const char **reason_r) { const struct mail_transaction_header *hdr; const void *data; struct stat st; size_t size, avail; uint32_t trans_size = 0; int ret; i_assert(file->sync_offset >= file->buffer_offset); *retry_r = FALSE; data = buffer_get_data(file->buffer, &size); if (file->buffer_offset + size < file->sync_offset) { *reason_r = t_strdup_printf( "log file shrank (%"PRIuUOFF_T" < %"PRIuUOFF_T")", file->buffer_offset + (uoff_t)size, file->sync_offset); mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); /* fix the sync_offset to avoid crashes later on */ file->sync_offset = file->buffer_offset + size; return 0; } while (file->sync_offset - file->buffer_offset + sizeof(*hdr) <= size) { hdr = CONST_PTR_OFFSET(data, file->sync_offset - file->buffer_offset); trans_size = mail_index_offset_to_uint32(hdr->size); if (trans_size == 0) { /* unfinished */ return 1; } if (trans_size < sizeof(*hdr)) { *reason_r = t_strdup_printf( "hdr.size too small (%u)", trans_size); mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); return 0; } if (file->sync_offset - file->buffer_offset + trans_size > size) break; /* transaction has been fully written */ if ((ret = log_file_track_sync(file, hdr, trans_size, reason_r)) <= 0) { if (ret < 0) return 0; break; } file->sync_offset += trans_size; } if (file->mmap_base != NULL && !file->locked) { /* Now that all the mmaped pages have page faulted, check if the file had changed while doing that. Only after the last page has faulted, the size returned by fstat() can be trusted. Otherwise it might point to a page boundary while the next page is still being written. Without this check we might see partial transactions, sometimes causing "Extension record updated without intro prefix" errors. */ if (fstat(file->fd, &st) < 0) { log_file_set_syscall_error(file, "fstat()"); *reason_r = t_strdup_printf("fstat() failed: %m"); return -1; } if ((uoff_t)st.st_size != file->last_size) { file->last_size = st.st_size; *retry_r = TRUE; *reason_r = "File size changed - retrying"; return 0; } } avail = file->sync_offset - file->buffer_offset; if (avail != size) { /* There's more data than we could sync at the moment. If the last record's size wasn't valid, we can't know if it will be updated unless we've locked the log. */ if (file->locked) { *reason_r = "Unexpected garbage at EOF"; mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); return 0; } /* The size field will be updated soon */ mail_index_flush_read_cache(file->log->index, file->filepath, file->fd, file->locked); } if (file->next != NULL && file->hdr.file_seq == file->next->hdr.prev_file_seq && file->next->hdr.prev_file_offset != file->sync_offset) { *reason_r = t_strdup_printf( "Invalid transaction log size " "(%"PRIuUOFF_T" vs %u): %s", file->sync_offset, file->log->head->hdr.prev_file_offset, file->filepath); mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); return 0; } return 1; } static int mail_transaction_log_file_insert_read(struct mail_transaction_log_file *file, uoff_t offset, const char **reason_r) { void *data; size_t size; ssize_t ret; size = file->buffer_offset - offset; buffer_copy(file->buffer, size, file->buffer, 0, (size_t)-1); data = buffer_get_space_unsafe(file->buffer, 0, size); ret = pread_full(file->fd, data, size, offset); if (ret > 0) { /* success */ file->buffer_offset -= size; return 1; } /* failure. don't leave ourself to inconsistent state */ buffer_copy(file->buffer, 0, file->buffer, size, (size_t)-1); buffer_set_used_size(file->buffer, file->buffer->used - size); if (ret == 0) { *reason_r = "file shrank unexpectedly"; mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); return 0; } else if (errno == ESTALE) { /* log file was deleted in NFS server, fail silently */ *reason_r = t_strdup_printf("read() failed: %m"); return 0; } else { log_file_set_syscall_error(file, "pread()"); *reason_r = t_strdup_printf("read() failed: %m"); return -1; } } static int mail_transaction_log_file_read_more(struct mail_transaction_log_file *file, const char **reason_r) { void *data; size_t size; uint32_t read_offset; ssize_t ret; read_offset = file->buffer_offset + buffer_get_used_size(file->buffer); do { data = buffer_append_space_unsafe(file->buffer, LOG_PREFETCH); ret = pread(file->fd, data, LOG_PREFETCH, read_offset); if (ret > 0) read_offset += ret; size = read_offset - file->buffer_offset; buffer_set_used_size(file->buffer, size); } while (ret > 0 || (ret < 0 && errno == EINTR)); file->last_size = read_offset; if (ret < 0) { *reason_r = t_strdup_printf("pread() failed: %m"); if (errno == ESTALE) { /* log file was deleted in NFS server, fail silently */ return 0; } log_file_set_syscall_error(file, "pread()"); return -1; } return 1; } static bool mail_transaction_log_file_need_nfs_flush(struct mail_transaction_log_file *file) { const struct mail_index_header *hdr = &file->log->index->map->hdr; uoff_t max_offset = file->last_size; if (file->next != NULL && file->hdr.file_seq == file->next->hdr.prev_file_seq && file->next->hdr.prev_file_offset != max_offset) { /* we already have a newer log file which says that we haven't synced the entire file. */ return TRUE; } if (file->hdr.file_seq == hdr->log_file_seq && max_offset < hdr->log_file_head_offset) return TRUE; return FALSE; } static int mail_transaction_log_file_read(struct mail_transaction_log_file *file, uoff_t start_offset, bool nfs_flush, const char **reason_r) { bool retry; int ret; i_assert(file->mmap_base == NULL); /* NFS: if file isn't locked, we're optimistic that we can read enough data without flushing attribute cache. if after reading we notice that we really should have read more, flush the cache and try again. if file is locked, the attribute cache was already flushed when refreshing the log. */ if (file->log->nfs_flush && nfs_flush) { if (!file->locked) nfs_flush_attr_cache_unlocked(file->filepath); else nfs_flush_attr_cache_fd_locked(file->filepath, file->fd); } if (file->buffer != NULL && file->buffer_offset > start_offset) { /* we have to insert missing data to beginning of buffer */ ret = mail_transaction_log_file_insert_read(file, start_offset, reason_r); if (ret <= 0) return ret; } if (file->buffer == NULL) { file->buffer = buffer_create_dynamic(default_pool, LOG_PREFETCH); file->buffer_offset = start_offset; } if ((ret = mail_transaction_log_file_read_more(file, reason_r)) <= 0) ; else if (file->log->nfs_flush && !nfs_flush && mail_transaction_log_file_need_nfs_flush(file)) { /* we didn't read enough data. flush and try again. */ return mail_transaction_log_file_read(file, start_offset, TRUE, reason_r); } else if ((ret = mail_transaction_log_file_sync(file, &retry, reason_r)) == 0) { i_assert(!retry); /* retry happens only with mmap */ } i_assert(file->sync_offset >= file->buffer_offset); buffer_set_used_size(file->buffer, file->sync_offset - file->buffer_offset); return ret; } static bool log_file_map_check_offsets(struct mail_transaction_log_file *file, uoff_t start_offset, uoff_t end_offset, const char **reason_r) { struct stat st, st2; if (start_offset > file->sync_offset) { /* broken start offset */ if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) { *reason_r = t_strdup_printf( "%s: start_offset (%"PRIuUOFF_T") > " "current sync_offset (%"PRIuUOFF_T")", file->filepath, start_offset, file->sync_offset); return FALSE; } if (fstat(file->fd, &st) < 0) { log_file_set_syscall_error(file, "fstat()"); st.st_size = -1; } *reason_r = t_strdup_printf( "%s: start_offset (%"PRIuUOFF_T") > " "current sync_offset (%"PRIuUOFF_T"), file size=%"PRIuUOFF_T, file->filepath, start_offset, file->sync_offset, st.st_size); if (stat(file->filepath, &st2) == 0) { if (st.st_ino != st2.st_ino) { *reason_r = t_strdup_printf( "%s, file unexpectedly replaced", *reason_r); } } else if (errno == ENOENT) { *reason_r = t_strdup_printf( "%s, file unexpectedly deleted", *reason_r); } else { log_file_set_syscall_error(file, "stat()"); } return FALSE; } if (end_offset != (uoff_t)-1 && end_offset > file->sync_offset) { *reason_r = t_strdup_printf( "%s: end_offset (%"PRIuUOFF_T") > " "current sync_offset (%"PRIuUOFF_T")", file->filepath, start_offset, file->sync_offset); return FALSE; } return TRUE; } static int mail_transaction_log_file_mmap(struct mail_transaction_log_file *file, const char **reason_r) { if (file->buffer != NULL) { /* in case we just switched to mmaping */ buffer_free(&file->buffer); } file->mmap_size = file->last_size; file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ, MAP_SHARED, file->fd, 0); if (file->mmap_base == MAP_FAILED) { file->mmap_base = NULL; if (ioloop_time != file->last_mmap_error_time) { file->last_mmap_error_time = ioloop_time; log_file_set_syscall_error(file, t_strdup_printf( "mmap(size=%"PRIuSIZE_T")", file->mmap_size)); } *reason_r = t_strdup_printf("mmap(size=%"PRIuSIZE_T") failed: %m", file->mmap_size); file->mmap_size = 0; return -1; } if (file->mmap_size > mmap_get_page_size()) { if (madvise(file->mmap_base, file->mmap_size, MADV_SEQUENTIAL) < 0) log_file_set_syscall_error(file, "madvise()"); } buffer_create_from_const_data(&file->mmap_buffer, file->mmap_base, file->mmap_size); file->buffer = &file->mmap_buffer; file->buffer_offset = 0; return 0; } static void mail_transaction_log_file_munmap(struct mail_transaction_log_file *file) { if (file->mmap_base == NULL) return; i_assert(file->buffer != NULL); if (munmap(file->mmap_base, file->mmap_size) < 0) log_file_set_syscall_error(file, "munmap()"); file->mmap_base = NULL; file->mmap_size = 0; buffer_free(&file->buffer); } static int mail_transaction_log_file_map_mmap(struct mail_transaction_log_file *file, uoff_t start_offset, const char **reason_r) { struct stat st; bool retry; int ret; /* we are going to mmap() this file, but it's not necessarily mmaped currently. */ i_assert(file->buffer_offset == 0 || file->mmap_base == NULL); i_assert(file->mmap_size == 0 || file->mmap_base != NULL); if (fstat(file->fd, &st) < 0) { log_file_set_syscall_error(file, "fstat()"); *reason_r = t_strdup_printf("fstat() failed: %m"); return -1; } file->last_size = st.st_size; if ((uoff_t)st.st_size < file->sync_offset) { *reason_r = t_strdup_printf( "file size shrank (%"PRIuUOFF_T" < %"PRIuUOFF_T")", (uoff_t)st.st_size, file->sync_offset); mail_transaction_log_file_set_corrupted(file, "%s", *reason_r); return 0; } if (file->buffer != NULL && file->buffer_offset <= start_offset && (uoff_t)st.st_size == file->buffer_offset + file->buffer->used) { /* we already have the whole file mapped */ if ((ret = mail_transaction_log_file_sync(file, &retry, reason_r)) != 0 || !retry) return ret; /* size changed, re-mmap */ } do { mail_transaction_log_file_munmap(file); if (file->last_size - start_offset < mmap_get_page_size()) { /* just reading the file is probably faster */ return mail_transaction_log_file_read(file, start_offset, FALSE, reason_r); } if (mail_transaction_log_file_mmap(file, reason_r) < 0) return -1; ret = mail_transaction_log_file_sync(file, &retry, reason_r); } while (retry); return ret; } int mail_transaction_log_file_map(struct mail_transaction_log_file *file, uoff_t start_offset, uoff_t end_offset, const char **reason_r) { uoff_t map_start_offset = start_offset; size_t size; int ret; if (file->hdr.indexid == 0) { /* corrupted */ *reason_r = "corrupted, indexid=0"; return 0; } i_assert(start_offset >= file->hdr.hdr_size); i_assert(start_offset <= end_offset); i_assert(file->buffer == NULL || file->mmap_base != NULL || file->sync_offset >= file->buffer_offset + file->buffer->used); if (file->locked_sync_offset_updated && file == file->log->head && end_offset == (uoff_t)-1) { /* we're not interested of going further than sync_offset */ if (!log_file_map_check_offsets(file, start_offset, end_offset, reason_r)) return 0; i_assert(start_offset <= file->sync_offset); end_offset = file->sync_offset; } if (file->buffer != NULL && file->buffer_offset <= start_offset) { /* see if we already have it */ size = buffer_get_used_size(file->buffer); if (file->buffer_offset + size >= end_offset) return 1; } if (file->locked) { /* set this only when we've synced to end of file while locked (either end_offset=(uoff_t)-1 or we had to read anyway) */ file->locked_sync_offset_updated = TRUE; } if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) { if (start_offset < file->buffer_offset || file->buffer == NULL) { /* we had moved the log to memory but failed to read the beginning of the log file */ *reason_r = "Beginning of the log isn't available"; return 0; } return log_file_map_check_offsets(file, start_offset, end_offset, reason_r) ? 1 : 0; } if (start_offset > file->sync_offset) mail_transaction_log_file_skip_to_head(file); if (start_offset > file->sync_offset) { /* although we could just skip over the unwanted data, we have to sync everything so that modseqs are calculated correctly */ map_start_offset = file->sync_offset; } if ((file->log->index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0) ret = mail_transaction_log_file_map_mmap(file, map_start_offset, reason_r); else { mail_transaction_log_file_munmap(file); ret = mail_transaction_log_file_read(file, map_start_offset, FALSE, reason_r); } i_assert(file->buffer == NULL || file->mmap_base != NULL || file->sync_offset >= file->buffer_offset + file->buffer->used); if (ret <= 0) return ret; i_assert(file->buffer != NULL); return log_file_map_check_offsets(file, start_offset, end_offset, reason_r) ? 1 : 0; } void mail_transaction_log_file_move_to_memory(struct mail_transaction_log_file *file) { const char *error; buffer_t *buf; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) return; if (file->mmap_base != NULL) { /* just copy to memory */ i_assert(file->buffer_offset == 0); buf = buffer_create_dynamic(default_pool, file->mmap_size); buffer_append(buf, file->mmap_base, file->mmap_size); buffer_free(&file->buffer); file->buffer = buf; /* and lose the mmap */ if (munmap(file->mmap_base, file->mmap_size) < 0) log_file_set_syscall_error(file, "munmap()"); file->mmap_base = NULL; } else if (file->buffer_offset != 0) { /* we don't have the full log in the memory. read it. */ (void)mail_transaction_log_file_read(file, 0, FALSE, &error); } file->last_size = 0; if (close(file->fd) < 0) log_file_set_syscall_error(file, "close()"); file->fd = -1; i_free(file->filepath); file->filepath = i_strdup(file->log->filepath); } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-update.c0000644000175000017500000011336213165463624021005 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ /* Inside transaction we keep messages stored in sequences in uid fields. Before they're written to transaction log the sequences are changed to UIDs. */ #include "lib.h" #include "array.h" #include "time-util.h" #include "mail-index-private.h" #include "mail-index-transaction-private.h" static bool mail_index_transaction_has_ext_changes(struct mail_index_transaction *t); struct mail_index_record * mail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq) { i_assert(seq >= t->first_new_seq && seq <= t->last_new_seq); return array_idx_modifiable(&t->appends, seq - t->first_new_seq); } void mail_index_transaction_reset_v(struct mail_index_transaction *t) { ARRAY_TYPE(seq_array) *rec; struct mail_index_transaction_ext_hdr_update *ext_hdr; if (array_is_created(&t->ext_rec_updates)) { array_foreach_modifiable(&t->ext_rec_updates, rec) { if (array_is_created(rec)) array_free(rec); } array_free(&t->ext_rec_updates); } if (array_is_created(&t->ext_rec_atomics)) { array_foreach_modifiable(&t->ext_rec_atomics, rec) { if (array_is_created(rec)) array_free(rec); } array_free(&t->ext_rec_atomics); } if (array_is_created(&t->ext_hdr_updates)) { array_foreach_modifiable(&t->ext_hdr_updates, ext_hdr) { i_free(ext_hdr->data); i_free(ext_hdr->mask); } array_free(&t->ext_hdr_updates); } if (array_is_created(&t->keyword_updates)) { struct mail_index_transaction_keyword_update *u; array_foreach_modifiable(&t->keyword_updates, u) { if (array_is_created(&u->add_seq)) array_free(&u->add_seq); if (array_is_created(&u->remove_seq)) array_free(&u->remove_seq); } array_free(&t->keyword_updates); } if (array_is_created(&t->appends)) array_free(&t->appends); if (array_is_created(&t->modseq_updates)) array_free(&t->modseq_updates); if (array_is_created(&t->expunges)) array_free(&t->expunges); if (array_is_created(&t->updates)) array_free(&t->updates); if (array_is_created(&t->ext_resizes)) array_free(&t->ext_resizes); if (array_is_created(&t->ext_resets)) array_free(&t->ext_resets); if (array_is_created(&t->ext_reset_ids)) array_free(&t->ext_reset_ids); if (array_is_created(&t->ext_reset_atomic)) array_free(&t->ext_reset_atomic); if (t->attribute_updates != NULL) buffer_free(&t->attribute_updates); if (t->attribute_updates_suffix != NULL) buffer_free(&t->attribute_updates_suffix); t->first_new_seq = mail_index_view_get_messages_count(t->view)+1; t->last_new_seq = 0; t->last_update_idx = 0; t->min_flagupdate_seq = 0; t->max_flagupdate_seq = 0; t->min_highest_modseq = 0; memset(t->pre_hdr_mask, 0, sizeof(t->pre_hdr_mask)); memset(t->post_hdr_mask, 0, sizeof(t->post_hdr_mask)); t->appends_nonsorted = FALSE; t->expunges_nonsorted = FALSE; t->drop_unnecessary_flag_updates = FALSE; t->pre_hdr_changed = FALSE; t->post_hdr_changed = FALSE; t->reset = FALSE; t->index_deleted = FALSE; t->index_undeleted = FALSE; t->log_updates = FALSE; t->log_ext_updates = FALSE; t->tail_offset_changed = FALSE; } void mail_index_transaction_set_log_updates(struct mail_index_transaction *t) { /* flag updates aren't included in log_updates */ t->log_updates = array_is_created(&t->appends) || array_is_created(&t->modseq_updates) || array_is_created(&t->expunges) || array_is_created(&t->keyword_updates) || t->attribute_updates != NULL || t->pre_hdr_changed || t->post_hdr_changed || t->min_highest_modseq != 0; } void mail_index_update_day_headers(struct mail_index_transaction *t, time_t day_stamp) { struct mail_index_header hdr; const struct mail_index_record *rec; const int max_days = N_ELEMENTS(hdr.day_first_uid); time_t stamp; int i, days; hdr = *mail_index_get_header(t->view); rec = array_idx(&t->appends, 0); stamp = time_to_local_day_start(day_stamp); if ((time_t)hdr.day_stamp >= stamp) return; /* get number of days since last message */ days = (stamp - hdr.day_stamp) / (3600*24); if (days > max_days) days = max_days; /* @UNSAFE: move days forward and fill the missing days with old day_first_uid[0]. */ if (days > 0 && days < max_days) memmove(hdr.day_first_uid + days, hdr.day_first_uid, (max_days - days) * sizeof(hdr.day_first_uid[0])); for (i = 1; i < days; i++) hdr.day_first_uid[i] = hdr.day_first_uid[0]; hdr.day_stamp = stamp; hdr.day_first_uid[0] = rec->uid; mail_index_update_header(t, offsetof(struct mail_index_header, day_stamp), &hdr.day_stamp, sizeof(hdr.day_stamp), FALSE); mail_index_update_header(t, offsetof(struct mail_index_header, day_first_uid), hdr.day_first_uid, sizeof(hdr.day_first_uid), FALSE); } void mail_index_append(struct mail_index_transaction *t, uint32_t uid, uint32_t *seq_r) { struct mail_index_record *rec; i_assert(!t->no_appends); t->log_updates = TRUE; if (!array_is_created(&t->appends)) i_array_init(&t->appends, 32); /* sequence number is visible only inside given view, so let it generate it */ if (t->last_new_seq != 0) *seq_r = ++t->last_new_seq; else *seq_r = t->last_new_seq = t->first_new_seq; rec = array_append_space(&t->appends); if (uid != 0) { rec->uid = uid; if (!t->appends_nonsorted && t->last_new_seq != t->first_new_seq) { /* if previous record's UID is larger than this one, we'll have to sort the appends later */ rec = mail_index_transaction_lookup(t, *seq_r - 1); if (rec->uid > uid) t->appends_nonsorted = TRUE; else if (rec->uid == uid) i_panic("Duplicate UIDs added in transaction"); } if (t->highest_append_uid < uid) t->highest_append_uid = uid; } } void mail_index_append_finish_uids(struct mail_index_transaction *t, uint32_t first_uid, ARRAY_TYPE(seq_range) *uids_r) { struct mail_index_record *recs; unsigned int i, count; struct seq_range *range; uint32_t next_uid; if (!array_is_created(&t->appends)) return; i_assert(first_uid < (uint32_t)-1); /* first find the highest assigned uid */ recs = array_get_modifiable(&t->appends, &count); i_assert(count > 0); next_uid = first_uid; for (i = 0; i < count; i++) { if (next_uid <= recs[i].uid) next_uid = recs[i].uid + 1; } i_assert(next_uid > 0 && next_uid < (uint32_t)-1); /* assign missing uids */ for (i = 0; i < count; i++) { if (recs[i].uid == 0 || recs[i].uid < first_uid) { i_assert(next_uid < (uint32_t)-1); recs[i].uid = next_uid++; if (t->highest_append_uid < recs[i].uid) t->highest_append_uid = recs[i].uid; } else { if (next_uid != first_uid) t->appends_nonsorted = TRUE; } } /* write the saved uids range */ array_clear(uids_r); range = array_append_space(uids_r); range->seq1 = range->seq2 = recs[0].uid; for (i = 1; i < count; i++) { if (range->seq2 + 1 == recs[i].uid) range->seq2++; else { range = array_append_space(uids_r); range->seq1 = range->seq2 = recs[i].uid; } } } void mail_index_update_modseq(struct mail_index_transaction *t, uint32_t seq, uint64_t min_modseq) { struct mail_transaction_modseq_update *u; /* modseq=1 is the minimum always and it's only for mails that were created/modified before modseqs were enabled. */ if (min_modseq <= 1) return; if (!array_is_created(&t->modseq_updates)) i_array_init(&t->modseq_updates, 32); u = array_append_space(&t->modseq_updates); u->uid = seq; u->modseq_low32 = min_modseq & 0xffffffff; u->modseq_high32 = min_modseq >> 32; t->log_updates = TRUE; } void mail_index_update_highest_modseq(struct mail_index_transaction *t, uint64_t min_modseq) { /* modseq=1 is the minimum always and it's only for mails that were created/modified before modseqs were enabled. */ if (min_modseq <= 1) return; if (t->min_highest_modseq < min_modseq) t->min_highest_modseq = min_modseq; t->log_updates = TRUE; } static void mail_index_revert_ext(ARRAY_TYPE(seq_array_array) *ext_updates, uint32_t seq) { ARRAY_TYPE(seq_array) *seqs; unsigned int idx; if (!array_is_created(ext_updates)) return; array_foreach_modifiable(ext_updates, seqs) { if (array_is_created(seqs) && mail_index_seq_array_lookup(seqs, seq, &idx)) array_delete(seqs, idx, 1); } } static void mail_index_revert_changes_common(struct mail_index_transaction *t, uint32_t seq) { struct mail_index_transaction_keyword_update *kw_update; unsigned int i; /* remove extension updates */ mail_index_revert_ext(&t->ext_rec_updates, seq); mail_index_revert_ext(&t->ext_rec_atomics, seq); t->log_ext_updates = mail_index_transaction_has_ext_changes(t); /* remove keywords */ if (array_is_created(&t->keyword_updates)) { array_foreach_modifiable(&t->keyword_updates, kw_update) { if (array_is_created(&kw_update->add_seq)) { seq_range_array_remove(&kw_update->add_seq, seq); } if (array_is_created(&kw_update->remove_seq)) { seq_range_array_remove(&kw_update->remove_seq, seq); } } } /* remove modseqs */ if (array_is_created(&t->modseq_updates) && mail_index_seq_array_lookup((void *)&t->modseq_updates, seq, &i)) array_delete(&t->modseq_updates, i, 1); } void mail_index_revert_changes(struct mail_index_transaction *t, uint32_t seq) { mail_index_revert_changes_common(t, seq); mail_index_cancel_flag_updates(t, seq); } static void mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq) { i_assert(seq == t->last_new_seq); mail_index_revert_changes_common(t, seq); /* and finally remove the append itself */ array_delete(&t->appends, seq - t->first_new_seq, 1); t->last_new_seq--; if (t->first_new_seq > t->last_new_seq) { t->last_new_seq = 0; t->appends_nonsorted = FALSE; array_free(&t->appends); } mail_index_transaction_set_log_updates(t); } void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq) { static guid_128_t null_guid = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; mail_index_expunge_guid(t, seq, null_guid); } void mail_index_expunge_guid(struct mail_index_transaction *t, uint32_t seq, const guid_128_t guid_128) { const struct mail_transaction_expunge_guid *expunges; struct mail_transaction_expunge_guid *expunge; unsigned int count; i_assert(seq > 0); if (seq >= t->first_new_seq) { /* we can handle only the last append. otherwise we'd have to renumber sequences and that gets tricky. for now this is enough, since we typically want to expunge all the appends. */ mail_index_expunge_last_append(t, seq); } else { t->log_updates = TRUE; /* ignore duplicates here. drop them when committing. */ if (!array_is_created(&t->expunges)) i_array_init(&t->expunges, 64); else if (!t->expunges_nonsorted) { /* usually expunges are added in increasing order. */ expunges = array_get(&t->expunges, &count); if (count > 0 && seq < expunges[count-1].uid) t->expunges_nonsorted = TRUE; } expunge = array_append_space(&t->expunges); expunge->uid = seq; memcpy(expunge->guid_128, guid_128, sizeof(expunge->guid_128)); } } static void update_minmax_flagupdate_seq(struct mail_index_transaction *t, uint32_t seq1, uint32_t seq2) { if (t->min_flagupdate_seq == 0) { t->min_flagupdate_seq = seq1; t->max_flagupdate_seq = seq2; } else { if (t->min_flagupdate_seq > seq1) t->min_flagupdate_seq = seq1; if (t->max_flagupdate_seq < seq2) t->max_flagupdate_seq = seq2; } } unsigned int mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, unsigned int left_idx, unsigned int right_idx, uint32_t seq) { const struct mail_index_flag_update *updates; unsigned int idx, count; updates = array_get(&t->updates, &count); i_assert(left_idx <= right_idx && right_idx <= count); i_assert(count < INT_MAX); /* find the first update with either overlapping range, or the update which will come after our insert */ idx = left_idx; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (updates[idx].uid2 < seq) left_idx = idx+1; else if (updates[idx].uid1 > seq) right_idx = idx; else break; } if (left_idx > idx) idx++; return idx; } static void mail_index_insert_flag_update(struct mail_index_transaction *t, struct mail_index_flag_update u, unsigned int idx) { struct mail_index_flag_update *updates, tmp_update; unsigned int count, first_idx, max; updates = array_get_modifiable(&t->updates, &count); /* overlapping ranges, split/merge them */ i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1); i_assert(idx == count || updates[idx].uid2 >= u.uid1); /* first we'll just add the changes without trying to merge anything */ first_idx = idx; for (; idx < count && u.uid2 >= updates[idx].uid1; idx++) { i_assert(u.uid1 <= updates[idx].uid2); if (u.uid1 != updates[idx].uid1 && (updates[idx].add_flags != u.add_flags || updates[idx].remove_flags != u.remove_flags)) { if (u.uid1 < updates[idx].uid1) { /* insert new update */ tmp_update = u; tmp_update.uid2 = updates[idx].uid1 - 1; } else { /* split existing update from beginning */ tmp_update = updates[idx]; tmp_update.uid2 = u.uid1 - 1; updates[idx].uid1 = u.uid1; } i_assert(tmp_update.uid1 <= tmp_update.uid2); i_assert(updates[idx].uid1 <= updates[idx].uid2); array_insert(&t->updates, idx, &tmp_update, 1); updates = array_get_modifiable(&t->updates, &count); idx++; } else if (u.uid1 < updates[idx].uid1) { updates[idx].uid1 = u.uid1; } if (u.uid2 < updates[idx].uid2 && (updates[idx].add_flags != u.add_flags || updates[idx].remove_flags != u.remove_flags)) { /* split existing update from end */ tmp_update = updates[idx]; tmp_update.uid2 = u.uid2; updates[idx].uid1 = u.uid2 + 1; i_assert(tmp_update.uid1 <= tmp_update.uid2); i_assert(updates[idx].uid1 <= updates[idx].uid2); array_insert(&t->updates, idx, &tmp_update, 1); updates = array_get_modifiable(&t->updates, &count); } updates[idx].add_flags = (updates[idx].add_flags | u.add_flags) & ~u.remove_flags; updates[idx].remove_flags = (updates[idx].remove_flags | u.remove_flags) & ~u.add_flags; u.uid1 = updates[idx].uid2 + 1; if (updates[idx].add_flags == 0 && updates[idx].remove_flags == 0) { /* we can remove this update completely */ array_delete(&t->updates, idx, 1); updates = array_get_modifiable(&t->updates, &count); } if (u.uid1 > u.uid2) { /* break here before idx++ so last_update_idx is set correctly */ break; } } i_assert(idx <= count); if (u.uid1 <= u.uid2) { i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1); i_assert(idx == count || updates[idx].uid1 > u.uid2); array_insert(&t->updates, idx, &u, 1); } updates = array_get_modifiable(&t->updates, &count); t->last_update_idx = idx == count ? count-1 : idx; /* merge everything */ idx = first_idx == 0 ? 0 : first_idx - 1; max = count == 0 ? 0 : I_MIN(t->last_update_idx + 1, count-1); for (; idx < max; ) { if (updates[idx].uid2 + 1 == updates[idx+1].uid1 && updates[idx].add_flags == updates[idx+1].add_flags && updates[idx].remove_flags == updates[idx+1].remove_flags) { /* merge */ updates[idx].uid2 = updates[idx+1].uid2; array_delete(&t->updates, idx + 1, 1); max--; if (t->last_update_idx > idx) t->last_update_idx--; updates = array_get_modifiable(&t->updates, &count); } else { idx++; } } } static void mail_index_record_modify_flags(struct mail_index_record *rec, enum modify_type modify_type, enum mail_flags flags) { switch (modify_type) { case MODIFY_REPLACE: rec->flags = flags; break; case MODIFY_ADD: rec->flags |= flags; break; case MODIFY_REMOVE: rec->flags &= ~flags; break; } } void mail_index_update_flags_range(struct mail_index_transaction *t, uint32_t seq1, uint32_t seq2, enum modify_type modify_type, enum mail_flags flags) { struct mail_index_record *rec; struct mail_index_flag_update u, *last_update; unsigned int idx, first_idx, count; update_minmax_flagupdate_seq(t, seq1, seq2); if (seq2 >= t->first_new_seq) { /* updates for appended messages, modify them directly */ uint32_t seq; for (seq = I_MAX(t->first_new_seq, seq1); seq <= seq2; seq++) { rec = mail_index_transaction_lookup(t, seq); mail_index_record_modify_flags(rec, modify_type, flags); } if (seq1 >= t->first_new_seq) return; /* range contains also existing messages. update them next. */ seq2 = t->first_new_seq - 1; } i_assert(seq1 <= seq2 && seq1 > 0); i_assert(seq2 <= mail_index_view_get_messages_count(t->view)); if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0) t->drop_unnecessary_flag_updates = TRUE; i_zero(&u); u.uid1 = seq1; u.uid2 = seq2; switch (modify_type) { case MODIFY_REPLACE: u.add_flags = flags; u.remove_flags = ~flags & MAIL_INDEX_FLAGS_MASK; break; case MODIFY_ADD: if (flags == 0) return; u.add_flags = flags; break; case MODIFY_REMOVE: if (flags == 0) return; u.remove_flags = flags; break; } if (!array_is_created(&t->updates)) { i_array_init(&t->updates, 256); array_append(&t->updates, &u, 1); return; } last_update = array_get_modifiable(&t->updates, &count); if (t->last_update_idx < count) { /* fast path - hopefully we're updating the next message, or a message that is to be appended as last update */ last_update += t->last_update_idx; if (seq1 - 1 == last_update->uid2) { if (u.add_flags == last_update->add_flags && u.remove_flags == last_update->remove_flags && (t->last_update_idx + 1 == count || last_update[1].uid1 > seq2)) { /* we can just update the UID range */ last_update->uid2 = seq2; return; } } else if (seq1 > last_update->uid2) { /* hopefully we can just append it */ t->last_update_idx++; last_update++; } } if (t->last_update_idx == count) array_append(&t->updates, &u, 1); else { i_assert(t->last_update_idx < count); /* slow path */ if (seq1 > last_update->uid2) { /* added after this */ first_idx = t->last_update_idx + 1; } else { /* added before this or on top of this */ first_idx = 0; count = t->last_update_idx + 1; } idx = mail_index_transaction_get_flag_update_pos(t, first_idx, count, u.uid1); mail_index_insert_flag_update(t, u, idx); } } void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq, enum modify_type modify_type, enum mail_flags flags) { mail_index_update_flags_range(t, seq, seq, modify_type, flags); } static void mail_index_attribute_set_full(struct mail_index_transaction *t, const char *key, bool pvt, char prefix, time_t timestamp, uint32_t value_len) { uint32_t ts = timestamp; if (t->attribute_updates == NULL) { t->attribute_updates = buffer_create_dynamic(default_pool, 64); t->attribute_updates_suffix = buffer_create_dynamic(default_pool, 64); } buffer_append_c(t->attribute_updates, prefix); buffer_append_c(t->attribute_updates, pvt ? 'p' : 's'); buffer_append(t->attribute_updates, key, strlen(key)+1); buffer_append(t->attribute_updates_suffix, &ts, sizeof(ts)); if (prefix == '+') { buffer_append(t->attribute_updates_suffix, &value_len, sizeof(value_len)); } t->log_updates = TRUE; } void mail_index_attribute_set(struct mail_index_transaction *t, bool pvt, const char *key, time_t timestamp, uint32_t value_len) { mail_index_attribute_set_full(t, key, pvt, '+', timestamp, value_len); } void mail_index_attribute_unset(struct mail_index_transaction *t, bool pvt, const char *key, time_t timestamp) { mail_index_attribute_set_full(t, key, pvt, '-', timestamp, 0); } void mail_index_update_header(struct mail_index_transaction *t, size_t offset, const void *data, size_t size, bool prepend) { i_assert(offset < sizeof(t->pre_hdr_change)); i_assert(size <= sizeof(t->pre_hdr_change) - offset); t->log_updates = TRUE; if (prepend) { t->pre_hdr_changed = TRUE; memcpy(t->pre_hdr_change + offset, data, size); for (; size > 0; size--) t->pre_hdr_mask[offset++] = 1; } else { t->post_hdr_changed = TRUE; memcpy(t->post_hdr_change + offset, data, size); for (; size > 0; size--) t->post_hdr_mask[offset++] = 1; } } static void mail_index_ext_rec_updates_resize(struct mail_index_transaction *t, uint32_t ext_id, uint16_t new_record_size) { ARRAY_TYPE(seq_array) *array, old_array; unsigned int i; if (!array_is_created(&t->ext_rec_updates)) return; array = array_idx_modifiable(&t->ext_rec_updates, ext_id); if (!array_is_created(array)) return; old_array = *array; i_zero(array); mail_index_seq_array_alloc(array, new_record_size); /* copy the records' beginnings. leave the end zero-filled. */ for (i = 0; i < array_count(&old_array); i++) { const void *old_record = array_idx(&old_array, i); memcpy(array_append_space(array), old_record, old_array.arr.element_size); } array_free(&old_array); } void mail_index_ext_resize(struct mail_index_transaction *t, uint32_t ext_id, uint32_t hdr_size, uint16_t record_size, uint16_t record_align) { const struct mail_index_registered_ext *rext; const struct mail_transaction_ext_intro *resizes; unsigned int resizes_count; struct mail_transaction_ext_intro intro; uint32_t old_record_size = 0, old_record_align, old_header_size; i_zero(&intro); rext = array_idx(&t->view->index->extensions, ext_id); /* get ext_id from transaction's map if it's there */ if (!mail_index_map_get_ext_idx(t->view->map, ext_id, &intro.ext_id)) { /* have to create it */ intro.ext_id = (uint32_t)-1; old_record_align = rext->record_align; old_header_size = rext->hdr_size; } else { const struct mail_index_ext *ext; ext = array_idx(&t->view->map->extensions, intro.ext_id); old_record_align = ext->record_align; old_header_size = ext->hdr_size; } /* get the record size. if there are any existing record updates, they're using the registered size, not the map's existing record_size. */ if (array_is_created(&t->ext_resizes)) resizes = array_get(&t->ext_resizes, &resizes_count); else { resizes = NULL; resizes_count = 0; } if (ext_id < resizes_count && resizes[ext_id].name_size != 0) { /* already resized once. use the resized value. */ old_record_size = resizes[ext_id].record_size; } else { /* use the registered values. */ old_record_size = rext->record_size; } if (record_size != old_record_size && record_size != (uint16_t)-1) { /* if record_size grows, we'll just resize the existing ext_rec_updates array. it's not possible to shrink record_size without data loss. */ i_assert(record_size > old_record_size); mail_index_ext_rec_updates_resize(t, ext_id, record_size); } t->log_ext_updates = TRUE; if (!array_is_created(&t->ext_resizes)) i_array_init(&t->ext_resizes, ext_id + 2); intro.hdr_size = hdr_size != (uint32_t)-1 ? hdr_size : old_header_size; if (record_size != (uint16_t)-1) { i_assert(record_align != (uint16_t)-1); intro.record_size = record_size; intro.record_align = record_align; } else { i_assert(record_align == (uint16_t)-1); intro.record_size = old_record_size; intro.record_align = old_record_align; } intro.name_size = 1; array_idx_set(&t->ext_resizes, ext_id, &intro); } void mail_index_ext_resize_hdr(struct mail_index_transaction *t, uint32_t ext_id, uint32_t hdr_size) { mail_index_ext_resize(t, ext_id, hdr_size, (uint16_t)-1, (uint16_t)-1); } void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id, uint32_t reset_id, bool clear_data) { struct mail_transaction_ext_reset reset; i_assert(reset_id != 0); i_zero(&reset); reset.new_reset_id = reset_id; reset.preserve_data = !clear_data; mail_index_ext_set_reset_id(t, ext_id, reset_id); if (!array_is_created(&t->ext_resets)) i_array_init(&t->ext_resets, ext_id + 2); array_idx_set(&t->ext_resets, ext_id, &reset); t->log_ext_updates = TRUE; } void mail_index_ext_reset_inc(struct mail_index_transaction *t, uint32_t ext_id, uint32_t prev_reset_id, bool clear_data) { uint32_t expected_reset_id = prev_reset_id + 1; mail_index_ext_reset(t, ext_id, (uint32_t)-1, clear_data); if (!array_is_created(&t->ext_reset_atomic)) i_array_init(&t->ext_reset_atomic, ext_id + 2); array_idx_set(&t->ext_reset_atomic, ext_id, &expected_reset_id); } static bool mail_index_transaction_has_ext_updates(const ARRAY_TYPE(seq_array_array) *arr) { const ARRAY_TYPE(seq_array) *array; if (array_is_created(arr)) { array_foreach(arr, array) { if (array_is_created(array)) return TRUE; } } return FALSE; } static bool mail_index_transaction_has_ext_changes(struct mail_index_transaction *t) { if (mail_index_transaction_has_ext_updates(&t->ext_rec_updates)) return TRUE; if (mail_index_transaction_has_ext_updates(&t->ext_rec_atomics)) return TRUE; if (array_is_created(&t->ext_hdr_updates)) { const struct mail_index_transaction_ext_hdr_update *hdr; array_foreach(&t->ext_hdr_updates, hdr) { if (hdr->alloc_size > 0) return TRUE; } } if (array_is_created(&t->ext_resets)) { const struct mail_transaction_ext_reset *reset; array_foreach(&t->ext_resets, reset) { if (reset->new_reset_id != 0) return TRUE; } } if (array_is_created(&t->ext_resizes)) { const struct mail_transaction_ext_intro *resize; array_foreach(&t->ext_resizes, resize) { if (resize->name_size > 0) return TRUE; } } return FALSE; } static void mail_index_ext_update_reset(ARRAY_TYPE(seq_array_array) *arr, uint32_t ext_id) { if (array_is_created(arr) && ext_id < array_count(arr)) { /* if extension records have been updated, clear them */ ARRAY_TYPE(seq_array) *array; array = array_idx_modifiable(arr, ext_id); if (array_is_created(array)) array_clear(array); } } static void mail_index_ext_reset_changes(struct mail_index_transaction *t, uint32_t ext_id) { mail_index_ext_update_reset(&t->ext_rec_updates, ext_id); mail_index_ext_update_reset(&t->ext_rec_atomics, ext_id); if (array_is_created(&t->ext_hdr_updates) && ext_id < array_count(&t->ext_hdr_updates)) { /* if extension headers have been updated, clear them */ struct mail_index_transaction_ext_hdr_update *hdr; hdr = array_idx_modifiable(&t->ext_hdr_updates, ext_id); if (hdr->alloc_size > 0) { i_free_and_null(hdr->mask); i_free_and_null(hdr->data); } hdr->alloc_size = 0; } if (array_is_created(&t->ext_resets) && ext_id < array_count(&t->ext_resets)) { /* clear resets */ array_idx_clear(&t->ext_resets, ext_id); } if (array_is_created(&t->ext_resizes) && ext_id < array_count(&t->ext_resizes)) { /* clear resizes */ array_idx_clear(&t->ext_resizes, ext_id); } t->log_ext_updates = mail_index_transaction_has_ext_changes(t); } void mail_index_ext_using_reset_id(struct mail_index_transaction *t, uint32_t ext_id, uint32_t reset_id) { uint32_t *reset_id_p; bool changed; if (!array_is_created(&t->ext_reset_ids)) i_array_init(&t->ext_reset_ids, ext_id + 2); reset_id_p = array_idx_modifiable(&t->ext_reset_ids, ext_id); changed = *reset_id_p != reset_id && *reset_id_p != 0; *reset_id_p = reset_id; if (changed) { /* reset_id changed, clear existing changes */ mail_index_ext_reset_changes(t, ext_id); } } void mail_index_ext_set_reset_id(struct mail_index_transaction *t, uint32_t ext_id, uint32_t reset_id) { mail_index_ext_using_reset_id(t, ext_id, reset_id); /* make sure the changes get reset, even if reset_id doesn't change */ mail_index_ext_reset_changes(t, ext_id); } void mail_index_update_header_ext(struct mail_index_transaction *t, uint32_t ext_id, size_t offset, const void *data, size_t size) { struct mail_index_transaction_ext_hdr_update *hdr; size_t new_size; i_assert(offset <= (uint32_t)-1 && size <= (uint32_t)-1 && offset + size <= (uint32_t)-1); if (!array_is_created(&t->ext_hdr_updates)) i_array_init(&t->ext_hdr_updates, ext_id + 2); hdr = array_idx_modifiable(&t->ext_hdr_updates, ext_id); if (hdr->alloc_size < offset || hdr->alloc_size - offset < size) { i_assert(size < (size_t)-1 - offset); new_size = nearest_power(offset + size); hdr->mask = i_realloc(hdr->mask, hdr->alloc_size, new_size); hdr->data = i_realloc(hdr->data, hdr->alloc_size, new_size); hdr->alloc_size = new_size; } memset(hdr->mask + offset, 1, size); memcpy(hdr->data + offset, data, size); t->log_ext_updates = TRUE; } void mail_index_update_ext(struct mail_index_transaction *t, uint32_t seq, uint32_t ext_id, const void *data, void *old_data_r) { struct mail_index *index = t->view->index; const struct mail_index_registered_ext *rext; const struct mail_transaction_ext_intro *intro; uint16_t record_size; ARRAY_TYPE(seq_array) *array; unsigned int count; i_assert(seq > 0 && (seq <= mail_index_view_get_messages_count(t->view) || seq <= t->last_new_seq)); i_assert(ext_id < array_count(&index->extensions)); t->log_ext_updates = TRUE; if (!array_is_created(&t->ext_resizes)) { intro = NULL; count = 0; } else { intro = array_get(&t->ext_resizes, &count); } if (ext_id < count && intro[ext_id].name_size != 0) { /* resized record */ record_size = intro[ext_id].record_size; } else { rext = array_idx(&index->extensions, ext_id); record_size = rext->record_size; } i_assert(record_size > 0); if (!array_is_created(&t->ext_rec_updates)) i_array_init(&t->ext_rec_updates, ext_id + 2); array = array_idx_modifiable(&t->ext_rec_updates, ext_id); /* @UNSAFE */ if (!mail_index_seq_array_add(array, seq, data, record_size, old_data_r)) { /* not found, clear old_data if it was given */ if (old_data_r != NULL) memset(old_data_r, 0, record_size); } } int mail_index_atomic_inc_ext(struct mail_index_transaction *t, uint32_t seq, uint32_t ext_id, int diff) { ARRAY_TYPE(seq_array) *array; int32_t old_diff32, diff32 = diff; i_assert(seq > 0 && (seq <= mail_index_view_get_messages_count(t->view) || seq <= t->last_new_seq)); i_assert(ext_id < array_count(&t->view->index->extensions)); /* currently non-external transactions can be applied multiple times, causing multiple increments. FIXME: we need this now and it doesn't actually seem to be a real problem at least right now - why? */ /*i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);*/ t->log_ext_updates = TRUE; if (!array_is_created(&t->ext_rec_atomics)) i_array_init(&t->ext_rec_atomics, ext_id + 2); array = array_idx_modifiable(&t->ext_rec_atomics, ext_id); if (mail_index_seq_array_add(array, seq, &diff32, sizeof(diff32), &old_diff32)) { /* already incremented this sequence in this transaction */ diff32 += old_diff32; (void)mail_index_seq_array_add(array, seq, &diff32, sizeof(diff32), NULL); } return diff32; } static bool keyword_update_has_changes(struct mail_index_transaction *t, uint32_t seq, enum modify_type modify_type, struct mail_keywords *keywords) { ARRAY_TYPE(keyword_indexes) existing; const unsigned int *existing_idx; unsigned int i, j, existing_count; bool found; t_array_init(&existing, 32); if (seq < t->first_new_seq) mail_index_transaction_lookup_latest_keywords(t, seq, &existing); existing_idx = array_get(&existing, &existing_count); if (modify_type == MODIFY_REPLACE && existing_count != keywords->count) return TRUE; for (i = 0; i < keywords->count; i++) { found = FALSE; for (j = 0; j < existing_count; j++) { if (existing_idx[j] == keywords->idx[i]) { found = TRUE; break; } } switch (modify_type) { case MODIFY_ADD: case MODIFY_REPLACE: if (!found) return TRUE; break; case MODIFY_REMOVE: if (found) return TRUE; break; } } return FALSE; } static struct mail_keywords * keyword_update_remove_existing(struct mail_index_transaction *t, uint32_t seq) { ARRAY_TYPE(keyword_indexes) keywords; uint32_t i, keywords_count; t_array_init(&keywords, 32); if (t->view->v.lookup_full == NULL) { /* syncing is saving a list of changes into this transaction. the seq is actual an uid, so we can't lookup the existing keywords. we shouldn't get here unless we're reading pre-v2.2 keyword-reset records from .log files. so we don't really care about performance that much here, */ keywords_count = array_count(&t->view->index->keywords); for (i = 0; i < keywords_count; i++) array_append(&keywords, &i, 1); } else { mail_index_transaction_lookup_latest_keywords(t, seq, &keywords); } if (array_count(&keywords) == 0) return NULL; return mail_index_keywords_create_from_indexes(t->view->index, &keywords); } void mail_index_update_keywords(struct mail_index_transaction *t, uint32_t seq, enum modify_type modify_type, struct mail_keywords *keywords) { struct mail_index_transaction_keyword_update *u; struct mail_keywords *add_keywords = NULL, *remove_keywords = NULL; struct mail_keywords *unref_keywords = NULL; unsigned int i; bool changed; i_assert(seq > 0 && (seq <= mail_index_view_get_messages_count(t->view) || seq <= t->last_new_seq)); i_assert(keywords->index == t->view->index); if (keywords->count == 0 && modify_type != MODIFY_REPLACE) return; update_minmax_flagupdate_seq(t, seq, seq); if (!array_is_created(&t->keyword_updates)) { uint32_t max_idx = keywords->count == 0 ? 3 : keywords->idx[keywords->count-1]; i_array_init(&t->keyword_updates, max_idx + 1); } if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0) { T_BEGIN { changed = keyword_update_has_changes(t, seq, modify_type, keywords); } T_END; if (!changed) return; } switch (modify_type) { case MODIFY_REPLACE: /* split this into add+remove. remove all existing keywords not included in the keywords list */ if (seq < t->first_new_seq) { /* remove the ones currently in index */ remove_keywords = keyword_update_remove_existing(t, seq); unref_keywords = remove_keywords; } /* remove from all changes we've done in this transaction */ array_foreach_modifiable(&t->keyword_updates, u) seq_range_array_remove(&u->add_seq, seq); add_keywords = keywords; break; case MODIFY_ADD: add_keywords = keywords; break; case MODIFY_REMOVE: remove_keywords = keywords; break; } /* Update add_seq and remove_seq arrays which describe the keyword changes. First do the removes, since replace removes everything first. */ if (remove_keywords != NULL) { for (i = 0; i < remove_keywords->count; i++) { u = array_idx_modifiable(&t->keyword_updates, remove_keywords->idx[i]); seq_range_array_remove(&u->add_seq, seq); /* Don't bother updating remove_seq for new messages, since their initial state is "no keyword" anyway */ if (seq < t->first_new_seq) { seq_range_array_add_with_init(&u->remove_seq, 16, seq); } } } if (add_keywords != NULL) { for (i = 0; i < add_keywords->count; i++) { u = array_idx_modifiable(&t->keyword_updates, add_keywords->idx[i]); seq_range_array_add_with_init(&u->add_seq, 16, seq); seq_range_array_remove(&u->remove_seq, seq); } } if (unref_keywords != NULL) mail_index_keywords_unref(&unref_keywords); t->log_updates = TRUE; } bool mail_index_cancel_flag_updates(struct mail_index_transaction *t, uint32_t seq) { struct mail_index_flag_update *updates, tmp_update; unsigned int i, count; if (!array_is_created(&t->updates)) return FALSE; updates = array_get_modifiable(&t->updates, &count); i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq); if (i == count) return FALSE; else { i_assert(seq <= updates[i].uid2); if (seq < updates[i].uid1) return FALSE; } /* exists */ if (updates[i].uid1 == seq) { if (updates[i].uid2 != seq) updates[i].uid1++; else if (count > 1) array_delete(&t->updates, i, 1); else array_free(&t->updates); } else if (updates[i].uid2 == seq) { updates[i].uid2--; } else { /* need to split it in two */ tmp_update = updates[i]; tmp_update.uid1 = seq+1; updates[i].uid2 = seq-1; array_insert(&t->updates, i + 1, &tmp_update, 1); } return TRUE; } static bool mail_index_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq) { if (array_is_created(array)) { if (seq_range_array_remove(array, seq)) { if (array_count(array) == 0) array_free(array); return TRUE; } } return FALSE; } bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t, uint32_t seq) { struct mail_index_transaction_keyword_update *kw; bool ret = FALSE, have_kw_changes = FALSE; if (!array_is_created(&t->keyword_updates)) return FALSE; array_foreach_modifiable(&t->keyword_updates, kw) { if (mail_index_cancel_array(&kw->add_seq, seq)) ret = TRUE; if (mail_index_cancel_array(&kw->remove_seq, seq)) ret = TRUE; if (array_is_created(&kw->add_seq) || array_is_created(&kw->remove_seq)) have_kw_changes = TRUE; } if (!have_kw_changes) array_free(&t->keyword_updates); return ret; } void mail_index_transaction_reset(struct mail_index_transaction *t) { t->v.reset(t); } void mail_index_reset(struct mail_index_transaction *t) { mail_index_transaction_reset(t); t->reset = TRUE; } void mail_index_unset_fscked(struct mail_index_transaction *t) { struct mail_index_header new_hdr = *mail_index_get_header(t->view); i_assert(t->view->index->log_sync_locked); /* remove fsck'd-flag if it exists. */ if ((new_hdr.flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0) { new_hdr.flags &= ~MAIL_INDEX_HDR_FLAG_FSCKD; mail_index_update_header(t, offsetof(struct mail_index_header, flags), &new_hdr.flags, sizeof(new_hdr.flags), FALSE); } } void mail_index_set_deleted(struct mail_index_transaction *t) { i_assert(!t->index_undeleted); t->index_deleted = TRUE; } void mail_index_set_undeleted(struct mail_index_transaction *t) { i_assert(!t->index_deleted); t->index_undeleted = TRUE; } void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t, uint64_t max_modseq, ARRAY_TYPE(seq_range) *seqs) { i_assert(array_is_created(seqs)); t->max_modseq = max_modseq; t->conflict_seqs = seqs; } dovecot-2.2.33.2/src/lib-index/mail-transaction-log-private.h0000644000175000017500000001317613165463624020656 00000000000000#ifndef MAIL_TRANSACTION_LOG_VIEW_H #define MAIL_TRANSACTION_LOG_VIEW_H #include "buffer.h" #include "mail-transaction-log.h" struct dotlock_settings; /* Synchronization can take a while sometimes, especially when copying lots of mails. */ #define MAIL_TRANSACTION_LOG_LOCK_TIMEOUT (3*60) #define MAIL_TRANSACTION_LOG_LOCK_CHANGE_TIMEOUT (3*60) /* Rotate when log is older than ROTATE_TIME and larger than MIN_SIZE */ #define MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_MIN_SIZE (1024*32) /* If log is larger than MAX_SIZE, rotate regardless of the time */ #define MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_MAX_SIZE (1024*1024) #define MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_TIME (60*5) /* Delete .log.2 files older than this many seconds. Don't be too eager, older files are useful for QRESYNC and dsync. */ #define MAIL_TRANSACTION_LOG2_DEFAULT_STALE_SECS (60*60*24*2) #define MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file) ((file)->fd == -1) #define LOG_FILE_MODSEQ_CACHE_SIZE 10 struct modseq_cache { uoff_t offset; uint64_t highest_modseq; }; struct mail_transaction_log_file { struct mail_transaction_log *log; struct mail_transaction_log_file *next; /* refcount=0 is a valid state. files start that way, and they're freed only when mail_transaction_logs_clean() is called. */ int refcount; char *filepath; int fd; ino_t st_ino; dev_t st_dev; time_t last_mtime; uoff_t last_size; time_t last_mmap_error_time; struct mail_transaction_log_header hdr; buffer_t mmap_buffer; buffer_t *buffer; uoff_t buffer_offset; void *mmap_base; size_t mmap_size; /* points to the next uncommitted transaction. usually same as EOF. */ uoff_t sync_offset; /* highest modseq at sync_offset */ uint64_t sync_highest_modseq; /* saved_tail_offset is the offset that was last written to transaction log. max_tail_offset is what should be written to the log the next time a transaction is written. transaction log handling may update max_tail_offset automatically by making it skip external transactions after the last saved offset (to avoid re-reading them unneededly). */ uoff_t saved_tail_offset, max_tail_offset; /* don't give warnings about saved_tail_offset shrinking if sync_offset is less than this. */ uoff_t saved_tail_sync_offset; /* if we've seen _INDEX_[UN9DELETED transaction in this file, this is the offset. otherwise (uoff_t)-1 */ uoff_t index_deleted_offset, index_undeleted_offset; struct modseq_cache modseq_cache[LOG_FILE_MODSEQ_CACHE_SIZE]; struct file_lock *file_lock; time_t lock_created; unsigned int locked:1; unsigned int locked_sync_offset_updated:1; unsigned int corrupted:1; unsigned int need_rotate:1; }; struct mail_transaction_log { struct mail_index *index; struct mail_transaction_log_view *views; char *filepath, *filepath2; /* files is a linked list of all the opened log files. the list is sorted by the log file sequence, so that transaction views can use them easily. head contains a pointer to the newest log file. */ struct mail_transaction_log_file *files, *head; /* open_file is used temporarily while opening the log file. if _open() failed, it's left there for _create(). */ struct mail_transaction_log_file *open_file; unsigned int dotlock_count; struct dotlock *dotlock; unsigned int nfs_flush:1; unsigned int log_2_unlink_checked:1; }; void mail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file, const char *fmt, ...) ATTR_FORMAT(2, 3); void mail_transaction_log_get_dotlock_set(struct mail_transaction_log *log, struct dotlock_settings *set_r); struct mail_transaction_log_file * mail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log); struct mail_transaction_log_file * mail_transaction_log_file_alloc(struct mail_transaction_log *log, const char *path); void mail_transaction_log_file_free(struct mail_transaction_log_file **file); /* Returns 1 if log was opened, 0 if it didn't exist or was already open, -1 if error. */ int mail_transaction_log_file_open(struct mail_transaction_log_file *file, const char **reason_r); int mail_transaction_log_file_create(struct mail_transaction_log_file *file, bool reset); int mail_transaction_log_file_lock(struct mail_transaction_log_file *file); int mail_transaction_log_find_file(struct mail_transaction_log *log, uint32_t file_seq, bool nfs_flush, struct mail_transaction_log_file **file_r, const char **reason_r); /* Returns 1 if ok, 0 if file is corrupted or offset range is invalid, -1 if I/O error */ int mail_transaction_log_file_map(struct mail_transaction_log_file *file, uoff_t start_offset, uoff_t end_offset, const char **reason_r); void mail_transaction_log_file_move_to_memory(struct mail_transaction_log_file *file); void mail_transaction_logs_clean(struct mail_transaction_log *log); bool mail_transaction_log_want_rotate(struct mail_transaction_log *log); int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset); int mail_transaction_log_lock_head(struct mail_transaction_log *log, const char *lock_reason); void mail_transaction_log_file_unlock(struct mail_transaction_log_file *file, const char *lock_reason); void mail_transaction_update_modseq(const struct mail_transaction_header *hdr, const void *data, uint64_t *cur_modseq, unsigned int version); int mail_transaction_log_file_get_highest_modseq_at( struct mail_transaction_log_file *file, uoff_t offset, uint64_t *highest_modseq_r, const char **error_r); int mail_transaction_log_file_get_modseq_next_offset( struct mail_transaction_log_file *file, uint64_t modseq, uoff_t *next_offset_r); #endif dovecot-2.2.33.2/src/lib-index/mail-index-sync-ext.c0000644000175000017500000005654213165463624016760 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-index-modseq.h" #include "mail-transaction-log.h" void mail_index_sync_init_expunge_handlers(struct mail_index_sync_map_ctx *ctx) { const struct mail_index_ext *ext; const struct mail_index_registered_ext *rext; const uint32_t *id_map; void **contexts; struct mail_index_expunge_handler eh; unsigned int ext_count, id_map_count; unsigned int rext_count, context_count; uint32_t idx_ext_id, map_ext_id; if (!array_is_created(&ctx->view->map->extensions)) return; i_zero(&eh); if (array_is_created(&ctx->expunge_handlers)) array_clear(&ctx->expunge_handlers); else i_array_init(&ctx->expunge_handlers, 64); rext = array_get(&ctx->view->index->extensions, &rext_count); ext = array_get(&ctx->view->map->extensions, &ext_count); id_map = array_get(&ctx->view->map->ext_id_map, &id_map_count); contexts = array_get_modifiable(&ctx->extra_contexts, &context_count); i_assert(context_count >= rext_count); for (idx_ext_id = 0; idx_ext_id < rext_count; idx_ext_id++) { map_ext_id = idx_ext_id >= id_map_count ? (uint32_t)-1 : id_map[idx_ext_id]; if (rext[idx_ext_id].expunge_handler == NULL || (map_ext_id == (uint32_t)-1 && !rext[idx_ext_id].expunge_handler_call_always)) continue; eh.handler = rext[idx_ext_id].expunge_handler; eh.context = rext[idx_ext_id].expunge_context; eh.sync_context = &contexts[idx_ext_id]; eh.record_offset = map_ext_id == (uint32_t)-1 ? 0 : ext[map_ext_id].record_offset; array_append(&ctx->expunge_handlers, &eh, 1); } ctx->expunge_handlers_set = TRUE; ctx->expunge_handlers_used = TRUE; } void mail_index_sync_deinit_expunge_handlers(struct mail_index_sync_map_ctx *ctx) { const struct mail_index_expunge_handler *eh; if (!array_is_created(&ctx->expunge_handlers)) return; array_foreach(&ctx->expunge_handlers, eh) { if (eh->sync_context != NULL) { eh->handler(ctx, 0, NULL, eh->sync_context, eh->context); } } array_free(&ctx->expunge_handlers); } void mail_index_sync_init_handlers(struct mail_index_sync_map_ctx *ctx) { unsigned int count; if (!array_is_created(&ctx->view->map->extensions)) return; /* set space for extra contexts */ count = array_count(&ctx->view->index->extensions); i_assert(count > 0); if (!array_is_created(&ctx->extra_contexts)) i_array_init(&ctx->extra_contexts, count); /* make sure the extra_contexts contains everything */ (void)array_idx_modifiable(&ctx->extra_contexts, count - 1); /* we need to update the expunge handler list in case they had already been called */ ctx->expunge_handlers_set = FALSE; } void mail_index_sync_deinit_handlers(struct mail_index_sync_map_ctx *ctx) { const struct mail_index_registered_ext *rext; void **extra_contexts; unsigned int i, rext_count, context_count; if (!array_is_created(&ctx->extra_contexts)) return; rext = array_get(&ctx->view->index->extensions, &rext_count); extra_contexts = array_get_modifiable(&ctx->extra_contexts, &context_count); i_assert(context_count <= rext_count); for (i = 0; i < context_count; i++) { if (extra_contexts[i] != NULL) { rext[i].sync_handler.callback(ctx, 0, NULL, NULL, &extra_contexts[i]); } } array_free(&ctx->extra_contexts); } static struct mail_index_ext_header * get_ext_header(struct mail_index_map *map, const struct mail_index_ext *ext) { struct mail_index_ext_header *ext_hdr; void *hdr_base; /* do some kludgy jumping to get to it. */ hdr_base = buffer_get_modifiable_data(map->hdr_copy_buf, NULL); ext_hdr = PTR_OFFSET(hdr_base, ext->ext_offset); i_assert(memcmp((char *)(ext_hdr + 1), ext->name, strlen(ext->name)) == 0); return ext_hdr; } static int mail_index_ext_align_cmp(const void *p1, const void *p2) { const struct mail_index_ext *const *e1 = p1, *const *e2 = p2; return (int)(*e2)->record_align - (int)(*e1)->record_align; } static void sync_ext_reorder(struct mail_index_map *map, uint32_t ext_map_idx, uint16_t old_ext_size) { struct mail_index_ext *ext, **sorted; struct mail_index_ext_header *ext_hdr; uint16_t *old_offsets, *copy_sizes, min_align, max_align; uint32_t offset, new_record_size, rec_idx; unsigned int i, count; const void *src; buffer_t *new_buffer; size_t new_buffer_size; i_assert(MAIL_INDEX_MAP_IS_IN_MEMORY(map) && map->refcount == 1); ext = array_get_modifiable(&map->extensions, &count); i_assert(ext_map_idx < count); /* @UNSAFE */ old_offsets = t_new(uint16_t, count); copy_sizes = t_new(uint16_t, count); sorted = t_new(struct mail_index_ext *, count); for (i = 0; i < count; i++) { old_offsets[i] = ext[i].record_offset; copy_sizes[i] = ext[i].record_size; ext[i].record_offset = 0; sorted[i] = &ext[i]; } qsort(sorted, count, sizeof(struct mail_index_ext *), mail_index_ext_align_cmp); if (copy_sizes[ext_map_idx] > old_ext_size) { /* we are growing the extension record. remember this so we don't write extra data while copying the record */ copy_sizes[ext_map_idx] = old_ext_size; } /* we simply try to use the extensions with largest alignment requirement first. FIXME: if the extension sizes don't match alignment, this may not give the minimal layout. */ offset = MAIL_INDEX_RECORD_MIN_SIZE; max_align = sizeof(uint32_t); for (;;) { min_align = (uint16_t)-1; for (i = 0; i < count; i++) { if (sorted[i]->record_align > max_align) max_align = sorted[i]->record_align; if (sorted[i]->record_offset == 0 && sorted[i]->record_size > 0) { if ((offset % sorted[i]->record_align) == 0) break; if (sorted[i]->record_align < min_align) min_align = sorted[i]->record_align; } } if (i == count) { if (min_align == (uint16_t)-1) { /* all done */ break; } /* we have to leave space here */ i_assert(min_align > 1 && min_align < (uint16_t)-1); offset += min_align - (offset % min_align); } else { sorted[i]->record_offset = offset; offset += sorted[i]->record_size; } i_assert(offset < (uint16_t)-1); } if ((offset % max_align) != 0) { /* keep record size divisible with maximum alignment */ offset += max_align - (offset % max_align); } new_record_size = offset; i_assert(new_record_size >= sizeof(struct mail_index_record)); /* copy the records to new buffer */ new_buffer_size = map->rec_map->records_count * new_record_size; new_buffer = buffer_create_dynamic(default_pool, new_buffer_size); src = map->rec_map->records; offset = 0; for (rec_idx = 0; rec_idx < map->rec_map->records_count; rec_idx++) { /* write the base record */ buffer_write(new_buffer, offset, src, sizeof(struct mail_index_record)); /* write extensions */ for (i = 0; i < count; i++) { buffer_write(new_buffer, offset + ext[i].record_offset, CONST_PTR_OFFSET(src, old_offsets[i]), copy_sizes[i]); } src = CONST_PTR_OFFSET(src, map->hdr.record_size); offset += new_record_size; } if (new_buffer->used != new_buffer_size) { /* we didn't fully write the last record */ size_t space = new_buffer_size - new_buffer->used; i_assert(space < new_record_size); buffer_append_zero(new_buffer, space); } buffer_free(&map->rec_map->buffer); map->rec_map->buffer = new_buffer; map->rec_map->records = buffer_get_modifiable_data(map->rec_map->buffer, NULL); map->hdr.record_size = new_record_size; /* update record offsets in headers */ for (i = 0; i < count; i++) { ext_hdr = get_ext_header(map, &ext[i]); ext_hdr->record_offset = ext[i].record_offset; } } static void sync_ext_resize(const struct mail_transaction_ext_intro *u, uint32_t ext_map_idx, struct mail_index_sync_map_ctx *ctx, bool no_shrink) { struct mail_index_map *map; struct mail_index_ext *ext; struct mail_index_ext_header *ext_hdr; uint32_t old_padded_hdr_size, new_padded_hdr_size, old_record_size; bool reorder = FALSE; ext = array_idx_modifiable(&ctx->view->map->extensions, ext_map_idx); old_padded_hdr_size = MAIL_INDEX_HEADER_SIZE_ALIGN(ext->hdr_size); new_padded_hdr_size = MAIL_INDEX_HEADER_SIZE_ALIGN(u->hdr_size); if (ext->record_align != u->record_align || ext->record_size != u->record_size) { /* record changed */ } else if (new_padded_hdr_size < old_padded_hdr_size) { /* header is shrunk. do we allow? */ if (no_shrink) return; } else if (ext->hdr_size == u->hdr_size) { /* no changes */ return; } /* something changed. get ourself a new map before we start changing anything in it. */ map = mail_index_sync_get_atomic_map(ctx); /* ext was duplicated to the new map. */ ext = array_idx_modifiable(&map->extensions, ext_map_idx); if (new_padded_hdr_size < old_padded_hdr_size) { /* header shrank */ if (no_shrink) new_padded_hdr_size = old_padded_hdr_size; else { buffer_delete(map->hdr_copy_buf, ext->hdr_offset + new_padded_hdr_size, old_padded_hdr_size - new_padded_hdr_size); ext->hdr_size = u->hdr_size; } } else if (new_padded_hdr_size > old_padded_hdr_size) { /* header grown */ buffer_insert_zero(map->hdr_copy_buf, ext->hdr_offset + old_padded_hdr_size, new_padded_hdr_size - old_padded_hdr_size); ext->hdr_size = u->hdr_size; } else { if (ext->hdr_size != u->hdr_size) { /* aligned sizes were the same, but the actual sizes had changed */ ext->hdr_size = u->hdr_size; } } if (ext->record_align < u->record_align || (ext->record_align > u->record_align && !no_shrink)) { ext->record_align = u->record_align; reorder = TRUE; } old_record_size = ext->record_size; if (ext->record_size < u->record_size || (ext->record_size > u->record_size && !no_shrink)) { ext->record_size = u->record_size; reorder = TRUE; } i_assert((map->hdr_copy_buf->used % sizeof(uint64_t)) == 0); map->hdr_base = map->hdr_copy_buf->data; map->hdr.header_size = map->hdr_copy_buf->used; ext_hdr = get_ext_header(map, ext); ext_hdr->reset_id = ext->reset_id; ext_hdr->hdr_size = ext->hdr_size; ext_hdr->record_offset = ext->record_offset; ext_hdr->record_size = ext->record_size; ext_hdr->record_align = ext->record_align; if (new_padded_hdr_size != old_padded_hdr_size) { /* move all hdr_offset of all extensions after this one */ unsigned int i, count = array_count(&map->extensions); ssize_t diff = (ssize_t)new_padded_hdr_size - (ssize_t)old_padded_hdr_size; ext = array_idx_modifiable(&map->extensions, 0); for (i = ext_map_idx + 1; i < count; i++) { ext[i].ext_offset += diff; ext[i].hdr_offset += diff; } } if (reorder) sync_ext_reorder(map, ext_map_idx, old_record_size); } static bool mail_index_sync_ext_unknown_complain(struct mail_index_sync_map_ctx *ctx, uint32_t ext_map_idx) { unsigned char *p; if (ext_map_idx >= 1024) { /* don't try to track too high values */ return TRUE; } if (ctx->unknown_extensions == NULL) { ctx->unknown_extensions = buffer_create_dynamic(default_pool, ext_map_idx + 8); } p = buffer_get_space_unsafe(ctx->unknown_extensions, ext_map_idx, 1); if (*p != 0) { /* we've already complained once */ return FALSE; } *p = 1; return TRUE; } static void mail_index_sync_ext_init_new(struct mail_index_sync_map_ctx *ctx, const char *name, const struct mail_index_ext_header *ext_hdr, uint32_t *ext_map_idx_r) { struct mail_index_map *map; const struct mail_index_ext *ext; buffer_t *hdr_buf; uint32_t ext_map_idx; /* be sure to get a unique mapping before we modify the extensions, otherwise other map users will see the new extension but not the data records that sync_ext_reorder() adds. */ map = mail_index_sync_get_atomic_map(ctx); hdr_buf = map->hdr_copy_buf; i_assert(hdr_buf->used == map->hdr.header_size); if (MAIL_INDEX_HEADER_SIZE_ALIGN(hdr_buf->used) != hdr_buf->used) { /* we need to add padding between base header and extensions */ buffer_append_zero(hdr_buf, MAIL_INDEX_HEADER_SIZE_ALIGN(hdr_buf->used) - hdr_buf->used); } /* register record offset initially using zero, sync_ext_reorder() will fix it. */ ext_map_idx = mail_index_map_register_ext(map, name, hdr_buf->used, ext_hdr); ext = array_idx(&map->extensions, ext_map_idx); /* [padding] [header data] */ i_assert(ext_hdr->name_size == strlen(name)); buffer_append(hdr_buf, ext_hdr, sizeof(*ext_hdr)); buffer_append(hdr_buf, name, ext_hdr->name_size); /* header must begin and end in correct alignment */ buffer_append_zero(hdr_buf, MAIL_INDEX_HEADER_SIZE_ALIGN(hdr_buf->used) - hdr_buf->used + MAIL_INDEX_HEADER_SIZE_ALIGN(ext->hdr_size)); i_assert(hdr_buf->used == ext->hdr_offset + MAIL_INDEX_HEADER_SIZE_ALIGN(ext->hdr_size)); i_assert((hdr_buf->used % sizeof(uint64_t)) == 0); map->hdr.header_size = hdr_buf->used; map->hdr_base = hdr_buf->data; mail_index_sync_init_handlers(ctx); sync_ext_reorder(map, ext_map_idx, 0); i_assert(ext->record_offset != 0 || ext->record_size == 0); *ext_map_idx_r = ext_map_idx; } void mail_index_sync_ext_init(struct mail_index_sync_map_ctx *ctx, const char *name, bool fix_size, uint32_t *ext_map_idx_r) { struct mail_index_map *map = ctx->view->map; const struct mail_index_registered_ext *rext; struct mail_index_ext_header ext_hdr; struct mail_transaction_ext_intro u; uint32_t ext_id; if (!mail_index_ext_lookup(ctx->view->index, name, &ext_id)) i_unreached(); rext = array_idx(&ctx->view->index->extensions, ext_id); if (mail_index_map_lookup_ext(map, name, ext_map_idx_r)) { if (!fix_size) return; /* make sure it's the expected size */ i_zero(&u); u.hdr_size = rext->hdr_size; u.record_size = rext->record_size; u.record_align = rext->record_align; sync_ext_resize(&u, *ext_map_idx_r, ctx, FALSE); } else { i_zero(&ext_hdr); ext_hdr.name_size = strlen(name); ext_hdr.hdr_size = rext->hdr_size; ext_hdr.record_size = rext->record_size; ext_hdr.record_align = rext->record_align; mail_index_sync_ext_init_new(ctx, name, &ext_hdr, ext_map_idx_r); } } int mail_index_sync_ext_intro(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_intro *u) { struct mail_index_map *map = ctx->view->map; struct mail_index_ext_header ext_hdr; const struct mail_index_ext *ext; const char *name, *error; uint32_t ext_map_idx; bool no_shrink; /* default to ignoring the following extension updates in case this intro is corrupted */ ctx->cur_ext_map_idx = (uint32_t)-2; ctx->cur_ext_ignore = TRUE; ctx->cur_ext_record_size = 0; if (u->ext_id != (uint32_t)-1 && (!array_is_created(&map->extensions) || u->ext_id >= array_count(&map->extensions))) { if (!mail_index_sync_ext_unknown_complain(ctx, u->ext_id)) return -1; mail_index_sync_set_corrupted(ctx, "Extension introduction for unknown id %u", u->ext_id); return -1; } if (u->ext_id == (uint32_t)-1 && u->name_size == 0) { mail_index_sync_set_corrupted(ctx, "Extension introduction without id or name"); return -1; } if (u->ext_id != (uint32_t)-1) { name = NULL; ext_map_idx = u->ext_id; } else { name = t_strndup(u + 1, u->name_size); if (!mail_index_map_lookup_ext(map, name, &ext_map_idx)) ext_map_idx = (uint32_t)-1; } if (ext_map_idx == (uint32_t)-1) ext = NULL; else { ext = array_idx(&map->extensions, ext_map_idx); name = ext->name; } i_assert(name != NULL); if (!ctx->internal_update && strcmp(name, MAIL_INDEX_EXT_KEYWORDS) == 0) { /* Keyword extension is handled internally by the keyword code. Any attempt to modify them directly could cause assert-crashes later, so prevent them immediately. */ mail_index_sync_set_corrupted(ctx, "Extension introduction for keywords"); return -1; } i_zero(&ext_hdr); ext_hdr.name_size = strlen(name); ext_hdr.reset_id = u->reset_id; ext_hdr.hdr_size = u->hdr_size; ext_hdr.record_size = u->record_size; ext_hdr.record_align = u->record_align; no_shrink = (u->flags & MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK) != 0; /* make sure the header looks valid before doing anything with it */ if (mail_index_map_ext_hdr_check(&map->hdr, &ext_hdr, name, &error) < 0) { mail_index_sync_set_corrupted(ctx, "Broken extension introduction: %s", error); return -1; } ctx->cur_ext_record_size = u->record_size; if (ext != NULL) { /* exists already */ if (u->reset_id == ext->reset_id) { /* check if we need to resize anything */ sync_ext_resize(u, ext_map_idx, ctx, no_shrink); ctx->cur_ext_ignore = FALSE; } else { /* extension was reset and this transaction hadn't yet seen it. ignore this update (except for resets). */ ctx->cur_ext_ignore = TRUE; } ctx->cur_ext_map_idx = ext_map_idx; return 1; } mail_index_sync_ext_init_new(ctx, name, &ext_hdr, &ext_map_idx); ctx->cur_ext_ignore = FALSE; ctx->cur_ext_map_idx = ctx->internal_update ? (uint32_t)-1 : ext_map_idx; return 1; } static void mail_index_sync_ext_clear(struct mail_index_view *view, struct mail_index_map *map, struct mail_index_ext *ext) { struct mail_index_record *rec; uint32_t seq; memset(buffer_get_space_unsafe(map->hdr_copy_buf, ext->hdr_offset, ext->hdr_size), 0, ext->hdr_size); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); for (seq = 1; seq <= view->map->rec_map->records_count; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); memset(PTR_OFFSET(rec, ext->record_offset), 0, ext->record_size); } } int mail_index_sync_ext_reset(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_reset *u) { struct mail_index_map *map; struct mail_index_ext_header *ext_hdr; struct mail_index_ext *ext; if (ctx->cur_ext_map_idx == (uint32_t)-1) { mail_index_sync_set_corrupted(ctx, "Extension reset without intro prefix"); return -1; } if (ctx->cur_ext_map_idx == (uint32_t)-2 && ctx->cur_ext_ignore) { /* previous extension intro was broken */ return -1; } /* since we're resetting the extension, don't check cur_ext_ignore */ /* a new index file will be created, so the old data won't be accidentally used by other processes. */ map = mail_index_sync_get_atomic_map(ctx); ext = array_idx_modifiable(&map->extensions, ctx->cur_ext_map_idx); ext->reset_id = u->new_reset_id; if (!u->preserve_data) mail_index_sync_ext_clear(ctx->view, map, ext); ext_hdr = get_ext_header(map, ext); ext_hdr->reset_id = u->new_reset_id; return 1; } int mail_index_sync_ext_hdr_update(struct mail_index_sync_map_ctx *ctx, uint32_t offset, uint32_t size, const void *data) { struct mail_index_map *map = ctx->view->map; const struct mail_index_ext *ext; if (ctx->cur_ext_map_idx == (uint32_t)-1) { mail_index_sync_set_corrupted(ctx, "Extension header update without intro prefix"); return -1; } if (ctx->cur_ext_ignore) return 1; ext = array_idx(&map->extensions, ctx->cur_ext_map_idx); if (offset + size > ext->hdr_size) { #if 1 mail_index_sync_set_corrupted(ctx, "Extension header update points outside header size"); return -1; #else size = offset > ext->hdr_size ? 0 : ext->hdr_size - offset; #endif } buffer_write(map->hdr_copy_buf, ext->hdr_offset + offset, data, size); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); if (ext->index_idx == ctx->view->index->modseq_ext_id) mail_index_modseq_hdr_update(ctx->modseq_ctx); return 1; } int mail_index_sync_ext_rec_update(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_rec_update *u) { struct mail_index_view *view = ctx->view; struct mail_index_record *rec; const struct mail_index_ext *ext; const struct mail_index_registered_ext *rext; void *old_data; uint32_t seq; int ret; i_assert(ctx->cur_ext_map_idx != (uint32_t)-1); i_assert(!ctx->cur_ext_ignore); if (u->uid == 0 || u->uid >= view->map->hdr.next_uid) { mail_index_sync_set_corrupted(ctx, "Extension record update for invalid uid=%u", u->uid); return -1; } if (!mail_index_lookup_seq(view, u->uid, &seq)) return 1; ext = array_idx(&view->map->extensions, ctx->cur_ext_map_idx); i_assert(ext->record_offset + ctx->cur_ext_record_size <= view->map->hdr.record_size); rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); old_data = PTR_OFFSET(rec, ext->record_offset); rext = array_idx(&view->index->extensions, ext->index_idx); /* call sync handlers only when its registered type matches with current synchronization type (index/view) */ if ((rext->sync_handler.type & ctx->type) != 0) { void **extra_context = array_idx_modifiable(&ctx->extra_contexts, ext->index_idx); ret = rext->sync_handler.callback(ctx, seq, old_data, u + 1, extra_context); if (ret <= 0) return ret; } /* @UNSAFE */ memcpy(old_data, u + 1, ctx->cur_ext_record_size); if (ctx->cur_ext_record_size < ext->record_size) { memset(PTR_OFFSET(old_data, ctx->cur_ext_record_size), 0, ext->record_size - ctx->cur_ext_record_size); } return 1; } int mail_index_sync_ext_atomic_inc(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_ext_atomic_inc *u) { struct mail_index_view *view = ctx->view; struct mail_index_record *rec; const struct mail_index_ext *ext; void *data; uint32_t seq; uint64_t min_value, max_value, orig_num; i_assert(ctx->cur_ext_map_idx != (uint32_t)-1); i_assert(!ctx->cur_ext_ignore); if (u->uid == 0 || u->uid >= view->map->hdr.next_uid) { mail_index_sync_set_corrupted(ctx, "Extension record inc for invalid uid=%u", u->uid); return -1; } if (!mail_index_lookup_seq(view, u->uid, &seq)) return 1; ext = array_idx(&view->map->extensions, ctx->cur_ext_map_idx); i_assert(ext->record_offset + ctx->cur_ext_record_size <= view->map->hdr.record_size); rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); data = PTR_OFFSET(rec, ext->record_offset); min_value = u->diff >= 0 ? 0 : (uint64_t)(-(int64_t)u->diff); max_value = ctx->cur_ext_record_size == 8 ? (uint64_t)-1 : ((uint64_t)1 << (ctx->cur_ext_record_size*8)) - 1; if (u->diff <= 0) { /* skip */ } else if (max_value >= (uint32_t)u->diff) { max_value -= u->diff; } else { mail_index_sync_set_corrupted(ctx, "Extension record inc diff=%d larger than max value=%u " "(uid=%u)", u->diff, (unsigned int)max_value, u->uid); return -1; } switch (ctx->cur_ext_record_size) { case 1: { uint8_t *num = data; orig_num = *num; if (orig_num >= min_value && orig_num <= max_value) *num += u->diff; break; } case 2: { uint16_t *num = data; orig_num = *num; if (orig_num >= min_value && orig_num <= max_value) *num += u->diff; break; } case 4: { uint32_t *num = data; orig_num = *num; if (orig_num >= min_value && orig_num <= max_value) *num += u->diff; break; } case 8: { uint64_t *num = data; orig_num = *num; if (orig_num >= min_value && orig_num <= max_value) *num += u->diff; break; } default: mail_index_sync_set_corrupted(ctx, "Extension record inc with invalid size=%u", ctx->cur_ext_record_size); return -1; } if (orig_num < min_value) { mail_index_sync_set_corrupted(ctx, "Extension record inc drops number below zero " "(uid=%u, diff=%d, orig=%llu)", u->uid, u->diff, (unsigned long long)orig_num); return -1; } else if (orig_num > max_value) { mail_index_sync_set_corrupted(ctx, "Extension record inc overflows number " "(uid=%u, diff=%d, orig=%llu)", u->uid, u->diff, (unsigned long long)orig_num); return -1; } return 1; } dovecot-2.2.33.2/src/lib-index/mail-transaction-log.c0000644000175000017500000004341013165463624017173 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "file-dotlock.h" #include "nfs-workarounds.h" #include "mmap-util.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include #include #include static void mail_transaction_log_set_head(struct mail_transaction_log *log, struct mail_transaction_log_file *file) { i_assert(log->head != file); file->refcount++; log->head = file; i_assert(log->files != NULL); i_assert(log->files->next != NULL || log->files == file); } struct mail_transaction_log * mail_transaction_log_alloc(struct mail_index *index) { struct mail_transaction_log *log; log = i_new(struct mail_transaction_log, 1); log->index = index; return log; } static void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log) { struct stat st; uint32_t log2_rotate_time = log->index->map->hdr.log2_rotate_time; if (MAIL_INDEX_IS_IN_MEMORY(log->index)) return; if (log2_rotate_time == 0) { if (nfs_safe_stat(log->filepath2, &st) == 0) log2_rotate_time = st.st_mtime; else if (errno == ENOENT) log2_rotate_time = (uint32_t)-1; else { mail_index_set_error(log->index, "stat(%s) failed: %m", log->filepath2); return; } } if (log2_rotate_time != (uint32_t)-1 && ioloop_time - (time_t)log2_rotate_time >= (time_t)log->index->log_rotate_log2_stale_secs && !log->index->readonly) { i_unlink_if_exists(log->filepath2); log2_rotate_time = (uint32_t)-1; } if (log2_rotate_time != log->index->map->hdr.log2_rotate_time) { /* Write this as part of the next sync's transaction. We're here because we're already opening a sync lock, so it'll always happen. It's also required especially with mdbox map index, which doesn't like changes done outside syncing. */ log->index->pending_log2_rotate_time = log2_rotate_time; } } int mail_transaction_log_open(struct mail_transaction_log *log) { struct mail_transaction_log_file *file; const char *reason; int ret; i_free(log->filepath); i_free(log->filepath2); log->filepath = i_strconcat(log->index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL); log->filepath2 = i_strconcat(log->filepath, ".2", NULL); /* these settings aren't available at alloc() time, so we need to set them here: */ log->nfs_flush = (log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0; if (log->open_file != NULL) mail_transaction_log_file_free(&log->open_file); if (MAIL_INDEX_IS_IN_MEMORY(log->index)) return 0; file = mail_transaction_log_file_alloc(log, log->filepath); if ((ret = mail_transaction_log_file_open(file, &reason)) <= 0) { /* leave the file for _create() */ log->open_file = file; return ret; } mail_transaction_log_set_head(log, file); return 1; } int mail_transaction_log_create(struct mail_transaction_log *log, bool reset) { struct mail_transaction_log_file *file; if (MAIL_INDEX_IS_IN_MEMORY(log->index)) { file = mail_transaction_log_file_alloc_in_memory(log); mail_transaction_log_set_head(log, file); return 0; } file = mail_transaction_log_file_alloc(log, log->filepath); if (log->open_file != NULL) { /* remember what file we tried to open. if someone else created a new file, use it instead of recreating it */ file->st_ino = log->open_file->st_ino; file->st_dev = log->open_file->st_dev; file->last_size = log->open_file->last_size; file->last_mtime = log->open_file->last_mtime; mail_transaction_log_file_free(&log->open_file); } if (mail_transaction_log_file_create(file, reset) < 0) { mail_transaction_log_file_free(&file); return -1; } mail_transaction_log_set_head(log, file); return 1; } void mail_transaction_log_close(struct mail_transaction_log *log) { i_assert(log->views == NULL); if (log->open_file != NULL) mail_transaction_log_file_free(&log->open_file); if (log->head != NULL) log->head->refcount--; mail_transaction_logs_clean(log); i_assert(log->files == NULL); } void mail_transaction_log_free(struct mail_transaction_log **_log) { struct mail_transaction_log *log = *_log; *_log = NULL; mail_transaction_log_close(log); log->index->log = NULL; i_free(log->filepath); i_free(log->filepath2); i_free(log); } void mail_transaction_log_move_to_memory(struct mail_transaction_log *log) { struct mail_transaction_log_file *file; if (!log->index->initial_mapped && log->files != NULL && log->files->hdr.prev_file_seq != 0) { /* we couldn't read dovecot.index and we don't have the first .log file, so just start from scratch */ mail_transaction_log_close(log); } i_free(log->filepath); i_free(log->filepath2); log->filepath = i_strconcat(log->index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL); log->filepath2 = i_strconcat(log->filepath, ".2", NULL); if (log->head != NULL) mail_transaction_log_file_move_to_memory(log->head); else { file = mail_transaction_log_file_alloc_in_memory(log); mail_transaction_log_set_head(log, file); } } void mail_transaction_log_indexid_changed(struct mail_transaction_log *log) { struct mail_transaction_log_file *file; mail_transaction_logs_clean(log); for (file = log->files; file != NULL; file = file->next) { if (file->hdr.indexid != log->index->indexid) { mail_transaction_log_file_set_corrupted(file, "indexid changed: %u -> %u", file->hdr.indexid, log->index->indexid); } } if (log->head != NULL && log->head->hdr.indexid != log->index->indexid) { if (--log->head->refcount == 0) mail_transaction_log_file_free(&log->head); (void)mail_transaction_log_create(log, FALSE); } } void mail_transaction_logs_clean(struct mail_transaction_log *log) { struct mail_transaction_log_file *file, *next; /* remove only files from the beginning. this way if a view has referenced an old file, it can still find the new files even if there aren't any references to it currently. */ for (file = log->files; file != NULL; file = next) { next = file->next; i_assert(file->refcount >= 0); if (file->refcount > 0) break; mail_transaction_log_file_free(&file); } /* sanity check: we shouldn't have locked refcount=0 files */ for (; file != NULL; file = file->next) { i_assert(!file->locked || file->refcount > 0); } i_assert(log->head == NULL || log->files != NULL); } bool mail_transaction_log_want_rotate(struct mail_transaction_log *log) { struct mail_transaction_log_file *file = log->head; if (file->need_rotate) return TRUE; if (file->hdr.major_version < MAIL_TRANSACTION_LOG_MAJOR_VERSION || (file->hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION && file->hdr.minor_version < MAIL_TRANSACTION_LOG_MINOR_VERSION)) { /* upgrade immediately to a new log file format */ return TRUE; } if (file->sync_offset > log->index->log_rotate_max_size) { /* file is too large, definitely rotate */ return TRUE; } if (file->sync_offset < log->index->log_rotate_min_size) { /* file is still too small */ return FALSE; } /* rotate if the timestamp is old enough */ return file->hdr.create_stamp < ioloop_time - log->index->log_rotate_min_created_ago_secs; } int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset) { struct mail_transaction_log_file *file; const char *path = log->head->filepath; struct stat st; int ret; i_assert(log->head->locked); if (MAIL_INDEX_IS_IN_MEMORY(log->index)) { file = mail_transaction_log_file_alloc_in_memory(log); if (reset) { file->hdr.prev_file_seq = 0; file->hdr.prev_file_offset = 0; } } else { /* we're locked, we shouldn't need to worry about ESTALE problems in here. */ if (fstat(log->head->fd, &st) < 0) { mail_index_file_set_syscall_error(log->index, log->head->filepath, "fstat()"); return -1; } file = mail_transaction_log_file_alloc(log, path); file->st_dev = st.st_dev; file->st_ino = st.st_ino; file->last_mtime = st.st_mtime; file->last_size = st.st_size; if ((ret = mail_transaction_log_file_create(file, reset)) < 0) { mail_transaction_log_file_free(&file); return -1; } if (ret == 0) { mail_index_set_error(log->index, "Transaction log %s was recreated while we had it locked - " "locking is broken (lock_method=%s)", path, file_lock_method_to_str(log->index->lock_method)); mail_transaction_log_file_free(&file); return -1; } i_assert(file->locked); } if (--log->head->refcount == 0) mail_transaction_logs_clean(log); else { /* the newly created log file is already locked */ mail_transaction_log_file_unlock(log->head, !log->index->log_sync_locked ? "rotating" : "rotating while syncing"); } mail_transaction_log_set_head(log, file); return 0; } static int mail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush, const char **reason_r) { struct mail_transaction_log_file *file; struct stat st; i_assert(log->head != NULL); if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head)) { *reason_r = "Log is in memory"; return 0; } if (nfs_flush && log->nfs_flush) nfs_flush_file_handle_cache(log->filepath); if (nfs_safe_stat(log->filepath, &st) < 0) { if (errno != ENOENT) { mail_index_file_set_syscall_error(log->index, log->filepath, "stat()"); *reason_r = t_strdup_printf("stat(%s) failed: %m", log->filepath); return -1; } /* We shouldn't lose dovecot.index.log unless the mailbox was deleted or renamed. Just fail this and let the mailbox opening code figure out whether to create a new log file or not. Anything else can cause unwanted behavior (e.g. mailbox deletion not fully finishing due to .nfs* files and an IDLEing IMAP process creating the index back here). */ log->index->index_deleted = TRUE; *reason_r = "Trasnaction log lost while it was open"; return -1; } else if (log->head->st_ino == st.st_ino && CMP_DEV_T(log->head->st_dev, st.st_dev)) { /* NFS: log files get rotated to .log.2 files instead of being unlinked, so we don't bother checking if the existing file has already been unlinked here (in which case inodes could match but point to different files) */ *reason_r = "Log inode is unchanged"; return 0; } file = mail_transaction_log_file_alloc(log, log->filepath); if (mail_transaction_log_file_open(file, reason_r) <= 0) { mail_transaction_log_file_free(&file); return -1; } i_assert(!file->locked); if (--log->head->refcount == 0) mail_transaction_logs_clean(log); mail_transaction_log_set_head(log, file); *reason_r = "Log reopened"; return 0; } void mail_transaction_log_get_mailbox_sync_pos(struct mail_transaction_log *log, uint32_t *file_seq_r, uoff_t *file_offset_r) { *file_seq_r = log->head->hdr.file_seq; *file_offset_r = log->head->max_tail_offset; } void mail_transaction_log_set_mailbox_sync_pos(struct mail_transaction_log *log, uint32_t file_seq, uoff_t file_offset) { i_assert(file_seq == log->head->hdr.file_seq); i_assert(file_offset >= log->head->saved_tail_offset); if (file_offset >= log->head->max_tail_offset) log->head->max_tail_offset = file_offset; } int mail_transaction_log_find_file(struct mail_transaction_log *log, uint32_t file_seq, bool nfs_flush, struct mail_transaction_log_file **file_r, const char **reason_r) { struct mail_transaction_log_file *file; const char *reason; int ret; if (file_seq > log->head->hdr.file_seq) { /* see if the .log file has been recreated */ if (log->head->locked) { /* transaction log is locked. there's no way a newer file exists. */ *reason_r = "Log is locked - newer log can't exist"; return 0; } if (mail_transaction_log_refresh(log, FALSE, &reason) < 0) { *reason_r = reason; return -1; } if (file_seq > log->head->hdr.file_seq) { if (!nfs_flush || !log->nfs_flush) { *reason_r = t_strdup_printf( "Requested newer log than exists: %s", reason); return 0; } /* try again, this time flush attribute cache */ if (mail_transaction_log_refresh(log, TRUE, &reason) < 0) { *reason_r = t_strdup_printf( "Log refresh with NFS flush failed: %s", reason); return -1; } if (file_seq > log->head->hdr.file_seq) { *reason_r = t_strdup_printf( "Requested newer log than exists - " "still after NFS flush: %s", reason); return 0; } } } for (file = log->files; file != NULL; file = file->next) { if (file->hdr.file_seq == file_seq) { *file_r = file; return 1; } if (file->hdr.file_seq > file_seq && file->hdr.prev_file_seq == 0) { /* Fail here mainly to avoid unnecessarily trying to open .log.2 that most likely doesn't even exist. */ *reason_r = "Log was reset after requested file_seq"; return 0; } } if (MAIL_INDEX_IS_IN_MEMORY(log->index)) { *reason_r = "Logs are only in memory"; return 0; } /* see if we have it in log.2 file */ file = mail_transaction_log_file_alloc(log, log->filepath2); if ((ret = mail_transaction_log_file_open(file, reason_r)) <= 0) { mail_transaction_log_file_free(&file); return ret; } /* but is it what we expected? */ if (file->hdr.file_seq != file_seq) { *reason_r = t_strdup_printf(".log.2 contains file_seq=%u", file->hdr.file_seq); return 0; } *file_r = file; return 1; } int mail_transaction_log_lock_head(struct mail_transaction_log *log, const char *lock_reason) { struct mail_transaction_log_file *file; time_t lock_wait_started, lock_secs = 0; const char *reason; int ret = 0; /* we want to get the head file locked. this is a bit racy, since by the time we have it locked a new log file may have been created. creating new log file requires locking the head file, so if we can lock it and don't see another file, we can be sure no-one is creating a new log at the moment */ lock_wait_started = time(NULL); for (;;) { file = log->head; if (mail_transaction_log_file_lock(file) < 0) return -1; file->refcount++; ret = mail_transaction_log_refresh(log, TRUE, &reason); if (--file->refcount == 0) { mail_transaction_log_file_unlock(file, t_strdup_printf( "trying to lock head for %s", lock_reason)); mail_transaction_logs_clean(log); file = NULL; } if (ret == 0 && log->head == file) { /* success */ i_assert(file != NULL); lock_secs = file->lock_created - lock_wait_started; break; } if (file != NULL) { mail_transaction_log_file_unlock(file, t_strdup_printf( "trying to lock head for %s", lock_reason)); } if (ret < 0) break; /* try again */ } if (lock_secs > MAIL_TRANSACTION_LOG_LOCK_WARN_SECS) { i_warning("Locking transaction log file %s took %ld seconds (%s)", log->head->filepath, (long)lock_secs, lock_reason); } i_assert(ret < 0 || log->head != NULL); return ret; } int mail_transaction_log_sync_lock(struct mail_transaction_log *log, const char *lock_reason, uint32_t *file_seq_r, uoff_t *file_offset_r) { const char *reason; i_assert(!log->index->log_sync_locked); if (!log->log_2_unlink_checked) { /* we need to check once in a while if .log.2 should be deleted to avoid wasting space on such old files. but we also don't want to waste time on checking it when the same mailbox gets opened over and over again rapidly (e.g. pop3). so do this only when there have actually been some changes to mailbox (i.e. when it's being locked here) */ log->log_2_unlink_checked = TRUE; mail_transaction_log_2_unlink_old(log); } if (mail_transaction_log_lock_head(log, lock_reason) < 0) return -1; /* update sync_offset */ if (mail_transaction_log_file_map(log->head, log->head->sync_offset, (uoff_t)-1, &reason) <= 0) { mail_index_set_error(log->index, "Failed to map transaction log %s at " "sync_offset=%"PRIuUOFF_T" after locking: %s", log->head->filepath, log->head->sync_offset, reason); mail_transaction_log_file_unlock(log->head, t_strdup_printf( "%s - map failed", lock_reason)); return -1; } log->index->log_sync_locked = TRUE; *file_seq_r = log->head->hdr.file_seq; *file_offset_r = log->head->sync_offset; return 0; } void mail_transaction_log_sync_unlock(struct mail_transaction_log *log, const char *lock_reason) { i_assert(log->index->log_sync_locked); log->index->log_sync_locked = FALSE; mail_transaction_log_file_unlock(log->head, lock_reason); } void mail_transaction_log_get_head(struct mail_transaction_log *log, uint32_t *file_seq_r, uoff_t *file_offset_r) { *file_seq_r = log->head->hdr.file_seq; *file_offset_r = log->head->sync_offset; } void mail_transaction_log_get_tail(struct mail_transaction_log *log, uint32_t *file_seq_r) { struct mail_transaction_log_file *tail, *file = log->files; for (tail = file; file->next != NULL; file = file->next) { if (file->hdr.file_seq + 1 != file->next->hdr.file_seq) tail = file->next; } *file_seq_r = tail->hdr.file_seq; } bool mail_transaction_log_is_head_prev(struct mail_transaction_log *log, uint32_t file_seq, uoff_t file_offset) { return log->head->hdr.prev_file_seq == file_seq && log->head->hdr.prev_file_offset == file_offset; } int mail_transaction_log_unlink(struct mail_transaction_log *log) { if (unlink(log->filepath) < 0 && errno != ENOENT && errno != ESTALE) { mail_index_file_set_syscall_error(log->index, log->filepath, "unlink()"); return -1; } return 0; } void mail_transaction_log_get_dotlock_set(struct mail_transaction_log *log, struct dotlock_settings *set_r) { struct mail_index *index = log->index; i_zero(set_r); set_r->timeout = I_MIN(MAIL_TRANSACTION_LOG_LOCK_TIMEOUT, index->max_lock_timeout_secs); set_r->stale_timeout = MAIL_TRANSACTION_LOG_LOCK_CHANGE_TIMEOUT; set_r->nfs_flush = (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0; set_r->use_excl_lock = (index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0; } dovecot-2.2.33.2/src/lib-index/mail-index.c0000644000175000017500000006307513165463624015207 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "eacces-error.h" #include "hash.h" #include "str-sanitize.h" #include "mmap-util.h" #include "nfs-workarounds.h" #include "read-full.h" #include "write-full.h" #include "mail-index-alloc-cache.h" #include "mail-index-private.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-index-modseq.h" #include "mail-transaction-log-private.h" #include "mail-transaction-log-view-private.h" #include "mail-cache.h" #include #include #include #include struct mail_index_module_register mail_index_module_register = { 0 }; static void mail_index_close_nonopened(struct mail_index *index); struct mail_index *mail_index_alloc(const char *dir, const char *prefix) { struct mail_index *index; index = i_new(struct mail_index, 1); index->dir = i_strdup(dir); index->prefix = i_strdup(prefix); index->fd = -1; index->extension_pool = pool_alloconly_create(MEMPOOL_GROWING"index extension", 1024); p_array_init(&index->extensions, index->extension_pool, 5); i_array_init(&index->sync_lost_handlers, 4); i_array_init(&index->module_contexts, I_MIN(5, mail_index_module_register.id)); index->mode = 0600; index->gid = (gid_t)-1; index->lock_method = FILE_LOCK_METHOD_FCNTL; index->max_lock_timeout_secs = UINT_MAX; index->log_rotate_min_size = MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_MIN_SIZE; index->log_rotate_max_size = MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_MAX_SIZE; index->log_rotate_min_created_ago_secs = MAIL_TRANSACTION_LOG_ROTATE_DEFAULT_TIME; index->log_rotate_log2_stale_secs = MAIL_TRANSACTION_LOG2_DEFAULT_STALE_SECS; index->keywords_ext_id = mail_index_ext_register(index, MAIL_INDEX_EXT_KEYWORDS, 128, 2, 1); index->keywords_pool = pool_alloconly_create("keywords", 512); i_array_init(&index->keywords, 16); hash_table_create(&index->keywords_hash, index->keywords_pool, 0, strcase_hash, strcasecmp); index->log = mail_transaction_log_alloc(index); mail_index_modseq_init(index); return index; } void mail_index_free(struct mail_index **_index) { struct mail_index *index = *_index; *_index = NULL; i_assert(index->open_count == 0); mail_transaction_log_free(&index->log); hash_table_destroy(&index->keywords_hash); pool_unref(&index->extension_pool); pool_unref(&index->keywords_pool); array_free(&index->sync_lost_handlers); array_free(&index->keywords); array_free(&index->module_contexts); i_free(index->ext_hdr_init_data); i_free(index->gid_origin); i_free(index->error); i_free(index->dir); i_free(index->prefix); i_free(index); } void mail_index_set_fsync_mode(struct mail_index *index, enum fsync_mode mode, enum mail_index_fsync_mask mask) { index->fsync_mode = mode; index->fsync_mask = mask; } bool mail_index_use_existing_permissions(struct mail_index *index) { struct stat st; if (MAIL_INDEX_IS_IN_MEMORY(index)) return FALSE; if (stat(index->dir, &st) < 0) { if (errno != ENOENT) i_error("stat(%s) failed: %m", index->dir); return FALSE; } index->mode = st.st_mode & 0666; if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { /* directory's GID is used automatically for new files */ index->gid = (gid_t)-1; } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { /* group has same permissions as world, so don't bother changing it */ index->gid = (gid_t)-1; } else if (getegid() == st.st_gid) { /* using our own gid, no need to change it */ index->gid = (gid_t)-1; } else { index->gid = st.st_gid; } i_free(index->gid_origin); if (index->gid != (gid_t)-1) index->gid_origin = i_strdup("preserved existing GID"); return TRUE; } void mail_index_set_permissions(struct mail_index *index, mode_t mode, gid_t gid, const char *gid_origin) { index->mode = mode & 0666; index->gid = gid; i_free(index->gid_origin); index->gid_origin = i_strdup(gid_origin); } void mail_index_set_lock_method(struct mail_index *index, enum file_lock_method lock_method, unsigned int max_timeout_secs) { index->lock_method = lock_method; index->max_lock_timeout_secs = max_timeout_secs; } void mail_index_set_log_rotation(struct mail_index *index, uoff_t min_size, uoff_t max_size, unsigned int min_created_ago_secs, unsigned int log2_stale_secs) { index->log_rotate_min_size = min_size; index->log_rotate_max_size = max_size; index->log_rotate_min_created_ago_secs = min_created_ago_secs; index->log_rotate_log2_stale_secs = log2_stale_secs; } void mail_index_set_ext_init_data(struct mail_index *index, uint32_t ext_id, const void *data, size_t size) { const struct mail_index_registered_ext *rext; i_assert(index->ext_hdr_init_data == NULL || index->ext_hdr_init_id == ext_id); rext = array_idx(&index->extensions, ext_id); i_assert(rext->hdr_size == size); index->ext_hdr_init_id = ext_id; i_free(index->ext_hdr_init_data); index->ext_hdr_init_data = i_malloc(size); memcpy(index->ext_hdr_init_data, data, size); } uint32_t mail_index_ext_register(struct mail_index *index, const char *name, uint32_t default_hdr_size, uint16_t default_record_size, uint16_t default_record_align) { struct mail_index_registered_ext rext; uint32_t ext_id; if (*name == '\0' || strcmp(name, str_sanitize(name, -1)) != 0) i_panic("mail_index_ext_register(%s): Invalid name", name); if (default_record_size != 0 && default_record_align == 0) { i_panic("mail_index_ext_register(%s): " "Invalid record alignment", name); } if (mail_index_ext_lookup(index, name, &ext_id)) return ext_id; i_zero(&rext); rext.name = p_strdup(index->extension_pool, name); rext.index_idx = array_count(&index->extensions); rext.hdr_size = default_hdr_size; rext.record_size = default_record_size; rext.record_align = default_record_align; array_append(&index->extensions, &rext, 1); return rext.index_idx; } void mail_index_ext_register_resize_defaults(struct mail_index *index, uint32_t ext_id, uint32_t default_hdr_size, uint16_t default_record_size, uint16_t default_record_align) { struct mail_index_registered_ext *rext; rext = array_idx_modifiable(&index->extensions, ext_id); rext->hdr_size = default_hdr_size; rext->record_size = default_record_size; rext->record_align = default_record_align; } bool mail_index_ext_lookup(struct mail_index *index, const char *name, uint32_t *ext_id_r) { const struct mail_index_registered_ext *extensions; unsigned int i, count; extensions = array_get(&index->extensions, &count); for (i = 0; i < count; i++) { if (strcmp(extensions[i].name, name) == 0) { *ext_id_r = i; return TRUE; } } *ext_id_r = (uint32_t)-1; return FALSE; } void mail_index_register_expunge_handler(struct mail_index *index, uint32_t ext_id, bool call_always, mail_index_expunge_handler_t *cb, void *context) { struct mail_index_registered_ext *rext; rext = array_idx_modifiable(&index->extensions, ext_id); i_assert(rext->expunge_handler == NULL || rext->expunge_handler == cb); rext->expunge_handler = cb; rext->expunge_context = context; rext->expunge_handler_call_always = call_always; } void mail_index_unregister_expunge_handler(struct mail_index *index, uint32_t ext_id) { struct mail_index_registered_ext *rext; rext = array_idx_modifiable(&index->extensions, ext_id); i_assert(rext->expunge_handler != NULL); rext->expunge_handler = NULL; } void mail_index_register_sync_handler(struct mail_index *index, uint32_t ext_id, mail_index_sync_handler_t *cb, enum mail_index_sync_handler_type type) { struct mail_index_registered_ext *rext; rext = array_idx_modifiable(&index->extensions, ext_id); i_assert(rext->sync_handler.callback == NULL); rext->sync_handler.callback = cb; rext->sync_handler.type = type; } void mail_index_unregister_sync_handler(struct mail_index *index, uint32_t ext_id) { struct mail_index_registered_ext *rext; rext = array_idx_modifiable(&index->extensions, ext_id); i_assert(rext->sync_handler.callback != NULL); rext->sync_handler.callback = NULL; rext->sync_handler.type = 0; } void mail_index_register_sync_lost_handler(struct mail_index *index, mail_index_sync_lost_handler_t *cb) { array_append(&index->sync_lost_handlers, &cb, 1); } void mail_index_unregister_sync_lost_handler(struct mail_index *index, mail_index_sync_lost_handler_t *cb) { mail_index_sync_lost_handler_t *const *handlers; unsigned int i, count; handlers = array_get(&index->sync_lost_handlers, &count); for (i = 0; i < count; i++) { if (handlers[i] == cb) { array_delete(&index->sync_lost_handlers, i, 1); break; } } } bool mail_index_keyword_lookup(struct mail_index *index, const char *keyword, unsigned int *idx_r) { char *key; void *value; /* keywords_hash keeps a name => index mapping of keywords. Keywords are never removed from it, so the index values are valid for the lifetime of the mail_index. */ if (hash_table_lookup_full(index->keywords_hash, keyword, &key, &value)) { *idx_r = POINTER_CAST_TO(value, unsigned int); return TRUE; } *idx_r = UINT_MAX; return FALSE; } void mail_index_keyword_lookup_or_create(struct mail_index *index, const char *keyword, unsigned int *idx_r) { char *keyword_dup; i_assert(*keyword != '\0'); if (mail_index_keyword_lookup(index, keyword, idx_r)) return; keyword = keyword_dup = p_strdup(index->keywords_pool, keyword); *idx_r = array_count(&index->keywords); hash_table_insert(index->keywords_hash, keyword_dup, POINTER_CAST(*idx_r)); array_append(&index->keywords, &keyword, 1); /* keep the array NULL-terminated, but the NULL itself invisible */ array_append_zero(&index->keywords); array_delete(&index->keywords, array_count(&index->keywords)-1, 1); } const ARRAY_TYPE(keywords) *mail_index_get_keywords(struct mail_index *index) { return &index->keywords; } struct mail_keywords * mail_index_keywords_create(struct mail_index *index, const char *const keywords[]) { struct mail_keywords *k; unsigned int src, dest, i, count; count = str_array_length(keywords); if (count == 0) { k = i_new(struct mail_keywords, 1); k->index = index; k->refcount = 1; return k; } /* @UNSAFE */ k = i_malloc(MALLOC_ADD(sizeof(struct mail_keywords), MALLOC_MULTIPLY(sizeof(k->idx), (count-1)))); k->index = index; k->refcount = 1; /* look up the keywords from index. they're never removed from there so we can permanently store indexes to them. */ for (src = dest = 0; src < count; src++) { mail_index_keyword_lookup_or_create(index, keywords[src], &k->idx[dest]); /* ignore if this is a duplicate */ for (i = 0; i < src; i++) { if (k->idx[i] == k->idx[dest]) break; } if (i == src) dest++; } k->count = dest; return k; } struct mail_keywords * mail_index_keywords_create_from_indexes(struct mail_index *index, const ARRAY_TYPE(keyword_indexes) *keyword_indexes) { struct mail_keywords *k; const unsigned int *indexes; unsigned int src, dest, i, count; indexes = array_get(keyword_indexes, &count); if (count == 0) { k = i_new(struct mail_keywords, 1); k->index = index; k->refcount = 1; return k; } /* @UNSAFE */ k = i_malloc(MALLOC_ADD(sizeof(struct mail_keywords), MALLOC_MULTIPLY(sizeof(k->idx), (count-1)))); k->index = index; k->refcount = 1; /* copy but skip duplicates */ for (src = dest = 0; src < count; src++) { for (i = 0; i < src; i++) { if (k->idx[i] == indexes[src]) break; } if (i == src) k->idx[dest++] = indexes[src]; } k->count = dest; return k; } void mail_index_keywords_ref(struct mail_keywords *keywords) { keywords->refcount++; } void mail_index_keywords_unref(struct mail_keywords **_keywords) { struct mail_keywords *keywords = *_keywords; i_assert(keywords->refcount > 0); *_keywords = NULL; if (--keywords->refcount == 0) i_free(keywords); } int mail_index_try_open_only(struct mail_index *index) { i_assert(index->fd == -1); i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); /* Note that our caller must close index->fd by itself. */ if (index->readonly) errno = EACCES; else { index->fd = nfs_safe_open(index->filepath, O_RDWR); index->readonly = FALSE; } if (index->fd == -1 && errno == EACCES) { index->fd = open(index->filepath, O_RDONLY); index->readonly = TRUE; } if (index->fd == -1) { if (errno != ENOENT) { mail_index_set_syscall_error(index, "open()"); return -1; } /* have to create it */ return 0; } return 1; } static int mail_index_try_open(struct mail_index *index) { int ret; i_assert(index->fd == -1); if (MAIL_INDEX_IS_IN_MEMORY(index)) return 0; ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD); if (ret == 0) { /* it's corrupted - recreate it */ if (index->fd != -1) { if (close(index->fd) < 0) mail_index_set_syscall_error(index, "close()"); index->fd = -1; } } return ret; } int mail_index_create_tmp_file(struct mail_index *index, const char *path_prefix, const char **path_r) { mode_t old_mask; const char *path; int fd; i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); path = *path_r = t_strconcat(path_prefix, ".tmp", NULL); old_mask = umask(0); fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->mode); umask(old_mask); if (fd == -1 && errno == EEXIST) { /* stale temp file. unlink and recreate rather than overwriting, just to make sure locking problems won't cause corruption */ if (i_unlink(path) < 0) return -1; old_mask = umask(0); fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->mode); umask(old_mask); } if (fd == -1) { mail_index_file_set_syscall_error(index, path, "creat()"); return -1; } mail_index_fchown(index, fd, path); return fd; } static int mail_index_open_files(struct mail_index *index, enum mail_index_open_flags flags) { int ret; ret = mail_transaction_log_open(index->log); if (ret == 0) { if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) return 0; /* if dovecot.index exists, read it first so that we can get the correct indexid and log sequence */ (void)mail_index_try_open(index); if (index->indexid == 0) { /* Create a new indexid for us. If we're opening index into memory, index->map doesn't exist yet. */ index->indexid = ioloop_time; index->initial_create = TRUE; if (index->map != NULL) index->map->hdr.indexid = index->indexid; } ret = mail_transaction_log_create(index->log, FALSE); if (index->map != NULL) { /* log creation could have changed it if someone else just created it. */ index->map->hdr.indexid = index->indexid; } index->initial_create = FALSE; } if (ret >= 0) { ret = index->map != NULL ? 1 : mail_index_try_open(index); if (ret == 0) { /* corrupted */ mail_transaction_log_close(index->log); ret = mail_transaction_log_create(index->log, TRUE); if (ret == 0) { if (index->map != NULL) mail_index_unmap(&index->map); index->map = mail_index_map_alloc(index); } } } if (ret < 0) { /* open/create failed, fallback to in-memory indexes */ if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) return -1; if (mail_index_move_to_memory(index) < 0) return -1; } if (index->cache == NULL) index->cache = mail_cache_open_or_create(index); return 1; } static int mail_index_open_opened(struct mail_index *index, enum mail_index_open_flags flags) { int ret; i_assert(index->map != NULL); if ((index->map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) { /* index was marked corrupted. we'll probably need to recreate the files. */ mail_index_unmap(&index->map); mail_index_close_file(index); mail_transaction_log_close(index->log); if ((ret = mail_index_open_files(index, flags)) <= 0) return ret; } index->open_count++; return 1; } int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags) { int ret; if (index->open_count > 0) { if ((ret = mail_index_open_opened(index, flags)) <= 0) { /* doesn't exist and create flag not used */ } return ret; } index->filepath = MAIL_INDEX_IS_IN_MEMORY(index) ? i_strdup("(in-memory index)") : i_strconcat(index->dir, "/", index->prefix, NULL); index->readonly = FALSE; index->nodiskspace = FALSE; index->index_lock_timeout = FALSE; index->log_sync_locked = FALSE; index->flags = flags; index->readonly = (flags & MAIL_INDEX_OPEN_FLAG_READONLY) != 0; if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 && index->fsync_mode != FSYNC_MODE_ALWAYS) i_fatal("nfs flush requires mail_fsync=always"); if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 && (flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0) i_fatal("nfs flush requires mmap_disable=yes"); /* NOTE: increase open_count only after mail_index_open_files(). it's used elsewhere to check if we're doing an initial opening of the index files */ if ((ret = mail_index_open_files(index, flags)) <= 0) { /* doesn't exist and create flag not used */ mail_index_close_nonopened(index); return ret; } index->open_count++; if (index->log->head == NULL) { mail_index_close(index); mail_index_set_error(index, "Index is corrupted " "(log->view->head == NULL)"); return -1; } i_assert(index->map != NULL); mail_index_alloc_cache_index_opened(index); return 1; } int mail_index_open_or_create(struct mail_index *index, enum mail_index_open_flags flags) { int ret; flags |= MAIL_INDEX_OPEN_FLAG_CREATE; ret = mail_index_open(index, flags); i_assert(ret != 0); return ret < 0 ? -1 : 0; } void mail_index_close_file(struct mail_index *index) { if (index->fd != -1) { if (close(index->fd) < 0) mail_index_set_syscall_error(index, "close()"); index->fd = -1; } } static void mail_index_close_nonopened(struct mail_index *index) { i_assert(!index->syncing); i_assert(index->views == NULL); if (index->map != NULL) mail_index_unmap(&index->map); mail_index_close_file(index); mail_transaction_log_close(index->log); if (index->cache != NULL) mail_cache_free(&index->cache); i_free_and_null(index->filepath); index->indexid = 0; } void mail_index_close(struct mail_index *index) { i_assert(index->open_count > 0); mail_index_alloc_cache_index_closing(index); if (--index->open_count == 0) mail_index_close_nonopened(index); } int mail_index_unlink(struct mail_index *index) { const char *path; int last_errno = 0; if (MAIL_INDEX_IS_IN_MEMORY(index) || index->readonly) return 0; /* main index */ if (unlink(index->filepath) < 0 && errno != ENOENT) last_errno = errno; /* logs */ path = t_strconcat(index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL); if (unlink(path) < 0 && errno != ENOENT) last_errno = errno; path = t_strconcat(index->filepath, MAIL_TRANSACTION_LOG_SUFFIX".2", NULL); if (unlink(path) < 0 && errno != ENOENT) last_errno = errno; /* cache */ path = t_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL); if (unlink(path) < 0 && errno != ENOENT) last_errno = errno; if (last_errno == 0) return 0; else { errno = last_errno; return -1; } } int mail_index_reopen_if_changed(struct mail_index *index, const char **reason_r) { struct stat st1, st2; int ret; if (MAIL_INDEX_IS_IN_MEMORY(index)) { *reason_r = "in-memory index"; return 0; } if (index->fd == -1) goto final; if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) nfs_flush_file_handle_cache(index->filepath); if (nfs_safe_stat(index->filepath, &st2) < 0) { if (errno == ENOENT) { *reason_r = "index not found via stat()"; return 0; } mail_index_set_syscall_error(index, "stat()"); return -1; } if (fstat(index->fd, &st1) < 0) { if (!ESTALE_FSTAT(errno)) { mail_index_set_syscall_error(index, "fstat()"); return -1; } /* deleted/recreated, reopen */ *reason_r = "index is stale"; } else if (st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev)) { /* the same file */ *reason_r = "index unchanged"; return 1; } else { *reason_r = "index inode changed"; } /* new file, new locks. the old fd can keep its locks, they don't matter anymore as no-one's going to modify the file. */ mail_index_close_file(index); final: if ((ret = mail_index_try_open_only(index)) == 0) *reason_r = "index not found via open()"; else if (ret > 0) *reason_r = "index opened"; return ret; } int mail_index_refresh(struct mail_index *index) { int ret; ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD); return ret <= 0 ? -1 : 0; } struct mail_cache *mail_index_get_cache(struct mail_index *index) { return index->cache; } void mail_index_set_error(struct mail_index *index, const char *fmt, ...) { va_list va; i_free(index->error); if (fmt == NULL) index->error = NULL; else { va_start(va, fmt); index->error = i_strdup_vprintf(fmt, va); va_end(va); i_error("%s", index->error); } } bool mail_index_is_in_memory(struct mail_index *index) { return MAIL_INDEX_IS_IN_MEMORY(index); } int mail_index_move_to_memory(struct mail_index *index) { struct mail_index_map *map; if (MAIL_INDEX_IS_IN_MEMORY(index)) return index->map == NULL ? -1 : 0; if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) return -1; /* set the index as being into memory */ i_free_and_null(index->dir); i_free(index->filepath); index->filepath = i_strdup("(in-memory index)"); if (index->map == NULL) { /* index was never even opened. just mark it as being in memory and let the caller re-open the index. */ i_assert(index->fd == -1); return -1; } /* move index map to memory */ if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) { map = mail_index_map_clone(index->map); mail_index_unmap(&index->map); index->map = map; } if (index->log != NULL) { /* move transaction log to memory */ mail_transaction_log_move_to_memory(index->log); } if (index->fd != -1) { if (close(index->fd) < 0) mail_index_set_syscall_error(index, "close()"); index->fd = -1; } return 0; } void mail_index_mark_corrupted(struct mail_index *index) { index->indexid = 0; index->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED; if (!index->readonly) { if (unlink(index->filepath) < 0 && errno != ENOENT && errno != ESTALE) mail_index_set_syscall_error(index, "unlink()"); (void)mail_transaction_log_unlink(index->log); } } bool mail_index_is_deleted(struct mail_index *index) { return index->index_delete_requested || index->index_deleted; } int mail_index_get_modification_time(struct mail_index *index, time_t *mtime_r) { struct stat st; const char *path; *mtime_r = 0; if (MAIL_INDEX_IS_IN_MEMORY(index)) { /* this function doesn't make sense for in-memory indexes */ return 0; } /* index may not be open, so index->filepath may be NULL */ path = t_strconcat(index->dir, "/", index->prefix, MAIL_TRANSACTION_LOG_SUFFIX, NULL); if (stat(path, &st) < 0) { if (errno == ENOENT) { /* .log is always supposed to exist - don't bother trying to stat(dovecot.index) */ return 0; } mail_index_file_set_syscall_error(index, path, "stat()"); return -1; } *mtime_r = st.st_mtime; return 0; } void mail_index_fchown(struct mail_index *index, int fd, const char *path) { mode_t mode; if (index->gid == (gid_t)-1) { /* no gid changing */ return; } else if (fchown(fd, (uid_t)-1, index->gid) == 0) { /* success */ return; } if ((index->mode & 0060) >> 3 == (index->mode & 0006)) { /* group and world permissions are the same, so group doesn't really matter. ignore silently. */ return; } if (errno != EPERM) mail_index_file_set_syscall_error(index, path, "fchown()"); else { mail_index_set_error(index, "%s", eperm_error_get_chgrp("fchown", path, index->gid, index->gid_origin)); } /* continue, but change permissions so that only the common subset of group and world is used. this makes sure no one gets any extra permissions. */ mode = ((index->mode & 0060) >> 3) & (index->mode & 0006); mode |= (mode << 3) | (index->mode & 0600); if (fchmod(fd, mode) < 0) mail_index_file_set_syscall_error(index, path, "fchmod()"); } void mail_index_set_syscall_error(struct mail_index *index, const char *function) { mail_index_file_set_syscall_error(index, index->filepath, function); } void mail_index_file_set_syscall_error(struct mail_index *index, const char *filepath, const char *function) { const char *errstr; i_assert(filepath != NULL); i_assert(function != NULL); if (errno == ENOENT) { struct stat st; int old_errno = errno; i_assert(index->log->filepath != NULL); if (nfs_safe_stat(index->log->filepath, &st) < 0 && errno == ENOENT) { /* the index log has gone away */ index->index_deleted = TRUE; errno = old_errno; return; } errno = old_errno; } if (ENOSPACE(errno)) { index->nodiskspace = TRUE; if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) == 0) return; } if (errno == EACCES) { function = t_strcut(function, '('); if (strcmp(function, "creat") == 0 || strncmp(function, "file_dotlock_", 13) == 0) errstr = eacces_error_get_creating(function, filepath); else errstr = eacces_error_get(function, filepath); mail_index_set_error(index, "%s", errstr); } else { const char *suffix = errno != EFBIG ? "" : " (process was started with ulimit -f limit)"; mail_index_set_error(index, "%s failed with file %s: " "%m%s", function, filepath, suffix); } } const char *mail_index_get_error_message(struct mail_index *index) { return index->error; } void mail_index_reset_error(struct mail_index *index) { if (index->error != NULL) { i_free(index->error); index->error = NULL; } index->nodiskspace = FALSE; index->index_lock_timeout = FALSE; } dovecot-2.2.33.2/src/lib-index/mail-cache.h0000644000175000017500000001464113147010713015126 00000000000000#ifndef MAIL_CACHE_H #define MAIL_CACHE_H #include "mail-index.h" #define MAIL_CACHE_FILE_SUFFIX ".cache" struct mail_cache; struct mail_cache_view; struct mail_cache_transaction_ctx; struct mail_cache_compress_lock; enum mail_cache_decision_type { /* Not needed currently */ MAIL_CACHE_DECISION_NO = 0x00, /* Needed only for new mails. Drop when compressing. */ MAIL_CACHE_DECISION_TEMP = 0x01, /* Needed. */ MAIL_CACHE_DECISION_YES = 0x02, /* This decision has been forced manually, don't change it. */ MAIL_CACHE_DECISION_FORCED = 0x80 }; enum mail_cache_field_type { MAIL_CACHE_FIELD_FIXED_SIZE, MAIL_CACHE_FIELD_VARIABLE_SIZE, MAIL_CACHE_FIELD_STRING, MAIL_CACHE_FIELD_BITMASK, MAIL_CACHE_FIELD_HEADER, MAIL_CACHE_FIELD_COUNT }; struct mail_cache_field { const char *name; unsigned int idx; enum mail_cache_field_type type; unsigned int field_size; enum mail_cache_decision_type decision; /* If higher than the current last_used field, update it */ time_t last_used; }; struct mail_cache *mail_cache_open_or_create(struct mail_index *index); void mail_cache_free(struct mail_cache **cache); /* Register fields. fields[].idx is updated to contain field index. If field already exists and its caching decision is NO, the decision is updated to the input field's decision. */ void mail_cache_register_fields(struct mail_cache *cache, struct mail_cache_field *fields, unsigned int fields_count); /* Returns registered field index, or UINT_MAX if not found. */ unsigned int mail_cache_register_lookup(struct mail_cache *cache, const char *name); /* Returns specified field */ const struct mail_cache_field * mail_cache_register_get_field(struct mail_cache *cache, unsigned int field_idx); /* Returns a list of all registered fields */ struct mail_cache_field * mail_cache_register_get_list(struct mail_cache *cache, pool_t pool, unsigned int *count_r); /* Returns TRUE if cache should be compressed. */ bool mail_cache_need_compress(struct mail_cache *cache); /* Compress cache file. Offsets are updated to given transaction. The cache compression lock should be kept until the transaction is committed. mail_cache_compress_unlock() needs to be called afterwards. The lock doesn't prevent updates to the cache while it's held, it only prevents another cache compression. */ int mail_cache_compress(struct mail_cache *cache, struct mail_index_transaction *trans, struct mail_cache_compress_lock **lock_r); void mail_cache_compress_unlock(struct mail_cache_compress_lock **lock); /* Returns TRUE if there is at least something in the cache. */ bool mail_cache_exists(struct mail_cache *cache); /* Open and read cache header. Returns 0 if ok, -1 if error/corrupted. */ int mail_cache_open_and_verify(struct mail_cache *cache); struct mail_cache_view * mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview); void mail_cache_view_close(struct mail_cache_view **view); /* Normally cache decisions are updated on lookup/add. Use this function to enable/disable this (useful for precaching data). */ void mail_cache_view_update_cache_decisions(struct mail_cache_view *view, bool update); /* Copy caching decisions */ void mail_cache_decisions_copy(struct mail_index_transaction *itrans, struct mail_cache *src, struct mail_cache *dst); /* Get index transaction specific cache transaction. */ struct mail_cache_transaction_ctx * mail_cache_get_transaction(struct mail_cache_view *view, struct mail_index_transaction *t); void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx); int mail_cache_transaction_commit(struct mail_cache_transaction_ctx **ctx); void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **ctx); /* Add new field to given record. Updates are not allowed. Fixed size fields must be exactly the expected size. */ void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx, const void *data, size_t data_size); /* Returns TRUE if field is wanted to be added and it doesn't already exist. If current caching decisions say not to cache this field, FALSE is returned. If seq is 0, the existence isn't checked. */ bool mail_cache_field_want_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx); /* Like mail_cache_field_want_add(), but in caching decisions FALSE is returned only if the decision is a forced no. */ bool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq, unsigned int field_idx); /* Notify cache that the mail is now closed. Any records added with mail_cache_add() are unlikely to be required again. This mainly tells INDEX=MEMORY that it can free up the memory used by the mail. */ void mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx, uint32_t seq); /* Returns 1 if field exists, 0 if not, -1 if error. */ int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq, unsigned int field_idx); /* Returns TRUE if something is cached for the message, FALSE if not. */ bool mail_cache_field_exists_any(struct mail_cache_view *view, uint32_t seq); /* Returns current caching decision for given field. */ enum mail_cache_decision_type mail_cache_field_get_decision(struct mail_cache *cache, unsigned int field_idx); /* Set data_r and size_r to point to wanted field in cache file. Returns 1 if field was found, 0 if not, -1 if error. */ int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf, uint32_t seq, unsigned int field_idx); /* Return specified cached headers. Returns 1 if all fields were found, 0 if not, -1 if error. dest is updated only if all fields were found. */ int mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest, uint32_t seq, unsigned int field_idxs[], unsigned int fields_count); /* "Error in index cache file %s: ...". */ void mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...) ATTR_FORMAT(2, 3); void mail_cache_set_seq_corrupted_reason(struct mail_cache_view *cache_view, uint32_t seq, const char *reason); /* Delete the cache file. */ void mail_cache_reset(struct mail_cache *cache); /* Returns human-readable reason for why a cached field is missing for the specified mail. This is mainly for debugging purposes, so the exact field doesn't matter here. */ const char * mail_cache_get_missing_reason(struct mail_cache_view *view, uint32_t seq); #endif dovecot-2.2.33.2/src/lib-index/mail-index-view.c0000644000175000017500000004123013165463624016144 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "llist.h" #include "mail-index-view-private.h" #include "mail-transaction-log.h" struct mail_index_view * mail_index_view_dup_private(const struct mail_index_view *src) { struct mail_index_view *view; struct mail_index_map *map; view = i_new(struct mail_index_view, 1); mail_index_view_clone(view, src); map = mail_index_map_clone(view->map); mail_index_unmap(&view->map); view->map = map; return view; } void mail_index_view_clone(struct mail_index_view *dest, const struct mail_index_view *src) { i_zero(dest); dest->refcount = 1; dest->v = src->v; dest->index = src->index; if (src->log_view != NULL) { dest->log_view = mail_transaction_log_view_open(src->index->log); } dest->indexid = src->indexid; dest->inconsistency_id = src->inconsistency_id; dest->map = src->map; if (dest->map != NULL) dest->map->refcount++; dest->log_file_expunge_seq = src->log_file_expunge_seq; dest->log_file_expunge_offset = src->log_file_expunge_offset; dest->log_file_head_seq = src->log_file_head_seq; dest->log_file_head_offset = src->log_file_head_offset; i_array_init(&dest->module_contexts, I_MIN(5, mail_index_module_register.id)); DLLIST_PREPEND(&dest->index->views, dest); } void mail_index_view_ref(struct mail_index_view *view) { view->refcount++; } static void view_close(struct mail_index_view *view) { i_assert(view->refcount == 0); i_assert(view->index->views != NULL); DLLIST_REMOVE(&view->index->views, view); mail_transaction_log_view_close(&view->log_view); if (array_is_created(&view->syncs_hidden)) array_free(&view->syncs_hidden); mail_index_unmap(&view->map); if (array_is_created(&view->map_refs)) { mail_index_view_unref_maps(view); array_free(&view->map_refs); } array_free(&view->module_contexts); i_free(view); } bool mail_index_view_is_inconsistent(struct mail_index_view *view) { if (view->index->indexid != view->indexid || view->index->inconsistency_id != view->inconsistency_id) view->inconsistent = TRUE; return view->inconsistent; } struct mail_index *mail_index_view_get_index(struct mail_index_view *view) { return view->index; } unsigned int mail_index_view_get_transaction_count(struct mail_index_view *view) { i_assert(view->transactions >= 0); return view->transactions; } void mail_index_view_transaction_ref(struct mail_index_view *view) { view->transactions++; } void mail_index_view_transaction_unref(struct mail_index_view *view) { i_assert(view->transactions > 0); view->transactions--; } static void mail_index_view_ref_map(struct mail_index_view *view, struct mail_index_map *map) { struct mail_index_map *const *maps; unsigned int i, count; if (array_is_created(&view->map_refs)) { maps = array_get(&view->map_refs, &count); /* if map is already referenced, do nothing */ for (i = 0; i < count; i++) { if (maps[i] == map) return; } } else { i_array_init(&view->map_refs, 4); } /* reference the given mapping. the reference is dropped when the view is synchronized or closed. */ map->refcount++; array_append(&view->map_refs, &map, 1); } void mail_index_view_unref_maps(struct mail_index_view *view) { struct mail_index_map **maps; unsigned int i, count; if (!array_is_created(&view->map_refs)) return; maps = array_get_modifiable(&view->map_refs, &count); for (i = 0; i < count; i++) mail_index_unmap(&maps[i]); array_clear(&view->map_refs); } static uint32_t view_get_messages_count(struct mail_index_view *view) { return view->map->hdr.messages_count; } static const struct mail_index_header * view_get_header(struct mail_index_view *view) { return &view->map->hdr; } static const struct mail_index_record * view_lookup_full(struct mail_index_view *view, uint32_t seq, struct mail_index_map **map_r, bool *expunged_r) { static struct mail_index_record broken_rec; struct mail_index_map *map; const struct mail_index_record *rec, *head_rec; i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view)); /* look up the record */ rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); if (unlikely(rec->uid == 0)) { if (!view->inconsistent) { mail_index_set_error(view->index, "Corrupted Index file %s: Record [%u].uid=0", view->index->filepath, seq); (void)mail_index_fsck(view->index); view->inconsistent = TRUE; } /* we'll need to return something so the caller doesn't crash */ *map_r = view->map; if (expunged_r != NULL) *expunged_r = TRUE; return &broken_rec; } if (view->map == view->index->map) { /* view's mapping is latest. we can use it directly. */ *map_r = view->map; if (expunged_r != NULL) *expunged_r = FALSE; return rec; } /* look up the record from head mapping. it may contain some changes. start looking up from the same sequence as in the old view. if there are no expunges, it's there. otherwise it's somewhere before (since records can't be inserted). usually there are only a few expunges, so just going downwards from our initial sequence position is probably faster than binary search. */ if (seq > view->index->map->hdr.messages_count) seq = view->index->map->hdr.messages_count; if (seq == 0) { /* everything is expunged from head. use the old record. */ *map_r = view->map; if (expunged_r != NULL) *expunged_r = TRUE; return rec; } map = view->index->map; do { head_rec = MAIL_INDEX_REC_AT_SEQ(map, seq); if (head_rec->uid <= rec->uid) break; } while (--seq > 0); if (head_rec->uid == rec->uid) { /* found it. use it. reference the index mapping so that the returned record doesn't get invalidated after next sync. */ mail_index_view_ref_map(view, view->index->map); *map_r = view->index->map; if (expunged_r != NULL) *expunged_r = FALSE; return head_rec; } else { /* expuned from head. use the old record. */ *map_r = view->map; if (expunged_r != NULL) *expunged_r = TRUE; return rec; } } static void view_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r) { i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view)); *uid_r = MAIL_INDEX_REC_AT_SEQ(view->map, seq)->uid; } static void view_lookup_seq_range(struct mail_index_view *view, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r) { mail_index_map_lookup_seq_range(view->map, first_uid, last_uid, first_seq_r, last_seq_r); } static void view_lookup_first(struct mail_index_view *view, enum mail_flags flags, uint8_t flags_mask, uint32_t *seq_r) { #define LOW_UPDATE(x) \ STMT_START { if ((x) > low_uid) low_uid = x; } STMT_END const struct mail_index_header *hdr = &view->map->hdr; const struct mail_index_record *rec; uint32_t seq, seq2, low_uid = 1; *seq_r = 0; if ((flags_mask & MAIL_SEEN) != 0 && (flags & MAIL_SEEN) == 0) LOW_UPDATE(hdr->first_unseen_uid_lowwater); if ((flags_mask & MAIL_DELETED) != 0 && (flags & MAIL_DELETED) != 0) LOW_UPDATE(hdr->first_deleted_uid_lowwater); if (low_uid == 1) seq = 1; else { if (!mail_index_lookup_seq_range(view, low_uid, hdr->next_uid, &seq, &seq2)) return; } i_assert(hdr->messages_count <= view->map->rec_map->records_count); for (; seq <= hdr->messages_count; seq++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); if ((rec->flags & flags_mask) == (uint8_t)flags) { *seq_r = seq; break; } } } static void mail_index_data_lookup_keywords(struct mail_index_map *map, const unsigned char *data, ARRAY_TYPE(keyword_indexes) *keyword_idx) { const unsigned int *keyword_idx_map; unsigned int i, j, keyword_count, index_idx; uint32_t idx, hdr_size; uint16_t record_size, record_align; array_clear(keyword_idx); if (data == NULL) { /* no keywords at all in index */ return; } (void)mail_index_ext_get_size(map, map->index->keywords_ext_id, &hdr_size, &record_size, &record_align); /* keyword_idx_map[] contains file => index keyword mapping */ if (!array_is_created(&map->keyword_idx_map)) return; keyword_idx_map = array_get(&map->keyword_idx_map, &keyword_count); for (i = 0; i < record_size; i++) { /* first do the quick check to see if there's keywords at all */ if (data[i] == 0) continue; idx = i * CHAR_BIT; for (j = 0; j < CHAR_BIT; j++, idx++) { if ((data[i] & (1 << j)) == 0) continue; if (idx >= keyword_count) { /* extra bits set in keyword bytes. shouldn't happen, but just ignore. */ break; } index_idx = keyword_idx_map[idx]; array_append(keyword_idx, &index_idx, 1); } } } static void view_lookup_keywords(struct mail_index_view *view, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx) { struct mail_index_map *map; const void *data; mail_index_lookup_ext_full(view, seq, view->index->keywords_ext_id, &map, &data, NULL); mail_index_data_lookup_keywords(map, data, keyword_idx); } static const void * view_map_lookup_ext_full(struct mail_index_map *map, const struct mail_index_record *rec, uint32_t ext_id) { const struct mail_index_ext *ext; uint32_t idx; if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) return NULL; ext = array_idx(&map->extensions, idx); return ext->record_offset == 0 ? NULL : CONST_PTR_OFFSET(rec, ext->record_offset); } static void view_lookup_ext_full(struct mail_index_view *view, uint32_t seq, uint32_t ext_id, struct mail_index_map **map_r, const void **data_r, bool *expunged_r) { const struct mail_index_record *rec; rec = view->v.lookup_full(view, seq, map_r, expunged_r); *data_r = view_map_lookup_ext_full(*map_r, rec, ext_id); } static void view_get_header_ext(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, const void **data_r, size_t *data_size_r) { const struct mail_index_ext *ext; uint32_t idx; if (map == NULL) { /* no mapping given, use head mapping */ map = view->index->map; } if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) { /* extension doesn't exist in this index file */ *data_r = NULL; *data_size_r = 0; return; } ext = array_idx(&map->extensions, idx); *data_r = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); *data_size_r = ext->hdr_size; } static bool view_ext_get_reset_id(struct mail_index_view *view ATTR_UNUSED, struct mail_index_map *map, uint32_t ext_id, uint32_t *reset_id_r) { const struct mail_index_ext *ext; uint32_t idx; if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) return FALSE; ext = array_idx(&map->extensions, idx); *reset_id_r = ext->reset_id; return TRUE; } void mail_index_view_close(struct mail_index_view **_view) { struct mail_index_view *view = *_view; *_view = NULL; if (--view->refcount > 0) return; i_assert(view->transactions == 0); view->v.close(view); } uint32_t mail_index_view_get_messages_count(struct mail_index_view *view) { return view->v.get_messages_count(view); } const struct mail_index_header * mail_index_get_header(struct mail_index_view *view) { return view->v.get_header(view); } const struct mail_index_record * mail_index_lookup(struct mail_index_view *view, uint32_t seq) { struct mail_index_map *map; return mail_index_lookup_full(view, seq, &map); } const struct mail_index_record * mail_index_lookup_full(struct mail_index_view *view, uint32_t seq, struct mail_index_map **map_r) { return view->v.lookup_full(view, seq, map_r, NULL); } bool mail_index_is_expunged(struct mail_index_view *view, uint32_t seq) { struct mail_index_map *map; bool expunged; (void)view->v.lookup_full(view, seq, &map, &expunged); return expunged; } void mail_index_map_lookup_keywords(struct mail_index_map *map, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx) { const struct mail_index_ext *ext; const struct mail_index_record *rec; const void *data; uint32_t idx; if (!mail_index_map_get_ext_idx(map, map->index->keywords_ext_id, &idx)) data = NULL; else { rec = MAIL_INDEX_REC_AT_SEQ(map, seq); ext = array_idx(&map->extensions, idx); data = ext->record_offset == 0 ? NULL : CONST_PTR_OFFSET(rec, ext->record_offset); } mail_index_data_lookup_keywords(map, data, keyword_idx); } void mail_index_lookup_keywords(struct mail_index_view *view, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx) { view->v.lookup_keywords(view, seq, keyword_idx); } void mail_index_lookup_view_flags(struct mail_index_view *view, uint32_t seq, enum mail_flags *flags_r, ARRAY_TYPE(keyword_indexes) *keyword_idx) { const struct mail_index_record *rec; const unsigned char *keyword_data; i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view)); rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); *flags_r = rec->flags; keyword_data = view_map_lookup_ext_full(view->map, rec, view->index->keywords_ext_id); mail_index_data_lookup_keywords(view->map, keyword_data, keyword_idx); } void mail_index_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r) { view->v.lookup_uid(view, seq, uid_r); } bool mail_index_lookup_seq_range(struct mail_index_view *view, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r) { view->v.lookup_seq_range(view, first_uid, last_uid, first_seq_r, last_seq_r); return *first_seq_r != 0; } bool mail_index_lookup_seq(struct mail_index_view *view, uint32_t uid, uint32_t *seq_r) { view->v.lookup_seq_range(view, uid, uid, seq_r, seq_r); return *seq_r != 0; } void mail_index_lookup_first(struct mail_index_view *view, enum mail_flags flags, uint8_t flags_mask, uint32_t *seq_r) { view->v.lookup_first(view, flags, flags_mask, seq_r); } void mail_index_lookup_ext(struct mail_index_view *view, uint32_t seq, uint32_t ext_id, const void **data_r, bool *expunged_r) { struct mail_index_map *map; mail_index_lookup_ext_full(view, seq, ext_id, &map, data_r, expunged_r); } void mail_index_lookup_ext_full(struct mail_index_view *view, uint32_t seq, uint32_t ext_id, struct mail_index_map **map_r, const void **data_r, bool *expunged_r) { view->v.lookup_ext_full(view, seq, ext_id, map_r, data_r, expunged_r); } void mail_index_get_header_ext(struct mail_index_view *view, uint32_t ext_id, const void **data_r, size_t *data_size_r) { view->v.get_header_ext(view, NULL, ext_id, data_r, data_size_r); } void mail_index_map_get_header_ext(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, const void **data_r, size_t *data_size_r) { view->v.get_header_ext(view, map, ext_id, data_r, data_size_r); } bool mail_index_ext_get_reset_id(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, uint32_t *reset_id_r) { return view->v.ext_get_reset_id(view, map, ext_id, reset_id_r); } void mail_index_ext_get_size(struct mail_index_map *map, uint32_t ext_id, uint32_t *hdr_size_r, uint16_t *record_size_r, uint16_t *record_align_r) { const struct mail_index_ext *ext; uint32_t idx; i_assert(map != NULL); if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) { /* extension doesn't exist in this index file */ *hdr_size_r = 0; *record_size_r = 0; *record_align_r = 0; return; } ext = array_idx(&map->extensions, idx); *hdr_size_r = ext->hdr_size; *record_size_r = ext->record_size; *record_align_r = ext->record_align; } static struct mail_index_view_vfuncs view_vfuncs = { view_close, view_get_messages_count, view_get_header, view_lookup_full, view_lookup_uid, view_lookup_seq_range, view_lookup_first, view_lookup_keywords, view_lookup_ext_full, view_get_header_ext, view_ext_get_reset_id }; struct mail_index_view * mail_index_view_open_with_map(struct mail_index *index, struct mail_index_map *map) { struct mail_index_view *view; view = i_new(struct mail_index_view, 1); view->refcount = 1; view->v = view_vfuncs; view->index = index; view->log_view = mail_transaction_log_view_open(index->log); view->indexid = index->indexid; view->inconsistency_id = index->inconsistency_id; view->map = map; view->map->refcount++; view->log_file_expunge_seq = view->log_file_head_seq = view->map->hdr.log_file_seq; view->log_file_expunge_offset = view->log_file_head_offset = view->map->hdr.log_file_head_offset; i_array_init(&view->module_contexts, I_MIN(5, mail_index_module_register.id)); DLLIST_PREPEND(&index->views, view); return view; } struct mail_index_view *mail_index_view_open(struct mail_index *index) { return mail_index_view_open_with_map(index, index->map); } const struct mail_index_ext * mail_index_view_get_ext(struct mail_index_view *view, uint32_t ext_id) { uint32_t idx; if (!mail_index_map_get_ext_idx(view->map, ext_id, &idx)) return NULL; return array_idx(&view->map->extensions, idx); } dovecot-2.2.33.2/src/lib-index/test-mail-index-modseq.c0000644000175000017500000000515213165463624017442 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "unlink-directory.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mail-transaction-log-private.h" #define TESTDIR_NAME ".dovecot.test" static void test_mail_index_modseq_get_next_log_offset(void) { struct { uint32_t log_seq; uoff_t log_offset; } tests[] = { { 0, 0 }, { 2, 40 }, { 2, 148 }, { 2, 164 }, { 3, 40 }, { 3, 56 }, { 3, 72 }, { 3, 88 }, }; struct mail_index *index; struct mail_index_view *view, *view2; struct mail_index_transaction *trans; uint32_t seq, uid; (void)unlink_directory(TESTDIR_NAME, UNLINK_DIRECTORY_FLAG_RMDIR); if (mkdir(TESTDIR_NAME, 0700) < 0) i_error("mkdir(%s) failed: %m", TESTDIR_NAME); ioloop_time = 1; test_begin("mail_transaction_log_file_get_modseq_next_offset()"); index = mail_index_alloc(TESTDIR_NAME, "test.dovecot.index"); test_assert(mail_index_open_or_create(index, MAIL_INDEX_OPEN_FLAG_CREATE) == 0); view = mail_index_view_open(index); mail_index_modseq_enable(index); trans = mail_index_transaction_begin(view, 0); uid = 1234; mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid, sizeof(uid), TRUE); test_assert(mail_index_transaction_commit(&trans) == 0); for (uid = 1; uid <= 3; uid++) { trans = mail_index_transaction_begin(view, 0); mail_index_append(trans, uid, &seq); test_assert(mail_index_transaction_commit(&trans) == 0); } test_assert(mail_transaction_log_file_lock(index->log->head) == 0); test_assert(mail_transaction_log_rotate(index->log, FALSE) == 0); mail_transaction_log_file_unlock(index->log->head, "rotating"); for (uid = 4; uid <= 6; uid++) { trans = mail_index_transaction_begin(view, 0); mail_index_append(trans, uid, &seq); test_assert(mail_index_transaction_commit(&trans) == 0); } view2 = mail_index_view_open(index); for (uint64_t modseq = 1; modseq <= 7; modseq++) { uint32_t log_seq; uoff_t log_offset; test_assert_idx(mail_index_modseq_get_next_log_offset(view2, modseq, &log_seq, &log_offset) == (tests[modseq].log_seq != 0), modseq); test_assert_idx(tests[modseq].log_seq == log_seq && tests[modseq].log_offset == log_offset, modseq); } mail_index_view_close(&view); mail_index_view_close(&view2); mail_index_close(index); mail_index_free(&index); (void)unlink_directory(TESTDIR_NAME, UNLINK_DIRECTORY_FLAG_RMDIR); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_index_modseq_get_next_log_offset, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-finish.c0000644000175000017500000002220313123174404020761 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mail-index-transaction-private.h" int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1, const struct mail_transaction_expunge_guid *e2) { if (e1->uid < e2->uid) return -1; else if (e1->uid > e2->uid) return 1; else return 0; } void mail_index_transaction_sort_expunges(struct mail_index_transaction *t) { if (!t->expunges_nonsorted) return; array_sort(&t->expunges, mail_transaction_expunge_guid_cmp); t->expunges_nonsorted = FALSE; } static void ext_reset_update_atomic(struct mail_index_transaction *t, uint32_t ext_id, uint32_t expected_reset_id) { const struct mail_index_ext *map_ext; struct mail_transaction_ext_reset *reset; uint32_t idx, reset_id; if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) { /* new extension */ reset_id = 1; } else { map_ext = array_idx(&t->view->index->map->extensions, idx); reset_id = map_ext->reset_id + 1; } if (reset_id != expected_reset_id) { /* ignore this extension update */ mail_index_ext_set_reset_id(t, ext_id, 0); return; } if (reset_id == 0) reset_id++; array_idx_set(&t->ext_reset_ids, ext_id, &reset_id); /* reseting existing data is optional */ if (array_is_created(&t->ext_resets)) { reset = array_idx_modifiable(&t->ext_resets, ext_id); if (reset->new_reset_id == (uint32_t)-1) reset->new_reset_id = reset_id; } } static void transaction_update_atomic_reset_ids(struct mail_index_transaction *t) { const uint32_t *expected_reset_ids; unsigned int ext_id, count; if (!array_is_created(&t->ext_reset_atomic)) return; expected_reset_ids = array_get(&t->ext_reset_atomic, &count); for (ext_id = 0; ext_id < count; ext_id++) { if (expected_reset_ids[ext_id] != 0) { ext_reset_update_atomic(t, ext_id, expected_reset_ids[ext_id]); } } } static unsigned int mail_transaction_drop_range(struct mail_index_transaction *t, struct mail_index_flag_update update, unsigned int update_idx, ARRAY_TYPE(seq_range) *keeps) { const struct seq_range *keep_range; unsigned int i, keep_count; keep_range = array_get(keeps, &keep_count); if (keep_count == 1 && update.uid1 == keep_range[0].seq1 && update.uid2 == keep_range[0].seq2) { /* evereything is kept */ return update_idx + 1; } array_delete(&t->updates, update_idx, 1); /* add back all the updates we want to keep */ for (i = 0; i < keep_count; i++, update_idx++) { update.uid1 = keep_range[i].seq1; update.uid2 = keep_range[i].seq2; array_insert(&t->updates, update_idx, &update, 1); } return update_idx; } static void mail_index_transaction_finish_flag_updates(struct mail_index_transaction *t) { const struct mail_index_flag_update *updates, *u; const struct mail_index_record *rec; unsigned int i, count; ARRAY_TYPE(seq_range) keeps; uint32_t seq; if (!t->drop_unnecessary_flag_updates || !array_is_created(&t->updates)) return; t_array_init(&keeps, 64); updates = array_get(&t->updates, &count); for (i = 0; i < count; ) { /* first get the list of changes to drop */ u = &updates[i]; array_clear(&keeps); for (seq = u->uid1; seq <= u->uid2; seq++) { rec = mail_index_lookup(t->view, seq); if ((rec->flags & u->add_flags) != u->add_flags || (rec->flags & u->remove_flags) != 0) { /* keep this change */ seq_range_array_add(&keeps, seq); } } i = mail_transaction_drop_range(t, updates[i], i, &keeps); updates = array_get(&t->updates, &count); } if (array_count(&t->updates) == 0) array_free(&t->updates); } static void mail_index_transaction_check_conflicts(struct mail_index_transaction *t) { uint32_t seq; bool ret1, ret2; i_assert(t->max_modseq != 0); i_assert(t->conflict_seqs != NULL); if (t->max_modseq == mail_index_modseq_get_highest(t->view)) { /* no conflicts possible */ return; } if (t->min_flagupdate_seq == 0) { /* no flag updates */ return; } for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) { if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) { ret1 = mail_index_cancel_flag_updates(t, seq); ret2 = mail_index_cancel_keyword_updates(t, seq); if (ret1 || ret2) { seq_range_array_add_with_init(t->conflict_seqs, 16, seq); } } } mail_index_transaction_set_log_updates(t); } static uint32_t mail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq) { const struct mail_index_record *rec; i_assert(seq > 0); if (seq >= t->first_new_seq) rec = mail_index_transaction_lookup(t, seq); else { i_assert(seq <= t->view->map->hdr.messages_count); rec = MAIL_INDEX_REC_AT_SEQ(t->view->map, seq); } i_assert(rec->uid != 0); return rec->uid; } static void mail_index_convert_to_uids(struct mail_index_transaction *t, ARRAY_TYPE(seq_array) *array) { uint32_t *seq; unsigned int i, count; if (!array_is_created(array)) return; count = array_count(array); for (i = 0; i < count; i++) { seq = array_idx_modifiable(array, i); *seq = mail_index_transaction_get_uid(t, *seq); } } static uint32_t get_nonexpunged_uid2(struct mail_index_transaction *t, uint32_t uid1, uint32_t seq1) { seq1++; while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) { seq1++; uid1++; } return uid1; } void mail_index_transaction_seq_range_to_uid(struct mail_index_transaction *t, ARRAY_TYPE(seq_range) *array) { struct seq_range *range, *new_range; unsigned int i, count; uint32_t uid1, uid2, prev_uid = 0; if (!array_is_created(array)) return; count = array_count(array); for (i = 0; i < count; i++) { range = array_idx_modifiable(array, i); uid1 = mail_index_transaction_get_uid(t, range->seq1); uid2 = mail_index_transaction_get_uid(t, range->seq2); i_assert(uid1 > prev_uid); if (uid2 - uid1 == range->seq2 - range->seq1) { /* simple conversion */ range->seq1 = uid1; range->seq2 = uid2; prev_uid = uid2; } else { /* remove expunged UIDs */ new_range = array_insert_space(array, i); range = array_idx_modifiable(array, i + 1); count++; memcpy(new_range, range, array->arr.element_size); new_range->seq1 = uid1; new_range->seq2 = get_nonexpunged_uid2(t, uid1, range->seq1); i_assert(new_range->seq2 < uid2); /* continue the range without the inserted seqs */ range->seq1 += new_range->seq2 - new_range->seq1 + 1; prev_uid = new_range->seq2; } } } static void keyword_updates_convert_to_uids(struct mail_index_transaction *t) { struct mail_index_transaction_keyword_update *update; if (!array_is_created(&t->keyword_updates)) return; array_foreach_modifiable(&t->keyword_updates, update) { mail_index_transaction_seq_range_to_uid(t, &update->add_seq); mail_index_transaction_seq_range_to_uid(t, &update->remove_seq); } } static void expunges_convert_to_uids(struct mail_index_transaction *t) { struct mail_transaction_expunge_guid *expunges; unsigned int src, dest, count; if (!array_is_created(&t->expunges)) return; mail_index_transaction_sort_expunges(t); expunges = array_get_modifiable(&t->expunges, &count); if (count == 0) return; /* convert uids and drop duplicates */ expunges[0].uid = mail_index_transaction_get_uid(t, expunges[0].uid); for (src = dest = 1; src < count; src++) { expunges[dest].uid = mail_index_transaction_get_uid(t, expunges[src].uid); if (expunges[dest-1].uid != expunges[dest].uid) { if (dest != src) { memcpy(expunges[dest].guid_128, expunges[src].guid_128, sizeof(expunges[dest].guid_128)); } dest++; } } array_delete(&t->expunges, dest, count-dest); } static void mail_index_transaction_convert_to_uids(struct mail_index_transaction *t) { ARRAY_TYPE(seq_array) *update; if (array_is_created(&t->ext_rec_updates)) { array_foreach_modifiable(&t->ext_rec_updates, update) mail_index_convert_to_uids(t, update); } if (array_is_created(&t->ext_rec_atomics)) { array_foreach_modifiable(&t->ext_rec_atomics, update) mail_index_convert_to_uids(t, update); } keyword_updates_convert_to_uids(t); expunges_convert_to_uids(t); mail_index_convert_to_uids(t, (void *)&t->modseq_updates); mail_index_transaction_seq_range_to_uid(t, (void *)&t->updates); } void mail_index_transaction_finish_so_far(struct mail_index_transaction *t) { if (array_is_created(&t->appends)) mail_index_transaction_sort_appends(t); mail_index_transaction_finish_flag_updates(t); if (t->max_modseq != 0) mail_index_transaction_check_conflicts(t); } void mail_index_transaction_finish(struct mail_index_transaction *t) { mail_index_transaction_finish_so_far(t); if (array_is_created(&t->appends)) mail_index_update_day_headers(t, ioloop_time); if (array_is_created(&t->ext_reset_atomic)) transaction_update_atomic_reset_ids(t); /* finally convert all sequences to UIDs before we write them, but after we've checked and removed conflicts */ mail_index_transaction_convert_to_uids(t); /* and kind of ugly way to update highest modseq */ if (t->min_highest_modseq != 0) mail_index_update_modseq(t, 0, t->min_highest_modseq); } dovecot-2.2.33.2/src/lib-index/mail-index-view-sync.c0000644000175000017500000007047113165463624017127 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-index-modseq.h" #include "mail-transaction-log.h" struct mail_index_view_sync_ctx { struct mail_index_view *view; enum mail_index_view_sync_flags flags; struct mail_index_sync_map_ctx sync_map_ctx; /* After syncing view, map is replaced with sync_new_map. */ struct mail_index_map *sync_new_map; ARRAY_TYPE(seq_range) expunges; unsigned int finish_min_msg_count; const struct mail_transaction_header *hdr; const void *data; /* temporary variables while handling lost transaction logs: */ ARRAY_TYPE(keyword_indexes) lost_old_kw, lost_new_kw; buffer_t *lost_kw_buf; uint32_t lost_new_ext_idx; /* result of lost transaction logs: */ ARRAY_TYPE(seq_range) lost_flags; unsigned int lost_flag_idx; size_t data_offset; unsigned int failed:1; unsigned int sync_map_update:1; unsigned int skipped_expunges:1; unsigned int last_read:1; unsigned int log_was_lost:1; unsigned int hidden:1; }; static int view_sync_set_log_view_range(struct mail_index_view *view, bool sync_expunges, bool *reset_r, bool *partial_sync_r) { const struct mail_index_header *hdr = &view->index->map->hdr; uint32_t start_seq, end_seq; uoff_t start_offset, end_offset; const char *reason; int ret; *partial_sync_r = FALSE; start_seq = view->log_file_expunge_seq; start_offset = view->log_file_expunge_offset; end_seq = hdr->log_file_seq; end_offset = hdr->log_file_head_offset; if (end_seq < view->log_file_head_seq || (end_seq == view->log_file_head_seq && end_offset < view->log_file_head_offset)) { mail_index_set_error(view->index, "%s log position went backwards " "(%u,%"PRIuUOFF_T" < %u,%"PRIuUOFF_T")", view->index->filepath, end_seq, end_offset, view->log_file_head_seq, view->log_file_head_offset); return -1; } for (;;) { /* the view begins from the first non-synced transaction */ ret = mail_transaction_log_view_set(view->log_view, start_seq, start_offset, end_seq, end_offset, reset_r, &reason); if (ret <= 0) { mail_index_set_error(view->index, "Failed to map view for %s: %s", view->index->filepath, reason); return ret; } if (!*reset_r || sync_expunges) break; /* log was reset, but we don't want to sync expunges. we can't do this, so sync only up to the reset. */ mail_transaction_log_view_get_prev_pos(view->log_view, &end_seq, &end_offset); end_seq--; end_offset = (uoff_t)-1; if (end_seq < start_seq) { /* we have only this reset log */ mail_transaction_log_view_clear(view->log_view, view->log_file_expunge_seq); break; } *partial_sync_r = TRUE; } return 1; } static unsigned int view_sync_expunges2seqs(struct mail_index_view_sync_ctx *ctx) { struct mail_index_view *view = ctx->view; struct seq_range *src, *src_end, *dest; unsigned int count, expunge_count = 0; uint32_t prev_seq = 0; /* convert UIDs to sequences */ src = dest = array_get_modifiable(&ctx->expunges, &count); src_end = src + count; for (; src != src_end; src++) { if (!mail_index_lookup_seq_range(view, src->seq1, src->seq2, &dest->seq1, &dest->seq2)) count--; else { i_assert(dest->seq1 > prev_seq); prev_seq = dest->seq2; expunge_count += dest->seq2 - dest->seq1 + 1; dest++; } } array_delete(&ctx->expunges, count, array_count(&ctx->expunges) - count); return expunge_count; } static void view_sync_add_expunge_range(ARRAY_TYPE(seq_range) *dest, const struct seq_range *src, size_t src_size) { unsigned int i, src_count; i_assert(src_size % sizeof(*src) == 0); src_count = src_size / sizeof(*src); for (i = 0; i < src_count; i++) seq_range_array_add_range(dest, src[i].seq1, src[i].seq2); } static void view_sync_add_expunge_guids(ARRAY_TYPE(seq_range) *dest, const struct mail_transaction_expunge_guid *src, size_t src_size) { unsigned int i, src_count; i_assert(src_size % sizeof(*src) == 0); src_count = src_size / sizeof(*src); for (i = 0; i < src_count; i++) seq_range_array_add(dest, src[i].uid); } static int view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx, unsigned int *expunge_count_r) { struct mail_index_view *view = ctx->view; const struct mail_transaction_header *hdr; const void *data; int ret; /* get a list of expunge transactions. there may be some that we have already synced, but it doesn't matter because they'll get dropped out when converting to sequences. the uid ranges' validity has already been verified, so we can use them directly. */ mail_transaction_log_view_mark(view->log_view); while ((ret = mail_transaction_log_view_next(view->log_view, &hdr, &data)) > 0) { if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* skip expunge requests */ continue; } if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { view_sync_add_expunge_guids(&ctx->expunges, data, hdr->size); } else if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { view_sync_add_expunge_range(&ctx->expunges, data, hdr->size); } } mail_transaction_log_view_rewind(view->log_view); *expunge_count_r = view_sync_expunges2seqs(ctx); return ret; } static bool have_existing_expunges(struct mail_index_view *view, const struct seq_range *range, size_t size) { const struct seq_range *range_end; uint32_t seq1, seq2; range_end = CONST_PTR_OFFSET(range, size); for (; range != range_end; range++) { if (mail_index_lookup_seq_range(view, range->seq1, range->seq2, &seq1, &seq2)) return TRUE; } return FALSE; } static bool have_existing_guid_expunge(struct mail_index_view *view, const struct mail_transaction_expunge_guid *expunges, size_t size) { const struct mail_transaction_expunge_guid *expunges_end; uint32_t seq; expunges_end = CONST_PTR_OFFSET(expunges, size); for (; expunges != expunges_end; expunges++) { if (mail_index_lookup_seq(view, expunges->uid, &seq)) return TRUE; } return FALSE; } static bool view_sync_have_expunges(struct mail_index_view *view) { const struct mail_transaction_header *hdr; const void *data; bool have_expunges = FALSE; int ret; mail_transaction_log_view_mark(view->log_view); while ((ret = mail_transaction_log_view_next(view->log_view, &hdr, &data)) > 0) { if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* skip expunge requests */ continue; } if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { /* we have an expunge. see if it still exists. */ if (have_existing_guid_expunge(view, data, hdr->size)) { have_expunges = TRUE; break; } } else if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { /* we have an expunge. see if it still exists. */ if (have_existing_expunges(view, data, hdr->size)) { have_expunges = TRUE; break; } } } mail_transaction_log_view_rewind(view->log_view); /* handle failures as having expunges (which is safer). we'll probably fail later. */ return ret < 0 || have_expunges; } static int uint_cmp(const void *p1, const void *p2) { const unsigned int *u1 = p1, *u2 = p2; if (*u1 < *u2) return -1; if (*u1 > *u2) return 1; return 0; } static bool view_sync_lost_keywords_equal(struct mail_index_view_sync_ctx *ctx) { unsigned int *old_idx, *new_idx; unsigned int old_count, new_count; old_idx = array_get_modifiable(&ctx->lost_old_kw, &old_count); new_idx = array_get_modifiable(&ctx->lost_new_kw, &new_count); if (old_count != new_count) return FALSE; qsort(old_idx, old_count, sizeof(*old_idx), uint_cmp); qsort(new_idx, new_count, sizeof(*new_idx), uint_cmp); return memcmp(old_idx, new_idx, old_count * sizeof(*old_idx)) == 0; } static int view_sync_update_keywords(struct mail_index_view_sync_ctx *ctx, uint32_t uid) { struct mail_transaction_header thdr; struct mail_transaction_keyword_update kw_up; const unsigned int *kw_idx; const char *const *kw_names; unsigned int i, count; kw_idx = array_get(&ctx->lost_new_kw, &count); if (count == 0) return 0; kw_names = array_idx(&ctx->view->index->keywords, 0); i_zero(&thdr); thdr.type = MAIL_TRANSACTION_KEYWORD_UPDATE | MAIL_TRANSACTION_EXTERNAL; i_zero(&kw_up); kw_up.modify_type = MODIFY_ADD; /* add new flags one by one */ for (i = 0; i < count; i++) { kw_up.name_size = strlen(kw_names[kw_idx[i]]); buffer_set_used_size(ctx->lost_kw_buf, 0); buffer_append(ctx->lost_kw_buf, &kw_up, sizeof(kw_up)); buffer_append(ctx->lost_kw_buf, kw_names[kw_idx[i]], kw_up.name_size); if (ctx->lost_kw_buf->used % 4 != 0) { buffer_append_zero(ctx->lost_kw_buf, 4 - ctx->lost_kw_buf->used % 4); } buffer_append(ctx->lost_kw_buf, &uid, sizeof(uid)); buffer_append(ctx->lost_kw_buf, &uid, sizeof(uid)); thdr.size = ctx->lost_kw_buf->used; if (mail_index_sync_record(&ctx->sync_map_ctx, &thdr, ctx->lost_kw_buf->data) < 0) return -1; } return 0; } static int view_sync_apply_lost_changes(struct mail_index_view_sync_ctx *ctx, uint32_t old_seq, uint32_t new_seq) { struct mail_index_map *old_map = ctx->view->map; struct mail_index_map *new_map = ctx->view->index->map; const struct mail_index_record *old_rec, *new_rec; struct mail_transaction_header thdr; const struct mail_index_ext *ext; const uint64_t *modseqp; uint64_t new_modseq; bool changed = FALSE; old_rec = MAIL_INDEX_REC_AT_SEQ(old_map, old_seq); new_rec = MAIL_INDEX_REC_AT_SEQ(new_map, new_seq); i_zero(&thdr); if (old_rec->flags != new_rec->flags) { struct mail_transaction_flag_update flag_update; /* check this before syncing the record, since it updates old_rec. */ if ((old_rec->flags & MAIL_INDEX_FLAGS_MASK) != (new_rec->flags & MAIL_INDEX_FLAGS_MASK)) changed = TRUE; thdr.type = MAIL_TRANSACTION_FLAG_UPDATE | MAIL_TRANSACTION_EXTERNAL; thdr.size = sizeof(flag_update); i_zero(&flag_update); flag_update.uid1 = flag_update.uid2 = new_rec->uid; flag_update.add_flags = new_rec->flags; flag_update.remove_flags = ~new_rec->flags & 0xff; if (mail_index_sync_record(&ctx->sync_map_ctx, &thdr, &flag_update) < 0) return -1; } mail_index_map_lookup_keywords(old_map, old_seq, &ctx->lost_old_kw); mail_index_map_lookup_keywords(new_map, new_seq, &ctx->lost_new_kw); if (!view_sync_lost_keywords_equal(ctx)) { struct mail_transaction_keyword_reset kw_reset; thdr.type = MAIL_TRANSACTION_KEYWORD_RESET | MAIL_TRANSACTION_EXTERNAL; thdr.size = sizeof(kw_reset); /* remove all old flags by resetting them */ i_zero(&kw_reset); kw_reset.uid1 = kw_reset.uid2 = new_rec->uid; if (mail_index_sync_record(&ctx->sync_map_ctx, &thdr, &kw_reset) < 0) return -1; if (view_sync_update_keywords(ctx, new_rec->uid) < 0) return -1; changed = TRUE; } if (changed) { /* flags or keywords changed */ } else if (ctx->view->highest_modseq != 0 && ctx->lost_new_ext_idx != (uint32_t)-1) { /* if modseq has changed include this message in changed flags list, even if we didn't see any changes above. */ ext = array_idx(&new_map->extensions, ctx->lost_new_ext_idx); modseqp = CONST_PTR_OFFSET(new_rec, ext->record_offset); new_modseq = *modseqp; if (new_modseq > ctx->view->highest_modseq) changed = TRUE; } /* without modseqs lost_flags isn't updated perfectly correctly, because by the time we're comparing old flags it may have changed from what we last sent to the client (because the map is shared). This could be avoided by always keeping a private copy of the map in the view, but that's a waste of memory for as rare of a problem as this. */ if (changed) seq_range_array_add(&ctx->lost_flags, new_rec->uid); return 0; } static int view_sync_get_log_lost_changes(struct mail_index_view_sync_ctx *ctx, unsigned int *expunge_count_r) { struct mail_index_view *view = ctx->view; struct mail_index_map *old_map = view->map; struct mail_index_map *new_map = view->index->map; const unsigned int old_count = old_map->hdr.messages_count; const unsigned int new_count = new_map->hdr.messages_count; const struct mail_index_record *old_rec, *new_rec; struct mail_transaction_header thdr; uint32_t seqi, seqj; /* we don't update the map in the same order as it's typically done. map->rec_map may already have some messages appended that we don't want. get an atomic map to make sure these get removed. */ (void)mail_index_sync_get_atomic_map(&ctx->sync_map_ctx); if (!mail_index_map_get_ext_idx(new_map, view->index->modseq_ext_id, &ctx->lost_new_ext_idx)) ctx->lost_new_ext_idx = (uint32_t)-1; i_array_init(&ctx->lost_flags, 64); t_array_init(&ctx->lost_old_kw, 32); t_array_init(&ctx->lost_new_kw, 32); ctx->lost_kw_buf = buffer_create_dynamic(pool_datastack_create(), 128); /* handle expunges and sync flags */ seqi = seqj = 1; while (seqi <= old_count && seqj <= new_count) { old_rec = MAIL_INDEX_REC_AT_SEQ(old_map, seqi); new_rec = MAIL_INDEX_REC_AT_SEQ(new_map, seqj); if (old_rec->uid == new_rec->uid) { /* message found - check if flags have changed */ if (view_sync_apply_lost_changes(ctx, seqi, seqj) < 0) return -1; seqi++; seqj++; } else if (old_rec->uid < new_rec->uid) { /* message expunged */ seq_range_array_add(&ctx->expunges, old_rec->uid); seqi++; } else { /* new message appeared out of nowhere */ mail_index_set_error(view->index, "%s view is inconsistent: " "uid=%u inserted in the middle of mailbox", view->index->filepath, new_rec->uid); return -1; } } /* if there are old messages left, they're all expunged */ for (; seqi <= old_count; seqi++) { old_rec = MAIL_INDEX_REC_AT_SEQ(old_map, seqi); seq_range_array_add(&ctx->expunges, old_rec->uid); } /* if there are new messages left, they're all new messages */ thdr.type = MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL; thdr.size = sizeof(*new_rec); for (; seqj <= new_count; seqj++) { new_rec = MAIL_INDEX_REC_AT_SEQ(new_map, seqj); if (mail_index_sync_record(&ctx->sync_map_ctx, &thdr, new_rec) < 0) return -1; mail_index_map_lookup_keywords(new_map, seqj, &ctx->lost_new_kw); if (view_sync_update_keywords(ctx, new_rec->uid) < 0) return -1; } *expunge_count_r = view_sync_expunges2seqs(ctx); /* we have no idea how far we've synced - make sure these aren't used */ old_map->hdr.log_file_seq = 0; old_map->hdr.log_file_head_offset = 0; old_map->hdr.log_file_tail_offset = 0; if ((ctx->flags & MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES) != 0) { array_clear(&ctx->expunges); ctx->skipped_expunges = *expunge_count_r > 0; } else { view->log_file_head_seq = new_map->hdr.log_file_seq; view->log_file_head_offset = new_map->hdr.log_file_head_offset; } return 0; } static int mail_index_view_sync_init_fix(struct mail_index_view_sync_ctx *ctx) { struct mail_index_view *view = ctx->view; uint32_t seq; uoff_t offset; const char *reason; bool reset; int ret; /* replace the view's map */ view->index->map->refcount++; mail_index_unmap(&view->map); view->map = view->index->map; /* update log positions */ view->log_file_head_seq = seq = view->map->hdr.log_file_seq; view->log_file_head_offset = offset = view->map->hdr.log_file_head_offset; ret = mail_transaction_log_view_set(view->log_view, seq, offset, seq, offset, &reset, &reason); if (ret <= 0) { mail_index_set_error(view->index, "Failed to fix view for %s: %s", view->index->filepath, reason); return ret; } view->inconsistent = FALSE; return 0; } struct mail_index_view_sync_ctx * mail_index_view_sync_begin(struct mail_index_view *view, enum mail_index_view_sync_flags flags) { struct mail_index_view_sync_ctx *ctx; struct mail_index_map *tmp_map; unsigned int expunge_count = 0; bool reset, partial_sync, sync_expunges, have_expunges; int ret; i_assert(!view->syncing); i_assert(view->transactions == 0); view->syncing = TRUE; /* Syncing the view invalidates all previous looked up records. Unreference the mappings this view keeps because of them. */ mail_index_view_unref_maps(view); ctx = i_new(struct mail_index_view_sync_ctx, 1); ctx->view = view; ctx->flags = flags; sync_expunges = (flags & MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES) == 0; if (sync_expunges) i_array_init(&ctx->expunges, 64); if ((flags & MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT) != 0) { /* just get this view synced - don't return anything */ i_assert(sync_expunges); if (mail_index_view_sync_init_fix(ctx) < 0) ctx->failed = TRUE; return ctx; } if (mail_index_view_is_inconsistent(view)) { mail_index_set_error(view->index, "%s view is inconsistent", view->index->filepath); ctx->failed = TRUE; return ctx; } ret = view_sync_set_log_view_range(view, sync_expunges, &reset, &partial_sync); if (ret < 0) { ctx->failed = TRUE; return ctx; } if (ret == 0) { ctx->log_was_lost = TRUE; if (!sync_expunges) i_array_init(&ctx->expunges, 64); mail_index_sync_map_init(&ctx->sync_map_ctx, view, MAIL_INDEX_SYNC_HANDLER_VIEW); ret = view_sync_get_log_lost_changes(ctx, &expunge_count); mail_index_modseq_sync_end(&ctx->sync_map_ctx.modseq_ctx); mail_index_sync_map_deinit(&ctx->sync_map_ctx); if (ret < 0) { mail_index_set_error(view->index, "%s view syncing failed to apply changes", view->index->filepath); view->inconsistent = TRUE; ctx->failed = TRUE; return ctx; } have_expunges = expunge_count > 0; } else if (sync_expunges) { /* get list of all expunges first */ if (view_sync_get_expunges(ctx, &expunge_count) < 0) { ctx->failed = TRUE; return ctx; } have_expunges = expunge_count > 0; } else { have_expunges = view_sync_have_expunges(view); } ctx->finish_min_msg_count = reset ? 0 : view->map->hdr.messages_count - expunge_count; if (reset) { view->inconsistent = TRUE; mail_index_set_error(view->index, "%s reset, view is now inconsistent", view->index->filepath); ctx->failed = TRUE; return ctx; } if (!have_expunges && !partial_sync) { /* no expunges, we can just replace the map */ if (view->index->map->hdr.messages_count < ctx->finish_min_msg_count) { mail_index_set_error(view->index, "Index %s lost messages without expunging " "(%u -> %u)", view->index->filepath, view->map->hdr.messages_count, view->index->map->hdr.messages_count); ctx->finish_min_msg_count = 0; view->inconsistent = TRUE; } view->index->map->refcount++; mail_index_unmap(&view->map); view->map = view->index->map; } else { /* a) expunges seen. b) doing a partial sync because we saw a reset. Create a private map which we update. If we're syncing expunges the map will finally be replaced with the head map to remove the expunged messages. */ ctx->sync_map_update = TRUE; if (view->map->refcount > 1) { tmp_map = mail_index_map_clone(view->map); mail_index_unmap(&view->map); view->map = tmp_map; } if (sync_expunges) { ctx->sync_new_map = view->index->map; ctx->sync_new_map->refcount++; } } mail_index_sync_map_init(&ctx->sync_map_ctx, view, MAIL_INDEX_SYNC_HANDLER_VIEW); #ifdef DEBUG mail_index_map_check(view->map); #endif return ctx; } static bool view_sync_is_hidden(struct mail_index_view *view, uint32_t seq, uoff_t offset) { const struct mail_index_view_log_sync_area *sync; if (!array_is_created(&view->syncs_hidden)) return FALSE; array_foreach(&view->syncs_hidden, sync) { if (sync->log_file_offset <= offset && offset - sync->log_file_offset < sync->length && sync->log_file_seq == seq) return TRUE; } return FALSE; } static bool mail_index_view_sync_want(struct mail_index_view_sync_ctx *ctx, const struct mail_transaction_header *hdr) { struct mail_index_view *view = ctx->view; uint32_t seq; uoff_t offset, next_offset; mail_transaction_log_view_get_prev_pos(view->log_view, &seq, &offset); next_offset = offset + sizeof(*hdr) + hdr->size; if ((hdr->type & (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_GUID)) != 0 && (hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0) { if ((ctx->flags & MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES) != 0) { i_assert(!LOG_IS_BEFORE(seq, offset, view->log_file_expunge_seq, view->log_file_expunge_offset)); if (!ctx->skipped_expunges) { view->log_file_expunge_seq = seq; view->log_file_expunge_offset = offset; ctx->skipped_expunges = TRUE; } return FALSE; } if (LOG_IS_BEFORE(seq, offset, view->log_file_expunge_seq, view->log_file_expunge_offset)) { /* already synced */ return FALSE; } } if (LOG_IS_BEFORE(seq, offset, view->log_file_head_seq, view->log_file_head_offset)) { /* already synced */ return FALSE; } view->log_file_head_seq = seq; view->log_file_head_offset = next_offset; return TRUE; } static int mail_index_view_sync_get_next_transaction(struct mail_index_view_sync_ctx *ctx) { struct mail_transaction_log_view *log_view = ctx->view->log_view; struct mail_index_view *view = ctx->view; const struct mail_transaction_header *hdr; uint32_t seq; uoff_t offset; int ret; bool synced_to_map; do { /* Get the next transaction from log. */ ret = mail_transaction_log_view_next(log_view, &ctx->hdr, &ctx->data); if (ret <= 0) { if (ret < 0) return -1; ctx->hdr = NULL; ctx->last_read = TRUE; return 0; } hdr = ctx->hdr; /* skip records we've already synced */ } while (!mail_index_view_sync_want(ctx, hdr)); mail_transaction_log_view_get_prev_pos(log_view, &seq, &offset); /* If we started from a map that we didn't create ourself, some of the transactions may already be synced. at the end of this view sync we'll update file_seq=0 so that this check always becomes FALSE for subsequent syncs. */ synced_to_map = view->map->hdr.log_file_seq != 0 && LOG_IS_BEFORE(seq, offset, view->map->hdr.log_file_seq, view->map->hdr.log_file_head_offset); /* Apply transaction to view's mapping if needed (meaning we didn't just re-map the view to head mapping). */ if (ctx->sync_map_update && !synced_to_map) { if ((hdr->type & (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_GUID)) == 0) { ret = mail_index_sync_record(&ctx->sync_map_ctx, hdr, ctx->data); } if (ret < 0) return -1; } ctx->hidden = view_sync_is_hidden(view, seq, offset); return 1; } static bool mail_index_view_sync_get_rec(struct mail_index_view_sync_ctx *ctx, struct mail_index_view_sync_rec *rec) { const struct mail_transaction_header *hdr = ctx->hdr; const void *data = ctx->data; switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_FLAG_UPDATE: { const struct mail_transaction_flag_update *update = CONST_PTR_OFFSET(data, ctx->data_offset); /* data contains mail_transaction_flag_update[] */ for (;;) { ctx->data_offset += sizeof(*update); if (!MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(update)) break; /* skip internal flag changes */ if (ctx->data_offset == ctx->hdr->size) return FALSE; update = CONST_PTR_OFFSET(data, ctx->data_offset); } if (update->add_flags != 0 || update->remove_flags != 0) rec->type = MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS; else rec->type = MAIL_INDEX_VIEW_SYNC_TYPE_MODSEQ; rec->uid1 = update->uid1; rec->uid2 = update->uid2; break; } case MAIL_TRANSACTION_KEYWORD_UPDATE: { const struct mail_transaction_keyword_update *update = data; const uint32_t *uids; /* data contains mail_transaction_keyword_update header, the keyword name and an array of { uint32_t uid1, uid2; } */ if (ctx->data_offset == 0) { /* skip over the header and name */ ctx->data_offset = sizeof(*update) + update->name_size; if ((ctx->data_offset % 4) != 0) ctx->data_offset += 4 - (ctx->data_offset % 4); } uids = CONST_PTR_OFFSET(data, ctx->data_offset); rec->type = MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS; rec->uid1 = uids[0]; rec->uid2 = uids[1]; ctx->data_offset += sizeof(uint32_t) * 2; break; } case MAIL_TRANSACTION_KEYWORD_RESET: { const struct mail_transaction_keyword_reset *reset = CONST_PTR_OFFSET(data, ctx->data_offset); /* data contains mail_transaction_keyword_reset[] */ rec->type = MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS; rec->uid1 = reset->uid1; rec->uid2 = reset->uid2; ctx->data_offset += sizeof(*reset); break; } default: ctx->hdr = NULL; return FALSE; } rec->hidden = ctx->hidden; return TRUE; } static bool mail_index_view_sync_next_lost(struct mail_index_view_sync_ctx *ctx, struct mail_index_view_sync_rec *sync_rec) { const struct seq_range *range; unsigned int count; range = array_get(&ctx->lost_flags, &count); if (ctx->lost_flag_idx == count) { ctx->last_read = TRUE; return FALSE; } sync_rec->type = MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS; sync_rec->uid1 = range[ctx->lost_flag_idx].seq1; sync_rec->uid2 = range[ctx->lost_flag_idx].seq2; sync_rec->hidden = FALSE; ctx->lost_flag_idx++; return TRUE; } bool mail_index_view_sync_next(struct mail_index_view_sync_ctx *ctx, struct mail_index_view_sync_rec *sync_rec) { int ret; if (ctx->log_was_lost) return mail_index_view_sync_next_lost(ctx, sync_rec); do { if (ctx->hdr == NULL || ctx->data_offset == ctx->hdr->size) { ret = mail_index_view_sync_get_next_transaction(ctx); if (ret <= 0) { if (ret < 0) ctx->failed = TRUE; return FALSE; } ctx->data_offset = 0; } } while (!mail_index_view_sync_get_rec(ctx, sync_rec)); return TRUE; } void mail_index_view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx, const ARRAY_TYPE(seq_range) **expunges_r) { *expunges_r = &ctx->expunges; } static void mail_index_view_sync_clean_log_syncs(struct mail_index_view *view) { const struct mail_index_view_log_sync_area *syncs; unsigned int i, count; if (!array_is_created(&view->syncs_hidden)) return; /* Clean up to view's tail */ syncs = array_get(&view->syncs_hidden, &count); for (i = 0; i < count; i++) { if ((syncs[i].log_file_offset + syncs[i].length > view->log_file_expunge_offset && syncs[i].log_file_seq == view->log_file_expunge_seq) || syncs[i].log_file_seq > view->log_file_expunge_seq) break; } if (i > 0) array_delete(&view->syncs_hidden, 0, i); } int mail_index_view_sync_commit(struct mail_index_view_sync_ctx **_ctx, bool *delayed_expunges_r) { struct mail_index_view_sync_ctx *ctx = *_ctx; struct mail_index_view *view = ctx->view; int ret = ctx->failed ? -1 : 0; i_assert(view->syncing); *_ctx = NULL; *delayed_expunges_r = ctx->skipped_expunges; if ((!ctx->last_read || view->inconsistent) && (ctx->flags & MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT) == 0) { /* we didn't sync everything */ view->inconsistent = TRUE; ret = -1; } if (ctx->sync_map_ctx.modseq_ctx != NULL) mail_index_modseq_sync_end(&ctx->sync_map_ctx.modseq_ctx); if (ctx->sync_new_map != NULL) { mail_index_unmap(&view->map); view->map = ctx->sync_new_map; } else if (ctx->sync_map_update) { /* log offsets have no meaning in views. make sure they're not tried to be used wrong by setting them to zero. */ view->map->hdr.log_file_seq = 0; view->map->hdr.log_file_head_offset = 0; view->map->hdr.log_file_tail_offset = 0; } i_assert(view->map->hdr.messages_count >= ctx->finish_min_msg_count); if (!ctx->skipped_expunges) { view->log_file_expunge_seq = view->log_file_head_seq; view->log_file_expunge_offset = view->log_file_head_offset; } if (ctx->sync_map_ctx.view != NULL) mail_index_sync_map_deinit(&ctx->sync_map_ctx); mail_index_view_sync_clean_log_syncs(ctx->view); #ifdef DEBUG mail_index_map_check(view->map); #endif /* set log view to empty range so unneeded memory gets freed */ mail_transaction_log_view_clear(view->log_view, view->log_file_expunge_seq); if (array_is_created(&ctx->expunges)) array_free(&ctx->expunges); if (array_is_created(&ctx->lost_flags)) array_free(&ctx->lost_flags); view->highest_modseq = mail_index_map_modseq_get_highest(view->map); view->syncing = FALSE; i_free(ctx); return ret; } void mail_index_view_add_hidden_transaction(struct mail_index_view *view, uint32_t log_file_seq, uoff_t log_file_offset, unsigned int length) { struct mail_index_view_log_sync_area *area; if (!array_is_created(&view->syncs_hidden)) i_array_init(&view->syncs_hidden, 32); area = array_append_space(&view->syncs_hidden); area->log_file_seq = log_file_seq; area->log_file_offset = log_file_offset; area->length = length; } dovecot-2.2.33.2/src/lib-index/test-mail-index-transaction-finish.c0000644000175000017500000002170213165463624021754 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mail-index-transaction-private.h" static struct mail_index_record recs[20]; static uint64_t modseqs[N_ELEMENTS(recs)]; bool mail_index_map_get_ext_idx(struct mail_index_map *map ATTR_UNUSED, uint32_t ext_id ATTR_UNUSED, uint32_t *idx_r ATTR_UNUSED) { return FALSE; } void mail_index_ext_set_reset_id(struct mail_index_transaction *t ATTR_UNUSED, uint32_t ext_id ATTR_UNUSED, uint32_t reset_id ATTR_UNUSED) { } void mail_index_transaction_set_log_updates(struct mail_index_transaction *t ATTR_UNUSED) { } void mail_index_update_day_headers(struct mail_index_transaction *t ATTR_UNUSED, time_t day_stamp ATTR_UNUSED) {} bool mail_index_cancel_flag_updates(struct mail_index_transaction *t ATTR_UNUSED, uint32_t seq ATTR_UNUSED) { return TRUE; } bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t ATTR_UNUSED, uint32_t seq ATTR_UNUSED) { return TRUE; } void mail_index_transaction_sort_appends(struct mail_index_transaction *t ATTR_UNUSED) {} int mail_index_map(struct mail_index *index ATTR_UNUSED, enum mail_index_sync_handler_type type ATTR_UNUSED) { return 1; } void mail_index_update_modseq(struct mail_index_transaction *t ATTR_UNUSED, uint32_t seq ATTR_UNUSED, uint64_t min_modseq ATTR_UNUSED) {} const struct mail_index_record * mail_index_lookup(struct mail_index_view *view ATTR_UNUSED, uint32_t seq) { i_assert(seq < N_ELEMENTS(recs)); return &recs[seq]; } struct mail_index_record * mail_index_transaction_lookup(struct mail_index_transaction *t ATTR_UNUSED, uint32_t seq) { i_assert(seq < N_ELEMENTS(recs)); return &recs[seq]; } uint64_t mail_index_modseq_lookup(struct mail_index_view *view ATTR_UNUSED, uint32_t seq) { i_assert(seq < N_ELEMENTS(modseqs)); return modseqs[seq]; } uint64_t mail_index_modseq_get_highest(struct mail_index_view *view ATTR_UNUSED) { return modseqs[0]; } #define MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far) \ for (unsigned int sofar = 0; sofar < n_so_far; sofar++) \ mail_index_transaction_finish_so_far(t); \ mail_index_transaction_finish(t); static void test_mail_index_transaction_finish_flag_updates(unsigned int n_so_far) { struct mail_index_transaction *t; const struct mail_index_flag_update *updates; struct mail_index_flag_update u; unsigned int count; t = t_new(struct mail_index_transaction, 1); t->drop_unnecessary_flag_updates = TRUE; i_zero(&u); u.add_flags = MAIL_SEEN; u.remove_flags = MAIL_DRAFT; test_begin(t_strdup_printf("mail index transaction finish flag updates n_so_far=%u", n_so_far)); /* test fast path: all changed */ t_array_init(&t->updates, 10); u.uid1 = 1; u.uid2 = 2; array_append(&t->updates, &u, 1); u.uid1 = 4; u.uid2 = 5; array_append(&t->updates, &u, 1); MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); updates = array_get(&t->updates, &count); test_assert(count == 4); test_assert(updates[0].uid1 == 1*2 && updates[0].uid2 == 1*2); test_assert(updates[1].uid1 == 2*2 && updates[1].uid2 == 2*2); test_assert(updates[2].uid1 == 4*2 && updates[2].uid2 == 4*2); test_assert(updates[3].uid1 == 5*2 && updates[3].uid2 == 5*2); /* nothing changed */ t_array_init(&t->updates, 10); u.uid1 = 1; u.uid2 = 2; array_append(&t->updates, &u, 1); u.uid1 = 4; u.uid2 = 5; array_append(&t->updates, &u, 1); recs[1].flags = MAIL_SEEN; recs[2].flags = MAIL_SEEN; recs[4].flags = MAIL_SEEN; recs[5].flags = MAIL_SEEN; MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); test_assert(!array_is_created(&t->updates)); /* some changes */ t_array_init(&t->updates, 10); u.uid1 = 2; u.uid2 = 3; array_append(&t->updates, &u, 1); u.uid1 = 5; u.uid2 = 6; array_append(&t->updates, &u, 1); MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); updates = array_get(&t->updates, &count); test_assert(count == 2); test_assert(updates[0].uid1 == 3*2 && updates[0].uid2 == 3*2); test_assert(updates[1].uid1 == 6*2 && updates[1].uid2 == 6*2); test_end(); } static void test_mail_index_transaction_finish_check_conflicts(unsigned int n_so_far) { struct mail_index_transaction *t; const struct seq_range *conflicts; ARRAY_TYPE(seq_range) conflict_seqs = ARRAY_INIT; unsigned int count; t = t_new(struct mail_index_transaction, 1); t->view = t_new(struct mail_index_view, 1); t->min_flagupdate_seq = 5; t->max_flagupdate_seq = 8; t->conflict_seqs = &conflict_seqs; modseqs[0] = 1234; modseqs[5] = 5; modseqs[6] = 8; modseqs[7] = 6; modseqs[8] = 7; test_begin(t_strdup_printf("mail index transaction finish check conflicts n_so_far=%u", n_so_far)); /* fast path: no conflicts */ t->max_modseq = 1234; MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); test_assert(!array_is_created(&conflict_seqs)); /* try some conflicts */ t->max_modseq = 6; MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); i_assert(array_is_created(&conflict_seqs)); conflicts = array_get(&conflict_seqs, &count); test_assert(count == 2); test_assert(conflicts[0].seq1 == 6 && conflicts[0].seq2 == 6); test_assert(conflicts[1].seq1 == 8 && conflicts[1].seq2 == 8); test_end(); array_free(t->conflict_seqs); } static void test_mail_index_transaction_finish_modseq_updates(unsigned int n_so_far) { struct mail_index_transaction *t; const struct mail_transaction_modseq_update *ups; struct mail_transaction_modseq_update u; unsigned int count; t = t_new(struct mail_index_transaction, 1); test_begin(t_strdup_printf("mail index transaction finish modseq updates n_so_far=%u", n_so_far)); t_array_init(&t->modseq_updates, 10); u.modseq_low32 = 1234567890; u.modseq_high32 = 987654321; u.uid = 1; array_append(&t->modseq_updates, &u, 1); u.modseq_low32++; u.modseq_high32++; u.uid = 2; array_append(&t->modseq_updates, &u, 1); u.modseq_low32++; u.modseq_high32++; u.uid = 5; array_append(&t->modseq_updates, &u, 1); u.modseq_low32 = 1234; u.modseq_high32 = 0; u.uid = 2; array_append(&t->modseq_updates, &u, 1); MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); ups = array_get(&t->modseq_updates, &count); test_assert(count == 4); test_assert(ups[0].uid == 1*2); test_assert(ups[0].modseq_low32 == 1234567890 && ups[0].modseq_high32 == 987654321); test_assert(ups[1].uid == 2*2); test_assert(ups[1].modseq_low32 == 1234567891 && ups[1].modseq_high32 == 987654322); test_assert(ups[2].uid == 5*2); test_assert(ups[2].modseq_low32 == 1234567892 && ups[2].modseq_high32 == 987654323); test_assert(ups[3].uid == 2*2); test_assert(ups[3].modseq_low32 == 1234 && ups[3].modseq_high32 == 0); test_end(); } static void test_mail_index_transaction_finish_expunges(unsigned int n_so_far) { struct mail_index_transaction *t; guid_128_t guid1, guid2, guid3; const struct mail_transaction_expunge_guid *expunges; struct mail_transaction_expunge_guid expunge; unsigned int i, count; for (i = 0; i < sizeof(guid2); i++) { guid1[i] = i + 1; guid2[i] = i ^ 0xff; guid3[i] = i + 0x80; } recs[1].uid = 12; recs[2].uid = 15; recs[3].uid = 18; t = t_new(struct mail_index_transaction, 1); t->expunges_nonsorted = TRUE; test_begin(t_strdup_printf("mail index transaction finish expunges n_so_far=%u", n_so_far)); t_array_init(&t->expunges, 3); expunge.uid = 2; memcpy(expunge.guid_128, guid2, sizeof(expunge.guid_128)); array_append(&t->expunges, &expunge, 1); array_append(&t->expunges, &expunge, 1); expunge.uid = 1; memcpy(expunge.guid_128, guid1, sizeof(expunge.guid_128)); array_append(&t->expunges, &expunge, 1); array_append(&t->expunges, &expunge, 1); expunge.uid = 3; memcpy(expunge.guid_128, guid3, sizeof(expunge.guid_128)); array_append(&t->expunges, &expunge, 1); array_append(&t->expunges, &expunge, 1); MAIL_INDEX_TRANSACTION_FINISH(t, n_so_far); expunges = array_get(&t->expunges, &count); test_assert(count == 3); test_assert(expunges[0].uid == 12); test_assert(memcmp(expunges[0].guid_128, guid1, sizeof(guid1)) == 0); test_assert(expunges[1].uid == 15); test_assert(memcmp(expunges[1].guid_128, guid2, sizeof(guid2)) == 0); test_assert(expunges[2].uid == 18); test_assert(memcmp(expunges[2].guid_128, guid3, sizeof(guid3)) == 0); test_end(); } static void test_state_reset(void) { memset(recs, 0, sizeof(recs)); memset(modseqs, 0, sizeof(modseqs)); for (unsigned int n = 1; n < N_ELEMENTS(recs); n++) recs[n].uid = n*2; } static void test_mail_index_transaction_finish(void) { void (*const test_finish_functions[])(unsigned int) = { test_mail_index_transaction_finish_flag_updates, test_mail_index_transaction_finish_check_conflicts, test_mail_index_transaction_finish_modseq_updates, test_mail_index_transaction_finish_expunges, }; unsigned int i, j; for (i = 0; i < N_ELEMENTS(test_finish_functions); i++) { for (j = 0; j < 3; j++) { test_state_reset(); test_finish_functions[i](j); } } } int main(void) { static void (*test_functions[])(void) = { test_mail_index_transaction_finish, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-strmap.c0000644000175000017500000010477013165463624016511 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "istream.h" #include "ostream.h" #include "file-lock.h" #include "file-dotlock.h" #include "crc32.h" #include "safe-mkstemp.h" #include "str.h" #include "mail-index-private.h" #include "mail-index-strmap.h" #include struct mail_index_strmap { struct mail_index *index; char *path; int fd; struct istream *input; struct file_lock *file_lock; struct dotlock *dotlock; struct dotlock_settings dotlock_settings; }; struct mail_index_strmap_view { struct mail_index_strmap *strmap; struct mail_index_view *view; ARRAY_TYPE(mail_index_strmap_rec) recs; ARRAY(uint32_t) recs_crc32; struct hash2_table *hash; mail_index_strmap_key_cmp_t *key_compare; mail_index_strmap_rec_cmp_t *rec_compare; mail_index_strmap_remap_t *remap_cb; void *cb_context; uoff_t last_read_block_offset; uint32_t last_read_uid; uint32_t last_added_uid; uint32_t total_ref_count; uint32_t last_ref_index; uint32_t next_str_idx; uint32_t lost_expunged_uid; unsigned int desynced:1; }; struct mail_index_strmap_read_context { struct mail_index_strmap_view *view; struct istream *input; uoff_t end_offset; uint32_t highest_str_idx; uint32_t uid_lookup_seq; uint32_t lost_expunged_uid; const unsigned char *data, *end, *str_idx_base; struct mail_index_strmap_rec rec; uint32_t next_ref_index; unsigned int rec_size; unsigned int too_large_uids:1; }; struct mail_index_strmap_view_sync { struct mail_index_strmap_view *view; }; struct mail_index_strmap_hash_key { const char *str; uint32_t crc32; }; /* number of bytes required to store one string idx */ #define STRMAP_FILE_STRIDX_SIZE (sizeof(uint32_t)*2) /* renumber the string indexes when highest string idx becomes larger than *STRMAP_FILE_MAX_STRIDX_MULTIPLIER */ #define STRMAP_FILE_MAX_STRIDX_MULTIPLIER 2 #define STRIDX_MUST_RENUMBER(highest_idx, n_unique_indexes) \ (highest_idx > n_unique_indexes * STRMAP_FILE_MAX_STRIDX_MULTIPLIER) #define MAIL_INDEX_STRMAP_TIMEOUT_SECS 10 static const struct dotlock_settings default_dotlock_settings = { .timeout = MAIL_INDEX_STRMAP_TIMEOUT_SECS, .stale_timeout = 30 }; struct mail_index_strmap * mail_index_strmap_init(struct mail_index *index, const char *suffix) { struct mail_index_strmap *strmap; i_assert(index->open_count > 0); strmap = i_new(struct mail_index_strmap, 1); strmap->index = index; strmap->path = i_strconcat(index->filepath, suffix, NULL); strmap->fd = -1; strmap->dotlock_settings = default_dotlock_settings; strmap->dotlock_settings.use_excl_lock = (index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0; strmap->dotlock_settings.nfs_flush = (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0; return strmap; } static bool mail_index_strmap_read_rec_next(struct mail_index_strmap_read_context *ctx, uint32_t *crc32_r); static void mail_index_strmap_set_syscall_error(struct mail_index_strmap *strmap, const char *function) { i_assert(function != NULL); if (ENOSPACE(errno)) { strmap->index->nodiskspace = TRUE; if ((strmap->index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) == 0) return; } mail_index_set_error(strmap->index, "%s failed with strmap index file %s: %m", function, strmap->path); } static void mail_index_strmap_close(struct mail_index_strmap *strmap) { if (strmap->file_lock != NULL) file_lock_free(&strmap->file_lock); else if (strmap->dotlock != NULL) file_dotlock_delete(&strmap->dotlock); if (strmap->fd != -1) { if (close(strmap->fd) < 0) mail_index_strmap_set_syscall_error(strmap, "close()"); strmap->fd = -1; } if (strmap->input != NULL) i_stream_unref(&strmap->input); } void mail_index_strmap_deinit(struct mail_index_strmap **_strmap) { struct mail_index_strmap *strmap = *_strmap; *_strmap = NULL; mail_index_strmap_close(strmap); i_free(strmap->path); i_free(strmap); } static unsigned int mail_index_strmap_hash_key(const void *_key) { const struct mail_index_strmap_hash_key *key = _key; return key->crc32; } static bool mail_index_strmap_hash_cmp(const void *_key, const void *_value, void *context) { const struct mail_index_strmap_hash_key *key = _key; const struct mail_index_strmap_rec *rec = _value; struct mail_index_strmap_view *view = context; return view->key_compare(key->str, rec, view->cb_context); } struct mail_index_strmap_view * mail_index_strmap_view_open(struct mail_index_strmap *strmap, struct mail_index_view *idx_view, mail_index_strmap_key_cmp_t *key_compare_cb, mail_index_strmap_rec_cmp_t *rec_compare_cb, mail_index_strmap_remap_t *remap_cb, void *context, const ARRAY_TYPE(mail_index_strmap_rec) **recs_r, const struct hash2_table **hash_r) { struct mail_index_strmap_view *view; view = i_new(struct mail_index_strmap_view, 1); view->strmap = strmap; view->view = idx_view; view->key_compare = key_compare_cb; view->rec_compare = rec_compare_cb; view->remap_cb = remap_cb; view->cb_context = context; view->next_str_idx = 1; i_array_init(&view->recs, 64); i_array_init(&view->recs_crc32, 64); view->hash = hash2_create(0, sizeof(struct mail_index_strmap_rec), mail_index_strmap_hash_key, mail_index_strmap_hash_cmp, view); *recs_r = &view->recs; *hash_r = view->hash; return view; } void mail_index_strmap_view_close(struct mail_index_strmap_view **_view) { struct mail_index_strmap_view *view = *_view; *_view = NULL; array_free(&view->recs); array_free(&view->recs_crc32); hash2_destroy(&view->hash); i_free(view); } uint32_t mail_index_strmap_view_get_highest_idx(struct mail_index_strmap_view *view) { return view->next_str_idx-1; } static void mail_index_strmap_view_reset(struct mail_index_strmap_view *view) { view->remap_cb(NULL, 0, 0, view->cb_context); array_clear(&view->recs); array_clear(&view->recs_crc32); hash2_clear(view->hash); view->last_added_uid = 0; view->lost_expunged_uid = 0; view->desynced = FALSE; } void mail_index_strmap_view_set_corrupted(struct mail_index_strmap_view *view) { mail_index_set_error(view->strmap->index, "Corrupted strmap index file: %s", view->strmap->path); i_unlink(view->strmap->path); mail_index_strmap_close(view->strmap); mail_index_strmap_view_reset(view); } static int mail_index_strmap_open(struct mail_index_strmap_view *view) { struct mail_index_strmap *strmap = view->strmap; const struct mail_index_header *idx_hdr; struct mail_index_strmap_header hdr; const unsigned char *data; size_t size; int ret; i_assert(strmap->fd == -1); strmap->fd = open(strmap->path, O_RDWR); if (strmap->fd == -1) { if (errno == ENOENT) return 0; mail_index_strmap_set_syscall_error(strmap, "open()"); return -1; } strmap->input = i_stream_create_fd(strmap->fd, (size_t)-1, FALSE); ret = i_stream_read_data(strmap->input, &data, &size, sizeof(hdr)-1); if (ret <= 0) { if (ret < 0) { mail_index_strmap_set_syscall_error(strmap, "read()"); mail_index_strmap_close(strmap); } else { i_assert(ret == 0); mail_index_strmap_view_set_corrupted(view); } return ret; } memcpy(&hdr, data, sizeof(hdr)); idx_hdr = mail_index_get_header(view->view); if (hdr.version != MAIL_INDEX_STRMAP_VERSION || hdr.uid_validity != idx_hdr->uid_validity) { /* need to rebuild. if we already had something in the strmap, we can keep it. */ i_unlink(strmap->path); mail_index_strmap_close(strmap); return 0; } /* we'll read the entire file from the beginning */ view->last_added_uid = 0; view->last_read_uid = 0; view->total_ref_count = 0; view->last_read_block_offset = sizeof(struct mail_index_strmap_header); view->next_str_idx = 1; mail_index_strmap_view_reset(view); return 0; } static bool mail_index_strmap_need_reopen(struct mail_index_strmap *strmap) { struct stat st1, st2; /* FIXME: nfs flush */ if (fstat(strmap->fd, &st1) < 0) { if (!ESTALE_FSTAT(errno)) mail_index_strmap_set_syscall_error(strmap, "fstat()"); return TRUE; } if (stat(strmap->path, &st2) < 0) { mail_index_strmap_set_syscall_error(strmap, "stat()"); return TRUE; } return st1.st_ino != st2.st_ino || !CMP_DEV_T(st1.st_dev, st2.st_dev); } static int mail_index_strmap_refresh(struct mail_index_strmap_view *view) { uint32_t seq; if (MAIL_INDEX_IS_IN_MEMORY(view->strmap->index)) return -1; if (view->strmap->fd != -1) { if (!mail_index_strmap_need_reopen(view->strmap)) { if (view->lost_expunged_uid != 0) { /* last read failed because view had a message that didn't exist in the strmap (because it was expunged by another session). if the message still isn't expunged in this view, just continue using the current strmap. */ if (mail_index_lookup_seq(view->view, view->lost_expunged_uid, &seq)) return -1; } else if (view->desynced) { /* our view isn't synced with the disk, we can't read strmap without first resetting the view */ } else { i_stream_sync(view->strmap->input); return 0; } } mail_index_strmap_close(view->strmap); } return mail_index_strmap_open(view); } static int mail_index_strmap_read_packed(struct mail_index_strmap_read_context *ctx, uint32_t *num_r) { const unsigned char *data; const uint8_t *bytes, *p, *end; size_t size; int ret; ret = i_stream_read_data(ctx->input, &data, &size, sizeof(*num_r) - 1); if (ret <= 0) return ret; if (ctx->input->v_offset + size > ctx->end_offset) size = ctx->end_offset - ctx->input->v_offset; bytes = p = (const uint8_t *)data; end = bytes + size; if (mail_index_unpack_num(&p, end, num_r) < 0) return -1; i_stream_skip(ctx->input, p - bytes); return 1; } static int mail_index_strmap_uid_exists(struct mail_index_strmap_read_context *ctx, uint32_t uid) { const struct mail_index_record *rec; i_assert(ctx->uid_lookup_seq > 0); if (ctx->uid_lookup_seq > ctx->view->view->map->hdr.messages_count) { if (uid >= ctx->view->view->map->hdr.next_uid) { /* thread index has larger UIDs than what we've seen in our view. we'll have to read them again later when we know about them */ ctx->too_large_uids = TRUE; } return 0; } rec = MAIL_INDEX_REC_AT_SEQ(ctx->view->view->map, ctx->uid_lookup_seq); if (rec->uid == uid) { ctx->uid_lookup_seq++; return 1; } else if (rec->uid > uid) { return 0; } else { /* record that exists in index is missing from strmap. see if it's because the strmap is corrupted or because our current view is a bit stale and the message has already been expunged. */ mail_index_refresh(ctx->view->view->index); if (mail_index_is_expunged(ctx->view->view, ctx->uid_lookup_seq)) ctx->lost_expunged_uid = rec->uid; return -1; } } static int mail_index_strmap_read_rec_first(struct mail_index_strmap_read_context *ctx, uint32_t *crc32_r) { size_t size; uint32_t n, i, count, str_idx; int ret; /* *count *count where n = 0 -> count=1 (only Message-ID:) n = 1 -> count=2 (Message-ID: + In-Reply-To:) n = 2+ -> count=n (Message-ID: + References:) */ if (mail_index_strmap_read_packed(ctx, &n) <= 0) return -1; count = n < 2 ? n + 1 : n; ctx->view->total_ref_count += count; ctx->rec_size = count * (sizeof(ctx->rec.str_idx) + sizeof(*crc32_r)); ret = mail_index_strmap_uid_exists(ctx, ctx->rec.uid); if (ret < 0) return -1; if (i_stream_read_data(ctx->view->strmap->input, &ctx->data, &size, ctx->rec_size - 1) <= 0) return -1; ctx->str_idx_base = ctx->data + count * sizeof(uint32_t); if (ret == 0) { /* this message has already been expunged, ignore it. update highest string indexes anyway. */ for (i = 0; i < count; i++) { memcpy(&str_idx, ctx->str_idx_base, sizeof(str_idx)); if (ctx->highest_str_idx < str_idx) ctx->highest_str_idx = str_idx; ctx->str_idx_base += sizeof(str_idx); } i_stream_skip(ctx->view->strmap->input, ctx->rec_size); return 0; } /* everything exists. save it. FIXME: these ref_index values are thread index specific, perhaps something more generic should be used some day */ ctx->end = ctx->data + count * sizeof(*crc32_r); ctx->next_ref_index = 0; if (!mail_index_strmap_read_rec_next(ctx, crc32_r)) i_unreached(); ctx->next_ref_index = n == 1 ? 1 : 2; return 1; } static bool mail_index_strmap_read_rec_next(struct mail_index_strmap_read_context *ctx, uint32_t *crc32_r) { if (ctx->data == ctx->end) { i_stream_skip(ctx->view->strmap->input, ctx->rec_size); return FALSE; } /* FIXME: str_idx could be stored as packed relative values (first relative to highest_idx, the rest relative to the previous str_idx) */ /* read the record contents */ memcpy(&ctx->rec.str_idx, ctx->str_idx_base, sizeof(ctx->rec.str_idx)); memcpy(crc32_r, ctx->data, sizeof(*crc32_r)); ctx->rec.ref_index = ctx->next_ref_index++; if (ctx->highest_str_idx < ctx->rec.str_idx) ctx->highest_str_idx = ctx->rec.str_idx; /* get to the next record */ ctx->data += sizeof(*crc32_r); ctx->str_idx_base += sizeof(ctx->rec.str_idx); return TRUE; } static int strmap_read_block_init(struct mail_index_strmap_view *view, struct mail_index_strmap_read_context *ctx) { struct mail_index_strmap *strmap = view->strmap; const unsigned char *data; size_t size; uint32_t block_size, seq1, seq2; int ret; if (view->last_read_uid + 1 >= view->view->map->hdr.next_uid) { /* come back later when we know about the new UIDs */ return 0; } i_zero(ctx); ret = i_stream_read_data(strmap->input, &data, &size, sizeof(block_size)-1); if (ret <= 0) { if (strmap->input->stream_errno == 0) { /* no new data */ return 0; } mail_index_strmap_set_syscall_error(strmap, "read()"); return -1; } memcpy(&block_size, data, sizeof(block_size)); block_size = mail_index_offset_to_uint32(block_size) >> 2; if (block_size == 0) { /* the rest of the file is either not written, or the previous write didn't finish */ return 0; } i_stream_skip(strmap->input, sizeof(block_size)); ctx->view = view; ctx->input = strmap->input; ctx->end_offset = strmap->input->v_offset + block_size; if (ctx->end_offset < strmap->input->v_offset) { /* block size too large */ mail_index_strmap_view_set_corrupted(view); return -1; } ctx->rec.uid = view->last_read_uid + 1; /* FIXME: when reading multiple blocks we shouldn't have to calculate this every time */ if (!mail_index_lookup_seq_range(view->view, ctx->rec.uid, (uint32_t)-1, &seq1, &seq2)) seq1 = mail_index_view_get_messages_count(view->view) + 1; ctx->uid_lookup_seq = seq1; return 1; } static int strmap_read_block_next(struct mail_index_strmap_read_context *ctx, uint32_t *crc32_r) { uint32_t uid_diff; int ret; if (mail_index_strmap_read_rec_next(ctx, crc32_r)) return 1; /* get next UID */ do { if (ctx->input->v_offset == ctx->end_offset) { /* this block is done */ return 0; } if (mail_index_strmap_read_packed(ctx, &uid_diff) <= 0) return -1; ctx->rec.uid += uid_diff; ret = mail_index_strmap_read_rec_first(ctx, crc32_r); } while (ret == 0); return ret; } static int strmap_read_block_deinit(struct mail_index_strmap_read_context *ctx, int ret, bool update_block_offset) { struct mail_index_strmap_view *view = ctx->view; struct mail_index_strmap *strmap = view->strmap; if (ctx->highest_str_idx > view->total_ref_count) { /* if all string indexes are unique, highest_str_index equals total_ref_count. otherwise it's always lower. */ mail_index_set_error(strmap->index, "Corrupted strmap index file %s: " "String indexes too high " "(highest=%u max=%u)", strmap->path, ctx->highest_str_idx, view->total_ref_count); mail_index_strmap_view_set_corrupted(view); return -1; } if (ctx->lost_expunged_uid != 0) { /* our view contained a message that had since been expunged. */ i_assert(ret < 0); view->lost_expunged_uid = ctx->lost_expunged_uid; } else if (ret < 0) { if (strmap->input->stream_errno != 0) mail_index_strmap_set_syscall_error(strmap, "read()"); else mail_index_strmap_view_set_corrupted(view); return -1; } else if (update_block_offset && !ctx->too_large_uids) { view->last_read_block_offset = strmap->input->v_offset; view->last_read_uid = ctx->rec.uid; } if (view->next_str_idx <= ctx->highest_str_idx) view->next_str_idx = ctx->highest_str_idx + 1; return ret; } static bool strmap_view_sync_handle_conflict(struct mail_index_strmap_read_context *ctx, const struct mail_index_strmap_rec *hash_rec, struct hash2_iter *iter) { uint32_t seq; /* hopefully it's a message that has since been expunged */ if (!mail_index_lookup_seq(ctx->view->view, hash_rec->uid, &seq)) { /* message is no longer in our view. remove it completely. */ hash2_remove_iter(ctx->view->hash, iter); return TRUE; } if (mail_index_is_expunged(ctx->view->view, seq)) { /* it's quite likely a conflict. we may not be able to verify it, so just assume it is. nothing breaks even if we guess wrong, the performance just suffers a bit. */ return FALSE; } /* 0 means "doesn't match", which is the only acceptable case */ return ctx->view->rec_compare(&ctx->rec, hash_rec, ctx->view->cb_context) == 0; } static int strmap_view_sync_block_check_conflicts(struct mail_index_strmap_read_context *ctx, uint32_t crc32) { struct mail_index_strmap_rec *hash_rec; struct hash2_iter iter; if (crc32 == 0) { /* unique string - there are no conflicts */ return 0; } /* check for conflicting string indexes. they may happen if 1) msgid exists only for a message X that has been expunged 2) another process doesn't see X, but sees msgid for another message and writes it using a new string index 3) if we still see X, we now see the same msgid with two string indexes. if we detect such a conflict, we can't continue using the strmap index until X has been expunged. */ i_zero(&iter); while ((hash_rec = hash2_iterate(ctx->view->hash, crc32, &iter)) != NULL && hash_rec->str_idx != ctx->rec.str_idx) { /* CRC32 matches, but string index doesn't */ if (!strmap_view_sync_handle_conflict(ctx, hash_rec, &iter)) { ctx->lost_expunged_uid = hash_rec->uid; return -1; } } return 0; } static int mail_index_strmap_view_sync_block(struct mail_index_strmap_read_context *ctx) { struct mail_index_strmap_rec *hash_rec; uint32_t crc32, prev_uid = 0; int ret; while ((ret = strmap_read_block_next(ctx, &crc32)) > 0) { if (ctx->rec.uid <= ctx->view->last_added_uid) { if (ctx->rec.uid < ctx->view->last_added_uid || prev_uid != ctx->rec.uid) { /* we've already added this */ continue; } } prev_uid = ctx->rec.uid; if (strmap_view_sync_block_check_conflicts(ctx, crc32) < 0) { ret = -1; break; } ctx->view->last_added_uid = ctx->rec.uid; /* add the record to records array */ array_append(&ctx->view->recs, &ctx->rec, 1); array_append(&ctx->view->recs_crc32, &crc32, 1); /* add a separate copy of the record to hash */ hash_rec = hash2_insert_hash(ctx->view->hash, crc32); memcpy(hash_rec, &ctx->rec, sizeof(*hash_rec)); } return strmap_read_block_deinit(ctx, ret, TRUE); } struct mail_index_strmap_view_sync * mail_index_strmap_view_sync_init(struct mail_index_strmap_view *view, uint32_t *last_uid_r) { struct mail_index_strmap_view_sync *sync; struct mail_index_strmap_read_context ctx; int ret; sync = i_new(struct mail_index_strmap_view_sync, 1); sync->view = view; if (mail_index_strmap_refresh(view) < 0) { /* reading the strmap failed - just ignore and do this in-memory based on whatever we knew last */ } else if (view->strmap->input != NULL) { i_stream_seek(view->strmap->input, view->last_read_block_offset); while ((ret = strmap_read_block_init(view, &ctx)) > 0) { if (mail_index_strmap_view_sync_block(&ctx) < 0) { ret = -1; break; } if (ctx.too_large_uids) break; } if (ret < 0) { /* something failed - we can still use the strmap as far as we managed to read it, but our view is now out of sync */ view->desynced = TRUE; } else { i_assert(view->lost_expunged_uid == 0); } } *last_uid_r = view->last_added_uid; return sync; } static inline uint32_t crc32_str_nonzero(const char *str) { /* we'll flip the bits because of a bug in our old crc32 code. this keeps the index format backwards compatible with the new fixed crc32 code. */ uint32_t value = crc32_str(str) ^ 0xffffffffU; return value == 0 ? 1 : value; } void mail_index_strmap_view_sync_add(struct mail_index_strmap_view_sync *sync, uint32_t uid, uint32_t ref_index, const char *key) { struct mail_index_strmap_view *view = sync->view; struct mail_index_strmap_rec *rec, *old_rec; struct mail_index_strmap_hash_key hash_key; uint32_t str_idx; i_assert(uid > view->last_added_uid || (uid == view->last_added_uid && ref_index > view->last_ref_index)); hash_key.str = key; hash_key.crc32 = crc32_str_nonzero(key); old_rec = hash2_lookup(view->hash, &hash_key); if (old_rec != NULL) { /* The string already exists, use the same unique idx */ str_idx = old_rec->str_idx; } else { /* Newly seen string, assign a new unique idx to it */ str_idx = view->next_str_idx++; } i_assert(str_idx != 0); rec = hash2_insert(view->hash, &hash_key); rec->uid = uid; rec->ref_index = ref_index; rec->str_idx = str_idx; array_append(&view->recs, rec, 1); array_append(&view->recs_crc32, &hash_key.crc32, 1); view->last_added_uid = uid; view->last_ref_index = ref_index; } void mail_index_strmap_view_sync_add_unique(struct mail_index_strmap_view_sync *sync, uint32_t uid, uint32_t ref_index) { struct mail_index_strmap_view *view = sync->view; struct mail_index_strmap_rec rec; i_assert(uid > view->last_added_uid || (uid == view->last_added_uid && ref_index > view->last_ref_index)); i_zero(&rec); rec.uid = uid; rec.ref_index = ref_index; rec.str_idx = view->next_str_idx++; array_append(&view->recs, &rec, 1); array_append_zero(&view->recs_crc32); view->last_added_uid = uid; view->last_ref_index = ref_index; } static void mail_index_strmap_zero_terminate(struct mail_index_strmap_view *view) { /* zero-terminate the records array */ array_append_zero(&view->recs); array_delete(&view->recs, array_count(&view->recs)-1, 1); } static void mail_index_strmap_view_renumber(struct mail_index_strmap_view *view) { struct mail_index_strmap_read_context ctx; struct mail_index_strmap_rec *recs, *hash_rec; uint32_t prev_uid, str_idx, *recs_crc32, *renumber_map; unsigned int i, dest, count, count2; int ret; i_zero(&ctx); ctx.view = view; ctx.uid_lookup_seq = 1; /* create a map of old -> new index and remove records of expunged messages */ renumber_map = i_new(uint32_t, view->next_str_idx); str_idx = 0; prev_uid = 0; recs = array_get_modifiable(&view->recs, &count); recs_crc32 = array_get_modifiable(&view->recs_crc32, &count2); i_assert(count == count2); for (i = dest = 0; i < count; ) { if (prev_uid != recs[i].uid) { /* see if this record should be removed */ prev_uid = recs[i].uid; ret = mail_index_strmap_uid_exists(&ctx, prev_uid); i_assert(ret >= 0); if (ret == 0) { /* message expunged */ do { i++; } while (i < count && recs[i].uid == prev_uid); continue; } } i_assert(recs[i].str_idx < view->next_str_idx); if (renumber_map[recs[i].str_idx] == 0) renumber_map[recs[i].str_idx] = ++str_idx; if (i != dest) { recs[dest] = recs[i]; recs_crc32[dest] = recs_crc32[i]; } i++; dest++; } i_assert(renumber_map[0] == 0); array_delete(&view->recs, dest, i-dest); array_delete(&view->recs_crc32, dest, i-dest); mail_index_strmap_zero_terminate(view); /* notify caller of the renumbering */ i_assert(str_idx <= view->next_str_idx); view->remap_cb(renumber_map, view->next_str_idx, str_idx + 1, view->cb_context); /* renumber the indexes in-place and recreate the hash */ recs = array_get_modifiable(&view->recs, &count); hash2_clear(view->hash); for (i = 0; i < count; i++) { recs[i].str_idx = renumber_map[recs[i].str_idx]; hash_rec = hash2_insert_hash(view->hash, recs_crc32[i]); memcpy(hash_rec, &recs[i], sizeof(*hash_rec)); } /* update the new next_str_idx only after remapping */ view->next_str_idx = str_idx + 1; i_free(renumber_map); } static void mail_index_strmap_write_block(struct mail_index_strmap_view *view, struct ostream *output, unsigned int i, uint32_t base_uid) { const struct mail_index_strmap_rec *recs; const uint32_t *crc32; unsigned int j, n, count, count2, uid_rec_count; uint32_t block_size; uint8_t *p, packed[MAIL_INDEX_PACK_MAX_SIZE*2]; uoff_t block_offset, end_offset; /* skip over the block size for now, we don't know it yet */ block_offset = output->offset; block_size = 0; o_stream_nsend(output, &block_size, sizeof(block_size)); /* write records */ recs = array_get(&view->recs, &count); crc32 = array_get(&view->recs_crc32, &count2); i_assert(count == count2); while (i < count) { /* @UNSAFE: */ p = packed; mail_index_pack_num(&p, recs[i].uid - base_uid); base_uid = recs[i].uid; /* find how many records belong to this UID */ uid_rec_count = 1; for (j = i + 1; j < count; j++) { if (recs[j].uid != base_uid) break; uid_rec_count++; } view->total_ref_count += uid_rec_count; /* *count *count - FIXME: thread index specific code */ i_assert(recs[i].ref_index == 0); if (uid_rec_count == 1) { /* Only Message-ID: header */ n = 0; } else if (recs[i+1].ref_index == 1) { /* In-Reply-To: header */ n = 1; i_assert(uid_rec_count == 2); } else { /* References: header */ n = uid_rec_count; i_assert(recs[i+1].ref_index == 2); } mail_index_pack_num(&p, n); o_stream_nsend(output, packed, p-packed); for (j = 0; j < uid_rec_count; j++) o_stream_nsend(output, &crc32[i+j], sizeof(crc32[i+j])); for (j = 0; j < uid_rec_count; j++) { i_assert(j < 2 || recs[i+j].ref_index == j+1); o_stream_nsend(output, &recs[i+j].str_idx, sizeof(recs[i+j].str_idx)); } i += uid_rec_count; } /* we know the block size now - write it */ block_size = output->offset - (block_offset + sizeof(block_size)); block_size = mail_index_uint32_to_offset(block_size << 2); i_assert(block_size != 0); end_offset = output->offset; (void)o_stream_seek(output, block_offset); o_stream_nsend(output, &block_size, sizeof(block_size)); (void)o_stream_seek(output, end_offset); if (output->last_failed_errno != 0) return; i_assert(view->last_added_uid == recs[count-1].uid); view->last_read_uid = recs[count-1].uid; view->last_read_block_offset = output->offset; } static void mail_index_strmap_recreate_write(struct mail_index_strmap_view *view, struct ostream *output) { const struct mail_index_header *idx_hdr; struct mail_index_strmap_header hdr; idx_hdr = mail_index_get_header(view->view); /* write header */ i_zero(&hdr); hdr.version = MAIL_INDEX_STRMAP_VERSION; hdr.uid_validity = idx_hdr->uid_validity; o_stream_nsend(output, &hdr, sizeof(hdr)); view->total_ref_count = 0; mail_index_strmap_write_block(view, output, 0, 1); } static int mail_index_strmap_recreate(struct mail_index_strmap_view *view) { struct mail_index_strmap *strmap = view->strmap; string_t *str; struct ostream *output; const char *temp_path; int fd, ret = 0; if (array_count(&view->recs) == 0) { /* everything expunged - just unlink the existing index */ if (unlink(strmap->path) < 0 && errno != ENOENT) mail_index_strmap_set_syscall_error(strmap, "unlink()"); return 0; } str = t_str_new(256); str_append(str, strmap->path); fd = safe_mkstemp_hostpid_group(str, view->view->index->mode, view->view->index->gid, view->view->index->gid_origin); temp_path = str_c(str); if (fd == -1) { mail_index_set_error(strmap->index, "safe_mkstemp_hostpid(%s) failed: %m", temp_path); return -1; } output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(output); mail_index_strmap_recreate_write(view, output); if (o_stream_nfinish(output) < 0) { mail_index_set_error(strmap->index, "write(%s) failed: %s", temp_path, o_stream_get_error(output)); ret = -1; } o_stream_destroy(&output); if (close(fd) < 0) { mail_index_set_error(strmap->index, "close(%s) failed: %m", temp_path); ret = -1; } else if (ret == 0 && rename(temp_path, strmap->path) < 0) { mail_index_set_error(strmap->index, "rename(%s, %s) failed: %m", temp_path, strmap->path); ret = -1; } if (ret < 0) i_unlink(temp_path); return ret; } static int mail_index_strmap_lock(struct mail_index_strmap *strmap) { unsigned int timeout_secs; int ret; i_assert(strmap->fd != -1); if (strmap->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) { i_assert(strmap->file_lock == NULL); timeout_secs = I_MIN(MAIL_INDEX_STRMAP_TIMEOUT_SECS, strmap->index->max_lock_timeout_secs); ret = file_wait_lock(strmap->fd, strmap->path, F_WRLCK, strmap->index->lock_method, timeout_secs, &strmap->file_lock); if (ret <= 0) { mail_index_strmap_set_syscall_error(strmap, "file_wait_lock()"); } } else { i_assert(strmap->dotlock == NULL); ret = file_dotlock_create(&strmap->dotlock_settings, strmap->path, 0, &strmap->dotlock); if (ret <= 0) { mail_index_strmap_set_syscall_error(strmap, "file_dotlock_create()"); } } return ret; } static void mail_index_strmap_unlock(struct mail_index_strmap *strmap) { if (strmap->file_lock != NULL) file_unlock(&strmap->file_lock); else if (strmap->dotlock != NULL) file_dotlock_delete(&strmap->dotlock); } static int strmap_rec_cmp(const uint32_t *uid, const struct mail_index_strmap_rec *rec) { return *uid < rec->uid ? -1 : (*uid > rec->uid ? 1 : 0); } static int mail_index_strmap_write_append(struct mail_index_strmap_view *view) { struct mail_index_strmap_read_context ctx; const struct mail_index_strmap_rec *old_recs; unsigned int i, old_count; struct ostream *output; uint32_t crc32, next_uid; bool full_block; int ret; /* Check first if another process had written new records to the file. If there are any, hopefully they're the same as what we would be writing. There are two problematic cases when messages have been expunged recently: 1) The file contains UIDs that we don't have. This means the string indexes won't be compatible anymore, so we'll have to renumber ours to match the ones in the strmap file. Currently we don't bother handling 1) case. If indexes don't match what we have, we just don't write anything. 2) We have UIDs that don't exist in the file. We can't simply skip those records, because other records may have pointers to them using different string indexes than we have. Even if we renumbered those, future appends by other processes might cause the same problem (they see the string for the first time and assign it a new index, but we already have internally given it another index). So the only sensible choice is to write nothing and hope that the message goes away soon. */ next_uid = view->last_read_uid + 1; (void)array_bsearch_insert_pos(&view->recs, &next_uid, strmap_rec_cmp, &i); old_recs = array_get(&view->recs, &old_count); if (i < old_count) { while (i > 0 && old_recs[i-1].uid == old_recs[i].uid) i--; } i_stream_sync(view->strmap->input); i_stream_seek(view->strmap->input, view->last_read_block_offset); full_block = TRUE; ret = 0; while (i < old_count && (ret = strmap_read_block_init(view, &ctx)) > 0) { while ((ret = strmap_read_block_next(&ctx, &crc32)) > 0) { if (ctx.rec.uid != old_recs[i].uid || ctx.rec.str_idx != old_recs[i].str_idx) { /* mismatch */ if (ctx.rec.uid > old_recs[i].uid) { /* 1) case */ ctx.lost_expunged_uid = ctx.rec.uid; } else if (ctx.rec.uid < old_recs[i].uid) { /* 2) case */ ctx.lost_expunged_uid = old_recs[i].uid; } else { /* string index mismatch, shouldn't happen */ } ret = -1; break; } if (++i == old_count) { full_block = FALSE; break; } } if (strmap_read_block_deinit(&ctx, ret, full_block) < 0) { ret = -1; break; } } if (ret < 0) return -1; if (i == old_count) { /* nothing new to write */ return 0; } i_assert(full_block); i_assert(old_recs[i].uid > view->last_read_uid); /* write the new records */ output = o_stream_create_fd(view->strmap->fd, 0, FALSE); (void)o_stream_seek(output, view->last_read_block_offset); o_stream_cork(output); mail_index_strmap_write_block(view, output, i, view->last_read_uid + 1); if (o_stream_nfinish(output) < 0) { mail_index_strmap_set_syscall_error(view->strmap, "write()"); ret = -1; } o_stream_destroy(&output); return ret; } static int mail_index_strmap_write(struct mail_index_strmap_view *view) { int ret; /* FIXME: this renumbering doesn't work well when running for a long time since records aren't removed from hash often enough */ if (STRIDX_MUST_RENUMBER(view->next_str_idx - 1, hash2_count(view->hash))) { mail_index_strmap_view_renumber(view); if (!MAIL_INDEX_IS_IN_MEMORY(view->strmap->index)) { if (mail_index_strmap_recreate(view) < 0) { view->desynced = TRUE; return -1; } } return 0; } if (MAIL_INDEX_IS_IN_MEMORY(view->strmap->index) || view->desynced) return 0; if (view->strmap->fd == -1) { /* initial file creation */ if (mail_index_strmap_recreate(view) < 0) { view->desynced = TRUE; return -1; } return 0; } /* append the new records to the strmap file */ if (mail_index_strmap_lock(view->strmap) <= 0) { /* timeout / error */ ret = -1; } else if (mail_index_strmap_need_reopen(view->strmap)) { /* the file was already recreated - leave the syncing as it is for now and let the next sync re-read the file. */ ret = 0; } else { ret = mail_index_strmap_write_append(view); } mail_index_strmap_unlock(view->strmap); if (ret < 0) view->desynced = TRUE; return ret; } void mail_index_strmap_view_sync_commit(struct mail_index_strmap_view_sync **_sync) { struct mail_index_strmap_view_sync *sync = *_sync; struct mail_index_strmap_view *view = sync->view; *_sync = NULL; i_free(sync); (void)mail_index_strmap_write(view); mail_index_strmap_zero_terminate(view); /* zero-terminate the records array */ array_append_zero(&view->recs); array_delete(&view->recs, array_count(&view->recs)-1, 1); } void mail_index_strmap_view_sync_rollback(struct mail_index_strmap_view_sync **_sync) { struct mail_index_strmap_view_sync *sync = *_sync; *_sync = NULL; mail_index_strmap_view_reset(sync->view); mail_index_strmap_zero_terminate(sync->view); i_free(sync); } dovecot-2.2.33.2/src/lib-index/mail-index-alloc-cache.h0000644000175000017500000000103113123174404017312 00000000000000#ifndef MAIL_INDEX_ALLOC_CACHE_H #define MAIL_INDEX_ALLOC_CACHE_H /* If using in-memory indexes, give index_dir=NULL. */ struct mail_index * ATTR_NULL(1, 2) mail_index_alloc_cache_get(const char *mailbox_path, const char *index_dir, const char *prefix); void mail_index_alloc_cache_unref(struct mail_index **index); void mail_index_alloc_cache_destroy_unrefed(void); /* internal: */ void mail_index_alloc_cache_index_opened(struct mail_index *index); void mail_index_alloc_cache_index_closing(struct mail_index *index); #endif dovecot-2.2.33.2/src/lib-index/mail-index-sync-keywords.c0000644000175000017500000002374713123174404020015 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "mail-index-modseq.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-transaction-log.h" static bool keyword_lookup(struct mail_index_sync_map_ctx *ctx, const char *keyword_name, unsigned int *idx_r) { struct mail_index_map *map = ctx->view->map; const unsigned int *idx_map; unsigned int i, count, keyword_idx; if (array_is_created(&map->keyword_idx_map) && mail_index_keyword_lookup(ctx->view->index, keyword_name, &keyword_idx)) { /* FIXME: slow. maybe create index -> file mapping as well */ idx_map = array_get(&map->keyword_idx_map, &count); for (i = 0; i < count; i++) { if (idx_map[i] == keyword_idx) { *idx_r = i; return TRUE; } } } return FALSE; } static buffer_t * keywords_get_header_buf(struct mail_index_map *map, const struct mail_index_ext *ext, unsigned int new_count, unsigned int *keywords_count_r, size_t *rec_offset_r, size_t *name_offset_root_r, size_t *name_offset_r) { buffer_t *buf; const struct mail_index_keyword_header *kw_hdr; const struct mail_index_keyword_header_rec *kw_rec; const char *name; struct mail_index_keyword_header new_kw_hdr; uint32_t offset; kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); kw_rec = (const void *)(kw_hdr + 1); name = (const char *)(kw_rec + kw_hdr->keywords_count); if (kw_hdr->keywords_count == 0) return NULL; i_assert((size_t)(name - (const char *)kw_hdr) < ext->hdr_size); new_kw_hdr = *kw_hdr; new_kw_hdr.keywords_count += new_count; *keywords_count_r = new_kw_hdr.keywords_count; offset = kw_rec[kw_hdr->keywords_count-1].name_offset; offset += strlen(name + offset) + 1; buf = buffer_create_dynamic(pool_datastack_create(), 512); buffer_append(buf, &new_kw_hdr, sizeof(new_kw_hdr)); buffer_append(buf, kw_rec, sizeof(*kw_rec) * kw_hdr->keywords_count); *rec_offset_r = buf->used; buffer_write(buf, buf->used + sizeof(*kw_rec) * new_count, name, offset); *name_offset_root_r = buf->used; *name_offset_r = offset; return buf; } static void keywords_ext_register(struct mail_index_sync_map_ctx *ctx, uint32_t ext_map_idx, uint32_t reset_id, uint32_t hdr_size, uint32_t keywords_count) { buffer_t ext_intro_buf; struct mail_transaction_ext_intro *u; unsigned char ext_intro_data[sizeof(*u) + sizeof(MAIL_INDEX_EXT_KEYWORDS)-1]; i_assert(keywords_count > 0); buffer_create_from_data(&ext_intro_buf, ext_intro_data, sizeof(ext_intro_data)); u = buffer_append_space_unsafe(&ext_intro_buf, sizeof(*u)); u->ext_id = ext_map_idx; u->reset_id = reset_id; u->hdr_size = hdr_size; u->record_size = (keywords_count + CHAR_BIT - 1) / CHAR_BIT; if ((u->record_size % 4) != 0) { /* since we aren't properly aligned anyway, reserve one extra byte for future */ u->record_size++; } u->record_align = 1; if (ext_map_idx == (uint32_t)-1) { u->name_size = strlen(MAIL_INDEX_EXT_KEYWORDS); buffer_append(&ext_intro_buf, MAIL_INDEX_EXT_KEYWORDS, u->name_size); } ctx->internal_update = TRUE; if (mail_index_sync_ext_intro(ctx, u) < 0) i_panic("Keyword extension growing failed"); ctx->internal_update = FALSE; } static void keywords_header_add(struct mail_index_sync_map_ctx *ctx, const char *keyword_name, unsigned int *keyword_idx_r) { struct mail_index_map *map; const struct mail_index_ext *ext = NULL; struct mail_index_keyword_header *kw_hdr; struct mail_index_keyword_header_rec kw_rec; uint32_t ext_map_idx; buffer_t *buf = NULL; size_t keyword_len, rec_offset, name_offset, name_offset_root; unsigned int keywords_count; /* if we crash in the middle of writing the header, the keywords are more or less corrupted. avoid that by making sure the header is updated atomically. */ map = mail_index_sync_get_atomic_map(ctx); if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) ext_map_idx = (uint32_t)-1; else { /* update existing header */ ext = array_idx(&map->extensions, ext_map_idx); buf = keywords_get_header_buf(map, ext, 1, &keywords_count, &rec_offset, &name_offset_root, &name_offset); } if (buf == NULL) { /* create new / replace broken header */ const unsigned int initial_keywords_count = 1; buf = buffer_create_dynamic(pool_datastack_create(), 512); kw_hdr = buffer_append_space_unsafe(buf, sizeof(*kw_hdr)); kw_hdr->keywords_count = initial_keywords_count; keywords_count = kw_hdr->keywords_count; rec_offset = buf->used; name_offset_root = rec_offset + initial_keywords_count * sizeof(kw_rec); name_offset = 0; } /* add the keyword */ i_zero(&kw_rec); kw_rec.name_offset = name_offset; keyword_len = strlen(keyword_name) + 1; buffer_write(buf, rec_offset, &kw_rec, sizeof(kw_rec)); buffer_write(buf, name_offset_root, keyword_name, keyword_len); rec_offset += sizeof(kw_rec); kw_rec.name_offset += keyword_len; name_offset_root += keyword_len; if ((buf->used % 4) != 0) buffer_append_zero(buf, 4 - (buf->used % 4)); if (ext == NULL || buf->used > ext->hdr_size || (uint32_t)ext->record_size * CHAR_BIT < keywords_count) { /* if we need to grow the buffer, add some padding */ buffer_append_zero(buf, 128); keywords_ext_register(ctx, ext_map_idx, ext == NULL ? 0 : ext->reset_id, buf->used, keywords_count); /* map may have changed */ map = ctx->view->map; if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) i_unreached(); ext = array_idx(&map->extensions, ext_map_idx); i_assert(ext->hdr_size == buf->used); } buffer_copy(map->hdr_copy_buf, ext->hdr_offset, buf, 0, buf->used); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); if (mail_index_map_parse_keywords(map) < 0) i_panic("Keyword update corrupted keywords header"); *keyword_idx_r = keywords_count - 1; i_assert(*keyword_idx_r / CHAR_BIT < ext->record_size); } static int keywords_update_records(struct mail_index_sync_map_ctx *ctx, const struct mail_index_ext *ext, unsigned int keyword_idx, enum modify_type type, uint32_t uid1, uint32_t uid2) { struct mail_index_view *view = ctx->view; struct mail_index_record *rec; unsigned char *data, data_mask; unsigned int data_offset; uint32_t seq1, seq2; i_assert(keyword_idx != UINT_MAX); if (!mail_index_lookup_seq_range(view, uid1, uid2, &seq1, &seq2)) return 1; mail_index_modseq_update_keyword(ctx->modseq_ctx, keyword_idx, seq1, seq2); data_offset = keyword_idx / CHAR_BIT; data_mask = 1 << (keyword_idx % CHAR_BIT); i_assert(data_offset < ext->record_size); data_offset += ext->record_offset; i_assert(data_offset >= MAIL_INDEX_RECORD_MIN_SIZE); switch (type) { case MODIFY_ADD: for (; seq1 <= seq2; seq1++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq1); data = PTR_OFFSET(rec, data_offset); *data |= data_mask; } break; case MODIFY_REMOVE: data_mask = ~data_mask; for (; seq1 <= seq2; seq1++) { rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq1); data = PTR_OFFSET(rec, data_offset); *data &= data_mask; } break; default: i_unreached(); } return 1; } int mail_index_sync_keywords(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const struct mail_transaction_keyword_update *rec) { struct mail_index_view *view = ctx->view; const char *keyword_name; const struct mail_index_ext *ext; const uint32_t *uid, *end; uint32_t seqset_offset, ext_map_idx; unsigned int keyword_idx; int ret; i_assert(rec->name_size > 0); seqset_offset = sizeof(*rec) + rec->name_size; if ((seqset_offset % 4) != 0) seqset_offset += 4 - (seqset_offset % 4); i_assert(seqset_offset < hdr->size); uid = CONST_PTR_OFFSET(rec, seqset_offset); end = CONST_PTR_OFFSET(rec, hdr->size); keyword_name = t_strndup(rec + 1, rec->name_size); if (!keyword_lookup(ctx, keyword_name, &keyword_idx)) keywords_header_add(ctx, keyword_name, &keyword_idx); /* if the keyword wasn't found, the "keywords" extension was created. if it was found, the record size should already be correct, but in case it isn't just fix it ourself. */ if (!mail_index_map_lookup_ext(view->map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) i_unreached(); ext = array_idx(&view->map->extensions, ext_map_idx); if (keyword_idx / CHAR_BIT >= ext->record_size) { if (rec->modify_type == MODIFY_REMOVE) { /* nothing to do */ return 1; } /* grow the record size */ keywords_ext_register(ctx, ext_map_idx, ext->reset_id, ext->hdr_size, array_count(&view->map->keyword_idx_map)); if (!mail_index_map_lookup_ext(view->map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) i_unreached(); ext = array_idx(&view->map->extensions, ext_map_idx); } while (uid+2 <= end) { ret = keywords_update_records(ctx, ext, keyword_idx, rec->modify_type, uid[0], uid[1]); if (ret <= 0) return ret; uid += 2; } return 1; } int mail_index_sync_keywords_reset(struct mail_index_sync_map_ctx *ctx, const struct mail_transaction_header *hdr, const struct mail_transaction_keyword_reset *r) { struct mail_index_map *map = ctx->view->map; struct mail_index_record *rec; const struct mail_index_ext *ext; const struct mail_transaction_keyword_reset *end; uint32_t ext_map_idx, seq1, seq2; if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) { /* nothing to do */ return 1; } ext = array_idx(&map->extensions, ext_map_idx); end = CONST_PTR_OFFSET(r, hdr->size); for (; r != end; r++) { if (!mail_index_lookup_seq_range(ctx->view, r->uid1, r->uid2, &seq1, &seq2)) continue; mail_index_modseq_reset_keywords(ctx->modseq_ctx, seq1, seq2); for (; seq1 <= seq2; seq1++) { rec = MAIL_INDEX_REC_AT_SEQ(map, seq1); memset(PTR_OFFSET(rec, ext->record_offset), 0, ext->record_size); } } return 1; } dovecot-2.2.33.2/src/lib-index/test-mail-transaction-log-file.c0000644000175000017500000004140413165463624021066 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #define TEST_LOG_VERSION MAIL_TRANSACTION_LOG_VERSION_FULL(1, 3) #define INITIAL_MODSEQ 100 struct update_modseq_test { enum mail_transaction_type type; unsigned int version; #define NOUPDATE (INITIAL_MODSEQ) #define UPDATE (INITIAL_MODSEQ+1) uint64_t expected_modseq; unsigned int count; union { const struct mail_transaction_flag_update *flag_update; const struct mail_transaction_modseq_update *modseq_update; } v; } update_modseq_tests[] = { /* expunges: increase modseq */ { MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT | MAIL_TRANSACTION_EXTERNAL, TEST_LOG_VERSION, UPDATE, 1, { } }, { MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT | MAIL_TRANSACTION_EXTERNAL, TEST_LOG_VERSION, UPDATE, 1, { } }, /* expunges: don't increase modseq */ { MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXTERNAL, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXTERNAL, TEST_LOG_VERSION, NOUPDATE, 1, { } }, /* flag changes: don't increase modseq */ { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = 0 } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_BACKEND } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .remove_flags = MAIL_INDEX_MAIL_FLAG_BACKEND } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_DIRTY } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .remove_flags = MAIL_INDEX_MAIL_FLAG_DIRTY } } } }, /* flag changes: increase modseq */ { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_SEEN } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .remove_flags = MAIL_SEEN } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_SEEN | MAIL_INDEX_MAIL_FLAG_BACKEND } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_SEEN | MAIL_INDEX_MAIL_FLAG_DIRTY } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .remove_flags = MAIL_SEEN | MAIL_INDEX_MAIL_FLAG_BACKEND } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .remove_flags = MAIL_SEEN | MAIL_INDEX_MAIL_FLAG_DIRTY } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 2, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_DIRTY }, { .uid1 = 3, .uid2 = 4, .add_flags = MAIL_SEEN } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = 0, .modseq_inc_flag = 1 } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, TEST_LOG_VERSION, UPDATE, 2, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_DIRTY }, { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_DIRTY, .modseq_inc_flag = 1 } } } }, /* flag changes: increase modseq with old version */ { MAIL_TRANSACTION_FLAG_UPDATE, MAIL_TRANSACTION_LOG_VERSION_FULL(1, 2), UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_BACKEND } } } }, { MAIL_TRANSACTION_FLAG_UPDATE, MAIL_TRANSACTION_LOG_VERSION_FULL(1, 2), UPDATE, 1, { .flag_update = (const struct mail_transaction_flag_update[]) { { .uid1 = 1, .uid2 = 2, .add_flags = MAIL_INDEX_MAIL_FLAG_DIRTY } } } }, /* modseq updates: don't increase modseq */ { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 50, .modseq_high32 = 0 } } } }, { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 100, .modseq_high32 = 0 } } } }, /* modseq updates: increase modseq */ { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, 500, 1, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 500, .modseq_high32 = 0 } } } }, { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, 500, 2, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 50, .modseq_high32 = 0 }, { .uid = 1, .modseq_low32 = 500, .modseq_high32 = 0 } } } }, { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, 500, 1, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 500, .modseq_high32 = 0 }, { .uid = 1, .modseq_low32 = 200, .modseq_high32 = 0 } } } }, { MAIL_TRANSACTION_MODSEQ_UPDATE, TEST_LOG_VERSION, (uint64_t)4294967346, 1, { .modseq_update = (const struct mail_transaction_modseq_update[]) { { .uid = 1, .modseq_low32 = 50, .modseq_high32 = 1 } } } }, /* appends, keyword changes, attribute changes: increase modseq */ { MAIL_TRANSACTION_APPEND, TEST_LOG_VERSION, UPDATE, 1, { } }, { MAIL_TRANSACTION_KEYWORD_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { } }, { MAIL_TRANSACTION_KEYWORD_RESET, TEST_LOG_VERSION, UPDATE, 1, { } }, { MAIL_TRANSACTION_ATTRIBUTE_UPDATE, TEST_LOG_VERSION, UPDATE, 1, { } }, /* others: don't increase modseq */ { MAIL_TRANSACTION_HEADER_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_HEADER_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_INTRO, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_RESET, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_HDR_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_REC_UPDATE, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_ATOMIC_INC, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_EXT_HDR_UPDATE32, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_INDEX_DELETED, TEST_LOG_VERSION, NOUPDATE, 1, { } }, { MAIL_TRANSACTION_INDEX_UNDELETED, TEST_LOG_VERSION, NOUPDATE, 1, { } }, }; static size_t update_modseq_test_get_size(const struct update_modseq_test *test) { enum mail_transaction_type type = test->type & MAIL_TRANSACTION_TYPE_MASK; if (type == (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT)) type = MAIL_TRANSACTION_EXPUNGE; if (type == (MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT)) type = MAIL_TRANSACTION_EXPUNGE_GUID; switch (type) { case MAIL_TRANSACTION_EXPUNGE: return sizeof(struct mail_transaction_expunge); case MAIL_TRANSACTION_EXPUNGE_GUID: return sizeof(struct mail_transaction_expunge_guid); case MAIL_TRANSACTION_APPEND: return sizeof(struct mail_index_record); case MAIL_TRANSACTION_KEYWORD_UPDATE: return sizeof(struct mail_transaction_keyword_update); case MAIL_TRANSACTION_KEYWORD_RESET: return sizeof(struct mail_transaction_keyword_reset); case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: return 4; case MAIL_TRANSACTION_FLAG_UPDATE: return sizeof(struct mail_transaction_flag_update); case MAIL_TRANSACTION_MODSEQ_UPDATE: return sizeof(struct mail_transaction_modseq_update); case MAIL_TRANSACTION_HEADER_UPDATE: case MAIL_TRANSACTION_EXT_INTRO: case MAIL_TRANSACTION_EXT_RESET: case MAIL_TRANSACTION_EXT_HDR_UPDATE: case MAIL_TRANSACTION_EXT_REC_UPDATE: case MAIL_TRANSACTION_EXT_ATOMIC_INC: case MAIL_TRANSACTION_EXT_HDR_UPDATE32: case MAIL_TRANSACTION_INDEX_DELETED: case MAIL_TRANSACTION_INDEX_UNDELETED: return 4; case MAIL_TRANSACTION_TYPE_MASK: case MAIL_TRANSACTION_BOUNDARY: case MAIL_TRANSACTION_EXPUNGE_PROT: case MAIL_TRANSACTION_EXTERNAL: case MAIL_TRANSACTION_SYNC: break; } i_unreached(); } static void test_mail_transaction_update_modseq(void) { struct mail_transaction_header hdr; unsigned char tempbuf[1024] = { 0 }; test_begin("mail_transaction_update_modseq()"); for (unsigned int i = 0; i < N_ELEMENTS(update_modseq_tests); i++) { const struct update_modseq_test *test = &update_modseq_tests[i]; const void *data = test->v.flag_update; uint64_t cur_modseq = INITIAL_MODSEQ; if (data == NULL) data = tempbuf; hdr.type = test->type; hdr.size = sizeof(hdr) + update_modseq_test_get_size(test) * test->count; hdr.size = mail_index_uint32_to_offset(hdr.size); mail_transaction_update_modseq(&hdr, data, &cur_modseq, test->version); test_assert_idx(cur_modseq >= INITIAL_MODSEQ, i); test_assert_idx(test->expected_modseq == cur_modseq, i); } test_end(); } static struct mail_index *test_mail_index_open(void) { struct mail_index *index = mail_index_alloc(NULL, "test.dovecot.index"); test_assert(mail_index_open_or_create(index, MAIL_INDEX_OPEN_FLAG_CREATE) == 0); struct mail_index_view *view = mail_index_view_open(index); struct mail_index_transaction *trans = mail_index_transaction_begin(view, 0); uint32_t uid_validity = 1234; mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); test_assert(mail_index_transaction_commit(&trans) == 0); mail_index_view_close(&view); return index; } static void test_mail_transaction_log_file_modseq_offsets(void) { test_begin("mail_transaction_log_file_get_modseq_next_offset() and _get_highest_modseq_at()"); struct mail_index *index = test_mail_index_open(); struct mail_transaction_log_file *file = index->log->head; const unsigned int max_modseq = LOG_FILE_MODSEQ_CACHE_SIZE+2; uoff_t modseq_next_offset[max_modseq+1]; uoff_t modseq_alt_next_offset[max_modseq+1]; /* start with modseq=2, because modseq=1 is the initial state */ modseq_next_offset[1] = sizeof(struct mail_transaction_log_header); modseq_alt_next_offset[1] = sizeof(struct mail_transaction_log_header); for (uint64_t modseq = 2; modseq <= max_modseq; modseq++) { uint32_t seq; struct mail_index_view *view = mail_index_view_open(index); struct mail_index_transaction *trans = mail_index_transaction_begin(view, 0); mail_index_append(trans, modseq, &seq); test_assert(mail_index_transaction_commit(&trans) == 0); modseq_next_offset[modseq] = file->sync_offset; mail_index_view_close(&view); /* add a non-modseq updating change */ view = mail_index_view_open(index); trans = mail_index_transaction_begin(view, 0); mail_index_update_flags(trans, seq, MODIFY_ADD, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY); test_assert(mail_index_transaction_commit(&trans) == 0); mail_index_view_close(&view); modseq_alt_next_offset[modseq] = file->sync_offset; } /* mail_transaction_log_file_get_highest_modseq_at() is simultaneously tested and it can also add offsets to cache. The difference is that it adds the highest possible offset, while mail_transaction_log_file_get_modseq_next_offset() adds the lowest possible offset. So we'll need to allow both. */ #define MODSEQ_MATCH(modseq, next_offset) \ ((next_offset) == modseq_next_offset[modseq] || \ (next_offset) == modseq_alt_next_offset[modseq]) /* 1) mail_transaction_log_file_get_modseq_next_offset() tests */ uint64_t modseq; uoff_t next_offset; /* initial_modseq fast path */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 1, &next_offset) == 0); test_assert(next_offset == modseq_next_offset[1]); /* sync_highest_modseq fast path - it skips to sync_offset instead of using exactly the same max_modseq */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, max_modseq, &next_offset) == 0); test_assert(next_offset == file->sync_offset); test_assert(next_offset != modseq_next_offset[max_modseq]); /* update the offset for the random tests */ modseq_next_offset[max_modseq] = file->sync_offset; /* add to cache */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0); test_assert(MODSEQ_MATCH(2, next_offset)); /* get it from cache */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0); test_assert(MODSEQ_MATCH(2, next_offset)); /* get next value from cache */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 3, &next_offset) == 0); test_assert(MODSEQ_MATCH(3, next_offset)); /* get previous value from cache again */ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0); test_assert(MODSEQ_MATCH(2, next_offset)); /* do some random testing with cache */ for (unsigned int i = 0; i < LOG_FILE_MODSEQ_CACHE_SIZE*10; i++) { modseq = (rand() % max_modseq) + 1; test_assert(mail_transaction_log_file_get_modseq_next_offset(file, modseq, &next_offset) == 0); test_assert(MODSEQ_MATCH(modseq, next_offset)); } /* go through all modseqs - do this after randomness testing or modseq_alt_next_offset[] matching isn't triggered */ for (modseq = 1; modseq <= max_modseq; modseq++) { test_assert(mail_transaction_log_file_get_modseq_next_offset(file, modseq, &next_offset) == 0); test_assert(MODSEQ_MATCH(modseq, next_offset)); } /* 2) mail_transaction_log_file_get_highest_modseq_at() tests */ uint64_t modseq_at; const char *error; /* initial_offset */ test_assert(mail_transaction_log_file_get_highest_modseq_at(file, modseq_next_offset[1], &modseq, &error) == 0); test_assert(modseq == 1); /* sync_offset fast path */ test_assert(mail_transaction_log_file_get_highest_modseq_at(file, file->sync_offset, &modseq, &error) == 0); test_assert(modseq == max_modseq); /* do some random testing with cache */ for (unsigned int i = 0; i < LOG_FILE_MODSEQ_CACHE_SIZE*10; i++) { modseq = (rand() % max_modseq) + 1; test_assert(mail_transaction_log_file_get_highest_modseq_at(file, modseq_next_offset[modseq], &modseq_at, &error) == 0); test_assert(modseq_at == modseq); test_assert(mail_transaction_log_file_get_highest_modseq_at(file, modseq_alt_next_offset[modseq], &modseq_at, &error) == 0); test_assert(modseq_at == modseq); } /* go through all modseqs - do this after randomness testing or modseq_alt_next_offset[] matching isn't triggered */ for (modseq = 1; modseq <= max_modseq; modseq++) { test_assert(mail_transaction_log_file_get_highest_modseq_at(file, modseq_next_offset[modseq], &modseq_at, &error) == 0); test_assert(modseq_at == modseq); } mail_index_close(index); mail_index_free(&index); test_end(); } static void test_mail_transaction_log_file_get_modseq_next_offset_inconsistency(void) { test_begin("mail_transaction_log_file_get_modseq_next_offset() inconsistency"); struct mail_index *index = test_mail_index_open(); struct mail_transaction_log_file *file = index->log->head; uint32_t seq; /* add modseq=2 */ struct mail_index_view *view = mail_index_view_open(index); struct mail_index_transaction *trans = mail_index_transaction_begin(view, 0); mail_index_append(trans, 1, &seq); test_assert(mail_index_transaction_commit(&trans) == 0); mail_index_view_close(&view); /* emulate a broken mail_index_modseq_header header */ file->sync_highest_modseq = 3; uoff_t next_offset; test_expect_error_string("Transaction log modseq tracking is corrupted"); test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0); test_expect_no_more_errors(); test_assert(next_offset == file->sync_offset); mail_index_close(index); mail_index_free(&index); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_transaction_update_modseq, test_mail_transaction_log_file_modseq_offsets, test_mail_transaction_log_file_get_modseq_next_offset_inconsistency, NULL }; ioloop_time = 1; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-lock.c0000644000175000017500000000350013123174404016105 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ /* Locking should never fail or timeout. Exclusive locks must be kept as short time as possible. Shared locks can be long living, so if we can't get exclusive lock directly, we'll recreate the index. That means the shared lock holders can keep using the old file. lock_id is used to figure out if acquired lock is still valid. When index file is reopened, the lock_id can become invalid. It doesn't matter however, as no-one's going to modify the old file anymore. lock_id also tells us if we're referring to a shared or an exclusive lock. This allows us to drop back to shared locking once all exclusive locks are dropped. Shared locks have even numbers, exclusive locks have odd numbers. The number is increased by two every time the lock is dropped or index file is reopened. */ #include "lib.h" #include "nfs-workarounds.h" #include "mail-index-private.h" #define MAIL_INDEX_SHARED_LOCK_TIMEOUT 120 int mail_index_lock_fd(struct mail_index *index, const char *path, int fd, int lock_type, unsigned int timeout_secs, struct file_lock **lock_r) { if (fd == -1) { i_assert(MAIL_INDEX_IS_IN_MEMORY(index)); return 1; } return file_wait_lock(fd, path, lock_type, index->lock_method, timeout_secs, lock_r); } void mail_index_flush_read_cache(struct mail_index *index, const char *path, int fd, bool locked) { if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) == 0) return; /* Assume flock() is emulated with fcntl(), because that's how most OSes work nowadays. */ if (locked && (index->lock_method == FILE_LOCK_METHOD_FCNTL || index->lock_method == FILE_LOCK_METHOD_FLOCK)) { nfs_flush_read_cache_locked(path, fd); } else { nfs_flush_read_cache_unlocked(path, fd); } } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-private.h0000644000175000017500000001415213165463624021177 00000000000000#ifndef MAIL_INDEX_TRANSACTION_PRIVATE_H #define MAIL_INDEX_TRANSACTION_PRIVATE_H #include "seq-range-array.h" #include "mail-transaction-log.h" ARRAY_DEFINE_TYPE(seq_array_array, ARRAY_TYPE(seq_array)); struct mail_index_transaction_keyword_update { ARRAY_TYPE(seq_range) add_seq; ARRAY_TYPE(seq_range) remove_seq; }; struct mail_index_transaction_ext_hdr_update { size_t alloc_size; /* mask is in bytes, not bits */ unsigned char *mask; unsigned char *data; }; struct mail_index_transaction_vfuncs { void (*reset)(struct mail_index_transaction *t); int (*commit)(struct mail_index_transaction *t, struct mail_index_transaction_commit_result *result_r); void (*rollback)(struct mail_index_transaction *t); }; union mail_index_transaction_module_context { struct mail_index_transaction_vfuncs super; struct mail_index_module_register *reg; }; struct mail_index_flag_update { uint32_t uid1, uid2; uint16_t add_flags; uint16_t remove_flags; }; struct mail_index_transaction { struct mail_index_transaction *prev, *next; int refcount; enum mail_index_transaction_flags flags; struct mail_index_transaction_vfuncs v, *vlast; struct mail_index_view *view; struct mail_index_view *latest_view; /* NOTE: If you add anything new, remember to update mail_index_transaction_reset_v() to reset it. */ ARRAY(struct mail_index_record) appends; uint32_t first_new_seq, last_new_seq; uint32_t highest_append_uid; /* lowest/highest sequence that updates flags/keywords */ uint32_t min_flagupdate_seq, max_flagupdate_seq; ARRAY(struct mail_transaction_modseq_update) modseq_updates; ARRAY(struct mail_transaction_expunge_guid) expunges; ARRAY(struct mail_index_flag_update) updates; size_t last_update_idx; unsigned char pre_hdr_change[sizeof(struct mail_index_header)]; unsigned char pre_hdr_mask[sizeof(struct mail_index_header)]; unsigned char post_hdr_change[sizeof(struct mail_index_header)]; unsigned char post_hdr_mask[sizeof(struct mail_index_header)]; ARRAY(struct mail_index_transaction_ext_hdr_update) ext_hdr_updates; ARRAY_TYPE(seq_array_array) ext_rec_updates; ARRAY_TYPE(seq_array_array) ext_rec_atomics; ARRAY(struct mail_transaction_ext_intro) ext_resizes; ARRAY(struct mail_transaction_ext_reset) ext_resets; ARRAY(uint32_t) ext_reset_ids; ARRAY(uint32_t) ext_reset_atomic; ARRAY(struct mail_index_transaction_keyword_update) keyword_updates; buffer_t *attribute_updates; /* [+-][ps]key\0.. */ buffer_t *attribute_updates_suffix; /* [].. */ uint64_t min_highest_modseq; uint64_t max_modseq; ARRAY_TYPE(seq_range) *conflict_seqs; /* Module-specific contexts. */ ARRAY(union mail_index_transaction_module_context *) module_contexts; unsigned int no_appends:1; unsigned int sync_transaction:1; unsigned int appends_nonsorted:1; unsigned int expunges_nonsorted:1; unsigned int drop_unnecessary_flag_updates:1; unsigned int pre_hdr_changed:1; unsigned int post_hdr_changed:1; unsigned int reset:1; unsigned int index_deleted:1; unsigned int index_undeleted:1; unsigned int commit_deleted_index:1; unsigned int tail_offset_changed:1; /* non-extension updates. flag updates don't change this because they may be added and removed, so be sure to check that the updates array is non-empty also. */ unsigned int log_updates:1; /* extension updates */ unsigned int log_ext_updates:1; }; #define MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) \ ((t)->log_updates || (t)->log_ext_updates || \ (array_is_created(&(t)->updates) && array_count(&(t)->updates) > 0) || \ (t)->index_deleted || (t)->index_undeleted) typedef void hook_mail_index_transaction_created_t(struct mail_index_transaction *t); void mail_index_transaction_hook_register(hook_mail_index_transaction_created_t *hook); void mail_index_transaction_hook_unregister(hook_mail_index_transaction_created_t *hook); struct mail_index_record * mail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq); void mail_index_transaction_ref(struct mail_index_transaction *t); void mail_index_transaction_unref(struct mail_index_transaction **t); void mail_index_transaction_reset_v(struct mail_index_transaction *t); void mail_index_transaction_sort_appends(struct mail_index_transaction *t); void mail_index_transaction_sort_expunges(struct mail_index_transaction *t); uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t); void mail_index_transaction_set_log_updates(struct mail_index_transaction *t); void mail_index_update_day_headers(struct mail_index_transaction *t, time_t day_stamp); unsigned int mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, unsigned int left_idx, unsigned int right_idx, uint32_t seq); void mail_index_transaction_lookup_latest_keywords(struct mail_index_transaction *t, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keywords); bool mail_index_cancel_flag_updates(struct mail_index_transaction *t, uint32_t seq); bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t, uint32_t seq); /* As input the array's each element starts with struct seq_range where uid1..uid2 are actually sequences within the transaction view. This function changes the sequences into UIDs. If the transaction has any appends, they must have already been assigned UIDs. */ void mail_index_transaction_seq_range_to_uid(struct mail_index_transaction *t, ARRAY_TYPE(seq_range) *array); void mail_index_transaction_finish_so_far(struct mail_index_transaction *t); void mail_index_transaction_finish(struct mail_index_transaction *t); void mail_index_transaction_export(struct mail_index_transaction *t, struct mail_transaction_log_append_ctx *append_ctx); int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1, const struct mail_transaction_expunge_guid *e2); unsigned int mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, unsigned int left_idx, unsigned int right_idx, uint32_t seq); void mail_index_ext_using_reset_id(struct mail_index_transaction *t, uint32_t ext_id, uint32_t reset_id); #endif dovecot-2.2.33.2/src/lib-index/mail-cache-decisions.c0000644000175000017500000001507613123174404017104 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ /* Users can be divided to three groups: 1. Most users will use only a single IMAP client which caches everything locally. For these users it's quite pointless to do any kind of caching as it only wastes disk space. That might also mean more disk I/O. 2. Some users use multiple IMAP clients which cache everything locally. These could benefit from caching until all clients have fetched the data. After that it's useless. 3. Some clients don't do permanent local caching at all. For example Pine and webmails. These clients would benefit from caching everything. Some locally caching clients might also access some data from server again, such as when searching messages. They could benefit from caching only these fields. After thinking about these a while, I figured out that people who care about performance most will be using Dovecot optimized LDA anyway which updates the indexes/cache immediately. In that case even the first user group would benefit from caching the same way as second group. LDA reads the mail anyway, so it might as well extract some information about it and store them into cache. So, group 1. and 2. could be optimally implemented by keeping things cached only for a while. I thought a week would be good. When cache file is compressed, everything older than week will be dropped. But how to figure out if user is in group 3? One quite easy rule would be to see if client is accessing messages older than a week. But with only that rule we might have already dropped useful cached data. It's not very nice if we have to read and cache it twice. Most locally caching clients always fetch new messages (all but body) when they see them. They fetch them in ascending order. Noncaching clients might fetch messages in pretty much any order, as they usually don't fetch everything they can, only what's visible in screen. Some will use server side sorting/threading which also makes messages to be fetched in random order. Second rule would then be that if a session doesn't fetch messages in ascending order, the fetched field type will be permanently cached. So, we have three caching decisions: 1. Don't cache: Clients have never wanted the field 2. Cache temporarily: Clients want this only once 3. Cache permanently: Clients want this more than once Different mailboxes have different decisions. Different fields have different decisions. There are some problems, such as if a client accesses message older than a week, we can't know if user just started using a new client which is just filling its local cache for the first time. Or it might be a client user hasn't just used for over a week. In these cases we shouldn't have marked the field to be permanently cached. User might also switch clients from non-caching to caching. So we should re-evaluate our caching decisions from time to time. This is done by checking the above rules constantly and marking when was the last time the decision was right. If decision hasn't matched for two months, it's changed. I picked two months because people go to at least one month vacations where they might still be reading mails, but with different clients. */ #include "lib.h" #include "ioloop.h" #include "mail-cache-private.h" void mail_cache_decision_state_update(struct mail_cache_view *view, uint32_t seq, unsigned int field) { struct mail_cache *cache = view->cache; enum mail_cache_decision_type dec; const struct mail_index_header *hdr; uint32_t uid; i_assert(field < cache->fields_count); if (view->no_decision_updates) return; dec = cache->fields[field].field.decision; if (dec == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) { /* don't update last_used */ return; } if (ioloop_time - cache->fields[field].field.last_used > 3600*24) { /* update last_used about once a day */ cache->fields[field].field.last_used = (uint32_t)ioloop_time; if (cache->field_file_map[field] != (uint32_t)-1) cache->field_header_write_pending = TRUE; } if (dec != MAIL_CACHE_DECISION_TEMP) { /* a) forced decision b) not cached, mail_cache_decision_add() will handle this c) permanently cached already, okay. */ return; } mail_index_lookup_uid(view->view, seq, &uid); hdr = mail_index_get_header(view->view); /* see if we want to change decision from TEMP to YES */ if (uid < cache->fields[field].uid_highwater || uid < hdr->day_first_uid[7]) { /* a) nonordered access within this session. if client doesn't request messages in growing order, we assume it doesn't have a permanent local cache. b) accessing message older than one week. assume it's a client with no local cache. if it was just a new client generating the local cache for the first time, we'll drop back to TEMP within few months. */ cache->fields[field].field.decision = MAIL_CACHE_DECISION_YES; cache->fields[field].decision_dirty = TRUE; if (cache->field_file_map[field] != (uint32_t)-1) cache->field_header_write_pending = TRUE; } else { cache->fields[field].uid_highwater = uid; } } void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq, unsigned int field) { struct mail_cache *cache = view->cache; uint32_t uid; i_assert(field < cache->fields_count); if (MAIL_CACHE_IS_UNUSABLE(cache) || view->no_decision_updates) return; if (cache->fields[field].field.decision != MAIL_CACHE_DECISION_NO) { /* a) forced decision b) we're already caching it, so it just wasn't in cache */ return; } /* field used the first time */ cache->fields[field].field.decision = MAIL_CACHE_DECISION_TEMP; cache->fields[field].decision_dirty = TRUE; cache->field_header_write_pending = TRUE; mail_index_lookup_uid(view->view, seq, &uid); cache->fields[field].uid_highwater = uid; } void mail_cache_decisions_copy(struct mail_index_transaction *itrans, struct mail_cache *src, struct mail_cache *dst) { struct mail_cache_compress_lock *lock = NULL; if (mail_cache_open_and_verify(src) < 0 || MAIL_CACHE_IS_UNUSABLE(src)) return; unsigned int count = 0; struct mail_cache_field *fields = mail_cache_register_get_list(src, pool_datastack_create(), &count); i_assert(fields != NULL || count == 0); if (count > 0) mail_cache_register_fields(dst, fields, count); dst->field_header_write_pending = TRUE; (void)mail_cache_compress(dst, itrans, &lock); if (lock != NULL) mail_cache_compress_unlock(&lock); } dovecot-2.2.33.2/src/lib-index/mail-index-modseq.c0000644000175000017500000005057013147010713016454 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-transaction-log-private.h" #include "mail-index-private.h" #include "mail-index-sync-private.h" #include "mail-index-modseq.h" ARRAY_DEFINE_TYPE(modseqs, uint64_t); enum modseq_metadata_idx { /* must be in the same order as enum mail_flags */ METADATA_MODSEQ_IDX_ANSWERED = 0, METADATA_MODSEQ_IDX_FLAGGED, METADATA_MODSEQ_IDX_DELETED, METADATA_MODSEQ_IDX_SEEN, METADATA_MODSEQ_IDX_DRAFT, METADATA_MODSEQ_IDX_KEYWORD_START }; struct metadata_modseqs { ARRAY_TYPE(modseqs) modseqs; }; struct mail_index_map_modseq { /* indexes use enum modseq_metadata_idx */ ARRAY(struct metadata_modseqs) metadata_modseqs; }; struct mail_index_modseq_sync { struct mail_index_sync_map_ctx *sync_map_ctx; struct mail_index_view *view; struct mail_transaction_log_view *log_view; struct mail_index_map_modseq *mmap; }; void mail_index_modseq_init(struct mail_index *index) { index->modseq_ext_id = mail_index_ext_register(index, MAIL_INDEX_MODSEQ_EXT_NAME, sizeof(struct mail_index_modseq_header), sizeof(uint64_t), sizeof(uint64_t)); } static uint64_t mail_index_modseq_get_head(struct mail_index *index) { return index->log->head == NULL ? 1 : I_MAX(index->log->head->sync_highest_modseq, 1); } void mail_index_modseq_enable(struct mail_index *index) { struct mail_index_transaction *trans; struct mail_index_view *view; struct mail_index_modseq_header hdr; uint32_t ext_map_idx; if (index->modseqs_enabled) return; if (!mail_index_map_get_ext_idx(index->map, index->modseq_ext_id, &ext_map_idx)) { /* modseqs not enabled to the index yet, add them. */ view = mail_index_view_open(index); trans = mail_index_transaction_begin(view, 0); i_zero(&hdr); hdr.highest_modseq = mail_index_modseq_get_head(index); mail_index_update_header_ext(trans, index->modseq_ext_id, 0, &hdr, sizeof(hdr)); /* commit also refreshes the index, which syncs the modseqs */ (void)mail_index_transaction_commit(&trans); mail_index_view_close(&view); /* get the modseq extension to index map */ if (!mail_index_map_get_ext_idx(index->map, index->modseq_ext_id, &ext_map_idx)) { /* didn't work for some reason */ return; } } index->modseqs_enabled = TRUE; } bool mail_index_have_modseq_tracking(struct mail_index *index) { return mail_index_map_get_modseq_header(index->map) != NULL; } const struct mail_index_modseq_header * mail_index_map_get_modseq_header(struct mail_index_map *map) { const struct mail_index_ext *ext; uint32_t idx; if (!mail_index_map_get_ext_idx(map, map->index->modseq_ext_id, &idx)) return NULL; ext = array_idx(&map->extensions, idx); if (ext->hdr_size != sizeof(struct mail_index_modseq_header)) return NULL; return CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); } uint64_t mail_index_map_modseq_get_highest(struct mail_index_map *map) { const struct mail_index_modseq_header *modseq_hdr; modseq_hdr = mail_index_map_get_modseq_header(map); if (modseq_hdr != NULL && modseq_hdr->highest_modseq != 0) return modseq_hdr->highest_modseq; else { /* fallback to returning the log head. if modseqs aren't enabled, we return 0. */ return map->index->log->head == NULL ? 0 : map->index->log->head->sync_highest_modseq; } } uint64_t mail_index_modseq_get_highest(struct mail_index_view *view) { return mail_index_map_modseq_get_highest(view->map); } static struct mail_index_map_modseq * mail_index_map_modseq(struct mail_index_view *view) { struct mail_index_map_modseq *mmap = view->map->rec_map->modseq; uint32_t ext_map_idx; if (mmap != NULL) return mmap; /* don't start tracking until we've seen modseq extension intro */ if (!mail_index_map_get_ext_idx(view->map, view->index->modseq_ext_id, &ext_map_idx)) return NULL; mmap = i_new(struct mail_index_map_modseq, 1); i_array_init(&mmap->metadata_modseqs, METADATA_MODSEQ_IDX_KEYWORD_START + array_count(&view->index->keywords)); view->map->rec_map->modseq = mmap; return mmap; } uint64_t mail_index_modseq_lookup(struct mail_index_view *view, uint32_t seq) { struct mail_index_map_modseq *mmap = mail_index_map_modseq(view); struct mail_index_map *map; const struct mail_index_ext *ext; const struct mail_index_record *rec; const uint64_t *modseqp; uint32_t ext_map_idx; if (mmap == NULL) return mail_index_modseq_get_head(view->index); rec = mail_index_lookup_full(view, seq, &map); if (!mail_index_map_get_ext_idx(map, view->index->modseq_ext_id, &ext_map_idx)) { /* not enabled yet */ return mail_index_modseq_get_head(view->index); } ext = array_idx(&map->extensions, ext_map_idx); modseqp = CONST_PTR_OFFSET(rec, ext->record_offset); if (*modseqp == 0) { /* If we're here because we just enabled modseqs, we'll return the same modseq (initial highestmodseq) for all messages. The next sync will change these zeros to initial highestmodseq or higher. If we're here because a message got appended but modseq wasn't set (older Dovecot?), we'll again use the current highest modseq. This isn't exactly correct, but it gets fixed after the next sync and this situation shouldn't normally happen anyway. */ return mail_index_modseq_get_highest(view); } return *modseqp; } int mail_index_modseq_set(struct mail_index_view *view, uint32_t seq, uint64_t min_modseq) { struct mail_index_map_modseq *mmap = mail_index_map_modseq(view); const struct mail_index_ext *ext; struct mail_index_record *rec; uint64_t *modseqp; uint32_t ext_map_idx; if (mmap == NULL) return -1; rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq); if (!mail_index_map_get_ext_idx(view->map, view->index->modseq_ext_id, &ext_map_idx)) return -1; ext = array_idx(&view->map->extensions, ext_map_idx); modseqp = PTR_OFFSET(rec, ext->record_offset); if (*modseqp > min_modseq) return 0; else { *modseqp = min_modseq; return 1; } } static uint64_t modseq_idx_lookup(struct mail_index_map_modseq *mmap, unsigned int idx, uint32_t seq) { const struct metadata_modseqs *metadata; const uint64_t *modseqs; unsigned int count; metadata = array_get(&mmap->metadata_modseqs, &count); if (idx >= count || !array_is_created(&metadata[idx].modseqs)) return 0; modseqs = array_get(&metadata[idx].modseqs, &count); return seq > count ? 0 : modseqs[seq-1]; } uint64_t mail_index_modseq_lookup_flags(struct mail_index_view *view, enum mail_flags flags_mask, uint32_t seq) { struct mail_index_map_modseq *mmap = mail_index_map_modseq(view); unsigned int i; uint64_t modseq, highest_modseq = 0; if (mmap != NULL) { /* first try to find a specific match */ for (i = 0; i < METADATA_MODSEQ_IDX_KEYWORD_START; i++) { if ((flags_mask & (1 << i)) != 0) { modseq = modseq_idx_lookup(mmap, i, seq); if (highest_modseq < modseq) highest_modseq = modseq; } } } if (highest_modseq == 0) { /* no specific matches, fallback to using the highest */ highest_modseq = mail_index_modseq_lookup(view, seq); } return highest_modseq; } uint64_t mail_index_modseq_lookup_keywords(struct mail_index_view *view, const struct mail_keywords *keywords, uint32_t seq) { struct mail_index_map_modseq *mmap = mail_index_map_modseq(view); unsigned int i, metadata_idx; uint64_t modseq, highest_modseq = 0; if (mmap != NULL) { /* first try to find a specific match */ for (i = 0; i < keywords->count; i++) { metadata_idx = METADATA_MODSEQ_IDX_KEYWORD_START + keywords->idx[i]; modseq = modseq_idx_lookup(mmap, metadata_idx, seq); if (highest_modseq < modseq) highest_modseq = modseq; } } if (highest_modseq == 0) { /* no specific matches, fallback to using the highest */ highest_modseq = mail_index_modseq_lookup(view, seq); } return highest_modseq; } static void mail_index_modseq_update(struct mail_index_modseq_sync *ctx, uint64_t modseq, bool nonzeros, uint32_t seq1, uint32_t seq2) { const struct mail_index_ext *ext; struct mail_index_record *rec; uint32_t ext_map_idx; uint64_t *modseqp; if (!mail_index_map_get_ext_idx(ctx->view->map, ctx->view->index->modseq_ext_id, &ext_map_idx)) return; ext = array_idx(&ctx->view->map->extensions, ext_map_idx); for (; seq1 <= seq2; seq1++) { rec = MAIL_INDEX_REC_AT_SEQ(ctx->view->map, seq1); modseqp = PTR_OFFSET(rec, ext->record_offset); if (*modseqp == 0 || (nonzeros && *modseqp < modseq)) *modseqp = modseq; } } static bool mail_index_modseq_update_to_highest(struct mail_index_modseq_sync *ctx, uint32_t seq1, uint32_t seq2) { uint64_t modseq; if (ctx->mmap == NULL) return FALSE; modseq = mail_transaction_log_view_get_prev_modseq(ctx->log_view); mail_index_modseq_update(ctx, modseq, TRUE, seq1, seq2); return TRUE; } static void mail_index_modseq_update_old_rec(struct mail_index_modseq_sync *ctx, const struct mail_transaction_header *thdr, const void *tdata) { ARRAY_TYPE(seq_range) uids = ARRAY_INIT; const struct seq_range *rec; buffer_t uid_buf; unsigned int i, count; uint32_t seq1, seq2; switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_APPEND: { const struct mail_index_record *appends = tdata; count = thdr->size / sizeof(*appends); for (i = 0; i < count; i++) { if (mail_index_lookup_seq(ctx->view, appends[i].uid, &seq1)) { (void)mail_index_modseq_update_to_highest(ctx, seq1, seq1); } } return; } case MAIL_TRANSACTION_FLAG_UPDATE: { buffer_create_from_const_data(&uid_buf, tdata, thdr->size); array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_flag_update)); break; } case MAIL_TRANSACTION_KEYWORD_UPDATE: { const struct mail_transaction_keyword_update *rec = tdata; unsigned int seqset_offset; seqset_offset = sizeof(*rec) + rec->name_size; if ((seqset_offset % 4) != 0) seqset_offset += 4 - (seqset_offset % 4); buffer_create_from_const_data(&uid_buf, CONST_PTR_OFFSET(tdata, seqset_offset), thdr->size - seqset_offset); array_create_from_buffer(&uids, &uid_buf, sizeof(uint32_t)*2); break; } case MAIL_TRANSACTION_KEYWORD_RESET: buffer_create_from_const_data(&uid_buf, tdata, thdr->size); array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_keyword_reset)); break; case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: break; default: return; } /* update modseqs */ count = array_is_created(&uids) ? array_count(&uids) : 0; for (i = 0; i < count; i++) { rec = array_idx(&uids, i); if (mail_index_lookup_seq_range(ctx->view, rec->seq1, rec->seq2, &seq1, &seq2)) (void)mail_index_modseq_update_to_highest(ctx, seq1, seq2); } } static void mail_index_modseq_sync_init(struct mail_index_modseq_sync *ctx) { struct mail_index_map *map = ctx->view->map; const struct mail_index_ext *ext; const struct mail_index_modseq_header *hdr; const struct mail_transaction_header *thdr; const void *tdata; const char *reason; uint32_t ext_map_idx; uint32_t end_seq; uoff_t end_offset; uint64_t cur_modseq; bool reset; int ret; if (!mail_index_map_get_ext_idx(map, ctx->view->index->modseq_ext_id, &ext_map_idx)) i_unreached(); ext = array_idx(&map->extensions, ext_map_idx); /* get the current highest_modseq. don't change any modseq below it. */ hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); /* Scan logs for updates between ext_hdr.log_* .. view position. There are two reasons why there could be any: 1) We just enabled modseqs and we're filling the initial values. 2) A non-modseq-aware Dovecot version added new messages and wrote dovecot.index file. */ mail_transaction_log_view_get_prev_pos(ctx->view->log_view, &end_seq, &end_offset); if (end_seq < hdr->log_seq || (end_seq == hdr->log_seq && end_offset <= hdr->log_offset)) { /* modseqs are up to date */ return; } ctx->log_view = mail_transaction_log_view_open(ctx->view->index->log); ret = mail_transaction_log_view_set(ctx->log_view, I_MAX(1, hdr->log_seq), hdr->log_offset, end_seq, end_offset, &reset, &reason); if (ret <= 0) { /* missing files / error - try with only the last file */ ret = mail_transaction_log_view_set(ctx->log_view, end_seq, 0, end_seq, end_offset, &reset, &reason); /* since we don't know if we skipped some changes, set all modseqs to beginning of the latest file. */ cur_modseq = mail_transaction_log_view_get_prev_modseq( ctx->log_view); if (cur_modseq < hdr->highest_modseq) { /* should happen only when setting initial modseqs. we may already have returned highest_modseq as some messages' modseq value. don't shrink it. */ cur_modseq = hdr->highest_modseq; } mail_index_modseq_update(ctx, cur_modseq, TRUE, 1, map->hdr.messages_count); } else { /* we have all the logs. replace zero modseqs with the current highest modseq (we may have already returned it for them). */ mail_index_modseq_update(ctx, hdr->highest_modseq, FALSE, 1, map->hdr.messages_count); } if (ret > 0) { while (mail_transaction_log_view_next(ctx->log_view, &thdr, &tdata) > 0) { T_BEGIN { mail_index_modseq_update_old_rec(ctx, thdr, tdata); } T_END; } } mail_transaction_log_view_close(&ctx->log_view); } struct mail_index_modseq_sync * mail_index_modseq_sync_begin(struct mail_index_sync_map_ctx *sync_map_ctx) { struct mail_index_modseq_sync *ctx; ctx = i_new(struct mail_index_modseq_sync, 1); ctx->sync_map_ctx = sync_map_ctx; ctx->view = sync_map_ctx->view; ctx->mmap = mail_index_map_modseq(ctx->view); if (ctx->mmap != NULL) { mail_index_modseq_sync_init(ctx); ctx->log_view = ctx->view->log_view; } return ctx; } static void mail_index_modseq_update_header(struct mail_index_modseq_sync *ctx) { struct mail_index_view *view = ctx->view; struct mail_index_map *map = view->map; const struct mail_index_ext *ext; const struct mail_index_modseq_header *old_modseq_hdr; struct mail_index_modseq_header new_modseq_hdr; uint32_t ext_map_idx, log_seq; uoff_t log_offset; uint64_t highest_modseq; if (!mail_index_map_get_ext_idx(map, view->index->modseq_ext_id, &ext_map_idx)) return; mail_transaction_log_view_get_prev_pos(view->log_view, &log_seq, &log_offset); highest_modseq = mail_transaction_log_view_get_prev_modseq(view->log_view); ext = array_idx(&map->extensions, ext_map_idx); old_modseq_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset); if (old_modseq_hdr->log_seq < log_seq || (old_modseq_hdr->log_seq == log_seq && old_modseq_hdr->log_offset < log_offset)) { i_zero(&new_modseq_hdr); new_modseq_hdr.highest_modseq = highest_modseq; new_modseq_hdr.log_seq = log_seq; new_modseq_hdr.log_offset = log_offset; buffer_write(map->hdr_copy_buf, ext->hdr_offset, &new_modseq_hdr, sizeof(new_modseq_hdr)); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); } } void mail_index_modseq_sync_end(struct mail_index_modseq_sync **_ctx) { struct mail_index_modseq_sync *ctx = *_ctx; *_ctx = NULL; if (ctx->mmap != NULL) { i_assert(ctx->mmap == ctx->view->map->rec_map->modseq); mail_index_modseq_update_header(ctx); } i_free(ctx); } void mail_index_modseq_sync_map_replaced(struct mail_index_modseq_sync *ctx) { ctx->mmap = mail_index_map_modseq(ctx->view); } void mail_index_modseq_hdr_update(struct mail_index_modseq_sync *ctx) { if (ctx->mmap == NULL) { ctx->mmap = mail_index_map_modseq(ctx->view); i_assert(ctx->mmap != NULL); mail_index_modseq_sync_init(ctx); ctx->log_view = ctx->view->log_view; } } void mail_index_modseq_append(struct mail_index_modseq_sync *ctx, uint32_t seq) { (void)mail_index_modseq_update_to_highest(ctx, seq, seq); } void mail_index_modseq_expunge(struct mail_index_modseq_sync *ctx, uint32_t seq1, uint32_t seq2) { struct metadata_modseqs *metadata; if (ctx->mmap == NULL) return; seq1--; array_foreach_modifiable(&ctx->mmap->metadata_modseqs, metadata) { if (array_is_created(&metadata->modseqs)) array_delete(&metadata->modseqs, seq1, seq2-seq1); } } static void modseqs_update(ARRAY_TYPE(modseqs) *array, uint32_t seq1, uint32_t seq2, uint64_t value) { uint64_t *modseqp; for (; seq1 <= seq2; seq1++) { modseqp = array_idx_modifiable(array, seq1-1); if (*modseqp < value) *modseqp = value; } } static void modseqs_idx_update(struct mail_index_modseq_sync *ctx, unsigned int idx, uint32_t seq1, uint32_t seq2) { struct metadata_modseqs *metadata; uint64_t modseq; if (!ctx->view->index->modseqs_enabled) { /* we want to keep permanent modseqs updated, but don't bother updating in-memory per-flag updates */ return; } modseq = mail_transaction_log_view_get_prev_modseq(ctx->log_view); metadata = array_idx_modifiable(&ctx->mmap->metadata_modseqs, idx); if (!array_is_created(&metadata->modseqs)) i_array_init(&metadata->modseqs, seq2 + 16); modseqs_update(&metadata->modseqs, seq1, seq2, modseq); } void mail_index_modseq_update_flags(struct mail_index_modseq_sync *ctx, enum mail_flags flags_mask, uint32_t seq1, uint32_t seq2) { unsigned int i; if (!mail_index_modseq_update_to_highest(ctx, seq1, seq2)) return; for (i = 0; i < METADATA_MODSEQ_IDX_KEYWORD_START; i++) { if ((flags_mask & (1 << i)) != 0) modseqs_idx_update(ctx, i, seq1, seq2); } } void mail_index_modseq_update_keyword(struct mail_index_modseq_sync *ctx, unsigned int keyword_idx, uint32_t seq1, uint32_t seq2) { if (!mail_index_modseq_update_to_highest(ctx, seq1, seq2)) return; modseqs_idx_update(ctx, METADATA_MODSEQ_IDX_KEYWORD_START + keyword_idx, seq1, seq2); } void mail_index_modseq_reset_keywords(struct mail_index_modseq_sync *ctx, uint32_t seq1, uint32_t seq2) { unsigned int i, count; if (!mail_index_modseq_update_to_highest(ctx, seq1, seq2)) return; count = array_count(&ctx->mmap->metadata_modseqs); for (i = METADATA_MODSEQ_IDX_KEYWORD_START; i < count; i++) modseqs_idx_update(ctx, i, seq1, seq2); } struct mail_index_map_modseq * mail_index_map_modseq_clone(const struct mail_index_map_modseq *mmap) { struct mail_index_map_modseq *new_mmap; const struct metadata_modseqs *src_metadata; struct metadata_modseqs *dest_metadata; unsigned int i, count; src_metadata = array_get(&mmap->metadata_modseqs, &count); new_mmap = i_new(struct mail_index_map_modseq, 1); i_array_init(&new_mmap->metadata_modseqs, count + 16); for (i = 0; i < count; i++) { dest_metadata = array_append_space(&new_mmap->metadata_modseqs); if (array_is_created(&src_metadata[i].modseqs)) { i_array_init(&dest_metadata->modseqs, array_count(&src_metadata[i].modseqs)); array_append_array(&dest_metadata->modseqs, &src_metadata[i].modseqs); } } return new_mmap; } void mail_index_map_modseq_free(struct mail_index_map_modseq **_mmap) { struct mail_index_map_modseq *mmap = *_mmap; struct metadata_modseqs *metadata; *_mmap = NULL; array_foreach_modifiable(&mmap->metadata_modseqs, metadata) { if (array_is_created(&metadata->modseqs)) array_free(&metadata->modseqs); } array_free(&mmap->metadata_modseqs); i_free(mmap); } bool mail_index_modseq_get_next_log_offset(struct mail_index_view *view, uint64_t modseq, uint32_t *log_seq_r, uoff_t *log_offset_r) { struct mail_transaction_log *log = view->index->log; struct mail_transaction_log_file *file, *prev_file; const char *reason; int ret; if (log->files == NULL) { /* we shouldn't normally get here */ return FALSE; } while (modseq < log->files->hdr.initial_modseq) { /* try to find the previous log file if it still exists */ ret = mail_transaction_log_find_file(log, log->files->hdr.file_seq - 1, FALSE, &file, &reason); if (ret <= 0) return FALSE; } prev_file = NULL; for (file = log->files; file != NULL; file = file->next) { if (modseq < file->hdr.initial_modseq) break; prev_file = file; } if (prev_file == NULL) { /* the log file has been deleted already */ return FALSE; } *log_seq_r = prev_file->hdr.file_seq; if (mail_transaction_log_file_get_modseq_next_offset(prev_file, modseq, log_offset_r) < 0) return FALSE; if (*log_seq_r > view->log_file_head_seq || (*log_seq_r == view->log_file_head_seq && *log_offset_r > view->log_file_head_offset)) { /* modseq is already beyond our view. move it back so the caller won't be confused. */ *log_seq_r = view->log_file_head_seq; *log_offset_r = view->log_file_head_offset; } return TRUE; } dovecot-2.2.33.2/src/lib-index/mail-index-modseq.h0000644000175000017500000000505013147010713016452 00000000000000#ifndef MAIL_INDEX_MODSEQ_H #define MAIL_INDEX_MODSEQ_H #include "mail-types.h" #define MAIL_INDEX_MODSEQ_EXT_NAME "modseq" struct mail_keywords; struct mail_index; struct mail_index_map; struct mail_index_view; struct mail_index_modseq; struct mail_index_map_modseq; struct mail_index_sync_map_ctx; struct mail_index_modseq_header { /* highest used modseq */ uint64_t highest_modseq; /* last tracked log file position */ uint32_t log_seq; uint32_t log_offset; }; void mail_index_modseq_init(struct mail_index *index); const struct mail_index_modseq_header * mail_index_map_get_modseq_header(struct mail_index_map *map); uint64_t mail_index_map_modseq_get_highest(struct mail_index_map *map); void mail_index_modseq_enable(struct mail_index *index); bool mail_index_have_modseq_tracking(struct mail_index *index); uint64_t mail_index_modseq_get_highest(struct mail_index_view *view); uint64_t mail_index_modseq_lookup(struct mail_index_view *view, uint32_t seq); uint64_t mail_index_modseq_lookup_flags(struct mail_index_view *view, enum mail_flags flags_mask, uint32_t seq); uint64_t mail_index_modseq_lookup_keywords(struct mail_index_view *view, const struct mail_keywords *keywords, uint32_t seq); int mail_index_modseq_set(struct mail_index_view *view, uint32_t seq, uint64_t min_modseq); struct mail_index_modseq_sync * mail_index_modseq_sync_begin(struct mail_index_sync_map_ctx *sync_map_ctx); void mail_index_modseq_sync_end(struct mail_index_modseq_sync **ctx); void mail_index_modseq_sync_map_replaced(struct mail_index_modseq_sync *ctx); void mail_index_modseq_hdr_update(struct mail_index_modseq_sync *ctx); void mail_index_modseq_append(struct mail_index_modseq_sync *ctx, uint32_t seq); void mail_index_modseq_expunge(struct mail_index_modseq_sync *ctx, uint32_t seq1, uint32_t seq2); void mail_index_modseq_update_flags(struct mail_index_modseq_sync *ctx, enum mail_flags flags_mask, uint32_t seq1, uint32_t seq2); void mail_index_modseq_update_keyword(struct mail_index_modseq_sync *ctx, unsigned int keyword_idx, uint32_t seq1, uint32_t seq2); void mail_index_modseq_reset_keywords(struct mail_index_modseq_sync *ctx, uint32_t seq1, uint32_t seq2); struct mail_index_map_modseq * mail_index_map_modseq_clone(const struct mail_index_map_modseq *mmap); void mail_index_map_modseq_free(struct mail_index_map_modseq **mmap); bool mail_index_modseq_get_next_log_offset(struct mail_index_view *view, uint64_t modseq, uint32_t *log_seq_r, uoff_t *log_offset_r); #endif dovecot-2.2.33.2/src/lib-index/mail-transaction-log-append.c0000644000175000017500000002003313123174404020421 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "write-full.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" void mail_transaction_log_append_add(struct mail_transaction_log_append_ctx *ctx, enum mail_transaction_type type, const void *data, size_t size) { struct mail_transaction_header hdr; i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0); i_assert((size % 4) == 0); if (size == 0) return; i_zero(&hdr); hdr.type = type | ctx->trans_flags; if (type == MAIL_TRANSACTION_EXPUNGE || type == MAIL_TRANSACTION_EXPUNGE_GUID) hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT; if (type == MAIL_TRANSACTION_BOUNDARY) hdr.type |= MAIL_TRANSACTION_EXTERNAL; hdr.size = sizeof(hdr) + size; hdr.size = mail_index_uint32_to_offset(hdr.size); buffer_append(ctx->output, &hdr, sizeof(hdr)); buffer_append(ctx->output, data, size); mail_transaction_update_modseq(&hdr, data, &ctx->new_highest_modseq, MAIL_TRANSACTION_LOG_HDR_VERSION(&ctx->log->head->hdr)); ctx->transaction_count++; } static int log_buffer_move_to_memory(struct mail_transaction_log_append_ctx *ctx) { struct mail_transaction_log_file *file = ctx->log->head; /* first we need to truncate this latest write so that log syncing doesn't break */ if (ftruncate(file->fd, file->sync_offset) < 0) { mail_index_file_set_syscall_error(ctx->log->index, file->filepath, "ftruncate()"); } if (mail_index_move_to_memory(ctx->log->index) < 0) return -1; i_assert(MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)); i_assert(file->buffer_offset + file->buffer->used == file->sync_offset); buffer_append_buf(file->buffer, ctx->output, 0, (size_t)-1); file->sync_offset = file->buffer_offset + file->buffer->used; return 0; } static int log_buffer_write(struct mail_transaction_log_append_ctx *ctx) { struct mail_transaction_log_file *file = ctx->log->head; if (ctx->output->used == 0) return 0; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) { if (file->buffer == NULL) { file->buffer = buffer_create_dynamic(default_pool, 4096); file->buffer_offset = sizeof(file->hdr); } buffer_append_buf(file->buffer, ctx->output, 0, (size_t)-1); file->sync_offset = file->buffer_offset + file->buffer->used; return 0; } if (write_full(file->fd, ctx->output->data, ctx->output->used) < 0) { /* write failure, fallback to in-memory indexes. */ mail_index_file_set_syscall_error(ctx->log->index, file->filepath, "write_full()"); return log_buffer_move_to_memory(ctx); } i_assert(!ctx->sync_includes_this || file->sync_offset + ctx->output->used == file->max_tail_offset); if ((ctx->want_fsync && file->log->index->fsync_mode != FSYNC_MODE_NEVER) || file->log->index->fsync_mode == FSYNC_MODE_ALWAYS) { if (fdatasync(file->fd) < 0) { mail_index_file_set_syscall_error(ctx->log->index, file->filepath, "fdatasync()"); return log_buffer_move_to_memory(ctx); } } if (file->mmap_base == NULL && file->buffer != NULL) { /* we're reading from a file. avoid re-reading the data that we just wrote. this is also important for some NFS clients, which for some reason sometimes can't read() this data we just wrote in the same process */ i_assert(file->buffer_offset + file->buffer->used == file->sync_offset); buffer_append(file->buffer, ctx->output->data, ctx->output->used); } file->sync_offset += ctx->output->used; return 0; } static void log_append_sync_offset_if_needed(struct mail_transaction_log_append_ctx *ctx) { struct mail_transaction_log_file *file = ctx->log->head; struct mail_transaction_header_update *u; struct mail_transaction_header *hdr; uint32_t offset; buffer_t buf; unsigned char update_data[sizeof(*u) + sizeof(offset)]; if (!ctx->index_sync_transaction) { /* this is a non-syncing transaction. update the tail offset only if we're already writing something else to transaction log anyway. */ i_assert(!ctx->tail_offset_changed); /* FIXME: For now we never do this update, because it would cause errors about shrinking tail offsets with old Dovecot versions. This is anyway just an optimization, so it doesn't matter all that much if we don't do it here. Finish this in v2.3. */ /*if (ctx->output->used == 0)*/ return; } else if (file->max_tail_offset == file->sync_offset) { /* we're synced all the way to tail offset, so this sync transaction can also be included in the same tail offset. */ if (ctx->output->used == 0 && !ctx->tail_offset_changed) { /* nothing to write here after all (e.g. all unchanged flag updates were dropped by export) */ return; } /* FIXME: when we remove exclusive log locking, we can't rely on this. then write non-changed offset + check real offset + rewrite the new offset if other transactions weren't written in the middle */ file->max_tail_offset += ctx->output->used + sizeof(*hdr) + sizeof(*u) + sizeof(offset); ctx->sync_includes_this = TRUE; } else { /* This is a syncing transaction. Since we're finishing a sync, we may need to update the tail offset even if we don't have anything else to do. */ } offset = file->max_tail_offset; if (file->saved_tail_offset == offset) return; i_assert(offset > file->saved_tail_offset); buffer_create_from_data(&buf, update_data, sizeof(update_data)); u = buffer_append_space_unsafe(&buf, sizeof(*u)); u->offset = offsetof(struct mail_index_header, log_file_tail_offset); u->size = sizeof(offset); buffer_append(&buf, &offset, sizeof(offset)); mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_HEADER_UPDATE, buf.data, buf.used); } static int mail_transaction_log_append_locked(struct mail_transaction_log_append_ctx *ctx) { struct mail_transaction_log_file *file = ctx->log->head; struct mail_transaction_boundary *boundary; if (file->sync_offset < file->last_size) { /* there is some garbage at the end of the transaction log (eg. previous write failed). remove it so reader doesn't break because of it. */ buffer_set_used_size(file->buffer, file->sync_offset - file->buffer_offset); if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) { if (ftruncate(file->fd, file->sync_offset) < 0) { mail_index_file_set_syscall_error(ctx->log->index, file->filepath, "ftruncate()"); } } } /* don't include log_file_tail_offset update in the transaction */ boundary = buffer_get_space_unsafe(ctx->output, sizeof(struct mail_transaction_header), sizeof(*boundary)); boundary->size = ctx->output->used; if (ctx->transaction_count <= 2) { /* 0-1 changes. don't bother with the boundary */ unsigned int boundary_size = sizeof(struct mail_transaction_header) + sizeof(*boundary); buffer_delete(ctx->output, 0, boundary_size); } log_append_sync_offset_if_needed(ctx); if (log_buffer_write(ctx) < 0) return -1; file->sync_highest_modseq = ctx->new_highest_modseq; return 0; } int mail_transaction_log_append_begin(struct mail_index *index, enum mail_transaction_type flags, struct mail_transaction_log_append_ctx **ctx_r) { struct mail_transaction_log_append_ctx *ctx; struct mail_transaction_boundary boundary; if (!index->log_sync_locked) { if (mail_transaction_log_lock_head(index->log, "appending") < 0) return -1; } ctx = i_new(struct mail_transaction_log_append_ctx, 1); ctx->log = index->log; ctx->output = buffer_create_dynamic(default_pool, 1024); ctx->trans_flags = flags; i_zero(&boundary); mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_BOUNDARY, &boundary, sizeof(boundary)); *ctx_r = ctx; return 0; } int mail_transaction_log_append_commit(struct mail_transaction_log_append_ctx **_ctx) { struct mail_transaction_log_append_ctx *ctx = *_ctx; struct mail_index *index = ctx->log->index; int ret = 0; *_ctx = NULL; ret = mail_transaction_log_append_locked(ctx); if (!index->log_sync_locked) mail_transaction_log_file_unlock(index->log->head, "appending"); buffer_free(&ctx->output); i_free(ctx); return ret; } dovecot-2.2.33.2/src/lib-index/mail-cache-private.h0000644000175000017500000002133213165463624016606 00000000000000#ifndef MAIL_CACHE_PRIVATE_H #define MAIL_CACHE_PRIVATE_H #include "file-dotlock.h" #include "mail-index-private.h" #include "mail-cache.h" #define MAIL_CACHE_MAJOR_VERSION 1 #define MAIL_CACHE_MINOR_VERSION 1 /* Drop fields that haven't been accessed for n seconds */ #define MAIL_CACHE_FIELD_DROP_SECS (3600*24*30) /* Never compress the file if it's smaller than this */ #define MAIL_CACHE_COMPRESS_MIN_SIZE (1024*32) /* Compress the file when n% of records are deleted */ #define MAIL_CACHE_COMPRESS_DELETE_PERCENTAGE 20 /* Compress the file when n% of rows contain continued rows. 200% means that there's 2 continued rows per record. */ #define MAIL_CACHE_COMPRESS_CONTINUED_PERCENTAGE 200 /* Compress the file when we need to follow more than n next_offsets to find the latest cache header. */ #define MAIL_CACHE_HEADER_FIELD_CONTINUE_COUNT 4 /* If cache record becomes larger than this, don't add it. */ #define MAIL_CACHE_RECORD_MAX_SIZE (64*1024) #define MAIL_CACHE_LOCK_TIMEOUT 10 #define MAIL_CACHE_LOCK_CHANGE_TIMEOUT 300 #define MAIL_CACHE_IS_UNUSABLE(cache) \ ((cache)->hdr == NULL) struct mail_cache_header { /* version is increased only when you can't have backwards compatibility. */ uint8_t major_version; uint8_t compat_sizeof_uoff_t; uint8_t minor_version; uint8_t unused; uint32_t indexid; uint32_t file_seq; uint32_t continued_record_count; /* NOTE: old versions used this for hole offset, so we can't fully rely on it */ uint32_t record_count; uint32_t backwards_compat_used_file_size; uint32_t deleted_record_count; uint32_t field_header_offset; }; struct mail_cache_header_fields { uint32_t next_offset; uint32_t size; uint32_t fields_count; #if 0 /* last time the field was accessed. not updated more often than once a day. */ uint32_t last_used[fields_count]; /* (uint32_t)-1 for variable sized fields */ uint32_t size[fields_count]; /* enum mail_cache_field_type */ uint8_t type[fields_count]; /* enum mail_cache_decision_type */ uint8_t decision[fields_count]; /* NUL-separated list of field names */ char name[fields_count][]; #endif }; #define MAIL_CACHE_FIELD_LAST_USED() \ (sizeof(uint32_t) * 3) #define MAIL_CACHE_FIELD_SIZE(count) \ (MAIL_CACHE_FIELD_LAST_USED() + sizeof(uint32_t) * (count)) #define MAIL_CACHE_FIELD_TYPE(count) \ (MAIL_CACHE_FIELD_SIZE(count) + sizeof(uint32_t) * (count)) #define MAIL_CACHE_FIELD_DECISION(count) \ (MAIL_CACHE_FIELD_TYPE(count) + sizeof(uint8_t) * (count)) #define MAIL_CACHE_FIELD_NAMES(count) \ (MAIL_CACHE_FIELD_DECISION(count) + sizeof(uint8_t) * (count)) struct mail_cache_record { uint32_t prev_offset; uint32_t size; /* full record size, including this header */ /* array of { uint32_t field; [ uint32_t size; ] { .. } } */ }; struct mail_cache_field_private { struct mail_cache_field field; uint32_t uid_highwater; /* Unused fields aren't written to cache file */ unsigned int used:1; unsigned int adding:1; unsigned int decision_dirty:1; }; struct mail_cache { struct mail_index *index; uint32_t ext_id; char *filepath; int fd; ino_t st_ino; dev_t st_dev; time_t last_mmap_error_time; size_t mmap_length; /* a) mmaping the whole file */ void *mmap_base; /* b) using file cache */ struct file_cache *file_cache; /* c) using small read() calls with MAIL_INDEX_OPEN_FLAG_SAVEONLY */ uoff_t read_offset; buffer_t *read_buf; /* mail_cache_map() increases this always. */ unsigned int remap_counter; struct dotlock_settings dotlock_settings; struct dotlock *dotlock; struct file_lock *file_lock; /* mmap_disable=no: hdr points to data / NULL when cache is invalid. mmap_disable=yes: hdr points to hdr_ro_copy. this is needed because cache invalidation can zero the data any time */ const struct mail_cache_header *hdr; struct mail_cache_header hdr_ro_copy; /* hdr_copy gets updated when cache is locked and written when unlocking and hdr_modified=TRUE */ struct mail_cache_header hdr_copy; pool_t field_pool; struct mail_cache_field_private *fields; uint32_t *field_file_map; unsigned int fields_count; HASH_TABLE(char *, void *) field_name_hash; /* name -> idx */ uint32_t last_field_header_offset; /* 0 is no need for compression, otherwise the file sequence number which we want compressed. */ uint32_t need_compress_file_seq; unsigned int *file_field_map; unsigned int file_fields_count; unsigned int opened:1; unsigned int locked:1; unsigned int last_lock_failed:1; unsigned int hdr_modified:1; unsigned int field_header_write_pending:1; unsigned int compressing:1; unsigned int map_with_read:1; }; struct mail_cache_loop_track { /* we're looping if size_sum > (max_offset-min_offset) */ uoff_t min_offset, max_offset; uoff_t size_sum; }; struct mail_cache_missing_reason_cache { uint32_t highest_checked_seq; uint32_t highest_seq_with_cache; uint32_t reset_id; uint32_t log_file_head_seq; uoff_t log_file_head_offset; }; struct mail_cache_view { struct mail_cache *cache; struct mail_index_view *view, *trans_view; struct mail_cache_transaction_ctx *transaction; uint32_t trans_seq1, trans_seq2; struct mail_cache_loop_track loop_track; struct mail_cache_missing_reason_cache reason_cache; /* if cached_exists_buf[field] == cached_exists_value, it's cached. this allows us to avoid constantly clearing the whole buffer. it needs to be cleared only when cached_exists_value is wrapped. */ buffer_t *cached_exists_buf; uint8_t cached_exists_value; uint32_t cached_exists_seq; unsigned int no_decision_updates:1; }; struct mail_cache_iterate_field { unsigned int field_idx; unsigned int size; const void *data; uoff_t offset; }; struct mail_cache_lookup_iterate_ctx { struct mail_cache_view *view; unsigned int remap_counter; uint32_t seq; const struct mail_cache_record *rec; unsigned int pos, rec_size; uint32_t offset; unsigned int trans_next_idx; unsigned int stop:1; unsigned int failed:1; unsigned int memory_appends_checked:1; unsigned int disk_appends_checked:1; }; /* Explicitly lock the cache file. Returns -1 if error / timed out, 1 if ok, 0 if cache is broken/doesn't exist */ int mail_cache_lock(struct mail_cache *cache); int mail_cache_try_lock(struct mail_cache *cache); /* Returns -1 if cache is / just got corrupted, 0 if ok. */ int mail_cache_unlock(struct mail_cache *cache); int mail_cache_write(struct mail_cache *cache, const void *data, size_t size, uoff_t offset); int mail_cache_append(struct mail_cache *cache, const void *data, size_t size, uint32_t *offset); int mail_cache_header_fields_read(struct mail_cache *cache); int mail_cache_header_fields_update(struct mail_cache *cache); void mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest); int mail_cache_header_fields_get_next_offset(struct mail_cache *cache, uint32_t *offset_r); void mail_cache_expunge_count(struct mail_cache *cache, unsigned int count); uint32_t mail_cache_lookup_cur_offset(struct mail_index_view *view, uint32_t seq, uint32_t *reset_id_r); int mail_cache_get_record(struct mail_cache *cache, uint32_t offset, const struct mail_cache_record **rec_r); uint32_t mail_cache_get_first_new_seq(struct mail_index_view *view); /* Returns TRUE if offset..size area has been tracked before. Returns FALSE if the area may or may not have been tracked before, but we don't know for sure yet. */ bool mail_cache_track_loops(struct mail_cache_loop_track *loop_track, uoff_t offset, uoff_t size); /* Iterate through a message's cached fields. */ void mail_cache_lookup_iter_init(struct mail_cache_view *view, uint32_t seq, struct mail_cache_lookup_iterate_ctx *ctx_r); /* Returns 1 if field was returned, 0 if end of fields, or -1 if error */ int mail_cache_lookup_iter_next(struct mail_cache_lookup_iterate_ctx *ctx, struct mail_cache_iterate_field *field_r); const struct mail_cache_record * mail_cache_transaction_lookup_rec(struct mail_cache_transaction_ctx *ctx, unsigned int seq, unsigned int *trans_next_idx); int mail_cache_map(struct mail_cache *cache, size_t offset, size_t size, const void **data_r); void mail_cache_file_close(struct mail_cache *cache); int mail_cache_reopen(struct mail_cache *cache); /* Notify the decision handling code that field was looked up for seq. This should be called even for fields that aren't currently in cache file */ void mail_cache_decision_state_update(struct mail_cache_view *view, uint32_t seq, unsigned int field); void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq, unsigned int field); int mail_cache_expunge_handler(struct mail_index_sync_map_ctx *sync_ctx, uint32_t seq, const void *data, void **sync_context, void *context); void mail_cache_set_syscall_error(struct mail_cache *cache, const char *function); #endif dovecot-2.2.33.2/src/lib-index/test-mail-transaction-log-append.c0000644000175000017500000001303013165463624021410 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include static bool log_lock_failure = FALSE; void mail_index_file_set_syscall_error(struct mail_index *index ATTR_UNUSED, const char *filepath ATTR_UNUSED, const char *function ATTR_UNUSED) { } int mail_transaction_log_lock_head(struct mail_transaction_log *log ATTR_UNUSED, const char *lock_reason ATTR_UNUSED) { return log_lock_failure ? -1 : 0; } void mail_transaction_log_file_unlock(struct mail_transaction_log_file *file ATTR_UNUSED, const char *lock_reason ATTR_UNUSED) {} void mail_transaction_update_modseq(const struct mail_transaction_header *hdr, const void *data ATTR_UNUSED, uint64_t *cur_modseq, unsigned int version ATTR_UNUSED) { if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) *cur_modseq += 1; } int mail_index_move_to_memory(struct mail_index *index ATTR_UNUSED) { return -1; } static void test_append_expunge(struct mail_transaction_log *log) { static unsigned int buf[] = { 0x12345678, 0xabcdef09 }; struct mail_transaction_log_file *file = log->head; struct mail_transaction_log_append_ctx *ctx; const struct mail_transaction_header *hdr; const unsigned int *bufp; const struct mail_transaction_boundary *bound; test_assert(mail_transaction_log_append_begin(log->index, MAIL_TRANSACTION_EXTERNAL, &ctx) == 0); mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_APPEND, &buf[0], sizeof(buf[0])); test_assert(ctx->new_highest_modseq == 0); mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_EXPUNGE, &buf[1], sizeof(buf[1])); test_assert(ctx->new_highest_modseq == 1); test_assert(mail_transaction_log_append_commit(&ctx) == 0); test_assert(file->sync_highest_modseq == 1); test_assert(file->sync_offset == file->buffer_offset + file->buffer->used); hdr = file->buffer->data; test_assert(hdr->type == (MAIL_TRANSACTION_BOUNDARY | MAIL_TRANSACTION_EXTERNAL)); test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(*bound)); bound = (const void *)(hdr + 1); test_assert(bound->size == file->buffer->used); hdr = (const void *)(bound + 1); test_assert(hdr->type == (MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL)); test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(buf[0])); bufp = (const void *)(hdr + 1); test_assert(*bufp == buf[0]); hdr = (const void *)(bufp + 1); test_assert(hdr->type == (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT | MAIL_TRANSACTION_EXTERNAL)); test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(buf[0])); bufp = (const void *)(hdr + 1); test_assert(*bufp == buf[1]); test_assert(file->buffer->used == (size_t)((const char *)(bufp+1) - (const char *)file->buffer->data)); buffer_set_used_size(file->buffer, 0); file->buffer_offset = 0; test_end(); } static void test_append_sync_offset(struct mail_transaction_log *log) { struct mail_transaction_log_file *file = log->head; struct mail_transaction_log_append_ctx *ctx; const struct mail_transaction_header *hdr; const struct mail_transaction_header_update *u; const uint32_t *offsetp; test_begin("transaction log append: append_sync_offset only"); test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) == 0); ctx->index_sync_transaction = TRUE; file->max_tail_offset = 123; test_assert(mail_transaction_log_append_commit(&ctx) == 0); test_assert(file->buffer->used == sizeof(*hdr) + sizeof(*u) + sizeof(*offsetp)); hdr = file->buffer->data; test_assert(hdr->type == MAIL_TRANSACTION_HEADER_UPDATE); test_assert(mail_index_offset_to_uint32(hdr->size) == file->buffer->used); u = (const void *)(hdr + 1); test_assert(u->offset == offsetof(struct mail_index_header, log_file_tail_offset)); test_assert(u->size == sizeof(*offsetp)); offsetp = (const void *)(u+1); test_assert(*offsetp == 123); test_end(); } static void test_mail_transaction_log_append(void) { struct mail_transaction_log *log; struct mail_transaction_log_file *file; struct mail_transaction_log_append_ctx *ctx; char tmp_path[] = "/tmp/dovecot.test.XXXXXX"; struct stat st; int fd; fd = mkstemp(tmp_path); if (fd == -1) i_fatal("mkstemp(%s) failed: %m", tmp_path); test_begin("transaction log append"); log = i_new(struct mail_transaction_log, 1); log->index = i_new(struct mail_index, 1); log->index->log = log; log->head = file = i_new(struct mail_transaction_log_file, 1); file->fd = -1; test_append_expunge(log); test_begin("transaction log append: lock failure"); log_lock_failure = TRUE; test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) < 0); log_lock_failure = FALSE; test_end(); test_append_sync_offset(log); /* do this after head->buffer has already been initialized */ test_begin("transaction log append: garbage truncation"); file->sync_offset = 1; file->buffer_offset = 1; file->last_size = 3; file->fd = fd; test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) == 0); test_assert(mail_transaction_log_append_commit(&ctx) == 0); if (fstat(fd, &st) < 0) i_fatal("fstat() failed: %m"); test_assert(st.st_size == 1); file->fd = -1; test_end(); buffer_free(&log->head->buffer); i_free(log->head); i_free(log->index); i_free(log); i_unlink(tmp_path); } int main(void) { static void (*test_functions[])(void) = { test_mail_transaction_log_append, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-index-map-read.c0000644000175000017500000003103713165463624016664 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "nfs-workarounds.h" #include "mmap-util.h" #include "read-full.h" #include "mail-index-private.h" #include "mail-index-sync-private.h" #include "mail-transaction-log-private.h" #include "ioloop.h" static void mail_index_map_copy_hdr(struct mail_index_map *map, const struct mail_index_header *hdr) { if (hdr->base_header_size < sizeof(map->hdr)) { /* header smaller than ours, make a copy so our newer headers won't have garbage in them */ i_zero(&map->hdr); memcpy(&map->hdr, hdr, hdr->base_header_size); } else { map->hdr = *hdr; } /* FIXME: backwards compatibility, remove later. In case this index is accessed with Dovecot v1.0, avoid recent message counter errors. */ map->hdr.unused_old_recent_messages_count = 0; } static int mail_index_mmap(struct mail_index_map *map, uoff_t file_size) { struct mail_index *index = map->index; struct mail_index_record_map *rec_map = map->rec_map; const struct mail_index_header *hdr; const char *error; i_assert(rec_map->mmap_base == NULL); buffer_free(&rec_map->buffer); if (file_size > SSIZE_T_MAX) { /* too large file to map into memory */ mail_index_set_error(index, "Index file too large: %s", index->filepath); return -1; } rec_map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, index->fd, 0); if (rec_map->mmap_base == MAP_FAILED) { rec_map->mmap_base = NULL; if (ioloop_time != index->last_mmap_error_time) { index->last_mmap_error_time = ioloop_time; mail_index_set_syscall_error(index, t_strdup_printf( "mmap(size=%"PRIuUOFF_T")", file_size)); } return -1; } rec_map->mmap_size = file_size; hdr = rec_map->mmap_base; if (rec_map->mmap_size > offsetof(struct mail_index_header, major_version) && hdr->major_version != MAIL_INDEX_MAJOR_VERSION) { /* major version change - handle silently */ return 0; } if (rec_map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) { mail_index_set_error(index, "Corrupted index file %s: " "File too small (%"PRIuSIZE_T")", index->filepath, rec_map->mmap_size); return 0; } if (!mail_index_check_header_compat(index, hdr, rec_map->mmap_size, &error)) { /* Can't use this file */ mail_index_set_error(index, "Corrupted index file %s: %s", index->filepath, error); return 0; } rec_map->mmap_used_size = hdr->header_size + hdr->messages_count * hdr->record_size; if (rec_map->mmap_used_size <= rec_map->mmap_size) rec_map->records_count = hdr->messages_count; else { rec_map->records_count = (rec_map->mmap_size - hdr->header_size) / hdr->record_size; rec_map->mmap_used_size = hdr->header_size + rec_map->records_count * hdr->record_size; mail_index_set_error(index, "Corrupted index file %s: " "messages_count too large (%u > %u)", index->filepath, hdr->messages_count, rec_map->records_count); } mail_index_map_copy_hdr(map, hdr); map->hdr_base = rec_map->mmap_base; rec_map->records = PTR_OFFSET(rec_map->mmap_base, map->hdr.header_size); return 1; } static int mail_index_read_header(struct mail_index *index, void *buf, size_t buf_size, size_t *pos_r) { size_t pos; int ret; memset(buf, 0, sizeof(struct mail_index_header)); /* try to read the whole header, but it's not necessarily an error to read less since the older versions of the index format could be smaller. Request reading up to buf_size, but accept if we only got the header. */ pos = 0; do { ret = pread(index->fd, PTR_OFFSET(buf, pos), buf_size - pos, pos); if (ret > 0) pos += ret; } while (ret > 0 && pos < sizeof(struct mail_index_header)); *pos_r = pos; return ret; } static int mail_index_try_read_map(struct mail_index_map *map, uoff_t file_size, bool *retry_r, bool try_retry) { struct mail_index *index = map->index; const struct mail_index_header *hdr; unsigned char read_buf[IO_BLOCK_SIZE]; const char *error; const void *buf; void *data = NULL; ssize_t ret; size_t pos, records_size, initial_buf_pos = 0; unsigned int records_count = 0, extra; i_assert(map->rec_map->mmap_base == NULL); *retry_r = FALSE; ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos); buf = read_buf; hdr = buf; if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) && hdr->major_version != MAIL_INDEX_MAJOR_VERSION) { /* major version change - handle silently */ return 0; } if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE && (ret > 0 || pos >= hdr->base_header_size)) { if (!mail_index_check_header_compat(index, hdr, file_size, &error)) { /* Can't use this file */ mail_index_set_error(index, "Corrupted index file %s: %s", index->filepath, error); return 0; } initial_buf_pos = pos; if (pos > hdr->header_size) pos = hdr->header_size; /* place the base header into memory. */ buffer_reset(map->hdr_copy_buf); buffer_append(map->hdr_copy_buf, buf, pos); if (pos != hdr->header_size) { /* @UNSAFE: read the rest of the header into memory */ data = buffer_append_space_unsafe(map->hdr_copy_buf, hdr->header_size - pos); ret = pread_full(index->fd, data, hdr->header_size - pos, pos); } } if (ret > 0) { /* header read, read the records now. */ records_size = (size_t)hdr->messages_count * hdr->record_size; records_count = hdr->messages_count; if (file_size - hdr->header_size < records_size || (hdr->record_size != 0 && records_size / hdr->record_size != hdr->messages_count)) { records_count = (file_size - hdr->header_size) / hdr->record_size; records_size = (size_t)records_count * hdr->record_size; mail_index_set_error(index, "Corrupted index file %s: " "messages_count too large (%u > %u)", index->filepath, hdr->messages_count, records_count); } if (map->rec_map->buffer == NULL) { map->rec_map->buffer = buffer_create_dynamic(default_pool, records_size); } /* @UNSAFE */ buffer_set_used_size(map->rec_map->buffer, 0); if (initial_buf_pos <= hdr->header_size) extra = 0; else { extra = initial_buf_pos - hdr->header_size; buffer_append(map->rec_map->buffer, CONST_PTR_OFFSET(buf, hdr->header_size), extra); } if (records_size > extra) { data = buffer_append_space_unsafe(map->rec_map->buffer, records_size - extra); ret = pread_full(index->fd, data, records_size - extra, hdr->header_size + extra); } } if (ret < 0) { if (errno == ESTALE && try_retry) { /* a new index file was renamed over this one. */ *retry_r = TRUE; return 0; } mail_index_set_syscall_error(index, "pread_full()"); return -1; } if (ret == 0) { mail_index_set_error(index, "Corrupted index file %s: File too small", index->filepath); return 0; } map->rec_map->records = buffer_get_modifiable_data(map->rec_map->buffer, NULL); map->rec_map->records_count = records_count; mail_index_map_copy_hdr(map, hdr); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); return 1; } static int mail_index_read_map(struct mail_index_map *map, uoff_t file_size) { struct mail_index *index = map->index; mail_index_sync_lost_handler_t *const *handlerp; struct stat st; unsigned int i; int ret; bool try_retry, retry; /* notify all "sync lost" handlers */ array_foreach(&index->sync_lost_handlers, handlerp) (**handlerp)(index); for (i = 0;; i++) { try_retry = i < MAIL_INDEX_ESTALE_RETRY_COUNT; if (file_size == (uoff_t)-1) { /* fstat() below failed */ ret = 0; retry = try_retry; } else { ret = mail_index_try_read_map(map, file_size, &retry, try_retry); } if (ret != 0 || !retry) break; /* ESTALE - reopen index file */ mail_index_close_file(index); ret = mail_index_try_open_only(index); if (ret <= 0) { if (ret == 0) { /* the file was lost */ errno = ENOENT; mail_index_set_syscall_error(index, "open()"); } return -1; } if (fstat(index->fd, &st) == 0) file_size = st.st_size; else { if (!ESTALE_FSTAT(errno)) { mail_index_set_syscall_error(index, "fstat()"); return -1; } file_size = (uoff_t)-1; } } return ret; } /* returns -1 = error, 0 = index files are unusable, 1 = index files are usable or at least repairable */ static int mail_index_map_latest_file(struct mail_index *index, const char **reason_r) { struct mail_index_map *old_map, *new_map; struct stat st; uoff_t file_size; bool use_mmap, unusable = FALSE; const char *error; int ret, try; *reason_r = NULL; ret = mail_index_reopen_if_changed(index, reason_r); if (ret <= 0) { if (ret < 0) return -1; /* the index file is lost/broken. let's hope that we can build it from the transaction log. */ return 1; } i_assert(index->fd != -1); if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) nfs_flush_attr_cache_fd_locked(index->filepath, index->fd); if (fstat(index->fd, &st) == 0) file_size = st.st_size; else { if (!ESTALE_FSTAT(errno)) { mail_index_set_syscall_error(index, "fstat()"); return -1; } file_size = (uoff_t)-1; } /* mmaping seems to be slower than just reading the file, so even if mmap isn't disabled don't use it unless the file is large enough */ use_mmap = (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0 && file_size != (uoff_t)-1 && file_size > MAIL_INDEX_MMAP_MIN_SIZE; new_map = mail_index_map_alloc(index); if (use_mmap) { ret = mail_index_mmap(new_map, file_size); } else { ret = mail_index_read_map(new_map, file_size); } if (ret == 0) { /* the index files are unusable */ unusable = TRUE; } for (try = 0; ret > 0; try++) { /* make sure the header is ok before using this mapping */ ret = mail_index_map_check_header(new_map, &error); if (ret < 0) { mail_index_set_error(index, "Corrupted index file %s: %s", index->filepath, error); } if (ret > 0) T_BEGIN { if (mail_index_map_parse_extensions(new_map) < 0) ret = 0; else if (mail_index_map_parse_keywords(new_map) < 0) ret = 0; } T_END; if (ret != 0 || try == 2) { if (ret < 0) { *reason_r = "Corrupted index file"; unusable = TRUE; ret = 0; } break; } /* fsck and try again */ old_map = index->map; index->map = new_map; if (mail_index_fsck(index) < 0) { ret = -1; break; } /* fsck replaced the map */ new_map = index->map; index->map = old_map; } if (ret <= 0) { mail_index_unmap(&new_map); return ret < 0 ? -1 : (unusable ? 0 : 1); } i_assert(new_map->rec_map->records != NULL); index->last_read_log_file_seq = new_map->hdr.log_file_seq; index->last_read_log_file_tail_offset = new_map->hdr.log_file_tail_offset; mail_index_unmap(&index->map); index->map = new_map; *reason_r = "Index mapped"; return 1; } int mail_index_map(struct mail_index *index, enum mail_index_sync_handler_type type) { const char *reason; int ret; i_assert(!index->mapping); index->mapping = TRUE; if (index->map == NULL) index->map = mail_index_map_alloc(index); /* first try updating the existing mapping from transaction log. */ if (index->initial_mapped) { /* we're not creating/opening the index. sync this as a view from transaction log. */ ret = mail_index_sync_map(&index->map, type, FALSE, "initial mapping"); } else { ret = 0; } if (ret == 0) { /* try to open and read the latest index. if it fails, we'll fallback to updating the existing mapping from transaction logs (which we'll also do even if the reopening succeeds). if index files are unusable (e.g. major version change) don't even try to use the transaction log. */ ret = mail_index_map_latest_file(index, &reason); if (ret > 0) { /* if we're creating the index file, we don't have any logs yet */ if (index->log->head != NULL && index->indexid != 0) { /* and update the map with the latest changes from transaction log */ ret = mail_index_sync_map(&index->map, type, TRUE, reason); } if (ret == 0) { /* we fsck'd the index. try opening again. */ ret = mail_index_map_latest_file(index, &reason); if (ret > 0 && index->indexid != 0) { ret = mail_index_sync_map(&index->map, type, TRUE, reason); } } } else if (ret == 0 && !index->readonly) { /* make sure we don't try to open the file again */ if (unlink(index->filepath) < 0 && errno != ENOENT) mail_index_set_syscall_error(index, "unlink()"); } } if (ret >= 0) index->initial_mapped = TRUE; index->mapping = FALSE; return ret; } dovecot-2.2.33.2/src/lib-index/mail-cache-compress.c0000644000175000017500000003754113165463624016773 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream.h" #include "nfs-workarounds.h" #include "read-full.h" #include "file-dotlock.h" #include "file-cache.h" #include "file-set-size.h" #include "mail-cache-private.h" #include #include struct mail_cache_copy_context { struct mail_cache *cache; buffer_t *buffer, *field_seen; ARRAY(unsigned int) bitmask_pos; uint32_t *field_file_map; uint8_t field_seen_value; bool new_msg; }; struct mail_cache_compress_lock { struct dotlock *dotlock; }; static void mail_cache_merge_bitmask(struct mail_cache_copy_context *ctx, const struct mail_cache_iterate_field *field) { unsigned char *dest; unsigned int i, *pos; pos = array_idx_modifiable(&ctx->bitmask_pos, field->field_idx); if (*pos == 0) { /* we decided to drop this field */ return; } dest = buffer_get_space_unsafe(ctx->buffer, *pos, field->size); for (i = 0; i < field->size; i++) dest[i] |= ((const unsigned char*)field->data)[i]; } static void mail_cache_compress_field(struct mail_cache_copy_context *ctx, const struct mail_cache_iterate_field *field) { struct mail_cache_field *cache_field; enum mail_cache_decision_type dec; uint32_t file_field_idx, size32; uint8_t *field_seen; file_field_idx = ctx->field_file_map[field->field_idx]; if (file_field_idx == (uint32_t)-1) return; cache_field = &ctx->cache->fields[field->field_idx].field; field_seen = buffer_get_space_unsafe(ctx->field_seen, field->field_idx, 1); if (*field_seen == ctx->field_seen_value) { /* duplicate */ if (cache_field->type == MAIL_CACHE_FIELD_BITMASK) mail_cache_merge_bitmask(ctx, field); return; } *field_seen = ctx->field_seen_value; dec = cache_field->decision & ~MAIL_CACHE_DECISION_FORCED; if (ctx->new_msg) { if (dec == MAIL_CACHE_DECISION_NO) return; } else { if (dec != MAIL_CACHE_DECISION_YES) return; } buffer_append(ctx->buffer, &file_field_idx, sizeof(file_field_idx)); if (cache_field->field_size == UINT_MAX) { size32 = (uint32_t)field->size; buffer_append(ctx->buffer, &size32, sizeof(size32)); } if (cache_field->type == MAIL_CACHE_FIELD_BITMASK) { /* remember the position in case we need to update it */ unsigned int pos = ctx->buffer->used; array_idx_set(&ctx->bitmask_pos, field->field_idx, &pos); } buffer_append(ctx->buffer, field->data, field->size); if ((field->size & 3) != 0) buffer_append_zero(ctx->buffer, 4 - (field->size & 3)); } static uint32_t get_next_file_seq(struct mail_cache *cache) { const struct mail_index_ext *ext; struct mail_index_view *view; uint32_t file_seq; /* make sure we look up the latest reset_id */ if (mail_index_refresh(cache->index) < 0) return -1; view = mail_index_view_open(cache->index); ext = mail_index_view_get_ext(view, cache->ext_id); file_seq = ext != NULL ? ext->reset_id + 1 : (uint32_t)ioloop_time; if (cache->hdr != NULL && file_seq <= cache->hdr->file_seq) file_seq = cache->hdr->file_seq + 1; mail_index_view_close(&view); return file_seq != 0 ? file_seq : 1; } static void mail_cache_compress_get_fields(struct mail_cache_copy_context *ctx, unsigned int used_fields_count) { struct mail_cache *cache = ctx->cache; struct mail_cache_field *field; unsigned int i, j, idx; /* Make mail_cache_header_fields_get() return the fields in the same order as we saved them. */ memcpy(cache->field_file_map, ctx->field_file_map, sizeof(uint32_t) * cache->fields_count); /* reverse mapping */ cache->file_fields_count = used_fields_count; i_free(cache->file_field_map); cache->file_field_map = used_fields_count == 0 ? NULL : i_new(unsigned int, used_fields_count); for (i = j = 0; i < cache->fields_count; i++) { idx = cache->field_file_map[i]; if (idx != (uint32_t)-1) { i_assert(idx < used_fields_count && cache->file_field_map != NULL && cache->file_field_map[idx] == 0); cache->file_field_map[idx] = i; j++; } /* change permanent decisions to temporary decisions. if they're still permanent they'll get updated later. */ field = &cache->fields[i].field; if (field->decision == MAIL_CACHE_DECISION_YES) field->decision = MAIL_CACHE_DECISION_TEMP; } i_assert(j == used_fields_count); buffer_set_used_size(ctx->buffer, 0); mail_cache_header_fields_get(cache, ctx->buffer); } static int mail_cache_copy(struct mail_cache *cache, struct mail_index_transaction *trans, int fd, uint32_t *file_seq_r, uoff_t *file_size_r, uint32_t *max_uid_r, ARRAY_TYPE(uint32_t) *ext_offsets) { struct mail_cache_copy_context ctx; struct mail_cache_lookup_iterate_ctx iter; struct mail_cache_iterate_field field; struct mail_index_view *view; struct mail_cache_view *cache_view; const struct mail_index_header *idx_hdr; struct mail_cache_header hdr; struct mail_cache_record cache_rec; struct ostream *output; uint32_t message_count, seq, first_new_seq, ext_offset; unsigned int i, used_fields_count, orig_fields_count, record_count; time_t max_drop_time; *max_uid_r = 0; /* get the latest info on fields */ if (mail_cache_header_fields_read(cache) < 0) return -1; view = mail_index_transaction_get_view(trans); cache_view = mail_cache_view_open(cache, view); output = o_stream_create_fd_file(fd, 0, FALSE); i_zero(&hdr); hdr.major_version = MAIL_CACHE_MAJOR_VERSION; hdr.minor_version = MAIL_CACHE_MINOR_VERSION; hdr.compat_sizeof_uoff_t = sizeof(uoff_t); hdr.indexid = cache->index->indexid; hdr.file_seq = get_next_file_seq(cache); o_stream_nsend(output, &hdr, sizeof(hdr)); i_zero(&ctx); ctx.cache = cache; ctx.buffer = buffer_create_dynamic(default_pool, 4096); ctx.field_seen = buffer_create_dynamic(default_pool, 64); ctx.field_seen_value = 0; ctx.field_file_map = t_new(uint32_t, cache->fields_count + 1); t_array_init(&ctx.bitmask_pos, 32); /* @UNSAFE: drop unused fields and create a field mapping for used fields */ idx_hdr = mail_index_get_header(view); max_drop_time = idx_hdr->day_stamp == 0 ? 0 : idx_hdr->day_stamp - MAIL_CACHE_FIELD_DROP_SECS; orig_fields_count = cache->fields_count; if (cache->file_fields_count == 0) { /* creating the initial cache file. add all fields. */ for (i = 0; i < orig_fields_count; i++) ctx.field_file_map[i] = i; used_fields_count = i; } else { for (i = used_fields_count = 0; i < orig_fields_count; i++) { struct mail_cache_field_private *priv = &cache->fields[i]; enum mail_cache_decision_type dec = priv->field.decision; /* if the decision isn't forced and this field hasn't been accessed for a while, drop it */ if ((dec & MAIL_CACHE_DECISION_FORCED) == 0 && priv->field.last_used < max_drop_time && !priv->adding) { dec = MAIL_CACHE_DECISION_NO; priv->field.decision = dec; } /* drop all fields we don't want */ if ((dec & ~MAIL_CACHE_DECISION_FORCED) == MAIL_CACHE_DECISION_NO && !priv->adding) { priv->used = FALSE; priv->field.last_used = 0; } ctx.field_file_map[i] = !priv->used ? (uint32_t)-1 : used_fields_count++; } } /* get sequence of first message which doesn't need its temp fields removed. */ first_new_seq = mail_cache_get_first_new_seq(view); message_count = mail_index_view_get_messages_count(view); i_array_init(ext_offsets, message_count); record_count = 0; for (seq = 1; seq <= message_count; seq++) { if (mail_index_transaction_is_expunged(trans, seq)) { array_append_zero(ext_offsets); continue; } ctx.new_msg = seq >= first_new_seq; buffer_set_used_size(ctx.buffer, 0); if (++ctx.field_seen_value == 0) { memset(buffer_get_modifiable_data(ctx.field_seen, NULL), 0, buffer_get_size(ctx.field_seen)); ctx.field_seen_value++; } i_zero(&cache_rec); buffer_append(ctx.buffer, &cache_rec, sizeof(cache_rec)); mail_cache_lookup_iter_init(cache_view, seq, &iter); while (mail_cache_lookup_iter_next(&iter, &field) > 0) mail_cache_compress_field(&ctx, &field); if (ctx.buffer->used == sizeof(cache_rec) || ctx.buffer->used > MAIL_CACHE_RECORD_MAX_SIZE) { /* nothing cached */ ext_offset = 0; } else { mail_index_lookup_uid(view, seq, max_uid_r); cache_rec.size = ctx.buffer->used; ext_offset = output->offset; buffer_write(ctx.buffer, 0, &cache_rec, sizeof(cache_rec)); o_stream_nsend(output, ctx.buffer->data, cache_rec.size); record_count++; } array_append(ext_offsets, &ext_offset, 1); } i_assert(orig_fields_count == cache->fields_count); hdr.record_count = record_count; hdr.field_header_offset = mail_index_uint32_to_offset(output->offset); mail_cache_compress_get_fields(&ctx, used_fields_count); o_stream_nsend(output, ctx.buffer->data, ctx.buffer->used); hdr.backwards_compat_used_file_size = output->offset; buffer_free(&ctx.buffer); buffer_free(&ctx.field_seen); (void)o_stream_seek(output, 0); o_stream_nsend(output, &hdr, sizeof(hdr)); mail_cache_view_close(&cache_view); if (o_stream_nfinish(output) < 0) { mail_cache_set_syscall_error(cache, "write()"); o_stream_destroy(&output); array_free(ext_offsets); return -1; } *file_size_r = output->offset; o_stream_destroy(&output); if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) { if (fdatasync(fd) < 0) { mail_cache_set_syscall_error(cache, "fdatasync()"); array_free(ext_offsets); return -1; } } *file_seq_r = hdr.file_seq; return 0; } static int mail_cache_compress_write(struct mail_cache *cache, struct mail_index_transaction *trans, int fd, const char *temp_path, bool *unlock) { struct stat st; uint32_t file_seq, old_offset, max_uid; ARRAY_TYPE(uint32_t) ext_offsets; const uint32_t *offsets; uoff_t file_size; unsigned int i, count; if (mail_cache_copy(cache, trans, fd, &file_seq, &file_size, &max_uid, &ext_offsets) < 0) return -1; if (fstat(fd, &st) < 0) { mail_cache_set_syscall_error(cache, "fstat()"); array_free(&ext_offsets); return -1; } if (rename(temp_path, cache->filepath) < 0) { mail_cache_set_syscall_error(cache, "rename()"); array_free(&ext_offsets); return -1; } if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_DEBUG) != 0) { i_debug("%s: Compressed, file_seq changed %u -> %u, " "size=%"PRIuUOFF_T", max_uid=%u", cache->filepath, cache->need_compress_file_seq, file_seq, file_size, max_uid); } /* once we're sure that the compression was successful, update the offsets */ mail_index_ext_reset(trans, cache->ext_id, file_seq, TRUE); offsets = array_get(&ext_offsets, &count); for (i = 0; i < count; i++) { if (offsets[i] != 0) { mail_index_update_ext(trans, i + 1, cache->ext_id, &offsets[i], &old_offset); } } array_free(&ext_offsets); if (*unlock) { (void)mail_cache_unlock(cache); *unlock = FALSE; } mail_cache_file_close(cache); cache->opened = TRUE; cache->fd = fd; cache->st_ino = st.st_ino; cache->st_dev = st.st_dev; cache->field_header_write_pending = FALSE; return 0; } static int mail_cache_compress_has_file_changed(struct mail_cache *cache) { struct mail_cache_header hdr; unsigned int i; int fd, ret; for (i = 0;; i++) { fd = nfs_safe_open(cache->filepath, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; mail_cache_set_syscall_error(cache, "open()"); return -1; } ret = read_full(fd, &hdr, sizeof(hdr)); i_close_fd(&fd); if (ret >= 0) { if (ret == 0) return 0; if (cache->need_compress_file_seq == 0) { /* previously it didn't exist or it was unusable and was just unlinked */ return 1; } return hdr.file_seq != cache->need_compress_file_seq; } else if (errno != ESTALE || i >= NFS_ESTALE_RETRY_COUNT) { mail_cache_set_syscall_error(cache, "read()"); return -1; } } } static int mail_cache_compress_dotlock(struct mail_cache *cache, struct dotlock **dotlock_r) { if (file_dotlock_create(&cache->dotlock_settings, cache->filepath, DOTLOCK_CREATE_FLAG_NONBLOCK, dotlock_r) <= 0) { if (errno != EAGAIN) mail_cache_set_syscall_error(cache, "file_dotlock_open()"); return -1; } return 0; } static int mail_cache_compress_locked(struct mail_cache *cache, struct mail_index_transaction *trans, bool *unlock, struct dotlock **dotlock_r) { const char *temp_path; const void *data; int fd, ret; /* There are two possible locking situations here: a) Cache is locked against any modifications. b) Cache doesn't exist or is unusable. There's no lock. Because the cache lock itself is unreliable, we'll be using a separate dotlock to guard against two processes compressing the cache at the same time. */ if (mail_cache_compress_dotlock(cache, dotlock_r) < 0) return -1; /* we've locked the cache compression now. if somebody else had just recreated the cache, reopen the cache and return success. */ if ((ret = mail_cache_compress_has_file_changed(cache)) != 0) { if (ret < 0) return -1; /* was just compressed, forget this */ cache->need_compress_file_seq = 0; file_dotlock_delete(dotlock_r); if (*unlock) { (void)mail_cache_unlock(cache); *unlock = FALSE; } return mail_cache_reopen(cache) < 0 ? -1 : 0; } if (cache->fd != -1) { /* make sure we have mapped it before reading. */ if (mail_cache_map(cache, 0, 0, &data) < 0) return -1; } /* we want to recreate the cache. write it first to a temporary file */ fd = mail_index_create_tmp_file(cache->index, cache->filepath, &temp_path); if (fd == -1) return -1; if (mail_cache_compress_write(cache, trans, fd, temp_path, unlock) < 0) { i_close_fd(&fd); i_unlink(temp_path); return -1; } if (cache->file_cache != NULL) file_cache_set_fd(cache->file_cache, cache->fd); if (mail_cache_map(cache, 0, 0, &data) < 0) return -1; if (mail_cache_header_fields_read(cache) < 0) return -1; cache->need_compress_file_seq = 0; return 0; } int mail_cache_compress(struct mail_cache *cache, struct mail_index_transaction *trans, struct mail_cache_compress_lock **lock_r) { struct dotlock *dotlock = NULL; bool unlock = FALSE; int ret; i_assert(!cache->compressing); *lock_r = NULL; if (MAIL_INDEX_IS_IN_MEMORY(cache->index) || cache->index->readonly) { *lock_r = i_new(struct mail_cache_compress_lock, 1); return 0; } /* compression isn't very efficient with small read()s */ if (cache->map_with_read) { cache->map_with_read = FALSE; if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); cache->hdr = NULL; cache->mmap_length = 0; } if (cache->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) { /* we're using dotlocking, cache file creation itself creates the dotlock file we need. */ if (!MAIL_CACHE_IS_UNUSABLE(cache)) { mail_index_flush_read_cache(cache->index, cache->filepath, cache->fd, FALSE); } } else { switch (mail_cache_try_lock(cache)) { case -1: /* already locked or some other error */ return -1; case 0: /* cache is broken or doesn't exist. just start creating it. */ break; default: /* locking succeeded. */ unlock = TRUE; } } cache->compressing = TRUE; ret = mail_cache_compress_locked(cache, trans, &unlock, &dotlock); cache->compressing = FALSE; if (unlock) { if (mail_cache_unlock(cache) < 0) ret = -1; } if (ret < 0) { if (dotlock != NULL) file_dotlock_delete(&dotlock); /* the fields may have been updated in memory already. reverse those changes by re-reading them from file. */ (void)mail_cache_header_fields_read(cache); } else { *lock_r = i_new(struct mail_cache_compress_lock, 1); (*lock_r)->dotlock = dotlock; } return ret; } void mail_cache_compress_unlock(struct mail_cache_compress_lock **_lock) { struct mail_cache_compress_lock *lock = *_lock; *_lock = NULL; if (lock->dotlock != NULL) file_dotlock_delete(&lock->dotlock); i_free(lock); } bool mail_cache_need_compress(struct mail_cache *cache) { return cache->need_compress_file_seq != 0 && (cache->index->flags & MAIL_INDEX_OPEN_FLAG_SAVEONLY) == 0 && !cache->index->readonly; } dovecot-2.2.33.2/src/lib-index/mail-index-transaction-sort-appends.c0000644000175000017500000001153713123174404022130 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "seq-range-array.h" #include "mail-index-private.h" #include "mail-index-transaction-private.h" struct uid_map { uint32_t idx; uint32_t uid; }; static int uid_map_cmp(const void *p1, const void *p2) { const struct uid_map *m1 = p1, *m2 = p2; return m1->uid < m2->uid ? -1 : (m1->uid > m2->uid ? 1 : 0); } static void mail_index_transaction_sort_appends_ext(ARRAY_TYPE(seq_array_array) *updates, uint32_t first_new_seq, const uint32_t *old_to_newseq_map) { ARRAY_TYPE(seq_array) *ext_rec_arrays; ARRAY_TYPE(seq_array) *old_array; ARRAY_TYPE(seq_array) new_array; unsigned int ext_count; const uint32_t *ext_rec; uint32_t seq; unsigned int i, j, count; if (!array_is_created(updates)) return; ext_rec_arrays = array_get_modifiable(updates, &count); for (j = 0; j < count; j++) { old_array = &ext_rec_arrays[j]; if (!array_is_created(old_array)) continue; ext_count = array_count(old_array); array_create(&new_array, default_pool, old_array->arr.element_size, ext_count); for (i = 0; i < ext_count; i++) { ext_rec = array_idx(old_array, i); seq = *ext_rec < first_new_seq ? *ext_rec : old_to_newseq_map[*ext_rec - first_new_seq]; (void)mail_index_seq_array_add(&new_array, seq, ext_rec+1, old_array->arr.element_size - sizeof(*ext_rec), NULL); } array_free(old_array); ext_rec_arrays[j] = new_array; } } static void sort_appends_seq_range(ARRAY_TYPE(seq_range) *array, uint32_t first_new_seq, const uint32_t *old_to_newseq_map) { struct seq_range *range, temp_range; ARRAY_TYPE(seq_range) old_seqs; uint32_t idx, idx1, idx2; unsigned int i, count; range = array_get_modifiable(array, &count); for (i = 0; i < count; i++) { if (range[i].seq2 >= first_new_seq) break; } if (i == count) { /* nothing to do */ return; } i_array_init(&old_seqs, count - i); if (range[i].seq1 < first_new_seq) { temp_range.seq1 = first_new_seq; temp_range.seq2 = range[i].seq2; array_append(&old_seqs, &temp_range, 1); range[i].seq2 = first_new_seq - 1; i++; } array_append(&old_seqs, &range[i], count - i); array_delete(array, i, count - i); range = array_get_modifiable(&old_seqs, &count); for (i = 0; i < count; i++) { idx1 = range[i].seq1 - first_new_seq; idx2 = range[i].seq2 - first_new_seq; for (idx = idx1; idx <= idx2; idx++) seq_range_array_add(array, old_to_newseq_map[idx]); } array_free(&old_seqs); } static void mail_index_transaction_sort_appends_keywords(struct mail_index_transaction *t, const uint32_t *old_to_newseq_map) { struct mail_index_transaction_keyword_update *update; if (!array_is_created(&t->keyword_updates)) return; array_foreach_modifiable(&t->keyword_updates, update) { if (array_is_created(&update->add_seq)) { sort_appends_seq_range(&update->add_seq, t->first_new_seq, old_to_newseq_map); } if (array_is_created(&update->remove_seq)) { sort_appends_seq_range(&update->remove_seq, t->first_new_seq, old_to_newseq_map); } } } void mail_index_transaction_sort_appends(struct mail_index_transaction *t) { struct mail_index_record *recs, *sorted_recs; struct uid_map *new_uid_map; uint32_t *old_to_newseq_map; unsigned int i, count; if (!array_is_created(&t->appends)) return; recs = array_get_modifiable(&t->appends, &count); i_assert(count > 0); if (!t->appends_nonsorted) { i_assert(recs[0].uid != 0); #ifdef DEBUG for (i = 1; i < count; i++) i_assert(recs[i-1].uid < recs[i].uid); #endif return; } /* first make a copy of the UIDs and map them to sequences */ new_uid_map = i_new(struct uid_map, count); for (i = 0; i < count; i++) { i_assert(recs[i].uid != 0); new_uid_map[i].idx = i; new_uid_map[i].uid = recs[i].uid; } /* now sort the UID map */ qsort(new_uid_map, count, sizeof(*new_uid_map), uid_map_cmp); /* sort mail records */ sorted_recs = i_new(struct mail_index_record, count); sorted_recs[0] = recs[new_uid_map[0].idx]; for (i = 1; i < count; i++) { sorted_recs[i] = recs[new_uid_map[i].idx]; if (sorted_recs[i].uid == sorted_recs[i-1].uid) i_panic("Duplicate UIDs added in transaction"); } buffer_write(t->appends.arr.buffer, 0, sorted_recs, sizeof(*sorted_recs) * count); i_free(sorted_recs); old_to_newseq_map = i_new(uint32_t, count); for (i = 0; i < count; i++) old_to_newseq_map[new_uid_map[i].idx] = i + t->first_new_seq; i_free(new_uid_map); mail_index_transaction_sort_appends_ext(&t->ext_rec_updates, t->first_new_seq, old_to_newseq_map); mail_index_transaction_sort_appends_ext(&t->ext_rec_atomics, t->first_new_seq, old_to_newseq_map); mail_index_transaction_sort_appends_keywords(t, old_to_newseq_map); i_free(old_to_newseq_map); t->appends_nonsorted = FALSE; } dovecot-2.2.33.2/src/lib-index/test-mail-index-map.c0000644000175000017500000000316213165463624016726 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mail-index-transaction-private.h" static void test_mail_index_map_lookup_seq_range_count(unsigned int messages_count) { struct mail_index_record_map rec_map; struct mail_index_map map; uint32_t seq, first_uid, last_uid, first_seq, last_seq, max_uid; i_zero(&map); i_zero(&rec_map); map.rec_map = &rec_map; map.hdr.messages_count = messages_count; map.hdr.record_size = sizeof(struct mail_index_record); rec_map.records_count = map.hdr.messages_count; rec_map.records = i_new(struct mail_index_record, map.hdr.messages_count); for (seq = 1; seq <= map.hdr.messages_count; seq++) MAIL_INDEX_REC_AT_SEQ(&map, seq)->uid = seq*2; max_uid = (seq-1)*2; map.hdr.next_uid = max_uid + 1; for (first_uid = 2; first_uid <= max_uid; first_uid++) { for (last_uid = first_uid; last_uid <= max_uid; last_uid++) { if (first_uid == last_uid && first_uid%2 != 0) continue; mail_index_map_lookup_seq_range(&map, first_uid, last_uid, &first_seq, &last_seq); test_assert((first_uid+1)/2 == first_seq && last_uid/2 == last_seq); } } i_free(rec_map.records); } static void test_mail_index_map_lookup_seq_range(void) { unsigned int i; test_begin("mail index map lookup seq range"); for (i = 1; i < 20; i++) test_mail_index_map_lookup_seq_range_count(i); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_index_map_lookup_seq_range, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-index/mail-cache-sync-update.c0000644000175000017500000000315613123174404017354 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-cache-private.h" #include "mail-index-sync-private.h" struct mail_cache_sync_context { unsigned expunge_count; }; void mail_cache_expunge_count(struct mail_cache *cache, unsigned int count) { if (mail_cache_lock(cache) > 0) { cache->hdr_copy.deleted_record_count += count; if (cache->hdr_copy.record_count >= count) cache->hdr_copy.record_count -= count; else cache->hdr_copy.record_count = 0; cache->hdr_modified = TRUE; (void)mail_cache_unlock(cache); } } static struct mail_cache_sync_context *mail_cache_handler_init(void **context) { struct mail_cache_sync_context *ctx; if (*context != NULL) ctx = *context; else { *context = i_new(struct mail_cache_sync_context, 1); ctx = *context; } return ctx; } static void mail_cache_handler_deinit(struct mail_index_sync_map_ctx *sync_ctx, struct mail_cache_sync_context *ctx) { struct mail_cache *cache = sync_ctx->view->index->cache; if (ctx == NULL) return; mail_cache_expunge_count(cache, ctx->expunge_count); i_free(ctx); } int mail_cache_expunge_handler(struct mail_index_sync_map_ctx *sync_ctx, uint32_t seq ATTR_UNUSED, const void *data, void **sync_context, void *context ATTR_UNUSED) { struct mail_cache_sync_context *ctx = *sync_context; const uint32_t *cache_offset = data; if (data == NULL) { mail_cache_handler_deinit(sync_ctx, ctx); *sync_context = NULL; return 0; } if (*cache_offset == 0) return 0; ctx = mail_cache_handler_init(sync_context); ctx->expunge_count++; return 0; } dovecot-2.2.33.2/src/lib-index/mail-index-util.c0000644000175000017500000000622513147010711016135 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "mail-index-private.h" uint32_t mail_index_uint32_to_offset(uint32_t offset) { i_assert(offset < 0x40000000); i_assert((offset & 3) == 0); offset >>= 2; offset = 0x00000080 | ((offset & 0x0000007f)) | 0x00008000 | ((offset & 0x00003f80) >> 7 << 8) | 0x00800000 | ((offset & 0x001fc000) >> 14 << 16) | 0x80000000 | ((offset & 0x0fe00000) >> 21 << 24); return cpu32_to_be(offset); } uint32_t mail_index_offset_to_uint32(uint32_t offset) { offset = be32_to_cpu(offset); if ((offset & 0x80808080) != 0x80808080) return 0; return (((offset & 0x0000007f)) | ((offset & 0x00007f00) >> 8 << 7) | ((offset & 0x007f0000) >> 16 << 14) | ((offset & 0x7f000000) >> 24 << 21)) << 2; } void mail_index_pack_num(uint8_t **p, uint32_t num) { /* number continues as long as the highest bit is set */ while (num >= 0x80) { **p = (num & 0x7f) | 0x80; *p += 1; num >>= 7; } **p = num; *p += 1; } int mail_index_unpack_num(const uint8_t **p, const uint8_t *end, uint32_t *num_r) { const uint8_t *c = *p; uint32_t value = 0; unsigned int bits = 0; for (;;) { if (unlikely(c == end)) { /* we should never see EOF */ *num_r = 0; return -1; } value |= (*c & 0x7f) << bits; if (*c < 0x80) break; bits += 7; c++; } if (unlikely(bits >= 32)) { /* broken input */ *p = end; *num_r = 0; return -1; } *p = c + 1; *num_r = value; return 0; } static int mail_index_seq_record_cmp(const uint32_t *key_seq, const uint32_t *data_seq) { return (int)*key_seq - (int)*data_seq; } bool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array, uint32_t seq, unsigned int *idx_r) { return array_bsearch_insert_pos(array, &seq, mail_index_seq_record_cmp, idx_r); } void mail_index_seq_array_alloc(ARRAY_TYPE(seq_array) *array, size_t record_size) { size_t aligned_record_size = (record_size + 3) & ~3; i_assert(!array_is_created(array)); array_create(array, default_pool, sizeof(uint32_t) + aligned_record_size, 1024 / (sizeof(uint32_t) + aligned_record_size)); } bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq, const void *record, size_t record_size, void *old_record) { void *p; unsigned int idx, aligned_record_size; /* records need to be 32bit aligned */ aligned_record_size = (record_size + 3) & ~3; if (!array_is_created(array)) mail_index_seq_array_alloc(array, record_size); i_assert(array->arr.element_size == sizeof(seq) + aligned_record_size); if (mail_index_seq_array_lookup(array, seq, &idx)) { /* already there, update */ p = array_idx_modifiable(array, idx); if (old_record != NULL) { /* save the old record before overwriting it */ memcpy(old_record, PTR_OFFSET(p, sizeof(seq)), record_size); } memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size); return TRUE; } else { /* insert */ p = array_insert_space(array, idx); memcpy(p, &seq, sizeof(seq)); memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size); return FALSE; } } dovecot-2.2.33.2/src/lib-index/mail-index-view-private.h0000644000175000017500000000656113165463624017631 00000000000000#ifndef MAIL_INDEX_VIEW_PRIVATE_H #define MAIL_INDEX_VIEW_PRIVATE_H #include "mail-index-private.h" struct mail_index_view_log_sync_area { uint32_t log_file_seq; unsigned int length; uoff_t log_file_offset; }; ARRAY_DEFINE_TYPE(view_log_sync_area, struct mail_index_view_log_sync_area); struct mail_index_view_vfuncs { void (*close)(struct mail_index_view *view); uint32_t (*get_messages_count)(struct mail_index_view *view); const struct mail_index_header * (*get_header)(struct mail_index_view *view); const struct mail_index_record * (*lookup_full)(struct mail_index_view *view, uint32_t seq, struct mail_index_map **map_r, bool *expunged_r); void (*lookup_uid)(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r); void (*lookup_seq_range)(struct mail_index_view *view, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r); void (*lookup_first)(struct mail_index_view *view, enum mail_flags flags, uint8_t flags_mask, uint32_t *seq_r); void (*lookup_keywords)(struct mail_index_view *view, uint32_t seq, ARRAY_TYPE(keyword_indexes) *keyword_idx); void (*lookup_ext_full)(struct mail_index_view *view, uint32_t seq, uint32_t ext_id, struct mail_index_map **map_r, const void **data_r, bool *expunged_r); void (*get_header_ext)(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, const void **data_r, size_t *data_size_r); bool (*ext_get_reset_id)(struct mail_index_view *view, struct mail_index_map *map, uint32_t ext_id, uint32_t *reset_id_r); }; union mail_index_view_module_context { struct mail_index_module_register *reg; }; struct mail_index_view { struct mail_index_view *prev, *next; int refcount; struct mail_index_view_vfuncs v; struct mail_index *index; struct mail_transaction_log_view *log_view; uint32_t indexid; unsigned int inconsistency_id; uint64_t highest_modseq; struct mail_index_map *map; /* All mappings where we have returned records. They need to be kept valid until view is synchronized. */ ARRAY(struct mail_index_map *) map_refs; /* expunge <= head */ uint32_t log_file_expunge_seq, log_file_head_seq; uoff_t log_file_expunge_offset, log_file_head_offset; /* Transaction log offsets which we don't want to return in view sync */ ARRAY_TYPE(view_log_sync_area) syncs_hidden; /* Module-specific contexts. */ ARRAY(union mail_index_view_module_context *) module_contexts; struct mail_index_transaction *transactions_list; int transactions; unsigned int inconsistent:1; /* this view was created by mail_index_sync_begin() */ unsigned int index_sync_view:1; /* this view is being synced */ unsigned int syncing:1; }; struct mail_index_view * mail_index_view_open_with_map(struct mail_index *index, struct mail_index_map *map); void mail_index_view_clone(struct mail_index_view *dest, const struct mail_index_view *src); struct mail_index_view * mail_index_view_dup_private(const struct mail_index_view *src); void mail_index_view_ref(struct mail_index_view *view); void mail_index_view_unref_maps(struct mail_index_view *view); void mail_index_view_add_hidden_transaction(struct mail_index_view *view, uint32_t log_file_seq, uoff_t log_file_offset, unsigned int length); struct mail_index_view *mail_index_dummy_view_open(struct mail_index *index); #endif dovecot-2.2.33.2/src/lib-index/mail-index-dummy-view.c0000644000175000017500000000170313123174404017263 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-private.h" #include "mail-index-view-private.h" static void dummy_view_close(struct mail_index_view *view ATTR_UNUSED) { i_assert(view->refcount == 0); array_free(&view->module_contexts); i_free(view); } static uint32_t dummy_view_get_message_count(struct mail_index_view *view ATTR_UNUSED) { return (uint32_t)-3; } static struct mail_index_view_vfuncs dummy_view_vfuncs = { dummy_view_close, dummy_view_get_message_count, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct mail_index_view *mail_index_dummy_view_open(struct mail_index *index) { struct mail_index_view *view; view = i_new(struct mail_index_view, 1); view->refcount = 1; view->v = dummy_view_vfuncs; view->index = index; i_array_init(&view->module_contexts, I_MIN(5, mail_index_module_register.id)); return view; } dovecot-2.2.33.2/src/lib-index/mail-transaction-log.h0000644000175000017500000002635013165463624017204 00000000000000#ifndef MAIL_TRANSACTION_LOG_H #define MAIL_TRANSACTION_LOG_H #include "mail-index.h" #define MAIL_TRANSACTION_LOG_SUFFIX ".log" #define MAIL_TRANSACTION_LOG_MAJOR_VERSION 1 #define MAIL_TRANSACTION_LOG_MINOR_VERSION 3 #define MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE 24 #define MAIL_TRANSACTION_LOG_VERSION_FULL(major, minor) \ ((major) << 8 | (minor)) #define MAIL_TRANSACTION_LOG_VERSION_HAVE(version, wanted_feature) \ ((version) >= MAIL_TRANSACTION_LOG_VERSION_##wanted_feature) #define MAIL_TRANSACTION_LOG_HDR_VERSION(hdr) \ MAIL_TRANSACTION_LOG_VERSION_FULL((hdr)->major_version, (hdr)->minor_version) #define MAIL_TRANSACTION_LOG_VERSION_COMPAT_FLAGS \ MAIL_TRANSACTION_LOG_VERSION_FULL(1, 2) #define MAIL_TRANSACTION_LOG_VERSION_HIDE_INTERNAL_MODSEQS \ MAIL_TRANSACTION_LOG_VERSION_FULL(1, 3) struct mail_transaction_log_header { uint8_t major_version; uint8_t minor_version; uint16_t hdr_size; uint32_t indexid; uint32_t file_seq; uint32_t prev_file_seq; uint32_t prev_file_offset; uint32_t create_stamp; uint64_t initial_modseq; /* v1.1+ (note: log's major/minor version) */ uint8_t compat_flags; /* enum mail_index_header_compat_flags, v1.2+ */ uint8_t unused[3]; uint32_t unused2; /* so that this struct is 64bit aligned */ }; enum mail_transaction_type { MAIL_TRANSACTION_EXPUNGE = 0x00000001, MAIL_TRANSACTION_APPEND = 0x00000002, MAIL_TRANSACTION_FLAG_UPDATE = 0x00000004, MAIL_TRANSACTION_HEADER_UPDATE = 0x00000020, MAIL_TRANSACTION_EXT_INTRO = 0x00000040, MAIL_TRANSACTION_EXT_RESET = 0x00000080, MAIL_TRANSACTION_EXT_HDR_UPDATE = 0x00000100, MAIL_TRANSACTION_EXT_REC_UPDATE = 0x00000200, MAIL_TRANSACTION_KEYWORD_UPDATE = 0x00000400, MAIL_TRANSACTION_KEYWORD_RESET = 0x00000800, MAIL_TRANSACTION_EXT_ATOMIC_INC = 0x00001000, MAIL_TRANSACTION_EXPUNGE_GUID = 0x00002000, MAIL_TRANSACTION_MODSEQ_UPDATE = 0x00008000, MAIL_TRANSACTION_EXT_HDR_UPDATE32 = 0x00010000, MAIL_TRANSACTION_INDEX_DELETED = 0x00020000, MAIL_TRANSACTION_INDEX_UNDELETED = 0x00040000, MAIL_TRANSACTION_BOUNDARY = 0x00080000, MAIL_TRANSACTION_ATTRIBUTE_UPDATE = 0x00100000, MAIL_TRANSACTION_TYPE_MASK = 0x0fffffff, #define MAIL_TRANSACTION_EXT_MASK \ (MAIL_TRANSACTION_EXT_INTRO | MAIL_TRANSACTION_EXT_RESET | \ MAIL_TRANSACTION_EXT_HDR_UPDATE | MAIL_TRANSACTION_EXT_HDR_UPDATE32 | \ MAIL_TRANSACTION_EXT_REC_UPDATE | MAIL_TRANSACTION_EXT_ATOMIC_INC) /* since we'll expunge mails based on data read from transaction log, try to avoid the possibility of corrupted transaction log expunging messages. this value is ORed to the actual MAIL_TRANSACTION_EXPUNGE* flag. if it's not present, assume corrupted log. */ MAIL_TRANSACTION_EXPUNGE_PROT = 0x0000cd90, /* Mailbox storage backend synchronization noticed this change. */ MAIL_TRANSACTION_EXTERNAL = 0x10000000, /* This change syncs the state with another mailbox (dsync), i.e. the change isn't something that a user requested locally. */ MAIL_TRANSACTION_SYNC = 0x20000000 }; struct mail_transaction_header { uint32_t size; uint32_t type; /* enum mail_transaction_type */ }; struct mail_transaction_modseq_update { uint32_t uid; /* don't use uint64_t here. it adds extra 32 bits of paddiong and also causes problems with CPUs that require alignment */ uint32_t modseq_low32; uint32_t modseq_high32; }; struct mail_transaction_expunge { uint32_t uid1, uid2; }; struct mail_transaction_expunge_guid { uint32_t uid; guid_128_t guid_128; }; struct mail_transaction_flag_update { uint32_t uid1, uid2; uint8_t add_flags; uint8_t remove_flags; uint8_t modseq_inc_flag; uint8_t padding; }; struct mail_transaction_keyword_update { uint8_t modify_type; /* enum modify_type : MODIFY_ADD / MODIFY_REMOVE */ uint8_t padding; uint16_t name_size; /* unsigned char name[]; array of { uint32_t uid1, uid2; } */ }; struct mail_transaction_keyword_reset { uint32_t uid1, uid2; }; struct mail_transaction_header_update { uint16_t offset; uint16_t size; /* unsigned char data[]; */ }; enum { /* Don't shrink hdr_size, record_size or record_align but grow them if necessary. */ MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK = 0x01 }; struct mail_transaction_ext_intro { /* old extension: set ext_id. don't set name. new extension: ext_id = (uint32_t)-1. give name. */ uint32_t ext_id; uint32_t reset_id; uint32_t hdr_size; uint16_t record_size; uint16_t record_align; uint16_t flags; uint16_t name_size; /* unsigned char name[]; */ }; struct mail_transaction_ext_reset { uint32_t new_reset_id; uint8_t preserve_data; uint8_t unused_padding[3]; }; /* these are set for the last ext_intro */ struct mail_transaction_ext_hdr_update { uint16_t offset; uint16_t size; /* unsigned char data[]; */ }; /* this _update32 version should have been the only ext_hdr_update, but since 16bit integers were originally used for now we'll just use this only when actually needed to be backwards compatible. */ struct mail_transaction_ext_hdr_update32 { uint32_t offset; uint32_t size; /* unsigned char data[]; */ }; struct mail_transaction_ext_rec_update { uint32_t uid; /* unsigned char data[]; */ }; struct mail_transaction_ext_atomic_inc { uint32_t uid; int32_t diff; }; struct mail_transaction_boundary { uint32_t size; }; struct mail_transaction_log_append_ctx { struct mail_transaction_log *log; buffer_t *output; enum mail_transaction_type trans_flags; uint64_t new_highest_modseq; unsigned int transaction_count; /* same as mail_index_transaction->sync_transaction */ unsigned int index_sync_transaction:1; /* same as mail_index_transaction->tail_offset_changed */ unsigned int tail_offset_changed:1; unsigned int sync_includes_this:1; unsigned int want_fsync:1; }; #define LOG_IS_BEFORE(seq1, offset1, seq2, offset2) \ (((offset1) < (offset2) && (seq1) == (seq2)) || (seq1) < (seq2)) struct mail_transaction_log * mail_transaction_log_alloc(struct mail_index *index); void mail_transaction_log_free(struct mail_transaction_log **log); /* Open the transaction log. Returns 1 if ok, 0 if file doesn't exist or it's is corrupted, -1 if there was some I/O error. */ int mail_transaction_log_open(struct mail_transaction_log *log); /* Create, or recreate, the transaction log. Returns 0 if ok, -1 if error. */ int mail_transaction_log_create(struct mail_transaction_log *log, bool reset); /* Close all the open transactions log files. */ void mail_transaction_log_close(struct mail_transaction_log *log); /* Notify of indexid change */ void mail_transaction_log_indexid_changed(struct mail_transaction_log *log); /* Returns the file seq/offset where the mailbox is currently synced at. Since the log is rotated only when mailbox is fully synced, the sequence points always to the latest file. This function doesn't actually find the latest sync position, so you'll need to use eg. log_view_set() before calling this. */ void mail_transaction_log_get_mailbox_sync_pos(struct mail_transaction_log *log, uint32_t *file_seq_r, uoff_t *file_offset_r); /* Set the current mailbox sync position. file_seq must always be the latest log file's sequence. The offset written automatically to the log when other transactions are being written. */ void mail_transaction_log_set_mailbox_sync_pos(struct mail_transaction_log *log, uint32_t file_seq, uoff_t file_offset); struct mail_transaction_log_view * mail_transaction_log_view_open(struct mail_transaction_log *log); void mail_transaction_log_view_close(struct mail_transaction_log_view **view); /* Set view boundaries. Returns -1 if error, 0 if files are lost or corrupted, 1 if ok. reset_r=TRUE if the whole index should be reset before applying any changes. */ int mail_transaction_log_view_set(struct mail_transaction_log_view *view, uint32_t min_file_seq, uoff_t min_file_offset, uint32_t max_file_seq, uoff_t max_file_offset, bool *reset_r, const char **reason_r); /* Scan through all of the log files that we can find. Returns -1 if error, 0 if ok. */ int mail_transaction_log_view_set_all(struct mail_transaction_log_view *view); /* Clear the view. If oldest_file_seq > 0, keep it and newer log files referenced so we don't get desynced. */ void mail_transaction_log_view_clear(struct mail_transaction_log_view *view, uint32_t oldest_file_seq); /* Read next transaction record from current position. The position is updated. Returns -1 if error, 0 if we're at end of the view, 1 if ok. */ int mail_transaction_log_view_next(struct mail_transaction_log_view *view, const struct mail_transaction_header **hdr_r, const void **data_r); /* Mark the current view's position to the record returned previously with _log_view_next(). */ void mail_transaction_log_view_mark(struct mail_transaction_log_view *view); /* Seek to previously marked position. */ void mail_transaction_log_view_rewind(struct mail_transaction_log_view *view); /* Returns the position of the record returned previously with mail_transaction_log_view_next() */ void mail_transaction_log_view_get_prev_pos(struct mail_transaction_log_view *view, uint32_t *file_seq_r, uoff_t *file_offset_r); /* Return the modseq of the change returned previously with _view_next(). */ uint64_t mail_transaction_log_view_get_prev_modseq(struct mail_transaction_log_view *view); /* Returns TRUE if we're at the end of the view window. */ bool mail_transaction_log_view_is_last(struct mail_transaction_log_view *view); /* Marks the log file in current position to be corrupted. */ void mail_transaction_log_view_set_corrupted(struct mail_transaction_log_view *view, const char *fmt, ...) ATTR_FORMAT(2, 3); bool mail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view); int mail_transaction_log_append_begin(struct mail_index *index, enum mail_transaction_type flags, struct mail_transaction_log_append_ctx **ctx_r); void mail_transaction_log_append_add(struct mail_transaction_log_append_ctx *ctx, enum mail_transaction_type type, const void *data, size_t size); int mail_transaction_log_append_commit(struct mail_transaction_log_append_ctx **ctx); /* Lock transaction log for index synchronization. Log cannot be read or written to while it's locked. Returns end offset. */ int mail_transaction_log_sync_lock(struct mail_transaction_log *log, const char *lock_reason, uint32_t *file_seq_r, uoff_t *file_offset_r); void mail_transaction_log_sync_unlock(struct mail_transaction_log *log, const char *lock_reason); /* Returns the current head. Works only when log is locked. */ void mail_transaction_log_get_head(struct mail_transaction_log *log, uint32_t *file_seq_r, uoff_t *file_offset_r); /* Returns the current tail from which all files are open to head. */ void mail_transaction_log_get_tail(struct mail_transaction_log *log, uint32_t *file_seq_r); /* Returns TRUE if given seq/offset is current head log's rotate point. */ bool mail_transaction_log_is_head_prev(struct mail_transaction_log *log, uint32_t file_seq, uoff_t file_offset); /* Move currently opened log head file to memory (called by mail_index_move_to_memory()) */ void mail_transaction_log_move_to_memory(struct mail_transaction_log *log); /* Unlink transaction log files */ int mail_transaction_log_unlink(struct mail_transaction_log *log); #endif dovecot-2.2.33.2/src/lib-index/mail-index-write.c0000644000175000017500000001066213144653606016327 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "read-full.h" #include "write-full.h" #include "ostream.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include #define MAIL_INDEX_MIN_UPDATE_SIZE 1024 /* if we're updating >= count-n messages, recreate the index */ #define MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT 10 static int mail_index_create_backup(struct mail_index *index) { const char *backup_path, *tmp_backup_path; int ret; if (index->fd != -1) { /* we very much want to avoid creating a backup file that hasn't been written to disk yet */ if (fdatasync(index->fd) < 0) { mail_index_set_error(index, "fdatasync(%s) failed: %m", index->filepath); return -1; } } backup_path = t_strconcat(index->filepath, ".backup", NULL); tmp_backup_path = t_strconcat(backup_path, ".tmp", NULL); ret = link(index->filepath, tmp_backup_path); if (ret < 0 && errno == EEXIST) { if (unlink(tmp_backup_path) < 0 && errno != ENOENT) { mail_index_set_error(index, "unlink(%s) failed: %m", tmp_backup_path); return -1; } ret = link(index->filepath, tmp_backup_path); } if (ret < 0) { if (errno == ENOENT) { /* no dovecot.index file, ignore */ return 0; } mail_index_set_error(index, "link(%s, %s) failed: %m", index->filepath, tmp_backup_path); return -1; } if (rename(tmp_backup_path, backup_path) < 0) { mail_index_set_error(index, "rename(%s, %s) failed: %m", tmp_backup_path, backup_path); return -1; } return 0; } static int mail_index_recreate(struct mail_index *index) { struct mail_index_map *map = index->map; struct ostream *output; unsigned int base_size; const char *path; int ret = 0, fd; i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); i_assert(map->hdr.indexid == index->indexid); i_assert((map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) == 0); i_assert(index->indexid != 0); fd = mail_index_create_tmp_file(index, index->filepath, &path); if (fd == -1) return -1; output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_cork(output); base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr)); o_stream_nsend(output, &map->hdr, base_size); o_stream_nsend(output, CONST_PTR_OFFSET(map->hdr_base, base_size), map->hdr.header_size - base_size); o_stream_nsend(output, map->rec_map->records, map->rec_map->records_count * map->hdr.record_size); o_stream_nflush(output); if (o_stream_nfinish(output) < 0) { mail_index_file_set_syscall_error(index, path, "write()"); ret = -1; } o_stream_destroy(&output); if (ret == 0 && index->fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync(fd) < 0) { mail_index_file_set_syscall_error(index, path, "fdatasync()"); ret = -1; } } if (close(fd) < 0) { mail_index_file_set_syscall_error(index, path, "close()"); ret = -1; } if ((index->flags & MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS) != 0) (void)mail_index_create_backup(index); if (ret == 0 && rename(path, index->filepath) < 0) { mail_index_set_error(index, "rename(%s, %s) failed: %m", path, index->filepath); ret = -1; } if (ret < 0) i_unlink(path); return ret; } void mail_index_write(struct mail_index *index, bool want_rotate) { struct mail_index_map *map = index->map; struct mail_index_header *hdr = &map->hdr; i_assert(index->log_sync_locked); if (index->readonly) return; /* rotate the .log before writing index, so the index will point to the latest log. */ if (want_rotate && hdr->log_file_seq == index->log->head->hdr.file_seq && hdr->log_file_tail_offset == hdr->log_file_head_offset) { if (mail_transaction_log_rotate(index->log, FALSE) == 0) { struct mail_transaction_log_file *file = index->log->head; i_assert(file->hdr.prev_file_seq == hdr->log_file_seq); i_assert(file->hdr.prev_file_offset == hdr->log_file_head_offset); hdr->log_file_seq = file->hdr.file_seq; hdr->log_file_head_offset = hdr->log_file_tail_offset = file->hdr.hdr_size; /* Assume .log.2 was created successfully. If it wasn't, it just causes an extra stat() and gets fixed later on. */ hdr->log2_rotate_time = ioloop_time; } } if (!MAIL_INDEX_IS_IN_MEMORY(index)) { if (mail_index_recreate(index) < 0) { (void)mail_index_move_to_memory(index); return; } } index->last_read_log_file_seq = hdr->log_file_seq; index->last_read_log_file_tail_offset = hdr->log_file_tail_offset; } dovecot-2.2.33.2/src/lib-index/mail-cache-fields.c0000644000175000017500000004144613123174404016372 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "hash.h" #include "file-cache.h" #include "read-full.h" #include "write-full.h" #include "mmap-util.h" #include "mail-cache-private.h" #include #define CACHE_FIELD_IS_NEWLY_WANTED(cache, field_idx) \ ((cache)->field_file_map[field_idx] == (uint32_t)-1 && \ (cache)->fields[field_idx].used) static bool field_has_fixed_size(enum mail_cache_field_type type) { switch (type) { case MAIL_CACHE_FIELD_FIXED_SIZE: case MAIL_CACHE_FIELD_BITMASK: return TRUE; case MAIL_CACHE_FIELD_VARIABLE_SIZE: case MAIL_CACHE_FIELD_STRING: case MAIL_CACHE_FIELD_HEADER: return FALSE; case MAIL_CACHE_FIELD_COUNT: break; } i_unreached(); return FALSE; } static bool field_decision_is_valid(enum mail_cache_decision_type type) { switch (type & ~MAIL_CACHE_DECISION_FORCED) { case MAIL_CACHE_DECISION_NO: case MAIL_CACHE_DECISION_TEMP: case MAIL_CACHE_DECISION_YES: return TRUE; default: return FALSE; } } static int field_type_verify(struct mail_cache *cache, unsigned int idx, enum mail_cache_field_type type, unsigned int size) { const struct mail_cache_field *field = &cache->fields[idx].field; if (field->type != type) { mail_cache_set_corrupted(cache, "registered field %s type changed", field->name); return -1; } if (field->field_size != size && field_has_fixed_size(type)) { mail_cache_set_corrupted(cache, "registered field %s size changed", field->name); return -1; } return 0; } static void mail_cache_field_update(struct mail_cache *cache, const struct mail_cache_field *newfield) { struct mail_cache_field_private *orig; bool initial_registering; i_assert(newfield->type < MAIL_CACHE_FIELD_COUNT); /* are we still doing the initial cache field registering for internal fields and for mail_*cache_fields settings? */ initial_registering = cache->file_fields_count == 0; orig = &cache->fields[newfield->idx]; if ((newfield->decision & MAIL_CACHE_DECISION_FORCED) != 0 || ((orig->field.decision & MAIL_CACHE_DECISION_FORCED) == 0 && newfield->decision > orig->field.decision)) { orig->field.decision = newfield->decision; if (!initial_registering) orig->decision_dirty = TRUE; } if (orig->field.last_used < newfield->last_used) { orig->field.last_used = newfield->last_used; if (!initial_registering) orig->decision_dirty = TRUE; } if (orig->decision_dirty) cache->field_header_write_pending = TRUE; (void)field_type_verify(cache, newfield->idx, newfield->type, newfield->field_size); } void mail_cache_register_fields(struct mail_cache *cache, struct mail_cache_field *fields, unsigned int fields_count) { char *name; void *value; unsigned int new_idx; unsigned int i, j, registered_count; new_idx = cache->fields_count; for (i = 0; i < fields_count; i++) { if (hash_table_lookup_full(cache->field_name_hash, fields[i].name, &name, &value)) { fields[i].idx = POINTER_CAST_TO(value, unsigned int); mail_cache_field_update(cache, &fields[i]); continue; } /* check if the same header is being registered in the same field array */ for (j = 0; j < i; j++) { if (strcasecmp(fields[i].name, fields[j].name) == 0) { fields[i].idx = fields[j].idx; break; } } if (j == i) fields[i].idx = new_idx++; } if (new_idx == cache->fields_count) return; /* @UNSAFE */ cache->fields = i_realloc_type(cache->fields, struct mail_cache_field_private, cache->fields_count, new_idx); cache->field_file_map = i_realloc_type(cache->field_file_map, uint32_t, cache->fields_count, new_idx); registered_count = cache->fields_count; for (i = 0; i < fields_count; i++) { unsigned int idx = fields[i].idx; if (idx < registered_count) continue; /* new index - save it */ name = p_strdup(cache->field_pool, fields[i].name); cache->fields[idx].field = fields[i]; cache->fields[idx].field.name = name; cache->fields[idx].field.last_used = fields[i].last_used; cache->field_file_map[idx] = (uint32_t)-1; if (!field_has_fixed_size(cache->fields[idx].field.type)) cache->fields[idx].field.field_size = UINT_MAX; hash_table_insert(cache->field_name_hash, name, POINTER_CAST(idx)); registered_count++; } i_assert(registered_count == new_idx); cache->fields_count = new_idx; } unsigned int mail_cache_register_lookup(struct mail_cache *cache, const char *name) { char *key; void *value; if (hash_table_lookup_full(cache->field_name_hash, name, &key, &value)) return POINTER_CAST_TO(value, unsigned int); else return UINT_MAX; } const struct mail_cache_field * mail_cache_register_get_field(struct mail_cache *cache, unsigned int field_idx) { i_assert(field_idx < cache->fields_count); return &cache->fields[field_idx].field; } struct mail_cache_field * mail_cache_register_get_list(struct mail_cache *cache, pool_t pool, unsigned int *count_r) { struct mail_cache_field *list; unsigned int i; if (!cache->opened) (void)mail_cache_open_and_verify(cache); list = cache->fields_count == 0 ? NULL : p_new(pool, struct mail_cache_field, cache->fields_count); for (i = 0; i < cache->fields_count; i++) { list[i] = cache->fields[i].field; list[i].name = p_strdup(pool, list[i].name); } *count_r = cache->fields_count; return list; } static int mail_cache_header_fields_get_offset(struct mail_cache *cache, uint32_t *offset_r, const struct mail_cache_header_fields **field_hdr_r) { const struct mail_cache_header_fields *field_hdr; struct mail_cache_header_fields tmp_field_hdr; const void *data; uint32_t offset = 0, next_offset, field_hdr_size; unsigned int next_count = 0; int ret; if (MAIL_CACHE_IS_UNUSABLE(cache)) { *offset_r = 0; if (field_hdr_r != NULL) *field_hdr_r = NULL; return 0; } /* find the latest header */ offset = 0; next_offset = cache->last_field_header_offset != 0 ? cache->last_field_header_offset : mail_index_offset_to_uint32(cache->hdr->field_header_offset); while (next_offset != 0) { if (next_offset == offset) { mail_cache_set_corrupted(cache, "next_offset in field header loops"); return -1; } /* In Dovecot v2.2+ we don't try to use any holes, so next_offset must always be larger than current offset. also makes it easier to guarantee there aren't any loops (which we don't bother doing for old files) */ if (next_offset < offset && cache->hdr->minor_version != 0) { mail_cache_set_corrupted(cache, "next_offset in field header decreases"); return -1; } offset = next_offset; if (cache->mmap_base != NULL || cache->map_with_read) { ret = mail_cache_map(cache, offset, sizeof(*field_hdr), &data); if (ret <= 0) { if (ret < 0) return -1; mail_cache_set_corrupted(cache, "header field next_offset points outside file"); return -1; } field_hdr = data; } else { /* if we need to follow multiple offsets to get to the last one, it's faster to just pread() the file instead of going through cache */ ret = pread_full(cache->fd, &tmp_field_hdr, sizeof(tmp_field_hdr), offset); if (ret < 0) { mail_cache_set_syscall_error(cache, "pread()"); return -1; } if (ret == 0) { mail_cache_set_corrupted(cache, "header field next_offset points outside file"); return -1; } field_hdr = &tmp_field_hdr; } next_offset = mail_index_offset_to_uint32(field_hdr->next_offset); next_count++; } if (offset == 0) { mail_cache_set_corrupted(cache, "missing header fields"); return -1; } cache->last_field_header_offset = offset; if (next_count > MAIL_CACHE_HEADER_FIELD_CONTINUE_COUNT) cache->need_compress_file_seq = cache->hdr->file_seq; if (field_hdr_r != NULL) { /* detect corrupted size later */ field_hdr_size = I_MAX(field_hdr->size, sizeof(*field_hdr)); if (cache->file_cache != NULL) { /* invalidate the cache fields area to make sure we get the latest cache decisions/last_used fields */ file_cache_invalidate(cache->file_cache, offset, field_hdr_size); } if (cache->read_buf != NULL) buffer_set_used_size(cache->read_buf, 0); ret = mail_cache_map(cache, offset, field_hdr_size, &data); if (ret < 0) return -1; if (ret == 0) { mail_cache_set_corrupted(cache, "header field size outside file"); return -1; } *field_hdr_r = data; } *offset_r = offset; return 0; } int mail_cache_header_fields_read(struct mail_cache *cache) { const struct mail_cache_header_fields *field_hdr; struct mail_cache_field field; const uint32_t *last_used, *sizes; const uint8_t *types, *decisions; const char *p, *names, *end; char *orig_key; void *orig_value; unsigned int fidx, new_fields_count; enum mail_cache_decision_type dec; time_t max_drop_time; uint32_t offset, i; if (mail_cache_header_fields_get_offset(cache, &offset, &field_hdr) < 0) return -1; if (offset == 0) { /* no fields - the file is empty */ return 0; } /* check the fixed size of the header. name[] has to be checked separately */ if (field_hdr->fields_count > INT_MAX / MAIL_CACHE_FIELD_NAMES(1) || field_hdr->size < MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count)) { mail_cache_set_corrupted(cache, "invalid field header size"); return -1; } new_fields_count = field_hdr->fields_count; if (new_fields_count != 0) { cache->file_field_map = i_realloc_type(cache->file_field_map, unsigned int, cache->file_fields_count, new_fields_count); } else { i_free_and_null(cache->file_field_map); } cache->file_fields_count = new_fields_count; last_used = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_LAST_USED()); sizes = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_SIZE(field_hdr->fields_count)); types = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_TYPE(field_hdr->fields_count)); decisions = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_DECISION(field_hdr->fields_count)); names = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count)); end = CONST_PTR_OFFSET(field_hdr, field_hdr->size); i_assert(names <= end); /* clear the old mapping */ for (i = 0; i < cache->fields_count; i++) cache->field_file_map[i] = (uint32_t)-1; max_drop_time = cache->index->map->hdr.day_stamp == 0 ? 0 : cache->index->map->hdr.day_stamp - MAIL_CACHE_FIELD_DROP_SECS; i_zero(&field); for (i = 0; i < field_hdr->fields_count; i++) { for (p = names; p != end && *p != '\0'; p++) ; if (p == end || *names == '\0') { mail_cache_set_corrupted(cache, "field header names corrupted"); return -1; } if (types[i] > MAIL_CACHE_FIELD_COUNT) { mail_cache_set_corrupted(cache, "field type corrupted"); return -1; } if (!field_decision_is_valid(decisions[i])) { mail_cache_set_corrupted(cache, "field decision type corrupted"); return -1; } if (hash_table_lookup_full(cache->field_name_hash, names, &orig_key, &orig_value)) { /* already exists, see if decision can be updated */ fidx = POINTER_CAST_TO(orig_value, unsigned int); if (!cache->fields[fidx].decision_dirty) { cache->fields[fidx].field.decision = decisions[i]; } if (field_type_verify(cache, fidx, types[i], sizes[i]) < 0) return -1; } else { field.name = names; field.type = types[i]; field.field_size = sizes[i]; field.decision = decisions[i]; mail_cache_register_fields(cache, &field, 1); fidx = field.idx; } if (cache->field_file_map[fidx] != (uint32_t)-1) { mail_cache_set_corrupted(cache, "Duplicated field in header: %s", names); return -1; } cache->fields[fidx].used = TRUE; cache->field_file_map[fidx] = i; cache->file_field_map[i] = fidx; /* update last_used if it's newer than ours */ if ((time_t)last_used[i] > cache->fields[fidx].field.last_used) cache->fields[fidx].field.last_used = last_used[i]; dec = cache->fields[fidx].field.decision; if (cache->fields[fidx].field.last_used < max_drop_time && cache->fields[fidx].field.last_used != 0 && (dec & MAIL_CACHE_DECISION_FORCED) == 0 && dec != MAIL_CACHE_DECISION_NO) { /* time to drop this field. don't bother dropping fields that have never been used. */ cache->need_compress_file_seq = cache->hdr->file_seq; } names = p + 1; } return 0; } static void copy_to_buf(struct mail_cache *cache, buffer_t *dest, bool add_new, size_t offset, size_t size) { const void *data; unsigned int i, field; /* copy the existing fields */ for (i = 0; i < cache->file_fields_count; i++) { field = cache->file_field_map[i]; data = CONST_PTR_OFFSET(&cache->fields[field], offset); buffer_append(dest, data, size); } if (!add_new) return; /* copy newly wanted fields */ for (i = 0; i < cache->fields_count; i++) { if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) { data = CONST_PTR_OFFSET(&cache->fields[i], offset); buffer_append(dest, data, size); } } } static void copy_to_buf_byte(struct mail_cache *cache, buffer_t *dest, bool add_new, size_t offset) { const int *data; unsigned int i, field; uint8_t byte; /* copy the existing fields */ for (i = 0; i < cache->file_fields_count; i++) { field = cache->file_field_map[i]; data = CONST_PTR_OFFSET(&cache->fields[field], offset); byte = (uint8_t)*data; buffer_append(dest, &byte, 1); } if (!add_new) return; /* copy newly wanted fields */ for (i = 0; i < cache->fields_count; i++) { if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) { data = CONST_PTR_OFFSET(&cache->fields[i], offset); byte = (uint8_t)*data; buffer_append(dest, &byte, 1); } } } static int mail_cache_header_fields_update_locked(struct mail_cache *cache) { buffer_t *buffer; uint32_t i, offset, dec_offset; int ret = 0; if (mail_cache_header_fields_read(cache) < 0 || mail_cache_header_fields_get_offset(cache, &offset, NULL) < 0) return -1; buffer = buffer_create_dynamic(pool_datastack_create(), 256); copy_to_buf(cache, buffer, FALSE, offsetof(struct mail_cache_field, last_used), sizeof(uint32_t)); ret = mail_cache_write(cache, buffer->data, buffer->used, offset + MAIL_CACHE_FIELD_LAST_USED()); if (ret == 0) { buffer_set_used_size(buffer, 0); copy_to_buf_byte(cache, buffer, FALSE, offsetof(struct mail_cache_field, decision)); dec_offset = offset + MAIL_CACHE_FIELD_DECISION(cache->file_fields_count); ret = mail_cache_write(cache, buffer->data, buffer->used, dec_offset); if (ret == 0) { for (i = 0; i < cache->file_fields_count; i++) cache->fields[i].decision_dirty = FALSE; } } if (ret == 0) cache->field_header_write_pending = FALSE; return ret; } int mail_cache_header_fields_update(struct mail_cache *cache) { int ret; if (cache->locked) { T_BEGIN { ret = mail_cache_header_fields_update_locked(cache); } T_END; return ret; } if (mail_cache_lock(cache) <= 0) return -1; T_BEGIN { ret = mail_cache_header_fields_update_locked(cache); } T_END; if (mail_cache_unlock(cache) < 0) ret = -1; return ret; } void mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest) { struct mail_cache_header_fields hdr; unsigned int field; const char *name; uint32_t i; i_zero(&hdr); hdr.fields_count = cache->file_fields_count; for (i = 0; i < cache->fields_count; i++) { if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) hdr.fields_count++; } buffer_append(dest, &hdr, sizeof(hdr)); /* we have to keep the field order for the existing fields. */ copy_to_buf(cache, dest, TRUE, offsetof(struct mail_cache_field, last_used), sizeof(uint32_t)); copy_to_buf(cache, dest, TRUE, offsetof(struct mail_cache_field, field_size), sizeof(uint32_t)); copy_to_buf_byte(cache, dest, TRUE, offsetof(struct mail_cache_field, type)); copy_to_buf_byte(cache, dest, TRUE, offsetof(struct mail_cache_field, decision)); i_assert(dest->used == sizeof(hdr) + (sizeof(uint32_t)*2 + 2) * hdr.fields_count); /* add existing fields' names */ for (i = 0; i < cache->file_fields_count; i++) { field = cache->file_field_map[i]; name = cache->fields[field].field.name; buffer_append(dest, name, strlen(name)+1); } /* add newly wanted fields' names */ for (i = 0; i < cache->fields_count; i++) { if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) { name = cache->fields[i].field.name; buffer_append(dest, name, strlen(name)+1); } } hdr.size = dest->used; buffer_write(dest, 0, &hdr, sizeof(hdr)); if ((hdr.size & 3) != 0) buffer_append_zero(dest, 4 - (hdr.size & 3)); } int mail_cache_header_fields_get_next_offset(struct mail_cache *cache, uint32_t *offset_r) { if (mail_cache_header_fields_get_offset(cache, offset_r, NULL) < 0) return -1; if (*offset_r == 0) { *offset_r = offsetof(struct mail_cache_header, field_header_offset); } else { *offset_r += offsetof(struct mail_cache_header_fields, next_offset); } return 0; } dovecot-2.2.33.2/src/lib-index/mail-index-private.h0000644000175000017500000002705713165463624016664 00000000000000#ifndef MAIL_INDEX_PRIVATE_H #define MAIL_INDEX_PRIVATE_H #include "file-lock.h" #include "mail-index.h" #include "mail-index-util.h" #include "mail-index-view-private.h" #include "mail-index-transaction-private.h" #include struct mail_transaction_header; struct mail_transaction_log_view; struct mail_index_sync_map_ctx; /* How large index files to mmap() instead of reading to memory. */ #define MAIL_INDEX_MMAP_MIN_SIZE (1024*64) /* How many times to retry opening index files if read/fstat returns ESTALE. This happens with NFS when the file has been deleted (ie. index file was rewritten by another computer than us). */ #define MAIL_INDEX_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT /* Large extension header sizes are probably caused by file corruption, so try to catch them by limiting the header size. */ #define MAIL_INDEX_EXT_HEADER_MAX_SIZE (1024*1024*16-1) /* Write to main index file when bytes-to-be-read-from-log is between these values. */ #define MAIL_INDEX_MIN_WRITE_BYTES (1024*8) #define MAIL_INDEX_MAX_WRITE_BYTES (1024*128) #define MAIL_INDEX_IS_IN_MEMORY(index) \ ((index)->dir == NULL) #define MAIL_INDEX_MAP_IS_IN_MEMORY(map) \ ((map)->rec_map->mmap_base == NULL) #define MAIL_INDEX_MAP_IDX(map, idx) \ ((struct mail_index_record *) \ PTR_OFFSET((map)->rec_map->records, (idx) * (map)->hdr.record_size)) #define MAIL_INDEX_REC_AT_SEQ(map, seq) \ ((struct mail_index_record *) \ PTR_OFFSET((map)->rec_map->records, ((seq)-1) * (map)->hdr.record_size)) #define MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(u) \ ((((u)->add_flags | (u)->remove_flags) & MAIL_INDEX_FLAGS_MASK) == 0 && \ (u)->modseq_inc_flag == 0) #define MAIL_INDEX_EXT_KEYWORDS "keywords" typedef int mail_index_expunge_handler_t(struct mail_index_sync_map_ctx *ctx, uint32_t seq, const void *data, void **sync_context, void *context); typedef int mail_index_sync_handler_t(struct mail_index_sync_map_ctx *ctx, uint32_t seq, void *old_data, const void *new_data, void **context); typedef void mail_index_sync_lost_handler_t(struct mail_index *index); #define MAIL_INDEX_HEADER_SIZE_ALIGN(size) \ (((size) + 7) & ~7) struct mail_index_ext { const char *name; uint32_t index_idx; /* index ext_id */ uint32_t reset_id; uint32_t ext_offset; /* points to beginning of mail_index_ext_header */ uint32_t hdr_offset; /* points to mail_index_ext_header.data[] */ uint32_t hdr_size; /* size of mail_index_ext_header.data[] */ uint16_t record_offset; uint16_t record_size; uint16_t record_align; }; struct mail_index_ext_header { uint32_t hdr_size; /* size of data[] */ uint32_t reset_id; uint16_t record_offset; uint16_t record_size; uint16_t record_align; uint16_t name_size; /* unsigned char name[name_size] */ /* unsigned char data[hdr_size] (starting 64bit aligned) */ }; struct mail_index_keyword_header { uint32_t keywords_count; /* struct mail_index_keyword_header_rec[] */ /* char name[][] */ }; struct mail_index_keyword_header_rec { uint32_t unused; /* for backwards compatibility */ uint32_t name_offset; /* relative to beginning of name[] */ }; enum mail_index_sync_handler_type { MAIL_INDEX_SYNC_HANDLER_FILE = 0x01, MAIL_INDEX_SYNC_HANDLER_HEAD = 0x02, MAIL_INDEX_SYNC_HANDLER_VIEW = 0x04 }; struct mail_index_sync_handler { mail_index_sync_handler_t *callback; enum mail_index_sync_handler_type type; }; struct mail_index_registered_ext { const char *name; uint32_t index_idx; /* index ext_id */ uint32_t hdr_size; /* size of mail_index_ext_header.data[] */ uint16_t record_size; uint16_t record_align; struct mail_index_sync_handler sync_handler; mail_index_expunge_handler_t *expunge_handler; void *expunge_context; unsigned int expunge_handler_call_always:1; }; struct mail_index_record_map { ARRAY(struct mail_index_map *) maps; void *mmap_base; size_t mmap_size, mmap_used_size; buffer_t *buffer; void *records; /* struct mail_index_record[] */ unsigned int records_count; struct mail_index_map_modseq *modseq; uint32_t last_appended_uid; }; struct mail_index_map { struct mail_index *index; int refcount; struct mail_index_header hdr; const void *hdr_base; buffer_t *hdr_copy_buf; pool_t extension_pool; ARRAY(struct mail_index_ext) extensions; ARRAY(uint32_t) ext_id_map; /* index -> file */ ARRAY(unsigned int) keyword_idx_map; /* file -> index */ struct mail_index_record_map *rec_map; }; struct mail_index_module_register { unsigned int id; }; union mail_index_module_context { struct mail_index_module_register *reg; }; struct mail_index { char *dir, *prefix; struct mail_cache *cache; struct mail_transaction_log *log; unsigned int open_count; enum mail_index_open_flags flags; enum fsync_mode fsync_mode; enum mail_index_fsync_mask fsync_mask; mode_t mode; gid_t gid; char *gid_origin; uoff_t log_rotate_min_size, log_rotate_max_size; unsigned int log_rotate_min_created_ago_secs; unsigned int log_rotate_log2_stale_secs; uint32_t pending_log2_rotate_time; pool_t extension_pool; ARRAY(struct mail_index_registered_ext) extensions; uint32_t ext_hdr_init_id; void *ext_hdr_init_data; ARRAY(mail_index_sync_lost_handler_t *) sync_lost_handlers; char *filepath; int fd; struct mail_index_map *map; time_t last_mmap_error_time; uint32_t indexid; unsigned int inconsistency_id; /* last_read_log_file_* contains the seq/offsets we last read from the main index file's headers. these are used to figure out when the main index file should be updated. */ uint32_t last_read_log_file_seq; uint32_t last_read_log_file_tail_offset; /* transaction log head seq/offset when we last fscked */ uint32_t fsck_log_head_file_seq; uoff_t fsck_log_head_file_offset; /* syncing will update this if non-NULL */ struct mail_index_transaction_commit_result *sync_commit_result; enum file_lock_method lock_method; unsigned int max_lock_timeout_secs; pool_t keywords_pool; ARRAY_TYPE(keywords) keywords; HASH_TABLE(char *, void *) keywords_hash; /* name -> unsigned int idx */ uint32_t keywords_ext_id; uint32_t modseq_ext_id; struct mail_index_view *views; /* Module-specific contexts. */ ARRAY(union mail_index_module_context *) module_contexts; char *error; unsigned int nodiskspace:1; unsigned int index_lock_timeout:1; unsigned int index_delete_requested:1; /* next sync sets it deleted */ unsigned int index_deleted:1; /* no changes allowed anymore */ unsigned int log_sync_locked:1; unsigned int readonly:1; unsigned int mapping:1; unsigned int syncing:1; unsigned int need_recreate:1; unsigned int index_min_write:1; unsigned int modseqs_enabled:1; unsigned int initial_create:1; unsigned int initial_mapped:1; unsigned int fscked:1; }; extern struct mail_index_module_register mail_index_module_register; /* Add/replace sync handler for specified extra record. */ void mail_index_register_expunge_handler(struct mail_index *index, uint32_t ext_id, bool call_always, mail_index_expunge_handler_t *callback, void *context); void mail_index_unregister_expunge_handler(struct mail_index *index, uint32_t ext_id); void mail_index_register_sync_handler(struct mail_index *index, uint32_t ext_id, mail_index_sync_handler_t *cb, enum mail_index_sync_handler_type type); void mail_index_unregister_sync_handler(struct mail_index *index, uint32_t ext_id); void mail_index_register_sync_lost_handler(struct mail_index *index, mail_index_sync_lost_handler_t *cb); void mail_index_unregister_sync_lost_handler(struct mail_index *index, mail_index_sync_lost_handler_t *cb); int mail_index_create_tmp_file(struct mail_index *index, const char *path_prefix, const char **path_r); int mail_index_try_open_only(struct mail_index *index); void mail_index_close_file(struct mail_index *index); int mail_index_reopen_if_changed(struct mail_index *index, const char **reason_r); /* Update/rewrite the main index file from index->map */ void mail_index_write(struct mail_index *index, bool want_rotate); void mail_index_flush_read_cache(struct mail_index *index, const char *path, int fd, bool locked); int mail_index_lock_fd(struct mail_index *index, const char *path, int fd, int lock_type, unsigned int timeout_secs, struct file_lock **lock_r); /* Allocate a new empty map. */ struct mail_index_map *mail_index_map_alloc(struct mail_index *index); /* Replace index->map with the latest index changes. This may reopen the index file and/or it may read the latest changes from transaction log. The log is read up to EOF, but non-synced expunges are skipped. If we mmap()ed the index file, the map is returned locked. Returns 1 = ok, 0 = corrupted, -1 = error. */ int mail_index_map(struct mail_index *index, enum mail_index_sync_handler_type type); /* Unreference given mapping and unmap it if it's dropped to zero. */ void mail_index_unmap(struct mail_index_map **map); /* Clone a map. The returned map is always in memory. */ struct mail_index_map *mail_index_map_clone(const struct mail_index_map *map); void mail_index_record_map_move_to_private(struct mail_index_map *map); /* Move a mmaped map to memory. */ void mail_index_map_move_to_memory(struct mail_index_map *map); void mail_index_fchown(struct mail_index *index, int fd, const char *path); bool mail_index_map_lookup_ext(struct mail_index_map *map, const char *name, uint32_t *idx_r); uint32_t mail_index_map_register_ext(struct mail_index_map *map, const char *name, uint32_t ext_offset, const struct mail_index_ext_header *ext_hdr); bool mail_index_map_get_ext_idx(struct mail_index_map *map, uint32_t ext_id, uint32_t *idx_r); const struct mail_index_ext * mail_index_view_get_ext(struct mail_index_view *view, uint32_t ext_id); void mail_index_map_lookup_seq_range(struct mail_index_map *map, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r); /* Returns 1 on success, 0 on non-critical errors we want to silently fix, -1 if map isn't usable. The caller is responsible for logging the errors if -1 is returned. */ int mail_index_map_check_header(struct mail_index_map *map, const char **error_r); /* Returns 1 if header is usable, 0 or -1 if not. The caller should log an error if -1 is returned, but not if 0 is returned. */ bool mail_index_check_header_compat(struct mail_index *index, const struct mail_index_header *hdr, uoff_t file_size, const char **error_r); int mail_index_map_parse_extensions(struct mail_index_map *map); int mail_index_map_parse_keywords(struct mail_index_map *map); void mail_index_map_init_extbufs(struct mail_index_map *map, unsigned int initial_count); int mail_index_map_ext_get_next(struct mail_index_map *map, unsigned int *offset, const struct mail_index_ext_header **ext_hdr_r, const char **name_r); int mail_index_map_ext_hdr_check(const struct mail_index_header *hdr, const struct mail_index_ext_header *ext_hdr, const char *name, const char **error_r); unsigned int mail_index_map_ext_hdr_offset(unsigned int name_len); void mail_index_view_transaction_ref(struct mail_index_view *view); void mail_index_view_transaction_unref(struct mail_index_view *view); void mail_index_fsck_locked(struct mail_index *index); void mail_index_set_error(struct mail_index *index, const char *fmt, ...) ATTR_FORMAT(2, 3); /* "%s failed with index file %s: %m" */ void mail_index_set_syscall_error(struct mail_index *index, const char *function); /* "%s failed with file %s: %m" */ void mail_index_file_set_syscall_error(struct mail_index *index, const char *filepath, const char *function); #endif dovecot-2.2.33.2/src/lib-index/Makefile.am0000644000175000017500000000765613147010713015036 00000000000000noinst_LTLIBRARIES = libindex.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail libindex_la_SOURCES = \ mail-cache.c \ mail-cache-compress.c \ mail-cache-decisions.c \ mail-cache-fields.c \ mail-cache-lookup.c \ mail-cache-transaction.c \ mail-cache-sync-update.c \ mail-index.c \ mail-index-alloc-cache.c \ mail-index-dummy-view.c \ mail-index-fsck.c \ mail-index-lock.c \ mail-index-map.c \ mail-index-map-hdr.c \ mail-index-map-read.c \ mail-index-modseq.c \ mail-index-transaction.c \ mail-index-transaction-export.c \ mail-index-transaction-finish.c \ mail-index-transaction-sort-appends.c \ mail-index-transaction-update.c \ mail-index-transaction-view.c \ mail-index-strmap.c \ mail-index-sync.c \ mail-index-sync-ext.c \ mail-index-sync-keywords.c \ mail-index-sync-update.c \ mail-index-util.c \ mail-index-view.c \ mail-index-view-sync.c \ mail-index-write.c \ mail-transaction-log.c \ mail-transaction-log-append.c \ mail-transaction-log-file.c \ mail-transaction-log-view.c \ mailbox-log.c headers = \ mail-cache.h \ mail-cache-private.h \ mail-index.h \ mail-index-alloc-cache.h \ mail-index-modseq.h \ mail-index-private.h \ mail-index-strmap.h \ mail-index-sync-private.h \ mail-index-transaction-private.h \ mail-index-util.h \ mail-index-view-private.h \ mail-transaction-log.h \ mail-transaction-log-private.h \ mail-transaction-log-view-private.h \ mailbox-log.h test_programs = \ test-mail-index-map \ test-mail-index-modseq \ test-mail-index-sync-ext \ test-mail-index-transaction-finish \ test-mail-index-transaction-update \ test-mail-transaction-log-append \ test-mail-transaction-log-file \ test-mail-transaction-log-view noinst_PROGRAMS = $(test_programs) test_libs = \ mail-index-util.lo \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_map_SOURCES = test-mail-index-map.c test_mail_index_map_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_map_DEPENDENCIES = $(test_deps) test_mail_index_modseq_SOURCES = test-mail-index-modseq.c test_mail_index_modseq_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_index_modseq_DEPENDENCIES = $(test_deps) test_mail_index_sync_ext_SOURCES = test-mail-index-sync-ext.c test_mail_index_sync_ext_LDADD = mail-index-sync-ext.lo $(test_libs) test_mail_index_sync_ext_DEPENDENCIES = $(test_deps) test_mail_index_transaction_finish_SOURCES = test-mail-index-transaction-finish.c test_mail_index_transaction_finish_LDADD = mail-index-transaction-finish.lo $(test_libs) test_mail_index_transaction_finish_DEPENDENCIES = $(test_deps) test_mail_index_transaction_update_SOURCES = test-mail-index-transaction-update.c test_mail_index_transaction_update_LDADD = mail-index-transaction-update.lo $(test_libs) test_mail_index_transaction_update_DEPENDENCIES = $(test_deps) test_mail_transaction_log_append_SOURCES = test-mail-transaction-log-append.c test_mail_transaction_log_append_LDADD = mail-transaction-log-append.lo $(test_libs) test_mail_transaction_log_append_DEPENDENCIES = $(test_deps) test_mail_transaction_log_file_SOURCES = test-mail-transaction-log-file.c test_mail_transaction_log_file_LDADD = $(noinst_LTLIBRARIES) $(test_libs) test_mail_transaction_log_file_DEPENDENCIES = $(test_deps) test_mail_transaction_log_view_SOURCES = test-mail-transaction-log-view.c test_mail_transaction_log_view_LDADD = mail-transaction-log-view.lo $(test_libs) test_mail_transaction_log_view_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/0002755000175000017500000000000013172375611013414 500000000000000dovecot-2.2.33.2/src/lib-storage/mail-autoexpunge.h0000644000175000017500000000041313123174404016756 00000000000000#ifndef MAIL_AUTOEXPUNGE_H #define MAIL_AUTOEXPUNGE_H /* Perform autoexpunging for all the user's mailboxes that have autoexpunging configured. Returns number of mails that were autoexpunged. */ unsigned int mail_user_autoexpunge(struct mail_user *user); #endif dovecot-2.2.33.2/src/lib-storage/mail-storage-service.h0000644000175000017500000001772213165463624017542 00000000000000#ifndef MAIL_STORAGE_SERVICE_H #define MAIL_STORAGE_SERVICE_H #include "net.h" struct master_service; struct mail_user; struct setting_parser_context; struct setting_parser_info; struct mail_storage_service_user; enum mail_storage_service_flags { /* Fail if we don't drop root privileges */ MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT = 0x01, /* Lookup user from userdb */ MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP = 0x02, /* Force mail_debug=yes */ MAIL_STORAGE_SERVICE_FLAG_DEBUG = 0x04, /* Keep the current process permissions */ MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS = 0x08, /* Don't chdir() to user's home */ MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR = 0x10, /* Drop privileges only temporarily (keep running as setuid-root) */ MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP = 0x20, /* Enable core dumps even when dropping privileges temporarily */ MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS = 0x40, /* Don't initialize logging or change log prefixes */ MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT = 0x80, /* Don't load plugins in _service_lookup() */ MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS = 0x100, /* Don't close auth connections because of idling. */ MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT = 0x200, /* When executing doveconf, tell it to use sysexits codes */ MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS = 0x400, /* Don't create namespaces, only the user. */ MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES = 0x800, }; struct mail_storage_service_input { const char *module; const char *service; const char *username; /* If set, use this string as the session ID */ const char *session_id; /* If set, use this string as the session ID prefix, but also append a unique session ID suffix to it. */ const char *session_id_prefix; /* If non-zero, override timestamp when session was created and set mail_user.session_restored=TRUE */ time_t session_create_time; struct ip_addr local_ip, remote_ip; in_port_t local_port, remote_port; const char *const *userdb_fields; /* Override specified global flags */ enum mail_storage_service_flags flags_override_add; enum mail_storage_service_flags flags_override_remove; /* override MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP for this lookup */ unsigned int no_userdb_lookup:1; /* Enable auth_debug=yes for this lookup */ unsigned int debug:1; }; extern struct module *mail_storage_service_modules; struct mail_storage_service_ctx * mail_storage_service_init(struct master_service *service, const struct setting_parser_info *set_roots[], enum mail_storage_service_flags flags) ATTR_NULL(2); struct auth_master_connection * mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx); /* Set auth connection (instead of creating a new one automatically). */ void mail_storage_service_set_auth_conn(struct mail_storage_service_ctx *ctx, struct auth_master_connection *conn); int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, pool_t pool, const struct setting_parser_info **user_info_r, const struct setting_parser_context **parser_r, const char **error_r) ATTR_NULL(2); /* Read settings and initialize context to use them. Do nothing if service is already initialized. This is mainly necessary when calling _get_auth_conn() or _all_init(). */ void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input) ATTR_NULL(2); /* Returns 1 if ok, 0 if user wasn't found, -1 if fatal error, -2 if error is user-specific (e.g. invalid settings). Error can be safely shown to untrusted users. */ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, struct mail_storage_service_user **user_r, const char **error_r); /* The next mail_storage_service_lookup() will save the userdb fields into the given pointer, allocated from the given pool. */ void mail_storage_service_save_userdb_fields(struct mail_storage_service_ctx *ctx, pool_t pool, const char *const **userdb_fields_r); /* Returns 0 if ok, -1 if fatal error, -2 if error is user-specific. */ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_user **mail_user_r); /* Returns 0 if ok, -1 if fatal error, -2 if error is user-specific. */ int mail_storage_service_next_with_session_suffix(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const char *session_id_postfix, struct mail_user **mail_user_r); void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user); /* Combine lookup() and next() into one call. */ int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, struct mail_storage_service_user **user_r, struct mail_user **mail_user_r, const char **error_r); void mail_storage_service_user_ref(struct mail_storage_service_user *user); void mail_storage_service_user_unref(struct mail_storage_service_user **user); /* FIXME: for backwards compatibility - remove */ #define mail_storage_service_user_free(user) \ mail_storage_service_user_unref(user) /* Initialize iterating through all users. */ void mail_storage_service_all_init(struct mail_storage_service_ctx *ctx); /* Same as mail_storage_service_all_init(), but give a user mask hint to the userdb iteration lookup. This itself isn't yet guaranteed to filter out any usernames. */ void mail_storage_service_all_init_mask(struct mail_storage_service_ctx *ctx, const char *user_mask_hint); /* Iterate through all usernames. Returns 1 if username was returned, 0 if there are no more users, -1 if error. */ int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx, const char **username_r); void mail_storage_service_deinit(struct mail_storage_service_ctx **ctx); /* Activate user context. Normally this is called automatically by the ioloop, but e.g. during loops at deinit where all users are being destroyed, it's useful to call this to set the correct user-specific log prefix. */ void mail_storage_service_io_activate_user(struct mail_storage_service_user *user); /* Deactivate user context. This only switches back to non-user-specific log prefix. */ void mail_storage_service_io_deactivate_user(struct mail_storage_service_user *user); void mail_storage_service_io_deactivate(struct mail_storage_service_ctx *ctx); /* Return the settings pointed to by set_root parameter in _init(). The settings contain all the changes done by userdb lookups. */ void **mail_storage_service_user_get_set(struct mail_storage_service_user *user); const struct mail_storage_settings * mail_storage_service_user_get_mail_set(struct mail_storage_service_user *user); const struct mail_storage_service_input * mail_storage_service_user_get_input(struct mail_storage_service_user *user); struct setting_parser_context * mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user); struct mail_storage_service_ctx * mail_storage_service_user_get_service_ctx(struct mail_storage_service_user *user); pool_t mail_storage_service_user_get_pool(struct mail_storage_service_user *user); const struct var_expand_table * mail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx, struct mail_storage_service_input *input); const char *mail_storage_service_fields_var_expand(const char *data, const char *const *fields); /* Return the settings pointed to by set_root parameter in _init() */ void *mail_storage_service_get_settings(struct master_service *service); /* Updates settings for storage service user, forwards return value of settings_parse_keyvalue() */ int mail_storage_service_user_set_setting(struct mail_storage_service_user *user, const char *key, const char *value, const char **error_r); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-header.c0000644000175000017500000000431113165463624016362 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-cache.h" #include "mail-storage-private.h" static struct mailbox_header_lookup_ctx * mailbox_header_lookup_init_real(struct mailbox *box, const char *const headers[]) { struct mail_cache_field *fields, header_field = { .type = MAIL_CACHE_FIELD_HEADER, .decision = MAIL_CACHE_DECISION_TEMP }; struct mailbox_header_lookup_ctx *ctx; const char *const *name; const char **sorted_headers, **dest_name; pool_t pool; unsigned int i, count; i_assert(*headers != NULL); for (count = 0, name = headers; *name != NULL; name++) count++; /* @UNSAFE: headers need to be sorted for filter stream. */ sorted_headers = t_new(const char *, count); memcpy(sorted_headers, headers, count * sizeof(*sorted_headers)); i_qsort(sorted_headers, count, sizeof(*sorted_headers), i_strcasecmp_p); headers = sorted_headers; /* @UNSAFE */ fields = t_new(struct mail_cache_field, count); for (i = 0; i < count; i++) { header_field.name = t_strconcat("hdr.", headers[i], NULL); fields[i] = header_field; } mail_cache_register_fields(box->cache, fields, count); pool = pool_alloconly_create("mailbox_header_lookup_ctx", 1024); ctx = p_new(pool, struct mailbox_header_lookup_ctx, 1); ctx->box = box; ctx->refcount = 1; ctx->pool = pool; ctx->count = count; ctx->idx = p_new(pool, unsigned int, count); /* @UNSAFE */ dest_name = p_new(pool, const char *, count + 1); for (i = 0; i < count; i++) { ctx->idx[i] = fields[i].idx; dest_name[i] = p_strdup(pool, headers[i]); } ctx->name = dest_name; return ctx; } struct mailbox_header_lookup_ctx * mailbox_header_lookup_init(struct mailbox *box, const char *const headers[]) { struct mailbox_header_lookup_ctx *ctx; T_BEGIN { ctx = mailbox_header_lookup_init_real(box, headers); } T_END; return ctx; } void mailbox_header_lookup_ref(struct mailbox_header_lookup_ctx *ctx) { i_assert(ctx->refcount > 0); ctx->refcount++; } void mailbox_header_lookup_unref(struct mailbox_header_lookup_ctx **_ctx) { struct mailbox_header_lookup_ctx *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->refcount > 0); if (--ctx->refcount > 0) return; pool_unref(&ctx->pool); } dovecot-2.2.33.2/src/lib-storage/mailbox-watch.h0000644000175000017500000000061713123174404016237 00000000000000#ifndef MAILBOX_WATCH_H #define MAILBOX_WATCH_H void mailbox_watch_add(struct mailbox *box, const char *path); void mailbox_watch_remove_all(struct mailbox *box); /* Create a new temporary ioloop, add all the watches back and call io_loop_extract_notify_fd() on it. Returns fd on success, -1 on error. */ int mailbox_watch_extract_notify_fd(struct mailbox *box, const char **reason_r); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-watch.c0000644000175000017500000000723113165463624016244 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mail-storage-private.h" #include "mailbox-watch.h" #include #include #include #define NOTIFY_DELAY_MSECS 500 struct mailbox_notify_file { struct mailbox_notify_file *next; char *path; time_t last_stamp; struct io *io_notify; }; static void notify_delay_callback(struct mailbox *box) { if (box->to_notify_delay != NULL) timeout_remove(&box->to_notify_delay); box->notify_callback(box, box->notify_context); } static void notify_timeout(struct mailbox *box) { struct mailbox_notify_file *file; struct stat st; bool notify = FALSE; for (file = box->notify_files; file != NULL; file = file->next) { if (stat(file->path, &st) == 0 && file->last_stamp != st.st_mtime) { file->last_stamp = st.st_mtime; notify = TRUE; } } if (notify) notify_delay_callback(box); } static void notify_callback(struct mailbox *box) { timeout_reset(box->to_notify); if (box->to_notify_delay == NULL) { box->to_notify_delay = timeout_add_short(NOTIFY_DELAY_MSECS, notify_delay_callback, box); } } void mailbox_watch_add(struct mailbox *box, const char *path) { const struct mail_storage_settings *set = box->storage->set; struct mailbox_notify_file *file; struct stat st; struct io *io = NULL; i_assert(set->mailbox_idle_check_interval > 0); (void)io_add_notify(path, notify_callback, box, &io); file = i_new(struct mailbox_notify_file, 1); file->path = i_strdup(path); file->last_stamp = stat(path, &st) < 0 ? 0 : st.st_mtime; file->io_notify = io; file->next = box->notify_files; box->notify_files = file; /* we still add a timeout if we don't have one already, * because we don't know what happens with [di]notify * when the filesystem is remote (NFS, ...) */ if (box->to_notify == NULL) { box->to_notify = timeout_add(set->mailbox_idle_check_interval * 1000, notify_timeout, box); } } void mailbox_watch_remove_all(struct mailbox *box) { struct mailbox_notify_file *file; while (box->notify_files != NULL) { file = box->notify_files; box->notify_files = file->next; if (file->io_notify != NULL) io_remove(&file->io_notify); i_free(file->path); i_free(file); } if (box->to_notify_delay != NULL) timeout_remove(&box->to_notify_delay); if (box->to_notify != NULL) timeout_remove(&box->to_notify); } static void notify_extract_callback(struct mailbox *box ATTR_UNUSED) { i_unreached(); } int mailbox_watch_extract_notify_fd(struct mailbox *box, const char **reason_r) { struct ioloop *ioloop; struct mailbox_notify_file *file; struct io *io, *const *iop; ARRAY(struct io *) temp_ios; int ret; bool failed = FALSE; /* add all the notify IOs to a new ioloop. */ ioloop = io_loop_create(); t_array_init(&temp_ios, 8); for (file = box->notify_files; file != NULL && !failed; file = file->next) { switch (io_add_notify(file->path, notify_extract_callback, box, &io)) { case IO_NOTIFY_ADDED: array_append(&temp_ios, &io, 1); break; case IO_NOTIFY_NOTFOUND: *reason_r = t_strdup_printf( "%s not found - can't watch it", file->path); failed = TRUE; break; case IO_NOTIFY_NOSUPPORT: *reason_r = "Filesystem notifications not supported"; failed = TRUE; break; } } if (failed) ret = -1; else if (array_count(&temp_ios) == 0) { *reason_r = "Mailbox has no IO notifications"; ret = -1; } else { ret = io_loop_extract_notify_fd(ioloop); if (ret == -1) *reason_r = "Couldn't extra notify fd"; } array_foreach(&temp_ios, iop) { struct io *io = *iop; io_remove(&io); } io_loop_destroy(&ioloop); return ret; } dovecot-2.2.33.2/src/lib-storage/mail-search-mime.h0000644000175000017500000001032513123174404016607 00000000000000#ifndef MAIL_SEARCH_MIMEPART_H #define MAIL_SEARCH_MIMEPART_H enum mail_search_mime_arg_type { SEARCH_MIME_OR, SEARCH_MIME_SUB, /* sizes */ SEARCH_MIME_SIZE_EQUAL, SEARCH_MIME_SIZE_LARGER, SEARCH_MIME_SIZE_SMALLER, /* part properties */ SEARCH_MIME_DESCRIPTION, SEARCH_MIME_DISPOSITION_TYPE, SEARCH_MIME_DISPOSITION_PARAM, SEARCH_MIME_ENCODING, SEARCH_MIME_ID, SEARCH_MIME_LANGUAGE, SEARCH_MIME_LOCATION, SEARCH_MIME_MD5, /* content-type */ SEARCH_MIME_TYPE, SEARCH_MIME_SUBTYPE, SEARCH_MIME_PARAM, /* headers */ SEARCH_MIME_HEADER, /* body */ SEARCH_MIME_BODY, SEARCH_MIME_TEXT, /* message */ SEARCH_MIME_CC, SEARCH_MIME_BCC, SEARCH_MIME_FROM, SEARCH_MIME_IN_REPLY_TO, SEARCH_MIME_MESSAGE_ID, SEARCH_MIME_REPLY_TO, SEARCH_MIME_SENDER, SEARCH_MIME_SENTBEFORE, SEARCH_MIME_SENTON, /* time must point to beginning of the day */ SEARCH_MIME_SENTSINCE, SEARCH_MIME_SUBJECT, SEARCH_MIME_TO, /* relations */ SEARCH_MIME_PARENT, SEARCH_MIME_CHILD, /* position */ SEARCH_MIME_DEPTH_EQUAL, SEARCH_MIME_DEPTH_MIN, SEARCH_MIME_DEPTH_MAX, SEARCH_MIME_INDEX, /* filename */ SEARCH_MIME_FILENAME_IS, SEARCH_MIME_FILENAME_CONTAINS, SEARCH_MIME_FILENAME_BEGINS, SEARCH_MIME_FILENAME_ENDS }; struct mail_search_mime_arg { /* NOTE: when adding new fields, make sure mail_search_mime_arg_dup_one() and mail_search_mime_arg_one_equals() are updated. */ struct mail_search_mime_arg *next; enum mail_search_mime_arg_type type; union { struct mail_search_mime_arg *subargs; const char *str; time_t time; uoff_t size; unsigned int number; } value; void *context; const char *field_name; /* for SEARCH_HEADER* */ bool match_not:1; /* result = !result */ bool match_always:1; /* result = 1 always */ bool nonmatch_always:1; /* result = 0 always */ int result; /* -1 = unknown, 0 = unmatched, 1 = matched */ }; struct mail_search_mime_part { struct mail_search_mime_arg *args; bool simplified:1; }; typedef void mail_search_mime_foreach_callback_t(struct mail_search_mime_arg *arg, void *context); /* Returns TRUE if the two mimepart search keys are fully compatible. */ bool mail_search_mime_parts_equal(const struct mail_search_mime_part *mpart1, const struct mail_search_mime_part *mpart2); /* Same as mail_search_mime_part_equal(), but for individual mail_search_mime_arg structs. All the siblings of arg1 and arg2 are also compared. */ bool mail_search_mime_arg_equals(const struct mail_search_mime_arg *arg1, const struct mail_search_mime_arg *arg2); /* Same as mail_search_mime_arg_equals(), but don't compare siblings. */ bool mail_search_mime_arg_one_equals(const struct mail_search_mime_arg *arg1, const struct mail_search_mime_arg *arg2); struct mail_search_mime_part * mail_search_mime_part_dup(pool_t pool, const struct mail_search_mime_part *mpart); struct mail_search_mime_arg * mail_search_mime_arg_dup(pool_t pool, const struct mail_search_mime_arg *arg); /* Reset the results in search arguments. match_always is reset only if full_reset is TRUE. */ void mail_search_mime_args_reset(struct mail_search_mime_arg *args, bool full_reset); /* goes through arguments in list that don't have a result yet. Returns 1 = search matched, 0 = search unmatched, -1 = don't know yet */ int mail_search_mime_args_foreach(struct mail_search_mime_arg *args, mail_search_mime_foreach_callback_t *callback, void *context) ATTR_NULL(3); #define mail_search_mime_args_foreach(args, callback, context) \ mail_search_mime_args_foreach(args + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct mail_search_mime_arg *, typeof(context))), \ (mail_search_mime_foreach_callback_t *)callback, context) /* Simplify/optimize search arguments. Afterwards all OR/SUB args are guaranteed to have match_not=FALSE. */ void mail_search_mime_simplify(struct mail_search_mime_part *args); /* Appends MIMEPART search key to the dest string and returns TRUE. */ bool mail_search_mime_part_to_imap(string_t *dest, const struct mail_search_mime_part *mpart, const char **error_r); /* Like mail_search_mime_part_to_imap(), but append only a single MIMEPART key. */ bool mail_search_mime_arg_to_imap(string_t *dest, const struct mail_search_mime_arg *arg, const char **error_r); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-attribute-internal.c0000644000175000017500000000676513165463624020766 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage-private.h" #include "mailbox-attribute-internal.h" /* * Internal mailbox attributes */ /* /private/specialuse (RFC 6154) */ static int mailbox_attribute_specialuse_get(struct mailbox_transaction_context *t, const char *key ATTR_UNUSED, struct mail_attribute_value *value_r) { const struct mailbox_settings *set = t->box->set; if (set == NULL || *set->special_use == '\0') return 0; value_r->value = set->special_use; return 1; } static struct mailbox_attribute_internal iattr_mbox_prv_special_use = { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, .key = MAILBOX_ATTRIBUTE_SPECIALUSE, .rank = MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY, .get = mailbox_attribute_specialuse_get }; /* /private/comment, /shared/comment (RFC 5464) */ static int mailbox_attribute_comment_get(struct mailbox_transaction_context *t, const char *key ATTR_UNUSED, struct mail_attribute_value *value_r) { const struct mailbox_settings *set = t->box->set; if (set == NULL || *set->comment == '\0') return 0; value_r->value = set->comment; return 1; } static struct mailbox_attribute_internal iattr_mbox_prv_comment = { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, .key = MAILBOX_ATTRIBUTE_COMMENT, .rank = MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT, .get = mailbox_attribute_comment_get }; static struct mailbox_attribute_internal iattr_mbox_shd_comment = { .type = MAIL_ATTRIBUTE_TYPE_SHARED, .key = MAILBOX_ATTRIBUTE_COMMENT, .rank = MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT, .get = mailbox_attribute_comment_get }; /* * Internal server attributes */ /* /shared/comment (RFC 5464) */ static int server_attribute_comment_get(struct mailbox_transaction_context *t, const char *key ATTR_UNUSED, struct mail_attribute_value *value_r) { const struct mail_storage_settings *set = t->box->storage->set; if (*set->mail_server_comment == '\0') return 0; value_r->value = set->mail_server_comment; return 1; } static struct mailbox_attribute_internal iattr_serv_shd_comment = { .type = MAIL_ATTRIBUTE_TYPE_SHARED, .key = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAIL_SERVER_ATTRIBUTE_COMMENT, .rank = MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY, .get = server_attribute_comment_get }; /* /shared/admin (RFC 5464) */ static int server_attribute_admin_get(struct mailbox_transaction_context *t, const char *key ATTR_UNUSED, struct mail_attribute_value *value_r) { const struct mail_storage_settings *set = t->box->storage->set; if (*set->mail_server_admin == '\0') return 0; value_r->value = set->mail_server_admin; return 1; } static struct mailbox_attribute_internal iattr_serv_shd_admin = { .type = MAIL_ATTRIBUTE_TYPE_SHARED, .key = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAIL_SERVER_ATTRIBUTE_ADMIN, .rank = MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY, .get = server_attribute_admin_get }; /* * Registry */ void mailbox_attributes_internal_init(void) { /* * Internal mailbox attributes */ /* /private/specialuse (RFC 6154) */ mailbox_attribute_register_internal(&iattr_mbox_prv_special_use); /* /private/comment (RFC 5464) */ mailbox_attribute_register_internal(&iattr_mbox_prv_comment); /* /shared/comment (RFC 5464) */ mailbox_attribute_register_internal(&iattr_mbox_shd_comment); /* * internal server attributes */ /* /shared/comment (RFC 5464) */ mailbox_attribute_register_internal(&iattr_serv_shd_comment); /* /shared/admin (RFC 5464) */ mailbox_attribute_register_internal(&iattr_serv_shd_admin); } dovecot-2.2.33.2/src/lib-storage/Makefile.in0000644000175000017500000011647013172375573015417 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-storage ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = am_libdovecot_storage_la_OBJECTS = libdovecot_storage_la_OBJECTS = $(am_libdovecot_storage_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_storage_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_storage_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__DEPENDENCIES_2 = list/libstorage_list.la index/libstorage_index.la \ register/libstorage_register.la ../lib-index/libindex.la \ ../lib-imap-storage/libimap-storage.la am_libstorage_la_OBJECTS = fail-mail-storage.lo fail-mailbox.lo \ fail-mail.lo mail.lo mail-autoexpunge.lo mail-copy.lo \ mail-error.lo mail-namespace.lo mail-search.lo \ mail-search-args-cmdline.lo mail-search-args-imap.lo \ mail-search-args-simplify.lo mail-search-build.lo \ mail-search-mime.lo mail-search-mime-build.lo \ mail-search-mime-register.lo mail-search-parser.lo \ mail-search-parser-imap.lo mail-search-parser-cmdline.lo \ mail-search-register.lo mail-search-register-human.lo \ mail-search-register-imap.lo mail-storage.lo \ mail-storage-hooks.lo mail-storage-service.lo \ mail-storage-settings.lo mail-thread.lo mail-user.lo \ mailbox-attribute.lo mailbox-attribute-internal.lo \ mailbox-get.lo mailbox-guid-cache.lo mailbox-header.lo \ mailbox-keywords.lo mailbox-list.lo mailbox-list-notify.lo \ mailbox-recent-flags.lo mailbox-search-result.lo \ mailbox-tree.lo mailbox-uidvalidity.lo mailbox-watch.lo libstorage_la_OBJECTS = $(am_libstorage_la_OBJECTS) am__EXEEXT_1 = test-mail-search-args-imap$(EXEEXT) \ test-mail-search-args-simplify$(EXEEXT) \ test-mail-storage$(EXEEXT) test-mailbox-get$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_mail_search_args_imap_OBJECTS = \ test-mail-search-args-imap.$(OBJEXT) test_mail_search_args_imap_OBJECTS = \ $(am_test_mail_search_args_imap_OBJECTS) am_test_mail_search_args_simplify_OBJECTS = \ test-mail-search-args-simplify.$(OBJEXT) test_mail_search_args_simplify_OBJECTS = \ $(am_test_mail_search_args_simplify_OBJECTS) am_test_mail_storage_OBJECTS = test-mail-storage.$(OBJEXT) test_mail_storage_OBJECTS = $(am_test_mail_storage_OBJECTS) am_test_mailbox_get_OBJECTS = test-mailbox-get.$(OBJEXT) test_mailbox_get_OBJECTS = $(am_test_mailbox_get_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_storage_la_SOURCES) $(libstorage_la_SOURCES) \ $(test_mail_search_args_imap_SOURCES) \ $(test_mail_search_args_simplify_SOURCES) \ $(test_mail_storage_SOURCES) $(test_mailbox_get_SOURCES) DIST_SOURCES = $(libdovecot_storage_la_SOURCES) \ $(libstorage_la_SOURCES) $(test_mail_search_args_imap_SOURCES) \ $(test_mail_search_args_simplify_SOURCES) \ $(test_mail_storage_SOURCES) $(test_mailbox_get_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = list index register noinst_LTLIBRARIES = libstorage.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DMODULEDIR=\""$(moduledir)"\" libstorage_la_SOURCES = \ fail-mail-storage.c \ fail-mailbox.c \ fail-mail.c \ mail.c \ mail-autoexpunge.c \ mail-copy.c \ mail-error.c \ mail-namespace.c \ mail-search.c \ mail-search-args-cmdline.c \ mail-search-args-imap.c \ mail-search-args-simplify.c \ mail-search-build.c \ mail-search-mime.c \ mail-search-mime-build.c \ mail-search-mime-register.c \ mail-search-parser.c \ mail-search-parser-imap.c \ mail-search-parser-cmdline.c \ mail-search-register.c \ mail-search-register-human.c \ mail-search-register-imap.c \ mail-storage.c \ mail-storage-hooks.c \ mail-storage-service.c \ mail-storage-settings.c \ mail-thread.c \ mail-user.c \ mailbox-attribute.c \ mailbox-attribute-internal.c \ mailbox-get.c \ mailbox-guid-cache.c \ mailbox-header.c \ mailbox-keywords.c \ mailbox-list.c \ mailbox-list-notify.c \ mailbox-recent-flags.c \ mailbox-search-result.c \ mailbox-tree.c \ mailbox-uidvalidity.c \ mailbox-watch.c headers = \ fail-mail-storage.h \ mail-autoexpunge.h \ mail-copy.h \ mail-error.h \ mail-namespace.h \ mail-search.h \ mail-search-build.h \ mail-search-mime.h \ mail-search-mime-build.h \ mail-search-mime-register.h \ mail-search-register.h \ mail-thread.h \ mail-storage.h \ mail-search-parser.h \ mail-search-parser-private.h \ mail-storage-private.h \ mail-storage-hooks.h \ mail-storage-service.h \ mail-storage-settings.h \ mail-user.h \ mailbox-attribute.h \ mailbox-attribute-internal.h \ mailbox-attribute-private.h \ mailbox-guid-cache.h \ mailbox-list.h \ mailbox-list-iter.h \ mailbox-list-private.h \ mailbox-list-notify.h \ mailbox-recent-flags.h \ mailbox-search-result-private.h \ mailbox-tree.h \ mailbox-uidvalidity.h \ mailbox-watch.h shlibs = \ @LINKED_STORAGE_LIBS@ \ list/libstorage_list.la \ index/libstorage_index.la \ register/libstorage_register.la \ ../lib-index/libindex.la \ ../lib-imap-storage/libimap-storage.la libstorage_la_LIBADD = $(shlibs) libstorage_la_DEPENDENCIES = $(shlibs) pkglib_LTLIBRARIES = libdovecot-storage.la libdovecot_storage_la_SOURCES = libdovecot_storage_la_LIBADD = \ libstorage.la \ ../lib-dovecot/libdovecot.la \ $(LINKED_STORAGE_LDADD) libdovecot_storage_la_DEPENDENCIES = \ libstorage.la \ ../lib-dovecot/libdovecot.la \ $(LIBDOVECOT_DEPS) libdovecot_storage_la_LDFLAGS = -export-dynamic test_programs = \ test-mail-search-args-imap \ test-mail-search-args-simplify \ test-mail-storage \ test-mailbox-get test_libs = \ $(top_builddir)/src/lib-test/libtest.la \ $(top_builddir)/src/lib/liblib.la test_mail_search_args_imap_SOURCES = test-mail-search-args-imap.c test_mail_search_args_imap_LDADD = libstorage.la $(LIBDOVECOT) test_mail_search_args_imap_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) test_mail_search_args_simplify_SOURCES = test-mail-search-args-simplify.c test_mail_search_args_simplify_LDADD = libstorage.la $(LIBDOVECOT) test_mail_search_args_simplify_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) test_mailbox_get_SOURCES = test-mailbox-get.c test_mailbox_get_LDADD = mailbox-get.lo $(test_libs) test_mailbox_get_DEPENDENCIES = $(noinst_LTLIBRARIES) $(test_libs) test_mail_storage_SOURCES = test-mail-storage.c test_mail_storage_LDADD = libstorage.la $(LIBDOVECOT) test_mail_storage_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) noinst_HEADERS = $(test_headers) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-storage.la: $(libdovecot_storage_la_OBJECTS) $(libdovecot_storage_la_DEPENDENCIES) $(EXTRA_libdovecot_storage_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_storage_la_LINK) -rpath $(pkglibdir) $(libdovecot_storage_la_OBJECTS) $(libdovecot_storage_la_LIBADD) $(LIBS) libstorage.la: $(libstorage_la_OBJECTS) $(libstorage_la_DEPENDENCIES) $(EXTRA_libstorage_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_la_OBJECTS) $(libstorage_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-mail-search-args-imap$(EXEEXT): $(test_mail_search_args_imap_OBJECTS) $(test_mail_search_args_imap_DEPENDENCIES) $(EXTRA_test_mail_search_args_imap_DEPENDENCIES) @rm -f test-mail-search-args-imap$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_search_args_imap_OBJECTS) $(test_mail_search_args_imap_LDADD) $(LIBS) test-mail-search-args-simplify$(EXEEXT): $(test_mail_search_args_simplify_OBJECTS) $(test_mail_search_args_simplify_DEPENDENCIES) $(EXTRA_test_mail_search_args_simplify_DEPENDENCIES) @rm -f test-mail-search-args-simplify$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_search_args_simplify_OBJECTS) $(test_mail_search_args_simplify_LDADD) $(LIBS) test-mail-storage$(EXEEXT): $(test_mail_storage_OBJECTS) $(test_mail_storage_DEPENDENCIES) $(EXTRA_test_mail_storage_DEPENDENCIES) @rm -f test-mail-storage$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_storage_OBJECTS) $(test_mail_storage_LDADD) $(LIBS) test-mailbox-get$(EXEEXT): $(test_mailbox_get_OBJECTS) $(test_mailbox_get_DEPENDENCIES) $(EXTRA_test_mailbox_get_DEPENDENCIES) @rm -f test-mailbox-get$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mailbox_get_OBJECTS) $(test_mailbox_get_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fail-mail-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fail-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fail-mailbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-autoexpunge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-copy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-error.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-namespace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-args-cmdline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-args-imap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-args-simplify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-build.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-mime-build.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-mime-register.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-mime.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-parser-cmdline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-parser-imap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-register-human.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-register-imap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search-register.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage-hooks.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage-service.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-user.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-attribute-internal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-attribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-get.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-guid-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-header.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-keywords.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-notify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-recent-flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-search-result.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-uidvalidity.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-watch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-search-args-imap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-search-args-simplify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mailbox-get.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool \ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS \ uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/mail-search-register-imap.c0000644000175000017500000003755713165463624020456 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "imap-date.h" #include "imap-seqset.h" #include "imap-utf7.h" #include "imap-util.h" #include "mail-search-register.h" #include "mail-search-parser.h" #include "mail-search-build.h" #include "mail-search-mime-build.h" struct mail_search_register *mail_search_register_imap; static struct mail_search_arg * imap_search_fallback(struct mail_search_build_context *ctx, const char *key) { struct mail_search_arg *sarg; if (*key == '*' || (*key >= '0' && *key <= '9')) { /* */ sarg = mail_search_build_new(ctx, SEARCH_SEQSET); p_array_init(&sarg->value.seqset, ctx->pool, 16); if (imap_seq_set_parse(key, &sarg->value.seqset) < 0) { ctx->_error = "Invalid messageset"; return NULL; } return sarg; } ctx->_error = p_strconcat(ctx->pool, "Unknown argument ", key, NULL); return NULL; } static struct mail_search_arg * imap_search_not(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0) return NULL; sarg->match_not = !sarg->match_not; return sarg; } static struct mail_search_arg * imap_search_or(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg, **subargs; /* */ sarg = mail_search_build_new(ctx, SEARCH_OR); subargs = &sarg->value.subargs; do { if (mail_search_build_key(ctx, sarg, subargs) < 0) return NULL; subargs = &(*subargs)->next; /* OR OR ... - put them all under one SEARCH_OR list. */ } while (mail_search_parse_skip_next(ctx->parser, "OR")); if (mail_search_build_key(ctx, sarg, subargs) < 0) return NULL; return sarg; } #define CALLBACK_STR(_func, _type) \ static struct mail_search_arg *\ imap_search_##_func(struct mail_search_build_context *ctx) \ { \ return mail_search_build_str(ctx, _type); \ } static struct mail_search_arg * imap_search_all(struct mail_search_build_context *ctx) { return mail_search_build_new(ctx, SEARCH_ALL); } static struct mail_search_arg * imap_search_uid(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* */ sarg = mail_search_build_str(ctx, SEARCH_UIDSET); if (sarg == NULL) return NULL; p_array_init(&sarg->value.seqset, ctx->pool, 16); if (strcmp(sarg->value.str, "$") == 0) { /* SEARCHRES: delay initialization */ } else { if (imap_seq_set_parse(sarg->value.str, &sarg->value.seqset) < 0) { ctx->_error = "Invalid UID messageset"; return NULL; } } return sarg; } #define CALLBACK_FLAG(_func, _flag, _not) \ static struct mail_search_arg *\ imap_search_##_func(struct mail_search_build_context *ctx) \ { \ struct mail_search_arg *sarg; \ sarg = mail_search_build_new(ctx, SEARCH_FLAGS); \ sarg->value.flags = _flag; \ sarg->match_not = _not; \ return sarg; \ } CALLBACK_FLAG(answered, MAIL_ANSWERED, FALSE) CALLBACK_FLAG(unanswered, MAIL_ANSWERED, TRUE) CALLBACK_FLAG(deleted, MAIL_DELETED, FALSE) CALLBACK_FLAG(undeleted, MAIL_DELETED, TRUE) CALLBACK_FLAG(draft, MAIL_DRAFT, FALSE) CALLBACK_FLAG(undraft, MAIL_DRAFT, TRUE) CALLBACK_FLAG(flagged, MAIL_FLAGGED, FALSE) CALLBACK_FLAG(unflagged, MAIL_FLAGGED, TRUE) CALLBACK_FLAG(seen, MAIL_SEEN, FALSE) CALLBACK_FLAG(unseen, MAIL_SEEN, TRUE) CALLBACK_FLAG(recent, MAIL_RECENT, FALSE) CALLBACK_FLAG(old, MAIL_RECENT, TRUE) static struct mail_search_arg * imap_search_new(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* NEW == (RECENT UNSEEN) */ sarg = mail_search_build_new(ctx, SEARCH_SUB); sarg->value.subargs = imap_search_recent(ctx); sarg->value.subargs->next = imap_search_unseen(ctx); return sarg; } CALLBACK_STR(keyword, SEARCH_KEYWORDS) static struct mail_search_arg * imap_search_unkeyword(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; sarg = imap_search_keyword(ctx); if (sarg != NULL) sarg->match_not = TRUE; return sarg; } static struct mail_search_arg * arg_new_date(struct mail_search_build_context *ctx, enum mail_search_arg_type type, enum mail_search_date_type date_type) { struct mail_search_arg *sarg; const char *value; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (!imap_parse_date(value, &sarg->value.time)) { ctx->_error = "Invalid search date parameter"; return NULL; } sarg->value.date_type = date_type; return sarg; } #define CALLBACK_DATE(_func, _type, _date_type) \ static struct mail_search_arg *\ imap_search_##_func(struct mail_search_build_context *ctx) \ { \ return arg_new_date(ctx, _type, _date_type); \ } CALLBACK_DATE(before, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(on, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(since, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(sentbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(senton, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(sentsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(x_savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED) CALLBACK_DATE(x_savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED) CALLBACK_DATE(x_savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED) static struct mail_search_arg * arg_new_size(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *sarg; const char *value; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (str_to_uoff(value, &sarg->value.size) < 0) { ctx->_error = "Invalid search size parameter"; return NULL; } return sarg; } static struct mail_search_arg * imap_search_larger(struct mail_search_build_context *ctx) { return arg_new_size(ctx, SEARCH_LARGER); } static struct mail_search_arg * imap_search_smaller(struct mail_search_build_context *ctx) { return arg_new_size(ctx, SEARCH_SMALLER); } static struct mail_search_arg * arg_new_header(struct mail_search_build_context *ctx, enum mail_search_arg_type type, const char *hdr_name) { struct mail_search_arg *sarg; const char *value; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (mail_search_build_get_utf8(ctx, value, &sarg->value.str) < 0) return NULL; sarg->hdr_field_name = p_strdup(ctx->pool, hdr_name); return sarg; } #define CALLBACK_HDR(_name, _type) \ static struct mail_search_arg *\ imap_search_##_name(struct mail_search_build_context *ctx) \ { \ return arg_new_header(ctx, _type, #_name); \ } CALLBACK_HDR(bcc, SEARCH_HEADER_ADDRESS) CALLBACK_HDR(cc, SEARCH_HEADER_ADDRESS) CALLBACK_HDR(from, SEARCH_HEADER_ADDRESS) CALLBACK_HDR(to, SEARCH_HEADER_ADDRESS) CALLBACK_HDR(subject, SEARCH_HEADER_COMPRESS_LWSP) static struct mail_search_arg * imap_search_header(struct mail_search_build_context *ctx) { const char *hdr_name; /* */ if (mail_search_parse_string(ctx->parser, &hdr_name) < 0) return NULL; if (mail_search_build_get_utf8(ctx, hdr_name, &hdr_name) < 0) return NULL; return arg_new_header(ctx, SEARCH_HEADER, t_str_ucase(hdr_name)); } static struct mail_search_arg * arg_new_body(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *sarg; sarg = mail_search_build_str(ctx, type); if (sarg == NULL) return NULL; if (mail_search_build_get_utf8(ctx, sarg->value.str, &sarg->value.str) < 0) return NULL; return sarg; } #define CALLBACK_BODY(_func, _type) \ static struct mail_search_arg *\ imap_search_##_func(struct mail_search_build_context *ctx) \ { \ return arg_new_body(ctx, _type); \ } CALLBACK_BODY(body, SEARCH_BODY) CALLBACK_BODY(text, SEARCH_TEXT) static struct mail_search_arg * arg_new_interval(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *sarg; const char *value; uint32_t interval; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (str_to_uint32(value, &interval) < 0 || interval == 0) { ctx->_error = "Invalid search interval parameter"; return NULL; } sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ; sarg->value.time = ioloop_time - interval; sarg->value.date_type = MAIL_SEARCH_DATE_TYPE_RECEIVED; return sarg; } static struct mail_search_arg * imap_search_older(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; sarg = arg_new_interval(ctx, SEARCH_BEFORE); if (sarg == NULL) return NULL; /* we need to match also equal, but SEARCH_BEFORE compares with "<" */ sarg->value.time++; return sarg; } static struct mail_search_arg * imap_search_younger(struct mail_search_build_context *ctx) { return arg_new_interval(ctx, SEARCH_SINCE); } static int arg_modseq_set_type(struct mail_search_build_context *ctx, struct mail_search_modseq *modseq, const char *name) { if (strcasecmp(name, "all") == 0) modseq->type = MAIL_SEARCH_MODSEQ_TYPE_ANY; else if (strcasecmp(name, "priv") == 0) modseq->type = MAIL_SEARCH_MODSEQ_TYPE_PRIVATE; else if (strcasecmp(name, "shared") == 0) modseq->type = MAIL_SEARCH_MODSEQ_TYPE_SHARED; else { ctx->_error = "Invalid MODSEQ type"; return -1; } return 0; } static int arg_modseq_set_ext(struct mail_search_build_context *ctx, struct mail_search_arg *sarg, const char *name) { const char *value; name = t_str_lcase(name); if (strncmp(name, "/flags/", 7) != 0) return 0; name += 7; /* set name */ if (*name == '\\') { /* system flag */ sarg->value.flags = imap_parse_system_flag(name); if (sarg->value.flags == 0 || sarg->value.flags == MAIL_RECENT) { ctx->_error = "Invalid MODSEQ system flag"; return -1; } } else { sarg->value.str = p_strdup(ctx->pool, name); } /* set type */ if (mail_search_parse_string(ctx->parser, &value) < 0) return -1; if (arg_modseq_set_type(ctx, sarg->value.modseq, value) < 0) return -1; return 1; } static struct mail_search_arg * imap_search_modseq(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; const char *value; int ret; /* [ ] */ sarg = mail_search_build_new(ctx, SEARCH_MODSEQ); sarg->value.modseq = p_new(ctx->pool, struct mail_search_modseq, 1); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if ((ret = arg_modseq_set_ext(ctx, sarg, value)) < 0) return NULL; if (ret > 0) { /* extension data used */ if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; } if (str_to_uint64(value, &sarg->value.modseq->modseq) < 0) { ctx->_error = "Invalid MODSEQ value"; return NULL; } return sarg; } static struct mail_search_arg * imap_search_last_result(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* SEARCHRES: delay initialization */ sarg = mail_search_build_new(ctx, SEARCH_UIDSET); sarg->value.str = "$"; p_array_init(&sarg->value.seqset, ctx->pool, 16); return sarg; } static void mail_search_arg_set_fuzzy(struct mail_search_arg *sarg) { for (; sarg != NULL; sarg = sarg->next) { sarg->fuzzy = TRUE; switch (sarg->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: mail_search_arg_set_fuzzy(sarg->value.subargs); break; default: break; } } } static struct mail_search_arg * imap_search_fuzzy(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0) return NULL; i_assert(sarg->next == NULL); mail_search_arg_set_fuzzy(sarg); return sarg; } static struct mail_search_arg * imap_search_mimepart(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; sarg = mail_search_build_new(ctx, SEARCH_MIMEPART); if (mail_search_mime_build(ctx, &sarg->value.mime_part) < 0) return NULL; return sarg; } static struct mail_search_arg * imap_search_inthread(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* */ enum mail_thread_type thread_type; const char *algorithm; if (mail_search_parse_string(ctx->parser, &algorithm) < 0) return NULL; if (!mail_thread_type_parse(algorithm, &thread_type)) { ctx->_error = "Unknown thread algorithm"; return NULL; } sarg = mail_search_build_new(ctx, SEARCH_INTHREAD); sarg->value.thread_type = thread_type; if (mail_search_build_key(ctx, sarg, &sarg->value.subargs) < 0) return NULL; return sarg; } CALLBACK_STR(x_guid, SEARCH_GUID) static struct mail_search_arg * imap_search_x_mailbox(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; string_t *utf8_name; sarg = mail_search_build_str(ctx, SEARCH_MAILBOX_GLOB); if (sarg == NULL) return NULL; utf8_name = t_str_new(strlen(sarg->value.str)); if (imap_utf7_to_utf8(sarg->value.str, utf8_name) < 0) { ctx->_error = "X-MAILBOX name not mUTF-7"; return NULL; } sarg->value.str = p_strdup(ctx->pool, str_c(utf8_name)); return sarg; } static struct mail_search_arg * imap_search_x_real_uid(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* */ sarg = mail_search_build_str(ctx, SEARCH_REAL_UID); if (sarg == NULL) return NULL; p_array_init(&sarg->value.seqset, ctx->pool, 16); if (imap_seq_set_parse(sarg->value.str, &sarg->value.seqset) < 0) { ctx->_error = "Invalid X-REAL-UID messageset"; return NULL; } return sarg; } static const struct mail_search_register_arg imap_register_args[] = { /* argument set operations */ { "NOT", imap_search_not }, { "OR", imap_search_or }, /* message sets */ { "ALL", imap_search_all }, { "UID", imap_search_uid }, /* flags */ { "ANSWERED", imap_search_answered }, { "UNANSWERED", imap_search_unanswered }, { "DELETED", imap_search_deleted }, { "UNDELETED", imap_search_undeleted }, { "DRAFT", imap_search_draft }, { "UNDRAFT", imap_search_undraft }, { "FLAGGED", imap_search_flagged }, { "UNFLAGGED", imap_search_unflagged }, { "SEEN", imap_search_seen }, { "UNSEEN", imap_search_unseen }, { "RECENT", imap_search_recent }, { "OLD", imap_search_old }, { "NEW", imap_search_new }, /* keywords */ { "KEYWORD", imap_search_keyword }, { "UNKEYWORD", imap_search_unkeyword }, /* dates */ { "BEFORE", imap_search_before }, { "ON", imap_search_on }, { "SINCE", imap_search_since }, { "SENTBEFORE", imap_search_sentbefore }, { "SENTON", imap_search_senton }, { "SENTSINCE", imap_search_sentsince }, { "X-SAVEDBEFORE", imap_search_x_savedbefore }, { "X-SAVEDON", imap_search_x_savedon }, { "X-SAVEDSINCE", imap_search_x_savedsince }, /* sizes */ { "LARGER", imap_search_larger }, { "SMALLER", imap_search_smaller }, /* headers */ { "BCC", imap_search_bcc }, { "CC", imap_search_cc }, { "FROM", imap_search_from }, { "TO", imap_search_to }, { "SUBJECT", imap_search_subject }, { "HEADER", imap_search_header }, /* body */ { "BODY", imap_search_body }, { "TEXT", imap_search_text }, /* WITHIN extension: */ { "OLDER", imap_search_older }, { "YOUNGER", imap_search_younger }, /* CONDSTORE extension: */ { "MODSEQ", imap_search_modseq }, /* SEARCHRES extension: */ { "$", imap_search_last_result }, /* FUZZY extension: */ { "FUZZY", imap_search_fuzzy }, /* SEARCH=MIMEPART extension: */ { "MIMEPART", imap_search_mimepart }, /* Other Dovecot extensions: */ { "INTHREAD", imap_search_inthread }, { "X-GUID", imap_search_x_guid }, { "X-MAILBOX", imap_search_x_mailbox }, { "X-REAL-UID", imap_search_x_real_uid } }; static struct mail_search_register *mail_search_register_init_imap(void) { struct mail_search_register *reg; reg = mail_search_register_init(); mail_search_register_add(reg, imap_register_args, N_ELEMENTS(imap_register_args)); mail_search_register_fallback(reg, imap_search_fallback); return reg; } struct mail_search_register * mail_search_register_get_imap(void) { if (mail_search_register_imap == NULL) mail_search_register_imap = mail_search_register_init_imap(); return mail_search_register_imap; } dovecot-2.2.33.2/src/lib-storage/test-mail-search-args-simplify.c0000644000175000017500000003454613165463624021444 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "test-common.h" #include "mail-storage-private.h" #include "mail-search-build.h" #include "mail-search-parser.h" #include "mail-search.h" struct { const char *input; const char *output; } tests[] = { { "ALL", "ALL" }, { "NOT ALL", "NOT ALL" }, { "ALL NOT ALL", "NOT ALL" }, { "ALL NOT ALL TEXT foo", "NOT ALL" }, { "OR ALL NOT ALL", "ALL" }, { "OR ALL OR NOT ALL TEXT foo", "ALL" }, { "OR ALL OR TEXT foo TEXT bar", "ALL" }, { "OR TEXT FOO ( ALL NOT ALL )", "TEXT FOO" }, { "TEXT FOO OR ALL NOT ALL", "TEXT FOO" }, { "TEXT foo", "TEXT foo" }, { "( TEXT foo )", "TEXT foo" }, { "( ( TEXT foo ) )", "TEXT foo" }, { "( ( TEXT foo ) ( TEXT bar ) )", "TEXT foo TEXT bar" }, { "OR ( TEXT foo ) ( TEXT bar )", "OR TEXT foo TEXT bar" }, { "OR ( TEXT foo ) OR ( TEXT bar ) ( TEXT baz )", "OR TEXT foo OR TEXT bar TEXT baz" }, { "OR ( ( TEXT foo TEXT foo2 ) ) ( ( TEXT bar ( TEXT baz ) ) )", "OR (TEXT foo TEXT foo2) (TEXT bar TEXT baz)" }, { "NOT ( TEXT foo )", "NOT TEXT foo" }, { "NOT ( NOT ( TEXT foo ) )", "TEXT foo" }, { "NOT OR ( TEXT foo ) ( TEXT bar )", "NOT TEXT foo NOT TEXT bar" }, { "NOT ( OR ( TEXT foo ) ( TEXT bar ) )", "NOT TEXT foo NOT TEXT bar" }, { "NOT ( TEXT foo TEXT bar )", "OR NOT TEXT foo NOT TEXT bar" }, { "ANSWERED FLAGGED SEEN", "(ANSWERED FLAGGED SEEN)" }, { "OR ( ANSWERED FLAGGED SEEN ) DRAFT", "OR (ANSWERED FLAGGED SEEN) DRAFT" }, { "ANSWERED TEXT foo FLAGGED SEEN", "(ANSWERED FLAGGED SEEN) TEXT foo" }, { "NOT ( ANSWERED FLAGGED SEEN )", "NOT (ANSWERED FLAGGED SEEN)" }, { "OR NOT ANSWERED OR NOT FLAGGED NOT SEEN", "NOT (ANSWERED FLAGGED SEEN)" }, { "OR NOT ANSWERED OR NOT FLAGGED SEEN", "OR NOT (ANSWERED FLAGGED) SEEN" }, { "OR NOT ANSWERED OR FLAGGED NOT SEEN", "OR NOT (ANSWERED SEEN) FLAGGED" }, { "NOT ANSWERED OR FLAGGED NOT SEEN", "NOT ANSWERED OR FLAGGED NOT SEEN" }, { "NOT ANSWERED OR NOT FLAGGED NOT SEEN", "NOT ANSWERED NOT (FLAGGED SEEN)" }, { "ANSWERED NOT FLAGGED SEEN NOT DRAFT", "(ANSWERED SEEN) NOT FLAGGED NOT DRAFT" }, { "OR NOT ANSWERED NOT SEEN", "NOT (ANSWERED SEEN)" }, { "OR NOT ANSWERED OR NOT SEEN TEXT foo", "OR NOT (ANSWERED SEEN) TEXT foo" }, { "ANSWERED ANSWERED", "ANSWERED" }, { "ANSWERED NOT ANSWERED", "NOT ALL" }, { "ANSWERED ANSWERED NOT ANSWERED", "NOT ALL" }, { "ANSWERED NOT ANSWERED ANSWERED NOT ANSWERED", "NOT ALL" }, { "NOT ANSWERED NOT ANSWERED", "NOT ANSWERED" }, { "NOT SEEN NOT ANSWERED NOT ANSWERED", "NOT SEEN NOT ANSWERED" }, { "OR NOT SEEN OR NOT ANSWERED NOT ANSWERED", "NOT (ANSWERED SEEN)" }, { "KEYWORD foo", "KEYWORD foo" }, { "KEYWORD foo KEYWORD bar", "KEYWORD foo KEYWORD bar" }, { "NOT KEYWORD foo", "NOT KEYWORD foo" }, { "NOT KEYWORD foo NOT KEYWORD bar", "NOT KEYWORD foo NOT KEYWORD bar" }, { "OR KEYWORD foo KEYWORD bar", "OR KEYWORD foo KEYWORD bar" }, { "OR NOT KEYWORD foo NOT KEYWORD bar", "OR NOT KEYWORD foo NOT KEYWORD bar" }, { "KEYWORD foo KEYWORD foo", "KEYWORD foo" }, { "KEYWORD foo NOT KEYWORD foo", "NOT ALL" }, { "OR KEYWORD foo NOT KEYWORD foo", "ALL" }, { "OR KEYWORD foo KEYWORD foo", "KEYWORD foo" }, { "NOT KEYWORD foo NOT KEYWORD foo", "NOT KEYWORD foo" }, { "1:* 1:*", "ALL" }, { "OR 1:5 6:*", "ALL" }, { "UID 1:* UID 1:*", "ALL" }, { "OR UID 1:5 UID 6:*", "ALL" }, { "2:* 2:*", "2:4294967295" }, { "OR 2:* 2:*", "2:4294967295" }, { "UID 2:* UID 2:*", "UID 2:4294967295" }, { "OR UID 2:* UID 2:*", "UID 2:4294967295" }, { "1:5 6:7", "NOT ALL" }, { "1:5 3:7", "3:5" }, { "1:5 3:7 4:9", "4:5" }, { "1:5 OR 3:4 4:6", "3:5" }, { "OR 1 2", "1:2" }, { "NOT 1,3:5", "2,6:4294967294" }, { "NOT 1:100 NOT 50:200", "201:4294967294" }, { "OR NOT 1:100 NOT 50:200", "1:49,101:4294967294" }, { "UID 1:5 UID 6:7", "NOT ALL" }, { "UID 1:5 UID 3:7", "UID 3:5" }, { "OR UID 1 UID 2", "UID 1:2" }, { "NOT UID 1,3:5", "UID 2,6:4294967294" }, { "1:5 UID 10:20", "1:5 UID 10:20" }, { "1:5 NOT UID 10:20", "1:5 UID 1:9,21:4294967294" }, { "ALL NOT UID 3:*", "NOT UID 3:4294967295" }, { "NOT 1:10 NOT *", "11:4294967294 NOT 4294967295" }, { "BEFORE 03-Aug-2014 BEFORE 01-Aug-2014 BEFORE 02-Aug-2014", "BEFORE \"01-Aug-2014\"" }, { "OR BEFORE 01-Aug-2014 BEFORE 02-Aug-2014", "BEFORE \"02-Aug-2014\"" }, { "OR BEFORE 01-Aug-2014 OR BEFORE 03-Aug-2014 BEFORE 02-Aug-2014", "BEFORE \"03-Aug-2014\"" }, { "BEFORE 03-Aug-2014 NOT BEFORE 01-Aug-2014 BEFORE 02-Aug-2014", "BEFORE \"02-Aug-2014\" NOT BEFORE \"01-Aug-2014\"" }, { "SENTBEFORE 03-Aug-2014 SENTBEFORE 01-Aug-2014 SENTBEFORE 02-Aug-2014", "SENTBEFORE \"01-Aug-2014\"" }, { "SENTBEFORE 03-Aug-2014 BEFORE 01-Aug-2014 SENTBEFORE 02-Aug-2014", "SENTBEFORE \"02-Aug-2014\" BEFORE \"01-Aug-2014\"" }, { "ON 03-Aug-2014 ON 03-Aug-2014", "ON \"03-Aug-2014\"" }, { "ON 03-Aug-2014 ON 04-Aug-2014", "ON \"03-Aug-2014\" ON \"04-Aug-2014\"" }, /* this could be replaced with e.g. NOT ALL */ { "OR ON 03-Aug-2014 ON 04-Aug-2014", "OR ON \"03-Aug-2014\" ON \"04-Aug-2014\"" }, { "SINCE 03-Aug-2014 SINCE 01-Aug-2014 SINCE 02-Aug-2014", "SINCE \"03-Aug-2014\"" }, { "OR SINCE 01-Aug-2014 SINCE 02-Aug-2014", "SINCE \"01-Aug-2014\"" }, { "OR SINCE 01-Aug-2014 OR SINCE 03-Aug-2014 SINCE 02-Aug-2014", "SINCE \"01-Aug-2014\"" }, { "SINCE 03-Aug-2014 NOT SINCE 01-Aug-2014 SINCE 02-Aug-2014", "SINCE \"03-Aug-2014\" NOT SINCE \"01-Aug-2014\"" }, { "SENTSINCE 03-Aug-2014 SENTSINCE 01-Aug-2014 SENTSINCE 02-Aug-2014", "SENTSINCE \"03-Aug-2014\"" }, { "SENTSINCE 03-Aug-2014 SINCE 01-Aug-2014 SENTSINCE 02-Aug-2014", "SENTSINCE \"03-Aug-2014\" SINCE \"01-Aug-2014\"" }, { "SMALLER 1 SMALLER 2", "SMALLER 1" }, { "OR SMALLER 1 SMALLER 2", "SMALLER 2" }, { "OR SMALLER 1 OR SMALLER 3 SMALLER 2", "SMALLER 3" }, { "SMALLER 3 NOT SMALLER 1 SMALLER 2", "SMALLER 2 NOT SMALLER 1" }, { "SMALLER 3 LARGER 5", "SMALLER 3 LARGER 5" }, /* this could be replaced with e.g. NOT ALL */ { "OR SMALLER 3 LARGER 5", "OR SMALLER 3 LARGER 5" }, { "LARGER 3 LARGER 1 LARGER 2", "LARGER 3" }, { "OR LARGER 1 LARGER 2", "LARGER 1" }, { "OR LARGER 1 OR LARGER 3 LARGER 2", "LARGER 1" }, { "LARGER 3 NOT LARGER 1 LARGER 2", "LARGER 3 NOT LARGER 1" }, { "SUBJECT foo SUBJECT foo", "SUBJECT foo" }, { "SUBJECT foo NOT SUBJECT foo", "NOT ALL" }, { "OR SUBJECT foo NOT SUBJECT foo", "ALL" }, { "SUBJECT foo SUBJECT foob", "SUBJECT foo SUBJECT foob" }, { "OR SUBJECT foo SUBJECT foo", "SUBJECT foo" }, { "FROM foo FROM foo", "FROM foo" }, { "FROM foo NOT FROM foo", "NOT ALL" }, { "OR FROM foo NOT FROM foo", "ALL" }, { "FROM foo FROM bar", "FROM foo FROM bar" }, { "FROM foo TO foo", "FROM foo TO foo" }, { "TEXT foo TEXT foo", "TEXT foo" }, { "TEXT foo TEXT foob", "TEXT foo TEXT foob" }, { "OR TEXT foo TEXT foo", "TEXT foo" }, { "OR NOT TEXT foo TEXT foo", "ALL" }, { "OR TEXT foo NOT TEXT foo", "ALL" }, { "TEXT foo NOT TEXT foo", "NOT ALL" }, { "NOT TEXT foo TEXT foo", "NOT ALL" }, { "BODY foo BODY foo", "BODY foo" }, { "BODY foo NOT BODY foo", "NOT ALL" }, { "OR BODY foo NOT BODY foo", "ALL" }, { "OR BODY foo BODY foo", "BODY foo" }, { "TEXT foo BODY foo", "TEXT foo BODY foo" }, { "OR ( TEXT foo OR TEXT foo TEXT foo ) ( TEXT foo ( TEXT foo ) )", "TEXT foo" }, /* value="" tests */ { "HEADER foo ", "HEADER FOO \"\"" }, { "SUBJECT ", "SUBJECT \"\"" }, { "BODY ", "ALL" }, { "TEXT ", "ALL" }, { "HEADER foo .", "HEADER FOO ." }, { "SUBJECT .", "SUBJECT ." }, { "BODY .", "BODY ." }, { "TEXT .", "TEXT ." }, /* OR: drop redundant args */ { "OR ( TEXT common1 TEXT unique1 ) TEXT common1", "TEXT common1" }, { "OR ( TEXT unique1 TEXT common1 ) TEXT common1", "TEXT common1" }, { "OR TEXT common1 ( TEXT common1 TEXT unique1 )", "TEXT common1" }, { "OR TEXT common1 ( TEXT unique1 TEXT common1 )", "TEXT common1" }, { "OR ( TEXT common1 TEXT common2 ) ( TEXT common1 TEXT common2 TEXT unique1 )", "TEXT common1 TEXT common2" }, { "OR TEXT common1 OR ( TEXT unique1 TEXT common1 ) ( TEXT unique3 TEXT common1 )", "TEXT common1" }, /* OR: extract common AND */ { "OR ( TEXT common1 TEXT unique1 ) ( TEXT common1 TEXT unique2 )", "OR TEXT unique1 TEXT unique2 TEXT common1" }, { "OR ( TEXT unique1 TEXT common1 ) ( TEXT unique2 TEXT common1 )", "OR TEXT unique1 TEXT unique2 TEXT common1" }, { "OR ( TEXT common1 TEXT unique1 ) ( TEXT unique2 TEXT common1 )", "OR TEXT unique1 TEXT unique2 TEXT common1" }, { "OR ( TEXT unique1 TEXT common1 ) ( TEXT common1 TEXT unique2 )", "OR TEXT unique1 TEXT unique2 TEXT common1" }, { "OR ( TEXT unique1 TEXT common1 ) ( TEXT common1 TEXT unique2 TEXT unique3 )", "OR TEXT unique1 (TEXT unique2 TEXT unique3) TEXT common1" }, { "OR ( TEXT common1 TEXT common2 TEXT unique1 ) ( TEXT common1 TEXT common2 TEXT unique2 )", "OR TEXT unique1 TEXT unique2 TEXT common2 TEXT common1" }, { "OR ( TEXT common1 TEXT common2 TEXT unique1 TEXT unique2 ) ( TEXT common1 TEXT common2 TEXT unique3 TEXT unique4 )", "OR (TEXT unique1 TEXT unique2) (TEXT unique3 TEXT unique4) TEXT common2 TEXT common1" }, /* non-matching cases */ { "OR ( TEXT unique1 TEXT unique2 ) TEXT unique3", "OR (TEXT unique1 TEXT unique2) TEXT unique3" }, { "OR ( TEXT unique1 TEXT unique2 ) ( TEXT unique3 TEXT unique4 )", "OR (TEXT unique1 TEXT unique2) (TEXT unique3 TEXT unique4)" }, { "OR ( TEXT common1 TEXT unique1 ) OR ( TEXT common1 TEXT unique2 ) TEXT unique3", "OR (TEXT common1 TEXT unique1) OR (TEXT common1 TEXT unique2) TEXT unique3" }, { "OR ( TEXT common1 TEXT unique1 ) OR ( TEXT common1 TEXT common2 ) ( TEXT common2 TEXT unique2 )", "OR (TEXT common1 TEXT unique1) OR (TEXT common1 TEXT common2) (TEXT common2 TEXT unique2)" }, /* SUB: drop redundant args */ { "( OR TEXT common1 TEXT unique1 ) TEXT common1", "TEXT common1" }, { "( OR TEXT unique1 TEXT common1 ) TEXT common1", "TEXT common1" }, { "TEXT common1 ( OR TEXT common1 TEXT unique1 )", "TEXT common1" }, { "TEXT common1 ( OR TEXT unique1 TEXT common1 )", "TEXT common1" }, { "( OR TEXT common1 TEXT common2 ) ( OR TEXT common1 OR TEXT common2 TEXT unique1 )", "OR TEXT common1 TEXT common2" }, { "TEXT common1 ( OR TEXT unique1 TEXT common1 ) ( OR TEXT unique3 TEXT common1 )", "TEXT common1" }, { "OR ( TEXT common1 ( OR TEXT unique1 TEXT common1 ) ) TEXT unique1", "OR TEXT common1 TEXT unique1" }, /* SUB: extract common OR */ { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT common1 TEXT unique2 )", "OR (TEXT unique1 TEXT unique2) TEXT common1" }, { "( OR TEXT unique1 TEXT common1 ) ( OR TEXT unique2 TEXT common1 )", "OR (TEXT unique1 TEXT unique2) TEXT common1" }, { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT unique2 TEXT common1 )", "OR (TEXT unique1 TEXT unique2) TEXT common1" }, { "( OR TEXT unique1 TEXT common1 ) ( OR TEXT common1 TEXT unique2 )", "OR (TEXT unique1 TEXT unique2) TEXT common1" }, { "( OR TEXT unique1 TEXT common1 ) ( OR TEXT common1 OR TEXT unique2 TEXT unique3 )", "OR (TEXT unique1 OR TEXT unique2 TEXT unique3) TEXT common1" }, { "( OR TEXT common1 OR TEXT common2 TEXT unique1 ) ( OR TEXT common1 OR TEXT common2 TEXT unique2 )", "OR (TEXT unique1 TEXT unique2) OR TEXT common2 TEXT common1" }, { "( OR TEXT common1 OR TEXT common2 OR TEXT unique1 TEXT unique2 ) ( OR TEXT common1 OR TEXT common2 OR TEXT unique3 TEXT unique4 )", "OR (OR TEXT unique1 TEXT unique2 OR TEXT unique3 TEXT unique4) OR TEXT common2 TEXT common1" }, /* non-matching cases */ { "( OR TEXT unique1 TEXT unique2 ) TEXT unique3", "OR TEXT unique1 TEXT unique2 TEXT unique3" }, { "( OR TEXT unique1 TEXT unique2 ) ( OR TEXT unique3 TEXT unique4 )", "OR TEXT unique1 TEXT unique2 OR TEXT unique3 TEXT unique4" }, { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT common1 TEXT unique2 ) TEXT unique3", "OR TEXT common1 TEXT unique1 OR TEXT common1 TEXT unique2 TEXT unique3" }, { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT common1 TEXT common2 ) ( OR TEXT common2 TEXT unique2 )", "OR TEXT common1 TEXT unique1 OR TEXT common1 TEXT common2 OR TEXT common2 TEXT unique2" }, }; static struct mail_search_args * test_build_search_args(const char *args) { struct mail_search_parser *parser; struct mail_search_args *sargs; const char *error, *charset = "UTF-8"; parser = mail_search_parser_init_cmdline(t_strsplit(args, " ")); if (mail_search_build(mail_search_register_get_imap(), parser, &charset, &sargs, &error) < 0) i_panic("%s", error); mail_search_parser_deinit(&parser); return sargs; } static bool test_search_args_are_initialized(struct mail_search_arg *arg) { for (; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_MODSEQ: if (arg->value.str != NULL && arg->initialized.keywords == NULL) return FALSE; break; case SEARCH_KEYWORDS: if (arg->initialized.keywords == NULL) return FALSE; break; case SEARCH_MAILBOX_GLOB: if (arg->initialized.mailbox_glob == NULL) return FALSE; break; case SEARCH_INTHREAD: case SEARCH_SUB: case SEARCH_OR: if (!test_search_args_are_initialized(arg->value.subargs)) return FALSE; break; default: break; } } return TRUE; } static void test_mail_search_args_simplify(void) { struct mail_search_args *args; struct mail_storage_settings set = { .mail_max_keyword_length = 100 }; struct mail_storage storage = { .set = &set }; struct mailbox box = { .opened = TRUE, .storage = &storage }; string_t *str = t_str_new(256); const char *error; unsigned int i; test_begin("mail search args simplify"); box.index = mail_index_alloc(NULL, "dovecot.index."); for (i = 0; i < N_ELEMENTS(tests); i++) { args = test_build_search_args(tests[i].input); /* delay simplification until after init. that way we can test that the simplification works correctly when working on already-initialized args. */ args->simplified = TRUE; mail_search_args_init(args, &box, FALSE, NULL); mail_search_args_simplify(args); str_truncate(str, 0); test_assert(mail_search_args_to_imap(str, args->args, &error)); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); test_assert_idx(test_search_args_are_initialized(args->args), i); mail_search_args_unref(&args); } mail_index_free(&box.index); test_end(); } static void test_mail_search_args_simplify_empty_lists(void) { struct mail_search_args *args; test_begin("mail search args simplify empty args"); args = mail_search_build_init(); mail_search_args_simplify(args); mail_search_args_unref(&args); test_end(); } int main(void) { static void (*test_functions[])(void) = { mail_storage_init, test_mail_search_args_simplify, test_mail_search_args_simplify_empty_lists, mail_storage_deinit, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-storage/mailbox-search-result-private.h0000644000175000017500000000261713165463624021377 00000000000000#ifndef MAILBOX_SEARCH_RESULT_PRIVATE_H #define MAILBOX_SEARCH_RESULT_PRIVATE_H #include "mail-storage.h" struct mail_search_result { struct mailbox *box; enum mailbox_search_result_flags flags; struct mail_search_args *search_args; /* UIDs of messages currently in the result */ ARRAY_TYPE(seq_range) uids; /* UIDs of messages that will never match the result */ ARRAY_TYPE(seq_range) never_uids; ARRAY_TYPE(seq_range) removed_uids, added_uids; unsigned int args_have_flags:1; unsigned int args_have_keywords:1; unsigned int args_have_modseq:1; }; struct mail_search_result * mailbox_search_result_alloc(struct mailbox *box, struct mail_search_args *args, enum mailbox_search_result_flags flags); /* called when initial search is done. */ void mailbox_search_result_initial_done(struct mail_search_result *result); void mailbox_search_results_initial_done(struct mail_search_context *ctx); void mailbox_search_result_add(struct mail_search_result *result, uint32_t uid); void mailbox_search_result_remove(struct mail_search_result *result, uint32_t uid); void mailbox_search_results_add(struct mail_search_context *ctx, uint32_t uid); void mailbox_search_results_remove(struct mailbox *box, uint32_t uid); void mailbox_search_result_never(struct mail_search_result *result, uint32_t uid); void mailbox_search_results_never(struct mail_search_context *ctx, uint32_t uid); #endif dovecot-2.2.33.2/src/lib-storage/mail-thread.h0000644000175000017500000000374113123174404015670 00000000000000#ifndef MAIL_THREAD_H #define MAIL_THREAD_H struct mailbox; struct mail_search_args; struct mail_thread_context; enum mail_thread_type { MAIL_THREAD_NONE, MAIL_THREAD_ORDEREDSUBJECT, MAIL_THREAD_REFERENCES, MAIL_THREAD_REFS }; struct mail_thread_child_node { /* Node's index in mail hash transaction */ uint32_t idx; /* UID or sequence */ uint32_t uid; /* Timestamp node was sorted with (depends on thread algorithm) */ time_t sort_date; }; ARRAY_DEFINE_TYPE(mail_thread_child_node, struct mail_thread_child_node); /* Convert thread type string to enum. Returns TRUE if ok, FALSE if type is unknown. */ bool mail_thread_type_parse(const char *str, enum mail_thread_type *type_r); /* Return thread type as string. */ const char *mail_thread_type_to_str(enum mail_thread_type type); /* Build thread from given search arguments. args=NULL searches everything. */ int mail_thread_init(struct mailbox *box, struct mail_search_args *args, struct mail_thread_context **ctx_r) ATTR_NULL(2); void mail_thread_deinit(struct mail_thread_context **ctx); /* Iterate through thread tree. If write_seqs=TRUE, sequences are returned in mail_thread_child_node.uid instead of UIDs. */ struct mail_thread_iterate_context * mail_thread_iterate_init(struct mail_thread_context *ctx, enum mail_thread_type thread_type, bool write_seqs); /* If child_iter_r is not NULL, it's set to contain another iterator if the returned node contains children. The returned iterator must be freed explicitly. */ const struct mail_thread_child_node * mail_thread_iterate_next(struct mail_thread_iterate_context *iter, struct mail_thread_iterate_context **child_iter_r); /* Returns number of nodes in the current iterator. */ unsigned int mail_thread_iterate_count(struct mail_thread_iterate_context *iter); /* Free the iterator. Iterators don't reference other iterators, so it doesn't matter in which order they're freed. */ int mail_thread_iterate_deinit(struct mail_thread_iterate_context **iter); #endif dovecot-2.2.33.2/src/lib-storage/mail-storage-hooks.c0000644000175000017500000001754113123174404017204 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hook-build.h" #include "llist.h" #include "module-dir.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" struct mail_storage_module_hooks { struct module *module; const struct mail_storage_hooks *hooks; bool forced; }; static ARRAY(struct mail_storage_module_hooks) module_hooks = ARRAY_INIT; static ARRAY(const struct mail_storage_hooks *) internal_hooks = ARRAY_INIT; void mail_storage_hooks_init(void) { if (!array_is_created(&module_hooks)) i_array_init(&module_hooks, 32); i_array_init(&internal_hooks, 8); } void mail_storage_hooks_deinit(void) { /* allow calling this even if mail_storage_hooks_init() hasn't been called, because e.g. doveadm plugins could call mail_storage_hooks_add() even though mail storage is never initialized. */ if (array_is_created(&internal_hooks)) array_free(&internal_hooks); if (array_is_created(&module_hooks)) array_free(&module_hooks); } void mail_storage_hooks_add(struct module *module, const struct mail_storage_hooks *hooks) { struct mail_storage_module_hooks new_hook; i_zero(&new_hook); new_hook.module = module; new_hook.hooks = hooks; /* allow adding hooks before mail_storage_hooks_init() */ if (!array_is_created(&module_hooks)) i_array_init(&module_hooks, 32); array_append(&module_hooks, &new_hook, 1); } void mail_storage_hooks_add_forced(struct module *module, const struct mail_storage_hooks *hooks) { struct mail_storage_module_hooks *hook; mail_storage_hooks_add(module, hooks); hook = array_idx_modifiable(&module_hooks, array_count(&module_hooks)-1); hook->forced = TRUE; } void mail_storage_hooks_remove(const struct mail_storage_hooks *hooks) { const struct mail_storage_module_hooks *module_hook; unsigned int idx = UINT_MAX; array_foreach(&module_hooks, module_hook) { if (module_hook->hooks == hooks) { idx = array_foreach_idx(&module_hooks, module_hook); break; } } i_assert(idx != UINT_MAX); array_delete(&module_hooks, idx, 1); } void mail_storage_hooks_add_internal(const struct mail_storage_hooks *hooks) { const struct mail_storage_hooks *const *existing_hooksp; /* make sure we don't add duplicate hooks */ array_foreach(&internal_hooks, existing_hooksp) i_assert(*existing_hooksp != hooks); array_append(&internal_hooks, &hooks, 1); } void mail_storage_hooks_remove_internal(const struct mail_storage_hooks *hooks) { const struct mail_storage_hooks *const *old_hooks; unsigned int idx = UINT_MAX; array_foreach(&internal_hooks, old_hooks) { if (*old_hooks == hooks) { idx = array_foreach_idx(&internal_hooks, old_hooks); break; } } i_assert(idx != UINT_MAX); array_delete(&internal_hooks, idx, 1); } static int mail_storage_module_hooks_cmp(const struct mail_storage_module_hooks *h1, const struct mail_storage_module_hooks *h2) { const char *s1 = h1->module->path, *s2 = h2->module->path; const char *p; p = strrchr(s1, '/'); if (p != NULL) s1 = p+1; p = strrchr(s2, '/'); if (p != NULL) s2 = p+1; if (strncmp(s1, "lib", 3) == 0) s1 += 3; if (strncmp(s2, "lib", 3) == 0) s2 += 3; return strcmp(s1, s2); } static void mail_user_add_plugin_hooks(struct mail_user *user) { const struct mail_storage_module_hooks *module_hook; ARRAY(struct mail_storage_module_hooks) tmp_hooks; const char *const *plugins, *name; /* first get all hooks wanted by the user */ t_array_init(&tmp_hooks, array_count(&module_hooks)); plugins = t_strsplit_spaces(user->set->mail_plugins, ", "); array_foreach(&module_hooks, module_hook) { if (!module_hook->forced) { name = module_get_plugin_name(module_hook->module); if (!str_array_find(plugins, name)) continue; } array_append(&tmp_hooks, module_hook, 1); } /* next we have to sort them by the modules' priority (based on name) */ array_sort(&tmp_hooks, mail_storage_module_hooks_cmp); /* now that we have them in order, save them to user's hooks */ p_array_init(&user->hooks, user->pool, array_count(&tmp_hooks) + array_count(&internal_hooks)); array_foreach(&tmp_hooks, module_hook) array_append(&user->hooks, &module_hook->hooks, 1); array_append_array(&user->hooks, &internal_hooks); } void hook_mail_user_created(struct mail_user *user) { const struct mail_storage_hooks *const *hooks; struct hook_build_context *ctx; mail_user_add_plugin_hooks(user); ctx = hook_build_init((void *)&user->v, sizeof(user->v)); user->vlast = &user->v; array_foreach(&user->hooks, hooks) { if ((*hooks)->mail_user_created != NULL) T_BEGIN { (*hooks)->mail_user_created(user); hook_build_update(ctx, user->vlast); } T_END; } user->vlast = NULL; hook_build_deinit(&ctx); } void hook_mail_namespace_storage_added(struct mail_namespace *ns) { const struct mail_storage_hooks *const *hooks; array_foreach(&ns->user->hooks, hooks) { if ((*hooks)->mail_namespace_storage_added != NULL) T_BEGIN { (*hooks)->mail_namespace_storage_added(ns); } T_END; } } void hook_mail_namespaces_created(struct mail_namespace *namespaces) { const struct mail_storage_hooks *const *hooks; array_foreach(&namespaces->user->hooks, hooks) { if (namespaces->user->error != NULL) break; if ((*hooks)->mail_namespaces_created != NULL) T_BEGIN { (*hooks)->mail_namespaces_created(namespaces); } T_END; } } void hook_mail_namespaces_added(struct mail_namespace *namespaces) { const struct mail_storage_hooks *const *hooks; array_foreach(&namespaces->user->hooks, hooks) { if (namespaces->user->error != NULL) break; if ((*hooks)->mail_namespaces_added != NULL) T_BEGIN { (*hooks)->mail_namespaces_added(namespaces); } T_END; } } void hook_mail_storage_created(struct mail_storage *storage) { const struct mail_storage_hooks *const *hooks; struct hook_build_context *ctx; ctx = hook_build_init((void *)&storage->v, sizeof(storage->v)); storage->vlast = &storage->v; array_foreach(&storage->user->hooks, hooks) { if ((*hooks)->mail_storage_created != NULL) T_BEGIN { (*hooks)->mail_storage_created(storage); hook_build_update(ctx, storage->vlast); } T_END; } storage->vlast = NULL; hook_build_deinit(&ctx); } void hook_mailbox_list_created(struct mailbox_list *list) { const struct mail_storage_hooks *const *hooks; struct hook_build_context *ctx; ctx = hook_build_init((void *)&list->v, sizeof(list->v)); list->vlast = &list->v; array_foreach(&list->ns->user->hooks, hooks) { if ((*hooks)->mailbox_list_created != NULL) T_BEGIN { (*hooks)->mailbox_list_created(list); hook_build_update(ctx, list->vlast); } T_END; } list->vlast = NULL; hook_build_deinit(&ctx); } void hook_mailbox_allocated(struct mailbox *box) { const struct mail_storage_hooks *const *hooks; struct hook_build_context *ctx; ctx = hook_build_init((void *)&box->v, sizeof(box->v)); box->vlast = &box->v; array_foreach(&box->storage->user->hooks, hooks) { if ((*hooks)->mailbox_allocated != NULL) T_BEGIN { (*hooks)->mailbox_allocated(box); hook_build_update(ctx, box->vlast); } T_END; } box->vlast = NULL; hook_build_deinit(&ctx); } void hook_mailbox_opened(struct mailbox *box) { const struct mail_storage_hooks *const *hooks; array_foreach(&box->storage->user->hooks, hooks) { if ((*hooks)->mailbox_opened != NULL) T_BEGIN { (*hooks)->mailbox_opened(box); } T_END; } } void hook_mail_allocated(struct mail *mail) { const struct mail_storage_hooks *const *hooks; struct mail_private *pmail = (struct mail_private *)mail; struct hook_build_context *ctx; ctx = hook_build_init((void *)&pmail->v, sizeof(pmail->v)); pmail->vlast = &pmail->v; array_foreach(&mail->box->storage->user->hooks, hooks) { if ((*hooks)->mail_allocated != NULL) T_BEGIN { (*hooks)->mail_allocated(mail); hook_build_update(ctx, pmail->vlast); } T_END; } pmail->vlast = NULL; hook_build_deinit(&ctx); } dovecot-2.2.33.2/src/lib-storage/mail-search-args-cmdline.c0000644000175000017500000000516013123174404020221 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-quote.h" #include "mail-search.h" static void mail_search_arg_to_cmdline(string_t *dest, const struct mail_search_arg *arg); static void mail_search_subargs_to_cmdline(string_t *dest, const struct mail_search_arg *args, const char *middle) { const struct mail_search_arg *arg; str_append(dest, "( "); for (arg = args; arg != NULL; arg = arg->next) { mail_search_arg_to_cmdline(dest, arg); if (arg->next != NULL) str_append(dest, middle); } str_append(dest, " )"); } static void mail_search_arg_to_cmdline(string_t *dest, const struct mail_search_arg *arg) { struct mail_search_arg new_arg; const char *error; if (arg->match_not) str_append(dest, "NOT "); switch (arg->type) { case SEARCH_OR: mail_search_subargs_to_cmdline(dest, arg->value.subargs, " OR "); return; case SEARCH_SUB: mail_search_subargs_to_cmdline(dest, arg->value.subargs, " "); return; case SEARCH_FLAGS: case SEARCH_KEYWORDS: { size_t pos = str_len(dest); new_arg = *arg; new_arg.match_not = FALSE; if (!mail_search_arg_to_imap(dest, &new_arg, &error)) i_unreached(); if (str_c(dest)[pos] == '(') { str_insert(dest, pos+1, " "); str_insert(dest, str_len(dest)-1, " "); } return; } case SEARCH_INTHREAD: str_append(dest, "INTHREAD "); imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type)); str_append_c(dest, ' '); mail_search_subargs_to_cmdline(dest, arg->value.subargs, " "); break; case SEARCH_MAILBOX: case SEARCH_MAILBOX_GLOB: str_append(dest, "MAILBOX "); imap_append_astring(dest, arg->value.str); return; case SEARCH_MAILBOX_GUID: str_append(dest, "MAILBOX-GUID "); imap_append_astring(dest, arg->value.str); return; case SEARCH_ALL: case SEARCH_SEQSET: case SEARCH_UIDSET: case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: case SEARCH_SMALLER: case SEARCH_LARGER: case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: case SEARCH_BODY: case SEARCH_TEXT: case SEARCH_MODSEQ: case SEARCH_GUID: case SEARCH_REAL_UID: case SEARCH_MIMEPART: break; } new_arg = *arg; new_arg.match_not = FALSE; if (!mail_search_arg_to_imap(dest, &new_arg, &error)) i_panic("mail_search_args_to_cmdline(): Missing handler: %s", error); } void mail_search_args_to_cmdline(string_t *dest, const struct mail_search_arg *args) { const struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { mail_search_arg_to_cmdline(dest, arg); if (arg->next != NULL) str_append_c(dest, ' '); } } dovecot-2.2.33.2/src/lib-storage/mailbox-get.c0000644000175000017500000001475313123174404015711 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-modseq.h" #include "mail-storage-private.h" void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2, uint32_t *seq1_r, uint32_t *seq2_r) { (void)mail_index_lookup_seq_range(box->view, uid1, uid2, seq1_r, seq2_r); } void mailbox_get_uid_range(struct mailbox *box, const ARRAY_TYPE(seq_range) *seqs, ARRAY_TYPE(seq_range) *uids) { const struct seq_range *range; unsigned int i, count; uint32_t seq, uid; range = array_get(seqs, &count); for (i = 0; i < count; i++) { if (range[i].seq2 == (uint32_t)-1) { i_assert(count == i-1); mail_index_lookup_uid(box->view, range[i].seq1, &uid); seq_range_array_add_range(uids, uid, (uint32_t)-1); break; } for (seq = range[i].seq1; seq <= range[i].seq2; seq++) { mail_index_lookup_uid(box->view, seq, &uid); seq_range_array_add(uids, uid); } } } static void add_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid, const struct mail_transaction_expunge *src, size_t src_size) { const struct mail_transaction_expunge *end; end = src + src_size / sizeof(*src); for (; src != end; src++) { if (src->uid2 >= min_uid) { seq_range_array_add_range(expunged_uids, src->uid1, src->uid2); } } } static void add_guid_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid, const struct mail_transaction_expunge_guid *src, size_t src_size) { const struct mail_transaction_expunge_guid *end; end = src + src_size / sizeof(*src); for (; src != end; src++) { if (src->uid >= min_uid) seq_range_array_add(expunged_uids, src->uid); } } static int mailbox_get_expunges_init(struct mailbox *box, uint64_t prev_modseq, struct mail_transaction_log_view **log_view_r, bool *modseq_too_old_r) { struct mail_transaction_log_view *log_view; uint32_t log_seq, tail_seq; uoff_t log_offset; const char *reason; bool reset; int ret; *modseq_too_old_r = FALSE; if (!mail_index_modseq_get_next_log_offset(box->view, prev_modseq, &log_seq, &log_offset)) { log_seq = 1; log_offset = 0; *modseq_too_old_r = TRUE; } if (log_seq > box->view->log_file_head_seq || (log_seq == box->view->log_file_head_seq && log_offset >= box->view->log_file_head_offset)) { /* we haven't seen this high expunges at all */ return 1; } log_view = mail_transaction_log_view_open(box->index->log); ret = mail_transaction_log_view_set(log_view, log_seq, log_offset, box->view->log_file_head_seq, box->view->log_file_head_offset, &reset, &reason); if (ret == 0) { mail_transaction_log_get_tail(box->index->log, &tail_seq); i_assert(tail_seq > log_seq); ret = mail_transaction_log_view_set(log_view, tail_seq, 0, box->view->log_file_head_seq, box->view->log_file_head_offset, &reset, &reason); i_assert(ret != 0); *modseq_too_old_r = TRUE; } if (ret <= 0) { mail_transaction_log_view_close(&log_view); return -1; } *log_view_r = log_view; return 0; } static void mailbox_get_expunged_guids(struct mail_transaction_log_view *log_view, ARRAY_TYPE(seq_range) *expunged_uids, ARRAY_TYPE(mailbox_expunge_rec) *expunges) { const struct mail_transaction_header *thdr; const void *tdata; const struct mail_transaction_expunge_guid *rec, *end; struct mailbox_expunge_rec *expunge; struct seq_range_iter iter; unsigned int n; uint32_t uid; while (mail_transaction_log_view_next(log_view, &thdr, &tdata) > 0) { if ((thdr->type & MAIL_TRANSACTION_TYPE_MASK) != MAIL_TRANSACTION_EXPUNGE_GUID) continue; rec = tdata; end = rec + thdr->size / sizeof(*rec); for (; rec != end; rec++) { if (!seq_range_exists(expunged_uids, rec->uid)) continue; seq_range_array_remove(expunged_uids, rec->uid); expunge = array_append_space(expunges); expunge->uid = rec->uid; memcpy(expunge->guid_128, rec->guid_128, sizeof(expunge->guid_128)); } } /* everything left in expunged_uids didn't get a GUID */ seq_range_array_iter_init(&iter, expunged_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { expunge = array_append_space(expunges); expunge->uid = uid; } } static bool ATTR_NULL(4, 5) mailbox_get_expunges_full(struct mailbox *box, uint64_t prev_modseq, const ARRAY_TYPE(seq_range) *uids_filter, ARRAY_TYPE(seq_range) *expunged_uids, ARRAY_TYPE(mailbox_expunge_rec) *expunges) { struct mail_transaction_log_view *log_view; ARRAY_TYPE(seq_range) tmp_expunged_uids = ARRAY_INIT; const struct mail_transaction_header *thdr; const struct seq_range *range; const void *tdata; uint32_t min_uid; bool modseq_too_old; int ret; i_assert(array_count(uids_filter) > 0); i_assert(expunged_uids == NULL || expunges == NULL); ret = mailbox_get_expunges_init(box, prev_modseq, &log_view, &modseq_too_old); if (ret != 0) return ret > 0; range = array_idx(uids_filter, 0); min_uid = range->seq1; /* first get UIDs of all actual expunges */ if (expunged_uids == NULL) { i_array_init(&tmp_expunged_uids, 64); expunged_uids = &tmp_expunged_uids; } mail_transaction_log_view_mark(log_view); while ((ret = mail_transaction_log_view_next(log_view, &thdr, &tdata)) > 0) { if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* skip expunge requests */ continue; } switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: add_expunges(expunged_uids, min_uid, tdata, thdr->size); break; case MAIL_TRANSACTION_EXPUNGE_GUID: add_guid_expunges(expunged_uids, min_uid, tdata, thdr->size); break; } } mail_transaction_log_view_rewind(log_view); /* drop UIDs that don't match the filter */ seq_range_array_intersect(expunged_uids, uids_filter); if (expunges != NULL) { mailbox_get_expunged_guids(log_view, expunged_uids, expunges); array_free(&tmp_expunged_uids); } mail_transaction_log_view_close(&log_view); return ret < 0 || modseq_too_old ? FALSE : TRUE; } bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq, const ARRAY_TYPE(seq_range) *uids_filter, ARRAY_TYPE(mailbox_expunge_rec) *expunges) { return mailbox_get_expunges_full(box, prev_modseq, uids_filter, NULL, expunges); } bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq, const ARRAY_TYPE(seq_range) *uids_filter, ARRAY_TYPE(seq_range) *expunged_uids) { return mailbox_get_expunges_full(box, prev_modseq, uids_filter, expunged_uids, NULL); } dovecot-2.2.33.2/src/lib-storage/fail-mail-storage.c0000644000175000017500000000247213123174404016771 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage-private.h" #include "fail-mail-storage.h" static struct mail_storage *fail_storage_alloc(void) { struct mail_storage *storage; pool_t pool; pool = pool_alloconly_create("fail mail storage", 1024); storage = p_new(pool, struct mail_storage, 1); *storage = fail_storage; storage->pool = pool; return storage; } static void fail_storage_destroy(struct mail_storage *storage ATTR_UNUSED) { } static void fail_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = "fail"; if (set->subscription_fname == NULL) set->subscription_fname = "subscriptions"; } struct mail_storage fail_storage = { .name = "fail", .class_flags = MAIL_STORAGE_CLASS_FLAG_NO_ROOT, .v = { NULL, fail_storage_alloc, NULL, fail_storage_destroy, NULL, fail_storage_get_list_settings, NULL, fail_mailbox_alloc, NULL, NULL, } }; struct mail_storage *fail_mail_storage_create(void) { struct mail_storage *storage; storage = fail_storage_alloc(); storage->refcount = 1; storage->storage_class = &fail_storage; p_array_init(&storage->module_contexts, storage->pool, 5); return storage; } dovecot-2.2.33.2/src/lib-storage/mailbox-attribute.c0000644000175000017500000003143613165463624017145 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "istream.h" #include "mail-storage-private.h" #include "bsearch-insert-pos.h" #include "mailbox-attribute-internal.h" static ARRAY(struct mailbox_attribute_internal) mailbox_internal_attributes; static pool_t mailbox_attribute_pool; void mailbox_attributes_init(void) { mailbox_attribute_pool = pool_alloconly_create("mailbox attributes", 2048); i_array_init(&mailbox_internal_attributes, 32); /* internal mailbox attributes */ mailbox_attributes_internal_init(); } void mailbox_attributes_deinit(void) { pool_unref(&mailbox_attribute_pool); array_free(&mailbox_internal_attributes); } /* * Internal attributes */ static int mailbox_attribute_internal_cmp( const struct mailbox_attribute_internal *reg1, const struct mailbox_attribute_internal *reg2) { if (reg1->type != reg2->type) return (int)reg1->type - (int)reg2->type; return strcmp(reg1->key, reg2->key); } void mailbox_attribute_register_internal( const struct mailbox_attribute_internal *iattr) { struct mailbox_attribute_internal ireg; unsigned int insert_idx; (void)array_bsearch_insert_pos(&mailbox_internal_attributes, iattr, mailbox_attribute_internal_cmp, &insert_idx); ireg = *iattr; ireg.key = p_strdup(mailbox_attribute_pool, iattr->key); array_insert(&mailbox_internal_attributes, insert_idx, &ireg, 1); } void mailbox_attribute_register_internals( const struct mailbox_attribute_internal *iattrs, unsigned int count) { unsigned int i; for (i = 0; i < count; i++) mailbox_attribute_register_internal(&iattrs[i]); } void mailbox_attribute_unregister_internal( const struct mailbox_attribute_internal *iattr) { unsigned int idx; if (!array_bsearch_insert_pos(&mailbox_internal_attributes, iattr, mailbox_attribute_internal_cmp, &idx)) { i_panic("mailbox_attribute_unregister_internal(%s): " "key not found", iattr->key); } array_delete(&mailbox_internal_attributes, idx, 1); } void mailbox_attribute_unregister_internals( const struct mailbox_attribute_internal *iattrs, unsigned int count) { unsigned int i; for (i = 0; i < count; i++) mailbox_attribute_unregister_internal(&iattrs[i]); } static const struct mailbox_attribute_internal * mailbox_internal_attribute_get(enum mail_attribute_type type, const char *key) { const struct mailbox_attribute_internal *iattr; struct mailbox_attribute_internal dreg; unsigned int insert_idx; i_zero(&dreg); dreg.type = type; dreg.key = key; if (array_bsearch_insert_pos(&mailbox_internal_attributes, &dreg, mailbox_attribute_internal_cmp, &insert_idx)) { /* exact match */ return array_idx(&mailbox_internal_attributes, insert_idx); } if (insert_idx == 0) { /* not found at all */ return NULL; } iattr = array_idx(&mailbox_internal_attributes, insert_idx-1); if (strncmp(iattr->key, key, strlen(iattr->key)) != 0) { /* iattr isn't a prefix of key */ return NULL; } else if ((iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN) != 0) { /* iattr is a prefix of key and it wants to handle the key */ return iattr; } else { return NULL; } } static void mailbox_internal_attributes_get(enum mail_attribute_type type, const char *prefix, bool have_dict, ARRAY_TYPE(const_string) *attrs) { const struct mailbox_attribute_internal *regs; struct mailbox_attribute_internal dreg; char *bare_prefix; size_t plen; unsigned int count, i; bare_prefix = t_strdup_noconst(prefix); plen = strlen(bare_prefix); if (plen > 0 && bare_prefix[plen-1] == '/') { bare_prefix[plen-1] = '\0'; plen--; } i_zero(&dreg); dreg.type = type; dreg.key = bare_prefix; (void)array_bsearch_insert_pos(&mailbox_internal_attributes, &dreg, mailbox_attribute_internal_cmp, &i); regs = array_get(&mailbox_internal_attributes, &count); for (; i < count; i++) { const char *key = regs[i].key; if (regs[i].type != type) return; if (plen > 0) { if (strncmp(key, bare_prefix, plen) != 0) return; if (key[plen] == '/') { /* remove prefix */ key += plen + 1; } else if (key[plen] == '\0') { /* list the key itself, so this becomes an empty key string. it's the same as how the dict backend works too. */ key += plen; } else { return; } } if (have_dict || regs[i].rank == MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY) array_append(attrs, &key, 1); } } /* * Attribute API */ static int mailbox_attribute_set_common(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value) { const struct mailbox_attribute_internal *iattr; int ret; iattr = mailbox_internal_attribute_get(type, key); /* allow internal server attribute only for inbox */ if (iattr != NULL && !t->box->inbox_any && strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0) iattr = NULL; /* handle internal attribute */ if (iattr != NULL) { switch (iattr->rank) { case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT: case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE: /* notify about assignment */ if (iattr->set != NULL && iattr->set(t, key, value) < 0) return -1; break; case MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY: if (iattr->set == NULL) { mail_storage_set_error(t->box->storage, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf( "The /%s/%s attribute cannot be changed", (type == MAIL_ATTRIBUTE_TYPE_SHARED ? "shared" : "private"), key)); return -1; } /* assign internal attribute */ return iattr->set(t, key, value); default: i_unreached(); } } /* FIXME: v2.3 should move the internal_attribute to attribute_set() parameter (as flag). not done yet for API backwards compatibility */ t->internal_attribute = iattr != NULL && iattr->rank != MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY; ret = t->box->v.attribute_set(t, type, key, value); t->internal_attribute = FALSE; return ret; } int mailbox_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value) { return mailbox_attribute_set_common(t, type, key, value); } int mailbox_attribute_unset(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key) { struct mail_attribute_value value; i_zero(&value); return mailbox_attribute_set_common(t, type, key, &value); } int mailbox_attribute_value_to_string(struct mail_storage *storage, const struct mail_attribute_value *value, const char **str_r) { string_t *str; const unsigned char *data; size_t size; if (value->value_stream == NULL) { *str_r = value->value; return 0; } str = t_str_new(128); i_stream_seek(value->value_stream, 0); while (i_stream_read_data(value->value_stream, &data, &size, 0) > 0) { if (memchr(data, '\0', size) != NULL) { mail_storage_set_error(storage, MAIL_ERROR_PARAMS, "Attribute string value has NULs"); return -1; } str_append_n(str, data, size); i_stream_skip(value->value_stream, size); } if (value->value_stream->stream_errno != 0) { mail_storage_set_critical(storage, "read(%s) failed: %s", i_stream_get_name(value->value_stream), i_stream_get_error(value->value_stream)); return -1; } i_assert(value->value_stream->eof); *str_r = str_c(str); return 0; } static int mailbox_attribute_get_common(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r) { const struct mailbox_attribute_internal *iattr; int ret; iattr = mailbox_internal_attribute_get(type, key); /* allow internal server attributes only for the inbox */ if (iattr != NULL && !t->box->inbox_user && strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0) iattr = NULL; /* internal attribute */ if (iattr != NULL) { switch (iattr->rank) { case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE: if ((ret = iattr->get(t, key, value_r)) != 0) { if (ret < 0) return -1; value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY; return 1; } case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT: break; case MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY: if ((ret = iattr->get(t, key, value_r)) <= 0) return ret; value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY; return 1; default: i_unreached(); } } /* user entries - FIXME: v2.3 should move the internal_attribute to attribute_get() parameter (as flag). not done yet for API backwards compatibility */ t->internal_attribute = iattr != NULL && iattr->rank != MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY; ret = t->box->v.attribute_get(t, type, key, value_r); t->internal_attribute = FALSE; if (ret != 0) return ret; /* default entries */ if (iattr != NULL) { switch (iattr->rank) { case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT: if (iattr->get == NULL) ret = 0; else { if ((ret = iattr->get(t, key, value_r)) < 0) return ret; } if (ret > 0) { value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY; return 1; } case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE: break; default: i_unreached(); } } return 0; } int mailbox_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r) { int ret; i_zero(value_r); if ((ret = mailbox_attribute_get_common(t, type, key, value_r)) <= 0) return ret; i_assert(value_r->value != NULL); return 1; } int mailbox_attribute_get_stream(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r) { int ret; i_zero(value_r); value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS; if ((ret = mailbox_attribute_get_common(t, type, key, value_r)) <= 0) return ret; i_assert(value_r->value != NULL || value_r->value_stream != NULL); return 1; } struct mailbox_attribute_internal_iter { struct mailbox_attribute_iter iter; ARRAY_TYPE(const_string) extra_attrs; unsigned int extra_attr_idx; struct mailbox_attribute_iter *real_iter; }; struct mailbox_attribute_iter * mailbox_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix) { struct mailbox_attribute_internal_iter *intiter; struct mailbox_attribute_iter *iter; ARRAY_TYPE(const_string) extra_attrs; const char *const *attr; bool have_dict; iter = box->v.attribute_iter_init(box, type, prefix); i_assert(iter->box != NULL); /* check which internal attributes may apply */ t_array_init(&extra_attrs, 4); have_dict = box->storage->set->mail_attribute_dict[0] != '\0'; mailbox_internal_attributes_get(type, prefix, have_dict, &extra_attrs); /* any extra internal attributes to add? */ if (array_count(&extra_attrs) == 0) { /* no */ return iter; } /* yes */ intiter = i_new(struct mailbox_attribute_internal_iter, 1); intiter->real_iter = iter; i_array_init(&intiter->extra_attrs, 4); /* copy relevant attributes */ array_foreach(&extra_attrs, attr) { /* skip internal server attributes unless we're interating inbox */ if (!box->inbox_any && strncmp(*attr, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0) continue; array_append(&intiter->extra_attrs, attr, 1); } return &intiter->iter; } const char *mailbox_attribute_iter_next(struct mailbox_attribute_iter *iter) { struct mailbox_attribute_internal_iter *intiter; const char *const *attrs; unsigned int count, i; const char *result; if (iter->box != NULL) { /* no internal attributes to add */ return iter->box->v.attribute_iter_next(iter); } /* filter out duplicate results */ intiter = (struct mailbox_attribute_internal_iter *)iter; attrs = array_get(&intiter->extra_attrs, &count); while ((result = intiter->real_iter->box-> v.attribute_iter_next(intiter->real_iter)) != NULL) { for (i = 0; i < count; i++) { if (strcasecmp(attrs[i], result) == 0) break; } if (i == count) { /* return normally */ return result; } /* this attribute name is also to be returned as extra; skip now */ } /* return extra attributes at the end */ if (intiter->extra_attr_idx < count) return attrs[intiter->extra_attr_idx++]; return NULL; } int mailbox_attribute_iter_deinit(struct mailbox_attribute_iter **_iter) { struct mailbox_attribute_iter *iter = *_iter; struct mailbox_attribute_internal_iter *intiter; int ret; *_iter = NULL; if (iter->box != NULL) { /* not wrapped */ return iter->box->v.attribute_iter_deinit(iter); } /* wrapped */ intiter = (struct mailbox_attribute_internal_iter *)iter; ret = intiter->real_iter->box->v.attribute_iter_deinit(intiter->real_iter); array_free(&intiter->extra_attrs); i_free(intiter); return ret; } dovecot-2.2.33.2/src/lib-storage/mailbox-recent-flags.h0000644000175000017500000000135513123174404017503 00000000000000#ifndef MAILBOX_RECENT_FLAGS #define MAILBOX_RECENT_FLAGS struct mailbox; struct mail_index_view; void mailbox_recent_flags_set_uid(struct mailbox *box, uint32_t uid); void mailbox_recent_flags_set_uid_forced(struct mailbox *box, uint32_t uid); void mailbox_recent_flags_set_seqs(struct mailbox *box, struct mail_index_view *view, uint32_t seq1, uint32_t seq2); bool mailbox_recent_flags_have_uid(struct mailbox *box, uint32_t uid); void mailbox_recent_flags_reset(struct mailbox *box); unsigned int mailbox_recent_flags_count(struct mailbox *box); void mailbox_recent_flags_expunge_seqs(struct mailbox *box, uint32_t seq1, uint32_t seq2); void mailbox_recent_flags_expunge_uid(struct mailbox *box, uint32_t uid); #endif dovecot-2.2.33.2/src/lib-storage/mail-search.h0000644000175000017500000002055413165463624015702 00000000000000#ifndef MAIL_SEARCH_H #define MAIL_SEARCH_H #include "seq-range-array.h" #include "mail-types.h" #include "mail-thread.h" struct mail_search_mime_part; enum mail_search_arg_type { SEARCH_OR, SEARCH_SUB, /* sequence sets */ SEARCH_ALL, SEARCH_SEQSET, SEARCH_UIDSET, /* flags */ SEARCH_FLAGS, SEARCH_KEYWORDS, /* dates (date_type required) */ SEARCH_BEFORE, SEARCH_ON, /* time must point to beginning of the day */ SEARCH_SINCE, /* sizes */ SEARCH_SMALLER, SEARCH_LARGER, /* headers */ SEARCH_HEADER, SEARCH_HEADER_ADDRESS, SEARCH_HEADER_COMPRESS_LWSP, /* body */ SEARCH_BODY, SEARCH_TEXT, /* extensions */ SEARCH_MODSEQ, SEARCH_INTHREAD, SEARCH_GUID, SEARCH_MAILBOX, SEARCH_MAILBOX_GUID, SEARCH_MAILBOX_GLOB, SEARCH_REAL_UID, SEARCH_MIMEPART }; enum mail_search_date_type { MAIL_SEARCH_DATE_TYPE_SENT = 1, MAIL_SEARCH_DATE_TYPE_RECEIVED, MAIL_SEARCH_DATE_TYPE_SAVED }; enum mail_search_arg_flag { /* Used by *BEFORE/SINCE/ON searches. When NOT set: Adjust search timestamps so that the email's timezone is included in the comparisons. For example "04-Nov-2016 00:00:00 +0200" would match 4th day. This allows searching for mails with dates from the email sender's point of view. For received/saved dates there is no known timezone, and without this flag the dates are compared using the server's local timezone. When set: Compare the timestamp as UTC. For example "04-Nov-2016 00:00:00 +0200" would be treated as "03-Nov-2016 22:00:00 UTC" and would match 3rd day. This allows searching for mails within precise time interval. Since imap-dates don't allow specifying timezone this isn't really possible with IMAP protocol, except using OLDER/YOUNGER searches. */ MAIL_SEARCH_ARG_FLAG_USE_TZ = 0x01, }; enum mail_search_modseq_type { MAIL_SEARCH_MODSEQ_TYPE_ANY = 0, MAIL_SEARCH_MODSEQ_TYPE_PRIVATE, MAIL_SEARCH_MODSEQ_TYPE_SHARED }; struct mail_search_modseq { uint64_t modseq; enum mail_search_modseq_type type; }; struct mail_search_arg { /* NOTE: when adding new fields, make sure mail_search_arg_dup_one() and mail_search_arg_one_equals() are updated. */ struct mail_search_arg *next; enum mail_search_arg_type type; struct { struct mail_search_arg *subargs; ARRAY_TYPE(seq_range) seqset; const char *str; time_t time; uoff_t size; enum mail_flags flags; enum mail_search_arg_flag search_flags; enum mail_search_date_type date_type; enum mail_thread_type thread_type; struct mail_search_modseq *modseq; struct mail_search_result *search_result; struct mail_search_mime_part *mime_part; } value; /* set by mail_search_args_init(): */ struct { struct mail_search_args *search_args; /* Note that initialized keywords may be empty if the keyword wasn't valid in this mailbox. */ struct mail_keywords *keywords; struct imap_match_glob *mailbox_glob; } initialized; void *context; const char *hdr_field_name; /* for SEARCH_HEADER* */ unsigned int match_not:1; /* result = !result */ unsigned int match_always:1; /* result = 1 always */ unsigned int nonmatch_always:1; /* result = 0 always */ unsigned int fuzzy:1; /* use fuzzy matching for this arg */ unsigned int no_fts:1; /* do NOT call FTS */ int result; /* -1 = unknown, 0 = unmatched, 1 = matched */ }; struct mail_search_args { int refcount, init_refcount; pool_t pool; struct mailbox *box; struct mail_search_arg *args; unsigned int simplified:1; unsigned int have_inthreads:1; /* Stop mail_search_next() when finding a non-matching mail. (Could be useful when wanting to find only the oldest mails.) */ unsigned int stop_on_nonmatch:1; /* fts plugin has already expanded the search args - no need to do it again. */ unsigned int fts_expanded:1; }; #define ARG_SET_RESULT(arg, res) \ STMT_START { \ (arg)->result = !(arg)->match_not ? (res) : \ (res) == -1 ? -1 : !(res); \ } STMT_END typedef void mail_search_foreach_callback_t(struct mail_search_arg *arg, void *context); /* Allocate keywords for search arguments. If change_uidsets is TRUE, change uidsets to seqsets. */ void mail_search_args_init(struct mail_search_args *args, struct mailbox *box, bool change_uidsets, const ARRAY_TYPE(seq_range) *search_saved_uidset) ATTR_NULL(4); /* Initialize arg and its children. args is used for getting mailbox and pool. */ void mail_search_arg_init(struct mail_search_args *args, struct mail_search_arg *arg, bool change_uidsets, const ARRAY_TYPE(seq_range) *search_saved_uidset); /* Free memory allocated by mail_search_args_init(). The args can initialized afterwards again if needed. The args can be reused for other queries after calling this. */ void mail_search_args_deinit(struct mail_search_args *args); /* Free arg and its siblings and children. */ void mail_search_arg_deinit(struct mail_search_arg *arg); /* Free arg and its children, but not its siblings. */ void mail_search_arg_one_deinit(struct mail_search_arg *arg); /* Convert sequence sets in args to UIDs. */ void mail_search_args_seq2uid(struct mail_search_args *args); /* Returns TRUE if the two search arguments are fully compatible. Always returns FALSE if there are seqsets, since they may point to different messages depending on when the search is run. */ bool mail_search_args_equal(const struct mail_search_args *args1, const struct mail_search_args *args2); /* Same as mail_search_args_equal(), but for individual mail_search_arg structs. All the siblings of arg1 and arg2 are also compared. */ bool mail_search_arg_equals(const struct mail_search_arg *arg1, const struct mail_search_arg *arg2); /* Same as mail_search_arg_equals(), but don't compare siblings. */ bool mail_search_arg_one_equals(const struct mail_search_arg *arg1, const struct mail_search_arg *arg2); void mail_search_args_ref(struct mail_search_args *args); void mail_search_args_unref(struct mail_search_args **args); struct mail_search_args * mail_search_args_dup(const struct mail_search_args *args); struct mail_search_arg * mail_search_arg_dup(pool_t pool, const struct mail_search_arg *arg); /* Reset the results in search arguments. match_always is reset only if full_reset is TRUE. */ void mail_search_args_reset(struct mail_search_arg *args, bool full_reset); /* goes through arguments in list that don't have a result yet. Returns 1 = search matched, 0 = search unmatched, -1 = don't know yet */ int mail_search_args_foreach(struct mail_search_arg *args, mail_search_foreach_callback_t *callback, void *context) ATTR_NULL(3); #define mail_search_args_foreach(args, callback, context) \ mail_search_args_foreach(args + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct mail_search_arg *, typeof(context))), \ (mail_search_foreach_callback_t *)callback, context) /* Fills have_headers and have_body based on if such search argument exists that needs to be checked. Returns the headers that we're searching for, or NULL if we're searching for TEXT. */ const char *const * mail_search_args_analyze(struct mail_search_arg *args, bool *have_headers, bool *have_body); /* Returns FALSE if search query contains MAILBOX[_GLOB] args such that the query can never match any messages in the given mailbox. */ bool mail_search_args_match_mailbox(struct mail_search_args *args, const char *vname, char sep); /* Simplify/optimize search arguments. Afterwards all OR/SUB args are guaranteed to have match_not=FALSE. */ void mail_search_args_simplify(struct mail_search_args *args); /* Append all args as IMAP SEARCH AND-query to the dest string and returns TRUE. If some search arg can't be written as IMAP SEARCH parameter, error_r is set and FALSE is returned. */ bool mail_search_args_to_imap(string_t *dest, const struct mail_search_arg *args, const char **error_r); /* Like mail_search_args_to_imap(), but append only a single arg. */ bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg, const char **error_r); /* Write all args to dest string as cmdline/human compatible input. */ void mail_search_args_to_cmdline(string_t *dest, const struct mail_search_arg *args); /* Serialization for search args' results. */ void mail_search_args_result_serialize(const struct mail_search_args *args, buffer_t *dest); void mail_search_args_result_deserialize(struct mail_search_args *args, const unsigned char *data, size_t size); #endif dovecot-2.2.33.2/src/lib-storage/mail-user.h0000644000175000017500000001725413165463624015416 00000000000000#ifndef MAIL_USER_H #define MAIL_USER_H #include "unichar.h" #include "mail-storage-settings.h" struct module; struct stats; struct fs_settings; struct ssl_iostream_settings; struct mail_user; struct mail_user_vfuncs { void (*deinit)(struct mail_user *user); void (*deinit_pre)(struct mail_user *user); void (*stats_fill)(struct mail_user *user, struct stats *stats); }; struct mail_user { pool_t pool; struct mail_user_vfuncs v, *vlast; int refcount; /* User's creator if such exists. For example for autocreated shared mailbox users their creator is the logged in user. */ struct mail_user *creator; /* Set if user was created via mail_storage_service. */ struct mail_storage_service_user *_service_user; const char *username; /* don't access the home directly. It may be set lazily. */ const char *_home; uid_t uid; gid_t gid; const char *service; const char *session_id; struct ip_addr *local_ip, *remote_ip; const char *auth_token, *auth_user; const char *const *userdb_fields; /* Timestamp when this session was initially created. Most importantly this stays the same after IMAP client is hibernated and restored. */ time_t session_create_time; const struct var_expand_table *var_expand_table; /* If non-NULL, fail the user initialization with this error. This could be set by plugins that need to fail the initialization. */ const char *error; const struct setting_parser_info *set_info; const struct mail_user_settings *unexpanded_set; struct mail_user_settings *set; struct mail_namespace *namespaces; struct mail_storage *storages; ARRAY(const struct mail_storage_hooks *) hooks; normalizer_func_t *default_normalizer; /* Filled lazily by mailbox_attribute_*() when accessing attributes. */ struct dict *_attr_dict; /* Module-specific contexts. See mail_storage_module_id. */ ARRAY(union mail_user_module_context *) module_contexts; /* User doesn't exist (as reported by userdb lookup when looking up home) */ unsigned int nonexistent:1; /* Either home is set or there is no home for the user. */ unsigned int home_looked_up:1; /* User is anonymous */ unsigned int anonymous:1; /* This is an autocreated user (e.g. for shared namespace or lda raw storage) */ unsigned int autocreated:1; /* mail_user_init() has been called */ unsigned int initialized:1; /* The initial namespaces have been created and hook_mail_namespaces_created() has been called. */ unsigned int namespaces_created:1; /* SET_STR_VARS in user's all settings have been expanded. This happens near the beginning of the user initialization, so this is rarely needed to be checked. */ unsigned int settings_expanded:1; /* Shortcut to mail_storage_settings.mail_debug */ unsigned int mail_debug:1; /* If INBOX can't be opened, log an error, but only once. */ unsigned int inbox_open_error_logged:1; /* Fuzzy search works for this user (FTS enabled) */ unsigned int fuzzy_search:1; /* We're running dsync */ unsigned int dsyncing:1; /* Failed to create attribute dict, don't try again */ unsigned int attr_dict_failed:1; /* We're deinitializing the user */ unsigned int deinitializing:1; /* Enable administrator user commands for the user */ unsigned int admin:1; /* Enable all statistics gathering */ unsigned int stats_enabled:1; /* This session was restored (e.g. IMAP unhibernation) */ unsigned int session_restored:1; }; struct mail_user_module_register { unsigned int id; }; union mail_user_module_context { struct mail_user_vfuncs super; struct mail_user_module_register *reg; }; extern struct mail_user_module_register mail_user_module_register; extern struct auth_master_connection *mail_user_auth_master_conn; extern const struct var_expand_func_table *mail_user_var_expand_func_table; struct mail_user *mail_user_alloc(const char *username, const struct setting_parser_info *set_info, const struct mail_user_settings *set); struct mail_user * mail_user_alloc_nodup_set(const char *username, const struct setting_parser_info *set_info, const struct mail_user_settings *set); /* Returns -1 if settings were invalid. */ int mail_user_init(struct mail_user *user, const char **error_r); void mail_user_ref(struct mail_user *user); void mail_user_unref(struct mail_user **user); /* Duplicate a mail_user. mail_user_init() and mail_namespaces_init() need to be called before the user is usable. */ struct mail_user *mail_user_dup(struct mail_user *user); /* Find another user from the given user's namespaces. */ struct mail_user *mail_user_find(struct mail_user *user, const char *name); /* Specify mail location %variable expansion data. */ void mail_user_set_vars(struct mail_user *user, const char *service, const struct ip_addr *local_ip, const struct ip_addr *remote_ip); /* Return %variable expansion table for the user. */ const struct var_expand_table * mail_user_var_expand_table(struct mail_user *user); /* Specify the user's home directory. This should be called also when it's known that the user doesn't have a home directory to avoid the internal lookup. */ void mail_user_set_home(struct mail_user *user, const char *home); /* Get the home directory for the user. Returns 1 if home directory looked up successfully, 0 if there is no home directory (either user doesn't exist or has no home directory) or -1 if lookup failed. */ int mail_user_get_home(struct mail_user *user, const char **home_r); /* Appends path + file prefix for creating a temporary file. The file prefix doesn't contain any uniqueness. */ void mail_user_set_get_temp_prefix(string_t *dest, const struct mail_user_settings *set); /* Returns 1 on success, 0 if lock_secs is reached, -1 on error */ int mail_user_lock_file_create(struct mail_user *user, const char *lock_fname, unsigned int lock_secs, struct file_lock **lock_r, const char **error_r); /* Returns TRUE if plugin is loaded for the user. */ bool mail_user_is_plugin_loaded(struct mail_user *user, struct module *module); /* If name exists in plugin_envs, return its value. */ const char *mail_user_plugin_getenv(struct mail_user *user, const char *name); const char *mail_user_set_plugin_getenv(const struct mail_user_settings *set, const char *name); /* Add more namespaces to user's namespaces. The ->next pointers may be changed, so the namespaces pointer will be updated to user->namespaces. */ void mail_user_add_namespace(struct mail_user *user, struct mail_namespace **namespaces); /* Drop autocreated shared namespaces that don't have any "usable" mailboxes. */ void mail_user_drop_useless_namespaces(struct mail_user *user); /* Replace ~/ at the beginning of the path with the user's home directory. */ const char *mail_user_home_expand(struct mail_user *user, const char *path); /* Returns 0 if ok, -1 if home directory isn't set. */ int mail_user_try_home_expand(struct mail_user *user, const char **path); /* Returns unique user+ip identifier for anvil. */ const char *mail_user_get_anvil_userip_ident(struct mail_user *user); /* Basically the same as mail_storage_find_class(), except automatically load storage plugins when needed. */ struct mail_storage * mail_user_get_storage_class(struct mail_user *user, const char *name); /* Initialize SSL client settings from mail_user settings. */ void mail_user_init_ssl_client_settings(struct mail_user *user, struct ssl_iostream_settings *ssl_set); /* Initialize fs_settings from mail_user settings. */ void mail_user_init_fs_settings(struct mail_user *user, struct fs_settings *fs_set, struct ssl_iostream_settings *ssl_set); /* Fill statistics for user. By default there are no statistics, so stats plugin must be loaded to have anything filled. */ void mail_user_stats_fill(struct mail_user *user, struct stats *stats); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-parser-cmdline.c0000644000175000017500000000442713123174404020566 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-search-parser-private.h" struct cmdline_mail_search_parser { struct mail_search_parser parser; const char *const *args; unsigned int list_level; }; static int cmdline_search_parse_key(struct mail_search_parser *_parser, const char **key_r) { struct cmdline_mail_search_parser *parser = (struct cmdline_mail_search_parser *)_parser; if (parser->args[0] == NULL) { if (parser->list_level != 0) { _parser->error = "Missing ')'"; return -1; } return 0; } if (strcmp(parser->args[0], "(") == 0) { parser->list_level++; parser->args++; *key_r = MAIL_SEARCH_PARSER_KEY_LIST; return 1; } else if (strcmp(parser->args[0], ")") == 0) { if (parser->list_level == 0) { _parser->error = "Unexpected ')'"; return -1; } parser->list_level--; parser->args++; *key_r = MAIL_SEARCH_PARSER_KEY_LIST; return 0; } else { *key_r = parser->args[0]; parser->args++; return 1; } } static int cmdline_search_parse_string(struct mail_search_parser *_parser, const char **value_r) { struct cmdline_mail_search_parser *parser = (struct cmdline_mail_search_parser *)_parser; if (parser->args[0] == NULL) { _parser->error = "Missing parameter for search key"; return -1; } *value_r = parser->args[0]; parser->args++; return 1; } static bool cmdline_search_parse_skip_next(struct mail_search_parser *_parser, const char *str) { struct cmdline_mail_search_parser *parser = (struct cmdline_mail_search_parser *)_parser; if (parser->args[0] == NULL) return FALSE; if (strcasecmp(parser->args[0], str) != 0) return FALSE; parser->args++; return TRUE; } static const struct mail_search_parser_vfuncs mail_search_parser_cmdline_vfuncs = { cmdline_search_parse_key, cmdline_search_parse_string, cmdline_search_parse_skip_next }; struct mail_search_parser * mail_search_parser_init_cmdline(const char *const args[]) { struct cmdline_mail_search_parser *parser; pool_t pool; pool = pool_alloconly_create("cmdline search parser", 1024); parser = p_new(pool, struct cmdline_mail_search_parser, 1); parser->parser.pool = pool; parser->parser.v = mail_search_parser_cmdline_vfuncs; parser->args = args; return &parser->parser; } dovecot-2.2.33.2/src/lib-storage/mail-search-mime-build.c0000644000175000017500000001040313123174404017674 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "charset-utf8.h" #include "mail-storage-private.h" #include "mail-search-parser.h" #include "mail-search-mime-register.h" #include "mail-search-mime-build.h" static int mail_search_mime_build_list(struct mail_search_mime_build_context *ctx, struct mail_search_mime_arg **arg_r); struct mail_search_mime_arg * mail_search_mime_build_new(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *arg; arg = p_new(ctx->ctx->pool, struct mail_search_mime_arg, 1); arg->type = type; return arg; } struct mail_search_mime_arg * mail_search_mime_build_str(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *sarg; const char *value; sarg = mail_search_mime_build_new(ctx, type); if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) return NULL; sarg->value.str = p_strdup(ctx->ctx->pool, value); return sarg; } static int mail_search_mime_build_key_int(struct mail_search_mime_build_context *ctx, struct mail_search_mime_arg *parent, struct mail_search_mime_arg **arg_r) { struct mail_search_mime_arg *sarg; struct mail_search_mime_arg *old_parent = ctx->parent; const char *key; const struct mail_search_mime_register_arg *reg_arg; int ret; ctx->parent = parent; if ((ret = mail_search_parse_key(ctx->ctx->parser, &key)) <= 0) return ret; if (strcmp(key, MAIL_SEARCH_PARSER_KEY_LIST) == 0) { if (mail_search_mime_build_list(ctx, &sarg) < 0) return -1; if (sarg->value.subargs == NULL) { ctx->ctx->_error = "No MIMEPART keys inside list"; return -1; } ctx->parent = old_parent; *arg_r = sarg; return 1; } key = t_str_ucase(key); reg_arg = mail_search_mime_register_find(key); if (reg_arg != NULL) sarg = reg_arg->build(ctx); else { sarg = NULL; ctx->ctx->_error = p_strconcat (ctx->ctx->pool, "Unknown MIMEPART key ", key, NULL); } ctx->parent = old_parent; *arg_r = sarg; return sarg == NULL ? -1 : 1; } int mail_search_mime_build_key(struct mail_search_mime_build_context *ctx, struct mail_search_mime_arg *parent, struct mail_search_mime_arg **arg_r) { int ret; ret = mail_search_mime_build_key_int(ctx, parent, arg_r); if (ret <= 0) { if (ret == 0) ctx->ctx->_error = "Missing MIMEPART key"; return -1; } return 0; } static int mail_search_mime_build_list(struct mail_search_mime_build_context *ctx, struct mail_search_mime_arg **arg_r) { struct mail_search_mime_arg *sarg, **subargs; enum mail_search_mime_arg_type cur_type = SEARCH_MIME_SUB; int ret; sarg = p_new(ctx->ctx->pool, struct mail_search_mime_arg, 1); sarg->type = cur_type; subargs = &sarg->value.subargs; while ((ret = mail_search_mime_build_key_int(ctx, sarg, subargs)) > 0) { if (cur_type == sarg->type) { /* expected type */ } else if (cur_type == SEARCH_MIME_SUB) { /* type changed. everything in this list must now belong to this type. */ cur_type = sarg->type; } else { ctx->ctx->_error = "Use parenthesis when mixing ANDs and ORs"; return -1; } subargs = &(*subargs)->next; sarg->type = SEARCH_MIME_SUB; } if (ret < 0) return -1; sarg->type = cur_type; *arg_r = sarg; return 0; } int mail_search_mime_build(struct mail_search_build_context *bctx, struct mail_search_mime_part **mpart_r) { struct mail_search_mime_build_context ctx; struct mail_search_mime_part *mpart; struct mail_search_mime_arg *root; int ret; *mpart_r = NULL; i_zero(&ctx); ctx.ctx = bctx; ctx.mime_part = mpart = p_new(bctx->pool, struct mail_search_mime_part, 1); if ((ret=mail_search_mime_build_key(&ctx, NULL, &root)) < 0) return ret; if (root->type == SEARCH_MIME_SUB && !root->match_not) { /* simple SUB root */ mpart->args = root->value.subargs; } else { mpart->args = root; } *mpart_r = mpart; return 0; } struct mail_search_mime_arg * mail_search_mime_build_add(pool_t pool, struct mail_search_mime_part *mpart, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *arg; arg = p_new(pool, struct mail_search_mime_arg, 1); arg->type = type; arg->next = mpart->args; mpart->args = arg; return arg; } dovecot-2.2.33.2/src/lib-storage/mail-storage-service.c0000644000175000017500000014401713165463624017533 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "base64.h" #include "hostpid.h" #include "module-dir.h" #include "restrict-access.h" #include "eacces-error.h" #include "ipwd.h" #include "str.h" #include "var-expand.h" #include "dict.h" #include "settings-parser.h" #include "auth-master.h" #include "master-service-private.h" #include "master-service-settings.h" #include "master-service-settings-cache.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-service.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_RESOURCE_H # include #endif /* If time moves backwards more than this, kill ourself instead of sleeping. */ #define MAX_TIME_BACKWARDS_SLEEP 5 #define MAX_NOWARN_FORWARD_SECS 10 #define ERRSTR_INVALID_USER_SETTINGS \ "Invalid user settings. Refer to server log for more information." struct mail_storage_service_privileges { uid_t uid; gid_t gid; const char *uid_source, *gid_source; const char *home; const char *chroot; }; struct mail_storage_service_ctx { pool_t pool; struct master_service *service; const char *default_log_prefix; struct auth_master_connection *conn, *iter_conn; struct auth_master_user_list_ctx *auth_list; const struct setting_parser_info **set_roots; enum mail_storage_service_flags flags; const char *set_cache_module, *set_cache_service; struct master_service_settings_cache *set_cache; pool_t userdb_next_pool; const char *const **userdb_next_fieldsp; unsigned int debug:1; unsigned int log_initialized:1; unsigned int config_permission_denied:1; }; struct mail_storage_service_user { pool_t pool; int refcount; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; enum mail_storage_service_flags flags; struct ioloop_context *ioloop_ctx; const char *log_prefix, *auth_token, *auth_user; const char *system_groups_user, *uid_source, *gid_source; const char *chdir_path; const struct mail_user_settings *user_set; const struct setting_parser_info *user_info; struct setting_parser_context *set_parser; unsigned int session_id_counter; unsigned int anonymous:1; unsigned int admin:1; }; struct module *mail_storage_service_modules = NULL; static void mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx, string_t *str, const char *format, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, const struct mail_storage_service_privileges *priv); static bool mail_user_set_get_mail_debug(const struct setting_parser_info *user_info, const struct mail_user_settings *user_set) { const struct mail_storage_settings *mail_set; mail_set = mail_user_set_get_driver_settings(user_info, user_set, MAIL_STORAGE_SET_DRIVER_NAME); return mail_set->mail_debug; } static void set_keyval(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const char *key, const char *value) { struct setting_parser_context *set_parser = user->set_parser; if (master_service_set_has_config_override(ctx->service, key)) { /* this setting was already overridden with -o parameter */ if (mail_user_set_get_mail_debug(user->user_info, user->user_set)) { i_debug("Ignoring overridden (-o) userdb setting: %s", key); } return; } if (settings_parse_keyvalue(set_parser, key, value) < 0) { i_fatal("Invalid userdb input %s=%s: %s", key, value, settings_parser_get_error(set_parser)); } } static int set_line(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const char *line) { struct setting_parser_context *set_parser = user->set_parser; bool mail_debug; const char *key, *orig_key, *append_value = NULL; size_t len; int ret; mail_debug = mail_user_set_get_mail_debug(user->user_info, user->user_set); if (strchr(line, '=') == NULL) line = t_strconcat(line, "=yes", NULL); orig_key = key = t_strcut(line, '='); len = strlen(key); if (len > 0 && key[len-1] == '+') { /* key+=value */ append_value = line + len + 1; key = t_strndup(key, len-1); } if (!settings_parse_is_valid_key(set_parser, key)) { /* assume it's a plugin setting */ key = t_strconcat("plugin/", key, NULL); line = t_strconcat("plugin/", line, NULL); } if (master_service_set_has_config_override(ctx->service, key)) { /* this setting was already overridden with -o parameter */ if (mail_debug) { i_debug("Ignoring overridden (-o) userdb setting: %s", key); } return 1; } if (append_value != NULL) { const void *value; enum setting_type type; value = settings_parse_get_value(set_parser, key, &type); if (value != NULL && type == SET_STR) { const char *const *strp = value; line = t_strdup_printf("%s=%s%s", key, *strp, append_value); } else { i_error("Ignoring %s userdb setting. " "'+' can only be used for strings.", orig_key); } } ret = settings_parse_line(set_parser, line); if (mail_debug && ret >= 0) { if (strstr(key, "pass") != NULL) { /* possibly a password field (e.g. imapc_password). hide the value. */ line = t_strconcat(key, "=", NULL); } i_debug(ret == 0 ? "Unknown userdb setting: %s" : "Added userdb setting: %s", line); } return ret; } static bool validate_chroot(const struct mail_user_settings *user_set, const char *dir) { const char *const *chroot_dirs; if (*dir == '\0') return FALSE; if (*user_set->valid_chroot_dirs == '\0') return FALSE; chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":"); while (*chroot_dirs != NULL) { if (**chroot_dirs != '\0' && strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0) return TRUE; chroot_dirs++; } return FALSE; } static int user_reply_handle(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const struct auth_user_reply *reply, const char **error_r) { const char *home = reply->home; const char *chroot = reply->chroot; const char *const *str, *line, *p; unsigned int i, count; int ret = 0; if (reply->uid != (uid_t)-1) { if (reply->uid == 0) { *error_r = "userdb returned 0 as uid"; return -1; } user->uid_source = "userdb lookup"; set_keyval(ctx, user, "mail_uid", dec2str(reply->uid)); } if (reply->gid != (uid_t)-1) { user->gid_source = "userdb lookup"; set_keyval(ctx, user, "mail_gid", dec2str(reply->gid)); } if (home != NULL && chroot == NULL && *user->user_set->valid_chroot_dirs != '\0' && (p = strstr(home, "/./")) != NULL) { /* wu-ftpd like /./ - check only if there's even a possibility of using them (non-empty valid_chroot_dirs) */ chroot = t_strdup_until(home, p); home = p + 2; } if (home != NULL) set_keyval(ctx, user, "mail_home", home); if (chroot != NULL) { if (!validate_chroot(user->user_set, chroot)) { *error_r = t_strdup_printf( "userdb returned invalid chroot directory: %s " "(see valid_chroot_dirs setting)", chroot); return -1; } set_keyval(ctx, user, "mail_chroot", chroot); } user->anonymous = reply->anonymous; str = array_get(&reply->extra_fields, &count); for (i = 0; i < count; i++) { line = str[i]; if (strncmp(line, "system_groups_user=", 19) == 0) { user->system_groups_user = p_strdup(user->pool, line + 19); } else if (strncmp(line, "chdir=", 6) == 0) { user->chdir_path = p_strdup(user->pool, line+6); } else if (strncmp(line, "nice=", 5) == 0) { #ifdef HAVE_SETPRIORITY int n; if (str_to_int(line + 5, &n) < 0) { i_error("userdb returned invalid nice value %s", line + 5); } else if (n != 0) { if (setpriority(PRIO_PROCESS, 0, n) < 0) i_error("setpriority(%d) failed: %m", n); } #endif } else if (strncmp(line, "auth_token=", 11) == 0) { user->auth_token = p_strdup(user->pool, line+11); } else if (strncmp(line, "auth_user=", 10) == 0) { user->auth_user = p_strdup(user->pool, line+10); } else if (strncmp(line, "admin=", 6) == 0) { user->admin = line[6] == 'y' || line[6] == 'Y' || line[6] == '1'; } else T_BEGIN { ret = set_line(ctx, user, line); } T_END; if (ret < 0) break; } if (ret < 0) { *error_r = t_strdup_printf("Invalid userdb input '%s': %s", str[i], settings_parser_get_error(user->set_parser)); } return ret; } static int service_auth_userdb_lookup(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, pool_t pool, const char **user, const char *const **fields_r, const char **error_r) { struct auth_user_info info; const char *new_username; int ret; i_zero(&info); info.service = input->service != NULL ? input->service : ctx->service->name; info.local_ip = input->local_ip; info.remote_ip = input->remote_ip; info.local_port = input->local_port; info.remote_port = input->remote_port; info.debug = input->debug; ret = auth_master_user_lookup(ctx->conn, *user, &info, pool, &new_username, fields_r); if (ret > 0) { if (strcmp(*user, new_username) != 0) { if (ctx->debug) i_debug("changed username to %s", new_username); *user = t_strdup(new_username); } *user = new_username; } else if (ret == 0) *error_r = "Unknown user"; else if (**fields_r != NULL) { *error_r = t_strdup(**fields_r); ret = -2; } else { *error_r = MAIL_ERRSTR_CRITICAL_MSG; } return ret; } static bool parse_uid(const char *str, uid_t *uid_r, const char **error_r) { struct passwd pw; if (str_to_uid(str, uid_r) == 0) return TRUE; switch (i_getpwnam(str, &pw)) { case -1: *error_r = t_strdup_printf("getpwnam(%s) failed: %m", str); return FALSE; case 0: *error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL); return FALSE; default: *uid_r = pw.pw_uid; return TRUE; } } static bool parse_gid(const char *str, gid_t *gid_r, const char **error_r) { struct group gr; if (str_to_gid(str, gid_r) == 0) return TRUE; switch (i_getgrnam(str, &gr)) { case -1: *error_r = t_strdup_printf("getgrnam(%s) failed: %m", str); return FALSE; case 0: *error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL); return FALSE; default: *gid_r = gr.gr_gid; return TRUE; } } static const struct var_expand_table * get_var_expand_table(struct master_service *service, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, const struct mail_storage_service_privileges *priv) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 's', NULL, "service" }, { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 'i', NULL, "uid" }, { '\0', NULL, "gid" }, { '\0', NULL, "session" }, { '\0', NULL, "auth_user" }, { '\0', NULL, "auth_username" }, { '\0', NULL, "auth_domain" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = input->username; tab[1].value = t_strcut(input->username, '@'); tab[2].value = strchr(input->username, '@'); if (tab[2].value != NULL) tab[2].value++; tab[3].value = service->name; tab[4].value = net_ip2addr(&input->local_ip); tab[5].value = net_ip2addr(&input->remote_ip); tab[6].value = my_pid; tab[7].value = priv == NULL ? NULL : dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid); tab[8].value = priv == NULL ? NULL : dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid); tab[9].value = input->session_id; if (user == NULL || user->auth_user == NULL) { tab[10].value = tab[0].value; tab[11].value = tab[1].value; tab[12].value = tab[2].value; } else { tab[10].value = user->auth_user; tab[11].value = t_strcut(user->auth_user, '@'); tab[12].value = strchr(user->auth_user, '@'); if (tab[12].value != NULL) tab[12].value++; } return tab; } const struct var_expand_table * mail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx, struct mail_storage_service_input *input) { struct mail_storage_service_privileges priv; i_zero(&priv); priv.uid = (uid_t)-1; priv.gid = (gid_t)-1; return get_var_expand_table(ctx->service, NULL, input, &priv); } static const char * user_expand_varstr(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv, const char *str) { string_t *ret; if (*str == SETTING_STRVAR_EXPANDED[0]) return str + 1; i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]); ret = t_str_new(256); mail_storage_service_var_expand(ctx, ret, str + 1, user, &user->input, priv); return str_c(ret); } static int service_parse_privileges(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv_r, const char **error_r) { const struct mail_user_settings *set = user->user_set; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; i_zero(priv_r); if (*set->mail_uid != '\0') { if (!parse_uid(set->mail_uid, &uid, error_r)) { *error_r = t_strdup_printf("%s (from %s)", *error_r, user->uid_source); return -1; } if (uid < (uid_t)set->first_valid_uid || (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) { *error_r = t_strdup_printf( "Mail access for users with UID %s not permitted " "(see first_valid_uid in config file, uid from %s).", dec2str(uid), user->uid_source); return -1; } } priv_r->uid = uid; priv_r->uid_source = user->uid_source; if (*set->mail_gid != '\0') { if (!parse_gid(set->mail_gid, &gid, error_r)) { *error_r = t_strdup_printf("%s (from %s)", *error_r, user->gid_source); return -1; } if (gid < (gid_t)set->first_valid_gid || (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) { *error_r = t_strdup_printf( "Mail access for users with GID %s not permitted " "(see first_valid_gid in config file, gid from %s).", dec2str(gid), user->gid_source); return -1; } } priv_r->gid = gid; priv_r->gid_source = user->gid_source; /* variable strings are expanded in mail_user_init(), but we need the home and chroot sooner so do them separately here. */ priv_r->home = user_expand_varstr(ctx, user, priv_r, user->user_set->mail_home); priv_r->chroot = user_expand_varstr(ctx, user, priv_r, user->user_set->mail_chroot); return 0; } static void mail_storage_service_seteuid_root(void) { if (seteuid(0) < 0) { i_fatal("mail-storage-service: " "Failed to restore temporarily dropped root privileges: " "seteuid(0) failed: %m"); } } static int service_drop_privileges(struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv, bool disallow_root, bool keep_setuid_root, bool setenv_only, const char **error_r) { const struct mail_user_settings *set = user->user_set; struct restrict_access_settings rset; uid_t current_euid, setuid_uid = 0; const char *cur_chroot, *error; current_euid = geteuid(); restrict_access_init(&rset); restrict_access_get_env(&rset); if (priv->uid != (uid_t)-1) { rset.uid = priv->uid; rset.uid_source = priv->uid_source; } else if (rset.uid == (uid_t)-1 && disallow_root && current_euid == 0) { *error_r = "User is missing UID (see mail_uid setting)"; return -1; } if (priv->gid != (gid_t)-1) { rset.gid = priv->gid; rset.gid_source = priv->gid_source; } else if (rset.gid == (gid_t)-1 && disallow_root && set->first_valid_gid > 0 && getegid() == 0) { *error_r = "User is missing GID (see mail_gid setting)"; return -1; } if (*set->mail_privileged_group != '\0') { if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid, &error)) { *error_r = t_strdup_printf( "%s (in mail_privileged_group setting)", error); return -1; } } if (*set->mail_access_groups != '\0') { rset.extra_groups = t_strconcat(set->mail_access_groups, ",", rset.extra_groups, NULL); } rset.first_valid_gid = set->first_valid_gid; rset.last_valid_gid = set->last_valid_gid; rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot; rset.system_groups_user = user->system_groups_user; cur_chroot = restrict_access_get_current_chroot(); if (cur_chroot != NULL) { /* we're already chrooted. make sure the chroots are equal. */ if (rset.chroot_dir == NULL) { *error_r = "Process is already chrooted, " "can't un-chroot for this user"; return -1; } if (strcmp(rset.chroot_dir, cur_chroot) != 0) { *error_r = t_strdup_printf( "Process is already chrooted to %s, " "can't chroot to %s", cur_chroot, priv->chroot); return -1; } /* chrooting to same directory where we're already chrooted */ rset.chroot_dir = NULL; } if (disallow_root && (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0))) { *error_r = "Mail access not allowed for root"; return -1; } if (keep_setuid_root) { if (current_euid != rset.uid && rset.uid != (uid_t)-1) { if (current_euid != 0) { /* we're changing the UID, switch back to root first */ mail_storage_service_seteuid_root(); } setuid_uid = rset.uid; } rset.uid = (uid_t)-1; disallow_root = FALSE; } if (!setenv_only) { restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home, disallow_root); } else { restrict_access_set_env(&rset); } if (setuid_uid != 0 && !setenv_only) { if (seteuid(setuid_uid) < 0) i_fatal("mail-storage-service: seteuid(%s) failed: %m", dec2str(setuid_uid)); } return 0; } static int mail_storage_service_init_post(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv, const char *session_id_suffix, struct mail_user **mail_user_r, const char **error_r) { const struct mail_storage_settings *mail_set; const char *home = priv->home; struct mail_user *mail_user; /* NOTE: if more user initialization is added, add it also to mail_user_dup() */ mail_user = mail_user_alloc_nodup_set(user->input.username, user->user_info, user->user_set); mail_user->_service_user = user; mail_storage_service_user_ref(user); mail_user_set_home(mail_user, *home == '\0' ? NULL : home); mail_user_set_vars(mail_user, ctx->service->name, &user->input.local_ip, &user->input.remote_ip); mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid; mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid; mail_user->anonymous = user->anonymous; mail_user->admin = user->admin; mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token); mail_user->auth_user = p_strdup(mail_user->pool, user->auth_user); if (user->input.session_create_time != 0) { mail_user->session_create_time = user->input.session_create_time; mail_user->session_restored = TRUE; } if (session_id_suffix == NULL) { if (user->session_id_counter++ == 0) { mail_user->session_id = p_strdup(mail_user->pool, user->input.session_id); } else { mail_user->session_id = p_strdup_printf(mail_user->pool, "%s:%u", user->input.session_id, user->session_id_counter); } } else mail_user->session_id = p_strdup_printf(mail_user->pool, "%s:%s", user->input.session_id, session_id_suffix); mail_user->userdb_fields = user->input.userdb_fields == NULL ? NULL : p_strarray_dup(mail_user->pool, user->input.userdb_fields); mail_set = mail_user_set_get_storage_set(mail_user); if (mail_set->mail_debug) { string_t *str = t_str_new(64); str_printfa(str, "Effective uid=%s, gid=%s, home=%s", dec2str(geteuid()), dec2str(getegid()), home); if (*priv->chroot != '\0') str_printfa(str, ", chroot=%s", priv->chroot); i_debug("%s", str_c(str)); } if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 && (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) { /* we don't want to write core files to any users' home directories since they could contain information about other users' mails as well. so do no chdiring to home. */ } else if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) { /* If possible chdir to home directory, so that core file could be written in case we crash. fallback to chdir()ing to root directory. this is needed because the current directory may not be accessible after dropping privileges, and for example unlink_directory() requires ability to open the current directory. */ const char *chdir_path = user->chdir_path != NULL ? user->chdir_path : home; if (chdir_path[0] == '\0') { if (chdir("/") < 0) i_error("chdir(/) failed: %m"); } else if (chdir(chdir_path) < 0) { if (errno == EACCES) { i_error("%s", eacces_error_get("chdir", t_strconcat(chdir_path, "/", NULL))); } else if (errno != ENOENT) i_error("chdir(%s) failed: %m", chdir_path); else if (mail_set->mail_debug) i_debug("Home dir not found: %s", chdir_path); if (chdir("/") < 0) i_error("chdir(/) failed: %m"); } } if (mail_user_init(mail_user, error_r) < 0) { mail_user_unref(&mail_user); return -1; } if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) { if (mail_namespaces_init(mail_user, error_r) < 0) { mail_user_unref(&mail_user); return -1; } } *mail_user_r = mail_user; return 0; } void mail_storage_service_io_activate_user(struct mail_storage_service_user *user) { i_set_failure_prefix("%s", user->log_prefix); } void mail_storage_service_io_deactivate_user(struct mail_storage_service_user *user) { i_set_failure_prefix("%s", user->service_ctx->default_log_prefix); } void mail_storage_service_io_deactivate(struct mail_storage_service_ctx *ctx) { i_set_failure_prefix("%s", ctx->default_log_prefix); } static const char *field_get_default(const char *data) { const char *p; p = strchr(data, ':'); if (p == NULL) return ""; else { /* default value given */ return p+1; } } const char *mail_storage_service_fields_var_expand(const char *data, const char *const *fields) { const char *field_name = t_strcut(data, ':'); unsigned int i; size_t field_name_len; if (fields == NULL) return field_get_default(data); field_name_len = strlen(field_name); for (i = 0; fields[i] != NULL; i++) { if (strncmp(fields[i], field_name, field_name_len) == 0 && fields[i][field_name_len] == '=') return fields[i] + field_name_len+1; } return field_get_default(data); } static const char * mail_storage_service_input_var_userdb(const char *data, void *context) { struct mail_storage_service_user *user = context; return mail_storage_service_fields_var_expand(data, user == NULL ? NULL : user->input.userdb_fields); } static void mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx, string_t *str, const char *format, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, const struct mail_storage_service_privileges *priv) { static const struct var_expand_func_table func_table[] = { { "userdb", mail_storage_service_input_var_userdb }, { NULL, NULL } }; var_expand_with_funcs(str, format, get_var_expand_table(ctx->service, user, input, priv), func_table, user); } static void mail_storage_service_init_log(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv) { ctx->log_initialized = TRUE; T_BEGIN { string_t *str; str = t_str_new(256); mail_storage_service_var_expand(ctx, str, user->user_set->mail_log_prefix, user, &user->input, priv); user->log_prefix = p_strdup(user->pool, str_c(str)); } T_END; master_service_init_log(ctx->service, user->log_prefix); if (master_service_get_client_limit(master_service) == 1) i_set_failure_send_prefix(user->log_prefix); io_loop_context_add_callbacks(user->ioloop_ctx, mail_storage_service_io_activate_user, mail_storage_service_io_deactivate_user, user); } static void mail_storage_service_time_moved(time_t old_time, time_t new_time) { long diff = new_time - old_time; if (diff > 0) { if (diff > MAX_NOWARN_FORWARD_SECS) i_warning("Time jumped forwards %ld seconds", diff); return; } diff = -diff; if (diff > MAX_TIME_BACKWARDS_SLEEP) { i_fatal("Time just moved backwards by %ld seconds. " "This might cause a lot of problems, " "so I'll just kill myself now. " "http://wiki2.dovecot.org/TimeMovedBackwards", diff); } else { i_error("Time just moved backwards by %ld seconds. " "I'll sleep now until we're back in present. " "http://wiki2.dovecot.org/TimeMovedBackwards", diff); /* Sleep extra second to make sure usecs also grows. */ diff++; while (diff > 0 && sleep(diff) != 0) { /* don't use sleep()'s return value, because it could get us to a long loop in case interrupts just keep coming */ diff = old_time - time(NULL) + 1; } } } struct mail_storage_service_ctx * mail_storage_service_init(struct master_service *service, const struct setting_parser_info *set_roots[], enum mail_storage_service_flags flags) { struct mail_storage_service_ctx *ctx; const char *version; pool_t pool; unsigned int count; version = master_service_get_version_string(service); if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) { i_fatal("Version mismatch: libdovecot-storage.so is '%s', " "while the running Dovecot binary is '%s'", PACKAGE_VERSION, version); } if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 && getuid() != 0) { /* service { user } isn't root. the permission drop can't be temporary. */ flags &= ~MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP; } (void)umask(0077); io_loop_set_time_moved_callback(current_ioloop, mail_storage_service_time_moved); mail_storage_init(); pool = pool_alloconly_create("mail storage service", 2048); ctx = p_new(pool, struct mail_storage_service_ctx, 1); ctx->pool = pool; ctx->service = service; ctx->flags = flags; /* @UNSAFE */ if (set_roots == NULL) count = 0; else for (count = 0; set_roots[count] != NULL; count++) ; ctx->set_roots = p_new(pool, const struct setting_parser_info *, count + 2); ctx->set_roots[0] = &mail_user_setting_parser_info; if (set_roots != NULL) { memcpy(ctx->set_roots + 1, set_roots, sizeof(*ctx->set_roots) * count); } /* do all the global initialization. delay initializing plugins until we drop privileges the first time. */ if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) { /* note: we may not have read any settings yet, so this logging may still be going to wrong location */ ctx->default_log_prefix = p_strconcat(pool, service->name, ": ", NULL); master_service_init_log(service, ctx->default_log_prefix); } dict_drivers_register_builtin(); return ctx; } struct auth_master_connection * mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx) { i_assert(ctx->conn != NULL); return ctx->conn; } static enum mail_storage_service_flags mail_storage_service_input_get_flags(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input) { enum mail_storage_service_flags flags; flags = (ctx->flags & ~input->flags_override_remove) | input->flags_override_add; if (input->no_userdb_lookup) { /* FIXME: for API backwards compatibility only */ flags &= ~MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; } return flags; } int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, pool_t pool, const struct setting_parser_info **user_info_r, const struct setting_parser_context **parser_r, const char **error_r) { struct master_service_settings_input set_input; const struct setting_parser_info *const *roots; struct master_service_settings_output set_output; const struct dynamic_settings_parser *dyn_parsers; enum mail_storage_service_flags flags; unsigned int i; ctx->config_permission_denied = FALSE; flags = input == NULL ? ctx->flags : mail_storage_service_input_get_flags(ctx, input); i_zero(&set_input); set_input.roots = ctx->set_roots; set_input.preserve_user = TRUE; /* settings reader may exec doveconf, which is going to clear environment, and if we're not doing a userdb lookup we want to use $HOME */ set_input.preserve_home = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0; set_input.use_sysexits = (flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0; if (input != NULL) { set_input.module = input->module; set_input.service = input->service; set_input.username = input->username; set_input.local_ip = input->local_ip; set_input.remote_ip = input->remote_ip; } if (input == NULL) { /* global settings read - don't create a cache for thi */ } else if (ctx->set_cache == NULL) { ctx->set_cache_module = p_strdup(ctx->pool, set_input.module); ctx->set_cache_service = p_strdup(ctx->pool, set_input.service); ctx->set_cache = master_service_settings_cache_init( ctx->service, set_input.module, set_input.service); } else { /* already looked up settings at least once. we really shouldn't be execing anymore. */ set_input.never_exec = TRUE; } dyn_parsers = mail_storage_get_dynamic_parsers(pool); if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 && null_strcmp(set_input.service, ctx->set_cache_service) == 0 && ctx->set_cache != NULL) { if (master_service_settings_cache_read(ctx->set_cache, &set_input, dyn_parsers, parser_r, error_r) < 0) { *error_r = t_strdup_printf( "Error reading configuration: %s", *error_r); return -1; } } else { settings_parser_dyn_update(pool, &set_input.roots, dyn_parsers); if (master_service_settings_read(ctx->service, &set_input, &set_output, error_r) < 0) { *error_r = t_strdup_printf( "Error reading configuration: %s", *error_r); ctx->config_permission_denied = set_output.permission_denied; return -1; } *parser_r = ctx->service->set_parser; } roots = settings_parser_get_roots(*parser_r); for (i = 0; roots[i] != NULL; i++) { if (strcmp(roots[i]->module_name, mail_user_setting_parser_info.module_name) == 0) { *user_info_r = roots[i]; return 0; } } i_unreached(); return -1; } void mail_storage_service_set_auth_conn(struct mail_storage_service_ctx *ctx, struct auth_master_connection *conn) { i_assert(ctx->conn == NULL); i_assert(mail_user_auth_master_conn == NULL); ctx->conn = conn; mail_user_auth_master_conn = conn; } static void mail_storage_service_first_init(struct mail_storage_service_ctx *ctx, const struct setting_parser_info *user_info, const struct mail_user_settings *user_set) { enum auth_master_flags flags = 0; ctx->debug = mail_user_set_get_mail_debug(user_info, user_set); if (ctx->debug) flags |= AUTH_MASTER_FLAG_DEBUG; if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0) flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT; mail_storage_service_set_auth_conn(ctx, auth_master_init(user_set->auth_socket_path, flags)); } static int mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx, const struct setting_parser_info *user_info, const struct mail_user_settings *user_set, const char **error_r) { struct module_dir_load_settings mod_set; if (*user_set->mail_plugins == '\0') return 0; if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0) return 0; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.binary_name = master_service_get_name(ctx->service); mod_set.setting_name = "mail_plugins"; mod_set.require_init_funcs = TRUE; mod_set.debug = mail_user_set_get_mail_debug(user_info, user_set); return module_dir_try_load_missing(&mail_storage_service_modules, user_set->mail_plugin_dir, user_set->mail_plugins, &mod_set, error_r); } static int extra_field_key_cmp_p(const char *const *s1, const char *const *s2) { const char *p1 = *s1, *p2 = *s2; for (; *p1 == *p2; p1++, p2++) { if (*p1 == '\0') return 0; } if (*p1 == '=') return -1; if (*p2 == '=') return 1; return *p1 - *p2; } static void mail_storage_service_set_log_prefix(struct mail_storage_service_ctx *ctx, const struct mail_user_settings *user_set, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, const struct mail_storage_service_privileges *priv) { string_t *str; str = t_str_new(256); mail_storage_service_var_expand(ctx, str, user_set->mail_log_prefix, user, input, priv); i_set_failure_prefix("%s", str_c(str)); } static const char * mail_storage_service_generate_session_id(pool_t pool, const char *prefix) { guid_128_t guid; size_t prefix_len = prefix == NULL ? 0 : strlen(prefix); string_t *str = str_new(pool, MAX_BASE64_ENCODED_SIZE(prefix_len + 1 + sizeof(guid))); if (prefix != NULL) str_printfa(str, "%s:", prefix); guid_128_generate(guid); base64_encode(guid, sizeof(guid), str); /* remove the trailing "==" */ i_assert(str_data(str)[str_len(str)-2] == '='); str_truncate(str, str_len(str)-2); return str_c(str); } static int mail_storage_service_lookup_real(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, bool update_log_prefix, struct mail_storage_service_user **user_r, const char **error_r) { enum mail_storage_service_flags flags; struct mail_storage_service_user *user; const char *username = input->username; const struct setting_parser_info *user_info; const struct mail_user_settings *user_set; const char *const *userdb_fields, *error; struct auth_user_reply reply; const struct setting_parser_context *set_parser; void **sets; pool_t user_pool, temp_pool; int ret = 1; user_pool = pool_alloconly_create(MEMPOOL_GROWING"mail storage service user", 1024*6); flags = mail_storage_service_input_get_flags(ctx, input); if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 && geteuid() != 0) { /* we dropped privileges only temporarily. switch back to root before reading settings, so we'll definitely have enough permissions to connect to the config socket. */ mail_storage_service_seteuid_root(); } if (mail_storage_service_read_settings(ctx, input, user_pool, &user_info, &set_parser, &error) < 0) { if (ctx->config_permission_denied) { /* just restart and maybe next time we will open the config socket before dropping privileges */ i_fatal("%s", error); } i_error("%s", error); pool_unref(&user_pool); *error_r = MAIL_ERRSTR_CRITICAL_MSG; return -1; } if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 && !ctx->log_initialized) { /* initialize logging again, in case we only read the settings for the first above */ ctx->log_initialized = TRUE; master_service_init_log(ctx->service, t_strconcat(ctx->service->name, ": ", NULL)); update_log_prefix = TRUE; } sets = master_service_settings_parser_get_others(master_service, set_parser); user_set = sets[0]; if (update_log_prefix) mail_storage_service_set_log_prefix(ctx, user_set, NULL, input, NULL); if (ctx->conn == NULL) mail_storage_service_first_init(ctx, user_info, user_set); /* load global plugins */ if (mail_storage_service_load_modules(ctx, user_info, user_set, &error) < 0) { i_error("%s", error); pool_unref(&user_pool); *error_r = MAIL_ERRSTR_CRITICAL_MSG; return -1; } if (ctx->userdb_next_pool == NULL) temp_pool = pool_alloconly_create("userdb lookup", 2048); else { temp_pool = ctx->userdb_next_pool; ctx->userdb_next_pool = NULL; pool_ref(temp_pool); } if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) { ret = service_auth_userdb_lookup(ctx, input, temp_pool, &username, &userdb_fields, error_r); if (ret <= 0) { pool_unref(&temp_pool); pool_unref(&user_pool); return ret; } if (ctx->userdb_next_fieldsp != NULL) *ctx->userdb_next_fieldsp = userdb_fields; } else { userdb_fields = input->userdb_fields; } user = p_new(user_pool, struct mail_storage_service_user, 1); user->refcount = 1; user->service_ctx = ctx; user->pool = user_pool; user->input = *input; user->input.userdb_fields = userdb_fields == NULL ? NULL : p_strarray_dup(user_pool, userdb_fields); user->input.username = p_strdup(user_pool, username); user->input.session_id = p_strdup(user_pool, input->session_id); if (user->input.session_id == NULL) { user->input.session_id = mail_storage_service_generate_session_id(user_pool, input->session_id_prefix); } user->input.session_create_time = input->session_create_time; user->user_info = user_info; user->flags = flags; user->set_parser = settings_parser_dup(set_parser, user_pool); sets = master_service_settings_parser_get_others(master_service, user->set_parser); user->user_set = sets[0]; user->gid_source = "mail_gid setting"; user->uid_source = "mail_uid setting"; if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0) (void)settings_parse_line(user->set_parser, "mail_debug=yes"); if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) { const char *home = getenv("HOME"); if (home != NULL) set_keyval(ctx, user, "mail_home", home); } if (userdb_fields != NULL) { auth_user_fields_parse(userdb_fields, temp_pool, &reply); array_sort(&reply.extra_fields, extra_field_key_cmp_p); if (user_reply_handle(ctx, user, &reply, &error) < 0) { i_error("Invalid settings in userdb: %s", error); *error_r = ERRSTR_INVALID_USER_SETTINGS; ret = -2; } } if (ret > 0 && !settings_parser_check(user->set_parser, user_pool, &error)) { i_error("Invalid settings (probably caused by userdb): %s", error); *error_r = ERRSTR_INVALID_USER_SETTINGS; ret = -2; } pool_unref(&temp_pool); /* load per-user plugins */ if (ret > 0) { if (mail_storage_service_load_modules(ctx, user_info, user->user_set, &error) < 0) { i_error("%s", error); *error_r = MAIL_ERRSTR_CRITICAL_MSG; ret = -2; } } if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0 && user_set->mail_plugins[0] != '\0') { /* mail_storage_service_load_modules() already avoids loading plugins when the _NO_PLUGINS flag is set. However, it's possible that the plugins are already loaded, because the plugin loading is a global state. This is especially true with doveadm, which loads the mail_plugins immediately at startup so it can find commands registered by plugins. It's fine that extra plugins are loaded - we'll just need to prevent any of their hooks from being called. One easy way to do this is just to clear out the mail_plugins setting: */ (void)settings_parse_line(user->set_parser, "mail_plugins="); } *user_r = user; return ret; } int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, struct mail_storage_service_user **user_r, const char **error_r) { char *old_log_prefix = i_strdup(i_get_failure_prefix()); bool update_log_prefix; int ret; if (io_loop_get_current_context(current_ioloop) == NULL) { /* no user yet. log prefix should be just "imap:" or something equally unhelpful. we don't know the proper log format yet, but initialize it to something better until we know it. */ const char *session_id = input->session_id != NULL ? input->session_id : (input->session_id_prefix != NULL ? input->session_id_prefix : NULL); i_set_failure_prefix("%s(%s%s,%s)", master_service_get_name(ctx->service), input->username, session_id == NULL ? "" : t_strdup_printf(",%s", session_id), input->remote_ip.family == 0 ? "" : t_strdup_printf(",%s", net_ip2addr(&input->remote_ip))); update_log_prefix = TRUE; } else { /* we might be here because we're doing a user lookup for a shared user. the log prefix is likely already usable, so just append our own without replacing the whole thing. */ i_set_failure_prefix("%suser-lookup(%s)", old_log_prefix, input->username); update_log_prefix = FALSE; } ret = mail_storage_service_lookup_real(ctx, input, update_log_prefix, user_r, error_r); i_set_failure_prefix("%s", old_log_prefix); i_free(old_log_prefix); return ret; } void mail_storage_service_save_userdb_fields(struct mail_storage_service_ctx *ctx, pool_t pool, const char *const **userdb_fields_r) { i_assert(pool != NULL); i_assert(userdb_fields_r != NULL); ctx->userdb_next_pool = pool; ctx->userdb_next_fieldsp = userdb_fields_r; *userdb_fields_r = NULL; } static int mail_storage_service_next_real(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const char *session_id_suffix, struct mail_user **mail_user_r) { struct mail_storage_service_privileges priv; const char *error; size_t len; bool disallow_root = (user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0; bool temp_priv_drop = (user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0; bool use_chroot; if (service_parse_privileges(ctx, user, &priv, &error) < 0) { i_error("%s", error); return -2; } if (*priv.home != '/' && *priv.home != '\0') { i_error("Relative home directory paths not supported: %s", priv.home); return -2; } /* we can't chroot if we want to switch between users. there's not much point either (from security point of view). but if we're already chrooted, we'll just have to continue and hope that the current chroot is the same as the wanted chroot */ use_chroot = !temp_priv_drop || restrict_access_get_current_chroot() != NULL; len = strlen(priv.chroot); if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 && strncmp(priv.home, priv.chroot, len - 2) == 0) { /* mail_chroot = /chroot/. means that the home dir already contains the chroot dir. remove it from home. */ if (use_chroot) { priv.home += len - 2; if (*priv.home == '\0') priv.home = "/"; priv.chroot = t_strndup(priv.chroot, len - 2); set_keyval(ctx, user, "mail_home", priv.home); set_keyval(ctx, user, "mail_chroot", priv.chroot); } } else if (len > 0 && !use_chroot) { /* we're not going to chroot. fix home directory so we can access it. */ if (*priv.home == '\0' || strcmp(priv.home, "/") == 0) priv.home = priv.chroot; else priv.home = t_strconcat(priv.chroot, priv.home, NULL); priv.chroot = ""; set_keyval(ctx, user, "mail_home", priv.home); } /* create ioloop context regardless of logging. it's also used by stats plugin. */ user->ioloop_ctx = io_loop_context_new(current_ioloop); if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) mail_storage_service_init_log(ctx, user, &priv); if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) { if (service_drop_privileges(user, &priv, disallow_root, temp_priv_drop, FALSE, &error) < 0) { i_error("Couldn't drop privileges: %s", error); return -1; } if (!temp_priv_drop || (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0) restrict_access_allow_coredumps(TRUE); } /* privileges are dropped. initialize plugins that haven't been initialized yet. */ module_dir_init(mail_storage_service_modules); if (mail_storage_service_init_post(ctx, user, &priv, session_id_suffix, mail_user_r, &error) < 0) { i_error("User initialization failed: %s", error); return -2; } return 0; } int mail_storage_service_next(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_user **mail_user_r) { return mail_storage_service_next_with_session_suffix(ctx, user, NULL, mail_user_r); } int mail_storage_service_next_with_session_suffix(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, const char *session_id_suffix, struct mail_user **mail_user_r) { char *old_log_prefix = i_strdup(i_get_failure_prefix()); int ret; mail_storage_service_set_log_prefix(ctx, user->user_set, user, &user->input, NULL); i_set_failure_prefix("%s", old_log_prefix); ret = mail_storage_service_next_real(ctx, user, session_id_suffix, mail_user_r); if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) != 0) i_set_failure_prefix("%s", old_log_prefix); i_free(old_log_prefix); return ret; } void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user) { struct mail_storage_service_privileges priv; const char *error; if (service_parse_privileges(ctx, user, &priv, &error) < 0) i_fatal("user %s: %s", user->input.username, error); if (service_drop_privileges(user, &priv, FALSE, FALSE, TRUE, &error) < 0) i_fatal("user %s: %s", user->input.username, error); } int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input, struct mail_storage_service_user **user_r, struct mail_user **mail_user_r, const char **error_r) { struct mail_storage_service_user *user; int ret; ret = mail_storage_service_lookup(ctx, input, &user, error_r); if (ret <= 0) return ret; ret = mail_storage_service_next(ctx, user, mail_user_r); if (ret < 0) { mail_storage_service_user_unref(&user); *error_r = ret == -2 ? ERRSTR_INVALID_USER_SETTINGS : MAIL_ERRSTR_CRITICAL_MSG; return ret; } *user_r = user; return 1; } void mail_storage_service_user_ref(struct mail_storage_service_user *user) { i_assert(user->refcount > 0); user->refcount++; } void mail_storage_service_user_unref(struct mail_storage_service_user **_user) { struct mail_storage_service_user *user = *_user; *_user = NULL; i_assert(user->refcount > 0); if (--user->refcount > 0) return; if (user->ioloop_ctx != NULL) { if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) { io_loop_context_remove_callbacks(user->ioloop_ctx, mail_storage_service_io_activate_user, mail_storage_service_io_deactivate_user, user); if (io_loop_get_current_context(current_ioloop) == user->ioloop_ctx) mail_storage_service_io_deactivate_user(user); } io_loop_context_unref(&user->ioloop_ctx); } settings_parser_deinit(&user->set_parser); pool_unref(&user->pool); } void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_input *input) { const struct setting_parser_info *user_info; const struct mail_user_settings *user_set; const struct setting_parser_context *set_parser; const char *error; pool_t temp_pool; void **sets; if (ctx->conn != NULL) return; temp_pool = pool_alloconly_create("service all settings", 4096); if (mail_storage_service_read_settings(ctx, input, temp_pool, &user_info, &set_parser, &error) < 0) i_fatal("%s", error); sets = master_service_settings_parser_get_others(master_service, set_parser); user_set = sets[0]; mail_storage_service_first_init(ctx, user_info, user_set); pool_unref(&temp_pool); } static int mail_storage_service_all_iter_deinit(struct mail_storage_service_ctx *ctx) { int ret = 0; if (ctx->auth_list != NULL) { ret = auth_master_user_list_deinit(&ctx->auth_list); auth_master_deinit(&ctx->iter_conn); } return ret; } void mail_storage_service_all_init(struct mail_storage_service_ctx *ctx) { mail_storage_service_all_init_mask(ctx, ""); } void mail_storage_service_all_init_mask(struct mail_storage_service_ctx *ctx, const char *user_mask_hint) { enum auth_master_flags flags = 0; (void)mail_storage_service_all_iter_deinit(ctx); mail_storage_service_init_settings(ctx, NULL); /* create a new connection, because the iteration might take a while and we might want to do USER lookups during it, which don't mix well in the same connection. */ if (ctx->debug) flags |= AUTH_MASTER_FLAG_DEBUG; ctx->iter_conn = auth_master_init(auth_master_get_socket_path(ctx->conn), flags); ctx->auth_list = auth_master_user_list_init(ctx->iter_conn, user_mask_hint, NULL); } int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx, const char **username_r) { i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0); *username_r = auth_master_user_list_next(ctx->auth_list); if (*username_r != NULL) return 1; return mail_storage_service_all_iter_deinit(ctx); } void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx) { struct mail_storage_service_ctx *ctx = *_ctx; *_ctx = NULL; (void)mail_storage_service_all_iter_deinit(ctx); if (ctx->conn != NULL) { if (mail_user_auth_master_conn == ctx->conn) mail_user_auth_master_conn = NULL; auth_master_deinit(&ctx->conn); } if (ctx->set_cache != NULL) master_service_settings_cache_deinit(&ctx->set_cache); pool_unref(&ctx->pool); module_dir_unload(&mail_storage_service_modules); mail_storage_deinit(); dict_drivers_unregister_builtin(); } void **mail_storage_service_user_get_set(struct mail_storage_service_user *user) { return master_service_settings_parser_get_others(master_service, user->set_parser); } const struct mail_storage_settings * mail_storage_service_user_get_mail_set(struct mail_storage_service_user *user) { return mail_user_set_get_driver_settings( user->user_info, user->user_set, MAIL_STORAGE_SET_DRIVER_NAME); } const struct mail_storage_service_input * mail_storage_service_user_get_input(struct mail_storage_service_user *user) { return &user->input; } struct setting_parser_context * mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user) { return user->set_parser; } struct mail_storage_service_ctx * mail_storage_service_user_get_service_ctx(struct mail_storage_service_user *user) { return user->service_ctx; } pool_t mail_storage_service_user_get_pool(struct mail_storage_service_user *user) { return user->pool; } void *mail_storage_service_get_settings(struct master_service *service) { void **sets, *set; T_BEGIN { sets = master_service_settings_get_others(service); set = sets[1]; } T_END; return set; } int mail_storage_service_user_set_setting(struct mail_storage_service_user *user, const char *key, const char *value, const char **error_r) { int ret = settings_parse_keyvalue(user->set_parser, key, value); *error_r = settings_parser_get_error(user->set_parser); return ret; } dovecot-2.2.33.2/src/lib-storage/fail-mail-storage.h0000644000175000017500000000105513123174404016772 00000000000000#ifndef FAIL_MAIL_STORAGE_H #define FAIL_MAIL_STORAGE_H extern struct mail_storage fail_storage; extern struct mailbox fail_mailbox; extern struct mail_vfuncs fail_mail_vfuncs; struct mail_storage *fail_mail_storage_create(void); struct mailbox * fail_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags); struct mail * fail_mailbox_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); #endif dovecot-2.2.33.2/src/lib-storage/list/0002755000175000017500000000000013172375611014367 500000000000000dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-sync.h0000644000175000017500000000211213165463624021141 00000000000000#ifndef MAILBOX_LIST_INDEX_SYNC_H #define MAILBOX_LIST_INDEX_SYNC_H #include "mailbox-list-index.h" struct mailbox_list_index_sync_context { struct mailbox_list *list; struct mailbox_list_index *ilist; char sep[2]; uint32_t next_uid; uint32_t orig_highest_name_id; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; unsigned int syncing_list:1; }; int mailbox_list_index_sync_begin(struct mailbox_list *list, struct mailbox_list_index_sync_context **sync_ctx_r); int mailbox_list_index_sync_end(struct mailbox_list_index_sync_context **_sync_ctx, bool success); int mailbox_list_index_sync(struct mailbox_list *list, bool refresh); /* Add name to index, return seq in index. */ uint32_t mailbox_list_index_sync_name(struct mailbox_list_index_sync_context *ctx, const char *name, struct mailbox_list_index_node **node_r, bool *created_r); int mailbox_list_index_sync_delete(struct mailbox_list_index_sync_context *sync_ctx, const char *name, bool delete_selectable); #endif dovecot-2.2.33.2/src/lib-storage/list/Makefile.in0000644000175000017500000006117013172375574016367 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/list ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_list_la_LIBADD = am_libstorage_list_la_OBJECTS = mailbox-list-delete.lo \ mailbox-list-fs.lo mailbox-list-fs-flags.lo \ mailbox-list-fs-iter.lo mailbox-list-index.lo \ mailbox-list-index-backend.lo mailbox-list-index-iter.lo \ mailbox-list-index-notify.lo mailbox-list-index-status.lo \ mailbox-list-index-sync.lo mailbox-list-iter.lo \ mailbox-list-maildir.lo mailbox-list-maildir-iter.lo \ mailbox-list-none.lo mailbox-list-notify-tree.lo \ mailbox-list-subscriptions.lo subscription-file.lo libstorage_list_la_OBJECTS = $(am_libstorage_list_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_list_la_SOURCES) DIST_SOURCES = $(libstorage_list_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_list.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_list_la_SOURCES = \ mailbox-list-delete.c \ mailbox-list-fs.c \ mailbox-list-fs-flags.c \ mailbox-list-fs-iter.c \ mailbox-list-index.c \ mailbox-list-index-backend.c \ mailbox-list-index-iter.c \ mailbox-list-index-notify.c \ mailbox-list-index-status.c \ mailbox-list-index-sync.c \ mailbox-list-iter.c \ mailbox-list-maildir.c \ mailbox-list-maildir-iter.c \ mailbox-list-none.c \ mailbox-list-notify-tree.c \ mailbox-list-subscriptions.c \ subscription-file.c headers = \ mailbox-list-delete.h \ mailbox-list-fs.h \ mailbox-list-index.h \ mailbox-list-index-storage.h \ mailbox-list-index-sync.h \ mailbox-list-iter-private.h \ mailbox-list-maildir.h \ mailbox-list-notify-tree.h \ mailbox-list-subscriptions.h \ subscription-file.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/list/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/list/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_list.la: $(libstorage_list_la_OBJECTS) $(libstorage_list_la_DEPENDENCIES) $(EXTRA_libstorage_list_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_list_la_OBJECTS) $(libstorage_list_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-delete.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-fs-flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-fs-iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-fs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index-backend.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index-iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index-notify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index-status.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-index.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-maildir-iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-maildir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-none.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-notify-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-subscriptions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subscription-file.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-status.c0000644000175000017500000006417113165463624021520 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index-modseq.h" #include "mailbox-list-index-storage.h" #include "mailbox-list-index.h" #define CACHED_STATUS_ITEMS \ (STATUS_MESSAGES | STATUS_UNSEEN | STATUS_RECENT | \ STATUS_UIDNEXT | STATUS_UIDVALIDITY | STATUS_HIGHESTMODSEQ) struct index_list_changes { struct mailbox_status status; guid_128_t guid; uint32_t seq; struct mailbox_index_vsize vsize; uint32_t first_uid; bool rec_changed; bool msgs_changed; bool hmodseq_changed; bool vsize_changed; bool first_saved_changed; }; struct index_list_storage_module index_list_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); /* Should the STATUS information for this mailbox not be written to the mailbox list index? */ #define MAILBOX_IS_NEVER_IN_INDEX(box) \ ((box)->inbox_any && !(box)->storage->set->mailbox_list_index_include_inbox) static int index_list_open_view(struct mailbox *box, bool status_check, struct mail_index_view **view_r, uint32_t *seq_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_list_index_node *node; struct mail_index_view *view; uint32_t seq; int ret; if (MAILBOX_IS_NEVER_IN_INDEX(box) && status_check) return 0; if (mailbox_list_index_refresh(box->list) < 0) return -1; node = mailbox_list_index_lookup(box->list, box->name); if (node == NULL) { /* mailbox not found */ return 0; } view = mail_index_view_open(ilist->index); if (mailbox_list_index_need_refresh(ilist, view)) { /* mailbox_list_index_refresh_later() was called. Can't trust the index's contents. */ ret = 1; } else if (!mail_index_lookup_seq(view, node->uid, &seq)) { /* our in-memory tree is out of sync */ ret = 1; } else if (!status_check) { /* this operation doesn't need the index to be up-to-date */ ret = 0; } else T_BEGIN { ret = box->v.list_index_has_changed == NULL ? 0 : box->v.list_index_has_changed(box, view, seq); } T_END; if (ret != 0) { /* error / mailbox has changed. we'll need to sync it. */ if (ret < 0) mailbox_list_index_refresh_later(box->list); else ilist->index_last_check_changed = TRUE; mail_index_view_close(&view); return ret < 0 ? -1 : 0; } *view_r = view; *seq_r = seq; return 1; } static int index_list_exists(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); struct mail_index_view *view; const struct mail_index_record *rec; enum mailbox_list_index_flags flags; uint32_t seq; int ret; if ((ret = index_list_open_view(box, FALSE, &view, &seq)) <= 0) { /* failure / not found. fallback to the real storage check just in case to see if the mailbox was just created. */ return ibox->module_ctx.super. exists(box, auto_boxes, existence_r); } rec = mail_index_lookup(view, seq); flags = rec->flags; mail_index_view_close(&view); if ((flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) *existence_r = MAILBOX_EXISTENCE_NONE; else if ((flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) *existence_r = MAILBOX_EXISTENCE_NOSELECT; else *existence_r = MAILBOX_EXISTENCE_SELECT; return 0; } bool mailbox_list_index_status(struct mailbox_list *list, struct mail_index_view *view, uint32_t seq, enum mailbox_status_items items, struct mailbox_status *status_r, uint8_t *mailbox_guid, struct mailbox_index_vsize *vsize_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const void *data; bool expunged; bool ret = TRUE; if ((items & STATUS_UIDVALIDITY) != 0 || mailbox_guid != NULL) { const struct mailbox_list_index_record *rec; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); rec = data; if (rec == NULL) ret = FALSE; else { if ((items & STATUS_UIDVALIDITY) != 0 && rec->uid_validity == 0) ret = FALSE; else status_r->uidvalidity = rec->uid_validity; if (mailbox_guid != NULL) memcpy(mailbox_guid, rec->guid, GUID_128_SIZE); } } if ((items & (STATUS_MESSAGES | STATUS_UNSEEN | STATUS_RECENT | STATUS_UIDNEXT)) != 0) { const struct mailbox_list_index_msgs_record *rec; mail_index_lookup_ext(view, seq, ilist->msgs_ext_id, &data, &expunged); rec = data; if (rec == NULL || rec->uidnext == 0) ret = FALSE; else { status_r->messages = rec->messages; status_r->unseen = rec->unseen; status_r->recent = rec->recent; status_r->uidnext = rec->uidnext; } } if ((items & STATUS_HIGHESTMODSEQ) != 0) { const uint64_t *rec; mail_index_lookup_ext(view, seq, ilist->hmodseq_ext_id, &data, &expunged); rec = data; if (rec == NULL || *rec == 0) ret = FALSE; else status_r->highest_modseq = *rec; } if (vsize_r != NULL) { mail_index_lookup_ext(view, seq, ilist->vsize_ext_id, &data, &expunged); if (data == NULL) ret = FALSE; else memcpy(vsize_r, data, sizeof(*vsize_r)); } return ret; } static int index_list_get_cached_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct mail_index_view *view; uint32_t seq; int ret; if (items == 0) return 1; if ((items & STATUS_UNSEEN) != 0 && (mailbox_get_private_flags_mask(box) & MAIL_SEEN) != 0) { /* can't get UNSEEN from list index, since each user has different \Seen flags */ return 0; } if ((ret = index_list_open_view(box, TRUE, &view, &seq)) <= 0) return ret; ret = mailbox_list_index_status(box->list, view, seq, items, status_r, NULL, NULL) ? 1 : 0; mail_index_view_close(&view); return ret; } static int index_list_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if ((items & ~CACHED_STATUS_ITEMS) == 0 && !box->opened) { if (index_list_get_cached_status(box, items, status_r) > 0) return 0; /* nonsynced / error, fallback to doing it the slow way */ } return ibox->module_ctx.super.get_status(box, items, status_r); } static int index_list_get_cached_guid(struct mailbox *box, guid_128_t guid_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_status status; struct mail_index_view *view; uint32_t seq; int ret; if (ilist->syncing) { /* syncing wants to know the GUID for a new mailbox. */ return 0; } if ((ret = index_list_open_view(box, FALSE, &view, &seq)) <= 0) return ret; ret = mailbox_list_index_status(box->list, view, seq, 0, &status, guid_r, NULL) ? 1 : 0; if (ret > 0 && guid_128_is_empty(guid_r)) ret = 0; mail_index_view_close(&view); return ret; } static int index_list_get_cached_vsize(struct mailbox *box, uoff_t *vsize_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_status status; struct mailbox_index_vsize vsize; struct mail_index_view *view; uint32_t seq; int ret; i_assert(!ilist->syncing); if ((ret = index_list_open_view(box, TRUE, &view, &seq)) <= 0) return ret; ret = mailbox_list_index_status(box->list, view, seq, STATUS_MESSAGES | STATUS_UIDNEXT, &status, NULL, &vsize) ? 1 : 0; if (ret > 0 && status.messages == 0 && status.uidnext > 0) { /* mailbox is empty. its size has to be zero, regardless of what the vsize header says. */ vsize.vsize = 0; } else if (ret > 0 && (vsize.highest_uid + 1 != status.uidnext || vsize.message_count != status.messages)) { /* out of date vsize info */ ret = 0; } if (ret > 0) *vsize_r = vsize.vsize; mail_index_view_close(&view); return ret; } static int index_list_get_cached_first_saved(struct mailbox *box, struct mailbox_index_first_saved *first_saved_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mail_index_view *view; struct mailbox_status status; const void *data; bool expunged; uint32_t seq; int ret; i_zero(first_saved_r); if ((ret = index_list_open_view(box, TRUE, &view, &seq)) <= 0) return ret; mail_index_lookup_ext(view, seq, ilist->first_saved_ext_id, &data, &expunged); if (data != NULL) memcpy(first_saved_r, data, sizeof(*first_saved_r)); if (first_saved_r->timestamp != 0 && first_saved_r->uid == 0) { /* mailbox was empty the last time we updated this. we'll need to verify if it still is. */ if (!mailbox_list_index_status(box->list, view, seq, STATUS_MESSAGES, &status, NULL, NULL) || status.messages != 0) first_saved_r->timestamp = 0; } mail_index_view_close(&view); return first_saved_r->timestamp != 0 ? 1 : 0; } static int index_list_try_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { enum mailbox_metadata_items noncached_items; int ret; i_assert(metadata_r != NULL); if (box->opened) { /* if mailbox is already opened, don't bother using the values in mailbox list index. they have a higher chance of being wrong. */ return 0; } /* see if we have a chance of fulfilling this without opening the mailbox. */ noncached_items = items & ~(MAILBOX_METADATA_GUID | MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_FIRST_SAVE_DATE); if ((noncached_items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0 && box->mail_vfuncs->get_physical_size == box->mail_vfuncs->get_virtual_size) noncached_items = items & ~MAILBOX_METADATA_PHYSICAL_SIZE; if (noncached_items != 0) return 0; if ((items & MAILBOX_METADATA_GUID) != 0) { if ((ret = index_list_get_cached_guid(box, metadata_r->guid)) <= 0) return ret; } if ((items & (MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_PHYSICAL_SIZE)) != 0) { if ((ret = index_list_get_cached_vsize(box, &metadata_r->virtual_size)) <= 0) return ret; if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0) metadata_r->physical_size = metadata_r->virtual_size; } if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) { struct mailbox_index_first_saved first_saved; /* start writing first_saved to mailbox list index if it wasn't there already. */ box->update_first_saved = TRUE; if ((ret = index_list_get_cached_first_saved(box, &first_saved)) <= 0) return ret; metadata_r->first_save_date = first_saved.timestamp == (uint32_t)-1 ? (time_t)-1 : (time_t)first_saved.timestamp; } return 1; } static int index_list_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if (index_list_try_get_metadata(box, items, metadata_r) != 0) return 0; return ibox->module_ctx.super.get_metadata(box, items, metadata_r); } static void index_list_update_fill_vsize(struct mailbox *box, struct mail_index_view *view, struct index_list_changes *changes_r) { const void *data; size_t size; mail_index_get_header_ext(view, box->vsize_hdr_ext_id, &data, &size); if (size == sizeof(changes_r->vsize)) memcpy(&changes_r->vsize, data, sizeof(changes_r->vsize)); } static bool index_list_update_fill_changes(struct mailbox *box, struct mail_index_view *list_view, struct index_list_changes *changes_r) { struct mailbox_list_index_node *node; struct mail_index_view *view; const struct mail_index_header *hdr; struct mailbox_metadata metadata; uint32_t seq1, seq2; i_zero(changes_r); node = mailbox_list_index_lookup(box->list, box->name); if (node == NULL) return FALSE; if (!mail_index_lookup_seq(list_view, node->uid, &changes_r->seq)) return FALSE; /* get STATUS info using the latest data in index. note that for shared mailboxes (with private indexes) this also means that the unseen count is always the owner's count, not what exists in the private index. */ view = mail_index_view_open(box->index); hdr = mail_index_get_header(view); changes_r->status.messages = hdr->messages_count; changes_r->status.unseen = hdr->messages_count - hdr->seen_messages_count; changes_r->status.uidvalidity = hdr->uid_validity; changes_r->status.uidnext = hdr->next_uid; if (!mail_index_lookup_seq_range(view, hdr->first_recent_uid, (uint32_t)-1, &seq1, &seq2)) changes_r->status.recent = 0; else changes_r->status.recent = seq2 - seq1 + 1; changes_r->status.highest_modseq = mail_index_modseq_get_highest(view); if (changes_r->status.highest_modseq == 0) { /* modseqs not enabled yet, but we can't return 0 */ changes_r->status.highest_modseq = 1; } index_list_update_fill_vsize(box, view, changes_r); mail_index_view_close(&view); hdr = NULL; if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) == 0) memcpy(changes_r->guid, metadata.guid, sizeof(changes_r->guid)); return TRUE; } static void index_list_first_saved_update_changes(struct mailbox *box, struct mail_index_view *list_view, struct index_list_changes *changes) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_index_first_saved first_saved; const void *data; bool expunged; mail_index_lookup_ext(list_view, changes->seq, ilist->first_saved_ext_id, &data, &expunged); if (data == NULL) i_zero(&first_saved); else memcpy(&first_saved, data, sizeof(first_saved)); if (mail_index_view_get_messages_count(box->view) > 0) mail_index_lookup_uid(box->view, 1, &changes->first_uid); if (first_saved.uid == 0 && first_saved.timestamp == 0) { /* it's not in the index yet. we'll set it only if we've just called MAILBOX_METADATA_FIRST_SAVE_DATE. */ changes->first_saved_changed = box->update_first_saved; } else { changes->first_saved_changed = changes->first_uid != first_saved.uid; } } static bool index_list_has_changed(struct mailbox *box, struct mail_index_view *list_view, struct index_list_changes *changes) { struct mailbox_status old_status; struct mailbox_index_vsize old_vsize; guid_128_t old_guid; i_zero(&old_status); i_zero(&old_vsize); memset(old_guid, 0, sizeof(old_guid)); (void)mailbox_list_index_status(box->list, list_view, changes->seq, CACHED_STATUS_ITEMS, &old_status, old_guid, &old_vsize); changes->rec_changed = old_status.uidvalidity != changes->status.uidvalidity && changes->status.uidvalidity != 0; if (!guid_128_equals(changes->guid, old_guid) && !guid_128_is_empty(changes->guid)) changes->rec_changed = TRUE; if (MAILBOX_IS_NEVER_IN_INDEX(box)) { /* check only UIDVALIDITY and GUID changes for INBOX */ return changes->rec_changed; } changes->msgs_changed = old_status.messages != changes->status.messages || old_status.unseen != changes->status.unseen || old_status.recent != changes->status.recent || old_status.uidnext != changes->status.uidnext; /* update highest-modseq only if they're ever been used */ if (old_status.highest_modseq == changes->status.highest_modseq) { changes->hmodseq_changed = FALSE; } else { changes->hmodseq_changed = TRUE; } if (memcmp(&old_vsize, &changes->vsize, sizeof(old_vsize)) != 0) changes->vsize_changed = TRUE; index_list_first_saved_update_changes(box, list_view, changes); return changes->rec_changed || changes->msgs_changed || changes->hmodseq_changed || changes->vsize_changed || changes->first_saved_changed; } static void index_list_update_first_saved(struct mailbox *box, struct mail_index_transaction *list_trans, const struct index_list_changes *changes) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_transaction_context *t; struct mail *mail; struct mailbox_index_first_saved first_saved; uint32_t seq, messages_count; time_t save_date; int ret = 0; i_zero(&first_saved); first_saved.timestamp = (uint32_t)-1; if (changes->first_uid != 0) { t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, MAIL_FETCH_SAVE_DATE, NULL); messages_count = mail_index_view_get_messages_count(box->view); for (seq = 1; seq <= messages_count; seq++) { mail_set_seq(mail, seq); if (mail_get_save_date(mail, &save_date) == 0) { first_saved.uid = mail->uid; first_saved.timestamp = save_date; break; } if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) { ret = -1; break; } } mail_free(&mail); (void)mailbox_transaction_commit(&t); } if (ret == 0) { mail_index_update_ext(list_trans, changes->seq, ilist->first_saved_ext_id, &first_saved, NULL); } } static void index_list_update(struct mailbox *box, struct mail_index_view *list_view, struct mail_index_transaction *list_trans, const struct index_list_changes *changes) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); if (changes->rec_changed) { struct mailbox_list_index_record rec; const void *old_data; bool expunged; mail_index_lookup_ext(list_view, changes->seq, ilist->ext_id, &old_data, &expunged); i_assert(old_data != NULL); memcpy(&rec, old_data, sizeof(rec)); if (changes->status.uidvalidity != 0) rec.uid_validity = changes->status.uidvalidity; if (!guid_128_is_empty(changes->guid)) memcpy(rec.guid, changes->guid, sizeof(rec.guid)); mail_index_update_ext(list_trans, changes->seq, ilist->ext_id, &rec, NULL); } if (changes->msgs_changed) { struct mailbox_list_index_msgs_record msgs; i_zero(&msgs); msgs.messages = changes->status.messages; msgs.unseen = changes->status.unseen; msgs.recent = changes->status.recent; msgs.uidnext = changes->status.uidnext; mail_index_update_ext(list_trans, changes->seq, ilist->msgs_ext_id, &msgs, NULL); } if (changes->hmodseq_changed) { mail_index_update_ext(list_trans, changes->seq, ilist->hmodseq_ext_id, &changes->status.highest_modseq, NULL); } if (changes->vsize_changed) { mail_index_update_ext(list_trans, changes->seq, ilist->vsize_ext_id, &changes->vsize, NULL); } if (changes->first_saved_changed) index_list_update_first_saved(box, list_trans, changes); } static int index_list_update_mailbox(struct mailbox *box) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mail_index_sync_ctx *list_sync_ctx; struct mail_index_view *list_view; struct mail_index_transaction *list_trans; struct index_list_changes changes; int ret; i_assert(box->opened); if (ilist->syncing || ilist->updating_status) return 0; if (box->deleting) { /* don't update status info while mailbox is being deleted. especially not a good idea if we're rollbacking a created mailbox that somebody else had just created */ return 0; } /* refresh the mailbox list index once. we can't do this again after locking, because it could trigger list syncing. */ (void)mailbox_list_index_refresh(box->list); /* first do a quick check while unlocked to see if anything changes */ list_view = mail_index_view_open(ilist->index); if (!index_list_update_fill_changes(box, list_view, &changes)) ret = -1; else if (index_list_has_changed(box, list_view, &changes)) ret = 1; else { /* if backend state changed on the last check, update it here now. we probably don't need to bother checking again if the state had changed? */ ret = ilist->index_last_check_changed; } mail_index_view_close(&list_view); if (ret <= 0) { if (ret < 0) mailbox_list_index_refresh_later(box->list); return 0; } /* looks like there are some changes. now lock the list index and do the whole thing all over again while locked. this guarantees that we'll always write the latest state of the mailbox. */ if (mail_index_sync_begin(ilist->index, &list_sync_ctx, &list_view, &list_trans, 0) < 0) { mailbox_set_index_error(box); return -1; } /* refresh to latest state of the mailbox now that we're locked */ if (mail_index_refresh(box->index) < 0) { mailbox_set_index_error(box); mail_index_sync_rollback(&list_sync_ctx); return -1; } if (!index_list_update_fill_changes(box, list_view, &changes)) mailbox_list_index_refresh_later(box->list); else { ilist->updating_status = TRUE; if (index_list_has_changed(box, list_view, &changes)) index_list_update(box, list_view, list_trans, &changes); if (box->v.list_index_update_sync != NULL) { box->v.list_index_update_sync(box, list_trans, changes.seq); } ilist->updating_status = FALSE; } struct mail_index_sync_rec sync_rec; while (mail_index_sync_next(list_sync_ctx, &sync_rec)) ; if (mail_index_sync_commit(&list_sync_ctx) < 0) { mailbox_set_index_error(box); return -1; } ilist->index_last_check_changed = FALSE; return 0; } void mailbox_list_index_update_mailbox_index(struct mailbox *box, const struct mailbox_update *update) { struct mail_index_view *list_view; struct mail_index_transaction *list_trans; struct index_list_changes changes; struct mailbox_status status; guid_128_t mailbox_guid; bool guid_changed = FALSE; int ret; i_zero(&changes); /* update the mailbox list index even if it has some other pending changes. */ if ((ret = index_list_open_view(box, FALSE, &list_view, &changes.seq)) <= 0) return; (void)mailbox_list_index_status(box->list, list_view, changes.seq, CACHED_STATUS_ITEMS, &status, mailbox_guid, NULL); if (update->uid_validity != 0) { changes.rec_changed = TRUE; changes.status.uidvalidity = update->uid_validity; } if (!guid_128_equals(update->mailbox_guid, mailbox_guid) && !guid_128_is_empty(update->mailbox_guid) && !guid_128_is_empty(mailbox_guid)) { changes.rec_changed = TRUE; memcpy(changes.guid, update->mailbox_guid, sizeof(changes.guid)); guid_changed = TRUE; } if (guid_changed || update->uid_validity != 0 || update->min_next_uid != 0 || update->min_first_recent_uid != 0 || update->min_highest_modseq != 0) { /* reset status counters to 0. let the syncing later figure out their correct values. */ changes.msgs_changed = TRUE; changes.hmodseq_changed = TRUE; } list_trans = mail_index_transaction_begin(list_view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); index_list_update(box, list_view, list_trans, &changes); (void)mail_index_transaction_commit(&list_trans); mail_index_view_close(&list_view); } static struct mailbox_sync_context * index_list_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); const struct mail_index_header *hdr; hdr = mail_index_get_header(box->view); ibox->pre_sync_log_file_seq = hdr->log_file_seq; ibox->pre_sync_log_file_head_offset = hdr->log_file_head_offset; return ibox->module_ctx.super.sync_init(box, flags); } static int index_list_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r) { struct mailbox *box = ctx->box; struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); const struct mail_index_header *hdr; if (ibox->module_ctx.super.sync_deinit(ctx, status_r) < 0) return -1; ctx = NULL; hdr = mail_index_get_header(box->view); if (!ilist->opened && ibox->pre_sync_log_file_head_offset == hdr->log_file_head_offset && ibox->pre_sync_log_file_seq == hdr->log_file_seq) { /* List index isn't open and sync changed nothing. Don't bother opening the list index. */ return 0; } /* it probably doesn't matter much here if we push/pop the error, but might as well do it. */ mail_storage_last_error_push(mailbox_get_storage(box)); (void)index_list_update_mailbox(box); mail_storage_last_error_pop(mailbox_get_storage(box)); return 0; } static int index_list_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct mailbox *box = t->box; struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if (ibox->module_ctx.super.transaction_commit(t, changes_r) < 0) return -1; t = NULL; if (!changes_r->changed) return 0; /* this transaction commit may have been done in error handling path and the caller still wants to access the current error. make sure that whatever we do here won't change the error. */ mail_storage_last_error_push(mailbox_get_storage(box)); (void)index_list_update_mailbox(box); mail_storage_last_error_pop(mailbox_get_storage(box)); return 0; } void mailbox_list_index_status_set_info_flags(struct mailbox *box, uint32_t uid, enum mailbox_info_flags *flags) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mail_index_view *view; struct mailbox_status status; uint32_t seq; int ret; view = mail_index_view_open(ilist->index); if (!mail_index_lookup_seq(view, uid, &seq)) { /* our in-memory tree is out of sync */ ret = 1; } else T_BEGIN { /* kludge: avoid breaking API for v2.2.x. Fixed in v2.3.x. */ box->list_index_has_changed_quick = TRUE; ret = box->v.list_index_has_changed == NULL ? 0 : box->v.list_index_has_changed(box, view, seq); box->list_index_has_changed_quick = FALSE; } T_END; if (ret != 0) { /* error / not up to date. don't waste time with it. */ mail_index_view_close(&view); return; } status.recent = 0; (void)mailbox_list_index_status(box->list, view, seq, STATUS_RECENT, &status, NULL, NULL); mail_index_view_close(&view); if (status.recent != 0) *flags |= MAILBOX_MARKED; else *flags |= MAILBOX_UNMARKED; } void mailbox_list_index_status_init_mailbox(struct mailbox_vfuncs *v) { v->exists = index_list_exists; v->get_status = index_list_get_status; v->get_metadata = index_list_get_metadata; v->sync_init = index_list_sync_init; v->sync_deinit = index_list_sync_deinit; v->transaction_commit = index_list_transaction_commit; } void mailbox_list_index_status_init_finish(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); ilist->msgs_ext_id = mail_index_ext_register(ilist->index, "msgs", 0, sizeof(struct mailbox_list_index_msgs_record), sizeof(uint32_t)); ilist->hmodseq_ext_id = mail_index_ext_register(ilist->index, "hmodseq", 0, sizeof(uint64_t), sizeof(uint64_t)); ilist->vsize_ext_id = mail_index_ext_register(ilist->index, "vsize", 0, sizeof(struct mailbox_index_vsize), sizeof(uint64_t)); ilist->first_saved_ext_id = mail_index_ext_register(ilist->index, "1saved", 0, sizeof(struct mailbox_index_first_saved), sizeof(uint32_t)); } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-delete.h0000644000175000017500000000750213165463624020332 00000000000000#ifndef MAILBOX_LIST_DELETE_H #define MAILBOX_LIST_DELETE_H #include "mailbox-list.h" /* Delete the mailbox atomically by rename()ing it to trash_dir and afterwards recursively deleting the trash_dir. If the rename() fails because trash_dir already exists, the trash_dir is first deleted and rename() is retried. Returns 1 if the rename() succeeded. Returns 0 if rename() fails with EXDEV, which means the source and destination are on different filesystems and the rename can never succeeed. If the path didn't exist, returns -1 and sets the list error to MAIL_ERROR_NOTFOUND. Attempting to delete INBOX or the namespace root returns -1 and sets the list error to MAIL_ERROR_NOTPOSSIBLE. Returns -1 and sets the list error on other errors. */ int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list, const char *name, const char *trash_dir); /* Try to unlink() the path. Returns 0 on success. If the path didn't exist, returns -1 and sets the list error to MAIL_ERROR_NOTFOUND. Returns -1 and sets the list error on other errors. */ int mailbox_list_delete_mailbox_file(struct mailbox_list *list, const char *name, const char *path); /* Delete all files from the given path. Also all internal directories (as returned by is_internal_name() check) are recursively deleted. Otherwise directories are left undeleted. Returns 0 if anything was unlink()ed and no unexpected errors happened. Also returns 0 if there were no files and the path was successfully rmdir()ed. If the path didn't exist, returns -1 and sets the list error to MAIL_ERROR_NOTFOUND. If the path exists and has subdirectories, but no files were unlink()ed, returns -1 and sets the list error to MAIL_ERROR_NOTPOSSIBLE. Attempting to delete INBOX or the namespace root returns -1 and sets the list error to MAIL_ERROR_NOTPOSSIBLE. Returns -1 and sets the list error on other errors. */ int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, const char *name, const char *path, bool rmdir_path); /* Lookup INDEX, CONTROL and ALT directories for the mailbox and delete them. Returns 1 if anything was unlink()ed or rmdir()ed, 0 if not. Returns -1 and sets the list error on any errors. */ int mailbox_list_delete_finish(struct mailbox_list *list, const char *name); /* Finish mailbox deletion by calling mailbox_list_delete_finish() if needed. Set root_delete_success to TRUE if the mail root directory was successfully deleted, FALSE if not. The list is expected to have a proper error when root_delete_success==FALSE. Returns 0 if mailbox deletion should be treated as success. If not, returns -1 and sets the list error if necessary. */ int mailbox_list_delete_finish_ret(struct mailbox_list *list, const char *name, bool root_delete_success); /* rmdir() path and its parent directories until the root directory is reached. The root isn't rmdir()ed. */ void mailbox_list_delete_until_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type); /* Call mailbox_list_delete_until_root() for all the paths of the mailbox. */ void mailbox_list_delete_mailbox_until_root(struct mailbox_list *list, const char *storage_name); /* Wrapper to unlink_directory(UNLINK_DIRECTORY_FLAG_RMDIR). If it fails due to ELOOP, try to unlink() the path instead. */ int mailbox_list_delete_trash(const char *path); /* Try to unlink() the path to the mailbox. Returns 0 on success. If the path didn't exist, returns -1 and sets the list error to MAIL_ERROR_NOTFOUND. If the path is a directory, returns -1 and sets the list error to MAIL_ERROR_NOTPOSSIBLE. Returns -1 and sets the list error on other errors. */ int mailbox_list_delete_symlink_default(struct mailbox_list *list, const char *name); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index.c0000644000175000017500000006732113165463624020177 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "str.h" #include "mail-index-view-private.h" #include "mail-storage-hooks.h" #include "mail-storage-private.h" #include "mailbox-list-index-storage.h" #include "mailbox-list-index-sync.h" #define MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS 1000 /* dovecot.list.index.log doesn't have to be kept for that long. */ #define MAILBOX_LIST_INDEX_LOG_ROTATE_MIN_SIZE (8*1024) #define MAILBOX_LIST_INDEX_LOG_ROTATE_MAX_SIZE (64*1024) #define MAILBOX_LIST_INDEX_LOG_ROTATE_SECS_AGO (5*60) #define MAILBOX_LIST_INDEX_LOG2_STALE_SECS (10*60) static void mailbox_list_index_init_finish(struct mailbox_list *list); struct mailbox_list_index_module mailbox_list_index_module = MODULE_CONTEXT_INIT(&mailbox_list_module_register); void mailbox_list_index_set_index_error(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_set_internal_error(list); mail_index_reset_error(ilist->index); } static void mailbox_list_index_init_pool(struct mailbox_list_index *ilist) { ilist->mailbox_pool = pool_alloconly_create("mailbox list index", 4096); hash_table_create_direct(&ilist->mailbox_names, ilist->mailbox_pool, 0); hash_table_create_direct(&ilist->mailbox_hash, ilist->mailbox_pool, 0); } void mailbox_list_index_reset(struct mailbox_list_index *ilist) { hash_table_destroy(&ilist->mailbox_names); hash_table_destroy(&ilist->mailbox_hash); pool_unref(&ilist->mailbox_pool); ilist->mailbox_tree = NULL; ilist->highest_name_id = 0; ilist->sync_log_file_seq = 0; ilist->sync_log_file_offset = 0; mailbox_list_index_init_pool(ilist); } int mailbox_list_index_index_open(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const struct mail_storage_settings *set = list->mail_set; enum mail_index_open_flags index_flags; unsigned int lock_timeout; if (ilist->opened) return 0; if (mailbox_list_mkdir_missing_list_index_root(list) < 0) return -1; i_assert(ilist->index != NULL); index_flags = mail_storage_settings_to_index_flags(set); if (strcmp(list->name, MAILBOX_LIST_NAME_INDEX) == 0) { /* LAYOUT=index. this is the only location for the mailbox data, so we must never move it into memory. */ index_flags |= MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY; } lock_timeout = set->mail_max_lock_timeout == 0 ? UINT_MAX : set->mail_max_lock_timeout; if (!mail_index_use_existing_permissions(ilist->index)) { struct mailbox_permissions perm; mailbox_list_get_root_permissions(list, &perm); mail_index_set_permissions(ilist->index, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); } mail_index_set_log_rotation(ilist->index, MAILBOX_LIST_INDEX_LOG_ROTATE_MIN_SIZE, MAILBOX_LIST_INDEX_LOG_ROTATE_MAX_SIZE, MAILBOX_LIST_INDEX_LOG_ROTATE_SECS_AGO, MAILBOX_LIST_INDEX_LOG2_STALE_SECS); mail_index_set_fsync_mode(ilist->index, set->parsed_fsync_mode, 0); mail_index_set_lock_method(ilist->index, set->parsed_lock_method, lock_timeout); if (mail_index_open_or_create(ilist->index, index_flags) < 0) { if (mail_index_move_to_memory(ilist->index) < 0) { /* try opening once more. it should be created directly into memory now, except if it fails with LAYOUT=index backend. */ if (mail_index_open_or_create(ilist->index, index_flags) < 0) { mailbox_list_set_internal_error(list); return -1; } } } ilist->opened = TRUE; return 0; } struct mailbox_list_index_node * mailbox_list_index_node_find_sibling(struct mailbox_list_index_node *node, const char *name) { while (node != NULL) { if (strcmp(node->name, name) == 0) return node; node = node->next; } return NULL; } static struct mailbox_list_index_node * mailbox_list_index_lookup_real(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_node *node = ilist->mailbox_tree; const char *const *path; unsigned int i; char sep[2]; if (*name == '\0') return mailbox_list_index_node_find_sibling(node, ""); sep[0] = mailbox_list_get_hierarchy_sep(list); sep[1] = '\0'; path = t_strsplit(name, sep); for (i = 0;; i++) { node = mailbox_list_index_node_find_sibling(node, path[i]); if (node == NULL || path[i+1] == NULL) break; node = node->children; } return node; } struct mailbox_list_index_node * mailbox_list_index_lookup(struct mailbox_list *list, const char *name) { struct mailbox_list_index_node *node; T_BEGIN { node = mailbox_list_index_lookup_real(list, name); } T_END; return node; } struct mailbox_list_index_node * mailbox_list_index_lookup_uid(struct mailbox_list_index *ilist, uint32_t uid) { return hash_table_lookup(ilist->mailbox_hash, POINTER_CAST(uid)); } void mailbox_list_index_node_get_path(const struct mailbox_list_index_node *node, char sep, string_t *str) { if (node->parent != NULL) { mailbox_list_index_node_get_path(node->parent, sep, str); str_append_c(str, sep); } str_append(str, node->name); } void mailbox_list_index_node_unlink(struct mailbox_list_index *ilist, struct mailbox_list_index_node *node) { struct mailbox_list_index_node **prev; prev = node->parent == NULL ? &ilist->mailbox_tree : &node->parent->children; while (*prev != node) prev = &(*prev)->next; *prev = node->next; } static int mailbox_list_index_parse_header(struct mailbox_list_index *ilist, struct mail_index_view *view) { const void *data, *name_start, *p; size_t i, len, size; uint32_t id, prev_id = 0; string_t *str; char *name; int ret = 0; mail_index_map_get_header_ext(view, view->map, ilist->ext_id, &data, &size); if (size == 0) return 0; str = t_str_new(128); for (i = sizeof(struct mailbox_list_index_header); i < size; ) { /* get id */ if (i + sizeof(id) > size) return -1; memcpy(&id, CONST_PTR_OFFSET(data, i), sizeof(id)); i += sizeof(id); if (id <= prev_id) { /* allow extra space in the end as long as last id=0 */ return id == 0 ? 0 : -1; } prev_id = id; /* get name */ p = memchr(CONST_PTR_OFFSET(data, i), '\0', size-i); if (p == NULL) return -1; name_start = CONST_PTR_OFFSET(data, i); len = (const char *)p - (const char *)name_start; if (uni_utf8_get_valid_data(name_start, len, str)) { name = p_strndup(ilist->mailbox_pool, name_start, len); } else { /* corrupted index. fix the name. */ name = p_strdup(ilist->mailbox_pool, str_c(str)); str_truncate(str, 0); ret = -1; } i += len + 1; /* add id => name to hash table */ hash_table_insert(ilist->mailbox_names, POINTER_CAST(id), name); ilist->highest_name_id = id; } i_assert(i == size); return ret; } static void mailbox_list_index_generate_name(struct mailbox_list_index *ilist, struct mailbox_list_index_node *node, const char *prefix) { guid_128_t guid; char *name; i_assert(node->name_id != 0); guid_128_generate(guid); name = p_strdup_printf(ilist->mailbox_pool, "%s%s", prefix, guid_128_to_string(guid)); node->name = name; node->flags |= MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME; hash_table_insert(ilist->mailbox_names, POINTER_CAST(node->name_id), name); if (ilist->highest_name_id < node->name_id) ilist->highest_name_id = node->name_id; } static int mailbox_list_index_node_cmp(const struct mailbox_list_index_node *n1, const struct mailbox_list_index_node *n2) { return n1->parent == n2->parent && strcmp(n1->name, n2->name) == 0 ? 0 : -1; } static unsigned int mailbox_list_index_node_hash(const struct mailbox_list_index_node *node) { return str_hash(node->name) ^ POINTER_CAST_TO(node->parent, unsigned int); } static bool node_has_parent(const struct mailbox_list_index_node *parent, const struct mailbox_list_index_node *node) { const struct mailbox_list_index_node *n; for (n = parent; n != NULL; n = n->parent) { if (n == node) return TRUE; } return FALSE; } static int mailbox_list_index_parse_records(struct mailbox_list_index *ilist, struct mail_index_view *view, const char **error_r) { struct mailbox_list_index_node *node, *parent; HASH_TABLE(struct mailbox_list_index_node *, struct mailbox_list_index_node *) duplicate_hash; const struct mail_index_record *rec; const struct mailbox_list_index_record *irec; const void *data; bool expunged; uint32_t seq, uid, count; *error_r = NULL; hash_table_create(&duplicate_hash, default_pool, 0, mailbox_list_index_node_hash, mailbox_list_index_node_cmp); count = mail_index_view_get_messages_count(view); for (seq = 1; seq <= count; seq++) { node = p_new(ilist->mailbox_pool, struct mailbox_list_index_node, 1); rec = mail_index_lookup(view, seq); node->uid = rec->uid; node->flags = rec->flags; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); if (data == NULL) { *error_r = "Missing list extension data"; /* list index is missing, no point trying to do second scan either */ count = 0; break; } irec = data; if (!ilist->has_backing_store && guid_128_is_empty(irec->guid) && (rec->flags & (MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_NOSELECT)) == 0) { /* no backing store and mailbox has no GUID. it can't be selectable, but the flag is missing. */ node->flags |= MAILBOX_LIST_INDEX_FLAG_NOSELECT; *error_r = "mailbox is missing guid - " "setting it non-selectable"; node->corrupted_flags = TRUE; } node->name_id = irec->name_id; if (node->name_id == 0) { /* invalid name_id - assign a new one */ node->name_id = ++ilist->highest_name_id; node->corrupted_ext = TRUE; } node->name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(irec->name_id)); if (node->name == NULL) { *error_r = t_strdup_printf( "name_id=%u not in index header", irec->name_id); if (ilist->has_backing_store) break; /* generate a new name and use it */ mailbox_list_index_generate_name(ilist, node, "unknown-"); } hash_table_insert(ilist->mailbox_hash, POINTER_CAST(node->uid), node); } /* do a second scan to create the actual mailbox tree hierarchy. this is needed because the parent_uid may be smaller or higher than the current node's uid */ if (*error_r != NULL && ilist->has_backing_store) count = 0; for (seq = 1; seq <= count; seq++) { mail_index_lookup_uid(view, seq, &uid); mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); irec = data; node = mailbox_list_index_lookup_uid(ilist, uid); i_assert(node != NULL); if (irec->parent_uid != 0) { /* node should have a parent */ parent = mailbox_list_index_lookup_uid(ilist, irec->parent_uid); if (parent == NULL) { *error_r = t_strdup_printf( "parent_uid=%u points to nonexistent record", irec->parent_uid); if (ilist->has_backing_store) break; /* just place it under the root */ node->corrupted_ext = TRUE; } else if (node_has_parent(parent, node)) { *error_r = t_strdup_printf( "parent_uid=%u loops to node itself (%s)", uid, node->name); if (ilist->has_backing_store) break; /* just place it under the root */ node->corrupted_ext = TRUE; } else { node->parent = parent; node->next = parent->children; parent->children = node; continue; } } if (hash_table_lookup(duplicate_hash, node) == NULL) hash_table_insert(duplicate_hash, node, node); else { const char *old_name = node->name; if (ilist->has_backing_store) { *error_r = t_strdup_printf( "Duplicate mailbox '%s' in index", node->name); break; } /* we have only the mailbox list index and this node may have a different GUID, so rename it. */ node->corrupted_ext = TRUE; node->name_id = ++ilist->highest_name_id; mailbox_list_index_generate_name(ilist, node, t_strconcat(node->name, "-duplicate-", NULL)); *error_r = t_strdup_printf( "Duplicate mailbox '%s' in index, renaming to %s", old_name, node->name); } node->next = ilist->mailbox_tree; ilist->mailbox_tree = node; } hash_table_destroy(&duplicate_hash); return *error_r == NULL ? 0 : -1; } int mailbox_list_index_parse(struct mailbox_list *list, struct mail_index_view *view, bool force) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const struct mail_index_header *hdr; const char *error; hdr = mail_index_get_header(view); if (!force && hdr->log_file_seq == ilist->sync_log_file_seq && hdr->log_file_head_offset == ilist->sync_log_file_offset) { /* nothing changed */ return 0; } if ((hdr->flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0) ilist->call_corruption_callback = TRUE; mailbox_list_index_reset(ilist); ilist->sync_log_file_seq = hdr->log_file_seq; ilist->sync_log_file_offset = hdr->log_file_head_offset; if (mailbox_list_index_parse_header(ilist, view) < 0) { mailbox_list_set_critical(list, "Corrupted mailbox list index header %s", ilist->path); if (ilist->has_backing_store) { mail_index_mark_corrupted(ilist->index); return -1; } ilist->call_corruption_callback = TRUE; ilist->corrupted_names_or_parents = TRUE; } if (mailbox_list_index_parse_records(ilist, view, &error) < 0) { mailbox_list_set_critical(list, "Corrupted mailbox list index %s: %s", ilist->path, error); if (ilist->has_backing_store) { mail_index_mark_corrupted(ilist->index); return -1; } /* FIXME: find any missing mailboxes, add them and write the index back. */ ilist->call_corruption_callback = TRUE; ilist->corrupted_names_or_parents = TRUE; } return 0; } bool mailbox_list_index_need_refresh(struct mailbox_list_index *ilist, struct mail_index_view *view) { const struct mailbox_list_index_header *hdr; const void *data; size_t size; if (!ilist->has_backing_store) return FALSE; mail_index_get_header_ext(view, ilist->ext_id, &data, &size); hdr = data; return hdr != NULL && hdr->refresh_flag != 0; } int mailbox_list_index_refresh(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if (ilist->syncing) return 0; if (ilist->last_refresh_timeval.tv_usec == ioloop_timeval.tv_usec && ilist->last_refresh_timeval.tv_sec == ioloop_timeval.tv_sec) { /* we haven't been to ioloop since last refresh, skip checking it. when we're accessing many mailboxes at once (e.g. opening a virtual mailbox) we don't want to stat/read the index every single time. */ return 0; } return mailbox_list_index_refresh_force(list); } int mailbox_list_index_refresh_force(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mail_index_view *view; int ret; bool refresh; i_assert(!ilist->syncing); ilist->last_refresh_timeval = ioloop_timeval; if (mailbox_list_index_index_open(list) < 0) return -1; if (mail_index_refresh(ilist->index) < 0) { mailbox_list_index_set_index_error(list); return -1; } view = mail_index_view_open(ilist->index); if ((refresh = mailbox_list_index_need_refresh(ilist, view)) || ilist->mailbox_tree == NULL) { /* refresh list of mailboxes */ ret = mailbox_list_index_sync(list, refresh); } else { ret = mailbox_list_index_parse(list, view, FALSE); } mail_index_view_close(&view); if (mailbox_list_index_handle_corruption(list) < 0) ret = -1; return ret; } static void mailbox_list_index_refresh_timeout(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); timeout_remove(&ilist->to_refresh); (void)mailbox_list_index_refresh(list); } void mailbox_list_index_refresh_later(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_header new_hdr; struct mail_index_view *view; struct mail_index_transaction *trans; memset(&ilist->last_refresh_timeval, 0, sizeof(ilist->last_refresh_timeval)); if (!ilist->has_backing_store) return; (void)mailbox_list_index_index_open(list); view = mail_index_view_open(ilist->index); if (!mailbox_list_index_need_refresh(ilist, view)) { new_hdr.refresh_flag = 1; trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header_ext(trans, ilist->ext_id, offsetof(struct mailbox_list_index_header, refresh_flag), &new_hdr.refresh_flag, sizeof(new_hdr.refresh_flag)); if (mail_index_transaction_commit(&trans) < 0) mail_index_mark_corrupted(ilist->index); } mail_index_view_close(&view); if (ilist->to_refresh == NULL) { ilist->to_refresh = timeout_add(MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS, mailbox_list_index_refresh_timeout, list); } } int mailbox_list_index_handle_corruption(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mail_storage *const *storagep; int ret = 0; if (!ilist->call_corruption_callback) return 0; /* make sure we don't recurse */ if (ilist->handling_corruption) return 0; ilist->handling_corruption = TRUE; array_foreach(&list->ns->all_storages, storagep) { if ((*storagep)->v.list_index_corrupted != NULL) { if ((*storagep)->v.list_index_corrupted(*storagep) < 0) ret = -1; else { /* FIXME: implement a generic handler that just lists mailbox directories in filesystem and adds the missing ones to the index. */ } } } if (ret == 0) ret = mailbox_list_index_set_uncorrupted(list); ilist->handling_corruption = FALSE; return ret; } int mailbox_list_index_set_uncorrupted(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_sync_context *sync_ctx; ilist->call_corruption_callback = FALSE; if (mailbox_list_index_sync_begin(list, &sync_ctx) < 0) return -1; mail_index_unset_fscked(sync_ctx->trans); return mailbox_list_index_sync_end(&sync_ctx, TRUE); } static void mailbox_list_index_deinit(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if (ilist->to_refresh != NULL) timeout_remove(&ilist->to_refresh); if (ilist->index != NULL) { hash_table_destroy(&ilist->mailbox_hash); hash_table_destroy(&ilist->mailbox_names); pool_unref(&ilist->mailbox_pool); if (ilist->opened) mail_index_close(ilist->index); mail_index_free(&ilist->index); } ilist->module_ctx.super.deinit(list); } static void mailbox_list_index_refresh_if_found(struct mailbox_list *list, const char *name, bool selectable) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_node *node; if (ilist->syncing) return; mailbox_list_last_error_push(list); (void)mailbox_list_index_refresh_force(list); node = mailbox_list_index_lookup(list, name); if (node != NULL && (!selectable || (node->flags & (MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_NOSELECT)) == 0)) { /* index is out of sync - refresh */ mailbox_list_index_refresh_later(list); } mailbox_list_last_error_pop(list); } static void mailbox_list_index_refresh_if_not_found(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if (ilist->syncing) return; mailbox_list_last_error_push(list); (void)mailbox_list_index_refresh_force(list); if (mailbox_list_index_lookup(list, name) == NULL) { /* index is out of sync - refresh */ mailbox_list_index_refresh_later(list); } mailbox_list_last_error_pop(list); } static int mailbox_list_index_open_mailbox(struct mailbox *box) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if (ibox->module_ctx.super.open(box) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) mailbox_list_index_refresh_if_found(box->list, box->name, TRUE); return -1; } return 0; } static int mailbox_list_index_create_mailbox(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if (ibox->module_ctx.super.create_box(box, update, directory) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) mailbox_list_index_refresh_if_not_found(box->list, box->name); return -1; } mailbox_list_index_refresh_later(box->list); return 0; } static int mailbox_list_index_update_mailbox(struct mailbox *box, const struct mailbox_update *update) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); if (ibox->module_ctx.super.update_box(box, update) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) mailbox_list_index_refresh_if_found(box->list, box->name, TRUE); return -1; } mailbox_list_index_update_mailbox_index(box, update); return 0; } static int mailbox_list_index_delete_mailbox(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if (ilist->module_ctx.super.delete_mailbox(list, name) < 0) { if (mailbox_list_get_last_mail_error(list) == MAIL_ERROR_NOTFOUND) mailbox_list_index_refresh_if_found(list, name, FALSE); return -1; } mailbox_list_index_refresh_later(list); return 0; } static int mailbox_list_index_delete_dir(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if (ilist->module_ctx.super.delete_dir(list, name) < 0) { if (mailbox_list_get_last_mail_error(list) == MAIL_ERROR_NOTFOUND) mailbox_list_index_refresh_if_found(list, name, FALSE); return -1; } mailbox_list_index_refresh_later(list); return 0; } static int mailbox_list_index_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct mailbox_list_index *oldilist = INDEX_LIST_CONTEXT(oldlist); if (oldilist->module_ctx.super.rename_mailbox(oldlist, oldname, newlist, newname) < 0) { if (mailbox_list_get_last_mail_error(oldlist) == MAIL_ERROR_NOTFOUND) mailbox_list_index_refresh_if_found(oldlist, oldname, FALSE); if (mailbox_list_get_last_mail_error(newlist) == MAIL_ERROR_EXISTS) mailbox_list_index_refresh_if_not_found(newlist, newname); return -1; } mailbox_list_index_refresh_later(oldlist); if (oldlist != newlist) mailbox_list_index_refresh_later(newlist); return 0; } static int mailbox_list_index_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(_list); struct mail_index_view *view; struct mail_index_transaction *trans; const void *data; size_t size; uint32_t counter; if (ilist->module_ctx.super.set_subscribed(_list, name, set) < 0) return -1; /* update the "subscriptions changed" counter/timestamp. its purpose is to trigger NOTIFY watcher to handle SubscriptionChange events */ if (mailbox_list_index_index_open(_list) < 0) return -1; view = mail_index_view_open(ilist->index); mail_index_get_header_ext(view, ilist->subs_hdr_ext_id, &data, &size); if (size != sizeof(counter)) counter = ioloop_time; else { memcpy(&counter, data, size); if (++counter < (uint32_t)ioloop_time) counter = ioloop_time; } trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header_ext(trans, ilist->subs_hdr_ext_id, 0, &counter, sizeof(counter)); (void)mail_index_transaction_commit(&trans); mail_index_view_close(&view); return 0; } static bool mailbox_list_index_is_enabled(struct mailbox_list *list) { if (!list->mail_set->mailbox_list_index) return FALSE; if (strcmp(list->name, MAILBOX_LIST_NAME_NONE) == 0) return FALSE; i_assert(list->set.list_index_fname != NULL); if (list->set.list_index_fname[0] == '\0') return FALSE; return TRUE; } static void mailbox_list_index_created(struct mailbox_list *list) { struct mailbox_list_vfuncs *v = list->vlast; struct mailbox_list_index *ilist; bool has_backing_store; /* layout=index doesn't have any backing store */ has_backing_store = strcmp(list->name, MAILBOX_LIST_NAME_INDEX) != 0; if (!mailbox_list_index_is_enabled(list)) { /* reserve the module context anyway, so syncing code knows that the index is disabled */ i_assert(has_backing_store); ilist = NULL; MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist); return; } ilist = p_new(list->pool, struct mailbox_list_index, 1); ilist->module_ctx.super = *v; list->vlast = &ilist->module_ctx.super; ilist->has_backing_store = has_backing_store; ilist->pending_init = TRUE; v->deinit = mailbox_list_index_deinit; v->iter_init = mailbox_list_index_iter_init; v->iter_deinit = mailbox_list_index_iter_deinit; v->iter_next = mailbox_list_index_iter_next; v->delete_mailbox = mailbox_list_index_delete_mailbox; v->delete_dir = mailbox_list_index_delete_dir; v->rename_mailbox = mailbox_list_index_rename_mailbox; v->set_subscribed = mailbox_list_index_set_subscribed; v->notify_init = mailbox_list_index_notify_init; v->notify_next = mailbox_list_index_notify_next; v->notify_deinit = mailbox_list_index_notify_deinit; v->notify_wait = mailbox_list_index_notify_wait; v->notify_flush = mailbox_list_index_notify_flush; MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist); if ((list->flags & MAILBOX_LIST_FLAG_SECONDARY) != 0) { /* secondary lists aren't accessible via namespaces, so we need to finish them now. */ mailbox_list_index_init_finish(list); } } static void mailbox_list_index_init_finish(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const char *dir; if (ilist == NULL || !ilist->pending_init) return; ilist->pending_init = FALSE; /* we've delayed this part of the initialization so that mbox format can override the index root directory path */ if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_LIST_INDEX, &dir)) { /* in-memory indexes */ dir = NULL; } i_assert(ilist->has_backing_store || dir != NULL); i_assert(list->set.list_index_fname != NULL); ilist->path = dir == NULL ? "(in-memory mailbox list index)" : p_strdup_printf(list->pool, "%s/%s", dir, list->set.list_index_fname); ilist->index = mail_index_alloc(dir, list->set.list_index_fname); ilist->ext_id = mail_index_ext_register(ilist->index, "list", sizeof(struct mailbox_list_index_header), sizeof(struct mailbox_list_index_record), sizeof(uint32_t)); ilist->subs_hdr_ext_id = mail_index_ext_register(ilist->index, "subs", sizeof(uint32_t), 0, sizeof(uint32_t)); mailbox_list_index_init_pool(ilist); mailbox_list_index_status_init_finish(list); } static void mailbox_list_index_namespaces_added(struct mail_namespace *namespaces) { struct mail_namespace *ns; for (ns = namespaces; ns != NULL; ns = ns->next) mailbox_list_index_init_finish(ns->list); } static void mailbox_list_index_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct index_list_mailbox *ibox; if (ilist == NULL) return; ibox = p_new(box->pool, struct index_list_mailbox, 1); ibox->module_ctx.super = *v; box->vlast = &ibox->module_ctx.super; MODULE_CONTEXT_SET(box, index_list_storage_module, ibox); /* for layout=index these get overridden */ v->open = mailbox_list_index_open_mailbox; v->create_box = mailbox_list_index_create_mailbox; v->update_box = mailbox_list_index_update_mailbox; mailbox_list_index_status_init_mailbox(v); mailbox_list_index_backend_init_mailbox(box, v); } static struct mail_storage_hooks mailbox_list_index_hooks = { .mailbox_list_created = mailbox_list_index_created, .mail_namespaces_added = mailbox_list_index_namespaces_added, .mailbox_allocated = mailbox_list_index_mailbox_allocated }; void mailbox_list_index_init(void); /* called in mailbox-list-register.c */ void mailbox_list_index_init(void) { mail_storage_hooks_add_internal(&mailbox_list_index_hooks); } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-fs.c0000644000175000017500000003755113147010712017464 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "mkdir-parents.h" #include "mailbox-log.h" #include "subscription-file.h" #include "mail-storage.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-delete.h" #include "mailbox-list-fs.h" #include #include #include #define GLOBAL_TEMP_PREFIX ".temp." extern struct mailbox_list fs_mailbox_list; static struct mailbox_list *fs_list_alloc(void) { struct fs_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("fs list", 2048); list = p_new(pool, struct fs_mailbox_list, 1); list->list = fs_mailbox_list; list->list.pool = pool; list->temp_prefix = p_strconcat(pool, GLOBAL_TEMP_PREFIX, my_hostname, ".", my_pid, ".", NULL); return &list->list; } static void fs_list_deinit(struct mailbox_list *_list) { struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; pool_unref(&list->list.pool); } static char fs_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED) { return '/'; } static const char * fs_list_get_path_to(const struct mailbox_list_settings *set, const char *root_dir, const char *name) { if (*set->maildir_name != '\0' && set->index_control_use_maildir_name) { return t_strdup_printf("%s/%s%s/%s", root_dir, set->mailbox_dir_name, name, set->maildir_name); } else { return t_strdup_printf("%s/%s%s", root_dir, set->mailbox_dir_name, name); } } static int fs_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type, const char **path_r) { const struct mailbox_list_settings *set = &_list->set; const char *root_dir, *error; if (name == NULL) { /* return root directories */ return mailbox_list_set_get_root_path(set, type, path_r) ? 1 : 0; } i_assert(mailbox_list_is_valid_name(_list, name, &error)); if (mailbox_list_try_get_absolute_path(_list, &name)) { if (type == MAILBOX_LIST_PATH_TYPE_INDEX && *set->index_dir == '\0') return 0; *path_r = name; return 1; } root_dir = set->root_dir; switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: if (*set->maildir_name != '\0') { *path_r = t_strdup_printf("%s/%s%s", set->root_dir, set->mailbox_dir_name, name); return 1; } break; case MAILBOX_LIST_PATH_TYPE_ALT_DIR: if (set->alt_dir == NULL) return 0; if (*set->maildir_name != '\0') { /* maildir_name is for the mailbox, caller is asking for the directory name */ *path_r = t_strdup_printf("%s/%s%s", set->alt_dir, set->mailbox_dir_name, name); return 1; } root_dir = set->alt_dir; break; case MAILBOX_LIST_PATH_TYPE_MAILBOX: break; case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: if (set->alt_dir == NULL) return 0; root_dir = set->alt_dir; break; case MAILBOX_LIST_PATH_TYPE_CONTROL: if (set->control_dir != NULL) { *path_r = fs_list_get_path_to(set, set->control_dir, name); return 1; } break; case MAILBOX_LIST_PATH_TYPE_INDEX: if (set->index_dir != NULL) { if (*set->index_dir == '\0') return 0; *path_r = fs_list_get_path_to(set, set->index_dir, name); return 1; } break; case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE: if (set->index_pvt_dir == NULL) return 0; *path_r = fs_list_get_path_to(set, set->index_pvt_dir, name); return 1; case MAILBOX_LIST_PATH_TYPE_LIST_INDEX: i_unreached(); } if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR || type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) { /* don't use inbox_path */ } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) { /* If INBOX is a file, index and control directories are located in root directory. */ if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 || type == MAILBOX_LIST_PATH_TYPE_MAILBOX || type == MAILBOX_LIST_PATH_TYPE_DIR) { *path_r = set->inbox_path; return 1; } } if (root_dir == NULL) return 0; if (*set->maildir_name == '\0') { *path_r = t_strdup_printf("%s/%s%s", root_dir, set->mailbox_dir_name, name); } else { *path_r = t_strdup_printf("%s/%s%s/%s", root_dir, set->mailbox_dir_name, name, set->maildir_name); } return 1; } static const char * fs_list_get_temp_prefix(struct mailbox_list *_list, bool global) { struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; return global ? GLOBAL_TEMP_PREFIX : list->temp_prefix; } static const char * fs_list_join_refpattern(struct mailbox_list *_list ATTR_UNUSED, const char *ref, const char *pattern) { if (*pattern == '/' || *pattern == '~') { /* pattern overrides reference */ } else if (*ref != '\0') { /* merge reference and pattern */ pattern = t_strconcat(ref, pattern, NULL); } return pattern; } static int fs_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; enum mailbox_list_path_type type; const char *path; if (_list->set.subscription_fname == NULL) { mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE, "Subscriptions not supported"); return -1; } type = _list->set.control_dir != NULL ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; path = t_strconcat(mailbox_list_get_root_forced(_list, type), "/", _list->set.subscription_fname, NULL); return subsfile_set_subscribed(_list, path, list->temp_prefix, name, set); } static const char *mailbox_list_fs_get_trash_dir(struct mailbox_list *list) { const char *root_dir; root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR); return t_strdup_printf("%s/"MAILBOX_LIST_FS_TRASH_DIR_NAME, root_dir); } static int fs_list_delete_maildir(struct mailbox_list *list, const char *name) { const char *path, *trash_dir; bool rmdir_path; int ret; if (*list->set.maildir_name != '\0' && *list->set.mailbox_dir_name != '\0') { trash_dir = mailbox_list_fs_get_trash_dir(list); ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir); if (ret < 0) return -1; if (ret > 0) { /* try to delete the parent directory */ if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0) i_unreached(); if (rmdir(path) < 0 && errno != ENOENT && errno != ENOTEMPTY && errno != EEXIST) { mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); } return 0; } } rmdir_path = *list->set.maildir_name != '\0'; if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0) i_unreached(); return mailbox_list_delete_mailbox_nonrecursive(list, name, path, rmdir_path); } static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name) { const char *path; int ret; ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) return -1; i_assert(ret > 0); if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { ret = mailbox_list_delete_mailbox_file(list, name, path); } else { ret = fs_list_delete_maildir(list, name); } if (ret == 0 && list->set.no_noselect) mailbox_list_delete_until_root(list, path, MAILBOX_LIST_PATH_TYPE_MAILBOX); i_assert(ret <= 0); return mailbox_list_delete_finish_ret(list, name, ret == 0); } static int fs_list_rmdir(struct mailbox_list *list, const char *name, const char *path) { guid_128_t dir_sha128; if (rmdir(path) < 0) return -1; mailbox_name_get_sha128(name, dir_sha128); mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR, dir_sha128); return 0; } static int fs_list_delete_dir(struct mailbox_list *list, const char *name) { const char *path, *child_name, *child_path, *p; char sep; int ret; if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0) i_unreached(); ret = fs_list_rmdir(list, name, path); if (!list->set.iter_from_index_dir) { /* it should exist only in the mail directory */ if (ret == 0) return 0; } else if (ret == 0 || errno == ENOENT) { /* the primary list location is the index directory, but it exists in both index and mail directories. */ if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0) i_unreached(); if (fs_list_rmdir(list, name, path) == 0) return 0; if (ret == 0 && errno == ENOENT) { /* partial existence: exists in _DIR, but not in _INDEX. return success anyway. */ return 0; } /* a) both directories didn't exist b) index directory couldn't be rmdir()ed for some reason */ } if (errno == ENOENT || errno == ENOTDIR) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); } else if (errno == ENOTEMPTY || errno == EEXIST) { /* mbox workaround: if only .imap/ directory is preventing the deletion, remove it */ sep = mailbox_list_get_hierarchy_sep(list); child_name = t_strdup_printf("%s%cchild", name, sep); if (mailbox_list_get_path(list, child_name, MAILBOX_LIST_PATH_TYPE_INDEX, &child_path) > 0 && strncmp(path, child_path, strlen(path)) == 0) { /* drop the "/child" part out. */ p = strrchr(child_path, '/'); if (rmdir(t_strdup_until(child_path, p)) == 0) { /* try again */ if (fs_list_rmdir(list, name, path) == 0) return 0; } } mailbox_list_set_error(list, MAIL_ERROR_EXISTS, "Mailbox has children, delete them first"); } else { mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); } return -1; } static int rename_dir(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, enum mailbox_list_path_type type, bool rmdir_parent) { struct stat st; const char *oldpath, *newpath, *p, *oldparent, *newparent; if (mailbox_list_get_path(oldlist, oldname, type, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, type, &newpath) <= 0) return 0; if (strcmp(oldpath, newpath) == 0) return 0; p = strrchr(oldpath, '/'); oldparent = p == NULL ? "/" : t_strdup_until(oldpath, p); p = strrchr(newpath, '/'); newparent = p == NULL ? "/" : t_strdup_until(newpath, p); if (strcmp(oldparent, newparent) != 0 && stat(oldpath, &st) == 0) { /* make sure the newparent exists */ struct mailbox_permissions perm; mailbox_list_get_root_permissions(newlist, &perm); if (mkdir_parents_chgrp(newparent, perm.dir_create_mode, perm.file_create_gid, perm.file_create_gid_origin) < 0 && errno != EEXIST) { if (mailbox_list_set_error_from_errno(oldlist)) return -1; mailbox_list_set_critical(oldlist, "mkdir_parents(%s) failed: %m", newparent); return -1; } } if (rename(oldpath, newpath) < 0 && errno != ENOENT) { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); return -1; } if (rmdir_parent && (p = strrchr(oldpath, '/')) != NULL) { oldpath = t_strdup_until(oldpath, p); if (rmdir(oldpath) < 0 && errno != ENOENT && errno != ENOTEMPTY && errno != EEXIST) { mailbox_list_set_critical(oldlist, "rmdir(%s) failed: %m", oldpath); } } /* avoid leaving empty directories lying around */ mailbox_list_delete_until_root(oldlist, oldpath, type); return 0; } static int fs_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct mail_storage *oldstorage; const char *oldvname, *oldpath, *newpath, *alt_newpath, *root_path, *p; struct stat st; struct mailbox_permissions old_perm, new_perm; bool rmdir_parent = FALSE; oldvname = mailbox_list_get_vname(oldlist, oldname); if (mailbox_list_get_storage(&oldlist, oldvname, &oldstorage) < 0) return -1; if (mailbox_list_get_path(oldlist, oldname, MAILBOX_LIST_PATH_TYPE_DIR, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_DIR, &newpath) <= 0) i_unreached(); if (mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_ALT_DIR, &alt_newpath) < 0) i_unreached(); root_path = mailbox_list_get_root_forced(oldlist, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(oldpath, root_path) == 0) { /* most likely INBOX */ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf("Renaming %s isn't supported.", oldname)); return -1; } mailbox_list_get_permissions(oldlist, oldname, &old_perm); mailbox_list_get_permissions(newlist, newname, &new_perm); /* if we're renaming under another mailbox, require its permissions to be same as ours. */ if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL && (new_perm.file_create_mode != old_perm.file_create_mode || new_perm.dir_create_mode != old_perm.dir_create_mode || new_perm.file_create_gid != old_perm.file_create_gid)) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across conflicting " "directory permissions"); return -1; } /* create the hierarchy */ p = strrchr(newpath, '/'); if (p != NULL) { p = t_strdup_until(newpath, p); if (mkdir_parents_chgrp(p, new_perm.dir_create_mode, new_perm.file_create_gid, new_perm.file_create_gid_origin) < 0 && errno != EEXIST) { if (mailbox_list_set_error_from_errno(oldlist)) return -1; mailbox_list_set_critical(oldlist, "mkdir_parents(%s) failed: %m", p); return -1; } } /* first check that the destination mailbox doesn't exist. this is racy, but we need to be atomic and there's hardly any possibility that someone actually tries to rename two mailboxes to same new one */ if (lstat(newpath, &st) == 0) { mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS, "Target mailbox already exists"); return -1; } else if (errno == ENOTDIR) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Target mailbox doesn't allow inferior mailboxes"); return -1; } else if (errno != ENOENT && errno != EACCES) { mailbox_list_set_critical(oldlist, "lstat(%s) failed: %m", newpath); return -1; } if (alt_newpath != NULL) { if (stat(alt_newpath, &st) == 0) { /* race condition or a directory left there lying around? safest to just report error. */ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS, "Target mailbox already exists"); return -1; } else if (errno != ENOENT) { mailbox_list_set_critical(oldlist, "stat(%s) failed: %m", alt_newpath); return -1; } } if (rename(oldpath, newpath) < 0) { if (ENOTFOUND(errno)) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(oldlist, oldname)); } else if (!mailbox_list_set_error_from_errno(oldlist)) { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); } return -1; } if (alt_newpath != NULL) { (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_ALT_DIR, rmdir_parent); } (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_CONTROL, rmdir_parent); (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_INDEX, rmdir_parent); return 0; } struct mailbox_list fs_mailbox_list = { .name = MAILBOX_LIST_NAME_FS, .props = 0, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = fs_list_alloc, .deinit = fs_list_deinit, .get_hierarchy_sep = fs_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = fs_list_get_path, .get_temp_prefix = fs_list_get_temp_prefix, .join_refpattern = fs_list_join_refpattern, .iter_init = fs_list_iter_init, .iter_next = fs_list_iter_next, .iter_deinit = fs_list_iter_deinit, .get_mailbox_flags = fs_list_get_mailbox_flags, .subscriptions_refresh = mailbox_list_subscriptions_refresh, .set_subscribed = fs_list_set_subscribed, .delete_mailbox = fs_list_delete_mailbox, .delete_dir = fs_list_delete_dir, .delete_symlink = mailbox_list_delete_symlink_default, .rename_mailbox = fs_list_rename_mailbox, } }; dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-notify-tree.c0000644000175000017500000000717113123174404021317 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-index.h" #include "mail-storage.h" #include "mailbox-list-private.h" #include "mailbox-list-index.h" #include "mailbox-list-notify-tree.h" struct mailbox_list_notify_tree { struct mailbox_list *list; struct mailbox_tree_context *mailboxes; struct mail_index_view *view; bool failed; }; static void mailbox_list_notify_node_get_status(struct mailbox_list_notify_tree *tree, struct mailbox_notify_node *nnode) { struct mailbox_status status; uint32_t seq; if (!mail_index_lookup_seq(tree->view, nnode->index_uid, &seq)) return; i_zero(&status); (void)mailbox_list_index_status(tree->list, tree->view, seq, STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN | STATUS_HIGHESTMODSEQ, &status, nnode->guid, NULL); nnode->uidvalidity = status.uidvalidity; nnode->uidnext = status.uidnext; nnode->messages = status.messages; nnode->unseen = status.unseen; nnode->highest_modseq = status.highest_modseq; } static void mailbox_list_notify_node_build(struct mailbox_list_notify_tree *tree, struct mailbox_list_index_node *index_node, string_t *path) { struct mailbox_node *node; struct mailbox_notify_node *nnode; size_t prefix_len; bool created; str_append(path, index_node->name); node = mailbox_tree_get(tree->mailboxes, str_c(path), &created); nnode = (struct mailbox_notify_node *)node; nnode->index_uid = index_node->uid; if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) node->flags = MAILBOX_NONEXISTENT; else if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) node->flags = MAILBOX_NOSELECT; else { node->flags = 0; mailbox_list_notify_node_get_status(tree, nnode); } if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0) node->flags |= MAILBOX_NOINFERIORS; if (index_node->children != NULL) { str_append_c(path, mailbox_list_get_hierarchy_sep(tree->list)); prefix_len = str_len(path); index_node = index_node->children; for (; index_node != NULL; index_node = index_node->next) { str_truncate(path, prefix_len); mailbox_list_notify_node_build(tree, index_node, path); } } } static void mailbox_list_notify_tree_build(struct mailbox_list_notify_tree *tree) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(tree->list); struct mailbox_list_index_node *index_node; string_t *path = t_str_new(128); if (mailbox_list_index_refresh(tree->list) < 0) tree->failed = TRUE; tree->view = mail_index_view_open(ilist->index); index_node = ilist->mailbox_tree; for (; index_node != NULL; index_node = index_node->next) { str_truncate(path, 0); mailbox_list_notify_node_build(tree, index_node, path); } mail_index_view_close(&tree->view); } struct mailbox_list_notify_tree * mailbox_list_notify_tree_init(struct mailbox_list *list) { struct mailbox_list_notify_tree *tree; tree = i_new(struct mailbox_list_notify_tree, 1); tree->list = list; tree->mailboxes = mailbox_tree_init_size(mailbox_list_get_hierarchy_sep(list), sizeof(struct mailbox_notify_node)); mailbox_list_notify_tree_build(tree); return tree; } void mailbox_list_notify_tree_deinit(struct mailbox_list_notify_tree **_tree) { struct mailbox_list_notify_tree *tree = *_tree; *_tree = NULL; mailbox_tree_deinit(&tree->mailboxes); i_free(tree); } struct mailbox_notify_node * mailbox_list_notify_tree_lookup(struct mailbox_list_notify_tree *tree, const char *storage_name) { struct mailbox_node *node; node = mailbox_tree_lookup(tree->mailboxes, storage_name); return (struct mailbox_notify_node *)node; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-subscriptions.h0000644000175000017500000000241113123174404021756 00000000000000#ifndef MAILBOX_LIST_SUBSCRIPTIONS_H #define MAILBOX_LIST_SUBSCRIPTIONS_H #include "mailbox-list-iter.h" struct mailbox_tree_context; struct mailbox_list_iterate_context; int mailbox_list_subscriptions_refresh(struct mailbox_list *src_list, struct mailbox_list *dest_list); /* Set MAILBOX_SUBSCRIBED and MAILBOX_CHILD_SUBSCRIBED flags, clearing them if they already are there when they shouldn't. */ void mailbox_list_set_subscription_flags(struct mailbox_list *list, const char *vname, enum mailbox_info_flags *flags); /* Add subscriptions matching the iteration to the given tree */ void mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, struct mailbox_tree_context *tree, bool default_nonexistent); /* Iterate through subscriptions, call mailbox_list.get_mailbox_flags() if necessary for mailboxes to get their flags. */ struct mailbox_list_iterate_context * mailbox_list_subscriptions_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags); const struct mailbox_info * mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *ctx); int mailbox_list_subscriptions_iter_deinit(struct mailbox_list_iterate_context *ctx); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-iter-private.h0000644000175000017500000000224713147010712021466 00000000000000#ifndef MAILBOX_LIST_ITER_PRIVATE_H #define MAILBOX_LIST_ITER_PRIVATE_H #include "mailbox-list-private.h" #include "mailbox-list-iter.h" #include "mailbox-list-delete.h" struct autocreate_box { const char *name; const struct mailbox_settings *set; enum mailbox_info_flags flags; bool child_listed; }; ARRAY_DEFINE_TYPE(mailbox_settings, struct mailbox_settings *); struct mailbox_list_autocreate_iterate_context { unsigned int idx; struct mailbox_info new_info; ARRAY(struct autocreate_box) boxes; ARRAY_TYPE(mailbox_settings) box_sets; ARRAY_TYPE(mailbox_settings) all_ns_box_sets; HASH_TABLE(char *, char *) duplicate_vnames; bool listing_autoboxes:1; }; static inline bool mailbox_list_iter_try_delete_noselect(struct mailbox_list_iterate_context *ctx, const struct mailbox_info *info, const char *storage_name) { if ((info->flags & (MAILBOX_NOSELECT|MAILBOX_NOCHILDREN)) == (MAILBOX_NOSELECT|MAILBOX_NOCHILDREN) && ctx->list->set.no_noselect) { /* Try to rmdir() all \NoSelect mailbox leafs and afterwards their parents. */ mailbox_list_delete_mailbox_until_root(ctx->list, storage_name); return TRUE; } return FALSE; } #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-delete.c0000644000175000017500000003227713165463624020334 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hex-binary.h" #include "hostpid.h" #include "randgen.h" #include "unlink-directory.h" #include "mailbox-list-private.h" #include "mailbox-list-delete.h" #include #include #include static int mailbox_list_check_root_delete(struct mailbox_list *list, const char *name, const char *path) { const char *root_dir; root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR); if (strcmp(root_dir, path) != 0) return 0; if (strcmp(name, "INBOX") == 0 && (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "INBOX can't be deleted."); return -1; } mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Mail storage root can't be deleted."); return -1; } static const char *unique_fname(void) { unsigned char randbuf[8]; random_fill_weak(randbuf, sizeof(randbuf)); return t_strdup_printf("%s.%s.%s", my_hostname, my_pid, binary_to_hex(randbuf, sizeof(randbuf))); } int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list, const char *name, const char *trash_dir) { const char *src, *trash_dest; unsigned int count; if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &src) <= 0) i_unreached(); if (mailbox_list_check_root_delete(list, name, src) < 0) return -1; /* rename the mailbox dir to trash dir, which atomically marks it as being deleted. */ count = 0; trash_dest = trash_dir; for (; rename(src, trash_dest) < 0; count++) { if (ENOTFOUND(errno)) { if (trash_dest != trash_dir && count < 5) { /* either the source was just deleted or the trash dir was deleted. */ trash_dest = trash_dir; continue; } mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); return -1; } if (errno == EXDEV) { /* can't do this the fast way */ return 0; } if (!EDESTDIREXISTS(errno)) { if (mailbox_list_set_error_from_errno(list)) return -1; mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", src, trash_dest); return -1; } /* trash dir already exists. the reasons for this are: a) another process is in the middle of deleting it b) previous process crashed and didn't delete it c) NFS: mailbox was recently deleted, but some connection still has that mailbox open. the directory contains .nfs* files that can't be deleted until the mailbox is closed. Because of c) we'll first try to rename the mailbox under the trash directory and only later try to delete the entire trash directory. */ if (trash_dir == trash_dest) { trash_dest = t_strconcat(trash_dir, "/", unique_fname(), NULL); } else if (mailbox_list_delete_trash(trash_dest) < 0 && (errno != ENOTEMPTY || count >= 5)) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", trash_dest); return -1; } } if (mailbox_list_delete_trash(trash_dir) < 0 && errno != ENOTEMPTY && errno != EBUSY) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", trash_dir); /* it's already renamed to trash dir, which means it's deleted as far as the client is concerned. Report success. */ } return 1; } int mailbox_list_delete_mailbox_file(struct mailbox_list *list, const char *name, const char *path) { /* we can simply unlink() the file */ if (unlink(path) == 0) return 0; else if (ENOTFOUND(errno)) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); return -1; } else { if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "unlink(%s) failed: %m", path); } return -1; } } int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, const char *name, const char *path, bool rmdir_path) { DIR *dir; struct dirent *d; string_t *full_path; size_t dir_len; bool mailbox_dir, unlinked_something = FALSE; int ret = 0; if (mailbox_list_check_root_delete(list, name, path) < 0) return -1; dir = opendir(path); if (dir == NULL) { if (errno == ENOENT) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); } else { if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "opendir(%s) failed: %m", path); } } return -1; } full_path = t_str_new(256); str_append(full_path, path); str_append_c(full_path, '/'); dir_len = str_len(full_path); for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) { if (d->d_name[0] == '.') { /* skip . and .. */ if (d->d_name[1] == '\0') continue; if (d->d_name[1] == '.' && d->d_name[2] == '\0') continue; } mailbox_dir = list->v.is_internal_name != NULL && list->v.is_internal_name(list, d->d_name); str_truncate(full_path, dir_len); str_append(full_path, d->d_name); if (mailbox_dir) { if (mailbox_list_delete_trash(str_c(full_path)) < 0) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", str_c(full_path)); } else { unlinked_something = TRUE; } continue; } /* trying to unlink() a directory gives either EPERM or EISDIR (non-POSIX). it doesn't really work anywhere in practise, so don't bother stat()ing the file first */ if (unlink(str_c(full_path)) == 0) unlinked_something = TRUE; else if (errno != ENOENT && !UNLINK_EISDIR(errno)) { mailbox_list_set_critical(list, "unlink(%s) failed: %m", str_c(full_path)); ret = -1; } else { /* child directories still exist */ rmdir_path = FALSE; } } if (errno != 0) { mailbox_list_set_critical(list, "readdir(%s) failed: %m", path); ret = -1; } if (closedir(dir) < 0) { mailbox_list_set_critical(list, "closedir(%s) failed: %m", path); ret = -1; } if (ret < 0) return -1; if (rmdir_path) { unsigned int try_count = 0; int ret = rmdir(path); while (ret < 0 && errno == ENOTEMPTY && try_count++ < 10) { /* We didn't see any child directories, so this is either a race condition or .nfs* files were left lying around. In case it's .nfs* files, retry after waiting a bit. Hopefully all processes keeping those files open will have closed them by then. */ usleep(100000); ret = rmdir(path); } if (rmdir(path) == 0) unlinked_something = TRUE; else if (errno == ENOENT) { /* race condition with another process, which finished deleting it first. */ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); } else if (errno != ENOTEMPTY && errno != EEXIST) { mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); return -1; } } if (!unlinked_something) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Mailbox has children, can't delete it"); return -1; } return 0; } static bool mailbox_list_path_is_index(struct mailbox_list *list, enum mailbox_list_path_type type) { const char *index_root, *type_root; if (type == MAILBOX_LIST_PATH_TYPE_INDEX) return TRUE; /* e.g. CONTROL dir could point to the same INDEX dir. */ type_root = mailbox_list_get_root_forced(list, type); index_root = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_INDEX); return strcmp(type_root, index_root) == 0; } void mailbox_list_delete_until_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type) { const char *root_dir, *p; size_t len; if (list->set.iter_from_index_dir && !list->set.no_noselect && mailbox_list_path_is_index(list, type)) { /* Don't auto-rmdir parent index directories with ITERINDEX. Otherwise it'll get us into inconsistent state with a \NoSelect mailbox in the mail directory but not in index directory. */ return; } root_dir = mailbox_list_get_root_forced(list, type); if (strncmp(path, root_dir, strlen(root_dir)) != 0) { /* mbox workaround: name=child/box, root_dir=mail/.imap/, path=mail/child/.imap/box. we'll want to try to delete the .imap/ part, but no further. */ len = strlen(path); while (len > 0 && path[len-1] != '/') len--; if (len == 0) return; len--; while (len > 0 && path[len-1] != '/') len--; if (len == 0) return; root_dir = t_strndup(path, len-1); } while (strcmp(path, root_dir) != 0) { if (rmdir(path) < 0 && errno != ENOENT) { if (errno == ENOTEMPTY || errno == EEXIST) return; mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); return; } p = strrchr(path, '/'); if (p == NULL) break; path = t_strdup_until(path, p); } } void mailbox_list_delete_mailbox_until_root(struct mailbox_list *list, const char *storage_name) { enum mailbox_list_path_type types[] = { MAILBOX_LIST_PATH_TYPE_DIR, MAILBOX_LIST_PATH_TYPE_ALT_DIR, MAILBOX_LIST_PATH_TYPE_CONTROL, MAILBOX_LIST_PATH_TYPE_INDEX, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE, }; const char *path; for (unsigned int i = 0; i < N_ELEMENTS(types); i++) { if (mailbox_list_get_path(list, storage_name, types[i], &path) > 0) mailbox_list_delete_until_root(list, path, types[i]); } } static int mailbox_list_try_delete(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type) { const char *mailbox_path, *index_path, *path; int ret; if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &mailbox_path) <= 0 || mailbox_list_get_path(list, name, type, &path) <= 0 || strcmp(path, mailbox_path) == 0) return 0; if (type == MAILBOX_LIST_PATH_TYPE_CONTROL && mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &index_path) > 0 && strcmp(index_path, path) == 0) { /* CONTROL dir is the same as INDEX dir, which we already deleted. We don't want to continue especially with iter_from_index_dir=yes, because it could be deleting the index directory. */ return 0; } /* Note that only ALT currently uses maildir_name in paths. INDEX and CONTROL don't. */ if (type != MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX || *list->set.maildir_name == '\0') { /* this directory may contain also child mailboxes' data. we don't want to delete that. */ bool rmdir_path = *list->set.maildir_name != '\0'; if (mailbox_list_delete_mailbox_nonrecursive(list, name, path, rmdir_path) == 0) ret = 1; else { enum mail_error error = mailbox_list_get_last_mail_error(list); if (error != MAIL_ERROR_NOTFOUND && error != MAIL_ERROR_NOTPOSSIBLE) return -1; ret = 0; } } else { if (mailbox_list_delete_trash(path) == 0) ret = 1; else if (errno == ENOENT || errno == ENOTEMPTY) ret = 0; else { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", path); return -1; } } /* Avoid leaving empty parent directories lying around. These parent directories' existence or removal doesn't affect our return value. */ mailbox_list_delete_until_root(list, path, type); return ret; } int mailbox_list_delete_finish(struct mailbox_list *list, const char *name) { int ret, ret2; ret = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX); ret2 = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL); if (ret == 0 || ret2 < 0) ret = ret2; ret2 = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX); if (ret == 0 || ret2 < 0) ret = ret2; return ret; } int mailbox_list_delete_finish_ret(struct mailbox_list *list, const char *name, bool root_delete_success) { int ret2; if (!root_delete_success && mailbox_list_get_last_mail_error(list) != MAIL_ERROR_NOTFOUND) { /* unexpected error - preserve it */ return -1; } else if ((ret2 = mailbox_list_delete_finish(list, name)) < 0) { /* unexpected error */ return -1; } else if (ret2 > 0) { /* successfully deleted */ return 0; } else if (root_delete_success) { /* nothing deleted by us, but root was successfully deleted */ return 0; } else { /* nothing deleted by us and the root didn't exist either. make sure the list has the correct error set, since it could have been changed. */ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); return -1; } } int mailbox_list_delete_trash(const char *path) { if (unlink_directory(path, UNLINK_DIRECTORY_FLAG_RMDIR) < 0) { if (errno == ELOOP) { /* it's a symlink? try just deleting it */ if (unlink(path) == 0) return 0; errno = ELOOP; return -1; } return -1; } return 0; } int mailbox_list_delete_symlink_default(struct mailbox_list *list, const char *name) { const char *path; int ret; ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path); if (ret < 0) return -1; i_assert(ret > 0); if (unlink(path) == 0) return 0; if (errno == ENOENT) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); } else if (UNLINK_EISDIR(errno)) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Mailbox isn't a symlink"); } else { mailbox_list_set_critical(list, "unlink(%s) failed: %m", path); } return -1; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-fs-flags.c0000644000175000017500000001616613144653606020571 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mailbox-list-fs.h" #include /* Assume that if atime < mtime, there are new mails. If it's good enough for UW-IMAP, it's good enough for us. */ #define STAT_GET_MARKED_FILE(st) \ ((st).st_size == 0 ? MAILBOX_UNMARKED : \ (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED) static int list_is_maildir_mailbox(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags_r) { const char *path, *maildir_path; struct stat st, st2; bool mailbox_files; switch (type) { case MAILBOX_LIST_FILE_TYPE_FILE: case MAILBOX_LIST_FILE_TYPE_OTHER: /* non-directories aren't valid */ *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; case MAILBOX_LIST_FILE_TYPE_DIR: case MAILBOX_LIST_FILE_TYPE_UNKNOWN: case MAILBOX_LIST_FILE_TYPE_SYMLINK: break; } path = t_strdup_printf("%s/%s", dir, fname); if (stat(path, &st) < 0) { if (errno == ENOENT) { *flags_r |= MAILBOX_NONEXISTENT; return 0; } else { /* non-selectable. probably either access denied, or symlink destination not found. don't bother logging errors. */ *flags_r |= MAILBOX_NOSELECT; return 1; } } if (!S_ISDIR(st.st_mode)) { if (strncmp(fname, ".nfs", 4) == 0) { /* temporary NFS file */ *flags_r |= MAILBOX_NONEXISTENT; } else { *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; } return 0; } /* ok, we've got a directory. see what we can do about it. */ /* 1st link is "." 2nd link is ".." 3rd link is either child mailbox or mailbox dir rest of the links are child mailboxes if mailboxes are files, then 3+ links are all child mailboxes. */ mailbox_files = (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0; if (st.st_nlink == 2 && !mailbox_files) { *flags_r |= MAILBOX_NOSELECT; return 1; } /* we have at least one directory. see if this mailbox is selectable */ maildir_path = t_strconcat(path, "/", list->set.maildir_name, NULL); if (stat(maildir_path, &st2) < 0) *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; else if (!S_ISDIR(st2.st_mode)) { if (mailbox_files) { *flags_r |= st.st_nlink == 2 ? MAILBOX_NOCHILDREN : MAILBOX_CHILDREN; } else { *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; } } else { /* now we know what link count 3 means. */ if (st.st_nlink == 3) *flags_r |= MAILBOX_NOCHILDREN; else *flags_r |= MAILBOX_CHILDREN; } *flags_r |= MAILBOX_SELECT; return 1; } static bool is_inbox_file(struct mailbox_list *list, const char *path, const char *fname) { const char *inbox_path; if (strcasecmp(fname, "INBOX") != 0) return FALSE; if (mailbox_list_get_path(list, "INBOX", MAILBOX_LIST_PATH_TYPE_MAILBOX, &inbox_path) <= 0) i_unreached(); return strcmp(inbox_path, path) == 0; } int fs_list_get_mailbox_flags(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags_r) { struct stat st; const char *path; *flags_r = 0; if (*list->set.maildir_name != '\0' && !list->set.iter_from_index_dir) { /* maildir_name is set: This is the simple case that works for all mail storage formats, because the only thing that matters for existence or child checks is whether the maildir_name exists or not. For example with Maildir this doesn't care whether the "cur" directory exists; as long as the parent maildir_name exists, the Maildir is selectable. */ return list_is_maildir_mailbox(list, dir, fname, type, flags_r); } /* maildir_name is not set: Now we (may) need to use storage-specific code to determine whether the mailbox is selectable or if it has children. We're here also when iterating from index directory, because even though maildir_name is set, it's not used for index directory. */ if (!list->set.iter_from_index_dir && list->v.is_internal_name != NULL && list->v.is_internal_name(list, fname)) { /* skip internal dirs. For example Maildir's cur/new/tmp */ *flags_r |= MAILBOX_NOSELECT; return 0; } switch (type) { case MAILBOX_LIST_FILE_TYPE_DIR: /* We know that we're looking at a directory. If the storage uses files, it has to be a \NoSelect directory. */ if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { *flags_r |= MAILBOX_NOSELECT; return 1; } break; case MAILBOX_LIST_FILE_TYPE_FILE: /* We know that we're looking at a file. If the storage doesn't use files, it's not a mailbox and we want to skip it. */ if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; } break; default: break; } /* we've done all filtering we can before stat()ing */ path = t_strconcat(dir, "/", fname, NULL); if (stat(path, &st) < 0) { if (ENOTFOUND(errno)) { *flags_r |= MAILBOX_NONEXISTENT; return 0; } else if (ENOACCESS(errno)) { *flags_r |= MAILBOX_NOSELECT; return 1; } else { /* non-selectable. probably either access denied, or symlink destination not found. don't bother logging errors. */ mailbox_list_set_critical(list, "stat(%s) failed: %m", path); return -1; } } if (!S_ISDIR(st.st_mode)) { if (strncmp(fname, ".nfs", 4) == 0) { /* temporary NFS file */ *flags_r |= MAILBOX_NONEXISTENT; return 0; } if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; } /* looks like a valid mailbox file */ if (is_inbox_file(list, path, fname) && strcmp(fname, "INBOX") != 0) { /* it's possible for INBOX to have child mailboxes as long as the inbox file itself isn't in /INBOX */ } else { *flags_r |= MAILBOX_NOINFERIORS; } /* Return mailbox files as always existing. The current mailbox_exists() code would do the same stat() anyway without further checks, so might as well avoid the second stat(). */ *flags_r |= MAILBOX_SELECT; *flags_r |= STAT_GET_MARKED_FILE(st); return 1; } /* This is a directory */ if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { /* We should get here only if type is MAILBOX_LIST_FILE_TYPE_UNKNOWN because the filesystem didn't return the type. Normally this should have already been handled by the MAILBOX_LIST_FILE_TYPE_DIR check above. */ *flags_r |= MAILBOX_NOSELECT; return 1; } if (list->v.is_internal_name == NULL || list->set.iter_from_index_dir) { /* This mailbox format doesn't use any special directories (e.g. Maildir's cur/new/tmp). In that case we can look at the directory's link count to determine whether there are children or not. The directory's link count equals the number of subdirectories it has. The first two links are for "." and "..". link count < 2 can happen with filesystems that don't support link counts. we'll just ignore them for now.. */ if (st.st_nlink == 2) *flags_r |= MAILBOX_NOCHILDREN; else if (st.st_nlink > 2) *flags_r |= MAILBOX_CHILDREN; } return 1; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-fs-iter.c0000644000175000017500000006177613165463624020451 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "unichar.h" #include "imap-match.h" #include "imap-utf7.h" #include "mail-storage.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-iter-private.h" #include "mailbox-list-fs.h" #include #include #include #include struct list_dir_entry { const char *fname; enum mailbox_info_flags info_flags; }; struct list_dir_context { struct list_dir_context *parent; pool_t pool; const char *storage_name; /* this directory's info flags. */ enum mailbox_info_flags info_flags; /* all files in this directory */ ARRAY(struct list_dir_entry) entries; unsigned int entry_idx; }; struct fs_list_iterate_context { struct mailbox_list_iterate_context ctx; const char *const *valid_patterns; /* roots can be either /foo, ~user/bar or baz */ ARRAY(const char *) roots; unsigned int root_idx; char sep; pool_t info_pool; struct mailbox_info info; /* current directory we're handling */ struct list_dir_context *dir; unsigned int inbox_found:1; unsigned int inbox_has_children:1; unsigned int list_inbox_inbox:1; }; static int fs_get_existence_info_flag(struct fs_list_iterate_context *ctx, const char *vname, enum mailbox_info_flags *info_flags) { struct mailbox *box; enum mailbox_flags flags = 0; enum mailbox_existence existence; bool auto_boxes; int ret; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RAW_LIST) != 0) flags |= MAILBOX_FLAG_IGNORE_ACLS; auto_boxes = (ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0; box = mailbox_alloc(ctx->ctx.list, vname, flags); ret = mailbox_exists(box, auto_boxes, &existence); mailbox_free(&box); if (ret < 0) { /* this can only be an internal error */ mailbox_list_set_internal_error(ctx->ctx.list); return -1; } switch (existence) { case MAILBOX_EXISTENCE_NONE: /* We already found out that this mailbox exists. So this is either a race condition or ACL plugin prevented access to this. In any case treat this as a \NoSelect mailbox so that we'll recurse into its potential children. This is especially important if ACL disabled access to the parent mailbox, but child mailboxes would be accessible. */ case MAILBOX_EXISTENCE_NOSELECT: *info_flags |= MAILBOX_NOSELECT; break; case MAILBOX_EXISTENCE_SELECT: *info_flags |= MAILBOX_SELECT; break; } return 0; } static void fs_list_rename_invalid(struct fs_list_iterate_context *ctx, const char *storage_name) { /* the storage_name is completely invalid, rename it to something more sensible. we could do this for all names that aren't valid mUTF-7, but that might lead to accidents in future when UTF-8 storage names are used */ string_t *destname = t_str_new(128); string_t *dest = t_str_new(128); const char *root, *src; root = mailbox_list_get_root_forced(ctx->ctx.list, MAILBOX_LIST_PATH_TYPE_MAILBOX); src = t_strconcat(root, "/", storage_name, NULL); (void)uni_utf8_get_valid_data((const void *)storage_name, strlen(storage_name), destname); str_append(dest, root); str_append_c(dest, '/'); (void)imap_utf8_to_utf7(str_c(destname), dest); if (rename(src, str_c(dest)) < 0 && errno != ENOENT) i_error("rename(%s, %s) failed: %m", src, str_c(dest)); } static const char * dir_get_storage_name(struct list_dir_context *dir, const char *fname) { if (*dir->storage_name == '\0') { /* regular root */ return fname; } else if (strcmp(dir->storage_name, "/") == 0) { /* full_filesystem_access=yes "/" root */ return t_strconcat("/", fname, NULL); } else { /* child */ return *fname == '\0' ? dir->storage_name : t_strconcat(dir->storage_name, "/", fname, NULL); } } static int dir_entry_get(struct fs_list_iterate_context *ctx, const char *dir_path, struct list_dir_context *dir, const struct dirent *d) { const char *storage_name, *vname, *root_dir; struct list_dir_entry *entry; enum imap_match_result match; enum mailbox_info_flags info_flags; int ret; /* skip . and .. */ if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) return 0; if (strcmp(d->d_name, ctx->ctx.list->set.maildir_name) == 0) { /* mail storage's internal directory (e.g. dbox-Mails). this also means that the parent is selectable */ dir->info_flags &= ~MAILBOX_NOSELECT; dir->info_flags |= MAILBOX_SELECT; return 0; } if (ctx->ctx.list->set.subscription_fname != NULL && strcmp(d->d_name, ctx->ctx.list->set.subscription_fname) == 0) { /* if this is the subscriptions file, skip it */ root_dir = mailbox_list_get_root_forced(ctx->ctx.list, MAILBOX_LIST_PATH_TYPE_DIR); if (strcmp(root_dir, dir_path) == 0) return 0; } /* check the pattern */ storage_name = dir_get_storage_name(dir, d->d_name); vname = mailbox_list_get_vname(ctx->ctx.list, storage_name); if (!uni_utf8_str_is_valid(vname)) { fs_list_rename_invalid(ctx, storage_name); /* just skip this in this iteration, we'll see it on the next list */ return 0; } match = imap_match(ctx->ctx.glob, vname); if ((dir->info_flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS)) == 0 && (ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0) { /* we don't know yet if the parent has children. need to figure out if this file is actually a visible mailbox */ } else if (match != IMAP_MATCH_YES && (match & IMAP_MATCH_CHILDREN) == 0) { /* mailbox doesn't match any patterns, we don't care about it */ return 0; } if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { ret = mailbox_list_dirent_is_alias_symlink(ctx->ctx.list, dir_path, d); if (ret != 0) return ret < 0 ? -1 : 0; } ret = ctx->ctx.list->v. get_mailbox_flags(ctx->ctx.list, dir_path, d->d_name, mailbox_list_get_file_type(d), &info_flags); if (ret <= 0) return ret; if (!MAILBOX_INFO_FLAGS_FINISHED(info_flags)) { /* mailbox existence isn't known yet. need to figure it out the hard way. */ if (fs_get_existence_info_flag(ctx, vname, &info_flags) < 0) return -1; } if ((info_flags & MAILBOX_NONEXISTENT) != 0) return 0; /* mailbox exists - make sure parent knows it has children */ dir->info_flags &= ~(MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS); dir->info_flags |= MAILBOX_CHILDREN; if (match != IMAP_MATCH_YES && (match & IMAP_MATCH_CHILDREN) == 0) { /* this mailbox didn't actually match any pattern, we just needed to know the children state */ return 0; } /* entry matched a pattern. we're going to return this. */ entry = array_append_space(&dir->entries); entry->fname = p_strdup(dir->pool, d->d_name); entry->info_flags = info_flags; return 0; } static bool fs_list_get_storage_path(struct fs_list_iterate_context *ctx, const char *storage_name, const char **path_r) { const char *root, *path = storage_name; if (*path == '~') { if (!mailbox_list_try_get_absolute_path(ctx->ctx.list, &path)) { /* a) couldn't expand ~user/ b) mailbox is under our mail root, we changed path to storage_name */ } /* NOTE: the path may have been translated to a storage_name instead of path */ } if (*path != '/') { /* non-absolute path. add the mailbox root dir as prefix. */ enum mailbox_list_path_type type = ctx->ctx.list->set.iter_from_index_dir ? MAILBOX_LIST_PATH_TYPE_INDEX : MAILBOX_LIST_PATH_TYPE_MAILBOX; if (!mailbox_list_get_root_path(ctx->ctx.list, type, &root)) return FALSE; if (ctx->ctx.list->set.iter_from_index_dir && ctx->ctx.list->set.mailbox_dir_name[0] != '\0') { /* append "mailboxes/" to the index root */ root = t_strconcat(root, "/", ctx->ctx.list->set.mailbox_dir_name, NULL); } path = *path == '\0' ? root : t_strconcat(root, "/", path, NULL); } *path_r = path; return TRUE; } static int fs_list_dir_read(struct fs_list_iterate_context *ctx, struct list_dir_context *dir) { DIR *fsdir; struct dirent *d; const char *path; int ret = 0; if (!fs_list_get_storage_path(ctx, dir->storage_name, &path)) return 0; if (path == NULL) { /* no mailbox root dir */ return 0; } fsdir = opendir(path); if (fsdir == NULL) { if (ENOTFOUND(errno)) { /* root) user gave invalid hiearchy, ignore sub) probably just race condition with other client deleting the mailbox. */ return 0; } if (errno == EACCES) { /* ignore permission errors */ return 0; } mailbox_list_set_critical(ctx->ctx.list, "opendir(%s) failed: %m", path); return -1; } if ((dir->info_flags & (MAILBOX_SELECT | MAILBOX_NOSELECT)) == 0) { /* we don't know if the parent is selectable or not. start with the assumption that it isn't, until we see maildir_name */ dir->info_flags |= MAILBOX_NOSELECT; } errno = 0; while ((d = readdir(fsdir)) != NULL) T_BEGIN { if (dir_entry_get(ctx, path, dir, d) < 0) ret = -1; errno = 0; } T_END; if (errno != 0) { mailbox_list_set_critical(ctx->ctx.list, "readdir(%s) failed: %m", path); ret = -1; } if (closedir(fsdir) < 0) { mailbox_list_set_critical(ctx->ctx.list, "closedir(%s) failed: %m", path); ret = -1; } return ret; } static struct list_dir_context * fs_list_read_dir(struct fs_list_iterate_context *ctx, const char *storage_name, enum mailbox_info_flags info_flags) { struct list_dir_context *dir; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"fs iter dir", 256); dir = p_new(pool, struct list_dir_context, 1); dir->pool = pool; dir->storage_name = p_strdup(pool, storage_name); dir->info_flags = info_flags; p_array_init(&dir->entries, pool, 16); if (fs_list_dir_read(ctx, dir) < 0) ctx->ctx.failed = TRUE; if ((dir->info_flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS)) == 0) { /* assume this directory has no children */ dir->info_flags |= MAILBOX_NOCHILDREN; } return dir; } static bool fs_list_get_valid_patterns(struct fs_list_iterate_context *ctx, const char *const *patterns) { struct mailbox_list *_list = ctx->ctx.list; ARRAY(const char *) valid_patterns; const char *pattern, *test_pattern, *real_pattern, *error; size_t prefix_len; prefix_len = strlen(_list->ns->prefix); p_array_init(&valid_patterns, ctx->ctx.pool, 8); for (; *patterns != NULL; patterns++) { /* check that we're not trying to do any "../../" lists */ test_pattern = *patterns; /* skip namespace prefix if possible. this allows using e.g. ~/mail/ prefix and have it pass the pattern validation. */ if (strncmp(test_pattern, _list->ns->prefix, prefix_len) == 0) test_pattern += prefix_len; if (!uni_utf8_str_is_valid(test_pattern)) { /* ignore invalid UTF8 patterns */ continue; } /* check pattern also when it's converted to use real separators. */ real_pattern = mailbox_list_get_storage_name(_list, test_pattern); if (mailbox_list_is_valid_name(_list, test_pattern, &error) && mailbox_list_is_valid_name(_list, real_pattern, &error)) { pattern = p_strdup(ctx->ctx.pool, *patterns); array_append(&valid_patterns, &pattern, 1); } } array_append_zero(&valid_patterns); /* NULL-terminate */ ctx->valid_patterns = array_idx(&valid_patterns, 0); return array_count(&valid_patterns) > 1; } static void fs_list_get_roots(struct fs_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->ctx.list->ns; char ns_sep = mail_namespace_get_sep(ns); bool full_fs_access = ctx->ctx.list->mail_set->mail_full_filesystem_access; const char *const *patterns, *pattern, *const *parentp, *const *childp; const char *p, *last, *root, *prefix_vname; unsigned int i; size_t parentlen; i_assert(*ctx->valid_patterns != NULL); /* get the root dirs for all the patterns */ p_array_init(&ctx->roots, ctx->ctx.pool, 8); for (patterns = ctx->valid_patterns; *patterns != NULL; patterns++) { pattern = *patterns; if (strncmp(pattern, ns->prefix, ns->prefix_len) != 0) { /* typically e.g. prefix=foo/bar/, pattern=foo/%/% we'll use root="" for this. it might of course also be pattern=foo/%/prefix/% where we could optimize with root=prefix, but probably too much trouble to implement. */ prefix_vname = ""; } else { for (p = last = pattern; *p != '\0'; p++) { if (*p == '%' || *p == '*') break; if (*p == ns_sep) last = p; } prefix_vname = t_strdup_until(pattern, last); } if (*pattern == ns_sep && full_fs_access) { /* pattern=/something with full filesystem access. (without full filesystem access we want to skip this if namespace prefix begins with separator) */ root = "/"; } else if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && ns->prefix_len == 6 && strcasecmp(prefix_vname, "INBOX") == 0 && strncasecmp(ns->prefix, pattern, ns->prefix_len) == 0) { /* special case: Namespace prefix is INBOX/ and we just want to see its contents (not the INBOX's children). */ root = ""; } else if ((ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && ns->type == MAIL_NAMESPACE_TYPE_SHARED && !ctx->ctx.list->mail_set->mail_shared_explicit_inbox && (prefix_vname[0] == '\0' || (strncmp(ns->prefix, prefix_vname, ns->prefix_len-1) == 0 && prefix_vname[ns->prefix_len-1] == '\0'))) { /* we need to handle ns prefix explicitly here, because getting storage name with mail_shared_explicit_inbox=no would return root=INBOX. (e.g. LIST "" shared/user/box has to return the box when it doesn't exist but shared/user/box/child exists) */ root = ""; } else { root = mailbox_list_get_storage_name(ctx->ctx.list, prefix_vname); } if (*root == '/') { /* /absolute/path */ i_assert(full_fs_access); } else if (*root == '~') { /* ~user/path - don't expand the ~user/ path, since we need to be able to convert the path back to vname */ i_assert(full_fs_access); } else { /* mailbox name */ } root = p_strdup(ctx->ctx.pool, root); array_append(&ctx->roots, &root, 1); } /* sort the root dirs so that /foo is before /foo/bar */ array_sort(&ctx->roots, i_strcmp_p); /* remove /foo/bar when there already exists /foo parent */ for (i = 1; i < array_count(&ctx->roots); ) { parentp = array_idx(&ctx->roots, i-1); childp = array_idx(&ctx->roots, i); parentlen = strlen(*parentp); if (strncmp(*parentp, *childp, parentlen) == 0 && (parentlen == 0 || (*childp)[parentlen] == ctx->sep || (*childp)[parentlen] == '\0')) array_delete(&ctx->roots, i, 1); else i++; } } static void fs_list_next_root(struct fs_list_iterate_context *ctx) { const char *const *roots; unsigned int count; i_assert(ctx->dir == NULL); roots = array_get(&ctx->roots, &count); if (ctx->root_idx == count) return; ctx->dir = fs_list_read_dir(ctx, roots[ctx->root_idx], MAILBOX_NOSELECT); ctx->root_idx++; } struct mailbox_list_iterate_context * fs_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct fs_list_iterate_context *ctx; pool_t pool; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* we're listing only subscribed mailboxes. we can't optimize it, so just use the generic code. */ return mailbox_list_subscriptions_iter_init(_list, patterns, flags); } pool = pool_alloconly_create("mailbox list fs iter", 2048); ctx = p_new(pool, struct fs_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = _list; ctx->ctx.flags = flags; array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); ctx->info_pool = pool_alloconly_create("fs list", 1024); ctx->sep = mail_namespace_get_sep(_list->ns); ctx->info.ns = _list->ns; if (!fs_list_get_valid_patterns(ctx, patterns)) { /* we've only invalid patterns (or INBOX). create a glob anyway to avoid any crashes due to glob being accessed elsewhere */ ctx->ctx.glob = imap_match_init(pool, "", TRUE, ctx->sep); return &ctx->ctx; } ctx->ctx.glob = imap_match_init_multiple(pool, ctx->valid_patterns, TRUE, ctx->sep); fs_list_get_roots(ctx); fs_list_next_root(ctx); return &ctx->ctx; } int fs_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct fs_list_iterate_context *ctx = (struct fs_list_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_deinit(_ctx); while (ctx->dir != NULL) { struct list_dir_context *dir = ctx->dir; ctx->dir = dir->parent; pool_unref(&dir->pool); } if (ctx->info_pool != NULL) pool_unref(&ctx->info_pool); pool_unref(&_ctx->pool); return ret; } static void inbox_flags_set(struct fs_list_iterate_context *ctx, enum imap_match_result child_dir_match) { struct mail_namespace *ns = ctx->ctx.list->ns; /* INBOX is always selectable */ ctx->info.flags &= ~(MAILBOX_NOSELECT | MAILBOX_NONEXISTENT); if (*ns->prefix != '\0' && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* we're listing INBOX for a namespace with a prefix. if there are children for the INBOX, they're returned under the mailbox prefix, not under the INBOX itself. For example with INBOX = /var/inbox/%u/Maildir, root = ~/Maildir: ~/Maildir/INBOX/foo/ shows up as /INBOX/foo and INBOX can't directly have any children. */ if (ns->prefix_len == 6 && strncasecmp(ns->prefix, "INBOX", ns->prefix_len-1) == 0 && (ctx->info.flags & MAILBOX_CHILDREN) != 0 && (child_dir_match & IMAP_MATCH_CHILDREN) != 0) { /* except, INBOX/ prefix is once again a special case. we're now listing both the namespace prefix and the INBOX. we're now doing a LIST INBOX/%, so we'll need to create a fake \NoSelect INBOX/INBOX */ ctx->list_inbox_inbox = TRUE; } else { ctx->info.flags &= ~MAILBOX_CHILDREN; ctx->info.flags |= MAILBOX_NOINFERIORS; } } } static const char * fs_list_get_inbox_vname(struct fs_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->ctx.list->ns; if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) return "INBOX"; else return p_strconcat(ctx->info_pool, ns->prefix, "INBOX", NULL); } static bool list_file_unfound_inbox(struct fs_list_iterate_context *ctx) { ctx->info.flags = 0; ctx->info.vname = fs_list_get_inbox_vname(ctx); if (mailbox_list_mailbox(ctx->ctx.list, "INBOX", &ctx->info.flags) < 0) ctx->ctx.failed = TRUE; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) != 0 && (ctx->info.flags & MAILBOX_NONEXISTENT) != 0) return FALSE; inbox_flags_set(ctx, 0); if (ctx->inbox_has_children) ctx->info.flags |= MAILBOX_CHILDREN; else { /* we got here because we didn't see INBOX among other mailboxes, which means it has no children. */ ctx->info.flags |= MAILBOX_NOCHILDREN; } return TRUE; } static bool list_file_is_any_inbox(struct fs_list_iterate_context *ctx, const char *storage_name) { const char *path, *inbox_path; if (!fs_list_get_storage_path(ctx, storage_name, &path)) return FALSE; if (mailbox_list_get_path(ctx->ctx.list, "INBOX", MAILBOX_LIST_PATH_TYPE_DIR, &inbox_path) <= 0) i_unreached(); return strcmp(path, inbox_path) == 0; } static int fs_list_entry(struct fs_list_iterate_context *ctx, const struct list_dir_entry *entry) { struct mail_namespace *ns = ctx->ctx.list->ns; struct list_dir_context *dir, *subdir = NULL; enum imap_match_result match, child_dir_match; const char *storage_name, *vname, *child_dir_name; dir = ctx->dir; storage_name = dir_get_storage_name(dir, entry->fname); vname = mailbox_list_get_vname(ctx->ctx.list, storage_name); ctx->info.vname = p_strdup(ctx->info_pool, vname); ctx->info.flags = entry->info_flags; match = imap_match(ctx->ctx.glob, ctx->info.vname); child_dir_name = t_strdup_printf("%s%c", ctx->info.vname, ctx->sep); child_dir_match = imap_match(ctx->ctx.glob, child_dir_name); if (child_dir_match == IMAP_MATCH_YES) child_dir_match |= IMAP_MATCH_CHILDREN; if ((ctx->info.flags & (MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS)) != 0) { /* mailbox has no children */ } else if ((ctx->info.flags & MAILBOX_CHILDREN) != 0 && (child_dir_match & IMAP_MATCH_CHILDREN) == 0) { /* mailbox has children, but we don't want to list them */ } else if (((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 || (child_dir_match & IMAP_MATCH_CHILDREN) != 0) && *entry->fname != '\0') { /* a) mailbox has children and we want to return them b) we don't want to return mailbox's children, but we need to know if it has any */ subdir = fs_list_read_dir(ctx, storage_name, entry->info_flags); subdir->parent = dir; ctx->dir = subdir; /* the scanning may have updated the dir's info flags */ ctx->info.flags = subdir->info_flags; } /* handle INBOXes correctly */ if (strcasecmp(ctx->info.vname, "INBOX") == 0 && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* either this is user's INBOX, or it's a naming conflict */ if (!list_file_is_any_inbox(ctx, storage_name)) { if (subdir == NULL) { /* no children */ } else if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { if (strcmp(storage_name, "INBOX") == 0) { /* INBOX and its children are in different paths */ ctx->inbox_has_children = TRUE; } else { /* naming conflict, skip its children also */ ctx->dir = dir; pool_unref(&subdir->pool); } } else if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) { /* INBOX itself is \NoInferiors, but this INBOX is a directory, and we can make INBOX have children using it. */ ctx->inbox_has_children = TRUE; } return 0; } inbox_flags_set(ctx, child_dir_match); ctx->info.vname = "INBOX"; /* always return uppercased */ ctx->inbox_found = TRUE; } else if (strcmp(storage_name, "INBOX") == 0 && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* this is /INBOX. don't return it, unless it has children. */ i_assert(*ns->prefix != '\0'); if ((ctx->info.flags & MAILBOX_CHILDREN) == 0) return 0; /* although it could be selected with this name, it would be confusing for clients to see the same mails in both INBOX and /INBOX. */ ctx->info.flags &= ~MAILBOX_SELECT; ctx->info.flags |= MAILBOX_NOSELECT; } else if ((ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && list_file_is_any_inbox(ctx, storage_name)) { if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* probably mbox inbox file */ return 0; } /* shared/user/INBOX */ ctx->info.flags &= ~(MAILBOX_NOSELECT | MAILBOX_NONEXISTENT); ctx->info.flags |= MAILBOX_SELECT; ctx->inbox_found = TRUE; } if (match != IMAP_MATCH_YES) { /* mailbox's children may match, but the mailbox itself doesn't */ return 0; } if (mailbox_list_iter_try_delete_noselect(&ctx->ctx, &ctx->info, storage_name)) return 0; return 1; } static int fs_list_next(struct fs_list_iterate_context *ctx) { struct list_dir_context *dir; const struct list_dir_entry *entries; unsigned int count; int ret; while (ctx->dir != NULL) { /* NOTE: fs_list_entry() may change ctx->dir */ entries = array_get(&ctx->dir->entries, &count); while (ctx->dir->entry_idx < count) { p_clear(ctx->info_pool); ret = fs_list_entry(ctx, &entries[ctx->dir->entry_idx++]); if (ret > 0) return 1; if (ret < 0) ctx->ctx.failed = TRUE; entries = array_get(&ctx->dir->entries, &count); } dir = ctx->dir; ctx->dir = dir->parent; pool_unref(&dir->pool); if (ctx->dir == NULL) fs_list_next_root(ctx); } if (ctx->list_inbox_inbox) { ctx->info.flags = MAILBOX_CHILDREN | MAILBOX_NOSELECT; ctx->info.vname = p_strconcat(ctx->info_pool, ctx->ctx.list->ns->prefix, "INBOX", NULL); ctx->list_inbox_inbox = FALSE; if (imap_match(ctx->ctx.glob, ctx->info.vname) == IMAP_MATCH_YES) return 1; } if (!ctx->inbox_found && ctx->ctx.glob != NULL && (ctx->ctx.list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && imap_match(ctx->ctx.glob, fs_list_get_inbox_vname(ctx)) == IMAP_MATCH_YES) { /* INBOX wasn't seen while listing other mailboxes. It might be located elsewhere. */ ctx->inbox_found = TRUE; return list_file_unfound_inbox(ctx) ? 1 : 0; } /* finished */ return 0; } const struct mailbox_info * fs_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct fs_list_iterate_context *ctx = (struct fs_list_iterate_context *)_ctx; int ret; if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_next(_ctx); T_BEGIN { ret = fs_list_next(ctx); } T_END; if (ret == 0) return mailbox_list_iter_default_next(_ctx); else if (ret < 0) return NULL; if (_ctx->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && !_ctx->list->ns->list->mail_set->mail_shared_explicit_inbox && strlen(ctx->info.vname) < _ctx->list->ns->prefix_len) { /* shared/user INBOX, IMAP code already lists it */ return fs_list_iter_next(_ctx); } if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, ctx->info.vname, &ctx->info.flags); } i_assert(ctx->info.vname != NULL); return &ctx->info; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-sync.c0000644000175000017500000003674713165463624021161 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "str.h" #include "mail-index.h" #include "mail-storage.h" #include "mailbox-list-index-sync.h" static void node_lookup_guid(struct mailbox_list_index_sync_context *ctx, const struct mailbox_list_index_node *node, guid_128_t guid_r) { struct mailbox *box; struct mailbox_metadata metadata; const char *vname; string_t *str = t_str_new(128); char ns_sep = mailbox_list_get_hierarchy_sep(ctx->list); mailbox_list_index_node_get_path(node, ns_sep, str); vname = mailbox_list_get_vname(ctx->list, str_c(str)); box = mailbox_alloc(ctx->list, vname, 0); if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) == 0) memcpy(guid_r, metadata.guid, GUID_128_SIZE); mailbox_free(&box); } static void node_add_to_index(struct mailbox_list_index_sync_context *ctx, const struct mailbox_list_index_node *node, uint32_t *seq_r) { struct mailbox_list_index_record irec; uint32_t seq; i_zero(&irec); irec.name_id = node->name_id; if (node->parent != NULL) irec.parent_uid = node->parent->uid; /* get mailbox GUID if possible. we need to do this early in here to make mailbox rename detection work in NOTIFY */ if (ctx->syncing_list) T_BEGIN { node_lookup_guid(ctx, node, irec.guid); } T_END; mail_index_append(ctx->trans, node->uid, &seq); mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)MAILBOX_LIST_INDEX_FLAG_NONEXISTENT); mail_index_update_ext(ctx->trans, seq, ctx->ilist->ext_id, &irec, NULL); *seq_r = seq; } static struct mailbox_list_index_node * mailbox_list_index_node_add(struct mailbox_list_index_sync_context *ctx, struct mailbox_list_index_node *parent, const char *name, uint32_t *seq_r) { struct mailbox_list_index_node *node; char *dup_name; node = p_new(ctx->ilist->mailbox_pool, struct mailbox_list_index_node, 1); node->flags = MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS; /* we don't bother doing name deduplication here, even though it would be possible. */ node->name = dup_name = p_strdup(ctx->ilist->mailbox_pool, name); node->name_id = ++ctx->ilist->highest_name_id; node->uid = ctx->next_uid++; if (parent != NULL) { node->parent = parent; node->next = parent->children; parent->children = node; } else { node->next = ctx->ilist->mailbox_tree; ctx->ilist->mailbox_tree = node; } hash_table_insert(ctx->ilist->mailbox_hash, POINTER_CAST(node->uid), node); hash_table_insert(ctx->ilist->mailbox_names, POINTER_CAST(node->name_id), dup_name); node_add_to_index(ctx, node, seq_r); return node; } uint32_t mailbox_list_index_sync_name(struct mailbox_list_index_sync_context *ctx, const char *name, struct mailbox_list_index_node **node_r, bool *created_r) { const char *const *path, *empty_path[] = { "", NULL }; struct mailbox_list_index_node *node, *parent; unsigned int i; uint32_t seq = 0; path = *name == '\0' ? empty_path : t_strsplit(name, ctx->sep); /* find the last node that exists in the path */ node = ctx->ilist->mailbox_tree; parent = NULL; for (i = 0; path[i] != NULL; i++) { node = mailbox_list_index_node_find_sibling(node, path[i]); if (node == NULL) break; node->flags |= MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS; parent = node; node = node->children; } node = parent; if (path[i] == NULL) { /* the entire path exists */ i_assert(node != NULL); if (!mail_index_lookup_seq(ctx->view, node->uid, &seq)) i_panic("mailbox list index: lost uid=%u", node->uid); *created_r = FALSE; } else { /* create missing parts of the path */ for (; path[i] != NULL; i++) { node = mailbox_list_index_node_add(ctx, node, path[i], &seq); } *created_r = TRUE; } *node_r = node; return seq; } static void get_existing_name_ids(ARRAY_TYPE(uint32_t) *ids, const struct mailbox_list_index_node *node) { for (; node != NULL; node = node->next) { if (node->children != NULL) get_existing_name_ids(ids, node->children); array_append(ids, &node->name_id, 1); } } static void mailbox_list_index_sync_names(struct mailbox_list_index_sync_context *ctx) { struct mailbox_list_index *ilist = ctx->ilist; ARRAY_TYPE(uint32_t) existing_name_ids; buffer_t *hdr_buf; const void *ext_data; size_t ext_size; const char *name; const uint32_t *id_p; uint32_t prev_id = 0; /* get all existing name IDs sorted */ t_array_init(&existing_name_ids, 64); get_existing_name_ids(&existing_name_ids, ilist->mailbox_tree); array_sort(&existing_name_ids, uint32_cmp); hdr_buf = buffer_create_dynamic(pool_datastack_create(), 1024); buffer_append_zero(hdr_buf, sizeof(struct mailbox_list_index_header)); /* add existing names to header (with deduplication) */ array_foreach(&existing_name_ids, id_p) { if (*id_p != prev_id) { buffer_append(hdr_buf, id_p, sizeof(*id_p)); name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(*id_p)); i_assert(name != NULL); buffer_append(hdr_buf, name, strlen(name) + 1); prev_id = *id_p; } } buffer_append_zero(hdr_buf, sizeof(*id_p)); /* make sure header size is ok in index and update it */ mail_index_get_header_ext(ctx->view, ilist->ext_id, &ext_data, &ext_size); if (nearest_power(ext_size) != nearest_power(hdr_buf->used)) { mail_index_ext_resize(ctx->trans, ilist->ext_id, nearest_power(hdr_buf->used), sizeof(struct mailbox_list_index_record), sizeof(uint32_t)); } mail_index_update_header_ext(ctx->trans, ilist->ext_id, 0, hdr_buf->data, hdr_buf->used); } static void mailbox_list_index_node_clear_exists(struct mailbox_list_index_node *node) { while (node != NULL) { if (node->children != NULL) mailbox_list_index_node_clear_exists(node->children); node->flags &= ~MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS; node = node->next; } } static void sync_expunge_nonexistent(struct mailbox_list_index_sync_context *sync_ctx, struct mailbox_list_index_node *node) { uint32_t seq; while (node != NULL) { if (node->children != NULL) sync_expunge_nonexistent(sync_ctx, node->children); if ((node->flags & MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS) == 0) { if (mail_index_lookup_seq(sync_ctx->view, node->uid, &seq)) mail_index_expunge(sync_ctx->trans, seq); mailbox_list_index_node_unlink(sync_ctx->ilist, node); } node = node->next; } } int mailbox_list_index_sync_begin(struct mailbox_list *list, struct mailbox_list_index_sync_context **sync_ctx_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_sync_context *sync_ctx; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; const struct mail_index_header *hdr; bool fix_attempted = FALSE; i_assert(!ilist->syncing); retry: if (mailbox_list_index_index_open(list) < 0) return -1; if (mail_index_sync_begin(ilist->index, &index_sync_ctx, &view, &trans, MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES) < 0) { mailbox_list_index_set_index_error(list); return -1; } mailbox_list_index_reset(ilist); /* re-parse mailbox list now that it's refreshed and locked */ if (mailbox_list_index_parse(list, view, TRUE) < 0) { mail_index_sync_rollback(&index_sync_ctx); return -1; } if (ilist->call_corruption_callback && !fix_attempted) { /* unlock and resync the index */ mail_index_sync_rollback(&index_sync_ctx); if (mailbox_list_index_handle_corruption(list) < 0) return -1; fix_attempted = TRUE; goto retry; } sync_ctx = i_new(struct mailbox_list_index_sync_context, 1); sync_ctx->list = list; sync_ctx->ilist = ilist; sync_ctx->sep[0] = mailbox_list_get_hierarchy_sep(list); sync_ctx->orig_highest_name_id = ilist->highest_name_id; sync_ctx->index_sync_ctx = index_sync_ctx; sync_ctx->trans = trans; hdr = mail_index_get_header(view); sync_ctx->next_uid = hdr->next_uid; if (hdr->uid_validity == 0) { /* first time indexing, set uidvalidity */ uint32_t uid_validity = ioloop_time; mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } sync_ctx->view = mail_index_transaction_open_updated_view(trans); ilist->sync_ctx = sync_ctx; ilist->syncing = TRUE; *sync_ctx_r = sync_ctx; return 0; } static int mailbox_list_index_sync_list(struct mailbox_list_index_sync_context *sync_ctx) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; enum mailbox_list_index_flags flags; const char *patterns[2]; struct mailbox_list_index_node *node; uint32_t seq; bool created; /* clear EXISTS-flags, so after sync we know what can be expunged */ mailbox_list_index_node_clear_exists(sync_ctx->ilist->mailbox_tree); /* don't include autocreated mailboxes in index until they're actually created. this index may be used by multiple users, so we also want to ignore ACLs here. */ patterns[0] = "*"; patterns[1] = NULL; iter = sync_ctx->ilist->module_ctx.super. iter_init(sync_ctx->list, patterns, MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_NO_AUTO_BOXES); sync_ctx->syncing_list = TRUE; while ((info = sync_ctx->ilist->module_ctx.super.iter_next(iter)) != NULL) { flags = 0; if ((info->flags & MAILBOX_NONEXISTENT) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NONEXISTENT; if ((info->flags & MAILBOX_NOSELECT) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NOSELECT; if ((info->flags & MAILBOX_NOINFERIORS) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NOINFERIORS; T_BEGIN { const char *name = mailbox_list_get_storage_name(info->ns->list, info->vname); seq = mailbox_list_index_sync_name(sync_ctx, name, &node, &created); } T_END; node->flags = flags | MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS; mail_index_update_flags(sync_ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)flags); } sync_ctx->syncing_list = FALSE; if (sync_ctx->ilist->module_ctx.super.iter_deinit(iter) < 0) return -1; /* successfully listed everything, expunge any unseen mailboxes */ sync_expunge_nonexistent(sync_ctx, sync_ctx->ilist->mailbox_tree); return 0; } static void mailbox_list_index_sync_update_hdr(struct mailbox_list_index_sync_context *sync_ctx) { if (sync_ctx->orig_highest_name_id != sync_ctx->ilist->highest_name_id || sync_ctx->ilist->corrupted_names_or_parents) { /* new names added. this implicitly resets refresh flag */ T_BEGIN { mailbox_list_index_sync_names(sync_ctx); } T_END; sync_ctx->ilist->corrupted_names_or_parents = FALSE; } else if (mailbox_list_index_need_refresh(sync_ctx->ilist, sync_ctx->view)) { /* we're synced, reset refresh flag */ struct mailbox_list_index_header new_hdr; new_hdr.refresh_flag = 0; mail_index_update_header_ext(sync_ctx->trans, sync_ctx->ilist->ext_id, offsetof(struct mailbox_list_index_header, refresh_flag), &new_hdr.refresh_flag, sizeof(new_hdr.refresh_flag)); } } static void mailbox_list_index_sync_update_corrupted_node(struct mailbox_list_index_sync_context *sync_ctx, struct mailbox_list_index_node *node) { struct mailbox_list_index_record irec; uint32_t seq; const void *data; bool expunged; if (!mail_index_lookup_seq(sync_ctx->view, node->uid, &seq)) return; if (node->corrupted_ext) { mail_index_lookup_ext(sync_ctx->view, seq, sync_ctx->ilist->ext_id, &data, &expunged); i_assert(data != NULL); memcpy(&irec, data, sizeof(irec)); irec.name_id = node->name_id; irec.parent_uid = node->parent == NULL ? 0 : node->parent->uid; mail_index_update_ext(sync_ctx->trans, seq, sync_ctx->ilist->ext_id, &irec, NULL); node->corrupted_ext = FALSE; } if (node->corrupted_flags) { mail_index_update_flags(sync_ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)node->flags); node->corrupted_flags = FALSE; } else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME) != 0) { /* rely on lib-index to drop unnecessary updates */ mail_index_update_flags(sync_ctx->trans, seq, MODIFY_ADD, (enum mail_flags)MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME); } } static void mailbox_list_index_sync_update_corrupted_nodes(struct mailbox_list_index_sync_context *sync_ctx, struct mailbox_list_index_node *node) { for (; node != NULL; node = node->next) { mailbox_list_index_sync_update_corrupted_node(sync_ctx, node); mailbox_list_index_sync_update_corrupted_nodes(sync_ctx, node->children); } } static void mailbox_list_index_sync_update_corrupted(struct mailbox_list_index_sync_context *sync_ctx) { if (!sync_ctx->ilist->corrupted_names_or_parents) return; mailbox_list_index_sync_update_corrupted_nodes(sync_ctx, sync_ctx->ilist->mailbox_tree); } int mailbox_list_index_sync_end(struct mailbox_list_index_sync_context **_sync_ctx, bool success) { struct mailbox_list_index_sync_context *sync_ctx = *_sync_ctx; int ret; *_sync_ctx = NULL; if (success) { mailbox_list_index_sync_update_corrupted(sync_ctx); mailbox_list_index_sync_update_hdr(sync_ctx); } mail_index_view_close(&sync_ctx->view); if (success) { struct mail_index_sync_rec sync_rec; while (mail_index_sync_next(sync_ctx->index_sync_ctx, &sync_rec)) ; if ((ret = mail_index_sync_commit(&sync_ctx->index_sync_ctx)) < 0) mailbox_list_index_set_index_error(sync_ctx->list); } else { mail_index_sync_rollback(&sync_ctx->index_sync_ctx); ret = -1; } sync_ctx->ilist->syncing = FALSE; sync_ctx->ilist->sync_ctx = NULL; i_free(sync_ctx); return ret; } int mailbox_list_index_sync(struct mailbox_list *list, bool refresh) { struct mailbox_list_index_sync_context *sync_ctx; int ret = 0; if (mailbox_list_index_sync_begin(list, &sync_ctx) < 0) return -1; if (!sync_ctx->ilist->has_backing_store) { /* no backing store - we have nothing to sync to */ } else if (refresh || sync_ctx->ilist->call_corruption_callback || sync_ctx->ilist->corrupted_names_or_parents || sync_ctx->ilist->highest_name_id == 0 || !sync_ctx->list->mail_set->mailbox_list_index_very_dirty_syncs) { /* sync the index against the backing store */ ret = mailbox_list_index_sync_list(sync_ctx); } return mailbox_list_index_sync_end(&sync_ctx, ret == 0); } int mailbox_list_index_sync_delete(struct mailbox_list_index_sync_context *sync_ctx, const char *name, bool delete_selectable) { struct mailbox_list_index_record rec; struct mailbox_list_index_node *node; const void *data; bool expunged; uint32_t seq; node = mailbox_list_index_lookup(sync_ctx->list, name); if (node == NULL) { mailbox_list_set_error(sync_ctx->list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); return -1; } if (!mail_index_lookup_seq(sync_ctx->view, node->uid, &seq)) i_panic("mailbox list index: lost uid=%u", node->uid); if (delete_selectable) { /* make it at least non-selectable */ node->flags = MAILBOX_LIST_INDEX_FLAG_NOSELECT; mail_index_update_flags(sync_ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)node->flags); mail_index_lookup_ext(sync_ctx->view, seq, sync_ctx->ilist->ext_id, &data, &expunged); i_assert(data != NULL && !expunged); memcpy(&rec, data, sizeof(rec)); rec.uid_validity = 0; i_zero(&rec.guid); mail_index_update_ext(sync_ctx->trans, seq, sync_ctx->ilist->ext_id, &rec, NULL); } if (node->children != NULL) { /* can't delete this directory before its children, but we may have made it non-selectable already */ return 0; } /* we can remove the entire node */ mail_index_expunge(sync_ctx->trans, seq); mailbox_list_index_node_unlink(sync_ctx->ilist, node); return 1; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-none.c0000644000175000017500000001110113165463624020010 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "imap-match.h" #include "mailbox-list-private.h" #define GLOBAL_TEMP_PREFIX ".temp." struct noop_list_iterate_context { struct mailbox_list_iterate_context ctx; struct mailbox_info inbox_info; unsigned int list_inbox:1; }; extern struct mailbox_list none_mailbox_list; static struct mailbox_list *none_list_alloc(void) { struct mailbox_list *list; pool_t pool; pool = pool_alloconly_create("none list", 2048); list = p_new(pool, struct mailbox_list, 1); *list = none_mailbox_list; list->pool = pool; return list; } static void none_list_deinit(struct mailbox_list *list) { pool_unref(&list->pool); } static char none_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED) { return '/'; } static int none_list_get_path(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED, enum mailbox_list_path_type type ATTR_UNUSED, const char **path_r ATTR_UNUSED) { return 0; } static const char * none_list_get_temp_prefix(struct mailbox_list *list ATTR_UNUSED, bool global ATTR_UNUSED) { return GLOBAL_TEMP_PREFIX; } static int none_list_subscriptions_refresh(struct mailbox_list *src_list ATTR_UNUSED, struct mailbox_list *dest_list ATTR_UNUSED) { return 0; } static int none_list_set_subscribed(struct mailbox_list *list, const char *name ATTR_UNUSED, bool set ATTR_UNUSED) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); return -1; } static int none_list_delete_mailbox(struct mailbox_list *list, const char *name ATTR_UNUSED) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); return -1; } static int none_list_delete_dir(struct mailbox_list *list, const char *name ATTR_UNUSED) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); return -1; } static int none_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname ATTR_UNUSED, struct mailbox_list *newlist ATTR_UNUSED, const char *newname ATTR_UNUSED) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); return -1; } static struct mailbox_list_iterate_context * none_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct noop_list_iterate_context *ctx; pool_t pool; pool = pool_alloconly_create("mailbox list none iter", 1024); ctx = p_new(pool, struct noop_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = list; ctx->ctx.flags = flags; ctx->ctx.glob = imap_match_init_multiple(pool, patterns, TRUE, mail_namespace_get_sep(list->ns)); array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && imap_match(ctx->ctx.glob, "INBOX") == IMAP_MATCH_YES) { ctx->list_inbox = TRUE; ctx->inbox_info.ns = list->ns; ctx->inbox_info.vname = "INBOX"; } return &ctx->ctx; } static int none_list_iter_deinit(struct mailbox_list_iterate_context *ctx) { pool_unref(&ctx->pool); return 0; } static const struct mailbox_info * none_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct noop_list_iterate_context *ctx = (struct noop_list_iterate_context *)_ctx; if (ctx->list_inbox) { ctx->list_inbox = FALSE; return &ctx->inbox_info; } return NULL; } static int none_list_get_mailbox_flags(struct mailbox_list *list ATTR_UNUSED, const char *dir ATTR_UNUSED, const char *fname ATTR_UNUSED, enum mailbox_list_file_type type ATTR_UNUSED, enum mailbox_info_flags *flags) { *flags = MAILBOX_NONEXISTENT; return 0; } struct mailbox_list none_mailbox_list = { .name = MAILBOX_LIST_NAME_NONE, .props = MAILBOX_LIST_PROP_NO_ROOT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = none_list_alloc, .deinit = none_list_deinit, .get_hierarchy_sep = none_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = none_list_get_path, .get_temp_prefix = none_list_get_temp_prefix, .iter_init = none_list_iter_init, .iter_next = none_list_iter_next, .iter_deinit = none_list_iter_deinit, .get_mailbox_flags = none_list_get_mailbox_flags, .subscriptions_refresh = none_list_subscriptions_refresh, .set_subscribed = none_list_set_subscribed, .delete_mailbox = none_list_delete_mailbox, .delete_dir = none_list_delete_dir, .delete_symlink = none_list_delete_dir, .rename_mailbox = none_list_rename_mailbox, } }; dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-fs.h0000644000175000017500000000151313123174404017461 00000000000000#ifndef MAILBOX_LIST_FS_H #define MAILBOX_LIST_FS_H #include "mailbox-list-private.h" /* When doing deletion via renaming it first to trash directory, use this as the trash directory name */ #define MAILBOX_LIST_FS_TRASH_DIR_NAME "..DOVECOT-TrasH" struct fs_mailbox_list { struct mailbox_list list; const char *temp_prefix; }; struct mailbox_list_iterate_context * fs_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags); int fs_list_iter_deinit(struct mailbox_list_iterate_context *ctx); const struct mailbox_info * fs_list_iter_next(struct mailbox_list_iterate_context *ctx); int fs_list_get_mailbox_flags(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-notify.c0000644000175000017500000007037713165463624021512 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include "mail-storage-private.h" #include "mailbox-list-notify.h" #include "mailbox-list-notify-tree.h" #include "mailbox-list-index.h" #include #define NOTIFY_DELAY_MSECS 500 enum ilist_ext_type { ILIST_EXT_NONE, ILIST_EXT_BASE, ILIST_EXT_MSGS, ILIST_EXT_HIGHESTMODSEQ, ILIST_EXT_UNKNOWN }; struct mailbox_list_notify_rename { uint32_t old_uid, new_uid; }; struct mailbox_list_inotify_entry { uint32_t uid; guid_128_t guid; bool expunge; }; struct mailbox_list_notify_index { struct mailbox_list_notify notify; struct mailbox_tree_context *subscriptions; struct mailbox_list_notify_tree *tree; struct mail_index_view *view, *old_view; struct mail_index_view_sync_ctx *sync_ctx; enum ilist_ext_type cur_ext; uint32_t cur_ext_id; void (*wait_callback)(void *context); void *wait_context; struct io *io_wait, *io_wait_inbox; struct timeout *to_wait, *to_notify; ARRAY_TYPE(seq_range) new_uids, expunged_uids, changed_uids; ARRAY_TYPE(const_string) new_subscriptions, new_unsubscriptions; ARRAY(struct mailbox_list_notify_rename) renames; struct seq_range_iter new_uids_iter, expunged_uids_iter; struct seq_range_iter changed_uids_iter; unsigned int new_uids_n, expunged_uids_n, changed_uids_n; unsigned int rename_idx, subscription_idx, unsubscription_idx; struct mailbox_list_notify_rec notify_rec; string_t *rec_name; char *list_log_path, *inbox_log_path; struct stat list_last_st, inbox_last_st; struct mailbox *inbox; unsigned int initialized:1; unsigned int read_failed:1; unsigned int inbox_event_pending:1; }; static const enum mailbox_status_items notify_status_items = STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN | STATUS_HIGHESTMODSEQ; static enum mailbox_list_notify_event mailbox_list_index_get_changed_events(const struct mailbox_notify_node *nnode, const struct mailbox_status *status) { enum mailbox_list_notify_event events = 0; if (nnode->uidvalidity != status->uidvalidity) events |= MAILBOX_LIST_NOTIFY_UIDVALIDITY; if (nnode->uidnext != status->uidnext) events |= MAILBOX_LIST_NOTIFY_APPENDS; if (nnode->messages > status->messages) { /* NOTE: not entirely reliable, since there could be both expunges and appends.. but it shouldn't make any difference in practise, since anybody interested in expunges is most likely also interested in appends. */ events |= MAILBOX_LIST_NOTIFY_EXPUNGES; } if (nnode->unseen != status->unseen) events |= MAILBOX_LIST_NOTIFY_SEEN_CHANGES; if (nnode->highest_modseq < status->highest_modseq) events |= MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES; return events; } static void mailbox_notify_node_update_status(struct mailbox_notify_node *nnode, struct mailbox_status *status) { nnode->uidvalidity = status->uidvalidity; nnode->uidnext = status->uidnext; nnode->messages = status->messages; nnode->unseen = status->unseen; nnode->highest_modseq = status->highest_modseq; } static void mailbox_list_index_notify_init_inbox(struct mailbox_list_notify_index *inotify) { inotify->inbox = mailbox_alloc(inotify->notify.list, "INBOX", MAILBOX_FLAG_READONLY); if (mailbox_open(inotify->inbox) < 0) mailbox_free(&inotify->inbox); inotify->inbox_log_path = i_strconcat(inotify->inbox->index->filepath, ".log", NULL); } int mailbox_list_index_notify_init(struct mailbox_list *list, enum mailbox_list_notify_event mask, struct mailbox_list_notify **notify_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_notify_index *inotify; const char *index_dir; if (ilist == NULL) { /* can't do this without mailbox list indexes */ return -1; } (void)mailbox_list_index_refresh(list); inotify = i_new(struct mailbox_list_notify_index, 1); inotify->notify.list = list; inotify->notify.mask = mask; inotify->view = mail_index_view_open(ilist->index); inotify->old_view = mail_index_view_dup_private(inotify->view); inotify->tree = mailbox_list_notify_tree_init(list); i_array_init(&inotify->new_uids, 8); i_array_init(&inotify->expunged_uids, 8); i_array_init(&inotify->changed_uids, 16); i_array_init(&inotify->renames, 16); i_array_init(&inotify->new_subscriptions, 16); i_array_init(&inotify->new_unsubscriptions, 16); inotify->rec_name = str_new(default_pool, 64); if ((mask & (MAILBOX_LIST_NOTIFY_SUBSCRIBE | MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0) { (void)mailbox_list_iter_subscriptions_refresh(list); mailbox_tree_sort(list->subscriptions); inotify->subscriptions = mailbox_tree_dup(list->subscriptions); } inotify->list_log_path = i_strdup(ilist->index->log->filepath); if (list->mail_set->mailbox_list_index_include_inbox) { /* INBOX can be handled also using mailbox list index */ } else if ((list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) == 0) { /* no INBOX in this namespace */ } else if ((mask & MAILBOX_LIST_NOTIFY_STATUS) == 0) { /* not interested in mailbox changes */ } else if (mailbox_list_get_path(list, "INBOX", MAILBOX_LIST_PATH_TYPE_INDEX, &index_dir) <= 0) { /* no indexes for INBOX? can't handle it */ } else { mailbox_list_index_notify_init_inbox(inotify); } *notify_r = &inotify->notify; return 1; } void mailbox_list_index_notify_deinit(struct mailbox_list_notify *notify) { struct mailbox_list_notify_index *inotify = (struct mailbox_list_notify_index *)notify; bool b; if (inotify->inbox != NULL) mailbox_free(&inotify->inbox); if (inotify->subscriptions != NULL) mailbox_tree_deinit(&inotify->subscriptions); if (inotify->io_wait != NULL) io_remove(&inotify->io_wait); if (inotify->io_wait_inbox != NULL) io_remove(&inotify->io_wait_inbox); if (inotify->to_wait != NULL) timeout_remove(&inotify->to_wait); if (inotify->to_notify != NULL) timeout_remove(&inotify->to_notify); if (inotify->sync_ctx != NULL) (void)mail_index_view_sync_commit(&inotify->sync_ctx, &b); mail_index_view_close(&inotify->view); mail_index_view_close(&inotify->old_view); mailbox_list_notify_tree_deinit(&inotify->tree); array_free(&inotify->new_subscriptions); array_free(&inotify->new_unsubscriptions); array_free(&inotify->new_uids); array_free(&inotify->expunged_uids); array_free(&inotify->changed_uids); array_free(&inotify->renames); str_free(&inotify->rec_name); i_free(inotify->list_log_path); i_free(inotify->inbox_log_path); i_free(inotify); } static struct mailbox_list_index_node * notify_lookup_guid(struct mailbox_list_notify_index *inotify, struct mail_index_view *view, uint32_t uid, enum mailbox_status_items items, struct mailbox_status *status_r, guid_128_t guid_r) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(inotify->notify.list); struct mailbox_list_index_node *index_node; uint32_t seq; if (!mail_index_lookup_seq(view, uid, &seq)) return NULL; index_node = mailbox_list_index_lookup_uid(ilist, uid); if (index_node == NULL) { /* re-parse the index list using the given view. we could be jumping here between old and new view. */ (void)mailbox_list_index_parse(inotify->notify.list, view, FALSE); index_node = mailbox_list_index_lookup_uid(ilist, uid); if (index_node == NULL) return NULL; } /* get GUID */ i_zero(status_r); memset(guid_r, 0, GUID_128_SIZE); (void)mailbox_list_index_status(inotify->notify.list, view, seq, items, status_r, guid_r, NULL); return index_node; } static void notify_update_stat(struct mailbox_list_notify_index *inotify) { bool call = FALSE; if (stat(inotify->list_log_path, &inotify->list_last_st) < 0 && errno != ENOENT) { i_error("stat(%s) failed: %m", inotify->list_log_path); call = TRUE; } if (inotify->inbox_log_path != NULL) { if (stat(inotify->inbox_log_path, &inotify->inbox_last_st) < 0 && errno != ENOENT) { i_error("stat(%s) failed: %m", inotify->inbox_log_path); call = TRUE; } } if (call) mailbox_list_index_notify_wait(&inotify->notify, NULL, NULL); } static void mailbox_list_index_notify_sync_init(struct mailbox_list_notify_index *inotify) { struct mail_index_view_sync_rec sync_rec; notify_update_stat(inotify); (void)mail_index_refresh(inotify->view->index); /* sync the view so that map extensions gets updated */ inotify->sync_ctx = mail_index_view_sync_begin(inotify->view, 0); mail_transaction_log_view_mark(inotify->view->log_view); while (mail_index_view_sync_next(inotify->sync_ctx, &sync_rec)) ; mail_transaction_log_view_rewind(inotify->view->log_view); inotify->cur_ext = ILIST_EXT_NONE; inotify->cur_ext_id = (uint32_t)-1; } static bool notify_ext_rec(struct mailbox_list_notify_index *inotify, uint32_t uid) { struct mailbox_list_notify *notify = &inotify->notify; switch (inotify->cur_ext) { case ILIST_EXT_NONE: i_unreached(); case ILIST_EXT_BASE: /* UIDVALIDITY changed */ if ((notify->mask & MAILBOX_LIST_NOTIFY_UIDVALIDITY) == 0) return FALSE; break; case ILIST_EXT_MSGS: /* APPEND, EXPUNGE, \Seen or \Recent flag change */ if ((notify->mask & MAILBOX_LIST_NOTIFY_STATUS) == 0) return FALSE; break; case ILIST_EXT_HIGHESTMODSEQ: /* when this doesn't come with EXT_MSGS update, it can only be a flag change or an explicit modseq change */ if ((notify->mask & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) == 0) return FALSE; break; case ILIST_EXT_UNKNOWN: return FALSE; } seq_range_array_add(&inotify->changed_uids, uid); return TRUE; } static int mailbox_list_index_notify_read_next(struct mailbox_list_notify_index *inotify) { struct mailbox_list_notify *notify = &inotify->notify; struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(notify->list); const struct mail_transaction_header *hdr; const void *data; int ret; ret = mail_transaction_log_view_next(inotify->view->log_view, &hdr, &data); if (ret <= 0) return ret; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* all mailbox index updates are external */ return 1; } switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_APPEND: { /* mailbox added or renamed */ const struct mail_index_record *rec, *end; if ((notify->mask & (MAILBOX_LIST_NOTIFY_CREATE | MAILBOX_LIST_NOTIFY_RENAME)) == 0) break; end = CONST_PTR_OFFSET(data, hdr->size); for (rec = data; rec != end; rec++) seq_range_array_add(&inotify->new_uids, rec->uid); break; } case MAIL_TRANSACTION_EXPUNGE_GUID: { /* mailbox deleted or renamed */ const struct mail_transaction_expunge_guid *rec, *end; if ((notify->mask & (MAILBOX_LIST_NOTIFY_DELETE | MAILBOX_LIST_NOTIFY_RENAME)) == 0) break; end = CONST_PTR_OFFSET(data, hdr->size); for (rec = data; rec != end; rec++) seq_range_array_add(&inotify->expunged_uids, rec->uid); break; } case MAIL_TRANSACTION_EXT_INTRO: { struct mail_index_map *map = inotify->view->map; const struct mail_transaction_ext_intro *rec = data; const struct mail_index_ext *ext = NULL; const char *name; uint32_t ext_map_idx; if (!array_is_created(&map->extensions)) break; /* we want to know what extension the future ext-rec-updates are changing. we're assuming here that there is only one ext-intro record before those, which is true at least for now. */ if (rec->ext_id != (uint32_t)-1 && rec->ext_id < array_count(&map->extensions)) { /* get extension by id */ ext = array_idx(&map->extensions, rec->ext_id); } else if (rec->name_size > 0) { /* by name */ name = t_strndup(rec+1, rec->name_size); if (mail_index_map_lookup_ext(map, name, &ext_map_idx)) ext = array_idx(&map->extensions, ext_map_idx); } if (ext != NULL) { if (ext->index_idx == ilist->ext_id) inotify->cur_ext = ILIST_EXT_BASE; else if (ext->index_idx == ilist->msgs_ext_id) inotify->cur_ext = ILIST_EXT_MSGS; else if (ext->index_idx == ilist->hmodseq_ext_id) inotify->cur_ext = ILIST_EXT_HIGHESTMODSEQ; else inotify->cur_ext = ILIST_EXT_UNKNOWN; inotify->cur_ext_id = ext->index_idx; } break; } case MAIL_TRANSACTION_EXT_REC_UPDATE: { const struct mail_index_registered_ext *ext; const struct mail_transaction_ext_rec_update *rec; unsigned int i, record_size; if (inotify->cur_ext == ILIST_EXT_NONE) { i_error("%s: Missing ext-intro for ext-rec-update", ilist->index->filepath); break; } /* the record is padded to 32bits in the transaction log */ ext = array_idx(&inotify->view->index->extensions, inotify->cur_ext_id); record_size = (sizeof(*rec) + ext->record_size + 3) & ~3; for (i = 0; i < hdr->size; i += record_size) { rec = CONST_PTR_OFFSET(data, i); if (i + record_size > hdr->size) break; if (!notify_ext_rec(inotify, rec->uid)) break; } break; } } return 1; } static int mailbox_list_inotify_entry_guid_cmp(const struct mailbox_list_inotify_entry *r1, const struct mailbox_list_inotify_entry *r2) { int ret; ret = memcmp(r1->guid, r2->guid, sizeof(r1->guid)); if (ret != 0) return ret; if (r1->expunge == r2->expunge) { /* this really shouldn't happen */ return 0; } return r1->expunge ? -1 : 1; } static void mailbox_list_index_notify_find_renames(struct mailbox_list_notify_index *inotify) { ARRAY(struct mailbox_list_inotify_entry) entries; struct mailbox_status status; struct mailbox_list_notify_rename *rename; struct mailbox_list_inotify_entry *entry; const struct mailbox_list_inotify_entry *e; unsigned int i, count; guid_128_t guid; uint32_t uid; /* first get all of the added and expunged GUIDs */ t_array_init(&entries, array_count(&inotify->new_uids) + array_count(&inotify->expunged_uids)); while (seq_range_array_iter_nth(&inotify->expunged_uids_iter, inotify->expunged_uids_n++, &uid)) { if (notify_lookup_guid(inotify, inotify->old_view, uid, 0, &status, guid) != NULL && !guid_128_is_empty(guid)) { entry = array_append_space(&entries); entry->uid = uid; entry->expunge = TRUE; memcpy(entry->guid, guid, sizeof(entry->guid)); } } (void)mailbox_list_index_parse(inotify->notify.list, inotify->view, TRUE); while (seq_range_array_iter_nth(&inotify->new_uids_iter, inotify->new_uids_n++, &uid)) { if (notify_lookup_guid(inotify, inotify->view, uid, 0, &status, guid) != NULL && !guid_128_is_empty(guid)) { entry = array_append_space(&entries); entry->uid = uid; memcpy(entry->guid, guid, sizeof(entry->guid)); } } /* now sort the entries by GUID and find those that have been both added and expunged */ array_sort(&entries, mailbox_list_inotify_entry_guid_cmp); e = array_get(&entries, &count); for (i = 1; i < count; i++) { if (e[i-1].expunge && !e[i].expunge && memcmp(e[i-1].guid, e[i].guid, sizeof(e[i].guid)) == 0) { rename = array_append_space(&inotify->renames); rename->old_uid = e[i-1].uid; rename->new_uid = e[i].uid; seq_range_array_remove(&inotify->expunged_uids, rename->old_uid); seq_range_array_remove(&inotify->new_uids, rename->new_uid); } } } static void mailbox_list_index_notify_find_subscribes(struct mailbox_list_notify_index *inotify) { struct mailbox_tree_iterate_context *old_iter, *new_iter; struct mailbox_tree_context *old_tree, *new_tree; const char *old_path = NULL, *new_path = NULL; pool_t pool; int ret; if (mailbox_list_iter_subscriptions_refresh(inotify->notify.list) < 0) return; mailbox_tree_sort(inotify->notify.list->subscriptions); old_tree = inotify->subscriptions; new_tree = mailbox_tree_dup(inotify->notify.list->subscriptions); old_iter = mailbox_tree_iterate_init(old_tree, NULL, MAILBOX_SUBSCRIBED); new_iter = mailbox_tree_iterate_init(new_tree, NULL, MAILBOX_SUBSCRIBED); pool = mailbox_tree_get_pool(new_tree); for (;;) { if (old_path == NULL) { if (mailbox_tree_iterate_next(old_iter, &old_path) == NULL) old_path = NULL; } if (new_path == NULL) { if (mailbox_tree_iterate_next(new_iter, &new_path) == NULL) new_path = NULL; } if (old_path == NULL) { if (new_path == NULL) break; ret = 1; } else if (new_path == NULL) ret = -1; else { ret = strcmp(old_path, new_path); } if (ret == 0) { old_path = NULL; new_path = NULL; } else if (ret > 0) { new_path = p_strdup(pool, new_path); array_append(&inotify->new_subscriptions, &new_path, 1); new_path = NULL; } else { old_path = p_strdup(pool, old_path); array_append(&inotify->new_unsubscriptions, &old_path, 1); old_path = NULL; } } mailbox_tree_iterate_deinit(&old_iter); mailbox_tree_iterate_deinit(&new_iter); mailbox_tree_deinit(&inotify->subscriptions); inotify->subscriptions = new_tree; } static void mailbox_list_index_notify_reset_iters(struct mailbox_list_notify_index *inotify) { seq_range_array_iter_init(&inotify->new_uids_iter, &inotify->new_uids); seq_range_array_iter_init(&inotify->expunged_uids_iter, &inotify->expunged_uids); seq_range_array_iter_init(&inotify->changed_uids_iter, &inotify->changed_uids); inotify->changed_uids_n = 0; inotify->new_uids_n = 0; inotify->expunged_uids_n = 0; inotify->rename_idx = 0; inotify->subscription_idx = 0; inotify->unsubscription_idx = 0; } static void mailbox_list_index_notify_read_init(struct mailbox_list_notify_index *inotify) { bool b; int ret; mailbox_list_index_notify_sync_init(inotify); /* read all changes from .log file */ while ((ret = mailbox_list_index_notify_read_next(inotify)) > 0) ; inotify->read_failed = ret < 0; (void)mail_index_view_sync_commit(&inotify->sync_ctx, &b); /* remove changes for already deleted mailboxes */ seq_range_array_remove_seq_range(&inotify->new_uids, &inotify->expunged_uids); seq_range_array_remove_seq_range(&inotify->changed_uids, &inotify->expunged_uids); mailbox_list_index_notify_reset_iters(inotify); if (array_count(&inotify->new_uids) > 0 && array_count(&inotify->expunged_uids) > 0) { mailbox_list_index_notify_find_renames(inotify); mailbox_list_index_notify_reset_iters(inotify); } if (inotify->subscriptions != NULL) mailbox_list_index_notify_find_subscribes(inotify); inotify->initialized = TRUE; } static void mailbox_list_index_notify_read_deinit(struct mailbox_list_notify_index *inotify) { /* save the old view so we can look up expunged records */ mail_index_view_close(&inotify->old_view); inotify->old_view = mail_index_view_dup_private(inotify->view); array_clear(&inotify->new_subscriptions); array_clear(&inotify->new_unsubscriptions); array_clear(&inotify->new_uids); array_clear(&inotify->expunged_uids); array_clear(&inotify->changed_uids); array_clear(&inotify->renames); inotify->initialized = FALSE; } static bool mailbox_list_index_notify_lookup(struct mailbox_list_notify_index *inotify, struct mail_index_view *view, uint32_t uid, enum mailbox_status_items items, struct mailbox_status *status_r, struct mailbox_list_notify_rec **rec_r) { struct mailbox_list_notify_rec *rec = &inotify->notify_rec; struct mailbox_list_index_node *index_node; const char *storage_name; char ns_sep = mailbox_list_get_hierarchy_sep(inotify->notify.list); i_zero(rec); index_node = notify_lookup_guid(inotify, view, uid, items, status_r, rec->guid); if (index_node == NULL) return FALSE; /* get storage_name */ str_truncate(inotify->rec_name, 0); mailbox_list_index_node_get_path(index_node, ns_sep, inotify->rec_name); storage_name = str_c(inotify->rec_name); rec->storage_name = storage_name; rec->vname = mailbox_list_get_vname(inotify->notify.list, rec->storage_name); *rec_r = rec; return TRUE; } static bool mailbox_list_index_notify_rename(struct mailbox_list_notify_index *inotify, unsigned int idx) { const struct mailbox_list_notify_rename *rename; struct mailbox_list_notify_rec *rec; struct mailbox_status status; const char *old_vname; rename = array_idx(&inotify->renames, idx); /* lookup the old name */ if (!mailbox_list_index_notify_lookup(inotify, inotify->old_view, rename->old_uid, 0, &status, &rec)) return FALSE; old_vname = t_strdup(rec->vname); /* return using the new name */ if (!mailbox_list_index_notify_lookup(inotify, inotify->view, rename->new_uid, 0, &status, &rec)) return FALSE; rec->old_vname = old_vname; rec->events = MAILBOX_LIST_NOTIFY_RENAME; return TRUE; } static bool mailbox_list_index_notify_subscribe(struct mailbox_list_notify_index *inotify, unsigned int idx) { struct mailbox_list_notify_rec *rec = &inotify->notify_rec; const char *const *vnamep; i_zero(rec); vnamep = array_idx(&inotify->new_subscriptions, idx); rec->vname = *vnamep; rec->storage_name = mailbox_list_get_storage_name(inotify->notify.list, rec->vname); rec->events = MAILBOX_LIST_NOTIFY_SUBSCRIBE; return TRUE; } static bool mailbox_list_index_notify_unsubscribe(struct mailbox_list_notify_index *inotify, unsigned int idx) { struct mailbox_list_notify_rec *rec = &inotify->notify_rec; const char *const *vnamep; i_zero(rec); vnamep = array_idx(&inotify->new_unsubscriptions, idx); rec->vname = *vnamep; rec->storage_name = mailbox_list_get_storage_name(inotify->notify.list, rec->vname); rec->events = MAILBOX_LIST_NOTIFY_UNSUBSCRIBE; return TRUE; } static bool mailbox_list_index_notify_expunge(struct mailbox_list_notify_index *inotify, uint32_t uid) { struct mailbox_list_notify_rec *rec; struct mailbox_status status; if (!mailbox_list_index_notify_lookup(inotify, inotify->old_view, uid, 0, &status, &rec)) return FALSE; rec->events = MAILBOX_LIST_NOTIFY_DELETE; return TRUE; } static bool mailbox_list_index_notify_new(struct mailbox_list_notify_index *inotify, uint32_t uid) { struct mailbox_list_notify_rec *rec; struct mailbox_status status; if (!mailbox_list_index_notify_lookup(inotify, inotify->view, uid, 0, &status, &rec)) i_unreached(); rec->events = MAILBOX_LIST_NOTIFY_CREATE; return TRUE; } static bool mailbox_list_index_notify_change(struct mailbox_list_notify_index *inotify, uint32_t uid) { struct mailbox_list_notify_rec *rec; struct mailbox_notify_node *nnode, empty_node; struct mailbox_status status; if (!mailbox_list_index_notify_lookup(inotify, inotify->view, uid, notify_status_items, &status, &rec)) { /* Mailbox is already deleted. We won't get here if we're tracking MAILBOX_LIST_NOTIFY_DELETE or _RENAME (which update expunged_uids). */ return FALSE; } /* get the old status */ nnode = mailbox_list_notify_tree_lookup(inotify->tree, rec->storage_name); if (nnode == NULL) { /* mailbox didn't exist earlier - report all events as new */ i_zero(&empty_node); nnode = &empty_node; } rec->events |= mailbox_list_index_get_changed_events(nnode, &status); /* update internal state */ mailbox_notify_node_update_status(nnode, &status); return rec->events != 0; } static bool mailbox_list_index_notify_try_next(struct mailbox_list_notify_index *inotify) { uint32_t uid; /* first show mailbox deletes */ if (seq_range_array_iter_nth(&inotify->expunged_uids_iter, inotify->expunged_uids_n++, &uid)) return mailbox_list_index_notify_expunge(inotify, uid); /* mailbox renames */ if (inotify->rename_idx < array_count(&inotify->renames)) { return mailbox_list_index_notify_rename(inotify, inotify->rename_idx++); } /* next mailbox creates */ if (seq_range_array_iter_nth(&inotify->new_uids_iter, inotify->new_uids_n++, &uid)) return mailbox_list_index_notify_new(inotify, uid); /* subscribes */ if (inotify->subscription_idx < array_count(&inotify->new_subscriptions)) { return mailbox_list_index_notify_subscribe(inotify, inotify->subscription_idx++); } if (inotify->unsubscription_idx < array_count(&inotify->new_unsubscriptions)) { return mailbox_list_index_notify_unsubscribe(inotify, inotify->unsubscription_idx++); } /* STATUS updates */ while (seq_range_array_iter_nth(&inotify->changed_uids_iter, inotify->changed_uids_n++, &uid)) { if (mailbox_list_index_notify_change(inotify, uid)) return TRUE; } return FALSE; } static enum mailbox_list_notify_event mailbox_list_notify_inbox_get_events(struct mailbox_list_notify_index *inotify) { struct mailbox_status old_status, new_status; struct mailbox_notify_node old_nnode; mailbox_get_open_status(inotify->inbox, notify_status_items, &old_status); if (mailbox_sync(inotify->inbox, MAILBOX_SYNC_FLAG_FAST) < 0) { i_error("Mailbox list index notify: Failed to sync INBOX: %s", mailbox_get_last_internal_error(inotify->inbox, NULL)); return 0; } mailbox_get_open_status(inotify->inbox, notify_status_items, &new_status); mailbox_notify_node_update_status(&old_nnode, &old_status); return mailbox_list_index_get_changed_events(&old_nnode, &new_status); } int mailbox_list_index_notify_next(struct mailbox_list_notify *notify, const struct mailbox_list_notify_rec **rec_r) { struct mailbox_list_notify_index *inotify = (struct mailbox_list_notify_index *)notify; if (!inotify->initialized) mailbox_list_index_notify_read_init(inotify); if (mailbox_list_index_handle_corruption(notify->list) < 0) return -1; while (mailbox_list_index_notify_try_next(inotify)) { if ((inotify->notify_rec.events & inotify->notify.mask) != 0) { *rec_r = &inotify->notify_rec; return 1; } else { /* caller doesn't care about this change */ } } if (inotify->inbox_event_pending) { inotify->inbox_event_pending = FALSE; i_zero(&inotify->notify_rec); inotify->notify_rec.vname = "INBOX"; inotify->notify_rec.storage_name = "INBOX"; inotify->notify_rec.events = mailbox_list_notify_inbox_get_events(inotify); *rec_r = &inotify->notify_rec; return 1; } mailbox_list_index_notify_read_deinit(inotify); return inotify->read_failed ? -1 : 0; } static void notify_now_callback(struct mailbox_list_notify_index *inotify) { timeout_remove(&inotify->to_notify); inotify->wait_callback(inotify->wait_context); } static void notify_callback(struct mailbox_list_notify_index *inotify) { #define INOTIFY_ST_CHANGED(last_st, prev_st) \ ((last_st).st_mtime != (prev_st).st_mtime || \ ST_MTIME_NSEC(last_st) != ST_MTIME_NSEC(prev_st) || \ (last_st).st_size != (prev_st).st_size || \ (last_st).st_ino != (prev_st).st_ino) struct stat list_prev_st = inotify->list_last_st; struct stat inbox_prev_st = inotify->inbox_last_st; notify_update_stat(inotify); if (INOTIFY_ST_CHANGED(inotify->inbox_last_st, inbox_prev_st)) inotify->inbox_event_pending = TRUE; if (inotify->inbox_event_pending || INOTIFY_ST_CHANGED(inotify->list_last_st, list_prev_st)) { /* log has changed. call the callback with a small delay to allow bundling multiple changes together */ if (inotify->to_notify != NULL) { /* already doing this */ return; } inotify->to_notify = timeout_add_short(NOTIFY_DELAY_MSECS, notify_now_callback, inotify); } } void mailbox_list_index_notify_wait(struct mailbox_list_notify *notify, void (*callback)(void *context), void *context) { struct mailbox_list_notify_index *inotify = (struct mailbox_list_notify_index *)notify; unsigned int check_interval; inotify->wait_callback = callback; inotify->wait_context = context; if (callback == NULL) { if (inotify->io_wait != NULL) io_remove(&inotify->io_wait); if (inotify->io_wait_inbox != NULL) io_remove(&inotify->io_wait_inbox); if (inotify->to_wait != NULL) timeout_remove(&inotify->to_wait); if (inotify->to_notify != NULL) timeout_remove(&inotify->to_notify); } else if (inotify->to_wait == NULL) { (void)io_add_notify(inotify->list_log_path, notify_callback, inotify, &inotify->io_wait); /* we need to check for INBOX explicitly, because INBOX changes don't get added to mailbox.list.index.log */ if (inotify->inbox_log_path != NULL) { (void)io_add_notify(inotify->inbox_log_path, notify_callback, inotify, &inotify->io_wait_inbox); } /* check with timeout as well, in case io_add_notify() doesn't work (e.g. NFS) */ check_interval = notify->list->mail_set->mailbox_idle_check_interval; i_assert(check_interval > 0); inotify->to_wait = timeout_add(check_interval * 1000, notify_callback, inotify); notify_update_stat(inotify); } } void mailbox_list_index_notify_flush(struct mailbox_list_notify *notify) { struct mailbox_list_notify_index *inotify = (struct mailbox_list_notify_index *)notify; if (inotify->to_notify != NULL) notify_now_callback(inotify); } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-backend.c0000644000175000017500000006314613165463624021565 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "mail-index.h" #include "subscription-file.h" #include "mailbox-list-delete.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-index-storage.h" #include "mailbox-list-index-sync.h" #include #define GLOBAL_TEMP_PREFIX ".temp." struct index_mailbox_list { struct mailbox_list list; const char *temp_prefix; const char *create_mailbox_name; guid_128_t create_mailbox_guid; }; extern struct mailbox_list index_mailbox_list; static int index_list_rename_mailbox(struct mailbox_list *_oldlist, const char *oldname, struct mailbox_list *_newlist, const char *newname); static struct mailbox_list *index_list_alloc(void) { struct index_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("index list", 2048); list = p_new(pool, struct index_mailbox_list, 1); list->list = index_mailbox_list; list->list.pool = pool; list->temp_prefix = p_strconcat(pool, GLOBAL_TEMP_PREFIX, my_hostname, ".", my_pid, ".", NULL); return &list->list; } static int index_list_init(struct mailbox_list *_list, const char **error_r) { const char *dir; if (!_list->mail_set->mailbox_list_index) { *error_r = "LAYOUT=index requires mailbox_list_index=yes"; return -1; } if (mailbox_list_get_root_path(_list, MAILBOX_LIST_PATH_TYPE_INDEX, &dir) && mailbox_list_mkdir_root(_list, dir, MAILBOX_LIST_PATH_TYPE_INDEX) < 0) { *error_r = t_strdup_printf("Failed to create the index root directory: %s", mailbox_list_get_last_internal_error(_list, NULL)); return -1; } return 0; } static void index_list_deinit(struct mailbox_list *_list) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; pool_unref(&list->list.pool); } static char index_list_get_hierarchy_sep(struct mailbox_list *list) { return *list->ns->set->separator != '\0' ? *list->ns->set->separator : MAILBOX_LIST_INDEX_HIERARHCY_SEP; } static int index_list_get_refreshed_node_seq(struct index_mailbox_list *list, struct mail_index_view *view, const char *name, struct mailbox_list_index_node **node_r, uint32_t *seq_r) { unsigned int i; *node_r = NULL; *seq_r = 0; for (i = 0; i < 2; i++) { *node_r = mailbox_list_index_lookup(&list->list, name); if (*node_r == NULL) return 0; if (mail_index_lookup_seq(view, (*node_r)->uid, seq_r)) return 1; /* mailbox was just expunged. refreshing should notice it. */ if (mailbox_list_index_refresh_force(&list->list) < 0) return -1; } i_panic("mailbox list index: refreshing doesn't lose expunged uid=%u", (*node_r)->uid); return -1; } static const char * index_get_guid_path(struct mailbox_list *_list, const char *root_dir, const guid_128_t mailbox_guid) { return t_strdup_printf("%s/%s%s", root_dir, _list->set.mailbox_dir_name, guid_128_to_string(mailbox_guid)); } static int index_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type, const char **path_r) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(_list); struct mail_index_view *view; struct mailbox_list_index_node *node; struct mailbox_status status; guid_128_t mailbox_guid; const char *root_dir; uint32_t seq; int ret; if (name == NULL) { /* return root directories */ return mailbox_list_set_get_root_path(&_list->set, type, path_r) ? 1 : 0; } /* consistently use mailbox_dir_name as part of all mailbox directories (index/control/etc) */ switch (type) { case MAILBOX_LIST_PATH_TYPE_MAILBOX: type = MAILBOX_LIST_PATH_TYPE_DIR; break; case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: type = MAILBOX_LIST_PATH_TYPE_ALT_DIR; break; case MAILBOX_LIST_PATH_TYPE_LIST_INDEX: i_unreached(); default: break; } if (!mailbox_list_set_get_root_path(&_list->set, type, &root_dir)) return 0; if (list->create_mailbox_name != NULL && strcmp(list->create_mailbox_name, name) == 0) { *path_r = index_get_guid_path(_list, root_dir, list->create_mailbox_guid); return 1; } if (ilist->sync_ctx != NULL) { /* we could get here during sync from index_list_mailbox_create_selectable() */ view = ilist->sync_ctx->view; node = mailbox_list_index_lookup(&list->list, name); if (node == NULL) { seq = 0; ret = 0; } else if (mail_index_lookup_seq(view, node->uid, &seq)) { ret = 1; } else { i_panic("mailbox list index: lost uid=%u", node->uid); } } else { if (mailbox_list_index_refresh(&list->list) < 0) return -1; view = mail_index_view_open(ilist->index); ret = index_list_get_refreshed_node_seq(list, view, name, &node, &seq); if (ret < 0) { mail_index_view_close(&view); return -1; } } i_assert(ret == 0 || seq != 0); if (ret == 0) { mailbox_list_set_error(_list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); ret = -1; } else if (!mailbox_list_index_status(_list, view, seq, 0, &status, mailbox_guid, NULL) || guid_128_is_empty(mailbox_guid)) { mailbox_list_set_error(_list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); ret = -1; } else { *path_r = index_get_guid_path(_list, root_dir, mailbox_guid); ret = 1; } if (ilist->sync_ctx == NULL) mail_index_view_close(&view); return ret; } static const char * index_list_get_temp_prefix(struct mailbox_list *_list, bool global) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; return global ? GLOBAL_TEMP_PREFIX : list->temp_prefix; } static int index_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; const char *path; if (_list->set.subscription_fname == NULL) { mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE, "Subscriptions not supported"); return -1; } path = t_strconcat(_list->set.control_dir != NULL ? _list->set.control_dir : _list->set.root_dir, "/", _list->set.subscription_fname, NULL); return subsfile_set_subscribed(_list, path, list->temp_prefix, name, set); } static int index_list_node_exists(struct index_mailbox_list *list, const char *name, enum mailbox_existence *existence_r) { struct mailbox_list_index_node *node; *existence_r = MAILBOX_EXISTENCE_NONE; if (mailbox_list_index_refresh(&list->list) < 0) return -1; node = mailbox_list_index_lookup(&list->list, name); if (node == NULL) return 0; if ((node->flags & (MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_NOSELECT)) == 0) { /* selectable */ *existence_r = MAILBOX_EXISTENCE_SELECT; } else { /* non-selectable */ *existence_r = MAILBOX_EXISTENCE_NOSELECT; } return 0; } static int index_list_mailbox_create_dir(struct index_mailbox_list *list, const char *name) { struct mailbox_list_index_sync_context *sync_ctx; struct mailbox_list_index_node *node; uint32_t seq; bool created; int ret; if (mailbox_list_index_sync_begin(&list->list, &sync_ctx) < 0) return -1; seq = mailbox_list_index_sync_name(sync_ctx, name, &node, &created); if (created || (node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) { /* didn't already exist */ node->flags = MAILBOX_LIST_INDEX_FLAG_NOSELECT; mail_index_update_flags(sync_ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)node->flags); ret = 1; } else { /* already existed */ ret = 0; } if (mailbox_list_index_sync_end(&sync_ctx, TRUE) < 0) ret = -1; return ret; } static int index_list_mailbox_create_selectable(struct mailbox *box, const guid_128_t mailbox_guid) { struct index_mailbox_list *list = (struct index_mailbox_list *)box->list; struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_list_index_sync_context *sync_ctx; struct mailbox_list_index_record rec; struct mailbox_list_index_node *node; const void *data; bool expunged, created; uint32_t seq; if (mailbox_list_index_sync_begin(&list->list, &sync_ctx) < 0) return -1; seq = mailbox_list_index_sync_name(sync_ctx, box->name, &node, &created); if (box->corrupted_mailbox_name) { /* an existing mailbox is being created with a "unknown" name. opening the mailbox will hopefully find its real name and rename it. */ node->flags |= MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME; mail_index_update_flags(sync_ctx->trans, seq, MODIFY_ADD, (enum mail_flags)MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME); } if (!created && (node->flags & (MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_NOSELECT)) == 0) { /* already selectable */ (void)mailbox_list_index_sync_end(&sync_ctx, TRUE); return 0; } mail_index_lookup_ext(sync_ctx->view, seq, ilist->ext_id, &data, &expunged); i_assert(data != NULL && !expunged); memcpy(&rec, data, sizeof(rec)); i_assert(guid_128_is_empty(rec.guid)); /* make it selectable */ node->flags &= ~(MAILBOX_LIST_INDEX_FLAG_NONEXISTENT | MAILBOX_LIST_INDEX_FLAG_NOSELECT | MAILBOX_LIST_INDEX_FLAG_NOINFERIORS); mail_index_update_flags(sync_ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)node->flags); memcpy(rec.guid, mailbox_guid, sizeof(rec.guid)); mail_index_update_ext(sync_ctx->trans, seq, ilist->ext_id, &rec, NULL); if (mailbox_list_index_sync_end(&sync_ctx, TRUE) < 0) { /* make sure we forget any changes done internally */ mailbox_list_index_reset(ilist); return -1; } return 1; } static int index_list_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); struct index_mailbox_list *list = (struct index_mailbox_list *)box->list; struct mailbox_update new_update; enum mailbox_existence existence; int ret; /* first do a quick check that it doesn't exist */ if ((ret = index_list_node_exists(list, box->name, &existence)) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if (existence == MAILBOX_EXISTENCE_NONE && directory) { /* now add the directory to index locked */ if ((ret = index_list_mailbox_create_dir(list, box->name)) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } } else if (existence != MAILBOX_EXISTENCE_SELECT && !directory) { /* if no GUID is requested, generate it ourself. set UIDVALIDITY to index sometimes later. */ if (update == NULL) i_zero(&new_update); else new_update = *update; if (guid_128_is_empty(new_update.mailbox_guid)) guid_128_generate(new_update.mailbox_guid); /* create the backend mailbox first before it exists in the list. the mailbox creation wants to use get_path() though, so use a bit kludgy create_mailbox_* variables during the creation to return the path. we'll also support recursively creating more mailboxes in here. */ const char *old_name; guid_128_t old_guid; old_name = list->create_mailbox_name; guid_128_copy(old_guid, list->create_mailbox_guid); list->create_mailbox_name = box->name; guid_128_copy(list->create_mailbox_guid, new_update.mailbox_guid); ret = ibox->module_ctx.super.create_box(box, &new_update, FALSE); if (ret == 0) { /* backend mailbox was successfully created. now add it to the list. */ ret = index_list_mailbox_create_selectable(box, new_update.mailbox_guid); if (ret < 0) mail_storage_copy_list_error(box->storage, box->list); if (ret <= 0) { /* failed to add to list. rollback the backend mailbox creation */ bool create_error = ret < 0; if (create_error) mail_storage_last_error_push(box->storage); if (mailbox_delete(box) < 0) ret = -1; if (create_error) mail_storage_last_error_pop(box->storage); } } list->create_mailbox_name = old_name; guid_128_copy(list->create_mailbox_guid, old_guid); if (ret < 0) return ret; } else { ret = 0; } if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } return 0; } static int index_list_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); const char *root_dir, *old_path, *new_path; if (mailbox_list_get_path(box->list, box->name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &old_path) <= 0) old_path = NULL; if (ibox->module_ctx.super.update_box(box, update) < 0) return -1; /* rename the directory */ if (!guid_128_is_empty(update->mailbox_guid) && old_path != NULL && mailbox_list_set_get_root_path(&box->list->set, MAILBOX_LIST_PATH_TYPE_MAILBOX, &root_dir)) { new_path = index_get_guid_path(box->list, root_dir, update->mailbox_guid); if (strcmp(old_path, new_path) == 0) ; else if (rename(old_path, new_path) == 0) ; else if (errno == ENOENT) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name)); return -1; } else { mail_storage_set_critical(box->storage, "rename(%s, %s) failed: %m", old_path, new_path); return -1; } } mailbox_list_index_update_mailbox_index(box, update); return 0; } static int index_list_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { struct index_mailbox_list *list = (struct index_mailbox_list *)box->list; if (index_list_node_exists(list, box->name, existence_r) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } return 0; } static bool mailbox_has_corrupted_name(struct mailbox *box) { struct mailbox_list_index_node *node; if (box->corrupted_mailbox_name) return TRUE; node = mailbox_list_index_lookup(box->list, box->name); return node != NULL && (node->flags & MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME) != 0; } static void index_list_rename_corrupted(struct mailbox *box, const char *newname) { if (index_list_rename_mailbox(box->list, box->name, box->list, newname) == 0 || box->list->error != MAIL_ERROR_EXISTS) return; /* mailbox already exists. don't give up yet, just use the newname as prefix and add the "lost-xx" as suffix. */ char sep = mailbox_list_get_hierarchy_sep(box->list); const char *oldname = box->name; /* oldname should be at the root level, but check for hierarchies anyway to be safe. */ const char *p = strrchr(oldname, sep); if (p != NULL) oldname = p+1; newname = t_strdup_printf("%s-%s", newname, oldname); (void)index_list_rename_mailbox(box->list, box->name, box->list, newname); } static int index_list_mailbox_open(struct mailbox *box) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); const void *data; const unsigned char *name_hdr; size_t name_hdr_size; if (ibox->module_ctx.super.open(box) < 0) return -1; if (box->view == NULL) { /* FIXME: dsync-merge is performing a delete in obox - remove this check once dsync-merging is no longer used. */ return 0; } /* if mailbox name has changed, update it to the header. Use \0 as the hierarchy separator in the header. This is to make sure we don't keep rewriting the name just in case some backend switches between separators when accessed different ways. */ /* Get the current mailbox name with \0 separators. */ char sep = mailbox_list_get_hierarchy_sep(box->list); char *box_zerosep_name = t_strdup_noconst(box->name); size_t box_name_len = strlen(box_zerosep_name); for (size_t i = 0; i < box_name_len; i++) { if (box_zerosep_name[i] == sep) box_zerosep_name[i] = '\0'; } /* Does it match what's in the header now? */ mail_index_get_header_ext(box->view, box->box_name_hdr_ext_id, &data, &name_hdr_size); name_hdr = data; while (name_hdr_size > 0 && name_hdr[name_hdr_size-1] == '\0') { /* Remove trailing \0 - header doesn't shrink always */ name_hdr_size--; } if (name_hdr_size == box_name_len && memcmp(box_zerosep_name, name_hdr, box_name_len) == 0) { /* Same mailbox name */ } else if (!mailbox_has_corrupted_name(box)) { /* Mailbox name changed - update */ struct mail_index_transaction *trans = mail_index_transaction_begin(box->view, 0); mail_index_ext_resize_hdr(trans, box->box_name_hdr_ext_id, box_name_len); mail_index_update_header_ext(trans, box->box_name_hdr_ext_id, 0, box_zerosep_name, box_name_len); (void)mail_index_transaction_commit(&trans); } else if (name_hdr_size > 0) { /* Mailbox name is corrupted. Rename it to the previous name. */ char sep = mailbox_list_get_hierarchy_sep(box->list); char *newname = t_malloc0(name_hdr_size + 1); memcpy(newname, name_hdr, name_hdr_size); for (size_t i = 0; i < name_hdr_size; i++) { if (newname[i] == '\0') newname[i] = sep; } index_list_rename_corrupted(box, newname); } return 0; } static void index_list_try_delete(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type) { const char *mailbox_path, *path; if (mailbox_list_get_path(_list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &mailbox_path) <= 0 || mailbox_list_get_path(_list, name, type, &path) <= 0 || strcmp(path, mailbox_path) == 0) return; if (*_list->set.maildir_name == '\0' && (_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { /* this directory may contain also child mailboxes' data. we don't want to delete that. */ bool rmdir_path = *_list->set.maildir_name != '\0'; if (mailbox_list_delete_mailbox_nonrecursive(_list, name, path, rmdir_path) < 0) return; } else { if (mailbox_list_delete_trash(path) < 0 && errno != ENOENT && errno != ENOTEMPTY) { mailbox_list_set_critical(_list, "unlink_directory(%s) failed: %m", path); } } /* avoid leaving empty directories lying around */ mailbox_list_delete_until_root(_list, path, type); } static void index_list_delete_finish(struct mailbox_list *list, const char *name) { index_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX); index_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL); index_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX); } static int index_list_delete_entry(struct index_mailbox_list *list, const char *name, bool delete_selectable) { struct mailbox_list_index_sync_context *sync_ctx; int ret; if (list->create_mailbox_name != NULL && strcmp(name, list->create_mailbox_name) == 0) { /* we're rollbacking a failed create. if the name exists in the list, it was done by somebody else so we don't want to remove it. */ return 0; } if (mailbox_list_index_sync_begin(&list->list, &sync_ctx) < 0) return -1; ret = mailbox_list_index_sync_delete(sync_ctx, name, delete_selectable); if (mailbox_list_index_sync_end(&sync_ctx, TRUE) < 0) return -1; return ret; } static int index_list_delete_mailbox(struct mailbox_list *_list, const char *name) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; const char *path; int ret; /* first delete the mailbox files */ ret = mailbox_list_get_path(_list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret <= 0) return ret; if ((_list->flags & (MAILBOX_LIST_FLAG_NO_MAIL_FILES | MAILBOX_LIST_FLAG_NO_DELETES)) != 0) { ret = 0; } else if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { ret = mailbox_list_delete_mailbox_file(_list, name, path); } else { ret = mailbox_list_delete_mailbox_nonrecursive(_list, name, path, TRUE); } if ((ret == 0 || (_list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) != 0) && (_list->flags & MAILBOX_LIST_FLAG_NO_DELETES) == 0) index_list_delete_finish(_list, name); if (ret == 0) { if (index_list_delete_entry(list, name, TRUE) < 0) return -1; } return ret; } static int index_list_delete_dir(struct mailbox_list *_list, const char *name) { struct index_mailbox_list *list = (struct index_mailbox_list *)_list; int ret; if ((ret = index_list_delete_entry(list, name, FALSE)) < 0) return -1; if (ret == 0) { mailbox_list_set_error(_list, MAIL_ERROR_EXISTS, "Mailbox has children, delete them first"); return -1; } return 0; } static int index_list_delete_symlink(struct mailbox_list *_list, const char *name ATTR_UNUSED) { mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE, "Symlinks not supported"); return -1; } static int index_list_rename_mailbox(struct mailbox_list *_oldlist, const char *oldname, struct mailbox_list *_newlist, const char *newname) { struct index_mailbox_list *list = (struct index_mailbox_list *)_oldlist; const size_t oldname_len = strlen(oldname); struct mailbox_list_index_sync_context *sync_ctx; struct mailbox_list_index_record oldrec, newrec; struct mailbox_list_index_node *oldnode, *newnode, *child; const void *data; bool created, expunged; uint32_t oldseq, newseq; if (_oldlist != _newlist) { mailbox_list_set_error(_oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across namespaces."); return -1; } if (strncmp(oldname, newname, oldname_len) == 0 && newname[oldname_len] == mailbox_list_get_hierarchy_sep(_newlist)) { mailbox_list_set_error(_oldlist, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailbox under itself."); return -1; } if (mailbox_list_index_sync_begin(&list->list, &sync_ctx) < 0) return -1; oldnode = mailbox_list_index_lookup(&list->list, oldname); if (oldnode == NULL) { (void)mailbox_list_index_sync_end(&sync_ctx, FALSE); mailbox_list_set_error(&list->list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname)); return -1; } if (!mail_index_lookup_seq(sync_ctx->view, oldnode->uid, &oldseq)) i_panic("mailbox list index: lost uid=%u", oldnode->uid); newseq = mailbox_list_index_sync_name(sync_ctx, newname, &newnode, &created); if (!created) { (void)mailbox_list_index_sync_end(&sync_ctx, FALSE); mailbox_list_set_error(&list->list, MAIL_ERROR_EXISTS, "Target mailbox already exists"); return -1; } i_assert(oldnode != newnode); /* copy all the data from old node to new node */ newnode->uid = oldnode->uid; newnode->flags = oldnode->flags; newnode->children = oldnode->children; oldnode->children = NULL; for (child = newnode->children; child != NULL; child = child->next) child->parent = newnode; /* remove the old node from existence */ mailbox_list_index_node_unlink(sync_ctx->ilist, oldnode); /* update the old index record to contain the new name_id/parent_uid, then expunge the added index record */ mail_index_lookup_ext(sync_ctx->view, oldseq, sync_ctx->ilist->ext_id, &data, &expunged); i_assert(data != NULL && !expunged); memcpy(&oldrec, data, sizeof(oldrec)); mail_index_lookup_ext(sync_ctx->view, newseq, sync_ctx->ilist->ext_id, &data, &expunged); i_assert(data != NULL && !expunged); memcpy(&newrec, data, sizeof(newrec)); oldrec.name_id = newrec.name_id; oldrec.parent_uid = newrec.parent_uid; if ((newnode->flags & MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME) != 0) { /* mailbox is renamed - clear away the corruption flag so the new name will be written to the mailbox index header. */ newnode->flags &= ~MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME; mail_index_update_flags(sync_ctx->trans, oldseq, MODIFY_REMOVE, (enum mail_flags)MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME); } mail_index_update_ext(sync_ctx->trans, oldseq, sync_ctx->ilist->ext_id, &oldrec, NULL); mail_index_expunge(sync_ctx->trans, newseq); return mailbox_list_index_sync_end(&sync_ctx, TRUE); } static struct mailbox_list_iterate_context * index_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct mailbox_list_iterate_context *ctx; pool_t pool; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { return mailbox_list_subscriptions_iter_init(list, patterns, flags); } pool = pool_alloconly_create("mailbox list index backend iter", 1024); ctx = p_new(pool, struct mailbox_list_iterate_context, 1); ctx->pool = pool; ctx->list = list; ctx->flags = flags; array_create(&ctx->module_contexts, pool, sizeof(void *), 5); return ctx; } static const struct mailbox_info * index_list_iter_next(struct mailbox_list_iterate_context *ctx) { if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_next(ctx); return NULL; } static int index_list_iter_deinit(struct mailbox_list_iterate_context *ctx) { if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_deinit(ctx); pool_unref(&ctx->pool); return 0; } struct mailbox_list index_mailbox_list = { .name = MAILBOX_LIST_NAME_INDEX, .props = MAILBOX_LIST_PROP_NO_ROOT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = index_list_alloc, .init = index_list_init, .deinit = index_list_deinit, .get_hierarchy_sep = index_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = index_list_get_path, .get_temp_prefix = index_list_get_temp_prefix, .iter_init = index_list_iter_init, .iter_next = index_list_iter_next, .iter_deinit = index_list_iter_deinit, .subscriptions_refresh = mailbox_list_subscriptions_refresh, .set_subscribed = index_list_set_subscribed, .delete_mailbox = index_list_delete_mailbox, .delete_dir = index_list_delete_dir, .delete_symlink = index_list_delete_symlink, .rename_mailbox = index_list_rename_mailbox, } }; void mailbox_list_index_backend_init_mailbox(struct mailbox *box, struct mailbox_vfuncs *v) { if (strcmp(box->list->name, MAILBOX_LIST_NAME_INDEX) != 0) return; v->create_box = index_list_mailbox_create; v->update_box = index_list_mailbox_update; v->exists = index_list_mailbox_exists; v->open = index_list_mailbox_open; } dovecot-2.2.33.2/src/lib-storage/list/subscription-file.h0000644000175000017500000000154113123174404020111 00000000000000#ifndef SUBSCRIPTION_FILE_H #define SUBSCRIPTION_FILE_H struct stat; struct mailbox_list; /* Initialize new subscription file listing. */ struct subsfile_list_context * subsfile_list_init(struct mailbox_list *list, const char *path); /* Deinitialize subscription file listing. Returns 0 if ok, or -1 if some error occurred while listing. */ int subsfile_list_deinit(struct subsfile_list_context **ctx); /* Call fstat() for subscription file */ int subsfile_list_fstat(struct subsfile_list_context *ctx, struct stat *st_r); /* Returns the next subscribed mailbox, or NULL. */ const char *subsfile_list_next(struct subsfile_list_context *ctx); /* Returns 1 if subscribed, 0 if no changes done, -1 if error. */ int subsfile_set_subscribed(struct mailbox_list *list, const char *path, const char *temp_prefix, const char *name, bool set); #endif dovecot-2.2.33.2/src/lib-storage/list/subscription-file.c0000644000175000017500000002444113165463624020123 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "istream.h" #include "ostream.h" #include "nfs-workarounds.h" #include "mkdir-parents.h" #include "file-dotlock.h" #include "mailbox-list-private.h" #include "subscription-file.h" #include #include #define SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT #define SUBSCRIPTION_FILE_LOCK_TIMEOUT 120 #define SUBSCRIPTION_FILE_CHANGE_TIMEOUT 30 struct subsfile_list_context { struct mailbox_list *list; struct istream *input; char *path; string_t *name; unsigned int version; bool failed; }; static const char version2_header[] = "V\t2\n\n"; static void subsread_set_syscall_error(struct mailbox_list *list, const char *function, const char *path) { if (errno == EACCES && !list->mail_set->mail_debug) { mailbox_list_set_error(list, MAIL_ERROR_PERM, "No permission to read subscriptions"); } else { mailbox_list_set_critical(list, "%s failed with subscription file %s: %m", function, path); } } static void subswrite_set_syscall_error(struct mailbox_list *list, const char *function, const char *path) { if (errno == EACCES && !list->mail_set->mail_debug) { mailbox_list_set_error(list, MAIL_ERROR_PERM, "No permission to modify subscriptions"); } else { mailbox_list_set_critical(list, "%s failed with subscription file %s: %m", function, path); } } static void subsfile_list_read_header(struct mailbox_list *list, struct istream *input, unsigned int *version_r) { const unsigned char version2_header_len = strlen(version2_header); const unsigned char *data; size_t size; int ret; *version_r = 0; ret = i_stream_read_data(input, &data, &size, version2_header_len-1); if (ret < 0) { i_assert(ret == -1); if (input->stream_errno != 0) subswrite_set_syscall_error(list, "read()", i_stream_get_name(input)); return; } if (ret > 0 && memcmp(data, version2_header, version2_header_len) == 0) { *version_r = 2; i_stream_skip(input, version2_header_len); } } static const char *next_line(struct mailbox_list *list, const char *path, struct istream *input, bool *failed_r, bool ignore_estale) { const char *line; *failed_r = FALSE; while ((line = i_stream_next_line(input)) == NULL) { switch (i_stream_read(input)) { case -1: if (input->stream_errno != 0 && (input->stream_errno != ESTALE || !ignore_estale)) { subswrite_set_syscall_error(list, "read()", path); *failed_r = TRUE; } return NULL; case -2: /* mailbox name too large */ mailbox_list_set_critical(list, "Subscription file %s contains lines longer " "than %u characters", path, (unsigned int)list->mailbox_name_max_length); *failed_r = TRUE; return NULL; } } return line; } int subsfile_set_subscribed(struct mailbox_list *list, const char *path, const char *temp_prefix, const char *name, bool set) { const struct mail_storage_settings *mail_set = list->mail_set; struct dotlock_settings dotlock_set; struct dotlock *dotlock; struct mailbox_permissions perm; const char *line, *dir, *fname, *escaped_name; struct istream *input = NULL; struct ostream *output; int fd_in, fd_out; enum mailbox_list_path_type type; bool found, changed = FALSE, failed = FALSE; unsigned int version = 0; if (strcasecmp(name, "INBOX") == 0) name = "INBOX"; i_zero(&dotlock_set); dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; dotlock_set.nfs_flush = mail_set->mail_nfs_storage; dotlock_set.temp_prefix = temp_prefix; dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT; dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT; mailbox_list_get_root_permissions(list, &perm); fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); if (fd_out == -1 && errno == ENOENT) { /* directory hasn't been created yet. */ type = list->set.control_dir != NULL ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; fname = strrchr(path, '/'); if (fname != NULL) { dir = t_strdup_until(path, fname); if (mailbox_list_mkdir_root(list, dir, type) < 0) return -1; } fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); } if (fd_out == -1) { if (errno == EAGAIN) { mailbox_list_set_error(list, MAIL_ERROR_TEMP, "Timeout waiting for subscription file lock"); } else { subswrite_set_syscall_error(list, "file_dotlock_open()", path); } return -1; } fd_in = nfs_safe_open(path, O_RDONLY); if (fd_in == -1 && errno != ENOENT) { subswrite_set_syscall_error(list, "open()", path); file_dotlock_delete(&dotlock); return -1; } if (fd_in != -1) { input = i_stream_create_fd_autoclose(&fd_in, list->mailbox_name_max_length+1); i_stream_set_return_partial_line(input, TRUE); subsfile_list_read_header(list, input, &version); } found = FALSE; output = o_stream_create_fd_file(fd_out, 0, FALSE); o_stream_cork(output); if (version >= 2) o_stream_send_str(output, version2_header); if (version < 2 || name[0] == '\0') escaped_name = name; else { const char *const *tmp; char separators[2]; string_t *str = t_str_new(64); separators[0] = mailbox_list_get_hierarchy_sep(list); separators[1] = '\0'; tmp = t_strsplit(name, separators); str_append_tabescaped(str, *tmp); for (tmp++; *tmp != NULL; tmp++) { str_append_c(str, '\t'); str_append_tabescaped(str, *tmp); } escaped_name = str_c(str); } if (input != NULL) { while ((line = next_line(list, path, input, &failed, FALSE)) != NULL) { if (strcmp(line, escaped_name) == 0) { found = TRUE; if (!set) { changed = TRUE; continue; } } o_stream_nsend_str(output, line); o_stream_nsend(output, "\n", 1); } i_stream_destroy(&input); } if (!failed && set && !found) { /* append subscription */ line = t_strconcat(escaped_name, "\n", NULL); o_stream_nsend_str(output, line); changed = TRUE; } if (changed && !failed) { if (o_stream_nfinish(output) < 0) { subswrite_set_syscall_error(list, "write()", path); failed = TRUE; } else if (mail_set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fsync(fd_out) < 0) { subswrite_set_syscall_error(list, "fsync()", path); failed = TRUE; } } } else { o_stream_ignore_last_errors(output); } o_stream_destroy(&output); if (failed || !changed) { if (file_dotlock_delete(&dotlock) < 0) { subswrite_set_syscall_error(list, "file_dotlock_delete()", path); failed = TRUE; } } else { enum dotlock_replace_flags flags = DOTLOCK_REPLACE_FLAG_VERIFY_OWNER; if (file_dotlock_replace(&dotlock, flags) < 0) { subswrite_set_syscall_error(list, "file_dotlock_replace()", path); failed = TRUE; } } return failed ? -1 : (changed ? 1 : 0); } struct subsfile_list_context * subsfile_list_init(struct mailbox_list *list, const char *path) { struct subsfile_list_context *ctx; int fd; ctx = i_new(struct subsfile_list_context, 1); ctx->list = list; fd = nfs_safe_open(path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) { subsread_set_syscall_error(list, "open()", path); ctx->failed = TRUE; } } else { ctx->input = i_stream_create_fd_autoclose(&fd, list->mailbox_name_max_length+1); i_stream_set_return_partial_line(ctx->input, TRUE); subsfile_list_read_header(ctx->list, ctx->input, &ctx->version); } ctx->path = i_strdup(path); ctx->name = str_new(default_pool, 128); return ctx; } int subsfile_list_deinit(struct subsfile_list_context **_ctx) { struct subsfile_list_context *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; if (ctx->input != NULL) i_stream_destroy(&ctx->input); str_free(&ctx->name); i_free(ctx->path); i_free(ctx); return ret; } int subsfile_list_fstat(struct subsfile_list_context *ctx, struct stat *st_r) { const struct stat *st; if (ctx->failed) return -1; if (i_stream_stat(ctx->input, FALSE, &st) < 0) { ctx->failed = TRUE; return -1; } *st_r = *st; return 0; } static const char * subsfile_list_unescaped(struct subsfile_list_context *ctx, const char *line) { const char *p; str_truncate(ctx->name, 0); while ((p = strchr(line, '\t')) != NULL) { str_append_tabunescaped(ctx->name, line, p-line); str_append_c(ctx->name, mailbox_list_get_hierarchy_sep(ctx->list)); line = p+1; } str_append_tabunescaped(ctx->name, line, strlen(line)); return str_c(ctx->name); } const char *subsfile_list_next(struct subsfile_list_context *ctx) { const char *line; unsigned int i; int fd; if (ctx->failed || ctx->input == NULL) return NULL; for (i = 0;; i++) { line = next_line(ctx->list, ctx->path, ctx->input, &ctx->failed, i < SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT); if (ctx->input->stream_errno != ESTALE || i == SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT) break; /* Reopen the subscription file and re-send everything. this isn't the optimal behavior, but it's allowed by IMAP and this way we don't have to read everything into memory or try to play any guessing games. */ i_stream_destroy(&ctx->input); fd = nfs_safe_open(ctx->path, O_RDONLY); if (fd == -1) { /* In case of ENOENT all the subscriptions got lost. Just return end of subscriptions list in that case. */ if (errno != ENOENT) { subsread_set_syscall_error(ctx->list, "open()", ctx->path); ctx->failed = TRUE; } return NULL; } ctx->input = i_stream_create_fd_autoclose(&fd, ctx->list->mailbox_name_max_length+1); i_stream_set_return_partial_line(ctx->input, TRUE); } if (ctx->version > 1 && line != NULL) line = subsfile_list_unescaped(ctx, line); return line; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-iter.c0000644000175000017500000001541513147010712021117 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-match.h" #include "mail-storage.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-iter-private.h" #include "mailbox-list-index.h" static bool iter_use_index(struct mailbox_list *list, enum mailbox_list_iter_flags flags) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* for now we don't use indexes when listing subscriptions, because it needs to list also the nonexistent subscribed mailboxes, which don't exist in the index. */ return FALSE; } if ((flags & MAILBOX_LIST_ITER_RAW_LIST) != 0 && ilist->has_backing_store) { /* no indexing wanted with raw lists */ return FALSE; } if (mailbox_list_index_refresh(list) < 0 && ilist->has_backing_store) { /* refresh failed */ return FALSE; } return TRUE; } struct mailbox_list_iterate_context * mailbox_list_index_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_iterate_context *ctx; pool_t pool; char ns_sep = mail_namespace_get_sep(list->ns); if (!iter_use_index(list, flags)) { /* no indexing */ return ilist->module_ctx.super.iter_init(list, patterns, flags); } pool = pool_alloconly_create("mailbox list index iter", 2048); ctx = p_new(pool, struct mailbox_list_index_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = list; ctx->ctx.flags = flags; ctx->ctx.glob = imap_match_init_multiple(pool, patterns, TRUE, ns_sep); array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); ctx->info_pool = pool_alloconly_create("mailbox list index iter info", 128); ctx->ctx.index_iteration = TRUE; /* listing mailboxes from index */ ctx->info.ns = list->ns; ctx->path = str_new(pool, 128); ctx->next_node = ilist->mailbox_tree; ctx->mailbox_pool = ilist->mailbox_pool; pool_ref(ctx->mailbox_pool); return &ctx->ctx; } static void mailbox_list_index_update_info(struct mailbox_list_index_iterate_context *ctx) { struct mailbox_list_index_node *node = ctx->next_node; struct mailbox *box; p_clear(ctx->info_pool); str_truncate(ctx->path, ctx->parent_len); /* the root directory may have an empty name. in that case we'll still want to insert the separator, so check for non-NULL parent rather than non-empty path. */ if (node->parent != NULL) { str_append_c(ctx->path, mailbox_list_get_hierarchy_sep(ctx->ctx.list)); } str_append(ctx->path, node->name); ctx->info.vname = mailbox_list_get_vname(ctx->ctx.list, str_c(ctx->path)); ctx->info.vname = p_strdup(ctx->info_pool, ctx->info.vname); ctx->info.flags = 0; if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) ctx->info.flags |= MAILBOX_NONEXISTENT; else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) ctx->info.flags |= MAILBOX_NOSELECT; if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0) ctx->info.flags |= MAILBOX_NOINFERIORS; ctx->info.flags |= node->children != NULL ? MAILBOX_CHILDREN : MAILBOX_NOCHILDREN; if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, ctx->info.vname, &ctx->info.flags); } if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) { box = mailbox_alloc(ctx->ctx.list, ctx->info.vname, 0); mailbox_list_index_status_set_info_flags(box, node->uid, &ctx->info.flags); mailbox_free(&box); } } static void mailbox_list_index_update_next(struct mailbox_list_index_iterate_context *ctx, bool follow_children) { struct mailbox_list_index_node *node = ctx->next_node; if (node->children != NULL && follow_children) { ctx->parent_len = str_len(ctx->path); ctx->next_node = node->children; } else { while (node->next == NULL) { node = node->parent; if (node != NULL) { ctx->parent_len -= strlen(node->name); if (node->parent != NULL) ctx->parent_len--; } if (node == NULL) { /* last one */ ctx->next_node = NULL; return; } } ctx->next_node = node->next; } } static bool iter_subscriptions_ok(struct mailbox_list_index_iterate_context *ctx) { if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) return TRUE; if ((ctx->info.flags & MAILBOX_SUBSCRIBED) != 0) return TRUE; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 && (ctx->info.flags & MAILBOX_CHILD_SUBSCRIBED) != 0) return TRUE; return FALSE; } const struct mailbox_info * mailbox_list_index_iter_next(struct mailbox_list_iterate_context *_ctx) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(_ctx->list); if (!_ctx->index_iteration) { /* index isn't being used */ return ilist->module_ctx.super.iter_next(_ctx); } struct mailbox_list_index_iterate_context *ctx = (struct mailbox_list_index_iterate_context *)_ctx; bool follow_children; enum imap_match_result match; /* listing mailboxes from index */ while (ctx->next_node != NULL) { mailbox_list_index_update_info(ctx); match = imap_match(_ctx->glob, ctx->info.vname); follow_children = (match & (IMAP_MATCH_YES | IMAP_MATCH_CHILDREN)) != 0; if (match == IMAP_MATCH_YES && iter_subscriptions_ok(ctx)) { /* If this is a) \NoSelect leaf, b) not LAYOUT=index and c) NO-NOSELECT is set, try to rmdir the leaf directores from filesystem. (With LAYOUT=index the \NoSelect mailboxes aren't on the filesystem.) */ if (ilist->has_backing_store && mailbox_list_iter_try_delete_noselect(_ctx, &ctx->info, str_c(ctx->path))) { /* Deleted \NoSelect leaf. Refresh the index later on so it gets removed from the index as well. */ mailbox_list_index_refresh_later(_ctx->list); } else { mailbox_list_index_update_next(ctx, TRUE); return &ctx->info; } } else if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 && (ctx->info.flags & MAILBOX_CHILD_SUBSCRIBED) == 0) { /* listing only subscriptions, but there are no subscribed children. */ follow_children = FALSE; } mailbox_list_index_update_next(ctx, follow_children); } return mailbox_list_iter_default_next(_ctx); } int mailbox_list_index_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(_ctx->list); if (!_ctx->index_iteration) return ilist->module_ctx.super.iter_deinit(_ctx); struct mailbox_list_index_iterate_context *ctx = (struct mailbox_list_index_iterate_context *)_ctx; int ret = ctx->failed ? -1 : 0; pool_unref(&ctx->mailbox_pool); pool_unref(&ctx->info_pool); pool_unref(&_ctx->pool); return ret; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index-storage.h0000644000175000017500000000070413144653606021634 00000000000000#ifndef MAILBOX_LIST_INDEX_STORAGE_H #define MAILBOX_LIST_INDEX_STORAGE_H #include "mail-storage-private.h" #define INDEX_LIST_STORAGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, index_list_storage_module) struct index_list_mailbox { union mailbox_module_context module_ctx; uint32_t pre_sync_log_file_seq; uoff_t pre_sync_log_file_head_offset; }; extern MODULE_CONTEXT_DEFINE(index_list_storage_module, &mail_storage_module_register); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-subscriptions.c0000644000175000017500000002353313123174404021761 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "unichar.h" #include "imap-match.h" #include "subscription-file.h" #include "mailbox-tree.h" #include "mailbox-list-private.h" #include "mailbox-list-subscriptions.h" #include struct subscriptions_mailbox_list_iterate_context { struct mailbox_list_iterate_context ctx; struct mailbox_tree_context *tree; struct mailbox_tree_iterate_context *iter; struct mailbox_info info; }; static int mailbox_list_subscription_fill_one(struct mailbox_list *list, struct mailbox_list *src_list, const char *name) { struct mail_namespace *ns, *default_ns = list->ns; struct mail_namespace *namespaces = default_ns->user->namespaces; struct mailbox_node *node; const char *vname, *ns_name, *error; size_t len; bool created; /* default_ns is whatever namespace we're currently listing. if we have e.g. prefix="" and prefix=pub/ namespaces with pub/ namespace having subscriptions=no, we want to: 1) when listing "" namespace we want to skip over any names that begin with pub/. */ if (src_list->ns->prefix_len == 0) ns_name = name; else { /* we could have two-level namespace: ns/ns2/ */ ns_name = t_strconcat(src_list->ns->prefix, name, NULL); } ns = mail_namespace_find_unsubscribable(namespaces, ns_name); if (ns != NULL && ns != default_ns) { if (ns->prefix_len > 0) return 0; /* prefix="" namespace=no : catching this is basically the same as not finding any namespace. */ ns = NULL; } /* 2) when listing pub/ namespace, skip over entries that don't begin with pub/. */ if (ns == NULL && (default_ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) return 0; /* When listing shared namespace's subscriptions, we need to autocreate all the visible child namespaces. their subscriptions are listed later. */ if (ns != NULL && mail_namespace_is_shared_user_root(ns)) { /* we'll need to get the namespace autocreated. one easy way is to just ask to join a reference and pattern */ (void)mailbox_list_join_refpattern(ns->list, ns_name, ""); } /* When listing pub/ namespace, skip over the namespace prefix in the name. the rest of the name is storage_name. */ if (ns == NULL) ns = default_ns; else if (strncmp(ns_name, ns->prefix, ns->prefix_len) == 0) { ns_name += ns->prefix_len; name = ns_name; } else { /* "pub" entry - this shouldn't be possible normally, because it should be saved as "pub/", but handle it anyway */ i_assert(strncmp(ns_name, ns->prefix, ns->prefix_len-1) == 0 && ns_name[ns->prefix_len-1] == '\0'); name = ns_name = ""; } len = strlen(name); if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) { /* entry ends with hierarchy separator, remove it. this exists mainly for backwards compatibility with old Dovecot versions and non-Dovecot software that added them */ name = t_strndup(name, len-1); } if (!mailbox_list_is_valid_name(list, name, &error)) { /* we'll only get into trouble if we show this */ return -1; } else { vname = mailbox_list_get_vname(list, name); if (!uni_utf8_str_is_valid(vname)) return -1; node = mailbox_tree_get(list->subscriptions, vname, &created); node->flags = MAILBOX_SUBSCRIBED; } return 0; } int mailbox_list_subscriptions_refresh(struct mailbox_list *src_list, struct mailbox_list *dest_list) { struct subsfile_list_context *subsfile_ctx; struct stat st; enum mailbox_list_path_type type; const char *path, *name; char sep; int ret; /* src_list is subscriptions=yes, dest_list is subscriptions=no (or the same as src_list) */ i_assert((src_list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0); if (dest_list->subscriptions == NULL) { sep = mail_namespace_get_sep(src_list->ns); dest_list->subscriptions = mailbox_tree_init(sep); } type = src_list->set.control_dir != NULL ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; if (!mailbox_list_get_root_path(src_list, type, &path) || src_list->set.subscription_fname == NULL) { /* no subscriptions (e.g. pop3c) */ return 0; } path = t_strconcat(path, "/", src_list->set.subscription_fname, NULL); if (stat(path, &st) < 0) { if (errno == ENOENT) { /* no subscriptions */ mailbox_tree_clear(dest_list->subscriptions); dest_list->subscriptions_mtime = 0; return 0; } mailbox_list_set_critical(dest_list, "stat(%s) failed: %m", path); return -1; } if (st.st_mtime == dest_list->subscriptions_mtime && st.st_mtime < dest_list->subscriptions_read_time-1) { /* we're up to date */ return 0; } mailbox_tree_clear(dest_list->subscriptions); dest_list->subscriptions_read_time = ioloop_time; subsfile_ctx = subsfile_list_init(dest_list, path); if (subsfile_list_fstat(subsfile_ctx, &st) == 0) dest_list->subscriptions_mtime = st.st_mtime; while ((name = subsfile_list_next(subsfile_ctx)) != NULL) T_BEGIN { T_BEGIN { ret = mailbox_list_subscription_fill_one(dest_list, src_list, name); } T_END; if (ret < 0) { i_warning("Subscriptions file %s: " "Removing invalid entry: %s", path, name); (void)subsfile_set_subscribed(src_list, path, mailbox_list_get_temp_prefix(src_list), name, FALSE); } } T_END; if (subsfile_list_deinit(&subsfile_ctx) < 0) { dest_list->subscriptions_mtime = (time_t)-1; return -1; } return 0; } void mailbox_list_set_subscription_flags(struct mailbox_list *list, const char *vname, enum mailbox_info_flags *flags) { struct mailbox_node *node; *flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); node = mailbox_tree_lookup(list->subscriptions, vname); if (node != NULL) { *flags |= node->flags & MAILBOX_SUBSCRIBED; /* the only reason why node might have a child is if one of them is subscribed */ if (node->children != NULL) *flags |= MAILBOX_CHILD_SUBSCRIBED; } } void mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, struct mailbox_tree_context *tree, bool default_nonexistent) { struct mailbox_list_iter_update_context update_ctx; struct mailbox_tree_iterate_context *iter; const char *name; i_zero(&update_ctx); update_ctx.iter_ctx = ctx; update_ctx.tree_ctx = tree; update_ctx.glob = ctx->glob; update_ctx.leaf_flags = MAILBOX_SUBSCRIBED; if (default_nonexistent) update_ctx.leaf_flags |= MAILBOX_NONEXISTENT; update_ctx.parent_flags = MAILBOX_CHILD_SUBSCRIBED; update_ctx.match_parents = (ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0; iter = mailbox_tree_iterate_init(ctx->list->subscriptions, NULL, MAILBOX_SUBSCRIBED); while (mailbox_tree_iterate_next(iter, &name) != NULL) mailbox_list_iter_update(&update_ctx, name); mailbox_tree_iterate_deinit(&iter); } struct mailbox_list_iterate_context * mailbox_list_subscriptions_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct subscriptions_mailbox_list_iterate_context *ctx; pool_t pool; char sep = mail_namespace_get_sep(list->ns); pool = pool_alloconly_create("mailbox list subscriptions iter", 1024); ctx = p_new(pool, struct subscriptions_mailbox_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = list; ctx->ctx.flags = flags; ctx->ctx.glob = imap_match_init_multiple(pool, patterns, TRUE, sep); array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); ctx->tree = mailbox_tree_init(sep); mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree, FALSE); ctx->info.ns = list->ns; /* the tree usually has only those entries we want to iterate through, but there are also non-matching root entries (e.g. "LSUB foo/%" will include the "foo"), which we'll drop with MAILBOX_MATCHED. */ ctx->iter = mailbox_tree_iterate_init(ctx->tree, NULL, MAILBOX_MATCHED); return &ctx->ctx; } const struct mailbox_info * mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *_ctx) { struct subscriptions_mailbox_list_iterate_context *ctx = (struct subscriptions_mailbox_list_iterate_context *)_ctx; struct mailbox_list *list = _ctx->list; struct mailbox_node *node; enum mailbox_info_flags subs_flags; const char *vname, *storage_name, *error; int ret; node = mailbox_tree_iterate_next(ctx->iter, &vname); if (node == NULL) return mailbox_list_iter_default_next(_ctx); ctx->info.vname = vname; subs_flags = node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && (_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) { /* don't care about flags, just return it */ ctx->info.flags = subs_flags; return &ctx->info; } storage_name = mailbox_list_get_storage_name(list, vname); if (!mailbox_list_is_valid_name(list, storage_name, &error)) { /* broken entry in subscriptions file */ ctx->info.flags = MAILBOX_NONEXISTENT; } else if (mailbox_list_mailbox(list, storage_name, &ctx->info.flags) < 0) { ctx->info.flags = 0; _ctx->failed = TRUE; } else if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 && (ctx->info.flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN)) == 0) { ret = mailbox_has_children(list, storage_name); if (ret < 0) _ctx->failed = TRUE; else if (ret == 0) ctx->info.flags |= MAILBOX_NOCHILDREN; else ctx->info.flags |= MAILBOX_CHILDREN; } ctx->info.flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); ctx->info.flags |= node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); return &ctx->info; } int mailbox_list_subscriptions_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct subscriptions_mailbox_list_iterate_context *ctx = (struct subscriptions_mailbox_list_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; mailbox_tree_iterate_deinit(&ctx->iter); mailbox_tree_deinit(&ctx->tree); pool_unref(&_ctx->pool); return ret; } dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-maildir.h0000644000175000017500000000162213123174404020473 00000000000000#ifndef MAILBOX_LIST_MAILDIR_H #define MAILBOX_LIST_MAILDIR_H #include "mailbox-list-private.h" /* When doing deletion via renaming it first to trash directory, use this as the trash directory name */ #define MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME "DOVECOT-TRASHED" struct maildir_mailbox_list { struct mailbox_list list; const char *global_temp_prefix, *temp_prefix; char sep; }; struct mailbox_list_iterate_context * maildir_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags); int maildir_list_iter_deinit(struct mailbox_list_iterate_context *ctx); const struct mailbox_info * maildir_list_iter_next(struct mailbox_list_iterate_context *ctx); int maildir_list_get_mailbox_flags(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-notify-tree.h0000644000175000017500000000114713123174404021321 00000000000000#ifndef MAILBOX_LIST_NOTIFY_TREE_H #define MAILBOX_LIST_NOTIFY_TREE_H #include "mailbox-tree.h" struct mailbox_notify_node { struct mailbox_node node; guid_128_t guid; uint32_t index_uid; uint32_t uidvalidity; uint32_t uidnext; uint32_t messages; uint32_t unseen; uint64_t highest_modseq; }; struct mailbox_list_notify_tree * mailbox_list_notify_tree_init(struct mailbox_list *list); void mailbox_list_notify_tree_deinit(struct mailbox_list_notify_tree **tree); struct mailbox_notify_node * mailbox_list_notify_tree_lookup(struct mailbox_list_notify_tree *tree, const char *storage_name); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-index.h0000644000175000017500000001620013165463624020172 00000000000000#ifndef MAILBOX_LIST_INDEX_H #define MAILBOX_LIST_INDEX_H /* Mailbox list index basically contains: Header contains ID => name mapping. The name isn't the full mailbox name, but rather each hierarchy level has its own ID and name. For example a mailbox name "foo/bar" (with '/' as separator) would have separate IDs for "foo" and "bar" names. The records contain { parent_uid, uid, name_id } field that can be used to build the whole mailbox tree. parent_uid=0 means root, otherwise it's the parent node's uid. Each record also contains GUID for each selectable mailbox. If a mailbox is recreated using the same name, its GUID also changes. Note however that the UID doesn't change, because the UID refers to the mailbox name, not to the mailbox itself. The records may contain also extensions for allowing mailbox_get_status() to return values directly from the mailbox list index. Storage backends may also add their own extensions to figure out if a record is up to date. */ #include "module-context.h" #include "mail-types.h" #include "mail-storage.h" #include "mailbox-list-private.h" #include #define MAILBOX_LIST_INDEX_HIERARHCY_SEP '~' #define INDEX_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_list_index_module) struct mail_index_view; struct mailbox_index_vsize; struct mailbox_vfuncs; /* stored in mail_index_record.flags: */ enum mailbox_list_index_flags { MAILBOX_LIST_INDEX_FLAG_NONEXISTENT = MAIL_DELETED, MAILBOX_LIST_INDEX_FLAG_NOSELECT = MAIL_DRAFT, MAILBOX_LIST_INDEX_FLAG_NOINFERIORS = MAIL_ANSWERED, MAILBOX_LIST_INDEX_FLAG_CORRUPTED_NAME = MAIL_SEEN, /* set during syncing for mailboxes that still exist */ MAILBOX_LIST_INDEX_FLAG_SYNC_EXISTS = MAIL_FLAGGED }; struct mailbox_list_index_header { uint8_t refresh_flag; /* array of { uint32_t id; char name[]; } */ }; struct mailbox_list_index_record { /* points to given id in header */ uint32_t name_id; /* parent mailbox's UID, 0 = root */ uint32_t parent_uid; /* the following fields are temporarily zero while unknown, also permanently zero for \NoSelect and \Nonexistent mailboxes: */ guid_128_t guid; uint32_t uid_validity; }; struct mailbox_list_index_msgs_record { uint32_t messages; uint32_t unseen; uint32_t recent; uint32_t uidnext; }; struct mailbox_list_index_node { struct mailbox_list_index_node *parent; struct mailbox_list_index_node *next; struct mailbox_list_index_node *children; uint32_t name_id, uid; enum mailbox_list_index_flags flags; /* extension data is corrupted on disk - need to update it */ bool corrupted_ext; /* flags are corrupted on disk - need to update it */ bool corrupted_flags; const char *name; }; struct mailbox_list_index { union mailbox_list_module_context module_ctx; const char *path; struct mail_index *index; uint32_t ext_id, msgs_ext_id, hmodseq_ext_id, subs_hdr_ext_id; uint32_t vsize_ext_id, first_saved_ext_id; struct timeval last_refresh_timeval; pool_t mailbox_pool; /* uin32_t id => name */ HASH_TABLE(void *, char *) mailbox_names; uint32_t highest_name_id; struct mailbox_list_index_sync_context *sync_ctx; uint32_t sync_log_file_seq; uoff_t sync_log_file_offset; uint32_t sync_stamp; struct timeout *to_refresh; /* uint32_t uid => node */ HASH_TABLE(void *, struct mailbox_list_index_node *) mailbox_hash; struct mailbox_list_index_node *mailbox_tree; unsigned int pending_init:1; unsigned int opened:1; unsigned int syncing:1; unsigned int updating_status:1; unsigned int has_backing_store:1; unsigned int index_last_check_changed:1; unsigned int corrupted_names_or_parents:1; unsigned int handling_corruption:1; unsigned int call_corruption_callback:1; }; struct mailbox_list_index_iterate_context { struct mailbox_list_iterate_context ctx; pool_t mailbox_pool; struct mailbox_info info; pool_t info_pool; size_t parent_len; string_t *path; struct mailbox_list_index_node *next_node; unsigned int failed:1; }; extern MODULE_CONTEXT_DEFINE(mailbox_list_index_module, &mailbox_list_module_register); void mailbox_list_index_set_index_error(struct mailbox_list *list); struct mailbox_list_index_node * mailbox_list_index_lookup(struct mailbox_list *list, const char *name); struct mailbox_list_index_node * mailbox_list_index_lookup_uid(struct mailbox_list_index *ilist, uint32_t uid); void mailbox_list_index_node_get_path(const struct mailbox_list_index_node *node, char sep, string_t *str); void mailbox_list_index_node_unlink(struct mailbox_list_index *ilist, struct mailbox_list_index_node *node); int mailbox_list_index_index_open(struct mailbox_list *list); bool mailbox_list_index_need_refresh(struct mailbox_list_index *ilist, struct mail_index_view *view); /* Refresh the index, but only if it hasn't been refreshed "recently" (= within this same ioloop run) */ int mailbox_list_index_refresh(struct mailbox_list *list); /* Refresh the index regardless of when the last refresh was done. */ int mailbox_list_index_refresh_force(struct mailbox_list *list); void mailbox_list_index_refresh_later(struct mailbox_list *list); int mailbox_list_index_handle_corruption(struct mailbox_list *list); int mailbox_list_index_set_uncorrupted(struct mailbox_list *list); struct mailbox_list_index_node * mailbox_list_index_node_find_sibling(struct mailbox_list_index_node *node, const char *name); void mailbox_list_index_reset(struct mailbox_list_index *ilist); int mailbox_list_index_parse(struct mailbox_list *list, struct mail_index_view *view, bool force); struct mailbox_list_iterate_context * mailbox_list_index_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags); const struct mailbox_info * mailbox_list_index_iter_next(struct mailbox_list_iterate_context *ctx); int mailbox_list_index_iter_deinit(struct mailbox_list_iterate_context *ctx); bool mailbox_list_index_status(struct mailbox_list *list, struct mail_index_view *view, uint32_t seq, enum mailbox_status_items items, struct mailbox_status *status_r, uint8_t *mailbox_guid, struct mailbox_index_vsize *vsize_r); void mailbox_list_index_status_set_info_flags(struct mailbox *box, uint32_t uid, enum mailbox_info_flags *flags); void mailbox_list_index_update_mailbox_index(struct mailbox *box, const struct mailbox_update *update); int mailbox_list_index_notify_init(struct mailbox_list *list, enum mailbox_list_notify_event mask, struct mailbox_list_notify **notify_r); void mailbox_list_index_notify_deinit(struct mailbox_list_notify *notify); int mailbox_list_index_notify_next(struct mailbox_list_notify *notify, const struct mailbox_list_notify_rec **rec_r); void mailbox_list_index_notify_wait(struct mailbox_list_notify *notify, void (*callback)(void *context), void *context); void mailbox_list_index_notify_flush(struct mailbox_list_notify *notify); void mailbox_list_index_status_init_mailbox(struct mailbox_vfuncs *v); void mailbox_list_index_backend_init_mailbox(struct mailbox *box, struct mailbox_vfuncs *v); void mailbox_list_index_status_init_finish(struct mailbox_list *list); #endif dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-maildir.c0000644000175000017500000003702113147010712020465 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hostpid.h" #include "eacces-error.h" #include "mkdir-parents.h" #include "str.h" #include "subscription-file.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-delete.h" #include "mailbox-list-maildir.h" #include #include #define MAILDIR_GLOBAL_TEMP_PREFIX "temp." #define IMAPDIR_GLOBAL_TEMP_PREFIX ".temp." extern struct mailbox_list maildir_mailbox_list; extern struct mailbox_list imapdir_mailbox_list; static struct mailbox_list *maildir_list_alloc(void) { struct maildir_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("maildir++ list", 2048); list = p_new(pool, struct maildir_mailbox_list, 1); list->list = maildir_mailbox_list; list->list.pool = pool; list->sep = '.'; list->global_temp_prefix = MAILDIR_GLOBAL_TEMP_PREFIX; list->temp_prefix = p_strconcat(pool, list->global_temp_prefix, my_hostname, ".", my_pid, ".", NULL); return &list->list; } static struct mailbox_list *imapdir_list_alloc(void) { struct maildir_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("imapdir list", 1024); list = p_new(pool, struct maildir_mailbox_list, 1); list->list = imapdir_mailbox_list; list->list.pool = pool; list->sep = '.'; list->global_temp_prefix = IMAPDIR_GLOBAL_TEMP_PREFIX; list->temp_prefix = p_strconcat(pool, list->global_temp_prefix, my_hostname, ".", my_pid, ".", NULL); return &list->list; } static void maildir_list_deinit(struct mailbox_list *_list) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; pool_unref(&list->list.pool); } static const char * maildir_list_get_dirname_path(struct mailbox_list *list, const char *dir, const char *name) { if (*name == '\0') return dir; else if (list->name == imapdir_mailbox_list.name) return t_strdup_printf("%s/%s", dir, name); return t_strdup_printf("%s/%c%s", dir, mailbox_list_get_hierarchy_sep(list), name); } static const char * maildir_list_get_absolute_path(struct mailbox_list *list, const char *name) { const char *p; if (!mailbox_list_try_get_absolute_path(list, &name)) { /* fallback to using as ~name */ return name; } p = strrchr(name, '/'); if (p == NULL) return name; return maildir_list_get_dirname_path(list, t_strdup_until(name, p), p+1); } static char maildir_list_get_hierarchy_sep(struct mailbox_list *_list) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; return list->sep; } static int maildir_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type, const char **path_r) { const char *root_dir; if (name == NULL) { /* return root directories */ return mailbox_list_set_get_root_path(&_list->set, type, path_r) ? 1 : 0; } if (_list->mail_set->mail_full_filesystem_access && (*name == '/' || *name == '~')) { *path_r = maildir_list_get_absolute_path(_list, name); return 1; } root_dir = _list->set.root_dir; switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: case MAILBOX_LIST_PATH_TYPE_MAILBOX: break; case MAILBOX_LIST_PATH_TYPE_ALT_DIR: case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: if (_list->set.alt_dir == NULL) return 0; root_dir = _list->set.alt_dir; break; case MAILBOX_LIST_PATH_TYPE_CONTROL: if (_list->set.control_dir != NULL) { *path_r = maildir_list_get_dirname_path(_list, _list->set.control_dir, name); return 1; } break; case MAILBOX_LIST_PATH_TYPE_INDEX: if (_list->set.index_dir != NULL) { if (*_list->set.index_dir == '\0') return 0; *path_r = maildir_list_get_dirname_path(_list, _list->set.index_dir, name); return 1; } break; case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE: if (_list->set.index_pvt_dir == NULL) return 0; *path_r = maildir_list_get_dirname_path(_list, _list->set.index_pvt_dir, name); return 1; case MAILBOX_LIST_PATH_TYPE_LIST_INDEX: i_unreached(); } if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR || type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) { /* don't use inbox_path */ } else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL) { *path_r = _list->set.inbox_path; return 1; } *path_r = maildir_list_get_dirname_path(_list, root_dir, name); return 1; } static const char * maildir_list_get_temp_prefix(struct mailbox_list *_list, bool global) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; return global ? list->global_temp_prefix : list->temp_prefix; } static int maildir_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; const char *path; if (_list->set.subscription_fname == NULL) { mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE, "Subscriptions not supported"); return -1; } path = t_strconcat(_list->set.control_dir != NULL ? _list->set.control_dir : _list->set.root_dir, "/", _list->set.subscription_fname, NULL); return subsfile_set_subscribed(_list, path, list->temp_prefix, name, set); } static const char * mailbox_list_maildir_get_trash_dir(struct mailbox_list *_list) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; const char *root_dir; root_dir = mailbox_list_get_root_forced(_list, MAILBOX_LIST_PATH_TYPE_DIR); return t_strdup_printf("%s/%c%c"MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME, root_dir, list->sep, list->sep); } static int maildir_list_delete_maildir(struct mailbox_list *list, const char *name) { const char *path, *trash_dir; int ret = 0; trash_dir = mailbox_list_maildir_get_trash_dir(list); ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir); if (ret < 0) return -1; if (ret == 0) { /* we could actually use just unlink_directory() but error handling is easier this way :) */ if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0) i_unreached(); if (mailbox_list_delete_mailbox_nonrecursive(list, name, path, TRUE) < 0) return -1; } return 0; } static int maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) { const char *path; int ret; if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) return -1; i_assert(ret > 0); ret = mailbox_list_delete_mailbox_file(list, name, path); } else { ret = maildir_list_delete_maildir(list, name); } i_assert(ret <= 0); return mailbox_list_delete_finish_ret(list, name, ret == 0); } static int maildir_list_delete_dir(struct mailbox_list *list, const char *name) { const char *path; struct stat st; /* with maildir++ there aren't any non-selectable mailboxes. we'll always fail. */ if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0) i_unreached(); if (stat(path, &st) == 0) { mailbox_list_set_error(list, MAIL_ERROR_EXISTS, "Mailbox exists"); } else if (errno == ENOENT || errno == ENOTDIR) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(list, name)); } else { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); } return -1; } static int rename_dir(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, enum mailbox_list_path_type type) { const char *oldpath, *newpath; if (mailbox_list_get_path(oldlist, oldname, type, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, type, &newpath) <= 0) return 0; if (strcmp(oldpath, newpath) == 0) return 0; if (rename(oldpath, newpath) < 0 && errno != ENOENT) { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); return -1; } return 0; } static int maildir_rename_children(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; ARRAY(const char *) names_arr; const char *pattern, *oldpath, *newpath, *old_childname, *new_childname; const char *const *names, *old_vname, *new_vname; unsigned int i, count; size_t old_vnamelen; pool_t pool; char old_ns_sep; int ret; ret = 0; /* first get the list of the children and save them to memory, because we can't rely on readdir() not skipping files while the directory is being modified. this doesn't protect against modifications by other processes though. */ pool = pool_alloconly_create("Maildir++ children list", 1024); i_array_init(&names_arr, 64); old_vname = mailbox_list_get_vname(oldlist, oldname); old_vnamelen = strlen(old_vname); new_vname = mailbox_list_get_vname(newlist, newname); old_ns_sep = mail_namespace_get_sep(oldlist->ns); pattern = t_strdup_printf("%s%c*", old_vname, old_ns_sep); iter = mailbox_list_iter_init(oldlist, pattern, MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_RAW_LIST); while ((info = mailbox_list_iter_next(iter)) != NULL) { const char *name; /* verify that the prefix matches, otherwise we could have problems with mailbox names containing '%' and '*' chars */ if (strncmp(info->vname, old_vname, old_vnamelen) == 0 && info->vname[old_vnamelen] == old_ns_sep) { name = p_strdup(pool, info->vname + old_vnamelen); array_append(&names_arr, &name, 1); } } if (mailbox_list_iter_deinit(&iter) < 0) { ret = -1; names = NULL; count = 0; } else { names = array_get(&names_arr, &count); } for (i = 0; i < count; i++) { old_childname = mailbox_list_get_storage_name(oldlist, t_strconcat(old_vname, names[i], NULL)); if (strcmp(old_childname, new_vname) == 0) { /* When doing RENAME "a" "a.b" we see "a.b" here. We don't want to rename it anymore to "a.b.b". */ continue; } new_childname = mailbox_list_get_storage_name(newlist, t_strconcat(new_vname, names[i], NULL)); if (mailbox_list_get_path(oldlist, old_childname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 || mailbox_list_get_path(newlist, new_childname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0) i_unreached(); /* FIXME: it's possible to merge two mailboxes if either one of them doesn't have existing root mailbox. We could check this but I'm not sure if it's worth it. It could be even considered as a feature. Anyway, the bug with merging is that if both mailboxes have identically named child mailbox they conflict. Just ignore those and leave them under the old mailbox. */ if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno)) ret = 1; else { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); ret = -1; break; } (void)rename_dir(oldlist, old_childname, newlist, new_childname, MAILBOX_LIST_PATH_TYPE_CONTROL); (void)rename_dir(oldlist, old_childname, newlist, new_childname, MAILBOX_LIST_PATH_TYPE_INDEX); } array_free(&names_arr); pool_unref(&pool); return ret; } static int maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { const char *oldpath, *newpath, *root_path; int ret; bool found; /* NOTE: it's possible to rename a nonexistent mailbox which has children. In that case we should ignore the rename() error. */ if (mailbox_list_get_path(oldlist, oldname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0) i_unreached(); root_path = mailbox_list_get_root_forced(oldlist, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(oldpath, root_path) == 0) { /* most likely INBOX */ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf("Renaming %s isn't supported.", oldname)); return -1; } /* if we're renaming under another mailbox, require its permissions to be same as ours. */ if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL) { struct mailbox_permissions old_perm, new_perm; mailbox_list_get_permissions(oldlist, oldname, &old_perm); mailbox_list_get_permissions(newlist, newname, &new_perm); if ((new_perm.file_create_mode != old_perm.file_create_mode || new_perm.dir_create_mode != old_perm.dir_create_mode || new_perm.file_create_gid != old_perm.file_create_gid)) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across conflicting " "directory permissions"); return -1; } } ret = rename(oldpath, newpath); if (ret == 0 || errno == ENOENT) { (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_CONTROL); (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_INDEX); found = ret == 0; T_BEGIN { ret = maildir_rename_children(oldlist, oldname, newlist, newname); } T_END; if (ret < 0) return -1; if (!found && ret == 0) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND, T_MAILBOX_LIST_ERR_NOT_FOUND(oldlist, oldname)); return -1; } return 0; } if (EDESTDIREXISTS(errno)) { mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS, "Target mailbox already exists"); } else { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); } return -1; } struct mailbox_list maildir_mailbox_list = { .name = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS, .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME | MAILBOX_LIST_PROP_NO_ALT_DIR | MAILBOX_LIST_PROP_NO_NOSELECT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = maildir_list_alloc, .deinit = maildir_list_deinit, .get_hierarchy_sep = maildir_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = maildir_list_get_path, .get_temp_prefix = maildir_list_get_temp_prefix, .iter_init = maildir_list_iter_init, .iter_next = maildir_list_iter_next, .iter_deinit = maildir_list_iter_deinit, .get_mailbox_flags = maildir_list_get_mailbox_flags, .subscriptions_refresh = mailbox_list_subscriptions_refresh, .set_subscribed = maildir_list_set_subscribed, .delete_mailbox = maildir_list_delete_mailbox, .delete_dir = maildir_list_delete_dir, .delete_symlink = mailbox_list_delete_symlink_default, .rename_mailbox = maildir_list_rename_mailbox, } }; struct mailbox_list imapdir_mailbox_list = { .name = MAILBOX_LIST_NAME_IMAPDIR, .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME | MAILBOX_LIST_PROP_NO_ALT_DIR | MAILBOX_LIST_PROP_NO_NOSELECT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = imapdir_list_alloc, .deinit = maildir_list_deinit, .get_hierarchy_sep = maildir_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = maildir_list_get_path, .get_temp_prefix = maildir_list_get_temp_prefix, .iter_init = maildir_list_iter_init, .iter_next = maildir_list_iter_next, .iter_deinit = maildir_list_iter_deinit, .get_mailbox_flags = maildir_list_get_mailbox_flags, .subscriptions_refresh = mailbox_list_subscriptions_refresh, .set_subscribed = maildir_list_set_subscribed, .delete_mailbox = maildir_list_delete_mailbox, .delete_dir = maildir_list_delete_dir, .delete_symlink = mailbox_list_delete_symlink_default, .rename_mailbox = maildir_list_rename_mailbox, } }; dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-maildir-iter.c0000644000175000017500000003314113165463624021443 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "ioloop.h" #include "unlink-directory.h" #include "unichar.h" #include "imap-match.h" #include "imap-utf7.h" #include "mailbox-tree.h" #include "mailbox-list-delete.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-maildir.h" #include #include #include struct maildir_list_iterate_context { struct mailbox_list_iterate_context ctx; const char *dir; char prefix_char; struct mailbox_tree_context *tree_ctx; struct mailbox_tree_iterate_context *tree_iter; struct mailbox_info info; }; static void node_fix_parents(struct mailbox_node *node) { /* Fix parent nodes' children states. also if we happened to create any of the parents, we need to mark them nonexistent. */ node = node->parent; for (; node != NULL; node = node->parent) { if ((node->flags & MAILBOX_MATCHED) == 0) node->flags |= MAILBOX_NONEXISTENT; node->flags |= MAILBOX_CHILDREN; node->flags &= ~MAILBOX_NOCHILDREN; } } static void maildir_fill_parents(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only, const char *vname) { struct mail_namespace *ns = ctx->ctx.list->ns; struct mailbox_node *node; const char *p; size_t vname_len = strlen(vname); bool created; char ns_sep = mail_namespace_get_sep(ns); while ((p = strrchr(vname, ns_sep)) != NULL) { vname = t_strdup_until(vname, p); if (imap_match(glob, vname) != IMAP_MATCH_YES) continue; if (ns->prefix_len > 0 && vname_len == ns->prefix_len-1 && strncmp(vname, ns->prefix, ns->prefix_len - 1) == 0 && vname[ns->prefix_len-1] == ns_sep) { /* don't return matches to namespace prefix itself */ continue; } created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, vname) : mailbox_tree_get(ctx->tree_ctx, vname, &created); if (node != NULL) { if (created) { /* we haven't yet seen this mailbox, but we might see it later */ node->flags = MAILBOX_NONEXISTENT; } if (!update_only) node->flags |= MAILBOX_MATCHED; node->flags |= MAILBOX_CHILDREN; node->flags &= ~MAILBOX_NOCHILDREN; node_fix_parents(node); } } } static void maildir_set_children(struct maildir_list_iterate_context *ctx, const char *vname) { struct mailbox_node *node; const char *p; char hierarchy_sep; hierarchy_sep = mail_namespace_get_sep(ctx->ctx.list->ns); /* mark the first existing parent as containing children */ while ((p = strrchr(vname, hierarchy_sep)) != NULL) { vname = t_strdup_until(vname, p); node = mailbox_tree_lookup(ctx->tree_ctx, vname); if (node != NULL) { node->flags &= ~MAILBOX_NOCHILDREN; node->flags |= MAILBOX_CHILDREN; break; } } } static int maildir_fill_inbox(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, const char *inbox_name, bool update_only) { struct mailbox_node *node; enum mailbox_info_flags flags; enum imap_match_result match; bool created; int ret; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) { /* always show INBOX */ } else { /* INBOX may be Maildir root or completely elsewhere. show it only if it has already been created */ ret = mailbox_list_mailbox(ctx->ctx.list, "INBOX", &flags); if (ret < 0) return -1; if ((flags & MAILBOX_NONEXISTENT) != 0) update_only = TRUE; } if (update_only) { node = mailbox_tree_lookup(ctx->tree_ctx, inbox_name); if (node != NULL) node->flags &= ~MAILBOX_NONEXISTENT; return 0; } /* add the INBOX only if it matches the patterns */ match = imap_match(glob, inbox_name); if (match == IMAP_MATCH_PARENT) maildir_fill_parents(ctx, glob, FALSE, inbox_name); else if (match == IMAP_MATCH_YES) { node = mailbox_tree_get(ctx->tree_ctx, inbox_name, &created); if (created) node->flags = MAILBOX_NOCHILDREN; else node->flags &= ~MAILBOX_NONEXISTENT; node->flags |= MAILBOX_MATCHED; } return 0; } static bool maildir_get_type(const char *dir, const char *fname, enum mailbox_list_file_type *type_r, enum mailbox_info_flags *flags) { const char *path; struct stat st; path = *fname == '\0' ? dir : t_strdup_printf("%s/%s", dir, fname); if (stat(path, &st) < 0) { if (errno == ENOENT) { /* just deleted? */ *flags |= MAILBOX_NONEXISTENT; } else { *flags |= MAILBOX_NOSELECT; } return FALSE; } if (S_ISDIR(st.st_mode)) { *type_r = MAILBOX_LIST_FILE_TYPE_DIR; return TRUE; } else { if (strncmp(fname, ".nfs", 4) == 0) *flags |= MAILBOX_NONEXISTENT; else *flags |= MAILBOX_NOSELECT; return FALSE; } } int maildir_list_get_mailbox_flags(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags_r) { *flags_r = 0; switch (type) { case MAILBOX_LIST_FILE_TYPE_DIR: case MAILBOX_LIST_FILE_TYPE_FILE: case MAILBOX_LIST_FILE_TYPE_OTHER: break; case MAILBOX_LIST_FILE_TYPE_UNKNOWN: case MAILBOX_LIST_FILE_TYPE_SYMLINK: /* need to check with stat() to be sure */ if (!list->mail_set->maildir_stat_dirs && *fname != '\0' && strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 && strncmp(fname, ".nfs", 4) != 0) { /* just assume it's a valid mailbox */ return 1; } if (!maildir_get_type(dir, fname, &type, flags_r)) return 0; break; } switch (type) { case MAILBOX_LIST_FILE_TYPE_DIR: if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { *flags_r |= MAILBOX_NOSELECT; return 0; } break; case MAILBOX_LIST_FILE_TYPE_FILE: if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { *flags_r |= MAILBOX_NOSELECT; return 0; } break; case MAILBOX_LIST_FILE_TYPE_OTHER: *flags_r |= MAILBOX_NOSELECT; return 0; case MAILBOX_LIST_FILE_TYPE_UNKNOWN: case MAILBOX_LIST_FILE_TYPE_SYMLINK: i_unreached(); } if (*fname != '\0') { /* this tells maildir storage code that it doesn't need to see if cur/ exists, because just the existence of .dir/ assumes that the mailbox exists. */ *flags_r |= MAILBOX_SELECT; } return 1; } static bool maildir_delete_trash_dir(struct maildir_list_iterate_context *ctx, const char *fname) { const char *path; struct stat st; if (fname[1] != ctx->prefix_char || ctx->prefix_char == '\0' || strcmp(fname+2, MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME) != 0) return FALSE; /* this directory is in the middle of being deleted, or the process trying to delete it had died. delete it ourself if it's been there longer than one hour. */ path = t_strdup_printf("%s/%s", ctx->dir, fname); if (stat(path, &st) == 0 && st.st_mtime < ioloop_time - 3600) (void)mailbox_list_delete_trash(path); return TRUE; } static int maildir_fill_readdir_entry(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, const struct dirent *d, bool update_only) { struct mailbox_list *list = ctx->ctx.list; const char *fname, *storage_name, *vname; enum mailbox_info_flags flags; enum imap_match_result match; struct mailbox_node *node; bool created; int ret; fname = d->d_name; if (fname[0] == ctx->prefix_char) storage_name = fname + 1; else { if (ctx->prefix_char != '\0' || fname[0] == '.') return 0; storage_name = fname; } /* skip . and .. */ if (fname[0] == '.' && (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) return 0; vname = mailbox_list_get_vname(list, storage_name); if (!uni_utf8_str_is_valid(vname)) { /* the storage_name is completely invalid, rename it to something more sensible. we could do this for all names that aren't valid mUTF-7, but that might lead to accidents in future when UTF-8 storage names are used */ const char *src = t_strdup_printf("%s/%s", ctx->dir, fname); string_t *destvname = t_str_new(128); string_t *dest = t_str_new(128); (void)uni_utf8_get_valid_data((const void *)fname, strlen(fname), destvname); str_append(dest, ctx->dir); str_append_c(dest, '/'); (void)imap_utf8_to_utf7(str_c(destvname), dest); if (rename(src, str_c(dest)) < 0 && errno != ENOENT) i_error("rename(%s, %s) failed: %m", src, str_c(dest)); /* just skip this in this iteration, we'll see it on the next list */ return 0; } /* make sure the pattern matches */ match = imap_match(glob, vname); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) == 0) return 0; /* check if this is an actual mailbox */ if (maildir_delete_trash_dir(ctx, fname)) return 0; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { ret = mailbox_list_dirent_is_alias_symlink(list, ctx->dir, d); if (ret != 0) return ret < 0 ? -1 : 0; } T_BEGIN { ret = list->v.get_mailbox_flags(list, ctx->dir, fname, mailbox_list_get_file_type(d), &flags); } T_END; if (ret <= 0) return ret; /* we know the children flags ourself, so ignore if any of them were set. */ flags &= ~(MAILBOX_NOINFERIORS | MAILBOX_CHILDREN | MAILBOX_NOCHILDREN); if ((match & IMAP_MATCH_PARENT) != 0) maildir_fill_parents(ctx, glob, update_only, vname); else { created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, vname) : mailbox_tree_get(ctx->tree_ctx, vname, &created); if (node != NULL) { if (created) node->flags = MAILBOX_NOCHILDREN; else node->flags &= ~MAILBOX_NONEXISTENT; if (!update_only) node->flags |= MAILBOX_MATCHED; node->flags |= flags; node_fix_parents(node); } else { i_assert(update_only); maildir_set_children(ctx, vname); } } return 0; } static int maildir_fill_readdir(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only) { struct mailbox_list *list = ctx->ctx.list; struct mail_namespace *ns = list->ns; DIR *dirp; struct dirent *d; const char *vname; int ret = 0; dirp = opendir(ctx->dir); if (dirp == NULL) { if (errno == EACCES) { mailbox_list_set_critical(list, "%s", mail_error_eacces_msg("opendir", ctx->dir)); } else if (errno != ENOENT) { mailbox_list_set_critical(list, "opendir(%s) failed: %m", ctx->dir); return -1; } return 0; } while ((d = readdir(dirp)) != NULL) { T_BEGIN { ret = maildir_fill_readdir_entry(ctx, glob, d, update_only); } T_END; if (ret < 0) break; } if (closedir(dirp) < 0) { mailbox_list_set_critical(list, "readdir(%s) failed: %m", ctx->dir); return -1; } if (ret < 0) return -1; if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* make sure INBOX is listed */ return maildir_fill_inbox(ctx, glob, "INBOX", update_only); } else if ((ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) { /* show shared INBOX. */ vname = mailbox_list_get_vname(ns->list, "INBOX"); return maildir_fill_inbox(ctx, glob, vname, update_only); } else { return 0; } } struct mailbox_list_iterate_context * maildir_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct maildir_mailbox_list *list = (struct maildir_mailbox_list *)_list; struct maildir_list_iterate_context *ctx; pool_t pool; char ns_sep = mail_namespace_get_sep(_list->ns); int ret; pool = pool_alloconly_create("mailbox list maildir iter", 1024); ctx = p_new(pool, struct maildir_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = _list; ctx->ctx.flags = flags; ctx->ctx.glob = imap_match_init_multiple(pool, patterns, TRUE, ns_sep); array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); ctx->tree_ctx = mailbox_tree_init(ns_sep); ctx->info.ns = _list->ns; ctx->prefix_char = strcmp(_list->name, MAILBOX_LIST_NAME_IMAPDIR) == 0 ? '\0' : list->sep; if (_list->set.iter_from_index_dir) ctx->dir = _list->set.index_dir; else ctx->dir = _list->set.root_dir; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* Listing only subscribed mailboxes. Flags are set later if needed. */ bool default_nonexistent = (flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0; mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree_ctx, default_nonexistent); } if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || (flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) { /* Add/update mailbox list with flags */ bool update_only = (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0; T_BEGIN { ret = maildir_fill_readdir(ctx, ctx->ctx.glob, update_only); } T_END; if (ret < 0) { ctx->ctx.failed = TRUE; return &ctx->ctx; } } ctx->tree_iter = mailbox_tree_iterate_init(ctx->tree_ctx, NULL, MAILBOX_MATCHED); return &ctx->ctx; } int maildir_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct maildir_list_iterate_context *ctx = (struct maildir_list_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; if (ctx->tree_iter != NULL) mailbox_tree_iterate_deinit(&ctx->tree_iter); mailbox_tree_deinit(&ctx->tree_ctx); pool_unref(&ctx->ctx.pool); return ret; } const struct mailbox_info * maildir_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct maildir_list_iterate_context *ctx = (struct maildir_list_iterate_context *)_ctx; struct mailbox_node *node; if (_ctx->failed) return NULL; node = mailbox_tree_iterate_next(ctx->tree_iter, &ctx->info.vname); if (node == NULL) return mailbox_list_iter_default_next(_ctx); ctx->info.flags = node->flags; if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0 && (_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) { /* we're listing all mailboxes but we want to know \Subscribed flags */ mailbox_list_set_subscription_flags(_ctx->list, ctx->info.vname, &ctx->info.flags); } return &ctx->info; } dovecot-2.2.33.2/src/lib-storage/list/Makefile.am0000644000175000017500000000211013123174404016324 00000000000000noinst_LTLIBRARIES = libstorage_list.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_list_la_SOURCES = \ mailbox-list-delete.c \ mailbox-list-fs.c \ mailbox-list-fs-flags.c \ mailbox-list-fs-iter.c \ mailbox-list-index.c \ mailbox-list-index-backend.c \ mailbox-list-index-iter.c \ mailbox-list-index-notify.c \ mailbox-list-index-status.c \ mailbox-list-index-sync.c \ mailbox-list-iter.c \ mailbox-list-maildir.c \ mailbox-list-maildir-iter.c \ mailbox-list-none.c \ mailbox-list-notify-tree.c \ mailbox-list-subscriptions.c \ subscription-file.c headers = \ mailbox-list-delete.h \ mailbox-list-fs.h \ mailbox-list-index.h \ mailbox-list-index-storage.h \ mailbox-list-index-sync.h \ mailbox-list-iter-private.h \ mailbox-list-maildir.h \ mailbox-list-notify-tree.h \ mailbox-list-subscriptions.h \ subscription-file.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/list/mailbox-list-iter.c0000644000175000017500000010232513165463624020025 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "imap-match.h" #include "mail-storage.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-private.h" #include "mailbox-list-iter-private.h" enum autocreate_match_result { /* list contains the mailbox */ AUTOCREATE_MATCH_RESULT_YES = 0x01, /* list contains children of the mailbox */ AUTOCREATE_MATCH_RESULT_CHILDREN = 0x02, /* list contains parents of the mailbox */ AUTOCREATE_MATCH_RESULT_PARENT = 0x04 }; struct ns_list_iterate_context { struct mailbox_list_iterate_context ctx; struct mailbox_list_iterate_context *backend_ctx; struct mail_namespace *namespaces, *cur_ns; struct mailbox_list *error_list; pool_t pool; const char **patterns, **patterns_ns_match; enum mail_namespace_type type_mask; struct mailbox_info ns_info; struct mailbox_info inbox_info; const struct mailbox_info *pending_backend_info; unsigned int cur_ns_prefix_sent:1; unsigned int inbox_list:1; unsigned int inbox_listed:1; }; static bool ns_match_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *pattern); static int mailbox_list_match_anything(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *prefix); static struct mailbox_list_iterate_context mailbox_list_iter_failed; struct mailbox_list_iterate_context * mailbox_list_iter_init(struct mailbox_list *list, const char *pattern, enum mailbox_list_iter_flags flags) { const char *patterns[2]; patterns[0] = pattern; patterns[1] = NULL; return mailbox_list_iter_init_multiple(list, patterns, flags); } int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list) { struct mail_namespace *ns = list->ns; if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) { /* no subscriptions in this namespace. find where they are. */ ns = mail_namespace_find_subscribable(ns->user->namespaces, ns->prefix); if (ns == NULL) { /* no subscriptions. avoid crashes by initializing a subscriptions tree. */ if (list->subscriptions == NULL) { char sep = mail_namespace_get_sep(list->ns); list->subscriptions = mailbox_tree_init(sep); } return 0; } } return ns->list->v.subscriptions_refresh(ns->list, list); } static struct mailbox_settings * mailbox_settings_add_ns_prefix(pool_t pool, struct mail_namespace *ns, struct mailbox_settings *in_set) { struct mailbox_settings *out_set; if (ns->prefix_len == 0 || strcasecmp(in_set->name, "INBOX") == 0) return in_set; out_set = p_new(pool, struct mailbox_settings, 1); *out_set = *in_set; if (*in_set->name == '\0') { /* namespace prefix itself */ out_set->name = p_strndup(pool, ns->prefix, ns->prefix_len-1); } else { out_set->name = p_strconcat(pool, ns->prefix, in_set->name, NULL); } return out_set; } static void mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->list->ns; struct mailbox_list_autocreate_iterate_context *actx; struct mailbox_settings *const *box_sets, *set; struct autocreate_box *autobox; unsigned int i, count; if (!array_is_created(&ns->set->mailboxes)) return; box_sets = array_get(&ns->set->mailboxes, &count); if (count == 0) return; actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1); ctx->autocreate_ctx = actx; hash_table_create(&actx->duplicate_vnames, ctx->pool, 0, str_hash, strcmp); /* build the list of mailboxes we need to consider as existing */ p_array_init(&actx->boxes, ctx->pool, 16); p_array_init(&actx->box_sets, ctx->pool, 16); p_array_init(&actx->all_ns_box_sets, ctx->pool, 16); for (i = 0; i < count; i++) { if (strcmp(box_sets[i]->autocreate, MAILBOX_SET_AUTO_NO) == 0) continue; set = mailbox_settings_add_ns_prefix(ctx->pool, ns, box_sets[i]); /* autocreate mailbox belongs to listed namespace */ array_append(&actx->all_ns_box_sets, &set, 1); if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0) { array_append(&actx->box_sets, &set, 1); autobox = array_append_space(&actx->boxes); autobox->name = set->name; autobox->set = set; if (strcasecmp(autobox->name, "INBOX") == 0) { /* make sure duplicate INBOX/Inbox/etc. won't get created */ autobox->name = "INBOX"; } } } } struct mailbox_list_iterate_context * mailbox_list_iter_init_multiple(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct mailbox_list_iterate_context *ctx; i_assert(*patterns != NULL); if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { if (mailbox_list_iter_subscriptions_refresh(list) < 0) return &mailbox_list_iter_failed; } ctx = list->v.iter_init(list, patterns, flags); if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) mailbox_list_iter_init_autocreate(ctx); return ctx; } static bool ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { if ((ctx->type_mask & ns->type) == 0) return FALSE; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { if (ns->alias_for != NULL) return FALSE; } return TRUE; } static bool ns_is_match_within_ns(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *prefix_without_sep, const char *pattern, enum imap_match_result result) { if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) { switch (result) { case IMAP_MATCH_YES: case IMAP_MATCH_CHILDREN: return TRUE; case IMAP_MATCH_NO: case IMAP_MATCH_PARENT: break; } return FALSE; } switch (result) { case IMAP_MATCH_YES: /* allow matching prefix only when it's done without wildcards */ if (strcmp(prefix_without_sep, pattern) == 0) return TRUE; break; case IMAP_MATCH_CHILDREN: { /* allow this only if there isn't another namespace with longer prefix that matches this pattern (namespaces are sorted by prefix length) */ struct mail_namespace *tmp; T_BEGIN { for (tmp = ns->next; tmp != NULL; tmp = tmp->next) { if (ns_match_simple(ctx, tmp) && ns_match_next(ctx, tmp, pattern)) break; } } T_END; if (tmp == NULL) return TRUE; break; } case IMAP_MATCH_NO: case IMAP_MATCH_PARENT: break; } return FALSE; } static bool list_pattern_has_wildcards(const char *pattern) { for (; *pattern != '\0'; pattern++) { if (*pattern == '%' || *pattern == '*') return TRUE; } return FALSE; } static bool ns_match_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *pattern) { struct imap_match_glob *glob; enum imap_match_result result; const char *prefix_without_sep; size_t len; len = ns->prefix_len; if (len > 0 && ns->prefix[len-1] == mail_namespace_get_sep(ns)) len--; if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) == 0) { /* non-listable namespace matches only with exact prefix */ if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0) return FALSE; /* with prefix="", list=no we don't want to show anything, except when the client explicitly lists a mailbox without wildcards (e.g. LIST "" mailbox). this is mainly useful for working around client bugs (and supporting a specific IMAP client behavior that's not exactly buggy but not very good IMAP behavior either). */ if (ns->prefix_len == 0 && list_pattern_has_wildcards(pattern)) return FALSE; } prefix_without_sep = t_strndup(ns->prefix, len); if (*prefix_without_sep == '\0') result = IMAP_MATCH_CHILDREN; else { glob = imap_match_init(pool_datastack_create(), pattern, TRUE, mail_namespace_get_sep(ns)); result = imap_match(glob, prefix_without_sep); } return ns_is_match_within_ns(ctx, ns, prefix_without_sep, pattern, result); } static bool mailbox_list_ns_match_patterns(struct ns_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->cur_ns; unsigned int i; if (!ns_match_simple(ctx, ns)) return FALSE; /* filter out namespaces whose prefix doesn't match. this same code handles both with and without STAR_WITHIN_NS, so the "without" case is slower than necessary, but this shouldn't matter much */ T_BEGIN { for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) { if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i])) break; } } T_END; return ctx->patterns_ns_match[i] != NULL; } static bool iter_next_try_prefix_pattern(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *pattern) { struct imap_match_glob *glob; enum imap_match_result result; const char *prefix_without_sep; i_assert(ns->prefix_len > 0); if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) == 0) { /* non-listable namespace matches only with exact prefix */ if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0) return FALSE; } prefix_without_sep = t_strndup(ns->prefix, ns->prefix_len-1); glob = imap_match_init(pool_datastack_create(), pattern, TRUE, mail_namespace_get_sep(ns)); result = imap_match(glob, prefix_without_sep); return result == IMAP_MATCH_YES && ns_is_match_within_ns(ctx, ns, prefix_without_sep, pattern, result); } static bool mailbox_list_ns_prefix_match(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { unsigned int i; bool ret = FALSE; for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) { T_BEGIN { ret = iter_next_try_prefix_pattern(ctx, ns, ctx->patterns_ns_match[i]); } T_END; if (ret) break; } return ret; } static int ns_prefix_is_visible(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { int ret; if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0) return 1; if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) { if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) != 0) return ret; } return 0; } static int ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context *ctx, const char *prefix) { struct mail_namespace *ns; size_t prefix_len = strlen(prefix); int ret; for (ns = ctx->namespaces; ns != NULL; ns = ns->next) { if (ns->prefix_len > prefix_len && strncmp(ns->prefix, prefix, prefix_len) == 0) { ret = ns_prefix_is_visible(ctx, ns); if (ret != 0) return ret; } } return 0; } static bool mailbox_ns_prefix_is_shared_inbox(struct mail_namespace *ns) { return ns->type == MAIL_NAMESPACE_TYPE_SHARED && (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && !ns->list->mail_set->mail_shared_explicit_inbox; } static bool mailbox_is_shared_inbox(struct mail_namespace *ns, const char *vname) { return mailbox_ns_prefix_is_shared_inbox(ns) && strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 && vname[ns->prefix_len-1] == '\0'; } static int mailbox_list_match_anything(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *prefix) { enum mailbox_list_iter_flags list_flags = MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct mailbox_list_iterate_context *list_iter; const struct mailbox_info *info; const char *pattern; int ret; if ((ret = ns_prefix_has_visible_child_namespace(ctx, prefix)) != 0) return ret; pattern = t_strconcat(prefix, "%", NULL); list_iter = mailbox_list_iter_init(ns->list, pattern, list_flags); info = mailbox_list_iter_next(list_iter); if (info != NULL && mailbox_ns_prefix_is_shared_inbox(ns) && mailbox_is_shared_inbox(ns, info->vname)) { /* we don't want to see this, try the next one */ info = mailbox_list_iter_next(list_iter); } ret = info != NULL ? 1 : 0; if (mailbox_list_iter_deinit(&list_iter) < 0) { if (ret == 0) ret = -1; } return ret; } static bool mailbox_ns_prefix_check_selection_criteria(struct ns_list_iterate_context *ctx) { if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { if ((ctx->ns_info.flags & MAILBOX_SUBSCRIBED) != 0) return TRUE; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 && (ctx->ns_info.flags & MAILBOX_CHILD_SUBSCRIBED) != 0) return TRUE; return FALSE; } return TRUE; } static bool mailbox_list_ns_prefix_return(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, bool has_children) { struct mailbox *box; enum mailbox_existence existence; int ret; if (strncasecmp(ns->prefix, "INBOX", 5) == 0 && ns->prefix[5] == mail_namespace_get_sep(ns)) { /* prefix=INBOX/ (or prefix=INBOX/something/) namespace exists. so we can create children to INBOX. */ ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS; } if (ns->prefix_len == 0 || !mailbox_list_ns_prefix_match(ctx, ns)) return FALSE; i_zero(&ctx->ns_info); ctx->ns_info.ns = ns; ctx->ns_info.vname = p_strndup(ctx->pool, ns->prefix, ns->prefix_len-1); if (ns->special_use_mailboxes) ctx->ns_info.flags |= MAILBOX_CHILD_SPECIALUSE; if (strcasecmp(ctx->ns_info.vname, "INBOX") == 0) { i_assert(!ctx->inbox_listed); ctx->inbox_listed = TRUE; ctx->ns_info.flags |= ctx->inbox_info.flags | MAILBOX_SELECT; } if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RETURN_SUBSCRIBED | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) { mailbox_list_set_subscription_flags(ns->list, ctx->ns_info.vname, &ctx->ns_info.flags); } if (!mailbox_ns_prefix_check_selection_criteria(ctx)) return FALSE; /* see if the namespace has children */ if (has_children) ctx->ns_info.flags |= MAILBOX_CHILDREN; else if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 || (ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) { /* need to check this explicitly */ if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) > 0) ctx->ns_info.flags |= MAILBOX_CHILDREN; else if (ret == 0) { if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 && !mailbox_ns_prefix_is_shared_inbox(ns)) { /* no children -> not visible */ return FALSE; } ctx->ns_info.flags |= MAILBOX_NOCHILDREN; } } if ((ctx->ns_info.flags & MAILBOX_SELECT) == 0) { /* see if namespace prefix is selectable */ box = mailbox_alloc(ns->list, ctx->ns_info.vname, 0); if (mailbox_exists(box, TRUE, &existence) == 0 && existence == MAILBOX_EXISTENCE_SELECT) ctx->ns_info.flags |= MAILBOX_SELECT; else ctx->ns_info.flags |= MAILBOX_NONEXISTENT; mailbox_free(&box); } return TRUE; } static void inbox_set_children_flags(struct ns_list_iterate_context *ctx) { const char *prefix; int ret; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0) return; if ((ctx->inbox_info.flags & (MAILBOX_CHILDREN | MAILBOX_NOINFERIORS | MAILBOX_NOCHILDREN)) != 0) return; if (mail_namespace_find_prefix(ctx->namespaces, "") == NULL) { /* prefix="" namespace doesn't exist, and neither does anything beginning with prefix=INBOX/ (we checked this earlier). there's no way to create children for INBOX. */ ctx->inbox_info.flags |= MAILBOX_NOINFERIORS; return; } /* INBOX namespace doesn't exist and we didn't see any children listed for INBOX. this could be because there truly aren't any children, or that the list patterns just didn't match them. */ prefix = t_strdup_printf("INBOX%c", mail_namespace_get_sep(ctx->inbox_info.ns)); ret = mailbox_list_match_anything(ctx, ctx->inbox_info.ns, prefix); if (ret > 0) ctx->inbox_info.flags |= MAILBOX_CHILDREN; else if (ret == 0) ctx->inbox_info.flags |= MAILBOX_NOCHILDREN; } static void mailbox_list_ns_iter_failed(struct ns_list_iterate_context *ctx) { enum mail_error error; const char *errstr; if (ctx->cur_ns->list != ctx->error_list) { errstr = mailbox_list_get_last_error(ctx->cur_ns->list, &error); mailbox_list_set_error(ctx->error_list, error, errstr); } ctx->ctx.failed = TRUE; } static bool mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context *_ctx, const struct mailbox_info **info_r) { struct ns_list_iterate_context *ctx = (struct ns_list_iterate_context *)_ctx; struct mail_namespace *ns; const struct mailbox_info *info; bool has_children; if (ctx->cur_ns == NULL) { if (!ctx->inbox_listed && ctx->inbox_list && !_ctx->failed) { /* send delayed INBOX reply */ ctx->inbox_listed = TRUE; inbox_set_children_flags(ctx); *info_r = &ctx->inbox_info; return TRUE; } *info_r = NULL; return TRUE; } if (ctx->backend_ctx == NULL) { i_assert(ctx->pending_backend_info == NULL); if (!mailbox_list_ns_match_patterns(ctx)) { /* namespace's children don't match the patterns, but the namespace prefix itself might */ ns = ctx->cur_ns; ctx->cur_ns = ctx->cur_ns->next; if (mailbox_list_ns_prefix_return(ctx, ns, FALSE)) { *info_r = &ctx->ns_info; return TRUE; } return FALSE; } /* start listing this namespace's mailboxes */ ctx->backend_ctx = mailbox_list_iter_init_multiple(ctx->cur_ns->list, ctx->patterns, _ctx->flags); ctx->cur_ns_prefix_sent = FALSE; } if (ctx->pending_backend_info == NULL) info = mailbox_list_iter_next(ctx->backend_ctx); else { info = ctx->pending_backend_info; ctx->pending_backend_info = NULL; } if (!ctx->cur_ns_prefix_sent) { /* delayed sending of namespace prefix */ ctx->cur_ns_prefix_sent = TRUE; has_children = info != NULL && !mailbox_is_shared_inbox(info->ns, info->vname); if (mailbox_list_ns_prefix_return(ctx, ctx->cur_ns, has_children)) { ctx->pending_backend_info = info; *info_r = &ctx->ns_info; return TRUE; } } if (info != NULL) { if (strcasecmp(info->vname, "INBOX") == 0 && ctx->inbox_list) { /* delay sending INBOX reply. we already saved its flags at init stage, except for \Noinferiors and subscription states */ ctx->inbox_info.flags |= (info->flags & (MAILBOX_NOINFERIORS | MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED)); return FALSE; } if (strncasecmp(info->vname, "INBOX", 5) == 0 && info->vname[5] == mail_namespace_get_sep(info->ns)) { /* we know now that INBOX has children */ ctx->inbox_info.flags |= MAILBOX_CHILDREN; ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS; } if (info->ns->prefix_len > 0 && strncmp(info->vname, info->ns->prefix, info->ns->prefix_len-1) == 0 && info->vname[info->ns->prefix_len-1] == '\0') { /* this is an entry for namespace prefix, which we already returned. (e.g. shared/$user/INBOX entry returned as shared/$user, or when listing subscribed namespace prefix). */ return FALSE; } *info_r = info; return TRUE; } /* finished with this namespace */ if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) mailbox_list_ns_iter_failed(ctx); ctx->cur_ns = ctx->cur_ns->next; return FALSE; } static const struct mailbox_info * mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx) { const struct mailbox_info *info = NULL; while (!mailbox_list_ns_iter_try_next(_ctx, &info)) ; return info; } static int mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct ns_list_iterate_context *ctx = (struct ns_list_iterate_context *)_ctx; int ret; if (ctx->backend_ctx != NULL) { if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) mailbox_list_ns_iter_failed(ctx); } ret = _ctx->failed ? -1 : 0; pool_unref(&ctx->pool); return ret; } static const char ** dup_patterns_without_stars(pool_t pool, const char *const *patterns, unsigned int count) { const char **dup; unsigned int i; dup = p_new(pool, const char *, count + 1); for (i = 0; i < count; i++) { char *p = p_strdup(pool, patterns[i]); dup[i] = p; for (; *p != '\0'; p++) { if (*p == '*') *p = '%'; } } return dup; } static bool patterns_match_inbox(struct mail_namespace *namespaces, const char *const *patterns) { struct mail_namespace *ns = mail_namespace_find_inbox(namespaces); struct imap_match_glob *glob; glob = imap_match_init_multiple(pool_datastack_create(), patterns, TRUE, mail_namespace_get_sep(ns)); return imap_match(glob, "INBOX") == IMAP_MATCH_YES; } static int inbox_info_init(struct ns_list_iterate_context *ctx, struct mail_namespace *namespaces) { enum mailbox_info_flags flags; int ret; ctx->inbox_info.vname = "INBOX"; ctx->inbox_info.ns = mail_namespace_find_inbox(namespaces); i_assert(ctx->inbox_info.ns != NULL); if ((ret = mailbox_list_mailbox(ctx->inbox_info.ns->list, "INBOX", &flags)) > 0) ctx->inbox_info.flags = flags; return ret; } struct mailbox_list_iterate_context * mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, const char *const *patterns, enum mail_namespace_type type_mask, enum mailbox_list_iter_flags flags) { struct ns_list_iterate_context *ctx; unsigned int i, count; pool_t pool; i_assert(namespaces != NULL); pool = pool_alloconly_create("mailbox list namespaces", 1024); ctx = p_new(pool, struct ns_list_iterate_context, 1); ctx->pool = pool; ctx->type_mask = type_mask; ctx->ctx.flags = flags; ctx->ctx.list = p_new(pool, struct mailbox_list, 1); ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next; ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit; ctx->namespaces = namespaces; ctx->error_list = namespaces->list; count = str_array_length(patterns); ctx->patterns = p_new(pool, const char *, count + 1); for (i = 0; i < count; i++) ctx->patterns[i] = p_strdup(pool, patterns[i]); if (patterns_match_inbox(namespaces, ctx->patterns) && (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) { /* we're going to list the INBOX. get its own flags (i.e. not [no]children) immediately, so if we end up seeing something else called INBOX (e.g. namespace prefix) we can show it immediately with the proper flags. */ ctx->inbox_list = TRUE; if (inbox_info_init(ctx, namespaces) < 0) { pool_unref(&pool); return &mailbox_list_iter_failed; } } if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) { /* create copies of patterns with '*' wildcard changed to '%'. this is used only when checking which namespaces to list */ ctx->patterns_ns_match = dup_patterns_without_stars(pool, ctx->patterns, count); } else { ctx->patterns_ns_match = ctx->patterns; } ctx->cur_ns = namespaces; ctx->ctx.list->ns = namespaces; return &ctx->ctx; } static enum autocreate_match_result autocreate_box_match(const ARRAY_TYPE(mailbox_settings) *boxes, struct mail_namespace *ns, const char *name, bool only_subscribed, unsigned int *idx_r) { struct mailbox_settings *const *sets; unsigned int i, count; size_t len, name_len = strlen(name); enum autocreate_match_result result = 0; char sep = mail_namespace_get_sep(ns); *idx_r = UINT_MAX; sets = array_get(boxes, &count); for (i = 0; i < count; i++) { if (only_subscribed && strcmp(sets[i]->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0) continue; len = I_MIN(name_len, strlen(sets[i]->name)); if (strncmp(name, sets[i]->name, len) != 0) continue; if (name[len] == '\0' && sets[i]->name[len] == '\0') { result |= AUTOCREATE_MATCH_RESULT_YES; *idx_r = i; } else if (name[len] == '\0' && sets[i]->name[len] == sep) result |= AUTOCREATE_MATCH_RESULT_CHILDREN; else if (name[len] == sep && sets[i]->name[len] == '\0') result |= AUTOCREATE_MATCH_RESULT_PARENT; } return result; } const struct mailbox_info * mailbox_list_iter_autocreate_filter(struct mailbox_list_iterate_context *ctx, const struct mailbox_info *_info) { struct mailbox_list_autocreate_iterate_context *actx = ctx->autocreate_ctx; if (actx == NULL || _info == NULL) return _info; actx->new_info = *_info; struct mailbox_info *info = &actx->new_info; enum autocreate_match_result match, match2; unsigned int idx; match = autocreate_box_match(&actx->box_sets, ctx->list->ns, info->vname, FALSE, &idx); if (!actx->listing_autoboxes) { if ((match & AUTOCREATE_MATCH_RESULT_YES) != 0) { /* we have an exact match in the list. don't list it at the end. */ array_delete(&actx->boxes, idx, 1); array_delete(&actx->box_sets, idx, 1); } if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0 && hash_table_lookup(actx->duplicate_vnames, info->vname) == NULL) { /* Prevent autocreate-iteration from adding this mailbox as a duplicate. For example we're listing % and we're here because "foo" was found. However, there's also "foo/bar" with auto=create. We're telling here to the autocreate iteration code that "foo" was already found and it doesn't need to add it again. */ char *vname = p_strdup(ctx->pool, info->vname); hash_table_insert(actx->duplicate_vnames, vname, vname); } } if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) { if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) info->flags |= MAILBOX_CHILD_SUBSCRIBED; else { info->flags &= ~MAILBOX_NOCHILDREN; info->flags |= MAILBOX_CHILDREN; } } /* make sure the mailbox existence flags are correct. */ if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) match2 = match; else { match2 = autocreate_box_match(&actx->all_ns_box_sets, ctx->list->ns, info->vname, FALSE, &idx); } if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) info->flags &= ~MAILBOX_NONEXISTENT; if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) { info->flags &= ~MAILBOX_NOCHILDREN; info->flags |= MAILBOX_CHILDREN; } if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 && (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) { /* we're listing all mailboxes and want \Subscribed flag */ match2 = autocreate_box_match(&actx->all_ns_box_sets, ctx->list->ns, info->vname, TRUE, &idx); if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) { /* mailbox is also marked as autosubscribe */ info->flags |= MAILBOX_SUBSCRIBED; } if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) { /* mailbox also has a children marked as autosubscribe */ info->flags |= MAILBOX_CHILD_SUBSCRIBED; } } if ((match & AUTOCREATE_MATCH_RESULT_PARENT) != 0) { /* there are autocreate parent boxes. set their children flag states. */ struct autocreate_box *autobox; size_t name_len; char sep = mail_namespace_get_sep(ctx->list->ns); array_foreach_modifiable(&actx->boxes, autobox) { name_len = strlen(autobox->name); if (strncmp(info->vname, autobox->name, name_len) != 0 || info->vname[name_len] != sep) continue; if ((info->flags & MAILBOX_NONEXISTENT) == 0) autobox->flags |= MAILBOX_CHILDREN; if ((info->flags & MAILBOX_SUBSCRIBED) != 0) autobox->flags |= MAILBOX_CHILD_SUBSCRIBED; autobox->child_listed = TRUE; } } return info; } static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx, const struct autocreate_box *autobox) { struct mailbox_list_autocreate_iterate_context *actx = ctx->autocreate_ctx; enum imap_match_result match; i_zero(&actx->new_info); actx->new_info.ns = ctx->list->ns; actx->new_info.vname = autobox->name; actx->new_info.flags = autobox->flags; if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) actx->new_info.flags |= MAILBOX_SUBSCRIBED; if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0) { if ((ctx->list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0 && ctx->list->set.maildir_name[0] == '\0') { /* mailbox format using files (e.g. mbox) without DIRNAME specified */ actx->new_info.flags |= MAILBOX_NOINFERIORS; } else { actx->new_info.flags |= MAILBOX_NOCHILDREN; } } match = imap_match(ctx->glob, actx->new_info.vname); if (match == IMAP_MATCH_YES) { actx->new_info.special_use = *autobox->set->special_use == '\0' ? NULL : autobox->set->special_use; return TRUE; } if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) { enum mailbox_info_flags old_flags = actx->new_info.flags; char sep = mail_namespace_get_sep(ctx->list->ns); const char *p; char *vname; /* e.g. autocreate=foo/bar and we're listing % */ actx->new_info.flags = MAILBOX_NONEXISTENT | (old_flags & (MAILBOX_CHILDREN | MAILBOX_CHILD_SUBSCRIBED)); if ((old_flags & MAILBOX_NONEXISTENT) == 0) { actx->new_info.flags |= MAILBOX_CHILDREN; actx->new_info.flags &= ~MAILBOX_NOCHILDREN; } if ((old_flags & MAILBOX_SUBSCRIBED) != 0) actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED; do { p = strrchr(actx->new_info.vname, sep); i_assert(p != NULL); actx->new_info.vname = vname = p_strdup_until(ctx->pool, actx->new_info.vname, p); match = imap_match(ctx->glob, actx->new_info.vname); } while (match != IMAP_MATCH_YES); if (hash_table_lookup(actx->duplicate_vnames, vname) == NULL) { hash_table_insert(actx->duplicate_vnames, vname, vname); return TRUE; } } return FALSE; } static const struct mailbox_info * mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx) { const struct mailbox_info *info; const struct mailbox_settings *set; info = ctx->list->v.iter_next(ctx); if (info == NULL) return NULL; ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE; if ((ctx->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) { set = mailbox_settings_find(ctx->list->ns, info->vname); if (set != NULL && *set->special_use != '\0') { ctx->specialuse_info = *info; ctx->specialuse_info.special_use = *set->special_use == '\0' ? NULL : set->special_use; info = &ctx->specialuse_info; } } return mailbox_list_iter_autocreate_filter(ctx, info); } const struct mailbox_info * mailbox_list_iter_default_next(struct mailbox_list_iterate_context *ctx) { struct mailbox_list_autocreate_iterate_context *actx = ctx->autocreate_ctx; const struct autocreate_box *autoboxes, *autobox; unsigned int count; if (actx == NULL) return NULL; /* do not drop boxes anymore */ actx->listing_autoboxes = TRUE; /* list missing mailboxes */ autoboxes = array_get(&actx->boxes, &count); while (actx->idx < count) { autobox = &autoboxes[actx->idx++]; if (autocreate_iter_autobox(ctx, autobox)) return &actx->new_info; } i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets)); return NULL; } static bool special_use_selection(struct mailbox_list_iterate_context *ctx, const struct mailbox_info *info) { if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 && (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) != 0) { /* LIST (SPECIAL-USE RECURSIVEMATCH) used. for now we support this only for namespace prefixes */ if ((info->flags & MAILBOX_CHILD_SPECIALUSE) != 0) return TRUE; } return (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) == 0 || info->special_use != NULL; } const struct mailbox_info * mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx) { const struct mailbox_info *info; if (ctx == &mailbox_list_iter_failed) return NULL; do { T_BEGIN { info = mailbox_list_iter_next_call(ctx); } T_END; } while (info != NULL && !special_use_selection(ctx, info)); return info; } int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx) { struct mailbox_list_iterate_context *ctx = *_ctx; *_ctx = NULL; if (ctx == &mailbox_list_iter_failed) return -1; if (ctx->autocreate_ctx != NULL) hash_table_destroy(&ctx->autocreate_ctx->duplicate_vnames); return ctx->list->v.iter_deinit(ctx); } static void node_fix_parents(struct mailbox_node *node) { /* If we happened to create any of the parents, we need to mark them nonexistent. */ node = node->parent; for (; node != NULL; node = node->parent) { if ((node->flags & MAILBOX_MATCHED) == 0) node->flags |= MAILBOX_NONEXISTENT; } } static void mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx, const char *name) { struct mail_namespace *ns = ctx->iter_ctx->list->ns; struct mailbox_node *node; enum mailbox_info_flags create_flags, always_flags; enum imap_match_result match; const char *p; bool created, add_matched; create_flags = MAILBOX_NOCHILDREN; always_flags = ctx->leaf_flags; add_matched = TRUE; for (;;) { created = FALSE; match = imap_match(ctx->glob, name); if (match == IMAP_MATCH_YES) { node = ctx->update_only ? mailbox_tree_lookup(ctx->tree_ctx, name) : mailbox_tree_get(ctx->tree_ctx, name, &created); if (created) { node->flags = create_flags; if (create_flags != 0) node_fix_parents(node); } if (node != NULL) { if (!ctx->update_only && add_matched) node->flags |= MAILBOX_MATCHED; if ((always_flags & MAILBOX_CHILDREN) != 0) node->flags &= ~MAILBOX_NOCHILDREN; node->flags |= always_flags; } /* We don't want to show the parent mailboxes unless something else matches them, but if they are matched we want to show them having child subscriptions */ add_matched = FALSE; } else { if ((match & IMAP_MATCH_PARENT) == 0) break; /* We've a (possibly) non-subscribed parent mailbox which has a subscribed child mailbox. Make sure we return the parent mailbox. */ } if (!ctx->match_parents) break; /* see if parent matches */ p = strrchr(name, mail_namespace_get_sep(ns)); if (p == NULL) break; name = t_strdup_until(name, p); create_flags |= MAILBOX_NONEXISTENT; create_flags &= ~MAILBOX_NOCHILDREN; always_flags = MAILBOX_CHILDREN | ctx->parent_flags; } } void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx, const char *name) { T_BEGIN { mailbox_list_iter_update_real(ctx, name); } T_END; } dovecot-2.2.33.2/src/lib-storage/mail-search-register-human.c0000644000175000017500000001451713165463624020627 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "unichar.h" #include "settings-parser.h" #include "mail-storage.h" #include "mail-search-register.h" #include "mail-search-parser.h" #include "mail-search-build.h" #include #include struct mail_search_register *mail_search_register_human; static struct mail_search_arg * human_search_or(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; /* this changes the parent arg to be an OR block instead of AND block */ ctx->parent->type = SEARCH_OR; if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0) return NULL; return sarg; } static struct mail_search_arg * arg_new_human_date(struct mail_search_build_context *ctx, enum mail_search_arg_type type, enum mail_search_date_type date_type) { struct mail_search_arg *sarg; const char *value; bool utc; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (mail_parse_human_timestamp(value, &sarg->value.time, &utc) < 0) sarg->value.time = (time_t)-1; if (utc) sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ; if (sarg->value.time == (time_t)-1) { ctx->_error = p_strconcat(ctx->pool, "Invalid search date parameter: ", value, NULL); return NULL; } sarg->value.date_type = date_type; return sarg; } #define CALLBACK_DATE(_func, _type, _date_type) \ static struct mail_search_arg *\ human_search_##_func(struct mail_search_build_context *ctx) \ { \ return arg_new_human_date(ctx, _type, _date_type); \ } CALLBACK_DATE(before, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(on, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(since, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_RECEIVED) CALLBACK_DATE(sentbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(senton, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(sentsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SENT) CALLBACK_DATE(savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED) CALLBACK_DATE(savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED) CALLBACK_DATE(savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED) static struct mail_search_arg * arg_new_human_size(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *sarg; const char *value, *error; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; if (settings_get_size(value, &sarg->value.size, &error) < 0) { ctx->_error = p_strdup(ctx->pool, error); return NULL; } return sarg; } static struct mail_search_arg * human_search_larger(struct mail_search_build_context *ctx) { return arg_new_human_size(ctx, SEARCH_LARGER); } static struct mail_search_arg * human_search_smaller(struct mail_search_build_context *ctx) { return arg_new_human_size(ctx, SEARCH_SMALLER); } static struct mail_search_arg * human_search_guid(struct mail_search_build_context *ctx) { return mail_search_build_str(ctx, SEARCH_GUID); } static struct mail_search_arg * human_search_mailbox(struct mail_search_build_context *ctx) { struct mail_search_arg *sarg; sarg = mail_search_build_str(ctx, SEARCH_MAILBOX); if (sarg == NULL) return NULL; if (strchr(sarg->value.str, '*') != NULL || strchr(sarg->value.str, '%') != NULL) sarg->type = SEARCH_MAILBOX_GLOB; if (!uni_utf8_str_is_valid(sarg->value.str)) { ctx->_error = p_strconcat(ctx->pool, "Mailbox name not valid UTF-8: ", sarg->value.str, NULL); return NULL; } return sarg; } static struct mail_search_arg * human_search_mailbox_guid(struct mail_search_build_context *ctx) { return mail_search_build_str(ctx, SEARCH_MAILBOX_GUID); } static struct mail_search_arg * human_search_oldestonly(struct mail_search_build_context *ctx) { ctx->args->stop_on_nonmatch = TRUE; return mail_search_build_new(ctx, SEARCH_ALL); } static const struct mail_search_register_arg human_register_args[] = { { "OR", human_search_or }, /* dates */ { "BEFORE", human_search_before }, { "ON", human_search_on }, { "SINCE", human_search_since }, { "SENTBEFORE", human_search_sentbefore }, { "SENTON", human_search_senton }, { "SENTSINCE", human_search_sentsince }, { "SAVEDBEFORE", human_search_savedbefore }, { "SAVEDON", human_search_savedon }, { "SAVEDSINCE", human_search_savedsince }, { "X-SAVEDBEFORE", human_search_savedbefore }, { "X-SAVEDON", human_search_savedon }, { "X-SAVEDSINCE", human_search_savedsince }, /* sizes */ { "LARGER", human_search_larger }, { "SMALLER", human_search_smaller }, /* Other Dovecot extensions: */ { "GUID", human_search_guid }, { "MAILBOX", human_search_mailbox }, { "MAILBOX-GUID", human_search_mailbox_guid }, { "OLDESTONLY", human_search_oldestonly } }; static struct mail_search_register * mail_search_register_init_human(struct mail_search_register *imap_register) { struct mail_search_register *reg; mail_search_register_fallback_t *fallback; ARRAY(struct mail_search_register_arg) copy_args; const struct mail_search_register_arg *human_args, *imap_args; unsigned int i, j, human_count, imap_count; int ret; reg = mail_search_register_init(); mail_search_register_add(reg, human_register_args, N_ELEMENTS(human_register_args)); /* find and register args in imap that don't exist in human */ imap_args = mail_search_register_get(imap_register, &imap_count); human_args = mail_search_register_get(reg, &human_count); t_array_init(©_args, imap_count); for (i = j = 0; i < imap_count && j < human_count; ) { ret = strcmp(imap_args[i].key, human_args[j].key); if (ret < 0) { array_append(©_args, &imap_args[i], 1); i++; } else if (ret > 0) { j++; } else { i++; j++; } } for (; i < imap_count; i++) array_append(©_args, &imap_args[i], 1); imap_args = array_get(©_args, &imap_count); mail_search_register_add(reg, imap_args, imap_count); if (mail_search_register_get_fallback(imap_register, &fallback)) mail_search_register_fallback(reg, fallback); return reg; } struct mail_search_register *mail_search_register_get_human(void) { if (mail_search_register_human == NULL) { struct mail_search_register *imap_reg = mail_search_register_get_imap(); mail_search_register_human = mail_search_register_init_human(imap_reg); } return mail_search_register_human; } dovecot-2.2.33.2/src/lib-storage/mail-user.c0000644000175000017500000004464313165463624015413 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hostpid.h" #include "ioloop.h" #include "net.h" #include "module-dir.h" #include "home-expand.h" #include "file-create-locked.h" #include "safe-mkstemp.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "settings-parser.h" #include "iostream-ssl.h" #include "fs-api.h" #include "auth-master.h" #include "master-service.h" #include "dict.h" #include "mail-storage-settings.h" #include "mail-storage-private.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mailbox-list-private.h" #include "mail-autoexpunge.h" #include "mail-user.h" struct mail_user_module_register mail_user_module_register = { 0 }; struct auth_master_connection *mail_user_auth_master_conn; static void mail_user_deinit_base(struct mail_user *user) { if (user->_attr_dict != NULL) { (void)dict_wait(user->_attr_dict); dict_deinit(&user->_attr_dict); } mail_namespaces_deinit(&user->namespaces); if (user->_service_user != NULL) mail_storage_service_user_unref(&user->_service_user); } static void mail_user_deinit_pre_base(struct mail_user *user ATTR_UNUSED) { } static void mail_user_stats_fill_base(struct mail_user *user ATTR_UNUSED, struct stats *stats ATTR_UNUSED) { } static struct mail_user * mail_user_alloc_int(const char *username, const struct setting_parser_info *set_info, const struct mail_user_settings *set, pool_t pool) { struct mail_user *user; const char *error; i_assert(username != NULL); i_assert(*username != '\0'); user = p_new(pool, struct mail_user, 1); user->pool = pool; user->refcount = 1; user->username = p_strdup(pool, username); user->set_info = set_info; user->unexpanded_set = set; user->set = settings_dup_with_pointers(set_info, user->unexpanded_set, pool); user->service = master_service_get_name(master_service); user->default_normalizer = uni_utf8_to_decomposed_titlecase; user->session_create_time = ioloop_time; /* check settings so that the duplicated structure will again contain the parsed fields */ if (!settings_check(set_info, pool, user->set, &error)) i_panic("Settings check unexpectedly failed: %s", error); user->v.deinit = mail_user_deinit_base; user->v.deinit_pre = mail_user_deinit_pre_base; user->v.stats_fill = mail_user_stats_fill_base; p_array_init(&user->module_contexts, user->pool, 5); return user; } struct mail_user * mail_user_alloc_nodup_set(const char *username, const struct setting_parser_info *set_info, const struct mail_user_settings *set) { pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"mail user", 16*1024); return mail_user_alloc_int(username, set_info, set, pool); } struct mail_user *mail_user_alloc(const char *username, const struct setting_parser_info *set_info, const struct mail_user_settings *set) { pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"mail user", 16*1024); return mail_user_alloc_int(username, set_info, settings_dup(set_info, set, pool), pool); } static void mail_user_expand_plugins_envs(struct mail_user *user) { const char **envs, *home; string_t *str; unsigned int i, count; if (!array_is_created(&user->set->plugin_envs)) return; str = t_str_new(256); envs = array_get_modifiable(&user->set->plugin_envs, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (user->_home == NULL && var_has_key(envs[i+1], 'h', "home") && mail_user_get_home(user, &home) <= 0) { user->error = p_strdup_printf(user->pool, "userdb didn't return a home directory, " "but plugin setting %s used it (%%h): %s", envs[i], envs[i+1]); return; } str_truncate(str, 0); var_expand_with_funcs(str, envs[i+1], mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); envs[i+1] = p_strdup(user->pool, str_c(str)); } } int mail_user_init(struct mail_user *user, const char **error_r) { const struct mail_storage_settings *mail_set; const char *home, *key, *value; bool need_home_dir; need_home_dir = user->_home == NULL && settings_vars_have_key(user->set_info, user->set, 'h', "home", &key, &value); if (need_home_dir && mail_user_get_home(user, &home) <= 0) { user->error = p_strdup_printf(user->pool, "userdb didn't return a home directory, " "but %s used it (%%h): %s", key, value); } /* expand settings after we can expand %h */ settings_var_expand_with_funcs(user->set_info, user->set, user->pool, mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); user->settings_expanded = TRUE; mail_user_expand_plugins_envs(user); /* autocreated users for shared mailboxes need to be fully initialized if they don't exist, since they're going to be used anyway */ if (user->error == NULL || user->nonexistent) { mail_set = mail_user_set_get_storage_set(user); user->mail_debug = mail_set->mail_debug; user->initialized = TRUE; hook_mail_user_created(user); } if (user->error != NULL) { *error_r = t_strdup(user->error); return -1; } return 0; } void mail_user_ref(struct mail_user *user) { i_assert(user->refcount > 0); user->refcount++; } void mail_user_unref(struct mail_user **_user) { struct mail_user *user = *_user; i_assert(user->refcount > 0); *_user = NULL; if (user->refcount > 1) { user->refcount--; return; } user->deinitializing = TRUE; /* call deinit() and deinit_pre() with refcount=1, otherwise we may assert-crash in mail_user_ref() that is called by some handlers. */ user->v.deinit_pre(user); user->v.deinit(user); i_assert(user->refcount == 1); pool_unref(&user->pool); } struct mail_user *mail_user_find(struct mail_user *user, const char *name) { struct mail_namespace *ns; for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->owner != NULL && strcmp(ns->owner->username, name) == 0) return ns->owner; } return NULL; } void mail_user_set_vars(struct mail_user *user, const char *service, const struct ip_addr *local_ip, const struct ip_addr *remote_ip) { i_assert(service != NULL); user->service = p_strdup(user->pool, service); if (local_ip != NULL && local_ip->family != 0) { user->local_ip = p_new(user->pool, struct ip_addr, 1); *user->local_ip = *local_ip; } if (remote_ip != NULL && remote_ip->family != 0) { user->remote_ip = p_new(user->pool, struct ip_addr, 1); *user->remote_ip = *remote_ip; } } const struct var_expand_table * mail_user_var_expand_table(struct mail_user *user) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 's', NULL, "service" }, { 'h', NULL, "home" }, { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 'i', NULL, "uid" }, { '\0', NULL, "gid" }, { '\0', NULL, "session" }, { '\0', NULL, "auth_user" }, { '\0', NULL, "auth_username" }, { '\0', NULL, "auth_domain" }, /* NOTE: keep this synced with imap-hibernate's imap_client_var_expand_table() */ { '\0', NULL, NULL } }; struct var_expand_table *tab; /* use a cached table, unless home directory has been set afterwards */ if (user->var_expand_table != NULL && user->var_expand_table[4].value == user->_home) return user->var_expand_table; tab = p_malloc(user->pool, sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = user->username; tab[1].value = p_strdup(user->pool, t_strcut(user->username, '@')); tab[2].value = strchr(user->username, '@'); if (tab[2].value != NULL) tab[2].value++; tab[3].value = user->service; tab[4].value = user->_home; /* don't look it up unless we need it */ tab[5].value = user->local_ip == NULL ? NULL : p_strdup(user->pool, net_ip2addr(user->local_ip)); tab[6].value = user->remote_ip == NULL ? NULL : p_strdup(user->pool, net_ip2addr(user->remote_ip)); tab[7].value = my_pid; tab[8].value = p_strdup(user->pool, dec2str(user->uid)); tab[9].value = p_strdup(user->pool, dec2str(user->gid)); tab[10].value = user->session_id; if (user->auth_user == NULL) { tab[11].value = tab[0].value; tab[12].value = tab[1].value; tab[13].value = tab[2].value; } else { tab[11].value = user->auth_user; tab[12].value = p_strdup(user->pool, t_strcut(user->auth_user, '@')); tab[13].value = strchr(user->auth_user, '@'); if (tab[13].value != NULL) tab[13].value++; } user->var_expand_table = tab; return user->var_expand_table; } static const char * mail_user_var_expand_func_userdb(const char *data, void *context) { struct mail_user *user = context; return mail_storage_service_fields_var_expand(data, user->userdb_fields); } void mail_user_set_home(struct mail_user *user, const char *home) { user->_home = p_strdup(user->pool, home); user->home_looked_up = TRUE; } void mail_user_add_namespace(struct mail_user *user, struct mail_namespace **namespaces) { struct mail_namespace **tmp, *next, *ns = *namespaces; for (; ns != NULL; ns = next) { next = ns->next; tmp = &user->namespaces; for (; *tmp != NULL; tmp = &(*tmp)->next) { i_assert(*tmp != ns); if (strlen(ns->prefix) < strlen((*tmp)->prefix)) break; } ns->next = *tmp; *tmp = ns; } *namespaces = user->namespaces; T_BEGIN { hook_mail_namespaces_added(user->namespaces); } T_END; } void mail_user_drop_useless_namespaces(struct mail_user *user) { struct mail_namespace *ns, *next; /* drop all autocreated unusable (typically shared) namespaces. don't drop the autocreated prefix="" namespace that we explicitly created for being the fallback namespace. */ for (ns = user->namespaces; ns != NULL; ns = next) { next = ns->next; if ((ns->flags & NAMESPACE_FLAG_USABLE) == 0 && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0 && ns->prefix_len > 0) mail_namespace_destroy(ns); } } const char *mail_user_home_expand(struct mail_user *user, const char *path) { (void)mail_user_try_home_expand(user, &path); return path; } static int mail_user_userdb_lookup_home(struct mail_user *user) { struct auth_user_info info; struct auth_user_reply reply; pool_t userdb_pool; const char *username, *const *fields; int ret; i_assert(!user->home_looked_up); i_zero(&info); info.service = user->service; if (user->local_ip != NULL) info.local_ip = *user->local_ip; if (user->remote_ip != NULL) info.remote_ip = *user->remote_ip; userdb_pool = pool_alloconly_create("userdb lookup", 2048); ret = auth_master_user_lookup(mail_user_auth_master_conn, user->username, &info, userdb_pool, &username, &fields); if (ret > 0) { auth_user_fields_parse(fields, userdb_pool, &reply); user->_home = p_strdup(user->pool, reply.home); } pool_unref(&userdb_pool); return ret; } static void mail_user_get_mail_home(struct mail_user *user) { const char *home = user->set->mail_home; string_t *str; if (user->settings_expanded) { user->_home = home[0] != '\0' ? home : NULL; return; } /* we're still initializing user. need to do the expansion ourself. */ i_assert(home[0] == SETTING_STRVAR_UNEXPANDED[0]); home++; if (home[0] == '\0') return; str = t_str_new(128); var_expand_with_funcs(str, home, mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); user->_home = p_strdup(user->pool, str_c(str)); } int mail_user_get_home(struct mail_user *user, const char **home_r) { int ret; if (user->home_looked_up) { *home_r = user->_home; return user->_home != NULL ? 1 : 0; } if (mail_user_auth_master_conn == NULL) { /* no userdb connection. we can only use mail_home setting. */ mail_user_get_mail_home(user); } else if ((ret = mail_user_userdb_lookup_home(user)) < 0) { /* userdb lookup failed */ return -1; } else if (ret == 0) { /* user doesn't exist */ user->nonexistent = TRUE; } else if (user->_home == NULL) { /* no home returned by userdb lookup, fallback to mail_home setting. */ mail_user_get_mail_home(user); } user->home_looked_up = TRUE; *home_r = user->_home; return user->_home != NULL ? 1 : 0; } bool mail_user_is_plugin_loaded(struct mail_user *user, struct module *module) { const char *const *plugins; bool ret; T_BEGIN { plugins = t_strsplit_spaces(user->set->mail_plugins, ", "); ret = str_array_find(plugins, module_get_plugin_name(module)); } T_END; return ret; } const char *mail_user_plugin_getenv(struct mail_user *user, const char *name) { return mail_user_set_plugin_getenv(user->set, name); } const char *mail_user_set_plugin_getenv(const struct mail_user_settings *set, const char *name) { const char *const *envs; unsigned int i, count; if (!array_is_created(&set->plugin_envs)) return NULL; envs = array_get(&set->plugin_envs, &count); for (i = 0; i < count; i += 2) { if (strcmp(envs[i], name) == 0) return envs[i+1]; } return NULL; } int mail_user_try_home_expand(struct mail_user *user, const char **pathp) { const char *home, *path = *pathp; if (*path != '~') { /* no need to expand home */ return 0; } if (mail_user_get_home(user, &home) <= 0) return -1; path = home_expand_tilde(path, home); if (path == NULL) return -1; *pathp = path; return 0; } void mail_user_set_get_temp_prefix(string_t *dest, const struct mail_user_settings *set) { str_append(dest, set->mail_temp_dir); str_append(dest, "/dovecot."); str_append(dest, master_service_get_name(master_service)); str_append_c(dest, '.'); } int mail_user_lock_file_create(struct mail_user *user, const char *lock_fname, unsigned int lock_secs, struct file_lock **lock_r, const char **error_r) { bool created; const char *home, *path, *error; int ret; if ((ret = mail_user_get_home(user, &home)) < 0) { /* home lookup failed - shouldn't really happen */ *error_r = "Failed to lookup home directory"; errno = EINVAL; return -1; } if (ret == 0) { *error_r = "User has no home directory"; errno = EINVAL; return -1; } const struct mail_storage_settings *mail_set = mail_user_set_get_storage_set(user); struct file_create_settings lock_set = { .lock_timeout_secs = lock_secs, .lock_method = mail_set->parsed_lock_method, }; struct mailbox_list *inbox_list = mail_namespace_find_inbox(user->namespaces)->list; if (inbox_list->set.volatile_dir == NULL) path = t_strdup_printf("%s/%s", home, lock_fname); else { path = t_strdup_printf("%s/%s", inbox_list->set.volatile_dir, lock_fname); lock_set.mkdir_mode = 0700; } if (file_create_locked(path, &lock_set, lock_r, &created, &error) == -1) { *error_r = t_strdup_printf("file_create_locked(%s) failed: %s", path, error); return errno == EAGAIN ? 0 : -1; } file_lock_set_unlink_on_free(*lock_r, TRUE); file_lock_set_close_on_free(*lock_r, TRUE); return 1; } const char *mail_user_get_anvil_userip_ident(struct mail_user *user) { if (user->remote_ip == NULL) return NULL; return t_strconcat(net_ip2addr(user->remote_ip), "/", str_tabescape(user->username), NULL); } static void mail_user_try_load_class_plugin(struct mail_user *user, const char *name) { struct module_dir_load_settings mod_set; struct module *module; size_t name_len = strlen(name); i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.binary_name = master_service_get_name(master_service); mod_set.setting_name = ""; mod_set.require_init_funcs = TRUE; mod_set.debug = user->mail_debug; mail_storage_service_modules = module_dir_load_missing(mail_storage_service_modules, user->set->mail_plugin_dir, name, &mod_set); /* initialize the module (and only this module!) immediately so that the class gets registered */ for (module = mail_storage_service_modules; module != NULL; module = module->next) { if (strncmp(module->name, name, name_len) == 0 && strcmp(module->name + name_len, "_plugin") == 0) { if (!module->initialized) { module->initialized = TRUE; module->init(module); } break; } } } struct mail_storage * mail_user_get_storage_class(struct mail_user *user, const char *name) { struct mail_storage *storage; storage = mail_storage_find_class(name); if (storage == NULL || storage->v.alloc != NULL) return storage; /* it's implemented by a plugin. load it and check again. */ mail_user_try_load_class_plugin(user, name); storage = mail_storage_find_class(name); if (storage != NULL && storage->v.alloc == NULL) { i_error("Storage driver '%s' exists as a stub, " "but its plugin couldn't be loaded", name); return NULL; } return storage; } struct mail_user *mail_user_dup(struct mail_user *user) { struct mail_user *user2; user2 = mail_user_alloc(user->username, user->set_info, user->unexpanded_set); if (user2->_service_user != NULL) { user2->_service_user = user->_service_user; mail_storage_service_user_ref(user2->_service_user); } if (user->_home != NULL) mail_user_set_home(user2, user->_home); mail_user_set_vars(user2, user->service, user->local_ip, user->remote_ip); user2->uid = user->uid; user2->gid = user->gid; user2->anonymous = user->anonymous; user2->admin = user->admin; user2->auth_token = p_strdup(user2->pool, user->auth_token); user2->auth_user = p_strdup(user2->pool, user->auth_user); user2->session_id = p_strdup(user2->pool, user->session_id); user2->session_create_time = user->session_create_time; user2->userdb_fields = user->userdb_fields == NULL ? NULL : p_strarray_dup(user2->pool, user->userdb_fields); return user2; } void mail_user_init_ssl_client_settings(struct mail_user *user, struct ssl_iostream_settings *ssl_set) { const struct mail_storage_settings *mail_set = mail_user_set_get_storage_set(user); ssl_set->ca_dir = mail_set->ssl_client_ca_dir; ssl_set->ca_file = mail_set->ssl_client_ca_file; } void mail_user_init_fs_settings(struct mail_user *user, struct fs_settings *fs_set, struct ssl_iostream_settings *ssl_set) { fs_set->username = user->username; fs_set->session_id = user->session_id; fs_set->base_dir = user->set->base_dir; fs_set->temp_dir = user->set->mail_temp_dir; fs_set->debug = user->mail_debug; fs_set->enable_timing = user->stats_enabled; fs_set->ssl_client_set = ssl_set; mail_user_init_ssl_client_settings(user, ssl_set); } void mail_user_stats_fill(struct mail_user *user, struct stats *stats) { user->v.stats_fill(user, stats); } static const struct var_expand_func_table mail_user_var_expand_func_table_arr[] = { { "userdb", mail_user_var_expand_func_userdb }, { NULL, NULL } }; const struct var_expand_func_table *mail_user_var_expand_func_table = mail_user_var_expand_func_table_arr; dovecot-2.2.33.2/src/lib-storage/mailbox-tree.h0000644000175000017500000000300113123174404016056 00000000000000#ifndef MAILBOX_TREE_H #define MAILBOX_TREE_H #include "mailbox-list.h" struct mailbox_node { struct mailbox_node *parent; struct mailbox_node *next; struct mailbox_node *children; char *name; enum mailbox_info_flags flags; }; ARRAY_DEFINE_TYPE(mailbox_node, struct mailbox_node *); struct mailbox_tree_context *mailbox_tree_init(char separator); struct mailbox_tree_context * mailbox_tree_init_size(char separator, unsigned int mailbox_node_size); void mailbox_tree_deinit(struct mailbox_tree_context **tree); void mailbox_tree_set_separator(struct mailbox_tree_context *tree, char separator); void mailbox_tree_set_parents_nonexistent(struct mailbox_tree_context *tree); void mailbox_tree_clear(struct mailbox_tree_context *tree); pool_t mailbox_tree_get_pool(struct mailbox_tree_context *tree); struct mailbox_node * mailbox_tree_get(struct mailbox_tree_context *tree, const char *path, bool *created_r); struct mailbox_node * mailbox_tree_lookup(struct mailbox_tree_context *tree, const char *path); struct mailbox_tree_iterate_context * ATTR_NULL(2) mailbox_tree_iterate_init(struct mailbox_tree_context *tree, struct mailbox_node *root, unsigned int flags_mask); struct mailbox_node * mailbox_tree_iterate_next(struct mailbox_tree_iterate_context *ctx, const char **path_r); void mailbox_tree_iterate_deinit(struct mailbox_tree_iterate_context **ctx); struct mailbox_tree_context *mailbox_tree_dup(struct mailbox_tree_context *src); void mailbox_tree_sort(struct mailbox_tree_context *tree); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-attribute-internal.h0000644000175000017500000000074613123174404020751 00000000000000#ifndef MAILBOX_ATTRIBUTE_INTERNAL_H #define MAILBOX_ATTRIBUTE_INTERNAL_H /* RFC 5464, Section 3.2.1.2: Mailbox entries */ #define MAILBOX_ATTRIBUTE_COMMENT "comment" /* RFC 6154, Section 4: IMAP METADATA Entry for Special-Use Attributes */ #define MAILBOX_ATTRIBUTE_SPECIALUSE "specialuse" /* RFC 5464, Section 3.2.1.1: Server entries */ #define MAIL_SERVER_ATTRIBUTE_COMMENT "comment" #define MAIL_SERVER_ATTRIBUTE_ADMIN "admin" void mailbox_attributes_internal_init(void); #endif dovecot-2.2.33.2/src/lib-storage/mail-error.c0000644000175000017500000000156413123174404015546 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "eacces-error.h" #include "mail-error.h" bool mail_error_from_errno(enum mail_error *error_r, const char **error_string_r) { if (ENOACCESS(errno)) { *error_r = MAIL_ERROR_PERM; *error_string_r = MAIL_ERRSTR_NO_PERMISSION; } else if (ENOQUOTA(errno)) { *error_r = MAIL_ERROR_NOQUOTA; *error_string_r = MAIL_ERRSTR_NO_QUOTA; } else if (ENOTFOUND(errno)) { *error_r = MAIL_ERROR_NOTFOUND; *error_string_r = errno != ELOOP ? "Not found" : "Directory structure is broken"; } else { return FALSE; } return TRUE; } const char *mail_error_eacces_msg(const char *func, const char *path) { return eacces_error_get(func, path); } const char *mail_error_create_eacces_msg(const char *func, const char *path) { return eacces_error_get_creating(func, path); } dovecot-2.2.33.2/src/lib-storage/fail-mailbox.c0000644000175000017500000001700713165463624016053 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "fail-mail-storage.h" #define TEST_UID_VALIDITY 1 static bool fail_mailbox_is_readonly(struct mailbox *box ATTR_UNUSED) { return FALSE; } static int fail_mailbox_enable(struct mailbox *box, enum mailbox_feature features) { box->enabled_features = features; return -1; } static int fail_mailbox_exists(struct mailbox *box ATTR_UNUSED, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { *existence_r = MAILBOX_EXISTENCE_NONE; return -1; } static int fail_mailbox_open(struct mailbox *box) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } static void fail_mailbox_close(struct mailbox *box ATTR_UNUSED) { } static void fail_mailbox_free(struct mailbox *box ATTR_UNUSED) { } static int fail_mailbox_create(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED, bool directory ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox can't be created"); return -1; } static int fail_mailbox_update(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox can't be updated"); return -1; } static int fail_mailbox_delete(struct mailbox *box) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox can't be deleted"); return -1; } static int fail_mailbox_rename(struct mailbox *src, struct mailbox *dest ATTR_UNUSED) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox can't be renamed"); return -1; } static int fail_mailbox_get_status(struct mailbox *box ATTR_UNUSED, enum mailbox_status_items items ATTR_UNUSED, struct mailbox_status *status_r) { status_r->uidvalidity = TEST_UID_VALIDITY; status_r->uidnext = 1; mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } static int fail_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items ATTR_UNUSED, struct mailbox_metadata *metadata_r ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } static int fail_mailbox_set_subscribed(struct mailbox *box, bool set ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox can't be subscribed"); return -1; } static struct mailbox_sync_context * fail_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags ATTR_UNUSED) { struct mailbox_sync_context *ctx; ctx = i_new(struct mailbox_sync_context, 1); ctx->box = box; return ctx; } static bool fail_mailbox_sync_next(struct mailbox_sync_context *ctx ATTR_UNUSED, struct mailbox_sync_rec *sync_rec_r ATTR_UNUSED) { return FALSE; } static int fail_mailbox_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r ATTR_UNUSED) { mail_storage_set_error(ctx->box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(ctx->box->vname)); i_free(ctx); return -1; } static void fail_mailbox_notify_changes(struct mailbox *box ATTR_UNUSED) { } static struct mailbox_transaction_context * fail_mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct mailbox_transaction_context *ctx; ctx = i_new(struct mailbox_transaction_context, 1); ctx->box = box; ctx->flags = flags; i_array_init(&ctx->module_contexts, 5); return ctx; } static void fail_mailbox_transaction_rollback(struct mailbox_transaction_context *t) { array_free(&t->module_contexts); i_free(t); } static int fail_mailbox_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { changes_r->uid_validity = TEST_UID_VALIDITY; fail_mailbox_transaction_rollback(t); return 0; } static struct mail_search_context * fail_mailbox_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program ATTR_UNUSED, enum mail_fetch_field wanted_fields ATTR_UNUSED, struct mailbox_header_lookup_ctx *wanted_headers ATTR_UNUSED) { struct mail_search_context *ctx; ctx = i_new(struct mail_search_context, 1); ctx->transaction = t; ctx->args = args; i_array_init(&ctx->results, 5); i_array_init(&ctx->module_contexts, 5); return ctx; } static int fail_mailbox_search_deinit(struct mail_search_context *ctx) { array_free(&ctx->results); array_free(&ctx->module_contexts); i_free(ctx); return 0; } static bool fail_mailbox_search_next_nonblock(struct mail_search_context *ctx ATTR_UNUSED, struct mail **mail_r, bool *tryagain_r) { *tryagain_r = FALSE; *mail_r = NULL; return FALSE; } static bool fail_mailbox_search_next_update_seq(struct mail_search_context *ctx ATTR_UNUSED) { return FALSE; } static struct mail_save_context * fail_mailbox_save_alloc(struct mailbox_transaction_context *t) { struct mail_save_context *ctx; ctx = i_new(struct mail_save_context, 1); ctx->transaction = t; return ctx; } static int fail_mailbox_save_begin(struct mail_save_context *ctx ATTR_UNUSED, struct istream *input ATTR_UNUSED) { return -1; } static int fail_mailbox_save_continue(struct mail_save_context *ctx ATTR_UNUSED) { return -1; } static int fail_mailbox_save_finish(struct mail_save_context *ctx ATTR_UNUSED) { return -1; } static void fail_mailbox_save_cancel(struct mail_save_context *ctx ATTR_UNUSED) { } static int fail_mailbox_copy(struct mail_save_context *ctx ATTR_UNUSED, struct mail *mail ATTR_UNUSED) { return -1; } static bool fail_mailbox_is_inconsistent(struct mailbox *box ATTR_UNUSED) { return FALSE; } struct mailbox fail_mailbox = { .v = { fail_mailbox_is_readonly, fail_mailbox_enable, fail_mailbox_exists, fail_mailbox_open, fail_mailbox_close, fail_mailbox_free, fail_mailbox_create, fail_mailbox_update, fail_mailbox_delete, fail_mailbox_rename, fail_mailbox_get_status, fail_mailbox_get_metadata, fail_mailbox_set_subscribed, NULL, NULL, NULL, NULL, NULL, NULL, NULL, fail_mailbox_sync_init, fail_mailbox_sync_next, fail_mailbox_sync_deinit, NULL, fail_mailbox_notify_changes, fail_mailbox_transaction_begin, fail_mailbox_transaction_commit, fail_mailbox_transaction_rollback, NULL, fail_mailbox_mail_alloc, fail_mailbox_search_init, fail_mailbox_search_deinit, fail_mailbox_search_next_nonblock, fail_mailbox_search_next_update_seq, fail_mailbox_save_alloc, fail_mailbox_save_begin, fail_mailbox_save_continue, fail_mailbox_save_finish, fail_mailbox_save_cancel, fail_mailbox_copy, NULL, NULL, NULL, fail_mailbox_is_inconsistent } }; struct mailbox * fail_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct mailbox *box; pool_t pool; pool = pool_alloconly_create("fail mailbox", 1024+512); box = p_new(pool, struct mailbox, 1); *box = fail_mailbox; box->vname = p_strdup(pool, vname); box->name = p_strdup(pool, mailbox_list_get_storage_name(list, vname)); box->storage = storage; box->list = list; box->pool = pool; box->flags = flags; p_array_init(&box->search_results, pool, 16); p_array_init(&box->module_contexts, pool, 5); return box; } dovecot-2.2.33.2/src/lib-storage/mail-autoexpunge.c0000644000175000017500000001517713165463624017001 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mailbox-list-iter.h" #include "mail-storage-private.h" #include "mail-namespace.h" #include "mail-user.h" #include "mail-autoexpunge.h" #define AUTOEXPUNGE_LOCK_FNAME "dovecot.autoexpunge.lock" static bool mailbox_autoexpunge_lock(struct mail_user *user, struct file_lock **lock) { const char *error; int ret; if (*lock != NULL) return TRUE; /* Try to lock the autoexpunging. If the lock already exists, another process is already busy with expunging, so we don't have to do it. The easiest place where to store the lock file to is the home directory, but allow autoexpunging to work even if we can't get it. The lock isn't really required; it 1) improves performance so that multiple processes won't do the same work unnecessarily, and 2) it helps to avoid duplicate mails being added with lazy_expunge. */ ret = mail_user_lock_file_create(user, AUTOEXPUNGE_LOCK_FNAME, 0, lock, &error); if (ret < 0) { i_error("autoexpunge: Couldn't create %s lock: %s", AUTOEXPUNGE_LOCK_FNAME, error); /* do autoexpunging anyway */ return TRUE; } else if (ret == 0) { /* another process is autoexpunging, so we don't need to. */ return FALSE; } else { return TRUE; } } static int mailbox_autoexpunge(struct mailbox *box, unsigned int interval_time, unsigned int max_mails, unsigned int *expunged_count) { struct mailbox_transaction_context *t; struct mail *mail; struct mailbox_metadata metadata; const struct mail_index_header *hdr; struct mailbox_status status; uint32_t seq; time_t timestamp, expire_time, last_rename_stamp = 0; const void *data; size_t size; unsigned int count = 0; int ret = 0; if ((unsigned int)ioloop_time < interval_time) expire_time = 0; else expire_time = ioloop_time - interval_time; /* first try to check quickly from mailbox list index if we should bother opening this mailbox. */ if (mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) { /* autocreated mailbox doesn't exist yet */ return 0; } return -1; } if (interval_time == 0 && status.messages <= max_mails) return 0; if (max_mails == 0 || status.messages <= max_mails) { if (mailbox_get_metadata(box, MAILBOX_METADATA_FIRST_SAVE_DATE, &metadata) < 0) return -1; if (metadata.first_save_date == (time_t)-1 || metadata.first_save_date > expire_time) return 0; } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) return -1; mail_index_get_header_ext(box->view, box->box_last_rename_stamp_ext_id, &data, &size); if (size >= sizeof(uint32_t)) last_rename_stamp = *(const uint32_t*)data; t = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(t, "autoexpunge"); mail = mail_alloc(t, 0, NULL); hdr = mail_index_get_header(box->view); for (seq = 1; seq <= hdr->messages_count; seq++) { mail_set_seq(mail, seq); if (max_mails > 0 && hdr->messages_count - seq + 1 > max_mails) { /* max_mails is still being reached -> expunge. don't even check saved-dates before we're below max_mails. */ mail_autoexpunge(mail); count++; } else if (interval_time == 0) { /* only max_mails is used. nothing further to do. */ break; } else if (mail_get_save_date(mail, ×tamp) == 0) { if (I_MAX(last_rename_stamp, timestamp) > expire_time) break; mail_autoexpunge(mail); count++; } else if (mailbox_get_last_mail_error(box) == MAIL_ERROR_EXPUNGED) { /* already expunged */ } else { /* failed */ ret = -1; break; } } mail_free(&mail); if (mailbox_transaction_commit(&t) < 0) ret = -1; else *expunged_count += count; return ret; } static void mailbox_autoexpunge_set(struct mail_namespace *ns, const char *vname, unsigned int autoexpunge, unsigned int autoexpunge_max_mails, unsigned int *expunged_count) { struct mailbox *box; /* autoexpunge is configured by admin, so we can safely ignore any ACLs the user might normally have against expunging in the mailbox. */ box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, "autoexpunge"); if (mailbox_autoexpunge(box, autoexpunge, autoexpunge_max_mails, expunged_count) < 0) { i_error("Failed to autoexpunge mailbox '%s': %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); } mailbox_free(&box); } static void mailbox_autoexpunge_wildcards(struct mail_namespace *ns, const struct mailbox_settings *set, unsigned int *expunged_count) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; const char *iter_name; iter_name = t_strconcat(ns->prefix, set->name, NULL); iter = mailbox_list_iter_init(ns->list, iter_name, MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN { mailbox_autoexpunge_set(ns, info->vname, set->autoexpunge, set->autoexpunge_max_mails, expunged_count); } T_END; if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Failed to iterate autoexpunge mailboxes '%s': %s", iter_name, mailbox_list_get_last_internal_error(ns->list, NULL)); } } static bool mail_namespace_autoexpunge(struct mail_namespace *ns, struct file_lock **lock, unsigned int *expunged_count) { struct mailbox_settings *const *box_set; const char *vname; if (!array_is_created(&ns->set->mailboxes)) return TRUE; array_foreach(&ns->set->mailboxes, box_set) { if ((*box_set)->autoexpunge == 0 && (*box_set)->autoexpunge_max_mails == 0) continue; if (!mailbox_autoexpunge_lock(ns->user, lock)) return FALSE; if (strpbrk((*box_set)->name, "*?") != NULL) mailbox_autoexpunge_wildcards(ns, *box_set, expunged_count); else { if ((*box_set)->name[0] == '\0' && ns->prefix_len > 0 && ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) vname = t_strndup(ns->prefix, ns->prefix_len - 1); else vname = t_strconcat(ns->prefix, (*box_set)->name, NULL); mailbox_autoexpunge_set(ns, vname, (*box_set)->autoexpunge, (*box_set)->autoexpunge_max_mails, expunged_count); } } return TRUE; } unsigned int mail_user_autoexpunge(struct mail_user *user) { struct file_lock *lock = NULL; struct mail_namespace *ns; unsigned int expunged_count = 0; for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->alias_for == NULL) { if (!mail_namespace_autoexpunge(ns, &lock, &expunged_count)) break; } } if (lock != NULL) file_lock_free(&lock); return expunged_count; } dovecot-2.2.33.2/src/lib-storage/mail-storage.h0000644000175000017500000012401013165463624016071 00000000000000#ifndef MAIL_STORAGE_H #define MAIL_STORAGE_H struct message_size; #include "seq-range-array.h" #include "file-lock.h" #include "guid.h" #include "mail-types.h" #include "mail-error.h" #include "mail-namespace.h" #include "mailbox-list.h" #include "mailbox-attribute.h" /* If some operation is taking long, call notify_ok every n seconds. */ #define MAIL_STORAGE_STAYALIVE_SECS 15 enum mail_storage_flags { /* Remember message headers' MD5 sum */ MAIL_STORAGE_FLAG_KEEP_HEADER_MD5 = 0x01, /* Don't try to autodetect anything, require that the given data contains all the necessary information. */ MAIL_STORAGE_FLAG_NO_AUTODETECTION = 0x02, /* Don't autocreate any directories. If they don't exist, fail to create the storage. */ MAIL_STORAGE_FLAG_NO_AUTOCREATE = 0x04, /* Don't verify existence or accessibility of any directories. Create the storage in any case. */ MAIL_STORAGE_FLAG_NO_AUTOVERIFY = 0x08 }; enum mailbox_flags { /* Mailbox must not be modified even if asked */ MAILBOX_FLAG_READONLY = 0x01, /* Only saving/copying mails to mailbox works. */ MAILBOX_FLAG_SAVEONLY = 0x02, /* Remove MAIL_RECENT flags when syncing */ MAILBOX_FLAG_DROP_RECENT = 0x04, /* Don't create index files for the mailbox */ MAILBOX_FLAG_NO_INDEX_FILES = 0x10, /* Keep mailbox exclusively locked all the time while it's open */ MAILBOX_FLAG_KEEP_LOCKED = 0x20, /* Enable if mailbox is used for serving POP3. This allows making better caching decisions. */ MAILBOX_FLAG_POP3_SESSION = 0x40, /* Enable if mailbox is used for saving a mail delivery using MDA. This causes ACL plugin to use POST right rather than INSERT. */ MAILBOX_FLAG_POST_SESSION = 0x80, /* Force opening mailbox and ignoring any ACLs */ MAILBOX_FLAG_IGNORE_ACLS = 0x100, /* Open mailbox even if it's already marked as deleted */ MAILBOX_FLAG_OPEN_DELETED = 0x200, /* Mailbox is opened for deletion, which should be performed as efficiently as possible, even allowing the mailbox state to become inconsistent. For example this disables lazy_expunge plugin and quota updates (possibly resulting in broken quota). and This is useful for example when deleting entire user accounts. */ MAILBOX_FLAG_DELETE_UNSAFE = 0x400, /* Mailbox is used for caching purposes. Some of the mails may be stubs, which exist in the index but that don't have a mail body. The backend shouldn't treat it as corruption if a mail body isn't found. */ MAILBOX_FLAG_USE_STUBS = 0x800, }; enum mailbox_feature { /* Enable tracking modsequences */ MAILBOX_FEATURE_CONDSTORE = 0x01, /* Enable tracking expunge modsequences */ MAILBOX_FEATURE_QRESYNC = 0x02 }; enum mailbox_existence { MAILBOX_EXISTENCE_NONE, MAILBOX_EXISTENCE_NOSELECT, MAILBOX_EXISTENCE_SELECT }; enum mailbox_status_items { STATUS_MESSAGES = 0x01, STATUS_RECENT = 0x02, STATUS_UIDNEXT = 0x04, STATUS_UIDVALIDITY = 0x08, STATUS_UNSEEN = 0x10, STATUS_FIRST_UNSEEN_SEQ = 0x20, STATUS_KEYWORDS = 0x40, STATUS_HIGHESTMODSEQ = 0x80, STATUS_PERMANENT_FLAGS = 0x200, STATUS_FIRST_RECENT_UID = 0x400, STATUS_LAST_CACHED_SEQ = 0x800, STATUS_CHECK_OVER_QUOTA = 0x1000, /* return error if over quota */ STATUS_HIGHESTPVTMODSEQ = 0x2000, /* status items that must not be looked up with mailbox_get_open_status(), because they can return failure. */ #define MAILBOX_STATUS_FAILING_ITEMS \ (STATUS_LAST_CACHED_SEQ | STATUS_CHECK_OVER_QUOTA) }; enum mailbox_metadata_items { MAILBOX_METADATA_GUID = 0x01, MAILBOX_METADATA_VIRTUAL_SIZE = 0x02, MAILBOX_METADATA_CACHE_FIELDS = 0x04, MAILBOX_METADATA_PRECACHE_FIELDS = 0x08, MAILBOX_METADATA_BACKEND_NAMESPACE = 0x10, MAILBOX_METADATA_PHYSICAL_SIZE = 0x20, MAILBOX_METADATA_FIRST_SAVE_DATE = 0x40 /* metadata items that require mailbox to be synced at least once. */ #define MAILBOX_METADATA_SYNC_ITEMS \ (MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_PHYSICAL_SIZE | \ MAILBOX_METADATA_FIRST_SAVE_DATE) }; enum mailbox_search_result_flags { /* Update search results whenever the mailbox view is synced. Expunged messages are removed even without this flag. */ MAILBOX_SEARCH_RESULT_FLAG_UPDATE = 0x01, /* Queue changes so _sync() can be used. */ MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC = 0x02 }; enum mail_sort_type { MAIL_SORT_ARRIVAL = 0x0001, MAIL_SORT_CC = 0x0002, MAIL_SORT_DATE = 0x0004, MAIL_SORT_FROM = 0x0008, MAIL_SORT_SIZE = 0x0010, MAIL_SORT_SUBJECT = 0x0020, MAIL_SORT_TO = 0x0040, MAIL_SORT_RELEVANCY = 0x0080, MAIL_SORT_DISPLAYFROM = 0x0100, MAIL_SORT_DISPLAYTO = 0x0200, MAIL_SORT_POP3_ORDER = 0x0400, /* Maximum size for sort program (each one separately + END) */ #define MAX_SORT_PROGRAM_SIZE (11 + 1) MAIL_SORT_MASK = 0x0fff, MAIL_SORT_FLAG_REVERSE = 0x1000, /* reverse this mask type */ MAIL_SORT_END = 0x0000 /* ends sort program */ }; enum mail_fetch_field { MAIL_FETCH_FLAGS = 0x00000001, MAIL_FETCH_MESSAGE_PARTS = 0x00000002, MAIL_FETCH_STREAM_HEADER = 0x00000004, MAIL_FETCH_STREAM_BODY = 0x00000008, MAIL_FETCH_DATE = 0x00000010, MAIL_FETCH_RECEIVED_DATE = 0x00000020, MAIL_FETCH_SAVE_DATE = 0x00000040, MAIL_FETCH_PHYSICAL_SIZE = 0x00000080, MAIL_FETCH_VIRTUAL_SIZE = 0x00000100, /* Set has_nuls / has_no_nuls fields */ MAIL_FETCH_NUL_STATE = 0x00000200, MAIL_FETCH_STREAM_BINARY = 0x00000400, /* specials: */ MAIL_FETCH_IMAP_BODY = 0x00001000, MAIL_FETCH_IMAP_BODYSTRUCTURE = 0x00002000, MAIL_FETCH_IMAP_ENVELOPE = 0x00004000, MAIL_FETCH_FROM_ENVELOPE = 0x00008000, MAIL_FETCH_HEADER_MD5 = 0x00010000, MAIL_FETCH_STORAGE_ID = 0x00020000, #define MAIL_FETCH_UIDL_FILE_NAME MAIL_FETCH_STORAGE_ID /* FIXME: remove in v2.3 */ MAIL_FETCH_UIDL_BACKEND = 0x00040000, MAIL_FETCH_MAILBOX_NAME = 0x00080000, MAIL_FETCH_SEARCH_RELEVANCY = 0x00100000, MAIL_FETCH_GUID = 0x00200000, MAIL_FETCH_POP3_ORDER = 0x00400000, MAIL_FETCH_REFCOUNT = 0x00800000, MAIL_FETCH_BODY_SNIPPET = 0x01000000 }; enum mailbox_transaction_flags { /* Hide changes done in this transaction from next view sync */ MAILBOX_TRANSACTION_FLAG_HIDE = 0x01, /* External transaction. Should be used for copying and appends, but nothing else. */ MAILBOX_TRANSACTION_FLAG_EXTERNAL = 0x02, /* Always assign UIDs to messages when saving/copying. Normally this is done only if it can be done easily. */ MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS = 0x04, /* Refresh the index so lookups return latest flags/modseqs */ MAILBOX_TRANSACTION_FLAG_REFRESH = 0x08, /* Don't update caching decisions no matter what we do in this transaction (useful for e.g. precaching) */ MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC = 0x10, /* Sync transaction describes changes to mailbox that already happened to another mailbox with whom we're syncing with (dsync) */ MAILBOX_TRANSACTION_FLAG_SYNC = 0x20, /* Don't trigger any notifications for this transaction. This especially means the notify plugin. This would normally be used only with _FLAG_SYNC. */ MAILBOX_TRANSACTION_FLAG_NO_NOTIFY = 0x40, /* Append fills in an existing stub mail for the specified UID, instead of saving a new mail. This requires mailbox to be opened with MAILBOX_FLAG_USE_STUBS. */ MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB = 0x80, }; enum mailbox_sync_flags { /* Make sure we sync all external changes done to mailbox */ MAILBOX_SYNC_FLAG_FULL_READ = 0x01, /* Make sure we write all our internal changes into the mailbox */ MAILBOX_SYNC_FLAG_FULL_WRITE = 0x02, /* If it's not too much trouble, check if there are some changes */ MAILBOX_SYNC_FLAG_FAST = 0x04, /* Don't sync expunges from our view */ MAILBOX_SYNC_FLAG_NO_EXPUNGES = 0x08, /* If mailbox is currently inconsistent, fix it instead of failing. */ MAILBOX_SYNC_FLAG_FIX_INCONSISTENT = 0x40, /* Syncing after an EXPUNGE command. This is just an informational flag for plugins. */ MAILBOX_SYNC_FLAG_EXPUNGE = 0x80, /* Force doing a full resync of indexes. */ MAILBOX_SYNC_FLAG_FORCE_RESYNC = 0x100, /* FIXME: kludge until something better comes along: Request full text search index optimization */ MAILBOX_SYNC_FLAG_OPTIMIZE = 0x400 }; enum mailbox_sync_type { MAILBOX_SYNC_TYPE_EXPUNGE = 0x01, MAILBOX_SYNC_TYPE_FLAGS = 0x02, MAILBOX_SYNC_TYPE_MODSEQ = 0x04 }; struct message_part; struct mail_namespace; struct mail_storage; struct mail_search_args; struct mail_search_result; struct mail_keywords; struct mail_save_context; struct mailbox; struct mailbox_transaction_context; struct mailbox_status { uint32_t messages; /* STATUS_MESSAGES */ uint32_t recent; /* STATUS_RECENT */ uint32_t unseen; /* STATUS_UNSEEN */ uint32_t uidvalidity; /* STATUS_UIDVALIDITY */ uint32_t uidnext; /* STATUS_UIDNEXT */ uint32_t first_unseen_seq; /* STATUS_FIRST_UNSEEN_SEQ */ uint32_t first_recent_uid; /* STATUS_FIRST_RECENT_UID */ uint32_t last_cached_seq; /* STATUS_LAST_CACHED_SEQ */ uint64_t highest_modseq; /* STATUS_HIGHESTMODSEQ */ /* 0 if no private index (STATUS_HIGHESTPVTMODSEQ) */ uint64_t highest_pvt_modseq; /* NULL-terminated array of keywords (STATUS_KEYWORDS) */ const ARRAY_TYPE(keywords) *keywords; /* These flags can be permanently modified (STATUS_PERMANENT_FLAGS) */ enum mail_flags permanent_flags; /* These flags can be modified (STATUS_PERMANENT_FLAGS) */ enum mail_flags flags; /* All keywords can be permanently modified (STATUS_PERMANENT_FLAGS) */ unsigned int permanent_keywords:1; /* More keywords can be created (STATUS_PERMANENT_FLAGS) */ unsigned int allow_new_keywords:1; /* Modseqs aren't permanent (index is in memory) (STATUS_HIGHESTMODSEQ) */ unsigned int nonpermanent_modseqs:1; /* Modseq tracking has never been enabled for this mailbox yet. (STATUS_HIGHESTMODSEQ) */ unsigned int no_modseq_tracking:1; /* Messages have GUIDs (always set) */ unsigned int have_guids:1; /* mailbox_save_set_guid() works (always set) */ unsigned int have_save_guids:1; /* GUIDs are always 128bit (always set) */ unsigned int have_only_guid128:1; }; struct mailbox_cache_field { const char *name; int decision; /* enum mail_cache_decision_type */ /* last_used is unchanged, if it's (time_t)-1 */ time_t last_used; }; ARRAY_DEFINE_TYPE(mailbox_cache_field, struct mailbox_cache_field); struct mailbox_metadata { guid_128_t guid; /* sum of virtual size of all messages in mailbox */ uint64_t virtual_size; /* sum of physical size of all messages in mailbox */ uint64_t physical_size; /* timestamp of when the first message was saved. (time_t)-1 if there are no mails in the mailbox. */ time_t first_save_date; /* Fields that have "temp" or "yes" caching decision. */ const ARRAY_TYPE(mailbox_cache_field) *cache_fields; /* Fields that should be precached */ enum mail_fetch_field precache_fields; /* imapc backend returns this based on the remote NAMESPACE reply, while currently other backends return "" and type the same as the mailbox's real namespace type */ const char *backend_ns_prefix; enum mail_namespace_type backend_ns_type; }; struct mailbox_update { /* All non-zero fields are changed. */ guid_128_t mailbox_guid; uint32_t uid_validity; uint32_t min_next_uid; uint32_t min_first_recent_uid; uint64_t min_highest_modseq; uint64_t min_highest_pvt_modseq; /* Modify caching decisions, terminated by name=NULL */ const struct mailbox_cache_field *cache_updates; }; struct mail_transaction_commit_changes { /* Unreference the pool to free memory used by these changes. */ pool_t pool; /* UIDVALIDITY for assigned UIDs. */ uint32_t uid_validity; /* UIDs assigned to saved messages. Not necessarily ascending. If UID assignment wasn't required (e.g. LDA), this array may also be empty. Otherwise all of the saved mails got an UID. */ ARRAY_TYPE(seq_range) saved_uids; /* number of modseq changes that couldn't be changed as requested */ unsigned int ignored_modseq_changes; /* TRUE if anything actually changed with this commit */ bool changed; /* User doesn't have read ACL for the mailbox, so don't show the uid_validity / saved_uids. */ bool no_read_perm; }; struct mailbox_sync_rec { uint32_t seq1, seq2; enum mailbox_sync_type type; }; struct mailbox_sync_status { /* There are expunges that haven't been synced yet */ unsigned int sync_delayed_expunges:1; }; struct mailbox_expunge_rec { /* IMAP UID */ uint32_t uid; /* 128 bit GUID. If the actual GUID has a different size, this contains last bits of its SHA1 sum. */ guid_128_t guid_128; }; ARRAY_DEFINE_TYPE(mailbox_expunge_rec, struct mailbox_expunge_rec); enum mail_lookup_abort { /* Perform everything no matter what it takes */ MAIL_LOOKUP_ABORT_NEVER = 0, /* Abort if the operation would require reading message header/body or otherwise opening the mail file (e.g. with dbox metadata is read by opening and reading the file). This still allows somewhat fast operations to be performed, such as stat()ing a file. */ MAIL_LOOKUP_ABORT_READ_MAIL, /* Abort if the operation can't be done fully using cache file */ MAIL_LOOKUP_ABORT_NOT_IN_CACHE }; enum mail_access_type { MAIL_ACCESS_TYPE_DEFAULT = 0, /* Mail is being used for searching */ MAIL_ACCESS_TYPE_SEARCH, /* Mail is being used for sorting results */ MAIL_ACCESS_TYPE_SORT, }; struct mail { /* always set */ struct mailbox *box; struct mailbox_transaction_context *transaction; uint32_t seq, uid; unsigned int expunged:1; unsigned int saving:1; /* This mail is still being saved */ unsigned int has_nuls:1; /* message data is known to contain NULs */ unsigned int has_no_nuls:1; /* -''- known to not contain NULs */ /* Mail's header/body stream was opened within this request. If lookup_abort!=MAIL_LOOKUP_ABORT_NEVER, this can't become TRUE. */ bool mail_stream_opened:1; /* Mail's fast metadata was accessed within this request, e.g. the mail file was stat()ed. If mail_stream_opened==TRUE, this value isn't accurate anymore, because some backends may always set this when stream is opened and some don't. If lookup_abort is MAIL_LOOKUP_ABORT_NOT_IN_CACHE, this can't become TRUE. */ bool mail_metadata_accessed:1; enum mail_access_type access_type; /* If the lookup is aborted, error is set to MAIL_ERROR_NOTPOSSIBLE */ enum mail_lookup_abort lookup_abort; }; struct mail_storage_callbacks { /* "* OK " */ void (*notify_ok)(struct mailbox *mailbox, const char *text, void *context); /* "* NO " */ void (*notify_no)(struct mailbox *mailbox, const char *text, void *context); }; struct mailbox_virtual_pattern { struct mail_namespace *ns; const char *pattern; }; ARRAY_DEFINE_TYPE(mailbox_virtual_patterns, struct mailbox_virtual_pattern); ARRAY_DEFINE_TYPE(mail_storage, struct mail_storage *); ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *); extern ARRAY_TYPE(mail_storage) mail_storage_classes; typedef void mailbox_notify_callback_t(struct mailbox *box, void *context); void mail_storage_init(void); void mail_storage_deinit(void); /* register all mail storages */ void mail_storage_register_all(void); /* Register mail storage class with given name - all methods that are NULL are set to default methods */ void mail_storage_class_register(struct mail_storage *storage_class); void mail_storage_class_unregister(struct mail_storage *storage_class); /* Find mail storage class by name */ struct mail_storage *mail_storage_find_class(const char *name); /* Create a new instance of registered mail storage class with given storage-specific data. If driver is NULL, it's tried to be autodetected from ns location. If ns location is NULL, it uses the first storage that exists. The storage is put into ns->storage. */ int mail_storage_create(struct mail_namespace *ns, const char *driver, enum mail_storage_flags flags, const char **error_r) ATTR_NULL(2); int mail_storage_create_full(struct mail_namespace *ns, const char *driver, const char *data, enum mail_storage_flags flags, struct mail_storage **storage_r, const char **error_r) ATTR_NULL(2); void mail_storage_unref(struct mail_storage **storage); /* Returns the mail storage settings. */ const struct mail_storage_settings * mail_storage_get_settings(struct mail_storage *storage) ATTR_PURE; struct mail_user *mail_storage_get_user(struct mail_storage *storage) ATTR_PURE; /* Set storage callback functions to use. */ void mail_storage_set_callbacks(struct mail_storage *storage, struct mail_storage_callbacks *callbacks, void *context) ATTR_NULL(3); /* Purge storage's mailboxes (freeing disk space from expunged mails), if supported by the storage. Otherwise just a no-op. */ int mail_storage_purge(struct mail_storage *storage); /* Returns the error message of last occurred error. */ const char * ATTR_NOWARN_UNUSED_RESULT mail_storage_get_last_error(struct mail_storage *storage, enum mail_error *error_r) ATTR_NULL(2); /* Wrapper for mail_storage_get_last_error(); */ const char * ATTR_NOWARN_UNUSED_RESULT mailbox_get_last_error(struct mailbox *box, enum mail_error *error_r) ATTR_NULL(2); /* Wrapper for mail_storage_get_last_error(); */ enum mail_error mailbox_get_last_mail_error(struct mailbox *box); const char * ATTR_NOWARN_UNUSED_RESULT mail_storage_get_last_internal_error(struct mail_storage *storage, enum mail_error *error_r) ATTR_NULL(2); /* Wrapper for mail_storage_get_last_internal_error(); */ const char * ATTR_NOWARN_UNUSED_RESULT mailbox_get_last_internal_error(struct mailbox *box, enum mail_error *error_r) ATTR_NULL(2); /* Save the last error until it's popped. This is useful for cases where the storage has already failed, but the cleanup code path changes the error to something else unwanted. */ void mail_storage_last_error_push(struct mail_storage *storage); void mail_storage_last_error_pop(struct mail_storage *storage); /* Returns TRUE if mailboxes are files. */ bool mail_storage_is_mailbox_file(struct mail_storage *storage) ATTR_PURE; /* Initialize mailbox without actually opening any files or verifying that it exists. Note that append and copy may open the selected mailbox again with possibly different readonly-state. */ struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname, enum mailbox_flags flags); /* Like mailbox_alloc(), but use mailbox GUID. */ struct mailbox *mailbox_alloc_guid(struct mailbox_list *list, const guid_128_t guid, enum mailbox_flags flags); /* Set a human-readable reason for why this mailbox is being accessed. This is used for logging purposes. */ void mailbox_set_reason(struct mailbox *box, const char *reason); /* Get mailbox existence state. If auto_boxes=FALSE, return MAILBOX_EXISTENCE_NONE for autocreated mailboxes that haven't been physically created yet */ int mailbox_exists(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r); /* Open the mailbox. If this function isn't called explicitly, it's also called internally by lib-storage when necessary. */ int mailbox_open(struct mailbox *box); /* Open mailbox as read-only using the given stream as input. */ int mailbox_open_stream(struct mailbox *box, struct istream *input); /* Close mailbox. Same as if mailbox was freed and re-allocated. */ void mailbox_close(struct mailbox *box); /* Close and free the mailbox. */ void mailbox_free(struct mailbox **box); /* Returns TRUE if box1 points to the same mailbox as ns2/vname2. */ bool mailbox_equals(const struct mailbox *box1, const struct mail_namespace *ns2, const char *vname2) ATTR_PURE; /* Returns TRUE if the mailbox is user's INBOX or another user's shared INBOX */ bool mailbox_is_any_inbox(struct mailbox *box); /* Change mailbox_verify_create_name() to not verify new mailbox name restrictions (but still check that it's a valid existing name). This is mainly used by dsync to make sure the sync works even though the original name isn't valid anymore. */ void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set); /* Returns -1 if mailbox_create() is guaranteed to fail because the mailbox name is invalid, 0 not. The error message contains a reason. */ int mailbox_verify_create_name(struct mailbox *box); /* Create a mailbox. Returns failure if it already exists. Mailbox name is allowed to contain multiple new nonexistent hierarchy levels. If directory is TRUE, the mailbox should be created so that it can contain children. The mailbox itself doesn't have to be created as long as it shows up in LIST. If update is non-NULL, its contents are used to set initial mailbox metadata. */ int mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) ATTR_NULL(2); /* Update existing mailbox's metadata. */ int mailbox_update(struct mailbox *box, const struct mailbox_update *update); /* Delete mailbox (and its parent directory, if it has no siblings) */ int mailbox_delete(struct mailbox *box); /* Delete mailbox, but only if it's empty. If it's not, fails with MAIL_ERROR_EXISTS. */ int mailbox_delete_empty(struct mailbox *box); /* Rename mailbox (and its children). Renaming across different mailbox lists is possible only between private namespaces and storages of the same type. If the rename fails, the error is set to src's storage. */ int mailbox_rename(struct mailbox *src, struct mailbox *dest); /* Subscribe/unsubscribe mailbox. Subscribing to nonexistent mailboxes is optional. */ int mailbox_set_subscribed(struct mailbox *box, bool set); /* Returns TRUE if mailbox is subscribed, FALSE if not. This function doesn't refresh the subscriptions list, but assumes that it's been done by e.g. mailbox_list_iter*(). */ bool mailbox_is_subscribed(struct mailbox *box); /* Enable the given feature for the mailbox. */ int mailbox_enable(struct mailbox *box, enum mailbox_feature features); /* Returns all enabled features. */ enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box) ATTR_PURE; /* Returns storage of given mailbox */ struct mail_storage *mailbox_get_storage(const struct mailbox *box) ATTR_PURE; /* Return namespace of given mailbox. */ struct mail_namespace * mailbox_get_namespace(const struct mailbox *box) ATTR_PURE; /* Returns the storage's settings. */ const struct mail_storage_settings * mailbox_get_settings(struct mailbox *box) ATTR_PURE; /* Returns the mailbox's settings, or NULL if there are none. */ const struct mailbox_settings * mailbox_settings_find(struct mail_namespace *ns, const char *vname); /* Returns the (virtual) name of the given mailbox. */ const char *mailbox_get_vname(const struct mailbox *box) ATTR_PURE; /* Returns the backend name of given mailbox. */ const char *mailbox_get_name(const struct mailbox *box) ATTR_PURE; /* Returns TRUE if mailbox is read-only. */ bool mailbox_is_readonly(struct mailbox *box); /* Returns TRUE if two mailboxes point to the same physical mailbox. */ bool mailbox_backends_equal(const struct mailbox *box1, const struct mailbox *box2); /* Returns TRUE if mailbox is now in inconsistent state, meaning that the message IDs etc. may have changed - only way to recover this would be to fully close the mailbox and reopen it. With IMAP connection this would mean a forced disconnection since we can't do forced CLOSE. */ bool mailbox_is_inconsistent(struct mailbox *box); /* Gets the mailbox status information. If mailbox isn't opened yet, try to return the results from mailbox list indexes. Otherwise the mailbox is opened and synced. If the mailbox is already opened, no syncing is done automatically. */ int mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); /* Gets the mailbox status, requires that mailbox is already opened. */ void mailbox_get_open_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); /* Gets mailbox metadata */ int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r); /* Returns a mask of flags that are private to user in this mailbox (as opposed to flags shared between users). */ enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box); /* Synchronize the mailbox. */ struct mailbox_sync_context * mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); bool mailbox_sync_next(struct mailbox_sync_context *ctx, struct mailbox_sync_rec *sync_rec_r); int mailbox_sync_deinit(struct mailbox_sync_context **ctx, struct mailbox_sync_status *status_r); /* One-step mailbox synchronization. Use this if you don't care about changes. */ int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags); /* Call given callback function when something changes in the mailbox. */ void mailbox_notify_changes(struct mailbox *box, mailbox_notify_callback_t *callback, void *context) ATTR_NULL(3); #define mailbox_notify_changes(box, callback, context) \ mailbox_notify_changes(box, (mailbox_notify_callback_t *)callback, \ (void *)((char *)context + CALLBACK_TYPECHECK(callback, \ void (*)(struct mailbox *, typeof(context))))) void mailbox_notify_changes_stop(struct mailbox *box); struct mailbox_transaction_context * mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags); int mailbox_transaction_commit(struct mailbox_transaction_context **t); int mailbox_transaction_commit_get_changes( struct mailbox_transaction_context **t, struct mail_transaction_commit_changes *changes_r); void mailbox_transaction_rollback(struct mailbox_transaction_context **t); /* Set a reason for why the transaction is created. This is used for logging purposes. */ void mailbox_transaction_set_reason(struct mailbox_transaction_context *t, const char *reason); /* Return the number of active transactions for the mailbox. */ unsigned int mailbox_transaction_get_count(const struct mailbox *box) ATTR_PURE; /* When committing transaction, drop flag/keyword updates for messages whose modseq is larger than max_modseq. Save those messages' sequences to the given array. */ void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t, uint64_t max_modseq, ARRAY_TYPE(seq_range) *seqs); struct mailbox * mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t) ATTR_PURE; /* Convert uid range to sequence range. */ void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2, uint32_t *seq1_r, uint32_t *seq2_r); /* Convert sequence range to uid range. If sequences contain (uint32_t)-1 to specify "*", they're preserved. */ void mailbox_get_uid_range(struct mailbox *box, const ARRAY_TYPE(seq_range) *seqs, ARRAY_TYPE(seq_range) *uids); /* Get list of messages' that have been expunged after prev_modseq and that exist in uids_filter range. UIDs that have been expunged after the last mailbox sync aren't returned. Returns TRUE if ok, FALSE if modseq is lower than we can check for (but expunged_uids is still set as best as it can). */ bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq, const ARRAY_TYPE(seq_range) *uids_filter, ARRAY_TYPE(mailbox_expunge_rec) *expunges); /* Same as mailbox_get_expunges(), but return only list of UIDs. Not caring about GUIDs is slightly faster. */ bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq, const ARRAY_TYPE(seq_range) *uids_filter, ARRAY_TYPE(seq_range) *expunged_uids); /* Initialize header lookup for given headers. */ struct mailbox_header_lookup_ctx * mailbox_header_lookup_init(struct mailbox *box, const char *const headers[]); void mailbox_header_lookup_ref(struct mailbox_header_lookup_ctx *ctx); void mailbox_header_lookup_unref(struct mailbox_header_lookup_ctx **ctx); /* Initialize new search request. If sort_program is non-NULL, the messages are returned in the requested order, otherwise from first to last. */ struct mail_search_context * ATTR_NULL(3, 5) mailbox_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); /* Deinitialize search request. */ int mailbox_search_deinit(struct mail_search_context **ctx); /* Search the next message. Returns TRUE if found, FALSE if not. */ bool mailbox_search_next(struct mail_search_context *ctx, struct mail **mail_r); /* Like mailbox_search_next(), but don't spend too much time searching. Returns FALSE with tryagain_r=FALSE if finished, and tryagain_r=TRUE if more results will be returned by calling the function again. */ bool mailbox_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r); /* Returns TRUE if some messages were already expunged and we couldn't determine correctly if those messages should have been returned in this search. */ bool mailbox_search_seen_lost_data(struct mail_search_context *ctx); /* Remember the search result for future use. This must be called before the first mailbox_search_next*() call. */ struct mail_search_result * mailbox_search_result_save(struct mail_search_context *ctx, enum mailbox_search_result_flags flags); /* Free memory used by search result. */ void mailbox_search_result_free(struct mail_search_result **result); /* A simplified API for searching and saving the result. */ int mailbox_search_result_build(struct mailbox_transaction_context *t, struct mail_search_args *args, enum mailbox_search_result_flags flags, struct mail_search_result **result_r); /* Return all messages' UIDs in the search result. */ const ARRAY_TYPE(seq_range) * mailbox_search_result_get(struct mail_search_result *result); /* Return messages that have been removed and added since the last sync call. This function must not be called if search result wasn't saved with _QUEUE_SYNC flag. */ void mailbox_search_result_sync(struct mail_search_result *result, ARRAY_TYPE(seq_range) *removed_uids, ARRAY_TYPE(seq_range) *added_uids); /* Build mail_keywords from NULL-terminated keywords list. Any duplicate keywords are removed. Returns 0 if successful, -1 if there are invalid keywords (error is set). */ int mailbox_keywords_create(struct mailbox *box, const char *const keywords[], struct mail_keywords **keywords_r); /* Like mailbox_keywords_create(), except ignore invalid keywords. */ struct mail_keywords * mailbox_keywords_create_valid(struct mailbox *box, const char *const keywords[]); struct mail_keywords * mailbox_keywords_create_from_indexes(struct mailbox *box, const ARRAY_TYPE(keyword_indexes) *idx); void mailbox_keywords_ref(struct mail_keywords *keywords); void mailbox_keywords_unref(struct mail_keywords **keywords); /* Returns TRUE if keyword is valid, FALSE and error if not. */ bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword, const char **error_r); /* Initialize saving a new mail. You must not try to save more than one mail at a time. */ struct mail_save_context * mailbox_save_alloc(struct mailbox_transaction_context *t); /* Set the flags and keywords. Nothing is set by default. */ void mailbox_save_set_flags(struct mail_save_context *ctx, enum mail_flags flags, struct mail_keywords *keywords); /* Copy flags and keywords from given mail. */ void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail); /* Set message's modseq to be at least min_modseq. */ void mailbox_save_set_min_modseq(struct mail_save_context *ctx, uint64_t min_modseq); /* If received date isn't specified the current time is used. timezone_offset specifies the preferred timezone in minutes, but it may be ignored if backend doesn't support storing it. */ void mailbox_save_set_received_date(struct mail_save_context *ctx, time_t received_date, int timezone_offset); /* Set the "message saved" date. This should be set only when you're replicating/restoring an existing mailbox. */ void mailbox_save_set_save_date(struct mail_save_context *ctx, time_t save_date); /* Set the envelope sender. This is currently used only with mbox files to specify the address in From_-line. */ void mailbox_save_set_from_envelope(struct mail_save_context *ctx, const char *envelope); /* Set message's UID. If UID is smaller than the current next_uid, it's given a new UID anyway. */ void mailbox_save_set_uid(struct mail_save_context *ctx, uint32_t uid); /* Set globally unique ID for the saved mail. A new GUID is generated by default. This function should usually be called only when copying an existing mail (or restoring a mail from backup). */ void mailbox_save_set_guid(struct mail_save_context *ctx, const char *guid); /* Set message's POP3 UIDL, if the backend supports it. */ void mailbox_save_set_pop3_uidl(struct mail_save_context *ctx, const char *uidl); /* Specify ordering for POP3 messages. The default is to add them to the end of the mailbox. Not all backends support this. */ void mailbox_save_set_pop3_order(struct mail_save_context *ctx, unsigned int order); /* FIXME: Remove in v2.3. Obsolete - use mailbox_save_get_dest_mail() instead */ void mailbox_save_set_dest_mail(struct mail_save_context *ctx, struct mail *mail); /* Returns the destination mail */ struct mail *mailbox_save_get_dest_mail(struct mail_save_context *ctx); /* Begin saving the message. All mail_save_set_*() calls must have been called before this function. If the save initialization fails, the context is freed and -1 is returned. After beginning the save you should keep calling i_stream_read() and calling mailbox_save_continue() as long as there's more input. */ int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input); int mailbox_save_continue(struct mail_save_context *ctx); int mailbox_save_finish(struct mail_save_context **ctx); void mailbox_save_cancel(struct mail_save_context **ctx); struct mailbox_transaction_context * mailbox_save_get_transaction(struct mail_save_context *ctx); /* Copy the given message. You'll need to specify the flags etc. using the mailbox_save_*() functions. */ int mailbox_copy(struct mail_save_context **ctx, struct mail *mail); /* Move the given message. This is usually equivalent to copy+expunge, but without enforcing quota. */ int mailbox_move(struct mail_save_context **ctx, struct mail *mail); /* Same as mailbox_copy(), but treat the message as if it's being saved, not copied. (For example: New mail delivered to multiple maildirs, with each mails being hard link copies.) */ int mailbox_save_using_mail(struct mail_save_context **ctx, struct mail *mail); struct mail *mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) ATTR_NULL(3); void mail_free(struct mail **mail); void mail_set_seq(struct mail *mail, uint32_t seq); /* Returns TRUE if successful, FALSE if message doesn't exist. mail_*() functions shouldn't be called if FALSE is returned. */ bool mail_set_uid(struct mail *mail, uint32_t uid); /* Add wanted fields/headers on top of existing ones. These will be forgotten after the next mail_set_seq/uid(). */ void mail_add_temp_wanted_fields(struct mail *mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers) ATTR_NULL(3); /* Returns message's flags */ enum mail_flags mail_get_flags(struct mail *mail); /* Returns message's keywords */ const char *const *mail_get_keywords(struct mail *mail); /* Returns message's keywords */ const ARRAY_TYPE(keyword_indexes) *mail_get_keyword_indexes(struct mail *mail); /* Returns message's modseq */ uint64_t mail_get_modseq(struct mail *mail); /* Returns message's private modseq, or 0 if message hasn't had any private flag changes. This is useful only for shared mailboxes that have a private index defined. */ uint64_t mail_get_pvt_modseq(struct mail *mail); /* Returns message's MIME parts */ int mail_get_parts(struct mail *mail, struct message_part **parts_r); /* Get the Date-header of the mail. Timezone is in minutes. date=0 if it wasn't found or it was invalid. */ int mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r); /* Get the time when the mail was received (IMAP INTERNALDATE). */ int mail_get_received_date(struct mail *mail, time_t *date_r); /* Get the time when the mail was saved into this mailbox. This time may not always be entirely reliable. */ int mail_get_save_date(struct mail *mail, time_t *date_r); /* Get the space used by the mail as seen by the reader. Linefeeds are always counted as being CR+LF. */ int mail_get_virtual_size(struct mail *mail, uoff_t *size_r); /* Get the size of the stream returned by mail_get_stream(). */ int mail_get_physical_size(struct mail *mail, uoff_t *size_r); /* Get value for single header field, or NULL if header wasn't found. Returns 1 if header was found, 0 if not, -1 if error. */ int mail_get_first_header(struct mail *mail, const char *field, const char **value_r); /* Like mail_get_first_header(), but decode MIME encoded words to UTF-8. Also multiline headers are returned unfolded. Do not use this function for getting structured fields (e.g. address fields), because decoding may break the structuring. Instead parse them first and only afterwards decode the encoded words. */ int mail_get_first_header_utf8(struct mail *mail, const char *field, const char **value_r); /* Return a NULL-terminated list of values for each found field. Returns 1 if headers were found, 0 if not (value_r==NULL) or -1 if error. */ int mail_get_headers(struct mail *mail, const char *field, const char *const **value_r); /* Like mail_get_headers(), but decode MIME encoded words to UTF-8. Also multiline headers are returned unfolded. Do not use for structured fields (see mail_get_first_header_utf8()). */ int mail_get_headers_utf8(struct mail *mail, const char *field, const char *const **value_r); /* Returns stream containing specified headers. The returned stream will be automatically freed when the mail is closed, or when another mail_get_header_stream() call is made (so you can't have multiple header streams open at the same time). */ int mail_get_header_stream(struct mail *mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r); /* Returns input stream pointing to beginning of message header. hdr_size and body_size are updated unless they're NULL. The returned stream is destroyed automatically, don't unreference it. */ int mail_get_stream(struct mail *mail, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) ATTR_NULL(2, 3); /* Same as mail_get_stream(), but specify a reason why the mail is being read. This can be useful for debugging purposes. */ int mail_get_stream_because(struct mail *mail, struct message_size *hdr_size, struct message_size *body_size, const char *reason, struct istream **stream_r) ATTR_NULL(2, 3); /* Similar to mail_get_stream(), but the stream may or may not contain the message body. */ int mail_get_hdr_stream(struct mail *mail, struct message_size *hdr_size, struct istream **stream_r) ATTR_NULL(2); /* Same as mail_get_hdr_stream(), but specify a reason why the header is being read. This can be useful for debugging purposes. */ int mail_get_hdr_stream_because(struct mail *mail, struct message_size *hdr_size, const char *reason, struct istream **stream_r); /* Returns the message part's body decoded to 8bit binary. If the Content-Transfer-Encoding isn't supported, returns -1 and sets error to MAIL_ERROR_CONVERSION. If the part refers to a multipart, all of its children are returned decoded. */ int mail_get_binary_stream(struct mail *mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, bool *binary_r, struct istream **stream_r); /* Like mail_get_binary_stream(), but only return the size. */ int mail_get_binary_size(struct mail *mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *lines_r); /* Get any of the "special" fields. Unhandled specials are returned as "". */ int mail_get_special(struct mail *mail, enum mail_fetch_field field, const char **value_r); /* Returns the mail for the physical message. Normally this is the mail itself, but in virtual mailboxes it points to the backend mailbox. */ int mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r); /* FIXME: For backwards compatibility for now, use mail_get_backend_mail() instead. */ struct mail *mail_get_real_mail(struct mail *mail); /* Update message flags. */ void mail_update_flags(struct mail *mail, enum modify_type modify_type, enum mail_flags flags); /* Update message keywords. */ void mail_update_keywords(struct mail *mail, enum modify_type modify_type, struct mail_keywords *keywords); /* Update message's modseq to be at least min_modseq. */ void mail_update_modseq(struct mail *mail, uint64_t min_modseq); /* Update message's private modseq to be at least min_pvt_modseq. */ void mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq); /* Update message's POP3 UIDL (if possible). */ void mail_update_pop3_uidl(struct mail *mail, const char *uidl); /* Expunge this message. Sequence numbers don't change until commit. */ void mail_expunge(struct mail *mail); /* Add missing fields to cache. */ void mail_precache(struct mail *mail); /* Mark a cached field corrupted and have it recalculated. */ void mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field); void mail_set_cache_corrupted_reason(struct mail *mail, enum mail_fetch_field field, const char *reason); /* Return 128 bit GUID using input string. If guid is already 128 bit hex encoded, it's returned as-is. Otherwise SHA1 sum is taken and its last 128 bits are returned. */ void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r); /* Parse a human-writable string into a timestamp. utc_r controls whether the returned timestamp should be treated as an exact UTC time (TRUE), or whether this is a human-given date where the timestamp could be adjusted by the matched mails' timezones (see MAIL_SEARCH_ARG_FLAG_USE_TZ). Returns 0 and timestamp on success, -1 if the string couldn't be parsed. Currently supported string formats: yyyy-mm-dd (utc=FALSE), imap date (utc=FALSE), unix timestamp (utc=TRUE), interval (e.g. n days, utc=TRUE). */ int mail_parse_human_timestamp(const char *str, time_t *timestamp_r, bool *utc_r); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-parser-private.h0000644000175000017500000000101213123174404020615 00000000000000#ifndef MAIL_SEARCH_PARSER_PRIVATE_H #define MAIL_SEARCH_PARSER_PRIVATE_H #include "mail-search-parser.h" struct mail_search_parser_vfuncs { int (*parse_key)(struct mail_search_parser *parser, const char **key_r); int (*parse_string)(struct mail_search_parser *parser, const char **value_r); bool (*parse_skip_next)(struct mail_search_parser *parser, const char *str); }; struct mail_search_parser { struct mail_search_parser_vfuncs v; pool_t pool; const char *cur_key; const char *error; }; #endif dovecot-2.2.33.2/src/lib-storage/mail-search-mime-register.c0000644000175000017500000003401013123174404020421 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "imap-date.h" #include "imap-seqset.h" #include "imap-utf7.h" #include "imap-util.h" #include "mail-search-parser.h" #include "mail-search-mime-register.h" #include "mail-search-mime-build.h" #include "mail-search-mime.h" struct mail_search_mime_register { ARRAY(struct mail_search_mime_register_arg) args; bool args_sorted:1; }; struct mail_search_mime_register *mail_search_mime_register = NULL; static void mail_search_register_add_default(void); /* * Register */ static struct mail_search_mime_register * mail_search_mime_register_init(void) { struct mail_search_mime_register *reg = mail_search_mime_register; if (reg == NULL) { reg = i_new(struct mail_search_mime_register, 1); i_array_init(®->args, 64); mail_search_mime_register = reg; mail_search_register_add_default(); } return reg; } void mail_search_mime_register_deinit(void) { struct mail_search_mime_register *reg = mail_search_mime_register; mail_search_mime_register = NULL; if (reg == NULL) return; array_free(®->args); i_free(reg); } void mail_search_mime_register_add( const struct mail_search_mime_register_arg *arg, unsigned int count) { struct mail_search_mime_register *reg = mail_search_mime_register_init(); array_append(®->args, arg, count); reg->args_sorted = FALSE; } static int mail_search_mime_register_arg_cmp( const struct mail_search_mime_register_arg *arg1, const struct mail_search_mime_register_arg *arg2) { return strcmp(arg1->key, arg2->key); } const struct mail_search_mime_register_arg * mail_search_mime_register_get(unsigned int *count_r) { struct mail_search_mime_register *reg = mail_search_mime_register_init(); if (!reg->args_sorted) { array_sort(®->args, mail_search_mime_register_arg_cmp); reg->args_sorted = TRUE; } return array_get(®->args, count_r); } const struct mail_search_mime_register_arg * mail_search_mime_register_find(const char *key) { struct mail_search_mime_register_arg arg; struct mail_search_mime_register *reg = mail_search_mime_register_init(); if (!reg->args_sorted) { array_sort(®->args, mail_search_mime_register_arg_cmp); reg->args_sorted = TRUE; } arg.key = key; return array_bsearch(®->args, &arg, mail_search_mime_register_arg_cmp); } /* * Default MIMEPART args */ static struct mail_search_mime_arg * mail_search_mime_not(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; if (mail_search_mime_build_key(ctx, ctx->parent, &smarg) < 0) return NULL; smarg->match_not = !smarg->match_not; return smarg; } static struct mail_search_mime_arg * mail_search_mime_or(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg, **subargs; /* */ smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_OR); subargs = &smarg->value.subargs; do { if (mail_search_mime_build_key(ctx, smarg, subargs) < 0) return NULL; subargs = &(*subargs)->next; /* OR OR ... - put them all under one SEARCH_MIME_OR list. */ } while (mail_search_parse_skip_next(ctx->ctx->parser, "OR")); if (mail_search_mime_build_key(ctx, smarg, subargs) < 0) return NULL; return smarg; } #define CALLBACK_STR(_func, _type) \ static struct mail_search_mime_arg *\ mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \ { \ return mail_search_mime_build_str(ctx, _type); \ } static struct mail_search_mime_arg * arg_new_date(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *smarg; const char *value; smarg = mail_search_mime_build_new(ctx, type); if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) return NULL; if (!imap_parse_date(value, &smarg->value.time)) { ctx->ctx->_error = "Invalid search date parameter"; return NULL; } return smarg; } #define CALLBACK_DATE(_func, _type) \ static struct mail_search_mime_arg *\ mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \ { \ return arg_new_date(ctx, _type); \ } CALLBACK_DATE(sentbefore, SEARCH_MIME_SENTBEFORE) CALLBACK_DATE(senton, SEARCH_MIME_SENTON) CALLBACK_DATE(sentsince, SEARCH_MIME_SENTSINCE) static struct mail_search_mime_arg * mail_search_mime_size(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; enum mail_search_mime_arg_type type; const char *key, *value; uoff_t size; if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) { ctx->ctx->_error = "Invalid MIMEPART SIZE key type"; return NULL; } key = t_str_ucase(key); if (strcmp(key, "LARGER") == 0) type = SEARCH_MIME_SIZE_LARGER; else if (strcmp(key, "SMALLER") == 0) type = SEARCH_MIME_SIZE_SMALLER; else { type = SEARCH_MIME_SIZE_EQUAL; value = key; } if (type != SEARCH_MIME_SIZE_EQUAL && mail_search_parse_string(ctx->ctx->parser, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART SIZE value"; return NULL; } if (str_to_uoff(value, &size) < 0) { ctx->ctx->_error = "Invalid MIMEPART SIZE value"; return NULL; } smarg = mail_search_mime_build_new(ctx, type); smarg->value.size = size; return smarg; } CALLBACK_STR(description, SEARCH_MIME_DESCRIPTION) CALLBACK_STR(encoding, SEARCH_MIME_ENCODING) CALLBACK_STR(id, SEARCH_MIME_ID) CALLBACK_STR(language, SEARCH_MIME_LANGUAGE) CALLBACK_STR(location, SEARCH_MIME_LOCATION) CALLBACK_STR(md5, SEARCH_MIME_MD5) CALLBACK_STR(type, SEARCH_MIME_TYPE) CALLBACK_STR(subtype, SEARCH_MIME_SUBTYPE) CALLBACK_STR(bcc, SEARCH_MIME_BCC) CALLBACK_STR(cc, SEARCH_MIME_CC) CALLBACK_STR(from, SEARCH_MIME_FROM) CALLBACK_STR(in_reply_to, SEARCH_MIME_IN_REPLY_TO) CALLBACK_STR(message_id, SEARCH_MIME_MESSAGE_ID) CALLBACK_STR(reply_to, SEARCH_MIME_REPLY_TO) CALLBACK_STR(sender, SEARCH_MIME_SENDER) CALLBACK_STR(subject, SEARCH_MIME_SUBJECT) CALLBACK_STR(to, SEARCH_MIME_TO) static struct mail_search_mime_arg * arg_new_field(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *smarg; const char *field_name, *value; /* */ if (mail_search_parse_string(ctx->ctx->parser, &field_name) < 0) return NULL; if (mail_search_build_get_utf8(ctx->ctx, field_name, &field_name) < 0) return NULL; if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) return NULL; if (mail_search_build_get_utf8(ctx->ctx, value, &value) < 0) return NULL; smarg = mail_search_mime_build_new(ctx, type); smarg->field_name = str_ucase(p_strdup(ctx->ctx->pool, field_name)); smarg->value.str = value; return smarg; } static struct mail_search_mime_arg * mail_search_mime_param(struct mail_search_mime_build_context *ctx) { return arg_new_field (ctx, SEARCH_MIME_PARAM); } static struct mail_search_mime_arg * mail_search_mime_header(struct mail_search_mime_build_context *ctx) { return arg_new_field (ctx, SEARCH_MIME_HEADER); } static struct mail_search_mime_arg * arg_new_body(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type) { struct mail_search_mime_arg *smarg; smarg = mail_search_mime_build_str(ctx, type); if (smarg == NULL) return NULL; if (mail_search_build_get_utf8(ctx->ctx, smarg->value.str, &smarg->value.str) < 0) return NULL; return smarg; } #define CALLBACK_BODY(_func, _type) \ static struct mail_search_mime_arg *\ mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \ { \ return arg_new_body(ctx, _type); \ } CALLBACK_BODY(body, SEARCH_MIME_BODY) CALLBACK_BODY(text, SEARCH_MIME_TEXT) static struct mail_search_mime_arg * mail_search_mime_disposition(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; const char *key, *value; if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) { ctx->ctx->_error = "Invalid MIMEPART DISPOSITION key type"; return NULL; } key = t_str_ucase(key); if (strcmp(key, "TYPE") == 0) { if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART DISPOSITION TYPE value"; return NULL; } smarg = mail_search_mime_build_new (ctx, SEARCH_MIME_DISPOSITION_TYPE); smarg->value.str = p_strdup(ctx->ctx->pool, value); return smarg; } else if (strcmp(key, "PARAM") == 0) { return arg_new_field (ctx, SEARCH_MIME_DISPOSITION_PARAM); } ctx->ctx->_error = "Invalid MIMEPART DISPOSITION key type"; return NULL; } static struct mail_search_mime_arg * mail_search_mime_depth(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; enum mail_search_mime_arg_type type; const char *key, *value; unsigned int depth; if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) { ctx->ctx->_error = "Invalid MIMEPART DEPTH key"; return NULL; } key = t_str_ucase(key); if (strcmp(key, "MIN") == 0) type = SEARCH_MIME_DEPTH_MIN; else if (strcmp(key, "MAX") == 0) type = SEARCH_MIME_DEPTH_MAX; else { type = SEARCH_MIME_DEPTH_EQUAL; value = key; } if (type != SEARCH_MIME_DEPTH_EQUAL && mail_search_parse_string(ctx->ctx->parser, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART DEPTH value"; return NULL; } if (str_to_uint(value, &depth) < 0) { ctx->ctx->_error = "Invalid MIMEPART DEPTH level"; return NULL; } smarg = mail_search_mime_build_new(ctx, type); smarg->value.number = depth; return smarg; } static struct mail_search_mime_arg * mail_search_mime_index(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; const char *value; unsigned int index; if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART INDEX value"; return NULL; } if (str_to_uint(value, &index) < 0) { ctx->ctx->_error = "Invalid MIMEPART INDEX number"; return NULL; } smarg = mail_search_mime_build_new (ctx, SEARCH_MIME_INDEX); smarg->value.number = index; return smarg; } static struct mail_search_mime_arg * mail_search_mime_filename(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg; enum mail_search_mime_arg_type type; const char *key, *value; if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) { ctx->ctx->_error = "Invalid MIMEPART FILENAME match type"; return NULL; } key = t_str_ucase(key); if (strcmp(key, "IS") == 0) type = SEARCH_MIME_FILENAME_IS; else if (strcmp(key, "CONTAINS") == 0) type = SEARCH_MIME_FILENAME_CONTAINS; else if (strcmp(key, "BEGINS") == 0) type = SEARCH_MIME_FILENAME_BEGINS; else if (strcmp(key, "ENDS") == 0) type = SEARCH_MIME_FILENAME_ENDS; else { ctx->ctx->_error = "Invalid MIMEPART FILENAME match type"; return NULL; } if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART FILENAME string value"; return NULL; } if (mail_search_build_get_utf8(ctx->ctx, value, &value) < 0) { ctx->ctx->_error = "Invalid MIMEPART FILENAME stromg value"; return NULL; } smarg = mail_search_mime_build_new(ctx, type); smarg->value.str = value; return smarg; } static struct mail_search_mime_arg * mail_search_mime_parent(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg, *subargs; smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_PARENT); if (mail_search_mime_build_key(ctx, smarg, &subargs) < 0) return NULL; if (subargs == smarg) smarg->value.subargs = NULL; else if (subargs->type == SEARCH_MIME_SUB) smarg->value.subargs = subargs->value.subargs; else smarg->value.subargs = subargs; return smarg; } static struct mail_search_mime_arg * mail_search_mime_child(struct mail_search_mime_build_context *ctx) { struct mail_search_mime_arg *smarg, *subargs; smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_CHILD); if (mail_search_mime_build_key(ctx, smarg, &subargs) < 0) return NULL; if (subargs == smarg) smarg->value.subargs = NULL; else if (subargs->type == SEARCH_MIME_SUB) smarg->value.subargs = subargs->value.subargs; else smarg->value.subargs = subargs; return smarg; } static struct mail_search_mime_arg * mail_search_mime_exists(struct mail_search_mime_build_context *ctx) { if (ctx->parent == NULL || (ctx->parent->type != SEARCH_MIME_PARENT && ctx->parent->type != SEARCH_MIME_CHILD)) { ctx->ctx->_error = "EXISTS key can only be used with PARENT or CHILD"; return NULL; } return ctx->parent; } static const struct mail_search_mime_register_arg mime_register_args[] = { /* argument set operations */ { "NOT", mail_search_mime_not }, { "OR", mail_search_mime_or }, /* dates */ { "SENTBEFORE", mail_search_mime_sentbefore }, { "SENTON", mail_search_mime_senton }, { "SENTSINCE", mail_search_mime_sentsince }, /* size */ { "SIZE", mail_search_mime_size }, /* part properties */ { "DESCRIPTION", mail_search_mime_description }, { "DISPOSITION", mail_search_mime_disposition }, { "ENCODING", mail_search_mime_encoding }, { "ID", mail_search_mime_id }, { "LANGUAGE", mail_search_mime_language }, { "LOCATION", mail_search_mime_location }, { "MD5", mail_search_mime_md5 }, /* content-type */ { "TYPE", mail_search_mime_type }, { "SUBTYPE", mail_search_mime_subtype }, { "PARAM", mail_search_mime_param }, /* headers */ { "HEADER", mail_search_mime_header }, /* message */ { "BCC", mail_search_mime_bcc }, { "CC", mail_search_mime_cc }, { "FROM", mail_search_mime_from }, { "IN-REPLY-TO", mail_search_mime_in_reply_to }, { "MESSAGE-ID", mail_search_mime_message_id }, { "REPLY-TO", mail_search_mime_reply_to }, { "SENDER", mail_search_mime_sender }, { "SUBJECT", mail_search_mime_subject }, { "TO", mail_search_mime_to }, /* body */ { "BODY", mail_search_mime_body }, { "TEXT", mail_search_mime_text }, /* position */ { "DEPTH", mail_search_mime_depth }, { "INDEX", mail_search_mime_index }, /* relations */ { "PARENT", mail_search_mime_parent }, { "CHILD", mail_search_mime_child }, { "EXISTS", mail_search_mime_exists }, /* filename */ { "FILENAME", mail_search_mime_filename }, }; static void mail_search_register_add_default(void) { mail_search_mime_register_add(mime_register_args, N_ELEMENTS(mime_register_args)); } dovecot-2.2.33.2/src/lib-storage/mailbox-tree.c0000644000175000017500000001732013165463624016075 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "mailbox-tree.h" struct mailbox_tree_context { pool_t pool; char separator; bool parents_nonexistent; bool sorted; unsigned int node_size; struct mailbox_node *nodes; }; struct mailbox_tree_iterate_context { struct mailbox_node *root, *next_node; unsigned int flags_mask; char separator; ARRAY(struct mailbox_node *) node_path; string_t *path_str; size_t parent_pos; unsigned int first_child:1; }; struct mailbox_tree_context *mailbox_tree_init(char separator) { return mailbox_tree_init_size(separator, sizeof(struct mailbox_node)); } struct mailbox_tree_context * mailbox_tree_init_size(char separator, unsigned int mailbox_node_size) { struct mailbox_tree_context *tree; i_assert(mailbox_node_size >= sizeof(struct mailbox_node)); tree = i_new(struct mailbox_tree_context, 1); tree->pool = pool_alloconly_create(MEMPOOL_GROWING"mailbox_tree", 10240); tree->separator = separator; tree->node_size = mailbox_node_size; return tree; } void mailbox_tree_deinit(struct mailbox_tree_context **_tree) { struct mailbox_tree_context *tree = *_tree; *_tree = NULL; pool_unref(&tree->pool); i_free(tree); } void mailbox_tree_set_separator(struct mailbox_tree_context *tree, char separator) { tree->separator = separator; } void mailbox_tree_set_parents_nonexistent(struct mailbox_tree_context *tree) { tree->parents_nonexistent = TRUE; } void mailbox_tree_clear(struct mailbox_tree_context *tree) { p_clear(tree->pool); tree->nodes = NULL; } pool_t mailbox_tree_get_pool(struct mailbox_tree_context *tree) { return tree->pool; } static struct mailbox_node * ATTR_NULL(2) mailbox_tree_traverse(struct mailbox_tree_context *tree, const char *path, bool create, bool *created_r) { struct mailbox_node **node, *parent; const char *name; string_t *str; *created_r = FALSE; if (path == NULL) return tree->nodes; if (strncasecmp(path, "INBOX", 5) == 0 && (path[5] == '\0' || path[5] == tree->separator)) path = t_strdup_printf("INBOX%s", path+5); parent = NULL; node = &tree->nodes; str = t_str_new(strlen(path)+1); for (name = path;; path++) { if (*path != tree->separator && *path != '\0') continue; str_truncate(str, 0); str_append_n(str, name, (size_t) (path - name)); name = str_c(str); /* find the node */ while (*node != NULL) { if (strcmp((*node)->name, name) == 0) break; node = &(*node)->next; } if (*node == NULL) { /* not found, create it */ if (!create) break; *node = p_malloc(tree->pool, tree->node_size); (*node)->parent = parent; (*node)->name = p_strdup(tree->pool, name); if (tree->parents_nonexistent) (*node)->flags = MAILBOX_NONEXISTENT; tree->sorted = FALSE; *created_r = TRUE; } if (*path == '\0') break; name = path+1; parent = *node; node = &(*node)->children; } return *node; } struct mailbox_node * mailbox_tree_get(struct mailbox_tree_context *tree, const char *path, bool *created_r) { struct mailbox_node *node; bool created; T_BEGIN { node = mailbox_tree_traverse(tree, path, TRUE, &created); } T_END; if (created && tree->parents_nonexistent) node->flags = 0; *created_r = created; return node; } struct mailbox_node * mailbox_tree_lookup(struct mailbox_tree_context *tree, const char *path) { struct mailbox_node *node; bool created; T_BEGIN { node = mailbox_tree_traverse(tree, path, FALSE, &created); } T_END; return node; } struct mailbox_tree_iterate_context * mailbox_tree_iterate_init(struct mailbox_tree_context *tree, struct mailbox_node *root, unsigned int flags_mask) { struct mailbox_tree_iterate_context *ctx; ctx = i_new(struct mailbox_tree_iterate_context, 1); ctx->separator = tree->separator; ctx->root = root != NULL ? root : tree->nodes; ctx->flags_mask = flags_mask; ctx->path_str = str_new(default_pool, 256); i_array_init(&ctx->node_path, 16); ctx->next_node = ctx->root; return ctx; } static void mailbox_tree_iterate_set_next_node(struct mailbox_tree_iterate_context *ctx) { struct mailbox_node *node = ctx->next_node; struct mailbox_node *const *nodes; unsigned int i, count; if (node->children != NULL) { array_append(&ctx->node_path, &node, 1); ctx->parent_pos = str_len(ctx->path_str); node = node->children; ctx->first_child = TRUE; } else if (node->next != NULL) { node = node->next; } else { nodes = array_get(&ctx->node_path, &count); node = NULL; for (i = count; i != 0; i--) { size_t len = strlen(nodes[i-1]->name) + 1; i_assert(len <= ctx->parent_pos); ctx->parent_pos -= len; if (nodes[i-1]->next != NULL) { node = nodes[i-1]->next; ctx->first_child = TRUE; i--; if (ctx->parent_pos != 0) ctx->parent_pos--; break; } } array_delete(&ctx->node_path, i, count - i); } ctx->next_node = node; } struct mailbox_node * mailbox_tree_iterate_next(struct mailbox_tree_iterate_context *ctx, const char **path_r) { struct mailbox_node *node; do { node = ctx->next_node; if (node == NULL) return NULL; str_truncate(ctx->path_str, ctx->parent_pos); if (ctx->first_child) { ctx->first_child = FALSE; if (node->parent != NULL) { str_append_c(ctx->path_str, ctx->separator); ctx->parent_pos++; } } str_append(ctx->path_str, node->name); mailbox_tree_iterate_set_next_node(ctx); } while ((node->flags & ctx->flags_mask) != ctx->flags_mask); *path_r = str_c(ctx->path_str); return node; } void mailbox_tree_iterate_deinit(struct mailbox_tree_iterate_context **_ctx) { struct mailbox_tree_iterate_context *ctx = *_ctx; *_ctx = NULL; str_free(&ctx->path_str); array_free(&ctx->node_path); i_free(ctx); } static struct mailbox_node * ATTR_NULL(1, 2) mailbox_tree_dup_branch(struct mailbox_tree_context *dest_tree, struct mailbox_node *dest_parent, const struct mailbox_node *src) { struct mailbox_node *node, *dest_nodes = NULL, **dest = &dest_nodes; for (; src != NULL; src = src->next) { *dest = node = p_malloc(dest_tree->pool, dest_tree->node_size); node->name = p_strdup(dest_tree->pool, src->name); node->flags = src->flags; node->parent = dest_parent; node->children = mailbox_tree_dup_branch(dest_tree, node, src->children); dest = &node->next; } return dest_nodes; } struct mailbox_tree_context *mailbox_tree_dup(struct mailbox_tree_context *src) { struct mailbox_tree_context *dest; /* for now we don't need to support extra data */ i_assert(src->node_size == sizeof(struct mailbox_node)); dest = mailbox_tree_init_size(src->separator, src->node_size); dest->nodes = mailbox_tree_dup_branch(dest, NULL, src->nodes); return dest; } static int mailbox_node_name_cmp(struct mailbox_node *const *node1, struct mailbox_node *const *node2) { return strcmp((*node1)->name, (*node2)->name); } static void mailbox_tree_sort_branch(struct mailbox_node **nodes, ARRAY_TYPE(mailbox_node) *tmparr) { struct mailbox_node *node, *const *nodep, **dest; if (*nodes == NULL) return; /* first put the nodes into an array and sort it */ array_clear(tmparr); for (node = *nodes; node != NULL; node = node->next) array_append(tmparr, &node, 1); array_sort(tmparr, mailbox_node_name_cmp); /* update the node pointers */ dest = nodes; array_foreach(tmparr, nodep) { *dest = *nodep; dest = &(*dest)->next; } *dest = NULL; /* sort the children */ for (node = *nodes; node != NULL; node = node->next) mailbox_tree_sort_branch(&node->children, tmparr); } void mailbox_tree_sort(struct mailbox_tree_context *tree) { if (tree->sorted) return; tree->sorted = TRUE; T_BEGIN { ARRAY_TYPE(mailbox_node) tmparr; t_array_init(&tmparr, 32); mailbox_tree_sort_branch(&tree->nodes, &tmparr); } T_END; } dovecot-2.2.33.2/src/lib-storage/mail-search-parser.h0000644000175000017500000000246313123174404017160 00000000000000#ifndef MAIL_SEARCH_PARSER_H #define MAIL_SEARCH_PARSER_H #define MAIL_SEARCH_PARSER_KEY_LIST "(" struct imap_arg; /* Build a parser parsing the given imap args. NOTE: args must not be freed until this parser is destroyed. */ struct mail_search_parser * mail_search_parser_init_imap(const struct imap_arg *args); /* Build a parser parsing the given command line args. */ struct mail_search_parser * mail_search_parser_init_cmdline(const char *const args[]); void mail_search_parser_deinit(struct mail_search_parser **parser); /* Key is set to the next search key, or MAIL_SEARCH_PARSER_KEY_LIST for beginning of a list. Returns 1 if ok, 0 if no more keys in this list/query, -1 if parsing error. */ int mail_search_parse_key(struct mail_search_parser *parser, const char **key_r); /* Get the next string. Returns 0 if ok, -1 if parsing error. */ int mail_search_parse_string(struct mail_search_parser *parser, const char **value_r); /* If next parameter equals to the given string case-insensitively, skip over it and return TRUE. Otherwise do nothing and return FALSE. */ bool mail_search_parse_skip_next(struct mail_search_parser *parser, const char *str); /* Returns the reason string for parsing error. */ const char *mail_search_parser_get_error(struct mail_search_parser *parser); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-list-private.h0000644000175000017500000002105713165463624017570 00000000000000#ifndef MAILBOX_LIST_PRIVATE_H #define MAILBOX_LIST_PRIVATE_H #include "mailbox-log.h" #include "mailbox-list-notify.h" #include "mail-namespace.h" #include "mailbox-list.h" #include "mailbox-list-iter.h" #include "mail-storage-settings.h" #define MAILBOX_LIST_NAME_MAILDIRPLUSPLUS "maildir++" #define MAILBOX_LIST_NAME_IMAPDIR "imapdir" #define MAILBOX_LIST_NAME_FS "fs" #define MAILBOX_LIST_NAME_INDEX "index" #define MAILBOX_LIST_NAME_NONE "none" #define MAILBOX_LIST_INDEX_DEFAULT_PREFIX "dovecot.list.index" #define MAILBOX_LOG_FILE_NAME "dovecot.mailbox.log" #define T_MAILBOX_LIST_ERR_NOT_FOUND(list, name) \ t_strdup_printf(MAIL_ERRSTR_MAILBOX_NOT_FOUND, \ mailbox_list_get_vname(list, name)) struct stat; struct dirent; struct fs; struct imap_match_glob; struct mailbox_tree_context; struct mailbox_list_notify; struct mailbox_list_notify_rec; #define MAILBOX_INFO_FLAGS_FINISHED(flags) \ (((flags) & (MAILBOX_SELECT | MAILBOX_NOSELECT | \ MAILBOX_NONEXISTENT)) != 0) struct mailbox_list_vfuncs { struct mailbox_list *(*alloc)(void); int (*init)(struct mailbox_list *list, const char **error_r); void (*deinit)(struct mailbox_list *list); int (*get_storage)(struct mailbox_list **list, const char *vname, struct mail_storage **storage_r); char (*get_hierarchy_sep)(struct mailbox_list *list); const char *(*get_vname)(struct mailbox_list *list, const char *storage_name); const char *(*get_storage_name)(struct mailbox_list *list, const char *vname); int (*get_path)(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type, const char **path_r); const char *(*get_temp_prefix)(struct mailbox_list *list, bool global); const char *(*join_refpattern)(struct mailbox_list *list, const char *ref, const char *pattern); struct mailbox_list_iterate_context * (*iter_init)(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags); const struct mailbox_info * (*iter_next)(struct mailbox_list_iterate_context *ctx); int (*iter_deinit)(struct mailbox_list_iterate_context *ctx); int (*get_mailbox_flags)(struct mailbox_list *list, const char *dir, const char *fname, enum mailbox_list_file_type type, enum mailbox_info_flags *flags_r); /* Returns TRUE if name is mailbox's internal file/directory. If it does, mailbox deletion assumes it can safely delete it. */ bool (*is_internal_name)(struct mailbox_list *list, const char *name); /* Read subscriptions from src_list, but place them into dest_list->subscriptions. Set errors to dest_list. */ int (*subscriptions_refresh)(struct mailbox_list *src_list, struct mailbox_list *dest_list); int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set); int (*delete_mailbox)(struct mailbox_list *list, const char *name); int (*delete_dir)(struct mailbox_list *list, const char *name); int (*delete_symlink)(struct mailbox_list *list, const char *name); int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname); int (*notify_init)(struct mailbox_list *list, enum mailbox_list_notify_event mask, struct mailbox_list_notify **notify_r); int (*notify_next)(struct mailbox_list_notify *notify, const struct mailbox_list_notify_rec **rec_r); void (*notify_deinit)(struct mailbox_list_notify *notify); void (*notify_wait)(struct mailbox_list_notify *notify, void (*callback)(void *context), void *context); void (*notify_flush)(struct mailbox_list_notify *notify); }; struct mailbox_list_module_register { unsigned int id; }; union mailbox_list_module_context { struct mailbox_list_vfuncs super; struct mailbox_list_module_register *reg; }; struct mailbox_list { const char *name; enum mailbox_list_properties props; size_t mailbox_name_max_length; struct mailbox_list_vfuncs v, *vlast; /* private: */ pool_t pool; struct mail_namespace *ns; struct mailbox_list_settings set; const struct mail_storage_settings *mail_set; enum mailbox_list_flags flags; /* may not be set yet, use mailbox_list_get_permissions() to access */ struct mailbox_permissions root_permissions; struct mailbox_tree_context *subscriptions; time_t subscriptions_mtime, subscriptions_read_time; struct mailbox_log *changelog; time_t changelog_timestamp; pool_t guid_cache_pool; HASH_TABLE(uint8_t *, struct mailbox_guid_cache_rec *) guid_cache; bool guid_cache_errors; /* Last error set in mailbox_list_set_critical(). */ char *last_internal_error; char *error_string; enum mail_error error; bool temporary_error; ARRAY(struct mail_storage_error) error_stack; ARRAY(union mailbox_list_module_context *) module_contexts; unsigned int index_root_dir_created:1; unsigned int list_index_root_dir_created:1; unsigned int guid_cache_updated:1; unsigned int guid_cache_invalidated:1; unsigned int last_error_is_internal:1; }; union mailbox_list_iterate_module_context { struct mailbox_list_module_register *reg; }; struct mailbox_list_iterate_context { struct mailbox_list *list; pool_t pool; enum mailbox_list_iter_flags flags; bool failed; bool index_iteration; struct imap_match_glob *glob; struct mailbox_list_autocreate_iterate_context *autocreate_ctx; struct mailbox_info specialuse_info; ARRAY(union mailbox_list_iterate_module_context *) module_contexts; }; struct mailbox_list_iter_update_context { struct mailbox_list_iterate_context *iter_ctx; struct mailbox_tree_context *tree_ctx; struct imap_match_glob *glob; enum mailbox_info_flags leaf_flags, parent_flags; unsigned int update_only:1; unsigned int match_parents:1; }; /* Modules should use do "my_id = mailbox_list_module_id++" and use objects' module_contexts[id] for their own purposes. */ extern struct mailbox_list_module_register mailbox_list_module_register; void mailbox_lists_init(void); void mailbox_lists_deinit(void); void mailbox_list_settings_init_defaults(struct mailbox_list_settings *set_r); int mailbox_list_settings_parse(struct mail_user *user, const char *data, struct mailbox_list_settings *set_r, const char **error_r); const char * mailbox_list_escape_name(struct mailbox_list *list, const char *vname); const char * mailbox_list_escape_name_params(const char *vname, const char *ns_prefix, char ns_sep, char list_sep, char escape_char, const char *maildir_name); const char * mailbox_list_unescape_name(struct mailbox_list *list, const char *src); const char * mailbox_list_unescape_name_params(const char *src, const char *ns_prefix, char ns_sep, char list_sep, char escape_char); const char *mailbox_list_default_get_storage_name(struct mailbox_list *list, const char *vname); const char *mailbox_list_default_get_vname(struct mailbox_list *list, const char *storage_name); const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list, enum mailbox_list_path_type type); bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set, enum mailbox_list_path_type type, const char **path_r); int mailbox_list_delete_index_control(struct mailbox_list *list, const char *name); void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx, const char *name); int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list); const struct mailbox_info * mailbox_list_iter_default_next(struct mailbox_list_iterate_context *ctx); enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d); int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list, const char *dir_path, const struct dirent *d); bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name); void mailbox_permissions_copy(struct mailbox_permissions *dest, const struct mailbox_permissions *src, pool_t pool); void mailbox_list_add_change(struct mailbox_list *list, enum mailbox_log_record_type type, const guid_128_t guid_128); void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r); void mailbox_list_clear_error(struct mailbox_list *list); void mailbox_list_set_error(struct mailbox_list *list, enum mail_error error, const char *string); void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...) ATTR_FORMAT(2, 3); void mailbox_list_set_internal_error(struct mailbox_list *list); bool mailbox_list_set_error_from_errno(struct mailbox_list *list); const struct mailbox_info * mailbox_list_iter_autocreate_filter(struct mailbox_list_iterate_context *ctx, const struct mailbox_info *_info); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-register.c0000644000175000017500000000407613165463624017520 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-search.h" #include "mail-search-register.h" struct mail_search_register { ARRAY(struct mail_search_register_arg) args; mail_search_register_fallback_t *fallback; unsigned int args_sorted:1; }; struct mail_search_register *mail_search_register_init(void) { struct mail_search_register *reg; reg = i_new(struct mail_search_register, 1); i_array_init(®->args, 64); return reg; } void mail_search_register_deinit(struct mail_search_register **_reg) { struct mail_search_register *reg = *_reg; *_reg = NULL; array_free(®->args); i_free(reg); } void mail_search_register_add(struct mail_search_register *reg, const struct mail_search_register_arg *arg, unsigned int count) { array_append(®->args, arg, count); reg->args_sorted = FALSE; } void mail_search_register_fallback(struct mail_search_register *reg, mail_search_register_fallback_t *fallback) { reg->fallback = fallback; } static int mail_search_register_arg_cmp(const struct mail_search_register_arg *arg1, const struct mail_search_register_arg *arg2) { return strcmp(arg1->key, arg2->key); } const struct mail_search_register_arg * mail_search_register_get(struct mail_search_register *reg, unsigned int *count_r) { if (!reg->args_sorted) { array_sort(®->args, mail_search_register_arg_cmp); reg->args_sorted = TRUE; } return array_get(®->args, count_r); } const struct mail_search_register_arg * mail_search_register_find(struct mail_search_register *reg, const char *key) { struct mail_search_register_arg arg; if (!reg->args_sorted) { array_sort(®->args, mail_search_register_arg_cmp); reg->args_sorted = TRUE; } arg.key = key; return array_bsearch(®->args, &arg, mail_search_register_arg_cmp); } bool mail_search_register_get_fallback(struct mail_search_register *reg, mail_search_register_fallback_t **fallback_r) { if (reg->fallback == NULL) return FALSE; *fallback_r = reg->fallback; return TRUE; } dovecot-2.2.33.2/src/lib-storage/mail-search.c0000644000175000017500000004437713165463624015706 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "imap-match.h" #include "mail-index.h" #include "mail-storage.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "mail-search.h" #include "mail-search-mime.h" static void mailbox_uidset_change(struct mail_search_arg *arg, struct mailbox *box, const ARRAY_TYPE(seq_range) *search_saved_uidset) { struct seq_range *uids; unsigned int i, count; uint32_t seq1, seq2; if (arg->value.str != NULL && strcmp(arg->value.str, "$") == 0) { /* SEARCHRES: Replace with saved uidset */ array_clear(&arg->value.seqset); if (search_saved_uidset == NULL || !array_is_created(search_saved_uidset)) return; array_append_array(&arg->value.seqset, search_saved_uidset); return; } arg->type = SEARCH_SEQSET; /* make a copy of the UIDs */ count = array_count(&arg->value.seqset); if (count == 0) { /* empty set, keep it */ return; } uids = t_new(struct seq_range, count); memcpy(uids, array_idx(&arg->value.seqset, 0), sizeof(*uids) * count); /* put them back to the range as sequences */ array_clear(&arg->value.seqset); for (i = 0; i < count; i++) { mailbox_get_seq_range(box, uids[i].seq1, uids[i].seq2, &seq1, &seq2); if (seq1 != 0) { seq_range_array_add_range(&arg->value.seqset, seq1, seq2); } if (uids[i].seq2 == (uint32_t)-1) { /* make sure the last message is in the range */ mailbox_get_seq_range(box, 1, (uint32_t)-1, &seq1, &seq2); seq_range_array_add(&arg->value.seqset, seq2); } } } void mail_search_arg_init(struct mail_search_args *args, struct mail_search_arg *arg, bool change_uidsets, const ARRAY_TYPE(seq_range) *search_saved_uidset) { struct mail_search_args *thread_args; const char *keywords[2]; for (; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_UIDSET: if (change_uidsets) T_BEGIN { mailbox_uidset_change(arg, args->box, search_saved_uidset); } T_END; break; case SEARCH_MODSEQ: if (arg->value.str == NULL) break; /* fall through - modseq with keyword */ case SEARCH_KEYWORDS: keywords[0] = arg->value.str; keywords[1] = NULL; i_assert(arg->initialized.keywords == NULL); arg->initialized.keywords = mailbox_keywords_create_valid(args->box, keywords); break; case SEARCH_MAILBOX_GLOB: { struct mail_namespace *ns = mailbox_get_namespace(args->box); arg->initialized.mailbox_glob = imap_match_init(default_pool, arg->value.str, TRUE, mail_namespace_get_sep(ns)); break; } case SEARCH_INTHREAD: thread_args = arg->initialized.search_args; if (thread_args == NULL) { arg->initialized.search_args = thread_args = p_new(args->pool, struct mail_search_args, 1); thread_args->pool = args->pool; thread_args->args = arg->value.subargs; thread_args->simplified = TRUE; thread_args->init_refcount = 1; /* simplification should have unnested all inthreads, so we'll assume that have_inthreads=FALSE */ } thread_args->refcount++; thread_args->box = args->box; /* fall through */ case SEARCH_SUB: case SEARCH_OR: mail_search_arg_init(args, arg->value.subargs, change_uidsets, search_saved_uidset); break; default: break; } } } void mail_search_args_init(struct mail_search_args *args, struct mailbox *box, bool change_uidsets, const ARRAY_TYPE(seq_range) *search_saved_uidset) { i_assert(args->init_refcount <= args->refcount); if (args->init_refcount++ > 0) { i_assert(args->box == box); return; } args->box = box; if (!args->simplified) mail_search_args_simplify(args); mail_search_arg_init(args, args->args, change_uidsets, search_saved_uidset); } void mail_search_arg_deinit(struct mail_search_arg *arg) { for (; arg != NULL; arg = arg->next) mail_search_arg_one_deinit(arg); } void mail_search_arg_one_deinit(struct mail_search_arg *arg) { switch (arg->type) { case SEARCH_MODSEQ: case SEARCH_KEYWORDS: if (arg->initialized.keywords == NULL) break; mailbox_keywords_unref(&arg->initialized.keywords); break; case SEARCH_MAILBOX_GLOB: if (arg->initialized.mailbox_glob == NULL) break; imap_match_deinit(&arg->initialized.mailbox_glob); break; case SEARCH_INTHREAD: i_assert(arg->initialized.search_args->refcount > 0); if (arg->value.search_result != NULL) mailbox_search_result_free(&arg->value.search_result); arg->initialized.search_args->refcount--; arg->initialized.search_args->box = NULL; /* fall through */ case SEARCH_SUB: case SEARCH_OR: mail_search_arg_deinit(arg->value.subargs); break; default: break; } } void mail_search_args_deinit(struct mail_search_args *args) { if (--args->init_refcount > 0) return; mail_search_arg_deinit(args->args); args->box = NULL; } static void mail_search_args_seq2uid_sub(struct mail_search_args *args, struct mail_search_arg *arg, ARRAY_TYPE(seq_range) *uids) { for (; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_SEQSET: array_clear(uids); mailbox_get_uid_range(args->box, &arg->value.seqset, uids); /* replace sequences with UIDs in the existing array. this way it's possible to switch between uidsets and seqsets constantly without leaking memory */ arg->type = SEARCH_UIDSET; array_clear(&arg->value.seqset); array_append_array(&arg->value.seqset, uids); break; case SEARCH_SUB: case SEARCH_OR: case SEARCH_INTHREAD: mail_search_args_seq2uid_sub(args, arg->value.subargs, uids); break; default: break; } } } void mail_search_args_seq2uid(struct mail_search_args *args) { T_BEGIN { ARRAY_TYPE(seq_range) uids; t_array_init(&uids, 128); mail_search_args_seq2uid_sub(args, args->args, &uids); } T_END; } void mail_search_args_ref(struct mail_search_args *args) { i_assert(args->refcount > 0); args->refcount++; } void mail_search_args_unref(struct mail_search_args **_args) { struct mail_search_args *args = *_args; i_assert(args->refcount > 0); *_args = NULL; if (--args->refcount > 0) { i_assert(args->init_refcount <= args->refcount); return; } i_assert(args->init_refcount <= 1); if (args->init_refcount == 1) mail_search_args_deinit(args); pool_unref(&args->pool); } static struct mail_search_arg * mail_search_arg_dup_one(pool_t pool, const struct mail_search_arg *arg) { struct mail_search_arg *new_arg; new_arg = p_new(pool, struct mail_search_arg, 1); new_arg->type = arg->type; new_arg->match_not = arg->match_not; new_arg->match_always = arg->match_always; new_arg->nonmatch_always = arg->nonmatch_always; new_arg->fuzzy = arg->fuzzy; new_arg->value.search_flags = arg->value.search_flags; switch (arg->type) { case SEARCH_INTHREAD: new_arg->value.thread_type = arg->value.thread_type; /* fall through */ case SEARCH_OR: case SEARCH_SUB: new_arg->value.subargs = mail_search_arg_dup(pool, arg->value.subargs); break; case SEARCH_ALL: break; case SEARCH_SEQSET: case SEARCH_UIDSET: case SEARCH_REAL_UID: p_array_init(&new_arg->value.seqset, pool, array_count(&arg->value.seqset)); array_append_array(&new_arg->value.seqset, &arg->value.seqset); break; case SEARCH_FLAGS: new_arg->value.flags = arg->value.flags; break; case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: new_arg->value.time = arg->value.time; new_arg->value.date_type = arg->value.date_type; break; case SEARCH_SMALLER: case SEARCH_LARGER: new_arg->value.size = arg->value.size; break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: new_arg->hdr_field_name = p_strdup(pool, arg->hdr_field_name); /* fall through */ case SEARCH_KEYWORDS: case SEARCH_BODY: case SEARCH_TEXT: case SEARCH_GUID: case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: new_arg->value.str = p_strdup(pool, arg->value.str); break; case SEARCH_MODSEQ: new_arg->value.modseq = p_new(pool, struct mail_search_modseq, 1); *new_arg->value.modseq = *arg->value.modseq; break; case SEARCH_MIMEPART: new_arg->value.mime_part = mail_search_mime_part_dup(pool, arg->value.mime_part); break; } return new_arg; } struct mail_search_arg * mail_search_arg_dup(pool_t pool, const struct mail_search_arg *arg) { struct mail_search_arg *new_arg = NULL, **dest = &new_arg; for (; arg != NULL; arg = arg->next) { *dest = mail_search_arg_dup_one(pool, arg); dest = &(*dest)->next; } return new_arg; } struct mail_search_args * mail_search_args_dup(const struct mail_search_args *args) { struct mail_search_args *new_args; new_args = mail_search_build_init(); new_args->simplified = args->simplified; new_args->have_inthreads = args->have_inthreads; new_args->args = mail_search_arg_dup(new_args->pool, args->args); return new_args; } void mail_search_args_reset(struct mail_search_arg *args, bool full_reset) { while (args != NULL) { if (args->type == SEARCH_OR || args->type == SEARCH_SUB) mail_search_args_reset(args->value.subargs, full_reset); if (args->match_always) { if (!full_reset) args->result = 1; else { args->match_always = FALSE; args->result = -1; } } else if (args->nonmatch_always) { if (!full_reset) args->result = 0; else { args->nonmatch_always = FALSE; args->result = -1; } } else { args->result = -1; } args = args->next; } } static void search_arg_foreach(struct mail_search_arg *arg, mail_search_foreach_callback_t *callback, void *context) { struct mail_search_arg *subarg; if (arg->result != -1) return; if (arg->type == SEARCH_SUB) { /* sublist of conditions */ i_assert(arg->value.subargs != NULL); arg->result = 1; subarg = arg->value.subargs; while (subarg != NULL) { if (subarg->result == -1) search_arg_foreach(subarg, callback, context); if (subarg->result == -1) arg->result = -1; else if (subarg->result == 0) { /* didn't match */ arg->result = 0; break; } subarg = subarg->next; } if (arg->match_not && arg->result != -1) arg->result = !arg->result; } else if (arg->type == SEARCH_OR) { /* OR-list of conditions */ i_assert(arg->value.subargs != NULL); subarg = arg->value.subargs; arg->result = 0; while (subarg != NULL) { if (subarg->result == -1) search_arg_foreach(subarg, callback, context); if (subarg->result == -1) arg->result = -1; else if (subarg->result > 0) { /* matched */ arg->result = 1; break; } subarg = subarg->next; } if (arg->match_not && arg->result != -1) arg->result = !arg->result; } else { /* just a single condition */ callback(arg, context); } } #undef mail_search_args_foreach int mail_search_args_foreach(struct mail_search_arg *args, mail_search_foreach_callback_t *callback, void *context) { int result; result = 1; for (; args != NULL; args = args->next) { search_arg_foreach(args, callback, context); if (args->result == 0) { /* didn't match */ return 0; } if (args->result == -1) result = -1; } return result; } static void search_arg_analyze(struct mail_search_arg *arg, buffer_t *headers, bool *have_body, bool *have_text) { static const char *date_hdr = "Date"; struct mail_search_arg *subarg; if (arg->result != -1) return; switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: subarg = arg->value.subargs; while (subarg != NULL) { if (subarg->result == -1) { search_arg_analyze(subarg, headers, have_body, have_text); } subarg = subarg->next; } break; case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: if (arg->value.date_type == MAIL_SEARCH_DATE_TYPE_SENT) buffer_append(headers, &date_hdr, sizeof(const char *)); break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: buffer_append(headers, &arg->hdr_field_name, sizeof(const char *)); break; case SEARCH_BODY: *have_body = TRUE; break; case SEARCH_TEXT: *have_text = TRUE; *have_body = TRUE; break; default: break; } } const char *const * mail_search_args_analyze(struct mail_search_arg *args, bool *have_headers, bool *have_body) { const char *null = NULL; buffer_t *headers; bool have_text; *have_headers = *have_body = have_text = FALSE; headers = buffer_create_dynamic(pool_datastack_create(), 128); for (; args != NULL; args = args->next) search_arg_analyze(args, headers, have_body, &have_text); *have_headers = have_text || headers->used != 0; if (headers->used == 0) return NULL; buffer_append(headers, &null, sizeof(const char *)); return buffer_get_data(headers, NULL); } static bool mail_search_args_match_mailbox_arg(const struct mail_search_arg *arg, const char *vname, char sep) { const struct mail_search_arg *subarg; bool ret; switch (arg->type) { case SEARCH_OR: subarg = arg->value.subargs; for (; subarg != NULL; subarg = subarg->next) { if (mail_search_args_match_mailbox_arg(subarg, vname, sep)) return TRUE; } return FALSE; case SEARCH_SUB: case SEARCH_INTHREAD: subarg = arg->value.subargs; for (; subarg != NULL; subarg = subarg->next) { if (!mail_search_args_match_mailbox_arg(subarg, vname, sep)) return FALSE; } return TRUE; case SEARCH_MAILBOX: ret = strcmp(arg->value.str, vname) == 0; return ret != arg->match_not; case SEARCH_MAILBOX_GLOB: { T_BEGIN { struct imap_match_glob *glob; glob = imap_match_init(pool_datastack_create(), arg->value.str, TRUE, sep); ret = imap_match(glob, vname) == IMAP_MATCH_YES; } T_END; return ret != arg->match_not; } default: break; } return TRUE; } bool mail_search_args_match_mailbox(struct mail_search_args *args, const char *vname, char sep) { const struct mail_search_arg *arg; if (!args->simplified) mail_search_args_simplify(args); for (arg = args->args; arg != NULL; arg = arg->next) { if (!mail_search_args_match_mailbox_arg(arg, vname, sep)) return FALSE; } return TRUE; } bool mail_search_arg_one_equals(const struct mail_search_arg *arg1, const struct mail_search_arg *arg2) { if (arg1->type != arg2->type || arg1->match_not != arg2->match_not || arg1->fuzzy != arg2->fuzzy || arg1->value.search_flags != arg2->value.search_flags) return FALSE; switch (arg1->type) { case SEARCH_OR: case SEARCH_SUB: return mail_search_arg_equals(arg1->value.subargs, arg2->value.subargs); case SEARCH_ALL: return TRUE; case SEARCH_SEQSET: /* sequences may point to different messages at different times, never assume they match */ return FALSE; case SEARCH_UIDSET: return array_cmp(&arg1->value.seqset, &arg2->value.seqset); case SEARCH_REAL_UID: return array_cmp(&arg1->value.seqset, &arg2->value.seqset); case SEARCH_FLAGS: return arg1->value.flags == arg2->value.flags; case SEARCH_KEYWORDS: return strcasecmp(arg1->value.str, arg2->value.str) == 0; case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: return arg1->value.time == arg2->value.time && arg1->value.date_type == arg2->value.date_type; case SEARCH_SMALLER: case SEARCH_LARGER: return arg1->value.size == arg2->value.size; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (strcasecmp(arg1->hdr_field_name, arg2->hdr_field_name) != 0) return FALSE; /* fall through */ case SEARCH_BODY: case SEARCH_TEXT: case SEARCH_GUID: case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: /* don't bother doing case-insensitive comparison. it must not be done for guid/mailbox, and for others we should support full i18n case-insensitivity (or the active comparator in future). */ return strcmp(arg1->value.str, arg2->value.str) == 0; case SEARCH_MODSEQ: { const struct mail_search_modseq *m1 = arg1->value.modseq; const struct mail_search_modseq *m2 = arg2->value.modseq; return m1->modseq == m2->modseq && m1->type == m2->type; } case SEARCH_INTHREAD: if (arg1->value.thread_type != arg2->value.thread_type) return FALSE; return mail_search_arg_equals(arg1->value.subargs, arg2->value.subargs); case SEARCH_MIMEPART: return mail_search_mime_parts_equal(arg1->value.mime_part, arg2->value.mime_part); } i_unreached(); return FALSE; } bool mail_search_arg_equals(const struct mail_search_arg *arg1, const struct mail_search_arg *arg2) { while (arg1 != NULL && arg2 != NULL) { if (!mail_search_arg_one_equals(arg1, arg2)) return FALSE; arg1 = arg1->next; arg2 = arg2->next; } return arg1 == NULL && arg2 == NULL; } bool mail_search_args_equal(const struct mail_search_args *args1, const struct mail_search_args *args2) { i_assert(args1->simplified == args2->simplified); i_assert(args1->box == args2->box); return mail_search_arg_equals(args1->args, args2->args); } static void mail_search_args_result_serialize_arg(const struct mail_search_arg *arg, buffer_t *dest) { const struct mail_search_arg *subarg; buffer_append_c(dest, arg->result < 0 ? 0xff : arg->result); switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: subarg = arg->value.subargs; for (; subarg != NULL; subarg = subarg->next) mail_search_args_result_serialize_arg(subarg, dest); default: break; } } void mail_search_args_result_serialize(const struct mail_search_args *args, buffer_t *dest) { const struct mail_search_arg *arg; for (arg = args->args; arg != NULL; arg = arg->next) mail_search_args_result_serialize_arg(arg, dest); } static void mail_search_args_result_deserialize_arg(struct mail_search_arg *arg, const unsigned char **data, size_t *size) { struct mail_search_arg *subarg; i_assert(*size > 0); arg->result = **data == 0xff ? -1 : **data; *data += 1; *size -= 1; switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: subarg = arg->value.subargs; for (; subarg != NULL; subarg = subarg->next) { mail_search_args_result_deserialize_arg(subarg, data, size); } default: break; } } void mail_search_args_result_deserialize(struct mail_search_args *args, const unsigned char *data, size_t size) { struct mail_search_arg *arg; for (arg = args->args; arg != NULL; arg = arg->next) mail_search_args_result_deserialize_arg(arg, &data, &size); } dovecot-2.2.33.2/src/lib-storage/mail-namespace.c0000644000175000017500000005324313165463624016365 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "file-lock.h" #include "settings-parser.h" #include "mailbox-list-private.h" #include "mail-storage-private.h" #include "mail-storage-settings.h" #include "mail-namespace.h" static struct mail_namespace_settings prefixless_ns_unexpanded_set = { .name = "", .type = "private", .separator = "", .prefix = "0", .location = "0fail::LAYOUT=none", .alias_for = NULL, .inbox = FALSE, .hidden = TRUE, .list = "no", .subscriptions = FALSE, .ignore_on_failure = FALSE, .disabled = FALSE, .mailboxes = ARRAY_INIT }; static struct mail_namespace_settings prefixless_ns_set; void mail_namespace_add_storage(struct mail_namespace *ns, struct mail_storage *storage) { if (ns->storage == NULL) ns->storage = storage; array_append(&ns->all_storages, &storage, 1); if (storage->v.add_list != NULL) storage->v.add_list(storage, ns->list); hook_mail_namespace_storage_added(ns); } void mail_namespace_finish_list_init(struct mail_namespace *ns, struct mailbox_list *list) { ns->list = list; ns->prefix_len = strlen(ns->prefix); } static void mail_namespace_free(struct mail_namespace *ns) { struct mail_storage **storagep; if (array_is_created(&ns->all_storages)) { array_foreach_modifiable(&ns->all_storages, storagep) mail_storage_unref(storagep); array_free(&ns->all_storages); } if (ns->list != NULL) mailbox_list_destroy(&ns->list); if (ns->owner != ns->user && ns->owner != NULL) mail_user_unref(&ns->owner); i_free(ns->prefix); i_free(ns); } static bool namespace_has_special_use_mailboxes(struct mail_namespace_settings *ns_set) { struct mailbox_settings *const *box_set; if (!array_is_created(&ns_set->mailboxes)) return FALSE; array_foreach(&ns_set->mailboxes, box_set) { if ((*box_set)->special_use[0] != '\0') return TRUE; } return FALSE; } int mail_namespace_alloc(struct mail_user *user, void *user_all_settings, struct mail_namespace_settings *ns_set, struct mail_namespace_settings *unexpanded_set, struct mail_namespace **ns_r, const char **error_r) { struct mail_namespace *ns; ns = i_new(struct mail_namespace, 1); ns->refcount = 1; ns->user = user; ns->prefix = i_strdup(ns_set->prefix); ns->set = ns_set; ns->unexpanded_set = unexpanded_set; ns->user_set = user_all_settings; ns->mail_set = mail_user_set_get_driver_settings(user->set_info, ns->user_set, MAIL_STORAGE_SET_DRIVER_NAME); i_array_init(&ns->all_storages, 2); if (strcmp(ns_set->type, "private") == 0) { ns->owner = user; ns->type = MAIL_NAMESPACE_TYPE_PRIVATE; } else if (strcmp(ns_set->type, "shared") == 0) ns->type = MAIL_NAMESPACE_TYPE_SHARED; else if (strcmp(ns_set->type, "public") == 0) ns->type = MAIL_NAMESPACE_TYPE_PUBLIC; else { *error_r = t_strdup_printf("Unknown namespace type: %s", ns_set->type); mail_namespace_free(ns); return -1; } if (strcmp(ns_set->list, "children") == 0) ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN; else if (strcmp(ns_set->list, "yes") == 0) ns->flags |= NAMESPACE_FLAG_LIST_PREFIX; else if (strcmp(ns_set->list, "no") != 0) { *error_r = t_strdup_printf("Invalid list setting value: %s", ns_set->list); mail_namespace_free(ns); return -1; } if (ns_set->inbox) { ns->flags |= NAMESPACE_FLAG_INBOX_USER | NAMESPACE_FLAG_INBOX_ANY; } if (ns_set->hidden) ns->flags |= NAMESPACE_FLAG_HIDDEN; if (ns_set->subscriptions) ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS; *ns_r = ns; return 0; } int mail_namespaces_init_add(struct mail_user *user, struct mail_namespace_settings *ns_set, struct mail_namespace_settings *unexpanded_ns_set, struct mail_namespace **ns_p, const char **error_r) { const struct mail_storage_settings *mail_set = mail_user_set_get_storage_set(user); struct mail_namespace *ns; const char *driver, *error; int ret; if (*ns_set->location == '\0') ns_set->location = mail_set->mail_location; if (mail_set->mail_debug) { i_debug("Namespace %s: type=%s, prefix=%s, sep=%s, " "inbox=%s, hidden=%s, list=%s, subscriptions=%s " "location=%s", ns_set->name, ns_set->type, ns_set->prefix, ns_set->separator == NULL ? "" : ns_set->separator, ns_set->inbox ? "yes" : "no", ns_set->hidden ? "yes" : "no", ns_set->list, ns_set->subscriptions ? "yes" : "no", ns_set->location); } if ((ret = mail_namespace_alloc(user, user->set, ns_set, unexpanded_ns_set, &ns, error_r)) < 0) return ret; if (ns_set == &prefixless_ns_set) { /* autocreated prefix="" namespace */ ns->flags |= NAMESPACE_FLAG_UNUSABLE | NAMESPACE_FLAG_AUTOCREATED; } ns->special_use_mailboxes = namespace_has_special_use_mailboxes(ns_set); if (ns->type == MAIL_NAMESPACE_TYPE_SHARED && (strchr(ns->prefix, '%') != NULL || strchr(ns->set->location, '%') != NULL)) { /* dynamic shared namespace. the above check catches wrong mixed %% usage, but still allows for specifying a shared namespace to an explicit location without any %% */ ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL; driver = MAIL_SHARED_STORAGE_NAME; } else { driver = NULL; } if (mail_storage_create(ns, driver, 0, &error) < 0) { *error_r = t_strdup_printf("Namespace '%s': %s", ns->prefix, error); mail_namespace_free(ns); return -1; } *ns_p = ns; return 0; } static bool namespace_is_valid_alias_storage(struct mail_namespace *ns, const char **error_r) { if (strcmp(ns->storage->name, ns->alias_for->storage->name) != 0) { *error_r = t_strdup_printf( "Namespace %s can't have alias_for=%s " "to a different storage type (%s vs %s)", ns->prefix, ns->alias_for->prefix, ns->storage->name, ns->alias_for->storage->name); return FALSE; } if ((ns->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 && ns->storage != ns->alias_for->storage) { *error_r = t_strdup_printf( "Namespace %s can't have alias_for=%s " "to a different storage (different root dirs)", ns->prefix, ns->alias_for->prefix); return FALSE; } return TRUE; } static int namespace_set_alias_for(struct mail_namespace *ns, struct mail_namespace *all_namespaces, const char **error_r) { if (ns->set->alias_for != NULL) { ns->alias_for = mail_namespace_find_prefix(all_namespaces, ns->set->alias_for); if (ns->alias_for == NULL) { *error_r = t_strdup_printf("Invalid namespace alias_for: %s", ns->set->alias_for); return -1; } if (ns->alias_for->alias_for != NULL) { *error_r = t_strdup_printf("Chained namespace alias_for: %s", ns->set->alias_for); return -1; } if (!namespace_is_valid_alias_storage(ns, error_r)) return -1; if ((ns->alias_for->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* copy inbox=yes */ ns->flags |= NAMESPACE_FLAG_INBOX_USER; } ns->alias_chain_next = ns->alias_for->alias_chain_next; ns->alias_for->alias_chain_next = ns; } return 0; } static bool namespaces_check(struct mail_namespace *namespaces, const char **error_r) { struct mail_namespace *ns, *inbox_ns = NULL; unsigned int subscriptions_count = 0; bool visible_namespaces = FALSE, have_list_yes = FALSE; char ns_sep, list_sep = '\0'; for (ns = namespaces; ns != NULL; ns = ns->next) { ns_sep = mail_namespace_get_sep(ns); if (mail_namespace_find_prefix(ns->next, ns->prefix) != NULL) { *error_r = t_strdup_printf( "Duplicate namespace prefix: \"%s\"", ns->prefix); return FALSE; } if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0) visible_namespaces = TRUE; /* check the inbox=yes status before alias_for changes it */ if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { if (inbox_ns != NULL) { *error_r = "There can be only one namespace with " "inbox=yes"; return FALSE; } inbox_ns = ns; } if (namespace_set_alias_for(ns, namespaces, error_r) < 0) return FALSE; if (*ns->prefix != '\0' && (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) != 0 && ns->prefix[strlen(ns->prefix)-1] != ns_sep) { *error_r = t_strdup_printf( "list=yes requires prefix=%s " "to end with separator %c", ns->prefix, ns_sep); return FALSE; } if (*ns->prefix != '\0' && (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) != 0 && ns->prefix[0] == ns_sep) { *error_r = t_strdup_printf( "list=yes requires prefix=%s " "not to start with separator", ns->prefix); return FALSE; } if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) != 0) { if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0) have_list_yes = TRUE; if (list_sep == '\0') list_sep = ns_sep; else if (list_sep != ns_sep) { *error_r = "All list=yes namespaces must use " "the same separator"; return FALSE; } } if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) subscriptions_count++; } if (inbox_ns == NULL) { *error_r = "inbox=yes namespace missing"; return FALSE; } if (!have_list_yes) { *error_r = "list=yes namespace missing"; return FALSE; } if (!visible_namespaces) { *error_r = "hidden=no namespace missing"; return FALSE; } if (subscriptions_count == 0) { *error_r = "subscriptions=yes namespace missing"; return FALSE; } return TRUE; } int mail_namespaces_init_finish(struct mail_namespace *namespaces, const char **error_r) { struct mail_namespace *ns; bool prefixless_found = FALSE; i_assert(namespaces != NULL); for (ns = namespaces; ns != NULL; ns = ns->next) { if (ns->prefix_len == 0) prefixless_found = TRUE; } if (!prefixless_found) { prefixless_ns_set = prefixless_ns_unexpanded_set; /* a pretty evil way to expand the values */ prefixless_ns_set.prefix++; prefixless_ns_set.location++; if (mail_namespaces_init_add(namespaces->user, &prefixless_ns_set, &prefixless_ns_unexpanded_set, &ns, error_r) < 0) i_unreached(); ns->next = namespaces; namespaces = ns; } if (namespaces->user->autocreated) { /* e.g. raw user - don't check namespaces' validity */ } else if (!namespaces_check(namespaces, error_r)) { namespaces->user->error = t_strconcat("namespace configuration error: ", *error_r, NULL); } if (namespaces->user->error == NULL) { mail_user_add_namespace(namespaces->user, &namespaces); T_BEGIN { hook_mail_namespaces_created(namespaces); } T_END; } /* allow namespace hooks to return failure via the user error */ if (namespaces->user->error != NULL) { namespaces->user->namespaces = NULL; *error_r = t_strdup(namespaces->user->error); while (namespaces != NULL) { ns = namespaces; namespaces = ns->next; mail_namespace_free(ns); } return -1; } namespaces->user->namespaces_created = TRUE; return 0; } int mail_namespaces_init(struct mail_user *user, const char **error_r) { const struct mail_storage_settings *mail_set; struct mail_namespace_settings *const *ns_set; struct mail_namespace_settings *const *unexpanded_ns_set; struct mail_namespace *namespaces, **ns_p; unsigned int i, count, count2; i_assert(user->initialized); namespaces = NULL; ns_p = &namespaces; mail_set = mail_user_set_get_storage_set(user); if (array_is_created(&user->set->namespaces)) { ns_set = array_get(&user->set->namespaces, &count); unexpanded_ns_set = array_get(&user->unexpanded_set->namespaces, &count2); i_assert(count == count2); } else { ns_set = unexpanded_ns_set = NULL; count = 0; } for (i = 0; i < count; i++) { if (ns_set[i]->disabled) continue; if (mail_namespaces_init_add(user, ns_set[i], unexpanded_ns_set[i], ns_p, error_r) < 0) { if (!ns_set[i]->ignore_on_failure) { mail_namespaces_deinit(&namespaces); return -1; } if (mail_set->mail_debug) { i_debug("Skipping namespace %s: %s", ns_set[i]->prefix, *error_r); } } else { ns_p = &(*ns_p)->next; } } if (namespaces == NULL) { /* no namespaces defined, create a default one */ return mail_namespaces_init_location(user, NULL, error_r); } return mail_namespaces_init_finish(namespaces, error_r); } int mail_namespaces_init_location(struct mail_user *user, const char *location, const char **error_r) { struct mail_namespace_settings *inbox_set, *unexpanded_inbox_set; struct mail_namespace *ns; const struct mail_storage_settings *mail_set; const char *error, *driver, *location_source; bool default_location = FALSE; int ret; i_assert(location == NULL || *location != '\0'); inbox_set = p_new(user->pool, struct mail_namespace_settings, 1); *inbox_set = mail_namespace_default_settings; inbox_set->inbox = TRUE; /* enums must be changed */ inbox_set->type = "private"; inbox_set->list = "yes"; unexpanded_inbox_set = p_new(user->pool, struct mail_namespace_settings, 1); *unexpanded_inbox_set = *inbox_set; driver = NULL; mail_set = mail_user_set_get_storage_set(user); if (location != NULL) { inbox_set->location = p_strdup(user->pool, location); location_source = "mail_location parameter"; } else if (*mail_set->mail_location != '\0') { location_source = "mail_location setting"; inbox_set->location = mail_set->mail_location; default_location = TRUE; } else { location_source = "environment MAIL"; inbox_set->location = getenv("MAIL"); } if (inbox_set->location == NULL) { /* support also maildir-specific environment */ inbox_set->location = getenv("MAILDIR"); if (inbox_set->location == NULL) inbox_set->location = ""; else { driver = "maildir"; location_source = "environment MAILDIR"; } } if (default_location) { /* treat this the same as if a namespace was created with default settings. dsync relies on finding a namespace without explicit location setting. */ unexpanded_inbox_set->location = SETTING_STRVAR_UNEXPANDED; } else { unexpanded_inbox_set->location = p_strconcat(user->pool, SETTING_STRVAR_EXPANDED, inbox_set->location, NULL); } if ((ret = mail_namespace_alloc(user, user->set, inbox_set, unexpanded_inbox_set, &ns, error_r)) < 0) return ret; if (mail_storage_create(ns, driver, 0, &error) < 0) { if (*inbox_set->location != '\0') { *error_r = t_strdup_printf( "Initializing mail storage from %s " "failed: %s", location_source, error); } else { *error_r = t_strdup_printf("mail_location not set and " "autodetection failed: %s", error); } mail_namespace_free(ns); return -1; } return mail_namespaces_init_finish(ns, error_r); } struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user) { struct mail_namespace *ns; ns = i_new(struct mail_namespace, 1); ns->refcount = 1; ns->user = user; ns->owner = user; ns->prefix = i_strdup(""); ns->flags = NAMESPACE_FLAG_INBOX_USER | NAMESPACE_FLAG_INBOX_ANY | NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_SUBSCRIPTIONS; ns->user_set = user->set; ns->mail_set = mail_user_set_get_storage_set(user); i_array_init(&ns->all_storages, 2); return ns; } void mail_namespaces_deinit(struct mail_namespace **_namespaces) { struct mail_namespace *ns, *next; /* update *_namespaces as needed, instead of immediately setting it to NULL. for example mdbox_storage.destroy() wants to go through user's namespaces. */ while (*_namespaces != NULL) { ns = *_namespaces; next = ns->next; mail_namespace_free(ns); *_namespaces = next; } } void mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces, struct mail_storage_callbacks *callbacks, void *context) { struct mail_namespace *ns; struct mail_storage *const *storagep; for (ns = namespaces; ns != NULL; ns = ns->next) { array_foreach(&ns->all_storages, storagep) mail_storage_set_callbacks(*storagep, callbacks, context); } } void mail_namespace_ref(struct mail_namespace *ns) { i_assert(ns->refcount > 0); ns->refcount++; } void mail_namespace_unref(struct mail_namespace **_ns) { struct mail_namespace *ns = *_ns; i_assert(ns->refcount > 0); *_ns = NULL; if (--ns->refcount > 0) return; i_assert(ns->destroyed); mail_namespace_free(ns); } void mail_namespace_destroy(struct mail_namespace *ns) { struct mail_namespace **nsp; i_assert(!ns->destroyed); /* remove from user's namespaces list */ for (nsp = &ns->user->namespaces; *nsp != NULL; nsp = &(*nsp)->next) { if (*nsp == ns) { *nsp = ns->next; break; } } ns->destroyed = TRUE; mail_namespace_unref(&ns); } struct mail_storage * mail_namespace_get_default_storage(struct mail_namespace *ns) { return ns->storage; } char mail_namespace_get_sep(struct mail_namespace *ns) { return *ns->set->separator != '\0' ? *ns->set->separator : mailbox_list_get_hierarchy_sep(ns->list); } char mail_namespaces_get_root_sep(struct mail_namespace *namespaces) { while ((namespaces->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0) namespaces = namespaces->next; return mail_namespace_get_sep(namespaces); } static bool mail_namespace_is_usable_prefix(struct mail_namespace *ns, const char *mailbox, bool inbox) { if (strncmp(ns->prefix, mailbox, ns->prefix_len) == 0) { /* true exact prefix match */ return TRUE; } if (inbox && strncmp(ns->prefix, "INBOX", 5) == 0 && strncmp(ns->prefix+5, mailbox+5, ns->prefix_len-5) == 0) { /* we already checked that mailbox begins with case-insensitive INBOX. this namespace also begins with INBOX and the rest of the prefix matches too. */ return TRUE; } if (strncmp(ns->prefix, mailbox, ns->prefix_len-1) == 0 && mailbox[ns->prefix_len-1] == '\0' && ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) { /* we're trying to access the namespace prefix itself */ return TRUE; } return FALSE; } static struct mail_namespace * mail_namespace_find_mask(struct mail_namespace *namespaces, const char *box, enum namespace_flags flags, enum namespace_flags mask) { struct mail_namespace *ns = namespaces; struct mail_namespace *best = NULL; size_t best_len = 0; bool inbox; inbox = strncasecmp(box, "INBOX", 5) == 0; if (inbox && box[5] == '\0') { /* find the INBOX namespace */ while (ns != NULL) { if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && (ns->flags & mask) == flags) return ns; if (*ns->prefix == '\0') best = ns; ns = ns->next; } return best; } for (; ns != NULL; ns = ns->next) { if (ns->prefix_len >= best_len && (ns->flags & mask) == flags && mail_namespace_is_usable_prefix(ns, box, inbox)) { best = ns; best_len = ns->prefix_len; } } return best; } static struct mail_namespace * mail_namespace_find_shared(struct mail_namespace *ns, const char *mailbox) { struct mailbox_list *list = ns->list; struct mail_storage *storage; if (mailbox_list_get_storage(&list, mailbox, &storage) < 0) return ns; return mailbox_list_get_namespace(list); } struct mail_namespace * mail_namespace_find(struct mail_namespace *namespaces, const char *mailbox) { struct mail_namespace *ns; ns = mail_namespace_find_mask(namespaces, mailbox, 0, 0); i_assert(ns != NULL); if (mail_namespace_is_shared_user_root(ns)) { /* see if we need to autocreate a namespace for shared user */ if (strchr(mailbox, mail_namespace_get_sep(ns)) != NULL) return mail_namespace_find_shared(ns, mailbox); } return ns; } struct mail_namespace * mail_namespace_find_unalias(struct mail_namespace *namespaces, const char **mailbox) { struct mail_namespace *ns; const char *storage_name; ns = mail_namespace_find(namespaces, *mailbox); if (ns->alias_for != NULL) { storage_name = mailbox_list_get_storage_name(ns->list, *mailbox); ns = ns->alias_for; *mailbox = mailbox_list_get_vname(ns->list, storage_name); } return ns; } struct mail_namespace * mail_namespace_find_visible(struct mail_namespace *namespaces, const char *mailbox) { return mail_namespace_find_mask(namespaces, mailbox, 0, NAMESPACE_FLAG_HIDDEN); } struct mail_namespace * mail_namespace_find_subscribable(struct mail_namespace *namespaces, const char *mailbox) { return mail_namespace_find_mask(namespaces, mailbox, NAMESPACE_FLAG_SUBSCRIPTIONS, NAMESPACE_FLAG_SUBSCRIPTIONS); } struct mail_namespace * mail_namespace_find_unsubscribable(struct mail_namespace *namespaces, const char *mailbox) { return mail_namespace_find_mask(namespaces, mailbox, 0, NAMESPACE_FLAG_SUBSCRIPTIONS); } struct mail_namespace * mail_namespace_find_inbox(struct mail_namespace *namespaces) { i_assert(namespaces != NULL); /* there should always be an INBOX */ while ((namespaces->flags & NAMESPACE_FLAG_INBOX_USER) == 0) { namespaces = namespaces->next; i_assert(namespaces != NULL); } return namespaces; } struct mail_namespace * mail_namespace_find_prefix(struct mail_namespace *namespaces, const char *prefix) { struct mail_namespace *ns; size_t len = strlen(prefix); for (ns = namespaces; ns != NULL; ns = ns->next) { if (ns->prefix_len == len && strcmp(ns->prefix, prefix) == 0) return ns; } return NULL; } struct mail_namespace * mail_namespace_find_prefix_nosep(struct mail_namespace *namespaces, const char *prefix) { struct mail_namespace *ns; size_t len = strlen(prefix); for (ns = namespaces; ns != NULL; ns = ns->next) { if (ns->prefix_len == len + 1 && strncmp(ns->prefix, prefix, len) == 0 && ns->prefix[len] == mail_namespace_get_sep(ns)) return ns; } return NULL; } bool mail_namespace_is_shared_user_root(struct mail_namespace *ns) { struct mail_storage *const *storagep; if (ns->type != MAIL_NAMESPACE_TYPE_SHARED) return FALSE; if ((ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0) { /* child of the shared root */ return FALSE; } /* if we have driver=shared storage, we're a real shared root */ array_foreach(&ns->all_storages, storagep) { if (strcmp((*storagep)->name, MAIL_SHARED_STORAGE_NAME) == 0) return TRUE; } return FALSE; } dovecot-2.2.33.2/src/lib-storage/mail-search-parser-imap.c0000644000175000017500000000561713123174404020103 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-arg.h" #include "mail-search-parser-private.h" struct imap_arg_stack { struct imap_arg_stack *prev; const struct imap_arg *args; }; struct imap_mail_search_parser { struct mail_search_parser parser; struct imap_arg_stack root, *cur; }; static int imap_search_parse_key(struct mail_search_parser *_parser, const char **key_r) { struct imap_mail_search_parser *parser = (struct imap_mail_search_parser *)_parser; const struct imap_arg *arg = parser->cur->args; struct imap_arg_stack *stack; switch (arg->type) { case IMAP_ARG_NIL: case IMAP_ARG_ATOM: *key_r = imap_arg_as_astring(arg); break; case IMAP_ARG_STRING: case IMAP_ARG_LITERAL: _parser->error = t_strconcat( "Unexpected string as search key: ", imap_arg_as_astring(arg), NULL); return -1; case IMAP_ARG_LIST: stack = p_new(_parser->pool, struct imap_arg_stack, 1); stack->prev = parser->cur; stack->args = imap_arg_as_list(arg); parser->cur->args++; parser->cur = stack; *key_r = MAIL_SEARCH_PARSER_KEY_LIST; return 1; case IMAP_ARG_EOL: parser->cur = parser->cur->prev; return 0; case IMAP_ARG_LITERAL_SIZE: case IMAP_ARG_LITERAL_SIZE_NONSYNC: i_unreached(); } parser->cur->args++; return 1; } static int imap_search_parse_string(struct mail_search_parser *_parser, const char **value_r) { struct imap_mail_search_parser *parser = (struct imap_mail_search_parser *)_parser; const struct imap_arg *arg = parser->cur->args; switch (arg->type) { case IMAP_ARG_NIL: case IMAP_ARG_ATOM: case IMAP_ARG_STRING: case IMAP_ARG_LITERAL: *value_r = imap_arg_as_astring(arg); break; case IMAP_ARG_LIST: _parser->error = "Unexpected ("; return -1; case IMAP_ARG_EOL: _parser->error = "Missing parameter for search key"; return -1; case IMAP_ARG_LITERAL_SIZE: case IMAP_ARG_LITERAL_SIZE_NONSYNC: i_unreached(); } parser->cur->args++; return 1; } static bool imap_search_parse_skip_next(struct mail_search_parser *_parser, const char *str) { struct imap_mail_search_parser *parser = (struct imap_mail_search_parser *)_parser; const char *arg; if (!imap_arg_get_astring(parser->cur->args, &arg)) return FALSE; if (strcasecmp(arg, str) != 0) return FALSE; parser->cur->args++; return TRUE; } static const struct mail_search_parser_vfuncs mail_search_parser_imap_vfuncs = { imap_search_parse_key, imap_search_parse_string, imap_search_parse_skip_next }; struct mail_search_parser * mail_search_parser_init_imap(const struct imap_arg *args) { struct imap_mail_search_parser *parser; pool_t pool; pool = pool_alloconly_create("imap search parser", 1024); parser = p_new(pool, struct imap_mail_search_parser, 1); parser->parser.pool = pool; parser->parser.v = mail_search_parser_imap_vfuncs; parser->root.args = args; parser->cur = &parser->root; return &parser->parser; } dovecot-2.2.33.2/src/lib-storage/register/0002755000175000017500000000000013172375611015240 500000000000000dovecot-2.2.33.2/src/lib-storage/register/Makefile.in0000644000175000017500000005137713172375574017250 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/register ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_register_la_LIBADD = nodist_libstorage_register_la_OBJECTS = mail-storage-register.lo \ mailbox-list-register.lo libstorage_register_la_OBJECTS = \ $(nodist_libstorage_register_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(nodist_libstorage_register_la_SOURCES) DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_register.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage nodist_libstorage_register_la_SOURCES = \ mail-storage-register.c \ mailbox-list-register.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/register/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/register/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_register.la: $(libstorage_register_la_OBJECTS) $(libstorage_register_la_DEPENDENCIES) $(EXTRA_libstorage_register_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_register_la_OBJECTS) $(libstorage_register_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-storage-register.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-list-register.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile mail-storage-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "mail-storage.h"' >>$@ for i in $(mail_storages) ; do \ echo "extern struct mail_storage $${i}_storage;" >>$@ ; \ done echo 'void mail_storage_register_all(void) {' >>$@ for i in $(mail_storages) ; do \ echo "mail_storage_class_register(&$${i}_storage);" >>$@ ; \ done echo '}' >>$@ mailbox-list-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "mailbox-list.h"' >>$@ for i in $(mailbox_list_drivers) ; do \ echo "extern struct mailbox_list $${i}_mailbox_list;" >>$@ ; \ done echo "void mailbox_list_index_init(void);" >>$@ echo 'void mailbox_list_register_all(void) {' >>$@ for i in $(mailbox_list_drivers) ; do \ echo "mailbox_list_register(&$${i}_mailbox_list);" >>$@ ; \ done echo "mailbox_list_index_init();" >>$@ echo '}' >>$@ distclean-generic: rm -f Makefile mail-storage-register.c mailbox-list-register.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/register/Makefile.am0000644000175000017500000000260713165463624017223 00000000000000noinst_LTLIBRARIES = libstorage_register.la mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mail-storage-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "mail-storage.h"' >>$@ for i in $(mail_storages) ; do \ echo "extern struct mail_storage $${i}_storage;" >>$@ ; \ done echo 'void mail_storage_register_all(void) {' >>$@ for i in $(mail_storages) ; do \ echo "mail_storage_class_register(&$${i}_storage);" >>$@ ; \ done echo '}' >>$@ mailbox-list-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "mailbox-list.h"' >>$@ for i in $(mailbox_list_drivers) ; do \ echo "extern struct mailbox_list $${i}_mailbox_list;" >>$@ ; \ done echo "void mailbox_list_index_init(void);" >>$@ echo 'void mailbox_list_register_all(void) {' >>$@ for i in $(mailbox_list_drivers) ; do \ echo "mailbox_list_register(&$${i}_mailbox_list);" >>$@ ; \ done echo "mailbox_list_index_init();" >>$@ echo '}' >>$@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage nodist_libstorage_register_la_SOURCES = \ mail-storage-register.c \ mailbox-list-register.c distclean-generic: rm -f Makefile mail-storage-register.c mailbox-list-register.c dovecot-2.2.33.2/src/lib-storage/mailbox-attribute-private.h0000644000175000017500000000055513123174404020605 00000000000000#ifndef MAILBOX_ATTRIBUTE_PRIVATE_H #define MAILBOX_ATTRIBUTE_PRIVATE_H struct mailbox_attribute_iter { struct mailbox *box; }; void mailbox_attributes_init(void); void mailbox_attributes_deinit(void); int mailbox_attribute_value_to_string(struct mail_storage *storage, const struct mail_attribute_value *value, const char **str_r); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-build.c0000644000175000017500000001357413123174404016763 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "charset-utf8.h" #include "mail-storage-private.h" #include "mail-search-register.h" #include "mail-search-parser.h" #include "mail-search-build.h" static int mail_search_build_list(struct mail_search_build_context *ctx, struct mail_search_arg **arg_r); struct mail_search_arg * mail_search_build_new(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *arg; arg = p_new(ctx->pool, struct mail_search_arg, 1); arg->type = type; return arg; } struct mail_search_arg * mail_search_build_str(struct mail_search_build_context *ctx, enum mail_search_arg_type type) { struct mail_search_arg *sarg; const char *value; sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; sarg->value.str = p_strdup(ctx->pool, value); return sarg; } static int mail_search_build_key_int(struct mail_search_build_context *ctx, struct mail_search_arg *parent, struct mail_search_arg **arg_r) { struct mail_search_arg *sarg; struct mail_search_arg *old_parent = ctx->parent; const char *key; const struct mail_search_register_arg *reg_arg; mail_search_register_fallback_t *fallback; int ret; ctx->parent = parent; if ((ret = mail_search_parse_key(ctx->parser, &key)) <= 0) return ret; if (strcmp(key, MAIL_SEARCH_PARSER_KEY_LIST) == 0) { if (mail_search_build_list(ctx, &sarg) < 0) return -1; if (sarg->value.subargs == NULL) { ctx->_error = "No search parameters inside list"; return -1; } ctx->parent = old_parent; *arg_r = sarg; return 1; } key = t_str_ucase(key); reg_arg = mail_search_register_find(ctx->reg, key); if (reg_arg != NULL) sarg = reg_arg->build(ctx); else if (mail_search_register_get_fallback(ctx->reg, &fallback)) sarg = fallback(ctx, key); else { sarg = NULL; ctx->_error = p_strconcat(ctx->pool, "Unknown argument ", key, NULL); } ctx->parent = old_parent; *arg_r = sarg; return sarg == NULL ? -1 : 1; } int mail_search_build_key(struct mail_search_build_context *ctx, struct mail_search_arg *parent, struct mail_search_arg **arg_r) { int ret; ret = mail_search_build_key_int(ctx, parent, arg_r); if (ret <= 0) { if (ret == 0) ctx->_error = "Missing argument"; return -1; } return 0; } static int mail_search_build_list(struct mail_search_build_context *ctx, struct mail_search_arg **arg_r) { struct mail_search_arg *sarg, **subargs; enum mail_search_arg_type cur_type = SEARCH_SUB; int ret; sarg = p_new(ctx->pool, struct mail_search_arg, 1); sarg->type = cur_type; subargs = &sarg->value.subargs; while ((ret = mail_search_build_key_int(ctx, sarg, subargs)) > 0) { if (cur_type == sarg->type) { /* expected type */ } else if (cur_type == SEARCH_SUB) { /* type changed. everything in this list must now belong to this type. */ cur_type = sarg->type; } else { ctx->_error = "Use parenthesis when mixing ANDs and ORs"; return -1; } subargs = &(*subargs)->next; sarg->type = SEARCH_SUB; } if (ret < 0) return -1; sarg->type = cur_type; *arg_r = sarg; return 0; } int mail_search_build(struct mail_search_register *reg, struct mail_search_parser *parser, const char **charset, struct mail_search_args **args_r, const char **error_r) { struct mail_search_build_context ctx; struct mail_search_args *args; struct mail_search_arg *root; const char *str; int ret; *args_r = NULL; *error_r = NULL; i_zero(&ctx); ctx.args = args = mail_search_build_init(); ctx.pool = args->pool; ctx.reg = reg; ctx.parser = parser; ctx.charset = p_strdup(ctx.pool, *charset); ret = mail_search_build_list(&ctx, &root); if (!ctx.charset_checked && ret == 0) { /* make sure we give an error message if charset is invalid */ ret = mail_search_build_get_utf8(&ctx, "", &str); } if (ret < 0) { *error_r = ctx._error != NULL ? t_strdup(ctx._error) : t_strdup(mail_search_parser_get_error(parser)); if (ctx.unknown_charset) *charset = NULL; pool_unref(&args->pool); return -1; } if (root->type == SEARCH_SUB && !root->match_not) { /* simple SUB root */ args->args = root->value.subargs; } else { args->args = root; } *args_r = args; return 0; } struct mail_search_args *mail_search_build_init(void) { struct mail_search_args *args; pool_t pool; pool = pool_alloconly_create("mail search args", 4096); args = p_new(pool, struct mail_search_args, 1); args->pool = pool; args->refcount = 1; return args; } struct mail_search_arg * mail_search_build_add(struct mail_search_args *args, enum mail_search_arg_type type) { struct mail_search_arg *arg; arg = p_new(args->pool, struct mail_search_arg, 1); arg->type = type; arg->next = args->args; args->args = arg; return arg; } void mail_search_build_add_all(struct mail_search_args *args) { (void)mail_search_build_add(args, SEARCH_ALL); } void mail_search_build_add_seqset(struct mail_search_args *args, uint32_t seq1, uint32_t seq2) { struct mail_search_arg *arg; arg = mail_search_build_add(args, SEARCH_SEQSET); p_array_init(&arg->value.seqset, args->pool, 1); seq_range_array_add_range(&arg->value.seqset, seq1, seq2); } int mail_search_build_get_utf8(struct mail_search_build_context *ctx, const char *input, const char **output_r) { int ret; T_BEGIN { string_t *utf8 = t_str_new(128); enum charset_result result; if (charset_to_utf8_str(ctx->charset, NULL, input, utf8, &result) < 0) { /* unknown charset */ ctx->_error = "Unknown charset"; ctx->unknown_charset = TRUE; ret = -1; } else if (result != CHARSET_RET_OK) { /* invalid key */ ctx->_error = "Invalid search key"; ret = -1; } else { *output_r = p_strdup(ctx->pool, str_c(utf8)); ret = 0; } } T_END; ctx->charset_checked = TRUE; return ret; } dovecot-2.2.33.2/src/lib-storage/mail-thread.c0000644000175000017500000000165313123174404015663 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-thread.h" struct { const char *name; enum mail_thread_type type; } mail_thread_type_strings[] = { { "REFERENCES", MAIL_THREAD_REFERENCES }, { "REFS", MAIL_THREAD_REFS }, { "ORDEREDSUBJECT", MAIL_THREAD_ORDEREDSUBJECT } }; bool mail_thread_type_parse(const char *str, enum mail_thread_type *type_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(mail_thread_type_strings); i++) { if (strcasecmp(str, mail_thread_type_strings[i].name) == 0) { *type_r = mail_thread_type_strings[i].type; return TRUE; } } return FALSE; } const char *mail_thread_type_to_str(enum mail_thread_type type) { unsigned int i; for (i = 0; i < N_ELEMENTS(mail_thread_type_strings); i++) { if (mail_thread_type_strings[i].type == type) return mail_thread_type_strings[i].name; } i_panic("Unknown mail_thread_type %d", type); } dovecot-2.2.33.2/src/lib-storage/test-mail-storage.c0000644000175000017500000002067313165463624017053 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "mail-storage-private.h" static void test_mail_storage_errors(void) { struct mail_storage storage; enum mail_error mail_error; const char *errstr; test_begin("mail storage errors"); i_zero(&storage); /* try a regular error */ mail_storage_set_error(&storage, MAIL_ERROR_PERM, "error1"); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "error1") == 0); test_assert(mail_error == MAIL_ERROR_PERM); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "error1") == 0); test_assert(mail_error == MAIL_ERROR_PERM); test_assert(!storage.last_error_is_internal); /* set the error to itself */ mail_storage_set_error(&storage, MAIL_ERROR_PARAMS, mail_storage_get_last_error(&storage, &mail_error)); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "error1") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "error1") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(!storage.last_error_is_internal); /* clear the error - asking for it afterwards is a bug */ mail_storage_clear_error(&storage); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "BUG: Unknown internal error") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "BUG: Unknown internal error") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(!storage.last_error_is_internal); /* set internal error in preparation for the next test */ test_expect_error_string("critical0"); mail_storage_set_critical(&storage, "critical0"); test_expect_no_more_errors(); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical0") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* internal error without specifying what it is. this needs to clear the previous internal error. */ mail_storage_set_internal_error(&storage); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strstr(mail_storage_get_last_internal_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(!storage.last_error_is_internal); /* proper internal error */ test_expect_error_string("critical1"); mail_storage_set_critical(&storage, "critical1"); test_expect_no_more_errors(); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical1") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* use it in the following internal error */ test_expect_error_string("critical2: critical1"); mail_storage_set_critical(&storage, "critical2: %s", mail_storage_get_last_internal_error(&storage, &mail_error)); test_expect_no_more_errors(); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical2: critical1") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* use the previous non-internal error as part of the internal error */ test_expect_error_string("critical3: "MAIL_ERRSTR_CRITICAL_MSG); mail_storage_set_critical(&storage, "critical3: %s", mail_storage_get_last_error(&storage, &mail_error)); test_expect_no_more_errors(); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); errstr = mail_storage_get_last_internal_error(&storage, &mail_error); test_assert(strncmp(errstr, "critical3: ", 11) == 0); test_assert(strstr(errstr+11, MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* clear the error again and check that all is as expected */ mail_storage_clear_error(&storage); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "BUG: Unknown internal error") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "BUG: Unknown internal error") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(!storage.last_error_is_internal); /* use internal error as a regular error (although that really shouldn't be done) */ test_expect_error_string("critical4"); mail_storage_set_critical(&storage, "critical4"); mail_storage_set_error(&storage, MAIL_ERROR_PARAMS, mail_storage_get_last_internal_error(&storage, &mail_error)); test_expect_no_more_errors(); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "critical4") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical4") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(!storage.last_error_is_internal); mail_storage_clear_error(&storage); test_end(); } static void test_mail_storage_last_error_push_pop(void) { struct mail_storage storage; enum mail_error mail_error; test_begin("mail_storage_last_error_push/pop()"); i_zero(&storage); /* regular error 1 */ mail_storage_set_error(&storage, MAIL_ERROR_PERM, "regular error 1"); mail_storage_last_error_push(&storage); /* critical error 1 */ test_expect_error_string("critical error 1"); mail_storage_set_critical(&storage, "critical error 1"); test_expect_no_more_errors(); mail_storage_last_error_push(&storage); /* regular error 2 */ mail_storage_set_error(&storage, MAIL_ERROR_PARAMS, "regular error 2"); mail_storage_last_error_push(&storage); /* critical error 2 */ test_expect_error_string("critical error 2"); mail_storage_set_critical(&storage, "critical error 2"); test_expect_no_more_errors(); mail_storage_last_error_push(&storage); /* -- clear all errors -- */ mail_storage_clear_error(&storage); /* critical error 2 pop */ mail_storage_last_error_pop(&storage); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical error 2") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* regular error 2 pop */ mail_storage_last_error_pop(&storage); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "regular error 2") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "regular error 2") == 0); test_assert(mail_error == MAIL_ERROR_PARAMS); test_assert(!storage.last_error_is_internal); /* critical error 1 pop */ mail_storage_last_error_pop(&storage); test_assert(strstr(mail_storage_get_last_error(&storage, &mail_error), MAIL_ERRSTR_CRITICAL_MSG) != NULL); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "critical error 1") == 0); test_assert(mail_error == MAIL_ERROR_TEMP); test_assert(storage.last_error_is_internal); /* regular error 1 pop */ mail_storage_last_error_pop(&storage); test_assert(strcmp(mail_storage_get_last_error(&storage, &mail_error), "regular error 1") == 0); test_assert(mail_error == MAIL_ERROR_PERM); test_assert(strcmp(mail_storage_get_last_internal_error(&storage, &mail_error), "regular error 1") == 0); test_assert(mail_error == MAIL_ERROR_PERM); test_assert(!storage.last_error_is_internal); mail_storage_clear_error(&storage); i_assert(array_count(&storage.error_stack) == 0); array_free(&storage.error_stack); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_storage_errors, test_mail_storage_last_error_push_pop, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-storage/mail-storage-private.h0000644000175000017500000007154413165463624017556 00000000000000#ifndef MAIL_STORAGE_PRIVATE_H #define MAIL_STORAGE_PRIVATE_H #include "module-context.h" #include "unichar.h" #include "file-lock.h" #include "mail-storage.h" #include "mail-storage-hooks.h" #include "mail-storage-settings.h" #include "mailbox-attribute-private.h" #include "mail-index-private.h" /* Default prefix for indexes */ #define MAIL_INDEX_PREFIX "dovecot.index" /* Block size when read()ing message header. */ #define MAIL_READ_HDR_BLOCK_SIZE (1024*4) /* Block size when read()ing message (header and) body. */ #define MAIL_READ_FULL_BLOCK_SIZE IO_BLOCK_SIZE #define MAIL_SHARED_STORAGE_NAME "shared" struct mail_storage_module_register { unsigned int id; }; struct mail_module_register { unsigned int id; }; struct mail_storage_vfuncs { const struct setting_parser_info *(*get_setting_parser_info)(void); struct mail_storage *(*alloc)(void); int (*create)(struct mail_storage *storage, struct mail_namespace *ns, const char **error_r); void (*destroy)(struct mail_storage *storage); void (*add_list)(struct mail_storage *storage, struct mailbox_list *list); void (*get_list_settings)(const struct mail_namespace *ns, struct mailbox_list_settings *set); bool (*autodetect)(const struct mail_namespace *ns, struct mailbox_list_settings *set); struct mailbox *(*mailbox_alloc)(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags); int (*purge)(struct mail_storage *storage); /* Called when mailbox list index corruption has been detected. The callback should add any missing mailboxes to the list index. Returns 0 on success, -1 on temporary failure that didn't properly fix the index. */ int (*list_index_corrupted)(struct mail_storage *storage); }; union mail_storage_module_context { struct mail_storage_vfuncs super; struct mail_storage_module_register *reg; }; enum mail_storage_class_flags { /* mailboxes are files, not directories */ MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE = 0x01, /* root_dir points to a unique directory */ MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT = 0x02, /* mailbox_open_stream() is supported */ MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS = 0x04, /* never use quota for this storage (e.g. virtual mailboxes) */ MAIL_STORAGE_CLASS_FLAG_NOQUOTA = 0x08, /* Storage doesn't need a mail root directory */ MAIL_STORAGE_CLASS_FLAG_NO_ROOT = 0x10, /* Storage uses one file per message */ MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG = 0x20, /* Messages have GUIDs (always set mailbox_status.have_guids=TRUE) */ MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS = 0x40, /* mailbox_save_set_guid() works (always set mailbox_status.have_save_guids=TRUE) */ MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS = 0x80, /* message content can be unstructured binary data (e.g. zlib plugin is allowed to compress/decompress mails) */ MAIL_STORAGE_CLASS_FLAG_BINARY_DATA = 0x100, /* Message GUIDs can only be 128bit (always set mailbox_status.have_only_guid128) */ MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUID128 = 0x200, /* Storage deletes all files internally - mailbox list's delete_mailbox() shouldn't delete anything itself. */ MAIL_STORAGE_CLASS_FLAG_NO_LIST_DELETES = 0x400, /* Storage supports stubs (used for caching purposes). */ MAIL_STORAGE_CLASS_FLAG_STUBS = 0x800, }; struct mail_binary_cache { struct timeout *to; struct mailbox *box; uint32_t uid; uoff_t orig_physical_pos; bool include_hdr; struct istream *input; uoff_t size; }; struct mail_storage_error { char *error_string; enum mail_error error; char *last_internal_error; bool last_error_is_internal; }; struct mail_storage { const char *name; enum mail_storage_class_flags class_flags; /* Fields that the storage backend can get by other means than parsing the message header/body. For example the imapc backend can lookup MAIL_FETCH_IMAP_BODYSTRUCTURE from the remote server. Adding fields here avoids adding them to index_mail_data.access_part. */ enum mail_fetch_field nonbody_access_fields; struct mail_storage_vfuncs v, *vlast; /* private: */ pool_t pool; struct mail_storage *prev, *next; /* counting number of times mail_storage_create() has returned this same storage. */ int refcount; /* counting number of objects (e.g. mailbox) that have a pointer to this storage. */ int obj_refcount; /* Linked list of all mailboxes in the storage */ struct mailbox *mailboxes; /* A "root dir" to enable storage sharing. It is only ever used for * uniqueness checking (via strcmp) and never used as a path. */ const char *unique_root_dir; /* Last error set in mail_storage_set_critical(). */ char *last_internal_error; char *error_string; enum mail_error error; ARRAY(struct mail_storage_error) error_stack; const struct mail_storage *storage_class; struct mail_user *user; const char *temp_path_prefix; const struct mail_storage_settings *set; enum mail_storage_flags flags; struct mail_storage_callbacks callbacks; void *callback_context; struct mail_binary_cache binary_cache; /* Filled lazily by mailbox_attribute_*() when accessing shared attributes. */ struct dict *_shared_attr_dict; /* Module-specific contexts. See mail_storage_module_id. */ ARRAY(union mail_storage_module_context *) module_contexts; /* Failed to create shared attribute dict, don't try again */ unsigned int shared_attr_dict_failed:1; unsigned int last_error_is_internal:1; }; struct mail_attachment_part { struct message_part *part; const char *content_type, *content_disposition; }; struct virtual_mailbox_vfuncs { /* convert backend UIDs to virtual UIDs. if some backend UID doesn't exist in mailbox, it's simply ignored */ void (*get_virtual_uids)(struct mailbox *box, struct mailbox *backend_mailbox, const ARRAY_TYPE(seq_range) *backend_uids, ARRAY_TYPE(seq_range) *virtual_uids_r); /* like get_virtual_uids(), but if a backend UID doesn't exist, convert it to 0. */ void (*get_virtual_uid_map)(struct mailbox *box, struct mailbox *backend_mailbox, const ARRAY_TYPE(seq_range) *backend_uids, ARRAY_TYPE(uint32_t) *virtual_uids_r); void (*get_virtual_backend_boxes)(struct mailbox *box, ARRAY_TYPE(mailboxes) *mailboxes, bool only_with_msgs); }; struct mailbox_vfuncs { bool (*is_readonly)(struct mailbox *box); int (*enable)(struct mailbox *box, enum mailbox_feature features); int (*exists)(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r); int (*open)(struct mailbox *box); void (*close)(struct mailbox *box); void (*free)(struct mailbox *box); int (*create_box)(struct mailbox *box, const struct mailbox_update *update, bool directory); int (*update_box)(struct mailbox *box, const struct mailbox_update *update); int (*delete_box)(struct mailbox *box); int (*rename_box)(struct mailbox *src, struct mailbox *dest); int (*get_status)(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); int (*get_metadata)(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r); int (*set_subscribed)(struct mailbox *box, bool set); int (*attribute_set)(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value); int (*attribute_get)(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r); struct mailbox_attribute_iter * (*attribute_iter_init)(struct mailbox *box, enum mail_attribute_type type, const char *prefix); const char *(*attribute_iter_next)(struct mailbox_attribute_iter *iter); int (*attribute_iter_deinit)(struct mailbox_attribute_iter *iter); /* Lookup sync extension record and figure out if it mailbox has changed since. Returns 1 = yes, 0 = no, -1 = error. */ int (*list_index_has_changed)(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq); /* Update the sync extension record. */ void (*list_index_update_sync)(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq); struct mailbox_sync_context * (*sync_init)(struct mailbox *box, enum mailbox_sync_flags flags); bool (*sync_next)(struct mailbox_sync_context *ctx, struct mailbox_sync_rec *sync_rec_r); int (*sync_deinit)(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r); /* Called once for each expunge. Called one or more times for flag/keyword changes. Once the sync is finished, called with uid=0 and sync_type=0. */ void (*sync_notify)(struct mailbox *box, uint32_t uid, enum mailbox_sync_type sync_type); void (*notify_changes)(struct mailbox *box); struct mailbox_transaction_context * (*transaction_begin)(struct mailbox *box, enum mailbox_transaction_flags flags); int (*transaction_commit)(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r); void (*transaction_rollback)(struct mailbox_transaction_context *t); enum mail_flags (*get_private_flags_mask)(struct mailbox *box); struct mail * (*mail_alloc)(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); struct mail_search_context * (*search_init)(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); int (*search_deinit)(struct mail_search_context *ctx); bool (*search_next_nonblock)(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r); /* Internal search function which updates ctx->seq */ bool (*search_next_update_seq)(struct mail_search_context *ctx); struct mail_save_context * (*save_alloc)(struct mailbox_transaction_context *t); int (*save_begin)(struct mail_save_context *ctx, struct istream *input); int (*save_continue)(struct mail_save_context *ctx); int (*save_finish)(struct mail_save_context *ctx); void (*save_cancel)(struct mail_save_context *ctx); int (*copy)(struct mail_save_context *ctx, struct mail *mail); /* Called during transaction commit/rollback if saving was done */ int (*transaction_save_commit_pre)(struct mail_save_context *save_ctx); void (*transaction_save_commit_post) (struct mail_save_context *save_ctx, struct mail_index_transaction_commit_result *result_r); void (*transaction_save_rollback)(struct mail_save_context *save_ctx); bool (*is_inconsistent)(struct mailbox *box); }; union mailbox_module_context { struct mailbox_vfuncs super; struct mail_storage_module_register *reg; }; struct mail_msgpart_partial_cache { uint32_t uid; uoff_t physical_start; uoff_t physical_pos, virtual_pos; }; struct mailbox_index_vsize { uint64_t vsize; uint32_t highest_uid; uint32_t message_count; }; struct mailbox_index_pop3_uidl { uint32_t max_uid_with_pop3_uidl; }; struct mailbox_index_first_saved { uint32_t uid; uint32_t timestamp; }; struct mailbox { const char *name; /* mailbox's virtual name (from mail_namespace_get_vname()) */ const char *vname; struct mail_storage *storage; struct mailbox_list *list; struct mailbox_vfuncs v, *vlast; /* virtual mailboxes: */ const struct virtual_mailbox_vfuncs *virtual_vfuncs; /* private: */ pool_t pool, metadata_pool; /* Linked list of all mailboxes in this storage */ struct mailbox *prev, *next; /* these won't be set until mailbox is opened: */ struct mail_index *index; struct mail_index_view *view; struct mail_cache *cache; /* Private per-user index/view for shared mailboxes. These are synced against the primary index and used to store per-user flags. These are non-NULL only when mailbox has per-user flags. */ struct mail_index *index_pvt; struct mail_index_view *view_pvt; /* Filled lazily by mailbox_get_permissions() */ struct mailbox_permissions _perm; /* Filled lazily when mailbox is opened, use mailbox_get_path() to access it */ const char *_path; /* Filled lazily when mailbox is opened, use mailbox_get_index_path() to access it */ const char *_index_path; /* Reason for why mailbox is being accessed or NULL if unknown. */ const char *reason; /* default vfuncs for new struct mails. */ const struct mail_vfuncs *mail_vfuncs; /* Mailbox settings, or NULL if defaults */ const struct mailbox_settings *set; /* If non-zero, fail mailbox_open() with this error. mailbox_alloc() can set this to force open to fail. */ enum mail_error open_error; struct istream *input; const char *index_prefix; enum mailbox_flags flags; unsigned int transaction_count; enum mailbox_feature enabled_features; struct mail_msgpart_partial_cache partial_cache; uint32_t vsize_hdr_ext_id; uint32_t pop3_uidl_hdr_ext_id; uint32_t box_name_hdr_ext_id; uint32_t box_last_rename_stamp_ext_id; uint32_t mail_vsize_ext_id; /* MAIL_RECENT flags handling */ ARRAY_TYPE(seq_range) recent_flags; uint32_t recent_flags_prev_uid; uint32_t recent_flags_count; struct mail_index_view *tmp_sync_view; /* Mailbox notification settings: */ mailbox_notify_callback_t *notify_callback; void *notify_context; struct timeout *to_notify, *to_notify_delay; struct mailbox_notify_file *notify_files; /* Increased by one for each new struct mailbox. */ unsigned int generation_sequence; /* Saved search results */ ARRAY(struct mail_search_result *) search_results; /* Module-specific contexts. See mail_storage_module_id. */ ARRAY(union mailbox_module_context *) module_contexts; /* When FAST open flag is used, the mailbox isn't actually opened until it's synced for the first time. */ unsigned int opened:1; /* Mailbox was deleted while we had it open. */ unsigned int mailbox_deleted:1; /* Mailbox is being created */ unsigned int creating:1; /* Mailbox is being deleted */ unsigned int deleting:1; /* Mailbox is being undeleted */ unsigned int mailbox_undeleting:1; /* Don't use MAIL_INDEX_SYNC_FLAG_DELETING_INDEX for sync flag */ unsigned int delete_sync_check:1; /* Delete mailbox only if it's empty */ unsigned int deleting_must_be_empty:1; /* The backend wants to skip checking if there are 0 messages before calling mailbox_list.delete_mailbox() */ unsigned int delete_skip_empty_check:1; /* Mailbox was already marked as deleted within this allocation. */ unsigned int marked_deleted:1; /* TRUE if this is an INBOX for this user */ unsigned int inbox_user:1; /* TRUE if this is an INBOX for this namespace (user or shared) */ unsigned int inbox_any:1; /* When copying to this mailbox, require that mailbox_copy() uses mailbox_save_*() to actually save a new physical copy rather than simply incrementing a reference count (e.g. via hard link) */ unsigned int disable_reflink_copy_to:1; /* Don't allow creating any new keywords */ unsigned int disallow_new_keywords:1; /* Mailbox has been synced at least once */ unsigned int synced:1; /* Updating cache file is disabled */ unsigned int mail_cache_disabled:1; /* Update first_saved field to mailbox list index. */ unsigned int update_first_saved:1; /* mailbox_verify_create_name() only checks for mailbox_verify_name() */ unsigned int skip_create_name_restrictions:1; /* v2.2.x API kludge: quick-parameter to list_index_has_changed() */ unsigned int list_index_has_changed_quick:1; /* Using LAYOUT=index and mailbox is being opened with a corrupted mailbox name. Try to revert to the previously known good name. */ unsigned int corrupted_mailbox_name:1; }; struct mail_vfuncs { void (*close)(struct mail *mail); void (*free)(struct mail *mail); void (*set_seq)(struct mail *mail, uint32_t seq, bool saving); bool (*set_uid)(struct mail *mail, uint32_t uid); void (*set_uid_cache_updates)(struct mail *mail, bool set); bool (*prefetch)(struct mail *mail); void (*precache)(struct mail *mail); void (*add_temp_wanted_fields)(struct mail *mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers); enum mail_flags (*get_flags)(struct mail *mail); const char *const *(*get_keywords)(struct mail *mail); const ARRAY_TYPE(keyword_indexes) * (*get_keyword_indexes)(struct mail *mail); uint64_t (*get_modseq)(struct mail *mail); uint64_t (*get_pvt_modseq)(struct mail *mail); int (*get_parts)(struct mail *mail, struct message_part **parts_r); int (*get_date)(struct mail *mail, time_t *date_r, int *timezone_r); int (*get_received_date)(struct mail *mail, time_t *date_r); int (*get_save_date)(struct mail *mail, time_t *date_r); int (*get_virtual_size)(struct mail *mail, uoff_t *size_r); int (*get_physical_size)(struct mail *mail, uoff_t *size_r); int (*get_first_header)(struct mail *mail, const char *field, bool decode_to_utf8, const char **value_r); int (*get_headers)(struct mail *mail, const char *field, bool decode_to_utf8, const char *const **value_r); int (*get_header_stream)(struct mail *mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r); int (*get_stream)(struct mail *mail, bool get_body, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r); int (*get_binary_stream)(struct mail *mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *lines_r, bool *binary_r, struct istream **stream_r); int (*get_special)(struct mail *mail, enum mail_fetch_field field, const char **value_r); /* FIXME: v2.3 API should change this to return -1 on failure. for now NULL means failure so we don't break backwards compatibility. */ struct mail *(*get_real_mail)(struct mail *mail); void (*update_flags)(struct mail *mail, enum modify_type modify_type, enum mail_flags flags); void (*update_keywords)(struct mail *mail, enum modify_type modify_type, struct mail_keywords *keywords); void (*update_modseq)(struct mail *mail, uint64_t min_modseq); void (*update_pvt_modseq)(struct mail *mail, uint64_t min_pvt_modseq); void (*update_pop3_uidl)(struct mail *mail, const char *uidl); void (*expunge)(struct mail *mail); void (*set_cache_corrupted)(struct mail *mail, enum mail_fetch_field field); int (*istream_opened)(struct mail *mail, struct istream **input); void (*set_cache_corrupted_reason)(struct mail *mail, enum mail_fetch_field field, const char *reason); }; union mail_module_context { struct mail_vfuncs super; struct mail_module_register *reg; }; struct mail_private { struct mail mail; struct mail_vfuncs v, *vlast; /* normally NULL, but in case this is a "backend mail" for a mail created by virtual storage, this points back to the original virtual mail. at least mailbox_copy() bypasses the virtual storage, so this allows mail_log plugin to log the copy operation using the original mailbox name. */ struct mail *vmail; uint32_t seq_pvt; /* initial wanted fields/headers, set by mail_alloc(): */ enum mail_fetch_field wanted_fields; struct mailbox_header_lookup_ctx *wanted_headers; pool_t pool, data_pool; ARRAY(union mail_module_context *) module_contexts; const char *get_stream_reason; bool autoexpunged:1; }; struct mailbox_list_context { struct mail_storage *storage; enum mailbox_list_flags flags; bool failed; }; union mailbox_transaction_module_context { struct mail_storage_module_register *reg; }; struct mailbox_transaction_stats { unsigned long open_lookup_count; unsigned long stat_lookup_count; unsigned long fstat_lookup_count; /* number of files we've opened and read */ unsigned long files_read_count; /* number of bytes we've had to read from files */ unsigned long long files_read_bytes; /* number of cache lookup hits */ unsigned long cache_hit_count; }; struct mail_save_private_changes { /* first saved mail is 0, second is 1, etc. we'll map these to UIDs using struct mail_transaction_commit_changes. */ unsigned int mailnum; enum mail_flags flags; }; struct mailbox_transaction_context { struct mailbox *box; enum mailbox_transaction_flags flags; char *reason; union mail_index_transaction_module_context module_ctx; struct mail_index_transaction_vfuncs super; int mail_ref_count; struct mail_index_transaction *itrans; struct dict_transaction_context *attr_pvt_trans, *attr_shared_trans; /* view contains all changes done within this transaction */ struct mail_index_view *view; /* for private index updates: */ struct mail_index_transaction *itrans_pvt; struct mail_index_view *view_pvt; struct mail_cache_view *cache_view; struct mail_cache_transaction_ctx *cache_trans; struct mail_transaction_commit_changes *changes; ARRAY(union mailbox_transaction_module_context *) module_contexts; uint32_t prev_pop3_uidl_tracking_seq; uint32_t highest_pop3_uidl_uid; struct mail_save_context *save_ctx; /* number of mails saved/copied within this transaction. */ unsigned int save_count; /* List of private flags added with save/copy. These are added to the private index after committing the mails to the shared index. */ ARRAY(struct mail_save_private_changes) pvt_saves; /* these statistics are never reset by mail-storage API: */ struct mailbox_transaction_stats stats; /* Set to TRUE to update stats_* fields */ unsigned int stats_track:1; /* We've done some non-transactional (e.g. dovecot-uidlist updates) */ unsigned int nontransactional_changes:1; /* FIXME: v2.3: this should be in attribute_get/set() parameters */ unsigned int internal_attribute:1; }; union mail_search_module_context { struct mail_storage_module_register *reg; }; struct mail_search_context { struct mailbox_transaction_context *transaction; struct mail_search_args *args; struct mail_search_sort_program *sort_program; enum mail_fetch_field wanted_fields; struct mailbox_header_lookup_ctx *wanted_headers; normalizer_func_t *normalizer; /* if non-NULL, specifies that a search resulting is being updated. this can be used as a search optimization: if searched message already exists in search result, it's not necessary to check if static data matches. */ struct mail_search_result *update_result; /* add matches to these search results */ ARRAY(struct mail_search_result *) results; uint32_t seq; uint32_t progress_cur, progress_max; ARRAY(union mail_search_module_context *) module_contexts; unsigned int seen_lost_data:1; unsigned int progress_hidden:1; }; struct mail_save_data { enum mail_flags flags; enum mail_flags pvt_flags; struct mail_keywords *keywords; uint64_t min_modseq; time_t received_date, save_date; int received_tz_offset; uint32_t uid, stub_seq; char *guid, *pop3_uidl, *from_envelope; unsigned int pop3_order; struct ostream *output; struct mail_save_attachment *attach; }; struct mail_save_context { struct mailbox_transaction_context *transaction; struct mail *dest_mail; /* Set during mailbox_copy(). This is useful when copying is implemented via save, and the save_*() methods want to access the source mail. */ struct mail *copy_src_mail; /* data that changes for each saved mail */ struct mail_save_data data; /* returns TRUE if message part is an attachment. */ bool (*part_is_attachment)(struct mail_save_context *ctx, const struct mail_attachment_part *part); /* mailbox_save_alloc() called, but finish/cancel not. the same context is usually returned by the backends for reuse. */ unsigned int unfinished:1; /* mailbox_save_finish() or mailbox_copy() is being called. */ unsigned int finishing:1; /* mail was copied or moved using saving (requires: copying_or_moving==TRUE). */ unsigned int copying_via_save:1; /* mail is being saved, not copied. However, this is set also with mailbox_save_using_mail() and then copying_or_moving==TRUE. */ unsigned int saving:1; /* mail is being moved - ignore quota (requires: copying_or_moving==TRUE && saving==FALSE). */ unsigned int moving:1; /* mail is being copied or moved. However, this is set also with mailbox_save_using_mail() and then saving==TRUE. */ unsigned int copying_or_moving:1; /* dest_mail was set via mailbox_save_set_dest_mail() */ unsigned int dest_mail_external:1; }; struct mailbox_sync_context { struct mailbox *box; enum mailbox_sync_flags flags; bool open_failed; }; struct mailbox_header_lookup_ctx { struct mailbox *box; pool_t pool; int refcount; unsigned int count; const char *const *name; unsigned int *idx; }; /* Modules should use do "my_id = mail_storage_module_id++" and use objects' module_contexts[id] for their own purposes. */ extern struct mail_storage_module_register mail_storage_module_register; /* Storage's module_id for mail_index. */ extern struct mail_module_register mail_module_register; #define MAIL_STORAGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_storage_mail_index_module) extern MODULE_CONTEXT_DEFINE(mail_storage_mail_index_module, &mail_index_module_register); void mail_storage_obj_ref(struct mail_storage *storage); void mail_storage_obj_unref(struct mail_storage *storage); /* Set error message in storage. Critical errors are logged with i_error(), but user sees only "internal error" message. */ void mail_storage_clear_error(struct mail_storage *storage); void mail_storage_set_error(struct mail_storage *storage, enum mail_error error, const char *string); void mail_storage_set_critical(struct mail_storage *storage, const char *fmt, ...) ATTR_FORMAT(2, 3); void mail_storage_set_internal_error(struct mail_storage *storage); void mailbox_set_index_error(struct mailbox *box); void mail_storage_set_index_error(struct mail_storage *storage, struct mail_index *index); bool mail_storage_set_error_from_errno(struct mail_storage *storage); void mail_storage_copy_list_error(struct mail_storage *storage, struct mailbox_list *list); void mail_storage_copy_error(struct mail_storage *dest, struct mail_storage *src); /* set record in mail cache corrupted */ void mail_set_mail_cache_corrupted(struct mail *mail, const char *fmt, ...) ATTR_FORMAT(2, 3); /* Indicate mail being expunged by autoexpunge */ void mail_autoexpunge(struct mail *mail); /* Returns TRUE if everything should already be in memory after this call or if prefetching is not supported, i.e. the caller shouldn't do more prefetching before this message is handled. */ bool mail_prefetch(struct mail *mail); void mail_set_aborted(struct mail *mail); void mail_set_expunged(struct mail *mail); void mail_set_seq_saving(struct mail *mail, uint32_t seq); void mailbox_set_deleted(struct mailbox *box); int mailbox_mark_index_deleted(struct mailbox *box, bool del); /* Easy wrapper for getting mailbox's MAILBOX_LIST_PATH_TYPE_MAILBOX. The mailbox must already be opened and the caller must know that the storage has mailbox files (i.e. NULL/empty path is never returned). */ const char *mailbox_get_path(struct mailbox *box) ATTR_PURE; /* Similar to mailbox_get_path() but for MAILBOX_LIST_PATH_TYPE_INDEX. */ const char *mailbox_get_index_path(struct mailbox *box) ATTR_PURE; /* Wrapper to mailbox_list_get_path() */ int mailbox_get_path_to(struct mailbox *box, enum mailbox_list_path_type type, const char **path_r); /* Get mailbox permissions. */ const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box); /* Force permissions to be refreshed on next lookup */ void mailbox_refresh_permissions(struct mailbox *box); /* Open private index files for mailbox. Returns 1 if opened, 0 if there are no private indexes (or flags) in this mailbox, -1 if error. */ int mailbox_open_index_pvt(struct mailbox *box); /* Create path's directory with proper permissions. The root directory is also created if necessary. Returns 1 if created, 0 if it already existed, -1 if error. */ int mailbox_mkdir(struct mailbox *box, const char *path, enum mailbox_list_path_type type); /* Create a non-mailbox type directory for mailbox if it's missing (e.g. index). Optimized for case where the directory usually exists. */ int mailbox_create_missing_dir(struct mailbox *box, enum mailbox_list_path_type type); /* Returns TRUE if mailbox is autocreated. */ bool mailbox_is_autocreated(struct mailbox *box); /* Returns -1 if error, 0 if failed with EEXIST, 1 if ok */ int mailbox_create_fd(struct mailbox *box, const char *path, int flags, int *fd_r); /* Create a lock file to the mailbox with the given filename. If it succeeds, returns 1 and lock_r, which needs to be freed once finished with the lock. If lock_secs is reached, returns 0 and error_r. Returns -1 and sets error_r on other errors. */ int mailbox_lock_file_create(struct mailbox *box, const char *lock_fname, unsigned int lock_secs, struct file_lock **lock_r, const char **error_r); unsigned int mail_storage_get_lock_timeout(struct mail_storage *storage, unsigned int secs); void mail_storage_free_binary_cache(struct mail_storage *storage); enum mail_index_open_flags mail_storage_settings_to_index_flags(const struct mail_storage_settings *set); void mailbox_save_context_deinit(struct mail_save_context *ctx); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-uidvalidity.c0000644000175000017500000001464213123174404017456 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "read-full.h" #include "write-full.h" #include "eacces-error.h" #include "mailbox-list.h" #include "mailbox-uidvalidity.h" #include #include #include #include #include #define RETRY_COUNT 10 static uint32_t mailbox_uidvalidity_next_fallback(void) { static uint32_t uid_validity = 0; /* we failed to use the uidvalidity file. don't fail the mailbox creation because of it though, most of the time it's safe enough to use the current time as the uidvalidity value. */ if (uid_validity < (uint32_t)ioloop_time) uid_validity = (uint32_t)ioloop_time; else uid_validity++; if (uid_validity == 0) uid_validity = 1; return uid_validity; } static void mailbox_uidvalidity_write(struct mailbox_list *list, const char *path, uint32_t uid_validity) { char buf[8+1]; int fd; struct mailbox_permissions perm; mode_t old_mask; mailbox_list_get_root_permissions(list, &perm); old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT, 0666); umask(old_mask); if (fd == -1) { i_error("open(%s) failed: %m", path); return; } if (perm.file_create_gid != (gid_t)-1 && fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { i_error("%s", eperm_error_get_chgrp("fchown", path, perm.file_create_gid, perm.file_create_gid_origin)); } else { i_error("fchown(%s, -1, %ld) failed: %m", path, (long)perm.file_create_gid); } } if (i_snprintf(buf, sizeof(buf), "%08x", uid_validity) < 0) i_unreached(); if (pwrite_full(fd, buf, strlen(buf), 0) < 0) i_error("write(%s) failed: %m", path); if (close(fd) < 0) i_error("close(%s) failed: %m", path); } static int mailbox_uidvalidity_rename(const char *path, uint32_t *uid_validity, bool log_enoent) { string_t *src, *dest; unsigned int i; size_t prefix_len; int ret; src = t_str_new(256); str_append(src, path); dest = t_str_new(256); str_append(dest, path); prefix_len = str_len(src); for (i = 0; i < RETRY_COUNT; i++) { str_truncate(src, prefix_len); str_truncate(dest, prefix_len); str_printfa(src, ".%08x", *uid_validity); *uid_validity += 1; if (*uid_validity == 0) *uid_validity += 1; str_printfa(dest, ".%08x", *uid_validity); if ((ret = rename(str_c(src), str_c(dest))) == 0 || errno != ENOENT) break; /* possibly a race condition. try the next value. */ } if (ret < 0 && (errno != ENOENT || log_enoent)) i_error("rename(%s, %s) failed: %m", str_c(src), str_c(dest)); return ret; } static uint32_t mailbox_uidvalidity_next_rescan(struct mailbox_list *list, const char *path) { DIR *d; struct dirent *dp; const char *fname, *dir, *prefix, *tmp; unsigned int i; size_t prefix_len; uint32_t cur_value, min_value, max_value; mode_t old_mask; int fd; fname = strrchr(path, '/'); if (fname == NULL) { dir = "."; fname = path; } else { dir = t_strdup_until(path, fname); fname++; } d = opendir(dir); if (d == NULL && errno == ENOENT) { /* FIXME: the PATH_TYPE_CONTROL should come as a parameter, but that's an API change, do it in v2.3. it's not really a problem though, since currently all backends use control dirs for the uidvalidity file. */ (void)mailbox_list_mkdir_root(list, dir, MAILBOX_LIST_PATH_TYPE_CONTROL); d = opendir(dir); } if (d == NULL) { i_error("opendir(%s) failed: %m", dir); return mailbox_uidvalidity_next_fallback(); } prefix = t_strconcat(fname, ".", NULL); prefix_len = strlen(prefix); /* just in case there happens to be multiple matching uidvalidity files, track the min/max values. use the max value and delete the min value file. */ max_value = 0; min_value = (uint32_t)-1; while ((dp = readdir(d)) != NULL) { if (strncmp(dp->d_name, prefix, prefix_len) == 0) { if (str_to_uint32_hex(dp->d_name + prefix_len, &cur_value) >= 0) { if (min_value > cur_value) min_value = cur_value; if (max_value < cur_value) max_value = cur_value; } } } if (closedir(d) < 0) i_error("closedir(%s) failed: %m", dir); if (max_value == 0) { /* no uidvalidity files. create one. */ for (i = 0; i < RETRY_COUNT; i++) { cur_value = mailbox_uidvalidity_next_fallback(); tmp = t_strdup_printf("%s.%08x", path, cur_value); /* the file is empty, don't bother with permissions */ old_mask = umask(0); fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0444); umask(old_mask); if (fd != -1 || errno != EEXIST) break; /* already exists. although it's quite unlikely we'll hit this race condition. more likely we'll create a duplicate file.. */ } if (fd == -1) { i_error("creat(%s) failed: %m", tmp); return cur_value; } i_close_fd(&fd); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; } if (min_value != max_value) { /* duplicate uidvalidity files, delete the oldest */ tmp = t_strdup_printf("%s.%08x", path, min_value); i_unlink_if_exists(tmp); } cur_value = max_value; if (mailbox_uidvalidity_rename(path, &cur_value, TRUE) < 0) return mailbox_uidvalidity_next_fallback(); mailbox_uidvalidity_write(list, path, cur_value); return cur_value; } uint32_t mailbox_uidvalidity_next(struct mailbox_list *list, const char *path) { char buf[8+1]; uint32_t cur_value; int fd, ret; fd = open(path, O_RDWR); if (fd == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", path); return mailbox_uidvalidity_next_rescan(list, path); } ret = read_full(fd, buf, sizeof(buf)-1); if (ret < 0) { i_error("read(%s) failed: %m", path); i_close_fd(&fd); return mailbox_uidvalidity_next_rescan(list, path); } buf[sizeof(buf)-1] = 0; if (ret == 0 || str_to_uint32_hex(buf, &cur_value) < 0 || cur_value == 0) { /* broken value */ i_close_fd(&fd); return mailbox_uidvalidity_next_rescan(list, path); } /* we now have the current uidvalidity value that's hopefully correct */ if (mailbox_uidvalidity_rename(path, &cur_value, FALSE) < 0) { i_close_fd(&fd); return mailbox_uidvalidity_next_rescan(list, path); } /* fast path succeeded. write the current value to the main uidvalidity file. */ if (i_snprintf(buf, sizeof(buf), "%08x", cur_value) < 0) i_unreached(); if (pwrite_full(fd, buf, strlen(buf), 0) < 0) i_error("write(%s) failed: %m", path); if (close(fd) < 0) i_error("close(%s) failed: %m", path); return cur_value; } dovecot-2.2.33.2/src/lib-storage/mailbox-guid-cache.h0000644000175000017500000000035313123174404017117 00000000000000#ifndef MAILBOX_GUID_CACHE_H #define MAILBOX_GUID_CACHE_H int mailbox_guid_cache_find(struct mailbox_list *list, const guid_128_t guid, const char **vname_r); void mailbox_guid_cache_refresh(struct mailbox_list *list); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-mime.c0000644000175000017500000003660413165463624016625 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "utc-offset.h" #include "imap-date.h" #include "imap-util.h" #include "imap-quote.h" #include "mail-search.h" #include "mail-search-mime.h" /* * */ static struct mail_search_mime_arg * mail_search_mime_arg_dup_one(pool_t pool, const struct mail_search_mime_arg *arg) { struct mail_search_mime_arg *new_arg; new_arg = p_new(pool, struct mail_search_mime_arg, 1); new_arg->type = arg->type; new_arg->match_not = arg->match_not; new_arg->match_always = arg->match_always; new_arg->nonmatch_always = arg->nonmatch_always; switch (arg->type) { case SEARCH_MIME_OR: case SEARCH_MIME_SUB: new_arg->value.subargs = mail_search_mime_arg_dup(pool, arg->value.subargs); break; case SEARCH_MIME_SIZE_EQUAL: case SEARCH_MIME_SIZE_LARGER: case SEARCH_MIME_SIZE_SMALLER: new_arg->value.size = arg->value.size; break; case SEARCH_MIME_HEADER: new_arg->field_name = p_strdup(pool, arg->field_name); /* fall through */ case SEARCH_MIME_DESCRIPTION: case SEARCH_MIME_DISPOSITION_TYPE: case SEARCH_MIME_DISPOSITION_PARAM: case SEARCH_MIME_ENCODING: case SEARCH_MIME_ID: case SEARCH_MIME_LANGUAGE: case SEARCH_MIME_LOCATION: case SEARCH_MIME_MD5: case SEARCH_MIME_TYPE: case SEARCH_MIME_SUBTYPE: case SEARCH_MIME_PARAM: case SEARCH_MIME_BODY: case SEARCH_MIME_TEXT: case SEARCH_MIME_CC: case SEARCH_MIME_BCC: case SEARCH_MIME_FROM: case SEARCH_MIME_IN_REPLY_TO: case SEARCH_MIME_MESSAGE_ID: case SEARCH_MIME_REPLY_TO: case SEARCH_MIME_SENDER: case SEARCH_MIME_SUBJECT: case SEARCH_MIME_TO: case SEARCH_MIME_FILENAME_IS: case SEARCH_MIME_FILENAME_CONTAINS: case SEARCH_MIME_FILENAME_BEGINS: case SEARCH_MIME_FILENAME_ENDS: new_arg->value.str = p_strdup(pool, arg->value.str); break; case SEARCH_MIME_SENTBEFORE: case SEARCH_MIME_SENTON: case SEARCH_MIME_SENTSINCE: new_arg->value.time = arg->value.time; break; case SEARCH_MIME_PARENT: case SEARCH_MIME_CHILD: if (new_arg->value.subargs != NULL) { new_arg->value.subargs = mail_search_mime_arg_dup(pool, arg->value.subargs); } break; case SEARCH_MIME_DEPTH_EQUAL: case SEARCH_MIME_DEPTH_MIN: case SEARCH_MIME_DEPTH_MAX: case SEARCH_MIME_INDEX: new_arg->value.number = arg->value.number; break; } return new_arg; } struct mail_search_mime_arg * mail_search_mime_arg_dup(pool_t pool, const struct mail_search_mime_arg *arg) { struct mail_search_mime_arg *new_arg = NULL, **dest = &new_arg; for (; arg != NULL; arg = arg->next) { *dest = mail_search_mime_arg_dup_one(pool, arg); dest = &(*dest)->next; } return new_arg; } struct mail_search_mime_part * mail_search_mime_part_dup(pool_t pool, const struct mail_search_mime_part *mpart) { struct mail_search_mime_part *new_mpart; new_mpart = p_new(pool, struct mail_search_mime_part, 1); new_mpart->simplified = mpart->simplified; new_mpart->args = mail_search_mime_arg_dup(pool, mpart->args); return new_mpart; } /* * */ void mail_search_mime_args_reset(struct mail_search_mime_arg *args, bool full_reset) { while (args != NULL) { if (args->type == SEARCH_MIME_OR || args->type == SEARCH_MIME_SUB) mail_search_mime_args_reset(args->value.subargs, full_reset); if (args->match_always) { if (!full_reset) args->result = 1; else { args->match_always = FALSE; args->result = -1; } } else if (args->nonmatch_always) { if (!full_reset) args->result = 0; else { args->nonmatch_always = FALSE; args->result = -1; } } else { args->result = -1; } args = args->next; } } static void search_mime_arg_foreach(struct mail_search_mime_arg *arg, mail_search_mime_foreach_callback_t *callback, void *context) { struct mail_search_mime_arg *subarg; if (arg->result != -1) return; if (arg->type == SEARCH_MIME_SUB) { /* sublist of conditions */ i_assert(arg->value.subargs != NULL); arg->result = 1; subarg = arg->value.subargs; while (subarg != NULL) { if (subarg->result == -1) search_mime_arg_foreach(subarg, callback, context); if (subarg->result == -1) arg->result = -1; else if (subarg->result == 0) { /* didn't match */ arg->result = 0; break; } subarg = subarg->next; } if (arg->match_not && arg->result != -1) arg->result = arg->result > 0 ? 0 : 1; } else if (arg->type == SEARCH_MIME_OR) { /* OR-list of conditions */ i_assert(arg->value.subargs != NULL); subarg = arg->value.subargs; arg->result = 0; while (subarg != NULL) { if (subarg->result == -1) search_mime_arg_foreach(subarg, callback, context); if (subarg->result == -1) arg->result = -1; else if (subarg->result > 0) { /* matched */ arg->result = 1; break; } subarg = subarg->next; } if (arg->match_not && arg->result != -1) arg->result = arg->result > 0 ? 0 : 1; } else { /* just a single condition */ callback(arg, context); } } #undef mail_search_mime_args_foreach int mail_search_mime_args_foreach(struct mail_search_mime_arg *args, mail_search_mime_foreach_callback_t *callback, void *context) { int result; result = 1; for (; args != NULL; args = args->next) { search_mime_arg_foreach(args, callback, context); if (args->result == 0) { /* didn't match */ return 0; } if (args->result == -1) result = -1; } return result; } /* * */ bool mail_search_mime_arg_one_equals(const struct mail_search_mime_arg *arg1, const struct mail_search_mime_arg *arg2) { if (arg1->type != arg2->type || arg1->match_not != arg2->match_not) return FALSE; switch (arg1->type) { case SEARCH_MIME_OR: case SEARCH_MIME_SUB: return mail_search_mime_arg_equals(arg1->value.subargs, arg2->value.subargs); case SEARCH_MIME_SIZE_EQUAL: case SEARCH_MIME_SIZE_LARGER: case SEARCH_MIME_SIZE_SMALLER: return arg1->value.size == arg2->value.size; case SEARCH_MIME_HEADER: case SEARCH_MIME_DISPOSITION_PARAM: case SEARCH_MIME_PARAM: if (strcasecmp(arg1->field_name, arg2->field_name) != 0) return FALSE; /* fall through */ case SEARCH_MIME_DESCRIPTION: case SEARCH_MIME_DISPOSITION_TYPE: case SEARCH_MIME_ENCODING: case SEARCH_MIME_ID: case SEARCH_MIME_LANGUAGE: case SEARCH_MIME_LOCATION: case SEARCH_MIME_MD5: case SEARCH_MIME_TYPE: case SEARCH_MIME_SUBTYPE: case SEARCH_MIME_BODY: case SEARCH_MIME_TEXT: case SEARCH_MIME_CC: case SEARCH_MIME_BCC: case SEARCH_MIME_FROM: case SEARCH_MIME_IN_REPLY_TO: case SEARCH_MIME_MESSAGE_ID: case SEARCH_MIME_REPLY_TO: case SEARCH_MIME_SENDER: case SEARCH_MIME_SUBJECT: case SEARCH_MIME_TO: case SEARCH_MIME_FILENAME_IS: case SEARCH_MIME_FILENAME_CONTAINS: case SEARCH_MIME_FILENAME_BEGINS: case SEARCH_MIME_FILENAME_ENDS: /* don't bother doing case-insensitive comparison. we should support full i18n case-insensitivity (or the active comparator in future). */ return strcmp(arg1->value.str, arg2->value.str) == 0; case SEARCH_MIME_SENTBEFORE: case SEARCH_MIME_SENTON: case SEARCH_MIME_SENTSINCE: return arg1->value.time == arg2->value.time; case SEARCH_MIME_PARENT: case SEARCH_MIME_CHILD: if (arg1->value.subargs == NULL) return arg2->value.subargs == NULL; if (arg2->value.subargs == NULL) return FALSE; return mail_search_mime_arg_equals(arg1->value.subargs, arg2->value.subargs); case SEARCH_MIME_DEPTH_EQUAL: case SEARCH_MIME_DEPTH_MIN: case SEARCH_MIME_DEPTH_MAX: case SEARCH_MIME_INDEX: return arg1->value.number == arg2->value.number; break; } i_unreached(); return FALSE; } bool mail_search_mime_arg_equals(const struct mail_search_mime_arg *arg1, const struct mail_search_mime_arg *arg2) { while (arg1 != NULL && arg2 != NULL) { if (!mail_search_mime_arg_one_equals(arg1, arg2)) return FALSE; arg1 = arg1->next; arg2 = arg2->next; } return arg1 == NULL && arg2 == NULL; } bool mail_search_mime_parts_equal(const struct mail_search_mime_part *mpart1, const struct mail_search_mime_part *mpart2) { i_assert(mpart1->simplified == mpart2->simplified); return mail_search_mime_arg_equals(mpart1->args, mpart2->args); } /* * */ void mail_search_mime_simplify(struct mail_search_mime_part *mpart) { mpart->simplified = TRUE; // FIXME: implement and use } /* * */ static bool mail_search_mime_subargs_to_imap(string_t *dest, const struct mail_search_mime_arg *args, const char *prefix, const char **error_r) { const struct mail_search_mime_arg *arg; if (prefix[0] == '\0') str_append_c(dest, '('); for (arg = args; arg != NULL; arg = arg->next) { if (arg->next != NULL) str_append(dest, prefix); if (!mail_search_mime_arg_to_imap(dest, arg, error_r)) return FALSE; if (arg->next != NULL) str_append_c(dest, ' '); } if (prefix[0] == '\0') str_append_c(dest, ')'); return TRUE; } static bool mail_search_mime_arg_to_imap_date(string_t *dest, const struct mail_search_mime_arg *arg) { time_t timestamp = arg->value.time; const char *str; struct tm *tm; int tz_offset; tm = localtime(×tamp); tz_offset = utc_offset(tm, timestamp); timestamp -= tz_offset * 60; if (!imap_to_date(timestamp, &str)) return FALSE; str_printfa(dest, " \"%s\"", str); return TRUE; } bool mail_search_mime_arg_to_imap(string_t *dest, const struct mail_search_mime_arg *arg, const char **error_r) { if (arg->match_not) str_append(dest, "NOT "); switch (arg->type) { case SEARCH_MIME_OR: if (!mail_search_mime_subargs_to_imap (dest, arg->value.subargs, "OR ", error_r)) return FALSE; break; case SEARCH_MIME_SUB: if (!mail_search_mime_subargs_to_imap (dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_MIME_SIZE_EQUAL: str_printfa(dest, "SIZE %llu", (unsigned long long)arg->value.size); break; case SEARCH_MIME_SIZE_LARGER: str_printfa(dest, "SIZE LARGER %llu", (unsigned long long)arg->value.size); break; case SEARCH_MIME_SIZE_SMALLER: str_printfa(dest, "SIZE SMALLER %llu", (unsigned long long)arg->value.size); break; case SEARCH_MIME_DESCRIPTION: str_append(dest, "DESCRIPTION "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_DISPOSITION_TYPE: str_append(dest, "DISPOSITION TYPE "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_DISPOSITION_PARAM: str_append(dest, "DISPOSITION PARAM "); imap_append_astring(dest, arg->field_name); str_append_c(dest, ' '); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_ENCODING: str_append(dest, "ENCODING "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_ID: str_append(dest, "ID "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_LANGUAGE: str_append(dest, "LANGUAGE "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_LOCATION: str_append(dest, "LOCATION "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_MD5: str_append(dest, "MD5 "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_TYPE: str_append(dest, "TYPE "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_SUBTYPE: str_append(dest, "SUBTYPE "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_PARAM: str_append(dest, "PARAM "); imap_append_astring(dest, arg->field_name); str_append_c(dest, ' '); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_HEADER: str_append(dest, "HEADER "); imap_append_astring(dest, arg->field_name); str_append_c(dest, ' '); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_BODY: str_append(dest, "BODY "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_TEXT: str_append(dest, "TEXT "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_CC: str_append(dest, "CC "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_BCC: str_append(dest, "BCC "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_FROM: str_append(dest, "FROM "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_IN_REPLY_TO: str_append(dest, "IN-REPLY-TO "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_MESSAGE_ID: str_append(dest, "MESSAGE-ID "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_REPLY_TO: str_append(dest, "REPLY-TO "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_SENDER: str_append(dest, "SENDER "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_SENTBEFORE: str_append(dest, "SENTBEFORE"); if (!mail_search_mime_arg_to_imap_date(dest, arg)) { *error_r = t_strdup_printf( "SENTBEFORE can't be written as IMAP MIMEPART key " "for timestamp %ld", (long)arg->value.time); return FALSE; } break; case SEARCH_MIME_SENTON: str_append(dest, "SENTON"); if (!mail_search_mime_arg_to_imap_date(dest, arg)) { *error_r = t_strdup_printf( "SENTON can't be written as IMAP MIMEPART key " "for timestamp %ld", (long)arg->value.time); return FALSE; } break; case SEARCH_MIME_SENTSINCE: str_append(dest, "SENTSINCE"); if (!mail_search_mime_arg_to_imap_date(dest, arg)) { *error_r = t_strdup_printf( "SENTSINCE can't be written as IMAP MIMEPART key " "for timestamp %ld", (long)arg->value.time); return FALSE; } break; case SEARCH_MIME_SUBJECT: str_append(dest, "SUBJECT "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_TO: str_append(dest, "TO "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_DEPTH_EQUAL: str_printfa(dest, "DEPTH %u", arg->value.number); break; case SEARCH_MIME_DEPTH_MIN: str_printfa(dest, "DEPTH MIN %u", arg->value.number); break; case SEARCH_MIME_DEPTH_MAX: str_printfa(dest, "DEPTH MAX %u", arg->value.number); break; case SEARCH_MIME_INDEX: str_printfa(dest, "INDEX %u", arg->value.number); break; case SEARCH_MIME_PARENT: str_append(dest, "PARENT "); if (arg->value.subargs == NULL) str_append(dest, "EXISTS"); else if (!mail_search_mime_subargs_to_imap (dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_MIME_CHILD: str_append(dest, "CHILD "); if (arg->value.subargs == NULL) str_append(dest, "EXISTS"); else if (!mail_search_mime_subargs_to_imap (dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_MIME_FILENAME_IS: str_append(dest, "FILENAME IS "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_FILENAME_CONTAINS: str_append(dest, "FILENAME CONTAINS "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_FILENAME_BEGINS: str_append(dest, "FILENAME BEGINS "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MIME_FILENAME_ENDS: str_append(dest, "FILENAME ENDS "); imap_append_astring(dest, arg->value.str); break; } return TRUE; } bool mail_search_mime_part_to_imap(string_t *dest, const struct mail_search_mime_part *mpart, const char **error_r) { const struct mail_search_mime_arg *arg; i_assert(mpart->args != NULL); if (mpart->args->next == NULL) { if (!mail_search_mime_arg_to_imap(dest, mpart->args, error_r)) return FALSE; } else { str_append_c(dest, '('); for (arg = mpart->args; arg != NULL; arg = arg->next) { if (!mail_search_mime_arg_to_imap(dest, arg, error_r)) return FALSE; if (arg->next != NULL) str_append_c(dest, ' '); } str_append_c(dest, ')'); } return TRUE; } dovecot-2.2.33.2/src/lib-storage/mail-search-build.h0000644000175000017500000000360513123174404016762 00000000000000#ifndef MAIL_SEARCH_BUILD_H #define MAIL_SEARCH_BUILD_H #include "mail-search.h" #include "mail-search-register.h" struct mailbox; struct mail_search_build_context { pool_t pool; struct mail_search_args *args; struct mail_search_register *reg; struct mail_search_parser *parser; const char *charset; struct mail_search_arg *parent; /* error is either here or in parser */ const char *_error; bool charset_checked; bool unknown_charset; }; /* Start building a new search query. Use mail_search_args_unref() to free it. */ struct mail_search_args *mail_search_build_init(void); /* Convert IMAP SEARCH command compatible parameters to mail_search_args. If charset is unknown, it's changed to NULL. */ int mail_search_build(struct mail_search_register *reg, struct mail_search_parser *parser, const char **charset, struct mail_search_args **args_r, const char **error_r); /* Add new search arg with given type. */ struct mail_search_arg * mail_search_build_add(struct mail_search_args *args, enum mail_search_arg_type type); /* Add SEARCH_ALL to search args. */ void mail_search_build_add_all(struct mail_search_args *args); /* Add a sequence set to search args. */ void mail_search_build_add_seqset(struct mail_search_args *args, uint32_t seq1, uint32_t seq2); /* Convert input string into UTF-8 */ int mail_search_build_get_utf8(struct mail_search_build_context *ctx, const char *input, const char **output_r); struct mail_search_arg * mail_search_build_new(struct mail_search_build_context *ctx, enum mail_search_arg_type type); struct mail_search_arg * mail_search_build_str(struct mail_search_build_context *ctx, enum mail_search_arg_type type); /* Returns 0 if arg is returned, -1 if error. */ int mail_search_build_key(struct mail_search_build_context *ctx, struct mail_search_arg *parent, struct mail_search_arg **arg_r); #endif dovecot-2.2.33.2/src/lib-storage/index/0002755000175000017500000000000013172375611014523 500000000000000dovecot-2.2.33.2/src/lib-storage/index/dbox-single/0002755000175000017500000000000013172375611016736 500000000000000dovecot-2.2.33.2/src/lib-storage/index/dbox-single/Makefile.in0000644000175000017500000005617013172375573020741 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/dbox-single ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_dbox_single_la_LIBADD = am_libstorage_dbox_single_la_OBJECTS = sdbox-copy.lo sdbox-file.lo \ sdbox-mail.lo sdbox-save.lo sdbox-sync.lo \ sdbox-sync-rebuild.lo sdbox-storage.lo libstorage_dbox_single_la_OBJECTS = \ $(am_libstorage_dbox_single_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_dbox_single_la_SOURCES) DIST_SOURCES = $(libstorage_dbox_single_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_dbox_single.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common libstorage_dbox_single_la_SOURCES = \ sdbox-copy.c \ sdbox-file.c \ sdbox-mail.c \ sdbox-save.c \ sdbox-sync.c \ sdbox-sync-rebuild.c \ sdbox-storage.c headers = \ sdbox-file.h \ sdbox-storage.h \ sdbox-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/dbox-single/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/dbox-single/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_dbox_single.la: $(libstorage_dbox_single_la_OBJECTS) $(libstorage_dbox_single_la_DEPENDENCIES) $(EXTRA_libstorage_dbox_single_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_dbox_single_la_OBJECTS) $(libstorage_dbox_single_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-copy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-sync-rebuild.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdbox-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-save.c0000644000175000017500000002422613165463624021105 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fdatasync-path.h" #include "hex-binary.h" #include "hex-dec.h" #include "str.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "write-full.h" #include "index-mail.h" #include "mail-copy.h" #include "index-pop3-uidl.h" #include "dbox-attachment.h" #include "dbox-save.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include "sdbox-sync.h" struct sdbox_save_context { struct dbox_save_context ctx; struct sdbox_mailbox *mbox; struct sdbox_sync_context *sync_ctx; struct dbox_file *cur_file; struct dbox_file_append_context *append_ctx; uint32_t first_saved_seq; ARRAY(struct dbox_file *) files; }; struct dbox_file * sdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)t->save_ctx; struct dbox_file *const *files, *file; unsigned int count; i_assert(seq >= ctx->first_saved_seq); files = array_get(&ctx->files, &count); i_assert(count > 0); i_assert(seq - ctx->first_saved_seq < count); file = files[seq - ctx->first_saved_seq]; i_assert(((struct sdbox_file *)file)->written_to_disk); return file; } struct mail_save_context * sdbox_save_alloc(struct mailbox_transaction_context *t) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)t->box; struct sdbox_save_context *ctx = (struct sdbox_save_context *)t->save_ctx; i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (ctx != NULL) { /* use the existing allocated structure */ ctx->cur_file = NULL; ctx->ctx.failed = FALSE; ctx->ctx.finished = FALSE; ctx->ctx.dbox_output = NULL; return &ctx->ctx.ctx; } ctx = i_new(struct sdbox_save_context, 1); ctx->ctx.ctx.transaction = t; ctx->ctx.trans = t->itrans; ctx->mbox = mbox; i_array_init(&ctx->files, 32); t->save_ctx = &ctx->ctx.ctx; return t->save_ctx; } void sdbox_save_add_file(struct mail_save_context *_ctx, struct dbox_file *file) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; struct dbox_file *const *files; unsigned int count; if (ctx->first_saved_seq == 0) ctx->first_saved_seq = ctx->ctx.seq; files = array_get(&ctx->files, &count); if (count > 0) { /* a plugin may leave a previously saved file open. we'll close it here to avoid eating too many fds. */ dbox_file_close(files[count-1]); } array_append(&ctx->files, &file, 1); } int sdbox_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; struct dbox_file *file; int ret; file = sdbox_file_create(ctx->mbox); ctx->append_ctx = dbox_file_append_init(file); ret = dbox_file_get_append_stream(ctx->append_ctx, &ctx->ctx.dbox_output); if (ret <= 0) { i_assert(ret != 0); dbox_file_append_rollback(&ctx->append_ctx); dbox_file_unref(&file); ctx->ctx.failed = TRUE; return -1; } ctx->cur_file = file; dbox_save_begin(&ctx->ctx, input); sdbox_save_add_file(_ctx, file); return ctx->ctx.failed ? -1 : 0; } static int dbox_save_mail_write_metadata(struct dbox_save_context *ctx, struct dbox_file *file) { struct sdbox_file *sfile = (struct sdbox_file *)file; const ARRAY_TYPE(mail_attachment_extref) *extrefs_arr; const struct mail_attachment_extref *extrefs; struct dbox_message_header dbox_msg_hdr; uoff_t message_size; guid_128_t guid_128; unsigned int i, count; i_assert(file->msg_header_size == sizeof(dbox_msg_hdr)); message_size = ctx->dbox_output->offset - file->msg_header_size - file->file_header_size; dbox_save_write_metadata(&ctx->ctx, ctx->dbox_output, message_size, NULL, guid_128); dbox_msg_header_fill(&dbox_msg_hdr, message_size); if (o_stream_pwrite(ctx->dbox_output, &dbox_msg_hdr, sizeof(dbox_msg_hdr), file->file_header_size) < 0) { dbox_file_set_syscall_error(file, "pwrite()"); return -1; } sfile->written_to_disk = TRUE; /* remember the attachment paths until commit time */ extrefs_arr = index_attachment_save_get_extrefs(&ctx->ctx); if (extrefs_arr != NULL) extrefs = array_get(extrefs_arr, &count); else { extrefs = NULL; count = 0; } if (count > 0) { sfile->attachment_pool = pool_alloconly_create("sdbox attachment paths", 512); p_array_init(&sfile->attachment_paths, sfile->attachment_pool, count); for (i = 0; i < count; i++) { const char *path = p_strdup(sfile->attachment_pool, extrefs[i].path); array_append(&sfile->attachment_paths, &path, 1); } } return 0; } static int dbox_save_finish_write(struct mail_save_context *_ctx) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; struct dbox_file **files; ctx->ctx.finished = TRUE; if (ctx->ctx.dbox_output == NULL) return -1; if (_ctx->data.save_date != (time_t)-1) { /* we can't change ctime, but we can add the date to cache */ struct index_mail *mail = (struct index_mail *)_ctx->dest_mail; uint32_t t = _ctx->data.save_date; index_mail_cache_add(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t)); } dbox_save_end(&ctx->ctx); files = array_idx_modifiable(&ctx->files, array_count(&ctx->files) - 1); if (!ctx->ctx.failed) T_BEGIN { if (dbox_save_mail_write_metadata(&ctx->ctx, *files) < 0) ctx->ctx.failed = TRUE; } T_END; if (ctx->ctx.failed) { index_storage_save_abort_last(&ctx->ctx.ctx, ctx->ctx.seq); dbox_file_append_rollback(&ctx->append_ctx); dbox_file_unlink(*files); dbox_file_unref(files); array_delete(&ctx->files, array_count(&ctx->files) - 1, 1); } else { dbox_file_append_checkpoint(ctx->append_ctx); if (dbox_file_append_commit(&ctx->append_ctx) < 0) ctx->ctx.failed = TRUE; dbox_file_close(*files); } i_stream_unref(&ctx->ctx.input); ctx->ctx.dbox_output = NULL; return ctx->ctx.failed ? -1 : 0; } int sdbox_save_finish(struct mail_save_context *ctx) { int ret; ret = dbox_save_finish_write(ctx); index_save_context_free(ctx); return ret; } void sdbox_save_cancel(struct mail_save_context *_ctx) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; ctx->failed = TRUE; (void)sdbox_save_finish(_ctx); } static int dbox_save_assign_uids(struct sdbox_save_context *ctx, const ARRAY_TYPE(seq_range) *uids) { struct dbox_file *const *files; struct seq_range_iter iter; unsigned int i, count, n = 0; uint32_t uid; bool ret; seq_range_array_iter_init(&iter, uids); files = array_get(&ctx->files, &count); for (i = 0; i < count; i++) { struct sdbox_file *sfile = (struct sdbox_file *)files[i]; ret = seq_range_array_iter_nth(&iter, n++, &uid); i_assert(ret); if (sdbox_file_assign_uid(sfile, uid, FALSE) < 0) return -1; if (ctx->ctx.highest_pop3_uidl_seq == i+1) { index_pop3_uidl_set_max_uid(&ctx->mbox->box, ctx->ctx.trans, uid); } } i_assert(!seq_range_array_iter_nth(&iter, n, &uid)); return 0; } static int dbox_save_assign_stub_uids(struct sdbox_save_context *ctx) { struct dbox_file *const *files; unsigned int i, count; files = array_get(&ctx->files, &count); for (i = 0; i < count; i++) { struct sdbox_file *sfile = (struct sdbox_file *)files[i]; uint32_t uid; mail_index_lookup_uid(ctx->ctx.trans->view, ctx->first_saved_seq + i, &uid); i_assert(uid != 0); if (sdbox_file_assign_uid(sfile, uid, TRUE) < 0) return -1; } return 0; } static void dbox_save_unref_files(struct sdbox_save_context *ctx) { struct dbox_file **files; unsigned int i, count; files = array_get_modifiable(&ctx->files, &count); for (i = 0; i < count; i++) { if (ctx->ctx.failed) { struct sdbox_file *sfile = (struct sdbox_file *)files[i]; (void)sdbox_file_unlink_aborted_save(sfile); } dbox_file_unref(&files[i]); } array_free(&ctx->files); } int sdbox_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; const struct mail_index_header *hdr; i_assert(ctx->ctx.finished); if (array_count(&ctx->files) == 0) { /* the mail must be freed in the commit_pre() */ return 0; } if (sdbox_sync_begin(ctx->mbox, SDBOX_SYNC_FLAG_FORCE | SDBOX_SYNC_FLAG_FSYNC, &ctx->sync_ctx) < 0) { sdbox_transaction_save_rollback(_ctx); return -1; } /* update dbox header flags */ dbox_save_update_header_flags(&ctx->ctx, ctx->sync_ctx->sync_view, ctx->mbox->hdr_ext_id, offsetof(struct sdbox_index_header, flags)); hdr = mail_index_get_header(ctx->sync_ctx->sync_view); if ((_ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0) { /* assign UIDs for new messages */ mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid, &_t->changes->saved_uids); if (dbox_save_assign_uids(ctx, &_t->changes->saved_uids) < 0) { sdbox_transaction_save_rollback(_ctx); return -1; } } else { /* assign UIDs that we stashed away */ if (dbox_save_assign_stub_uids(ctx) < 0) { sdbox_transaction_save_rollback(_ctx); return -1; } } _t->changes->uid_validity = hdr->uid_validity; return 0; } void sdbox_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; _ctx->transaction = NULL; /* transaction is already freed */ if (array_count(&ctx->files) == 0) { sdbox_transaction_save_rollback(_ctx); return; } mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx, result); if (sdbox_sync_finish(&ctx->sync_ctx, TRUE) < 0) ctx->ctx.failed = TRUE; if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { const char *box_path = mailbox_get_path(&ctx->mbox->box); if (fdatasync_path(box_path) < 0) { mail_storage_set_critical(storage, "fdatasync_path(%s) failed: %m", box_path); } } sdbox_transaction_save_rollback(_ctx); } void sdbox_transaction_save_rollback(struct mail_save_context *_ctx) { struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx; if (!ctx->ctx.finished) sdbox_save_cancel(_ctx); dbox_save_unref_files(ctx); if (ctx->sync_ctx != NULL) (void)sdbox_sync_finish(&ctx->sync_ctx, FALSE); i_free(ctx); } dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-sync.c0000644000175000017500000002161413165463624021121 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dbox-attachment.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include "sdbox-sync.h" #define SDBOX_REBUILD_COUNT 3 static void dbox_sync_file_move_if_needed(struct dbox_file *file, enum sdbox_sync_entry_type type) { struct stat st; bool move_to_alt = type == SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT; bool deleted; if (move_to_alt == dbox_file_is_in_alt(file) && !move_to_alt) { /* unopened dbox files default to primary dir. stat the file to update its location. */ (void)dbox_file_stat(file, &st); } if (move_to_alt != dbox_file_is_in_alt(file)) { /* move the file. if it fails, nothing broke so don't worry about it. */ if (dbox_file_open(file, &deleted) > 0 && !deleted) (void)sdbox_file_move(file, move_to_alt); } } static void sdbox_sync_file(struct sdbox_sync_context *ctx, uint32_t seq, uint32_t uid, enum sdbox_sync_entry_type type) { struct dbox_file *file; enum modify_type modify_type; switch (type) { case SDBOX_SYNC_ENTRY_TYPE_EXPUNGE: if (!mail_index_transaction_is_expunged(ctx->trans, seq)) { mail_index_expunge(ctx->trans, seq); array_append(&ctx->expunged_uids, &uid, 1); } break; case SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT: case SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT: /* update flags in the sync transaction, mainly to make sure that these alt changes get marked as synced and won't be retried */ modify_type = type == SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT ? MODIFY_ADD : MODIFY_REMOVE; mail_index_update_flags(ctx->trans, seq, modify_type, (enum mail_flags)DBOX_INDEX_FLAG_ALT); file = sdbox_file_init(ctx->mbox, uid); dbox_sync_file_move_if_needed(file, type); dbox_file_unref(&file); break; } } static void sdbox_sync_add(struct sdbox_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { uint32_t uid; enum sdbox_sync_entry_type type; uint32_t seq, seq1, seq2; if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { /* we're interested */ type = SDBOX_SYNC_ENTRY_TYPE_EXPUNGE; } else if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS) { /* we care only about alt flag changes */ if ((sync_rec->add_flags & DBOX_INDEX_FLAG_ALT) != 0) type = SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT; else if ((sync_rec->remove_flags & DBOX_INDEX_FLAG_ALT) != 0) type = SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT; else return; } else { /* not interested */ return; } if (!mail_index_lookup_seq_range(ctx->sync_view, sync_rec->uid1, sync_rec->uid2, &seq1, &seq2)) { /* already expunged everything. nothing to do. */ return; } for (seq = seq1; seq <= seq2; seq++) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); sdbox_sync_file(ctx, seq, uid, type); } } static int sdbox_sync_index(struct sdbox_sync_context *ctx) { struct mailbox *box = &ctx->mbox->box; const struct mail_index_header *hdr; struct mail_index_sync_rec sync_rec; uint32_t seq1, seq2; hdr = mail_index_get_header(ctx->sync_view); if (hdr->uid_validity == 0) { /* newly created index file */ if (hdr->next_uid == 1) { /* could be just a race condition where we opened the mailbox between mkdir and index creation. fix this silently. */ if (sdbox_mailbox_create_indexes(box, NULL, ctx->trans) < 0) return -1; return 1; } mail_storage_set_critical(box->storage, "sdbox %s: Broken index: missing UIDVALIDITY", mailbox_get_path(box)); sdbox_set_mailbox_corrupted(box); return 0; } /* mark the newly seen messages as recent */ if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid, hdr->next_uid, &seq1, &seq2)) mailbox_recent_flags_set_seqs(box, ctx->sync_view, seq1, seq2); while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) sdbox_sync_add(ctx, &sync_rec); return 1; } static void dbox_sync_file_expunge(struct sdbox_sync_context *ctx, uint32_t uid) { struct mailbox *box = &ctx->mbox->box; struct dbox_file *file; struct sdbox_file *sfile; int ret; file = sdbox_file_init(ctx->mbox, uid); sfile = (struct sdbox_file *)file; if (file->storage->attachment_dir != NULL) ret = sdbox_file_unlink_with_attachments(sfile); else ret = dbox_file_unlink(file); /* do sync_notify only when the file was unlinked by us */ if (ret > 0 && box->v.sync_notify != NULL) box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); dbox_file_unref(&file); } static void dbox_sync_expunge_files(struct sdbox_sync_context *ctx) { const uint32_t *uidp; /* NOTE: Index is no longer locked. Multiple processes may be unlinking the files at the same time. */ ctx->mbox->box.tmp_sync_view = ctx->sync_view; array_foreach(&ctx->expunged_uids, uidp) T_BEGIN { dbox_sync_file_expunge(ctx, *uidp); } T_END; if (ctx->mbox->box.v.sync_notify != NULL) ctx->mbox->box.v.sync_notify(&ctx->mbox->box, 0, 0); ctx->mbox->box.tmp_sync_view = NULL; } static int sdbox_refresh_header(struct sdbox_mailbox *mbox, bool retry, bool log_error) { struct mail_index_view *view; struct sdbox_index_header hdr; bool need_resize; int ret; view = mail_index_view_open(mbox->box.index); ret = sdbox_read_header(mbox, &hdr, log_error, &need_resize); mail_index_view_close(&view); if (ret < 0 && retry) { mail_index_refresh(mbox->box.index); return sdbox_refresh_header(mbox, FALSE, log_error); } return ret; } int sdbox_sync_begin(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags, struct sdbox_sync_context **ctx_r) { struct mail_storage *storage = mbox->box.storage; const struct mail_index_header *hdr = mail_index_get_header(mbox->box.view); struct sdbox_sync_context *ctx; enum mail_index_sync_flags sync_flags; unsigned int i; int ret; bool rebuild, force_rebuild; force_rebuild = (flags & SDBOX_SYNC_FLAG_FORCE_REBUILD) != 0; rebuild = force_rebuild || (hdr->flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0 || mbox->corrupted_rebuild_count != 0 || sdbox_refresh_header(mbox, TRUE, FALSE) < 0; ctx = i_new(struct sdbox_sync_context, 1); ctx->mbox = mbox; ctx->flags = flags; i_array_init(&ctx->expunged_uids, 32); sync_flags = index_storage_get_sync_flags(&mbox->box); if (!rebuild && (flags & SDBOX_SYNC_FLAG_FORCE) == 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; if ((flags & SDBOX_SYNC_FLAG_FSYNC) != 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_FSYNC; /* don't write unnecessary dirty flag updates */ sync_flags |= MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES; for (i = 0;; i++) { ret = index_storage_expunged_sync_begin(&mbox->box, &ctx->index_sync_ctx, &ctx->sync_view, &ctx->trans, sync_flags); if (mail_index_reset_fscked(mbox->box.index)) sdbox_set_mailbox_corrupted(&mbox->box); if (ret <= 0) { array_free(&ctx->expunged_uids); i_free(ctx); *ctx_r = NULL; return ret; } if (rebuild) ret = 0; else { if ((ret = sdbox_sync_index(ctx)) > 0) break; } /* failure. keep the index locked while we're doing a rebuild. */ if (ret == 0) { if (i >= SDBOX_REBUILD_COUNT) { mail_storage_set_critical(storage, "sdbox %s: Index keeps breaking", mailbox_get_path(&ctx->mbox->box)); ret = -1; } else { /* do a full resync and try again. */ rebuild = FALSE; ret = sdbox_sync_index_rebuild(mbox, force_rebuild); } } mail_index_sync_rollback(&ctx->index_sync_ctx); if (ret < 0) { index_storage_expunging_deinit(&ctx->mbox->box); array_free(&ctx->expunged_uids); i_free(ctx); return -1; } } *ctx_r = ctx; return 0; } int sdbox_sync_finish(struct sdbox_sync_context **_ctx, bool success) { struct sdbox_sync_context *ctx = *_ctx; int ret = success ? 0 : -1; *_ctx = NULL; if (success) { mail_index_view_ref(ctx->sync_view); if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { mailbox_set_index_error(&ctx->mbox->box); ret = -1; } else { dbox_sync_expunge_files(ctx); mail_index_view_close(&ctx->sync_view); } } else { mail_index_sync_rollback(&ctx->index_sync_ctx); } index_storage_expunging_deinit(&ctx->mbox->box); array_free(&ctx->expunged_uids); i_free(ctx); return ret; } int sdbox_sync(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags) { struct sdbox_sync_context *sync_ctx; if (sdbox_sync_begin(mbox, flags, &sync_ctx) < 0) return -1; if (sync_ctx == NULL) return 0; return sdbox_sync_finish(&sync_ctx, TRUE); } struct mailbox_sync_context * sdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; enum sdbox_sync_flags sdbox_sync_flags = 0; int ret = 0; if (mail_index_reset_fscked(box->index)) sdbox_set_mailbox_corrupted(box); if (index_mailbox_want_full_sync(&mbox->box, flags) || mbox->corrupted_rebuild_count != 0) { if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0) sdbox_sync_flags |= SDBOX_SYNC_FLAG_FORCE_REBUILD; ret = sdbox_sync(mbox, sdbox_sync_flags); } return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-file.c0000644000175000017500000003055213165463624021065 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "eacces-error.h" #include "fdatasync-path.h" #include "mkdir-parents.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "fs-api.h" #include "dbox-attachment.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include #include static void sdbox_file_init_paths(struct sdbox_file *file, const char *fname) { struct mailbox *box = &file->mbox->box; const char *alt_path; i_free(file->file.primary_path); i_free(file->file.alt_path); file->file.primary_path = i_strdup_printf("%s/%s", mailbox_get_path(box), fname); file->file.cur_path = file->file.primary_path; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path) > 0) file->file.alt_path = i_strdup_printf("%s/%s", alt_path, fname); } struct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid) { struct sdbox_file *file; const char *fname; file = i_new(struct sdbox_file, 1); file->file.storage = &mbox->storage->storage; file->mbox = mbox; T_BEGIN { if (uid != 0) { fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid); sdbox_file_init_paths(file, fname); file->uid = uid; } else { sdbox_file_init_paths(file, dbox_generate_tmp_filename()); } } T_END; dbox_file_init(&file->file); return &file->file; } struct dbox_file *sdbox_file_create(struct sdbox_mailbox *mbox) { struct dbox_file *file; file = sdbox_file_init(mbox, 0); file->fd = file->storage->v. file_create_fd(file, file->primary_path, FALSE); return file; } void sdbox_file_free(struct dbox_file *file) { struct sdbox_file *sfile = (struct sdbox_file *)file; if (sfile->attachment_pool != NULL) pool_unref(&sfile->attachment_pool); dbox_file_free(file); } int sdbox_file_get_attachments(struct dbox_file *file, const char **extrefs_r) { const char *line; bool deleted; int ret; *extrefs_r = NULL; /* read the metadata */ ret = dbox_file_open(file, &deleted); if (ret > 0) { if (deleted) return 0; if ((ret = dbox_file_seek(file, 0)) > 0) ret = dbox_file_metadata_read(file); } if (ret <= 0) { if (ret < 0) return -1; /* corrupted file. we're deleting it anyway. */ line = NULL; } else { line = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF); } if (line == NULL) { /* no attachments */ return 0; } *extrefs_r = line; return 1; } const char * sdbox_file_attachment_relpath(struct sdbox_file *file, const char *srcpath) { const char *p; p = strchr(srcpath, '-'); if (p == NULL) { mail_storage_set_critical(file->mbox->box.storage, "sdbox attachment path in invalid format: %s", srcpath); } else { p = strchr(p+1, '-'); } return t_strdup_printf("%s-%s-%u", p == NULL ? srcpath : t_strdup_until(srcpath, p), guid_128_to_string(file->mbox->mailbox_guid), file->uid); } static int sdbox_file_rename_attachments(struct sdbox_file *file) { struct dbox_storage *storage = file->file.storage; struct fs_file *src_file, *dest_file; const char *const *pathp, *src, *dest; int ret = 0; array_foreach(&file->attachment_paths, pathp) T_BEGIN { src = t_strdup_printf("%s/%s", storage->attachment_dir, *pathp); dest = t_strdup_printf("%s/%s", storage->attachment_dir, sdbox_file_attachment_relpath(file, *pathp)); src_file = fs_file_init(storage->attachment_fs, src, FS_OPEN_MODE_READONLY); dest_file = fs_file_init(storage->attachment_fs, dest, FS_OPEN_MODE_READONLY); if (fs_rename(src_file, dest_file) < 0) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(storage->attachment_fs)); ret = -1; } fs_file_deinit(&src_file); fs_file_deinit(&dest_file); } T_END; return ret; } int sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid, bool ignore_if_exists) { const char *p, *old_path, *dir, *new_fname, *new_path; struct stat st; i_assert(file->uid == 0); i_assert(uid != 0); old_path = file->file.cur_path; p = strrchr(old_path, '/'); i_assert(p != NULL); dir = t_strdup_until(old_path, p); new_fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid); new_path = t_strdup_printf("%s/%s", dir, new_fname); if (!ignore_if_exists && stat(new_path, &st) == 0) { mail_storage_set_critical(&file->file.storage->storage, "sdbox: %s already exists, rebuilding index", new_path); sdbox_set_mailbox_corrupted(&file->mbox->box); return -1; } if (rename(old_path, new_path) < 0) { mail_storage_set_critical(&file->file.storage->storage, "rename(%s, %s) failed: %m", old_path, new_path); return -1; } sdbox_file_init_paths(file, new_fname); file->uid = uid; if (array_is_created(&file->attachment_paths)) { if (sdbox_file_rename_attachments(file) < 0) return -1; } return 0; } static int sdbox_file_unlink_aborted_save_attachments(struct sdbox_file *file) { struct dbox_storage *storage = file->file.storage; struct fs *fs = storage->attachment_fs; struct fs_file *fs_file; const char *const *pathp, *path; int ret = 0; array_foreach(&file->attachment_paths, pathp) T_BEGIN { /* we don't know if we aborted before renaming this attachment, so try deleting both source and dest path. the source paths point to temporary files (not to source messages' attachment paths), so it's safe to delete them. */ path = t_strdup_printf("%s/%s", storage->attachment_dir, *pathp); fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); if (fs_delete(fs_file) < 0 && errno != ENOENT) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(fs)); ret = -1; } fs_file_deinit(&fs_file); path = t_strdup_printf("%s/%s", storage->attachment_dir, sdbox_file_attachment_relpath(file, *pathp)); fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); if (fs_delete(fs_file) < 0 && errno != ENOENT) { mail_storage_set_critical(&storage->storage, "%s", fs_last_error(fs)); ret = -1; } fs_file_deinit(&fs_file); } T_END; return ret; } int sdbox_file_unlink_aborted_save(struct sdbox_file *file) { int ret = 0; if (unlink(file->file.cur_path) < 0) { mail_storage_set_critical(file->mbox->box.storage, "unlink(%s) failed: %m", file->file.cur_path); ret = -1; } if (array_is_created(&file->attachment_paths)) { if (sdbox_file_unlink_aborted_save_attachments(file) < 0) ret = -1; } return ret; } int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents) { struct sdbox_file *sfile = (struct sdbox_file *)file; struct mailbox *box = &sfile->mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *p, *dir; mode_t old_mask; int fd; old_mask = umask(0666 & ~perm->file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); if (fd == -1 && errno == ENOENT && parents && (p = strrchr(path, '/')) != NULL) { dir = t_strdup_until(path, p); if (mkdir_parents_chgrp(dir, perm->dir_create_mode, perm->file_create_gid, perm->file_create_gid_origin) < 0 && errno != EEXIST) { mail_storage_set_critical(box->storage, "mkdir_parents(%s) failed: %m", dir); return -1; } /* try again */ old_mask = umask(0666 & ~perm->file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); } if (fd == -1) { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", path); } else if (perm->file_create_gid == (gid_t)-1) { /* no group change */ } else if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s, -1, %ld) failed: %m", path, (long)perm->file_create_gid); } /* continue anyway */ } return fd; } int sdbox_file_move(struct dbox_file *file, bool alt_path) { struct mail_storage *storage = &file->storage->storage; struct ostream *output; const char *dest_dir, *temp_path, *dest_path, *p; struct stat st; struct utimbuf ut; bool deleted; int out_fd, ret = 0; i_assert(file->input != NULL); if (dbox_file_is_in_alt(file) == alt_path) return 0; if (file->alt_path == NULL) return 0; if (stat(file->cur_path, &st) < 0 && errno == ENOENT) { /* already expunged/moved by another session */ return 0; } dest_path = !alt_path ? file->primary_path : file->alt_path; i_assert(dest_path != NULL); p = strrchr(dest_path, '/'); i_assert(p != NULL); dest_dir = t_strdup_until(dest_path, p); temp_path = t_strdup_printf("%s/%s", dest_dir, dbox_generate_tmp_filename()); /* first copy the file. make sure to catch every possible error since we really don't want to break the file. */ out_fd = file->storage->v.file_create_fd(file, temp_path, TRUE); if (out_fd == -1) return -1; output = o_stream_create_fd_file(out_fd, 0, FALSE); i_stream_seek(file->input, 0); ret = o_stream_send_istream(output, file->input) > 0 ? 0 : -1; if (o_stream_nfinish(output) < 0) { mail_storage_set_critical(storage, "write(%s) failed: %s", temp_path, o_stream_get_error(output)); ret = -1; } else if (file->input->stream_errno != 0) { mail_storage_set_critical(storage, "read(%s) failed: %s", temp_path, i_stream_get_error(file->input)); ret = -1; } o_stream_unref(&output); if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER && ret == 0) { if (fsync(out_fd) < 0) { mail_storage_set_critical(storage, "fsync(%s) failed: %m", temp_path); ret = -1; } } if (close(out_fd) < 0) { mail_storage_set_critical(storage, "close(%s) failed: %m", temp_path); ret = -1; } if (ret < 0) { i_unlink(temp_path); return -1; } /* preserve the original atime/mtime. this isn't necessary for Dovecot, but could be useful for external reasons. */ ut.actime = st.st_atime; ut.modtime = st.st_mtime; if (utime(temp_path, &ut) < 0) { mail_storage_set_critical(storage, "utime(%s) failed: %m", temp_path); } /* the temp file was successfully written. rename it now to the destination file. the destination shouldn't exist, but if it does its contents should be the same (except for maybe older metadata) */ if (rename(temp_path, dest_path) < 0) { mail_storage_set_critical(storage, "rename(%s, %s) failed: %m", temp_path, dest_path); i_unlink_if_exists(temp_path); return -1; } if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync_path(dest_dir) < 0) { mail_storage_set_critical(storage, "fdatasync(%s) failed: %m", dest_dir); i_unlink(dest_path); return -1; } } if (unlink(file->cur_path) < 0) { dbox_file_set_syscall_error(file, "unlink()"); if (errno == EACCES) { /* configuration problem? revert the write */ i_unlink(dest_path); } /* who knows what happened to the file. keep both just to be sure both won't get deleted. */ return -1; } /* file was successfully moved - reopen it */ dbox_file_close(file); if (dbox_file_open(file, &deleted) <= 0) { mail_storage_set_critical(storage, "dbox_file_move(%s): reopening file failed", dest_path); return -1; } return 0; } static int sdbox_unlink_attachments(struct sdbox_file *sfile, const ARRAY_TYPE(mail_attachment_extref) *extrefs) { struct dbox_storage *storage = sfile->file.storage; const struct mail_attachment_extref *extref; const char *path; int ret = 0; array_foreach(extrefs, extref) T_BEGIN { path = sdbox_file_attachment_relpath(sfile, extref->path); if (index_attachment_delete(&storage->storage, storage->attachment_fs, path) < 0) ret = -1; } T_END; return ret; } int sdbox_file_unlink_with_attachments(struct sdbox_file *sfile) { ARRAY_TYPE(mail_attachment_extref) extrefs; const char *extrefs_line; pool_t pool; int ret; ret = sdbox_file_get_attachments(&sfile->file, &extrefs_line); if (ret < 0) return -1; if (ret == 0) { /* no attachments */ return dbox_file_unlink(&sfile->file); } pool = pool_alloconly_create("sdbox attachments unlink", 1024); p_array_init(&extrefs, pool, 16); if (!index_attachment_parse_extrefs(extrefs_line, pool, &extrefs)) { i_warning("%s: Ignoring corrupted extref: %s", sfile->file.cur_path, extrefs_line); array_clear(&extrefs); } /* try to delete the file first, so if it fails we don't have missing attachments */ if ((ret = dbox_file_unlink(&sfile->file)) >= 0) (void)sdbox_unlink_attachments(sfile, &extrefs); pool_unref(&pool); return ret; } dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-sync-rebuild.c0000644000175000017500000001323113165463624022541 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "index-rebuild.h" #include "mail-cache.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include "sdbox-sync.h" #include static void sdbox_sync_set_uidvalidity(struct index_rebuild_context *ctx) { uint32_t uid_validity; /* if uidvalidity is set in the old index, use it */ uid_validity = mail_index_get_header(ctx->view)->uid_validity; if (uid_validity == 0) uid_validity = dbox_get_uidvalidity_next(ctx->box->list); mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } static int sdbox_sync_add_file_index(struct index_rebuild_context *ctx, struct dbox_file *file, uint32_t uid, bool primary) { uint32_t seq; bool deleted; int ret; if ((ret = dbox_file_open(file, &deleted)) > 0) { if (deleted) return 0; ret = dbox_file_seek(file, 0); } if (ret == 0) { if ((ret = dbox_file_fix(file, 0)) > 0) ret = dbox_file_seek(file, 0); } if (ret <= 0) { if (ret < 0) return -1; i_warning("sdbox: Skipping unfixable file: %s", file->cur_path); return 0; } if (!dbox_file_is_in_alt(file) && !primary) { /* we were supposed to open the file in alt storage, but it exists in primary storage as well. skip it to avoid adding it twice. */ return 0; } mail_index_append(ctx->trans, uid, &seq); T_BEGIN { index_rebuild_index_metadata(ctx, seq, uid); } T_END; return 0; } static int sdbox_sync_add_file(struct index_rebuild_context *ctx, const char *fname, bool primary) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)ctx->box; struct dbox_file *file; uint32_t uid; int ret; if (strncmp(fname, SDBOX_MAIL_FILE_PREFIX, strlen(SDBOX_MAIL_FILE_PREFIX)) != 0) return 0; fname += strlen(SDBOX_MAIL_FILE_PREFIX); if (str_to_uint32(fname, &uid) < 0 || uid == 0) { i_warning("sdbox %s: Ignoring invalid filename %s", mailbox_get_path(ctx->box), fname); return 0; } file = sdbox_file_init(mbox, uid); if (!primary) file->cur_path = file->alt_path; ret = sdbox_sync_add_file_index(ctx, file, uid, primary); dbox_file_unref(&file); return ret; } static int sdbox_sync_index_rebuild_dir(struct index_rebuild_context *ctx, const char *path, bool primary) { struct mail_storage *storage = ctx->box->storage; DIR *dir; struct dirent *d; int ret = 0; dir = opendir(path); if (dir == NULL) { if (errno == ENOENT) { if (!primary) { /* alt directory doesn't exist, ignore */ return 0; } return index_mailbox_fix_inconsistent_existence(ctx->box, path); } mail_storage_set_critical(storage, "opendir(%s) failed: %m", path); return -1; } do { errno = 0; if ((d = readdir(dir)) == NULL) break; ret = sdbox_sync_add_file(ctx, d->d_name, primary); } while (ret >= 0); if (errno != 0) { mail_storage_set_critical(storage, "readdir(%s) failed: %m", path); ret = -1; } if (closedir(dir) < 0) { mail_storage_set_critical(storage, "closedir(%s) failed: %m", path); ret = -1; } return ret; } static void sdbox_sync_update_header(struct index_rebuild_context *ctx) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)ctx->box; struct sdbox_index_header hdr; bool need_resize; if (sdbox_read_header(mbox, &hdr, FALSE, &need_resize) < 0) i_zero(&hdr); if (guid_128_is_empty(hdr.mailbox_guid)) guid_128_generate(hdr.mailbox_guid); if (++hdr.rebuild_count == 0) hdr.rebuild_count = 1; /* mailbox is being reset. this gets written directly there */ mail_index_set_ext_init_data(ctx->box->index, mbox->hdr_ext_id, &hdr, sizeof(hdr)); } static int sdbox_sync_index_rebuild_singles(struct index_rebuild_context *ctx) { const char *path, *alt_path; int ret = 0; path = mailbox_get_path(ctx->box); if (mailbox_get_path_to(ctx->box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path) < 0) return -1; sdbox_sync_set_uidvalidity(ctx); if (sdbox_sync_index_rebuild_dir(ctx, path, TRUE) < 0) { mail_storage_set_critical(ctx->box->storage, "sdbox: Rebuilding failed on path %s", mailbox_get_path(ctx->box)); ret = -1; } else if (alt_path != NULL) { if (sdbox_sync_index_rebuild_dir(ctx, alt_path, FALSE) < 0) { mail_storage_set_critical(ctx->box->storage, "sdbox: Rebuilding failed on alt path %s", alt_path); ret = -1; } } sdbox_sync_update_header(ctx); return ret; } int sdbox_sync_index_rebuild(struct sdbox_mailbox *mbox, bool force) { struct index_rebuild_context *ctx; struct mail_index_view *view; struct mail_index_transaction *trans; struct sdbox_index_header hdr; bool need_resize; int ret; if (!force && sdbox_read_header(mbox, &hdr, FALSE, &need_resize) == 0) { if (hdr.rebuild_count != mbox->corrupted_rebuild_count && hdr.rebuild_count != 0) { /* already rebuilt by someone else */ return 0; } } i_warning("sdbox %s: Rebuilding index", mailbox_get_path(&mbox->box)); if (dbox_verify_alt_storage(mbox->box.list) < 0) { mail_storage_set_critical(mbox->box.storage, "sdbox %s: Alt storage not mounted, " "aborting index rebuild", mailbox_get_path(&mbox->box)); return -1; } view = mail_index_view_open(mbox->box.index); trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); ctx = index_index_rebuild_init(&mbox->box, view, trans); ret = sdbox_sync_index_rebuild_singles(ctx); index_index_rebuild_deinit(&ctx, dbox_get_uidvalidity_next); if (ret < 0) mail_index_transaction_rollback(&trans); else { mail_index_unset_fscked(trans); ret = mail_index_transaction_commit(&trans); } mail_index_view_close(&view); mbox->corrupted_rebuild_count = 0; return ret; } dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-storage.c0000644000175000017500000003365713165463624021623 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fs-api.h" #include "master-service.h" #include "mail-index-modseq.h" #include "mail-search-build.h" #include "mailbox-list-private.h" #include "index-pop3-uidl.h" #include "dbox-mail.h" #include "dbox-save.h" #include "sdbox-file.h" #include "sdbox-sync.h" #include "sdbox-storage.h" extern struct mail_storage dbox_storage, sdbox_storage; extern struct mailbox sdbox_mailbox; extern struct dbox_storage_vfuncs sdbox_dbox_storage_vfuncs; static struct mail_storage *sdbox_storage_alloc(void) { struct sdbox_storage *storage; pool_t pool; pool = pool_alloconly_create("sdbox storage", 512+256); storage = p_new(pool, struct sdbox_storage, 1); storage->storage.v = sdbox_dbox_storage_vfuncs; storage->storage.storage = sdbox_storage; storage->storage.storage.pool = pool; return &storage->storage.storage; } static int sdbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct dbox_storage *storage = (struct dbox_storage *)_storage; enum fs_properties props; if (dbox_storage_create(_storage, ns, error_r) < 0) return -1; if (storage->attachment_fs != NULL) { props = fs_get_properties(storage->attachment_fs); if ((props & FS_PROPERTY_RENAME) == 0) { *error_r = "mail_attachment_fs: " "Backend doesn't support renaming"; return -1; } } return 0; } static const char * sdbox_storage_find_root_dir(const struct mail_namespace *ns) { bool debug = ns->mail_set->mail_debug; const char *home, *path; if (ns->owner != NULL && mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/sdbox", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug) i_debug("sdbox: root exists (%s)", path); return path; } if (debug) i_debug("sdbox: access(%s, rwx): failed: %m", path); } return NULL; } static bool sdbox_storage_autodetect(const struct mail_namespace *ns, struct mailbox_list_settings *set) { bool debug = ns->mail_set->mail_debug; struct stat st; const char *path, *root_dir; if (set->root_dir != NULL) root_dir = set->root_dir; else { root_dir = sdbox_storage_find_root_dir(ns); if (root_dir == NULL) { if (debug) i_debug("sdbox: couldn't find root dir"); return FALSE; } } /* NOTE: this check works for mdbox as well. we'll rely on the autodetect ordering to catch mdbox before we get here. */ path = t_strconcat(root_dir, "/"DBOX_MAILBOX_DIR_NAME, NULL); if (stat(path, &st) < 0) { if (debug) i_debug("sdbox autodetect: stat(%s) failed: %m", path); return FALSE; } if (!S_ISDIR(st.st_mode)) { if (debug) i_debug("sdbox autodetect: %s not a directory", path); return FALSE; } set->root_dir = root_dir; dbox_storage_get_list_settings(ns, set); return TRUE; } static struct mailbox * sdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct sdbox_mailbox *mbox; struct index_mailbox_context *ibox; pool_t pool; /* dbox can't work without index files */ flags &= ~MAILBOX_FLAG_NO_INDEX_FILES; pool = pool_alloconly_create("sdbox mailbox", 1024*3); mbox = p_new(pool, struct sdbox_mailbox, 1); mbox->box = sdbox_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &sdbox_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); ibox = INDEX_STORAGE_CONTEXT(&mbox->box); ibox->index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS | MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY; mbox->storage = (struct sdbox_storage *)storage; return &mbox->box; } int sdbox_read_header(struct sdbox_mailbox *mbox, struct sdbox_index_header *hdr, bool log_error, bool *need_resize_r) { struct mail_index_view *view; const void *data; size_t data_size; int ret = 0; i_assert(mbox->box.opened); view = mail_index_view_open(mbox->box.index); mail_index_get_header_ext(view, mbox->hdr_ext_id, &data, &data_size); if (data_size < SDBOX_INDEX_HEADER_MIN_SIZE && (!mbox->box.creating || data_size != 0)) { if (log_error) { mail_storage_set_critical( &mbox->storage->storage.storage, "sdbox %s: Invalid dbox header size", mailbox_get_path(&mbox->box)); } ret = -1; } else { i_zero(hdr); memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr))); if (guid_128_is_empty(hdr->mailbox_guid)) ret = -1; else { /* data is valid. remember it in case mailbox is being reset */ mail_index_set_ext_init_data(mbox->box.index, mbox->hdr_ext_id, hdr, sizeof(*hdr)); } } mail_index_view_close(&view); *need_resize_r = data_size < sizeof(*hdr); return ret; } static void sdbox_update_header(struct sdbox_mailbox *mbox, struct mail_index_transaction *trans, const struct mailbox_update *update) { struct sdbox_index_header hdr, new_hdr; bool need_resize; if (sdbox_read_header(mbox, &hdr, TRUE, &need_resize) < 0) { i_zero(&hdr); need_resize = TRUE; } new_hdr = hdr; if (update != NULL && !guid_128_is_empty(update->mailbox_guid)) { memcpy(new_hdr.mailbox_guid, update->mailbox_guid, sizeof(new_hdr.mailbox_guid)); } else if (guid_128_is_empty(new_hdr.mailbox_guid)) { guid_128_generate(new_hdr.mailbox_guid); } if (need_resize) { mail_index_ext_resize_hdr(trans, mbox->hdr_ext_id, sizeof(new_hdr)); } if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) { mail_index_update_header_ext(trans, mbox->hdr_ext_id, 0, &new_hdr, sizeof(new_hdr)); } memcpy(mbox->mailbox_guid, new_hdr.mailbox_guid, sizeof(mbox->mailbox_guid)); } int sdbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; struct mail_index_transaction *new_trans = NULL; const struct mail_index_header *hdr; uint32_t uid_validity, uid_next; if (trans == NULL) { new_trans = mail_index_transaction_begin(box->view, 0); trans = new_trans; } hdr = mail_index_get_header(box->view); if (update != NULL && update->uid_validity != 0) uid_validity = update->uid_validity; else if (hdr->uid_validity != 0) uid_validity = hdr->uid_validity; else { /* set uidvalidity */ uid_validity = dbox_get_uidvalidity_next(box->list); } if (hdr->uid_validity != uid_validity) { mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } if (update != NULL && hdr->next_uid < update->min_next_uid) { uid_next = update->min_next_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, next_uid), &uid_next, sizeof(uid_next), TRUE); } if (update != NULL && update->min_first_recent_uid != 0 && hdr->first_recent_uid < update->min_first_recent_uid) { uint32_t first_recent_uid = update->min_first_recent_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } if (update != NULL && update->min_highest_modseq != 0 && mail_index_modseq_get_highest(box->view) < update->min_highest_modseq) { mail_index_modseq_enable(box->index); mail_index_update_highest_modseq(trans, update->min_highest_modseq); } if (box->inbox_user && box->creating) { /* initialize pop3-uidl header when creating mailbox (not on mailbox_update()) */ index_pop3_uidl_set_max_uid(box, trans, 0); } sdbox_update_header(mbox, trans, update); if (new_trans != NULL) { if (mail_index_transaction_commit(&new_trans) < 0) { mailbox_set_index_error(box); return -1; } } return 0; } static const char * sdbox_get_attachment_path_suffix(struct dbox_file *_file) { struct sdbox_file *file = (struct sdbox_file *)_file; return t_strdup_printf("-%s-%u", guid_128_to_string(file->mbox->mailbox_guid), file->uid); } void sdbox_set_mailbox_corrupted(struct mailbox *box) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; struct sdbox_index_header hdr; bool need_resize; if (sdbox_read_header(mbox, &hdr, TRUE, &need_resize) < 0 || hdr.rebuild_count == 0) mbox->corrupted_rebuild_count = 1; else mbox->corrupted_rebuild_count = hdr.rebuild_count; } static void sdbox_set_file_corrupted(struct dbox_file *_file) { struct sdbox_file *file = (struct sdbox_file *)_file; sdbox_set_mailbox_corrupted(&file->mbox->box); } static int sdbox_mailbox_alloc_index(struct sdbox_mailbox *mbox) { struct sdbox_index_header hdr; if (index_storage_mailbox_alloc_index(&mbox->box) < 0) return -1; mbox->hdr_ext_id = mail_index_ext_register(mbox->box.index, "dbox-hdr", sizeof(struct sdbox_index_header), 0, 0); /* set the initialization data in case the mailbox is created */ i_zero(&hdr); guid_128_generate(hdr.mailbox_guid); mail_index_set_ext_init_data(mbox->box.index, mbox->hdr_ext_id, &hdr, sizeof(hdr)); return 0; } static int sdbox_mailbox_open(struct mailbox *box) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; struct sdbox_index_header hdr; bool need_resize; time_t path_ctime; if (dbox_mailbox_check_existence(box, &path_ctime) < 0) return -1; if (sdbox_mailbox_alloc_index(mbox) < 0) return -1; if (dbox_mailbox_open(box, path_ctime) < 0) return -1; if (box->creating) { /* wait for mailbox creation to initialize the index */ return 0; } /* get/generate mailbox guid */ if (sdbox_read_header(mbox, &hdr, FALSE, &need_resize) < 0) { /* looks like the mailbox is corrupted */ (void)sdbox_sync(mbox, SDBOX_SYNC_FLAG_FORCE); if (sdbox_read_header(mbox, &hdr, TRUE, &need_resize) < 0) i_zero(&hdr); } if (guid_128_is_empty(hdr.mailbox_guid)) { /* regenerate it */ if (sdbox_mailbox_create_indexes(box, NULL, NULL) < 0 || sdbox_read_header(mbox, &hdr, TRUE, &need_resize) < 0) return -1; } memcpy(mbox->mailbox_guid, hdr.mailbox_guid, sizeof(mbox->mailbox_guid)); return 0; } static void sdbox_mailbox_close(struct mailbox *box) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; if (mbox->corrupted_rebuild_count != 0) (void)sdbox_sync(mbox, 0); index_storage_mailbox_close(box); } static int sdbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; struct sdbox_index_header hdr; bool need_resize; if (dbox_mailbox_create(box, update, directory) < 0) return -1; if (directory || !guid_128_is_empty(mbox->mailbox_guid)) return 0; /* another process just created the mailbox. read the mailbox_guid. */ if (sdbox_read_header(mbox, &hdr, FALSE, &need_resize) < 0) { mail_storage_set_critical(box->storage, "sdbox %s: Failed to read newly created dbox header", mailbox_get_path(&mbox->box)); return -1; } memcpy(mbox->mailbox_guid, hdr.mailbox_guid, sizeof(mbox->mailbox_guid)); i_assert(!guid_128_is_empty(mbox->mailbox_guid)); return 0; } static int sdbox_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box; if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; if ((items & MAILBOX_METADATA_GUID) != 0) { memcpy(metadata_r->guid, mbox->mailbox_guid, sizeof(metadata_r->guid)); } return 0; } static int dbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { if (!box->opened) { if (mailbox_open(box) < 0) return -1; } if (sdbox_mailbox_create_indexes(box, update, NULL) < 0) return -1; return index_storage_mailbox_update_common(box, update); } struct mail_storage sdbox_storage = { .name = SDBOX_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA | MAIL_STORAGE_CLASS_FLAG_STUBS, .v = { NULL, sdbox_storage_alloc, sdbox_storage_create, dbox_storage_destroy, NULL, dbox_storage_get_list_settings, sdbox_storage_autodetect, sdbox_mailbox_alloc, NULL, NULL, } }; struct mail_storage dbox_storage = { .name = "dbox", /* alias */ .class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG, .v = { NULL, sdbox_storage_alloc, sdbox_storage_create, dbox_storage_destroy, NULL, dbox_storage_get_list_settings, sdbox_storage_autodetect, sdbox_mailbox_alloc, NULL, NULL, } }; struct mailbox sdbox_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, sdbox_mailbox_open, sdbox_mailbox_close, index_storage_mailbox_free, sdbox_mailbox_create, dbox_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, sdbox_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, sdbox_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, dbox_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, dbox_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, sdbox_save_alloc, sdbox_save_begin, dbox_save_continue, sdbox_save_finish, sdbox_save_cancel, sdbox_copy, sdbox_transaction_save_commit_pre, sdbox_transaction_save_commit_post, sdbox_transaction_save_rollback, index_storage_is_inconsistent } }; struct dbox_storage_vfuncs sdbox_dbox_storage_vfuncs = { sdbox_file_free, sdbox_file_create_fd, sdbox_mail_open, sdbox_mailbox_create_indexes, sdbox_get_attachment_path_suffix, sdbox_set_mailbox_corrupted, sdbox_set_file_corrupted }; dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-copy.c0000644000175000017500000001343013165463624021114 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "nfs-workarounds.h" #include "fs-api.h" #include "dbox-save.h" #include "dbox-attachment.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include "mail-copy.h" static int sdbox_file_copy_attachments(struct sdbox_file *src_file, struct sdbox_file *dest_file) { struct dbox_storage *src_storage = src_file->file.storage; struct dbox_storage *dest_storage = dest_file->file.storage; struct fs_file *src_fsfile, *dest_fsfile; ARRAY_TYPE(mail_attachment_extref) extrefs; const struct mail_attachment_extref *extref; const char *extrefs_line, *src, *dest, *dest_relpath; pool_t pool; int ret; if (src_storage->attachment_dir == NULL) { /* no attachments in source storage */ return 1; } if (dest_storage->attachment_dir == NULL || strcmp(src_storage->attachment_dir, dest_storage->attachment_dir) != 0 || strcmp(src_storage->storage.set->mail_attachment_fs, dest_storage->storage.set->mail_attachment_fs) != 0 || strcmp(src_storage->storage.set->mail_attachment_hash, dest_storage->storage.set->mail_attachment_hash) != 0) { /* different attachment dirs/settings between storages. have to copy the slow way. */ return 0; } if ((ret = sdbox_file_get_attachments(&src_file->file, &extrefs_line)) <= 0) return ret < 0 ? -1 : 1; pool = pool_alloconly_create("sdbox attachments copy", 1024); p_array_init(&extrefs, pool, 16); if (!index_attachment_parse_extrefs(extrefs_line, pool, &extrefs)) { mail_storage_set_critical(&dest_storage->storage, "Can't copy %s with corrupted extref metadata: %s", src_file->file.cur_path, extrefs_line); pool_unref(&pool); return -1; } dest_file->attachment_pool = pool_alloconly_create("sdbox attachment copy paths", 512); p_array_init(&dest_file->attachment_paths, dest_file->attachment_pool, array_count(&extrefs)); ret = 1; array_foreach(&extrefs, extref) T_BEGIN { src = t_strdup_printf("%s/%s", dest_storage->attachment_dir, sdbox_file_attachment_relpath(src_file, extref->path)); dest_relpath = p_strconcat(dest_file->attachment_pool, extref->path, "-", guid_generate(), NULL); dest = t_strdup_printf("%s/%s", dest_storage->attachment_dir, dest_relpath); /* we verified above that attachment_fs is compatible for src and dest, so it doesn't matter which storage's attachment_fs we use. in any case we need to use the same one or fs_copy() will crash with assert. */ src_fsfile = fs_file_init(dest_storage->attachment_fs, src, FS_OPEN_MODE_READONLY); dest_fsfile = fs_file_init(dest_storage->attachment_fs, dest, FS_OPEN_MODE_READONLY); if (fs_copy(src_fsfile, dest_fsfile) < 0) { mail_storage_set_critical(&dest_storage->storage, "%s", fs_last_error(dest_storage->attachment_fs)); ret = -1; } else { array_append(&dest_file->attachment_paths, &dest_relpath, 1); } fs_file_deinit(&src_fsfile); fs_file_deinit(&dest_fsfile); } T_END; pool_unref(&pool); return ret; } static int sdbox_copy_hardlink(struct mail_save_context *_ctx, struct mail *mail) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; struct sdbox_mailbox *dest_mbox = (struct sdbox_mailbox *)_ctx->transaction->box; struct sdbox_mailbox *src_mbox; struct dbox_file *src_file, *dest_file; const char *src_path, *dest_path; int ret; if (strcmp(mail->box->storage->name, SDBOX_STORAGE_NAME) == 0) src_mbox = (struct sdbox_mailbox *)mail->box; else { /* Source storage isn't sdbox, can't hard link */ return 0; } src_file = sdbox_file_init(src_mbox, mail->uid); dest_file = sdbox_file_init(dest_mbox, 0); ctx->ctx.data.flags &= ~DBOX_INDEX_FLAG_ALT; src_path = src_file->primary_path; dest_path = dest_file->primary_path; ret = nfs_safe_link(src_path, dest_path, FALSE); if (ret < 0 && errno == ENOENT && src_file->alt_path != NULL) { src_path = src_file->alt_path; if (dest_file->alt_path != NULL) { dest_path = dest_file->cur_path = dest_file->alt_path; ctx->ctx.data.flags |= DBOX_INDEX_FLAG_ALT; } ret = nfs_safe_link(src_path, dest_path, FALSE); } if (ret < 0) { if (ECANTLINK(errno)) ret = 0; else if (errno == ENOENT) { /* try if the fallback copying code can still read the file (the mail could still have the stream open) */ ret = 0; } else { mail_storage_set_critical( _ctx->transaction->box->storage, "link(%s, %s) failed: %m", src_path, dest_path); } dbox_file_unref(&src_file); dbox_file_unref(&dest_file); return ret; } ret = sdbox_file_copy_attachments((struct sdbox_file *)src_file, (struct sdbox_file *)dest_file); if (ret <= 0) { (void)sdbox_file_unlink_aborted_save((struct sdbox_file *)dest_file); dbox_file_unref(&src_file); dbox_file_unref(&dest_file); return ret; } ((struct sdbox_file *)dest_file)->written_to_disk = TRUE; dbox_save_add_to_index(ctx); index_copy_cache_fields(_ctx, mail, ctx->seq); sdbox_save_add_file(_ctx, dest_file); mail_set_seq_saving(_ctx->dest_mail, ctx->seq); dbox_file_unref(&src_file); return 1; } int sdbox_copy(struct mail_save_context *_ctx, struct mail *mail) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_t->box; int ret; i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); ctx->finished = TRUE; if (mail_storage_copy_can_use_hardlink(mail->box, &mbox->box) && _ctx->data.guid == NULL) { T_BEGIN { ret = sdbox_copy_hardlink(_ctx, mail); } T_END; if (ret != 0) { index_save_context_free(_ctx); return ret > 0 ? 0 : -1; } /* non-fatal hardlinking failure, try the slow way */ } return mail_storage_copy(_ctx, mail); } dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-sync.h0000644000175000017500000000204013123174404021103 00000000000000#ifndef SDBOX_SYNC_H #define SDBOX_SYNC_H struct mailbox; struct sdbox_mailbox; enum sdbox_sync_flags { SDBOX_SYNC_FLAG_FORCE = 0x01, SDBOX_SYNC_FLAG_FSYNC = 0x02, SDBOX_SYNC_FLAG_FORCE_REBUILD = 0x04 }; enum sdbox_sync_entry_type { SDBOX_SYNC_ENTRY_TYPE_EXPUNGE, SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT, SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT }; struct sdbox_sync_context { struct sdbox_mailbox *mbox; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; enum sdbox_sync_flags flags; ARRAY_TYPE(uint32_t) expunged_uids; }; int sdbox_sync_begin(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags, struct sdbox_sync_context **ctx_r); int sdbox_sync_finish(struct sdbox_sync_context **ctx, bool success); int sdbox_sync(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags); int sdbox_sync_index_rebuild(struct sdbox_mailbox *mbox, bool force); struct mailbox_sync_context * sdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-file.h0000644000175000017500000000300413123174404021047 00000000000000#ifndef SDBOX_FILE_H #define SDBOX_FILE_H #include "dbox-file.h" struct sdbox_file { struct dbox_file file; struct sdbox_mailbox *mbox; /* 0 while file is being created */ uint32_t uid; /* list of attachment paths while saving/copying message */ pool_t attachment_pool; ARRAY_TYPE(const_string) attachment_paths; bool written_to_disk; }; struct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid); struct dbox_file *sdbox_file_create(struct sdbox_mailbox *mbox); void sdbox_file_free(struct dbox_file *file); /* Get file's extrefs metadata. */ int sdbox_file_get_attachments(struct dbox_file *file, const char **extrefs_r); /* Returns attachment path for this file, given the source path. The result is always ---. The source path is expected to contain -[-*]. */ const char * sdbox_file_attachment_relpath(struct sdbox_file *file, const char *srcpath); /* Assign UID for a newly created file (by renaming it) */ int sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid, bool ignore_if_exists); int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents); /* Move the file to alt path or back. */ int sdbox_file_move(struct dbox_file *file, bool alt_path); /* Unlink file and all of its referenced attachments. */ int sdbox_file_unlink_with_attachments(struct sdbox_file *sfile); /* Unlink file and its attachments when rollbacking a saved message. */ int sdbox_file_unlink_aborted_save(struct sdbox_file *file); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-mail.c0000644000175000017500000001043713165463624021070 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "str.h" #include "index-mail.h" #include "dbox-mail.h" #include "sdbox-storage.h" #include "sdbox-file.h" #include static void sdbox_mail_set_expunged(struct dbox_mail *mail) { struct mail *_mail = &mail->imail.mail.mail; mail_index_refresh(_mail->box->index); if (mail_index_is_expunged(_mail->transaction->view, _mail->seq)) { mail_set_expunged(_mail); return; } mail_storage_set_critical(_mail->box->storage, "dbox %s: Unexpectedly lost uid=%u", mailbox_get_path(_mail->box), _mail->uid); sdbox_set_mailbox_corrupted(_mail->box); } static int sdbox_mail_file_set(struct dbox_mail *mail) { struct mail *_mail = &mail->imail.mail.mail; struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_mail->box; bool deleted; int ret; if (mail->open_file != NULL) { /* already set */ return 0; } else if (!_mail->saving) { mail->open_file = sdbox_file_init(mbox, _mail->uid); return 0; } else { /* mail is being saved in this transaction */ mail->open_file = sdbox_save_file_get_file(_mail->transaction, _mail->seq); mail->open_file->refcount++; /* it doesn't have input stream yet */ ret = dbox_file_open(mail->open_file, &deleted); if (ret <= 0) { mail_storage_set_critical(_mail->box->storage, "dbox %s: Unexpectedly lost mail being saved", mailbox_get_path(_mail->box)); sdbox_set_mailbox_corrupted(_mail->box); return -1; } return 1; } } static int sdbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_mail->box; struct dbox_mail *mail = (struct dbox_mail *)_mail; struct stat st; switch (field) { case MAIL_FETCH_REFCOUNT: if (sdbox_mail_file_set(mail) < 0) return -1; _mail->transaction->stats.fstat_lookup_count++; if (dbox_file_stat(mail->open_file, &st) < 0) { if (errno == ENOENT) mail_set_expunged(_mail); return -1; } *value_r = p_strdup_printf(mail->imail.mail.data_pool, "%lu", (unsigned long)st.st_nlink); return 0; case MAIL_FETCH_UIDL_BACKEND: if (!dbox_header_have_flag(&mbox->box, mbox->hdr_ext_id, offsetof(struct sdbox_index_header, flags), DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS)) { *value_r = ""; return 0; } break; case MAIL_FETCH_POP3_ORDER: if (!dbox_header_have_flag(&mbox->box, mbox->hdr_ext_id, offsetof(struct sdbox_index_header, flags), DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS)) { *value_r = ""; return 0; } break; default: break; } return dbox_mail_get_special(_mail, field, value_r); } int sdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r, struct dbox_file **file_r) { struct mail *_mail = &mail->imail.mail.mail; bool deleted; int ret; if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(_mail); return -1; } _mail->mail_stream_opened = TRUE; ret = sdbox_mail_file_set(mail); if (ret < 0) return -1; if (ret == 0) { if (!dbox_file_is_open(mail->open_file)) _mail->transaction->stats.open_lookup_count++; if (dbox_file_open(mail->open_file, &deleted) <= 0) return -1; if (deleted) { sdbox_mail_set_expunged(mail); return -1; } } *file_r = mail->open_file; *offset_r = 0; return 0; } struct mail_vfuncs sdbox_mail_vfuncs = { dbox_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, dbox_mail_get_received_date, dbox_mail_get_save_date, dbox_mail_get_virtual_size, dbox_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, dbox_mail_get_stream, index_mail_get_binary_stream, sdbox_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/dbox-single/sdbox-storage.h0000644000175000017500000000406113165463624021613 00000000000000#ifndef SDBOX_STORAGE_H #define SDBOX_STORAGE_H #include "index-storage.h" #include "dbox-storage.h" #define SDBOX_STORAGE_NAME "sdbox" #define SDBOX_MAIL_FILE_PREFIX "u." #define SDBOX_MAIL_FILE_FORMAT SDBOX_MAIL_FILE_PREFIX"%u" #define SDBOX_INDEX_HEADER_MIN_SIZE (sizeof(uint32_t)) struct sdbox_index_header { /* increased every time a full mailbox rebuild is done */ uint32_t rebuild_count; guid_128_t mailbox_guid; uint8_t flags; /* enum dbox_index_header_flags */ uint8_t unused[3]; }; struct sdbox_storage { struct dbox_storage storage; }; struct sdbox_mailbox { struct mailbox box; struct sdbox_storage *storage; uint32_t hdr_ext_id; /* if non-zero, storage should be rebuilt (except if rebuild_count has changed from this value) */ uint32_t corrupted_rebuild_count; guid_128_t mailbox_guid; }; extern struct mail_vfuncs sdbox_mail_vfuncs; int sdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r, struct dbox_file **file_r); int sdbox_read_header(struct sdbox_mailbox *mbox, struct sdbox_index_header *hdr, bool log_error, bool *need_resize_r); int sdbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans); void sdbox_set_mailbox_corrupted(struct mailbox *box); struct mail_save_context * sdbox_save_alloc(struct mailbox_transaction_context *_t); int sdbox_save_begin(struct mail_save_context *ctx, struct istream *input); int sdbox_save_finish(struct mail_save_context *ctx); void sdbox_save_cancel(struct mail_save_context *ctx); struct dbox_file * sdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq); void sdbox_save_add_file(struct mail_save_context *ctx, struct dbox_file *file); int sdbox_transaction_save_commit_pre(struct mail_save_context *ctx); void sdbox_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void sdbox_transaction_save_rollback(struct mail_save_context *ctx); int sdbox_copy(struct mail_save_context *ctx, struct mail *mail); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-single/Makefile.am0000644000175000017500000000127513123174404020706 00000000000000noinst_LTLIBRARIES = libstorage_dbox_single.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common libstorage_dbox_single_la_SOURCES = \ sdbox-copy.c \ sdbox-file.c \ sdbox-mail.c \ sdbox-save.c \ sdbox-sync.c \ sdbox-sync-rebuild.c \ sdbox-storage.c headers = \ sdbox-file.h \ sdbox-storage.h \ sdbox-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-attachment.h0000644000175000017500000000351113123174404020040 00000000000000#ifndef INDEX_ATTACHMENT_H #define INDEX_ATTACHMENT_H #include "sha1.h" struct fs; struct mail_save_context; struct mail_storage; struct mail_attachment_extref { /* path without attachment_dir/ prefix */ const char *path; /* offset in input stream where part begins */ uoff_t start_offset; uoff_t size; /* If non-zero, this attachment was saved as base64-decoded and it need to be encoded back before presenting it to client. Each line (except last one) consists of this many base64 blocks (4 chars of base64 encoded data). */ unsigned int base64_blocks_per_line; /* Line feeds are CRLF instead of LF */ bool base64_have_crlf; }; ARRAY_DEFINE_TYPE(mail_attachment_extref, struct mail_attachment_extref); void index_attachment_save_begin(struct mail_save_context *ctx, struct fs *fs, struct istream *input); int index_attachment_save_continue(struct mail_save_context *ctx); int index_attachment_save_finish(struct mail_save_context *ctx); void index_attachment_save_free(struct mail_save_context *ctx); const ARRAY_TYPE(mail_attachment_extref) * index_attachment_save_get_extrefs(struct mail_save_context *ctx); /* Delete a given attachment name from storage (name is same as mail_attachment_extref.name). */ int index_attachment_delete(struct mail_storage *storage, struct fs *fs, const char *name); void index_attachment_append_extrefs(string_t *str, const ARRAY_TYPE(mail_attachment_extref) *extrefs); /* Parse extrefs value to given array. Names are allocated from the given pool. */ bool index_attachment_parse_extrefs(const char *line, pool_t pool, ARRAY_TYPE(mail_attachment_extref) *extrefs); int index_attachment_stream_get(struct fs *fs, const char *attachment_dir, const char *path_suffix, struct istream **stream, uoff_t full_size, const char *ext_refs, const char **error_r); #endif dovecot-2.2.33.2/src/lib-storage/index/Makefile.in0000644000175000017500000007304613172375573016527 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_index_la_LIBADD = am_libstorage_index_la_OBJECTS = istream-mail.lo index-attachment.lo \ index-attribute.lo index-mail.lo index-mail-binary.lo \ index-mail-headers.lo index-mailbox-size.lo index-pop3-uidl.lo \ index-rebuild.lo index-search.lo index-search-mime.lo \ index-search-result.lo index-sort.lo index-sort-string.lo \ index-status.lo index-storage.lo index-sync.lo \ index-sync-changes.lo index-sync-pvt.lo index-sync-search.lo \ index-thread.lo index-thread-finish.lo index-thread-links.lo \ index-transaction.lo libstorage_index_la_OBJECTS = $(am_libstorage_index_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_index_la_SOURCES) DIST_SOURCES = $(libstorage_index_la_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = maildir mbox dbox-common dbox-multi dbox-single cydir imapc pop3c raw shared noinst_LTLIBRARIES = libstorage_index.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage libstorage_index_la_SOURCES = \ istream-mail.c \ index-attachment.c \ index-attribute.c \ index-mail.c \ index-mail-binary.c \ index-mail-headers.c \ index-mailbox-size.c \ index-pop3-uidl.c \ index-rebuild.c \ index-search.c \ index-search-mime.c \ index-search-result.c \ index-sort.c \ index-sort-string.c \ index-status.c \ index-storage.c \ index-sync.c \ index-sync-changes.c \ index-sync-pvt.c \ index-sync-search.c \ index-thread.c \ index-thread-finish.c \ index-thread-links.c \ index-transaction.c headers = \ istream-mail.h \ index-attachment.h \ index-mail.h \ index-mailbox-size.h \ index-pop3-uidl.h \ index-rebuild.h \ index-search-private.h \ index-search-result.h \ index-sort.h \ index-sort-private.h \ index-storage.h \ index-sync-changes.h \ index-sync-private.h \ index-thread-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_index.la: $(libstorage_index_la_OBJECTS) $(libstorage_index_la_DEPENDENCIES) $(EXTRA_libstorage_index_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_index_la_OBJECTS) $(libstorage_index_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-attachment.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-attribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-mail-binary.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-mail-headers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-mailbox-size.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-pop3-uidl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-rebuild.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-search-mime.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-search-result.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sort-string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sort.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-status.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sync-changes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sync-pvt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sync-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-thread-finish.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-thread-links.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index-transaction.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-mail.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool \ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/index-pop3-uidl.c0000644000175000017500000000640713123174404017526 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "index-storage.h" #include "index-mail.h" #include "index-pop3-uidl.h" void index_pop3_uidl_set_max_uid(struct mailbox *box, struct mail_index_transaction *trans, uint32_t uid) { struct mailbox_index_pop3_uidl uidl; i_zero(&uidl); uidl.max_uid_with_pop3_uidl = uid; mail_index_update_header_ext(trans, box->pop3_uidl_hdr_ext_id, 0, &uidl, sizeof(uidl)); } bool index_pop3_uidl_can_exist(struct mail *mail) { struct mailbox_index_pop3_uidl uidl; const void *data; size_t size; /* We'll assume that if the header exists, it's up-to-date. normally UIDLs are set only during migration, so this value never changes. Also even if it does, it becomes out-of-date only when the mailbox is modified with old Dovecot versions. To fix that we'd have to add and keep updating "max tracked uid" in this header for every saved mail, which isn't worth it. */ mail_index_get_header_ext(mail->transaction->view, mail->box->pop3_uidl_hdr_ext_id, &data, &size); if (size < sizeof(uidl)) { /* this header isn't set yet */ return TRUE; } memcpy(&uidl, data, size); return mail->uid <= uidl.max_uid_with_pop3_uidl; } void index_pop3_uidl_update_exists(struct mail *mail, bool exists) { struct mailbox_transaction_context *trans = mail->transaction; if (exists) { if (trans->highest_pop3_uidl_uid < mail->uid) { trans->highest_pop3_uidl_uid = mail->uid; trans->prev_pop3_uidl_tracking_seq = mail->seq; } } else if (mail->seq == trans->prev_pop3_uidl_tracking_seq+1) { trans->prev_pop3_uidl_tracking_seq++; } else { /* skipping mails. we don't know the state. */ } } void index_pop3_uidl_update_exists_finish(struct mailbox_transaction_context *trans) { struct mail_index_view *view; struct mailbox_index_pop3_uidl uidl; const void *data; size_t size; bool seen_all_msgs; mail_index_get_header_ext(trans->view, trans->box->pop3_uidl_hdr_ext_id, &data, &size); if (trans->highest_pop3_uidl_uid == 0 && size >= sizeof(uidl)) { /* header already set and nothing to change */ return; } /* First check that we actually looked at UIDL for all messages. Otherwise we can't say for sure if the newest messages had UIDLs. */ if (trans->prev_pop3_uidl_tracking_seq != mail_index_view_get_messages_count(trans->view)) return; /* Just to be sure: Refresh the index and check again. POP3 keeps transactions open for duration of the entire session. Maybe another process already added new mails (and already updated this header). This check is racy, but normally UIDLs aren't added after migration so it's a bit questionable if it's even worth having this check in there. */ view = mail_index_view_open(trans->box->index); seen_all_msgs = mail_index_refresh(trans->box->index) == 0 && trans->prev_pop3_uidl_tracking_seq == mail_index_view_get_messages_count(view); mail_index_view_close(&view); if (!seen_all_msgs) return; /* check if we have already the same header */ if (size >= sizeof(uidl)) { memcpy(&uidl, data, size); if (trans->highest_pop3_uidl_uid == uidl.max_uid_with_pop3_uidl) return; } index_pop3_uidl_set_max_uid(trans->box, trans->itrans, trans->highest_pop3_uidl_uid); } dovecot-2.2.33.2/src/lib-storage/index/index-rebuild.h0000644000175000017500000000146413165463624017356 00000000000000#ifndef INDEX_REBUILD_H #define INDEX_REBUILD_H struct mailbox_list; struct index_rebuild_context { struct mailbox *box; struct mail_index_view *view; struct mail_index_transaction *trans; uint32_t cache_ext_id; uint32_t cache_reset_id; struct mail_index *backup_index; struct mail_index_view *backup_view; unsigned int cache_used:1; }; typedef unsigned int index_rebuild_generate_uidvalidity_t(struct mailbox_list *list); struct index_rebuild_context * index_index_rebuild_init(struct mailbox *box, struct mail_index_view *view, struct mail_index_transaction *trans); void index_index_rebuild_deinit(struct index_rebuild_context **ctx, index_rebuild_generate_uidvalidity_t *cb); void index_rebuild_index_metadata(struct index_rebuild_context *ctx, uint32_t new_seq, uint32_t uid); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/0002755000175000017500000000000013172375611015614 500000000000000dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-search.c0000644000175000017500000002043113165463624020236 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-arg.h" #include "imap-seqset.h" #include "imap-util.h" #include "mail-search.h" #include "imapc-msgmap.h" #include "imapc-storage.h" #include "imapc-search.h" #define IMAPC_CONTEXT(obj) \ MODULE_CONTEXT(obj, imapc_storage_module) struct imapc_search_context { union mail_search_module_context module_ctx; ARRAY_TYPE(seq_range) rseqs; struct seq_range_iter iter; unsigned int n; bool finished; bool success; }; static MODULE_CONTEXT_DEFINE_INIT(imapc_storage_module, &mail_storage_module_register); static bool imapc_build_search_query_args(struct imapc_mailbox *mbox, const struct mail_search_arg *args, bool parent_or, string_t *str); static bool imapc_search_is_fast_local(const struct mail_search_arg *args) { const struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: if (!imapc_search_is_fast_local(arg->value.subargs)) return FALSE; break; case SEARCH_ALL: case SEARCH_SEQSET: case SEARCH_UIDSET: case SEARCH_FLAGS: case SEARCH_KEYWORDS: case SEARCH_MODSEQ: case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: case SEARCH_REAL_UID: break; default: return FALSE; } } return TRUE; } static bool imapc_build_search_query_arg(struct imapc_mailbox *mbox, const struct mail_search_arg *arg, string_t *str) { struct mail_search_arg arg2 = *arg; const char *error; if (arg->match_not) str_append(str, "NOT "); arg2.match_not = FALSE; arg = &arg2; switch (arg->type) { case SEARCH_OR: imapc_build_search_query_args(mbox, arg->value.subargs, TRUE, str); return TRUE; case SEARCH_SUB: str_append_c(str, '('); imapc_build_search_query_args(mbox, arg->value.subargs, FALSE, str); str_append_c(str, ')'); return TRUE; case SEARCH_SEQSET: /* translate to UIDs */ T_BEGIN { ARRAY_TYPE(seq_range) uids; t_array_init(&uids, 64); mailbox_get_uid_range(&mbox->box, &arg->value.seqset, &uids); str_append(str, "UID "); imap_write_seq_range(str, &uids); } T_END; return TRUE; case SEARCH_BEFORE: case SEARCH_SINCE: if ((mbox->capabilities & IMAPC_CAPABILITY_WITHIN) == 0) { /* a bit kludgy way to check this.. */ size_t pos = str_len(str); if (!mail_search_arg_to_imap(str, arg, &error)) return FALSE; if (strncasecmp(str_c(str) + pos, "OLDER", 5) == 0 || strncasecmp(str_c(str) + pos, "YOUNGER", 7) == 0) return FALSE; return TRUE; } /* fall through */ case SEARCH_ALL: case SEARCH_UIDSET: case SEARCH_FLAGS: case SEARCH_KEYWORDS: case SEARCH_ON: case SEARCH_SMALLER: case SEARCH_LARGER: case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: case SEARCH_BODY: case SEARCH_TEXT: return mail_search_arg_to_imap(str, arg, &error); /* extensions */ case SEARCH_MODSEQ: if ((mbox->capabilities & IMAPC_CAPABILITY_CONDSTORE) == 0) return FALSE; return mail_search_arg_to_imap(str, arg, &error); case SEARCH_INTHREAD: case SEARCH_GUID: case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: case SEARCH_REAL_UID: case SEARCH_MIMEPART: /* not supported for now */ break; } return FALSE; } static bool imapc_build_search_query_args(struct imapc_mailbox *mbox, const struct mail_search_arg *args, bool parent_or, string_t *str) { const struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { if (parent_or && arg->next != NULL) str_append(str, "OR "); if (!imapc_build_search_query_arg(mbox, arg, str)) return FALSE; str_append_c(str, ' '); } str_truncate(str, str_len(str)-1); return TRUE; } static bool imapc_build_search_query(struct imapc_mailbox *mbox, const struct mail_search_args *args, const char **query_r) { string_t *str = t_str_new(128); if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_SEARCH)) { /* SEARCH command passthrough not enabled */ return FALSE; } if (imapc_search_is_fast_local(args->args)) return FALSE; if ((mbox->capabilities & IMAPC_CAPABILITY_ESEARCH) != 0) str_append(str, "SEARCH RETURN (ALL) "); else str_append(str, "UID SEARCH "); if (!imapc_build_search_query_args(mbox, args->args, FALSE, str)) return FALSE; *query_r = str_c(str); return TRUE; } static void imapc_search_callback(const struct imapc_command_reply *reply, void *context) { struct mail_search_context *ctx = context; struct imapc_mailbox *mbox = (struct imapc_mailbox *)ctx->transaction->box; struct imapc_search_context *ictx = IMAPC_CONTEXT(ctx); ictx->finished = TRUE; if (reply->state == IMAPC_COMMAND_STATE_OK) { seq_range_array_iter_init(&ictx->iter, &ictx->rseqs); ictx->success = TRUE; } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { mail_storage_set_internal_error(mbox->box.storage); } else { mail_storage_set_critical(mbox->box.storage, "imapc: Command failed: %s", reply->text_full); } imapc_client_stop(mbox->storage->client->client); } struct mail_search_context * imapc_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)t->box; struct mail_search_context *ctx; struct imapc_search_context *ictx; struct imapc_command *cmd; const char *search_query; ctx = index_storage_search_init(t, args, sort_program, wanted_fields, wanted_headers); if (!imapc_build_search_query(mbox, args, &search_query)) { /* can't optimize this with SEARCH */ return ctx; } ictx = i_new(struct imapc_search_context, 1); i_array_init(&ictx->rseqs, 64); MODULE_CONTEXT_SET(ctx, imapc_storage_module, ictx); cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_search_callback, ctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, search_query); i_assert(mbox->search_ctx == NULL); mbox->search_ctx = ictx; while (!ictx->finished) imapc_client_run(mbox->storage->client->client); mbox->search_ctx = NULL; return ctx; } static void imapc_search_set_matches(struct mail_search_arg *args) { for (; args != NULL; args = args->next) { if (args->type == SEARCH_OR || args->type == SEARCH_SUB) imapc_search_set_matches(args->value.subargs); args->match_always = TRUE; args->result = 1; } } bool imapc_search_next_update_seq(struct mail_search_context *ctx) { struct imapc_search_context *ictx = IMAPC_CONTEXT(ctx); if (ictx == NULL || !ictx->success) return index_storage_search_next_update_seq(ctx); if (!seq_range_array_iter_nth(&ictx->iter, ictx->n++, &ctx->seq)) return FALSE; ctx->progress_cur = ctx->seq; imapc_search_set_matches(ctx->args->args); return TRUE; } int imapc_search_deinit(struct mail_search_context *ctx) { struct imapc_search_context *ictx = IMAPC_CONTEXT(ctx); if (ictx != NULL) { array_free(&ictx->rseqs); i_free(ictx); } return index_storage_search_deinit(ctx); } void imapc_search_reply_search(const struct imap_arg *args, struct imapc_mailbox *mbox) { struct imapc_msgmap *msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); const char *atom; uint32_t uid, rseq; if (mbox->search_ctx == NULL) { i_error("Unexpected SEARCH reply"); return; } /* we're doing UID SEARCH, so need to convert UIDs to sequences */ for (unsigned int i = 0; args[i].type != IMAP_ARG_EOL; i++) { if (!imap_arg_get_atom(&args[i], &atom) || str_to_uint32(atom, &uid) < 0 || uid == 0) { i_error("Invalid SEARCH reply"); break; } if (imapc_msgmap_uid_to_rseq(msgmap, uid, &rseq)) seq_range_array_add(&mbox->search_ctx->rseqs, rseq); } } void imapc_search_reply_esearch(const struct imap_arg *args, struct imapc_mailbox *mbox) { const char *atom; if (mbox->search_ctx == NULL) { i_error("Unexpected ESEARCH reply"); return; } /* It should contain ALL or nonexistent if nothing matched */ if (args[0].type != IMAP_ARG_EOL && (!imap_arg_atom_equals(&args[0], "ALL") || !imap_arg_get_atom(&args[1], &atom) || imap_seq_set_nostar_parse(atom, &mbox->search_ctx->rseqs) < 0)) i_error("Invalid ESEARCH reply"); } dovecot-2.2.33.2/src/lib-storage/index/imapc/Makefile.in0000644000175000017500000005645013172375573017620 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/imapc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_imapc_la_LIBADD = am_libstorage_imapc_la_OBJECTS = imapc-list.lo imapc-mail.lo \ imapc-mail-fetch.lo imapc-mailbox.lo imapc-save.lo \ imapc-search.lo imapc-settings.lo imapc-sync.lo \ imapc-storage.lo libstorage_imapc_la_OBJECTS = $(am_libstorage_imapc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_imapc_la_SOURCES) DIST_SOURCES = $(libstorage_imapc_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_imapc.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/list \ -I$(top_srcdir)/src/lib-storage/index libstorage_imapc_la_SOURCES = \ imapc-list.c \ imapc-mail.c \ imapc-mail-fetch.c \ imapc-mailbox.c \ imapc-save.c \ imapc-search.c \ imapc-settings.c \ imapc-sync.c \ imapc-storage.c headers = \ imapc-list.h \ imapc-mail.h \ imapc-search.h \ imapc-settings.h \ imapc-storage.h \ imapc-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/imapc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/imapc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_imapc.la: $(libstorage_imapc_la_OBJECTS) $(libstorage_imapc_la_DEPENDENCIES) $(EXTRA_libstorage_imapc_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_imapc_la_OBJECTS) $(libstorage_imapc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-mail-fetch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-mailbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-sync.c0000644000175000017500000004713513165463624017757 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "str.h" #include "sort.h" #include "imap-util.h" #include "mail-cache.h" #include "mail-index-modseq.h" #include "index-sync-private.h" #include "imapc-msgmap.h" #include "imapc-list.h" #include "imapc-storage.h" #include "imapc-sync.h" struct imapc_sync_command { struct imapc_sync_context *ctx; char *cmd_str; bool ignore_no; }; static void imapc_sync_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_sync_command *cmd = context; struct imapc_sync_context *ctx = cmd->ctx; i_assert(ctx->sync_command_count > 0); if (reply->state == IMAPC_COMMAND_STATE_OK) ; else if (reply->state == IMAPC_COMMAND_STATE_NO && cmd->ignore_no) { /* maybe the message was expunged already. some servers fail STOREs with NO in such situation. */ } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { /* the disconnection is already logged, don't flood the logs unnecessarily */ mail_storage_set_internal_error(&ctx->mbox->storage->storage); ctx->failed = TRUE; } else { mail_storage_set_critical(&ctx->mbox->storage->storage, "imapc: Sync command '%s' failed: %s", cmd->cmd_str, reply->text_full); ctx->failed = TRUE; } if (--ctx->sync_command_count == 0) imapc_client_stop(ctx->mbox->storage->client->client); i_free(cmd->cmd_str); i_free(cmd); } static struct imapc_command * imapc_sync_cmd_full(struct imapc_sync_context *ctx, const char *cmd_str, bool ignore_no) { struct imapc_sync_command *sync_cmd; struct imapc_command *cmd; sync_cmd = i_new(struct imapc_sync_command, 1); sync_cmd->ctx = ctx; sync_cmd->cmd_str = i_strdup(cmd_str); sync_cmd->ignore_no = ignore_no; ctx->sync_command_count++; cmd = imapc_client_mailbox_cmd(ctx->mbox->client_box, imapc_sync_callback, sync_cmd); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, cmd_str); return cmd; } static struct imapc_command * imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd_str) { return imapc_sync_cmd_full(ctx, cmd_str, FALSE); } static unsigned int imapc_sync_store_hash(const struct imapc_sync_store *store) { return str_hash(store->flags) ^ store->modify_type; } static int imapc_sync_store_cmp(const struct imapc_sync_store *store1, const struct imapc_sync_store *store2) { if (store1->modify_type != store2->modify_type) return 1; return strcmp(store1->flags, store2->flags); } static const char *imapc_sync_flags_sort(const char *flags) { if (strchr(flags, ' ') == NULL) return flags; const char **str = t_strsplit(flags, " "); i_qsort(str, str_array_length(str), sizeof(const char *), i_strcasecmp_p); return t_strarray_join(str, " "); } static void imapc_sync_store_flush(struct imapc_sync_context *ctx) { struct imapc_sync_store *store; const char *sorted_flags; if (ctx->prev_uid1 == 0) return; sorted_flags = imapc_sync_flags_sort(str_c(ctx->prev_flags)); struct imapc_sync_store store_lookup = { .modify_type = ctx->prev_modify_type, .flags = sorted_flags, }; store = hash_table_lookup(ctx->stores, &store_lookup); if (store == NULL) { store = p_new(ctx->pool, struct imapc_sync_store, 1); store->modify_type = ctx->prev_modify_type; store->flags = p_strdup(ctx->pool, sorted_flags); p_array_init(&store->uids, ctx->pool, 4); hash_table_insert(ctx->stores, store, store); } seq_range_array_add_range(&store->uids, ctx->prev_uid1, ctx->prev_uid2); } static void imapc_sync_store(struct imapc_sync_context *ctx, enum modify_type modify_type, uint32_t uid1, uint32_t uid2, const char *flags) { if (ctx->prev_flags == NULL) { ctx->prev_flags = str_new(ctx->pool, 128); hash_table_create(&ctx->stores, ctx->pool, 0, imapc_sync_store_hash, imapc_sync_store_cmp); } if (ctx->prev_uid1 != uid1 || ctx->prev_uid2 != uid2 || ctx->prev_modify_type != modify_type) { imapc_sync_store_flush(ctx); ctx->prev_uid1 = uid1; ctx->prev_uid2 = uid2; ctx->prev_modify_type = modify_type; str_truncate(ctx->prev_flags, 0); } if (str_len(ctx->prev_flags) > 0) str_append_c(ctx->prev_flags, ' '); str_append(ctx->prev_flags, flags); } static void imapc_sync_finish_store(struct imapc_sync_context *ctx) { struct hash_iterate_context *iter; struct imapc_sync_store *store; string_t *cmd = t_str_new(128); imapc_sync_store_flush(ctx); if (!hash_table_is_created(ctx->stores)) return; iter = hash_table_iterate_init(ctx->stores); while (hash_table_iterate(iter, ctx->stores, &store, &store)) { str_truncate(cmd, 0); str_append(cmd, "UID STORE "); imap_write_seq_range(cmd, &store->uids); str_printfa(cmd, " %cFLAGS (%s)", store->modify_type == MODIFY_ADD ? '+' : '-', store->flags); imapc_sync_cmd_full(ctx, str_c(cmd), TRUE); } hash_table_iterate_deinit(&iter); hash_table_destroy(&ctx->stores); } static void imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx, uint32_t seq1, uint32_t seq2) { const struct mail_index_record *rec; uint32_t seq, uid1, uid2; /* if any of them has a missing \Deleted flag, just add it to all of them. */ for (seq = seq1; seq <= seq2; seq++) { rec = mail_index_lookup(ctx->sync_view, seq); if ((rec->flags & MAIL_DELETED) == 0) break; } if (seq <= seq2) { mail_index_lookup_uid(ctx->sync_view, seq1, &uid1); mail_index_lookup_uid(ctx->sync_view, seq2, &uid2); imapc_sync_store(ctx, MODIFY_ADD, uid1, uid2, "\\Deleted"); } } static void imapc_sync_index_flags(struct imapc_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { string_t *str = t_str_new(128); i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS); if (sync_rec->add_flags != 0) { i_assert((sync_rec->add_flags & MAIL_RECENT) == 0); imap_write_flags(str, sync_rec->add_flags, NULL); imapc_sync_store(ctx, MODIFY_ADD, sync_rec->uid1, sync_rec->uid2, str_c(str)); } if (sync_rec->remove_flags != 0) { i_assert((sync_rec->remove_flags & MAIL_RECENT) == 0); str_truncate(str, 0); imap_write_flags(str, sync_rec->remove_flags, NULL); imapc_sync_store(ctx, MODIFY_REMOVE, sync_rec->uid1, sync_rec->uid2, str_c(str)); } } static void imapc_sync_index_keyword(struct imapc_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { const char *const *kw_p; enum modify_type modify_type; switch (sync_rec->type) { case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: modify_type = MODIFY_ADD; break; case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: modify_type = MODIFY_REMOVE; break; default: i_unreached(); } kw_p = array_idx(ctx->keywords, sync_rec->keyword_idx); imapc_sync_store(ctx, modify_type, sync_rec->uid1, sync_rec->uid2, *kw_p); } static void imapc_sync_expunge_finish(struct imapc_sync_context *ctx) { string_t *str; if (array_count(&ctx->expunged_uids) == 0) return; if ((ctx->mbox->capabilities & IMAPC_CAPABILITY_UIDPLUS) == 0) { /* just expunge everything */ imapc_sync_cmd(ctx, "EXPUNGE"); return; } /* build a list of UIDs to expunge */ str = t_str_new(128); str_append(str, "UID EXPUNGE "); imap_write_seq_range(str, &ctx->expunged_uids); imapc_sync_cmd(ctx, str_c(str)); } static void imapc_sync_expunge_eom(struct imapc_sync_context *ctx) { struct imapc_mailbox *mbox = ctx->mbox; uint32_t lseq, uid, msg_count; if (mbox->sync_next_lseq == 0) return; /* if we haven't seen FETCH reply for some messages at the end of mailbox they've been externally expunged. */ msg_count = mail_index_view_get_messages_count(ctx->sync_view); for (lseq = mbox->sync_next_lseq; lseq <= msg_count; lseq++) { mail_index_lookup_uid(ctx->sync_view, lseq, &uid); if (uid >= mbox->sync_uid_next) { /* another process already added new messages to index that our IMAP connection hasn't seen yet */ break; } mail_index_expunge(ctx->trans, lseq); } mbox->sync_next_lseq = 0; mbox->sync_next_rseq = 0; } static void imapc_sync_uid_validity(struct imapc_sync_context *ctx) { struct imapc_mailbox *mbox = ctx->mbox; const struct mail_index_header *hdr; hdr = mail_index_get_header(ctx->sync_view); if (hdr->uid_validity != mbox->sync_uid_validity && mbox->sync_uid_validity != 0) { if (hdr->uid_validity != 0) { /* uidvalidity changed, reset the entire mailbox */ mail_index_reset(ctx->trans); mbox->sync_fetch_first_uid = 1; } mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &mbox->sync_uid_validity, sizeof(mbox->sync_uid_validity), TRUE); } } static void imapc_sync_uid_next(struct imapc_sync_context *ctx) { struct imapc_mailbox *mbox = ctx->mbox; const struct mail_index_header *hdr; uint32_t uid_next = mbox->sync_uid_next; if (uid_next < mbox->min_append_uid) uid_next = mbox->min_append_uid; hdr = mail_index_get_header(ctx->sync_view); if (hdr->next_uid < uid_next) { mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, next_uid), &uid_next, sizeof(uid_next), FALSE); } } static void imapc_sync_highestmodseq(struct imapc_sync_context *ctx) { if (imapc_mailbox_has_modseqs(ctx->mbox) && mail_index_modseq_get_highest(ctx->sync_view) < ctx->mbox->sync_highestmodseq) mail_index_update_highest_modseq(ctx->trans, ctx->mbox->sync_highestmodseq); } static void imapc_initial_sync_check(struct imapc_sync_context *ctx, bool nooped) { struct imapc_msgmap *msgmap = imapc_client_mailbox_get_msgmap(ctx->mbox->client_box); struct mail_index_view *view = ctx->mbox->delayed_sync_view; const struct mail_index_header *hdr = mail_index_get_header(view); uint32_t rseq, lseq, ruid, luid, rcount, lcount; rseq = lseq = 1; rcount = imapc_msgmap_count(msgmap); lcount = mail_index_view_get_messages_count(view); while (rseq <= rcount || lseq <= lcount) { if (rseq <= rcount) ruid = imapc_msgmap_rseq_to_uid(msgmap, rseq); else ruid = (uint32_t)-1; if (lseq <= lcount) mail_index_lookup_uid(view, lseq, &luid); else luid = (uint32_t)-1; if (ruid == luid) { /* message exists in index and in remote server */ lseq++; rseq++; } else if (luid < ruid) { /* message exists in index but not in remote server */ if (luid >= ctx->mbox->sync_uid_next) { /* the message was added to index by another imapc session, and it's not visible yet in this session */ break; } /* it's already expunged and we should have marked it */ i_assert(mail_index_is_expunged(view, lseq) || seq_range_exists(&ctx->mbox->delayed_expunged_uids, luid)); lseq++; } else { /* message doesn't exist in index, but exists in remote server */ if (lseq > lcount && ruid >= hdr->next_uid) { /* the message hasn't been yet added to index */ break; } /* another imapc session expunged it => NOOP should send us an EXPUNGE event */ if (!nooped) { imapc_mailbox_noop(ctx->mbox); imapc_initial_sync_check(ctx, TRUE); return; } /* already nooped => index is corrupted */ imapc_mailbox_set_corrupted(ctx->mbox, "Expunged message uid=%u reappeared", ruid); ctx->failed = TRUE; rseq++; } } } static void imapc_sync_send_commands(struct imapc_sync_context *ctx, uint32_t first_uid) { string_t *cmd = t_str_new(64); if (ctx->mbox->exists_count == 0) { /* empty mailbox - no point in fetching anything */ return; } str_printfa(cmd, "UID FETCH %u:* (FLAGS", first_uid); if (imapc_mailbox_has_modseqs(ctx->mbox)) { str_append(cmd, " MODSEQ"); mail_index_modseq_enable(ctx->mbox->box.index); } if (IMAPC_BOX_HAS_FEATURE(ctx->mbox, IMAPC_FEATURE_GMAIL_MIGRATION)) { enum mailbox_info_flags flags; if (first_uid == 1 && !mail_index_is_in_memory(ctx->mbox->box.index)) { /* these can be efficiently fetched among flags and stored into cache */ str_append(cmd, " X-GM-MSGID"); } /* do this only for the \All mailbox */ if (imapc_list_get_mailbox_flags(ctx->mbox->box.list, ctx->mbox->box.name, &flags) == 0 && (flags & MAILBOX_SPECIALUSE_ALL) != 0) str_append(cmd, " X-GM-LABELS"); } str_append_c(cmd, ')'); imapc_sync_cmd(ctx, str_c(cmd)); if (IMAPC_BOX_HAS_FEATURE(ctx->mbox, IMAPC_FEATURE_GMAIL_MIGRATION) && ctx->mbox->storage->set->pop3_deleted_flag[0] != '\0') { struct imapc_command *cmd; cmd = imapc_sync_cmd(ctx, "SEARCH RETURN (ALL) X-GM-RAW \"in:^pop\""); i_free(ctx->mbox->sync_gmail_pop3_search_tag); ctx->mbox->sync_gmail_pop3_search_tag = i_strdup(imapc_command_get_tag(cmd)); } } static void imapc_sync_index(struct imapc_sync_context *ctx) { struct imapc_mailbox *mbox = ctx->mbox; struct mail_index_sync_rec sync_rec; uint32_t seq1, seq2; i_array_init(&ctx->expunged_uids, 64); ctx->keywords = mail_index_get_keywords(mbox->box.index); ctx->pool = pool_alloconly_create("imapc sync pool", 1024); imapc_sync_uid_validity(ctx); while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) T_BEGIN { if (!mail_index_lookup_seq_range(ctx->sync_view, sync_rec.uid1, sync_rec.uid2, &seq1, &seq2)) { /* already expunged, nothing to do. */ } else switch (sync_rec.type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: imapc_sync_add_missing_deleted_flags(ctx, seq1, seq2); seq_range_array_add_range(&ctx->expunged_uids, sync_rec.uid1, sync_rec.uid2); break; case MAIL_INDEX_SYNC_TYPE_FLAGS: imapc_sync_index_flags(ctx, &sync_rec); break; case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: imapc_sync_index_keyword(ctx, &sync_rec); break; } } T_END; imapc_sync_finish_store(ctx); pool_unref(&ctx->pool); if (!mbox->initial_sync_done) { /* with initial syncing we're fetching all messages' flags and expunge mails from local index that no longer exist on remote server */ i_assert(mbox->sync_fetch_first_uid == 1); mbox->sync_next_lseq = 1; mbox->sync_next_rseq = 1; } if (mbox->sync_fetch_first_uid != 0) { /* we'll resync existing messages' flags and add new messages. adding new messages requires sync locking to avoid duplicates. */ imapc_sync_send_commands(ctx, mbox->sync_fetch_first_uid); } imapc_sync_expunge_finish(ctx); while (ctx->sync_command_count > 0) imapc_mailbox_run(mbox); array_free(&ctx->expunged_uids); /* add uidnext & highestmodseq after all appends */ imapc_sync_uid_next(ctx); imapc_sync_highestmodseq(ctx); if (!ctx->failed) imapc_sync_expunge_eom(ctx); if (mbox->box.v.sync_notify != NULL) mbox->box.v.sync_notify(&mbox->box, 0, 0); if (!ctx->failed) { /* reset only after a successful sync */ mbox->sync_fetch_first_uid = 0; } if (!mbox->initial_sync_done && !ctx->failed) { imapc_initial_sync_check(ctx, FALSE); mbox->initial_sync_done = TRUE; } } void imapc_sync_mailbox_reopened(struct imapc_mailbox *mbox) { struct imapc_sync_context *ctx = mbox->sync_ctx; i_assert(mbox->syncing); if (!mbox->initial_sync_done) { /* the same sync commands are automatically already retried by lib-imap-client. don't duplicate them here. */ return; } /* we got disconnected while syncing. need to re-fetch everything */ mbox->sync_next_lseq = 1; mbox->sync_next_rseq = 1; imapc_sync_send_commands(ctx, 1); } static int imapc_sync_begin(struct imapc_mailbox *mbox, struct imapc_sync_context **ctx_r, bool force) { struct imapc_sync_context *ctx; enum mail_index_sync_flags sync_flags; int ret; i_assert(!mbox->syncing); ctx = i_new(struct imapc_sync_context, 1); ctx->mbox = mbox; sync_flags = index_storage_get_sync_flags(&mbox->box) | MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY; if (!force) sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; ret = mail_index_sync_begin(mbox->box.index, &ctx->index_sync_ctx, &ctx->sync_view, &ctx->trans, sync_flags); if (ret <= 0) { if (ret < 0) mailbox_set_index_error(&mbox->box); i_free(ctx); *ctx_r = NULL; return ret; } i_assert(mbox->delayed_sync_trans == NULL); mbox->sync_view = ctx->sync_view; mbox->delayed_sync_view = mail_index_transaction_open_updated_view(ctx->trans); mbox->delayed_sync_trans = ctx->trans; mbox->delayed_sync_cache_view = mail_cache_view_open(mbox->box.cache, mbox->delayed_sync_view); mbox->delayed_sync_cache_trans = mail_cache_get_transaction(mbox->delayed_sync_cache_view, mbox->delayed_sync_trans); mbox->min_append_uid = mail_index_get_header(ctx->sync_view)->next_uid; mbox->syncing = TRUE; mbox->sync_ctx = ctx; if (!mbox->box.deleting) imapc_sync_index(ctx); mail_index_view_close(&mbox->delayed_sync_view); mbox->delayed_sync_trans = NULL; mbox->sync_view = NULL; *ctx_r = ctx; return 0; } static int imapc_sync_finish(struct imapc_sync_context **_ctx) { struct imapc_sync_context *ctx = *_ctx; bool changes; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; if (ret == 0) { if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { mailbox_set_index_error(&ctx->mbox->box); ret = -1; } } else { mail_index_sync_rollback(&ctx->index_sync_ctx); } if (ctx->mbox->sync_gmail_pop3_search_tag != NULL) { mail_storage_set_critical(&ctx->mbox->storage->storage, "gmail-pop3 search not successful"); i_free_and_null(ctx->mbox->sync_gmail_pop3_search_tag); ret = -1; } mail_cache_view_close(&ctx->mbox->delayed_sync_cache_view); ctx->mbox->delayed_sync_cache_trans = NULL; ctx->mbox->syncing = FALSE; ctx->mbox->sync_ctx = NULL; /* this is done simply to commit delayed expunges if there are any (has to be done after sync is committed) */ if (imapc_mailbox_commit_delayed_trans(ctx->mbox, &changes) < 0) ctx->failed = TRUE; i_free(ctx); return ret; } static int imapc_sync(struct imapc_mailbox *mbox) { struct imapc_sync_context *sync_ctx; bool force = mbox->sync_fetch_first_uid != 0; if ((mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0) { /* we're only saving mails here - no syncing actually wanted */ return 0; } if (imapc_sync_begin(mbox, &sync_ctx, force) < 0) return -1; if (sync_ctx == NULL) return 0; if (imapc_sync_finish(&sync_ctx) < 0) return -1; return 0; } static void imapc_noop_if_needed(struct imapc_mailbox *mbox, enum mailbox_sync_flags flags) { if (!mbox->initial_sync_done) { /* we just SELECTed/EXAMINEd the mailbox, don't do another NOOP. */ } else if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 && ((mbox->capabilities & IMAPC_CAPABILITY_IDLE) == 0 || (flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0)) { /* do NOOP to make sure we have the latest changes before starting sync. this is necessary either because se don't support IDLE at all, or because we want to be sure that we have the latest changes (IDLE is started with a small delay, so we might not actually even be in IDLE right not) */ imapc_mailbox_noop(mbox); } } struct mailbox_sync_context * imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; struct imapc_mailbox_list *list = mbox->storage->client->_list; bool changes; int ret = 0; if (list != NULL) { if (!list->refreshed_mailboxes && list->last_refreshed_mailboxes < ioloop_time) list->refreshed_mailboxes_recently = FALSE; } imapc_noop_if_needed(mbox, flags); if (imapc_mailbox_commit_delayed_trans(mbox, &changes) < 0) ret = -1; if ((changes || mbox->sync_fetch_first_uid != 0 || index_mailbox_want_full_sync(&mbox->box, flags)) && ret == 0) ret = imapc_sync(mbox); return index_mailbox_sync_init(box, flags, ret < 0); } int imapc_mailbox_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)ctx->box; int ret; ret = index_mailbox_sync_deinit(ctx, status_r); ctx = NULL; if (mbox->client_box == NULL) return ret; imapc_client_mailbox_idle(mbox->client_box); return ret; } dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-sync.h0000644000175000017500000000174213165463624017756 00000000000000#ifndef CYDIR_SYNC_H #define CYDIR_SYNC_H struct mailbox; struct mailbox_sync_status; struct imapc_sync_store { enum modify_type modify_type; const char *flags; ARRAY_TYPE(seq_range) uids; }; struct imapc_sync_context { struct imapc_mailbox *mbox; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; const ARRAY_TYPE(keywords) *keywords; ARRAY_TYPE(seq_range) expunged_uids; unsigned int sync_command_count; pool_t pool; HASH_TABLE(struct imapc_sync_store *, struct imapc_sync_store *) stores; uint32_t prev_uid1, prev_uid2; enum modify_type prev_modify_type; string_t *prev_flags; unsigned int failed:1; }; struct mailbox_sync_context * imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); int imapc_mailbox_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r); void imapc_sync_mailbox_reopened(struct imapc_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-mailbox.c0000644000175000017500000004777713123174404020437 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mail-index-modseq.h" #include "imap-arg.h" #include "imap-seqset.h" #include "imap-util.h" #include "imapc-mail.h" #include "imapc-msgmap.h" #include "imapc-search.h" #include "imapc-sync.h" #include "imapc-storage.h" #define NOTIFY_DELAY_MSECS 500 void imapc_mailbox_set_corrupted(struct imapc_mailbox *mbox, const char *reason, ...) { const char *errmsg; va_list va; va_start(va, reason); errmsg = t_strdup_printf("Mailbox '%s' state corrupted: %s", mbox->box.name, t_strdup_vprintf(reason, va)); va_end(va); mail_storage_set_internal_error(&mbox->storage->storage); if (!mbox->initial_sync_done) { /* we failed during initial sync. need to rebuild indexes if we want to get this fixed */ mail_index_mark_corrupted(mbox->box.index); } else { /* maybe the remote server is buggy and has become confused. try reconnecting. */ } imapc_client_mailbox_reconnect(mbox->client_box, errmsg); } static struct mail_index_view * imapc_mailbox_get_sync_view(struct imapc_mailbox *mbox) { if (mbox->sync_view == NULL) mbox->sync_view = mail_index_view_open(mbox->box.index); return mbox->sync_view; } static void imapc_mailbox_init_delayed_trans(struct imapc_mailbox *mbox) { if (mbox->delayed_sync_trans != NULL) return; i_assert(mbox->delayed_sync_cache_view == NULL); i_assert(mbox->delayed_sync_cache_trans == NULL); mbox->delayed_sync_trans = mail_index_transaction_begin(imapc_mailbox_get_sync_view(mbox), MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mbox->delayed_sync_view = mail_index_transaction_open_updated_view(mbox->delayed_sync_trans); mbox->delayed_sync_cache_view = mail_cache_view_open(mbox->box.cache, mbox->delayed_sync_view); mbox->delayed_sync_cache_trans = mail_cache_get_transaction(mbox->delayed_sync_cache_view, mbox->delayed_sync_trans); } static int imapc_mailbox_commit_delayed_expunges(struct imapc_mailbox *mbox) { struct mail_index_view *view = imapc_mailbox_get_sync_view(mbox); struct mail_index_transaction *trans; struct seq_range_iter iter; unsigned int n; uint32_t lseq, uid; int ret; trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); seq_range_array_iter_init(&iter, &mbox->delayed_expunged_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { if (mail_index_lookup_seq(view, uid, &lseq)) mail_index_expunge(trans, lseq); } array_clear(&mbox->delayed_expunged_uids); ret = mail_index_transaction_commit(&trans); if (ret < 0) mailbox_set_index_error(&mbox->box); return ret; } int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox, bool *changes_r) { int ret = 0; *changes_r = FALSE; if (mbox->delayed_sync_view != NULL) mail_index_view_close(&mbox->delayed_sync_view); if (mbox->delayed_sync_trans != NULL) { if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) { mailbox_set_index_error(&mbox->box); ret = -1; } *changes_r = TRUE; } mbox->delayed_sync_cache_trans = NULL; if (mbox->delayed_sync_cache_view != NULL) mail_cache_view_close(&mbox->delayed_sync_cache_view); if (mbox->sync_view != NULL) mail_index_view_close(&mbox->sync_view); if (array_count(&mbox->delayed_expunged_uids) > 0) { /* delayed expunges - commit them now in a separate transaction */ if (imapc_mailbox_commit_delayed_expunges(mbox) < 0) ret = -1; } return ret; } static void imapc_mailbox_idle_timeout(struct imapc_mailbox *mbox) { timeout_remove(&mbox->to_idle_delay); if (mbox->box.notify_callback != NULL) mbox->box.notify_callback(&mbox->box, mbox->box.notify_context); } static void imapc_mailbox_idle_notify(struct imapc_mailbox *mbox) { struct ioloop *old_ioloop = current_ioloop; if (mbox->box.notify_callback != NULL && mbox->to_idle_delay == NULL) { io_loop_set_current(mbox->storage->root_ioloop); mbox->to_idle_delay = timeout_add_short(NOTIFY_DELAY_MSECS, imapc_mailbox_idle_timeout, mbox); io_loop_set_current(old_ioloop); } } static void imapc_untagged_exists(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { struct mail_index_view *view; uint32_t exists_count = reply->num; const struct mail_index_header *hdr; if (mbox == NULL) return; view = mbox->delayed_sync_view; if (view == NULL) view = imapc_mailbox_get_sync_view(mbox); if (mbox->selecting) { /* We don't know the latest flags, refresh them. */ mbox->sync_fetch_first_uid = 1; } else if (mbox->sync_fetch_first_uid != 1) { hdr = mail_index_get_header(view); mbox->sync_fetch_first_uid = hdr->next_uid; } mbox->exists_count = exists_count; mbox->exists_received = TRUE; imapc_mailbox_idle_notify(mbox); } static bool keywords_are_equal(struct mail_keywords *kw, const ARRAY_TYPE(keyword_indexes) *kw_arr) { const unsigned int *kw_idx; unsigned int i, j, count; kw_idx = array_get(kw_arr, &count); if (count != kw->count) return FALSE; /* there are normally only a few keywords, so O(n^2) is fine */ for (i = 0; i < count; i++) { for (j = 0; j < count; j++) { if (kw->idx[i] == kw_idx[j]) break; } if (j == count) return FALSE; } return TRUE; } static int imapc_mailbox_msgmap_update(struct imapc_mailbox *mbox, uint32_t rseq, uint32_t fetch_uid, uint32_t *lseq_r, uint32_t *uid_r) { struct imapc_msgmap *msgmap; uint32_t uid, msg_count, rseq2; *lseq_r = 0; *uid_r = uid = fetch_uid; if (rseq > mbox->exists_count) { /* Receiving a FETCH for a message that EXISTS hasn't announced yet. MS Exchange has a bug where our UID FETCH request sometimes sends replies where sequences are above EXISTS value, but their UIDs are for existing messages. We'll just ignore these replies. */ return 0; } if (rseq < mbox->prev_skipped_rseq && fetch_uid > mbox->prev_skipped_uid) { /* This was the initial attempt at catching the above MS Exchange bug, but the above one appears to catch all these cases. But keep it here just in case. */ imapc_mailbox_set_corrupted(mbox, "FETCH sequence/UID order is mixed " "(seq=%u,%u vs uid=%u,%u)", mbox->prev_skipped_rseq, rseq, mbox->prev_skipped_uid, fetch_uid); return -1; } msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); msg_count = imapc_msgmap_count(msgmap); if (fetch_uid != 0 && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS)) { /* if we know the UID, use own own generated rseq instead of the potentially broken rseq that the server sent. */ uint32_t fixed_rseq; if (imapc_msgmap_uid_to_rseq(msgmap, fetch_uid, &fixed_rseq)) rseq = fixed_rseq; } if (rseq <= msg_count) { uid = imapc_msgmap_rseq_to_uid(msgmap, rseq); if (uid != fetch_uid && fetch_uid != 0) { imapc_mailbox_set_corrupted(mbox, "FETCH UID mismatch (%u != %u)", fetch_uid, uid); return -1; } *uid_r = uid; } else if (fetch_uid == 0 || rseq != msg_count+1) { /* probably a flag update for a message we haven't yet received our initial UID FETCH for. we should get another one. */ if (fetch_uid == 0) return 0; if (imapc_msgmap_uid_to_rseq(msgmap, fetch_uid, &rseq2)) { imapc_mailbox_set_corrupted(mbox, "FETCH returned wrong sequence for UID %u " "(%u != %u)", fetch_uid, rseq, rseq2); return -1; } mbox->prev_skipped_rseq = rseq; mbox->prev_skipped_uid = fetch_uid; } else if (fetch_uid < imapc_msgmap_uidnext(msgmap)) { imapc_mailbox_set_corrupted(mbox, "Expunged message reappeared in session " "(uid=%u < next_uid=%u)", fetch_uid, imapc_msgmap_uidnext(msgmap)); return -1; } else { /* newly seen message */ imapc_msgmap_append(msgmap, rseq, uid); if (uid < mbox->min_append_uid) { /* message is already added to index */ } else if (mbox->syncing) { mail_index_append(mbox->delayed_sync_trans, uid, lseq_r); mbox->min_append_uid = uid + 1; } } return 0; } static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { uint32_t lseq, rseq = reply->num; struct imapc_fetch_request *const *fetch_requestp; struct imapc_mail *const *mailp; const struct imap_arg *list, *flags_list, *modseq_list; const char *atom, *guid = NULL; const struct mail_index_record *rec = NULL; enum mail_flags flags; uint32_t fetch_uid, uid; uint64_t modseq = 0; unsigned int i, j; ARRAY_TYPE(const_string) keywords = ARRAY_INIT; bool seen_flags = FALSE, have_labels = FALSE; if (mbox == NULL || rseq == 0 || !imap_arg_get_list(reply->args, &list)) return; fetch_uid = 0; flags = 0; for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) { if (!imap_arg_get_atom(&list[i], &atom)) return; if (strcasecmp(atom, "UID") == 0) { if (!imap_arg_get_atom(&list[i+1], &atom) || str_to_uint32(atom, &fetch_uid) < 0) return; } else if (strcasecmp(atom, "FLAGS") == 0) { if (!imap_arg_get_list(&list[i+1], &flags_list)) return; t_array_init(&keywords, 8); seen_flags = TRUE; for (j = 0; flags_list[j].type != IMAP_ARG_EOL; j++) { if (!imap_arg_get_atom(&flags_list[j], &atom)) return; if (atom[0] == '\\') flags |= imap_parse_system_flag(atom); else { /* keyword */ array_append(&keywords, &atom, 1); } } } else if (strcasecmp(atom, "MODSEQ") == 0 && imapc_mailbox_has_modseqs(mbox)) { /* (modseq-number) */ if (!imap_arg_get_list(&list[i+1], &modseq_list)) return; if (!imap_arg_get_atom(&modseq_list[0], &atom) || str_to_uint64(atom, &modseq) < 0 || modseq_list[1].type != IMAP_ARG_EOL) return; } else if (strcasecmp(atom, "X-GM-MSGID") == 0 && !mbox->initial_sync_done) { if (imap_arg_get_atom(&list[i+1], &atom)) guid = atom; } else if (strcasecmp(atom, "X-GM-LABELS") == 0 && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GMAIL_MIGRATION)) { if (!imap_arg_get_list(&list[i+1], &flags_list)) return; for (j = 0; flags_list[j].type != IMAP_ARG_EOL; j++) { if (!imap_arg_get_astring(&flags_list[j], &atom)) return; if (strcasecmp(atom, "\\Muted") != 0) have_labels = TRUE; } } } imapc_mailbox_init_delayed_trans(mbox); if (imapc_mailbox_msgmap_update(mbox, rseq, fetch_uid, &lseq, &uid) < 0 || uid == 0) return; if ((flags & MAIL_RECENT) == 0 && mbox->highest_nonrecent_uid < uid) { /* remember for STATUS_FIRST_RECENT_UID */ mbox->highest_nonrecent_uid = uid; } /* FIXME: we should ideally also pass these through so they show up to clients. */ flags &= ~MAIL_RECENT; /* if this is a reply to some FETCH request, update the mail's fields */ array_foreach(&mbox->fetch_requests, fetch_requestp) { array_foreach(&(*fetch_requestp)->mails, mailp) { struct imapc_mail *mail = *mailp; if (mail->imail.mail.mail.uid == uid) imapc_mail_fetch_update(mail, reply, list); } } if (lseq == 0) { if (!mail_index_lookup_seq(mbox->delayed_sync_view, uid, &lseq)) { /* already expunged by another session */ if (rseq == mbox->sync_next_rseq) mbox->sync_next_rseq++; return; } } if (rseq == mbox->sync_next_rseq) { /* we're doing the initial full sync of mails. expunge any mails that no longer exist. */ i_assert(mbox->syncing); while (mbox->sync_next_lseq < lseq) { mail_index_expunge(mbox->delayed_sync_trans, mbox->sync_next_lseq); mbox->sync_next_lseq++; } i_assert(lseq == mbox->sync_next_lseq); mbox->sync_next_rseq++; mbox->sync_next_lseq++; } rec = mail_index_lookup(mbox->delayed_sync_view, lseq); if (seen_flags && rec->flags != flags) { mail_index_update_flags(mbox->delayed_sync_trans, lseq, MODIFY_REPLACE, flags); } if (seen_flags) { ARRAY_TYPE(keyword_indexes) old_kws; struct mail_keywords *kw; t_array_init(&old_kws, 8); mail_index_lookup_keywords(mbox->delayed_sync_view, lseq, &old_kws); if (have_labels) { /* add keyword for mails that have GMail labels. this can be used for "All Mail" mailbox migrations with dsync */ atom = "$GMailHaveLabels"; array_append(&keywords, &atom, 1); } array_append_zero(&keywords); kw = mail_index_keywords_create(mbox->box.index, array_idx(&keywords, 0)); if (!keywords_are_equal(kw, &old_kws)) { mail_index_update_keywords(mbox->delayed_sync_trans, lseq, MODIFY_REPLACE, kw); } mail_index_keywords_unref(&kw); } if (modseq != 0) { if (mail_index_modseq_lookup(mbox->delayed_sync_view, lseq) < modseq) mail_index_update_modseq(mbox->delayed_sync_trans, lseq, modseq); array_idx_set(&mbox->rseq_modseqs, rseq-1, &modseq); } if (guid != NULL) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(&mbox->box); const enum index_cache_field guid_cache_idx = ibox->cache_fields[MAIL_CACHE_GUID].idx; if (mail_cache_field_can_add(mbox->delayed_sync_cache_trans, lseq, guid_cache_idx)) { mail_cache_add(mbox->delayed_sync_cache_trans, lseq, guid_cache_idx, guid, strlen(guid)+1); } } imapc_mailbox_idle_notify(mbox); } static void imapc_untagged_expunge(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { struct imapc_msgmap *msgmap; uint32_t lseq, uid, rseq = reply->num; if (mbox == NULL || rseq == 0) return; mbox->prev_skipped_rseq = 0; mbox->prev_skipped_uid = 0; if (mbox->exists_count == 0) { imapc_mailbox_set_corrupted(mbox, "EXPUNGE received for empty mailbox"); return; } mbox->exists_count--; msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); if (rseq > imapc_msgmap_count(msgmap)) { /* we haven't even seen this message yet */ return; } uid = imapc_msgmap_rseq_to_uid(msgmap, rseq); imapc_msgmap_expunge(msgmap, rseq); if (array_is_created(&mbox->rseq_modseqs)) array_delete(&mbox->rseq_modseqs, rseq-1, 1); imapc_mailbox_init_delayed_trans(mbox); if (mail_index_lookup_seq(mbox->sync_view, uid, &lseq)) mail_index_expunge(mbox->delayed_sync_trans, lseq); else if (mail_index_lookup_seq(mbox->delayed_sync_view, uid, &lseq)) { /* this message exists only in this transaction. lib-index can't currently handle expunging anything except the last appended message in a transaction, and fixing it would be quite a lot of trouble. so instead we'll just delay doing this expunge until after the current transaction has been committed. */ seq_range_array_add(&mbox->delayed_expunged_uids, uid); } else { /* already expunged by another session */ } imapc_mailbox_idle_notify(mbox); } static void imapc_untagged_esearch_gmail_pop3(const struct imap_arg *args, struct imapc_mailbox *mbox) { struct imapc_msgmap *msgmap; const char *atom; struct seq_range_iter iter; ARRAY_TYPE(seq_range) rseqs; unsigned int n; uint32_t rseq, lseq, uid; ARRAY_TYPE(keyword_indexes) keywords; struct mail_keywords *kw; unsigned int pop3_deleted_kw_idx; i_free_and_null(mbox->sync_gmail_pop3_search_tag); /* It should contain ALL or nonexistent if nothing matched */ if (args[0].type == IMAP_ARG_EOL) return; t_array_init(&rseqs, 64); if (!imap_arg_atom_equals(&args[0], "ALL") || !imap_arg_get_atom(&args[1], &atom) || imap_seq_set_nostar_parse(atom, &rseqs) < 0) { i_error("Invalid gmail-pop3 ESEARCH reply"); return; } mail_index_keyword_lookup_or_create(mbox->box.index, mbox->storage->set->pop3_deleted_flag, &pop3_deleted_kw_idx); t_array_init(&keywords, 1); array_append(&keywords, &pop3_deleted_kw_idx, 1); kw = mail_index_keywords_create_from_indexes(mbox->box.index, &keywords); msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); seq_range_array_iter_init(&iter, &rseqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &rseq)) { if (rseq > imapc_msgmap_count(msgmap)) { /* we haven't even seen this message yet */ break; } uid = imapc_msgmap_rseq_to_uid(msgmap, rseq); if (!mail_index_lookup_seq(mbox->delayed_sync_view, uid, &lseq)) continue; /* add the pop3_deleted_flag */ mail_index_update_keywords(mbox->delayed_sync_trans, lseq, MODIFY_ADD, kw); } mail_index_keywords_unref(&kw); } static void imapc_untagged_search(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { if (mbox != NULL) imapc_search_reply_search(reply->args, mbox); } static void imapc_untagged_esearch(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { const struct imap_arg *tag_list; const char *str; if (mbox == NULL || !imap_arg_get_list(reply->args, &tag_list)) return; /* ESEARCH begins with (TAG ) */ if (!imap_arg_atom_equals(&tag_list[0], "TAG") || !imap_arg_get_string(&tag_list[1], &str) || tag_list[2].type != IMAP_ARG_EOL) return; /* for now the only ESEARCH reply that we have is for getting GMail's list of hidden POP3 messages. */ if (mbox->sync_gmail_pop3_search_tag != NULL && strcmp(mbox->sync_gmail_pop3_search_tag, str) == 0) imapc_untagged_esearch_gmail_pop3(reply->args+1, mbox); else imapc_search_reply_esearch(reply->args+1, mbox); } static void imapc_resp_text_uidvalidity(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { uint32_t uid_validity; if (mbox == NULL || str_to_uint32(reply->resp_text_value, &uid_validity) < 0 || uid_validity == 0) return; if (mbox->sync_uid_validity != uid_validity) { mbox->sync_uid_validity = uid_validity; imapc_mail_cache_free(&mbox->prev_mail_cache); } } static void imapc_resp_text_uidnext(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { uint32_t uid_next; if (mbox == NULL || str_to_uint32(reply->resp_text_value, &uid_next) < 0) return; mbox->sync_uid_next = uid_next; } static void imapc_resp_text_highestmodseq(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { uint64_t highestmodseq; if (mbox == NULL || str_to_uint64(reply->resp_text_value, &highestmodseq) < 0) return; mbox->sync_highestmodseq = highestmodseq; } static void imapc_resp_text_permanentflags(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { const struct imap_arg *flags_args, *arg; const char *flag; unsigned int idx; i_assert(reply->args[0].type == IMAP_ARG_ATOM); if (mbox == NULL || !imap_arg_get_list(&reply->args[1], &flags_args)) return; mbox->permanent_flags = 0; mbox->box.disallow_new_keywords = TRUE; for (arg = flags_args; arg->type != IMAP_ARG_EOL; arg++) { if (!imap_arg_get_atom(arg, &flag)) continue; if (strcmp(flag, "\\*") == 0) mbox->box.disallow_new_keywords = FALSE; else if (*flag == '\\') mbox->permanent_flags |= imap_parse_system_flag(flag); else { /* we'll simply make sure that it exists in the index */ mail_index_keyword_lookup_or_create(mbox->box.index, flag, &idx); } } } void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox, const char *key, imapc_mailbox_callback_t *callback) { struct imapc_mailbox_event_callback *cb; cb = array_append_space(&mbox->untagged_callbacks); cb->name = p_strdup(mbox->box.pool, key); cb->callback = callback; } void imapc_mailbox_register_resp_text(struct imapc_mailbox *mbox, const char *key, imapc_mailbox_callback_t *callback) { struct imapc_mailbox_event_callback *cb; cb = array_append_space(&mbox->resp_text_callbacks); cb->name = p_strdup(mbox->box.pool, key); cb->callback = callback; } void imapc_mailbox_register_callbacks(struct imapc_mailbox *mbox) { imapc_mailbox_register_untagged(mbox, "EXISTS", imapc_untagged_exists); imapc_mailbox_register_untagged(mbox, "FETCH", imapc_untagged_fetch); imapc_mailbox_register_untagged(mbox, "EXPUNGE", imapc_untagged_expunge); imapc_mailbox_register_untagged(mbox, "SEARCH", imapc_untagged_search); imapc_mailbox_register_untagged(mbox, "ESEARCH", imapc_untagged_esearch); imapc_mailbox_register_resp_text(mbox, "UIDVALIDITY", imapc_resp_text_uidvalidity); imapc_mailbox_register_resp_text(mbox, "UIDNEXT", imapc_resp_text_uidnext); imapc_mailbox_register_resp_text(mbox, "HIGHESTMODSEQ", imapc_resp_text_highestmodseq); imapc_mailbox_register_resp_text(mbox, "PERMANENTFLAGS", imapc_resp_text_permanentflags); } dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-storage.c0000644000175000017500000011135713165463624020445 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "imap-arg.h" #include "imap-resp-code.h" #include "mailbox-tree.h" #include "imapc-connection.h" #include "imapc-msgmap.h" #include "imapc-mail.h" #include "imapc-list.h" #include "imapc-search.h" #include "imapc-sync.h" #include "imapc-settings.h" #include "imapc-storage.h" #define DNS_CLIENT_SOCKET_NAME "dns-client" struct imapc_open_context { struct imapc_mailbox *mbox; int ret; }; struct imapc_resp_code_map { const char *code; enum mail_error error; }; extern struct mail_storage imapc_storage; extern struct mailbox imapc_mailbox; static struct imapc_resp_code_map imapc_resp_code_map[] = { { IMAP_RESP_CODE_UNAVAILABLE, MAIL_ERROR_TEMP }, { IMAP_RESP_CODE_AUTHFAILED, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_AUTHZFAILED, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_EXPIRED, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_PRIVACYREQUIRED, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_CONTACTADMIN, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_NOPERM, MAIL_ERROR_PERM }, { IMAP_RESP_CODE_INUSE, MAIL_ERROR_INUSE }, { IMAP_RESP_CODE_EXPUNGEISSUED, MAIL_ERROR_EXPUNGED }, { IMAP_RESP_CODE_CORRUPTION, MAIL_ERROR_TEMP }, { IMAP_RESP_CODE_SERVERBUG, MAIL_ERROR_TEMP }, /* { IMAP_RESP_CODE_CLIENTBUG, 0 }, */ { IMAP_RESP_CODE_CANNOT, MAIL_ERROR_NOTPOSSIBLE }, { IMAP_RESP_CODE_LIMIT, MAIL_ERROR_LIMIT }, { IMAP_RESP_CODE_OVERQUOTA, MAIL_ERROR_NOQUOTA }, { IMAP_RESP_CODE_ALREADYEXISTS, MAIL_ERROR_EXISTS }, { IMAP_RESP_CODE_NONEXISTENT, MAIL_ERROR_NOTFOUND } }; static void imapc_untagged_status(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client); static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client); static int imapc_mailbox_run_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); bool imap_resp_text_code_parse(const char *str, enum mail_error *error_r) { unsigned int i; if (str == NULL) return FALSE; for (i = 0; i < N_ELEMENTS(imapc_resp_code_map); i++) { if (strcmp(imapc_resp_code_map[i].code, str) == 0) { *error_r = imapc_resp_code_map[i].error; return TRUE; } } return FALSE; } bool imapc_mailbox_has_modseqs(struct imapc_mailbox *mbox) { return (mbox->capabilities & (IMAPC_CAPABILITY_CONDSTORE | IMAPC_CAPABILITY_QRESYNC)) != 0 && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_MODSEQ); } static struct mail_storage *imapc_storage_alloc(void) { struct imapc_storage *storage; pool_t pool; pool = pool_alloconly_create("imapc storage", 2048); storage = p_new(pool, struct imapc_storage, 1); storage->storage = imapc_storage; storage->storage.pool = pool; storage->root_ioloop = current_ioloop; return &storage->storage; } void imapc_copy_error_from_reply(struct imapc_storage *storage, enum mail_error default_error, const struct imapc_command_reply *reply) { enum mail_error error; if (imap_resp_text_code_parse(reply->resp_text_key, &error)) { mail_storage_set_error(&storage->storage, error, reply->text_without_resp); } else { mail_storage_set_error(&storage->storage, default_error, reply->text_without_resp); } } void imapc_simple_context_init(struct imapc_simple_context *sctx, struct imapc_storage_client *client) { i_zero(sctx); sctx->client = client; sctx->ret = -2; } void imapc_simple_run(struct imapc_simple_context *sctx) { if (imapc_storage_client_handle_auth_failure(sctx->client)) { imapc_client_logout(sctx->client->client); sctx->ret = -1; } while (sctx->ret == -2) imapc_client_run(sctx->client->client); } void imapc_mailbox_run(struct imapc_mailbox *mbox) { imapc_mail_fetch_flush(mbox); imapc_mailbox_run_nofetch(mbox); } void imapc_mailbox_run_nofetch(struct imapc_mailbox *mbox) { do { imapc_client_run(mbox->storage->client->client); } while (mbox->storage->reopen_count > 0); } void imapc_simple_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_simple_context *ctx = context; if (reply->state == IMAPC_COMMAND_STATE_OK) ctx->ret = 0; else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->client->_storage, MAIL_ERROR_PARAMS, reply); ctx->ret = -1; } else if (imapc_storage_client_handle_auth_failure(ctx->client)) { ctx->ret = -1; } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { mail_storage_set_internal_error(&ctx->client->_storage->storage); ctx->ret = -1; } else { mail_storage_set_critical(&ctx->client->_storage->storage, "imapc: Command failed: %s", reply->text_full); ctx->ret = -1; } imapc_client_stop(ctx->client->client); } void imapc_mailbox_noop(struct imapc_mailbox *mbox) { struct imapc_command *cmd; struct imapc_simple_context sctx; if (mbox->client_box == NULL) { /* mailbox opening hasn't finished yet */ return; } imapc_simple_context_init(&sctx, mbox->storage->client); cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_simple_callback, &sctx); imapc_command_send(cmd, "NOOP"); imapc_simple_run(&sctx); } static void imapc_storage_client_untagged_cb(const struct imapc_untagged_reply *reply, void *context) { struct imapc_storage_client *client = context; struct imapc_mailbox *mbox = reply->untagged_box_context; const struct imapc_storage_event_callback *cb; const struct imapc_mailbox_event_callback *mcb; array_foreach(&client->untagged_callbacks, cb) { if (strcasecmp(reply->name, cb->name) == 0) cb->callback(reply, client); } if (mbox == NULL) return; array_foreach(&mbox->untagged_callbacks, mcb) { if (strcasecmp(reply->name, mcb->name) == 0) mcb->callback(reply, mbox); } if (reply->resp_text_key != NULL) { array_foreach(&mbox->resp_text_callbacks, mcb) { if (strcasecmp(reply->resp_text_key, mcb->name) == 0) mcb->callback(reply, mbox); } } } static void imapc_storage_client_login_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_storage_client *client = context; client->auth_returned = TRUE; imapc_client_stop(client->client); if (reply->state == IMAPC_COMMAND_STATE_OK) return; if (client->destroying && reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { /* user's work was finished before imapc login finished - it's not an error */ return; } client->auth_failed_state = reply->state; client->auth_failed_reason = i_strdup(reply->text_full); if (!imapc_storage_client_handle_auth_failure(client)) i_unreached(); } bool imapc_storage_client_handle_auth_failure(struct imapc_storage_client *client) { if (client->auth_failed_state == IMAPC_COMMAND_STATE_OK) return FALSE; /* We need to set the error to either storage or to list, depending on whether the caller is from mail-storage.h API or mailbox-list.h API. We don't know here what the caller is though, so just set the error to both of them. */ if (client->_storage != NULL) { if (client->auth_failed_state == IMAPC_COMMAND_STATE_DISCONNECTED) mail_storage_set_internal_error(&client->_storage->storage); else { mail_storage_set_error(&client->_storage->storage, MAIL_ERROR_PERM, client->auth_failed_reason); } } if (client->_list != NULL) { if (client->auth_failed_state == IMAPC_COMMAND_STATE_DISCONNECTED) mailbox_list_set_internal_error(&client->_list->list); else { mailbox_list_set_error(&client->_list->list, MAIL_ERROR_PERM, client->auth_failed_reason); } } return TRUE; } static void imapc_storage_client_login(struct imapc_storage_client *client, struct mail_user *user, const char *host) { imapc_client_login(client->client); if (!user->namespaces_created) { /* we're still initializing the user. wait for the login to finish, so we can fail the user creation if it fails. */ while (!client->auth_returned) imapc_client_run(client->client); if (imapc_storage_client_handle_auth_failure(client)) { user->error = p_strdup_printf(user->pool, "imapc: Login to %s failed: %s", host, client->auth_failed_reason); } } } int imapc_storage_client_create(struct mail_namespace *ns, const struct imapc_settings *imapc_set, const struct mail_storage_settings *mail_set, struct imapc_storage_client **client_r, const char **error_r) { struct imapc_storage_client *client; struct imapc_client_settings set; string_t *str; i_zero(&set); set.host = imapc_set->imapc_host; if (*set.host == '\0') { *error_r = "missing imapc_host"; return -1; } set.port = imapc_set->imapc_port; if (imapc_set->imapc_user[0] != '\0') set.username = imapc_set->imapc_user; else if (ns->owner != NULL) set.username = ns->owner->username; else set.username = ns->user->username; set.master_user = imapc_set->imapc_master_user; set.password = imapc_set->imapc_password; if (*set.password == '\0') { *error_r = "missing imapc_password"; return -1; } set.sasl_mechanisms = imapc_set->imapc_sasl_mechanisms; set.use_proxyauth = (imapc_set->parsed_features & IMAPC_FEATURE_PROXYAUTH) != 0; set.cmd_timeout_msecs = imapc_set->imapc_cmd_timeout * 1000; set.connect_retry_count = imapc_set->imapc_connection_retry_count; set.connect_retry_interval_msecs = imapc_set->imapc_connection_retry_interval*1000; set.max_idle_time = imapc_set->imapc_max_idle_time; set.max_line_length = imapc_set->imapc_max_line_length; set.dns_client_socket_path = *ns->user->set->base_dir == '\0' ? "" : t_strconcat(ns->user->set->base_dir, "/", DNS_CLIENT_SOCKET_NAME, NULL); set.debug = mail_set->mail_debug; set.rawlog_dir = mail_user_home_expand(ns->user, imapc_set->imapc_rawlog_dir); str = t_str_new(128); mail_user_set_get_temp_prefix(str, ns->user->set); set.temp_path_prefix = str_c(str); set.ssl_ca_dir = mail_set->ssl_client_ca_dir; set.ssl_ca_file = mail_set->ssl_client_ca_file; set.ssl_verify = imapc_set->imapc_ssl_verify; if (strcmp(imapc_set->imapc_ssl, "imaps") == 0) set.ssl_mode = IMAPC_CLIENT_SSL_MODE_IMMEDIATE; else if (strcmp(imapc_set->imapc_ssl, "starttls") == 0) set.ssl_mode = IMAPC_CLIENT_SSL_MODE_STARTTLS; else set.ssl_mode = IMAPC_CLIENT_SSL_MODE_NONE; set.ssl_crypto_device = mail_set->ssl_crypto_device; set.throttle_set.init_msecs = imapc_set->throttle_init_msecs; set.throttle_set.max_msecs = imapc_set->throttle_max_msecs; set.throttle_set.shrink_min_msecs = imapc_set->throttle_shrink_min_msecs; client = i_new(struct imapc_storage_client, 1); client->refcount = 1; i_array_init(&client->untagged_callbacks, 16); client->client = imapc_client_init(&set); imapc_client_register_untagged(client->client, imapc_storage_client_untagged_cb, client); imapc_client_set_login_callback(client->client, imapc_storage_client_login_callback, client); if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 && (imapc_set->parsed_features & IMAPC_FEATURE_DELAY_LOGIN) == 0) { /* start logging in immediately */ imapc_storage_client_login(client, ns->user, set.host); } *client_r = client; return 0; } void imapc_storage_client_unref(struct imapc_storage_client **_client) { struct imapc_storage_client *client = *_client; struct imapc_storage_event_callback *cb; *_client = NULL; i_assert(client->refcount > 0); if (--client->refcount > 0) return; imapc_client_deinit(&client->client); array_foreach_modifiable(&client->untagged_callbacks, cb) i_free(cb->name); array_free(&client->untagged_callbacks); i_free(client->auth_failed_reason); i_free(client); } static int imapc_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct imapc_storage *storage = (struct imapc_storage *)_storage; struct imapc_mailbox_list *imapc_list = NULL; storage->set = mail_namespace_get_driver_settings(ns, _storage); /* serialize all the settings */ _storage->unique_root_dir = p_strdup_printf(_storage->pool, "%s%s://(%s|%s):%s@%s:%u/%s mechs:%s features:%s " "rawlog:%s cmd_timeout:%u maxidle:%u maxline:%"PRIuSIZE_T"u " "pop3delflg:%s root_dir:%s", storage->set->imapc_ssl, storage->set->imapc_ssl_verify ? "(verify)" : "", storage->set->imapc_user, storage->set->imapc_master_user, storage->set->imapc_password, storage->set->imapc_host, storage->set->imapc_port, storage->set->imapc_list_prefix, storage->set->imapc_sasl_mechanisms, storage->set->imapc_features, storage->set->imapc_rawlog_dir, storage->set->imapc_cmd_timeout, storage->set->imapc_max_idle_time, (size_t) storage->set->imapc_max_line_length, storage->set->pop3_deleted_flag, ns->list->set.root_dir); if (strcmp(ns->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) { imapc_list = (struct imapc_mailbox_list *)ns->list; storage->client = imapc_list->client; storage->client->refcount++; } else { if (imapc_storage_client_create(ns, storage->set, _storage->set, &storage->client, error_r) < 0) return -1; } storage->client->_storage = storage; p_array_init(&storage->remote_namespaces, _storage->pool, 4); if (IMAPC_HAS_FEATURE(storage, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) { _storage->nonbody_access_fields |= MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE; } imapc_storage_client_register_untagged(storage->client, "STATUS", imapc_untagged_status); imapc_storage_client_register_untagged(storage->client, "NAMESPACE", imapc_untagged_namespace); return 0; } static void imapc_storage_destroy(struct mail_storage *_storage) { struct imapc_storage *storage = (struct imapc_storage *)_storage; storage->client->destroying = TRUE; /* make sure all pending commands are aborted before anything is deinitialized */ imapc_client_logout(storage->client->client); imapc_storage_client_unref(&storage->client); index_storage_destroy(_storage); } void imapc_storage_client_register_untagged(struct imapc_storage_client *client, const char *name, imapc_storage_callback_t *callback) { struct imapc_storage_event_callback *cb; cb = array_append_space(&client->untagged_callbacks); cb->name = i_strdup(name); cb->callback = callback; } static void imapc_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_IMAPC; set->escape_char = IMAPC_LIST_ESCAPE_CHAR; } static struct mailbox * imapc_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct imapc_mailbox *mbox; pool_t pool; pool = pool_alloconly_create("imapc mailbox", 1024*4); mbox = p_new(pool, struct imapc_mailbox, 1); mbox->box = imapc_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &imapc_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = (struct imapc_storage *)storage; p_array_init(&mbox->untagged_callbacks, pool, 16); p_array_init(&mbox->resp_text_callbacks, pool, 16); p_array_init(&mbox->fetch_requests, pool, 16); p_array_init(&mbox->delayed_expunged_uids, pool, 16); mbox->pending_fetch_cmd = str_new(pool, 128); mbox->prev_mail_cache.fd = -1; imapc_mailbox_register_callbacks(mbox); return &mbox->box; } const char *imapc_mailbox_get_remote_name(struct imapc_mailbox *mbox) { if (strcmp(mbox->box.list->name, MAILBOX_LIST_NAME_IMAPC) != 0) return mbox->box.name; return imapc_list_to_remote((struct imapc_mailbox_list *)mbox->box.list, mbox->box.name); } static int imapc_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { if (strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPC) != 0) { if (box->inbox_any) *existence_r = MAILBOX_EXISTENCE_SELECT; else *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } enum mailbox_info_flags flags; struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)box->list; if (imapc_storage_client_handle_auth_failure(list->client)) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if (imapc_list_get_mailbox_flags(box->list, box->name, &flags) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if ((flags & MAILBOX_NONEXISTENT) != 0) *existence_r = MAILBOX_EXISTENCE_NONE; else if ((flags & MAILBOX_NOSELECT) != 0) *existence_r = MAILBOX_EXISTENCE_NOSELECT; else *existence_r = MAILBOX_EXISTENCE_SELECT; return 0; } static bool imapc_mailbox_want_examine(struct imapc_mailbox *mbox) { if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_EXAMINE)) { /* mainly a Courier-workaround: With POP3-only Maildir that doesn't have UIDVALIDITY set, EXAMINE won't generate a permanent UIDVALIDITY while SELECT will. */ return FALSE; } return (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 && ((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 || (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0); } static bool imapc_mailbox_verify_select(struct imapc_mailbox *mbox, const char **error_r) { if (!mbox->exists_received) *error_r = "EXISTS not received"; else if (mbox->sync_uid_validity == 0) *error_r = "UIDVALIDITY not received"; else return TRUE; return FALSE; } static void imapc_mailbox_reopen_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_mailbox *mbox = context; const char *errmsg; i_assert(mbox->storage->reopen_count > 0); mbox->storage->reopen_count--; mbox->selecting = FALSE; if (reply->state != IMAPC_COMMAND_STATE_OK) errmsg = reply->text_full; else if (imapc_mailbox_verify_select(mbox, &errmsg)) errmsg = NULL; if (errmsg != NULL) { imapc_client_mailbox_reconnect(mbox->client_box, t_strdup_printf("Reopening mailbox '%s' failed: %s", mbox->box.name, errmsg)); } imapc_client_stop(mbox->storage->client->client); } static void imapc_mailbox_reopen(void *context) { struct imapc_mailbox *mbox = context; struct imapc_command *cmd; /* we're reconnecting and need to reopen the mailbox */ mbox->prev_skipped_rseq = 0; mbox->prev_skipped_uid = 0; imapc_msgmap_reset(imapc_client_mailbox_get_msgmap(mbox->client_box)); if (mbox->selecting) { /* We reconnected during the initial SELECT/EXAMINE. It'll be automatically resent by lib-imap-client, so we don't need to send it again here. */ i_assert(!mbox->initial_sync_done); return; } mbox->initial_sync_done = FALSE; mbox->selecting = TRUE; mbox->exists_received = FALSE; cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_mailbox_reopen_callback, mbox); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); if (imapc_mailbox_want_examine(mbox)) { imapc_command_sendf(cmd, "EXAMINE %s", imapc_mailbox_get_remote_name(mbox)); } else { imapc_command_sendf(cmd, "SELECT %s", imapc_mailbox_get_remote_name(mbox)); } mbox->storage->reopen_count++; if (mbox->syncing) imapc_sync_mailbox_reopened(mbox); } static void imapc_mailbox_open_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_open_context *ctx = context; const char *error; ctx->mbox->selecting = FALSE; ctx->mbox->selected = TRUE; if (reply->state == IMAPC_COMMAND_STATE_OK) { if (!imapc_mailbox_verify_select(ctx->mbox, &error)) { mail_storage_set_critical(ctx->mbox->box.storage, "imapc: Opening mailbox '%s' failed: %s", ctx->mbox->box.name, error); ctx->ret = -1; } else { ctx->ret = 0; } } else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->mbox->storage, MAIL_ERROR_NOTFOUND, reply); ctx->ret = -1; } else if (imapc_storage_client_handle_auth_failure(ctx->mbox->storage->client)) { ctx->ret = -1; } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { ctx->ret = -1; mail_storage_set_internal_error(ctx->mbox->box.storage); } else { mail_storage_set_critical(ctx->mbox->box.storage, "imapc: Opening mailbox '%s' failed: %s", ctx->mbox->box.name, reply->text_full); ctx->ret = -1; } imapc_client_stop(ctx->mbox->storage->client->client); } static int imapc_mailbox_get_capabilities(struct imapc_mailbox *mbox) { /* If authentication failed, don't check again. */ if (imapc_storage_client_handle_auth_failure(mbox->storage->client)) return -1; return imapc_client_get_capabilities(mbox->storage->client->client, &mbox->capabilities); } static void imapc_mailbox_get_extensions(struct imapc_mailbox *mbox) { if (mbox->guid_fetch_field_name == NULL) { /* see if we can get message GUIDs somehow */ if ((mbox->capabilities & IMAPC_CAPABILITY_X_GM_EXT_1) != 0) { /* GMail */ mbox->guid_fetch_field_name = "X-GM-MSGID"; } } } int imapc_mailbox_select(struct imapc_mailbox *mbox) { struct imapc_command *cmd; struct imapc_open_context ctx; i_assert(mbox->client_box == NULL); if (imapc_mailbox_get_capabilities(mbox) < 0) return -1; if (imapc_mailbox_has_modseqs(mbox)) { if (!array_is_created(&mbox->rseq_modseqs)) i_array_init(&mbox->rseq_modseqs, 32); else array_clear(&mbox->rseq_modseqs); } mbox->client_box = imapc_client_mailbox_open(mbox->storage->client->client, mbox); imapc_client_mailbox_set_reopen_cb(mbox->client_box, imapc_mailbox_reopen, mbox); imapc_mailbox_get_extensions(mbox); mbox->selecting = TRUE; mbox->exists_received = FALSE; ctx.mbox = mbox; ctx.ret = -2; cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_mailbox_open_callback, &ctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT | IMAPC_COMMAND_FLAG_RETRIABLE); if (imapc_mailbox_want_examine(mbox)) { imapc_command_sendf(cmd, "EXAMINE %s", imapc_mailbox_get_remote_name(mbox)); } else { imapc_command_sendf(cmd, "SELECT %s", imapc_mailbox_get_remote_name(mbox)); } while (ctx.ret == -2) imapc_mailbox_run(mbox); return ctx.ret; } static int imapc_mailbox_open(struct mailbox *box) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; if (index_storage_mailbox_open(box, FALSE) < 0) return -1; if (box->deleting || (box->flags & MAILBOX_FLAG_SAVEONLY) != 0) { /* We don't actually want to SELECT the mailbox. */ return 0; } if (*box->name == '\0' && (box->list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) { /* trying to open INBOX as the namespace prefix. Don't allow this. */ mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox isn't selectable"); mailbox_close(box); return -1; } if (imapc_mailbox_select(mbox) < 0) { mailbox_close(box); return -1; } return 0; } void imapc_mail_cache_free(struct imapc_mail_cache *cache) { if (cache->fd != -1) { if (close(cache->fd) < 0) i_error("close(imapc cached mail) failed: %m"); cache->fd = -1; } if (cache->buf != NULL) buffer_free(&cache->buf); cache->uid = 0; } static void imapc_mailbox_close(struct mailbox *box) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; bool changes; (void)imapc_mailbox_commit_delayed_trans(mbox, &changes); imapc_mail_fetch_flush(mbox); if (mbox->client_box != NULL) imapc_client_mailbox_close(&mbox->client_box); if (array_is_created(&mbox->rseq_modseqs)) array_free(&mbox->rseq_modseqs); if (mbox->sync_view != NULL) mail_index_view_close(&mbox->sync_view); if (mbox->to_idle_delay != NULL) timeout_remove(&mbox->to_idle_delay); if (mbox->to_idle_check != NULL) timeout_remove(&mbox->to_idle_check); imapc_mail_cache_free(&mbox->prev_mail_cache); index_storage_mailbox_close(box); } static int imapc_mailbox_create(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED, bool directory) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; struct imapc_command *cmd; struct imapc_simple_context sctx; const char *name = imapc_mailbox_get_remote_name(mbox); if (!directory) ; else if (strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) { struct imapc_mailbox_list *imapc_list = (struct imapc_mailbox_list *)box->list; name = t_strdup_printf("%s%c", name, imapc_list->root_sep); } else { name = t_strdup_printf("%s%c", name, mailbox_list_get_hierarchy_sep(box->list)); } imapc_simple_context_init(&sctx, mbox->storage->client); cmd = imapc_client_cmd(mbox->storage->client->client, imapc_simple_callback, &sctx); imapc_command_sendf(cmd, "CREATE %s", name); imapc_simple_run(&sctx); return sctx.ret; } static int imapc_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { if (!guid_128_is_empty(update->mailbox_guid) || update->uid_validity != 0 || update->min_next_uid != 0 || update->min_first_recent_uid != 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); } return index_storage_mailbox_update(box, update); } static void imapc_untagged_status(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { struct imapc_storage *storage = client->_storage; struct mailbox_status *status; const struct imap_arg *list; const char *name, *key, *value; uint32_t num; unsigned int i; if (!imap_arg_get_astring(&reply->args[0], &name) || !imap_arg_get_list(&reply->args[1], &list)) return; if (storage->cur_status_box == NULL) return; if (strcmp(storage->cur_status_box->box.name, name) == 0) { /* match */ } else if (strcasecmp(storage->cur_status_box->box.name, "INBOX") == 0 && strcasecmp(name, "INBOX") == 0) { /* case-insensitive INBOX */ } else { return; } status = storage->cur_status; for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) { if (!imap_arg_get_atom(&list[i], &key) || !imap_arg_get_atom(&list[i+1], &value) || str_to_uint32(value, &num) < 0) return; if (strcasecmp(key, "MESSAGES") == 0) status->messages = num; else if (strcasecmp(key, "RECENT") == 0) status->recent = num; else if (strcasecmp(key, "UIDNEXT") == 0) status->uidnext = num; else if (strcasecmp(key, "UIDVALIDITY") == 0) status->uidvalidity = num; else if (strcasecmp(key, "UNSEEN") == 0) status->unseen = num; else if (strcasecmp(key, "HIGHESTMODSEQ") == 0 && imapc_mailbox_has_modseqs(storage->cur_status_box)) status->highest_modseq = num; } } static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { struct imapc_storage *storage = client->_storage; static enum mail_namespace_type ns_types[] = { MAIL_NAMESPACE_TYPE_PRIVATE, MAIL_NAMESPACE_TYPE_SHARED, MAIL_NAMESPACE_TYPE_PUBLIC }; struct imapc_namespace *ns; const struct imap_arg *list, *list2; const char *prefix, *sep; unsigned int i; array_clear(&storage->remote_namespaces); for (i = 0; i < N_ELEMENTS(ns_types); i++) { if (reply->args[i].type == IMAP_ARG_NIL) continue; if (!imap_arg_get_list(&reply->args[i], &list)) break; for (; list->type != IMAP_ARG_EOL; list++) { if (!imap_arg_get_list(list, &list2) || !imap_arg_get_astring(&list2[0], &prefix) || !imap_arg_get_nstring(&list2[1], &sep)) break; ns = array_append_space(&storage->remote_namespaces); ns->prefix = p_strdup(storage->storage.pool, prefix); ns->separator = sep == NULL ? '\0' : sep[0]; ns->type = ns_types[i]; } } } static int imapc_mailbox_get_selected_status(struct imapc_mailbox *mbox, enum mailbox_status_items items, struct mailbox_status *status_r) { int ret = 0; index_storage_get_open_status(&mbox->box, items, status_r); if ((items & STATUS_PERMANENT_FLAGS) != 0) status_r->permanent_flags = mbox->permanent_flags; if ((items & STATUS_FIRST_RECENT_UID) != 0) status_r->first_recent_uid = mbox->highest_nonrecent_uid + 1; if ((items & STATUS_HIGHESTMODSEQ) != 0) { /* FIXME: this doesn't work perfectly. we're now just returning the HIGHESTMODSEQ from the current index, which may or may not be correct. with QRESYNC enabled we could be returning sync_highestmodseq, but that would require implementing VANISHED replies. and without QRESYNC we'd have to issue STATUS (HIGHESTMODSEQ), which isn't efficient since we get here constantly (after every IMAP command). */ } if (imapc_mailbox_has_modseqs(mbox)) { /* even if local indexes are only in memory, we still have modseqs on the IMAP server itself. */ status_r->nonpermanent_modseqs = FALSE; } return ret; } static int imapc_mailbox_delete(struct mailbox *box) { box->delete_skip_empty_check = TRUE; return index_storage_mailbox_delete(box); } static int imapc_mailbox_run_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; struct imapc_command *cmd; struct imapc_simple_context sctx; string_t *str; if (imapc_mailbox_get_capabilities(mbox) < 0) return -1; str = t_str_new(256); if ((items & STATUS_MESSAGES) != 0) str_append(str, " MESSAGES"); if ((items & STATUS_RECENT) != 0) str_append(str, " RECENT"); if ((items & STATUS_UIDNEXT) != 0) str_append(str, " UIDNEXT"); if ((items & STATUS_UIDVALIDITY) != 0) str_append(str, " UIDVALIDITY"); if ((items & STATUS_UNSEEN) != 0) str_append(str, " UNSEEN"); if ((items & STATUS_HIGHESTMODSEQ) != 0 && imapc_mailbox_has_modseqs(mbox)) str_append(str, " HIGHESTMODSEQ"); if (str_len(str) == 0) { /* nothing requested */ return 0; } imapc_simple_context_init(&sctx, mbox->storage->client); mbox->storage->cur_status_box = mbox; mbox->storage->cur_status = status_r; cmd = imapc_client_cmd(mbox->storage->client->client, imapc_simple_callback, &sctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_sendf(cmd, "STATUS %s (%1s)", imapc_mailbox_get_remote_name(mbox), str_c(str)+1); imapc_simple_run(&sctx); mbox->storage->cur_status_box = NULL; mbox->storage->cur_status = NULL; return sctx.ret; } static int imapc_mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; if (mbox->guid_fetch_field_name != NULL || IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED)) status_r->have_guids = TRUE; if (box->opened) { if (imapc_mailbox_get_selected_status(mbox, items, status_r) < 0) { /* can't do anything about this */ } } else if ((items & (STATUS_FIRST_UNSEEN_SEQ | STATUS_KEYWORDS | STATUS_PERMANENT_FLAGS | STATUS_FIRST_RECENT_UID)) != 0) { /* getting these requires opening the mailbox */ if (mailbox_open(box) < 0) return -1; if (imapc_mailbox_get_selected_status(mbox, items, status_r) < 0) return -1; } else { if (imapc_mailbox_run_status(box, items, status_r) < 0) return -1; } if (box->opened && !box->deleting && (items & STATUS_UIDNEXT) != 0 && mbox->sync_uid_next == 0) { /* Courier-workaround, it doesn't send UIDNEXT on SELECT */ if (imapc_mailbox_run_status(box, STATUS_UIDNEXT, status_r) < 0) return -1; } return 0; } static int imapc_mailbox_get_namespaces(struct imapc_mailbox *mbox) { struct imapc_storage *storage = mbox->storage; struct imapc_command *cmd; struct imapc_simple_context sctx; if (storage->namespaces_requested) return 0; if (imapc_mailbox_get_capabilities(mbox) < 0) return -1; if ((mbox->capabilities & IMAPC_CAPABILITY_NAMESPACE) == 0) { /* NAMESPACE capability not supported */ return 0; } imapc_simple_context_init(&sctx, storage->client); cmd = imapc_client_cmd(storage->client->client, imapc_simple_callback, &sctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "NAMESPACE"); imapc_simple_run(&sctx); if (sctx.ret < 0) return -1; storage->namespaces_requested = TRUE; return 0; } static const struct imapc_namespace * imapc_namespace_find_mailbox(struct imapc_storage *storage, const char *name) { const struct imapc_namespace *ns, *best_ns = NULL; size_t best_len = UINT_MAX, len; array_foreach(&storage->remote_namespaces, ns) { len = strlen(ns->prefix); if (strncmp(ns->prefix, name, len) == 0) { if (best_len > len) { best_ns = ns; best_len = len; } } } return best_ns; } static int imapc_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; const struct imapc_namespace *ns; if ((items & MAILBOX_METADATA_GUID) != 0) { /* a bit ugly way to do this, but better than nothing for now. FIXME: if indexes are enabled, keep this there. */ mail_generate_guid_128_hash(box->name, metadata_r->guid); items &= ~MAILBOX_METADATA_GUID; } if ((items & MAILBOX_METADATA_BACKEND_NAMESPACE) != 0) { if (imapc_mailbox_get_namespaces(mbox) < 0) return -1; ns = imapc_namespace_find_mailbox(mbox->storage, box->name); if (ns != NULL) { metadata_r->backend_ns_prefix = ns->prefix; metadata_r->backend_ns_type = ns->type; } items &= ~MAILBOX_METADATA_BACKEND_NAMESPACE; } if (items != 0) { if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; } return 0; } static void imapc_noop_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_storage *storage = context; if (reply->state == IMAPC_COMMAND_STATE_OK) ; else if (reply->state == IMAPC_COMMAND_STATE_NO) imapc_copy_error_from_reply(storage, MAIL_ERROR_PARAMS, reply); else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) mail_storage_set_internal_error(&storage->storage); else { mail_storage_set_critical(&storage->storage, "imapc: NOOP failed: %s", reply->text_full); } } static void imapc_idle_timeout(struct imapc_mailbox *mbox) { struct imapc_command *cmd; cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_noop_callback, mbox->storage); imapc_command_send(cmd, "NOOP"); } static void imapc_idle_noop_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_mailbox *mbox = context; imapc_noop_callback(reply, mbox->box.storage); if (mbox->client_box != NULL) imapc_client_mailbox_idle(mbox->client_box); } static void imapc_notify_changes(struct mailbox *box) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; const struct mail_storage_settings *set = box->storage->set; struct imapc_command *cmd; if (box->notify_callback == NULL) { if (mbox->to_idle_check != NULL) timeout_remove(&mbox->to_idle_check); return; } if ((mbox->capabilities & IMAPC_CAPABILITY_IDLE) != 0) { /* remote server is already in IDLE. but since some servers don't notice changes immediately, we'll force them to check here by sending a NOOP. this helps with clients that break IDLE when clicking "get mail". */ cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_idle_noop_callback, mbox); imapc_command_send(cmd, "NOOP"); } else { /* remote server doesn't support IDLE. check for changes with NOOP every once in a while. */ i_assert(!imapc_client_is_running(mbox->storage->client->client)); mbox->to_idle_check = timeout_add(set->mailbox_idle_check_interval * 1000, imapc_idle_timeout, mbox); } } static bool imapc_is_inconsistent(struct mailbox *box) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; if (box->view != NULL && mail_index_view_is_inconsistent(box->view)) return TRUE; return mbox->client_box == NULL ? FALSE : !imapc_client_mailbox_is_opened(mbox->client_box); } struct mail_storage imapc_storage = { .name = IMAPC_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_NO_ROOT | MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT, .v = { imapc_get_setting_parser_info, imapc_storage_alloc, imapc_storage_create, imapc_storage_destroy, NULL, imapc_storage_get_list_settings, NULL, imapc_mailbox_alloc, NULL, NULL, } }; struct mailbox imapc_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, imapc_mailbox_exists, imapc_mailbox_open, imapc_mailbox_close, index_storage_mailbox_free, imapc_mailbox_create, imapc_mailbox_update, imapc_mailbox_delete, index_storage_mailbox_rename, imapc_mailbox_get_status, imapc_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, NULL, NULL, imapc_mailbox_sync_init, index_mailbox_sync_next, imapc_mailbox_sync_deinit, NULL, imapc_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, imapc_mail_alloc, imapc_search_init, imapc_search_deinit, index_storage_search_next_nonblock, imapc_search_next_update_seq, imapc_save_alloc, imapc_save_begin, imapc_save_continue, imapc_save_finish, imapc_save_cancel, imapc_copy, imapc_transaction_save_commit_pre, imapc_transaction_save_commit_post, imapc_transaction_save_rollback, imapc_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-storage.h0000644000175000017500000001510413165463624020443 00000000000000#ifndef IMAPC_STORAGE_H #define IMAPC_STORAGE_H #include "index-storage.h" #include "imapc-settings.h" #include "imapc-client.h" #define IMAPC_STORAGE_NAME "imapc" #define IMAPC_LIST_ESCAPE_CHAR '%' #define IMAPC_LIST_BROKEN_CHAR '~' struct imap_arg; struct imapc_untagged_reply; struct imapc_command_reply; struct imapc_mailbox; struct imapc_storage_client; typedef void imapc_storage_callback_t(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client); typedef void imapc_mailbox_callback_t(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox); struct imapc_storage_event_callback { char *name; imapc_storage_callback_t *callback; }; struct imapc_mailbox_event_callback { const char *name; imapc_mailbox_callback_t *callback; }; #define IMAPC_HAS_FEATURE(mstorage, feature) \ (((mstorage)->set->parsed_features & feature) != 0) #define IMAPC_BOX_HAS_FEATURE(mbox, feature) \ (((mbox)->storage->set->parsed_features & feature) != 0) struct imapc_namespace { const char *prefix; char separator; enum mail_namespace_type type; }; struct imapc_storage_client { int refcount; /* either one of these may not be available: */ struct imapc_storage *_storage; struct imapc_mailbox_list *_list; struct imapc_client *client; ARRAY(struct imapc_storage_event_callback) untagged_callbacks; /* IMAPC_COMMAND_STATE_OK if no auth failure (yet), otherwise result to the LOGIN/AUTHENTICATE command. */ enum imapc_command_state auth_failed_state; char *auth_failed_reason; /* Authentication reply was received (success or failure) */ bool auth_returned:1; /* Authentication failed */ unsigned int auth_failed:1; unsigned int destroying:1; }; struct imapc_storage { struct mail_storage storage; const struct imapc_settings *set; struct ioloop *root_ioloop; struct imapc_storage_client *client; struct imapc_mailbox *cur_status_box; struct mailbox_status *cur_status; unsigned int reopen_count; ARRAY(struct imapc_namespace) remote_namespaces; unsigned int namespaces_requested:1; }; struct imapc_mail_cache { uint32_t uid; /* either fd != -1 or buf != NULL */ int fd; buffer_t *buf; }; struct imapc_fetch_request { ARRAY(struct imapc_mail *) mails; }; struct imapc_mailbox { struct mailbox box; struct imapc_storage *storage; struct imapc_client_mailbox *client_box; enum imapc_capability capabilities; struct mail_index_transaction *delayed_sync_trans; struct mail_index_view *sync_view, *delayed_sync_view; struct mail_cache_view *delayed_sync_cache_view; struct mail_cache_transaction_ctx *delayed_sync_cache_trans; struct timeout *to_idle_check, *to_idle_delay; ARRAY(struct imapc_fetch_request *) fetch_requests; /* if non-empty, contains the latest FETCH command we're going to be sending soon (but still waiting to see if we can increase its UID range) */ string_t *pending_fetch_cmd; struct imapc_fetch_request *pending_fetch_request; struct timeout *to_pending_fetch_send; ARRAY(struct imapc_mailbox_event_callback) untagged_callbacks; ARRAY(struct imapc_mailbox_event_callback) resp_text_callbacks; enum mail_flags permanent_flags; uint32_t highest_nonrecent_uid; ARRAY(uint64_t) rseq_modseqs; ARRAY_TYPE(seq_range) delayed_expunged_uids; uint32_t sync_uid_validity; uint32_t sync_uid_next; uint64_t sync_highestmodseq; uint32_t sync_fetch_first_uid; uint32_t sync_next_lseq; uint32_t sync_next_rseq; uint32_t exists_count; uint32_t min_append_uid; char *sync_gmail_pop3_search_tag; /* keep the previous fetched message body cached, mainly for partial IMAP fetches */ struct imapc_mail_cache prev_mail_cache; uint32_t prev_skipped_rseq, prev_skipped_uid; struct imapc_sync_context *sync_ctx; const char *guid_fetch_field_name; struct imapc_search_context *search_ctx; unsigned int selecting:1; unsigned int syncing:1; unsigned int initial_sync_done:1; unsigned int selected:1; unsigned int exists_received:1; }; struct imapc_simple_context { struct imapc_storage_client *client; int ret; }; int imapc_storage_client_create(struct mail_namespace *ns, const struct imapc_settings *imapc_set, const struct mail_storage_settings *mail_set, struct imapc_storage_client **client_r, const char **error_r); void imapc_storage_client_unref(struct imapc_storage_client **client); bool imapc_storage_client_handle_auth_failure(struct imapc_storage_client *client); struct mail_save_context * imapc_save_alloc(struct mailbox_transaction_context *_t); int imapc_save_begin(struct mail_save_context *ctx, struct istream *input); int imapc_save_continue(struct mail_save_context *ctx); int imapc_save_finish(struct mail_save_context *ctx); void imapc_save_cancel(struct mail_save_context *ctx); int imapc_copy(struct mail_save_context *ctx, struct mail *mail); int imapc_transaction_save_commit_pre(struct mail_save_context *ctx); void imapc_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void imapc_transaction_save_rollback(struct mail_save_context *ctx); void imapc_mailbox_run(struct imapc_mailbox *mbox); void imapc_mailbox_run_nofetch(struct imapc_mailbox *mbox); void imapc_mail_cache_free(struct imapc_mail_cache *cache); int imapc_mailbox_select(struct imapc_mailbox *mbox); bool imapc_mailbox_has_modseqs(struct imapc_mailbox *mbox); bool imap_resp_text_code_parse(const char *str, enum mail_error *error_r); void imapc_copy_error_from_reply(struct imapc_storage *storage, enum mail_error default_error, const struct imapc_command_reply *reply); void imapc_simple_context_init(struct imapc_simple_context *sctx, struct imapc_storage_client *client); void imapc_simple_run(struct imapc_simple_context *sctx); void imapc_simple_callback(const struct imapc_command_reply *reply, void *context); int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox, bool *changes_r); void imapc_mailbox_noop(struct imapc_mailbox *mbox); void imapc_mailbox_set_corrupted(struct imapc_mailbox *mbox, const char *reason, ...) ATTR_FORMAT(2, 3); const char *imapc_mailbox_get_remote_name(struct imapc_mailbox *mbox); void imapc_storage_client_register_untagged(struct imapc_storage_client *client, const char *name, imapc_storage_callback_t *callback); void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox, const char *name, imapc_mailbox_callback_t *callback); void imapc_mailbox_register_resp_text(struct imapc_mailbox *mbox, const char *key, imapc_mailbox_callback_t *callback); void imapc_mailbox_register_callbacks(struct imapc_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-mail.c0000644000175000017500000004577013165463624017730 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hex-binary.h" #include "sha1.h" #include "istream.h" #include "message-part-data.h" #include "imap-envelope.h" #include "imapc-msgmap.h" #include "imapc-mail.h" #include "imapc-storage.h" static bool imapc_mail_get_cached_guid(struct mail *_mail); struct mail * imapc_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct imapc_mail *mail; pool_t pool; pool = pool_alloconly_create("mail", 2048); mail = p_new(pool, struct imapc_mail, 1); mail->imail.mail.pool = pool; mail->fd = -1; index_mail_init(&mail->imail, t, wanted_fields, wanted_headers); return &mail->imail.mail.mail; } static bool imapc_mail_is_expunged(struct mail *_mail) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct imapc_msgmap *msgmap; uint32_t lseq, rseq; if (!mbox->initial_sync_done) { /* unknown at this point */ return FALSE; } if (mbox->sync_view != NULL) { /* check if another session has already expunged it */ if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq)) return TRUE; } /* check if we've received EXPUNGE for it */ msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) return TRUE; /* we may be running against a server that hasn't bothered sending us an EXPUNGE. see if NOOP sends it. */ imapc_mailbox_noop(mbox); if (!mbox->initial_sync_done) { /* NOOP caused a reconnection and desync */ return FALSE; } return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq); } static int imapc_mail_failed(struct mail *mail, const char *field) { struct imapc_mail *imail = (struct imapc_mail *)mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->box; bool fix_broken_mail = FALSE; if (mail->expunged || imapc_mail_is_expunged(mail)) { mail_set_expunged(mail); } else if (!imapc_client_mailbox_is_opened(mbox->client_box)) { /* we've already logged a disconnection error */ mail_storage_set_internal_error(mail->box->storage); } else { /* By default we'll assume that this is a critical failure, because we don't want to lose any data. We can be here either because it's a temporary failure on the server or it's a permanent failure. Unfortunately we can't know which case it is, so permanent failures need to be worked around by setting imapc_features=fetch-fix-broken-mails. One reason for permanent failures was that earlier Exchange versions failed to return any data for messages in Calendars mailbox. This seems to be fixed in newer versions. */ fix_broken_mail = imail->fetch_ignore_if_missing; mail_storage_set_critical(mail->box->storage, "imapc: Remote server didn't send %s for UID %u in %s%s (FETCH replied: %s)", field, mail->uid, mail->box->vname, fix_broken_mail ? " - treating it as empty" : "", imail->last_fetch_reply); } return fix_broken_mail ? 0 : -1; } static uint64_t imapc_mail_get_modseq(struct mail *_mail) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct imapc_msgmap *msgmap; const uint64_t *modseqs; unsigned int count; uint32_t rseq; if (!imapc_mailbox_has_modseqs(mbox)) return index_mail_get_modseq(_mail); msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); if (imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) { modseqs = array_get(&mbox->rseq_modseqs, &count); if (rseq <= count) return modseqs[rseq-1]; } return 1; /* unknown modseq */ } static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (data->received_date == (time_t)-1) { if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE, NULL) < 0) return -1; if (data->received_date == (time_t)-1) { if (imapc_mail_failed(_mail, "INTERNALDATE") < 0) return -1; /* assume that the server never returns INTERNALDATE for this mail (see BODY[] failure handling) */ data->received_date = 0; } } *date_r = data->received_date; return 0; } static int imapc_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (data->save_date == (time_t)-1) { /* FIXME: we could use a value stored in cache */ return imapc_mail_get_received_date(_mail, date_r); } *date_r = data->save_date; return 0; } static int imapc_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct istream *input; uoff_t old_offset; int ret; if (data->physical_size == (uoff_t)-1) (void)index_mail_get_physical_size(_mail, size_r); if (data->physical_size != (uoff_t)-1) { *size_r = data->physical_size; return 0; } if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE) && data->stream == NULL) { /* trust RFC822.SIZE to be correct */ if (imapc_mail_fetch(_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL) < 0) return -1; if (data->physical_size == (uoff_t)-1) { if (imapc_mail_failed(_mail, "RFC822.SIZE") < 0) return -1; /* assume that the server never returns RFC822.SIZE for this mail (see BODY[] failure handling) */ data->physical_size = 0; } *size_r = data->physical_size; return 0; } old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream(_mail, NULL, NULL, &input) < 0) return -1; i_assert(data->stream != NULL); i_stream_seek(data->stream, old_offset); ret = i_stream_get_size(data->stream, TRUE, &data->physical_size); if (ret <= 0) { i_assert(ret != 0); mail_storage_set_critical(_mail->box->storage, "imapc: stat(%s) failed: %m", i_stream_get_name(data->stream)); return -1; } *size_r = data->physical_size; return 0; } static int imapc_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (imapc_mail_get_physical_size(_mail, size_r) < 0) return -1; data->virtual_size = data->physical_size; return 0; } static int imapc_mail_get_header_stream(struct mail *_mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; enum mail_lookup_abort old_abort = _mail->lookup_abort; int ret; if (mail->imail.data.access_part != 0 || !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) { /* we're going to be reading the header/body anyway */ return index_mail_get_header_stream(_mail, headers, stream_r); } /* see if the wanted headers are already in cache */ _mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; ret = index_mail_get_header_stream(_mail, headers, stream_r); _mail->lookup_abort = old_abort; if (ret == 0) return 0; /* fetch only the wanted headers */ if (imapc_mail_fetch(_mail, 0, headers->name) < 0) return -1; /* the headers should cached now. */ return index_mail_get_header_stream(_mail, headers, stream_r); } static int imapc_mail_get_headers(struct mail *_mail, const char *field, bool decode_to_utf8, const char *const **value_r) { struct mailbox_header_lookup_ctx *headers; const char *header_names[2]; const unsigned char *data; size_t size; struct istream *input; int ret; header_names[0] = field; header_names[1] = NULL; headers = mailbox_header_lookup_init(_mail->box, header_names); ret = mail_get_header_stream(_mail, headers, &input); mailbox_header_lookup_unref(&headers); if (ret < 0) return -1; while (i_stream_read_data(input, &data, &size, 0) > 0) i_stream_skip(input, size); /* the header should cached now. */ return index_mail_get_headers(_mail, field, decode_to_utf8, value_r); } static int imapc_mail_get_first_header(struct mail *_mail, const char *field, bool decode_to_utf8, const char **value_r) { const char *const *values; int ret; ret = imapc_mail_get_headers(_mail, field, decode_to_utf8, &values); if (ret <= 0) return ret; *value_r = values[0]; return 1; } static int imapc_mail_get_stream(struct mail *_mail, bool get_body, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct index_mail_data *data = &mail->imail.data; enum mail_fetch_field fetch_field; if (get_body && !mail->body_fetched && mail->imail.data.stream != NULL) { /* we've fetched the header, but we need the body now too */ index_mail_close_streams(&mail->imail); /* don't re-use any cached header sizes. we may be intentionally downloading the full body because the header wasn't returned correctly (e.g. pop3-migration does this) */ data->hdr_size_set = FALSE; } /* See if we can get it from cache. If the wanted_fields/headers are set properly, this is usually already done by prefetching. */ imapc_mail_try_init_stream_from_cache(mail); if (data->stream == NULL) { if (!data->initialized) { /* coming here from mail_set_seq() */ mail_set_aborted(_mail); return -1; } fetch_field = get_body || (data->access_part & READ_BODY) != 0 ? MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER; if (imapc_mail_fetch(_mail, fetch_field, NULL) < 0) return -1; if (data->stream == NULL) { if (imapc_mail_failed(_mail, "BODY[]")) return -1; i_assert(data->stream == NULL); /* return the broken email as empty */ mail->body_fetched = TRUE; data->stream = i_stream_create_from_data(NULL, 0); imapc_mail_init_stream(mail); } } return index_mail_init_stream(&mail->imail, hdr_size, body_size, stream_r); } bool imapc_mail_has_headers_in_cache(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers) { struct mail *_mail = &mail->mail.mail; unsigned int i; for (i = 0; i < headers->count; i++) { if (mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, headers->idx[i]) <= 0) return FALSE; } return TRUE; } void imapc_mail_update_access_parts(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct index_mail_data *data = &mail->data; struct mailbox_header_lookup_ctx *header_ctx; const char *str; time_t date; uoff_t size; if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0) (void)index_mail_get_received_date(_mail, &date); if ((data->wanted_fields & MAIL_FETCH_SAVE_DATE) != 0) { if (index_mail_get_save_date(_mail, &date) < 0) { (void)index_mail_get_received_date(_mail, &date); data->save_date = data->received_date; } } if ((data->wanted_fields & (MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE)) != 0) { if (index_mail_get_physical_size(_mail, &size) < 0 && !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE)) data->access_part |= READ_HDR | READ_BODY; } if ((data->wanted_fields & MAIL_FETCH_GUID) != 0) (void)imapc_mail_get_cached_guid(_mail); if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0) (void)index_mail_get_cached_body(mail, &str); if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) (void)index_mail_get_cached_bodystructure(mail, &str); if (data->access_part == 0 && data->wanted_headers != NULL && !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) { /* see if all wanted headers exist in cache */ if (!imapc_mail_has_headers_in_cache(mail, data->wanted_headers)) data->access_part |= PARSE_HDR; } if (data->access_part == 0 && (data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0 && !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) { /* the common code already checked this partially, but we need a guaranteed correct answer */ header_ctx = mailbox_header_lookup_init(_mail->box, message_part_envelope_headers); if (!imapc_mail_has_headers_in_cache(mail, header_ctx)) data->access_part |= PARSE_HDR; mailbox_header_lookup_unref(&header_ctx); } } static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) { struct imapc_mail *imail = (struct imapc_mail *)_mail; struct index_mail *mail = &imail->imail; index_mail_set_seq(_mail, seq, saving); /* searching code handles prefetching internally, elsewhere we want to do it immediately */ if (!mail->search_mail && !_mail->saving) (void)imapc_mail_prefetch(_mail); } static void imapc_mail_add_temp_wanted_fields(struct mail *_mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers) { struct index_mail *mail = (struct index_mail *)_mail; index_mail_add_temp_wanted_fields(_mail, fields, headers); if (_mail->seq != 0) imapc_mail_update_access_parts(mail); } static void imapc_mail_close(struct mail *_mail) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct imapc_mail_cache *cache = &mbox->prev_mail_cache; if (mail->fetch_count > 0) { imapc_mail_fetch_flush(mbox); while (mail->fetch_count > 0) imapc_mailbox_run_nofetch(mbox); } index_mail_close(_mail); mail->fetching_headers = NULL; if (mail->body_fetched) { imapc_mail_cache_free(cache); cache->uid = _mail->uid; if (mail->fd != -1) { cache->fd = mail->fd; mail->fd = -1; } else { cache->buf = mail->body; mail->body = NULL; } } if (mail->fd != -1) { if (close(mail->fd) < 0) i_error("close(imapc mail) failed: %m"); mail->fd = -1; } if (mail->body != NULL) buffer_free(&mail->body); mail->header_fetched = FALSE; mail->body_fetched = FALSE; } static int imapc_mail_get_hdr_hash(struct index_mail *imail) { struct istream *input; const unsigned char *data; size_t size; uoff_t old_offset; struct sha1_ctxt sha1_ctx; unsigned char sha1_output[SHA1_RESULTLEN]; const char *sha1_str; sha1_init(&sha1_ctx); old_offset = imail->data.stream == NULL ? 0 : imail->data.stream->v_offset; if (mail_get_hdr_stream(&imail->mail.mail, NULL, &input) < 0) return -1; i_assert(imail->data.stream != NULL); while (i_stream_read_data(input, &data, &size, 0) > 0) { sha1_loop(&sha1_ctx, data, size); i_stream_skip(input, size); } i_stream_seek(imail->data.stream, old_offset); sha1_result(&sha1_ctx, sha1_output); sha1_str = binary_to_hex(sha1_output, sizeof(sha1_output)); imail->data.guid = p_strdup(imail->mail.data_pool, sha1_str); return 0; } static bool imapc_mail_get_cached_guid(struct mail *_mail) { struct index_mail *imail = (struct index_mail *)_mail; const enum index_cache_field cache_idx = imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; string_t *str; if (imail->data.guid != NULL) { if (mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_idx)) { /* GUID was prefetched - add to cache */ index_mail_cache_add_idx(imail, cache_idx, imail->data.guid, strlen(imail->data.guid)+1); } return TRUE; } str = str_new(imail->mail.data_pool, 64); if (mail_cache_lookup_field(_mail->transaction->cache_view, str, imail->mail.mail.seq, cache_idx) > 0) { imail->data.guid = str_c(str); return TRUE; } return FALSE; } static int imapc_mail_get_guid(struct mail *_mail, const char **value_r) { struct index_mail *imail = (struct index_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; const enum index_cache_field cache_idx = imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; if (imapc_mail_get_cached_guid(_mail)) { *value_r = imail->data.guid; return 0; } /* GUID not in cache, fetch it */ if (mbox->guid_fetch_field_name != NULL) { if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID, NULL) < 0) return -1; if (imail->data.guid == NULL) { (void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name); return -1; } } else { /* use hash of message headers as the GUID */ if (imapc_mail_get_hdr_hash(imail) < 0) return -1; } index_mail_cache_add_idx(imail, cache_idx, imail->data.guid, strlen(imail->data.guid)+1); *value_r = imail->data.guid; return 0; } static int imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct index_mail *imail = (struct index_mail *)_mail; uint64_t num; switch (field) { case MAIL_FETCH_GUID: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) && mbox->guid_fetch_field_name == NULL) { /* GUIDs not supported by server */ break; } *value_r = ""; return imapc_mail_get_guid(_mail, value_r); case MAIL_FETCH_UIDL_BACKEND: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GMAIL_MIGRATION)) break; if (imapc_mail_get_guid(_mail, value_r) < 0) return -1; if (str_to_uint64(*value_r, &num) < 0) { mail_storage_set_critical(_mail->box->storage, "X-GM-MSGID not 64bit integer as expected for POP3 UIDL generation: %s", *value_r); return -1; } *value_r = p_strdup_printf(imail->mail.data_pool, "GmailId%llx", (unsigned long long)num); return 0; case MAIL_FETCH_IMAP_BODY: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) break; if (index_mail_get_cached_body(imail, value_r)) return 0; if (imapc_mail_fetch(_mail, field, NULL) < 0) return -1; if (imail->data.body == NULL) { (void)imapc_mail_failed(_mail, "BODY"); return -1; } *value_r = imail->data.body; return 0; case MAIL_FETCH_IMAP_BODYSTRUCTURE: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) break; if (index_mail_get_cached_bodystructure(imail, value_r)) return 0; if (imapc_mail_fetch(_mail, field, NULL) < 0) return -1; if (imail->data.bodystructure == NULL) { (void)imapc_mail_failed(_mail, "BODYSTRUCTURE"); return -1; } *value_r = imail->data.bodystructure; return 0; default: break; } return index_mail_get_special(_mail, field, value_r); } struct mail_vfuncs imapc_mail_vfuncs = { imapc_mail_close, index_mail_free, imapc_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, imapc_mail_prefetch, index_mail_precache, imapc_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, imapc_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, imapc_mail_get_received_date, imapc_mail_get_save_date, imapc_mail_get_virtual_size, imapc_mail_get_physical_size, imapc_mail_get_first_header, imapc_mail_get_headers, imapc_mail_get_header_stream, imapc_mail_get_stream, index_mail_get_binary_stream, imapc_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-save.c0000644000175000017500000003004213165463624017726 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "imap-date.h" #include "imap-util.h" #include "index-mail.h" #include "mail-copy.h" #include "mailbox-list-private.h" #include "imapc-storage.h" #include "imapc-sync.h" #include "imapc-mail.h" struct imapc_save_context { struct mail_save_context ctx; struct imapc_mailbox *mbox; struct mail_index_transaction *trans; int fd; char *temp_path; struct istream *input; uint32_t dest_uid_validity; ARRAY_TYPE(seq_range) dest_saved_uids; unsigned int save_count; unsigned int failed:1; unsigned int finished:1; }; struct imapc_save_cmd_context { struct imapc_save_context *ctx; int ret; }; void imapc_transaction_save_rollback(struct mail_save_context *_ctx); struct mail_save_context * imapc_save_alloc(struct mailbox_transaction_context *t) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)t->box; struct imapc_save_context *ctx; i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (t->save_ctx == NULL) { ctx = i_new(struct imapc_save_context, 1); ctx->ctx.transaction = t; ctx->mbox = mbox; ctx->trans = t->itrans; ctx->fd = -1; t->save_ctx = &ctx->ctx; } return t->save_ctx; } int imapc_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; const char *path; i_assert(ctx->fd == -1); if (imapc_storage_client_handle_auth_failure(ctx->mbox->storage->client)) return -1; ctx->fd = imapc_client_create_temp_fd(ctx->mbox->storage->client->client, &path); if (ctx->fd == -1) { mail_storage_set_critical(storage, "Couldn't create temp file %s", path); ctx->failed = TRUE; return -1; } /* we may not know the size of the input, or be sure that it contains only CRLFs. so we'll always first write the mail to a temp file and upload it from there to remote server. */ ctx->finished = FALSE; ctx->temp_path = i_strdup(path); ctx->input = i_stream_create_crlf(input); _ctx->data.output = o_stream_create_fd_file(ctx->fd, 0, FALSE); o_stream_cork(_ctx->data.output); return 0; } int imapc_save_continue(struct mail_save_context *_ctx) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; if (ctx->failed) return -1; if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "o_stream_send_istream(%s) failed: %m", ctx->temp_path); } ctx->failed = TRUE; return -1; } return 0; } static void imapc_save_appenduid(struct imapc_save_context *ctx, const struct imapc_command_reply *reply, uint32_t *uid_r) { const char *const *args; uint32_t uid_validity, dest_uid; *uid_r = 0; /* */ args = t_strsplit(reply->resp_text_value, " "); if (str_array_length(args) != 2) return; if (str_to_uint32(args[0], &uid_validity) < 0) return; if (ctx->dest_uid_validity == 0) ctx->dest_uid_validity = uid_validity; else if (ctx->dest_uid_validity != uid_validity) return; if (str_to_uint32(args[1], &dest_uid) == 0) { seq_range_array_add_with_init(&ctx->dest_saved_uids, 32, dest_uid); *uid_r = dest_uid; } } static void imapc_save_add_to_index(struct imapc_save_context *ctx, uint32_t uid) { struct mail *_mail = ctx->ctx.dest_mail; struct index_mail *imail = (struct index_mail *)_mail; uint32_t seq; /* we'll temporarily append messages and at commit time expunge them all, since we can't guarantee that no one else has saved messages to remote server during our transaction */ mail_index_append(ctx->trans, uid, &seq); mail_set_seq_saving(_mail, seq); imail->data.no_caching = TRUE; imail->data.forced_no_caching = TRUE; if (ctx->fd != -1) { struct imapc_mail *imapc_mail = (struct imapc_mail *)imail; imail->data.stream = i_stream_create_fd_autoclose(&ctx->fd, 0); imapc_mail->header_fetched = TRUE; imapc_mail->body_fetched = TRUE; imapc_mail_init_stream(imapc_mail); } ctx->save_count++; } static void imapc_save_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_save_cmd_context *ctx = context; uint32_t uid = 0; if (reply->state == IMAPC_COMMAND_STATE_OK) { if (reply->resp_text_key != NULL && strcasecmp(reply->resp_text_key, "APPENDUID") == 0) imapc_save_appenduid(ctx->ctx, reply, &uid); imapc_save_add_to_index(ctx->ctx, uid); ctx->ret = 0; } else if (imapc_storage_client_handle_auth_failure(ctx->ctx->mbox->storage->client)) { ctx->ret = -1; } else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->ctx->mbox->storage, MAIL_ERROR_PARAMS, reply); ctx->ret = -1; } else { mail_storage_set_critical(&ctx->ctx->mbox->storage->storage, "imapc: APPEND failed: %s", reply->text_full); ctx->ret = -1; } imapc_client_stop(ctx->ctx->mbox->storage->client->client); } static void imapc_save_noop_callback(const struct imapc_command_reply *reply ATTR_UNUSED, void *context) { struct imapc_save_cmd_context *ctx = context; /* we don't really care about the reply */ ctx->ret = 0; imapc_client_stop(ctx->ctx->mbox->storage->client->client); } static void imapc_append_keywords(string_t *str, struct mail_keywords *kw) { const ARRAY_TYPE(keywords) *kw_arr; const char *const *kw_p; unsigned int i; kw_arr = mail_index_get_keywords(kw->index); for (i = 0; i < kw->count; i++) { kw_p = array_idx(kw_arr, kw->idx[i]); if (str_len(str) > 1) str_append_c(str, ' '); str_append(str, *kw_p); } } static int imapc_save_append(struct imapc_save_context *ctx) { struct mail_save_context *_ctx = &ctx->ctx; struct mail_save_data *mdata = &_ctx->data; struct imapc_command *cmd; struct imapc_save_cmd_context sctx; struct istream *input; const char *flags = "", *internaldate = ""; if (mdata->flags != 0 || mdata->keywords != NULL) { string_t *str = t_str_new(64); str_append(str, " ("); imap_write_flags(str, mdata->flags & ~MAIL_RECENT, NULL); if (mdata->keywords != NULL) imapc_append_keywords(str, mdata->keywords); str_append_c(str, ')'); flags = str_c(str); } if (mdata->received_date != (time_t)-1) { internaldate = t_strdup_printf(" \"%s\"", imap_to_datetime(mdata->received_date)); } ctx->mbox->exists_received = FALSE; input = i_stream_create_fd(ctx->fd, IO_BLOCK_SIZE, FALSE); sctx.ctx = ctx; sctx.ret = -2; cmd = imapc_client_cmd(ctx->mbox->storage->client->client, imapc_save_callback, &sctx); imapc_command_sendf(cmd, "APPEND %s%1s%1s %p", imapc_mailbox_get_remote_name(ctx->mbox), flags, internaldate, input); i_stream_unref(&input); while (sctx.ret == -2) imapc_mailbox_run(ctx->mbox); if (sctx.ret == 0 && ctx->mbox->selected && !ctx->mbox->exists_received) { /* e.g. Courier doesn't send EXISTS reply before the tagged APPEND reply. That isn't exactly required by the IMAP RFC, but it makes the behavior better. See if NOOP finds the mail. */ sctx.ret = -2; cmd = imapc_client_cmd(ctx->mbox->storage->client->client, imapc_save_noop_callback, &sctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "NOOP"); while (sctx.ret == -2) imapc_mailbox_run(ctx->mbox); } return sctx.ret; } int imapc_save_finish(struct mail_save_context *_ctx) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; ctx->finished = TRUE; if (!ctx->failed) { if (o_stream_nfinish(_ctx->data.output) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %s", ctx->temp_path, o_stream_get_error(_ctx->data.output)); } ctx->failed = TRUE; } } if (!ctx->failed) { if (imapc_save_append(ctx) < 0) ctx->failed = TRUE; } if (_ctx->data.output != NULL) o_stream_unref(&_ctx->data.output); if (ctx->input != NULL) i_stream_unref(&ctx->input); if (ctx->fd != -1) { if (close(ctx->fd) < 0) i_error("close(%s) failed: %m", ctx->temp_path); ctx->fd = -1; } i_free(ctx->temp_path); index_save_context_free(_ctx); return ctx->failed ? -1 : 0; } void imapc_save_cancel(struct mail_save_context *_ctx) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; ctx->failed = TRUE; (void)imapc_save_finish(_ctx); } int imapc_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mail_transaction_commit_changes *changes = _ctx->transaction->changes; uint32_t i, last_seq; i_assert(ctx->finished); /* expunge all added messages from index before commit */ last_seq = mail_index_view_get_messages_count(_ctx->transaction->view); for (i = 0; i < ctx->save_count; i++) mail_index_expunge(ctx->trans, last_seq - i); if (array_is_created(&ctx->dest_saved_uids)) { changes->uid_validity = ctx->dest_uid_validity; array_append_array(&changes->saved_uids, &ctx->dest_saved_uids); } return 0; } void imapc_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result ATTR_UNUSED) { imapc_transaction_save_rollback(_ctx); } void imapc_transaction_save_rollback(struct mail_save_context *_ctx) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; /* FIXME: if we really want to rollback, we should expunge messages we already saved */ if (!ctx->finished) imapc_save_cancel(_ctx); if (array_is_created(&ctx->dest_saved_uids)) array_free(&ctx->dest_saved_uids); i_free(ctx); } static void imapc_save_copyuid(struct imapc_save_context *ctx, const struct imapc_command_reply *reply, uint32_t *uid_r) { const char *const *args; uint32_t uid_validity, dest_uid; *uid_r = 0; /* */ args = t_strsplit(reply->resp_text_value, " "); if (str_array_length(args) != 3) return; if (str_to_uint32(args[0], &uid_validity) < 0) return; if (ctx->dest_uid_validity == 0) ctx->dest_uid_validity = uid_validity; else if (ctx->dest_uid_validity != uid_validity) return; if (str_to_uint32(args[2], &dest_uid) == 0) { seq_range_array_add_with_init(&ctx->dest_saved_uids, 32, dest_uid); *uid_r = dest_uid; } } static void imapc_copy_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_save_cmd_context *ctx = context; uint32_t uid = 0; if (reply->state == IMAPC_COMMAND_STATE_OK) { if (reply->resp_text_key != NULL && strcasecmp(reply->resp_text_key, "COPYUID") == 0) imapc_save_copyuid(ctx->ctx, reply, &uid); imapc_save_add_to_index(ctx->ctx, uid); ctx->ret = 0; } else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->ctx->mbox->storage, MAIL_ERROR_PARAMS, reply); ctx->ret = -1; } else { mail_storage_set_critical(&ctx->ctx->mbox->storage->storage, "imapc: COPY failed: %s", reply->text_full); ctx->ret = -1; } imapc_client_stop(ctx->ctx->mbox->storage->client->client); } int imapc_copy(struct mail_save_context *_ctx, struct mail *mail) { struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; struct imapc_mailbox *src_mbox = (struct imapc_mailbox *)mail->box; struct imapc_command *cmd; struct imapc_save_cmd_context sctx; i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (_t->box->storage == mail->box->storage) { /* same server, we can use COPY for the mail */ sctx.ret = -2; sctx.ctx = ctx; cmd = imapc_client_mailbox_cmd(src_mbox->client_box, imapc_copy_callback, &sctx); imapc_command_sendf(cmd, "UID COPY %u %s", mail->uid, _t->box->name); while (sctx.ret == -2) imapc_mailbox_run(src_mbox); ctx->finished = TRUE; index_save_context_free(_ctx); return sctx.ret; } return mail_storage_copy(_ctx, mail); } dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-settings.c0000644000175000017500000001071413165463624020634 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "imapc-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imapc_settings, name), NULL } static bool imapc_settings_check(void *_set, pool_t pool, const char **error_r); static const struct setting_define imapc_setting_defines[] = { DEF(SET_STR, imapc_host), DEF(SET_IN_PORT, imapc_port), DEF(SET_STR_VARS, imapc_user), DEF(SET_STR_VARS, imapc_master_user), DEF(SET_STR, imapc_password), DEF(SET_STR, imapc_sasl_mechanisms), DEF(SET_ENUM, imapc_ssl), DEF(SET_BOOL, imapc_ssl_verify), DEF(SET_STR, imapc_features), DEF(SET_STR, imapc_rawlog_dir), DEF(SET_STR, imapc_list_prefix), DEF(SET_TIME, imapc_cmd_timeout), DEF(SET_TIME, imapc_max_idle_time), DEF(SET_UINT, imapc_connection_retry_count), DEF(SET_TIME, imapc_connection_retry_interval), DEF(SET_SIZE, imapc_max_line_length), DEF(SET_STR, pop3_deleted_flag), SETTING_DEFINE_LIST_END }; static const struct imapc_settings imapc_default_settings = { .imapc_host = "", .imapc_port = 143, .imapc_user = "", .imapc_master_user = "", .imapc_password = "", .imapc_sasl_mechanisms = "", .imapc_ssl = "no:imaps:starttls", .imapc_ssl_verify = TRUE, .imapc_features = "", .imapc_rawlog_dir = "", .imapc_list_prefix = "", .imapc_cmd_timeout = 5*60, .imapc_max_idle_time = 60*29, .imapc_connection_retry_count = 1, .imapc_connection_retry_interval = 1, .imapc_max_line_length = 0, .pop3_deleted_flag = "" }; static const struct setting_parser_info imapc_setting_parser_info = { .module_name = "imapc", .defines = imapc_setting_defines, .defaults = &imapc_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imapc_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = imapc_settings_check }; const struct setting_parser_info *imapc_get_setting_parser_info(void) { return &imapc_setting_parser_info; } /* */ struct imapc_feature_list { const char *name; enum imapc_features num; }; static const struct imapc_feature_list imapc_feature_list[] = { { "rfc822.size", IMAPC_FEATURE_RFC822_SIZE }, { "guid-forced", IMAPC_FEATURE_GUID_FORCED }, { "fetch-headers", IMAPC_FEATURE_FETCH_HEADERS }, { "gmail-migration", IMAPC_FEATURE_GMAIL_MIGRATION }, { "search", IMAPC_FEATURE_SEARCH }, { "zimbra-workarounds", IMAPC_FEATURE_ZIMBRA_WORKAROUNDS }, { "no-examine", IMAPC_FEATURE_NO_EXAMINE }, { "proxyauth", IMAPC_FEATURE_PROXYAUTH }, { "fetch-msn-workarounds", IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS }, { "fetch-fix-broken-mails", IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS }, { "modseq", IMAPC_FEATURE_MODSEQ }, { "delay-login", IMAPC_FEATURE_DELAY_LOGIN }, { "fetch-bodystructure", IMAPC_FEATURE_FETCH_BODYSTRUCTURE }, { NULL, 0 } }; static int imapc_settings_parse_throttle(struct imapc_settings *set, const char *throttle_str, const char **error_r) { const char *const *tmp; tmp = t_strsplit(throttle_str, ":"); if (str_array_length(tmp) != 3 || str_to_uint(tmp[0], &set->throttle_init_msecs) < 0 || str_to_uint(tmp[1], &set->throttle_max_msecs) < 0 || str_to_uint(tmp[2], &set->throttle_shrink_min_msecs) < 0) { *error_r = "imapc_features: Invalid throttle settings"; return -1; } return 0; } static int imapc_settings_parse_features(struct imapc_settings *set, const char **error_r) { enum imapc_features features = 0; const struct imapc_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->imapc_features, " ,"); for (; *str != NULL; str++) { list = imapc_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (strncasecmp(*str, "throttle:", 9) == 0) { if (imapc_settings_parse_throttle(set, *str + 9, error_r) < 0) return -1; continue; } if (list->name == NULL) { *error_r = t_strdup_printf("imapc_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool imapc_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct imapc_settings *set = _set; if (set->imapc_max_idle_time == 0) { *error_r = "imapc_max_idle_time must not be 0"; return FALSE; } if (imapc_settings_parse_features(set, error_r) < 0) return FALSE; return TRUE; } dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-mail-fetch.c0000644000175000017500000006346113165463624021014 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "ioloop.h" #include "istream.h" #include "istream-concat.h" #include "istream-header-filter.h" #include "message-header-parser.h" #include "imap-arg.h" #include "imap-date.h" #include "imap-quote.h" #include "imap-bodystructure.h" #include "imap-resp-code.h" #include "imapc-mail.h" #include "imapc-storage.h" static void imapc_mail_set_failure(struct imapc_mail *mail, const struct imapc_command_reply *reply) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->imail.mail.mail.box; mail->last_fetch_reply = p_strdup(mail->imail.mail.pool, reply->text_full); switch (reply->state) { case IMAPC_COMMAND_STATE_OK: break; case IMAPC_COMMAND_STATE_NO: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS)) { /* fetch-fix-broken-mails feature disabled - fail any mails with missing replies */ break; } if (reply->resp_text_key != NULL && (strcasecmp(reply->resp_text_key, IMAP_RESP_CODE_SERVERBUG) == 0 || strcasecmp(reply->resp_text_key, IMAP_RESP_CODE_LIMIT) == 0)) { /* this is a temporary error, retrying should work. Yahoo sends * BYE + NO [LIMIT] UID FETCH Rate limit hit. */ } else { /* hopefully this is a permanent failure */ mail->fetch_ignore_if_missing = TRUE; } break; case IMAPC_COMMAND_STATE_BAD: case IMAPC_COMMAND_STATE_DISCONNECTED: case IMAPC_COMMAND_STATE_AUTH_FAILED: mail->fetch_failed = TRUE; break; } } static void imapc_mail_fetch_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_fetch_request *request = context; struct imapc_fetch_request *const *requests; struct imapc_mail *const *mailp; struct imapc_mailbox *mbox = NULL; unsigned int i, count; array_foreach(&request->mails, mailp) { struct imapc_mail *mail = *mailp; i_assert(mail->fetch_count > 0); imapc_mail_set_failure(mail, reply); if (--mail->fetch_count == 0) mail->fetching_fields = 0; pool_unref(&mail->imail.mail.pool); mbox = (struct imapc_mailbox *)mail->imail.mail.mail.box; } i_assert(mbox != NULL); requests = array_get(&mbox->fetch_requests, &count); for (i = 0; i < count; i++) { if (requests[i] == request) { array_delete(&mbox->fetch_requests, i, 1); break; } } i_assert(i < count); array_free(&request->mails); i_free(request); if (reply->state == IMAPC_COMMAND_STATE_OK) ; else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(mbox->storage, MAIL_ERROR_PARAMS, reply); } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { /* The disconnection message was already logged */ mail_storage_set_internal_error(&mbox->storage->storage); } else { mail_storage_set_critical(&mbox->storage->storage, "imapc: Mail FETCH failed: %s", reply->text_full); } imapc_client_stop(mbox->storage->client->client); } static bool headers_have_subset(const char *const *superset, const char *const *subset) { unsigned int i; if (superset == NULL) return FALSE; if (subset != NULL) { for (i = 0; subset[i] != NULL; i++) { if (!str_array_icase_find(superset, subset[i])) return FALSE; } } return TRUE; } static const char *const * headers_merge(pool_t pool, const char *const *h1, const char *const *h2) { ARRAY_TYPE(const_string) headers; const char *value; unsigned int i; p_array_init(&headers, pool, 16); if (h1 != NULL) { for (i = 0; h1[i] != NULL; i++) { value = p_strdup(pool, h1[i]); array_append(&headers, &value, 1); } } if (h2 != NULL) { for (i = 0; h2[i] != NULL; i++) { if (h1 == NULL || !str_array_icase_find(h1, h2[i])) { value = p_strdup(pool, h2[i]); array_append(&headers, &value, 1); } } } array_append_zero(&headers); return array_idx(&headers, 0); } static bool imapc_mail_try_merge_fetch(struct imapc_mailbox *mbox, string_t *str) { const char *s1 = str_c(str); const char *s2 = str_c(mbox->pending_fetch_cmd); const char *p1, *p2; i_assert(strncmp(s1, "UID FETCH ", 10) == 0); i_assert(strncmp(s2, "UID FETCH ", 10) == 0); /* skip over UID range */ p1 = strchr(s1+10, ' '); p2 = strchr(s2+10, ' '); if (null_strcmp(p1, p2) != 0) return FALSE; /* append the new UID to the pending FETCH UID range */ str_truncate(str, p1-s1); str_insert(mbox->pending_fetch_cmd, p2-s2, ","); str_insert(mbox->pending_fetch_cmd, p2-s2+1, str_c(str) + 10); return TRUE; } static void imapc_mail_delayed_send_or_merge(struct imapc_mail *mail, string_t *str) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->imail.mail.mail.box; if (mbox->pending_fetch_request != NULL && !imapc_mail_try_merge_fetch(mbox, str)) { /* send the previous FETCH and create a new one */ imapc_mail_fetch_flush(mbox); } if (mbox->pending_fetch_request == NULL) { mbox->pending_fetch_request = i_new(struct imapc_fetch_request, 1); i_array_init(&mbox->pending_fetch_request->mails, 4); i_assert(mbox->pending_fetch_cmd->used == 0); str_append_str(mbox->pending_fetch_cmd, str); } array_append(&mbox->pending_fetch_request->mails, &mail, 1); if (mbox->to_pending_fetch_send == NULL && array_count(&mbox->pending_fetch_request->mails) > mbox->box.storage->set->mail_prefetch_count) { /* we're now prefetching the maximum number of mails. this most likely means that we need to flush out the command now before sending anything else. delay it a little bit though in case the sending code doesn't actually use mail_prefetch_count and wants to fetch more. note that we don't want to add this timeout too early, because we want to optimize the maximum number of messages placed into a single FETCH. even without timeout the command gets flushed by imapc_mail_fetch() call. */ mbox->to_pending_fetch_send = timeout_add_short(0, imapc_mail_fetch_flush, mbox); } } static int imapc_mail_send_fetch(struct mail *_mail, enum mail_fetch_field fields, const char *const *headers) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct mail_index_view *view; string_t *str; uint32_t seq; unsigned int i; i_assert(headers == NULL || IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)); if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(_mail); return -1; } _mail->mail_stream_opened = TRUE; /* drop any fields that we may already be fetching currently */ fields &= ~mail->fetching_fields; if (headers_have_subset(mail->fetching_headers, headers)) headers = NULL; if (fields == 0 && headers == NULL) return mail->fetch_sent ? 0 : 1; if (!_mail->saving) { /* if we already know that the mail is expunged, don't try to FETCH it */ view = mbox->delayed_sync_view != NULL ? mbox->delayed_sync_view : mbox->box.view; if (!mail_index_lookup_seq(view, _mail->uid, &seq) || mail_index_is_expunged(view, seq)) { mail_set_expunged(_mail); return -1; } } else if (mbox->client_box == NULL) { /* opened as save-only. we'll need to fetch the mail, so actually SELECT/EXAMINE the mailbox */ i_assert(mbox->box.opened); if (imapc_mailbox_select(mbox) < 0) return -1; } if ((fields & MAIL_FETCH_STREAM_BODY) != 0) fields |= MAIL_FETCH_STREAM_HEADER; str = t_str_new(64); str_printfa(str, "UID FETCH %u (", _mail->uid); if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0) str_append(str, "INTERNALDATE "); if ((fields & (MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE)) != 0) str_append(str, "RFC822.SIZE "); if ((fields & MAIL_FETCH_GUID) != 0) { str_append(str, mbox->guid_fetch_field_name); str_append_c(str, ' '); } if ((fields & MAIL_FETCH_IMAP_BODY) != 0) str_append(str, "BODY "); if ((fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) str_append(str, "BODYSTRUCTURE "); if ((fields & MAIL_FETCH_STREAM_BODY) != 0) { if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_ZIMBRA_WORKAROUNDS)) str_append(str, "BODY.PEEK[] "); else { /* BODY.PEEK[] can return different headers than BODY.PEEK[HEADER] (e.g. invalid 8bit chars replaced with '?' in HEADER) - this violates IMAP protocol and messes up dsync since it sometimes fetches the full body and sometimes only the headers. */ str_append(str, "BODY.PEEK[HEADER] BODY.PEEK[TEXT] "); } fields |= MAIL_FETCH_STREAM_HEADER; } else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0) str_append(str, "BODY.PEEK[HEADER] "); else if (headers != NULL) { mail->fetching_headers = headers_merge(mail->imail.mail.data_pool, headers, mail->fetching_headers); str_append(str, "BODY.PEEK[HEADER.FIELDS ("); for (i = 0; mail->fetching_headers[i] != NULL; i++) { if (i > 0) str_append_c(str, ' '); imap_append_astring(str, mail->fetching_headers[i]); } str_append(str, ")] "); mail->header_list_fetched = FALSE; } str_truncate(str, str_len(str)-1); str_append_c(str, ')'); pool_ref(mail->imail.mail.pool); mail->fetching_fields |= fields; mail->fetch_count++; mail->fetch_sent = FALSE; mail->fetch_failed = FALSE; imapc_mail_delayed_send_or_merge(mail, str); return 1; } static void imapc_mail_cache_get(struct imapc_mail *mail, struct imapc_mail_cache *cache) { if (mail->body_fetched) return; if (cache->fd != -1) { mail->fd = cache->fd; mail->imail.data.stream = i_stream_create_fd(mail->fd, 0, FALSE); cache->fd = -1; } else if (cache->buf != NULL) { mail->body = cache->buf; mail->imail.data.stream = i_stream_create_from_data(mail->body->data, mail->body->used); cache->buf = NULL; } else { return; } mail->header_fetched = TRUE; mail->body_fetched = TRUE; imapc_mail_init_stream(mail); } static enum mail_fetch_field imapc_mail_get_wanted_fetch_fields(struct imapc_mail *mail) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->imail.mail.mail.box; struct index_mail_data *data = &mail->imail.data; enum mail_fetch_field fields = 0; if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0 && data->received_date == (time_t)-1) fields |= MAIL_FETCH_RECEIVED_DATE; if ((data->wanted_fields & MAIL_FETCH_SAVE_DATE) != 0 && data->save_date == (time_t)-1 && data->received_date == (time_t)-1) fields |= MAIL_FETCH_RECEIVED_DATE; if ((data->wanted_fields & (MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE)) != 0 && data->physical_size == (uoff_t)-1 && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE)) fields |= MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE; if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 && data->body == NULL && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) fields |= MAIL_FETCH_IMAP_BODY; if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 && data->bodystructure == NULL && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) fields |= MAIL_FETCH_IMAP_BODYSTRUCTURE; if ((data->wanted_fields & MAIL_FETCH_GUID) != 0 && data->guid == NULL && mbox->guid_fetch_field_name != NULL) fields |= MAIL_FETCH_GUID; if (data->stream == NULL && data->access_part != 0) { if ((data->access_part & (READ_BODY | PARSE_BODY)) != 0) fields |= MAIL_FETCH_STREAM_BODY; fields |= MAIL_FETCH_STREAM_HEADER; } return fields; } void imapc_mail_try_init_stream_from_cache(struct imapc_mail *mail) { struct mail *_mail = &mail->imail.mail.mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; if (mbox->prev_mail_cache.uid == _mail->uid) imapc_mail_cache_get(mail, &mbox->prev_mail_cache); } bool imapc_mail_prefetch(struct mail *_mail) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct index_mail_data *data = &mail->imail.data; enum mail_fetch_field fields; const char *const *headers = NULL; /* try to get as much from cache as possible */ imapc_mail_update_access_parts(&mail->imail); /* If mail is already cached we can avoid re-FETCHing the mail. However, don't initialize the stream if we don't actually want to access the mail. */ if (mail->imail.data.access_part != 0) imapc_mail_try_init_stream_from_cache(mail); fields = imapc_mail_get_wanted_fetch_fields(mail); if (data->wanted_headers != NULL && data->stream == NULL && (fields & MAIL_FETCH_STREAM_HEADER) == 0 && !imapc_mail_has_headers_in_cache(&mail->imail, data->wanted_headers)) { /* fetch specific headers */ if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) headers = data->wanted_headers->name; else fields |= MAIL_FETCH_STREAM_HEADER; } if (fields != 0 || headers != NULL) T_BEGIN { if (imapc_mail_send_fetch(_mail, fields, headers) > 0) mail->imail.data.prefetch_sent = TRUE; } T_END; return !mail->imail.data.prefetch_sent; } static bool imapc_mail_have_fields(struct imapc_mail *imail, enum mail_fetch_field fields) { if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0) { if (imail->imail.data.received_date == (time_t)-1) return FALSE; fields &= ~MAIL_FETCH_RECEIVED_DATE; } if ((fields & (MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE)) != 0) { if (imail->imail.data.physical_size == (uoff_t)-1) return FALSE; fields &= ~(MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE); } if ((fields & MAIL_FETCH_GUID) != 0) { if (imail->imail.data.guid == NULL) return FALSE; fields &= ~MAIL_FETCH_GUID; } if ((fields & MAIL_FETCH_IMAP_BODY) != 0) { if (imail->imail.data.body == NULL) return FALSE; fields &= ~MAIL_FETCH_IMAP_BODY; } if ((fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) { if (imail->imail.data.bodystructure == NULL) return FALSE; fields &= ~MAIL_FETCH_IMAP_BODYSTRUCTURE; } if ((fields & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0) { if (imail->imail.data.stream == NULL) return FALSE; fields &= ~(MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY); } i_assert(fields == 0); return TRUE; } int imapc_mail_fetch(struct mail *_mail, enum mail_fetch_field fields, const char *const *headers) { struct imapc_mail *imail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; int ret; if ((fields & MAIL_FETCH_GUID) != 0 && mbox->guid_fetch_field_name == NULL) { mail_storage_set_error(_mail->box->storage, MAIL_ERROR_NOTPOSSIBLE, "Message GUID not available in this server"); return -1; } if (_mail->saving) { mail_storage_set_error(_mail->box->storage, MAIL_ERROR_NOTPOSSIBLE, "Attempting to issue FETCH for a mail not yet committed"); return -1; } fields |= imapc_mail_get_wanted_fetch_fields(imail); T_BEGIN { ret = imapc_mail_send_fetch(_mail, fields, headers); } T_END; if (ret < 0) return -1; /* we'll continue waiting until we've got all the fields we wanted, or until all FETCH replies have been received (i.e. some FETCHes failed) */ if (ret > 0) imapc_mail_fetch_flush(mbox); while (imail->fetch_count > 0 && (!imapc_mail_have_fields(imail, fields) || !imail->header_list_fetched)) { imapc_mailbox_run_nofetch(mbox); } if (imail->fetch_failed) { mail_storage_set_internal_error(&mbox->storage->storage); return -1; } return 0; } void imapc_mail_fetch_flush(struct imapc_mailbox *mbox) { struct imapc_command *cmd; struct imapc_mail *const *mailp; if (mbox->pending_fetch_request == NULL) { i_assert(mbox->to_pending_fetch_send == NULL); return; } array_foreach(&mbox->pending_fetch_request->mails, mailp) (*mailp)->fetch_sent = TRUE; cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_mail_fetch_callback, mbox->pending_fetch_request); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); array_append(&mbox->fetch_requests, &mbox->pending_fetch_request, 1); imapc_command_send(cmd, str_c(mbox->pending_fetch_cmd)); mbox->pending_fetch_request = NULL; if (mbox->to_pending_fetch_send != NULL) timeout_remove(&mbox->to_pending_fetch_send); str_truncate(mbox->pending_fetch_cmd, 0); } static bool imapc_find_lfile_arg(const struct imapc_untagged_reply *reply, const struct imap_arg *arg, int *fd_r) { const struct imap_arg *list; unsigned int i, count; for (i = 0; i < reply->file_args_count; i++) { const struct imapc_arg_file *farg = &reply->file_args[i]; if (farg->parent_arg == arg->parent && imap_arg_get_list_full(arg->parent, &list, &count) && farg->list_idx < count && &list[farg->list_idx] == arg) { *fd_r = farg->fd; return TRUE; } } return FALSE; } static void imapc_stream_filter(struct istream **input) { static const char *imapc_hide_headers[] = { /* Added by MS Exchange 2010 when \Flagged flag is set. This violates IMAP guarantee of messages being immutable. */ "X-Message-Flag" }; struct istream *filter_input; filter_input = i_stream_create_header_filter(*input, HEADER_FILTER_EXCLUDE, imapc_hide_headers, N_ELEMENTS(imapc_hide_headers), *null_header_filter_callback, (void *)NULL); i_stream_unref(input); *input = filter_input; } void imapc_mail_init_stream(struct imapc_mail *mail) { struct index_mail *imail = &mail->imail; struct mail *_mail = &imail->mail.mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct istream *input; uoff_t size; int ret; i_stream_set_name(imail->data.stream, t_strdup_printf("imapc mail uid=%u", _mail->uid)); index_mail_set_read_buffer_size(_mail, imail->data.stream); if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE)) { /* enable filtering only when we're not passing through RFC822.SIZE. otherwise we'll get size mismatches. */ imapc_stream_filter(&imail->data.stream); } if (imail->mail.v.istream_opened != NULL) { if (imail->mail.v.istream_opened(_mail, &imail->data.stream) < 0) { index_mail_close_streams(imail); return; } } else if (mail->body_fetched) { ret = i_stream_get_size(imail->data.stream, TRUE, &size); if (ret < 0) { index_mail_close_streams(imail); return; } i_assert(ret != 0); imail->data.physical_size = size; /* we'll assume that the remote server is working properly and sending CRLF linefeeds */ imail->data.virtual_size = size; } imail->data.stream_has_only_header = !mail->body_fetched; if (index_mail_init_stream(imail, NULL, NULL, &input) < 0) index_mail_close_streams(imail); } static void imapc_fetch_stream(struct imapc_mail *mail, const struct imapc_untagged_reply *reply, const struct imap_arg *arg, bool have_header, bool have_body) { struct index_mail *imail = &mail->imail; struct istream *hdr_stream = NULL; const char *value; int fd; if (imail->data.stream != NULL) { i_assert(mail->header_fetched); if (mail->body_fetched || !have_body) return; if (have_header) { /* replace the existing stream */ } else if (mail->fd == -1) { /* append this body stream to the existing header stream */ hdr_stream = imail->data.stream; i_stream_ref(hdr_stream); } else { /* append this body stream to the existing header stream. we'll need to recreate the stream with autoclosed fd. */ if (lseek(mail->fd, 0, SEEK_SET) < 0) i_error("lseek(imapc) failed: %m"); hdr_stream = i_stream_create_fd_autoclose(&mail->fd, 0); } index_mail_close_streams(imail); if (mail->fd != -1) { if (close(mail->fd) < 0) i_error("close(imapc mail) failed: %m"); mail->fd = -1; } } else { if (!have_header) { /* BODY.PEEK[TEXT] received - we can't currently handle this before receiving BODY.PEEK[HEADER] reply */ return; } } if (arg->type == IMAP_ARG_LITERAL_SIZE) { if (!imapc_find_lfile_arg(reply, arg, &fd)) { if (hdr_stream != NULL) i_stream_unref(&hdr_stream); return; } if ((fd = dup(fd)) == -1) { i_error("dup() failed: %m"); if (hdr_stream != NULL) i_stream_unref(&hdr_stream); return; } mail->fd = fd; imail->data.stream = i_stream_create_fd(fd, 0, FALSE); } else { if (!imap_arg_get_nstring(arg, &value)) value = NULL; if (value == NULL) { mail_set_expunged(&imail->mail.mail); if (hdr_stream != NULL) i_stream_unref(&hdr_stream); return; } if (mail->body == NULL) { mail->body = buffer_create_dynamic(default_pool, arg->str_len + 1); } else if (!have_header && hdr_stream != NULL) { /* header is already in the buffer - add body now without destroying the existing header data */ i_stream_unref(&hdr_stream); } else { buffer_set_used_size(mail->body, 0); } buffer_append(mail->body, value, arg->str_len); imail->data.stream = i_stream_create_from_data(mail->body->data, mail->body->used); } if (have_header) mail->header_fetched = TRUE; mail->body_fetched = have_body; if (hdr_stream != NULL) { struct istream *inputs[3]; inputs[0] = hdr_stream; inputs[1] = imail->data.stream; inputs[2] = NULL; imail->data.stream = i_stream_create_concat(inputs); i_stream_unref(&inputs[0]); i_stream_unref(&inputs[1]); } imapc_mail_init_stream(mail); } static void imapc_fetch_header_stream(struct imapc_mail *mail, const struct imapc_untagged_reply *reply, const struct imap_arg *args) { const enum message_header_parser_flags hdr_parser_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR; const struct imap_arg *hdr_list; struct mailbox_header_lookup_ctx *headers_ctx; struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct istream *input; ARRAY_TYPE(const_string) hdr_arr; const char *value; int ret, fd; if (!imap_arg_get_list(args, &hdr_list)) return; if (!imap_arg_atom_equals(args+1, "]")) return; args += 2; /* see if this is reply to the latest headers list request (parse it even if it's not) */ t_array_init(&hdr_arr, 16); while (imap_arg_get_astring(hdr_list, &value)) { array_append(&hdr_arr, &value, 1); hdr_list++; } if (hdr_list->type != IMAP_ARG_EOL) return; array_append_zero(&hdr_arr); if (headers_have_subset(array_idx(&hdr_arr, 0), mail->fetching_headers)) mail->header_list_fetched = TRUE; if (args->type == IMAP_ARG_LITERAL_SIZE) { if (!imapc_find_lfile_arg(reply, args, &fd)) return; input = i_stream_create_fd(fd, 0, FALSE); } else { if (!imap_arg_get_nstring(args, &value)) return; if (value == NULL) { mail_set_expunged(&mail->imail.mail.mail); return; } input = i_stream_create_from_data(value, args->str_len); } headers_ctx = mailbox_header_lookup_init(mail->imail.mail.mail.box, array_idx(&hdr_arr, 0)); index_mail_parse_header_init(&mail->imail, headers_ctx); parser = message_parse_header_init(input, NULL, hdr_parser_flags); while ((ret = message_parse_header_next(parser, &hdr)) > 0) index_mail_parse_header(NULL, hdr, &mail->imail); i_assert(ret != 0); index_mail_parse_header(NULL, NULL, &mail->imail); message_parse_header_deinit(&parser); mailbox_header_lookup_unref(&headers_ctx); i_stream_destroy(&input); } static const char * imapc_args_to_bodystructure(struct imapc_mail *mail, const struct imap_arg *list_arg, bool extended) { const struct imap_arg *args; struct message_part *parts = NULL; const char *ret, *error; pool_t pool; if (!imap_arg_get_list(list_arg, &args)) { mail_storage_set_critical(mail->imail.mail.mail.box->storage, "imapc: Server sent invalid BODYSTRUCTURE parameters"); return NULL; } pool = pool_alloconly_create("imap bodystructure", 1024); if (imap_bodystructure_parse_args(args, pool, &parts, &error) < 0) { mail_storage_set_critical(mail->imail.mail.mail.box->storage, "imapc: Server sent invalid BODYSTRUCTURE: %s", error); ret = NULL; } else { string_t *str = t_str_new(128); imap_bodystructure_write(parts, str, extended); ret = p_strdup(mail->imail.mail.data_pool, str_c(str)); } pool_unref(&pool); return ret; } void imapc_mail_fetch_update(struct imapc_mail *mail, const struct imapc_untagged_reply *reply, const struct imap_arg *args) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->imail.mail.mail.box; const char *key, *value; unsigned int i; uoff_t size; time_t t; int tz; bool match = FALSE; for (i = 0; args[i].type != IMAP_ARG_EOL; i += 2) { if (!imap_arg_get_atom(&args[i], &key) || args[i+1].type == IMAP_ARG_EOL) break; if (strcasecmp(key, "BODY[]") == 0) { imapc_fetch_stream(mail, reply, &args[i+1], TRUE, TRUE); match = TRUE; } else if (strcasecmp(key, "BODY[HEADER]") == 0) { imapc_fetch_stream(mail, reply, &args[i+1], TRUE, FALSE); match = TRUE; } else if (strcasecmp(key, "BODY[TEXT]") == 0) { imapc_fetch_stream(mail, reply, &args[i+1], FALSE, TRUE); match = TRUE; } else if (strcasecmp(key, "BODY[HEADER.FIELDS") == 0) { imapc_fetch_header_stream(mail, reply, &args[i+1]); match = TRUE; } else if (strcasecmp(key, "INTERNALDATE") == 0) { if (imap_arg_get_astring(&args[i+1], &value) && imap_parse_datetime(value, &t, &tz)) mail->imail.data.received_date = t; match = TRUE; } else if (strcasecmp(key, "BODY") == 0) { if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) { mail->imail.data.body = imapc_args_to_bodystructure(mail, &args[i+1], FALSE); } match = TRUE; } else if (strcasecmp(key, "BODYSTRUCTURE") == 0) { if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) { mail->imail.data.bodystructure = imapc_args_to_bodystructure(mail, &args[i+1], TRUE); } match = TRUE; } else if (strcasecmp(key, "RFC822.SIZE") == 0) { if (imap_arg_get_atom(&args[i+1], &value) && str_to_uoff(value, &size) == 0 && IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE)) mail->imail.data.physical_size = size; match = TRUE; } else if (strcasecmp(key, "X-GM-MSGID") == 0 || strcasecmp(key, "X-GUID") == 0) { if (imap_arg_get_astring(&args[i+1], &value)) { mail->imail.data.guid = p_strdup(mail->imail.mail.pool, value); } match = TRUE; } } if (!match) { /* this is only a FETCH FLAGS update for the wanted mail */ } else { imapc_client_stop(mbox->storage->client->client); } } dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-settings.h0000644000175000017500000000301513165463624020635 00000000000000#ifndef IMAPC_SETTINGS_H #define IMAPC_SETTINGS_H #include "net.h" /* */ enum imapc_features { IMAPC_FEATURE_RFC822_SIZE = 0x01, IMAPC_FEATURE_GUID_FORCED = 0x02, IMAPC_FEATURE_FETCH_HEADERS = 0x04, IMAPC_FEATURE_GMAIL_MIGRATION = 0x08, IMAPC_FEATURE_SEARCH = 0x10, IMAPC_FEATURE_ZIMBRA_WORKAROUNDS = 0x20, IMAPC_FEATURE_NO_EXAMINE = 0x40, IMAPC_FEATURE_PROXYAUTH = 0x80, IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS = 0x100, IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS = 0x200, IMAPC_FEATURE_MODSEQ = 0x400, IMAPC_FEATURE_DELAY_LOGIN = 0x800, IMAPC_FEATURE_FETCH_BODYSTRUCTURE = 0x1000, }; /* */ /* * NOTE: Any additions here should be reflected in imapc_storage_create's * serialization of settings. */ struct imapc_settings { const char *imapc_host; in_port_t imapc_port; const char *imapc_user; const char *imapc_master_user; const char *imapc_password; const char *imapc_sasl_mechanisms; const char *imapc_ssl; bool imapc_ssl_verify; const char *imapc_features; const char *imapc_rawlog_dir; const char *imapc_list_prefix; unsigned int imapc_cmd_timeout; unsigned int imapc_max_idle_time; unsigned int imapc_connection_retry_count; unsigned int imapc_connection_retry_interval; uoff_t imapc_max_line_length; const char *pop3_deleted_flag; enum imapc_features parsed_features; unsigned int throttle_init_msecs; unsigned int throttle_max_msecs; unsigned int throttle_shrink_min_msecs; }; const struct setting_parser_info *imapc_get_setting_parser_info(void); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-list.c0000644000175000017500000007246213165463624017757 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "imap-arg.h" #include "imap-match.h" #include "imap-utf7.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "imapc-storage.h" #include "imapc-list.h" struct imapc_mailbox_list_iterate_context { struct mailbox_list_iterate_context ctx; struct mailbox_tree_context *tree; struct mailbox_node *ns_root; struct mailbox_tree_iterate_context *iter; struct mailbox_info info; string_t *special_use; }; static struct { const char *str; enum mailbox_info_flags flag; } imap_list_flags[] = { { "\\NoSelect", MAILBOX_NOSELECT }, { "\\NonExistent", MAILBOX_NONEXISTENT }, { "\\NoInferiors", MAILBOX_NOINFERIORS }, { "\\Subscribed", MAILBOX_SUBSCRIBED }, { "\\All", MAILBOX_SPECIALUSE_ALL }, { "\\Archive", MAILBOX_SPECIALUSE_ARCHIVE }, { "\\Drafts", MAILBOX_SPECIALUSE_DRAFTS }, { "\\Flagged", MAILBOX_SPECIALUSE_FLAGGED }, { "\\Junk", MAILBOX_SPECIALUSE_JUNK }, { "\\Sent", MAILBOX_SPECIALUSE_SENT }, { "\\Trash", MAILBOX_SPECIALUSE_TRASH }, { "\\Important", MAILBOX_SPECIALUSE_IMPORTANT } }; extern struct mailbox_list imapc_mailbox_list; static void imapc_list_send_hierarcy_sep_lookup(struct imapc_mailbox_list *list); static void imapc_untagged_list(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client); static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client); static struct mailbox_list *imapc_list_alloc(void) { struct imapc_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("imapc mailbox list", 1024); list = p_new(pool, struct imapc_mailbox_list, 1); list->list = imapc_mailbox_list; list->list.pool = pool; /* separator is set lazily */ list->mailboxes = mailbox_tree_init('\0'); mailbox_tree_set_parents_nonexistent(list->mailboxes); return &list->list; } static int imapc_list_init(struct mailbox_list *_list, const char **error_r) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; list->set = mail_user_set_get_driver_settings(_list->ns->user->set_info, _list->ns->user_set, IMAPC_STORAGE_NAME); if (imapc_storage_client_create(_list->ns, list->set, _list->mail_set, &list->client, error_r) < 0) return -1; list->client->_list = list; imapc_storage_client_register_untagged(list->client, "LIST", imapc_untagged_list); imapc_storage_client_register_untagged(list->client, "LSUB", imapc_untagged_lsub); imapc_list_send_hierarcy_sep_lookup(list); return 0; } static void imapc_list_deinit(struct mailbox_list *_list) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; /* make sure all pending commands are aborted before anything is deinitialized */ if (list->client != NULL) { list->client->destroying = TRUE; imapc_client_logout(list->client->client); imapc_storage_client_unref(&list->client); } if (list->index_list != NULL) mailbox_list_destroy(&list->index_list); mailbox_tree_deinit(&list->mailboxes); if (list->tmp_subscriptions != NULL) mailbox_tree_deinit(&list->tmp_subscriptions); pool_unref(&list->list.pool); } static void imapc_list_copy_error_from_reply(struct imapc_mailbox_list *list, enum mail_error default_error, const struct imapc_command_reply *reply) { enum mail_error error; if (imap_resp_text_code_parse(reply->resp_text_key, &error)) { mailbox_list_set_error(&list->list, error, reply->text_without_resp); } else { mailbox_list_set_error(&list->list, default_error, reply->text_without_resp); } } static void imapc_list_simple_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_simple_context *ctx = context; if (reply->state == IMAPC_COMMAND_STATE_OK) ctx->ret = 0; else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_list_copy_error_from_reply(ctx->client->_list, MAIL_ERROR_PARAMS, reply); ctx->ret = -1; } else if (imapc_storage_client_handle_auth_failure(ctx->client)) { ctx->ret = -1; } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { mailbox_list_set_internal_error(&ctx->client->_list->list); ctx->ret = -1; } else { mailbox_list_set_critical(&ctx->client->_list->list, "imapc: Command failed: %s", reply->text_full); ctx->ret = -1; } imapc_client_stop(ctx->client->client); } static bool imap_list_flag_parse(const char *str, enum mailbox_info_flags *flag_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(imap_list_flags); i++) { if (strcasecmp(str, imap_list_flags[i].str) == 0) { *flag_r = imap_list_flags[i].flag; return TRUE; } } return FALSE; } static const char * imapc_list_to_vname(struct imapc_mailbox_list *list, const char *imapc_name) { const char *list_name; /* typically mailbox_list_escape_name() is used to escape vname into a list name. but we want to convert remote IMAP name to a list name, so we need to use the remote IMAP separator. */ list_name = mailbox_list_escape_name_params(imapc_name, "", list->root_sep, mailbox_list_get_hierarchy_sep(&list->list), list->list.set.escape_char, ""); /* list_name is now valid, so we can convert it to vname */ return mailbox_list_get_vname(&list->list, list_name); } const char *imapc_list_to_remote(struct imapc_mailbox_list *list, const char *name) { return mailbox_list_unescape_name_params(name, "", list->root_sep, mailbox_list_get_hierarchy_sep(&list->list), list->list.set.escape_char); } static struct mailbox_node * imapc_list_update_tree(struct imapc_mailbox_list *list, struct mailbox_tree_context *tree, const struct imap_arg *args) { struct mailbox_node *node; const struct imap_arg *flags; const char *name, *flag; enum mailbox_info_flags info_flag, info_flags = 0; bool created; if (!imap_arg_get_list(&args[0], &flags) || args[1].type == IMAP_ARG_EOL || !imap_arg_get_astring(&args[2], &name)) return NULL; while (imap_arg_get_atom(flags, &flag)) { if (imap_list_flag_parse(flag, &info_flag)) info_flags |= info_flag; flags++; } T_BEGIN { const char *vname = imapc_list_to_vname(list, name); if ((info_flags & MAILBOX_NONEXISTENT) != 0) node = mailbox_tree_lookup(tree, vname); else node = mailbox_tree_get(tree, vname, &created); } T_END; if (node != NULL) node->flags = info_flags; return node; } static void imapc_untagged_list(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { struct imapc_mailbox_list *list = client->_list; const struct imap_arg *args = reply->args; const char *sep, *name; if (list->root_sep == '\0') { /* we haven't asked for the separator yet. lets see if this is the reply for its request. */ if (args[0].type == IMAP_ARG_EOL || !imap_arg_get_nstring(&args[1], &sep) || !imap_arg_get_astring(&args[2], &name)) return; /* we can't handle NIL separator yet */ list->root_sep = sep == NULL ? '/' : sep[0]; mailbox_tree_set_separator(list->mailboxes, list->root_sep); } else { (void)imapc_list_update_tree(list, list->mailboxes, args); } } static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { struct imapc_mailbox_list *list = client->_list; const struct imap_arg *args = reply->args; struct mailbox_node *node; if (list->root_sep == '\0') { /* we haven't asked for the separator yet */ return; } node = imapc_list_update_tree(list, list->tmp_subscriptions != NULL ? list->tmp_subscriptions : list->list.subscriptions, args); if (node != NULL) { if ((node->flags & MAILBOX_NOSELECT) == 0) node->flags |= MAILBOX_SUBSCRIBED; else { /* LSUB \Noselect means that the mailbox isn't subscribed, but it has children that are */ node->flags &= ~MAILBOX_NOSELECT; } } } static void imapc_list_sep_verify(struct imapc_mailbox_list *list) { const char *imapc_list_prefix = list->set->imapc_list_prefix; if (list->root_sep == '\0') { mailbox_list_set_critical(&list->list, "imapc: LIST didn't return hierarchy separator"); } else if (imapc_list_prefix[0] != '\0' && imapc_list_prefix[strlen(imapc_list_prefix)-1] == list->root_sep) { mailbox_list_set_critical(&list->list, "imapc_list_prefix must not end with hierarchy separator"); } } static void imapc_storage_sep_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_mailbox_list *list = context; list->root_sep_pending = FALSE; if (reply->state == IMAPC_COMMAND_STATE_OK) imapc_list_sep_verify(list); else if (reply->state == IMAPC_COMMAND_STATE_NO) imapc_list_copy_error_from_reply(list, MAIL_ERROR_PARAMS, reply); else if (imapc_storage_client_handle_auth_failure(list->client)) ; else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) mailbox_list_set_internal_error(&list->list); else if (!list->list.ns->user->deinitializing) { mailbox_list_set_critical(&list->list, "imapc: Command failed: %s", reply->text_full); } imapc_client_stop(list->client->client); } static void imapc_list_send_hierarcy_sep_lookup(struct imapc_mailbox_list *list) { struct imapc_command *cmd; if (list->root_sep_pending) return; list->root_sep_pending = TRUE; cmd = imapc_client_cmd(list->client->client, imapc_storage_sep_callback, list); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "LIST \"\" \"\""); } int imapc_list_try_get_root_sep(struct imapc_mailbox_list *list, char *sep_r) { if (list->root_sep == '\0') { if (imapc_storage_client_handle_auth_failure(list->client)) return -1; imapc_list_send_hierarcy_sep_lookup(list); while (list->root_sep_pending) imapc_client_run(list->client->client); if (list->root_sep == '\0') return -1; } *sep_r = list->root_sep; return 0; } static char imapc_list_get_hierarchy_sep(struct mailbox_list *_list) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; char sep; if (imapc_list_try_get_root_sep(list, &sep) < 0) { /* we can't really return a failure here. just return a common separator and fail all the future list operations. */ return '/'; } return sep; } static const char * imapc_list_get_storage_name(struct mailbox_list *_list, const char *vname) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; const char *prefix = list->set->imapc_list_prefix; const char *storage_name; storage_name = mailbox_list_default_get_storage_name(_list, vname); if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) { storage_name = storage_name[0] == '\0' ? prefix : t_strdup_printf("%s%c%s", prefix, mailbox_list_get_hierarchy_sep(_list), storage_name); } return storage_name; } static const char * imapc_list_get_vname(struct mailbox_list *_list, const char *storage_name) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; const char *prefix = list->set->imapc_list_prefix; size_t prefix_len; if (*storage_name == '\0') { /* ACL plugin does these lookups */ } else if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) { prefix_len = strlen(prefix); i_assert(strncmp(prefix, storage_name, prefix_len) == 0); storage_name += prefix_len; if (storage_name[0] == '\0') { /* we're looking up the prefix itself */ } else { i_assert(storage_name[0] == mailbox_list_get_hierarchy_sep(_list)); storage_name++; } } return mailbox_list_default_get_vname(_list, storage_name); } static struct mailbox_list *imapc_list_get_fs(struct imapc_mailbox_list *list) { struct mailbox_list_settings list_set; const char *error, *dir; dir = list->list.set.index_dir; if (dir == NULL) dir = list->list.set.root_dir; if (dir == NULL || dir[0] == '\0') { /* indexes disabled */ } else if (list->index_list == NULL && !list->index_list_failed) { mailbox_list_settings_init_defaults(&list_set); list_set.layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS; list_set.root_dir = dir; list_set.escape_char = IMAPC_LIST_ESCAPE_CHAR; list_set.broken_char = IMAPC_LIST_BROKEN_CHAR; if (mailbox_list_create(list_set.layout, list->list.ns, &list_set, MAILBOX_LIST_FLAG_SECONDARY, &list->index_list, &error) < 0) { i_error("imapc: Couldn't create %s mailbox list: %s", list_set.layout, error); list->index_list_failed = TRUE; } } return list->index_list; } static const char * imapc_list_get_fs_name(struct imapc_mailbox_list *list, const char *name) { struct mailbox_list *fs_list = imapc_list_get_fs(list); struct mail_namespace *ns = list->list.ns; const char *vname; char ns_sep = mail_namespace_get_sep(ns); if (name == NULL) return NULL; vname = mailbox_list_get_vname(&list->list, name); if (list->set->imapc_list_prefix[0] != '\0') { /* put back the prefix, so it gets included in the filesystem. */ size_t vname_len = strlen(vname); if (ns->prefix_len > 0) { /* skip over the namespace prefix */ i_assert(strncmp(vname, ns->prefix, ns->prefix_len-1) == 0); if (vname_len == ns->prefix_len-1) vname = ""; else { i_assert(vname[ns->prefix_len-1] == ns_sep); vname += ns->prefix_len; } } if (vname[0] == '\0') { vname = t_strconcat(ns->prefix, list->set->imapc_list_prefix, NULL); } else { vname = t_strdup_printf("%s%s%c%s", ns->prefix, list->set->imapc_list_prefix, ns_sep, vname); } } return mailbox_list_get_storage_name(fs_list, vname); } static int imapc_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type, const char **path_r) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_list *fs_list = imapc_list_get_fs(list); const char *fs_name; if (fs_list != NULL) { fs_name = imapc_list_get_fs_name(list, name); return mailbox_list_get_path(fs_list, fs_name, type, path_r); } else { *path_r = NULL; return 0; } } static const char * imapc_list_get_temp_prefix(struct mailbox_list *_list, bool global) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_list *fs_list = imapc_list_get_fs(list); if (fs_list != NULL) { return global ? mailbox_list_get_global_temp_prefix(fs_list) : mailbox_list_get_temp_prefix(fs_list); } else { i_panic("imapc: Can't return a temp prefix for '%s'", _list->ns->prefix); return NULL; } } static const char * imapc_list_join_refpattern(struct mailbox_list *list ATTR_UNUSED, const char *ref, const char *pattern) { return t_strconcat(ref, pattern, NULL); } static struct imapc_command * imapc_list_simple_context_init(struct imapc_simple_context *ctx, struct imapc_mailbox_list *list) { imapc_simple_context_init(ctx, list->client); return imapc_client_cmd(list->client->client, imapc_list_simple_callback, ctx); } static void imapc_list_delete_unused_indexes(struct imapc_mailbox_list *list) { struct mailbox_list *fs_list = imapc_list_get_fs(list); struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; const char *imapc_list_prefix = list->set->imapc_list_prefix; size_t imapc_list_prefix_len = strlen(imapc_list_prefix); const char *fs_name, *vname; if (fs_list == NULL) return; iter = mailbox_list_iter_init(fs_list, "*", MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN { vname = info->vname; if (imapc_list_prefix_len > 0 && strcasecmp(vname, "INBOX") != 0) { /* skip over the namespace prefix */ i_assert(strncmp(vname, fs_list->ns->prefix, fs_list->ns->prefix_len) == 0); vname += fs_list->ns->prefix_len; /* skip over the imapc list prefix */ i_assert(strncmp(vname, imapc_list_prefix, imapc_list_prefix_len) == 0); vname += imapc_list_prefix_len; if (vname[0] != '\0') { i_assert(vname[0] == mail_namespace_get_sep(fs_list->ns)); vname++; } /* put back the namespace prefix */ if (fs_list->ns->prefix_len > 0) { vname = t_strconcat(fs_list->ns->prefix, vname, NULL); } } if (mailbox_tree_lookup(list->mailboxes, vname) == NULL) { fs_name = mailbox_list_get_storage_name(fs_list, info->vname); (void)fs_list->v.delete_mailbox(fs_list, fs_name); } } T_END; (void)mailbox_list_iter_deinit(&iter); } static int imapc_list_refresh(struct imapc_mailbox_list *list) { struct imapc_command *cmd; struct imapc_simple_context ctx; struct mailbox_node *node; const char *pattern; char sep; if (imapc_list_try_get_root_sep(list, &sep) < 0) return -1; if (list->refreshed_mailboxes) return 0; if (*list->set->imapc_list_prefix == '\0') pattern = "*"; else { /* list "prefix*" instead of "prefix.*". this may return a bit more than we want, but we're also interested in the flags of the prefix itself. */ pattern = t_strdup_printf("%s*", list->set->imapc_list_prefix); } cmd = imapc_list_simple_context_init(&ctx, list); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_sendf(cmd, "LIST \"\" %s", pattern); mailbox_tree_deinit(&list->mailboxes); list->mailboxes = mailbox_tree_init(mail_namespace_get_sep(list->list.ns)); mailbox_tree_set_parents_nonexistent(list->mailboxes); imapc_simple_run(&ctx); if ((list->list.ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* INBOX always exists in IMAP server. since this namespace is marked with inbox=yes, show the INBOX even if imapc_list_prefix doesn't match it */ bool created; node = mailbox_tree_get(list->mailboxes, "INBOX", &created); if (*list->set->imapc_list_prefix != '\0') { /* this listing didn't include the INBOX itself, but might have included its children. make sure there aren't any extra flags in it (especially \NonExistent) */ node->flags &= MAILBOX_CHILDREN; } } if (ctx.ret == 0) { list->refreshed_mailboxes = TRUE; list->refreshed_mailboxes_recently = TRUE; list->last_refreshed_mailboxes = ioloop_time; imapc_list_delete_unused_indexes(list); } return ctx.ret; } static void imapc_list_build_match_tree(struct imapc_mailbox_list_iterate_context *ctx) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)ctx->ctx.list; struct mailbox_list_iter_update_context update_ctx; struct mailbox_tree_iterate_context *iter; struct mailbox_node *node; const char *name; i_zero(&update_ctx); update_ctx.iter_ctx = &ctx->ctx; update_ctx.tree_ctx = ctx->tree; update_ctx.glob = ctx->ctx.glob; update_ctx.match_parents = TRUE; iter = mailbox_tree_iterate_init(list->mailboxes, NULL, 0); while ((node = mailbox_tree_iterate_next(iter, &name)) != NULL) { update_ctx.leaf_flags = node->flags; mailbox_list_iter_update(&update_ctx, name); } mailbox_tree_iterate_deinit(&iter); } static struct mailbox_list_iterate_context * imapc_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_list_iterate_context *_ctx; struct imapc_mailbox_list_iterate_context *ctx; pool_t pool; const char *ns_root_name; char ns_sep; int ret = 0; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || (flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) ret = imapc_list_refresh(list); list->iter_count++; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* we're listing only subscriptions. just use the cached subscriptions list. */ _ctx = mailbox_list_subscriptions_iter_init(_list, patterns, flags); if (ret < 0) _ctx->failed = TRUE; return _ctx; } /* if we've already failed, make sure we don't call mailbox_list_get_hierarchy_sep(), since it clears the error */ ns_sep = ret < 0 ? '/' : mail_namespace_get_sep(_list->ns); pool = pool_alloconly_create("mailbox list imapc iter", 1024); ctx = p_new(pool, struct imapc_mailbox_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = _list; ctx->ctx.flags = flags; ctx->ctx.glob = imap_match_init_multiple(pool, patterns, FALSE, ns_sep); array_create(&ctx->ctx.module_contexts, pool, sizeof(void *), 5); ctx->info.ns = _list->ns; ctx->tree = mailbox_tree_init(ns_sep); mailbox_tree_set_parents_nonexistent(ctx->tree); if (ret == 0) imapc_list_build_match_tree(ctx); if (list->list.ns->prefix_len > 0) { ns_root_name = t_strndup(_list->ns->prefix, _list->ns->prefix_len - 1); ctx->ns_root = mailbox_tree_lookup(ctx->tree, ns_root_name); } ctx->iter = mailbox_tree_iterate_init(ctx->tree, NULL, 0); if (ret < 0) ctx->ctx.failed = TRUE; return &ctx->ctx; } static void imapc_list_write_special_use(struct imapc_mailbox_list_iterate_context *ctx, struct mailbox_node *node) { unsigned int i; if (ctx->special_use == NULL) ctx->special_use = str_new(ctx->ctx.pool, 64); str_truncate(ctx->special_use, 0); for (i = 0; i < N_ELEMENTS(imap_list_flags); i++) { if ((node->flags & imap_list_flags[i].flag) != 0 && (node->flags & MAILBOX_SPECIALUSE_MASK) != 0) { str_append(ctx->special_use, imap_list_flags[i].str); str_append_c(ctx->special_use, ' '); } } if (str_len(ctx->special_use) > 0) { str_truncate(ctx->special_use, str_len(ctx->special_use) - 1); ctx->info.special_use = str_c(ctx->special_use); } else { ctx->info.special_use = NULL; } } static const struct mailbox_info * imapc_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct imapc_mailbox_list_iterate_context *ctx = (struct imapc_mailbox_list_iterate_context *)_ctx; struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_ctx->list; struct mailbox_node *node; const char *vname; if (_ctx->failed) return NULL; if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_next(_ctx); do { node = mailbox_tree_iterate_next(ctx->iter, &vname); if (node == NULL) return mailbox_list_iter_default_next(_ctx); } while ((node->flags & MAILBOX_MATCHED) == 0); if (ctx->info.ns->prefix_len > 0 && strncmp(vname, ctx->info.ns->prefix, ctx->info.ns->prefix_len-1) == 0 && vname[ctx->info.ns->prefix_len] == '\0' && list->set->imapc_list_prefix[0] == '\0') { /* don't return "" name */ return imapc_list_iter_next(_ctx); } ctx->info.vname = vname; ctx->info.flags = node->flags; if ((_ctx->list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* we're iterating the INBOX namespace. pass through the SPECIAL-USE flags if they exist. */ imapc_list_write_special_use(ctx, node); } else { ctx->info.special_use = NULL; } return &ctx->info; } static int imapc_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct imapc_mailbox_list_iterate_context *ctx = (struct imapc_mailbox_list_iterate_context *)_ctx; struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_ctx->list; int ret = _ctx->failed ? -1 : 0; i_assert(list->iter_count > 0); if (--list->iter_count == 0) { list->refreshed_mailboxes = FALSE; list->refreshed_subscriptions = FALSE; } if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) return mailbox_list_subscriptions_iter_deinit(_ctx); mailbox_tree_iterate_deinit(&ctx->iter); mailbox_tree_deinit(&ctx->tree); pool_unref(&_ctx->pool); return ret; } static int imapc_list_subscriptions_refresh(struct mailbox_list *_src_list, struct mailbox_list *dest_list) { struct imapc_mailbox_list *src_list = (struct imapc_mailbox_list *)_src_list; struct imapc_simple_context ctx; struct imapc_command *cmd; const char *pattern; char list_sep, dest_sep = mail_namespace_get_sep(dest_list->ns); i_assert(src_list->tmp_subscriptions == NULL); if (imapc_list_try_get_root_sep(src_list, &list_sep) < 0) return -1; if (src_list->refreshed_subscriptions) { if (dest_list->subscriptions == NULL) dest_list->subscriptions = mailbox_tree_init(dest_sep); return 0; } src_list->tmp_subscriptions = mailbox_tree_init(mail_namespace_get_sep(_src_list->ns)); cmd = imapc_list_simple_context_init(&ctx, src_list); if (*src_list->set->imapc_list_prefix == '\0') pattern = "*"; else pattern = t_strdup_printf("%s*", src_list->set->imapc_list_prefix); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_sendf(cmd, "LSUB \"\" %s", pattern); imapc_simple_run(&ctx); if (ctx.ret < 0) return -1; /* replace subscriptions tree in destination */ if (dest_list->subscriptions != NULL) mailbox_tree_deinit(&dest_list->subscriptions); dest_list->subscriptions = src_list->tmp_subscriptions; src_list->tmp_subscriptions = NULL; mailbox_tree_set_separator(dest_list->subscriptions, dest_sep); src_list->refreshed_subscriptions = TRUE; return 0; } static int imapc_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct imapc_command *cmd; struct imapc_simple_context ctx; cmd = imapc_list_simple_context_init(&ctx, list); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_sendf(cmd, set ? "SUBSCRIBE %s" : "UNSUBSCRIBE %s", imapc_list_to_remote(list, name)); imapc_simple_run(&ctx); return ctx.ret; } static int imapc_list_delete_mailbox(struct mailbox_list *_list, const char *name) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_list *fs_list = imapc_list_get_fs(list); enum imapc_capability capa; struct imapc_command *cmd; struct imapc_simple_context ctx; if (imapc_storage_client_handle_auth_failure(list->client)) return -1; if (imapc_client_get_capabilities(list->client->client, &capa) < 0) return -1; cmd = imapc_list_simple_context_init(&ctx, list); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); if (!imapc_command_connection_is_selected(cmd)) imapc_command_abort(&cmd); else { imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); if ((capa & IMAPC_CAPABILITY_UNSELECT) != 0) imapc_command_sendf(cmd, "UNSELECT"); else imapc_command_sendf(cmd, "SELECT \"~~~\""); imapc_simple_run(&ctx); } cmd = imapc_list_simple_context_init(&ctx, list); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_sendf(cmd, "DELETE %s", imapc_list_to_remote(list, name)); imapc_simple_run(&ctx); if (fs_list != NULL && ctx.ret == 0) { name = imapc_list_get_fs_name(list, name); (void)fs_list->v.delete_mailbox(fs_list, name); } return ctx.ret; } static int imapc_list_delete_dir(struct mailbox_list *_list, const char *name) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_list *fs_list = imapc_list_get_fs(list); if (fs_list != NULL) { name = imapc_list_get_fs_name(list, name); (void)mailbox_list_delete_dir(fs_list, name); } return 0; } static int imapc_list_delete_symlink(struct mailbox_list *list, const char *name ATTR_UNUSED) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Not supported"); return -1; } static int imapc_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist; struct mailbox_list *fs_list = imapc_list_get_fs(list); struct imapc_command *cmd; struct imapc_simple_context ctx; if (oldlist != newlist) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailboxes across storages."); return -1; } cmd = imapc_list_simple_context_init(&ctx, list); imapc_command_sendf(cmd, "RENAME %s %s", imapc_list_to_remote(list, oldname), imapc_list_to_remote(list, newname)); imapc_simple_run(&ctx); if (ctx.ret == 0 && fs_list != NULL && oldlist == newlist) { oldname = imapc_list_get_fs_name(list, oldname); newname = imapc_list_get_fs_name(list, newname); (void)fs_list->v.rename_mailbox(fs_list, oldname, fs_list, newname); } return ctx.ret; } int imapc_list_get_mailbox_flags(struct mailbox_list *_list, const char *name, enum mailbox_info_flags *flags_r) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct mailbox_node *node; const char *vname; vname = mailbox_list_get_vname(_list, name); if (!list->refreshed_mailboxes_recently) { if (imapc_list_refresh(list) < 0) return -1; i_assert(list->refreshed_mailboxes_recently); } if (list->mailboxes == NULL) { /* imapc list isn't used, but e.g. LAYOUT=none */ *flags_r = 0; return 0; } node = mailbox_tree_lookup(list->mailboxes, vname); if (node == NULL) *flags_r = MAILBOX_NONEXISTENT; else *flags_r = node->flags; return 0; } struct mailbox_list imapc_mailbox_list = { .name = MAILBOX_LIST_NAME_IMAPC, .props = MAILBOX_LIST_PROP_NO_ROOT | MAILBOX_LIST_PROP_AUTOCREATE_DIRS, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = imapc_list_alloc, .init = imapc_list_init, .deinit = imapc_list_deinit, .get_hierarchy_sep = imapc_list_get_hierarchy_sep, .get_vname = imapc_list_get_vname, .get_storage_name = imapc_list_get_storage_name, .get_path = imapc_list_get_path, .get_temp_prefix = imapc_list_get_temp_prefix, .join_refpattern = imapc_list_join_refpattern, .iter_init = imapc_list_iter_init, .iter_next = imapc_list_iter_next, .iter_deinit = imapc_list_iter_deinit, .subscriptions_refresh = imapc_list_subscriptions_refresh, .set_subscribed = imapc_list_set_subscribed, .delete_mailbox = imapc_list_delete_mailbox, .delete_dir = imapc_list_delete_dir, .delete_symlink = imapc_list_delete_symlink, .rename_mailbox = imapc_list_rename_mailbox, } }; dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-list.h0000644000175000017500000000237413165463624017757 00000000000000#ifndef IMAPC_LIST_H #define IMAPC_LIST_H struct imap_arg; #include "mailbox-list-private.h" #define MAILBOX_LIST_NAME_IMAPC "imapc" struct imapc_mailbox_list { struct mailbox_list list; const struct imapc_settings *set; struct imapc_storage_client *client; struct mailbox_list *index_list; /* mailboxes are stored as vnames */ struct mailbox_tree_context *mailboxes, *tmp_subscriptions; char root_sep; time_t last_refreshed_mailboxes; unsigned int iter_count; /* mailboxes/subscriptions are fully refreshed only during mailbox list iteration. */ unsigned int refreshed_subscriptions:1; unsigned int refreshed_mailboxes:1; /* mailbox list's "recently refreshed" state is reset by syncing a mailbox. mainly we use this to cache mailboxes' existence to avoid issuing a LIST command every time. */ unsigned int refreshed_mailboxes_recently:1; unsigned int index_list_failed:1; unsigned int root_sep_pending:1; unsigned int root_sep_lookup_failed:1; }; int imapc_list_get_mailbox_flags(struct mailbox_list *list, const char *name, enum mailbox_info_flags *flags_r); int imapc_list_try_get_root_sep(struct imapc_mailbox_list *list, char *sep_r); const char *imapc_list_to_remote(struct imapc_mailbox_list *list, const char *name); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-search.h0000644000175000017500000000117513123174404020234 00000000000000#ifndef IMAPC_SEARCH_H #define IMAPC_SEARCH_H struct mail_search_context * imapc_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); bool imapc_search_next_update_seq(struct mail_search_context *ctx); int imapc_search_deinit(struct mail_search_context *ctx); void imapc_search_reply_search(const struct imap_arg *args, struct imapc_mailbox *mbox); void imapc_search_reply_esearch(const struct imap_arg *args, struct imapc_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/imapc-mail.h0000644000175000017500000000261113165463624017720 00000000000000#ifndef IMAPC_MAIL_H #define IMAPC_MAIL_H #include "index-mail.h" struct imap_arg; struct imapc_untagged_reply; struct imapc_mailbox; struct imapc_mail { struct index_mail imail; enum mail_fetch_field fetching_fields; const char *const *fetching_headers; unsigned int fetch_count; bool fetch_sent; const char *last_fetch_reply; int fd; buffer_t *body; bool header_fetched; bool body_fetched; bool header_list_fetched; bool fetch_ignore_if_missing; bool fetch_failed; }; extern struct mail_vfuncs imapc_mail_vfuncs; struct mail * imapc_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); int imapc_mail_fetch(struct mail *mail, enum mail_fetch_field fields, const char *const *headers); void imapc_mail_try_init_stream_from_cache(struct imapc_mail *mail); bool imapc_mail_prefetch(struct mail *mail); void imapc_mail_fetch_flush(struct imapc_mailbox *mbox); void imapc_mail_init_stream(struct imapc_mail *mail); bool imapc_mail_has_headers_in_cache(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers); void imapc_mail_fetch_update(struct imapc_mail *mail, const struct imapc_untagged_reply *reply, const struct imap_arg *args); void imapc_mail_update_access_parts(struct index_mail *mail); void imapc_mail_command_flush(struct imapc_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/imapc/Makefile.am0000644000175000017500000000141013123174404017553 00000000000000noinst_LTLIBRARIES = libstorage_imapc.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/list \ -I$(top_srcdir)/src/lib-storage/index libstorage_imapc_la_SOURCES = \ imapc-list.c \ imapc-mail.c \ imapc-mail-fetch.c \ imapc-mailbox.c \ imapc-save.c \ imapc-search.c \ imapc-settings.c \ imapc-sync.c \ imapc-storage.c headers = \ imapc-list.h \ imapc-mail.h \ imapc-search.h \ imapc-settings.h \ imapc-storage.h \ imapc-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-thread-finish.c0000644000175000017500000004664313165463624020460 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "imap-base-subject.h" #include "mail-storage-private.h" #include "index-thread-private.h" struct mail_thread_shadow_node { uint32_t first_child_idx, next_sibling_idx; }; struct mail_thread_root_node { /* node.idx usually points to indexes from mail hash. However REFERENCES step (5) may add temporary dummy roots. They use larger index numbers than exist in the hash. */ struct mail_thread_child_node node; /* Used temporarily by (5)(B) base subject gathering. root_idx1 is node's index in roots[] array + 1. parent_root_idx points to root_idx1, or 0 for root. */ unsigned int root_idx1; uint32_t parent_root_idx1; /* subject contained a Re: or Fwd: */ unsigned int reply_or_forward:1; /* a dummy node */ unsigned int dummy:1; /* ignore this node - it's a dummy without children */ unsigned int ignore:1; }; struct thread_finish_context { unsigned int refcount; struct mail *tmp_mail; struct mail_thread_cache *cache; ARRAY(struct mail_thread_root_node) roots; ARRAY(struct mail_thread_shadow_node) shadow_nodes; unsigned int next_new_root_idx; unsigned int use_sent_date:1; unsigned int return_seqs:1; }; struct mail_thread_iterate_context { struct thread_finish_context *ctx; ARRAY_TYPE(mail_thread_child_node) children; unsigned int next_idx; bool failed; }; struct subject_gather_context { struct thread_finish_context *ctx; pool_t subject_pool; HASH_TABLE(char *, struct mail_thread_root_node *) subject_hash; }; static void add_base_subject(struct subject_gather_context *ctx, const char *subject, struct mail_thread_root_node *node) { struct mail_thread_root_node *hash_node; char *hash_subject; bool is_reply_or_forward; subject = imap_get_base_subject_cased(pool_datastack_create(), subject, &is_reply_or_forward); /* (ii) If the thread subject is empty, skip this message. */ if (*subject == '\0') return; /* (iii) Look up the message associated with the thread subject in the subject table. */ if (!hash_table_lookup_full(ctx->subject_hash, subject, &hash_subject, &hash_node)) { /* (iv) If there is no message in the subject table with the thread subject, add the current message and the thread subject to the subject table. */ hash_subject = p_strdup(ctx->subject_pool, subject); hash_table_insert(ctx->subject_hash, hash_subject, node); } else { /* Otherwise, if the message in the subject table is not a dummy, AND either of the following criteria are true: The current message is a dummy, OR The message in the subject table is a reply or forward and the current message is not. then replace the message in the subject table with the current message. */ if (!hash_node->dummy && (node->dummy || (hash_node->reply_or_forward && !is_reply_or_forward))) { hash_node->parent_root_idx1 = node->root_idx1; hash_table_update(ctx->subject_hash, hash_subject, node); } else { node->parent_root_idx1 = hash_node->root_idx1; } } node->reply_or_forward = is_reply_or_forward; } static int mail_thread_child_node_cmp(const struct mail_thread_child_node *c1, const struct mail_thread_child_node *c2) { if (c1->sort_date < c2->sort_date) return -1; if (c1->sort_date > c2->sort_date) return 1; if (c1->uid < c2->uid) return -1; if (c1->uid > c2->uid) return 1; return 0; } static int mail_thread_root_node_cmp(const struct mail_thread_root_node *r1, const struct mail_thread_root_node *r2) { return mail_thread_child_node_cmp(&r1->node, &r2->node); } static uint32_t thread_lookup_existing(struct thread_finish_context *ctx, uint32_t idx) { const struct mail_thread_node *node; node = array_idx(&ctx->cache->thread_nodes, idx); i_assert(MAIL_THREAD_NODE_EXISTS(node)); i_assert(node->uid != 0); return node->uid; } static void thread_child_node_fill(struct thread_finish_context *ctx, struct mail_thread_child_node *child) { int tz; child->uid = thread_lookup_existing(ctx, child->idx); if (!mail_set_uid(ctx->tmp_mail, child->uid)) { /* the UID should have existed. we would have rebuild the thread tree otherwise. */ i_unreached(); } /* get sent date if we want to use it and if it's valid */ if (!ctx->use_sent_date) child->sort_date = 0; else if (mail_get_date(ctx->tmp_mail, &child->sort_date, &tz) < 0) child->sort_date = 0; if (child->sort_date == 0) { /* fallback to received date */ (void)mail_get_received_date(ctx->tmp_mail, &child->sort_date); } } static void thread_sort_children(struct thread_finish_context *ctx, uint32_t parent_idx, ARRAY_TYPE(mail_thread_child_node) *sorted_children) { const struct mail_thread_shadow_node *shadows; struct mail_thread_child_node child; unsigned int count; i_zero(&child); array_clear(sorted_children); /* add all child indexes to the array */ shadows = array_get(&ctx->shadow_nodes, &count); child.idx = shadows[parent_idx].first_child_idx; i_assert(child.idx != 0); if (shadows[child.idx].next_sibling_idx == 0) { /* only child - don't bother setting sort date */ child.uid = thread_lookup_existing(ctx, child.idx); array_append(sorted_children, &child, 1); return; } while (child.idx != 0) { thread_child_node_fill(ctx, &child); array_append(sorted_children, &child, 1); child.idx = shadows[child.idx].next_sibling_idx; } /* sort the children */ array_sort(sorted_children, mail_thread_child_node_cmp); } static void gather_base_subjects(struct thread_finish_context *ctx) { struct subject_gather_context gather_ctx; struct mail_thread_root_node *roots; const char *subject; unsigned int i, count; ARRAY_TYPE(mail_thread_child_node) sorted_children; const struct mail_thread_child_node *children; uint32_t idx, uid; i_zero(&gather_ctx); gather_ctx.ctx = ctx; roots = array_get_modifiable(&ctx->roots, &count); if (count == 0) return; gather_ctx.subject_pool = pool_alloconly_create(MEMPOOL_GROWING"base subjects", nearest_power(count * 20)); hash_table_create(&gather_ctx.subject_hash, gather_ctx.subject_pool, count * 2, str_hash, strcmp); i_array_init(&sorted_children, 64); for (i = 0; i < count; i++) { roots[i].root_idx1 = i + 1; if (!roots[i].dummy) idx = roots[i].node.idx; else if (!roots[i].ignore) { /* find the oldest child */ thread_sort_children(ctx, roots[i].node.idx, &sorted_children); children = array_idx(&sorted_children, 0); idx = children[0].idx; } else { /* dummy without children */ continue; } uid = thread_lookup_existing(ctx, idx); if (!mail_set_uid(ctx->tmp_mail, uid)) { /* the UID should have existed. we would have rebuild the thread tree otherwise. */ i_unreached(); } if (mail_get_first_header(ctx->tmp_mail, HDR_SUBJECT, &subject) > 0) T_BEGIN { add_base_subject(&gather_ctx, subject, &roots[i]); } T_END; } i_assert(roots[count-1].parent_root_idx1 <= count); array_free(&sorted_children); hash_table_destroy(&gather_ctx.subject_hash); pool_unref(&gather_ctx.subject_pool); } static void thread_add_shadow_child(struct thread_finish_context *ctx, uint32_t parent_idx, uint32_t child_idx) { struct mail_thread_shadow_node *parent_shadow, *child_shadow; parent_shadow = array_idx_modifiable(&ctx->shadow_nodes, parent_idx); child_shadow = array_idx_modifiable(&ctx->shadow_nodes, child_idx); child_shadow->next_sibling_idx = parent_shadow->first_child_idx; parent_shadow->first_child_idx = child_idx; } static void mail_thread_root_thread_merge(struct thread_finish_context *ctx, struct mail_thread_root_node *cur) { struct mail_thread_root_node *roots, *root, new_root; struct mail_thread_shadow_node *shadows; unsigned int count; uint32_t idx, next_idx; i_assert(cur->parent_root_idx1 != 0); /* The highest parent is the same as the current message in the subject table. */ roots = array_get_modifiable(&ctx->roots, &count); root = cur; do { i_assert(root->parent_root_idx1 <= count); root = &roots[root->parent_root_idx1 - 1]; } while (root->parent_root_idx1 != 0); i_assert(!root->ignore); shadows = array_idx_modifiable(&ctx->shadow_nodes, 0); if (cur->dummy) { /* If both messages are dummies, append the current message's children to the children of the message in the subject table (the children of both messages become siblings), and then delete the current message. */ i_assert(root->dummy); idx = shadows[cur->node.idx].first_child_idx; while (idx != 0) { next_idx = shadows[idx].next_sibling_idx; thread_add_shadow_child(ctx, root->node.idx, idx); idx = next_idx; } shadows[cur->node.idx].first_child_idx = 0; cur->ignore = TRUE; } else if (root->dummy || (cur->reply_or_forward && !root->reply_or_forward)) { /* a) If the message in the subject table is a dummy and the current message is not, make the current message a child of the message in the subject table (a sibling of its children). b) If the current message is a reply or forward and the message in the subject table is not, make the current message a child of the message in the subject table (a sibling of its children). */ thread_add_shadow_child(ctx, root->node.idx, cur->node.idx); cur->ignore = TRUE; } else { /* Otherwise, create a new dummy message and make both the current message and the message in the subject table children of the dummy. Then replace the message in the subject table with the dummy message. */ i_zero(&new_root); new_root.root_idx1 = array_count(&ctx->roots) + 1; new_root.node.idx = ctx->next_new_root_idx++; new_root.dummy = TRUE; thread_add_shadow_child(ctx, new_root.node.idx, root->node.idx); thread_add_shadow_child(ctx, new_root.node.idx, cur->node.idx); root->parent_root_idx1 = new_root.root_idx1; root->ignore = TRUE; cur->ignore = TRUE; /* append last, since it breaks root and cur pointers */ array_append(&ctx->roots, &new_root, 1); /* make sure all shadow indexes are accessible directly */ (void)array_idx_modifiable(&ctx->shadow_nodes, new_root.node.idx); } } static bool merge_subject_threads(struct thread_finish_context *ctx) { struct mail_thread_root_node *roots; unsigned int i, count; bool changed = FALSE; roots = array_get_modifiable(&ctx->roots, &count); for (i = 0; i < count; i++) { if (roots[i].parent_root_idx1 != 0 && !roots[i].ignore) { mail_thread_root_thread_merge(ctx, &roots[i]); /* more roots may have been added */ roots = array_idx_modifiable(&ctx->roots, 0); changed = TRUE; } } return changed; } static void sort_root_nodes(struct thread_finish_context *ctx) { ARRAY_TYPE(mail_thread_child_node) sorted_children; const struct mail_thread_child_node *children; const struct mail_thread_shadow_node *shadows; struct mail_thread_root_node *roots; unsigned int i, count, child_count; i_array_init(&sorted_children, 64); shadows = array_idx(&ctx->shadow_nodes, 0); roots = array_get_modifiable(&ctx->roots, &count); for (i = 0; i < count; i++) { if (roots[i].ignore) continue; if (roots[i].dummy) { /* sort by the first child */ if (shadows[roots[i].node.idx].first_child_idx == 0) { /* childless dummy node */ roots[i].ignore = TRUE; continue; } thread_sort_children(ctx, roots[i].node.idx, &sorted_children); children = array_get(&sorted_children, &child_count); if (child_count == 1) { /* only one child - deferred step (3). promote the child to the root. */ roots[i].node = children[0]; thread_child_node_fill(ctx, &roots[i].node); roots[i].dummy = FALSE; } else { roots[i].node.uid = children[0].uid; roots[i].node.sort_date = children[0].sort_date; } } else { thread_child_node_fill(ctx, &roots[i].node); } } array_free(&sorted_children); array_sort(&ctx->roots, mail_thread_root_node_cmp); } static int mail_thread_root_node_idx_cmp(const void *key, const void *value) { const uint32_t *idx = key; const struct mail_thread_root_node *root = value; return *idx < root->node.idx ? -1 : *idx > root->node.idx ? 1 : 0; } static void sort_root_nodes_ref2(struct thread_finish_context *ctx, uint32_t record_count) { const struct mail_thread_node *node; struct mail_thread_root_node *roots, *root; struct mail_thread_child_node child; const struct mail_thread_shadow_node *shadows; unsigned int root_count; uint32_t idx, parent_idx; roots = array_get_modifiable(&ctx->roots, &root_count); /* drop childless dummy nodes */ shadows = array_idx(&ctx->shadow_nodes, 0); for (idx = 1; idx < root_count; idx++) { if (roots[idx].dummy && shadows[roots[idx].node.idx].first_child_idx == 0) roots[idx].ignore = TRUE; } for (idx = 1; idx < record_count; idx++) { node = array_idx(&ctx->cache->thread_nodes, idx); if (!MAIL_THREAD_NODE_EXISTS(node)) continue; child.idx = idx; thread_child_node_fill(ctx, &child); parent_idx = idx; while (node->parent_idx != 0) { parent_idx = node->parent_idx; node = array_idx(&ctx->cache->thread_nodes, node->parent_idx); } root = bsearch(&parent_idx, roots, root_count, sizeof(*roots), mail_thread_root_node_idx_cmp); i_assert(root != NULL); if (root->node.sort_date < child.sort_date) root->node.sort_date = child.sort_date; } array_sort(&ctx->roots, mail_thread_root_node_cmp); } static void mail_thread_create_shadows(struct thread_finish_context *ctx, uint32_t record_count) { const struct mail_thread_node *node, *parent; struct mail_thread_root_node root; struct mail_thread_child_node child; uint32_t idx, parent_idx; ctx->use_sent_date = FALSE; i_zero(&root); i_zero(&child); /* We may see dummy messages without parents or children. We can't free them since the nodes are in an array, but they may get reused later so just leave them be. With the current algorithm when this happens all the struct fields are always zero at that point, so we don't even have to try to zero them. */ for (idx = 1; idx < record_count; idx++) { node = array_idx(&ctx->cache->thread_nodes, idx); if (node->parent_idx == 0) { /* root node - add to roots list */ root.node.idx = idx; if (!MAIL_THREAD_NODE_EXISTS(node)) { root.dummy = TRUE; root.node.uid = 0; } else { root.dummy = FALSE; root.node.uid = node->uid; } array_append(&ctx->roots, &root, 1); continue; } i_assert(node->parent_idx < record_count); if (!MAIL_THREAD_NODE_EXISTS(node)) { /* dummy node */ continue; } /* Find the node's first non-dummy parent and add the node as its child. If there are no non-dummy parents, add it as the highest dummy's child. */ parent_idx = node->parent_idx; parent = array_idx(&ctx->cache->thread_nodes, parent_idx); while (!MAIL_THREAD_NODE_EXISTS(parent) && parent->parent_idx != 0) { parent_idx = parent->parent_idx; parent = array_idx(&ctx->cache->thread_nodes, parent_idx); } thread_add_shadow_child(ctx, parent_idx, idx); } } static void mail_thread_finish(struct thread_finish_context *ctx, enum mail_thread_type thread_type) { unsigned int record_count = array_count(&ctx->cache->thread_nodes); ctx->next_new_root_idx = record_count + 1; /* (2) save root nodes and (3) remove dummy messages */ i_array_init(&ctx->roots, I_MIN(128, record_count)); i_array_init(&ctx->shadow_nodes, record_count); /* make sure all shadow indexes are accessible directly. */ (void)array_idx_modifiable(&ctx->shadow_nodes, record_count); mail_thread_create_shadows(ctx, record_count); /* (4) */ ctx->use_sent_date = TRUE; switch (thread_type) { case MAIL_THREAD_REFERENCES: sort_root_nodes(ctx); /* (5) Gather together messages under the root that have the same base subject text. */ gather_base_subjects(ctx); /* (5.C) Merge threads with the same thread subject. */ if (merge_subject_threads(ctx)) { /* root ordering may have changed, sort them again. */ sort_root_nodes(ctx); } break; case MAIL_THREAD_REFS: sort_root_nodes_ref2(ctx, record_count); break; default: i_unreached(); } } static void nodes_change_uids_to_seqs(struct mail_thread_iterate_context *iter, bool root) { struct mail_thread_child_node *children; struct mailbox *box = iter->ctx->tmp_mail->box; unsigned int i, count; uint32_t uid, seq; children = array_get_modifiable(&iter->children, &count); for (i = 0; i < count; i++) { uid = children[i].uid; if (uid == 0) { /* dummy root */ if (root) continue; i_unreached(); } else { mailbox_get_seq_range(box, uid, uid, &seq, &seq); i_assert(seq != 0); } children[i].uid = seq; } } static void mail_thread_iterate_fill_root(struct mail_thread_iterate_context *iter) { struct mail_thread_root_node *roots; unsigned int i, count; roots = array_get_modifiable(&iter->ctx->roots, &count); i_array_init(&iter->children, count); for (i = 0; i < count; i++) { if (!roots[i].ignore) { if (roots[i].dummy) roots[i].node.uid = 0; array_append(&iter->children, &roots[i].node, 1); } } } static struct mail_thread_iterate_context * mail_thread_iterate_children(struct mail_thread_iterate_context *parent_iter, uint32_t parent_idx) { struct mail_thread_iterate_context *child_iter; child_iter = i_new(struct mail_thread_iterate_context, 1); child_iter->ctx = parent_iter->ctx; child_iter->ctx->refcount++; i_array_init(&child_iter->children, 8); thread_sort_children(child_iter->ctx, parent_idx, &child_iter->children); if (child_iter->ctx->return_seqs) nodes_change_uids_to_seqs(child_iter, FALSE); return child_iter; } struct mail_thread_iterate_context * mail_thread_iterate_init_full(struct mail_thread_cache *cache, struct mail *tmp_mail, enum mail_thread_type thread_type, bool return_seqs) { struct mail_thread_iterate_context *iter; struct thread_finish_context *ctx; iter = i_new(struct mail_thread_iterate_context, 1); ctx = iter->ctx = i_new(struct thread_finish_context, 1); ctx->refcount = 1; ctx->cache = cache; ctx->tmp_mail = tmp_mail; ctx->return_seqs = return_seqs; mail_thread_finish(ctx, thread_type); mail_thread_iterate_fill_root(iter); if (return_seqs) nodes_change_uids_to_seqs(iter, TRUE); return iter; } const struct mail_thread_child_node * mail_thread_iterate_next(struct mail_thread_iterate_context *iter, struct mail_thread_iterate_context **child_iter_r) { const struct mail_thread_child_node *children, *child; const struct mail_thread_shadow_node *shadow; unsigned int count; children = array_get(&iter->children, &count); if (iter->next_idx >= count) return NULL; child = &children[iter->next_idx++]; shadow = array_idx(&iter->ctx->shadow_nodes, child->idx); *child_iter_r = shadow->first_child_idx == 0 ? NULL : mail_thread_iterate_children(iter, child->idx); if (child->uid == 0 && *child_iter_r == NULL) { /* this is a dummy node without children, there's no point in returning it */ return mail_thread_iterate_next(iter, child_iter_r); } return child; } unsigned int mail_thread_iterate_count(struct mail_thread_iterate_context *iter) { return array_count(&iter->children); } int mail_thread_iterate_deinit(struct mail_thread_iterate_context **_iter) { struct mail_thread_iterate_context *iter = *_iter; *_iter = NULL; if (--iter->ctx->refcount == 0) { array_free(&iter->ctx->roots); array_free(&iter->ctx->shadow_nodes); i_free(iter->ctx); } array_free(&iter->children); i_free(iter); return 0; } dovecot-2.2.33.2/src/lib-storage/index/pop3c/0002755000175000017500000000000013172375611015547 500000000000000dovecot-2.2.33.2/src/lib-storage/index/pop3c/Makefile.in0000644000175000017500000005543313172375573017553 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/pop3c ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_pop3c_la_LIBADD = am_libstorage_pop3c_la_OBJECTS = pop3c-client.lo pop3c-mail.lo \ pop3c-settings.lo pop3c-storage.lo pop3c-sync.lo libstorage_pop3c_la_OBJECTS = $(am_libstorage_pop3c_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_pop3c_la_SOURCES) DIST_SOURCES = $(libstorage_pop3c_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_pop3c.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_pop3c_la_SOURCES = \ pop3c-client.c \ pop3c-mail.c \ pop3c-settings.c \ pop3c-storage.c \ pop3c-sync.c headers = \ pop3c-client.h \ pop3c-settings.h \ pop3c-storage.h \ pop3c-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/pop3c/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/pop3c/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_pop3c.la: $(libstorage_pop3c_la_OBJECTS) $(libstorage_pop3c_la_DEPENDENCIES) $(EXTRA_libstorage_pop3c_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_pop3c_la_OBJECTS) $(libstorage_pop3c_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3c-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3c-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3c-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3c-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3c-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-client.h0000644000175000017500000000531013123174404020126 00000000000000#ifndef POP3C_CLIENT_H #define POP3C_CLIENT_H #include "net.h" #include "pop3c-settings.h" enum pop3c_capability { POP3C_CAPABILITY_PIPELINING = 0x01, POP3C_CAPABILITY_TOP = 0x02, POP3C_CAPABILITY_UIDL = 0x04 }; enum pop3c_command_state { POP3C_COMMAND_STATE_OK, POP3C_COMMAND_STATE_ERR, POP3C_COMMAND_STATE_DISCONNECTED }; enum pop3c_client_ssl_mode { POP3C_CLIENT_SSL_MODE_NONE, POP3C_CLIENT_SSL_MODE_IMMEDIATE, POP3C_CLIENT_SSL_MODE_STARTTLS }; struct pop3c_client_settings { const char *host; in_port_t port; const char *master_user; const char *username; const char *password; const char *dns_client_socket_path; const char *temp_path_prefix; enum pop3c_client_ssl_mode ssl_mode; enum pop3c_features parsed_features; const char *ssl_ca_dir, *ssl_ca_file; bool ssl_verify; const char *rawlog_dir; const char *ssl_crypto_device; bool debug; }; typedef void pop3c_login_callback_t(enum pop3c_command_state state, const char *reply, void *context); typedef void pop3c_cmd_callback_t(enum pop3c_command_state state, const char *reply, void *context); struct pop3c_client * pop3c_client_init(const struct pop3c_client_settings *set); void pop3c_client_deinit(struct pop3c_client **client); void pop3c_client_login(struct pop3c_client *client, pop3c_login_callback_t *callback, void *context); bool pop3c_client_is_connected(struct pop3c_client *client); enum pop3c_capability pop3c_client_get_capabilities(struct pop3c_client *client); /* Returns 0 if received +OK reply, reply contains the text without the +OK. Returns -1 if received -ERR reply or disconnected. */ int pop3c_client_cmd_line(struct pop3c_client *client, const char *cmdline, const char **reply_r); /* Start the command asynchronously. Call the callback when finished. */ struct pop3c_client_cmd * pop3c_client_cmd_line_async(struct pop3c_client *client, const char *cmdline, pop3c_cmd_callback_t *callback, void *context); /* Send a command, don't care if it succeeds or not. */ void pop3c_client_cmd_line_async_nocb(struct pop3c_client *client, const char *cmdline); /* Returns 0 and stream if succeeded, -1 and error if received -ERR reply or disconnected. */ int pop3c_client_cmd_stream(struct pop3c_client *client, const char *cmdline, struct istream **input_r, const char **error_r); /* Start the command asynchronously. Call the callback when finished. */ struct istream * pop3c_client_cmd_stream_async(struct pop3c_client *client, const char *cmdline, pop3c_cmd_callback_t *callback, void *context); /* Wait for the next async command to finish. It's an error to call this when there are no pending async commands. */ void pop3c_client_wait_one(struct pop3c_client *client); #endif dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-sync.h0000644000175000017500000000053613123174404017631 00000000000000#ifndef POP3C_SYNC_H #define POP3C_SYNC_H struct mailbox; struct pop3c_mailbox; struct mailbox_sync_context * pop3c_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); int pop3c_sync(struct pop3c_mailbox *mbox); int pop3c_sync_get_sizes(struct pop3c_mailbox *mbox); int pop3c_sync_get_uidls(struct pop3c_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-storage.h0000644000175000017500000000170613165463624020334 00000000000000#ifndef POP3C_STORAGE_H #define POP3C_STORAGE_H #include "index-storage.h" #define POP3C_STORAGE_NAME "pop3c" struct pop3c_storage { struct mail_storage storage; const struct pop3c_settings *set; }; struct pop3c_mailbox { struct mailbox box; struct pop3c_storage *storage; struct pop3c_client *client; pool_t uidl_pool; unsigned int msg_count; /* LIST sizes */ uoff_t *msg_sizes; /* UIDL strings */ const char *const *msg_uidls; /* index UIDs for each message in this session. the UID may not exist for the entire session */ uint32_t *msg_uids; unsigned int logged_in:1; }; struct pop3c_mail { struct index_mail imail; struct istream *prefetch_stream; unsigned int prefetching:1; unsigned int prefetching_body:1; }; struct mail * pop3c_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); extern struct mail_vfuncs pop3c_mail_vfuncs; #endif dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-settings.c0000644000175000017500000000512213123174404020504 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "pop3c-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct pop3c_settings, name), NULL } static const struct setting_define pop3c_setting_defines[] = { DEF(SET_STR, pop3c_host), DEF(SET_IN_PORT, pop3c_port), DEF(SET_STR_VARS, pop3c_user), DEF(SET_STR_VARS, pop3c_master_user), DEF(SET_STR, pop3c_password), DEF(SET_ENUM, pop3c_ssl), DEF(SET_BOOL, pop3c_ssl_verify), DEF(SET_STR, pop3c_rawlog_dir), DEF(SET_BOOL, pop3c_quick_received_date), DEF(SET_STR, pop3c_features), SETTING_DEFINE_LIST_END }; static const struct pop3c_settings pop3c_default_settings = { .pop3c_host = "", .pop3c_port = 110, .pop3c_user = "%u", .pop3c_master_user = "", .pop3c_password = "", .pop3c_ssl = "no:pop3s:starttls", .pop3c_ssl_verify = TRUE, .pop3c_rawlog_dir = "", .pop3c_quick_received_date = FALSE, .pop3c_features = "" }; /* */ struct pop3c_feature_list { const char *name; enum pop3c_features num; }; static const struct pop3c_feature_list pop3c_feature_list[] = { { "no-pipelining", POP3C_FEATURE_NO_PIPELINING }, { NULL, 0 } }; static int pop3c_settings_parse_features(struct pop3c_settings *set, const char **error_r) { enum pop3c_features features = 0; const struct pop3c_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->pop3c_features, " ,"); for (; *str != NULL; str++) { list = pop3c_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("pop3c_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool pop3c_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct pop3c_settings *set = _set; if (pop3c_settings_parse_features(set, error_r) < 0) return FALSE; return TRUE; } /* */ static const struct setting_parser_info pop3c_setting_parser_info = { .module_name = "pop3c", .defines = pop3c_setting_defines, .defaults = &pop3c_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct pop3c_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = pop3c_settings_check }; const struct setting_parser_info *pop3c_get_setting_parser_info(void) { return &pop3c_setting_parser_info; } dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-mail.c0000644000175000017500000002102713165463624017603 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "index-mail.h" #include "pop3c-client.h" #include "pop3c-sync.h" #include "pop3c-storage.h" struct mail * pop3c_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct pop3c_mail *mail; pool_t pool; pool = pool_alloconly_create("mail", 2048); mail = p_new(pool, struct pop3c_mail, 1); mail->imail.mail.pool = pool; index_mail_init(&mail->imail, t, wanted_fields, wanted_headers); return &mail->imail.mail.mail; } static void pop3c_mail_close(struct mail *_mail) { struct pop3c_mail *pmail = (struct pop3c_mail *)_mail; struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; /* wait for any prefetch to finish before closing the mail */ while (pmail->prefetching) pop3c_client_wait_one(mbox->client); if (pmail->prefetch_stream != NULL) i_stream_unref(&pmail->prefetch_stream); index_mail_close(_mail); } static int pop3c_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; int tz; if (mbox->storage->set->pop3c_quick_received_date) { /* we don't care about the date, just return the current date */ *date_r = ioloop_time; return 0; } /* FIXME: we could also parse the first Received: header and get the date from there, but since this code is unlikely to be called except during migration, I don't think it really matters. */ return index_mail_get_date(_mail, date_r, &tz); } static int pop3c_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (data->save_date == (time_t)-1) { /* FIXME: we could use a value stored in cache */ return pop3c_mail_get_received_date(_mail, date_r); } *date_r = data->save_date; return 0; } static int pop3c_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; struct message_size hdr_size, body_size; struct istream *input; if (mail->data.virtual_size != (uoff_t)-1) { /* virtual size is already known. it's the same as our (correct) physical size */ *size_r = mail->data.virtual_size; return 0; } if (index_mail_get_physical_size(_mail, size_r) == 0) { *size_r = mail->data.physical_size; return 0; } if (_mail->lookup_abort == MAIL_LOOKUP_ABORT_READ_MAIL && (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) { /* kludge: we want output for POP3 LIST with pop3_fast_size_lookups=yes. use the remote's LIST values regardless of their correctness */ if (mbox->msg_sizes == NULL) { if (pop3c_sync_get_sizes(mbox) < 0) return -1; } i_assert(_mail->seq <= mbox->msg_count); *size_r = mbox->msg_sizes[_mail->seq-1]; return 0; } /* slow way: get the whole message body */ if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0) return -1; i_assert(mail->data.physical_size != (uoff_t)-1); *size_r = mail->data.physical_size; return 0; } static void pop3c_mail_cache_size(struct index_mail *mail) { uoff_t size; if (i_stream_get_size(mail->data.stream, TRUE, &size) <= 0) return; mail->data.virtual_size = size; /* it'll be actually added to index when closing the mail in index_mail_cache_sizes() */ } static void pop3c_mail_prefetch_done(enum pop3c_command_state state, const char *reply ATTR_UNUSED, void *context) { struct pop3c_mail *pmail = context; switch (state) { case POP3C_COMMAND_STATE_OK: break; case POP3C_COMMAND_STATE_ERR: case POP3C_COMMAND_STATE_DISCONNECTED: i_stream_unref(&pmail->prefetch_stream); /* let pop3c_mail_get_stream() figure out the error handling. in case of a -ERR a retry might even work. */ break; } pmail->prefetching = FALSE; } static bool pop3c_mail_prefetch(struct mail *_mail) { struct pop3c_mail *pmail = (struct pop3c_mail *)_mail; struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; enum pop3c_capability capa; const char *cmd; if (pmail->imail.data.access_part != 0 && pmail->imail.data.stream == NULL) { capa = pop3c_client_get_capabilities(mbox->client); pmail->prefetching_body = (capa & POP3C_CAPABILITY_TOP) == 0 || (pmail->imail.data.access_part & (READ_BODY | PARSE_BODY)) != 0; if (pmail->prefetching_body) cmd = t_strdup_printf("RETR %u\r\n", _mail->seq); else cmd = t_strdup_printf("TOP %u 0\r\n", _mail->seq); pmail->prefetching = TRUE; pmail->prefetch_stream = pop3c_client_cmd_stream_async(mbox->client, cmd, pop3c_mail_prefetch_done, pmail); i_stream_set_name(pmail->prefetch_stream, t_strcut(cmd, '\r')); return !pmail->prefetching; } return index_mail_prefetch(_mail); } static int pop3c_mail_get_stream(struct mail *_mail, bool get_body, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct pop3c_mail *pmail = (struct pop3c_mail *)_mail; struct index_mail *mail = &pmail->imail; struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; enum pop3c_capability capa; const char *name, *cmd, *error; struct istream *input; bool new_stream = FALSE; if ((mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0) get_body = TRUE; while (pmail->prefetching) { /* wait for prefetch to finish */ pop3c_client_wait_one(mbox->client); } if (pmail->prefetch_stream != NULL && mail->data.stream == NULL) { mail->data.stream = pmail->prefetch_stream; pmail->prefetch_stream = NULL; new_stream = TRUE; } if (get_body && mail->data.stream != NULL) { name = i_stream_get_name(mail->data.stream); if (strncmp(name, "RETR", 4) == 0) { /* we've fetched the body */ } else if (strncmp(name, "TOP", 3) == 0) { /* we've fetched the header, but we need the body now too */ index_mail_close_streams(mail); } else { i_panic("Unexpected POP3 stream name: %s", name); } } if (mail->data.stream == NULL) { capa = pop3c_client_get_capabilities(mbox->client); if (get_body || (capa & POP3C_CAPABILITY_TOP) == 0) { cmd = t_strdup_printf("RETR %u\r\n", _mail->seq); get_body = TRUE; } else { cmd = t_strdup_printf("TOP %u 0\r\n", _mail->seq); } if (pop3c_client_cmd_stream(mbox->client, cmd, &input, &error) < 0) { mail_storage_set_error(mbox->box.storage, !pop3c_client_is_connected(mbox->client) ? MAIL_ERROR_TEMP : MAIL_ERROR_EXPUNGED, error); return -1; } mail->data.stream = input; i_stream_set_name(mail->data.stream, t_strcut(cmd, '\r')); new_stream = TRUE; } if (new_stream) { if (mail->mail.v.istream_opened != NULL) { if (mail->mail.v.istream_opened(_mail, &mail->data.stream) < 0) { index_mail_close_streams(mail); return -1; } } if (get_body) pop3c_mail_cache_size(mail); } /* if this stream is used by some filter stream, make the filter stream blocking */ mail->data.stream->blocking = TRUE; return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static int pop3c_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)_mail->box; switch (field) { case MAIL_FETCH_UIDL_BACKEND: case MAIL_FETCH_GUID: if (mbox->msg_uidls == NULL) { if (pop3c_sync_get_uidls(mbox) < 0) return -1; } i_assert(_mail->seq <= mbox->msg_count); *value_r = mbox->msg_uidls[_mail->seq-1]; return 0; default: return index_mail_get_special(_mail, field, value_r); } } struct mail_vfuncs pop3c_mail_vfuncs = { pop3c_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, pop3c_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, pop3c_mail_get_received_date, pop3c_mail_get_save_date, index_mail_get_virtual_size, pop3c_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, pop3c_mail_get_stream, index_mail_get_binary_stream, pop3c_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-storage.c0000644000175000017500000002313213165463624020324 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "mail-copy.h" #include "mail-user.h" #include "mailbox-list-private.h" #include "index-mail.h" #include "pop3c-client.h" #include "pop3c-sync.h" #include "pop3c-storage.h" #define DNS_CLIENT_SOCKET_NAME "dns-client" extern struct mail_storage pop3c_storage; extern struct mailbox pop3c_mailbox; static struct mail_storage *pop3c_storage_alloc(void) { struct pop3c_storage *storage; pool_t pool; pool = pool_alloconly_create("pop3c storage", 512+256); storage = p_new(pool, struct pop3c_storage, 1); storage->storage = pop3c_storage; storage->storage.pool = pool; return &storage->storage; } static int pop3c_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct pop3c_storage *storage = (struct pop3c_storage *)_storage; storage->set = mail_namespace_get_driver_settings(ns, _storage); if (storage->set->pop3c_host[0] == '\0') { *error_r = "missing pop3c_host"; return -1; } if (storage->set->pop3c_password[0] == '\0') { *error_r = "missing pop3c_password"; return -1; } return 0; } static struct pop3c_client * pop3c_client_create_from_set(struct mail_storage *storage, const struct pop3c_settings *set) { struct pop3c_client_settings client_set; string_t *str; i_zero(&client_set); client_set.host = set->pop3c_host; client_set.port = set->pop3c_port; client_set.username = set->pop3c_user; client_set.master_user = set->pop3c_master_user; client_set.password = set->pop3c_password; client_set.dns_client_socket_path = storage->user->set->base_dir[0] == '\0' ? "" : t_strconcat(storage->user->set->base_dir, "/", DNS_CLIENT_SOCKET_NAME, NULL); str = t_str_new(128); mail_user_set_get_temp_prefix(str, storage->user->set); client_set.temp_path_prefix = str_c(str); client_set.debug = storage->user->mail_debug; client_set.rawlog_dir = mail_user_home_expand(storage->user, set->pop3c_rawlog_dir); client_set.ssl_ca_dir = storage->set->ssl_client_ca_dir; client_set.ssl_ca_file = storage->set->ssl_client_ca_file; client_set.ssl_verify = set->pop3c_ssl_verify; if (strcmp(set->pop3c_ssl, "pop3s") == 0) client_set.ssl_mode = POP3C_CLIENT_SSL_MODE_IMMEDIATE; else if (strcmp(set->pop3c_ssl, "starttls") == 0) client_set.ssl_mode = POP3C_CLIENT_SSL_MODE_STARTTLS; else client_set.ssl_mode = POP3C_CLIENT_SSL_MODE_NONE; client_set.ssl_crypto_device = storage->set->ssl_crypto_device; return pop3c_client_init(&client_set); } static void pop3c_storage_get_list_settings(const struct mail_namespace *ns, struct mailbox_list_settings *set) { set->layout = MAILBOX_LIST_NAME_FS; if (set->root_dir != NULL && *set->root_dir != '\0' && set->index_dir == NULL) { /* we don't really care about root_dir, but we just need to get index_dir autocreated. it happens when index_dir differs from root_dir. */ set->index_dir = set->root_dir; set->root_dir = p_strconcat(ns->user->pool, set->root_dir, "/.", NULL); } } static struct mailbox * pop3c_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct pop3c_mailbox *mbox; pool_t pool; pool = pool_alloconly_create("pop3c mailbox", 1024*3); mbox = p_new(pool, struct pop3c_mailbox, 1); mbox->box = pop3c_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &pop3c_mail_vfuncs; mbox->storage = (struct pop3c_storage *)storage; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); return &mbox->box; } static int pop3c_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { if (box->inbox_any) *existence_r = MAILBOX_EXISTENCE_SELECT; else *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } static void pop3c_login_callback(enum pop3c_command_state state, const char *reply, void *context) { struct pop3c_mailbox *mbox = context; switch (state) { case POP3C_COMMAND_STATE_OK: mbox->logged_in = TRUE; break; case POP3C_COMMAND_STATE_ERR: if (strncmp(reply, "[IN-USE] ", 9) == 0) { mail_storage_set_error(mbox->box.storage, MAIL_ERROR_INUSE, reply + 9); } else { /* authentication failure probably */ mail_storage_set_error(mbox->box.storage, MAIL_ERROR_PARAMS, reply); } break; case POP3C_COMMAND_STATE_DISCONNECTED: mail_storage_set_critical(mbox->box.storage, "pop3c: Disconnected from remote server"); break; } } static int pop3c_mailbox_open(struct mailbox *box) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)box; if (strcmp(box->name, "INBOX") != 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } if (index_storage_mailbox_open(box, FALSE) < 0) return -1; mbox->client = pop3c_client_create_from_set(box->storage, mbox->storage->set); pop3c_client_login(mbox->client, pop3c_login_callback, mbox); pop3c_client_wait_one(mbox->client); return mbox->logged_in ? 0 : -1; } static void pop3c_mailbox_close(struct mailbox *box) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)box; if (mbox->uidl_pool != NULL) pool_unref(&mbox->uidl_pool); i_free_and_null(mbox->msg_uids); i_free_and_null(mbox->msg_sizes); pop3c_client_deinit(&mbox->client); index_storage_mailbox_close(box); } static int pop3c_mailbox_create(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED, bool directory ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "POP3 mailbox creation isn't supported"); return -1; } static int pop3c_mailbox_update(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED) { if (!guid_128_is_empty(update->mailbox_guid) || update->uid_validity != 0 || update->min_next_uid != 0 || update->min_first_recent_uid != 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "POP3 mailbox update isn't supported"); } return index_storage_mailbox_update(box, update); } static int pop3c_mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)box; if (index_storage_get_status(box, items, status_r) < 0) return -1; if ((pop3c_client_get_capabilities(mbox->client) & POP3C_CAPABILITY_UIDL) == 0) status_r->have_guids = FALSE; return 0; } static int pop3c_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { if ((items & MAILBOX_METADATA_GUID) != 0) { /* a bit ugly way to do this, but better than nothing for now. FIXME: if indexes are enabled, keep this there. */ mail_generate_guid_128_hash(box->name, metadata_r->guid); items &= ~MAILBOX_METADATA_GUID; } if (items != 0) { if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; } return 0; } static void pop3c_notify_changes(struct mailbox *box ATTR_UNUSED) { } static struct mail_save_context * pop3c_save_alloc(struct mailbox_transaction_context *t) { struct mail_save_context *ctx; ctx = i_new(struct mail_save_context, 1); ctx->transaction = t; return ctx; } static int pop3c_save_begin(struct mail_save_context *ctx, struct istream *input ATTR_UNUSED) { mail_storage_set_error(ctx->transaction->box->storage, MAIL_ERROR_NOTPOSSIBLE, "POP3 doesn't support saving mails"); return -1; } static int pop3c_save_continue(struct mail_save_context *ctx ATTR_UNUSED) { return -1; } static int pop3c_save_finish(struct mail_save_context *ctx) { index_save_context_free(ctx); return -1; } static void pop3c_save_cancel(struct mail_save_context *ctx) { index_save_context_free(ctx); } static bool pop3c_storage_is_inconsistent(struct mailbox *box) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)box; return index_storage_is_inconsistent(box) || !pop3c_client_is_connected(mbox->client); } struct mail_storage pop3c_storage = { .name = POP3C_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_NO_ROOT | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS, .v = { pop3c_get_setting_parser_info, pop3c_storage_alloc, pop3c_storage_create, index_storage_destroy, NULL, pop3c_storage_get_list_settings, NULL, pop3c_mailbox_alloc, NULL, NULL, } }; struct mailbox pop3c_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, pop3c_mailbox_exists, pop3c_mailbox_open, pop3c_mailbox_close, index_storage_mailbox_free, pop3c_mailbox_create, pop3c_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, pop3c_mailbox_get_status, pop3c_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, pop3c_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, pop3c_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, pop3c_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, pop3c_save_alloc, pop3c_save_begin, pop3c_save_continue, pop3c_save_finish, pop3c_save_cancel, mail_storage_copy, NULL, NULL, NULL, pop3c_storage_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-settings.h0000644000175000017500000000114413123174404020511 00000000000000#ifndef POP3C_SETTINGS_H #define POP3C_SETTINGS_H #include "net.h" /* */ enum pop3c_features { POP3C_FEATURE_NO_PIPELINING = 0x1, }; /* */ struct pop3c_settings { const char *pop3c_host; in_port_t pop3c_port; const char *pop3c_user; const char *pop3c_master_user; const char *pop3c_password; const char *pop3c_ssl; bool pop3c_ssl_verify; const char *pop3c_rawlog_dir; bool pop3c_quick_received_date; const char *pop3c_features; enum pop3c_features parsed_features; }; const struct setting_parser_info *pop3c_get_setting_parser_info(void); #endif dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-client.c0000644000175000017500000006104613165463624020144 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "istream-chain.h" #include "istream-dot.h" #include "istream-seekable.h" #include "ostream.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "safe-mkstemp.h" #include "base64.h" #include "str.h" #include "dns-lookup.h" #include "pop3c-client.h" #include #define POP3C_MAX_INBUF_SIZE (1024*32) #define POP3C_DNS_LOOKUP_TIMEOUT_MSECS (1000*30) #define POP3C_CONNECT_TIMEOUT_MSECS (1000*30) #define POP3C_COMMAND_TIMEOUT_MSECS (1000*60*5) enum pop3c_client_state { /* No connection */ POP3C_CLIENT_STATE_DISCONNECTED = 0, /* Trying to connect */ POP3C_CLIENT_STATE_CONNECTING, POP3C_CLIENT_STATE_STARTTLS, /* Connected, trying to authenticate */ POP3C_CLIENT_STATE_USER, POP3C_CLIENT_STATE_AUTH, POP3C_CLIENT_STATE_PASS, /* Post-authentication, asking for capabilities */ POP3C_CLIENT_STATE_CAPA, /* Authenticated, ready to accept commands */ POP3C_CLIENT_STATE_DONE }; struct pop3c_client_sync_cmd_ctx { enum pop3c_command_state state; char *reply; }; struct pop3c_client_cmd { struct istream *input; struct istream_chain *chain; bool reading_dot; pop3c_cmd_callback_t *callback; void *context; }; struct pop3c_client { pool_t pool; struct pop3c_client_settings set; struct ssl_iostream_context *ssl_ctx; struct ip_addr ip; int fd; struct io *io; struct istream *input, *raw_input; struct ostream *output, *raw_output; struct ssl_iostream *ssl_iostream; struct timeout *to; struct dns_lookup *dns_lookup; enum pop3c_client_state state; enum pop3c_capability capabilities; const char *auth_mech; pop3c_login_callback_t *login_callback; void *login_context; ARRAY(struct pop3c_client_cmd) commands; const char *input_line; struct istream *dot_input; unsigned int running:1; }; static void pop3c_dns_callback(const struct dns_lookup_result *result, struct pop3c_client *client); static void pop3c_client_connect_ip(struct pop3c_client *client); static int pop3c_client_ssl_init(struct pop3c_client *client); static void pop3c_client_input(struct pop3c_client *client); struct pop3c_client * pop3c_client_init(const struct pop3c_client_settings *set) { struct pop3c_client *client; struct ssl_iostream_settings ssl_set; const char *error; pool_t pool; pool = pool_alloconly_create("pop3c client", 1024); client = p_new(pool, struct pop3c_client, 1); client->pool = pool; client->fd = -1; p_array_init(&client->commands, pool, 16); client->set.debug = set->debug; client->set.host = p_strdup(pool, set->host); client->set.port = set->port; client->set.master_user = p_strdup_empty(pool, set->master_user); client->set.username = p_strdup(pool, set->username); client->set.password = p_strdup(pool, set->password); client->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); client->set.temp_path_prefix = p_strdup(pool, set->temp_path_prefix); client->set.rawlog_dir = p_strdup(pool, set->rawlog_dir); if (set->ssl_mode != POP3C_CLIENT_SSL_MODE_NONE) { client->set.ssl_mode = set->ssl_mode; client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir); client->set.ssl_ca_file = p_strdup(pool, set->ssl_ca_file); client->set.ssl_verify = set->ssl_verify; i_zero(&ssl_set); ssl_set.ca_dir = set->ssl_ca_dir; ssl_set.ca_file = set->ssl_ca_file; ssl_set.verify_remote_cert = set->ssl_verify; ssl_set.crypto_device = set->ssl_crypto_device; if (ssl_iostream_context_init_client(&ssl_set, &client->ssl_ctx, &error) < 0) { i_error("pop3c(%s:%u): Couldn't initialize SSL context: %s", set->host, set->port, error); } } return client; } static void client_login_callback(struct pop3c_client *client, enum pop3c_command_state state, const char *reason) { pop3c_login_callback_t *callback = client->login_callback; void *context = client->login_context; if (client->login_callback != NULL) { client->login_callback = NULL; client->login_context = NULL; callback(state, reason, context); } } static void pop3c_client_async_callback(struct pop3c_client *client, enum pop3c_command_state state, const char *reply) { struct pop3c_client_cmd *cmd, cmd_copy; bool running = client->running; i_assert(reply != NULL); i_assert(array_count(&client->commands) > 0); cmd = array_idx_modifiable(&client->commands, 0); if (cmd->input != NULL && state == POP3C_COMMAND_STATE_OK && !cmd->reading_dot) { /* read the full input into seekable-istream before calling the callback */ i_assert(client->dot_input == NULL); i_stream_chain_append(cmd->chain, client->input); client->dot_input = cmd->input; cmd->reading_dot = TRUE; return; } cmd_copy = *cmd; array_delete(&client->commands, 0, 1); if (cmd_copy.input != NULL) { i_stream_seek(cmd_copy.input, 0); i_stream_unref(&cmd_copy.input); } if (cmd_copy.callback != NULL) cmd_copy.callback(state, reply, cmd_copy.context); if (running) io_loop_stop(current_ioloop); } static void pop3c_client_async_callback_disconnected(struct pop3c_client *client) { pop3c_client_async_callback(client, POP3C_COMMAND_STATE_DISCONNECTED, "Disconnected"); } static void pop3c_client_disconnect(struct pop3c_client *client) { client->state = POP3C_CLIENT_STATE_DISCONNECTED; if (client->running) io_loop_stop(current_ioloop); if (client->dns_lookup != NULL) dns_lookup_abort(&client->dns_lookup); if (client->to != NULL) timeout_remove(&client->to); if (client->io != NULL) io_remove(&client->io); if (client->input != NULL) i_stream_destroy(&client->input); if (client->output != NULL) o_stream_destroy(&client->output); if (client->ssl_iostream != NULL) ssl_iostream_unref(&client->ssl_iostream); if (client->fd != -1) { if (close(client->fd) < 0) i_error("close(pop3c) failed: %m"); client->fd = -1; } while (array_count(&client->commands) > 0) pop3c_client_async_callback_disconnected(client); client_login_callback(client, POP3C_COMMAND_STATE_DISCONNECTED, "Disconnected"); } void pop3c_client_deinit(struct pop3c_client **_client) { struct pop3c_client *client = *_client; pop3c_client_disconnect(client); if (client->ssl_ctx != NULL) ssl_iostream_context_deinit(&client->ssl_ctx); pool_unref(&client->pool); } static void pop3c_client_ioloop_changed(struct pop3c_client *client) { if (client->to != NULL) client->to = io_loop_move_timeout(&client->to); if (client->io != NULL) client->io = io_loop_move_io(&client->io); if (client->output != NULL) o_stream_switch_ioloop(client->output); } static void pop3c_client_timeout(struct pop3c_client *client) { switch (client->state) { case POP3C_CLIENT_STATE_CONNECTING: i_error("pop3c(%s): connect(%s, %u) timed out after %u seconds", client->set.host, net_ip2addr(&client->ip), client->set.port, POP3C_CONNECT_TIMEOUT_MSECS/1000); break; case POP3C_CLIENT_STATE_DONE: i_error("pop3c(%s): Command timed out after %u seconds", client->set.host, POP3C_COMMAND_TIMEOUT_MSECS/1000); break; default: i_error("pop3c(%s): Authentication timed out after %u seconds", client->set.host, POP3C_CONNECT_TIMEOUT_MSECS/1000); break; } pop3c_client_disconnect(client); } static int pop3c_client_dns_lookup(struct pop3c_client *client) { struct dns_lookup_settings dns_set; i_assert(client->state == POP3C_CLIENT_STATE_CONNECTING); if (client->set.dns_client_socket_path[0] == '\0') { struct ip_addr *ips; unsigned int ips_count; int ret; ret = net_gethostbyname(client->set.host, &ips, &ips_count); if (ret != 0) { i_error("pop3c(%s): net_gethostbyname() failed: %s", client->set.host, net_gethosterror(ret)); return -1; } i_assert(ips_count > 0); client->ip = ips[0]; pop3c_client_connect_ip(client); } else { i_zero(&dns_set); dns_set.dns_client_socket_path = client->set.dns_client_socket_path; dns_set.timeout_msecs = POP3C_DNS_LOOKUP_TIMEOUT_MSECS; if (dns_lookup(client->set.host, &dns_set, pop3c_dns_callback, client, &client->dns_lookup) < 0) return -1; } return 0; } void pop3c_client_wait_one(struct pop3c_client *client) { struct ioloop *ioloop, *prev_ioloop = current_ioloop; bool timeout_added = FALSE, failed = FALSE; if (client->state == POP3C_CLIENT_STATE_DISCONNECTED && array_count(&client->commands) > 0) { while (array_count(&client->commands) > 0) pop3c_client_async_callback_disconnected(client); } i_assert(client->fd != -1 || client->state == POP3C_CLIENT_STATE_CONNECTING); i_assert(array_count(&client->commands) > 0 || client->state == POP3C_CLIENT_STATE_CONNECTING); ioloop = io_loop_create(); pop3c_client_ioloop_changed(client); if (client->ip.family == 0) { /* we're connecting, start DNS lookup after our ioloop is created */ if (pop3c_client_dns_lookup(client) < 0) failed = TRUE; } else if (client->to == NULL) { client->to = timeout_add(POP3C_COMMAND_TIMEOUT_MSECS, pop3c_client_timeout, client); timeout_added = TRUE; } if (!failed) { client->running = TRUE; io_loop_run(ioloop); client->running = FALSE; } if (timeout_added && client->to != NULL) timeout_remove(&client->to); io_loop_set_current(prev_ioloop); pop3c_client_ioloop_changed(client); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); } static void pop3c_client_starttls(struct pop3c_client *client) { o_stream_nsend_str(client->output, "STLS\r\n"); client->state = POP3C_CLIENT_STATE_STARTTLS; } static void pop3c_client_authenticate1(struct pop3c_client *client) { const struct pop3c_client_settings *set = &client->set; if (client->set.debug) { if (set->master_user == NULL) { i_debug("pop3c(%s): Authenticating as '%s' (with USER+PASS)", client->set.host, set->username); } else { i_debug("pop3c(%s): Authenticating as master user '%s' for user '%s' (with SASL PLAIN)", client->set.host, set->master_user, set->username); } } if (set->master_user == NULL) { o_stream_nsend_str(client->output, t_strdup_printf("USER %s\r\n", set->username)); client->state = POP3C_CLIENT_STATE_USER; } else { client->state = POP3C_CLIENT_STATE_AUTH; o_stream_nsend_str(client->output, "AUTH PLAIN\r\n"); } } static const char * pop3c_client_get_sasl_plain_request(struct pop3c_client *client) { const struct pop3c_client_settings *set = &client->set; string_t *in, *out; in = t_str_new(128); if (set->master_user != NULL) { str_append(in, set->username); str_append_c(in, '\0'); str_append(in, set->master_user); } else { str_append_c(in, '\0'); str_append(in, set->username); } str_append_c(in, '\0'); str_append(in, set->password); out = t_str_new(128); base64_encode(str_data(in), str_len(in), out); str_append(out, "\r\n"); return str_c(out); } static void pop3c_client_login_finished(struct pop3c_client *client) { io_remove(&client->io); client->io = io_add(client->fd, IO_READ, pop3c_client_input, client); timeout_remove(&client->to); client->state = POP3C_CLIENT_STATE_DONE; if (client->running) io_loop_stop(current_ioloop); } static int pop3c_client_prelogin_input_line(struct pop3c_client *client, const char *line) { bool success = line[0] == '+'; const char *reply; switch (client->state) { case POP3C_CLIENT_STATE_CONNECTING: if (!success) { i_error("pop3c(%s): Server sent invalid banner: %s", client->set.host, line); return -1; } if (client->set.ssl_mode == POP3C_CLIENT_SSL_MODE_STARTTLS) pop3c_client_starttls(client); else pop3c_client_authenticate1(client); break; case POP3C_CLIENT_STATE_STARTTLS: if (!success) { i_error("pop3c(%s): STLS failed: %s", client->set.host, line); return -1; } if (pop3c_client_ssl_init(client) < 0) pop3c_client_disconnect(client); break; case POP3C_CLIENT_STATE_USER: if (!success) { i_error("pop3c(%s): USER failed: %s", client->set.host, line); return -1; } /* the PASS reply can take a long time. switch to command timeout. */ timeout_remove(&client->to); client->to = timeout_add(POP3C_COMMAND_TIMEOUT_MSECS, pop3c_client_timeout, client); o_stream_nsend_str(client->output, t_strdup_printf("PASS %s\r\n", client->set.password)); client->state = POP3C_CLIENT_STATE_PASS; client->auth_mech = "USER+PASS"; break; case POP3C_CLIENT_STATE_AUTH: if (line[0] != '+') { i_error("pop3c(%s): AUTH PLAIN failed: %s", client->set.host, line); return -1; } o_stream_nsend_str(client->output, pop3c_client_get_sasl_plain_request(client)); client->state = POP3C_CLIENT_STATE_PASS; client->auth_mech = "AUTH PLAIN"; break; case POP3C_CLIENT_STATE_PASS: if (client->login_callback != NULL) { reply = strncasecmp(line, "+OK ", 4) == 0 ? line + 4 : strncasecmp(line, "-ERR ", 5) == 0 ? line + 5 : line; client_login_callback(client, success ? POP3C_COMMAND_STATE_OK : POP3C_COMMAND_STATE_ERR, reply); } else if (!success) { i_error("pop3c(%s): Authentication via %s failed: %s", client->set.host, client->auth_mech, line); } if (!success) return -1; o_stream_nsend_str(client->output, "CAPA\r\n"); client->state = POP3C_CLIENT_STATE_CAPA; break; case POP3C_CLIENT_STATE_CAPA: if (strncasecmp(line, "-ERR", 4) == 0) { /* CAPA command not supported. some commands still support UIDL though. */ client->capabilities |= POP3C_CAPABILITY_UIDL; pop3c_client_login_finished(client); break; } else if (strcmp(line, ".") == 0) { pop3c_client_login_finished(client); break; } if ((client->set.parsed_features & POP3C_FEATURE_NO_PIPELINING) == 0 && strcasecmp(line, "PIPELINING") == 0) client->capabilities |= POP3C_CAPABILITY_PIPELINING; else if (strcasecmp(line, "TOP") == 0) client->capabilities |= POP3C_CAPABILITY_TOP; else if (strcasecmp(line, "UIDL") == 0) client->capabilities |= POP3C_CAPABILITY_UIDL; break; case POP3C_CLIENT_STATE_DISCONNECTED: case POP3C_CLIENT_STATE_DONE: i_unreached(); } return 0; } static void pop3c_client_prelogin_input(struct pop3c_client *client) { const char *line, *errstr; i_assert(client->state != POP3C_CLIENT_STATE_DONE); /* we need to read as much as we can with SSL streams to avoid hanging */ while ((line = i_stream_read_next_line(client->input)) != NULL) { if (pop3c_client_prelogin_input_line(client, line) < 0) { pop3c_client_disconnect(client); return; } } if (client->input->closed || client->input->eof || client->input->stream_errno != 0) { /* disconnected */ if (client->ssl_iostream == NULL) { i_error("pop3c(%s): Server disconnected unexpectedly", client->set.host); } else { errstr = ssl_iostream_get_last_error(client->ssl_iostream); if (errstr == NULL) { errstr = client->input->stream_errno == 0 ? "EOF" : strerror(client->input->stream_errno); } i_error("pop3c(%s): Server disconnected: %s", client->set.host, errstr); } pop3c_client_disconnect(client); } } static int pop3c_client_ssl_handshaked(const char **error_r, void *context) { struct pop3c_client *client = context; const char *error; if (ssl_iostream_check_cert_validity(client->ssl_iostream, client->set.host, &error) == 0) { if (client->set.debug) { i_debug("pop3c(%s): SSL handshake successful", client->set.host); } return 0; } else if (!client->set.ssl_verify) { if (client->set.debug) { i_debug("pop3c(%s): SSL handshake successful, " "ignoring invalid certificate: %s", client->set.host, error); } return 0; } else { *error_r = error; return -1; } } static int pop3c_client_ssl_init(struct pop3c_client *client) { struct ssl_iostream_settings ssl_set; const char *error; if (client->ssl_ctx == NULL) { i_error("pop3c(%s): No SSL context", client->set.host); return -1; } i_zero(&ssl_set); if (client->set.ssl_verify) { ssl_set.verbose_invalid_cert = TRUE; ssl_set.verify_remote_cert = TRUE; ssl_set.require_valid_cert = TRUE; } if (client->set.debug) i_debug("pop3c(%s): Starting SSL handshake", client->set.host); if (client->raw_input != client->input) { /* recreate rawlog after STARTTLS */ i_stream_ref(client->raw_input); o_stream_ref(client->raw_output); i_stream_destroy(&client->input); o_stream_destroy(&client->output); client->input = client->raw_input; client->output = client->raw_output; } if (io_stream_create_ssl_client(client->ssl_ctx, client->set.host, &ssl_set, &client->input, &client->output, &client->ssl_iostream, &error) < 0) { i_error("pop3c(%s): Couldn't initialize SSL client: %s", client->set.host, error); return -1; } ssl_iostream_set_handshake_callback(client->ssl_iostream, pop3c_client_ssl_handshaked, client); if (ssl_iostream_handshake(client->ssl_iostream) < 0) { i_error("pop3c(%s): SSL handshake failed: %s", client->set.host, ssl_iostream_get_last_error(client->ssl_iostream)); return -1; } if (*client->set.rawlog_dir != '\0') { iostream_rawlog_create(client->set.rawlog_dir, &client->input, &client->output); } return 0; } static void pop3c_client_connected(struct pop3c_client *client) { int err; err = net_geterror(client->fd); if (err != 0) { i_error("pop3c(%s): connect(%s, %u) failed: %s", client->set.host, net_ip2addr(&client->ip), client->set.port, strerror(err)); pop3c_client_disconnect(client); return; } io_remove(&client->io); client->io = io_add(client->fd, IO_READ, pop3c_client_prelogin_input, client); if (client->set.ssl_mode == POP3C_CLIENT_SSL_MODE_IMMEDIATE) { if (pop3c_client_ssl_init(client) < 0) pop3c_client_disconnect(client); } } static void pop3c_client_connect_ip(struct pop3c_client *client) { client->fd = net_connect_ip(&client->ip, client->set.port, NULL); if (client->fd == -1) { pop3c_client_disconnect(client); return; } client->input = client->raw_input = i_stream_create_fd(client->fd, POP3C_MAX_INBUF_SIZE, FALSE); client->output = client->raw_output = o_stream_create_fd(client->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); if (*client->set.rawlog_dir != '\0' && client->set.ssl_mode != POP3C_CLIENT_SSL_MODE_IMMEDIATE) { iostream_rawlog_create(client->set.rawlog_dir, &client->input, &client->output); } client->io = io_add(client->fd, IO_WRITE, pop3c_client_connected, client); client->to = timeout_add(POP3C_CONNECT_TIMEOUT_MSECS, pop3c_client_timeout, client); if (client->set.debug) { i_debug("pop3c(%s): Connecting to %s:%u", client->set.host, net_ip2addr(&client->ip), client->set.port); } } static void pop3c_dns_callback(const struct dns_lookup_result *result, struct pop3c_client *client) { client->dns_lookup = NULL; if (result->ret != 0) { i_error("pop3c(%s): dns_lookup() failed: %s", client->set.host, result->error); pop3c_client_disconnect(client); return; } i_assert(result->ips_count > 0); client->ip = result->ips[0]; pop3c_client_connect_ip(client); } void pop3c_client_login(struct pop3c_client *client, pop3c_login_callback_t *callback, void *context) { if (client->fd != -1) { i_assert(callback == NULL); return; } i_assert(client->login_callback == NULL); client->login_callback = callback; client->login_context = context; client->state = POP3C_CLIENT_STATE_CONNECTING; if (client->set.debug) i_debug("pop3c(%s): Looking up IP address", client->set.host); } bool pop3c_client_is_connected(struct pop3c_client *client) { return client->fd != -1; } enum pop3c_capability pop3c_client_get_capabilities(struct pop3c_client *client) { return client->capabilities; } static int pop3c_client_dot_input(struct pop3c_client *client) { ssize_t ret; while ((ret = i_stream_read(client->dot_input)) > 0 || ret == -2) { i_stream_skip(client->dot_input, i_stream_get_data_size(client->dot_input)); } if (ret == 0) return 0; i_assert(ret == -1); if (client->dot_input->stream_errno == 0) ret = 1; client->dot_input = NULL; if (ret > 0) { /* currently we don't actually care about preserving the +OK reply line for multi-line replies, so just return it as empty */ pop3c_client_async_callback(client, POP3C_COMMAND_STATE_OK, ""); return 1; } else { pop3c_client_async_callback_disconnected(client); return -1; } } static int pop3c_client_input_next_reply(struct pop3c_client *client) { const char *line; enum pop3c_command_state state; line = i_stream_read_next_line(client->input); if (line == NULL) return client->input->eof ? -1 : 0; if (strncasecmp(line, "+OK", 3) == 0) { line += 3; state = POP3C_COMMAND_STATE_OK; } else if (strncasecmp(line, "-ERR", 4) == 0) { line += 4; state = POP3C_COMMAND_STATE_ERR; } else { i_error("pop3c(%s): Server sent unrecognized line: %s", client->set.host, line); state = POP3C_COMMAND_STATE_ERR; } if (line[0] == ' ') line++; if (array_count(&client->commands) == 0) { i_error("pop3c(%s): Server sent line when no command was running: %s", client->set.host, line); } else { pop3c_client_async_callback(client, state, line); } return 1; } static void pop3c_client_input(struct pop3c_client *client) { int ret; if (client->to != NULL) timeout_reset(client->to); do { if (client->dot_input != NULL) { /* continue reading the current multiline reply */ if ((ret = pop3c_client_dot_input(client)) == 0) return; } else { ret = pop3c_client_input_next_reply(client); } } while (ret > 0); if (ret < 0) { i_error("pop3c(%s): Server disconnected unexpectedly", client->set.host); pop3c_client_disconnect(client); } } static void pop3c_client_cmd_reply(enum pop3c_command_state state, const char *reply, void *context) { struct pop3c_client_sync_cmd_ctx *ctx = context; i_assert(ctx->reply == NULL); ctx->state = state; ctx->reply = i_strdup(reply); } int pop3c_client_cmd_line(struct pop3c_client *client, const char *cmdline, const char **reply_r) { struct pop3c_client_sync_cmd_ctx ctx; i_zero(&ctx); pop3c_client_cmd_line_async(client, cmdline, pop3c_client_cmd_reply, &ctx); while (ctx.reply == NULL) pop3c_client_wait_one(client); *reply_r = t_strdup(ctx.reply); i_free(ctx.reply); return ctx.state == POP3C_COMMAND_STATE_OK ? 0 : -1; } struct pop3c_client_cmd * pop3c_client_cmd_line_async(struct pop3c_client *client, const char *cmdline, pop3c_cmd_callback_t *callback, void *context) { struct pop3c_client_cmd *cmd; if ((client->capabilities & POP3C_CAPABILITY_PIPELINING) == 0) { while (array_count(&client->commands) > 0) pop3c_client_wait_one(client); } i_assert(client->state == POP3C_CLIENT_STATE_DISCONNECTED || client->state == POP3C_CLIENT_STATE_DONE); if (client->state == POP3C_CLIENT_STATE_DONE) o_stream_nsend_str(client->output, cmdline); cmd = array_append_space(&client->commands); cmd->callback = callback; cmd->context = context; return cmd; } void pop3c_client_cmd_line_async_nocb(struct pop3c_client *client, const char *cmdline) { pop3c_client_cmd_line_async(client, cmdline, NULL, NULL); } static int seekable_fd_callback(const char **path_r, void *context) { struct pop3c_client *client = context; string_t *path; int fd; path = t_str_new(128); str_append(path, client->set.temp_path_prefix); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } int pop3c_client_cmd_stream(struct pop3c_client *client, const char *cmdline, struct istream **input_r, const char **error_r) { struct pop3c_client_sync_cmd_ctx ctx; const char *reply; if (client->state == POP3C_CLIENT_STATE_DISCONNECTED) { *error_r = "Disconnected from server"; return -1; } i_zero(&ctx); *input_r = pop3c_client_cmd_stream_async(client, cmdline, pop3c_client_cmd_reply, &ctx); while (ctx.reply == NULL) pop3c_client_wait_one(client); reply = t_strdup(ctx.reply); i_free(ctx.reply); if (ctx.state == POP3C_COMMAND_STATE_OK) return 0; i_stream_unref(input_r); *error_r = reply; return -1; } struct istream * pop3c_client_cmd_stream_async(struct pop3c_client *client, const char *cmdline, pop3c_cmd_callback_t *callback, void *context) { struct istream *input, *inputs[2]; struct pop3c_client_cmd *cmd; cmd = pop3c_client_cmd_line_async(client, cmdline, callback, context); input = i_stream_create_chain(&cmd->chain); inputs[0] = i_stream_create_dot(input, TRUE); inputs[1] = NULL; cmd->input = i_stream_create_seekable(inputs, POP3C_MAX_INBUF_SIZE, seekable_fd_callback, client); i_stream_unref(&input); i_stream_unref(&inputs[0]); i_stream_ref(cmd->input); return cmd->input; } dovecot-2.2.33.2/src/lib-storage/index/pop3c/pop3c-sync.c0000644000175000017500000002325013165463624017635 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "bsearch-insert-pos.h" #include "str.h" #include "strnum.h" #include "index-mail.h" #include "pop3c-client.h" #include "pop3c-storage.h" #include "pop3c-sync.h" struct pop3c_sync_msg { uint32_t seq; const char *uidl; }; ARRAY_DEFINE_TYPE(pop3c_sync_msg, struct pop3c_sync_msg); int pop3c_sync_get_uidls(struct pop3c_mailbox *mbox) { ARRAY_TYPE(const_string) uidls; struct istream *input; const char *error, *cline; char *line, *p; unsigned int seq, line_seq; if (mbox->msg_uidls != NULL) return 0; if ((pop3c_client_get_capabilities(mbox->client) & POP3C_CAPABILITY_UIDL) == 0) { mail_storage_set_error(mbox->box.storage, MAIL_ERROR_NOTPOSSIBLE, "UIDLs not supported by server"); return -1; } if (pop3c_client_cmd_stream(mbox->client, "UIDL\r\n", &input, &error) < 0) { mail_storage_set_critical(mbox->box.storage, "UIDL failed: %s", error); return -1; } mbox->uidl_pool = pool_alloconly_create("POP3 UIDLs", 1024*32); p_array_init(&uidls, mbox->uidl_pool, 64); seq = 0; while ((line = i_stream_read_next_line(input)) != NULL) { seq++; p = strchr(line, ' '); if (p == NULL) { mail_storage_set_critical(mbox->box.storage, "Invalid UIDL line: %s", line); break; } *p++ = '\0'; if (str_to_uint(line, &line_seq) < 0 || line_seq != seq) { mail_storage_set_critical(mbox->box.storage, "Unexpected UIDL seq: %s != %u", line, seq); break; } cline = p_strdup(mbox->uidl_pool, p); array_append(&uidls, &cline, 1); } i_stream_destroy(&input); if (line != NULL) { pool_unref(&mbox->uidl_pool); return -1; } if (seq == 0) { /* make msg_uidls non-NULL */ array_append_zero(&uidls); } mbox->msg_uidls = array_idx(&uidls, 0); mbox->msg_count = seq; return 0; } int pop3c_sync_get_sizes(struct pop3c_mailbox *mbox) { struct istream *input; const char *error; char *line, *p; unsigned int seq, line_seq; i_assert(mbox->msg_sizes == NULL); if (mbox->msg_uidls == NULL) { if (pop3c_sync_get_uidls(mbox) < 0) return -1; } if (mbox->msg_count == 0) { mbox->msg_sizes = i_new(uoff_t, 1); return 0; } if (pop3c_client_cmd_stream(mbox->client, "LIST\r\n", &input, &error) < 0) { mail_storage_set_critical(mbox->box.storage, "LIST failed: %s", error); return -1; } mbox->msg_sizes = i_new(uoff_t, mbox->msg_count); seq = 0; while ((line = i_stream_read_next_line(input)) != NULL) { if (++seq > mbox->msg_count) { mail_storage_set_critical(mbox->box.storage, "Too much data in LIST: %s", line); break; } p = strchr(line, ' '); if (p == NULL) { mail_storage_set_critical(mbox->box.storage, "Invalid LIST line: %s", line); break; } *p++ = '\0'; if (str_to_uint(line, &line_seq) < 0 || line_seq != seq) { mail_storage_set_critical(mbox->box.storage, "Unexpected LIST seq: %s != %u", line, seq); break; } if (str_to_uoff(p, &mbox->msg_sizes[seq-1]) < 0) { mail_storage_set_critical(mbox->box.storage, "Invalid LIST size: %s", p); break; } } i_stream_destroy(&input); if (line != NULL) { i_free_and_null(mbox->msg_sizes); return -1; } return 0; } static void pop3c_get_local_msgs(pool_t pool, ARRAY_TYPE(pop3c_sync_msg) *local_msgs, uint32_t messages_count, struct mail_cache_view *cache_view, unsigned int cache_idx) { string_t *str = t_str_new(128); struct pop3c_sync_msg msg; uint32_t seq; i_zero(&msg); for (seq = 1; seq <= messages_count; seq++) { str_truncate(str, 0); if (mail_cache_lookup_field(cache_view, str, seq, cache_idx) > 0) msg.uidl = p_strdup(pool, str_c(str)); msg.seq = seq; array_idx_set(local_msgs, seq-1, &msg); } } static void pop3c_get_remote_msgs(ARRAY_TYPE(pop3c_sync_msg) *remote_msgs, struct pop3c_mailbox *mbox) { struct pop3c_sync_msg *msg; uint32_t seq; for (seq = 1; seq <= mbox->msg_count; seq++) { msg = array_append_space(remote_msgs); msg->seq = seq; msg->uidl = mbox->msg_uidls[seq-1]; } } static int pop3c_sync_msg_uidl_cmp(const struct pop3c_sync_msg *msg1, const struct pop3c_sync_msg *msg2) { return null_strcmp(msg1->uidl, msg2->uidl); } static void pop3c_sync_messages(struct pop3c_mailbox *mbox, struct mail_index_view *sync_view, struct mail_index_transaction *sync_trans, struct mail_cache_view *cache_view) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(&mbox->box); const struct mail_index_header *hdr; struct mail_cache_transaction_ctx *cache_trans; ARRAY_TYPE(pop3c_sync_msg) local_msgs, remote_msgs; const struct pop3c_sync_msg *lmsg, *rmsg; uint32_t seq1, seq2, next_uid; unsigned int lidx, ridx, lcount, rcount; unsigned int cache_idx = ibox->cache_fields[MAIL_CACHE_POP3_UIDL].idx; pool_t pool; i_assert(mbox->msg_uids == NULL); /* set our uidvalidity */ hdr = mail_index_get_header(sync_view); if (hdr->uid_validity == 0) { uint32_t uid_validity = ioloop_time; mail_index_update_header(sync_trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } pool = pool_alloconly_create(MEMPOOL_GROWING"pop3c sync", 10240); p_array_init(&local_msgs, pool, hdr->messages_count); pop3c_get_local_msgs(pool, &local_msgs, hdr->messages_count, cache_view, cache_idx); p_array_init(&remote_msgs, pool, mbox->msg_count); pop3c_get_remote_msgs(&remote_msgs, mbox); /* sort the messages by UIDLs, because some servers reorder messages */ array_sort(&local_msgs, pop3c_sync_msg_uidl_cmp); array_sort(&remote_msgs, pop3c_sync_msg_uidl_cmp); /* skip over existing messages with matching UIDLs and expunge the ones that no longer exist in remote. */ mbox->msg_uids = mbox->msg_count == 0 ? i_new(uint32_t, 1) : /* avoid malloc(0) assert */ i_new(uint32_t, mbox->msg_count); cache_trans = mail_cache_get_transaction(cache_view, sync_trans); lmsg = array_get(&local_msgs, &lcount); rmsg = array_get(&remote_msgs, &rcount); next_uid = hdr->next_uid; lidx = ridx = 0; while (lidx < lcount || ridx < rcount) { uint32_t lseq = lidx < lcount ? lmsg[lidx].seq : 0; uint32_t rseq = ridx < rcount ? rmsg[ridx].seq : 0; int ret; if (lidx >= lcount) ret = 1; else if (ridx >= rcount || lmsg[lidx].uidl == NULL) ret = -1; else ret = strcmp(lmsg[lidx].uidl, rmsg[ridx].uidl); if (ret < 0) { /* message expunged in remote, or we didn't have a local message's UIDL in cache. */ mail_index_expunge(sync_trans, lseq); lidx++; } else if (ret > 0) { /* new message in remote */ i_assert(mbox->msg_uids[rseq-1] == 0); mbox->msg_uids[rseq-1] = next_uid; mail_index_append(sync_trans, next_uid++, &lseq); mail_cache_add(cache_trans, lseq, cache_idx, rmsg[ridx].uidl, strlen(rmsg[ridx].uidl)+1); ridx++; } else { /* UIDL matched */ i_assert(mbox->msg_uids[rseq-1] == 0); mail_index_lookup_uid(sync_view, lseq, &mbox->msg_uids[rseq-1]); lidx++; ridx++; } } /* mark the newly seen messages as recent */ if (mail_index_lookup_seq_range(sync_view, hdr->first_recent_uid, hdr->next_uid, &seq1, &seq2)) mailbox_recent_flags_set_seqs(&mbox->box, sync_view, seq1, seq2); pool_unref(&pool); } int pop3c_sync(struct pop3c_mailbox *mbox) { struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view, *trans_view; struct mail_index_transaction *sync_trans; struct mail_index_sync_rec sync_rec; struct mail_cache_view *cache_view = NULL; enum mail_index_sync_flags sync_flags; unsigned int idx; string_t *str; const char *reply; int ret; bool deletions = FALSE; if (pop3c_sync_get_uidls(mbox) < 0) return -1; sync_flags = index_storage_get_sync_flags(&mbox->box) | MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY; ret = mail_index_sync_begin(mbox->box.index, &index_sync_ctx, &sync_view, &sync_trans, sync_flags); if (ret <= 0) { if (ret < 0) mailbox_set_index_error(&mbox->box); return ret; } if (mbox->msg_uids == NULL) { trans_view = mail_index_transaction_open_updated_view(sync_trans); cache_view = mail_cache_view_open(mbox->box.cache, trans_view); pop3c_sync_messages(mbox, sync_view, sync_trans, cache_view); } /* mark expunges messages as deleted in this pop3 session, if those exist */ str = t_str_new(32); while (mail_index_sync_next(index_sync_ctx, &sync_rec)) { if (sync_rec.type != MAIL_INDEX_SYNC_TYPE_EXPUNGE) continue; if (!bsearch_insert_pos(&sync_rec.uid1, mbox->msg_uids, mbox->msg_count, sizeof(uint32_t), uint32_cmp, &idx)) { /* no such messages in this session */ continue; } for (; idx < mbox->msg_count; idx++) { i_assert(mbox->msg_uids[idx] >= sync_rec.uid1); if (mbox->msg_uids[idx] > sync_rec.uid2) break; str_truncate(str, 0); str_printfa(str, "DELE %u\r\n", idx+1); pop3c_client_cmd_line_async_nocb(mbox->client, str_c(str)); deletions = TRUE; } } if (mail_index_sync_commit(&index_sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); return -1; } if (cache_view != NULL) { mail_cache_view_close(&cache_view); mail_index_view_close(&trans_view); } if (deletions) { if (pop3c_client_cmd_line(mbox->client, "QUIT\r\n", &reply) < 0) { mail_storage_set_error(mbox->box.storage, MAIL_ERROR_TEMP, reply); return -1; } } return 0; } struct mailbox_sync_context * pop3c_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct pop3c_mailbox *mbox = (struct pop3c_mailbox *)box; int ret = 0; if ((flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0 && mbox->msg_uidls == NULL) { /* FIXME: reconnect */ } ret = pop3c_sync(mbox); return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/pop3c/Makefile.am0000644000175000017500000000116713123174404017517 00000000000000noinst_LTLIBRARIES = libstorage_pop3c.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_pop3c_la_SOURCES = \ pop3c-client.c \ pop3c-mail.c \ pop3c-settings.c \ pop3c-storage.c \ pop3c-sync.c headers = \ pop3c-client.h \ pop3c-settings.h \ pop3c-storage.h \ pop3c-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-mail.c0000644000175000017500000021347413165463624016653 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "ioloop.h" #include "istream.h" #include "hex-binary.h" #include "str.h" #include "message-date.h" #include "message-part-data.h" #include "message-part-serialize.h" #include "message-parser.h" #include "message-snippet.h" #include "imap-bodystructure.h" #include "imap-envelope.h" #include "mail-cache.h" #include "mail-index-modseq.h" #include "index-storage.h" #include "istream-mail.h" #include "index-mail.h" #include #define BODY_SNIPPET_ALGO_V1 "1" #define BODY_SNIPPET_MAX_CHARS 100 struct mail_cache_field global_cache_fields[MAIL_INDEX_CACHE_FIELD_COUNT] = { { .name = "flags", .type = MAIL_CACHE_FIELD_BITMASK, .field_size = sizeof(uint32_t) }, { .name = "date.sent", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(struct mail_sent_date) }, { .name = "date.received", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(uint32_t) }, { .name = "date.save", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(uint32_t) }, { .name = "size.virtual", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(uoff_t) }, { .name = "size.physical", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(uoff_t) }, { .name = "imap.body", .type = MAIL_CACHE_FIELD_STRING }, { .name = "imap.bodystructure", .type = MAIL_CACHE_FIELD_STRING }, { .name = "imap.envelope", .type = MAIL_CACHE_FIELD_STRING }, { .name = "pop3.uidl", .type = MAIL_CACHE_FIELD_STRING }, { .name = "pop3.order", .type = MAIL_CACHE_FIELD_FIXED_SIZE, .field_size = sizeof(uint32_t) }, { .name = "guid", .type = MAIL_CACHE_FIELD_STRING }, { .name = "mime.parts", .type = MAIL_CACHE_FIELD_VARIABLE_SIZE }, { .name = "binary.parts", .type = MAIL_CACHE_FIELD_VARIABLE_SIZE }, { .name = "body.snippet", .type = MAIL_CACHE_FIELD_VARIABLE_SIZE } /* FIXME: for now need to update get_metadata_precache_fields() in index-status.c when adding more fields. those fields should probably just be moved here to the same struct. */ }; static void index_mail_init_data(struct index_mail *mail); static int index_mail_parse_body(struct index_mail *mail, enum index_cache_field field); int index_mail_cache_lookup_field(struct index_mail *mail, buffer_t *buf, unsigned int field_idx) { int ret; ret = mail_cache_lookup_field(mail->mail.mail.transaction->cache_view, buf, mail->data.seq, field_idx); if (ret > 0) mail->mail.mail.transaction->stats.cache_hit_count++; return ret; } static int get_serialized_parts(struct index_mail *mail, buffer_t **part_buf_r) { const unsigned int field_idx = mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; *part_buf_r = buffer_create_dynamic(pool_datastack_create(), 128); return index_mail_cache_lookup_field(mail, *part_buf_r, field_idx); } static struct message_part *get_unserialized_parts(struct index_mail *mail) { struct message_part *parts; buffer_t *part_buf; const char *error; if (get_serialized_parts(mail, &part_buf) <= 0) return NULL; parts = message_part_deserialize(mail->mail.data_pool, part_buf->data, part_buf->used, &error); if (parts == NULL) { mail_set_mail_cache_corrupted(&mail->mail.mail, "Corrupted cached mime.parts data: %s (parts=%s)", error, binary_to_hex(part_buf->data, part_buf->used)); } return parts; } static bool message_parts_have_nuls(const struct message_part *part) { for (; part != NULL; part = part->next) { if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) return TRUE; if (part->children != NULL) { if (message_parts_have_nuls(part->children)) return TRUE; } } return FALSE; } static bool get_cached_parts(struct index_mail *mail) { struct message_part *part; T_BEGIN { part = get_unserialized_parts(mail); } T_END; if (part == NULL) return FALSE; /* we know the NULs now, update them */ if (message_parts_have_nuls(part)) { mail->mail.mail.has_nuls = TRUE; mail->mail.mail.has_no_nuls = FALSE; } else { mail->mail.mail.has_nuls = FALSE; mail->mail.mail.has_no_nuls = TRUE; } mail->data.parts = part; return TRUE; } void index_mail_set_message_parts_corrupted(struct mail *mail, const char *error) { buffer_t *part_buf; const char *parts_str; if (get_serialized_parts((struct index_mail *)mail, &part_buf) <= 0) parts_str = ""; else parts_str = binary_to_hex(part_buf->data, part_buf->used); mail_set_cache_corrupted_reason(mail, MAIL_FETCH_MESSAGE_PARTS, t_strdup_printf( "Cached MIME parts don't match message during parsing: %s (parts=%s)", error, parts_str)); } static bool index_mail_get_fixed_field(struct index_mail *mail, enum index_cache_field field, void *data, size_t data_size) { const unsigned int field_idx = mail->ibox->cache_fields[field].idx; buffer_t buf; int ret; buffer_create_from_data(&buf, data, data_size); if (index_mail_cache_lookup_field(mail, &buf, field_idx) <= 0) ret = FALSE; else { i_assert(buf.used == data_size); ret = TRUE; } return ret; } bool index_mail_get_cached_uoff_t(struct index_mail *mail, enum index_cache_field field, uoff_t *size_r) { return index_mail_get_fixed_field(mail, field, size_r, sizeof(*size_r)); } static bool index_mail_get_pvt(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; if (mail->seq_pvt != 0) return TRUE; if (_mail->box->view_pvt == NULL) { /* no private view (set by view syncing) -> no private flags */ return FALSE; } if (_mail->saving) { /* mail is still being saved, it has no private flags yet */ return FALSE; } i_assert(_mail->uid != 0); index_transaction_init_pvt(_mail->transaction); if (!mail_index_lookup_seq(_mail->transaction->view_pvt, _mail->uid, &mail->seq_pvt)) mail->seq_pvt = 0; return mail->seq_pvt != 0; } enum mail_flags index_mail_get_flags(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; const struct mail_index_record *rec; enum mail_flags flags, pvt_flags_mask; rec = mail_index_lookup(_mail->transaction->view, _mail->seq); flags = rec->flags & (MAIL_FLAGS_NONRECENT | MAIL_INDEX_MAIL_FLAG_BACKEND); if (mailbox_recent_flags_have_uid(_mail->box, _mail->uid)) flags |= MAIL_RECENT; if (index_mail_get_pvt(_mail)) { /* mailbox has private flags */ pvt_flags_mask = mailbox_get_private_flags_mask(_mail->box); flags &= ~pvt_flags_mask; rec = mail_index_lookup(_mail->transaction->view_pvt, mail->seq_pvt); flags |= rec->flags & pvt_flags_mask; } return flags; } uint64_t index_mail_get_modseq(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.modseq != 0) return mail->data.modseq; mail_index_modseq_enable(_mail->box->index); mail->data.modseq = mail_index_modseq_lookup(_mail->transaction->view, _mail->seq); return mail->data.modseq; } uint64_t index_mail_get_pvt_modseq(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.pvt_modseq != 0) return mail->data.pvt_modseq; if (mailbox_open_index_pvt(_mail->box) <= 0) return 0; index_transaction_init_pvt(_mail->transaction); mail_index_modseq_enable(_mail->box->index_pvt); mail->data.pvt_modseq = mail_index_modseq_lookup(_mail->transaction->view_pvt, _mail->seq); return mail->data.pvt_modseq; } const char *const *index_mail_get_keywords(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; const char *const *names; const unsigned int *keyword_indexes; unsigned int i, count, names_count; if (array_is_created(&data->keywords)) return array_idx(&data->keywords, 0); (void)index_mail_get_keyword_indexes(_mail); keyword_indexes = array_get(&data->keyword_indexes, &count); names = array_get(mail->ibox->keyword_names, &names_count); p_array_init(&data->keywords, mail->mail.data_pool, count + 1); for (i = 0; i < count; i++) { const char *name; i_assert(keyword_indexes[i] < names_count); name = names[keyword_indexes[i]]; array_append(&data->keywords, &name, 1); } /* end with NULL */ array_append_zero(&data->keywords); return array_idx(&data->keywords, 0); } const ARRAY_TYPE(keyword_indexes) * index_mail_get_keyword_indexes(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (!array_is_created(&data->keyword_indexes)) { p_array_init(&data->keyword_indexes, mail->mail.data_pool, 32); mail_index_lookup_keywords(_mail->transaction->view, mail->data.seq, &data->keyword_indexes); } return &data->keyword_indexes; } int index_mail_get_parts(struct mail *_mail, struct message_part **parts_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; data->cache_fetch_fields |= MAIL_FETCH_MESSAGE_PARTS; if (data->parts != NULL || get_cached_parts(mail)) { *parts_r = data->parts; return 0; } if (data->parser_ctx == NULL) { const char *reason = index_mail_cache_reason(_mail, "mime parts"); if (index_mail_parse_headers(mail, NULL, reason) < 0) return -1; /* parts may be set now as a result of some plugin */ } if (data->parts == NULL) { data->save_message_parts = TRUE; if (index_mail_parse_body(mail, 0) < 0) return -1; } *parts_r = data->parts; return 0; } int index_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; data->cache_fetch_fields |= MAIL_FETCH_RECEIVED_DATE; if (data->received_date == (time_t)-1) { uint32_t t; if (index_mail_get_fixed_field(mail, MAIL_CACHE_RECEIVED_DATE, &t, sizeof(t))) data->received_date = t; } *date_r = data->received_date; return *date_r == (time_t)-1 ? -1 : 0; } int index_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; data->cache_fetch_fields |= MAIL_FETCH_SAVE_DATE; if (data->save_date == (time_t)-1) { uint32_t t; if (index_mail_get_fixed_field(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t))) data->save_date = t; } *date_r = data->save_date; return *date_r == (time_t)-1 ? -1 : 0; } static int index_mail_cache_sent_date(struct index_mail *mail) { struct index_mail_data *data = &mail->data; const char *str; time_t t; int ret, tz; if (data->sent_date.time != (uint32_t)-1) return 0; if ((ret = mail_get_first_header(&mail->mail.mail, "Date", &str)) < 0) return ret; if (ret == 0 || !message_date_parse((const unsigned char *)str, strlen(str), &t, &tz)) { /* 0 = not found / invalid */ t = 0; tz = 0; } data->sent_date.time = t; data->sent_date.timezone = tz; index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE, &data->sent_date, sizeof(data->sent_date)); return 0; } int index_mail_get_date(struct mail *_mail, time_t *date_r, int *timezone_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mail_sent_date sentdate; data->cache_fetch_fields |= MAIL_FETCH_DATE; if (data->sent_date.time != (uint32_t)-1) { *timezone_r = data->sent_date.timezone; *date_r = data->sent_date.time; return 0; } if (index_mail_get_fixed_field(mail, MAIL_CACHE_SENT_DATE, &sentdate, sizeof(sentdate))) data->sent_date = sentdate; if (index_mail_cache_sent_date(mail) < 0) return -1; *timezone_r = data->sent_date.timezone; *date_r = data->sent_date.time; return 0; } static bool get_cached_msgpart_sizes(struct index_mail *mail) { struct index_mail_data *data = &mail->data; if (data->parts == NULL) (void)get_cached_parts(mail); if (data->parts != NULL) { data->hdr_size_set = TRUE; data->hdr_size = data->parts->header_size; data->body_size = data->parts->body_size; data->body_size_set = TRUE; data->virtual_size = data->parts->header_size.virtual_size + data->body_size.virtual_size; data->physical_size = data->parts->header_size.physical_size + data->body_size.physical_size; } return data->parts != NULL; } const uint32_t *index_mail_get_vsize_extension(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; const void *idata; bool expunged ATTR_UNUSED; mail_index_lookup_ext(_mail->transaction->view, _mail->seq, _mail->box->mail_vsize_ext_id, &idata, &expunged); const uint32_t *vsize = idata; if (vsize != NULL && *vsize > 0) { mail->data.virtual_size = (*vsize)-1; } return vsize; } bool index_mail_get_cached_virtual_size(struct index_mail *mail, uoff_t *size_r) { struct index_mail_data *data = &mail->data; struct mail *_mail = &mail->mail.mail; uoff_t size; unsigned int idx ATTR_UNUSED; /* see if we can get it from index */ const uint32_t *vsize = index_mail_get_vsize_extension(_mail); data->cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE; if (data->virtual_size == (uoff_t)-1) { if (index_mail_get_cached_uoff_t(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE, &size)) data->virtual_size = size; else { if (!get_cached_msgpart_sizes(mail)) return FALSE; } } if (data->hdr_size_set && data->physical_size != (uoff_t)-1) { data->body_size.physical_size = data->physical_size - data->hdr_size.physical_size; data->body_size.virtual_size = data->virtual_size - data->hdr_size.virtual_size; data->body_size_set = TRUE; } *size_r = data->virtual_size; /* if vsize is present and wanted for index, but missing from index add it to index. */ if (vsize != NULL && *vsize == 0 && data->virtual_size < (uint32_t)-1) { uint32_t vsize = data->virtual_size+1; mail_index_update_ext(_mail->transaction->itrans, _mail->seq, _mail->box->mail_vsize_ext_id, &vsize, NULL); } return TRUE; } static void index_mail_get_cached_body_size(struct index_mail *mail) { struct index_mail_data *data = &mail->data; uoff_t tmp; if (!data->hdr_size_set) return; /* we've already called get_cached_msgpart_sizes() and it didn't work. try to do this by using cached virtual size and a quick physical size lookup. */ if (!index_mail_get_cached_virtual_size(mail, &tmp)) return; if (!data->body_size_set) { enum mail_lookup_abort old_abort = mail->mail.mail.lookup_abort; /* get the physical size, but not if it requires reading through the whole message */ if (mail->mail.mail.lookup_abort < MAIL_LOOKUP_ABORT_READ_MAIL) mail->mail.mail.lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; if (mail_get_physical_size(&mail->mail.mail, &tmp) == 0) { /* we should have everything now. try again. */ (void)index_mail_get_cached_virtual_size(mail, &tmp); } mail->mail.mail.lookup_abort = old_abort; } } int index_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct message_size hdr_size, body_size; struct istream *input; uoff_t old_offset; if (index_mail_get_cached_virtual_size(mail, size_r)) return 0; old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream_because(_mail, &hdr_size, &body_size, index_mail_cache_reason(_mail, "virtual size"), &input) < 0) return -1; i_stream_seek(data->stream, old_offset); i_assert(data->virtual_size != (uoff_t)-1); *size_r = data->virtual_size; return 0; } int index_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; uoff_t size; data->cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; if (data->physical_size == (uoff_t)-1) { if (index_mail_get_cached_uoff_t(mail, MAIL_CACHE_PHYSICAL_FULL_SIZE, &size)) data->physical_size = size; else (void)get_cached_msgpart_sizes(mail); } *size_r = data->physical_size; return *size_r == (uoff_t)-1 ? -1 : 0; } void index_mail_cache_add(struct index_mail *mail, enum index_cache_field field, const void *data, size_t data_size) { index_mail_cache_add_idx(mail, mail->ibox->cache_fields[field].idx, data, data_size); } void index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx, const void *data, size_t data_size) { struct mail *_mail = &mail->mail.mail; const struct mail_storage_settings *set = _mail->box->storage->set; const struct mail_index_header *hdr; if (set->mail_cache_min_mail_count > 0) { /* First check if we've configured caching not to be used with low enough message count. */ hdr = mail_index_get_header(_mail->transaction->view); if (hdr->messages_count < set->mail_cache_min_mail_count) return; } if (!mail->data.no_caching && mail->data.dont_cache_field_idx != field_idx && !_mail->box->mail_cache_disabled) { mail_cache_add(_mail->transaction->cache_trans, _mail->seq, field_idx, data, data_size); } } static void parse_bodystructure_part_header(struct message_part *part, struct message_header_line *hdr, pool_t pool) { message_part_data_parse_from_header(pool, part, hdr); } static bool want_plain_bodystructure_cached(struct index_mail *mail) { const unsigned int cache_field_body = mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx; const unsigned int cache_field_bodystructure = mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; struct mail *_mail = &mail->mail.mail; if ((mail->data.wanted_fields & (MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0) return TRUE; if (mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_field_body)) return TRUE; if (mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_field_bodystructure)) return TRUE; return FALSE; } static void index_mail_body_parsed_cache_flags(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; struct index_mail_data *data = &mail->data; unsigned int cache_flags_idx; uint32_t cache_flags = data->cache_flags; bool want_cached; cache_flags_idx = mail->ibox->cache_fields[MAIL_CACHE_FLAGS].idx; want_cached = mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_flags_idx); if (data->parsed_bodystructure && message_part_data_is_plain_7bit(data->parts) && (want_cached || want_plain_bodystructure_cached(mail))) { cache_flags |= MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII; /* we need message_parts cached to be able to actually use it in BODY/BODYSTRUCTURE reply */ want_cached = TRUE; data->save_message_parts = TRUE; } /* cache flags should never get unset as long as the message doesn't change, but try to handle it anyway */ cache_flags &= ~(MAIL_CACHE_FLAG_BINARY_HEADER | MAIL_CACHE_FLAG_BINARY_BODY | MAIL_CACHE_FLAG_HAS_NULS | MAIL_CACHE_FLAG_HAS_NO_NULS); if (message_parts_have_nuls(data->parts)) { _mail->has_nuls = TRUE; _mail->has_no_nuls = FALSE; cache_flags |= MAIL_CACHE_FLAG_HAS_NULS; } else { _mail->has_nuls = FALSE; _mail->has_no_nuls = TRUE; cache_flags |= MAIL_CACHE_FLAG_HAS_NO_NULS; } if (data->hdr_size.virtual_size == data->hdr_size.physical_size) cache_flags |= MAIL_CACHE_FLAG_BINARY_HEADER; if (data->body_size.virtual_size == data->body_size.physical_size) cache_flags |= MAIL_CACHE_FLAG_BINARY_BODY; if (cache_flags != data->cache_flags && want_cached) { index_mail_cache_add_idx(mail, cache_flags_idx, &cache_flags, sizeof(cache_flags)); } data->cache_flags = cache_flags; } static void index_mail_body_parsed_cache_message_parts(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; struct index_mail_data *data = &mail->data; const unsigned int cache_field = mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; enum mail_cache_decision_type decision; buffer_t *buffer; if (data->messageparts_saved_to_cache || mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_field) != 0) { /* already cached */ return; } decision = mail_cache_field_get_decision(_mail->box->cache, cache_field); if (decision == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) { /* we never want it cached */ return; } if (decision == MAIL_CACHE_DECISION_NO && !data->save_message_parts && (data->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) == 0) { /* we didn't really care about the message parts themselves, just wanted to use something that depended on it */ return; } T_BEGIN { buffer = buffer_create_dynamic(pool_datastack_create(), 1024); message_part_serialize(mail->data.parts, buffer); index_mail_cache_add_idx(mail, cache_field, buffer->data, buffer->used); } T_END; data->messageparts_saved_to_cache = TRUE; } static void index_mail_body_parsed_cache_bodystructure(struct index_mail *mail, enum index_cache_field field) { struct mail *_mail = &mail->mail.mail; struct index_mail_data *data = &mail->data; const unsigned int cache_field_parts = mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; const unsigned int cache_field_body = mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx; const unsigned int cache_field_bodystructure = mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; enum mail_cache_decision_type dec; string_t *str; bool bodystructure_cached = FALSE; bool plain_bodystructure = FALSE; bool cache_bodystructure, cache_body; if ((data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0) { if (data->messageparts_saved_to_cache || mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_field_parts) > 0) { /* cached it as flag + message_parts */ plain_bodystructure = TRUE; } } if (!data->parsed_bodystructure) return; i_assert(data->parts != NULL); /* If BODY is fetched first but BODYSTRUCTURE is also wanted, we don't normally want to first cache BODY and then BODYSTRUCTURE. So check the wanted_fields also in here. */ if (plain_bodystructure) cache_bodystructure = FALSE; else if (field == MAIL_CACHE_IMAP_BODYSTRUCTURE || (data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) { cache_bodystructure = mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_field_bodystructure); } else { cache_bodystructure = mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_field_bodystructure); } if (cache_bodystructure) { str = str_new(mail->mail.data_pool, 128); imap_bodystructure_write(data->parts, str, TRUE); data->bodystructure = str_c(str); index_mail_cache_add(mail, MAIL_CACHE_IMAP_BODYSTRUCTURE, str_c(str), str_len(str)+1); bodystructure_cached = TRUE; } else { bodystructure_cached = mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_field_bodystructure) > 0; } /* normally don't cache both BODY and BODYSTRUCTURE, but do it if BODY is forced to be cached */ dec = mail_cache_field_get_decision(_mail->box->cache, cache_field_body); if (plain_bodystructure || (bodystructure_cached && (dec != (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_YES)))) cache_body = FALSE; else if (field == MAIL_CACHE_IMAP_BODY) { cache_body = mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_field_body); } else { cache_body = mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_field_body); } if (cache_body) { str = str_new(mail->mail.data_pool, 128); imap_bodystructure_write(data->parts, str, FALSE); data->body = str_c(str); index_mail_cache_add(mail, MAIL_CACHE_IMAP_BODY, str_c(str), str_len(str)+1); } } static bool index_mail_want_cache(struct index_mail *mail, enum index_cache_field field) { struct mail *_mail = &mail->mail.mail; enum mail_fetch_field fetch_field; unsigned int cache_field; switch (field) { case MAIL_CACHE_SENT_DATE: fetch_field = MAIL_FETCH_DATE; break; case MAIL_CACHE_RECEIVED_DATE: fetch_field = MAIL_FETCH_RECEIVED_DATE; break; case MAIL_CACHE_SAVE_DATE: fetch_field = MAIL_FETCH_SAVE_DATE; break; case MAIL_CACHE_VIRTUAL_FULL_SIZE: fetch_field = MAIL_FETCH_VIRTUAL_SIZE; break; case MAIL_CACHE_PHYSICAL_FULL_SIZE: fetch_field = MAIL_FETCH_PHYSICAL_SIZE; break; case MAIL_CACHE_BODY_SNIPPET: fetch_field = MAIL_FETCH_BODY_SNIPPET; break; default: i_unreached(); } if ((mail->data.dont_cache_fetch_fields & fetch_field) != 0) return FALSE; cache_field = mail->ibox->cache_fields[field].idx; if ((mail->data.cache_fetch_fields & fetch_field) != 0) { return mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_field); } else { return mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, cache_field); } } static void index_mail_body_parsed_cache_body_snippet(struct index_mail *mail) { if (mail->data.body_snippet != NULL && index_mail_want_cache(mail, MAIL_CACHE_BODY_SNIPPET)) { index_mail_cache_add(mail, MAIL_CACHE_BODY_SNIPPET, mail->data.body_snippet, strlen(mail->data.body_snippet)+1); } } static void index_mail_cache_sizes(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; struct mail_index_view *view = _mail->transaction->view; static enum index_cache_field size_fields[] = { MAIL_CACHE_VIRTUAL_FULL_SIZE, MAIL_CACHE_PHYSICAL_FULL_SIZE }; uoff_t sizes[N_ELEMENTS(size_fields)]; unsigned int i; uint32_t vsize; uint32_t idx ATTR_UNUSED; sizes[0] = mail->data.virtual_size; sizes[1] = mail->data.physical_size; /* store the virtual size in index if extension for it exists or extension for box virtual size exists and size fits and is present and size is not cached or cached size differs */ if ((mail_index_map_get_ext_idx(view->map, _mail->box->mail_vsize_ext_id, &idx) || mail_index_map_get_ext_idx(view->map, _mail->box->vsize_hdr_ext_id, &idx)) && (sizes[0] != (uoff_t)-1 && sizes[0] < (uint32_t)-1)) { const uint32_t *vsize_ext = index_mail_get_vsize_extension(_mail); /* vsize = 0 means it's not present in index, consult cache. we store vsize for every +4GB-1 mail to cache because index can only hold 2^32-1 size. Cache will not be used when vsize is stored in index. */ vsize = sizes[0] + 1; if (vsize_ext == NULL || vsize != *vsize_ext) { mail_index_update_ext(_mail->transaction->itrans, _mail->seq, _mail->box->mail_vsize_ext_id, &vsize, NULL); } /* it's already in index, so don't update cache */ sizes[0] = (uoff_t)-1; } for (i = 0; i < N_ELEMENTS(size_fields); i++) { if (sizes[i] != (uoff_t)-1 && index_mail_want_cache(mail, size_fields[i])) { index_mail_cache_add(mail, size_fields[i], &sizes[i], sizeof(sizes[i])); } } } static void index_mail_cache_dates(struct index_mail *mail) { static enum index_cache_field date_fields[] = { MAIL_CACHE_RECEIVED_DATE, MAIL_CACHE_SAVE_DATE }; time_t dates[N_ELEMENTS(date_fields)]; unsigned int i; uint32_t t; dates[0] = mail->data.received_date; dates[1] = mail->mail.mail.saving ? ioloop_time : mail->data.save_date; for (i = 0; i < N_ELEMENTS(date_fields); i++) { if (dates[i] != (time_t)-1 && index_mail_want_cache(mail, date_fields[i])) { t = dates[i]; index_mail_cache_add(mail, date_fields[i], &t, sizeof(t)); } } if (mail->data.sent_date_parsed && index_mail_want_cache(mail, MAIL_CACHE_SENT_DATE)) (void)index_mail_cache_sent_date(mail); } static struct message_part * index_mail_find_first_text_mime_part(struct message_part *parts) { struct message_part_data *body_data = parts->data; struct message_part *part; i_assert(body_data != NULL); if (body_data->content_type == NULL || strcasecmp(body_data->content_type, "text") == 0) { /* use any text/ part, even if we don't know what exactly it is. */ return parts; } if (strcasecmp(body_data->content_type, "multipart") != 0) { /* for now we support only text Content-Types */ return NULL; } if (strcasecmp(body_data->content_subtype, "alternative") == 0) { /* text/plain > text/html > text/ */ struct message_part *html_part = NULL, *text_part = NULL; for (part = parts->children; part != NULL; part = part->next) { struct message_part_data *sub_body_data = part->data; i_assert(sub_body_data != NULL); if (sub_body_data->content_type == NULL || strcasecmp(sub_body_data->content_type, "text") == 0) { if (sub_body_data->content_subtype == NULL || strcasecmp(sub_body_data->content_subtype, "plain") == 0) return part; if (strcasecmp(sub_body_data->content_subtype, "html") == 0) html_part = part; else text_part = part; } } return html_part != NULL ? html_part : text_part; } /* find the first usable MIME part */ for (part = parts->children; part != NULL; part = part->next) { struct message_part *subpart = index_mail_find_first_text_mime_part(part); if (subpart != NULL) return subpart; } return NULL; } static int index_mail_write_body_snippet(struct index_mail *mail) { struct message_part *part; struct istream *input; uoff_t old_offset; string_t *str; int ret; i_assert(mail->data.parsed_bodystructure); part = index_mail_find_first_text_mime_part(mail->data.parts); if (part == NULL) { mail->data.body_snippet = BODY_SNIPPET_ALGO_V1; return 0; } old_offset = mail->data.stream == NULL ? 0 : mail->data.stream->v_offset; const char *reason = index_mail_cache_reason(&mail->mail.mail, "snippet"); if (mail_get_stream_because(&mail->mail.mail, NULL, NULL, reason, &input) < 0) return -1; i_assert(mail->data.stream != NULL); i_stream_seek(input, part->physical_pos); input = i_stream_create_limit(input, part->header_size.physical_size + part->body_size.physical_size); str = str_new(mail->mail.data_pool, 128); str_append(str, BODY_SNIPPET_ALGO_V1); ret = message_snippet_generate(input, BODY_SNIPPET_MAX_CHARS, str); if (ret == 0) mail->data.body_snippet = str_c(str); i_stream_destroy(&input); i_stream_seek(mail->data.stream, old_offset); return ret; } static int index_mail_parse_body_finish(struct index_mail *mail, enum index_cache_field field, bool success) { struct istream *parser_input = mail->data.parser_input; const char *error = NULL; int ret; if (parser_input == NULL) { ret = message_parser_deinit_from_parts(&mail->data.parser_ctx, &mail->data.parts, &error) < 0 ? 0 : 1; } else { mail->data.parser_input = NULL; i_stream_ref(parser_input); ret = message_parser_deinit_from_parts(&mail->data.parser_ctx, &mail->data.parts, &error) < 0 ? 0 : 1; if (success && (parser_input->stream_errno == 0 || parser_input->stream_errno == EPIPE)) { /* do one final read, which verifies that the message size is correct. */ if (i_stream_read(parser_input) != -1 || i_stream_have_bytes_left(parser_input)) i_unreached(); } /* EPIPE = input already closed. allow the caller to decide if that is an error or not. (for example we could be coming here from IMAP APPEND when IMAP client has closed the connection too early. we don't want to log an error in that case.) */ if (parser_input->stream_errno != 0 && parser_input->stream_errno != EPIPE) { index_mail_stream_log_failure_for(mail, parser_input); ret = -1; } i_stream_unref(&parser_input); } if (ret <= 0) { if (ret == 0) { i_assert(error != NULL); index_mail_set_message_parts_corrupted(&mail->mail.mail, error); } mail->data.parts = NULL; mail->data.parsed_bodystructure = FALSE; if (mail->data.save_bodystructure_body) mail->data.save_bodystructure_header = TRUE; return -1; } if (mail->data.save_bodystructure_body) { mail->data.parsed_bodystructure = TRUE; mail->data.save_bodystructure_header = FALSE; mail->data.save_bodystructure_body = FALSE; i_assert(mail->data.parts != NULL); } if (mail->data.save_body_snippet) { if (index_mail_write_body_snippet(mail) < 0) return -1; mail->data.save_body_snippet = FALSE; } if (mail->data.no_caching) { /* if we're here because we aborted parsing, don't get any further or we may crash while generating output from incomplete data */ return 0; } (void)get_cached_msgpart_sizes(mail); index_mail_body_parsed_cache_flags(mail); index_mail_body_parsed_cache_message_parts(mail); index_mail_body_parsed_cache_bodystructure(mail, field); index_mail_body_parsed_cache_body_snippet(mail); index_mail_cache_sizes(mail); index_mail_cache_dates(mail); return 0; } static void index_mail_stream_log_failure(struct index_mail *mail) { index_mail_stream_log_failure_for(mail, mail->data.stream); } int index_mail_stream_check_failure(struct index_mail *mail) { if (mail->data.stream->stream_errno == 0) return 0; index_mail_stream_log_failure(mail); return -1; } void index_mail_refresh_expunged(struct mail *mail) { mail_index_refresh(mail->box->index); if (mail_index_is_expunged(mail->transaction->view, mail->seq)) mail_set_expunged(mail); } void index_mail_stream_log_failure_for(struct index_mail *mail, struct istream *input) { struct mail *_mail = &mail->mail.mail; i_assert(input->stream_errno != 0); if (input->stream_errno == ENOENT) { /* was the mail just expunged? we could get here especially if external attachments are used and the attachment is deleted before we've opened the file. */ index_mail_refresh_expunged(_mail); if (_mail->expunged) return; } mail_storage_set_critical(_mail->box->storage, "read(%s) failed: %s (uid=%u, box=%s, read reason=%s)", i_stream_get_name(input), i_stream_get_error(input), _mail->uid, mailbox_get_vname(_mail->box), mail->mail.get_stream_reason == NULL ? "" : mail->mail.get_stream_reason); } static int index_mail_parse_body(struct index_mail *mail, enum index_cache_field field) { struct index_mail_data *data = &mail->data; uoff_t old_offset; int ret; i_assert(data->parser_ctx != NULL); old_offset = data->stream->v_offset; i_stream_seek(data->stream, data->hdr_size.physical_size); if (data->save_bodystructure_body) { /* bodystructure header is parsed, we want the body's mime headers too */ i_assert(!data->save_bodystructure_header); message_parser_parse_body(data->parser_ctx, parse_bodystructure_part_header, mail->mail.data_pool); } else { message_parser_parse_body(data->parser_ctx, *null_message_part_header_callback, (void *)NULL); } ret = index_mail_stream_check_failure(mail); if (index_mail_parse_body_finish(mail, field, TRUE) < 0) ret = -1; i_stream_seek(data->stream, old_offset); return ret; } static void index_mail_stream_destroy_callback(struct index_mail *mail) { i_assert(mail->data.destroying_stream); mail->data.destroying_stream = FALSE; } void index_mail_set_read_buffer_size(struct mail *_mail, struct istream *input) { struct index_mail *mail = (struct index_mail *)_mail; unsigned int block_size; i_stream_set_max_buffer_size(input, MAIL_READ_FULL_BLOCK_SIZE); block_size = (mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0 ? MAIL_READ_FULL_BLOCK_SIZE : MAIL_READ_HDR_BLOCK_SIZE; i_stream_set_init_buffer_size(input, block_size); } int index_mail_init_stream(struct index_mail *mail, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct mail *_mail = &mail->mail.mail; struct index_mail_data *data = &mail->data; struct istream *input; bool has_nuls; int ret; if (_mail->box->storage->user->mail_debug && mail->mail.get_stream_reason != NULL && mail->mail.get_stream_reason[0] != '\0') { i_debug("Mailbox %s: Opened mail UID=%u because: %s", _mail->box->vname, _mail->uid, mail->mail.get_stream_reason); } _mail->mail_stream_opened = TRUE; if (!data->initialized_wrapper_stream && _mail->transaction->stats_track) { input = i_stream_create_mail(_mail, data->stream, !data->stream_has_only_header); i_stream_unref(&data->stream); data->stream = input; data->initialized_wrapper_stream = TRUE; } if (!data->destroy_callback_set) { /* do this only once in case a plugin changes the stream. otherwise the check would break. */ data->destroy_callback_set = TRUE; i_stream_add_destroy_callback(data->stream, index_mail_stream_destroy_callback, mail); } if (hdr_size != NULL || body_size != NULL) (void)get_cached_msgpart_sizes(mail); if (hdr_size != NULL || body_size != NULL) { i_stream_seek(data->stream, 0); if (!data->hdr_size_set) { if ((data->access_part & PARSE_HDR) != 0) { (void)get_cached_parts(mail); if (index_mail_parse_headers(mail, NULL, "parse header") < 0) return -1; } else { if (message_get_header_size(data->stream, &data->hdr_size, &has_nuls) < 0) { index_mail_stream_log_failure(mail); return -1; } data->hdr_size_set = TRUE; } } if (hdr_size != NULL) *hdr_size = data->hdr_size; } if (body_size != NULL) { if (!data->body_size_set) index_mail_get_cached_body_size(mail); if (!data->body_size_set) { i_stream_seek(data->stream, data->hdr_size.physical_size); if ((data->access_part & PARSE_BODY) != 0) { if (index_mail_parse_body(mail, 0) < 0) return -1; } else { if (message_get_body_size(data->stream, &data->body_size, &has_nuls) < 0) { index_mail_stream_log_failure(mail); return -1; } data->body_size_set = TRUE; } } *body_size = data->body_size; } if (data->hdr_size_set && data->body_size_set) { data->virtual_size = data->hdr_size.virtual_size + data->body_size.virtual_size; data->physical_size = data->hdr_size.physical_size + data->body_size.physical_size; } ret = index_mail_stream_check_failure(mail); i_stream_seek(data->stream, 0); if (ret < 0) return -1; *stream_r = data->stream; return 0; } static int index_mail_parse_bodystructure(struct index_mail *mail, enum index_cache_field field) { struct index_mail_data *data = &mail->data; string_t *str; if (data->parsed_bodystructure && field != MAIL_CACHE_BODY_SNIPPET) { /* we have everything parsed already, but just not written to a string */ index_mail_body_parsed_cache_bodystructure(mail, field); } else { if (data->save_bodystructure_header || !data->save_bodystructure_body || field == MAIL_CACHE_BODY_SNIPPET) { /* we haven't parsed the header yet */ const char *reason = index_mail_cache_reason(&mail->mail.mail, "bodystructure"); data->save_bodystructure_header = TRUE; data->save_bodystructure_body = TRUE; (void)get_cached_parts(mail); if (index_mail_parse_headers(mail, NULL, reason) < 0) { data->save_bodystructure_header = TRUE; return -1; } i_assert(data->parser_ctx != NULL); } if (index_mail_parse_body(mail, field) < 0) return -1; } i_assert(data->parts != NULL); /* if we didn't want to have the body(structure) cached, it's still not written. */ switch (field) { case MAIL_CACHE_IMAP_BODY: if (data->body == NULL) { str = str_new(mail->mail.data_pool, 128); imap_bodystructure_write(data->parts, str, FALSE); data->body = str_c(str); } break; case MAIL_CACHE_IMAP_BODYSTRUCTURE: if (data->bodystructure == NULL) { str = str_new(mail->mail.data_pool, 128); imap_bodystructure_write(data->parts, str, TRUE); data->bodystructure = str_c(str); } break; case MAIL_CACHE_BODY_SNIPPET: i_assert(data->body_snippet != NULL && data->body_snippet[0] != '\0'); break; default: i_unreached(); } return 0; } static void index_mail_get_plain_bodystructure(struct index_mail *mail, string_t *str, bool extended) { str_printfa(str, IMAP_BODY_PLAIN_7BIT_ASCII" %"PRIuUOFF_T" %u", mail->data.parts->body_size.virtual_size, mail->data.parts->body_size.lines); if (extended) str_append(str, " NIL NIL NIL NIL"); } static int index_mail_fetch_body_snippet(struct index_mail *mail, const char **value_r) { const struct mail_cache_field *cache_fields = mail->ibox->cache_fields; const unsigned int cache_field = cache_fields[MAIL_CACHE_BODY_SNIPPET].idx; string_t *str; if (mail->data.body_snippet == NULL) { str = str_new(mail->mail.data_pool, 128); if (index_mail_cache_lookup_field(mail, str, cache_field) > 0 && str_len(str) > 0) mail->data.body_snippet = str_c(str); } if (mail->data.body_snippet != NULL) { *value_r = mail->data.body_snippet; return 0; } /* reuse the IMAP bodystructure parsing code to get all the useful headers that we need. */ mail->data.save_body_snippet = TRUE; if (index_mail_parse_bodystructure(mail, MAIL_CACHE_BODY_SNIPPET) < 0) return -1; i_assert(mail->data.body_snippet != NULL); *value_r = mail->data.body_snippet; return 0; } bool index_mail_get_cached_body(struct index_mail *mail, const char **value_r) { const struct mail_cache_field *cache_fields = mail->ibox->cache_fields; const unsigned int body_cache_field = cache_fields[MAIL_CACHE_IMAP_BODY].idx; const unsigned int bodystructure_cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; struct index_mail_data *data = &mail->data; string_t *str; const char *error; if (data->body != NULL) { *value_r = data->body; return TRUE; } str = str_new(mail->mail.data_pool, 128); if ((data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 && get_cached_parts(mail)) { index_mail_get_plain_bodystructure(mail, str, FALSE); *value_r = data->body = str_c(str); return TRUE; } /* 2) get BODY if it exists */ if (index_mail_cache_lookup_field(mail, str, body_cache_field) > 0) { *value_r = data->body = str_c(str); return TRUE; } /* 3) get it using BODYSTRUCTURE if it exists */ if (index_mail_cache_lookup_field(mail, str, bodystructure_cache_field) > 0) { data->bodystructure = p_strdup(mail->mail.data_pool, str_c(str)); str_truncate(str, 0); if (imap_body_parse_from_bodystructure(data->bodystructure, str, &error) < 0) { /* broken, continue.. */ mail_set_cache_corrupted_reason(&mail->mail.mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, t_strdup_printf( "Invalid BODYSTRUCTURE %s: %s", data->bodystructure, error)); } else { *value_r = data->body = str_c(str); return TRUE; } } str_free(&str); return FALSE; } bool index_mail_get_cached_bodystructure(struct index_mail *mail, const char **value_r) { const struct mail_cache_field *cache_fields = mail->ibox->cache_fields; const unsigned int bodystructure_cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; struct index_mail_data *data = &mail->data; string_t *str; if (data->bodystructure != NULL) { *value_r = data->bodystructure; return TRUE; } str = str_new(mail->mail.data_pool, 128); if ((data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 && get_cached_parts(mail)) { index_mail_get_plain_bodystructure(mail, str, TRUE); *value_r = data->bodystructure = str_c(str); return TRUE; } if (index_mail_cache_lookup_field(mail, str, bodystructure_cache_field) > 0) { *value_r = data->bodystructure = str_c(str); return TRUE; } str_free(&str); return FALSE; } int index_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; switch (field) { case MAIL_FETCH_IMAP_BODY: if (index_mail_get_cached_body(mail, value_r)) return 0; /* parse body structure, and save BODY/BODYSTRUCTURE depending on what we want cached */ if (index_mail_parse_bodystructure(mail, MAIL_CACHE_IMAP_BODY) < 0) return -1; i_assert(data->body != NULL); *value_r = data->body; return 0; case MAIL_FETCH_IMAP_BODYSTRUCTURE: if (index_mail_get_cached_bodystructure(mail, value_r)) return 0; if (index_mail_parse_bodystructure(mail, MAIL_CACHE_IMAP_BODYSTRUCTURE) < 0) return -1; i_assert(data->bodystructure != NULL); *value_r = data->bodystructure; return 0; case MAIL_FETCH_IMAP_ENVELOPE: if (data->envelope == NULL) { if (index_mail_headers_get_envelope(mail) < 0) return -1; } *value_r = data->envelope; return 0; case MAIL_FETCH_FROM_ENVELOPE: *value_r = data->from_envelope != NULL ? data->from_envelope : ""; return 0; case MAIL_FETCH_BODY_SNIPPET: return index_mail_fetch_body_snippet(mail, value_r); case MAIL_FETCH_STORAGE_ID: case MAIL_FETCH_UIDL_BACKEND: case MAIL_FETCH_SEARCH_RELEVANCY: case MAIL_FETCH_GUID: case MAIL_FETCH_HEADER_MD5: case MAIL_FETCH_POP3_ORDER: case MAIL_FETCH_REFCOUNT: *value_r = ""; return 0; case MAIL_FETCH_MAILBOX_NAME: *value_r = _mail->box->vname; return 0; default: i_unreached(); return -1; } } struct mail *index_mail_get_real_mail(struct mail *mail) { return mail; } struct mail * index_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct index_mail *mail; pool_t pool; pool = pool_alloconly_create("mail", 2048); mail = p_new(pool, struct index_mail, 1); mail->mail.pool = pool; index_mail_init(mail, t, wanted_fields, wanted_headers); return &mail->mail.mail; } void index_mail_init(struct index_mail *mail, struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { array_create(&mail->mail.module_contexts, mail->mail.pool, sizeof(void *), 5); mail->mail.v = *t->box->mail_vfuncs; mail->mail.mail.box = t->box; mail->mail.mail.transaction = t; t->mail_ref_count++; mail->mail.data_pool = pool_alloconly_create("index_mail", 16384); mail->ibox = INDEX_STORAGE_CONTEXT(t->box); mail->mail.wanted_fields = wanted_fields; if (wanted_headers != NULL) { mail->mail.wanted_headers = wanted_headers; mailbox_header_lookup_ref(wanted_headers); } index_mail_init_data(mail); } static void index_mail_close_streams_full(struct index_mail *mail, bool closing) { struct index_mail_data *data = &mail->data; struct message_part *parts; const char *error; if (data->parser_ctx != NULL) { if (message_parser_deinit_from_parts(&data->parser_ctx, &parts, &error) < 0) index_mail_set_message_parts_corrupted(&mail->mail.mail, error); mail->data.parser_input = NULL; if (mail->data.save_bodystructure_body) mail->data.save_bodystructure_header = TRUE; } if (data->filter_stream != NULL) i_stream_unref(&data->filter_stream); if (data->stream != NULL) { struct istream *orig_stream = data->stream; data->destroying_stream = TRUE; if (!closing && data->destroy_callback_set) { /* we're replacing the stream with a new one. it's allowed to have references until the mail is closed (but we can't really check that) */ i_stream_remove_destroy_callback(data->stream, index_mail_stream_destroy_callback); } i_stream_unref(&data->stream); /* there must be no references to the mail when the mail is being closed. */ if (!closing) data->destroying_stream = FALSE; else if (mail->data.destroying_stream) { i_panic("Input stream %s unexpectedly has references", i_stream_get_name(orig_stream)); } data->initialized_wrapper_stream = FALSE; data->destroy_callback_set = FALSE; } } void index_mail_close_streams(struct index_mail *mail) { index_mail_close_streams_full(mail, FALSE); } static void index_mail_init_data(struct index_mail *mail) { struct index_mail_data *data = &mail->data; data->virtual_size = (uoff_t)-1; data->physical_size = (uoff_t)-1; data->save_date = (time_t)-1; data->received_date = (time_t)-1; data->sent_date.time = (uint32_t)-1; data->dont_cache_field_idx = UINT_MAX; data->wanted_fields = mail->mail.wanted_fields; if (mail->mail.wanted_headers != NULL) { data->wanted_headers = mail->mail.wanted_headers; mailbox_header_lookup_ref(data->wanted_headers); } } static void index_mail_reset_data(struct index_mail *mail) { i_zero(&mail->data); p_clear(mail->mail.data_pool); index_mail_init_data(mail); mail->mail.mail.seq = 0; mail->mail.mail.uid = 0; mail->mail.seq_pvt = 0; mail->mail.mail.expunged = FALSE; mail->mail.mail.has_nuls = FALSE; mail->mail.mail.has_no_nuls = FALSE; mail->mail.mail.saving = FALSE; mail->mail.mail.mail_stream_opened = FALSE; mail->mail.mail.mail_metadata_accessed = FALSE; } void index_mail_close(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->mail.mail.seq == 0) { /* mail_set_seq*() hasn't been called yet, or is being called right now. Don't reset anything yet. We especially don't want to reset wanted_fields or wanted_headers so that mail_add_temp_wanted_fields() can be called by plugins before mail_set_seq_saving() for mail_save_context.dest_mail. */ return; } /* If uid == 0 but seq != 0, we came here from saving a (non-mbox) message. If that happens, don't bother checking if anything should be cached since it was already checked. Also by now the transaction may have already been rollbacked and seq point to a nonexistent message. */ if (mail->mail.mail.uid != 0) { index_mail_cache_sizes(mail); index_mail_cache_dates(mail); } index_mail_close_streams_full(mail, TRUE); /* Notify cache that the mail is no longer open. This mainly helps with INDEX=MEMORY to keep all data added with mail_cache_add() in memory until this point. */ mail_cache_close_mail(_mail->transaction->cache_trans, _mail->seq); if (mail->data.wanted_headers != NULL) mailbox_header_lookup_unref(&mail->data.wanted_headers); if (!mail->freeing) index_mail_reset_data(mail); } static void check_envelope(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const unsigned int cache_field_envelope = mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx; unsigned int cache_field_hdr; if ((mail->data.access_part & PARSE_HDR) != 0) { mail->data.save_envelope = TRUE; return; } /* if "imap.envelope" is cached, that's all we need */ if (mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_field_envelope) > 0) return; /* don't waste time doing full checks for all required headers. assume that if we have "hdr.message-id" cached, we don't need to parse the header. */ cache_field_hdr = mail_cache_register_lookup(_mail->box->cache, "hdr.message-id"); if (cache_field_hdr == UINT_MAX || mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_field_hdr) <= 0) mail->data.access_part |= PARSE_HDR; mail->data.save_envelope = TRUE; } void index_mail_update_access_parts_pre(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mail_storage *storage = _mail->box->storage; const struct mail_cache_field *cache_fields = mail->ibox->cache_fields; struct mail_cache_view *cache_view = _mail->transaction->cache_view; if (_mail->seq == 0) { /* mail_add_temp_wanted_fields() called before mail_set_seq*(). We'll allow this, since it can be useful for plugins to call it for mail_save_context.dest_mail. This function is called again in mail_set_seq*(). */ return; } if ((data->wanted_fields & (MAIL_FETCH_NUL_STATE | MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0 && !_mail->has_nuls && !_mail->has_no_nuls) { (void)index_mail_get_fixed_field(mail, MAIL_CACHE_FLAGS, &data->cache_flags, sizeof(data->cache_flags)); _mail->has_nuls = (data->cache_flags & MAIL_CACHE_FLAG_HAS_NULS) != 0; _mail->has_no_nuls = (data->cache_flags & MAIL_CACHE_FLAG_HAS_NO_NULS) != 0; /* we currently don't forcibly set the nul state. if it's not already cached, the caller can figure out itself what to do when neither is set */ } /* see if wanted_fields can tell us if we need to read/parse header/body */ if ((data->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0 && (storage->nonbody_access_fields & MAIL_FETCH_MESSAGE_PARTS) == 0 && data->parts == NULL) { const unsigned int cache_field = cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; if (mail_cache_field_exists(cache_view, _mail->seq, cache_field) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_message_parts = TRUE; } } if ((data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0 && (storage->nonbody_access_fields & MAIL_FETCH_IMAP_ENVELOPE) == 0 && data->envelope == NULL) check_envelope(mail); if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 && (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0 && (storage->nonbody_access_fields & MAIL_FETCH_IMAP_BODY) == 0 && data->body == NULL) { /* we need either imap.body or imap.bodystructure */ const unsigned int cache_field1 = cache_fields[MAIL_CACHE_IMAP_BODY].idx; const unsigned int cache_field2 = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (mail_cache_field_exists(cache_view, _mail->seq, cache_field1) <= 0 && mail_cache_field_exists(cache_view, _mail->seq, cache_field2) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_bodystructure_header = TRUE; data->save_bodystructure_body = TRUE; } } if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 && (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0 && (storage->nonbody_access_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) == 0 && data->bodystructure == NULL) { const unsigned int cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (mail_cache_field_exists(cache_view, _mail->seq, cache_field) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_bodystructure_header = TRUE; data->save_bodystructure_body = TRUE; } } if ((data->wanted_fields & MAIL_FETCH_DATE) != 0 && (storage->nonbody_access_fields & MAIL_FETCH_DATE) == 0 && data->sent_date.time == (uint32_t)-1) { const unsigned int cache_field = cache_fields[MAIL_CACHE_SENT_DATE].idx; if (mail_cache_field_exists(cache_view, _mail->seq, cache_field) <= 0) { data->access_part |= PARSE_HDR; data->save_sent_date = TRUE; } } if ((data->wanted_fields & MAIL_FETCH_BODY_SNIPPET) != 0 && (storage->nonbody_access_fields & MAIL_FETCH_BODY_SNIPPET) == 0) { const unsigned int cache_field = cache_fields[MAIL_CACHE_BODY_SNIPPET].idx; if (mail_cache_field_exists(cache_view, _mail->seq, cache_field) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_body_snippet = TRUE; } } if ((data->wanted_fields & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0) { if ((data->wanted_fields & MAIL_FETCH_STREAM_HEADER) != 0) data->access_part |= READ_HDR; if ((data->wanted_fields & MAIL_FETCH_STREAM_BODY) != 0) data->access_part |= READ_BODY; } } void index_mail_update_access_parts_post(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; const struct mail_index_header *hdr; struct istream *input; if (_mail->seq == 0) { /* see index_mail_update_access_parts_pre() */ return; } /* when mail_prefetch_count>1, at this point we've started the prefetching to all the mails and we're now starting to access the first mail. */ if (data->access_part != 0) { /* open stream immediately to set expunged flag if it's already lost */ /* open the stream only if we didn't get here from mailbox_save_init() */ hdr = mail_index_get_header(_mail->transaction->view); if (!_mail->saving && _mail->uid < hdr->next_uid) { if ((data->access_part & (READ_BODY | PARSE_BODY)) != 0) (void)mail_get_stream_because(_mail, NULL, NULL, "access", &input); else (void)mail_get_hdr_stream(_mail, NULL, &input); } } } void index_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.seq == seq) { if (!saving) return; /* we started saving a mail, aborted it, and now we're saving another mail with the same sequence. make sure the mail gets reset. */ } mail->mail.v.close(&mail->mail.mail); mail->data.seq = seq; mail->mail.mail.seq = seq; mail->mail.mail.saving = saving; mail_index_lookup_uid(_mail->transaction->view, seq, &mail->mail.mail.uid); if (mail_index_view_is_inconsistent(_mail->transaction->view)) { mail_set_expunged(&mail->mail.mail); return; } if (!mail->search_mail) { index_mail_update_access_parts_pre(_mail); index_mail_update_access_parts_post(_mail); } else { /* searching code will call the index_mail_update_access_parts_*() after we know the mail is actually wanted to be fetched. */ } mail->data.initialized = TRUE; } bool index_mail_prefetch(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; /* HAVE_POSIX_FADVISE alone isn't enough for CentOS 4.9 */ #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) struct mail_storage *storage = _mail->box->storage; struct istream *input; off_t len; int fd; if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG) == 0) { /* we're handling only file-per-msg storages for now. */ return TRUE; } if (mail->data.access_part == 0) { /* everything we need is cached */ return TRUE; } if (mail->data.stream == NULL) { (void)mail_get_stream_because(_mail, NULL, NULL, "prefetch", &input); if (mail->data.stream == NULL) return TRUE; } /* tell OS to start reading the file into memory */ fd = i_stream_get_fd(mail->data.stream); if (fd != -1) { if ((mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0) len = 0; else len = MAIL_READ_HDR_BLOCK_SIZE; if (posix_fadvise(fd, 0, len, POSIX_FADV_WILLNEED) < 0) { i_error("posix_fadvise(%s) failed: %m", i_stream_get_name(mail->data.stream)); } mail->data.prefetch_sent = TRUE; } #endif return !mail->data.prefetch_sent; } bool index_mail_set_uid(struct mail *_mail, uint32_t uid) { struct index_mail *mail = (struct index_mail *)_mail; uint32_t seq; if (mail_index_lookup_seq(_mail->transaction->view, uid, &seq)) { index_mail_set_seq(_mail, seq, FALSE); return TRUE; } else { mail->mail.v.close(&mail->mail.mail); mail->mail.mail.uid = uid; mail_set_expunged(&mail->mail.mail); return FALSE; } } void index_mail_add_temp_wanted_fields(struct mail *_mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mailbox_header_lookup_ctx *new_wanted_headers; ARRAY_TYPE(const_string) names; unsigned int i; data->wanted_fields |= fields; if (headers == NULL) { /* keep old ones */ } else if (data->wanted_headers == NULL) { data->wanted_headers = headers; mailbox_header_lookup_ref(headers); } else { /* merge headers */ t_array_init(&names, 32); for (i = 0; i < data->wanted_headers->count; i++) array_append(&names, &data->wanted_headers->name[i], 1); for (i = 0; i < headers->count; i++) array_append(&names, &headers->name[i], 1); array_append_zero(&names); new_wanted_headers = mailbox_header_lookup_init(_mail->box, array_idx(&names, 0)); if (data->wanted_headers != NULL) mailbox_header_lookup_unref(&data->wanted_headers); data->wanted_headers = new_wanted_headers; } index_mail_update_access_parts_pre(_mail); /* Don't call _post(), which would try to open the stream. It should be enough to delay the opening until it happens anyway. Otherwise there's not really any good place to call this in the plugins: set_seq() call get_stream() internally, which can already start parsing the headers, so it's too late. If we use get_stream() and there's a _post() call here, it gets into infinite loop. The loop could probably be prevented in some way, but it's probably better to eventually try to remove the _post() call entirely everywhere. */ } void index_mail_set_uid_cache_updates(struct mail *_mail, bool set) { struct index_mail *mail = (struct index_mail *)_mail; mail->data.no_caching = set || mail->data.forced_no_caching; } void index_mail_free(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct mailbox_header_lookup_ctx *headers_ctx = (struct mailbox_header_lookup_ctx *)mail->mail.wanted_headers; /* make sure mailbox_search_*() users don't try to free the mail directly */ i_assert(!mail->search_mail); mail->freeing = TRUE; mail->mail.v.close(_mail); i_assert(_mail->transaction->mail_ref_count > 0); _mail->transaction->mail_ref_count--; if (mail->header_data != NULL) buffer_free(&mail->header_data); if (array_is_created(&mail->header_lines)) array_free(&mail->header_lines); if (array_is_created(&mail->header_match)) array_free(&mail->header_match); if (array_is_created(&mail->header_match_lines)) array_free(&mail->header_match_lines); if (headers_ctx != NULL) mailbox_header_lookup_unref(&headers_ctx); pool_unref(&mail->mail.data_pool); pool_unref(&mail->mail.pool); } void index_mail_cache_parse_continue(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct message_block block; while (message_parser_parse_next_block(mail->data.parser_ctx, &block) > 0) { if (block.size != 0) continue; if (!mail->data.header_parsed) { index_mail_parse_header(block.part, block.hdr, mail); if (block.hdr == NULL) mail->data.header_parsed = TRUE; } else { message_part_data_parse_from_header(mail->mail.data_pool, block.part, block.hdr); } } } void index_mail_cache_parse_deinit(struct mail *_mail, time_t received_date, bool success) { struct index_mail *mail = (struct index_mail *)_mail; if (!success) { /* we're going to delete this mail anyway, don't bother trying to update cache file */ mail->data.no_caching = TRUE; mail->data.forced_no_caching = TRUE; if (mail->data.parser_ctx == NULL) { /* we didn't even start cache parsing */ return; } } /* This is needed with 0 byte mails to get hdr=NULL call done. */ index_mail_cache_parse_continue(_mail); if (mail->data.received_date == (time_t)-1) mail->data.received_date = received_date; if (mail->data.save_date == (time_t)-1) { /* this save_date may not be exactly the same as what we get in future, but then again neither mbox nor maildir guarantees it anyway. */ mail->data.save_date = ioloop_time; } (void)index_mail_parse_body_finish(mail, 0, success); } static bool index_mail_update_pvt_flags(struct mail *_mail, enum modify_type modify_type, enum mail_flags pvt_flags) { struct mail_private *mail = (struct mail_private *)_mail; const struct mail_index_record *rec; enum mail_flags old_pvt_flags; if (!index_mail_get_pvt(_mail)) return FALSE; if (pvt_flags == 0 && modify_type != MODIFY_REPLACE) return FALSE; /* see if the flags actually change anything */ rec = mail_index_lookup(_mail->transaction->view_pvt, mail->seq_pvt); old_pvt_flags = rec->flags & mailbox_get_private_flags_mask(_mail->box); switch (modify_type) { case MODIFY_ADD: return (old_pvt_flags & pvt_flags) != pvt_flags; case MODIFY_REPLACE: return old_pvt_flags != pvt_flags; case MODIFY_REMOVE: return (old_pvt_flags & pvt_flags) != 0; } i_unreached(); } void index_mail_update_flags(struct mail *_mail, enum modify_type modify_type, enum mail_flags flags) { struct mail_private *mail = (struct mail_private *)_mail; enum mail_flags pvt_flags_mask, pvt_flags = 0; bool update_modseq = FALSE; flags &= MAIL_FLAGS_NONRECENT | MAIL_INDEX_MAIL_FLAG_BACKEND; if (_mail->box->view_pvt != NULL) { /* mailbox has private flags */ pvt_flags_mask = mailbox_get_private_flags_mask(_mail->box); pvt_flags = flags & pvt_flags_mask; flags &= ~pvt_flags_mask; if (index_mail_update_pvt_flags(_mail, modify_type, pvt_flags)) { mail_index_update_flags(_mail->transaction->itrans_pvt, mail->seq_pvt, modify_type, pvt_flags); update_modseq = TRUE; } } if (!update_modseq) { /* no forced modseq update */ } else if (modify_type == MODIFY_REMOVE) { /* add the modseq update separately */ mail_index_update_flags(_mail->transaction->itrans, _mail->seq, MODIFY_ADD, (enum mail_flags )MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ); } else { /* add as part of the flag updates */ flags |= MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ; } mail_index_update_flags(_mail->transaction->itrans, _mail->seq, modify_type, flags); } void index_mail_update_keywords(struct mail *mail, enum modify_type modify_type, struct mail_keywords *keywords) { struct index_mail *imail = (struct index_mail *)mail; if (array_is_created(&imail->data.keyword_indexes)) array_free(&imail->data.keyword_indexes); if (array_is_created(&imail->data.keywords)) { /* clear the keywords array so the next mail_get_keywords() returns the updated keywords. don't free the array, because then any existing mail_get_keywords() return values would point to broken data. this won't leak memory because the array is allocated from mail's memory pool. */ memset(&imail->data.keywords, 0, sizeof(imail->data.keywords)); } mail_index_update_keywords(mail->transaction->itrans, mail->seq, modify_type, keywords); } void index_mail_update_modseq(struct mail *mail, uint64_t min_modseq) { mail_index_update_modseq(mail->transaction->itrans, mail->seq, min_modseq); } void index_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) { if (mail->box->view_pvt == NULL) return; index_transaction_init_pvt(mail->transaction); mail_index_update_modseq(mail->transaction->itrans_pvt, mail->seq, min_pvt_modseq); } void index_mail_expunge(struct mail *mail) { enum mail_lookup_abort old_abort = mail->lookup_abort; const char *value; guid_128_t guid_128; mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; if (mail_get_special(mail, MAIL_FETCH_GUID, &value) < 0) mail_index_expunge(mail->transaction->itrans, mail->seq); else { mail_generate_guid_128_hash(value, guid_128); mail_index_expunge_guid(mail->transaction->itrans, mail->seq, guid_128); } mail->lookup_abort = old_abort; } static void index_mail_parse(struct mail *mail, bool parse_body) { struct index_mail *imail = (struct index_mail *)mail; imail->data.access_part |= PARSE_HDR; if (index_mail_parse_headers(imail, NULL, "precache") == 0) { if (parse_body) { imail->data.access_part |= PARSE_BODY; (void)index_mail_parse_body(imail, 0); } } } void index_mail_precache(struct mail *mail) { struct index_mail *imail = (struct index_mail *)mail; enum mail_fetch_field cache; time_t date; uoff_t size; const char *str; if (mail_cache_field_exists_any(mail->transaction->cache_view, mail->seq)) { /* already cached this mail (we should get here only if FTS plugin decreased the first precached seq) */ return; } cache = imail->data.wanted_fields; if ((cache & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0) index_mail_parse(mail, (cache & MAIL_FETCH_STREAM_BODY) != 0); if ((cache & MAIL_FETCH_RECEIVED_DATE) != 0) (void)mail_get_received_date(mail, &date); if ((cache & MAIL_FETCH_SAVE_DATE) != 0) (void)mail_get_save_date(mail, &date); if ((cache & MAIL_FETCH_VIRTUAL_SIZE) != 0) (void)mail_get_virtual_size(mail, &size); if ((cache & MAIL_FETCH_PHYSICAL_SIZE) != 0) (void)mail_get_physical_size(mail, &size); if ((cache & MAIL_FETCH_UIDL_BACKEND) != 0) (void)mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &str); if ((cache & MAIL_FETCH_POP3_ORDER) != 0) (void)mail_get_special(mail, MAIL_FETCH_POP3_ORDER, &str); if ((cache & MAIL_FETCH_GUID) != 0) (void)mail_get_special(mail, MAIL_FETCH_GUID, &str); } static void index_mail_reset_vsize_ext(struct mail *mail) { unsigned int idx; uint32_t vsize = 0; struct mail_index_view *view = mail->transaction->view; if (mail_index_map_get_ext_idx(view->map, mail->box->mail_vsize_ext_id, &idx)) { mail_index_update_ext(mail->transaction->itrans, mail->seq, mail->box->mail_vsize_ext_id, &vsize, NULL); } } void index_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field) { index_mail_set_cache_corrupted_reason(mail, field, ""); } void index_mail_set_cache_corrupted_reason(struct mail *mail, enum mail_fetch_field field, const char *reason) { struct index_mail *imail = (struct index_mail *)mail; const char *field_name; switch ((int)field) { case 0: field_name = "fields"; break; case MAIL_FETCH_PHYSICAL_SIZE: field_name = "physical size"; imail->data.physical_size = (uoff_t)-1; imail->data.virtual_size = (uoff_t)-1; imail->data.parts = NULL; index_mail_reset_vsize_ext(mail); break; case MAIL_FETCH_VIRTUAL_SIZE: field_name = "virtual size"; imail->data.physical_size = (uoff_t)-1; imail->data.virtual_size = (uoff_t)-1; imail->data.parts = NULL; index_mail_reset_vsize_ext(mail); break; case MAIL_FETCH_MESSAGE_PARTS: field_name = "MIME parts"; imail->data.parts = NULL; break; case MAIL_FETCH_IMAP_BODY: field_name = "IMAP BODY"; imail->data.body = NULL; imail->data.bodystructure = NULL; break; case MAIL_FETCH_IMAP_BODYSTRUCTURE: field_name = "IMAP BODYSTRUCTURE"; imail->data.body = NULL; imail->data.bodystructure = NULL; break; default: field_name = t_strdup_printf("#%x", field); } /* make sure we don't cache invalid values */ mail_cache_transaction_reset(mail->transaction->cache_trans); imail->data.no_caching = TRUE; imail->data.forced_no_caching = TRUE; if (reason[0] == '\0') { mail_set_mail_cache_corrupted(mail, "Broken %s in mailbox %s", field_name, mail->box->vname); } else { mail_set_mail_cache_corrupted(mail, "Broken %s in mailbox %s: %s", field_name, mail->box->vname, reason); } } int index_mail_opened(struct mail *mail ATTR_UNUSED, struct istream **stream ATTR_UNUSED) { return 0; } void index_mail_save_finish(struct mail_save_context *ctx) { struct index_mail *imail = (struct index_mail *)ctx->dest_mail; if (ctx->data.from_envelope != NULL && imail->data.from_envelope == NULL) { imail->data.from_envelope = p_strdup(imail->mail.data_pool, ctx->data.from_envelope); } } const char *index_mail_cache_reason(struct mail *mail, const char *reason) { const char *cache_reason = mail_cache_get_missing_reason(mail->transaction->cache_view, mail->seq); return t_strdup_printf("%s (%s)", reason, cache_reason); } dovecot-2.2.33.2/src/lib-storage/index/index-sync-private.h0000644000175000017500000000243213123174404020335 00000000000000#ifndef INDEX_SYNC_PRIVATE_H #define INDEX_SYNC_PRIVATE_H #include "index-storage.h" struct index_mailbox_sync_pvt_context; struct index_mailbox_sync_context { struct mailbox_sync_context ctx; struct mail_index_view_sync_ctx *sync_ctx; uint32_t messages_count; ARRAY_TYPE(seq_range) flag_updates; ARRAY_TYPE(seq_range) hidden_updates; ARRAY_TYPE(seq_range) all_flag_update_uids; const ARRAY_TYPE(seq_range) *expunges; unsigned int flag_update_idx, hidden_update_idx, expunge_pos; bool failed; }; void index_sync_search_results_uidify(struct index_mailbox_sync_context *ctx); void index_sync_search_results_update(struct index_mailbox_sync_context *ctx); void index_sync_search_results_expunge(struct index_mailbox_sync_context *ctx); /* Returns 1 = ok, 0 = no private indexes, -1 = error */ int index_mailbox_sync_pvt_init(struct mailbox *box, bool lock, struct index_mailbox_sync_pvt_context **ctx_r); int index_mailbox_sync_pvt_newmails(struct index_mailbox_sync_pvt_context *ctx, struct mailbox_transaction_context *trans); int index_mailbox_sync_pvt_view(struct index_mailbox_sync_pvt_context *ctx, ARRAY_TYPE(seq_range) *flag_updates, ARRAY_TYPE(seq_range) *hidden_updates); void index_mailbox_sync_pvt_deinit(struct index_mailbox_sync_pvt_context **ctx); #endif dovecot-2.2.33.2/src/lib-storage/index/index-search-result.c0000644000175000017500000001266413165463624020510 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "seq-range-array.h" #include "mail-search.h" #include "mailbox-search-result-private.h" #include "index-storage.h" #include "index-search-result.h" static void search_result_range_remove(struct mail_search_result *result, const ARRAY_TYPE(seq_range) *changed_uids_arr, unsigned int *idx, uint32_t *next_uid, uint32_t last_uid) { const struct seq_range *uids; unsigned int i, count; uint32_t uid; /* remove full seq_ranges */ uid = *next_uid; uids = array_get(changed_uids_arr, &count); for (i = *idx; uids[i].seq2 < last_uid;) { i_assert(uids[i].seq1 <= uid); for (; uid <= uids[i].seq2; uid++) mailbox_search_result_remove(result, uid); i++; i_assert(i < count); uid = uids[i].seq1; } /* remove the last seq_range */ i_assert(uids[i].seq1 <= uid && uids[i].seq2 >= last_uid); for (; uid <= last_uid; uid++) mailbox_search_result_remove(result, uid); if (uid > uids[i].seq2) { /* finished this range */ if (++i < count) uid = uids[i].seq1; else { /* this was the last searched message */ uid = 0; } } *next_uid = uid; *idx = i; } static int search_result_update_search(struct mail_search_result *result, const ARRAY_TYPE(seq_range) *changed_uids_arr) { struct mailbox_transaction_context *t; struct mail_search_context *search_ctx; struct mail *mail; const struct seq_range *changed_uids; unsigned int changed_count, changed_idx; uint32_t next_uid; int ret; changed_uids = array_get(changed_uids_arr, &changed_count); i_assert(changed_count > 0); next_uid = changed_uids[0].seq1; changed_idx = 0; mail_search_args_init(result->search_args, result->box, FALSE, NULL); t = mailbox_transaction_begin(result->box, 0); search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL); /* tell search that we're updating an existing search result, so it can do some optimizations based on it */ search_ctx->update_result = result; while (mailbox_search_next(search_ctx, &mail)) { i_assert(next_uid != 0); if (next_uid != mail->uid) { /* some messages in changed_uids didn't match. make sure they don't exist in the search result. */ search_result_range_remove(result, changed_uids_arr, &changed_idx, &next_uid, mail->uid-1); i_assert(next_uid == mail->uid); } if (changed_uids[changed_idx].seq2 > next_uid) { next_uid++; } else if (++changed_idx < changed_count) { next_uid = changed_uids[changed_idx].seq1; } else { /* this was the last searched message */ next_uid = 0; } /* match - make sure it exists in search result */ mailbox_search_result_add(result, mail->uid); } mail_search_args_deinit(result->search_args); ret = mailbox_search_deinit(&search_ctx); if (next_uid != 0 && ret == 0) { /* last message(s) didn't match. make sure they don't exist in the search result. */ search_result_range_remove(result, changed_uids_arr, &changed_idx, &next_uid, changed_uids[changed_count-1].seq2); } if (mailbox_transaction_commit(&t) < 0) ret = -1; return ret; } int index_search_result_update_flags(struct mail_search_result *result, const ARRAY_TYPE(seq_range) *uids) { struct mail_search_arg search_arg; int ret; if (array_count(uids) == 0) return 0; /* add a temporary search parameter to limit the search only to the changed messages */ i_zero(&search_arg); search_arg.type = SEARCH_UIDSET; search_arg.value.seqset = *uids; search_arg.next = result->search_args->args; result->search_args->args = &search_arg; ret = search_result_update_search(result, uids); i_assert(result->search_args->args == &search_arg); result->search_args->args = search_arg.next; return ret; } int index_search_result_update_appends(struct mail_search_result *result, unsigned int old_messages_count) { struct mailbox_transaction_context *t; struct mail_search_context *search_ctx; struct mail *mail; struct mail_search_arg search_arg; uint32_t message_count; int ret; message_count = mail_index_view_get_messages_count(result->box->view); if (old_messages_count == message_count) { /* no new messages */ return 0; } /* add a temporary search parameter to limit the search only to the new messages */ i_zero(&search_arg); search_arg.type = SEARCH_SEQSET; t_array_init(&search_arg.value.seqset, 1); seq_range_array_add_range(&search_arg.value.seqset, old_messages_count + 1, message_count); search_arg.next = result->search_args->args; result->search_args->args = &search_arg; /* add all messages matching the search to search result */ t = mailbox_transaction_begin(result->box, 0); search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL); while (mailbox_search_next(search_ctx, &mail)) mailbox_search_result_add(result, mail->uid); ret = mailbox_search_deinit(&search_ctx); if (mailbox_transaction_commit(&t) < 0) ret = -1; i_assert(result->search_args->args == &search_arg); result->search_args->args = search_arg.next; return ret; } void index_search_results_update_expunges(struct mailbox *box, const ARRAY_TYPE(seq_range) *expunges) { const struct seq_range *seqs; uint32_t seq, uid; if (array_count(&box->search_results) == 0) return; array_foreach(expunges, seqs) { for (seq = seqs->seq1; seq <= seqs->seq2; seq++) { mail_index_lookup_uid(box->view, seq, &uid); mailbox_search_results_remove(box, uid); } } } dovecot-2.2.33.2/src/lib-storage/index/istream-mail.c0000644000175000017500000001176213165463624017204 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage-private.h" #include "istream-private.h" #include "index-mail.h" #include "istream-mail.h" struct mail_istream { struct istream_private istream; struct mail *mail; uoff_t expected_size; unsigned int files_read_increased:1; unsigned int input_has_body:1; }; static bool i_stream_mail_try_get_cached_size(struct mail_istream *mstream) { struct mail *mail = mstream->mail; enum mail_lookup_abort orig_lookup_abort; if (mstream->expected_size != (uoff_t)-1) return TRUE; /* make sure this call doesn't change any existing error message, just in case there's already something important in it. */ mail_storage_last_error_push(mail->box->storage); orig_lookup_abort = mail->lookup_abort; mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; if (mail_get_physical_size(mail, &mstream->expected_size) < 0) mstream->expected_size = (uoff_t)-1; mail->lookup_abort = orig_lookup_abort; mail_storage_last_error_pop(mail->box->storage); return mstream->expected_size != (uoff_t)-1; } static const char * i_stream_mail_get_cached_mail_id(struct mail_istream *mstream ATTR_UNUSED) { #if 0 /* FIXME: This function may get called in the middle of header parsing, which then goes into parsing cached headers and causes crashes. So disable this for now. Eventually it would be nice if recursion was possible by each parser using its own private struct. */ static const char *headers[] = { "Message-Id", "Date", "Subject" }; struct mail *mail = mstream->mail; enum mail_lookup_abort orig_lookup_abort; const char *value, *ret = ""; unsigned int i; orig_lookup_abort = mail->lookup_abort; mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; for (i = 0; i < N_ELEMENTS(headers); i++) { if (mail_get_first_header(mail, headers[i], &value) > 0) { ret = t_strdup_printf("%s=%s", headers[i], value); break; } } mail->lookup_abort = orig_lookup_abort; return ret; #else return ""; #endif } static void i_stream_mail_set_size_corrupted(struct mail_istream *mstream, size_t size) { uoff_t cur_size = mstream->istream.istream.v_offset + size; const char *str, *mail_id; char chr; if (mstream->expected_size < cur_size) { str = "smaller"; chr = '<'; } else { str = "larger"; chr = '>'; } mail_id = i_stream_mail_get_cached_mail_id(mstream); if (mail_id[0] != '\0') mail_id = t_strconcat(", cached ", mail_id, NULL); io_stream_set_error(&mstream->istream.iostream, "Cached message size %s than expected " "(%"PRIuUOFF_T" %c %"PRIuUOFF_T", box=%s, UID=%u%s)", str, mstream->expected_size, chr, cur_size, mailbox_get_vname(mstream->mail->box), mstream->mail->uid, mail_id); mail_set_cache_corrupted_reason(mstream->mail, MAIL_FETCH_PHYSICAL_SIZE, t_strdup_printf("read(%s) failed: %s", i_stream_get_name(&mstream->istream.istream), mstream->istream.iostream.error)); mstream->istream.istream.stream_errno = EINVAL; } static ssize_t i_stream_mail_read(struct istream_private *stream) { struct mail_istream *mstream = (struct mail_istream *)stream; size_t size; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); size = i_stream_get_data_size(&stream->istream); if (ret > 0) { mstream->mail->transaction->stats.files_read_bytes += ret; if (!mstream->files_read_increased) { mstream->files_read_increased = TRUE; mstream->mail->transaction->stats.files_read_count++; } if (mstream->expected_size < stream->istream.v_offset + size) { i_stream_mail_set_size_corrupted(mstream, size); return -1; } } else if (ret == -1 && stream->istream.eof) { if (!mstream->input_has_body) { /* trying to read past the header, but this stream doesn't have the body */ return -1; } if (stream->istream.stream_errno != 0) { if (stream->istream.stream_errno == ENOENT) { /* update mail's expunged-flag if needed */ index_mail_refresh_expunged(mstream->mail); } return -1; } if (i_stream_mail_try_get_cached_size(mstream) && mstream->expected_size > stream->istream.v_offset + size) { i_stream_mail_set_size_corrupted(mstream, size); return -1; } } return ret; } struct istream *i_stream_create_mail(struct mail *mail, struct istream *input, bool input_has_body) { struct mail_istream *mstream; mstream = i_new(struct mail_istream, 1); mstream->mail = mail; mstream->input_has_body = input_has_body; mstream->expected_size = (uoff_t)-1; (void)i_stream_mail_try_get_cached_size(mstream); mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; mstream->istream.stream_size_passthrough = TRUE; mstream->istream.read = i_stream_mail_read; mstream->istream.istream.readable_fd = input->readable_fd; mstream->istream.istream.blocking = input->blocking; mstream->istream.istream.seekable = input->seekable; return i_stream_create(&mstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-storage/index/index-attribute.c0000644000175000017500000002043313165463624017723 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "dict.h" #include "index-storage.h" struct index_storage_attribute_iter { struct mailbox_attribute_iter iter; struct dict_iterate_context *diter; char *prefix; size_t prefix_len; bool dict_disabled; }; static struct mail_namespace * mail_user_find_attribute_namespace(struct mail_user *user) { struct mail_namespace *ns; ns = mail_namespace_find_inbox(user->namespaces); if (ns != NULL) return ns; for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) return ns; } return NULL; } static int index_storage_get_user_dict(struct mail_storage *err_storage, struct mail_user *user, struct dict **dict_r) { struct mail_namespace *ns; struct mail_storage *attr_storage; const char *error; if (user->_attr_dict != NULL) { *dict_r = user->_attr_dict; return 0; } if (user->attr_dict_failed) { mail_storage_set_internal_error(err_storage); return -1; } ns = mail_user_find_attribute_namespace(user); if (ns == NULL) { /* probably never happens? */ mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox attributes not available for this mailbox"); return -1; } attr_storage = mail_namespace_get_default_storage(ns); if (*attr_storage->set->mail_attribute_dict == '\0') { mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox attributes not enabled"); return -1; } if (dict_init(attr_storage->set->mail_attribute_dict, DICT_DATA_TYPE_STRING, user->username, user->set->base_dir, &user->_attr_dict, &error) < 0) { mail_storage_set_critical(err_storage, "mail_attribute_dict: dict_init(%s) failed: %s", attr_storage->set->mail_attribute_dict, error); user->attr_dict_failed = TRUE; return -1; } *dict_r = user->_attr_dict; return 0; } static int index_storage_get_dict(struct mailbox *box, enum mail_attribute_type type, struct dict **dict_r, const char **mailbox_prefix_r) { struct mail_storage *storage = box->storage; struct mail_namespace *ns; struct mailbox_metadata metadata; struct dict_settings set; const char *error; if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) return -1; *mailbox_prefix_r = guid_128_to_string(metadata.guid); ns = mailbox_get_namespace(box); if (type == MAIL_ATTRIBUTE_TYPE_PRIVATE) { /* private attributes are stored in user's own dict */ return index_storage_get_user_dict(storage, storage->user, dict_r); } else if (ns->user == ns->owner) { /* user owns the mailbox. shared attributes are stored in the same dict. */ return index_storage_get_user_dict(storage, storage->user, dict_r); } else if (ns->owner != NULL) { /* accessing shared attribute of a shared mailbox. use the owner's dict. */ return index_storage_get_user_dict(storage, ns->owner, dict_r); } /* accessing shared attributes of a public mailbox. no user owns it, so use the storage's dict. */ if (storage->_shared_attr_dict != NULL) { *dict_r = storage->_shared_attr_dict; return 0; } if (*storage->set->mail_attribute_dict == '\0') { mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox attributes not enabled"); return -1; } if (storage->shared_attr_dict_failed) { mail_storage_set_internal_error(storage); return -1; } i_zero(&set); set.username = storage->user->username; set.base_dir = storage->user->set->base_dir; if (mail_user_get_home(storage->user, &set.home_dir) <= 0) set.home_dir = NULL; if (dict_init_full(storage->set->mail_attribute_dict, &set, &storage->_shared_attr_dict, &error) < 0) { mail_storage_set_critical(storage, "mail_attribute_dict: dict_init(%s) failed: %s", storage->set->mail_attribute_dict, error); storage->shared_attr_dict_failed = TRUE; return -1; } *dict_r = storage->_shared_attr_dict; return 0; } static const char * key_get_prefixed(enum mail_attribute_type type, const char *mailbox_prefix, const char *key) { switch (type) { case MAIL_ATTRIBUTE_TYPE_PRIVATE: return t_strconcat(DICT_PATH_PRIVATE, mailbox_prefix, "/", key, NULL); case MAIL_ATTRIBUTE_TYPE_SHARED: return t_strconcat(DICT_PATH_SHARED, mailbox_prefix, "/", key, NULL); } i_unreached(); } static int index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t, enum mail_attribute_type type, struct dict_transaction_context **dtrans_r, const char **mailbox_prefix_r) { struct dict_transaction_context **dtransp = NULL; struct dict *dict; struct mailbox_metadata metadata; switch (type) { case MAIL_ATTRIBUTE_TYPE_PRIVATE: dtransp = &t->attr_pvt_trans; break; case MAIL_ATTRIBUTE_TYPE_SHARED: dtransp = &t->attr_shared_trans; break; } i_assert(dtransp != NULL); if (*dtransp != NULL) { /* transaction already created */ if (mailbox_get_metadata(t->box, MAILBOX_METADATA_GUID, &metadata) < 0) return -1; *mailbox_prefix_r = guid_128_to_string(metadata.guid); *dtrans_r = *dtransp; return 0; } if (index_storage_get_dict(t->box, type, &dict, mailbox_prefix_r) < 0) return -1; *dtransp = *dtrans_r = dict_transaction_begin(dict); return 0; } int index_storage_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value) { struct dict_transaction_context *dtrans; const char *mailbox_prefix; bool pvt = type == MAIL_ATTRIBUTE_TYPE_PRIVATE; time_t ts = value->last_change != 0 ? value->last_change : ioloop_time; int ret = 0; if (index_storage_attribute_get_dict_trans(t, type, &dtrans, &mailbox_prefix) < 0) return -1; T_BEGIN { const char *prefixed_key = key_get_prefixed(type, mailbox_prefix, key); const char *value_str; if (mailbox_attribute_value_to_string(t->box->storage, value, &value_str) < 0) { ret = -1; } else if (value_str != NULL) { dict_set(dtrans, prefixed_key, value_str); mail_index_attribute_set(t->itrans, pvt, key, ts, strlen(value_str)); } else { dict_unset(dtrans, prefixed_key); mail_index_attribute_unset(t->itrans, pvt, key, ts); } } T_END; return ret; } int index_storage_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r) { struct dict *dict; const char *mailbox_prefix; int ret; i_zero(value_r); if (index_storage_get_dict(t->box, type, &dict, &mailbox_prefix) < 0) return -1; ret = dict_lookup(dict, pool_datastack_create(), key_get_prefixed(type, mailbox_prefix, key), &value_r->value); if (ret < 0) { mail_storage_set_internal_error(t->box->storage); return -1; } return ret; } struct mailbox_attribute_iter * index_storage_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix) { struct index_storage_attribute_iter *iter; struct dict *dict; const char *mailbox_prefix; iter = i_new(struct index_storage_attribute_iter, 1); iter->iter.box = box; if (index_storage_get_dict(box, type, &dict, &mailbox_prefix) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTPOSSIBLE) iter->dict_disabled = TRUE; } else { iter->prefix = i_strdup(key_get_prefixed(type, mailbox_prefix, prefix)); iter->prefix_len = strlen(iter->prefix); iter->diter = dict_iterate_init(dict, iter->prefix, DICT_ITERATE_FLAG_RECURSE | DICT_ITERATE_FLAG_NO_VALUE); } return &iter->iter; } const char * index_storage_attribute_iter_next(struct mailbox_attribute_iter *_iter) { struct index_storage_attribute_iter *iter = (struct index_storage_attribute_iter *)_iter; const char *key, *value; if (iter->diter == NULL || !dict_iterate(iter->diter, &key, &value)) return NULL; i_assert(strncmp(key, iter->prefix, iter->prefix_len) == 0); key += iter->prefix_len; return key; } int index_storage_attribute_iter_deinit(struct mailbox_attribute_iter *_iter) { struct index_storage_attribute_iter *iter = (struct index_storage_attribute_iter *)_iter; int ret; if (iter->diter == NULL) { ret = iter->dict_disabled ? 0 : -1; } else { if ((ret = dict_iterate_deinit(&iter->diter)) < 0) mail_storage_set_internal_error(_iter->box->storage); } i_free(iter->prefix); i_free(iter); return ret; } dovecot-2.2.33.2/src/lib-storage/index/index-rebuild.c0000644000175000017500000001770413165463624017355 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-cache.h" #include "mail-index-modseq.h" #include "mailbox-list-private.h" #include "index-storage.h" #include "index-rebuild.h" static void index_index_copy_vsize(struct index_rebuild_context *ctx, struct mail_index_view *view, uint32_t old_seq, uint32_t new_seq) { const void *data; bool expunged; mail_index_lookup_ext(view, old_seq, ctx->box->mail_vsize_ext_id, &data, &expunged); if (data != NULL && !expunged) { mail_index_update_ext(ctx->trans, new_seq, ctx->box->mail_vsize_ext_id, data, NULL); } } static void index_index_copy_cache(struct index_rebuild_context *ctx, struct mail_index_view *view, uint32_t old_seq, uint32_t new_seq) { struct mail_index_map *map; const void *data; uint32_t reset_id; bool expunged; if (ctx->cache_ext_id == (uint32_t)-1) return; mail_index_lookup_ext_full(view, old_seq, ctx->cache_ext_id, &map, &data, &expunged); if (expunged) return; if (!mail_index_ext_get_reset_id(view, map, ctx->cache_ext_id, &reset_id) || reset_id == 0) return; if (!ctx->cache_used) { /* set reset id */ ctx->cache_used = TRUE; ctx->cache_reset_id = reset_id; mail_index_ext_reset(ctx->trans, ctx->cache_ext_id, ctx->cache_reset_id, TRUE); } if (ctx->cache_reset_id == reset_id) { mail_index_update_ext(ctx->trans, new_seq, ctx->cache_ext_id, data, NULL); } } static void index_index_copy_from_old(struct index_rebuild_context *ctx, struct mail_index_view *view, uint32_t old_seq, uint32_t new_seq) { struct mail_index *index = mail_index_view_get_index(view); const struct mail_index_record *rec; ARRAY_TYPE(keyword_indexes) old_keywords; struct mail_keywords *kw; uint64_t modseq; /* copy flags */ rec = mail_index_lookup(view, old_seq); mail_index_update_flags(ctx->trans, new_seq, MODIFY_REPLACE, rec->flags); /* copy keywords */ t_array_init(&old_keywords, 32); mail_index_lookup_keywords(view, old_seq, &old_keywords); kw = mail_index_keywords_create_from_indexes(index, &old_keywords); mail_index_update_keywords(ctx->trans, new_seq, MODIFY_REPLACE, kw); mail_index_keywords_unref(&kw); /* copy modseq */ modseq = mail_index_modseq_lookup(view, old_seq); mail_index_update_modseq(ctx->trans, new_seq, modseq); index_index_copy_vsize(ctx, view, old_seq, new_seq); index_index_copy_cache(ctx, view, old_seq, new_seq); } void index_rebuild_index_metadata(struct index_rebuild_context *ctx, uint32_t new_seq, uint32_t uid) { uint32_t old_seq; if (mail_index_lookup_seq(ctx->view, uid, &old_seq)) { /* the message exists in the old index. copy the metadata from it. */ index_index_copy_from_old(ctx, ctx->view, old_seq, new_seq); } else if (ctx->backup_view != NULL && mail_index_lookup_seq(ctx->backup_view, uid, &old_seq)) { /* copy the metadata from backup index. */ index_index_copy_from_old(ctx, ctx->backup_view, old_seq, new_seq); } } static void index_rebuild_header(struct index_rebuild_context *ctx, index_rebuild_generate_uidvalidity_t *gen_uidvalidity) { const struct mail_index_header *hdr, *backup_hdr, *trans_hdr; struct mail_index *index = mail_index_view_get_index(ctx->view); struct mail_index_modseq_header modseq_hdr; struct mail_index_view *trans_view; uint32_t uid_validity, next_uid, first_recent_uid; uint64_t modseq; hdr = mail_index_get_header(ctx->view); backup_hdr = ctx->backup_view == NULL ? NULL : mail_index_get_header(ctx->backup_view); trans_view = mail_index_transaction_open_updated_view(ctx->trans); trans_hdr = mail_index_get_header(trans_view); /* set uidvalidity */ if (hdr->uid_validity != 0) uid_validity = hdr->uid_validity; else if (backup_hdr != NULL && backup_hdr->uid_validity != 0) uid_validity = backup_hdr->uid_validity; else uid_validity = gen_uidvalidity(ctx->box->list); mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); /* set next-uid */ if (hdr->next_uid != 0) next_uid = hdr->next_uid; else if (backup_hdr != NULL && backup_hdr->next_uid != 0) next_uid = backup_hdr->next_uid; else next_uid = 1; if (next_uid > trans_hdr->next_uid) { mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, next_uid), &next_uid, sizeof(next_uid), FALSE); } /* set first_recent_uid */ first_recent_uid = hdr->first_recent_uid; if (backup_hdr != NULL && backup_hdr->first_recent_uid > first_recent_uid && backup_hdr->first_recent_uid <= next_uid) first_recent_uid = backup_hdr->first_recent_uid; first_recent_uid = I_MIN(first_recent_uid, next_uid); mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); /* set highest-modseq */ i_zero(&modseq_hdr); modseq_hdr.highest_modseq = mail_index_modseq_get_highest(ctx->view); if (ctx->backup_view != NULL) { modseq = mail_index_modseq_get_highest(ctx->backup_view); if (modseq_hdr.highest_modseq < modseq) modseq_hdr.highest_modseq = modseq; } mail_index_update_header_ext(ctx->trans, index->modseq_ext_id, 0, &modseq_hdr, sizeof(modseq_hdr)); mail_index_view_close(&trans_view); } static void index_rebuild_box_name_header(struct index_rebuild_context *ctx) { const void *name_hdr; size_t name_hdr_size; mail_index_get_header_ext(ctx->view, ctx->box->box_name_hdr_ext_id, &name_hdr, &name_hdr_size); if (name_hdr_size == 0 && ctx->backup_view != NULL) { mail_index_get_header_ext(ctx->backup_view, ctx->box->box_name_hdr_ext_id, &name_hdr, &name_hdr_size); } if (name_hdr_size == 0) return; mail_index_update_header_ext(ctx->trans, ctx->box->box_name_hdr_ext_id, 0, name_hdr, name_hdr_size); } struct index_rebuild_context * index_index_rebuild_init(struct mailbox *box, struct mail_index_view *view, struct mail_index_transaction *trans) { struct index_rebuild_context *ctx; const char *index_dir, *backup_path; enum mail_index_open_flags open_flags = MAIL_INDEX_OPEN_FLAG_READONLY; ctx = i_new(struct index_rebuild_context, 1); ctx->box = box; ctx->view = view; ctx->trans = trans; mail_index_reset(ctx->trans); mailbox_recent_flags_reset(box); (void)mail_index_ext_lookup(box->index, "cache", &ctx->cache_ext_id); /* open cache and read the caching decisions. */ (void)mail_cache_open_and_verify(ctx->box->cache); /* if backup index file exists, try to use it */ index_dir = mailbox_get_index_path(box); backup_path = t_strconcat(box->index_prefix, ".backup", NULL); ctx->backup_index = mail_index_alloc(index_dir, backup_path); #ifndef MMAP_CONFLICTS_WRITE if (box->storage->set->mmap_disable) #endif open_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; mail_index_set_lock_method(ctx->backup_index, box->storage->set->parsed_lock_method, UINT_MAX); if (mail_index_open(ctx->backup_index, open_flags) <= 0) mail_index_free(&ctx->backup_index); else ctx->backup_view = mail_index_view_open(ctx->backup_index); return ctx; } void index_index_rebuild_deinit(struct index_rebuild_context **_ctx, index_rebuild_generate_uidvalidity_t *cb) { struct index_rebuild_context *ctx = *_ctx; struct mail_cache_compress_lock *lock = NULL; *_ctx = NULL; /* initialize cache file with the old field decisions */ (void)mail_cache_compress(ctx->box->cache, ctx->trans, &lock); if (lock != NULL) { /* FIXME: this is a bit too early. ideally we should return it from this function and unlock only after the transaction is committed, but it would be an API change and this rebuilding isn't happening normally anyway. */ mail_cache_compress_unlock(&lock); } index_rebuild_header(ctx, cb); index_rebuild_box_name_header(ctx); if (ctx->backup_index != NULL) { mail_index_view_close(&ctx->backup_view); mail_index_close(ctx->backup_index); mail_index_free(&ctx->backup_index); } i_free(ctx); } dovecot-2.2.33.2/src/lib-storage/index/index-sort.h0000644000175000017500000000105413123174404016677 00000000000000#ifndef INDEX_SORT_H #define INDEX_SORT_H struct mail_search_sort_program; struct mail_search_sort_program * index_sort_program_init(struct mailbox_transaction_context *t, const enum mail_sort_type *sort_program); int index_sort_program_deinit(struct mail_search_sort_program **program); void index_sort_list_add(struct mail_search_sort_program *program, struct mail *mail); void index_sort_list_finish(struct mail_search_sort_program *program); bool index_sort_list_next(struct mail_search_sort_program *program, uint32_t *seq_r); #endif dovecot-2.2.33.2/src/lib-storage/index/shared/0002755000175000017500000000000013172375611015771 500000000000000dovecot-2.2.33.2/src/lib-storage/index/shared/shared-storage.h0000644000175000017500000000074113165463624020776 00000000000000#ifndef SHARED_STORAGE_H #define SHARED_STORAGE_H struct shared_storage { struct mail_storage storage; union mailbox_list_module_context list_module_ctx; const char *ns_prefix_pattern; const char *location, *unexpanded_location; const char *storage_class_name; }; struct mailbox_list *shared_mailbox_list_alloc(void); /* Returns -1 = error, 0 = user doesn't exist, 1 = ok */ int shared_storage_get_namespace(struct mail_namespace **_ns, const char **_name); #endif dovecot-2.2.33.2/src/lib-storage/index/shared/Makefile.in0000644000175000017500000005450213172375573017771 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/shared ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_shared_la_LIBADD = am_libstorage_shared_la_OBJECTS = shared-list.lo shared-storage.lo libstorage_shared_la_OBJECTS = $(am_libstorage_shared_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_shared_la_SOURCES) DIST_SOURCES = $(libstorage_shared_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_shared.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_shared_la_SOURCES = \ shared-list.c \ shared-storage.c headers = \ shared-storage.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/shared/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/shared/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_shared.la: $(libstorage_shared_la_OBJECTS) $(libstorage_shared_la_DEPENDENCIES) $(EXTRA_libstorage_shared_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_shared_la_OBJECTS) $(libstorage_shared_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shared-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shared-storage.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/shared/shared-list.c0000644000175000017500000002024013165463624020274 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-match.h" #include "mailbox-tree.h" #include "mailbox-list-private.h" #include "index-storage.h" #include "shared-storage.h" extern struct mailbox_list shared_mailbox_list; static struct mailbox_list *shared_list_alloc(void) { struct mailbox_list *list; pool_t pool; pool = pool_alloconly_create("shared list", 2048); list = p_new(pool, struct mailbox_list, 1); *list = shared_mailbox_list; list->pool = pool; return list; } static void shared_list_deinit(struct mailbox_list *list) { pool_unref(&list->pool); } static void shared_list_copy_error(struct mailbox_list *shared_list, struct mail_namespace *backend_ns) { const char *str; enum mail_error error; str = mailbox_list_get_last_error(backend_ns->list, &error); mailbox_list_set_error(shared_list, error, str); } static int shared_get_storage(struct mailbox_list **list, const char *vname, struct mail_storage **storage_r) { struct mail_namespace *ns = (*list)->ns; const char *name; name = mailbox_list_get_storage_name(*list, vname); if (*name == '\0' && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) { /* trying to access the shared/ prefix itself */ *storage_r = ns->storage; return 0; } if (shared_storage_get_namespace(&ns, &name) < 0) return -1; *list = ns->list; return mailbox_list_get_storage(list, vname, storage_r); } static char shared_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED) { return '/'; } static int shared_list_get_path(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type, const char **path_r) { struct mail_namespace *ns = list->ns; if (mail_namespace_get_default_storage(list->ns) == NULL || name == NULL || shared_storage_get_namespace(&ns, &name) < 0) { /* we don't have a directory we can use. */ *path_r = NULL; return 0; } return mailbox_list_get_path(ns->list, name, type, path_r); } static const char * shared_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED) { i_panic("shared mailbox list: Can't return a temp prefix for '%s'", list->ns->prefix); return NULL; } static const char * shared_list_join_refpattern(struct mailbox_list *list, const char *ref, const char *pattern) { struct mail_namespace *ns = list->ns; const char *ns_ref, *prefix = list->ns->prefix; size_t prefix_len = strlen(prefix); if (*ref != '\0' && strncmp(ref, prefix, prefix_len) == 0) ns_ref = ref + prefix_len; else ns_ref = NULL; if (ns_ref != NULL && *ns_ref != '\0' && shared_storage_get_namespace(&ns, &ns_ref) == 0) return mailbox_list_join_refpattern(ns->list, ref, pattern); /* fallback to default behavior */ if (*ref != '\0') pattern = t_strconcat(ref, pattern, NULL); return pattern; } static void shared_list_create_missing_namespaces(struct mailbox_list *list, const char *const *patterns) { struct mail_namespace *ns; char sep = mail_namespace_get_sep(list->ns); const char *list_pat, *name; unsigned int i; for (i = 0; patterns[i] != NULL; i++) { const char *last = NULL, *p; /* we'll require that the pattern begins with the list's namespace prefix. we could also handle other patterns (e.g. %/user/%), but it's more of a theoretical problem. */ if (strncmp(list->ns->prefix, patterns[i], list->ns->prefix_len) != 0) continue; list_pat = patterns[i] + list->ns->prefix_len; for (p = list_pat; *p != '\0'; p++) { if (*p == '%' || *p == '*') break; if (*p == sep) last = p; } if (last != NULL) { ns = list->ns; name = t_strdup_until(list_pat, last); (void)shared_storage_get_namespace(&ns, &name); } } } static struct mailbox_list_iterate_context * shared_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct mailbox_list_iterate_context *ctx; pool_t pool; char sep = mail_namespace_get_sep(list->ns); pool = pool_alloconly_create("mailbox list shared iter", 1024); ctx = p_new(pool, struct mailbox_list_iterate_context, 1); ctx->pool = pool; ctx->list = list; ctx->flags = flags; ctx->glob = imap_match_init_multiple(pool, patterns, FALSE, sep); array_create(&ctx->module_contexts, pool, sizeof(void *), 5); if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 && (list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) T_BEGIN { shared_list_create_missing_namespaces(list, patterns); } T_END; return ctx; } static const struct mailbox_info * shared_list_iter_next(struct mailbox_list_iterate_context *ctx ATTR_UNUSED) { return NULL; } static int shared_list_iter_deinit(struct mailbox_list_iterate_context *ctx) { pool_unref(&ctx->pool); return 0; } static int shared_list_subscriptions_refresh(struct mailbox_list *src_list, struct mailbox_list *dest_list) { char sep; if (dest_list->subscriptions == NULL) { sep = mail_namespace_get_sep(src_list->ns); dest_list->subscriptions = mailbox_tree_init(sep); } return 0; } static int shared_list_set_subscribed(struct mailbox_list *list, const char *name, bool set) { struct mail_namespace *ns = list->ns; int ret; if (shared_storage_get_namespace(&ns, &name) < 0) return -1; ret = mailbox_list_set_subscribed(ns->list, name, set); if (ret < 0) shared_list_copy_error(list, ns); return ret; } static int shared_list_delete_mailbox(struct mailbox_list *list, const char *name) { struct mail_namespace *ns = list->ns; int ret; if (shared_storage_get_namespace(&ns, &name) < 0) return -1; ret = ns->list->v.delete_mailbox(ns->list, name); if (ret < 0) shared_list_copy_error(list, ns); return ret; } static int shared_list_delete_dir(struct mailbox_list *list, const char *name) { struct mail_namespace *ns = list->ns; int ret; if (shared_storage_get_namespace(&ns, &name) < 0) return -1; ret = mailbox_list_delete_dir(ns->list, name); if (ret < 0) shared_list_copy_error(list, ns); return ret; } static int shared_list_delete_symlink(struct mailbox_list *list, const char *name) { struct mail_namespace *ns = list->ns; int ret; if (shared_storage_get_namespace(&ns, &name) < 0) return -1; ret = mailbox_list_delete_symlink(ns->list, name); if (ret < 0) shared_list_copy_error(list, ns); return ret; } static int shared_list_rename_get_ns(struct mailbox_list *oldlist, const char **oldname, struct mailbox_list *newlist, const char **newname, struct mail_namespace **ns_r) { struct mail_namespace *old_ns = oldlist->ns, *new_ns = newlist->ns; if (shared_storage_get_namespace(&old_ns, oldname) < 0 || shared_storage_get_namespace(&new_ns, newname) < 0) return -1; if (old_ns != new_ns) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Can't rename shared mailboxes across storages."); return -1; } *ns_r = old_ns; return 0; } static int shared_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct mail_namespace *ns; int ret; if (shared_list_rename_get_ns(oldlist, &oldname, newlist, &newname, &ns) < 0) return -1; ret = ns->list->v.rename_mailbox(ns->list, oldname, ns->list, newname); if (ret < 0) shared_list_copy_error(oldlist, ns); return ret; } struct mailbox_list shared_mailbox_list = { .name = "shared", .props = 0, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, .v = { .alloc = shared_list_alloc, .deinit = shared_list_deinit, .get_storage = shared_get_storage, .get_hierarchy_sep = shared_list_get_hierarchy_sep, .get_vname = mailbox_list_default_get_vname, .get_storage_name = mailbox_list_default_get_storage_name, .get_path = shared_list_get_path, .get_temp_prefix = shared_list_get_temp_prefix, .join_refpattern = shared_list_join_refpattern, .iter_init = shared_list_iter_init, .iter_next = shared_list_iter_next, .iter_deinit = shared_list_iter_deinit, .subscriptions_refresh = shared_list_subscriptions_refresh, .set_subscribed = shared_list_set_subscribed, .delete_mailbox = shared_list_delete_mailbox, .delete_dir = shared_list_delete_dir, .delete_symlink = shared_list_delete_symlink, .rename_mailbox = shared_list_rename_mailbox, } }; dovecot-2.2.33.2/src/lib-storage/index/shared/shared-storage.c0000644000175000017500000002504713165463624020777 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "ioloop.h" #include "var-expand.h" #include "index-storage.h" #include "mail-storage-service.h" #include "mailbox-list-private.h" #include "fail-mail-storage.h" #include "shared-storage.h" #include extern struct mail_storage shared_storage; static struct mail_storage *shared_storage_alloc(void) { struct shared_storage *storage; pool_t pool; pool = pool_alloconly_create("shared storage", 1024); storage = p_new(pool, struct shared_storage, 1); storage->storage = shared_storage; storage->storage.pool = pool; return &storage->storage; } static int shared_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct shared_storage *storage = (struct shared_storage *)_storage; struct mail_storage *storage_class; const char *driver, *p; char *wildcardp, key; bool have_username; /* location must begin with the actual mailbox driver */ p = strchr(ns->set->location, ':'); if (p == NULL) { *error_r = "Shared mailbox location not prefixed with driver"; return -1; } driver = t_strdup_until(ns->set->location, p); storage->location = p_strdup(_storage->pool, ns->set->location); storage->unexpanded_location = p_strdup(_storage->pool, ns->unexpanded_set->location); storage->storage_class_name = p_strdup(_storage->pool, driver); storage_class = mail_user_get_storage_class(_storage->user, driver); if (storage_class != NULL) _storage->class_flags = storage_class->class_flags; else if (strcmp(driver, "auto") != 0) { *error_r = t_strconcat("Unknown shared storage driver: ", driver, NULL); return -1; } wildcardp = strchr(ns->prefix, '%'); if (wildcardp == NULL) { *error_r = "Shared namespace prefix doesn't contain %"; return -1; } storage->ns_prefix_pattern = p_strdup(_storage->pool, wildcardp); have_username = FALSE; for (p = storage->ns_prefix_pattern; *p != '\0'; p++) { if (*p != '%') continue; key = p[1]; if (key == 'u' || key == 'n') have_username = TRUE; else if (key != '%' && key != 'd') break; } if (*p != '\0') { *error_r = "Shared namespace prefix contains unknown variables"; return -1; } if (!have_username) { *error_r = "Shared namespace prefix doesn't contain %u or %n"; return -1; } if (p[-1] != mail_namespace_get_sep(ns) && (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) != 0) { *error_r = "Shared namespace prefix doesn't end with hierarchy separator"; return -1; } /* truncate prefix after the above checks are done, so they can log the full prefix in error conditions */ *wildcardp = '\0'; ns->prefix_len = strlen(ns->prefix); return 0; } static void shared_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { set->layout = "shared"; } static void get_nonexistent_user_location(struct shared_storage *storage, const char *username, string_t *location) { /* user wasn't found. we'll still need to create the storage to avoid exposing which users exist and which don't. */ str_append(location, storage->storage_class_name); str_append_c(location, ':'); /* use a reachable but nonexistent path as the mail root directory */ str_append(location, storage->storage.user->set->base_dir); str_append(location, "/user-not-found/"); str_append(location, username); } static bool shared_namespace_exists(struct mail_namespace *ns) { const char *path; struct stat st; path = mailbox_list_get_root_forced(ns->list, MAILBOX_LIST_PATH_TYPE_DIR); if (path == NULL) { /* we can't know if this exists */ return TRUE; } return stat(path, &st) == 0; } int shared_storage_get_namespace(struct mail_namespace **_ns, const char **_name) { struct mail_storage *_storage = (*_ns)->storage; struct mailbox_list *list = (*_ns)->list; struct shared_storage *storage = (struct shared_storage *)_storage; struct mail_user *user = _storage->user; static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 'h', NULL, "home" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; struct mail_namespace *new_ns, *ns = *_ns; struct mail_namespace_settings *ns_set, *unexpanded_ns_set; struct mail_user *owner; const char *domain = NULL, *username = NULL, *userdomain = NULL; const char *name, *p, *next, **dest, *error; string_t *prefix, *location; char ns_sep = mail_namespace_get_sep(ns); int ret; p = storage->ns_prefix_pattern; for (name = *_name; *p != '\0';) { if (*p != '%') { if (*p != *name) break; p++; name++; continue; } switch (*++p) { case 'd': dest = &domain; break; case 'n': dest = &username; break; case 'u': dest = &userdomain; break; default: /* we checked this already above */ i_unreached(); } p++; next = strchr(name, *p != '\0' ? *p : ns_sep); if (next == NULL) { *dest = name; name = ""; break; } *dest = t_strdup_until(name, next); name = next; } if (*p != '\0') { if (*name == '\0' || (name[1] == '\0' && *name == ns_sep)) { /* trying to open / mailbox */ name = "INBOX"; } else { mailbox_list_set_critical(list, "Invalid namespace prefix %s vs %s", storage->ns_prefix_pattern, *_name); return -1; } } /* successfully matched the name. */ if (userdomain != NULL) { /* user@domain given */ domain = strchr(userdomain, '@'); if (domain == NULL) username = userdomain; else { username = t_strdup_until(userdomain, domain); domain++; } } else if (username == NULL) { /* trying to open namespace "shared/domain" namespace prefix. */ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(*_name)); return -1; } else { if (domain == NULL) { /* no domain given, use ours (if we have one) */ domain = strchr(user->username, '@'); if (domain != NULL) domain++; } userdomain = domain == NULL ? username : t_strconcat(username, "@", domain, NULL); } if (*userdomain == '\0') { mailbox_list_set_error(list, MAIL_ERROR_PARAMS, "Empty username doesn't exist"); return -1; } /* expand the namespace prefix and see if it already exists. this should normally happen only when the mailbox is being opened */ tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = userdomain; tab[1].value = username; tab[2].value = domain; prefix = t_str_new(128); str_append(prefix, ns->prefix); var_expand(prefix, storage->ns_prefix_pattern, tab); *_ns = mail_namespace_find_prefix(user->namespaces, str_c(prefix)); if (*_ns != NULL) { *_name = mailbox_list_get_storage_name(ns->list, t_strconcat(ns->prefix, name, NULL)); return 0; } owner = mail_user_alloc(userdomain, user->set_info, user->unexpanded_set); owner->_service_user = user->_service_user; mail_storage_service_user_ref(owner->_service_user); owner->creator = user; owner->autocreated = TRUE; owner->session_id = p_strdup(owner->pool, user->session_id); if (mail_user_init(owner, &error) < 0) { if (!owner->nonexistent) { mailbox_list_set_critical(list, "Couldn't create namespace '%s' for user %s: %s", ns->prefix, userdomain, error); mail_user_unref(&owner); return -1; } ret = 0; } else if (!var_has_key(storage->location, 'h', "home")) { ret = 1; } else { /* we'll need to look up the user's home directory */ if ((ret = mail_user_get_home(owner, &tab[3].value)) < 0) { mailbox_list_set_critical(list, "Namespace '%s': " "Could not lookup home for user %s", ns->prefix, userdomain); mail_user_unref(&owner); return -1; } } /* create the new namespace */ new_ns = i_new(struct mail_namespace, 1); new_ns->refcount = 1; new_ns->type = MAIL_NAMESPACE_TYPE_SHARED; new_ns->user = user; new_ns->prefix = i_strdup(str_c(prefix)); new_ns->owner = owner; new_ns->flags = (NAMESPACE_FLAG_SUBSCRIPTIONS & ns->flags) | NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN | NAMESPACE_FLAG_AUTOCREATED | NAMESPACE_FLAG_INBOX_ANY; new_ns->user_set = user->set; new_ns->mail_set = _storage->set; i_array_init(&new_ns->all_storages, 2); location = t_str_new(256); if (ret > 0) var_expand(location, storage->location, tab); else { get_nonexistent_user_location(storage, userdomain, location); new_ns->flags |= NAMESPACE_FLAG_UNUSABLE; if (ns->user->mail_debug) { i_debug("shared: Tried to access mails of " "nonexistent user %s", userdomain); } } ns_set = p_new(user->pool, struct mail_namespace_settings, 1); ns_set->type = "shared"; ns_set->separator = p_strdup_printf(user->pool, "%c", ns_sep); ns_set->prefix = new_ns->prefix; ns_set->location = p_strdup(user->pool, str_c(location)); ns_set->hidden = TRUE; ns_set->list = "yes"; new_ns->set = ns_set; unexpanded_ns_set = p_new(user->pool, struct mail_namespace_settings, 1); *unexpanded_ns_set = *ns_set; unexpanded_ns_set->location = p_strdup(user->pool, storage->unexpanded_location); new_ns->unexpanded_set = unexpanded_ns_set; /* We need to create a prefix="" namespace for the owner */ if (mail_namespaces_init_location(owner, str_c(location), &error) < 0) { /* owner gets freed by namespace deinit */ mail_namespace_destroy(new_ns); return -1; } if (mail_storage_create(new_ns, NULL, _storage->flags | MAIL_STORAGE_FLAG_NO_AUTOVERIFY, &error) < 0) { mailbox_list_set_critical(list, "Namespace '%s': %s", new_ns->prefix, error); /* owner gets freed by namespace deinit */ mail_namespace_destroy(new_ns); return -1; } if ((new_ns->flags & NAMESPACE_FLAG_UNUSABLE) == 0 && !shared_namespace_exists(new_ns)) { /* this user doesn't have a usable storage */ new_ns->flags |= NAMESPACE_FLAG_UNUSABLE; } /* mark the shared namespace root as usable, since it now has child namespaces */ ns->flags |= NAMESPACE_FLAG_USABLE; *_name = mailbox_list_get_storage_name(new_ns->list, t_strconcat(new_ns->prefix, name, NULL)); *_ns = new_ns; if (_storage->class_flags == 0) { /* flags are unset if we were using "auto" storage */ _storage->class_flags = mail_namespace_get_default_storage(new_ns)->class_flags; } mail_user_add_namespace(user, &new_ns); return 0; } struct mail_storage shared_storage = { .name = MAIL_SHARED_STORAGE_NAME, .class_flags = 0, /* unknown at this point */ .v = { NULL, shared_storage_alloc, shared_storage_create, index_storage_destroy, NULL, shared_storage_get_list_settings, NULL, fail_mailbox_alloc, NULL, NULL, } }; dovecot-2.2.33.2/src/lib-storage/index/shared/Makefile.am0000644000175000017500000000064513123174404017741 00000000000000noinst_LTLIBRARIES = libstorage_shared.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_shared_la_SOURCES = \ shared-list.c \ shared-storage.c headers = \ shared-storage.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-storage.c0000644000175000017500000010516513165463624017372 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ioloop.h" #include "str.h" #include "mkdir-parents.h" #include "dict.h" #include "mail-index-alloc-cache.h" #include "mail-index-private.h" #include "mail-index-modseq.h" #include "mailbox-log.h" #include "mailbox-list-private.h" #include "mail-search-build.h" #include "index-storage.h" #include "index-mail.h" #include "index-attachment.h" #include "index-thread-private.h" #include "index-mailbox-size.h" #include #include #include #define LOCK_NOTIFY_INTERVAL 30 struct index_storage_module index_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); static void set_cache_decisions(struct mail_cache *cache, const char *set, const char *fields, enum mail_cache_decision_type dec) { struct mail_cache_field field; const char *const *arr; unsigned int idx; if (fields == NULL || *fields == '\0') return; for (arr = t_strsplit_spaces(fields, " ,"); *arr != NULL; arr++) { const char *name = *arr; idx = mail_cache_register_lookup(cache, name); if (idx != UINT_MAX) { field = *mail_cache_register_get_field(cache, idx); } else if (strncasecmp(name, "hdr.", 4) == 0) { i_zero(&field); field.name = name; field.type = MAIL_CACHE_FIELD_HEADER; } else { i_error("%s: Unknown cache field name '%s', ignoring", set, *arr); continue; } field.decision = dec; mail_cache_register_fields(cache, &field, 1); } } static void index_cache_register_defaults(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); const struct mail_storage_settings *set = box->storage->set; struct mail_cache *cache = box->cache; ibox->cache_fields = i_malloc(sizeof(global_cache_fields)); memcpy(ibox->cache_fields, global_cache_fields, sizeof(global_cache_fields)); mail_cache_register_fields(cache, ibox->cache_fields, MAIL_INDEX_CACHE_FIELD_COUNT); if (strcmp(set->mail_never_cache_fields, "*") == 0) { /* all caching disabled for now */ box->mail_cache_disabled = TRUE; return; } set_cache_decisions(cache, "mail_cache_fields", set->mail_cache_fields, MAIL_CACHE_DECISION_TEMP); set_cache_decisions(cache, "mail_always_cache_fields", set->mail_always_cache_fields, MAIL_CACHE_DECISION_YES | MAIL_CACHE_DECISION_FORCED); set_cache_decisions(cache, "mail_never_cache_fields", set->mail_never_cache_fields, MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED); } void index_storage_lock_notify(struct mailbox *box, enum mailbox_lock_notify_type notify_type, unsigned int secs_left) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); struct mail_storage *storage = box->storage; const char *str; time_t now; /* if notify type changes, print the message immediately */ now = time(NULL); if (ibox->last_notify_type == MAILBOX_LOCK_NOTIFY_NONE || ibox->last_notify_type == notify_type) { if (ibox->last_notify_type == MAILBOX_LOCK_NOTIFY_NONE && notify_type == MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE) { /* first override notification, show it */ } else { if (now < ibox->next_lock_notify || secs_left < 15) return; } } ibox->next_lock_notify = now + LOCK_NOTIFY_INTERVAL; ibox->last_notify_type = notify_type; switch (notify_type) { case MAILBOX_LOCK_NOTIFY_NONE: break; case MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT: if (storage->callbacks.notify_no == NULL) break; str = t_strdup_printf("Mailbox is locked, will abort in " "%u seconds", secs_left); storage->callbacks. notify_no(box, str, storage->callback_context); break; case MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE: if (storage->callbacks.notify_ok == NULL) break; str = t_strdup_printf("Stale mailbox lock file detected, " "will override in %u seconds", secs_left); storage->callbacks. notify_ok(box, str, storage->callback_context); break; } } void index_storage_lock_notify_reset(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL; ibox->last_notify_type = MAILBOX_LOCK_NOTIFY_NONE; } static int index_mailbox_alloc_index(struct mailbox *box, struct mail_index **index_r) { const char *index_dir, *mailbox_path; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &mailbox_path) < 0) return -1; if ((box->flags & MAILBOX_FLAG_NO_INDEX_FILES) != 0 || mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &index_dir) <= 0) index_dir = NULL; *index_r = mail_index_alloc_cache_get(mailbox_path, index_dir, box->index_prefix); return 0; } int index_storage_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { return index_storage_mailbox_exists_full(box, NULL, existence_r); } int index_storage_mailbox_exists_full(struct mailbox *box, const char *subdir, enum mailbox_existence *existence_r) { struct stat st; enum mail_error error; const char *path, *path2, *index_path; int ret; /* see if it's selectable */ ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) { mailbox_list_get_last_error(box->list, &error); if (error != MAIL_ERROR_NOTFOUND) return -1; *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } if (ret == 0) { /* no mailboxes in this storage? */ *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } ret = (subdir != NULL || !box->list->set.iter_from_index_dir) ? 0 : mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &index_path); if (ret > 0 && strcmp(path, index_path) != 0) { /* index directory is different - prefer looking it up first since it might be on a faster storage. since the directory itself exists also for \NoSelect mailboxes, we'll need to check the dovecot.index.log existence. */ index_path = t_strconcat(index_path, "/", box->index_prefix, ".log", NULL); if (stat(index_path, &st) == 0) { *existence_r = MAILBOX_EXISTENCE_SELECT; return 0; } } if (subdir != NULL) path = t_strconcat(path, "/", subdir, NULL); if (stat(path, &st) == 0) { *existence_r = MAILBOX_EXISTENCE_SELECT; return 0; } if (!ENOTFOUND(errno) && errno != EACCES) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } /* see if it's non-selectable */ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_DIR, &path2) <= 0 || (strcmp(path, path2) != 0 && stat(path2, &st) == 0)) { *existence_r = MAILBOX_EXISTENCE_NOSELECT; return 0; } *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } int index_storage_mailbox_alloc_index(struct mailbox *box) { if (box->index != NULL) return 0; if (mailbox_create_missing_dir(box, MAILBOX_LIST_PATH_TYPE_INDEX) < 0) return -1; if (index_mailbox_alloc_index(box, &box->index) < 0) return -1; mail_index_set_fsync_mode(box->index, box->storage->set->parsed_fsync_mode, 0); mail_index_set_lock_method(box->index, box->storage->set->parsed_lock_method, mail_storage_get_lock_timeout(box->storage, UINT_MAX)); return 0; } int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); enum mail_index_open_flags index_flags; int ret; i_assert(!box->opened); index_flags = ibox->index_flags; if (move_to_memory) index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE; if (index_storage_mailbox_alloc_index(box) < 0) return -1; /* make sure mail_index_set_permissions() has been called */ (void)mailbox_get_permissions(box); ret = mail_index_open(box->index, index_flags); if (ret <= 0 || move_to_memory) { if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) { i_assert(ret <= 0); mailbox_set_index_error(box); return -1; } if (mail_index_move_to_memory(box->index) < 0) { /* try opening once more. it should be created directly into memory now. */ if (mail_index_open_or_create(box->index, index_flags) < 0) i_panic("in-memory index creation failed"); } } if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) { if (mail_index_is_in_memory(box->index)) { mail_storage_set_critical(box->storage, "Couldn't create index file"); mail_index_close(box->index); return -1; } } if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) { if (mail_index_is_deleted(box->index)) { mailbox_set_deleted(box); mail_index_close(box->index); return -1; } } box->cache = mail_index_get_cache(box->index); index_cache_register_defaults(box); box->view = mail_index_view_open(box->index); ibox->keyword_names = mail_index_get_keywords(box->index); box->vsize_hdr_ext_id = mail_index_ext_register(box->index, "hdr-vsize", sizeof(struct mailbox_index_vsize), 0, sizeof(uint64_t)); box->pop3_uidl_hdr_ext_id = mail_index_ext_register(box->index, "hdr-pop3-uidl", sizeof(struct mailbox_index_pop3_uidl), 0, 0); box->box_name_hdr_ext_id = mail_index_ext_register(box->index, "box-name", 0, 0, 0); box->box_last_rename_stamp_ext_id = mail_index_ext_register(box->index, "last-rename-stamp", sizeof(uint32_t), 0, sizeof(uint32_t)); box->mail_vsize_ext_id = mail_index_ext_register(box->index, "vsize", 0, sizeof(uint32_t), sizeof(uint32_t)); box->opened = TRUE; if ((box->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0) mail_index_modseq_enable(box->index); index_thread_mailbox_opened(box); hook_mailbox_opened(box); return 0; } void index_storage_mailbox_alloc(struct mailbox *box, const char *vname, enum mailbox_flags flags, const char *index_prefix) { static unsigned int mailbox_generation_sequence = 0; struct index_mailbox_context *ibox; i_assert(vname != NULL); box->generation_sequence = ++mailbox_generation_sequence; box->vname = p_strdup(box->pool, vname); box->name = p_strdup(box->pool, mailbox_list_get_storage_name(box->list, vname)); box->flags = flags; box->index_prefix = p_strdup(box->pool, index_prefix); p_array_init(&box->search_results, box->pool, 16); array_create(&box->module_contexts, box->pool, sizeof(void *), 5); ibox = p_new(box->pool, struct index_mailbox_context, 1); ibox->list_index_sync_ext_id = (uint32_t)-1; ibox->index_flags = MAIL_INDEX_OPEN_FLAG_CREATE | mail_storage_settings_to_index_flags(box->storage->set); if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) ibox->index_flags |= MAIL_INDEX_OPEN_FLAG_SAVEONLY; if (box->storage->user->mail_debug) ibox->index_flags |= MAIL_INDEX_OPEN_FLAG_DEBUG; ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL; MODULE_CONTEXT_SET(box, index_storage_module, ibox); box->inbox_user = strcmp(box->name, "INBOX") == 0 && (box->list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0; box->inbox_any = strcmp(box->name, "INBOX") == 0 && (box->list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0; } int index_storage_mailbox_enable(struct mailbox *box, enum mailbox_feature feature) { if ((feature & MAILBOX_FEATURE_CONDSTORE) != 0) { box->enabled_features |= MAILBOX_FEATURE_CONDSTORE; if (box->opened) mail_index_modseq_enable(box->index); } return 0; } void index_storage_mailbox_close(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); mailbox_watch_remove_all(box); if (box->input != NULL) i_stream_unref(&box->input); if (box->view_pvt != NULL) mail_index_view_close(&box->view_pvt); if (box->index_pvt != NULL) mail_index_close(box->index_pvt); if (box->view != NULL) { mail_index_view_close(&box->view); mail_index_close(box->index); } box->cache = NULL; ibox->keyword_names = NULL; i_free_and_null(ibox->cache_fields); ibox->sync_last_check = 0; } static void index_storage_mailbox_unref_indexes(struct mailbox *box) { if (box->index_pvt != NULL) mail_index_alloc_cache_unref(&box->index_pvt); if (box->index != NULL) mail_index_alloc_cache_unref(&box->index); } void index_storage_mailbox_free(struct mailbox *box) { index_storage_mailbox_unref_indexes(box); } static void index_storage_mailbox_update_cache(struct mailbox *box, const struct mailbox_update *update) { const struct mailbox_cache_field *updates = update->cache_updates; ARRAY(struct mail_cache_field) new_fields; const struct mail_cache_field *old_fields; struct mail_cache_field field; unsigned int i, j, old_count; old_fields = mail_cache_register_get_list(box->cache, pool_datastack_create(), &old_count); /* There shouldn't be many fields, so don't worry about O(n^2). */ t_array_init(&new_fields, 32); for (i = 0; updates[i].name != NULL; i++) { /* see if it's an existing field */ for (j = 0; j < old_count; j++) { if (strcmp(updates[i].name, old_fields[j].name) == 0) break; } if (j != old_count) { field = old_fields[j]; } else if (strncmp(updates[i].name, "hdr.", 4) == 0) { /* new header */ i_zero(&field); field.name = updates[i].name; field.type = MAIL_CACHE_FIELD_HEADER; } else { /* new unknown field. we can't do anything about this since we don't know its type */ continue; } field.decision = updates[i].decision; if (updates[i].last_used != (time_t)-1) field.last_used = updates[i].last_used; array_append(&new_fields, &field, 1); } if (array_count(&new_fields) > 0) { mail_cache_register_fields(box->cache, array_idx_modifiable(&new_fields, 0), array_count(&new_fields)); } } static int index_storage_mailbox_update_pvt(struct mailbox *box, const struct mailbox_update *update) { struct mail_index_transaction *trans; struct mail_index_view *view; int ret; if ((ret = mailbox_open_index_pvt(box)) <= 0) return ret; mail_index_refresh(box->index_pvt); view = mail_index_view_open(box->index_pvt); trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); if (update->min_highest_modseq != 0 && mail_index_modseq_get_highest(view) < update->min_highest_pvt_modseq) { mail_index_modseq_enable(box->index_pvt); mail_index_update_highest_modseq(trans, update->min_highest_pvt_modseq); } if ((ret = mail_index_transaction_commit(&trans)) < 0) mailbox_set_index_error(box); mail_index_view_close(&view); return ret; } int index_storage_mailbox_update_common(struct mailbox *box, const struct mailbox_update *update) { int ret = 0; if (update->cache_updates != NULL) index_storage_mailbox_update_cache(box, update); if (update->min_highest_pvt_modseq != 0) { if (index_storage_mailbox_update_pvt(box, update) < 0) ret = -1; } return ret; } int index_storage_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { const struct mail_index_header *hdr; struct mail_index_view *view; struct mail_index_transaction *trans; int ret; if (mailbox_open(box) < 0) return -1; /* make sure we get the latest index info */ mail_index_refresh(box->index); view = mail_index_view_open(box->index); hdr = mail_index_get_header(view); trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); if (update->uid_validity != 0 && hdr->uid_validity != update->uid_validity) { uint32_t uid_validity = update->uid_validity; if (hdr->uid_validity != 0) { /* UIDVALIDITY change requires index to be reset */ mail_index_reset(trans); } mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } if (update->min_next_uid != 0 && hdr->next_uid < update->min_next_uid) { uint32_t next_uid = update->min_next_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, next_uid), &next_uid, sizeof(next_uid), FALSE); } if (update->min_first_recent_uid != 0 && hdr->first_recent_uid < update->min_first_recent_uid) { uint32_t first_recent_uid = update->min_first_recent_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } if (update->min_highest_modseq != 0 && mail_index_modseq_get_highest(view) < update->min_highest_modseq) { mail_index_modseq_enable(box->index); mail_index_update_highest_modseq(trans, update->min_highest_modseq); } if ((ret = mail_index_transaction_commit(&trans)) < 0) mailbox_set_index_error(box); mail_index_view_close(&view); return ret < 0 ? -1 : index_storage_mailbox_update_common(box, update); } int index_storage_mailbox_create(struct mailbox *box, bool directory) { const char *path, *p; enum mailbox_list_path_type type; enum mailbox_existence existence; bool create_parent_dir; int ret; if ((box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) != 0) { /* Layout doesn't support creating \NoSelect mailboxes. Switch to creating a selectable mailbox. */ directory = FALSE; } type = directory ? MAILBOX_LIST_PATH_TYPE_DIR : MAILBOX_LIST_PATH_TYPE_MAILBOX; if ((ret = mailbox_get_path_to(box, type, &path)) < 0) return -1; if (ret == 0) { /* layout=none */ mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox creation not supported"); return -1; } create_parent_dir = !directory && (box->list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0; if (create_parent_dir) { /* we only need to make sure that the parent directory exists */ p = strrchr(path, '/'); if (p == NULL) return 1; path = t_strdup_until(path, p); } if ((ret = mailbox_mkdir(box, path, type)) < 0) return -1; if (box->list->set.iter_from_index_dir) { /* need to also create the directory to index path or iteration won't find it. */ int ret2; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0) i_unreached(); if ((ret2 = mailbox_mkdir(box, path, type)) < 0) return -1; if (ret == 0 && ret2 > 0) { /* finish partial creation: existed in mail directory, but not in index directory. */ ret = 1; } } mailbox_refresh_permissions(box); if (ret == 0) { /* directory already exists */ if (create_parent_dir) return 1; if (!directory && *box->list->set.mailbox_dir_name == '\0') { /* For example: layout=fs, path=~/Maildir/foo might itself exist, but does it have the cur|new|tmp subdirs? */ if (mailbox_exists(box, FALSE, &existence) < 0) return -1; if (existence != MAILBOX_EXISTENCE_SELECT) return 1; } mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } if (directory) { /* we only wanted to create the directory and it's done now */ return 0; } /* the caller should still create the mailbox */ return 1; } int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted) { guid_128_t dir_sha128; enum mail_error error; if (mailbox_list_delete_dir(box->list, box->name) == 0) return 0; mailbox_list_get_last_error(box->list, &error); if (error != MAIL_ERROR_NOTFOUND || !mailbox_deleted) { mail_storage_copy_list_error(box->storage, box->list); return -1; } /* failed directory deletion, but mailbox deletion succeeded. this was probably maildir++, which internally deleted the directory as well. add changelog record about that too. */ mailbox_name_get_sha128(box->vname, dir_sha128); mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_DIR, dir_sha128); return 0; } static int mailbox_delete_all_attributes(struct mailbox_transaction_context *t, enum mail_attribute_type type) { struct mailbox_attribute_iter *iter; const char *key; int ret = 0; bool inbox = t->box->inbox_any; iter = mailbox_attribute_iter_init(t->box, type, ""); while ((key = mailbox_attribute_iter_next(iter)) != NULL) { if (inbox && strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0) continue; if (mailbox_attribute_unset(t, type, key) < 0) { if (mailbox_get_last_mail_error(t->box) != MAIL_ERROR_NOTPOSSIBLE) { ret = -1; break; } } } if (mailbox_attribute_iter_deinit(&iter) < 0) ret = -1; return ret; } static int mailbox_expunge_all_data(struct mailbox *box) { struct mail_search_context *ctx; struct mailbox_transaction_context *t; struct mail *mail; struct mail_search_args *search_args; (void)mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ); t = mailbox_transaction_begin(box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); ctx = mailbox_search_init(t, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) mail_expunge(mail); if (mailbox_search_deinit(&ctx) < 0) { mailbox_transaction_rollback(&t); return -1; } if (mailbox_delete_all_attributes(t, MAIL_ATTRIBUTE_TYPE_PRIVATE) < 0 || mailbox_delete_all_attributes(t, MAIL_ATTRIBUTE_TYPE_SHARED) < 0) { mailbox_transaction_rollback(&t); return -1; } if (mailbox_transaction_commit(&t) < 0) return -1; /* sync to actually perform the expunges */ return mailbox_sync(box, 0); } int index_storage_mailbox_delete_pre(struct mailbox *box) { struct mailbox_status status; if (!box->opened) { /* \noselect mailbox, try deleting only the directory */ if (index_storage_mailbox_delete_dir(box, FALSE) == 0) return 0; if (mailbox_is_autocreated(box)) { /* Return success when trying to delete autocreated mailbox. The client sees it as existing, so we shouldn't be returning an error. */ return 0; } return -1; } if ((box->list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { /* specifically support symlinked shared mailboxes. a deletion will simply remove the symlink, not actually expunge any mails */ if (mailbox_list_delete_symlink(box->list, box->name) == 0) return 0; } /* we can't easily atomically delete all mails and the mailbox. so: 1) expunge all mails 2) mark the mailbox deleted (modifications after this will fail) 3) check if a race condition between 1) and 2) added any mails: yes) abort and undelete mailbox no) finish deleting the mailbox */ if (!box->deleting_must_be_empty) { if (mailbox_expunge_all_data(box) < 0) return -1; } if (mailbox_mark_index_deleted(box, TRUE) < 0) return -1; if (!box->delete_skip_empty_check || box->deleting_must_be_empty) { if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) return -1; mailbox_get_open_status(box, STATUS_MESSAGES, &status); if (status.messages == 0) ; else if (box->deleting_must_be_empty) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox isn't empty"); return -1; } else { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "New mails were added to mailbox during deletion"); return -1; } } return 1; } int index_storage_mailbox_delete_post(struct mailbox *box) { struct mailbox_metadata metadata; int ret_guid; ret_guid = mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata); /* Make sure the indexes are closed before trying to delete the directory that contains them. It can still fail with some NFS implementations if indexes are opened by another session, but that can't really be helped. */ mailbox_close(box); index_storage_mailbox_unref_indexes(box); mail_index_alloc_cache_destroy_unrefed(); if (box->list->v.delete_mailbox(box->list, box->name) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if (ret_guid == 0) { mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_MAILBOX, metadata.guid); } if (index_storage_mailbox_delete_dir(box, TRUE) < 0) { if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) return -1; /* we deleted the mailbox, but couldn't delete the directory because it has children. that's not an error. */ } return 0; } int index_storage_mailbox_delete(struct mailbox *box) { int ret; if ((ret = index_storage_mailbox_delete_pre(box)) <= 0) return ret; /* mails have been now successfully deleted. some mailbox formats may at this point do some other deletion that is required for it. the _post() deletion will close the index and delete the directory. */ return index_storage_mailbox_delete_post(box); } int index_storage_mailbox_rename(struct mailbox *src, struct mailbox *dest) { guid_128_t guid; if (src->list->v.rename_mailbox(src->list, src->name, dest->list, dest->name) < 0) { mail_storage_copy_list_error(src->storage, src->list); return -1; } if (mailbox_open(dest) == 0) { struct mail_index_transaction *t = mail_index_transaction_begin(dest->view, 0); uint32_t stamp = ioloop_time; mail_index_update_header_ext(t, dest->box_last_rename_stamp_ext_id, 0, &stamp, sizeof(stamp)); /* can't do much if this fails anyways */ (void)mail_index_transaction_commit(&t); } /* we'll track mailbox names, instead of GUIDs. We may be renaming a non-selectable mailbox (directory), which doesn't even have a GUID */ mailbox_name_get_sha128(dest->vname, guid); mailbox_list_add_change(src->list, MAILBOX_LOG_RECORD_RENAME, guid); return 0; } int index_mailbox_update_last_temp_file_scan(struct mailbox *box) { uint32_t last_temp_file_scan = ioloop_time; struct mail_index_transaction *trans = mail_index_transaction_begin(box->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header(trans, offsetof(struct mail_index_header, last_temp_file_scan), &last_temp_file_scan, sizeof(last_temp_file_scan), TRUE); if (mail_index_transaction_commit(&trans) < 0) { mailbox_set_index_error(box); return -1; } return 0; } bool index_storage_is_readonly(struct mailbox *box) { return (box->flags & MAILBOX_FLAG_READONLY) != 0; } bool index_storage_is_inconsistent(struct mailbox *box) { return box->view != NULL && mail_index_view_is_inconsistent(box->view); } void index_save_context_free(struct mail_save_context *ctx) { index_mail_save_finish(ctx); i_free_and_null(ctx->data.from_envelope); i_free_and_null(ctx->data.guid); i_free_and_null(ctx->data.pop3_uidl); index_attachment_save_free(ctx); i_zero(&ctx->data); ctx->unfinished = FALSE; } static void mail_copy_cache_field(struct mail_save_context *ctx, struct mail *src_mail, uint32_t dest_seq, const char *name, buffer_t *buf) { struct mailbox_transaction_context *dest_trans = ctx->transaction; const struct mail_cache_field *dest_field; unsigned int src_field_idx, dest_field_idx; uint32_t t; src_field_idx = mail_cache_register_lookup(src_mail->box->cache, name); i_assert(src_field_idx != UINT_MAX); dest_field_idx = mail_cache_register_lookup(dest_trans->box->cache, name); if (dest_field_idx == UINT_MAX) { /* unknown field */ return; } dest_field = mail_cache_register_get_field(dest_trans->box->cache, dest_field_idx); if ((dest_field->decision & ~MAIL_CACHE_DECISION_FORCED) == MAIL_CACHE_DECISION_NO) { /* field not wanted in destination mailbox */ return; } buffer_set_used_size(buf, 0); if (strcmp(name, "date.save") == 0) { /* save date must update when mail is copied */ t = ioloop_time; buffer_append(buf, &t, sizeof(t)); } else { if (mail_cache_lookup_field(src_mail->transaction->cache_view, buf, src_mail->seq, src_field_idx) <= 0) buffer_set_used_size(buf, 0); else if (strcmp(name, "size.physical") == 0 || strcmp(name, "size.virtual") == 0) { /* FIXME: until mail_cache_lookup() can read unwritten cached data from buffer, we'll do this optimization to make quota plugin's work faster */ struct index_mail *imail = (struct index_mail *)ctx->dest_mail; uoff_t size; i_assert(buf->used == sizeof(size)); memcpy(&size, buf->data, sizeof(size)); if (strcmp(name, "size.physical") == 0) imail->data.physical_size = size; else imail->data.virtual_size = size; } } if (buf->used > 0) { mail_cache_add(dest_trans->cache_trans, dest_seq, dest_field_idx, buf->data, buf->used); } } static void index_copy_vsize_extension(struct mail_save_context *ctx, struct mail *src_mail, uint32_t dest_seq) { struct index_mail *src_imail = (struct index_mail *)src_mail; unsigned int idx; bool expunged ATTR_UNUSED; (void)index_mail_get_vsize_extension(src_mail); if (src_imail->data.virtual_size == (uoff_t)-1) return; if (mail_index_map_get_ext_idx(ctx->transaction->view->map, ctx->transaction->box->mail_vsize_ext_id, &idx) && src_imail->data.virtual_size < (uint32_t)-1) { uint32_t vsize = src_imail->data.virtual_size+1; mail_index_update_ext(ctx->transaction->itrans, dest_seq, ctx->transaction->box->mail_vsize_ext_id, &vsize, NULL); } } void index_copy_cache_fields(struct mail_save_context *ctx, struct mail *src_mail, uint32_t dest_seq) { T_BEGIN { struct mailbox_metadata src_metadata, dest_metadata; const struct mailbox_cache_field *field; buffer_t *buf; if (mailbox_get_metadata(src_mail->box, MAILBOX_METADATA_CACHE_FIELDS, &src_metadata) < 0) i_unreached(); /* the only reason we're doing the destination lookup is to make sure that the cache file is opened and the cache decisinos are up to date */ if (mailbox_get_metadata(ctx->transaction->box, MAILBOX_METADATA_CACHE_FIELDS, &dest_metadata) < 0) i_unreached(); buf = buffer_create_dynamic(pool_datastack_create(), 1024); array_foreach(src_metadata.cache_fields, field) { mail_copy_cache_field(ctx, src_mail, dest_seq, field->name, buf); } index_copy_vsize_extension(ctx, src_mail, dest_seq); } T_END; } int index_storage_set_subscribed(struct mailbox *box, bool set) { struct mail_namespace *ns; struct mailbox_list *list = box->list; const char *subs_name; guid_128_t guid; if ((list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) subs_name = box->name; else { /* subscriptions=no namespace, find another one where we can add the subscription to */ ns = mail_namespace_find_subscribable(list->ns->user->namespaces, box->vname); if (ns == NULL) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "This namespace has no subscriptions"); return -1; } /* use as the subscription name */ subs_name = t_strconcat(list->ns->prefix, box->name, NULL); /* drop the common prefix (typically there isn't one) */ i_assert(strncmp(ns->prefix, subs_name, strlen(ns->prefix)) == 0); subs_name += strlen(ns->prefix); list = ns->list; } if (mailbox_list_set_subscribed(list, subs_name, set) < 0) { mail_storage_copy_list_error(box->storage, list); return -1; } /* subscriptions are about names, not about mailboxes. it's possible to have a subscription to nonexistent mailbox. renames also don't change subscriptions. so instead of using actual GUIDs, we'll use hash of the name. */ mailbox_name_get_sha128(box->vname, guid); mailbox_list_add_change(list, set ? MAILBOX_LOG_RECORD_SUBSCRIBE : MAILBOX_LOG_RECORD_UNSUBSCRIBE, guid); return 0; } void index_storage_destroy(struct mail_storage *storage) { if (storage->_shared_attr_dict != NULL) { (void)dict_wait(storage->_shared_attr_dict); dict_deinit(&storage->_shared_attr_dict); } } static void index_storage_expunging_init(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); if (ibox->vsize_update != NULL) return; ibox->vsize_update = index_mailbox_vsize_update_init(box); if (!index_mailbox_vsize_want_updates(ibox->vsize_update) || !index_mailbox_vsize_update_wait_lock(ibox->vsize_update)) index_mailbox_vsize_update_deinit(&ibox->vsize_update); } void index_storage_expunging_deinit(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); if (ibox->vsize_update != NULL) index_mailbox_vsize_update_deinit(&ibox->vsize_update); } static bool index_storage_expunging_want_updates(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); bool ret; i_assert(ibox->vsize_update == NULL); ibox->vsize_update = index_mailbox_vsize_update_init(box); ret = index_mailbox_vsize_want_updates(ibox->vsize_update); index_mailbox_vsize_update_deinit(&ibox->vsize_update); return ret; } int index_storage_expunged_sync_begin(struct mailbox *box, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, enum mail_index_sync_flags flags) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); int ret; /* try to avoid locking vsize updates by checking if we see any expunges */ if (mail_index_sync_have_any_expunges(box->index)) index_storage_expunging_init(box); ret = mail_index_sync_begin(box->index, ctx_r, view_r, trans_r, flags); if (ret <= 0) { if (ret < 0) mailbox_set_index_error(box); index_storage_expunging_deinit(box); return ret; } if (ibox->vsize_update == NULL && mail_index_sync_has_expunges(*ctx_r) && index_storage_expunging_want_updates(box)) { /* race condition - need to abort the sync and retry with the vsize locked */ mail_index_sync_rollback(ctx_r); index_storage_expunging_deinit(box); return index_storage_expunged_sync_begin(box, ctx_r, view_r, trans_r, flags); } return 1; } void index_storage_save_abort_last(struct mail_save_context *ctx, uint32_t seq) { struct index_mail *imail = (struct index_mail *)ctx->dest_mail; /* Close the mail before it's expunged. This allows it to be reset cleanly. */ imail->data.no_caching = TRUE; imail->mail.v.close(&imail->mail.mail); mail_index_expunge(ctx->transaction->itrans, seq); /* currently we can't just drop pending cache updates for this one specific record, so we'll reset the whole cache transaction. */ mail_cache_transaction_reset(ctx->transaction->cache_trans); } int index_mailbox_fix_inconsistent_existence(struct mailbox *box, const char *path) { const char *index_path; struct stat st; /* Could be a race condition or could be because ITERINDEX is used and the index directory exists, but the storage directory doesn't. Handle the existence inconsistency by creating this directory if the index directory exists (don't bother checking if ITERINDEX is set or not - it doesn't matter since either both dirs should exist or not). */ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &index_path) < 0) return -1; if (strcmp(index_path, path) == 0) { /* there's no separate index path - mailbox was just deleted */ } else if (stat(index_path, &st) == 0) { /* inconsistency - create also the mail directory */ return mailbox_mkdir(box, path, MAILBOX_LIST_PATH_TYPE_MAILBOX); } else if (errno == ENOENT) { /* race condition - mailbox was just deleted */ } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", index_path); return -1; } mailbox_set_deleted(box); return -1; } dovecot-2.2.33.2/src/lib-storage/index/istream-mail.h0000644000175000017500000000024513123174404017170 00000000000000#ifndef ISTREAM_MAIL_H #define ISTREAM_MAIL_H struct istream *i_stream_create_mail(struct mail *mail, struct istream *input, bool input_has_body); #endif dovecot-2.2.33.2/src/lib-storage/index/raw/0002755000175000017500000000000013172375611015314 500000000000000dovecot-2.2.33.2/src/lib-storage/index/raw/Makefile.in0000644000175000017500000005455313172375573017322 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/raw ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_raw_la_LIBADD = am_libstorage_raw_la_OBJECTS = raw-mail.lo raw-sync.lo raw-storage.lo libstorage_raw_la_OBJECTS = $(am_libstorage_raw_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_raw_la_SOURCES) DIST_SOURCES = $(libstorage_raw_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_raw.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_raw_la_SOURCES = \ raw-mail.c \ raw-sync.c \ raw-storage.c headers = \ raw-storage.h \ raw-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/raw/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/raw/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_raw.la: $(libstorage_raw_la_OBJECTS) $(libstorage_raw_la_DEPENDENCIES) $(EXTRA_libstorage_raw_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_raw_la_OBJECTS) $(libstorage_raw_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/raw/raw-sync.h0000644000175000017500000000025113123174404017135 00000000000000#ifndef RAW_SYNC_H #define RAW_SYNC_H struct mailbox; struct mailbox_sync_context * raw_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); #endif dovecot-2.2.33.2/src/lib-storage/index/raw/raw-storage.h0000644000175000017500000000163513165463624017647 00000000000000#ifndef RAW_STORAGE_H #define RAW_STORAGE_H #include "index-storage.h" #define RAW_STORAGE_NAME "raw" #define RAW_SUBSCRIPTION_FILE_NAME "subscriptions" struct raw_storage { struct mail_storage storage; }; struct raw_mailbox { struct mailbox box; struct raw_storage *storage; time_t mtime, ctime; uoff_t size; const char *envelope_sender; unsigned int synced:1; unsigned int have_filename:1; }; extern struct mail_vfuncs raw_mail_vfuncs; struct mail_user * raw_storage_create_from_set(const struct setting_parser_info *set_info, const struct mail_user_settings *set); int raw_mailbox_alloc_stream(struct mail_user *user, struct istream *input, time_t received_time, const char *envelope_sender, struct mailbox **box_r); int raw_mailbox_alloc_path(struct mail_user *user, const char *path, time_t received_time, const char *envelope_sender, struct mailbox **box_r); #endif dovecot-2.2.33.2/src/lib-storage/index/raw/raw-storage.c0000644000175000017500000001630313165463624017640 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "index-mail.h" #include "mail-copy.h" #include "mailbox-list-private.h" #include "raw-sync.h" #include "raw-storage.h" extern struct mail_storage raw_storage; extern struct mailbox raw_mailbox; struct mail_user * raw_storage_create_from_set(const struct setting_parser_info *set_info, const struct mail_user_settings *set) { struct mail_user *user; struct mail_namespace *ns; struct mail_namespace_settings *ns_set; struct mail_storage_settings *mail_set; const char *error; user = mail_user_alloc("raw mail user", set_info, set); user->autocreated = TRUE; mail_user_set_home(user, "/"); if (mail_user_init(user, &error) < 0) i_fatal("Raw user initialization failed: %s", error); ns_set = p_new(user->pool, struct mail_namespace_settings, 1); ns_set->name = "raw-storage"; ns_set->location = ":LAYOUT=none"; ns_set->separator = "/"; ns = mail_namespaces_init_empty(user); /* raw storage doesn't have INBOX. We especially don't want LIST to return INBOX. */ ns->flags &= ~NAMESPACE_FLAG_INBOX_USER; ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL; ns->set = ns_set; /* absolute paths are ok with raw storage */ mail_set = p_new(user->pool, struct mail_storage_settings, 1); *mail_set = *ns->mail_set; mail_set->mail_full_filesystem_access = TRUE; ns->mail_set = mail_set; if (mail_storage_create(ns, "raw", 0, &error) < 0) i_fatal("Couldn't create internal raw storage: %s", error); if (mail_namespaces_init_finish(ns, &error) < 0) i_fatal("Couldn't create internal raw namespace: %s", error); return user; } static int ATTR_NULL(2, 3) raw_mailbox_alloc_common(struct mail_user *user, struct istream *input, const char *path, time_t received_time, const char *envelope_sender, struct mailbox **box_r) { struct mail_namespace *ns = user->namespaces; struct mailbox *box; struct raw_mailbox *raw_box; const char *name; name = path != NULL ? path : i_stream_get_name(input); box = *box_r = mailbox_alloc(ns->list, name, MAILBOX_FLAG_NO_INDEX_FILES); if (input != NULL) { if (mailbox_open_stream(box, input) < 0) return -1; } else { if (mailbox_open(box) < 0) return -1; } if (mailbox_sync(box, 0) < 0) return -1; i_assert(strcmp(box->storage->name, RAW_STORAGE_NAME) == 0); raw_box = (struct raw_mailbox *)box; raw_box->envelope_sender = envelope_sender; raw_box->mtime = received_time; return 0; } int raw_mailbox_alloc_stream(struct mail_user *user, struct istream *input, time_t received_time, const char *envelope_sender, struct mailbox **box_r) { return raw_mailbox_alloc_common(user, input, NULL, received_time, envelope_sender, box_r); } int raw_mailbox_alloc_path(struct mail_user *user, const char *path, time_t received_time, const char *envelope_sender, struct mailbox **box_r) { return raw_mailbox_alloc_common(user, NULL, path, received_time, envelope_sender, box_r); } static struct mail_storage *raw_storage_alloc(void) { struct raw_storage *storage; pool_t pool; pool = pool_alloconly_create("raw storage", 512+256); storage = p_new(pool, struct raw_storage, 1); storage->storage = raw_storage; storage->storage.pool = pool; return &storage->storage; } static void raw_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_FS; if (set->subscription_fname == NULL) set->subscription_fname = RAW_SUBSCRIPTION_FILE_NAME; } static struct mailbox * raw_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct raw_mailbox *mbox; pool_t pool; flags |= MAILBOX_FLAG_READONLY | MAILBOX_FLAG_NO_INDEX_FILES; pool = pool_alloconly_create("raw mailbox", 1024*3); mbox = p_new(pool, struct raw_mailbox, 1); mbox->box = raw_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &raw_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, "dovecot.index"); mbox->mtime = mbox->ctime = (time_t)-1; mbox->storage = (struct raw_storage *)storage; mbox->size = (uoff_t)-1; return &mbox->box; } static int raw_mailbox_open(struct mailbox *box) { struct raw_mailbox *mbox = (struct raw_mailbox *)box; const char *path; int fd; if (box->input != NULL) { mbox->mtime = mbox->ctime = ioloop_time; return index_storage_mailbox_open(box, FALSE); } path = box->_path = box->name; mbox->have_filename = TRUE; fd = open(path, O_RDONLY); if (fd == -1) { if (ENOTFOUND(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); } else if (!mail_storage_set_error_from_errno(box->storage)) { mail_storage_set_critical(box->storage, "open(%s) failed: %m", path); } return -1; } box->input = i_stream_create_fd_autoclose(&fd, MAIL_READ_FULL_BLOCK_SIZE); i_stream_set_name(box->input, path); i_stream_set_init_buffer_size(box->input, MAIL_READ_FULL_BLOCK_SIZE); return index_storage_mailbox_open(box, FALSE); } static int raw_mailbox_create(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED, bool directory ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Raw mailbox creation isn't supported"); return -1; } static int raw_mailbox_update(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Raw mailbox update isn't supported"); return -1; } static void raw_notify_changes(struct mailbox *box ATTR_UNUSED) { } struct mail_storage raw_storage = { .name = RAW_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE | MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA, .v = { NULL, raw_storage_alloc, NULL, index_storage_destroy, NULL, raw_storage_get_list_settings, NULL, raw_mailbox_alloc, NULL, NULL, } }; struct mailbox raw_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, raw_mailbox_open, index_storage_mailbox_close, index_storage_mailbox_free, raw_mailbox_create, raw_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, index_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, raw_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, raw_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, index_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, NULL, NULL, NULL, NULL, NULL, mail_storage_copy, NULL, NULL, NULL, index_storage_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/raw/raw-mail.c0000644000175000017500000000752113165463624017120 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "index-mail.h" #include "raw-storage.h" #include #include #include static int raw_mail_stat(struct mail *mail) { struct raw_mailbox *mbox = (struct raw_mailbox *)mail->box; const struct stat *st; if (mail->lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE) { mail_set_aborted(mail); return -1; } mail->mail_metadata_accessed = TRUE; mail->transaction->stats.fstat_lookup_count++; if (i_stream_stat(mail->box->input, TRUE, &st) < 0) { mail_storage_set_critical(mail->box->storage, "stat(%s) failed: %m", i_stream_get_name(mail->box->input)); return -1; } if (mbox->mtime != (time_t)-1) mbox->mtime = st->st_mtime; if (mbox->ctime != (time_t)-1) mbox->ctime = st->st_ctime; mbox->size = st->st_size; return 0; } static int raw_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct raw_mailbox *mbox = (struct raw_mailbox *)_mail->box; if (mbox->mtime == (time_t)-1) { if (raw_mail_stat(_mail) < 0) return -1; } *date_r = mail->data.received_date = mbox->mtime; return 0; } static int raw_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct raw_mailbox *mbox = (struct raw_mailbox *)_mail->box; if (mbox->ctime == (time_t)-1) { if (raw_mail_stat(_mail) < 0) return -1; } *date_r = mail->data.save_date = mbox->ctime; return 0; } static int raw_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct raw_mailbox *mbox = (struct raw_mailbox *)_mail->box; if (mbox->size == (uoff_t)-1) { if (raw_mail_stat(_mail) < 0) return -1; } *size_r = mail->data.physical_size = mbox->size; return 0; } static int raw_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.stream == NULL) { /* we can't just reference mbox->input, because index_mail_close() expects to be able to free the stream */ mail->data.stream = i_stream_create_limit(_mail->box->input, (uoff_t)-1); } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static int raw_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct raw_mailbox *mbox = (struct raw_mailbox *)_mail->box; switch (field) { case MAIL_FETCH_FROM_ENVELOPE: *value_r = mbox->envelope_sender != NULL ? mbox->envelope_sender : ""; return 0; case MAIL_FETCH_STORAGE_ID: *value_r = mbox->have_filename ? mailbox_get_path(_mail->box) : ""; return 0; default: return index_mail_get_special(_mail, field, value_r); } } struct mail_vfuncs raw_mail_vfuncs = { index_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, raw_mail_get_received_date, raw_mail_get_save_date, index_mail_get_virtual_size, raw_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, raw_mail_get_stream, index_mail_get_binary_stream, raw_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/raw/Makefile.am0000644000175000017500000000066213123174404017263 00000000000000noinst_LTLIBRARIES = libstorage_raw.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_raw_la_SOURCES = \ raw-mail.c \ raw-sync.c \ raw-storage.c headers = \ raw-storage.h \ raw-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/raw/raw-sync.c0000644000175000017500000000330713165463624017150 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "raw-storage.h" #include "raw-sync.h" static int raw_sync(struct raw_mailbox *mbox) { struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_sync_rec sync_rec; struct mail_index_transaction *trans; uint32_t seq, uid_validity = ioloop_time; enum mail_index_sync_flags sync_flags; int ret; i_assert(!mbox->synced); sync_flags = index_storage_get_sync_flags(&mbox->box) | MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY | MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; if (mail_index_view_get_messages_count(mbox->box.view) > 0) { /* already-synced index was opened via mail-index-alloc-cache. */ return 0; } ret = mail_index_sync_begin(mbox->box.index, &index_sync_ctx, &sync_view, &trans, sync_flags); if (ret <= 0) { if (ret < 0) mailbox_set_index_error(&mbox->box); return ret; } /* set our uidvalidity */ mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); /* add our one and only message */ mail_index_append(trans, 1, &seq); mailbox_recent_flags_set_uid(&mbox->box, 1); while (mail_index_sync_next(index_sync_ctx, &sync_rec)) ; if (mail_index_sync_commit(&index_sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); return -1; } mbox->synced = TRUE; return 0; } struct mailbox_sync_context * raw_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct raw_mailbox *mbox = (struct raw_mailbox *)box; int ret = 0; if (!mbox->synced) ret = raw_sync(mbox); return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/index-sync.c0000644000175000017500000003673113165463624016704 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "seq-range-array.h" #include "ioloop.h" #include "array.h" #include "index-mailbox-size.h" #include "index-sync-private.h" struct index_storage_list_index_record { uint32_t size; uint32_t mtime; }; enum mail_index_sync_flags index_storage_get_sync_flags(struct mailbox *box) { enum mail_index_sync_flags sync_flags = 0; if ((box->flags & MAILBOX_FLAG_DROP_RECENT) != 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT; if (box->deleting) { sync_flags |= box->delete_sync_check ? MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX : MAIL_INDEX_SYNC_FLAG_DELETING_INDEX; } return sync_flags; } bool index_mailbox_want_full_sync(struct mailbox *box, enum mailbox_sync_flags flags) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); if ((flags & MAILBOX_SYNC_FLAG_FAST) != 0 && ioloop_time < ibox->sync_last_check + MAILBOX_FULL_SYNC_INTERVAL) return FALSE; if ((flags & MAILBOX_SYNC_FLAG_FAST) != 0 && (box->flags & MAILBOX_FLAG_SAVEONLY) != 0) { /* lib-lda is syncing the mailbox after saving a mail. it only wants to find the new mail for potentially copying to other mailboxes. that's mainly an optimization, and since the mail was most likely already added to index we don't need to do a full sync to find it. the main benefit here is to avoid a very costly sync with a large Maildir/new/ */ return FALSE; } if (box->to_notify != NULL) timeout_reset(box->to_notify); ibox->sync_last_check = ioloop_time; return TRUE; } static void index_view_sync_recs_get(struct index_mailbox_sync_context *ctx) { struct mail_index_view_sync_rec sync_rec; uint32_t seq1, seq2; i_array_init(&ctx->flag_updates, 128); i_array_init(&ctx->hidden_updates, 32); while (mail_index_view_sync_next(ctx->sync_ctx, &sync_rec)) { switch (sync_rec.type) { case MAIL_INDEX_VIEW_SYNC_TYPE_MODSEQ: case MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS: if (!mail_index_lookup_seq_range(ctx->ctx.box->view, sync_rec.uid1, sync_rec.uid2, &seq1, &seq2)) break; if (!sync_rec.hidden && sync_rec.type == MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS) { seq_range_array_add_range(&ctx->flag_updates, seq1, seq2); } else { seq_range_array_add_range(&ctx->hidden_updates, seq1, seq2); } break; } } } static void index_view_sync_cleanup_updates(struct index_mailbox_sync_context *ctx) { /* remove expunged messages from flag updates */ if (ctx->expunges != NULL) { seq_range_array_remove_seq_range(&ctx->flag_updates, ctx->expunges); seq_range_array_remove_seq_range(&ctx->hidden_updates, ctx->expunges); } /* remove flag updates from hidden updates */ seq_range_array_remove_seq_range(&ctx->hidden_updates, &ctx->flag_updates); } struct mailbox_sync_context * index_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags, bool failed) { struct index_mailbox_sync_context *ctx; struct index_mailbox_sync_pvt_context *pvt_ctx; enum mail_index_view_sync_flags sync_flags = 0; ctx = i_new(struct index_mailbox_sync_context, 1); ctx->ctx.box = box; ctx->ctx.flags = flags; if (failed) { ctx->failed = TRUE; return &ctx->ctx; } if ((flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) != 0) sync_flags |= MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES; if ((flags & MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) != 0) { sync_flags |= MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT; ctx->messages_count = 0; } else { ctx->messages_count = mail_index_view_get_messages_count(box->view); } if ((flags & MAILBOX_SYNC_FLAG_FAST) != 0) { /* we most likely did a fast sync. refresh the index anyway in case there were some new changes. */ (void)mail_index_refresh(box->index); } ctx->sync_ctx = mail_index_view_sync_begin(box->view, sync_flags); if ((flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0) { mail_index_view_sync_get_expunges(ctx->sync_ctx, &ctx->expunges); ctx->expunge_pos = array_count(ctx->expunges); } index_view_sync_recs_get(ctx); index_sync_search_results_expunge(ctx); /* sync private index if needed. it doesn't use box->view, so it doesn't matter if it's called at _sync_init() or _sync_deinit(). however we also need to know if any private flags have changed since last sync, so we need to call it before _sync_next() calls. */ if (index_mailbox_sync_pvt_init(box, FALSE, &pvt_ctx) > 0) { (void)index_mailbox_sync_pvt_view(pvt_ctx, &ctx->flag_updates, &ctx->hidden_updates); index_mailbox_sync_pvt_deinit(&pvt_ctx); } index_view_sync_cleanup_updates(ctx); return &ctx->ctx; } static bool index_mailbox_sync_next_expunge(struct index_mailbox_sync_context *ctx, struct mailbox_sync_rec *sync_rec_r) { const struct seq_range *range; if (ctx->expunge_pos == 0) return FALSE; /* expunges is a sorted array of sequences. it's easiest for us to print them from end to beginning. */ ctx->expunge_pos--; range = array_idx(ctx->expunges, ctx->expunge_pos); i_assert(range->seq2 <= ctx->messages_count); mailbox_recent_flags_expunge_seqs(ctx->ctx.box, range->seq1, range->seq2); ctx->messages_count -= range->seq2 - range->seq1 + 1; sync_rec_r->seq1 = range->seq1; sync_rec_r->seq2 = range->seq2; sync_rec_r->type = MAILBOX_SYNC_TYPE_EXPUNGE; return TRUE; } bool index_mailbox_sync_next(struct mailbox_sync_context *_ctx, struct mailbox_sync_rec *sync_rec_r) { struct index_mailbox_sync_context *ctx = (struct index_mailbox_sync_context *)_ctx; const struct seq_range *range; unsigned int count; if (ctx->failed) return FALSE; range = array_get(&ctx->flag_updates, &count); if (ctx->flag_update_idx < count) { sync_rec_r->type = MAILBOX_SYNC_TYPE_FLAGS; sync_rec_r->seq1 = range[ctx->flag_update_idx].seq1; sync_rec_r->seq2 = range[ctx->flag_update_idx].seq2; ctx->flag_update_idx++; return TRUE; } if ((_ctx->box->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0) { /* hidden flag changes' MODSEQs still need to be returned */ range = array_get(&ctx->hidden_updates, &count); if (ctx->hidden_update_idx < count) { sync_rec_r->type = MAILBOX_SYNC_TYPE_MODSEQ; sync_rec_r->seq1 = range[ctx->hidden_update_idx].seq1; sync_rec_r->seq2 = range[ctx->hidden_update_idx].seq2; ctx->hidden_update_idx++; return TRUE; } } return index_mailbox_sync_next_expunge(ctx, sync_rec_r); } static void index_mailbox_expunge_unseen_recent(struct index_mailbox_sync_context *ctx) { struct mailbox *box = ctx->ctx.box; struct mail_index_view *view = ctx->ctx.box->view; const struct mail_index_header *hdr; uint32_t seq, start_uid, uid; if (!array_is_created(&box->recent_flags)) return; /* expunges array contained expunges for the messages that were already visible in this view, but append+expunge would be invisible. recent_flags may however contain the append UID, so we'll have to remove it separately */ hdr = mail_index_get_header(view); if (ctx->messages_count == 0) uid = 0; else if (ctx->messages_count <= hdr->messages_count) mail_index_lookup_uid(view, ctx->messages_count, &uid); else { i_assert(mail_index_view_is_inconsistent(view)); return; } for (seq = ctx->messages_count + 1; seq <= hdr->messages_count; seq++) { start_uid = uid; mail_index_lookup_uid(view, seq, &uid); if (start_uid + 1 > uid - 1) continue; box->recent_flags_count -= seq_range_array_remove_range(&box->recent_flags, start_uid + 1, uid - 1); } if (uid + 1 < hdr->next_uid) { box->recent_flags_count -= seq_range_array_remove_range(&box->recent_flags, uid + 1, hdr->next_uid - 1); } #ifdef DEBUG if (!mail_index_view_is_inconsistent(view)) { const struct seq_range *range; unsigned int i, count; range = array_get(&box->recent_flags, &count); for (i = 0; i < count; i++) { for (uid = range[i].seq1; uid <= range[i].seq2; uid++) { if (uid >= hdr->next_uid) break; (void)mail_index_lookup_seq(view, uid, &seq); i_assert(seq != 0); } } } #endif } void index_sync_update_recent_count(struct mailbox *box) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); const struct mail_index_header *hdr; uint32_t seq1, seq2; hdr = mail_index_get_header(box->view); if (hdr->first_recent_uid < ibox->recent_flags_prev_first_recent_uid) { mail_storage_set_critical(box->storage, "Mailbox %s: first_recent_uid unexpectedly shrank: %u -> %u", box->vname, ibox->recent_flags_prev_first_recent_uid, hdr->first_recent_uid); mailbox_recent_flags_reset(box); } if (hdr->first_recent_uid > box->recent_flags_prev_uid || hdr->next_uid > ibox->recent_flags_last_check_nextuid) { ibox->recent_flags_prev_first_recent_uid = hdr->first_recent_uid; ibox->recent_flags_last_check_nextuid = hdr->next_uid; if (mail_index_lookup_seq_range(box->view, hdr->first_recent_uid, hdr->next_uid, &seq1, &seq2)) { mailbox_recent_flags_set_seqs(box, box->view, seq1, seq2); } } } static void index_mailbox_sync_free(struct index_mailbox_sync_context *ctx) { if (array_is_created(&ctx->flag_updates)) array_free(&ctx->flag_updates); if (array_is_created(&ctx->hidden_updates)) array_free(&ctx->hidden_updates); if (array_is_created(&ctx->all_flag_update_uids)) array_free(&ctx->all_flag_update_uids); i_free(ctx); } int index_mailbox_sync_deinit(struct mailbox_sync_context *_ctx, struct mailbox_sync_status *status_r) { struct index_mailbox_sync_context *ctx = (struct index_mailbox_sync_context *)_ctx; struct mailbox_sync_rec sync_rec; bool delayed_expunges = FALSE; int ret = ctx->failed ? -1 : 0; /* finish handling expunges, so we don't break when updating recent flags */ while (index_mailbox_sync_next_expunge(ctx, &sync_rec) > 0) ; /* convert sequences to uids before syncing view */ index_sync_search_results_uidify(ctx); if (ctx->sync_ctx != NULL) { if (mail_index_view_sync_commit(&ctx->sync_ctx, &delayed_expunges) < 0) { mailbox_set_index_error(_ctx->box); ret = -1; } } if (ret < 0) { index_mailbox_sync_free(ctx); return -1; } index_mailbox_expunge_unseen_recent(ctx); if ((_ctx->box->flags & MAILBOX_FLAG_DROP_RECENT) == 0 && _ctx->box->opened) { /* mailbox syncing didn't necessarily update our recent state */ index_sync_update_recent_count(_ctx->box); } if (status_r != NULL) status_r->sync_delayed_expunges = delayed_expunges; /* update search results after private index is updated */ index_sync_search_results_update(ctx); /* update vsize header if wanted */ index_mailbox_vsize_update_appends(_ctx->box); if (ret == 0 && mail_index_view_is_inconsistent(_ctx->box->view)) { /* we probably had MAILBOX_SYNC_FLAG_FIX_INCONSISTENT set, but the view got broken in the middle. FIXME: We could attempt to fix it automatically. In any case now the view isn't usable and we can't return success. */ mailbox_set_index_error(_ctx->box); ret = -1; } index_mailbox_sync_free(ctx); return ret; } bool index_keyword_array_cmp(const ARRAY_TYPE(keyword_indexes) *k1, const ARRAY_TYPE(keyword_indexes) *k2) { const unsigned int *idx1, *idx2; unsigned int i, j, count1, count2; if (!array_is_created(k1)) return !array_is_created(k2) || array_count(k2) == 0; if (!array_is_created(k2)) return array_count(k1) == 0; /* The arrays may not be sorted, but they usually are. Optimize for the assumption that they are */ idx1 = array_get(k1, &count1); idx2 = array_get(k2, &count2); if (count1 != count2) return FALSE; for (i = 0; i < count1; i++) { if (idx1[i] != idx2[i]) { /* not found / unsorted array. check. */ for (j = 0; j < count1; j++) { if (idx1[i] == idx2[j]) break; } if (j == count1) return FALSE; } } return TRUE; } enum mailbox_sync_type index_sync_type_convert(enum mail_index_sync_type type) { enum mailbox_sync_type ret = 0; if ((type & MAIL_INDEX_SYNC_TYPE_EXPUNGE) != 0) ret |= MAILBOX_SYNC_TYPE_EXPUNGE; if ((type & (MAIL_INDEX_SYNC_TYPE_FLAGS | MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD | MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE)) != 0) ret |= MAILBOX_SYNC_TYPE_FLAGS; return ret; } static uint32_t index_list_get_ext_id(struct mailbox *box, struct mail_index_view *view) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); if (ibox->list_index_sync_ext_id == (uint32_t)-1) { ibox->list_index_sync_ext_id = mail_index_ext_register(mail_index_view_get_index(view), "index sync", 0, sizeof(struct index_storage_list_index_record), sizeof(uint32_t)); } return ibox->list_index_sync_ext_id; } enum index_storage_list_change index_storage_list_index_has_changed_full(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq) { const struct index_storage_list_index_record *rec; const void *data; const char *dir, *path; struct stat st; uint32_t ext_id; bool expunged; int ret; if (mail_index_is_in_memory(mail_index_view_get_index(list_view))) return INDEX_STORAGE_LIST_CHANGE_INMEMORY; ext_id = index_list_get_ext_id(box, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); rec = data; if (rec == NULL || expunged || rec->size == 0 || rec->mtime == 0) { /* doesn't exist / not synced */ return INDEX_STORAGE_LIST_CHANGE_NORECORD; } if (box->storage->set->mailbox_list_index_very_dirty_syncs) return INDEX_STORAGE_LIST_CHANGE_NONE; ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &dir); if (ret < 0) return INDEX_STORAGE_LIST_CHANGE_ERROR; i_assert(ret > 0); path = t_strconcat(dir, "/", box->index_prefix, ".log", NULL); if (stat(path, &st) < 0) { if (errno == ENOENT) return INDEX_STORAGE_LIST_CHANGE_NOT_IN_FS; mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return INDEX_STORAGE_LIST_CHANGE_ERROR; } if (rec->size != (st.st_size & 0xffffffffU)) return INDEX_STORAGE_LIST_CHANGE_SIZE_CHANGED; if (rec->mtime != (st.st_mtime & 0xffffffffU)) return INDEX_STORAGE_LIST_CHANGE_MTIME_CHANGED; return INDEX_STORAGE_LIST_CHANGE_NONE; } int index_storage_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq) { switch (index_storage_list_index_has_changed_full(box, list_view, seq)) { case INDEX_STORAGE_LIST_CHANGE_ERROR: return -1; case INDEX_STORAGE_LIST_CHANGE_NONE: return 0; default: return 1; } } void index_storage_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq) { struct mail_index_view *list_view; const struct index_storage_list_index_record *old_rec; struct index_storage_list_index_record new_rec; const void *data; const char *dir, *path; struct stat st; uint32_t ext_id; bool expunged; int ret; list_view = mail_index_transaction_get_view(trans); if (mail_index_is_in_memory(mail_index_view_get_index(list_view))) return; /* get the current record */ ext_id = index_list_get_ext_id(box, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); if (expunged) return; old_rec = data; ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &dir); if (ret < 0) return; i_assert(ret > 0); path = t_strconcat(dir, "/", box->index_prefix, ".log", NULL); if (stat(path, &st) < 0) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return; } i_zero(&new_rec); new_rec.size = st.st_size & 0xffffffffU; new_rec.mtime = st.st_mtime & 0xffffffffU; if (old_rec == NULL || memcmp(old_rec, &new_rec, sizeof(*old_rec)) != 0) mail_index_update_ext(trans, seq, ext_id, &new_rec, NULL); } dovecot-2.2.33.2/src/lib-storage/index/index-sync-changes.h0000644000175000017500000000204013123174404020266 00000000000000#ifndef INDEX_SYNC_CHANGES_H #define INDEX_SYNC_CHANGES_H struct index_sync_changes_context * index_sync_changes_init(struct mail_index_sync_ctx *index_sync_ctx, struct mail_index_view *sync_view, struct mail_index_transaction *sync_trans, bool dirty_flag_updates); void index_sync_changes_deinit(struct index_sync_changes_context **_ctx); void index_sync_changes_reset(struct index_sync_changes_context *ctx); void index_sync_changes_delete_to(struct index_sync_changes_context *ctx, uint32_t last_uid); void index_sync_changes_read(struct index_sync_changes_context *ctx, uint32_t uid, bool *sync_expunge_r, guid_128_t expunged_guid_128); bool index_sync_changes_have(struct index_sync_changes_context *ctx); uint32_t index_sync_changes_get_next_uid(struct index_sync_changes_context *ctx); void index_sync_changes_apply(struct index_sync_changes_context *ctx, pool_t pool, uint8_t *flags, ARRAY_TYPE(keyword_indexes) *keywords, enum mail_index_sync_type *sync_type_r) ATTR_NULL(2); #endif dovecot-2.2.33.2/src/lib-storage/index/index-thread-links.c0000644000175000017500000001557113123174404020301 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "message-id.h" #include "mail-storage.h" #include "index-thread-private.h" static uint32_t thread_msg_add(struct mail_thread_cache *cache, uint32_t uid, uint32_t msgid_idx) { struct mail_thread_node *node; i_assert(msgid_idx != 0); i_assert(msgid_idx < cache->first_invalid_msgid_str_idx); node = array_idx_modifiable(&cache->thread_nodes, msgid_idx); if (node->uid == 0) node->uid = uid; else { /* duplicate message-id, keep the original. if the original ever gets expunged, rebuild. */ node->expunge_rebuilds = TRUE; msgid_idx = cache->next_invalid_msgid_str_idx++; node = array_idx_modifiable(&cache->thread_nodes, msgid_idx); node->uid = uid; } return msgid_idx; } static bool thread_node_has_ancestor(struct mail_thread_cache *cache, const struct mail_thread_node *node, const struct mail_thread_node *ancestor) { while (node != ancestor) { if (node->parent_idx == 0) return FALSE; node = array_idx(&cache->thread_nodes, node->parent_idx); } return TRUE; } static void thread_link_reference(struct mail_thread_cache *cache, uint32_t parent_idx, uint32_t child_idx) { struct mail_thread_node *node, *parent, *child; uint32_t idx; i_assert(parent_idx < cache->first_invalid_msgid_str_idx); /* either child_idx or parent_idx may cause thread_nodes array to grow. in such situation the other pointer may become invalid if we don't get the pointers in correct order. */ if (child_idx < parent_idx) { parent = array_idx_modifiable(&cache->thread_nodes, parent_idx); child = array_idx_modifiable(&cache->thread_nodes, child_idx); } else { child = array_idx_modifiable(&cache->thread_nodes, child_idx); parent = array_idx_modifiable(&cache->thread_nodes, parent_idx); } child->parent_link_refcount++; if (thread_node_has_ancestor(cache, parent, child)) { if (parent == child) { /* loops to itself - ignore */ return; } /* child is an ancestor of parent. Adding child -> parent_node would introduce a loop. If any messages referencing the path between parent_node's parent and child_node get expunged, we have to rebuild the tree because the loop might break. For example: #1: a -> b (a.ref=1, b.ref=1) #2: b -> a (a.ref=2, b.ref=2) #3: c -> a -> b (a.ref=3, b.ref=3, c.ref=1) Expunging #3 wouldn't break the loop, but expunging #1 would. */ node = parent; do { idx = node->parent_idx; i_assert(idx != 0); node = array_idx_modifiable(&cache->thread_nodes, idx); node->child_unref_rebuilds = TRUE; } while (node != child); return; } else if (child->parent_idx == parent_idx) { /* The same link already exists */ return; } /* Set parent_node as child_node's parent */ if (child->parent_idx == 0) { child->parent_idx = parent_idx; } else { /* Conflicting parent already exists, keep the original */ if (MAIL_THREAD_NODE_EXISTS(child)) { /* If this message gets expunged, the parent is changed. */ child->expunge_rebuilds = TRUE; } else { /* Message doesn't exist, so it was one of the node's children that created the original reference. If that reference gets dropped, the parent is changed. We could catch this in one of several ways: a) Link to parent node gets unreferenced b) Link to this node gets unreferenced c) Any of the child nodes gets expunged b) is probably the least likely to happen, so use it */ child->child_unref_rebuilds = TRUE; } } } static uint32_t thread_link_references(struct mail_thread_cache *cache, uint32_t uid, const struct mail_index_strmap_rec *msgid_map, unsigned int *msgid_map_idx) { uint32_t parent_idx; if (msgid_map->uid != uid) return 0; parent_idx = msgid_map->str_idx; msgid_map++; *msgid_map_idx += 1; for (; msgid_map->uid == uid; msgid_map++) { thread_link_reference(cache, parent_idx, msgid_map->str_idx); parent_idx = msgid_map->str_idx; *msgid_map_idx += 1; } i_assert(parent_idx < cache->first_invalid_msgid_str_idx); return parent_idx; } void mail_thread_add(struct mail_thread_cache *cache, const struct mail_index_strmap_rec *msgid_map, unsigned int *msgid_map_idx) { struct mail_thread_node *node; uint32_t idx, parent_idx; i_assert(msgid_map->ref_index == MAIL_THREAD_NODE_REF_MSGID); i_assert(cache->last_uid <= msgid_map->uid); cache->last_uid = msgid_map->uid; idx = thread_msg_add(cache, msgid_map->uid, msgid_map->str_idx); parent_idx = thread_link_references(cache, msgid_map->uid, msgid_map + 1, msgid_map_idx); node = array_idx_modifiable(&cache->thread_nodes, idx); if (node->parent_idx != parent_idx && node->parent_idx != 0) { /* conflicting parent, remove it. */ node->parent_idx = 0; /* If this message gets expunged, we have to revert back to the original parent. */ node->expunge_rebuilds = TRUE; } if (parent_idx != 0) thread_link_reference(cache, parent_idx, idx); *msgid_map_idx += 1; } static bool mail_thread_unref_link(struct mail_thread_cache *cache, uint32_t parent_idx, uint32_t child_idx) { struct mail_thread_node *parent, *child; parent = array_idx_modifiable(&cache->thread_nodes, parent_idx); if (parent->child_unref_rebuilds) return FALSE; child = array_idx_modifiable(&cache->thread_nodes, child_idx); i_assert(child->parent_link_refcount > 0); child->parent_link_refcount--; if (child->parent_link_refcount == 0) { /* we don't have a root anymore */ child->parent_idx = 0; } return TRUE; } bool mail_thread_remove(struct mail_thread_cache *cache, const struct mail_index_strmap_rec *msgid_map, unsigned int *msgid_map_idx) { struct mail_thread_node *node; uint32_t idx, parent_idx; unsigned int count = 1; idx = msgid_map->str_idx; i_assert(idx != 0); if (msgid_map->uid > cache->last_uid) { /* this message was never added to the cache, skip */ while (msgid_map[count].uid == msgid_map->uid) count++; *msgid_map_idx += count; return TRUE; } node = array_idx_modifiable(&cache->thread_nodes, idx); if (node->expunge_rebuilds) { /* this catches the duplicate message-id case */ return FALSE; } i_assert(node->uid == msgid_map->uid); /* update link refcounts */ if (msgid_map[count].uid == node->uid) { parent_idx = msgid_map[count].str_idx; count++; while (msgid_map[count].uid == node->uid) { if (!mail_thread_unref_link(cache, parent_idx, msgid_map[count].str_idx)) return FALSE; parent_idx = msgid_map[count].str_idx; count++; } if (!mail_thread_unref_link(cache, parent_idx, idx)) return FALSE; } /* mark this message as expunged */ node->uid = 0; /* we don't know (and don't want to waste time figuring out) if other messages point to this removed message, so don't delete the node */ *msgid_map_idx += count; return TRUE; } dovecot-2.2.33.2/src/lib-storage/index/index-sort-private.h0000644000175000017500000000220613123174404020347 00000000000000#ifndef INDEX_SORT_PRIVATE_H #define INDEX_SORT_PRIVATE_H #include "index-sort.h" struct mail_search_sort_program { struct mailbox_transaction_context *t; enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE]; struct mail *temp_mail; unsigned int slow_mails_left; void (*sort_list_add)(struct mail_search_sort_program *program, struct mail *mail); void (*sort_list_finish)(struct mail_search_sort_program *program); void *context; ARRAY_TYPE(uint32_t) seqs; unsigned int iter_idx; bool failed; }; /* Returns 1 on success, 0 if mail is already expunged, -1 on other errors. */ int index_sort_header_get(struct mail_search_sort_program *program, uint32_t seq, enum mail_sort_type sort_type, string_t *dest); int index_sort_node_cmp_type(struct mail_search_sort_program *program, const enum mail_sort_type *sort_program, uint32_t seq1, uint32_t seq2); void index_sort_list_init_string(struct mail_search_sort_program *program); void index_sort_list_add_string(struct mail_search_sort_program *program, struct mail *mail); void index_sort_list_finish_string(struct mail_search_sort_program *program); #endif dovecot-2.2.33.2/src/lib-storage/index/cydir/0002755000175000017500000000000013172375611015635 500000000000000dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-sync.c0000644000175000017500000001030713165463624020010 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "cydir-storage.h" #include "cydir-sync.h" static void cydir_sync_set_uidvalidity(struct cydir_sync_context *ctx) { uint32_t uid_validity = ioloop_time; mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); ctx->uid_validity = uid_validity; } static string_t *cydir_get_path_prefix(struct cydir_mailbox *mbox) { string_t *path = str_new(default_pool, 256); str_append(path, mailbox_get_path(&mbox->box)); str_append_c(path, '/'); return path; } static void cydir_sync_expunge(struct cydir_sync_context *ctx, uint32_t seq1, uint32_t seq2) { struct mailbox *box = &ctx->mbox->box; uint32_t uid; if (ctx->path == NULL) { ctx->path = cydir_get_path_prefix(ctx->mbox); ctx->path_dir_prefix_len = str_len(ctx->path); } for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(ctx->sync_view, seq1, &uid); str_truncate(ctx->path, ctx->path_dir_prefix_len); str_printfa(ctx->path, "%u.", uid); if (i_unlink_if_exists(str_c(ctx->path)) < 0) { /* continue anyway */ } else { if (box->v.sync_notify != NULL) { box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } mail_index_expunge(ctx->trans, seq1); } } } static void cydir_sync_index(struct cydir_sync_context *ctx) { struct mailbox *box = &ctx->mbox->box; const struct mail_index_header *hdr; struct mail_index_sync_rec sync_rec; uint32_t seq1, seq2; hdr = mail_index_get_header(ctx->sync_view); if (hdr->uid_validity != 0) ctx->uid_validity = hdr->uid_validity; else cydir_sync_set_uidvalidity(ctx); /* mark the newly seen messages as recent */ if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid, hdr->next_uid, &seq1, &seq2)) { mailbox_recent_flags_set_seqs(&ctx->mbox->box, ctx->sync_view, seq1, seq2); } while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) { if (!mail_index_lookup_seq_range(ctx->sync_view, sync_rec.uid1, sync_rec.uid2, &seq1, &seq2)) { /* already expunged, nothing to do. */ continue; } switch (sync_rec.type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: cydir_sync_expunge(ctx, seq1, seq2); break; case MAIL_INDEX_SYNC_TYPE_FLAGS: case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: /* FIXME: should be bother calling sync_notify()? */ break; } } if (box->v.sync_notify != NULL) box->v.sync_notify(box, 0, 0); } int cydir_sync_begin(struct cydir_mailbox *mbox, struct cydir_sync_context **ctx_r, bool force) { struct cydir_sync_context *ctx; enum mail_index_sync_flags sync_flags; int ret; ctx = i_new(struct cydir_sync_context, 1); ctx->mbox = mbox; sync_flags = index_storage_get_sync_flags(&mbox->box) | MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY; if (!force) sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; ret = index_storage_expunged_sync_begin(&mbox->box, &ctx->index_sync_ctx, &ctx->sync_view, &ctx->trans, sync_flags); if (ret <= 0) { i_free(ctx); *ctx_r = NULL; return ret; } cydir_sync_index(ctx); index_storage_expunging_deinit(&mbox->box); *ctx_r = ctx; return 0; } int cydir_sync_finish(struct cydir_sync_context **_ctx, bool success) { struct cydir_sync_context *ctx = *_ctx; int ret = success ? 0 : -1; *_ctx = NULL; if (success) { if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { mailbox_set_index_error(&ctx->mbox->box); ret = -1; } } else { mail_index_sync_rollback(&ctx->index_sync_ctx); } if (ctx->path != NULL) str_free(&ctx->path); i_free(ctx); return ret; } static int cydir_sync(struct cydir_mailbox *mbox) { struct cydir_sync_context *sync_ctx; if (cydir_sync_begin(mbox, &sync_ctx, FALSE) < 0) return -1; return sync_ctx == NULL ? 0 : cydir_sync_finish(&sync_ctx, TRUE); } struct mailbox_sync_context * cydir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct cydir_mailbox *mbox = (struct cydir_mailbox *)box; int ret = 0; if (index_mailbox_want_full_sync(&mbox->box, flags)) ret = cydir_sync(mbox); return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/cydir/Makefile.in0000644000175000017500000005501713172375573017637 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/cydir ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_cydir_la_LIBADD = am_libstorage_cydir_la_OBJECTS = cydir-mail.lo cydir-save.lo \ cydir-sync.lo cydir-storage.lo libstorage_cydir_la_OBJECTS = $(am_libstorage_cydir_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_cydir_la_SOURCES) DIST_SOURCES = $(libstorage_cydir_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_cydir.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_cydir_la_SOURCES = \ cydir-mail.c \ cydir-save.c \ cydir-sync.c \ cydir-storage.c headers = \ cydir-storage.h \ cydir-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/cydir/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/cydir/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_cydir.la: $(libstorage_cydir_la_OBJECTS) $(libstorage_cydir_la_DEPENDENCIES) $(EXTRA_libstorage_cydir_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_cydir_la_OBJECTS) $(libstorage_cydir_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cydir-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cydir-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cydir-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cydir-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-sync.h0000644000175000017500000000114113123174404017776 00000000000000#ifndef CYDIR_SYNC_H #define CYDIR_SYNC_H struct mailbox; struct cydir_sync_context { struct cydir_mailbox *mbox; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; string_t *path; size_t path_dir_prefix_len; uint32_t uid_validity; }; int cydir_sync_begin(struct cydir_mailbox *mbox, struct cydir_sync_context **ctx_r, bool force); int cydir_sync_finish(struct cydir_sync_context **ctx, bool success); struct mailbox_sync_context * cydir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); #endif dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-save.c0000644000175000017500000002011613165463624017771 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hostpid.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "str.h" #include "index-mail.h" #include "cydir-storage.h" #include "cydir-sync.h" #include #include struct cydir_save_context { struct mail_save_context ctx; struct cydir_mailbox *mbox; struct mail_index_transaction *trans; char *tmp_basename; unsigned int mail_count; struct cydir_sync_context *sync_ctx; /* updated for each appended mail: */ uint32_t seq; struct istream *input; int fd; unsigned int failed:1; unsigned int finished:1; }; static char *cydir_generate_tmp_filename(void) { static unsigned int create_count = 0; return i_strdup_printf("temp.%s.P%sQ%uM%s.%s", dec2str(ioloop_timeval.tv_sec), my_pid, create_count++, dec2str(ioloop_timeval.tv_usec), my_hostname); } static const char * cydir_get_save_path(struct cydir_save_context *ctx, unsigned int num) { const char *dir; dir = mailbox_get_path(&ctx->mbox->box); return t_strdup_printf("%s/%s.%u", dir, ctx->tmp_basename, num); } struct mail_save_context * cydir_save_alloc(struct mailbox_transaction_context *t) { struct cydir_mailbox *mbox = (struct cydir_mailbox *)t->box; struct cydir_save_context *ctx; i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (t->save_ctx == NULL) { ctx = i_new(struct cydir_save_context, 1); ctx->ctx.transaction = t; ctx->mbox = mbox; ctx->trans = t->itrans; ctx->tmp_basename = cydir_generate_tmp_filename(); ctx->fd = -1; t->save_ctx = &ctx->ctx; } return t->save_ctx; } int cydir_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; struct mailbox_transaction_context *trans = _ctx->transaction; enum mail_flags save_flags; struct istream *crlf_input; ctx->failed = FALSE; T_BEGIN { const char *path; path = cydir_get_save_path(ctx, ctx->mail_count); ctx->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0660); if (ctx->fd != -1) { _ctx->data.output = o_stream_create_fd_file(ctx->fd, 0, FALSE); o_stream_cork(_ctx->data.output); } else { mail_storage_set_critical(trans->box->storage, "open(%s) failed: %m", path); ctx->failed = TRUE; } } T_END; if (ctx->failed) return -1; /* add to index */ save_flags = _ctx->data.flags & ~MAIL_RECENT; mail_index_append(ctx->trans, 0, &ctx->seq); mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, save_flags); if (_ctx->data.keywords != NULL) { mail_index_update_keywords(ctx->trans, ctx->seq, MODIFY_REPLACE, _ctx->data.keywords); } if (_ctx->data.min_modseq != 0) { mail_index_update_modseq(ctx->trans, ctx->seq, _ctx->data.min_modseq); } mail_set_seq_saving(_ctx->dest_mail, ctx->seq); crlf_input = i_stream_create_crlf(input); ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input); i_stream_unref(&crlf_input); return ctx->failed ? -1 : 0; } int cydir_save_continue(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; if (ctx->failed) return -1; do { if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %m", cydir_get_save_path(ctx, ctx->mail_count)); } ctx->failed = TRUE; return -1; } index_mail_cache_parse_continue(_ctx->dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; } static int cydir_save_flush(struct cydir_save_context *ctx, const char *path) { struct mail_storage *storage = &ctx->mbox->storage->storage; struct stat st; int ret = 0; if (o_stream_nfinish(ctx->ctx.data.output) < 0) { mail_storage_set_critical(storage, "write(%s) failed: %s", path, o_stream_get_error(ctx->ctx.data.output)); ret = -1; } if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fsync(ctx->fd) < 0) { mail_storage_set_critical(storage, "fsync(%s) failed: %m", path); ret = -1; } } if (ctx->ctx.data.received_date == (time_t)-1) { if (fstat(ctx->fd, &st) == 0) ctx->ctx.data.received_date = st.st_mtime; else { mail_storage_set_critical(storage, "fstat(%s) failed: %m", path); ret = -1; } } else { struct utimbuf ut; ut.actime = ioloop_time; ut.modtime = ctx->ctx.data.received_date; if (utime(path, &ut) < 0) { mail_storage_set_critical(storage, "utime(%s) failed: %m", path); ret = -1; } } o_stream_destroy(&ctx->ctx.data.output); if (close(ctx->fd) < 0) { mail_storage_set_critical(storage, "close(%s) failed: %m", path); ret = -1; } ctx->fd = -1; return ret; } int cydir_save_finish(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; const char *path = cydir_get_save_path(ctx, ctx->mail_count); ctx->finished = TRUE; if (ctx->fd != -1) { if (cydir_save_flush(ctx, path) < 0) ctx->failed = TRUE; } if (!ctx->failed) ctx->mail_count++; else i_unlink(path); index_mail_cache_parse_deinit(_ctx->dest_mail, _ctx->data.received_date, !ctx->failed); if (ctx->input != NULL) i_stream_unref(&ctx->input); index_save_context_free(_ctx); return ctx->failed ? -1 : 0; } void cydir_save_cancel(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; ctx->failed = TRUE; (void)cydir_save_finish(_ctx); } int cydir_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; const struct mail_index_header *hdr; struct seq_range_iter iter; uint32_t uid; const char *dir; string_t *src_path, *dest_path; unsigned int n; size_t src_prefixlen, dest_prefixlen; i_assert(ctx->finished); if (cydir_sync_begin(ctx->mbox, &ctx->sync_ctx, TRUE) < 0) { ctx->failed = TRUE; cydir_transaction_save_rollback(_ctx); return -1; } hdr = mail_index_get_header(ctx->sync_ctx->sync_view); mail_index_append_finish_uids(ctx->trans, hdr->next_uid, &_t->changes->saved_uids); _t->changes->uid_validity = ctx->sync_ctx->uid_validity; dir = mailbox_get_path(&ctx->mbox->box); src_path = t_str_new(256); str_printfa(src_path, "%s/%s.", dir, ctx->tmp_basename); src_prefixlen = str_len(src_path); dest_path = t_str_new(256); str_append(dest_path, dir); str_append_c(dest_path, '/'); dest_prefixlen = str_len(dest_path); seq_range_array_iter_init(&iter, &_t->changes->saved_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid) > 0) { str_truncate(src_path, src_prefixlen); str_truncate(dest_path, dest_prefixlen); str_printfa(src_path, "%u", n-1); str_printfa(dest_path, "%u.", uid); if (rename(str_c(src_path), str_c(dest_path)) < 0) { mail_storage_set_critical(&ctx->mbox->storage->storage, "rename(%s, %s) failed: %m", str_c(src_path), str_c(dest_path)); ctx->failed = TRUE; cydir_transaction_save_rollback(_ctx); return -1; } } return 0; } void cydir_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; _ctx->transaction = NULL; /* transaction is already freed */ mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx, result); (void)cydir_sync_finish(&ctx->sync_ctx, TRUE); cydir_transaction_save_rollback(_ctx); } void cydir_transaction_save_rollback(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; if (!ctx->finished) cydir_save_cancel(&ctx->ctx); if (ctx->sync_ctx != NULL) (void)cydir_sync_finish(&ctx->sync_ctx, FALSE); i_free(ctx->tmp_basename); i_free(ctx); } dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-storage.c0000644000175000017500000001064513165463624020505 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-copy.h" #include "index-mail.h" #include "mailbox-list-private.h" #include "cydir-sync.h" #include "cydir-storage.h" #include extern struct mail_storage cydir_storage; extern struct mailbox cydir_mailbox; static struct mail_storage *cydir_storage_alloc(void) { struct cydir_storage *storage; pool_t pool; pool = pool_alloconly_create("cydir storage", 512+256); storage = p_new(pool, struct cydir_storage, 1); storage->storage = cydir_storage; storage->storage.pool = pool; return &storage->storage; } static void cydir_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_FS; if (set->subscription_fname == NULL) set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME; } static struct mailbox * cydir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct cydir_mailbox *mbox; pool_t pool; /* cydir can't work without index files */ flags &= ~MAILBOX_FLAG_NO_INDEX_FILES; pool = pool_alloconly_create("cydir mailbox", 1024*3); mbox = p_new(pool, struct cydir_mailbox, 1); mbox->box = cydir_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &cydir_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = (struct cydir_storage *)storage; return &mbox->box; } static int cydir_mailbox_open(struct mailbox *box) { const char *box_path = mailbox_get_path(box); struct stat st; if (stat(box_path, &st) == 0) { /* exists, open it */ } else if (errno == ENOENT) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } else if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", mail_error_eacces_msg("stat", box_path)); return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", box_path); return -1; } if (index_storage_mailbox_open(box, FALSE) < 0) return -1; mail_index_set_fsync_mode(box->index, box->storage->set->parsed_fsync_mode, MAIL_INDEX_FSYNC_MASK_APPENDS | MAIL_INDEX_FSYNC_MASK_EXPUNGES); return 0; } static int cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { int ret; if ((ret = index_storage_mailbox_create(box, directory)) <= 0) return ret; return update == NULL ? 0 : index_storage_mailbox_update(box, update); } static void cydir_notify_changes(struct mailbox *box) { if (box->notify_callback == NULL) mailbox_watch_remove_all(box); else mailbox_watch_add(box, mailbox_get_path(box)); } struct mail_storage cydir_storage = { .name = CYDIR_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA, .v = { NULL, cydir_storage_alloc, NULL, index_storage_destroy, NULL, cydir_storage_get_list_settings, NULL, cydir_mailbox_alloc, NULL, NULL, } }; struct mailbox cydir_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, cydir_mailbox_open, index_storage_mailbox_close, index_storage_mailbox_free, cydir_mailbox_create, index_storage_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, NULL, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, cydir_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, cydir_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, index_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, cydir_save_alloc, cydir_save_begin, cydir_save_continue, cydir_save_finish, cydir_save_cancel, mail_storage_copy, cydir_transaction_save_commit_pre, cydir_transaction_save_commit_post, cydir_transaction_save_rollback, index_storage_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-mail.c0000644000175000017500000001007113165463624017754 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "index-mail.h" #include "cydir-storage.h" #include #include #include static const char *cydir_mail_get_path(struct mail *mail) { const char *dir; dir = mailbox_get_path(mail->box); return t_strdup_printf("%s/%u.", dir, mail->uid); } static int cydir_mail_stat(struct mail *mail, struct stat *st_r) { const char *path; if (mail->lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE) { mail_set_aborted(mail); return -1; } mail->mail_metadata_accessed = TRUE; mail->transaction->stats.stat_lookup_count++; path = cydir_mail_get_path(mail); if (stat(path, st_r) < 0) { if (errno == ENOENT) mail_set_expunged(mail); else { mail_storage_set_critical(mail->box->storage, "stat(%s) failed: %m", path); } return -1; } return 0; } static int cydir_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct stat st; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (cydir_mail_stat(_mail, &st) < 0) return -1; data->received_date = st.st_mtime; *date_r = data->received_date; return 0; } static int cydir_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct stat st; if (index_mail_get_save_date(_mail, date_r) == 0) return 0; if (cydir_mail_stat(_mail, &st) < 0) return -1; data->save_date = st.st_ctime; *date_r = data->save_date; return 0; } static int cydir_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct stat st; if (index_mail_get_physical_size(_mail, size_r) == 0) return 0; if (cydir_mail_stat(_mail, &st) < 0) return -1; data->physical_size = data->virtual_size = st.st_size; *size_r = data->physical_size; return 0; } static int cydir_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; struct istream *input; const char *path; int fd; if (mail->data.stream == NULL) { _mail->transaction->stats.open_lookup_count++; path = cydir_mail_get_path(_mail); fd = open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) mail_set_expunged(_mail); else { mail_storage_set_critical(_mail->box->storage, "open(%s) failed: %m", path); } return -1; } input = i_stream_create_fd_autoclose(&fd, 0); i_stream_set_name(input, path); index_mail_set_read_buffer_size(_mail, input); if (mail->mail.v.istream_opened != NULL) { if (mail->mail.v.istream_opened(_mail, &input) < 0) { i_stream_unref(&input); return -1; } } mail->data.stream = input; } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } struct mail_vfuncs cydir_mail_vfuncs = { index_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, cydir_mail_get_received_date, cydir_mail_get_save_date, cydir_mail_get_physical_size, /* physical = virtual in our case */ cydir_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, cydir_mail_get_stream, index_mail_get_binary_stream, index_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/cydir/cydir-storage.h0000644000175000017500000000167213165463624020512 00000000000000#ifndef CYDIR_STORAGE_H #define CYDIR_STORAGE_H #include "index-storage.h" #define CYDIR_STORAGE_NAME "cydir" #define CYDIR_SUBSCRIPTION_FILE_NAME "subscriptions." struct cydir_storage { struct mail_storage storage; }; struct cydir_mailbox { struct mailbox box; struct cydir_storage *storage; }; extern struct mail_vfuncs cydir_mail_vfuncs; struct mail_save_context * cydir_save_alloc(struct mailbox_transaction_context *_t); int cydir_save_begin(struct mail_save_context *ctx, struct istream *input); int cydir_save_continue(struct mail_save_context *ctx); int cydir_save_finish(struct mail_save_context *ctx); void cydir_save_cancel(struct mail_save_context *ctx); int cydir_transaction_save_commit_pre(struct mail_save_context *ctx); void cydir_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void cydir_transaction_save_rollback(struct mail_save_context *ctx); #endif dovecot-2.2.33.2/src/lib-storage/index/cydir/Makefile.am0000644000175000017500000000072013123174404017577 00000000000000noinst_LTLIBRARIES = libstorage_cydir.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_cydir_la_SOURCES = \ cydir-mail.c \ cydir-save.c \ cydir-sync.c \ cydir-storage.c headers = \ cydir-storage.h \ cydir-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-transaction.c0000644000175000017500000001472013165463624020247 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "dict.h" #include "index-storage.h" #include "index-sync-private.h" #include "index-pop3-uidl.h" #include "index-mail.h" static void index_transaction_free(struct mailbox_transaction_context *t) { if (t->view_pvt != NULL) mail_index_view_close(&t->view_pvt); mail_cache_view_close(&t->cache_view); mail_index_view_close(&t->view); if (array_is_created(&t->pvt_saves)) array_free(&t->pvt_saves); array_free(&t->module_contexts); i_free(t->reason); i_free(t); } static int index_transaction_index_commit(struct mail_index_transaction *index_trans, struct mail_index_transaction_commit_result *result_r) { struct mailbox_transaction_context *t = MAIL_STORAGE_CONTEXT(index_trans); struct index_mailbox_sync_pvt_context *pvt_sync_ctx = NULL; int ret = 0; index_pop3_uidl_update_exists_finish(t); if (t->nontransactional_changes) t->changes->changed = TRUE; if (t->attr_pvt_trans != NULL) { if (dict_transaction_commit(&t->attr_pvt_trans) < 0) { mail_storage_set_internal_error(t->box->storage); ret = -1; } } if (t->attr_shared_trans != NULL) { if (dict_transaction_commit(&t->attr_shared_trans) < 0) { mail_storage_set_internal_error(t->box->storage); ret = -1; } } if (t->save_ctx != NULL) { mailbox_save_context_deinit(t->save_ctx); if (ret < 0) { t->box->v.transaction_save_rollback(t->save_ctx); t->save_ctx = NULL; } else if (t->box->v.transaction_save_commit_pre(t->save_ctx) < 0) { t->save_ctx = NULL; ret = -1; } else { t->changes->changed = TRUE; } } if (array_is_created(&t->pvt_saves)) { if (index_mailbox_sync_pvt_init(t->box, TRUE, &pvt_sync_ctx) < 0) ret = -1; } i_assert(t->mail_ref_count == 0); if (ret < 0) t->super.rollback(index_trans); else { if (t->super.commit(index_trans, result_r) < 0) { mailbox_set_index_error(t->box); ret = -1; } else if (result_r->commit_size > 0) { /* something was written to the transaction log */ t->changes->changed = TRUE; } } if (t->save_ctx != NULL) { i_assert(t->save_ctx->dest_mail == NULL); t->box->v.transaction_save_commit_post(t->save_ctx, result_r); } if (pvt_sync_ctx != NULL) { if (index_mailbox_sync_pvt_newmails(pvt_sync_ctx, t) < 0) { /* failed to add private flags. a bit too late to return failure though, so just ignore silently */ } index_mailbox_sync_pvt_deinit(&pvt_sync_ctx); } index_transaction_free(t); return ret; } static void index_transaction_index_rollback(struct mail_index_transaction *index_trans) { struct mailbox_transaction_context *t = MAIL_STORAGE_CONTEXT(index_trans); if (t->attr_pvt_trans != NULL) dict_transaction_rollback(&t->attr_pvt_trans); if (t->attr_shared_trans != NULL) dict_transaction_rollback(&t->attr_shared_trans); if (t->save_ctx != NULL) { mailbox_save_context_deinit(t->save_ctx); t->box->v.transaction_save_rollback(t->save_ctx); } i_assert(t->mail_ref_count == 0); t->super.rollback(index_trans); index_transaction_free(t); } static enum mail_index_transaction_flags index_transaction_flags_get(enum mailbox_transaction_flags flags) { enum mail_index_transaction_flags itrans_flags; itrans_flags = MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES; if ((flags & MAILBOX_TRANSACTION_FLAG_HIDE) != 0) itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_HIDE; if ((flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0) itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; if ((flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0) itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_SYNC; return itrans_flags; } void index_transaction_init_pvt(struct mailbox_transaction_context *t) { enum mail_index_transaction_flags itrans_flags; if (t->box->view_pvt == NULL || t->itrans_pvt != NULL) return; itrans_flags = index_transaction_flags_get(t->flags); t->itrans_pvt = mail_index_transaction_begin(t->box->view_pvt, itrans_flags); t->view_pvt = mail_index_transaction_open_updated_view(t->itrans_pvt); } void index_transaction_init(struct mailbox_transaction_context *t, struct mailbox *box, enum mailbox_transaction_flags flags) { enum mail_index_transaction_flags itrans_flags; i_assert(box->opened); itrans_flags = index_transaction_flags_get(flags); if ((flags & MAILBOX_TRANSACTION_FLAG_REFRESH) != 0) mail_index_refresh(box->index); t->box = box; t->itrans = mail_index_transaction_begin(box->view, itrans_flags); t->view = mail_index_transaction_open_updated_view(t->itrans); array_create(&t->module_contexts, default_pool, sizeof(void *), 5); t->cache_view = mail_cache_view_open(box->cache, t->view); t->cache_trans = mail_cache_get_transaction(t->cache_view, t->itrans); if ((flags & MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC) != 0) mail_cache_view_update_cache_decisions(t->cache_view, FALSE); /* set up after mail_cache_get_transaction(), so that we'll still have the cache_trans available in _index_commit() */ t->super = t->itrans->v; t->itrans->v.commit = index_transaction_index_commit; t->itrans->v.rollback = index_transaction_index_rollback; MODULE_CONTEXT_SET(t->itrans, mail_storage_mail_index_module, t); } struct mailbox_transaction_context * index_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct mailbox_transaction_context *t; t = i_new(struct mailbox_transaction_context, 1); index_transaction_init(t, box, flags); return t; } int index_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct mailbox *box = t->box; struct mail_index_transaction *itrans = t->itrans; struct mail_index_transaction_commit_result result; int ret = 0; i_zero(changes_r); changes_r->pool = pool_alloconly_create(MEMPOOL_GROWING "transaction changes", 512); p_array_init(&changes_r->saved_uids, changes_r->pool, 32); t->changes = changes_r; if (t->itrans_pvt != NULL) ret = mail_index_transaction_commit(&t->itrans_pvt); if (mail_index_transaction_commit_full(&itrans, &result) < 0) ret = -1; t = NULL; if (ret < 0 && mail_index_is_deleted(box->index)) mailbox_set_deleted(box); changes_r->ignored_modseq_changes = result.ignored_modseq_changes; return ret; } void index_transaction_rollback(struct mailbox_transaction_context *t) { struct mail_index_transaction *itrans = t->itrans; if (t->itrans_pvt != NULL) mail_index_transaction_rollback(&t->itrans_pvt); mail_index_transaction_rollback(&itrans); } dovecot-2.2.33.2/src/lib-storage/index/index-attachment.c0000644000175000017500000003014713165463624020053 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "safe-mkstemp.h" #include "fs-api.h" #include "istream.h" #include "ostream.h" #include "base64.h" #include "hash-format.h" #include "str.h" #include "message-parser.h" #include "rfc822-parser.h" #include "fs-api.h" #include "istream-fs-file.h" #include "istream-attachment-connector.h" #include "istream-attachment-extractor.h" #include "mail-user.h" #include "index-mail.h" #include "index-attachment.h" enum mail_attachment_decode_option { MAIL_ATTACHMENT_DECODE_OPTION_NONE = '-', MAIL_ATTACHMENT_DECODE_OPTION_BASE64 = 'B', MAIL_ATTACHMENT_DECODE_OPTION_CRLF = 'C' }; struct mail_save_attachment { pool_t pool; struct fs *fs; struct istream *input; struct fs_file *cur_file; ARRAY_TYPE(mail_attachment_extref) extrefs; }; static const char *index_attachment_dir_get(struct mail_storage *storage) { return mail_user_home_expand(storage->user, storage->set->mail_attachment_dir); } static bool index_attachment_want(const struct istream_attachment_header *hdr, void *context) { struct mail_save_context *ctx = context; struct mail_attachment_part apart; i_zero(&apart); apart.part = hdr->part; apart.content_type = hdr->content_type; apart.content_disposition = hdr->content_disposition; if (ctx->part_is_attachment != NULL) return ctx->part_is_attachment(ctx, &apart); /* don't treat text/ parts as attachments */ return hdr->content_type != NULL && strncasecmp(hdr->content_type, "text/", 5) != 0; } static int index_attachment_open_temp_fd(void *context) { struct mail_save_context *ctx = context; struct mail_storage *storage = ctx->transaction->box->storage; string_t *temp_path; int fd; temp_path = t_str_new(256); mail_user_set_get_temp_prefix(temp_path, storage->user->set); fd = safe_mkstemp_hostpid(temp_path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { mail_storage_set_critical(storage, "safe_mkstemp(%s) failed: %m", str_c(temp_path)); return -1; } if (unlink(str_c(temp_path)) < 0) { mail_storage_set_critical(storage, "unlink(%s) failed: %m", str_c(temp_path)); i_close_fd(&fd); return -1; } return fd; } static int index_attachment_open_ostream(struct istream_attachment_info *info, struct ostream **output_r, const char **error_r ATTR_UNUSED, void *context) { struct mail_save_context *ctx = context; struct mail_save_attachment *attach = ctx->data.attach; struct mail_storage *storage = ctx->transaction->box->storage; struct mail_attachment_extref *extref; enum fs_open_flags flags = 0; const char *attachment_dir, *path, *digest = info->hash; guid_128_t guid_128; i_assert(attach->cur_file == NULL); if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) flags |= FS_OPEN_FLAG_FSYNC; if (strlen(digest) < 4) { /* make sure we can access first 4 bytes without accessing out of bounds memory */ digest = t_strconcat(digest, "\0\0\0\0", NULL); } guid_128_generate(guid_128); attachment_dir = index_attachment_dir_get(storage); path = t_strdup_printf("%s/%c%c/%c%c/%s-%s", attachment_dir, digest[0], digest[1], digest[2], digest[3], digest, guid_128_to_string(guid_128)); attach->cur_file = fs_file_init(attach->fs, path, FS_OPEN_MODE_REPLACE | flags); extref = array_append_space(&attach->extrefs); extref->start_offset = info->start_offset; extref->size = info->encoded_size; extref->path = p_strdup(attach->pool, path + strlen(attachment_dir) + 1); extref->base64_blocks_per_line = info->base64_blocks_per_line; extref->base64_have_crlf = info->base64_have_crlf; *output_r = fs_write_stream(attach->cur_file); return 0; } static int index_attachment_close_ostream(struct ostream *output, bool success, const char **error, void *context) { struct mail_save_context *ctx = context; struct mail_save_attachment *attach = ctx->data.attach; int ret = success ? 0 : -1; i_assert(attach->cur_file != NULL); if (ret < 0) fs_write_stream_abort_error(attach->cur_file, &output, "%s", *error); else if (fs_write_stream_finish(attach->cur_file, &output) < 0) { *error = t_strdup_printf("Couldn't create attachment %s: %s", fs_file_path(attach->cur_file), fs_file_last_error(attach->cur_file)); ret = -1; } fs_file_deinit(&attach->cur_file); if (ret < 0) { array_delete(&attach->extrefs, array_count(&attach->extrefs)-1, 1); } return ret; } void index_attachment_save_begin(struct mail_save_context *ctx, struct fs *fs, struct istream *input) { struct mail_storage *storage = ctx->transaction->box->storage; struct mail_save_attachment *attach; struct istream_attachment_settings set; const char *error; pool_t pool; i_assert(ctx->data.attach == NULL); if (*storage->set->mail_attachment_dir == '\0') return; i_zero(&set); set.min_size = storage->set->mail_attachment_min_size; if (hash_format_init(storage->set->mail_attachment_hash, &set.hash_format, &error) < 0) { /* we already checked this when verifying settings */ i_panic("mail_attachment_hash=%s unexpectedly failed: %s", storage->set->mail_attachment_hash, error); } set.want_attachment = index_attachment_want; set.open_temp_fd = index_attachment_open_temp_fd; set.open_attachment_ostream = index_attachment_open_ostream; set.close_attachment_ostream = index_attachment_close_ostream; pool = pool_alloconly_create("save attachment", 1024); attach = p_new(pool, struct mail_save_attachment, 1); attach->pool = pool; attach->fs = fs; attach->input = i_stream_create_attachment_extractor(input, &set, ctx); p_array_init(&attach->extrefs, attach->pool, 8); ctx->data.attach = attach; } static int save_check_write_error(struct mail_storage *storage, struct ostream *output) { if (output->last_failed_errno == 0) return 0; if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); } return -1; } int index_attachment_save_continue(struct mail_save_context *ctx) { struct mail_storage *storage = ctx->transaction->box->storage; struct mail_save_attachment *attach = ctx->data.attach; const unsigned char *data; size_t size; ssize_t ret; if (attach->input->stream_errno != 0) return -1; do { ret = i_stream_read(attach->input); if (ret > 0 || ret == -2) { data = i_stream_get_data(attach->input, &size); o_stream_nsend(ctx->data.output, data, size); i_stream_skip(attach->input, size); } index_mail_cache_parse_continue(ctx->dest_mail); if (ret == 0 && !i_stream_attachment_extractor_can_retry(attach->input)) { /* need more input */ return 0; } } while (ret != -1); if (attach->input->stream_errno != 0) { mail_storage_set_critical(storage, "read(%s) failed: %s", i_stream_get_name(attach->input), i_stream_get_error(attach->input)); return -1; } if (ctx->data.output != NULL) { if (save_check_write_error(storage, ctx->data.output) < 0) return -1; } return 0; } int index_attachment_save_finish(struct mail_save_context *ctx) { struct mail_save_attachment *attach = ctx->data.attach; (void)i_stream_read(attach->input); i_assert(attach->input->eof); return attach->input->stream_errno == 0 ? 0 : -1; } void index_attachment_save_free(struct mail_save_context *ctx) { struct mail_save_attachment *attach = ctx->data.attach; if (attach != NULL) { i_stream_unref(&attach->input); pool_unref(&attach->pool); ctx->data.attach = NULL; } } const ARRAY_TYPE(mail_attachment_extref) * index_attachment_save_get_extrefs(struct mail_save_context *ctx) { return ctx->data.attach == NULL ? NULL : &ctx->data.attach->extrefs; } static int index_attachment_delete_real(struct mail_storage *storage, struct fs *fs, const char *name) { struct fs_file *file; const char *path; int ret; path = t_strdup_printf("%s/%s", index_attachment_dir_get(storage), name); file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); if ((ret = fs_delete(file)) < 0) mail_storage_set_critical(storage, "%s", fs_last_error(fs)); fs_file_deinit(&file); return ret; } int index_attachment_delete(struct mail_storage *storage, struct fs *fs, const char *name) { int ret; T_BEGIN { ret = index_attachment_delete_real(storage, fs, name); } T_END; return ret; } void index_attachment_append_extrefs(string_t *str, const ARRAY_TYPE(mail_attachment_extref) *extrefs) { const struct mail_attachment_extref *extref; bool add_space = FALSE; unsigned int startpos; array_foreach(extrefs, extref) { if (!add_space) add_space = TRUE; else str_append_c(str, ' '); str_printfa(str, "%"PRIuUOFF_T" %"PRIuUOFF_T" ", extref->start_offset, extref->size); startpos = str_len(str); if (extref->base64_have_crlf) str_append_c(str, MAIL_ATTACHMENT_DECODE_OPTION_CRLF); if (extref->base64_blocks_per_line > 0) { str_printfa(str, "%c%u", MAIL_ATTACHMENT_DECODE_OPTION_BASE64, extref->base64_blocks_per_line * 4); } if (startpos == str_len(str)) { /* make it clear there are no options */ str_append_c(str, MAIL_ATTACHMENT_DECODE_OPTION_NONE); } str_append_c(str, ' '); str_append(str, extref->path); } } static bool parse_extref_decode_options(const char *str, struct mail_attachment_extref *extref) { unsigned int num; if (*str == MAIL_ATTACHMENT_DECODE_OPTION_NONE) return str[1] == '\0'; while (*str != '\0') { switch (*str) { case MAIL_ATTACHMENT_DECODE_OPTION_BASE64: str++; num = 0; while (*str >= '0' && *str <= '9') { num = num*10 + (*str-'0'); str++; } if (num == 0 || num % 4 != 0) return FALSE; extref->base64_blocks_per_line = num/4; break; case MAIL_ATTACHMENT_DECODE_OPTION_CRLF: extref->base64_have_crlf = TRUE; str++; break; default: return FALSE; } } return TRUE; } bool index_attachment_parse_extrefs(const char *line, pool_t pool, ARRAY_TYPE(mail_attachment_extref) *extrefs) { struct mail_attachment_extref extref; const char *const *args; unsigned int i, len; uoff_t last_voffset; args = t_strsplit(line, " "); len = str_array_length(args); if ((len % 4) != 0) return FALSE; last_voffset = 0; for (i = 0; args[i] != NULL; i += 4) { const char *start_offset_str = args[i+0]; const char *size_str = args[i+1]; const char *decode_options = args[i+2]; const char *path = args[i+3]; i_zero(&extref); if (str_to_uoff(start_offset_str, &extref.start_offset) < 0 || str_to_uoff(size_str, &extref.size) < 0 || extref.start_offset < last_voffset || !parse_extref_decode_options(decode_options, &extref)) return FALSE; last_voffset += extref.size + (extref.start_offset - last_voffset); extref.path = p_strdup(pool, path); array_append(extrefs, &extref, 1); } return TRUE; } int index_attachment_stream_get(struct fs *fs, const char *attachment_dir, const char *path_suffix, struct istream **stream, uoff_t full_size, const char *ext_refs, const char **error_r) { ARRAY_TYPE(mail_attachment_extref) extrefs_arr; const struct mail_attachment_extref *extref; struct istream_attachment_connector *conn; struct istream *input; struct fs_file *file; const char *path; int ret; *error_r = NULL; t_array_init(&extrefs_arr, 16); if (!index_attachment_parse_extrefs(ext_refs, pool_datastack_create(), &extrefs_arr)) { *error_r = "Broken ext-refs string"; return -1; } conn = istream_attachment_connector_begin(*stream, full_size); array_foreach(&extrefs_arr, extref) { path = t_strdup_printf("%s/%s%s", attachment_dir, extref->path, path_suffix); file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY | FS_OPEN_FLAG_SEEKABLE); input = i_stream_create_fs_file(&file, IO_BLOCK_SIZE); ret = istream_attachment_connector_add(conn, input, extref->start_offset, extref->size, extref->base64_blocks_per_line, extref->base64_have_crlf, error_r); i_stream_unref(&input); if (ret < 0) { istream_attachment_connector_abort(&conn); return -1; } } input = istream_attachment_connector_finish(&conn); i_stream_set_name(input, t_strdup_printf( "attachments-connector(%s)", i_stream_get_name(*stream))); i_stream_unref(stream); *stream = input; return 0; } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/0002755000175000017500000000000013172375611016745 500000000000000dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-storage.c0000644000175000017500000002661313165463624021441 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "abspath.h" #include "ioloop.h" #include "abspath.h" #include "fs-api.h" #include "mkdir-parents.h" #include "unlink-old-files.h" #include "mailbox-uidvalidity.h" #include "mailbox-list-private.h" #include "index-storage.h" #include "dbox-storage.h" #include #include #include #include void dbox_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_FS; if (set->subscription_fname == NULL) set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME; if (*set->maildir_name == '\0') set->maildir_name = DBOX_MAILDIR_NAME; if (*set->mailbox_dir_name == '\0') set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME; } static bool dbox_alt_path_has_changed(const char *root_dir, const char *alt_path, const char *alt_path2, const char *alt_symlink_path) { const char *linkpath; if (t_readlink(alt_symlink_path, &linkpath) < 0) { if (errno == ENOENT) return alt_path != NULL; i_error("readlink(%s) failed: %m", alt_symlink_path); return FALSE; } if (alt_path == NULL) { i_warning("dbox %s: Original ALT=%s, " "but currently no ALT path set", root_dir, linkpath); return TRUE; } else if (strcmp(linkpath, alt_path) != 0) { if (strcmp(linkpath, alt_path2) == 0) { /* FIXME: for backwards compatibility. old versions created the symlink to mailboxes/ directory, which was fine with sdbox, but didn't even exist with mdbox. we'll silently replace the symlink. */ return TRUE; } i_warning("dbox %s: Original ALT=%s, " "but currently ALT=%s", root_dir, linkpath, alt_path); return TRUE; } return FALSE; } static void dbox_verify_alt_path(struct mailbox_list *list) { const char *root_dir, *alt_symlink_path, *alt_path, *alt_path2; root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR); alt_symlink_path = t_strconcat(root_dir, "/"DBOX_ALT_SYMLINK_NAME, NULL); (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR, &alt_path); (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path2); if (!dbox_alt_path_has_changed(root_dir, alt_path, alt_path2, alt_symlink_path)) return; /* unlink/create the current alt path symlink */ i_unlink_if_exists(alt_symlink_path); if (alt_path != NULL) { if (symlink(alt_path, alt_symlink_path) < 0 && errno != EEXIST) { i_error("symlink(%s, %s) failed: %m", alt_path, alt_symlink_path); } } } int dbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct dbox_storage *storage = (struct dbox_storage *)_storage; const struct mail_storage_settings *set = _storage->set; const char *error; if (*set->mail_attachment_fs != '\0' && *set->mail_attachment_dir != '\0') { const char *name, *args, *dir; args = strpbrk(set->mail_attachment_fs, ": "); if (args == NULL) { name = set->mail_attachment_fs; args = ""; } else { name = t_strdup_until(set->mail_attachment_fs, args++); } if (strcmp(name, "sis-queue") == 0 && (_storage->class_flags & MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG) != 0) { /* FIXME: the deduplication part doesn't work, because sdbox renames the files.. */ *error_r = "mail_attachment_fs: " "sis-queue not currently supported by sdbox"; return -1; } dir = mail_user_home_expand(_storage->user, set->mail_attachment_dir); storage->attachment_dir = p_strdup(_storage->pool, dir); if (mailbox_list_init_fs(ns->list, name, args, storage->attachment_dir, &storage->attachment_fs, &error) < 0) { *error_r = t_strdup_printf("mail_attachment_fs: %s", error); return -1; } } if (!ns->list->set.alt_dir_nocheck) dbox_verify_alt_path(ns->list); return 0; } void dbox_storage_destroy(struct mail_storage *_storage) { struct dbox_storage *storage = (struct dbox_storage *)_storage; if (storage->attachment_fs != NULL) fs_deinit(&storage->attachment_fs); index_storage_destroy(_storage); } uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list) { const char *path; path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL); path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL); return mailbox_uidvalidity_next(list, path); } void dbox_notify_changes(struct mailbox *box) { const char *dir, *path; if (box->notify_callback == NULL) mailbox_watch_remove_all(box); else { if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &dir) <= 0) return; path = t_strdup_printf("%s/"MAIL_INDEX_PREFIX".log", dir); mailbox_watch_add(box, path); } } static bool dbox_cleanup_temp_files(struct mailbox_list *list, const char *path, time_t last_scan_time, time_t last_change_time) { unsigned int interval = list->mail_set->mail_temp_scan_interval; /* check once in a while if there are temp files to clean up */ if (interval == 0) { /* disabled */ return FALSE; } else if (last_scan_time >= ioloop_time - (time_t)interval) { /* not the time to scan it yet */ return FALSE; } else { bool stated = FALSE; if (last_change_time == (time_t)-1) { /* Don't know the ctime yet - look it up. */ struct stat st; if (stat(path, &st) < 0) { if (errno == ENOENT) i_error("stat(%s) failed: %m", path); return FALSE; } last_change_time = st.st_ctime; stated = TRUE; } if (last_scan_time > last_change_time + DBOX_TMP_DELETE_SECS) { /* there haven't been any changes to this directory since we last checked it. If we did an extra stat(), we need to update the last_scan_time to avoid stat()ing the next time. */ return stated; } const char *prefix = mailbox_list_get_global_temp_prefix(list); (void)unlink_old_files(path, prefix, ioloop_time - DBOX_TMP_DELETE_SECS); return TRUE; } return FALSE; } int dbox_mailbox_check_existence(struct mailbox *box, time_t *path_ctime_r) { const char *index_path, *box_path = mailbox_get_path(box); struct stat st; int ret = -1; *path_ctime_r = (time_t)-1; if (box->list->set.iter_from_index_dir) { /* Just because the index directory exists, it doesn't mean that the mailbox is selectable. Check that by seeing if dovecot.index.log exists. If it doesn't, fallback to checking for the dbox-Mails in the mail root directory. So this also means that if a mailbox is \NoSelect, listing it will always do a stat() for dbox-Mails in the mail root directory. That's not ideal, but this makes the behavior safer and \NoSelect mailboxes are somewhat rare. */ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &index_path) < 0) return -1; i_assert(index_path != NULL); index_path = t_strconcat(index_path, "/", box->index_prefix, ".log", NULL); ret = stat(index_path, &st); } if (ret < 0) { ret = stat(box_path, &st); if (ret == 0) *path_ctime_r = st.st_ctime; } if (ret == 0) { return 0; } else if (errno == ENOENT || errno == ENAMETOOLONG) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } else if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", mail_error_eacces_msg("stat", box_path)); return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", box_path); return -1; } } int dbox_mailbox_open(struct mailbox *box, time_t path_ctime) { const char *box_path = mailbox_get_path(box); if (index_storage_mailbox_open(box, FALSE) < 0) return -1; mail_index_set_fsync_mode(box->index, box->storage->set->parsed_fsync_mode, MAIL_INDEX_FSYNC_MASK_APPENDS | MAIL_INDEX_FSYNC_MASK_EXPUNGES); const struct mail_index_header *hdr = mail_index_get_header(box->view); if (dbox_cleanup_temp_files(box->list, box_path, hdr->last_temp_file_scan, path_ctime)) { /* temp files were scanned. update the last scan timestamp. */ index_mailbox_update_last_temp_file_scan(box); } return 0; } static int dir_is_empty(struct mail_storage *storage, const char *path) { DIR *dir; struct dirent *d; int ret = 1; dir = opendir(path); if (dir == NULL) { if (errno == ENOENT) { /* race condition with DELETE/RENAME? */ return 1; } mail_storage_set_critical(storage, "opendir(%s) failed: %m", path); return -1; } while ((d = readdir(dir)) != NULL) { if (*d->d_name == '.') continue; ret = 0; break; } if (closedir(dir) < 0) { mail_storage_set_critical(storage, "closedir(%s) failed: %m", path); ret = -1; } return ret; } int dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct dbox_storage *storage = (struct dbox_storage *)box->storage; const char *alt_path; struct stat st; int ret; if ((ret = index_storage_mailbox_create(box, directory)) <= 0) return ret; if (mailbox_open(box) < 0) return -1; if (mail_index_get_header(box->view)->uid_validity != 0) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } /* if alt path already exists and contains files, rebuild storage so that we don't start overwriting files. */ ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path); if (ret > 0 && stat(alt_path, &st) == 0) { ret = dir_is_empty(box->storage, alt_path); if (ret < 0) return -1; if (ret == 0) { mail_storage_set_critical(&storage->storage, "Mailbox %s has existing files in alt path, " "rebuilding storage to avoid losing messages", box->vname); storage->v.set_mailbox_corrupted(box); return -1; } /* dir is empty, ignore it */ } return dbox_mailbox_create_indexes(box, update); } int dbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update) { struct dbox_storage *storage = (struct dbox_storage *)box->storage; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; int ret; /* use syncing as a lock */ ret = mail_index_sync_begin(box->index, &sync_ctx, &view, &trans, 0); if (ret <= 0) { i_assert(ret != 0); mailbox_set_index_error(box); return -1; } if (mail_index_get_header(view)->uid_validity == 0) { if (storage->v.mailbox_create_indexes(box, update, trans) < 0) { mail_index_sync_rollback(&sync_ctx); return -1; } } return mail_index_sync_commit(&sync_ctx); } int dbox_verify_alt_storage(struct mailbox_list *list) { const char *alt_path; struct stat st; if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR, &alt_path)) return 0; /* make sure alt storage is mounted. if it's not, abort the rebuild. */ if (stat(alt_path, &st) == 0) return 0; if (errno != ENOENT) { i_error("stat(%s) failed: %m", alt_path); return -1; } /* try to create the alt directory. if it fails, it means alt storage isn't mounted. */ if (mailbox_list_mkdir_root(list, alt_path, MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) return -1; return 0; } bool dbox_header_have_flag(struct mailbox *box, uint32_t ext_id, unsigned int flags_offset, uint8_t flag) { const void *data; size_t data_size; uint8_t flags = 0; mail_index_get_header_ext(box->view, ext_id, &data, &data_size); if (flags_offset < data_size) flags = *((const uint8_t *)data + flags_offset); return (flags & flag) != 0; } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/Makefile.in0000644000175000017500000005571213172375573020751 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/dbox-common ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_dbox_common_la_LIBADD = am_libstorage_dbox_common_la_OBJECTS = dbox-attachment.lo dbox-file.lo \ dbox-file-fix.lo dbox-mail.lo dbox-save.lo dbox-storage.lo libstorage_dbox_common_la_OBJECTS = \ $(am_libstorage_dbox_common_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_dbox_common_la_SOURCES) DIST_SOURCES = $(libstorage_dbox_common_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_dbox_common.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_dbox_common_la_SOURCES = \ dbox-attachment.c \ dbox-file.c \ dbox-file-fix.c \ dbox-mail.c \ dbox-save.c \ dbox-storage.c headers = \ dbox-attachment.h \ dbox-file.h \ dbox-mail.h \ dbox-save.h \ dbox-storage.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/dbox-common/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/dbox-common/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_dbox_common.la: $(libstorage_dbox_common_la_OBJECTS) $(libstorage_dbox_common_la_DEPENDENCIES) $(EXTRA_libstorage_dbox_common_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_dbox_common_la_OBJECTS) $(libstorage_dbox_common_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-attachment.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-file-fix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-storage.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-save.h0000644000175000017500000000203713165463624020732 00000000000000#ifndef DBOX_SAVE_H #define DBOX_SAVE_H #include "dbox-storage.h" struct dbox_save_context { struct mail_save_context ctx; struct mail_index_transaction *trans; /* updated for each appended mail: */ uint32_t seq; struct istream *input; struct ostream *dbox_output; uint32_t highest_pop3_uidl_seq; unsigned int failed:1; unsigned int finished:1; unsigned int have_pop3_uidls:1; unsigned int have_pop3_orders:1; }; void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input); int dbox_save_continue(struct mail_save_context *_ctx); void dbox_save_end(struct dbox_save_context *ctx); void dbox_save_write_metadata(struct mail_save_context *ctx, struct ostream *output, uoff_t output_msg_size, const char *orig_mailbox_name, guid_128_t guid_128_r) ATTR_NULL(4); void dbox_save_add_to_index(struct dbox_save_context *ctx); void dbox_save_update_header_flags(struct dbox_save_context *ctx, struct mail_index_view *sync_view, uint32_t ext_id, unsigned int flags_offset); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-mail.c0000644000175000017500000002044113165463624020710 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "str.h" #include "index-storage.h" #include "index-mail.h" #include "index-pop3-uidl.h" #include "dbox-attachment.h" #include "dbox-storage.h" #include "dbox-file.h" #include "dbox-mail.h" struct mail * dbox_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct dbox_mail *mail; pool_t pool; pool = pool_alloconly_create("mail", 2048); mail = p_new(pool, struct dbox_mail, 1); mail->imail.mail.pool = pool; index_mail_init(&mail->imail, t, wanted_fields, wanted_headers); return &mail->imail.mail.mail; } void dbox_mail_close(struct mail *_mail) { struct dbox_mail *mail = (struct dbox_mail *)_mail; index_mail_close(_mail); /* close the dbox file only after index is closed, since it may still try to read from it. */ if (mail->open_file != NULL) dbox_file_unref(&mail->open_file); } int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r) { struct dbox_storage *storage = (struct dbox_storage *)mail->imail.mail.mail.box->storage; uoff_t offset; if (storage->v.mail_open(mail, &offset, file_r) < 0) return -1; if (dbox_file_seek(*file_r, offset) <= 0) return -1; if (dbox_file_metadata_read(*file_r) <= 0) return -1; if (mail->imail.data.stream != NULL) { /* we just messed up mail's input stream by reading metadata */ i_stream_seek((*file_r)->input, offset); i_stream_sync(mail->imail.data.stream); } return 0; } static int dbox_mail_metadata_get(struct dbox_mail *mail, enum dbox_metadata_key key, const char **value_r) { struct dbox_file *file; if (dbox_mail_metadata_read(mail, &file) < 0) return -1; *value_r = dbox_file_metadata_get(file, key); return 0; } int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct dbox_mail *mail = (struct dbox_mail *)_mail; struct index_mail_data *data = &mail->imail.data; struct dbox_file *file; if (index_mail_get_physical_size(_mail, size_r) == 0) return 0; if (dbox_mail_metadata_read(mail, &file) < 0) return -1; data->physical_size = dbox_file_get_plaintext_size(file); *size_r = data->physical_size; return 0; } int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) { struct dbox_mail *mail = (struct dbox_mail *)_mail; struct index_mail_data *data = &mail->imail.data; const char *value; uintmax_t size; if (index_mail_get_cached_virtual_size(&mail->imail, size_r)) return 0; if (dbox_mail_metadata_get(mail, DBOX_METADATA_VIRTUAL_SIZE, &value) < 0) return -1; if (value == NULL) return index_mail_get_virtual_size(_mail, size_r); if (str_to_uintmax_hex(value, &size) < 0 || size > (uoff_t)-1) return -1; data->virtual_size = (uoff_t)size; *size_r = data->virtual_size; return 0; } int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct dbox_mail *mail = (struct dbox_mail *)_mail; struct index_mail_data *data = &mail->imail.data; const char *value; uintmax_t time; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (dbox_mail_metadata_get(mail, DBOX_METADATA_RECEIVED_TIME, &value) < 0) return -1; time = 0; if (value != NULL && str_to_uintmax_hex(value, &time) < 0) return -1; data->received_date = (time_t)time; *date_r = data->received_date; return 0; } int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct dbox_storage *storage = (struct dbox_storage *)_mail->box->storage; struct dbox_mail *mail = (struct dbox_mail *)_mail; struct index_mail_data *data = &mail->imail.data; struct dbox_file *file; struct stat st; uoff_t offset; if (index_mail_get_save_date(_mail, date_r) == 0) return 0; if (storage->v.mail_open(mail, &offset, &file) < 0) return -1; _mail->transaction->stats.fstat_lookup_count++; if (dbox_file_stat(file, &st) < 0) { if (errno == ENOENT) mail_set_expunged(_mail); return -1; } *date_r = data->save_date = st.st_ctime; return 0; } static int dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key, enum index_cache_field cache_field, const char **value_r) { struct index_mail *imail = &mail->imail; struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(imail->mail.mail.box); const char *value; string_t *str; uint32_t order; str = str_new(imail->mail.data_pool, 64); if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view, str, imail->mail.mail.seq, ibox->cache_fields[cache_field].idx) > 0) { if (cache_field == MAIL_CACHE_POP3_ORDER) { i_assert(str_len(str) == sizeof(order)); memcpy(&order, str_data(str), sizeof(order)); str_truncate(str, 0); if (order != 0) str_printfa(str, "%u", order); else { /* order=0 means it doesn't exist. we don't want to return "0" though, because then the mails get ordered to beginning, while nonexistent are supposed to be ordered at the end. */ } } *value_r = str_c(str); return 0; } if (dbox_mail_metadata_get(mail, key, &value) < 0) return -1; if (value == NULL) value = ""; if (cache_field != MAIL_CACHE_POP3_ORDER) { index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, value, strlen(value)+1); } else { if (str_to_uint(value, &order) < 0) order = 0; index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, &order, sizeof(order)); } /* don't return pointer to dbox metadata directly, since it may change unexpectedly */ str_truncate(str, 0); str_append(str, value); *value_r = str_c(str); return 0; } int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct dbox_mail *mail = (struct dbox_mail *)_mail; int ret; /* keep the UIDL in cache file, otherwise POP3 would open all mail files and read the metadata. same for GUIDs if they're used. */ switch (field) { case MAIL_FETCH_UIDL_BACKEND: if (!index_pop3_uidl_can_exist(_mail)) { *value_r = ""; return 0; } ret = dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL, MAIL_CACHE_POP3_UIDL, value_r); if (ret == 0) { index_pop3_uidl_update_exists(&mail->imail.mail.mail, (*value_r)[0] != '\0'); } return ret; case MAIL_FETCH_POP3_ORDER: if (!index_pop3_uidl_can_exist(_mail)) { /* we're assuming that if there's a POP3 order, there's also a UIDL */ *value_r = ""; return 0; } return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_ORDER, MAIL_CACHE_POP3_ORDER, value_r); case MAIL_FETCH_GUID: return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID, MAIL_CACHE_GUID, value_r); default: break; } return index_mail_get_special(_mail, field, value_r); } static int get_mail_stream(struct dbox_mail *mail, uoff_t offset, struct istream **stream_r) { struct mail_private *pmail = &mail->imail.mail; struct dbox_file *file = mail->open_file; int ret; if ((ret = dbox_file_seek(file, offset)) <= 0) { *stream_r = NULL; return ret; } *stream_r = i_stream_create_limit(file->input, file->cur_physical_size); if (pmail->v.istream_opened != NULL) { if (pmail->v.istream_opened(&pmail->mail, stream_r) < 0) return -1; } if (file->storage->attachment_dir == NULL) return 1; else return dbox_attachment_file_get_stream(file, stream_r); } int dbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct dbox_storage *storage = (struct dbox_storage *)_mail->box->storage; struct dbox_mail *mail = (struct dbox_mail *)_mail; struct index_mail_data *data = &mail->imail.data; struct istream *input; uoff_t offset; int ret; if (data->stream == NULL) { if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0) return -1; ret = get_mail_stream(mail, offset, &input); if (ret <= 0) { if (ret < 0) return -1; dbox_file_set_corrupted(mail->open_file, "uid=%u points to broken data at offset=" "%"PRIuUOFF_T, _mail->uid, offset); if (input != NULL) i_stream_unref(&input); return -1; } data->stream = input; index_mail_set_read_buffer_size(_mail, input); } return index_mail_init_stream(&mail->imail, hdr_size, body_size, stream_r); } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-attachment.h0000644000175000017500000000061713123174404022113 00000000000000#ifndef DBOX_ATTACHMENT_H #define DBOX_ATTACHMENT_H #include "index-attachment.h" struct dbox_file; void dbox_attachment_save_write_metadata(struct mail_save_context *ctx, string_t *str); /* Build a single message body stream out of the current message and all of its attachments. */ int dbox_attachment_file_get_stream(struct dbox_file *file, struct istream **stream); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-storage.h0000644000175000017500000000575013165463624021445 00000000000000#ifndef DBOX_STORAGE_H #define DBOX_STORAGE_H #include "mail-storage-private.h" struct dbox_file; struct dbox_mail; struct dbox_storage; struct dbox_save_context; #define DBOX_SUBSCRIPTION_FILE_NAME "subscriptions" #define DBOX_UIDVALIDITY_FILE_NAME "dovecot-uidvalidity" #define DBOX_TEMP_FILE_PREFIX ".temp." #define DBOX_ALT_SYMLINK_NAME "dbox-alt-root" #define DBOX_MAILBOX_DIR_NAME "mailboxes" #define DBOX_TRASH_DIR_NAME "trash" #define DBOX_MAILDIR_NAME "dbox-Mails" /* Delete temp files having ctime older than this. */ #define DBOX_TMP_DELETE_SECS (36*60*60) /* Flag specifies if the message should be in primary or alternative storage */ #define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND enum dbox_index_header_flags { /* messages' metadata contain POP3 UIDLs */ DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS = 0x01, /* messages' metadata contain POP3 orders */ DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS = 0x02 }; struct dbox_storage_vfuncs { /* dbox file has zero references now. it should be either freed or left open in case it's accessed again soon */ void (*file_unrefed)(struct dbox_file *file); /* create a new file using the same permissions as file. if parents=TRUE, create the directory if necessary */ int (*file_create_fd)(struct dbox_file *file, const char *path, bool parents); /* open the mail and return its file/offset */ int (*mail_open)(struct dbox_mail *mail, uoff_t *offset_r, struct dbox_file **file_r); /* create/update mailbox indexes */ int (*mailbox_create_indexes)(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans); /* returns attachment path suffix. mdbox returns "", sdbox returns "--" */ const char *(*get_attachment_path_suffix)(struct dbox_file *file); /* mark the mailbox corrupted */ void (*set_mailbox_corrupted)(struct mailbox *box); /* mark the file corrupted */ void (*set_file_corrupted)(struct dbox_file *file); }; struct dbox_storage { struct mail_storage storage; struct dbox_storage_vfuncs v; struct fs *attachment_fs; const char *attachment_dir; }; void dbox_storage_get_list_settings(const struct mail_namespace *ns, struct mailbox_list_settings *set); int dbox_storage_create(struct mail_storage *storage, struct mail_namespace *ns, const char **error_r); void dbox_storage_destroy(struct mail_storage *storage); uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list); void dbox_notify_changes(struct mailbox *box); int dbox_mailbox_check_existence(struct mailbox *box, time_t *path_ctime_r); int dbox_mailbox_open(struct mailbox *box, time_t path_ctime); int dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory); int dbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update); int dbox_verify_alt_storage(struct mailbox_list *list); bool dbox_header_have_flag(struct mailbox *box, uint32_t ext_id, unsigned int flags_offset, uint8_t flag); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-attachment.c0000644000175000017500000000400713165463543022116 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "str.h" #include "dbox-file.h" #include "dbox-save.h" #include "dbox-attachment.h" void dbox_attachment_save_write_metadata(struct mail_save_context *ctx, string_t *str) { const ARRAY_TYPE(mail_attachment_extref) *extrefs; extrefs = index_attachment_save_get_extrefs(ctx); if (extrefs == NULL || array_count(extrefs) == 0) return; str_append_c(str, DBOX_METADATA_EXT_REF); index_attachment_append_extrefs(str, extrefs); str_append_c(str, '\n'); } static int dbox_attachment_file_get_stream_from(struct dbox_file *file, const char *ext_refs, struct istream **stream, const char **error_r) { const char *path_suffix; uoff_t msg_size; if (file->storage->attachment_dir == NULL) { mail_storage_set_critical(&file->storage->storage, "%s contains references to external attachments, " "but mail_attachment_dir is unset", file->cur_path); return -1; } msg_size = dbox_file_get_plaintext_size(file); path_suffix = file->storage->v.get_attachment_path_suffix(file); if (index_attachment_stream_get(file->storage->attachment_fs, file->storage->attachment_dir, path_suffix, stream, msg_size, ext_refs, error_r) < 0) return 0; return 1; } int dbox_attachment_file_get_stream(struct dbox_file *file, struct istream **stream) { const char *ext_refs, *error; int ret; /* need to read metadata in case there are external references */ if ((ret = dbox_file_metadata_read(file)) <= 0) return ret; i_stream_seek(file->input, file->cur_offset + file->msg_header_size); ext_refs = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF); if (ext_refs == NULL) return 1; /* we have external references. */ T_BEGIN { ret = dbox_attachment_file_get_stream_from(file, ext_refs, stream, &error); if (ret == 0) { dbox_file_set_corrupted(file, "Corrupted ext-refs metadata %s: %s", ext_refs, error); } } T_END; return ret; } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-file.h0000644000175000017500000001723313165463624020717 00000000000000#ifndef DBOX_FILE_H #define DBOX_FILE_H /* The file begins with a header followed by zero or more messages: Metadata block begins with DBOX_MAGIC_POST, followed by zero or more lines in format . The block ends with an empty line. Unknown metadata should be ignored, but preserved when copying. There should be no duplicates for the current metadata, but future extensions may need them so they should be preserved. */ #define DBOX_VERSION 2 #define DBOX_MAGIC_PRE "\001\002" #define DBOX_MAGIC_POST "\n\001\003\n" /* prefer flock(). fcntl() locking currently breaks if trying to access the same file from multiple mail_storages within same process. that's why we fallback to dotlocks. */ #ifdef HAVE_FLOCK # define DBOX_FILE_LOCK_METHOD_FLOCK #endif struct dbox_file; struct stat; enum dbox_header_key { /* Must be sizeof(struct dbox_message_header) when appending (hex) */ DBOX_HEADER_MSG_HEADER_SIZE = 'M', /* Creation UNIX timestamp (hex) */ DBOX_HEADER_CREATE_STAMP = 'C', /* metadata used by old Dovecot versions */ DBOX_HEADER_OLDV1_APPEND_OFFSET = 'A' }; /* NOTE: all valid keys are uppercase characters. if this changes, change dbox-file-fix.c:dbox_file_match_post_magic() to recognize them */ enum dbox_metadata_key { /* Globally unique identifier for the message. Preserved when copying. */ DBOX_METADATA_GUID = 'G', /* POP3 UIDL overriding the default format */ DBOX_METADATA_POP3_UIDL = 'P', /* POP3 message ordering (for migrated mails) */ DBOX_METADATA_POP3_ORDER = 'O', /* Received UNIX timestamp in hex */ DBOX_METADATA_RECEIVED_TIME = 'R', /* Physical message size in hex. Necessary only if it differs from the dbox_message_header.message_size_hex, for example because the message is compressed. */ DBOX_METADATA_PHYSICAL_SIZE = 'Z', /* Virtual message size in hex (line feeds counted as CRLF) */ DBOX_METADATA_VIRTUAL_SIZE = 'V', /* Pointer to external message data. Format is: 1*( ) */ DBOX_METADATA_EXT_REF = 'X', /* Mailbox name where this message was originally saved to. When rebuild finds a message whose mailbox is unknown, it's placed to this mailbox. */ DBOX_METADATA_ORIG_MAILBOX = 'B', /* metadata used by old Dovecot versions */ DBOX_METADATA_OLDV1_EXPUNGED = 'E', DBOX_METADATA_OLDV1_FLAGS = 'F', DBOX_METADATA_OLDV1_KEYWORDS = 'K', DBOX_METADATA_OLDV1_SAVE_TIME = 'S', DBOX_METADATA_OLDV1_SPACE = ' ' }; enum dbox_message_type { /* Normal message */ DBOX_MESSAGE_TYPE_NORMAL = 'N' }; struct dbox_message_header { unsigned char magic_pre[2]; unsigned char type; unsigned char space1; unsigned char oldv1_uid_hex[8]; unsigned char space2; unsigned char message_size_hex[16]; /* */ unsigned char save_lf; }; struct dbox_metadata_header { unsigned char magic_post[sizeof(DBOX_MAGIC_POST)-1]; }; struct dbox_file { struct dbox_storage *storage; int refcount; time_t create_time; unsigned int file_version; unsigned int file_header_size; unsigned int msg_header_size; const char *cur_path; char *primary_path, *alt_path; int fd; struct istream *input; #ifdef DBOX_FILE_LOCK_METHOD_FLOCK struct file_lock *lock; #else struct dotlock *lock; #endif uoff_t cur_offset; uoff_t cur_physical_size; /* Metadata for the currently seeked metadata block. */ pool_t metadata_pool; ARRAY(const char *) metadata; uoff_t metadata_read_offset; unsigned int appending:1; unsigned int corrupted:1; }; struct dbox_file_append_context { struct dbox_file *file; uoff_t first_append_offset, last_checkpoint_offset, last_flush_offset; struct ostream *output; }; #define dbox_file_is_open(file) ((file)->fd != -1) #define dbox_file_is_in_alt(file) ((file)->cur_path == (file)->alt_path) void dbox_file_init(struct dbox_file *file); void dbox_file_unref(struct dbox_file **file); /* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error. If file is deleted, deleted_r=TRUE and 1 is returned. */ int dbox_file_open(struct dbox_file *file, bool *deleted_r); /* Try to open file only from primary path. */ int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r); /* Close the file handle from the file, but don't free it. */ void dbox_file_close(struct dbox_file *file); /* fstat() or stat() the file. If file is already deleted, fails with errno=ENOENT. */ int dbox_file_stat(struct dbox_file *file, struct stat *st_r); /* Try to lock the dbox file. Returns 1 if ok, 0 if already locked by someone else, -1 if error. */ int dbox_file_try_lock(struct dbox_file *file); void dbox_file_unlock(struct dbox_file *file); /* Seek to given offset in file. Returns 1 if ok/expunged, 0 if file/offset is corrupted, -1 if I/O error. */ int dbox_file_seek(struct dbox_file *file, uoff_t offset); /* Start seeking at the beginning of the file. */ void dbox_file_seek_rewind(struct dbox_file *file); /* Seek to next message after current one. If there are no more messages, returns 0 and last_r is set to TRUE. Returns 1 if ok, 0 if file is corrupted, -1 if I/O error. */ int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r); /* Start appending to dbox file */ struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file); /* Finish writing appended mails. */ int dbox_file_append_commit(struct dbox_file_append_context **ctx); /* Truncate appended mails. */ void dbox_file_append_rollback(struct dbox_file_append_context **ctx); /* Get output stream for appending a new message. Returns 1 if ok, 0 if file can't be appended to (old file version or corruption) or -1 if error. */ int dbox_file_get_append_stream(struct dbox_file_append_context *ctx, struct ostream **output_r); /* Call after message has been fully saved. If this isn't done, the writes since the last checkpoint are truncated. */ void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx); /* Flush output buffer. */ int dbox_file_append_flush(struct dbox_file_append_context *ctx); /* Read current message's metadata. Returns 1 if ok, 0 if metadata is corrupted, -1 if I/O error. */ int dbox_file_metadata_read(struct dbox_file *file); /* Return wanted metadata value, or NULL if not found. */ const char *dbox_file_metadata_get(struct dbox_file *file, enum dbox_metadata_key key); /* Returns DBOX_METADATA_PHYSICAL_SIZE if set, otherwise physical size from header. They differ only for e.g. compressed mails. */ uoff_t dbox_file_get_plaintext_size(struct dbox_file *file); /* Fix a broken dbox file by rename()ing over it with a fixed file. Everything before start_offset is assumed to be valid and is simply copied. The file is reopened afterwards. Returns 1 if ok, 0 if the resulting file has no mails and was deleted, -1 if I/O error. */ int dbox_file_fix(struct dbox_file *file, uoff_t start_offset); /* Delete the given dbox file. Returns 1 if deleted, 0 if file wasn't found or -1 if error. */ int dbox_file_unlink(struct dbox_file *file); /* Fill dbox_message_header with given size. */ void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr, uoff_t message_size); void dbox_file_set_syscall_error(struct dbox_file *file, const char *function); void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...) ATTR_FORMAT(2, 3); /* private: */ const char *dbox_generate_tmp_filename(void); void dbox_file_free(struct dbox_file *file); int dbox_file_header_write(struct dbox_file *file, struct ostream *output); int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r); int dbox_file_metadata_skip_header(struct dbox_file *file); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-mail.h0000644000175000017500000000174713165463624020725 00000000000000#ifndef DBOX_MAIL_H #define DBOX_MAIL_H #include "index-mail.h" struct dbox_mail { struct index_mail imail; struct dbox_file *open_file; uoff_t offset; }; struct mail * dbox_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); void dbox_mail_close(struct mail *mail); int dbox_mail_get_physical_size(struct mail *mail, uoff_t *size_r); int dbox_mail_get_virtual_size(struct mail *mail, uoff_t *size_r); int dbox_mail_get_received_date(struct mail *mail, time_t *date_r); int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r); int dbox_mail_get_special(struct mail *mail, enum mail_fetch_field field, const char **value_r); int dbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r); int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-file.c0000644000175000017500000004567013165463624020720 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "hex-dec.h" #include "hex-binary.h" #include "hostpid.h" #include "istream.h" #include "ostream.h" #include "file-lock.h" #include "file-dotlock.h" #include "mkdir-parents.h" #include "eacces-error.h" #include "str.h" #include "dbox-storage.h" #include "dbox-file.h" #include #include #include #include #define DBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE #ifndef DBOX_FILE_LOCK_METHOD_FLOCK static const struct dotlock_settings dotlock_set = { .stale_timeout = 60*10, .use_excl_lock = TRUE }; #endif const char *dbox_generate_tmp_filename(void) { static unsigned int create_count = 0; return t_strdup_printf(DBOX_TEMP_FILE_PREFIX"%lu.P%sQ%uM%u.%s", (unsigned long)ioloop_timeval.tv_sec, my_pid, create_count++, (unsigned int)ioloop_timeval.tv_usec, my_hostname); } void dbox_file_set_syscall_error(struct dbox_file *file, const char *function) { mail_storage_set_critical(&file->storage->storage, "%s failed for file %s: %m", function, file->cur_path); } void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...) { va_list args; va_start(args, reason); mail_storage_set_critical(&file->storage->storage, "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s", file->cur_path, file->input == NULL ? 0 : file->input->v_offset, t_strdup_vprintf(reason, args)); va_end(args); file->storage->v.set_file_corrupted(file); } void dbox_file_init(struct dbox_file *file) { file->refcount = 1; file->fd = -1; file->cur_offset = (uoff_t)-1; file->cur_path = file->primary_path; } void dbox_file_free(struct dbox_file *file) { i_assert(file->refcount == 0); if (file->metadata_pool != NULL) pool_unref(&file->metadata_pool); dbox_file_close(file); i_free(file->primary_path); i_free(file->alt_path); i_free(file); } void dbox_file_unref(struct dbox_file **_file) { struct dbox_file *file = *_file; *_file = NULL; i_assert(file->refcount > 0); if (--file->refcount == 0) file->storage->v.file_unrefed(file); } static int dbox_file_parse_header(struct dbox_file *file, const char *line) { const char *const *tmp, *value; unsigned int pos; enum dbox_header_key key; file->file_version = *line - '0'; if (!i_isdigit(line[0]) || line[1] != ' ' || (file->file_version != 1 && file->file_version != DBOX_VERSION)) { dbox_file_set_corrupted(file, "Invalid dbox version"); return -1; } line += 2; pos = 2; file->msg_header_size = 0; for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) { uintmax_t time; key = **tmp; value = *tmp + 1; switch (key) { case DBOX_HEADER_OLDV1_APPEND_OFFSET: break; case DBOX_HEADER_MSG_HEADER_SIZE: if (str_to_uint_hex(value, &file->msg_header_size) < 0) { dbox_file_set_corrupted(file, "Invalid message header size"); return -1; } break; case DBOX_HEADER_CREATE_STAMP: if (str_to_uintmax_hex(value, &time) < 0) { dbox_file_set_corrupted(file, "Invalid create time stamp"); return -1; } file->create_time = (time_t)time; break; } pos += strlen(value) + 2; } if (file->msg_header_size == 0) { dbox_file_set_corrupted(file, "Missing message header size"); return -1; } return 0; } static int dbox_file_read_header(struct dbox_file *file) { const char *line; unsigned int hdr_size; int ret; i_stream_seek(file->input, 0); line = i_stream_read_next_line(file->input); if (line == NULL) { if (file->input->stream_errno == 0) { dbox_file_set_corrupted(file, "EOF while reading file header"); return 0; } dbox_file_set_syscall_error(file, "read()"); return -1; } hdr_size = file->input->v_offset; T_BEGIN { ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1; } T_END; if (ret > 0) file->file_header_size = hdr_size; return ret; } static int dbox_file_open_fd(struct dbox_file *file, bool try_altpath) { const char *path; int flags = O_RDWR; bool alt = FALSE; /* try the primary path first */ path = file->primary_path; while ((file->fd = open(path, flags)) == -1) { if (errno == EACCES && flags == O_RDWR) { flags = O_RDONLY; continue; } if (errno != ENOENT) { mail_storage_set_critical(&file->storage->storage, "open(%s) failed: %m", path); return -1; } if (file->alt_path == NULL || alt || !try_altpath) { /* not found */ return 0; } /* try the alternative path */ path = file->alt_path; alt = TRUE; } file->cur_path = path; return 1; } static int dbox_file_open_full(struct dbox_file *file, bool try_altpath, bool *notfound_r) { int ret, fd; *notfound_r = FALSE; if (file->input != NULL) return 1; if (file->fd == -1) { T_BEGIN { ret = dbox_file_open_fd(file, try_altpath); } T_END; if (ret <= 0) { if (ret < 0) return -1; *notfound_r = TRUE; return 1; } } /* we're manually checking at dbox_file_close() if we need to close the fd or not. */ fd = file->fd; file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE); i_stream_set_name(file->input, file->cur_path); i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE); return dbox_file_read_header(file); } int dbox_file_open(struct dbox_file *file, bool *deleted_r) { return dbox_file_open_full(file, TRUE, deleted_r); } int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r) { return dbox_file_open_full(file, FALSE, notfound_r); } int dbox_file_stat(struct dbox_file *file, struct stat *st_r) { const char *path; bool alt = FALSE; if (dbox_file_is_open(file)) { if (fstat(file->fd, st_r) < 0) { mail_storage_set_critical(&file->storage->storage, "fstat(%s) failed: %m", file->cur_path); return -1; } return 0; } /* try the primary path first */ path = file->primary_path; while (stat(path, st_r) < 0) { if (errno != ENOENT) { mail_storage_set_critical(&file->storage->storage, "stat(%s) failed: %m", path); return -1; } if (file->alt_path == NULL || alt) { /* not found */ return -1; } /* try the alternative path */ path = file->alt_path; alt = TRUE; } file->cur_path = path; return 0; } int dbox_file_header_write(struct dbox_file *file, struct ostream *output) { string_t *hdr; hdr = t_str_new(128); str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION, DBOX_HEADER_MSG_HEADER_SIZE, (unsigned int)sizeof(struct dbox_message_header), DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time); file->file_version = DBOX_VERSION; file->file_header_size = str_len(hdr); file->msg_header_size = sizeof(struct dbox_message_header); return o_stream_send(output, str_data(hdr), str_len(hdr)); } void dbox_file_close(struct dbox_file *file) { dbox_file_unlock(file); if (file->input != NULL) { /* stream autocloses the fd when it gets destroyed. note that the stream may outlive the struct dbox_file. */ i_stream_unref(&file->input); file->fd = -1; } else if (file->fd != -1) { if (close(file->fd) < 0) dbox_file_set_syscall_error(file, "close()"); file->fd = -1; } file->cur_offset = (uoff_t)-1; } int dbox_file_try_lock(struct dbox_file *file) { int ret; i_assert(file->fd != -1); #ifdef DBOX_FILE_LOCK_METHOD_FLOCK ret = file_try_lock(file->fd, file->cur_path, F_WRLCK, FILE_LOCK_METHOD_FLOCK, &file->lock); if (ret < 0) { mail_storage_set_critical(&file->storage->storage, "file_try_lock(%s) failed: %m", file->cur_path); } #else ret = file_dotlock_create(&dotlock_set, file->cur_path, DOTLOCK_CREATE_FLAG_NONBLOCK, &file->lock); if (ret < 0) { mail_storage_set_critical(&file->storage->storage, "file_dotlock_create(%s) failed: %m", file->cur_path); } #endif return ret; } void dbox_file_unlock(struct dbox_file *file) { i_assert(!file->appending || file->lock == NULL); if (file->lock != NULL) { #ifdef DBOX_FILE_LOCK_METHOD_FLOCK file_unlock(&file->lock); #else file_dotlock_delete(&file->lock); #endif } if (file->input != NULL) i_stream_sync(file->input); } int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r) { struct dbox_message_header hdr; const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(file->input, &data, &size, file->msg_header_size - 1); if (ret <= 0) { if (file->input->stream_errno == 0) { /* EOF, broken offset or file truncated */ dbox_file_set_corrupted(file, "EOF reading msg header " "(got %"PRIuSIZE_T"/%u bytes)", size, file->msg_header_size); return 0; } dbox_file_set_syscall_error(file, "read()"); return -1; } memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size)); if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) { /* probably broken offset */ dbox_file_set_corrupted(file, "msg header has bad magic value"); return 0; } if (data[file->msg_header_size-1] != '\n') { dbox_file_set_corrupted(file, "msg header doesn't end with LF"); return 0; } *physical_size_r = hex2dec(hdr.message_size_hex, sizeof(hdr.message_size_hex)); return 1; } int dbox_file_seek(struct dbox_file *file, uoff_t offset) { uoff_t size; int ret; i_assert(file->input != NULL); if (offset == 0) offset = file->file_header_size; if (offset != file->cur_offset) { i_stream_seek(file->input, offset); ret = dbox_file_read_mail_header(file, &size); if (ret <= 0) return ret; file->cur_offset = offset; file->cur_physical_size = size; } i_stream_seek(file->input, offset + file->msg_header_size); return 1; } static int dbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset) { const char *line; size_t buf_size; int ret; i_stream_seek(file->input, *offset); if ((ret = dbox_file_metadata_skip_header(file)) <= 0) return ret; /* skip over the actual metadata */ buf_size = i_stream_get_max_buffer_size(file->input); i_stream_set_max_buffer_size(file->input, (size_t)-1); while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') { /* end of metadata */ break; } } i_stream_set_max_buffer_size(file->input, buf_size); *offset = file->input->v_offset; return 1; } void dbox_file_seek_rewind(struct dbox_file *file) { file->cur_offset = (uoff_t)-1; } int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r) { uoff_t offset; int ret; i_assert(file->input != NULL); if (file->cur_offset == (uoff_t)-1) { /* first mail. we may not have read the file at all yet, so set the offset afterwards. */ offset = 0; } else { offset = file->cur_offset + file->msg_header_size + file->cur_physical_size; if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) { *offset_r = file->cur_offset; return ret; } if (i_stream_is_eof(file->input)) { *last_r = TRUE; return 0; } } *offset_r = offset; *last_r = FALSE; ret = dbox_file_seek(file, offset); if (*offset_r == 0) *offset_r = file->file_header_size; return ret; } struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file) { struct dbox_file_append_context *ctx; i_assert(!file->appending); file->appending = TRUE; ctx = i_new(struct dbox_file_append_context, 1); ctx->file = file; if (file->fd != -1) { ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE); o_stream_set_name(ctx->output, file->cur_path); o_stream_cork(ctx->output); } return ctx; } int dbox_file_append_commit(struct dbox_file_append_context **_ctx) { struct dbox_file_append_context *ctx = *_ctx; int ret; i_assert(ctx->file->appending); *_ctx = NULL; ret = dbox_file_append_flush(ctx); if (ctx->last_checkpoint_offset != ctx->output->offset) { o_stream_close(ctx->output); if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) { dbox_file_set_syscall_error(ctx->file, "ftruncate()"); return -1; } } o_stream_unref(&ctx->output); ctx->file->appending = FALSE; i_free(ctx); return ret; } void dbox_file_append_rollback(struct dbox_file_append_context **_ctx) { struct dbox_file_append_context *ctx = *_ctx; struct dbox_file *file = ctx->file; bool close_file = FALSE; i_assert(ctx->file->appending); *_ctx = NULL; if (ctx->first_append_offset == 0) { /* nothing changed */ } else if (ctx->first_append_offset == file->file_header_size) { /* rollbacking everything */ if (unlink(file->cur_path) < 0) dbox_file_set_syscall_error(file, "unlink()"); close_file = TRUE; } else { /* truncating only some mails */ o_stream_close(ctx->output); if (ftruncate(file->fd, ctx->first_append_offset) < 0) dbox_file_set_syscall_error(file, "ftruncate()"); } if (ctx->output != NULL) { o_stream_ignore_last_errors(ctx->output); o_stream_unref(&ctx->output); } i_free(ctx); if (close_file) dbox_file_close(file); file->appending = FALSE; } int dbox_file_append_flush(struct dbox_file_append_context *ctx) { struct mail_storage *storage = &ctx->file->storage->storage; if (ctx->last_flush_offset == ctx->output->offset && ctx->last_checkpoint_offset == ctx->output->offset) return 0; if (o_stream_nfinish(ctx->output) < 0) { dbox_file_set_syscall_error(ctx->file, "write()"); return -1; } if (ctx->last_checkpoint_offset != ctx->output->offset) { if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) { dbox_file_set_syscall_error(ctx->file, "ftruncate()"); return -1; } if (o_stream_seek(ctx->output, ctx->last_checkpoint_offset) < 0) { dbox_file_set_syscall_error(ctx->file, "lseek()"); return -1; } } if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync(ctx->file->fd) < 0) { dbox_file_set_syscall_error(ctx->file, "fdatasync()"); return -1; } } ctx->last_flush_offset = ctx->output->offset; return 0; } void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx) { ctx->last_checkpoint_offset = ctx->output->offset; } int dbox_file_get_append_stream(struct dbox_file_append_context *ctx, struct ostream **output_r) { struct dbox_file *file = ctx->file; struct stat st; if (ctx->output == NULL) { /* file creation had failed */ return -1; } if (ctx->last_checkpoint_offset != ctx->output->offset) { /* a message was aborted. don't try appending to this file anymore. */ return -1; } if (file->file_version == 0) { /* newly created file, write the file header */ if (dbox_file_header_write(file, ctx->output) < 0) { dbox_file_set_syscall_error(file, "write()"); return -1; } *output_r = ctx->output; return 1; } /* file has existing mails */ if (file->file_version != DBOX_VERSION || file->msg_header_size != sizeof(struct dbox_message_header)) { /* created by an incompatible version, can't append */ return 0; } if (ctx->output->offset == 0) { /* first append to existing file. seek to eof first. */ if (fstat(file->fd, &st) < 0) { dbox_file_set_syscall_error(file, "fstat()"); return -1; } if (st.st_size < file->msg_header_size) { dbox_file_set_corrupted(file, "dbox file size too small"); return 0; } if (o_stream_seek(ctx->output, st.st_size) < 0) { dbox_file_set_syscall_error(file, "lseek()"); return -1; } } *output_r = ctx->output; return 1; } int dbox_file_metadata_skip_header(struct dbox_file *file) { struct dbox_metadata_header metadata_hdr; const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(file->input, &data, &size, sizeof(metadata_hdr) - 1); if (ret <= 0) { if (file->input->stream_errno == 0) { /* EOF, broken offset */ dbox_file_set_corrupted(file, "Unexpected EOF while reading metadata header"); return 0; } dbox_file_set_syscall_error(file, "read()"); return -1; } memcpy(&metadata_hdr, data, sizeof(metadata_hdr)); if (memcmp(metadata_hdr.magic_post, DBOX_MAGIC_POST, sizeof(metadata_hdr.magic_post)) != 0) { /* probably broken offset */ dbox_file_set_corrupted(file, "metadata header has bad magic value"); return 0; } i_stream_skip(file->input, sizeof(metadata_hdr)); return 1; } static int dbox_file_metadata_read_at(struct dbox_file *file, uoff_t metadata_offset) { const char *line; size_t buf_size; int ret; if (file->metadata_pool != NULL) p_clear(file->metadata_pool); else { file->metadata_pool = pool_alloconly_create("dbox metadata", 1024); } p_array_init(&file->metadata, file->metadata_pool, 16); i_stream_seek(file->input, metadata_offset); if ((ret = dbox_file_metadata_skip_header(file)) <= 0) return ret; ret = 0; buf_size = i_stream_get_max_buffer_size(file->input); /* use unlimited line length for metadata */ i_stream_set_max_buffer_size(file->input, (size_t)-1); while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') { /* end of metadata */ ret = 1; break; } line = p_strdup(file->metadata_pool, line); array_append(&file->metadata, &line, 1); } i_stream_set_max_buffer_size(file->input, buf_size); if (ret == 0) dbox_file_set_corrupted(file, "missing end-of-metadata line"); return ret; } int dbox_file_metadata_read(struct dbox_file *file) { uoff_t metadata_offset; int ret; i_assert(file->cur_offset != (uoff_t)-1); if (file->metadata_read_offset == file->cur_offset) return 1; metadata_offset = file->cur_offset + file->msg_header_size + file->cur_physical_size; ret = dbox_file_metadata_read_at(file, metadata_offset); if (ret <= 0) return ret; file->metadata_read_offset = file->cur_offset; return 1; } const char *dbox_file_metadata_get(struct dbox_file *file, enum dbox_metadata_key key) { const char *const *metadata; unsigned int i, count; metadata = array_get(&file->metadata, &count); for (i = 0; i < count; i++) { if (*metadata[i] == (char)key) return metadata[i] + 1; } return NULL; } uoff_t dbox_file_get_plaintext_size(struct dbox_file *file) { const char *value; uintmax_t size; i_assert(file->metadata_read_offset == file->cur_offset); /* see if we have it in metadata */ value = dbox_file_metadata_get(file, DBOX_METADATA_PHYSICAL_SIZE); if (value == NULL || str_to_uintmax_hex(value, &size) < 0 || size > (uoff_t)-1) { /* no. that means we can use the size in the header */ return file->cur_physical_size; } return (uoff_t)size; } void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr, uoff_t message_size) { memset(dbox_msg_hdr, ' ', sizeof(*dbox_msg_hdr)); memcpy(dbox_msg_hdr->magic_pre, DBOX_MAGIC_PRE, sizeof(dbox_msg_hdr->magic_pre)); dbox_msg_hdr->type = DBOX_MESSAGE_TYPE_NORMAL; dec2hex(dbox_msg_hdr->message_size_hex, message_size, sizeof(dbox_msg_hdr->message_size_hex)); dbox_msg_hdr->save_lf = '\n'; } int dbox_file_unlink(struct dbox_file *file) { const char *path; bool alt = FALSE; path = file->primary_path; while (unlink(path) < 0) { if (errno != ENOENT) { mail_storage_set_critical(&file->storage->storage, "unlink(%s) failed: %m", path); return -1; } if (file->alt_path == NULL || alt) { /* not found */ return 0; } /* try the alternative path */ path = file->alt_path; alt = TRUE; } return 1; } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-save.c0000644000175000017500000001563713165463624020737 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "str.h" #include "hex-binary.h" #include "index-mail.h" #include "dbox-attachment.h" #include "dbox-file.h" #include "dbox-save.h" void dbox_save_add_to_index(struct dbox_save_context *ctx) { struct mail_save_data *mdata = &ctx->ctx.data; enum mail_flags save_flags; if ((ctx->ctx.transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0) mail_index_append(ctx->trans, mdata->uid, &ctx->seq); else ctx->seq = mdata->stub_seq; save_flags = mdata->flags & ~MAIL_RECENT; mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, save_flags); if (mdata->keywords != NULL) { mail_index_update_keywords(ctx->trans, ctx->seq, MODIFY_REPLACE, mdata->keywords); } if (mdata->min_modseq != 0) { mail_index_update_modseq(ctx->trans, ctx->seq, mdata->min_modseq); } } void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input) { struct mail_save_context *_ctx = &ctx->ctx; struct mail_storage *_storage = _ctx->transaction->box->storage; struct dbox_storage *storage = (struct dbox_storage *)_storage; struct dbox_message_header dbox_msg_hdr; struct istream *crlf_input; dbox_save_add_to_index(ctx); mail_set_seq_saving(_ctx->dest_mail, ctx->seq); crlf_input = i_stream_create_lf(input); ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input); i_stream_unref(&crlf_input); /* write a dummy header. it'll get rewritten when we're finished */ i_zero(&dbox_msg_hdr); o_stream_cork(ctx->dbox_output); if (o_stream_send(ctx->dbox_output, &dbox_msg_hdr, sizeof(dbox_msg_hdr)) < 0) { mail_storage_set_critical(_storage, "write(%s) failed: %m", o_stream_get_name(ctx->dbox_output)); ctx->failed = TRUE; } _ctx->data.output = ctx->dbox_output; if (_ctx->data.received_date == (time_t)-1) _ctx->data.received_date = ioloop_time; index_attachment_save_begin(_ctx, storage->attachment_fs, ctx->input); } int dbox_save_continue(struct mail_save_context *_ctx) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; if (ctx->failed) return -1; if (_ctx->data.attach != NULL) return index_attachment_save_continue(_ctx); do { if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %m", o_stream_get_name(_ctx->data.output)); } ctx->failed = TRUE; return -1; } index_mail_cache_parse_continue(_ctx->dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; } void dbox_save_end(struct dbox_save_context *ctx) { struct mail_save_data *mdata = &ctx->ctx.data; struct ostream *dbox_output = ctx->dbox_output; i_assert(mdata->output != NULL); if (mdata->attach != NULL && !ctx->failed) { if (index_attachment_save_finish(&ctx->ctx) < 0) ctx->failed = TRUE; } if (o_stream_nfinish(mdata->output) < 0) { mail_storage_set_critical(ctx->ctx.transaction->box->storage, "write(%s) failed: %m", o_stream_get_name(mdata->output)); ctx->failed = TRUE; } if (mdata->output != dbox_output) { /* e.g. zlib plugin had changed this */ o_stream_ref(dbox_output); o_stream_destroy(&mdata->output); mdata->output = dbox_output; } index_mail_cache_parse_deinit(ctx->ctx.dest_mail, ctx->ctx.data.received_date, !ctx->failed); } void dbox_save_write_metadata(struct mail_save_context *_ctx, struct ostream *output, uoff_t output_msg_size, const char *orig_mailbox_name, guid_128_t guid_128) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; struct mail_save_data *mdata = &ctx->ctx.data; struct dbox_metadata_header metadata_hdr; const char *guid; string_t *str; uoff_t vsize; i_zero(&metadata_hdr); memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST, sizeof(metadata_hdr.magic_post)); o_stream_nsend(output, &metadata_hdr, sizeof(metadata_hdr)); str = t_str_new(256); if (output_msg_size != ctx->input->v_offset) { /* a plugin changed the data written to disk, so the "message size" dbox header doesn't contain the actual "physical" message size. we need to save it as a separate metadata header. */ str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE, (unsigned long long)ctx->input->v_offset); } str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME, (unsigned long)mdata->received_date); if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0) i_unreached(); str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE, (unsigned long long)vsize); if (mdata->pop3_uidl != NULL) { i_assert(strchr(mdata->pop3_uidl, '\n') == NULL); str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL, mdata->pop3_uidl); ctx->have_pop3_uidls = TRUE; ctx->highest_pop3_uidl_seq = I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq); } if (mdata->pop3_order != 0) { str_printfa(str, "%c%u\n", DBOX_METADATA_POP3_ORDER, mdata->pop3_order); ctx->have_pop3_orders = TRUE; ctx->highest_pop3_uidl_seq = I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq); } guid = mdata->guid; if (guid != NULL) mail_generate_guid_128_hash(guid, guid_128); else { guid_128_generate(guid_128); guid = guid_128_to_string(guid_128); } str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid); if (orig_mailbox_name != NULL && strchr(orig_mailbox_name, '\r') == NULL && strchr(orig_mailbox_name, '\n') == NULL) { /* save the original mailbox name so if mailbox indexes get corrupted we can place at least some (hopefully most) of the messages to correct mailboxes. */ str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX, orig_mailbox_name); } dbox_attachment_save_write_metadata(_ctx, str); str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } void dbox_save_update_header_flags(struct dbox_save_context *ctx, struct mail_index_view *sync_view, uint32_t ext_id, unsigned int flags_offset) { const void *data; size_t data_size; uint8_t old_flags = 0, flags; mail_index_get_header_ext(sync_view, ext_id, &data, &data_size); if (flags_offset < data_size) old_flags = *((const uint8_t *)data + flags_offset); else { /* grow old dbox header */ mail_index_ext_resize_hdr(ctx->trans, ext_id, flags_offset+1); } flags = old_flags; if (ctx->have_pop3_uidls) flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS; if (ctx->have_pop3_orders) flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS; if (flags != old_flags) { /* flags changed, update them */ mail_index_update_header_ext(ctx->trans, ext_id, flags_offset, &flags, 1); } } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/dbox-file-fix.c0000644000175000017500000003330613165463624021475 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hex-dec.h" #include "istream.h" #include "ostream.h" #include "message-size.h" #include "dbox-storage.h" #include "dbox-file.h" #include #define DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX ".broken" static int dbox_file_match_pre_magic(struct istream *input, uoff_t *pre_offset, size_t *need_bytes) { const struct dbox_message_header *hdr; const unsigned char *data; size_t size; uoff_t offset = input->v_offset; bool have_lf = FALSE; data = i_stream_get_data(input, &size); if (data[0] == '\n') { data++; size--; offset++; have_lf = TRUE; } i_assert(data[0] == DBOX_MAGIC_PRE[0]); if (size < sizeof(*hdr)) { *need_bytes = sizeof(*hdr) + (have_lf ? 1 : 0); return -1; } hdr = (const void *)data; if (memcmp(hdr->magic_pre, DBOX_MAGIC_PRE, strlen(DBOX_MAGIC_PRE)) != 0) return 0; if (hdr->type != DBOX_MESSAGE_TYPE_NORMAL) return 0; if (hdr->space1 != ' ' || hdr->space2 != ' ') return 0; if (hex2dec(hdr->message_size_hex, sizeof(hdr->message_size_hex)) == 0 && memcmp(hdr->message_size_hex, "0000000000000000", sizeof(hdr->message_size_hex)) != 0) return 0; *pre_offset = offset; return 1; } static bool memchr_nocontrol(const unsigned char *data, char chr, unsigned int len, const unsigned char **pos_r) { unsigned int i; for (i = 0; i < len; i++) { if (data[i] == chr) { *pos_r = data+i; return TRUE; } if (data[i] < ' ') return FALSE; } *pos_r = NULL; return TRUE; } static int dbox_file_match_post_magic(struct istream *input, bool input_full, size_t *need_bytes) { const unsigned char *data, *p; size_t i, size; bool allow_control; data = i_stream_get_data(input, &size); if (size < strlen(DBOX_MAGIC_POST)) { *need_bytes = strlen(DBOX_MAGIC_POST); return -1; } if (memcmp(data, DBOX_MAGIC_POST, strlen(DBOX_MAGIC_POST)) != 0) return 0; /* see if the metadata block looks valid */ for (i = strlen(DBOX_MAGIC_POST); i < size; ) { switch (data[i]) { case '\n': return 1; case DBOX_METADATA_GUID: case DBOX_METADATA_POP3_UIDL: case DBOX_METADATA_ORIG_MAILBOX: case DBOX_METADATA_OLDV1_KEYWORDS: /* these could contain anything */ allow_control = TRUE; break; case DBOX_METADATA_POP3_ORDER: case DBOX_METADATA_RECEIVED_TIME: case DBOX_METADATA_PHYSICAL_SIZE: case DBOX_METADATA_VIRTUAL_SIZE: case DBOX_METADATA_EXT_REF: case DBOX_METADATA_OLDV1_EXPUNGED: case DBOX_METADATA_OLDV1_FLAGS: case DBOX_METADATA_OLDV1_SAVE_TIME: case DBOX_METADATA_OLDV1_SPACE: /* no control chars */ allow_control = FALSE; break; default: if (data[i] < 'A' || data[i] > 'Z') return 0; /* unknown */ allow_control = TRUE; break; } if (allow_control) { p = memchr(data+i, '\n', size-i); } else { if (!memchr_nocontrol(data+i, '\n', size-i, &p)) return 0; } if (p == NULL) { /* LF not found - try to find the end-of-metadata LF */ if (input_full) { /* can't look any further - assume it's ok */ return 1; } *need_bytes = size+1; return -1; } i = p - data+1; } *need_bytes = size+1; return -1; } static int dbox_file_find_next_magic(struct dbox_file *file, uoff_t *offset_r, bool *pre_r) { /* We're scanning message bodies here, trying to find the beginning of the next message. Although our magic strings are very unlikely to be found in regular emails, they are much more likely when emails are stored compressed.. So try to be sure we find the correct magic markers. */ struct istream *input = file->input; uoff_t orig_offset, pre_offset, post_offset, prev_offset; const unsigned char *data, *magic; size_t size, need_bytes, prev_need_bytes; int ret, match; *pre_r = FALSE; orig_offset = prev_offset = input->v_offset; need_bytes = strlen(DBOX_MAGIC_POST); prev_need_bytes = 0; while ((ret = i_stream_read_data(input, &data, &size, need_bytes-1)) > 0 || ret == -2) { /* search for the beginning of a potential pre/post magic */ i_assert(size > 1); i_assert(prev_offset != input->v_offset || need_bytes > prev_need_bytes); prev_offset = input->v_offset; prev_need_bytes = need_bytes; magic = memchr(data, DBOX_MAGIC_PRE[0], size); if (magic == NULL) { i_stream_skip(input, size-1); need_bytes = strlen(DBOX_MAGIC_POST); continue; } if (magic == data && input->v_offset == orig_offset) { /* beginning of the file */ } else if (magic != data && magic[-1] == '\n') { /* PRE/POST block? leave \n */ i_stream_skip(input, magic-data-1); } else { i_stream_skip(input, magic-data+1); need_bytes = strlen(DBOX_MAGIC_POST); continue; } pre_offset = (uoff_t)-1; match = dbox_file_match_pre_magic(input, &pre_offset, &need_bytes); if (match < 0) { /* more data needed */ if (ret == -2) { i_stream_skip(input, 2); need_bytes = strlen(DBOX_MAGIC_POST); } continue; } if (match > 0) *pre_r = TRUE; match = dbox_file_match_post_magic(input, ret == -2, &need_bytes); if (match < 0) { /* more data needed */ if (ret == -2) { i_stream_skip(input, 2); need_bytes = strlen(DBOX_MAGIC_POST); } continue; } if (match > 0) { post_offset = input->v_offset; if (pre_offset == (uoff_t)-1 || post_offset < pre_offset) { pre_offset = post_offset; *pre_r = FALSE; } } if (pre_offset != (uoff_t)-1) { *offset_r = pre_offset; ret = 1; break; } i_stream_skip(input, size-1); } if (ret <= 0) { i_assert(ret == -1); if (input->stream_errno != 0) dbox_file_set_syscall_error(file, "read()"); else { ret = 0; *offset_r = input->v_offset; } } i_stream_seek(input, orig_offset); return ret <= 0 ? ret : 1; } static int stream_copy(struct dbox_file *file, struct ostream *output, const char *out_path, uoff_t count) { struct istream *input; off_t bytes; input = i_stream_create_limit(file->input, count); bytes = o_stream_send_istream(output, input); errno = input->stream_errno; i_stream_unref(&input); if (errno != 0) { mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %m", file->cur_path); return -1; } if (o_stream_nfinish(output) < 0) { mail_storage_set_critical(&file->storage->storage, "write(%s) failed: %m", out_path); return -1; } i_assert(bytes >= 0); if ((uoff_t)bytes != count) { mail_storage_set_critical(&file->storage->storage, "o_stream_send_istream(%s) copied only %" PRIuUOFF_T" of %"PRIuUOFF_T" bytes", out_path, bytes, count); return -1; } return 0; } static void dbox_file_skip_broken_header(struct dbox_file *file) { const size_t magic_len = strlen(DBOX_MAGIC_PRE); const unsigned char *data; size_t i, size; /* if there's LF close to our position, assume that the header ends there. */ data = i_stream_get_data(file->input, &size); if (size > file->msg_header_size + 16) size = file->msg_header_size + 16; for (i = 0; i < size; i++) { if (data[i] == '\n') { i_stream_skip(file->input, i); return; } } /* skip at least the magic bytes if possible */ if (size > magic_len && memcmp(data, DBOX_MAGIC_PRE, magic_len) == 0) i_stream_skip(file->input, magic_len); } static void dbox_file_copy_metadata(struct dbox_file *file, struct ostream *output, bool *have_guid_r) { const char *line; uoff_t prev_offset = file->input->v_offset; *have_guid_r = FALSE; while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') { /* end of metadata */ return; } if (*line < 32) { /* broken - possibly a new pre-magic block */ i_stream_seek(file->input, prev_offset); return; } if (*line == DBOX_METADATA_VIRTUAL_SIZE) { /* it may be wrong - recreate it */ continue; } if (*line == DBOX_METADATA_GUID) *have_guid_r = TRUE; o_stream_nsend_str(output, line); o_stream_nsend_str(output, "\n"); } } static int dbox_file_fix_write_stream(struct dbox_file *file, uoff_t start_offset, const char *temp_path, struct ostream *output) { struct dbox_message_header msg_hdr; uoff_t offset, msg_size, hdr_offset, body_offset; bool pre, write_header, have_guid; struct message_size body; bool has_nuls; struct istream *body_input; guid_128_t guid_128; int ret; i_stream_seek(file->input, 0); if (start_offset > 0) { /* copy the valid data */ if (stream_copy(file, output, temp_path, start_offset) < 0) return -1; } else { /* the file header is broken. recreate it */ if (dbox_file_header_write(file, output) < 0) { dbox_file_set_syscall_error(file, "write()"); return -1; } } while ((ret = dbox_file_find_next_magic(file, &offset, &pre)) > 0) { msg_size = offset - file->input->v_offset; if (msg_size < 256 && pre) { /* probably some garbage or some broken headers. we most likely don't miss anything by skipping over this data. */ i_stream_skip(file->input, msg_size); hdr_offset = file->input->v_offset; ret = dbox_file_read_mail_header(file, &msg_size); if (ret <= 0) { if (ret < 0) return -1; dbox_file_skip_broken_header(file); body_offset = file->input->v_offset; msg_size = (uoff_t)-1; } else { i_stream_skip(file->input, file->msg_header_size); body_offset = file->input->v_offset; i_stream_skip(file->input, msg_size); } ret = dbox_file_find_next_magic(file, &offset, &pre); if (ret <= 0) break; if (!pre && msg_size == offset - body_offset) { /* msg header ok, copy it */ i_stream_seek(file->input, hdr_offset); if (stream_copy(file, output, temp_path, file->msg_header_size) < 0) return -1; write_header = FALSE; } else { /* msg header is broken. write our own. */ i_stream_seek(file->input, body_offset); if (msg_size != (uoff_t)-1) { /* previous magic find might have skipped too much. seek back and make sure */ ret = dbox_file_find_next_magic(file, &offset, &pre); if (ret <= 0) break; } write_header = TRUE; msg_size = offset - body_offset; } } else { /* treat this data as a separate message. */ write_header = TRUE; body_offset = file->input->v_offset; } /* write msg header */ if (write_header) { dbox_msg_header_fill(&msg_hdr, msg_size); o_stream_nsend(output, &msg_hdr, sizeof(msg_hdr)); } /* write msg body */ i_assert(file->input->v_offset == body_offset); if (stream_copy(file, output, temp_path, msg_size) < 0) return -1; i_assert(file->input->v_offset == offset); /* get message body size */ i_stream_seek(file->input, body_offset); body_input = i_stream_create_limit(file->input, msg_size); ret = message_get_body_size(body_input, &body, &has_nuls); i_stream_unref(&body_input); if (ret < 0) { errno = body_input->stream_errno; mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %m", file->cur_path); return -1; } /* write msg metadata. */ i_assert(file->input->v_offset == offset); ret = dbox_file_metadata_skip_header(file); if (ret < 0) return -1; o_stream_nsend_str(output, DBOX_MAGIC_POST); if (ret == 0) have_guid = FALSE; else dbox_file_copy_metadata(file, output, &have_guid); if (!have_guid) { guid_128_generate(guid_128); o_stream_nsend_str(output, t_strdup_printf("%c%s\n", DBOX_METADATA_GUID, guid_128_to_string(guid_128))); } o_stream_nsend_str(output, t_strdup_printf("%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE, (unsigned long long)body.virtual_size)); o_stream_nsend_str(output, "\n"); if (output->stream_errno != 0) break; } if (o_stream_nfinish(output) < 0) { mail_storage_set_critical(&file->storage->storage, "write(%s) failed: %m", temp_path); ret = -1; } return ret; } int dbox_file_fix(struct dbox_file *file, uoff_t start_offset) { struct ostream *output; const char *dir, *p, *temp_path, *broken_path; bool deleted, have_messages; int fd, ret; i_assert(dbox_file_is_open(file)); p = strrchr(file->cur_path, '/'); i_assert(p != NULL); dir = t_strdup_until(file->cur_path, p); temp_path = t_strdup_printf("%s/%s", dir, dbox_generate_tmp_filename()); fd = file->storage->v.file_create_fd(file, temp_path, FALSE); if (fd == -1) return -1; output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_cork(output); ret = dbox_file_fix_write_stream(file, start_offset, temp_path, output); if (ret < 0) o_stream_ignore_last_errors(output); have_messages = output->offset > file->file_header_size; o_stream_unref(&output); if (close(fd) < 0) { mail_storage_set_critical(&file->storage->storage, "close(%s) failed: %m", temp_path); ret = -1; } if (ret < 0) { if (unlink(temp_path) < 0) { mail_storage_set_critical(&file->storage->storage, "unlink(%s) failed: %m", temp_path); } return -1; } /* keep a copy of the original file in case someone wants to look at it */ broken_path = t_strconcat(file->cur_path, DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX, NULL); if (link(file->cur_path, broken_path) < 0) { mail_storage_set_critical(&file->storage->storage, "link(%s, %s) failed: %m", file->cur_path, broken_path); } else { i_warning("dbox: Copy of the broken file saved to %s", broken_path); } if (!have_messages) { /* the resulting file has no messages. just delete the file. */ dbox_file_close(file); i_unlink(temp_path); i_unlink(file->cur_path); return 0; } if (rename(temp_path, file->cur_path) < 0) { mail_storage_set_critical(&file->storage->storage, "rename(%s, %s) failed: %m", temp_path, file->cur_path); return -1; } /* file was successfully recreated - reopen it */ dbox_file_close(file); if (dbox_file_open(file, &deleted) <= 0) { mail_storage_set_critical(&file->storage->storage, "dbox_file_fix(%s): reopening file failed", file->cur_path); return -1; } return 1; } dovecot-2.2.33.2/src/lib-storage/index/dbox-common/Makefile.am0000644000175000017500000000116313123174404020711 00000000000000noinst_LTLIBRARIES = libstorage_dbox_common.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_dbox_common_la_SOURCES = \ dbox-attachment.c \ dbox-file.c \ dbox-file-fix.c \ dbox-mail.c \ dbox-save.c \ dbox-storage.c headers = \ dbox-attachment.h \ dbox-file.h \ dbox-mail.h \ dbox-save.h \ dbox-storage.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/maildir/0002755000175000017500000000000013172375611016144 500000000000000dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-storage.c0000644000175000017500000005022313165463624021317 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mkdir-parents.h" #include "eacces-error.h" #include "unlink-old-files.h" #include "mailbox-uidvalidity.h" #include "mailbox-list-private.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include "maildir-sync.h" #include "index-mail.h" #include #define MAILDIR_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, maildir_mailbox_list_module) #define MAILDIR_SUBFOLDER_FILENAME "maildirfolder" struct maildir_mailbox_list_context { union mailbox_list_module_context module_ctx; const struct maildir_settings *set; }; extern struct mail_storage maildir_storage; extern struct mailbox maildir_mailbox; static MODULE_CONTEXT_DEFINE_INIT(maildir_mailbox_list_module, &mailbox_list_module_register); static const char *maildir_subdirs[] = { "cur", "new", "tmp" }; static struct mail_storage *maildir_storage_alloc(void) { struct maildir_storage *storage; pool_t pool; pool = pool_alloconly_create("maildir storage", 512+256); storage = p_new(pool, struct maildir_storage, 1); storage->storage = maildir_storage; storage->storage.pool = pool; return &storage->storage; } static int maildir_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r ATTR_UNUSED) { struct maildir_storage *storage = (struct maildir_storage *)_storage; struct mailbox_list *list = ns->list; const char *dir; storage->set = mail_namespace_get_driver_settings(ns, _storage); storage->temp_prefix = p_strdup(_storage->pool, mailbox_list_get_temp_prefix(list)); if (list->set.control_dir == NULL && list->set.inbox_path == NULL && (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) { /* put the temp files into tmp/ directory preferrably */ storage->temp_prefix = p_strconcat(_storage->pool, "tmp/", storage->temp_prefix, NULL); dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR); } else { /* control dir should also be writable */ dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL); } _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/", storage->temp_prefix, NULL); return 0; } static void maildir_storage_get_list_settings(const struct mail_namespace *ns, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS; if (set->subscription_fname == NULL) set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME; if (set->inbox_path == NULL && *set->maildir_name == '\0' && (strcmp(set->layout, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 || strcmp(set->layout, MAILBOX_LIST_NAME_FS) == 0) && (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) { /* Maildir++ INBOX is the Maildir base itself */ set->inbox_path = set->root_dir; } } static const char * maildir_storage_find_root_dir(const struct mail_namespace *ns) { bool debug = ns->mail_set->mail_debug; const char *home, *path; /* we'll need to figure out the maildir location ourself. It's ~/Maildir unless we are chrooted. */ if (ns->owner != NULL && mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/Maildir", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug) i_debug("maildir: root exists (%s)", path); return path; } if (debug) i_debug("maildir: access(%s, rwx): failed: %m", path); } else { if (debug) i_debug("maildir: Home directory not set"); if (access("/cur", R_OK|W_OK|X_OK) == 0) { if (debug) i_debug("maildir: /cur exists, assuming chroot"); return "/"; } } return NULL; } static bool maildir_storage_autodetect(const struct mail_namespace *ns, struct mailbox_list_settings *set) { bool debug = ns->mail_set->mail_debug; struct stat st; const char *path, *root_dir; if (set->root_dir != NULL) root_dir = set->root_dir; else { root_dir = maildir_storage_find_root_dir(ns); if (root_dir == NULL) { if (debug) i_debug("maildir: couldn't find root dir"); return FALSE; } } path = t_strconcat(root_dir, "/cur", NULL); if (stat(path, &st) < 0) { if (debug) i_debug("maildir autodetect: stat(%s) failed: %m", path); return FALSE; } if (!S_ISDIR(st.st_mode)) { if (debug) i_debug("maildir autodetect: %s not a directory", path); return FALSE; } set->root_dir = root_dir; maildir_storage_get_list_settings(ns, set); return TRUE; } static int mkdir_verify(struct mailbox *box, const char *dir, bool verify) { const struct mailbox_permissions *perm; struct stat st; if (verify) { if (stat(dir, &st) == 0) return 0; if (errno != ENOENT) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", dir); return -1; } } perm = mailbox_get_permissions(box); if (mkdir_parents_chgrp(dir, perm->dir_create_mode, perm->file_create_gid, perm->file_create_gid_origin) == 0) return 0; if (errno == EEXIST) { if (verify) return 0; mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); } else if (errno == ENOENT) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox was deleted while it was being created"); } else if (errno == EACCES) { if (box->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED) { /* shared namespace, don't log permission errors */ mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return -1; } mail_storage_set_critical(box->storage, "%s", mail_error_create_eacces_msg("mkdir", dir)); } else { mail_storage_set_critical(box->storage, "mkdir(%s) failed: %m", dir); } return -1; } static int maildir_check_tmp(struct mail_storage *storage, const char *dir) { unsigned int interval = storage->set->mail_temp_scan_interval; const char *path; struct stat st; /* if tmp/ directory exists, we need to clean it up once in a while */ path = t_strconcat(dir, "/tmp", NULL); if (stat(path, &st) < 0) { if (errno == ENOENT || errno == ENAMETOOLONG) return 0; if (errno == EACCES) { mail_storage_set_critical(storage, "%s", mail_error_eacces_msg("stat", path)); return -1; } mail_storage_set_critical(storage, "stat(%s) failed: %m", path); return -1; } if (interval == 0) { /* disabled */ } else if (st.st_atime > st.st_ctime + MAILDIR_TMP_DELETE_SECS) { /* the directory should be empty. we won't do anything until ctime changes. */ } else if (st.st_atime < ioloop_time - (time_t)interval) { /* time to scan */ (void)unlink_old_files(path, "", ioloop_time - MAILDIR_TMP_DELETE_SECS); } return 1; } /* create or fix maildir, ignore if it already exists */ static int create_maildir_subdirs(struct mailbox *box, bool verify) { const char *path, *box_path; unsigned int i; enum mail_error error; int ret = 0; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &box_path) < 0) return -1; for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) { path = t_strconcat(box_path, "/", maildir_subdirs[i], NULL); if (mkdir_verify(box, path, verify) < 0) { error = mailbox_get_last_mail_error(box); if (error != MAIL_ERROR_EXISTS) return -1; /* try to create all of the directories in case one of them doesn't exist */ ret = -1; } } return ret; } static void maildir_lock_touch_timeout(struct maildir_mailbox *mbox) { (void)maildir_uidlist_lock_touch(mbox->uidlist); } static struct mailbox * maildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct maildir_mailbox *mbox; pool_t pool; pool = pool_alloconly_create("maildir mailbox", 1024*3); mbox = p_new(pool, struct maildir_mailbox, 1); mbox->box = maildir_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &maildir_mail_vfuncs; mbox->maildir_list_index_ext_id = (uint32_t)-1; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = (struct maildir_storage *)storage; return &mbox->box; } static int maildir_mailbox_open_existing(struct mailbox *box) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; mbox->uidlist = maildir_uidlist_init(mbox); mbox->keywords = maildir_keywords_init(mbox); if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) { if (maildir_uidlist_lock(mbox->uidlist) <= 0) return -1; mbox->keep_lock_to = timeout_add(MAILDIR_LOCK_TOUCH_SECS * 1000, maildir_lock_touch_timeout, mbox); } if (index_storage_mailbox_open(box, FALSE) < 0) return -1; mbox->maildir_ext_id = mail_index_ext_register(mbox->box.index, "maildir", sizeof(mbox->maildir_hdr), 0, 0); return 0; } static bool maildir_storage_is_readonly(struct mailbox *box) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; if (index_storage_is_readonly(box)) return TRUE; if (maildir_is_backend_readonly(mbox)) { /* return read-only only if there are no private flags (that are stored in index files) */ if (mailbox_get_private_flags_mask(box) == 0) return TRUE; } return FALSE; } static int maildir_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { return index_storage_mailbox_exists_full(box, "cur", existence_r); } static int maildir_mailbox_open(struct mailbox *box) { const char *box_path = mailbox_get_path(box); const char *root_dir; struct stat st; int ret; /* begin by checking if tmp/ directory exists and if it should be cleaned up. */ ret = maildir_check_tmp(box->storage, box_path); if (ret > 0) { /* exists */ return maildir_mailbox_open_existing(box); } if (ret < 0) return -1; /* tmp/ directory doesn't exist. does the maildir? autocreate missing dirs only with Maildir++ and imapdir layouts. */ if (strcmp(box->list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0 && strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPDIR) != 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } root_dir = mailbox_list_get_root_forced(box->list, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(box_path, root_dir) == 0 && !box->inbox_any) { /* root directory for some namespace. */ errno = ENOENT; } else if (stat(box_path, &st) == 0) { /* yes, we'll need to create the missing dirs */ if (create_maildir_subdirs(box, TRUE) < 0) return -1; return maildir_mailbox_open_existing(box); } if (errno == ENOENT || errno == ENAMETOOLONG) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", box_path); return -1; } } static int maildir_create_shared(struct mailbox *box) { const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *path; mode_t old_mask; int fd, ret; ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) return -1; i_assert(ret > 0); old_mask = umask(0); path = t_strconcat(path, "/dovecot-shared", NULL); fd = open(path, O_WRONLY | O_CREAT, perm->file_create_mode); umask(old_mask); if (fd == -1) { mail_storage_set_critical(box->storage, "open(%s) failed: %m", path); return -1; } if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", path); } } i_close_fd(&fd); return 0; } static int maildir_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; struct maildir_uidlist *uidlist; bool locked = FALSE; int ret = 0; if (!box->opened) { if (mailbox_open(box) < 0) return -1; } uidlist = mbox->uidlist; if (update->uid_validity != 0 || update->min_next_uid != 0 || !guid_128_is_empty(update->mailbox_guid)) { if (maildir_uidlist_lock(uidlist) <= 0) return -1; locked = TRUE; if (!guid_128_is_empty(update->mailbox_guid)) maildir_uidlist_set_mailbox_guid(uidlist, update->mailbox_guid); if (update->uid_validity != 0) maildir_uidlist_set_uid_validity(uidlist, update->uid_validity); if (update->min_next_uid != 0) { maildir_uidlist_set_next_uid(uidlist, update->min_next_uid, FALSE); } ret = maildir_uidlist_update(uidlist); } if (ret == 0) ret = index_storage_mailbox_update(box, update); if (locked) maildir_uidlist_unlock(uidlist); return ret; } static int maildir_create_maildirfolder_file(struct mailbox *box) { const struct mailbox_permissions *perm; const char *path; mode_t old_mask; int fd; /* Maildir++ spec wants that maildirfolder named file is created for all subfolders. Do this only with Maildir++ layout. */ if (strcmp(box->list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0) return 0; perm = mailbox_get_permissions(box); path = t_strconcat(mailbox_get_path(box), "/"MAILDIR_SUBFOLDER_FILENAME, NULL); old_mask = umask(0); fd = open(path, O_CREAT | O_WRONLY, perm->file_create_mode); umask(old_mask); if (fd != -1) { /* ok */ } else if (errno == ENOENT) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox was deleted while it was being created"); return -1; } else { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", path); return -1; } if (perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) { /* ok */ } else if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", path); } } i_close_fd(&fd); return 0; } static int maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { const char *root_dir, *shared_path; struct stat st; int ret; if ((ret = index_storage_mailbox_create(box, directory)) <= 0) return ret; ret = 0; /* the maildir is created now. finish the creation as best as we can */ if (create_maildir_subdirs(box, FALSE) < 0) ret = -1; if (maildir_create_maildirfolder_file(box) < 0) ret = -1; /* if dovecot-shared exists in the root dir, copy it to newly created mailboxes */ root_dir = mailbox_list_get_root_forced(box->list, MAILBOX_LIST_PATH_TYPE_MAILBOX); shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL); if (stat(shared_path, &st) == 0) { if (maildir_create_shared(box) < 0) ret = -1; } if (update != NULL) { if (maildir_mailbox_update(box, update) < 0) ret = -1; } return ret; } static int maildir_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; if ((items & MAILBOX_METADATA_GUID) != 0) { if (maildir_uidlist_get_mailbox_guid(mbox->uidlist, metadata_r->guid) < 0) return -1; } return 0; } static void maildir_mailbox_close(struct mailbox *box) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; if (mbox->keep_lock_to != NULL) { maildir_uidlist_unlock(mbox->uidlist); timeout_remove(&mbox->keep_lock_to); } if (mbox->flags_view != NULL) mail_index_view_close(&mbox->flags_view); if (mbox->keywords != NULL) maildir_keywords_deinit(&mbox->keywords); maildir_uidlist_deinit(&mbox->uidlist); index_storage_mailbox_close(box); } static void maildir_notify_changes(struct mailbox *box) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; const char *box_path = mailbox_get_path(box); if (box->notify_callback == NULL) mailbox_watch_remove_all(&mbox->box); else { mailbox_watch_add(&mbox->box, t_strconcat(box_path, "/new", NULL)); mailbox_watch_add(&mbox->box, t_strconcat(box_path, "/cur", NULL)); } } static bool maildir_is_internal_name(struct mailbox_list *list ATTR_UNUSED, const char *name) { return strcmp(name, "cur") == 0 || strcmp(name, "new") == 0 || strcmp(name, "tmp") == 0; } static void maildir_storage_add_list(struct mail_storage *storage, struct mailbox_list *list) { struct maildir_mailbox_list_context *mlist; mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1); mlist->module_ctx.super = list->v; mlist->set = mail_namespace_get_driver_settings(list->ns, storage); list->v.is_internal_name = maildir_is_internal_name; MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist); } uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list) { const char *path; path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL); path = t_strconcat(path, "/"MAILDIR_UIDVALIDITY_FNAME, NULL); return mailbox_uidvalidity_next(list, path); } static enum mail_flags maildir_get_private_flags_mask(struct mailbox *box) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; const char *path, *path2; struct stat st; if (mbox->private_flags_mask_set) return mbox->_private_flags_mask; mbox->private_flags_mask_set = TRUE; path = mailbox_list_get_root_forced(box->list, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (box->list->set.index_pvt_dir != NULL) { /* private index directory is set. we'll definitely have private flags. */ mbox->_private_flags_mask = MAIL_SEEN; } else if (!mailbox_list_get_root_path(box->list, MAILBOX_LIST_PATH_TYPE_INDEX, &path2) || strcmp(path, path2) == 0) { /* no separate index directory. we can't have private flags, so don't even bother checking if dovecot-shared exists */ } else { path = t_strconcat(mailbox_get_path(box), "/dovecot-shared", NULL); if (stat(path, &st) == 0) mbox->_private_flags_mask = MAIL_SEEN; } return mbox->_private_flags_mask; } bool maildir_is_backend_readonly(struct maildir_mailbox *mbox) { if (!mbox->backend_readonly_set) { const char *box_path = mailbox_get_path(&mbox->box); mbox->backend_readonly_set = TRUE; if (access(t_strconcat(box_path, "/cur", NULL), W_OK) < 0 && errno == EACCES) mbox->backend_readonly = TRUE; } return mbox->backend_readonly; } struct mail_storage maildir_storage = { .name = MAILDIR_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA, .v = { maildir_get_setting_parser_info, maildir_storage_alloc, maildir_storage_create, index_storage_destroy, maildir_storage_add_list, maildir_storage_get_list_settings, maildir_storage_autodetect, maildir_mailbox_alloc, NULL, NULL, } }; struct mailbox maildir_mailbox = { .v = { maildir_storage_is_readonly, index_storage_mailbox_enable, maildir_mailbox_exists, maildir_mailbox_open, maildir_mailbox_close, index_storage_mailbox_free, maildir_mailbox_create, maildir_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, maildir_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, maildir_list_index_has_changed, maildir_list_index_update_sync, maildir_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, maildir_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, maildir_get_private_flags_mask, index_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, maildir_save_alloc, maildir_save_begin, maildir_save_continue, maildir_save_finish, maildir_save_cancel, maildir_copy, maildir_transaction_save_commit_pre, maildir_transaction_save_commit_post, maildir_transaction_save_rollback, index_storage_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-filename-flags.h0000644000175000017500000000102613123174404022514 00000000000000#ifndef MAILDIR_FILENAME_FLAGS_H #define MAILDIR_FILENAME_FLAGS_H void maildir_filename_flags_get(struct maildir_keywords_sync_ctx *ctx, const char *fname, enum mail_flags *flags_r, ARRAY_TYPE(keyword_indexes) *keywords_r); const char *maildir_filename_flags_set(const char *fname, enum mail_flags flags); const char *maildir_filename_flags_kw_set(struct maildir_keywords_sync_ctx *ctx, const char *fname, enum mail_flags flags, ARRAY_TYPE(keyword_indexes) *keywords); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-keywords.h0000644000175000017500000000241013123174404021507 00000000000000#ifndef MAILDIR_KEYWORDS_H #define MAILDIR_KEYWORDS_H #define MAILDIR_KEYWORDS_NAME "dovecot-keywords" struct maildir_mailbox; struct maildir_keywords; struct maildir_keywords_sync_ctx; struct maildir_keywords *maildir_keywords_init(struct maildir_mailbox *mbox); void maildir_keywords_deinit(struct maildir_keywords **mk); /* Initialize a read-only maildir_keywords instance. Mailbox needs to contain the dovecot-keywords file, but otherwise it doesn't have to be in maildir format. */ struct maildir_keywords * maildir_keywords_init_readonly(struct mailbox *box); struct maildir_keywords_sync_ctx * maildir_keywords_sync_init(struct maildir_keywords *mk, struct mail_index *index); /* Don't try to add any nonexistent keywords */ struct maildir_keywords_sync_ctx * maildir_keywords_sync_init_readonly(struct maildir_keywords *mk, struct mail_index *index); void maildir_keywords_sync_deinit(struct maildir_keywords_sync_ctx **ctx); /* Returns keyword index. */ unsigned int maildir_keywords_char_idx(struct maildir_keywords_sync_ctx *ctx, char keyword); /* Returns keyword character for given index, or \0 if keyword couldn't be added. */ char maildir_keywords_idx_char(struct maildir_keywords_sync_ctx *ctx, unsigned int idx); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/Makefile.in0000644000175000017500000005730213172375573020145 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/maildir ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_maildir_la_LIBADD = am_libstorage_maildir_la_OBJECTS = maildir-copy.lo maildir-filename.lo \ maildir-filename-flags.lo maildir-keywords.lo maildir-mail.lo \ maildir-save.lo maildir-settings.lo maildir-storage.lo \ maildir-sync.lo maildir-sync-index.lo maildir-uidlist.lo \ maildir-util.lo libstorage_maildir_la_OBJECTS = $(am_libstorage_maildir_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_maildir_la_SOURCES) DIST_SOURCES = $(libstorage_maildir_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_maildir.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_maildir_la_SOURCES = \ maildir-copy.c \ maildir-filename.c \ maildir-filename-flags.c \ maildir-keywords.c \ maildir-mail.c \ maildir-save.c \ maildir-settings.c \ maildir-storage.c \ maildir-sync.c \ maildir-sync-index.c \ maildir-uidlist.c \ maildir-util.c headers = \ maildir-filename.h \ maildir-filename-flags.h \ maildir-keywords.h \ maildir-storage.h \ maildir-settings.h \ maildir-sync.h \ maildir-uidlist.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/maildir/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/maildir/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_maildir.la: $(libstorage_maildir_la_OBJECTS) $(libstorage_maildir_la_DEPENDENCIES) $(EXTRA_libstorage_maildir_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_maildir_la_OBJECTS) $(libstorage_maildir_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-copy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-filename-flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-filename.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-keywords.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-sync-index.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-uidlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildir-util.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-uidlist.h0000644000175000017500000001521213123174404021321 00000000000000#ifndef MAILDIR_UIDLIST_H #define MAILDIR_UIDLIST_H #include "mail-storage.h" #define MAILDIR_UIDLIST_NAME "dovecot-uidlist" /* how many seconds to wait before overriding uidlist.lock */ #define MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT (60*2) struct maildir_mailbox; struct maildir_uidlist; struct maildir_uidlist_sync_ctx; struct maildir_uidlist_rec; enum maildir_uidlist_sync_flags { MAILDIR_UIDLIST_SYNC_PARTIAL = 0x01, MAILDIR_UIDLIST_SYNC_KEEP_STATE = 0x02, MAILDIR_UIDLIST_SYNC_FORCE = 0x04, MAILDIR_UIDLIST_SYNC_TRYLOCK = 0x08, MAILDIR_UIDLIST_SYNC_NOREFRESH = 0x10, MAILDIR_UIDLIST_SYNC_NOLOCK = 0x20 }; enum maildir_uidlist_rec_flag { MAILDIR_UIDLIST_REC_FLAG_NEW_DIR = 0x01, MAILDIR_UIDLIST_REC_FLAG_MOVED = 0x02, MAILDIR_UIDLIST_REC_FLAG_RECENT = 0x04, MAILDIR_UIDLIST_REC_FLAG_NONSYNCED = 0x08, MAILDIR_UIDLIST_REC_FLAG_RACING = 0x10 }; enum maildir_uidlist_hdr_ext_key { MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY = 'V', MAILDIR_UIDLIST_HDR_EXT_NEXT_UID = 'N', MAILDIR_UIDLIST_HDR_EXT_GUID = 'G', /* POP3 UIDL format unless overridden by records */ MAILDIR_UIDLIST_HDR_EXT_POP3_UIDL_FORMAT = 'P' }; #define MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(c) \ ((c) >= 'A' && (c) <= 'Z') enum maildir_uidlist_rec_ext_key { /* Physical message size. If filename also contains ,S= this isn't written to uidlist. */ MAILDIR_UIDLIST_REC_EXT_PSIZE = 'S', /* Virtual message size. If filename also contains ,W= this isn't written to uidlist. */ MAILDIR_UIDLIST_REC_EXT_VSIZE = 'W', /* POP3 UIDL overriding the default format */ MAILDIR_UIDLIST_REC_EXT_POP3_UIDL = 'P', /* POP3 message ordering number. Lower numbered messages are listed first. Messages without ordering number are listed after them. The idea is to be able to preserve POP3 UIDL list and IMAP UIDs perfectly when migrating from other servers. */ MAILDIR_UIDLIST_REC_EXT_POP3_ORDER = 'O', /* Message GUID (default is the base filename) */ MAILDIR_UIDLIST_REC_EXT_GUID = 'G' }; int maildir_uidlist_lock(struct maildir_uidlist *uidlist); int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist); int maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist); void maildir_uidlist_unlock(struct maildir_uidlist *uidlist); bool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist); bool maildir_uidlist_is_read(struct maildir_uidlist *uidlist); /* Returns TRUE if uidlist file is currently open */ bool maildir_uidlist_is_open(struct maildir_uidlist *uidlist); struct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox); void maildir_uidlist_deinit(struct maildir_uidlist **uidlist); /* Returns -1 if error, 0 if file is broken or lost, 1 if ok. If nfs_flush=TRUE and storage has NFS_FLUSH flag set, the NFS attribute cache is flushed to make sure that we see the latest uidlist file. */ int maildir_uidlist_refresh(struct maildir_uidlist *uidlist); /* Like maildir_uidlist_refresh(), but if uidlist isn't opened yet, try to fill in the uidvalidity/nextuid from index file instead. */ int maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist); /* Look up uidlist record for given filename. Returns 1 if found, 0 if not found, -1 if error */ int maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_flag *flags_r, const char **fname_r); /* Returns extension's value or NULL if it doesn't exist. */ const char * maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key); uint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist); uint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist); int maildir_uidlist_get_mailbox_guid(struct maildir_uidlist *uidlist, guid_128_t mailbox_guid); void maildir_uidlist_set_mailbox_guid(struct maildir_uidlist *uidlist, const guid_128_t mailbox_guid); void maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist, uint32_t uid_validity); void maildir_uidlist_set_next_uid(struct maildir_uidlist *uidlist, uint32_t next_uid, bool force); /* Update extended record. */ void maildir_uidlist_set_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key, const char *value); void maildir_uidlist_unset_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key); /* If uidlist has changed, update it. This is mostly meant to be used with maildir_uidlist_set_ext() */ int maildir_uidlist_update(struct maildir_uidlist *uidlist); void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist); /* Sync uidlist with what's actually on maildir. Returns same as maildir_uidlist_lock(). */ int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist, enum maildir_uidlist_sync_flags sync_flags, struct maildir_uidlist_sync_ctx **sync_ctx_r); int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx, const char *filename, enum maildir_uidlist_rec_flag flags); int maildir_uidlist_sync_next_uid(struct maildir_uidlist_sync_ctx *ctx, const char *filename, uint32_t uid, enum maildir_uidlist_rec_flag flags, struct maildir_uidlist_rec **rec_r); void maildir_uidlist_sync_remove(struct maildir_uidlist_sync_ctx *ctx, const char *filename); void maildir_uidlist_sync_set_ext(struct maildir_uidlist_sync_ctx *ctx, struct maildir_uidlist_rec *rec, enum maildir_uidlist_rec_ext_key key, const char *value); void maildir_uidlist_update_fname(struct maildir_uidlist *uidlist, const char *filename); const char * maildir_uidlist_sync_get_full_filename(struct maildir_uidlist_sync_ctx *ctx, const char *filename); void maildir_uidlist_sync_recreate(struct maildir_uidlist_sync_ctx *ctx); void maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx); int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **ctx, bool success); bool maildir_uidlist_get_uid(struct maildir_uidlist *uidlist, const char *filename, uint32_t *uid_r); const char * maildir_uidlist_get_full_filename(struct maildir_uidlist *uidlist, const char *filename); void maildir_uidlist_add_flags(struct maildir_uidlist *uidlist, const char *filename, enum maildir_uidlist_rec_flag flags); /* List all maildir files. */ struct maildir_uidlist_iter_ctx * maildir_uidlist_iter_init(struct maildir_uidlist *uidlist); bool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx, uint32_t *uid_r, enum maildir_uidlist_rec_flag *flags_r, const char **filename_r); void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx **ctx); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-mail.c0000644000175000017500000005534013165463624020602 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "nfs-workarounds.h" #include "index-mail.h" #include "maildir-storage.h" #include "maildir-filename.h" #include "maildir-uidlist.h" #include "maildir-sync.h" #include #include #include #include struct maildir_open_context { int fd; char *path; }; static int do_open(struct maildir_mailbox *mbox, const char *path, struct maildir_open_context *ctx) { ctx->fd = nfs_safe_open(path, O_RDONLY); if (ctx->fd != -1) { ctx->path = i_strdup(path); return 1; } if (errno == ENOENT) return 0; if (errno == EACCES) { mail_storage_set_critical(&mbox->storage->storage, "%s", mail_error_eacces_msg("open", path)); } else { mail_storage_set_critical(&mbox->storage->storage, "open(%s) failed: %m", path); } return -1; } static int do_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st) { if (stat(path, st) == 0) return 1; if (errno == ENOENT) return 0; if (errno == EACCES) { mail_storage_set_critical(&mbox->storage->storage, "%s", mail_error_eacces_msg("stat", path)); } else { mail_storage_set_critical(&mbox->storage->storage, "stat(%s) failed: %m", path); } return -1; } static struct istream * maildir_open_mail(struct maildir_mailbox *mbox, struct mail *mail, bool *deleted_r) { struct istream *input; const char *path; struct maildir_open_context ctx; *deleted_r = FALSE; ctx.fd = -1; ctx.path = NULL; mail->transaction->stats.open_lookup_count++; if (!mail->saving) { if (maildir_file_do(mbox, mail->uid, do_open, &ctx) < 0) return NULL; } else { path = maildir_save_file_get_path(mail->transaction, mail->seq); if (do_open(mbox, path, &ctx) <= 0) return NULL; } if (ctx.fd == -1) { *deleted_r = TRUE; return NULL; } input = i_stream_create_fd_autoclose(&ctx.fd, 0); if (input->stream_errno == EISDIR) { i_stream_destroy(&input); if (maildir_lose_unexpected_dir(&mbox->storage->storage, ctx.path) >= 0) *deleted_r = TRUE; } else { i_stream_set_name(input, ctx.path); index_mail_set_read_buffer_size(mail, input); } i_free(ctx.path); return input; } static int maildir_mail_stat(struct mail *mail, struct stat *st_r) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box; struct index_mail *imail = (struct index_mail *)mail; const char *path; int fd, ret; if (mail->lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE) { mail_set_aborted(mail); return -1; } mail->mail_metadata_accessed = TRUE; if (imail->data.access_part != 0 && imail->data.stream == NULL) { /* we're going to open the mail anyway */ struct istream *input; (void)mail_get_stream(mail, NULL, NULL, &input); } if (imail->data.stream != NULL && (fd = i_stream_get_fd(imail->data.stream)) != -1) { mail->transaction->stats.fstat_lookup_count++; if (fstat(fd, st_r) < 0) { mail_storage_set_critical(mail->box->storage, "fstat(%s) failed: %m", i_stream_get_name(imail->data.stream)); return -1; } } else if (!mail->saving) { mail->transaction->stats.stat_lookup_count++; ret = maildir_file_do(mbox, mail->uid, do_stat, st_r); if (ret <= 0) { if (ret == 0) mail_set_expunged(mail); return -1; } } else { mail->transaction->stats.stat_lookup_count++; path = maildir_save_file_get_path(mail->transaction, mail->seq); if (stat(path, st_r) < 0) { mail_storage_set_critical(mail->box->storage, "stat(%s) failed: %m", path); return -1; } } return 0; } static int maildir_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct stat st; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (maildir_mail_stat(_mail, &st) < 0) return -1; *date_r = data->received_date = st.st_mtime; return 0; } static int maildir_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct stat st; if (index_mail_get_save_date(_mail, date_r) == 0) return 0; if (maildir_mail_stat(_mail, &st) < 0) return -1; *date_r = data->save_date = st.st_ctime; return 0; } static int maildir_mail_get_fname(struct maildir_mailbox *mbox, struct mail *mail, const char **fname_r) { enum maildir_uidlist_rec_flag flags; struct mail_index_view *view; uint32_t seq; bool exists; int ret; ret = maildir_sync_lookup(mbox, mail->uid, &flags, fname_r); if (ret != 0) return ret; /* file exists in index file, but not in dovecot-uidlist anymore. */ mail_set_expunged(mail); /* one reason this could happen is if we delayed opening dovecot-uidlist and we're trying to open a mail that got recently expunged. Let's test this theory first: */ mail_index_refresh(mbox->box.index); view = mail_index_view_open(mbox->box.index); exists = mail_index_lookup_seq(view, mail->uid, &seq); mail_index_view_close(&view); if (exists) { /* the message still exists in index. this means there's some kind of a desync, which doesn't get fixed if cur/ mtime is the same as in index. fix this by forcing a resync. */ (void)maildir_storage_sync_force(mbox, mail->uid); } return 0; } static int maildir_get_pop3_state(struct index_mail *mail) { struct mailbox *box = mail->mail.mail.box; struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); const struct mail_cache_field *fields; unsigned int i, count, psize_idx, vsize_idx; enum mail_cache_decision_type dec, vsize_dec; enum mail_fetch_field allowed_pop3_fields; bool not_pop3_only = FALSE; if (mail->pop3_state_set) return mail->pop3_state; /* if this mail itself has non-pop3 fields we know we're not pop3-only */ allowed_pop3_fields = MAIL_FETCH_FLAGS | MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY | MAIL_FETCH_STORAGE_ID | MAIL_FETCH_VIRTUAL_SIZE; if (mail->data.wanted_headers != NULL || (mail->data.wanted_fields & ~allowed_pop3_fields) != 0) not_pop3_only = TRUE; /* get vsize decisions */ psize_idx = ibox->cache_fields[MAIL_CACHE_PHYSICAL_FULL_SIZE].idx; vsize_idx = ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx; if (not_pop3_only) { vsize_dec = mail_cache_field_get_decision(box->cache, vsize_idx); vsize_dec &= ~MAIL_CACHE_DECISION_FORCED; } else { /* also check if there are any non-[pv]size cached fields */ vsize_dec = MAIL_CACHE_DECISION_NO; fields = mail_cache_register_get_list(box->cache, pool_datastack_create(), &count); for (i = 0; i < count; i++) { dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED; if (fields[i].idx == vsize_idx) vsize_dec = dec; else if (dec != MAIL_CACHE_DECISION_NO && fields[i].idx != psize_idx) not_pop3_only = TRUE; } } if (index_mail_get_vsize_extension(&mail->mail.mail) != NULL) { /* having a vsize extension in index is the same as having vsize's caching decision YES */ vsize_dec = MAIL_CACHE_DECISION_YES; } if (!not_pop3_only) { /* either nothing is cached, or only vsize is cached. */ mail->pop3_state = 1; } else if (vsize_dec != MAIL_CACHE_DECISION_YES && (box->flags & MAILBOX_FLAG_POP3_SESSION) == 0) { /* if virtual size isn't cached permanently, POP3 isn't being used */ mail->pop3_state = -1; } else { /* possibly a mixed pop3/imap */ mail->pop3_state = 0; } mail->pop3_state_set = TRUE; return mail->pop3_state; } static int maildir_quick_size_lookup(struct index_mail *mail, bool vsize, uoff_t *size_r) { struct mail *_mail = &mail->mail.mail; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; enum maildir_uidlist_rec_ext_key key; const char *path, *fname, *value; if (!_mail->saving) { if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) return -1; } else { if (maildir_save_file_get_size(_mail->transaction, _mail->seq, vsize, size_r) == 0) return 1; path = maildir_save_file_get_path(_mail->transaction, _mail->seq); fname = strrchr(path, '/'); fname = fname != NULL ? fname + 1 : path; } /* size can be included in filename */ if (vsize || !mbox->storage->set->maildir_broken_filename_sizes) { if (maildir_filename_get_size(fname, vsize ? MAILDIR_EXTRA_VIRTUAL_SIZE : MAILDIR_EXTRA_FILE_SIZE, size_r)) return 1; } /* size can be included in uidlist entry */ if (!_mail->saving) { key = vsize ? MAILDIR_UIDLIST_REC_EXT_VSIZE : MAILDIR_UIDLIST_REC_EXT_PSIZE; value = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, key); if (value != NULL && str_to_uoff(value, size_r) == 0) return 1; } return 0; } static void maildir_handle_size_caching(struct index_mail *mail, bool quick_check, bool vsize) { struct mailbox *box = mail->mail.mail.box; struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; enum mail_fetch_field field; uoff_t size; int pop3_state; field = vsize ? MAIL_FETCH_VIRTUAL_SIZE : MAIL_FETCH_PHYSICAL_SIZE; if ((mail->data.dont_cache_fetch_fields & field) != 0) return; if (quick_check && maildir_quick_size_lookup(mail, vsize, &size) > 0) { /* already in filename / uidlist. don't add it anywhere, including to the uidlist if it's already in filename. do some extra checks here to catch potential cache bugs. */ if (vsize && mail->data.virtual_size != size) { mail_set_mail_cache_corrupted(&mail->mail.mail, "Corrupted virtual size: " "%"PRIuUOFF_T" != %"PRIuUOFF_T, mail->data.virtual_size, size); mail->data.virtual_size = size; } else if (!vsize && mail->data.physical_size != size) { mail_set_mail_cache_corrupted(&mail->mail.mail, "Corrupted physical size: " "%"PRIuUOFF_T" != %"PRIuUOFF_T, mail->data.physical_size, size); mail->data.physical_size = size; } mail->data.dont_cache_fetch_fields |= field; return; } /* 1 = pop3-only, 0 = mixed, -1 = no pop3 */ pop3_state = maildir_get_pop3_state(mail); if (pop3_state >= 0 && mail->mail.mail.uid != 0) { /* if size is wanted permanently, store it to uidlist so that in case cache file gets lost we can get it quickly */ mail->data.dont_cache_fetch_fields |= field; size = vsize ? mail->data.virtual_size : mail->data.physical_size; maildir_uidlist_set_ext(mbox->uidlist, mail->mail.mail.uid, vsize ? MAILDIR_UIDLIST_REC_EXT_VSIZE : MAILDIR_UIDLIST_REC_EXT_PSIZE, dec2str(size)); } } static int maildir_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct message_size hdr_size, body_size; struct istream *input; uoff_t old_offset; if (maildir_uidlist_is_read(mbox->uidlist) || (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) { /* try to get the size from uidlist. this is especially useful with pop3 to avoid unnecessarily opening the cache file. */ if (maildir_quick_size_lookup(mail, TRUE, &data->virtual_size) < 0) return -1; } if (data->virtual_size == (uoff_t)-1) { if (index_mail_get_cached_virtual_size(mail, size_r)) { i_assert(mail->data.virtual_size != (uoff_t)-1); maildir_handle_size_caching(mail, TRUE, TRUE); return 0; } if (maildir_quick_size_lookup(mail, TRUE, &data->virtual_size) < 0) return -1; } if (data->virtual_size != (uoff_t)-1) { data->dont_cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE; *size_r = data->virtual_size; return 0; } /* fallback to reading the file */ old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0) return -1; i_stream_seek(data->stream, old_offset); maildir_handle_size_caching(mail, FALSE, TRUE); *size_r = data->virtual_size; return 0; } static int maildir_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; struct index_mail_data *data = &mail->data; struct stat st; struct message_size hdr_size, body_size; struct istream *input; const char *path; int ret; if (maildir_uidlist_is_read(mbox->uidlist) || (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) { /* try to get the size from uidlist (see virtual size above) */ if (maildir_quick_size_lookup(mail, FALSE, &data->physical_size) < 0) return -1; } if (data->physical_size == (uoff_t)-1) { if (index_mail_get_physical_size(_mail, size_r) == 0) { i_assert(mail->data.physical_size != (uoff_t)-1); maildir_handle_size_caching(mail, TRUE, FALSE); return 0; } if (maildir_quick_size_lookup(mail, FALSE, &data->physical_size) < 0) return -1; } if (data->physical_size != (uoff_t)-1) { data->dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; *size_r = data->physical_size; return 0; } if (mail->mail.v.istream_opened != NULL) { /* we can't use stat(), because this may be a mail that some plugin has changed (e.g. zlib). need to do it the slow way. */ if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0) return -1; st.st_size = hdr_size.physical_size + body_size.physical_size; } else if (!_mail->saving) { ret = maildir_file_do(mbox, _mail->uid, do_stat, &st); if (ret <= 0) { if (ret == 0) mail_set_expunged(_mail); return -1; } } else { /* saved mail which hasn't been committed yet */ path = maildir_save_file_get_path(_mail->transaction, _mail->seq); if (stat(path, &st) < 0) { mail_storage_set_critical(_mail->box->storage, "stat(%s) failed: %m", path); return -1; } } data->physical_size = st.st_size; maildir_handle_size_caching(mail, FALSE, FALSE); *size_r = st.st_size; return 0; } static int maildir_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; const char *path, *fname = NULL, *end, *guid, *uidl, *order; struct stat st; switch (field) { case MAIL_FETCH_GUID: /* use GUID from uidlist if it exists */ i_assert(!_mail->saving); if (mail->data.guid != NULL) { *value_r = mail->data.guid; return 0; } /* first make sure that we have a refreshed uidlist */ if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) return -1; guid = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_GUID); if (guid != NULL) { if (*guid != '\0') { *value_r = mail->data.guid = p_strdup(mail->mail.data_pool, guid); return 0; } mail_storage_set_critical(_mail->box->storage, "Maildir %s: Corrupted dovecot-uidlist: " "UID %u had empty GUID, clearing it", mailbox_get_path(_mail->box), _mail->uid); maildir_uidlist_unset_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_GUID); } /* default to base filename: */ if (maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, value_r) < 0) return -1; mail->data.guid = mail->data.filename; return 0; case MAIL_FETCH_STORAGE_ID: if (mail->data.filename != NULL) { *value_r = mail->data.filename; return 0; } if (fname != NULL) { /* we came here from MAIL_FETCH_GUID, avoid a second lookup */ } else if (!_mail->saving) { if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) return -1; } else { path = maildir_save_file_get_path(_mail->transaction, _mail->seq); fname = strrchr(path, '/'); fname = fname != NULL ? fname + 1 : path; } end = strchr(fname, MAILDIR_INFO_SEP); mail->data.filename = end == NULL ? p_strdup(mail->mail.data_pool, fname) : p_strdup_until(mail->mail.data_pool, fname, end); *value_r = mail->data.filename; return 0; case MAIL_FETCH_UIDL_BACKEND: uidl = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_POP3_UIDL); if (uidl == NULL) { /* use the default */ *value_r = ""; } else if (*uidl == '\0') { /* special optimization case: use the base file name */ return maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, value_r); } else { *value_r = p_strdup(mail->mail.data_pool, uidl); } return 0; case MAIL_FETCH_POP3_ORDER: order = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_POP3_ORDER); if (order == NULL) { *value_r = ""; } else { *value_r = p_strdup(mail->mail.data_pool, order); } return 0; case MAIL_FETCH_REFCOUNT: if (maildir_mail_stat(_mail, &st) < 0) return -1; *value_r = p_strdup_printf(mail->mail.data_pool, "%lu", (unsigned long)st.st_nlink); return 0; default: return index_mail_get_special(_mail, field, value_r); } } static int maildir_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; struct index_mail_data *data = &mail->data; bool deleted; if (data->stream == NULL) { data->stream = maildir_open_mail(mbox, _mail, &deleted); if (data->stream == NULL) { if (deleted) mail_set_expunged(_mail); return -1; } if (mail->mail.v.istream_opened != NULL) { if (mail->mail.v.istream_opened(_mail, &data->stream) < 0) { i_stream_unref(&data->stream); return -1; } } } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static void maildir_update_pop3_uidl(struct mail *_mail, const char *uidl) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; const char *fname; if (maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, &fname) == 0 && strcmp(uidl, fname) == 0) { /* special case optimization: empty UIDL means the same as base filename */ uidl = ""; } _mail->transaction->nontransactional_changes = TRUE; maildir_uidlist_set_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_POP3_UIDL, uidl); } static void maildir_mail_remove_sizes_from_uidlist(struct mail *mail) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box; if (maildir_uidlist_lookup_ext(mbox->uidlist, mail->uid, MAILDIR_UIDLIST_REC_EXT_VSIZE) != NULL) { maildir_uidlist_unset_ext(mbox->uidlist, mail->uid, MAILDIR_UIDLIST_REC_EXT_VSIZE); } if (maildir_uidlist_lookup_ext(mbox->uidlist, mail->uid, MAILDIR_UIDLIST_REC_EXT_PSIZE) != NULL) { maildir_uidlist_unset_ext(mbox->uidlist, mail->uid, MAILDIR_UIDLIST_REC_EXT_PSIZE); } } struct maildir_size_fix_ctx { uoff_t physical_size; char wrong_key; }; static int do_fix_size(struct maildir_mailbox *mbox, const char *path, struct maildir_size_fix_ctx *ctx) { const char *fname, *newpath, *extra, *info, *dir; struct stat st; fname = strrchr(path, '/'); i_assert(fname != NULL); dir = t_strdup_until(path, fname++); extra = strchr(fname, MAILDIR_EXTRA_SEP); i_assert(extra != NULL); info = strchr(fname, MAILDIR_INFO_SEP); if (info == NULL) info = ""; if (ctx->physical_size == (uoff_t)-1) { if (stat(path, &st) < 0) { if (errno == ENOENT) return 0; mail_storage_set_critical(&mbox->storage->storage, "stat(%s) failed: %m", path); return -1; } ctx->physical_size = st.st_size; } newpath = t_strdup_printf("%s/%s,S=%"PRIuUOFF_T"%s", dir, t_strdup_until(fname, extra), ctx->physical_size, info); if (rename(path, newpath) == 0) { mail_storage_set_critical(mbox->box.storage, "Maildir filename has wrong %c value, " "renamed the file from %s to %s", ctx->wrong_key, path, newpath); return 1; } if (errno == ENOENT) return 0; mail_storage_set_critical(&mbox->storage->storage, "rename(%s, %s) failed: %m", path, newpath); return -1; } static void maildir_mail_remove_sizes_from_filename(struct mail *mail, enum mail_fetch_field field) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box; struct mail_private *pmail = (struct mail_private *)mail; enum maildir_uidlist_rec_flag flags; const char *fname; uoff_t size; struct maildir_size_fix_ctx ctx; if (mbox->storage->set->maildir_broken_filename_sizes) { /* never try to fix sizes in maildir filenames */ return; } if (maildir_sync_lookup(mbox, mail->uid, &flags, &fname) <= 0) return; if (strchr(fname, MAILDIR_EXTRA_SEP) == NULL) return; i_zero(&ctx); ctx.physical_size = (uoff_t)-1; if (field == MAIL_FETCH_VIRTUAL_SIZE && maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE, &size)) { ctx.wrong_key = 'W'; } else if (field == MAIL_FETCH_PHYSICAL_SIZE && maildir_filename_get_size(fname, MAILDIR_EXTRA_FILE_SIZE, &size)) { ctx.wrong_key = 'S'; } else { /* the broken size isn't in filename */ return; } if (pmail->v.istream_opened != NULL) { /* the mail could be e.g. compressed. get the physical size the slow way by actually reading the mail. */ struct istream *input; const struct stat *stp; if (mail_get_stream(mail, NULL, NULL, &input) < 0) return; if (i_stream_stat(input, TRUE, &stp) < 0) return; ctx.physical_size = stp->st_size; } (void)maildir_file_do(mbox, mail->uid, do_fix_size, &ctx); } static void maildir_mail_set_cache_corrupted_reason(struct mail *_mail, enum mail_fetch_field field, const char *reason) { if (field == MAIL_FETCH_PHYSICAL_SIZE || field == MAIL_FETCH_VIRTUAL_SIZE) { maildir_mail_remove_sizes_from_uidlist(_mail); maildir_mail_remove_sizes_from_filename(_mail, field); } index_mail_set_cache_corrupted_reason(_mail, field, reason); } static void maildir_mail_set_cache_corrupted(struct mail *_mail, enum mail_fetch_field field) { maildir_mail_set_cache_corrupted_reason(_mail, field, ""); } struct mail_vfuncs maildir_mail_vfuncs = { index_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, maildir_mail_get_received_date, maildir_mail_get_save_date, maildir_mail_get_virtual_size, maildir_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, maildir_mail_get_stream, index_mail_get_binary_stream, maildir_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, maildir_update_pop3_uidl, index_mail_expunge, maildir_mail_set_cache_corrupted, index_mail_opened, maildir_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-settings.h0000644000175000017500000000044713123174404021510 00000000000000#ifndef MAILDIR_SETTINGS_H #define MAILDIR_SETTINGS_H struct maildir_settings { bool maildir_copy_with_hardlinks; bool maildir_very_dirty_syncs; bool maildir_broken_filename_sizes; bool maildir_empty_new; }; const struct setting_parser_info *maildir_get_setting_parser_info(void); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-copy.c0000644000175000017500000000760313165463624020631 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "nfs-workarounds.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-filename.h" #include "maildir-keywords.h" #include "maildir-sync.h" #include "index-mail.h" #include "mail-copy.h" #include #include struct hardlink_ctx { const char *dest_path; unsigned int success:1; }; static int do_hardlink(struct maildir_mailbox *mbox, const char *path, struct hardlink_ctx *ctx) { int ret; if (mbox->storage->storage.set->mail_nfs_storage) ret = nfs_safe_link(path, ctx->dest_path, FALSE); else ret = link(path, ctx->dest_path); if (ret < 0) { if (errno == ENOENT) return 0; if (ENOQUOTA(errno)) { mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); return -1; } /* we could handle the EEXIST condition by changing the filename, but it practically never happens so just fallback to standard copying for the rare cases when it does. */ if (errno == EACCES || ECANTLINK(errno) || errno == EEXIST) return 1; mail_storage_set_critical(&mbox->storage->storage, "link(%s, %s) failed: %m", path, ctx->dest_path); return -1; } ctx->success = TRUE; return 1; } static int maildir_copy_hardlink(struct mail_save_context *ctx, struct mail *mail) { struct maildir_mailbox *dest_mbox = (struct maildir_mailbox *)ctx->transaction->box; struct maildir_mailbox *src_mbox; struct maildir_filename *mf; struct hardlink_ctx do_ctx; const char *path, *guid, *dest_fname; uoff_t vsize, size; enum mail_lookup_abort old_abort; if (strcmp(mail->box->storage->name, MAILDIR_STORAGE_NAME) == 0) src_mbox = (struct maildir_mailbox *)mail->box; else if (strcmp(mail->box->storage->name, "raw") == 0) { /* lda uses raw format */ src_mbox = NULL; } else { /* Can't hard link files from the source storage */ return 0; } /* hard link to tmp/ with a newly generated filename and later when we have uidlist locked, move it to new/cur. */ dest_fname = maildir_filename_generate(); i_zero(&do_ctx); do_ctx.dest_path = t_strdup_printf("%s/tmp/%s", mailbox_get_path(&dest_mbox->box), dest_fname); if (src_mbox != NULL) { /* maildir */ if (maildir_file_do(src_mbox, mail->uid, do_hardlink, &do_ctx) < 0) return -1; } else { /* raw / lda */ if (mail_get_special(mail, MAIL_FETCH_STORAGE_ID, &path) < 0 || *path == '\0') return 0; if (do_hardlink(dest_mbox, path, &do_ctx) < 0) return -1; } if (!do_ctx.success) { /* couldn't copy with hardlinking, fallback to copying */ return 0; } /* hardlinked to tmp/, treat as normal copied mail */ mf = maildir_save_add(ctx, dest_fname, mail); if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) == 0) { if (*guid != '\0') maildir_save_set_dest_basename(ctx, mf, guid); } /* remember size/vsize if possible */ old_abort = mail->lookup_abort; mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; if (mail_get_physical_size(mail, &size) < 0) size = (uoff_t)-1; if (mail_get_virtual_size(mail, &vsize) < 0) vsize = (uoff_t)-1; maildir_save_set_sizes(mf, size, vsize); mail->lookup_abort = old_abort; return 1; } int maildir_copy(struct mail_save_context *ctx, struct mail *mail) { struct mailbox_transaction_context *_t = ctx->transaction; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_t->box; int ret; i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (mbox->storage->set->maildir_copy_with_hardlinks && mail_storage_copy_can_use_hardlink(mail->box, &mbox->box)) { T_BEGIN { ret = maildir_copy_hardlink(ctx, mail); } T_END; if (ret != 0) { index_save_context_free(ctx); return ret > 0 ? 0 : -1; } /* non-fatal hardlinking failure, try the slow way */ } return mail_storage_copy(ctx, mail); } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-filename.c0000644000175000017500000000616613165463624021442 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "time-util.h" #include "hostpid.h" #include "maildir-storage.h" #include "maildir-filename.h" const char *maildir_filename_generate(void) { static struct timeval last_tv = { 0, 0 }; struct timeval tv; /* use secs + usecs to guarantee uniqueness within this process. */ if (timeval_cmp(&ioloop_timeval, &last_tv) > 0) tv = ioloop_timeval; else { tv = last_tv; if (++tv.tv_usec == 1000000) { tv.tv_sec++; tv.tv_usec = 0; } } last_tv = tv; return t_strdup_printf("%s.M%sP%s.%s", dec2str(tv.tv_sec), dec2str(tv.tv_usec), my_pid, my_hostname); } bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r) { uoff_t size = 0; for (; *fname != '\0'; fname++) { i_assert(*fname != '/'); if (*fname == ',' && fname[1] == type && fname[2] == '=') { fname += 3; break; } } if (*fname == '\0') return FALSE; while (*fname >= '0' && *fname <= '9') { size = size * 10 + (*fname - '0'); fname++; } if (*fname != MAILDIR_INFO_SEP && *fname != MAILDIR_EXTRA_SEP && *fname != '\0') return FALSE; *size_r = size; return TRUE; } /* a char* hash function from ASU -- from glib */ unsigned int maildir_filename_base_hash(const char *s) { unsigned int g, h = 0; while (*s != MAILDIR_INFO_SEP && *s != '\0') { i_assert(*s != '/'); h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h; } int maildir_filename_base_cmp(const char *fname1, const char *fname2) { while (*fname1 == *fname2 && *fname1 != MAILDIR_INFO_SEP && *fname1 != '\0') { i_assert(*fname1 != '/'); fname1++; fname2++; } if ((*fname1 == '\0' || *fname1 == MAILDIR_INFO_SEP) && (*fname2 == '\0' || *fname2 == MAILDIR_INFO_SEP)) return 0; return *fname1 - *fname2; } static bool maildir_fname_get_usecs(const char *fname, int *usecs_r) { int usecs = 0; /* Assume we already read the timestamp. Next up is "..". Find usecs inside the uniqueness. */ if (*fname != '.') return FALSE; fname++; while (*fname != '\0' && *fname != '.' && *fname != MAILDIR_INFO_SEP) { if (*fname++ == 'M') { while (*fname >= '0' && *fname <= '9') { usecs = usecs * 10 + (*fname - '0'); fname++; } *usecs_r = usecs; return TRUE; } } return FALSE; } int maildir_filename_sort_cmp(const char *fname1, const char *fname2) { const char *s1, *s2; time_t secs1 = 0, secs2 = 0; int ret, usecs1, usecs2; /* sort primarily by the timestamp in file name */ for (s1 = fname1; *s1 >= '0' && *s1 <= '9'; s1++) secs1 = secs1 * 10 + (*s1 - '0'); for (s2 = fname2; *s2 >= '0' && *s2 <= '9'; s2++) secs2 = secs2 * 10 + (*s2 - '0'); ret = (int)((long)secs1 - (long)secs2); if (ret == 0) { /* sort secondarily by microseconds, if they exist */ if (maildir_fname_get_usecs(s1, &usecs1) && maildir_fname_get_usecs(s2, &usecs2)) ret = usecs1 - usecs2; if (ret == 0) { /* fallback to comparing the base file name */ ret = maildir_filename_base_cmp(s1, s2); } } return ret; } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-sync-index.c0000644000175000017500000005647113165463624021747 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "maildir-storage.h" #include "index-sync-changes.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include "maildir-filename-flags.h" #include "maildir-sync.h" #include #include struct maildir_index_sync_context { struct maildir_mailbox *mbox; struct maildir_sync_context *maildir_sync_ctx; struct mail_index_view *view; struct mail_index_sync_ctx *sync_ctx; struct maildir_keywords_sync_ctx *keywords_sync_ctx; struct mail_index_transaction *trans; struct maildir_uidlist_sync_ctx *uidlist_sync_ctx; struct index_sync_changes_context *sync_changes; enum mail_flags flags; ARRAY_TYPE(keyword_indexes) keywords, idx_keywords; uint32_t uid; bool update_maildir_hdr_cur; time_t start_time; unsigned int flag_change_count, expunge_count, new_msgs_count; }; struct maildir_keywords_sync_ctx * maildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx) { return ctx->keywords_sync_ctx; } void maildir_sync_set_new_msgs_count(struct maildir_index_sync_context *ctx, unsigned int count) { ctx->new_msgs_count = count; } static bool maildir_expunge_is_valid_guid(struct maildir_index_sync_context *ctx, uint32_t uid, const char *filename, guid_128_t expunged_guid_128) { guid_128_t guid_128; const char *guid; if (guid_128_is_empty(expunged_guid_128)) { /* no GUID associated with expunge */ return TRUE; } T_BEGIN { guid = maildir_uidlist_lookup_ext(ctx->mbox->uidlist, uid, MAILDIR_UIDLIST_REC_EXT_GUID); if (guid == NULL) guid = t_strcut(filename, *MAILDIR_INFO_SEP_S); mail_generate_guid_128_hash(guid, guid_128); } T_END; if (memcmp(guid_128, expunged_guid_128, sizeof(guid_128)) == 0) return TRUE; mail_storage_set_critical(&ctx->mbox->storage->storage, "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", ctx->mbox->box.vname, ctx->uid, guid_128_to_string(guid_128), guid_128_to_string(expunged_guid_128)); return FALSE; } static int maildir_expunge(struct maildir_mailbox *mbox, const char *path, struct maildir_index_sync_context *ctx) { struct mailbox *box = &mbox->box; ctx->expunge_count++; if (unlink(path) == 0) { if (box->v.sync_notify != NULL) { box->v.sync_notify(box, ctx->uid, MAILBOX_SYNC_TYPE_EXPUNGE); } return 1; } if (errno == ENOENT) return 0; if (UNLINK_EISDIR(errno)) return maildir_lose_unexpected_dir(box->storage, path); mail_storage_set_critical(&mbox->storage->storage, "unlink(%s) failed: %m", path); return -1; } static int maildir_sync_flags(struct maildir_mailbox *mbox, const char *path, struct maildir_index_sync_context *ctx) { struct mailbox *box = &mbox->box; struct stat st; const char *dir, *fname, *newfname, *newpath; enum mail_index_sync_type sync_type; uint8_t flags8; ctx->flag_change_count++; fname = strrchr(path, '/'); i_assert(fname != NULL); fname++; dir = t_strdup_until(path, fname); i_assert(*fname != '\0'); /* get the current flags and keywords */ maildir_filename_flags_get(ctx->keywords_sync_ctx, fname, &ctx->flags, &ctx->keywords); /* apply changes */ flags8 = ctx->flags; index_sync_changes_apply(ctx->sync_changes, NULL, &flags8, &ctx->keywords, &sync_type); ctx->flags = flags8; /* and try renaming with the new name */ newfname = maildir_filename_flags_kw_set(ctx->keywords_sync_ctx, fname, ctx->flags, &ctx->keywords); newpath = t_strconcat(dir, newfname, NULL); if (strcmp(path, newpath) == 0) { /* just make sure that the file still exists. avoid rename() here because it's slow on HFS. */ if (stat(path, &st) < 0) { if (errno == ENOENT) return 0; mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } } else { if (rename(path, newpath) < 0) { if (errno == ENOENT) return 0; if (!ENOSPACE(errno) && errno != EACCES) { mail_storage_set_critical(box->storage, "rename(%s, %s) failed: %m", path, newpath); } return -1; } } if (box->v.sync_notify != NULL) { box->v.sync_notify(box, ctx->uid, index_sync_type_convert(sync_type)); } return 1; } static int maildir_handle_uid_insertion(struct maildir_index_sync_context *ctx, enum maildir_uidlist_rec_flag uflags, const char *filename, uint32_t uid) { int ret; if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) { /* partial syncing */ return 0; } /* most likely a race condition: we read the maildir, then someone else expunged messages and committed changes to index. so, this message shouldn't actually exist. */ if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RACING) == 0) { /* mark it racy and check in next sync */ ctx->mbox->maildir_hdr.cur_check_time = 0; maildir_sync_set_racing(ctx->maildir_sync_ctx); maildir_uidlist_add_flags(ctx->mbox->uidlist, filename, MAILDIR_UIDLIST_REC_FLAG_RACING); return 0; } if (ctx->uidlist_sync_ctx == NULL) { ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, MAILDIR_UIDLIST_SYNC_PARTIAL | MAILDIR_UIDLIST_SYNC_KEEP_STATE, &ctx->uidlist_sync_ctx); if (ret <= 0) return -1; } uflags &= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; maildir_uidlist_sync_remove(ctx->uidlist_sync_ctx, filename); ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx, filename, uflags); i_assert(ret > 0); /* give the new UID to it immediately */ maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx); i_warning("Maildir %s: Expunged message reappeared, giving a new UID " "(old uid=%u, file=%s)%s", mailbox_get_path(&ctx->mbox->box), uid, filename, strncmp(filename, "msg.", 4) != 0 ? "" : " (Your MDA is saving MH files into Maildir?)"); return 0; } int maildir_sync_index_begin(struct maildir_mailbox *mbox, struct maildir_sync_context *maildir_sync_ctx, struct maildir_index_sync_context **ctx_r) { struct mailbox *_box = &mbox->box; struct maildir_index_sync_context *ctx; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; enum mail_index_sync_flags sync_flags; sync_flags = index_storage_get_sync_flags(&mbox->box); /* don't drop recent messages if we're saving messages */ if (maildir_sync_ctx == NULL) sync_flags &= ~MAIL_INDEX_SYNC_FLAG_DROP_RECENT; if (index_storage_expunged_sync_begin(_box, &sync_ctx, &view, &trans, sync_flags) < 0) return -1; ctx = i_new(struct maildir_index_sync_context, 1); ctx->mbox = mbox; ctx->maildir_sync_ctx = maildir_sync_ctx; ctx->sync_ctx = sync_ctx; ctx->view = view; ctx->trans = trans; ctx->keywords_sync_ctx = maildir_keywords_sync_init(mbox->keywords, _box->index); ctx->sync_changes = index_sync_changes_init(ctx->sync_ctx, ctx->view, ctx->trans, maildir_is_backend_readonly(mbox)); ctx->start_time = time(NULL); *ctx_r = ctx; return 0; } static bool maildir_index_header_has_changed(const struct maildir_index_header *old_hdr, const struct maildir_index_header *new_hdr) { #define DIR_DELAYED_REFRESH(hdr, name) \ ((hdr)->name ## _check_time <= \ (hdr)->name ## _mtime + MAILDIR_SYNC_SECS) if (old_hdr->new_mtime != new_hdr->new_mtime || old_hdr->new_mtime_nsecs != new_hdr->new_mtime_nsecs || old_hdr->cur_mtime != new_hdr->cur_mtime || old_hdr->cur_mtime_nsecs != new_hdr->cur_mtime_nsecs || old_hdr->uidlist_mtime != new_hdr->uidlist_mtime || old_hdr->uidlist_mtime_nsecs != new_hdr->uidlist_mtime_nsecs || old_hdr->uidlist_size != new_hdr->uidlist_size) return TRUE; return DIR_DELAYED_REFRESH(old_hdr, new) != DIR_DELAYED_REFRESH(new_hdr, new) || DIR_DELAYED_REFRESH(old_hdr, cur) != DIR_DELAYED_REFRESH(new_hdr, cur); } static void maildir_sync_index_update_ext_header(struct maildir_index_sync_context *ctx) { struct maildir_mailbox *mbox = ctx->mbox; const char *cur_path; const void *data; size_t data_size; struct stat st; cur_path = t_strconcat(mailbox_get_path(&mbox->box), "/cur", NULL); if (ctx->update_maildir_hdr_cur && stat(cur_path, &st) == 0) { if ((time_t)mbox->maildir_hdr.cur_check_time < st.st_mtime) mbox->maildir_hdr.cur_check_time = st.st_mtime; mbox->maildir_hdr.cur_mtime = st.st_mtime; mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st); } mail_index_get_header_ext(mbox->box.view, mbox->maildir_ext_id, &data, &data_size); if (data_size != sizeof(mbox->maildir_hdr) || maildir_index_header_has_changed(data, &mbox->maildir_hdr)) { mail_index_update_header_ext(ctx->trans, mbox->maildir_ext_id, 0, &mbox->maildir_hdr, sizeof(mbox->maildir_hdr)); } } static int maildir_sync_index_finish(struct maildir_index_sync_context *ctx, bool success) { struct maildir_mailbox *mbox = ctx->mbox; unsigned int time_diff; int ret = success ? 0 : -1; time_diff = time(NULL) - ctx->start_time; if (time_diff >= MAILDIR_SYNC_TIME_WARN_SECS) { i_warning("Maildir %s: Synchronization took %u seconds " "(%u new msgs, %u flag change attempts, " "%u expunge attempts)", mailbox_get_path(&ctx->mbox->box), time_diff, ctx->new_msgs_count, ctx->flag_change_count, ctx->expunge_count); mail_index_sync_no_warning(ctx->sync_ctx); } if (ret < 0) mail_index_sync_rollback(&ctx->sync_ctx); else { maildir_sync_index_update_ext_header(ctx); /* Set syncing_commit=TRUE so that if any sync callbacks try to access mails which got lost (eg. expunge callback trying to open the file which was just unlinked) we don't try to start a second index sync and crash. */ mbox->syncing_commit = TRUE; if (mail_index_sync_commit(&ctx->sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); ret = -1; } mbox->syncing_commit = FALSE; } index_storage_expunging_deinit(&mbox->box); maildir_keywords_sync_deinit(&ctx->keywords_sync_ctx); index_sync_changes_deinit(&ctx->sync_changes); i_free(ctx); return ret; } int maildir_sync_index_commit(struct maildir_index_sync_context **_ctx) { struct maildir_index_sync_context *ctx = *_ctx; *_ctx = NULL; return maildir_sync_index_finish(ctx, TRUE); } void maildir_sync_index_rollback(struct maildir_index_sync_context **_ctx) { struct maildir_index_sync_context *ctx = *_ctx; *_ctx = NULL; (void)maildir_sync_index_finish(ctx, FALSE); } static int uint_cmp(const unsigned int *i1, const unsigned int *i2) { if (*i1 < *i2) return -1; else if (*i1 > *i2) return 1; else return 0; } static void maildir_sync_mail_keywords(struct maildir_index_sync_context *ctx, uint32_t seq) { struct mailbox *box = &ctx->mbox->box; struct mail_keywords *kw; unsigned int i, j, old_count, new_count; const unsigned int *old_indexes, *new_indexes; bool have_indexonly_keywords; int diff; mail_index_lookup_keywords(ctx->view, seq, &ctx->idx_keywords); if (index_keyword_array_cmp(&ctx->keywords, &ctx->idx_keywords)) { /* no changes - we should get here usually */ return; } /* sort the keywords */ array_sort(&ctx->idx_keywords, uint_cmp); array_sort(&ctx->keywords, uint_cmp); /* drop keywords that are in index-only. we don't want to touch them. */ old_indexes = array_get(&ctx->idx_keywords, &old_count); have_indexonly_keywords = FALSE; for (i = old_count; i > 0; i--) { if (maildir_keywords_idx_char(ctx->keywords_sync_ctx, old_indexes[i-1]) == '\0') { have_indexonly_keywords = TRUE; array_delete(&ctx->idx_keywords, i-1, 1); } } if (!have_indexonly_keywords) { /* no index-only keywords found, so something changed. just replace them all. */ kw = mail_index_keywords_create_from_indexes(box->index, &ctx->keywords); mail_index_update_keywords(ctx->trans, seq, MODIFY_REPLACE, kw); mail_index_keywords_unref(&kw); return; } /* check again if non-index-only keywords changed */ if (index_keyword_array_cmp(&ctx->keywords, &ctx->idx_keywords)) return; /* we can't reset all the keywords or we'd drop indexonly keywords too. so first remove the unwanted keywords and then add back the wanted ones. we can get these lists easily by removing common elements from old and new keywords. */ new_indexes = array_get(&ctx->keywords, &new_count); for (i = j = 0; i < old_count && j < new_count; ) { diff = (int)old_indexes[i] - (int)new_indexes[j]; if (diff == 0) { array_delete(&ctx->keywords, j, 1); array_delete(&ctx->idx_keywords, i, 1); old_indexes = array_get(&ctx->idx_keywords, &old_count); new_indexes = array_get(&ctx->keywords, &new_count); } else if (diff < 0) { i++; } else { j++; } } if (array_count(&ctx->idx_keywords) > 0) { kw = mail_index_keywords_create_from_indexes(box->index, &ctx->idx_keywords); mail_index_update_keywords(ctx->trans, seq, MODIFY_REMOVE, kw); mail_index_keywords_unref(&kw); } if (array_count(&ctx->keywords) > 0) { kw = mail_index_keywords_create_from_indexes(box->index, &ctx->keywords); mail_index_update_keywords(ctx->trans, seq, MODIFY_ADD, kw); mail_index_keywords_unref(&kw); } } int maildir_sync_index(struct maildir_index_sync_context *ctx, bool partial) { struct maildir_mailbox *mbox = ctx->mbox; struct mail_index_view *view = ctx->view; struct mail_index_view *view2; struct maildir_uidlist_iter_ctx *iter; struct mail_index_transaction *trans = ctx->trans; const struct mail_index_header *hdr; struct mail_index_header empty_hdr; const struct mail_index_record *rec; uint32_t seq, seq2, uid, prev_uid; enum maildir_uidlist_rec_flag uflags; const char *filename; uint32_t uid_validity, next_uid, hdr_next_uid, first_recent_uid; uint32_t first_uid; unsigned int changes = 0; int ret = 0; time_t time_before_sync; guid_128_t expunged_guid_128; enum mail_flags private_flags_mask; bool expunged, full_rescan = FALSE; i_assert(!mbox->syncing_commit); first_uid = 1; hdr = mail_index_get_header(view); uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist); if (uid_validity != hdr->uid_validity && uid_validity != 0 && hdr->uid_validity != 0) { /* uidvalidity changed and index isn't being synced for the first time, reset the index so we can add all messages as new */ i_warning("Maildir %s: UIDVALIDITY changed (%u -> %u)", mailbox_get_path(&ctx->mbox->box), hdr->uid_validity, uid_validity); mail_index_reset(trans); mailbox_recent_flags_reset(&mbox->box); first_uid = hdr->messages_count + 1; i_zero(&empty_hdr); empty_hdr.next_uid = 1; hdr = &empty_hdr; } hdr_next_uid = hdr->next_uid; ctx->mbox->box.tmp_sync_view = view; private_flags_mask = mailbox_get_private_flags_mask(&mbox->box); time_before_sync = time(NULL); mbox->syncing_commit = TRUE; seq = prev_uid = 0; first_recent_uid = I_MAX(hdr->first_recent_uid, 1); i_array_init(&ctx->keywords, MAILDIR_MAX_KEYWORDS); i_array_init(&ctx->idx_keywords, MAILDIR_MAX_KEYWORDS); iter = maildir_uidlist_iter_init(mbox->uidlist); while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) { maildir_filename_flags_get(ctx->keywords_sync_ctx, filename, &ctx->flags, &ctx->keywords); i_assert(uid > prev_uid); prev_uid = uid; /* the private flags are kept only in indexes. don't use them at all even for newly seen mails */ ctx->flags &= ~private_flags_mask; again: seq++; ctx->uid = uid; if (seq > hdr->messages_count) { if (uid < hdr_next_uid) { if (maildir_handle_uid_insertion(ctx, uflags, filename, uid) < 0) ret = -1; seq--; continue; } /* Trust uidlist recent flags only for newly added messages. When saving/copying messages with flags they're stored to cur/ and uidlist treats them as non-recent. */ if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) == 0) { if (uid >= first_recent_uid) first_recent_uid = uid + 1; } hdr_next_uid = uid + 1; mail_index_append(trans, uid, &seq); mail_index_update_flags(trans, seq, MODIFY_REPLACE, ctx->flags); if (array_count(&ctx->keywords) > 0) { struct mail_keywords *kw; kw = mail_index_keywords_create_from_indexes( mbox->box.index, &ctx->keywords); mail_index_update_keywords(trans, seq, MODIFY_REPLACE, kw); mail_index_keywords_unref(&kw); } continue; } rec = mail_index_lookup(view, seq); if (uid > rec->uid) { /* already expunged (no point in showing guid in the expunge record anymore) */ mail_index_expunge(ctx->trans, seq); goto again; } if (uid < rec->uid) { if (maildir_handle_uid_insertion(ctx, uflags, filename, uid) < 0) ret = -1; seq--; continue; } index_sync_changes_read(ctx->sync_changes, ctx->uid, &expunged, expunged_guid_128); if (expunged) { if (!maildir_expunge_is_valid_guid(ctx, ctx->uid, filename, expunged_guid_128)) continue; if (maildir_file_do(mbox, ctx->uid, maildir_expunge, ctx) >= 0) { /* successful expunge */ mail_index_expunge(ctx->trans, seq); } if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx->maildir_sync_ctx); continue; } /* the private flags are stored only in indexes, keep them */ ctx->flags |= rec->flags & private_flags_mask; if (index_sync_changes_have(ctx->sync_changes)) { /* apply flag changes to maildir */ if (maildir_file_do(mbox, ctx->uid, maildir_sync_flags, ctx) < 0) ctx->flags |= MAIL_INDEX_MAIL_FLAG_DIRTY; if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx->maildir_sync_ctx); } if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) { /* partial syncing */ if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) { /* we last saw this mail in new/, but it's not there anymore. possibly expunged, make sure. */ full_rescan = TRUE; } continue; } if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) { /* we haven't been able to update maildir with this record's flag changes. don't sync them. */ continue; } if (ctx->flags != (rec->flags & MAIL_FLAGS_NONRECENT)) { mail_index_update_flags(trans, seq, MODIFY_REPLACE, ctx->flags); } maildir_sync_mail_keywords(ctx, seq); } maildir_uidlist_iter_deinit(&iter); if (!partial) { /* expunge the rest */ for (seq++; seq <= hdr->messages_count; seq++) mail_index_expunge(ctx->trans, seq); } /* add \Recent flags. use updated view so it contains newly appended messages. */ view2 = mail_index_transaction_open_updated_view(trans); if (mail_index_lookup_seq_range(view2, first_recent_uid, (uint32_t)-1, &seq, &seq2) && seq2 >= first_uid) { if (seq < first_uid) { /* UIDVALIDITY changed, skip over the old messages */ seq = first_uid; } mailbox_recent_flags_set_seqs(&mbox->box, view2, seq, seq2); } mail_index_view_close(&view2); if (ctx->uidlist_sync_ctx != NULL) { if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, TRUE) < 0) ret = -1; } if (mbox->box.v.sync_notify != NULL) mbox->box.v.sync_notify(&mbox->box, 0, 0); ctx->mbox->box.tmp_sync_view = NULL; /* check cur/ mtime later. if we came here from saving messages they could still be moved to cur/ directory. */ ctx->update_maildir_hdr_cur = TRUE; mbox->maildir_hdr.cur_check_time = time_before_sync; if (uid_validity == 0) { uid_validity = hdr->uid_validity != 0 ? hdr->uid_validity : maildir_get_uidvalidity_next(mbox->box.list); maildir_uidlist_set_uid_validity(mbox->uidlist, uid_validity); } maildir_uidlist_set_next_uid(mbox->uidlist, hdr_next_uid, FALSE); if (uid_validity != hdr->uid_validity) { mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } next_uid = maildir_uidlist_get_next_uid(mbox->uidlist); if (hdr_next_uid < next_uid) { mail_index_update_header(trans, offsetof(struct mail_index_header, next_uid), &next_uid, sizeof(next_uid), FALSE); } i_assert(hdr->first_recent_uid <= first_recent_uid); if (hdr->first_recent_uid < first_recent_uid) { mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } array_free(&ctx->keywords); array_free(&ctx->idx_keywords); mbox->syncing_commit = FALSE; return ret < 0 ? -1 : (full_rescan ? 0 : 1); } static unsigned int maildir_list_get_ext_id(struct maildir_mailbox *mbox, struct mail_index_view *view) { if (mbox->maildir_list_index_ext_id == (uint32_t)-1) { mbox->maildir_list_index_ext_id = mail_index_ext_register(mail_index_view_get_index(view), "maildir", 0, sizeof(struct maildir_list_index_record), sizeof(uint32_t)); } return mbox->maildir_list_index_ext_id; } int maildir_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; const struct maildir_list_index_record *rec; const void *data; const char *root_dir, *new_dir, *cur_dir; struct stat st; uint32_t ext_id; bool expunged; int ret; ret = index_storage_list_index_has_changed(box, list_view, seq); if (ret != 0 || box->storage->set->mailbox_list_index_very_dirty_syncs) return ret; if (mbox->storage->set->maildir_very_dirty_syncs) { /* we don't track cur/new directories with dirty syncs */ return 0; } ext_id = maildir_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); rec = data; if (rec == NULL || expunged || rec->new_mtime == 0 || rec->cur_mtime == 0) { /* doesn't exist, not synced or dirty-synced */ return 1; } ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &root_dir); if (ret < 0) return ret; i_assert(ret > 0); /* check if new/ changed */ new_dir = t_strconcat(root_dir, "/new", NULL); if (stat(new_dir, &st) < 0) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", new_dir); return -1; } if ((time_t)rec->new_mtime != st.st_mtime) return 1; /* check if cur/ changed */ cur_dir = t_strconcat(root_dir, "/cur", NULL); if (stat(cur_dir, &st) < 0) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", cur_dir); return -1; } if ((time_t)rec->cur_mtime != st.st_mtime) return 1; return 0; } void maildir_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; struct mail_index_view *list_view; const struct maildir_index_header *mhdr = &mbox->maildir_hdr; const struct maildir_list_index_record *old_rec; struct maildir_list_index_record new_rec; const void *data; uint32_t ext_id; bool expunged; index_storage_list_index_update_sync(box, trans, seq); if (mbox->storage->set->maildir_very_dirty_syncs) { /* we don't track cur/new directories with dirty syncs */ return; } /* get the current record */ list_view = mail_index_transaction_get_view(trans); ext_id = maildir_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); if (expunged) return; old_rec = data; i_zero(&new_rec); if (mhdr->new_check_time <= mhdr->new_mtime + MAILDIR_SYNC_SECS || mhdr->cur_check_time <= mhdr->cur_mtime + MAILDIR_SYNC_SECS) { /* dirty, we need a refresh next time */ } else { new_rec.new_mtime = mhdr->new_mtime; new_rec.cur_mtime = mhdr->cur_mtime; } if (old_rec == NULL || memcmp(old_rec, &new_rec, sizeof(*old_rec)) != 0) mail_index_update_ext(trans, seq, ext_id, &new_rec, NULL); } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-uidlist.c0000644000175000017500000015677213165463624021350 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ /* Version 1 format has been used for most versions of Dovecot up to v1.0.x. It's also compatible with Courier IMAP's courierimapuiddb file. The format is: header: 1 entry: -- Version 2 format was written by a few development Dovecot versions, but v1.0.x still parses the format. The format has field after . -- Version 3 format is an extensible format used by Dovecot v1.1 and later. It's also parsed by v1.0.2 (and later). The format is: header: 3 [ ...] entry: [ ...] : See enum maildir_uidlist_*_ext_key for used keys. */ #include "lib.h" #include "array.h" #include "hash.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "file-dotlock.h" #include "nfs-workarounds.h" #include "eacces-error.h" #include "maildir-storage.h" #include "maildir-filename.h" #include "maildir-uidlist.h" #include #include /* NFS: How many times to retry reading dovecot-uidlist file if ESTALE error occurs in the middle of reading it */ #define UIDLIST_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT #define UIDLIST_VERSION 3 #define UIDLIST_COMPRESS_PERCENTAGE 75 #define UIDLIST_IS_LOCKED(uidlist) \ ((uidlist)->lock_count > 0) struct maildir_uidlist_rec { uint32_t uid; uint32_t flags; char *filename; unsigned char *extensions; /* \0[\0 ...]\0 */ }; ARRAY_DEFINE_TYPE(maildir_uidlist_rec_p, struct maildir_uidlist_rec *); HASH_TABLE_DEFINE_TYPE(path_to_maildir_uidlist_rec, char *, struct maildir_uidlist_rec *); struct maildir_uidlist { struct mailbox *box; char *path; struct maildir_index_header *mhdr; int fd; dev_t fd_dev; ino_t fd_ino; off_t fd_size; unsigned int lock_count; struct dotlock_settings dotlock_settings; struct dotlock *dotlock; pool_t record_pool; ARRAY_TYPE(maildir_uidlist_rec_p) records; HASH_TABLE_TYPE(path_to_maildir_uidlist_rec) files; unsigned int change_counter; unsigned int version; unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid; unsigned int hdr_next_uid; unsigned int read_records_count, read_line_count; uoff_t last_read_offset; string_t *hdr_extensions; guid_128_t mailbox_guid; unsigned int recreate:1; unsigned int recreate_on_change:1; unsigned int initial_read:1; unsigned int initial_hdr_read:1; unsigned int retry_rewind:1; unsigned int locked_refresh:1; unsigned int unsorted:1; unsigned int have_mailbox_guid:1; unsigned int opened_readonly:1; }; struct maildir_uidlist_sync_ctx { struct maildir_uidlist *uidlist; enum maildir_uidlist_sync_flags sync_flags; pool_t record_pool; ARRAY_TYPE(maildir_uidlist_rec_p) records; HASH_TABLE_TYPE(path_to_maildir_uidlist_rec) files; unsigned int first_unwritten_pos, first_new_pos; unsigned int new_files_count; unsigned int finish_change_counter; unsigned int partial:1; unsigned int finished:1; unsigned int changed:1; unsigned int failed:1; unsigned int locked:1; }; struct maildir_uidlist_iter_ctx { struct maildir_uidlist *uidlist; struct maildir_uidlist_rec *const *next, *const *end; unsigned int change_counter; uint32_t prev_uid; }; static int maildir_uidlist_open_latest(struct maildir_uidlist *uidlist); static bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx, struct maildir_uidlist_rec **rec_r); static int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist, bool nonblock, bool refresh, bool refresh_when_locked) { struct mailbox *box = uidlist->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *path = uidlist->path; mode_t old_mask; const enum dotlock_create_flags dotlock_flags = nonblock ? DOTLOCK_CREATE_FLAG_NONBLOCK : 0; int i, ret; if (uidlist->lock_count > 0) { if (!uidlist->locked_refresh && refresh_when_locked) { if (maildir_uidlist_refresh(uidlist) < 0) return -1; } uidlist->lock_count++; return 1; } index_storage_lock_notify_reset(box); for (i = 0;; i++) { old_mask = umask(0777 & ~perm->file_create_mode); ret = file_dotlock_create(&uidlist->dotlock_settings, path, dotlock_flags, &uidlist->dotlock); umask(old_mask); if (ret > 0) break; /* failure */ if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT); return 0; } if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) { if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", eacces_error_get_creating("file_dotlock_create", path)); } else { mail_storage_set_critical(box->storage, "file_dotlock_create(%s) failed: %m", path); } return -1; } /* the control dir doesn't exist. create it unless the whole mailbox was just deleted. */ if (!maildir_set_deleted(uidlist->box)) return -1; } uidlist->lock_count++; uidlist->locked_refresh = FALSE; if (refresh) { /* make sure we have the latest changes before changing anything */ if (maildir_uidlist_refresh(uidlist) < 0) { maildir_uidlist_unlock(uidlist); return -1; } } return 1; } int maildir_uidlist_lock(struct maildir_uidlist *uidlist) { return maildir_uidlist_lock_timeout(uidlist, FALSE, TRUE, FALSE); } int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist) { return maildir_uidlist_lock_timeout(uidlist, TRUE, TRUE, FALSE); } int maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist) { i_assert(UIDLIST_IS_LOCKED(uidlist)); return file_dotlock_touch(uidlist->dotlock); } bool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist) { return UIDLIST_IS_LOCKED(uidlist); } bool maildir_uidlist_is_read(struct maildir_uidlist *uidlist) { return uidlist->initial_read; } bool maildir_uidlist_is_open(struct maildir_uidlist *uidlist) { return uidlist->fd != -1; } void maildir_uidlist_unlock(struct maildir_uidlist *uidlist) { i_assert(uidlist->lock_count > 0); if (--uidlist->lock_count > 0) return; uidlist->locked_refresh = FALSE; file_dotlock_delete(&uidlist->dotlock); } static bool dotlock_callback(unsigned int secs_left, bool stale, void *context) { struct mailbox *box = context; index_storage_lock_notify(box, stale ? MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE : MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, secs_left); return TRUE; } struct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox) { struct mailbox *box = &mbox->box; struct maildir_uidlist *uidlist; const char *control_dir; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL, &control_dir) <= 0) i_unreached(); uidlist = i_new(struct maildir_uidlist, 1); uidlist->box = box; uidlist->mhdr = &mbox->maildir_hdr; uidlist->fd = -1; uidlist->path = i_strconcat(control_dir, "/"MAILDIR_UIDLIST_NAME, NULL); i_array_init(&uidlist->records, 128); hash_table_create(&uidlist->files, default_pool, 4096, maildir_filename_base_hash, maildir_filename_base_cmp); uidlist->next_uid = 1; uidlist->hdr_extensions = str_new(default_pool, 128); uidlist->dotlock_settings.use_io_notify = TRUE; uidlist->dotlock_settings.use_excl_lock = box->storage->set->dotlock_use_excl; uidlist->dotlock_settings.nfs_flush = box->storage->set->mail_nfs_storage; uidlist->dotlock_settings.timeout = mail_storage_get_lock_timeout(box->storage, MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT + 2); uidlist->dotlock_settings.stale_timeout = MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT; uidlist->dotlock_settings.callback = dotlock_callback; uidlist->dotlock_settings.context = box; uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix; return uidlist; } static void maildir_uidlist_close(struct maildir_uidlist *uidlist) { struct mail_storage *storage = uidlist->box->storage; if (uidlist->fd != -1) { if (close(uidlist->fd) < 0) { mail_storage_set_critical(storage, "close(%s) failed: %m", uidlist->path); } uidlist->fd = -1; uidlist->fd_ino = 0; } uidlist->last_read_offset = 0; uidlist->read_line_count = 0; } static void maildir_uidlist_reset(struct maildir_uidlist *uidlist) { maildir_uidlist_close(uidlist); uidlist->last_seen_uid = 0; uidlist->initial_hdr_read = FALSE; uidlist->read_records_count = 0; hash_table_clear(uidlist->files, FALSE); array_clear(&uidlist->records); } void maildir_uidlist_deinit(struct maildir_uidlist **_uidlist) { struct maildir_uidlist *uidlist = *_uidlist; i_assert(!UIDLIST_IS_LOCKED(uidlist)); *_uidlist = NULL; (void)maildir_uidlist_update(uidlist); maildir_uidlist_close(uidlist); hash_table_destroy(&uidlist->files); if (uidlist->record_pool != NULL) pool_unref(&uidlist->record_pool); array_free(&uidlist->records); str_free(&uidlist->hdr_extensions); i_free(uidlist->path); i_free(uidlist); } static int maildir_uid_cmp(struct maildir_uidlist_rec *const *rec1, struct maildir_uidlist_rec *const *rec2) { return (*rec1)->uid < (*rec2)->uid ? -1 : (*rec1)->uid > (*rec2)->uid ? 1 : 0; } static void ATTR_FORMAT(2, 3) maildir_uidlist_set_corrupted(struct maildir_uidlist *uidlist, const char *fmt, ...) { struct mail_storage *storage = uidlist->box->storage; va_list args; va_start(args, fmt); if (uidlist->retry_rewind) { mail_storage_set_critical(storage, "Broken or unexpectedly changed file %s " "line %u: %s - re-reading from beginning", uidlist->path, uidlist->read_line_count, t_strdup_vprintf(fmt, args)); } else { mail_storage_set_critical(storage, "Broken file %s line %u: %s", uidlist->path, uidlist->read_line_count, t_strdup_vprintf(fmt, args)); } va_end(args); } static void maildir_uidlist_update_hdr(struct maildir_uidlist *uidlist, const struct stat *st) { struct maildir_index_header *mhdr = uidlist->mhdr; if (mhdr->uidlist_mtime == 0 && uidlist->version != UIDLIST_VERSION) { /* upgrading from older verson. don't update the uidlist times until it uses the new format */ uidlist->recreate = TRUE; return; } mhdr->uidlist_mtime = st->st_mtime; mhdr->uidlist_mtime_nsecs = ST_MTIME_NSEC(*st); mhdr->uidlist_size = st->st_size; } static unsigned int maildir_uidlist_records_array_delete(struct maildir_uidlist *uidlist, struct maildir_uidlist_rec *rec) { struct maildir_uidlist_rec *const *recs, *const *pos; unsigned int idx, count; recs = array_get(&uidlist->records, &count); if (!uidlist->unsorted) { pos = array_bsearch(&uidlist->records, &rec, maildir_uid_cmp); i_assert(pos != NULL); idx = pos - recs; } else { for (idx = 0; idx < count; idx++) { if (recs[idx]->uid == rec->uid) break; } i_assert(idx != count); } array_delete(&uidlist->records, idx, 1); return idx; } static bool maildir_uidlist_read_extended(struct maildir_uidlist *uidlist, const char **line_p, struct maildir_uidlist_rec *rec) { const char *start, *line = *line_p; buffer_t *buf; buf = buffer_create_dynamic(pool_datastack_create(), 128); while (*line != '\0' && *line != ':') { /* skip over an extension field */ start = line; while (*line != ' ' && *line != '\0') line++; if (MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*start)) { buffer_append(buf, start, line - start); buffer_append_c(buf, '\0'); } else { maildir_uidlist_set_corrupted(uidlist, "Invalid extension record, removing: %s", t_strdup_until(start, line)); uidlist->recreate = TRUE; } while (*line == ' ') line++; } if (buf->used > 0) { /* save the extensions */ buffer_append_c(buf, '\0'); rec->extensions = p_malloc(uidlist->record_pool, buf->used); memcpy(rec->extensions, buf->data, buf->used); } if (*line == ':') line++; if (*line == '\0') return FALSE; *line_p = line; return TRUE; } static bool maildir_uidlist_next(struct maildir_uidlist *uidlist, const char *line) { struct maildir_uidlist_rec *rec, *old_rec, *const *recs; unsigned int count; uint32_t uid; uid = 0; while (*line >= '0' && *line <= '9') { uid = uid*10 + (*line - '0'); line++; } if (uid == 0 || *line != ' ') { /* invalid file */ maildir_uidlist_set_corrupted(uidlist, "Invalid data: %s", line); return FALSE; } if (uid <= uidlist->prev_read_uid) { maildir_uidlist_set_corrupted(uidlist, "UIDs not ordered (%u >= %u)", uid, uidlist->prev_read_uid); return FALSE; } if (uid >= (uint32_t)-1) { maildir_uidlist_set_corrupted(uidlist, "UID too high (%u)", uid); return FALSE; } uidlist->prev_read_uid = uid; if (uid <= uidlist->last_seen_uid) { /* we already have this */ return TRUE; } uidlist->last_seen_uid = uid; if (uid >= uidlist->next_uid && uidlist->version == 1) { maildir_uidlist_set_corrupted(uidlist, "UID larger than next_uid (%u >= %u)", uid, uidlist->next_uid); return FALSE; } rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1); rec->uid = uid; rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; while (*line == ' ') line++; if (uidlist->version == UIDLIST_VERSION) { /* read extended fields */ bool ret; T_BEGIN { ret = maildir_uidlist_read_extended(uidlist, &line, rec); } T_END; if (!ret) { maildir_uidlist_set_corrupted(uidlist, "Invalid extended fields: %s", line); return FALSE; } } if (strchr(line, '/') != NULL) { maildir_uidlist_set_corrupted(uidlist, "%s: Broken filename at line %u: %s", uidlist->path, uidlist->read_line_count, line); return FALSE; } old_rec = hash_table_lookup(uidlist->files, line); if (old_rec == NULL) { /* no conflicts */ } else if (old_rec->uid == uid) { /* most likely this is a record we saved ourself, but couldn't update last_seen_uid because uidlist wasn't refreshed while it was locked. another possibility is a duplicate file record. currently it would be a bug, but not that big of a deal. also perhaps in future such duplicate lines could be used to update extended fields. so just let it through anyway. we'll waste a bit of memory here by allocating the record twice, but that's not really a problem. */ rec->filename = old_rec->filename; hash_table_insert(uidlist->files, rec->filename, rec); uidlist->unsorted = TRUE; return TRUE; } else { /* This can happen if expunged file is moved back and the file was appended to uidlist. */ i_warning("%s: Duplicate file entry at line %u: " "%s (uid %u -> %u)%s", uidlist->path, uidlist->read_line_count, line, old_rec->uid, uid, uidlist->retry_rewind ? " - retrying by re-reading from beginning" : ""); if (uidlist->retry_rewind) return FALSE; /* Delete the old UID */ (void)maildir_uidlist_records_array_delete(uidlist, old_rec); /* Replace the old record with this new one */ *old_rec = *rec; rec = old_rec; uidlist->recreate = TRUE; } recs = array_get(&uidlist->records, &count); if (count > 0 && recs[count-1]->uid > uid) { /* we most likely have some records in the array that we saved ourself without refreshing uidlist */ uidlist->unsorted = TRUE; } rec->filename = p_strdup(uidlist->record_pool, line); hash_table_insert(uidlist->files, rec->filename, rec); array_append(&uidlist->records, &rec, 1); return TRUE; } static int maildir_uidlist_read_v3_header(struct maildir_uidlist *uidlist, const char *line, unsigned int *uid_validity_r, unsigned int *next_uid_r) { char key; str_truncate(uidlist->hdr_extensions, 0); while (*line != '\0') { const char *value; key = *line; value = ++line; while (*line != '\0' && *line != ' ') line++; value = t_strdup_until(value, line); switch (key) { case MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY: if (str_to_uint(value, uid_validity_r) < 0) { maildir_uidlist_set_corrupted(uidlist, "Invalid mailbox UID_VALIDITY: %s", value); return -1; } break; case MAILDIR_UIDLIST_HDR_EXT_NEXT_UID: if (str_to_uint(value, next_uid_r) < 0) { maildir_uidlist_set_corrupted(uidlist, "Invalid mailbox NEXT_UID: %s", value); return -1; } break; case MAILDIR_UIDLIST_HDR_EXT_GUID: if (guid_128_from_string(value, uidlist->mailbox_guid) < 0) { maildir_uidlist_set_corrupted(uidlist, "Invalid mailbox GUID: %s", value); return -1; } uidlist->have_mailbox_guid = TRUE; break; default: if (str_len(uidlist->hdr_extensions) > 0) str_append_c(uidlist->hdr_extensions, ' '); str_printfa(uidlist->hdr_extensions, "%c%s", key, value); break; } while (*line == ' ') line++; } return 0; } static int maildir_uidlist_read_header(struct maildir_uidlist *uidlist, struct istream *input) { unsigned int uid_validity = 0, next_uid = 0; const char *line; int ret; line = i_stream_read_next_line(input); if (line == NULL) { /* I/O error / empty file */ return input->stream_errno == 0 ? 0 : -1; } uidlist->read_line_count = 1; if (*line < '0' || *line > '9' || line[1] != ' ') { maildir_uidlist_set_corrupted(uidlist, "Corrupted header (invalid version number)"); return 0; } uidlist->version = *line - '0'; line += 2; switch (uidlist->version) { case 1: if (sscanf(line, "%u %u", &uid_validity, &next_uid) != 2) { maildir_uidlist_set_corrupted(uidlist, "Corrupted header (version 1)"); return 0; } break; case UIDLIST_VERSION: T_BEGIN { ret = maildir_uidlist_read_v3_header(uidlist, line, &uid_validity, &next_uid); } T_END; if (ret < 0) return 0; break; default: maildir_uidlist_set_corrupted(uidlist, "Unsupported version %u", uidlist->version); return 0; } if (uid_validity == 0 || next_uid == 0) { maildir_uidlist_set_corrupted(uidlist, "Broken header (uidvalidity = %u, next_uid=%u)", uid_validity, next_uid); return 0; } if (uid_validity == uidlist->uid_validity && next_uid < uidlist->hdr_next_uid) { maildir_uidlist_set_corrupted(uidlist, "next_uid header was lowered (%u -> %u)", uidlist->hdr_next_uid, next_uid); return 0; } uidlist->uid_validity = uid_validity; uidlist->next_uid = next_uid; uidlist->hdr_next_uid = next_uid; return 1; } static void maildir_uidlist_records_sort_by_uid(struct maildir_uidlist *uidlist) { array_sort(&uidlist->records, maildir_uid_cmp); uidlist->unsorted = FALSE; } static int maildir_uidlist_update_read(struct maildir_uidlist *uidlist, bool *retry_r, bool try_retry) { struct mail_storage *storage = uidlist->box->storage; const char *line; uint32_t orig_next_uid, orig_uid_validity; struct istream *input; struct stat st; uoff_t last_read_offset; int fd, ret; bool readonly = FALSE; *retry_r = FALSE; if (uidlist->fd == -1) { fd = nfs_safe_open(uidlist->path, O_RDWR); if (fd == -1 && errno == EACCES) { fd = nfs_safe_open(uidlist->path, O_RDONLY); readonly = TRUE; } if (fd == -1) { if (errno != ENOENT) { mail_storage_set_critical(storage, "open(%s) failed: %m", uidlist->path); return -1; } return 0; } last_read_offset = 0; } else { /* the file was updated */ fd = uidlist->fd; if (lseek(fd, 0, SEEK_SET) < 0) { if (errno == ESTALE && try_retry) { *retry_r = TRUE; return -1; } mail_storage_set_critical(storage, "lseek(%s) failed: %m", uidlist->path); return -1; } uidlist->fd = -1; uidlist->fd_ino = 0; last_read_offset = uidlist->last_read_offset; uidlist->last_read_offset = 0; } if (fstat(fd, &st) < 0) { i_close_fd(&fd); if (errno == ESTALE && try_retry) { *retry_r = TRUE; return -1; } mail_storage_set_critical(storage, "fstat(%s) failed: %m", uidlist->path); return -1; } if (uidlist->record_pool == NULL) { uidlist->record_pool = pool_alloconly_create(MEMPOOL_GROWING "uidlist record_pool", nearest_power(st.st_size - st.st_size/8)); } input = i_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_seek(input, last_read_offset); orig_uid_validity = uidlist->uid_validity; orig_next_uid = uidlist->next_uid; ret = input->v_offset != 0 ? 1 : maildir_uidlist_read_header(uidlist, input); if (ret > 0) { uidlist->prev_read_uid = 0; uidlist->change_counter++; uidlist->retry_rewind = last_read_offset != 0 && try_retry; ret = 1; while ((line = i_stream_read_next_line(input)) != NULL) { uidlist->read_records_count++; uidlist->read_line_count++; if (!maildir_uidlist_next(uidlist, line)) { if (!uidlist->retry_rewind) ret = 0; else { ret = -1; *retry_r = TRUE; } break; } } uidlist->retry_rewind = FALSE; if (input->stream_errno != 0) ret = -1; if (uidlist->unsorted) { uidlist->recreate_on_change = TRUE; maildir_uidlist_records_sort_by_uid(uidlist); } if (uidlist->next_uid <= uidlist->prev_read_uid) uidlist->next_uid = uidlist->prev_read_uid + 1; if (ret > 0 && uidlist->uid_validity != orig_uid_validity && orig_uid_validity != 0) { uidlist->recreate = TRUE; } else if (ret > 0 && uidlist->next_uid < orig_next_uid) { mail_storage_set_critical(storage, "%s: next_uid was lowered (%u -> %u, hdr=%u)", uidlist->path, orig_next_uid, uidlist->next_uid, uidlist->hdr_next_uid); uidlist->recreate = TRUE; uidlist->next_uid = orig_next_uid; } } if (ret == 0) { /* file is broken */ i_unlink(uidlist->path); } else if (ret > 0) { /* success */ if (readonly) uidlist->recreate_on_change = TRUE; uidlist->fd = fd; uidlist->fd_dev = st.st_dev; uidlist->fd_ino = st.st_ino; uidlist->fd_size = st.st_size; uidlist->last_read_offset = input->v_offset; maildir_uidlist_update_hdr(uidlist, &st); } else if (!*retry_r) { /* I/O error */ if (input->stream_errno == ESTALE && try_retry) *retry_r = TRUE; else { mail_storage_set_critical(storage, "read(%s) failed: %s", uidlist->path, i_stream_get_error(input)); } uidlist->last_read_offset = 0; } i_stream_destroy(&input); if (ret <= 0) { if (close(fd) < 0) { mail_storage_set_critical(storage, "close(%s) failed: %m", uidlist->path); } } return ret; } static int maildir_uidlist_stat(struct maildir_uidlist *uidlist, struct stat *st_r) { struct mail_storage *storage = uidlist->box->storage; if (storage->set->mail_nfs_storage) { nfs_flush_file_handle_cache(uidlist->path); nfs_flush_attr_cache_unlocked(uidlist->path); } if (nfs_safe_stat(uidlist->path, st_r) < 0) { if (errno != ENOENT) { mail_storage_set_critical(storage, "stat(%s) failed: %m", uidlist->path); return -1; } return 0; } return 1; } static int maildir_uidlist_has_changed(struct maildir_uidlist *uidlist, bool *recreated_r) { struct mail_storage *storage = uidlist->box->storage; struct stat st; int ret; *recreated_r = FALSE; if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0) return -1; if (ret == 0) { *recreated_r = TRUE; return 1; } if (st.st_ino != uidlist->fd_ino || !CMP_DEV_T(st.st_dev, uidlist->fd_dev)) { /* file recreated */ *recreated_r = TRUE; return 1; } if (storage->set->mail_nfs_storage) { /* NFS: either the file hasn't been changed, or it has already been deleted and the inodes just happen to be the same. check if the fd is still valid. */ if (fstat(uidlist->fd, &st) < 0) { if (errno == ESTALE) { *recreated_r = TRUE; return 1; } mail_storage_set_critical(storage, "fstat(%s) failed: %m", uidlist->path); return -1; } } if (st.st_size != uidlist->fd_size) { /* file modified but not recreated */ return 1; } else { /* unchanged */ return 0; } } static int maildir_uidlist_open_latest(struct maildir_uidlist *uidlist) { bool recreated; int ret; if (uidlist->fd != -1) { ret = maildir_uidlist_has_changed(uidlist, &recreated); if (ret <= 0) { if (UIDLIST_IS_LOCKED(uidlist)) uidlist->locked_refresh = TRUE; return ret < 0 ? -1 : 1; } if (!recreated) return 0; maildir_uidlist_reset(uidlist); } uidlist->fd = nfs_safe_open(uidlist->path, O_RDWR); if (uidlist->fd == -1 && errno == EACCES) { uidlist->fd = nfs_safe_open(uidlist->path, O_RDONLY); uidlist->recreate_on_change = TRUE; } if (uidlist->fd == -1 && errno != ENOENT) { mail_storage_set_critical(uidlist->box->storage, "open(%s) failed: %m", uidlist->path); return -1; } return 0; } int maildir_uidlist_refresh(struct maildir_uidlist *uidlist) { unsigned int i; bool retry; int ret; if (maildir_uidlist_open_latest(uidlist) < 0) return -1; for (i = 0; ; i++) { ret = maildir_uidlist_update_read(uidlist, &retry, i < UIDLIST_ESTALE_RETRY_COUNT); if (!retry) break; /* ESTALE - try reopening and rereading */ maildir_uidlist_close(uidlist); } if (ret >= 0) { uidlist->initial_read = TRUE; uidlist->initial_hdr_read = TRUE; if (UIDLIST_IS_LOCKED(uidlist)) uidlist->locked_refresh = TRUE; if (!uidlist->have_mailbox_guid) { uidlist->recreate = TRUE; (void)maildir_uidlist_update(uidlist); } } return ret; } int maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist) { const struct maildir_index_header *mhdr = uidlist->mhdr; struct mail_index *index = uidlist->box->index; struct mail_index_view *view; const struct mail_index_header *hdr; struct stat st; int ret; i_assert(UIDLIST_IS_LOCKED(uidlist)); if (uidlist->fd != -1) return maildir_uidlist_refresh(uidlist); if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0) return ret; if (ret > 0 && st.st_size == mhdr->uidlist_size && st.st_mtime == (time_t)mhdr->uidlist_mtime && ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), mhdr->uidlist_mtime_nsecs) && (!mail_index_is_in_memory(index) || st.st_mtime < ioloop_time-1)) { /* index is up-to-date. look up the uidvalidity and next-uid from it. we'll need to create a new view temporarily to make sure we get the latest values. */ view = mail_index_view_open(index); hdr = mail_index_get_header(view); uidlist->uid_validity = hdr->uid_validity; uidlist->next_uid = hdr->next_uid; uidlist->initial_hdr_read = TRUE; mail_index_view_close(&view); if (UIDLIST_IS_LOCKED(uidlist)) uidlist->locked_refresh = TRUE; return 1; } else { return maildir_uidlist_refresh(uidlist); } } static int maildir_uid_bsearch_cmp(const uint32_t *uidp, struct maildir_uidlist_rec *const *recp) { return *uidp < (*recp)->uid ? -1 : *uidp > (*recp)->uid ? 1 : 0; } static int maildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid, struct maildir_uidlist_rec **rec_r) { struct maildir_uidlist_rec *const *pos; if (!uidlist->initial_read) { /* first time we need to read uidlist */ if (maildir_uidlist_refresh(uidlist) < 0) return -1; } pos = array_bsearch(&uidlist->records, &uid, maildir_uid_bsearch_cmp); if (pos == NULL) { *rec_r = NULL; return 0; } *rec_r = *pos; return 1; } int maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_flag *flags_r, const char **fname_r) { struct maildir_uidlist_rec *rec; int ret; if ((ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec)) <= 0) return ret; *flags_r = rec->flags; *fname_r = rec->filename; return 1; } const char * maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key) { struct maildir_uidlist_rec *rec; const unsigned char *p; int ret; ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec); if (ret <= 0 || rec->extensions == NULL) return NULL; p = rec->extensions; while (*p != '\0') { /* \0 */ if (*p == (unsigned char)key) return (const char *)p + 1; p += strlen((const char *)p) + 1; } return NULL; } uint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist) { return uidlist->uid_validity; } uint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist) { return !uidlist->initial_hdr_read ? 0 : uidlist->next_uid; } int maildir_uidlist_get_mailbox_guid(struct maildir_uidlist *uidlist, guid_128_t mailbox_guid) { if (!uidlist->initial_hdr_read) { if (maildir_uidlist_refresh(uidlist) < 0) return -1; } if (!uidlist->have_mailbox_guid) { uidlist->recreate = TRUE; if (maildir_uidlist_update(uidlist) < 0) return -1; } memcpy(mailbox_guid, uidlist->mailbox_guid, GUID_128_SIZE); return 0; } void maildir_uidlist_set_mailbox_guid(struct maildir_uidlist *uidlist, const guid_128_t mailbox_guid) { if (memcmp(uidlist->mailbox_guid, mailbox_guid, sizeof(uidlist->mailbox_guid)) != 0) { memcpy(uidlist->mailbox_guid, mailbox_guid, sizeof(uidlist->mailbox_guid)); uidlist->recreate = TRUE; } } void maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist, uint32_t uid_validity) { i_assert(uid_validity != 0); if (uid_validity != uidlist->uid_validity) { uidlist->uid_validity = uid_validity; uidlist->recreate = TRUE; } } void maildir_uidlist_set_next_uid(struct maildir_uidlist *uidlist, uint32_t next_uid, bool force) { if (uidlist->next_uid < next_uid || force) { i_assert(next_uid != 0); uidlist->next_uid = next_uid; uidlist->recreate = TRUE; } } static void maildir_uidlist_rec_set_ext(struct maildir_uidlist_rec *rec, pool_t pool, enum maildir_uidlist_rec_ext_key key, const char *value) { const unsigned char *p; buffer_t *buf; size_t len; /* copy existing extensions, except for the one we're updating */ buf = buffer_create_dynamic(pool_datastack_create(), 128); if (rec->extensions != NULL) { p = rec->extensions; while (*p != '\0') { /* \0 */ i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*p)); len = strlen((const char *)p) + 1; if (*p != (unsigned char)key) buffer_append(buf, p, len); p += len; } } if (value != NULL) { buffer_append_c(buf, key); buffer_append(buf, value, strlen(value) + 1); } buffer_append_c(buf, '\0'); rec->extensions = p_malloc(pool, buf->used); memcpy(rec->extensions, buf->data, buf->used); } static void ATTR_NULL(4) maildir_uidlist_set_ext_internal(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key, const char *value) { struct maildir_uidlist_rec *rec; int ret; i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(key)); ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec); if (ret <= 0) { if (ret < 0) return; /* maybe it's a new message */ if (maildir_uidlist_refresh(uidlist) < 0) return; if (maildir_uidlist_lookup_rec(uidlist, uid, &rec) <= 0) { /* message is already expunged, ignore */ return; } } T_BEGIN { maildir_uidlist_rec_set_ext(rec, uidlist->record_pool, key, value); } T_END; if (rec->uid != (uint32_t)-1) { /* message already exists in uidlist, need to recreate it */ uidlist->recreate = TRUE; } } void maildir_uidlist_set_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key, const char *value) { maildir_uidlist_set_ext_internal(uidlist, uid, key, value); } void maildir_uidlist_unset_ext(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_ext_key key) { maildir_uidlist_set_ext_internal(uidlist, uid, key, NULL); } static void maildir_uidlist_generate_uid_validity(struct maildir_uidlist *uidlist) { const struct mail_index_header *hdr; if (uidlist->box->opened) { hdr = mail_index_get_header(uidlist->box->view); if (hdr->uid_validity != 0) { uidlist->uid_validity = hdr->uid_validity; return; } } uidlist->uid_validity = maildir_get_uidvalidity_next(uidlist->box->list); } static int maildir_uidlist_write_fd(struct maildir_uidlist *uidlist, int fd, const char *path, unsigned int first_idx, uoff_t *file_size_r) { struct mail_storage *storage = uidlist->box->storage; struct maildir_uidlist_iter_ctx *iter; struct ostream *output; struct maildir_uidlist_rec *rec; string_t *str; const unsigned char *p; const char *strp; size_t len; i_assert(fd != -1); output = o_stream_create_fd_file(fd, (uoff_t)-1, FALSE); o_stream_cork(output); str = t_str_new(512); if (output->offset == 0) { i_assert(first_idx == 0); uidlist->version = UIDLIST_VERSION; if (uidlist->uid_validity == 0) maildir_uidlist_generate_uid_validity(uidlist); if (!uidlist->have_mailbox_guid) guid_128_generate(uidlist->mailbox_guid); i_assert(uidlist->next_uid > 0); str_printfa(str, "%u %c%u %c%u %c%s", uidlist->version, MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY, uidlist->uid_validity, MAILDIR_UIDLIST_HDR_EXT_NEXT_UID, uidlist->next_uid, MAILDIR_UIDLIST_HDR_EXT_GUID, guid_128_to_string(uidlist->mailbox_guid)); if (str_len(uidlist->hdr_extensions) > 0) { str_append_c(str, ' '); str_append_str(str, uidlist->hdr_extensions); } str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } iter = maildir_uidlist_iter_init(uidlist); i_assert(first_idx <= array_count(&uidlist->records)); iter->next += first_idx; while (maildir_uidlist_iter_next_rec(iter, &rec)) { uidlist->read_records_count++; str_truncate(str, 0); str_printfa(str, "%u", rec->uid); if (rec->extensions != NULL) { for (p = rec->extensions; *p != '\0'; ) { i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*p)); len = strlen((const char *)p); str_append_c(str, ' '); str_append_n(str, p, len); p += len + 1; } } str_append(str, " :"); strp = strchr(rec->filename, *MAILDIR_INFO_SEP_S); if (strp == NULL) str_append(str, rec->filename); else str_append_n(str, rec->filename, strp - rec->filename); str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } maildir_uidlist_iter_deinit(&iter); if (o_stream_nfinish(output) < 0) { mail_storage_set_critical(storage, "write(%s) failed: %s", path, o_stream_get_error(output)); o_stream_unref(&output); return -1; } *file_size_r = output->offset; o_stream_unref(&output); if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync(fd) < 0) { mail_storage_set_critical(storage, "fdatasync(%s) failed: %m", path); return -1; } } return 0; } static void maildir_uidlist_records_drop_expunges(struct maildir_uidlist *uidlist) { struct mail_index_view *view; struct maildir_uidlist_rec *const *recs; ARRAY_TYPE(maildir_uidlist_rec_p) new_records; const struct mail_index_header *hdr; const struct mail_index_record *rec; unsigned int i, count; uint32_t seq; /* we could get here when opening and locking mailbox, before index files have been opened. */ if (!uidlist->box->opened) return; mail_index_refresh(uidlist->box->index); view = mail_index_view_open(uidlist->box->index); count = array_count(&uidlist->records); hdr = mail_index_get_header(view); if (count * UIDLIST_COMPRESS_PERCENTAGE / 100 <= hdr->messages_count) { /* too much trouble to be worth it */ mail_index_view_close(&view); return; } i_array_init(&new_records, hdr->messages_count + 64); recs = array_get(&uidlist->records, &count); for (i = 0, seq = 1; i < count && seq <= hdr->messages_count; ) { rec = mail_index_lookup(view, seq); if (recs[i]->uid < rec->uid) { /* expunged entry */ hash_table_remove(uidlist->files, recs[i]->filename); i++; } else if (recs[i]->uid > rec->uid) { /* index isn't up to date. we're probably just syncing it here. ignore this entry. */ seq++; } else { array_append(&new_records, &recs[i], 1); seq++; i++; } } /* drop messages expunged at the end of index */ while (i < count && recs[i]->uid < hdr->next_uid) { hash_table_remove(uidlist->files, recs[i]->filename); i++; } /* view might not be completely up-to-date, so preserve any messages left */ for (; i < count; i++) array_append(&new_records, &recs[i], 1); array_free(&uidlist->records); uidlist->records = new_records; mail_index_view_close(&view); } static int maildir_uidlist_recreate(struct maildir_uidlist *uidlist) { struct mailbox *box = uidlist->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *control_dir, *temp_path; struct stat st; mode_t old_mask; uoff_t file_size; int i, fd, ret; i_assert(uidlist->initial_read); maildir_uidlist_records_drop_expunges(uidlist); if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL, &control_dir) <= 0) i_unreached(); temp_path = t_strconcat(control_dir, "/" MAILDIR_UIDLIST_NAME ".tmp", NULL); for (i = 0;; i++) { old_mask = umask(0777 & ~perm->file_create_mode); fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0777); umask(old_mask); if (fd != -1) break; if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", temp_path); return -1; } /* the control dir doesn't exist. create it unless the whole mailbox was just deleted. */ if (!maildir_set_deleted(uidlist->box)) return -1; } if (perm->file_create_gid != (gid_t)-1 && fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", temp_path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", temp_path); } } uidlist->read_records_count = 0; ret = maildir_uidlist_write_fd(uidlist, fd, temp_path, 0, &file_size); if (ret == 0) { if (rename(temp_path, uidlist->path) < 0) { mail_storage_set_critical(box->storage, "rename(%s, %s) failed: %m", temp_path, uidlist->path); ret = -1; } } if (ret < 0) i_unlink(temp_path); else if (fstat(fd, &st) < 0) { mail_storage_set_critical(box->storage, "fstat(%s) failed: %m", temp_path); ret = -1; } else if (file_size != (uoff_t)st.st_size) { i_assert(!file_dotlock_is_locked(uidlist->dotlock)); mail_storage_set_critical(box->storage, "Maildir uidlist dotlock overridden: %s", uidlist->path); ret = -1; } else { maildir_uidlist_close(uidlist); uidlist->fd = fd; uidlist->fd_dev = st.st_dev; uidlist->fd_ino = st.st_ino; uidlist->fd_size = st.st_size; uidlist->last_read_offset = st.st_size; uidlist->recreate = FALSE; uidlist->recreate_on_change = FALSE; uidlist->have_mailbox_guid = TRUE; maildir_uidlist_update_hdr(uidlist, &st); } if (ret < 0) i_close_fd(&fd); return ret; } int maildir_uidlist_update(struct maildir_uidlist *uidlist) { int ret; if (!uidlist->recreate) return 0; if (maildir_uidlist_lock(uidlist) <= 0) return -1; ret = maildir_uidlist_recreate(uidlist); maildir_uidlist_unlock(uidlist); return ret; } static bool maildir_uidlist_want_compress(struct maildir_uidlist_sync_ctx *ctx) { unsigned int min_rewrite_count; if (!ctx->uidlist->locked_refresh) return FALSE; if (ctx->uidlist->recreate) return TRUE; min_rewrite_count = (ctx->uidlist->read_records_count + ctx->new_files_count) * UIDLIST_COMPRESS_PERCENTAGE / 100; return min_rewrite_count >= array_count(&ctx->uidlist->records); } static bool maildir_uidlist_want_recreate(struct maildir_uidlist_sync_ctx *ctx) { struct maildir_uidlist *uidlist = ctx->uidlist; if (!uidlist->locked_refresh || !uidlist->initial_read) return FALSE; if (ctx->finish_change_counter != uidlist->change_counter) return TRUE; if (uidlist->fd == -1 || uidlist->version != UIDLIST_VERSION || !uidlist->have_mailbox_guid) return TRUE; return maildir_uidlist_want_compress(ctx); } static int maildir_uidlist_sync_update(struct maildir_uidlist_sync_ctx *ctx) { struct maildir_uidlist *uidlist = ctx->uidlist; struct mail_storage *storage = uidlist->box->storage; struct stat st; uoff_t file_size; if (maildir_uidlist_want_recreate(ctx) || uidlist->recreate_on_change) return maildir_uidlist_recreate(uidlist); if (!uidlist->locked_refresh || uidlist->fd == -1) { /* make sure we have the latest file (e.g. NOREFRESH used) */ i_assert(uidlist->initial_hdr_read); if (maildir_uidlist_open_latest(uidlist) < 0) return -1; if (uidlist->recreate_on_change) return maildir_uidlist_recreate(uidlist); } i_assert(ctx->first_unwritten_pos != UINT_MAX); if (lseek(uidlist->fd, 0, SEEK_END) < 0) { mail_storage_set_critical(storage, "lseek(%s) failed: %m", uidlist->path); return -1; } if (maildir_uidlist_write_fd(uidlist, uidlist->fd, uidlist->path, ctx->first_unwritten_pos, &file_size) < 0) return -1; if (fstat(uidlist->fd, &st) < 0) { mail_storage_set_critical(storage, "fstat(%s) failed: %m", uidlist->path); return -1; } if ((uoff_t)st.st_size != file_size) { i_warning("%s: file size changed unexpectedly after write", uidlist->path); } else if (uidlist->locked_refresh) { uidlist->fd_size = st.st_size; uidlist->last_read_offset = st.st_size; maildir_uidlist_update_hdr(uidlist, &st); } return 0; } static void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist, bool nonsynced) { struct maildir_uidlist_rec **recs; unsigned int i, count; recs = array_get_modifiable(&uidlist->records, &count); if (nonsynced) { for (i = 0; i < count; i++) recs[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; } else { for (i = 0; i < count; i++) recs[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; } } static int maildir_uidlist_sync_lock(struct maildir_uidlist *uidlist, enum maildir_uidlist_sync_flags sync_flags, bool *locked_r) { bool nonblock, refresh; int ret; *locked_r = FALSE; if ((sync_flags & MAILDIR_UIDLIST_SYNC_NOLOCK) != 0) { if (maildir_uidlist_refresh(uidlist) < 0) return -1; return 1; } nonblock = (sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0; refresh = (sync_flags & MAILDIR_UIDLIST_SYNC_NOREFRESH) == 0; ret = maildir_uidlist_lock_timeout(uidlist, nonblock, refresh, refresh); if (ret <= 0) { if (ret < 0 || !nonblock) return ret; /* couldn't lock it */ if ((sync_flags & MAILDIR_UIDLIST_SYNC_FORCE) == 0) return 0; /* forcing the sync anyway */ if (maildir_uidlist_refresh(uidlist) < 0) return -1; } else { *locked_r = TRUE; } return 1; } void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist) { maildir_uidlist_mark_all(uidlist, TRUE); } int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist, enum maildir_uidlist_sync_flags sync_flags, struct maildir_uidlist_sync_ctx **sync_ctx_r) { struct maildir_uidlist_sync_ctx *ctx; bool locked; int ret; ret = maildir_uidlist_sync_lock(uidlist, sync_flags, &locked); if (ret <= 0) return ret; *sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1); ctx->uidlist = uidlist; ctx->sync_flags = sync_flags; ctx->partial = !locked || (sync_flags & MAILDIR_UIDLIST_SYNC_PARTIAL) != 0; ctx->locked = locked; ctx->first_unwritten_pos = UINT_MAX; ctx->first_new_pos = UINT_MAX; if (ctx->partial) { if ((sync_flags & MAILDIR_UIDLIST_SYNC_KEEP_STATE) == 0) { /* initially mark all nonsynced */ maildir_uidlist_mark_all(uidlist, TRUE); } return 1; } i_assert(uidlist->locked_refresh); ctx->record_pool = pool_alloconly_create(MEMPOOL_GROWING "maildir_uidlist_sync", 16384); hash_table_create(&ctx->files, ctx->record_pool, 4096, maildir_filename_base_hash, maildir_filename_base_cmp); i_array_init(&ctx->records, array_count(&uidlist->records)); return 1; } static int maildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx, const char *filename, uint32_t uid, enum maildir_uidlist_rec_flag flags, struct maildir_uidlist_rec **rec_r) { struct maildir_uidlist *uidlist = ctx->uidlist; struct maildir_uidlist_rec *rec, *const *recs; unsigned int count; /* we'll update uidlist directly */ rec = hash_table_lookup(uidlist->files, filename); if (rec == NULL) { /* doesn't exist in uidlist */ if (!ctx->locked) { /* we can't add it, so just ignore it */ return 1; } if (ctx->first_new_pos == UINT_MAX) ctx->first_new_pos = array_count(&uidlist->records); ctx->new_files_count++; ctx->changed = TRUE; if (uidlist->record_pool == NULL) { uidlist->record_pool = pool_alloconly_create(MEMPOOL_GROWING "uidlist record_pool", 1024); } rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1); rec->uid = (uint32_t)-1; rec->filename = p_strdup(uidlist->record_pool, filename); array_append(&uidlist->records, &rec, 1); uidlist->change_counter++; hash_table_insert(uidlist->files, rec->filename, rec); } else if (strcmp(rec->filename, filename) != 0) { rec->filename = p_strdup(uidlist->record_pool, filename); } if (uid != 0) { if (rec->uid != uid && rec->uid != (uint32_t)-1) { mail_storage_set_critical(uidlist->box->storage, "Maildir: %s changed UID %u -> %u", filename, rec->uid, uid); return -1; } rec->uid = uid; if (uidlist->next_uid <= uid) uidlist->next_uid = uid + 1; else { recs = array_get(&uidlist->records, &count); if (count > 1 && uid < recs[count-1]->uid) uidlist->unsorted = TRUE; } } rec->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; ctx->finished = FALSE; *rec_r = rec; return 1; } static unsigned char *ext_dup(pool_t pool, const unsigned char *extensions) { unsigned char *ret; if (extensions == NULL) return NULL; T_BEGIN { unsigned int len; for (len = 0; extensions[len] != '\0'; len++) { while (extensions[len] != '\0') len++; } ret = p_malloc(pool, len + 1); memcpy(ret, extensions, len); } T_END; return ret; } int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx, const char *filename, enum maildir_uidlist_rec_flag flags) { struct maildir_uidlist_rec *rec; return maildir_uidlist_sync_next_uid(ctx, filename, 0, flags, &rec); } int maildir_uidlist_sync_next_uid(struct maildir_uidlist_sync_ctx *ctx, const char *filename, uint32_t uid, enum maildir_uidlist_rec_flag flags, struct maildir_uidlist_rec **rec_r) { struct maildir_uidlist *uidlist = ctx->uidlist; struct maildir_uidlist_rec *rec, *old_rec; const char *p; *rec_r = NULL; if (ctx->failed) return -1; for (p = filename; *p != '\0'; p++) { if (*p == 13 || *p == 10) { i_warning("Maildir %s: Ignoring a file with #0x%x: %s", mailbox_get_path(uidlist->box), *p, filename); return 1; } } if (ctx->partial) { return maildir_uidlist_sync_next_partial(ctx, filename, uid, flags, rec_r); } rec = hash_table_lookup(ctx->files, filename); if (rec != NULL) { if ((rec->flags & (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR | MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) { /* possibly duplicate */ return 0; } /* probably was in new/ and now we're seeing it in cur/. remove new/moved flags so if this happens again we'll know to check for duplicates. */ rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR | MAILDIR_UIDLIST_REC_FLAG_MOVED); if (strcmp(rec->filename, filename) != 0) rec->filename = p_strdup(ctx->record_pool, filename); } else { old_rec = hash_table_lookup(uidlist->files, filename); i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist)); rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1); if (old_rec != NULL) { *rec = *old_rec; rec->extensions = ext_dup(ctx->record_pool, rec->extensions); } else { rec->uid = (uint32_t)-1; ctx->new_files_count++; ctx->changed = TRUE; /* didn't exist in uidlist, it's recent */ flags |= MAILDIR_UIDLIST_REC_FLAG_RECENT; } rec->filename = p_strdup(ctx->record_pool, filename); hash_table_insert(ctx->files, rec->filename, rec); array_append(&ctx->records, &rec, 1); } if (uid != 0) { rec->uid = uid; if (uidlist->next_uid <= uid) uidlist->next_uid = uid + 1; } rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; *rec_r = rec; return 1; } void maildir_uidlist_sync_remove(struct maildir_uidlist_sync_ctx *ctx, const char *filename) { struct maildir_uidlist_rec *rec; unsigned int idx; i_assert(ctx->partial); i_assert(ctx->uidlist->locked_refresh); rec = hash_table_lookup(ctx->uidlist->files, filename); i_assert(rec != NULL); i_assert(rec->uid != (uint32_t)-1); hash_table_remove(ctx->uidlist->files, filename); idx = maildir_uidlist_records_array_delete(ctx->uidlist, rec); if (ctx->first_unwritten_pos != UINT_MAX) { i_assert(ctx->first_unwritten_pos > idx); ctx->first_unwritten_pos--; } if (ctx->first_new_pos != UINT_MAX) { i_assert(ctx->first_new_pos > idx); ctx->first_new_pos--; } ctx->changed = TRUE; ctx->uidlist->recreate = TRUE; } void maildir_uidlist_sync_set_ext(struct maildir_uidlist_sync_ctx *ctx, struct maildir_uidlist_rec *rec, enum maildir_uidlist_rec_ext_key key, const char *value) { pool_t pool = ctx->partial ? ctx->uidlist->record_pool : ctx->record_pool; i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(key)); maildir_uidlist_rec_set_ext(rec, pool, key, value); } const char * maildir_uidlist_sync_get_full_filename(struct maildir_uidlist_sync_ctx *ctx, const char *filename) { struct maildir_uidlist_rec *rec; rec = hash_table_lookup(ctx->files, filename); return rec == NULL ? NULL : rec->filename; } bool maildir_uidlist_get_uid(struct maildir_uidlist *uidlist, const char *filename, uint32_t *uid_r) { struct maildir_uidlist_rec *rec; rec = hash_table_lookup(uidlist->files, filename); if (rec == NULL) return FALSE; *uid_r = rec->uid; return TRUE; } void maildir_uidlist_update_fname(struct maildir_uidlist *uidlist, const char *filename) { struct maildir_uidlist_rec *rec; rec = hash_table_lookup(uidlist->files, filename); if (rec == NULL) return; rec->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED; if (strcmp(rec->filename, filename) != 0) rec->filename = p_strdup(uidlist->record_pool, filename); } const char * maildir_uidlist_get_full_filename(struct maildir_uidlist *uidlist, const char *filename) { struct maildir_uidlist_rec *rec; rec = hash_table_lookup(uidlist->files, filename); return rec == NULL ? NULL : rec->filename; } static int maildir_assign_uid_cmp(const void *p1, const void *p2) { const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2; if ((*rec1)->uid != (*rec2)->uid) { if ((*rec1)->uid < (*rec2)->uid) return -1; else return 1; } return maildir_filename_sort_cmp((*rec1)->filename, (*rec2)->filename); } static void maildir_uidlist_assign_uids(struct maildir_uidlist_sync_ctx *ctx) { struct maildir_uidlist_rec **recs; unsigned int dest, count; i_assert(UIDLIST_IS_LOCKED(ctx->uidlist)); i_assert(ctx->first_new_pos != UINT_MAX); if (ctx->first_unwritten_pos == UINT_MAX) ctx->first_unwritten_pos = ctx->first_new_pos; /* sort new files and assign UIDs for them */ recs = array_get_modifiable(&ctx->uidlist->records, &count); qsort(recs + ctx->first_new_pos, count - ctx->first_new_pos, sizeof(*recs), maildir_assign_uid_cmp); for (dest = ctx->first_new_pos; dest < count; dest++) { if (recs[dest]->uid == (uint32_t)-1) break; } for (; dest < count; dest++) { i_assert(recs[dest]->uid == (uint32_t)-1); i_assert(ctx->uidlist->next_uid < (uint32_t)-1); recs[dest]->uid = ctx->uidlist->next_uid++; recs[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED; } if (ctx->uidlist->locked_refresh && ctx->uidlist->initial_read) ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1; ctx->new_files_count = 0; ctx->first_new_pos = UINT_MAX; ctx->uidlist->change_counter++; ctx->finish_change_counter = ctx->uidlist->change_counter; } static void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx) { struct maildir_uidlist *uidlist = ctx->uidlist; /* buffer is unsorted, sort it by UID */ array_sort(&ctx->records, maildir_uid_cmp); array_free(&uidlist->records); uidlist->records = ctx->records; ctx->records.arr.buffer = NULL; i_assert(array_is_created(&uidlist->records)); hash_table_destroy(&uidlist->files); uidlist->files = ctx->files; i_zero(&ctx->files); if (uidlist->record_pool != NULL) pool_unref(&uidlist->record_pool); uidlist->record_pool = ctx->record_pool; ctx->record_pool = NULL; if (ctx->new_files_count != 0) { ctx->first_new_pos = array_count(&uidlist->records) - ctx->new_files_count; maildir_uidlist_assign_uids(ctx); } else { ctx->uidlist->change_counter++; } } void maildir_uidlist_sync_recreate(struct maildir_uidlist_sync_ctx *ctx) { ctx->uidlist->recreate = TRUE; } void maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx) { if (!ctx->partial) { if (!ctx->failed) maildir_uidlist_swap(ctx); } else { if (ctx->new_files_count != 0 && !ctx->failed) { i_assert(ctx->changed); i_assert(ctx->locked); maildir_uidlist_assign_uids(ctx); } } ctx->finished = TRUE; /* mbox=NULL means we're coming from dbox rebuilding code. the dbox is already locked, so allow uidlist recreation */ i_assert(ctx->locked || !ctx->changed); if ((ctx->changed || maildir_uidlist_want_compress(ctx)) && !ctx->failed && ctx->locked) { T_BEGIN { if (maildir_uidlist_sync_update(ctx) < 0) { /* we couldn't write everything we wanted. make sure we don't continue using those UIDs */ maildir_uidlist_reset(ctx->uidlist); ctx->failed = TRUE; } } T_END; } } int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx, bool success) { struct maildir_uidlist_sync_ctx *ctx = *_ctx; int ret; *_ctx = NULL; if (!success) ctx->failed = TRUE; ret = ctx->failed ? -1 : 0; if (!ctx->finished) maildir_uidlist_sync_finish(ctx); if (ctx->partial) maildir_uidlist_mark_all(ctx->uidlist, FALSE); if (ctx->locked) maildir_uidlist_unlock(ctx->uidlist); if (hash_table_is_created(ctx->files)) hash_table_destroy(&ctx->files); if (ctx->record_pool != NULL) pool_unref(&ctx->record_pool); if (array_is_created(&ctx->records)) array_free(&ctx->records); i_free(ctx); return ret; } void maildir_uidlist_add_flags(struct maildir_uidlist *uidlist, const char *filename, enum maildir_uidlist_rec_flag flags) { struct maildir_uidlist_rec *rec; rec = hash_table_lookup(uidlist->files, filename); i_assert(rec != NULL); rec->flags |= flags; } struct maildir_uidlist_iter_ctx * maildir_uidlist_iter_init(struct maildir_uidlist *uidlist) { struct maildir_uidlist_iter_ctx *ctx; unsigned int count; ctx = i_new(struct maildir_uidlist_iter_ctx, 1); ctx->uidlist = uidlist; ctx->next = array_get(&uidlist->records, &count); ctx->end = ctx->next + count; ctx->change_counter = ctx->uidlist->change_counter; return ctx; } static void maildir_uidlist_iter_update_idx(struct maildir_uidlist_iter_ctx *ctx) { unsigned int old_rev_idx, idx, count; old_rev_idx = ctx->end - ctx->next; ctx->next = array_get(&ctx->uidlist->records, &count); ctx->end = ctx->next + count; idx = old_rev_idx >= count ? 0 : count - old_rev_idx; while (idx < count && ctx->next[idx]->uid <= ctx->prev_uid) idx++; while (idx > 0 && ctx->next[idx-1]->uid > ctx->prev_uid) idx--; ctx->next += idx; } static bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx, struct maildir_uidlist_rec **rec_r) { struct maildir_uidlist_rec *rec; if (ctx->change_counter != ctx->uidlist->change_counter) maildir_uidlist_iter_update_idx(ctx); if (ctx->next == ctx->end) return FALSE; rec = *ctx->next; i_assert(rec->uid != (uint32_t)-1); ctx->prev_uid = rec->uid; ctx->next++; *rec_r = rec; return TRUE; } bool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx, uint32_t *uid_r, enum maildir_uidlist_rec_flag *flags_r, const char **filename_r) { struct maildir_uidlist_rec *rec; if (!maildir_uidlist_iter_next_rec(ctx, &rec)) return FALSE; *uid_r = rec->uid; *flags_r = rec->flags; *filename_r = rec->filename; return TRUE; } void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx **_ctx) { i_free(*_ctx); *_ctx = NULL; } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-settings.c0000644000175000017500000000232413123174404021477 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "maildir-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct maildir_settings, name), NULL } static const struct setting_define maildir_setting_defines[] = { DEF(SET_BOOL, maildir_copy_with_hardlinks), DEF(SET_BOOL, maildir_very_dirty_syncs), DEF(SET_BOOL, maildir_broken_filename_sizes), DEF(SET_BOOL, maildir_empty_new), SETTING_DEFINE_LIST_END }; static const struct maildir_settings maildir_default_settings = { .maildir_copy_with_hardlinks = TRUE, .maildir_very_dirty_syncs = FALSE, .maildir_broken_filename_sizes = FALSE, .maildir_empty_new = FALSE }; static const struct setting_parser_info maildir_setting_parser_info = { .module_name = "maildir", .defines = maildir_setting_defines, .defaults = &maildir_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct maildir_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; const struct setting_parser_info *maildir_get_setting_parser_info(void) { return &maildir_setting_parser_info; } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-sync.c0000644000175000017500000010462613165463624020636 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ /* Here's a description of how we handle Maildir synchronization and it's problems: We want to be as efficient as we can. The most efficient way to check if changes have occurred is to stat() the new/ and cur/ directories and uidlist file - if their mtimes haven't changed, there's no changes and we don't need to do anything. Problem 1: Multiple changes can happen within a single second - nothing guarantees that once we synced it, someone else didn't just then make a modification. Such modifications wouldn't get noticed until a new modification occurred later. Problem 2: Syncing cur/ directory is much more costly than syncing new/. Moving mails from new/ to cur/ will always change mtime of cur/ causing us to sync it as well. Problem 3: We may not be able to move mail from new/ to cur/ because we're out of quota, or simply because we're accessing a read-only mailbox. MAILDIR_SYNC_SECS ----------------- Several checks below use MAILDIR_SYNC_SECS, which should be maximum clock drift between all computers accessing the maildir (eg. via NFS), rounded up to next second. Our default is 1 second, since everyone should be using NTP. Note that setting it to 0 works only if there's only one computer accessing the maildir. It's practically impossible to make two clocks _exactly_ synchronized. It might be possible to only use file server's clock by looking at the atime field, but I don't know how well that would actually work. cur directory ------------- We have dirty_cur_time variable which is set to cur/ directory's mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have synchronized the directory. When dirty_cur_time is non-zero, we don't synchronize the cur/ directory until a) cur/'s mtime changes b) opening a mail fails with ENOENT c) time() > dirty_cur_time + MAILDIR_SYNC_SECS This allows us to modify the maildir multiple times without having to sync it at every change. The sync will eventually be done to make sure we didn't miss any external changes. The dirty_cur_time is set when: - we change message flags - we expunge messages - we move mail from new/ to cur/ - we sync cur/ directory and it's mtime is >= time() - MAILDIR_SYNC_SECS It's unset when we do the final syncing, ie. when mtime is older than time() - MAILDIR_SYNC_SECS. new directory ------------- If new/'s mtime is >= time() - MAILDIR_SYNC_SECS, always synchronize it. dirty_cur_time-like feature might save us a few syncs, but that might break a client which saves a mail in one connection and tries to fetch it in another one. new/ directory is almost always empty, so syncing it should be very fast anyway. Actually this can still happen if we sync only new/ dir while another client is also moving mails from it to cur/ - it takes us a while to see them. That's pretty unlikely to happen however, and only way to fix it would be to always synchronize cur/ after new/. Normally we move all mails from new/ to cur/ whenever we sync it. If it's not possible for some reason, we mark the mail with "probably exists in new/ directory" flag. If rename() still fails because of ENOSPC or EDQUOT, we still save the flag changes in index with dirty-flag on. When moving the mail to cur/ directory, or when we notice it's already moved there, we apply the flag changes to the filename, rename it and remove the dirty flag. If there's dirty flags, this should be tried every time after expunge or when closing the mailbox. uidlist ------- This file contains UID <-> filename mappings. It's updated only when new mail arrives, so it may contain filenames that have already been deleted. Updating is done by getting uidlist.lock file, writing the whole uidlist into it and rename()ing it over the old uidlist. This means there's no need to lock the file for reading. Whenever uidlist is rewritten, it's mtime must be larger than the old one's. Use utime() before rename() if needed. Note that inode checking wouldn't have been sufficient as inode numbers can be reused. This file is usually read the first time you need to know filename for given UID. After that it's not re-read unless new mails come that we don't know about. broken clients -------------- Originally the middle identifier in Maildir filename was specified only as _. That however created a problem with randomized PIDs which made it possible that the same PID was reused within one second. So if within one second a mail was delivered, MUA moved it to cur/ and another mail was delivered by a new process using same PID as the first one, we likely ended up overwriting the first mail when the second mail was moved over it. Nowadays everyone should be giving a bit more specific identifier, for example include microseconds in it which Dovecot does. There's a simple way to prevent this from happening in some cases: Don't move the mail from new/ to cur/ if it's mtime is >= time() - MAILDIR_SYNC_SECS. The second delivery's link() call then fails because the file is already in new/, and it will then use a different filename. There's a few problems with this however: - it requires extra stat() call which is unneeded extra I/O - another MUA might still move the mail to cur/ - if first file's flags are modified by either Dovecot or another MUA, it's moved to cur/ (you _could_ just do the dirty-flagging but that'd be ugly) Because this is useful only for very few people and it requires extra I/O, I decided not to implement this. It should be however quite easy to do since we need to be able to deal with files in new/ in any case. It's also possible to never accidentally overwrite a mail by using link() + unlink() rather than rename(). This however isn't very good idea as it introduces potential race conditions when multiple clients are accessing the mailbox: Trying to move the same mail from new/ to cur/ at the same time: a) Client 1 uses slightly different filename than client 2, for example one sets read-flag on but the other doesn't. You have the same mail duplicated now. b) Client 3 sees the mail between Client 1's and 2's link() calls and changes it's flag. You have the same mail duplicated now. And it gets worse when they're unlink()ing in cur/ directory: c) Client 1 changes mails's flag and client 2 changes it back between 1's link() and unlink(). The mail is now expunged. d) If you try to deal with the duplicates by unlink()ing another one of them, you might end up unlinking both of them. So, what should we do then if we notice a duplicate? First of all, it might not be a duplicate at all, readdir() might have just returned it twice because it was just renamed. What we should do is create a completely new base name for it and rename() it to that. If the call fails with ENOENT, it only means that it wasn't a duplicate after all. */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "hash.h" #include "str.h" #include "eacces-error.h" #include "nfs-workarounds.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-filename.h" #include "maildir-sync.h" #include #include #include #include #include #define MAILDIR_FILENAME_FLAG_FOUND 128 /* When rename()ing many files from new/ to cur/, it's possible that next readdir() skips some files. we don't of course wish to lose them, so we go and rescan the new/ directory again from beginning until no files are left. This value is just an optimization to avoid checking the directory twice unneededly. usually only NFS is the problem case. 1 is the safest bet here, but I guess 5 will do just fine too. */ #define MAILDIR_RENAME_RESCAN_COUNT 5 /* This is mostly to avoid infinite looping when rename() destination already exists as the hard link of the file itself. */ #define MAILDIR_SCAN_DIR_MAX_COUNT 5 #define DUPE_LINKS_DELETE_SECS 30 enum maildir_scan_why { WHY_FORCED = 0x01, WHY_FIRSTSYNC = 0x02, WHY_NEWCHANGED = 0x04, WHY_CURCHANGED = 0x08, WHY_DROPRECENT = 0x10, WHY_FINDRECENT = 0x20, WHY_DELAYEDNEW = 0x40, WHY_DELAYEDCUR = 0x80 }; struct maildir_sync_context { struct maildir_mailbox *mbox; const char *new_dir, *cur_dir; enum mailbox_sync_flags flags; time_t last_touch, last_notify; struct maildir_uidlist_sync_ctx *uidlist_sync_ctx; struct maildir_index_sync_context *index_sync_ctx; unsigned int partial:1; unsigned int locked:1; unsigned int racing:1; }; void maildir_sync_set_racing(struct maildir_sync_context *ctx) { ctx->racing = TRUE; } void maildir_sync_notify(struct maildir_sync_context *ctx) { time_t now; if (ctx == NULL) { /* we got here from maildir-save.c. it has no maildir_sync_context, */ return; } now = time(NULL); if (now - ctx->last_touch > MAILDIR_LOCK_TOUCH_SECS && ctx->locked) { (void)maildir_uidlist_lock_touch(ctx->mbox->uidlist); ctx->last_touch = now; } if (now - ctx->last_notify > MAIL_STORAGE_STAYALIVE_SECS) { struct mailbox *box = &ctx->mbox->box; if (box->storage->callbacks.notify_ok != NULL) { box->storage->callbacks. notify_ok(box, "Hang in there..", box->storage->callback_context); } ctx->last_notify = now; } } static struct maildir_sync_context * maildir_sync_context_new(struct maildir_mailbox *mbox, enum mailbox_sync_flags flags) { struct maildir_sync_context *ctx; ctx = t_new(struct maildir_sync_context, 1); ctx->mbox = mbox; ctx->new_dir = t_strconcat(mailbox_get_path(&mbox->box), "/new", NULL); ctx->cur_dir = t_strconcat(mailbox_get_path(&mbox->box), "/cur", NULL); ctx->last_touch = ioloop_time; ctx->last_notify = ioloop_time; ctx->flags = flags; return ctx; } static void maildir_sync_deinit(struct maildir_sync_context *ctx) { if (ctx->uidlist_sync_ctx != NULL) (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, FALSE); if (ctx->index_sync_ctx != NULL) maildir_sync_index_rollback(&ctx->index_sync_ctx); } static int maildir_fix_duplicate(struct maildir_sync_context *ctx, const char *dir, const char *fname2) { const char *fname1, *path1, *path2; const char *new_fname, *new_path; struct stat st1, st2; uoff_t size; fname1 = maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx, fname2); i_assert(fname1 != NULL); path1 = t_strconcat(dir, "/", fname1, NULL); path2 = t_strconcat(dir, "/", fname2, NULL); if (stat(path1, &st1) < 0 || stat(path2, &st2) < 0) { /* most likely the files just don't exist anymore. don't really care about other errors much. */ return 0; } if (st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev)) { /* Files are the same. this means either a race condition between stat() calls, or that the files were link()ed. */ if (st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink && st1.st_ctime == st2.st_ctime && st1.st_ctime < ioloop_time - DUPE_LINKS_DELETE_SECS) { /* The file has hard links and it hasn't had any changes (such as renames) for a while, so this isn't a race condition. rename()ing one file on top of the other would fix this safely, except POSIX decided that rename() doesn't work that way. So we'll have unlink() one and hope that another process didn't just decide to unlink() the other (uidlist lock prevents this from happening) */ if (i_unlink(path2) == 0) i_warning("Unlinked a duplicate: %s", path2); } return 0; } new_fname = maildir_filename_generate(); /* preserve S= and W= sizes if they're available. (S=size is required for zlib plugin to work) */ if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_FILE_SIZE, &size)) { new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T, new_fname, MAILDIR_EXTRA_FILE_SIZE, size); } if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_VIRTUAL_SIZE, &size)) { new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T, new_fname, MAILDIR_EXTRA_VIRTUAL_SIZE, size); } new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box), "/new/", new_fname, NULL); if (rename(path2, new_path) == 0) i_warning("Fixed a duplicate: %s -> %s", path2, new_fname); else if (errno != ENOENT) { mail_storage_set_critical(&ctx->mbox->storage->storage, "Couldn't fix a duplicate: rename(%s, %s) failed: %m", path2, new_path); return -1; } return 0; } static int maildir_rename_empty_basename(struct maildir_sync_context *ctx, const char *dir, const char *fname) { const char *old_path, *new_fname, *new_path; old_path = t_strconcat(dir, "/", fname, NULL); new_fname = maildir_filename_generate(); new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box), "/new/", new_fname, NULL); if (rename(old_path, new_path) == 0) i_warning("Fixed broken filename: %s -> %s", old_path, new_fname); else if (errno != ENOENT) { mail_storage_set_critical(&ctx->mbox->storage->storage, "Couldn't fix a broken filename: rename(%s, %s) failed: %m", old_path, new_path); return -1; } return 0; } static int maildir_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st_r) { struct mailbox *box = &mbox->box; int i; for (i = 0;; i++) { if (nfs_safe_stat(path, st_r) == 0) return 0; if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) break; if (!maildir_set_deleted(box)) return -1; /* try again */ } mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } static int maildir_scan_dir(struct maildir_sync_context *ctx, bool new_dir, bool final, enum maildir_scan_why why) { struct mail_storage *storage = &ctx->mbox->storage->storage; const char *path; DIR *dirp; string_t *src, *dest; struct dirent *dp; struct stat st; enum maildir_uidlist_rec_flag flags; unsigned int time_diff, i, readdir_count = 0, move_count = 0; time_t start_time; int ret = 1; bool move_new, dir_changed = FALSE; path = new_dir ? ctx->new_dir : ctx->cur_dir; for (i = 0;; i++) { dirp = opendir(path); if (dirp != NULL) break; if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) { if (errno == EACCES) { mail_storage_set_critical(storage, "%s", eacces_error_get("opendir", path)); } else { mail_storage_set_critical(storage, "opendir(%s) failed: %m", path); } return -1; } if (!maildir_set_deleted(&ctx->mbox->box)) return -1; /* try again */ } #ifdef HAVE_DIRFD if (fstat(dirfd(dirp), &st) < 0) { mail_storage_set_critical(storage, "fstat(%s) failed: %m", path); (void)closedir(dirp); return -1; } #else if (maildir_stat(ctx->mbox, path, &st) < 0) { (void)closedir(dirp); return -1; } #endif start_time = time(NULL); if (new_dir) { ctx->mbox->maildir_hdr.new_check_time = start_time; ctx->mbox->maildir_hdr.new_mtime = st.st_mtime; ctx->mbox->maildir_hdr.new_mtime_nsecs = ST_MTIME_NSEC(st); } else { ctx->mbox->maildir_hdr.cur_check_time = start_time; ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime; ctx->mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st); } src = t_str_new(1024); dest = t_str_new(1024); move_new = new_dir && ctx->locked && ((ctx->mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0 || ctx->mbox->storage->set->maildir_empty_new); errno = 0; for (; (dp = readdir(dirp)) != NULL; errno = 0) { if (dp->d_name[0] == '.') continue; if (dp->d_name[0] == MAILDIR_INFO_SEP) { /* don't even try to use file with empty base name */ if (maildir_rename_empty_basename(ctx, path, dp->d_name) < 0) break; continue; } flags = 0; if (move_new) { i_assert(dp->d_name[0] != '\0'); str_truncate(src, 0); str_truncate(dest, 0); str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name); str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name); if (strchr(dp->d_name, MAILDIR_INFO_SEP) == NULL) { str_append(dest, MAILDIR_FLAGS_FULL_SEP); } if (rename(str_c(src), str_c(dest)) == 0) { /* we moved it - it's \Recent for us */ dir_changed = TRUE; move_count++; flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED | MAILDIR_UIDLIST_REC_FLAG_RECENT; } else if (ENOTFOUND(errno)) { /* someone else moved it already */ dir_changed = TRUE; move_count++; flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED | MAILDIR_UIDLIST_REC_FLAG_RECENT; } else if (ENOSPACE(errno) || errno == EACCES) { /* not enough disk space / read-only maildir, leave here */ flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; move_new = FALSE; } else { flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; mail_storage_set_critical(storage, "rename(%s, %s) failed: %m", str_c(src), str_c(dest)); } if ((move_count % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx); } else if (new_dir) { flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR | MAILDIR_UIDLIST_REC_FLAG_RECENT; } readdir_count++; if ((readdir_count % MAILDIR_SLOW_CHECK_COUNT) == 0) maildir_sync_notify(ctx); ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx, dp->d_name, flags); if (ret <= 0) { if (ret < 0) break; /* possibly duplicate - try fixing it */ T_BEGIN { ret = maildir_fix_duplicate(ctx, path, dp->d_name); } T_END; if (ret < 0) break; } } #ifdef __APPLE__ if (errno == EINVAL && move_count > 0 && !final) { /* OS X HFS+: readdir() fails sometimes when rename() have been done. */ move_count = MAILDIR_RENAME_RESCAN_COUNT + 1; } else #endif if (errno != 0) { mail_storage_set_critical(storage, "readdir(%s) failed: %m", path); ret = -1; } if (closedir(dirp) < 0) { mail_storage_set_critical(storage, "closedir(%s) failed: %m", path); ret = -1; } if (dir_changed) { /* save the exact new times. the new mtimes should be >= "start_time", but just in case something weird happens and mtime doesn't update, use "start_time". */ if (stat(ctx->new_dir, &st) == 0) { ctx->mbox->maildir_hdr.new_check_time = I_MAX(st.st_mtime, start_time); ctx->mbox->maildir_hdr.new_mtime = st.st_mtime; ctx->mbox->maildir_hdr.new_mtime_nsecs = ST_MTIME_NSEC(st); } if (stat(ctx->cur_dir, &st) == 0) { ctx->mbox->maildir_hdr.new_check_time = I_MAX(st.st_mtime, start_time); ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime; ctx->mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st); } } time_diff = time(NULL) - start_time; if (time_diff >= MAILDIR_SYNC_TIME_WARN_SECS) { i_warning("Maildir: Scanning %s took %u seconds " "(%u readdir()s, %u rename()s to cur/, why=0x%x)", path, time_diff, readdir_count, move_count, why); } return ret < 0 ? -1 : (move_count <= MAILDIR_RENAME_RESCAN_COUNT || final ? 0 : 1); } static void maildir_sync_get_header(struct maildir_mailbox *mbox) { const void *data; size_t data_size; mail_index_get_header_ext(mbox->box.view, mbox->maildir_ext_id, &data, &data_size); if (data_size == 0) { /* header doesn't exist */ } else { memcpy(&mbox->maildir_hdr, data, I_MIN(sizeof(mbox->maildir_hdr), data_size)); } } int maildir_sync_header_refresh(struct maildir_mailbox *mbox) { if (mail_index_refresh(mbox->box.index) < 0) { mailbox_set_index_error(&mbox->box); return -1; } maildir_sync_get_header(mbox); return 0; } static int maildir_sync_quick_check(struct maildir_mailbox *mbox, bool undirty, const char *new_dir, const char *cur_dir, bool *new_changed_r, bool *cur_changed_r, enum maildir_scan_why *why_r) { #define DIR_DELAYED_REFRESH(hdr, name) \ ((hdr)->name ## _check_time <= \ (hdr)->name ## _mtime + MAILDIR_SYNC_SECS && \ (undirty || \ (time_t)(hdr)->name ## _check_time < ioloop_time - MAILDIR_SYNC_SECS)) #define DIR_MTIME_CHANGED(st, hdr, name) \ ((st).st_mtime != (time_t)(hdr)->name ## _mtime || \ !ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), (hdr)->name ## _mtime_nsecs)) struct maildir_index_header *hdr = &mbox->maildir_hdr; struct stat new_st, cur_st; bool refreshed = FALSE, check_new = FALSE, check_cur = FALSE; *why_r = 0; if (mbox->maildir_hdr.new_mtime == 0) { maildir_sync_get_header(mbox); if (mbox->maildir_hdr.new_mtime == 0) { /* first sync */ *why_r |= WHY_FIRSTSYNC; *new_changed_r = *cur_changed_r = TRUE; return 0; } } *new_changed_r = *cur_changed_r = FALSE; /* try to avoid stat()ing by first checking delayed changes */ if (DIR_DELAYED_REFRESH(hdr, new) || (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->storage->set->maildir_very_dirty_syncs)) { /* refresh index and try again */ if (maildir_sync_header_refresh(mbox) < 0) return -1; refreshed = TRUE; if (DIR_DELAYED_REFRESH(hdr, new)) { *why_r |= WHY_DELAYEDNEW; *new_changed_r = TRUE; } if (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->storage->set->maildir_very_dirty_syncs) { *why_r |= WHY_DELAYEDCUR; *cur_changed_r = TRUE; } if (*new_changed_r && *cur_changed_r) return 0; } if (!*new_changed_r) { if (maildir_stat(mbox, new_dir, &new_st) < 0) return -1; check_new = TRUE; } if (!*cur_changed_r) { if (maildir_stat(mbox, cur_dir, &cur_st) < 0) return -1; check_cur = TRUE; } for (;;) { if (check_new) { *new_changed_r = DIR_MTIME_CHANGED(new_st, hdr, new); if (*new_changed_r) *why_r |= WHY_NEWCHANGED; } if (check_cur) { *cur_changed_r = DIR_MTIME_CHANGED(cur_st, hdr, cur); if (*cur_changed_r) *why_r |= WHY_CURCHANGED; } if ((!*new_changed_r && !*cur_changed_r) || refreshed) break; /* refresh index and try again */ if (maildir_sync_header_refresh(mbox) < 0) return -1; refreshed = TRUE; } return 0; } static void maildir_sync_update_next_uid(struct maildir_mailbox *mbox) { const struct mail_index_header *hdr; uint32_t uid_validity; hdr = mail_index_get_header(mbox->box.view); if (hdr->uid_validity == 0) return; uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist); if (uid_validity == hdr->uid_validity || uid_validity == 0) { /* make sure uidlist's next_uid is at least as large as index file's. typically this happens only if uidlist gets deleted. */ maildir_uidlist_set_uid_validity(mbox->uidlist, hdr->uid_validity); maildir_uidlist_set_next_uid(mbox->uidlist, hdr->next_uid, FALSE); } } static bool have_recent_messages(struct maildir_sync_context *ctx, bool seen_changes) { const struct mail_index_header *hdr; uint32_t next_uid; hdr = mail_index_get_header(ctx->mbox->box.view); if (!seen_changes) { /* index is up to date. get the next-uid from it */ next_uid = hdr->next_uid; } else { (void)maildir_uidlist_refresh(ctx->mbox->uidlist); next_uid = maildir_uidlist_get_next_uid(ctx->mbox->uidlist); } return hdr->first_recent_uid < next_uid; } static int maildir_sync_get_changes(struct maildir_sync_context *ctx, bool *new_changed_r, bool *cur_changed_r, enum maildir_scan_why *why_r) { struct maildir_mailbox *mbox = ctx->mbox; enum mail_index_sync_flags flags = 0; bool undirty = (ctx->flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0; *why_r = 0; if (maildir_sync_quick_check(mbox, undirty, ctx->new_dir, ctx->cur_dir, new_changed_r, cur_changed_r, why_r) < 0) return -1; /* if there are files in new/, we'll need to move them. we'll check this by seeing if we have any recent messages */ if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) { if (!*new_changed_r && have_recent_messages(ctx, FALSE)) { *new_changed_r = TRUE; *why_r |= WHY_DROPRECENT; } } else if (*new_changed_r) { /* if recent messages have been externally deleted from new/, we need to get them out of index. this requires that we make sure they weren't just moved to cur/. */ if (!*cur_changed_r && have_recent_messages(ctx, TRUE)) { *cur_changed_r = TRUE; *why_r |= WHY_FINDRECENT; } } if (*new_changed_r || *cur_changed_r) return 1; if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT; if (mbox->synced) { /* refresh index only after the first sync, i.e. avoid wasting time on refreshing it immediately after it was just opened */ mail_index_refresh(mbox->box.index); } return mail_index_sync_have_any(mbox->box.index, flags) ? 1 : 0; } static int ATTR_NULL(3) maildir_sync_context(struct maildir_sync_context *ctx, bool forced, uint32_t *find_uid, bool *lost_files_r) { enum maildir_uidlist_sync_flags sync_flags; enum maildir_uidlist_rec_flag flags; bool new_changed, cur_changed, lock_failure; const char *fname; enum maildir_scan_why why; int ret; *lost_files_r = FALSE; if (forced) { new_changed = cur_changed = TRUE; why = WHY_FORCED; } else { ret = maildir_sync_get_changes(ctx, &new_changed, &cur_changed, &why); if (ret <= 0) return ret; } /* Locking, locking, locking.. Wasn't maildir supposed to be lockless? We can get here either as beginning a real maildir sync, or when committing changes to maildir but a file was lost (maybe renamed). So, we're going to need two locks. One for index and one for uidlist. To avoid deadlocking do the uidlist lock always first. uidlist is needed only for figuring out UIDs for newly seen files, so theoretically we wouldn't need to lock it unless there are new files. It has a few problems though, assuming the index lock didn't already protect it (eg. in-memory indexes): 1. Just because you see a new file which doesn't exist in uidlist file, doesn't mean that the file really exists anymore, or that your readdir() lists all new files. Meaning that this is possible: A: opendir(), readdir() -> new file ... -- new files are written to the maildir -- B: opendir(), readdir() -> new file, lock uidlist, readdir() -> another new file, rewrite uidlist, unlock A: ... lock uidlist, readdir() -> nothing left, rewrite uidlist, unlock The second time running A didn't see the two new files. To handle this correctly, it must not remove the new unseen files from uidlist. This is possible to do, but adds extra complexity. 2. If another process is rename()ing files while we are readdir()ing, it's possible that readdir() never lists some files, causing Dovecot to assume they were expunged. In next sync they would show up again, but client could have already been notified of that and they would show up under new UIDs, so the damage is already done. Both of the problems can be avoided if we simply lock the uidlist before syncing and keep it until sync is finished. Typically this would happen in any case, as there is the index lock.. The second case is still a problem with external changes though, because maildir doesn't require any kind of locking. Luckily this problem rarely happens except under high amount of modifications. */ if (!cur_changed) { ctx->partial = TRUE; sync_flags = MAILDIR_UIDLIST_SYNC_PARTIAL; } else { ctx->partial = FALSE; sync_flags = 0; if (forced) sync_flags |= MAILDIR_UIDLIST_SYNC_FORCE; if ((ctx->flags & MAILBOX_SYNC_FLAG_FAST) != 0) sync_flags |= MAILDIR_UIDLIST_SYNC_TRYLOCK; } ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags, &ctx->uidlist_sync_ctx); lock_failure = ret <= 0; if (ret <= 0) { struct mail_storage *storage = ctx->mbox->box.storage; if (ret == 0) { /* timeout */ return 0; } /* locking failed. sync anyway without locking so that it's possible to expunge messages when out of quota. */ if (forced) { /* we're already forcing a sync, we're trying to find a message that was probably already expunged, don't loop for a long time trying to find it. */ return -1; } ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags | MAILDIR_UIDLIST_SYNC_NOLOCK, &ctx->uidlist_sync_ctx); if (ret <= 0) { i_assert(ret != 0); return -1; } if (storage->callbacks.notify_no != NULL) { storage->callbacks.notify_no(&ctx->mbox->box, "Internal mailbox synchronization failure, " "showing only old mails.", storage->callback_context); } } ctx->locked = maildir_uidlist_is_locked(ctx->mbox->uidlist); if (!ctx->locked) ctx->partial = TRUE; if (!ctx->mbox->syncing_commit && (ctx->locked || lock_failure)) { if (maildir_sync_index_begin(ctx->mbox, ctx, &ctx->index_sync_ctx) < 0) return -1; } if (new_changed || cur_changed) { /* if we're going to check cur/ dir our current logic requires that new/ dir is checked as well. it's a good idea anyway. */ unsigned int count = 0; bool final = FALSE; while ((ret = maildir_scan_dir(ctx, TRUE, final, why)) > 0) { /* rename()d at least some files, which might have caused some other files to be missed. check again (see MAILDIR_RENAME_RESCAN_COUNT). */ if (++count >= MAILDIR_SCAN_DIR_MAX_COUNT) final = TRUE; } if (ret < 0) return -1; if (cur_changed) { if (maildir_scan_dir(ctx, FALSE, TRUE, why) < 0) return -1; } maildir_sync_update_next_uid(ctx->mbox); /* finish uidlist syncing, but keep it still locked */ maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx); } if (!ctx->locked) { /* make sure we sync the maildir later */ ctx->mbox->maildir_hdr.new_mtime = 0; ctx->mbox->maildir_hdr.cur_mtime = 0; } if (ctx->index_sync_ctx != NULL) { /* NOTE: index syncing here might cause a re-sync due to files getting lost, so this function might be called re-entrantly. */ ret = maildir_sync_index(ctx->index_sync_ctx, ctx->partial); if (ret < 0) maildir_sync_index_rollback(&ctx->index_sync_ctx); else if (maildir_sync_index_commit(&ctx->index_sync_ctx) < 0) return -1; if (ret < 0) return -1; if (ret == 0) *lost_files_r = TRUE; i_assert(maildir_uidlist_is_locked(ctx->mbox->uidlist) || lock_failure); } if (find_uid != NULL && *find_uid != 0) { ret = maildir_uidlist_lookup(ctx->mbox->uidlist, *find_uid, &flags, &fname); if (ret < 0) return -1; if (ret == 0) { /* UID is expunged */ *find_uid = 0; } else if ((flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) == 0) { *find_uid = 0; } else { /* we didn't find it, possibly expunged? */ } } return maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, TRUE); } int maildir_sync_lookup(struct maildir_mailbox *mbox, uint32_t uid, enum maildir_uidlist_rec_flag *flags_r, const char **fname_r) { int ret; ret = maildir_uidlist_lookup(mbox->uidlist, uid, flags_r, fname_r); if (ret != 0) return ret; if (maildir_uidlist_is_open(mbox->uidlist)) { /* refresh uidlist and check again in case it was added after the last mailbox sync */ if (mbox->sync_uidlist_refreshed) { /* we've already refreshed it, don't bother again */ return ret; } mbox->sync_uidlist_refreshed = TRUE; if (maildir_uidlist_refresh(mbox->uidlist) < 0) return -1; } else { /* the uidlist doesn't exist. */ if (maildir_storage_sync_force(mbox, uid) < 0) return -1; } /* try again */ return maildir_uidlist_lookup(mbox->uidlist, uid, flags_r, fname_r); } static int maildir_sync_run(struct maildir_mailbox *mbox, enum mailbox_sync_flags flags, bool force_resync, uint32_t *uid, bool *lost_files_r) { struct maildir_sync_context *ctx; bool retry, lost_files; int ret; T_BEGIN { ctx = maildir_sync_context_new(mbox, flags); ret = maildir_sync_context(ctx, force_resync, uid, lost_files_r); retry = ctx->racing; maildir_sync_deinit(ctx); } T_END; if (retry) T_BEGIN { /* we're racing some file. retry the sync again to see if the file is really gone or not. if it is, this is a bit of unnecessary work, but if it's not, this is necessary for e.g. doveadm force-resync to work. */ ctx = maildir_sync_context_new(mbox, 0); ret = maildir_sync_context(ctx, TRUE, NULL, &lost_files); maildir_sync_deinit(ctx); } T_END; return ret; } int maildir_storage_sync_force(struct maildir_mailbox *mbox, uint32_t uid) { bool lost_files; int ret; ret = maildir_sync_run(mbox, MAILBOX_SYNC_FLAG_FAST, TRUE, &uid, &lost_files); if (uid != 0) { /* maybe it's expunged. check again. */ ret = maildir_sync_run(mbox, 0, TRUE, NULL, &lost_files); } return ret; } int maildir_sync_refresh_flags_view(struct maildir_mailbox *mbox) { struct mail_index_view_sync_ctx *sync_ctx; bool delayed_expunges; mail_index_refresh(mbox->box.index); if (mbox->flags_view == NULL) mbox->flags_view = mail_index_view_open(mbox->box.index); sync_ctx = mail_index_view_sync_begin(mbox->flags_view, MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT); if (mail_index_view_sync_commit(&sync_ctx, &delayed_expunges) < 0) { mailbox_set_index_error(&mbox->box); return -1; } /* make sure the map stays in private memory */ if (mbox->flags_view->map->refcount > 1) { struct mail_index_map *map; map = mail_index_map_clone(mbox->flags_view->map); mail_index_unmap(&mbox->flags_view->map); mbox->flags_view->map = map; } mail_index_record_map_move_to_private(mbox->flags_view->map); mail_index_map_move_to_memory(mbox->flags_view->map); return 0; } struct mailbox_sync_context * maildir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)box; bool lost_files, force_resync; int ret = 0; force_resync = (flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0; if (index_mailbox_want_full_sync(&mbox->box, flags)) { ret = maildir_sync_run(mbox, flags, force_resync, NULL, &lost_files); i_assert(!maildir_uidlist_is_locked(mbox->uidlist) || (box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0); if (lost_files) { /* lost some files from new/, see if thery're in cur/ */ ret = maildir_storage_sync_force(mbox, 0); } } if (mbox->storage->set->maildir_very_dirty_syncs) { if (maildir_sync_refresh_flags_view(mbox) < 0) ret = -1; maildir_uidlist_set_all_nonsynced(mbox->uidlist); } mbox->synced = TRUE; mbox->sync_uidlist_refreshed = FALSE; return index_mailbox_sync_init(box, flags, ret < 0); } int maildir_sync_is_synced(struct maildir_mailbox *mbox) { bool new_changed, cur_changed; enum maildir_scan_why why; int ret; T_BEGIN { const char *box_path = mailbox_get_path(&mbox->box); const char *new_dir, *cur_dir; new_dir = t_strconcat(box_path, "/new", NULL); cur_dir = t_strconcat(box_path, "/cur", NULL); ret = maildir_sync_quick_check(mbox, FALSE, new_dir, cur_dir, &new_changed, &cur_changed, &why); } T_END; return ret < 0 ? -1 : (!new_changed && !cur_changed); } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-filename.h0000644000175000017500000000065113123174404021425 00000000000000#ifndef MAILDIR_FILENAME_H #define MAILDIR_FILENAME_H struct maildir_keywords_sync_ctx; const char *maildir_filename_generate(void); bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r); unsigned int maildir_filename_base_hash(const char *fname); int maildir_filename_base_cmp(const char *fname1, const char *fname2); int maildir_filename_sort_cmp(const char *fname1, const char *fname2); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-keywords.c0000644000175000017500000002770213165463624021530 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ /* note that everything here depends on uidlist file being locked the whole time. that's why we don't have any locking of our own, or that we do things that would be racy otherwise. */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "hash.h" #include "str.h" #include "istream.h" #include "eacces-error.h" #include "file-dotlock.h" #include "write-full.h" #include "nfs-workarounds.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include #include /* how many seconds to wait before overriding dovecot-keywords.lock */ #define KEYWORDS_LOCK_STALE_TIMEOUT (60*2) struct maildir_keywords { struct maildir_mailbox *mbox; struct mail_storage *storage; char *path; pool_t pool; ARRAY_TYPE(keywords) list; HASH_TABLE(char *, void *) hash; /* name -> idx+1 */ struct dotlock_settings dotlock_settings; time_t synced_mtime; unsigned int synced:1; unsigned int changed:1; }; struct maildir_keywords_sync_ctx { struct maildir_keywords *mk; struct mail_index *index; const ARRAY_TYPE(keywords) *keywords; ARRAY(char) idx_to_chr; unsigned int chridx_to_idx[MAILDIR_MAX_KEYWORDS]; bool readonly; }; struct maildir_keywords *maildir_keywords_init(struct maildir_mailbox *mbox) { struct maildir_keywords *mk; mk = maildir_keywords_init_readonly(&mbox->box); mk->mbox = mbox; return mk; } struct maildir_keywords * maildir_keywords_init_readonly(struct mailbox *box) { struct maildir_keywords *mk; const char *dir; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL, &dir) <= 0) i_unreached(); mk = i_new(struct maildir_keywords, 1); mk->storage = box->storage; mk->path = i_strconcat(dir, "/" MAILDIR_KEYWORDS_NAME, NULL); mk->pool = pool_alloconly_create("maildir keywords", 512); i_array_init(&mk->list, MAILDIR_MAX_KEYWORDS); hash_table_create(&mk->hash, mk->pool, 0, strcase_hash, strcasecmp); mk->dotlock_settings.use_excl_lock = box->storage->set->dotlock_use_excl; mk->dotlock_settings.nfs_flush = box->storage->set->mail_nfs_storage; mk->dotlock_settings.timeout = mail_storage_get_lock_timeout(box->storage, KEYWORDS_LOCK_STALE_TIMEOUT + 2); mk->dotlock_settings.stale_timeout = KEYWORDS_LOCK_STALE_TIMEOUT; mk->dotlock_settings.temp_prefix = mailbox_list_get_temp_prefix(box->list); return mk; } void maildir_keywords_deinit(struct maildir_keywords **_mk) { struct maildir_keywords *mk = *_mk; *_mk = NULL; hash_table_destroy(&mk->hash); array_free(&mk->list); pool_unref(&mk->pool); i_free(mk->path); i_free(mk); } static void maildir_keywords_clear(struct maildir_keywords *mk) { array_clear(&mk->list); hash_table_clear(mk->hash, TRUE); p_clear(mk->pool); } static int maildir_keywords_sync(struct maildir_keywords *mk) { struct istream *input; struct stat st; char *line, *p, *new_name; const char **strp; unsigned int idx; int fd; /* Remember that we rely on uidlist file locking in here. That's why we rely on stat()'s timestamp and don't bother handling ESTALE errors. */ if (mk->storage->set->mail_nfs_storage) { /* file is updated only by replacing it, no need to flush attribute cache */ nfs_flush_file_handle_cache(mk->path); } if (nfs_safe_stat(mk->path, &st) < 0) { if (errno == ENOENT) { maildir_keywords_clear(mk); mk->synced = TRUE; return 0; } mail_storage_set_critical(mk->storage, "stat(%s) failed: %m", mk->path); return -1; } if (st.st_mtime == mk->synced_mtime) { /* hasn't changed */ mk->synced = TRUE; return 0; } mk->synced_mtime = st.st_mtime; fd = open(mk->path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { maildir_keywords_clear(mk); mk->synced = TRUE; return 0; } mail_storage_set_critical(mk->storage, "open(%s) failed: %m", mk->path); return -1; } maildir_keywords_clear(mk); input = i_stream_create_fd(fd, 1024, FALSE); while ((line = i_stream_read_next_line(input)) != NULL) { p = strchr(line, ' '); if (p == NULL) { /* note that when converting .customflags file this case happens in the first line. */ continue; } *p++ = '\0'; if (str_to_uint(line, &idx) < 0 || idx >= MAILDIR_MAX_KEYWORDS || *p == '\0' || hash_table_lookup(mk->hash, p) != NULL) { /* shouldn't happen */ continue; } /* save it */ new_name = p_strdup(mk->pool, p); hash_table_insert(mk->hash, new_name, POINTER_CAST(idx + 1)); strp = array_idx_modifiable(&mk->list, idx); *strp = new_name; } i_stream_destroy(&input); if (close(fd) < 0) { mail_storage_set_critical(mk->storage, "close(%s) failed: %m", mk->path); return -1; } mk->synced = TRUE; return 0; } static int maildir_keywords_lookup(struct maildir_keywords *mk, const char *name, unsigned int *chridx_r) { void *value; value = hash_table_lookup(mk->hash, name); if (value == NULL) { if (mk->synced) return 0; if (maildir_keywords_sync(mk) < 0) return -1; i_assert(mk->synced); value = hash_table_lookup(mk->hash, name); if (value == NULL) return 0; } *chridx_r = POINTER_CAST_TO(value, unsigned int)-1; return 1; } static void maildir_keywords_create(struct maildir_keywords *mk, const char *name, unsigned int chridx) { const char **strp; char *new_name; i_assert(chridx < MAILDIR_MAX_KEYWORDS); new_name = p_strdup(mk->pool, name); hash_table_insert(mk->hash, new_name, POINTER_CAST(chridx + 1)); strp = array_idx_modifiable(&mk->list, chridx); *strp = new_name; mk->changed = TRUE; } static int maildir_keywords_lookup_or_create(struct maildir_keywords *mk, const char *name, unsigned int *chridx_r) { const char *const *keywords; unsigned int i, count; int ret; if ((ret = maildir_keywords_lookup(mk, name, chridx_r)) != 0) return ret; /* see if we are full */ keywords = array_get(&mk->list, &count); for (i = 0; i < count; i++) { if (keywords[i] == NULL) break; } if (i == count && count >= MAILDIR_MAX_KEYWORDS) return -1; if (!maildir_uidlist_is_locked(mk->mbox->uidlist)) return -1; maildir_keywords_create(mk, name, i); *chridx_r = i; return 1; } static const char * maildir_keywords_idx(struct maildir_keywords *mk, unsigned int idx) { const char *const *keywords; unsigned int count; keywords = array_get(&mk->list, &count); if (idx >= count) { if (mk->synced) return NULL; if (maildir_keywords_sync(mk) < 0) return NULL; i_assert(mk->synced); keywords = array_get(&mk->list, &count); } return idx >= count ? NULL : keywords[idx]; } static int maildir_keywords_write_fd(struct maildir_keywords *mk, const char *path, int fd) { struct maildir_mailbox *mbox = mk->mbox; struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *const *keywords; unsigned int i, count; string_t *str; struct stat st; str = t_str_new(256); keywords = array_get(&mk->list, &count); for (i = 0; i < count; i++) { if (keywords[i] != NULL) str_printfa(str, "%u %s\n", i, keywords[i]); } if (write_full(fd, str_data(str), str_len(str)) < 0) { mail_storage_set_critical(mk->storage, "write_full(%s) failed: %m", path); return -1; } if (fstat(fd, &st) < 0) { mail_storage_set_critical(mk->storage, "fstat(%s) failed: %m", path); return -1; } if (st.st_gid != perm->file_create_gid && perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(mk->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(mk->storage, "fchown(%s) failed: %m", path); } } } /* mtime must grow every time */ if (st.st_mtime <= mk->synced_mtime) { struct utimbuf ut; mk->synced_mtime = ioloop_time <= mk->synced_mtime ? mk->synced_mtime + 1 : ioloop_time; ut.actime = ioloop_time; ut.modtime = mk->synced_mtime; if (utime(path, &ut) < 0) { mail_storage_set_critical(mk->storage, "utime(%s) failed: %m", path); return -1; } } if (fsync(fd) < 0) { mail_storage_set_critical(mk->storage, "fsync(%s) failed: %m", path); return -1; } return 0; } static int maildir_keywords_commit(struct maildir_keywords *mk) { const struct mailbox_permissions *perm; struct dotlock *dotlock; const char *lock_path; mode_t old_mask; int i, fd; mk->synced = FALSE; if (!mk->changed || mk->mbox == NULL) return 0; lock_path = t_strconcat(mk->path, ".lock", NULL); i_unlink_if_exists(lock_path); perm = mailbox_get_permissions(&mk->mbox->box); for (i = 0;; i++) { /* we could just create the temp file directly, but doing it this ways avoids potential problems with overwriting contents in malicious symlinks */ old_mask = umask(0777 & ~perm->file_create_mode); fd = file_dotlock_open(&mk->dotlock_settings, mk->path, DOTLOCK_CREATE_FLAG_NONBLOCK, &dotlock); umask(old_mask); if (fd != -1) break; if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) { mail_storage_set_critical(mk->storage, "file_dotlock_open(%s) failed: %m", mk->path); return -1; } /* the control dir doesn't exist. create it unless the whole mailbox was just deleted. */ if (!maildir_set_deleted(&mk->mbox->box)) return -1; } if (maildir_keywords_write_fd(mk, lock_path, fd) < 0) { file_dotlock_delete(&dotlock); return -1; } if (file_dotlock_replace(&dotlock, 0) < 0) { mail_storage_set_critical(mk->storage, "file_dotlock_replace(%s) failed: %m", mk->path); return -1; } mk->changed = FALSE; return 0; } struct maildir_keywords_sync_ctx * maildir_keywords_sync_init(struct maildir_keywords *mk, struct mail_index *index) { struct maildir_keywords_sync_ctx *ctx; ctx = i_new(struct maildir_keywords_sync_ctx, 1); ctx->mk = mk; ctx->index = index; ctx->keywords = mail_index_get_keywords(index); i_array_init(&ctx->idx_to_chr, MAILDIR_MAX_KEYWORDS); return ctx; } struct maildir_keywords_sync_ctx * maildir_keywords_sync_init_readonly(struct maildir_keywords *mk, struct mail_index *index) { struct maildir_keywords_sync_ctx *ctx; ctx = maildir_keywords_sync_init(mk, index); ctx->readonly = TRUE; return ctx; } void maildir_keywords_sync_deinit(struct maildir_keywords_sync_ctx **_ctx) { struct maildir_keywords_sync_ctx *ctx = *_ctx; *_ctx = NULL; T_BEGIN { (void)maildir_keywords_commit(ctx->mk); } T_END; array_free(&ctx->idx_to_chr); i_free(ctx); } unsigned int maildir_keywords_char_idx(struct maildir_keywords_sync_ctx *ctx, char keyword) { const char *name; unsigned int chridx, idx; i_assert(keyword >= MAILDIR_KEYWORD_FIRST && keyword <= MAILDIR_KEYWORD_LAST); chridx = keyword - MAILDIR_KEYWORD_FIRST; if (ctx->chridx_to_idx[chridx] != 0) return ctx->chridx_to_idx[chridx]; /* lookup / create */ name = maildir_keywords_idx(ctx->mk, chridx); if (name == NULL) { /* name is lost. just generate one ourself. */ name = t_strdup_printf("unknown-%u", chridx); while (maildir_keywords_lookup(ctx->mk, name, &idx) > 0) { /* don't create a duplicate name. keep changing the name until it doesn't exist */ name = t_strconcat(name, "?", NULL); } maildir_keywords_create(ctx->mk, name, chridx); } mail_index_keyword_lookup_or_create(ctx->index, name, &idx); ctx->chridx_to_idx[chridx] = idx; return idx; } char maildir_keywords_idx_char(struct maildir_keywords_sync_ctx *ctx, unsigned int idx) { const char *const *name_p; char *chr_p; unsigned int chridx; int ret; chr_p = array_idx_modifiable(&ctx->idx_to_chr, idx); if (*chr_p != '\0') return *chr_p; name_p = array_idx(ctx->keywords, idx); ret = !ctx->readonly ? maildir_keywords_lookup_or_create(ctx->mk, *name_p, &chridx) : maildir_keywords_lookup(ctx->mk, *name_p, &chridx); if (ret <= 0) return '\0'; *chr_p = chridx + MAILDIR_KEYWORD_FIRST; return *chr_p; } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-util.c0000644000175000017500000002265313165463624020636 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "str.h" #include "mkdir-parents.h" #include "mailbox-list-private.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include "maildir-filename-flags.h" #include "maildir-sync.h" #include #include #include #include #include #include #define MAILDIR_RESYNC_RETRY_COUNT 10 static const char * maildir_filename_guess(struct maildir_mailbox *mbox, uint32_t uid, const char *fname, enum maildir_uidlist_rec_flag *uidlist_flags, bool *have_flags_r) { struct mail_index_view *view = mbox->flags_view; struct maildir_keywords_sync_ctx *kw_ctx; enum mail_flags flags; ARRAY_TYPE(keyword_indexes) keywords; const char *p; uint32_t seq; if (view == NULL || !mail_index_lookup_seq(view, uid, &seq)) { *have_flags_r = FALSE; return fname; } t_array_init(&keywords, 32); mail_index_lookup_view_flags(view, seq, &flags, &keywords); if (array_count(&keywords) == 0) { *have_flags_r = (flags & MAIL_FLAGS_NONRECENT) != 0; fname = maildir_filename_flags_set(fname, flags); } else { *have_flags_r = TRUE; kw_ctx = maildir_keywords_sync_init_readonly(mbox->keywords, mbox->box.index); fname = maildir_filename_flags_kw_set(kw_ctx, fname, flags, &keywords); maildir_keywords_sync_deinit(&kw_ctx); } if (*have_flags_r) { /* don't even bother looking into new/ dir */ *uidlist_flags &= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; } else if ((*uidlist_flags & MAILDIR_UIDLIST_REC_FLAG_MOVED) == 0 && ((*uidlist_flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 || mailbox_recent_flags_have_uid(&mbox->box, uid))) { /* probably in new/ dir, drop ":2," from fname */ *uidlist_flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; p = strrchr(fname, MAILDIR_INFO_SEP); if (p != NULL) fname = t_strdup_until(fname, p); } return fname; } static int maildir_file_do_try(struct maildir_mailbox *mbox, uint32_t uid, maildir_file_do_func *callback, void *context) { const char *path, *fname; enum maildir_uidlist_rec_flag flags; bool have_flags; int ret; ret = maildir_sync_lookup(mbox, uid, &flags, &fname); if (ret <= 0) return ret == 0 ? -2 : -1; if ((flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) { /* let's see if we can guess the filename based on index */ fname = maildir_filename_guess(mbox, uid, fname, &flags, &have_flags); } /* make a copy, just in case callback refreshes uidlist and the pointer becomes invalid. */ fname = t_strdup(fname); ret = 0; if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) { /* probably in new/ dir */ path = t_strconcat(mailbox_get_path(&mbox->box), "/new/", fname, NULL); ret = callback(mbox, path, context); } if (ret == 0) { path = t_strconcat(mailbox_get_path(&mbox->box), "/cur/", fname, NULL); ret = callback(mbox, path, context); } if (ret > 0 && (flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) { /* file was found. make sure we remember its latest name. */ maildir_uidlist_update_fname(mbox->uidlist, fname); } else if (ret == 0 && (flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) == 0) { /* file wasn't found. mark this message nonsynced, so we can retry the lookup by guessing the flags */ maildir_uidlist_add_flags(mbox->uidlist, fname, MAILDIR_UIDLIST_REC_FLAG_NONSYNCED); } return ret; } static int do_racecheck(struct maildir_mailbox *mbox, const char *path, void *context) { const uint32_t *uidp = context; struct stat st; int ret; ret = lstat(path, &st); if (ret == 0 && (st.st_mode & S_IFMT) == S_IFLNK) { /* most likely a symlink pointing to a nonexistent file */ mail_storage_set_critical(&mbox->storage->storage, "Maildir: Symlink destination doesn't exist for UID=%u: %s", *uidp, path); return -2; } else if (ret < 0 && errno != ENOENT) { mail_storage_set_critical(&mbox->storage->storage, "lstat(%s) failed: %m", path); return -1; } else { /* success or ENOENT, either way we're done */ mail_storage_set_critical(&mbox->storage->storage, "maildir_file_do(%s): Filename keeps changing for UID=%u", path, *uidp); return -1; } } #undef maildir_file_do int maildir_file_do(struct maildir_mailbox *mbox, uint32_t uid, maildir_file_do_func *callback, void *context) { int i, ret; T_BEGIN { ret = maildir_file_do_try(mbox, uid, callback, context); } T_END; if (ret == 0 && mbox->storage->set->maildir_very_dirty_syncs) T_BEGIN { /* try guessing again with refreshed flags */ if (maildir_sync_refresh_flags_view(mbox) == 0) ret = maildir_file_do_try(mbox, uid, callback, context); } T_END; for (i = 0; i < MAILDIR_RESYNC_RETRY_COUNT && ret == 0; i++) { /* file is either renamed or deleted. sync the maildir and see which one. if file appears to be renamed constantly, don't try to open it more than 10 times. */ if (maildir_storage_sync_force(mbox, uid) < 0) return -1; T_BEGIN { ret = maildir_file_do_try(mbox, uid, callback, context); } T_END; } if (i == MAILDIR_RESYNC_RETRY_COUNT) T_BEGIN { ret = maildir_file_do_try(mbox, uid, do_racecheck, &uid); } T_END; return ret == -2 ? 0 : ret; } static int maildir_create_path(struct mailbox *box, const char *path, enum mailbox_list_path_type type, bool retry) { const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *p, *parent; if (mkdir_chgrp(path, perm->dir_create_mode, perm->file_create_gid, perm->file_create_gid_origin) == 0) return 0; switch (errno) { case EEXIST: return 0; case ENOENT: p = strrchr(path, '/'); if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX || p == NULL || !retry) { /* mailbox was being deleted just now */ mailbox_set_deleted(box); return -1; } /* create index/control root directory */ parent = t_strdup_until(path, p); if (mailbox_list_mkdir_root(box->list, parent, type) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } /* should work now, try again */ return maildir_create_path(box, path, type, FALSE); default: mail_storage_set_critical(box->storage, "mkdir(%s) failed: %m", path); return -1; } } static int maildir_create_subdirs(struct mailbox *box) { static const char *subdirs[] = { "cur", "new", "tmp" }; const char *dirs[N_ELEMENTS(subdirs) + 2]; enum mailbox_list_path_type types[N_ELEMENTS(subdirs) + 2]; struct stat st; const char *path; unsigned int i, count; /* @UNSAFE: get a list of directories we want to create */ for (i = 0; i < N_ELEMENTS(subdirs); i++) { types[i] = MAILBOX_LIST_PATH_TYPE_MAILBOX; dirs[i] = t_strconcat(mailbox_get_path(box), "/", subdirs[i], NULL); } if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL, &path) > 0) { types[i] = MAILBOX_LIST_PATH_TYPE_CONTROL; dirs[i++] = path; } if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path) > 0) { types[i] = MAILBOX_LIST_PATH_TYPE_INDEX; dirs[i++] = path; } count = i; i_assert(count <= N_ELEMENTS(dirs)); for (i = 0; i < count; i++) { path = dirs[i]; if (stat(path, &st) == 0) continue; if (errno != ENOENT) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); break; } if (maildir_create_path(box, path, types[i], TRUE) < 0) break; } return i == N_ELEMENTS(dirs) ? 0 : -1; } bool maildir_set_deleted(struct mailbox *box) { struct stat st; int ret; if (stat(mailbox_get_path(box), &st) < 0) { if (errno == ENOENT) mailbox_set_deleted(box); else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", mailbox_get_path(box)); } return FALSE; } /* maildir itself exists. create all of its subdirectories in case they got lost. */ T_BEGIN { ret = maildir_create_subdirs(box); } T_END; return ret < 0 ? FALSE : TRUE; } int maildir_lose_unexpected_dir(struct mail_storage *storage, const char *path) { const char *dest, *fname, *p; /* There's a directory in maildir, get rid of it. In some installations this was caused by a messed up configuration where e.g. mails was initially delivered to new/new/ directory. Also Dovecot v2.0.0 - v2.0.4 sometimes may have renamed tmp/ directory under new/ or cur/. */ if (rmdir(path) == 0) { mail_storage_set_critical(storage, "Maildir: rmdir()ed unwanted empty directory: %s", path); return 1; } else if (errno == ENOENT) { /* someone else rmdired or renamed it */ return 0; } else if (errno != ENOTEMPTY) { mail_storage_set_critical(storage, "Maildir: Found unwanted directory %s, " "but rmdir() failed: %m", path); return -1; } /* It's not safe to delete this directory since it has some files in it, but it's also not helpful to log this message over and over again. Get rid of this error by renaming the directory elsewhere */ p = strrchr(path, '/'); i_assert(p != NULL); fname = p + 1; while (p != path && p[-1] != '/') p--; i_assert(p != NULL); dest = t_strconcat(t_strdup_until(path, p), "extra-", fname, NULL); if (rename(path, dest) == 0) { mail_storage_set_critical(storage, "Maildir: renamed unwanted directory %s to %s", path, dest); return 1; } else if (errno == ENOENT) { /* someone else renamed it (could have been flag change) */ return 0; } else { mail_storage_set_critical(storage, "Maildir: Found unwanted directory, " "but rename(%s, %s) failed: %m", path, dest); return -1; } } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-sync.h0000644000175000017500000000441513165463624020636 00000000000000#ifndef MAILDIR_SYNC_H #define MAILDIR_SYNC_H /* All systems accessing the filesystem must have their clock less than this many seconds apart from each others. 0 works only for local filesystems. */ #define MAILDIR_SYNC_SECS 1 /* After moving this many mails from new/ to cur/, check if we need to touch the uidlist lock. */ #define MAILDIR_SLOW_MOVE_COUNT 100 /* readdir() should be pretty fast to do, but check anyway every n files to see if we need to touch the uidlist lock. */ #define MAILDIR_SLOW_CHECK_COUNT 10000 /* If syncing takes longer than this, log a warning. */ #define MAILDIR_SYNC_TIME_WARN_SECS MAIL_TRANSACTION_LOG_LOCK_WARN_SECS struct maildir_mailbox; struct maildir_sync_context; struct maildir_keywords_sync_ctx; struct maildir_index_sync_context; int maildir_sync_is_synced(struct maildir_mailbox *mbox); struct mailbox_sync_context * maildir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); int maildir_storage_sync_force(struct maildir_mailbox *mbox, uint32_t uid); int maildir_sync_header_refresh(struct maildir_mailbox *mbox); int maildir_sync_index_begin(struct maildir_mailbox *mbox, struct maildir_sync_context *maildir_sync_ctx, struct maildir_index_sync_context **ctx_r) ATTR_NULL(2); int maildir_sync_index(struct maildir_index_sync_context *sync_ctx, bool partial); int maildir_sync_index_commit(struct maildir_index_sync_context **_ctx); void maildir_sync_index_rollback(struct maildir_index_sync_context **_ctx); struct maildir_keywords_sync_ctx * maildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx); void maildir_sync_set_racing(struct maildir_sync_context *ctx); void maildir_sync_notify(struct maildir_sync_context *ctx); void maildir_sync_set_new_msgs_count(struct maildir_index_sync_context *ctx, unsigned int count); int maildir_sync_refresh_flags_view(struct maildir_mailbox *mbox); int maildir_sync_lookup(struct maildir_mailbox *mbox, uint32_t uid, enum maildir_uidlist_rec_flag *flags_r, const char **fname_r); int maildir_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq); void maildir_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-save.c0000644000175000017500000007501413165463624020616 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "fdatasync-path.h" #include "eacces-error.h" #include "str.h" #include "index-mail.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include "maildir-filename.h" #include "maildir-filename-flags.h" #include "maildir-sync.h" #include #include #include #include #include #define MAILDIR_FILENAME_FLAG_MOVED 0x10000000 struct maildir_filename { struct maildir_filename *next; const char *tmp_name, *dest_basename; const char *pop3_uidl, *guid; uoff_t size, vsize; enum mail_flags flags; unsigned int pop3_order; unsigned int preserve_filename:1; unsigned int keywords_count; /* unsigned int keywords[]; */ }; struct maildir_save_context { struct mail_save_context ctx; pool_t pool; struct maildir_mailbox *mbox; struct mail_index_transaction *trans; struct maildir_uidlist_sync_ctx *uidlist_sync_ctx; struct maildir_keywords_sync_ctx *keywords_sync_ctx; struct maildir_index_sync_context *sync_ctx; struct mail *cur_dest_mail; const char *tmpdir, *newdir, *curdir; struct maildir_filename *files, **files_tail, *file_last; unsigned int files_count; buffer_t keywords_buffer; ARRAY_TYPE(keyword_indexes) keywords_array; struct istream *input; int fd; uint32_t first_seq, seq, last_nonrecent_uid; unsigned int have_keywords:1; unsigned int have_preserved_filenames:1; unsigned int locked:1; unsigned int failed:1; unsigned int last_save_finished:1; unsigned int locked_uidlist_refresh:1; }; static int maildir_file_move(struct maildir_save_context *ctx, struct maildir_filename *mf, const char *destname, bool newdir) { struct mail_storage *storage = &ctx->mbox->storage->storage; const char *tmp_path, *new_path; i_assert(*destname != '\0'); i_assert(*mf->tmp_name != '\0'); /* if we have flags, we'll move it to cur/ directly, because files in new/ directory can't have flags. alternative would be to write it in new/ and set the flags dirty in index file, but in that case external MUAs would see wrong flags. */ tmp_path = t_strconcat(ctx->tmpdir, "/", mf->tmp_name, NULL); new_path = newdir ? t_strconcat(ctx->newdir, "/", destname, NULL) : t_strconcat(ctx->curdir, "/", destname, NULL); /* maildir spec says we should use link() + unlink() here. however since our filename is guaranteed to be unique, rename() works just as well, except faster. even if the filename wasn't unique, the problem could still happen if the file was already moved from new/ to cur/, so link() doesn't really provide any safety anyway. Besides the small temporary performance benefits, this rename() is almost required with OSX's HFS+ filesystem, since it implements hard links in a pretty ugly way, which makes the performance crawl when a lot of hard links are used. */ if (rename(tmp_path, new_path) == 0) { mf->flags |= MAILDIR_FILENAME_FLAG_MOVED; return 0; } else if (ENOQUOTA(errno)) { mail_storage_set_error(storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); return -1; } else { mail_storage_set_critical(storage, "rename(%s, %s) failed: %m", tmp_path, new_path); return -1; } } static struct mail_save_context * maildir_save_transaction_init(struct mailbox_transaction_context *t) { struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->box; struct maildir_save_context *ctx; const char *path; pool_t pool; pool = pool_alloconly_create("maildir_save_context", 4096); ctx = p_new(pool, struct maildir_save_context, 1); ctx->ctx.transaction = t; ctx->pool = pool; ctx->mbox = mbox; ctx->trans = t->itrans; ctx->files_tail = &ctx->files; ctx->fd = -1; path = mailbox_get_path(&mbox->box); ctx->tmpdir = p_strconcat(pool, path, "/tmp", NULL); ctx->newdir = p_strconcat(pool, path, "/new", NULL); ctx->curdir = p_strconcat(pool, path, "/cur", NULL); buffer_create_from_const_data(&ctx->keywords_buffer, "", 0); array_create_from_buffer(&ctx->keywords_array, &ctx->keywords_buffer, sizeof(unsigned int)); ctx->last_save_finished = TRUE; return &ctx->ctx; } struct maildir_filename * maildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname, struct mail *src_mail) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mail_save_data *mdata = &_ctx->data; struct maildir_filename *mf; struct istream *input; unsigned int keyword_count; i_assert(*tmp_fname != '\0'); /* allow caller to specify recent flag only when uid is specified (we're replicating, converting, etc.). */ if (mdata->uid == 0) mdata->flags |= MAIL_RECENT; else if ((mdata->flags & MAIL_RECENT) == 0 && ctx->last_nonrecent_uid < mdata->uid) ctx->last_nonrecent_uid = mdata->uid; /* now, we want to be able to rollback the whole append session, so we'll just store the name of this temp file and move it later into new/ or cur/. */ /* @UNSAFE */ keyword_count = mdata->keywords == NULL ? 0 : mdata->keywords->count; mf = p_malloc(ctx->pool, MALLOC_ADD(sizeof(*mf), MALLOC_MULTIPLY(sizeof(unsigned int), keyword_count))); mf->tmp_name = mf->dest_basename = p_strdup(ctx->pool, tmp_fname); mf->flags = mdata->flags; mf->size = (uoff_t)-1; mf->vsize = (uoff_t)-1; ctx->file_last = mf; i_assert(*ctx->files_tail == NULL); *ctx->files_tail = mf; ctx->files_tail = &mf->next; ctx->files_count++; if (mdata->keywords != NULL) { /* @UNSAFE */ mf->keywords_count = keyword_count; memcpy(mf + 1, mdata->keywords->idx, sizeof(unsigned int) * keyword_count); ctx->have_keywords = TRUE; } if (mdata->pop3_uidl != NULL) mf->pop3_uidl = p_strdup(ctx->pool, mdata->pop3_uidl); mf->pop3_order = mdata->pop3_order; /* insert into index */ mail_index_append(ctx->trans, mdata->uid, &ctx->seq); mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, mdata->flags & ~MAIL_RECENT); if (mdata->keywords != NULL) { mail_index_update_keywords(ctx->trans, ctx->seq, MODIFY_REPLACE, mdata->keywords); } if (mdata->min_modseq != 0) { mail_index_update_modseq(ctx->trans, ctx->seq, mdata->min_modseq); } if (ctx->first_seq == 0) { ctx->first_seq = ctx->seq; i_assert(ctx->files->next == NULL); } mail_set_seq_saving(_ctx->dest_mail, ctx->seq); if (ctx->input == NULL) { /* copying with hardlinking. */ i_assert(src_mail != NULL); index_copy_cache_fields(_ctx, src_mail, ctx->seq); ctx->cur_dest_mail = NULL; } else { input = index_mail_cache_parse_init(_ctx->dest_mail, ctx->input); i_stream_unref(&ctx->input); ctx->input = input; ctx->cur_dest_mail = _ctx->dest_mail; } return mf; } void maildir_save_set_dest_basename(struct mail_save_context *_ctx, struct maildir_filename *mf, const char *basename) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; mf->preserve_filename = TRUE; mf->dest_basename = p_strdup(ctx->pool, basename); ctx->have_preserved_filenames = TRUE; } void maildir_save_set_sizes(struct maildir_filename *mf, uoff_t size, uoff_t vsize) { mf->size = size; mf->vsize = vsize; } static bool maildir_get_dest_filename(struct maildir_save_context *ctx, struct maildir_filename *mf, const char **fname_r) { const char *basename = mf->dest_basename; if (mf->size != (uoff_t)-1 && !mf->preserve_filename) { basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename, MAILDIR_EXTRA_FILE_SIZE, mf->size); } if (mf->vsize != (uoff_t)-1 && !mf->preserve_filename) { basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename, MAILDIR_EXTRA_VIRTUAL_SIZE, mf->vsize); } if (mf->keywords_count == 0) { if ((mf->flags & MAIL_FLAGS_MASK) == MAIL_RECENT) { *fname_r = basename; return TRUE; } *fname_r = maildir_filename_flags_set(basename, mf->flags & MAIL_FLAGS_MASK); return FALSE; } i_assert(ctx->keywords_sync_ctx != NULL || mf->keywords_count == 0); buffer_create_from_const_data(&ctx->keywords_buffer, mf + 1, mf->keywords_count * sizeof(unsigned int)); *fname_r = maildir_filename_flags_kw_set(ctx->keywords_sync_ctx, basename, mf->flags & MAIL_FLAGS_MASK, &ctx->keywords_array); return FALSE; } static const char *maildir_mf_get_path(struct maildir_save_context *ctx, struct maildir_filename *mf) { const char *fname, *dir; if ((mf->flags & MAILDIR_FILENAME_FLAG_MOVED) == 0) { /* file is still in tmp/ */ return t_strdup_printf("%s/%s", ctx->tmpdir, mf->tmp_name); } /* already moved to new/ or cur/ */ dir = maildir_get_dest_filename(ctx, mf, &fname) ? ctx->newdir : ctx->curdir; return t_strdup_printf("%s/%s", dir, fname); } static struct maildir_filename * maildir_save_get_mf(struct mailbox_transaction_context *t, uint32_t seq) { struct maildir_save_context *save_ctx = (struct maildir_save_context *)t->save_ctx; struct maildir_filename *mf; i_assert(seq >= save_ctx->first_seq); seq -= save_ctx->first_seq; mf = save_ctx->files; while (seq > 0) { mf = mf->next; i_assert(mf != NULL); seq--; } return mf; } int maildir_save_file_get_size(struct mailbox_transaction_context *t, uint32_t seq, bool vsize, uoff_t *size_r) { struct maildir_filename *mf = maildir_save_get_mf(t, seq); *size_r = vsize ? mf->vsize : mf->size; return *size_r == (uoff_t)-1 ? -1 : 0; } const char *maildir_save_file_get_path(struct mailbox_transaction_context *t, uint32_t seq) { struct maildir_save_context *save_ctx = (struct maildir_save_context *)t->save_ctx; struct maildir_filename *mf = maildir_save_get_mf(t, seq); return maildir_mf_get_path(save_ctx, mf); } static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir, const char **fname_r) { struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); size_t prefix_len; const char *tmp_fname; string_t *path; mode_t old_mask; int fd; path = t_str_new(256); str_append(path, dir); str_append_c(path, '/'); prefix_len = str_len(path); do { tmp_fname = maildir_filename_generate(); str_truncate(path, prefix_len); str_append(path, tmp_fname); /* the generated filename is unique. the only reason why it might return an existing filename is if the time moved backwards. so we'll use O_EXCL anyway, although it's mostly useless. */ old_mask = umask(0777 & ~perm->file_create_mode); fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); umask(old_mask); } while (fd == -1 && errno == EEXIST); *fname_r = tmp_fname; if (fd == -1) { if (ENOQUOTA(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); } else { mail_storage_set_critical(box->storage, "open(%s) failed: %m", str_c(path)); } } else if (perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", str_c(path), perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", str_c(path)); } } } return fd; } struct mail_save_context * maildir_save_alloc(struct mailbox_transaction_context *t) { i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (t->save_ctx == NULL) t->save_ctx = maildir_save_transaction_init(t); return t->save_ctx; } int maildir_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct maildir_filename *mf; /* new mail, new failure state */ ctx->failed = FALSE; T_BEGIN { /* create a new file in tmp/ directory */ const char *fname; ctx->fd = maildir_create_tmp(ctx->mbox, ctx->tmpdir, &fname); if (ctx->fd == -1) ctx->failed = TRUE; else { if (ctx->mbox->storage->storage.set->mail_save_crlf) ctx->input = i_stream_create_crlf(input); else ctx->input = i_stream_create_lf(input); mf = maildir_save_add(_ctx, fname, NULL); if (_ctx->data.guid != NULL) { maildir_save_set_dest_basename(_ctx, mf, _ctx->data.guid); } } } T_END; if (!ctx->failed) { _ctx->data.output = o_stream_create_fd_file(ctx->fd, 0, FALSE); o_stream_cork(_ctx->data.output); ctx->last_save_finished = FALSE; } return ctx->failed ? -1 : 0; } int maildir_save_continue(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; if (ctx->failed) return -1; do { if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "o_stream_send_istream(%s/%s) " "failed: %m", ctx->tmpdir, ctx->file_last->tmp_name); } ctx->failed = TRUE; return -1; } if (ctx->cur_dest_mail != NULL) index_mail_cache_parse_continue(ctx->cur_dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; } static int maildir_save_finish_received_date(struct maildir_save_context *ctx, const char *path) { struct mail_storage *storage = &ctx->mbox->storage->storage; struct utimbuf buf; struct stat st; if (ctx->ctx.data.received_date != (time_t)-1) { /* set the received_date by modifying mtime */ buf.actime = ioloop_time; buf.modtime = ctx->ctx.data.received_date; if (utime(path, &buf) < 0) { mail_storage_set_critical(storage, "utime(%s) failed: %m", path); return -1; } } else if (ctx->fd != -1) { if (fstat(ctx->fd, &st) == 0) ctx->ctx.data.received_date = st.st_mtime; else { mail_storage_set_critical(storage, "fstat(%s) failed: %m", path); return -1; } } else { /* hardlinked */ if (stat(path, &st) == 0) ctx->ctx.data.received_date = st.st_mtime; else { mail_storage_set_critical(storage, "stat(%s) failed: %m", path); return -1; } } return 0; } static void maildir_save_remove_last_filename(struct maildir_save_context *ctx) { struct maildir_filename **fm; index_storage_save_abort_last(&ctx->ctx, ctx->seq); ctx->seq--; for (fm = &ctx->files; (*fm)->next != NULL; fm = &(*fm)->next) ; i_assert(*fm == ctx->file_last); *fm = NULL; ctx->files_tail = fm; ctx->file_last = NULL; ctx->files_count--; } static int maildir_save_finish_real(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; const char *path, *output_errstr; off_t real_size; uoff_t size; int output_errno; ctx->last_save_finished = TRUE; if (ctx->failed && ctx->fd == -1) { /* tmp file creation failed */ return -1; } path = t_strconcat(ctx->tmpdir, "/", ctx->file_last->tmp_name, NULL); if (!ctx->failed && o_stream_nfinish(_ctx->data.output) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %s", path, o_stream_get_error(_ctx->data.output)); } ctx->failed = TRUE; } if (_ctx->data.save_date != (time_t)-1) { /* we can't change ctime, but we can add the date to cache */ struct index_mail *mail = (struct index_mail *)_ctx->dest_mail; uint32_t t = _ctx->data.save_date; index_mail_cache_add(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t)); } if (maildir_save_finish_received_date(ctx, path) < 0) ctx->failed = TRUE; if (ctx->cur_dest_mail != NULL) { index_mail_cache_parse_deinit(ctx->cur_dest_mail, ctx->ctx.data.received_date, !ctx->failed); } i_stream_unref(&ctx->input); /* remember the size in case we want to add it to filename */ ctx->file_last->size = _ctx->data.output->offset; if (ctx->cur_dest_mail == NULL || mail_get_virtual_size(ctx->cur_dest_mail, &ctx->file_last->vsize) < 0) ctx->file_last->vsize = (uoff_t)-1; output_errno = _ctx->data.output->last_failed_errno; output_errstr = t_strdup(o_stream_get_error(_ctx->data.output)); o_stream_destroy(&_ctx->data.output); if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER && !ctx->failed) { if (fsync(ctx->fd) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "fsync(%s) failed: %m", path); } ctx->failed = TRUE; } } real_size = lseek(ctx->fd, 0, SEEK_END); if (real_size == (off_t)-1) { mail_storage_set_critical(storage, "lseek(%s) failed: %m", path); } else if (real_size != (off_t)ctx->file_last->size && (!maildir_filename_get_size(ctx->file_last->dest_basename, MAILDIR_EXTRA_FILE_SIZE, &size) || size != ctx->file_last->size)) { /* e.g. zlib plugin was used. the "physical size" must be in the maildir filename, since stat() will return wrong size */ ctx->file_last->preserve_filename = FALSE; /* preserve the GUID if needed */ if (ctx->file_last->guid == NULL) ctx->file_last->guid = ctx->file_last->dest_basename; /* reset the base name as well, just in case there's a ,W=vsize */ ctx->file_last->dest_basename = ctx->file_last->tmp_name; } if (close(ctx->fd) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "close(%s) failed: %m", path); } ctx->failed = TRUE; } ctx->fd = -1; if (ctx->failed) { /* delete the tmp file */ i_unlink_if_exists(path); if (ENOQUOTA(output_errno)) { mail_storage_set_error(storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); } else if (output_errno != 0) { mail_storage_set_critical(storage, "write(%s) failed: %s", path, output_errstr); } maildir_save_remove_last_filename(ctx); return -1; } ctx->file_last = NULL; return 0; } int maildir_save_finish(struct mail_save_context *ctx) { int ret; T_BEGIN { ret = maildir_save_finish_real(ctx); } T_END; index_save_context_free(ctx); return ret; } void maildir_save_cancel(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; ctx->failed = TRUE; (void)maildir_save_finish(_ctx); } static void maildir_save_unlink_files(struct maildir_save_context *ctx) { struct maildir_filename *mf; for (mf = ctx->files; mf != NULL; mf = mf->next) T_BEGIN { i_unlink(maildir_mf_get_path(ctx, mf)); } T_END; ctx->files = NULL; } static int maildir_transaction_fsync_dirs(struct maildir_save_context *ctx, bool new_changed, bool cur_changed) { struct mail_storage *storage = &ctx->mbox->storage->storage; if (storage->set->parsed_fsync_mode == FSYNC_MODE_NEVER) return 0; if (new_changed) { if (fdatasync_path(ctx->newdir) < 0) { mail_storage_set_critical(storage, "fdatasync_path(%s) failed: %m", ctx->newdir); return -1; } } if (cur_changed) { if (fdatasync_path(ctx->curdir) < 0) { mail_storage_set_critical(storage, "fdatasync_path(%s) failed: %m", ctx->curdir); return -1; } } return 0; } static int seq_range_cmp(const struct seq_range *r1, const struct seq_range *r2) { if (r1->seq1 < r2->seq2) return -1; else if (r1->seq1 > r2->seq2) return 1; else return 0; } static uint32_t maildir_save_set_recent_flags(struct maildir_save_context *ctx) { struct maildir_mailbox *mbox = ctx->mbox; ARRAY_TYPE(seq_range) saved_sorted_uids; const struct seq_range *uids; unsigned int i, count; uint32_t uid; count = array_count(&ctx->ctx.transaction->changes->saved_uids); if (count == 0) return 0; t_array_init(&saved_sorted_uids, count); array_append_array(&saved_sorted_uids, &ctx->ctx.transaction->changes->saved_uids); array_sort(&saved_sorted_uids, seq_range_cmp); uids = array_get(&saved_sorted_uids, &count); for (i = 0; i < count; i++) { for (uid = uids[i].seq1; uid <= uids[i].seq2; uid++) mailbox_recent_flags_set_uid(&mbox->box, uid); } return uids[count-1].seq2 + 1; } static int maildir_save_sync_index(struct maildir_save_context *ctx) { struct mailbox_transaction_context *_t = ctx->ctx.transaction; struct maildir_mailbox *mbox = ctx->mbox; uint32_t first_uid, next_uid, first_recent_uid; int ret; /* we'll need to keep the lock past the sync deinit */ ret = maildir_uidlist_lock(mbox->uidlist); i_assert(ret > 0); if (maildir_sync_header_refresh(mbox) < 0) return -1; if ((ret = maildir_uidlist_refresh_fast_init(mbox->uidlist)) < 0) return -1; if (ret == 0) { /* uidlist doesn't exist. make sure all existing message are added to uidlist first. */ (void)maildir_storage_sync_force(mbox, 0); } if (maildir_sync_index_begin(mbox, NULL, &ctx->sync_ctx) < 0) return -1; ctx->keywords_sync_ctx = maildir_sync_get_keywords_sync_ctx(ctx->sync_ctx); /* now that uidlist is locked, make sure all the existing mails have been added to index. we don't really look into the maildir, just add all the new mails listed in dovecot-uidlist to index. */ if (maildir_sync_index(ctx->sync_ctx, TRUE) < 0) return -1; /* if messages were added to index, assign them UIDs */ first_uid = maildir_uidlist_get_next_uid(mbox->uidlist); i_assert(first_uid != 0); mail_index_append_finish_uids(ctx->trans, first_uid, &_t->changes->saved_uids); i_assert(ctx->files_count == seq_range_count(&_t->changes->saved_uids)); /* these mails are all recent in our session */ T_BEGIN { next_uid = maildir_save_set_recent_flags(ctx); } T_END; if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) first_recent_uid = next_uid; else if (ctx->last_nonrecent_uid != 0) first_recent_uid = ctx->last_nonrecent_uid + 1; else first_recent_uid = 0; if (first_recent_uid != 0) { /* maildir_sync_index() dropped recent flags from existing messages. we'll still need to drop recent flags from these newly added messages. */ mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } return 0; } static void maildir_save_rollback_index_changes(struct maildir_save_context *ctx) { uint32_t seq; if (ctx->seq == 0) return; for (seq = ctx->seq; seq >= ctx->first_seq; seq--) mail_index_expunge(ctx->trans, seq); mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans); } static bool maildir_filename_has_conflict(struct maildir_filename *mf, struct maildir_filename *prev_mf) { if (strcmp(mf->dest_basename, prev_mf->dest_basename) == 0) { /* already used this */ return TRUE; } if (prev_mf->guid != NULL && strcmp(mf->dest_basename, prev_mf->guid) == 0) { /* previous filename also had a conflict */ return TRUE; } return FALSE; } static void maildir_filename_check_conflicts(struct maildir_save_context *ctx, struct maildir_filename *mf, struct maildir_filename *prev_mf) { uoff_t size, vsize; if (!ctx->locked_uidlist_refresh && ctx->locked) { (void)maildir_uidlist_refresh(ctx->mbox->uidlist); ctx->locked_uidlist_refresh = TRUE; } if (!maildir_filename_get_size(mf->dest_basename, MAILDIR_EXTRA_FILE_SIZE, &size)) size = (uoff_t)-1; if (!maildir_filename_get_size(mf->dest_basename, MAILDIR_EXTRA_VIRTUAL_SIZE, &vsize)) vsize = (uoff_t)-1; if (size != mf->size || vsize != mf->vsize || !ctx->locked_uidlist_refresh || (prev_mf != NULL && maildir_filename_has_conflict(mf, prev_mf)) || maildir_uidlist_get_full_filename(ctx->mbox->uidlist, mf->dest_basename) != NULL) { /* a) dest_basename didn't contain the (correct) size/vsize. they're required for good performance. b) file already exists. give it another name. but preserve the size/vsize in the filename if possible */ if (mf->size == (uoff_t)-1) mf->size = size; if (mf->vsize == (uoff_t)-1) mf->vsize = size; mf->guid = mf->dest_basename; mf->dest_basename = p_strdup(ctx->pool, maildir_filename_generate()); mf->preserve_filename = FALSE; } } static int maildir_filename_dest_basename_cmp(struct maildir_filename *const *f1, struct maildir_filename *const *f2) { return strcmp((*f1)->dest_basename, (*f2)->dest_basename); } static int maildir_save_move_files_to_newcur(struct maildir_save_context *ctx) { ARRAY(struct maildir_filename *) files; struct maildir_filename *mf, *const *mfp, *prev_mf; bool newdir, new_changed, cur_changed; int ret; /* put files into an array sorted by the destination filename. this way we can easily check if there are duplicate destination filenames within this transaction. */ t_array_init(&files, ctx->files_count); for (mf = ctx->files; mf != NULL; mf = mf->next) array_append(&files, &mf, 1); array_sort(&files, maildir_filename_dest_basename_cmp); new_changed = cur_changed = FALSE; prev_mf = NULL; array_foreach(&files, mfp) { mf = *mfp; T_BEGIN { const char *dest; if (mf->preserve_filename) { maildir_filename_check_conflicts(ctx, mf, prev_mf); } newdir = maildir_get_dest_filename(ctx, mf, &dest); if (newdir) new_changed = TRUE; else cur_changed = TRUE; ret = maildir_file_move(ctx, mf, dest, newdir); } T_END; if (ret < 0) return -1; prev_mf = mf; } if (ctx->locked) { i_assert(ctx->sync_ctx != NULL); maildir_sync_set_new_msgs_count(ctx->sync_ctx, array_count(&files)); } return maildir_transaction_fsync_dirs(ctx, new_changed, cur_changed); } static void maildir_save_sync_uidlist(struct maildir_save_context *ctx) { struct mailbox_transaction_context *t = ctx->ctx.transaction; struct maildir_filename *mf; struct seq_range_iter iter; enum maildir_uidlist_rec_flag flags; struct maildir_uidlist_rec *rec; unsigned int n = 0; uint32_t uid; bool newdir, bret; int ret; seq_range_array_iter_init(&iter, &t->changes->saved_uids); for (mf = ctx->files; mf != NULL; mf = mf->next) T_BEGIN { const char *dest; bret = seq_range_array_iter_nth(&iter, n++, &uid); i_assert(bret); newdir = maildir_get_dest_filename(ctx, mf, &dest); flags = MAILDIR_UIDLIST_REC_FLAG_RECENT; if (newdir) flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR; ret = maildir_uidlist_sync_next_uid(ctx->uidlist_sync_ctx, dest, uid, flags, &rec); i_assert(ret > 0); i_assert(rec != NULL); if (mf->guid != NULL) { maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec, MAILDIR_UIDLIST_REC_EXT_GUID, mf->guid); } if (mf->pop3_uidl != NULL) { maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec, MAILDIR_UIDLIST_REC_EXT_POP3_UIDL, mf->pop3_uidl); } if (mf->pop3_order > 0) { maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec, MAILDIR_UIDLIST_REC_EXT_POP3_ORDER, t_strdup_printf("%u", mf->pop3_order)); } } T_END; i_assert(!seq_range_array_iter_nth(&iter, n, &uid)); } int maildir_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; enum maildir_uidlist_sync_flags sync_flags; int ret; i_assert(_ctx->data.output == NULL); i_assert(ctx->last_save_finished); if (ctx->files_count == 0) return 0; sync_flags = MAILDIR_UIDLIST_SYNC_PARTIAL | MAILDIR_UIDLIST_SYNC_NOREFRESH; if ((_t->flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0) { /* we want to assign UIDs, we must lock uidlist */ } else if (ctx->have_keywords) { /* keywords file updating relies on uidlist lock. */ } else if (ctx->have_preserved_filenames) { /* we're trying to use some potentially existing filenames. we must lock to avoid race conditions where two sessions try to save the same filename. */ } else { /* no requirement to lock uidlist. if we happen to get a lock, assign uids. */ sync_flags |= MAILDIR_UIDLIST_SYNC_TRYLOCK; } ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags, &ctx->uidlist_sync_ctx); if (ret > 0) { ctx->locked = TRUE; if (maildir_save_sync_index(ctx) < 0) { maildir_transaction_save_rollback(_ctx); return -1; } } else if (ret == 0 && (sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0) { ctx->locked = FALSE; i_assert(ctx->uidlist_sync_ctx == NULL); /* since we couldn't lock uidlist, we'll have to drop the appends to index. */ maildir_save_rollback_index_changes(ctx); } else { maildir_transaction_save_rollback(_ctx); return -1; } T_BEGIN { ret = maildir_save_move_files_to_newcur(ctx); } T_END; if (ctx->locked) { if (ret == 0) { /* update dovecot-uidlist file. */ maildir_save_sync_uidlist(ctx); } if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, ret == 0) < 0) ret = -1; } _t->changes->uid_validity = maildir_uidlist_get_uid_validity(ctx->mbox->uidlist); if (ctx->locked) { /* It doesn't matter if index syncing fails */ ctx->keywords_sync_ctx = NULL; if (ret < 0) maildir_sync_index_rollback(&ctx->sync_ctx); else (void)maildir_sync_index_commit(&ctx->sync_ctx); } if (ret < 0) { ctx->keywords_sync_ctx = !ctx->have_keywords ? NULL : maildir_keywords_sync_init(ctx->mbox->keywords, ctx->mbox->box.index); /* unlink the files we just moved in an attempt to rollback the transaction. uidlist is still locked, so at least other Dovecot instances haven't yet seen the files. we need to have the keywords sync context to be able to generate the destination filenames if keywords were used. */ maildir_save_unlink_files(ctx); if (ctx->keywords_sync_ctx != NULL) maildir_keywords_sync_deinit(&ctx->keywords_sync_ctx); /* returning failure finishes the save_context */ maildir_transaction_save_rollback(_ctx); return -1; } return 0; } void maildir_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result ATTR_UNUSED) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; _ctx->transaction = NULL; /* transaction is already freed */ if (ctx->locked) maildir_uidlist_unlock(ctx->mbox->uidlist); pool_unref(&ctx->pool); } void maildir_transaction_save_rollback(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; i_assert(_ctx->data.output == NULL); if (!ctx->last_save_finished) maildir_save_cancel(&ctx->ctx); /* delete files in tmp/ */ maildir_save_unlink_files(ctx); if (ctx->uidlist_sync_ctx != NULL) (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, FALSE); if (ctx->sync_ctx != NULL) maildir_sync_index_rollback(&ctx->sync_ctx); if (ctx->locked) maildir_uidlist_unlock(ctx->mbox->uidlist); pool_unref(&ctx->pool); } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-filename-flags.c0000644000175000017500000001123013165463624022520 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "maildir-storage.h" #include "maildir-keywords.h" #include "maildir-filename-flags.h" void maildir_filename_flags_get(struct maildir_keywords_sync_ctx *ctx, const char *fname, enum mail_flags *flags_r, ARRAY_TYPE(keyword_indexes) *keywords_r) { const char *info; array_clear(keywords_r); *flags_r = 0; info = strrchr(fname, MAILDIR_INFO_SEP); if (info == NULL || info[1] != '2' || info[2] != MAILDIR_FLAGS_SEP) return; for (info += 3; *info != '\0' && *info != MAILDIR_FLAGS_SEP; info++) { switch (*info) { case 'R': /* replied */ *flags_r |= MAIL_ANSWERED; break; case 'S': /* seen */ *flags_r |= MAIL_SEEN; break; case 'T': /* trashed */ *flags_r |= MAIL_DELETED; break; case 'D': /* draft */ *flags_r |= MAIL_DRAFT; break; case 'F': /* flagged */ *flags_r |= MAIL_FLAGGED; break; default: if (*info >= MAILDIR_KEYWORD_FIRST && *info <= MAILDIR_KEYWORD_LAST) { int idx; idx = maildir_keywords_char_idx(ctx, *info); if (idx < 0) { /* unknown keyword. */ break; } array_append(keywords_r, (unsigned int *)&idx, 1); break; } /* unknown flag - ignore */ break; } } } static int char_cmp(const void *p1, const void *p2) { const unsigned char *c1 = p1, *c2 = p2; return *c1 - *c2; } static void maildir_filename_append_keywords(struct maildir_keywords_sync_ctx *ctx, ARRAY_TYPE(keyword_indexes) *keywords, string_t *fname) { const unsigned int *indexes; unsigned int i, count; size_t start = str_len(fname); char chr; indexes = array_get(keywords, &count); for (i = 0; i < count; i++) { chr = maildir_keywords_idx_char(ctx, indexes[i]); if (chr != '\0') str_append_c(fname, chr); } qsort(str_c_modifiable(fname) + start, str_len(fname) - start, 1, char_cmp); } static const char * ATTR_NULL(1, 4) maildir_filename_flags_full_set(struct maildir_keywords_sync_ctx *ctx, const char *fname, enum mail_flags flags, ARRAY_TYPE(keyword_indexes) *keywords) { string_t *flags_str; enum mail_flags flags_left; const char *info, *oldflags; int nextflag; /* remove the old :info from file name, and get the old flags */ info = strrchr(fname, MAILDIR_INFO_SEP); if (info != NULL && strrchr(fname, '/') > info) info = NULL; oldflags = ""; if (info != NULL) { fname = t_strdup_until(fname, info); if (info[1] == '2' && info[2] == MAILDIR_FLAGS_SEP) oldflags = info+3; } /* insert the new flags between old flags. flags must be sorted by their ASCII code. unknown flags are kept. */ flags_str = t_str_new(256); str_append(flags_str, fname); str_append(flags_str, MAILDIR_FLAGS_FULL_SEP); flags_left = flags; for (;;) { /* skip all known flags */ while (*oldflags == 'D' || *oldflags == 'F' || *oldflags == 'R' || *oldflags == 'S' || *oldflags == 'T' || (*oldflags >= MAILDIR_KEYWORD_FIRST && *oldflags <= MAILDIR_KEYWORD_LAST)) oldflags++; nextflag = *oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP ? 256 : (unsigned char) *oldflags; if ((flags_left & MAIL_DRAFT) && nextflag > 'D') { str_append_c(flags_str, 'D'); flags_left &= ~MAIL_DRAFT; } if ((flags_left & MAIL_FLAGGED) && nextflag > 'F') { str_append_c(flags_str, 'F'); flags_left &= ~MAIL_FLAGGED; } if ((flags_left & MAIL_ANSWERED) && nextflag > 'R') { str_append_c(flags_str, 'R'); flags_left &= ~MAIL_ANSWERED; } if ((flags_left & MAIL_SEEN) && nextflag > 'S') { str_append_c(flags_str, 'S'); flags_left &= ~MAIL_SEEN; } if ((flags_left & MAIL_DELETED) && nextflag > 'T') { str_append_c(flags_str, 'T'); flags_left &= ~MAIL_DELETED; } if (keywords != NULL && array_is_created(keywords) && nextflag > MAILDIR_KEYWORD_FIRST) { maildir_filename_append_keywords(ctx, keywords, flags_str); keywords = NULL; } if (*oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP) break; str_append_c(flags_str, *oldflags); oldflags++; } if (*oldflags == MAILDIR_FLAGS_SEP) { /* another flagset, we don't know about these, just keep them */ while (*oldflags != '\0') str_append_c(flags_str, *oldflags++); } return str_c(flags_str); } const char *maildir_filename_flags_set(const char *fname, enum mail_flags flags) { return maildir_filename_flags_full_set(NULL, fname, flags, NULL); } const char *maildir_filename_flags_kw_set(struct maildir_keywords_sync_ctx *ctx, const char *fname, enum mail_flags flags, ARRAY_TYPE(keyword_indexes) *keywords) { return maildir_filename_flags_full_set(ctx, fname, flags, keywords); } dovecot-2.2.33.2/src/lib-storage/index/maildir/maildir-storage.h0000644000175000017500000001165613165463624021333 00000000000000#ifndef MAILDIR_STORAGE_H #define MAILDIR_STORAGE_H #include "maildir-settings.h" #define MAILDIR_STORAGE_NAME "maildir" #define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions" #define MAILDIR_UIDVALIDITY_FNAME "dovecot-uidvalidity" /* "base,S=123:2," means: [ [..]] 2 */ #define MAILDIR_INFO_SEP ':' #define MAILDIR_EXTRA_SEP ',' #define MAILDIR_FLAGS_SEP ',' #define MAILDIR_INFO_SEP_S ":" #define MAILDIR_EXTRA_SEP_S "," #define MAILDIR_FLAGS_SEP_S "," /* ":2," is the standard flags separator */ #define MAILDIR_FLAGS_FULL_SEP MAILDIR_INFO_SEP_S "2" MAILDIR_FLAGS_SEP_S #define MAILDIR_KEYWORD_FIRST 'a' #define MAILDIR_KEYWORD_LAST 'z' #define MAILDIR_MAX_KEYWORDS (MAILDIR_KEYWORD_LAST - MAILDIR_KEYWORD_FIRST + 1) /* Maildir++ extension: include file size in the filename to avoid stat() */ #define MAILDIR_EXTRA_FILE_SIZE 'S' /* Something (can't remember what anymore) could use 'W' in filename to avoid calculating file's virtual size (added missing CRs). */ #define MAILDIR_EXTRA_VIRTUAL_SIZE 'W' /* Delete files having ctime older than this from tmp/. 36h is standard. */ #define MAILDIR_TMP_DELETE_SECS (36*60*60) /* How often to touch the uidlist lock file when it's locked. This is done both when using KEEP_LOCKED flag and when syncing a large maildir. */ #define MAILDIR_LOCK_TOUCH_SECS 10 /* If an operation fails with ENOENT, we'll check if the mailbox is deleted or if some directory is just missing. If it's missing, we'll create the directories and try again this many times before failing. */ #define MAILDIR_DELETE_RETRY_COUNT 3 #include "index-storage.h" struct timeval; struct maildir_save_context; struct maildir_copy_context; struct maildir_index_header { uint32_t new_check_time, new_mtime, new_mtime_nsecs; uint32_t cur_check_time, cur_mtime, cur_mtime_nsecs; uint32_t uidlist_mtime, uidlist_mtime_nsecs, uidlist_size; }; struct maildir_list_index_record { uint32_t new_mtime, cur_mtime; }; struct maildir_storage { struct mail_storage storage; const struct maildir_settings *set; const char *temp_prefix; }; struct maildir_mailbox { struct mailbox box; struct maildir_storage *storage; struct mail_index_view *flags_view; struct timeout *keep_lock_to; /* Filled lazily by mailbox_get_private_flags_mask() */ enum mail_flags _private_flags_mask; /* maildir sync: */ struct maildir_uidlist *uidlist; struct maildir_keywords *keywords; struct maildir_index_header maildir_hdr; uint32_t maildir_ext_id; uint32_t maildir_list_index_ext_id; unsigned int synced:1; unsigned int syncing_commit:1; unsigned int private_flags_mask_set:1; unsigned int backend_readonly:1; unsigned int backend_readonly_set:1; unsigned int sync_uidlist_refreshed:1; }; extern struct mail_vfuncs maildir_mail_vfuncs; /* Return -1 = error, 0 = file not found, 1 = ok */ typedef int maildir_file_do_func(struct maildir_mailbox *mbox, const char *path, void *context); int maildir_file_do(struct maildir_mailbox *mbox, uint32_t uid, maildir_file_do_func *callback, void *context); #define maildir_file_do(mbox, seq, callback, context) \ maildir_file_do(mbox, seq + \ CALLBACK_TYPECHECK(callback, int (*)( \ struct maildir_mailbox *, const char *, typeof(context))), \ (maildir_file_do_func *)callback, context) bool maildir_set_deleted(struct mailbox *box); uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list); int maildir_lose_unexpected_dir(struct mail_storage *storage, const char *path); bool maildir_is_backend_readonly(struct maildir_mailbox *mbox); struct mail_save_context * maildir_save_alloc(struct mailbox_transaction_context *_t); int maildir_save_begin(struct mail_save_context *ctx, struct istream *input); int maildir_save_continue(struct mail_save_context *ctx); int maildir_save_finish(struct mail_save_context *ctx); void maildir_save_cancel(struct mail_save_context *ctx); struct maildir_filename * maildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname, struct mail *src_mail) ATTR_NULL(3); void maildir_save_set_dest_basename(struct mail_save_context *ctx, struct maildir_filename *mf, const char *basename); void maildir_save_set_sizes(struct maildir_filename *mf, uoff_t size, uoff_t vsize); int maildir_save_file_get_size(struct mailbox_transaction_context *t, uint32_t seq, bool vsize, uoff_t *size_r); const char *maildir_save_file_get_path(struct mailbox_transaction_context *t, uint32_t seq); int maildir_transaction_save_commit_pre(struct mail_save_context *ctx); void maildir_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void maildir_transaction_save_rollback(struct mail_save_context *ctx); int maildir_copy(struct mail_save_context *ctx, struct mail *mail); int maildir_transaction_copy_commit(struct maildir_copy_context *ctx); void maildir_transaction_copy_rollback(struct maildir_copy_context *ctx); #endif dovecot-2.2.33.2/src/lib-storage/index/maildir/Makefile.am0000644000175000017500000000144613123174404020114 00000000000000noinst_LTLIBRARIES = libstorage_maildir.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_maildir_la_SOURCES = \ maildir-copy.c \ maildir-filename.c \ maildir-filename-flags.c \ maildir-keywords.c \ maildir-mail.c \ maildir-save.c \ maildir-settings.c \ maildir-storage.c \ maildir-sync.c \ maildir-sync-index.c \ maildir-uidlist.c \ maildir-util.c headers = \ maildir-filename.h \ maildir-filename-flags.h \ maildir-keywords.h \ maildir-storage.h \ maildir-settings.h \ maildir-sync.h \ maildir-uidlist.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-pop3-uidl.h0000644000175000017500000000072613123174404017531 00000000000000#ifndef INDEX_POP3_H #define INDEX_POP3_H struct mail_index_transaction; struct mail; struct mailbox; struct mailbox_transaction_context; void index_pop3_uidl_set_max_uid(struct mailbox *box, struct mail_index_transaction *trans, uint32_t uid); bool index_pop3_uidl_can_exist(struct mail *mail); void index_pop3_uidl_update_exists(struct mail *mail, bool exists); void index_pop3_uidl_update_exists_finish(struct mailbox_transaction_context *trans); #endif dovecot-2.2.33.2/src/lib-storage/index/index-search.c0000644000175000017500000014121513165463624017167 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "istream.h" #include "utc-offset.h" #include "str.h" #include "time-util.h" #include "unichar.h" #include "imap-match.h" #include "message-address.h" #include "message-date.h" #include "message-search.h" #include "message-parser.h" #include "mail-index-modseq.h" #include "index-storage.h" #include "index-mail.h" #include "index-sort.h" #include "mail-search.h" #include "mailbox-search-result-private.h" #include "index-search-private.h" #include #define SEARCH_NOTIFY_INTERVAL_SECS 10 #define SEARCH_COST_DENTRY 3ULL #define SEARCH_COST_ATTR 1ULL #define SEARCH_COST_FILES_READ 25ULL #define SEARCH_COST_KBYTE 15ULL #define SEARCH_COST_CACHE 1ULL #define SEARCH_MIN_NONBLOCK_USECS 200000 #define SEARCH_MAX_NONBLOCK_USECS 250000 #define SEARCH_INITIAL_MAX_COST 30000 #define SEARCH_RECALC_MIN_USECS 50000 struct search_header_context { struct index_search_context *index_ctx; struct index_mail *imail; struct mail_search_arg *args; struct message_block decoded_block; bool decoded_block_set; struct message_header_line *hdr; unsigned int parse_headers:1; unsigned int custom_header:1; unsigned int threading:1; }; struct search_body_context { struct index_search_context *index_ctx; struct istream *input; struct message_part *part; }; static void search_parse_msgset_args(unsigned int messages_count, struct mail_search_arg *args, uint32_t *seq1_r, uint32_t *seq2_r); static void ATTR_NULL(2) search_none(struct mail_search_arg *arg ATTR_UNUSED, void *ctx ATTR_UNUSED) { } static void search_set_failed(struct index_search_context *ctx) { if (ctx->failed) return; /* remember the first failure */ mail_storage_last_error_push(ctx->box->storage); ctx->failed = TRUE; } static void search_cur_mail_failed(struct index_search_context *ctx) { switch (mailbox_get_last_mail_error(ctx->cur_mail->box)) { case MAIL_ERROR_EXPUNGED: ctx->mail_ctx.seen_lost_data = TRUE; break; case MAIL_ERROR_LOOKUP_ABORTED: /* expected failure */ break; default: search_set_failed(ctx); break; } } static void search_init_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { struct mailbox_metadata metadata; bool match; switch (arg->type) { case SEARCH_SEQSET: ctx->have_seqsets = TRUE; break; case SEARCH_UIDSET: case SEARCH_INTHREAD: case SEARCH_FLAGS: case SEARCH_KEYWORDS: case SEARCH_MODSEQ: if (arg->type == SEARCH_MODSEQ) mail_index_modseq_enable(ctx->box->index); ctx->have_index_args = TRUE; break; case SEARCH_MAILBOX_GUID: if (mailbox_get_metadata(ctx->box, MAILBOX_METADATA_GUID, &metadata) < 0) { /* result will be unknown */ break; } match = strcmp(guid_128_to_string(metadata.guid), arg->value.str) == 0; if (match != arg->match_not) arg->match_always = TRUE; else arg->nonmatch_always = TRUE; break; case SEARCH_MAILBOX: case SEARCH_MAILBOX_GLOB: ctx->have_mailbox_args = TRUE; break; case SEARCH_ALL: if (!arg->match_not) arg->match_always = TRUE; else arg->nonmatch_always = TRUE; break; default: break; } } static void search_seqset_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { if (arg->type == SEARCH_SEQSET) { if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq)) ARG_SET_RESULT(arg, 1); else ARG_SET_RESULT(arg, 0); } } static int search_arg_match_keywords(struct index_search_context *ctx, struct mail_search_arg *arg) { ARRAY_TYPE(keyword_indexes) keyword_indexes_arr; const struct mail_keywords *search_kws = arg->initialized.keywords; const unsigned int *keyword_indexes; unsigned int i, j, count; if (search_kws->count == 0) { /* invalid keyword - never matches */ return 0; } t_array_init(&keyword_indexes_arr, 128); mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq, &keyword_indexes_arr); keyword_indexes = array_get(&keyword_indexes_arr, &count); /* there probably aren't many keywords, so O(n*m) for now */ for (i = 0; i < search_kws->count; i++) { for (j = 0; j < count; j++) { if (search_kws->idx[i] == keyword_indexes[j]) break; } if (j == count) return 0; } return 1; } static bool index_search_get_pvt(struct index_search_context *ctx, uint32_t uid) { index_transaction_init_pvt(ctx->mail_ctx.transaction); if (ctx->pvt_uid == uid) return ctx->pvt_seq != 0; ctx->pvt_uid = uid; return mail_index_lookup_seq(ctx->mail_ctx.transaction->view_pvt, uid, &ctx->pvt_seq); } /* Returns >0 = matched, 0 = not matched, -1 = unknown */ static int search_arg_match_index(struct index_search_context *ctx, struct mail_search_arg *arg, const struct mail_index_record *rec) { enum mail_flags flags, pvt_flags_mask; uint64_t modseq; int ret; switch (arg->type) { case SEARCH_UIDSET: case SEARCH_INTHREAD: return seq_range_exists(&arg->value.seqset, rec->uid); case SEARCH_FLAGS: /* recent flag shouldn't be set, but indexes from v1.0.x may contain it. */ flags = rec->flags & ~MAIL_RECENT; if ((arg->value.flags & MAIL_RECENT) != 0 && mailbox_recent_flags_have_uid(ctx->box, rec->uid)) flags |= MAIL_RECENT; if (ctx->box->view_pvt == NULL) { /* no private view (set by view syncing) -> no private flags */ } else { pvt_flags_mask = mailbox_get_private_flags_mask(ctx->box); flags &= ~pvt_flags_mask; if (index_search_get_pvt(ctx, rec->uid)) { rec = mail_index_lookup(ctx->mail_ctx.transaction->view_pvt, ctx->pvt_seq); flags |= rec->flags & pvt_flags_mask; } } return (flags & arg->value.flags) == arg->value.flags; case SEARCH_KEYWORDS: T_BEGIN { ret = search_arg_match_keywords(ctx, arg); } T_END; return ret; case SEARCH_MODSEQ: { if (arg->value.flags != 0) { modseq = mail_index_modseq_lookup_flags(ctx->view, arg->value.flags, ctx->mail_ctx.seq); } else if (arg->initialized.keywords != NULL) { modseq = mail_index_modseq_lookup_keywords(ctx->view, arg->initialized.keywords, ctx->mail_ctx.seq); } else { modseq = mail_index_modseq_lookup(ctx->view, ctx->mail_ctx.seq); } return modseq >= arg->value.modseq->modseq; } default: return -1; } } static void search_index_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { const struct mail_index_record *rec; rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq); switch (search_arg_match_index(ctx, arg, rec)) { case -1: /* unknown */ break; case 0: ARG_SET_RESULT(arg, 0); break; default: ARG_SET_RESULT(arg, 1); break; } } /* Returns >0 = matched, 0 = not matched, -1 = unknown */ static int search_arg_match_mailbox(struct index_search_context *ctx, struct mail_search_arg *arg) { struct mailbox *box = ctx->cur_mail->box; const char *str; switch (arg->type) { case SEARCH_MAILBOX: /* first try to match the mailbox name itself. this is important when using "mailbox virtual/foo" parameter foin doveadm's search query, otherwise we can never fetch anything with doveadm from virtual mailboxes because the mailbox parameter is compared to the mail's backend mailbox. */ if (strcmp(box->vname, arg->value.str) == 0) return 1; if (mail_get_special(ctx->cur_mail, MAIL_FETCH_MAILBOX_NAME, &str) < 0) { search_cur_mail_failed(ctx); return -1; } if (strcasecmp(str, "INBOX") == 0) return strcasecmp(arg->value.str, "INBOX") == 0; return strcmp(str, arg->value.str) == 0; case SEARCH_MAILBOX_GLOB: if (imap_match(arg->initialized.mailbox_glob, box->vname) == IMAP_MATCH_YES) return 1; if (mail_get_special(ctx->cur_mail, MAIL_FETCH_MAILBOX_NAME, &str) < 0) { search_cur_mail_failed(ctx); return -1; } return imap_match(arg->initialized.mailbox_glob, str) == IMAP_MATCH_YES; default: return -1; } } static void search_mailbox_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { switch (search_arg_match_mailbox(ctx, arg)) { case -1: /* unknown */ break; case 0: ARG_SET_RESULT(arg, 0); break; default: ARG_SET_RESULT(arg, 1); break; } } /* Returns >0 = matched, 0 = not matched, -1 = unknown */ static int search_arg_match_cached(struct index_search_context *ctx, struct mail_search_arg *arg) { const char *str; struct tm *tm; uoff_t virtual_size; time_t date; int tz_offset; bool have_tz_offset; switch (arg->type) { /* internal dates */ case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: have_tz_offset = FALSE; tz_offset = 0; date = (time_t)-1; switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: if (mail_get_date(ctx->cur_mail, &date, &tz_offset) < 0) { search_cur_mail_failed(ctx); return -1; } have_tz_offset = TRUE; break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: if (mail_get_received_date(ctx->cur_mail, &date) < 0) { search_cur_mail_failed(ctx); return -1; } break; case MAIL_SEARCH_DATE_TYPE_SAVED: if (mail_get_save_date(ctx->cur_mail, &date) < 0) { search_cur_mail_failed(ctx); return -1; } break; } if ((arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) { if (!have_tz_offset) { tm = localtime(&date); tz_offset = utc_offset(tm, date); } date += tz_offset * 60; } switch (arg->type) { case SEARCH_BEFORE: return date < arg->value.time; case SEARCH_ON: return date >= arg->value.time && date < arg->value.time + 3600*24; case SEARCH_SINCE: return date >= arg->value.time; default: /* unreachable */ break; } /* sizes */ case SEARCH_SMALLER: case SEARCH_LARGER: if (mail_get_virtual_size(ctx->cur_mail, &virtual_size) < 0) { search_cur_mail_failed(ctx); return -1; } if (arg->type == SEARCH_SMALLER) return virtual_size < arg->value.size; else return virtual_size > arg->value.size; case SEARCH_GUID: if (mail_get_special(ctx->cur_mail, MAIL_FETCH_GUID, &str) < 0) { search_cur_mail_failed(ctx); return -1; } return strcmp(str, arg->value.str) == 0; case SEARCH_REAL_UID: { struct mail *real_mail; if (mail_get_backend_mail(ctx->cur_mail, &real_mail) < 0) { search_cur_mail_failed(ctx); return -1; } return seq_range_exists(&arg->value.seqset, real_mail->uid); } default: return -1; } } static void search_cached_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { switch (search_arg_match_cached(ctx, arg)) { case -1: /* unknown */ break; case 0: ARG_SET_RESULT(arg, 0); break; default: ARG_SET_RESULT(arg, 1); break; } } static int search_sent(enum mail_search_arg_type type, time_t search_time, const unsigned char *sent_value, size_t sent_value_len) { time_t sent_time; int timezone_offset; if (sent_value == NULL) return 0; /* NOTE: RFC-3501 specifies that timezone is ignored in searches. sent_time is returned as UTC, so change it. */ if (!message_date_parse(sent_value, sent_value_len, &sent_time, &timezone_offset)) return 0; sent_time += timezone_offset * 60; switch (type) { case SEARCH_BEFORE: return sent_time < search_time; case SEARCH_ON: return sent_time >= search_time && sent_time < search_time + 3600*24; case SEARCH_SINCE: return sent_time >= search_time; default: i_unreached(); } } static struct message_search_context * msg_search_arg_context(struct index_search_context *ctx, struct mail_search_arg *arg) { enum message_search_flags flags = 0; if (arg->context == NULL) T_BEGIN { string_t *dtc = t_str_new(128); if (ctx->mail_ctx.normalizer(arg->value.str, strlen(arg->value.str), dtc) < 0) i_panic("search key not utf8: %s", arg->value.str); if (arg->type == SEARCH_BODY) flags |= MESSAGE_SEARCH_FLAG_SKIP_HEADERS; /* we don't get here if arg is "", but dtc can be "" if it only contains characters that we need to ignore. handle those searches by returning them as non-matched. */ if (str_len(dtc) > 0) { arg->context = message_search_init(str_c(dtc), ctx->mail_ctx.normalizer, flags); } } T_END; return arg->context; } static void compress_lwsp(string_t *dest, const unsigned char *src, size_t src_len) { size_t i; bool prev_lwsp = TRUE; for (i = 0; i < src_len; i++) { if (IS_LWSP(src[i])) { if (!prev_lwsp) { prev_lwsp = TRUE; str_append_c(dest, ' '); } } else { prev_lwsp = FALSE; str_append_c(dest, src[i]); } } } static void search_header_arg(struct mail_search_arg *arg, struct search_header_context *ctx) { struct message_search_context *msg_search_ctx; struct message_block block; struct message_header_line hdr; int ret; /* first check that the field name matches to argument. */ switch (arg->type) { case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT) return; /* date is handled differently than others */ if (strcasecmp(ctx->hdr->name, "Date") == 0) { if (ctx->hdr->continues) { ctx->hdr->use_full_value = TRUE; return; } ret = search_sent(arg->type, arg->value.time, ctx->hdr->full_value, ctx->hdr->full_value_len); ARG_SET_RESULT(arg, ret); } return; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: ctx->custom_header = TRUE; if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0) return; break; default: return; } if (arg->value.str[0] == '\0') { /* we're just testing existence of the field. always matches. */ ARG_SET_RESULT(arg, 1); return; } if (ctx->hdr->continues) { ctx->hdr->use_full_value = TRUE; return; } i_zero(&block); /* We're searching only for values, so drop header name and middle parts. We use header searching so that MIME words will be decoded. */ hdr = *ctx->hdr; hdr.name = ""; hdr.name_len = 0; hdr.middle_len = 0; block.hdr = &hdr; msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg); if (msg_search_ctx == NULL) return; if (!ctx->decoded_block_set) { T_BEGIN { struct message_address *addr; string_t *str; switch (arg->type) { case SEARCH_HEADER: /* simple match */ break; case SEARCH_HEADER_ADDRESS: /* we have to match against normalized address */ addr = message_address_parse(pool_datastack_create(), ctx->hdr->full_value, ctx->hdr->full_value_len, UINT_MAX, TRUE); str = t_str_new(ctx->hdr->value_len); message_address_write(str, addr); hdr.value = hdr.full_value = str_data(str); hdr.value_len = hdr.full_value_len = str_len(str); break; case SEARCH_HEADER_COMPRESS_LWSP: /* convert LWSP to single spaces */ str = t_str_new(hdr.full_value_len); compress_lwsp(str, hdr.full_value, hdr.full_value_len); hdr.value = hdr.full_value = str_data(str); hdr.value_len = hdr.full_value_len = str_len(str); break; default: i_unreached(); } ret = message_search_more_get_decoded(msg_search_ctx, &block, &ctx->decoded_block) ? 1 : 0; ctx->decoded_block_set = TRUE; } T_END; } else { /* this block was already decoded and saved by an earlier search arg. use the already-decoded block to avoid duplicating work. */ ret = message_search_more_decoded(msg_search_ctx, &ctx->decoded_block) ? 1 : 0; } /* there may be multiple headers. don't mark this failed yet. */ if (ret > 0) ARG_SET_RESULT(arg, 1); } static void search_header_unmatch(struct mail_search_arg *arg, struct search_header_context *ctx ATTR_UNUSED) { switch (arg->type) { case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT) break; if (arg->match_not) { /* date header not found, so we match only for NOT searches */ ARG_SET_RESULT(arg, 0); } break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: ARG_SET_RESULT(arg, 0); break; default: break; } } static void search_header(struct message_header_line *hdr, struct search_header_context *ctx) { if (hdr == NULL) { /* end of headers, mark all unknown SEARCH_HEADERs unmatched */ (void)mail_search_args_foreach(ctx->args, search_header_unmatch, ctx); return; } if (hdr->eoh) return; if (ctx->parse_headers) index_mail_parse_header(NULL, hdr, ctx->imail); if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) { ctx->hdr = hdr; ctx->decoded_block_set = FALSE; ctx->custom_header = FALSE; (void)mail_search_args_foreach(ctx->args, search_header_arg, ctx); } } static void search_body(struct mail_search_arg *arg, struct search_body_context *ctx) { struct message_search_context *msg_search_ctx; const char *error; int ret; switch (arg->type) { case SEARCH_BODY: case SEARCH_TEXT: break; default: return; } msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg); if (msg_search_ctx == NULL) { ARG_SET_RESULT(arg, 0); return; } i_stream_seek(ctx->input, 0); ret = message_search_msg(msg_search_ctx, ctx->input, ctx->part, &error); if (ret < 0 && ctx->input->stream_errno == 0) { /* try again without cached parts */ index_mail_set_message_parts_corrupted(ctx->index_ctx->cur_mail, error); i_stream_seek(ctx->input, 0); ret = message_search_msg(msg_search_ctx, ctx->input, NULL, &error); i_assert(ret >= 0 || ctx->input->stream_errno != 0); } if (ctx->input->stream_errno != 0) { mail_storage_set_critical(ctx->index_ctx->box->storage, "read(%s) failed: %s", i_stream_get_name(ctx->input), i_stream_get_error(ctx->input)); } ARG_SET_RESULT(arg, ret); } static int search_arg_match_text(struct mail_search_arg *args, struct index_search_context *ctx) { const enum message_header_parser_flags hdr_parser_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; struct index_mail *imail = (struct index_mail *)ctx->cur_mail; struct mail *real_mail; struct istream *input = NULL; struct mailbox_header_lookup_ctx *headers_ctx; struct search_header_context hdr_ctx; struct search_body_context body_ctx; const char *const *headers; bool have_headers, have_body, failed = FALSE; int ret; /* first check what we need to use */ headers = mail_search_args_analyze(args, &have_headers, &have_body); if (!have_headers && !have_body) return -1; i_zero(&hdr_ctx); hdr_ctx.index_ctx = ctx; /* hdr_ctx.imail is different from imail for mails in virtual mailboxes */ if (mail_get_backend_mail(ctx->cur_mail, &real_mail) < 0) { search_cur_mail_failed(ctx); return -1; } hdr_ctx.imail = (struct index_mail *)real_mail; hdr_ctx.custom_header = TRUE; hdr_ctx.args = args; headers_ctx = headers == NULL ? NULL : mailbox_header_lookup_init(ctx->box, headers); if (headers != NULL && (!have_body || ctx->cur_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)) { /* try to look up the specified headers from cache */ i_assert(*headers != NULL); if (mail_get_header_stream(ctx->cur_mail, headers_ctx, &input) < 0) { search_cur_mail_failed(ctx); failed = TRUE; } else { message_parse_header(input, NULL, hdr_parser_flags, search_header, &hdr_ctx); } input = NULL; } else if (have_headers) { /* we need to read the entire header */ ret = have_body ? mail_get_stream_because(ctx->cur_mail, NULL, NULL, "search", &input) : mail_get_hdr_stream_because(ctx->cur_mail, NULL, "search", &input); if (ret < 0) { search_cur_mail_failed(ctx); failed = TRUE; } else { hdr_ctx.parse_headers = index_mail_want_parse_headers(hdr_ctx.imail); if (hdr_ctx.parse_headers) { index_mail_parse_header_init(hdr_ctx.imail, headers_ctx); } message_parse_header(input, NULL, hdr_parser_flags, search_header, &hdr_ctx); if (input->stream_errno != 0) { mail_storage_set_critical(ctx->box->storage, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); failed = TRUE; search_set_failed(ctx); } } } if (headers_ctx != NULL) mailbox_header_lookup_unref(&headers_ctx); if (failed) { /* opening mail failed. maybe because of lookup_abort. update access_parts for prefetching */ if (have_body) imail->data.access_part |= READ_HDR | READ_BODY; else imail->data.access_part |= READ_HDR; return -1; } if (have_headers) { /* see if the header search succeeded in finishing the search */ ret = mail_search_args_foreach(args, search_none, (void *)NULL); if (ret >= 0 || !have_body) return ret; } i_assert(have_body); if (ctx->cur_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { imail->data.access_part |= READ_HDR | READ_BODY; return -1; } if (input == NULL) { /* we didn't search headers. */ struct message_size hdr_size; if (mail_get_stream_because(ctx->cur_mail, &hdr_size, NULL, "search", &input) < 0) { search_cur_mail_failed(ctx); return -1; } i_stream_seek(input, hdr_size.physical_size); } i_zero(&body_ctx); body_ctx.index_ctx = ctx; body_ctx.input = input; /* Get parts if they already exist in cache. If they don't, message-search will parse the mail automatically. */ ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; (void)mail_get_parts(ctx->cur_mail, &body_ctx.part); ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; return mail_search_args_foreach(args, search_body, &body_ctx); } static bool search_msgset_fix_limits(unsigned int messages_count, ARRAY_TYPE(seq_range) *seqset, bool match_not) { struct seq_range *range; unsigned int count; i_assert(messages_count > 0); range = array_get_modifiable(seqset, &count); if (count > 0) { i_assert(range[0].seq1 != 0); if (range[count-1].seq2 == (uint32_t)-1) { /* "*" used, make sure the last message is in the range (e.g. with count+1:* we still want to include it) */ seq_range_array_add(seqset, messages_count); } /* remove all nonexistent messages */ seq_range_array_remove_range(seqset, messages_count + 1, (uint32_t)-1); } if (!match_not) return array_count(seqset) > 0; else { /* if all messages are in the range, it can't match */ range = array_get_modifiable(seqset, &count); return count == 0 || range[0].seq1 != 1 || range[count-1].seq2 != messages_count; } } static void search_msgset_fix(unsigned int messages_count, ARRAY_TYPE(seq_range) *seqset, uint32_t *seq1_r, uint32_t *seq2_r, bool match_not) { const struct seq_range *range; unsigned int count; uint32_t min_seq, max_seq; if (!search_msgset_fix_limits(messages_count, seqset, match_not)) { *seq1_r = (uint32_t)-1; *seq2_r = 0; return; } range = array_get(seqset, &count); if (!match_not) { min_seq = range[0].seq1; max_seq = range[count-1].seq2; } else if (count == 0) { /* matches all messages */ min_seq = 1; max_seq = messages_count; } else { min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1; max_seq = range[count-1].seq2 < messages_count ? messages_count : range[count-1].seq1 - 1; if (min_seq > max_seq) { *seq1_r = (uint32_t)-1; *seq2_r = 0; return; } } if (*seq1_r < min_seq || *seq1_r == 0) *seq1_r = min_seq; if (*seq2_r > max_seq) *seq2_r = max_seq; } static void search_or_parse_msgset_args(unsigned int messages_count, struct mail_search_arg *args, uint32_t *seq1_r, uint32_t *seq2_r) { uint32_t seq1, seq2, min_seq1 = 0, max_seq2 = 0; for (; args != NULL; args = args->next) { seq1 = 1; seq2 = messages_count; switch (args->type) { case SEARCH_SUB: i_assert(!args->match_not); search_parse_msgset_args(messages_count, args->value.subargs, &seq1, &seq2); break; case SEARCH_OR: i_assert(!args->match_not); search_or_parse_msgset_args(messages_count, args->value.subargs, &seq1, &seq2); break; case SEARCH_SEQSET: search_msgset_fix(messages_count, &args->value.seqset, &seq1, &seq2, args->match_not); break; default: break; } if (min_seq1 == 0) { min_seq1 = seq1; max_seq2 = seq2; } else { if (seq1 < min_seq1) min_seq1 = seq1; if (seq2 > max_seq2) max_seq2 = seq2; } } i_assert(min_seq1 != 0); if (min_seq1 > *seq1_r) *seq1_r = min_seq1; if (max_seq2 < *seq2_r) *seq2_r = max_seq2; } static void search_parse_msgset_args(unsigned int messages_count, struct mail_search_arg *args, uint32_t *seq1_r, uint32_t *seq2_r) { for (; args != NULL; args = args->next) { switch (args->type) { case SEARCH_SUB: i_assert(!args->match_not); search_parse_msgset_args(messages_count, args->value.subargs, seq1_r, seq2_r); break; case SEARCH_OR: /* go through our children and use the widest seqset range */ i_assert(!args->match_not); search_or_parse_msgset_args(messages_count, args->value.subargs, seq1_r, seq2_r); break; case SEARCH_SEQSET: search_msgset_fix(messages_count, &args->value.seqset, seq1_r, seq2_r, args->match_not); break; default: break; } } } static void search_limit_lowwater(struct index_search_context *ctx, uint32_t uid_lowwater, uint32_t *first_seq) { uint32_t seq1, seq2; if (uid_lowwater == 0) return; (void)mail_index_lookup_seq_range(ctx->view, uid_lowwater, (uint32_t)-1, &seq1, &seq2); if (*first_seq < seq1) *first_seq = seq1; } static bool search_limit_by_hdr(struct index_search_context *ctx, struct mail_search_arg *args, uint32_t *seq1, uint32_t *seq2) { const struct mail_index_header *hdr; enum mail_flags pvt_flags_mask; uint64_t highest_modseq; hdr = mail_index_get_header(ctx->view); /* we can't trust that private view's header is fully up to date, so do this optimization only for non-private flags */ pvt_flags_mask = ctx->box->view_pvt == NULL ? 0 : mailbox_get_private_flags_mask(ctx->box); for (; args != NULL; args = args->next) { switch (args->type) { case SEARCH_ALL: if (args->match_not) { /* NOT ALL - pointless noop query */ return FALSE; } continue; case SEARCH_MODSEQ: /* MODSEQ higher than current HIGHESTMODSEQ? */ highest_modseq = mail_index_modseq_get_highest(ctx->view); if (args->value.modseq->modseq > highest_modseq) return FALSE; continue; default: continue; case SEARCH_FLAGS: break; } if ((args->value.flags & MAIL_SEEN) != 0 && (pvt_flags_mask & MAIL_SEEN) == 0) { /* SEEN with 0 seen? */ if (!args->match_not && hdr->seen_messages_count == 0) return FALSE; if (hdr->seen_messages_count == hdr->messages_count) { /* UNSEEN with all seen? */ if (args->match_not) return FALSE; } else if (args->match_not) { /* UNSEEN with lowwater limiting */ search_limit_lowwater(ctx, hdr->first_unseen_uid_lowwater, seq1); } } if ((args->value.flags & MAIL_DELETED) != 0 && (pvt_flags_mask & MAIL_DELETED) == 0) { /* DELETED with 0 deleted? */ if (!args->match_not && hdr->deleted_messages_count == 0) return FALSE; if (hdr->deleted_messages_count == hdr->messages_count) { /* UNDELETED with all deleted? */ if (args->match_not) return FALSE; } else if (!args->match_not) { /* DELETED with lowwater limiting */ search_limit_lowwater(ctx, hdr->first_deleted_uid_lowwater, seq1); } } } return *seq1 <= *seq2; } static void search_get_seqset(struct index_search_context *ctx, unsigned int messages_count, struct mail_search_arg *args) { if (messages_count == 0) { /* no messages, don't check sequence ranges. although we could give error message then for FETCH, we shouldn't do it for UID FETCH. */ ctx->seq1 = 1; ctx->seq2 = 0; return; } ctx->seq1 = 1; ctx->seq2 = messages_count; search_parse_msgset_args(messages_count, args, &ctx->seq1, &ctx->seq2); if (ctx->seq1 == 0) { ctx->seq1 = 1; ctx->seq2 = messages_count; } if (ctx->seq1 > ctx->seq2) { /* no matches */ return; } /* See if this search query can never match based on data in index's header. We'll scan only the root level args, which is usually enough. */ if (!search_limit_by_hdr(ctx, args, &ctx->seq1, &ctx->seq2)) { /* no matches */ ctx->seq1 = 1; ctx->seq2 = 0; } } static int search_build_subthread(struct mail_thread_iterate_context *iter, ARRAY_TYPE(seq_range) *uids) { struct mail_thread_iterate_context *child_iter; const struct mail_thread_child_node *node; int ret = 0; while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) { if (child_iter != NULL) { if (search_build_subthread(child_iter, uids) < 0) ret = -1; } seq_range_array_add(uids, node->uid); } if (mail_thread_iterate_deinit(&iter) < 0) ret = -1; return ret; } static int search_build_inthread_result(struct index_search_context *ctx, struct mail_search_arg *arg) { struct mail_thread_iterate_context *iter, *child_iter; const struct mail_thread_child_node *node; const ARRAY_TYPE(seq_range) *search_uids; ARRAY_TYPE(seq_range) thread_uids; int ret = 0; /* mail_search_args_init() must have been called by now */ i_assert(arg->initialized.search_args != NULL); p_array_init(&arg->value.seqset, ctx->mail_ctx.args->pool, 64); if (mailbox_search_result_build(ctx->mail_ctx.transaction, arg->initialized.search_args, MAILBOX_SEARCH_RESULT_FLAG_UPDATE | MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC, &arg->value.search_result) < 0) return -1; if (ctx->thread_ctx == NULL) { /* failed earlier */ return -1; } search_uids = mailbox_search_result_get(arg->value.search_result); if (array_count(search_uids) == 0) { /* search found nothing - no threads can match */ return 0; } t_array_init(&thread_uids, 128); iter = mail_thread_iterate_init(ctx->thread_ctx, arg->value.thread_type, FALSE); while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) { seq_range_array_add(&thread_uids, node->uid); if (child_iter != NULL) { if (search_build_subthread(child_iter, &thread_uids) < 0) ret = -1; } if (seq_range_array_have_common(&thread_uids, search_uids)) { /* yes, we want this thread */ seq_range_array_merge(&arg->value.seqset, &thread_uids); } array_clear(&thread_uids); } if (mail_thread_iterate_deinit(&iter) < 0) ret = -1; return ret; } static int search_build_inthreads(struct index_search_context *ctx, struct mail_search_arg *arg) { int ret = 0; for (; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: if (search_build_inthreads(ctx, arg->value.subargs) < 0) ret = -1; break; case SEARCH_INTHREAD: if (search_build_inthread_result(ctx, arg) < 0) ret = -1; break; default: break; } } return ret; } static void wanted_sort_fields_get(struct mailbox *box, const enum mail_sort_type *sort_program, struct mailbox_header_lookup_ctx *wanted_headers, enum mail_fetch_field *wanted_fields_r, struct mailbox_header_lookup_ctx **headers_ctx_r) { ARRAY_TYPE(const_string) headers; const char *header; unsigned int i; *wanted_fields_r = 0; *headers_ctx_r = NULL; t_array_init(&headers, 8); for (i = 0; sort_program[i] != MAIL_SORT_END; i++) { header = NULL; switch (sort_program[i] & MAIL_SORT_MASK) { case MAIL_SORT_ARRIVAL: *wanted_fields_r |= MAIL_FETCH_RECEIVED_DATE; break; case MAIL_SORT_CC: header = "Cc"; break; case MAIL_SORT_DATE: *wanted_fields_r |= MAIL_FETCH_DATE; break; case MAIL_SORT_FROM: header = "From"; break; case MAIL_SORT_SIZE: *wanted_fields_r |= MAIL_FETCH_VIRTUAL_SIZE; break; case MAIL_SORT_SUBJECT: header = "Subject"; break; case MAIL_SORT_TO: header = "To"; break; } if (header != NULL) array_append(&headers, &header, 1); } if (wanted_headers != NULL) { for (i = 0; wanted_headers->name[i] != NULL; i++) array_append(&headers, &wanted_headers->name[i], 1); } if (array_count(&headers) > 0) { array_append_zero(&headers); *headers_ctx_r = mailbox_header_lookup_init(box, array_idx(&headers, 0)); } } struct mail_search_context * index_storage_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct index_search_context *ctx; struct mailbox_status status; ctx = i_new(struct index_search_context, 1); ctx->mail_ctx.transaction = t; ctx->mail_ctx.normalizer = t->box->storage->user->default_normalizer; ctx->box = t->box; ctx->view = t->view; ctx->mail_ctx.args = args; ctx->mail_ctx.sort_program = index_sort_program_init(t, sort_program); ctx->max_mails = t->box->storage->set->mail_prefetch_count + 1; if (ctx->max_mails == 0) ctx->max_mails = UINT_MAX; ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST; if (gettimeofday(&ctx->last_nonblock_timeval, NULL) < 0) i_fatal("gettimeofday() failed: %m"); mailbox_get_open_status(t->box, STATUS_MESSAGES, &status); ctx->mail_ctx.progress_max = status.messages; i_array_init(&ctx->mail_ctx.results, 5); array_create(&ctx->mail_ctx.module_contexts, default_pool, sizeof(void *), 5); i_array_init(&ctx->mails, ctx->max_mails); mail_search_args_reset(ctx->mail_ctx.args->args, TRUE); if (args->have_inthreads) { if (mail_thread_init(t->box, NULL, &ctx->thread_ctx) < 0) search_set_failed(ctx); if (search_build_inthreads(ctx, args->args) < 0) search_set_failed(ctx); } if (sort_program != NULL) { wanted_sort_fields_get(ctx->box, sort_program, wanted_headers, &ctx->mail_ctx.wanted_fields, &ctx->mail_ctx.wanted_headers); } else if (wanted_headers != NULL) { ctx->mail_ctx.wanted_headers = wanted_headers; mailbox_header_lookup_ref(wanted_headers); } ctx->mail_ctx.wanted_fields |= wanted_fields; search_get_seqset(ctx, status.messages, args->args); (void)mail_search_args_foreach(args->args, search_init_arg, ctx); /* Need to reset results for match_always cases */ mail_search_args_reset(ctx->mail_ctx.args->args, FALSE); return &ctx->mail_ctx; } static void ATTR_NULL(2) search_arg_deinit(struct mail_search_arg *arg, struct index_search_context *ctx) { switch (arg->type) { case SEARCH_MIMEPART: index_search_mime_arg_deinit(arg, ctx); break; default: if (arg->context != NULL) { struct message_search_context *search_ctx = arg->context; message_search_deinit(&search_ctx); arg->context = NULL; } } } int index_storage_search_deinit(struct mail_search_context *_ctx) { struct index_search_context *ctx = (struct index_search_context *)_ctx; struct mail **mailp; int ret; ret = ctx->failed ? -1 : 0; mail_search_args_reset(ctx->mail_ctx.args->args, FALSE); (void)mail_search_args_foreach(ctx->mail_ctx.args->args, search_arg_deinit, ctx); if (ctx->mail_ctx.wanted_headers != NULL) mailbox_header_lookup_unref(&ctx->mail_ctx.wanted_headers); if (ctx->mail_ctx.sort_program != NULL) { if (index_sort_program_deinit(&ctx->mail_ctx.sort_program) < 0) ret = -1; } if (ctx->thread_ctx != NULL) mail_thread_deinit(&ctx->thread_ctx); array_free(&ctx->mail_ctx.results); array_free(&ctx->mail_ctx.module_contexts); array_foreach_modifiable(&ctx->mails, mailp) { struct index_mail *imail = (struct index_mail *)*mailp; imail->search_mail = FALSE; mail_free(mailp); } if (ctx->failed) mail_storage_last_error_pop(ctx->box->storage); array_free(&ctx->mails); i_free(ctx); return ret; } static unsigned long long search_get_cost(struct mailbox_transaction_context *trans) { return trans->stats.open_lookup_count * SEARCH_COST_DENTRY + trans->stats.stat_lookup_count * SEARCH_COST_DENTRY + trans->stats.fstat_lookup_count * SEARCH_COST_ATTR + trans->stats.cache_hit_count * SEARCH_COST_CACHE + trans->stats.files_read_count * SEARCH_COST_FILES_READ + (trans->stats.files_read_bytes/1024) * SEARCH_COST_KBYTE; } static int search_match_once(struct index_search_context *ctx) { int ret; ret = mail_search_args_foreach(ctx->mail_ctx.args->args, search_cached_arg, ctx); if (ret < 0) ret = search_arg_match_text(ctx->mail_ctx.args->args, ctx); if (ret < 0) ret = index_search_mime_arg_match(ctx->mail_ctx.args->args, ctx); return ret; } static bool search_arg_is_static(struct mail_search_arg *arg) { struct mail_search_arg *subarg; switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: /* they're static only if all subargs are static */ subarg = arg->value.subargs; for (; subarg != NULL; subarg = subarg->next) { if (!search_arg_is_static(subarg)) return FALSE; } return TRUE; case SEARCH_SEQSET: /* changes between syncs, but we can't really handle this currently. seqsets should be converted to uidsets first. */ case SEARCH_FLAGS: case SEARCH_KEYWORDS: case SEARCH_MODSEQ: case SEARCH_INTHREAD: break; case SEARCH_ALL: case SEARCH_UIDSET: case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: case SEARCH_SMALLER: case SEARCH_LARGER: case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: case SEARCH_BODY: case SEARCH_TEXT: case SEARCH_GUID: case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: case SEARCH_REAL_UID: case SEARCH_MIMEPART: return TRUE; } return FALSE; } static void search_set_static_matches(struct mail_search_arg *arg) { for (; arg != NULL; arg = arg->next) { if (search_arg_is_static(arg)) arg->result = 1; } } static bool search_has_static_nonmatches(struct mail_search_arg *arg) { for (; arg != NULL; arg = arg->next) { if (arg->result == 0 && search_arg_is_static(arg)) return TRUE; } return FALSE; } static void search_match_finish(struct index_search_context *ctx, int match) { if (match == 0 && search_has_static_nonmatches(ctx->mail_ctx.args->args)) { /* if there are saved search results remember that this message never matches */ mailbox_search_results_never(&ctx->mail_ctx, ctx->cur_mail->uid); } } static int search_match_next(struct index_search_context *ctx) { static enum mail_lookup_abort cache_lookups[] = { MAIL_LOOKUP_ABORT_NOT_IN_CACHE, MAIL_LOOKUP_ABORT_READ_MAIL, MAIL_LOOKUP_ABORT_NEVER }; unsigned int i, n = N_ELEMENTS(cache_lookups); int ret = -1; if (ctx->have_mailbox_args) { /* check that the mailbox name matches. this makes sense only with virtual mailboxes. */ ret = mail_search_args_foreach(ctx->mail_ctx.args->args, search_mailbox_arg, ctx); } /* avoid doing extra work for as long as possible */ if (ctx->max_mails > 1) { /* we're doing prefetching. if we have to read the mail, do a prefetch first and the final search later */ n--; } i_assert(ctx->cur_mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER); for (i = 0; i < n && ret < 0; i++) { ctx->cur_mail->lookup_abort = cache_lookups[i]; T_BEGIN { ret = search_match_once(ctx); } T_END; } ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; search_match_finish(ctx, ret); return ret; } static void index_storage_search_notify(struct mailbox *box, struct index_search_context *ctx) { float percentage; unsigned int msecs, secs; if (ctx->last_notify.tv_sec == 0) { /* set the search time in here, in case a plugin already spent some time indexing the mailbox */ ctx->search_start_time = ioloop_timeval; } else if (box->storage->callbacks.notify_ok != NULL && !ctx->mail_ctx.progress_hidden) { percentage = ctx->mail_ctx.progress_cur * 100.0 / ctx->mail_ctx.progress_max; msecs = timeval_diff_msecs(&ioloop_timeval, &ctx->search_start_time); secs = (msecs / (percentage / 100.0) - msecs) / 1000; T_BEGIN { const char *text; text = t_strdup_printf("Searched %d%% of the mailbox, " "ETA %d:%02d", (int)percentage, secs/60, secs%60); box->storage->callbacks. notify_ok(box, text, box->storage->callback_context); } T_END; } ctx->last_notify = ioloop_timeval; } static bool search_would_block(struct index_search_context *ctx) { struct timeval now; unsigned long long guess_cost; long long usecs; bool ret; if (ctx->cost < ctx->next_time_check_cost) return FALSE; if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); usecs = timeval_diff_usecs(&now, &ctx->last_nonblock_timeval); if (usecs < 0) { /* clock moved backwards. */ ctx->last_nonblock_timeval = now; ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST; return TRUE; } else if (usecs < SEARCH_MIN_NONBLOCK_USECS) { /* not finished yet. estimate the next time lookup */ ret = FALSE; } else { /* done, or close enough anyway */ ctx->last_nonblock_timeval = now; ret = TRUE; } guess_cost = ctx->cost * (SEARCH_MAX_NONBLOCK_USECS / (double)usecs); if (usecs < SEARCH_RECALC_MIN_USECS) { /* the estimate may not be very good since we spent so little time doing this search. don't allow huge changes to the guess, but allow anyway large enough so that we can move to right direction. */ if (guess_cost > ctx->next_time_check_cost*3) guess_cost = ctx->next_time_check_cost*3; else if (guess_cost < ctx->next_time_check_cost/3) guess_cost = ctx->next_time_check_cost/3; } if (ret) ctx->cost = 0; ctx->next_time_check_cost = guess_cost; return ret; } static int search_more_with_mail(struct index_search_context *ctx, struct mail *mail) { struct mail_search_context *_ctx = &ctx->mail_ctx; struct mailbox *box = _ctx->transaction->box; struct index_mail *imail = (struct index_mail *)mail; unsigned long long cost1, cost2; int match, ret; if (search_would_block(ctx)) { /* this lookup is useful when a large number of messages match */ return 0; } if (ioloop_time - ctx->last_notify.tv_sec >= SEARCH_NOTIFY_INTERVAL_SECS) index_storage_search_notify(box, ctx); mail_search_args_reset(_ctx->args->args, FALSE); cost1 = search_get_cost(mail->transaction); ret = -1; while (box->v.search_next_update_seq(_ctx)) { mail_set_seq(mail, _ctx->seq); ctx->cur_mail = mail; /* mail's access_type is SEARCH only while using it to process the search query. afterwards the mail can still be accessed for fetching. */ ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_SEARCH; T_BEGIN { match = search_match_next(ctx); } T_END; ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_DEFAULT; ctx->cur_mail = NULL; i_assert(imail->data.search_results == NULL); if (match < 0) { /* result isn't known yet, do a prefetch and finish later */ imail->data.search_results = buffer_create_dynamic(imail->mail.data_pool, 64); mail_search_args_result_serialize(_ctx->args, imail->data.search_results); } mail_search_args_reset(_ctx->args->args, FALSE); if (match != 0) { /* either matched or result is still unknown. anyway we're far enough now that we probably want to update the access_parts. the only problem here is if searching would want fewer access_parts than the fetching part, but that's probably not a big problem usually. */ index_mail_update_access_parts_pre(mail); ret = 1; break; } /* non-match */ if (_ctx->args->stop_on_nonmatch) { ret = -1; break; } cost2 = search_get_cost(mail->transaction); ctx->cost += cost2 - cost1; cost1 = cost2; if (search_would_block(ctx)) { ret = 0; break; } } cost2 = search_get_cost(mail->transaction); ctx->cost += cost2 - cost1; return ret; } struct mail *index_search_get_mail(struct index_search_context *ctx) { struct index_mail *imail; struct mail *const *mails, *mail; unsigned int count; if (ctx->unused_mail_idx == ctx->max_mails) return NULL; mails = array_get(&ctx->mails, &count); if (ctx->unused_mail_idx < count) return mails[ctx->unused_mail_idx]; mail = mail_alloc(ctx->mail_ctx.transaction, ctx->mail_ctx.wanted_fields, ctx->mail_ctx.wanted_headers); imail = (struct index_mail *)mail; imail->search_mail = TRUE; ctx->mail_ctx.transaction->stats_track = TRUE; array_append(&ctx->mails, &mail, 1); return mail; } static int search_more_with_prefetching(struct index_search_context *ctx, struct mail **mail_r) { struct mail *mail, *const *mails; unsigned int count; int ret = 0; while ((mail = index_search_get_mail(ctx)) != NULL) { T_BEGIN { ret = search_more_with_mail(ctx, mail); } T_END; if (ret <= 0) break; if (ctx->mail_ctx.sort_program != NULL) { /* don't prefetch when using a sort program, since the mails' access order will change */ i_assert(ctx->unused_mail_idx == 0); *mail_r = mail; return 1; } if (mail_prefetch(mail) && ctx->unused_mail_idx == 0) { /* no prefetching done, return it immediately */ *mail_r = mail; return 1; } ctx->unused_mail_idx++; } if (mail != NULL) { if (ret == 0) { /* wait */ return 0; } i_assert(ret < 0); if (ctx->unused_mail_idx == 0) { /* finished */ return -1; } } else { /* prefetch buffer is full. */ } /* return the next message */ i_assert(ctx->unused_mail_idx > 0); mails = array_get(&ctx->mails, &count); *mail_r = mails[0]; if (--ctx->unused_mail_idx > 0) { array_delete(&ctx->mails, 0, 1); array_append(&ctx->mails, mail_r, 1); } index_mail_update_access_parts_post(*mail_r); return 1; } static bool search_finish_prefetch(struct index_search_context *ctx, struct index_mail *imail) { int ret; i_assert(imail->mail.mail.lookup_abort == MAIL_LOOKUP_ABORT_NEVER); ctx->cur_mail = &imail->mail.mail; ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_SEARCH; mail_search_args_result_deserialize(ctx->mail_ctx.args, imail->data.search_results->data, imail->data.search_results->used); T_BEGIN { ret = search_match_once(ctx); search_match_finish(ctx, ret); } T_END; ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_DEFAULT; ctx->cur_mail = NULL; return ret > 0; } static int search_more(struct index_search_context *ctx, struct mail **mail_r) { struct index_mail *imail; int ret; while ((ret = search_more_with_prefetching(ctx, mail_r)) > 0) { imail = (struct index_mail *)*mail_r; if (imail->data.search_results == NULL) break; /* prefetch running - searching wasn't finished yet */ if (search_finish_prefetch(ctx, imail)) break; /* search finished as non-match */ if (ctx->mail_ctx.args->stop_on_nonmatch) { ret = -1; break; } } return ret; } bool index_storage_search_next_nonblock(struct mail_search_context *_ctx, struct mail **mail_r, bool *tryagain_r) { struct index_search_context *ctx = (struct index_search_context *)_ctx; struct mail *mail, *const *mailp; uint32_t seq; int ret; *tryagain_r = FALSE; if (_ctx->sort_program == NULL) { ret = search_more(ctx, &mail); if (ret == 0) { *tryagain_r = TRUE; return FALSE; } if (ret < 0) return FALSE; *mail_r = mail; return TRUE; } if (!ctx->sorted) { while ((ret = search_more(ctx, &mail)) > 0) index_sort_list_add(_ctx->sort_program, mail); if (ret == 0) { *tryagain_r = TRUE; return FALSE; } /* finished searching the messages. now sort them and start returning the messages. */ ctx->sorted = TRUE; index_sort_list_finish(_ctx->sort_program); } /* everything searched at this point already. just returning matches from sort list. FIXME: we could do prefetching here also. */ if (!index_sort_list_next(_ctx->sort_program, &seq)) return FALSE; mailp = array_idx(&ctx->mails, 0); mail_set_seq(*mailp, seq); index_mail_update_access_parts_pre(*mailp); index_mail_update_access_parts_post(*mailp); *mail_r = *mailp; return TRUE; } bool index_storage_search_next_update_seq(struct mail_search_context *_ctx) { struct index_search_context *ctx = (struct index_search_context *)_ctx; uint32_t uid; int ret; if (_ctx->seq == 0) { /* first time */ _ctx->seq = ctx->seq1; } else { _ctx->seq++; } if (!ctx->have_seqsets && !ctx->have_index_args && _ctx->update_result == NULL) { _ctx->progress_cur = _ctx->seq; return _ctx->seq <= ctx->seq2; } ret = 0; while (_ctx->seq <= ctx->seq2) { /* check if the sequence matches */ ret = mail_search_args_foreach(ctx->mail_ctx.args->args, search_seqset_arg, ctx); if (ret != 0 && ctx->have_index_args) { /* check if flags/keywords match before anything else is done. mail_set_seq() can be a bit slow. */ ret = mail_search_args_foreach(ctx->mail_ctx.args->args, search_index_arg, ctx); } if (ret != 0 && _ctx->update_result != NULL) { /* see if this message never matches */ mail_index_lookup_uid(ctx->view, _ctx->seq, &uid); if (seq_range_exists(&_ctx->update_result->never_uids, uid)) ret = 0; } if (ret != 0) break; /* doesn't, try next one */ _ctx->seq++; mail_search_args_reset(ctx->mail_ctx.args->args, FALSE); } if (ret != 0 && _ctx->update_result != NULL) { mail_index_lookup_uid(ctx->view, _ctx->seq, &uid); if (seq_range_exists(&_ctx->update_result->uids, uid)) { /* we already know that the static data matches. mark it as such. */ search_set_static_matches(_ctx->args->args); } } ctx->mail_ctx.progress_cur = _ctx->seq; return ret != 0; } dovecot-2.2.33.2/src/lib-storage/index/index-storage.h0000644000175000017500000001733613165463624017401 00000000000000#ifndef INDEX_STORAGE_H #define INDEX_STORAGE_H #include "file-dotlock.h" #include "mail-storage-private.h" #include "mail-index-private.h" #include "mailbox-recent-flags.h" /* FIXME: remove in v2.3 */ #include "mailbox-watch.h" #define MAILBOX_FULL_SYNC_INTERVAL 5 enum mailbox_lock_notify_type { MAILBOX_LOCK_NOTIFY_NONE, /* Mailbox is locked, will abort in secs_left */ MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, /* Mailbox lock looks stale, will override in secs_left */ MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE }; enum index_storage_list_change { INDEX_STORAGE_LIST_CHANGE_ERROR = -1, INDEX_STORAGE_LIST_CHANGE_NONE = 0, INDEX_STORAGE_LIST_CHANGE_INMEMORY, INDEX_STORAGE_LIST_CHANGE_NORECORD, INDEX_STORAGE_LIST_CHANGE_NOT_IN_FS, INDEX_STORAGE_LIST_CHANGE_SIZE_CHANGED, INDEX_STORAGE_LIST_CHANGE_MTIME_CHANGED }; struct index_mailbox_context { union mailbox_module_context module_ctx; enum mail_index_open_flags index_flags; time_t next_lock_notify; /* temporary */ enum mailbox_lock_notify_type last_notify_type; const ARRAY_TYPE(keywords) *keyword_names; struct mail_cache_field *cache_fields; struct mailbox_vsize_update *vsize_update; uint32_t recent_flags_prev_first_recent_uid; uint32_t recent_flags_last_check_nextuid; time_t sync_last_check; uint32_t list_index_sync_ext_id; }; #define INDEX_STORAGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, index_storage_module) extern MODULE_CONTEXT_DEFINE(index_storage_module, &mail_storage_module_register); void index_storage_lock_notify(struct mailbox *box, enum mailbox_lock_notify_type notify_type, unsigned int secs_left); void index_storage_lock_notify_reset(struct mailbox *box); int index_storage_mailbox_alloc_index(struct mailbox *box); void index_storage_mailbox_alloc(struct mailbox *box, const char *vname, enum mailbox_flags flags, const char *index_prefix); int index_storage_mailbox_exists(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r); int index_storage_mailbox_exists_full(struct mailbox *box, const char *subdir, enum mailbox_existence *existence_r) ATTR_NULL(2); int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory); int index_storage_mailbox_enable(struct mailbox *box, enum mailbox_feature feature); void index_storage_mailbox_close(struct mailbox *box); void index_storage_mailbox_free(struct mailbox *box); int index_storage_mailbox_update(struct mailbox *box, const struct mailbox_update *update); int index_storage_mailbox_update_common(struct mailbox *box, const struct mailbox_update *update); int index_storage_mailbox_create(struct mailbox *box, bool directory); int index_storage_mailbox_delete_pre(struct mailbox *box); int index_storage_mailbox_delete_post(struct mailbox *box); int index_storage_mailbox_delete(struct mailbox *box); int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted); int index_storage_mailbox_rename(struct mailbox *src, struct mailbox *dest); int index_mailbox_update_last_temp_file_scan(struct mailbox *box); int index_mailbox_fix_inconsistent_existence(struct mailbox *box, const char *path); bool index_storage_is_readonly(struct mailbox *box); bool index_storage_is_inconsistent(struct mailbox *box); /* FIXME: for backwards compatibility - remove in v2.3 */ #define index_mailbox_set_recent_seq(box, view, seq1, seq2) \ mailbox_recent_flags_set_seqs(box, view, seq1, seq2) #define index_mailbox_check_add(box, path) mailbox_watch_add(box, path) #define index_mailbox_check_remove_all(box) mailbox_watch_remove_all(box) enum mail_index_sync_flags index_storage_get_sync_flags(struct mailbox *box); bool index_mailbox_want_full_sync(struct mailbox *box, enum mailbox_sync_flags flags); struct mailbox_sync_context * index_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags, bool failed); bool index_mailbox_sync_next(struct mailbox_sync_context *ctx, struct mailbox_sync_rec *sync_rec_r); int index_mailbox_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r); int index_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags); enum mailbox_sync_type index_sync_type_convert(enum mail_index_sync_type type); void index_sync_update_recent_count(struct mailbox *box); int index_storage_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); void index_storage_get_open_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); int index_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r); int index_mailbox_get_virtual_size(struct mailbox *box, struct mailbox_metadata *metadata_r); int index_mailbox_get_physical_size(struct mailbox *box, struct mailbox_metadata *metadata_r); int index_storage_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value); int index_storage_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r); struct mailbox_attribute_iter * index_storage_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix); const char * index_storage_attribute_iter_next(struct mailbox_attribute_iter *iter); int index_storage_attribute_iter_deinit(struct mailbox_attribute_iter *iter); struct mail_search_context * index_storage_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); int index_storage_search_deinit(struct mail_search_context *ctx); bool index_storage_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r); bool index_storage_search_next_update_seq(struct mail_search_context *ctx); struct mailbox_transaction_context * index_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags); void index_transaction_init(struct mailbox_transaction_context *t, struct mailbox *box, enum mailbox_transaction_flags flags); void index_transaction_init_pvt(struct mailbox_transaction_context *t); int index_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r); void index_transaction_rollback(struct mailbox_transaction_context *t); void index_save_context_free(struct mail_save_context *ctx); void index_copy_cache_fields(struct mail_save_context *ctx, struct mail *src_mail, uint32_t dest_seq); int index_storage_set_subscribed(struct mailbox *box, bool set); void index_storage_destroy(struct mail_storage *storage); bool index_keyword_array_cmp(const ARRAY_TYPE(keyword_indexes) *k1, const ARRAY_TYPE(keyword_indexes) *k2); int index_storage_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq); enum index_storage_list_change index_storage_list_index_has_changed_full(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq); void index_storage_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq); int index_storage_expunged_sync_begin(struct mailbox *box, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, struct mail_index_transaction **trans_r, enum mail_index_sync_flags flags); void index_storage_expunging_deinit(struct mailbox *box); void index_storage_save_abort_last(struct mail_save_context *ctx, uint32_t seq); #endif dovecot-2.2.33.2/src/lib-storage/index/index-mail-binary.c0000644000175000017500000004165113165463624020131 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "safe-mkstemp.h" #include "istream.h" #include "istream-crlf.h" #include "istream-seekable.h" #include "istream-base64.h" #include "istream-qp.h" #include "istream-header-filter.h" #include "ostream.h" #include "message-binary-part.h" #include "message-parser.h" #include "message-decoder.h" #include "mail-user.h" #include "index-storage.h" #include "index-mail.h" #define MAIL_BINARY_CACHE_EXPIRE_MSECS (60*1000) #define IS_CONVERTED_CTE(cte) \ ((cte) == MESSAGE_CTE_QP || (cte) == MESSAGE_CTE_BASE64) struct binary_block { struct istream *input; uoff_t physical_pos; unsigned int body_lines_count; bool converted, converted_hdr; }; struct binary_ctx { struct mail *mail; struct istream *input; bool has_nuls, converted; /* each block is its own input stream. basically each converted MIME body has its own block and the parts between the MIME bodies are unconverted blocks */ ARRAY(struct binary_block) blocks; uoff_t copy_start_offset; }; static void binary_copy_to(struct binary_ctx *ctx, uoff_t end_offset) { struct binary_block *block; struct istream *linput, *cinput; uoff_t orig_offset, size; i_assert(end_offset >= ctx->copy_start_offset); if (end_offset == ctx->copy_start_offset) return; size = end_offset - ctx->copy_start_offset; orig_offset = ctx->input->v_offset; i_stream_seek(ctx->input, ctx->copy_start_offset); linput = i_stream_create_limit(ctx->input, size); cinput = i_stream_create_crlf(linput); i_stream_unref(&linput); block = array_append_space(&ctx->blocks); block->input = cinput; i_stream_seek(ctx->input, orig_offset); } static void binary_cte_filter_callback(struct header_filter_istream *input, struct message_header_line *hdr, bool *matched ATTR_UNUSED, void *context ATTR_UNUSED) { static const char *cte_binary = "Content-Transfer-Encoding: binary\r\n"; if (hdr != NULL && hdr->eoh) { i_stream_header_filter_add(input, cte_binary, strlen(cte_binary)); } } static int add_binary_part(struct binary_ctx *ctx, const struct message_part *part, bool include_hdr) { static const char *filter_headers[] = { "Content-Transfer-Encoding", }; struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct message_part *child; struct message_size hdr_size; struct istream *linput; struct binary_block *block; enum message_cte cte; uoff_t part_end_offset; int ret; /* first parse the header to find c-t-e. */ i_stream_seek(ctx->input, part->physical_pos); cte = MESSAGE_CTE_78BIT; parser = message_parse_header_init(ctx->input, &hdr_size, 0); while ((ret = message_parse_header_next(parser, &hdr)) > 0) { if (strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0) cte = message_decoder_parse_cte(hdr); } i_assert(ret < 0); if (message_parse_header_has_nuls(parser)) { /* we're not converting NULs to 0x80 when doing a binary fetch, even if they're in the message header. */ ctx->has_nuls = TRUE; } message_parse_header_deinit(&parser); if (ctx->input->stream_errno != 0) { errno = ctx->input->stream_errno; mail_storage_set_critical(ctx->mail->box->storage, "read(%s) failed: %s", i_stream_get_name(ctx->input), i_stream_get_error(ctx->input)); return -1; } if (cte == MESSAGE_CTE_UNKNOWN) { mail_storage_set_error(ctx->mail->box->storage, MAIL_ERROR_CONVERSION, "Unknown Content-Transfer-Encoding."); return -1; } i_stream_seek(ctx->input, part->physical_pos); if (!include_hdr) { /* body only */ } else if (IS_CONVERTED_CTE(cte)) { /* write header with modified content-type */ if (ctx->copy_start_offset != 0) binary_copy_to(ctx, part->physical_pos); block = array_append_space(&ctx->blocks); block->physical_pos = part->physical_pos; block->converted = TRUE; block->converted_hdr = TRUE; linput = i_stream_create_limit(ctx->input, (uoff_t)-1); block->input = i_stream_create_header_filter(linput, HEADER_FILTER_EXCLUDE | HEADER_FILTER_HIDE_BODY, filter_headers, N_ELEMENTS(filter_headers), binary_cte_filter_callback, (void *)NULL); i_stream_unref(&linput); } else { /* copy everything as-is until the end of this header */ binary_copy_to(ctx, part->physical_pos + part->header_size.physical_size); } ctx->copy_start_offset = part->physical_pos + part->header_size.physical_size; part_end_offset = part->physical_pos + part->header_size.physical_size + part->body_size.physical_size; if (part->children != NULL) { /* multipart */ for (child = part->children; child != NULL; child = child->next) { if (add_binary_part(ctx, child, TRUE) < 0) return -1; } binary_copy_to(ctx, part_end_offset); ctx->copy_start_offset = part_end_offset; return 0; } if (part->body_size.physical_size == 0) { /* no body */ ctx->copy_start_offset = part_end_offset; return 0; } /* single part - write decoded data */ block = array_append_space(&ctx->blocks); block->physical_pos = part->physical_pos; i_stream_seek(ctx->input, part->physical_pos + part->header_size.physical_size); linput = i_stream_create_limit(ctx->input, part->body_size.physical_size); switch (cte) { case MESSAGE_CTE_UNKNOWN: i_unreached(); case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: /* no conversion necessary */ if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) ctx->has_nuls = TRUE; block->input = i_stream_create_crlf(linput); break; case MESSAGE_CTE_QP: block->input = i_stream_create_qp_decoder(linput); ctx->converted = block->converted = TRUE; break; case MESSAGE_CTE_BASE64: block->input = i_stream_create_base64_decoder(linput); ctx->converted = block->converted = TRUE; break; } i_stream_unref(&linput); ctx->copy_start_offset = part_end_offset; return 0; } static int fd_callback(const char **path_r, void *context) { struct mail *_mail = context; string_t *path; int fd; path = t_str_new(256); mail_user_set_get_temp_prefix(path, _mail->box->storage->user->set); fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("Temp file creation to %s failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static void binary_streams_free(struct binary_ctx *ctx) { struct binary_block *block; array_foreach_modifiable(&ctx->blocks, block) i_stream_unref(&block->input); } static void binary_parts_update(struct binary_ctx *ctx, const struct message_part *part, struct message_binary_part **msg_bin_parts) { struct index_mail *mail = (struct index_mail *)ctx->mail; struct binary_block *blocks; struct message_binary_part bin_part; unsigned int i, count; uoff_t size; bool found; blocks = array_get_modifiable(&ctx->blocks, &count); for (; part != NULL; part = part->next) { binary_parts_update(ctx, part->children, msg_bin_parts); i_zero(&bin_part); /* default to unchanged header */ bin_part.binary_hdr_size = part->header_size.virtual_size; bin_part.physical_pos = part->physical_pos; found = FALSE; for (i = 0; i < count; i++) { if (blocks[i].physical_pos != part->physical_pos || !blocks[i].converted) continue; size = blocks[i].input->v_offset; if (blocks[i].converted_hdr) bin_part.binary_hdr_size = size; else bin_part.binary_body_size = size; found = TRUE; } if (found) { bin_part.next = *msg_bin_parts; *msg_bin_parts = p_new(mail->mail.data_pool, struct message_binary_part, 1); **msg_bin_parts = bin_part; } } } static void binary_parts_cache(struct binary_ctx *ctx) { struct index_mail *mail = (struct index_mail *)ctx->mail; buffer_t *buf; buf = buffer_create_dynamic(pool_datastack_create(), 128); message_binary_part_serialize(mail->data.bin_parts, buf); index_mail_cache_add(mail, MAIL_CACHE_BINARY_PARTS, buf->data, buf->used); } static struct istream **blocks_get_streams(struct binary_ctx *ctx) { struct istream **streams; const struct binary_block *blocks; unsigned int i, count; blocks = array_get(&ctx->blocks, &count); streams = t_new(struct istream *, count+1); for (i = 0; i < count; i++) { streams[i] = blocks[i].input; i_assert(streams[i]->v_offset == 0); } return streams; } static int blocks_count_lines(struct binary_ctx *ctx, struct istream *full_input) { struct binary_block *blocks, *cur_block; unsigned int block_idx, block_count; uoff_t cur_block_offset, cur_block_size; const unsigned char *data, *p; size_t size, skip; ssize_t ret; blocks = array_get_modifiable(&ctx->blocks, &block_count); cur_block = blocks; cur_block_offset = 0; block_idx = 0; /* count the number of lines each block contains */ while ((ret = i_stream_read_data(full_input, &data, &size, 0)) > 0) { i_assert(cur_block_offset <= cur_block->input->v_offset); if (cur_block->input->eof) { /* this is the last input for this block. the input may also contain the next block's data, which we don't want to include in this block's line count. */ cur_block_size = cur_block->input->v_offset + i_stream_get_data_size(cur_block->input); i_assert(size >= cur_block_size - cur_block_offset); size = cur_block_size - cur_block_offset; } skip = size; while ((p = memchr(data, '\n', size)) != NULL) { size -= p-data+1; data = p+1; cur_block->body_lines_count++; } i_stream_skip(full_input, skip); cur_block_offset += skip; if (cur_block->input->eof) { /* go to the next block */ if (++block_idx == block_count) { i_assert(i_stream_is_eof(full_input)); ret = -1; break; } cur_block++; cur_block_offset = 0; } } i_assert(ret == -1); if (full_input->stream_errno != 0) return -1; i_assert(block_count == 0 || !i_stream_have_bytes_left(cur_block->input)); i_assert(block_count == 0 || block_idx+1 == block_count); return 0; } static int index_mail_read_binary_to_cache(struct mail *_mail, const struct message_part *part, bool include_hdr, const char *reason, bool *binary_r, bool *converted_r) { struct index_mail *mail = (struct index_mail *)_mail; struct mail_binary_cache *cache = &_mail->box->storage->binary_cache; struct binary_ctx ctx; struct istream *is; i_zero(&ctx); ctx.mail = _mail; t_array_init(&ctx.blocks, 8); mail_storage_free_binary_cache(_mail->box->storage); if (mail_get_stream_because(_mail, NULL, NULL, reason, &ctx.input) < 0) return -1; if (add_binary_part(&ctx, part, include_hdr) < 0) { binary_streams_free(&ctx); return -1; } if (array_count(&ctx.blocks) != 0) { is = i_streams_merge(blocks_get_streams(&ctx), IO_BLOCK_SIZE, fd_callback, _mail); } else { is = i_stream_create_from_data("", 0); } i_stream_set_name(is, t_strdup_printf( "", _mail->box->vname, _mail->uid)); if (blocks_count_lines(&ctx, is) < 0) { if (is->stream_errno == EINVAL) { /* MIME part contains invalid data */ mail_storage_set_error(_mail->box->storage, MAIL_ERROR_INVALIDDATA, "Invalid data in MIME part"); } else { mail_storage_set_critical(_mail->box->storage, "read(%s) failed: %s", i_stream_get_name(is), i_stream_get_error(is)); } i_stream_unref(&is); binary_streams_free(&ctx); return -1; } if (_mail->uid > 0) { cache->to = timeout_add(MAIL_BINARY_CACHE_EXPIRE_MSECS, mail_storage_free_binary_cache, _mail->box->storage); cache->box = _mail->box; cache->uid = _mail->uid; cache->orig_physical_pos = part->physical_pos; cache->include_hdr = include_hdr; cache->input = is; } i_assert(!i_stream_have_bytes_left(is)); cache->size = is->v_offset; i_stream_seek(is, 0); if (part->parent == NULL && include_hdr && mail->data.bin_parts == NULL) { binary_parts_update(&ctx, part, &mail->data.bin_parts); if (_mail->uid > 0) binary_parts_cache(&ctx); } binary_streams_free(&ctx); *binary_r = ctx.converted ? TRUE : ctx.has_nuls; *converted_r = ctx.converted; return 0; } static bool get_cached_binary_parts(struct index_mail *mail) { const unsigned int field_idx = mail->ibox->cache_fields[MAIL_CACHE_BINARY_PARTS].idx; buffer_t *part_buf; int ret; if (mail->data.bin_parts != NULL) return TRUE; part_buf = buffer_create_dynamic(pool_datastack_create(), 128); ret = index_mail_cache_lookup_field(mail, part_buf, field_idx); if (ret <= 0) return FALSE; if (message_binary_part_deserialize(mail->mail.data_pool, part_buf->data, part_buf->used, &mail->data.bin_parts) < 0) { mail_set_mail_cache_corrupted(&mail->mail.mail, "Corrupted cached binary.parts data"); return FALSE; } return TRUE; } static struct message_part * msg_part_find(struct message_part *parts, uoff_t physical_pos) { struct message_part *part, *child; for (part = parts; part != NULL; part = part->next) { if (part->physical_pos == physical_pos) return part; child = msg_part_find(part->children, physical_pos); if (child != NULL) return child; } return NULL; } static int index_mail_get_binary_size(struct mail *_mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *lines_r) { struct index_mail *mail = (struct index_mail *)_mail; struct message_part *all_parts, *msg_part; const struct message_binary_part *bin_part, *root_bin_part; uoff_t size, end_offset; unsigned int lines; bool binary, converted; if (mail_get_parts(_mail, &all_parts) < 0) return -1; /* first lookup from cache */ if (!get_cached_binary_parts(mail)) { /* not found. parse the whole message */ if (index_mail_read_binary_to_cache(_mail, all_parts, TRUE, "binary.size", &binary, &converted) < 0) return -1; } size = part->header_size.virtual_size + part->body_size.virtual_size; /* note that we assume here that binary translation doesn't change the headers' line counts. this isn't true if the original message contained duplicate Content-Transfer-Encoding lines, but since that's invalid anyway we don't bother trying to handle it. */ lines = part->header_size.lines + part->body_size.lines; end_offset = part->physical_pos + size; bin_part = mail->data.bin_parts; root_bin_part = NULL; for (; bin_part != NULL; bin_part = bin_part->next) { msg_part = msg_part_find(all_parts, bin_part->physical_pos); if (msg_part == NULL) { /* either binary.parts or mime.parts is broken */ mail_set_cache_corrupted_reason(_mail, MAIL_FETCH_MESSAGE_PARTS, t_strdup_printf( "BINARY part at offset %"PRIuUOFF_T" not found from MIME parts", bin_part->physical_pos)); return -1; } if (msg_part->physical_pos >= part->physical_pos && msg_part->physical_pos < end_offset) { if (msg_part->physical_pos == part->physical_pos) root_bin_part = bin_part; size -= msg_part->header_size.virtual_size + msg_part->body_size.virtual_size; size += bin_part->binary_hdr_size + bin_part->binary_body_size; lines -= msg_part->body_size.lines; lines += bin_part->binary_body_lines_count; } } if (!include_hdr) { if (root_bin_part != NULL) size -= root_bin_part->binary_hdr_size; else size -= part->header_size.virtual_size; lines -= part->header_size.lines; } *size_r = size; *lines_r = lines; return 0; } int index_mail_get_binary_stream(struct mail *_mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *lines_r, bool *binary_r, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; struct mail_binary_cache *cache = &_mail->box->storage->binary_cache; struct istream *input; bool binary, converted; if (stream_r == NULL) { return index_mail_get_binary_size(_mail, part, include_hdr, size_r, lines_r); } /* current implementation doesn't bother implementing this, because it's not needed by anything. */ i_assert(lines_r == NULL); /* FIXME: always put the header to temp file. skip it when needed. */ if (cache->box == _mail->box && cache->uid == _mail->uid && cache->orig_physical_pos == part->physical_pos && cache->include_hdr == include_hdr) { /* we have this cached already */ i_stream_seek(cache->input, 0); timeout_reset(cache->to); binary = TRUE; converted = TRUE; } else { if (index_mail_read_binary_to_cache(_mail, part, include_hdr, "binary stream", &binary, &converted) < 0) return -1; mail->data.cache_fetch_fields |= MAIL_FETCH_STREAM_BINARY; } *size_r = cache->size; *binary_r = binary; if (!converted) { /* don't keep this cached. it's exactly the same as the original stream */ i_assert(mail->data.stream != NULL); i_stream_seek(mail->data.stream, part->physical_pos + (include_hdr ? 0 : part->header_size.physical_size)); input = i_stream_create_crlf(mail->data.stream); *stream_r = i_stream_create_limit(input, *size_r); i_stream_unref(&input); mail_storage_free_binary_cache(_mail->box->storage); } else { *stream_r = cache->input; i_stream_ref(cache->input); } return 0; } dovecot-2.2.33.2/src/lib-storage/index/index-sync-pvt.c0000644000175000017500000002277613165463624017517 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mailbox-list-private.h" #include "index-sync-private.h" struct index_mailbox_sync_pvt_context { struct mailbox *box; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view_pvt; struct mail_index_transaction *trans_pvt; struct mail_index_view *view_shared; }; static int sync_pvt_expunges(struct index_mailbox_sync_pvt_context *ctx) { uint32_t seq_shared, seq_pvt, count_shared, count_pvt; uint32_t uid_shared, uid_pvt; count_shared = mail_index_view_get_messages_count(ctx->view_shared); count_pvt = mail_index_view_get_messages_count(ctx->view_pvt); seq_shared = seq_pvt = 1; while (seq_pvt <= count_pvt && seq_shared <= count_shared) { mail_index_lookup_uid(ctx->view_pvt, seq_pvt, &uid_pvt); mail_index_lookup_uid(ctx->view_shared, seq_shared, &uid_shared); if (uid_pvt == uid_shared) { seq_pvt++; seq_shared++; } else if (uid_pvt < uid_shared) { /* message expunged */ mail_index_expunge(ctx->trans_pvt, seq_pvt); seq_pvt++; } else { mail_storage_set_critical(ctx->box->storage, "%s: Message UID=%u unexpectedly inserted to mailbox", ctx->box->index_pvt->filepath, uid_shared); return -1; } } return 0; } static void sync_pvt_copy_self_flags(struct index_mailbox_sync_pvt_context *ctx, ARRAY_TYPE(keyword_indexes) *keywords, uint32_t seq_old, uint32_t seq_new) { const struct mail_index_record *old_rec; old_rec = mail_index_lookup(ctx->view_pvt, seq_old); mail_index_lookup_keywords(ctx->view_pvt, seq_old, keywords); if (old_rec->flags != 0) { mail_index_update_flags(ctx->trans_pvt, seq_new, MODIFY_ADD, old_rec->flags); } if (array_count(keywords) > 0) { struct mail_keywords *kw; kw = mail_index_keywords_create_from_indexes(ctx->box->index_pvt, keywords); mail_index_update_keywords(ctx->trans_pvt, seq_new, MODIFY_ADD, kw); mail_index_keywords_unref(&kw); } } static void sync_pvt_copy_shared_flags(struct index_mailbox_sync_pvt_context *ctx, uint32_t seq_shared, uint32_t seq_pvt) { const struct mail_index_record *rec; rec = mail_index_lookup(ctx->view_shared, seq_shared); mail_index_update_flags(ctx->trans_pvt, seq_pvt, MODIFY_ADD, rec->flags & mailbox_get_private_flags_mask(ctx->box)); } static int index_mailbox_sync_view_refresh(struct index_mailbox_sync_pvt_context *ctx) { /* open a view for the latest version of the index */ if (mail_index_refresh(ctx->box->index_pvt) < 0 || mail_index_refresh(ctx->box->index) < 0) { mailbox_set_index_error(ctx->box); return -1; } if (ctx->view_shared != NULL) mail_index_view_close(&ctx->view_shared); ctx->view_shared = mail_index_view_open(ctx->box->index); return 0; } static int index_mailbox_sync_open(struct index_mailbox_sync_pvt_context *ctx, bool force) { const struct mail_index_header *hdr_shared, *hdr_pvt; if (index_mailbox_sync_view_refresh(ctx) < 0) return -1; hdr_shared = mail_index_get_header(ctx->view_shared); if (hdr_shared->uid_validity == 0 && !force) { /* the mailbox hasn't been fully created yet, no need for a private index yet */ return 0; } hdr_pvt = mail_index_get_header(ctx->box->view_pvt); if (hdr_pvt->next_uid == hdr_shared->next_uid && hdr_pvt->messages_count == hdr_shared->messages_count && !force) { /* no new or expunged mails, don't bother syncing */ return 0; } if (mail_index_sync_begin(ctx->box->index_pvt, &ctx->sync_ctx, &ctx->view_pvt, &ctx->trans_pvt, 0) < 0) { mailbox_set_index_error(ctx->box); return -1; } /* refresh once more now that we're locked */ if (index_mailbox_sync_view_refresh(ctx) < 0) return -1; return 1; } int index_mailbox_sync_pvt_init(struct mailbox *box, bool lock, struct index_mailbox_sync_pvt_context **ctx_r) { struct index_mailbox_sync_pvt_context *ctx; int ret; *ctx_r = NULL; if ((ret = mailbox_open_index_pvt(box)) <= 0) return ret; ctx = i_new(struct index_mailbox_sync_pvt_context, 1); ctx->box = box; if (lock) { if (index_mailbox_sync_open(ctx, TRUE) < 0) { index_mailbox_sync_pvt_deinit(&ctx); return -1; } } *ctx_r = ctx; return 1; } static int index_mailbox_sync_pvt_index(struct index_mailbox_sync_pvt_context *ctx, const struct mail_save_private_changes *pvt_changes, unsigned int pvt_changes_count) { const struct mail_index_header *hdr_shared, *hdr_pvt; ARRAY_TYPE(keyword_indexes) keywords; uint32_t seq_shared, seq_pvt, seq_old_pvt, seq2, count_shared, uid; unsigned int pc_idx = 0; bool reset = FALSE, preserve_old_flags = FALSE, copy_shared_flags; int ret; if (ctx->sync_ctx == NULL) { if ((ret = index_mailbox_sync_open(ctx, FALSE)) <= 0) return ret; } hdr_pvt = mail_index_get_header(ctx->view_pvt); hdr_shared = mail_index_get_header(ctx->view_shared); if (hdr_shared->uid_validity == hdr_pvt->uid_validity) { /* same mailbox. expunge messages from private index that no longer exist. */ if (sync_pvt_expunges(ctx) < 0) { reset = TRUE; preserve_old_flags = TRUE; t_array_init(&keywords, 32); } } else if (hdr_pvt->uid_validity == 0 || hdr_pvt->uid_validity != 0) { /* mailbox created/recreated */ reset = TRUE; } /* for public namespaces copy the initial private flags from the shared index. this allows Sieve scripts to set the initial flags. */ copy_shared_flags = ctx->box->list->ns->type == MAIL_NAMESPACE_TYPE_PUBLIC; count_shared = mail_index_view_get_messages_count(ctx->view_shared); if (!reset) { if (!mail_index_lookup_seq_range(ctx->view_shared, hdr_pvt->next_uid, hdr_shared->next_uid, &seq_shared, &seq2)) { /* no new messages */ seq_shared = count_shared+1; } } else { mail_index_reset(ctx->trans_pvt); mail_index_update_header(ctx->trans_pvt, offsetof(struct mail_index_header, uid_validity), &hdr_shared->uid_validity, sizeof(hdr_shared->uid_validity), TRUE); seq_shared = 1; } uid = 0; for (; seq_shared <= count_shared; seq_shared++) { mail_index_lookup_uid(ctx->view_shared, seq_shared, &uid); mail_index_append(ctx->trans_pvt, uid, &seq_pvt); if (preserve_old_flags && mail_index_lookup_seq(ctx->view_pvt, uid, &seq_old_pvt)) { /* copy flags from the original private index */ sync_pvt_copy_self_flags(ctx, &keywords, seq_old_pvt, seq_pvt); } else if (copy_shared_flags) { sync_pvt_copy_shared_flags(ctx, seq_shared, seq_pvt); } /* add private flags for the recently saved/copied messages */ while (pc_idx < pvt_changes_count && pvt_changes[pc_idx].mailnum <= uid) { if (pvt_changes[pc_idx].mailnum == uid) { mail_index_update_flags(ctx->trans_pvt, seq_pvt, MODIFY_ADD, pvt_changes[pc_idx].flags); } pc_idx++; } } if (uid < hdr_shared->next_uid) { mail_index_update_header(ctx->trans_pvt, offsetof(struct mail_index_header, next_uid), &hdr_shared->next_uid, sizeof(hdr_shared->next_uid), FALSE); } if ((ret = mail_index_sync_commit(&ctx->sync_ctx)) < 0) mailbox_set_index_error(ctx->box); return ret; } static int mail_save_private_changes_mailnum_cmp(const struct mail_save_private_changes *c1, const struct mail_save_private_changes *c2) { if (c1->mailnum < c2->mailnum) return -1; if (c1->mailnum > c2->mailnum) return 1; return 0; } int index_mailbox_sync_pvt_newmails(struct index_mailbox_sync_pvt_context *ctx, struct mailbox_transaction_context *trans) { struct mail_save_private_changes *pvt_changes; struct seq_range_iter iter; unsigned int i, n, pvt_count; uint32_t uid; if (index_mailbox_sync_view_refresh(ctx) < 0) return -1; /* translate mail numbers to UIDs */ pvt_changes = array_get_modifiable(&trans->pvt_saves, &pvt_count); n = i = 0; seq_range_array_iter_init(&iter, &trans->changes->saved_uids); while (seq_range_array_iter_nth(&iter, n, &uid)) { if (pvt_changes[i].mailnum == n) { pvt_changes[i].mailnum = uid; i++; } n++; } /* sort the changes by UID */ array_sort(&trans->pvt_saves, mail_save_private_changes_mailnum_cmp); /* add new mails to the private index with the private flags */ return index_mailbox_sync_pvt_index(ctx, pvt_changes, pvt_count); } int index_mailbox_sync_pvt_view(struct index_mailbox_sync_pvt_context *ctx, ARRAY_TYPE(seq_range) *flag_updates, ARRAY_TYPE(seq_range) *hidden_updates) { struct mail_index_view_sync_ctx *view_sync_ctx; struct mail_index_view_sync_rec sync_rec; uint32_t seq1, seq2; bool delayed_expunges; /* sync private index against shared index by adding/removing mails */ if (index_mailbox_sync_pvt_index(ctx, NULL, 0) < 0) return -1; /* sync the private view */ view_sync_ctx = mail_index_view_sync_begin(ctx->box->view_pvt, 0); while (mail_index_view_sync_next(view_sync_ctx, &sync_rec)) { if (sync_rec.type != MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS) continue; /* *_updates contains ctx->box->view sequences (not view_pvt sequences) */ if (mail_index_lookup_seq_range(ctx->box->view, sync_rec.uid1, sync_rec.uid2, &seq1, &seq2)) { if (!sync_rec.hidden) { seq_range_array_add_range(flag_updates, seq1, seq2); } else { seq_range_array_add_range(hidden_updates, seq1, seq2); } } } if (mail_index_view_sync_commit(&view_sync_ctx, &delayed_expunges) < 0) return -1; return 0; } void index_mailbox_sync_pvt_deinit(struct index_mailbox_sync_pvt_context **_ctx) { struct index_mailbox_sync_pvt_context *ctx = *_ctx; *_ctx = NULL; if (ctx->sync_ctx != NULL) mail_index_sync_rollback(&ctx->sync_ctx); if (ctx->view_shared != NULL) mail_index_view_close(&ctx->view_shared); i_free(ctx); } dovecot-2.2.33.2/src/lib-storage/index/index-search-result.h0000644000175000017500000000063713123174404020477 00000000000000#ifndef INDEX_SEARCH_RESULT_H #define INDEX_SEARCH_RESULT_H int index_search_result_update_flags(struct mail_search_result *result, const ARRAY_TYPE(seq_range) *uids); int index_search_result_update_appends(struct mail_search_result *result, unsigned int old_messages_count); void index_search_results_update_expunges(struct mailbox *box, const ARRAY_TYPE(seq_range) *expunges); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/0002755000175000017500000000000013172375611016607 500000000000000dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-map.h0000644000175000017500000001372413123174404020562 00000000000000#ifndef MDBOX_MAP_H #define MDBOX_MAP_H #include "seq-range-array.h" struct dbox_file_append_context; struct mdbox_map_append_context; struct mdbox_storage; enum mdbox_map_append_flags { DBOX_MAP_APPEND_FLAG_ALT = 0x01 }; struct mdbox_map_mail_index_header { uint32_t highest_file_id; /* increased every time storage is rebuilt */ uint32_t rebuild_count; }; struct mdbox_map_mail_index_record { uint32_t file_id; uint32_t offset; uint32_t size; /* including pre/post metadata */ }; struct mdbox_map_file_msg { uint32_t map_uid; uint32_t offset; uint32_t refcount; }; ARRAY_DEFINE_TYPE(mdbox_map_file_msg, struct mdbox_map_file_msg); struct mdbox_map * mdbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list); void mdbox_map_deinit(struct mdbox_map **map); /* Open the map. Returns 1 if ok, 0 if map doesn't exist, -1 if error. */ int mdbox_map_open(struct mdbox_map *map); /* Open or create the map. This is done automatically for most operations. Returns 0 if ok, -1 if error. */ int mdbox_map_open_or_create(struct mdbox_map *map); /* Refresh the map. Returns 0 if ok, -1 if error. */ int mdbox_map_refresh(struct mdbox_map *map); /* Returns TRUE if map has been fsck'd. */ bool mdbox_map_is_fscked(struct mdbox_map *map); /* Return the current rebuild counter */ uint32_t mdbox_map_get_rebuild_count(struct mdbox_map *map); /* Look up file_id and offset for given map UID. Returns 1 if ok, 0 if UID is already expunged, -1 if error. */ int mdbox_map_lookup(struct mdbox_map *map, uint32_t map_uid, uint32_t *file_id_r, uoff_t *offset_r); /* Like mdbox_map_lookup(), but look up everything. */ int mdbox_map_lookup_full(struct mdbox_map *map, uint32_t map_uid, struct mdbox_map_mail_index_record *rec_r, uint16_t *refcount_r); /* Like mdbox_map_lookup_full(), but look up with sequence. */ int mdbox_map_lookup_seq_full(struct mdbox_map *map, uint32_t seq, struct mdbox_map_mail_index_record *rec_r, uint16_t *refcount_r); /* Return map UID for the map sequence. */ uint32_t mdbox_map_lookup_uid(struct mdbox_map *map, uint32_t seq); /* Returns the total number of messages in the map. */ unsigned int mdbox_map_get_messages_count(struct mdbox_map *map); /* Get all messages from file */ int mdbox_map_get_file_msgs(struct mdbox_map *map, uint32_t file_id, ARRAY_TYPE(mdbox_map_file_msg) *recs); /* Begin atomic context. There can be multiple transactions/appends within the same atomic context. */ struct mdbox_map_atomic_context *mdbox_map_atomic_begin(struct mdbox_map *map); /* Lock the map immediately. */ int mdbox_map_atomic_lock(struct mdbox_map_atomic_context *atomic, const char *reason); /* Returns TRUE if map is locked */ bool mdbox_map_atomic_is_locked(struct mdbox_map_atomic_context *atomic); /* When finish() is called, rollback the changes. If data was already written to map's transaction log, this desyncs the map and causes a rebuild */ void mdbox_map_atomic_set_failed(struct mdbox_map_atomic_context *atomic); /* Mark this atomic as having succeeded. This is internally done if transaction or append is committed within this atomic, but not when the atomic is used standalone. */ void mdbox_map_atomic_set_success(struct mdbox_map_atomic_context *atomic); /* Remove fsck'd flag. */ void mdbox_map_atomic_unset_fscked(struct mdbox_map_atomic_context *atomic); /* Commit/rollback changes within this atomic context. */ int mdbox_map_atomic_finish(struct mdbox_map_atomic_context **atomic); struct mdbox_map_transaction_context * mdbox_map_transaction_begin(struct mdbox_map_atomic_context *atomic, bool external); /* Write transaction to map and leave it locked. Call _free() to update tail offset and unlock. */ int mdbox_map_transaction_commit(struct mdbox_map_transaction_context *ctx, const char *reason); void mdbox_map_transaction_free(struct mdbox_map_transaction_context **ctx); int mdbox_map_update_refcount(struct mdbox_map_transaction_context *ctx, uint32_t map_uid, int diff); int mdbox_map_update_refcounts(struct mdbox_map_transaction_context *ctx, const ARRAY_TYPE(uint32_t) *map_uids, int diff); int mdbox_map_remove_file_id(struct mdbox_map *map, uint32_t file_id); /* Return all files containing messages with zero refcount. */ int mdbox_map_get_zero_ref_files(struct mdbox_map *map, ARRAY_TYPE(seq_range) *file_ids_r); struct mdbox_map_append_context * mdbox_map_append_begin(struct mdbox_map_atomic_context *atomic); /* Request file for saving a new message with given size (if available). If an existing file can be used, the record is locked and updated in index. Returns 0 if ok, -1 if error. */ int mdbox_map_append_next(struct mdbox_map_append_context *ctx, uoff_t mail_size, enum mdbox_map_append_flags flags, struct dbox_file_append_context **file_append_ctx_r, struct ostream **output_r); /* Finished saving the last mail. Saves the message size. */ void mdbox_map_append_finish(struct mdbox_map_append_context *ctx); /* Abort saving the last mail. */ void mdbox_map_append_abort(struct mdbox_map_append_context *ctx); /* Assign map UIDs to all appended msgs to multi-files. */ int mdbox_map_append_assign_map_uids(struct mdbox_map_append_context *ctx, uint32_t *first_map_uid_r, uint32_t *last_map_uid_r); /* The appends are existing messages that were simply moved to a new file. map_uids contains the moved messages' map UIDs. */ int mdbox_map_append_move(struct mdbox_map_append_context *ctx, const ARRAY_TYPE(uint32_t) *map_uids, const ARRAY_TYPE(seq_range) *expunge_map_uids); /* Flush/fsync appends. */ int mdbox_map_append_flush(struct mdbox_map_append_context *ctx); /* Returns 0 if ok, -1 if error. */ int mdbox_map_append_commit(struct mdbox_map_append_context *ctx); void mdbox_map_append_free(struct mdbox_map_append_context **ctx); /* Returns map's uidvalidity */ uint32_t mdbox_map_get_uid_validity(struct mdbox_map *map); void mdbox_map_set_corrupted(struct mdbox_map *map, const char *format, ...) ATTR_FORMAT(2, 3); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/Makefile.in0000644000175000017500000005702213172375573020607 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/dbox-multi ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_dbox_multi_la_LIBADD = am_libstorage_dbox_multi_la_OBJECTS = mdbox-deleted-storage.lo \ mdbox-file.lo mdbox-mail.lo mdbox-map.lo mdbox-purge.lo \ mdbox-save.lo mdbox-settings.lo mdbox-sync.lo mdbox-storage.lo \ mdbox-storage-rebuild.lo libstorage_dbox_multi_la_OBJECTS = \ $(am_libstorage_dbox_multi_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_dbox_multi_la_SOURCES) DIST_SOURCES = $(libstorage_dbox_multi_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_dbox_multi.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common libstorage_dbox_multi_la_SOURCES = \ mdbox-deleted-storage.c \ mdbox-file.c \ mdbox-mail.c \ mdbox-map.c \ mdbox-purge.c \ mdbox-save.c \ mdbox-settings.c \ mdbox-sync.c \ mdbox-storage.c \ mdbox-storage-rebuild.c headers = \ mdbox-file.h \ mdbox-map.h \ mdbox-map-private.h \ mdbox-settings.h \ mdbox-storage.h \ mdbox-storage-rebuild.h \ mdbox-sync.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/dbox-multi/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/dbox-multi/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_dbox_multi.la: $(libstorage_dbox_multi_la_OBJECTS) $(libstorage_dbox_multi_la_DEPENDENCIES) $(EXTRA_libstorage_dbox_multi_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_dbox_multi_la_OBJECTS) $(libstorage_dbox_multi_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-deleted-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-map.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-purge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-storage-rebuild.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdbox-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-file.c0000644000175000017500000002206013123174404020710 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "hex-dec.h" #include "hex-binary.h" #include "hostpid.h" #include "istream.h" #include "ostream.h" #include "file-lock.h" #include "file-set-size.h" #include "mkdir-parents.h" #include "fdatasync-path.h" #include "eacces-error.h" #include "str.h" #include "mailbox-list-private.h" #include "mdbox-storage.h" #include "mdbox-map-private.h" #include "mdbox-file.h" #include #include #include #include static struct mdbox_file * mdbox_find_and_move_open_file(struct mdbox_storage *storage, uint32_t file_id) { struct mdbox_file *const *files; unsigned int i, count; files = array_get(&storage->open_files, &count); for (i = 0; i < count; i++) { if (files[i]->file_id == file_id) return files[i]; } return NULL; } void mdbox_files_free(struct mdbox_storage *storage) { struct mdbox_file *const *files; unsigned int i, count; files = array_get(&storage->open_files, &count); for (i = 0; i < count; i++) dbox_file_free(&files[i]->file); array_clear(&storage->open_files); } void mdbox_files_sync_input(struct mdbox_storage *storage) { struct mdbox_file *const *files; unsigned int i, count; files = array_get(&storage->open_files, &count); for (i = 0; i < count; i++) { if (files[i]->file.input != NULL) i_stream_sync(files[i]->file.input); } } static void mdbox_close_open_files(struct mdbox_storage *storage, unsigned int close_count) { struct mdbox_file *const *files; unsigned int i, count; files = array_get(&storage->open_files, &count); for (i = 0; i < count;) { if (files[i]->file.refcount == 0) { dbox_file_free(&files[i]->file); array_delete(&storage->open_files, i, 1); if (--close_count == 0) break; files = array_get(&storage->open_files, &count); } else { i++; } } } static void mdbox_file_init_paths(struct mdbox_file *file, const char *fname, bool alt) { i_free(file->file.primary_path); i_free(file->file.alt_path); file->file.primary_path = i_strdup_printf("%s/%s", file->storage->storage_dir, fname); if (file->storage->alt_storage_dir != NULL) { file->file.alt_path = i_strdup_printf("%s/%s", file->storage->alt_storage_dir, fname); } file->file.cur_path = !alt ? file->file.primary_path : file->file.alt_path; } static int mdbox_file_create(struct mdbox_file *file) { struct dbox_file *_file = &file->file; bool create_parents; int ret; create_parents = dbox_file_is_in_alt(_file); _file->fd = _file->storage->v. file_create_fd(_file, _file->cur_path, create_parents); if (_file->fd == -1) return -1; if (file->storage->preallocate_space) { ret = file_preallocate(_file->fd, file->storage->set->mdbox_rotate_size); if (ret < 0) { switch (errno) { case ENOSPC: case EDQUOT: /* ignore */ break; default: i_error("file_preallocate(%s) failed: %m", _file->cur_path); break; } } else if (ret == 0) { /* not supported by filesystem, disable. */ file->storage->preallocate_space = FALSE; } } return 0; } static struct dbox_file * mdbox_file_init_full(struct mdbox_storage *storage, uint32_t file_id, bool alt_dir) { struct mdbox_file *file; const char *fname; unsigned int count; file = file_id == 0 ? NULL : mdbox_find_and_move_open_file(storage, file_id); if (file != NULL) { file->file.refcount++; return &file->file; } count = array_count(&storage->open_files); if (count > MDBOX_MAX_OPEN_UNUSED_FILES) { mdbox_close_open_files(storage, count - MDBOX_MAX_OPEN_UNUSED_FILES); } file = i_new(struct mdbox_file, 1); file->storage = storage; file->file.storage = &storage->storage; file->file_id = file_id; fname = file_id == 0 ? dbox_generate_tmp_filename() : t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id); mdbox_file_init_paths(file, fname, FALSE); dbox_file_init(&file->file); if (alt_dir) file->file.cur_path = file->file.alt_path; if (file_id != 0) array_append(&storage->open_files, &file, 1); else (void)mdbox_file_create(file); return &file->file; } struct dbox_file * mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id) { return mdbox_file_init_full(storage, file_id, FALSE); } struct dbox_file * mdbox_file_init_new_alt(struct mdbox_storage *storage) { return mdbox_file_init_full(storage, 0, TRUE); } int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id) { struct stat st; const char *old_path; const char *new_dir, *new_fname, *new_path; i_assert(file->file_id == 0); i_assert(file_id != 0); old_path = file->file.cur_path; new_fname = t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id); new_dir = !dbox_file_is_in_alt(&file->file) ? file->storage->storage_dir : file->storage->alt_storage_dir; new_path = t_strdup_printf("%s/%s", new_dir, new_fname); if (stat(new_path, &st) == 0) { mail_storage_set_critical(&file->file.storage->storage, "mdbox: %s already exists, rebuilding index", new_path); mdbox_storage_set_corrupted(file->storage); return -1; } if (rename(old_path, new_path) < 0) { mail_storage_set_critical(&file->storage->storage.storage, "rename(%s, %s) failed: %m", old_path, new_path); return -1; } mdbox_file_init_paths(file, new_fname, dbox_file_is_in_alt(&file->file)); file->file_id = file_id; array_append(&file->storage->open_files, &file, 1); return 0; } static struct mdbox_file * mdbox_find_oldest_unused_file(struct mdbox_storage *storage, unsigned int *idx_r) { struct mdbox_file *const *files, *oldest_file = NULL; unsigned int i, count; files = array_get(&storage->open_files, &count); *idx_r = count; for (i = 0; i < count; i++) { if (files[i]->file.refcount == 0) { if (oldest_file == NULL || files[i]->close_time < oldest_file->close_time) { oldest_file = files[i]; *idx_r = i; } } } return oldest_file; } static void mdbox_file_close_timeout(struct mdbox_storage *storage) { struct mdbox_file *oldest; unsigned int i; time_t close_time = ioloop_time - MDBOX_CLOSE_UNUSED_FILES_TIMEOUT_SECS; while ((oldest = mdbox_find_oldest_unused_file(storage, &i)) != NULL) { if (oldest->close_time > close_time) break; array_delete(&storage->open_files, i, 1); dbox_file_free(&oldest->file); } if (oldest == NULL) timeout_remove(&storage->to_close_unused_files); } static void mdbox_file_close_later(struct mdbox_file *mfile) { if (mfile->storage->to_close_unused_files == NULL) { mfile->storage->to_close_unused_files = timeout_add(MDBOX_CLOSE_UNUSED_FILES_TIMEOUT_SECS*1000, mdbox_file_close_timeout, mfile->storage); } } void mdbox_file_unrefed(struct dbox_file *file) { struct mdbox_file *mfile = (struct mdbox_file *)file; struct mdbox_file *oldest_file; unsigned int i, count; /* don't cache metadata seeks while file isn't being referenced */ file->metadata_read_offset = (uoff_t)-1; mfile->close_time = ioloop_time; if (mfile->file_id != 0) { count = array_count(&mfile->storage->open_files); if (count <= MDBOX_MAX_OPEN_UNUSED_FILES) { /* we can leave this file open for now */ mdbox_file_close_later(mfile); return; } /* close the oldest file with refcount=0 */ oldest_file = mdbox_find_oldest_unused_file(mfile->storage, &i); i_assert(oldest_file != NULL); array_delete(&mfile->storage->open_files, i, 1); if (oldest_file != mfile) { dbox_file_free(&oldest_file->file); mdbox_file_close_later(mfile); return; } /* have to close ourself */ } dbox_file_free(file); } int mdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents) { struct mdbox_file *mfile = (struct mdbox_file *)file; struct mdbox_map *map = mfile->storage->map; struct mailbox_permissions perm; mode_t old_mask; const char *p, *dir; int fd; mailbox_list_get_root_permissions(map->root_list, &perm); old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); if (fd == -1 && errno == ENOENT && parents && (p = strrchr(path, '/')) != NULL) { dir = t_strdup_until(path, p); if (mailbox_list_mkdir_root(map->root_list, dir, path != file->alt_path ? MAILBOX_LIST_PATH_TYPE_DIR : MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) { mail_storage_copy_list_error(&file->storage->storage, map->root_list); return -1; } /* try again */ old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); } if (fd == -1) { mail_storage_set_critical(&file->storage->storage, "open(%s, O_CREAT) failed: %m", path); } else if (perm.file_create_gid == (gid_t)-1) { /* no group change */ } else if (fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(&file->storage->storage, "%s", eperm_error_get_chgrp("fchown", path, perm.file_create_gid, perm.file_create_gid_origin)); } else { mail_storage_set_critical(&file->storage->storage, "fchown(%s, -1, %ld) failed: %m", path, (long)perm.file_create_gid); } /* continue anyway */ } return fd; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-map.c0000644000175000017500000011774213165463624020575 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "ostream.h" #include "mkdir-parents.h" #include "unlink-old-files.h" #include "mailbox-list-private.h" #include "mdbox-storage.h" #include "mdbox-file.h" #include "mdbox-map-private.h" #include #define MAX_BACKWARDS_LOOKUPS 10 #define DBOX_FORCE_PURGE_MIN_BYTES (1024*1024*10) #define DBOX_FORCE_PURGE_MIN_RATIO 0.5 #define MAP_STORAGE(map) (&(map)->storage->storage.storage) struct mdbox_map_transaction_context { struct mdbox_map_atomic_context *atomic; struct mail_index_transaction *trans; unsigned int changed:1; unsigned int committed:1; }; static int mdbox_map_generate_uid_validity(struct mdbox_map *map); void mdbox_map_set_corrupted(struct mdbox_map *map, const char *format, ...) { va_list args; va_start(args, format); mail_storage_set_critical(MAP_STORAGE(map), "mdbox map %s corrupted: %s", map->index->filepath, t_strdup_vprintf(format, args)); va_end(args); mdbox_storage_set_corrupted(map->storage); } struct mdbox_map * mdbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list) { struct mdbox_map *map; const char *root, *index_root; root = mailbox_list_get_root_forced(root_list, MAILBOX_LIST_PATH_TYPE_DIR); index_root = mailbox_list_get_root_forced(root_list, MAILBOX_LIST_PATH_TYPE_INDEX); map = i_new(struct mdbox_map, 1); map->storage = storage; map->set = storage->set; map->path = i_strconcat(root, "/"MDBOX_GLOBAL_DIR_NAME, NULL); map->index_path = i_strconcat(index_root, "/"MDBOX_GLOBAL_DIR_NAME, NULL); map->index = mail_index_alloc(map->index_path, MDBOX_GLOBAL_INDEX_PREFIX); mail_index_set_fsync_mode(map->index, MAP_STORAGE(map)->set->parsed_fsync_mode, 0); mail_index_set_lock_method(map->index, MAP_STORAGE(map)->set->parsed_lock_method, mail_storage_get_lock_timeout(MAP_STORAGE(map), UINT_MAX)); map->root_list = root_list; map->map_ext_id = mail_index_ext_register(map->index, "map", sizeof(struct mdbox_map_mail_index_header), sizeof(struct mdbox_map_mail_index_record), sizeof(uint32_t)); map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0, sizeof(uint16_t), sizeof(uint16_t)); return map; } void mdbox_map_deinit(struct mdbox_map **_map) { struct mdbox_map *map = *_map; *_map = NULL; if (map->view != NULL) { mail_index_view_close(&map->view); mail_index_close(map->index); } mail_index_free(&map->index); i_free(map->index_path); i_free(map->path); i_free(map); } static int mdbox_map_mkdir_storage(struct mdbox_map *map) { if (mailbox_list_mkdir_root(map->root_list, map->path, MAILBOX_LIST_PATH_TYPE_DIR) < 0) { mail_storage_copy_list_error(MAP_STORAGE(map), map->root_list); return -1; } if (strcmp(map->path, map->index_path) != 0 && mailbox_list_mkdir_root(map->root_list, map->index_path, MAILBOX_LIST_PATH_TYPE_INDEX) < 0) { mail_storage_copy_list_error(MAP_STORAGE(map), map->root_list); return -1; } return 0; } static void mdbox_map_cleanup(struct mdbox_map *map) { unsigned int interval = MAP_STORAGE(map)->set->mail_temp_scan_interval; struct stat st; if (stat(map->path, &st) < 0) return; /* check once in a while if there are temp files to clean up */ if (interval == 0) { /* disabled */ } else if (st.st_atime > st.st_ctime + DBOX_TMP_DELETE_SECS) { /* there haven't been any changes to this directory since we last checked it. */ } else if (st.st_atime < ioloop_time - (time_t)interval) { /* time to scan */ (void)unlink_old_files(map->path, DBOX_TEMP_FILE_PREFIX, ioloop_time - DBOX_TMP_DELETE_SECS); } } static int mdbox_map_open_internal(struct mdbox_map *map, bool create_missing) { enum mail_index_open_flags open_flags; struct mailbox_permissions perm; int ret = 0; if (map->view != NULL) { /* already opened */ return 1; } mailbox_list_get_root_permissions(map->root_list, &perm); mail_index_set_permissions(map->index, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY | mail_storage_settings_to_index_flags(MAP_STORAGE(map)->set); if (create_missing) { if ((ret = mdbox_map_mkdir_storage(map)) < 0) return -1; if (ret > 0) { /* storage/ directory already existed. the index should exist also. */ } else { open_flags |= MAIL_INDEX_OPEN_FLAG_CREATE; } } ret = mail_index_open(map->index, open_flags); if (ret == 0 && create_missing) { /* storage/ already existed, but indexes didn't. we'll need to take extra steps to make sure we won't overwrite any m.* files that may already exist. */ map->verify_existing_file_ids = TRUE; open_flags |= MAIL_INDEX_OPEN_FLAG_CREATE; ret = mail_index_open(map->index, open_flags); } if (ret < 0) { mail_storage_set_index_error(MAP_STORAGE(map), map->index); return -1; } if (ret == 0) { /* index not found - for now just return failure */ i_assert(!create_missing); return 0; } map->view = mail_index_view_open(map->index); mdbox_map_cleanup(map); if (mail_index_get_header(map->view)->uid_validity == 0) { if (mdbox_map_generate_uid_validity(map) < 0) { mail_storage_set_index_error(MAP_STORAGE(map), map->index); mail_index_close(map->index); return -1; } if (mdbox_map_refresh(map) < 0) { mail_index_close(map->index); return -1; } } return 1; } int mdbox_map_open(struct mdbox_map *map) { return mdbox_map_open_internal(map, FALSE); } int mdbox_map_open_or_create(struct mdbox_map *map) { return mdbox_map_open_internal(map, TRUE) <= 0 ? -1 : 0; } int mdbox_map_refresh(struct mdbox_map *map) { struct mail_index_view_sync_ctx *ctx; bool delayed_expunges, fscked; int ret = 0; /* some open files may have read partially written mails. now that map syncing makes the new mails visible, we need to make sure the partial data is flushed out of memory */ mdbox_files_sync_input(map->storage); if (mail_index_refresh(map->view->index) < 0) { mail_storage_set_index_error(MAP_STORAGE(map), map->index); return -1; } if (mail_index_view_get_transaction_count(map->view) > 0) { /* can't sync when there are transactions */ return 0; } ctx = mail_index_view_sync_begin(map->view, MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT); fscked = mail_index_reset_fscked(map->view->index); if (mail_index_view_sync_commit(&ctx, &delayed_expunges) < 0) { mail_storage_set_index_error(MAP_STORAGE(map), map->index); ret = -1; } if (fscked) mdbox_storage_set_corrupted(map->storage); return ret; } bool mdbox_map_is_fscked(struct mdbox_map *map) { const struct mail_index_header *hdr; if (map->view == NULL) { /* map isn't opened yet. don't bother. */ return FALSE; } hdr = mail_index_get_header(map->view); return (hdr->flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0; } static void mdbox_map_get_ext_hdr(struct mdbox_map *map, struct mail_index_view *view, struct mdbox_map_mail_index_header *hdr_r) { const void *data; size_t data_size; mail_index_get_header_ext(view, map->map_ext_id, &data, &data_size); i_zero(hdr_r); memcpy(hdr_r, data, I_MIN(data_size, sizeof(*hdr_r))); } uint32_t mdbox_map_get_rebuild_count(struct mdbox_map *map) { struct mdbox_map_mail_index_header hdr; mdbox_map_get_ext_hdr(map, map->view, &hdr); return hdr.rebuild_count; } static int mdbox_map_lookup_seq(struct mdbox_map *map, uint32_t seq, const struct mdbox_map_mail_index_record **rec_r) { const struct mdbox_map_mail_index_record *rec; const void *data; uint32_t uid; mail_index_lookup_ext(map->view, seq, map->map_ext_id, &data, NULL); rec = data; if (rec == NULL || rec->file_id == 0) { mail_index_lookup_uid(map->view, seq, &uid); mdbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid); return -1; } *rec_r = rec; return 0; } static int mdbox_map_get_seq(struct mdbox_map *map, uint32_t map_uid, uint32_t *seq_r) { if (!mail_index_lookup_seq(map->view, map_uid, seq_r)) { /* not found - try again after a refresh */ if (mdbox_map_refresh(map) < 0) return -1; if (!mail_index_lookup_seq(map->view, map_uid, seq_r)) return 0; } return 1; } int mdbox_map_lookup(struct mdbox_map *map, uint32_t map_uid, uint32_t *file_id_r, uoff_t *offset_r) { const struct mdbox_map_mail_index_record *rec; uint32_t seq; int ret; if (mdbox_map_open_or_create(map) < 0) return -1; if ((ret = mdbox_map_get_seq(map, map_uid, &seq)) <= 0) return ret; if (mdbox_map_lookup_seq(map, seq, &rec) < 0) return -1; *file_id_r = rec->file_id; *offset_r = rec->offset; return 1; } int mdbox_map_lookup_full(struct mdbox_map *map, uint32_t map_uid, struct mdbox_map_mail_index_record *rec_r, uint16_t *refcount_r) { uint32_t seq; int ret; if (mdbox_map_open_or_create(map) < 0) return -1; if ((ret = mdbox_map_get_seq(map, map_uid, &seq)) <= 0) return ret; return mdbox_map_lookup_seq_full(map, seq, rec_r, refcount_r); } int mdbox_map_lookup_seq_full(struct mdbox_map *map, uint32_t seq, struct mdbox_map_mail_index_record *rec_r, uint16_t *refcount_r) { const struct mdbox_map_mail_index_record *rec; const uint16_t *ref16_p; const void *data; if (mdbox_map_lookup_seq(map, seq, &rec) < 0) return -1; *rec_r = *rec; mail_index_lookup_ext(map->view, seq, map->ref_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing ref extension"); return -1; } ref16_p = data; *refcount_r = *ref16_p; return 1; } uint32_t mdbox_map_lookup_uid(struct mdbox_map *map, uint32_t seq) { uint32_t uid; mail_index_lookup_uid(map->view, seq, &uid); return uid; } unsigned int mdbox_map_get_messages_count(struct mdbox_map *map) { return mail_index_view_get_messages_count(map->view); } int mdbox_map_view_lookup_rec(struct mdbox_map *map, struct mail_index_view *view, uint32_t seq, struct dbox_mail_lookup_rec *rec_r) { const uint16_t *ref16_p; const void *data; i_zero(rec_r); mail_index_lookup_uid(view, seq, &rec_r->map_uid); mail_index_lookup_ext(view, seq, map->map_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing map extension"); return -1; } memcpy(&rec_r->rec, data, sizeof(rec_r->rec)); mail_index_lookup_ext(view, seq, map->ref_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing ref extension"); return -1; } ref16_p = data; rec_r->refcount = *ref16_p; return 0; } int mdbox_map_get_file_msgs(struct mdbox_map *map, uint32_t file_id, ARRAY_TYPE(mdbox_map_file_msg) *recs) { const struct mail_index_header *hdr; struct dbox_mail_lookup_rec rec; struct mdbox_map_file_msg msg; uint32_t seq; if (mdbox_map_refresh(map) < 0) return -1; hdr = mail_index_get_header(map->view); i_zero(&msg); for (seq = 1; seq <= hdr->messages_count; seq++) { if (mdbox_map_view_lookup_rec(map, map->view, seq, &rec) < 0) return -1; if (rec.rec.file_id == file_id) { msg.map_uid = rec.map_uid; msg.offset = rec.rec.offset; msg.refcount = rec.refcount; array_append(recs, &msg, 1); } } return 0; } int mdbox_map_get_zero_ref_files(struct mdbox_map *map, ARRAY_TYPE(seq_range) *file_ids_r) { const struct mail_index_header *hdr; const struct mdbox_map_mail_index_record *rec; const uint16_t *ref16_p; const void *data; uint32_t seq; bool expunged; int ret; if ((ret = mdbox_map_open(map)) <= 0) { /* no map / internal error */ return ret; } if (mdbox_map_refresh(map) < 0) return -1; hdr = mail_index_get_header(map->view); for (seq = 1; seq <= hdr->messages_count; seq++) { mail_index_lookup_ext(map->view, seq, map->ref_ext_id, &data, &expunged); if (data != NULL && !expunged) { ref16_p = data; if (*ref16_p != 0) continue; } mail_index_lookup_ext(map->view, seq, map->map_ext_id, &data, &expunged); if (data != NULL && !expunged) { rec = data; seq_range_array_add(file_ids_r, rec->file_id); } } return 0; } struct mdbox_map_atomic_context *mdbox_map_atomic_begin(struct mdbox_map *map) { struct mdbox_map_atomic_context *atomic; atomic = i_new(struct mdbox_map_atomic_context, 1); atomic->map = map; return atomic; } static void mdbox_map_sync_handle(struct mdbox_map *map, struct mail_index_sync_ctx *sync_ctx) { struct mail_index_sync_rec sync_rec; uint32_t seq1, seq2; uoff_t offset1, offset2; mail_index_sync_get_offsets(sync_ctx, &seq1, &offset1, &seq2, &offset2); if (offset1 != offset2 || seq1 != seq2) { /* something had crashed. need a full resync. */ i_warning("mdbox %s: Inconsistency in map index " "(%u,%"PRIuUOFF_T" != %u,%"PRIuUOFF_T")", map->path, seq1, offset1, seq2, offset2); mdbox_storage_set_corrupted(map->storage); } while (mail_index_sync_next(sync_ctx, &sync_rec)) ; } int mdbox_map_atomic_lock(struct mdbox_map_atomic_context *atomic, const char *reason) { int ret; if (atomic->locked) return 0; if (mdbox_map_open_or_create(atomic->map) < 0) return -1; /* use syncing to lock the transaction log, so that we always see log's head_offset = tail_offset */ ret = mail_index_sync_begin(atomic->map->index, &atomic->sync_ctx, &atomic->sync_view, &atomic->sync_trans, MAIL_INDEX_SYNC_FLAG_UPDATE_TAIL_OFFSET); if (mail_index_reset_fscked(atomic->map->index)) mdbox_storage_set_corrupted(atomic->map->storage); if (ret <= 0) { i_assert(ret != 0); mail_storage_set_index_error(MAP_STORAGE(atomic->map), atomic->map->index); return -1; } mail_index_sync_set_reason(atomic->sync_ctx, reason); atomic->locked = TRUE; /* reset refresh state so that if it's wanted to be done locked, it gets the latest changes */ atomic->map_refreshed = FALSE; mdbox_map_sync_handle(atomic->map, atomic->sync_ctx); return 0; } bool mdbox_map_atomic_is_locked(struct mdbox_map_atomic_context *atomic) { return atomic->locked; } void mdbox_map_atomic_set_failed(struct mdbox_map_atomic_context *atomic) { atomic->success = FALSE; atomic->failed = TRUE; } void mdbox_map_atomic_set_success(struct mdbox_map_atomic_context *atomic) { if (!atomic->failed) atomic->success = TRUE; } void mdbox_map_atomic_unset_fscked(struct mdbox_map_atomic_context *atomic) { mail_index_unset_fscked(atomic->sync_trans); } int mdbox_map_atomic_finish(struct mdbox_map_atomic_context **_atomic) { struct mdbox_map_atomic_context *atomic = *_atomic; int ret = 0; *_atomic = NULL; if (atomic->sync_ctx == NULL) { /* not locked */ i_assert(!atomic->locked); } else if (atomic->success) { if (mail_index_sync_commit(&atomic->sync_ctx) < 0) { mail_storage_set_index_error(MAP_STORAGE(atomic->map), atomic->map->index); ret = -1; } } else { mail_index_sync_rollback(&atomic->sync_ctx); } i_free(atomic); return ret; } struct mdbox_map_transaction_context * mdbox_map_transaction_begin(struct mdbox_map_atomic_context *atomic, bool external) { struct mdbox_map_transaction_context *ctx; enum mail_index_transaction_flags flags = MAIL_INDEX_TRANSACTION_FLAG_FSYNC; bool success; if (external) flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; ctx = i_new(struct mdbox_map_transaction_context, 1); ctx->atomic = atomic; if (atomic->locked && atomic->map_refreshed) { /* already refreshed within a lock, don't do it again */ success = TRUE; } else { success = mdbox_map_open(atomic->map) > 0 && mdbox_map_refresh(atomic->map) == 0; } if (success) { atomic->map_refreshed = TRUE; ctx->trans = mail_index_transaction_begin(atomic->map->view, flags); } return ctx; } int mdbox_map_transaction_commit(struct mdbox_map_transaction_context *ctx, const char *reason) { i_assert(!ctx->committed); ctx->committed = TRUE; if (!ctx->changed) return 0; if (mdbox_map_atomic_lock(ctx->atomic, reason) < 0) return -1; if (mail_index_transaction_commit(&ctx->trans) < 0) { mail_storage_set_index_error(MAP_STORAGE(ctx->atomic->map), ctx->atomic->map->index); return -1; } mdbox_map_atomic_set_success(ctx->atomic); return 0; } void mdbox_map_transaction_free(struct mdbox_map_transaction_context **_ctx) { struct mdbox_map_transaction_context *ctx = *_ctx; *_ctx = NULL; if (ctx->trans != NULL) mail_index_transaction_rollback(&ctx->trans); i_free(ctx); } int mdbox_map_update_refcount(struct mdbox_map_transaction_context *ctx, uint32_t map_uid, int diff) { struct mdbox_map *map = ctx->atomic->map; const void *data; uint32_t seq; int old_diff, new_diff; if (unlikely(ctx->trans == NULL)) return -1; if (!mail_index_lookup_seq(map->view, map_uid, &seq)) { /* we can't refresh map here since view has a transaction open. */ if (diff > 0) { /* the message was probably just purged */ mail_storage_set_error(MAP_STORAGE(map), MAIL_ERROR_EXPUNGED, "Some of the requested messages no longer exist."); } else { mdbox_map_set_corrupted(map, "refcount update lost map_uid=%u", map_uid); } return -1; } mail_index_lookup_ext(map->view, seq, map->ref_ext_id, &data, NULL); old_diff = data == NULL ? 0 : *((const uint16_t *)data); ctx->changed = TRUE; new_diff = mail_index_atomic_inc_ext(ctx->trans, seq, map->ref_ext_id, diff); if (old_diff + new_diff < 0) { mdbox_map_set_corrupted(map, "map_uid=%u refcount too low", map_uid); return -1; } if (old_diff + new_diff >= 32768 && new_diff > 0) { /* we're getting close to the 64k limit. fail early to make it less likely that two processes increase the refcount enough times to cross the limit */ mail_storage_set_error(MAP_STORAGE(map), MAIL_ERROR_LIMIT, t_strdup_printf("Message has been copied too many times (%d + %d)", old_diff, new_diff)); return -1; } return 0; } int mdbox_map_update_refcounts(struct mdbox_map_transaction_context *ctx, const ARRAY_TYPE(uint32_t) *map_uids, int diff) { const uint32_t *uidp; unsigned int i, count; if (unlikely(ctx->trans == NULL)) return -1; count = array_count(map_uids); for (i = 0; i < count; i++) { uidp = array_idx(map_uids, i); if (mdbox_map_update_refcount(ctx, *uidp, diff) < 0) return -1; } return 0; } int mdbox_map_remove_file_id(struct mdbox_map *map, uint32_t file_id) { struct mdbox_map_atomic_context *atomic; struct mdbox_map_transaction_context *map_trans; const struct mail_index_header *hdr; const struct mdbox_map_mail_index_record *rec; const void *data; uint32_t seq; int ret = 0; /* make sure the map is refreshed, otherwise we might be expunging messages that have already been moved to other files. */ /* we need a per-file transaction, otherwise we can't refresh the map */ atomic = mdbox_map_atomic_begin(map); map_trans = mdbox_map_transaction_begin(atomic, TRUE); hdr = mail_index_get_header(map->view); for (seq = 1; seq <= hdr->messages_count; seq++) { mail_index_lookup_ext(map->view, seq, map->map_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing map extension"); ret = -1; break; } rec = data; if (rec->file_id == file_id) { map_trans->changed = TRUE; mail_index_expunge(map_trans->trans, seq); } } if (ret == 0) ret = mdbox_map_transaction_commit(map_trans, "removing file"); mdbox_map_transaction_free(&map_trans); if (mdbox_map_atomic_finish(&atomic) < 0) ret = -1; return ret; } struct mdbox_map_append_context * mdbox_map_append_begin(struct mdbox_map_atomic_context *atomic) { struct mdbox_map_append_context *ctx; ctx = i_new(struct mdbox_map_append_context, 1); ctx->atomic = atomic; ctx->map = atomic->map; ctx->first_new_file_id = (uint32_t)-1; i_array_init(&ctx->file_appends, 64); i_array_init(&ctx->files, 64); i_array_init(&ctx->appends, 128); if (mdbox_map_open_or_create(atomic->map) < 0) ctx->failed = TRUE; else { /* refresh the map so we can try appending to the latest files */ if (mdbox_map_refresh(atomic->map) == 0) atomic->map_refreshed = TRUE; else ctx->failed = TRUE; } return ctx; } static time_t day_begin_stamp(unsigned int interval) { struct tm tm; time_t stamp; unsigned int unit = 1; if (interval == 0) return 0; /* get the beginning of day/hour/minute depending on how large the interval is */ tm = *localtime(&ioloop_time); if (interval >= 60) { tm.tm_sec = 0; unit = 60; if (interval >= 3600) { tm.tm_min = 0; unit = 3600; if (interval >= 3600*24) { tm.tm_hour = 0; unit = 3600*24; } } } stamp = mktime(&tm); if (stamp == (time_t)-1) i_panic("mktime(today) failed"); return stamp - (interval - unit); } static bool dbox_try_open(struct dbox_file *file, bool want_altpath) { bool notfound; if (want_altpath) { if (dbox_file_open(file, ¬found) <= 0) return FALSE; } else { if (dbox_file_open_primary(file, ¬found) <= 0) return FALSE; } if (notfound) return FALSE; if (file->lock != NULL) { /* already locked, we're possibly in the middle of purging it in which case we really don't want to write there. */ return FALSE; } if (dbox_file_is_in_alt(file) != want_altpath) { /* different alt location than what we want, can't use it */ return FALSE; } return TRUE; } static bool dbox_file_is_ok_at(struct dbox_file *file, uoff_t offset) { bool last; int ret; if (dbox_file_seek(file, offset) == 0) return FALSE; while ((ret = dbox_file_seek_next(file, &offset, &last)) > 0); if (ret == 0 && !last) return FALSE; return TRUE; } static bool mdbox_map_file_try_append(struct mdbox_map_append_context *ctx, bool want_altpath, const struct mdbox_map_mail_index_record *rec, time_t stamp, uoff_t mail_size, struct dbox_file_append_context **file_append_r, struct ostream **output_r, bool *retry_later_r) { struct mdbox_map *map = ctx->map; struct mdbox_storage *storage = map->storage; struct dbox_file *file; struct dbox_file_append_context *file_append; struct stat st; bool file_too_old = FALSE; int ret; *file_append_r = NULL; *output_r = NULL; *retry_later_r = FALSE; file = mdbox_file_init(storage, rec->file_id); if (!dbox_try_open(file, want_altpath)) { dbox_file_unref(&file); return TRUE; } if (file->create_time < stamp) file_too_old = TRUE; else if ((ret = dbox_file_try_lock(file)) <= 0) { /* locking failed */ *retry_later_r = ret == 0; } else if (stat(file->cur_path, &st) < 0) { if (errno != ENOENT) i_error("stat(%s) failed: %m", file->cur_path); /* the file was unlinked between opening and locking it. */ } else if (st.st_size != rec->offset + rec->size && /* check if there's any garbage at the end of file. note that there may be valid messages added by another session before we locked it (but after we refreshed map index). */ !dbox_file_is_ok_at(file, rec->offset + rec->size)) { /* error message was already logged */ } else { file_append = dbox_file_append_init(file); if (dbox_file_get_append_stream(file_append, output_r) <= 0) { /* couldn't append to this file */ } else if ((*output_r)->offset + mail_size > map->set->mdbox_rotate_size) { /* file was too large after all */ } else { /* success */ *file_append_r = file_append; return TRUE; } dbox_file_append_rollback(&file_append); } /* failure */ dbox_file_unlock(file); dbox_file_unref(&file); return !file_too_old; } static bool mdbox_map_is_appending(struct mdbox_map_append_context *ctx, uint32_t file_id) { struct dbox_file_append_context *const *file_appends; unsigned int i, count; /* there shouldn't be many files open, don't bother with anything faster. */ file_appends = array_get(&ctx->file_appends, &count); for (i = 0; i < count; i++) { struct mdbox_file *mfile = (struct mdbox_file *)file_appends[i]->file; if (mfile->file_id == file_id) return TRUE; } return FALSE; } static struct dbox_file_append_context * mdbox_map_find_existing_append(struct mdbox_map_append_context *ctx, uoff_t mail_size, bool want_altpath, struct ostream **output_r) { struct mdbox_map *map = ctx->map; struct dbox_file_append_context *const *file_appends, *append; struct mdbox_file *mfile; unsigned int i, count; uoff_t append_offset; /* first try to use files already used in this append */ file_appends = array_get(&ctx->file_appends, &count); for (i = count; i > ctx->files_nonappendable_count; i--) { append = file_appends[i-1]; if (dbox_file_is_in_alt(append->file) != want_altpath) continue; if (append->file->fd == -1) { /* already closed it (below). we might be able to still fit some small mail there, but that's too much trouble */ continue; } append_offset = append->output->offset; if (append_offset + mail_size <= map->set->mdbox_rotate_size && dbox_file_get_append_stream(append, output_r) > 0) return append; /* can't append to this file anymore. if we created this file, close it so we don't waste fds. if we didn't, we can't close it without also losing our lock too early. */ mfile = (struct mdbox_file *)append->file; if (mfile->file_id == 0 && dbox_file_append_flush(append) == 0) dbox_file_close(append->file); } ctx->files_nonappendable_count = count; return NULL; } static int mdbox_map_find_primary_files(struct mdbox_map_append_context *ctx, ARRAY_TYPE(seq_range) *file_ids_r) { struct mdbox_storage *dstorage = ctx->map->storage; struct mail_storage *storage = &dstorage->storage.storage; DIR *dir; struct dirent *d; uint32_t file_id; int ret = 0; /* we want to quickly find the latest alt file, but we also want to avoid accessing the alt storage as much as possible. typically most of the older mails would be in alt storage, so we'll just put the few m.* files in primary storage to checked_file_ids array. other files are then known to exist in alt storage. */ dir = opendir(dstorage->storage_dir); if (dir == NULL) { mail_storage_set_critical(storage, "opendir(%s) failed: %m", dstorage->storage_dir); return -1; } for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) { if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX, strlen(MDBOX_MAIL_FILE_PREFIX)) != 0) continue; if (str_to_uint32(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX), &file_id) < 0) continue; seq_range_array_add(file_ids_r, file_id); } if (errno != 0) { mail_storage_set_critical(storage, "readdir(%s) failed: %m", dstorage->storage_dir); ret = -1; } if (closedir(dir) < 0) { mail_storage_set_critical(storage, "closedir(%s) failed: %m", dstorage->storage_dir); ret = -1; } return ret; } static int mdbox_map_find_appendable_file(struct mdbox_map_append_context *ctx, uoff_t mail_size, bool want_altpath, struct dbox_file_append_context **file_append_r, struct ostream **output_r) { struct mdbox_map *map = ctx->map; ARRAY_TYPE(seq_range) checked_file_ids; const struct mail_index_header *hdr; const struct mdbox_map_mail_index_record *rec; unsigned int backwards_lookup_count; uint32_t seq, seq1, uid; time_t stamp; bool retry_later; if (mail_size >= map->set->mdbox_rotate_size) return 0; /* try to find an existing appendable file */ stamp = day_begin_stamp(map->set->mdbox_rotate_interval); hdr = mail_index_get_header(map->view); backwards_lookup_count = 0; t_array_init(&checked_file_ids, 16); if (want_altpath) { /* we want to save to alt storage. */ if (mdbox_map_find_primary_files(ctx, &checked_file_ids) < 0) return -1; } for (seq = hdr->messages_count; seq > 0; seq--) { if (mdbox_map_lookup_seq(map, seq, &rec) < 0) return -1; if (seq_range_exists(&checked_file_ids, rec->file_id)) continue; seq_range_array_add(&checked_file_ids, rec->file_id); if (++backwards_lookup_count > MAX_BACKWARDS_LOOKUPS) { /* we've wasted enough time here */ break; } /* first lookup: this should be enough usually, but we can't be sure until after locking. also if messages were recently moved, this message might not be the last one in the file. */ if (rec->offset + rec->size + mail_size >= map->set->mdbox_rotate_size) continue; if (mdbox_map_is_appending(ctx, rec->file_id)) { /* already checked this */ continue; } mail_index_lookup_uid(map->view, seq, &uid); if (!mdbox_map_file_try_append(ctx, want_altpath, rec, stamp, mail_size, file_append_r, output_r, &retry_later)) { /* file is too old. the rest of the files are too. */ break; } /* NOTE: we've now refreshed map view. there are no guarantees about sequences anymore. */ if (*file_append_r != NULL) return 1; /* FIXME: use retry_later somehow */ if (uid == 1 || !mail_index_lookup_seq_range(map->view, 1, uid-1, &seq1, &seq)) break; seq++; } return 0; } int mdbox_map_append_next(struct mdbox_map_append_context *ctx, uoff_t mail_size, enum mdbox_map_append_flags flags, struct dbox_file_append_context **file_append_ctx_r, struct ostream **output_r) { struct dbox_file *file; struct mdbox_map_append *append; struct dbox_file_append_context *file_append; bool existing, want_altpath; int ret; if (ctx->failed) return -1; want_altpath = (flags & DBOX_MAP_APPEND_FLAG_ALT) != 0; file_append = mdbox_map_find_existing_append(ctx, mail_size, want_altpath, output_r); if (file_append != NULL) { ret = 1; existing = TRUE; } else { ret = mdbox_map_find_appendable_file(ctx, mail_size, want_altpath, &file_append, output_r); existing = FALSE; } if (ret > 0) file = file_append->file; else if (ret < 0) return -1; else { /* create a new file */ file = (flags & DBOX_MAP_APPEND_FLAG_ALT) == 0 ? mdbox_file_init(ctx->map->storage, 0) : mdbox_file_init_new_alt(ctx->map->storage); file_append = dbox_file_append_init(file); ret = dbox_file_get_append_stream(file_append, output_r); if (ret <= 0) { i_assert(ret < 0); dbox_file_append_rollback(&file_append); dbox_file_unref(&file); return -1; } } append = array_append_space(&ctx->appends); append->file_append = file_append; append->offset = (*output_r)->offset; append->size = (uint32_t)-1; if (!existing) { i_assert(file_append->first_append_offset == 0); file_append->first_append_offset = file_append->output->offset; array_append(&ctx->file_appends, &file_append, 1); array_append(&ctx->files, &file, 1); } *file_append_ctx_r = file_append; return 0; } static void mdbox_map_append_close_if_unneeded(struct mdbox_map *map, struct dbox_file_append_context *append_ctx) { struct mdbox_file *mfile = (struct mdbox_file *)append_ctx->file; uoff_t end_offset = append_ctx->output->offset; /* if this file is now large enough not to fit any other mails and we created it, close its fd since it's not needed anymore. */ if (end_offset > map->set->mdbox_rotate_size && mfile->file_id == 0 && dbox_file_append_flush(append_ctx) == 0) dbox_file_close(append_ctx->file); } void mdbox_map_append_finish(struct mdbox_map_append_context *ctx) { struct mdbox_map_append *appends, *last; unsigned int count; uoff_t cur_offset; appends = array_get_modifiable(&ctx->appends, &count); i_assert(count > 0); last = &appends[count-1]; i_assert(last->size == (uint32_t)-1); cur_offset = last->file_append->output->offset; i_assert(cur_offset >= last->offset); last->size = cur_offset - last->offset; dbox_file_append_checkpoint(last->file_append); mdbox_map_append_close_if_unneeded(ctx->map, last->file_append); } void mdbox_map_append_abort(struct mdbox_map_append_context *ctx) { struct mdbox_map_append *appends; unsigned int count; appends = array_get_modifiable(&ctx->appends, &count); i_assert(count > 0 && appends[count-1].size == (uint32_t)-1); array_delete(&ctx->appends, count-1, 1); } static int mdbox_find_highest_file_id(struct mdbox_map *map, uint32_t *file_id_r) { const size_t prefix_len = strlen(MDBOX_MAIL_FILE_PREFIX); DIR *dir; struct dirent *d; unsigned int id, highest_id = 0; dir = opendir(map->path); if (dir == NULL) { i_error("opendir(%s) failed: %m", map->path); return -1; } while ((d = readdir(dir)) != NULL) { if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX, prefix_len) == 0 && str_to_uint(d->d_name + prefix_len, &id) == 0) { if (highest_id < id) highest_id = id; } } (void)closedir(dir); *file_id_r = highest_id; return 0; } static int mdbox_map_assign_file_ids(struct mdbox_map_append_context *ctx, bool separate_transaction, const char *reason) { struct dbox_file_append_context *const *file_appends; unsigned int i, count; struct mdbox_map_mail_index_header hdr; uint32_t first_file_id, file_id, existing_id; /* start the syncing. we'll need it even if there are no file ids to be assigned. */ if (mdbox_map_atomic_lock(ctx->atomic, reason) < 0) return -1; mdbox_map_get_ext_hdr(ctx->map, ctx->atomic->sync_view, &hdr); file_id = hdr.highest_file_id + 1; if (ctx->map->verify_existing_file_ids) { /* storage/ directory had been already created but without indexes. scan to see if there exists a higher m.* file id than what is in header, so we won't accidentally overwrite any existing files. */ if (mdbox_find_highest_file_id(ctx->map, &existing_id) < 0) return -1; if (file_id < existing_id+1) file_id = existing_id+1; } /* assign file_ids for newly created files */ first_file_id = file_id; file_appends = array_get(&ctx->file_appends, &count); for (i = 0; i < count; i++) { struct mdbox_file *mfile = (struct mdbox_file *)file_appends[i]->file; if (dbox_file_append_flush(file_appends[i]) < 0) return -1; if (mfile->file_id == 0) { if (mdbox_file_assign_file_id(mfile, file_id++) < 0) return -1; } } ctx->trans = !separate_transaction ? NULL : mail_index_transaction_begin(ctx->map->view, MAIL_INDEX_TRANSACTION_FLAG_FSYNC); /* update the highest used file_id */ if (first_file_id != file_id) { file_id--; mail_index_update_header_ext(ctx->trans != NULL ? ctx->trans : ctx->atomic->sync_trans, ctx->map->map_ext_id, 0, &file_id, sizeof(file_id)); } return 0; } int mdbox_map_append_assign_map_uids(struct mdbox_map_append_context *ctx, uint32_t *first_map_uid_r, uint32_t *last_map_uid_r) { const struct mdbox_map_append *appends; const struct mail_index_header *hdr; struct mdbox_map_mail_index_record rec; unsigned int i, count; ARRAY_TYPE(seq_range) uids; const struct seq_range *range; uint32_t seq; uint16_t ref16; int ret = 0; if (array_count(&ctx->appends) == 0) { *first_map_uid_r = 0; *last_map_uid_r = 0; return 0; } if (mdbox_map_assign_file_ids(ctx, TRUE, "saving - assign uids") < 0) return -1; /* append map records to index */ i_zero(&rec); ref16 = 1; appends = array_get(&ctx->appends, &count); for (i = 0; i < count; i++) { struct mdbox_file *mfile = (struct mdbox_file *)appends[i].file_append->file; i_assert(appends[i].offset <= (uint32_t)-1); i_assert(appends[i].size <= (uint32_t)-1); rec.file_id = mfile->file_id; rec.offset = appends[i].offset; rec.size = appends[i].size; mail_index_append(ctx->trans, 0, &seq); mail_index_update_ext(ctx->trans, seq, ctx->map->map_ext_id, &rec, NULL); mail_index_update_ext(ctx->trans, seq, ctx->map->ref_ext_id, &ref16, NULL); } /* assign map UIDs for appended records */ hdr = mail_index_get_header(ctx->atomic->sync_view); t_array_init(&uids, 1); mail_index_append_finish_uids(ctx->trans, hdr->next_uid, &uids); range = array_idx(&uids, 0); i_assert(range[0].seq2 - range[0].seq1 + 1 == count); if (hdr->uid_validity == 0) { /* we don't really care about uidvalidity, but it can't be 0 */ uint32_t uid_validity = ioloop_time; mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } if (mail_index_transaction_commit(&ctx->trans) < 0) { mail_storage_set_index_error(MAP_STORAGE(ctx->map), ctx->map->index); return -1; } *first_map_uid_r = range[0].seq1; *last_map_uid_r = range[0].seq2; return ret; } int mdbox_map_append_move(struct mdbox_map_append_context *ctx, const ARRAY_TYPE(uint32_t) *map_uids, const ARRAY_TYPE(seq_range) *expunge_map_uids) { const struct mdbox_map_append *appends; struct mdbox_map_mail_index_record rec; struct seq_range_iter iter; const uint32_t *uids; unsigned int i, j, map_uids_count, appends_count; uint32_t uid, seq, next_uid; /* map is locked by this call */ if (mdbox_map_assign_file_ids(ctx, FALSE, "purging - update uids") < 0) return -1; i_zero(&rec); appends = array_get(&ctx->appends, &appends_count); next_uid = mail_index_get_header(ctx->atomic->sync_view)->next_uid; uids = array_get(map_uids, &map_uids_count); for (i = j = 0; i < map_uids_count; i++) { struct mdbox_file *mfile = (struct mdbox_file *)appends[j].file_append->file; i_assert(j < appends_count); rec.file_id = mfile->file_id; rec.offset = appends[j].offset; rec.size = appends[j].size; j++; if (!mail_index_lookup_seq(ctx->atomic->sync_view, uids[i], &seq)) { /* We wrote the email to the new m.* file, but another process already expunged it and purged it. Deleting the email from the new m.* file would be problematic at this point, so just add the mail back to the map with refcount=0 and the next purge will remove it. */ mail_index_append(ctx->atomic->sync_trans, next_uid++, &seq); } mail_index_update_ext(ctx->atomic->sync_trans, seq, ctx->map->map_ext_id, &rec, NULL); } seq_range_array_iter_init(&iter, expunge_map_uids); i = 0; while (seq_range_array_iter_nth(&iter, i++, &uid)) { if (!mail_index_lookup_seq(ctx->atomic->sync_view, uid, &seq)) i_unreached(); mail_index_expunge(ctx->atomic->sync_trans, seq); } return 0; } int mdbox_map_append_flush(struct mdbox_map_append_context *ctx) { struct dbox_file_append_context **file_appends; unsigned int i, count; i_assert(ctx->trans == NULL); file_appends = array_get_modifiable(&ctx->file_appends, &count); for (i = 0; i < count; i++) { if (dbox_file_append_flush(file_appends[i]) < 0) return -1; } return 0; } int mdbox_map_append_commit(struct mdbox_map_append_context *ctx) { struct dbox_file_append_context **file_appends; unsigned int i, count; i_assert(ctx->trans == NULL); file_appends = array_get_modifiable(&ctx->file_appends, &count); for (i = 0; i < count; i++) { if (dbox_file_append_commit(&file_appends[i]) < 0) return -1; } mdbox_map_atomic_set_success(ctx->atomic); return 0; } void mdbox_map_append_free(struct mdbox_map_append_context **_ctx) { struct mdbox_map_append_context *ctx = *_ctx; struct dbox_file_append_context **file_appends; struct dbox_file **files; unsigned int i, count; *_ctx = NULL; if (ctx->trans != NULL) mail_index_transaction_rollback(&ctx->trans); file_appends = array_get_modifiable(&ctx->file_appends, &count); for (i = 0; i < count; i++) { if (file_appends[i] != NULL) dbox_file_append_rollback(&file_appends[i]); } files = array_get_modifiable(&ctx->files, &count); for (i = 0; i < count; i++) { dbox_file_unlock(files[i]); dbox_file_unref(&files[i]); } array_free(&ctx->appends); array_free(&ctx->file_appends); array_free(&ctx->files); i_free(ctx); } static int mdbox_map_generate_uid_validity(struct mdbox_map *map) { const struct mail_index_header *hdr; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; uint32_t uid_validity; int ret; /* do this inside syncing, so that we're locked and there are no race conditions */ ret = mail_index_sync_begin(map->index, &sync_ctx, &view, &trans, 0); if (ret <= 0) { i_assert(ret != 0); return -1; } mdbox_map_sync_handle(map, sync_ctx); hdr = mail_index_get_header(map->view); if (hdr->uid_validity != 0) { /* someone else beat us to it */ } else { uid_validity = ioloop_time; mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } mail_index_sync_set_reason(sync_ctx, "uidvalidity initialization"); return mail_index_sync_commit(&sync_ctx); } uint32_t mdbox_map_get_uid_validity(struct mdbox_map *map) { uint32_t uid_validity; i_assert(map->view != NULL); uid_validity = mail_index_get_header(map->view)->uid_validity; if (uid_validity == 0) mdbox_map_set_corrupted(map, "lost uidvalidity"); return uid_validity; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-storage.h0000644000175000017500000000726513165463624021467 00000000000000#ifndef MDBOX_STORAGE_H #define MDBOX_STORAGE_H #include "index-storage.h" #include "dbox-storage.h" #include "mdbox-settings.h" #define MDBOX_STORAGE_NAME "mdbox" #define MDBOX_DELETED_STORAGE_NAME "mdbox_deleted" #define MDBOX_GLOBAL_INDEX_PREFIX "dovecot.map.index" #define MDBOX_GLOBAL_DIR_NAME "storage" #define MDBOX_MAIL_FILE_PREFIX "m." #define MDBOX_MAIL_FILE_FORMAT MDBOX_MAIL_FILE_PREFIX"%u" #define MDBOX_MAX_OPEN_UNUSED_FILES 2 #define MDBOX_CLOSE_UNUSED_FILES_TIMEOUT_SECS 30 #define MDBOX_INDEX_HEADER_MIN_SIZE (sizeof(uint32_t)) struct mdbox_index_header { uint32_t map_uid_validity; guid_128_t mailbox_guid; uint8_t flags; /* enum dbox_index_header_flags */ uint8_t unused[3]; }; struct mdbox_storage { struct dbox_storage storage; const struct mdbox_settings *set; /* paths for storage directories */ const char *storage_dir, *alt_storage_dir; struct mdbox_map *map; ARRAY(struct mdbox_file *) open_files; struct timeout *to_close_unused_files; ARRAY_TYPE(uint32_t) move_to_alt_map_uids; ARRAY_TYPE(uint32_t) move_from_alt_map_uids; /* if non-zero, storage should be rebuilt (except if rebuild_count has changed from this value) */ uint32_t corrupted_rebuild_count; unsigned int corrupted:1; unsigned int rebuilding_storage:1; unsigned int preallocate_space:1; }; struct mdbox_mail_index_record { uint32_t map_uid; /* UNIX timestamp of when the message was saved/copied to this mailbox */ uint32_t save_date; }; struct mdbox_mailbox { struct mailbox box; struct mdbox_storage *storage; uint32_t map_uid_validity; uint32_t ext_id, hdr_ext_id, guid_ext_id; unsigned int mdbox_deleted_synced:1; unsigned int creating:1; }; extern struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs; extern struct mail_vfuncs mdbox_mail_vfuncs; int mdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r, struct dbox_file **file_r); /* Get map_uid for wanted message. */ int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uint32_t *map_uid_r); uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list); int mdbox_read_header(struct mdbox_mailbox *mbox, struct mdbox_index_header *hdr, bool *need_resize_r); void mdbox_update_header(struct mdbox_mailbox *mbox, struct mail_index_transaction *trans, const struct mailbox_update *update) ATTR_NULL(3); int mdbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans); struct mail_save_context * mdbox_save_alloc(struct mailbox_transaction_context *_t); int mdbox_save_begin(struct mail_save_context *ctx, struct istream *input); int mdbox_save_finish(struct mail_save_context *ctx); void mdbox_save_cancel(struct mail_save_context *ctx); struct dbox_file * mdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq, uoff_t *offset_r); int mdbox_transaction_save_commit_pre(struct mail_save_context *ctx); void mdbox_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void mdbox_transaction_save_rollback(struct mail_save_context *ctx); int mdbox_copy(struct mail_save_context *ctx, struct mail *mail); void mdbox_purge_alt_flag_change(struct mail *mail, bool move_to_alt); int mdbox_purge(struct mail_storage *storage); int mdbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r); void mdbox_storage_destroy(struct mail_storage *_storage); int mdbox_mailbox_open(struct mailbox *box); void mdbox_storage_set_corrupted(struct mdbox_storage *storage); void mdbox_set_mailbox_corrupted(struct mailbox *box); void mdbox_set_file_corrupted(struct dbox_file *file); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-map-private.h0000644000175000017500000000256713165463624022250 00000000000000#ifndef MDBOX_MAP_PRIVATE_H #define MDBOX_MAP_PRIVATE_H #include "mdbox-map.h" struct dbox_mail_lookup_rec { uint32_t map_uid; uint16_t refcount; struct mdbox_map_mail_index_record rec; }; struct mdbox_map { struct mdbox_storage *storage; const struct mdbox_settings *set; char *path, *index_path; struct mail_index *index; struct mail_index_view *view; uint32_t map_ext_id, ref_ext_id; struct mailbox_list *root_list; unsigned int verify_existing_file_ids:1; }; struct mdbox_map_append { struct dbox_file_append_context *file_append; uoff_t offset, size; }; struct mdbox_map_append_context { struct mdbox_map *map; struct mdbox_map_atomic_context *atomic; struct mail_index_transaction *trans; ARRAY(struct dbox_file_append_context *) file_appends; ARRAY(struct dbox_file *) files; ARRAY(struct mdbox_map_append) appends; uint32_t first_new_file_id; unsigned int files_nonappendable_count; unsigned int failed:1; }; struct mdbox_map_atomic_context { struct mdbox_map *map; struct mail_index_transaction *sync_trans; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *sync_view; unsigned int map_refreshed:1; unsigned int locked:1; unsigned int success:1; unsigned int failed:1; }; int mdbox_map_view_lookup_rec(struct mdbox_map *map, struct mail_index_view *view, uint32_t seq, struct dbox_mail_lookup_rec *rec_r); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-storage-rebuild.c0000644000175000017500000006716313165463624023111 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "hash.h" #include "str.h" #include "mail-cache.h" #include "index-rebuild.h" #include "mail-namespace.h" #include "mailbox-list-private.h" #include "mdbox-storage.h" #include "mdbox-file.h" #include "mdbox-map-private.h" #include "mdbox-sync.h" #include "mdbox-storage-rebuild.h" #include #include #define REBUILD_MAX_REFCOUNT 32768 struct mdbox_rebuild_msg { struct mdbox_rebuild_msg *guid_hash_next; guid_128_t guid_128; uint32_t file_id; uint32_t offset; uint32_t rec_size; uoff_t mail_size; uint32_t map_uid; uint16_t refcount; unsigned int seen_zero_ref_in_map:1; }; struct rebuild_msg_mailbox { struct mailbox *box; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; uint32_t next_uid; }; struct mdbox_storage_rebuild_context { struct mdbox_storage *storage; struct mdbox_map_atomic_context *atomic; pool_t pool; struct mdbox_map_mail_index_header orig_map_hdr; HASH_TABLE(uint8_t *, struct mdbox_rebuild_msg *) guid_hash; ARRAY(struct mdbox_rebuild_msg *) msgs; ARRAY_TYPE(seq_range) seen_file_ids; uint32_t rebuild_count; uint32_t highest_file_id; struct mailbox_list *default_list; struct rebuild_msg_mailbox prev_msg; unsigned int have_pop3_uidls:1; unsigned int have_pop3_orders:1; }; static struct mdbox_storage_rebuild_context * mdbox_storage_rebuild_init(struct mdbox_storage *storage, struct mdbox_map_atomic_context *atomic) { struct mdbox_storage_rebuild_context *ctx; i_assert(!storage->rebuilding_storage); ctx = i_new(struct mdbox_storage_rebuild_context, 1); ctx->storage = storage; ctx->atomic = atomic; ctx->pool = pool_alloconly_create("dbox map rebuild", 1024*256); hash_table_create(&ctx->guid_hash, ctx->pool, 0, guid_128_hash, guid_128_cmp); i_array_init(&ctx->msgs, 512); i_array_init(&ctx->seen_file_ids, 128); ctx->storage->rebuilding_storage = TRUE; return ctx; } static void mdbox_storage_rebuild_deinit(struct mdbox_storage_rebuild_context *ctx) { i_assert(ctx->storage->rebuilding_storage); ctx->storage->rebuilding_storage = FALSE; hash_table_destroy(&ctx->guid_hash); pool_unref(&ctx->pool); array_free(&ctx->seen_file_ids); array_free(&ctx->msgs); i_free(ctx); } static int mdbox_rebuild_msg_offset_cmp(struct mdbox_rebuild_msg *const *m1, struct mdbox_rebuild_msg *const *m2) { if ((*m1)->file_id < (*m2)->file_id) return -1; if ((*m1)->file_id > (*m2)->file_id) return 1; if ((*m1)->offset < (*m2)->offset) return -1; if ((*m1)->offset > (*m2)->offset) return 1; if ((*m1)->rec_size < (*m2)->rec_size) return -1; if ((*m1)->rec_size > (*m2)->rec_size) return 1; return 0; } static int mdbox_rebuild_msg_uid_cmp(struct mdbox_rebuild_msg *const *m1, struct mdbox_rebuild_msg *const *m2) { if ((*m1)->map_uid < (*m2)->map_uid) return -1; if ((*m1)->map_uid > (*m2)->map_uid) return 1; return 0; } static void rebuild_scan_metadata(struct mdbox_storage_rebuild_context *ctx, struct dbox_file *file) { if (dbox_file_metadata_get(file, DBOX_METADATA_POP3_UIDL) != NULL) ctx->have_pop3_uidls = TRUE; if (dbox_file_metadata_get(file, DBOX_METADATA_POP3_ORDER) != NULL) ctx->have_pop3_orders = TRUE; } static int rebuild_file_mails(struct mdbox_storage_rebuild_context *ctx, struct dbox_file *file, uint32_t file_id) { const char *guid; uint8_t *guid_p; struct mdbox_rebuild_msg *rec, *old_rec; uoff_t offset, prev_offset; bool last, first, fixed = FALSE; int ret; dbox_file_seek_rewind(file); prev_offset = 0; while ((ret = dbox_file_seek_next(file, &offset, &last)) >= 0) { if (ret > 0) { if ((ret = dbox_file_metadata_read(file)) < 0) break; } if (ret == 0) { /* file is corrupted. fix it and retry. */ if (fixed || last) break; first = prev_offset == 0; if (prev_offset == 0) { /* use existing file header if it was ok */ prev_offset = offset; } if ((ret = dbox_file_fix(file, prev_offset)) < 0) break; if (ret == 0) { /* file was deleted */ return 1; } fixed = TRUE; if (!first) { /* seek to the offset where we last left off */ ret = dbox_file_seek(file, prev_offset); if (ret <= 0) break; } continue; } prev_offset = offset; guid = dbox_file_metadata_get(file, DBOX_METADATA_GUID); if (guid == NULL || *guid == '\0') { dbox_file_set_corrupted(file, "Message is missing GUID"); ret = 0; break; } rebuild_scan_metadata(ctx, file); rec = p_new(ctx->pool, struct mdbox_rebuild_msg, 1); rec->file_id = file_id; rec->offset = offset; rec->rec_size = file->input->v_offset - offset; rec->mail_size = dbox_file_get_plaintext_size(file); mail_generate_guid_128_hash(guid, rec->guid_128); i_assert(!guid_128_is_empty(rec->guid_128)); array_append(&ctx->msgs, &rec, 1); guid_p = rec->guid_128; old_rec = hash_table_lookup(ctx->guid_hash, guid_p); if (old_rec == NULL) hash_table_insert(ctx->guid_hash, guid_p, rec); else if (rec->mail_size == old_rec->mail_size) { /* two mails' GUID and size are the same, which quite likely means that their contents are the same as well. we'll compare the mail sizes instead of the record sizes, because the records' metadata may differ. save this duplicate mail with refcount=0 to the map, so it will eventually be purged. */ rec->seen_zero_ref_in_map = TRUE; } else { /* duplicate GUID, but not a duplicate message. */ i_error("mdbox %s: Duplicate GUID %s in " "m.%u:%u (size=%"PRIuUOFF_T") and m.%u:%u " "(size=%"PRIuUOFF_T")", ctx->storage->storage_dir, guid, old_rec->file_id, old_rec->offset, old_rec->mail_size, rec->file_id, rec->offset, rec->mail_size); rec->guid_hash_next = old_rec->guid_hash_next; old_rec->guid_hash_next = rec; } } if (ret < 0) return -1; else if (ret == 0 && !last) return 0; else return 1; } static int rebuild_rename_file(struct mdbox_storage_rebuild_context *ctx, const char *dir, const char **fname_p, uint32_t *file_id_r) { const char *old_path, *new_path, *fname = *fname_p; old_path = t_strconcat(dir, "/", fname, NULL); do { new_path = t_strdup_printf("%s/"MDBOX_MAIL_FILE_FORMAT, dir, ++ctx->highest_file_id); /* use link()+unlink() instead of rename() to make sure we don't overwrite any files. */ if (link(old_path, new_path) == 0) { i_unlink(old_path); *fname_p = strrchr(new_path, '/') + 1; *file_id_r = ctx->highest_file_id; return 0; } } while (errno == EEXIST); i_error("link(%s, %s) failed: %m", old_path, new_path); return -1; } static int rebuild_add_file(struct mdbox_storage_rebuild_context *ctx, const char *dir, const char *fname) { struct dbox_file *file; uint32_t file_id; const char *id_str, *ext; bool deleted; int ret = 0; id_str = fname + strlen(MDBOX_MAIL_FILE_PREFIX); if (str_to_uint32(id_str, &file_id) < 0 || file_id == 0) { /* m.*.broken files are created by file fixing m.*.lock files are created if flock() isn't available */ ext = strrchr(id_str, '.'); if (ext == NULL || (strcmp(ext, ".broken") != 0 && strcmp(ext, ".lock") != 0)) { i_warning("mdbox rebuild: " "Skipping file with missing ID: %s/%s", dir, fname); } return 0; } if (!seq_range_exists(&ctx->seen_file_ids, file_id)) { if (ctx->highest_file_id < file_id) ctx->highest_file_id = file_id; } else { /* duplicate file. either readdir() returned it twice (unlikely) or it exists in both alt and primary storage. to make sure we don't lose any mails from either of the files, give this file a new ID and rename it. */ if (rebuild_rename_file(ctx, dir, &fname, &file_id) < 0) return -1; } seq_range_array_add(&ctx->seen_file_ids, file_id); file = mdbox_file_init(ctx->storage, file_id); if ((ret = dbox_file_open(file, &deleted)) > 0 && !deleted) ret = rebuild_file_mails(ctx, file, file_id); if (ret == 0) i_error("mdbox rebuild: Failed to fix file %s/%s", dir, fname); dbox_file_unref(&file); return ret < 0 ? -1 : 0; } static void rebuild_add_missing_map_uids(struct mdbox_storage_rebuild_context *ctx, uint32_t next_uid) { struct mdbox_rebuild_msg **msgs; struct mdbox_map_mail_index_record rec; unsigned int i, count; uint32_t seq; i_zero(&rec); msgs = array_get_modifiable(&ctx->msgs, &count); for (i = 0; i < count; i++) { if (msgs[i]->map_uid != 0) continue; rec.file_id = msgs[i]->file_id; rec.offset = msgs[i]->offset; rec.size = msgs[i]->rec_size; msgs[i]->map_uid = next_uid++; mail_index_append(ctx->atomic->sync_trans, msgs[i]->map_uid, &seq); mail_index_update_ext(ctx->atomic->sync_trans, seq, ctx->storage->map->map_ext_id, &rec, NULL); } } static int rebuild_apply_map(struct mdbox_storage_rebuild_context *ctx) { struct mdbox_map *map = ctx->storage->map; const struct mail_index_header *hdr; struct mdbox_rebuild_msg **pos; struct mdbox_rebuild_msg search_msg, *search_msgp = &search_msg; struct dbox_mail_lookup_rec rec; uint32_t seq; array_sort(&ctx->msgs, mdbox_rebuild_msg_offset_cmp); /* msgs now contains a list of all messages that exists in m.* files, sorted by file_id,offset */ hdr = mail_index_get_header(ctx->atomic->sync_view); for (seq = 1; seq <= hdr->messages_count; seq++) { if (mdbox_map_view_lookup_rec(map, ctx->atomic->sync_view, seq, &rec) < 0) return -1; /* look up the rebuild msg record for this message based on the (file_id, offset, size) triplet */ search_msg.file_id = rec.rec.file_id; search_msg.offset = rec.rec.offset; search_msg.rec_size = rec.rec.size; pos = array_bsearch(&ctx->msgs, &search_msgp, mdbox_rebuild_msg_offset_cmp); if (pos == NULL || (*pos)->map_uid != 0) { /* map record points to nonexistent or a duplicate message. */ mail_index_expunge(ctx->atomic->sync_trans, seq); } else { /* remember this message's map_uid */ (*pos)->map_uid = rec.map_uid; if (rec.refcount == 0) (*pos)->seen_zero_ref_in_map = TRUE; } } rebuild_add_missing_map_uids(ctx, hdr->next_uid); /* afterwards we're interested in looking up map_uids. re-sort the messages to make it easier. */ array_sort(&ctx->msgs, mdbox_rebuild_msg_uid_cmp); return 0; } static struct mdbox_rebuild_msg * rebuild_lookup_map_uid(struct mdbox_storage_rebuild_context *ctx, uint32_t map_uid) { struct mdbox_rebuild_msg search_msg, *search_msgp = &search_msg; struct mdbox_rebuild_msg **pos; search_msg.map_uid = map_uid; pos = array_bsearch(&ctx->msgs, &search_msgp, mdbox_rebuild_msg_uid_cmp); return pos == NULL ? NULL : *pos; } static bool guid_hash_have_map_uid(struct mdbox_rebuild_msg **recp, uint32_t map_uid) { struct mdbox_rebuild_msg *rec; for (rec = *recp; rec != NULL; rec = rec->guid_hash_next) { if (rec->map_uid == map_uid) { *recp = rec; return TRUE; } } return FALSE; } static void rebuild_mailbox_multi(struct mdbox_storage_rebuild_context *ctx, struct index_rebuild_context *rebuild_ctx, struct mdbox_mailbox *mbox, struct mail_index_view *view, struct mail_index_transaction *trans) { struct mdbox_mail_index_record new_dbox_rec; const struct mail_index_header *hdr; struct mdbox_rebuild_msg *rec; const void *data; const uint8_t *guid_p; uint32_t old_seq, new_seq, uid, map_uid; /* Rebuild the mailbox's index. Note that index is reset at this point, so although we can still access the old messages, we'll need to append anything we want to keep as new messages. */ hdr = mail_index_get_header(view); for (old_seq = 1; old_seq <= hdr->messages_count; old_seq++) { mail_index_lookup_ext(view, old_seq, mbox->ext_id, &data, NULL); if (data == NULL) { i_zero(&new_dbox_rec); map_uid = 0; } else { memcpy(&new_dbox_rec, data, sizeof(new_dbox_rec)); map_uid = new_dbox_rec.map_uid; } mail_index_lookup_ext(view, old_seq, mbox->guid_ext_id, &data, NULL); guid_p = data; /* see if we can find this message based on 1) GUID, 2) map_uid */ rec = guid_p == NULL ? NULL : hash_table_lookup(ctx->guid_hash, guid_p); if (rec == NULL) { /* multi-dbox message that wasn't found with GUID. either it's lost or GUID has been corrupted. we can still try to look it up using map_uid. */ rec = map_uid == 0 ? NULL : rebuild_lookup_map_uid(ctx, map_uid); map_uid = rec == NULL ? 0 : rec->map_uid; } else if (!guid_hash_have_map_uid(&rec, map_uid)) { /* message's GUID and map_uid point to different physical messages. assume that GUID is correct and map_uid is wrong. */ map_uid = rec->map_uid; } else { /* everything was ok. use this specific record's map_uid to avoid duplicating mails in case the same GUID exists multiple times */ } if (rec != NULL && rec->refcount < REBUILD_MAX_REFCOUNT) T_BEGIN { /* keep this message. add it to mailbox index. */ i_assert(map_uid != 0); rec->refcount++; mail_index_lookup_uid(view, old_seq, &uid); mail_index_append(trans, uid, &new_seq); index_rebuild_index_metadata(rebuild_ctx, new_seq, uid); new_dbox_rec.map_uid = map_uid; mail_index_update_ext(trans, new_seq, mbox->ext_id, &new_dbox_rec, NULL); mail_index_update_ext(trans, new_seq, mbox->guid_ext_id, rec->guid_128, NULL); } T_END; } } static void mdbox_rebuild_get_header(struct mail_index_view *view, uint32_t hdr_ext_id, struct mdbox_index_header *hdr_r, bool *need_resize_r) { const void *data; size_t data_size; mail_index_get_header_ext(view, hdr_ext_id, &data, &data_size); i_zero(hdr_r); memcpy(hdr_r, data, I_MIN(data_size, sizeof(*hdr_r))); *need_resize_r = data_size < sizeof(*hdr_r); } static void mdbox_header_update(struct mdbox_storage_rebuild_context *ctx, struct index_rebuild_context *rebuild_ctx, struct mdbox_mailbox *mbox) { struct mdbox_index_header hdr, backup_hdr; bool need_resize, need_resize_backup; mdbox_rebuild_get_header(rebuild_ctx->view, mbox->hdr_ext_id, &hdr, &need_resize); if (rebuild_ctx->backup_view == NULL) { i_zero(&backup_hdr); need_resize = TRUE; } else { mdbox_rebuild_get_header(rebuild_ctx->backup_view, mbox->hdr_ext_id, &backup_hdr, &need_resize_backup); } /* make sure we have valid mailbox guid */ if (guid_128_is_empty(hdr.mailbox_guid)) { if (!guid_128_is_empty(backup_hdr.mailbox_guid)) { memcpy(hdr.mailbox_guid, backup_hdr.mailbox_guid, sizeof(hdr.mailbox_guid)); } else { guid_128_generate(hdr.mailbox_guid); } } /* update map's uid-validity */ hdr.map_uid_validity = mdbox_map_get_uid_validity(mbox->storage->map); if (ctx->have_pop3_uidls) hdr.flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS; if (ctx->have_pop3_orders) hdr.flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS; /* and write changes */ if (need_resize) { mail_index_ext_resize_hdr(rebuild_ctx->trans, mbox->hdr_ext_id, sizeof(hdr)); } mail_index_update_header_ext(rebuild_ctx->trans, mbox->hdr_ext_id, 0, &hdr, sizeof(hdr)); } static int rebuild_mailbox(struct mdbox_storage_rebuild_context *ctx, struct mail_namespace *ns, const char *vname) { struct mailbox *box; struct mdbox_mailbox *mbox; struct mail_index_sync_ctx *sync_ctx; struct mail_index_view *view; struct mail_index_transaction *trans; struct index_rebuild_context *rebuild_ctx; enum mail_error error; int ret; box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, "mdbox rebuild"); if (box->storage != &ctx->storage->storage.storage) { /* the namespace has multiple storages. */ mailbox_free(&box); return 0; } if (mailbox_open(box) < 0) { error = mailbox_get_last_mail_error(box); i_error("Couldn't open mailbox '%s': %s", vname, mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); if (error == MAIL_ERROR_TEMP) return -1; /* non-temporary error, ignore */ return 0; } mbox = (struct mdbox_mailbox *)box; ret = mail_index_sync_begin(box->index, &sync_ctx, &view, &trans, MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES); if (ret <= 0) { i_assert(ret != 0); mailbox_set_index_error(box); mailbox_free(&box); return -1; } rebuild_ctx = index_index_rebuild_init(&mbox->box, view, trans); mdbox_header_update(ctx, rebuild_ctx, mbox); rebuild_mailbox_multi(ctx, rebuild_ctx, mbox, view, trans); index_index_rebuild_deinit(&rebuild_ctx, dbox_get_uidvalidity_next); mail_index_unset_fscked(trans); mail_index_sync_set_reason(sync_ctx, "mdbox storage rebuild"); if (mail_index_sync_commit(&sync_ctx) < 0) { mailbox_set_index_error(box); ret = -1; } mailbox_free(&box); return ret < 0 ? -1 : 0; } static int rebuild_namespace_mailboxes(struct mdbox_storage_rebuild_context *ctx, struct mail_namespace *ns) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; int ret = 0; if (ctx->default_list == NULL || (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) ctx->default_list = ns->list; iter = mailbox_list_iter_init(ns->list, "*", MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) == 0) { T_BEGIN { ret = rebuild_mailbox(ctx, ns, info->vname); } T_END; if (ret < 0) { ret = -1; break; } } } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static int rebuild_mailboxes(struct mdbox_storage_rebuild_context *ctx) { struct mail_storage *storage = &ctx->storage->storage.storage; struct mail_namespace *ns; for (ns = storage->user->namespaces; ns != NULL; ns = ns->next) { if (ns->storage == storage && ns->alias_for == NULL) { if (rebuild_namespace_mailboxes(ctx, ns) < 0) return -1; } } if (ctx->default_list == NULL) i_panic("No namespace found for storage=%s", storage->name); return 0; } static int rebuild_msg_mailbox_commit(struct rebuild_msg_mailbox *msg) { mail_index_sync_set_reason(msg->sync_ctx, "mdbox storage rebuild"); if (mail_index_sync_commit(&msg->sync_ctx) < 0) return -1; mailbox_free(&msg->box); i_zero(msg); return 0; } static int rebuild_restore_msg(struct mdbox_storage_rebuild_context *ctx, struct mdbox_rebuild_msg *msg) { struct mail_storage *storage = &ctx->storage->storage.storage; struct dbox_file *file; const struct mail_index_header *hdr; struct mdbox_mail_index_record dbox_rec; const char *mailbox = NULL; struct mailbox *box; struct mdbox_mailbox *mbox; enum mail_error error; bool deleted, created; int ret; uint32_t seq; /* first see if message contains the mailbox it was originally saved to */ file = mdbox_file_init(ctx->storage, msg->file_id); ret = dbox_file_open(file, &deleted); if (ret > 0 && !deleted) ret = dbox_file_seek(file, msg->offset); if (ret > 0 && !deleted && dbox_file_metadata_read(file) > 0) { mailbox = dbox_file_metadata_get(file, DBOX_METADATA_ORIG_MAILBOX); if (mailbox != NULL) { mailbox = mailbox_list_get_vname(ctx->default_list, mailbox); mailbox = t_strdup(mailbox); } rebuild_scan_metadata(ctx, file); } dbox_file_unref(&file); if (ret <= 0 || deleted) { if (ret < 0) return -1; /* we shouldn't get here, so apparently we couldn't fix something. just ignore the mail.. */ return 0; } if (mailbox == NULL) mailbox = "INBOX"; /* we have the destination mailbox. now open it and add the message there. */ created = FALSE; box = ctx->prev_msg.box != NULL && strcmp(mailbox, ctx->prev_msg.box->vname) == 0 ? ctx->prev_msg.box : NULL; while (box == NULL) { box = mailbox_alloc(ctx->default_list, mailbox, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, "mdbox rebuild restore"); i_assert(box->storage == storage); if (mailbox_open(box) == 0) break; error = mailbox_get_last_mail_error(box); if (error == MAIL_ERROR_NOTFOUND && !created) { /* mailbox doesn't exist currently? see if creating it helps. */ created = TRUE; (void)mailbox_create(box, NULL, FALSE); mailbox_free(&box); continue; } mailbox_free(&box); if (error == MAIL_ERROR_TEMP) return -1; if (strcmp(mailbox, "INBOX") != 0) { /* see if we can save to INBOX instead. */ mailbox = "INBOX"; } else { /* this shouldn't happen */ return -1; } } mbox = (struct mdbox_mailbox *)box; /* switch the mailbox cache if necessary */ if (box != ctx->prev_msg.box && ctx->prev_msg.box != NULL) { if (rebuild_msg_mailbox_commit(&ctx->prev_msg) < 0) return -1; } if (ctx->prev_msg.box == NULL) { ret = mail_index_sync_begin(box->index, &ctx->prev_msg.sync_ctx, &ctx->prev_msg.view, &ctx->prev_msg.trans, 0); if (ret <= 0) { i_assert(ret != 0); mailbox_set_index_error(box); mailbox_free(&box); return -1; } ctx->prev_msg.box = box; hdr = mail_index_get_header(ctx->prev_msg.view); ctx->prev_msg.next_uid = hdr->next_uid; } /* add the new message */ i_zero(&dbox_rec); dbox_rec.map_uid = msg->map_uid; dbox_rec.save_date = ioloop_time; mail_index_append(ctx->prev_msg.trans, ctx->prev_msg.next_uid++, &seq); mail_index_update_ext(ctx->prev_msg.trans, seq, mbox->ext_id, &dbox_rec, NULL); mail_index_update_ext(ctx->prev_msg.trans, seq, mbox->guid_ext_id, msg->guid_128, NULL); i_assert(msg->refcount == 0); msg->refcount++; return 0; } static int rebuild_handle_zero_refs(struct mdbox_storage_rebuild_context *ctx) { struct mdbox_rebuild_msg **msgs; unsigned int i, count; /* if we have messages at this point which have refcount=0, they're either already expunged or they were somehow lost for some reason. we'll need to figure out what to do about them. */ msgs = array_get_modifiable(&ctx->msgs, &count); for (i = 0; i < count; i++) { if (msgs[i]->refcount != 0) continue; if (msgs[i]->seen_zero_ref_in_map) { /* we've seen the map record, trust it. */ continue; } /* either map record was lost for this message or the message was lost from its mailbox. safest way to handle this is to restore the message. */ if (rebuild_restore_msg(ctx, msgs[i]) < 0) return -1; } if (ctx->prev_msg.box != NULL) { if (rebuild_msg_mailbox_commit(&ctx->prev_msg) < 0) return -1; } return 0; } static void rebuild_update_refcounts(struct mdbox_storage_rebuild_context *ctx) { const struct mail_index_header *hdr; const void *data; struct mdbox_rebuild_msg **msgs; const uint16_t *ref16_p; uint32_t seq, map_uid; unsigned int i, count; /* update refcounts for existing map records */ msgs = array_get_modifiable(&ctx->msgs, &count); hdr = mail_index_get_header(ctx->atomic->sync_view); for (seq = 1, i = 0; seq <= hdr->messages_count && i < count; seq++) { mail_index_lookup_uid(ctx->atomic->sync_view, seq, &map_uid); if (map_uid != msgs[i]->map_uid) { /* we've already expunged this map record */ i_assert(map_uid < msgs[i]->map_uid); continue; } mail_index_lookup_ext(ctx->atomic->sync_view, seq, ctx->storage->map->ref_ext_id, &data, NULL); ref16_p = data; if (ref16_p == NULL || *ref16_p != msgs[i]->refcount) { mail_index_update_ext(ctx->atomic->sync_trans, seq, ctx->storage->map->ref_ext_id, &msgs[i]->refcount, NULL); } i++; } /* update refcounts for newly created map records */ for (; i < count; i++, seq++) { mail_index_update_ext(ctx->atomic->sync_trans, seq, ctx->storage->map->ref_ext_id, &msgs[i]->refcount, NULL); } } static int rebuild_finish(struct mdbox_storage_rebuild_context *ctx) { struct mdbox_map_mail_index_header map_hdr; i_assert(ctx->default_list != NULL); if (rebuild_handle_zero_refs(ctx) < 0) return -1; rebuild_update_refcounts(ctx); /* update map header */ map_hdr = ctx->orig_map_hdr; map_hdr.highest_file_id = ctx->highest_file_id; map_hdr.rebuild_count = ++ctx->rebuild_count; mail_index_update_header_ext(ctx->atomic->sync_trans, ctx->storage->map->map_ext_id, 0, &map_hdr, sizeof(map_hdr)); return 0; } static int mdbox_storage_rebuild_scan_dir(struct mdbox_storage_rebuild_context *ctx, const char *storage_dir, bool alt) { DIR *dir; struct dirent *d; int ret = 0; dir = opendir(storage_dir); if (dir == NULL) { if (alt && errno == ENOENT) return 0; mail_storage_set_critical(&ctx->storage->storage.storage, "opendir(%s) failed: %m", storage_dir); return -1; } for (errno = 0; (d = readdir(dir)) != NULL && ret == 0; errno = 0) { if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX, strlen(MDBOX_MAIL_FILE_PREFIX)) == 0) T_BEGIN { ret = rebuild_add_file(ctx, storage_dir, d->d_name); } T_END; } if (ret == 0 && errno != 0) { mail_storage_set_critical(&ctx->storage->storage.storage, "readdir(%s) failed: %m", storage_dir); ret = -1; } if (closedir(dir) < 0) { mail_storage_set_critical(&ctx->storage->storage.storage, "closedir(%s) failed: %m", storage_dir); ret = -1; } return ret; } static int mdbox_storage_rebuild_scan(struct mdbox_storage_rebuild_context *ctx) { const void *data; size_t data_size; if (mdbox_map_open_or_create(ctx->storage->map) < 0) return -1; /* begin by locking the map, so that other processes can't try to rebuild at the same time. */ if (mdbox_map_atomic_lock(ctx->atomic, "mdbox storage rebuild") < 0) return -1; /* fsck the map just in case its UIDs are broken */ if (mail_index_fsck(ctx->storage->map->index) < 0) { mail_storage_set_index_error(&ctx->storage->storage.storage, ctx->storage->map->index); return -1; } /* get old map header */ mail_index_get_header_ext(ctx->atomic->sync_view, ctx->storage->map->map_ext_id, &data, &data_size); i_zero(&ctx->orig_map_hdr); memcpy(&ctx->orig_map_hdr, data, I_MIN(data_size, sizeof(ctx->orig_map_hdr))); ctx->highest_file_id = ctx->orig_map_hdr.highest_file_id; /* get storage rebuild counter after locking */ ctx->rebuild_count = mdbox_map_get_rebuild_count(ctx->storage->map); if (ctx->rebuild_count != ctx->storage->corrupted_rebuild_count && ctx->storage->corrupted) { /* storage was already rebuilt by someone else */ return 0; } i_warning("mdbox %s: rebuilding indexes", ctx->storage->storage_dir); if (mdbox_storage_rebuild_scan_dir(ctx, ctx->storage->storage_dir, FALSE) < 0) return -1; if (ctx->storage->alt_storage_dir != NULL) { if (mdbox_storage_rebuild_scan_dir(ctx, ctx->storage->alt_storage_dir, TRUE) < 0) return -1; } if (rebuild_apply_map(ctx) < 0 || rebuild_mailboxes(ctx) < 0 || rebuild_finish(ctx) < 0) { mdbox_map_atomic_set_failed(ctx->atomic); return -1; } return 0; } int mdbox_storage_rebuild_in_context(struct mdbox_storage *storage, struct mdbox_map_atomic_context *atomic) { struct mdbox_storage_rebuild_context *ctx; int ret; if (dbox_verify_alt_storage(storage->map->root_list) < 0) { mail_storage_set_critical(&storage->storage.storage, "mdbox rebuild: Alt storage %s not mounted, aborting", storage->alt_storage_dir); mdbox_map_atomic_set_failed(atomic); return -1; } ctx = mdbox_storage_rebuild_init(storage, atomic); ret = mdbox_storage_rebuild_scan(ctx); mdbox_storage_rebuild_deinit(ctx); if (ret == 0) { storage->corrupted = FALSE; storage->corrupted_rebuild_count = 0; } return ret; } int mdbox_storage_rebuild(struct mdbox_storage *storage) { struct mdbox_map_atomic_context *atomic; int ret; atomic = mdbox_map_atomic_begin(storage->map); ret = mdbox_storage_rebuild_in_context(storage, atomic); mdbox_map_atomic_set_success(atomic); mdbox_map_atomic_unset_fscked(atomic); if (mdbox_map_atomic_finish(&atomic) < 0) ret = -1; return ret; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-mail.c0000644000175000017500000001616513165463624020737 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "str.h" #include "index-mail.h" #include "dbox-mail.h" #include "mdbox-storage.h" #include "mdbox-sync.h" #include "mdbox-map.h" #include "mdbox-file.h" #include int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uint32_t *map_uid_r) { const struct mdbox_mail_index_record *dbox_rec; struct mdbox_index_header hdr; const void *data; uint32_t uid, cur_map_uid_validity; bool need_resize; mail_index_lookup_ext(view, seq, mbox->ext_id, &data, NULL); dbox_rec = data; if (dbox_rec == NULL || dbox_rec->map_uid == 0) { mail_index_lookup_uid(view, seq, &uid); mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uid lost for uid %u", mailbox_get_path(&mbox->box), uid); mdbox_storage_set_corrupted(mbox->storage); return -1; } if (mbox->map_uid_validity == 0) { if (mdbox_read_header(mbox, &hdr, &need_resize) < 0) return -1; mbox->map_uid_validity = hdr.map_uid_validity; } if (mdbox_map_open_or_create(mbox->storage->map) < 0) return -1; cur_map_uid_validity = mdbox_map_get_uid_validity(mbox->storage->map); if (cur_map_uid_validity != mbox->map_uid_validity) { mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uidvalidity mismatch (%u vs %u)", mailbox_get_path(&mbox->box), mbox->map_uid_validity, cur_map_uid_validity); mdbox_storage_set_corrupted(mbox->storage); return -1; } *map_uid_r = dbox_rec->map_uid; return 0; } static void dbox_mail_set_expunged(struct dbox_mail *mail, uint32_t map_uid) { struct mail *_mail = &mail->imail.mail.mail; struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)_mail->box; mail_index_refresh(_mail->box->index); if (mail_index_is_expunged(_mail->transaction->view, _mail->seq)) { mail_set_expunged(_mail); return; } mdbox_map_set_corrupted(mbox->storage->map, "Unexpectedly lost %s uid=%u map_uid=%u", mailbox_get_vname(_mail->box), _mail->uid, map_uid); } static int dbox_mail_open_init(struct dbox_mail *mail, uint32_t map_uid) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->imail.mail.mail.box; uint32_t file_id; int ret; if ((ret = mdbox_map_lookup(mbox->storage->map, map_uid, &file_id, &mail->offset)) <= 0) { if (ret < 0) return -1; /* map_uid doesn't exist anymore. either it got just expunged or the map index is corrupted. */ dbox_mail_set_expunged(mail, map_uid); return -1; } else { mail->open_file = mdbox_file_init(mbox->storage, file_id); } return 0; } int mdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r, struct dbox_file **file_r) { struct mail *_mail = &mail->imail.mail.mail; struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)_mail->box; uint32_t prev_file_id = 0, map_uid = 0; bool deleted; if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(_mail); return -1; } _mail->mail_stream_opened = TRUE; do { if (mail->open_file != NULL) { /* already open */ } else if (!_mail->saving) { if (mdbox_mail_lookup(mbox, _mail->transaction->view, _mail->seq, &map_uid) < 0) return -1; if (dbox_mail_open_init(mail, map_uid) < 0) return -1; } else { /* mail is being saved in this transaction */ mail->open_file = mdbox_save_file_get_file(_mail->transaction, _mail->seq, &mail->offset); } if (!dbox_file_is_open(mail->open_file)) _mail->transaction->stats.open_lookup_count++; if (dbox_file_open(mail->open_file, &deleted) <= 0) return -1; if (deleted) { /* either it's expunged now or moved to another file. */ struct mdbox_file *mfile = (struct mdbox_file *)mail->open_file; if (mfile->file_id == prev_file_id) { dbox_mail_set_expunged(mail, map_uid); return -1; } prev_file_id = mfile->file_id; if (mdbox_map_refresh(mbox->storage->map) < 0) return -1; dbox_file_unref(&mail->open_file); } } while (mail->open_file == NULL); *file_r = mail->open_file; *offset_r = mail->offset; return 0; } static int mdbox_mail_get_save_date(struct mail *mail, time_t *date_r) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->transaction->box; const struct mdbox_mail_index_record *dbox_rec; const void *data; mail_index_lookup_ext(mail->transaction->view, mail->seq, mbox->ext_id, &data, NULL); dbox_rec = data; if (dbox_rec == NULL || dbox_rec->map_uid == 0) { /* lost for some reason, use fallback */ return dbox_mail_get_save_date(mail, date_r); } *date_r = dbox_rec->save_date; return 0; } static int mdbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct dbox_mail *mail = (struct dbox_mail *)_mail; struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)_mail->transaction->box; struct mdbox_map_mail_index_record rec; uint32_t map_uid; uint16_t refcount; switch (field) { case MAIL_FETCH_REFCOUNT: if (mdbox_mail_lookup(mbox, _mail->transaction->view, _mail->seq, &map_uid) < 0) return -1; if (mdbox_map_lookup_full(mbox->storage->map, map_uid, &rec, &refcount) < 0) return -1; *value_r = p_strdup_printf(mail->imail.mail.data_pool, "%u", refcount); return 0; case MAIL_FETCH_UIDL_BACKEND: if (!dbox_header_have_flag(&mbox->box, mbox->hdr_ext_id, offsetof(struct mdbox_index_header, flags), DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS)) { *value_r = ""; return 0; } break; case MAIL_FETCH_POP3_ORDER: if (!dbox_header_have_flag(&mbox->box, mbox->hdr_ext_id, offsetof(struct mdbox_index_header, flags), DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS)) { *value_r = ""; return 0; } break; default: break; } return dbox_mail_get_special(_mail, field, value_r); } static void mdbox_mail_update_flags(struct mail *mail, enum modify_type modify_type, enum mail_flags flags) { if ((flags & DBOX_INDEX_FLAG_ALT) != 0) { mdbox_purge_alt_flag_change(mail, modify_type != MODIFY_REMOVE); flags &= ~DBOX_INDEX_FLAG_ALT; if (flags == 0 && modify_type != MODIFY_REPLACE) return; } index_mail_update_flags(mail, modify_type, flags); } struct mail_vfuncs mdbox_mail_vfuncs = { dbox_mail_close, index_mail_free, index_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, dbox_mail_get_received_date, mdbox_mail_get_save_date, dbox_mail_get_virtual_size, dbox_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, dbox_mail_get_stream, index_mail_get_binary_stream, mdbox_mail_get_special, index_mail_get_real_mail, mdbox_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-settings.h0000644000175000017500000000043513165463624021653 00000000000000#ifndef MDBOX_SETTINGS_H #define MDBOX_SETTINGS_H struct mdbox_settings { bool mdbox_preallocate_space; bool mdbox_purge_preserve_alt; uoff_t mdbox_rotate_size; unsigned int mdbox_rotate_interval; }; const struct setting_parser_info *mdbox_get_setting_parser_info(void); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-save.c0000644000175000017500000003470613165463624020754 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fdatasync-path.h" #include "hex-binary.h" #include "hex-dec.h" #include "str.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "write-full.h" #include "index-mail.h" #include "index-pop3-uidl.h" #include "mail-copy.h" #include "dbox-save.h" #include "mdbox-storage.h" #include "mdbox-map.h" #include "mdbox-file.h" #include "mdbox-sync.h" struct dbox_save_mail { struct dbox_file_append_context *file_append; uint32_t seq; uint32_t append_offset; bool written_to_disk; }; struct mdbox_save_context { struct dbox_save_context ctx; struct mdbox_mailbox *mbox; struct mdbox_sync_context *sync_ctx; struct dbox_file *cur_file; struct dbox_file_append_context *cur_file_append; struct mdbox_map_append_context *append_ctx; ARRAY_TYPE(uint32_t) copy_map_uids; struct mdbox_map_atomic_context *atomic; struct mdbox_map_transaction_context *map_trans; ARRAY(struct dbox_save_mail) mails; }; static struct dbox_file * mdbox_copy_file_get_file(struct mailbox_transaction_context *t, uint32_t seq, uoff_t *offset_r) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)t->save_ctx; const struct mdbox_mail_index_record *rec; const void *data; uint32_t file_id; mail_index_lookup_ext(t->view, seq, ctx->mbox->ext_id, &data, NULL); rec = data; if (mdbox_map_lookup(ctx->mbox->storage->map, rec->map_uid, &file_id, offset_r) < 0) i_unreached(); return mdbox_file_init(ctx->mbox->storage, file_id); } struct dbox_file * mdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq, uoff_t *offset_r) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)t->save_ctx; const struct dbox_save_mail *mails, *mail; unsigned int count; mails = array_get(&ctx->mails, &count); i_assert(count > 0); i_assert(seq >= mails[0].seq); mail = &mails[seq - mails[0].seq]; i_assert(mail->seq == seq); if (mail->file_append == NULL) { /* copied mail */ return mdbox_copy_file_get_file(t, seq, offset_r); } /* saved mail */ i_assert(mail->written_to_disk); if (dbox_file_append_flush(mail->file_append) < 0) ctx->ctx.failed = TRUE; mail->file_append->file->refcount++; *offset_r = mail->append_offset; return mail->file_append->file; } struct mail_save_context * mdbox_save_alloc(struct mailbox_transaction_context *t) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)t->box; struct mdbox_save_context *ctx = (struct mdbox_save_context *)t->save_ctx; i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (ctx != NULL) { /* use the existing allocated structure */ ctx->cur_file = NULL; ctx->ctx.failed = FALSE; ctx->ctx.finished = FALSE; ctx->ctx.dbox_output = NULL; ctx->cur_file_append = NULL; return &ctx->ctx.ctx; } ctx = i_new(struct mdbox_save_context, 1); ctx->ctx.ctx.transaction = t; ctx->ctx.trans = t->itrans; ctx->mbox = mbox; ctx->atomic = mdbox_map_atomic_begin(mbox->storage->map); ctx->append_ctx = mdbox_map_append_begin(ctx->atomic); i_array_init(&ctx->mails, 32); t->save_ctx = &ctx->ctx.ctx; return t->save_ctx; } int mdbox_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; struct dbox_save_mail *save_mail; uoff_t mail_size, append_offset; /* get the size of the mail to be saved, if possible */ if (i_stream_get_size(input, TRUE, &mail_size) <= 0) { /* we couldn't find out the exact size. fallback to non-exact, maybe it'll give something useful. the mail size is used only to figure out if it's causing mdbox file to grow too large. */ if (i_stream_get_size(input, FALSE, &mail_size) <= 0) mail_size = 0; } if (mdbox_map_append_next(ctx->append_ctx, mail_size, 0, &ctx->cur_file_append, &ctx->ctx.dbox_output) < 0) { ctx->ctx.failed = TRUE; return -1; } i_assert(ctx->ctx.dbox_output->offset <= (uint32_t)-1); append_offset = ctx->ctx.dbox_output->offset; ctx->cur_file = ctx->cur_file_append->file; dbox_save_begin(&ctx->ctx, input); save_mail = array_append_space(&ctx->mails); save_mail->file_append = ctx->cur_file_append; save_mail->seq = ctx->ctx.seq; save_mail->append_offset = append_offset; return ctx->ctx.failed ? -1 : 0; } static int mdbox_save_mail_write_metadata(struct mdbox_save_context *ctx, struct dbox_save_mail *mail) { struct dbox_file *file = mail->file_append->file; struct dbox_message_header dbox_msg_hdr; uoff_t message_size; guid_128_t guid_128; i_assert(file->msg_header_size == sizeof(dbox_msg_hdr)); message_size = ctx->ctx.dbox_output->offset - mail->append_offset - mail->file_append->file->msg_header_size; dbox_save_write_metadata(&ctx->ctx.ctx, ctx->ctx.dbox_output, message_size, ctx->mbox->box.name, guid_128); /* save the 128bit GUID to index so if the map index gets corrupted we can still find the message */ mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq, ctx->mbox->guid_ext_id, guid_128, NULL); dbox_msg_header_fill(&dbox_msg_hdr, message_size); if (o_stream_pwrite(ctx->ctx.dbox_output, &dbox_msg_hdr, sizeof(dbox_msg_hdr), mail->append_offset) < 0) { dbox_file_set_syscall_error(file, "pwrite()"); return -1; } mail->written_to_disk = TRUE; return 0; } static int mdbox_save_finish_write(struct mail_save_context *_ctx) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; struct dbox_save_mail *mail; ctx->ctx.finished = TRUE; if (ctx->ctx.dbox_output == NULL) return -1; dbox_save_end(&ctx->ctx); mail = array_idx_modifiable(&ctx->mails, array_count(&ctx->mails) - 1); if (!ctx->ctx.failed) T_BEGIN { if (mdbox_save_mail_write_metadata(ctx, mail) < 0) ctx->ctx.failed = TRUE; else mdbox_map_append_finish(ctx->append_ctx); } T_END; if (mail->file_append->file->input != NULL) { /* if we try to read the saved mail before unlocking file, make sure the input stream doesn't have stale data */ i_stream_sync(mail->file_append->file->input); } i_stream_unref(&ctx->ctx.input); if (ctx->ctx.failed) { index_storage_save_abort_last(&ctx->ctx.ctx, ctx->ctx.seq); mdbox_map_append_abort(ctx->append_ctx); array_delete(&ctx->mails, array_count(&ctx->mails) - 1, 1); return -1; } return 0; } int mdbox_save_finish(struct mail_save_context *ctx) { int ret; ret = mdbox_save_finish_write(ctx); index_save_context_free(ctx); return ret; } void mdbox_save_cancel(struct mail_save_context *_ctx) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; ctx->failed = TRUE; (void)mdbox_save_finish(_ctx); } static void mdbox_save_set_map_uids(struct mdbox_save_context *ctx, uint32_t first_map_uid, uint32_t last_map_uid) { struct mdbox_mailbox *mbox = ctx->mbox; struct mail_index_view *view = ctx->ctx.ctx.transaction->view; const struct mdbox_mail_index_record *old_rec; struct mdbox_mail_index_record rec; const struct dbox_save_mail *mails; unsigned int i, count; const void *data; uint32_t next_map_uid = first_map_uid; mdbox_update_header(mbox, ctx->ctx.trans, NULL); i_zero(&rec); rec.save_date = ioloop_time; mails = array_get(&ctx->mails, &count); for (i = 0; i < count; i++) { mail_index_lookup_ext(view, mails[i].seq, mbox->ext_id, &data, NULL); old_rec = data; if (old_rec != NULL && old_rec->map_uid != 0) { /* message was copied. keep the existing map uid */ continue; } rec.map_uid = next_map_uid++; mail_index_update_ext(ctx->ctx.trans, mails[i].seq, mbox->ext_id, &rec, NULL); } i_assert(next_map_uid == last_map_uid + 1); } int mdbox_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; const struct mail_index_header *hdr; uint32_t first_map_uid, last_map_uid; i_assert(ctx->ctx.finished); /* flush/fsync writes to m.* files before locking the map */ if (mdbox_map_append_flush(ctx->append_ctx) < 0) { mdbox_transaction_save_rollback(_ctx); return -1; } /* make sure the map gets locked */ if (mdbox_map_atomic_lock(ctx->atomic, "saving") < 0) { mdbox_transaction_save_rollback(_ctx); return -1; } /* lock the mailbox after map to avoid deadlocks. if we've noticed any corruption, deal with it later, otherwise we won't have up-to-date atomic->sync_view */ if (mdbox_sync_begin(ctx->mbox, MDBOX_SYNC_FLAG_NO_PURGE | MDBOX_SYNC_FLAG_FORCE | MDBOX_SYNC_FLAG_FSYNC | MDBOX_SYNC_FLAG_NO_REBUILD, ctx->atomic, &ctx->sync_ctx) < 0) { mdbox_transaction_save_rollback(_ctx); return -1; } /* assign map UIDs for newly saved messages after we've successfully acquired all the locks. the transaction is now very unlikely to fail. the UIDs are written to the transaction log immediately within this function, but the map is left locked. */ if (mdbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid, &last_map_uid) < 0) { mdbox_transaction_save_rollback(_ctx); return -1; } /* update dbox header flags */ dbox_save_update_header_flags(&ctx->ctx, ctx->sync_ctx->sync_view, ctx->mbox->hdr_ext_id, offsetof(struct mdbox_index_header, flags)); /* assign UIDs for new messages */ hdr = mail_index_get_header(ctx->sync_ctx->sync_view); mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid, &_t->changes->saved_uids); if (ctx->ctx.highest_pop3_uidl_seq != 0) { const struct dbox_save_mail *mails; struct seq_range_iter iter; unsigned int highest_pop3_uidl_idx; uint32_t uid; mails = array_idx(&ctx->mails, 0); highest_pop3_uidl_idx = ctx->ctx.highest_pop3_uidl_seq - mails[0].seq; i_assert(mails[highest_pop3_uidl_idx].seq == ctx->ctx.highest_pop3_uidl_seq); seq_range_array_iter_init(&iter, &_t->changes->saved_uids); if (!seq_range_array_iter_nth(&iter, highest_pop3_uidl_idx, &uid)) i_unreached(); index_pop3_uidl_set_max_uid(&ctx->mbox->box, ctx->ctx.trans, uid); } /* save map UIDs to mailbox index */ if (first_map_uid != 0) mdbox_save_set_map_uids(ctx, first_map_uid, last_map_uid); /* increase map's refcount for copied mails */ if (array_is_created(&ctx->copy_map_uids)) { ctx->map_trans = mdbox_map_transaction_begin(ctx->atomic, FALSE); if (mdbox_map_update_refcounts(ctx->map_trans, &ctx->copy_map_uids, 1) < 0) { mdbox_transaction_save_rollback(_ctx); return -1; } mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "copying"); } else { mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "saving"); } _t->changes->uid_validity = hdr->uid_validity; return 0; } void mdbox_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; _ctx->transaction = NULL; /* transaction is already freed */ mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx, result); /* finish writing the mailbox APPENDs */ if (mdbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) { /* commit refcount increases for copied mails */ if (ctx->map_trans != NULL) { if (mdbox_map_transaction_commit(ctx->map_trans, "copy refcount updates") < 0) mdbox_map_atomic_set_failed(ctx->atomic); } /* flush file append writes */ if (mdbox_map_append_commit(ctx->append_ctx) < 0) mdbox_map_atomic_set_failed(ctx->atomic); } mdbox_map_append_free(&ctx->append_ctx); /* update the sync tail offset, everything else was already written at this point. */ (void)mdbox_map_atomic_finish(&ctx->atomic); if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { const char *box_path = mailbox_get_path(&ctx->mbox->box); if (fdatasync_path(box_path) < 0) { mail_storage_set_critical(storage, "fdatasync_path(%s) failed: %m", box_path); } } mdbox_transaction_save_rollback(_ctx); } void mdbox_transaction_save_rollback(struct mail_save_context *_ctx) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; if (!ctx->ctx.finished) mdbox_save_cancel(&ctx->ctx.ctx); if (ctx->append_ctx != NULL) mdbox_map_append_free(&ctx->append_ctx); if (ctx->map_trans != NULL) mdbox_map_transaction_free(&ctx->map_trans); if (ctx->atomic != NULL) (void)mdbox_map_atomic_finish(&ctx->atomic); if (array_is_created(&ctx->copy_map_uids)) array_free(&ctx->copy_map_uids); if (ctx->sync_ctx != NULL) (void)mdbox_sync_finish(&ctx->sync_ctx, FALSE); array_free(&ctx->mails); i_free(ctx); } int mdbox_copy(struct mail_save_context *_ctx, struct mail *mail) { struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx; struct dbox_save_mail *save_mail; struct mdbox_mailbox *src_mbox; struct mdbox_mail_index_record rec; const void *guid_data; guid_128_t wanted_guid; ctx->ctx.finished = TRUE; if (mail->box->storage != _ctx->transaction->box->storage || _ctx->transaction->box->disable_reflink_copy_to) return mail_storage_copy(_ctx, mail); src_mbox = (struct mdbox_mailbox *)mail->box; i_zero(&rec); rec.save_date = ioloop_time; if (mdbox_mail_lookup(src_mbox, mail->transaction->view, mail->seq, &rec.map_uid) < 0) { index_save_context_free(_ctx); return -1; } mail_index_lookup_ext(mail->transaction->view, mail->seq, src_mbox->guid_ext_id, &guid_data, NULL); if (guid_data == NULL || guid_128_is_empty(guid_data)) { /* missing GUID, something's broken. don't copy using refcounting. */ return mail_storage_copy(_ctx, mail); } else if (_ctx->data.guid != NULL && (guid_128_from_string(_ctx->data.guid, wanted_guid) < 0 || memcmp(guid_data, wanted_guid, sizeof(wanted_guid)) != 0)) { /* GUID change requested. we can't do it with refcount copying */ return mail_storage_copy(_ctx, mail); } /* remember the map_uid so we can later increase its refcount */ if (!array_is_created(&ctx->copy_map_uids)) i_array_init(&ctx->copy_map_uids, 32); array_append(&ctx->copy_map_uids, &rec.map_uid, 1); /* add message to mailbox index */ dbox_save_add_to_index(&ctx->ctx); mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq, ctx->mbox->ext_id, &rec, NULL); mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq, ctx->mbox->guid_ext_id, guid_data, NULL); index_copy_cache_fields(_ctx, mail, ctx->ctx.seq); save_mail = array_append_space(&ctx->mails); save_mail->seq = ctx->ctx.seq; mail_set_seq_saving(_ctx->dest_mail, ctx->ctx.seq); index_save_context_free(_ctx); return 0; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-sync.c0000644000175000017500000002551113165463624020764 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ /* Expunging works like: 1. Lock map index by beginning a map sync. 2. Write map UID refcount changes to map index (=> tail != head). 3. Expunge messages from mailbox index. 4. Finish map sync, which updates tail=head and unlocks map index. If something crashes after 2 but before 4 is finished, tail != head and reader can do a full resync to figure out what got broken. */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "str.h" #include "mdbox-storage.h" #include "mdbox-storage-rebuild.h" #include "mdbox-map.h" #include "mdbox-file.h" #include "mdbox-sync.h" static int dbox_sync_verify_expunge_guid(struct mdbox_sync_context *ctx, uint32_t seq, const guid_128_t guid_128) { const void *data; uint32_t uid; mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); if (guid_128_is_empty(guid_128) || memcmp(data, guid_128, GUID_128_SIZE) == 0) return 0; mail_storage_set_critical(&ctx->mbox->storage->storage.storage, "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", ctx->mbox->box.vname, uid, guid_128_to_string(data), guid_128_to_string(guid_128)); mdbox_storage_set_corrupted(ctx->mbox->storage); return -1; } static int mdbox_sync_expunge(struct mdbox_sync_context *ctx, uint32_t seq, const guid_128_t guid_128) { uint32_t map_uid; if (seq_range_array_add(&ctx->expunged_seqs, seq)) { /* already marked as expunged in this sync */ return 0; } if (dbox_sync_verify_expunge_guid(ctx, seq, guid_128) < 0) return -1; if (mdbox_mail_lookup(ctx->mbox, ctx->sync_view, seq, &map_uid) < 0) return -1; if (mdbox_map_update_refcount(ctx->map_trans, map_uid, -1) < 0) return -1; return 0; } static int mdbox_sync_rec(struct mdbox_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { uint32_t seq, seq1, seq2; if (sync_rec->type != MAIL_INDEX_SYNC_TYPE_EXPUNGE) { /* not interested */ return 0; } if (!mail_index_lookup_seq_range(ctx->sync_view, sync_rec->uid1, sync_rec->uid2, &seq1, &seq2)) { /* already expunged everything. nothing to do. */ return 0; } for (seq = seq1; seq <= seq2; seq++) { if (mdbox_sync_expunge(ctx, seq, sync_rec->guid_128) < 0) return -1; } return 0; } static int dbox_sync_mark_expunges(struct mdbox_sync_context *ctx) { enum mail_index_transaction_flags flags = MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; struct mailbox *box = &ctx->mbox->box; struct mail_index_transaction *trans; struct seq_range_iter iter; unsigned int n; const void *data; uint32_t seq, uid; /* use a separate transaction here so that we can commit the changes during map transaction */ trans = mail_index_transaction_begin(ctx->sync_view, flags); seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); mail_index_expunge_guid(trans, seq, data); } if (mail_index_transaction_commit(&trans) < 0) return -1; if (box->v.sync_notify != NULL) { /* do notifications after commit finished successfully */ box->tmp_sync_view = ctx->sync_view; seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } box->tmp_sync_view = NULL; } return 0; } static int mdbox_sync_index(struct mdbox_sync_context *ctx) { struct mailbox *box = &ctx->mbox->box; const struct mail_index_header *hdr; struct mail_index_sync_rec sync_rec; uint32_t seq1, seq2; int ret = 0; hdr = mail_index_get_header(ctx->sync_view); if (hdr->uid_validity == 0) { /* newly created index file */ if (hdr->next_uid == 1) { /* could be just a race condition where we opened the mailbox between mkdir and index creation. fix this silently. */ if (mdbox_mailbox_create_indexes(box, NULL, ctx->trans) < 0) return -1; return 1; } mail_storage_set_critical(box->storage, "Mailbox %s: Broken index: missing UIDVALIDITY", box->vname); return 0; } /* mark the newly seen messages as recent */ if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid, hdr->next_uid, &seq1, &seq2)) { mailbox_recent_flags_set_seqs(&ctx->mbox->box, ctx->sync_view, seq1, seq2); } /* handle syncing records without map being locked. */ if (mdbox_map_atomic_is_locked(ctx->atomic)) { ctx->map_trans = mdbox_map_transaction_begin(ctx->atomic, FALSE); i_array_init(&ctx->expunged_seqs, 64); } while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) { if ((ret = mdbox_sync_rec(ctx, &sync_rec)) < 0) break; } /* write refcount changes to map index. transaction commit updates the log head, while tail is left behind. */ if (mdbox_map_atomic_is_locked(ctx->atomic)) { if (ret == 0) ret = mdbox_map_transaction_commit(ctx->map_trans, "mdbox syncing"); /* write changes to mailbox index */ if (ret == 0) ret = dbox_sync_mark_expunges(ctx); /* finish the map changes and unlock the map. this also updates map's tail -> head. */ if (ret < 0) mdbox_map_atomic_set_failed(ctx->atomic); mdbox_map_transaction_free(&ctx->map_trans); ctx->expunged_count = seq_range_count(&ctx->expunged_seqs); array_free(&ctx->expunged_seqs); } if (box->v.sync_notify != NULL) box->v.sync_notify(box, 0, 0); return ret == 0 ? 1 : (ctx->mbox->storage->corrupted ? 0 : -1); } static int mdbox_sync_try_begin(struct mdbox_sync_context *ctx, enum mail_index_sync_flags sync_flags) { struct mdbox_mailbox *mbox = ctx->mbox; int ret; ret = index_storage_expunged_sync_begin(&mbox->box, &ctx->index_sync_ctx, &ctx->sync_view, &ctx->trans, sync_flags); if (mail_index_reset_fscked(mbox->box.index)) mdbox_storage_set_corrupted(mbox->storage); if (ret <= 0) return ret; /* error / nothing to do */ if (!mdbox_map_atomic_is_locked(ctx->atomic) && mail_index_sync_has_expunges(ctx->index_sync_ctx)) { /* we have expunges, so we need to write to map. it needs to be locked before mailbox index. */ mail_index_sync_set_reason(ctx->index_sync_ctx, "mdbox expunge check"); mail_index_sync_rollback(&ctx->index_sync_ctx); index_storage_expunging_deinit(&ctx->mbox->box); if (mdbox_map_atomic_lock(ctx->atomic, "mdbox syncing with expunges") < 0) return -1; return mdbox_sync_try_begin(ctx, sync_flags); } return 1; } int mdbox_sync_begin(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags, struct mdbox_map_atomic_context *atomic, struct mdbox_sync_context **ctx_r) { struct mail_storage *storage = mbox->box.storage; const struct mail_index_header *hdr = mail_index_get_header(mbox->box.view); struct mdbox_sync_context *ctx; const char *reason; enum mail_index_sync_flags sync_flags; int ret; bool rebuild, storage_rebuilt = FALSE; *ctx_r = NULL; /* avoid race conditions with mailbox creation, don't check for dbox headers until syncing has locked the mailbox */ rebuild = mbox->storage->corrupted || (hdr->flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0 || mdbox_map_is_fscked(mbox->storage->map) || (flags & MDBOX_SYNC_FLAG_FORCE_REBUILD) != 0; if (rebuild && (flags & MDBOX_SYNC_FLAG_NO_REBUILD) == 0) { if (mdbox_storage_rebuild_in_context(mbox->storage, atomic) < 0) return -1; mailbox_recent_flags_reset(&mbox->box); storage_rebuilt = TRUE; } ctx = i_new(struct mdbox_sync_context, 1); ctx->mbox = mbox; ctx->flags = flags; ctx->atomic = atomic; sync_flags = index_storage_get_sync_flags(&mbox->box); if (!rebuild && (flags & MDBOX_SYNC_FLAG_FORCE) == 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES; if ((flags & MDBOX_SYNC_FLAG_FSYNC) != 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_FSYNC; /* don't write unnecessary dirty flag updates */ sync_flags |= MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES; ret = mdbox_sync_try_begin(ctx, sync_flags); if (ret <= 0) { /* failed / nothing to do */ index_storage_expunging_deinit(&mbox->box); i_free(ctx); return ret; } if ((ret = mdbox_sync_index(ctx)) <= 0) { mail_index_sync_set_reason(ctx->index_sync_ctx, ret < 0 ? "mdbox syncing failed" : "mdbox syncing found corruption"); mail_index_sync_rollback(&ctx->index_sync_ctx); index_storage_expunging_deinit(&mbox->box); i_free_and_null(ctx); if (ret < 0) return -1; /* corrupted */ if (storage_rebuilt) { mail_storage_set_critical(storage, "mdbox %s: Storage keeps breaking", mailbox_get_path(&mbox->box)); return -1; } /* we'll need to rebuild storage. try again from the beginning. */ mdbox_storage_set_corrupted(mbox->storage); if ((flags & MDBOX_SYNC_FLAG_NO_REBUILD) != 0) { mail_storage_set_critical(storage, "mdbox %s: Can't rebuild storage", mailbox_get_path(&mbox->box)); return -1; } return mdbox_sync_begin(mbox, flags, atomic, ctx_r); } index_storage_expunging_deinit(&mbox->box); if (!mdbox_map_atomic_is_locked(ctx->atomic)) reason = "mdbox synced"; else { /* may be 0 msgs, but that still informs that the map was locked */ reason = t_strdup_printf("mdbox synced - %u msgs expunged", ctx->expunged_count); } mail_index_sync_set_reason(ctx->index_sync_ctx, reason); *ctx_r = ctx; return 0; } int mdbox_sync_finish(struct mdbox_sync_context **_ctx, bool success) { struct mdbox_sync_context *ctx = *_ctx; int ret = success ? 0 : -1; *_ctx = NULL; if (success) { if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { mailbox_set_index_error(&ctx->mbox->box); ret = -1; } } else { mail_index_sync_rollback(&ctx->index_sync_ctx); } i_free(ctx); return ret; } int mdbox_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags) { struct mdbox_sync_context *sync_ctx; struct mdbox_map_atomic_context *atomic; int ret; atomic = mdbox_map_atomic_begin(mbox->storage->map); ret = mdbox_sync_begin(mbox, flags, atomic, &sync_ctx); if (ret == 0 && sync_ctx != NULL) ret = mdbox_sync_finish(&sync_ctx, TRUE); if (ret == 0) mdbox_map_atomic_set_success(atomic); if (mdbox_map_atomic_finish(&atomic) < 0) ret = -1; return ret; } struct mailbox_sync_context * mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; enum mdbox_sync_flags mdbox_sync_flags = 0; int ret = 0; if (mail_index_reset_fscked(box->index)) mdbox_storage_set_corrupted(mbox->storage); if (index_mailbox_want_full_sync(&mbox->box, flags) || mbox->storage->corrupted) { if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0) mdbox_sync_flags |= MDBOX_SYNC_FLAG_FORCE_REBUILD; ret = mdbox_sync(mbox, mdbox_sync_flags); } return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-deleted-storage.c0000644000175000017500000002042513165463624023057 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "mkdir-parents.h" #include "master-service.h" #include "mail-index-modseq.h" #include "mail-index-alloc-cache.h" #include "mailbox-log.h" #include "mailbox-list-private.h" #include "mail-copy.h" #include "dbox-mail.h" #include "dbox-save.h" #include "mdbox-map.h" #include "mdbox-file.h" #include "mdbox-sync.h" #include "mdbox-storage-rebuild.h" #include "mdbox-storage.h" extern struct mail_storage mdbox_deleted_storage; extern struct mailbox mdbox_deleted_mailbox; extern struct dbox_storage_vfuncs mdbox_deleted_dbox_storage_vfuncs; static struct mail_storage *mdbox_deleted_storage_alloc(void) { struct mdbox_storage *storage; pool_t pool; pool = pool_alloconly_create("mdbox deleted storage", 2048); storage = p_new(pool, struct mdbox_storage, 1); storage->storage.v = mdbox_dbox_storage_vfuncs; storage->storage.storage = mdbox_deleted_storage; storage->storage.storage.pool = pool; return &storage->storage.storage; } static struct mailbox * mdbox_deleted_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct mdbox_mailbox *mbox; pool_t pool; flags |= MAILBOX_FLAG_READONLY | MAILBOX_FLAG_NO_INDEX_FILES; pool = pool_alloconly_create("mdbox deleted mailbox", 1024*3); mbox = p_new(pool, struct mdbox_mailbox, 1); mbox->box = mdbox_deleted_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &mdbox_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = (struct mdbox_storage *)storage; return &mbox->box; } static int mdbox_deleted_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; struct mail_index_transaction *new_trans = NULL; uint32_t uid_validity = ioloop_time; uint32_t uid_next = 1; if (update != NULL && update->uid_validity != 0) uid_validity = update->uid_validity; if (trans == NULL) { new_trans = mail_index_transaction_begin(box->view, 0); trans = new_trans; } mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); mail_index_update_header(trans, offsetof(struct mail_index_header, next_uid), &uid_next, sizeof(uid_next), TRUE); mbox->creating = TRUE; mdbox_update_header(mbox, trans, update); mbox->creating = FALSE; if (new_trans != NULL) { if (mail_index_transaction_commit(&new_trans) < 0) { mailbox_set_index_error(box); return -1; } } return 0; } static const char * mdbox_get_attachment_path_suffix(struct dbox_file *file ATTR_UNUSED) { return ""; } static int mdbox_deleted_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; if ((items & MAILBOX_METADATA_GUID) != 0) guid_128_generate(metadata_r->guid); return 0; } static struct mail_save_context * mdbox_deleted_save_alloc(struct mailbox_transaction_context *t) { struct mail_save_context *ctx; ctx = i_new(struct mail_save_context, 1); ctx->transaction = t; return ctx; } static int mdbox_deleted_save_begin(struct mail_save_context *ctx, struct istream *input ATTR_UNUSED) { mail_storage_set_error(ctx->transaction->box->storage, MAIL_ERROR_NOTPOSSIBLE, "mdbox_deleted doesn't support saving mails"); return -1; } static int mdbox_deleted_save_continue(struct mail_save_context *ctx ATTR_UNUSED) { return -1; } static int mdbox_deleted_save_finish(struct mail_save_context *ctx) { index_save_context_free(ctx); return -1; } static void mdbox_deleted_save_cancel(struct mail_save_context *ctx) { index_save_context_free(ctx); } static int mdbox_deleted_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags ATTR_UNUSED) { struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; struct mdbox_mail_index_record rec; struct mdbox_map_mail_index_record map_rec; enum mail_index_sync_flags sync_flags; uint16_t refcount; uint32_t map_seq, map_count, seq, uid = 0; int ret = 0; if (mbox->mdbox_deleted_synced) { /* don't bother supporting incremental syncs */ return 0; } if (!mbox->box.inbox_user && mbox->box.name[0] != '\0') { /* since mailbox list currently shows all the existing mailboxes, we don't want all of them to list the deleted messages. only show messages in user's INBOX or the namespace prefix. */ return 0; } if (mdbox_map_open(mbox->storage->map) < 0) return -1; if (mdbox_deleted_mailbox_create_indexes(&mbox->box, NULL, NULL) < 0) return -1; i_zero(&rec); rec.save_date = ioloop_time; sync_flags = index_storage_get_sync_flags(&mbox->box); if (mail_index_sync_begin(mbox->box.index, &index_sync_ctx, &sync_view, &trans, sync_flags) < 0) { mailbox_set_index_error(&mbox->box); return -1; } map_count = mdbox_map_get_messages_count(mbox->storage->map); for (map_seq = 1; map_seq <= map_count; map_seq++) { if (mdbox_map_lookup_seq_full(mbox->storage->map, map_seq, &map_rec, &refcount) < 0) { ret = -1; break; } if (refcount == 0) { rec.map_uid = mdbox_map_lookup_uid(mbox->storage->map, map_seq); mail_index_append(trans, ++uid, &seq); mail_index_update_ext(trans, seq, mbox->ext_id, &rec, NULL); } } if (ret < 0) mail_index_sync_rollback(&index_sync_ctx); else { if (mail_index_sync_commit(&index_sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); ret = -1; } else { mbox->mdbox_deleted_synced = TRUE; } } return ret; } static struct mailbox_sync_context * mdbox_deleted_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; enum mdbox_sync_flags mdbox_sync_flags = 0; int ret = 0; if (index_mailbox_want_full_sync(&mbox->box, flags) || mbox->storage->corrupted) ret = mdbox_deleted_sync(mbox, mdbox_sync_flags); return index_mailbox_sync_init(box, flags, ret < 0); } struct mail_storage mdbox_deleted_storage = { .name = MDBOX_DELETED_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA, .v = { mdbox_get_setting_parser_info, mdbox_deleted_storage_alloc, mdbox_storage_create, mdbox_storage_destroy, NULL, dbox_storage_get_list_settings, NULL, mdbox_deleted_mailbox_alloc, NULL, NULL, } }; struct mailbox mdbox_deleted_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, mdbox_mailbox_open, index_storage_mailbox_close, index_storage_mailbox_free, dbox_mailbox_create, index_storage_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, mdbox_deleted_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, mdbox_deleted_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, dbox_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, dbox_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, mdbox_deleted_save_alloc, mdbox_deleted_save_begin, mdbox_deleted_save_continue, mdbox_deleted_save_finish, mdbox_deleted_save_cancel, mail_storage_copy, NULL, NULL, NULL, index_storage_is_inconsistent } }; struct dbox_storage_vfuncs mdbox_deleted_dbox_storage_vfuncs = { mdbox_file_unrefed, mdbox_file_create_fd, mdbox_mail_open, mdbox_deleted_mailbox_create_indexes, mdbox_get_attachment_path_suffix, mdbox_set_mailbox_corrupted, mdbox_set_file_corrupted }; dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-sync.h0000644000175000017500000000210113123174404020744 00000000000000#ifndef MDBOX_SYNC_H #define MDBOX_SYNC_H struct mailbox; struct mdbox_mailbox; enum mdbox_sync_flags { MDBOX_SYNC_FLAG_FORCE = 0x01, MDBOX_SYNC_FLAG_FSYNC = 0x02, MDBOX_SYNC_FLAG_FORCE_REBUILD = 0x04, MDBOX_SYNC_FLAG_NO_PURGE = 0x08, MDBOX_SYNC_FLAG_NO_REBUILD = 0x10 }; struct mdbox_sync_context { struct mdbox_mailbox *mbox; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; struct mdbox_map_transaction_context *map_trans; struct mdbox_map_atomic_context *atomic; enum mdbox_sync_flags flags; ARRAY_TYPE(seq_range) expunged_seqs; unsigned int expunged_count; }; int mdbox_sync_begin(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags, struct mdbox_map_atomic_context *atomic, struct mdbox_sync_context **ctx_r); int mdbox_sync_finish(struct mdbox_sync_context **ctx, bool success); int mdbox_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags); struct mailbox_sync_context * mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-purge.c0000644000175000017500000004533513165463624021140 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "hash.h" #include "dbox-attachment.h" #include "mdbox-storage.h" #include "mdbox-storage-rebuild.h" #include "mdbox-file.h" #include "mdbox-map.h" #include "mdbox-sync.h" #include /* Altmoving works like: 1. Message's DBOX_INDEX_FLAG_ALT flag is changed. This is caught by mdbox code and map UID's alt-refcount is updated. It won't be written to disk. 2. mdbox_purge() is called, which checks if map UID's refcount equals to its alt-refcount. If it does, it's moved to alt storage. Moving to primary storage is done if _ALT flag was removed from any message. */ enum mdbox_msg_action { MDBOX_MSG_ACTION_MOVE_TO_ALT = 1, MDBOX_MSG_ACTION_MOVE_FROM_ALT }; struct mdbox_purge_context { pool_t pool; struct mdbox_storage *storage; uint32_t lowest_primary_file_id; /* list of file_ids that exist in primary storage. this list is looked up while there is no locking, so it may not be accurate anymore by the time it's used. */ ARRAY_TYPE(seq_range) primary_file_ids; /* list of file_ids that we need to purge */ ARRAY_TYPE(seq_range) purge_file_ids; /* uint32_t map_uid => enum mdbox_msg_action action */ HASH_TABLE(void *, void *) altmoves; bool have_altmoves; struct mdbox_map_atomic_context *atomic; struct mdbox_map_append_context *append_ctx; }; static int mdbox_map_file_msg_offset_cmp(const struct mdbox_map_file_msg *m1, const struct mdbox_map_file_msg *m2) { if (m1->offset < m2->offset) return -1; else if (m1->offset > m2->offset) return 1; else return 0; } static int mdbox_file_read_metadata_hdr(struct dbox_file *file, struct dbox_metadata_header *meta_hdr_r) { const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(file->input, &data, &size, sizeof(*meta_hdr_r)); if (ret <= 0) { i_assert(ret == -1); if (file->input->stream_errno == 0) { dbox_file_set_corrupted(file, "missing metadata"); return 0; } mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %s", file->cur_path, i_stream_get_error(file->input)); return -1; } memcpy(meta_hdr_r, data, sizeof(*meta_hdr_r)); if (memcmp(meta_hdr_r->magic_post, DBOX_MAGIC_POST, sizeof(meta_hdr_r->magic_post)) != 0) { dbox_file_set_corrupted(file, "invalid metadata magic"); return 0; } i_stream_skip(file->input, sizeof(*meta_hdr_r)); return 1; } static int mdbox_file_metadata_copy(struct dbox_file *file, struct ostream *output) { struct dbox_metadata_header meta_hdr; const char *line; size_t buf_size; int ret; if ((ret = mdbox_file_read_metadata_hdr(file, &meta_hdr)) <= 0) return ret; o_stream_nsend(output, &meta_hdr, sizeof(meta_hdr)); buf_size = i_stream_get_max_buffer_size(file->input); /* use unlimited line length for metadata */ i_stream_set_max_buffer_size(file->input, (size_t)-1); while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == '\0') { /* end of metadata */ break; } o_stream_nsend_str(output, line); o_stream_nsend(output, "\n", 1); } i_stream_set_max_buffer_size(file->input, buf_size); if (line == NULL) { dbox_file_set_corrupted(file, "missing end-of-metadata line"); return 0; } o_stream_nsend(output, "\n", 1); return 1; } static int mdbox_metadata_get_extrefs(struct dbox_file *file, pool_t ext_refs_pool, ARRAY_TYPE(mail_attachment_extref) *extrefs) { struct dbox_metadata_header meta_hdr; const char *line; size_t buf_size; int ret; /* skip and ignore the header */ if ((ret = mdbox_file_read_metadata_hdr(file, &meta_hdr)) <= 0) return ret; buf_size = i_stream_get_max_buffer_size(file->input); /* use unlimited line length for metadata */ i_stream_set_max_buffer_size(file->input, (size_t)-1); while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == '\0') { /* end of metadata */ break; } if (*line == DBOX_METADATA_EXT_REF) T_BEGIN { if (!index_attachment_parse_extrefs(line+1, ext_refs_pool, extrefs)) { i_warning("%s: Ignoring corrupted extref: %s", file->cur_path, line); } } T_END; } i_stream_set_max_buffer_size(file->input, buf_size); if (line == NULL) { dbox_file_set_corrupted(file, "missing end-of-metadata line"); return 0; } return 1; } static bool mdbox_purge_want_altpath(struct mdbox_purge_context *ctx, struct dbox_file *file, uint32_t map_uid) { enum mdbox_msg_action action; void *value; if (dbox_file_is_in_alt(file) && ctx->storage->set->mdbox_purge_preserve_alt) return TRUE; if (!ctx->have_altmoves) return FALSE; value = hash_table_lookup(ctx->altmoves, POINTER_CAST(map_uid)); action = POINTER_CAST_TO(value, enum mdbox_msg_action); return action == MDBOX_MSG_ACTION_MOVE_TO_ALT; } static int mdbox_purge_save_msg(struct mdbox_purge_context *ctx, struct dbox_file *file, const struct mdbox_map_file_msg *msg) { struct dbox_file_append_context *out_file_append; struct istream *input; struct ostream *output; enum mdbox_map_append_flags append_flags; uoff_t msg_size; off_t ret; if (ctx->append_ctx == NULL) ctx->append_ctx = mdbox_map_append_begin(ctx->atomic); append_flags = !mdbox_purge_want_altpath(ctx, file, msg->map_uid) ? 0 : DBOX_MAP_APPEND_FLAG_ALT; msg_size = file->msg_header_size + file->cur_physical_size; if (mdbox_map_append_next(ctx->append_ctx, file->cur_physical_size, append_flags, &out_file_append, &output) < 0) return -1; i_assert(file != out_file_append->file); input = i_stream_create_limit(file->input, msg_size); ret = o_stream_send_istream(output, input); if (input->stream_errno != 0) { mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %s", file->cur_path, i_stream_get_error(input)); i_stream_unref(&input); return -1; } i_stream_unref(&input); if (o_stream_nfinish(output) < 0) { mail_storage_set_critical(&file->storage->storage, "write(%s) failed: %s", out_file_append->file->cur_path, o_stream_get_error(output)); return -1; } if (ret != (off_t)msg_size) { i_assert(ret < (off_t)msg_size); i_assert(i_stream_is_eof(file->input)); dbox_file_set_corrupted(file, "truncated message at EOF"); return 0; } /* copy metadata */ if ((ret = mdbox_file_metadata_copy(file, output)) <= 0) return ret; mdbox_map_append_finish(ctx->append_ctx); return 1; } static int mdbox_file_purge_check_refcounts(struct mdbox_purge_context *ctx, const ARRAY_TYPE(mdbox_map_file_msg) *msgs_arr) { struct mdbox_map *map = ctx->storage->map; struct mdbox_map_mail_index_record rec; uint16_t refcount; const struct mdbox_map_file_msg *msgs; unsigned int i, count; int ret; if (mdbox_map_atomic_lock(ctx->atomic, "purging check") < 0) return -1; msgs = array_get(msgs_arr, &count); for (i = 0; i < count; i++) { if (msgs[i].refcount != 0) continue; ret = mdbox_map_lookup_full(map, msgs[i].map_uid, &rec, &refcount); if (ret <= 0) { if (ret < 0) return -1; mdbox_map_set_corrupted(map, "Purging unexpectedly lost map_uid=%u", msgs[i].map_uid); return -1; } if (refcount > 0) return 0; } return 1; } static int mdbox_purge_attachments(struct mdbox_purge_context *ctx, const ARRAY_TYPE(mail_attachment_extref) *extrefs_arr) { struct dbox_storage *storage = &ctx->storage->storage; const struct mail_attachment_extref *extref; int ret = 0; array_foreach(extrefs_arr, extref) { if (index_attachment_delete(&storage->storage, storage->attachment_fs, extref->path) < 0) ret = -1; } return ret; } static int mdbox_file_purge(struct mdbox_purge_context *ctx, struct dbox_file *file, uint32_t file_id) { struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage; struct stat st; ARRAY_TYPE(mdbox_map_file_msg) msgs_arr; const struct mdbox_map_file_msg *msgs; ARRAY_TYPE(seq_range) expunged_map_uids; ARRAY_TYPE(uint32_t) copied_map_uids; ARRAY_TYPE(mail_attachment_extref) ext_refs; pool_t ext_refs_pool; unsigned int i, count; uoff_t offset; int ret; i_assert(ctx->atomic == NULL); i_assert(ctx->append_ctx == NULL); if ((ret = dbox_file_try_lock(file)) <= 0) return ret; /* make sure the file still exists. another process may have already deleted it. */ if (stat(file->cur_path, &st) < 0) { dbox_file_unlock(file); if (errno == ENOENT) return 0; mail_storage_set_critical(&file->storage->storage, "stat(%s) failed: %m", file->cur_path); return -1; } /* get list of map UIDs that exist in this file (again has to be done after locking) */ i_array_init(&msgs_arr, 128); if (mdbox_map_get_file_msgs(dstorage->map, file_id, &msgs_arr) < 0) { array_free(&msgs_arr); dbox_file_unlock(file); return -1; } /* sort messages by their offset */ array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp); ext_refs_pool = pool_alloconly_create("mdbox purge ext refs", 1024); ctx->atomic = mdbox_map_atomic_begin(ctx->storage->map); msgs = array_get(&msgs_arr, &count); i_array_init(&ext_refs, 32); i_array_init(&copied_map_uids, I_MIN(count, 1)); i_array_init(&expunged_map_uids, I_MIN(count, 1)); offset = file->file_header_size; for (i = 0; i < count; i++) { if ((ret = dbox_file_seek(file, offset)) <= 0) break; if (msgs[i].offset != offset) { /* map doesn't match file's actual contents */ dbox_file_set_corrupted(file, "purging found mismatched offsets " "(%"PRIuUOFF_T" vs %u, %u/%u)", offset, msgs[i].offset, i, count); ret = 0; break; } if (msgs[i].refcount == 0) { /* skip over expunged message */ i_stream_seek(file->input, offset + file->msg_header_size + file->cur_physical_size); /* skip metadata */ ret = mdbox_metadata_get_extrefs(file, ext_refs_pool, &ext_refs); if (ret <= 0) break; seq_range_array_add(&expunged_map_uids, msgs[i].map_uid); } else { /* non-expunged message. write it to output file. */ i_stream_seek(file->input, offset); ret = mdbox_purge_save_msg(ctx, file, &msgs[i]); if (ret <= 0) break; array_append(&copied_map_uids, &msgs[i].map_uid, 1); } offset = file->input->v_offset; } if (offset != (uoff_t)st.st_size && ret > 0) { /* file has more messages than what map tells us */ dbox_file_set_corrupted(file, "more messages available than in map " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size); ret = 0; } if (ret > 0 && ctx->append_ctx != NULL) { /* flush writes before locking the map */ if (mdbox_map_append_flush(ctx->append_ctx) < 0) ret = -1; } if (ret <= 0) ret = -1; else { /* it's possible that one of the messages we purged was just copied to another mailbox. the only way to prevent that would be to keep map locked during the purge, but that could keep it locked for too long. instead we'll check here if there are such copies, and if there are cancel this file's purge. */ ret = mdbox_file_purge_check_refcounts(ctx, &msgs_arr); } array_free(&msgs_arr); msgs = NULL; if (ret <= 0) { /* failed */ } else if (ctx->append_ctx == NULL) { /* everything purged from this file */ ret = 1; } else { /* assign new file_id + offset to moved messages */ if (mdbox_map_append_move(ctx->append_ctx, &copied_map_uids, &expunged_map_uids) < 0 || mdbox_map_append_commit(ctx->append_ctx) < 0) ret = -1; else ret = 1; } if (ctx->append_ctx != NULL) mdbox_map_append_free(&ctx->append_ctx); (void)mdbox_map_atomic_finish(&ctx->atomic); /* unlink only after unlocking map, so readers don't see it temporarily vanished */ if (ret > 0) { (void)dbox_file_unlink(file); if (mdbox_map_remove_file_id(ctx->storage->map, file_id) < 0) ret = -1; } else { dbox_file_unlock(file); } array_free(&copied_map_uids); array_free(&expunged_map_uids); (void)mdbox_purge_attachments(ctx, &ext_refs); array_free(&ext_refs); pool_unref(&ext_refs_pool); return ret; } void mdbox_purge_alt_flag_change(struct mail *mail, bool move_to_alt) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->box; ARRAY_TYPE(uint32_t) *dest; uint32_t map_uid; /* we'll assume here that alt flag won't be changed multiple times for the same mail. it shouldn't happen with current code, and checking for it would just slow down the code. so the way it works currently is just that map_uids are added to an array, which is later sorted and processed further. note that it's still possible that the same map_uid exists in the array multiple times. */ if (mdbox_mail_lookup(mbox, mbox->box.view, mail->seq, &map_uid) < 0) return; dest = move_to_alt ? &mbox->storage->move_to_alt_map_uids : &mbox->storage->move_from_alt_map_uids; if (!array_is_created(dest)) i_array_init(dest, 256); array_append(dest, &map_uid, 1); } static struct mdbox_purge_context * mdbox_purge_alloc(struct mdbox_storage *storage) { struct mdbox_purge_context *ctx; pool_t pool; pool = pool_alloconly_create("mdbox purge context", 1024*32); ctx = p_new(pool, struct mdbox_purge_context, 1); ctx->pool = pool; ctx->storage = storage; ctx->lowest_primary_file_id = (uint32_t)-1; i_array_init(&ctx->primary_file_ids, 64); i_array_init(&ctx->purge_file_ids, 64); hash_table_create_direct(&ctx->altmoves, pool, 0); return ctx; } static void mdbox_purge_free(struct mdbox_purge_context **_ctx) { struct mdbox_purge_context *ctx = *_ctx; *_ctx = NULL; hash_table_destroy(&ctx->altmoves); array_free(&ctx->primary_file_ids); array_free(&ctx->purge_file_ids); pool_unref(&ctx->pool); } static int mdbox_purge_get_primary_files(struct mdbox_purge_context *ctx) { struct mdbox_storage *dstorage = ctx->storage; struct mail_storage *storage = &dstorage->storage.storage; DIR *dir; struct dirent *d; string_t *path; unsigned int file_id; size_t dir_len; int ret = 0; if (!array_is_created(&dstorage->move_to_alt_map_uids) && !array_is_created(&dstorage->move_from_alt_map_uids)) { /* we don't need to do alt moving, don't bother getting list of primary files */ return 0; } dir = opendir(dstorage->storage_dir); if (dir == NULL) { if (errno == ENOENT) { /* no storage directory at all yet */ return 0; } mail_storage_set_critical(storage, "opendir(%s) failed: %m", dstorage->storage_dir); return -1; } path = t_str_new(256); str_append(path, dstorage->storage_dir); str_append_c(path, '/'); dir_len = str_len(path); for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) { if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX, strlen(MDBOX_MAIL_FILE_PREFIX)) != 0) continue; if (str_to_uint32(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX), &file_id) < 0) continue; str_truncate(path, dir_len); str_append(path, d->d_name); seq_range_array_add(&ctx->primary_file_ids, file_id); } if (array_count(&ctx->primary_file_ids) > 0) { const struct seq_range *range = array_idx(&ctx->primary_file_ids, 0); ctx->lowest_primary_file_id = range[0].seq1; } if (errno != 0) { mail_storage_set_critical(storage, "readdir(%s) failed: %m", dstorage->storage_dir); ret = -1; } if (closedir(dir) < 0) { mail_storage_set_critical(storage, "closedir(%s) failed: %m", dstorage->storage_dir); ret = -1; } return ret; } static int uint32_t_cmp(const uint32_t *u1, const uint32_t *u2) { if (*u1 < *u2) return -1; if (*u1 > *u2) return 1; return 0; } static int mdbox_altmove_add_files(struct mdbox_purge_context *ctx) { struct mdbox_storage *dstorage = ctx->storage; const uint32_t *map_uids; unsigned int i, count, alt_refcount = 0; struct mdbox_map_mail_index_record cur_rec; enum mdbox_msg_action action; uint32_t cur_map_uid; uint16_t cur_refcount = 0; uoff_t offset; int ret = 0; /* first add move-to-alt actions */ if (array_is_created(&dstorage->move_to_alt_map_uids)) { array_sort(&dstorage->move_to_alt_map_uids, uint32_t_cmp); map_uids = array_get(&dstorage->move_to_alt_map_uids, &count); } else { map_uids = NULL; count = 0; } cur_map_uid = 0; for (i = 0; i < count; i++) { if (cur_map_uid != map_uids[i]) { cur_map_uid = map_uids[i]; if (mdbox_map_lookup_full(dstorage->map, cur_map_uid, &cur_rec, &cur_refcount) < 0) { cur_refcount = (uint16_t)-1; ret = -1; } alt_refcount = 1; } else { alt_refcount++; } if (alt_refcount == cur_refcount && seq_range_exists(&ctx->primary_file_ids, cur_rec.file_id)) { /* all instances marked as moved to alt storage */ action = MDBOX_MSG_ACTION_MOVE_TO_ALT; hash_table_insert(ctx->altmoves, POINTER_CAST(cur_map_uid), POINTER_CAST(action)); seq_range_array_add(&ctx->purge_file_ids, cur_rec.file_id); } } /* next add move-from-alt actions. they override move-to-alt actions in case there happen to be any conflicts (shouldn't). only a single move-from-alt record is needed to do the move. */ if (array_is_created(&dstorage->move_from_alt_map_uids)) map_uids = array_get(&dstorage->move_from_alt_map_uids, &count); else { map_uids = NULL; count = 0; } cur_map_uid = 0; for (i = 0; i < count; i++) { if (cur_map_uid == map_uids[i]) continue; cur_map_uid = map_uids[i]; if (mdbox_map_lookup(dstorage->map, cur_map_uid, &cur_rec.file_id, &offset) < 0) { ret = -1; continue; } if (seq_range_exists(&ctx->primary_file_ids, cur_rec.file_id)) { /* already in primary storage */ continue; } action = MDBOX_MSG_ACTION_MOVE_FROM_ALT; hash_table_insert(ctx->altmoves, POINTER_CAST(cur_map_uid), POINTER_CAST(action)); seq_range_array_add(&ctx->purge_file_ids, cur_rec.file_id); } ctx->have_altmoves = hash_table_count(ctx->altmoves) > 0; return ret; } int mdbox_purge(struct mail_storage *_storage) { struct mdbox_storage *storage = (struct mdbox_storage *)_storage; struct mdbox_purge_context *ctx; struct dbox_file *file; struct seq_range_iter iter; unsigned int i = 0; uint32_t file_id; bool deleted; int ret; ctx = mdbox_purge_alloc(storage); ret = mdbox_map_get_zero_ref_files(storage->map, &ctx->purge_file_ids); if (storage->alt_storage_dir != NULL) { if (mdbox_purge_get_primary_files(ctx) < 0) ret = -1; else { /* add files that can be altmoved */ if (mdbox_altmove_add_files(ctx) < 0) ret = -1; } } seq_range_array_iter_init(&iter, &ctx->purge_file_ids); i = 0; while (ret == 0 && seq_range_array_iter_nth(&iter, i++, &file_id)) T_BEGIN { file = mdbox_file_init(storage, file_id); if (dbox_file_open(file, &deleted) > 0 && !deleted) { if (mdbox_file_purge(ctx, file, file_id) < 0) ret = -1; } else { if (mdbox_map_remove_file_id(storage->map, file_id) < 0) ret = -1; } dbox_file_unref(&file); } T_END; mdbox_purge_free(&ctx); if (storage->corrupted) { /* purging found corrupted files */ (void)mdbox_storage_rebuild(storage); ret = -1; } return ret; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-storage-rebuild.h0000644000175000017500000000043413123174404023067 00000000000000#ifndef MDBOX_STORAGE_REBUILD_H #define MDBOX_STORAGE_REBUILD_H struct mdbox_map_atomic_context; int mdbox_storage_rebuild_in_context(struct mdbox_storage *storage, struct mdbox_map_atomic_context *atomic); int mdbox_storage_rebuild(struct mdbox_storage *storage); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-settings.c0000644000175000017500000000224613165463624021650 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "mdbox-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mdbox_settings, name), NULL } static const struct setting_define mdbox_setting_defines[] = { DEF(SET_BOOL, mdbox_preallocate_space), DEF(SET_BOOL, mdbox_purge_preserve_alt), DEF(SET_SIZE, mdbox_rotate_size), DEF(SET_TIME, mdbox_rotate_interval), SETTING_DEFINE_LIST_END }; static const struct mdbox_settings mdbox_default_settings = { .mdbox_preallocate_space = FALSE, .mdbox_purge_preserve_alt = FALSE, .mdbox_rotate_size = 2*1024*1024, .mdbox_rotate_interval = 0 }; static const struct setting_parser_info mdbox_setting_parser_info = { .module_name = "mdbox", .defines = mdbox_setting_defines, .defaults = &mdbox_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mdbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; const struct setting_parser_info *mdbox_get_setting_parser_info(void) { return &mdbox_setting_parser_info; } dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-file.h0000644000175000017500000000133013123174404020712 00000000000000#ifndef MDBOX_FILE_H #define MDBOX_FILE_H #include "dbox-file.h" struct mdbox_file { struct dbox_file file; struct mdbox_storage *storage; uint32_t file_id; time_t close_time; }; struct dbox_file * mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id); struct dbox_file * mdbox_file_init_new_alt(struct mdbox_storage *storage); /* Assign file ID for a newly created file. */ int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id); void mdbox_file_unrefed(struct dbox_file *file); int mdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents); void mdbox_files_free(struct mdbox_storage *storage); void mdbox_files_sync_input(struct mdbox_storage *storage); #endif dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/mdbox-storage.c0000644000175000017500000003435613165463624021463 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "mkdir-parents.h" #include "master-service.h" #include "mail-index-modseq.h" #include "mail-index-alloc-cache.h" #include "mailbox-log.h" #include "mailbox-list-private.h" #include "index-pop3-uidl.h" #include "dbox-mail.h" #include "dbox-save.h" #include "mdbox-map.h" #include "mdbox-file.h" #include "mdbox-sync.h" #include "mdbox-storage-rebuild.h" #include "mdbox-storage.h" extern struct mail_storage mdbox_storage; extern struct mailbox mdbox_mailbox; static struct mail_storage *mdbox_storage_alloc(void) { struct mdbox_storage *storage; pool_t pool; pool = pool_alloconly_create("mdbox storage", 2048); storage = p_new(pool, struct mdbox_storage, 1); storage->storage.v = mdbox_dbox_storage_vfuncs; storage->storage.storage = mdbox_storage; storage->storage.storage.pool = pool; return &storage->storage.storage; } int mdbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct mdbox_storage *storage = (struct mdbox_storage *)_storage; const char *dir; storage->set = mail_namespace_get_driver_settings(ns, _storage); storage->preallocate_space = storage->set->mdbox_preallocate_space; if (*ns->list->set.mailbox_dir_name == '\0') { *error_r = "mdbox: MAILBOXDIR must not be empty"; return -1; } _storage->unique_root_dir = p_strdup(_storage->pool, ns->list->set.root_dir); dir = mailbox_list_get_root_forced(ns->list, MAILBOX_LIST_PATH_TYPE_DIR); storage->storage_dir = p_strconcat(_storage->pool, dir, "/"MDBOX_GLOBAL_DIR_NAME, NULL); if (ns->list->set.alt_dir != NULL) { storage->alt_storage_dir = p_strconcat(_storage->pool, ns->list->set.alt_dir, "/"MDBOX_GLOBAL_DIR_NAME, NULL); } i_array_init(&storage->open_files, 64); storage->map = mdbox_map_init(storage, ns->list); return dbox_storage_create(_storage, ns, error_r); } void mdbox_storage_destroy(struct mail_storage *_storage) { struct mdbox_storage *storage = (struct mdbox_storage *)_storage; mdbox_files_free(storage); mdbox_map_deinit(&storage->map); if (storage->to_close_unused_files != NULL) timeout_remove(&storage->to_close_unused_files); if (array_is_created(&storage->move_from_alt_map_uids)) array_free(&storage->move_from_alt_map_uids); if (array_is_created(&storage->move_to_alt_map_uids)) array_free(&storage->move_to_alt_map_uids); array_free(&storage->open_files); dbox_storage_destroy(_storage); } static const char * mdbox_storage_find_root_dir(const struct mail_namespace *ns) { bool debug = ns->mail_set->mail_debug; const char *home, *path; if (ns->owner != NULL && mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/mdbox", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug) i_debug("mdbox: root exists (%s)", path); return path; } if (debug) i_debug("mdbox: access(%s, rwx): failed: %m", path); } return NULL; } static bool mdbox_storage_autodetect(const struct mail_namespace *ns, struct mailbox_list_settings *set) { bool debug = ns->mail_set->mail_debug; struct stat st; const char *path, *root_dir; if (set->root_dir != NULL) root_dir = set->root_dir; else { root_dir = mdbox_storage_find_root_dir(ns); if (root_dir == NULL) { if (debug) i_debug("mdbox: couldn't find root dir"); return FALSE; } } path = t_strconcat(root_dir, "/"MDBOX_GLOBAL_DIR_NAME, NULL); if (stat(path, &st) < 0) { if (debug) i_debug("mdbox autodetect: stat(%s) failed: %m", path); return FALSE; } if (!S_ISDIR(st.st_mode)) { if (debug) i_debug("mdbox autodetect: %s not a directory", path); return FALSE; } set->root_dir = root_dir; dbox_storage_get_list_settings(ns, set); return TRUE; } static struct mailbox * mdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct mdbox_mailbox *mbox; struct index_mailbox_context *ibox; pool_t pool; /* dbox can't work without index files */ flags &= ~MAILBOX_FLAG_NO_INDEX_FILES; pool = pool_alloconly_create("mdbox mailbox", 1024*3); mbox = p_new(pool, struct mdbox_mailbox, 1); mbox->box = mdbox_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &mdbox_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); ibox = INDEX_STORAGE_CONTEXT(&mbox->box); ibox->index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS | MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY; mbox->storage = (struct mdbox_storage *)storage; return &mbox->box; } int mdbox_mailbox_open(struct mailbox *box) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; time_t path_ctime; if (dbox_mailbox_check_existence(box, &path_ctime) < 0) return -1; if (dbox_mailbox_open(box, path_ctime) < 0) return -1; mbox->ext_id = mail_index_ext_register(mbox->box.index, "mdbox", 0, sizeof(struct mdbox_mail_index_record), sizeof(uint32_t)); mbox->hdr_ext_id = mail_index_ext_register(mbox->box.index, "mdbox-hdr", sizeof(struct mdbox_index_header), 0, 0); mbox->guid_ext_id = mail_index_ext_register(mbox->box.index, "guid", 0, GUID_128_SIZE, 1); return 0; } static void mdbox_mailbox_close(struct mailbox *box) { struct mdbox_storage *mstorage = (struct mdbox_storage *)box->storage; if (mstorage->corrupted && !mstorage->rebuilding_storage) (void)mdbox_storage_rebuild(mstorage); index_storage_mailbox_close(box); } int mdbox_read_header(struct mdbox_mailbox *mbox, struct mdbox_index_header *hdr, bool *need_resize_r) { const void *data; size_t data_size; i_assert(mbox->box.opened); mail_index_get_header_ext(mbox->box.view, mbox->hdr_ext_id, &data, &data_size); if (data_size < MDBOX_INDEX_HEADER_MIN_SIZE && (!mbox->creating || data_size != 0)) { mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: Invalid dbox header size: %"PRIuSIZE_T, mailbox_get_path(&mbox->box), data_size); mdbox_storage_set_corrupted(mbox->storage); return -1; } i_zero(hdr); if (data_size > 0) memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr))); *need_resize_r = data_size < sizeof(*hdr); return 0; } void mdbox_update_header(struct mdbox_mailbox *mbox, struct mail_index_transaction *trans, const struct mailbox_update *update) { struct mdbox_index_header hdr, new_hdr; bool need_resize; if (mdbox_read_header(mbox, &hdr, &need_resize) < 0) { i_zero(&hdr); need_resize = TRUE; } new_hdr = hdr; if (update != NULL && !guid_128_is_empty(update->mailbox_guid)) { memcpy(new_hdr.mailbox_guid, update->mailbox_guid, sizeof(new_hdr.mailbox_guid)); } else if (guid_128_is_empty(new_hdr.mailbox_guid)) { guid_128_generate(new_hdr.mailbox_guid); } new_hdr.map_uid_validity = mdbox_map_get_uid_validity(mbox->storage->map); if (need_resize) { mail_index_ext_resize_hdr(trans, mbox->hdr_ext_id, sizeof(new_hdr)); } if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) { mail_index_update_header_ext(trans, mbox->hdr_ext_id, 0, &new_hdr, sizeof(new_hdr)); } } static int ATTR_NULL(2, 3) mdbox_write_index_header(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; struct mail_index_transaction *new_trans = NULL; struct mail_index_view *view; const struct mail_index_header *hdr; uint32_t uid_validity, uid_next; if (mdbox_map_open_or_create(mbox->storage->map) < 0) return -1; if (trans == NULL) { new_trans = mail_index_transaction_begin(box->view, 0); trans = new_trans; } view = mail_index_view_open(box->index); hdr = mail_index_get_header(view); uid_validity = hdr->uid_validity; if (update != NULL && update->uid_validity != 0) uid_validity = update->uid_validity; else if (uid_validity == 0) { /* set uidvalidity */ uid_validity = dbox_get_uidvalidity_next(box->list); } if (hdr->uid_validity != uid_validity) { mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); } if (update != NULL && hdr->next_uid < update->min_next_uid) { uid_next = update->min_next_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, next_uid), &uid_next, sizeof(uid_next), TRUE); } if (update != NULL && update->min_first_recent_uid != 0 && hdr->first_recent_uid < update->min_first_recent_uid) { uint32_t first_recent_uid = update->min_first_recent_uid; mail_index_update_header(trans, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } if (update != NULL && update->min_highest_modseq != 0 && mail_index_modseq_get_highest(view) < update->min_highest_modseq) { mail_index_modseq_enable(box->index); mail_index_update_highest_modseq(trans, update->min_highest_modseq); } mail_index_view_close(&view); if (box->inbox_user && box->creating) { /* initialize pop3-uidl header when creating mailbox (not on mailbox_update()) */ index_pop3_uidl_set_max_uid(box, trans, 0); } mdbox_update_header(mbox, trans, update); if (new_trans != NULL) { if (mail_index_transaction_commit(&new_trans) < 0) { mailbox_set_index_error(box); return -1; } } return 0; } int mdbox_mailbox_create_indexes(struct mailbox *box, const struct mailbox_update *update, struct mail_index_transaction *trans) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; int ret; mbox->creating = TRUE; ret = mdbox_write_index_header(box, update, trans); mbox->creating = FALSE; return ret; } void mdbox_storage_set_corrupted(struct mdbox_storage *storage) { if (storage->corrupted) { /* already set it corrupted (possibly recursing back here) */ return; } storage->corrupted = TRUE; storage->corrupted_rebuild_count = (uint32_t)-1; if (mdbox_map_open(storage->map) > 0 && mdbox_map_refresh(storage->map) == 0) { storage->corrupted_rebuild_count = mdbox_map_get_rebuild_count(storage->map); } } static const char * mdbox_get_attachment_path_suffix(struct dbox_file *file ATTR_UNUSED) { return ""; } void mdbox_set_mailbox_corrupted(struct mailbox *box) { struct mdbox_storage *mstorage = (struct mdbox_storage *)box->storage; mdbox_storage_set_corrupted(mstorage); } void mdbox_set_file_corrupted(struct dbox_file *file) { struct mdbox_storage *mstorage = (struct mdbox_storage *)file->storage; mdbox_storage_set_corrupted(mstorage); } static int mdbox_mailbox_get_guid(struct mdbox_mailbox *mbox, guid_128_t guid_r) { const struct mail_index_header *idx_hdr; struct mdbox_index_header hdr; bool need_resize; int ret = 0; i_assert(!mbox->creating); /* there's a race condition between mkdir and getting the mailbox GUID. normally this is handled by mdbox syncing, but GUID can be looked up without syncing. when we detect this situation we'll try to finish creating the indexes first, which usually means just waiting for the sync lock to get unlocked by the other process creating them. */ idx_hdr = mail_index_get_header(mbox->box.view); if (idx_hdr->uid_validity == 0 && idx_hdr->next_uid == 1) { if (dbox_mailbox_create_indexes(&mbox->box, NULL) < 0) return -1; } if (mdbox_read_header(mbox, &hdr, &need_resize) < 0) i_zero(&hdr); if (guid_128_is_empty(hdr.mailbox_guid)) { /* regenerate it */ if (mdbox_write_index_header(&mbox->box, NULL, NULL) < 0 || mdbox_read_header(mbox, &hdr, &need_resize) < 0) ret = -1; } if (ret == 0) memcpy(guid_r, hdr.mailbox_guid, GUID_128_SIZE); return ret; } static int mdbox_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; if ((items & MAILBOX_METADATA_GUID) != 0) { if (mdbox_mailbox_get_guid(mbox, metadata_r->guid) < 0) return -1; } return 0; } static int mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { if (!box->opened) { if (mailbox_open(box) < 0) return -1; } if (mdbox_write_index_header(box, update, NULL) < 0) return -1; return index_storage_mailbox_update_common(box, update); } struct mail_storage mdbox_storage = { .name = MDBOX_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS | MAIL_STORAGE_CLASS_FLAG_BINARY_DATA, .v = { mdbox_get_setting_parser_info, mdbox_storage_alloc, mdbox_storage_create, mdbox_storage_destroy, NULL, dbox_storage_get_list_settings, mdbox_storage_autodetect, mdbox_mailbox_alloc, mdbox_purge, NULL, } }; struct mailbox mdbox_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, mdbox_mailbox_open, mdbox_mailbox_close, index_storage_mailbox_free, dbox_mailbox_create, mdbox_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, mdbox_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, index_storage_list_index_has_changed, index_storage_list_index_update_sync, mdbox_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, dbox_notify_changes, index_transaction_begin, index_transaction_commit, index_transaction_rollback, NULL, dbox_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, mdbox_save_alloc, mdbox_save_begin, dbox_save_continue, mdbox_save_finish, mdbox_save_cancel, mdbox_copy, mdbox_transaction_save_commit_pre, mdbox_transaction_save_commit_post, mdbox_transaction_save_rollback, index_storage_is_inconsistent } }; struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs = { mdbox_file_unrefed, mdbox_file_create_fd, mdbox_mail_open, mdbox_mailbox_create_indexes, mdbox_get_attachment_path_suffix, mdbox_set_mailbox_corrupted, mdbox_set_file_corrupted }; dovecot-2.2.33.2/src/lib-storage/index/dbox-multi/Makefile.am0000644000175000017500000000146413123174404020557 00000000000000noinst_LTLIBRARIES = libstorage_dbox_multi.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common libstorage_dbox_multi_la_SOURCES = \ mdbox-deleted-storage.c \ mdbox-file.c \ mdbox-mail.c \ mdbox-map.c \ mdbox-purge.c \ mdbox-save.c \ mdbox-settings.c \ mdbox-sync.c \ mdbox-storage.c \ mdbox-storage-rebuild.c headers = \ mdbox-file.h \ mdbox-map.h \ mdbox-map-private.h \ mdbox-settings.h \ mdbox-storage.h \ mdbox-storage-rebuild.h \ mdbox-sync.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-sync-search.c0000644000175000017500000000515613123174404020131 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "seq-range-array.h" #include "mail-search.h" #include "mailbox-search-result-private.h" #include "index-search-result.h" #include "index-sync-private.h" static bool search_result_want_flag_updates(const struct mail_search_result *result) { if (!result->args_have_flags && !result->args_have_keywords && !result->args_have_modseq) { /* search result doesn't care about flag changes */ return FALSE; } return TRUE; } static void index_sync_uidify_array(struct index_mailbox_sync_context *ctx, const ARRAY_TYPE(seq_range) *changes) { const struct seq_range *range; uint32_t seq, uid; array_foreach(changes, range) { for (seq = range->seq1; seq <= range->seq2; seq++) { mail_index_lookup_uid(ctx->ctx.box->view, seq, &uid); seq_range_array_add(&ctx->all_flag_update_uids, uid); } } } static void index_sync_uidify(struct index_mailbox_sync_context *ctx) { unsigned int count; count = array_count(&ctx->flag_updates) + array_count(&ctx->hidden_updates); i_array_init(&ctx->all_flag_update_uids, count*2); index_sync_uidify_array(ctx, &ctx->flag_updates); index_sync_uidify_array(ctx, &ctx->hidden_updates); } void index_sync_search_results_uidify(struct index_mailbox_sync_context *ctx) { struct mail_search_result *const *results; unsigned int i, count; i_assert(!array_is_created(&ctx->all_flag_update_uids)); results = array_get(&ctx->ctx.box->search_results, &count); for (i = 0; i < count; i++) { if ((results[i]->flags & MAILBOX_SEARCH_RESULT_FLAG_UPDATE) != 0 && search_result_want_flag_updates(results[i])) { index_sync_uidify(ctx); break; } } } static void search_result_update(struct index_mailbox_sync_context *ctx, struct mail_search_result *result) { if ((result->flags & MAILBOX_SEARCH_RESULT_FLAG_UPDATE) == 0) { /* not an updateable search result */ return; } if (search_result_want_flag_updates(result)) { (void)index_search_result_update_flags(result, &ctx->all_flag_update_uids); } (void)index_search_result_update_appends(result, ctx->messages_count); } void index_sync_search_results_update(struct index_mailbox_sync_context *ctx) { struct mail_search_result *const *results; unsigned int i, count; results = array_get(&ctx->ctx.box->search_results, &count); for (i = 0; i < count; i++) search_result_update(ctx, results[i]); } void index_sync_search_results_expunge(struct index_mailbox_sync_context *ctx) { if (ctx->expunges != NULL) { index_search_results_update_expunges(ctx->ctx.box, ctx->expunges); } } dovecot-2.2.33.2/src/lib-storage/index/index-mail.h0000644000175000017500000002357513165463624016661 00000000000000#ifndef INDEX_MAIL_H #define INDEX_MAIL_H #include "message-size.h" #include "mail-cache.h" #include "mail-storage-private.h" enum index_cache_field { /* fixed size fields */ MAIL_CACHE_FLAGS = 0, MAIL_CACHE_SENT_DATE, MAIL_CACHE_RECEIVED_DATE, MAIL_CACHE_SAVE_DATE, MAIL_CACHE_VIRTUAL_FULL_SIZE, MAIL_CACHE_PHYSICAL_FULL_SIZE, /* variable sized field */ MAIL_CACHE_IMAP_BODY, MAIL_CACHE_IMAP_BODYSTRUCTURE, MAIL_CACHE_IMAP_ENVELOPE, MAIL_CACHE_POP3_UIDL, MAIL_CACHE_POP3_ORDER, MAIL_CACHE_GUID, MAIL_CACHE_MESSAGE_PARTS, MAIL_CACHE_BINARY_PARTS, MAIL_CACHE_BODY_SNIPPET, MAIL_INDEX_CACHE_FIELD_COUNT }; extern struct mail_cache_field global_cache_fields[MAIL_INDEX_CACHE_FIELD_COUNT]; #define IMAP_BODY_PLAIN_7BIT_ASCII \ "\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\"" enum mail_cache_record_flag { /* If binary flags are set, it's not checked whether mail is missing CRs. So this flag may be set as an optimization for regular non-binary mails as well if it's known that it contains valid CR+LF line breaks. */ MAIL_CACHE_FLAG_BINARY_HEADER = 0x0001, MAIL_CACHE_FLAG_BINARY_BODY = 0x0002, /* Mail header or body is known to contain NUL characters. */ MAIL_CACHE_FLAG_HAS_NULS = 0x0004, /* Mail header or body is known to not contain NUL characters. */ MAIL_CACHE_FLAG_HAS_NO_NULS = 0x0020, /* obsolete _HAS_NO_NULS flag, which was being set incorrectly */ MAIL_CACHE_FLAG_HAS_NO_NULS_BROKEN = 0x0008, /* BODY is IMAP_BODY_PLAIN_7BIT_ASCII and rest of BODYSTRUCTURE fields are NIL */ MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII = 0x0010 }; enum index_mail_access_part { READ_HDR = 0x01, READ_BODY = 0x02, PARSE_HDR = 0x04, PARSE_BODY = 0x08 }; struct mail_sent_date { uint32_t time; int32_t timezone; }; struct index_mail_line { unsigned int field_idx; uint32_t start_pos, end_pos; uint32_t line_num; }; struct message_header_line; struct index_mail_data { time_t date, received_date, save_date; uoff_t virtual_size, physical_size; struct mail_sent_date sent_date; struct index_mail_line parse_line; uint32_t parse_line_num; struct message_part *parts; struct message_binary_part *bin_parts; const char *envelope, *body, *bodystructure, *guid, *filename; const char *from_envelope, *body_snippet; struct message_part_envelope *envelope_data; uint32_t seq; uint32_t cache_flags; uint64_t modseq, pvt_modseq; enum index_mail_access_part access_part; /* dont_cache_fields overrides cache_fields */ enum mail_fetch_field cache_fetch_fields, dont_cache_fetch_fields; unsigned int dont_cache_field_idx; enum mail_fetch_field wanted_fields; struct mailbox_header_lookup_ctx *wanted_headers; buffer_t *search_results; struct istream *stream, *filter_stream; struct tee_istream *tee_stream; struct message_size hdr_size, body_size; struct istream *parser_input; struct message_parser_ctx *parser_ctx; int parsing_count; ARRAY_TYPE(keywords) keywords; ARRAY_TYPE(keyword_indexes) keyword_indexes; unsigned int initialized:1; unsigned int save_sent_date:1; unsigned int sent_date_parsed:1; unsigned int save_envelope:1; unsigned int save_bodystructure_header:1; unsigned int save_bodystructure_body:1; unsigned int save_message_parts:1; unsigned int save_body_snippet:1; unsigned int stream_has_only_header:1; unsigned int parsed_bodystructure:1; unsigned int hdr_size_set:1; unsigned int body_size_set:1; unsigned int messageparts_saved_to_cache:1; unsigned int header_parsed:1; unsigned int no_caching:1; unsigned int forced_no_caching:1; unsigned int destroying_stream:1; unsigned int initialized_wrapper_stream:1; unsigned int destroy_callback_set:1; unsigned int prefetch_sent:1; unsigned int header_parser_initialized:1; }; struct index_mail { struct mail_private mail; struct index_mail_data data; struct index_mailbox_context *ibox; int pop3_state; /* per-mail variables, here for performance reasons: */ uint32_t header_seq; string_t *header_data; ARRAY(struct index_mail_line) header_lines; #define HEADER_MATCH_FLAG_FOUND 1 #define HEADER_MATCH_SKIP_COUNT 2 #define HEADER_MATCH_USABLE(mail, num) \ ((num & ~1) == (mail)->header_match_value) ARRAY(uint8_t) header_match; ARRAY(unsigned int) header_match_lines; uint8_t header_match_value; unsigned int pop3_state_set:1; /* mail created by mailbox_search_*() */ unsigned int search_mail:1; /* close() is being called from mail_free() */ unsigned int freeing:1; }; struct mail * index_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); void index_mail_init(struct index_mail *mail, struct mailbox_transaction_context *_t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *_wanted_headers); void index_mail_set_seq(struct mail *mail, uint32_t seq, bool saving); bool index_mail_set_uid(struct mail *mail, uint32_t uid); void index_mail_set_uid_cache_updates(struct mail *mail, bool set); bool index_mail_prefetch(struct mail *mail); void index_mail_add_temp_wanted_fields(struct mail *mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers); void index_mail_update_access_parts_pre(struct mail *mail); void index_mail_update_access_parts_post(struct mail *_mail); void index_mail_close(struct mail *mail); void index_mail_close_streams(struct index_mail *mail); void index_mail_free(struct mail *mail); void index_mail_set_message_parts_corrupted(struct mail *mail, const char *error); bool index_mail_want_parse_headers(struct index_mail *mail); void index_mail_parse_header_init(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers) ATTR_NULL(2); void index_mail_parse_header(struct message_part *part, struct message_header_line *hdr, struct index_mail *mail) ATTR_NULL(1); int index_mail_parse_headers(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers, const char *reason) ATTR_NULL(2); int index_mail_headers_get_envelope(struct index_mail *mail); int index_mail_get_first_header(struct mail *_mail, const char *field, bool decode_to_utf8, const char **value_r); int index_mail_get_headers(struct mail *_mail, const char *field, bool decode_to_utf8, const char *const **value_r); int index_mail_get_header_stream(struct mail *_mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r); void index_mail_set_read_buffer_size(struct mail *mail, struct istream *input); enum mail_flags index_mail_get_flags(struct mail *_mail); uint64_t index_mail_get_modseq(struct mail *_mail); uint64_t index_mail_get_pvt_modseq(struct mail *_mail); const char *const *index_mail_get_keywords(struct mail *_mail); const ARRAY_TYPE(keyword_indexes) * index_mail_get_keyword_indexes(struct mail *_mail); int index_mail_get_parts(struct mail *_mail, struct message_part **parts_r); int index_mail_get_received_date(struct mail *_mail, time_t *date_r); int index_mail_get_save_date(struct mail *_mail, time_t *date_r); int index_mail_get_date(struct mail *_mail, time_t *date_r, int *timezone_r); int index_mail_get_virtual_size(struct mail *mail, uoff_t *size_r); int index_mail_get_physical_size(struct mail *mail, uoff_t *size_r); int index_mail_init_stream(struct index_mail *mail, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) ATTR_NULL(2, 3); int index_mail_get_binary_stream(struct mail *_mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *body_lines_r, bool *binary_r, struct istream **stream_r); int index_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r); struct mail *index_mail_get_real_mail(struct mail *mail); void index_mail_update_flags(struct mail *mail, enum modify_type modify_type, enum mail_flags flags); void index_mail_update_keywords(struct mail *mail, enum modify_type modify_type, struct mail_keywords *keywords); void index_mail_update_modseq(struct mail *mail, uint64_t min_modseq); void index_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq); void index_mail_expunge(struct mail *mail); void index_mail_precache(struct mail *mail); void index_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field); void index_mail_set_cache_corrupted_reason(struct mail *mail, enum mail_fetch_field field, const char *reason); int index_mail_opened(struct mail *mail, struct istream **stream); int index_mail_stream_check_failure(struct index_mail *mail); void index_mail_stream_log_failure_for(struct index_mail *mail, struct istream *input); void index_mail_refresh_expunged(struct mail *mail); struct index_mail *index_mail_get_index_mail(struct mail *mail); bool index_mail_get_cached_uoff_t(struct index_mail *mail, enum index_cache_field field, uoff_t *size_r); bool index_mail_get_cached_virtual_size(struct index_mail *mail, uoff_t *size_r); bool index_mail_get_cached_body(struct index_mail *mail, const char **value_r); bool index_mail_get_cached_bodystructure(struct index_mail *mail, const char **value_r); const uint32_t *index_mail_get_vsize_extension(struct mail *_mail); void index_mail_cache_add(struct index_mail *mail, enum index_cache_field field, const void *data, size_t data_size); void index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx, const void *data, size_t data_size); struct istream *index_mail_cache_parse_init(struct mail *mail, struct istream *input); void index_mail_cache_parse_continue(struct mail *mail); void index_mail_cache_parse_deinit(struct mail *mail, time_t received_date, bool success); int index_mail_cache_lookup_field(struct index_mail *mail, buffer_t *buf, unsigned int field_idx); void index_mail_save_finish(struct mail_save_context *ctx); const char *index_mail_cache_reason(struct mail *mail, const char *reason); #endif dovecot-2.2.33.2/src/lib-storage/index/index-mailbox-size.h0000644000175000017500000000133213123174404020312 00000000000000#ifndef INDEX_MAILBOX_SIZE_H #define INDEX_MAILBOX_SIZE_H struct mailbox; struct mailbox_vsize_update * index_mailbox_vsize_update_init(struct mailbox *box); void index_mailbox_vsize_update_deinit(struct mailbox_vsize_update **update); void index_mailbox_vsize_hdr_expunge(struct mailbox_vsize_update *update, uint32_t uid, uoff_t vsize); bool index_mailbox_vsize_update_try_lock(struct mailbox_vsize_update *update); bool index_mailbox_vsize_update_wait_lock(struct mailbox_vsize_update *update); /* Returns TRUE if expunges & appends should be updating the header. */ bool index_mailbox_vsize_want_updates(struct mailbox_vsize_update *update); void index_mailbox_vsize_update_appends(struct mailbox *box); #endif dovecot-2.2.33.2/src/lib-storage/index/index-sort.c0000644000175000017500000004275213123174404016704 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "unichar.h" #include "message-address.h" #include "message-header-decode.h" #include "imap-base-subject.h" #include "index-storage.h" #include "index-sort-private.h" struct mail_sort_node_date { uint32_t seq; time_t date; }; ARRAY_DEFINE_TYPE(mail_sort_node_date, struct mail_sort_node_date); struct mail_sort_node_size { uint32_t seq; uoff_t size; }; ARRAY_DEFINE_TYPE(mail_sort_node_size, struct mail_sort_node_size); struct mail_sort_node_float { uint32_t seq; float num; }; ARRAY_DEFINE_TYPE(mail_sort_node_float, struct mail_sort_node_float); struct sort_cmp_context { struct mail_search_sort_program *program; bool reverse; }; static struct sort_cmp_context static_node_cmp_context; static void index_sort_program_set_mail_failed(struct mail_search_sort_program *program, struct mail *mail) { switch (mailbox_get_last_mail_error(mail->box)) { case MAIL_ERROR_EXPUNGED: break; case MAIL_ERROR_LOOKUP_ABORTED: /* just change the error message */ i_assert(program->slow_mails_left == 0); mail_storage_set_error(program->t->box->storage, MAIL_ERROR_LIMIT, "Requested sort would have taken too long."); /* fall through */ default: program->failed = TRUE; break; } } static time_t index_sort_program_set_date_failed(struct mail_search_sort_program *program, struct mail *mail) { index_sort_program_set_mail_failed(program, mail); if (mailbox_get_last_mail_error(mail->box) == MAIL_ERROR_LIMIT) { /* limit reached - sort the rest of the mails at the end of the list by their UIDs */ return LONG_MAX; } else { /* expunged / some other error - sort in the beginning */ return 0; } } static void index_sort_list_add_arrival(struct mail_search_sort_program *program, struct mail *mail) { ARRAY_TYPE(mail_sort_node_date) *nodes = program->context; struct mail_sort_node_date *node; node = array_append_space(nodes); node->seq = mail->seq; if (mail_get_received_date(mail, &node->date) < 0) node->date = index_sort_program_set_date_failed(program, mail); } static void index_sort_list_add_date(struct mail_search_sort_program *program, struct mail *mail) { ARRAY_TYPE(mail_sort_node_date) *nodes = program->context; struct mail_sort_node_date *node; int tz; node = array_append_space(nodes); node->seq = mail->seq; if (mail_get_date(mail, &node->date, &tz) < 0) node->date = index_sort_program_set_date_failed(program, mail); else if (node->date == 0) { if (mail_get_received_date(mail, &node->date) < 0) node->date = index_sort_program_set_date_failed(program, mail); } } static void index_sort_list_add_size(struct mail_search_sort_program *program, struct mail *mail) { ARRAY_TYPE(mail_sort_node_size) *nodes = program->context; struct mail_sort_node_size *node; node = array_append_space(nodes); node->seq = mail->seq; if (mail_get_virtual_size(mail, &node->size) < 0) { index_sort_program_set_mail_failed(program, mail); node->size = 0; } } static int index_sort_get_pop3_order(struct mail *mail, uoff_t *size_r) { const char *str; if (mail_get_special(mail, MAIL_FETCH_POP3_ORDER, &str) < 0) { *size_r = (uint32_t)-1; return -1; } if (str_to_uoff(str, size_r) < 0) *size_r = (uint32_t)-1; return 0; } static void index_sort_list_add_pop3_order(struct mail_search_sort_program *program, struct mail *mail) { ARRAY_TYPE(mail_sort_node_size) *nodes = program->context; struct mail_sort_node_size *node; node = array_append_space(nodes); node->seq = mail->seq; (void)index_sort_get_pop3_order(mail, &node->size); } static int index_sort_get_relevancy(struct mail *mail, float *result_r) { const char *str; if (mail_get_special(mail, MAIL_FETCH_SEARCH_RELEVANCY, &str) < 0) { *result_r = 0; return -1; } *result_r = strtod(str, NULL); return 0; } static void index_sort_list_add_relevancy(struct mail_search_sort_program *program, struct mail *mail) { ARRAY_TYPE(mail_sort_node_float) *nodes = program->context; struct mail_sort_node_float *node; node = array_append_space(nodes); node->seq = mail->seq; (void)index_sort_get_relevancy(mail, &node->num); } void index_sort_list_add(struct mail_search_sort_program *program, struct mail *mail) { enum mail_access_type orig_access_type = mail->access_type; bool prev_slow = mail->mail_stream_opened || mail->mail_metadata_accessed; i_assert(mail->transaction == program->t); /* if lookup_abort isn't NEVER, mail_sort_max_read_count handling doesn't work right. */ i_assert(mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER); if (program->slow_mails_left == 0) mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; mail->access_type = MAIL_ACCESS_TYPE_SORT; T_BEGIN { program->sort_list_add(program, mail); } T_END; mail->access_type = orig_access_type; if (!prev_slow && (mail->mail_stream_opened || mail->mail_metadata_accessed)) { i_assert(program->slow_mails_left > 0); program->slow_mails_left--; } mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; } static int sort_node_date_cmp(const struct mail_sort_node_date *n1, const struct mail_sort_node_date *n2) { struct sort_cmp_context *ctx = &static_node_cmp_context; if (n1->date < n2->date) return !ctx->reverse ? -1 : 1; if (n1->date > n2->date) return !ctx->reverse ? 1 : -1; return index_sort_node_cmp_type(ctx->program, ctx->program->sort_program + 1, n1->seq, n2->seq); } static void index_sort_list_finish_date(struct mail_search_sort_program *program) { ARRAY_TYPE(mail_sort_node_date) *nodes = program->context; array_sort(nodes, sort_node_date_cmp); memcpy(&program->seqs, nodes, sizeof(program->seqs)); i_free(nodes); program->context = NULL; } static int sort_node_size_cmp(const struct mail_sort_node_size *n1, const struct mail_sort_node_size *n2) { struct sort_cmp_context *ctx = &static_node_cmp_context; if (n1->size < n2->size) return !ctx->reverse ? -1 : 1; if (n1->size > n2->size) return !ctx->reverse ? 1 : -1; return index_sort_node_cmp_type(ctx->program, ctx->program->sort_program + 1, n1->seq, n2->seq); } static void index_sort_list_finish_size(struct mail_search_sort_program *program) { ARRAY_TYPE(mail_sort_node_size) *nodes = program->context; array_sort(nodes, sort_node_size_cmp); memcpy(&program->seqs, nodes, sizeof(program->seqs)); i_free(nodes); program->context = NULL; } static int sort_node_float_cmp(const struct mail_sort_node_float *n1, const struct mail_sort_node_float *n2) { struct sort_cmp_context *ctx = &static_node_cmp_context; if (n1->num < n2->num) return !ctx->reverse ? -1 : 1; if (n1->num > n2->num) return !ctx->reverse ? 1 : -1; return index_sort_node_cmp_type(ctx->program, ctx->program->sort_program + 1, n1->seq, n2->seq); } static void index_sort_list_finish_float(struct mail_search_sort_program *program) { ARRAY_TYPE(mail_sort_node_float) *nodes = program->context; /* NOTE: higher relevancy is returned first, unlike with all other number based sort keys, so temporarily reverse the search */ static_node_cmp_context.reverse = !static_node_cmp_context.reverse; array_sort(nodes, sort_node_float_cmp); static_node_cmp_context.reverse = !static_node_cmp_context.reverse; memcpy(&program->seqs, nodes, sizeof(program->seqs)); i_free(nodes); program->context = NULL; } void index_sort_list_finish(struct mail_search_sort_program *program) { i_zero(&static_node_cmp_context); static_node_cmp_context.program = program; static_node_cmp_context.reverse = (program->sort_program[0] & MAIL_SORT_FLAG_REVERSE) != 0; program->sort_list_finish(program); } bool index_sort_list_next(struct mail_search_sort_program *program, uint32_t *seq_r) { const uint32_t *seqp; if (program->iter_idx == array_count(&program->seqs)) return FALSE; seqp = array_idx(&program->seqs, program->iter_idx++); *seq_r = *seqp; return TRUE; } struct mail_search_sort_program * index_sort_program_init(struct mailbox_transaction_context *t, const enum mail_sort_type *sort_program) { struct mail_search_sort_program *program; unsigned int i; if (sort_program == NULL || sort_program[0] == MAIL_SORT_END) return NULL; /* we support internal sorting by the primary condition */ program = i_new(struct mail_search_sort_program, 1); program->t = t; program->temp_mail = mail_alloc(t, 0, NULL); program->temp_mail->access_type = MAIL_ACCESS_TYPE_SORT; program->slow_mails_left = program->t->box->storage->set->mail_sort_max_read_count; if (program->slow_mails_left == 0) program->slow_mails_left = UINT_MAX; for (i = 0; i < MAX_SORT_PROGRAM_SIZE; i++) { program->sort_program[i] = sort_program[i]; if (sort_program[i] == MAIL_SORT_END) break; } if (i == MAX_SORT_PROGRAM_SIZE) i_panic("index_sort_program_init(): Invalid sort program"); switch (program->sort_program[0] & MAIL_SORT_MASK) { case MAIL_SORT_ARRIVAL: case MAIL_SORT_DATE: { ARRAY_TYPE(mail_sort_node_date) *nodes; nodes = i_malloc(sizeof(*nodes)); i_array_init(nodes, 128); if ((program->sort_program[0] & MAIL_SORT_MASK) == MAIL_SORT_ARRIVAL) program->sort_list_add = index_sort_list_add_arrival; else program->sort_list_add = index_sort_list_add_date; program->sort_list_finish = index_sort_list_finish_date; program->context = nodes; break; } case MAIL_SORT_SIZE: { ARRAY_TYPE(mail_sort_node_size) *nodes; nodes = i_malloc(sizeof(*nodes)); i_array_init(nodes, 128); program->sort_list_add = index_sort_list_add_size; program->sort_list_finish = index_sort_list_finish_size; program->context = nodes; break; } case MAIL_SORT_CC: case MAIL_SORT_FROM: case MAIL_SORT_SUBJECT: case MAIL_SORT_TO: case MAIL_SORT_DISPLAYFROM: case MAIL_SORT_DISPLAYTO: program->sort_list_add = index_sort_list_add_string; program->sort_list_finish = index_sort_list_finish_string; index_sort_list_init_string(program); break; case MAIL_SORT_RELEVANCY: { ARRAY_TYPE(mail_sort_node_float) *nodes; nodes = i_malloc(sizeof(*nodes)); i_array_init(nodes, 128); program->sort_list_add = index_sort_list_add_relevancy; program->sort_list_finish = index_sort_list_finish_float; program->context = nodes; break; } case MAIL_SORT_POP3_ORDER: { ARRAY_TYPE(mail_sort_node_size) *nodes; nodes = i_malloc(sizeof(*nodes)); i_array_init(nodes, 128); program->sort_list_add = index_sort_list_add_pop3_order; program->sort_list_finish = index_sort_list_finish_size; program->context = nodes; break; } default: i_unreached(); } return program; } int index_sort_program_deinit(struct mail_search_sort_program **_program) { struct mail_search_sort_program *program = *_program; *_program = NULL; if (program->context != NULL) index_sort_list_finish(program); mail_free(&program->temp_mail); array_free(&program->seqs); int ret = program->failed ? -1 : 0; i_free(program); return ret; } static int get_first_addr(struct mail *mail, const char *header, struct message_address **addr_r) { const char *str; int ret; if ((ret = mail_get_first_header(mail, header, &str)) <= 0) { *addr_r = NULL; return ret; } *addr_r = message_address_parse(pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, TRUE); return 0; } static int get_first_mailbox(struct mail *mail, const char *header, const char **mailbox_r) { struct message_address *addr; if (get_first_addr(mail, header, &addr) < 0) { *mailbox_r = ""; return -1; } *mailbox_r = addr != NULL && addr->mailbox != NULL ? addr->mailbox : ""; return 0; } static int get_display_name(struct mail *mail, const char *header, const char **name_r) { struct message_address *addr; *name_r = ""; if (get_first_addr(mail, header, &addr) < 0) return -1; if (addr == NULL) return 0; if (addr->name != NULL) { string_t *str; size_t len = strlen(addr->name); str = t_str_new(len*2); (void)message_header_decode_utf8( (const unsigned char *)addr->name, len, str, NULL); if (str_len(str) > 0) { *name_r = str_c(str); return 0; } } if (addr->mailbox != NULL && addr->domain != NULL) *name_r = t_strconcat(addr->mailbox, "@", addr->domain, NULL); else if (addr->mailbox != NULL) *name_r = addr->mailbox; return 0; } static void index_sort_set_seq(struct mail_search_sort_program *program, struct mail *mail, uint32_t seq) { if ((mail->mail_stream_opened || mail->mail_metadata_accessed) && program->slow_mails_left > 0) program->slow_mails_left--; mail_set_seq(mail, seq); if (program->slow_mails_left == 0) { /* too many slow lookups - just return the rest of the results in whatever order. */ mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; } } int index_sort_header_get(struct mail_search_sort_program *program, uint32_t seq, enum mail_sort_type sort_type, string_t *dest) { struct mail *mail = program->temp_mail; const char *str; int ret; bool reply_or_fw; index_sort_set_seq(program, mail, seq); str_truncate(dest, 0); switch (sort_type & MAIL_SORT_MASK) { case MAIL_SORT_SUBJECT: ret = mail_get_first_header(mail, "Subject", &str); if (ret < 0) break; if (ret == 0) { /* nonexistent header */ return 1; } str = imap_get_base_subject_cased(pool_datastack_create(), str, &reply_or_fw); str_append(dest, str); return 1; case MAIL_SORT_CC: ret = get_first_mailbox(mail, "Cc", &str); break; case MAIL_SORT_FROM: ret = get_first_mailbox(mail, "From", &str); break; case MAIL_SORT_TO: ret = get_first_mailbox(mail, "To", &str); break; case MAIL_SORT_DISPLAYFROM: ret = get_display_name(mail, "From", &str); break; case MAIL_SORT_DISPLAYTO: ret = get_display_name(mail, "To", &str); break; default: i_unreached(); } if (ret < 0) { index_sort_program_set_mail_failed(program, mail); if (!program->failed) return 0; return -1; } (void)uni_utf8_to_decomposed_titlecase(str, strlen(str), dest); return 1; } int index_sort_node_cmp_type(struct mail_search_sort_program *program, const enum mail_sort_type *sort_program, uint32_t seq1, uint32_t seq2) { struct mail *mail = program->temp_mail; enum mail_sort_type sort_type; time_t time1, time2; uoff_t size1, size2; float float1, float2; int tz, ret = 0; sort_type = *sort_program & MAIL_SORT_MASK; switch (sort_type) { case MAIL_SORT_CC: case MAIL_SORT_FROM: case MAIL_SORT_TO: case MAIL_SORT_SUBJECT: case MAIL_SORT_DISPLAYFROM: case MAIL_SORT_DISPLAYTO: T_BEGIN { string_t *str1, *str2; str1 = t_str_new(256); str2 = t_str_new(256); if (index_sort_header_get(program, seq1, sort_type, str1) < 0) index_sort_program_set_mail_failed(program, mail); if (index_sort_header_get(program, seq2, sort_type, str2) < 0) index_sort_program_set_mail_failed(program, mail); ret = strcmp(str_c(str1), str_c(str2)); } T_END; break; case MAIL_SORT_ARRIVAL: index_sort_set_seq(program, mail, seq1); if (mail_get_received_date(mail, &time1) < 0) time1 = index_sort_program_set_date_failed(program, mail); index_sort_set_seq(program, mail, seq2); if (mail_get_received_date(mail, &time2) < 0) time2 = index_sort_program_set_date_failed(program, mail); ret = time1 < time2 ? -1 : (time1 > time2 ? 1 : 0); break; case MAIL_SORT_DATE: index_sort_set_seq(program, mail, seq1); if (mail_get_date(mail, &time1, &tz) < 0) time1 = index_sort_program_set_date_failed(program, mail); else if (time1 == 0) { if (mail_get_received_date(mail, &time1) < 0) time1 = index_sort_program_set_date_failed(program, mail); } index_sort_set_seq(program, mail, seq2); if (mail_get_date(mail, &time2, &tz) < 0) time2 = index_sort_program_set_date_failed(program, mail); else if (time2 == 0) { if (mail_get_received_date(mail, &time2) < 0) time2 = index_sort_program_set_date_failed(program, mail); } ret = time1 < time2 ? -1 : (time1 > time2 ? 1 : 0); break; case MAIL_SORT_SIZE: index_sort_set_seq(program, mail, seq1); if (mail_get_virtual_size(mail, &size1) < 0) { index_sort_program_set_mail_failed(program, mail); size1 = 0; } index_sort_set_seq(program, mail, seq2); if (mail_get_virtual_size(mail, &size2) < 0) { index_sort_program_set_mail_failed(program, mail); size2 = 0; } ret = size1 < size2 ? -1 : (size1 > size2 ? 1 : 0); break; case MAIL_SORT_RELEVANCY: index_sort_set_seq(program, mail, seq1); if (index_sort_get_relevancy(mail, &float1) < 0) index_sort_program_set_mail_failed(program, mail); index_sort_set_seq(program, mail, seq2); if (index_sort_get_relevancy(mail, &float2) < 0) index_sort_program_set_mail_failed(program, mail); /* NOTE: higher relevancy is returned first, unlike with all other number based sort keys */ ret = float1 < float2 ? 1 : (float1 > float2 ? -1 : 0); break; case MAIL_SORT_POP3_ORDER: /* 32bit numbers would be enough, but since there is already existing code for uoff_t in sizes, just use them. */ index_sort_set_seq(program, mail, seq1); if (index_sort_get_pop3_order(mail, &size1) < 0) index_sort_program_set_mail_failed(program, mail); index_sort_set_seq(program, mail, seq2); if (index_sort_get_pop3_order(mail, &size2) < 0) index_sort_program_set_mail_failed(program, mail); ret = size1 < size2 ? -1 : (size1 > size2 ? 1 : 0); break; case MAIL_SORT_END: return seq1 < seq2 ? -1 : (seq1 > seq2 ? 1 : 0); case MAIL_SORT_MASK: case MAIL_SORT_FLAG_REVERSE: i_unreached(); } if (ret == 0) { return index_sort_node_cmp_type(program, sort_program+1, seq1, seq2); } if ((*sort_program & MAIL_SORT_FLAG_REVERSE) != 0) ret = ret < 0 ? 1 : -1; return ret; } dovecot-2.2.33.2/src/lib-storage/index/index-mail-headers.c0000644000175000017500000006372713165463624020270 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "array.h" #include "buffer.h" #include "str.h" #include "message-date.h" #include "message-part-data.h" #include "message-parser.h" #include "message-header-decode.h" #include "istream-tee.h" #include "istream-header-filter.h" #include "imap-envelope.h" #include "imap-bodystructure.h" #include "index-storage.h" #include "index-mail.h" static const enum message_header_parser_flags hdr_parser_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR; static const enum message_parser_flags msg_parser_flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK; static int header_line_cmp(const struct index_mail_line *l1, const struct index_mail_line *l2) { int diff; diff = (int)l1->field_idx - (int)l2->field_idx; return diff != 0 ? diff : (int)l1->line_num - (int)l2->line_num; } static void index_mail_parse_header_finish(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const struct index_mail_line *lines; const unsigned char *header, *data; const uint8_t *match; buffer_t *buf; size_t data_size; unsigned int i, j, count, match_idx, match_count; bool noncontiguous; /* sort it first so fields are grouped together and ordered by line number */ array_sort(&mail->header_lines, header_line_cmp); lines = array_get(&mail->header_lines, &count); match = array_get(&mail->header_match, &match_count); header = buffer_get_data(mail->header_data, NULL); buf = buffer_create_dynamic(pool_datastack_create(), 256); /* go through all the header lines we found */ for (i = match_idx = 0; i < count; i = j) { /* matches and header lines are both sorted, all matches until lines[i] weren't found */ while (match_idx < lines[i].field_idx && match_idx < match_count) { if (HEADER_MATCH_USABLE(mail, match[match_idx]) && mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, match_idx)) { /* this header doesn't exist. remember that. */ i_assert((match[match_idx] & HEADER_MATCH_FLAG_FOUND) == 0); index_mail_cache_add_idx(mail, match_idx, "", 0); } match_idx++; } if (match_idx < match_count) { /* save index to first header line */ i_assert(match_idx == lines[i].field_idx); j = i + 1; array_idx_set(&mail->header_match_lines, match_idx, &j); match_idx++; } if (!mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, lines[i].field_idx)) { /* header is already cached. skip over all the header lines. */ for (j = i+1; j < count; j++) { if (lines[j].field_idx != lines[i].field_idx) break; } continue; } /* buffer contains: { uint32_t line_num[], 0, header texts } noncontiguous is just a small optimization.. */ buffer_set_used_size(buf, 0); buffer_append(buf, &lines[i].line_num, sizeof(lines[i].line_num)); noncontiguous = FALSE; for (j = i+1; j < count; j++) { if (lines[j].field_idx != lines[i].field_idx) break; if (lines[j].start_pos != lines[j-1].end_pos) noncontiguous = TRUE; buffer_append(buf, &lines[j].line_num, sizeof(lines[j].line_num)); } buffer_append_zero(buf, sizeof(uint32_t)); if (noncontiguous) { for (; i < j; i++) { buffer_append(buf, header + lines[i].start_pos, lines[i].end_pos - lines[i].start_pos); } i--; } else { buffer_append(buf, header + lines[i].start_pos, lines[j-1].end_pos - lines[i].start_pos); } data = buffer_get_data(buf, &data_size); index_mail_cache_add_idx(mail, lines[i].field_idx, data, data_size); } for (; match_idx < match_count; match_idx++) { if (HEADER_MATCH_USABLE(mail, match[match_idx]) && mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, match_idx)) { /* this header doesn't exist. remember that. */ i_assert((match[match_idx] & HEADER_MATCH_FLAG_FOUND) == 0); index_mail_cache_add_idx(mail, match_idx, "", 0); } } mail->data.dont_cache_field_idx = UINT_MAX; mail->data.header_parser_initialized = FALSE; } static unsigned int get_header_field_idx(struct mailbox *box, const char *field, enum mail_cache_decision_type decision) { struct mail_cache_field header_field; i_zero(&header_field); header_field.type = MAIL_CACHE_FIELD_HEADER; header_field.decision = decision; T_BEGIN { header_field.name = t_strconcat("hdr.", field, NULL); mail_cache_register_fields(box->cache, &header_field, 1); } T_END; return header_field.idx; } bool index_mail_want_parse_headers(struct index_mail *mail) { if (mail->data.wanted_headers != NULL || mail->data.save_bodystructure_header) return TRUE; if ((mail->data.cache_fetch_fields & MAIL_FETCH_DATE) != 0 && !mail->data.sent_date_parsed) return TRUE; return FALSE; } static void index_mail_parse_header_register_all_wanted(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const struct mail_cache_field *all_cache_fields; unsigned int i, count; all_cache_fields = mail_cache_register_get_list(_mail->box->cache, pool_datastack_create(), &count); for (i = 0; i < count; i++) { if (strncasecmp(all_cache_fields[i].name, "hdr.", 4) != 0) continue; if (!mail_cache_field_want_add(_mail->transaction->cache_trans, _mail->seq, i)) continue; array_idx_set(&mail->header_match, all_cache_fields[i].idx, &mail->header_match_value); } } void index_mail_parse_header_init(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers) { struct index_mail_data *data = &mail->data; const uint8_t *match; unsigned int i, field_idx, match_count; i_assert(!mail->data.header_parser_initialized); mail->header_seq = data->seq; if (mail->header_data == NULL) { mail->header_data = buffer_create_dynamic(default_pool, 4096); i_array_init(&mail->header_lines, 32); i_array_init(&mail->header_match, 32); i_array_init(&mail->header_match_lines, 32); mail->header_match_value = HEADER_MATCH_SKIP_COUNT; } else { buffer_set_used_size(mail->header_data, 0); array_clear(&mail->header_lines); array_clear(&mail->header_match_lines); mail->header_match_value += HEADER_MATCH_SKIP_COUNT; i_assert((mail->header_match_value & (HEADER_MATCH_SKIP_COUNT-1)) == 0); if (mail->header_match_value == 0) { /* wrapped, we'll have to clear the buffer */ array_clear(&mail->header_match); mail->header_match_value = HEADER_MATCH_SKIP_COUNT; } } if (headers != NULL) { for (i = 0; i < headers->count; i++) { array_idx_set(&mail->header_match, headers->idx[i], &mail->header_match_value); } } if (data->wanted_headers != NULL && data->wanted_headers != headers) { headers = data->wanted_headers; for (i = 0; i < headers->count; i++) { array_idx_set(&mail->header_match, headers->idx[i], &mail->header_match_value); } } /* register also all the other headers that exist in cache file */ T_BEGIN { index_mail_parse_header_register_all_wanted(mail); } T_END; /* if we want sent date, it doesn't mean that we also want to cache Date: header. if we have Date field's index set at this point we know that we want it. otherwise add it and remember that we don't want it cached. */ field_idx = get_header_field_idx(mail->mail.mail.box, "Date", MAIL_CACHE_DECISION_NO); match = array_get(&mail->header_match, &match_count); if (field_idx < match_count && match[field_idx] == mail->header_match_value) { /* cache Date: header */ } else if ((data->cache_fetch_fields & MAIL_FETCH_DATE) != 0 || data->save_sent_date) { /* parse Date: header, but don't cache it. */ data->dont_cache_field_idx = field_idx; array_idx_set(&mail->header_match, field_idx, &mail->header_match_value); } mail->data.header_parser_initialized = TRUE; mail->data.parse_line_num = 0; i_zero(&mail->data.parse_line); } static void index_mail_parse_finish_imap_envelope(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const unsigned int cache_field_envelope = mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx; string_t *str; str = str_new(mail->mail.data_pool, 256); imap_envelope_write(mail->data.envelope_data, str); mail->data.envelope = str_c(str); if (mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_field_envelope)) { index_mail_cache_add_idx(mail, cache_field_envelope, str_data(str), str_len(str)); } } void index_mail_parse_header(struct message_part *part, struct message_header_line *hdr, struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; struct index_mail_data *data = &mail->data; unsigned int field_idx, count; uint8_t *match; i_assert(data->header_parser_initialized); data->parse_line_num++; if (data->save_bodystructure_header) { i_assert(part != NULL); message_part_data_parse_from_header(mail->mail.data_pool, part, hdr); } if (data->save_envelope) { message_part_envelope_parse_from_header(mail->mail.data_pool, &data->envelope_data, hdr); if (hdr == NULL) index_mail_parse_finish_imap_envelope(mail); } if (hdr == NULL) { /* end of headers */ if (mail->data.save_sent_date) mail->data.sent_date_parsed = TRUE; T_BEGIN { index_mail_parse_header_finish(mail); } T_END; if (data->save_bodystructure_header) { i_assert(!data->save_bodystructure_body || data->parser_ctx != NULL); data->save_bodystructure_header = FALSE; } return; } if (!hdr->continued) { T_BEGIN { const char *cache_field_name = t_strconcat("hdr.", hdr->name, NULL); data->parse_line.field_idx = mail_cache_register_lookup(_mail->box->cache, cache_field_name); } T_END; } field_idx = data->parse_line.field_idx; match = array_get_modifiable(&mail->header_match, &count); if (field_idx >= count || !HEADER_MATCH_USABLE(mail, match[field_idx])) { /* we don't want this header. */ return; } if (!hdr->continued) { /* beginning of a line. add the header name. */ data->parse_line.start_pos = str_len(mail->header_data); data->parse_line.line_num = data->parse_line_num; str_append(mail->header_data, hdr->name); str_append_n(mail->header_data, hdr->middle, hdr->middle_len); /* remember that we saw this header so we don't add it to cache as nonexistent. */ match[field_idx] |= HEADER_MATCH_FLAG_FOUND; } str_append_n(mail->header_data, hdr->value, hdr->value_len); if (!hdr->no_newline) str_append(mail->header_data, "\n"); if (!hdr->continues) { data->parse_line.end_pos = str_len(mail->header_data); array_append(&mail->header_lines, &data->parse_line, 1); } } static void index_mail_parse_part_header_cb(struct message_part *part, struct message_header_line *hdr, struct index_mail *mail) { index_mail_parse_header(part, hdr, mail); } static void index_mail_parse_header_cb(struct message_header_line *hdr, struct index_mail *mail) { index_mail_parse_header(mail->data.parts, hdr, mail); } struct istream * index_mail_cache_parse_init(struct mail *_mail, struct istream *input) { struct index_mail *mail = (struct index_mail *)_mail; struct istream *input2; i_assert(mail->data.tee_stream == NULL); i_assert(mail->data.parser_ctx == NULL); /* we're doing everything for now, figure out later if we want to save them. */ mail->data.save_sent_date = TRUE; mail->data.save_bodystructure_header = TRUE; mail->data.save_bodystructure_body = TRUE; mail->data.tee_stream = tee_i_stream_create(input); input = tee_i_stream_create_child(mail->data.tee_stream); input2 = tee_i_stream_create_child(mail->data.tee_stream); index_mail_parse_header_init(mail, NULL); mail->data.parser_input = input; mail->data.parser_ctx = message_parser_init(mail->mail.data_pool, input, hdr_parser_flags, msg_parser_flags); i_stream_unref(&input); return input2; } static void index_mail_init_parser(struct index_mail *mail) { struct index_mail_data *data = &mail->data; struct message_part *parts; const char *error; if (data->parser_ctx != NULL) { data->parser_input = NULL; if (message_parser_deinit_from_parts(&data->parser_ctx, &parts, &error) < 0) { index_mail_set_message_parts_corrupted(&mail->mail.mail, error); data->parts = NULL; } } if (data->parts == NULL) { data->parser_input = data->stream; data->parser_ctx = message_parser_init(mail->mail.data_pool, data->stream, hdr_parser_flags, msg_parser_flags); } else { data->parser_ctx = message_parser_init_from_parts(data->parts, data->stream, hdr_parser_flags, msg_parser_flags); } } int index_mail_parse_headers(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers, const char *reason) { struct index_mail_data *data = &mail->data; struct istream *input; uoff_t old_offset; old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_hdr_stream_because(&mail->mail.mail, NULL, reason, &input) < 0) return -1; i_assert(data->stream != NULL); index_mail_parse_header_init(mail, headers); if (data->parts == NULL || data->save_bodystructure_header) { /* initialize bodystructure parsing in case we read the whole message. */ index_mail_init_parser(mail); message_parser_parse_header(data->parser_ctx, &data->hdr_size, index_mail_parse_part_header_cb, mail); } else { /* just read the header */ i_assert(!data->save_bodystructure_body || data->parser_ctx != NULL); message_parse_header(data->stream, &data->hdr_size, hdr_parser_flags, index_mail_parse_header_cb, mail); } if (index_mail_stream_check_failure(mail) < 0) return -1; data->hdr_size_set = TRUE; data->access_part &= ~PARSE_HDR; i_stream_seek(data->stream, old_offset); return 0; } static void imap_envelope_parse_callback(struct message_header_line *hdr, struct index_mail *mail) { message_part_envelope_parse_from_header(mail->mail.data_pool, &mail->data.envelope_data, hdr); if (hdr == NULL) index_mail_parse_finish_imap_envelope(mail); } int index_mail_headers_get_envelope(struct index_mail *mail) { const unsigned int cache_field_envelope = mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx; struct mailbox_header_lookup_ctx *header_ctx; struct istream *stream; uoff_t old_offset; string_t *str; str = str_new(mail->mail.data_pool, 256); if (index_mail_cache_lookup_field(mail, str, cache_field_envelope) > 0) { mail->data.envelope = str_c(str); return 0; } str_free(&str); old_offset = mail->data.stream == NULL ? 0 : mail->data.stream->v_offset; mail->data.save_envelope = TRUE; header_ctx = mailbox_header_lookup_init(mail->mail.mail.box, message_part_envelope_headers); if (mail_get_header_stream(&mail->mail.mail, header_ctx, &stream) < 0) { mailbox_header_lookup_unref(&header_ctx); return -1; } mailbox_header_lookup_unref(&header_ctx); if (mail->data.envelope == NULL && stream != NULL) { /* we got the headers from cache - parse them to get the envelope */ message_parse_header(stream, NULL, hdr_parser_flags, imap_envelope_parse_callback, mail); if (stream->stream_errno != 0) { index_mail_stream_log_failure_for(mail, stream); return -1; } mail->data.save_envelope = FALSE; } if (mail->data.stream != NULL) i_stream_seek(mail->data.stream, old_offset); return 0; } static size_t get_header_size(buffer_t *buffer, size_t pos) { const unsigned char *data; size_t i, size; data = buffer_get_data(buffer, &size); i_assert(pos <= size); for (i = pos; i < size; i++) { if (data[i] == '\n') { if (i+1 == size || (data[i+1] != ' ' && data[i+1] != '\t')) return i - pos; } } return size - pos; } static int index_mail_header_is_parsed(struct index_mail *mail, unsigned int field_idx) { const uint8_t *match; unsigned int count; match = array_get(&mail->header_match, &count); if (field_idx < count && HEADER_MATCH_USABLE(mail, match[field_idx])) return (match[field_idx] & HEADER_MATCH_FLAG_FOUND) != 0; return -1; } static bool skip_header(const unsigned char **data, size_t len) { const unsigned char *p = *data; size_t i; for (i = 0; i < len; i++) { if (p[i] == ':') break; } if (i == len) return FALSE; for (i++; i < len; i++) { if (!IS_LWSP(p[i])) break; } *data = p + i; return TRUE; } static const char *const * index_mail_get_parsed_header(struct index_mail *mail, unsigned int field_idx) { ARRAY(const char *) header_values; const struct index_mail_line *lines; const unsigned char *header, *value_start, *value_end; const unsigned int *line_idx; const char *value; unsigned int i, lines_count, first_line_idx; line_idx = array_idx(&mail->header_match_lines, field_idx); i_assert(*line_idx != 0); first_line_idx = *line_idx - 1; p_array_init(&header_values, mail->mail.data_pool, 4); header = buffer_get_data(mail->header_data, NULL); lines = array_get(&mail->header_lines, &lines_count); for (i = first_line_idx; i < lines_count; i++) { if (lines[i].field_idx != lines[first_line_idx].field_idx) break; /* skip header: and drop ending LF */ value_start = header + lines[i].start_pos; value_end = header + lines[i].end_pos; if (skip_header(&value_start, value_end - value_start)) { if (value_start != value_end && value_end[-1] == '\n') value_end--; value = p_strndup(mail->mail.data_pool, value_start, value_end - value_start); array_append(&header_values, &value, 1); } } array_append_zero(&header_values); return array_idx(&header_values, 0); } static int index_mail_get_raw_headers(struct index_mail *mail, const char *field, const char *const **value_r) { struct mail *_mail = &mail->mail.mail; const char *headers[2], *value; struct mailbox_header_lookup_ctx *headers_ctx; unsigned char *data; unsigned int field_idx; string_t *dest; size_t i, len, len2; int ret; ARRAY(const char *) header_values; i_assert(field != NULL); field_idx = get_header_field_idx(_mail->box, field, MAIL_CACHE_DECISION_TEMP); dest = str_new(mail->mail.data_pool, 128); if (mail_cache_lookup_headers(_mail->transaction->cache_view, dest, _mail->seq, &field_idx, 1) <= 0) { /* not in cache / error - first see if it's already parsed */ p_free(mail->mail.data_pool, dest); if (mail->data.header_parser_initialized) { /* don't try to parse headers recursively. we're here because message size was wrong and istream-mail wants to log some cached headers. */ i_assert(mail->mail.mail.lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE); mail_set_aborted(&mail->mail.mail); return -1; } if (mail->header_seq != mail->data.seq || index_mail_header_is_parsed(mail, field_idx) < 0) { /* parse */ const char *reason = index_mail_cache_reason(_mail, t_strdup_printf("header %s", field)); headers[0] = field; headers[1] = NULL; headers_ctx = mailbox_header_lookup_init(_mail->box, headers); ret = index_mail_parse_headers(mail, headers_ctx, reason); mailbox_header_lookup_unref(&headers_ctx); if (ret < 0) return -1; } if ((ret = index_mail_header_is_parsed(mail, field_idx)) <= 0) { /* not found */ i_assert(ret != -1); *value_r = p_new(mail->mail.data_pool, const char *, 1); return 0; } *value_r = index_mail_get_parsed_header(mail, field_idx); return 0; } _mail->transaction->stats.cache_hit_count++; data = buffer_get_modifiable_data(dest, &len); if (len == 0) { /* cached as nonexistent. */ *value_r = p_new(mail->mail.data_pool, const char *, 1); return 0; } p_array_init(&header_values, mail->mail.data_pool, 4); /* cached. skip "header name: " parts in dest. */ for (i = 0; i < len; i++) { if (data[i] == ':') { i++; while (i < len && IS_LWSP(data[i])) i++; /* @UNSAFE */ len2 = get_header_size(dest, i); data[i + len2] = '\0'; value = (const char *)data + i; i += len2 + 1; array_append(&header_values, &value, 1); } } array_append_zero(&header_values); *value_r = array_idx(&header_values, 0); return 0; } static int unfold_header(pool_t pool, const char **_str) { const char *str = *_str; char *new_str; unsigned int i, j; for (i = 0; str[i] != '\0'; i++) { if (str[i] == '\n') break; } if (str[i] == '\0') return 0; /* @UNSAFE */ new_str = p_malloc(pool, i + strlen(str+i) + 1); memcpy(new_str, str, i); for (j = i; str[i] != '\0'; i++) { if (str[i] == '\n') { new_str[j++] = ' '; i++; if (str[i] == '\0') break; if (str[i] != ' ' && str[i] != '\t') { /* corrupted */ return -1; } } else { new_str[j++] = str[i]; } } new_str[j] = '\0'; *_str = new_str; return 0; } static void str_replace_nuls(string_t *str) { char *data = str_c_modifiable(str); size_t i, len = str_len(str); for (i = 0; i < len; i++) { if (data[i] == '\0') data[i] = ' '; } } static int index_mail_headers_decode(struct index_mail *mail, const char *const **_list, unsigned int max_count) { const char *const *list = *_list; const char **decoded_list, *input; unsigned int i, count; string_t *str; count = str_array_length(list); if (count > max_count) count = max_count; decoded_list = p_new(mail->mail.data_pool, const char *, count + 1); str = t_str_new(512); for (i = 0; i < count; i++) { str_truncate(str, 0); input = list[i]; /* unfold all lines into a single line */ if (unfold_header(mail->mail.data_pool, &input) < 0) return -1; /* decode MIME encoded-words. decoding may also add new LFs. */ message_header_decode_utf8((const unsigned char *)input, strlen(input), str, NULL); if (strcmp(str_c(str), input) != 0) { if (strlen(str_c(str)) != str_len(str)) { /* replace NULs with spaces */ str_replace_nuls(str); } input = p_strdup(mail->mail.data_pool, str_c(str)); } decoded_list[i] = input; } *_list = decoded_list; return 0; } int index_mail_get_headers(struct mail *_mail, const char *field, bool decode_to_utf8, const char *const **value_r) { struct index_mail *mail = (struct index_mail *)_mail; bool retry = TRUE; int ret; for (;; retry = FALSE) { if (index_mail_get_raw_headers(mail, field, value_r) < 0) return -1; if (**value_r == NULL) return 0; if (!decode_to_utf8) return 1; T_BEGIN { ret = index_mail_headers_decode(mail, value_r, UINT_MAX); } T_END; if (ret < 0 && retry) { mail_set_mail_cache_corrupted(_mail, "Broken header %s", field); } else { break; } } if (ret < 0) { i_panic("BUG: Broken header %s for mail UID %u " "wasn't fixed by re-parsing the header", field, _mail->uid); } return 1; } int index_mail_get_first_header(struct mail *_mail, const char *field, bool decode_to_utf8, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; const char *const *list; bool retry = TRUE; int ret; for (;; retry = FALSE) { if (index_mail_get_raw_headers(mail, field, &list) < 0) return -1; if (!decode_to_utf8 || list[0] == NULL) { ret = 0; break; } T_BEGIN { ret = index_mail_headers_decode(mail, &list, 1); } T_END; if (ret < 0 && retry) { mail_set_mail_cache_corrupted(_mail, "Broken header %s", field); /* retry by parsing the full header */ } else { break; } } if (ret < 0) { i_panic("BUG: Broken header %s for mail UID %u " "wasn't fixed by re-parsing the header", field, _mail->uid); } *value_r = list[0]; return list[0] != NULL ? 1 : 0; } static void header_cache_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched ATTR_UNUSED, struct index_mail *mail) { index_mail_parse_header(NULL, hdr, mail); } int index_mail_get_header_stream(struct mail *_mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; struct istream *input; string_t *dest; if (mail->data.filter_stream != NULL) { const unsigned char *data; size_t size; /* read through the previous filter_stream. this makes sure that the fields are added to cache, and most importantly it resets header_parser_initialized=FALSE so we don't assert on it. */ while (i_stream_read_more(mail->data.filter_stream, &data, &size) > 0) i_stream_skip(mail->data.filter_stream, size); i_stream_destroy(&mail->data.filter_stream); } if (mail->data.save_bodystructure_header) { /* we have to parse the header. */ const char *reason = index_mail_cache_reason(_mail, "bodystructure"); if (index_mail_parse_headers(mail, headers, reason) < 0) return -1; } dest = str_new(mail->mail.data_pool, 256); if (mail_cache_lookup_headers(_mail->transaction->cache_view, dest, _mail->seq, headers->idx, headers->count) > 0) { str_append(dest, "\n"); _mail->transaction->stats.cache_hit_count++; mail->data.filter_stream = i_stream_create_from_data(str_data(dest), str_len(dest)); *stream_r = mail->data.filter_stream; return 0; } /* not in cache / error */ p_free(mail->mail.data_pool, dest); unsigned int first_not_found = UINT_MAX, not_found_count = 0; for (unsigned int i = 0; i < headers->count; i++) { if (mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, headers->idx[i]) <= 0) { if (not_found_count++ == 0) first_not_found = i; } } const char *reason; if (not_found_count == 0) reason = "BUG: all headers seem to exist in cache"; else { i_assert(first_not_found != UINT_MAX); reason = index_mail_cache_reason(_mail, t_strdup_printf( "%u/%u headers not cached (first=%s)", not_found_count, headers->count, headers->name[first_not_found])); } if (mail_get_hdr_stream_because(_mail, NULL, reason, &input) < 0) return -1; index_mail_parse_header_init(mail, headers); mail->data.filter_stream = i_stream_create_header_filter(mail->data.stream, HEADER_FILTER_INCLUDE | HEADER_FILTER_ADD_MISSING_EOH | HEADER_FILTER_HIDE_BODY, headers->name, headers->count, header_cache_callback, mail); *stream_r = mail->data.filter_stream; return 0; } dovecot-2.2.33.2/src/lib-storage/index/index-sort-string.c0000644000175000017500000006507113165463624020222 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ /* The idea is that we use 32bit integers for string sort IDs which specifiy the sort order for primary sort condition. The whole 32bit integer space is used and whenever adding a string, the available space is halved and the new ID is added in the middle. For example if we add one mail the first time, it gets ID 2^31. If we then add two mails which are sorted before the first one, they get IDs 2^31/3 and 2^31/3*2. Once we run out of the available space between IDs, more space is made by renumbering some IDs. */ #include "lib.h" #include "array.h" #include "str.h" #include "index-storage.h" #include "index-sort-private.h" struct mail_sort_node { uint32_t seq:29; uint32_t wanted:1; uint32_t no_update:1; uint32_t sort_id_changed:1; uint32_t sort_id; }; ARRAY_DEFINE_TYPE(mail_sort_node, struct mail_sort_node); struct sort_string_context { struct mail_search_sort_program *program; const char *primary_sort_name; ARRAY_TYPE(mail_sort_node) zero_nodes, nonzero_nodes, sorted_nodes; const char **sort_strings; pool_t sort_string_pool; unsigned int first_missing_sort_id_idx; uint32_t ext_id, last_seq, highest_reset_id, prev_seq; uint32_t lowest_nonexpunged_zero; unsigned int regetting:1; unsigned int have_all_wanted:1; unsigned int no_writing:1; unsigned int reverse:1; unsigned int seqs_nonsorted:1; unsigned int broken:1; unsigned int failed:1; }; static struct sort_string_context *static_zero_cmp_context; static void index_sort_list_reset_broken(struct sort_string_context *ctx, const char *reason); static void index_sort_node_add(struct sort_string_context *ctx, struct mail_sort_node *node); void index_sort_list_init_string(struct mail_search_sort_program *program) { struct sort_string_context *ctx; const char *name; switch (program->sort_program[0] & MAIL_SORT_MASK) { case MAIL_SORT_CC: name = "sort-c"; break; case MAIL_SORT_FROM: name = "sort-f"; break; case MAIL_SORT_SUBJECT: name = "sort-s"; break; case MAIL_SORT_TO: name = "sort-t"; break; case MAIL_SORT_DISPLAYFROM: name = "sort-df"; break; case MAIL_SORT_DISPLAYTO: name = "sort-dt"; break; default: i_unreached(); } program->context = ctx = i_new(struct sort_string_context, 1); ctx->reverse = (program->sort_program[0] & MAIL_SORT_FLAG_REVERSE) != 0; ctx->program = program; ctx->primary_sort_name = name; ctx->ext_id = mail_index_ext_register(program->t->box->index, name, 0, sizeof(uint32_t), sizeof(uint32_t)); i_array_init(&ctx->zero_nodes, 128); i_array_init(&ctx->nonzero_nodes, 128); } static int sort_node_seq_cmp(const struct mail_sort_node *n1, const struct mail_sort_node *n2) { if (n1->seq < n2->seq) return -1; if (n1->seq > n2->seq) return 1; return 0; } static void index_sort_generate_seqs(struct sort_string_context *ctx) { struct mail_sort_node *nodes, *nodes2; unsigned int i, j, count, count2; uint32_t seq; nodes = array_get_modifiable(&ctx->nonzero_nodes, &count); nodes2 = array_get_modifiable(&ctx->zero_nodes, &count2); if (!array_is_created(&ctx->program->seqs)) i_array_init(&ctx->program->seqs, count + count2); else array_clear(&ctx->program->seqs); for (i = j = 0;;) { if (i < count && j < count2) { if (nodes[i].seq < nodes2[j].seq) seq = nodes[i++].seq; else seq = nodes2[j++].seq; } else if (i < count) { seq = nodes[i++].seq; } else if (j < count2) { seq = nodes2[j++].seq; } else { break; } array_append(&ctx->program->seqs, &seq, 1); } } static void index_sort_reget_sort_ids(struct sort_string_context *ctx) { struct mail_sort_node node; const uint32_t *seqs; unsigned int i, count; i_assert(!ctx->regetting); ctx->regetting = TRUE; index_sort_generate_seqs(ctx); array_clear(&ctx->zero_nodes); array_clear(&ctx->nonzero_nodes); i_zero(&node); node.wanted = TRUE; seqs = array_get(&ctx->program->seqs, &count); for (i = 0; i < count; i++) { node.seq = seqs[i]; index_sort_node_add(ctx, &node); } ctx->regetting = FALSE; } static void index_sort_node_add(struct sort_string_context *ctx, struct mail_sort_node *node) { struct mail_index_map *map; const void *data; uint32_t reset_id; bool expunged; mail_index_lookup_ext_full(ctx->program->t->view, node->seq, ctx->ext_id, &map, &data, &expunged); if (expunged) { /* we don't want to update expunged messages' sort IDs */ node->no_update = TRUE; /* we can't trust expunged messages' sort IDs. they might be valid, but it's also possible that sort IDs were updated and the expunged messages' sort IDs became invalid. we could use sort ID if we could know the extension's reset_id at the time of the expunge so we could compare it to highest_reset_id, but this isn't currently possible. */ node->sort_id = 0; } else { node->sort_id = ctx->broken || data == NULL ? 0 : *(const uint32_t *)data; if (node->sort_id == 0) { if (ctx->lowest_nonexpunged_zero > node->seq || ctx->lowest_nonexpunged_zero == 0) ctx->lowest_nonexpunged_zero = node->seq; } else if (ctx->lowest_nonexpunged_zero != 0 && ctx->lowest_nonexpunged_zero <= node->seq) { uint32_t nonzero_uid, zero_uid; mail_index_lookup_uid(ctx->program->t->view, node->seq, &nonzero_uid); mail_index_lookup_uid(ctx->program->t->view, ctx->lowest_nonexpunged_zero, &zero_uid); index_sort_list_reset_broken(ctx, t_strdup_printf( "sort_id=0 found in the middle " "(uid=%u has sort_id, uid=%u doesn't)", nonzero_uid, zero_uid)); ctx->broken = TRUE; node->sort_id = 0; } } if (node->sort_id != 0) { /* if reset ID increases, lookup all existing messages' sort IDs again. if it decreases, ignore the sort ID. */ if (!mail_index_ext_get_reset_id(ctx->program->t->view, map, ctx->ext_id, &reset_id)) reset_id = 0; if (reset_id != ctx->highest_reset_id) { if (reset_id < ctx->highest_reset_id) { i_assert(expunged); node->sort_id = 0; } else if (ctx->have_all_wanted) { /* a bit late to start changing the reset_id. the node lists aren't ordered by sequence anymore. */ node->sort_id = 0; ctx->no_writing = TRUE; } else { ctx->highest_reset_id = reset_id; index_sort_reget_sort_ids(ctx); } } } if (node->sort_id == 0) array_append(&ctx->zero_nodes, node, 1); else array_append(&ctx->nonzero_nodes, node, 1); if (ctx->last_seq < node->seq) ctx->last_seq = node->seq; } void index_sort_list_add_string(struct mail_search_sort_program *program, struct mail *mail) { struct sort_string_context *ctx = program->context; struct mail_sort_node node; i_zero(&node); node.seq = mail->seq; node.wanted = TRUE; if (mail->seq < ctx->prev_seq) ctx->seqs_nonsorted = TRUE; ctx->prev_seq = mail->seq; index_sort_node_add(ctx, &node); } static int sort_node_zero_string_cmp(const struct mail_sort_node *n1, const struct mail_sort_node *n2) { struct sort_string_context *ctx = static_zero_cmp_context; int ret; ret = strcmp(ctx->sort_strings[n1->seq], ctx->sort_strings[n2->seq]); if (ret != 0) return !ctx->reverse ? ret : -ret; return index_sort_node_cmp_type(ctx->program, ctx->program->sort_program + 1, n1->seq, n2->seq); } static void index_sort_zeroes(struct sort_string_context *ctx) { enum mail_sort_type sort_type = ctx->program->sort_program[0]; string_t *str; pool_t pool; struct mail_sort_node *nodes; unsigned int i, count; /* first get all the messages' sort strings. although this takes more memory, it makes error handling easier and probably also helps CPU caching. */ ctx->sort_strings = i_new(const char *, ctx->last_seq + 1); ctx->sort_string_pool = pool = pool_alloconly_create("sort strings", 1024*64); str = str_new(default_pool, 512); nodes = array_get_modifiable(&ctx->zero_nodes, &count); for (i = 0; i < count; i++) { i_assert(nodes[i].seq <= ctx->last_seq); T_BEGIN { if (index_sort_header_get(ctx->program, nodes[i].seq, sort_type, str) < 0) { nodes[i].no_update = TRUE; ctx->failed = TRUE; } ctx->sort_strings[nodes[i].seq] = str_len(str) == 0 ? "" : p_strdup(pool, str_c(str)); } T_END; } str_free(&str); /* we have all strings, sort nodes based on them */ static_zero_cmp_context = ctx; array_sort(&ctx->zero_nodes, sort_node_zero_string_cmp); } static bool index_sort_get_expunged_string(struct sort_string_context *ctx, uint32_t idx, string_t *str, const char **result_r) { enum mail_sort_type sort_type = ctx->program->sort_program[0]; const struct mail_sort_node *nodes; const char *result = NULL; unsigned int i, count; uint32_t sort_id; /* Look forwards and backwards to see if there are identical sort_ids. If we do find them, try to get their sort string and use it to update the rest. */ nodes = array_get(&ctx->nonzero_nodes, &count); sort_id = nodes[idx].sort_id; /* If previous sort ID is identical and its sort string is set, we can trust it. If it's expunged, we already verified that there are no non-expunged messages. */ if (idx > 0 && nodes[idx-1].sort_id == sort_id && ctx->sort_strings[nodes[idx].seq] != NULL) { *result_r = ctx->sort_strings[nodes[idx].seq]; return TRUE; } /* Go forwards as long as there are identical sort IDs. If we find one that's not expunged, update string table for all messages with identical sort IDs. */ for (i = idx + 1; i < count; i++) { if (nodes[i].sort_id != sort_id) break; if (ctx->sort_strings[nodes[i].seq] != NULL) { /* usually we fill all identical sort_ids and this shouldn't happen, but we can get here if we skipped over messages when binary searching */ result = ctx->sort_strings[nodes[i].seq]; break; } if (index_sort_header_get(ctx->program, nodes[i].seq, sort_type, str) > 0) { result = str_len(str) == 0 ? "" : p_strdup(ctx->sort_string_pool, str_c(str)); break; } } if (result == NULL) { /* unknown */ return FALSE; } /* fill all identical sort_ids with the same value */ for (i = idx; i > 0 && nodes[i-1].sort_id == sort_id; i--) ; for (i = idx; i < count && nodes[i].sort_id == sort_id; i++) ctx->sort_strings[nodes[i].seq] = result; *result_r = result; return TRUE; } static bool index_sort_get_string(struct sort_string_context *ctx, uint32_t idx, struct mail_sort_node *node, const char **str_r) { uint32_t seq = node->seq; int ret = 1; if (node->no_update) { /* we've already determined that we can't do this lookup */ *str_r = ctx->sort_strings[seq]; return FALSE; } if (ctx->sort_strings[seq] == NULL) T_BEGIN { string_t *str; const char *result; str = t_str_new(256); ret = index_sort_header_get(ctx->program, seq, ctx->program->sort_program[0], str); if (ret < 0) ctx->failed = TRUE; else if (ret == 0) { if (!index_sort_get_expunged_string(ctx, idx, str, &result)) ctx->sort_strings[seq] = ""; else { /* found the expunged string - return success */ ctx->sort_strings[seq] = result; ret = 1; } } else { ctx->sort_strings[seq] = str_len(str) == 0 ? "" : p_strdup(ctx->sort_string_pool, str_c(str)); } } T_END; if (ret <= 0) node->no_update = TRUE; *str_r = ctx->sort_strings[seq]; return ret > 0; } static void index_sort_bsearch(struct sort_string_context *ctx, const char *key, unsigned int start_idx, unsigned int *idx_r, const char **prev_str_r) { struct mail_sort_node *nodes; const char *str, *str2; unsigned int idx, left_idx, right_idx, prev; int ret; nodes = array_get_modifiable(&ctx->nonzero_nodes, &right_idx); i_assert(right_idx < INT_MAX); idx = left_idx = start_idx; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (index_sort_get_string(ctx, idx, &nodes[idx], &str)) ret = strcmp(key, str); else { /* put expunged (and otherwise failed) messages first */ ret = 1; for (prev = idx; prev > 0; ) { prev--; if (index_sort_get_string(ctx, prev, &nodes[prev], &str2)) { ret = strcmp(key, str2); if (ret <= 0) { idx = prev; str = str2; } break; } } } if (ret > 0) left_idx = idx+1; else if (ret < 0) right_idx = idx; else { *idx_r = idx + 1; *prev_str_r = str; return; } } if (left_idx > idx) idx++; *idx_r = idx; if (idx > start_idx) { bool success; prev = idx; do { prev--; success = index_sort_get_string(ctx, prev, &nodes[prev], &str2); } while (!success && prev > 0 && nodes[prev-1].sort_id == nodes[prev].sort_id); *prev_str_r = str2; } } static void index_sort_merge(struct sort_string_context *ctx) { struct mail_sort_node *znodes, *nznodes; const char *zstr, *nzstr, *prev_str; unsigned int zpos, nzpos, nz_next_pos, zcount, nzcount; int ret; /* both zero_nodes and nonzero_nodes are sorted. we'll now just have to merge them together. use sorted_nodes as the result array. */ i_array_init(&ctx->sorted_nodes, array_count(&ctx->nonzero_nodes) + array_count(&ctx->zero_nodes)); znodes = array_get_modifiable(&ctx->zero_nodes, &zcount); nznodes = array_get_modifiable(&ctx->nonzero_nodes, &nzcount); prev_str = NULL; for (zpos = nzpos = 0; zpos < zcount && nzpos < nzcount; ) { zstr = ctx->sort_strings[znodes[zpos].seq]; if (index_sort_get_string(ctx, nzpos, &nznodes[nzpos], &nzstr)) ret = strcmp(zstr, nzstr); else if (prev_str != NULL && strcmp(zstr, prev_str) == 0) { /* identical to previous message, must keep them together */ ret = -1; } else { /* we can't be yet sure about the order, but future nznodes may reveal that the znode must be added later. if future nznodes don't reveal that, we have no idea about these nodes' order. so just always put the expunged message first. */ ret = 1; } if (ret <= 0) { array_append(&ctx->sorted_nodes, &znodes[zpos], 1); prev_str = zstr; zpos++; } else { array_append(&ctx->sorted_nodes, &nznodes[nzpos], 1); prev_str = nzstr; nzpos++; /* avoid looking up all existing messages' strings by binary searching the next zero-node position. don't bother if it looks like more work than linear scanning. */ if (zcount - zpos < (nzcount - nzpos)/2) { index_sort_bsearch(ctx, zstr, nzpos, &nz_next_pos, &prev_str); array_append(&ctx->sorted_nodes, &nznodes[nzpos], nz_next_pos - nzpos); nzpos = nz_next_pos; } } } /* only one of zero_nodes and nonzero_nodes can be non-empty now */ for (; zpos < zcount; zpos++) array_append(&ctx->sorted_nodes, &znodes[zpos], 1); for (; nzpos < nzcount; nzpos++) array_append(&ctx->sorted_nodes, &nznodes[nzpos], 1); /* future index_sort_get_string() calls use ctx->nonzero_nodes, but we use only ctx->sorted_nodes. make them identical. */ array_free(&ctx->nonzero_nodes); ctx->nonzero_nodes = ctx->sorted_nodes; } static int index_sort_add_ids_range(struct sort_string_context *ctx, unsigned int left_idx, unsigned int right_idx, const char **reason_r) { struct mail_sort_node *nodes; unsigned int i, count, rightmost_idx, skip; const char *left_str = NULL, *right_str = NULL, *str = NULL; uint32_t left_sort_id, right_sort_id, diff, left_str_idx = 0; bool no_left_str = FALSE, no_right_str = FALSE; int ret; nodes = array_get_modifiable(&ctx->sorted_nodes, &count); rightmost_idx = count - 1; /* get the sort IDs from left and right */ left_sort_id = nodes[left_idx].sort_id; right_sort_id = nodes[right_idx].sort_id; /* check if all of them should have the same sort IDs. we don't want to hit the renumbering code in that situation. */ if (left_sort_id == right_sort_id && left_sort_id != 0) { /* they should all have the same sort ID */ for (i = left_idx + 1; i < right_idx; i++) { nodes[i].sort_id = left_sort_id; nodes[i].sort_id_changed = TRUE; } return 0; } if (left_sort_id == 0) { i_assert(left_idx == 0); left_sort_id = 1; } if (right_sort_id == 0) { i_assert(right_idx == rightmost_idx); right_sort_id = (uint32_t)-1; } i_assert(left_sort_id <= right_sort_id); diff = right_sort_id - left_sort_id; while (diff / (right_idx-left_idx + 2) == 0) { /* we most likely don't have enough space. we have to renumber some of the existing sort IDs. do this by widening the area we're giving sort IDs. */ while (left_idx > 0) { if (nodes[--left_idx].sort_id != left_sort_id) { left_sort_id = nodes[left_idx].sort_id; if (left_sort_id == 0) { i_assert(left_idx == 0); left_sort_id = 1; } break; } } while (right_idx < rightmost_idx) { right_idx++; if (nodes[right_idx].sort_id > right_sort_id) break; } right_sort_id = nodes[right_idx].sort_id; if (right_sort_id == 0) { i_assert(right_idx == rightmost_idx); right_sort_id = (uint32_t)-1; } i_assert(left_sort_id <= right_sort_id); if (diff == right_sort_id - left_sort_id) { /* we did nothing, but there's still not enough space. have to renumber the leftmost/rightmost node(s) */ i_assert(left_idx == 0 && right_idx == rightmost_idx); if (left_sort_id > 1) { left_sort_id = 1; no_left_str = TRUE; } else { i_assert(right_sort_id != (uint32_t)-1); right_sort_id = (uint32_t)-1; no_right_str = TRUE; } } diff = right_sort_id - left_sort_id; } if (nodes[left_idx].sort_id != 0 && !no_left_str) { if (!index_sort_get_string(ctx, left_idx, &nodes[left_idx], &left_str)) { /* not equivalent with any message */ left_str = NULL; } else { left_str_idx = left_idx; } left_idx++; } if (nodes[right_idx].sort_id != 0 && !no_right_str) { if (!index_sort_get_string(ctx, right_idx, &nodes[right_idx], &right_str)) { /* not equivalent with any message */ right_str = NULL; } right_idx--; } i_assert(left_idx <= right_idx); /* give (new) sort IDs to all nodes in left_idx..right_idx range. divide the available space so that each message gets an equal sized share. some messages' sort strings may be equivalent, so give them the same sort IDs. */ for (i = left_idx; i <= right_idx; i++) { if (!index_sort_get_string(ctx, i, &nodes[i], &str)) { /* it doesn't really matter what we give to this message, since it's only temporary and we don't know its correct position anyway. so let's assume it's equivalent to previous message. */ nodes[i].sort_id = left_sort_id; continue; } ret = left_str == NULL ? 1 : strcmp(str, left_str); if (ret <= 0) { if (ret < 0) { /* broken sort_ids */ uint32_t str_uid, left_str_uid; mail_index_lookup_uid(ctx->program->t->view, nodes[i].seq, &str_uid); mail_index_lookup_uid(ctx->program->t->view, nodes[left_str_idx].seq, &left_str_uid); *reason_r = t_strdup_printf( "(idx=%u, seq=%u, uid=%u) '%s' < left string (idx=%u, seq=%u, uid=%u) '%s'", i, nodes[i].seq, str_uid, str, left_str_idx, nodes[left_str_idx].seq, left_str_uid, left_str); return -1; } nodes[i].sort_id = left_sort_id; } else if (right_str != NULL && strcmp(str, right_str) == 0) { /* the rest of the sort IDs should be the same */ nodes[i].sort_id = right_sort_id; left_sort_id = right_sort_id; } else { /* divide the available space equally. leave the same sized space also between the first and the last messages */ skip = (right_sort_id - left_sort_id) / (right_idx - i + 2); if (skip == 0) { /* broken sort IDs (we previously assigned left_sort_id=right_sort_id) */ uint32_t uid; mail_index_lookup_uid(ctx->program->t->view, nodes[i].seq, &uid); *reason_r = t_strdup_printf( "no sort_id space for uid=%u", uid); return -1; } left_sort_id += skip; i_assert(left_sort_id < right_sort_id); nodes[i].sort_id = left_sort_id; left_str = str; left_str_idx = i; } nodes[i].sort_id_changed = TRUE; } i_assert(str != NULL); if (right_str == NULL || strcmp(str, right_str) < 0 || (strcmp(str, right_str) == 0 && nodes[i-1].sort_id == right_sort_id)) return 0; *reason_r = t_strdup_printf("Invalid sort_id order ('%s' > '%s')", str, right_str); return -1; } static int index_sort_add_sort_ids(struct sort_string_context *ctx, const char **reason_r) { const struct mail_sort_node *nodes; unsigned int i, left_idx, right_idx, count; nodes = array_get(&ctx->sorted_nodes, &count); for (i = 0; i < count; i++) { if (nodes[i].sort_id != 0) continue; /* get the range for all sort_id=0 nodes. include the nodes left and right of the range as well */ for (right_idx = i + 1; right_idx < count; right_idx++) { if (nodes[right_idx].sort_id != 0) break; } if (right_idx == count) right_idx--; left_idx = i == 0 ? 0 : i - 1; if (index_sort_add_ids_range(ctx, left_idx, right_idx, reason_r) < 0) return -1; } return 0; } static void index_sort_write_changed_sort_ids(struct sort_string_context *ctx) { struct mail_index_transaction *itrans = ctx->program->t->itrans; uint32_t ext_id = ctx->ext_id; const struct mail_sort_node *nodes; unsigned int i, count; uint32_t lowest_failed_seq; if (ctx->no_writing) { /* our reset_id is already stale - don't even bother trying to write */ return; } mail_index_ext_reset_inc(itrans, ext_id, ctx->highest_reset_id, FALSE); /* We require that there aren't sort_id=0 gaps in the middle of the mails. At this point they could exist though, because some of the mail lookups may have failed. Failures due to expunges don't matter, because on the next lookup those mails will be lost anyway. Otherwise, make sure we don't write those gaps out First find the lowest non-expunged mail that has no_update set. */ nodes = array_get_modifiable(&ctx->sorted_nodes, &count); lowest_failed_seq = (uint32_t)-1; for (i = 0; i < count; i++) { uint32_t seq = nodes[i].seq; if (nodes[i].no_update && lowest_failed_seq > seq && !mail_index_is_expunged(ctx->program->t->view, seq)) lowest_failed_seq = seq; } /* add the missing sort IDs to index, but only for those sequences that are below lowest_failed_seq */ nodes = array_get_modifiable(&ctx->sorted_nodes, &count); for (i = 0; i < count; i++) { i_assert(nodes[i].sort_id != 0); if (!nodes[i].sort_id_changed || nodes[i].no_update || nodes[i].seq >= lowest_failed_seq) continue; mail_index_update_ext(itrans, nodes[i].seq, ext_id, &nodes[i].sort_id, NULL); } } static int sort_node_cmp(const struct mail_sort_node *n1, const struct mail_sort_node *n2) { struct sort_string_context *ctx = static_zero_cmp_context; if (n1->sort_id < n2->sort_id) return !ctx->reverse ? -1 : 1; if (n1->sort_id > n2->sort_id) return !ctx->reverse ? 1 : -1; return index_sort_node_cmp_type(ctx->program, ctx->program->sort_program + 1, n1->seq, n2->seq); } static void index_sort_add_missing(struct sort_string_context *ctx) { struct mail_sort_node node; const uint32_t *seqs; unsigned int i, count; uint32_t seq, next_seq; ctx->have_all_wanted = TRUE; seqs = array_get(&ctx->program->seqs, &count); for (i = 0, next_seq = 1; i < count; i++) { if (seqs[i] == next_seq) next_seq++; else { i_assert(next_seq < seqs[i]); for (seq = next_seq; seq < seqs[i]; seq++) { i_zero(&node); node.seq = seq; index_sort_node_add(ctx, &node); } next_seq = seqs[i] + 1; } } if (ctx->lowest_nonexpunged_zero == 0) { /* we're handling only expunged zeros. if it causes us to renumber some existing sort IDs, don't save them. */ ctx->no_writing = TRUE; } } static void index_sort_list_reset_broken(struct sort_string_context *ctx, const char *reason) { struct mailbox *box = ctx->program->t->box; struct mail_sort_node *node; mail_storage_set_critical(box->storage, "%s: Broken %s indexes, resetting: %s", box->name, ctx->primary_sort_name, reason); array_clear(&ctx->zero_nodes); array_append_array(&ctx->zero_nodes, &ctx->nonzero_nodes); array_clear(&ctx->nonzero_nodes); array_foreach_modifiable(&ctx->zero_nodes, node) node->sort_id = 0; } void index_sort_list_finish_string(struct mail_search_sort_program *program) { struct sort_string_context *ctx = program->context; const struct mail_sort_node *nodes; unsigned int i, count; const char *reason; uint32_t seq; static_zero_cmp_context = ctx; if (array_count(&ctx->zero_nodes) == 0) { /* fast path: we have all sort IDs */ array_sort(&ctx->nonzero_nodes, sort_node_cmp); nodes = array_get(&ctx->nonzero_nodes, &count); if (!array_is_created(&program->seqs)) i_array_init(&program->seqs, count); else array_clear(&program->seqs); for (i = 0; i < count; i++) { seq = nodes[i].seq; array_append(&program->seqs, &seq, 1); } array_free(&ctx->nonzero_nodes); } else { if (ctx->seqs_nonsorted) { /* the nodes need to be sorted by sequence initially */ array_sort(&ctx->zero_nodes, sort_node_seq_cmp); array_sort(&ctx->nonzero_nodes, sort_node_seq_cmp); } /* we have to add some sort IDs. we'll do this for all messages, so first remember what messages we wanted to know about. */ index_sort_generate_seqs(ctx); /* add messages not in seqs list */ index_sort_add_missing(ctx); /* sort all messages with sort IDs */ array_sort(&ctx->nonzero_nodes, sort_node_cmp); for (;;) { /* sort all messages without sort IDs */ index_sort_zeroes(ctx); if (ctx->reverse) { /* sort lists are descending currently, but merging and sort ID assigning works only with ascending lists. reverse the lists temporarily. we can't do this while earlier because secondary sort conditions must not be reversed in results (but while assigning sort IDs it doesn't matter). */ array_reverse(&ctx->nonzero_nodes); array_reverse(&ctx->zero_nodes); } /* merge zero and non-zero arrays into sorted_nodes */ index_sort_merge(ctx); /* give sort IDs to messages missing them */ if (index_sort_add_sort_ids(ctx, &reason) == 0) break; /* broken, try again with sort IDs reset */ index_sort_list_reset_broken(ctx, reason); } index_sort_write_changed_sort_ids(ctx); if (ctx->reverse) { /* restore the correct sort order */ array_reverse(&ctx->sorted_nodes); } nodes = array_get(&ctx->sorted_nodes, &count); array_clear(&program->seqs); for (i = 0; i < count; i++) { if (nodes[i].wanted) { seq = nodes[i].seq; array_append(&program->seqs, &seq, 1); } } pool_unref(&ctx->sort_string_pool); i_free(ctx->sort_strings); array_free(&ctx->sorted_nodes); /* NOTE: we already freed nonzero_nodes and made it point to sorted_nodes. */ } if (ctx->failed) program->failed = TRUE; array_free(&ctx->zero_nodes); i_free(ctx); program->context = NULL; } dovecot-2.2.33.2/src/lib-storage/index/index-mailbox-size.c0000644000175000017500000003324013165463624020323 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "net.h" #include "write-full.h" #include "mail-search-build.h" #include "index-storage.h" #include "index-mailbox-size.h" /* Saving new mails: After transaction is committed and synced, trigger vsize updating. Lock vsize updates. Check if the message count + last-indexed-uid are still valid. If they are, add all the missing new mails. Unlock. Fetching vsize: Lock vsize updates. Check if the message count + last-indexed-uid are still valid. If not, set them to zero. Add all the missing mails. Unlock. Expunging mails: Check if syncing would expunge any mails. If so, lock the vsize updates before locking syncing (to avoid deadlocks). Check if the message count + last-indexed-uid are still valid. If not, unlock vsize and do nothing else. Otherwise, for each expunged mail whose UID <= last-indexed-uid, decrease the message count and the vsize in memory. After syncing is successfully committed, write the changes to header. Unlock. Note that the final expunge handling with some mailbox formats is done while syncing is no longer locked. Because of this we need to have the vsize locking. The final vsize header update requires committing a transaction, which internally is the same as a sync lock. So to avoid deadlocks we always need to lock vsize updates before sync. */ #define VSIZE_LOCK_SUFFIX ".vsize.lock" #define VSIZE_UPDATE_MAX_LOCK_SECS 10 #define INDEXER_SOCKET_NAME "indexer" #define INDEXER_HANDSHAKE "VERSION\tindexer\t1\t0\n" struct mailbox_vsize_update { struct mailbox *box; struct mail_index_view *view; struct mailbox_index_vsize vsize_hdr, orig_vsize_hdr; struct file_lock *lock; bool lock_failed; bool rebuild; bool written; bool finish_in_background; }; static void vsize_header_refresh(struct mailbox_vsize_update *update) { const void *data; size_t size; if (update->view != NULL) mail_index_view_close(&update->view); (void)mail_index_refresh(update->box->index); update->view = mail_index_view_open(update->box->index); mail_index_get_header_ext(update->view, update->box->vsize_hdr_ext_id, &data, &size); if (size > 0) { memcpy(&update->orig_vsize_hdr, data, I_MIN(size, sizeof(update->orig_vsize_hdr))); } if (size == sizeof(update->vsize_hdr)) memcpy(&update->vsize_hdr, data, sizeof(update->vsize_hdr)); else { if (size != 0) { mail_storage_set_critical(update->box->storage, "vsize-hdr has invalid size: %"PRIuSIZE_T, size); } update->rebuild = TRUE; i_zero(&update->vsize_hdr); } } static void index_mailbox_vsize_check_rebuild(struct mailbox_vsize_update *update) { uint32_t seq1, seq2; if (update->vsize_hdr.highest_uid == 0) return; if (!mail_index_lookup_seq_range(update->view, 1, update->vsize_hdr.highest_uid, &seq1, &seq2)) seq2 = 0; if (update->vsize_hdr.message_count != seq2) { if (update->vsize_hdr.message_count < seq2) { mail_storage_set_critical(update->box->storage, "vsize-hdr has invalid message-count (%u < %u)", update->vsize_hdr.message_count, seq2); } else { /* some messages have been expunged, rescan */ } i_zero(&update->vsize_hdr); update->rebuild = TRUE; } } struct mailbox_vsize_update * index_mailbox_vsize_update_init(struct mailbox *box) { struct mailbox_vsize_update *update; i_assert(box->opened); update = i_new(struct mailbox_vsize_update, 1); update->box = box; vsize_header_refresh(update); return update; } static bool vsize_update_lock_full(struct mailbox_vsize_update *update, unsigned int lock_secs) { struct mailbox *box = update->box; const char *error; int ret; if (update->lock != NULL) return TRUE; if (update->lock_failed) return FALSE; if (MAIL_INDEX_IS_IN_MEMORY(box->index)) return FALSE; ret = mailbox_lock_file_create(box, VSIZE_LOCK_SUFFIX, lock_secs, &update->lock, &error); if (ret <= 0) { /* don't log lock timeouts, because we're somewhat expecting them. Especially when lock_secs is 0. */ if (ret < 0) mail_storage_set_critical(box->storage, "%s", error); update->lock_failed = TRUE; return FALSE; } update->rebuild = FALSE; vsize_header_refresh(update); index_mailbox_vsize_check_rebuild(update); return TRUE; } bool index_mailbox_vsize_update_try_lock(struct mailbox_vsize_update *update) { return vsize_update_lock_full(update, 0); } bool index_mailbox_vsize_update_wait_lock(struct mailbox_vsize_update *update) { return vsize_update_lock_full(update, VSIZE_UPDATE_MAX_LOCK_SECS); } bool index_mailbox_vsize_want_updates(struct mailbox_vsize_update *update) { return update->vsize_hdr.highest_uid > 0; } static void index_mailbox_vsize_update_write(struct mailbox_vsize_update *update) { struct mail_index_transaction *trans; if (update->written) return; update->written = TRUE; if (update->rebuild == FALSE && memcmp(&update->orig_vsize_hdr, &update->vsize_hdr, sizeof(update->vsize_hdr)) == 0) { /* no changes */ return; } trans = mail_index_transaction_begin(update->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header_ext(trans, update->box->vsize_hdr_ext_id, 0, &update->vsize_hdr, sizeof(update->vsize_hdr)); (void)mail_index_transaction_commit(&trans); } static void index_mailbox_vsize_notify_indexer(struct mailbox *box) { string_t *str = t_str_new(256); const char *path; int fd; path = t_strconcat(box->storage->user->set->base_dir, "/"INDEXER_SOCKET_NAME, NULL); fd = net_connect_unix(path); if (fd == -1) { mail_storage_set_critical(box->storage, "Can't start vsize building on background: " "net_connect_unix(%s) failed: %m", path); return; } str_append(str, INDEXER_HANDSHAKE); str_append(str, "APPEND\t0\t"); str_append_tabescaped(str, box->storage->user->username); str_append_c(str, '\t'); str_append_tabescaped(str, box->vname); str_append_c(str, '\n'); if (write_full(fd, str_data(str), str_len(str)) < 0) { mail_storage_set_critical(box->storage, "Can't start vsize building on background: " "write(%s) failed: %m", path); } i_close_fd(&fd); } void index_mailbox_vsize_update_deinit(struct mailbox_vsize_update **_update) { struct mailbox_vsize_update *update = *_update; *_update = NULL; if (update->lock != NULL || update->rebuild) index_mailbox_vsize_update_write(update); if (update->lock != NULL) file_lock_free(&update->lock); if (update->finish_in_background) index_mailbox_vsize_notify_indexer(update->box); mail_index_view_close(&update->view); i_free(update); } void index_mailbox_vsize_hdr_expunge(struct mailbox_vsize_update *update, uint32_t uid, uoff_t vsize) { i_assert(update->lock != NULL); if (uid > update->vsize_hdr.highest_uid) return; if (update->vsize_hdr.message_count == 0) { mail_storage_set_critical(update->box->storage, "vsize-hdr's message_count shrank below 0"); i_zero(&update->vsize_hdr); return; } update->vsize_hdr.message_count--; if (update->vsize_hdr.vsize < vsize) { mail_storage_set_critical(update->box->storage, "vsize-hdr's vsize shrank below 0"); i_zero(&update->vsize_hdr); return; } update->vsize_hdr.vsize -= vsize; } static int index_mailbox_vsize_hdr_add_missing(struct mailbox_vsize_update *update, bool require_result) { struct mailbox_index_vsize *vsize_hdr = &update->vsize_hdr; struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct mailbox_status status; struct mail *mail; unsigned int mails_left; uint32_t seq1, seq2; uoff_t vsize; int ret = 0; mailbox_get_open_status(update->box, STATUS_UIDNEXT, &status); if (vsize_hdr->highest_uid + 1 >= status.uidnext) { /* nothing to do - we should have usually caught this already before locking */ return 0; } /* note that update->view may be more up-to-date than box->view. we'll just add whatever new mails are in box->view. if we'll notice that some of the new mails are missing, we'll need to stop there since that expunge will be applied later on to the vsize header. */ search_args = mail_search_build_init(); if (!mail_index_lookup_seq_range(update->box->view, vsize_hdr->highest_uid + 1, status.uidnext-1, &seq1, &seq2)) { /* nothing existed, but update uidnext */ vsize_hdr->highest_uid = status.uidnext - 1; mail_search_args_unref(&search_args); return 0; } mail_search_build_add_seqset(search_args, seq1, seq2); trans = mailbox_transaction_begin(update->box, 0); mailbox_transaction_set_reason(trans, "vsize update"); search_ctx = mailbox_search_init(trans, search_args, NULL, MAIL_FETCH_VIRTUAL_SIZE, NULL); if (!require_result) mails_left = 0; else if (update->box->storage->set->mail_vsize_bg_after_count == 0) mails_left = UINT_MAX; else mails_left = update->box->storage->set->mail_vsize_bg_after_count; while (mailbox_search_next(search_ctx, &mail)) { if (mails_left == 0) { /* if there are any more mails whose vsize can't be looked up from cache, abort and finish on background. */ mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; } ret = mail_get_virtual_size(mail, &vsize); mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (ret < 0 && mailbox_get_last_mail_error(update->box) == MAIL_ERROR_LOOKUP_ABORTED) { /* abort and finish on background */ i_assert(mails_left == 0); mail_storage_set_error(update->box->storage, MAIL_ERROR_INUSE, "Finishing vsize calculation on background"); if (require_result) update->finish_in_background = TRUE; break; } if (mail->mail_stream_opened || mail->mail_metadata_accessed) { /* slow vsize lookup */ i_assert(mails_left > 0); mails_left--; } if (ret < 0) { if (mail->expunged) continue; ret = -1; break; } vsize_hdr->vsize += vsize; vsize_hdr->highest_uid = mail->uid; vsize_hdr->message_count++; } if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; mail_search_args_unref(&search_args); if (ret == 0) { /* success, cache all */ vsize_hdr->highest_uid = status.uidnext - 1; } else { /* search failed, cache only up to highest seen uid */ } (void)mailbox_transaction_commit(&trans); return ret; } int index_mailbox_get_virtual_size(struct mailbox *box, struct mailbox_metadata *metadata_r) { struct mailbox_vsize_update *update; struct mailbox_status status; int ret; mailbox_get_open_status(box, STATUS_MESSAGES | STATUS_UIDNEXT, &status); update = index_mailbox_vsize_update_init(box); if (update->vsize_hdr.highest_uid + 1 == status.uidnext && update->vsize_hdr.message_count == status.messages) { /* up to date */ metadata_r->virtual_size = update->vsize_hdr.vsize; index_mailbox_vsize_update_deinit(&update); return 0; } /* we need to update it - lock it if possible. if not, update it anyway internally even though we won't be saving the result. */ (void)index_mailbox_vsize_update_wait_lock(update); ret = index_mailbox_vsize_hdr_add_missing(update, TRUE); metadata_r->virtual_size = update->vsize_hdr.vsize; index_mailbox_vsize_update_deinit(&update); return ret; } int index_mailbox_get_physical_size(struct mailbox *box, struct mailbox_metadata *metadata_r) { struct mailbox_transaction_context *trans; struct mail_search_context *ctx; struct mail *mail; struct mail_search_args *search_args; uoff_t size; int ret = 0; /* if physical size = virtual size always for the storage, we can use the optimized vsize code for this */ if (box->mail_vfuncs->get_physical_size == box->mail_vfuncs->get_virtual_size) { if (index_mailbox_get_virtual_size(box, metadata_r) < 0) return -1; metadata_r->physical_size = metadata_r->virtual_size; return 0; } /* do it the slow way (we could implement similar logic as for vsize, but for now it's not really needed) */ if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) return -1; trans = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(trans, "mailbox physical size"); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); ctx = mailbox_search_init(trans, search_args, NULL, MAIL_FETCH_PHYSICAL_SIZE, NULL); mail_search_args_unref(&search_args); metadata_r->physical_size = 0; while (mailbox_search_next(ctx, &mail)) { if (mail_get_physical_size(mail, &size) == 0) metadata_r->physical_size += size; else { const char *errstr; enum mail_error error; errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_EXPUNGED) { i_error("Couldn't get size of mail UID %u in %s: %s", mail->uid, box->vname, errstr); ret = -1; break; } } } if (mailbox_search_deinit(&ctx) < 0) { i_error("Listing mails in %s failed: %s", box->vname, mailbox_get_last_internal_error(box, NULL)); ret = -1; } (void)mailbox_transaction_commit(&trans); return ret; } void index_mailbox_vsize_update_appends(struct mailbox *box) { struct mailbox_vsize_update *update; struct mailbox_status status; update = index_mailbox_vsize_update_init(box); /* update here only if we don't need to rebuild the whole vsize. */ index_mailbox_vsize_check_rebuild(update); if (index_mailbox_vsize_want_updates(update)) { /* Get the UIDNEXT only after checking that vsize updating is even potentially wanted for this mailbox. We especially don't want to do this with imapc, because it could trigger a remote STATUS (UIDNEXT) call. */ mailbox_get_open_status(update->box, STATUS_UIDNEXT, &status); if (update->vsize_hdr.highest_uid + 1 != status.uidnext && index_mailbox_vsize_update_try_lock(update)) (void)index_mailbox_vsize_hdr_add_missing(update, FALSE); } index_mailbox_vsize_update_deinit(&update); } dovecot-2.2.33.2/src/lib-storage/index/index-search-mime.c0000644000175000017500000003775413165463624020130 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "message-date.h" #include "message-address.h" #include "message-part-data.h" #include "imap-bodystructure.h" #include "mail-search.h" #include "mail-search-mime.h" #include "index-search-private.h" struct search_mimepart_stack { unsigned int index; }; struct search_mimepart_context { pool_t pool; struct index_search_context *index_ctx; /* message parts parsed from BODYSTRUCTURE */ struct message_part *mime_parts, *mime_part; string_t *buf; unsigned int depth, index; ARRAY(struct search_mimepart_stack) stack; }; static void search_mime_arg(struct mail_search_mime_arg *arg, struct search_mimepart_context *mpctx); static int seach_arg_mime_parent_match(struct search_mimepart_context *mpctx, struct mail_search_mime_arg *args) { struct message_part *part = mpctx->mime_part; unsigned int prev_depth, prev_index; struct search_mimepart_stack *level; int ret; if (args->value.subargs == NULL) { /* PARENT EXISTS: matches if this part has a parent. */ return (part->parent != NULL ? 1 : 0); } /* PARENT : matches if this part's parent matches the mpart-key (subargs). */ prev_depth = mpctx->depth; prev_index = mpctx->index; level = array_idx_modifiable (&mpctx->stack, mpctx->depth-1); mpctx->mime_part = part->parent; mail_search_mime_args_reset(args->value.subargs, TRUE); mpctx->index = level->index; mpctx->depth = mpctx->depth-1; ret = mail_search_mime_args_foreach (args->value.subargs, search_mime_arg, mpctx); mpctx->mime_part = part; mpctx->index = prev_index; mpctx->depth = prev_depth; return ret; } static int seach_arg_mime_child_match(struct search_mimepart_context *mpctx, struct mail_search_mime_arg *args) { struct message_part *part, *prev_part; unsigned int prev_depth, prev_index, depth; struct search_mimepart_stack *level; int ret = 0; part = mpctx->mime_part; if (args->value.subargs == NULL) { /* CHILD EXISTS: matches if this part has any children; i.e., it is multipart. */ return (part->children != NULL ? 1 : 0); } /* CHILD : matches if this part has any child that mathes the mpart-key (subargs). */ prev_part = part; prev_depth = mpctx->depth; prev_index = mpctx->index; depth = mpctx->depth; T_BEGIN { ARRAY(struct search_mimepart_stack) prev_stack; /* preserve current stack for any nested CHILD PARENT nastyness */ t_array_init(&prev_stack, 16); array_copy(&prev_stack.arr, 0, &mpctx->stack.arr, 0, array_count(&mpctx->stack)); depth++; if (depth < array_count(&mpctx->stack)) level = array_idx_modifiable(&mpctx->stack, depth); else { i_assert(depth == array_count(&mpctx->stack)); level = array_append_space(&mpctx->stack); } level->index = 1; part = part->children; while (part != NULL) { mpctx->mime_part = part; mail_search_mime_args_reset(args->value.subargs, TRUE); mpctx->depth = depth - prev_depth; mpctx->index = level->index; if ((ret=mail_search_mime_args_foreach (args->value.subargs, search_mime_arg, mpctx)) != 0) break; if (part->children != NULL) { depth++; if (depth < array_count(&mpctx->stack)) level = array_idx_modifiable(&mpctx->stack, depth); else { i_assert(depth == array_count(&mpctx->stack)); level = array_append_space(&mpctx->stack); } level->index = 1; part = part->children; } else { while (part->next == NULL) { if (part->parent == NULL || part->parent == prev_part) break; depth--; level = array_idx_modifiable(&mpctx->stack, depth); part = part->parent; } level->index++; part = part->next; } } array_clear(&mpctx->stack); array_copy(&mpctx->stack.arr, 0, &prev_stack.arr, 0, array_count(&prev_stack)); } T_END; mpctx->mime_part = prev_part; mpctx->index = prev_index; mpctx->depth = prev_depth; return ret; } static int seach_arg_mime_substring_match( struct search_mimepart_context *mpctx ATTR_UNUSED, const char *key, const char *value) { if (value == NULL) return 0; /* FIXME: Normalization is required */ return (strstr(value, key) != NULL ? 1 : 0); } static int seach_arg_mime_envelope_time_match( struct search_mimepart_context *mpctx ATTR_UNUSED, enum mail_search_mime_arg_type type, time_t search_time, const struct message_part_envelope *envelope) { time_t sent_time; int timezone_offset; if (envelope == NULL) return 0; /* NOTE: RFC-3501 specifies that timezone is ignored in searches. sent_time is returned as UTC, so change it. */ // FIXME: adjust comment if (!message_date_parse((const unsigned char *)envelope->date, strlen(envelope->date), &sent_time, &timezone_offset)) return 0; sent_time += timezone_offset * 60; switch (type) { case SEARCH_MIME_SENTBEFORE: return sent_time < search_time ? 1 : 0; case SEARCH_MIME_SENTON: return (sent_time >= search_time && sent_time < search_time + 3600*24) ? 1 : 0; case SEARCH_MIME_SENTSINCE: return sent_time >= search_time ? 1 : 0; default: i_unreached(); } } static int seach_arg_mime_envelope_address_match( struct search_mimepart_context *mpctx ATTR_UNUSED, enum mail_search_mime_arg_type type, const char *key, const struct message_part_envelope *envelope) { const struct message_address *addrs; string_t *addrs_enc; if (envelope == NULL) return 0; switch (type) { case SEARCH_MIME_CC: addrs = envelope->cc; break; case SEARCH_MIME_BCC: addrs = envelope->bcc; break; case SEARCH_MIME_FROM: addrs = envelope->from; break; case SEARCH_MIME_SENDER: addrs = envelope->sender; break; case SEARCH_MIME_REPLY_TO: addrs = envelope->reply_to; break; case SEARCH_MIME_TO: addrs = envelope->to; break; default: i_unreached(); } /* FIXME: do we need to normalize anything? at least case insensitivity. MIME header encoding will make this a bit difficult, so it should probably be normalized directly in the struct message_address. */ addrs_enc = t_str_new(128); message_address_write(addrs_enc, addrs); return (strstr(str_c(addrs_enc), key) != NULL ? 1 : 0); } static int seach_arg_mime_filename_match(struct search_mimepart_context *mpctx, struct mail_search_mime_arg *arg) { struct index_search_context *ictx = mpctx->index_ctx; struct message_part *part = mpctx->mime_part; char *key; const char *value; size_t vlen, alen; if (!message_part_data_get_filename(part, &value)) return 0; if (mpctx->buf == NULL) mpctx->buf = str_new(default_pool, 256); if (arg->context == NULL) { str_truncate(mpctx->buf, 0); if (ictx->mail_ctx.normalizer(arg->value.str, strlen(arg->value.str), mpctx->buf) < 0) i_panic("search key not utf8: %s", arg->value.str); key = i_strdup(str_c(mpctx->buf)); arg->context = (void *)key; } else { key = (char *)arg->context; } str_truncate(mpctx->buf, 0); if (ictx->mail_ctx.normalizer(value, strlen(value), mpctx->buf) >= 0) value = str_c(mpctx->buf); switch (arg->type) { case SEARCH_MIME_FILENAME_IS: return (strcmp(value, key) == 0 ? 1 : 0); case SEARCH_MIME_FILENAME_CONTAINS: return (strstr(value, key) != NULL ? 1 : 0); case SEARCH_MIME_FILENAME_BEGINS: return (strncmp(value, key, strlen(key)) == 0 ? 1 : 0); case SEARCH_MIME_FILENAME_ENDS: vlen = strlen(value); alen = strlen(key); return (strncmp(value + (vlen - alen), key, alen) == 0 ? 1 : 0); default: break; } i_unreached(); } static void search_arg_mime_filename_deinit( struct search_mimepart_context *mpctx ATTR_UNUSED, struct mail_search_mime_arg *arg) { char *key = (char *)arg->context; i_free(key); } static int seach_arg_mime_param_match(const struct message_part_param *params, unsigned int params_count, const char *name, const char *key) { unsigned int i; /* FIXME: Is normalization required? */ for (i = 0; i < params_count; i++) { if (strcasecmp(params[i].name, name) == 0) { if (key == NULL || *key == '\0') return 1; return (strstr(params[i].value, key) != NULL ? 1 : 0); } } return 0; } static int seach_arg_mime_language_match(struct search_mimepart_context *mpctx, const char *key) { struct message_part_data *data = mpctx->mime_part->data; const char *const *lang; i_assert(data != NULL); lang = data->content_language; if (lang != NULL) { while (*lang != NULL) { /* FIXME: Should use RFC 4647 matching rules */ if (strcasecmp(*lang, key) == 0) return 1; lang++; } } return 0; } /* Returns >0 = matched, 0 = not matched (unused), -1 = unknown */ static int search_mime_arg_match(struct search_mimepart_context *mpctx, struct mail_search_mime_arg *arg) { struct message_part *part = mpctx->mime_part; const struct message_part_data *data = part->data; i_assert(data != NULL); switch (arg->type) { case SEARCH_MIME_OR: case SEARCH_MIME_SUB: i_unreached(); case SEARCH_MIME_SIZE_EQUAL: return (part->body_size.virtual_size == arg->value.size ? 1 : 0); case SEARCH_MIME_SIZE_LARGER: return (part->body_size.virtual_size > arg->value.size ? 1 : 0); case SEARCH_MIME_SIZE_SMALLER: return (part->body_size.virtual_size < arg->value.size ? 1 : 0); case SEARCH_MIME_DESCRIPTION: return seach_arg_mime_substring_match(mpctx, arg->value.str, data->content_description); case SEARCH_MIME_DISPOSITION_TYPE: return (data->content_disposition != NULL && strcasecmp(data->content_disposition, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_DISPOSITION_PARAM: return seach_arg_mime_param_match (data->content_disposition_params, data->content_disposition_params_count, arg->field_name, arg->value.str); case SEARCH_MIME_ENCODING: return (data->content_transfer_encoding != NULL && strcasecmp(data->content_transfer_encoding, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_ID: return (data->content_id != NULL && strcasecmp(data->content_id, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_LANGUAGE: return seach_arg_mime_language_match(mpctx, arg->value.str); case SEARCH_MIME_LOCATION: return (data->content_location != NULL && strcasecmp(data->content_location, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_MD5: return (data->content_md5 != NULL && strcmp(data->content_md5, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_TYPE: return (data->content_type != NULL && strcasecmp(data->content_type, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_SUBTYPE: return (data->content_subtype != NULL && strcasecmp(data->content_subtype, arg->value.str) == 0 ? 1 : 0); case SEARCH_MIME_PARAM: return seach_arg_mime_param_match (data->content_type_params, data->content_type_params_count, arg->field_name, arg->value.str); case SEARCH_MIME_SENTBEFORE: case SEARCH_MIME_SENTON: case SEARCH_MIME_SENTSINCE: return seach_arg_mime_envelope_time_match (mpctx, arg->type, arg->value.time, data->envelope); case SEARCH_MIME_CC: case SEARCH_MIME_BCC: case SEARCH_MIME_FROM: case SEARCH_MIME_REPLY_TO: case SEARCH_MIME_SENDER: case SEARCH_MIME_TO: return seach_arg_mime_envelope_address_match (mpctx, arg->type, arg->value.str, data->envelope); case SEARCH_MIME_SUBJECT: if (data->envelope == NULL) return 0; return seach_arg_mime_substring_match(mpctx, arg->value.str, data->envelope->subject); case SEARCH_MIME_IN_REPLY_TO: if (data->envelope == NULL) return 0; return seach_arg_mime_substring_match(mpctx, arg->value.str, data->envelope->in_reply_to); case SEARCH_MIME_MESSAGE_ID: if (data->envelope == NULL) return 0; return seach_arg_mime_substring_match(mpctx, arg->value.str, data->envelope->message_id); case SEARCH_MIME_DEPTH_EQUAL: return (mpctx->depth == arg->value.number ? 1 : 0); case SEARCH_MIME_DEPTH_MIN: return (mpctx->depth >= arg->value.number ? 1 : 0); case SEARCH_MIME_DEPTH_MAX: return (mpctx->depth <= arg->value.number ? 1 : 0); case SEARCH_MIME_INDEX: return (mpctx->index == arg->value.number ? 1 : 0); case SEARCH_MIME_PARENT: return seach_arg_mime_parent_match(mpctx, arg); case SEARCH_MIME_CHILD: return seach_arg_mime_child_match(mpctx, arg); case SEARCH_MIME_FILENAME_IS: case SEARCH_MIME_FILENAME_CONTAINS: case SEARCH_MIME_FILENAME_BEGINS: case SEARCH_MIME_FILENAME_ENDS: return seach_arg_mime_filename_match(mpctx, arg); case SEARCH_MIME_HEADER: case SEARCH_MIME_BODY: case SEARCH_MIME_TEXT: break; } return -1; } static void search_mime_arg(struct mail_search_mime_arg *arg, struct search_mimepart_context *mpctx) { switch (search_mime_arg_match(mpctx, arg)) { case -1: /* unknown */ break; case 0: ARG_SET_RESULT(arg, 0); break; default: ARG_SET_RESULT(arg, 1); break; } } static int seach_arg_mime_parts_match(struct search_mimepart_context *mpctx, struct mail_search_mime_arg *args, struct message_part *parts) { struct message_part *part; struct search_mimepart_stack *level; int ret; level = array_append_space(&mpctx->stack); level->index = 1; part = parts; while (part != NULL) { mpctx->mime_part = part; mail_search_mime_args_reset(args, TRUE); mpctx->index = level->index; mpctx->depth = array_count(&mpctx->stack)-1; if ((ret=mail_search_mime_args_foreach (args, search_mime_arg, mpctx)) != 0) return ret; if (part->children != NULL) { level = array_append_space(&mpctx->stack); level->index = 1; part = part->children; } else { while (part->next == NULL) { if (part->parent == NULL) break; array_delete(&mpctx->stack, array_count(&mpctx->stack)-1, 1); level = array_idx_modifiable (&mpctx->stack, array_count(&mpctx->stack)-1); part = part->parent; } level->index++; part = part->next; } } return 0; } /* Returns >0 = matched, 0 = not matched, -1 = unknown */ static int search_arg_match_mimepart(struct search_mimepart_context *mpctx, struct mail_search_arg *arg) { struct index_search_context *ctx = mpctx->index_ctx; const char *bodystructure, *error; if (arg->type != SEARCH_MIMEPART) return -1; if (mpctx->pool == NULL) { mpctx->pool = pool_alloconly_create (MEMPOOL_GROWING"search mime parts", 4096); p_array_init(&mpctx->stack, mpctx->pool, 16); } if (mpctx->mime_parts == NULL) { /* FIXME: could the mail object already have message_part tree with data? */ if (mail_get_special(ctx->cur_mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &bodystructure) < 0) return -1; if (imap_bodystructure_parse_full(bodystructure, mpctx->pool, &mpctx->mime_parts, &error) < 0) return -1; } /* FIXME: implement HEADER, BODY and TEXT (not from BODYSTRUCTURE) Needs to support FTS */ return seach_arg_mime_parts_match (mpctx, arg->value.mime_part->args, mpctx->mime_parts); } static void search_mimepart_arg(struct mail_search_arg *arg, struct search_mimepart_context *mpctx) { switch (search_arg_match_mimepart(mpctx, arg)) { case -1: /* unknown */ break; case 0: ARG_SET_RESULT(arg, 0); break; default: ARG_SET_RESULT(arg, 1); break; } } int index_search_mime_arg_match(struct mail_search_arg *args, struct index_search_context *ctx) { struct search_mimepart_context mpctx; int ret; i_zero(&mpctx); mpctx.index_ctx = ctx; ret = mail_search_args_foreach(args, search_mimepart_arg, &mpctx); if (mpctx.pool != NULL) pool_unref(&mpctx.pool); if (mpctx.buf != NULL) str_free(&mpctx.buf); return ret; } static void search_mime_arg_deinit(struct mail_search_mime_arg *arg, struct search_mimepart_context *mpctx ATTR_UNUSED) { switch (arg->type) { case SEARCH_MIME_FILENAME_IS: case SEARCH_MIME_FILENAME_CONTAINS: case SEARCH_MIME_FILENAME_BEGINS: case SEARCH_MIME_FILENAME_ENDS: search_arg_mime_filename_deinit(mpctx, arg); break; default: break; } } void index_search_mime_arg_deinit(struct mail_search_arg *arg, struct index_search_context *ctx) { struct search_mimepart_context mpctx; struct mail_search_mime_arg *args; i_assert(arg->type == SEARCH_MIMEPART); args = arg->value.mime_part->args; i_zero(&mpctx); mpctx.index_ctx = ctx; mail_search_mime_args_reset(args, TRUE); (void)mail_search_mime_args_foreach(args, search_mime_arg_deinit, &mpctx); } dovecot-2.2.33.2/src/lib-storage/index/index-search-private.h0000644000175000017500000000226513165463624020645 00000000000000#ifndef INDEX_SEARCH_PRIVATE_H #define INDEX_SEARCH_PRIVATE_H #include "mail-storage-private.h" #include struct mail_search_mime_part; struct imap_message_part; struct index_search_context { struct mail_search_context mail_ctx; struct mail_index_view *view; struct mailbox *box; uint32_t pvt_uid, pvt_seq; enum mail_fetch_field extra_wanted_fields; struct mailbox_header_lookup_ctx *extra_wanted_headers; uint32_t seq1, seq2; struct mail *cur_mail; struct index_mail *cur_imail; struct mail_thread_context *thread_ctx; ARRAY(struct mail *) mails; unsigned int unused_mail_idx; unsigned int max_mails; struct timeval search_start_time, last_notify; struct timeval last_nonblock_timeval; unsigned long long cost, next_time_check_cost; unsigned int failed:1; unsigned int sorted:1; unsigned int have_seqsets:1; unsigned int have_index_args:1; unsigned int have_mailbox_args:1; }; struct mail *index_search_get_mail(struct index_search_context *ctx); int index_search_mime_arg_match(struct mail_search_arg *args, struct index_search_context *ctx); void index_search_mime_arg_deinit(struct mail_search_arg *arg, struct index_search_context *ctx); #endif dovecot-2.2.33.2/src/lib-storage/index/index-sync-changes.c0000644000175000017500000001155513123174404020274 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "index-storage.h" #include "index-sync-changes.h" struct index_sync_changes_context { struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *sync_trans; ARRAY(struct mail_index_sync_rec) syncs; struct mail_index_sync_rec sync_rec; bool dirty_flag_updates; }; struct index_sync_changes_context * index_sync_changes_init(struct mail_index_sync_ctx *index_sync_ctx, struct mail_index_view *sync_view, struct mail_index_transaction *sync_trans, bool dirty_flag_updates) { struct index_sync_changes_context *ctx; ctx = i_new(struct index_sync_changes_context, 1); ctx->index_sync_ctx = index_sync_ctx; ctx->sync_view = sync_view; ctx->sync_trans = sync_trans; ctx->dirty_flag_updates = dirty_flag_updates; i_array_init(&ctx->syncs, 16); return ctx; } void index_sync_changes_deinit(struct index_sync_changes_context **_ctx) { struct index_sync_changes_context *ctx = *_ctx; *_ctx = NULL; array_free(&ctx->syncs); i_free(ctx); } void index_sync_changes_reset(struct index_sync_changes_context *ctx) { array_clear(&ctx->syncs); i_zero(&ctx->sync_rec); } void index_sync_changes_delete_to(struct index_sync_changes_context *ctx, uint32_t last_uid) { struct mail_index_sync_rec *syncs; unsigned int src, dest, count; syncs = array_get_modifiable(&ctx->syncs, &count); for (src = dest = 0; src < count; src++) { i_assert(last_uid >= syncs[src].uid1); if (last_uid <= syncs[src].uid2) { /* keep it */ if (src != dest) syncs[dest] = syncs[src]; dest++; } } array_delete(&ctx->syncs, dest, count - dest); } static bool index_sync_changes_have_expunges(struct index_sync_changes_context *ctx, unsigned int count, guid_128_t expunged_guid_128_r) { const struct mail_index_sync_rec *syncs; unsigned int i; syncs = array_idx(&ctx->syncs, 0); for (i = 0; i < count; i++) { if (syncs[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { memcpy(expunged_guid_128_r, syncs[i].guid_128, GUID_128_SIZE); return TRUE; } } return FALSE; } void index_sync_changes_read(struct index_sync_changes_context *ctx, uint32_t uid, bool *sync_expunge_r, guid_128_t expunged_guid_128_r) { struct mail_index_sync_rec *sync_rec = &ctx->sync_rec; uint32_t seq1, seq2; unsigned int orig_count; *sync_expunge_r = FALSE; index_sync_changes_delete_to(ctx, uid); orig_count = array_count(&ctx->syncs); while (uid >= sync_rec->uid1) { if (uid <= sync_rec->uid2) { array_append(&ctx->syncs, sync_rec, 1); if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { *sync_expunge_r = TRUE; memcpy(expunged_guid_128_r, sync_rec->guid_128, GUID_128_SIZE); } } if (!mail_index_sync_next(ctx->index_sync_ctx, sync_rec)) { i_zero(sync_rec); break; } switch (sync_rec->type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: break; case MAIL_INDEX_SYNC_TYPE_FLAGS: case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: if (!ctx->dirty_flag_updates) break; /* mark the changes as dirty */ (void)mail_index_lookup_seq_range(ctx->sync_view, sync_rec->uid1, sync_rec->uid2, &seq1, &seq2); i_zero(sync_rec); if (seq1 == 0) break; mail_index_update_flags_range(ctx->sync_trans, seq1, seq2, MODIFY_ADD, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY); break; } } if (!*sync_expunge_r && orig_count > 0) { *sync_expunge_r = index_sync_changes_have_expunges(ctx, orig_count, expunged_guid_128_r); } } bool index_sync_changes_have(struct index_sync_changes_context *ctx) { return array_count(&ctx->syncs) > 0; } uint32_t index_sync_changes_get_next_uid(struct index_sync_changes_context *ctx) { return ctx->sync_rec.uid1; } void index_sync_changes_apply(struct index_sync_changes_context *ctx, pool_t pool, uint8_t *flags, ARRAY_TYPE(keyword_indexes) *keywords, enum mail_index_sync_type *sync_type_r) { const struct mail_index_sync_rec *syncs; unsigned int i, count; enum mail_index_sync_type sync_type = 0; syncs = array_get(&ctx->syncs, &count); for (i = 0; i < count; i++) { switch (syncs[i].type) { case MAIL_INDEX_SYNC_TYPE_FLAGS: mail_index_sync_flags_apply(&syncs[i], flags); sync_type |= MAIL_INDEX_SYNC_TYPE_FLAGS; break; case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: if (!array_is_created(keywords)) { /* no existing keywords */ if (syncs[i].type != MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD) break; /* adding, create the array */ p_array_init(keywords, pool, I_MIN(10, count - i)); } if (mail_index_sync_keywords_apply(&syncs[i], keywords)) sync_type |= syncs[i].type; break; default: break; } } *sync_type_r = sync_type; } dovecot-2.2.33.2/src/lib-storage/index/mbox/0002755000175000017500000000000013172375611015470 500000000000000dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync-parse.c0000644000175000017500000003763013123174404020603 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ /* MD5 header summing logic was pretty much copy&pasted from popa3d by Solar Designer */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "write-full.h" #include "message-parser.h" #include "mail-index.h" #include "mbox-storage.h" #include "mbox-md5.h" #include "mbox-sync-private.h" #define IS_LWSP_LF(c) (IS_LWSP(c) || (c) == '\n') struct mbox_sync_header_func { const char *header; bool (*func)(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr); }; struct mbox_flag_type mbox_status_flags[] = { { 'R', MAIL_SEEN }, { 'O', MBOX_NONRECENT_KLUDGE }, { 0, 0 } }; struct mbox_flag_type mbox_xstatus_flags[] = { { 'A', MAIL_ANSWERED }, { 'F', MAIL_FLAGGED }, { 'T', MAIL_DRAFT }, { 'D', MAIL_DELETED }, { 0, 0 } }; static void parse_trailing_whitespace(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { size_t i, space = 0; /* the value may contain newlines. we can't count whitespace before and after it as a single contiguous whitespace block, as that may get us into situation where removing whitespace goes eg. " \n \n" -> " \n\n" which would then be treated as end of headers. that could probably be avoided by being careful, but as newlines should never be there (we don't generate them), it's not worth the trouble. */ for (i = hdr->full_value_len; i > 0; i--) { if (!IS_LWSP(hdr->full_value[i-1])) break; space++; } if ((ssize_t)space > ctx->mail.space) { i_assert(space != 0); ctx->mail.offset = ctx->hdr_offset + str_len(ctx->header) + i; ctx->mail.space = space; } } static enum mail_flags mbox_flag_find(struct mbox_flag_type *flags, char chr) { int i; for (i = 0; flags[i].chr != 0; i++) { if (flags[i].chr == chr) return flags[i].flag; } return 0; } static bool parse_status_flags(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr, struct mbox_flag_type *flags_list) { enum mail_flags flag; size_t i; bool duplicates = FALSE; ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; for (i = 0; i < hdr->full_value_len; i++) { flag = mbox_flag_find(flags_list, hdr->full_value[i]); if ((ctx->mail.flags & flag) != 0) duplicates = TRUE; else ctx->mail.flags |= flag; } ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; return duplicates; } static bool parse_status(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { if (parse_status_flags(ctx, hdr, mbox_status_flags)) ctx->mail.status_broken = TRUE; ctx->hdr_pos[MBOX_HDR_STATUS] = str_len(ctx->header); return TRUE; } static bool parse_x_status(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { if (parse_status_flags(ctx, hdr, mbox_xstatus_flags)) ctx->mail.xstatus_broken = TRUE; ctx->hdr_pos[MBOX_HDR_X_STATUS] = str_len(ctx->header); return TRUE; } static void parse_imap_keywords_list(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr, size_t pos) { struct mailbox *box = &ctx->sync_ctx->mbox->box; struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); const char *keyword, *error; size_t keyword_start; unsigned int idx, count; count = 0; while (pos < hdr->full_value_len) { if (IS_LWSP_LF(hdr->full_value[pos])) { pos++; continue; } /* read the keyword */ keyword_start = pos; for (; pos < hdr->full_value_len; pos++) { if (IS_LWSP_LF(hdr->full_value[pos])) break; } /* add it to index's keyword list if it's not there already */ keyword = t_strndup(hdr->full_value + keyword_start, pos - keyword_start); if (mailbox_keyword_is_valid(&ctx->sync_ctx->mbox->box, keyword, &error)) { mail_index_keyword_lookup_or_create(box->index, keyword, &idx); } count++; } if (count != array_count(ibox->keyword_names)) { /* need to update this list */ ctx->imapbase_rewrite = TRUE; ctx->need_rewrite = TRUE; } } static bool parse_x_imap_base(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { size_t i, j, uid_last_pos; uint32_t uid_validity, uid_last; if (ctx->seq != 1 || ctx->seen_imapbase || ctx->sync_ctx->renumber_uids) { /* Valid only in first message */ return FALSE; } /* 10x */ for (i = 0, uid_validity = 0; i < hdr->full_value_len; i++) { if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') { if (hdr->full_value[i] != ' ') return FALSE; break; } uid_validity = uid_validity * 10 + (hdr->full_value[i] - '0'); } if (uid_validity == 0) { /* broken */ return FALSE; } for (; i < hdr->full_value_len; i++) { if (!IS_LWSP_LF(hdr->full_value[i])) break; } uid_last_pos = i; for (uid_last = 0, j = 0; i < hdr->full_value_len; i++, j++) { if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') { if (!IS_LWSP_LF(hdr->full_value[i])) return FALSE; break; } uid_last = uid_last * 10 + (hdr->full_value[i] - '0'); } if (j != 10 || hdr->full_value_offset != ctx->hdr_offset + str_len(ctx->header)) { /* uid-last field must be exactly 10 characters to make rewriting it easier. also don't try to do this if some headers have been removed */ ctx->imapbase_rewrite = TRUE; ctx->need_rewrite = TRUE; } else { ctx->last_uid_value_start_pos = uid_last_pos; ctx->sync_ctx->base_uid_last_offset = hdr->full_value_offset + uid_last_pos; } if (ctx->sync_ctx->base_uid_validity == 0) { /* first time parsing this (ie. we're not rewriting). save the values. */ ctx->sync_ctx->base_uid_validity = uid_validity; ctx->sync_ctx->base_uid_last = uid_last; if (ctx->sync_ctx->next_uid-1 <= uid_last) { /* new messages have been added since our last sync. just update our internal next_uid. */ ctx->sync_ctx->next_uid = uid_last+1; } else { /* we need to rewrite the next-uid */ ctx->need_rewrite = TRUE; } i_assert(ctx->sync_ctx->next_uid > ctx->sync_ctx->prev_msg_uid); } ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header); ctx->seen_imapbase = TRUE; T_BEGIN { parse_imap_keywords_list(ctx, hdr, i); } T_END; parse_trailing_whitespace(ctx, hdr); return TRUE; } static bool parse_x_imap(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { if (!parse_x_imap_base(ctx, hdr)) return FALSE; /* this is the c-client style "FOLDER INTERNAL DATA" message. skip it. */ ctx->mail.pseudo = TRUE; return TRUE; } static bool parse_x_keywords_real(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { struct mailbox *box = &ctx->sync_ctx->mbox->box; ARRAY_TYPE(keyword_indexes) keyword_list; const unsigned int *list; string_t *keyword; size_t keyword_start; unsigned int i, idx, count; size_t pos; if (array_is_created(&ctx->mail.keywords)) return FALSE; /* duplicate header, delete */ /* read keyword indexes to temporary array first */ keyword = t_str_new(128); t_array_init(&keyword_list, 16); for (pos = 0; pos < hdr->full_value_len; ) { if (IS_LWSP_LF(hdr->full_value[pos])) { pos++; continue; } /* read the keyword string */ keyword_start = pos; for (; pos < hdr->full_value_len; pos++) { if (IS_LWSP_LF(hdr->full_value[pos])) break; } str_truncate(keyword, 0); str_append_n(keyword, hdr->full_value + keyword_start, pos - keyword_start); if (!mail_index_keyword_lookup(box->index, str_c(keyword), &idx)) { /* keyword wasn't found. that means the sent mail originally contained X-Keywords header. Delete it. */ return FALSE; } /* check that the keyword isn't already added there. we don't want duplicates. */ list = array_get(&keyword_list, &count); for (i = 0; i < count; i++) { if (list[i] == idx) break; } if (i == count) array_append(&keyword_list, &idx, 1); } /* once we know how many keywords there are, we can allocate the array from mail_keyword_pool without wasting memory. */ if (array_count(&keyword_list) > 0) { p_array_init(&ctx->mail.keywords, ctx->sync_ctx->mail_keyword_pool, array_count(&keyword_list)); array_append_array(&ctx->mail.keywords, &keyword_list); } ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header); parse_trailing_whitespace(ctx, hdr); return TRUE; } static bool parse_x_keywords(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { bool ret; T_BEGIN { ret = parse_x_keywords_real(ctx, hdr); } T_END; return ret; } static bool parse_x_uid(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { uint32_t value = 0; size_t i; if (ctx->mail.uid != 0) { /* duplicate */ return FALSE; } for (i = 0; i < hdr->full_value_len; i++) { if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') break; value = value*10 + (hdr->full_value[i] - '0'); } for (; i < hdr->full_value_len; i++) { if (!IS_LWSP_LF(hdr->full_value[i])) { /* broken value */ return FALSE; } } if (ctx->sync_ctx == NULL) { /* we're in mbox_sync_parse_match_mail(). don't do any extra checks. */ ctx->mail.uid = value; return TRUE; } if (ctx->seq == 1 && !ctx->seen_imapbase) { /* Don't bother allowing X-UID before X-IMAPbase header. c-client doesn't allow it either, and this way the UID doesn't have to be reset if X-IMAPbase header isn't what we expect it to be. */ return FALSE; } if (value == ctx->sync_ctx->next_uid) { /* X-UID is the next expected one. allow it because we'd just use this UID anyway. X-IMAPbase header still needs to be updated for this. */ ctx->sync_ctx->next_uid++; } else if (value > ctx->sync_ctx->next_uid) { /* UID is larger than expected. Don't allow it because incoming mails can contain untrusted X-UID fields, causing possibly DoS if the UIDs get large enough. */ ctx->mail.uid_broken = TRUE; return FALSE; } if (value <= ctx->sync_ctx->prev_msg_uid) { /* broken - UIDs must be growing */ ctx->mail.uid_broken = TRUE; return FALSE; } ctx->mail.uid = value; /* if we had multiple X-UID headers, we could have uid_broken=TRUE here. */ ctx->mail.uid_broken = FALSE; if (ctx->sync_ctx->dest_first_mail && ctx->seq != 1) { /* if we're expunging the first mail, delete this header since otherwise X-IMAPbase header would be added after this, which we don't like */ return FALSE; } ctx->hdr_pos[MBOX_HDR_X_UID] = str_len(ctx->header); ctx->parsed_uid = value; parse_trailing_whitespace(ctx, hdr); return TRUE; } static bool parse_content_length(struct mbox_sync_mail_context *ctx, struct message_header_line *hdr) { uoff_t value = 0; size_t i; if (ctx->content_length != (uoff_t)-1) { /* duplicate */ return FALSE; } for (i = 0; i < hdr->full_value_len; i++) { if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') break; value = value*10 + (hdr->full_value[i] - '0'); } for (; i < hdr->full_value_len; i++) { if (!IS_LWSP_LF(hdr->full_value[i])) { /* broken value */ return FALSE; } } ctx->content_length = value; return TRUE; } static struct mbox_sync_header_func header_funcs[] = { { "Content-Length", parse_content_length }, { "Status", parse_status }, { "X-IMAP", parse_x_imap }, { "X-IMAPbase", parse_x_imap_base }, { "X-Keywords", parse_x_keywords }, { "X-Status", parse_x_status }, { "X-UID", parse_x_uid } }; static int mbox_sync_bsearch_header_func_cmp(const void *p1, const void *p2) { const char *key = p1; const struct mbox_sync_header_func *func = p2; return strcasecmp(key, func->header); } int mbox_sync_parse_next_mail(struct istream *input, struct mbox_sync_mail_context *ctx) { struct mbox_sync_context *sync_ctx = ctx->sync_ctx; struct message_header_parser_ctx *hdr_ctx; struct message_header_line *hdr; struct mbox_sync_header_func *func; struct mbox_md5_context *mbox_md5_ctx; size_t line_start_pos; int i, ret; ctx->hdr_offset = ctx->mail.offset; ctx->mail.flags = MAIL_RECENT; /* default to having recent flag */ ctx->header_first_change = (size_t)-1; ctx->header_last_change = 0; for (i = 0; i < MBOX_HDR_COUNT; i++) ctx->hdr_pos[i] = (size_t)-1; ctx->content_length = (uoff_t)-1; str_truncate(ctx->header, 0); mbox_md5_ctx = ctx->sync_ctx->mbox->md5_v.init(); line_start_pos = 0; hdr_ctx = message_parse_header_init(input, NULL, 0); while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) { if (hdr->eoh) { ctx->have_eoh = TRUE; break; } if (!hdr->continued) { line_start_pos = str_len(ctx->header); str_append(ctx->header, hdr->name); str_append_n(ctx->header, hdr->middle, hdr->middle_len); } func = bsearch(hdr->name, header_funcs, N_ELEMENTS(header_funcs), sizeof(*header_funcs), mbox_sync_bsearch_header_func_cmp); if (func != NULL) { if (hdr->continues) { hdr->use_full_value = TRUE; continue; } if (!func->func(ctx, hdr)) { /* this header is broken, remove it */ ctx->need_rewrite = TRUE; str_truncate(ctx->header, line_start_pos); if (ctx->header_first_change == (size_t)-1) { ctx->header_first_change = line_start_pos; } continue; } buffer_append(ctx->header, hdr->full_value, hdr->full_value_len); } else { ctx->sync_ctx->mbox->md5_v.more(mbox_md5_ctx, hdr); buffer_append(ctx->header, hdr->value, hdr->value_len); } if (!hdr->no_newline) { if (hdr->crlf_newline) str_append_c(ctx->header, '\r'); str_append_c(ctx->header, '\n'); } } i_assert(ret != 0); message_parse_header_deinit(&hdr_ctx); ctx->sync_ctx->mbox->md5_v.finish(mbox_md5_ctx, ctx->hdr_md5_sum); if ((ctx->seq == 1 && !ctx->seen_imapbase) || (ctx->seq > 1 && sync_ctx->dest_first_mail)) { /* missing X-IMAPbase */ ctx->need_rewrite = TRUE; if (sync_ctx->base_uid_validity == 0) { /* figure out a new UIDVALIDITY for us. */ sync_ctx->base_uid_validity = sync_ctx->hdr->uid_validity != 0 && !sync_ctx->renumber_uids ? sync_ctx->hdr->uid_validity : I_MAX((uint32_t)ioloop_time, 1); } } ctx->body_offset = input->v_offset; if (input->stream_errno != 0) { mbox_sync_set_critical(ctx->sync_ctx, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return -1; } return 0; } bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq) { struct mbox_sync_mail_context ctx; struct message_header_parser_ctx *hdr_ctx; struct message_header_line *hdr; struct header_func *func; struct mbox_md5_context *mbox_md5_ctx; const void *data; bool expunged; uint32_t uid; int ret; /* we only wish to be sure that this mail actually is what we expect it to be. If there's X-UID header and it matches our UID, we use it. Otherwise it could mean that the X-UID header is invalid and it's just not yet been rewritten. In that case use MD5 sum, if it exists. */ mail_index_lookup_uid(view, seq, &uid); i_zero(&ctx); mbox_md5_ctx = mbox->md5_v.init(); hdr_ctx = message_parse_header_init(mbox->mbox_stream, NULL, 0); while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) { if (hdr->eoh) break; func = bsearch(hdr->name, header_funcs, N_ELEMENTS(header_funcs), sizeof(*header_funcs), mbox_sync_bsearch_header_func_cmp); if (func != NULL) { if (strcasecmp(hdr->name, "X-UID") == 0) { if (hdr->continues) { hdr->use_full_value = TRUE; continue; } (void)parse_x_uid(&ctx, hdr); if (ctx.mail.uid == uid) break; } } else { mbox->md5_v.more(mbox_md5_ctx, hdr); } } i_assert(ret != 0); message_parse_header_deinit(&hdr_ctx); mbox->md5_v.finish(mbox_md5_ctx, ctx.hdr_md5_sum); if (ctx.mail.uid == uid) return TRUE; /* match by MD5 sum */ mbox->mbox_save_md5 = TRUE; mail_index_lookup_ext(view, seq, mbox->md5hdr_ext_idx, &data, &expunged); return data == NULL ? 0 : memcmp(data, ctx.hdr_md5_sum, 16) == 0; } dovecot-2.2.33.2/src/lib-storage/index/mbox/Makefile.in0000644000175000017500000005746413172375573017502 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-storage/index/mbox ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstorage_mbox_la_LIBADD = am_libstorage_mbox_la_OBJECTS = istream-raw-mbox.lo mbox-file.lo \ mbox-lock.lo mbox-mail.lo mbox-md5-apop3d.lo mbox-md5-all.lo \ mbox-save.lo mbox-settings.lo mbox-sync-list-index.lo \ mbox-sync-parse.lo mbox-sync-rewrite.lo mbox-sync-update.lo \ mbox-sync.lo mbox-storage.lo libstorage_mbox_la_OBJECTS = $(am_libstorage_mbox_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstorage_mbox_la_SOURCES) DIST_SOURCES = $(libstorage_mbox_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstorage_mbox.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_mbox_la_SOURCES = \ istream-raw-mbox.c \ mbox-file.c \ mbox-lock.c \ mbox-mail.c \ mbox-md5-apop3d.c \ mbox-md5-all.c \ mbox-save.c \ mbox-settings.c \ mbox-sync-list-index.c \ mbox-sync-parse.c \ mbox-sync-rewrite.c \ mbox-sync-update.c \ mbox-sync.c \ mbox-storage.c headers = \ istream-raw-mbox.h \ mbox-file.h \ mbox-lock.h \ mbox-md5.h \ mbox-settings.h \ mbox-storage.h \ mbox-sync-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/mbox/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-storage/index/mbox/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstorage_mbox.la: $(libstorage_mbox_la_OBJECTS) $(libstorage_mbox_la_DEPENDENCIES) $(EXTRA_libstorage_mbox_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstorage_mbox_la_OBJECTS) $(libstorage_mbox_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-raw-mbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-lock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-md5-all.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-md5-apop3d.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-sync-list-index.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-sync-parse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-sync-rewrite.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-sync-update.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-sync.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-save.c0000644000175000017500000005171013165463624017463 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "base64.h" #include "hostpid.h" #include "randgen.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "write-full.h" #include "istream-header-filter.h" #include "istream-crlf.h" #include "istream-concat.h" #include "message-parser.h" #include "mail-user.h" #include "index-mail.h" #include "mbox-storage.h" #include "mbox-file.h" #include "mbox-from.h" #include "mbox-lock.h" #include "mbox-md5.h" #include "mbox-sync-private.h" #include #include #include #include #include #define MBOX_DELIVERY_ID_RAND_BYTES (64/8) struct mbox_save_context { struct mail_save_context ctx; struct mbox_mailbox *mbox; struct mail_index_transaction *trans; uoff_t append_offset, mail_offset; time_t orig_atime; string_t *headers; size_t space_end_idx; uint32_t seq, next_uid, uid_validity; struct istream *input; struct ostream *output; uoff_t extra_hdr_offset, eoh_offset; char last_char; struct mbox_md5_context *mbox_md5_ctx; char *x_delivery_id_header; unsigned int synced:1; unsigned int failed:1; unsigned int finished:1; }; static void write_error(struct mbox_save_context *ctx) { mbox_set_syscall_error(ctx->mbox, "write()"); ctx->failed = TRUE; } static int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset) { struct stat st; char ch; int fd; if (ctx->mbox->mbox_writeonly) { *offset = 0; return 0; } fd = ctx->mbox->mbox_fd; if (fstat(fd, &st) < 0) { mbox_set_syscall_error(ctx->mbox, "fstat()"); return -1; } ctx->orig_atime = st.st_atime; *offset = (uoff_t)st.st_size; if (st.st_size == 0) return 0; if (lseek(fd, st.st_size-1, SEEK_SET) < 0) { mbox_set_syscall_error(ctx->mbox, "lseek()"); return -1; } if (read(fd, &ch, 1) != 1) { mbox_set_syscall_error(ctx->mbox, "read()"); return -1; } if (ch != '\n') { if (write_full(fd, "\n", 1) < 0) { write_error(ctx); return -1; } *offset += 1; } return 0; } static int mbox_append_lf(struct mbox_save_context *ctx) { if (o_stream_send(ctx->output, "\n", 1) < 0) { write_error(ctx); return -1; } return 0; } static int write_from_line(struct mbox_save_context *ctx, time_t received_date, const char *from_envelope) { int ret; T_BEGIN { const char *line; if (from_envelope == NULL) { struct mail_storage *storage = &ctx->mbox->storage->storage; from_envelope = strchr(storage->user->username, '@') != NULL ? storage->user->username : t_strconcat(storage->user->username, "@", my_hostdomain(), NULL); } else if (*from_envelope == '\0') { /* can't write empty envelope */ from_envelope = "MAILER-DAEMON"; } /* save in local timezone, no matter what it was given with */ line = mbox_from_create(from_envelope, received_date); if ((ret = o_stream_send_str(ctx->output, line)) < 0) write_error(ctx); } T_END; return ret; } static int mbox_write_content_length(struct mbox_save_context *ctx) { uoff_t end_offset; const char *str; size_t len; i_assert(ctx->eoh_offset != (uoff_t)-1); if (ctx->mbox->mbox_writeonly) { /* we can't seek, don't set Content-Length */ return 0; } end_offset = ctx->output->offset; /* write Content-Length headers */ str = t_strdup_printf("\nContent-Length: %s", dec2str(end_offset - ctx->eoh_offset)); len = strlen(str); /* flush manually here so that we don't confuse seek() errors with buffer flushing errors */ if (o_stream_flush(ctx->output) < 0) { write_error(ctx); return -1; } if (o_stream_seek(ctx->output, ctx->extra_hdr_offset + ctx->space_end_idx - len) < 0) { mbox_set_syscall_error(ctx->mbox, "lseek()"); return -1; } if (o_stream_send(ctx->output, str, len) < 0 || o_stream_flush(ctx->output) < 0) { write_error(ctx); return -1; } if (o_stream_seek(ctx->output, end_offset) < 0) { mbox_set_syscall_error(ctx->mbox, "lseek()"); return -1; } return 0; } static void mbox_save_init_sync(struct mailbox_transaction_context *t) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->box; struct mbox_save_context *ctx = (struct mbox_save_context *)t->save_ctx; const struct mail_index_header *hdr; struct mail_index_view *view; /* open a new view to get the header. this is required if we just synced the mailbox so we can get updated next_uid. */ mail_index_refresh(mbox->box.index); view = mail_index_view_open(mbox->box.index); hdr = mail_index_get_header(view); ctx->next_uid = hdr->next_uid; ctx->uid_validity = hdr->uid_validity; ctx->synced = TRUE; mail_index_view_close(&view); } static void status_flags_append(string_t *str, enum mail_flags flags, const struct mbox_flag_type *flags_list) { int i; flags ^= MBOX_NONRECENT_KLUDGE; for (i = 0; flags_list[i].chr != 0; i++) { if ((flags & flags_list[i].flag) != 0) str_append_c(str, flags_list[i].chr); } } static void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags) { /* write the Status: header always. It always gets added soon anyway. */ str_append(str, "Status: "); status_flags_append(str, flags, mbox_status_flags); str_append_c(str, '\n'); if ((flags & XSTATUS_FLAGS_MASK) != 0) { str_append(str, "X-Status: "); status_flags_append(str, flags, mbox_xstatus_flags); str_append_c(str, '\n'); } } static void mbox_save_append_keyword_headers(struct mbox_save_context *ctx, struct mail_keywords *keywords) { unsigned char space[MBOX_HEADER_PADDING+1 + sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN]; const ARRAY_TYPE(keywords) *keyword_names_list; const char *const *keyword_names; unsigned int i, count, keyword_names_count; keyword_names_list = mail_index_get_keywords(ctx->mbox->box.index); keyword_names = array_get(keyword_names_list, &keyword_names_count); str_append(ctx->headers, "X-Keywords:"); count = keywords == NULL ? 0 : keywords->count; for (i = 0; i < count; i++) { i_assert(keywords->idx[i] < keyword_names_count); str_append_c(ctx->headers, ' '); str_append(ctx->headers, keyword_names[keywords->idx[i]]); } memset(space, ' ', sizeof(space)); str_append_n(ctx->headers, space, sizeof(space)); ctx->space_end_idx = str_len(ctx->headers); str_append_c(ctx->headers, '\n'); } static int mbox_save_init_file(struct mbox_save_context *ctx, struct mbox_transaction_context *t) { struct mailbox_transaction_context *_t = &t->t; struct mbox_mailbox *mbox = ctx->mbox; struct mail_storage *storage = &mbox->storage->storage; int ret; if (mbox_is_backend_readonly(ctx->mbox)) { mail_storage_set_error(storage, MAIL_ERROR_PERM, "Read-only mbox"); return -1; } if (ctx->append_offset == (uoff_t)-1) { /* first appended mail in this transaction */ if (t->write_lock_id == 0) { if (mbox_lock(mbox, F_WRLCK, &t->write_lock_id) <= 0) return -1; } if (mbox->mbox_fd == -1) { if (mbox_file_open(mbox) < 0) return -1; } /* update mbox_sync_dirty state */ ret = mbox_sync_has_changed(mbox, TRUE); if (ret < 0) return -1; } if (!ctx->synced) { /* we'll need to assign UID for the mail immediately. */ if (mbox_sync(mbox, 0) < 0) return -1; mbox_save_init_sync(_t); } /* the syncing above could have changed the append offset */ if (ctx->append_offset == (uoff_t)-1) { if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0) return -1; i_assert(mbox->mbox_fd != -1); ctx->output = o_stream_create_fd_file(mbox->mbox_fd, ctx->append_offset, FALSE); o_stream_cork(ctx->output); } return 0; } static void save_header_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched, struct mbox_save_context *ctx) { if (hdr != NULL) { if (strncmp(hdr->name, "From ", 5) == 0) { /* we can't allow From_-lines in headers. there's no legitimate reason for allowing them in any case, so just drop them. */ *matched = TRUE; return; } if (!*matched && ctx->mbox_md5_ctx != NULL) ctx->mbox->md5_v.more(ctx->mbox_md5_ctx, hdr); } } static void mbox_save_x_delivery_id(struct mbox_save_context *ctx) { unsigned char md5_result[MD5_RESULTLEN]; buffer_t *buf; string_t *str; void *randbuf; buf = buffer_create_dynamic(pool_datastack_create(), 256); buffer_append(buf, &ioloop_time, sizeof(ioloop_time)); buffer_append(buf, &ioloop_timeval.tv_usec, sizeof(ioloop_timeval.tv_usec)); randbuf = buffer_append_space_unsafe(buf, MBOX_DELIVERY_ID_RAND_BYTES); random_fill_weak(randbuf, MBOX_DELIVERY_ID_RAND_BYTES); md5_get_digest(buf->data, buf->used, md5_result); str = t_str_new(128); str_append(str, "X-Delivery-ID: "); base64_encode(md5_result, sizeof(md5_result), str); str_append_c(str, '\n'); ctx->x_delivery_id_header = i_strdup(str_c(str)); } static struct istream * mbox_save_get_input_stream(struct mbox_save_context *ctx, struct istream *input) { struct istream *filter, *ret, *cache_input, *streams[3]; /* filter out unwanted headers and keep track of headers' MD5 sum */ filter = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_ADD_MISSING_EOH | HEADER_FILTER_END_BODY_WITH_LF, mbox_save_drop_headers, mbox_save_drop_headers_count, save_header_callback, ctx); if ((ctx->mbox->storage->storage.flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0) { /* we're using MD5 sums to generate POP3 UIDLs. clients don't like it much if there are duplicates, so make sure that there can't be any by appending our own X-Delivery-ID header. */ const char *hdr; T_BEGIN { mbox_save_x_delivery_id(ctx); } T_END; hdr = ctx->x_delivery_id_header; streams[0] = i_stream_create_from_data(hdr, strlen(hdr)); streams[1] = filter; streams[2] = NULL; ret = i_stream_create_concat(streams); i_stream_unref(&filter); filter = ret; } /* convert linefeeds to wanted format */ ret = ctx->mbox->storage->storage.set->mail_save_crlf ? i_stream_create_crlf(filter) : i_stream_create_lf(filter); i_stream_unref(&filter); /* caching creates a tee stream */ cache_input = index_mail_cache_parse_init(ctx->ctx.dest_mail, ret); i_stream_unref(&ret); ret = cache_input; return ret; } struct mail_save_context * mbox_save_alloc(struct mailbox_transaction_context *t) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->box; struct mbox_save_context *ctx; i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (t->save_ctx == NULL) { ctx = i_new(struct mbox_save_context, 1); ctx->ctx.transaction = t; ctx->mbox = mbox; ctx->trans = t->itrans; ctx->append_offset = (uoff_t)-1; ctx->headers = str_new(default_pool, 512); ctx->mail_offset = (uoff_t)-1; t->save_ctx = &ctx->ctx; } return t->save_ctx; } int mbox_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; struct mail_save_data *mdata = &_ctx->data; struct mbox_transaction_context *t = (struct mbox_transaction_context *)_ctx->transaction; enum mail_flags save_flags; uint64_t offset; /* FIXME: we could write timezone_offset to From-line.. */ if (mdata->received_date == (time_t)-1) mdata->received_date = ioloop_time; ctx->failed = FALSE; ctx->seq = 0; if (mbox_save_init_file(ctx, t) < 0) { ctx->failed = TRUE; return -1; } save_flags = mdata->flags; if (mdata->uid == 0) save_flags |= MAIL_RECENT; str_truncate(ctx->headers, 0); if (ctx->synced) { if (ctx->mbox->mbox_save_md5) ctx->mbox_md5_ctx = ctx->mbox->md5_v.init(); if (ctx->next_uid < mdata->uid) { /* we can use the wanted UID */ ctx->next_uid = mdata->uid; } if (ctx->output->offset == 0) { /* writing the first mail. Insert X-IMAPbase as well. */ str_printfa(ctx->headers, "X-IMAPbase: %u %010u\n", ctx->uid_validity, ctx->next_uid); } str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid); mail_index_append(ctx->trans, ctx->next_uid, &ctx->seq); mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, save_flags & ~MAIL_RECENT); if (mdata->keywords != NULL) { mail_index_update_keywords(ctx->trans, ctx->seq, MODIFY_REPLACE, mdata->keywords); } if (mdata->min_modseq != 0) { mail_index_update_modseq(ctx->trans, ctx->seq, mdata->min_modseq); } offset = ctx->output->offset == 0 ? 0 : ctx->output->offset - 1; mail_index_update_ext(ctx->trans, ctx->seq, ctx->mbox->mbox_ext_idx, &offset, NULL); ctx->next_uid++; /* parse and cache the mail headers as we read it */ mail_set_seq_saving(_ctx->dest_mail, ctx->seq); } mbox_save_append_flag_headers(ctx->headers, save_flags); mbox_save_append_keyword_headers(ctx, mdata->keywords); str_append_c(ctx->headers, '\n'); i_assert(ctx->mbox->mbox_lock_type == F_WRLCK); ctx->mail_offset = ctx->output->offset; ctx->eoh_offset = (uoff_t)-1; ctx->last_char = '\n'; if (write_from_line(ctx, mdata->received_date, mdata->from_envelope) < 0) ctx->failed = TRUE; else ctx->input = mbox_save_get_input_stream(ctx, input); return ctx->failed ? -1 : 0; } static int mbox_save_body_input(struct mbox_save_context *ctx) { const unsigned char *data; size_t size; data = i_stream_get_data(ctx->input, &size); if (size > 0) { if (o_stream_send(ctx->output, data, size) < 0) { write_error(ctx); return -1; } ctx->last_char = data[size-1]; i_stream_skip(ctx->input, size); } return 0; } static int mbox_save_body(struct mbox_save_context *ctx) { ssize_t ret; while ((ret = i_stream_read(ctx->input)) != -1) { if (mbox_save_body_input(ctx) < 0) return -1; /* i_stream_read() may have returned 0 at EOF because of this parser */ index_mail_cache_parse_continue(ctx->ctx.dest_mail); if (ret == 0) return 0; } i_assert(ctx->last_char == '\n'); return 0; } static int mbox_save_finish_headers(struct mbox_save_context *ctx) { i_assert(ctx->eoh_offset == (uoff_t)-1); /* append our own headers and ending empty line */ ctx->extra_hdr_offset = ctx->output->offset; if (o_stream_send(ctx->output, str_data(ctx->headers), str_len(ctx->headers)) < 0) { write_error(ctx); return -1; } ctx->eoh_offset = ctx->output->offset; return 0; } int mbox_save_continue(struct mail_save_context *_ctx) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; const unsigned char *data; size_t i, size; ssize_t ret; if (ctx->failed) return -1; if (ctx->eoh_offset != (uoff_t)-1) { /* writing body */ return mbox_save_body(ctx); } while ((ret = i_stream_read_more(ctx->input, &data, &size)) > 0) { for (i = 0; i < size; i++) { if (data[i] == '\n' && ((i == 0 && ctx->last_char == '\n') || (i > 0 && data[i-1] == '\n'))) { /* end of headers. we don't need to worry about CRs because they're dropped */ break; } } if (i != size) { /* found end of headers. write the rest of them (not including the finishing empty line) */ if (o_stream_send(ctx->output, data, i) < 0) { write_error(ctx); return -1; } ctx->last_char = '\n'; i_stream_skip(ctx->input, i + 1); break; } if (o_stream_send(ctx->output, data, size) < 0) { write_error(ctx); return -1; } i_assert(size > 0); ctx->last_char = data[size-1]; i_stream_skip(ctx->input, size); index_mail_cache_parse_continue(ctx->ctx.dest_mail); } if (ret == 0) return 0; if (ctx->input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(ctx->input), i_stream_get_error(ctx->input)); ctx->failed = TRUE; return -1; } i_assert(ctx->last_char == '\n'); if (ctx->mbox_md5_ctx) { unsigned char hdr_md5_sum[16]; if (ctx->x_delivery_id_header != NULL) { struct message_header_line hdr; i_zero(&hdr); hdr.name = ctx->x_delivery_id_header; hdr.name_len = sizeof("X-Delivery-ID")-1; hdr.middle = (const unsigned char *)hdr.name + hdr.name_len; hdr.middle_len = 2; hdr.value = hdr.full_value = hdr.middle + hdr.middle_len; hdr.value_len = strlen((const char *)hdr.value); ctx->mbox->md5_v.more(ctx->mbox_md5_ctx, &hdr); } ctx->mbox->md5_v.finish(ctx->mbox_md5_ctx, hdr_md5_sum); mail_index_update_ext(ctx->trans, ctx->seq, ctx->mbox->md5hdr_ext_idx, hdr_md5_sum, NULL); } if (mbox_save_finish_headers(ctx) < 0) return -1; /* write body */ if (mbox_save_body_input(ctx) < 0) return -1; return ctx->input->eof ? 0 : mbox_save_body(ctx); } int mbox_save_finish(struct mail_save_context *_ctx) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; if (!ctx->failed && ctx->eoh_offset == (uoff_t)-1) (void)mbox_save_finish_headers(ctx); if (ctx->output != NULL) { /* make sure everything is written */ if (o_stream_nfinish(ctx->output) < 0) write_error(ctx); } ctx->finished = TRUE; if (!ctx->failed) { i_assert(ctx->output != NULL); T_BEGIN { if (mbox_write_content_length(ctx) < 0 || mbox_append_lf(ctx) < 0) ctx->failed = TRUE; } T_END; } index_mail_cache_parse_deinit(ctx->ctx.dest_mail, ctx->ctx.data.received_date, !ctx->failed); if (ctx->input != NULL) i_stream_destroy(&ctx->input); if (ctx->failed && ctx->mail_offset != (uoff_t)-1) { /* saving this mail failed - truncate back to beginning of it */ (void)o_stream_nfinish(ctx->output); if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->mail_offset) < 0) mbox_set_syscall_error(ctx->mbox, "ftruncate()"); (void)o_stream_seek(ctx->output, ctx->mail_offset); ctx->mail_offset = (uoff_t)-1; } if (ctx->seq != 0 && ctx->failed) { index_storage_save_abort_last(&ctx->ctx, ctx->seq); } index_save_context_free(_ctx); return ctx->failed ? -1 : 0; } void mbox_save_cancel(struct mail_save_context *_ctx) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; ctx->failed = TRUE; (void)mbox_save_finish(_ctx); } static void mbox_transaction_save_deinit(struct mbox_save_context *ctx) { if (ctx->output != NULL) o_stream_destroy(&ctx->output); str_free(&ctx->headers); } static void mbox_save_truncate(struct mbox_save_context *ctx) { if (ctx->append_offset == (uoff_t)-1 || ctx->mbox->mbox_fd == -1) return; i_assert(ctx->mbox->mbox_lock_type == F_WRLCK); /* failed, truncate file back to original size. output stream needs to be flushed before truncating so unref() won't write anything. */ if (ctx->output != NULL) (void)o_stream_flush(ctx->output); if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->append_offset) < 0) mbox_set_syscall_error(ctx->mbox, "ftruncate()"); } int mbox_transaction_save_commit_pre(struct mail_save_context *_ctx) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; struct mbox_mailbox *mbox = ctx->mbox; struct stat st; int ret = 0; i_assert(ctx->finished); i_assert(mbox->mbox_fd != -1); if (fstat(mbox->mbox_fd, &st) < 0) { mbox_set_syscall_error(mbox, "fstat()"); ret = -1; } if (ctx->synced) { _t->changes->uid_validity = ctx->uid_validity; mail_index_append_finish_uids(ctx->trans, 0, &_t->changes->saved_uids); mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, next_uid), &ctx->next_uid, sizeof(ctx->next_uid), FALSE); if (ret == 0) { mbox->mbox_hdr.sync_mtime = st.st_mtime; mbox->mbox_hdr.sync_size = st.st_size; mail_index_update_header_ext(ctx->trans, mbox->mbox_ext_idx, 0, &mbox->mbox_hdr, sizeof(mbox->mbox_hdr)); } } if (ret == 0 && ctx->orig_atime != st.st_atime) { /* try to set atime back to its original value. (it'll fail with EPERM for shared mailboxes where we aren't the file's owner) */ struct utimbuf buf; buf.modtime = st.st_mtime; buf.actime = ctx->orig_atime; if (utime(mailbox_get_path(&mbox->box), &buf) < 0 && errno != EPERM) mbox_set_syscall_error(mbox, "utime()"); } if (ctx->output != NULL) { /* flush the final LF */ if (o_stream_nfinish(ctx->output) < 0) write_error(ctx); } if (mbox->mbox_fd != -1 && !mbox->mbox_writeonly && mbox->storage->storage.set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync(mbox->mbox_fd) < 0) { mbox_set_syscall_error(mbox, "fdatasync()"); mbox_save_truncate(ctx); ret = -1; } } mbox_transaction_save_deinit(ctx); if (ret < 0) i_free(ctx); return ret; } void mbox_transaction_save_commit_post(struct mail_save_context *_ctx, struct mail_index_transaction_commit_result *result ATTR_UNUSED) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; i_assert(ctx->mbox->mbox_lock_type == F_WRLCK); if (ctx->synced) { /* after saving mails with UIDs we need to update the last-uid */ (void)mbox_sync(ctx->mbox, MBOX_SYNC_HEADER | MBOX_SYNC_REWRITE); } i_free(ctx); } void mbox_transaction_save_rollback(struct mail_save_context *_ctx) { struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx; if (!ctx->finished) mbox_save_cancel(&ctx->ctx); mbox_save_truncate(ctx); mbox_transaction_save_deinit(ctx); i_free(ctx); } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-file.h0000644000175000017500000000076613123174404017443 00000000000000#ifndef MBOX_FILE_H #define MBOX_FILE_H int mbox_file_open(struct mbox_mailbox *mbox); void mbox_file_close(struct mbox_mailbox *mbox); int mbox_file_open_stream(struct mbox_mailbox *mbox); void mbox_file_close_stream(struct mbox_mailbox *mbox); int mbox_file_lookup_offset(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uoff_t *offset_r); int mbox_file_seek(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, bool *deleted_r); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-storage.h0000644000175000017500000000602713165463624020177 00000000000000#ifndef MBOX_STORAGE_H #define MBOX_STORAGE_H #include "index-storage.h" #include "mbox-settings.h" #include "mbox-md5.h" /* Padding to leave in X-Keywords header when rewriting mbox */ #define MBOX_HEADER_PADDING 50 /* Don't write Content-Length header unless it's value is larger than this. */ #define MBOX_MIN_CONTENT_LENGTH_SIZE 1024 #define MBOX_STORAGE_NAME "mbox" #define MBOX_SUBSCRIPTION_FILE_NAME ".subscriptions" #define MBOX_INDEX_DIR_NAME ".imap" #define MBOX_UIDVALIDITY_FNAME "dovecot-uidvalidity" struct mbox_index_header { uint64_t sync_size; uint32_t sync_mtime; uint8_t dirty_flag; uint8_t unused[3]; guid_128_t mailbox_guid; }; struct mbox_list_index_record { uint32_t mtime; uint32_t size; }; struct mbox_storage { struct mail_storage storage; const struct mbox_settings *set; enum mbox_lock_type *read_locks; enum mbox_lock_type *write_locks; unsigned int lock_settings_initialized:1; }; struct mbox_mailbox { struct mailbox box; struct mbox_storage *storage; int mbox_fd; struct istream *mbox_stream, *mbox_file_stream; int mbox_lock_type; dev_t mbox_dev; ino_t mbox_ino; unsigned int mbox_excl_locks, mbox_shared_locks; struct dotlock *mbox_dotlock; unsigned int mbox_lock_id, mbox_global_lock_id; struct timeout *keep_lock_to; bool mbox_writeonly; unsigned int external_transactions; uint32_t mbox_ext_idx, md5hdr_ext_idx, mbox_list_index_ext_id; struct mbox_index_header mbox_hdr; const struct mailbox_update *sync_hdr_update; struct mbox_md5_vfuncs md5_v; unsigned int no_mbox_file:1; unsigned int invalid_mbox_file:1; unsigned int mbox_broken_offsets:1; unsigned int mbox_save_md5:1; unsigned int mbox_dotlocked:1; unsigned int mbox_used_privileges:1; unsigned int mbox_privileged_locking:1; unsigned int syncing:1; unsigned int backend_readonly:1; unsigned int backend_readonly_set:1; }; struct mbox_transaction_context { struct mailbox_transaction_context t; union mail_index_transaction_module_context module_ctx; unsigned int read_lock_id; unsigned int write_lock_id; }; extern struct mail_vfuncs mbox_mail_vfuncs; extern const char *mbox_hide_headers[], *mbox_save_drop_headers[]; extern unsigned int mbox_hide_headers_count, mbox_save_drop_headers_count; void mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function); struct mailbox_sync_context * mbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); struct mail_save_context * mbox_save_alloc(struct mailbox_transaction_context *_t); int mbox_save_begin(struct mail_save_context *ctx, struct istream *input); int mbox_save_continue(struct mail_save_context *ctx); int mbox_save_finish(struct mail_save_context *ctx); void mbox_save_cancel(struct mail_save_context *ctx); int mbox_transaction_save_commit_pre(struct mail_save_context *ctx); void mbox_transaction_save_commit_post(struct mail_save_context *ctx, struct mail_index_transaction_commit_result *result); void mbox_transaction_save_rollback(struct mail_save_context *ctx); bool mbox_is_backend_readonly(struct mbox_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-md5-apop3d.c0000644000175000017500000000570713123174404020370 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "md5.h" #include "message-parser.h" #include "mbox-md5.h" struct mbox_md5_context { struct md5_context hdr_md5_ctx; bool seen_received_hdr; }; struct mbox_md5_header_func { const char *header; bool (*func)(struct mbox_md5_context *ctx, struct message_header_line *hdr); }; static bool parse_date(struct mbox_md5_context *ctx, struct message_header_line *hdr) { if (!ctx->seen_received_hdr) { /* Received-header contains date too, and more trusted one */ md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); } return TRUE; } static bool parse_delivered_to(struct mbox_md5_context *ctx, struct message_header_line *hdr) { md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); return TRUE; } static bool parse_message_id(struct mbox_md5_context *ctx, struct message_header_line *hdr) { if (!ctx->seen_received_hdr) { /* Received-header contains unique ID too, and more trusted one */ md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); } return TRUE; } static bool parse_received(struct mbox_md5_context *ctx, struct message_header_line *hdr) { if (!ctx->seen_received_hdr) { /* get only the first received-header */ md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); if (!hdr->continues) ctx->seen_received_hdr = TRUE; } return TRUE; } static bool parse_x_delivery_id(struct mbox_md5_context *ctx, struct message_header_line *hdr) { /* Let the local delivery agent help generate unique ID's but don't blindly trust this header alone as it could just as easily come from the remote. */ md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); return TRUE; } static struct mbox_md5_header_func md5_header_funcs[] = { { "Date", parse_date }, { "Delivered-To", parse_delivered_to }, { "Message-ID", parse_message_id }, { "Received", parse_received }, { "X-Delivery-ID", parse_x_delivery_id } }; static int bsearch_header_func_cmp(const void *p1, const void *p2) { const char *key = p1; const struct mbox_md5_header_func *func = p2; return strcasecmp(key, func->header); } static struct mbox_md5_context *mbox_md5_apop3d_init(void) { struct mbox_md5_context *ctx; ctx = i_new(struct mbox_md5_context, 1); md5_init(&ctx->hdr_md5_ctx); return ctx; } static void mbox_md5_apop3d_more(struct mbox_md5_context *ctx, struct message_header_line *hdr) { struct mbox_md5_header_func *func; func = bsearch(hdr->name, md5_header_funcs, N_ELEMENTS(md5_header_funcs), sizeof(*md5_header_funcs), bsearch_header_func_cmp); if (func != NULL) (void)func->func(ctx, hdr); } static void mbox_md5_apop3d_finish(struct mbox_md5_context *ctx, unsigned char result[STATIC_ARRAY 16]) { md5_final(&ctx->hdr_md5_ctx, result); i_free(ctx); } struct mbox_md5_vfuncs mbox_md5_apop3d = { mbox_md5_apop3d_init, mbox_md5_apop3d_more, mbox_md5_apop3d_finish }; dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync-private.h0000644000175000017500000001267113165463624021161 00000000000000#ifndef MBOX_SYNC_PRIVATE_H #define MBOX_SYNC_PRIVATE_H #include "md5.h" #include "mail-index.h" #include enum mbox_sync_flags { MBOX_SYNC_HEADER = 0x02, MBOX_SYNC_LOCK_READING = 0x04, MBOX_SYNC_UNDIRTY = 0x08, MBOX_SYNC_REWRITE = 0x10, MBOX_SYNC_FORCE_SYNC = 0x20, MBOX_SYNC_READONLY = 0x40 }; struct mbox_flag_type { char chr; enum mail_flags flag; }; enum header_position { MBOX_HDR_STATUS, MBOX_HDR_X_IMAPBASE, MBOX_HDR_X_KEYWORDS, MBOX_HDR_X_STATUS, MBOX_HDR_X_UID, MBOX_HDR_COUNT }; /* kludgy. swap MAIL_RECENT with MBOX_NONRECENT_KLUDGE when writing Status header, because 'O' flag means non-recent but internally we want to use recent flag. */ #define MBOX_NONRECENT_KLUDGE MAIL_RECENT #define STATUS_FLAGS_MASK (MAIL_SEEN|MBOX_NONRECENT_KLUDGE) #define XSTATUS_FLAGS_MASK (MAIL_ANSWERED|MAIL_FLAGGED|MAIL_DRAFT|MAIL_DELETED) extern struct mbox_flag_type mbox_status_flags[]; extern struct mbox_flag_type mbox_xstatus_flags[]; struct mbox_sync_mail { /* uid=0 can mean that this mail describes an expunged area or that this is a pseudo message */ uint32_t uid; uint32_t idx_seq; ARRAY_TYPE(keyword_indexes) keywords; uint8_t flags; unsigned int uid_broken:1; unsigned int expunged:1; unsigned int pseudo:1; unsigned int status_broken:1; unsigned int xstatus_broken:1; uoff_t from_offset; uoff_t body_size; /* following variables have a bit overloaded functionality: a) space <= 0 : offset points to beginning of headers. space is the amount of space missing that is required to be able to rewrite the headers b) space > 0 : offset points to beginning of whitespace that can be removed. space is the amount of data that can be removed from there. note that the message may contain more whitespace elsewhere. */ uoff_t offset; off_t space; }; struct mbox_sync_mail_context { struct mbox_sync_context *sync_ctx; struct mbox_sync_mail mail; uint32_t seq; uoff_t hdr_offset, body_offset; size_t header_first_change, header_last_change; string_t *header; unsigned char hdr_md5_sum[16]; uoff_t content_length; size_t hdr_pos[MBOX_HDR_COUNT]; uint32_t parsed_uid, last_uid_updated_value; unsigned int last_uid_value_start_pos; unsigned int have_eoh:1; unsigned int need_rewrite:1; unsigned int seen_imapbase:1; unsigned int updated:1; unsigned int recent:1; unsigned int dirty:1; unsigned int imapbase_rewrite:1; unsigned int imapbase_updated:1; }; struct mbox_sync_context { struct mbox_mailbox *mbox; enum mbox_sync_flags flags; struct istream *input, *file_input; int write_fd; time_t orig_mtime, orig_atime; uoff_t orig_size; struct stat last_stat; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *t; struct mail_index_header reset_hdr; const struct mail_index_header *hdr; string_t *header, *from_line; /* header state: */ uint32_t base_uid_validity, base_uid_last; uoff_t base_uid_last_offset; /* mail state: */ ARRAY(struct mbox_sync_mail) mails; struct index_sync_changes_context *sync_changes; /* per-mail pool */ pool_t mail_keyword_pool; /* used for mails[].keywords */ pool_t saved_keywords_pool; uint32_t prev_msg_uid, next_uid, idx_next_uid; uint32_t seq, idx_seq, need_space_seq; uint32_t last_nonrecent_uid; off_t expunged_space, space_diff; unsigned int dest_first_mail:1; unsigned int first_mail_crlf_expunged:1; /* global flags: */ unsigned int keep_recent:1; unsigned int readonly:1; unsigned int delay_writes:1; unsigned int renumber_uids:1; unsigned int moved_offsets:1; unsigned int ext_modified:1; unsigned int index_reset:1; unsigned int errors:1; }; int mbox_sync_header_refresh(struct mbox_mailbox *mbox); int mbox_sync(struct mbox_mailbox *mbox, enum mbox_sync_flags flags); int mbox_sync_has_changed(struct mbox_mailbox *mbox, bool leave_dirty); void mbox_sync_set_critical(struct mbox_sync_context *sync_ctx, const char *fmt, ...) ATTR_FORMAT(2, 3); int mbox_sync_parse_next_mail(struct istream *input, struct mbox_sync_mail_context *ctx); bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq); void mbox_sync_update_header(struct mbox_sync_mail_context *ctx); void mbox_sync_update_header_from(struct mbox_sync_mail_context *ctx, const struct mbox_sync_mail *mail); int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff); int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx, uoff_t end_offset, off_t move_diff, uoff_t extra_space, uint32_t first_seq, uint32_t last_seq); int mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset); void mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx); void mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty); int mbox_move(struct mbox_sync_context *sync_ctx, uoff_t dest, uoff_t source, uoff_t size); void mbox_sync_move_buffer(struct mbox_sync_mail_context *ctx, size_t pos, size_t need, size_t have); void mbox_sync_headers_add_space(struct mbox_sync_mail_context *ctx, size_t size); int mbox_sync_get_guid(struct mbox_mailbox *mbox); int mbox_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq); void mbox_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/istream-raw-mbox.c0000644000175000017500000005341313165463624020762 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream-private.h" #include "istream-raw-mbox.h" #include "mbox-from.h" struct raw_mbox_istream { struct istream_private istream; time_t received_time, next_received_time; char *sender, *next_sender; uoff_t from_offset, hdr_offset, body_offset, mail_size; uoff_t input_peak_offset; unsigned int locked:1; unsigned int seeked:1; unsigned int crlf_ending:1; unsigned int corrupted:1; unsigned int mail_size_forced:1; unsigned int eof:1; unsigned int header_missing_eoh:1; }; static void i_stream_raw_mbox_destroy(struct iostream_private *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream; i_free(rstream->sender); i_free(rstream->next_sender); i_stream_seek(rstream->istream.parent, rstream->istream.istream.v_offset); i_stream_unref(&rstream->istream.parent); } static int mbox_read_from_line(struct raw_mbox_istream *rstream) { const unsigned char *buf, *p; char *sender; time_t received_time; size_t pos, line_pos; ssize_t ret; unsigned int skip; int tz; buf = i_stream_get_data(rstream->istream.parent, &pos); i_assert(pos > 0); /* from_offset points to "\nFrom ", so unless we're at the beginning of the file, skip the initial \n */ if (rstream->from_offset == 0) skip = 0; else { skip = 1; if (*buf == '\r') skip++; } while ((p = memchr(buf+skip, '\n', pos-skip)) == NULL) { ret = i_stream_read(rstream->istream.parent); buf = i_stream_get_data(rstream->istream.parent, &pos); if (ret < 0) { if (ret == -2) { /* From_-line is too long, but we should be able to parse what we have so far. */ break; } /* EOF shouldn't happen */ rstream->istream.istream.eof = rstream->istream.parent->eof; rstream->istream.istream.stream_errno = rstream->istream.parent->stream_errno; return -1; } i_assert(pos > 0); } line_pos = p == NULL ? 0 : (size_t)(p - buf); /* beginning of mbox */ if (memcmp(buf+skip, "From ", 5) != 0 || mbox_from_parse((buf+skip)+5, (pos-skip)-5, &received_time, &tz, &sender) < 0) { /* broken From - should happen only at beginning of file if this isn't a mbox.. */ io_stream_set_error(&rstream->istream.iostream, "mbox file doesn't begin with 'From ' line"); rstream->istream.istream.stream_errno = EINVAL; return -1; } if (rstream->istream.istream.v_offset == rstream->from_offset) { rstream->received_time = received_time; i_free(rstream->sender); rstream->sender = sender; } else { rstream->next_received_time = received_time; i_free(rstream->next_sender); rstream->next_sender = sender; } /* skip over From-line */ if (line_pos == 0) { /* line was too long. skip the input until we find LF. */ rstream->istream.istream.v_offset += pos; i_stream_skip(rstream->istream.parent, pos); while ((ret = i_stream_read(rstream->istream.parent)) > 0) { p = memchr(buf, '\n', pos); if (p != NULL) break; rstream->istream.istream.v_offset += pos; i_stream_skip(rstream->istream.parent, pos); } if (ret <= 0) { i_assert(ret == -1); /* EOF shouldn't happen */ rstream->istream.istream.eof = rstream->istream.parent->eof; rstream->istream.istream.stream_errno = rstream->istream.parent->stream_errno; return -1; } line_pos = (size_t)(p - buf); } rstream->istream.istream.v_offset += line_pos+1; i_stream_skip(rstream->istream.parent, line_pos+1); rstream->hdr_offset = rstream->istream.istream.v_offset; return 0; } static void handle_end_of_mail(struct raw_mbox_istream *rstream, size_t pos) { rstream->mail_size = rstream->istream.istream.v_offset + pos - rstream->hdr_offset; if (rstream->hdr_offset + rstream->mail_size < rstream->body_offset) { uoff_t new_body_offset = rstream->hdr_offset + rstream->mail_size; if (rstream->body_offset != (uoff_t)-1) { /* Header didn't have ending \n */ rstream->header_missing_eoh = TRUE; } else { /* "headers\n\nFrom ..", the second \n belongs to next message which we didn't know at the time yet. */ } /* The +2 check is for CR+LF linefeeds */ i_assert(rstream->body_offset == (uoff_t)-1 || rstream->body_offset == new_body_offset + 1 || rstream->body_offset == new_body_offset + 2); rstream->body_offset = new_body_offset; } } static ssize_t i_stream_raw_mbox_read(struct istream_private *stream) { static const char *mbox_from = "\nFrom "; struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream; const unsigned char *buf; const char *fromp; char *sender; time_t received_time; size_t i, pos, new_pos, from_start_pos, from_after_pos; ssize_t ret = 0; int eoh_char, tz; bool crlf_ending = FALSE; i_assert(rstream->seeked); i_assert(stream->istream.v_offset >= rstream->from_offset); if (stream->istream.eof) return -1; if (rstream->corrupted) { rstream->istream.istream.stream_errno = EINVAL; return -1; } i_stream_seek(stream->parent, stream->istream.v_offset); stream->pos -= stream->skip; stream->skip = 0; stream->buffer = NULL; do { buf = i_stream_get_data(stream->parent, &pos); if (pos > 1 && stream->istream.v_offset + pos > rstream->input_peak_offset) { /* fake our read count. needed because if in the end we have only one character in buffer and we skip it (as potential CR), we want to get back to this i_stream_raw_mbox_read() to read more data. */ ret = pos; break; } ret = i_stream_read(stream->parent); } while (ret > 0); stream->istream.stream_errno = stream->parent->stream_errno; if (ret < 0) { if (ret == -2) { if (stream->skip == stream->pos) { /* From_-line is longer than our input buffer. finish the check without seeing the LF. */ } else if (stream->istream.v_offset + pos == rstream->input_peak_offset) { /* we've read everything our parent stream has to offer. */ stream->buffer = buf; return -2; } /* parent stream is full, but we haven't returned all its bytes to our caller yet. */ } else if (stream->istream.v_offset != 0 || pos == 0) { /* we've read the whole file, final byte should be the \n trailer */ if (pos > 0 && buf[pos-1] == '\n') { pos--; if (pos > 0 && buf[pos-1] == '\r') { crlf_ending = TRUE; pos--; } } i_assert(pos >= stream->pos); ret = pos == stream->pos ? -1 : (ssize_t)(pos - stream->pos); stream->buffer = buf; stream->pos = pos; if (stream->istream.v_offset == rstream->from_offset) { /* haven't seen From-line yet, so this mbox stream is now at EOF */ rstream->eof = TRUE; } stream->istream.eof = TRUE; rstream->crlf_ending = crlf_ending; handle_end_of_mail(rstream, pos); return ret < 0 ? i_stream_raw_mbox_read(stream) : ret; } } if (stream->istream.v_offset == rstream->from_offset) { /* beginning of message, we haven't yet read our From-line */ if (pos == 2 && ret > 0) { /* we're at the end of file with CR+LF linefeeds? need more data to verify it. */ rstream->input_peak_offset = stream->istream.v_offset + pos; return i_stream_raw_mbox_read(stream); } if (mbox_read_from_line(rstream) < 0) { io_stream_set_error(&stream->iostream, "Next message unexpectedly corrupted in mbox file " "%s at %"PRIuUOFF_T, i_stream_get_name(&stream->istream), stream->istream.v_offset); if (stream->istream.v_offset != 0) i_error("%s", stream->iostream.error); stream->pos = 0; rstream->eof = TRUE; rstream->corrupted = TRUE; return -1; } /* got it. we don't want to return it however, so start again from headers */ buf = i_stream_get_data(stream->parent, &pos); if (pos == 0) return i_stream_raw_mbox_read(stream); } /* See if we have From-line here - note that it works right only because all characters are different in mbox_from. */ fromp = mbox_from; from_start_pos = from_after_pos = (size_t)-1; eoh_char = rstream->body_offset == (uoff_t)-1 ? '\n' : -1; for (i = stream->pos; i < pos; i++) { if (buf[i] == eoh_char && ((i > 0 && buf[i-1] == '\n') || (i > 1 && buf[i-1] == '\r' && buf[i-2] == '\n') || stream->istream.v_offset + i == rstream->hdr_offset)) { rstream->body_offset = stream->istream.v_offset + i + 1; eoh_char = -1; } if ((char)buf[i] == *fromp) { if (*++fromp == '\0') { /* potential From-line, see if we have the rest of the line buffered. */ i++; if (i >= 7 && buf[i-7] == '\r') { /* CR also belongs to it. */ crlf_ending = TRUE; from_start_pos = i - 7; } else { crlf_ending = FALSE; from_start_pos = i - 6; } if (rstream->mail_size == (uoff_t)-1 || rstream->hdr_offset + rstream->mail_size == stream->istream.v_offset + from_start_pos) { from_after_pos = i; if (ret == -2) { /* even if we don't have the whole line, we need to finish this check now. */ goto mbox_verify; } } fromp = mbox_from; } else if (from_after_pos != (size_t)-1) { /* we have the whole From-line here now. See if it's a valid one. */ mbox_verify: if (mbox_from_parse(buf + from_after_pos, pos - from_after_pos, &received_time, &tz, &sender) == 0) { /* yep, we stop here. */ rstream->next_received_time = received_time; i_free(rstream->next_sender); rstream->next_sender = sender; stream->istream.eof = TRUE; rstream->crlf_ending = crlf_ending; handle_end_of_mail(rstream, from_start_pos); break; } from_after_pos = (size_t)-1; } } else { fromp = mbox_from; if ((char)buf[i] == *fromp) fromp++; } } /* we want to go at least one byte further next time */ rstream->input_peak_offset = stream->istream.v_offset + i; if (from_after_pos != (size_t)-1) { /* we're waiting for the \n at the end of From-line */ new_pos = from_start_pos; } else { /* leave out the beginnings of potential From-line + CR */ new_pos = i - (fromp - mbox_from); if (new_pos > 0) new_pos--; } if (stream->istream.v_offset - rstream->hdr_offset + new_pos > rstream->mail_size) { /* istream_raw_mbox_set_next_offset() used invalid cached next_offset? */ io_stream_set_error(&stream->iostream, "Next message unexpectedly lost from mbox file " "%s at %"PRIuUOFF_T" (%s)", i_stream_get_name(&stream->istream), rstream->hdr_offset + rstream->mail_size, rstream->mail_size_forced ? "cached" : "noncached"); i_error("%s", stream->iostream.error); rstream->eof = TRUE; rstream->corrupted = TRUE; rstream->istream.istream.stream_errno = EINVAL; stream->pos = 0; return -1; } stream->buffer = buf; if (new_pos == stream->pos) { if (stream->istream.eof || ret > 0) return i_stream_raw_mbox_read(stream); i_assert(new_pos > 0); ret = -2; } else { i_assert(new_pos > stream->pos); ret = new_pos - stream->pos; stream->pos = new_pos; } return ret; } static void i_stream_raw_mbox_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream; stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; stream->buffer = NULL; rstream->input_peak_offset = 0; rstream->eof = FALSE; } static void i_stream_raw_mbox_sync(struct istream_private *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream; i_stream_sync(stream->parent); rstream->istream.skip = 0; rstream->istream.pos = 0; rstream->input_peak_offset = 0; } static int i_stream_raw_mbox_stat(struct istream_private *stream, bool exact) { const struct stat *st; struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; stream->statbuf.st_size = !exact && rstream->seeked && rstream->mail_size != (uoff_t)-1 ? (off_t)rstream->mail_size : -1; return 0; } struct istream *i_stream_create_raw_mbox(struct istream *input) { struct raw_mbox_istream *rstream; i_assert(input->v_offset == 0); rstream = i_new(struct raw_mbox_istream, 1); rstream->body_offset = (uoff_t)-1; rstream->mail_size = (uoff_t)-1; rstream->received_time = (time_t)-1; rstream->next_received_time = (time_t)-1; rstream->istream.iostream.destroy = i_stream_raw_mbox_destroy; rstream->istream.max_buffer_size = input->real_stream->max_buffer_size; rstream->istream.read = i_stream_raw_mbox_read; rstream->istream.seek = i_stream_raw_mbox_seek; rstream->istream.sync = i_stream_raw_mbox_sync; rstream->istream.stat = i_stream_raw_mbox_stat; rstream->istream.istream.readable_fd = input->readable_fd; rstream->istream.istream.blocking = input->blocking; rstream->istream.istream.seekable = input->seekable; return i_stream_create(&rstream->istream, input, -1); } static int istream_raw_mbox_is_valid_from(struct raw_mbox_istream *rstream) { const unsigned char *data; size_t size; time_t received_time; char *sender; int tz; /* minimal: "From x Thu Nov 29 22:33:52 2001" = 31 chars */ (void)i_stream_read_data(rstream->istream.parent, &data, &size, 30); if ((size == 1 && data[0] == '\n') || (size == 2 && data[0] == '\r' && data[1] == '\n')) { /* EOF */ return 1; } if (size > 31 && memcmp(data, "\nFrom ", 6) == 0) { data += 6; size -= 6; } else if (size > 32 && memcmp(data, "\r\nFrom ", 7) == 0) { data += 7; size -= 7; } else { return 0; } while (memchr(data, '\n', size) == NULL) { if (i_stream_read_data(rstream->istream.parent, &data, &size, size) < 0) break; } if (mbox_from_parse(data, size, &received_time, &tz, &sender) < 0) return 0; rstream->next_received_time = received_time; i_free(rstream->next_sender); rstream->next_sender = sender; return 1; } uoff_t istream_raw_mbox_get_start_offset(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->seeked); return rstream->from_offset; } int istream_raw_mbox_get_header_offset(struct istream *stream, uoff_t *hdr_offset_r) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->seeked); if (rstream->hdr_offset == rstream->from_offset) (void)i_stream_read(stream); if (rstream->corrupted) { i_error("Unexpectedly lost From-line from mbox file %s at " "%"PRIuUOFF_T, i_stream_get_name(stream), rstream->from_offset); return -1; } if (stream->stream_errno != 0) return -1; *hdr_offset_r = rstream->hdr_offset; return 0; } int istream_raw_mbox_get_body_offset(struct istream *stream, uoff_t *body_offset_r) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; uoff_t offset; i_assert(rstream->seeked); if (rstream->body_offset != (uoff_t)-1) { *body_offset_r = rstream->body_offset; return 0; } offset = stream->v_offset; i_stream_seek(stream, rstream->hdr_offset); while (rstream->body_offset == (uoff_t)-1) { i_stream_skip(stream, i_stream_get_data_size(stream)); if (i_stream_read(stream) < 0) { if (rstream->corrupted) { i_error("Unexpectedly lost From-line from mbox file " "%s at %"PRIuUOFF_T, i_stream_get_name(stream), rstream->from_offset); } else { i_assert(rstream->body_offset != (uoff_t)-1); } return -1; } } i_stream_seek(stream, offset); *body_offset_r = rstream->body_offset; return 0; } int istream_raw_mbox_get_body_size(struct istream *stream, uoff_t expected_body_size, uoff_t *body_size_r) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; const unsigned char *data; size_t size; uoff_t old_offset, body_offset, body_size, next_body_offset; i_assert(rstream->seeked); i_assert(rstream->hdr_offset != (uoff_t)-1); if (istream_raw_mbox_get_body_offset(stream, &body_offset) < 0) return -1; body_size = rstream->mail_size == (uoff_t)-1 ? (uoff_t)-1 : rstream->mail_size - (rstream->body_offset - rstream->hdr_offset); old_offset = stream->v_offset; if (expected_body_size != (uoff_t)-1) { /* if we already have the existing body size, use it as long as it's >= expected body_size. otherwise the previous parsing may have stopped at a From_-line that belongs to the body. */ if (body_size != (uoff_t)-1 && body_size >= expected_body_size) { *body_size_r = body_size; return 0; } next_body_offset = rstream->body_offset + expected_body_size; /* If header_missing_eoh is set, the message body begins with a From_-line and the body_offset is pointing to the line *before* the first line of the body, i.e. the empty line separating the headers from the body. If that is the case, we'll have to skip over the empty line to get the correct next_body_offset. */ if (rstream->header_missing_eoh) { i_assert(body_size == 0); next_body_offset += rstream->crlf_ending ? 2 : 1; } i_stream_seek(rstream->istream.parent, next_body_offset); if (istream_raw_mbox_is_valid_from(rstream) > 0) { rstream->mail_size = next_body_offset - rstream->hdr_offset; i_stream_seek(stream, old_offset); *body_size_r = expected_body_size; return 0; } /* invalid expected_body_size */ } if (body_size != (uoff_t)-1) { *body_size_r = body_size; return 0; } /* have to read through the message body */ while (i_stream_read_data(stream, &data, &size, 0) > 0) i_stream_skip(stream, size); i_stream_seek(stream, old_offset); if (stream->stream_errno != 0) return -1; i_assert(rstream->mail_size != (uoff_t)-1); *body_size_r = rstream->mail_size - (rstream->body_offset - rstream->hdr_offset); return 0; } time_t istream_raw_mbox_get_received_time(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->seeked); if (rstream->received_time == (time_t)-1) (void)i_stream_read(stream); return rstream->received_time; } const char *istream_raw_mbox_get_sender(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->seeked); if (rstream->sender == NULL) (void)i_stream_read(stream); return rstream->sender == NULL ? "" : rstream->sender; } bool istream_raw_mbox_has_crlf_ending(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->seeked); return rstream->crlf_ending; } int istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; uoff_t body_size; if (istream_raw_mbox_get_body_size(stream, expected_body_size, &body_size) < 0) return -1; rstream->mail_size = (uoff_t)-1; rstream->received_time = rstream->next_received_time; rstream->next_received_time = (time_t)-1; i_free(rstream->sender); rstream->sender = rstream->next_sender; rstream->next_sender = NULL; rstream->from_offset = rstream->body_offset + body_size; rstream->hdr_offset = rstream->from_offset; rstream->body_offset = (uoff_t)-1; rstream->header_missing_eoh = FALSE; if (stream->v_offset != rstream->from_offset) i_stream_seek_mark(stream, rstream->from_offset); i_stream_seek_mark(rstream->istream.parent, rstream->from_offset); rstream->eof = FALSE; rstream->istream.istream.eof = FALSE; return 0; } int istream_raw_mbox_seek(struct istream *stream, uoff_t offset) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; bool check; i_assert(rstream->locked); /* reset any (corruption) errors */ stream->stream_errno = 0; i_free_and_null(stream->real_stream->iostream.error); rstream->corrupted = FALSE; rstream->eof = FALSE; rstream->istream.istream.eof = FALSE; /* if seeked is FALSE, we unlocked in the middle. don't try to use any cached state then. */ if (rstream->mail_size != (uoff_t)-1 && rstream->seeked && rstream->hdr_offset + rstream->mail_size == offset) return istream_raw_mbox_next(stream, (uoff_t)-1); if (offset == rstream->from_offset && rstream->seeked) { /* back to beginning of current message */ offset = rstream->hdr_offset; check = offset == 0; } else { rstream->body_offset = (uoff_t)-1; rstream->mail_size = (uoff_t)-1; rstream->received_time = (time_t)-1; rstream->next_received_time = (time_t)-1; rstream->header_missing_eoh = FALSE; i_free(rstream->sender); rstream->sender = NULL; i_free(rstream->next_sender); rstream->next_sender = NULL; rstream->from_offset = offset; rstream->hdr_offset = offset; check = TRUE; } rstream->seeked = TRUE; i_stream_seek_mark(stream, offset); i_stream_seek_mark(rstream->istream.parent, offset); if (check) (void)i_stream_read(stream); return rstream->corrupted ? -1 : 0; } void istream_raw_mbox_set_next_offset(struct istream *stream, uoff_t offset) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; i_assert(rstream->hdr_offset != (uoff_t)-1); rstream->mail_size_forced = TRUE; rstream->mail_size = offset - rstream->hdr_offset; } bool istream_raw_mbox_is_eof(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; return rstream->eof; } bool istream_raw_mbox_is_corrupted(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; return rstream->corrupted; } void istream_raw_mbox_set_locked(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; rstream->locked = TRUE; } void istream_raw_mbox_set_unlocked(struct istream *stream) { struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream->real_stream; rstream->locked = FALSE; rstream->seeked = FALSE; } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-settings.h0000644000175000017500000000064413123174404020357 00000000000000#ifndef MBOX_SETTINGS_H #define MBOX_SETTINGS_H struct mbox_settings { const char *mbox_read_locks; const char *mbox_write_locks; unsigned int mbox_lock_timeout; unsigned int mbox_dotlock_change_timeout; uoff_t mbox_min_index_size; bool mbox_dirty_syncs; bool mbox_very_dirty_syncs; bool mbox_lazy_writes; const char *mbox_md5; }; const struct setting_parser_info *mbox_get_setting_parser_info(void); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-mail.c0000644000175000017500000003125613165463624017452 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "hex-binary.h" #include "index-mail.h" #include "mbox-storage.h" #include "mbox-file.h" #include "mbox-lock.h" #include "mbox-sync-private.h" #include "istream-raw-mbox.h" #include "istream-header-filter.h" #include #include #include static void mbox_prepare_resync(struct mail *mail) { struct mbox_transaction_context *t = (struct mbox_transaction_context *)mail->transaction; struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->box; if (mbox->mbox_lock_type == F_RDLCK) { if (mbox->mbox_lock_id == t->read_lock_id) t->read_lock_id = 0; mbox_unlock(mbox, mbox->mbox_lock_id); i_assert(mbox->mbox_lock_type == F_UNLCK); } } static int mbox_mail_seek(struct index_mail *mail) { struct mbox_transaction_context *t = (struct mbox_transaction_context *)mail->mail.mail.transaction; struct mail *_mail = &mail->mail.mail; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; enum mbox_sync_flags sync_flags = 0; int ret, try; bool deleted; if (_mail->expunged || mbox->syncing) return -1; if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(_mail); return -1; } _mail->mail_stream_opened = TRUE; if (mbox->mbox_stream != NULL && istream_raw_mbox_is_corrupted(mbox->mbox_stream)) { /* clear the corruption by forcing a full resync */ sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC; } for (try = 0; try < 2; try++) { if ((sync_flags & MBOX_SYNC_FORCE_SYNC) != 0) { /* dirty offsets are broken. make sure we can sync. */ mbox_prepare_resync(_mail); } if (mbox->mbox_lock_type == F_UNLCK) { i_assert(t->read_lock_id == 0); sync_flags |= MBOX_SYNC_LOCK_READING; if (mbox_sync(mbox, sync_flags) < 0) return -1; t->read_lock_id = mbox_get_cur_lock_id(mbox); i_assert(t->read_lock_id != 0); /* refresh index file after mbox has been locked to make sure we get only up-to-date mbox offsets. */ if (mail_index_refresh(mbox->box.index) < 0) { mailbox_set_index_error(&mbox->box); return -1; } i_assert(mbox->mbox_lock_type != F_UNLCK); } else if (t->read_lock_id == 0) { /* file is already locked by another transaction, but we must keep it locked for the entire transaction, so increase the lock counter. */ if (mbox_lock(mbox, mbox->mbox_lock_type, &t->read_lock_id) < 0) i_unreached(); } if (mbox_file_open_stream(mbox) < 0) return -1; ret = mbox_file_seek(mbox, _mail->transaction->view, _mail->seq, &deleted); if (ret > 0) { /* success */ break; } if (ret < 0) { if (deleted) mail_set_expunged(_mail); return -1; } /* we'll need to re-sync it completely */ sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC; } if (ret == 0) { mail_storage_set_critical(&mbox->storage->storage, "Losing sync for mail uid=%u in mbox file %s", _mail->uid, mailbox_get_path(&mbox->box)); } return 0; } static int mbox_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (mbox_mail_seek(mail) < 0) return -1; data->received_date = istream_raw_mbox_get_received_time(mbox->mbox_stream); if (data->received_date == (time_t)-1) { /* it's broken and conflicts with our "not found" return value. change it. */ data->received_date = 0; } *date_r = data->received_date; return 0; } static int mbox_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (index_mail_get_save_date(_mail, date_r) == 0) return 0; /* no way to know this. save the current time into cache and use that from now on. this works only as long as the index files are permanent */ data->save_date = ioloop_time; *date_r = data->save_date; return 0; } static int mbox_mail_get_md5_header(struct index_mail *mail, const char **value_r) { struct mail *_mail = &mail->mail.mail; static uint8_t empty_md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; const void *ext_data; if (mail->data.guid != NULL) { *value_r = mail->data.guid; return 1; } mail_index_lookup_ext(_mail->transaction->view, _mail->seq, mbox->md5hdr_ext_idx, &ext_data, NULL); if (ext_data != NULL && memcmp(ext_data, empty_md5, 16) != 0) { mail->data.guid = p_strdup(mail->mail.data_pool, binary_to_hex(ext_data, 16)); *value_r = mail->data.guid; return 1; } else if (mail_index_is_expunged(_mail->transaction->view, _mail->seq)) { mail_set_expunged(_mail); return -1; } else { return 0; } } static int mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; uoff_t offset; bool move_offset; int ret; switch (field) { case MAIL_FETCH_FROM_ENVELOPE: if (mbox_mail_seek(mail) < 0) return -1; *value_r = istream_raw_mbox_get_sender(mbox->mbox_stream); return 0; case MAIL_FETCH_GUID: case MAIL_FETCH_HEADER_MD5: if ((ret = mbox_mail_get_md5_header(mail, value_r)) != 0) return ret < 0 ? -1 : 0; /* i guess in theory the empty_md5 is valid and can happen, but it's almost guaranteed that it means the MD5 sum is missing. recalculate it. */ if (mbox->mbox_lock_type == F_UNLCK || mbox->mbox_stream == NULL) { offset = 0; move_offset = FALSE; } else { offset = istream_raw_mbox_get_start_offset(mbox->mbox_stream); move_offset = TRUE; } mbox->mbox_save_md5 = TRUE; if (mbox_sync(mbox, MBOX_SYNC_FORCE_SYNC | MBOX_SYNC_READONLY) < 0) return -1; if (move_offset) { if (istream_raw_mbox_seek(mbox->mbox_stream, offset) < 0) { i_error("mbox %s sync lost during MD5 syncing", _mail->box->name); return -1; } } if ((ret = mbox_mail_get_md5_header(mail, value_r)) == 0) { i_error("mbox %s resyncing didn't save header MD5 values", _mail->box->name); return -1; } return ret < 0 ? -1 : 0; default: break; } return index_mail_get_special(_mail, field, value_r); } static int mbox_mail_get_next_offset(struct index_mail *mail, uoff_t *next_offset_r) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box; struct mail_index_view *view; const struct mail_index_header *hdr; uint32_t seq; int trailer_size; int ret = 1; *next_offset_r = (uoff_t)-1; hdr = mail_index_get_header(mail->mail.mail.transaction->view); if (mail->mail.mail.seq > hdr->messages_count) { /* we're appending a new message */ return 0; } /* We can't really trust trans_view. The next message may already be expunged from it. Also hdr.messages_count may be incorrect there. So refresh the index to get the latest changes and get the next message's offset using a new view. */ i_assert(mbox->mbox_lock_type != F_UNLCK); if (mbox_sync_header_refresh(mbox) < 0) return -1; view = mail_index_view_open(mail->mail.mail.box->index); hdr = mail_index_get_header(view); if (!mail_index_lookup_seq(view, mail->mail.mail.uid, &seq)) i_panic("Message unexpectedly expunged from index"); if (seq < hdr->messages_count) { if (mbox_file_lookup_offset(mbox, view, seq + 1, next_offset_r) <= 0) ret = -1; } else if (mail->mail.mail.box->input != NULL) { /* opened the mailbox as input stream. we can't trust the sync_size, since it's wrong with compressed mailboxes */ ret = 0; } else { /* last message, use the synced mbox size */ trailer_size = mbox->storage->storage.set->mail_save_crlf ? 2 : 1; *next_offset_r = mbox->mbox_hdr.sync_size - trailer_size; } mail_index_view_close(&view); return ret; } static int mbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; struct istream *input; struct message_size hdr_size; uoff_t old_offset, body_offset, body_size, next_offset; if (index_mail_get_physical_size(_mail, size_r) == 0) return 0; /* we want to return the header size as seen by mail_get_stream(). */ old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream(_mail, &hdr_size, NULL, &input) < 0) return -1; /* our header size varies, so don't do any caching */ if (istream_raw_mbox_get_body_offset(mbox->mbox_stream, &body_offset) < 0) { mail_storage_set_critical(_mail->box->storage, "mbox %s: Couldn't get body offset for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } /* use the next message's offset to avoid reading through the entire message body to find out its size */ if (mbox_mail_get_next_offset(mail, &next_offset) > 0) body_size = next_offset - body_offset; else body_size = (uoff_t)-1; /* verify that the calculated body size is correct */ if (istream_raw_mbox_get_body_size(mbox->mbox_stream, body_size, &body_size) < 0) { mail_storage_set_critical(_mail->box->storage, "mbox %s: Couldn't get body size for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } data->physical_size = hdr_size.physical_size + body_size; *size_r = data->physical_size; i_stream_seek(input, old_offset); return 0; } static int mbox_mail_init_stream(struct index_mail *mail) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box; struct istream *raw_stream; uoff_t hdr_offset, next_offset; int ret; if (mbox_mail_seek(mail) < 0) return -1; ret = mbox_mail_get_next_offset(mail, &next_offset); if (ret < 0) { if (mbox_mail_seek(mail) < 0) return -1; ret = mbox_mail_get_next_offset(mail, &next_offset); if (ret < 0) { i_warning("mbox %s: Can't find next message offset " "for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); } } raw_stream = mbox->mbox_stream; if (istream_raw_mbox_get_header_offset(raw_stream, &hdr_offset) < 0) { mail_storage_set_critical(mbox->box.storage, "mbox %s: Couldn't get header offset for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } i_stream_seek(raw_stream, hdr_offset); if (next_offset != (uoff_t)-1) istream_raw_mbox_set_next_offset(raw_stream, next_offset); raw_stream = i_stream_create_limit(raw_stream, (uoff_t)-1); mail->data.stream = i_stream_create_header_filter(raw_stream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, mbox_hide_headers, mbox_hide_headers_count, *null_header_filter_callback, (void *)NULL); i_stream_unref(&raw_stream); return 0; } static int mbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.stream == NULL) { if (mbox_mail_init_stream(mail) < 0) return -1; } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static void mbox_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) { struct index_mail *mail = (struct index_mail *)_mail; index_mail_set_seq(_mail, seq, saving); mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; } static bool mbox_mail_set_uid(struct mail *_mail, uint32_t uid) { struct index_mail *mail = (struct index_mail *)_mail; bool ret; ret = index_mail_set_uid(_mail, uid); mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; return ret; } struct mail_vfuncs mbox_mail_vfuncs = { index_mail_close, index_mail_free, mbox_mail_set_seq, mbox_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, mbox_mail_get_received_date, mbox_mail_get_save_date, index_mail_get_virtual_size, mbox_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, mbox_mail_get_stream, index_mail_get_binary_stream, mbox_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened, index_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-file.c0000644000175000017500000001211413165463624017437 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "mbox-storage.h" #include "mbox-sync-private.h" #include "mbox-file.h" #include "istream-raw-mbox.h" #include #include #define MBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE int mbox_file_open(struct mbox_mailbox *mbox) { struct stat st; int fd; i_assert(mbox->mbox_fd == -1); if (mbox->mbox_file_stream != NULL) { /* read-only mbox stream */ i_assert(mbox_is_backend_readonly(mbox)); return 0; } fd = open(mailbox_get_path(&mbox->box), mbox_is_backend_readonly(mbox) ? O_RDONLY : O_RDWR); if (fd == -1 && errno == EACCES && !mbox->backend_readonly) { mbox->backend_readonly = TRUE; fd = open(mailbox_get_path(&mbox->box), O_RDONLY); } if (fd == -1) { mbox_set_syscall_error(mbox, "open()"); return -1; } if (fstat(fd, &st) < 0) { mbox_set_syscall_error(mbox, "fstat()"); i_close_fd(&fd); return -1; } mbox->mbox_writeonly = S_ISFIFO(st.st_mode); mbox->mbox_fd = fd; mbox->mbox_dev = st.st_dev; mbox->mbox_ino = st.st_ino; return 0; } void mbox_file_close(struct mbox_mailbox *mbox) { mbox_file_close_stream(mbox); if (mbox->mbox_fd != -1) { if (close(mbox->mbox_fd) < 0) mbox_set_syscall_error(mbox, "close()"); mbox->mbox_fd = -1; } } int mbox_file_open_stream(struct mbox_mailbox *mbox) { if (mbox->mbox_stream != NULL) return 0; if (mbox->mbox_file_stream != NULL) { /* read-only mbox stream */ i_assert(mbox->mbox_fd == -1 && mbox_is_backend_readonly(mbox)); } else { if (mbox->mbox_fd == -1) { if (mbox_file_open(mbox) < 0) return -1; } if (mbox->mbox_writeonly) { mbox->mbox_file_stream = i_stream_create_from_data("", 0); } else { mbox->mbox_file_stream = i_stream_create_fd(mbox->mbox_fd, MBOX_READ_BLOCK_SIZE, FALSE); i_stream_set_init_buffer_size(mbox->mbox_file_stream, MBOX_READ_BLOCK_SIZE); } i_stream_set_name(mbox->mbox_file_stream, mailbox_get_path(&mbox->box)); } mbox->mbox_stream = i_stream_create_raw_mbox(mbox->mbox_file_stream); if (mbox->mbox_lock_type != F_UNLCK) istream_raw_mbox_set_locked(mbox->mbox_stream); return 0; } static void mbox_file_fix_atime(struct mbox_mailbox *mbox) { struct utimbuf buf; struct stat st; if (mbox->box.recent_flags_count > 0 && (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 && mbox->mbox_fd != -1 && !mbox_is_backend_readonly(mbox)) { /* we've seen recent messages which we want to keep recent. keep file's atime lower than mtime so \Marked status gets shown while listing */ if (fstat(mbox->mbox_fd, &st) < 0) { mbox_set_syscall_error(mbox, "fstat()"); return; } if (st.st_atime >= st.st_mtime) { buf.modtime = st.st_mtime; buf.actime = buf.modtime - 1; /* EPERM can happen with shared mailboxes */ if (utime(mailbox_get_path(&mbox->box), &buf) < 0 && errno != EPERM) mbox_set_syscall_error(mbox, "utime()"); } } } void mbox_file_close_stream(struct mbox_mailbox *mbox) { /* if we read anything, fix the atime if needed */ mbox_file_fix_atime(mbox); if (mbox->mbox_stream != NULL) i_stream_destroy(&mbox->mbox_stream); if (mbox->mbox_file_stream != NULL) { if (mbox->mbox_fd == -1) { /* read-only mbox stream */ i_assert(mbox_is_backend_readonly(mbox)); i_stream_seek(mbox->mbox_file_stream, 0); } else { i_stream_destroy(&mbox->mbox_file_stream); } } } int mbox_file_lookup_offset(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uoff_t *offset_r) { const void *data; bool deleted; mail_index_lookup_ext(view, seq, mbox->mbox_ext_idx, &data, &deleted); if (deleted) return -1; if (data == NULL) { mail_storage_set_critical(&mbox->storage->storage, "Cached message offset lost for seq %u in mbox file %s", seq, mailbox_get_path(&mbox->box)); mbox->mbox_hdr.dirty_flag = TRUE; mbox->mbox_broken_offsets = TRUE; return 0; } *offset_r = *((const uint64_t *)data); return 1; } int mbox_file_seek(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, bool *deleted_r) { uoff_t offset; int ret; ret = mbox_file_lookup_offset(mbox, view, seq, &offset); if (ret <= 0) { *deleted_r = ret < 0; return ret; } *deleted_r = FALSE; if (istream_raw_mbox_seek(mbox->mbox_stream, offset) < 0) { if (offset == 0) { mbox->invalid_mbox_file = TRUE; mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox isn't a valid mbox file"); return -1; } if (mbox->mbox_hdr.dirty_flag) return 0; mail_storage_set_critical(&mbox->storage->storage, "Cached message offset %s is invalid for mbox file %s", dec2str(offset), mailbox_get_path(&mbox->box)); mbox->mbox_hdr.dirty_flag = TRUE; mbox->mbox_broken_offsets = TRUE; return 0; } if (mbox->mbox_hdr.dirty_flag) { /* we're dirty - make sure this is the correct mail */ if (!mbox_sync_parse_match_mail(mbox, view, seq)) return 0; ret = istream_raw_mbox_seek(mbox->mbox_stream, offset); i_assert(ret == 0); } return 1; } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-settings.c0000644000175000017500000000270313123174404020350 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "mbox-settings.h" #include #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mbox_settings, name), NULL } static const struct setting_define mbox_setting_defines[] = { DEF(SET_STR, mbox_read_locks), DEF(SET_STR, mbox_write_locks), DEF(SET_TIME, mbox_lock_timeout), DEF(SET_TIME, mbox_dotlock_change_timeout), DEF(SET_SIZE, mbox_min_index_size), DEF(SET_BOOL, mbox_dirty_syncs), DEF(SET_BOOL, mbox_very_dirty_syncs), DEF(SET_BOOL, mbox_lazy_writes), DEF(SET_ENUM, mbox_md5), SETTING_DEFINE_LIST_END }; static const struct mbox_settings mbox_default_settings = { .mbox_read_locks = "fcntl", .mbox_write_locks = "dotlock fcntl", .mbox_lock_timeout = 5*60, .mbox_dotlock_change_timeout = 2*60, .mbox_min_index_size = 0, .mbox_dirty_syncs = TRUE, .mbox_very_dirty_syncs = FALSE, .mbox_lazy_writes = TRUE, .mbox_md5 = "apop3d:all" }; static const struct setting_parser_info mbox_setting_parser_info = { .module_name = "mbox", .defines = mbox_setting_defines, .defaults = &mbox_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; const struct setting_parser_info *mbox_get_setting_parser_info(void) { return &mbox_setting_parser_info; } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync-update.c0000644000175000017500000003177113123174404020753 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "str.h" #include "message-parser.h" #include "index-storage.h" #include "index-sync-changes.h" #include "mbox-storage.h" #include "mbox-sync-private.h" /* Line length when to wrap X-IMAP, X-IMAPbase and X-Keywords headers to next line. Keep this pretty long, as after we wrap we lose compatibility with UW-IMAP */ #define KEYWORD_WRAP_LINE_LENGTH 1024 static void status_flags_append(struct mbox_sync_mail_context *ctx, const struct mbox_flag_type *flags_list) { int i; for (i = 0; flags_list[i].chr != 0; i++) { if ((ctx->mail.flags & flags_list[i].flag) != 0) str_append_c(ctx->header, flags_list[i].chr); } } void mbox_sync_move_buffer(struct mbox_sync_mail_context *ctx, size_t pos, size_t need, size_t have) { ssize_t diff = (ssize_t)need - (ssize_t)have; int i; i_assert(have < SSIZE_T_MAX); if (diff == 0) { if (ctx->header_last_change < pos + have || ctx->header_last_change == (size_t)-1) ctx->header_last_change = pos + have; } else { /* FIXME: if (diff < ctx->space && pos < ctx->offset) then move the data only up to space offset and give/take the space from there. update header_last_change accordingly. (except pos and offset can't be compared directly) */ ctx->header_last_change = (size_t)-1; for (i = 0; i < MBOX_HDR_COUNT; i++) { if (ctx->hdr_pos[i] > pos && ctx->hdr_pos[i] != (size_t)-1) ctx->hdr_pos[i] += diff; } if (ctx->mail.space > 0) { i_assert(ctx->mail.offset + ctx->mail.space <= ctx->hdr_offset + pos || ctx->mail.offset > ctx->hdr_offset + pos + have); if (ctx->mail.offset > ctx->hdr_offset + pos) { /* free space offset moves */ ctx->mail.offset += diff; } } if (diff < 0) str_delete(ctx->header, pos, -diff); else { ctx->header_last_change = (size_t)-1; buffer_copy(ctx->header, pos + diff, ctx->header, pos, (size_t)-1); } } } static void status_flags_replace(struct mbox_sync_mail_context *ctx, size_t pos, const struct mbox_flag_type *flags_list) { unsigned char *data; size_t size; int i, need, have; ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; if (ctx->header_first_change > pos) ctx->header_first_change = pos; /* how many bytes do we need? */ for (i = 0, need = 0; flags_list[i].chr != 0; i++) { if ((ctx->mail.flags & flags_list[i].flag) != 0) need++; } /* how many bytes do we have now? */ data = buffer_get_modifiable_data(ctx->header, &size); for (have = 0; pos < size; pos++) { if (data[pos] == '\n' || data[pos] == '\r') break; /* see if this is unknown flag for us */ for (i = 0; flags_list[i].chr != 0; i++) { if (flags_list[i].chr == (char)data[pos]) break; } if (flags_list[i].chr != 0) have++; else { /* save this one */ data[pos-have] = data[pos]; } } pos -= have; mbox_sync_move_buffer(ctx, pos, need, have); /* @UNSAFE */ data = buffer_get_space_unsafe(ctx->header, pos, need); for (i = 0; flags_list[i].chr != 0; i++) { if ((ctx->mail.flags & flags_list[i].flag) != 0) *data++ = flags_list[i].chr; } ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; } static void keywords_append(struct mbox_sync_context *sync_ctx, string_t *dest, const ARRAY_TYPE(keyword_indexes) *keyword_indexes_arr) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(&sync_ctx->mbox->box); const char *const *keyword_names; const unsigned int *keyword_indexes; unsigned int i, idx_count, keywords_count; size_t last_break; keyword_names = array_get(ibox->keyword_names, &keywords_count); keyword_indexes = array_get(keyword_indexes_arr, &idx_count); for (i = 0, last_break = str_len(dest); i < idx_count; i++) { i_assert(keyword_indexes[i] < keywords_count); /* wrap the line whenever it gets too long */ if (str_len(dest) - last_break < KEYWORD_WRAP_LINE_LENGTH) { if (i > 0) str_append_c(dest, ' '); } else { str_append(dest, "\n\t"); last_break = str_len(dest); } str_append(dest, keyword_names[keyword_indexes[i]]); } } static void keywords_append_all(struct mbox_sync_mail_context *ctx, string_t *dest, size_t startpos) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(&ctx->sync_ctx->mbox->box); const char *const *names; const unsigned char *p; unsigned int i, count; size_t last_break; p = str_data(dest); if (str_len(dest) - startpos < KEYWORD_WRAP_LINE_LENGTH) last_break = startpos; else { /* set last_break to beginning of line */ for (last_break = str_len(dest); last_break > 0; last_break--) { if (p[last_break-1] == '\n') break; } } names = array_get(ibox->keyword_names, &count); for (i = 0; i < count; i++) { /* wrap the line whenever it gets too long */ if (str_len(dest) - last_break < KEYWORD_WRAP_LINE_LENGTH) str_append_c(dest, ' '); else { str_append(dest, "\n\t"); last_break = str_len(dest); } str_append(dest, names[i]); } } static void mbox_sync_add_missing_headers(struct mbox_sync_mail_context *ctx) { size_t new_hdr_size, startpos; new_hdr_size = str_len(ctx->header); if (new_hdr_size > 0 && str_data(ctx->header)[new_hdr_size-1] != '\n') { /* broken header - doesn't end with \n. fix it. */ str_append_c(ctx->header, '\n'); } if (ctx->sync_ctx->dest_first_mail && ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] == (size_t)-1) { i_assert(ctx->sync_ctx->base_uid_validity != 0); str_append(ctx->header, "X-IMAPbase: "); ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header); /* startpos must start from identical position as when updating */ startpos = str_len(ctx->header); str_printfa(ctx->header, "%u ", ctx->sync_ctx->base_uid_validity); ctx->last_uid_updated_value = ctx->sync_ctx->next_uid-1; ctx->last_uid_value_start_pos = str_len(ctx->header) - ctx->hdr_pos[MBOX_HDR_X_IMAPBASE]; ctx->imapbase_updated = TRUE; str_printfa(ctx->header, "%010u", ctx->last_uid_updated_value); keywords_append_all(ctx, ctx->header, startpos); str_append_c(ctx->header, '\n'); } if (ctx->hdr_pos[MBOX_HDR_X_UID] == (size_t)-1 && !ctx->mail.pseudo) { str_append(ctx->header, "X-UID: "); ctx->hdr_pos[MBOX_HDR_X_UID] = str_len(ctx->header); str_printfa(ctx->header, "%u\n", ctx->mail.uid); } ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; if (ctx->hdr_pos[MBOX_HDR_STATUS] == (size_t)-1 && (ctx->mail.flags & STATUS_FLAGS_MASK) != 0) { str_append(ctx->header, "Status: "); ctx->hdr_pos[MBOX_HDR_STATUS] = str_len(ctx->header); status_flags_append(ctx, mbox_status_flags); str_append_c(ctx->header, '\n'); } if (ctx->hdr_pos[MBOX_HDR_X_STATUS] == (size_t)-1 && (ctx->mail.flags & XSTATUS_FLAGS_MASK) != 0) { str_append(ctx->header, "X-Status: "); ctx->hdr_pos[MBOX_HDR_X_STATUS] = str_len(ctx->header); status_flags_append(ctx, mbox_xstatus_flags); str_append_c(ctx->header, '\n'); } ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE; if (ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] == (size_t)-1 && array_is_created(&ctx->mail.keywords) && array_count(&ctx->mail.keywords) > 0) { str_append(ctx->header, "X-Keywords: "); ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header); keywords_append(ctx->sync_ctx, ctx->header, &ctx->mail.keywords); str_append_c(ctx->header, '\n'); } if (ctx->content_length == (uoff_t)-1 && ctx->mail.body_size >= MBOX_MIN_CONTENT_LENGTH_SIZE) { str_printfa(ctx->header, "Content-Length: %"PRIuUOFF_T"\n", ctx->mail.body_size); } if (str_len(ctx->header) != new_hdr_size) { if (ctx->header_first_change == (size_t)-1) ctx->header_first_change = new_hdr_size; ctx->header_last_change = (size_t)-1; } if (ctx->have_eoh) str_append_c(ctx->header, '\n'); } static void mbox_sync_update_status(struct mbox_sync_mail_context *ctx) { if (ctx->hdr_pos[MBOX_HDR_STATUS] != (size_t)-1) { status_flags_replace(ctx, ctx->hdr_pos[MBOX_HDR_STATUS], mbox_status_flags); } } static void mbox_sync_update_xstatus(struct mbox_sync_mail_context *ctx) { if (ctx->hdr_pos[MBOX_HDR_X_STATUS] != (size_t)-1) { status_flags_replace(ctx, ctx->hdr_pos[MBOX_HDR_X_STATUS], mbox_xstatus_flags); } } static void mbox_sync_update_line(struct mbox_sync_mail_context *ctx, size_t pos, string_t *new_line) { const char *hdr, *p; uoff_t file_pos; if (ctx->header_first_change > pos) ctx->header_first_change = pos; /* set p = end of header, handle also wrapped headers */ hdr = p = str_c(ctx->header) + pos; for (;;) { p = strchr(p, '\n'); if (p == NULL) { /* shouldn't really happen, but allow anyway.. */ p = hdr + strlen(hdr); break; } if (p[1] != '\t' && p[1] != ' ') break; p += 2; } file_pos = pos + ctx->hdr_offset; if (ctx->mail.space > 0 && ctx->mail.offset >= file_pos && ctx->mail.offset < file_pos + (p - hdr)) { /* extra space points to this line. remove it. */ ctx->mail.offset = ctx->hdr_offset; ctx->mail.space = 0; } mbox_sync_move_buffer(ctx, pos, str_len(new_line), p - hdr + 1); buffer_copy(ctx->header, pos, new_line, 0, (size_t)-1); } static void mbox_sync_update_xkeywords(struct mbox_sync_mail_context *ctx) { string_t *str; if (ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] == (size_t)-1) return; str = t_str_new(256); if (array_is_created(&ctx->mail.keywords)) keywords_append(ctx->sync_ctx, str, &ctx->mail.keywords); str_append_c(str, '\n'); mbox_sync_update_line(ctx, ctx->hdr_pos[MBOX_HDR_X_KEYWORDS], str); } static void mbox_sync_update_x_imap_base(struct mbox_sync_mail_context *ctx) { struct mbox_sync_context *sync_ctx = ctx->sync_ctx; string_t *str; i_assert(sync_ctx->base_uid_validity != 0); if (!sync_ctx->dest_first_mail || ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] == (size_t)-1) return; if (!ctx->imapbase_rewrite) { /* uid-last might need updating, but we'll do it later by writing it directly where needed. */ return; } /* a) keyword list changed, b) uid-last didn't use 10 digits */ str = t_str_new(200); str_printfa(str, "%u ", sync_ctx->base_uid_validity); ctx->last_uid_updated_value = sync_ctx->next_uid-1; ctx->last_uid_value_start_pos = str_len(str); ctx->imapbase_updated = TRUE; str_printfa(str, "%010u", ctx->last_uid_updated_value); keywords_append_all(ctx, str, 0); str_append_c(str, '\n'); mbox_sync_update_line(ctx, ctx->hdr_pos[MBOX_HDR_X_IMAPBASE], str); } static void mbox_sync_update_x_uid(struct mbox_sync_mail_context *ctx) { string_t *str; if (ctx->hdr_pos[MBOX_HDR_X_UID] == (size_t)-1 || ctx->mail.uid == ctx->parsed_uid) return; str = t_str_new(64); str_printfa(str, "%u\n", ctx->mail.uid); mbox_sync_update_line(ctx, ctx->hdr_pos[MBOX_HDR_X_UID], str); } static void mbox_sync_update_header_real(struct mbox_sync_mail_context *ctx) { i_assert(ctx->mail.uid != 0 || ctx->mail.pseudo); if (!ctx->sync_ctx->keep_recent) ctx->mail.flags &= ~MAIL_RECENT; mbox_sync_update_status(ctx); mbox_sync_update_xstatus(ctx); mbox_sync_update_xkeywords(ctx); mbox_sync_update_x_imap_base(ctx); mbox_sync_update_x_uid(ctx); mbox_sync_add_missing_headers(ctx); ctx->updated = TRUE; } void mbox_sync_update_header(struct mbox_sync_mail_context *ctx) { T_BEGIN { mbox_sync_update_header_real(ctx); } T_END; } static void mbox_sync_update_header_from_real(struct mbox_sync_mail_context *ctx, const struct mbox_sync_mail *mail) { if (mail->status_broken || (ctx->mail.flags & STATUS_FLAGS_MASK) != (mail->flags & STATUS_FLAGS_MASK) || (ctx->mail.flags & MAIL_RECENT) != 0) { ctx->mail.flags = (ctx->mail.flags & ~STATUS_FLAGS_MASK) | (mail->flags & STATUS_FLAGS_MASK); if (!ctx->sync_ctx->keep_recent) ctx->mail.flags &= ~MAIL_RECENT; mbox_sync_update_status(ctx); } if (mail->xstatus_broken || (ctx->mail.flags & XSTATUS_FLAGS_MASK) != (mail->flags & XSTATUS_FLAGS_MASK)) { ctx->mail.flags = (ctx->mail.flags & ~XSTATUS_FLAGS_MASK) | (mail->flags & XSTATUS_FLAGS_MASK); mbox_sync_update_xstatus(ctx); } if (!array_is_created(&mail->keywords) || array_count(&mail->keywords) == 0) { /* no keywords for this mail */ if (array_is_created(&ctx->mail.keywords)) { array_clear(&ctx->mail.keywords); mbox_sync_update_xkeywords(ctx); } } else if (!array_is_created(&ctx->mail.keywords)) { /* adding first keywords */ p_array_init(&ctx->mail.keywords, ctx->sync_ctx->mail_keyword_pool, array_count(&mail->keywords)); array_append_array(&ctx->mail.keywords, &mail->keywords); mbox_sync_update_xkeywords(ctx); } else if (!array_cmp(&ctx->mail.keywords, &mail->keywords)) { /* keywords changed. */ array_clear(&ctx->mail.keywords); array_append_array(&ctx->mail.keywords, &mail->keywords); mbox_sync_update_xkeywords(ctx); } i_assert(ctx->mail.uid == 0 || ctx->mail.uid == mail->uid); ctx->mail.uid = mail->uid; mbox_sync_update_x_imap_base(ctx); mbox_sync_update_x_uid(ctx); mbox_sync_add_missing_headers(ctx); } void mbox_sync_update_header_from(struct mbox_sync_mail_context *ctx, const struct mbox_sync_mail *mail) { T_BEGIN { mbox_sync_update_header_from_real(ctx, mail); } T_END; } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-storage.c0000644000175000017500000005626613165463624020204 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "restrict-access.h" #include "master-service.h" #include "mailbox-list-private.h" #include "mbox-storage.h" #include "mbox-lock.h" #include "mbox-file.h" #include "mbox-sync-private.h" #include "istream-raw-mbox.h" #include "mail-copy.h" #include "index-mail.h" #include /* How often to touch the dotlock file when using KEEP_LOCKED flag */ #define MBOX_LOCK_TOUCH_MSECS (10*1000) /* Assume that if atime < mtime, there are new mails. If it's good enough for UW-IMAP, it's good enough for us. */ #define STAT_GET_MARKED(st) \ ((st).st_size == 0 ? MAILBOX_UNMARKED : \ (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED) #define MBOX_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mbox_mailbox_list_module) struct mbox_mailbox_list { union mailbox_list_module_context module_ctx; const struct mbox_settings *set; }; /* NOTE: must be sorted for istream-header-filter. Note that it's not such a good idea to change this list, as the messages will then change from client's point of view. So if you do it, change all mailboxes' UIDVALIDITY so all caches are reset. */ const char *mbox_hide_headers[] = { "Content-Length", "Status", "X-IMAP", "X-IMAPbase", "X-Keywords", "X-Status", "X-UID" }; unsigned int mbox_hide_headers_count = N_ELEMENTS(mbox_hide_headers); /* A bit ugly duplification of the above list. It's safe to modify this list without bad side effects, just keep the list sorted. */ const char *mbox_save_drop_headers[] = { "Content-Length", "Status", "X-Delivery-ID", "X-IMAP", "X-IMAPbase", "X-Keywords", "X-Status", "X-UID" }; unsigned int mbox_save_drop_headers_count = N_ELEMENTS(mbox_save_drop_headers); extern struct mail_storage mbox_storage; extern struct mailbox mbox_mailbox; static MODULE_CONTEXT_DEFINE_INIT(mbox_mailbox_list_module, &mailbox_list_module_register); void mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function) { i_assert(function != NULL); if (ENOQUOTA(errno)) { mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); } else { const char *toobig_error = errno != EFBIG ? "" : " (process was started with ulimit -f limit)"; mail_storage_set_critical(&mbox->storage->storage, "%s failed with mbox file %s: %m%s", function, mailbox_get_path(&mbox->box), toobig_error); } } static int mbox_list_get_path(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type, const char **path_r) { struct mbox_mailbox_list *mlist = MBOX_LIST_CONTEXT(list); const char *path, *p; int ret; *path_r = NULL; ret = mlist->module_ctx.super.get_path(list, name, type, &path); if (ret <= 0) return ret; switch (type) { case MAILBOX_LIST_PATH_TYPE_CONTROL: case MAILBOX_LIST_PATH_TYPE_INDEX: case MAILBOX_LIST_PATH_TYPE_LIST_INDEX: if (name == NULL && type == MAILBOX_LIST_PATH_TYPE_CONTROL && list->set.control_dir != NULL) { /* kind of a kludge for backwards compatibility: the subscriptions file is in the root control_dir without .imap/ suffix */ *path_r = path; return 1; } if (name == NULL) { *path_r = t_strconcat(path, "/"MBOX_INDEX_DIR_NAME, NULL); return 1; } p = strrchr(path, '/'); if (p == NULL) return 0; *path_r = t_strconcat(t_strdup_until(path, p), "/"MBOX_INDEX_DIR_NAME"/", p+1, NULL); break; case MAILBOX_LIST_PATH_TYPE_DIR: case MAILBOX_LIST_PATH_TYPE_ALT_DIR: case MAILBOX_LIST_PATH_TYPE_MAILBOX: case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE: *path_r = path; break; } return 1; } static struct mail_storage *mbox_storage_alloc(void) { struct mbox_storage *storage; pool_t pool; pool = pool_alloconly_create("mbox storage", 512+256); storage = p_new(pool, struct mbox_storage, 1); storage->storage = mbox_storage; storage->storage.pool = pool; return &storage->storage; } static int mbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns, const char **error_r) { struct mbox_storage *storage = (struct mbox_storage *)_storage; struct stat st; const char *dir; if (master_service_get_client_limit(master_service) > 1) { /* we can't handle locking related problems. */ *error_r = "mbox requires client_limit=1 for service"; return -1; } storage->set = mail_namespace_get_driver_settings(ns, _storage); if (mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_INDEX, &dir)) { _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/", mailbox_list_get_temp_prefix(ns->list), NULL); } if (stat(ns->list->set.root_dir, &st) == 0 && !S_ISDIR(st.st_mode)) { *error_r = t_strdup_printf( "mbox root directory can't be a file: %s " "(http://wiki2.dovecot.org/MailLocation/Mbox)", ns->list->set.root_dir); return -1; } return 0; } static void mbox_storage_get_list_settings(const struct mail_namespace *ns, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_FS; if (set->subscription_fname == NULL) set->subscription_fname = MBOX_SUBSCRIPTION_FILE_NAME; if (set->inbox_path == NULL) { set->inbox_path = t_strconcat(set->root_dir, "/inbox", NULL); if (ns->mail_set->mail_debug) i_debug("mbox: INBOX defaulted to %s", set->inbox_path); } } static bool mbox_is_file(const char *path, const char *name, bool debug) { struct stat st; if (stat(path, &st) < 0) { if (debug) { i_debug("mbox autodetect: %s: stat(%s) failed: %m", name, path); } return FALSE; } if (S_ISDIR(st.st_mode)) { if (debug) { i_debug("mbox autodetect: %s: is a directory (%s)", name, path); } return FALSE; } if (access(path, R_OK|W_OK) < 0) { if (debug) { i_debug("mbox autodetect: %s: no R/W access (%s)", name, path); } return FALSE; } if (debug) i_debug("mbox autodetect: %s: yes (%s)", name, path); return TRUE; } static bool mbox_is_dir(const char *path, const char *name, bool debug) { struct stat st; if (stat(path, &st) < 0) { if (debug) { i_debug("mbox autodetect: %s: stat(%s) failed: %m", name, path); } return FALSE; } if (!S_ISDIR(st.st_mode)) { if (debug) { i_debug("mbox autodetect: %s: is not a directory (%s)", name, path); } return FALSE; } if (access(path, R_OK|W_OK|X_OK) < 0) { if (debug) { i_debug("mbox autodetect: %s: no R/W/X access (%s)", name, path); } return FALSE; } if (debug) i_debug("mbox autodetect: %s: yes (%s)", name, path); return TRUE; } static bool mbox_storage_is_root_dir(const char *dir, bool debug) { if (mbox_is_dir(t_strconcat(dir, "/"MBOX_INDEX_DIR_NAME, NULL), "has "MBOX_INDEX_DIR_NAME"/", debug)) return TRUE; if (mbox_is_file(t_strconcat(dir, "/inbox", NULL), "has inbox", debug)) return TRUE; if (mbox_is_file(t_strconcat(dir, "/mbox", NULL), "has mbox", debug)) return TRUE; return FALSE; } static const char *mbox_storage_find_root_dir(const struct mail_namespace *ns) { bool debug = ns->mail_set->mail_debug; const char *home, *path; if (ns->owner == NULL || mail_user_get_home(ns->owner, &home) <= 0) { if (debug) i_debug("maildir: Home directory not set"); home = ""; } path = t_strconcat(home, "/mail", NULL); if (mbox_storage_is_root_dir(path, debug)) return path; path = t_strconcat(home, "/Mail", NULL); if (mbox_storage_is_root_dir(path, debug)) return path; return NULL; } static const char * mbox_storage_find_inbox_file(const char *user, bool debug) { const char *path; path = t_strconcat("/var/mail/", user, NULL); if (access(path, R_OK|W_OK) == 0) { if (debug) i_debug("mbox: INBOX exists (%s)", path); return path; } if (debug) i_debug("mbox: INBOX: access(%s, rw) failed: %m", path); path = t_strconcat("/var/spool/mail/", user, NULL); if (access(path, R_OK|W_OK) == 0) { if (debug) i_debug("mbox: INBOX exists (%s)", path); return path; } if (debug) i_debug("mbox: INBOX: access(%s, rw) failed: %m", path); return NULL; } static bool mbox_storage_autodetect(const struct mail_namespace *ns, struct mailbox_list_settings *set) { bool debug = ns->mail_set->mail_debug; const char *root_dir, *inbox_path; root_dir = set->root_dir; inbox_path = set->inbox_path; if (root_dir != NULL) { if (inbox_path == NULL && mbox_is_file(root_dir, "INBOX file", debug)) { /* using location= */ inbox_path = root_dir; root_dir = NULL; } else if (!mbox_storage_is_root_dir(root_dir, debug)) return FALSE; } if (root_dir == NULL) { root_dir = mbox_storage_find_root_dir(ns); if (root_dir == NULL) { if (debug) i_debug("mbox: couldn't find root dir"); return FALSE; } } if (inbox_path == NULL) { inbox_path = mbox_storage_find_inbox_file(ns->user->username, debug); } set->root_dir = root_dir; set->inbox_path = inbox_path; mbox_storage_get_list_settings(ns, set); return TRUE; } static bool want_memory_indexes(struct mbox_storage *storage, const char *path) { struct stat st; if (storage->set->mbox_min_index_size == 0) return FALSE; if (stat(path, &st) < 0) { if (errno == ENOENT) st.st_size = 0; else { mail_storage_set_critical(&storage->storage, "stat(%s) failed: %m", path); return FALSE; } } return (uoff_t)st.st_size < storage->set->mbox_min_index_size; } static struct mailbox * mbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct mbox_mailbox *mbox; pool_t pool; pool = pool_alloconly_create("mbox mailbox", 1024*3); mbox = p_new(pool, struct mbox_mailbox, 1); mbox->box = mbox_mailbox; mbox->box.pool = pool; mbox->box.storage = storage; mbox->box.list = list; mbox->box.mail_vfuncs = &mbox_mail_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = (struct mbox_storage *)storage; mbox->mbox_fd = -1; mbox->mbox_lock_type = F_UNLCK; mbox->mbox_list_index_ext_id = (uint32_t)-1; if (strcmp(mbox->storage->set->mbox_md5, "apop3d") == 0) mbox->md5_v = mbox_md5_apop3d; else if (strcmp(mbox->storage->set->mbox_md5, "all") == 0) mbox->md5_v = mbox_md5_all; else { i_fatal("Invalid mbox_md5 setting: %s", mbox->storage->set->mbox_md5); } if ((storage->flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0) mbox->mbox_save_md5 = TRUE; return &mbox->box; } static void mbox_lock_touch_timeout(struct mbox_mailbox *mbox) { mbox_dotlock_touch(mbox); } static int mbox_mailbox_open_finish(struct mbox_mailbox *mbox, bool move_to_memory) { if (index_storage_mailbox_open(&mbox->box, move_to_memory) < 0) return -1; mbox->mbox_ext_idx = mail_index_ext_register(mbox->box.index, "mbox", sizeof(mbox->mbox_hdr), sizeof(uint64_t), sizeof(uint64_t)); mbox->md5hdr_ext_idx = mail_index_ext_register(mbox->box.index, "header-md5", 0, 16, 1); return 0; } static int mbox_mailbox_open_existing(struct mbox_mailbox *mbox) { struct mailbox *box = &mbox->box; const char *rootdir, *box_path = mailbox_get_path(box); bool move_to_memory; move_to_memory = want_memory_indexes(mbox->storage, box_path); if (box->inbox_any || strcmp(box->name, "INBOX") == 0) { /* if INBOX isn't under the root directory, it's probably in /var/mail and we want to allow privileged dotlocking */ rootdir = mailbox_list_get_root_forced(box->list, MAILBOX_LIST_PATH_TYPE_DIR); if (strncmp(box_path, rootdir, strlen(rootdir)) != 0) mbox->mbox_privileged_locking = TRUE; } if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) { if (mbox_lock(mbox, F_WRLCK, &mbox->mbox_global_lock_id) <= 0) return -1; if (mbox->mbox_dotlock != NULL) { mbox->keep_lock_to = timeout_add(MBOX_LOCK_TOUCH_MSECS, mbox_lock_touch_timeout, mbox); } } return mbox_mailbox_open_finish(mbox, move_to_memory); } static bool mbox_storage_is_readonly(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; if (index_storage_is_readonly(box)) return TRUE; if (mbox_is_backend_readonly(mbox)) { /* return read-only only if there are no private flags (that are stored in index files) */ if (mailbox_get_private_flags_mask(box) == 0) return TRUE; } return FALSE; } static int mbox_mailbox_open(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; struct stat st; int ret; if (box->input != NULL) { i_stream_ref(box->input); mbox->mbox_file_stream = box->input; mbox->backend_readonly = TRUE; mbox->backend_readonly_set = TRUE; mbox->no_mbox_file = TRUE; return mbox_mailbox_open_finish(mbox, FALSE); } ret = stat(mailbox_get_path(box), &st); if (ret == 0) { if (!S_ISDIR(st.st_mode)) return mbox_mailbox_open_existing(mbox); mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox isn't selectable"); return -1; } else if (ENOTFOUND(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } else if (mail_storage_set_error_from_errno(box->storage)) { return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", mailbox_get_path(box)); return -1; } } static int mbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; int ret = 0; if (!box->opened) { if (mailbox_open(box) < 0) return -1; } if (update->uid_validity != 0 || update->min_next_uid != 0 || !guid_128_is_empty(update->mailbox_guid)) { mbox->sync_hdr_update = update; ret = mbox_sync(mbox, MBOX_SYNC_HEADER | MBOX_SYNC_FORCE_SYNC | MBOX_SYNC_REWRITE); mbox->sync_hdr_update = NULL; } if (ret == 0) ret = index_storage_mailbox_update(box, update); return ret; } static int create_inbox(struct mailbox *box) { const char *inbox_path; int fd; inbox_path = mailbox_get_path(box); fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660); if (fd == -1 && errno == EACCES) { /* try again with increased privileges */ (void)restrict_access_use_priv_gid(); fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660); restrict_access_drop_priv_gid(); } if (fd != -1) { i_close_fd(&fd); return 0; } else if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", mail_error_create_eacces_msg("open", inbox_path)); return -1; } else if (errno == EEXIST) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } else { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", inbox_path); return -1; } } static int mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { int fd, ret; if ((ret = index_storage_mailbox_create(box, directory)) <= 0) return ret; if (box->inbox_any) { if (create_inbox(box) < 0) return -1; } else { /* create the mbox file */ ret = mailbox_create_fd(box, mailbox_get_path(box), O_RDWR | O_CREAT | O_EXCL, &fd); if (ret < 0) return -1; if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } i_close_fd(&fd); } return update == NULL ? 0 : mbox_mailbox_update(box, update); } static void mbox_mailbox_close(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; const struct mail_index_header *hdr; enum mbox_sync_flags sync_flags = 0; if (mbox->mbox_stream != NULL && istream_raw_mbox_is_corrupted(mbox->mbox_stream)) { /* clear the corruption by forcing a full resync */ sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC; } if (box->view != NULL) { hdr = mail_index_get_header(box->view); if ((hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 && !mbox_is_backend_readonly(mbox)) { /* we've done changes to mbox which haven't been written yet. do it now. */ sync_flags |= MBOX_SYNC_REWRITE; } } if (sync_flags != 0 && !mbox->invalid_mbox_file) (void)mbox_sync(mbox, sync_flags); if (mbox->mbox_global_lock_id != 0) mbox_unlock(mbox, mbox->mbox_global_lock_id); if (mbox->keep_lock_to != NULL) timeout_remove(&mbox->keep_lock_to); mbox_file_close(mbox); if (mbox->mbox_file_stream != NULL) i_stream_destroy(&mbox->mbox_file_stream); index_storage_mailbox_close(box); } static int mbox_mailbox_get_guid(struct mbox_mailbox *mbox, guid_128_t guid_r) { const char *errstr; if (mail_index_is_in_memory(mbox->box.index)) { errstr = "Mailbox GUIDs are not permanent without index files"; if (mbox->storage->set->mbox_min_index_size != 0) { errstr = t_strconcat(errstr, " (mbox_min_index_size is non-zero)", NULL); } mail_storage_set_error(mbox->box.storage, MAIL_ERROR_NOTPOSSIBLE, errstr); return -1; } if (mbox_sync_header_refresh(mbox) < 0) return -1; if (!guid_128_is_empty(mbox->mbox_hdr.mailbox_guid)) { /* we have the GUID */ } else if (mbox_file_open(mbox) < 0) return -1; else if (mbox->backend_readonly) { mail_storage_set_error(mbox->box.storage, MAIL_ERROR_PERM, "Can't set mailbox GUID to a read-only mailbox"); return -1; } else { /* create another mailbox and sync */ struct mailbox *box2; struct mbox_mailbox *mbox2; int ret; i_assert(mbox->mbox_lock_type == F_UNLCK); box2 = mailbox_alloc(mbox->box.list, mbox->box.vname, 0); ret = mailbox_sync(box2, 0); mbox2 = (struct mbox_mailbox *)box2; memcpy(guid_r, mbox2->mbox_hdr.mailbox_guid, GUID_128_SIZE); mailbox_free(&box2); return ret; } memcpy(guid_r, mbox->mbox_hdr.mailbox_guid, GUID_128_SIZE); return 0; } static int mbox_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; if ((items & MAILBOX_METADATA_GUID) != 0) { if (mbox_mailbox_get_guid(mbox, metadata_r->guid) < 0) return -1; } return 0; } static void mbox_notify_changes(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; if (box->notify_callback == NULL) mailbox_watch_remove_all(box); else if (!mbox->no_mbox_file) mailbox_watch_add(box, mailbox_get_path(box)); } static bool mbox_is_internal_name(struct mailbox_list *list ATTR_UNUSED, const char *name) { size_t len; /* don't allow *.lock files/dirs */ len = strlen(name); if (len > 5 && strcmp(name+len-5, ".lock") == 0) return TRUE; return strcmp(name, MBOX_INDEX_DIR_NAME) == 0; } static void mbox_storage_add_list(struct mail_storage *storage, struct mailbox_list *list) { struct mbox_mailbox_list *mlist; mlist = p_new(list->pool, struct mbox_mailbox_list, 1); mlist->module_ctx.super = list->v; mlist->set = mail_namespace_get_driver_settings(list->ns, storage); if (strcmp(list->name, MAILBOX_LIST_NAME_FS) == 0 && *list->set.maildir_name == '\0') { /* have to use .imap/ directories */ list->v.get_path = mbox_list_get_path; } list->v.is_internal_name = mbox_is_internal_name; MODULE_CONTEXT_SET(list, mbox_mailbox_list_module, mlist); } static struct mailbox_transaction_context * mbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; struct mbox_transaction_context *mt; if ((flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0) mbox->external_transactions++; mt = i_new(struct mbox_transaction_context, 1); index_transaction_init(&mt->t, box, flags); return &mt->t; } static void mbox_transaction_unlock(struct mailbox *box, unsigned int lock_id1, unsigned int lock_id2) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; if (lock_id1 != 0) mbox_unlock(mbox, lock_id1); if (lock_id2 != 0) mbox_unlock(mbox, lock_id2); if (mbox->mbox_global_lock_id == 0) { i_assert(mbox->box.transaction_count > 0); i_assert(mbox->box.transaction_count > 1 || mbox->external_transactions > 0 || mbox->mbox_lock_type == F_UNLCK); } else { /* mailbox opened with MAILBOX_FLAG_KEEP_LOCKED */ i_assert(mbox->mbox_lock_type == F_WRLCK); } } static int mbox_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct mbox_transaction_context *mt = (struct mbox_transaction_context *)t; struct mailbox *box = t->box; struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; unsigned int read_lock_id = mt->read_lock_id; unsigned int write_lock_id = mt->write_lock_id; int ret; if ((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0) { i_assert(mbox->external_transactions > 0); mbox->external_transactions--; } ret = index_transaction_commit(t, changes_r); mbox_transaction_unlock(box, read_lock_id, write_lock_id); return ret; } static void mbox_transaction_rollback(struct mailbox_transaction_context *t) { struct mbox_transaction_context *mt = (struct mbox_transaction_context *)t; struct mailbox *box = t->box; struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; unsigned int read_lock_id = mt->read_lock_id; unsigned int write_lock_id = mt->write_lock_id; if ((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0) { i_assert(mbox->external_transactions > 0); mbox->external_transactions--; } index_transaction_rollback(t); mbox_transaction_unlock(box, read_lock_id, write_lock_id); } bool mbox_is_backend_readonly(struct mbox_mailbox *mbox) { if (!mbox->backend_readonly_set) { mbox->backend_readonly_set = TRUE; if (access(mailbox_get_path(&mbox->box), R_OK|W_OK) < 0 && errno == EACCES) mbox->backend_readonly = TRUE; } return mbox->backend_readonly; } struct mail_storage mbox_storage = { .name = MBOX_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE | MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS | MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS, .v = { mbox_get_setting_parser_info, mbox_storage_alloc, mbox_storage_create, index_storage_destroy, mbox_storage_add_list, mbox_storage_get_list_settings, mbox_storage_autodetect, mbox_mailbox_alloc, NULL, NULL, } }; struct mailbox mbox_mailbox = { .v = { mbox_storage_is_readonly, index_storage_mailbox_enable, index_storage_mailbox_exists, mbox_mailbox_open, mbox_mailbox_close, index_storage_mailbox_free, mbox_mailbox_create, mbox_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, index_storage_get_status, mbox_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, mbox_list_index_has_changed, mbox_list_index_update_sync, mbox_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, mbox_notify_changes, mbox_transaction_begin, mbox_transaction_commit, mbox_transaction_rollback, NULL, index_mail_alloc, index_storage_search_init, index_storage_search_deinit, index_storage_search_next_nonblock, index_storage_search_next_update_seq, mbox_save_alloc, mbox_save_begin, mbox_save_continue, mbox_save_finish, mbox_save_cancel, mail_storage_copy, mbox_transaction_save_commit_pre, mbox_transaction_save_commit_post, mbox_transaction_save_rollback, index_storage_is_inconsistent } }; dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync-list-index.c0000644000175000017500000000476413165463624021566 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mbox-storage.h" #include "mbox-sync-private.h" static unsigned int mbox_list_get_ext_id(struct mbox_mailbox *mbox, struct mail_index_view *view) { if (mbox->mbox_list_index_ext_id == (uint32_t)-1) { mbox->mbox_list_index_ext_id = mail_index_ext_register(mail_index_view_get_index(view), "mbox", 0, sizeof(struct mbox_list_index_record), sizeof(uint32_t)); } return mbox->mbox_list_index_ext_id; } int mbox_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; const struct mbox_list_index_record *rec; const void *data; const char *path; struct stat st; uint32_t ext_id; bool expunged; int ret; ret = index_storage_list_index_has_changed(box, list_view, seq); if (ret != 0 || box->storage->set->mailbox_list_index_very_dirty_syncs) return ret; ext_id = mbox_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); rec = data; if (rec == NULL || expunged || rec->mtime == 0) { /* doesn't exist or not synced */ return 1; } ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) return ret; i_assert(ret > 0); if (stat(path, &st) < 0) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } if ((time_t)rec->mtime != st.st_mtime || rec->size != (uint32_t)(st.st_size & 0xffffffffU)) return 1; return 0; } void mbox_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; struct mail_index_view *list_view; const struct mbox_index_header *mhdr = &mbox->mbox_hdr; const struct mbox_list_index_record *old_rec; struct mbox_list_index_record new_rec; const void *data; uint32_t ext_id; bool expunged; index_storage_list_index_update_sync(box, trans, seq); /* get the current record */ list_view = mail_index_transaction_get_view(trans); ext_id = mbox_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); if (expunged) return; old_rec = data; i_zero(&new_rec); new_rec.mtime = mhdr->sync_mtime; new_rec.size = mhdr->sync_size & 0xffffffffU; if (old_rec == NULL || memcmp(old_rec, &new_rec, sizeof(*old_rec)) != 0) mail_index_update_ext(trans, seq, ext_id, &new_rec, NULL); } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync.c0000644000175000017500000016621013165463624017503 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ /* Modifying mbox can be slow, so we try to do it all at once minimizing the required disk I/O. We may need to: - Update message flags in Status, X-Status and X-Keywords headers - Write missing X-UID and X-IMAPbase headers - Write missing or broken Content-Length header if there's space - Expunge specified messages Here's how we do it: - Start reading the mails from the beginning - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end of them, remember how much each message has and offset to beginning of the padding - If header needs to be rewritten and there's enough space, do it - If we didn't have enough space, remember how much was missing - Continue reading and counting the padding in each message. If available padding is enough to rewrite all the previous messages needing it, do it - When we encounter expunged message, treat all of it as padding and rewrite previous messages if needed (and there's enough space). Afterwards keep moving messages backwards to fill the expunged space. Moving is done by rewriting each message's headers, with possibly adding missing Content-Length header and padding. Message bodies are moved without modifications. - If we encounter end of file, grow the file and rewrite needed messages - Rewriting is done by moving message body forward, rewriting message's header and doing the same for previous message, until all of them are rewritten. */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "buffer.h" #include "hostpid.h" #include "istream.h" #include "file-set-size.h" #include "str.h" #include "read-full.h" #include "write-full.h" #include "message-date.h" #include "istream-raw-mbox.h" #include "mbox-storage.h" #include "index-sync-changes.h" #include "mailbox-uidvalidity.h" #include "mbox-from.h" #include "mbox-file.h" #include "mbox-lock.h" #include "mbox-sync-private.h" #include #include #include /* The text below was taken exactly as c-client wrote it to my mailbox, so it's probably copyrighted by University of Washington. */ #define PSEUDO_MESSAGE_BODY \ "This text is part of the internal format of your mail folder, and is not\n" \ "a real message. It is created automatically by the mail system software.\n" \ "If deleted, important folder data will be lost, and it will be re-created\n" \ "with the data reset to initial values.\n" void mbox_sync_set_critical(struct mbox_sync_context *sync_ctx, const char *fmt, ...) { va_list va; sync_ctx->errors = TRUE; if (sync_ctx->ext_modified) { mail_storage_set_critical(&sync_ctx->mbox->storage->storage, "mbox file %s was modified while we were syncing, " "check your locking settings", mailbox_get_path(&sync_ctx->mbox->box)); } va_start(va, fmt); mail_storage_set_critical(&sync_ctx->mbox->storage->storage, "Sync failed for mbox file %s: %s", mailbox_get_path(&sync_ctx->mbox->box), t_strdup_vprintf(fmt, va)); va_end(va); } int mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset) { if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) { mbox_sync_set_critical(sync_ctx, "Unexpectedly lost From-line at offset %"PRIuUOFF_T, from_offset); return -1; } return 0; } void mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx) { struct stat st; /* Do this even if ext_modified is already set. Expunging code relies on last_stat being updated. */ if (fstat(sync_ctx->write_fd, &st) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "fstat()"); return; } if (st.st_size != sync_ctx->last_stat.st_size || (sync_ctx->last_stat.st_mtime != 0 && !CMP_ST_MTIME(&st, &sync_ctx->last_stat))) sync_ctx->ext_modified = TRUE; sync_ctx->last_stat = st; } void mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty) { if (dirty) { /* just mark the stat as dirty. */ sync_ctx->last_stat.st_mtime = 0; return; } if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0) mbox_set_syscall_error(sync_ctx->mbox, "fstat()"); i_stream_sync(sync_ctx->input); } static int mbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx) { uoff_t offset; /* get EOF */ (void)istream_raw_mbox_get_header_offset(sync_ctx->input, &offset); if (istream_raw_mbox_is_eof(sync_ctx->input)) return 0; p_clear(sync_ctx->mail_keyword_pool); i_zero(mail_ctx); mail_ctx->sync_ctx = sync_ctx; mail_ctx->seq = ++sync_ctx->seq; mail_ctx->header = sync_ctx->header; mail_ctx->mail.from_offset = istream_raw_mbox_get_start_offset(sync_ctx->input); if (istream_raw_mbox_get_header_offset(sync_ctx->input, &mail_ctx->mail.offset) < 0) { mbox_sync_set_critical(sync_ctx, "Couldn't get header offset for seq=%u", mail_ctx->seq); return -1; } if (mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx) < 0) return -1; if (istream_raw_mbox_is_corrupted(sync_ctx->input)) return -1; i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset || sync_ctx->input->eof); if (istream_raw_mbox_get_body_size(sync_ctx->input, mail_ctx->content_length, &mail_ctx->mail.body_size) < 0) { mbox_sync_set_critical(sync_ctx, "Couldn't get body size for seq=%u", mail_ctx->seq); return -1; } i_assert(mail_ctx->mail.body_size < OFF_T_MAX); if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 && !mail_ctx->mail.pseudo) { if (!sync_ctx->keep_recent) { /* need to add 'O' flag to Status-header */ mail_ctx->need_rewrite = TRUE; } mail_ctx->recent = TRUE; } return 1; } static void mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx, uint32_t uid, bool *sync_expunge_r) { guid_128_t expunged_guid_128; if (uid == 0 || sync_ctx->index_reset) { /* nothing for this or the future ones */ uid = (uint32_t)-1; } index_sync_changes_read(sync_ctx->sync_changes, uid, sync_expunge_r, expunged_guid_128); if (sync_ctx->readonly) { /* we can't expunge anything from read-only mboxes */ *sync_expunge_r = FALSE; } } static bool mbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx, uint32_t uid, const struct mail_index_record **rec_r) { const struct mail_index_record *rec = NULL; uint32_t messages_count; bool ret = FALSE; if (sync_ctx->index_reset) { *rec_r = NULL; return TRUE; } messages_count = mail_index_view_get_messages_count(sync_ctx->sync_view); while (sync_ctx->idx_seq <= messages_count) { rec = mail_index_lookup(sync_ctx->sync_view, sync_ctx->idx_seq); if (uid <= rec->uid) break; /* externally expunged message, remove from index */ mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq); sync_ctx->idx_seq++; rec = NULL; } if (rec == NULL && uid < sync_ctx->idx_next_uid) { /* this UID was already in index and it was expunged */ mbox_sync_set_critical(sync_ctx, "Expunged message reappeared to mailbox " "(UID %u < %u, seq=%u, idx_msgs=%u)", uid, sync_ctx->idx_next_uid, sync_ctx->seq, messages_count); ret = FALSE; rec = NULL; } else if (rec != NULL && rec->uid != uid) { /* new UID in the middle of the mailbox - shouldn't happen */ mbox_sync_set_critical(sync_ctx, "UID inserted in the middle of mailbox " "(%u > %u, seq=%u, idx_msgs=%u)", rec->uid, uid, sync_ctx->seq, messages_count); ret = FALSE; rec = NULL; } else { ret = TRUE; } *rec_r = rec; return ret; } static void mbox_sync_find_index_md5(struct mbox_sync_context *sync_ctx, unsigned char hdr_md5_sum[], const struct mail_index_record **rec_r) { const struct mail_index_record *rec = NULL; uint32_t messages_count; const void *data; if (sync_ctx->index_reset) { *rec_r = NULL; return; } messages_count = mail_index_view_get_messages_count(sync_ctx->sync_view); while (sync_ctx->idx_seq <= messages_count) { rec = mail_index_lookup(sync_ctx->sync_view, sync_ctx->idx_seq); mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq, sync_ctx->mbox->md5hdr_ext_idx, &data, NULL); if (data != NULL && memcmp(data, hdr_md5_sum, 16) == 0) break; /* externally expunged message, remove from index */ mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq); sync_ctx->idx_seq++; rec = NULL; } *rec_r = rec; } static void mbox_sync_update_from_offset(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail *mail, bool nocheck) { const void *data; uint64_t offset; if (!nocheck) { /* see if from_offset needs updating */ mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq, sync_ctx->mbox->mbox_ext_idx, &data, NULL); if (data != NULL && *((const uint64_t *)data) == mail->from_offset) return; } offset = mail->from_offset; mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq, sync_ctx->mbox->mbox_ext_idx, &offset, NULL); } static void mbox_sync_update_index_keywords(struct mbox_sync_mail_context *mail_ctx) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; struct mail_index *index = sync_ctx->mbox->box.index; struct mail_keywords *keywords; keywords = !array_is_created(&mail_ctx->mail.keywords) ? mail_index_keywords_create(index, NULL) : mail_index_keywords_create_from_indexes(index, &mail_ctx->mail.keywords); mail_index_update_keywords(sync_ctx->t, sync_ctx->idx_seq, MODIFY_REPLACE, keywords); mail_index_keywords_unref(&keywords); } static void mbox_sync_update_md5_if_changed(struct mbox_sync_mail_context *mail_ctx) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; const void *ext_data; mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq, sync_ctx->mbox->md5hdr_ext_idx, &ext_data, NULL); if (ext_data == NULL || memcmp(mail_ctx->hdr_md5_sum, ext_data, 16) != 0) { mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq, sync_ctx->mbox->md5hdr_ext_idx, mail_ctx->hdr_md5_sum, NULL); } } static void mbox_sync_get_dirty_flags(struct mbox_sync_mail_context *mail_ctx, const struct mail_index_record *rec) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; ARRAY_TYPE(keyword_indexes) idx_keywords; uint8_t idx_flags, mbox_flags; /* default to undirtying the message. it gets added back if flags/keywords don't match what is in the index. */ mail_ctx->mail.flags &= ~MAIL_INDEX_MAIL_FLAG_DIRTY; /* replace flags */ idx_flags = rec->flags & MAIL_FLAGS_NONRECENT; mbox_flags = mail_ctx->mail.flags & MAIL_FLAGS_NONRECENT; if (idx_flags != mbox_flags) { mail_ctx->need_rewrite = TRUE; mail_ctx->mail.flags = (mail_ctx->mail.flags & MAIL_RECENT) | idx_flags | MAIL_INDEX_MAIL_FLAG_DIRTY; } /* replace keywords */ t_array_init(&idx_keywords, 32); mail_index_lookup_keywords(sync_ctx->sync_view, sync_ctx->idx_seq, &idx_keywords); if (!index_keyword_array_cmp(&idx_keywords, &mail_ctx->mail.keywords)) { mail_ctx->need_rewrite = TRUE; mail_ctx->mail.flags |= MAIL_INDEX_MAIL_FLAG_DIRTY; if (!array_is_created(&mail_ctx->mail.keywords)) { p_array_init(&mail_ctx->mail.keywords, sync_ctx->mail_keyword_pool, array_count(&idx_keywords)); } array_clear(&mail_ctx->mail.keywords); array_append_array(&mail_ctx->mail.keywords, &idx_keywords); } } static void mbox_sync_update_flags(struct mbox_sync_mail_context *mail_ctx, const struct mail_index_record *rec) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; struct mailbox *box = &sync_ctx->mbox->box; struct mbox_sync_mail *mail = &mail_ctx->mail; enum mail_index_sync_type sync_type; ARRAY_TYPE(keyword_indexes) orig_keywords = ARRAY_INIT; uint8_t flags, orig_flags; if (rec != NULL) { if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) { /* flags and keywords are dirty. replace the current ones from the flags in index file. */ mbox_sync_get_dirty_flags(mail_ctx, rec); } } flags = orig_flags = mail->flags & MAIL_FLAGS_NONRECENT; if (array_is_created(&mail->keywords)) { t_array_init(&orig_keywords, 32); array_append_array(&orig_keywords, &mail->keywords); } /* apply new changes */ index_sync_changes_apply(sync_ctx->sync_changes, sync_ctx->mail_keyword_pool, &flags, &mail->keywords, &sync_type); if (flags != orig_flags || !index_keyword_array_cmp(&mail->keywords, &orig_keywords)) { mail_ctx->need_rewrite = TRUE; mail->flags = flags | (mail->flags & MAIL_RECENT) | MAIL_INDEX_MAIL_FLAG_DIRTY; } if (sync_type != 0 && box->v.sync_notify != NULL) { box->v.sync_notify(box, mail_ctx->mail.uid, index_sync_type_convert(sync_type)); } } static void mbox_sync_update_index(struct mbox_sync_mail_context *mail_ctx, const struct mail_index_record *rec) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; struct mbox_sync_mail *mail = &mail_ctx->mail; ARRAY_TYPE(keyword_indexes) idx_keywords; uint8_t mbox_flags; mbox_flags = mail->flags & ~MAIL_RECENT; if (!sync_ctx->delay_writes) { /* changes are written to the mbox file */ mbox_flags &= ~MAIL_INDEX_MAIL_FLAG_DIRTY; } else if (mail_ctx->need_rewrite) { /* make sure this message gets written later */ mbox_flags |= MAIL_INDEX_MAIL_FLAG_DIRTY; } if (rec == NULL) { /* new message */ mail_index_append(sync_ctx->t, mail->uid, &sync_ctx->idx_seq); mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq, MODIFY_REPLACE, mbox_flags); mbox_sync_update_index_keywords(mail_ctx); if (sync_ctx->mbox->mbox_save_md5 != 0) { mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq, sync_ctx->mbox->md5hdr_ext_idx, mail_ctx->hdr_md5_sum, NULL); } } else { if ((rec->flags & MAIL_FLAGS_NONRECENT) != (mbox_flags & MAIL_FLAGS_NONRECENT)) { /* flags other than recent/dirty have changed */ mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq, MODIFY_REPLACE, mbox_flags); } else if (((rec->flags ^ mbox_flags) & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) { /* only dirty flag state changed */ bool dirty; dirty = (mbox_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0; mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq, dirty ? MODIFY_ADD : MODIFY_REMOVE, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY); } /* see if keywords changed */ t_array_init(&idx_keywords, 32); mail_index_lookup_keywords(sync_ctx->sync_view, sync_ctx->idx_seq, &idx_keywords); if (!index_keyword_array_cmp(&idx_keywords, &mail->keywords)) mbox_sync_update_index_keywords(mail_ctx); /* see if we need to update md5 sum. */ if (sync_ctx->mbox->mbox_save_md5 != 0) mbox_sync_update_md5_if_changed(mail_ctx); } if (!mail_ctx->recent) { /* Mail has "Status: O" header. No messages before this can be recent. */ sync_ctx->last_nonrecent_uid = mail->uid; } /* update from_offsets, but not if we're going to rewrite this message. rewriting would just move it anyway. */ if (sync_ctx->need_space_seq == 0) { bool nocheck = rec == NULL || sync_ctx->expunged_space > 0; mbox_sync_update_from_offset(sync_ctx, mail, nocheck); } } static int mbox_read_from_line(struct mbox_sync_mail_context *ctx) { struct istream *input = ctx->sync_ctx->file_input; const unsigned char *data; size_t size, from_line_size; buffer_set_used_size(ctx->sync_ctx->from_line, 0); from_line_size = ctx->hdr_offset - ctx->mail.from_offset; i_stream_seek(input, ctx->mail.from_offset); for (;;) { data = i_stream_get_data(input, &size); if (size >= from_line_size) size = from_line_size; buffer_append(ctx->sync_ctx->from_line, data, size); i_stream_skip(input, size); from_line_size -= size; if (from_line_size == 0) break; if (i_stream_read(input) < 0) return -1; } return 0; } static int mbox_rewrite_base_uid_last(struct mbox_sync_context *sync_ctx) { unsigned char buf[10]; const char *str; uint32_t uid_last; unsigned int i; int ret; i_assert(sync_ctx->base_uid_last_offset != 0); /* first check that the 10 bytes are there and they're exactly as expected. just an extra safety check to make sure we never write to wrong location in the mbox file. */ ret = pread_full(sync_ctx->write_fd, buf, sizeof(buf), sync_ctx->base_uid_last_offset); if (ret < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pread_full()"); return -1; } if (ret == 0) { mbox_sync_set_critical(sync_ctx, "X-IMAPbase uid-last offset unexpectedly outside mbox"); return -1; } for (i = 0, uid_last = 0; i < sizeof(buf); i++) { if (buf[i] < '0' || buf[i] > '9') { uid_last = (uint32_t)-1; break; } uid_last = uid_last * 10 + (buf[i] - '0'); } if (uid_last != sync_ctx->base_uid_last) { mbox_sync_set_critical(sync_ctx, "X-IMAPbase uid-last unexpectedly lost"); return -1; } /* and write it */ str = t_strdup_printf("%010u", sync_ctx->next_uid - 1); if (pwrite_full(sync_ctx->write_fd, str, 10, sync_ctx->base_uid_last_offset) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } mbox_sync_file_updated(sync_ctx, FALSE); sync_ctx->base_uid_last = sync_ctx->next_uid - 1; return 0; } static int mbox_write_from_line(struct mbox_sync_mail_context *ctx) { string_t *str = ctx->sync_ctx->from_line; if (pwrite_full(ctx->sync_ctx->write_fd, str_data(str), str_len(str), ctx->mail.from_offset) < 0) { mbox_set_syscall_error(ctx->sync_ctx->mbox, "pwrite_full()"); return -1; } mbox_sync_file_updated(ctx->sync_ctx, FALSE); return 0; } static void update_from_offsets(struct mbox_sync_context *sync_ctx) { const struct mbox_sync_mail *mails; unsigned int i, count; uint32_t ext_idx; uint64_t offset; ext_idx = sync_ctx->mbox->mbox_ext_idx; mails = array_get(&sync_ctx->mails, &count); for (i = 0; i < count; i++) { if (mails[i].idx_seq == 0 || mails[i].expunged) continue; sync_ctx->moved_offsets = TRUE; offset = mails[i].from_offset; mail_index_update_ext(sync_ctx->t, mails[i].idx_seq, ext_idx, &offset, NULL); } } static void mbox_sync_handle_expunge(struct mbox_sync_mail_context *mail_ctx) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; struct mailbox *box = &sync_ctx->mbox->box; if (box->v.sync_notify != NULL) { box->v.sync_notify(box, mail_ctx->mail.uid, MAILBOX_SYNC_TYPE_EXPUNGE); } mail_index_expunge(sync_ctx->t, mail_ctx->mail.idx_seq); mail_ctx->mail.expunged = TRUE; mail_ctx->mail.offset = mail_ctx->mail.from_offset; mail_ctx->mail.space = mail_ctx->body_offset - mail_ctx->mail.from_offset + mail_ctx->mail.body_size; mail_ctx->mail.body_size = 0; mail_ctx->mail.uid = 0; if (sync_ctx->seq == 1) { /* expunging first message, fix space to contain next message's \n header too since it will be removed. */ mail_ctx->mail.space++; if (istream_raw_mbox_has_crlf_ending(sync_ctx->input)) { mail_ctx->mail.space++; sync_ctx->first_mail_crlf_expunged = TRUE; } /* uid-last offset is invalid now */ sync_ctx->base_uid_last_offset = 0; } sync_ctx->expunged_space += mail_ctx->mail.space; } static int mbox_sync_handle_header(struct mbox_sync_mail_context *mail_ctx) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; uoff_t orig_from_offset, postlf_from_offset = (uoff_t)-1; off_t move_diff; int ret; if (sync_ctx->expunged_space > 0 && sync_ctx->need_space_seq == 0) { /* move the header backwards to fill expunged space */ move_diff = -sync_ctx->expunged_space; orig_from_offset = mail_ctx->mail.from_offset; if (sync_ctx->dest_first_mail) { /* we're moving this mail to beginning of file. skip the initial \n (it's already counted in expunged_space) */ mail_ctx->mail.from_offset++; if (sync_ctx->first_mail_crlf_expunged) mail_ctx->mail.from_offset++; } postlf_from_offset = mail_ctx->mail.from_offset; /* read the From-line before rewriting overwrites it */ if (mbox_read_from_line(mail_ctx) < 0) return -1; i_assert(mail_ctx->mail.from_offset + move_diff != 1 && mail_ctx->mail.from_offset + move_diff != 2); mbox_sync_update_header(mail_ctx); ret = mbox_sync_try_rewrite(mail_ctx, move_diff); if (ret < 0) return -1; if (ret > 0) { /* rewrite successful, write From-line to new location */ i_assert(move_diff > 0 || (off_t)mail_ctx->mail.from_offset >= -move_diff); mail_ctx->mail.from_offset += move_diff; mail_ctx->mail.offset += move_diff; if (mbox_write_from_line(mail_ctx) < 0) return -1; } else { if (sync_ctx->dest_first_mail) { /* didn't have enough space, move the offset back so seeking into it doesn't fail */ mail_ctx->mail.from_offset = orig_from_offset; } } } else if (mail_ctx->need_rewrite) { mbox_sync_update_header(mail_ctx); if (sync_ctx->delay_writes && sync_ctx->need_space_seq == 0) { /* mark it dirty and do it later. we can't do this if we're in the middle of rewriting acquiring more space. */ mail_ctx->dirty = TRUE; return 0; } if ((ret = mbox_sync_try_rewrite(mail_ctx, 0)) < 0) return -1; } else { /* nothing to do */ return 0; } if (ret == 0 && sync_ctx->need_space_seq == 0) { /* first mail with no space to write it */ sync_ctx->need_space_seq = sync_ctx->seq; sync_ctx->space_diff = 0; if (sync_ctx->expunged_space > 0) { /* create dummy message to describe the expunged data */ struct mbox_sync_mail mail; /* if this is going to be the first mail, increase the from_offset to point to the beginning of the From-line, because the previous [CR]LF is already covered by expunged_space. */ i_assert(postlf_from_offset != (uoff_t)-1); mail_ctx->mail.from_offset = postlf_from_offset; i_zero(&mail); mail.expunged = TRUE; mail.offset = mail.from_offset = mail_ctx->mail.from_offset - sync_ctx->expunged_space; mail.space = sync_ctx->expunged_space; sync_ctx->space_diff = sync_ctx->expunged_space; sync_ctx->expunged_space = 0; i_assert(sync_ctx->space_diff < -mail_ctx->mail.space); sync_ctx->need_space_seq--; array_append(&sync_ctx->mails, &mail, 1); } } return 0; } static int mbox_sync_handle_missing_space(struct mbox_sync_mail_context *mail_ctx) { struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx; uoff_t end_offset, move_diff, extra_space, needed_space; uint32_t last_seq; ARRAY_TYPE(keyword_indexes) keywords_copy; i_assert(mail_ctx->mail.uid == 0 || mail_ctx->mail.space > 0 || mail_ctx->mail.offset == mail_ctx->hdr_offset); if (array_is_created(&mail_ctx->mail.keywords)) { /* mail's keywords are allocated from a pool that's cleared for each mail. we'll need to copy it to something more permanent. */ p_array_init(&keywords_copy, sync_ctx->saved_keywords_pool, array_count(&mail_ctx->mail.keywords)); array_append_array(&keywords_copy, &mail_ctx->mail.keywords); mail_ctx->mail.keywords = keywords_copy; } array_append(&sync_ctx->mails, &mail_ctx->mail, 1); sync_ctx->space_diff += mail_ctx->mail.space; if (sync_ctx->space_diff < 0) { if (sync_ctx->expunged_space > 0) { i_assert(sync_ctx->expunged_space == mail_ctx->mail.space); sync_ctx->expunged_space = 0; } return 0; } /* we have enough space now */ if (mail_ctx->mail.uid == 0) { /* this message was expunged. fill more or less of the space. space_diff now consists of a negative "bytes needed" sum, plus the expunged space of this message. so it contains how many bytes of _extra_ space we have. */ i_assert(mail_ctx->mail.space >= sync_ctx->space_diff); extra_space = MBOX_HEADER_PADDING * (sync_ctx->seq - sync_ctx->need_space_seq + 1); needed_space = mail_ctx->mail.space - sync_ctx->space_diff; if ((uoff_t)sync_ctx->space_diff > needed_space + extra_space) { /* don't waste too much on padding */ move_diff = needed_space + extra_space; sync_ctx->expunged_space = mail_ctx->mail.space - move_diff; } else { move_diff = mail_ctx->mail.space; extra_space = sync_ctx->space_diff; sync_ctx->expunged_space = 0; } last_seq = sync_ctx->seq - 1; array_delete(&sync_ctx->mails, array_count(&sync_ctx->mails) - 1, 1); end_offset = mail_ctx->mail.from_offset; } else { /* this message gave enough space from headers. rewriting stops at the end of this message's headers. */ sync_ctx->expunged_space = 0; last_seq = sync_ctx->seq; end_offset = mail_ctx->body_offset; move_diff = 0; extra_space = sync_ctx->space_diff; } mbox_sync_file_update_ext_modified(sync_ctx); if (mbox_sync_rewrite(sync_ctx, last_seq == sync_ctx->seq ? mail_ctx : NULL, end_offset, move_diff, extra_space, sync_ctx->need_space_seq, last_seq) < 0) return -1; update_from_offsets(sync_ctx); /* mail_ctx may contain wrong data after rewrite, so make sure we don't try to access it */ i_zero(mail_ctx); sync_ctx->need_space_seq = 0; sync_ctx->space_diff = 0; array_clear(&sync_ctx->mails); p_clear(sync_ctx->saved_keywords_pool); return 0; } static int mbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq) { struct mbox_mailbox *mbox = sync_ctx->mbox; uoff_t old_offset, offset; uint32_t uid; int ret; bool deleted; if (seq == 0) { if (istream_raw_mbox_seek(mbox->mbox_stream, 0) < 0) { mbox->invalid_mbox_file = TRUE; mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox isn't a valid mbox file"); return -1; } seq++; } else { old_offset = istream_raw_mbox_get_start_offset(sync_ctx->input); ret = mbox_file_seek(mbox, sync_ctx->sync_view, seq, &deleted); if (ret < 0) { if (deleted) { mbox_sync_set_critical(sync_ctx, "Message was expunged unexpectedly"); } return -1; } if (ret == 0) { if (istream_raw_mbox_seek(mbox->mbox_stream, old_offset) < 0) { mbox_sync_set_critical(sync_ctx, "Error seeking back to original " "offset %s", dec2str(old_offset)); return -1; } return 0; } } if (seq <= 1) uid = 0; else mail_index_lookup_uid(sync_ctx->sync_view, seq-1, &uid); sync_ctx->prev_msg_uid = uid; /* set to -1, since it's always increased later */ sync_ctx->seq = seq-1; if (sync_ctx->seq == 0 && istream_raw_mbox_get_start_offset(sync_ctx->input) != 0) { /* this mbox has pseudo mail which contains the X-IMAP header */ sync_ctx->seq++; } sync_ctx->idx_seq = seq; sync_ctx->dest_first_mail = sync_ctx->seq == 0; if (istream_raw_mbox_get_body_offset(sync_ctx->input, &offset) < 0) { mbox_sync_set_critical(sync_ctx, "Message body offset lookup failed"); return -1; } return 1; } static int mbox_sync_seek_to_uid(struct mbox_sync_context *sync_ctx, uint32_t uid) { struct mail_index_view *sync_view = sync_ctx->sync_view; uint32_t seq1, seq2; uoff_t size; int ret; i_assert(!sync_ctx->index_reset); if (!mail_index_lookup_seq_range(sync_view, uid, (uint32_t)-1, &seq1, &seq2)) { /* doesn't exist anymore, seek to end of file */ ret = i_stream_get_size(sync_ctx->file_input, TRUE, &size); if (ret < 0) { mbox_set_syscall_error(sync_ctx->mbox, "i_stream_get_size()"); return -1; } i_assert(ret != 0); if (istream_raw_mbox_seek(sync_ctx->mbox->mbox_stream, size) < 0) { mbox_sync_set_critical(sync_ctx, "Error seeking to end of mbox"); return -1; } sync_ctx->idx_seq = mail_index_view_get_messages_count(sync_view) + 1; return 1; } return mbox_sync_seek_to_seq(sync_ctx, seq1); } static int mbox_sync_partial_seek_next(struct mbox_sync_context *sync_ctx, uint32_t next_uid, bool *partial, bool *skipped_mails) { uint32_t messages_count, uid; int ret; i_assert(!sync_ctx->index_reset); /* delete sync records up to next message. so if there's still something left in array, it means the next message needs modifying */ index_sync_changes_delete_to(sync_ctx->sync_changes, next_uid); if (index_sync_changes_have(sync_ctx->sync_changes)) return 1; if (sync_ctx->hdr->first_recent_uid <= next_uid && !sync_ctx->keep_recent) { /* we'll need to rewrite Status: O headers */ return 1; } uid = index_sync_changes_get_next_uid(sync_ctx->sync_changes); if (sync_ctx->hdr->first_recent_uid < sync_ctx->hdr->next_uid && (uid > sync_ctx->hdr->first_recent_uid || uid == 0) && !sync_ctx->keep_recent) { /* we'll need to rewrite Status: O headers */ uid = sync_ctx->hdr->first_recent_uid; } if (uid != 0) { /* we can skip forward to next record which needs updating. */ if (uid != next_uid) { *skipped_mails = TRUE; next_uid = uid; } ret = mbox_sync_seek_to_uid(sync_ctx, next_uid); } else { /* if there's no sync records left, we can stop. except if this is a dirty sync, check if there are new messages. */ if (!sync_ctx->mbox->mbox_hdr.dirty_flag) return 0; messages_count = mail_index_view_get_messages_count(sync_ctx->sync_view); if (sync_ctx->seq + 1 != messages_count) { ret = mbox_sync_seek_to_seq(sync_ctx, messages_count); *skipped_mails = TRUE; } else { ret = 1; } *partial = FALSE; } if (ret == 0) { /* seek failed because the offset is dirty. just ignore and continue from where we are now. */ *partial = FALSE; ret = 1; } return ret; } static void mbox_sync_hdr_update(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx) { const struct mailbox_update *update = sync_ctx->mbox->sync_hdr_update; if (update->uid_validity != 0) { sync_ctx->base_uid_validity = update->uid_validity; mail_ctx->imapbase_rewrite = TRUE; mail_ctx->need_rewrite = TRUE; } if (update->min_next_uid != 0 && sync_ctx->base_uid_last+1 < update->min_next_uid) { i_assert(sync_ctx->next_uid <= update->min_next_uid); sync_ctx->base_uid_last = update->min_next_uid-1; sync_ctx->next_uid = update->min_next_uid; mail_ctx->imapbase_rewrite = TRUE; mail_ctx->need_rewrite = TRUE; } } static bool mbox_sync_imapbase(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx) { if (sync_ctx->base_uid_validity != 0 && sync_ctx->hdr->uid_validity != 0 && sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity) { i_warning("UIDVALIDITY changed (%u -> %u) in mbox file %s", sync_ctx->hdr->uid_validity, sync_ctx->base_uid_validity, mailbox_get_path(&sync_ctx->mbox->box)); sync_ctx->index_reset = TRUE; return TRUE; } if (sync_ctx->mbox->sync_hdr_update != NULL) mbox_sync_hdr_update(sync_ctx, mail_ctx); return FALSE; } static int mbox_sync_loop(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx, bool partial) { const struct mail_index_record *rec; uint32_t uid, messages_count; uoff_t offset; int ret; bool expunged, skipped_mails, uids_broken; messages_count = mail_index_view_get_messages_count(sync_ctx->sync_view); /* always start from first message so we can read X-IMAP or X-IMAPbase header */ ret = mbox_sync_seek_to_seq(sync_ctx, 0); if (ret <= 0) return ret; if (sync_ctx->renumber_uids) { /* expunge everything */ while (sync_ctx->idx_seq <= messages_count) { mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq++); } } skipped_mails = uids_broken = FALSE; while ((ret = mbox_sync_read_next_mail(sync_ctx, mail_ctx)) > 0) { uid = mail_ctx->mail.uid; if (mail_ctx->seq == 1) { if (mbox_sync_imapbase(sync_ctx, mail_ctx)) { sync_ctx->mbox->mbox_hdr.dirty_flag = TRUE; return 0; } } if (mail_ctx->mail.uid_broken && partial) { /* UID ordering problems, resync everything to make sure we get everything right */ if (sync_ctx->mbox->mbox_hdr.dirty_flag) return 0; mbox_sync_set_critical(sync_ctx, "UIDs broken with partial sync"); sync_ctx->mbox->mbox_hdr.dirty_flag = TRUE; return 0; } if (mail_ctx->mail.uid_broken) uids_broken = TRUE; if (mail_ctx->mail.pseudo) uid = 0; rec = NULL; ret = 1; if (uid != 0) { if (!mbox_sync_read_index_rec(sync_ctx, uid, &rec)) ret = 0; } if (ret == 0) { /* UID found but it's broken */ uid = 0; } else if (uid == 0 && !mail_ctx->mail.pseudo && (sync_ctx->delay_writes || sync_ctx->idx_seq <= messages_count)) { /* If we can't use/store X-UID header, use MD5 sum. Also check for existing MD5 sums when we're actually able to write X-UIDs. */ sync_ctx->mbox->mbox_save_md5 = TRUE; mbox_sync_find_index_md5(sync_ctx, mail_ctx->hdr_md5_sum, &rec); if (rec != NULL) uid = mail_ctx->mail.uid = rec->uid; } /* get all sync records related to this message. with pseudo message just get the first sync record so we can jump to it with partial seeking. */ mbox_sync_read_index_syncs(sync_ctx, mail_ctx->mail.pseudo ? 1 : uid, &expunged); if (mail_ctx->mail.pseudo) { /* if it was set, it was for the next message */ expunged = FALSE; } else { if (rec == NULL) { /* message wasn't found from index. we have to read everything from now on, no skipping */ partial = FALSE; } } if (uid == 0 && !mail_ctx->mail.pseudo) { /* missing/broken X-UID. all the rest of the mails need new UIDs. */ while (sync_ctx->idx_seq <= messages_count) { mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq++); } if (sync_ctx->next_uid == (uint32_t)-1) { /* oh no, we're out of UIDs. this shouldn't happen normally, so just try to get it fixed without crashing. */ mail_storage_set_critical( &sync_ctx->mbox->storage->storage, "Out of UIDs, renumbering them in mbox " "file %s", mailbox_get_path(&sync_ctx->mbox->box)); sync_ctx->renumber_uids = TRUE; return 0; } mail_ctx->need_rewrite = TRUE; mail_ctx->mail.uid = sync_ctx->next_uid++; } sync_ctx->prev_msg_uid = mail_ctx->mail.uid; if (!mail_ctx->mail.pseudo) mail_ctx->mail.idx_seq = sync_ctx->idx_seq; if (!expunged) { if (!mail_ctx->mail.pseudo) T_BEGIN { mbox_sync_update_flags(mail_ctx, rec); } T_END; if (mbox_sync_handle_header(mail_ctx) < 0) return -1; sync_ctx->dest_first_mail = FALSE; } else { mbox_sync_handle_expunge(mail_ctx); } if (!mail_ctx->mail.pseudo) { if (!expunged) T_BEGIN { mbox_sync_update_index(mail_ctx, rec); } T_END; sync_ctx->idx_seq++; } if (istream_raw_mbox_next(sync_ctx->input, mail_ctx->mail.body_size) < 0) return -1; offset = istream_raw_mbox_get_start_offset(sync_ctx->input); if (sync_ctx->need_space_seq != 0) { if (mbox_sync_handle_missing_space(mail_ctx) < 0) return -1; if (mbox_sync_seek(sync_ctx, offset) < 0) return -1; } else if (sync_ctx->expunged_space > 0) { if (!expunged) { /* move the body */ mbox_sync_file_update_ext_modified(sync_ctx); if (mbox_move(sync_ctx, mail_ctx->body_offset - sync_ctx->expunged_space, mail_ctx->body_offset, mail_ctx->mail.body_size) < 0) return -1; if (mbox_sync_seek(sync_ctx, offset) < 0) return -1; } } else if (partial) { ret = mbox_sync_partial_seek_next(sync_ctx, uid + 1, &partial, &skipped_mails); if (ret <= 0) break; } } if (ret < 0) return -1; if (istream_raw_mbox_is_eof(sync_ctx->input)) { /* rest of the messages in index don't exist -> expunge them */ while (sync_ctx->idx_seq <= messages_count) mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq++); } if (!skipped_mails) sync_ctx->mbox->mbox_hdr.dirty_flag = FALSE; sync_ctx->mbox->mbox_broken_offsets = FALSE; if (uids_broken && sync_ctx->delay_writes) { /* once we get around to writing the changes, we'll need to do a full sync to avoid the "UIDs broken in partial sync" error */ sync_ctx->mbox->mbox_hdr.dirty_flag = TRUE; } return 1; } static int mbox_write_pseudo(struct mbox_sync_context *sync_ctx) { string_t *str; unsigned int uid_validity; i_assert(sync_ctx->write_fd != -1); if (sync_ctx->mbox->sync_hdr_update != NULL) { const struct mailbox_update *update = sync_ctx->mbox->sync_hdr_update; if (update->uid_validity != 0) sync_ctx->base_uid_validity = update->uid_validity; if (update->min_next_uid != 0) sync_ctx->base_uid_last = update->min_next_uid-1; } uid_validity = sync_ctx->base_uid_validity != 0 ? sync_ctx->base_uid_validity : sync_ctx->hdr->uid_validity; i_assert(uid_validity != 0); str = t_str_new(1024); str_printfa(str, "%sDate: %s\n" "From: Mail System Internal Data \n" "Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA" "\nMessage-ID: <%s@%s>\n" "X-IMAP: %u %010u\n" "Status: RO\n" "\n" PSEUDO_MESSAGE_BODY "\n", mbox_from_create("MAILER_DAEMON", ioloop_time), message_date_create(ioloop_time), my_hostname, dec2str(ioloop_time), my_hostname, uid_validity, sync_ctx->next_uid-1); if (pwrite_full(sync_ctx->write_fd, str_data(str), str_len(str), 0) < 0) { if (!ENOSPACE(errno)) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } /* out of disk space, truncate to empty */ if (ftruncate(sync_ctx->write_fd, 0) < 0) mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()"); } sync_ctx->base_uid_validity = uid_validity; sync_ctx->base_uid_last_offset = 0; /* don't bother calculating */ sync_ctx->base_uid_last = sync_ctx->next_uid-1; return 0; } static int mbox_append_zero(struct mbox_sync_context *sync_ctx, uoff_t orig_file_size, uoff_t count) { char block[IO_BLOCK_SIZE]; uoff_t offset = orig_file_size; ssize_t ret = 0; memset(block, 0, I_MIN(sizeof(block), count)); while (count > 0) { ret = pwrite(sync_ctx->write_fd, block, I_MIN(sizeof(block), count), offset); if (ret < 0) break; offset += ret; count -= ret; } if (ret < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite()"); if (ftruncate(sync_ctx->write_fd, orig_file_size) < 0) mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()"); return -1; } return 0; } static int mbox_sync_handle_eof_updates(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx) { uoff_t file_size, offset, padding, trailer_size; int ret; if (!istream_raw_mbox_is_eof(sync_ctx->input)) { i_assert(sync_ctx->need_space_seq == 0); i_assert(sync_ctx->expunged_space == 0); return 0; } ret = i_stream_get_size(sync_ctx->file_input, TRUE, &file_size); if (ret < 0) { mbox_set_syscall_error(sync_ctx->mbox, "i_stream_get_size()"); return -1; } if (ret == 0) { /* Not a file - allow anyway */ return 0; } if (file_size < sync_ctx->file_input->v_offset) { mbox_sync_set_critical(sync_ctx, "file size unexpectedly shrank " "(%"PRIuUOFF_T" vs %"PRIuUOFF_T")", file_size, sync_ctx->file_input->v_offset); return -1; } trailer_size = file_size - sync_ctx->file_input->v_offset; i_assert(trailer_size <= 2); if (sync_ctx->need_space_seq != 0) { i_assert(sync_ctx->write_fd != -1); i_assert(sync_ctx->space_diff < 0); padding = MBOX_HEADER_PADDING * (sync_ctx->seq - sync_ctx->need_space_seq + 1); sync_ctx->space_diff -= padding; i_assert(sync_ctx->expunged_space <= -sync_ctx->space_diff); sync_ctx->space_diff += sync_ctx->expunged_space; sync_ctx->expunged_space = 0; if (mail_ctx->have_eoh && !mail_ctx->updated) str_append_c(mail_ctx->header, '\n'); i_assert(sync_ctx->space_diff < 0); if (mbox_append_zero(sync_ctx, file_size, -sync_ctx->space_diff) < 0) return -1; mbox_sync_file_updated(sync_ctx, FALSE); if (mbox_sync_rewrite(sync_ctx, mail_ctx, file_size, -sync_ctx->space_diff, padding, sync_ctx->need_space_seq, sync_ctx->seq) < 0) return -1; update_from_offsets(sync_ctx); sync_ctx->need_space_seq = 0; array_clear(&sync_ctx->mails); p_clear(sync_ctx->saved_keywords_pool); } if (sync_ctx->expunged_space > 0) { i_assert(sync_ctx->write_fd != -1); mbox_sync_file_update_ext_modified(sync_ctx); /* copy trailer, then truncate the file */ file_size = sync_ctx->last_stat.st_size; if (file_size == (uoff_t)sync_ctx->expunged_space) { /* everything deleted, the trailer_size still contains the \n trailer though */ trailer_size = 0; } else if (sync_ctx->expunged_space == (off_t)file_size + 1 || sync_ctx->expunged_space == (off_t)file_size + 2) { /* everything deleted and we didn't have a proper trailer. */ trailer_size = 0; sync_ctx->expunged_space = file_size; } i_assert(file_size >= sync_ctx->expunged_space + trailer_size); offset = file_size - sync_ctx->expunged_space - trailer_size; i_assert(offset == 0 || offset > 31); if (mbox_move(sync_ctx, offset, offset + sync_ctx->expunged_space, trailer_size) < 0) return -1; if (ftruncate(sync_ctx->write_fd, offset + trailer_size) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()"); return -1; } if (offset == 0) { if (mbox_write_pseudo(sync_ctx) < 0) return -1; } sync_ctx->expunged_space = 0; mbox_sync_file_updated(sync_ctx, FALSE); } else { if (file_size == 0 && sync_ctx->mbox->sync_hdr_update != NULL) { if (mbox_write_pseudo(sync_ctx) < 0) return -1; } } return 0; } static void mbox_sync_index_update_ext_header(struct mbox_mailbox *mbox, struct mail_index_transaction *trans) { const struct mailbox_update *update = mbox->sync_hdr_update; const void *data; size_t data_size; if (update != NULL && !guid_128_is_empty(update->mailbox_guid)) { memcpy(mbox->mbox_hdr.mailbox_guid, update->mailbox_guid, sizeof(mbox->mbox_hdr.mailbox_guid)); } else if (guid_128_is_empty(mbox->mbox_hdr.mailbox_guid)) { guid_128_generate(mbox->mbox_hdr.mailbox_guid); } mail_index_get_header_ext(mbox->box.view, mbox->mbox_ext_idx, &data, &data_size); if (data_size != sizeof(mbox->mbox_hdr) || memcmp(data, &mbox->mbox_hdr, data_size) != 0) { if (data_size != sizeof(mbox->mbox_hdr)) { /* upgrading from v1.x */ mail_index_ext_resize(trans, mbox->mbox_ext_idx, sizeof(mbox->mbox_hdr), sizeof(uint64_t), sizeof(uint64_t)); } mail_index_update_header_ext(trans, mbox->mbox_ext_idx, 0, &mbox->mbox_hdr, sizeof(mbox->mbox_hdr)); } } static uint32_t mbox_get_uidvalidity_next(struct mailbox_list *list) { const char *path; path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL); path = t_strconcat(path, "/"MBOX_UIDVALIDITY_FNAME, NULL); return mailbox_uidvalidity_next(list, path); } static int mbox_sync_update_index_header(struct mbox_sync_context *sync_ctx) { struct mail_index_view *view; const struct stat *st; uint32_t first_recent_uid, seq, seq2; if (i_stream_stat(sync_ctx->file_input, FALSE, &st) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()"); return -1; } if (sync_ctx->moved_offsets && ((uint64_t)st->st_size == sync_ctx->mbox->mbox_hdr.sync_size || (uint64_t)st->st_size == sync_ctx->orig_size)) { /* We moved messages inside the mbox file without changing the file's size. If mtime doesn't change, another process not using the same index file as us can't know that the file was changed. So make sure the mtime changes. This should happen rarely enough that the sleeping doesn't become a performance problem. Note that to do this perfectly safe we should do this wait whenever mails are moved or expunged, regardless of whether the file's size changed. That however could become a performance problem and the consequences of being wrong are quite minimal (an extra logged error message). */ while (sync_ctx->orig_mtime == st->st_mtime) { usleep(500000); if (utime(mailbox_get_path(&sync_ctx->mbox->box), NULL) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "utime()"); return -1; } if (i_stream_stat(sync_ctx->file_input, FALSE, &st) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()"); return -1; } } } sync_ctx->mbox->mbox_hdr.sync_mtime = st->st_mtime; sync_ctx->mbox->mbox_hdr.sync_size = st->st_size; mbox_sync_index_update_ext_header(sync_ctx->mbox, sync_ctx->t); /* only reason not to have UID validity at this point is if the file is entirely empty. In that case just make up a new one if needed. */ i_assert(sync_ctx->base_uid_validity != 0 || st->st_size <= 0); if (sync_ctx->base_uid_validity == 0) { sync_ctx->base_uid_validity = sync_ctx->hdr->uid_validity != 0 ? sync_ctx->hdr->uid_validity : mbox_get_uidvalidity_next(sync_ctx->mbox->box.list); } if (sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity) { mail_index_update_header(sync_ctx->t, offsetof(struct mail_index_header, uid_validity), &sync_ctx->base_uid_validity, sizeof(sync_ctx->base_uid_validity), TRUE); } if (istream_raw_mbox_is_eof(sync_ctx->input) && sync_ctx->next_uid != sync_ctx->hdr->next_uid) { i_assert(sync_ctx->next_uid != 0); mail_index_update_header(sync_ctx->t, offsetof(struct mail_index_header, next_uid), &sync_ctx->next_uid, sizeof(sync_ctx->next_uid), FALSE); } if (sync_ctx->last_nonrecent_uid < sync_ctx->hdr->first_recent_uid) { /* other sessions have already marked more messages as recent. */ sync_ctx->last_nonrecent_uid = sync_ctx->hdr->first_recent_uid - 1; } /* mark recent messages */ view = mail_index_transaction_open_updated_view(sync_ctx->t); if (mail_index_lookup_seq_range(view, sync_ctx->last_nonrecent_uid + 1, (uint32_t)-1, &seq, &seq2)) { mailbox_recent_flags_set_seqs(&sync_ctx->mbox->box, view, seq, seq2); } mail_index_view_close(&view); first_recent_uid = !sync_ctx->keep_recent ? sync_ctx->next_uid : sync_ctx->last_nonrecent_uid + 1; if (sync_ctx->hdr->first_recent_uid < first_recent_uid) { mail_index_update_header(sync_ctx->t, offsetof(struct mail_index_header, first_recent_uid), &first_recent_uid, sizeof(first_recent_uid), FALSE); } return 0; } static void mbox_sync_restart(struct mbox_sync_context *sync_ctx) { sync_ctx->base_uid_validity = 0; sync_ctx->base_uid_last = 0; sync_ctx->base_uid_last_offset = 0; array_clear(&sync_ctx->mails); p_clear(sync_ctx->saved_keywords_pool); index_sync_changes_reset(sync_ctx->sync_changes); mail_index_sync_reset(sync_ctx->index_sync_ctx); mail_index_transaction_reset(sync_ctx->t); if (sync_ctx->index_reset) { mail_index_reset(sync_ctx->t); sync_ctx->reset_hdr.next_uid = 1; sync_ctx->hdr = &sync_ctx->reset_hdr; mailbox_recent_flags_reset(&sync_ctx->mbox->box); } sync_ctx->prev_msg_uid = 0; sync_ctx->next_uid = sync_ctx->hdr->next_uid; sync_ctx->idx_next_uid = sync_ctx->hdr->next_uid; sync_ctx->seq = 0; sync_ctx->idx_seq = 1; sync_ctx->need_space_seq = 0; sync_ctx->expunged_space = 0; sync_ctx->space_diff = 0; sync_ctx->dest_first_mail = TRUE; sync_ctx->ext_modified = FALSE; sync_ctx->errors = FALSE; } static int mbox_sync_do(struct mbox_sync_context *sync_ctx, enum mbox_sync_flags flags) { struct mbox_index_header *mbox_hdr = &sync_ctx->mbox->mbox_hdr; struct mbox_sync_mail_context mail_ctx; const struct stat *st; unsigned int i; int ret, partial; if (i_stream_stat(sync_ctx->file_input, FALSE, &st) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()"); return -1; } sync_ctx->last_stat = *st; sync_ctx->orig_size = st->st_size; sync_ctx->orig_atime = st->st_atime; sync_ctx->orig_mtime = st->st_mtime; if ((flags & MBOX_SYNC_FORCE_SYNC) != 0) { /* forcing a full sync. assume file has changed. */ partial = FALSE; mbox_hdr->dirty_flag = TRUE; } else if ((uint32_t)st->st_mtime == mbox_hdr->sync_mtime && (uint64_t)st->st_size == mbox_hdr->sync_size) { /* file is fully synced */ if (mbox_hdr->dirty_flag && (flags & MBOX_SYNC_UNDIRTY) != 0) partial = FALSE; else partial = TRUE; } else if ((flags & MBOX_SYNC_UNDIRTY) != 0 || (uint64_t)st->st_size == mbox_hdr->sync_size) { /* we want to do full syncing. always do this if file size hasn't changed but timestamp has. it most likely means that someone had modified some header and we probably want to know about it */ partial = FALSE; sync_ctx->mbox->mbox_hdr.dirty_flag = TRUE; } else { /* see if we can delay syncing the whole file. normally we only notice expunges and appends in partial syncing. */ partial = TRUE; sync_ctx->mbox->mbox_hdr.dirty_flag = TRUE; } mbox_sync_restart(sync_ctx); for (i = 0;;) { ret = mbox_sync_loop(sync_ctx, &mail_ctx, partial); if (ret > 0 && !sync_ctx->errors) break; if (ret < 0) return -1; /* a) partial sync didn't work b) we ran out of UIDs c) syncing had errors */ if (sync_ctx->delay_writes && (sync_ctx->errors || sync_ctx->renumber_uids)) { /* fixing a broken mbox state, be sure to write the changes (except if we're readonly). */ if (!sync_ctx->readonly) sync_ctx->delay_writes = FALSE; } if (++i == 3) break; mbox_sync_restart(sync_ctx); partial = FALSE; } if (mbox_sync_handle_eof_updates(sync_ctx, &mail_ctx) < 0) return -1; /* only syncs left should be just appends (and their updates) which weren't synced yet for some reason (crash). we'll just ignore them, as we've overwritten them above. */ index_sync_changes_reset(sync_ctx->sync_changes); if (sync_ctx->base_uid_last != sync_ctx->next_uid-1 && ret > 0 && !sync_ctx->delay_writes && sync_ctx->base_uid_last_offset != 0) { /* Rewrite uid_last in X-IMAPbase header if we've seen it (ie. the file isn't empty) */ ret = mbox_rewrite_base_uid_last(sync_ctx); } else { ret = 0; } if (mbox_sync_update_index_header(sync_ctx) < 0) return -1; return ret; } int mbox_sync_header_refresh(struct mbox_mailbox *mbox) { const void *data; size_t data_size; if (mail_index_refresh(mbox->box.index) < 0) { mailbox_set_index_error(&mbox->box); return -1; } mail_index_get_header_ext(mbox->box.view, mbox->mbox_ext_idx, &data, &data_size); if (data_size == 0) { /* doesn't exist yet. */ i_zero(&mbox->mbox_hdr); return 0; } memcpy(&mbox->mbox_hdr, data, I_MIN(sizeof(mbox->mbox_hdr), data_size)); if (mbox->mbox_broken_offsets) mbox->mbox_hdr.dirty_flag = TRUE; return 0; } int mbox_sync_get_guid(struct mbox_mailbox *mbox) { struct mail_index_transaction *trans; unsigned int lock_id; int ret; if (mbox_lock(mbox, F_WRLCK, &lock_id) <= 0) return -1; ret = mbox_sync_header_refresh(mbox); if (ret == 0) { trans = mail_index_transaction_begin(mbox->box.view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mbox_sync_index_update_ext_header(mbox, trans); ret = mail_index_transaction_commit(&trans); } mbox_unlock(mbox, lock_id); return ret; } int mbox_sync_has_changed(struct mbox_mailbox *mbox, bool leave_dirty) { const struct stat *st; struct stat statbuf; if (mbox->mbox_file_stream != NULL && mbox->mbox_fd == -1) { /* read-only stream */ if (i_stream_stat(mbox->mbox_file_stream, FALSE, &st) < 0) { if (errno == ENOENT) { mailbox_set_deleted(&mbox->box); return 0; } mbox_set_syscall_error(mbox, "i_stream_stat()"); return -1; } } else { if (stat(mailbox_get_path(&mbox->box), &statbuf) < 0) { if (errno == ENOENT) { mailbox_set_deleted(&mbox->box); return 0; } mbox_set_syscall_error(mbox, "stat()"); return -1; } st = &statbuf; } if (mbox_sync_header_refresh(mbox) < 0) return -1; if (guid_128_is_empty(mbox->mbox_hdr.mailbox_guid)) { /* need to assign mailbox GUID */ return 1; } if ((uint32_t)st->st_mtime == mbox->mbox_hdr.sync_mtime && (uint64_t)st->st_size == mbox->mbox_hdr.sync_size) { /* fully synced */ if (!mbox->mbox_hdr.dirty_flag || leave_dirty) return 0; /* flushing dirtyness */ } /* file changed */ return 1; } static void mbox_sync_context_free(struct mbox_sync_context *sync_ctx) { index_sync_changes_deinit(&sync_ctx->sync_changes); index_storage_expunging_deinit(&sync_ctx->mbox->box); if (sync_ctx->index_sync_ctx != NULL) mail_index_sync_rollback(&sync_ctx->index_sync_ctx); pool_unref(&sync_ctx->mail_keyword_pool); pool_unref(&sync_ctx->saved_keywords_pool); str_free(&sync_ctx->header); str_free(&sync_ctx->from_line); array_free(&sync_ctx->mails); } static int mbox_sync_int(struct mbox_mailbox *mbox, enum mbox_sync_flags flags, unsigned int *lock_id) { struct mail_index_sync_ctx *index_sync_ctx; struct mail_index_view *sync_view; struct mail_index_transaction *trans; struct mbox_sync_context sync_ctx; enum mail_index_sync_flags sync_flags; int ret, changed; bool delay_writes, readonly; readonly = mbox_is_backend_readonly(mbox) || (flags & MBOX_SYNC_READONLY) != 0; delay_writes = readonly || ((flags & MBOX_SYNC_REWRITE) == 0 && mbox->storage->set->mbox_lazy_writes); if (!mbox->storage->set->mbox_dirty_syncs && !mbox->storage->set->mbox_very_dirty_syncs) flags |= MBOX_SYNC_UNDIRTY; if ((flags & MBOX_SYNC_LOCK_READING) != 0) { if (mbox_lock(mbox, F_RDLCK, lock_id) <= 0) return -1; } if ((flags & MBOX_SYNC_HEADER) != 0 || (flags & MBOX_SYNC_FORCE_SYNC) != 0) { if (mbox_sync_header_refresh(mbox) < 0) return -1; changed = 1; } else { bool leave_dirty = (flags & MBOX_SYNC_UNDIRTY) == 0; if ((changed = mbox_sync_has_changed(mbox, leave_dirty)) < 0) return -1; } if ((flags & MBOX_SYNC_LOCK_READING) != 0) { /* we just want to lock it for reading. if mbox hasn't been modified don't do any syncing. */ if (!changed) return 0; /* have to sync to make sure offsets have stayed the same */ mbox_unlock(mbox, *lock_id); *lock_id = 0; } /* flush input streams' buffers */ if (mbox->mbox_stream != NULL) i_stream_sync(mbox->mbox_stream); if (mbox->mbox_file_stream != NULL) i_stream_sync(mbox->mbox_file_stream); again: if (changed) { /* we're most likely modifying the mbox while syncing, just lock it for writing immediately. the mbox must be locked before index syncing is started to avoid deadlocks, so we don't have much choice either (well, easy ones anyway). */ int lock_type = readonly ? F_RDLCK : F_WRLCK; if ((ret = mbox_lock(mbox, lock_type, lock_id)) <= 0) { if (ret == 0 || lock_type == F_RDLCK) return -1; /* try as read-only */ if (mbox_lock(mbox, F_RDLCK, lock_id) <= 0) return -1; mbox->backend_readonly = readonly = TRUE; mbox->backend_readonly_set = TRUE; delay_writes = TRUE; } } sync_flags = index_storage_get_sync_flags(&mbox->box); if ((flags & MBOX_SYNC_REWRITE) != 0) sync_flags |= MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY; ret = index_storage_expunged_sync_begin(&mbox->box, &index_sync_ctx, &sync_view, &trans, sync_flags); if (ret <= 0) return ret; if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) { /* see if we need to drop recent flags */ sync_ctx.hdr = mail_index_get_header(sync_view); if (sync_ctx.hdr->first_recent_uid < sync_ctx.hdr->next_uid) changed = 1; } if (!changed && !mail_index_sync_have_more(index_sync_ctx)) { /* nothing to do */ nothing_to_do: /* index may need to do internal syncing though, so commit instead of rollbacking. */ index_storage_expunging_deinit(&mbox->box); if (mail_index_sync_commit(&index_sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); return -1; } return 0; } i_zero(&sync_ctx); sync_ctx.mbox = mbox; sync_ctx.keep_recent = (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0; sync_ctx.hdr = mail_index_get_header(sync_view); sync_ctx.from_line = str_new(default_pool, 256); sync_ctx.header = str_new(default_pool, 4096); sync_ctx.index_sync_ctx = index_sync_ctx; sync_ctx.sync_view = sync_view; sync_ctx.t = trans; sync_ctx.mail_keyword_pool = pool_alloconly_create("mbox keywords", 512); sync_ctx.saved_keywords_pool = pool_alloconly_create("mbox saved keywords", 4096); /* make sure we've read the latest keywords in index */ (void)mail_index_get_keywords(mbox->box.index); i_array_init(&sync_ctx.mails, 64); sync_ctx.flags = flags; sync_ctx.readonly = readonly; sync_ctx.delay_writes = delay_writes; sync_ctx.sync_changes = index_sync_changes_init(index_sync_ctx, sync_view, trans, sync_ctx.delay_writes); if (!changed && delay_writes) { /* if we have only flag changes, we don't need to open the mbox file */ bool expunged; uint32_t uid; mbox_sync_read_index_syncs(&sync_ctx, 1, &expunged); uid = expunged ? 1 : index_sync_changes_get_next_uid(sync_ctx.sync_changes); if (uid == 0) { sync_ctx.index_sync_ctx = NULL; mbox_sync_context_free(&sync_ctx); goto nothing_to_do; } } if (*lock_id == 0) { /* ok, we have something to do but no locks. we'll have to restart syncing to avoid deadlocking. */ mbox_sync_context_free(&sync_ctx); changed = 1; goto again; } if (mbox_file_open_stream(mbox) < 0) { mbox_sync_context_free(&sync_ctx); return -1; } sync_ctx.file_input = sync_ctx.mbox->mbox_file_stream; sync_ctx.input = sync_ctx.mbox->mbox_stream; sync_ctx.write_fd = sync_ctx.mbox->mbox_lock_type != F_WRLCK ? -1 : sync_ctx.mbox->mbox_fd; ret = mbox_sync_do(&sync_ctx, flags); if (ret < 0) mail_index_sync_rollback(&index_sync_ctx); else if (mail_index_sync_commit(&index_sync_ctx) < 0) { mailbox_set_index_error(&mbox->box); ret = -1; } sync_ctx.t = NULL; sync_ctx.index_sync_ctx = NULL; if (ret == 0 && mbox->mbox_fd != -1 && sync_ctx.keep_recent && !readonly) { /* try to set atime back to its original value. (it'll fail with EPERM for shared mailboxes where we aren't the file's owner) */ struct utimbuf buf; struct stat st; if (fstat(mbox->mbox_fd, &st) < 0) mbox_set_syscall_error(mbox, "fstat()"); else { buf.modtime = st.st_mtime; buf.actime = sync_ctx.orig_atime; if (utime(mailbox_get_path(&mbox->box), &buf) < 0 && errno != EPERM) mbox_set_syscall_error(mbox, "utime()"); } } i_assert(*lock_id != 0); if (mbox->storage->storage.set->mail_nfs_storage && mbox->mbox_fd != -1) { if (fdatasync(mbox->mbox_fd) < 0) { mbox_set_syscall_error(mbox, "fdatasync()"); ret = -1; } } mbox_sync_context_free(&sync_ctx); return ret; } int mbox_sync(struct mbox_mailbox *mbox, enum mbox_sync_flags flags) { unsigned int lock_id = 0; int ret; i_assert(mbox->mbox_lock_type != F_RDLCK || (flags & MBOX_SYNC_READONLY) != 0); mbox->syncing = TRUE; ret = mbox_sync_int(mbox, flags, &lock_id); mbox->syncing = FALSE; if (lock_id != 0) { if (ret < 0) { /* syncing failed, don't leave it locked */ mbox_unlock(mbox, lock_id); } else if ((flags & MBOX_SYNC_LOCK_READING) == 0) { if (mbox_unlock(mbox, lock_id) < 0) ret = -1; } else if (mbox->mbox_lock_type != F_RDLCK) { /* drop to read lock */ unsigned int read_lock_id = 0; if (mbox_lock(mbox, F_RDLCK, &read_lock_id) <= 0) ret = -1; if (mbox_unlock(mbox, lock_id) < 0) ret = -1; } } if (mbox->box.v.sync_notify != NULL) mbox->box.v.sync_notify(&mbox->box, 0, 0); return ret; } struct mailbox_sync_context * mbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; enum mbox_sync_flags mbox_sync_flags = 0; int ret = 0; if (index_mailbox_want_full_sync(&mbox->box, flags)) { if ((flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0 && !mbox->storage->set->mbox_very_dirty_syncs) mbox_sync_flags |= MBOX_SYNC_UNDIRTY; if ((flags & MAILBOX_SYNC_FLAG_FULL_WRITE) != 0) mbox_sync_flags |= MBOX_SYNC_REWRITE; if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0) { mbox_sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_REWRITE | MBOX_SYNC_FORCE_SYNC; } ret = mbox_sync(mbox, mbox_sync_flags); } return index_mailbox_sync_init(box, flags, ret < 0); } dovecot-2.2.33.2/src/lib-storage/index/mbox/istream-raw-mbox.h0000644000175000017500000000513513123174404020752 00000000000000#ifndef ISTREAM_RAW_MBOX_H #define ISTREAM_RAW_MBOX_H /* Create a mbox stream for parsing mbox. Reading stops before From-line, you'll have to call istream_raw_mbox_next() to get to next message. path is used only for logging purposes. */ struct istream *i_stream_create_raw_mbox(struct istream *input); /* Return offset to beginning of the "\nFrom"-line. */ uoff_t istream_raw_mbox_get_start_offset(struct istream *stream); /* Return offset to beginning of the headers. */ int istream_raw_mbox_get_header_offset(struct istream *stream, uoff_t *hdr_offset_r); /* Return offset to beginning of the body. */ int istream_raw_mbox_get_body_offset(struct istream *stream, uoff_t *body_offset_r); /* Return the number of bytes in the body of this message. If expected_body_size isn't (uoff_t)-1, we'll use it as potentially valid body size to avoid actually reading through the whole message. */ int istream_raw_mbox_get_body_size(struct istream *stream, uoff_t expected_body_size, uoff_t *body_size_r); /* Return received time of current message, or (time_t)-1 if the timestamp is broken. */ time_t istream_raw_mbox_get_received_time(struct istream *stream); /* Return sender of current message. */ const char *istream_raw_mbox_get_sender(struct istream *stream); /* Return TRUE if the empty line between this and the next mail contains CR. */ bool istream_raw_mbox_has_crlf_ending(struct istream *stream); /* Jump to next message. If expected_body_size isn't (uoff_t)-1, we'll use it as potentially valid body size. */ int istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size); /* Seek to message at given offset. offset must point to beginning of "\nFrom ", or 0 for beginning of file. Returns -1 if it offset doesn't contain a valid From-line. */ int istream_raw_mbox_seek(struct istream *stream, uoff_t offset); /* Set next message's start offset. If this isn't set, read stops at the next valid From_-line, even if it belongs to the current message's body (Content-Length: header can be used to determine that). */ void istream_raw_mbox_set_next_offset(struct istream *stream, uoff_t offset); /* Returns TRUE if we've read the whole mbox. */ bool istream_raw_mbox_is_eof(struct istream *stream); /* Returns TRUE if we've noticed corruption in used offsets/sizes. */ bool istream_raw_mbox_is_corrupted(struct istream *stream); /* Change stream's locking state. We'll assert-crash if stream is tried to be read while it's unlocked. */ void istream_raw_mbox_set_locked(struct istream *stream); void istream_raw_mbox_set_unlocked(struct istream *stream); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-md5.h0000644000175000017500000000064113123174404017201 00000000000000#ifndef MBOX_MD5_H #define MBOX_MD5_H struct message_header_line; struct mbox_md5_vfuncs { struct mbox_md5_context *(*init)(void); void (*more)(struct mbox_md5_context *ctx, struct message_header_line *hdr); void (*finish)(struct mbox_md5_context *ctx, unsigned char result[STATIC_ARRAY 16]); }; extern struct mbox_md5_vfuncs mbox_md5_apop3d; extern struct mbox_md5_vfuncs mbox_md5_all; #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-md5-all.c0000644000175000017500000000150713123174404017744 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "md5.h" #include "message-parser.h" #include "mbox-md5.h" struct mbox_md5_context { struct md5_context hdr_md5_ctx; }; static struct mbox_md5_context *mbox_md5_all_init(void) { struct mbox_md5_context *ctx; ctx = i_new(struct mbox_md5_context, 1); md5_init(&ctx->hdr_md5_ctx); return ctx; } static void mbox_md5_all_more(struct mbox_md5_context *ctx, struct message_header_line *hdr) { md5_update(&ctx->hdr_md5_ctx, hdr->value, hdr->value_len); } static void mbox_md5_all_finish(struct mbox_md5_context *ctx, unsigned char result[STATIC_ARRAY 16]) { md5_final(&ctx->hdr_md5_ctx, result); i_free(ctx); } struct mbox_md5_vfuncs mbox_md5_all = { mbox_md5_all_init, mbox_md5_all_more, mbox_md5_all_finish }; dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-lock.h0000644000175000017500000000074413123174404017450 00000000000000#ifndef MBOX_LOCK_H #define MBOX_LOCK_H /* NOTE: if mbox file is not open, it's opened. if it is open but file has been overwritten (ie. inode has changed), it's reopened. */ int mbox_lock(struct mbox_mailbox *mbox, int lock_type, unsigned int *lock_id_r); int ATTR_NOWARN_UNUSED_RESULT mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id); unsigned int mbox_get_cur_lock_id(struct mbox_mailbox *mbox); void mbox_dotlock_touch(struct mbox_mailbox *mbox); #endif dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-lock.c0000644000175000017500000005627313165463624017466 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "eacces-error.h" #include "restrict-access.h" #include "nfs-workarounds.h" #include "ipwd.h" #include "mail-index-private.h" #include "mbox-storage.h" #include "istream-raw-mbox.h" #include "mbox-file.h" #include "mbox-lock.h" #include #include #include #include #ifdef HAVE_FLOCK # include #endif /* 0.1 .. 0.2msec */ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) enum mbox_lock_type { MBOX_LOCK_DOTLOCK, MBOX_LOCK_DOTLOCK_TRY, MBOX_LOCK_FCNTL, MBOX_LOCK_FLOCK, MBOX_LOCK_LOCKF, MBOX_LOCK_COUNT }; enum mbox_dotlock_op { MBOX_DOTLOCK_OP_LOCK, MBOX_DOTLOCK_OP_UNLOCK, MBOX_DOTLOCK_OP_TOUCH }; struct mbox_lock_context { struct mbox_mailbox *mbox; bool locked_status[MBOX_LOCK_COUNT]; bool checked_file; int lock_type; bool dotlock_last_stale; bool fcntl_locked; bool using_privileges; }; struct mbox_lock_data { enum mbox_lock_type type; const char *name; int (*func)(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); }; static int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); static int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); static int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); #ifdef HAVE_FLOCK static int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); #else # define mbox_lock_flock NULL #endif #ifdef HAVE_LOCKF static int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time); #else # define mbox_lock_lockf NULL #endif static struct mbox_lock_data lock_data[] = { { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock }, { MBOX_LOCK_DOTLOCK_TRY, "dotlock_try", mbox_lock_dotlock_try }, { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl }, { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock }, { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf }, { 0, NULL, NULL } }; static int ATTR_NOWARN_UNUSED_RESULT mbox_lock_list(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time, int idx); static int ATTR_NOWARN_UNUSED_RESULT mbox_unlock_files(struct mbox_lock_context *ctx); static void mbox_read_lock_methods(const char *str, const char *env, enum mbox_lock_type *locks) { enum mbox_lock_type type; const char *const *lock; int i, dest; for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) { for (type = 0; lock_data[type].name != NULL; type++) { if (strcasecmp(*lock, lock_data[type].name) == 0) { type = lock_data[type].type; break; } } if (lock_data[type].name == NULL) i_fatal("%s: Invalid value %s", env, *lock); if (lock_data[type].func == NULL) { i_fatal("%s: Support for lock type %s " "not compiled into binary", env, *lock); } for (i = 0; i < dest; i++) { if (locks[i] == type) i_fatal("%s: Duplicated value %s", env, *lock); } /* @UNSAFE */ locks[dest++] = type; } locks[dest] = (enum mbox_lock_type)-1; } static void mbox_init_lock_settings(struct mbox_storage *storage) { enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1]; enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1]; int r, w; mbox_read_lock_methods(storage->set->mbox_read_locks, "mbox_read_locks", read_locks); mbox_read_lock_methods(storage->set->mbox_write_locks, "mbox_write_locks", write_locks); /* check that read/write list orders match. write_locks must contain at least read_locks and possibly more. */ for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) { if (read_locks[r] == (enum mbox_lock_type)-1) break; if (read_locks[r] == write_locks[w]) r++; } if (read_locks[r] != (enum mbox_lock_type)-1) { i_fatal("mbox read/write lock list settings are invalid. " "Lock ordering must be the same with both, " "and write locks must contain all read locks " "(and possibly more)"); } storage->read_locks = p_new(storage->storage.pool, enum mbox_lock_type, MBOX_LOCK_COUNT+1); memcpy(storage->read_locks, read_locks, sizeof(*storage->read_locks) * (MBOX_LOCK_COUNT+1)); storage->write_locks = p_new(storage->storage.pool, enum mbox_lock_type, MBOX_LOCK_COUNT+1); memcpy(storage->write_locks, write_locks, sizeof(*storage->write_locks) * (MBOX_LOCK_COUNT+1)); storage->lock_settings_initialized = TRUE; } static int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type) { struct mbox_mailbox *mbox = ctx->mbox; struct stat st; if (ctx->checked_file || lock_type == F_UNLCK) return 0; if (mbox->mbox_fd != -1) { /* we could flush NFS file handle cache here if we wanted to be sure that the file is latest, but mbox files get rarely deleted and the flushing might cause errors (e.g. EBUSY for trying to flush a /var/mail mountpoint) */ if (nfs_safe_stat(mailbox_get_path(&mbox->box), &st) < 0) { if (errno == ENOENT) mailbox_set_deleted(&mbox->box); else mbox_set_syscall_error(mbox, "stat()"); return -1; } if (st.st_ino != mbox->mbox_ino || !CMP_DEV_T(st.st_dev, mbox->mbox_dev)) mbox_file_close(mbox); } if (mbox->mbox_fd == -1) { if (mbox_file_open(mbox) < 0) return -1; } ctx->checked_file = TRUE; return 0; } static bool dotlock_callback(unsigned int secs_left, bool stale, void *context) { struct mbox_lock_context *ctx = context; enum mbox_lock_type *lock_types; int i; if (ctx->using_privileges) restrict_access_drop_priv_gid(); if (stale && !ctx->dotlock_last_stale) { /* get next index we wish to try locking. it's the one after dotlocking. */ lock_types = ctx->lock_type == F_WRLCK || (ctx->lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ? ctx->mbox->storage->write_locks : ctx->mbox->storage->read_locks; for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) { if (lock_types[i] == MBOX_LOCK_DOTLOCK) break; } if (lock_types[i] != (enum mbox_lock_type)-1 && lock_types[i+1] != (enum mbox_lock_type)-1) { i++; if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) { /* we couldn't get fd lock - it's really locked */ ctx->dotlock_last_stale = TRUE; return FALSE; } mbox_lock_list(ctx, F_UNLCK, 0, i); } } ctx->dotlock_last_stale = stale; index_storage_lock_notify(&ctx->mbox->box, stale ? MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE : MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, secs_left); if (ctx->using_privileges) { if (restrict_access_use_priv_gid() < 0) { /* shouldn't get here */ return FALSE; } } return TRUE; } static int ATTR_NULL(2) ATTR_NOWARN_UNUSED_RESULT mbox_dotlock_privileged_op(struct mbox_mailbox *mbox, struct dotlock_settings *set, enum mbox_dotlock_op op) { const char *box_path, *dir, *fname; int ret = -1, orig_dir_fd, orig_errno; orig_dir_fd = open(".", O_RDONLY); if (orig_dir_fd == -1) { mail_storage_set_critical(&mbox->storage->storage, "open(.) failed: %m"); return -1; } /* allow dotlocks to be created only for files we can read while we're unprivileged. to make sure there are no race conditions we first have to chdir to the mbox file's directory and then use relative paths. unless this is done, users could: - create *.lock files to any directory writable by the privileged group - DoS other users by dotlocking their mailboxes infinitely */ box_path = mailbox_get_path(&mbox->box); fname = strrchr(box_path, '/'); if (fname == NULL) { /* already relative */ fname = box_path; } else { dir = t_strdup_until(box_path, fname); if (chdir(dir) < 0) { mail_storage_set_critical(&mbox->storage->storage, "chdir(%s) failed: %m", dir); i_close_fd(&orig_dir_fd); return -1; } fname++; } if (op == MBOX_DOTLOCK_OP_LOCK) { if (access(fname, R_OK) < 0) { mail_storage_set_critical(&mbox->storage->storage, "access(%s) failed: %m", box_path); i_close_fd(&orig_dir_fd); return -1; } } if (restrict_access_use_priv_gid() < 0) { i_close_fd(&orig_dir_fd); return -1; } switch (op) { case MBOX_DOTLOCK_OP_LOCK: /* we're now privileged - avoid doing as much as possible */ ret = file_dotlock_create(set, fname, 0, &mbox->mbox_dotlock); if (ret > 0) mbox->mbox_used_privileges = TRUE; else if (ret < 0 && errno == EACCES) { const char *errmsg = eacces_error_get_creating("file_dotlock_create", fname); mail_storage_set_critical(&mbox->storage->storage, "%s", errmsg); } else { mbox_set_syscall_error(mbox, "file_dotlock_create()"); } break; case MBOX_DOTLOCK_OP_UNLOCK: /* we're now privileged - avoid doing as much as possible */ ret = file_dotlock_delete(&mbox->mbox_dotlock); if (ret < 0) mbox_set_syscall_error(mbox, "file_dotlock_delete()"); mbox->mbox_used_privileges = FALSE; break; case MBOX_DOTLOCK_OP_TOUCH: ret = file_dotlock_touch(mbox->mbox_dotlock); if (ret < 0) mbox_set_syscall_error(mbox, "file_dotlock_touch()"); break; } orig_errno = errno; restrict_access_drop_priv_gid(); if (fchdir(orig_dir_fd) < 0) { mail_storage_set_critical(&mbox->storage->storage, "fchdir() failed: %m"); } i_close_fd(&orig_dir_fd); errno = orig_errno; return ret; } static void mbox_dotlock_log_eacces_error(struct mbox_mailbox *mbox, const char *path) { const char *dir, *errmsg, *name; struct stat st; struct group group; int orig_errno = errno; errmsg = eacces_error_get_creating("file_dotlock_create", path); dir = strrchr(path, '/'); dir = dir == NULL ? "." : t_strdup_until(path, dir); /* allow privileged locking for a) user's own INBOX, b) another user's shared INBOX, and c) anything called INBOX (in inbox=no namespace) */ if (!mbox->box.inbox_any && strcmp(mbox->box.name, "INBOX") != 0) { mail_storage_set_critical(&mbox->storage->storage, "%s (not INBOX -> no privileged locking)", errmsg); } else if (!mbox->mbox_privileged_locking) { dir = mailbox_list_get_root_forced(mbox->box.list, MAILBOX_LIST_PATH_TYPE_DIR); mail_storage_set_critical(&mbox->storage->storage, "%s (under root dir %s -> no privileged locking)", errmsg, dir); } else if (stat(dir, &st) == 0 && (st.st_mode & 02) == 0 && /* not world-writable */ (st.st_mode & 020) != 0) { /* group-writable */ if (i_getgrgid(st.st_gid, &group) <= 0) name = dec2str(st.st_gid); else name = group.gr_name; mail_storage_set_critical(&mbox->storage->storage, "%s (set mail_privileged_group=%s)", errmsg, name); } else { mail_storage_set_critical(&mbox->storage->storage, "%s (nonstandard permissions in %s)", errmsg, dir); } errno = orig_errno; } static int mbox_lock_dotlock_int(struct mbox_lock_context *ctx, int lock_type, bool try) { struct mbox_mailbox *mbox = ctx->mbox; struct dotlock_settings set; int ret; if (lock_type == F_UNLCK) { if (!mbox->mbox_dotlocked) return 1; if (!mbox->mbox_used_privileges) { if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) { mbox_set_syscall_error(mbox, "file_dotlock_delete()"); } } else { ctx->using_privileges = TRUE; mbox_dotlock_privileged_op(mbox, NULL, MBOX_DOTLOCK_OP_UNLOCK); ctx->using_privileges = FALSE; } mbox->mbox_dotlocked = FALSE; return 1; } if (mbox->mbox_dotlocked) return 1; ctx->dotlock_last_stale = TRUE; i_zero(&set); set.use_excl_lock = mbox->storage->storage.set->dotlock_use_excl; set.nfs_flush = mbox->storage->storage.set->mail_nfs_storage; set.timeout = mail_storage_get_lock_timeout(&mbox->storage->storage, mbox->storage->set->mbox_lock_timeout); set.stale_timeout = mbox->storage->set->mbox_dotlock_change_timeout; set.callback = dotlock_callback; set.context = ctx; ret = file_dotlock_create(&set, mailbox_get_path(&mbox->box), 0, &mbox->mbox_dotlock); if (ret >= 0) { /* success / timeout */ } else if (errno == EACCES && restrict_access_have_priv_gid() && mbox->mbox_privileged_locking) { /* try again, this time with extra privileges */ ret = mbox_dotlock_privileged_op(mbox, &set, MBOX_DOTLOCK_OP_LOCK); } else if (errno == EACCES) mbox_dotlock_log_eacces_error(mbox, mailbox_get_path(&mbox->box)); else mbox_set_syscall_error(mbox, "file_dotlock_create()"); if (ret < 0) { if ((ENOSPACE(errno) || errno == EACCES) && try) return 1; return -1; } if (ret == 0) { mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT); return 0; } mbox->mbox_dotlocked = TRUE; if (mbox_file_open_latest(ctx, lock_type) < 0) return -1; return 1; } static int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time ATTR_UNUSED) { return mbox_lock_dotlock_int(ctx, lock_type, FALSE); } static int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time ATTR_UNUSED) { return mbox_lock_dotlock_int(ctx, lock_type, TRUE); } #ifdef HAVE_FLOCK static int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time) { time_t now; unsigned int next_alarm; if (mbox_file_open_latest(ctx, lock_type) < 0) return -1; if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1) return 1; if (lock_type == F_WRLCK) lock_type = LOCK_EX; else if (lock_type == F_RDLCK) lock_type = LOCK_SH; else lock_type = LOCK_UN; if (max_wait_time == 0) { /* usually we're waiting here, but if we came from mbox_lock_dotlock(), we just want to try locking */ lock_type |= LOCK_NB; } else { now = time(NULL); if (now >= max_wait_time) alarm(1); else alarm(I_MIN(max_wait_time - now, 5)); } while (flock(ctx->mbox->mbox_fd, lock_type) < 0) { if (errno != EINTR) { if (errno == EWOULDBLOCK && max_wait_time == 0) { /* non-blocking lock trying failed */ return 0; } alarm(0); mbox_set_syscall_error(ctx->mbox, "flock()"); return -1; } now = time(NULL); if (now >= max_wait_time) { alarm(0); return 0; } /* notify locks once every 5 seconds. try to use rounded values. */ next_alarm = (max_wait_time - now) % 5; if (next_alarm == 0) next_alarm = 5; alarm(next_alarm); index_storage_lock_notify(&ctx->mbox->box, MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, max_wait_time - now); } alarm(0); return 1; } #endif #ifdef HAVE_LOCKF static int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time) { time_t now; unsigned int next_alarm; if (mbox_file_open_latest(ctx, lock_type) < 0) return -1; if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1) return 1; if (lock_type == F_UNLCK) lock_type = F_ULOCK; else if (max_wait_time == 0) { /* usually we're waiting here, but if we came from mbox_lock_dotlock(), we just want to try locking */ lock_type = F_TLOCK; } else { now = time(NULL); if (now >= max_wait_time) alarm(1); else alarm(I_MIN(max_wait_time - now, 5)); lock_type = F_LOCK; } while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) { if (errno != EINTR) { if ((errno == EACCES || errno == EAGAIN) && max_wait_time == 0) { /* non-blocking lock trying failed */ return 0; } alarm(0); mbox_set_syscall_error(ctx->mbox, "lockf()"); return -1; } now = time(NULL); if (now >= max_wait_time) { alarm(0); return 0; } /* notify locks once every 5 seconds. try to use rounded values. */ next_alarm = (max_wait_time - now) % 5; if (next_alarm == 0) next_alarm = 5; alarm(next_alarm); index_storage_lock_notify(&ctx->mbox->box, MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, max_wait_time - now); } alarm(0); return 1; } #endif static int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time) { struct flock fl; time_t now; unsigned int next_alarm; int wait_type; if (mbox_file_open_latest(ctx, lock_type) < 0) return -1; if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1) return 1; i_zero(&fl); fl.l_type = lock_type; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; if (max_wait_time == 0) { /* usually we're waiting here, but if we came from mbox_lock_dotlock(), we just want to try locking */ wait_type = F_SETLK; } else { wait_type = F_SETLKW; now = time(NULL); if (now >= max_wait_time) alarm(1); else alarm(I_MIN(max_wait_time - now, 5)); } while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) { if (errno != EINTR) { if ((errno == EACCES || errno == EAGAIN) && wait_type == F_SETLK) { /* non-blocking lock trying failed */ return 0; } alarm(0); if (errno != EACCES) { mbox_set_syscall_error(ctx->mbox, "fcntl()"); return -1; } mail_storage_set_critical(&ctx->mbox->storage->storage, "fcntl() failed with mbox file %s: " "File is locked by another process (EACCES)", mailbox_get_path(&ctx->mbox->box)); return -1; } now = time(NULL); if (now >= max_wait_time) { alarm(0); return 0; } /* notify locks once every 5 seconds. try to use rounded values. */ next_alarm = (max_wait_time - now) % 5; if (next_alarm == 0) next_alarm = 5; alarm(next_alarm); index_storage_lock_notify(&ctx->mbox->box, MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, max_wait_time - now); } alarm(0); ctx->fcntl_locked = TRUE; return 1; } static int ATTR_NOWARN_UNUSED_RESULT mbox_lock_list(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time, int idx) { enum mbox_lock_type *lock_types; enum mbox_lock_type type; int i, ret = 0; bool locked_status; ctx->lock_type = lock_type; lock_types = lock_type == F_WRLCK || (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ? ctx->mbox->storage->write_locks : ctx->mbox->storage->read_locks; for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) { type = lock_types[i]; locked_status = lock_type != F_UNLCK; if (ctx->locked_status[type] == locked_status) continue; ctx->locked_status[type] = locked_status; ret = lock_data[type].func(ctx, lock_type, max_wait_time); if (ret <= 0) break; } return ret; } static int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type, bool *fcntl_locked_r) { struct mbox_lock_context ctx; time_t max_wait_time; int ret, i; bool drop_locks; *fcntl_locked_r = FALSE; index_storage_lock_notify_reset(&mbox->box); if (!mbox->storage->lock_settings_initialized) mbox_init_lock_settings(mbox->storage); if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) { /* read-only mbox stream. no need to lock. */ i_assert(mbox_is_backend_readonly(mbox)); mbox->mbox_lock_type = lock_type; return 1; } max_wait_time = time(NULL) + mail_storage_get_lock_timeout(&mbox->storage->storage, mbox->storage->set->mbox_lock_timeout); i_zero(&ctx); ctx.mbox = mbox; if (mbox->mbox_lock_type == F_WRLCK) { /* dropping to shared lock. first drop those that we don't remove completely. */ const enum mbox_lock_type *read_locks = mbox->storage->read_locks; for (i = 0; i < MBOX_LOCK_COUNT; i++) ctx.locked_status[i] = TRUE; for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++) ctx.locked_status[read_locks[i]] = FALSE; drop_locks = TRUE; } else { drop_locks = FALSE; } mbox->mbox_lock_type = lock_type; ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0); if (ret <= 0) { if (!drop_locks) mbox_unlock_files(&ctx); if (ret == 0) { mail_storage_set_error(&mbox->storage->storage, MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT); } return ret; } if (drop_locks) { /* dropping to shared lock: drop the locks that are only in write list */ const enum mbox_lock_type *read_locks = mbox->storage->read_locks; const enum mbox_lock_type *write_locks = mbox->storage->write_locks; memset(ctx.locked_status, 0, sizeof(ctx.locked_status)); for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++) ctx.locked_status[write_locks[i]] = TRUE; for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++) ctx.locked_status[read_locks[i]] = FALSE; mbox->mbox_lock_type = F_WRLCK; mbox_lock_list(&ctx, F_UNLCK, 0, 0); mbox->mbox_lock_type = F_RDLCK; } *fcntl_locked_r = ctx.fcntl_locked; return 1; } int mbox_lock(struct mbox_mailbox *mbox, int lock_type, unsigned int *lock_id_r) { const char *path = mailbox_get_path(&mbox->box); int mbox_fd = mbox->mbox_fd; bool fcntl_locked; int ret; if (lock_type == F_RDLCK && mbox->external_transactions > 0 && mbox->mbox_lock_type != F_RDLCK) { /* we have a transaction open that is going to save mails and apparently also wants to read from the same mailbox (copy, move, catenate). we need to write lock the mailbox, since we can't later upgrade a read lock to write lock. */ lock_type = F_WRLCK; } /* allow only unlock -> shared/exclusive or exclusive -> shared */ i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK); i_assert(lock_type == F_RDLCK || mbox->mbox_lock_type != F_RDLCK); if (mbox->mbox_lock_type == F_UNLCK) { ret = mbox_update_locking(mbox, lock_type, &fcntl_locked); if (ret <= 0) return ret; if (mbox->storage->storage.set->mail_nfs_storage) { if (fcntl_locked) { nfs_flush_attr_cache_fd_locked(path, mbox_fd); nfs_flush_read_cache_locked(path, mbox_fd); } else { nfs_flush_attr_cache_unlocked(path); nfs_flush_read_cache_unlocked(path, mbox_fd); } } mbox->mbox_lock_id += 2; } if (lock_type == F_RDLCK) { mbox->mbox_shared_locks++; *lock_id_r = mbox->mbox_lock_id; } else { mbox->mbox_excl_locks++; *lock_id_r = mbox->mbox_lock_id + 1; } if (mbox->mbox_stream != NULL) istream_raw_mbox_set_locked(mbox->mbox_stream); return 1; } static int mbox_unlock_files(struct mbox_lock_context *ctx) { int ret = 0; if (mbox_lock_list(ctx, F_UNLCK, 0, 0) < 0) ret = -1; ctx->mbox->mbox_lock_id += 2; ctx->mbox->mbox_lock_type = F_UNLCK; return ret; } int mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id) { struct mbox_lock_context ctx; bool fcntl_locked; int i; i_assert(mbox->mbox_lock_id == (lock_id & ~1)); if (lock_id & 1) { /* dropping exclusive lock */ i_assert(mbox->mbox_excl_locks > 0); if (--mbox->mbox_excl_locks > 0) return 0; if (mbox->mbox_shared_locks > 0) { /* drop to shared lock */ if (mbox_update_locking(mbox, F_RDLCK, &fcntl_locked) < 0) return -1; return 0; } } else { /* dropping shared lock */ i_assert(mbox->mbox_shared_locks > 0); if (--mbox->mbox_shared_locks > 0) return 0; if (mbox->mbox_excl_locks > 0) return 0; } /* all locks gone */ /* make sure we don't read the stream while unlocked */ if (mbox->mbox_stream != NULL) istream_raw_mbox_set_unlocked(mbox->mbox_stream); i_zero(&ctx); ctx.mbox = mbox; for (i = 0; i < MBOX_LOCK_COUNT; i++) ctx.locked_status[i] = TRUE; return mbox_unlock_files(&ctx); } unsigned int mbox_get_cur_lock_id(struct mbox_mailbox *mbox) { return mbox->mbox_lock_id + (mbox->mbox_excl_locks > 0 ? 1 : 0); } void mbox_dotlock_touch(struct mbox_mailbox *mbox) { if (mbox->mbox_dotlock == NULL) return; if (!mbox->mbox_used_privileges) (void)file_dotlock_touch(mbox->mbox_dotlock); else { mbox_dotlock_privileged_op(mbox, NULL, MBOX_DOTLOCK_OP_TOUCH); } } dovecot-2.2.33.2/src/lib-storage/index/mbox/mbox-sync-rewrite.c0000644000175000017500000004370113165463624021161 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "write-full.h" #include "message-parser.h" #include "mbox-storage.h" #include "mbox-sync-private.h" #include "istream-raw-mbox.h" int mbox_move(struct mbox_sync_context *sync_ctx, uoff_t dest, uoff_t source, uoff_t size) { struct istream *input; struct ostream *output; off_t ret; i_assert(source > 0 || (dest != 1 && dest != 2)); i_assert(size < OFF_T_MAX); if (size == 0 || source == dest) return 0; i_stream_sync(sync_ctx->input); output = o_stream_create_fd_file(sync_ctx->write_fd, (uoff_t)-1, FALSE); i_stream_seek(sync_ctx->file_input, source); if (o_stream_seek(output, dest) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "o_stream_seek()"); o_stream_unref(&output); return -1; } input = i_stream_create_limit(sync_ctx->file_input, size); ret = o_stream_send_istream(output, input); i_stream_unref(&input); if (ret == (off_t)size) ret = 0; else if (ret >= 0) { mbox_sync_set_critical(sync_ctx, "mbox_move(%"PRIuUOFF_T", %"PRIuUOFF_T", %"PRIuUOFF_T ") moved only %"PRIuUOFF_T" bytes", dest, source, size, (uoff_t)ret); ret = -1; } else if (ret < 0) { errno = output->stream_errno; mbox_set_syscall_error(sync_ctx->mbox, "o_stream_send_istream()"); } mbox_sync_file_updated(sync_ctx, FALSE); o_stream_destroy(&output); return (int)ret; } static int mbox_fill_space(struct mbox_sync_context *sync_ctx, uoff_t offset, uoff_t size) { unsigned char space[1024]; memset(space, ' ', sizeof(space)); while (size > sizeof(space)) { if (pwrite_full(sync_ctx->write_fd, space, sizeof(space), offset) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } size -= sizeof(space); } if (pwrite_full(sync_ctx->write_fd, space, size, offset) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } mbox_sync_file_updated(sync_ctx, TRUE); return 0; } void mbox_sync_headers_add_space(struct mbox_sync_mail_context *ctx, size_t size) { size_t data_size, pos, start_pos; const unsigned char *data; void *p; i_assert(size < SSIZE_T_MAX); if (ctx->mail.pseudo) start_pos = ctx->hdr_pos[MBOX_HDR_X_IMAPBASE]; else if (ctx->mail.space > 0) { /* update the header using the existing offset. otherwise we might chose wrong header and just decrease the available space */ start_pos = ctx->mail.offset - ctx->hdr_offset; } else { /* Append at the end of X-Keywords header, or X-UID if it doesn't exist */ start_pos = ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] != (size_t)-1 ? ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] : ctx->hdr_pos[MBOX_HDR_X_UID]; } data = str_data(ctx->header); data_size = str_len(ctx->header); i_assert(start_pos < data_size); for (pos = start_pos; pos < data_size; pos++) { if (data[pos] == '\n') { /* possibly continues in next line */ if (pos+1 == data_size || !IS_LWSP(data[pos+1])) break; start_pos = pos+1; } else if (!IS_LWSP(data[pos]) && data[pos] != '\r') { start_pos = pos+1; } } /* pos points to end of header now, and start_pos to beginning of whitespace. */ mbox_sync_move_buffer(ctx, pos, size, 0); p = buffer_get_space_unsafe(ctx->header, pos, size); memset(p, ' ', size); if (ctx->header_first_change > pos) ctx->header_first_change = pos; ctx->header_last_change = (size_t)-1; ctx->mail.space = (pos - start_pos) + size; ctx->mail.offset = ctx->hdr_offset; if (ctx->mail.space > 0) ctx->mail.offset += start_pos; } static void mbox_sync_header_remove_space(struct mbox_sync_mail_context *ctx, size_t start_pos, size_t *size) { const unsigned char *data; size_t data_size, pos, last_line_pos; /* find the end of the LWSP */ data = str_data(ctx->header); data_size = str_len(ctx->header); for (pos = last_line_pos = start_pos; pos < data_size; pos++) { if (data[pos] == '\n') { /* possibly continues in next line */ if (pos+1 == data_size || !IS_LWSP(data[pos+1])) { data_size = pos; break; } last_line_pos = pos+1; } else if (!IS_LWSP(data[pos]) && data[pos] != '\r') { start_pos = last_line_pos = pos+1; } } if (start_pos == data_size) return; /* and remove what we can */ if (ctx->header_first_change > start_pos) ctx->header_first_change = start_pos; ctx->header_last_change = (size_t)-1; if (data_size - start_pos <= *size) { /* remove it all */ mbox_sync_move_buffer(ctx, start_pos, 0, data_size - start_pos); *size -= data_size - start_pos; return; } /* we have more space than needed. since we're removing from the beginning of header instead of end, we don't have to worry about multiline-headers. */ mbox_sync_move_buffer(ctx, start_pos, 0, *size); if (last_line_pos <= start_pos + *size) last_line_pos = start_pos; else last_line_pos -= *size; data_size -= *size; *size = 0; if (ctx->mail.space < (off_t)(data_size - last_line_pos)) { ctx->mail.space = data_size - last_line_pos; ctx->mail.offset = ctx->hdr_offset; if (ctx->mail.space > 0) ctx->mail.offset += last_line_pos; } } static void mbox_sync_headers_remove_space(struct mbox_sync_mail_context *ctx, size_t size) { static enum header_position space_positions[] = { MBOX_HDR_X_UID, MBOX_HDR_X_KEYWORDS, MBOX_HDR_X_IMAPBASE }; enum header_position pos; int i; ctx->mail.space = 0; ctx->mail.offset = ctx->hdr_offset; for (i = 0; i < 3 && size > 0; i++) { pos = space_positions[i]; if (ctx->hdr_pos[pos] != (size_t)-1) { mbox_sync_header_remove_space(ctx, ctx->hdr_pos[pos], &size); } } /* FIXME: see if we could remove X-Keywords header completely */ } static void mbox_sync_first_mail_written(struct mbox_sync_mail_context *ctx, uoff_t hdr_offset) { /* we wrote the first mail. update last-uid offset so we can find it later */ i_assert(ctx->last_uid_value_start_pos != 0); i_assert(ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] != (size_t)-1); ctx->sync_ctx->base_uid_last_offset = hdr_offset + ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] + ctx->last_uid_value_start_pos; if (ctx->imapbase_updated) { /* update so a) we don't try to update it later unneededly, b) if we do actually update it, we see the correct value */ ctx->sync_ctx->base_uid_last = ctx->last_uid_updated_value; } } int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff) { struct mbox_sync_context *sync_ctx = ctx->sync_ctx; size_t old_hdr_size, new_hdr_size; i_assert(sync_ctx->mbox->mbox_lock_type == F_WRLCK); old_hdr_size = ctx->body_offset - ctx->hdr_offset; new_hdr_size = str_len(ctx->header); if (new_hdr_size <= old_hdr_size) { /* add space. note that we must call add_space() even if we're not adding anything so mail.offset gets fixed. */ mbox_sync_headers_add_space(ctx, old_hdr_size - new_hdr_size); } else if (new_hdr_size > old_hdr_size) { /* try removing the space where we can */ mbox_sync_headers_remove_space(ctx, new_hdr_size - old_hdr_size); new_hdr_size = str_len(ctx->header); if (new_hdr_size <= old_hdr_size) { /* good, we removed enough. */ i_assert(new_hdr_size == old_hdr_size); } else if (move_diff < 0 && new_hdr_size - old_hdr_size <= (uoff_t)-move_diff) { /* moving backwards - we can use the extra space from it, just update expunged_space accordingly */ i_assert(ctx->mail.space == 0); i_assert(sync_ctx->expunged_space >= (off_t)(new_hdr_size - old_hdr_size)); sync_ctx->expunged_space -= new_hdr_size - old_hdr_size; } else { /* couldn't get enough space */ i_assert(ctx->mail.space == 0); ctx->mail.space = -(ssize_t)(new_hdr_size - old_hdr_size); return 0; } } i_assert(ctx->mail.space >= 0); if (ctx->header_first_change == (size_t)-1 && move_diff == 0) { /* no changes actually. we get here if index sync record told us to do something that was already there */ return 1; } if (move_diff != 0) { /* forget about partial write optimizations */ ctx->header_first_change = 0; ctx->header_last_change = 0; } if (ctx->header_last_change != (size_t)-1 && ctx->header_last_change != 0) str_truncate(ctx->header, ctx->header_last_change); if (pwrite_full(sync_ctx->write_fd, str_data(ctx->header) + ctx->header_first_change, str_len(ctx->header) - ctx->header_first_change, ctx->hdr_offset + ctx->header_first_change + move_diff) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } if (sync_ctx->dest_first_mail && (ctx->imapbase_updated || ctx->sync_ctx->base_uid_last != 0)) { /* the position might have moved as a result of moving whitespace */ mbox_sync_first_mail_written(ctx, ctx->hdr_offset + move_diff); } mbox_sync_file_updated(sync_ctx, FALSE); return 1; } static int mbox_sync_read_next(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx, struct mbox_sync_mail *mails, uint32_t seq, uint32_t idx, uoff_t expunged_space) { unsigned int first_mail_expunge_extra; uint32_t orig_next_uid; i_zero(mail_ctx); mail_ctx->sync_ctx = sync_ctx; mail_ctx->seq = seq; mail_ctx->header = sync_ctx->header; if (istream_raw_mbox_get_header_offset(sync_ctx->input, &mail_ctx->mail.offset) < 0) { mbox_sync_set_critical(sync_ctx, "Couldn't get header offset for seq=%u", seq); return -1; } mail_ctx->mail.body_size = mails[idx].body_size; orig_next_uid = sync_ctx->next_uid; if (mails[idx].uid != 0) { /* This will force the UID to be the one that we originally assigned to it, regardless of whether it's broken or not in the file. */ sync_ctx->next_uid = mails[idx].uid; sync_ctx->prev_msg_uid = mails[idx].uid - 1; } else { /* Pseudo mail shouldn't have X-UID header at all */ i_assert(mails[idx].pseudo); sync_ctx->prev_msg_uid = 0; } first_mail_expunge_extra = 1 + (sync_ctx->first_mail_crlf_expunged ? 1 : 0); if (mails[idx].from_offset + first_mail_expunge_extra - expunged_space != 0) { sync_ctx->dest_first_mail = mails[idx].from_offset == 0; } else { /* we need to skip over the initial \n (it's already counted in expunged_space) */ sync_ctx->dest_first_mail = TRUE; mails[idx].from_offset += first_mail_expunge_extra; } if (mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx) < 0) return -1; i_assert(mail_ctx->mail.pseudo == mails[idx].pseudo); /* set next_uid back before updating the headers. this is important if we're updating the first message to make X-IMAP[base] header have the correct value. */ sync_ctx->next_uid = orig_next_uid; if (mails[idx].space != 0) { if (mails[idx].space < 0) { /* remove all possible spacing before updating */ mbox_sync_headers_remove_space(mail_ctx, (size_t)-1); } mbox_sync_update_header_from(mail_ctx, &mails[idx]); } else { /* updating might just try to add headers and mess up our calculations completely. so only add the EOH here. */ if (mail_ctx->have_eoh) str_append_c(mail_ctx->header, '\n'); } return 0; } static int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx, struct mbox_sync_mail *mails, uint32_t seq, uint32_t idx, uint32_t padding, off_t move_diff, uoff_t expunged_space, uoff_t end_offset, bool first_nonexpunged) { struct mbox_sync_mail_context new_mail_ctx; uoff_t offset, dest_offset; size_t need_space; if (mail_ctx == NULL) { if (mbox_sync_seek(sync_ctx, mails[idx].from_offset) < 0) return -1; if (mbox_sync_read_next(sync_ctx, &new_mail_ctx, mails, seq, idx, expunged_space) < 0) return -1; mail_ctx = &new_mail_ctx; } else { i_assert(seq == mail_ctx->seq); if (mail_ctx->mail.space < 0) mail_ctx->mail.space = 0; i_stream_seek(sync_ctx->input, mail_ctx->body_offset); } if (mail_ctx->mail.space <= 0) { need_space = str_len(mail_ctx->header) - mail_ctx->mail.space - (mail_ctx->body_offset - mail_ctx->hdr_offset); if (need_space != (uoff_t)-mails[idx].space) { /* this check works only if we're doing the first write, or if the file size was changed externally */ mbox_sync_file_update_ext_modified(sync_ctx); mbox_sync_set_critical(sync_ctx, "seq=%u uid=%u uid_broken=%d " "originally needed %"PRIuUOFF_T " bytes, now needs %"PRIuSIZE_T" bytes", seq, mails[idx].uid, mails[idx].uid_broken, (uoff_t)-mails[idx].space, need_space); return -1; } } if (first_nonexpunged && expunged_space > 0) { /* move From-line (after parsing headers so we don't overwrite them) */ i_assert(mails[idx].from_offset >= expunged_space); if (mbox_move(sync_ctx, mails[idx].from_offset - expunged_space, mails[idx].from_offset, mails[idx].offset - mails[idx].from_offset) < 0) return -1; } if (mails[idx].space == 0) { /* don't touch spacing */ } else if (padding < (uoff_t)mail_ctx->mail.space) { mbox_sync_headers_remove_space(mail_ctx, mail_ctx->mail.space - padding); } else { mbox_sync_headers_add_space(mail_ctx, padding - mail_ctx->mail.space); } /* move the body of this message and headers of next message forward, then write the headers */ offset = sync_ctx->input->v_offset; dest_offset = offset + move_diff; i_assert(offset <= end_offset); if (mbox_move(sync_ctx, dest_offset, offset, end_offset - offset) < 0) return -1; /* the header may actually be moved backwards if there was expunged space which we wanted to remove */ i_assert(dest_offset >= str_len(mail_ctx->header)); dest_offset -= str_len(mail_ctx->header); i_assert(dest_offset >= mails[idx].from_offset - expunged_space); if (pwrite_full(sync_ctx->write_fd, str_data(mail_ctx->header), str_len(mail_ctx->header), dest_offset) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()"); return -1; } mbox_sync_file_updated(sync_ctx, TRUE); if (sync_ctx->dest_first_mail) { mbox_sync_first_mail_written(mail_ctx, dest_offset); sync_ctx->dest_first_mail = FALSE; } mails[idx].offset = dest_offset + (mail_ctx->mail.offset - mail_ctx->hdr_offset); mails[idx].space = mail_ctx->mail.space; return 0; } int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, struct mbox_sync_mail_context *mail_ctx, uoff_t end_offset, off_t move_diff, uoff_t extra_space, uint32_t first_seq, uint32_t last_seq) { struct mbox_sync_mail *mails; uoff_t offset, dest_offset, next_end_offset, next_move_diff; uoff_t start_offset, expunged_space; uint32_t idx, first_nonexpunged_idx, padding_per_mail; uint32_t orig_prev_msg_uid; unsigned int count; int ret = 0; i_assert(extra_space < OFF_T_MAX); i_assert(sync_ctx->mbox->mbox_lock_type == F_WRLCK); mails = array_get_modifiable(&sync_ctx->mails, &count); i_assert(count == last_seq - first_seq + 1); /* if there's expunges in mails[], we would get more correct balancing by counting only them here. however, that might make us overwrite data which hasn't yet been copied backwards. to avoid too much complexity, we just leave all the rest of the extra space to first mail */ idx = last_seq - first_seq + 1; padding_per_mail = extra_space / idx; /* after expunge the next mail must have been missing space, or we would have moved it backwards already */ expunged_space = 0; start_offset = mails[0].from_offset; for (first_nonexpunged_idx = 0;; first_nonexpunged_idx++) { i_assert(first_nonexpunged_idx != idx); if (!mails[first_nonexpunged_idx].expunged) break; expunged_space += mails[first_nonexpunged_idx].space; } i_assert(mails[first_nonexpunged_idx].space < 0); orig_prev_msg_uid = sync_ctx->prev_msg_uid; /* start moving backwards. */ while (idx > first_nonexpunged_idx) { idx--; if (idx == first_nonexpunged_idx) { /* give the rest of the extra space to first mail. we might also have to move the mail backwards to fill the expunged space */ padding_per_mail = move_diff + expunged_space + mails[idx].space; } next_end_offset = mails[idx].offset; if (mails[idx].space <= 0 && !mails[idx].expunged) { /* give space to this mail. end_offset is left to contain this message's From-line (ie. below we move only headers + body). */ bool first_nonexpunged = idx == first_nonexpunged_idx; next_move_diff = -mails[idx].space; if (mbox_sync_read_and_move(sync_ctx, mail_ctx, mails, first_seq + idx, idx, padding_per_mail, move_diff, expunged_space, end_offset, first_nonexpunged) < 0) { ret = -1; break; } move_diff -= next_move_diff + mails[idx].space; } else { /* this mail provides more space. just move it forward from the extra space offset and set end_offset to point to beginning of extra space. that way the header will be moved along with previous mail's body. if this is expunged mail, we're moving following mail's From-line and maybe headers. */ offset = mails[idx].offset + mails[idx].space; dest_offset = offset + move_diff; i_assert(offset <= end_offset); if (mbox_move(sync_ctx, dest_offset, offset, end_offset - offset) < 0) { ret = -1; break; } move_diff += mails[idx].space; if (!mails[idx].expunged) { move_diff -= padding_per_mail; mails[idx].space = padding_per_mail; if (mbox_fill_space(sync_ctx, move_diff + mails[idx].offset, padding_per_mail) < 0) { ret = -1; break; } } mails[idx].offset += move_diff; } mail_ctx = NULL; i_assert(move_diff >= 0 || idx == first_nonexpunged_idx); i_assert(next_end_offset <= end_offset); end_offset = next_end_offset; mails[idx].from_offset += move_diff; } if (ret == 0) { i_assert(mails[idx].from_offset == start_offset); i_assert(move_diff + (off_t)expunged_space >= 0); } mbox_sync_file_updated(sync_ctx, FALSE); sync_ctx->prev_msg_uid = orig_prev_msg_uid; return ret; } dovecot-2.2.33.2/src/lib-storage/index/mbox/Makefile.am0000644000175000017500000000146513123174404017441 00000000000000noinst_LTLIBRARIES = libstorage_mbox.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index libstorage_mbox_la_SOURCES = \ istream-raw-mbox.c \ mbox-file.c \ mbox-lock.c \ mbox-mail.c \ mbox-md5-apop3d.c \ mbox-md5-all.c \ mbox-save.c \ mbox-settings.c \ mbox-sync-list-index.c \ mbox-sync-parse.c \ mbox-sync-rewrite.c \ mbox-sync-update.c \ mbox-sync.c \ mbox-storage.c headers = \ istream-raw-mbox.h \ mbox-file.h \ mbox-lock.h \ mbox-md5.h \ mbox-settings.h \ mbox-storage.h \ mbox-sync-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-thread.c0000644000175000017500000004472013165463624017174 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* doc/thread-refs.txt describes the incremental algorithm we use here. */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "hash2.h" #include "message-id.h" #include "mail-search.h" #include "mail-search-build.h" #include "mailbox-search-result-private.h" #include "index-storage.h" #include "index-thread-private.h" #define MAIL_THREAD_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_thread_storage_module) struct mail_thread_context { struct mailbox *box; struct mailbox_transaction_context *t; struct mail_index_strmap_view_sync *strmap_sync; struct mail *tmp_mail; struct mail_search_args *search_args; ARRAY_TYPE(seq_range) added_uids; unsigned int failed:1; unsigned int corrupted:1; }; struct mail_thread_mailbox { union mailbox_module_context module_ctx; unsigned int next_msgid_idx; struct mail_thread_cache *cache; struct mail_index_strmap *strmap; struct mail_index_strmap_view *strmap_view; /* sorted by UID, ref_index */ const ARRAY_TYPE(mail_index_strmap_rec) *msgid_map; const struct hash2_table *msgid_hash; /* set only temporarily while needed */ struct mail_thread_context *ctx; }; static MODULE_CONTEXT_DEFINE_INIT(mail_thread_storage_module, &mail_storage_module_register); static void mail_thread_clear(struct mail_thread_context *ctx); static int mail_strmap_rec_get_msgid(struct mail_thread_context *ctx, const struct mail_index_strmap_rec *rec, const char **msgid_r) { struct mail *mail = ctx->tmp_mail; const char *msgids = NULL, *msgid; unsigned int n = 0; int ret; if (!mail_set_uid(mail, rec->uid)) return 0; switch (rec->ref_index) { case MAIL_THREAD_NODE_REF_MSGID: /* Message-ID: header */ ret = mail_get_first_header(mail, HDR_MESSAGE_ID, &msgids); break; case MAIL_THREAD_NODE_REF_INREPLYTO: /* In-Reply-To: header */ ret = mail_get_first_header(mail, HDR_IN_REPLY_TO, &msgids); break; default: /* References: header */ ret = mail_get_first_header(mail, HDR_REFERENCES, &msgids); n = rec->ref_index - MAIL_THREAD_NODE_REF_REFERENCES1; break; } if (ret < 0) { if (mail->expunged) { /* treat it as if it didn't exist. trying to add it again will result in failure. */ return 0; } return -1; } /* get the nth message-id */ msgid = message_id_get_next(&msgids); if (msgid != NULL) { for (; n > 0; n--) msgid = message_id_get_next(&msgids); } if (msgid == NULL) { /* shouldn't have happened, probably corrupted */ mail_storage_set_critical(mail->box->storage, "Corrupted thread index for mailbox %s: " "UID %u lost Message ID %u", mail->box->vname, mail->uid, rec->ref_index); ctx->failed = TRUE; ctx->corrupted = TRUE; return -1; } *msgid_r = msgid; return 1; } static bool mail_thread_hash_key_cmp(const char *key, const struct mail_index_strmap_rec *rec, void *context) { struct mail_thread_mailbox *tbox = context; struct mail_thread_context *ctx = tbox->ctx; const char *msgid; bool cmp_ret; int ret; /* either a match or a collision, need to look closer */ T_BEGIN { ret = mail_strmap_rec_get_msgid(ctx, rec, &msgid); if (ret <= 0) { if (ret < 0) ctx->failed = TRUE; cmp_ret = FALSE; } else { cmp_ret = strcmp(msgid, key) == 0; } } T_END; return cmp_ret; } static int mail_thread_hash_rec_cmp(const struct mail_index_strmap_rec *rec1, const struct mail_index_strmap_rec *rec2, void *context) { struct mail_thread_mailbox *tbox = context; struct mail_thread_context *ctx = tbox->ctx; const char *msgid1, *msgid2; int ret; T_BEGIN { ret = mail_strmap_rec_get_msgid(ctx, rec1, &msgid1); if (ret > 0) { msgid1 = t_strdup(msgid1); ret = mail_strmap_rec_get_msgid(ctx, rec2, &msgid2); } ret = ret <= 0 ? -1 : strcmp(msgid1, msgid2) == 0; } T_END; return ret; } static void mail_thread_strmap_remap(const uint32_t *idx_map, unsigned int old_count, unsigned int new_count, void *context) { struct mail_thread_mailbox *tbox = context; struct mail_thread_cache *cache = tbox->cache; ARRAY_TYPE(mail_thread_node) new_nodes; const struct mail_thread_node *old_nodes; struct mail_thread_node *node; unsigned int i, nodes_count, max, new_first_invalid, invalid_count; if (cache->search_result == NULL) return; if (new_count == 0) { /* strmap was reset, we'll need to rebuild thread */ mailbox_search_result_free(&cache->search_result); return; } invalid_count = cache->next_invalid_msgid_str_idx - cache->first_invalid_msgid_str_idx; old_nodes = array_get(&cache->thread_nodes, &nodes_count); i_array_init(&new_nodes, new_count + invalid_count + 32); /* optimization: allocate all nodes initially */ (void)array_idx_modifiable(&new_nodes, new_count-1); /* renumber existing valid nodes. all existing records in old_nodes should also exist in idx_map since we've removed expunged messages from the cache before committing the sync. */ max = I_MIN(I_MIN(old_count, nodes_count), cache->first_invalid_msgid_str_idx); for (i = 0; i < max; i++) { if (idx_map[i] == 0) { /* expunged record. */ i_assert(old_nodes[i].uid == 0); } else { node = array_idx_modifiable(&new_nodes, idx_map[i]); *node = old_nodes[i]; if (node->parent_idx != 0) { node->parent_idx = idx_map[node->parent_idx]; i_assert(node->parent_idx != 0); } } } /* copy invalid nodes, if any. no other messages point to them, so this is safe. we still need to update their parent_idx pointers though. */ new_first_invalid = new_count + 1 + THREAD_INVALID_MSGID_STR_IDX_SKIP_COUNT; for (i = 0; i < invalid_count; i++) { node = array_idx_modifiable(&new_nodes, new_first_invalid + i); *node = old_nodes[cache->first_invalid_msgid_str_idx + i]; if (node->parent_idx != 0) { node->parent_idx = idx_map[node->parent_idx]; i_assert(node->parent_idx != 0); } } cache->first_invalid_msgid_str_idx = new_first_invalid; cache->next_invalid_msgid_str_idx = new_first_invalid + invalid_count; /* replace the old nodes with the renumbered ones */ array_free(&cache->thread_nodes); cache->thread_nodes = new_nodes; } static int thread_get_mail_header(struct mail *mail, const char *name, const char **value_r) { if (mail_get_first_header(mail, name, value_r) < 0) { if (!mail->expunged) return -1; /* Message is expunged. Instead of failing the entire THREAD command, just treat the header as nonexistent. */ *value_r = NULL; } return 0; } static int mail_thread_map_add_mail(struct mail_thread_context *ctx, struct mail *mail) { const char *message_id, *in_reply_to, *references, *msgid; uint32_t ref_index; if (thread_get_mail_header(mail, HDR_MESSAGE_ID, &message_id) < 0 || thread_get_mail_header(mail, HDR_REFERENCES, &references) < 0) return -1; /* add Message-ID: */ msgid = message_id_get_next(&message_id); if (msgid != NULL) { mail_index_strmap_view_sync_add(ctx->strmap_sync, mail->uid, MAIL_THREAD_NODE_REF_MSGID, msgid); } else { mail_index_strmap_view_sync_add_unique(ctx->strmap_sync, mail->uid, MAIL_THREAD_NODE_REF_MSGID); } /* add References: if there are any valid ones */ msgid = message_id_get_next(&references); if (msgid != NULL) { ref_index = MAIL_THREAD_NODE_REF_REFERENCES1; do { mail_index_strmap_view_sync_add(ctx->strmap_sync, mail->uid, ref_index, msgid); ref_index++; msgid = message_id_get_next(&references); } while (msgid != NULL); } else { /* no References:, use In-Reply-To: */ if (thread_get_mail_header(mail, HDR_IN_REPLY_TO, &in_reply_to) < 0) return -1; msgid = message_id_get_next(&in_reply_to); if (msgid != NULL) { mail_index_strmap_view_sync_add(ctx->strmap_sync, mail->uid, MAIL_THREAD_NODE_REF_INREPLYTO, msgid); } } if (ctx->failed) { /* message-id lookup failed in hash compare */ return -1; } return 0; } static int mail_thread_index_map_build(struct mail_thread_context *ctx) { static const char *wanted_headers[] = { HDR_MESSAGE_ID, HDR_IN_REPLY_TO, HDR_REFERENCES, NULL }; struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(ctx->box); struct mailbox_header_lookup_ctx *headers_ctx; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mail *mail; uint32_t last_uid, seq1, seq2; int ret = 0; if (tbox->strmap_view == NULL) { /* first time we're threading this mailbox */ tbox->strmap_view = mail_index_strmap_view_open(tbox->strmap, ctx->box->view, mail_thread_hash_key_cmp, mail_thread_hash_rec_cmp, mail_thread_strmap_remap, tbox, &tbox->msgid_map, &tbox->msgid_hash); } headers_ctx = mailbox_header_lookup_init(ctx->box, wanted_headers); ctx->tmp_mail = mail_alloc(ctx->t, 0, headers_ctx); /* add all missing UIDs */ ctx->strmap_sync = mail_index_strmap_view_sync_init(tbox->strmap_view, &last_uid); mailbox_get_seq_range(ctx->box, last_uid + 1, (uint32_t)-1, &seq1, &seq2); if (seq1 == 0) { /* nothing is missing */ mail_index_strmap_view_sync_commit(&ctx->strmap_sync); mailbox_header_lookup_unref(&headers_ctx); return 0; } search_args = mail_search_build_init(); mail_search_build_add_seqset(search_args, seq1, seq2); search_ctx = mailbox_search_init(ctx->t, search_args, NULL, 0, headers_ctx); mailbox_header_lookup_unref(&headers_ctx); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { if (mail_thread_map_add_mail(ctx, mail) < 0) { ret = -1; break; } } if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; if (ret < 0) mail_index_strmap_view_sync_rollback(&ctx->strmap_sync); else mail_index_strmap_view_sync_commit(&ctx->strmap_sync); return ret; } static int msgid_map_cmp(const uint32_t *uid, const struct mail_index_strmap_rec *rec) { return *uid < rec->uid ? -1 : (*uid > rec->uid ? 1 : 0); } static bool mail_thread_cache_update_removes(struct mail_thread_mailbox *tbox, ARRAY_TYPE(seq_range) *added_uids) { struct mail_thread_cache *cache = tbox->cache; ARRAY_TYPE(seq_range) removed_uids; const struct seq_range *uids; const struct mail_index_strmap_rec *msgid_map; unsigned int i, j, idx, map_count, uid_count; uint32_t uid; t_array_init(&removed_uids, 64); mailbox_search_result_sync(cache->search_result, &removed_uids, added_uids); /* first check that we're not inserting any messages in the middle */ uids = array_get(added_uids, &uid_count); if (uid_count > 0 && uids[0].seq1 <= cache->last_uid) return FALSE; /* next remove messages so we'll see early if we have to rebuild. we expect to find all removed UIDs from msgid_map that are <= max UID in msgid_map */ msgid_map = array_get(tbox->msgid_map, &map_count); uids = array_get(&removed_uids, &uid_count); for (i = j = 0; i < uid_count; i++) { /* find and remove from the map */ bsearch_insert_pos(&uids[i].seq1, &msgid_map[j], map_count - j, sizeof(*msgid_map), msgid_map_cmp, &idx); j += idx; if (j == map_count) { /* all removals after this are about messages we never even added to the cache */ i_assert(uids[i].seq1 > cache->last_uid); break; } while (j > 0 && msgid_map[j-1].uid == msgid_map[j].uid) j--; /* remove the messages from cache */ for (uid = uids[i].seq1; uid <= uids[i].seq2; uid++) { if (j == map_count) { i_assert(uid > cache->last_uid); break; } i_assert(msgid_map[j].uid == uid); if (!mail_thread_remove(cache, msgid_map + j, &j)) return FALSE; } } return TRUE; } static void mail_thread_cache_update_adds(struct mail_thread_mailbox *tbox, ARRAY_TYPE(seq_range) *added_uids) { struct mail_thread_cache *cache = tbox->cache; const struct seq_range *uids; const struct mail_index_strmap_rec *msgid_map; unsigned int i, j, map_count, uid_count; uint32_t uid; /* everything removed successfully, add the new messages. all of them should already be in msgid_map. */ uids = array_get(added_uids, &uid_count); if (uid_count == 0) return; (void)array_bsearch_insert_pos(tbox->msgid_map, &uids[0].seq1, msgid_map_cmp, &j); msgid_map = array_get(tbox->msgid_map, &map_count); i_assert(j < map_count); while (j > 0 && msgid_map[j-1].uid == msgid_map[j].uid) j--; for (i = 0; i < uid_count; i++) { for (uid = uids[i].seq1; uid <= uids[i].seq2; uid++) { while (j < map_count && msgid_map[j].uid < uid) j++; i_assert(j < map_count && msgid_map[j].uid == uid); mail_thread_add(cache, msgid_map+j, &j); } } } static void mail_thread_cache_fix_invalid_indexes(struct mail_thread_mailbox *tbox) { struct mail_thread_cache *cache = tbox->cache; uint32_t highest_idx, new_first_idx, count; highest_idx = mail_index_strmap_view_get_highest_idx(tbox->strmap_view); new_first_idx = highest_idx + 1 + THREAD_INVALID_MSGID_STR_IDX_SKIP_COUNT; count = cache->next_invalid_msgid_str_idx - cache->first_invalid_msgid_str_idx; if (count == 0) { /* there are no invalid indexes yet, we can update the first invalid index position to delay conflicts. */ cache->first_invalid_msgid_str_idx = cache->next_invalid_msgid_str_idx = new_first_idx; } else if (highest_idx >= cache->first_invalid_msgid_str_idx) { /* conflict - move the invalid indexes forward */ array_copy(&cache->thread_nodes.arr, new_first_idx, &cache->thread_nodes.arr, cache->first_invalid_msgid_str_idx, count); cache->first_invalid_msgid_str_idx = new_first_idx; cache->next_invalid_msgid_str_idx = new_first_idx + count; } } static void mail_thread_cache_sync_remove(struct mail_thread_mailbox *tbox, struct mail_thread_context *ctx) { struct mail_thread_cache *cache = tbox->cache; if (cache->search_result == NULL) return; if (mail_search_args_equal(ctx->search_args, cache->search_result->search_args)) { t_array_init(&ctx->added_uids, 64); if (mail_thread_cache_update_removes(tbox, &ctx->added_uids)) { /* successfully updated the cache */ return; } } /* failed to use the cache, rebuild */ mailbox_search_result_free(&cache->search_result); } static void mail_thread_cache_sync_add(struct mail_thread_mailbox *tbox, struct mail_thread_context *ctx, struct mail_search_context *search_ctx) { struct mail_thread_cache *cache = tbox->cache; struct mail *mail; const struct mail_index_strmap_rec *msgid_map; unsigned int i, count; mail_thread_cache_fix_invalid_indexes(tbox); if (cache->search_result != NULL) { /* we already checked at sync_remove that we can use this search result. */ mail_thread_cache_update_adds(tbox, &ctx->added_uids); return; } cache->last_uid = 0; cache->first_invalid_msgid_str_idx = cache->next_invalid_msgid_str_idx = mail_index_strmap_view_get_highest_idx(tbox->strmap_view) + 1 + THREAD_INVALID_MSGID_STR_IDX_SKIP_COUNT; array_clear(&cache->thread_nodes); cache->search_result = mailbox_search_result_save(search_ctx, MAILBOX_SEARCH_RESULT_FLAG_UPDATE | MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC); msgid_map = array_get(tbox->msgid_map, &count); /* we're relying on the array being zero-terminated (outside used count - kind of kludgy) */ i_assert(msgid_map[count].uid == 0); i = 0; while (i < count && mailbox_search_next(search_ctx, &mail)) { while (msgid_map[i].uid < mail->uid) i++; i_assert(i < count); mail_thread_add(cache, msgid_map+i, &i); } } int mail_thread_init(struct mailbox *box, struct mail_search_args *args, struct mail_thread_context **ctx_r) { struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(box); struct mail_thread_context *ctx; struct mail_search_context *search_ctx; int ret; i_assert(tbox->ctx == NULL); if (args != NULL) mail_search_args_ref(args); else { args = mail_search_build_init(); mail_search_build_add_all(args); mail_search_args_init(args, box, FALSE, NULL); } ctx = i_new(struct mail_thread_context, 1); ctx->box = box; ctx->search_args = args; ctx->t = mailbox_transaction_begin(ctx->box, 0); /* perform search first, so we don't break if there are INTHREAD keys */ search_ctx = mailbox_search_init(ctx->t, args, NULL, 0, NULL); tbox->ctx = ctx; mail_thread_cache_sync_remove(tbox, ctx); ret = mail_thread_index_map_build(ctx); if (ret == 0) mail_thread_cache_sync_add(tbox, ctx, search_ctx); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; if (ctx->failed) { ret = -1; if (ctx->corrupted) mail_index_strmap_view_set_corrupted(tbox->strmap_view); } if (ret < 0) { mail_thread_deinit(&ctx); return -1; } else { i_zero(&ctx->added_uids); *ctx_r = ctx; return 0; } } static void mail_thread_clear(struct mail_thread_context *ctx) { mail_free(&ctx->tmp_mail); (void)mailbox_transaction_commit(&ctx->t); } void mail_thread_deinit(struct mail_thread_context **_ctx) { struct mail_thread_context *ctx = *_ctx; struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(ctx->box); *_ctx = NULL; mail_thread_clear(ctx); mail_search_args_unref(&ctx->search_args); tbox->ctx = NULL; i_free(ctx); } struct mail_thread_iterate_context * mail_thread_iterate_init(struct mail_thread_context *ctx, enum mail_thread_type thread_type, bool write_seqs) { struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(ctx->box); return mail_thread_iterate_init_full(tbox->cache, ctx->tmp_mail, thread_type, write_seqs); } static void mail_thread_mailbox_close(struct mailbox *box) { struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(box); i_assert(tbox->ctx == NULL); if (tbox->strmap_view != NULL) mail_index_strmap_view_close(&tbox->strmap_view); if (tbox->cache->search_result != NULL) mailbox_search_result_free(&tbox->cache->search_result); tbox->module_ctx.super.close(box); } static void mail_thread_mailbox_free(struct mailbox *box) { struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(box); mail_index_strmap_deinit(&tbox->strmap); tbox->module_ctx.super.free(box); array_free(&tbox->cache->thread_nodes); i_free(tbox->cache); i_free(tbox); } void index_thread_mailbox_opened(struct mailbox *box) { struct mail_thread_mailbox *tbox = MAIL_THREAD_CONTEXT(box); if (tbox != NULL) { /* mailbox was already opened+closed once. */ return; } tbox = i_new(struct mail_thread_mailbox, 1); tbox->module_ctx.super = box->v; box->v.close = mail_thread_mailbox_close; box->v.free = mail_thread_mailbox_free; tbox->strmap = mail_index_strmap_init(box->index, MAIL_THREAD_INDEX_SUFFIX); tbox->next_msgid_idx = 1; tbox->cache = i_new(struct mail_thread_cache, 1); i_array_init(&tbox->cache->thread_nodes, 128); MODULE_CONTEXT_SET(box, mail_thread_storage_module, tbox); } dovecot-2.2.33.2/src/lib-storage/index/Makefile.am0000644000175000017500000000243613123174404016473 00000000000000SUBDIRS = maildir mbox dbox-common dbox-multi dbox-single cydir imapc pop3c raw shared noinst_LTLIBRARIES = libstorage_index.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage libstorage_index_la_SOURCES = \ istream-mail.c \ index-attachment.c \ index-attribute.c \ index-mail.c \ index-mail-binary.c \ index-mail-headers.c \ index-mailbox-size.c \ index-pop3-uidl.c \ index-rebuild.c \ index-search.c \ index-search-mime.c \ index-search-result.c \ index-sort.c \ index-sort-string.c \ index-status.c \ index-storage.c \ index-sync.c \ index-sync-changes.c \ index-sync-pvt.c \ index-sync-search.c \ index-thread.c \ index-thread-finish.c \ index-thread-links.c \ index-transaction.c headers = \ istream-mail.h \ index-attachment.h \ index-mail.h \ index-mailbox-size.h \ index-pop3-uidl.h \ index-rebuild.h \ index-search-private.h \ index-search-result.h \ index-sort.h \ index-sort-private.h \ index-storage.h \ index-sync-changes.h \ index-sync-private.h \ index-thread-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-storage/index/index-thread-private.h0000644000175000017500000000533513165463624020650 00000000000000#ifndef INDEX_THREAD_PRIVATE_H #define INDEX_THREAD_PRIVATE_H #include "crc32.h" #include "mail-thread.h" #include "mail-index-strmap.h" #define MAIL_THREAD_INDEX_SUFFIX ".thread" /* After initially building the index, assign first_invalid_msgid_idx to the next unused index + SKIP_COUNT. When more messages are added and the next valid msgid conflicts with the first invalid msgid, the invalid msgids will be moved forward again this many indexes. */ #define THREAD_INVALID_MSGID_STR_IDX_SKIP_COUNT \ (4096 / sizeof(struct mail_thread_node)) #define HDR_MESSAGE_ID "message-id" #define HDR_IN_REPLY_TO "in-reply-to" #define HDR_REFERENCES "references" #define HDR_SUBJECT "subject" #define MAIL_THREAD_NODE_REF_MSGID 0 #define MAIL_THREAD_NODE_REF_INREPLYTO 1 #define MAIL_THREAD_NODE_REF_REFERENCES1 2 struct mail_thread_node { /* UID of the message, or 0 for dummy nodes */ uint32_t uid; /* Index for this node's parent node, 0 = this is root */ uint32_t parent_idx; /* Number of messages containing "this message" -> "parent message" link, i.e. "number of links to parent node". However since parents can change, not all of these references might be from our current child nodes. When this refcount reaches 0, it means we must detach from our parent. */ unsigned int parent_link_refcount:30; /* If uid is expunged, rebuild the thread tree. */ unsigned int expunge_rebuilds:1; /* If a link between this node and its child gets unreferenced, rebuild the thread tree. */ unsigned int child_unref_rebuilds:1; }; ARRAY_DEFINE_TYPE(mail_thread_node, struct mail_thread_node); #define MAIL_THREAD_NODE_EXISTS(node) \ ((node)->uid != 0) struct mail_thread_cache { uint32_t last_uid; /* indexes used for invalid Message-IDs. that means no other messages point to them and they can safely be moved around whenever necessary. */ uint32_t first_invalid_msgid_str_idx; uint32_t next_invalid_msgid_str_idx; struct mail_search_result *search_result; /* indexed by mail_index_strmap_rec.str_idx */ ARRAY_TYPE(mail_thread_node) thread_nodes; }; static inline uint32_t crc32_str_nonzero(const char *str) { uint32_t value = crc32_str(str); return value == 0 ? 1 : value; } void mail_thread_add(struct mail_thread_cache *cache, const struct mail_index_strmap_rec *msgid_map, unsigned int *msgid_map_idx); bool mail_thread_remove(struct mail_thread_cache *cache, const struct mail_index_strmap_rec *msgid_map, unsigned int *msgid_map_idx); struct mail_thread_iterate_context * mail_thread_iterate_init_full(struct mail_thread_cache *cache, struct mail *tmp_mail, enum mail_thread_type thread_type, bool return_seqs); void index_thread_mailbox_opened(struct mailbox *box); #endif dovecot-2.2.33.2/src/lib-storage/index/index-status.c0000644000175000017500000002501113165463624017240 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-cache.h" #include "mail-index-modseq.h" #include "index-storage.h" static void get_last_cached_seq(struct mailbox *box, uint32_t *last_cached_seq_r) { const struct mail_index_header *hdr; struct mail_cache_view *cache_view; uint32_t seq; *last_cached_seq_r = 0; if (!mail_cache_exists(box->cache)) return; cache_view = mail_cache_view_open(box->cache, box->view); hdr = mail_index_get_header(box->view); for (seq = hdr->messages_count; seq > 0; seq--) { if (mail_cache_field_exists_any(cache_view, seq)) { *last_cached_seq_r = seq; break; } } mail_cache_view_close(&cache_view); } int index_storage_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { if (items == 0) { /* caller could have wanted only e.g. mailbox_status.have_* flags */ return 0; } if (!box->opened) { if (mailbox_open(box) < 0) return -1; if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) return -1; } index_storage_get_open_status(box, items, status_r); return 0; } static unsigned int index_storage_count_pvt_unseen(struct mailbox *box) { const struct mail_index_record *pvt_rec; uint32_t shared_seq, pvt_seq, shared_count, pvt_count; uint32_t shared_uid; unsigned int unseen_count = 0; /* we can't trust private index to be up to date. we'll need to go through the shared index and for each existing mail lookup its private flags. if a mail doesn't exist in private index then its flags are 0. */ shared_count = mail_index_view_get_messages_count(box->view); pvt_count = mail_index_view_get_messages_count(box->view_pvt); shared_seq = pvt_seq = 1; while (shared_seq <= shared_count && pvt_seq <= pvt_count) { mail_index_lookup_uid(box->view, shared_seq, &shared_uid); pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq); if (shared_uid == pvt_rec->uid) { if ((pvt_rec->flags & MAIL_SEEN) == 0) unseen_count++; shared_seq++; pvt_seq++; } else if (shared_uid < pvt_rec->uid) { shared_seq++; } else { pvt_seq++; } } unseen_count += (shared_count+1) - shared_seq; return unseen_count; } static uint32_t index_storage_find_first_pvt_unseen_seq(struct mailbox *box) { const struct mail_index_header *pvt_hdr; const struct mail_index_record *pvt_rec; uint32_t pvt_seq, pvt_count, shared_seq, seq2; pvt_count = mail_index_view_get_messages_count(box->view_pvt); mail_index_lookup_first(box->view_pvt, 0, MAIL_SEEN, &pvt_seq); if (pvt_seq == 0) pvt_seq = pvt_count+1; for (; pvt_seq <= pvt_count; pvt_seq++) { pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq); if ((pvt_rec->flags & MAIL_SEEN) == 0 && mail_index_lookup_seq(box->view, pvt_rec->uid, &shared_seq)) return shared_seq; } /* if shared index has any messages that don't exist in private index, the first of them is the first unseen message */ pvt_hdr = mail_index_get_header(box->view_pvt); if (mail_index_lookup_seq_range(box->view, pvt_hdr->next_uid, (uint32_t)-1, &shared_seq, &seq2)) return shared_seq; return 0; } void index_storage_get_open_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { const struct mail_index_header *hdr; /* we can get most of the status items without any trouble */ hdr = mail_index_get_header(box->view); status_r->messages = hdr->messages_count; if ((items & STATUS_RECENT) != 0) { if ((box->flags & MAILBOX_FLAG_DROP_RECENT) != 0) { /* recent flags are set and dropped by the previous sync while index was locked. if we updated the recent flags here we'd have a race condition. */ i_assert(box->synced); } else { /* make sure recent count is set, in case we haven't synced yet */ index_sync_update_recent_count(box); } status_r->recent = mailbox_recent_flags_count(box); i_assert(status_r->recent <= status_r->messages); } if ((items & STATUS_UNSEEN) != 0) { if (box->view_pvt == NULL || (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) { status_r->unseen = hdr->messages_count - hdr->seen_messages_count; } else { status_r->unseen = index_storage_count_pvt_unseen(box); } } status_r->uidvalidity = hdr->uid_validity; status_r->uidnext = hdr->next_uid; status_r->first_recent_uid = hdr->first_recent_uid; if ((items & STATUS_HIGHESTMODSEQ) != 0) { status_r->nonpermanent_modseqs = mail_index_is_in_memory(box->index); status_r->no_modseq_tracking = !mail_index_have_modseq_tracking(box->index); status_r->highest_modseq = mail_index_modseq_get_highest(box->view); if (status_r->highest_modseq == 0) { /* modseqs not enabled yet, but we can't return 0 */ status_r->highest_modseq = 1; } } if ((items & STATUS_HIGHESTPVTMODSEQ) != 0 && box->view_pvt != NULL) { status_r->highest_pvt_modseq = mail_index_modseq_get_highest(box->view_pvt); if (status_r->highest_pvt_modseq == 0) { /* modseqs not enabled yet, but we can't return 0 */ status_r->highest_pvt_modseq = 1; } } if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) { if (box->view_pvt == NULL || (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) { mail_index_lookup_first(box->view, 0, MAIL_SEEN, &status_r->first_unseen_seq); } else { status_r->first_unseen_seq = index_storage_find_first_pvt_unseen_seq(box); } } if ((items & STATUS_LAST_CACHED_SEQ) != 0) get_last_cached_seq(box, &status_r->last_cached_seq); if ((items & STATUS_KEYWORDS) != 0) status_r->keywords = mail_index_get_keywords(box->index); if ((items & STATUS_PERMANENT_FLAGS) != 0) { if (!mailbox_is_readonly(box)) { status_r->permanent_flags = MAIL_FLAGS_NONRECENT; status_r->permanent_keywords = TRUE; status_r->allow_new_keywords = !box->disallow_new_keywords; } status_r->flags = MAIL_FLAGS_NONRECENT; } } static void get_metadata_cache_fields(struct mailbox *box, struct mailbox_metadata *metadata_r) { const struct mail_cache_field *fields; enum mail_cache_decision_type dec; ARRAY_TYPE(mailbox_cache_field) *cache_fields; struct mailbox_cache_field *cf; unsigned int i, count; if (box->metadata_pool == NULL) { box->metadata_pool = pool_alloconly_create("mailbox metadata", 1024*3); } fields = mail_cache_register_get_list(box->cache, box->metadata_pool, &count); cache_fields = p_new(box->metadata_pool, ARRAY_TYPE(mailbox_cache_field), 1); p_array_init(cache_fields, box->metadata_pool, count); for (i = 0; i < count; i++) { dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED; if (dec != MAIL_CACHE_DECISION_NO) { cf = array_append_space(cache_fields); cf->name = fields[i].name; cf->decision = fields[i].decision; cf->last_used = fields[i].last_used; } } metadata_r->cache_fields = cache_fields; } static void get_metadata_precache_fields(struct mailbox *box, struct mailbox_metadata *metadata_r) { const struct mail_cache_field *fields; unsigned int i, count; enum mail_fetch_field cache = 0; fields = mail_cache_register_get_list(box->cache, pool_datastack_create(), &count); for (i = 0; i < count; i++) { const char *name = fields[i].name; if (strncmp(name, "hdr.", 4) == 0 || strcmp(name, "date.sent") == 0 || strcmp(name, "imap.envelope") == 0) cache |= MAIL_FETCH_STREAM_HEADER; else if (strcmp(name, "mime.parts") == 0 || strcmp(name, "binary.parts") == 0 || strcmp(name, "imap.body") == 0 || strcmp(name, "imap.bodystructure") == 0 || strcmp(name, "body.snippet") == 0) cache |= MAIL_FETCH_STREAM_BODY; else if (strcmp(name, "date.received") == 0) cache |= MAIL_FETCH_RECEIVED_DATE; else if (strcmp(name, "date.save") == 0) cache |= MAIL_FETCH_SAVE_DATE; else if (strcmp(name, "size.virtual") == 0) cache |= MAIL_FETCH_VIRTUAL_SIZE; else if (strcmp(name, "size.physical") == 0) cache |= MAIL_FETCH_PHYSICAL_SIZE; else if (strcmp(name, "pop3.uidl") == 0) cache |= MAIL_FETCH_UIDL_BACKEND; else if (strcmp(name, "pop3.order") == 0) cache |= MAIL_FETCH_POP3_ORDER; else if (strcmp(name, "guid") == 0) cache |= MAIL_FETCH_GUID; else if (strcmp(name, "flags") == 0) { /* just ignore for now at least.. */ } else if (box->storage->set->mail_debug) i_debug("Ignoring unknown cache field: %s", name); } metadata_r->precache_fields = cache; } static int index_mailbox_get_first_save_date(struct mailbox *box, struct mailbox_metadata *metadata_r) { const struct mail_index_header *hdr; struct mailbox_transaction_context *t; struct mail *mail; uint32_t seq; int ret = -1; hdr = mail_index_get_header(box->view); if (hdr->messages_count == 0) { metadata_r->first_save_date = (time_t)-1; return 0; } t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, 0, NULL); for (seq = 1; seq <= hdr->messages_count; seq++) { mail_set_seq(mail, seq); if (mail_get_save_date(mail, &metadata_r->first_save_date) == 0) { ret = 0; break; } if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) { /* failed */ break; } } mail_free(&mail); (void)mailbox_transaction_commit(&t); if (seq > hdr->messages_count) { /* all messages were expunged after all */ metadata_r->first_save_date = (time_t)-1; return 0; } return ret; } int index_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { /* handle items that don't require opening the mailbox */ if ((items & MAILBOX_METADATA_BACKEND_NAMESPACE) != 0) { metadata_r->backend_ns_prefix = ""; metadata_r->backend_ns_type = mailbox_list_get_namespace(box->list)->type; items &= ~MAILBOX_METADATA_BACKEND_NAMESPACE; } if (items == 0) return 0; /* handle items that require opening the mailbox */ if (!box->opened) { if (mailbox_open(box) < 0) return -1; } if (!box->synced && (items & MAILBOX_METADATA_SYNC_ITEMS) != 0) { if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) return -1; } if ((items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) { if (index_mailbox_get_virtual_size(box, metadata_r) < 0) return -1; } if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0) { if (index_mailbox_get_physical_size(box, metadata_r) < 0) return -1; } if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) { if (index_mailbox_get_first_save_date(box, metadata_r) < 0) return -1; } if ((items & MAILBOX_METADATA_CACHE_FIELDS) != 0) get_metadata_cache_fields(box, metadata_r); if ((items & MAILBOX_METADATA_PRECACHE_FIELDS) != 0) get_metadata_precache_fields(box, metadata_r); return 0; } dovecot-2.2.33.2/src/lib-storage/mailbox-attribute.h0000644000175000017500000002672713165463624017161 00000000000000#ifndef MAILBOX_ATTRIBUTE_H #define MAILBOX_ATTRIBUTE_H /* * Attribute Handling in Dovecot * ============================= * * What IMAP & doveadm users see gets translated into one of several things * depending on if we're operating on a mailbox or on server metadata ("" * mailbox in IMAP parlance). Consider these examples: * * /private/foo * /shared/foo * * Here "foo" can be any RFC defined attribute name, or a vendor-prefixed * non-standard name. (Our vendor prefix is "vendor/vendor.dovecot".) * * In all cases, the "/private" and "/shared" user visible prefixes get * replaced by priv/ and shared/, respectively. (Here, * is the GUID of the mailbox with which the attribute is associated.) This * way, attributes for all mailboxes can be stored in a single dict. For * example, the above examples would map to: * * priv//foo * shared//foo * * More concrete examples: * * /private/comment * /private/vendor/vendor.dovecot/abc * * turn into: * * priv//comment * priv//vendor/vendor.dovecot/abc * * Server attributes, that is attributes not associated with a mailbox, are * stored in the INBOX mailbox with a special prefix - * vendor/vendor.dovecot/pvt/server. For example, the server attribute * /private/comment gets mapped to: * * priv//vendor/vendor.dovecot/pvt/server/comment * * This means that if we set a /private/comment server attribute as well as * /private/comment INBOX mailbox attribute, we'll see the following paths * used in the dict: * * priv//comment <- mailbox attr * priv//vendor/vendor.dovecot/pvt/server/comment <- server attr * * The case of vendor specific server attributes is a bit confusing, but * consistent. For example, this server attribute: * * /private/vendor/vendor.dovecot/abc * * It will get mapped to: * * priv//vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/abc * | | | | * \----- server attr prefix -----/ \-- server attr name ---/ * * * Internal Attributes * ------------------- * * The final aspect of attribute handling in Dovecot are the so called * "internal attributes". * * The easiest way to explain internal attributes is to summarize attributes * in general. Attributes are just pairs that are stored in a * dict. The key is mangled according to the above rules before passed to * the dict code. That is, the key already encodes whether the attribute is * private or shared, the GUID of the mailbox (or of INBOX for server * attributes), etc. There is no processing of the value. It is stored and * returned to clients verbatim. * * Internal attributes, on the other hand, are special cased attributes. * That is, the code contains a list of specific attribute names and how to * handle them. Each internal attribute is defined by a struct * mailbox_attribute_internal. It contains the pre-parsed name of the * attribute (type, key, and flags), and how to handle getting and setting * of the attribute (rank, get, and set). * * The values for these attributes may come from two places - from the * attributes dict, or from the get function pointer. Which source to use * is identified by the rank (MAIL_ATTRIBUTE_INTERNAL_RANK_*). * * * Access * ------ * * In general, a user (IMAP or doveadm) can access all attributes for a * mailbox. The one exception are attributes under: * * /private/vendor/vendor.dovecot/pvt * /shared/vendor/vendor.dovecot/pvt * * Which as you may recall map to: * * priv//vendor/vendor.dovecot/pvt * shared//vendor/vendor.dovecot/pvt * * These are deemed internal to Dovecot, and therefore of no concern to the * user. * * Server attributes have a similar restriction. That is, attributes * beginning with the following are not accessible: * * /private/vendor/vendor.dovecot/pvt * /shared/vendor/vendor.dovecot/pvt * * However since server attributes are stored under the INBOX mailbox, these * paths map to: * * priv//vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt * shared//vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt * * As a result, the code performs access checks via the * MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE() macro to make sure that the * user is allowed access to the attribute. * * * Nicknames * --------- * * Since every path stored in the dict begins with priv/ or * shared/, these prefixes are often omitted. This also matches the * internal implementation where the priv/ or shared/ prefix is specified * using an enum, and only the path after the GUID is handled as a string. * For example: * * priv//vendor/vendor.dovecot/pvt/server/foo * * would be refered to as: * * vendor/vendor.dovecot/pvt/server/foo * * Since some of the generated paths are very long, developers often use a * shorthand to refer to some of these paths. For example, * * pvt/server/pvt * * is really: * * vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt * * Which when fully specified with a type and INBOX's GUID would turn into * one of the following: * * priv//vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt * shared//vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt */ struct mailbox; struct mailbox_transaction_context; /* RFC 5464 specifies that this is vendor//. The registered vendor-tokens always begin with "vendor." so there's some redundancy.. */ #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT "vendor/vendor.dovecot/" /* Prefix used for attributes reserved for Dovecot's internal use. Normal users cannot access these in any way. */ #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT"pvt/" /* Server attributes are currently stored in INBOX under this private prefix. They're under the pvt/ prefix so they won't be listed as regular INBOX attributes, but unlike other pvt/ attributes it's actually possible to access these attributes as regular users. If INBOX is deleted, attributes under this prefix are preserved. */ #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"server/" /* User can get/set all non-pvt/ attributes and also pvt/server/ (but not pvt/server/pvt/) attributes. */ #define MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE(key) \ (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT, \ strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) != 0 || \ (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, \ strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0 && \ strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT, \ strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) != 0)) enum mail_attribute_type { MAIL_ATTRIBUTE_TYPE_PRIVATE, MAIL_ATTRIBUTE_TYPE_SHARED }; enum mail_attribute_value_flags { MAIL_ATTRIBUTE_VALUE_FLAG_READONLY = 0x01, MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS = 0x02 }; struct mail_attribute_value { /* mailbox_attribute_set() can set either value or value_stream. mailbox_attribute_get() returns only values, but mailbox_attribute_get_stream() may return either value or value_stream. The caller must unreference the returned streams. */ const char *value; struct istream *value_stream; /* Last time the attribute was changed (0 = unknown). This may be returned even for values that don't exist anymore. */ time_t last_change; enum mail_attribute_value_flags flags; }; /* * Internal attribute */ enum mail_attribute_internal_rank { /* The internal attribute serves only as a source for a default value when the normal mailbox attribute storage has no entry for this attribute. Otherwise it is ignored. The `set' function is called only as a notification, not with the intention to store the value. The value is always assigned to the normal mailbox attribute storage. */ MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT = 0, /* The internal attribute serves as the main source of the attribute value. If the `get' function returns 0, the normal mailbox attribute storage is attempted to obtain the value. The `set' function is called only as a notification, not with the intention to store the value. The value is assigned to the normal mailbox attribute storage. */ MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE, /* The value for the internal attribute is never read from the normal mailbox attribute storage. If the `set' function is NULL, the attribute is read-only. If it is not NULL it is used to assign the attribute value; it is not assigned to the normal mailbox attribute storage. */ MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY }; enum mail_attribute_internal_flags { /* Apply this attribute to the given key and its children. */ MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN = 0x01 }; struct mailbox_attribute_internal { enum mail_attribute_type type; const char *key; /* relative to the GUID, e.g., "comment" */ enum mail_attribute_internal_rank rank; enum mail_attribute_internal_flags flags; /* Get the value of this internal attribute */ int (*get)(struct mailbox_transaction_context *t, const char *key, struct mail_attribute_value *value_r); /* Set the value of this internal attribute */ int (*set)(struct mailbox_transaction_context *t, const char *key, const struct mail_attribute_value *value); }; void mailbox_attribute_register_internal( const struct mailbox_attribute_internal *iattr); void mailbox_attribute_register_internals( const struct mailbox_attribute_internal *iattrs, unsigned int count); void mailbox_attribute_unregister_internal( const struct mailbox_attribute_internal *iattr); void mailbox_attribute_unregister_internals( const struct mailbox_attribute_internal *iattrs, unsigned int count); /* * Attribute API */ /* Set mailbox attribute key to value. The key should be compatible with IMAP METADATA, so for Dovecot-specific keys use MAILBOX_ATTRIBUTE_PREFIX_DOVECOT. */ int mailbox_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value); /* Delete mailbox attribute key. This is just a wrapper to mailbox_attribute_set() with value->value=NULL. */ int mailbox_attribute_unset(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key); /* Returns value for mailbox attribute key. Returns 1 if value was returned, 0 if value wasn't found (set to NULL), -1 if error */ int mailbox_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r); /* Same as mailbox_attribute_get(), but the returned value may be either an input stream or a string. */ int mailbox_attribute_get_stream(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r); /* Iterate through mailbox attributes of the given type. The prefix can be used to restrict what attributes are returned. */ struct mailbox_attribute_iter * mailbox_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix); /* Returns the attribute key or NULL if there are no more attributes. */ const char *mailbox_attribute_iter_next(struct mailbox_attribute_iter *iter); int mailbox_attribute_iter_deinit(struct mailbox_attribute_iter **iter); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-list-iter.h0000644000175000017500000000730213123174404017043 00000000000000#ifndef MAILBOX_LIST_ITER_H #define MAILBOX_LIST_ITER_H #include "mail-namespace.h" #include "mailbox-list.h" enum mailbox_list_iter_flags { /* Ignore index file and ACLs (used by ACL plugin internally) */ MAILBOX_LIST_ITER_RAW_LIST = 0x000001, /* Don't list autocreated mailboxes (e.g. INBOX) unless they physically exist */ MAILBOX_LIST_ITER_NO_AUTO_BOXES = 0x000004, /* Skip all kinds of mailbox aliases. This typically includes symlinks that point to the same directory. Also when iterating with mailbox_list_iter_init_namespaces() skip namespaces that have alias_for set. */ MAILBOX_LIST_ITER_SKIP_ALIASES = 0x000008, /* For mailbox_list_iter_init_namespaces(): '*' in a pattern doesn't match beyond namespace boundary (e.g. "foo*" or "*o" doesn't match "foo." namespace's mailboxes, but "*.*" does). also '%' can't match namespace prefixes, if there exists a parent namespace whose children it matches. */ MAILBOX_LIST_ITER_STAR_WITHIN_NS = 0x000010, /* List only subscribed mailboxes */ MAILBOX_LIST_ITER_SELECT_SUBSCRIBED = 0x000100, /* Return MAILBOX_CHILD_* if mailbox's children match selection criteria, even if the mailbox itself wouldn't match. */ MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH = 0x000200, /* Return only mailboxes that have special use flags */ MAILBOX_LIST_ITER_SELECT_SPECIALUSE = 0x000400, /* Don't return any flags unless it can be done without cost */ MAILBOX_LIST_ITER_RETURN_NO_FLAGS = 0x001000, /* Return MAILBOX_SUBSCRIBED flag */ MAILBOX_LIST_ITER_RETURN_SUBSCRIBED = 0x002000, /* Return children flags */ MAILBOX_LIST_ITER_RETURN_CHILDREN = 0x004000, /* Return IMAP special use flags */ MAILBOX_LIST_ITER_RETURN_SPECIALUSE = 0x008000 }; struct mailbox_info { const char *vname; const char *special_use; enum mailbox_info_flags flags; struct mail_namespace *ns; }; /* Returns a single pattern from given reference and pattern. */ const char *mailbox_list_join_refpattern(struct mailbox_list *list, const char *ref, const char *pattern); /* Initialize new mailbox list request. Pattern may contain '%' and '*' wildcards as defined by RFC-3501. */ struct mailbox_list_iterate_context * mailbox_list_iter_init(struct mailbox_list *list, const char *pattern, enum mailbox_list_iter_flags flags); /* Like mailbox_list_iter_init(), but support multiple patterns. Patterns is a NULL-terminated list of strings. It must contain at least one pattern. */ struct mailbox_list_iterate_context * mailbox_list_iter_init_multiple(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags); /* List mailbox_list_iter_init_multiple(), but list mailboxes from all the specified namespaces. If it fails, the error message is set to the first namespaces->list. */ struct mailbox_list_iterate_context * mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, const char *const *patterns, enum mail_namespace_type type_mask, enum mailbox_list_iter_flags flags); /* Get next mailbox. Returns the mailbox name */ const struct mailbox_info * mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx); /* Deinitialize mailbox list request. Returns -1 if some error occurred while listing. The error string can be looked up with mailbox_list_get_last_error(). */ int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **ctx); /* List one mailbox. Returns 1 if info returned, 0 if mailbox doesn't exist, -1 if error. */ int mailbox_list_mailbox(struct mailbox_list *list, const char *name, enum mailbox_info_flags *flags_r); /* Returns 1 if mailbox has children, 0 if not, -1 if error. */ int mailbox_has_children(struct mailbox_list *list, const char *name); #endif dovecot-2.2.33.2/src/lib-storage/mail-error.h0000644000175000017500000000524313165463624015564 00000000000000#ifndef MAIL_ERROR_H #define MAIL_ERROR_H /* Some error strings that should be used everywhere to avoid permissions checks from revealing mailbox's existence */ #define MAIL_ERRSTR_MAILBOX_NOT_FOUND "Mailbox doesn't exist: %s" #define MAIL_ERRSTR_NO_PERMISSION "Permission denied" /* And just for making error strings consistent: */ #define MAIL_ERRSTR_NO_QUOTA "Not enough disk quota" /* FIXME: Obsolete - remove for v2.3 */ #define MAIL_ERRSTR_NO_SPACE MAIL_ERRSTR_NO_QUOTA #define MAIL_ERRSTR_LOCK_TIMEOUT "Timeout while waiting for lock" /* Message to show to users when critical error occurs */ #define MAIL_ERRSTR_CRITICAL_MSG \ "Internal error occurred. Refer to server log for more information." #define MAIL_ERRSTR_CRITICAL_MSG_STAMP \ MAIL_ERRSTR_CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" #define T_MAIL_ERR_MAILBOX_NOT_FOUND(name) \ t_strdup_printf(MAIL_ERRSTR_MAILBOX_NOT_FOUND, name) enum mail_error { MAIL_ERROR_NONE = 0, /* Temporary internal error */ MAIL_ERROR_TEMP, /* Temporary failure because a subsystem is down */ MAIL_ERROR_UNAVAILABLE, /* It's not possible to do the wanted operation */ MAIL_ERROR_NOTPOSSIBLE, /* Invalid parameters (eg. mailbox name not valid) */ MAIL_ERROR_PARAMS, /* No permission to do the request */ MAIL_ERROR_PERM, /* Out of disk quota for user */ MAIL_ERROR_NOQUOTA, /* Item (e.g. mailbox) doesn't exist or it's not visible to us */ MAIL_ERROR_NOTFOUND, /* Item (e.g. mailbox) already exists */ MAIL_ERROR_EXISTS, /* Tried to access an expunged message */ MAIL_ERROR_EXPUNGED, /* Operation cannot be done because another session prevents it (e.g. lock timeout) */ MAIL_ERROR_INUSE, /* Can't do the requested data conversion (e.g. IMAP BINARY's UNKNOWN-CTE code) */ MAIL_ERROR_CONVERSION, /* Can't do the requested data conversion because the original data isn't valid. */ MAIL_ERROR_INVALIDDATA, /* Operation ran against some kind of a limit. */ MAIL_ERROR_LIMIT, /* Operation couldn't be finished as efficiently as required by mail.lookup_abort. */ MAIL_ERROR_LOOKUP_ABORTED, /* FIXME: Obsolete - remove in v2.3 */ MAIL_ERROR_NOSPACE = MAIL_ERROR_NOQUOTA }; /* Convert errno to mail_error and an error string. Returns TRUE if successful, FALSE if we couldn't handle the errno. */ bool mail_error_from_errno(enum mail_error *error_r, const char **error_string_r); /* Build a helpful error message for a failed EACCES syscall. */ const char *mail_error_eacces_msg(const char *func, const char *path); /* Build a helpful error message for a failed EACCES syscall that tried to write to directory (create, rename, etc). */ const char *mail_error_create_eacces_msg(const char *func, const char *path); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-recent-flags.c0000644000175000017500000000525613123174404017502 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage-private.h" #include "mailbox-recent-flags.h" void mailbox_recent_flags_set_uid(struct mailbox *box, uint32_t uid) { if (uid <= box->recent_flags_prev_uid) { if (seq_range_exists(&box->recent_flags, uid)) return; mail_storage_set_critical(box->storage, "Recent flags state corrupted for mailbox %s", box->vname); array_clear(&box->recent_flags); box->recent_flags_count = 0; } mailbox_recent_flags_set_uid_forced(box, uid); } void mailbox_recent_flags_set_uid_forced(struct mailbox *box, uint32_t uid) { box->recent_flags_prev_uid = uid; if (!mailbox_recent_flags_have_uid(box, uid)) { seq_range_array_add_with_init(&box->recent_flags, 64, uid); box->recent_flags_count++; } } void mailbox_recent_flags_set_seqs(struct mailbox *box, struct mail_index_view *view, uint32_t seq1, uint32_t seq2) { uint32_t uid; for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(view, seq1, &uid); mailbox_recent_flags_set_uid(box, uid); } } bool mailbox_recent_flags_have_uid(struct mailbox *box, uint32_t uid) { return array_is_created(&box->recent_flags) && seq_range_exists(&box->recent_flags, uid); } void mailbox_recent_flags_reset(struct mailbox *box) { if (array_is_created(&box->recent_flags)) array_clear(&box->recent_flags); box->recent_flags_count = 0; box->recent_flags_prev_uid = 0; } unsigned int mailbox_recent_flags_count(struct mailbox *box) { const struct mail_index_header *hdr; const struct seq_range *range; unsigned int i, count, recent_count; if (!array_is_created(&box->recent_flags)) return 0; hdr = mail_index_get_header(box->view); recent_count = box->recent_flags_count; range = array_get(&box->recent_flags, &count); for (i = count; i > 0; ) { i--; if (range[i].seq2 < hdr->next_uid) break; if (range[i].seq1 >= hdr->next_uid) { /* completely invisible to this view */ recent_count -= range[i].seq2 - range[i].seq1 + 1; } else { /* partially invisible */ recent_count -= range[i].seq2 - hdr->next_uid + 1; break; } } return recent_count; } void mailbox_recent_flags_expunge_seqs(struct mailbox *box, uint32_t seq1, uint32_t seq2) { uint32_t uid; if (!array_is_created(&box->recent_flags)) return; for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(box->view, seq1, &uid); if (seq_range_array_remove(&box->recent_flags, uid)) box->recent_flags_count--; } } void mailbox_recent_flags_expunge_uid(struct mailbox *box, uint32_t uid) { if (array_is_created(&box->recent_flags)) { if (seq_range_array_remove(&box->recent_flags, uid)) box->recent_flags_count--; } } dovecot-2.2.33.2/src/lib-storage/test-mail-search-args-imap.c0000644000175000017500000001500713165463624020525 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "test-common.h" #include "mail-search-build.h" #include "mail-search-parser.h" #include "mail-search.h" #define CURRENT_UNIX_TIME 1000000 static struct { const char *input, *output; } tests[] = { { "ALL", NULL }, { "1,5:6,10:15", NULL }, { "UID 1,5:6,10:15", NULL }, { "ANSWERED FLAGGED DELETED SEEN DRAFT RECENT", "ANSWERED FLAGGED DELETED SEEN DRAFT RECENT" }, { "KEYWORD foo KEYWORD bar", NULL }, { "BEFORE 20-May-2015", "BEFORE \"20-May-2015\"" }, { "ON 20-May-2015", "ON \"20-May-2015\"" }, { "SINCE 20-May-2015", "SINCE \"20-May-2015\"" }, { "SENTBEFORE 20-May-2015", "SENTBEFORE \"20-May-2015\"" }, { "SENTON 20-May-2015", "SENTON \"20-May-2015\"" }, { "SENTSINCE 20-May-2015", "SENTSINCE \"20-May-2015\"" }, { "X-SAVEDBEFORE 20-May-2015", "X-SAVEDBEFORE \"20-May-2015\"" }, { "X-SAVEDON 20-May-2015", "X-SAVEDON \"20-May-2015\"" }, { "X-SAVEDSINCE 20-May-2015", "X-SAVEDSINCE \"20-May-2015\"" }, { "OLDER 1", NULL }, { "OLDER 1000", NULL }, { "YOUNGER 1", NULL }, { "YOUNGER 1000", NULL }, { "SMALLER 0", NULL }, { "SMALLER 1", NULL }, { "SMALLER 4294967295", NULL }, { "LARGER 0", NULL }, { "LARGER 1", NULL }, { "LARGER 4294967295", NULL }, { "FROM foo", NULL }, { "TO foo", NULL }, { "CC foo", NULL }, { "BCC foo", NULL }, { "SUBJECT foo", NULL }, { "HEADER subjecT foo", "SUBJECT foo" }, { "HEADER subjecT2 foo", "HEADER SUBJECT2 foo" }, { "BODY foo", NULL }, { "TEXT foo", NULL }, { "MODSEQ 0", NULL }, { "MODSEQ 1", NULL }, { "MODSEQ 18446744073709551615", NULL }, { "MODSEQ /flags/keyword all 0", NULL }, { "MODSEQ /flags/\\Seen all 0", NULL }, { "MODSEQ /flags/\\Seen priv 0", NULL }, { "MODSEQ /flags/\\Seen shared 0", NULL }, { "INTHREAD REFERENCES seen", "INTHREAD REFERENCES (SEEN)" }, { "INTHREAD ORDEREDSUBJECT seen", "INTHREAD ORDEREDSUBJECT (SEEN)" }, { "INTHREAD REFS seen", "INTHREAD REFS (SEEN)" }, { "INTHREAD REFS ( OR text foo OR keyword bar seen )", "INTHREAD REFS ((OR TEXT foo OR KEYWORD bar SEEN))" }, { "X-GUID foo", NULL }, { "X-MAILBOX foo", NULL }, { "X-REAL-UID 1,5:6,10:15", NULL }, /* SEARCH=X-MIMEPART */ { "MIMEPART CHILD EXISTS", NULL }, { "MIMEPART ( CHILD EXISTS )", "MIMEPART CHILD EXISTS" }, { "MIMEPART ( CHILD EXISTS HEADER Comment Hopla )", "MIMEPART (CHILD EXISTS HEADER COMMENT Hopla)" }, { "MIMEPART ( DESCRIPTION Frop ENCODING base64 )", "MIMEPART (DESCRIPTION Frop ENCODING base64)" }, { "MIMEPART ( DISPOSITION TYPE attachment " "DISPOSITION PARAM FILENAME frop.txt )", "MIMEPART (DISPOSITION TYPE attachment " "DISPOSITION PARAM FILENAME frop.txt)" }, { "MIMEPART ( ID LANGUAGE en )", "MIMEPART (ID LANGUAGE en)" }, { "MIMEPART ( LOCATION http://www.dovecot.org )", "MIMEPART LOCATION http://www.dovecot.org" }, { "MIMEPART NOT MD5 373def35afde6378efd6172dfeadfd", NULL }, { "MIMEPART OR PARAM charset utf-8 TYPE text", "MIMEPART OR PARAM CHARSET utf-8 TYPE text" }, { "MIMEPART ( OR SIZE LARGER 25 SIZE SMALLER 1023 )", "MIMEPART OR SIZE LARGER 25 SIZE SMALLER 1023" }, { "MIMEPART ( TYPE video SUBTYPE mpeg )", "MIMEPART (TYPE video SUBTYPE mpeg)" }, { "( OR MIMEPART ( DEPTH 2 INDEX 1 ) MIMEPART ( DEPTH MAX 4 INDEX 3 ) )", "(OR MIMEPART (DEPTH 2 INDEX 1) MIMEPART (DEPTH MAX 4 INDEX 3))" }, { "MIMEPART FILENAME IS frop.txt", NULL }, { "MIMEPART FILENAME BEGINS frop", NULL }, { "MIMEPART FILENAME ENDS .txt", NULL }, { "MIMEPART FILENAME CONTAINS frop", NULL }, { "MIMEPART BODY frop MIMEPART TEXT frop", NULL }, { "MIMEPART ( CC appie BCC theo FROM leo REPLY-TO henk SENDER arie )", "MIMEPART (CC appie BCC theo FROM leo REPLY-TO henk SENDER arie)" }, { "MIMEPART ( MESSAGE-ID IN-REPLY-TO )", "MIMEPART (MESSAGE-ID IN-REPLY-TO )" }, { "MIMEPART ( SUBJECT Frop TO henkie SENTON 20-Feb-2017 )", "MIMEPART (SUBJECT Frop TO henkie SENTON \"20-Feb-2017\")" }, { "MIMEPART ( OR SENTBEFORE 20-May-2015 SENTSINCE 20-Feb-2017 )", "MIMEPART OR SENTBEFORE \"20-May-2015\" SENTSINCE \"20-Feb-2017\"" }, { "MIMEPART ( ID PARENT ID )", "MIMEPART (ID PARENT (ID ))" }, { "MIMEPART ( ID CHILD ( DESCRIPTION frop ID friep ) )", "MIMEPART (ID CHILD (DESCRIPTION frop ID friep))" }, { "MIMEPART CHILD EXISTS MIMEPART PARENT EXISTS", NULL }, }; static struct mail_search_arg test_failures[] = { { .type = SEARCH_MAILBOX }, { .type = SEARCH_MAILBOX_GUID }, { .type = SEARCH_BEFORE, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SENT, .time = 86400-1 } }, { .type = SEARCH_ON, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SENT, .time = 86400-1 } }, { .type = SEARCH_SINCE, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SENT, .time = 86400-1 } }, { .type = SEARCH_ON, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_RECEIVED, .time = 86400-1 } }, { .type = SEARCH_BEFORE, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SAVED, .time = 86400-1 } }, { .type = SEARCH_ON, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SAVED, .time = 86400-1 } }, { .type = SEARCH_SINCE, .value = { .date_type = MAIL_SEARCH_DATE_TYPE_SAVED, .time = 86400-1 } } }; static struct mail_search_args * test_build_search_args(const char *args) { struct mail_search_parser *parser; struct mail_search_args *sargs; const char *error, *charset = "UTF-8"; parser = mail_search_parser_init_cmdline(t_strsplit(args, " ")); if (mail_search_build(mail_search_register_get_imap(), parser, &charset, &sargs, &error) < 0) i_panic("%s", error); mail_search_parser_deinit(&parser); return sargs; } static void test_mail_search_args_imap(void) { struct mail_search_args *args; string_t *str = t_str_new(256); const char *output, *error; unsigned int i; ioloop_time = CURRENT_UNIX_TIME; /* YOUNGER/OLDER tests need this */ test_begin("mail search args imap"); for (i = 0; i < N_ELEMENTS(tests); i++) { args = test_build_search_args(tests[i].input); output = tests[i].output != NULL ? tests[i].output : tests[i].input; str_truncate(str, 0); test_assert_idx(mail_search_args_to_imap(str, args->args, &error), i); test_assert_idx(strcmp(str_c(str), output) == 0, i); mail_search_args_unref(&args); } for (i = 0; i < N_ELEMENTS(test_failures); i++) test_assert_idx(!mail_search_args_to_imap(str, &test_failures[i], &error), i); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_search_args_imap, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-storage/mail-search-register.h0000644000175000017500000000316713123174404017512 00000000000000#ifndef MAIL_SEARCH_REGISTER_H #define MAIL_SEARCH_REGISTER_H struct mail_search_arg; struct mail_search_build_context; struct mail_search_register_arg { const char *key; /* returns parsed arg or NULL if error. error message is set to ctx. */ struct mail_search_arg * (*build)(struct mail_search_build_context *ctx); }; typedef struct mail_search_arg * mail_search_register_fallback_t(struct mail_search_build_context *ctx, const char *key); struct mail_search_register *mail_search_register_init(void); void mail_search_register_deinit(struct mail_search_register **reg); void mail_search_register_add(struct mail_search_register *reg, const struct mail_search_register_arg *arg, unsigned int count); /* Register a fallback handler. It's responsible for giving also the "unknown key" error. */ void mail_search_register_fallback(struct mail_search_register *reg, mail_search_register_fallback_t *fallback); /* Return all registered args sorted. */ const struct mail_search_register_arg * mail_search_register_get(struct mail_search_register *reg, unsigned int *count_r); /* Find key's registered arg, or NULL if not found. */ const struct mail_search_register_arg * mail_search_register_find(struct mail_search_register *reg, const char *key); /* Get registered fallback arg. Returns FALSE if fallback hasn't been registered. */ bool mail_search_register_get_fallback(struct mail_search_register *reg, mail_search_register_fallback_t **fallback_r); struct mail_search_register *mail_search_register_get_imap(void); struct mail_search_register *mail_search_register_get_human(void); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-mime-register.h0000644000175000017500000000150713123174404020433 00000000000000#ifndef MAIL_SEARCH_MIME_REGISTER_H #define MAIL_SEARCH_MIME_REGISTER_H struct mail_search_mime_arg; struct mail_search_mime_build_context; struct mail_search_mime_register_arg { const char *key; /* returns parsed arg or NULL if error. error message is set to ctx->ctx. */ struct mail_search_mime_arg * (*build)(struct mail_search_mime_build_context *ctx); }; void mail_search_mime_register_deinit(void); void mail_search_mime_register_add( const struct mail_search_mime_register_arg *arg, unsigned int count); /* Return all registered args sorted. */ const struct mail_search_mime_register_arg * mail_search_mime_register_get(unsigned int *count_r); /* Find key's registered arg, or NULL if not found. */ const struct mail_search_mime_register_arg * mail_search_mime_register_find(const char *key); #endif dovecot-2.2.33.2/src/lib-storage/mailbox-list.h0000644000175000017500000003041613147010712016101 00000000000000#ifndef MAILBOX_LIST_H #define MAILBOX_LIST_H #include "mail-error.h" #ifdef PATH_MAX # define MAILBOX_LIST_NAME_MAX_LENGTH PATH_MAX #else # define MAILBOX_LIST_NAME_MAX_LENGTH 4096 #endif struct fs; struct mail_namespace; struct mail_storage; struct mailbox_list; enum mailbox_list_properties { /* maildir_name must always be empty */ MAILBOX_LIST_PROP_NO_MAILDIR_NAME = 0x01, /* alt directories not supported */ MAILBOX_LIST_PROP_NO_ALT_DIR = 0x02, /* no support for \noselect directories, only mailboxes */ MAILBOX_LIST_PROP_NO_NOSELECT = 0x04, /* mail root directory isn't required */ MAILBOX_LIST_PROP_NO_ROOT = 0x08, /* Automatically create mailbox directories when needed. Normally it's assumed that if a mailbox directory doesn't exist, the mailbox doesn't exist either. */ MAILBOX_LIST_PROP_AUTOCREATE_DIRS = 0x10 }; enum mailbox_list_flags { /* Mailboxes are files, not directories. */ MAILBOX_LIST_FLAG_MAILBOX_FILES = 0x01, /* Namespace already has a mailbox list, don't assign this mailbox list to it. */ MAILBOX_LIST_FLAG_SECONDARY = 0x02, /* There are no mail files, only index and/or control files. */ MAILBOX_LIST_FLAG_NO_MAIL_FILES = 0x04, /* LAYOUT=index: Don't delete any files in delete_mailbox(). */ MAILBOX_LIST_FLAG_NO_DELETES = 0x08 }; enum mailbox_info_flags { MAILBOX_NOSELECT = 0x001, MAILBOX_NONEXISTENT = 0x002, MAILBOX_CHILDREN = 0x004, MAILBOX_NOCHILDREN = 0x008, MAILBOX_NOINFERIORS = 0x010, MAILBOX_MARKED = 0x020, MAILBOX_UNMARKED = 0x040, MAILBOX_SUBSCRIBED = 0x080, MAILBOX_CHILD_SUBSCRIBED = 0x100, MAILBOX_CHILD_SPECIALUSE = 0x200, /* Internally used by lib-storage, use mailbox_info.special_use to actually access these: */ MAILBOX_SPECIALUSE_ALL = 0x00010000, MAILBOX_SPECIALUSE_ARCHIVE = 0x00020000, MAILBOX_SPECIALUSE_DRAFTS = 0x00040000, MAILBOX_SPECIALUSE_FLAGGED = 0x00080000, MAILBOX_SPECIALUSE_JUNK = 0x00100000, MAILBOX_SPECIALUSE_SENT = 0x00200000, MAILBOX_SPECIALUSE_TRASH = 0x00400000, MAILBOX_SPECIALUSE_IMPORTANT = 0x00800000, #define MAILBOX_SPECIALUSE_MASK 0x00ff0000 /* Internally used by lib-storage: */ MAILBOX_SELECT = 0x20000000, MAILBOX_MATCHED = 0x40000000 }; enum mailbox_list_path_type { /* Return directory's path (eg. ~/dbox/INBOX) */ MAILBOX_LIST_PATH_TYPE_DIR, MAILBOX_LIST_PATH_TYPE_ALT_DIR, /* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */ MAILBOX_LIST_PATH_TYPE_MAILBOX, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, /* Return control directory */ MAILBOX_LIST_PATH_TYPE_CONTROL, /* Return index directory ("" for in-memory) */ MAILBOX_LIST_PATH_TYPE_INDEX, /* Return the private index directory (NULL if none) */ MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE, /* Return mailbox list index directory (usually same as MAILBOX_LIST_PATH_TYPE_INDEX) */ MAILBOX_LIST_PATH_TYPE_LIST_INDEX, }; enum mailbox_list_file_type { MAILBOX_LIST_FILE_TYPE_UNKNOWN = 0, MAILBOX_LIST_FILE_TYPE_FILE, MAILBOX_LIST_FILE_TYPE_DIR, MAILBOX_LIST_FILE_TYPE_SYMLINK, MAILBOX_LIST_FILE_TYPE_OTHER }; struct mailbox_list_settings { const char *layout; /* FIXME: shouldn't be here */ const char *root_dir; const char *index_dir; const char *index_pvt_dir; const char *control_dir; const char *alt_dir; /* FIXME: dbox-specific.. */ /* Backend-local directory where volatile data, such as lock files, can be temporarily created. This setting allows specifying a separate directory for them to reduce disk I/O on the real storage. The volatile_dir can point to an in-memory filesystem. */ const char *volatile_dir; const char *inbox_path; const char *subscription_fname; const char *list_index_fname; /* Mailbox list index directory. NULL defaults to index directory. The path may be relative to the index directory. */ const char *list_index_dir; /* If non-empty, it means that mails exist in a maildir_name subdirectory. eg. if you have a directory containing directories: mail/ mail/foo/ mail/foo/Maildir If mailbox_name is empty, you have mailboxes "mail", "mail/foo" and "mail/foo/Maildir". If mailbox_name is "Maildir", you have a non-selectable mailbox "mail" and a selectable mailbox "mail/foo". */ const char *maildir_name; /* if set, store mailboxes under root_dir/mailbox_dir_name/. this setting contains either "" or "dir/". */ const char *mailbox_dir_name; /* Encode "bad" characters in mailbox names as */ char escape_char; /* If mailbox name can't be changed reversibly to UTF-8 and back, encode the problematic parts using in the user-visible UTF-8 name. The broken_char itself also has to be encoded the same way. */ char broken_char; /* Use UTF-8 mailbox names on filesystem instead of mUTF-7 */ bool utf8; /* Don't check/create the alt-dir symlink. */ bool alt_dir_nocheck; /* Use maildir_name also for index/control directories. This should have been the default since the beginning, but for backwards compatibility it had to be made an option. */ bool index_control_use_maildir_name; /* Perform mailbox iteration using the index directory instead of the mail root directory. This can be helpful if the indexes are on a faster storage. This could perhaps be made the default at some point, but for now since it's less tested it's optional. */ bool iter_from_index_dir; /* Avoid creating or listing \NoSelect mailboxes. */ bool no_noselect; }; struct mailbox_permissions { /* The actual uid/gid of the mailbox */ uid_t file_uid; gid_t file_gid; /* mode and GID to use for newly created files/dirs. (gid_t)-1 is used if the default GID can be used. */ mode_t file_create_mode, dir_create_mode; gid_t file_create_gid; /* origin (e.g. path) where the file_create_gid was got from */ const char *file_create_gid_origin; bool gid_origin_is_mailbox_path; bool mail_index_permissions_set; }; /* register all drivers */ void mailbox_list_register_all(void); void mailbox_list_register(const struct mailbox_list *list); void mailbox_list_unregister(const struct mailbox_list *list); const struct mailbox_list * mailbox_list_find_class(const char *driver); /* Returns 0 if ok, -1 if driver was unknown. */ int mailbox_list_create(const char *driver, struct mail_namespace *ns, const struct mailbox_list_settings *set, enum mailbox_list_flags flags, struct mailbox_list **list_r, const char **error_r); void mailbox_list_destroy(struct mailbox_list **list); const char * mailbox_list_get_driver_name(const struct mailbox_list *list) ATTR_PURE; const struct mailbox_list_settings * mailbox_list_get_settings(const struct mailbox_list *list) ATTR_PURE; enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list) ATTR_PURE; struct mail_namespace * mailbox_list_get_namespace(const struct mailbox_list *list) ATTR_PURE; struct mail_user * mailbox_list_get_user(const struct mailbox_list *list) ATTR_PURE; int mailbox_list_get_storage(struct mailbox_list **list, const char *vname, struct mail_storage **storage_r); void mailbox_list_get_default_storage(struct mailbox_list *list, struct mail_storage **storage); char mailbox_list_get_hierarchy_sep(struct mailbox_list *list); /* Returns the mode and GID that should be used when creating new files and directories to the specified mailbox. (gid_t)-1 is returned if it's not necessary to change the default gid. */ void mailbox_list_get_permissions(struct mailbox_list *list, const char *name, struct mailbox_permissions *permissions_r); /* Like mailbox_list_get_permissions(), but for creating files/dirs to the mail root directory (or even the root dir itself). */ void mailbox_list_get_root_permissions(struct mailbox_list *list, struct mailbox_permissions *permissions_r); /* mkdir() a root directory of given type with proper permissions. The path can be either the root itself or point to a directory under the root. */ int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type); /* Like mailbox_list_mkdir_root(), but don't log an error if it fails. */ int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type, const char **error_r); /* Call mailbox_list_mkdir_root() for index, unless the index root is the same as mailbox root. Returns 1 if ok, 0 if there are no indexes, -1 if error. Calling this multiple times does the check only once. */ int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list); /* Like mailbox_list_mkdir_missing_index_root(), but for mailbox list index root. */ int mailbox_list_mkdir_missing_list_index_root(struct mailbox_list *list); /* Returns TRUE if name is ok, FALSE if it can't be safely passed to mailbox_list_*() functions */ bool mailbox_list_is_valid_name(struct mailbox_list *list, const char *name, const char **error_r); const char *mailbox_list_get_storage_name(struct mailbox_list *list, const char *vname); const char *mailbox_list_get_vname(struct mailbox_list *list, const char *name); /* Get path to specified type of files in mailbox. Returns -1 if an error occurred (e.g. mailbox no longer exists), 0 if there are no files of this type (in-memory index, no alt dir, storage with no files), 1 if path was returned successfully. The path is set to NULL when returning -1/0. */ int mailbox_list_get_path(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type, const char **path_r); /* Get path to the root directory for files of specified type. Returns TRUE if path was returned, FALSE if there are no files of this type. */ bool mailbox_list_get_root_path(struct mailbox_list *list, enum mailbox_list_path_type type, const char **path_r); /* Like mailbox_list_get_root_path(), but assume that the root directory exists (assert crash if not) */ const char *mailbox_list_get_root_forced(struct mailbox_list *list, enum mailbox_list_path_type type); /* Returns mailbox's change log, or NULL if it doesn't have one. */ struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list); /* Specify timestamp to use when writing mailbox changes to changelog. The same timestamp is used until stamp is set to (time_t)-1, after which current time is used */ void mailbox_list_set_changelog_timestamp(struct mailbox_list *list, time_t stamp); /* Returns a prefix that temporary files should use without conflicting with the namespace. */ const char *mailbox_list_get_temp_prefix(struct mailbox_list *list); /* Returns prefix that's common to all get_temp_prefix() calls. Typically this returns either "temp." or ".temp.". */ const char *mailbox_list_get_global_temp_prefix(struct mailbox_list *list); /* Subscribe/unsubscribe mailbox. There should be no error when subscribing to already subscribed mailbox. Subscribing to unexisting mailboxes is optional. */ int mailbox_list_set_subscribed(struct mailbox_list *list, const char *name, bool set); /* Delete a non-selectable mailbox. Fail if the mailbox is selectable. */ int mailbox_list_delete_dir(struct mailbox_list *list, const char *name); /* Delete a symlinked mailbox. Fail if the mailbox isn't a symlink. */ int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name); /* Returns the error message of last occurred error. */ const char * ATTR_NOWARN_UNUSED_RESULT mailbox_list_get_last_error(struct mailbox_list *list, enum mail_error *error_r); /* Wrapper for mailbox_list_get_last_error() */ enum mail_error mailbox_list_get_last_mail_error(struct mailbox_list *list); const char * ATTR_NOWARN_UNUSED_RESULT mailbox_list_get_last_internal_error(struct mailbox_list *list, enum mail_error *error_r); /* Save the last error until it's popped. This is useful for cases where the list operation has already failed, but the cleanup code path changes the error to something else unwanted. */ void mailbox_list_last_error_push(struct mailbox_list *list); void mailbox_list_last_error_pop(struct mailbox_list *list); /* Create a fs based on the settings in the given mailbox_list. */ int mailbox_list_init_fs(struct mailbox_list *list, const char *driver, const char *args, const char *root_dir, struct fs **fs_r, const char **error_r); /* Return mailbox_list that was used to create the fs via mailbox_list_init_fs(). */ struct mailbox_list *mailbox_list_fs_get_list(struct fs *fs); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-args-imap.c0000644000175000017500000002113613165463624017550 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "utc-offset.h" #include "mail-index.h" #include "imap-date.h" #include "imap-util.h" #include "imap-quote.h" #include "mail-search.h" #include "mail-search-mime.h" #include static bool mail_search_subargs_to_imap(string_t *dest, const struct mail_search_arg *args, const char *prefix, const char **error_r) { const struct mail_search_arg *arg; if (prefix[0] == '\0') str_append_c(dest, '('); for (arg = args; arg != NULL; arg = arg->next) { if (arg->next != NULL) str_append(dest, prefix); if (!mail_search_arg_to_imap(dest, arg, error_r)) return FALSE; if (arg->next != NULL) str_append_c(dest, ' '); } if (prefix[0] == '\0') str_append_c(dest, ')'); return TRUE; } static bool mail_search_arg_to_imap_date(string_t *dest, const struct mail_search_arg *arg) { time_t timestamp = arg->value.time; const char *str; if ((arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) { struct tm *tm = localtime(×tamp); int tz_offset = utc_offset(tm, timestamp); timestamp -= tz_offset * 60; } if (!imap_to_date(timestamp, &str)) return FALSE; str_printfa(dest, " \"%s\"", str); return TRUE; } static void mail_search_arg_to_imap_flags(string_t *dest, enum mail_flags flags) { static const char *flag_names[] = { "ANSWERED", "FLAGGED", "DELETED", "SEEN", "DRAFT", "RECENT" }; i_assert(flags != 0); if (!bits_is_power_of_two(flags)) str_append_c(dest, '('); for (unsigned int i = 0; i < N_ELEMENTS(flag_names); i++) { if ((flags & (1 << i)) != 0) { str_append(dest, flag_names[i]); str_append_c(dest, ' '); } } str_truncate(dest, str_len(dest)-1); if (!bits_is_power_of_two(flags)) str_append_c(dest, ')'); } bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg, const char **error_r) { unsigned int start_pos; if (arg->match_not) str_append(dest, "NOT "); start_pos = str_len(dest); switch (arg->type) { case SEARCH_OR: if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "OR ", error_r)) return FALSE; break; case SEARCH_SUB: if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_ALL: str_append(dest, "ALL"); break; case SEARCH_SEQSET: imap_write_seq_range(dest, &arg->value.seqset); break; case SEARCH_UIDSET: str_append(dest, "UID "); imap_write_seq_range(dest, &arg->value.seqset); break; case SEARCH_FLAGS: mail_search_arg_to_imap_flags(dest, arg->value.flags); break; case SEARCH_KEYWORDS: { const struct mail_keywords *kw = arg->initialized.keywords; const ARRAY_TYPE(keywords) *names_arr; const char *const *namep; unsigned int i; if (kw == NULL || kw->count == 0) { /* uninitialized / invalid keyword */ str_printfa(dest, "KEYWORD %s", arg->value.str); break; } names_arr = mail_index_get_keywords(kw->index); if (kw->count > 1) str_append_c(dest, '('); for (i = 0; i < kw->count; i++) { namep = array_idx(names_arr, kw->idx[i]); if (i > 0) str_append_c(dest, ' '); str_printfa(dest, "KEYWORD %s", *namep); } if (kw->count > 1) str_append_c(dest, ')'); break; } case SEARCH_BEFORE: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTBEFORE"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "BEFORE"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDBEFORE"); break; } if (mail_search_arg_to_imap_date(dest, arg)) ; else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED || arg->value.time > ioloop_time) { *error_r = t_strdup_printf( "SEARCH_BEFORE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } else { str_truncate(dest, start_pos); str_printfa(dest, "OLDER %u", (unsigned int)(ioloop_time - arg->value.time + 1)); } break; case SEARCH_ON: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTON"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "ON"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDON"); break; } if (!mail_search_arg_to_imap_date(dest, arg)) { *error_r = t_strdup_printf( "SEARCH_ON can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } break; case SEARCH_SINCE: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTSINCE"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "SINCE"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDSINCE"); break; } if (mail_search_arg_to_imap_date(dest, arg)) ; else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED || arg->value.time >= ioloop_time) { *error_r = t_strdup_printf( "SEARCH_SINCE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } else { str_truncate(dest, start_pos); str_printfa(dest, "YOUNGER %u", (unsigned int)(ioloop_time - arg->value.time)); } break; case SEARCH_SMALLER: str_printfa(dest, "SMALLER %llu", (unsigned long long)arg->value.size); break; case SEARCH_LARGER: str_printfa(dest, "LARGER %llu", (unsigned long long)arg->value.size); break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (strcasecmp(arg->hdr_field_name, "From") == 0 || strcasecmp(arg->hdr_field_name, "To") == 0 || strcasecmp(arg->hdr_field_name, "Cc") == 0 || strcasecmp(arg->hdr_field_name, "Bcc") == 0 || strcasecmp(arg->hdr_field_name, "Subject") == 0) str_append(dest, t_str_ucase(arg->hdr_field_name)); else { str_append(dest, "HEADER "); imap_append_astring(dest, arg->hdr_field_name); } str_append_c(dest, ' '); imap_append_astring(dest, arg->value.str); break; case SEARCH_BODY: str_append(dest, "BODY "); imap_append_astring(dest, arg->value.str); break; case SEARCH_TEXT: str_append(dest, "TEXT "); imap_append_astring(dest, arg->value.str); break; /* extensions */ case SEARCH_MODSEQ: { bool extended_output = FALSE; str_append(dest, "MODSEQ "); if (arg->value.str != NULL) { str_printfa(dest, "/flags/%s", arg->value.str); extended_output = TRUE; } else if (arg->value.flags != 0) { str_append(dest, "/flags/"); imap_write_flags(dest, arg->value.flags, NULL); extended_output = TRUE; } if (extended_output) { str_append_c(dest, ' '); switch (arg->value.modseq->type) { case MAIL_SEARCH_MODSEQ_TYPE_ANY: str_append(dest, "all"); break; case MAIL_SEARCH_MODSEQ_TYPE_PRIVATE: str_append(dest, "priv"); break; case MAIL_SEARCH_MODSEQ_TYPE_SHARED: str_append(dest, "shared"); break; } str_append_c(dest, ' '); } str_printfa(dest, "%llu", (unsigned long long)arg->value.modseq->modseq); break; } case SEARCH_INTHREAD: str_append(dest, "INTHREAD "); imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type)); str_append_c(dest, ' '); if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_GUID: str_append(dest, "X-GUID "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MAILBOX: *error_r = "SEARCH_MAILBOX can't be written as IMAP"; return FALSE; case SEARCH_MAILBOX_GUID: *error_r = "SEARCH_MAILBOX_GUID can't be written as IMAP"; return FALSE; case SEARCH_MAILBOX_GLOB: str_append(dest, "X-MAILBOX "); imap_append_astring(dest, arg->value.str); break; case SEARCH_REAL_UID: str_append(dest, "X-REAL-UID "); imap_write_seq_range(dest, &arg->value.seqset); break; case SEARCH_MIMEPART: str_append(dest, "MIMEPART "); if (!mail_search_mime_part_to_imap(dest, arg->value.mime_part, error_r)) return FALSE; break; } return TRUE; } bool mail_search_args_to_imap(string_t *dest, const struct mail_search_arg *args, const char **error_r) { const struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { if (!mail_search_arg_to_imap(dest, arg, error_r)) return FALSE; if (arg->next != NULL) str_append_c(dest, ' '); } return TRUE; } dovecot-2.2.33.2/src/lib-storage/mailbox-list-notify.h0000644000175000017500000000434013147010712017404 00000000000000#ifndef MAILBOX_LIST_NOTIFY_H #define MAILBOX_LIST_NOTIFY_H #include "guid.h" struct mailbox_list_notify; enum mailbox_list_notify_event { MAILBOX_LIST_NOTIFY_CREATE = 0x01, MAILBOX_LIST_NOTIFY_DELETE = 0x02, MAILBOX_LIST_NOTIFY_RENAME = 0x04, MAILBOX_LIST_NOTIFY_SUBSCRIBE = 0x08, MAILBOX_LIST_NOTIFY_UNSUBSCRIBE = 0x10, MAILBOX_LIST_NOTIFY_UIDVALIDITY = 0x20, MAILBOX_LIST_NOTIFY_APPENDS = 0x40, MAILBOX_LIST_NOTIFY_EXPUNGES = 0x80, MAILBOX_LIST_NOTIFY_SEEN_CHANGES = 0x100, MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES = 0x200 #define MAILBOX_LIST_NOTIFY_STATUS \ (MAILBOX_LIST_NOTIFY_APPENDS | \ MAILBOX_LIST_NOTIFY_EXPUNGES | \ MAILBOX_LIST_NOTIFY_SEEN_CHANGES | \ MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) }; struct mailbox_list_notify { struct mailbox_list *list; enum mailbox_list_notify_event mask; }; struct mailbox_list_notify_rec { /* Each record can contain multiple events */ enum mailbox_list_notify_event events; /* For all events: */ const char *storage_name, *vname; /* For selectable mailboxes: */ guid_128_t guid; /* For rename: */ const char *old_vname; }; typedef void mailbox_list_notify_callback_t(void *); /* Monitor for specified changes in the mailbox list. Returns 0 if ok, -1 if notifications aren't supported. */ int mailbox_list_notify_init(struct mailbox_list *list, enum mailbox_list_notify_event mask, struct mailbox_list_notify **notify_r); void mailbox_list_notify_deinit(struct mailbox_list_notify **notify); /* Get the next change. Returns 1 if record was returned, 0 if there are no more changes currently or -1 if some error occurred */ int mailbox_list_notify_next(struct mailbox_list_notify *notify, const struct mailbox_list_notify_rec **rec_r); /* Call the specified callback when something changes. */ void mailbox_list_notify_wait(struct mailbox_list_notify *notify, mailbox_list_notify_callback_t *callback, void *context); #define mailbox_list_notify_wait(notify, callback, context) \ mailbox_list_notify_wait(notify + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (mailbox_list_notify_callback_t*)callback, context); /* Flush any delayed notifications now. */ void mailbox_list_notify_flush(struct mailbox_list_notify *notify); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-args-simplify.c0000644000175000017500000005304113123174404020443 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "mail-search.h" struct mail_search_simplify_prev_arg { struct { enum mail_search_arg_type type; enum mail_search_arg_flag search_flags; enum mail_search_date_type date_type; enum mail_flags mail_flags; bool match_not; bool fuzzy; } bin_mask; const char *hdr_field_name_mask; const char *str_mask; struct mail_search_arg *prev_arg; }; struct mail_search_simplify_ctx { pool_t pool; /* arg mask => prev_arg */ HASH_TABLE(struct mail_search_simplify_prev_arg *, struct mail_search_simplify_prev_arg *) prev_args; bool parent_and; bool removals; bool initialized; }; static int mail_search_simplify_prev_arg_cmp(const struct mail_search_simplify_prev_arg *arg1, const struct mail_search_simplify_prev_arg *arg2) { int ret; ret = memcmp(&arg1->bin_mask, &arg2->bin_mask, sizeof(arg1->bin_mask)); if (ret == 0) ret = null_strcmp(arg1->hdr_field_name_mask, arg2->hdr_field_name_mask); if (ret == 0) ret = null_strcmp(arg1->str_mask, arg2->str_mask); return ret; } static unsigned int mail_search_simplify_prev_arg_hash(const struct mail_search_simplify_prev_arg *arg) { unsigned int hash; hash = mem_hash(&arg->bin_mask, sizeof(arg->bin_mask)); if (arg->hdr_field_name_mask != NULL) hash ^= str_hash(arg->hdr_field_name_mask); if (arg->str_mask != NULL) hash ^= str_hash(arg->str_mask); return hash; } static void mail_search_arg_get_base_mask(const struct mail_search_arg *arg, struct mail_search_simplify_prev_arg *mask_r) { i_zero(mask_r); mask_r->bin_mask.type = arg->type; mask_r->bin_mask.fuzzy = arg->fuzzy; mask_r->bin_mask.search_flags = arg->value.search_flags; } static struct mail_search_arg ** mail_search_args_simplify_get_prev_argp(struct mail_search_simplify_ctx *ctx, const struct mail_search_simplify_prev_arg *mask) { struct mail_search_simplify_prev_arg *prev_arg; prev_arg = hash_table_lookup(ctx->prev_args, mask); if (prev_arg == NULL) { prev_arg = p_new(ctx->pool, struct mail_search_simplify_prev_arg, 1); prev_arg->bin_mask = mask->bin_mask; prev_arg->hdr_field_name_mask = p_strdup(ctx->pool, mask->hdr_field_name_mask); prev_arg->str_mask = p_strdup(ctx->pool, mask->str_mask); hash_table_insert(ctx->prev_args, prev_arg, prev_arg); } return &prev_arg->prev_arg; } static bool mail_search_args_merge_mask(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args, const struct mail_search_simplify_prev_arg *mask) { struct mail_search_arg **prev_argp; prev_argp = mail_search_args_simplify_get_prev_argp(ctx, mask); if (*prev_argp == NULL) { *prev_argp = args; return FALSE; } if (ctx->initialized) mail_search_arg_one_deinit(args); if ((*prev_argp)->match_not != args->match_not) { /* a && !a = 0 */ if (ctx->initialized) mail_search_arg_one_deinit(*prev_argp); (*prev_argp)->type = SEARCH_ALL; (*prev_argp)->match_not = ctx->parent_and; } /* duplicate keyword. */ return TRUE; } static bool mail_search_args_merge_flags(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; mail_search_arg_get_base_mask(args, &mask); mask.bin_mask.mail_flags = args->value.flags; return mail_search_args_merge_mask(ctx, args, &mask); } static bool mail_search_args_merge_keywords(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; mail_search_arg_get_base_mask(args, &mask); mask.str_mask = args->value.str; return mail_search_args_merge_mask(ctx, args, &mask); } static void mail_search_args_simplify_set(struct mail_search_arg *args) { const struct seq_range *seqset; unsigned int count; if (args->match_not) { /* invert the set to drop the NOT. Note that (uint32_t)-1 matches the last existing mail, which we don't know at this point. lib-imap/imap-seqset.c has similar code that disallows using (uint32_t)-1 as a real UID. */ if (seq_range_exists(&args->value.seqset, (uint32_t)-1)) return; args->match_not = FALSE; seq_range_array_invert(&args->value.seqset, 1, (uint32_t)-2); } seqset = array_get(&args->value.seqset, &count); if (count == 1 && seqset->seq1 == 1 && seqset->seq2 >= (uint32_t)-2) { /* 1:* is the same as ALL. */ args->type = SEARCH_ALL; } else if (count == 0) { /* empty set is the same as NOT ALL. this is mainly coming from mail_search_args_merge_set() intersection. */ args->type = SEARCH_ALL; args->match_not = TRUE; } } static bool mail_search_args_merge_set(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; struct mail_search_arg **prev_argp; if (args->match_not) { /* "*" used - can't simplify it */ return FALSE; } mail_search_arg_get_base_mask(args, &mask); mask.bin_mask.match_not = args->match_not; prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask); if (*prev_argp == NULL) { *prev_argp = args; return FALSE; } else if (ctx->parent_and) { seq_range_array_intersect(&(*prev_argp)->value.seqset, &args->value.seqset); return TRUE; } else { seq_range_array_merge(&(*prev_argp)->value.seqset, &args->value.seqset); return TRUE; } } static bool mail_search_args_merge_time(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; struct mail_search_arg **prev_argp, *prev_arg; mail_search_arg_get_base_mask(args, &mask); mask.bin_mask.match_not = args->match_not; mask.bin_mask.date_type = args->value.date_type; prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask); if (*prev_argp == NULL) { *prev_argp = args; return FALSE; } prev_arg = *prev_argp; switch (args->type) { case SEARCH_BEFORE: if (ctx->parent_and) { if (prev_arg->value.time < args->value.time) { /* prev_arg < 5 AND arg < 10 */ } else { /* prev_arg < 10 AND arg < 5 */ prev_arg->value.time = args->value.time; } } else { if (prev_arg->value.time < args->value.time) { /* prev_arg < 5 OR arg < 10 */ prev_arg->value.time = args->value.time; } else { /* prev_arg < 10 OR arg < 5 */ } } return TRUE; case SEARCH_ON: if (prev_arg->value.time == args->value.time) return TRUE; return FALSE; case SEARCH_SINCE: if (ctx->parent_and) { if (prev_arg->value.time < args->value.time) { /* prev_arg >= 5 AND arg >= 10 */ prev_arg->value.time = args->value.time; } else { /* prev_arg >= 10 AND arg >= 5 */ } } else { if (prev_arg->value.time < args->value.time) { /* prev_arg >= 5 OR arg >= 10 */ } else { /* prev_arg >= 10 OR arg >= 5 */ prev_arg->value.time = args->value.time; } } return TRUE; default: break; } return FALSE; } static bool mail_search_args_merge_size(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; struct mail_search_arg **prev_argp, *prev_arg; mail_search_arg_get_base_mask(args, &mask); mask.bin_mask.match_not = args->match_not; prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask); if (*prev_argp == NULL) { *prev_argp = args; return FALSE; } prev_arg = *prev_argp; switch (args->type) { case SEARCH_SMALLER: if (ctx->parent_and) { if (prev_arg->value.size < args->value.size) { /* prev_arg < 5 AND arg < 10 */ } else { /* prev_arg < 10 AND arg < 5 */ prev_arg->value.size = args->value.size; } } else { if (prev_arg->value.size < args->value.size) { /* prev_arg < 5 OR arg < 10 */ prev_arg->value.size = args->value.size; } else { /* prev_arg < 10 OR arg < 5 */ } } return TRUE; case SEARCH_LARGER: if (ctx->parent_and) { if (prev_arg->value.size < args->value.size) { /* prev_arg >= 5 AND arg >= 10 */ prev_arg->value.size = args->value.size; } else { /* prev_arg >= 10 AND arg >= 5 */ } } else { if (prev_arg->value.size < args->value.size) { /* prev_arg >= 5 OR arg >= 10 */ } else { /* prev_arg >= 10 OR arg >= 5 */ prev_arg->value.size = args->value.size; } } return TRUE; default: break; } return FALSE; } static bool mail_search_args_merge_text(struct mail_search_simplify_ctx *ctx, struct mail_search_arg *args) { struct mail_search_simplify_prev_arg mask; mail_search_arg_get_base_mask(args, &mask); mask.hdr_field_name_mask = args->hdr_field_name; mask.str_mask = args->value.str; return mail_search_args_merge_mask(ctx, args, &mask); } static bool mail_search_args_have_equal(const struct mail_search_arg *args, const struct mail_search_arg *wanted_arg) { const struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { if (mail_search_arg_one_equals(arg, wanted_arg)) return TRUE; } return FALSE; } static bool mail_search_args_remove_equal(struct mail_search_args *all_args, struct mail_search_arg **argsp, const struct mail_search_arg *wanted_arg, bool check_subs) { struct mail_search_arg **argp; bool found = FALSE; for (argp = argsp; (*argp) != NULL; ) { if (mail_search_arg_one_equals(*argp, wanted_arg)) { if (all_args->init_refcount > 0) mail_search_arg_one_deinit(*argp); *argp = (*argp)->next; found = TRUE; } else if (check_subs) { i_assert((*argp)->type == SEARCH_SUB || (*argp)->type == SEARCH_OR); if (!mail_search_args_remove_equal(all_args, &(*argp)->value.subargs, wanted_arg, FALSE)) { /* we already verified that this should have existed. */ i_unreached(); } if ((*argp)->value.subargs == NULL) *argp = (*argp)->next; else argp = &(*argp)->next; found = TRUE; } else { argp = &(*argp)->next; } } return found; } static bool mail_search_args_have_all_equal(struct mail_search_arg *parent_arg, const struct mail_search_arg *wanted_args) { const struct mail_search_arg *arg; i_assert(parent_arg->type == SEARCH_SUB || parent_arg->type == SEARCH_OR); for (arg = wanted_args; arg != NULL; arg = arg->next) { if (!mail_search_args_have_equal(parent_arg->value.subargs, arg)) return FALSE; } return TRUE; } static unsigned int mail_search_args_count(const struct mail_search_arg *args) { unsigned int count; for (count = 0; args != NULL; count++) args = args->next; return count; } static bool mail_search_args_simplify_drop_redundant_args(struct mail_search_args *all_args, struct mail_search_arg **argsp, bool and_arg) { struct mail_search_arg *arg, **argp, one_arg, *lowest_arg = NULL; enum mail_search_arg_type child_subargs_type; unsigned int count, lowest_count = UINT_MAX; bool ret = FALSE; if (*argsp == NULL) return FALSE; child_subargs_type = and_arg ? SEARCH_OR : SEARCH_SUB; /* find the arg which has the lowest number of child args */ for (arg = *argsp; arg != NULL; arg = arg->next) { if (arg->type != child_subargs_type) { one_arg = *arg; one_arg.next = NULL; lowest_arg = &one_arg; break; } count = mail_search_args_count(arg->value.subargs); if (count < lowest_count) { lowest_arg = arg->value.subargs; lowest_count = count; } } i_assert(lowest_arg != NULL); /* if there are any args that include lowest_arg, drop the arg since it's redundant. (non-SUB duplicates are dropped elsewhere.) */ for (argp = argsp; *argp != NULL; ) { if (*argp != lowest_arg && (*argp)->type == child_subargs_type && (*argp)->value.subargs != lowest_arg && mail_search_args_have_all_equal(*argp, lowest_arg)) { if (all_args->init_refcount > 0) mail_search_arg_one_deinit(*argp); *argp = (*argp)->next; ret = TRUE; } else { argp = &(*argp)->next; } } return ret; } static bool mail_search_args_simplify_extract_common(struct mail_search_args *all_args, struct mail_search_arg **argsp, pool_t pool, bool and_arg) { /* Simple SUB example: (a AND b) OR (a AND c) -> a AND (b OR c) More complicated example: (c1 AND c2 AND u1 AND u2) OR (c1 AND c2 AND u3 AND u4) -> c1 AND c2 AND ((u1 AND u2) OR (u3 AND u4)) Similarly for ORs: (a OR b) AND (a OR c) -> a OR (b AND c) (c1 OR c2 OR u1 OR u2) AND (c1 OR c2 OR u3 OR u4) -> c1 OR c2 OR ((u1 OR u2) AND (u3 OR u4)) */ struct mail_search_arg *arg, *sub_arg, *sub_next; struct mail_search_arg *new_arg, *child_arg, *common_args = NULL; enum mail_search_arg_type child_subargs_type; if (*argsp == NULL || (*argsp)->next == NULL) { /* single arg, nothing to extract */ return FALSE; } child_subargs_type = and_arg ? SEARCH_OR : SEARCH_SUB; /* find the first arg with child_subargs_type */ for (arg = *argsp; arg != NULL; arg = arg->next) { if (arg->type == child_subargs_type) break; } if (arg == NULL) return FALSE; for (sub_arg = arg->value.subargs; sub_arg != NULL; sub_arg = sub_next) { sub_next = sub_arg->next; /* check if sub_arg is found from all the args */ for (arg = *argsp; arg != NULL; arg = arg->next) { if (mail_search_arg_one_equals(arg, sub_arg)) { /* the whole arg matches */ } else if (arg->type == child_subargs_type && mail_search_args_have_equal(arg->value.subargs, sub_arg)) { /* exists as subarg */ } else { break; } } if (arg != NULL) continue; /* extract the arg and put it to common_args */ mail_search_args_remove_equal(all_args, argsp, sub_arg, TRUE); sub_arg->next = common_args; common_args = sub_arg; } if (common_args == NULL) return FALSE; /* replace all the original args with a single new SUB/OR arg */ new_arg = p_new(pool, struct mail_search_arg, 1); new_arg->type = child_subargs_type; if (*argsp == NULL) { /* there are only common args */ new_arg->value.subargs = common_args; } else { /* replace OR arg with AND(OR(non_common_args), common_args) or replace AND arg with OR(AND(non_common_args), common_args) */ child_arg = p_new(pool, struct mail_search_arg, 1); child_arg->type = and_arg ? SEARCH_SUB : SEARCH_OR; child_arg->value.subargs = *argsp; child_arg->next = common_args; new_arg->value.subargs = child_arg; } *argsp = new_arg; return TRUE; } static bool mail_search_args_simplify_sub(struct mail_search_args *all_args, pool_t pool, struct mail_search_arg **argsp, bool parent_and) { struct mail_search_simplify_ctx ctx; struct mail_search_arg *sub, **all_argsp = argsp; bool merged; i_zero(&ctx); ctx.initialized = all_args->init_refcount > 0; ctx.parent_and = parent_and; ctx.pool = pool_alloconly_create("mail search args simplify", 1024); hash_table_create(&ctx.prev_args, ctx.pool, 0, mail_search_simplify_prev_arg_hash, mail_search_simplify_prev_arg_cmp); while (*argsp != NULL) { struct mail_search_arg *args = *argsp; if (args->match_not && (args->type == SEARCH_SUB || args->type == SEARCH_OR)) { /* neg(p and q and ..) == neg(p) or neg(q) or .. neg(p or q or ..) == neg(p) and neg(q) and .. */ args->type = args->type == SEARCH_SUB ? SEARCH_OR : SEARCH_SUB; args->match_not = FALSE; sub = args->value.subargs; do { sub->match_not = !sub->match_not; sub = sub->next; } while (sub != NULL); } if ((args->type == SEARCH_SUB && parent_and) || (args->type == SEARCH_OR && !parent_and) || ((args->type == SEARCH_SUB || args->type == SEARCH_OR) && args->value.subargs->next == NULL)) { /* p and (q and ..) == p and q and .. p or (q or ..) == p or q or .. (p) = p */ sub = args->value.subargs; for (; sub->next != NULL; sub = sub->next) ; sub->next = args->next; *args = *args->value.subargs; ctx.removals = TRUE; continue; } if (args->type == SEARCH_SUB || args->type == SEARCH_OR || args->type == SEARCH_INTHREAD) { i_assert(!args->match_not); if (args->type != SEARCH_INTHREAD) { bool and_arg = args->type == SEARCH_SUB; if (mail_search_args_simplify_drop_redundant_args(all_args, &args->value.subargs, and_arg)) ctx.removals = TRUE; if (mail_search_args_simplify_extract_common(all_args, &args->value.subargs, pool, and_arg)) ctx.removals = TRUE; } if (mail_search_args_simplify_sub(all_args, pool, &args->value.subargs, args->type != SEARCH_OR)) ctx.removals = TRUE; } if (args->type == SEARCH_SEQSET || args->type == SEARCH_UIDSET) mail_search_args_simplify_set(args); /* try to merge arguments */ merged = FALSE; switch (args->type) { case SEARCH_ALL: { if (*all_argsp == args && args->next == NULL) { /* this arg has no siblings - no merging */ break; } if ((parent_and && !args->match_not) || (!parent_and && args->match_not)) { /* .. AND ALL .. .. OR NOT ALL .. This arg is irrelevant -> drop */ merged = TRUE; break; } /* .. AND NOT ALL .. .. OR ALL .. The other args are irrelevant -> drop them */ *all_argsp = args; args->next = NULL; ctx.removals = TRUE; break; } case SEARCH_FLAGS: merged = mail_search_args_merge_flags(&ctx, args); break; case SEARCH_KEYWORDS: merged = mail_search_args_merge_keywords(&ctx, args); break; case SEARCH_SEQSET: case SEARCH_UIDSET: merged = mail_search_args_merge_set(&ctx, args); break; case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: merged = mail_search_args_merge_time(&ctx, args); break; case SEARCH_SMALLER: case SEARCH_LARGER: merged = mail_search_args_merge_size(&ctx, args); break; case SEARCH_BODY: case SEARCH_TEXT: if (args->value.str[0] == '\0') { /* BODY "" and TEXT "" matches everything */ args->type = SEARCH_ALL; ctx.removals = TRUE; break; } /* fall through */ case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: merged = mail_search_args_merge_text(&ctx, args); break; default: break; } if (merged) { *argsp = args->next; ctx.removals = TRUE; continue; } argsp = &args->next; } hash_table_destroy(&ctx.prev_args); pool_unref(&ctx.pool); return ctx.removals; } static bool mail_search_args_simplify_merge_flags(struct mail_search_arg **argsp, bool parent_and) { struct mail_search_arg *prev_flags = NULL; bool removals = FALSE; while (*argsp != NULL) { struct mail_search_arg *args = *argsp; if (args->type == SEARCH_SUB || args->type == SEARCH_OR || args->type == SEARCH_INTHREAD) { if (mail_search_args_simplify_merge_flags(&args->value.subargs, args->type != SEARCH_OR)) removals = TRUE; } else if (args->type != SEARCH_FLAGS) { /* ignore non-flags */ } else if (!((!args->match_not && parent_and) || (args->match_not && !parent_and))) { /* can't merge these flags args */ } else if (prev_flags == NULL) { /* first flags arg */ prev_flags = args; } else { /* merge to previous arg */ prev_flags->value.flags |= args->value.flags; *argsp = args->next; removals = TRUE; continue; } argsp = &args->next; } return removals; } static bool mail_search_args_unnest_inthreads(struct mail_search_args *args, struct mail_search_arg **argp, bool parent_inthreads, bool parent_and) { struct mail_search_arg *arg, *thread_arg, *or_arg; bool child_inthreads = FALSE, non_inthreads = FALSE; for (arg = *argp; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_SUB: case SEARCH_OR: if (!mail_search_args_unnest_inthreads(args, &arg->value.subargs, parent_inthreads, arg->type != SEARCH_OR)) { arg->result = 1; child_inthreads = TRUE; } else { arg->result = 0; non_inthreads = TRUE; } break; case SEARCH_INTHREAD: if (mail_search_args_unnest_inthreads(args, &arg->value.subargs, TRUE, TRUE)) { /* children converted to SEARCH_INTHREADs */ arg->type = SEARCH_SUB; } args->have_inthreads = TRUE; arg->result = 1; child_inthreads = TRUE; break; default: arg->result = 0; non_inthreads = TRUE; break; } } if (!parent_inthreads || !child_inthreads || !non_inthreads) return FALSE; /* put all non-INTHREADs under a single INTHREAD */ thread_arg = p_new(args->pool, struct mail_search_arg, 1); thread_arg->type = SEARCH_INTHREAD; while (*argp != NULL) { arg = *argp; argp = &(*argp)->next; if (arg->result == 0) { /* not an INTHREAD or a SUB/OR with only INTHREADs */ arg->next = thread_arg->value.subargs; thread_arg->value.subargs = arg; } } if (!parent_and) { /* We want to OR the args */ or_arg = p_new(args->pool, struct mail_search_arg, 1); or_arg->type = SEARCH_OR; or_arg->value.subargs = thread_arg->value.subargs; thread_arg->value.subargs = or_arg; } return TRUE; } void mail_search_args_simplify(struct mail_search_args *args) { bool removals; args->simplified = TRUE; removals = mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE); if (mail_search_args_unnest_inthreads(args, &args->args, FALSE, TRUE)) { /* we may have added some extra SUBs that could be dropped */ if (mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE)) removals = TRUE; } do { if (mail_search_args_simplify_drop_redundant_args(args, &args->args, TRUE)) removals = TRUE; if (mail_search_args_simplify_extract_common(args, &args->args, args->pool, TRUE)) removals = TRUE; if (removals) removals = mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE); /* do the flag merging into a single arg only at the end. up until then they're treated as any other search args, which simplifies their handling. after the flags merging is done, further simplifications are still possible. */ if (mail_search_args_simplify_merge_flags(&args->args, TRUE)) removals = TRUE; } while (removals); } dovecot-2.2.33.2/src/lib-storage/mail-storage-hooks.h0000644000175000017500000000423713123174404017207 00000000000000#ifndef MAIL_STORAGE_HOOKS_H #define MAIL_STORAGE_HOOKS_H struct module; struct mail_user; struct mail_storage; struct mail_namespace; struct mailbox_list; struct mailbox; struct mail; struct mail_storage_hooks { void (*mail_user_created)(struct mail_user *user); void (*mail_namespace_storage_added)(struct mail_namespace *ns); /* called the first time user's initial namespaces were added */ void (*mail_namespaces_created)(struct mail_namespace *namespaces); /* called every time namespaces are added. most importantly called when shared mailbox accesses trigger creating new namespaces. this is called before mail_namespaces_created() at startup. The namespaces parameter contains all of the current namespaces. */ void (*mail_namespaces_added)(struct mail_namespace *namespaces); void (*mail_storage_created)(struct mail_storage *storage); void (*mailbox_list_created)(struct mailbox_list *list); void (*mailbox_allocated)(struct mailbox *box); void (*mailbox_opened)(struct mailbox *box); void (*mail_allocated)(struct mail *mail); }; void mail_storage_hooks_init(void); void mail_storage_hooks_deinit(void); void mail_storage_hooks_add(struct module *module, const struct mail_storage_hooks *hooks); /* Add hooks to this plugin regardless of whether it exists in user's mail_plugins setting. */ void mail_storage_hooks_add_forced(struct module *module, const struct mail_storage_hooks *hooks); void mail_storage_hooks_remove(const struct mail_storage_hooks *hooks); void mail_storage_hooks_add_internal(const struct mail_storage_hooks *hooks); void mail_storage_hooks_remove_internal(const struct mail_storage_hooks *hooks); void hook_mail_user_created(struct mail_user *user); void hook_mail_namespace_storage_added(struct mail_namespace *ns); void hook_mail_namespaces_created(struct mail_namespace *namespaces); void hook_mail_namespaces_added(struct mail_namespace *namespaces); void hook_mail_storage_created(struct mail_storage *storage); void hook_mailbox_list_created(struct mailbox_list *list); void hook_mailbox_allocated(struct mailbox *box); void hook_mailbox_opened(struct mailbox *box); void hook_mail_allocated(struct mail *mail); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-parser.c0000644000175000017500000000211613123174404017146 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-search-parser-private.h" void mail_search_parser_deinit(struct mail_search_parser **_parser) { struct mail_search_parser *parser = *_parser; *_parser = NULL; pool_unref(&parser->pool); } int mail_search_parse_key(struct mail_search_parser *parser, const char **key_r) { int ret; if ((ret = parser->v.parse_key(parser, key_r)) <= 0) return ret; parser->cur_key = *key_r; return 1; } int mail_search_parse_string(struct mail_search_parser *parser, const char **value_r) { int ret; ret = parser->v.parse_string(parser, value_r); if (ret < 0 && parser->cur_key != NULL) { parser->error = p_strdup_printf(parser->pool, "%s (for search key: %s)", parser->error, t_str_ucase(parser->cur_key)); } return ret; } bool mail_search_parse_skip_next(struct mail_search_parser *parser, const char *str) { return parser->v.parse_skip_next(parser, str); } const char *mail_search_parser_get_error(struct mail_search_parser *parser) { return parser->error; } dovecot-2.2.33.2/src/lib-storage/test-mailbox-get.c0000644000175000017500000001073013165463624016670 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "test-common.h" #include "mail-index-modseq.h" #include "mail-storage-private.h" static uint32_t expunge_uids[] = { 25, 15, 7, 3, 11, 1, 53, 33 }; static guid_128_t mail_guids[N_ELEMENTS(expunge_uids)]; static unsigned int expunge_idx; static unsigned int nonexternal_idx; void mail_index_lookup_uid(struct mail_index_view *view ATTR_UNUSED, uint32_t seq, uint32_t *uid_r) { *uid_r = seq; } bool mail_index_lookup_seq_range(struct mail_index_view *view ATTR_UNUSED, uint32_t first_uid, uint32_t last_uid, uint32_t *first_seq_r, uint32_t *last_seq_r) { *first_seq_r = first_uid; *last_seq_r = last_uid; return TRUE; } bool mail_index_modseq_get_next_log_offset(struct mail_index_view *view ATTR_UNUSED, uint64_t modseq, uint32_t *log_seq_r, uoff_t *log_offset_r) { *log_seq_r = modseq >> 32; *log_offset_r = modseq & 0xfffffff; return TRUE; } struct mail_transaction_log_view * mail_transaction_log_view_open(struct mail_transaction_log *log ATTR_UNUSED) { return NULL; } int mail_transaction_log_view_set(struct mail_transaction_log_view *view ATTR_UNUSED, uint32_t min_file_seq ATTR_UNUSED, uoff_t min_file_offset ATTR_UNUSED, uint32_t max_file_seq ATTR_UNUSED, uoff_t max_file_offset ATTR_UNUSED, bool *reset_r ATTR_UNUSED, const char **reason_r ATTR_UNUSED) { if (min_file_seq < 99) return 0; return 1; } void mail_transaction_log_view_close(struct mail_transaction_log_view **view ATTR_UNUSED) { } void mail_transaction_log_get_tail(struct mail_transaction_log *log ATTR_UNUSED, uint32_t *file_seq_r) { *file_seq_r = 100; } int mail_transaction_log_view_next(struct mail_transaction_log_view *view ATTR_UNUSED, const struct mail_transaction_header **hdr_r, const void **data_r) { static struct mail_transaction_header hdr; static struct mail_transaction_expunge_guid exp; static struct mail_transaction_expunge old_exp; if (expunge_idx == N_ELEMENTS(expunge_uids)) return 0; if (mail_guids[expunge_idx][0] == 0) { old_exp.uid1 = old_exp.uid2 = expunge_uids[expunge_idx]; hdr.type = MAIL_TRANSACTION_EXPUNGE; hdr.size = sizeof(old_exp); *data_r = &old_exp; } else { exp.uid = expunge_uids[expunge_idx]; memcpy(exp.guid_128, mail_guids[expunge_idx], sizeof(exp.guid_128)); hdr.type = MAIL_TRANSACTION_EXPUNGE_GUID; hdr.size = sizeof(exp); *data_r = &exp; } if (expunge_idx != nonexternal_idx) hdr.type |= MAIL_TRANSACTION_EXTERNAL; *hdr_r = &hdr; expunge_idx++; return 1; } void mail_transaction_log_view_mark(struct mail_transaction_log_view *view ATTR_UNUSED) { } void mail_transaction_log_view_rewind(struct mail_transaction_log_view *view ATTR_UNUSED) { expunge_idx = 0; } static void test_mailbox_get_expunges(void) { struct mailbox *box; ARRAY_TYPE(seq_range) uids_filter; ARRAY_TYPE(mailbox_expunge_rec) expunges; const struct mailbox_expunge_rec *exp; unsigned int count; uint64_t modseq; box = t_new(struct mailbox, 1); box->index = t_new(struct mail_index, 1); box->view = t_new(struct mail_index_view, 1); box->view->log_file_head_seq = 101; box->view->log_file_head_offset = 1024; test_begin("mailbox get expunges"); nonexternal_idx = 1; memset(mail_guids + 2, 0, GUID_128_SIZE); memset(mail_guids + 4, 0, GUID_128_SIZE); t_array_init(&uids_filter, 32); seq_range_array_add_range(&uids_filter, 1, 20); seq_range_array_add_range(&uids_filter, 53, 53); t_array_init(&expunges, 32); modseq = 98ULL << 32; test_assert(mailbox_get_expunges(box, modseq, &uids_filter, &expunges) == 0); exp = array_get(&expunges, &count); test_assert(count == 5); test_assert(exp[0].uid == 3); test_assert(memcmp(exp[0].guid_128, mail_guids[3], GUID_128_SIZE) == 0); test_assert(exp[1].uid == 1); test_assert(memcmp(exp[1].guid_128, mail_guids[5], GUID_128_SIZE) == 0); test_assert(exp[2].uid == 53); test_assert(memcmp(exp[2].guid_128, mail_guids[6], GUID_128_SIZE) == 0); test_assert(exp[3].uid == 7); test_assert(memcmp(exp[3].guid_128, mail_guids[2], GUID_128_SIZE) == 0); test_assert(exp[4].uid == 11); test_assert(memcmp(exp[4].guid_128, mail_guids[4], GUID_128_SIZE) == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mailbox_get_expunges, NULL }; unsigned int i, j; for (i = 0; i < N_ELEMENTS(mail_guids); i++) { for (j = 0; j < GUID_128_SIZE; j++) mail_guids[i][j] = j + i + 1; } return test_run(test_functions); } dovecot-2.2.33.2/src/lib-storage/fail-mail.c0000644000175000017500000001375713165463624015352 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage-private.h" #include "fail-mail-storage.h" struct mail * fail_mailbox_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields ATTR_UNUSED, struct mailbox_header_lookup_ctx *wanted_headers ATTR_UNUSED) { struct mail_private *mail; pool_t pool; pool = pool_alloconly_create("fail mail", 1024); mail = p_new(pool, struct mail_private, 1); mail->mail.box = t->box; mail->mail.transaction = t; mail->v = fail_mail_vfuncs; mail->pool = pool; p_array_init(&mail->module_contexts, pool, 5); return &mail->mail; } static void fail_mail_free(struct mail *mail) { struct mail_private *pmail = (struct mail_private *)mail; pool_unref(&pmail->pool); } static void fail_mail_set_seq(struct mail *mail, uint32_t seq, bool saving) { mail->seq = seq; mail->uid = seq; mail->saving = saving; mail->expunged = TRUE; mail->has_nuls = FALSE; mail->has_no_nuls = FALSE; } static bool fail_mail_set_uid(struct mail *mail, uint32_t uid) { fail_mail_set_seq(mail, uid, FALSE); return TRUE; } static void fail_mail_set_uid_cache_updates(struct mail *mail ATTR_UNUSED, bool set ATTR_UNUSED) { } static bool fail_mail_prefetch(struct mail *mail ATTR_UNUSED) { return TRUE; } static void fail_mail_precache(struct mail *mail ATTR_UNUSED) { } static void fail_mail_add_temp_wanted_fields(struct mail *mail ATTR_UNUSED, enum mail_fetch_field fields ATTR_UNUSED, struct mailbox_header_lookup_ctx *headers ATTR_UNUSED) { } static enum mail_flags fail_mail_get_flags(struct mail *mail ATTR_UNUSED) { return 0; } static const char *const * fail_mail_get_keywords(struct mail *mail ATTR_UNUSED) { return t_new(const char *, 1); } static const ARRAY_TYPE(keyword_indexes) * fail_mail_get_keyword_indexes(struct mail *mail ATTR_UNUSED) { ARRAY_TYPE(keyword_indexes) *kw_indexes; kw_indexes = t_new(ARRAY_TYPE(keyword_indexes), 1); t_array_init(kw_indexes, 1); array_append_zero(kw_indexes); return kw_indexes; } static uint64_t fail_mail_get_modseq(struct mail *mail ATTR_UNUSED) { return 0; } static int fail_mail_get_parts(struct mail *mail ATTR_UNUSED, struct message_part **parts_r ATTR_UNUSED) { return -1; } static int fail_mail_get_date(struct mail *mail ATTR_UNUSED, time_t *date_r ATTR_UNUSED, int *timezone_r ATTR_UNUSED) { return -1; } static int fail_mail_get_received_date(struct mail *mail ATTR_UNUSED, time_t *date_r ATTR_UNUSED) { return -1; } static int fail_mail_get_save_date(struct mail *mail ATTR_UNUSED, time_t *date_r ATTR_UNUSED) { return -1; } static int fail_mail_get_fail_mail_size(struct mail *mail ATTR_UNUSED, uoff_t *size_r ATTR_UNUSED) { return -1; } static int fail_mail_get_physical_size(struct mail *mail ATTR_UNUSED, uoff_t *size_r ATTR_UNUSED) { return -1; } static int fail_mail_get_first_header(struct mail *mail ATTR_UNUSED, const char *field ATTR_UNUSED, bool decode_to_utf8 ATTR_UNUSED, const char **value_r) { *value_r = NULL; return 0; } static int fail_mail_get_headers(struct mail *mail ATTR_UNUSED, const char *field ATTR_UNUSED, bool decode_to_utf8 ATTR_UNUSED, const char *const **value_r) { *value_r = NULL; return 0; } static int fail_mail_get_header_stream(struct mail *mail ATTR_UNUSED, struct mailbox_header_lookup_ctx *headers ATTR_UNUSED, struct istream **stream_r ATTR_UNUSED) { return -1; } static int fail_mail_get_stream(struct mail *mail ATTR_UNUSED, bool get_body ATTR_UNUSED, struct message_size *hdr_size ATTR_UNUSED, struct message_size *body_size ATTR_UNUSED, struct istream **stream_r ATTR_UNUSED) { return -1; } static int fail_mail_get_binary_stream(struct mail *_mail ATTR_UNUSED, const struct message_part *part ATTR_UNUSED, bool include_hdr ATTR_UNUSED, uoff_t *size_r ATTR_UNUSED, unsigned int *body_lines_r ATTR_UNUSED, bool *binary_r ATTR_UNUSED, struct istream **stream_r ATTR_UNUSED) { return -1; } static int fail_mail_get_special(struct mail *mail ATTR_UNUSED, enum mail_fetch_field field ATTR_UNUSED, const char **value_r ATTR_UNUSED) { return -1; } static struct mail * fail_mail_get_real_mail(struct mail *mail) { return mail; } static void fail_mail_update_flags(struct mail *mail ATTR_UNUSED, enum modify_type modify_type ATTR_UNUSED, enum mail_flags flags ATTR_UNUSED) { } static void fail_mail_update_keywords(struct mail *mail ATTR_UNUSED, enum modify_type modify_type ATTR_UNUSED, struct mail_keywords *keywords ATTR_UNUSED) { } static void fail_mail_update_modseq(struct mail *mail ATTR_UNUSED, uint64_t min_modseq ATTR_UNUSED) { } static void fail_mail_expunge(struct mail *mail ATTR_UNUSED) { } static void fail_mail_set_cache_corrupted(struct mail *mail ATTR_UNUSED, enum mail_fetch_field field ATTR_UNUSED) { } static void fail_mail_set_cache_corrupted_reason(struct mail *mail ATTR_UNUSED, enum mail_fetch_field field ATTR_UNUSED, const char *reason ATTR_UNUSED) { } struct mail_vfuncs fail_mail_vfuncs = { NULL, fail_mail_free, fail_mail_set_seq, fail_mail_set_uid, fail_mail_set_uid_cache_updates, fail_mail_prefetch, fail_mail_precache, fail_mail_add_temp_wanted_fields, fail_mail_get_flags, fail_mail_get_keywords, fail_mail_get_keyword_indexes, fail_mail_get_modseq, fail_mail_get_modseq, fail_mail_get_parts, fail_mail_get_date, fail_mail_get_received_date, fail_mail_get_save_date, fail_mail_get_fail_mail_size, fail_mail_get_physical_size, fail_mail_get_first_header, fail_mail_get_headers, fail_mail_get_header_stream, fail_mail_get_stream, fail_mail_get_binary_stream, fail_mail_get_special, fail_mail_get_real_mail, fail_mail_update_flags, fail_mail_update_keywords, fail_mail_update_modseq, fail_mail_update_modseq, NULL, fail_mail_expunge, fail_mail_set_cache_corrupted, NULL, fail_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/lib-storage/mailbox-search-result.c0000644000175000017500000001214313123174404017702 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage-private.h" #include "mail-search.h" #include "mailbox-search-result-private.h" static void mailbox_search_result_analyze_args(struct mail_search_result *result, struct mail_search_arg *arg) { for (; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_OR: case SEARCH_SUB: mailbox_search_result_analyze_args(result, arg->value.subargs); break; case SEARCH_FLAGS: result->args_have_flags = TRUE; break; case SEARCH_KEYWORDS: result->args_have_keywords = TRUE; break; case SEARCH_MODSEQ: result->args_have_modseq = TRUE; break; default: break; } } } struct mail_search_result * mailbox_search_result_alloc(struct mailbox *box, struct mail_search_args *args, enum mailbox_search_result_flags flags) { struct mail_search_result *result; result = i_new(struct mail_search_result, 1); result->box = box; result->flags = flags; i_array_init(&result->uids, 32); i_array_init(&result->never_uids, 128); if ((result->flags & MAILBOX_SEARCH_RESULT_FLAG_UPDATE) != 0) { result->search_args = args; mail_search_args_ref(result->search_args); mailbox_search_result_analyze_args(result, args->args); } array_append(&result->box->search_results, &result, 1); return result; } void mailbox_search_result_free(struct mail_search_result **_result) { struct mail_search_result *result = *_result; struct mail_search_result *const *results; unsigned int i, count; *_result = NULL; results = array_get(&result->box->search_results, &count); for (i = 0; i < count; i++) { if (results[i] == result) { array_delete(&result->box->search_results, i, 1); break; } } i_assert(i != count); if (result->search_args != NULL) mail_search_args_unref(&result->search_args); array_free(&result->uids); array_free(&result->never_uids); if (array_is_created(&result->removed_uids)) { array_free(&result->removed_uids); array_free(&result->added_uids); } i_free(result); } struct mail_search_result * mailbox_search_result_save(struct mail_search_context *ctx, enum mailbox_search_result_flags flags) { struct mail_search_result *result; result = mailbox_search_result_alloc(ctx->transaction->box, ctx->args, flags); array_append(&ctx->results, &result, 1); return result; } void mailbox_search_result_initial_done(struct mail_search_result *result) { if ((result->flags & MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC) != 0) { i_array_init(&result->removed_uids, 32); i_array_init(&result->added_uids, 32); } mail_search_args_seq2uid(result->search_args); } void mailbox_search_results_initial_done(struct mail_search_context *ctx) { struct mail_search_result *const *results; unsigned int i, count; results = array_get(&ctx->results, &count); for (i = 0; i < count; i++) mailbox_search_result_initial_done(results[i]); } void mailbox_search_result_add(struct mail_search_result *result, uint32_t uid) { i_assert(uid > 0); if (seq_range_exists(&result->uids, uid)) return; seq_range_array_add(&result->uids, uid); if (array_is_created(&result->added_uids)) { seq_range_array_add(&result->added_uids, uid); seq_range_array_remove(&result->removed_uids, uid); } } void mailbox_search_result_remove(struct mail_search_result *result, uint32_t uid) { if (seq_range_array_remove(&result->uids, uid)) { if (array_is_created(&result->removed_uids)) { seq_range_array_add(&result->removed_uids, uid); seq_range_array_remove(&result->added_uids, uid); } } } void mailbox_search_results_add(struct mail_search_context *ctx, uint32_t uid) { struct mail_search_result *const *results; unsigned int i, count; results = array_get(&ctx->results, &count); for (i = 0; i < count; i++) mailbox_search_result_add(results[i], uid); } void mailbox_search_results_remove(struct mailbox *box, uint32_t uid) { struct mail_search_result *const *results; unsigned int i, count; results = array_get(&box->search_results, &count); for (i = 0; i < count; i++) mailbox_search_result_remove(results[i], uid); } void mailbox_search_result_never(struct mail_search_result *result, uint32_t uid) { seq_range_array_add(&result->never_uids, uid); } void mailbox_search_results_never(struct mail_search_context *ctx, uint32_t uid) { struct mail_search_result *const *results; unsigned int i, count; if (ctx->update_result != NULL) mailbox_search_result_never(ctx->update_result, uid); results = array_get(&ctx->results, &count); for (i = 0; i < count; i++) mailbox_search_result_never(results[i], uid); } const ARRAY_TYPE(seq_range) * mailbox_search_result_get(struct mail_search_result *result) { return &result->uids; } void mailbox_search_result_sync(struct mail_search_result *result, ARRAY_TYPE(seq_range) *removed_uids, ARRAY_TYPE(seq_range) *added_uids) { array_clear(removed_uids); array_clear(added_uids); array_append_array(removed_uids, &result->removed_uids); array_append_array(added_uids, &result->added_uids); array_clear(&result->removed_uids); array_clear(&result->added_uids); } dovecot-2.2.33.2/src/lib-storage/mailbox-keywords.c0000644000175000017500000000633613123174404016777 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-arg.h" #include "mail-storage-private.h" static struct mail_keywords * mailbox_keywords_create_skip(struct mailbox *box, const char *const keywords[]) { struct mail_keywords *kw; T_BEGIN { ARRAY(const char *) valid_keywords; const char *error; t_array_init(&valid_keywords, 32); for (; *keywords != NULL; keywords++) { if (mailbox_keyword_is_valid(box, *keywords, &error)) array_append(&valid_keywords, keywords, 1); } array_append_zero(&valid_keywords); /* NULL-terminate */ kw = mail_index_keywords_create(box->index, keywords); } T_END; return kw; } static bool mailbox_keywords_are_valid(struct mailbox *box, const char *const keywords[], const char **error_r) { unsigned int i; for (i = 0; keywords[i] != NULL; i++) { if (!mailbox_keyword_is_valid(box, keywords[i], error_r)) return FALSE; } return TRUE; } int mailbox_keywords_create(struct mailbox *box, const char *const keywords[], struct mail_keywords **keywords_r) { const char *error, *empty_keyword_list = NULL; i_assert(box->opened); if (keywords == NULL) keywords = &empty_keyword_list; if (!mailbox_keywords_are_valid(box, keywords, &error)) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, error); return -1; } *keywords_r = mail_index_keywords_create(box->index, keywords); return 0; } struct mail_keywords * mailbox_keywords_create_valid(struct mailbox *box, const char *const keywords[]) { const char *empty_keyword_list = NULL; const char *error; i_assert(box->opened); if (keywords == NULL) keywords = &empty_keyword_list; if (mailbox_keywords_are_valid(box, keywords, &error)) return mail_index_keywords_create(box->index, keywords); else { /* found invalid keywords, do this the slow way */ return mailbox_keywords_create_skip(box, keywords); } } struct mail_keywords * mailbox_keywords_create_from_indexes(struct mailbox *box, const ARRAY_TYPE(keyword_indexes) *idx) { i_assert(box->opened); return mail_index_keywords_create_from_indexes(box->index, idx); } void mailbox_keywords_ref(struct mail_keywords *keywords) { mail_index_keywords_ref(keywords); } void mailbox_keywords_unref(struct mail_keywords **keywords) { mail_index_keywords_unref(keywords); } bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword, const char **error_r) { unsigned int i, idx; i_assert(box->opened); /* if it already exists, skip validity checks */ if (mail_index_keyword_lookup(box->index, keyword, &idx)) return TRUE; if (*keyword == '\0') { *error_r = "Empty keywords not allowed"; return FALSE; } if (box->disallow_new_keywords) { *error_r = "Can't create new keywords"; return FALSE; } /* these are IMAP-specific restrictions, but for now IMAP is all we care about */ for (i = 0; keyword[i] != '\0'; i++) { if (!IS_ATOM_CHAR(keyword[i])) { if ((unsigned char)keyword[i] < 0x80) *error_r = "Invalid characters in keyword"; else *error_r = "8bit characters in keyword"; return FALSE; } } if (i > box->storage->set->mail_max_keyword_length) { *error_r = "Keyword length too long"; return FALSE; } return TRUE; } dovecot-2.2.33.2/src/lib-storage/mailbox-list-notify.c0000644000175000017500000000217013147010712017376 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mailbox-list-private.h" #include "mailbox-list-notify.h" int mailbox_list_notify_init(struct mailbox_list *list, enum mailbox_list_notify_event mask, struct mailbox_list_notify **notify_r) { if (list->v.notify_init == NULL) return -1; return list->v.notify_init(list, mask, notify_r); } void mailbox_list_notify_deinit(struct mailbox_list_notify **_notify) { struct mailbox_list_notify *notify = *_notify; *_notify = NULL; notify->list->v.notify_deinit(notify); } int mailbox_list_notify_next(struct mailbox_list_notify *notify, const struct mailbox_list_notify_rec **rec_r) { return notify->list->v.notify_next(notify, rec_r); } #undef mailbox_list_notify_wait void mailbox_list_notify_wait(struct mailbox_list_notify *notify, void (*callback)(void *context), void *context) { notify->list->v.notify_wait(notify, callback, context); } void mailbox_list_notify_flush(struct mailbox_list_notify *notify) { if (notify->list->v.notify_flush != NULL) notify->list->v.notify_flush(notify); } dovecot-2.2.33.2/src/lib-storage/mail-storage.c0000644000175000017500000023105213165463624016071 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "llist.h" #include "str.h" #include "str-sanitize.h" #include "sha1.h" #include "unichar.h" #include "hex-binary.h" #include "file-create-locked.h" #include "istream.h" #include "eacces-error.h" #include "mkdir-parents.h" #include "time-util.h" #include "var-expand.h" #include "dsasl-client.h" #include "imap-date.h" #include "settings-parser.h" #include "mail-index-private.h" #include "mail-index-alloc-cache.h" #include "mailbox-tree.h" #include "mailbox-list-private.h" #include "mail-storage-private.h" #include "mail-storage-settings.h" #include "mail-namespace.h" #include "mail-search.h" #include "mail-search-register.h" #include "mail-search-mime-register.h" #include "mailbox-search-result-private.h" #include "mailbox-guid-cache.h" #include "mail-cache.h" #include #define MAILBOX_DELETE_RETRY_SECS 30 #define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255 extern struct mail_search_register *mail_search_register_imap; extern struct mail_search_register *mail_search_register_human; struct mail_storage_module_register mail_storage_module_register = { 0 }; struct mail_module_register mail_module_register = { 0 }; struct mail_storage_mail_index_module mail_storage_mail_index_module = MODULE_CONTEXT_INIT(&mail_index_module_register); ARRAY_TYPE(mail_storage) mail_storage_classes; static int mail_storage_init_refcount = 0; void mail_storage_init(void) { if (mail_storage_init_refcount++ > 0) return; dsasl_clients_init(); mailbox_attributes_init(); mailbox_lists_init(); mail_storage_hooks_init(); i_array_init(&mail_storage_classes, 8); mail_storage_register_all(); mailbox_list_register_all(); } void mail_storage_deinit(void) { i_assert(mail_storage_init_refcount > 0); if (--mail_storage_init_refcount > 0) return; if (mail_search_register_human != NULL) mail_search_register_deinit(&mail_search_register_human); if (mail_search_register_imap != NULL) mail_search_register_deinit(&mail_search_register_imap); mail_search_mime_register_deinit(); if (array_is_created(&mail_storage_classes)) array_free(&mail_storage_classes); mail_storage_hooks_deinit(); mailbox_lists_deinit(); mailbox_attributes_deinit(); dsasl_clients_deinit(); } void mail_storage_class_register(struct mail_storage *storage_class) { i_assert(mail_storage_find_class(storage_class->name) == NULL); /* append it after the list, so the autodetection order is correct */ array_append(&mail_storage_classes, &storage_class, 1); } void mail_storage_class_unregister(struct mail_storage *storage_class) { struct mail_storage *const *classes; unsigned int i, count; classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (classes[i] == storage_class) { array_delete(&mail_storage_classes, i, 1); break; } } } struct mail_storage *mail_storage_find_class(const char *name) { struct mail_storage *const *classes; unsigned int i, count; i_assert(name != NULL); classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (strcasecmp(classes[i]->name, name) == 0) return classes[i]; } return NULL; } static struct mail_storage * mail_storage_autodetect(const struct mail_namespace *ns, struct mailbox_list_settings *set) { struct mail_storage *const *classes; unsigned int i, count; classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (classes[i]->v.autodetect != NULL) { if (classes[i]->v.autodetect(ns, set)) return classes[i]; } } return NULL; } static void mail_storage_set_autodetection(const char **data, const char **driver) { const char *p; /* check if data is in driver:data format (eg. mbox:~/mail) */ p = *data; while (i_isalnum(*p) || *p == '_') p++; if (*p == ':' && p != *data) { /* no autodetection if the storage driver is given. */ *driver = t_strdup_until(*data, p); *data = p + 1; } } static struct mail_storage * mail_storage_get_class(struct mail_namespace *ns, const char *driver, struct mailbox_list_settings *list_set, enum mail_storage_flags flags, const char **error_r) { struct mail_storage *storage_class = NULL; const char *home; if (driver == NULL) { /* no mail_location, autodetect */ } else if (strcmp(driver, "auto") == 0) { /* explicit autodetection with "auto" driver. */ if (list_set->root_dir != NULL && *list_set->root_dir == '\0') { /* handle the same as with driver=NULL */ list_set->root_dir = NULL; } } else { storage_class = mail_user_get_storage_class(ns->user, driver); if (storage_class == NULL) { *error_r = t_strdup_printf( "Unknown mail storage driver %s", driver); return NULL; } } if (list_set->root_dir == NULL || *list_set->root_dir == '\0') { /* no root directory given. is this allowed? */ const struct mailbox_list *list; list = list_set->layout == NULL ? NULL : mailbox_list_find_class(list_set->layout); if (storage_class == NULL && (flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0) { /* autodetection should take care of this */ } else if (storage_class != NULL && (storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) { /* root not required for this storage */ } else if (list != NULL && (list->props & MAILBOX_LIST_PROP_NO_ROOT) != 0) { /* root not required for this layout */ } else { *error_r = "Root mail directory not given"; return NULL; } } if (storage_class != NULL) { storage_class->v.get_list_settings(ns, list_set); return storage_class; } storage_class = mail_storage_autodetect(ns, list_set); if (storage_class != NULL) return storage_class; (void)mail_user_get_home(ns->user, &home); if (home == NULL || *home == '\0') home = "(not set)"; if (ns->set->location == NULL || *ns->set->location == '\0') { *error_r = t_strdup_printf( "Mail storage autodetection failed with home=%s", home); } else if (strncmp(ns->set->location, "auto:", 5) == 0) { *error_r = t_strdup_printf( "Autodetection failed for %s (home=%s)", ns->set->location, home); } else { *error_r = t_strdup_printf( "Ambiguous mail location setting, " "don't know what to do with it: %s " "(try prefixing it with mbox: or maildir:)", ns->set->location); } return NULL; } static int mail_storage_verify_root(const char *root_dir, const char *dir_type, bool autocreate, const char **error_r) { struct stat st; if (stat(root_dir, &st) == 0) { /* exists */ return 1; } else if (errno == EACCES) { *error_r = mail_error_eacces_msg("stat", root_dir); return -1; } else if (errno != ENOENT && errno != ENOTDIR) { *error_r = t_strdup_printf("stat(%s) failed: %m", root_dir); return -1; } else if (!autocreate) { *error_r = t_strdup_printf( "Root %s directory doesn't exist: %s", dir_type, root_dir); return -1; } else { /* doesn't exist */ return 0; } } static int mail_storage_create_root(struct mailbox_list *list, enum mail_storage_flags flags, const char **error_r) { const char *root_dir, *type_name, *error; enum mailbox_list_path_type type; bool autocreate; int ret; if (list->set.iter_from_index_dir) { type = MAILBOX_LIST_PATH_TYPE_INDEX; type_name = "index"; } else { type = MAILBOX_LIST_PATH_TYPE_MAILBOX; type_name = "mail"; } if (!mailbox_list_get_root_path(list, type, &root_dir)) { /* storage doesn't use directories (e.g. shared root) */ return 0; } if ((flags & MAIL_STORAGE_FLAG_NO_AUTOVERIFY) != 0) { if (!list->mail_set->mail_debug) return 0; /* we don't need to verify, but since debugging is enabled, check and log if the root doesn't exist */ if (mail_storage_verify_root(root_dir, type_name, FALSE, &error) < 0) { i_debug("Namespace %s: Creating storage despite: %s", list->ns->prefix, error); } return 0; } autocreate = (flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) == 0; if (autocreate && list->set.iter_from_index_dir) { /* If the directories don't exist, we'll just autocreate them later. FIXME: Make this the default in v2.3 even when ITERINDEX isn't used. */ return 0; } ret = mail_storage_verify_root(root_dir, type_name, autocreate, error_r); if (ret == 0) { ret = mailbox_list_try_mkdir_root(list, root_dir, MAILBOX_LIST_PATH_TYPE_MAILBOX, error_r); } return ret < 0 ? -1 : 0; } static bool mail_storage_match_class(struct mail_storage *storage, const struct mail_storage *storage_class, const struct mailbox_list_settings *set) { if (strcmp(storage->name, storage_class->name) != 0) return FALSE; if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 && strcmp(storage->unique_root_dir, (set->root_dir != NULL ? set->root_dir : "")) != 0) return FALSE; if (strcmp(storage->name, "shared") == 0) { /* allow multiple independent shared namespaces */ return FALSE; } return TRUE; } static struct mail_storage * mail_storage_find(struct mail_user *user, const struct mail_storage *storage_class, const struct mailbox_list_settings *set) { struct mail_storage *storage = user->storages; for (; storage != NULL; storage = storage->next) { if (mail_storage_match_class(storage, storage_class, set)) return storage; } return NULL; } int mail_storage_create_full(struct mail_namespace *ns, const char *driver, const char *data, enum mail_storage_flags flags, struct mail_storage **storage_r, const char **error_r) { struct mail_storage *storage_class, *storage = NULL; struct mailbox_list *list; struct mailbox_list_settings list_set; enum mailbox_list_flags list_flags = 0; const char *p; if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 && ns->mail_set->pop3_uidl_format != NULL) { /* if pop3_uidl_format contains %m, we want to keep the header MD5 sums stored even if we're not running POP3 right now. */ p = ns->mail_set->pop3_uidl_format; while ((p = strchr(p, '%')) != NULL) { if (p[1] == '%') p += 2; else if (var_get_key(++p) == 'm') { flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5; break; } } } mailbox_list_settings_init_defaults(&list_set); if (data == NULL) { /* autodetect */ } else if (driver != NULL && strcmp(driver, "shared") == 0) { /* internal shared namespace */ list_set.root_dir = ns->user->set->base_dir; } else { if (driver == NULL) mail_storage_set_autodetection(&data, &driver); if (mailbox_list_settings_parse(ns->user, data, &list_set, error_r) < 0) return -1; } storage_class = mail_storage_get_class(ns, driver, &list_set, flags, error_r); if (storage_class == NULL) return -1; i_assert(list_set.layout != NULL); if (ns->list == NULL) { /* first storage for namespace */ if (mail_storage_is_mailbox_file(storage_class)) list_flags |= MAILBOX_LIST_FLAG_MAILBOX_FILES; if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) list_flags |= MAILBOX_LIST_FLAG_NO_MAIL_FILES; if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_LIST_DELETES) != 0) list_flags |= MAILBOX_LIST_FLAG_NO_DELETES; if (mailbox_list_create(list_set.layout, ns, &list_set, list_flags, &list, error_r) < 0) { *error_r = t_strdup_printf("Mailbox list driver %s: %s", list_set.layout, *error_r); return -1; } if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) == 0) { if (mail_storage_create_root(ns->list, flags, error_r) < 0) return -1; } } storage = mail_storage_find(ns->user, storage_class, &list_set); if (storage != NULL) { /* using an existing storage */ storage->refcount++; mail_namespace_add_storage(ns, storage); *storage_r = storage; return 0; } storage = storage_class->v.alloc(); storage->refcount = 1; storage->storage_class = storage_class; storage->user = ns->user; storage->set = ns->mail_set; storage->flags = flags; p_array_init(&storage->module_contexts, storage->pool, 5); if (storage->v.create != NULL && storage->v.create(storage, ns, error_r) < 0) { *error_r = t_strdup_printf("%s: %s", storage->name, *error_r); pool_unref(&storage->pool); return -1; } T_BEGIN { hook_mail_storage_created(storage); } T_END; DLLIST_PREPEND(&ns->user->storages, storage); mail_namespace_add_storage(ns, storage); *storage_r = storage; return 0; } int mail_storage_create(struct mail_namespace *ns, const char *driver, enum mail_storage_flags flags, const char **error_r) { struct mail_storage *storage; return mail_storage_create_full(ns, driver, ns->set->location, flags, &storage, error_r); } void mail_storage_unref(struct mail_storage **_storage) { struct mail_storage *storage = *_storage; i_assert(storage->refcount > 0); /* set *_storage=NULL only after calling destroy() callback. for example mdbox wants to access ns->storage */ if (--storage->refcount > 0) { *_storage = NULL; return; } if (storage->mailboxes != NULL) { i_panic("Trying to deinit storage without freeing mailbox %s", storage->mailboxes->vname); } if (storage->obj_refcount != 0) i_panic("Trying to deinit storage before freeing its objects"); DLLIST_REMOVE(&storage->user->storages, storage); storage->v.destroy(storage); i_free(storage->last_internal_error); i_free(storage->error_string); if (array_is_created(&storage->error_stack)) { i_assert(array_count(&storage->error_stack) == 0); array_free(&storage->error_stack); } *_storage = NULL; pool_unref(&storage->pool); mail_index_alloc_cache_destroy_unrefed(); } void mail_storage_obj_ref(struct mail_storage *storage) { i_assert(storage->refcount > 0); if (storage->obj_refcount++ == 0) mail_user_ref(storage->user); } void mail_storage_obj_unref(struct mail_storage *storage) { i_assert(storage->refcount > 0); i_assert(storage->obj_refcount > 0); if (--storage->obj_refcount == 0) { struct mail_user *user = storage->user; mail_user_unref(&user); } } void mail_storage_clear_error(struct mail_storage *storage) { i_free_and_null(storage->error_string); i_free(storage->last_internal_error); storage->last_error_is_internal = FALSE; storage->error = MAIL_ERROR_NONE; } void mail_storage_set_error(struct mail_storage *storage, enum mail_error error, const char *string) { if (storage->error_string != string) { i_free(storage->error_string); storage->error_string = i_strdup(string); } storage->last_error_is_internal = FALSE; storage->error = error; } void mail_storage_set_internal_error(struct mail_storage *storage) { const char *str; str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time); i_free(storage->error_string); storage->error_string = i_strdup(str); storage->error = MAIL_ERROR_TEMP; /* this function doesn't set last_internal_error, so last_error_is_internal can't be TRUE. */ storage->last_error_is_internal = FALSE; } void mail_storage_set_critical(struct mail_storage *storage, const char *fmt, ...) { char *old_error = storage->error_string; char *old_internal_error = storage->last_internal_error; va_list va; storage->error_string = NULL; storage->last_internal_error = NULL; /* critical errors may contain sensitive data, so let user see only "Internal error" with a timestamp to make it easier to look from log files the actual error message. */ mail_storage_set_internal_error(storage); va_start(va, fmt); storage->last_internal_error = i_strdup_vprintf(fmt, va); va_end(va); storage->last_error_is_internal = TRUE; i_error("%s", storage->last_internal_error); /* free the old_error and old_internal_error only after the new error is generated, because they may be one of the parameters. */ i_free(old_error); i_free(old_internal_error); } const char *mail_storage_get_last_internal_error(struct mail_storage *storage, enum mail_error *error_r) { if (error_r != NULL) *error_r = storage->error; if (storage->last_error_is_internal) { i_assert(storage->last_internal_error != NULL); return storage->last_internal_error; } return mail_storage_get_last_error(storage, error_r); } const char *mailbox_get_last_internal_error(struct mailbox *box, enum mail_error *error_r) { return mail_storage_get_last_internal_error(mailbox_get_storage(box), error_r); } void mail_storage_copy_error(struct mail_storage *dest, struct mail_storage *src) { const char *str; enum mail_error error; if (src == dest) return; str = mail_storage_get_last_error(src, &error); mail_storage_set_error(dest, error, str); } void mail_storage_copy_list_error(struct mail_storage *storage, struct mailbox_list *list) { const char *str; enum mail_error error; str = mailbox_list_get_last_error(list, &error); mail_storage_set_error(storage, error, str); } void mailbox_set_index_error(struct mailbox *box) { if (mail_index_is_deleted(box->index)) { mailbox_set_deleted(box); mail_index_reset_error(box->index); } else { mail_storage_set_index_error(box->storage, box->index); } } void mail_storage_set_index_error(struct mail_storage *storage, struct mail_index *index) { mail_storage_set_internal_error(storage); /* use the lib-index's error as our internal error string */ storage->last_internal_error = i_strdup(mail_index_get_error_message(index)); storage->last_error_is_internal = TRUE; mail_index_reset_error(index); } const struct mail_storage_settings * mail_storage_get_settings(struct mail_storage *storage) { return storage->set; } struct mail_user *mail_storage_get_user(struct mail_storage *storage) { return storage->user; } void mail_storage_set_callbacks(struct mail_storage *storage, struct mail_storage_callbacks *callbacks, void *context) { storage->callbacks = *callbacks; storage->callback_context = context; } int mail_storage_purge(struct mail_storage *storage) { return storage->v.purge == NULL ? 0 : storage->v.purge(storage); } const char *mail_storage_get_last_error(struct mail_storage *storage, enum mail_error *error_r) { /* We get here only in error situations, so we have to return some error. If storage->error is NONE, it means we forgot to set it at some point.. */ if (storage->error == MAIL_ERROR_NONE) { if (error_r != NULL) *error_r = MAIL_ERROR_TEMP; return storage->error_string != NULL ? storage->error_string : "BUG: Unknown internal error"; } if (storage->error_string == NULL) { /* This shouldn't happen.. */ storage->error_string = i_strdup_printf("BUG: Unknown 0x%x error", storage->error); } if (error_r != NULL) *error_r = storage->error; return storage->error_string; } const char *mailbox_get_last_error(struct mailbox *box, enum mail_error *error_r) { return mail_storage_get_last_error(box->storage, error_r); } enum mail_error mailbox_get_last_mail_error(struct mailbox *box) { enum mail_error error; mail_storage_get_last_error(box->storage, &error); return error; } void mail_storage_last_error_push(struct mail_storage *storage) { struct mail_storage_error *err; if (!array_is_created(&storage->error_stack)) i_array_init(&storage->error_stack, 2); err = array_append_space(&storage->error_stack); err->error_string = i_strdup(storage->error_string); err->error = storage->error; err->last_error_is_internal = storage->last_error_is_internal; if (err->last_error_is_internal) err->last_internal_error = i_strdup(storage->last_internal_error); } void mail_storage_last_error_pop(struct mail_storage *storage) { unsigned int count = array_count(&storage->error_stack); const struct mail_storage_error *err = array_idx(&storage->error_stack, count-1); i_free(storage->error_string); i_free(storage->last_internal_error); storage->error_string = err->error_string; storage->error = err->error; storage->last_error_is_internal = err->last_error_is_internal; storage->last_internal_error = err->last_internal_error; array_delete(&storage->error_stack, count-1, 1); } bool mail_storage_is_mailbox_file(struct mail_storage *storage) { return (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE) != 0; } bool mail_storage_set_error_from_errno(struct mail_storage *storage) { const char *error_string; enum mail_error error; if (!mail_error_from_errno(&error, &error_string)) return FALSE; if (storage->set->mail_debug && error != MAIL_ERROR_NOTFOUND) { /* debugging is enabled - admin may be debugging a (permission) problem, so return FALSE to get the caller to log the full error message. */ return FALSE; } mail_storage_set_error(storage, error, error_string); return TRUE; } const struct mailbox_settings * mailbox_settings_find(struct mail_namespace *ns, const char *vname) { struct mailbox_settings *const *box_set; if (!array_is_created(&ns->set->mailboxes)) return NULL; if (ns->prefix_len > 0 && strncmp(ns->prefix, vname, ns->prefix_len-1) == 0) { if (vname[ns->prefix_len-1] == mail_namespace_get_sep(ns)) vname += ns->prefix_len; else if (vname[ns->prefix_len-1] == '\0') { /* namespace prefix itself */ vname = ""; } } array_foreach(&ns->set->mailboxes, box_set) { if (strcmp((*box_set)->name, vname) == 0) return *box_set; } return NULL; } struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct mailbox_list *new_list = list; struct mail_storage *storage; struct mailbox *box; enum mail_error open_error = 0; const char *errstr = NULL; i_assert(uni_utf8_str_is_valid(vname)); if (strncasecmp(vname, "INBOX", 5) == 0 && strncmp(vname, "INBOX", 5) != 0) { /* make sure INBOX shows up in uppercase everywhere. do this regardless of whether we're in inbox=yes namespace, because clients expect INBOX to be case insensitive regardless of server's internal configuration. */ if (vname[5] == '\0') vname = "INBOX"; else if (vname[5] != mail_namespace_get_sep(list->ns)) /* not INBOX prefix */ ; else if (strncasecmp(list->ns->prefix, vname, 6) == 0 && strncmp(list->ns->prefix, "INBOX", 5) != 0) { mailbox_list_set_critical(list, "Invalid server configuration: " "Namespace prefix=%s must be uppercase INBOX", list->ns->prefix); open_error = MAIL_ERROR_TEMP; } else { vname = t_strconcat("INBOX", vname + 5, NULL); } } T_BEGIN { if (mailbox_list_get_storage(&new_list, vname, &storage) < 0) { /* do a delayed failure at mailbox_open() */ storage = mail_namespace_get_default_storage(list->ns); errstr = mailbox_list_get_last_error(new_list, &open_error); errstr = t_strdup(errstr); } box = storage->v.mailbox_alloc(storage, new_list, vname, flags); box->set = mailbox_settings_find(new_list->ns, vname); box->open_error = open_error; if (open_error != 0) mail_storage_set_error(storage, open_error, errstr); hook_mailbox_allocated(box); } T_END; DLLIST_PREPEND(&box->storage->mailboxes, box); mail_storage_obj_ref(box->storage); return box; } struct mailbox *mailbox_alloc_guid(struct mailbox_list *list, const guid_128_t guid, enum mailbox_flags flags) { struct mailbox *box = NULL; struct mailbox_metadata metadata; enum mail_error open_error = MAIL_ERROR_TEMP; const char *vname; if (mailbox_guid_cache_find(list, guid, &vname) < 0) { vname = NULL; } else if (vname != NULL) { box = mailbox_alloc(list, vname, flags); if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { } else if (memcmp(metadata.guid, guid, sizeof(metadata.guid)) != 0) { /* GUID mismatch, refresh cache and try again */ mailbox_free(&box); mailbox_guid_cache_refresh(list); return mailbox_alloc_guid(list, guid, flags); } else { /* successfully opened the correct mailbox */ return box; } i_error("mailbox_alloc_guid(%s): " "Couldn't verify mailbox GUID: %s", guid_128_to_string(guid), mailbox_get_last_internal_error(box, NULL)); vname = NULL; mailbox_free(&box); } else { vname = t_strdup_printf("(nonexistent mailbox with GUID=%s)", guid_128_to_string(guid)); open_error = MAIL_ERROR_NOTFOUND; } if (vname == NULL) { vname = t_strdup_printf("(error in mailbox with GUID=%s)", guid_128_to_string(guid)); } box = mailbox_alloc(list, vname, flags); box->open_error = open_error; return box; } void mailbox_set_reason(struct mailbox *box, const char *reason) { i_assert(reason != NULL); box->reason = p_strdup(box->pool, reason); } bool mailbox_is_autocreated(struct mailbox *box) { if (box->inbox_user) return TRUE; return box->set != NULL && strcmp(box->set->autocreate, MAILBOX_SET_AUTO_NO) != 0; } static int mailbox_autocreate(struct mailbox *box) { const char *errstr; enum mail_error error; if (mailbox_create(box, NULL, FALSE) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_EXISTS) { mail_storage_set_critical(box->storage, "Failed to autocreate mailbox %s: %s", box->vname, errstr); return -1; } } else if (box->set != NULL && strcmp(box->set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0) { if (mailbox_set_subscribed(box, TRUE) < 0) { mail_storage_set_critical(box->storage, "Failed to autosubscribe to mailbox %s: %s", box->vname, mailbox_get_last_internal_error(box, NULL)); return -1; } } return 0; } static int mailbox_autocreate_and_reopen(struct mailbox *box) { int ret; if (mailbox_autocreate(box) < 0) return -1; mailbox_close(box); ret = box->v.open(box); if (ret < 0 && box->inbox_user && !box->storage->user->inbox_open_error_logged) { box->storage->user->inbox_open_error_logged = TRUE; mail_storage_set_critical(box->storage, "Opening INBOX failed: %s", mailbox_get_last_internal_error(box, NULL)); } return ret; } static bool mailbox_name_verify_separators(const char *vname, char sep, const char **error_r) { unsigned int i; bool prev_sep = FALSE; /* Make sure the vname is correct: non-empty, doesn't begin or end with separator and no adjacent separators */ for (i = 0; vname[i] != '\0'; i++) { if (vname[i] == sep) { if (prev_sep) { *error_r = "Has adjacent hierarchy separators"; return FALSE; } prev_sep = TRUE; } else { prev_sep = FALSE; } } if (prev_sep && i > 0) { *error_r = "Ends with hierarchy separator"; return FALSE; } return TRUE; } static int mailbox_verify_name(struct mailbox *box) { struct mail_namespace *ns = box->list->ns; const char *error, *vname = box->vname; char list_sep, ns_sep; if (box->inbox_user) { /* this is INBOX - don't bother with further checks */ return 0; } list_sep = mailbox_list_get_hierarchy_sep(box->list); ns_sep = mail_namespace_get_sep(ns); if (ns->prefix_len > 0) { /* vname is either "namespace/box" or "namespace" */ if (strncmp(vname, ns->prefix, ns->prefix_len-1) != 0 || (vname[ns->prefix_len-1] != '\0' && vname[ns->prefix_len-1] != ns->prefix[ns->prefix_len-1])) { /* User input shouldn't normally be able to get us in here. The main reason this isn't an assert is to allow any input at all to mailbox_verify_*_name() without crashing. */ mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("Invalid mailbox name '%s': " "Missing namespace prefix '%s'", str_sanitize(vname, 80), ns->prefix)); return -1; } vname += ns->prefix_len - 1; if (vname[0] != '\0') { i_assert(vname[0] == ns->prefix[ns->prefix_len-1]); vname++; if (vname[0] == '\0') { /* "namespace/" isn't a valid mailbox name. */ mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Invalid mailbox name"); return -1; } } } if (ns_sep != list_sep && box->list->set.escape_char == '\0' && strchr(vname, list_sep) != NULL) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf( "Character not allowed in mailbox name: '%c'", list_sep)); return -1; } if (vname[0] == ns_sep && !box->storage->set->mail_full_filesystem_access) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Invalid mailbox name: Begins with hierarchy separator"); return -1; } if (!mailbox_name_verify_separators(vname, ns_sep, &error) || !mailbox_list_is_valid_name(box->list, box->name, &error)) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("Invalid mailbox name: %s", error)); return -1; } return 0; } static int mailbox_verify_existing_name(struct mailbox *box) { const char *path; if (box->opened) return 0; if (mailbox_verify_name(box) < 0) return -1; /* Make sure box->_path is set, so mailbox_get_path() works from now on. Note that this may also fail with some backends if the mailbox doesn't exist. */ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) < 0) { if (box->storage->error != MAIL_ERROR_NOTFOUND || !mailbox_is_autocreated(box)) return -1; /* if this is an autocreated mailbox, create it now */ if (mailbox_autocreate(box) < 0) return -1; mailbox_close(box); if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) < 0) return -1; } return 0; } static bool mailbox_name_has_control_chars(const char *name) { const char *p; for (p = name; *p != '\0'; p++) { if ((unsigned char)*p < ' ') return TRUE; } return FALSE; } void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set) { box->skip_create_name_restrictions = set; } int mailbox_verify_create_name(struct mailbox *box) { /* mailbox_alloc() already checks that vname is valid UTF8, so we don't need to verify that. check vname instead of storage name, because vname is what is visible to users, while storage name may be a fixed length GUID. */ if (mailbox_verify_name(box) < 0) return -1; if (box->skip_create_name_restrictions) return 0; if (mailbox_name_has_control_chars(box->vname)) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Control characters not allowed in new mailbox names"); return -1; } if (strlen(box->vname) > MAILBOX_LIST_NAME_MAX_LENGTH) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Mailbox name too long"); return -1; } /* check individual component names, too */ const char *old_name = box->name; const char *name; const char sep = mailbox_list_get_hierarchy_sep(box->list); while((name = strchr(old_name, sep)) != NULL) { if (name - old_name > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Mailbox name too long"); return -1; } name++; old_name = name; } if (strlen(old_name) > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Mailbox name too long"); return -1; } return 0; } static bool have_listable_namespace_prefix(struct mail_namespace *ns, const char *name) { size_t name_len = strlen(name); for (; ns != NULL; ns = ns->next) { if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) == 0) continue; if (ns->prefix_len <= name_len) continue; /* if prefix has multiple hierarchies, match any of the hierarchies */ if (strncmp(ns->prefix, name, name_len) == 0 && ns->prefix[name_len] == mail_namespace_get_sep(ns)) return TRUE; } return FALSE; } int mailbox_exists(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r) { switch (box->open_error) { case 0: break; case MAIL_ERROR_NOTFOUND: *existence_r = MAILBOX_EXISTENCE_NONE; return 0; default: /* unsure if this exists or not */ return -1; } if (mailbox_verify_name(box) < 0) { /* the mailbox name is invalid. we don't know if it currently exists or not, but since it can never be accessed in any way report it as if it didn't exist. */ *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } if (auto_boxes && mailbox_is_autocreated(box)) { *existence_r = MAILBOX_EXISTENCE_SELECT; return 0; } if (box->v.exists(box, auto_boxes, existence_r) < 0) return -1; if (!box->inbox_user && *existence_r == MAILBOX_EXISTENCE_NOSELECT && have_listable_namespace_prefix(box->storage->user->namespaces, box->vname)) { /* listable namespace prefix always exists. */ *existence_r = MAILBOX_EXISTENCE_NOSELECT; return 0; } /* if this is a shared namespace with only INBOX and mail_shared_explicit_inbox=no, we'll need to mark the namespace as usable here since nothing else will. */ box->list->ns->flags |= NAMESPACE_FLAG_USABLE; return 0; } static int ATTR_NULL(2) mailbox_open_full(struct mailbox *box, struct istream *input) { int ret; if (box->opened) return 0; if (box->storage->set->mail_debug && box->reason != NULL) { i_debug("%s: Mailbox opened because: %s", box->vname, box->reason); } switch (box->open_error) { case 0: break; case MAIL_ERROR_NOTFOUND: mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; default: mail_storage_set_internal_error(box->storage); box->storage->error = box->open_error; return -1; } if (mailbox_verify_existing_name(box) < 0) return -1; if (input != NULL) { if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0) { mail_storage_set_critical(box->storage, "Storage doesn't support streamed mailboxes"); return -1; } box->input = input; box->flags |= MAILBOX_FLAG_READONLY; i_stream_ref(box->input); } T_BEGIN { ret = box->v.open(box); } T_END; if (ret < 0 && box->storage->error == MAIL_ERROR_NOTFOUND && !box->deleting && box->input == NULL && mailbox_is_autocreated(box)) T_BEGIN { ret = mailbox_autocreate_and_reopen(box); } T_END; if (ret < 0) { if (box->input != NULL) i_stream_unref(&box->input); return -1; } box->list->ns->flags |= NAMESPACE_FLAG_USABLE; return 0; } static bool mailbox_try_undelete(struct mailbox *box) { time_t mtime; i_assert(!box->mailbox_undeleting); if ((box->flags & MAILBOX_FLAG_READONLY) != 0) { /* most importantly we don't do this because we want to avoid a loop: mdbox storage rebuild -> mailbox_open() -> mailbox_mark_index_deleted() -> mailbox_sync() -> mdbox storage rebuild. */ return FALSE; } if (mail_index_get_modification_time(box->index, &mtime) < 0) return FALSE; if (mtime + MAILBOX_DELETE_RETRY_SECS > time(NULL)) return FALSE; box->mailbox_undeleting = TRUE; int ret = mailbox_mark_index_deleted(box, FALSE); box->mailbox_undeleting = FALSE; if (ret < 0) return FALSE; box->mailbox_deleted = FALSE; return TRUE; } int mailbox_open(struct mailbox *box) { /* check that the storage supports stubs if require them */ if (((box->flags & MAILBOX_FLAG_USE_STUBS) != 0) && ((box->storage->storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_STUBS) == 0)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox does not support mail stubs"); return -1; } if (mailbox_open_full(box, NULL) < 0) { if (!box->mailbox_deleted || box->mailbox_undeleting) return -1; /* mailbox has been marked as deleted. if this deletion started (and crashed) a long time ago, it can be confusing to user that the mailbox can't be opened. so we'll just undelete it and reopen. */ if(!mailbox_try_undelete(box)) return -1; /* make sure we close the mailbox in the middle. some backends may not have fully opened the mailbox while it was being undeleted. */ mailbox_close(box); if (mailbox_open_full(box, NULL) < 0) return -1; } return 0; } static int mailbox_alloc_index_pvt(struct mailbox *box) { const char *index_dir; int ret; if (box->index_pvt != NULL) return 1; ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE, &index_dir); if (ret <= 0) return ret; /* error / no private indexes */ if (mailbox_create_missing_dir(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE) < 0) return -1; box->index_pvt = mail_index_alloc_cache_get(NULL, index_dir, t_strconcat(box->index_prefix, ".pvt", NULL)); mail_index_set_fsync_mode(box->index_pvt, box->storage->set->parsed_fsync_mode, 0); mail_index_set_lock_method(box->index_pvt, box->storage->set->parsed_lock_method, mail_storage_get_lock_timeout(box->storage, UINT_MAX)); return 1; } int mailbox_open_index_pvt(struct mailbox *box) { enum mail_index_open_flags index_flags; int ret; if (box->view_pvt != NULL) return 1; if (mailbox_get_private_flags_mask(box) == 0) return 0; if ((ret = mailbox_alloc_index_pvt(box)) <= 0) return ret; index_flags = MAIL_INDEX_OPEN_FLAG_CREATE | mail_storage_settings_to_index_flags(box->storage->set); if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) index_flags |= MAIL_INDEX_OPEN_FLAG_SAVEONLY; if (mail_index_open(box->index_pvt, index_flags) < 0) return -1; box->view_pvt = mail_index_view_open(box->index_pvt); return 1; } int mailbox_open_stream(struct mailbox *box, struct istream *input) { return mailbox_open_full(box, input); } int mailbox_enable(struct mailbox *box, enum mailbox_feature features) { if (mailbox_verify_name(box) < 0) return -1; return box->v.enable(box, features); } enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box) { return box->enabled_features; } void mail_storage_free_binary_cache(struct mail_storage *storage) { if (storage->binary_cache.box == NULL) return; timeout_remove(&storage->binary_cache.to); i_stream_destroy(&storage->binary_cache.input); i_zero(&storage->binary_cache); } void mailbox_close(struct mailbox *box) { if (!box->opened) return; if (box->transaction_count != 0) { i_panic("Trying to close mailbox %s with open transactions", box->name); } box->v.close(box); if (box->storage->binary_cache.box == box) mail_storage_free_binary_cache(box->storage); box->opened = FALSE; box->mailbox_deleted = FALSE; array_clear(&box->search_results); if (array_is_created(&box->recent_flags)) array_free(&box->recent_flags); box->recent_flags_prev_uid = 0; box->recent_flags_count = 0; } void mailbox_free(struct mailbox **_box) { struct mailbox *box = *_box; *_box = NULL; mailbox_close(box); box->v.free(box); DLLIST_REMOVE(&box->storage->mailboxes, box); mail_storage_obj_unref(box->storage); if (box->metadata_pool != NULL) pool_unref(&box->metadata_pool); pool_unref(&box->pool); } bool mailbox_equals(const struct mailbox *box1, const struct mail_namespace *ns2, const char *vname2) { struct mail_namespace *ns1 = mailbox_get_namespace(box1); const char *name1; if (ns1 != ns2) return FALSE; name1 = mailbox_get_vname(box1); if (strcmp(name1, vname2) == 0) return TRUE; return strcasecmp(name1, "INBOX") == 0 && strcasecmp(vname2, "INBOX") == 0; } bool mailbox_is_any_inbox(struct mailbox *box) { return box->inbox_any; } static void mailbox_copy_cache_decisions_from_inbox(struct mailbox *box) { struct mail_namespace *ns = mail_namespace_find_inbox(box->storage->user->namespaces); struct mailbox *inbox = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); enum mailbox_existence existence; /* this should be NoSelect but since inbox can never be NoSelect we use EXISTENCE_NONE to avoid creating inbox by accident */ mailbox_set_reason(inbox, "copy caching decisions"); if (mailbox_exists(inbox, FALSE, &existence) == 0 && existence != MAILBOX_EXISTENCE_NONE && mailbox_open(inbox) == 0 && mailbox_open(box) == 0) { struct mail_index_transaction *dit = mail_index_transaction_begin(box->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_cache_decisions_copy(dit, inbox->cache, box->cache); /* we can't do much about errors here */ (void)mail_index_transaction_commit(&dit); } mailbox_free(&inbox); } int mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { int ret; if (mailbox_verify_create_name(box) < 0) return -1; box->creating = TRUE; ret = box->v.create_box(box, update, directory); box->creating = FALSE; if (ret == 0) { box->list->guid_cache_updated = TRUE; if (!box->inbox_any) mailbox_copy_cache_decisions_from_inbox(box); } else if (box->opened) { /* Creation failed after (partially) opening the mailbox. It may not be in a valid state, so close it. */ mail_storage_last_error_push(box->storage); mailbox_close(box); mail_storage_last_error_pop(box->storage); } return ret; } int mailbox_update(struct mailbox *box, const struct mailbox_update *update) { int ret; i_assert(update->min_next_uid == 0 || update->min_first_recent_uid == 0 || update->min_first_recent_uid <= update->min_next_uid); if (mailbox_verify_existing_name(box) < 0) return -1; ret = box->v.update_box(box, update); if (!guid_128_is_empty(update->mailbox_guid)) box->list->guid_cache_invalidated = TRUE; return ret; } int mailbox_mark_index_deleted(struct mailbox *box, bool del) { struct mail_index_transaction *trans; enum mail_index_transaction_flags trans_flags = 0; enum mailbox_flags old_flag; int ret; if (box->marked_deleted && del) { /* we already marked it deleted. this allows plugins to "lock" the deletion earlier. */ return 0; } old_flag = box->flags & MAILBOX_FLAG_OPEN_DELETED; box->flags |= MAILBOX_FLAG_OPEN_DELETED; ret = mailbox_open(box); box->flags = (box->flags & ~MAILBOX_FLAG_OPEN_DELETED) | old_flag; if (ret < 0) return -1; trans_flags = del ? 0 : MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; trans = mail_index_transaction_begin(box->view, trans_flags); if (del) mail_index_set_deleted(trans); else mail_index_set_undeleted(trans); if (mail_index_transaction_commit(&trans) < 0) { mailbox_set_index_error(box); return -1; } if (del) { /* sync the mailbox. this finishes the index deletion and it can succeed only for a single session. we do it here, so the rest of the deletion code doesn't have to worry about race conditions. */ box->delete_sync_check = TRUE; ret = mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ); box->delete_sync_check = FALSE; if (ret < 0) return -1; } box->marked_deleted = del; return 0; } static void mailbox_close_reset_path(struct mailbox *box) { i_zero(&box->_perm); box->_path = NULL; box->_index_path = NULL; } int mailbox_delete(struct mailbox *box) { int ret; if (*box->name == '\0') { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Storage root can't be deleted"); return -1; } box->deleting = TRUE; if (mailbox_open(box) < 0) { if (mailbox_get_last_mail_error(box) != MAIL_ERROR_NOTFOUND && !box->mailbox_deleted) return -1; /* might be a \noselect mailbox, so continue deletion */ } ret = box->v.delete_box(box); if (ret < 0 && box->marked_deleted) { /* deletion failed. revert the mark so it can maybe be tried again later. */ if (mailbox_mark_index_deleted(box, FALSE) < 0) return -1; } box->deleting = FALSE; mailbox_close(box); /* if mailbox is reopened, its path may be different with LAYOUT=index */ mailbox_close_reset_path(box); return ret; } int mailbox_delete_empty(struct mailbox *box) { int ret; /* FIXME: should be a parameter to delete(), but since it changes API don't do it for now */ box->deleting_must_be_empty = TRUE; ret = mailbox_delete(box); box->deleting_must_be_empty = FALSE; return ret; } static bool mail_storages_rename_compatible(struct mail_storage *storage1, struct mail_storage *storage2, const char **error_r) { if (storage1 == storage2) return TRUE; if (strcmp(storage1->name, storage2->name) != 0) { *error_r = t_strdup_printf("storage %s != %s", storage1->name, storage2->name); return FALSE; } if ((storage1->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0) { /* e.g. mdbox where all mails are in storage/ directory and they can't be easily moved from there. */ *error_r = t_strdup_printf("storage %s uses unique root", storage1->name); return FALSE; } return TRUE; } static bool nullequals(const void *p1, const void *p2) { return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL); } static bool mailbox_lists_rename_compatible(struct mailbox_list *list1, struct mailbox_list *list2, const char **error_r) { if (!nullequals(list1->set.alt_dir, list2->set.alt_dir)) { *error_r = "one namespace has alt dir and another doesn't"; return FALSE; } if (!nullequals(list1->set.index_dir, list2->set.index_dir)) { *error_r = "one namespace has index dir and another doesn't"; return FALSE; } if (!nullequals(list1->set.control_dir, list2->set.control_dir)) { *error_r = "one namespace has control dir and another doesn't"; return FALSE; } return TRUE; } static int mailbox_rename_check_children(struct mailbox *src, struct mailbox *dest) { int ret = 0; size_t src_prefix_len = strlen(src->vname)+1; /* include separator */ size_t dest_prefix_len = strlen(dest->vname)+1; /* this can return folders with * in their name, that are not actually our children */ const char *pattern = t_strdup_printf("%s%c*", src->vname, mail_namespace_get_sep(src->list->ns)); struct mailbox_list_iterate_context *iter = mailbox_list_iter_init(src->list, pattern, MAILBOX_LIST_ITER_RETURN_NO_FLAGS); const struct mailbox_info *child; while((child = mailbox_list_iter_next(iter)) != NULL) { if (strncmp(child->vname, src->vname, src_prefix_len) != 0) continue; /* not our child */ /* if total length of new name exceeds the limit, fail */ if (strlen(child->vname + src_prefix_len)+dest_prefix_len > MAILBOX_LIST_NAME_MAX_LENGTH) { mail_storage_set_error(dest->storage, MAIL_ERROR_PARAMS, "Mailbox or child name too long"); ret = -1; break; } } /* something went bad */ if (mailbox_list_iter_deinit(&iter) < 0) { mail_storage_copy_list_error(dest->storage, src->list); ret = -1; } return ret; } int mailbox_rename(struct mailbox *src, struct mailbox *dest) { const char *error = NULL; /* Check only name validity, \Noselect don't necessarily exist. */ if (mailbox_verify_name(src) < 0) return -1; if (*src->name == '\0') { mail_storage_set_error(src->storage, MAIL_ERROR_PARAMS, "Can't rename mailbox root"); return -1; } if (mailbox_verify_create_name(dest) < 0) { mail_storage_copy_error(dest->storage, src->storage); return -1; } if (mailbox_rename_check_children(src, dest) != 0) { return -1; } if (!mail_storages_rename_compatible(src->storage, dest->storage, &error) || !mailbox_lists_rename_compatible(src->list, dest->list, &error)) { if (src->storage->set->mail_debug) { i_debug("Can't rename '%s' to '%s': %s", src->vname, dest->vname, error); } mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailboxes across specified storages."); return -1; } if (src->list != dest->list && (src->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE || dest->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE)) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across non-private namespaces."); return -1; } if (src->list == dest->list && strcmp(src->name, dest->name) == 0) { mail_storage_set_error(src->storage, MAIL_ERROR_EXISTS, "Can't rename mailbox to itself."); return -1; } if (src->v.rename_box(src, dest) < 0) return -1; src->list->guid_cache_invalidated = TRUE; dest->list->guid_cache_invalidated = TRUE; return 0; } int mailbox_set_subscribed(struct mailbox *box, bool set) { if (mailbox_verify_name(box) < 0) return -1; if (mailbox_list_iter_subscriptions_refresh(box->list) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if (mailbox_is_subscribed(box) == set) return 0; return box->v.set_subscribed(box, set); } bool mailbox_is_subscribed(struct mailbox *box) { struct mailbox_node *node; i_assert(box->list->subscriptions != NULL); node = mailbox_tree_lookup(box->list->subscriptions, box->vname); return node != NULL && (node->flags & MAILBOX_SUBSCRIBED) != 0; } struct mail_storage *mailbox_get_storage(const struct mailbox *box) { return box->storage; } struct mail_namespace * mailbox_get_namespace(const struct mailbox *box) { return box->list->ns; } const struct mail_storage_settings *mailbox_get_settings(struct mailbox *box) { return box->storage->set; } const char *mailbox_get_name(const struct mailbox *box) { return box->name; } const char *mailbox_get_vname(const struct mailbox *box) { return box->vname; } bool mailbox_is_readonly(struct mailbox *box) { i_assert(box->opened); return box->v.is_readonly(box); } bool mailbox_backends_equal(const struct mailbox *box1, const struct mailbox *box2) { struct mail_namespace *ns1 = box1->list->ns, *ns2 = box2->list->ns; if (strcmp(box1->name, box2->name) != 0) return FALSE; while (ns1->alias_for != NULL) ns1 = ns1->alias_for; while (ns2->alias_for != NULL) ns2 = ns2->alias_for; return ns1 == ns2; } static void mailbox_get_status_set_defaults(struct mailbox *box, struct mailbox_status *status_r) { i_zero(status_r); if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS) != 0) status_r->have_guids = TRUE; if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS) != 0) status_r->have_save_guids = TRUE; if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUID128) != 0) status_r->have_only_guid128 = TRUE; } int mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { mailbox_get_status_set_defaults(box, status_r); if (mailbox_verify_existing_name(box) < 0) return -1; if (box->v.get_status(box, items, status_r) < 0) return -1; i_assert(status_r->have_guids || !status_r->have_save_guids); return 0; } void mailbox_get_open_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { i_assert(box->opened); i_assert((items & MAILBOX_STATUS_FAILING_ITEMS) == 0); mailbox_get_status_set_defaults(box, status_r); if (box->v.get_status(box, items, status_r) < 0) i_unreached(); } int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { i_zero(metadata_r); if (mailbox_verify_existing_name(box) < 0) return -1; if (box->metadata_pool != NULL) p_clear(box->metadata_pool); if (box->v.get_metadata(box, items, metadata_r) < 0) return -1; i_assert((items & MAILBOX_METADATA_GUID) == 0 || !guid_128_is_empty(metadata_r->guid)); return 0; } enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box) { if (box->v.get_private_flags_mask != NULL) return box->v.get_private_flags_mask(box); else if (box->list->set.index_pvt_dir != NULL) return MAIL_SEEN; /* FIXME */ else return 0; } struct mailbox_sync_context * mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct mailbox_sync_context *ctx; if (box->transaction_count != 0) { i_panic("Trying to sync mailbox %s with open transactions", box->name); } if (!box->opened) { if (mailbox_open(box) < 0) { ctx = i_new(struct mailbox_sync_context, 1); ctx->box = box; ctx->flags = flags; ctx->open_failed = TRUE; return ctx; } } T_BEGIN { ctx = box->v.sync_init(box, flags); } T_END; return ctx; } bool mailbox_sync_next(struct mailbox_sync_context *ctx, struct mailbox_sync_rec *sync_rec_r) { if (ctx->open_failed) return FALSE; return ctx->box->v.sync_next(ctx, sync_rec_r); } int mailbox_sync_deinit(struct mailbox_sync_context **_ctx, struct mailbox_sync_status *status_r) { struct mailbox_sync_context *ctx = *_ctx; struct mailbox *box = ctx->box; const char *errormsg; enum mail_error error; int ret; *_ctx = NULL; i_zero(status_r); if (!ctx->open_failed) ret = box->v.sync_deinit(ctx, status_r); else { i_free(ctx); ret = -1; } if (ret < 0 && box->inbox_user && !box->storage->user->inbox_open_error_logged) { errormsg = mailbox_get_last_internal_error(box, &error); if (error == MAIL_ERROR_NOTPOSSIBLE) { box->storage->user->inbox_open_error_logged = TRUE; i_error("Syncing INBOX failed: %s", errormsg); } } if (ret == 0) box->synced = TRUE; return ret; } int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags) { struct mailbox_sync_context *ctx; struct mailbox_sync_status status; if (array_count(&box->search_results) == 0) { /* we don't care about mailbox's current state, so we might as well fix inconsistency state */ flags |= MAILBOX_SYNC_FLAG_FIX_INCONSISTENT; } ctx = mailbox_sync_init(box, flags); return mailbox_sync_deinit(&ctx, &status); } #undef mailbox_notify_changes void mailbox_notify_changes(struct mailbox *box, mailbox_notify_callback_t *callback, void *context) { i_assert(box->opened); box->notify_callback = callback; box->notify_context = context; box->v.notify_changes(box); } void mailbox_notify_changes_stop(struct mailbox *box) { i_assert(box->opened); box->notify_callback = NULL; box->notify_context = NULL; box->v.notify_changes(box); } struct mail_search_context * mailbox_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { i_assert(wanted_headers == NULL || wanted_headers->box == t->box); mail_search_args_ref(args); if (!args->simplified) mail_search_args_simplify(args); return t->box->v.search_init(t, args, sort_program, wanted_fields, wanted_headers); } int mailbox_search_deinit(struct mail_search_context **_ctx) { struct mail_search_context *ctx = *_ctx; struct mail_search_args *args = ctx->args; int ret; *_ctx = NULL; mailbox_search_results_initial_done(ctx); ret = ctx->transaction->box->v.search_deinit(ctx); mail_search_args_unref(&args); return ret; } bool mailbox_search_next(struct mail_search_context *ctx, struct mail **mail_r) { bool tryagain; while (!mailbox_search_next_nonblock(ctx, mail_r, &tryagain)) { if (!tryagain) return FALSE; } return TRUE; } bool mailbox_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r) { struct mailbox *box = ctx->transaction->box; *mail_r = NULL; *tryagain_r = FALSE; if (!box->v.search_next_nonblock(ctx, mail_r, tryagain_r)) return FALSE; else { mailbox_search_results_add(ctx, (*mail_r)->uid); return TRUE; } } bool mailbox_search_seen_lost_data(struct mail_search_context *ctx) { return ctx->seen_lost_data; } int mailbox_search_result_build(struct mailbox_transaction_context *t, struct mail_search_args *args, enum mailbox_search_result_flags flags, struct mail_search_result **result_r) { struct mail_search_context *ctx; struct mail *mail; int ret; ctx = mailbox_search_init(t, args, NULL, 0, NULL); *result_r = mailbox_search_result_save(ctx, flags); while (mailbox_search_next(ctx, &mail)) ; ret = mailbox_search_deinit(&ctx); if (ret < 0) mailbox_search_result_free(result_r); return ret; } struct mailbox_transaction_context * mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct mailbox_transaction_context *trans; i_assert((flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0 || (box->flags & MAILBOX_FLAG_USE_STUBS) != 0); i_assert(box->opened); box->transaction_count++; trans = box->v.transaction_begin(box, flags); trans->flags = flags; return trans; } int mailbox_transaction_commit(struct mailbox_transaction_context **t) { struct mail_transaction_commit_changes changes; int ret; /* Store changes temporarily so that plugins overriding transaction_commit() can look at them. */ ret = mailbox_transaction_commit_get_changes(t, &changes); if (changes.pool != NULL) pool_unref(&changes.pool); return ret; } int mailbox_transaction_commit_get_changes( struct mailbox_transaction_context **_t, struct mail_transaction_commit_changes *changes_r) { struct mailbox_transaction_context *t = *_t; struct mailbox *box = t->box; unsigned int save_count = t->save_count; int ret; changes_r->pool = NULL; *_t = NULL; T_BEGIN { ret = box->v.transaction_commit(t, changes_r); } T_END; /* either all the saved messages get UIDs or none, because a) we failed, b) MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS not set, c) backend doesn't support it (e.g. virtual plugin) */ i_assert(ret < 0 || seq_range_count(&changes_r->saved_uids) == save_count || array_count(&changes_r->saved_uids) == 0); /* decrease the transaction count only after transaction_commit(). that way if it creates and destroys transactions internally, we don't see transaction_count=0 until the parent transaction is fully finished */ box->transaction_count--; if (ret < 0 && changes_r->pool != NULL) pool_unref(&changes_r->pool); return ret; } void mailbox_transaction_rollback(struct mailbox_transaction_context **_t) { struct mailbox_transaction_context *t = *_t; struct mailbox *box = t->box; *_t = NULL; box->v.transaction_rollback(t); box->transaction_count--; } void mailbox_transaction_set_reason(struct mailbox_transaction_context *t, const char *reason) { i_assert(reason != NULL); i_free(t->reason); t->reason = i_strdup(reason); } unsigned int mailbox_transaction_get_count(const struct mailbox *box) { return box->transaction_count; } void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t, uint64_t max_modseq, ARRAY_TYPE(seq_range) *seqs) { mail_index_transaction_set_max_modseq(t->itrans, max_modseq, seqs); } struct mailbox * mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t) { return t->box; } static void mailbox_save_dest_mail_close(struct mail_save_context *ctx) { struct mail_private *mail = (struct mail_private *)ctx->dest_mail; mail->v.close(&mail->mail); } struct mail_save_context * mailbox_save_alloc(struct mailbox_transaction_context *t) { struct mail_save_context *ctx; T_BEGIN { ctx = t->box->v.save_alloc(t); } T_END; i_assert(!ctx->unfinished); ctx->unfinished = TRUE; ctx->data.received_date = (time_t)-1; ctx->data.save_date = (time_t)-1; /* Always have a dest_mail available. A lot of plugins make use of this. */ if (ctx->dest_mail == NULL) ctx->dest_mail = mail_alloc(t, 0, NULL); else { /* make sure the mail isn't used before mail_set_seq_saving() */ mailbox_save_dest_mail_close(ctx); } return ctx; } void mailbox_save_context_deinit(struct mail_save_context *ctx) { i_assert(ctx->dest_mail != NULL); if (!ctx->dest_mail_external) mail_free(&ctx->dest_mail); else ctx->dest_mail = NULL; } void mailbox_save_set_flags(struct mail_save_context *ctx, enum mail_flags flags, struct mail_keywords *keywords) { struct mailbox *box = ctx->transaction->box; if (ctx->data.keywords != NULL) mailbox_keywords_unref(&ctx->data.keywords); ctx->data.flags = flags & ~mailbox_get_private_flags_mask(box); ctx->data.pvt_flags = flags & mailbox_get_private_flags_mask(box); ctx->data.keywords = keywords; if (keywords != NULL) mailbox_keywords_ref(keywords); } void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail) { const char *const *keywords_list; struct mail_keywords *keywords; keywords_list = mail_get_keywords(mail); keywords = str_array_length(keywords_list) == 0 ? NULL : mailbox_keywords_create_valid(ctx->transaction->box, keywords_list); mailbox_save_set_flags(ctx, mail_get_flags(mail), keywords); if (keywords != NULL) mailbox_keywords_unref(&keywords); } void mailbox_save_set_min_modseq(struct mail_save_context *ctx, uint64_t min_modseq) { ctx->data.min_modseq = min_modseq; } void mailbox_save_set_received_date(struct mail_save_context *ctx, time_t received_date, int timezone_offset) { ctx->data.received_date = received_date; ctx->data.received_tz_offset = timezone_offset; } void mailbox_save_set_save_date(struct mail_save_context *ctx, time_t save_date) { ctx->data.save_date = save_date; } void mailbox_save_set_from_envelope(struct mail_save_context *ctx, const char *envelope) { i_free(ctx->data.from_envelope); ctx->data.from_envelope = i_strdup(envelope); } void mailbox_save_set_uid(struct mail_save_context *ctx, uint32_t uid) { ctx->data.uid = uid; if ((ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) != 0) { if (!mail_index_lookup_seq(ctx->transaction->view, uid, &ctx->data.stub_seq)) i_panic("Trying to fill in stub for nonexistent UID %u", uid); } } void mailbox_save_set_guid(struct mail_save_context *ctx, const char *guid) { i_assert(guid == NULL || *guid != '\0'); i_free(ctx->data.guid); ctx->data.guid = i_strdup(guid); } void mailbox_save_set_pop3_uidl(struct mail_save_context *ctx, const char *uidl) { i_assert(*uidl != '\0'); i_assert(strchr(uidl, '\n') == NULL); i_free(ctx->data.pop3_uidl); ctx->data.pop3_uidl = i_strdup(uidl); } void mailbox_save_set_pop3_order(struct mail_save_context *ctx, unsigned int order) { i_assert(order > 0); ctx->data.pop3_order = order; } void mailbox_save_set_dest_mail(struct mail_save_context *ctx, struct mail *mail) { i_assert(mail != NULL); if (!ctx->dest_mail_external) mail_free(&ctx->dest_mail); ctx->dest_mail = mail; ctx->dest_mail_external = TRUE; } struct mail *mailbox_save_get_dest_mail(struct mail_save_context *ctx) { return ctx->dest_mail; } int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input) { struct mailbox *box = (*ctx)->transaction->box; int ret; if (mail_index_is_deleted(box->index)) { mailbox_set_deleted(box); mailbox_save_cancel(ctx); return -1; } /* if we're filling in a stub, we must have set UID already (which in turn sets stub_seq) */ i_assert(((*ctx)->transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0 || (*ctx)->data.stub_seq != 0); if (!(*ctx)->copying_or_moving) { /* We're actually saving the mail. We're not being called by mail_storage_copy() because backend didn't support fast copying. */ i_assert(!(*ctx)->copying_via_save); (*ctx)->saving = TRUE; } else { i_assert((*ctx)->copying_via_save); } if (box->v.save_begin == NULL) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Saving messages not supported"); ret = -1; } else T_BEGIN { ret = box->v.save_begin(*ctx, input); } T_END; if (ret < 0) { mailbox_save_cancel(ctx); return -1; } return 0; } int mailbox_save_continue(struct mail_save_context *ctx) { int ret; T_BEGIN { ret = ctx->transaction->box->v.save_continue(ctx); } T_END; return ret; } static void mailbox_save_add_pvt_flags(struct mailbox_transaction_context *t, enum mail_flags pvt_flags) { struct mail_save_private_changes *save; if (!array_is_created(&t->pvt_saves)) i_array_init(&t->pvt_saves, 8); save = array_append_space(&t->pvt_saves); save->mailnum = t->save_count; save->flags = pvt_flags; } static void mailbox_save_context_reset(struct mail_save_context *ctx, bool success) { i_assert(!ctx->unfinished); if (!ctx->copying_or_moving) { /* we're finishing a save (not copy/move). Note that we could have come here also from mailbox_save_cancel(), in which case ctx->saving may be FALSE. */ i_assert(!ctx->copying_via_save); i_assert(ctx->saving || !success); ctx->saving = FALSE; } else { i_assert(ctx->copying_via_save || !success); /* We came from mailbox_copy(). saving==TRUE is possible here if we also came from mailbox_save_using_mail(). Don't set saving=FALSE yet in that case, because copy() is still running. */ } } int mailbox_save_finish(struct mail_save_context **_ctx) { struct mail_save_context *ctx = *_ctx; struct mailbox_transaction_context *t = ctx->transaction; /* we need to keep a copy of this because save_finish implementations will likely zero the data structure during cleanup */ struct mail_keywords *keywords = ctx->data.keywords; enum mail_flags pvt_flags = ctx->data.pvt_flags; bool copying_via_save = ctx->copying_via_save; int ret; /* Do one final continue. The caller may not have done it if the input stream's offset already matched the number of bytes that were wanted to be saved. But due to nested istreams some of the underlying ones may not have seen the EOF yet, and haven't flushed out the pending data. */ if (mailbox_save_continue(ctx) < 0) { mailbox_save_cancel(_ctx); return -1; } *_ctx = NULL; ctx->finishing = TRUE; T_BEGIN { ret = t->box->v.save_finish(ctx); } T_END; ctx->finishing = FALSE; if (ret == 0 && !copying_via_save) { if (pvt_flags != 0) mailbox_save_add_pvt_flags(t, pvt_flags); t->save_count++; } if (keywords != NULL) mailbox_keywords_unref(&keywords); mailbox_save_context_reset(ctx, TRUE); return ret; } void mailbox_save_cancel(struct mail_save_context **_ctx) { struct mail_save_context *ctx = *_ctx; struct mail_keywords *keywords = ctx->data.keywords; *_ctx = NULL; T_BEGIN { ctx->transaction->box->v.save_cancel(ctx); } T_END; if (keywords != NULL && !ctx->finishing) mailbox_keywords_unref(&keywords); /* the dest_mail is no longer valid. if we're still saving more mails, the mail sequence may get reused. make sure the mail gets reset in between */ mailbox_save_dest_mail_close(ctx); mailbox_save_context_reset(ctx, FALSE); } struct mailbox_transaction_context * mailbox_save_get_transaction(struct mail_save_context *ctx) { return ctx->transaction; } static int mailbox_copy_int(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx; struct mailbox_transaction_context *t = ctx->transaction; struct mail_keywords *keywords = ctx->data.keywords; enum mail_flags pvt_flags = ctx->data.pvt_flags; struct mail *backend_mail; int ret; *_ctx = NULL; if (mail_index_is_deleted(t->box->index)) { mailbox_set_deleted(t->box); mailbox_save_cancel(&ctx); return -1; } /* bypass virtual storage, so hard linking can be used whenever possible */ if (mail_get_backend_mail(mail, &backend_mail) < 0) { mailbox_save_cancel(&ctx); return -1; } i_assert(!ctx->copying_or_moving); i_assert(ctx->copy_src_mail == NULL); ctx->copying_or_moving = TRUE; ctx->copy_src_mail = mail; ctx->finishing = TRUE; T_BEGIN { ret = t->box->v.copy(ctx, backend_mail); } T_END; ctx->finishing = FALSE; if (ret == 0) { if (pvt_flags != 0) mailbox_save_add_pvt_flags(t, pvt_flags); t->save_count++; } if (keywords != NULL) mailbox_keywords_unref(&keywords); i_assert(!ctx->unfinished); ctx->copy_src_mail = NULL; ctx->copying_via_save = FALSE; ctx->copying_or_moving = FALSE; ctx->saving = FALSE; /* if we came from mailbox_save_using_mail() */ return ret; } int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx; i_assert(!ctx->saving); i_assert(!ctx->moving); return mailbox_copy_int(_ctx, mail); } int mailbox_move(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx; int ret; i_assert(!ctx->saving); i_assert(!ctx->moving); ctx->moving = TRUE; if ((ret = mailbox_copy_int(_ctx, mail)) == 0) mail_expunge(mail); ctx->moving = FALSE; return ret; } int mailbox_save_using_mail(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx; i_assert(!ctx->saving); i_assert(!ctx->moving); ctx->saving = TRUE; return mailbox_copy_int(_ctx, mail); } bool mailbox_is_inconsistent(struct mailbox *box) { return box->mailbox_deleted || box->v.is_inconsistent(box); } void mailbox_set_deleted(struct mailbox *box) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox was deleted under us"); box->mailbox_deleted = TRUE; } int mailbox_get_path_to(struct mailbox *box, enum mailbox_list_path_type type, const char **path_r) { int ret; if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX && box->_path != NULL) { if (box->_path[0] == '\0') { *path_r = NULL; return 0; } *path_r = box->_path; return 1; } if (type == MAILBOX_LIST_PATH_TYPE_INDEX && box->_index_path != NULL) { if (box->_index_path[0] == '\0') { *path_r = NULL; return 0; } *path_r = box->_index_path; return 1; } ret = mailbox_list_get_path(box->list, box->name, type, path_r); if (ret < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX && box->_path == NULL) box->_path = ret == 0 ? "" : p_strdup(box->pool, *path_r); if (type == MAILBOX_LIST_PATH_TYPE_INDEX && box->_index_path == NULL) box->_index_path = ret == 0 ? "" : p_strdup(box->pool, *path_r); return ret; } const char *mailbox_get_path(struct mailbox *box) { i_assert(box->_path != NULL); i_assert(box->_path[0] != '\0'); return box->_path; } const char *mailbox_get_index_path(struct mailbox *box) { i_assert(box->_index_path != NULL); i_assert(box->_index_path[0] != '\0'); return box->_index_path; } static void mailbox_get_permissions_if_not_set(struct mailbox *box) { if (box->_perm.file_create_mode != 0) return; if (box->input != NULL) { box->_perm.file_uid = geteuid(); box->_perm.file_create_mode = 0600; box->_perm.dir_create_mode = 0700; box->_perm.file_create_gid = (gid_t)-1; box->_perm.file_create_gid_origin = "defaults"; return; } struct mailbox_permissions perm; mailbox_list_get_permissions(box->list, box->name, &perm); mailbox_permissions_copy(&box->_perm, &perm, box->pool); } const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box) { mailbox_get_permissions_if_not_set(box); if (!box->_perm.mail_index_permissions_set && box->index != NULL) { box->_perm.mail_index_permissions_set = TRUE; mail_index_set_permissions(box->index, box->_perm.file_create_mode, box->_perm.file_create_gid, box->_perm.file_create_gid_origin); } return &box->_perm; } void mailbox_refresh_permissions(struct mailbox *box) { i_zero(&box->_perm); (void)mailbox_get_permissions(box); } int mailbox_create_fd(struct mailbox *box, const char *path, int flags, int *fd_r) { const struct mailbox_permissions *perm = mailbox_get_permissions(box); mode_t old_mask; int fd; i_assert((flags & O_CREAT) != 0); *fd_r = -1; old_mask = umask(0); fd = open(path, flags, perm->file_create_mode); umask(old_mask); if (fd != -1) { /* ok */ } else if (errno == EEXIST) { /* O_EXCL used, caller will handle this error */ return 0; } else if (errno == ENOENT) { mailbox_set_deleted(box); return -1; } else if (errno == ENOTDIR) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox doesn't allow inferior mailboxes"); return -1; } else if (mail_storage_set_error_from_errno(box->storage)) { return -1; } else { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", path); return -1; } if (perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) { /* ok */ } else if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", path); } } *fd_r = fd; return 1; } int mailbox_mkdir(struct mailbox *box, const char *path, enum mailbox_list_path_type type) { const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *root_dir; if (!perm->gid_origin_is_mailbox_path) { /* mailbox root directory doesn't exist, create it */ root_dir = mailbox_list_get_root_forced(box->list, type); if (mailbox_list_mkdir_root(box->list, root_dir, type) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } } if (mkdir_parents_chgrp(path, perm->dir_create_mode, perm->file_create_gid, perm->file_create_gid_origin) == 0) return 1; else if (errno == EEXIST) return 0; else if (errno == ENOTDIR) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox doesn't allow inferior mailboxes"); return -1; } else if (mail_storage_set_error_from_errno(box->storage)) { return -1; } else { mail_storage_set_critical(box->storage, "mkdir_parents(%s) failed: %m", path); return -1; } } int mailbox_create_missing_dir(struct mailbox *box, enum mailbox_list_path_type type) { const char *mail_dir, *dir; struct stat st; int ret; if ((ret = mailbox_get_path_to(box, type, &dir)) <= 0) return ret; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &mail_dir) < 0) return -1; if (null_strcmp(dir, mail_dir) != 0) { /* Mailbox directory is different - create a missing dir */ } else if ((box->list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) != 0) { /* This layout (e.g. imapc) wants to autocreate missing mailbox directories as well. */ } else { /* If the mailbox directory doesn't exist, the mailbox shouldn't exist at all. So just assume that it's already created and if there's a race condition just fail later. */ return 0; } /* we call this function even when the directory exists, so first do a quick check to see if we need to mkdir anything */ if (stat(dir, &st) == 0) return 0; if (null_strcmp(dir, mail_dir) != 0 && stat(mail_dir, &st) < 0 && (errno == ENOENT || errno == ENOTDIR)) { /* Race condition - mail root directory doesn't exist anymore either. We shouldn't create this directory anymore. */ mailbox_set_deleted(box); return -1; } return mailbox_mkdir(box, dir, type); } unsigned int mail_storage_get_lock_timeout(struct mail_storage *storage, unsigned int secs) { return storage->set->mail_max_lock_timeout == 0 ? secs : I_MIN(secs, storage->set->mail_max_lock_timeout); } enum mail_index_open_flags mail_storage_settings_to_index_flags(const struct mail_storage_settings *set) { enum mail_index_open_flags index_flags = 0; #ifndef MMAP_CONFLICTS_WRITE if (set->mmap_disable) #endif index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; if (set->dotlock_use_excl) index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL; if (set->mail_nfs_index) index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH; return index_flags; } int mail_parse_human_timestamp(const char *str, time_t *timestamp_r, bool *utc_r) { struct tm tm; unsigned int secs; const char *error; if (i_isdigit(str[0]) && i_isdigit(str[1]) && i_isdigit(str[2]) && i_isdigit(str[3]) && str[4] == '-' && i_isdigit(str[5]) && i_isdigit(str[6]) && str[7] == '-' && i_isdigit(str[8]) && i_isdigit(str[9]) && str[10] == '\0') { /* yyyy-mm-dd */ i_zero(&tm); tm.tm_year = (str[0]-'0') * 1000 + (str[1]-'0') * 100 + (str[2]-'0') * 10 + (str[3]-'0') - 1900; tm.tm_mon = (str[5]-'0') * 10 + (str[6]-'0') - 1; tm.tm_mday = (str[8]-'0') * 10 + (str[9]-'0'); *timestamp_r = mktime(&tm); *utc_r = FALSE; return 0; } else if (imap_parse_date(str, timestamp_r)) { /* imap date */ *utc_r = FALSE; return 0; } else if (str_to_time(str, timestamp_r) == 0) { /* unix timestamp */ *utc_r = TRUE; return 0; } else if (settings_get_time(str, &secs, &error) == 0) { *timestamp_r = ioloop_time - secs; *utc_r = TRUE; return 0; } else { return -1; } } void mail_set_mail_cache_corrupted(struct mail *mail, const char *fmt, ...) { struct mail_cache_view *cache_view = mail->transaction->cache_view; i_assert(cache_view != NULL); va_list va; va_start(va, fmt); T_BEGIN { mail_cache_set_seq_corrupted_reason(cache_view, mail->seq, t_strdup_printf("UID %u: %s", mail->uid, t_strdup_vprintf(fmt, va))); } T_END; /* update also the storage's internal error */ mailbox_set_index_error(mail->box); va_end(va); } int mailbox_lock_file_create(struct mailbox *box, const char *lock_fname, unsigned int lock_secs, struct file_lock **lock_r, const char **error_r) { const struct mailbox_permissions *perm; struct file_create_settings set; const char *lock_path; bool created; perm = mailbox_get_permissions(box); i_zero(&set); set.lock_timeout_secs = mail_storage_get_lock_timeout(box->storage, lock_secs); set.lock_method = box->storage->set->parsed_lock_method; set.mode = perm->file_create_mode; set.gid = perm->file_create_gid; set.gid_origin = perm->file_create_gid_origin; if (box->list->set.volatile_dir == NULL) lock_path = t_strdup_printf("%s/%s", box->index->dir, lock_fname); else { unsigned char box_name_sha1[SHA1_RESULTLEN]; string_t *str = t_str_new(128); /* Keep this simple: Use the lock_fname with a SHA1 of the mailbox name as the suffix. The mailbox name itself could be too large as a filename and creating the full directory structure would be pretty troublesome. It would also make it more difficult to perform the automated deletion of empty lock directories. */ str_printfa(str, "%s/%s.", box->list->set.volatile_dir, lock_fname); sha1_get_digest(box->name, strlen(box->name), box_name_sha1); binary_to_hex_append(str, box_name_sha1, sizeof(box_name_sha1)); lock_path = str_c(str); set.mkdir_mode = 0700; } if (file_create_locked(lock_path, &set, lock_r, &created, error_r) == -1) { *error_r = t_strdup_printf("file_create_locked(%s) failed: %s", lock_path, *error_r); return errno == EAGAIN ? 0 : -1; } file_lock_set_close_on_free(*lock_r, TRUE); file_lock_set_unlink_on_free(*lock_r, TRUE); return 1; } dovecot-2.2.33.2/src/lib-storage/mail.c0000644000175000017500000002547013165463624014434 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "hash.h" #include "hex-binary.h" #include "crc32.h" #include "sha1.h" #include "hostpid.h" #include "mail-cache.h" #include "mail-storage-private.h" #include struct mail *mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct mail *mail; i_assert(wanted_headers == NULL || wanted_headers->box == t->box); T_BEGIN { mail = t->box->v.mail_alloc(t, wanted_fields, wanted_headers); hook_mail_allocated(mail); } T_END; return mail; } void mail_free(struct mail **mail) { struct mail_private *p = (struct mail_private *)*mail; p->v.free(*mail); *mail = NULL; } void mail_set_seq(struct mail *mail, uint32_t seq) { struct mail_private *p = (struct mail_private *)mail; p->v.set_seq(mail, seq, FALSE); } void mail_set_seq_saving(struct mail *mail, uint32_t seq) { struct mail_private *p = (struct mail_private *)mail; p->v.set_seq(mail, seq, TRUE); } bool mail_set_uid(struct mail *mail, uint32_t uid) { struct mail_private *p = (struct mail_private *)mail; return p->v.set_uid(mail, uid); } bool mail_prefetch(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; bool ret; T_BEGIN { ret = p->v.prefetch(mail); } T_END; return ret; } void mail_add_temp_wanted_fields(struct mail *mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers) { struct mail_private *p = (struct mail_private *)mail; i_assert(headers == NULL || headers->box == mail->box); p->v.add_temp_wanted_fields(mail, fields, headers); } enum mail_flags mail_get_flags(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; return p->v.get_flags(mail); } uint64_t mail_get_modseq(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; return p->v.get_modseq(mail); } uint64_t mail_get_pvt_modseq(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; return p->v.get_pvt_modseq(mail); } const char *const *mail_get_keywords(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; return p->v.get_keywords(mail); } const ARRAY_TYPE(keyword_indexes) *mail_get_keyword_indexes(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; return p->v.get_keyword_indexes(mail); } int mail_get_parts(struct mail *mail, struct message_part **parts_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_parts(mail, parts_r); } T_END; return ret; } int mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_date(mail, date_r, timezone_r); } T_END; return ret; } int mail_get_received_date(struct mail *mail, time_t *date_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_received_date(mail, date_r); } T_END; return ret; } int mail_get_save_date(struct mail *mail, time_t *date_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_save_date(mail, date_r); } T_END; return ret; } int mail_get_virtual_size(struct mail *mail, uoff_t *size_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_virtual_size(mail, size_r); } T_END; return ret; } int mail_get_physical_size(struct mail *mail, uoff_t *size_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_physical_size(mail, size_r); } T_END; return ret; } int mail_get_first_header(struct mail *mail, const char *field, const char **value_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_first_header(mail, field, FALSE, value_r); } T_END; return ret; } int mail_get_first_header_utf8(struct mail *mail, const char *field, const char **value_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_first_header(mail, field, TRUE, value_r); } T_END; return ret; } int mail_get_headers(struct mail *mail, const char *field, const char *const **value_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_headers(mail, field, FALSE, value_r); } T_END; return ret; } int mail_get_headers_utf8(struct mail *mail, const char *field, const char *const **value_r) { struct mail_private *p = (struct mail_private *)mail; int ret; T_BEGIN { ret = p->v.get_headers(mail, field, TRUE, value_r); } T_END; return ret; } int mail_get_header_stream(struct mail *mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r) { struct mail_private *p = (struct mail_private *)mail; int ret; i_assert(headers->count > 0); i_assert(headers->box == mail->box); T_BEGIN { ret = p->v.get_header_stream(mail, headers, stream_r); } T_END; return ret; } void mail_set_aborted(struct mail *mail) { mail_storage_set_error(mail->box->storage, MAIL_ERROR_LOOKUP_ABORTED, "Mail field not cached"); } int mail_get_stream(struct mail *mail, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { return mail_get_stream_because(mail, hdr_size, body_size, "mail stream", stream_r); } int mail_get_stream_because(struct mail *mail, struct message_size *hdr_size, struct message_size *body_size, const char *reason, struct istream **stream_r) { struct mail_private *p = (struct mail_private *)mail; int ret; if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(mail); return -1; } T_BEGIN { p->get_stream_reason = reason; ret = p->v.get_stream(mail, TRUE, hdr_size, body_size, stream_r); p->get_stream_reason = ""; } T_END; return ret; } int mail_get_hdr_stream(struct mail *mail, struct message_size *hdr_size, struct istream **stream_r) { return mail_get_hdr_stream_because(mail, hdr_size, "header stream", stream_r); } int mail_get_hdr_stream_because(struct mail *mail, struct message_size *hdr_size, const char *reason, struct istream **stream_r) { struct mail_private *p = (struct mail_private *)mail; int ret; if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(mail); return -1; } T_BEGIN { p->get_stream_reason = reason; ret = p->v.get_stream(mail, FALSE, hdr_size, NULL, stream_r); p->get_stream_reason = ""; } T_END; return ret; } int mail_get_binary_stream(struct mail *mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, bool *binary_r, struct istream **stream_r) { struct mail_private *p = (struct mail_private *)mail; int ret; if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(mail); return -1; } T_BEGIN { ret = p->v.get_binary_stream(mail, part, include_hdr, size_r, NULL, binary_r, stream_r); } T_END; return ret; } int mail_get_binary_size(struct mail *mail, const struct message_part *part, bool include_hdr, uoff_t *size_r, unsigned int *lines_r) { struct mail_private *p = (struct mail_private *)mail; bool binary; int ret; T_BEGIN { ret = p->v.get_binary_stream(mail, part, include_hdr, size_r, lines_r, &binary, NULL); } T_END; return ret; } int mail_get_special(struct mail *mail, enum mail_fetch_field field, const char **value_r) { struct mail_private *p = (struct mail_private *)mail; if (p->v.get_special(mail, field, value_r) < 0) return -1; i_assert(*value_r != NULL); return 0; } int mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r) { struct mail_private *p = (struct mail_private *)mail; *real_mail_r = p->v.get_real_mail(mail); return *real_mail_r == NULL ? -1 : 0; } struct mail *mail_get_real_mail(struct mail *mail) { struct mail *backend_mail; if (mail_get_backend_mail(mail, &backend_mail) < 0) { i_panic("FIXME: Error occurred in mail_get_real_mail(), " "switch to using mail_get_backend_mail() instead"); } return backend_mail; } void mail_update_flags(struct mail *mail, enum modify_type modify_type, enum mail_flags flags) { struct mail_private *p = (struct mail_private *)mail; p->v.update_flags(mail, modify_type, flags); } void mail_update_keywords(struct mail *mail, enum modify_type modify_type, struct mail_keywords *keywords) { struct mail_private *p = (struct mail_private *)mail; p->v.update_keywords(mail, modify_type, keywords); } void mail_update_modseq(struct mail *mail, uint64_t min_modseq) { struct mail_private *p = (struct mail_private *)mail; p->v.update_modseq(mail, min_modseq); } void mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) { struct mail_private *p = (struct mail_private *)mail; p->v.update_pvt_modseq(mail, min_pvt_modseq); } void mail_update_pop3_uidl(struct mail *mail, const char *uidl) { struct mail_private *p = (struct mail_private *)mail; if (p->v.update_pop3_uidl != NULL) p->v.update_pop3_uidl(mail, uidl); } void mail_expunge(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; T_BEGIN { p->v.expunge(mail); } T_END; } void mail_autoexpunge(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; p->autoexpunged = TRUE; mail_expunge(mail); p->autoexpunged = FALSE; } void mail_set_expunged(struct mail *mail) { mail_storage_set_error(mail->box->storage, MAIL_ERROR_EXPUNGED, "Message was expunged"); mail->expunged = TRUE; } void mail_precache(struct mail *mail) { struct mail_private *p = (struct mail_private *)mail; T_BEGIN { p->v.precache(mail); } T_END; } void mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field) { mail_set_cache_corrupted_reason(mail, field, ""); } void mail_set_cache_corrupted_reason(struct mail *mail, enum mail_fetch_field field, const char *reason) { struct mail_private *p = (struct mail_private *)mail; /* FIXME: v2.3: rename set_cache_corrupted_reason() to just set_cache_corrupted(). we have two here for backwards API compatibility. */ if (p->v.set_cache_corrupted_reason != NULL) p->v.set_cache_corrupted_reason(mail, field, reason); else p->v.set_cache_corrupted(mail, field); } void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r) { unsigned char sha1_sum[SHA1_RESULTLEN]; buffer_t buf; if (guid_128_from_string(guid, guid_128_r) < 0) { /* not 128bit hex. use a hash of it instead. */ buffer_create_from_data(&buf, guid_128_r, GUID_128_SIZE); buffer_set_used_size(&buf, 0); sha1_get_digest(guid, strlen(guid), sha1_sum); #if SHA1_RESULTLEN < DBOX_GUID_BIN_LEN # error not possible #endif buffer_append(&buf, sha1_sum + SHA1_RESULTLEN - GUID_128_SIZE, GUID_128_SIZE); } } dovecot-2.2.33.2/src/lib-storage/mailbox-uidvalidity.h0000644000175000017500000000025313123174404017454 00000000000000#ifndef MAILBOX_UIDVALIDITY_H #define MAILBOX_UIDVALIDITY_H struct mailbox_list; uint32_t mailbox_uidvalidity_next(struct mailbox_list *list, const char *path); #endif dovecot-2.2.33.2/src/lib-storage/mail-storage-settings.h0000644000175000017500000000746113165463624017741 00000000000000#ifndef MAIL_STORAGE_SETTINGS_H #define MAIL_STORAGE_SETTINGS_H #include "file-lock.h" #include "fsync-mode.h" #define MAIL_STORAGE_SET_DRIVER_NAME "MAIL" struct mail_user; struct mail_namespace; struct mail_storage; struct mail_storage_settings { const char *mail_location; const char *mail_attachment_fs; const char *mail_attachment_dir; const char *mail_attachment_hash; uoff_t mail_attachment_min_size; const char *mail_attribute_dict; unsigned int mail_prefetch_count; const char *mail_cache_fields; const char *mail_always_cache_fields; const char *mail_never_cache_fields; const char *mail_server_comment; const char *mail_server_admin; unsigned int mail_cache_min_mail_count; unsigned int mailbox_idle_check_interval; unsigned int mail_max_keyword_length; unsigned int mail_max_lock_timeout; unsigned int mail_temp_scan_interval; unsigned int mail_vsize_bg_after_count; unsigned int mail_sort_max_read_count; bool mail_save_crlf; const char *mail_fsync; bool mmap_disable; bool dotlock_use_excl; bool mail_nfs_storage; bool mail_nfs_index; bool mailbox_list_index; bool mailbox_list_index_very_dirty_syncs; bool mailbox_list_index_include_inbox; bool mail_debug; bool mail_full_filesystem_access; bool maildir_stat_dirs; bool mail_shared_explicit_inbox; const char *lock_method; const char *pop3_uidl_format; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; const char *ssl_crypto_device; enum file_lock_method parsed_lock_method; enum fsync_mode parsed_fsync_mode; }; struct mail_namespace_settings { const char *name; const char *type; const char *separator; const char *prefix; const char *location; const char *alias_for; bool inbox; bool hidden; const char *list; bool subscriptions; bool ignore_on_failure; bool disabled; unsigned int order; ARRAY(struct mailbox_settings *) mailboxes; struct mail_user_settings *user_set; }; /* */ #define MAILBOX_SET_AUTO_NO "no" #define MAILBOX_SET_AUTO_CREATE "create" #define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe" /* */ struct mailbox_settings { const char *name; const char *autocreate; const char *special_use; const char *driver; const char *comment; unsigned int autoexpunge; unsigned int autoexpunge_max_mails; }; struct mail_user_settings { const char *base_dir; const char *auth_socket_path; const char *mail_temp_dir; const char *mail_uid; const char *mail_gid; const char *mail_home; const char *mail_chroot; const char *mail_access_groups; const char *mail_privileged_group; const char *valid_chroot_dirs; unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; const char *mail_plugins; const char *mail_plugin_dir; const char *mail_log_prefix; ARRAY(struct mail_namespace_settings *) namespaces; ARRAY(const char *) plugin_envs; }; extern const struct setting_parser_info mail_user_setting_parser_info; extern const struct setting_parser_info mail_namespace_setting_parser_info; extern const struct setting_parser_info mail_storage_setting_parser_info; extern const struct mail_namespace_settings mail_namespace_default_settings; extern const struct mailbox_settings mailbox_default_settings; const void * mail_user_set_get_driver_settings(const struct setting_parser_info *info, const struct mail_user_settings *set, const char *driver); const struct mail_storage_settings * mail_user_set_get_storage_set(struct mail_user *user); /* Get storage-specific settings, which may be namespace-specific. */ const void *mail_namespace_get_driver_settings(struct mail_namespace *ns, struct mail_storage *storage); /* FIXME: Obsolete - remove in v2.3 */ const void *mail_storage_get_driver_settings(struct mail_storage *storage); const struct dynamic_settings_parser * mail_storage_get_dynamic_parsers(pool_t pool); #endif dovecot-2.2.33.2/src/lib-storage/mail-search-mime-build.h0000644000175000017500000000265113123174404017707 00000000000000#ifndef MAIL_SEARCH_MIME_BUILD_H #define MAIL_SEARCH_MIME_BUILD_H #include "mail-search.h" #include "mail-search-build.h" #include "mail-search-register.h" #include "mail-search-mime.h" struct mailbox; struct mail_search_mime_build_context { struct mail_search_build_context *ctx; struct mail_search_mime_part *mime_part; struct mail_search_mime_arg *parent; }; /* Start building a new MIMPART search key. Use mail_search_mime_args_unref() to free it. */ struct mail_search_mime_part *mail_search_mime_build_init(void); /* Convert IMAP SEARCH command compatible parameters to mail_search_mime_args. */ int mail_search_mime_build(struct mail_search_build_context *bctx, struct mail_search_mime_part **mpart_r); /* Add new search arg with given type. */ struct mail_search_mime_arg * mail_search_mime_build_add(pool_t pool, struct mail_search_mime_part *mpart, enum mail_search_mime_arg_type type); struct mail_search_mime_arg * mail_search_mime_build_new(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type); struct mail_search_mime_arg * mail_search_mime_build_str(struct mail_search_mime_build_context *ctx, enum mail_search_mime_arg_type type); /* Returns 0 if arg is returned, -1 if error. */ int mail_search_mime_build_key(struct mail_search_mime_build_context *ctx, struct mail_search_mime_arg *parent, struct mail_search_mime_arg **arg_r); #endif dovecot-2.2.33.2/src/lib-storage/mail-namespace.h0000644000175000017500000001743213165463624016372 00000000000000#ifndef MAIL_NAMESPACE_H #define MAIL_NAMESPACE_H #include "mail-user.h" struct mail_storage_callbacks; enum mail_namespace_type { MAIL_NAMESPACE_TYPE_PRIVATE = 0x01, MAIL_NAMESPACE_TYPE_SHARED = 0x02, MAIL_NAMESPACE_TYPE_PUBLIC = 0x04 #define MAIL_NAMESPACE_TYPE_MASK_ALL \ (MAIL_NAMESPACE_TYPE_PRIVATE | MAIL_NAMESPACE_TYPE_SHARED | \ MAIL_NAMESPACE_TYPE_PUBLIC) }; enum namespace_flags { /* Namespace contains the user's INBOX mailbox. Normally only a single namespace has this flag set, but when using alias_for for the INBOX namespace the flag gets copied to the alias namespace as well */ NAMESPACE_FLAG_INBOX_USER = 0x01, /* Namespace contains someone's INBOX. This is set for both user's INBOX namespace and also for any other users' shared namespaces. */ NAMESPACE_FLAG_INBOX_ANY = 0x02, /* Namespace is visible only by explicitly using its full prefix */ NAMESPACE_FLAG_HIDDEN = 0x04, /* Namespace prefix is visible with LIST */ NAMESPACE_FLAG_LIST_PREFIX = 0x08, /* Namespace prefix isn't visible with LIST, but child mailboxes are */ NAMESPACE_FLAG_LIST_CHILDREN = 0x10, /* Namespace uses its own subscriptions. */ NAMESPACE_FLAG_SUBSCRIPTIONS = 0x20, /* Namespace was created automatically (for shared mailboxes) */ NAMESPACE_FLAG_AUTOCREATED = 0x1000, /* Namespace has at least some usable mailboxes. Autocreated namespaces that don't have usable mailboxes may be removed automatically. */ NAMESPACE_FLAG_USABLE = 0x2000, /* Automatically created namespace for a user that doesn't exist. */ NAMESPACE_FLAG_UNUSABLE = 0x4000, /* Don't track quota for this namespace */ NAMESPACE_FLAG_NOQUOTA = 0x8000, /* Don't enforce ACLs for this namespace */ NAMESPACE_FLAG_NOACL = 0x10000 }; struct mail_namespace { /* Namespaces are sorted by their prefix length, "" comes first */ struct mail_namespace *next; int refcount; enum mail_namespace_type type; enum namespace_flags flags; char *prefix; size_t prefix_len; /* If non-NULL, this points to a namespace with identical mail location and it should be considered as the primary way to access the mailboxes. This allows for example FTS plugin to avoid duplicating indexes for same mailboxes when they're accessed via different namespaces. */ struct mail_namespace *alias_for; /* alias_for->alias_chain_next starts each chain. The chain goes through all namespaces that have the same alias_for. */ struct mail_namespace *alias_chain_next; struct mail_user *user, *owner; struct mailbox_list *list; struct mail_storage *storage; /* default storage */ ARRAY(struct mail_storage *) all_storages; /* This may point to user->set, but it may also point to namespace-specific settings. When accessing namespace-specific settings it should be done through here instead of through the mail_user. */ struct mail_user_settings *user_set; const struct mail_namespace_settings *set, *unexpanded_set; const struct mail_storage_settings *mail_set; unsigned int special_use_mailboxes:1; unsigned int destroyed:1; }; /* Allocate a new namespace, and fill it based on the passed in settings. This is the most low-level namespace creation function. The storage isn't initialized for the namespace. user_all_settings normally points to user->set. If you want to override settings for the created namespace, you can duplicate the user's settings and provide a pointer to it here. Note that the pointer must contain ALL the settings, including the dynamic driver-specific settings, so it needs to created via settings-parser API. */ int mail_namespace_alloc(struct mail_user *user, void *user_all_settings, struct mail_namespace_settings *ns_set, struct mail_namespace_settings *unexpanded_set, struct mail_namespace **ns_r, const char **error_r); /* Add and initialize namespaces to user based on namespace settings. */ int mail_namespaces_init(struct mail_user *user, const char **error_r); /* Add and initialize INBOX namespace to user based on the given location. */ int mail_namespaces_init_location(struct mail_user *user, const char *location, const char **error_r) ATTR_NULL(2); /* Add an empty namespace to user. */ struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user); /* Deinitialize all namespaces. mail_user_deinit() calls this automatically for user's namespaces. */ void mail_namespaces_deinit(struct mail_namespace **namespaces); /* Allocate a new namespace and initialize it. This is called automatically by mail_namespaces_init(). */ int mail_namespaces_init_add(struct mail_user *user, struct mail_namespace_settings *ns_set, struct mail_namespace_settings *unexpanded_ns_set, struct mail_namespace **ns_p, const char **error_r); int mail_namespaces_init_finish(struct mail_namespace *namespaces, const char **error_r); void mail_namespace_ref(struct mail_namespace *ns); void mail_namespace_unref(struct mail_namespace **ns); /* Set storage callback functions to use in all namespaces. */ void mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces, struct mail_storage_callbacks *callbacks, void *context); /* Add a new storage to namespace. */ void mail_namespace_add_storage(struct mail_namespace *ns, struct mail_storage *storage); /* Destroy a single namespace and remove it from user's namespaces list. */ void mail_namespace_destroy(struct mail_namespace *ns); /* Returns the default storage to use for newly created mailboxes. */ struct mail_storage * mail_namespace_get_default_storage(struct mail_namespace *ns); /* Return namespace's hierarchy separator. */ char mail_namespace_get_sep(struct mail_namespace *ns); /* Returns the hierarchy separator for mailboxes that are listed at root. */ char mail_namespaces_get_root_sep(struct mail_namespace *namespaces) ATTR_PURE; /* Returns namespace based on the mailbox name's prefix. Note that there is always a prefix="" namespace, so for this function NULL is never returned. */ struct mail_namespace * mail_namespace_find(struct mail_namespace *namespaces, const char *mailbox); /* Same as mail_namespace_find(), but if the namespace has alias_for set, return that namespace instead and change mailbox name to be a valid inside it. */ struct mail_namespace * mail_namespace_find_unalias(struct mail_namespace *namespaces, const char **mailbox); /* Like mail_namespace_find(), but ignore hidden namespaces. */ struct mail_namespace * mail_namespace_find_visible(struct mail_namespace *namespaces, const char *mailbox); /* Like mail_namespace_find(), but find only from namespaces with subscriptions=yes. */ struct mail_namespace * mail_namespace_find_subscribable(struct mail_namespace *namespaces, const char *mailbox); /* Like mail_namespace_find(), but find only from namespaces with subscriptions=no. */ struct mail_namespace * mail_namespace_find_unsubscribable(struct mail_namespace *namespaces, const char *mailbox); /* Returns the INBOX namespace. It always exists, so NULL is never returned. */ struct mail_namespace * mail_namespace_find_inbox(struct mail_namespace *namespaces); /* Find a namespace with given prefix. */ struct mail_namespace * mail_namespace_find_prefix(struct mail_namespace *namespaces, const char *prefix); /* Like _find_prefix(), but ignore trailing separator */ struct mail_namespace * mail_namespace_find_prefix_nosep(struct mail_namespace *namespaces, const char *prefix); /* Called internally by mailbox_list_create(). */ void mail_namespace_finish_list_init(struct mail_namespace *ns, struct mailbox_list *list); /* Returns TRUE if this is the root of a type=shared namespace that is actually used for accessing shared users' mailboxes (as opposed to marking a type=public namespace "wrong"). */ bool mail_namespace_is_shared_user_root(struct mail_namespace *ns); #endif dovecot-2.2.33.2/src/lib-storage/mail-storage-settings.c0000644000175000017500000004025513165463624017732 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash-format.h" #include "var-expand.h" #include "unichar.h" #include "settings-parser.h" #include "mail-index.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mail-storage-settings.h" #include static bool mail_storage_settings_check(void *_set, pool_t pool, const char **error_r); static bool namespace_settings_check(void *_set, pool_t pool, const char **error_r); static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r); static bool mail_user_settings_check(void *_set, pool_t pool, const char **error_r); #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mail_storage_settings, name), NULL } static const struct setting_define mail_storage_setting_defines[] = { DEF(SET_STR_VARS, mail_location), { SET_ALIAS, "mail", 0, NULL }, DEF(SET_STR_VARS, mail_attachment_fs), DEF(SET_STR_VARS, mail_attachment_dir), DEF(SET_STR, mail_attachment_hash), DEF(SET_SIZE, mail_attachment_min_size), DEF(SET_STR_VARS, mail_attribute_dict), DEF(SET_UINT, mail_prefetch_count), DEF(SET_STR, mail_cache_fields), DEF(SET_STR, mail_always_cache_fields), DEF(SET_STR, mail_never_cache_fields), DEF(SET_STR, mail_server_comment), DEF(SET_STR, mail_server_admin), DEF(SET_UINT, mail_cache_min_mail_count), DEF(SET_TIME, mailbox_idle_check_interval), DEF(SET_UINT, mail_max_keyword_length), DEF(SET_TIME, mail_max_lock_timeout), DEF(SET_TIME, mail_temp_scan_interval), DEF(SET_UINT, mail_vsize_bg_after_count), DEF(SET_UINT, mail_sort_max_read_count), DEF(SET_BOOL, mail_save_crlf), DEF(SET_ENUM, mail_fsync), DEF(SET_BOOL, mmap_disable), DEF(SET_BOOL, dotlock_use_excl), DEF(SET_BOOL, mail_nfs_storage), DEF(SET_BOOL, mail_nfs_index), DEF(SET_BOOL, mailbox_list_index), DEF(SET_BOOL, mailbox_list_index_very_dirty_syncs), DEF(SET_BOOL, mailbox_list_index_include_inbox), DEF(SET_BOOL, mail_debug), DEF(SET_BOOL, mail_full_filesystem_access), DEF(SET_BOOL, maildir_stat_dirs), DEF(SET_BOOL, mail_shared_explicit_inbox), DEF(SET_ENUM, lock_method), DEF(SET_STR, pop3_uidl_format), DEF(SET_STR, ssl_client_ca_dir), DEF(SET_STR, ssl_client_ca_file), DEF(SET_STR, ssl_crypto_device), SETTING_DEFINE_LIST_END }; const struct mail_storage_settings mail_storage_default_settings = { .mail_location = "", .mail_attachment_fs = "sis posix", .mail_attachment_dir = "", .mail_attachment_hash = "%{sha1}", .mail_attachment_min_size = 1024*128, .mail_attribute_dict = "", .mail_prefetch_count = 0, .mail_cache_fields = "flags", .mail_always_cache_fields = "", .mail_never_cache_fields = "imap.envelope", .mail_server_comment = "", .mail_server_admin = "", .mail_cache_min_mail_count = 0, .mailbox_idle_check_interval = 30, .mail_max_keyword_length = 50, .mail_max_lock_timeout = 0, .mail_temp_scan_interval = 7*24*60*60, .mail_vsize_bg_after_count = 0, .mail_sort_max_read_count = 0, .mail_save_crlf = FALSE, .mail_fsync = "optimized:never:always", .mmap_disable = FALSE, .dotlock_use_excl = TRUE, .mail_nfs_storage = FALSE, .mail_nfs_index = FALSE, .mailbox_list_index = FALSE, .mailbox_list_index_very_dirty_syncs = FALSE, .mailbox_list_index_include_inbox = FALSE, .mail_debug = FALSE, .mail_full_filesystem_access = FALSE, .maildir_stat_dirs = FALSE, .mail_shared_explicit_inbox = FALSE, .lock_method = "fcntl:flock:dotlock", .pop3_uidl_format = "%08Xu%08Xv", .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .ssl_crypto_device = "" }; const struct setting_parser_info mail_storage_setting_parser_info = { .module_name = "mail", .defines = mail_storage_setting_defines, .defaults = &mail_storage_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mail_storage_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = mail_storage_settings_check }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mailbox_settings, name), NULL } static const struct setting_define mailbox_setting_defines[] = { DEF(SET_STR, name), { SET_ENUM, "auto", offsetof(struct mailbox_settings, autocreate), NULL } , DEF(SET_STR, special_use), DEF(SET_STR, driver), DEF(SET_STR, comment), DEF(SET_TIME, autoexpunge), DEF(SET_UINT, autoexpunge_max_mails), SETTING_DEFINE_LIST_END }; const struct mailbox_settings mailbox_default_settings = { .name = "", .autocreate = MAILBOX_SET_AUTO_NO":" MAILBOX_SET_AUTO_CREATE":" MAILBOX_SET_AUTO_SUBSCRIBE, .special_use = "", .driver = "", .comment = "", .autoexpunge = 0, .autoexpunge_max_mails = 0 }; const struct setting_parser_info mailbox_setting_parser_info = { .defines = mailbox_setting_defines, .defaults = &mailbox_default_settings, .type_offset = offsetof(struct mailbox_settings, name), .struct_size = sizeof(struct mailbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = mailbox_settings_check }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct mail_namespace_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, \ offsetof(struct mail_namespace_settings, field), defines } static const struct setting_define mail_namespace_setting_defines[] = { DEF(SET_STR, name), DEF(SET_ENUM, type), DEF(SET_STR, separator), DEF(SET_STR_VARS, prefix), DEF(SET_STR_VARS, location), { SET_ALIAS, "mail", 0, NULL }, { SET_ALIAS, "mail_location", 0, NULL }, DEF(SET_STR_VARS, alias_for), DEF(SET_BOOL, inbox), DEF(SET_BOOL, hidden), DEF(SET_ENUM, list), DEF(SET_BOOL, subscriptions), DEF(SET_BOOL, ignore_on_failure), DEF(SET_BOOL, disabled), DEF(SET_UINT, order), DEFLIST_UNIQUE(mailboxes, "mailbox", &mailbox_setting_parser_info), SETTING_DEFINE_LIST_END }; const struct mail_namespace_settings mail_namespace_default_settings = { .name = "", .type = "private:shared:public", .separator = "", .prefix = "", .location = "", .alias_for = NULL, .inbox = FALSE, .hidden = FALSE, .list = "yes:no:children", .subscriptions = TRUE, .ignore_on_failure = FALSE, .disabled = FALSE, .order = 0, .mailboxes = ARRAY_INIT }; const struct setting_parser_info mail_namespace_setting_parser_info = { .defines = mail_namespace_setting_defines, .defaults = &mail_namespace_default_settings, .type_offset = offsetof(struct mail_namespace_settings, name), .struct_size = sizeof(struct mail_namespace_settings), .parent_offset = offsetof(struct mail_namespace_settings, user_set), .parent = &mail_user_setting_parser_info, .check_func = namespace_settings_check }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct mail_user_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, \ offsetof(struct mail_user_settings, field), defines } static const struct setting_define mail_user_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, auth_socket_path), DEF(SET_STR_VARS, mail_temp_dir), DEF(SET_STR, mail_uid), DEF(SET_STR, mail_gid), DEF(SET_STR_VARS, mail_home), DEF(SET_STR_VARS, mail_chroot), DEF(SET_STR, mail_access_groups), DEF(SET_STR, mail_privileged_group), DEF(SET_STR, valid_chroot_dirs), DEF(SET_UINT, first_valid_uid), DEF(SET_UINT, last_valid_uid), DEF(SET_UINT, first_valid_gid), DEF(SET_UINT, last_valid_gid), DEF(SET_STR, mail_plugins), DEF(SET_STR, mail_plugin_dir), DEF(SET_STR, mail_log_prefix), DEFLIST_UNIQUE(namespaces, "namespace", &mail_namespace_setting_parser_info), { SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; static const struct mail_user_settings mail_user_default_settings = { .base_dir = PKG_RUNDIR, .auth_socket_path = "auth-userdb", .mail_temp_dir = "/tmp", .mail_uid = "", .mail_gid = "", .mail_home = "", .mail_chroot = "", .mail_access_groups = "", .mail_privileged_group = "", .valid_chroot_dirs = "", .first_valid_uid = 500, .last_valid_uid = 0, .first_valid_gid = 1, .last_valid_gid = 0, .mail_plugins = "", .mail_plugin_dir = MODULEDIR, .mail_log_prefix = "%s(%u): ", .namespaces = ARRAY_INIT, .plugin_envs = ARRAY_INIT }; const struct setting_parser_info mail_user_setting_parser_info = { .module_name = "mail", .defines = mail_user_setting_defines, .defaults = &mail_user_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mail_user_settings), .parent_offset = (size_t)-1, .check_func = mail_user_settings_check }; const void * mail_user_set_get_driver_settings(const struct setting_parser_info *info, const struct mail_user_settings *set, const char *driver) { const void *dset; dset = settings_find_dynamic(info, set, driver); if (dset == NULL) { i_panic("Default settings not found for storage driver %s", driver); } return dset; } const struct mail_storage_settings * mail_user_set_get_storage_set(struct mail_user *user) { return mail_user_set_get_driver_settings(user->set_info, user->set, MAIL_STORAGE_SET_DRIVER_NAME); } const void *mail_storage_get_driver_settings(struct mail_storage *storage) { return mail_user_set_get_driver_settings(storage->user->set_info, storage->user->set, storage->name); } const void *mail_namespace_get_driver_settings(struct mail_namespace *ns, struct mail_storage *storage) { return mail_user_set_get_driver_settings(storage->user->set_info, ns->user_set, storage->name); } const struct dynamic_settings_parser * mail_storage_get_dynamic_parsers(pool_t pool) { struct dynamic_settings_parser *parsers; struct mail_storage *const *storages; unsigned int i, j, count; storages = array_get(&mail_storage_classes, &count); parsers = p_new(pool, struct dynamic_settings_parser, 1 + count + 1); parsers[0].name = MAIL_STORAGE_SET_DRIVER_NAME; parsers[0].info = &mail_storage_setting_parser_info; for (i = 0, j = 1; i < count; i++) { if (storages[i]->v.get_setting_parser_info == NULL) continue; parsers[j].name = storages[i]->name; parsers[j].info = storages[i]->v.get_setting_parser_info(); j++; } parsers[j].name = NULL; return parsers; } static void fix_base_path(struct mail_user_settings *set, pool_t pool, const char **str) { if (*str != NULL && **str != '\0' && **str != '/') *str = p_strconcat(pool, set->base_dir, "/", *str, NULL); } /* */ static bool mail_storage_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct mail_storage_settings *set = _set; struct hash_format *format; const char *p, *error; bool uidl_format_ok; char c; if (set->mailbox_idle_check_interval == 0) { *error_r = "mailbox_idle_check_interval must not be 0"; return FALSE; } if (strcmp(set->mail_fsync, "optimized") == 0) set->parsed_fsync_mode = FSYNC_MODE_OPTIMIZED; else if (strcmp(set->mail_fsync, "never") == 0) set->parsed_fsync_mode = FSYNC_MODE_NEVER; else if (strcmp(set->mail_fsync, "always") == 0) set->parsed_fsync_mode = FSYNC_MODE_ALWAYS; else { *error_r = t_strdup_printf("Unknown mail_fsync: %s", set->mail_fsync); return FALSE; } if (set->mail_nfs_index && !set->mmap_disable) { *error_r = "mail_nfs_index=yes requires mmap_disable=yes"; return FALSE; } if (set->mail_nfs_index && set->parsed_fsync_mode != FSYNC_MODE_ALWAYS) { *error_r = "mail_nfs_index=yes requires mail_fsync=always"; return FALSE; } if (!file_lock_method_parse(set->lock_method, &set->parsed_lock_method)) { *error_r = t_strdup_printf("Unknown lock_method: %s", set->lock_method); return FALSE; } uidl_format_ok = FALSE; for (p = set->pop3_uidl_format; *p != '\0'; p++) { if (p[0] != '%' || p[1] == '\0') continue; c = var_get_key(++p); switch (c) { case 'v': case 'u': case 'm': case 'f': case 'g': uidl_format_ok = TRUE; break; case '%': break; default: *error_r = t_strdup_printf( "Unknown pop3_uidl_format variable: %%%c", c); return FALSE; } } if (!uidl_format_ok) { *error_r = "pop3_uidl_format setting doesn't contain any " "%% variables."; return FALSE; } if (strchr(set->mail_attachment_hash, '/') != NULL) { *error_r = "mail_attachment_hash setting " "must not contain '/' characters"; return FALSE; } if (hash_format_init(set->mail_attachment_hash, &format, &error) < 0) { *error_r = t_strconcat("Invalid mail_attachment_hash setting: ", error, NULL); return FALSE; } if (strchr(set->mail_attachment_hash, '-') != NULL) { *error_r = "mail_attachment_hash setting " "must not contain '-' characters"; return FALSE; } hash_format_deinit_free(&format); #ifndef CONFIG_BINARY if (*set->ssl_client_ca_dir != '\0' && access(set->ssl_client_ca_dir, X_OK) < 0) { *error_r = t_strdup_printf( "ssl_client_ca_dir: access(%s) failed: %m", set->ssl_client_ca_dir); return FALSE; } #endif // FIXME: check set->mail_server_admin syntax (RFC 5464, Section 6.2.2) return TRUE; } static bool namespace_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct mail_namespace_settings *ns = _set; struct mail_namespace_settings *const *namespaces; const char *name; unsigned int i, count; name = ns->prefix != NULL ? ns->prefix : ""; if (ns->separator[0] != '\0' && ns->separator[1] != '\0') { *error_r = t_strdup_printf("Namespace '%s': " "Hierarchy separator must be only one character long", name); return FALSE; } if (!uni_utf8_str_is_valid(name)) { *error_r = t_strdup_printf("Namespace prefix not valid UTF8: %s", name); return FALSE; } if (ns->alias_for != NULL && !ns->disabled) { if (array_is_created(&ns->user_set->namespaces)) { namespaces = array_get(&ns->user_set->namespaces, &count); } else { namespaces = NULL; count = 0; } for (i = 0; i < count; i++) { if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0) break; } if (i == count) { *error_r = t_strdup_printf( "Namespace '%s': alias_for points to " "unknown namespace: %s", name, ns->alias_for); return FALSE; } if (namespaces[i]->alias_for != NULL) { *error_r = t_strdup_printf( "Namespace '%s': alias_for chaining isn't " "allowed: %s -> %s", name, ns->alias_for, namespaces[i]->alias_for); return FALSE; } } return TRUE; } static bool mailbox_special_use_exists(const char *name) { if (name[0] != '\\') return FALSE; name++; if (strcasecmp(name, "All") == 0) return TRUE; if (strcasecmp(name, "Archive") == 0) return TRUE; if (strcasecmp(name, "Drafts") == 0) return TRUE; if (strcasecmp(name, "Flagged") == 0) return TRUE; if (strcasecmp(name, "Junk") == 0) return TRUE; if (strcasecmp(name, "Sent") == 0) return TRUE; if (strcasecmp(name, "Trash") == 0) return TRUE; return FALSE; } static bool mailbox_special_use_check(struct mailbox_settings *set, pool_t pool, const char **error_r) { const char *const *uses, *str; unsigned int i; uses = t_strsplit_spaces(set->special_use, " "); for (i = 0; uses[i] != NULL; i++) { if (!mailbox_special_use_exists(uses[i])) { *error_r = t_strdup_printf( "mailbox %s: unknown special_use: %s", set->name, uses[i]); return FALSE; } } /* make sure there are no extra spaces */ str = t_strarray_join(uses, " "); if (strcmp(str, set->special_use) != 0) set->special_use = p_strdup(pool, str); return TRUE; } static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r) { struct mailbox_settings *set = _set; if (!uni_utf8_str_is_valid(set->name)) { *error_r = t_strdup_printf("mailbox %s: name isn't valid UTF-8", set->name); return FALSE; } if (*set->special_use != '\0') { if (!mailbox_special_use_check(set, pool, error_r)) return FALSE; } return TRUE; } static bool mail_user_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r ATTR_UNUSED) { struct mail_user_settings *set = _set; #ifndef CONFIG_BINARY fix_base_path(set, pool, &set->auth_socket_path); #else if (*set->mail_plugins != '\0' && access(set->mail_plugin_dir, R_OK | X_OK) < 0) { *error_r = t_strdup_printf( "mail_plugin_dir: access(%s) failed: %m", set->mail_plugin_dir); return FALSE; } #endif return TRUE; } /* */ dovecot-2.2.33.2/src/lib-storage/mailbox-guid-cache.c0000644000175000017500000000542313123174404017115 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "mail-storage.h" #include "mailbox-list-private.h" #include "mailbox-guid-cache.h" struct mailbox_guid_cache_rec { guid_128_t guid; const char *vname; }; int mailbox_guid_cache_find(struct mailbox_list *list, const guid_128_t guid, const char **vname_r) { const struct mailbox_guid_cache_rec *rec; const uint8_t *guid_p = guid; if (!hash_table_is_created(list->guid_cache) || list->guid_cache_invalidated) { mailbox_guid_cache_refresh(list); rec = hash_table_lookup(list->guid_cache, guid_p); } else { rec = hash_table_lookup(list->guid_cache, guid_p); if (rec == NULL && list->guid_cache_updated) { mailbox_guid_cache_refresh(list); rec = hash_table_lookup(list->guid_cache, guid_p); } } if (rec == NULL) { *vname_r = NULL; return list->guid_cache_errors ? -1 : 0; } *vname_r = rec->vname; return 0; } void mailbox_guid_cache_refresh(struct mailbox_list *list) { struct mailbox_list_iterate_context *ctx; const struct mailbox_info *info; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_guid_cache_rec *rec; uint8_t *guid_p; if (!hash_table_is_created(list->guid_cache)) { list->guid_cache_pool = pool_alloconly_create("guid cache", 1024*16); hash_table_create(&list->guid_cache, list->guid_cache_pool, 0, guid_128_hash, guid_128_cmp); } else { hash_table_clear(list->guid_cache, TRUE); p_clear(list->guid_cache_pool); } list->guid_cache_invalidated = FALSE; list->guid_cache_updated = FALSE; list->guid_cache_errors = FALSE; ctx = mailbox_list_iter_init(list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(ctx)) != NULL) { if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) continue; box = mailbox_alloc(list, info->vname, 0); if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("Couldn't get mailbox %s GUID: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); list->guid_cache_errors = TRUE; } else if ((rec = hash_table_lookup(list->guid_cache, (const uint8_t *)metadata.guid)) != NULL) { i_warning("Mailbox %s has duplicate GUID with %s: %s", info->vname, rec->vname, guid_128_to_string(metadata.guid)); } else { rec = p_new(list->guid_cache_pool, struct mailbox_guid_cache_rec, 1); memcpy(rec->guid, metadata.guid, sizeof(rec->guid)); rec->vname = p_strdup(list->guid_cache_pool, info->vname); guid_p = rec->guid; hash_table_insert(list->guid_cache, guid_p, rec); } mailbox_free(&box); } if (mailbox_list_iter_deinit(&ctx) < 0) list->guid_cache_errors = TRUE; } dovecot-2.2.33.2/src/lib-storage/mailbox-list.c0000644000175000017500000015615013165463624016116 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "abspath.h" #include "ioloop.h" #include "file-create-locked.h" #include "mkdir-parents.h" #include "str.h" #include "sha1.h" #include "hash.h" #include "home-expand.h" #include "time-util.h" #include "unichar.h" #include "settings-parser.h" #include "iostream-ssl.h" #include "fs-api-private.h" #include "imap-utf7.h" #include "mailbox-log.h" #include "mailbox-tree.h" #include "mail-storage-private.h" #include "mail-storage-hooks.h" #include "mailbox-list-private.h" #include #include #include #include #include #define MAILBOX_LIST_FS_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_list_fs_module) struct mailbox_list_fs_context { union fs_api_module_context module_ctx; struct mailbox_list *list; }; struct mailbox_list_module_register mailbox_list_module_register = { 0 }; static ARRAY(const struct mailbox_list *) mailbox_list_drivers; static MODULE_CONTEXT_DEFINE_INIT(mailbox_list_fs_module, &fs_api_module_register); void mailbox_lists_init(void) { i_array_init(&mailbox_list_drivers, 4); } void mailbox_lists_deinit(void) { array_free(&mailbox_list_drivers); } static bool mailbox_list_driver_find(const char *name, unsigned int *idx_r) { const struct mailbox_list *const *drivers; unsigned int i, count; drivers = array_get(&mailbox_list_drivers, &count); for (i = 0; i < count; i++) { if (strcasecmp(drivers[i]->name, name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } void mailbox_list_register(const struct mailbox_list *list) { unsigned int idx; if (mailbox_list_driver_find(list->name, &idx)) { i_fatal("mailbox_list_register(%s): duplicate driver", list->name); } array_append(&mailbox_list_drivers, &list, 1); } void mailbox_list_unregister(const struct mailbox_list *list) { unsigned int idx; if (!mailbox_list_driver_find(list->name, &idx)) { i_fatal("mailbox_list_unregister(%s): unknown driver", list->name); } array_delete(&mailbox_list_drivers, idx, 1); } const struct mailbox_list * mailbox_list_find_class(const char *driver) { const struct mailbox_list *const *class_p; unsigned int idx; if (!mailbox_list_driver_find(driver, &idx)) return NULL; class_p = array_idx(&mailbox_list_drivers, idx); return *class_p; } int mailbox_list_create(const char *driver, struct mail_namespace *ns, const struct mailbox_list_settings *set, enum mailbox_list_flags flags, struct mailbox_list **list_r, const char **error_r) { const struct mailbox_list *class; struct mailbox_list *list; i_assert(ns->list == NULL || (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0); i_assert(set->subscription_fname == NULL || *set->subscription_fname != '\0'); if ((class = mailbox_list_find_class(driver)) == NULL) { *error_r = "Unknown driver name"; return -1; } if ((class->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 && *set->maildir_name != '\0') { *error_r = "maildir_name not supported by this driver"; return -1; } if ((class->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 && set->alt_dir != NULL) { *error_r = "alt_dir not supported by this driver"; return -1; } i_assert(set->root_dir == NULL || *set->root_dir != '\0' || (class->props & MAILBOX_LIST_PROP_NO_ROOT) != 0); list = class->v.alloc(); array_create(&list->module_contexts, list->pool, sizeof(void *), 5); list->ns = ns; list->mail_set = ns->mail_set; list->flags = flags; list->root_permissions.file_create_mode = (mode_t)-1; list->root_permissions.dir_create_mode = (mode_t)-1; list->root_permissions.file_create_gid = (gid_t)-1; list->changelog_timestamp = (time_t)-1; if (set->no_noselect) list->props |= MAILBOX_LIST_PROP_NO_NOSELECT; /* copy settings */ if (set->root_dir != NULL) { list->set.root_dir = p_strdup(list->pool, set->root_dir); list->set.index_dir = set->index_dir == NULL || strcmp(set->index_dir, set->root_dir) == 0 ? NULL : p_strdup(list->pool, set->index_dir); list->set.index_pvt_dir = set->index_pvt_dir == NULL || strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL : p_strdup(list->pool, set->index_pvt_dir); list->set.control_dir = set->control_dir == NULL || strcmp(set->control_dir, set->root_dir) == 0 ? NULL : p_strdup(list->pool, set->control_dir); } list->set.inbox_path = p_strdup(list->pool, set->inbox_path); list->set.subscription_fname = p_strdup(list->pool, set->subscription_fname); list->set.list_index_fname = p_strdup(list->pool, set->list_index_fname); list->set.list_index_dir = p_strdup(list->pool, set->list_index_dir); list->set.maildir_name = p_strdup(list->pool, set->maildir_name); list->set.mailbox_dir_name = p_strdup(list->pool, set->mailbox_dir_name); list->set.alt_dir = p_strdup(list->pool, set->alt_dir); list->set.alt_dir_nocheck = set->alt_dir_nocheck; list->set.volatile_dir = p_strdup(list->pool, set->volatile_dir); list->set.index_control_use_maildir_name = set->index_control_use_maildir_name; list->set.iter_from_index_dir = set->iter_from_index_dir; list->set.no_noselect = set->no_noselect; if (*set->mailbox_dir_name == '\0') list->set.mailbox_dir_name = ""; else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') { list->set.mailbox_dir_name = p_strdup(list->pool, set->mailbox_dir_name); } else { list->set.mailbox_dir_name = p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL); } list->set.escape_char = set->escape_char; list->set.broken_char = set->broken_char; list->set.utf8 = set->utf8; if (list->v.init != NULL) { if (list->v.init(list, error_r) < 0) { list->v.deinit(list); return -1; } } if (ns->mail_set->mail_debug) { i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s", list->name, list->set.root_dir == NULL ? "" : list->set.root_dir, list->set.index_dir == NULL ? "" : list->set.index_dir, list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir, list->set.control_dir == NULL ? "" : list->set.control_dir, list->set.inbox_path == NULL ? "" : list->set.inbox_path, list->set.alt_dir == NULL ? "" : list->set.alt_dir); } if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0) mail_namespace_finish_list_init(ns, list); *list_r = list; hook_mailbox_list_created(list); return 0; } static int fix_path(struct mail_user *user, const char *path, bool expand_home, const char **path_r, const char **error_r) { size_t len = strlen(path); if (len > 1 && path[len-1] == '/') path = t_strndup(path, len-1); if (!expand_home) { /* no ~ expansion */ } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') { /* ~otheruser/dir */ if (home_try_expand(&path) < 0) { *error_r = t_strconcat( "No home directory for system user. " "Can't expand ", t_strcut(path, '/'), " for ", NULL); return -1; } } else { if (mail_user_try_home_expand(user, &path) < 0) { *error_r = "Home directory not set for user. " "Can't expand ~/ for "; return -1; } } *path_r = path; return 0; } static const char *split_next_arg(const char *const **_args) { const char *const *args = *_args; const char *str = args[0]; args++; while (*args != NULL && **args == '\0') { args++; if (*args == NULL) { /* string ends with ":", just ignore it. */ break; } str = t_strconcat(str, ":", *args, NULL); args++; } *_args = args; return str; } void mailbox_list_settings_init_defaults(struct mailbox_list_settings *set_r) { i_zero(set_r); set_r->mailbox_dir_name = ""; set_r->maildir_name = ""; set_r->list_index_fname = MAILBOX_LIST_INDEX_DEFAULT_PREFIX; } static int mailbox_list_settings_parse_full(struct mail_user *user, const char *data, bool expand_home, struct mailbox_list_settings *set_r, const char **error_r) { const char *const *tmp, *key, *value, **dest, *str, *fname, *error; *error_r = NULL; mailbox_list_settings_init_defaults(set_r); if (*data == '\0') return 0; /* */ tmp = t_strsplit(data, ":"); str = split_next_arg(&tmp); if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) { *error_r = t_strconcat(error, "mail root dir in: ", data, NULL); return -1; } if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) { /* probably mbox user trying to avoid root_dir */ *error_r = t_strconcat("Mail root directory not given: ", data, NULL); return -1; } while (*tmp != NULL) { str = split_next_arg(&tmp); if (strcmp(str, "UTF-8") == 0) { set_r->utf8 = TRUE; continue; } value = strchr(str, '='); if (value == NULL) { key = str; value = ""; } else { key = t_strdup_until(str, value); value++; } if (strcmp(key, "INBOX") == 0) dest = &set_r->inbox_path; else if (strcmp(key, "INDEX") == 0) dest = &set_r->index_dir; else if (strcmp(key, "INDEXPVT") == 0) dest = &set_r->index_pvt_dir; else if (strcmp(key, "CONTROL") == 0) dest = &set_r->control_dir; else if (strcmp(key, "ALT") == 0) dest = &set_r->alt_dir; else if (strcmp(key, "ALTNOCHECK") == 0) { set_r->alt_dir_nocheck = TRUE; continue; } else if (strcmp(key, "LAYOUT") == 0) dest = &set_r->layout; else if (strcmp(key, "SUBSCRIPTIONS") == 0) dest = &set_r->subscription_fname; else if (strcmp(key, "DIRNAME") == 0) dest = &set_r->maildir_name; else if (strcmp(key, "MAILBOXDIR") == 0) dest = &set_r->mailbox_dir_name; else if (strcmp(key, "VOLATILEDIR") == 0) dest = &set_r->volatile_dir; else if (strcmp(key, "LISTINDEX") == 0) dest = &set_r->list_index_fname; else if (strcmp(key, "FULLDIRNAME") == 0) { set_r->index_control_use_maildir_name = TRUE; dest = &set_r->maildir_name; } else if (strcmp(key, "BROKENCHAR") == 0) { if (strlen(value) != 1) { *error_r = "BROKENCHAR value must be a single character"; return -1; } set_r->broken_char = value[0]; continue; } else if (strcmp(key, "ITERINDEX") == 0) { set_r->iter_from_index_dir = TRUE; continue; } else if (strcmp(key, "NO-NOSELECT") == 0) { set_r->no_noselect = TRUE; continue; } else { *error_r = t_strdup_printf("Unknown setting: %s", key); return -1; } if (fix_path(user, value, expand_home, dest, &error) < 0) { *error_r = t_strconcat(error, key, " in: ", data, NULL); return -1; } } if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0) set_r->index_dir = ""; if (set_r->iter_from_index_dir && (set_r->index_dir == NULL || set_r->index_dir[0] == '\0')) { *error_r = "ITERINDEX requires INDEX to be explicitly set"; return -1; } if (set_r->list_index_fname != NULL && (fname = strrchr(set_r->list_index_fname, '/')) != NULL) { /* non-default LISTINDEX directory */ set_r->list_index_dir = t_strdup_until(set_r->list_index_fname, fname); set_r->list_index_fname = fname+1; if (set_r->list_index_dir[0] != '/' && set_r->index_dir != NULL && set_r->index_dir[0] == '\0') { *error_r = "LISTINDEX directory is relative but INDEX=MEMORY"; return -1; } } return 0; } int mailbox_list_settings_parse(struct mail_user *user, const char *data, struct mailbox_list_settings *set_r, const char **error_r) { return mailbox_list_settings_parse_full(user, data, TRUE, set_r, error_r); } const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list, enum mailbox_list_path_type type) { const struct mail_storage_settings *mail_set; const char *location = list->ns->unexpanded_set->location; struct mail_user *user = list->ns->user; struct mailbox_list_settings set; const char *p, *path, *error; if (*location == SETTING_STRVAR_EXPANDED[0]) { /* set using -o or userdb lookup. */ return ""; } i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]); location++; if (*location == '\0') { mail_set = mail_user_set_get_driver_settings(user->set_info, user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME); i_assert(mail_set != NULL); location = mail_set->mail_location; if (*location == SETTING_STRVAR_EXPANDED[0]) return ""; i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]); location++; } /* type:settings */ p = strchr(location, ':'); if (p == NULL) return ""; if (mailbox_list_settings_parse_full(user, p + 1, FALSE, &set, &error) < 0) return ""; if (mailbox_list_set_get_root_path(&set, type, &path) <= 0) return ""; return path; } static bool need_escape_dirstart(const char *vname, const char *maildir_name) { size_t len; if (vname[0] == '.') { if (vname[1] == '\0' || vname[1] == '/') return TRUE; /* "." */ if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/')) return TRUE; /* ".." */ } if (*maildir_name != '\0') { len = strlen(maildir_name); if (strncmp(maildir_name, vname, len) == 0 && (vname[len] == '\0' || vname[len] == '/')) return TRUE; /* e.g. dbox-Mails */ } return FALSE; } const char * mailbox_list_escape_name_params(const char *vname, const char *ns_prefix, char ns_sep, char list_sep, char escape_char, const char *maildir_name) { size_t ns_prefix_len = strlen(ns_prefix); string_t *escaped_name = t_str_new(64); char dirstart = TRUE; /* no escaping of namespace prefix */ if (strncmp(ns_prefix, vname, ns_prefix_len) == 0) { str_append_n(escaped_name, vname, ns_prefix_len); vname += ns_prefix_len; } /* escape the mailbox name */ if (*vname == '~') { str_printfa(escaped_name, "%c%02x", escape_char, *vname); vname++; dirstart = FALSE; } for (; *vname != '\0'; vname++) { if (*vname == ns_sep) str_append_c(escaped_name, list_sep); else if (*vname == list_sep || *vname == escape_char || *vname == '/' || (dirstart && need_escape_dirstart(vname, maildir_name))) { str_printfa(escaped_name, "%c%02x", escape_char, *vname); } else { str_append_c(escaped_name, *vname); } dirstart = *vname == '/'; } return str_c(escaped_name); } const char * mailbox_list_escape_name(struct mailbox_list *list, const char *vname) { return mailbox_list_escape_name_params(vname, list->ns->prefix, mail_namespace_get_sep(list->ns), mailbox_list_get_hierarchy_sep(list), list->set.escape_char, list->set.maildir_name); } static int mailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name) { char *src, *dest; unsigned char chr; if ((src = strchr(name, list->set.broken_char)) == NULL) return 0; dest = src; while (*src != '\0') { if (*src == list->set.broken_char) { if (src[1] >= '0' && src[1] <= '9') chr = (src[1]-'0') * 0x10; else if (src[1] >= 'a' && src[1] <= 'f') chr = (src[1]-'a' + 10) * 0x10; else return -1; if (src[2] >= '0' && src[2] <= '9') chr += src[2]-'0'; else if (src[2] >= 'a' && src[2] <= 'f') chr += src[2]-'a' + 10; else return -1; *dest++ = chr; src += 3; } else { *dest++ = *src++; } } *dest++ = '\0'; return 0; } static char *mailbox_list_convert_sep(const char *storage_name, char src, char dest) { char *ret, *p; ret = p_strdup(unsafe_data_stack_pool, storage_name); for (p = ret; *p != '\0'; p++) { if (*p == src) *p = dest; } return ret; } const char *mailbox_list_default_get_storage_name(struct mailbox_list *list, const char *vname) { struct mail_namespace *ns = list->ns; size_t prefix_len = strlen(ns->prefix); const char *storage_name = vname; string_t *str; char list_sep, ns_sep, *ret; if (strcasecmp(storage_name, "INBOX") == 0 && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) storage_name = "INBOX"; else if (list->set.escape_char != '\0') storage_name = mailbox_list_escape_name(list, vname); if (prefix_len > 0 && (strcmp(storage_name, "INBOX") != 0 || (ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)) { /* skip namespace prefix, except if this is INBOX */ if (strncmp(ns->prefix, storage_name, prefix_len) == 0) storage_name += prefix_len; else if (strncmp(ns->prefix, storage_name, prefix_len-1) == 0 && strlen(storage_name) == prefix_len-1 && ns->prefix[prefix_len-1] == mail_namespace_get_sep(ns)) { /* trying to access the namespace prefix itself */ storage_name = ""; } else { /* we're converting a nonexistent mailbox name, such as a LIST pattern. */ } } if (!list->set.utf8) { /* UTF-8 -> mUTF-7 conversion */ str = t_str_new(strlen(storage_name)*2); if (imap_utf8_to_utf7(storage_name, str) < 0) i_panic("Mailbox name not UTF-8: %s", vname); storage_name = str_c(str); } list_sep = mailbox_list_get_hierarchy_sep(list); ns_sep = mail_namespace_get_sep(ns); if (*storage_name == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED && (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && !list->mail_set->mail_shared_explicit_inbox) { /* opening shared/$user. it's the same as INBOX. */ storage_name = "INBOX"; } if (list_sep != ns_sep && list->set.escape_char == '\0') { if (ns->type == MAIL_NAMESPACE_TYPE_SHARED && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) { /* shared namespace root. the backend storage's hierarchy separator isn't known yet, so do nothing. */ return storage_name; } ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep); } else if (list->set.broken_char == '\0' || strchr(storage_name, list->set.broken_char) == NULL) { /* no need to convert broken chars */ return storage_name; } else { ret = p_strdup(unsafe_data_stack_pool, storage_name); } if (list->set.broken_char != '\0') { if (mailbox_list_unescape_broken_chars(list, ret) < 0) { ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep); } } return ret; } const char *mailbox_list_get_storage_name(struct mailbox_list *list, const char *vname) { return list->v.get_storage_name(list, vname); } const char * mailbox_list_unescape_name_params(const char *src, const char *ns_prefix, char ns_sep, char list_sep, char escape_char) { size_t ns_prefix_len = strlen(ns_prefix); string_t *dest = t_str_new(strlen(src)); unsigned int num; if (strncmp(src, ns_prefix, ns_prefix_len) == 0) { str_append_n(dest, src, ns_prefix_len); src += ns_prefix_len; } for (; *src != '\0'; src++) { if (*src == escape_char && i_isxdigit(src[1]) && i_isxdigit(src[2])) { if (src[1] >= '0' && src[1] <= '9') num = src[1] - '0'; else num = i_toupper(src[1]) - 'A' + 10; num *= 16; if (src[2] >= '0' && src[2] <= '9') num += src[2] - '0'; else num += i_toupper(src[2]) - 'A' + 10; str_append_c(dest, num); src += 2; } else if (*src == list_sep) str_append_c(dest, ns_sep); else str_append_c(dest, *src); } return str_c(dest); } const char * mailbox_list_unescape_name(struct mailbox_list *list, const char *src) { return mailbox_list_unescape_name_params(src, list->ns->prefix, mail_namespace_get_sep(list->ns), mailbox_list_get_hierarchy_sep(list), list->set.escape_char); } static void mailbox_list_escape_broken_chars(struct mailbox_list *list, string_t *str) { unsigned int i; char buf[3]; if (strchr(str_c(str), list->set.broken_char) == NULL) return; for (i = 0; i < str_len(str); i++) { if (str_c(str)[i] == list->set.broken_char) { i_snprintf(buf, sizeof(buf), "%02x", list->set.broken_char); str_insert(str, i+1, buf); i += 2; } } } static void mailbox_list_escape_broken_name(struct mailbox_list *list, const char *vname, string_t *str) { str_truncate(str, 0); for (; *vname != '\0'; vname++) { if (*vname == '&' || (unsigned char)*vname >= 0x80) { str_printfa(str, "%c%02x", list->set.broken_char, (unsigned char)*vname); } else { str_append_c(str, *vname); } } } const char *mailbox_list_default_get_vname(struct mailbox_list *list, const char *storage_name) { size_t i, prefix_len, name_len; const char *vname = storage_name; char list_sep, ns_sep, *ret; if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && strcmp(vname, "INBOX") == 0 && list->ns->user == list->ns->owner) { /* user's INBOX - use as-is. NOTE: don't do case-insensitive comparison, otherwise we can't differentiate between INBOX and /inBox. */ return vname; } if (strcmp(vname, "INBOX") == 0 && list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && !list->mail_set->mail_shared_explicit_inbox) { /* convert to shared/$user, we don't really care about the INBOX suffix here. */ vname = ""; } if (*vname == '\0') { /* return namespace prefix without the separator */ if (list->ns->prefix_len == 0) return list->ns->prefix; else { return t_strndup(list->ns->prefix, list->ns->prefix_len - 1); } } else if (!list->set.utf8) { /* mUTF-7 -> UTF-8 conversion */ string_t *str = t_str_new(strlen(vname)); if (imap_utf7_to_utf8(vname, str) == 0) { if (list->set.broken_char != '\0') mailbox_list_escape_broken_chars(list, str); vname = str_c(str); } else if (list->set.broken_char != '\0') { mailbox_list_escape_broken_name(list, vname, str); vname = str_c(str); } } prefix_len = strlen(list->ns->prefix); if (list->set.escape_char != '\0') { vname = mailbox_list_unescape_name(list, vname); return prefix_len == 0 ? vname : t_strconcat(list->ns->prefix, vname, NULL); } list_sep = mailbox_list_get_hierarchy_sep(list); ns_sep = mail_namespace_get_sep(list->ns); if (list_sep != ns_sep || prefix_len > 0) { /* @UNSAFE */ name_len = strlen(vname); ret = t_malloc(MALLOC_ADD(prefix_len, name_len) + 1); memcpy(ret, list->ns->prefix, prefix_len); for (i = 0; i < name_len; i++) { ret[i + prefix_len] = vname[i] == list_sep ? ns_sep : vname[i]; } ret[i + prefix_len] = '\0'; vname = ret; } return vname; } const char *mailbox_list_get_vname(struct mailbox_list *list, const char *name) { return list->v.get_vname(list, name); } void mailbox_list_destroy(struct mailbox_list **_list) { struct mailbox_list *list = *_list; *_list = NULL; i_free_and_null(list->error_string); i_free(list->last_internal_error); if (hash_table_is_created(list->guid_cache)) { hash_table_destroy(&list->guid_cache); pool_unref(&list->guid_cache_pool); } if (list->subscriptions != NULL) mailbox_tree_deinit(&list->subscriptions); if (list->changelog != NULL) mailbox_log_free(&list->changelog); if (array_is_created(&list->error_stack)) { i_assert(array_count(&list->error_stack) == 0); array_free(&list->error_stack); } list->v.deinit(list); } const char *mailbox_list_get_driver_name(const struct mailbox_list *list) { return list->name; } const struct mailbox_list_settings * mailbox_list_get_settings(const struct mailbox_list *list) { return &list->set; } enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list) { return list->flags; } struct mail_namespace * mailbox_list_get_namespace(const struct mailbox_list *list) { return list->ns; } static mode_t get_dir_mode(mode_t mode) { /* add the execute bit if either read or write bit is set */ if ((mode & 0600) != 0) mode |= 0100; if ((mode & 0060) != 0) mode |= 0010; if ((mode & 0006) != 0) mode |= 0001; return mode; } struct mail_user * mailbox_list_get_user(const struct mailbox_list *list) { return list->ns->user; } static int mailbox_list_get_storage_driver(struct mailbox_list *list, const char *driver, struct mail_storage **storage_r) { struct mail_storage *const *storagep; const char *error, *data; array_foreach(&list->ns->all_storages, storagep) { if (strcmp((*storagep)->name, driver) == 0) { *storage_r = *storagep; return 0; } } data = strchr(list->ns->set->location, ':'); if (data == NULL) data = ""; else data++; if (mail_storage_create_full(list->ns, driver, data, 0, storage_r, &error) < 0) { mailbox_list_set_critical(list, "Namespace %s: Failed to create storage '%s': %s", list->ns->prefix, driver, error); return -1; } return 0; } int mailbox_list_get_storage(struct mailbox_list **list, const char *vname, struct mail_storage **storage_r) { const struct mailbox_settings *set; if ((*list)->v.get_storage != NULL) return (*list)->v.get_storage(list, vname, storage_r); set = mailbox_settings_find((*list)->ns, vname); if (set != NULL && set->driver != NULL && set->driver[0] != '\0') { return mailbox_list_get_storage_driver(*list, set->driver, storage_r); } *storage_r = mail_namespace_get_default_storage((*list)->ns); return 0; } void mailbox_list_get_default_storage(struct mailbox_list *list, struct mail_storage **storage) { *storage = mail_namespace_get_default_storage(list->ns); } char mailbox_list_get_hierarchy_sep(struct mailbox_list *list) { /* the current API doesn't allow returning an error, so imap code looks at the list's last error. make sure the error is cleared so the error-check doesn't return something irrelevant */ mailbox_list_clear_error(list); return list->v.get_hierarchy_sep(list); } static bool mailbox_list_get_permissions_stat(struct mailbox_list *list, const char *path, struct mailbox_permissions *permissions_r) { struct stat st; if (stat(path, &st) < 0) { if (errno == EACCES) { mailbox_list_set_critical(list, "%s", mail_error_eacces_msg("stat", path)); } else if (!ENOTFOUND(errno)) { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); } else if (list->mail_set->mail_debug) { i_debug("Namespace %s: %s doesn't exist yet, " "using default permissions", list->ns->prefix, path); } return FALSE; } permissions_r->file_uid = st.st_uid; permissions_r->file_gid = st.st_gid; permissions_r->file_create_mode = (st.st_mode & 0666) | 0600; permissions_r->dir_create_mode = (st.st_mode & 0777) | 0700; permissions_r->file_create_gid_origin = path; if (!S_ISDIR(st.st_mode)) { /* we're getting permissions from a file. apply +x modes as necessary. */ permissions_r->dir_create_mode = get_dir_mode(permissions_r->dir_create_mode); } if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { /* directory's GID is used automatically for new files */ permissions_r->file_create_gid = (gid_t)-1; } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { /* group has same permissions as world, so don't bother changing it */ permissions_r->file_create_gid = (gid_t)-1; } else if (getegid() == st.st_gid) { /* using our own gid, no need to change it */ permissions_r->file_create_gid = (gid_t)-1; } else { permissions_r->file_create_gid = st.st_gid; } if (!S_ISDIR(st.st_mode) && permissions_r->file_create_gid != (gid_t)-1) { /* we need to stat() the parent directory to see if it has setgid-bit set */ const char *p = strrchr(path, '/'); const char *parent_path = p == NULL ? NULL : t_strdup_until(path, p); if (parent_path != NULL && stat(parent_path, &st) == 0 && (st.st_mode & S_ISGID) != 0) { /* directory's GID is used automatically for new files */ permissions_r->file_create_gid = (gid_t)-1; } } return TRUE; } static void ATTR_NULL(2) mailbox_list_get_permissions_internal(struct mailbox_list *list, const char *name, struct mailbox_permissions *permissions_r) { const char *path = NULL, *parent_name, *p; i_zero(permissions_r); /* use safe defaults */ permissions_r->file_uid = (uid_t)-1; permissions_r->file_gid = (gid_t)-1; permissions_r->file_create_mode = 0600; permissions_r->dir_create_mode = 0700; permissions_r->file_create_gid = (gid_t)-1; permissions_r->file_create_gid_origin = "defaults"; if (list->set.iter_from_index_dir || (list->flags & MAILBOX_LIST_FLAG_NO_MAIL_FILES) != 0) { /* a) iterating from index dir. Use the index dir's permissions as well, since they might be in a faster storage. b) mail files don't exist in storage, but index files might. */ (void)mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &path); } if (name != NULL && path == NULL) { if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) < 0) name = NULL; } if (name == NULL && path == NULL) { (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR, &path); } if (path == NULL) { /* no filesystem support in storage */ } else if (mailbox_list_get_permissions_stat(list, path, permissions_r)) { /* got permissions from the given path */ permissions_r->gid_origin_is_mailbox_path = name != NULL; } else if (name != NULL) { /* path couldn't be stat()ed, try parent mailbox */ p = strrchr(name, mailbox_list_get_hierarchy_sep(list)); if (p == NULL) { /* return root defaults */ parent_name = NULL; } else { parent_name = t_strdup_until(name, p); } mailbox_list_get_permissions(list, parent_name, permissions_r); return; } else { /* assume current defaults for mailboxes that don't exist or can't be looked up for some other reason */ permissions_r->file_uid = geteuid(); permissions_r->file_gid = getegid(); } if (name == NULL) { mailbox_permissions_copy(&list->root_permissions, permissions_r, list->pool); } if (list->mail_set->mail_debug && name == NULL) { i_debug("Namespace %s: Using permissions from %s: " "mode=0%o gid=%s", list->ns->prefix, path != NULL ? path : "", (int)permissions_r->dir_create_mode, permissions_r->file_create_gid == (gid_t)-1 ? "default" : dec2str(permissions_r->file_create_gid)); } } void mailbox_list_get_permissions(struct mailbox_list *list, const char *name, struct mailbox_permissions *permissions_r) { mailbox_list_get_permissions_internal(list, name, permissions_r); } void mailbox_list_get_root_permissions(struct mailbox_list *list, struct mailbox_permissions *permissions_r) { if (list->root_permissions.file_create_mode != (mode_t)-1) *permissions_r = list->root_permissions; else { mailbox_list_get_permissions_internal(list, NULL, permissions_r); } } void mailbox_permissions_copy(struct mailbox_permissions *dest, const struct mailbox_permissions *src, pool_t pool) { *dest = *src; dest->file_create_gid_origin = p_strdup(pool, src->file_create_gid_origin); } static const char * get_expanded_path(const char *unexpanded_start, const char *unexpanded_stop, const char *expanded_full) { const char *ret; unsigned int i, slash_count = 0, slash2_count = 0; /* get the expanded path up to the same amount of '/' characters. if there isn't the same amount of '/' characters, it means %variable expansion added more of them and we can't handle this. */ for (i = 0; unexpanded_start+i != unexpanded_stop; i++) { if (unexpanded_start[i] == '/') slash_count++; } for (; unexpanded_start[i] != '\0'; i++) { if (unexpanded_start[i] == '/') slash2_count++; } for (i = 0; expanded_full[i] != '\0'; i++) { if (expanded_full[i] == '/') { if (slash_count == 0) break; slash_count--; } } if (slash_count != 0) return ""; ret = t_strndup(expanded_full, i); for (; expanded_full[i] != '\0'; i++) { if (expanded_full[i] == '/') { if (slash2_count == 0) return ""; slash2_count--; } } if (slash2_count != 0) return ""; return ret; } static int mailbox_list_try_mkdir_root_parent(struct mailbox_list *list, enum mailbox_list_path_type type, struct mailbox_permissions *perm, const char **error_r) { const char *expanded, *unexpanded, *root_dir, *p; struct stat st; bool home = FALSE; /* get the directory path up to last %variable. for example unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want to get expanded="/var/mail/domain/nn" */ unexpanded = mailbox_list_get_unexpanded_path(list, type); p = strrchr(unexpanded, '%'); if ((p == unexpanded && p[1] == 'h') || (p == NULL && unexpanded[0] == '~')) { /* home directory used */ if (!mailbox_list_get_root_path(list, type, &expanded)) i_unreached(); home = TRUE; } else if (p == NULL) { return 0; } else { while (p != unexpanded && *p != '/') p--; if (p == unexpanded) return 0; if (!mailbox_list_get_root_path(list, type, &expanded)) i_unreached(); expanded = get_expanded_path(unexpanded, p, expanded); if (*expanded == '\0') return 0; } /* get the first existing parent directory's permissions */ if (stat_first_parent(expanded, &root_dir, &st) < 0) { *error_r = errno == EACCES ? mail_error_eacces_msg("stat", root_dir) : t_strdup_printf("stat(%s) failed: %m", root_dir); return -1; } /* if the parent directory doesn't have setgid-bit enabled, we don't copy any permissions from it. */ if ((st.st_mode & S_ISGID) == 0) return 0; if (!home) { /* assuming we have e.g. /var/vmail/%d/%n directory, here we want to create up to /var/vmail/%d with permissions from the parent directory. we never want to create the %n directory itself. */ if (root_dir == expanded) { /* this is the %n directory */ } else { if (mkdir_parents_chgrp(expanded, st.st_mode, (gid_t)-1, root_dir) < 0 && errno != EEXIST) { *error_r = t_strdup_printf( "mkdir(%s) failed: %m", expanded); return -1; } } if (perm->file_create_gid == (gid_t)-1 && (perm->dir_create_mode & S_ISGID) == 0) { /* change the group for user directories */ perm->dir_create_mode |= S_ISGID; perm->file_create_gid = getegid(); perm->file_create_gid_origin = "egid"; perm->gid_origin_is_mailbox_path = FALSE; } } else { /* when using %h and the parent has setgid-bit, copy the permissions from it for the home we're creating */ perm->file_create_mode = st.st_mode & 0666; perm->dir_create_mode = st.st_mode; perm->file_create_gid = (gid_t)-1; perm->file_create_gid_origin = "parent"; perm->gid_origin_is_mailbox_path = FALSE; } return 0; } int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type, const char **error_r) { const char *root_dir; struct stat st; struct mailbox_permissions perm; if (stat(path, &st) == 0) { /* looks like it already exists, don't bother checking further. */ if (!S_ISDIR(st.st_mode)) { *error_r = t_strdup_printf( "Root directory is a file: %s", path); return -1; } return 0; } mailbox_list_get_root_permissions(list, &perm); if (!mailbox_list_get_root_path(list, type, &root_dir)) i_unreached(); i_assert(strncmp(root_dir, path, strlen(root_dir)) == 0); if (strcmp(root_dir, path) != 0 && stat(root_dir, &st) == 0) { /* creating a subdirectory under an already existing root dir. use the root's permissions */ } else { if (mailbox_list_try_mkdir_root_parent(list, type, &perm, error_r) < 0) return -1; } /* the rest of the directories exist only for one user. create them with default directory permissions */ if (mkdir_parents_chgrp(path, perm.dir_create_mode, perm.file_create_gid, perm.file_create_gid_origin) < 0 && errno != EEXIST) { if (errno == EACCES) *error_r = mail_error_create_eacces_msg("mkdir", path); else *error_r = t_strdup_printf("mkdir(%s) failed: %m", path); return -1; } return 0; } int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path, enum mailbox_list_path_type type) { const char *error; if (mailbox_list_try_mkdir_root(list, path, type, &error) < 0) { mailbox_list_set_critical(list, "%s", error); return -1; } if (type == MAILBOX_LIST_PATH_TYPE_INDEX) list->index_root_dir_created = TRUE; return 0; } static bool mailbox_list_is_valid_fs_name(struct mailbox_list *list, const char *name, const char **error_r) { bool ret, allow_internal_dirs; *error_r = NULL; if (list->mail_set->mail_full_filesystem_access) return TRUE; /* make sure it's not absolute path */ if (*name == '/') { *error_r = "Begins with '/'"; return FALSE; } if (*name == '~') { *error_r = "Begins with '~'"; return FALSE; } /* make sure the mailbox name doesn't contain any foolishness: "../" could give access outside the mailbox directory. "./" and "//" could fool ACL checks. some mailbox formats have reserved directory names, such as Maildir's cur/new/tmp. if any of those would conflict with the mailbox directory name, it's not valid. maildir++ is kludged here as a special case because all of its mailbox dirs begin with "." */ allow_internal_dirs = list->v.is_internal_name == NULL || *list->set.maildir_name != '\0' || strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0; T_BEGIN { const char *const *names; names = t_strsplit(name, "/"); for (; *names != NULL; names++) { const char *n = *names; if (*n == '\0') { *error_r = "Has adjacent '/' chars"; break; /* // */ } if (*n == '.') { if (n[1] == '\0') { *error_r = "Contains '.' part"; break; /* ./ */ } if (n[1] == '.' && n[2] == '\0') { *error_r = "Contains '..' part"; break; /* ../ */ } } if (*list->set.maildir_name != '\0' && strcmp(list->set.maildir_name, n) == 0) { /* don't allow maildir_name to be used as part of the mailbox name */ *error_r = "Contains reserved name"; break; } if (!allow_internal_dirs && list->v.is_internal_name(list, n)) { *error_r = "Contains reserved name"; break; } } ret = *names == NULL; } T_END; return ret; } bool mailbox_list_is_valid_name(struct mailbox_list *list, const char *name, const char **error_r) { if (*name == '\0') { if (*list->ns->prefix != '\0') { /* an ugly way to get to mailbox root (e.g. Maildir/ when it's not the INBOX) */ return TRUE; } *error_r = "Name is empty"; return FALSE; } /* either the list backend uses '/' as the hierarchy separator or it doesn't use filesystem at all (PROP_NO_ROOT) */ if ((list->props & MAILBOX_LIST_PROP_NO_ROOT) == 0 && mailbox_list_get_hierarchy_sep(list) != '/' && strchr(name, '/') != NULL) { *error_r = "Name must not have '/' characters"; return FALSE; } return mailbox_list_is_valid_fs_name(list, name, error_r); } int mailbox_list_get_path(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type, const char **path_r) { int ret; if ((ret = list->v.get_path(list, name, type, path_r)) <= 0) *path_r = NULL; else i_assert(*path_r != NULL); return ret; } bool mailbox_list_get_root_path(struct mailbox_list *list, enum mailbox_list_path_type type, const char **path_r) { int ret; if ((ret = list->v.get_path(list, NULL, type, path_r)) < 0) i_unreached(); if (ret == 0) *path_r = NULL; else i_assert(*path_r != NULL); return ret > 0; } const char *mailbox_list_get_root_forced(struct mailbox_list *list, enum mailbox_list_path_type type) { const char *path; if (!mailbox_list_get_root_path(list, type, &path)) i_unreached(); return path; } bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set, enum mailbox_list_path_type type, const char **path_r) { const char *path = NULL; switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: path = set->root_dir; break; case MAILBOX_LIST_PATH_TYPE_ALT_DIR: path = set->alt_dir; break; case MAILBOX_LIST_PATH_TYPE_MAILBOX: if (*set->mailbox_dir_name == '\0') path = set->root_dir; else { path = t_strconcat(set->root_dir, "/", set->mailbox_dir_name, NULL); path = t_strndup(path, strlen(path)-1); } break; case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: if (*set->mailbox_dir_name == '\0') path = set->root_dir; else if (set->alt_dir != NULL) { path = t_strconcat(set->alt_dir, "/", set->mailbox_dir_name, NULL); path = t_strndup(path, strlen(path)-1); } break; case MAILBOX_LIST_PATH_TYPE_CONTROL: path = set->control_dir != NULL ? set->control_dir : set->root_dir; break; case MAILBOX_LIST_PATH_TYPE_LIST_INDEX: if (set->list_index_dir != NULL) { if (set->list_index_dir[0] == '/') { path = set->list_index_dir; break; } /* relative path */ if (!mailbox_list_set_get_root_path(set, MAILBOX_LIST_PATH_TYPE_INDEX, &path)) i_unreached(); path = t_strconcat(path, "/", set->list_index_dir, NULL); break; } /* fall through - default to index directory */ case MAILBOX_LIST_PATH_TYPE_INDEX: if (set->index_dir != NULL) { if (set->index_dir[0] == '\0') { /* in-memory indexes */ return 0; } path = set->index_dir; } else { path = set->root_dir; } break; case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE: path = set->index_pvt_dir; break; } *path_r = path; return path != NULL; } const char *mailbox_list_get_temp_prefix(struct mailbox_list *list) { return list->v.get_temp_prefix(list, FALSE); } const char *mailbox_list_get_global_temp_prefix(struct mailbox_list *list) { return list->v.get_temp_prefix(list, TRUE); } const char *mailbox_list_join_refpattern(struct mailbox_list *list, const char *ref, const char *pattern) { if (list->v.join_refpattern != NULL) return list->v.join_refpattern(list, ref, pattern); /* the default implementation: */ if (*ref != '\0') { /* merge reference and pattern */ pattern = t_strconcat(ref, pattern, NULL); } return pattern; } int mailbox_has_children(struct mailbox_list *list, const char *name) { struct mailbox_list_iterate_context *iter; const char *pattern; int ret; pattern = t_strdup_printf("%s%c%%", name, mail_namespace_get_sep(list->ns)); iter = mailbox_list_iter_init(list, pattern, MAILBOX_LIST_ITER_RETURN_NO_FLAGS); ret = mailbox_list_iter_next(iter) != NULL ? 1 : 0; if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } int mailbox_list_mailbox(struct mailbox_list *list, const char *name, enum mailbox_info_flags *flags_r) { const char *path, *fname, *rootdir, *dir, *inbox; size_t len; *flags_r = 0; if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && strcasecmp(name, "INBOX") == 0) { /* special handling for INBOX, mainly because with Maildir++ layout it needs to check if the cur/ directory exists, which the Maildir++ layout backend itself can't do.. */ struct mailbox *box; enum mailbox_existence existence; int ret; /* kludge: with imapc backend we can get here with list=Maildir++ (for indexes), but list->ns->list=imapc */ box = mailbox_alloc(list->ns->list, "INBOX", 0); ret = mailbox_exists(box, FALSE, &existence); if (ret < 0) { const char *errstr; enum mail_error error; /* internal error or with imapc we can get here with login failures */ errstr = mailbox_get_last_error(box, &error); mailbox_list_set_error(list, error, errstr); } mailbox_free(&box); if (ret < 0) return -1; switch (existence) { case MAILBOX_EXISTENCE_NONE: case MAILBOX_EXISTENCE_NOSELECT: *flags_r |= MAILBOX_NONEXISTENT; return 0; case MAILBOX_EXISTENCE_SELECT: break; } return 1; } if (list->v.get_mailbox_flags == NULL) { /* can't do this optimized. do it the slow way. */ struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; const char *vname; vname = mailbox_list_get_vname(list, name); iter = mailbox_list_iter_init(list, vname, 0); info = mailbox_list_iter_next(iter); if (info == NULL) *flags_r = MAILBOX_NONEXISTENT; else *flags_r = info->flags; return mailbox_list_iter_deinit(&iter); } if (!list->set.iter_from_index_dir) { rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0) i_unreached(); } else { rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_INDEX); if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0) i_unreached(); } fname = strrchr(path, '/'); if (fname == NULL) { fname = path; dir = "/"; } else { dir = t_strdup_until(path, fname); fname++; } len = strlen(rootdir); if (strncmp(path, rootdir, len) == 0 && path[len] == '/') { /* looking up a regular mailbox under mail root dir */ } else if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && strcasecmp(name, "INBOX") == 0) { /* looking up INBOX that's elsewhere */ } else { /* looking up the root dir itself */ dir = path; fname = ""; } if (*fname == '\0' && *name == '\0' && (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to access it also via namespace prefix. */ if (mailbox_list_get_path(list, "INBOX", MAILBOX_LIST_PATH_TYPE_MAILBOX, &inbox) <= 0) i_unreached(); if (strcmp(inbox, dir) == 0) { *flags_r |= MAILBOX_NONEXISTENT; return 0; } } return list->v.get_mailbox_flags(list, dir, fname, MAILBOX_LIST_FILE_TYPE_UNKNOWN, flags_r); } static bool mailbox_list_init_changelog(struct mailbox_list *list) { struct mailbox_permissions perm; const char *path; if (list->changelog != NULL) return TRUE; /* don't do this in mailbox_list_create(), because _get_path() might be overridden by storage (mbox). */ if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path)) return FALSE; path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL); list->changelog = mailbox_log_alloc(path); mailbox_list_get_root_permissions(list, &perm); mailbox_log_set_permissions(list->changelog, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); return TRUE; } int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list) { const char *root_dir, *index_dir; int ret; if (list->index_root_dir_created) return 1; /* if index root dir hasn't been created yet, do it now */ ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &index_dir); if (ret <= 0) return ret; ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX, &root_dir); if (ret <= 0) return ret; if (strcmp(root_dir, index_dir) != 0) { if (mailbox_list_mkdir_root(list, index_dir, MAILBOX_LIST_PATH_TYPE_INDEX) < 0) return -1; } list->index_root_dir_created = TRUE; return 1; } int mailbox_list_mkdir_missing_list_index_root(struct mailbox_list *list) { const char *index_dir; if (list->set.list_index_dir == NULL) return mailbox_list_mkdir_missing_index_root(list); /* LISTINDEX points outside the index root directory */ if (list->list_index_root_dir_created) return 1; if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_LIST_INDEX, &index_dir)) return 0; if (mailbox_list_mkdir_root(list, index_dir, MAILBOX_LIST_PATH_TYPE_LIST_INDEX) < 0) return -1; list->list_index_root_dir_created = TRUE; return 1; } void mailbox_list_add_change(struct mailbox_list *list, enum mailbox_log_record_type type, const guid_128_t mailbox_guid) { struct mailbox_log_record rec; time_t stamp; if (!mailbox_list_init_changelog(list) || guid_128_is_empty(mailbox_guid)) return; if (mailbox_list_mkdir_missing_index_root(list) <= 0) return; stamp = list->changelog_timestamp != (time_t)-1 ? list->changelog_timestamp : ioloop_time; i_zero(&rec); rec.type = type; memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid)); mailbox_log_record_set_timestamp(&rec, stamp); (void)mailbox_log_append(list->changelog, &rec); } int mailbox_list_set_subscribed(struct mailbox_list *list, const char *name, bool set) { int ret; /* make sure we'll refresh the file on next list */ list->subscriptions_mtime = (time_t)-1; if ((ret = list->v.set_subscribed(list, name, set)) <= 0) return ret; return 0; } int mailbox_list_delete_dir(struct mailbox_list *list, const char *name) { const char *error; if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') { mailbox_list_set_error(list, MAIL_ERROR_PARAMS, "Invalid mailbox name"); return -1; } return list->v.delete_dir(list, name); } int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name) { const char *error; if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') { mailbox_list_set_error(list, MAIL_ERROR_PARAMS, "Invalid mailbox name"); return -1; } return list->v.delete_symlink(list, name); } void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r) { unsigned char sha[SHA1_RESULTLEN]; sha1_get_digest(name, strlen(name), sha); memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha))); } struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list) { return !mailbox_list_init_changelog(list) ? NULL : list->changelog; } void mailbox_list_set_changelog_timestamp(struct mailbox_list *list, time_t stamp) { list->changelog_timestamp = stamp; } enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d ATTR_UNUSED) { enum mailbox_list_file_type type; #ifdef HAVE_DIRENT_D_TYPE switch (d->d_type) { case DT_UNKNOWN: type = MAILBOX_LIST_FILE_TYPE_UNKNOWN; break; case DT_REG: type = MAILBOX_LIST_FILE_TYPE_FILE; break; case DT_DIR: type = MAILBOX_LIST_FILE_TYPE_DIR; break; case DT_LNK: type = MAILBOX_LIST_FILE_TYPE_SYMLINK; break; default: type = MAILBOX_LIST_FILE_TYPE_OTHER; break; } #else type = MAILBOX_LIST_FILE_TYPE_UNKNOWN; #endif return type; } int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list, const char *dir_path, const struct dirent *d) { struct stat st; int ret; if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK) return 1; T_BEGIN { const char *path, *linkpath; path = t_strconcat(dir_path, "/", d->d_name, NULL); if (lstat(path, &st) < 0) { mailbox_list_set_critical(list, "lstat(%s) failed: %m", path); ret = -1; } else if (!S_ISLNK(st.st_mode)) { ret = 0; } else if (t_readlink(path, &linkpath) < 0) { i_error("readlink(%s) failed: %m", path); ret = -1; } else { /* it's an alias only if it points to the same directory */ ret = strchr(linkpath, '/') == NULL ? 1 : 0; } } T_END; return ret; } static bool mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name) { if ((*name)[1] == '/') { /* ~/dir - use the configured home directory */ if (mail_user_try_home_expand(list->ns->user, name) < 0) return FALSE; } else { /* ~otheruser/dir - assume we're using system users */ if (home_try_expand(name) < 0) return FALSE; } return TRUE; } bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name) { const char *root_dir, *path, *mailbox_name; size_t len; if (!list->mail_set->mail_full_filesystem_access) return FALSE; if (**name == '~') { /* try to expand home directory */ if (!mailbox_list_try_get_home_path(list, name)) { /* fallback to using actual "~name" mailbox */ return FALSE; } } else { if (**name != '/') return FALSE; } /* okay, we have an absolute path now. but check first if it points to same directory as one of our regular mailboxes. */ root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX); len = strlen(root_dir); if (strncmp(root_dir, *name, len) == 0 && (*name)[len] == '/') { mailbox_name = *name + len + 1; if (mailbox_list_get_path(list, mailbox_name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0) return FALSE; if (strcmp(path, *name) == 0) { /* yeah, we can replace the full path with mailbox name. this way we can use indexes. */ *name = mailbox_name; return FALSE; } } return TRUE; } const char *mailbox_list_get_last_error(struct mailbox_list *list, enum mail_error *error_r) { if (error_r != NULL) *error_r = list->error; return list->error_string != NULL ? list->error_string : "Unknown internal list error"; } enum mail_error mailbox_list_get_last_mail_error(struct mailbox_list *list) { return list->error; } const char *mailbox_list_get_last_internal_error(struct mailbox_list *list, enum mail_error *error_r) { if (error_r != NULL) *error_r = list->error; if (list->last_error_is_internal) { i_assert(list->last_internal_error != NULL); return list->last_internal_error; } return mailbox_list_get_last_error(list, error_r); } void mailbox_list_clear_error(struct mailbox_list *list) { i_free_and_null(list->error_string); i_free(list->last_internal_error); list->last_error_is_internal = FALSE; list->error = MAIL_ERROR_NONE; } void mailbox_list_set_error(struct mailbox_list *list, enum mail_error error, const char *string) { i_free(list->error_string); list->error_string = i_strdup(string); list->last_error_is_internal = FALSE; list->error = error; } void mailbox_list_set_internal_error(struct mailbox_list *list) { const char *str; str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time); i_free(list->error_string); list->error_string = i_strdup(str); list->error = MAIL_ERROR_TEMP; } void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...) { va_list va; i_free(list->last_internal_error); va_start(va, fmt); list->last_internal_error = i_strdup_vprintf(fmt, va); va_end(va); list->last_error_is_internal = TRUE; i_error("%s", list->last_internal_error); /* critical errors may contain sensitive data, so let user see only "Internal error" with a timestamp to make it easier to look from log files the actual error message. */ mailbox_list_set_internal_error(list); } bool mailbox_list_set_error_from_errno(struct mailbox_list *list) { const char *error_string; enum mail_error error; if (!mail_error_from_errno(&error, &error_string)) return FALSE; mailbox_list_set_error(list, error, error_string); return TRUE; } void mailbox_list_last_error_push(struct mailbox_list *list) { struct mail_storage_error *err; if (!array_is_created(&list->error_stack)) i_array_init(&list->error_stack, 2); err = array_append_space(&list->error_stack); err->error_string = i_strdup(list->error_string); err->error = list->error; err->last_error_is_internal = list->last_error_is_internal; if (err->last_error_is_internal) err->last_internal_error = i_strdup(list->last_internal_error); } void mailbox_list_last_error_pop(struct mailbox_list *list) { unsigned int count = array_count(&list->error_stack); const struct mail_storage_error *err = array_idx(&list->error_stack, count-1); i_free(list->error_string); i_free(list->last_internal_error); list->error_string = err->error_string; list->error = err->error; list->last_error_is_internal = err->last_error_is_internal; list->last_internal_error = err->last_internal_error; array_delete(&list->error_stack, count-1, 1); } int mailbox_list_init_fs(struct mailbox_list *list, const char *driver, const char *args, const char *root_dir, struct fs **fs_r, const char **error_r) { struct fs_settings fs_set; struct ssl_iostream_settings ssl_set; struct mailbox_list_fs_context *ctx; struct fs *parent_fs; i_zero(&ssl_set); i_zero(&fs_set); mail_user_init_fs_settings(list->ns->user, &fs_set, &ssl_set); fs_set.root_path = root_dir; fs_set.temp_file_prefix = mailbox_list_get_global_temp_prefix(list); if (fs_init(driver, args, &fs_set, fs_r, error_r) < 0) return -1; /* add mailbox_list context to the parent fs, which allows mailbox_list_fs_get_list() to work */ for (parent_fs = *fs_r; parent_fs->parent != NULL; parent_fs = parent_fs->parent) ; ctx = p_new(list->pool, struct mailbox_list_fs_context, 1); ctx->list = list; MODULE_CONTEXT_SET(parent_fs, mailbox_list_fs_module, ctx); /* a bit kludgy notification to the fs that we're now finished setting up the module context. */ (void)fs_get_properties(*fs_r); return 0; } struct mailbox_list *mailbox_list_fs_get_list(struct fs *fs) { struct mailbox_list_fs_context *ctx; while (fs->parent != NULL) fs = fs->parent; ctx = MAILBOX_LIST_FS_CONTEXT(fs); return ctx == NULL ? NULL : ctx->list; } dovecot-2.2.33.2/src/lib-storage/mail-copy.c0000644000175000017500000000702713123174404015367 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "mail-storage-private.h" #include "mail-copy.h" static void mail_copy_set_failed(struct mail_save_context *ctx, struct mail *mail, const char *func) { const char *errstr; enum mail_error error; if (ctx->transaction->box->storage == mail->box->storage) return; errstr = mail_storage_get_last_error(mail->box->storage, &error); mail_storage_set_error(ctx->transaction->box->storage, error, t_strdup_printf("%s (%s)", errstr, func)); } int mail_save_copy_default_metadata(struct mail_save_context *ctx, struct mail *mail) { const char *from_envelope, *guid; time_t received_date; if (ctx->data.received_date == (time_t)-1) { if (mail_get_received_date(mail, &received_date) < 0) { mail_copy_set_failed(ctx, mail, "received-date"); return -1; } mailbox_save_set_received_date(ctx, received_date, 0); } if (ctx->data.from_envelope == NULL) { if (mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE, &from_envelope) < 0) { mail_copy_set_failed(ctx, mail, "from-envelope"); return -1; } if (*from_envelope != '\0') mailbox_save_set_from_envelope(ctx, from_envelope); } if (ctx->data.guid == NULL) { if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) < 0) { mail_copy_set_failed(ctx, mail, "guid"); return -1; } if (*guid != '\0') mailbox_save_set_guid(ctx, guid); } return 0; } static int mail_storage_try_copy(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx; struct mail_private *pmail = (struct mail_private *)mail; struct istream *input; ctx->copying_via_save = TRUE; /* we need to open the file in any case. caching metadata is unlikely to help anything. */ pmail->v.set_uid_cache_updates(mail, TRUE); if (mail_get_stream_because(mail, NULL, NULL, "copying", &input) < 0) { mail_copy_set_failed(ctx, mail, "stream"); return -1; } if (mail_save_copy_default_metadata(ctx, mail) < 0) return -1; if (mailbox_save_begin(_ctx, input) < 0) return -1; ssize_t ret; do { if (mailbox_save_continue(ctx) < 0) break; ret = i_stream_read(input); i_assert(ret != 0); } while (ret != -1); if (input->stream_errno != 0) { mail_storage_set_critical(ctx->transaction->box->storage, "copy: i_stream_read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return -1; } return 0; } int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail) { i_assert(ctx->copying_or_moving); if (ctx->data.keywords != NULL) { /* keywords gets unreferenced twice: first in mailbox_save_cancel()/_finish() and second time in mailbox_copy(). */ mailbox_keywords_ref(ctx->data.keywords); } if (mail_storage_try_copy(&ctx, mail) < 0) { if (ctx != NULL) mailbox_save_cancel(&ctx); return -1; } return mailbox_save_finish(&ctx); } bool mail_storage_copy_can_use_hardlink(struct mailbox *src, struct mailbox *dest) { const struct mailbox_permissions *src_perm = mailbox_get_permissions(src); const struct mailbox_permissions *dest_perm = mailbox_get_permissions(dest); if (src_perm->file_uid != dest_perm->file_uid) { /* if we don't have read permissions, we can't hard link (basically we'll catch 0600 files here) */ if ((src_perm->file_create_mode & 0022) == 0) return FALSE; } return src_perm->file_create_mode == dest_perm->file_create_mode && src_perm->file_create_gid == dest_perm->file_create_gid && !dest->disable_reflink_copy_to; } dovecot-2.2.33.2/src/lib-storage/Makefile.am0000644000175000017500000001013313165463624015370 00000000000000SUBDIRS = list index register noinst_LTLIBRARIES = libstorage.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DMODULEDIR=\""$(moduledir)"\" libstorage_la_SOURCES = \ fail-mail-storage.c \ fail-mailbox.c \ fail-mail.c \ mail.c \ mail-autoexpunge.c \ mail-copy.c \ mail-error.c \ mail-namespace.c \ mail-search.c \ mail-search-args-cmdline.c \ mail-search-args-imap.c \ mail-search-args-simplify.c \ mail-search-build.c \ mail-search-mime.c \ mail-search-mime-build.c \ mail-search-mime-register.c \ mail-search-parser.c \ mail-search-parser-imap.c \ mail-search-parser-cmdline.c \ mail-search-register.c \ mail-search-register-human.c \ mail-search-register-imap.c \ mail-storage.c \ mail-storage-hooks.c \ mail-storage-service.c \ mail-storage-settings.c \ mail-thread.c \ mail-user.c \ mailbox-attribute.c \ mailbox-attribute-internal.c \ mailbox-get.c \ mailbox-guid-cache.c \ mailbox-header.c \ mailbox-keywords.c \ mailbox-list.c \ mailbox-list-notify.c \ mailbox-recent-flags.c \ mailbox-search-result.c \ mailbox-tree.c \ mailbox-uidvalidity.c \ mailbox-watch.c headers = \ fail-mail-storage.h \ mail-autoexpunge.h \ mail-copy.h \ mail-error.h \ mail-namespace.h \ mail-search.h \ mail-search-build.h \ mail-search-mime.h \ mail-search-mime-build.h \ mail-search-mime-register.h \ mail-search-register.h \ mail-thread.h \ mail-storage.h \ mail-search-parser.h \ mail-search-parser-private.h \ mail-storage-private.h \ mail-storage-hooks.h \ mail-storage-service.h \ mail-storage-settings.h \ mail-user.h \ mailbox-attribute.h \ mailbox-attribute-internal.h \ mailbox-attribute-private.h \ mailbox-guid-cache.h \ mailbox-list.h \ mailbox-list-iter.h \ mailbox-list-private.h \ mailbox-list-notify.h \ mailbox-recent-flags.h \ mailbox-search-result-private.h \ mailbox-tree.h \ mailbox-uidvalidity.h \ mailbox-watch.h shlibs = \ @LINKED_STORAGE_LIBS@ \ list/libstorage_list.la \ index/libstorage_index.la \ register/libstorage_register.la \ ../lib-index/libindex.la \ ../lib-imap-storage/libimap-storage.la libstorage_la_LIBADD = $(shlibs) libstorage_la_DEPENDENCIES = $(shlibs) pkglib_LTLIBRARIES = libdovecot-storage.la libdovecot_storage_la_SOURCES = libdovecot_storage_la_LIBADD = \ libstorage.la \ ../lib-dovecot/libdovecot.la \ $(LINKED_STORAGE_LDADD) libdovecot_storage_la_DEPENDENCIES = \ libstorage.la \ ../lib-dovecot/libdovecot.la \ $(LIBDOVECOT_DEPS) libdovecot_storage_la_LDFLAGS = -export-dynamic test_programs = \ test-mail-search-args-imap \ test-mail-search-args-simplify \ test-mail-storage \ test-mailbox-get noinst_PROGRAMS = $(test_programs) test_libs = \ $(top_builddir)/src/lib-test/libtest.la \ $(top_builddir)/src/lib/liblib.la test_mail_search_args_imap_SOURCES = test-mail-search-args-imap.c test_mail_search_args_imap_LDADD = libstorage.la $(LIBDOVECOT) test_mail_search_args_imap_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) test_mail_search_args_simplify_SOURCES = test-mail-search-args-simplify.c test_mail_search_args_simplify_LDADD = libstorage.la $(LIBDOVECOT) test_mail_search_args_simplify_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) test_mailbox_get_SOURCES = test-mailbox-get.c test_mailbox_get_LDADD = mailbox-get.lo $(test_libs) test_mailbox_get_DEPENDENCIES = $(noinst_LTLIBRARIES) $(test_libs) test_mail_storage_SOURCES = test-mail-storage.c test_mail_storage_LDADD = libstorage.la $(LIBDOVECOT) test_mail_storage_DEPENDENCIES = libstorage.la $(LIBDOVECOT_DEPS) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) noinst_HEADERS = $(test_headers) dovecot-2.2.33.2/src/lib-storage/mail-copy.h0000644000175000017500000000117313123174404015370 00000000000000#ifndef MAIL_COPY_H #define MAIL_COPY_H struct mail; struct mail_save_context; struct mailbox; int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail); /* If save context already doesn't have some metadata fields set, copy them from the given mail (e.g. received date, from envelope, guid). */ int mail_save_copy_default_metadata(struct mail_save_context *ctx, struct mail *mail); /* Returns TRUE if mail can be copied using hard linking from src to dest. (Assuming the storage itself supports this.) */ bool mail_storage_copy_can_use_hardlink(struct mailbox *src, struct mailbox *dest); #endif dovecot-2.2.33.2/src/lib-mail/0002755000175000017500000000000013172375610012671 500000000000000dovecot-2.2.33.2/src/lib-mail/message-date.c0000644000175000017500000001437513165463624015331 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "utc-offset.h" #include "utc-mktime.h" #include "rfc822-parser.h" #include "message-date.h" #include /* RFC specifies ':' as the only allowed separator, but be forgiving also for some broken ones */ #define IS_TIME_SEP(c) \ ((c) == ':' || (c) == '.') struct message_date_parser_context { struct rfc822_parser_context parser; string_t *str; }; static const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char *weekday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static int parse_timezone(const unsigned char *str, size_t len) { int offset; char chr; if (len == 5 && (*str == '+' || *str == '-')) { /* numeric offset */ if (!i_isdigit(str[1]) || !i_isdigit(str[2]) || !i_isdigit(str[3]) || !i_isdigit(str[4])) return FALSE; offset = ((str[1]-'0') * 10 + (str[2]-'0')) * 60 + (str[3]-'0') * 10 + (str[4]-'0'); return *str == '+' ? offset : -offset; } if (len == 1) { /* military zone - handle them the correct way, not as RFC822 says. RFC2822 though suggests that they'd be considered as unspecified.. */ chr = i_toupper(*str); if (chr < 'J') return (*str-'A'+1) * 60; if (chr == 'J') return 0; if (chr <= 'M') return (*str-'A') * 60; if (chr < 'Z') return ('M'-*str) * 60; return 0; } if (len == 2 && i_toupper(str[0]) == 'U' && i_toupper(str[1]) == 'T') { /* UT - Universal Time */ return 0; } if (len == 3) { /* GMT | [ECMP][DS]T */ if (str[2] != 'T') return 0; switch (i_toupper(*str)) { case 'E': offset = -5 * 60; break; case 'C': offset = -6 * 60; break; case 'M': offset = -7 * 60; break; case 'P': offset = -8 * 60; break; default: /* GMT and others */ return 0; } if (i_toupper(str[1]) == 'D') return offset + 60; if (i_toupper(str[1]) == 'S') return offset; } return 0; } static int next_token(struct message_date_parser_context *ctx, const unsigned char **value, size_t *value_len) { int ret; str_truncate(ctx->str, 0); ret = ctx->parser.data == ctx->parser.end ? 0 : rfc822_parse_atom(&ctx->parser, ctx->str); *value = str_data(ctx->str); *value_len = str_len(ctx->str); return ret < 0 ? -1 : *value_len > 0; } static bool message_date_parser_tokens(struct message_date_parser_context *ctx, time_t *timestamp_r, int *timezone_offset_r) { struct tm tm; const unsigned char *value; size_t i, len; int ret; /* [weekday_name "," ] dd month_name [yy]yy hh:mi[:ss] timezone */ i_zero(&tm); rfc822_skip_lwsp(&ctx->parser); /* skip the optional weekday */ if (next_token(ctx, &value, &len) <= 0) return FALSE; if (len == 3) { if (*ctx->parser.data != ',') return FALSE; ctx->parser.data++; rfc822_skip_lwsp(&ctx->parser); if (next_token(ctx, &value, &len) <= 0) return FALSE; } /* dd */ if (len < 1 || len > 2 || !i_isdigit(value[0])) return FALSE; tm.tm_mday = value[0]-'0'; if (len == 2) { if (!i_isdigit(value[1])) return FALSE; tm.tm_mday = (tm.tm_mday * 10) + (value[1]-'0'); } /* month name */ if (next_token(ctx, &value, &len) <= 0 || len < 3) return FALSE; for (i = 0; i < 12; i++) { if (i_memcasecmp(month_names[i], value, 3) == 0) { tm.tm_mon = i; break; } } if (i == 12) return FALSE; /* [yy]yy */ if (next_token(ctx, &value, &len) <= 0 || (len != 2 && len != 4)) return FALSE; for (i = 0; i < len; i++) { if (!i_isdigit(value[i])) return FALSE; tm.tm_year = tm.tm_year * 10 + (value[i]-'0'); } if (len == 2) { /* two digit year, assume 1970+ */ if (tm.tm_year < 70) tm.tm_year += 100; } else { if (tm.tm_year < 1900) return FALSE; tm.tm_year -= 1900; } /* hh, allow also single digit */ if (next_token(ctx, &value, &len) <= 0 || len < 1 || len > 2 || !i_isdigit(value[0])) return FALSE; tm.tm_hour = value[0]-'0'; if (len == 2) { if (!i_isdigit(value[1])) return FALSE; tm.tm_hour = tm.tm_hour * 10 + (value[1]-'0'); } /* :mm (may be the last token) */ if (!IS_TIME_SEP(*ctx->parser.data)) return FALSE; ctx->parser.data++; rfc822_skip_lwsp(&ctx->parser); if (next_token(ctx, &value, &len) < 0 || len != 2 || !i_isdigit(value[0]) || !i_isdigit(value[1])) return FALSE; tm.tm_min = (value[0]-'0') * 10 + (value[1]-'0'); /* [:ss] */ if (ctx->parser.data != ctx->parser.end && IS_TIME_SEP(*ctx->parser.data)) { ctx->parser.data++; rfc822_skip_lwsp(&ctx->parser); if (next_token(ctx, &value, &len) <= 0 || len != 2 || !i_isdigit(value[0]) || !i_isdigit(value[1])) return FALSE; tm.tm_sec = (value[0]-'0') * 10 + (value[1]-'0'); } if ((ret = next_token(ctx, &value, &len)) < 0) return FALSE; if (ret == 0) { /* missing timezone */ *timezone_offset_r = 0; } else { /* timezone. invalid timezones are treated as GMT, because we may not know all the possible timezones that are used and it's better to give at least a mostly correct reply. FIXME: perhaps some different strict version of this function would be useful? */ *timezone_offset_r = parse_timezone(value, len); } tm.tm_isdst = -1; *timestamp_r = utc_mktime(&tm); if (*timestamp_r == (time_t)-1) return FALSE; *timestamp_r -= *timezone_offset_r * 60; return TRUE; } bool message_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r, int *timezone_offset_r) { bool success; T_BEGIN { struct message_date_parser_context ctx; rfc822_parser_init(&ctx.parser, data, size, NULL); ctx.str = t_str_new(128); success = message_date_parser_tokens(&ctx, timestamp_r, timezone_offset_r); } T_END; return success; } const char *message_date_create(time_t timestamp) { struct tm *tm; int offset; bool negative; tm = localtime(×tamp); offset = utc_offset(tm, timestamp); if (offset >= 0) negative = FALSE; else { negative = TRUE; offset = -offset; } return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d", weekday_names[tm->tm_wday], tm->tm_mday, month_names[tm->tm_mon], tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec, negative ? '-' : '+', offset / 60, offset % 60); } dovecot-2.2.33.2/src/lib-mail/rfc822-parser.c0000644000175000017500000002371313165463624015266 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "rfc822-parser.h" /* atext = ALPHA / DIGIT / ; Any character except controls, "!" / "#" / ; SP, and specials. "$" / "%" / ; Used for atoms "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~" MIME: token := 1* tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> "/" / "[" / "]" / "?" / "=" So token is same as dot-atom, except stops also at '/', '?' and '='. */ /* atext chars are marked with 1, alpha and digits with 2, atext-but-mime-tspecials with 4 */ unsigned char rfc822_atext_chars[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 4, /* 32-47 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 4, 0, 4, /* 48-63 */ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 64-79 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, /* 80-95 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 96-111 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, /* 112-127 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; void rfc822_parser_init(struct rfc822_parser_context *ctx, const unsigned char *data, size_t size, string_t *last_comment) { i_zero(ctx); ctx->data = data; ctx->end = data + size; ctx->last_comment = last_comment; } int rfc822_skip_comment(struct rfc822_parser_context *ctx) { const unsigned char *start; int level = 1; i_assert(*ctx->data == '('); if (ctx->last_comment != NULL) str_truncate(ctx->last_comment, 0); start = ++ctx->data; for (; ctx->data != ctx->end; ctx->data++) { switch (*ctx->data) { case '(': level++; break; case ')': if (--level == 0) { if (ctx->last_comment != NULL) { str_append_n(ctx->last_comment, start, ctx->data - start); } ctx->data++; return ctx->data != ctx->end; } break; case '\\': if (ctx->last_comment != NULL) { str_append_n(ctx->last_comment, start, ctx->data - start); } start = ctx->data + 1; ctx->data++; if (ctx->data == ctx->end) return -1; break; } } /* missing ')' */ return -1; } int rfc822_skip_lwsp(struct rfc822_parser_context *ctx) { for (; ctx->data != ctx->end;) { if (*ctx->data == ' ' || *ctx->data == '\t' || *ctx->data == '\r' || *ctx->data == '\n') { ctx->data++; continue; } if (*ctx->data != '(') break; if (rfc822_skip_comment(ctx) < 0) return -1; } return ctx->data != ctx->end; } int rfc822_parse_atom(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; /* atom = [CFWS] 1*atext [CFWS] atext = ; Any character except controls, SP, and specials. */ if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data)) return -1; for (start = ctx->data++; ctx->data != ctx->end; ctx->data++) { if (IS_ATEXT(*ctx->data)) continue; str_append_n(str, start, ctx->data - start); return rfc822_skip_lwsp(ctx); } str_append_n(str, start, ctx->data - start); return 0; } int rfc822_parse_dot_atom(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; int ret; /* dot-atom = [CFWS] dot-atom-text [CFWS] dot-atom-text = 1*atext *("." 1*atext) atext = ; Any character except controls, SP, and specials. For RFC-822 compatibility allow LWSP around '.' */ if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data)) return -1; for (start = ctx->data++; ctx->data != ctx->end; ) { if (IS_ATEXT(*ctx->data)) { ctx->data++; continue; } str_append_n(str, start, ctx->data - start); if ((ret = rfc822_skip_lwsp(ctx)) <= 0) return ret; if (*ctx->data != '.') return 1; ctx->data++; str_append_c(str, '.'); if ((ret = rfc822_skip_lwsp(ctx)) <= 0) return ret; start = ctx->data; } str_append_n(str, start, ctx->data - start); return 0; } int rfc822_parse_mime_token(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; for (start = ctx->data; ctx->data != ctx->end; ctx->data++) { if (IS_ATEXT_NON_TSPECIAL(*ctx->data) || *ctx->data == '.') continue; str_append_n(str, start, ctx->data - start); return rfc822_skip_lwsp(ctx); } str_append_n(str, start, ctx->data - start); return 0; } int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; size_t len; i_assert(*ctx->data == '"'); ctx->data++; for (start = ctx->data; ctx->data != ctx->end; ctx->data++) { switch (*ctx->data) { case '"': str_append_n(str, start, ctx->data - start); ctx->data++; return rfc822_skip_lwsp(ctx); case '\n': /* folding whitespace, remove the (CR)LF */ len = ctx->data - start; if (len > 0 && start[len-1] == '\r') len--; str_append_n(str, start, len); start = ctx->data + 1; break; case '\\': ctx->data++; if (ctx->data == ctx->end) return -1; str_append_n(str, start, ctx->data - start - 1); start = ctx->data; break; } } /* missing '"' */ return -1; } static int rfc822_parse_atom_or_dot(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; /* atom = [CFWS] 1*atext [CFWS] atext = ; Any character except controls, SP, and specials. The difference between this function and rfc822_parse_dot_atom() is that this doesn't just silently skip over all the whitespace. */ for (start = ctx->data; ctx->data != ctx->end; ctx->data++) { if (IS_ATEXT(*ctx->data) || *ctx->data == '.') continue; str_append_n(str, start, ctx->data - start); return rfc822_skip_lwsp(ctx); } str_append_n(str, start, ctx->data - start); return 0; } int rfc822_parse_phrase(struct rfc822_parser_context *ctx, string_t *str) { int ret; /* phrase = 1*word / obs-phrase word = atom / quoted-string obs-phrase = word *(word / "." / CFWS) */ if (ctx->data == ctx->end) return 0; if (*ctx->data == '.') return -1; for (;;) { if (*ctx->data == '"') ret = rfc822_parse_quoted_string(ctx, str); else ret = rfc822_parse_atom_or_dot(ctx, str); if (ret <= 0) return ret; if (!IS_ATEXT(*ctx->data) && *ctx->data != '"' && *ctx->data != '.') break; str_append_c(str, ' '); } return rfc822_skip_lwsp(ctx); } static int rfc822_parse_domain_literal(struct rfc822_parser_context *ctx, string_t *str) { const unsigned char *start; /* domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS] dcontent = dtext / quoted-pair dtext = NO-WS-CTL / ; Non white space controls %d33-90 / ; The rest of the US-ASCII %d94-126 ; characters not including "[", ; "]", or "\" */ i_assert(*ctx->data == '['); for (start = ctx->data; ctx->data != ctx->end; ctx->data++) { if (*ctx->data == '\\') { ctx->data++; if (ctx->data == ctx->end) break; } else if (*ctx->data == ']') { ctx->data++; str_append_n(str, start, ctx->data - start); return rfc822_skip_lwsp(ctx); } } /* missing ']' */ return -1; } int rfc822_parse_domain(struct rfc822_parser_context *ctx, string_t *str) { /* domain = dot-atom / domain-literal / obs-domain domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS] obs-domain = atom *("." atom) */ i_assert(*ctx->data == '@'); ctx->data++; if (rfc822_skip_lwsp(ctx) <= 0) return -1; if (*ctx->data == '[') return rfc822_parse_domain_literal(ctx, str); else return rfc822_parse_dot_atom(ctx, str); } int rfc822_parse_content_type(struct rfc822_parser_context *ctx, string_t *str) { if (rfc822_skip_lwsp(ctx) <= 0) return -1; /* get main type */ if (rfc822_parse_mime_token(ctx, str) <= 0) return -1; /* skip over "/" */ if (*ctx->data != '/') return -1; ctx->data++; if (rfc822_skip_lwsp(ctx) <= 0) return -1; str_append_c(str, '/'); /* get subtype */ return rfc822_parse_mime_token(ctx, str); } int rfc822_parse_content_param(struct rfc822_parser_context *ctx, const char **key_r, const char **value_r) { string_t *tmp; size_t value_pos; int ret; /* .. := *(";" parameter) parameter := attribute "=" value attribute := token value := token / quoted-string */ *key_r = NULL; *value_r = NULL; if (ctx->data == ctx->end) return 0; if (*ctx->data != ';') return -1; ctx->data++; if (rfc822_skip_lwsp(ctx) <= 0) return -1; tmp = t_str_new(64); if (rfc822_parse_mime_token(ctx, tmp) <= 0) return -1; str_append_c(tmp, '\0'); value_pos = str_len(tmp); if (*ctx->data != '=') return -1; ctx->data++; if ((ret = rfc822_skip_lwsp(ctx)) <= 0) { /* broken / no value */ } else if (*ctx->data == '"') { ret = rfc822_parse_quoted_string(ctx, tmp); } else if (ctx->data != ctx->end && *ctx->data == '=') { /* workaround for broken input: name==?utf-8?b?...?= */ while (ctx->data != ctx->end && *ctx->data != ';' && *ctx->data != ' ' && *ctx->data != '\t' && *ctx->data != '\r' && *ctx->data != '\n') { str_append_c(tmp, *ctx->data); ctx->data++; } } else { ret = rfc822_parse_mime_token(ctx, tmp); } *key_r = str_c(tmp); *value_r = *key_r + value_pos; return ret < 0 ? -1 : 1; } dovecot-2.2.33.2/src/lib-mail/message-header-parser.h0000644000175000017500000000531613165463624017136 00000000000000#ifndef MESSAGE_HEADER_PARSER_H #define MESSAGE_HEADER_PARSER_H #define IS_LWSP(c) \ ((c) == ' ' || (c) == '\t') struct message_size; struct message_header_parser_ctx; enum message_header_parser_flags { /* Don't add LWSP after "header: " to value. */ MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP = 0x01, /* Don't add CRs to full_value even if input had them */ MESSAGE_HEADER_PARSER_FLAG_DROP_CR = 0x02, /* Convert [CR+]LF+LWSP to a space character in full_value */ MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE = 0x04 }; struct message_header_line { const char *name; size_t name_len; const unsigned char *value; size_t value_len; const unsigned char *full_value; size_t full_value_len; const unsigned char *middle; size_t middle_len; uoff_t name_offset, full_value_offset; unsigned int continues:1; /* multiline header, continues in next line */ unsigned int continued:1; /* multiline header, continues */ unsigned int eoh:1; /* "end of headers" line */ unsigned int no_newline:1; /* no \n after this line */ unsigned int crlf_newline:1; /* newline was \r\n */ unsigned int use_full_value:1; /* set if you want full_value */ }; /* called once with hdr = NULL at the end of headers */ typedef void message_header_callback_t(struct message_header_line *hdr, void *context); struct message_header_parser_ctx * message_parse_header_init(struct istream *input, struct message_size *hdr_size, enum message_header_parser_flags flags) ATTR_NULL(2); void message_parse_header_deinit(struct message_header_parser_ctx **ctx); /* Read and return next header line. Returns 1 if header is returned, 0 if input stream is non-blocking and more data needs to be read, -1 when all is done or error occurred (see stream's error status). */ int message_parse_header_next(struct message_header_parser_ctx *ctx, struct message_header_line **hdr_r); /* Returns TRUE if the parser has seen NUL characters. */ bool message_parse_header_has_nuls(const struct message_header_parser_ctx *ctx) ATTR_PURE; /* Read and parse the header from the given stream. */ void message_parse_header(struct istream *input, struct message_size *hdr_size, enum message_header_parser_flags flags, message_header_callback_t *callback, void *context) ATTR_NULL(2); #define message_parse_header(input, hdr_size, flags, callback, context) \ message_parse_header(input, hdr_size, flags + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct message_header_line *hdr, typeof(context))), \ (message_header_callback_t *)callback, context) /* Write the header line to buffer exactly as it was read, including the newline. */ void message_header_line_write(buffer_t *output, const struct message_header_line *hdr); #endif dovecot-2.2.33.2/src/lib-mail/message-search.c0000644000175000017500000001453413165463624015656 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "str-find.h" #include "rfc822-parser.h" #include "message-decoder.h" #include "message-parser.h" #include "message-search.h" struct message_search_context { enum message_search_flags flags; normalizer_func_t *normalizer; struct str_find_context *str_find_ctx; struct message_part *prev_part; struct message_decoder_context *decoder; unsigned int content_type_text:1; /* text/any or message/any */ }; struct message_search_context * message_search_init(const char *normalized_key_utf8, normalizer_func_t *normalizer, enum message_search_flags flags) { struct message_search_context *ctx; i_assert(*normalized_key_utf8 != '\0'); ctx = i_new(struct message_search_context, 1); ctx->flags = flags; ctx->decoder = message_decoder_init(normalizer, 0); ctx->str_find_ctx = str_find_init(default_pool, normalized_key_utf8); return ctx; } void message_search_deinit(struct message_search_context **_ctx) { struct message_search_context *ctx = *_ctx; *_ctx = NULL; str_find_deinit(&ctx->str_find_ctx); message_decoder_deinit(&ctx->decoder); i_free(ctx); } static void parse_content_type(struct message_search_context *ctx, struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *content_type; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); content_type = t_str_new(64); (void)rfc822_parse_content_type(&parser, content_type); ctx->content_type_text = strncasecmp(str_c(content_type), "text/", 5) == 0 || strncasecmp(str_c(content_type), "message/", 8) == 0; } static void handle_header(struct message_search_context *ctx, struct message_header_line *hdr) { if (hdr->name_len == 12 && strcasecmp(hdr->name, "Content-Type") == 0) { if (hdr->continues) { hdr->use_full_value = TRUE; return; } T_BEGIN { parse_content_type(ctx, hdr); } T_END; } } static bool search_header(struct message_search_context *ctx, const struct message_header_line *hdr) { static const unsigned char crlf[2] = { '\r', '\n' }; return str_find_more(ctx->str_find_ctx, (const unsigned char *)hdr->name, hdr->name_len) || str_find_more(ctx->str_find_ctx, hdr->middle, hdr->middle_len) || str_find_more(ctx->str_find_ctx, hdr->full_value, hdr->full_value_len) || (!hdr->no_newline && str_find_more(ctx->str_find_ctx, crlf, 2)); } static bool message_search_more_decoded2(struct message_search_context *ctx, struct message_block *block) { if (block->hdr != NULL) { if (search_header(ctx, block->hdr)) return TRUE; } else { if (str_find_more(ctx->str_find_ctx, block->data, block->size)) return TRUE; } return FALSE; } bool message_search_more(struct message_search_context *ctx, struct message_block *raw_block) { struct message_block decoded_block; return message_search_more_get_decoded(ctx, raw_block, &decoded_block); } bool message_search_more_get_decoded(struct message_search_context *ctx, struct message_block *raw_block, struct message_block *decoded_block_r) { struct message_header_line *hdr = raw_block->hdr; struct message_block decoded_block; i_zero(decoded_block_r); decoded_block_r->part = raw_block->part; if (raw_block->part != ctx->prev_part) { /* part changes. we must change this before looking at content type */ message_search_reset(ctx); ctx->prev_part = raw_block->part; if (hdr == NULL) { /* we're returning to a multipart message. */ ctx->content_type_text = FALSE; } } if (hdr != NULL) { handle_header(ctx, hdr); if ((ctx->flags & MESSAGE_SEARCH_FLAG_SKIP_HEADERS) != 0) { /* we want to search only message bodies, but but decoder needs some headers so that it can decode the body properly. */ if (hdr->name_len != 12 && hdr->name_len != 25) return FALSE; if (strcasecmp(hdr->name, "Content-Type") != 0 && strcasecmp(hdr->name, "Content-Transfer-Encoding") != 0) return FALSE; } } else { /* body */ if (!ctx->content_type_text) return FALSE; } if (!message_decoder_decode_next_block(ctx->decoder, raw_block, &decoded_block)) return FALSE; if (decoded_block.hdr != NULL && (ctx->flags & MESSAGE_SEARCH_FLAG_SKIP_HEADERS) != 0) { /* Content-* header */ return FALSE; } *decoded_block_r = decoded_block; return message_search_more_decoded2(ctx, &decoded_block); } bool message_search_more_decoded(struct message_search_context *ctx, struct message_block *block) { if (block->part != ctx->prev_part) { /* part changes */ message_search_reset(ctx); ctx->prev_part = block->part; } return message_search_more_decoded2(ctx, block); } void message_search_reset(struct message_search_context *ctx) { /* Content-Type defaults to text/plain */ ctx->content_type_text = TRUE; ctx->prev_part = NULL; str_find_reset(ctx->str_find_ctx); message_decoder_decode_reset(ctx->decoder); } static int message_search_msg_real(struct message_search_context *ctx, struct istream *input, struct message_part *parts, const char **error_r) { const enum message_header_parser_flags hdr_parser_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; struct message_parser_ctx *parser_ctx; struct message_block raw_block; struct message_part *new_parts; int ret; message_search_reset(ctx); if (parts != NULL) { parser_ctx = message_parser_init_from_parts(parts, input, hdr_parser_flags, 0); } else { parser_ctx = message_parser_init(pool_datastack_create(), input, hdr_parser_flags, 0); } while ((ret = message_parser_parse_next_block(parser_ctx, &raw_block)) > 0) { if (message_search_more(ctx, &raw_block)) { ret = 1; break; } } i_assert(ret != 0); if (ret < 0 && input->stream_errno == 0) { /* normal exit */ ret = 0; } if (message_parser_deinit_from_parts(&parser_ctx, &new_parts, error_r) < 0) { /* broken parts */ ret = -1; } return ret; } int message_search_msg(struct message_search_context *ctx, struct istream *input, struct message_part *parts, const char **error_r) { char *error; int ret; T_BEGIN { ret = message_search_msg_real(ctx, input, parts, error_r); error = i_strdup(*error_r); } T_END; *error_r = t_strdup(error); i_free(error); return ret; } dovecot-2.2.33.2/src/lib-mail/message-address.c0000644000175000017500000003303313165463624016031 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "message-parser.h" #include "message-address.h" #include "rfc822-parser.h" struct message_address_parser_context { pool_t pool; struct rfc822_parser_context parser; struct message_address *first_addr, *last_addr, addr; string_t *str; bool fill_missing; }; static void add_address(struct message_address_parser_context *ctx) { struct message_address *addr; addr = p_new(ctx->pool, struct message_address, 1); memcpy(addr, &ctx->addr, sizeof(ctx->addr)); i_zero(&ctx->addr); if (ctx->first_addr == NULL) ctx->first_addr = addr; else ctx->last_addr->next = addr; ctx->last_addr = addr; } /* quote with "" and escape all '\', '"' and "'" characters if need */ static void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot) { const char *p; /* see if we need to quote it */ for (p = cstr; *p != '\0'; p++) { if (!IS_ATEXT(*p) && (escape_dot || *p != '.')) break; } if (*p == '\0') { str_append_data(dest, cstr, (size_t) (p - cstr)); return; } /* see if we need to escape it */ for (p = cstr; *p != '\0'; p++) { if (IS_ESCAPED_CHAR(*p)) break; } if (*p == '\0') { /* only quote */ str_append_c(dest, '"'); str_append_data(dest, cstr, (size_t) (p - cstr)); str_append_c(dest, '"'); return; } /* quote and escape */ str_append_c(dest, '"'); str_append_data(dest, cstr, (size_t) (p - cstr)); for (; *p != '\0'; p++) { if (IS_ESCAPED_CHAR(*p)) str_append_c(dest, '\\'); str_append_c(dest, *p); } str_append_c(dest, '"'); } static int parse_local_part(struct message_address_parser_context *ctx) { int ret; /* local-part = dot-atom / quoted-string / obs-local-part obs-local-part = word *("." word) */ i_assert(ctx->parser.data != ctx->parser.end); str_truncate(ctx->str, 0); if (*ctx->parser.data == '"') ret = rfc822_parse_quoted_string(&ctx->parser, ctx->str); else ret = rfc822_parse_dot_atom(&ctx->parser, ctx->str); if (ret < 0) return -1; ctx->addr.mailbox = p_strdup(ctx->pool, str_c(ctx->str)); return ret; } static int parse_domain(struct message_address_parser_context *ctx) { int ret; str_truncate(ctx->str, 0); if ((ret = rfc822_parse_domain(&ctx->parser, ctx->str)) < 0) return -1; ctx->addr.domain = p_strdup(ctx->pool, str_c(ctx->str)); return ret; } static int parse_domain_list(struct message_address_parser_context *ctx) { int ret; /* obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain) */ str_truncate(ctx->str, 0); for (;;) { if (ctx->parser.data == ctx->parser.end) return 0; if (*ctx->parser.data != '@') break; if (str_len(ctx->str) > 0) str_append_c(ctx->str, ','); str_append_c(ctx->str, '@'); if ((ret = rfc822_parse_domain(&ctx->parser, ctx->str)) <= 0) return ret; while (rfc822_skip_lwsp(&ctx->parser) > 0 && *ctx->parser.data == ',') ctx->parser.data++; } ctx->addr.route = p_strdup(ctx->pool, str_c(ctx->str)); return 1; } static int parse_angle_addr(struct message_address_parser_context *ctx) { int ret; /* "<" [ "@" route ":" ] local-part "@" domain ">" */ i_assert(*ctx->parser.data == '<'); ctx->parser.data++; if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) return ret; if (*ctx->parser.data == '@') { if (parse_domain_list(ctx) <= 0 || *ctx->parser.data != ':') { if (ctx->fill_missing) ctx->addr.route = "INVALID_ROUTE"; if (ctx->parser.data == ctx->parser.end) return -1; /* try to continue anyway */ } else { ctx->parser.data++; } if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) return ret; } if (*ctx->parser.data == '>') { /* <> address isn't valid */ } else { if ((ret = parse_local_part(ctx)) <= 0) return ret; if (*ctx->parser.data == '@') { if ((ret = parse_domain(ctx)) <= 0) return ret; } } if (*ctx->parser.data != '>') return -1; ctx->parser.data++; return rfc822_skip_lwsp(&ctx->parser); } static int parse_name_addr(struct message_address_parser_context *ctx) { /* name-addr = [display-name] angle-addr display-name = phrase */ str_truncate(ctx->str, 0); if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 || *ctx->parser.data != '<') return -1; ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->str)); if (*ctx->addr.name == '\0') { /* Cope with "
" without display name */ ctx->addr.name = NULL; } if (parse_angle_addr(ctx) < 0) { /* broken */ if (ctx->fill_missing) ctx->addr.domain = "SYNTAX_ERROR"; ctx->addr.invalid_syntax = TRUE; } return ctx->parser.data != ctx->parser.end; } static int parse_addr_spec(struct message_address_parser_context *ctx) { /* addr-spec = local-part "@" domain */ int ret, ret2 = -2; i_assert(ctx->parser.data != ctx->parser.end); str_truncate(ctx->parser.last_comment, 0); bool quoted_string = *ctx->parser.data == '"'; ret = parse_local_part(ctx); if (ret <= 0) { /* end of input or parsing local-part failed */ ctx->addr.invalid_syntax = TRUE; } if (ret != 0 && *ctx->parser.data == '@') { ret2 = parse_domain(ctx); if (ret2 <= 0) ret = ret2; } if (str_len(ctx->parser.last_comment) > 0) ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->parser.last_comment)); else if (ret2 == -2) { /* So far we've read user without @domain and without (Display Name). We'll assume that a single "user" (already read into addr.mailbox) is a mailbox, but if it's followed by anything else it's a display-name. */ str_append_c(ctx->str, ' '); size_t orig_str_len = str_len(ctx->str); (void)rfc822_parse_phrase(&ctx->parser, ctx->str); if (str_len(ctx->str) != orig_str_len) { ctx->addr.mailbox = NULL; ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->str)); } else { if (!quoted_string) ctx->addr.domain = ""; } ctx->addr.invalid_syntax = TRUE; ret = -1; } return ret; } static void add_fixed_address(struct message_address_parser_context *ctx) { if (ctx->addr.mailbox == NULL) { ctx->addr.mailbox = !ctx->fill_missing ? "" : "MISSING_MAILBOX"; ctx->addr.invalid_syntax = TRUE; } if (ctx->addr.domain == NULL || ctx->addr.domain[0] == '\0') { ctx->addr.domain = !ctx->fill_missing ? "" : "MISSING_DOMAIN"; ctx->addr.invalid_syntax = TRUE; } add_address(ctx); } static int parse_mailbox(struct message_address_parser_context *ctx) { const unsigned char *start; int ret; /* mailbox = name-addr / addr-spec */ start = ctx->parser.data; if ((ret = parse_name_addr(ctx)) < 0) { /* nope, should be addr-spec */ ctx->parser.data = start; ret = parse_addr_spec(ctx); if (ctx->addr.invalid_syntax && ctx->addr.name == NULL && ctx->addr.mailbox != NULL && ctx->addr.domain == NULL) { ctx->addr.name = ctx->addr.mailbox; ctx->addr.mailbox = NULL; } } if (ret < 0) ctx->addr.invalid_syntax = TRUE; add_fixed_address(ctx); return ret; } static int parse_group(struct message_address_parser_context *ctx) { int ret; /* group = display-name ":" [mailbox-list / CFWS] ";" [CFWS] display-name = phrase */ str_truncate(ctx->str, 0); if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 || *ctx->parser.data != ':') return -1; /* from now on don't return -1 even if there are problems, so that the caller knows this is a group */ ctx->parser.data++; if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) ctx->addr.invalid_syntax = TRUE; ctx->addr.mailbox = p_strdup(ctx->pool, str_c(ctx->str)); add_address(ctx); if (ret > 0 && *ctx->parser.data != ';') { for (;;) { /* mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list */ if (parse_mailbox(ctx) <= 0) { /* broken mailbox - try to continue anyway. */ } if (ctx->parser.data == ctx->parser.end || *ctx->parser.data != ',') break; ctx->parser.data++; if (rfc822_skip_lwsp(&ctx->parser) <= 0) { ret = -1; break; } } } if (ret >= 0) { if (ctx->parser.data == ctx->parser.end || *ctx->parser.data != ';') ret = -1; else { ctx->parser.data++; ret = rfc822_skip_lwsp(&ctx->parser); } } if (ret < 0) ctx->addr.invalid_syntax = TRUE; add_address(ctx); return ret == 0 ? 0 : 1; } static int parse_address(struct message_address_parser_context *ctx) { const unsigned char *start; int ret; /* address = mailbox / group */ start = ctx->parser.data; if ((ret = parse_group(ctx)) < 0) { /* not a group, try mailbox */ ctx->parser.data = start; ret = parse_mailbox(ctx); } return ret; } static int parse_address_list(struct message_address_parser_context *ctx, unsigned int max_addresses) { int ret = 0; /* address-list = (address *("," address)) / obs-addr-list */ while (max_addresses > 0) { max_addresses--; if ((ret = parse_address(ctx)) == 0) break; if (ctx->parser.data == ctx->parser.end || *ctx->parser.data != ',') { ret = -1; break; } ctx->parser.data++; if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) { if (ret < 0) { /* ends with some garbage */ add_fixed_address(ctx); } break; } } return ret; } static struct message_address * message_address_parse_real(pool_t pool, const unsigned char *data, size_t size, unsigned int max_addresses, bool fill_missing) { struct message_address_parser_context ctx; i_zero(&ctx); rfc822_parser_init(&ctx.parser, data, size, t_str_new(128)); ctx.pool = pool; ctx.str = t_str_new(128); ctx.fill_missing = fill_missing; if (rfc822_skip_lwsp(&ctx.parser) <= 0) { /* no addresses */ return NULL; } (void)parse_address_list(&ctx, max_addresses); return ctx.first_addr; } struct message_address * message_address_parse(pool_t pool, const unsigned char *data, size_t size, unsigned int max_addresses, bool fill_missing) { struct message_address *addr; if (pool->datastack_pool) { return message_address_parse_real(pool, data, size, max_addresses, fill_missing); } T_BEGIN { addr = message_address_parse_real(pool, data, size, max_addresses, fill_missing); } T_END; return addr; } void message_address_write(string_t *str, const struct message_address *addr) { const char *tmp; bool first = TRUE, in_group = FALSE; /* a) mailbox@domain b) name <@route:mailbox@domain> c) group: .. ; */ while (addr != NULL) { if (first) first = FALSE; else str_append(str, ", "); if (addr->domain == NULL) { if (!in_group) { /* beginning of group. mailbox is the group name, others are NULL. */ if (addr->mailbox != NULL && *addr->mailbox != '\0') { /* check for MIME encoded-word */ if (strstr(addr->mailbox, "=?") != NULL) /* MIME encoded-word MUST NOT appear within a 'quoted-string' so escaping and quoting of phrase is not possible, instead use obsolete RFC822 phrase syntax which allow spaces */ str_append(str, addr->mailbox); else str_append_maybe_escape(str, addr->mailbox, TRUE); } else { /* empty group name needs to be quoted */ str_append(str, "\"\""); } str_append(str, ": "); first = TRUE; } else { /* end of group. all fields should be NULL. */ i_assert(addr->mailbox == NULL); /* cut out the ", " */ tmp = str_c(str)+str_len(str)-2; i_assert((tmp[0] == ',' || tmp[0] == ':') && tmp[1] == ' '); if (tmp[0] == ',' && tmp[1] == ' ') str_truncate(str, str_len(str)-2); else if (tmp[0] == ':' && tmp[1] == ' ') str_truncate(str, str_len(str)-1); str_append_c(str, ';'); } in_group = !in_group; } else { /* "Display Name" */ i_assert(addr->mailbox != NULL); if (addr->name != NULL) { /* check for MIME encoded-word */ if (strstr(addr->name, "=?") != NULL) /* MIME encoded-word MUST NOT appear within a 'quoted-string' so escaping and quoting of phrase is not possible, instead use obsolete RFC822 phrase syntax which allow spaces */ str_append(str, addr->name); else str_append_maybe_escape(str, addr->name, TRUE); } if (addr->route != NULL || addr->mailbox[0] != '\0' || addr->domain[0] != '\0') { if (addr->name != NULL && addr->name[0] != '\0') str_append_c(str, ' '); str_append_c(str, '<'); if (addr->route != NULL) { str_append(str, addr->route); str_append_c(str, ':'); } if (addr->mailbox[0] == '\0') str_append(str, "\"\""); else str_append_maybe_escape(str, addr->mailbox, FALSE); if (addr->domain[0] != '\0') { str_append_c(str, '@'); str_append(str, addr->domain); } str_append_c(str, '>'); } } addr = addr->next; } } static const char *address_headers[] = { "From", "Sender", "Reply-To", "To", "Cc", "Bcc", "Resent-From", "Resent-Sender", "Resent-To", "Resent-Cc", "Resent-Bcc" }; bool message_header_is_address(const char *hdr_name) { unsigned int i; for (i = 0; i < N_ELEMENTS(address_headers); i++) { if (strcasecmp(hdr_name, address_headers[i]) == 0) return TRUE; } return FALSE; } void message_detail_address_parse(const char *delimiter_string, const char *address, const char **username_r, const char **detail_r) { const char *p, *domain; *username_r = address; *detail_r = ""; if (*delimiter_string == '\0') return; domain = strchr(address, '@'); p = strstr(address, delimiter_string); if (p != NULL && (domain == NULL || p < domain)) { /* user+detail@domain */ *username_r = t_strdup_until(*username_r, p); if (domain == NULL) *detail_r = p+strlen(delimiter_string); else { *detail_r = t_strdup_until(p+strlen(delimiter_string), domain); *username_r = t_strconcat(*username_r, domain, NULL); } } } dovecot-2.2.33.2/src/lib-mail/message-size.c0000644000175000017500000000724013165463624015357 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "message-parser.h" #include "message-size.h" int message_get_header_size(struct istream *input, struct message_size *hdr, bool *has_nuls_r) { const unsigned char *msg; size_t i, size, startpos, missing_cr_count; int ret; memset(hdr, 0, sizeof(struct message_size)); *has_nuls_r = FALSE; missing_cr_count = 0; startpos = 0; while (i_stream_read_data(input, &msg, &size, startpos) > 0) { for (i = startpos; i < size; i++) { if (msg[i] != '\n') { if (msg[i] == '\0') *has_nuls_r = TRUE; continue; } hdr->lines++; if (i == 0 || msg[i-1] != '\r') { /* missing CR */ missing_cr_count++; } if (i == 0 || (i == 1 && msg[i-1] == '\r')) { /* no headers at all */ break; } if ((i > 0 && msg[i-1] == '\n') || (i > 1 && msg[i-2] == '\n' && msg[i-1] == '\r')) { /* \n\n or \n\r\n - end of headers */ break; } } if (i < size) { /* end of header */ startpos = i+1; break; } /* leave the last two characters, they may be \r\n */ startpos = size == 1 ? 1 : 2; i_stream_skip(input, i - startpos); hdr->physical_size += i - startpos; } ret = input->stream_errno != 0 ? -1 : 0; i_stream_skip(input, startpos); hdr->physical_size += startpos; hdr->virtual_size = hdr->physical_size + missing_cr_count; i_assert(hdr->virtual_size >= hdr->physical_size); return ret; } int message_get_body_size(struct istream *input, struct message_size *body, bool *has_nuls_r) { const unsigned char *msg; size_t i, size, missing_cr_count; int ret; memset(body, 0, sizeof(struct message_size)); *has_nuls_r = FALSE; missing_cr_count = 0; if ((ret = i_stream_read_data(input, &msg, &size, 0)) <= 0) return ret < 0 && input->stream_errno != 0 ? -1 : 0; if (msg[0] == '\n') missing_cr_count++; do { for (i = 1; i < size; i++) { if (msg[i] > '\n') continue; if (msg[i] == '\n') { if (msg[i-1] != '\r') { /* missing CR */ missing_cr_count++; } /* increase after making sure we didn't break at virtual \r */ body->lines++; } else if (msg[i] == '\0') { *has_nuls_r = TRUE; } } /* leave the last character, it may be \r */ i_stream_skip(input, i - 1); body->physical_size += i - 1; } while (i_stream_read_data(input, &msg, &size, 1) > 0); ret = input->stream_errno != 0 ? -1 : 0; i_stream_skip(input, 1); body->physical_size++; body->virtual_size = body->physical_size + missing_cr_count; i_assert(body->virtual_size >= body->physical_size); return ret; } void message_size_add(struct message_size *dest, const struct message_size *src) { dest->virtual_size += src->virtual_size; dest->physical_size += src->physical_size; dest->lines += src->lines; } int message_skip_virtual(struct istream *input, uoff_t virtual_skip, bool *last_cr_r) { const unsigned char *msg; size_t i, size; bool cr_skipped = FALSE; int ret; *last_cr_r = FALSE; if (virtual_skip == 0) return 0; while ((ret = i_stream_read_data(input, &msg, &size, 0)) > 0) { for (i = 0; i < size && virtual_skip > 0; i++) { virtual_skip--; if (msg[i] == '\r') { /* CR */ if (virtual_skip == 0) *last_cr_r = TRUE; } else if (msg[i] == '\n') { /* LF */ if ((i == 0 && !cr_skipped) || (i > 0 && msg[i-1] != '\r')) { if (virtual_skip == 0) { /* CR/LF boundary */ *last_cr_r = TRUE; break; } virtual_skip--; } } } i_stream_skip(input, i); if (i < size) return 0; cr_skipped = msg[i-1] == '\r'; } i_assert(ret == -1); return input->stream_errno == 0 ? 0 : -1; } dovecot-2.2.33.2/src/lib-mail/Makefile.in0000644000175000017500000013550113172375573014671 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-mail ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libmail_la_LIBADD = am_libmail_la_OBJECTS = istream-attachment-connector.lo \ istream-attachment-extractor.lo istream-binary-converter.lo \ istream-dot.lo istream-header-filter.lo istream-nonuls.lo \ istream-qp-decoder.lo istream-qp-encoder.lo mail-html2text.lo \ mail-user-hash.lo mbox-from.lo message-address.lo \ message-binary-part.lo message-date.lo message-decoder.lo \ message-header-decode.lo message-header-encode.lo \ message-header-hash.lo message-header-parser.lo message-id.lo \ message-parser.lo message-part.lo message-part-data.lo \ message-part-serialize.lo message-search.lo message-size.lo \ message-snippet.lo ostream-dot.lo qp-decoder.lo qp-encoder.lo \ quoted-printable.lo rfc2231-parser.lo rfc822-parser.lo libmail_la_OBJECTS = $(am_libmail_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-istream-dot$(EXEEXT) \ test-istream-attachment$(EXEEXT) \ test-istream-binary-converter$(EXEEXT) \ test-istream-header-filter$(EXEEXT) \ test-istream-qp-decoder$(EXEEXT) \ test-istream-qp-encoder$(EXEEXT) test-mail-html2text$(EXEEXT) \ test-mbox-from$(EXEEXT) test-message-address$(EXEEXT) \ test-message-date$(EXEEXT) test-message-decoder$(EXEEXT) \ test-message-header-decode$(EXEEXT) \ test-message-header-encode$(EXEEXT) \ test-message-header-hash$(EXEEXT) \ test-message-header-parser$(EXEEXT) test-message-id$(EXEEXT) \ test-message-parser$(EXEEXT) test-message-part$(EXEEXT) \ test-message-search$(EXEEXT) test-message-snippet$(EXEEXT) \ test-ostream-dot$(EXEEXT) test-qp-decoder$(EXEEXT) \ test-qp-encoder$(EXEEXT) test-quoted-printable$(EXEEXT) \ test-rfc2231-parser$(EXEEXT) test-rfc822-parser$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_istream_attachment_OBJECTS = \ test-istream-attachment.$(OBJEXT) test_istream_attachment_OBJECTS = \ $(am_test_istream_attachment_OBJECTS) am_test_istream_binary_converter_OBJECTS = \ test-istream-binary-converter.$(OBJEXT) test_istream_binary_converter_OBJECTS = \ $(am_test_istream_binary_converter_OBJECTS) am_test_istream_dot_OBJECTS = test-istream-dot.$(OBJEXT) test_istream_dot_OBJECTS = $(am_test_istream_dot_OBJECTS) am_test_istream_header_filter_OBJECTS = \ test-istream-header-filter.$(OBJEXT) test_istream_header_filter_OBJECTS = \ $(am_test_istream_header_filter_OBJECTS) am_test_istream_qp_decoder_OBJECTS = \ test-istream-qp-decoder.$(OBJEXT) test_istream_qp_decoder_OBJECTS = \ $(am_test_istream_qp_decoder_OBJECTS) am_test_istream_qp_encoder_OBJECTS = \ test-istream-qp-encoder.$(OBJEXT) test_istream_qp_encoder_OBJECTS = \ $(am_test_istream_qp_encoder_OBJECTS) am_test_mail_html2text_OBJECTS = test-mail-html2text.$(OBJEXT) test_mail_html2text_OBJECTS = $(am_test_mail_html2text_OBJECTS) am_test_mbox_from_OBJECTS = test-mbox-from.$(OBJEXT) test_mbox_from_OBJECTS = $(am_test_mbox_from_OBJECTS) am_test_message_address_OBJECTS = test-message-address.$(OBJEXT) test_message_address_OBJECTS = $(am_test_message_address_OBJECTS) am_test_message_date_OBJECTS = test-message-date.$(OBJEXT) test_message_date_OBJECTS = $(am_test_message_date_OBJECTS) am_test_message_decoder_OBJECTS = test-message-decoder.$(OBJEXT) test_message_decoder_OBJECTS = $(am_test_message_decoder_OBJECTS) am_test_message_header_decode_OBJECTS = \ test-message-header-decode.$(OBJEXT) test_message_header_decode_OBJECTS = \ $(am_test_message_header_decode_OBJECTS) am_test_message_header_encode_OBJECTS = \ test-message-header-encode.$(OBJEXT) test_message_header_encode_OBJECTS = \ $(am_test_message_header_encode_OBJECTS) am_test_message_header_hash_OBJECTS = \ test-message-header-hash.$(OBJEXT) test_message_header_hash_OBJECTS = \ $(am_test_message_header_hash_OBJECTS) am_test_message_header_parser_OBJECTS = \ test-message-header-parser.$(OBJEXT) test_message_header_parser_OBJECTS = \ $(am_test_message_header_parser_OBJECTS) am_test_message_id_OBJECTS = test-message-id.$(OBJEXT) test_message_id_OBJECTS = $(am_test_message_id_OBJECTS) am_test_message_parser_OBJECTS = test-message-parser.$(OBJEXT) test_message_parser_OBJECTS = $(am_test_message_parser_OBJECTS) am_test_message_part_OBJECTS = test-message-part.$(OBJEXT) test_message_part_OBJECTS = $(am_test_message_part_OBJECTS) am_test_message_search_OBJECTS = test-message-search.$(OBJEXT) test_message_search_OBJECTS = $(am_test_message_search_OBJECTS) am_test_message_snippet_OBJECTS = test-message-snippet.$(OBJEXT) test_message_snippet_OBJECTS = $(am_test_message_snippet_OBJECTS) am_test_ostream_dot_OBJECTS = test-ostream-dot.$(OBJEXT) test_ostream_dot_OBJECTS = $(am_test_ostream_dot_OBJECTS) am_test_qp_decoder_OBJECTS = test-qp-decoder.$(OBJEXT) test_qp_decoder_OBJECTS = $(am_test_qp_decoder_OBJECTS) am_test_qp_encoder_OBJECTS = test-qp-encoder.$(OBJEXT) test_qp_encoder_OBJECTS = $(am_test_qp_encoder_OBJECTS) am_test_quoted_printable_OBJECTS = test-quoted-printable.$(OBJEXT) test_quoted_printable_OBJECTS = $(am_test_quoted_printable_OBJECTS) am_test_rfc2231_parser_OBJECTS = test-rfc2231-parser.$(OBJEXT) test_rfc2231_parser_OBJECTS = $(am_test_rfc2231_parser_OBJECTS) am_test_rfc822_parser_OBJECTS = test-rfc822-parser.$(OBJEXT) test_rfc822_parser_OBJECTS = $(am_test_rfc822_parser_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libmail_la_SOURCES) $(test_istream_attachment_SOURCES) \ $(test_istream_binary_converter_SOURCES) \ $(test_istream_dot_SOURCES) \ $(test_istream_header_filter_SOURCES) \ $(test_istream_qp_decoder_SOURCES) \ $(test_istream_qp_encoder_SOURCES) \ $(test_mail_html2text_SOURCES) $(test_mbox_from_SOURCES) \ $(test_message_address_SOURCES) $(test_message_date_SOURCES) \ $(test_message_decoder_SOURCES) \ $(test_message_header_decode_SOURCES) \ $(test_message_header_encode_SOURCES) \ $(test_message_header_hash_SOURCES) \ $(test_message_header_parser_SOURCES) \ $(test_message_id_SOURCES) $(test_message_parser_SOURCES) \ $(test_message_part_SOURCES) $(test_message_search_SOURCES) \ $(test_message_snippet_SOURCES) $(test_ostream_dot_SOURCES) \ $(test_qp_decoder_SOURCES) $(test_qp_encoder_SOURCES) \ $(test_quoted_printable_SOURCES) \ $(test_rfc2231_parser_SOURCES) $(test_rfc822_parser_SOURCES) DIST_SOURCES = $(libmail_la_SOURCES) \ $(test_istream_attachment_SOURCES) \ $(test_istream_binary_converter_SOURCES) \ $(test_istream_dot_SOURCES) \ $(test_istream_header_filter_SOURCES) \ $(test_istream_qp_decoder_SOURCES) \ $(test_istream_qp_encoder_SOURCES) \ $(test_mail_html2text_SOURCES) $(test_mbox_from_SOURCES) \ $(test_message_address_SOURCES) $(test_message_date_SOURCES) \ $(test_message_decoder_SOURCES) \ $(test_message_header_decode_SOURCES) \ $(test_message_header_encode_SOURCES) \ $(test_message_header_hash_SOURCES) \ $(test_message_header_parser_SOURCES) \ $(test_message_id_SOURCES) $(test_message_parser_SOURCES) \ $(test_message_part_SOURCES) $(test_message_search_SOURCES) \ $(test_message_snippet_SOURCES) $(test_ostream_dot_SOURCES) \ $(test_qp_decoder_SOURCES) $(test_qp_encoder_SOURCES) \ $(test_quoted_printable_SOURCES) \ $(test_rfc2231_parser_SOURCES) $(test_rfc822_parser_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libmail.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset libmail_la_SOURCES = \ istream-attachment-connector.c \ istream-attachment-extractor.c \ istream-binary-converter.c \ istream-dot.c \ istream-header-filter.c \ istream-nonuls.c \ istream-qp-decoder.c \ istream-qp-encoder.c \ mail-html2text.c \ mail-user-hash.c \ mbox-from.c \ message-address.c \ message-binary-part.c \ message-date.c \ message-decoder.c \ message-header-decode.c \ message-header-encode.c \ message-header-hash.c \ message-header-parser.c \ message-id.c \ message-parser.c \ message-part.c \ message-part-data.c \ message-part-serialize.c \ message-search.c \ message-size.c \ message-snippet.c \ ostream-dot.c \ qp-decoder.c \ qp-encoder.c \ quoted-printable.c \ rfc2231-parser.c \ rfc822-parser.c noinst_HEADERS = \ html-entities.h headers = \ istream-attachment-connector.h \ istream-attachment-extractor.h \ istream-binary-converter.h \ istream-dot.h \ istream-header-filter.h \ istream-nonuls.h \ istream-qp.h \ mail-user-hash.h \ mbox-from.h \ mail-html2text.h \ mail-types.h \ message-address.h \ message-binary-part.h \ message-date.h \ message-decoder.h \ message-header-decode.h \ message-header-encode.h \ message-header-hash.h \ message-header-parser.h \ message-id.h \ message-parser.h \ message-part.h \ message-part-data.h \ message-part-serialize.h \ message-search.h \ message-size.h \ message-snippet.h \ ostream-dot.h \ qp-decoder.h \ qp-encoder.h \ quoted-printable.h \ rfc2231-parser.h \ rfc822-parser.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-istream-dot \ test-istream-attachment \ test-istream-binary-converter \ test-istream-header-filter \ test-istream-qp-decoder \ test-istream-qp-encoder \ test-mail-html2text \ test-mbox-from \ test-message-address \ test-message-date \ test-message-decoder \ test-message-header-decode \ test-message-header-encode \ test-message-header-hash \ test-message-header-parser \ test-message-id \ test-message-parser \ test-message-part \ test-message-search \ test-message-snippet \ test-ostream-dot \ test-qp-decoder \ test-qp-encoder \ test-quoted-printable \ test-rfc2231-parser \ test-rfc822-parser test_libs = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_istream_dot_SOURCES = test-istream-dot.c test_istream_dot_LDADD = $(test_libs) test_istream_dot_DEPENDENCIES = $(test_deps) test_istream_qp_decoder_SOURCES = test-istream-qp-decoder.c test_istream_qp_decoder_LDADD = $(test_libs) test_istream_qp_decoder_DEPENDENCIES = $(test_deps) test_istream_qp_encoder_SOURCES = test-istream-qp-encoder.c test_istream_qp_encoder_LDADD = $(test_libs) test_istream_qp_encoder_DEPENDENCIES = $(test_deps) test_istream_binary_converter_SOURCES = test-istream-binary-converter.c test_istream_binary_converter_LDADD = $(test_libs) test_istream_binary_converter_DEPENDENCIES = $(test_deps) test_istream_attachment_SOURCES = test-istream-attachment.c test_istream_attachment_LDADD = $(test_libs) test_istream_attachment_DEPENDENCIES = $(test_deps) test_istream_header_filter_SOURCES = test-istream-header-filter.c test_istream_header_filter_LDADD = $(test_libs) test_istream_header_filter_DEPENDENCIES = $(test_deps) test_mbox_from_SOURCES = test-mbox-from.c test_mbox_from_LDADD = $(test_libs) test_mbox_from_DEPENDENCIES = $(test_deps) test_message_address_SOURCES = test-message-address.c test_message_address_LDADD = $(test_libs) test_message_address_DEPENDENCIES = $(test_deps) test_message_date_SOURCES = test-message-date.c test_message_date_LDADD = $(test_libs) test_message_date_DEPENDENCIES = $(test_deps) test_message_decoder_SOURCES = test-message-decoder.c test_message_decoder_LDADD = $(test_libs) ../lib-charset/libcharset.la test_message_decoder_DEPENDENCIES = $(test_deps) ../lib-charset/libcharset.la test_message_header_decode_SOURCES = test-message-header-decode.c test_message_header_decode_LDADD = $(test_libs) test_message_header_decode_DEPENDENCIES = $(test_deps) test_message_header_encode_SOURCES = test-message-header-encode.c test_message_header_encode_LDADD = $(test_libs) test_message_header_encode_DEPENDENCIES = $(test_deps) test_message_header_hash_SOURCES = test-message-header-hash.c test_message_header_hash_LDADD = $(test_libs) test_message_header_hash_DEPENDENCIES = $(test_deps) test_message_header_parser_SOURCES = test-message-header-parser.c test_message_header_parser_LDADD = $(test_libs) test_message_header_parser_DEPENDENCIES = $(test_deps) test_message_id_SOURCES = test-message-id.c test_message_id_LDADD = $(test_libs) test_message_id_DEPENDENCIES = $(test_deps) test_message_parser_SOURCES = test-message-parser.c test_message_parser_LDADD = $(test_libs) test_message_parser_DEPENDENCIES = $(test_deps) test_message_part_SOURCES = test-message-part.c test_message_part_LDADD = $(test_libs) test_message_part_DEPENDENCIES = $(test_deps) test_message_search_SOURCES = test-message-search.c test_message_search_LDADD = $(test_libs) ../lib-charset/libcharset.la test_message_search_DEPENDENCIES = $(test_deps) ../lib-charset/libcharset.la test_message_snippet_SOURCES = test-message-snippet.c test_message_snippet_LDADD = $(test_message_decoder_LDADD) test_message_snippet_DEPENDENCIES = $(test_deps) test_mail_html2text_SOURCES = test-mail-html2text.c test_mail_html2text_LDADD = $(test_libs) test_mail_html2text_DEPENDENCIES = $(test_deps) test_ostream_dot_SOURCES = test-ostream-dot.c test_ostream_dot_LDADD = $(test_libs) test_ostream_dot_DEPENDENCIES = $(test_deps) test_qp_decoder_SOURCES = test-qp-decoder.c test_qp_decoder_LDADD = $(test_libs) test_qp_decoder_DEPENDENCIES = $(test_deps) test_qp_encoder_SOURCES = test-qp-encoder.c test_qp_encoder_LDADD = $(test_libs) test_qp_encoder_DEPENDENCIES = $(test_deps) test_quoted_printable_SOURCES = test-quoted-printable.c test_quoted_printable_LDADD = $(test_libs) test_quoted_printable_DEPENDENCIES = $(test_deps) test_rfc2231_parser_SOURCES = test-rfc2231-parser.c test_rfc2231_parser_LDADD = $(test_libs) test_rfc2231_parser_DEPENDENCIES = $(test_deps) test_rfc822_parser_SOURCES = test-rfc822-parser.c test_rfc822_parser_LDADD = $(test_libs) test_rfc822_parser_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-mail/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-mail/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libmail.la: $(libmail_la_OBJECTS) $(libmail_la_DEPENDENCIES) $(EXTRA_libmail_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libmail_la_OBJECTS) $(libmail_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-istream-attachment$(EXEEXT): $(test_istream_attachment_OBJECTS) $(test_istream_attachment_DEPENDENCIES) $(EXTRA_test_istream_attachment_DEPENDENCIES) @rm -f test-istream-attachment$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_attachment_OBJECTS) $(test_istream_attachment_LDADD) $(LIBS) test-istream-binary-converter$(EXEEXT): $(test_istream_binary_converter_OBJECTS) $(test_istream_binary_converter_DEPENDENCIES) $(EXTRA_test_istream_binary_converter_DEPENDENCIES) @rm -f test-istream-binary-converter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_binary_converter_OBJECTS) $(test_istream_binary_converter_LDADD) $(LIBS) test-istream-dot$(EXEEXT): $(test_istream_dot_OBJECTS) $(test_istream_dot_DEPENDENCIES) $(EXTRA_test_istream_dot_DEPENDENCIES) @rm -f test-istream-dot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_dot_OBJECTS) $(test_istream_dot_LDADD) $(LIBS) test-istream-header-filter$(EXEEXT): $(test_istream_header_filter_OBJECTS) $(test_istream_header_filter_DEPENDENCIES) $(EXTRA_test_istream_header_filter_DEPENDENCIES) @rm -f test-istream-header-filter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_header_filter_OBJECTS) $(test_istream_header_filter_LDADD) $(LIBS) test-istream-qp-decoder$(EXEEXT): $(test_istream_qp_decoder_OBJECTS) $(test_istream_qp_decoder_DEPENDENCIES) $(EXTRA_test_istream_qp_decoder_DEPENDENCIES) @rm -f test-istream-qp-decoder$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_qp_decoder_OBJECTS) $(test_istream_qp_decoder_LDADD) $(LIBS) test-istream-qp-encoder$(EXEEXT): $(test_istream_qp_encoder_OBJECTS) $(test_istream_qp_encoder_DEPENDENCIES) $(EXTRA_test_istream_qp_encoder_DEPENDENCIES) @rm -f test-istream-qp-encoder$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_istream_qp_encoder_OBJECTS) $(test_istream_qp_encoder_LDADD) $(LIBS) test-mail-html2text$(EXEEXT): $(test_mail_html2text_OBJECTS) $(test_mail_html2text_DEPENDENCIES) $(EXTRA_test_mail_html2text_DEPENDENCIES) @rm -f test-mail-html2text$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mail_html2text_OBJECTS) $(test_mail_html2text_LDADD) $(LIBS) test-mbox-from$(EXEEXT): $(test_mbox_from_OBJECTS) $(test_mbox_from_DEPENDENCIES) $(EXTRA_test_mbox_from_DEPENDENCIES) @rm -f test-mbox-from$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mbox_from_OBJECTS) $(test_mbox_from_LDADD) $(LIBS) test-message-address$(EXEEXT): $(test_message_address_OBJECTS) $(test_message_address_DEPENDENCIES) $(EXTRA_test_message_address_DEPENDENCIES) @rm -f test-message-address$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_address_OBJECTS) $(test_message_address_LDADD) $(LIBS) test-message-date$(EXEEXT): $(test_message_date_OBJECTS) $(test_message_date_DEPENDENCIES) $(EXTRA_test_message_date_DEPENDENCIES) @rm -f test-message-date$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_date_OBJECTS) $(test_message_date_LDADD) $(LIBS) test-message-decoder$(EXEEXT): $(test_message_decoder_OBJECTS) $(test_message_decoder_DEPENDENCIES) $(EXTRA_test_message_decoder_DEPENDENCIES) @rm -f test-message-decoder$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_decoder_OBJECTS) $(test_message_decoder_LDADD) $(LIBS) test-message-header-decode$(EXEEXT): $(test_message_header_decode_OBJECTS) $(test_message_header_decode_DEPENDENCIES) $(EXTRA_test_message_header_decode_DEPENDENCIES) @rm -f test-message-header-decode$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_header_decode_OBJECTS) $(test_message_header_decode_LDADD) $(LIBS) test-message-header-encode$(EXEEXT): $(test_message_header_encode_OBJECTS) $(test_message_header_encode_DEPENDENCIES) $(EXTRA_test_message_header_encode_DEPENDENCIES) @rm -f test-message-header-encode$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_header_encode_OBJECTS) $(test_message_header_encode_LDADD) $(LIBS) test-message-header-hash$(EXEEXT): $(test_message_header_hash_OBJECTS) $(test_message_header_hash_DEPENDENCIES) $(EXTRA_test_message_header_hash_DEPENDENCIES) @rm -f test-message-header-hash$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_header_hash_OBJECTS) $(test_message_header_hash_LDADD) $(LIBS) test-message-header-parser$(EXEEXT): $(test_message_header_parser_OBJECTS) $(test_message_header_parser_DEPENDENCIES) $(EXTRA_test_message_header_parser_DEPENDENCIES) @rm -f test-message-header-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_header_parser_OBJECTS) $(test_message_header_parser_LDADD) $(LIBS) test-message-id$(EXEEXT): $(test_message_id_OBJECTS) $(test_message_id_DEPENDENCIES) $(EXTRA_test_message_id_DEPENDENCIES) @rm -f test-message-id$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_id_OBJECTS) $(test_message_id_LDADD) $(LIBS) test-message-parser$(EXEEXT): $(test_message_parser_OBJECTS) $(test_message_parser_DEPENDENCIES) $(EXTRA_test_message_parser_DEPENDENCIES) @rm -f test-message-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_parser_OBJECTS) $(test_message_parser_LDADD) $(LIBS) test-message-part$(EXEEXT): $(test_message_part_OBJECTS) $(test_message_part_DEPENDENCIES) $(EXTRA_test_message_part_DEPENDENCIES) @rm -f test-message-part$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_part_OBJECTS) $(test_message_part_LDADD) $(LIBS) test-message-search$(EXEEXT): $(test_message_search_OBJECTS) $(test_message_search_DEPENDENCIES) $(EXTRA_test_message_search_DEPENDENCIES) @rm -f test-message-search$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_search_OBJECTS) $(test_message_search_LDADD) $(LIBS) test-message-snippet$(EXEEXT): $(test_message_snippet_OBJECTS) $(test_message_snippet_DEPENDENCIES) $(EXTRA_test_message_snippet_DEPENDENCIES) @rm -f test-message-snippet$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_message_snippet_OBJECTS) $(test_message_snippet_LDADD) $(LIBS) test-ostream-dot$(EXEEXT): $(test_ostream_dot_OBJECTS) $(test_ostream_dot_DEPENDENCIES) $(EXTRA_test_ostream_dot_DEPENDENCIES) @rm -f test-ostream-dot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_ostream_dot_OBJECTS) $(test_ostream_dot_LDADD) $(LIBS) test-qp-decoder$(EXEEXT): $(test_qp_decoder_OBJECTS) $(test_qp_decoder_DEPENDENCIES) $(EXTRA_test_qp_decoder_DEPENDENCIES) @rm -f test-qp-decoder$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_qp_decoder_OBJECTS) $(test_qp_decoder_LDADD) $(LIBS) test-qp-encoder$(EXEEXT): $(test_qp_encoder_OBJECTS) $(test_qp_encoder_DEPENDENCIES) $(EXTRA_test_qp_encoder_DEPENDENCIES) @rm -f test-qp-encoder$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_qp_encoder_OBJECTS) $(test_qp_encoder_LDADD) $(LIBS) test-quoted-printable$(EXEEXT): $(test_quoted_printable_OBJECTS) $(test_quoted_printable_DEPENDENCIES) $(EXTRA_test_quoted_printable_DEPENDENCIES) @rm -f test-quoted-printable$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_quoted_printable_OBJECTS) $(test_quoted_printable_LDADD) $(LIBS) test-rfc2231-parser$(EXEEXT): $(test_rfc2231_parser_OBJECTS) $(test_rfc2231_parser_DEPENDENCIES) $(EXTRA_test_rfc2231_parser_DEPENDENCIES) @rm -f test-rfc2231-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_rfc2231_parser_OBJECTS) $(test_rfc2231_parser_LDADD) $(LIBS) test-rfc822-parser$(EXEEXT): $(test_rfc822_parser_OBJECTS) $(test_rfc822_parser_DEPENDENCIES) $(EXTRA_test_rfc822_parser_DEPENDENCIES) @rm -f test-rfc822-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_rfc822_parser_OBJECTS) $(test_rfc822_parser_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-attachment-connector.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-attachment-extractor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-binary-converter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-dot.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-header-filter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-nonuls.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-qp-decoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-qp-encoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-html2text.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-user-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox-from.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-address.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-binary-part.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-date.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-decoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-header-decode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-header-encode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-header-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-header-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-id.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-part-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-part-serialize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-part.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-size.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-snippet.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-dot.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qp-decoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qp-encoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quoted-printable.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc2231-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc822-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-attachment.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-binary-converter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-dot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-header-filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-qp-decoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream-qp-encoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mail-html2text.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mbox-from.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-address.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-date.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-decoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-header-decode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-header-encode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-header-hash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-header-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-id.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-part.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-search.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-message-snippet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-ostream-dot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-qp-decoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-qp-encoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-quoted-printable.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-rfc2231-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-rfc822-parser.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-mail/istream-header-filter.h0000644000175000017500000000343413123174404017133 00000000000000#ifndef ISTREAM_HEADER_FILTER_H #define ISTREAM_HEADER_FILTER_H struct header_filter_istream; enum header_filter_flags { /* Include only specified headers in output.*/ HEADER_FILTER_INCLUDE = 0x01, /* Exclude specified headers from output. */ HEADER_FILTER_EXCLUDE = 0x02, /* Use LF linefeeds instead of CRLF. */ HEADER_FILTER_NO_CR = 0x04, /* Return EOF at the beginning of message body. */ HEADER_FILTER_HIDE_BODY = 0x08, /* If the empty "end of headers" line doesn't exist, add it. */ HEADER_FILTER_ADD_MISSING_EOH = 0x10, /* If body doesn't end with [CR]LF, add it/them. */ HEADER_FILTER_END_BODY_WITH_LF = 0x20, /* Preserve the original LF or CRLF. */ HEADER_FILTER_CRLF_PRESERVE = 0x40 }; struct message_header_line; typedef void header_filter_callback(struct header_filter_istream *input, struct message_header_line *hdr, bool *matched, void *context); extern header_filter_callback *null_header_filter_callback; /* NOTE: headers list must be sorted. */ struct istream * i_stream_create_header_filter(struct istream *input, enum header_filter_flags flags, const char *const *headers, unsigned int headers_count, header_filter_callback *callback, void *context) ATTR_NULL(6); #define i_stream_create_header_filter(input, flags, headers, headers_count, \ callback, context) \ i_stream_create_header_filter(input, flags, headers, headers_count + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct header_filter_istream *, \ struct message_header_line *, bool *, typeof(context))), \ (header_filter_callback *)callback, context) /* Add more data to headers. Should called from the filter callback. */ void i_stream_header_filter_add(struct header_filter_istream *input, const void *data, size_t size); #endif dovecot-2.2.33.2/src/lib-mail/message-search.h0000644000175000017500000000311613123174404015642 00000000000000#ifndef MESSAGE_SEARCH_H #define MESSAGE_SEARCH_H struct message_block; struct message_part; struct message_search_context; enum message_search_flags { /* Skip the main header and all the MIME headers. */ MESSAGE_SEARCH_FLAG_SKIP_HEADERS = 0x01 }; /* The key must be given in UTF-8 charset */ struct message_search_context * message_search_init(const char *normalized_key_utf8, normalizer_func_t *normalizer, enum message_search_flags flags); void message_search_deinit(struct message_search_context **ctx); /* Returns TRUE if key is found from input buffer, FALSE if not. */ bool message_search_more(struct message_search_context *ctx, struct message_block *raw_block); /* Same as message_search_more(), but return the decoded block. If the same input is being fed to multiple searches, this avoids duplicating the work by doing the following searches with message_search_more_decoded() */ bool message_search_more_get_decoded(struct message_search_context *ctx, struct message_block *raw_block, struct message_block *decoded_block_r); /* The data has already passed through decoder. */ bool message_search_more_decoded(struct message_search_context *ctx, struct message_block *block); void message_search_reset(struct message_search_context *ctx); /* Search a full message. Returns 1 if match was found, 0 if not, -1 if error (if stream_error == 0, the parts contained broken data) */ int message_search_msg(struct message_search_context *ctx, struct istream *input, struct message_part *parts, const char **error_r) ATTR_NULL(3); #endif dovecot-2.2.33.2/src/lib-mail/message-binary-part.c0000644000175000017500000000241113123174404016615 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "numpack.h" #include "message-binary-part.h" void message_binary_part_serialize(const struct message_binary_part *parts, buffer_t *dest) { const struct message_binary_part *part; for (part = parts; part != NULL; part = part->next) { numpack_encode(dest, part->physical_pos); numpack_encode(dest, part->binary_hdr_size); numpack_encode(dest, part->binary_body_size); numpack_encode(dest, part->binary_body_lines_count); } } int message_binary_part_deserialize(pool_t pool, const void *data, size_t size, struct message_binary_part **parts_r) { const uint8_t *p = data, *end = p + size; uint64_t n1, n2, n3, n4; struct message_binary_part *part = NULL, *prev_part = NULL; while (p != end) { part = p_new(pool, struct message_binary_part, 1); part->next = prev_part; prev_part = part; if (numpack_decode(&p, end, &n1) < 0 || numpack_decode(&p, end, &n2) < 0 || numpack_decode(&p, end, &n3) < 0 || numpack_decode(&p, end, &n4) < 0 || n4 > UINT_MAX) return -1; part->physical_pos = n1; part->binary_hdr_size = n2; part->binary_body_size = n3; part->binary_body_lines_count = n4; } *parts_r = part; return 0; } dovecot-2.2.33.2/src/lib-mail/test-rfc822-parser.c0000644000175000017500000000367713165463624016252 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "rfc822-parser.h" #include "test-common.h" static void test_rfc822_parse_quoted_string(void) { const struct { const char *input, *output; int ret; } tests[] = { { "\"", "", -1 }, { "\"\"", "", 0 }, { "\"foo\"", "foo", 0 }, { "\"\"foo", "", 1 }, { "\"\"\"", "", 1 }, { "\"\\\"\"", "\"", 0 }, { "\"\\\\\"", "\\", 0 }, { "\"\\\\foo\\\\foo\\\\\"", "\\foo\\foo\\", 0 } }; struct rfc822_parser_context parser; string_t *str = t_str_new(64); unsigned int i = 0; test_begin("rfc822 parse quoted string"); for (i = 0; i < N_ELEMENTS(tests); i++) { rfc822_parser_init(&parser, (const void *)tests[i].input, strlen(tests[i].input), NULL); test_assert_idx(rfc822_parse_quoted_string(&parser, str) == tests[i].ret, i); test_assert_idx(tests[i].ret < 0 || strcmp(tests[i].output, str_c(str)) == 0, i); str_truncate(str, 0); } test_end(); } static void test_rfc822_parse_content_param(void) { const char *input = "; key1=value1#$!%&'*+-.^_`{|}~" "; key2=\" \\\"(),/:;<=>?@[\\\\]\""; const struct { const char *key, *value; } output[] = { { "key1", "value1#$!%&'*+-.^_`{|}~" }, { "key2", " \"(),/:;<=>?@[\\]" } }; struct rfc822_parser_context parser; const char *key, *value; unsigned int i = 0; int ret; test_begin("rfc822 parse content param"); rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); while ((ret = rfc822_parse_content_param(&parser, &key, &value)) > 0 && i < N_ELEMENTS(output)) { test_assert_idx(strcmp(output[i].key, key) == 0, i); test_assert_idx(strcmp(output[i].value, value) == 0, i); i++; } test_assert(ret == 0); test_assert(i == N_ELEMENTS(output)); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_rfc822_parse_quoted_string, test_rfc822_parse_content_param, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/message-decoder.h0000644000175000017500000000334113123174404016002 00000000000000#ifndef MESSAGE_DECODER_H #define MESSAGE_DECODER_H #include "unichar.h" struct message_header_line; enum message_cte { MESSAGE_CTE_UNKNOWN = 0, MESSAGE_CTE_78BIT, MESSAGE_CTE_BINARY, MESSAGE_CTE_QP, MESSAGE_CTE_BASE64 }; enum message_decoder_flags { /* Return binary MIME parts as-is without any conversion. */ MESSAGE_DECODER_FLAG_RETURN_BINARY = 0x02 }; struct message_block; /* Decode message's contents as UTF-8, both the headers and the MIME bodies. The bodies are decoded from quoted-printable and base64 formats if needed. */ struct message_decoder_context * message_decoder_init(normalizer_func_t *normalizer, enum message_decoder_flags flags); void message_decoder_deinit(struct message_decoder_context **ctx); /* Change the MESSAGE_DECODER_FLAG_RETURN_BINARY flag */ void message_decoder_set_return_binary(struct message_decoder_context *ctx, bool set); /* Decode input and return decoded output. Headers are returned only in their full multiline forms. Returns TRUE if output is given, FALSE if more data is needed. If the input ends in a partial character, it's returned in the next output. */ bool message_decoder_decode_next_block(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output); /* Returns the parsed Content-Type of the current MIME part. If there is no explicit Content-Type, returns NULL. */ const char * message_decoder_current_content_type(struct message_decoder_context *ctx); /* Call whenever message changes */ void message_decoder_decode_reset(struct message_decoder_context *ctx); /* Decode Content-Transfer-Encoding header. */ enum message_cte message_decoder_parse_cte(struct message_header_line *hdr); #endif dovecot-2.2.33.2/src/lib-mail/istream-attachment-connector.c0000644000175000017500000001054713165463624020551 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "istream-concat.h" #include "istream-sized.h" #include "istream-base64.h" #include "istream-attachment-connector.h" struct istream_attachment_connector { pool_t pool; struct istream *base_input; uoff_t base_input_offset, msg_size; uoff_t encoded_offset; ARRAY(struct istream *) streams; }; struct istream_attachment_connector * istream_attachment_connector_begin(struct istream *base_input, uoff_t msg_size) { struct istream_attachment_connector *conn; pool_t pool; pool = pool_alloconly_create("istream-attachment-connector", 1024); conn = p_new(pool, struct istream_attachment_connector, 1); conn->pool = pool; conn->base_input = base_input; conn->base_input_offset = base_input->v_offset; conn->msg_size = msg_size; p_array_init(&conn->streams, pool, 8); i_stream_ref(conn->base_input); return conn; } int istream_attachment_connector_add(struct istream_attachment_connector *conn, struct istream *decoded_input, uoff_t start_offset, uoff_t encoded_size, unsigned int base64_blocks_per_line, bool base64_have_crlf, const char **error_r) { struct istream *input, *input2; uoff_t base_prefix_size; if (start_offset < conn->encoded_offset) { *error_r = t_strdup_printf( "Attachment %s points before the previous attachment " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", i_stream_get_name(decoded_input), start_offset, conn->encoded_offset); return -1; } base_prefix_size = start_offset - conn->encoded_offset; if (start_offset + encoded_size > conn->msg_size) { *error_r = t_strdup_printf( "Attachment %s points outside message " "(%"PRIuUOFF_T" + %"PRIuUOFF_T" > %"PRIuUOFF_T")", i_stream_get_name(decoded_input), start_offset, encoded_size, conn->msg_size); return -1; } if (base_prefix_size > 0) { /* add a part of the base message before the attachment */ input = i_stream_create_min_sized_range(conn->base_input, conn->base_input_offset, base_prefix_size); i_stream_set_name(input, t_strdup_printf("%s middle", i_stream_get_name(conn->base_input))); array_append(&conn->streams, &input, 1); conn->base_input_offset += base_prefix_size; conn->encoded_offset += base_prefix_size; } conn->encoded_offset += encoded_size; if (base64_blocks_per_line == 0) { input = decoded_input; i_stream_ref(input); } else { input = i_stream_create_base64_encoder(decoded_input, base64_blocks_per_line*4, base64_have_crlf); i_stream_set_name(input, t_strdup_printf("%s[base64:%u b/l%s]", i_stream_get_name(decoded_input), base64_blocks_per_line, base64_have_crlf ? ",crlf" : "")); } input2 = i_stream_create_sized(input, encoded_size); array_append(&conn->streams, &input2, 1); i_stream_unref(&input); return 0; } static void istream_attachment_connector_free(struct istream_attachment_connector *conn) { struct istream *const *streamp, *stream; array_foreach(&conn->streams, streamp) { stream = *streamp; if (stream != NULL) i_stream_unref(&stream); } i_stream_unref(&conn->base_input); pool_unref(&conn->pool); } struct istream * istream_attachment_connector_finish(struct istream_attachment_connector **_conn) { struct istream_attachment_connector *conn = *_conn; struct istream **inputs, *input; uoff_t trailer_size; *_conn = NULL; if (conn->base_input_offset != conn->msg_size) { i_assert(conn->base_input_offset < conn->msg_size); if (conn->msg_size != (uoff_t)-1) { trailer_size = conn->msg_size - conn->encoded_offset; input = i_stream_create_sized_range(conn->base_input, conn->base_input_offset, trailer_size); i_stream_set_name(input, t_strdup_printf( "%s trailer", i_stream_get_name(conn->base_input))); } else { input = i_stream_create_range(conn->base_input, conn->base_input_offset, (uoff_t)-1); } array_append(&conn->streams, &input, 1); } array_append_zero(&conn->streams); inputs = array_idx_modifiable(&conn->streams, 0); input = i_stream_create_concat(inputs); istream_attachment_connector_free(conn); return input; } void istream_attachment_connector_abort(struct istream_attachment_connector **_conn) { struct istream_attachment_connector *conn = *_conn; *_conn = NULL; istream_attachment_connector_free(conn); } dovecot-2.2.33.2/src/lib-mail/mbox-from.h0000644000175000017500000000070313123174404014660 00000000000000#ifndef MBOX_FROM_H #define MBOX_FROM_H /* Parse time and sender from mbox-compatible From_-line. msg points to the data after "From ". */ int mbox_from_parse(const unsigned char *msg, size_t size, time_t *time_r, int *tz_offset_r, char **sender_r); /* Return a mbox-compatible From_-line using given sender and time. The returned string begins with "From ". */ const char *mbox_from_create(const char *sender, time_t timestamp); #endif dovecot-2.2.33.2/src/lib-mail/message-header-encode.h0000644000175000017500000000202613123174404017057 00000000000000#ifndef MESSAGE_HEADER_ENCODE_H #define MESSAGE_HEADER_ENCODE_H /* Encode UTF-8 input into output wherever necessary using either Q or B encoding depending on which takes less space (approximately). Folding whitespace is preserved. Bare [CR]LF will be preserved by adding a TAB after it to make it a valid folding whitespace. */ void message_header_encode(const char *input, string_t *output); void message_header_encode_data(const unsigned char *input, unsigned int len, string_t *output); /* Encode the whole UTF-8 input using "Q" or "B" encoding into output. The output is split into multiple lines if necessary (max 76 chars/line). The first line's length is given as parameter. All the control characters are encoded, including NUL, CR and LF. */ void message_header_encode_q(const unsigned char *input, unsigned int len, string_t *output, unsigned int first_line_len); void message_header_encode_b(const unsigned char *input, unsigned int len, string_t *output, unsigned int first_line_len); #endif dovecot-2.2.33.2/src/lib-mail/message-date.h0000644000175000017500000000063313123174404015313 00000000000000#ifndef MESSAGE_DATE_H #define MESSAGE_DATE_H /* Parses RFC2822 date/time string. timezone_offset is filled with the timezone's difference to UTC in minutes. */ bool message_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r, int *timezone_offset_r); /* Create RFC2822 date/time string from given time in local timezone. */ const char *message_date_create(time_t timestamp); #endif dovecot-2.2.33.2/src/lib-mail/mail-types.h0000644000175000017500000000067313123174404015044 00000000000000#ifndef MAIL_TYPES_H #define MAIL_TYPES_H enum mail_flags { MAIL_ANSWERED = 0x01, MAIL_FLAGGED = 0x02, MAIL_DELETED = 0x04, MAIL_SEEN = 0x08, MAIL_DRAFT = 0x10, MAIL_RECENT = 0x20, MAIL_FLAGS_MASK = 0x3f, MAIL_FLAGS_NONRECENT = (MAIL_FLAGS_MASK ^ MAIL_RECENT) }; enum modify_type { MODIFY_ADD, MODIFY_REMOVE, MODIFY_REPLACE }; ARRAY_DEFINE_TYPE(keywords, const char *); ARRAY_DEFINE_TYPE(keyword_indexes, unsigned int); #endif dovecot-2.2.33.2/src/lib-mail/message-snippet.h0000644000175000017500000000075613123174404016066 00000000000000#ifndef MESSAGE_SNIPPET_H #define MESSAGE_SNIPPET_H /* Generate UTF-8 text snippet from the beginning of the given mail input stream. The stream is expected to start at the MIME part's headers whose snippet is being generated. Returns 0 if ok, -1 if I/O error. Currently only Content-Type: text/ is supported, others will result in an empty string. */ int message_snippet_generate(struct istream *input, unsigned int max_snippet_chars, string_t *snippet); #endif dovecot-2.2.33.2/src/lib-mail/istream-qp-encoder.c0000644000175000017500000000713013165463624016460 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "qp-encoder.h" #include "istream-private.h" #include "istream-qp.h" struct qp_encoder_istream { struct istream_private istream; buffer_t *buf; struct qp_encoder *qp; }; static void i_stream_qp_encoder_close(struct iostream_private *stream, bool close_parent) { struct qp_encoder_istream *bstream = (struct qp_encoder_istream *)stream; if (bstream->qp != NULL) qp_encoder_deinit(&bstream->qp); if (bstream->buf != NULL) buffer_free(&bstream->buf); if (close_parent) i_stream_close(bstream->istream.parent); } static ssize_t i_stream_qp_encoder_read(struct istream_private *stream) { struct qp_encoder_istream *bstream = (struct qp_encoder_istream *)stream; const unsigned char *data; size_t size; int ret; for(;;) { if (stream->skip > 0) { i_assert(stream->skip <= bstream->buf->used); buffer_delete(bstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = bstream->buf->data; i_assert(stream->pos <= bstream->buf->used); if (stream->pos >= bstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already interpolated, return as much of it as we can */ if (bstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (bstream->buf->used <= bstream->istream.max_buffer_size) { new_pos = bstream->buf->used; if (stream->parent->eof) stream->istream.eof = TRUE; } else { new_pos = bstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } /* need to read more input */ ret = i_stream_read_more(stream->parent, &data, &size); if (ret == 0) return ret; if (size == 0 && ret == -1) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } qp_encoder_more(bstream->qp, data, size); i_stream_skip(stream->parent, size); } } static void i_stream_qp_encoder_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct qp_encoder_istream *bstream = (struct qp_encoder_istream *)stream; if (v_offset < stream->istream.v_offset) { /* seeking backwards - go back to beginning and seek forward from there. */ stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; i_stream_seek(stream->parent, 0); qp_encoder_finish(bstream->qp); buffer_set_used_size(bstream->buf, 0); } i_stream_default_seek_nonseekable(stream, v_offset, mark); } struct istream *i_stream_create_qp_encoder(struct istream *input, enum qp_encoder_flag flags) { struct qp_encoder_istream *bstream; bstream = i_new(struct qp_encoder_istream, 1); bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; bstream->buf = buffer_create_dynamic(default_pool, 128); bstream->qp = qp_encoder_init(bstream->buf, ISTREAM_QP_ENCODER_MAX_LINE_LENGTH, flags); bstream->istream.iostream.close = i_stream_qp_encoder_close; bstream->istream.read = i_stream_qp_encoder_read; bstream->istream.seek = i_stream_qp_encoder_seek; bstream->istream.istream.readable_fd = FALSE; bstream->istream.istream.blocking = input->blocking; bstream->istream.istream.seekable = input->seekable; return i_stream_create(&bstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-mail/istream-header-filter.c0000644000175000017500000004665113165463624017151 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "message-parser.h" #include "istream-private.h" #include "istream-header-filter.h" struct header_filter_istream { struct istream_private istream; pool_t pool; struct message_header_parser_ctx *hdr_ctx; const char **headers; unsigned int headers_count; header_filter_callback *callback; void *context; buffer_t *hdr_buf; struct message_size header_size; uoff_t skip_count; uoff_t last_lf_offset; unsigned int cur_line, parsed_lines; ARRAY(unsigned int) match_change_lines; unsigned int header_read:1; unsigned int seen_eoh:1; unsigned int header_parsed:1; unsigned int headers_edited:1; unsigned int exclude:1; unsigned int crlf:1; unsigned int crlf_preserve:1; unsigned int hide_body:1; unsigned int add_missing_eoh:1; unsigned int end_body_with_lf:1; unsigned int last_lf_added:1; unsigned int last_orig_crlf:1; unsigned int last_added_newline:1; unsigned int eoh_not_matched:1; unsigned int callbacks_called:1; unsigned int prev_matched:1; }; header_filter_callback *null_header_filter_callback = NULL; static ssize_t i_stream_header_filter_read(struct istream_private *stream); static void i_stream_header_filter_destroy(struct iostream_private *stream) { struct header_filter_istream *mstream = (struct header_filter_istream *)stream; if (mstream->hdr_ctx != NULL) message_parse_header_deinit(&mstream->hdr_ctx); i_stream_unref(&mstream->istream.parent); if (array_is_created(&mstream->match_change_lines)) array_free(&mstream->match_change_lines); pool_unref(&mstream->pool); } static ssize_t read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size) { const unsigned char *data; size_t pos; ssize_t ret; if (mstream->hide_body) { mstream->istream.istream.eof = TRUE; return -1; } data = i_stream_get_data(mstream->istream.parent, &pos); if (pos <= body_highwater_size) { i_assert(pos == body_highwater_size || (mstream->end_body_with_lf && pos+1 == body_highwater_size)); ret = i_stream_read(mstream->istream.parent); mstream->istream.istream.stream_errno = mstream->istream.parent->stream_errno; mstream->istream.istream.eof = mstream->istream.parent->eof; if (ret <= 0) { data = mstream->hdr_buf->data; pos = mstream->hdr_buf->used; i_assert(pos > 0); if (mstream->end_body_with_lf && data[pos-1] != '\n' && ret == -1 && mstream->istream.istream.eof) { /* add missing trailing LF to body */ if (mstream->crlf) buffer_append_c(mstream->hdr_buf, '\r'); buffer_append_c(mstream->hdr_buf, '\n'); mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &mstream->istream.pos); return mstream->hdr_buf->used - pos; } return ret; } data = i_stream_get_data(mstream->istream.parent, &pos); } buffer_append(mstream->hdr_buf, data + body_highwater_size, pos - body_highwater_size); mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos); ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip); i_assert(ret > 0); mstream->istream.pos = pos; return ret; } static int cmp_uint(const unsigned int *i1, const unsigned int *i2) { return *i1 < *i2 ? -1 : (*i1 > *i2 ? 1 : 0); } static bool match_line_changed(struct header_filter_istream *mstream) { if (!array_is_created(&mstream->match_change_lines)) return FALSE; return array_bsearch(&mstream->match_change_lines, &mstream->cur_line, cmp_uint) != NULL; } static void add_eol(struct header_filter_istream *mstream, bool orig_crlf) { if (mstream->crlf || (orig_crlf && mstream->crlf_preserve)) buffer_append(mstream->hdr_buf, "\r\n", 2); else buffer_append_c(mstream->hdr_buf, '\n'); mstream->last_orig_crlf = orig_crlf; mstream->last_added_newline = TRUE; } static ssize_t hdr_stream_update_pos(struct header_filter_istream *mstream) { ssize_t ret; size_t pos; mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos); ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip); i_assert(ret >= 0); mstream->istream.pos = pos; return ret; } static ssize_t read_header(struct header_filter_istream *mstream) { struct message_header_line *hdr; uoff_t highwater_offset; size_t max_buffer_size; ssize_t ret, ret2; int hdr_ret; if (mstream->hdr_ctx == NULL) { mstream->hdr_ctx = message_parse_header_init(mstream->istream.parent, NULL, 0); } /* remove skipped data from hdr_buf */ buffer_copy(mstream->hdr_buf, 0, mstream->hdr_buf, mstream->istream.skip, (size_t)-1); mstream->istream.pos -= mstream->istream.skip; mstream->istream.skip = 0; buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos); if (mstream->header_read) { i_assert(mstream->istream.skip == 0); highwater_offset = mstream->istream.istream.v_offset + mstream->istream.pos; if (highwater_offset >= mstream->header_size.virtual_size) { /* we want to return mixed headers and body */ size_t body_highwater_size = highwater_offset - mstream->header_size.virtual_size; return read_mixed(mstream, body_highwater_size); } } max_buffer_size = i_stream_get_max_buffer_size(&mstream->istream.istream); if (mstream->hdr_buf->used >= max_buffer_size) return -2; while ((hdr_ret = message_parse_header_next(mstream->hdr_ctx, &hdr)) > 0) { bool matched; if (!hdr->continued) mstream->cur_line++; if (hdr->eoh) { mstream->seen_eoh = TRUE; matched = TRUE; if (mstream->header_parsed && !mstream->headers_edited) { if (mstream->eoh_not_matched) matched = !matched; } else if (mstream->callback != NULL) { mstream->callback(mstream, hdr, &matched, mstream->context); mstream->callbacks_called = TRUE; } if (!matched) { mstream->seen_eoh = FALSE; mstream->eoh_not_matched = TRUE; continue; } add_eol(mstream, hdr->crlf_newline); continue; } if (hdr->continued) { /* Header line continued - use only the first line's matched-result. Otherwise multiline headers might end up being only partially picked, which wouldn't be very good. However, allow callbacks to modify the headers in any way they want. */ matched = mstream->prev_matched; } else if (mstream->headers_count == 0) { /* no include/exclude headers - default matching */ matched = FALSE; } else { matched = i_bsearch(hdr->name, mstream->headers, mstream->headers_count, sizeof(*mstream->headers), bsearch_strcasecmp) != NULL; } if (mstream->callback == NULL) { /* nothing gets excluded */ } else if (!mstream->header_parsed || mstream->headers_edited) { /* first time in this line or we have actually modified the header so we always want to call the callbacks */ bool orig_matched = matched; mstream->parsed_lines = mstream->cur_line; mstream->callback(mstream, hdr, &matched, mstream->context); mstream->callbacks_called = TRUE; if (matched != orig_matched && !hdr->continued && !mstream->headers_edited) { if (!array_is_created(&mstream->match_change_lines)) i_array_init(&mstream->match_change_lines, 8); array_append(&mstream->match_change_lines, &mstream->cur_line, 1); } } else if (!hdr->continued) { /* second time in this line. was it excluded by the callback the first time? */ if (match_line_changed(mstream)) matched = !matched; } mstream->prev_matched = matched; if (matched == mstream->exclude) { /* ignore */ } else { if (!hdr->continued) { buffer_append(mstream->hdr_buf, hdr->name, hdr->name_len); buffer_append(mstream->hdr_buf, hdr->middle, hdr->middle_len); } buffer_append(mstream->hdr_buf, hdr->value, hdr->value_len); if (!hdr->no_newline) add_eol(mstream, hdr->crlf_newline); if (mstream->skip_count >= mstream->hdr_buf->used) { /* we need more */ mstream->skip_count -= mstream->hdr_buf->used; buffer_set_used_size(mstream->hdr_buf, 0); } else { if (mstream->skip_count > 0) { mstream->istream.skip = mstream->skip_count; mstream->skip_count = 0; } break; } } if (mstream->hdr_buf->used >= max_buffer_size) break; } if (mstream->hdr_buf->used > 0) { const unsigned char *data = mstream->hdr_buf->data; mstream->last_added_newline = data[mstream->hdr_buf->used-1] == '\n'; } if (hdr_ret < 0) { if (mstream->istream.parent->stream_errno != 0) { mstream->istream.istream.stream_errno = mstream->istream.parent->stream_errno; mstream->istream.istream.eof = mstream->istream.parent->eof; return -1; } if (!mstream->seen_eoh && mstream->add_missing_eoh) { bool matched = TRUE; mstream->seen_eoh = TRUE; if (!mstream->last_added_newline) add_eol(mstream, mstream->last_orig_crlf); if (mstream->header_parsed && !mstream->headers_edited) { if (mstream->eoh_not_matched) matched = !matched; } else if (mstream->callback != NULL) { struct message_header_line fake_eoh_hdr = { .eoh = TRUE, .name = "", }; mstream->callback(mstream, &fake_eoh_hdr, &matched, mstream->context); mstream->callbacks_called = TRUE; } if (!matched) { mstream->seen_eoh = FALSE; } else { add_eol(mstream, mstream->last_orig_crlf); } } } /* don't copy eof here because we're only returning headers here. the body will be returned in separate read() call. */ ret = hdr_stream_update_pos(mstream); if (hdr_ret == 0) { /* need more data to finish parsing headers. we may have some data already available though. */ return ret; } if (hdr == NULL) { /* finished */ message_parse_header_deinit(&mstream->hdr_ctx); mstream->hdr_ctx = NULL; if ((!mstream->header_parsed || mstream->headers_edited || mstream->callbacks_called) && mstream->callback != NULL) { bool matched = FALSE; mstream->callback(mstream, NULL, &matched, mstream->context); /* check if the callback added more headers. this is allowed only if EOH wasn't added yet. */ ret2 = hdr_stream_update_pos(mstream); if (!mstream->seen_eoh) ret += ret2; else { i_assert(ret2 == 0); } } mstream->header_parsed = TRUE; mstream->header_read = TRUE; mstream->callbacks_called = FALSE; mstream->header_size.physical_size = mstream->istream.parent->v_offset; mstream->header_size.virtual_size = mstream->istream.istream.v_offset + mstream->istream.pos; } if (ret == 0) { /* we're at the end of headers. */ i_assert(hdr == NULL); i_assert(mstream->istream.istream.v_offset + mstream->istream.pos == mstream->header_size.virtual_size); return i_stream_header_filter_read(&mstream->istream); } return ret; } static ssize_t handle_end_body_with_lf(struct header_filter_istream *mstream, ssize_t ret) { struct istream_private *stream = &mstream->istream; const unsigned char *data; size_t size, last_offset; bool last_lf; data = i_stream_get_data(stream->parent, &size); last_offset = stream->parent->v_offset + size-1; if (mstream->last_lf_offset == last_offset) last_lf = TRUE; else if (size > 0) last_lf = data[size-1] == '\n'; else last_lf = FALSE; if (ret == -1 && stream->parent->eof && !last_lf) { /* missing LF, need to add it */ i_assert(!mstream->last_lf_added); i_assert(size == 0 || data[size-1] != '\n'); buffer_reset(mstream->hdr_buf); buffer_append(mstream->hdr_buf, data, size); if (mstream->crlf) buffer_append_c(mstream->hdr_buf, '\r'); buffer_append_c(mstream->hdr_buf, '\n'); mstream->last_lf_offset = last_offset; mstream->last_lf_added = TRUE; stream->skip = 0; stream->pos = mstream->hdr_buf->used; stream->buffer = mstream->hdr_buf->data; return mstream->crlf ? 2 : 1; } else { mstream->last_lf_offset = last_lf ? last_offset : (uoff_t)-1; } return ret; } static ssize_t i_stream_header_filter_read(struct istream_private *stream) { struct header_filter_istream *mstream = (struct header_filter_istream *)stream; uoff_t v_offset; ssize_t ret; if (mstream->last_lf_added) { stream->istream.eof = TRUE; return -1; } if (!mstream->header_read || stream->istream.v_offset < mstream->header_size.virtual_size) { ret = read_header(mstream); if (ret != -2 || stream->pos != stream->skip) return ret; } if (mstream->hide_body) { stream->istream.eof = TRUE; return -1; } v_offset = stream->parent_start_offset + stream->istream.v_offset - mstream->header_size.virtual_size + mstream->header_size.physical_size; i_stream_seek(stream->parent, v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); if (mstream->end_body_with_lf) ret = handle_end_body_with_lf(mstream, ret); return ret; } static void i_stream_header_filter_seek_to_header(struct header_filter_istream *mstream, uoff_t v_offset) { i_stream_seek(mstream->istream.parent, mstream->istream.parent_start_offset); mstream->istream.parent_expected_offset = mstream->istream.parent_start_offset; mstream->istream.access_counter = mstream->istream.parent->real_stream->access_counter; if (mstream->hdr_ctx != NULL) message_parse_header_deinit(&mstream->hdr_ctx); mstream->skip_count = v_offset; mstream->cur_line = 0; mstream->prev_matched = FALSE; mstream->header_read = FALSE; mstream->seen_eoh = FALSE; mstream->last_added_newline = TRUE; } static int skip_header(struct header_filter_istream *mstream) { size_t pos; if (mstream->header_read) return 0; if (mstream->istream.access_counter != mstream->istream.parent->real_stream->access_counter) { /* need to re-parse headers */ i_stream_header_filter_seek_to_header(mstream, 0); } while (!mstream->header_read && i_stream_read(&mstream->istream.istream) != -1) { pos = i_stream_get_data_size(&mstream->istream.istream); i_stream_skip(&mstream->istream.istream, pos); } return mstream->istream.istream.stream_errno != 0 ? -1 : 0; } static void stream_reset_to(struct header_filter_istream *mstream, uoff_t v_offset) { mstream->istream.istream.v_offset = v_offset; mstream->istream.skip = mstream->istream.pos = 0; mstream->istream.buffer = NULL; buffer_set_used_size(mstream->hdr_buf, 0); } static void i_stream_header_filter_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct header_filter_istream *mstream = (struct header_filter_istream *)stream; if (stream->istream.v_offset == v_offset) { /* just reset the input buffer */ stream_reset_to(mstream, v_offset); i_stream_seek(mstream->istream.parent, mstream->istream.parent_expected_offset); return; } /* if last_lf_added=TRUE, we're currently at EOF. So reset it only if we're seeking backwards, otherwise we would just add a duplicate */ mstream->last_lf_added = FALSE; if (v_offset == 0) { /* seeking to beginning of headers. */ stream_reset_to(mstream, 0); i_stream_header_filter_seek_to_header(mstream, 0); return; } /* if we haven't parsed the whole header yet, we don't know if we want to seek inside header or body. so make sure we've parsed the header. */ if (skip_header(mstream) < 0) return; stream_reset_to(mstream, v_offset); if (v_offset < mstream->header_size.virtual_size) { /* seek into headers. we'll have to re-parse them, use skip_count to set the wanted position */ i_stream_header_filter_seek_to_header(mstream, v_offset); } else { /* body */ v_offset += mstream->header_size.physical_size - mstream->header_size.virtual_size; i_stream_seek(stream->parent, stream->parent_start_offset + v_offset); } } static void ATTR_NORETURN i_stream_header_filter_sync(struct istream_private *stream ATTR_UNUSED) { i_panic("istream-header-filter sync() not implemented"); } static int i_stream_header_filter_stat(struct istream_private *stream, bool exact) { struct header_filter_istream *mstream = (struct header_filter_istream *)stream; const struct stat *st; uoff_t old_offset; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; if (stream->statbuf.st_size == -1 || !exact) return 0; /* fix the filtered header size */ old_offset = stream->istream.v_offset; if (skip_header(mstream) < 0) return -1; if (mstream->hide_body) { /* no body */ stream->statbuf.st_size = mstream->header_size.physical_size; } else if (!mstream->end_body_with_lf) { /* no last-LF */ } else if (mstream->last_lf_added) { /* yes, we have added LF */ stream->statbuf.st_size += mstream->crlf ? 2 : 1; } else if (mstream->last_lf_offset != (uoff_t)-1) { /* no, we didn't need to add LF */ } else { /* check if we need to add LF */ i_stream_seek(stream->parent, st->st_size - 1); (void)i_stream_read(stream->parent); if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } i_assert(stream->parent->eof); ssize_t ret = handle_end_body_with_lf(mstream, -1); if (ret > 0) stream->statbuf.st_size += ret; } stream->statbuf.st_size -= (off_t)mstream->header_size.physical_size - (off_t)mstream->header_size.virtual_size; i_stream_seek(&stream->istream, old_offset); return 0; } #undef i_stream_create_header_filter struct istream * i_stream_create_header_filter(struct istream *input, enum header_filter_flags flags, const char *const *headers, unsigned int headers_count, header_filter_callback *callback, void *context) { struct header_filter_istream *mstream; unsigned int i, j; int ret; i_assert((flags & (HEADER_FILTER_INCLUDE|HEADER_FILTER_EXCLUDE)) != 0); mstream = i_new(struct header_filter_istream, 1); mstream->pool = pool_alloconly_create(MEMPOOL_GROWING "header filter stream", 4096); mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; mstream->headers = headers_count == 0 ? NULL : p_new(mstream->pool, const char *, headers_count); for (i = j = 0; i < headers_count; i++) { ret = j == 0 ? -1 : strcasecmp(mstream->headers[j-1], headers[i]); if (ret == 0) { /* drop duplicate */ continue; } i_assert(ret < 0); mstream->headers[j++] = p_strdup(mstream->pool, headers[i]); } mstream->headers_count = j; mstream->hdr_buf = buffer_create_dynamic(mstream->pool, 1024); mstream->callback = callback; mstream->context = context; mstream->exclude = (flags & HEADER_FILTER_EXCLUDE) != 0; if ((flags & HEADER_FILTER_CRLF_PRESERVE) != 0) mstream->crlf_preserve = TRUE; else if ((flags & HEADER_FILTER_NO_CR) != 0) mstream->crlf = FALSE; else mstream->crlf = TRUE; mstream->hide_body = (flags & HEADER_FILTER_HIDE_BODY) != 0; mstream->add_missing_eoh = (flags & HEADER_FILTER_ADD_MISSING_EOH) != 0; mstream->end_body_with_lf = (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0; mstream->last_lf_offset = (uoff_t)-1; mstream->last_added_newline = TRUE; mstream->istream.iostream.destroy = i_stream_header_filter_destroy; mstream->istream.read = i_stream_header_filter_read; mstream->istream.seek = i_stream_header_filter_seek; mstream->istream.sync = i_stream_header_filter_sync; mstream->istream.stat = i_stream_header_filter_stat; mstream->istream.istream.readable_fd = FALSE; mstream->istream.istream.blocking = input->blocking; mstream->istream.istream.seekable = input->seekable; return i_stream_create(&mstream->istream, input, -1); } void i_stream_header_filter_add(struct header_filter_istream *input, const void *data, size_t size) { buffer_append(input->hdr_buf, data, size); input->headers_edited = TRUE; } dovecot-2.2.33.2/src/lib-mail/qp-decoder.c0000644000175000017500000001627613123174404015004 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "qp-decoder.h" /* quoted-printable lines can be max 76 characters. if we've seen more than that much whitespace, it means there really shouldn't be anything else left in the line except trailing whitespace. */ #define QP_MAX_WHITESPACE_LEN 76 #define QP_IS_TRAILING_WHITESPACE(c) \ ((c) == ' ' || (c) == '\t') enum qp_state { STATE_TEXT = 0, STATE_WHITESPACE, STATE_EQUALS, STATE_EQUALS_WHITESPACE, STATE_HEX2, STATE_CR, STATE_SOFTCR }; struct qp_decoder { buffer_t *dest; buffer_t *whitespace; enum qp_state state; char hexchar; }; struct qp_decoder *qp_decoder_init(buffer_t *dest) { struct qp_decoder *qp; qp = i_new(struct qp_decoder, 1); qp->dest = dest; qp->whitespace = buffer_create_dynamic(default_pool, 80); return qp; } void qp_decoder_deinit(struct qp_decoder **_qp) { struct qp_decoder *qp = *_qp; buffer_free(&qp->whitespace); i_free(qp); } static size_t qp_decoder_more_text(struct qp_decoder *qp, const unsigned char *src, size_t src_size) { size_t i, start = 0, ret = src_size; for (i = 0; i < src_size; i++) { if (src[i] > '=') { /* fast path */ continue; } switch (src[i]) { case '=': qp->state = STATE_EQUALS; break; case '\r': qp->state = STATE_CR; break; case '\n': /* LF without preceding CR */ buffer_append(qp->dest, src+start, i-start); buffer_append(qp->dest, "\r\n", 2); start = i+1; continue; case ' ': case '\t': i_assert(qp->whitespace->used == 0); qp->state = STATE_WHITESPACE; buffer_append_c(qp->whitespace, src[i]); break; default: continue; } ret = i+1; break; } buffer_append(qp->dest, src+start, i-start); return ret; } static void qp_decoder_invalid(struct qp_decoder *qp, const char **error_r) { switch (qp->state) { case STATE_EQUALS: buffer_append_c(qp->dest, '='); *error_r = "'=' not followed by two hex digits"; break; case STATE_HEX2: buffer_append_c(qp->dest, '='); buffer_append_c(qp->dest, qp->hexchar); *error_r = "'=' not followed by a hex digit"; break; case STATE_EQUALS_WHITESPACE: buffer_append_c(qp->dest, '='); buffer_append_buf(qp->dest, qp->whitespace, 0, (size_t)-1); buffer_set_used_size(qp->whitespace, 0); *error_r = "'=' not followed by newline"; break; case STATE_CR: buffer_append_buf(qp->dest, qp->whitespace, 0, (size_t)-1); buffer_set_used_size(qp->whitespace, 0); buffer_append_c(qp->dest, '\r'); *error_r = "CR not followed by LF"; break; case STATE_SOFTCR: buffer_append_c(qp->dest, '='); buffer_append_buf(qp->dest, qp->whitespace, 0, (size_t)-1); buffer_set_used_size(qp->whitespace, 0); buffer_append_c(qp->dest, '\r'); *error_r = "CR not followed by LF"; break; case STATE_TEXT: case STATE_WHITESPACE: i_unreached(); } qp->state = STATE_TEXT; i_assert(*error_r != NULL); } int qp_decoder_more(struct qp_decoder *qp, const unsigned char *src, size_t src_size, size_t *invalid_src_pos_r, const char **error_r) { const char *error; size_t i; *invalid_src_pos_r = (size_t)-1; *error_r = NULL; for (i = 0; i < src_size; ) { switch (qp->state) { case STATE_TEXT: i += qp_decoder_more_text(qp, src+i, src_size-i); /* don't increment i any more than we already did, so continue instead of break */ continue; case STATE_WHITESPACE: if (QP_IS_TRAILING_WHITESPACE(src[i])) { /* more whitespace */ if (qp->whitespace->used <= QP_MAX_WHITESPACE_LEN) buffer_append_c(qp->whitespace, src[i]); } else if (src[i] == '\r') { qp->state = STATE_CR; } else if (src[i] == '\n') { /* drop the trailing whitespace */ buffer_append(qp->dest, "\r\n", 2); buffer_set_used_size(qp->whitespace, 0); } else { /* this wasn't trailing whitespace. put it back. */ buffer_append_buf(qp->dest, qp->whitespace, 0, (size_t)-1); if (qp->whitespace->used > QP_MAX_WHITESPACE_LEN) { /* we already truncated some of the whitespace away, because the line is too long */ if (*invalid_src_pos_r == (size_t)-1) { *invalid_src_pos_r = i; *error_r = "Too much whitespace"; } } buffer_set_used_size(qp->whitespace, 0); qp->state = STATE_TEXT; continue; /* don't increment i */ } break; case STATE_EQUALS: if ((src[i] >= '0' && src[i] <= '9') || (src[i] >= 'A' && src[i] <= 'F') || /* lowercase hex isn't strictly valid, but allow */ (src[i] >= 'a' && src[i] <= 'f')) { qp->hexchar = src[i]; qp->state = STATE_HEX2; } else if (QP_IS_TRAILING_WHITESPACE(src[i])) { i_assert(qp->whitespace->used == 0); buffer_append_c(qp->whitespace, src[i]); qp->state = STATE_EQUALS_WHITESPACE; } else if (src[i] == '\r') qp->state = STATE_SOFTCR; else if (src[i] == '\n') { qp->state = STATE_TEXT; } else { /* invalid input */ qp_decoder_invalid(qp, &error); if (*invalid_src_pos_r == (size_t)-1) { *invalid_src_pos_r = i; *error_r = error; } continue; /* don't increment i */ } break; case STATE_HEX2: if ((src[i] >= '0' && src[i] <= '9') || (src[i] >= 'A' && src[i] <= 'F') || (src[i] >= 'a' && src[i] <= 'f')) { char data[3]; data[0] = qp->hexchar; data[1] = src[i]; data[2] = '\0'; if (hex_to_binary(data, qp->dest) < 0) i_unreached(); qp->state = STATE_TEXT; } else { /* invalid input */ qp_decoder_invalid(qp, &error); if (*invalid_src_pos_r == (size_t)-1) { *invalid_src_pos_r = i; *error_r = error; } continue; /* don't increment i */ } break; case STATE_EQUALS_WHITESPACE: if (QP_IS_TRAILING_WHITESPACE(src[i])) { if (qp->whitespace->used <= QP_MAX_WHITESPACE_LEN) buffer_append_c(qp->whitespace, src[i]); else { /* if this isn't going to get truncated anyway, it's going to be an error */ } } else if (src[i] == '\r') qp->state = STATE_SOFTCR; else if (src[i] == '\n') { buffer_set_used_size(qp->whitespace, 0); qp->state = STATE_TEXT; } else { /* = not followed by [CR]LF is invalid. */ qp_decoder_invalid(qp, &error); if (*invalid_src_pos_r == (size_t)-1) { *invalid_src_pos_r = i; *error_r = error; } continue; /* don't increment i */ } break; case STATE_CR: case STATE_SOFTCR: if (src[i] == '\n') { buffer_set_used_size(qp->whitespace, 0); if (qp->state != STATE_SOFTCR) buffer_append(qp->dest, "\r\n", 2); qp->state = STATE_TEXT; } else { qp_decoder_invalid(qp, &error); if (*invalid_src_pos_r == (size_t)-1) { *invalid_src_pos_r = i; *error_r = error; } continue; /* don't increment i */ } break; } i++; } i_assert((*invalid_src_pos_r == (size_t)-1) == (*error_r == NULL)); return *invalid_src_pos_r == (size_t)-1 ? 0 : -1; } int qp_decoder_finish(struct qp_decoder *qp, const char **error_r) { int ret; if (qp->state == STATE_TEXT || qp->state == STATE_WHITESPACE) { ret = 0; *error_r = NULL; } else { qp_decoder_invalid(qp, error_r); ret = -1; } qp->state = STATE_TEXT; buffer_set_used_size(qp->whitespace, 0); return ret; } dovecot-2.2.33.2/src/lib-mail/istream-binary-converter.c0000644000175000017500000002260113165463624017714 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "istream-private.h" #include "message-parser.h" #include "istream-binary-converter.h" #define BASE64_BLOCK_INPUT_SIZE 3 #define BASE64_BLOCK_SIZE 4 #define BASE64_BLOCKS_PER_LINE (76/BASE64_BLOCK_SIZE) #define MAX_HDR_BUFFER_SIZE (1024*32) struct binary_converter_istream { struct istream_private istream; pool_t pool; struct message_parser_ctx *parser; struct message_part *convert_part; char base64_delayed[BASE64_BLOCK_INPUT_SIZE-1]; unsigned int base64_delayed_len; unsigned int base64_block_pos; buffer_t *hdr_buf; size_t cte_header_len; unsigned int content_type_seen:1; }; static void stream_add_data(struct binary_converter_istream *bstream, const void *data, size_t size); static bool part_can_convert(const struct message_part *part) { /* some MUAs use "c-t-e: binary" for multiparts. we don't want to convert them. */ return (part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0; } static void stream_finish_convert_decision(struct binary_converter_istream *bstream) { buffer_t *buf = bstream->hdr_buf; const unsigned char *data; bstream->hdr_buf = NULL; if (!part_can_convert(bstream->convert_part)) { bstream->convert_part = NULL; stream_add_data(bstream, buf->data, buf->used); } else { stream_add_data(bstream, "Content-Transfer-Encoding: base64\r\n", 35); data = CONST_PTR_OFFSET(buf->data, bstream->cte_header_len); stream_add_data(bstream, data, buf->used - bstream->cte_header_len); } buffer_free(&buf); } static void stream_add_data(struct binary_converter_istream *bstream, const void *data, size_t size) { if (size == 0) return; if (bstream->hdr_buf != NULL) { if (bstream->hdr_buf->used + size <= MAX_HDR_BUFFER_SIZE) { buffer_append(bstream->hdr_buf, data, size); return; } /* buffer is getting too large. just finish the decision. */ stream_finish_convert_decision(bstream); } memcpy(i_stream_alloc(&bstream->istream, size), data, size); bstream->istream.pos += size; } static void stream_encode_base64(struct binary_converter_istream *bstream, const void *_data, size_t size) { struct istream_private *stream = &bstream->istream; const unsigned char *data = _data; buffer_t buf; void *dest; size_t encode_size, max_encoded_size; unsigned char base64_block[BASE64_BLOCK_INPUT_SIZE]; unsigned int base64_block_len, missing_len, encode_blocks; if (bstream->base64_delayed_len > 0) { if (bstream->base64_delayed_len == 1 && size == 1) { bstream->base64_delayed[1] = data[0]; bstream->base64_delayed_len++; return; } memcpy(base64_block, bstream->base64_delayed, bstream->base64_delayed_len); base64_block_len = bstream->base64_delayed_len; if (size == 0) { /* finish base64 */ } else { missing_len = BASE64_BLOCK_INPUT_SIZE - base64_block_len; i_assert(size >= missing_len); memcpy(base64_block + base64_block_len, data, missing_len); data += missing_len; size -= missing_len; base64_block_len = BASE64_BLOCK_INPUT_SIZE; } if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) { memcpy(i_stream_alloc(stream, 2), "\r\n", 2); stream->pos += 2; bstream->base64_block_pos = 0; } dest = i_stream_alloc(stream, BASE64_BLOCK_SIZE); buffer_create_from_data(&buf, dest, BASE64_BLOCK_SIZE); base64_encode(base64_block, base64_block_len, &buf); stream->pos += buf.used; bstream->base64_block_pos++; bstream->base64_delayed_len = 0; } while (size >= BASE64_BLOCK_INPUT_SIZE) { if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) { memcpy(i_stream_alloc(stream, 2), "\r\n", 2); stream->pos += 2; bstream->base64_block_pos = 0; } /* try to encode one full line of base64 blocks */ encode_size = I_MIN(size, BASE64_BLOCKS_PER_LINE*BASE64_BLOCK_SIZE); if (encode_size % BASE64_BLOCK_INPUT_SIZE != 0) encode_size -= encode_size % BASE64_BLOCK_INPUT_SIZE; encode_blocks = encode_size/BASE64_BLOCK_INPUT_SIZE; if (bstream->base64_block_pos + encode_blocks > BASE64_BLOCKS_PER_LINE) { encode_blocks = BASE64_BLOCKS_PER_LINE - bstream->base64_block_pos; encode_size = encode_blocks * BASE64_BLOCK_INPUT_SIZE; } max_encoded_size = MAX_BASE64_ENCODED_SIZE(encode_size); dest = i_stream_alloc(stream, max_encoded_size); buffer_create_from_data(&buf, dest, max_encoded_size); base64_encode(data, encode_size, &buf); stream->pos += buf.used; bstream->base64_block_pos += encode_blocks; data += encode_size; size -= encode_size; } if (size > 0) { /* encode these when more data is available */ i_assert(size < BASE64_BLOCK_INPUT_SIZE); memcpy(bstream->base64_delayed, data, size); bstream->base64_delayed_len = size; } } static void stream_add_hdr(struct binary_converter_istream *bstream, const struct message_header_line *hdr) { if (!hdr->continued) { stream_add_data(bstream, hdr->name, hdr->name_len); stream_add_data(bstream, hdr->middle, hdr->middle_len); } stream_add_data(bstream, hdr->value, hdr->value_len); if (!hdr->no_newline) stream_add_data(bstream, "\r\n", 2); } static ssize_t i_stream_binary_converter_read(struct istream_private *stream) { /* @UNSAFE */ struct binary_converter_istream *bstream = (struct binary_converter_istream *)stream; struct message_block block; size_t old_size, new_size; if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) return -2; old_size = stream->pos - stream->skip; switch (message_parser_parse_next_block(bstream->parser, &block)) { case -1: /* done / error */ if (bstream->convert_part != NULL && bstream->base64_delayed_len > 0) { /* flush any pending base64 output */ stream_encode_base64(bstream, "", 0); new_size = stream->pos - stream->skip; i_assert(old_size != new_size); return new_size - old_size; } stream->istream.eof = TRUE; stream->istream.stream_errno = stream->parent->stream_errno; return -1; case 0: /* need more data */ return 0; default: break; } if (block.part != bstream->convert_part && bstream->convert_part != NULL) { /* end of base64 encoded part */ stream_encode_base64(bstream, "", 0); } if (block.hdr != NULL) { /* parsing a header */ if (strcasecmp(block.hdr->name, "Content-Type") == 0) bstream->content_type_seen = TRUE; if (strcasecmp(block.hdr->name, "Content-Transfer-Encoding") == 0 && !block.hdr->continued && !block.hdr->continues && block.hdr->value_len == 6 && i_memcasecmp(block.hdr->value, "binary", 6) == 0 && part_can_convert(block.part) && bstream->convert_part != block.part) { /* looks like we want to convert this body part to base64, but if we haven't seen Content-Type yet delay the decision until we've read the rest of the header */ i_assert(block.part != NULL); bstream->convert_part = block.part; bstream->base64_block_pos = 0; if (!bstream->content_type_seen) { i_assert(bstream->hdr_buf == NULL); bstream->hdr_buf = buffer_create_dynamic(default_pool, 512); stream_add_hdr(bstream, block.hdr); bstream->cte_header_len = bstream->hdr_buf->used; } else { stream_add_data(bstream, "Content-Transfer-Encoding: base64\r\n", 35); } } else if (block.hdr->eoh && bstream->hdr_buf != NULL) { /* finish the decision about decoding */ stream_finish_convert_decision(bstream); stream_add_data(bstream, "\r\n", 2); } else { stream_add_hdr(bstream, block.hdr); } } else if (block.size == 0) { /* end of header */ if (bstream->hdr_buf != NULL) { /* message has no body */ bstream->convert_part = NULL; stream_add_data(bstream, bstream->hdr_buf->data, bstream->hdr_buf->used); buffer_free(&bstream->hdr_buf); } bstream->content_type_seen = FALSE; } else if (block.part == bstream->convert_part) { /* convert body part to base64 */ stream_encode_base64(bstream, block.data, block.size); } else { stream_add_data(bstream, block.data, block.size); } new_size = stream->pos - stream->skip; if (new_size == old_size) return i_stream_binary_converter_read(stream); return new_size - old_size; } static void i_stream_binary_converter_close(struct iostream_private *stream, bool close_parent) { struct binary_converter_istream *bstream = (struct binary_converter_istream *)stream; struct message_part *parts; if (bstream->parser != NULL) { if (message_parser_deinit(&bstream->parser, &parts) < 0) i_unreached(); /* we didn't use preparsed message_parts */ } if (bstream->pool != NULL) pool_unref(&bstream->pool); if (close_parent) i_stream_close(bstream->istream.parent); } struct istream *i_stream_create_binary_converter(struct istream *input) { struct binary_converter_istream *bstream; bstream = i_new(struct binary_converter_istream, 1); bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; bstream->istream.read = i_stream_binary_converter_read; bstream->istream.iostream.close = i_stream_binary_converter_close; bstream->istream.istream.readable_fd = FALSE; bstream->istream.istream.blocking = input->blocking; bstream->istream.istream.seekable = FALSE; bstream->pool = pool_alloconly_create("istream binary converter", 128); bstream->parser = message_parser_init(bstream->pool, input, 0, MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS | MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES); return i_stream_create(&bstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-mail/message-part-data.c0000644000175000017500000002752213123174404016254 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "array.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "message-address.h" #include "message-header-parser.h" #include "message-part-data.h" const char *message_part_envelope_headers[] = { "Date", "Subject", "From", "Sender", "Reply-To", "To", "Cc", "Bcc", "In-Reply-To", "Message-ID", NULL }; /* * */ bool message_part_data_is_plain_7bit(const struct message_part *part) { const struct message_part_data *data = part->data; i_assert(part->parent == NULL); /* if content-type is text/xxx we don't have to check any multipart stuff */ if ((part->flags & MESSAGE_PART_FLAG_TEXT) == 0) return FALSE; if (part->next != NULL || part->children != NULL) return FALSE; /* shouldn't happen normally.. */ /* must be text/plain */ if (data->content_subtype != NULL && strcasecmp(data->content_subtype, "plain") != 0) return FALSE; /* only allowed parameter is charset=us-ascii, which is also default */ if (data->content_type_params_count == 0) { /* charset defaults to us-ascii */ } else if (data->content_type_params_count != 1 || strcasecmp(data->content_type_params[0].name, "charset") != 0 || strcasecmp(data->content_type_params[0].value, MESSAGE_PART_DEFAULT_CHARSET) != 0) return FALSE; if (data->content_id != NULL || data->content_description != NULL) return FALSE; if (data->content_transfer_encoding != NULL && strcasecmp(data->content_transfer_encoding, "7bit") != 0) return FALSE; /* BODYSTRUCTURE checks: */ if (data->content_md5 != NULL || data->content_disposition != NULL || data->content_language != NULL || data->content_location != NULL) return FALSE; return TRUE; } bool message_part_data_get_filename(const struct message_part *part, const char **filename_r) { const struct message_part_data *data = part->data; const struct message_part_param *params; unsigned int params_count, i; params = data->content_disposition_params; params_count = data->content_disposition_params_count; if (data->content_disposition != NULL && strcasecmp(data->content_disposition, "attachment") != 0) { return FALSE; } for (i = 0; i < params_count; i++) { if (strcasecmp(params[i].name, "filename") == 0 && params[i].value != NULL) { *filename_r = params[i].value; return TRUE; } } return FALSE; } /* * Header parsing */ /* Message part envelope */ enum envelope_field { ENVELOPE_FIELD_DATE = 0, ENVELOPE_FIELD_SUBJECT, ENVELOPE_FIELD_FROM, ENVELOPE_FIELD_SENDER, ENVELOPE_FIELD_REPLY_TO, ENVELOPE_FIELD_TO, ENVELOPE_FIELD_CC, ENVELOPE_FIELD_BCC, ENVELOPE_FIELD_IN_REPLY_TO, ENVELOPE_FIELD_MESSAGE_ID, ENVELOPE_FIELD_UNKNOWN }; static enum envelope_field envelope_get_field(const char *name) { switch (*name) { case 'B': case 'b': if (strcasecmp(name, "Bcc") == 0) return ENVELOPE_FIELD_BCC; break; case 'C': case 'c': if (strcasecmp(name, "Cc") == 0) return ENVELOPE_FIELD_CC; break; case 'D': case 'd': if (strcasecmp(name, "Date") == 0) return ENVELOPE_FIELD_DATE; break; case 'F': case 'f': if (strcasecmp(name, "From") == 0) return ENVELOPE_FIELD_FROM; break; case 'I': case 'i': if (strcasecmp(name, "In-reply-to") == 0) return ENVELOPE_FIELD_IN_REPLY_TO; break; case 'M': case 'm': if (strcasecmp(name, "Message-id") == 0) return ENVELOPE_FIELD_MESSAGE_ID; break; case 'R': case 'r': if (strcasecmp(name, "Reply-to") == 0) return ENVELOPE_FIELD_REPLY_TO; break; case 'S': case 's': if (strcasecmp(name, "Subject") == 0) return ENVELOPE_FIELD_SUBJECT; if (strcasecmp(name, "Sender") == 0) return ENVELOPE_FIELD_SENDER; break; case 'T': case 't': if (strcasecmp(name, "To") == 0) return ENVELOPE_FIELD_TO; break; } return ENVELOPE_FIELD_UNKNOWN; } void message_part_envelope_parse_from_header(pool_t pool, struct message_part_envelope **data, struct message_header_line *hdr) { struct message_part_envelope *d; enum envelope_field field; struct message_address **addr_p; const char **str_p; if (*data == NULL) { *data = p_new(pool, struct message_part_envelope, 1); } if (hdr == NULL) return; field = envelope_get_field(hdr->name); if (field == ENVELOPE_FIELD_UNKNOWN) return; if (hdr->continues) { /* wait for full value */ hdr->use_full_value = TRUE; return; } d = *data; addr_p = NULL; str_p = NULL; switch (field) { case ENVELOPE_FIELD_DATE: str_p = &d->date; break; case ENVELOPE_FIELD_SUBJECT: str_p = &d->subject; break; case ENVELOPE_FIELD_MESSAGE_ID: str_p = &d->message_id; break; case ENVELOPE_FIELD_IN_REPLY_TO: str_p = &d->in_reply_to; break; case ENVELOPE_FIELD_CC: addr_p = &d->cc; break; case ENVELOPE_FIELD_BCC: addr_p = &d->bcc; break; case ENVELOPE_FIELD_FROM: addr_p = &d->from; break; case ENVELOPE_FIELD_SENDER: addr_p = &d->sender; break; case ENVELOPE_FIELD_TO: addr_p = &d->to; break; case ENVELOPE_FIELD_REPLY_TO: addr_p = &d->reply_to; break; case ENVELOPE_FIELD_UNKNOWN: i_unreached(); } if (addr_p != NULL) { *addr_p = message_address_parse(pool, hdr->full_value, hdr->full_value_len, UINT_MAX, TRUE); } else if (str_p != NULL) { *str_p = p_strndup(pool, hdr->full_value, hdr->full_value_len); } } /* Message part data */ static void parse_mime_parameters(struct rfc822_parser_context *parser, pool_t pool, const struct message_part_param **params_r, unsigned int *params_count_r) { const char *const *results; struct message_part_param *params; unsigned int params_count, i; rfc2231_parse(parser, &results); params_count = str_array_length(results); i_assert((params_count % 2) == 0); params_count /= 2; if (params_count > 0) { params = p_new(pool, struct message_part_param, params_count); for (i = 0; i < params_count; i++) { params[i].name = p_strdup(pool, results[i*2+0]); params[i].value = p_strdup(pool, results[i*2+1]); } *params_r = params; } *params_count_r = params_count; } static void parse_content_type(struct message_part_data *data, pool_t pool, struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *str; const char *value; unsigned int i; int ret; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); ret = rfc822_parse_content_type(&parser, str); /* Save content type and subtype */ value = str_c(str); for (i = 0; value[i] != '\0'; i++) { if (value[i] == '/') { data->content_subtype = p_strdup(pool, value + i+1); break; } } str_truncate(str, i); data->content_type = p_strdup(pool, str_c(str)); if (ret < 0) { /* Content-Type is broken, but we wanted to get it as well as we could. Don't try to read the parameters anymore though. We don't completely ignore a broken Content-Type, because then it would be written as text/plain. This would cause a mismatch with the message_part's MESSAGE_PART_FLAG_TEXT. */ return; } parse_mime_parameters(&parser, pool, &data->content_type_params, &data->content_type_params_count); } static void parse_content_transfer_encoding(struct message_part_data *data, pool_t pool, struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *str; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); if (rfc822_parse_mime_token(&parser, str) >= 0 && rfc822_skip_lwsp(&parser) == 0 && str_len(str) > 0) { data->content_transfer_encoding = p_strdup(pool, str_c(str)); } } static void parse_content_disposition(struct message_part_data *data, pool_t pool, struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *str; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); if (rfc822_parse_mime_token(&parser, str) < 0) return; data->content_disposition = p_strdup(pool, str_c(str)); parse_mime_parameters(&parser, pool, &data->content_disposition_params, &data->content_disposition_params_count); } static void parse_content_language(struct message_part_data *data, pool_t pool, const unsigned char *value, size_t value_len) { struct rfc822_parser_context parser; ARRAY_TYPE(const_string) langs; string_t *str; /* Language-Header = "Content-Language" ":" 1#Language-tag Language-Tag = Primary-tag *( "-" Subtag ) Primary-tag = 1*8ALPHA Subtag = 1*8ALPHA */ rfc822_parser_init(&parser, value, value_len, NULL); t_array_init(&langs, 16); str = t_str_new(128); rfc822_skip_lwsp(&parser); while (rfc822_parse_atom(&parser, str) >= 0) { const char *lang = p_strdup(pool, str_c(str)); array_append(&langs, &lang, 1); str_truncate(str, 0); if (parser.data == parser.end || *parser.data != ',') break; parser.data++; rfc822_skip_lwsp(&parser); } if (array_count(&langs) > 0) { array_append_zero(&langs); data->content_language = p_strarray_dup(pool, array_idx(&langs, 0)); } } static void parse_content_header(struct message_part_data *data, pool_t pool, struct message_header_line *hdr) { const char *name = hdr->name + strlen("Content-"); const char *value; if (hdr->continues) { hdr->use_full_value = TRUE; return; } value = t_strndup(hdr->full_value, hdr->full_value_len); switch (*name) { case 'i': case 'I': if (strcasecmp(name, "ID") == 0 && data->content_id == NULL) data->content_id = p_strdup(pool, value); break; case 'm': case 'M': if (strcasecmp(name, "MD5") == 0 && data->content_md5 == NULL) data->content_md5 = p_strdup(pool, value); break; case 't': case 'T': if (strcasecmp(name, "Type") == 0 && data->content_type == NULL) parse_content_type(data, pool, hdr); else if (strcasecmp(name, "Transfer-Encoding") == 0 && data->content_transfer_encoding == NULL) parse_content_transfer_encoding(data, pool, hdr); break; case 'l': case 'L': if (strcasecmp(name, "Language") == 0 && data->content_language == NULL) { parse_content_language(data, pool, hdr->full_value, hdr->full_value_len); } else if (strcasecmp(name, "Location") == 0 && data->content_location == NULL) { data->content_location = p_strdup(pool, value); } break; case 'd': case 'D': if (strcasecmp(name, "Description") == 0 && data->content_description == NULL) data->content_description = p_strdup(pool, value); else if (strcasecmp(name, "Disposition") == 0 && data->content_disposition_params == NULL) parse_content_disposition(data, pool, hdr); break; } } void message_part_data_parse_from_header(pool_t pool, struct message_part *part, struct message_header_line *hdr) { struct message_part_data *part_data; struct message_part_envelope *envelope; bool parent_rfc822; if (hdr == NULL) { if (part->data == NULL) { /* no Content-* headers. add an empty context structure anyway. */ part->data = part_data = p_new(pool, struct message_part_data, 1); } else if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { /* If there was no Mime-Version, forget all the Content-stuff */ part_data = part->data; envelope = part_data->envelope; i_zero(part_data); part_data->envelope = envelope; } return; } if (hdr->eoh) return; parent_rfc822 = part->parent != NULL && (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0; if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0) return; if (part->data == NULL) { /* initialize message part data */ part->data = part_data = p_new(pool, struct message_part_data, 1); } part_data = part->data; if (strncasecmp(hdr->name, "Content-", 8) == 0) { T_BEGIN { parse_content_header(part_data, pool, hdr); } T_END; } if (parent_rfc822) { /* message/rfc822, we need the envelope */ message_part_envelope_parse_from_header(pool, &part_data->envelope, hdr); } } dovecot-2.2.33.2/src/lib-mail/test-istream-header-filter.c0000644000175000017500000004432113165463624020116 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "message-header-parser.h" #include "istream-header-filter.h" #include "test-common.h" struct run_ctx { header_filter_callback *callback; unsigned int callback_call_count; bool null_hdr_seen; bool eoh_seen; bool callback_called; }; static void run_callback(struct header_filter_istream *input, struct message_header_line *hdr, bool *matched, struct run_ctx *ctx) { i_assert(!ctx->null_hdr_seen); ctx->callback_call_count++; if (hdr == NULL) ctx->null_hdr_seen = TRUE; else { i_assert(!ctx->eoh_seen); if (hdr->eoh) ctx->eoh_seen = TRUE; } if (ctx->callback != NULL) ctx->callback(input, hdr, matched, NULL); ctx->callback_called = TRUE; } static inline void test_istream_run_prep(struct run_ctx *run_ctx, header_filter_callback *callback) { i_zero(run_ctx); run_ctx->callback = callback; run_ctx->null_hdr_seen = FALSE; run_ctx->eoh_seen = FALSE; run_ctx->callback_called = FALSE; } static void test_istream_run_check(struct run_ctx *run_ctx, struct istream *filter, const char *output, enum header_filter_flags flags, bool first, size_t *size_r) { const unsigned char *data; const struct stat *st; if (first) test_assert(run_ctx->null_hdr_seen); else test_assert(run_ctx->null_hdr_seen == run_ctx->callback_called); if (first && ((flags & HEADER_FILTER_ADD_MISSING_EOH) != 0)) test_assert(run_ctx->eoh_seen); data = i_stream_get_data(filter, size_r); test_assert(*size_r == strlen(output) && memcmp(data, output, *size_r) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == *size_r); } static void test_istream_run(struct istream *test_istream, unsigned int input_len, const char *output, enum header_filter_flags flags, header_filter_callback *callback) { struct run_ctx run_ctx; struct istream *filter; unsigned int i, orig_callback_call_count; size_t size; test_istream_run_prep(&run_ctx, callback); filter = i_stream_create_header_filter(test_istream, flags, NULL, 0, run_callback, &run_ctx); for (i = 1; i < input_len; i++) { test_istream_set_size(test_istream, i); test_assert(i_stream_read(filter) >= 0); } test_istream_set_size(test_istream, input_len); test_assert(i_stream_read(filter) > 0); test_assert(i_stream_read(filter) == -1); test_istream_run_check(&run_ctx, filter, output, flags, TRUE, &size); orig_callback_call_count = run_ctx.callback_call_count; /* run again to make sure it's still correct the second time */ test_istream_run_prep(&run_ctx, callback); i_stream_skip(filter, size); i_stream_seek(filter, 0); while (i_stream_read(filter) > 0) ; test_istream_run_check(&run_ctx, filter, output, flags, FALSE, &size); test_assert(run_ctx.callback_call_count == 0 || run_ctx.callback_call_count == orig_callback_call_count); i_stream_unref(&filter); } static void ATTR_NULL(3) filter_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched, void *context ATTR_UNUSED) { if (hdr != NULL && (hdr->name_offset == 0 || strcmp(hdr->name, "X-Drop") == 0)) { /* drop 1) first header, 2) X-Drop header */ *matched = TRUE; } } static void test_istream_filter(void) { static const char *exclude_headers[] = { "Subject", "To" }; const char *input = "From: foo\nFrom: abc\nTo: bar\nSubject: plop\nX-Drop: 1\n\nhello world\n"; const char *output = "From: abc\n\nhello world\n"; struct istream *istream, *filter, *filter2; unsigned int i; size_t input_len = strlen(input); size_t output_len = strlen(output); const unsigned char *data; const struct stat *st; size_t size; test_begin("i_stream_create_header_filter: exclude"); istream = test_istream_create(input); filter = i_stream_create_header_filter(istream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, exclude_headers, N_ELEMENTS(exclude_headers), filter_callback, (void *)NULL); filter2 = i_stream_create_header_filter(filter, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, exclude_headers, N_ELEMENTS(exclude_headers), *null_header_filter_callback, (void *)NULL); i_stream_unref(&filter); filter = filter2; for (i = 1; i < input_len; i++) { test_istream_set_size(istream, i); test_assert(i_stream_read(filter) >= 0); } test_istream_set_size(istream, input_len); test_assert(i_stream_read(filter) > 0); test_assert(i_stream_read(filter) == -1); data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == size); i_stream_skip(filter, size); i_stream_seek(filter, 0); while (i_stream_read(filter) > 0) ; data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == size); i_stream_unref(&filter); i_stream_unref(&istream); test_end(); } static void add_random_text(string_t *dest, unsigned int count) { unsigned int i; for (i = 0; i < count; i++) str_append_c(dest, rand() % ('z'-'a'+1) + 'a'); } static void ATTR_NULL(3) filter2_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched, bool *null_hdr_seen) { if (hdr == NULL) *null_hdr_seen = TRUE; else if (strcmp(hdr->name, "To") == 0) *matched = TRUE; } static void test_istream_filter_large_buffer(void) { string_t *input, *output; struct istream *istream, *filter; const struct stat *st; const unsigned char *data; size_t size, prefix_len; const char *p; unsigned int i; bool null_hdr_seen = FALSE; test_begin("i_stream_create_header_filter: large buffer"); input = str_new(default_pool, 1024*128); output = str_new(default_pool, 1024*128); str_append(input, "From: "); add_random_text(input, 1024*31); str_append(input, "\nTo: "); add_random_text(input, 1024*32); str_append(input, "\nSubject: "); add_random_text(input, 1024*34); str_append(input, "\n\nbody\n"); istream = test_istream_create_data(str_data(input), str_len(input)); test_istream_set_max_buffer_size(istream, 8192); filter = i_stream_create_header_filter(istream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, NULL, 0, filter2_callback, &null_hdr_seen); for (i = 0; i < 2; i++) { for (;;) { ssize_t ret = i_stream_read(filter); i_assert(ret != 0); if (ret == -1) break; if (ret == -2) { data = i_stream_get_data(filter, &size); str_append_n(output, data, size); i_stream_skip(filter, size); } } /* callbacks are called only once */ test_assert(null_hdr_seen == (i == 0)); data = i_stream_get_data(filter, &size); test_assert(size <= 8192); str_append_n(output, data, size); p = strstr(str_c(input), "To: "); i_assert(p != NULL); prefix_len = p - str_c(input); test_assert(strncmp(str_c(input), str_c(output), prefix_len) == 0); p = strchr(p, '\n'); i_assert(p != NULL); test_assert(strcmp(p+1, str_c(output) + prefix_len) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == filter->v_offset + size); /* seek back and retry once with caching and different buffer size */ i_stream_seek(filter, 0); str_truncate(output, 0); test_istream_set_max_buffer_size(istream, 4096); null_hdr_seen = FALSE; } str_free(&input); str_free(&output); i_stream_unref(&filter); i_stream_unref(&istream); test_end(); } static void test_istream_filter_large_buffer2(void) { static const char *wanted_headers[] = { "References" }; string_t *input, *output; struct istream *istream, *filter; const struct stat *st; const unsigned char *data; size_t size; unsigned int i; int ret; test_begin("i_stream_create_header_filter: large buffer2"); input = str_new(default_pool, 1024*128); output = str_new(default_pool, 1024*128); str_append(input, "References: "); add_random_text(input, 1024*64); str_append(input, "\r\n\r\n"); istream = test_istream_create_data(str_data(input), str_len(input)); test_istream_set_max_buffer_size(istream, 8192); filter = i_stream_create_header_filter(istream, HEADER_FILTER_INCLUDE | HEADER_FILTER_HIDE_BODY, wanted_headers, N_ELEMENTS(wanted_headers), *null_header_filter_callback, (void *)NULL); for (i = 0; i < 2; i++) { while ((ret = i_stream_read_more(filter, &data, &size)) > 0) { str_append_n(output, data, size); i_stream_skip(filter, size); } test_assert(ret == -1); test_assert(filter->stream_errno == 0); test_assert(strcmp(str_c(input), str_c(output)) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == filter->v_offset + size); /* seek back and retry once with caching and different buffer size */ i_stream_seek(filter, 0); str_truncate(output, 0); test_istream_set_max_buffer_size(istream, 4096); } str_free(&input); str_free(&output); i_stream_unref(&filter); i_stream_unref(&istream); test_end(); } static void filter3_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched ATTR_UNUSED, string_t *dest) { if (hdr != NULL) message_header_line_write(dest, hdr); } static void test_istream_callbacks(void) { string_t *input, *output; const struct stat *st; struct istream *istream, *filter; unsigned int i; test_begin("i_stream_create_header_filter: callbacks"); input = str_new(default_pool, 1024*128); output = str_new(default_pool, 1024*128); str_append(input, "From: first line\n "); add_random_text(input, 1024*31); str_append(input, "\nTo: first line\n\tsecond line\n\t"); add_random_text(input, 1024*32); str_append(input, "\n last line\nSubject: "); add_random_text(input, 1024*34); str_append(input, "\n"); istream = test_istream_create_data(str_data(input), str_len(input)); test_istream_set_max_buffer_size(istream, 8192); filter = i_stream_create_header_filter(istream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, NULL, 0, filter3_callback, output); /* callback should be called exactly once for all the header input */ for (i = 0; i < 2; i++) { while (i_stream_read(filter) != -1) i_stream_skip(filter, i_stream_get_data_size(filter)); } test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == str_len(output)); test_assert(strcmp(str_c(output), str_c(input)) == 0); str_free(&input); str_free(&output); i_stream_unref(&filter); i_stream_unref(&istream); test_end(); } static void ATTR_NULL(3) edit_callback(struct header_filter_istream *input, struct message_header_line *hdr, bool *matched, void *context ATTR_UNUSED) { if (hdr == NULL) return; if (hdr->eoh) { /* add a new header */ const char *new_hdr = "Added: header\n\n"; i_stream_header_filter_add(input, new_hdr, strlen(new_hdr)); *matched = FALSE; } else if (strcasecmp(hdr->name, "To") == 0) { /* modify To header */ const char *new_to = "To: 123\n"; *matched = TRUE; i_stream_header_filter_add(input, new_to, strlen(new_to)); } } static void test_istream_edit(void) { const char *input = "From: foo\nTo: bar\n\nhello world\n"; const char *output = "From: foo\nTo: 123\nAdded: header\n\nhello world\n"; struct istream *istream; test_begin("i_stream_create_header_filter: edit headers"); istream = test_istream_create(input); test_istream_run(istream, strlen(input), output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, edit_callback); i_stream_unref(&istream); test_end(); } static void test_istream_end_body_with_lf(void) { const char *input = "From: foo\n\nhello world"; const char *output = "From: foo\n\nhello world\n"; const struct stat *st; struct istream *istream, *filter; unsigned int i; size_t input_len = strlen(input); size_t output_len = strlen(output); const unsigned char *data; string_t *str = t_str_new(64); size_t size; test_begin("i_stream_create_header_filter: end_body_with_lf"); istream = test_istream_create(input); filter = i_stream_create_header_filter(istream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_END_BODY_WITH_LF, NULL, 0, *null_header_filter_callback, (void *)NULL); for (i = 1; i < input_len; i++) { test_istream_set_size(istream, i); test_assert(i_stream_read(filter) >= 0); } test_istream_set_size(istream, input_len); test_assert(i_stream_read(filter) > 0); test_assert(i_stream_read(filter) > 0); test_assert(i_stream_read(filter) == -1); data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); test_assert(i_stream_stat(filter, TRUE, &st) == 0 && (uoff_t)st->st_size == filter->v_offset + size); i_stream_skip(filter, size); i_stream_seek(filter, 0); for (i = 1; i < input_len; i++) { test_istream_set_size(istream, i); test_assert(i_stream_read(filter) >= 0); data = i_stream_get_data(filter, &size); if (size > 0) str_append_n(str, data, size); i_stream_skip(filter, size); } test_istream_set_size(istream, input_len); test_assert(i_stream_read(filter) == 1); test_assert(i_stream_read(filter) == 1); test_assert(i_stream_read(filter) == -1); data = i_stream_get_data(filter, &size); str_append_n(str, data, size); test_assert(strcmp(str_c(str), output) == 0); i_stream_unref(&filter); i_stream_unref(&istream); test_end(); } static void test_istream_add_missing_eoh(void) { struct { const char *input; const char *output; unsigned int extra; } tests[] = { { "", "\n", 0 }, { "From: foo", "From: foo\n\n", 1 }, { "From: foo\n", "From: foo\n\n", 1 }, { "From: foo\n\n", "From: foo\n\n", 1 }, { "From: foo\n\nbar", "From: foo\n\nbar", 0 }, { "From: foo\r\n", "From: foo\r\n\r\n", 1 }, { "From: foo\r\n\r\n", "From: foo\r\n\r\n", 0 }, { "From: foo\r\n\r\nbar", "From: foo\r\n\r\nbar", 0 } }; struct istream *istream; unsigned int i; test_begin("i_stream_create_header_filter: add missing EOH"); for (i = 0; i < N_ELEMENTS(tests); i++) { istream = test_istream_create(tests[i].input); test_istream_run(istream, strlen(tests[i].input) + tests[i].extra, tests[i].output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_CRLF_PRESERVE | HEADER_FILTER_ADD_MISSING_EOH, *null_header_filter_callback); i_stream_unref(&istream); } test_end(); } static void test_istream_add_missing_eoh_and_edit(void) { const char *input = "From: foo\nTo: bar\n"; const char *output = "From: foo\nTo: 123\nAdded: header\n\n"; struct istream *istream; test_begin("i_stream_create_header_filter: add missing EOH and edit headers"); istream = test_istream_create(input); test_istream_run(istream, strlen(input), output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_ADD_MISSING_EOH | HEADER_FILTER_NO_CR, edit_callback); i_stream_unref(&istream); test_end(); } static void test_istream_hide_body(void) { struct { const char *input; const char *output; int extra; } tests[] = { { "From: foo", "From: foo", 0 }, { "From: foo\n", "From: foo\n", 0 }, { "From: foo\n\n", "From: foo\n\n", 1 }, { "From: foo\n\nbar", "From: foo\n\n", -2 }, { "From: foo\r\n", "From: foo\r\n", 0 }, { "From: foo\r\n\r\n", "From: foo\r\n\r\n", 0 }, { "From: foo\r\n\r\nbar", "From: foo\r\n\r\n", -3 } }; struct istream *istream; unsigned int i; test_begin("i_stream_create_header_filter: hide body"); for (i = 0; i < N_ELEMENTS(tests); i++) { istream = test_istream_create(tests[i].input); test_istream_run(istream, (int)strlen(tests[i].input) + tests[i].extra, tests[i].output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_CRLF_PRESERVE | HEADER_FILTER_HIDE_BODY, *null_header_filter_callback); i_stream_unref(&istream); } test_end(); } static void ATTR_NULL(3) strip_eoh_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched, void *context ATTR_UNUSED) { if (hdr != NULL && hdr->eoh) *matched = FALSE; } static void test_istream_strip_eoh(void) { const char *input = "From: foo\nTo: bar\n\nhello world\n"; const char *output = "From: foo\nTo: bar\nhello world\n"; struct istream *istream; test_begin("i_stream_create_header_filter: strip_eoh"); istream = test_istream_create(input); test_istream_run(istream, strlen(input), output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, strip_eoh_callback); i_stream_unref(&istream); test_end(); } static void ATTR_NULL(3) missing_eoh_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched ATTR_UNUSED, void *context ATTR_UNUSED) { if (hdr == NULL) { const char *new_hdr = "Subject: added\n\n"; i_stream_header_filter_add(input, new_hdr, strlen(new_hdr)); } } static void test_istream_missing_eoh_callback(void) { const char *input = "From: foo\nTo: bar\n"; const char *output = "From: foo\nTo: bar\nSubject: added\n\n"; struct istream *istream; test_begin("i_stream_create_header_filter: add headers when EOH is missing"); istream = test_istream_create(input); test_istream_run(istream, strlen(input) + 1, output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, missing_eoh_callback); i_stream_unref(&istream); test_end(); } static void test_istream_empty_missing_eoh_callback(void) { const char *input = ""; const char *output = "Subject: added\n\n"; struct istream *istream; test_begin("i_stream_create_header_filter: add headers when mail is empty"); istream = test_istream_create(input); test_istream_run(istream, strlen(input)+1, output, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, missing_eoh_callback); i_stream_unref(&istream); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_istream_filter, test_istream_filter_large_buffer, test_istream_filter_large_buffer2, test_istream_callbacks, test_istream_edit, test_istream_add_missing_eoh, test_istream_add_missing_eoh_and_edit, test_istream_end_body_with_lf, test_istream_hide_body, test_istream_strip_eoh, test_istream_missing_eoh_callback, test_istream_empty_missing_eoh_callback, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-message-header-parser.c0000644000175000017500000002433613165463624020111 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "message-size.h" #include "message-header-parser.h" #include "test-common.h" #define TEST1_MSG_BODY_LEN 5 static const char *test1_msg = "h1: v1\n" "h2:\n" " v2\r\n" "h3: \r\n" "\tv3\n" "\tw3\r\n" "\n" " body"; static void test_message_header_parser_one(struct message_header_parser_ctx *parser, enum message_header_parser_flags hdr_flags) { struct message_header_line *hdr; bool use_full_value; use_full_value = hdr_flags != 0; test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 0); if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP) == 0) test_assert(hdr->full_value_offset == 4); else test_assert(hdr->full_value_offset == 5); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h1") == 0); if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP) == 0) { test_assert(hdr->middle_len == 2 && memcmp(hdr->middle, ": ", 2) == 0); test_assert(hdr->value_len == 3 && memcmp(hdr->value, " v1", 3) == 0); } else { test_assert(hdr->middle_len == 3 && memcmp(hdr->middle, ": ", 3) == 0); test_assert(hdr->value_len == 2 && memcmp(hdr->value, "v1", 2) == 0); } test_assert(!hdr->continues && !hdr->continued && !hdr->eoh && !hdr->no_newline && !hdr->crlf_newline); test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 8 && hdr->full_value_offset == 11); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h2") == 0); test_assert(hdr->middle_len == 1 && memcmp(hdr->middle, ":", 1) == 0); test_assert(hdr->value_len == 0); test_assert(hdr->continues && !hdr->continued && !hdr->eoh && !hdr->no_newline && !hdr->crlf_newline); if (use_full_value) hdr->use_full_value = TRUE; test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 8 && hdr->full_value_offset == 11); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h2") == 0); test_assert(hdr->middle_len == 1 && memcmp(hdr->middle, ":", 1) == 0); test_assert(hdr->value_len == 3 && memcmp(hdr->value, " v2", 3) == 0); test_assert(!hdr->continues && hdr->continued && !hdr->eoh && !hdr->no_newline && hdr->crlf_newline); if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0) { test_assert(hdr->full_value_len == 3 && memcmp(hdr->full_value, " v2", 3) == 0); } else if (use_full_value) { test_assert(hdr->full_value_len == 4 && memcmp(hdr->full_value, "\n v2", 4) == 0); } test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 17 && hdr->full_value_offset == 21); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h3") == 0); test_assert(hdr->middle_len == 2 && memcmp(hdr->middle, ": ", 2) == 0); test_assert(hdr->value_len == 0); test_assert(hdr->continues && !hdr->continued && !hdr->eoh && !hdr->no_newline && hdr->crlf_newline); if (use_full_value) hdr->use_full_value = TRUE; test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 17 && hdr->full_value_offset == 21); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h3") == 0); test_assert(hdr->middle_len == 2 && memcmp(hdr->middle, ": ", 2) == 0); test_assert(hdr->value_len == 3 && memcmp(hdr->value, "\tv3", 3) == 0); test_assert(hdr->continues && hdr->continued && !hdr->eoh && !hdr->no_newline && !hdr->crlf_newline); if (use_full_value) hdr->use_full_value = TRUE; if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0) { test_assert(hdr->full_value_len == 3 && memcmp(hdr->full_value, " v3", 3) == 0); } else if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_DROP_CR) != 0) { test_assert(hdr->full_value_len == 4 && memcmp(hdr->full_value, "\n\tv3", 4) == 0); } else if (use_full_value) { test_assert(hdr->full_value_len == 5 && memcmp(hdr->full_value, "\r\n\tv3", 5) == 0); } test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 17 && hdr->full_value_offset == 21); test_assert(hdr->name_len == 2 && strcmp(hdr->name, "h3") == 0); test_assert(hdr->middle_len == 2 && memcmp(hdr->middle, ": ", 2) == 0); test_assert(hdr->value_len == 3 && memcmp(hdr->value, "\tw3", 3) == 0); test_assert(!hdr->continues && hdr->continued && !hdr->eoh && !hdr->no_newline && hdr->crlf_newline); if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0) { test_assert(hdr->full_value_len == 6 && memcmp(hdr->full_value, " v3 w3", 6) == 0); } else if ((hdr_flags & MESSAGE_HEADER_PARSER_FLAG_DROP_CR) != 0) { test_assert(hdr->full_value_len == 8 && memcmp(hdr->full_value, "\n\tv3\n\tw3", 8) == 0); } else if (use_full_value) { test_assert(hdr->full_value_len == 9 && memcmp(hdr->full_value, "\r\n\tv3\n\tw3", 9) == 0); } test_assert(message_parse_header_next(parser, &hdr) > 0); test_assert(hdr->name_offset == 32 && hdr->full_value_offset == 32); test_assert(hdr->name_len == 0 && hdr->middle_len == 0 && hdr->value_len == 0); test_assert(!hdr->continues && !hdr->continued && hdr->eoh && !hdr->no_newline && !hdr->crlf_newline); test_assert(message_parse_header_next(parser, &hdr) < 0); } static void test_message_header_parser(void) { static enum message_header_parser_flags max_hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR | MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; enum message_header_parser_flags hdr_flags; struct message_header_parser_ctx *parser; struct message_size hdr_size; struct istream *input; test_begin("message header parser"); input = test_istream_create(test1_msg); for (hdr_flags = 0; hdr_flags <= max_hdr_flags; hdr_flags++) { i_stream_seek(input, 0); parser = message_parse_header_init(input, &hdr_size, hdr_flags); test_message_header_parser_one(parser, hdr_flags); message_parse_header_deinit(&parser); } test_assert(hdr_size.physical_size == strlen(test1_msg)-TEST1_MSG_BODY_LEN); test_assert(hdr_size.virtual_size == strlen(test1_msg) - TEST1_MSG_BODY_LEN + 4); i_stream_unref(&input); test_end(); } static void hdr_write(string_t *str, struct message_header_line *hdr) { if (!hdr->continued) { str_append(str, hdr->name); if (hdr->middle_len > 0) str_append_n(str, hdr->middle, hdr->middle_len); } str_append_n(str, hdr->value, hdr->value_len); if (!hdr->no_newline) { if (hdr->crlf_newline) str_append_c(str, '\r'); str_append_c(str, '\n'); } } static void test_message_header_parser_partial(void) { struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct istream *input; unsigned int i, max = (strlen(test1_msg)-TEST1_MSG_BODY_LEN)*2; string_t *str; int ret; test_begin("message header parser partial"); input = test_istream_create(test1_msg); test_istream_set_allow_eof(input, FALSE); str = t_str_new(max); parser = message_parse_header_init(input, NULL, 0); for (i = 0; i <= max; i++) { test_istream_set_size(input, i/2); while ((ret = message_parse_header_next(parser, &hdr)) > 0) hdr_write(str, hdr); test_assert((ret == 0 && i < max) || (ret < 0 && i == max)); } message_parse_header_deinit(&parser); str_append(str, " body"); test_assert(strcmp(str_c(str), test1_msg) == 0); i_stream_unref(&input); test_end(); } static void test_message_header_parser_long_lines_str(const char *str, unsigned int buffer_size, struct message_size *size_r) { struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct istream *input; unsigned int i; size_t len = strlen(str); input = test_istream_create(str); test_istream_set_max_buffer_size(input, buffer_size); parser = message_parse_header_init(input, size_r, 0); for (i = 1; i <= len; i++) { test_istream_set_size(input, i); while (message_parse_header_next(parser, &hdr) > 0) ; } message_parse_header_deinit(&parser); i_stream_unref(&input); } static void test_message_header_parser_long_lines(void) { static const char *lf_str = "1234567890: 345\n\n"; static const char *crlf_str = "1234567890: 345\r\n\r\n"; struct message_size hdr_size; size_t i, len; test_begin("message header parser long lines"); len = strlen(lf_str); for (i = 2; i < len; i++) { test_message_header_parser_long_lines_str(lf_str, i, &hdr_size); test_assert(hdr_size.physical_size == len); test_assert(hdr_size.virtual_size == len + 2); } len = strlen(crlf_str); for (i = 3; i < len; i++) { test_message_header_parser_long_lines_str(crlf_str, i, &hdr_size); test_assert(hdr_size.physical_size == len); test_assert(hdr_size.virtual_size == len); } test_end(); } static void test_message_header_parser_extra_cr_in_eoh(void) { static const char *str = "a:b\n\r\r\n"; struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct istream *input; test_begin("message header parser extra CR in EOH"); input = test_istream_create(str); parser = message_parse_header_init(input, NULL, 0); test_assert(message_parse_header_next(parser, &hdr) > 0 && strcmp(hdr->name, "a") == 0); test_assert(message_parse_header_next(parser, &hdr) > 0 && strcmp(hdr->name, "\r") == 0 && hdr->middle_len == 0 && hdr->value_len == 0 && !hdr->eoh); test_assert(message_parse_header_next(parser, &hdr) < 0); message_parse_header_deinit(&parser); test_assert(input->stream_errno == 0); i_stream_unref(&input); test_end(); } static void test_message_header_parser_no_eoh(void) { static const char *str = "a:b\n"; struct message_header_parser_ctx *parser; struct message_header_line *hdr; struct istream *input; test_begin("message header parser no EOH"); input = test_istream_create(str); parser = message_parse_header_init(input, NULL, 0); test_assert(message_parse_header_next(parser, &hdr) > 0 && strcmp(hdr->name, "a") == 0); test_assert(message_parse_header_next(parser, &hdr) < 0); message_parse_header_deinit(&parser); test_assert(input->stream_errno == 0); i_stream_unref(&input); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_header_parser, test_message_header_parser_partial, test_message_header_parser_long_lines, test_message_header_parser_extra_cr_in_eoh, test_message_header_parser_no_eoh, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/message-part.c0000644000175000017500000000252713123174404015343 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "message-part.h" static const struct message_part * message_part_root(const struct message_part *part) { while (part->parent != NULL) part = part->parent; return part; } static bool message_part_find(const struct message_part *siblings, const struct message_part *part, unsigned int *n) { const struct message_part *p; for (p = siblings; p != NULL; p = p->next) { if (p == part) return TRUE; *n += 1; if (message_part_find(p->children, part, n)) return TRUE; } return FALSE; } unsigned int message_part_to_idx(const struct message_part *part) { const struct message_part *root; unsigned int n = 0; root = message_part_root(part); if (!message_part_find(root, part, &n)) i_unreached(); return n; } static struct message_part * message_sub_part_by_idx(struct message_part *parts, unsigned int idx) { struct message_part *part = parts; for (; part != NULL && idx > 0; part = part->next) { if (part->children_count >= idx) return message_sub_part_by_idx(part->children, idx-1); idx -= part->children_count + 1; } return part; } struct message_part * message_part_by_idx(struct message_part *parts, unsigned int idx) { i_assert(parts->parent == NULL); return message_sub_part_by_idx(parts, idx); } dovecot-2.2.33.2/src/lib-mail/message-header-encode.c0000644000175000017500000001427113165463624017072 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "base64.h" #include "message-header-encode.h" #define MIME_WRAPPER_LEN (strlen("=?utf-8?q?""?=")) #define MIME_MAX_LINE_LEN 76 #define IS_LWSP(c) \ ((c) == ' ' || (c) == '\t' || (c) == '\n') static bool input_idx_need_encoding(const unsigned char *input, unsigned int i, unsigned int len) { switch (input[i]) { case '\r': if (i+1 == len || input[i+1] != '\n') return TRUE; i++; /* fall through - verify the LF as well */ case '\n': if (i+1 == len) { /* trailing LF - we need to drop it */ return TRUE; } if (input[i+1] != '\t' && input[i+1] != ' ') { /* LF not followed by whitespace - we need to add the whitespace */ return TRUE; } break; case '\t': /* TAB doesn't need to be encoded */ break; case '=': /* =? - we need to check backwards a bit to see if there is LWSP (note that we don't want to return TRUE for the LWSP itself yet, so we need to do this backwards check) */ if ((i == 0 || IS_LWSP(input[i-1])) && i+2 <= len && memcmp(input + i, "=?", 2) == 0) return TRUE; break; default: /* 8bit chars */ if ((input[i] & 0x80) != 0) return TRUE; /* control chars */ if (input[i] < 32) return TRUE; break; } return FALSE; } void message_header_encode_q(const unsigned char *input, unsigned int len, string_t *output, unsigned int first_line_len) { unsigned int i, line_len_left; line_len_left = MIME_MAX_LINE_LEN - MIME_WRAPPER_LEN; if (first_line_len >= MIME_MAX_LINE_LEN - MIME_WRAPPER_LEN - 3) { str_append(output, "\n\t"); line_len_left--; } else { line_len_left -= first_line_len; } str_append(output, "=?utf-8?q?"); for (i = 0; i < len; i++) { if (line_len_left < 3) { /* if we're not at the beginning of an UTF8 character, go backwards until we are */ while (i > 0 && (input[i] & 0xc0) == 0x80) { str_truncate(output, str_len(output)-3); i--; } str_append(output, "?=\n\t=?utf-8?q?"); line_len_left = MIME_MAX_LINE_LEN - MIME_WRAPPER_LEN - 1; } switch (input[i]) { case ' ': str_append_c(output, '_'); break; case '=': case '?': case '_': line_len_left -= 2; str_printfa(output, "=%02X", input[i]); break; default: if (input[i] < 32 || (input[i] & 0x80) != 0) { line_len_left -= 2; str_printfa(output, "=%02X", input[i]); } else { str_append_c(output, input[i]); } break; } line_len_left--; } str_append(output, "?="); } void message_header_encode_b(const unsigned char *input, unsigned int len, string_t *output, unsigned int first_line_len) { unsigned int line_len, line_len_left, max; line_len = first_line_len; if (line_len >= MIME_MAX_LINE_LEN - MIME_WRAPPER_LEN) { str_append(output, "\n\t"); line_len = 1; } for (;;) { line_len_left = MIME_MAX_LINE_LEN - MIME_WRAPPER_LEN - line_len; max = MAX_BASE64_DECODED_SIZE(line_len_left); do { max--; if (max > len) max = len; else { /* all of it doesn't fit. find a character where we can split it from. */ while (max > 0 && (input[max] & 0xc0) == 0x80) max--; } } while (MAX_BASE64_ENCODED_SIZE(max) > line_len_left && max > 0); if (max > 0) { str_append(output, "=?utf-8?b?"); base64_encode(input, max, output); str_append(output, "?="); } input += max; len -= max; if (len == 0) break; str_append(output, "\n\t"); line_len = 1; } } void message_header_encode(const char *input, string_t *output) { message_header_encode_data((const void *)input, strlen(input), output); } void message_header_encode_data(const unsigned char *input, unsigned int len, string_t *output) { unsigned int i, j, first_line_len, cur_line_len, last_idx; unsigned int enc_chars, enc_len, base64_len, q_len; const unsigned char *next_line_input; unsigned int next_line_len = 0; bool use_q, cr; /* find the first word that needs encoding */ for (i = 0; i < len; i++) { if (input_idx_need_encoding(input, i, len)) break; } if (i == len) { /* no encoding necessary */ str_append_data(output, input, len); return; } /* go back to the beginning of the word so it is fully encoded */ if (input[i] != '\r' && input[i] != '\n') { while (i > 0 && !IS_LWSP(input[i-1])) i--; } /* write the prefix */ str_append_data(output, input, i); first_line_len = j = i; while (j > 0 && input[j-1] != '\n') j--; if (j != 0) first_line_len = j; input += i; len -= i; /* we'll encode data only up to the next LF, the rest is handled recursively. */ next_line_input = memchr(input, '\n', len); if (next_line_input != NULL) { if (next_line_input != input && next_line_input[-1] == '\r') next_line_input--; cur_line_len = next_line_input - input; next_line_len = len - cur_line_len; len = cur_line_len; } /* find the last word that needs encoding */ last_idx = 0; enc_chars = 0; for (i = 0; i < len; i++) { if (input_idx_need_encoding(input, i, len)) { last_idx = i + 1; enc_chars++; } } while (last_idx < len && !IS_LWSP(input[last_idx])) last_idx++; /* figure out if we should use Q or B encoding. Prefer Q if it's not too much larger. */ enc_len = last_idx; base64_len = MAX_BASE64_ENCODED_SIZE(enc_len); q_len = enc_len + enc_chars*3; use_q = q_len*2/3 <= base64_len; /* and do it */ if (enc_len == 0) ; else if (use_q) message_header_encode_q(input, enc_len, output, first_line_len); else message_header_encode_b(input, enc_len, output, first_line_len); str_append_data(output, input + last_idx, len - last_idx); if (next_line_input != NULL) { /* we're at [CR]LF */ i = 0; if (next_line_input[0] == '\r') { cr = TRUE; i++; } else { cr = FALSE; } i_assert(next_line_input[i] == '\n'); if (++i == next_line_len) return; /* drop trailing [CR]LF */ if (cr) str_append_c(output, '\r'); str_append_c(output, '\n'); if (next_line_input[i] == ' ' || next_line_input[i] == '\t') { str_append_c(output, next_line_input[i]); i++; } else { /* make it valid folding whitespace by adding a TAB */ str_append_c(output, '\t'); } message_header_encode_data(next_line_input+i, next_line_len-i, output); } } dovecot-2.2.33.2/src/lib-mail/mbox-from.c0000644000175000017500000001510713165463624014672 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "utc-mktime.h" #include "utc-offset.h" #include "mbox-from.h" #include #include static const char *weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static int mbox_parse_month(const unsigned char *msg, struct tm *tm) { int i; for (i = 0; i < 12; i++) { if (i_memcasecmp(months[i], msg, 3) == 0) { tm->tm_mon = i; break; } } if (i == 12 && memcmp(msg, "???", 3) == 0) { /* just a hack to parse one special mbox I have :) */ i = 0; } if (i == 12 || msg[3] != ' ') return -1; return 0; } static int mbox_parse_year(const unsigned char *msg, struct tm *tm) { if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || !i_isdigit(msg[2]) || !i_isdigit(msg[3])) return -1; tm->tm_year = (msg[0]-'0') * 1000 + (msg[1]-'0') * 100 + (msg[2]-'0') * 10 + (msg[3]-'0') - 1900; return 0; } int mbox_from_parse(const unsigned char *msg, size_t size, time_t *time_r, int *tz_offset_r, char **sender_r) { const unsigned char *msg_start, *sender_end, *msg_end; struct tm tm; int esc, alt_stamp, timezone_secs = 0, seen_timezone = FALSE; time_t t; *time_r = (time_t)-1; *sender_r = NULL; /* */ msg_start = msg; msg_end = msg + size; /* get sender */ if (msg < msg_end && *msg == '"') { /* "x y z"@domain - skip the quoted part */ esc = FALSE; msg++; while (msg < msg_end && (*msg != '"' || esc)) { if (*msg == '\r' || *msg == '\n') return -1; esc = *msg == '\\'; msg++; } msg++; } while (msg < msg_end && *msg != ' ') { if (*msg == '\r' || *msg == '\n') return -1; msg++; } sender_end = msg; while (msg < msg_end && *msg == ' ') msg++; /* next 29 chars should be in the date in asctime() format, eg. "Thu Nov 9 22:33:52 2001 +0300" - Some put the timezone before the year - Some use a named timezone before or after year, which we ignore - Some don't include seconds (-3) - Some don't include timezone (-5) */ if (msg+29-3-5 > msg_end) return -1; i_zero(&tm); /* skip weekday */ msg += 4; /* month */ if (mbox_parse_month(msg, &tm) < 0) { /* Try alternate timestamp: "Thu, 9 Nov 2002 22:33:52" */ alt_stamp = TRUE; msg++; if (!i_isdigit(msg[0])) return -1; tm.tm_mday = msg[0]-'0'; msg++; if (i_isdigit(msg[0])) { tm.tm_mday = tm.tm_mday*10 + msg[0]-'0'; msg++; } if (msg[0] != ' ') return -1; msg++; if (mbox_parse_month(msg, &tm) < 0) return -1; msg += 4; if (mbox_parse_year(msg, &tm) < 0) return -1; msg += 5; } else { alt_stamp = FALSE; msg += 4; /* day. single digit is usually preceded by extra space */ if (msg[0] == ' ') msg++; if (msg[1] == ' ') { if (!i_isdigit(msg[0])) return -1; tm.tm_mday = msg[0]-'0'; msg += 2; } else { if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ' ') return -1; tm.tm_mday = (msg[0]-'0') * 10 + (msg[1]-'0'); msg += 3; } } if (tm.tm_mday == 0) tm.tm_mday = 1; /* hour */ if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ':') return -1; tm.tm_hour = (msg[0]-'0') * 10 + (msg[1]-'0'); msg += 3; /* minute */ if (!i_isdigit(msg[0]) || !i_isdigit(msg[1])) return -1; tm.tm_min = (msg[0]-'0') * 10 + (msg[1]-'0'); msg += 2; /* optional second */ if (msg[0] == ':') { msg++; if (!i_isdigit(msg[0]) || !i_isdigit(msg[1])) return -1; tm.tm_sec = (msg[0]-'0') * 10 + (msg[1]-'0'); msg += 2; if (!alt_stamp) { if (msg[0] == ' ') msg++; else return -1; } } else if (!alt_stamp) { if (msg[0] != ' ') return -1; msg++; } /* optional named timezone */ if (alt_stamp) ; else if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || !i_isdigit(msg[2]) || !i_isdigit(msg[3])) { /* skip to next space */ while (msg < msg_end && *msg != ' ') { if (*msg == '\r' || *msg == '\n') return -1; msg++; } if (msg+5 > msg_end) return -1; msg++; } else if ((msg[0] == '-' || msg[0] == '+') && i_isdigit(msg[1]) && i_isdigit(msg[2]) && i_isdigit(msg[3]) && i_isdigit(msg[4]) && msg[5] == ' ') { /* numeric timezone, use it */ seen_timezone = TRUE; timezone_secs = (msg[1]-'0') * 10*60*60 + (msg[2]-'0') * 60*60 + (msg[3]-'0') * 10 + (msg[4]-'0'); if (msg[0] == '-') timezone_secs = -timezone_secs; msg += 6; } if (!alt_stamp) { /* year */ if (mbox_parse_year(msg, &tm) < 0) return -1; msg += 4; } tm.tm_isdst = -1; if (!seen_timezone && msg != msg_end && msg[0] == ' ' && (msg[1] == '-' || msg[1] == '+') && i_isdigit(msg[2]) && i_isdigit(msg[3]) && i_isdigit(msg[4]) && i_isdigit(msg[5])) { seen_timezone = TRUE; timezone_secs = (msg[2]-'0') * 10*60*60 + (msg[3]-'0') * 60*60 + (msg[4]-'0') * 10 + (msg[5]-'0'); if (msg[1] == '-') timezone_secs = -timezone_secs; } if (seen_timezone) { t = utc_mktime(&tm); if (t == (time_t)-1) return -1; t -= timezone_secs; *time_r = t; *tz_offset_r = timezone_secs/60; } else { /* assume local timezone */ *time_r = mktime(&tm); *tz_offset_r = utc_offset(localtime(time_r), *time_r); } *sender_r = i_strdup_until(msg_start, sender_end); return 0; } const char *mbox_from_create(const char *sender, time_t timestamp) { string_t *str; struct tm *tm; int year; str = t_str_new(256); str_append(str, "From "); str_append(str, sender); str_append(str, " "); /* we could use simply asctime(), but i18n etc. may break it. Example: "Thu Nov 29 22:33:52 2001" */ tm = localtime(×tamp); /* week day */ str_append(str, weekdays[tm->tm_wday]); str_append_c(str, ' '); /* month */ str_append(str, months[tm->tm_mon]); str_append_c(str, ' '); /* day */ str_append_c(str, (tm->tm_mday / 10) + '0'); str_append_c(str, (tm->tm_mday % 10) + '0'); str_append_c(str, ' '); /* hour */ str_append_c(str, (tm->tm_hour / 10) + '0'); str_append_c(str, (tm->tm_hour % 10) + '0'); str_append_c(str, ':'); /* minute */ str_append_c(str, (tm->tm_min / 10) + '0'); str_append_c(str, (tm->tm_min % 10) + '0'); str_append_c(str, ':'); /* second */ str_append_c(str, (tm->tm_sec / 10) + '0'); str_append_c(str, (tm->tm_sec % 10) + '0'); str_append_c(str, ' '); /* year */ year = tm->tm_year + 1900; str_append_c(str, (year / 1000) + '0'); str_append_c(str, ((year / 100) % 10) + '0'); str_append_c(str, ((year / 10) % 10) + '0'); str_append_c(str, (year % 10) + '0'); str_append_c(str, '\n'); return str_c(str); } dovecot-2.2.33.2/src/lib-mail/message-parser.c0000644000175000017500000007774513165463624015722 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "istream.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "message-parser.h" /* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix. We'll add a bit more just in case. */ #define BOUNDARY_END_MAX_LEN (70 + 2 + 2 + 10) struct message_boundary { struct message_boundary *next; struct message_part *part; const char *boundary; size_t len; unsigned int epilogue_found:1; }; struct message_parser_ctx { pool_t parser_pool, part_pool; struct istream *input; struct message_part *parts, *part; const char *broken_reason; enum message_header_parser_flags hdr_flags; enum message_parser_flags flags; const char *last_boundary; struct message_boundary *boundaries; size_t skip; char last_chr; unsigned int want_count; struct message_header_parser_ctx *hdr_parser_ctx; unsigned int prev_hdr_newline_size; int (*parse_next_block)(struct message_parser_ctx *ctx, struct message_block *block_r); unsigned int part_seen_content_type:1; unsigned int multipart:1; unsigned int eof:1; }; message_part_header_callback_t *null_message_part_header_callback = NULL; static int parse_next_header_init(struct message_parser_ctx *ctx, struct message_block *block_r); static int parse_next_body_to_boundary(struct message_parser_ctx *ctx, struct message_block *block_r); static int parse_next_body_to_eof(struct message_parser_ctx *ctx, struct message_block *block_r); static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx, struct message_block *block_r); static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx, struct message_block *block_r); static struct message_boundary * boundary_find(struct message_boundary *boundaries, const unsigned char *data, size_t len) { struct message_boundary *best = NULL; /* As MIME spec says: search from latest one to oldest one so that we don't break if the same boundary is used in nested parts. Also the full message line doesn't have to match the boundary, only the beginning. However, if there are multiple prefixes whose beginning matches, use the longest matching one. */ while (boundaries != NULL) { if (boundaries->len <= len && memcmp(boundaries->boundary, data, boundaries->len) == 0 && (best == NULL || best->len < boundaries->len)) best = boundaries; boundaries = boundaries->next; } return best; } static void parse_body_add_block(struct message_parser_ctx *ctx, struct message_block *block) { unsigned int missing_cr_count = 0; const unsigned char *cur, *next, *data = block->data; i_assert(block->size > 0); block->hdr = NULL; /* check if we have NULs */ if (memchr(data, '\0', block->size) != NULL) ctx->part->flags |= MESSAGE_PART_FLAG_HAS_NULS; /* count number of lines and missing CRs */ if (*data == '\n') { ctx->part->body_size.lines++; if (ctx->last_chr != '\r') missing_cr_count++; } cur = data + 1; while ((next = memchr(cur, '\n', block->size - (cur - data))) != NULL) { ctx->part->body_size.lines++; if (next[-1] != '\r') missing_cr_count++; cur = next + 1; } ctx->last_chr = data[block->size - 1]; ctx->skip += block->size; ctx->part->body_size.physical_size += block->size; ctx->part->body_size.virtual_size += block->size + missing_cr_count; } static int message_parser_read_more(struct message_parser_ctx *ctx, struct message_block *block_r, bool *full_r) { int ret; if (ctx->skip > 0) { i_stream_skip(ctx->input, ctx->skip); ctx->skip = 0; } *full_r = FALSE; ret = i_stream_read_data(ctx->input, &block_r->data, &block_r->size, ctx->want_count); if (ret <= 0) { switch (ret) { case 0: if (!ctx->input->eof) { i_assert(!ctx->input->blocking); return 0; } break; case -1: i_assert(ctx->input->eof || ctx->input->stream_errno != 0); ctx->eof = TRUE; if (block_r->size != 0) { /* EOF, but we still have some data. return it. */ return 1; } return -1; case -2: *full_r = TRUE; break; default: i_unreached(); } } if (!*full_r) { /* reset number of wanted characters if we actually got them */ ctx->want_count = 1; } return 1; } static struct message_part * message_part_append(pool_t pool, struct message_part *parent) { struct message_part *p, *part, **list; i_assert(parent != NULL); i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0); part = p_new(pool, struct message_part, 1); part->parent = parent; for (p = parent; p != NULL; p = p->parent) p->children_count++; /* set child position */ part->physical_pos = parent->physical_pos + parent->body_size.physical_size + parent->header_size.physical_size; list = &part->parent->children; while (*list != NULL) list = &(*list)->next; *list = part; return part; } static void parse_next_body_multipart_init(struct message_parser_ctx *ctx) { struct message_boundary *b; b = p_new(ctx->parser_pool, struct message_boundary, 1); b->part = ctx->part; b->boundary = ctx->last_boundary; b->len = strlen(b->boundary); b->next = ctx->boundaries; ctx->boundaries = b; ctx->last_boundary = NULL; } static int parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx, struct message_block *block_r) { ctx->part = message_part_append(ctx->part_pool, ctx->part); return parse_next_header_init(ctx, block_r); } static int boundary_line_find(struct message_parser_ctx *ctx, const unsigned char *data, size_t size, bool full, struct message_boundary **boundary_r) { *boundary_r = NULL; if (size < 2) { i_assert(!full); if (ctx->input->eof) return -1; ctx->want_count = 2; return 0; } if (data[0] != '-' || data[1] != '-') { /* not a boundary, just skip this line */ return -1; } /* need to find the end of line */ if (memchr(data + 2, '\n', size - 2) == NULL && size < BOUNDARY_END_MAX_LEN && !ctx->input->eof && !full) { /* no LF found */ ctx->want_count = BOUNDARY_END_MAX_LEN; return 0; } data += 2; size -= 2; *boundary_r = boundary_find(ctx->boundaries, data, size); if (*boundary_r == NULL) return -1; (*boundary_r)->epilogue_found = size >= (*boundary_r)->len + 2 && memcmp(data + (*boundary_r)->len, "--", 2) == 0; return 1; } static int parse_next_mime_header_init(struct message_parser_ctx *ctx, struct message_block *block_r) { ctx->part = message_part_append(ctx->part_pool, ctx->part); ctx->part->flags |= MESSAGE_PART_FLAG_IS_MIME; return parse_next_header_init(ctx, block_r); } static int parse_next_body_skip_boundary_line(struct message_parser_ctx *ctx, struct message_block *block_r) { const unsigned char *ptr; int ret; bool full; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; ptr = memchr(block_r->data, '\n', block_r->size); if (ptr == NULL) { parse_body_add_block(ctx, block_r); if (block_r->size > 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES) != 0) return 1; return 0; } /* found the LF */ block_r->size = (ptr - block_r->data) + 1; parse_body_add_block(ctx, block_r); if (ctx->boundaries == NULL || ctx->boundaries->part != ctx->part) { /* epilogue */ if (ctx->boundaries != NULL) ctx->parse_next_block = parse_next_body_to_boundary; else ctx->parse_next_block = parse_next_body_to_eof; } else { /* a new MIME part begins */ ctx->parse_next_block = parse_next_mime_header_init; } if (block_r->size > 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES) != 0) return 1; return ctx->parse_next_block(ctx, block_r); } static int parse_part_finish(struct message_parser_ctx *ctx, struct message_boundary *boundary, struct message_block *block_r, bool first_line) { struct message_part *part; size_t line_size; i_assert(ctx->last_boundary == NULL); /* get back to parent MIME part, summing the child MIME part sizes into parent's body sizes */ for (part = ctx->part; part != boundary->part; part = part->parent) { message_size_add(&part->parent->body_size, &part->body_size); message_size_add(&part->parent->body_size, &part->header_size); } i_assert(part != NULL); ctx->part = part; if (boundary->epilogue_found) { /* this boundary isn't needed anymore */ ctx->boundaries = boundary->next; } else { /* forget about the boundaries we possibly skipped */ ctx->boundaries = boundary; } /* the boundary itself should already be in buffer. add that. */ block_r->data = i_stream_get_data(ctx->input, &block_r->size); i_assert(block_r->size >= ctx->skip); block_r->data += ctx->skip; /* [[\r]\n]--[--] */ if (first_line) line_size = 0; else if (block_r->data[0] == '\r') { i_assert(block_r->data[1] == '\n'); line_size = 2; } else { i_assert(block_r->data[0] == '\n'); line_size = 1; } line_size += 2 + boundary->len + (boundary->epilogue_found ? 2 : 0); i_assert(block_r->size >= ctx->skip + line_size); block_r->size = line_size; parse_body_add_block(ctx, block_r); ctx->parse_next_block = parse_next_body_skip_boundary_line; if ((ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES) != 0) return 1; return ctx->parse_next_block(ctx, block_r); } static int parse_next_body_to_boundary(struct message_parser_ctx *ctx, struct message_block *block_r) { struct message_boundary *boundary = NULL; const unsigned char *data, *cur, *next, *end; size_t boundary_start; int ret; bool full; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; data = block_r->data; if (ctx->last_chr == '\n') { /* handle boundary in first line of message. alternatively it's an empty line. */ ret = boundary_line_find(ctx, block_r->data, block_r->size, full, &boundary); if (ret >= 0) { return ret == 0 ? 0 : parse_part_finish(ctx, boundary, block_r, TRUE); } } i_assert(block_r->size > 0); boundary_start = 0; /* skip to beginning of the next line. the first line was handled already. */ cur = data; end = data + block_r->size; while ((next = memchr(cur, '\n', end - cur)) != NULL) { cur = next + 1; boundary_start = next - data; if (next > data && next[-1] == '\r') boundary_start--; if (boundary_start != 0) { /* we can at least skip data until the first [CR]LF. input buffer can't be full anymore. */ full = FALSE; } ret = boundary_line_find(ctx, cur, end - cur, full, &boundary); if (ret >= 0) { /* found / need more data */ if (ret == 0 && boundary_start == 0) ctx->want_count += cur - block_r->data; break; } } if (next != NULL) { /* found / need more data */ i_assert(ret >= 0); i_assert(!(ret == 0 && full)); } else if (boundary_start == 0) { /* no linefeeds in this block. we can just skip it. */ ret = 0; if (block_r->data[block_r->size-1] == '\r' && !ctx->eof) { /* this may be the beginning of the \r\n--boundary */ block_r->size--; } boundary_start = block_r->size; } else { /* the boundary wasn't found from this data block, we'll need more data. */ ret = 0; ctx->want_count = (block_r->size - boundary_start) + 1; } if (ret > 0 || (ret == 0 && !ctx->eof)) { /* a) we found the boundary b) we need more data and haven't reached EOF yet so leave CR+LF + last line to buffer */ block_r->size = boundary_start; } if (block_r->size != 0) { parse_body_add_block(ctx, block_r); if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) == 0) return 0; return 1; } return ret <= 0 ? ret : parse_part_finish(ctx, boundary, block_r, FALSE); } static int parse_next_body_to_eof(struct message_parser_ctx *ctx, struct message_block *block_r) { bool full; int ret; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; parse_body_add_block(ctx, block_r); if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) == 0) return 0; return 1; } static void parse_content_type(struct message_parser_ctx *ctx, struct message_header_line *hdr) { struct rfc822_parser_context parser; const char *const *results; string_t *content_type; int ret; if (ctx->part_seen_content_type) return; ctx->part_seen_content_type = TRUE; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); content_type = t_str_new(64); ret = rfc822_parse_content_type(&parser, content_type); if (strcasecmp(str_c(content_type), "message/rfc822") == 0) ctx->part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822; else if (strncasecmp(str_c(content_type), "text", 4) == 0 && (str_len(content_type) == 4 || str_data(content_type)[4] == '/')) ctx->part->flags |= MESSAGE_PART_FLAG_TEXT; else if (strncasecmp(str_c(content_type), "multipart/", 10) == 0) { ctx->part->flags |= MESSAGE_PART_FLAG_MULTIPART; if (strcasecmp(str_c(content_type)+10, "digest") == 0) ctx->part->flags |= MESSAGE_PART_FLAG_MULTIPART_DIGEST; } if (ret < 0) return; if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 || ctx->last_boundary != NULL) return; rfc2231_parse(&parser, &results); for (; *results != NULL; results += 2) { if (strcasecmp(results[0], "boundary") == 0) { ctx->last_boundary = p_strdup(ctx->parser_pool, results[1]); break; } } } static bool block_is_at_eoh(const struct message_block *block) { if (block->size < 1) return FALSE; if (block->data[0] == '\n') return TRUE; if (block->data[0] == '\r') { if (block->size < 2) return FALSE; if (block->data[1] == '\n') return TRUE; } return FALSE; } #define MUTEX_FLAGS \ (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART) static int parse_next_header(struct message_parser_ctx *ctx, struct message_block *block_r) { struct message_part *part = ctx->part; struct message_header_line *hdr; struct message_boundary *boundary; bool full; int ret; if ((ret = message_parser_read_more(ctx, block_r, &full)) == 0) return ret; if (ret > 0 && block_is_at_eoh(block_r) && ctx->last_boundary != NULL && (part->flags & MESSAGE_PART_FLAG_IS_MIME) != 0) { /* we are at the end of headers and we've determined that we're going to start a multipart. add the boundary already here at this point so we can reliably determine whether the "\n--boundary" belongs to us or to a previous boundary. this is a problem if the boundary prefixes are identical, because MIME requires only the prefix to match. */ parse_next_body_multipart_init(ctx); ctx->multipart = TRUE; } /* before parsing the header see if we can find a --boundary from here. we're guaranteed to be at the beginning of the line here. */ if (ret > 0) { ret = ctx->boundaries == NULL ? -1 : boundary_line_find(ctx, block_r->data, block_r->size, full, &boundary); if (ret > 0 && boundary->part == ctx->part) { /* our own body begins with our own --boundary. we don't want to handle that yet. */ ret = -1; } } if (ret < 0) { /* no boundary */ ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr); if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) { ctx->want_count = i_stream_get_data_size(ctx->input) + 1; return ret; } } else if (ret == 0) { /* need more data */ return 0; } else { /* boundary found. stop parsing headers here. The previous [CR]LF belongs to the MIME boundary though. */ if (ctx->prev_hdr_newline_size > 0) { i_assert(ctx->part->header_size.lines > 0); /* remove the newline size from the MIME header */ ctx->part->header_size.lines--; ctx->part->header_size.physical_size -= ctx->prev_hdr_newline_size; ctx->part->header_size.virtual_size -= 2; /* add the newline size to the parent's body */ ctx->part->parent->body_size.lines++; ctx->part->parent->body_size.physical_size += ctx->prev_hdr_newline_size; ctx->part->parent->body_size.virtual_size += 2; } hdr = NULL; } if (hdr != NULL) { if (hdr->eoh) ; else if (strcasecmp(hdr->name, "Mime-Version") == 0) { /* it's MIME. Content-* headers are valid */ part->flags |= MESSAGE_PART_FLAG_IS_MIME; } else if (strcasecmp(hdr->name, "Content-Type") == 0) { if ((ctx->flags & MESSAGE_PARSER_FLAG_MIME_VERSION_STRICT) == 0) part->flags |= MESSAGE_PART_FLAG_IS_MIME; if (hdr->continues) hdr->use_full_value = TRUE; else T_BEGIN { parse_content_type(ctx, hdr); } T_END; } block_r->hdr = hdr; block_r->size = 0; ctx->prev_hdr_newline_size = hdr->no_newline ? 0 : (hdr->crlf_newline ? 2 : 1); return 1; } /* end of headers */ if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { /* It's not MIME. Reset everything we found from Content-Type. */ i_assert(!ctx->multipart); part->flags = 0; } ctx->last_boundary = NULL; if (!ctx->part_seen_content_type || (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { if (part->parent != NULL && (part->parent->flags & MESSAGE_PART_FLAG_MULTIPART_DIGEST) != 0) { /* when there's no content-type specified and we're below multipart/digest, assume message/rfc822 content-type */ part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822; } else { /* otherwise we default to text/plain */ part->flags |= MESSAGE_PART_FLAG_TEXT; } } if (message_parse_header_has_nuls(ctx->hdr_parser_ctx)) part->flags |= MESSAGE_PART_FLAG_HAS_NULS; message_parse_header_deinit(&ctx->hdr_parser_ctx); i_assert((part->flags & MUTEX_FLAGS) != MUTEX_FLAGS); ctx->last_chr = '\n'; if (ctx->multipart) { i_assert(ctx->last_boundary == NULL); ctx->multipart = FALSE; ctx->parse_next_block = parse_next_body_to_boundary; } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) ctx->parse_next_block = parse_next_body_message_rfc822_init; else if (ctx->boundaries != NULL) ctx->parse_next_block = parse_next_body_to_boundary; else ctx->parse_next_block = parse_next_body_to_eof; ctx->want_count = 1; /* return empty block as end of headers */ block_r->hdr = NULL; block_r->size = 0; return 1; } static int parse_next_header_init(struct message_parser_ctx *ctx, struct message_block *block_r) { i_assert(ctx->hdr_parser_ctx == NULL); ctx->hdr_parser_ctx = message_parse_header_init(ctx->input, &ctx->part->header_size, ctx->hdr_flags); ctx->part_seen_content_type = FALSE; ctx->prev_hdr_newline_size = 0; ctx->parse_next_block = parse_next_header; return parse_next_header(ctx, block_r); } static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED, struct message_block *block_r ATTR_UNUSED) { return -1; } static void preparsed_skip_to_next(struct message_parser_ctx *ctx) { ctx->parse_next_block = preparsed_parse_next_header_init; while (ctx->part != NULL) { if (ctx->part->next != NULL) { ctx->part = ctx->part->next; break; } /* parse epilogue of multipart parent if requested */ if (ctx->part->parent != NULL && (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) { /* check for presence of epilogue */ uoff_t part_end = ctx->part->physical_pos + ctx->part->header_size.physical_size + ctx->part->body_size.physical_size; uoff_t parent_end = ctx->part->parent->physical_pos + ctx->part->parent->header_size.physical_size + ctx->part->parent->body_size.physical_size; if (parent_end > part_end) { ctx->parse_next_block = preparsed_parse_epilogue_init; break; } } ctx->part = ctx->part->parent; } if (ctx->part == NULL) ctx->parse_next_block = preparsed_parse_eof; } static int preparsed_parse_body_finish(struct message_parser_ctx *ctx, struct message_block *block_r) { i_stream_skip(ctx->input, ctx->skip); ctx->skip = 0; preparsed_skip_to_next(ctx); return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx, struct message_block *block_r) { i_stream_skip(ctx->input, ctx->skip); ctx->skip = 0; ctx->parse_next_block = preparsed_parse_next_header_init; ctx->part = ctx->part->children; return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_body_more(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t end_offset = ctx->part->physical_pos + ctx->part->header_size.physical_size + ctx->part->body_size.physical_size; bool full; int ret; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; if (ctx->input->v_offset + block_r->size >= end_offset) { block_r->size = end_offset - ctx->input->v_offset; ctx->parse_next_block = preparsed_parse_body_finish; } ctx->skip = block_r->size; return 1; } static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t boundary_min_start, end_offset; const unsigned char *cur; bool full; int ret; i_assert(ctx->part->children != NULL); end_offset = ctx->part->children->physical_pos; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; if (ctx->input->v_offset + block_r->size >= end_offset) { /* we've got the full prologue: clip off the initial boundary */ block_r->size = end_offset - ctx->input->v_offset; cur = block_r->data + block_r->size - 1; /* [\r]\n--boundary[\r]\n */ if (block_r->size < 5 || *cur != '\n') { ctx->broken_reason = "Prologue boundary end not at expected position"; return -1; } cur--; if (*cur == '\r') cur--; /* find newline just before boundary */ for (; cur >= block_r->data; cur--) { if (*cur == '\n') break; } if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') { ctx->broken_reason = "Prologue boundary beginning not at expected position"; return -1; } if (cur != block_r->data && cur[-1] == '\r') cur--; /* clip boundary */ block_r->size = cur - block_r->data; ctx->parse_next_block = preparsed_parse_prologue_finish; ctx->skip = block_r->size; return 1; } /* retain enough data in the stream buffer to contain initial boundary */ if (end_offset > BOUNDARY_END_MAX_LEN) boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN; else boundary_min_start = 0; if (ctx->input->v_offset + block_r->size >= boundary_min_start) { if (boundary_min_start <= ctx->input->v_offset) return 0; block_r->size = boundary_min_start - ctx->input->v_offset; } ctx->skip = block_r->size; return 1; } static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t end_offset = ctx->part->physical_pos + ctx->part->header_size.physical_size + ctx->part->body_size.physical_size; bool full; int ret; if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; if (ctx->input->v_offset + block_r->size >= end_offset) { block_r->size = end_offset - ctx->input->v_offset; ctx->parse_next_block = preparsed_parse_body_finish; } ctx->skip = block_r->size; return 1; } static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t end_offset = ctx->part->physical_pos + ctx->part->header_size.physical_size + ctx->part->body_size.physical_size; const unsigned char *data, *cur; size_t size; bool full; int ret; if (end_offset - ctx->input->v_offset < 7) { ctx->broken_reason = "Epilogue position is wrong"; return -1; } if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) return ret; /* [\r]\n--boundary--[\r]\n */ if (block_r->size < 7) { ctx->want_count = 7; return 0; } data = block_r->data; size = block_r->size; cur = data; if (*cur == '\r') cur++; if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') { ctx->broken_reason = "Epilogue boundary start not at expected position"; return -1; } /* find the end of the line */ cur += 3; if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) { if (end_offset < ctx->input->v_offset + size) { ctx->broken_reason = "Epilogue boundary end not at expected position"; return -1; } else if (ctx->input->v_offset + size < end_offset && size < BOUNDARY_END_MAX_LEN && !ctx->input->eof && !full) { ctx->want_count = BOUNDARY_END_MAX_LEN; return 0; } } block_r->size = 0; ctx->parse_next_block = preparsed_parse_epilogue_more; ctx->skip = cur - data + 1; return 0; } static int preparsed_parse_body_init(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t offset = ctx->part->physical_pos + ctx->part->header_size.physical_size; if (offset < ctx->input->v_offset) { /* header was actually larger than the cached size suggested */ ctx->broken_reason = "Header larger than its cached size"; return -1; } i_stream_skip(ctx->input, offset - ctx->input->v_offset); /* multipart messages may begin with --boundary--, which makes them not have any children. */ if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 || ctx->part->children == NULL) ctx->parse_next_block = preparsed_parse_body_more; else ctx->parse_next_block = preparsed_parse_prologue_more; return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx, struct message_block *block_r) { uoff_t offset = ctx->part->physical_pos + ctx->part->header_size.physical_size + ctx->part->body_size.physical_size; ctx->part = ctx->part->parent; if (offset < ctx->input->v_offset) { /* last child was actually larger than the cached size suggested */ ctx->broken_reason = "Part larger than its cached size"; return -1; } i_stream_skip(ctx->input, offset - ctx->input->v_offset); ctx->parse_next_block = preparsed_parse_epilogue_boundary; return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_finish_header(struct message_parser_ctx *ctx, struct message_block *block_r) { if (ctx->part->children != NULL) { if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) ctx->parse_next_block = preparsed_parse_body_init; else { ctx->parse_next_block = preparsed_parse_next_header_init; ctx->part = ctx->part->children; } } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) { ctx->parse_next_block = preparsed_parse_body_init; } else { preparsed_skip_to_next(ctx); } return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_next_header(struct message_parser_ctx *ctx, struct message_block *block_r) { struct message_header_line *hdr; int ret; ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr); if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) { ctx->want_count = i_stream_get_data_size(ctx->input) + 1; return ret; } if (hdr != NULL) { block_r->hdr = hdr; block_r->size = 0; return 1; } message_parse_header_deinit(&ctx->hdr_parser_ctx); ctx->parse_next_block = preparsed_parse_finish_header; /* return empty block as end of headers */ block_r->hdr = NULL; block_r->size = 0; i_assert(ctx->skip == 0); if (ctx->input->v_offset != ctx->part->physical_pos + ctx->part->header_size.physical_size) { ctx->broken_reason = "Cached header size mismatch"; return -1; } return 1; } static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx, struct message_block *block_r) { struct istream *hdr_input; i_assert(ctx->hdr_parser_ctx == NULL); i_assert(ctx->part->physical_pos >= ctx->input->v_offset); i_stream_skip(ctx->input, ctx->part->physical_pos - ctx->input->v_offset); /* the header may become truncated by --boundaries. limit the header stream's size to what it's supposed to be to avoid duplicating (and keeping in sync!) all the same complicated logic as in parse_next_header(). */ hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size); ctx->hdr_parser_ctx = message_parse_header_init(hdr_input, NULL, ctx->hdr_flags); i_stream_unref(&hdr_input); ctx->parse_next_block = preparsed_parse_next_header; return preparsed_parse_next_header(ctx, block_r); } static struct message_parser_ctx * message_parser_init_int(struct istream *input, enum message_header_parser_flags hdr_flags, enum message_parser_flags flags) { struct message_parser_ctx *ctx; pool_t pool; pool = pool_alloconly_create("Message Parser", 1024); ctx = p_new(pool, struct message_parser_ctx, 1); ctx->parser_pool = pool; ctx->hdr_flags = hdr_flags; ctx->flags = flags; ctx->input = input; i_stream_ref(input); return ctx; } struct message_parser_ctx * message_parser_init(pool_t part_pool, struct istream *input, enum message_header_parser_flags hdr_flags, enum message_parser_flags flags) { struct message_parser_ctx *ctx; ctx = message_parser_init_int(input, hdr_flags, flags); ctx->part_pool = part_pool; ctx->parts = ctx->part = p_new(part_pool, struct message_part, 1); ctx->parse_next_block = parse_next_header_init; return ctx; } struct message_parser_ctx * message_parser_init_from_parts(struct message_part *parts, struct istream *input, enum message_header_parser_flags hdr_flags, enum message_parser_flags flags) { struct message_parser_ctx *ctx; i_assert(parts != NULL); ctx = message_parser_init_int(input, hdr_flags, flags); ctx->parts = ctx->part = parts; ctx->parse_next_block = preparsed_parse_next_header_init; return ctx; } int message_parser_deinit(struct message_parser_ctx **_ctx, struct message_part **parts_r) { const char *error; return message_parser_deinit_from_parts(_ctx, parts_r, &error); } int message_parser_deinit_from_parts(struct message_parser_ctx **_ctx, struct message_part **parts_r, const char **error_r) { struct message_parser_ctx *ctx = *_ctx; int ret = ctx->broken_reason != NULL ? -1 : 0; *_ctx = NULL; *parts_r = ctx->parts; *error_r = ctx->broken_reason; if (ctx->hdr_parser_ctx != NULL) message_parse_header_deinit(&ctx->hdr_parser_ctx); i_stream_unref(&ctx->input); pool_unref(&ctx->parser_pool); i_assert(ret < 0 || *parts_r != NULL); return ret; } int message_parser_parse_next_block(struct message_parser_ctx *ctx, struct message_block *block_r) { int ret; bool eof = FALSE, full; i_zero(block_r); while ((ret = ctx->parse_next_block(ctx, block_r)) == 0) { ret = message_parser_read_more(ctx, block_r, &full); if (ret == 0) { i_assert(!ctx->input->blocking); return 0; } if (ret == -1) { i_assert(!eof); eof = TRUE; } } block_r->part = ctx->part; if (ret < 0 && ctx->part != NULL) { /* Successful EOF or unexpected failure */ i_assert(ctx->input->eof || ctx->input->closed || ctx->input->stream_errno != 0 || ctx->broken_reason != NULL); while (ctx->part->parent != NULL) { message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size); message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size); ctx->part = ctx->part->parent; } } if (block_r->size == 0) { /* data isn't supposed to be read, so make sure it's NULL */ block_r->data = NULL; } return ret; } #undef message_parser_parse_header void message_parser_parse_header(struct message_parser_ctx *ctx, struct message_size *hdr_size, message_part_header_callback_t *callback, void *context) { struct message_block block; int ret; while ((ret = message_parser_parse_next_block(ctx, &block)) > 0) { callback(block.part, block.hdr, context); if (block.hdr == NULL) break; } i_assert(ret != 0); i_assert(ctx->part != NULL); if (ret < 0) { /* well, can't return error so fake end of headers */ callback(ctx->part, NULL, context); } *hdr_size = ctx->part->header_size; } #undef message_parser_parse_body void message_parser_parse_body(struct message_parser_ctx *ctx, message_part_header_callback_t *hdr_callback, void *context) { struct message_block block; int ret; while ((ret = message_parser_parse_next_block(ctx, &block)) > 0) { if (block.size == 0 && hdr_callback != NULL) hdr_callback(block.part, block.hdr, context); } i_assert(ret != 0); } dovecot-2.2.33.2/src/lib-mail/rfc822-parser.h0000644000175000017500000000441313123174404015254 00000000000000#ifndef RFC822_PARSER_H #define RFC822_PARSER_H struct rfc822_parser_context { const unsigned char *data, *end; string_t *last_comment; }; #define IS_ATEXT(c) \ (rfc822_atext_chars[(int)(unsigned char)(c)] != 0) #define IS_ATEXT_NON_TSPECIAL(c) \ ((rfc822_atext_chars[(int)(unsigned char)(c)] & 3) != 0) extern unsigned char rfc822_atext_chars[256]; /* Parse given data using RFC 822 token parser. */ void rfc822_parser_init(struct rfc822_parser_context *ctx, const unsigned char *data, size_t size, string_t *last_comment) ATTR_NULL(4); /* The functions below return 1 = more data available, 0 = no more data available (but a value might have been returned now), -1 = invalid input. LWSP is automatically skipped after value, but not before it. So typically you begin with skipping LWSP and then start using the parse functions. */ /* Parse comment. Assumes parser's data points to '(' */ int rfc822_skip_comment(struct rfc822_parser_context *ctx); /* Skip LWSP if there is any */ int ATTR_NOWARN_UNUSED_RESULT rfc822_skip_lwsp(struct rfc822_parser_context *ctx); /* Stop at next non-atext char */ int rfc822_parse_atom(struct rfc822_parser_context *ctx, string_t *str); /* Like parse_atom() but don't stop at '.' */ int rfc822_parse_dot_atom(struct rfc822_parser_context *ctx, string_t *str); /* Like parse_dot_atom() but stops for '/', '?' and '='. Also it doesn't allow LWSP around '.' chars. */ int rfc822_parse_mime_token(struct rfc822_parser_context *ctx, string_t *str); /* "quoted string" */ int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str); /* atom or quoted-string */ int rfc822_parse_phrase(struct rfc822_parser_context *ctx, string_t *str); /* dot-atom / domain-literal */ int rfc822_parse_domain(struct rfc822_parser_context *ctx, string_t *str); /* Parse Content-Type header's type/subtype. */ int rfc822_parse_content_type(struct rfc822_parser_context *ctx, string_t *str); /* For Content-Type style parameter parsing. Expect ";" key "=" value. value is unescaped if needed. The returned strings are allocated from data stack. Returns 1 = key/value set, 0 = no more data, -1 = invalid input. */ int rfc822_parse_content_param(struct rfc822_parser_context *ctx, const char **key_r, const char **value_r); #endif dovecot-2.2.33.2/src/lib-mail/message-snippet.c0000644000175000017500000000766713165463624016104 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "istream.h" #include "mail-html2text.h" #include "message-parser.h" #include "message-decoder.h" #include "message-snippet.h" enum snippet_state { /* beginning of the line */ SNIPPET_STATE_NEWLINE = 0, /* within normal text */ SNIPPET_STATE_NORMAL, /* within quoted text - skip until EOL */ SNIPPET_STATE_QUOTED }; struct snippet_context { string_t *snippet; unsigned int chars_left; enum snippet_state state; bool add_whitespace; struct mail_html2text *html2text; buffer_t *plain_output; }; static bool snippet_generate(struct snippet_context *ctx, const unsigned char *data, size_t size) { size_t i, count; if (ctx->html2text != NULL) { buffer_set_used_size(ctx->plain_output, 0); mail_html2text_more(ctx->html2text, data, size, ctx->plain_output); data = ctx->plain_output->data; size = ctx->plain_output->used; } /* message-decoder should feed us only valid and complete UTF-8 input */ for (i = 0; i < size; i += count) { count = 1; switch (ctx->state) { case SNIPPET_STATE_NEWLINE: if (data[i] == '>' && ctx->html2text == NULL) { ctx->state = SNIPPET_STATE_QUOTED; break; } ctx->state = SNIPPET_STATE_NORMAL; /* fallthrough */ case SNIPPET_STATE_NORMAL: if (size-i >= 3 && ((data[i] == 0xEF && data[i+1] == 0xBB && data[i+2] == 0xBF) || (data[i] == 0xBF && data[i+1] == 0xBB && data[i+2] == 0xEF))) { count += 2; /* because we skip +1 next */ break; } if (data[i] == '\r' || data[i] == '\n' || data[i] == '\t' || data[i] == ' ') { /* skip any leading whitespace */ if (str_len(ctx->snippet) > 1) ctx->add_whitespace = TRUE; if (data[i] == '\n') ctx->state = SNIPPET_STATE_NEWLINE; break; } if (ctx->add_whitespace) { str_append_c(ctx->snippet, ' '); ctx->add_whitespace = FALSE; if (ctx->chars_left-- == 0) return FALSE; } if (ctx->chars_left == 0) return FALSE; ctx->chars_left--; count = uni_utf8_char_bytes(data[i]); i_assert(i + count <= size); str_append_n(ctx->snippet, data + i, count); break; case SNIPPET_STATE_QUOTED: if (data[i] == '\n') ctx->state = SNIPPET_STATE_NEWLINE; break; } } return TRUE; } int message_snippet_generate(struct istream *input, unsigned int max_snippet_chars, string_t *snippet) { struct message_parser_ctx *parser; struct message_part *parts; struct message_decoder_context *decoder; struct message_block raw_block, block; struct snippet_context ctx; pool_t pool; int ret; i_zero(&ctx); pool = pool_alloconly_create("message snippet", 1024); ctx.snippet = snippet; ctx.chars_left = max_snippet_chars; parser = message_parser_init(pool_datastack_create(), input, 0, 0); decoder = message_decoder_init(NULL, 0); while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) { if (!message_decoder_decode_next_block(decoder, &raw_block, &block)) continue; if (block.size == 0) { const char *ct; if (block.hdr != NULL) continue; /* end of headers - verify that we can use this Content-Type. we get here only once, because we always handle only one non-multipart MIME part. */ ct = message_decoder_current_content_type(decoder); if (ct == NULL) /* text/plain */ ; else if (mail_html2text_content_type_match(ct)) { ctx.html2text = mail_html2text_init(MAIL_HTML2TEXT_FLAG_SKIP_QUOTED); ctx.plain_output = buffer_create_dynamic(pool, 1024); } else if (strncasecmp(ct, "text/", 5) != 0) break; continue; } if (!snippet_generate(&ctx, block.data, block.size)) break; } i_assert(ret != 0); message_decoder_deinit(&decoder); if (message_parser_deinit(&parser, &parts) < 0) i_unreached(); if (ctx.html2text != NULL) mail_html2text_deinit(&ctx.html2text); pool_unref(&pool); return input->stream_errno == 0 ? 0 : -1; } dovecot-2.2.33.2/src/lib-mail/mail-html2text.c0000644000175000017500000001734113123174404015626 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "unichar.h" #include "message-parser.h" #include "mail-html2text.h" /* Zero-width space (​) apparently also belongs here, but that gets a bit tricky to handle.. is it actually used anywhere? */ #define HTML_WHITESPACE(c) \ ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') enum html_state { /* regular text */ HTML_STATE_TEXT, /* tag outside "quoted string" */ HTML_STATE_TAG, /* tag inside "double quoted string" */ HTML_STATE_TAG_DQUOTED, /* tag -> "escape\ */ HTML_STATE_TAG_DQUOTED_ESCAPE, /* tag inside 'single quoted string' */ HTML_STATE_TAG_SQUOTED, /* tag -> 'escape\ */ HTML_STATE_TAG_SQUOTED_ESCAPE, /* comment */ HTML_STATE_COMMENT, /* comment is ending, we've seen "--" and now just waiting for ">" */ HTML_STATE_COMMENT_END, /* (java)script */ HTML_STATE_SCRIPT, /* CSS style */ HTML_STATE_STYLE, /* */ HTML_STATE_CDATA }; struct mail_html2text { enum mail_html2text_flags flags; enum html_state state; buffer_t *input; unsigned int quote_level; bool ignore_next_text; }; static struct { const char *name; unichar_t chr; } html_entities[] = { #include "html-entities.h" }; struct mail_html2text * mail_html2text_init(enum mail_html2text_flags flags) { struct mail_html2text *ht; ht = i_new(struct mail_html2text, 1); ht->flags = flags; ht->input = buffer_create_dynamic(default_pool, 512); return ht; } static size_t parse_tag_name(struct mail_html2text *ht, const unsigned char *data, size_t size) { size_t i; if (size >= 3 && memcmp(data, "!--", 3) == 0) { ht->state = HTML_STATE_COMMENT; return 3 + 1; } if (size >= 7 && i_memcasecmp(data, "script", 6) == 0 && (HTML_WHITESPACE(data[6]) || data[6] == '>')) { ht->state = HTML_STATE_SCRIPT; return 7 + 1; } if (size >= 6 && i_memcasecmp(data, "style", 5) == 0 && (HTML_WHITESPACE(data[5]) || data[5] == '>')) { ht->state = HTML_STATE_STYLE; return 6 + 1; } if (size >= 8 && i_memcasecmp(data, "![CDATA[", 8) == 0) { ht->state = HTML_STATE_CDATA; return 8 + 1; } if ((ht->flags & MAIL_HTML2TEXT_FLAG_SKIP_QUOTED) != 0) { if (size >= 10 && i_memcasecmp(data, "blockquote", 10) == 0 && (HTML_WHITESPACE(data[10]) || data[10] == '>')) { ht->quote_level++; ht->state = HTML_STATE_TAG; return 1; } else if (ht->quote_level > 0 && size >= 12 && i_memcasecmp(data, "/blockquote>", 12) == 0) { if (--ht->quote_level == 0) ht->ignore_next_text = FALSE; ht->state = HTML_STATE_TAG; return 1; } } if (size < 12) { /* can we see the whole tag name? */ for (i = 0; i < size; i++) { if (HTML_WHITESPACE(data[i]) || data[i] == '>') break; } if (i == size) { /* need more data */ return 0; } } ht->state = HTML_STATE_TAG; return 1; } static bool html_entity_get_unichar(const char *name, unichar_t *chr_r) { unichar_t chr; for (size_t i = 0; i < N_ELEMENTS(html_entities); i++) { if (strcasecmp(html_entities[i].name, name) == 0) { *chr_r = html_entities[i].chr; return TRUE; } } /* maybe it's just encoded binary byte it can be &#nnn; or &#xnnn; */ if (name[0] == '#' && ((name[1] == 'x' && str_to_uint32_hex(name+2, &chr) == 0) || str_to_uint32(name+1, &chr) == 0)) { *chr_r = chr; return TRUE; } return FALSE; } static size_t parse_entity(const unsigned char *data, size_t size, buffer_t *output) { char entity[10]; unichar_t chr; size_t i; for (i = 0; i < size; i++) { if (HTML_WHITESPACE(data[i]) || i >= sizeof(entity)) { /* broken entity */ return 1; } if (data[i] == ';') break; } if (i == size) return 0; i_assert(i < sizeof(entity)); memcpy(entity, data, i); entity[i] = '\0'; if (html_entity_get_unichar(entity, &chr)) uni_ucs4_to_utf8_c(chr, output); return i + 1 + 1; } static void mail_html2text_add_space(buffer_t *output) { const unsigned char *data = output->data; if (output->used > 0 && data[output->used-1] != ' ') buffer_append_c(output, ' '); } static size_t parse_data(struct mail_html2text *ht, const unsigned char *data, size_t size, buffer_t *output) { size_t i, ret; for (i = 0; i < size; i++) { char c = data[i]; switch (ht->state) { case HTML_STATE_TEXT: if (c == '<') { ret = parse_tag_name(ht, data+i+1, size-i-1); if (ret == 0) return i; i += ret - 1; } else if (c == '&') { ret = parse_entity(data+i+1, size-i-1, output); if (ret == 0) return i; i += ret - 1; } else if (ht->quote_level == 0) { buffer_append_c(output, c); } break; case HTML_STATE_TAG: if (c == '"') ht->state = HTML_STATE_TAG_DQUOTED; else if (c == '\'') ht->state = HTML_STATE_TAG_SQUOTED; else if (c == '>') { ht->state = HTML_STATE_TEXT; mail_html2text_add_space(output); } break; case HTML_STATE_TAG_DQUOTED: if (c == '"') ht->state = HTML_STATE_TAG; else if (c == '\\') ht->state = HTML_STATE_TAG_DQUOTED_ESCAPE; break; case HTML_STATE_TAG_DQUOTED_ESCAPE: ht->state = HTML_STATE_TAG_DQUOTED; break; case HTML_STATE_TAG_SQUOTED: if (c == '\'') ht->state = HTML_STATE_TAG; else if (c == '\\') ht->state = HTML_STATE_TAG_SQUOTED_ESCAPE; break; case HTML_STATE_TAG_SQUOTED_ESCAPE: ht->state = HTML_STATE_TAG_SQUOTED; break; case HTML_STATE_COMMENT: if (c == '-') { if (i+1 == size) return i; if (data[i+1] == '-') { ht->state = HTML_STATE_COMMENT_END; i++; } } break; case HTML_STATE_COMMENT_END: if (c == '>') ht->state = HTML_STATE_TEXT; else if (!HTML_WHITESPACE(c)) ht->state = HTML_STATE_COMMENT; break; case HTML_STATE_SCRIPT: if (c == '<') { unsigned int max_len = I_MIN(size-i, 9); if (i_memcasecmp(data+i, "", max_len) == 0) { if (max_len < 9) return i; mail_html2text_add_space(output); ht->state = HTML_STATE_TEXT; i += 8; } } break; case HTML_STATE_STYLE: if (c == '<') { unsigned int max_len = I_MIN(size-i, 8); if (i_memcasecmp(data+i, "", max_len) == 0) { if (max_len < 8) return i; mail_html2text_add_space(output); ht->state = HTML_STATE_TEXT; i += 7; } } break; case HTML_STATE_CDATA: if (c == ']') { unsigned int max_len = I_MIN(size-i, 3); if (i_memcasecmp(data+i, "]]>", max_len) == 0) { if (max_len < 3) return i; ht->state = HTML_STATE_TEXT; i += 2; break; } } if (ht->quote_level == 0) buffer_append_c(output, c); break; } } return i; } void mail_html2text_more(struct mail_html2text *ht, const unsigned char *data, size_t size, buffer_t *output) { size_t pos, inc_size, buf_orig_size; i_assert(size > 0); while (ht->input->used > 0) { /* we didn't get enough input the last time to know what to do. */ buf_orig_size = ht->input->used; inc_size = I_MIN(size, 128); buffer_append(ht->input, data, inc_size); pos = parse_data(ht, ht->input->data, ht->input->used, output); if (pos == 0) { /* we need to add more data into buffer */ data += inc_size; size -= inc_size; if (size == 0) return; } else if (pos >= buf_orig_size) { /* we parsed forward */ data += pos - buf_orig_size; size -= pos - buf_orig_size; buffer_set_used_size(ht->input, 0); } else { /* invalid input - eat away what we parsed so far and retry */ buffer_set_used_size(ht->input, buf_orig_size); buffer_delete(ht->input, 0, pos); } } pos = parse_data(ht, data, size, output); buffer_append(ht->input, data + pos, size - pos); } void mail_html2text_deinit(struct mail_html2text **_ht) { struct mail_html2text *ht = *_ht; *_ht = NULL; buffer_free(&ht->input); i_free(ht); } dovecot-2.2.33.2/src/lib-mail/test-quoted-printable.c0000644000175000017500000000162513165463624017220 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "quoted-printable.h" #include "test-common.h" static void test_quoted_printable_q_decode(void) { const char *data[] = { "=0A=0D ", "\n\r ", "__foo__bar__", " foo bar ", "foo=", "foo=", "foo=A", "foo=A", "foo=Ax", "foo=Ax", "foo=Ax=xy", "foo=Ax=xy" }; buffer_t *buf; unsigned int i; test_begin("quoted printable q decode"); buf = buffer_create_dynamic(pool_datastack_create(), 128); for (i = 0; i < N_ELEMENTS(data); i += 2) { quoted_printable_q_decode((const void *)data[i], strlen(data[i]), buf); test_assert(strcmp(data[i+1], str_c(buf)) == 0); buffer_set_used_size(buf, 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_quoted_printable_q_decode, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/message-header-decode.h0000644000175000017500000000136113123174404017046 00000000000000#ifndef MESSAGE_HEADER_DECODE_H #define MESSAGE_HEADER_DECODE_H #include "unichar.h" /* Return FALSE if you wish to stop decoding. charset is NULL when it's not RFC2047-encoded. */ typedef bool message_header_decode_callback_t(const unsigned char *data, size_t size, const char *charset, void *context); /* Decode RFC2047 encoded words. Call specified function for each decoded block. */ void message_header_decode(const unsigned char *data, size_t size, message_header_decode_callback_t *callback, void *context); /* Append decoded RFC2047 header as UTF-8 to given buffer. */ void message_header_decode_utf8(const unsigned char *data, size_t size, buffer_t *dest, normalizer_func_t *normalizer); #endif dovecot-2.2.33.2/src/lib-mail/istream-attachment-extractor.c0000644000175000017500000004737313165463624020601 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "ostream.h" #include "base64.h" #include "buffer.h" #include "str.h" #include "hash-format.h" #include "rfc822-parser.h" #include "message-parser.h" #include "istream-attachment-extractor.h" #define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024 enum mail_attachment_state { MAIL_ATTACHMENT_STATE_NO, MAIL_ATTACHMENT_STATE_MAYBE, MAIL_ATTACHMENT_STATE_YES }; enum base64_state { BASE64_STATE_0 = 0, BASE64_STATE_1, BASE64_STATE_2, BASE64_STATE_3, BASE64_STATE_CR, BASE64_STATE_EOB, BASE64_STATE_EOM }; struct attachment_istream_part { char *content_type, *content_disposition; enum mail_attachment_state state; /* start offset of the message part in the original input stream */ uoff_t start_offset; /* for saving attachments base64-decoded: */ enum base64_state base64_state; unsigned int base64_line_blocks, cur_base64_blocks; uoff_t base64_bytes; bool base64_have_crlf; /* CRLF linefeeds */ bool base64_failed; int temp_fd; struct ostream *temp_output; buffer_t *part_buf; }; struct attachment_istream { struct istream_private istream; pool_t pool; struct istream_attachment_settings set; void *context; struct message_parser_ctx *parser; struct message_part *cur_part; struct attachment_istream_part part; bool retry_read; }; static void stream_add_data(struct attachment_istream *astream, const void *data, size_t size) { if (size > 0) { memcpy(i_stream_alloc(&astream->istream, size), data, size); astream->istream.pos += size; } } static void parse_content_type(struct attachment_istream *astream, const struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *content_type; if (astream->part.content_type != NULL) return; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); T_BEGIN { content_type = t_str_new(64); (void)rfc822_parse_content_type(&parser, content_type); astream->part.content_type = i_strdup(str_c(content_type)); } T_END; } static void parse_content_disposition(struct attachment_istream *astream, const struct message_header_line *hdr) { /* just pass it without parsing to is_attachment() callback */ i_free(astream->part.content_disposition); astream->part.content_disposition = i_strndup(hdr->full_value, hdr->full_value_len); } static void astream_parse_header(struct attachment_istream *astream, struct message_header_line *hdr) { if (!hdr->continued) { stream_add_data(astream, hdr->name, hdr->name_len); stream_add_data(astream, hdr->middle, hdr->middle_len); } stream_add_data(astream, hdr->value, hdr->value_len); if (!hdr->no_newline) { if (hdr->crlf_newline) stream_add_data(astream, "\r\n", 2); else stream_add_data(astream, "\n", 1); } if (hdr->continues) { hdr->use_full_value = TRUE; return; } if (strcasecmp(hdr->name, "Content-Type") == 0) parse_content_type(astream, hdr); else if (strcasecmp(hdr->name, "Content-Disposition") == 0) parse_content_disposition(astream, hdr); } static bool astream_want_attachment(struct attachment_istream *astream, struct message_part *part) { struct istream_attachment_header ahdr; if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) { /* multiparts may contain attachments as children, but they're never themselves */ return FALSE; } if (astream->set.want_attachment == NULL) return TRUE; i_zero(&ahdr); ahdr.part = part; ahdr.content_type = astream->part.content_type; ahdr.content_disposition = astream->part.content_disposition; return astream->set.want_attachment(&ahdr, astream->context); } static int astream_base64_decode_lf(struct attachment_istream_part *part) { if (part->base64_have_crlf && part->base64_state != BASE64_STATE_CR) { /* mixed LF vs CRLFs */ return -1; } part->base64_state = BASE64_STATE_0; if (part->cur_base64_blocks < part->base64_line_blocks) { /* last line */ part->base64_state = BASE64_STATE_EOM; return 0; } else if (part->base64_line_blocks == 0) { /* first line */ if (part->cur_base64_blocks == 0) return -1; part->base64_line_blocks = part->cur_base64_blocks; } else if (part->cur_base64_blocks == part->base64_line_blocks) { /* line is ok */ } else { return -1; } part->cur_base64_blocks = 0; return 1; } static int astream_try_base64_decode_char(struct attachment_istream_part *part, size_t pos, char chr) { switch (part->base64_state) { case BASE64_STATE_0: if (base64_is_valid_char(chr)) part->base64_state++; else if (chr == '\r') part->base64_state = BASE64_STATE_CR; else if (chr == '\n') { return astream_base64_decode_lf(part); } else { return -1; } break; case BASE64_STATE_1: if (!base64_is_valid_char(chr)) return -1; part->base64_state++; break; case BASE64_STATE_2: if (base64_is_valid_char(chr)) part->base64_state++; else if (chr == '=') part->base64_state = BASE64_STATE_EOB; else return -1; break; case BASE64_STATE_3: part->base64_bytes = part->temp_output->offset + pos + 1; if (base64_is_valid_char(chr)) { part->base64_state = BASE64_STATE_0; part->cur_base64_blocks++; } else if (chr == '=') { part->base64_state = BASE64_STATE_EOM; part->cur_base64_blocks++; if (part->cur_base64_blocks > part->base64_line_blocks && part->base64_line_blocks > 0) { /* too many blocks */ return -1; } return 0; } else { return -1; } break; case BASE64_STATE_CR: if (chr != '\n') return -1; if (!part->base64_have_crlf) { if (part->base64_line_blocks != 0) { /* mixed LF vs CRLFs */ return -1; } part->base64_have_crlf = TRUE; } return astream_base64_decode_lf(part); case BASE64_STATE_EOB: if (chr != '=') return -1; part->base64_bytes = part->temp_output->offset + pos + 1; part->base64_state = BASE64_STATE_EOM; part->cur_base64_blocks++; if (part->cur_base64_blocks > part->base64_line_blocks && part->base64_line_blocks > 0) { /* too many blocks */ return -1; } return 0; case BASE64_STATE_EOM: i_unreached(); } return 1; } static void astream_try_base64_decode(struct attachment_istream_part *part, const unsigned char *data, size_t size) { size_t i; int ret; if (part->base64_failed || part->base64_state == BASE64_STATE_EOM) return; for (i = 0; i < size; i++) { ret = astream_try_base64_decode_char(part, i, (char)data[i]); if (ret <= 0) { if (ret < 0) part->base64_failed = TRUE; break; } } } static int astream_open_output(struct attachment_istream *astream) { int fd; i_assert(astream->part.temp_fd == -1); fd = astream->set.open_temp_fd(astream->context); if (fd == -1) return -1; astream->part.temp_fd = fd; astream->part.temp_output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(astream->part.temp_output); return 0; } static void astream_add_body(struct attachment_istream *astream, const struct message_block *block) { struct attachment_istream_part *part = &astream->part; buffer_t *part_buf; size_t new_size; switch (part->state) { case MAIL_ATTACHMENT_STATE_NO: stream_add_data(astream, block->data, block->size); break; case MAIL_ATTACHMENT_STATE_MAYBE: /* we'll write data to in-memory buffer until we reach attachment min_size */ if (part->part_buf == NULL) { part->part_buf = buffer_create_dynamic(default_pool, astream->set.min_size); } part_buf = part->part_buf; new_size = part_buf->used + block->size; if (new_size < astream->set.min_size) { buffer_append(part_buf, block->data, block->size); break; } /* attachment is large enough. we'll first copy the buffered data from memory to temp file */ if (astream_open_output(astream) < 0) { /* failed, fallback to just saving it inline */ part->state = MAIL_ATTACHMENT_STATE_NO; stream_add_data(astream, part_buf->data, part_buf->used); stream_add_data(astream, block->data, block->size); break; } part->state = MAIL_ATTACHMENT_STATE_YES; astream_try_base64_decode(part, part_buf->data, part_buf->used); hash_format_loop(astream->set.hash_format, part_buf->data, part_buf->used); o_stream_nsend(part->temp_output, part_buf->data, part_buf->used); buffer_set_used_size(part_buf, 0); /* fall through - write the new data to temp file */ case MAIL_ATTACHMENT_STATE_YES: astream_try_base64_decode(part, block->data, block->size); hash_format_loop(astream->set.hash_format, block->data, block->size); o_stream_nsend(part->temp_output, block->data, block->size); break; } } static int astream_decode_base64(struct attachment_istream *astream) { struct attachment_istream_part *part = &astream->part; buffer_t *extra_buf = NULL; struct istream *input, *base64_input; struct ostream *output; const unsigned char *data; size_t size; ssize_t ret; buffer_t *buf; int outfd; bool failed = FALSE; if (part->base64_bytes < astream->set.min_size || part->temp_output->offset > part->base64_bytes + BASE64_ATTACHMENT_MAX_EXTRA_BYTES) { /* only a small part of the MIME part is base64-encoded. */ return -1; } if (part->base64_line_blocks == 0) { /* only one line of base64 */ part->base64_line_blocks = part->cur_base64_blocks; i_assert(part->base64_line_blocks > 0); } /* decode base64 data and write it to another temp file */ outfd = astream->set.open_temp_fd(astream->context); if (outfd == -1) return -1; buf = buffer_create_dynamic(default_pool, 1024); input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE); base64_input = i_stream_create_limit(input, part->base64_bytes); output = o_stream_create_fd_file(outfd, 0, FALSE); o_stream_cork(output); hash_format_reset(astream->set.hash_format); size_t bytes_needed = 1; while ((ret = i_stream_read_bytes(base64_input, &data, &size, bytes_needed)) > 0) { buffer_set_used_size(buf, 0); if (base64_decode(data, size, &size, buf) < 0) { i_error("istream-attachment: BUG: " "Attachment base64 data unexpectedly broke"); failed = TRUE; break; } i_stream_skip(base64_input, size); o_stream_nsend(output, buf->data, buf->used); hash_format_loop(astream->set.hash_format, buf->data, buf->used); bytes_needed = i_stream_get_data_size(base64_input) + 1; } if (ret != -1) { i_assert(failed); } else if (base64_input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(base64_input), i_stream_get_error(base64_input)); failed = TRUE; } if (o_stream_nfinish(output) < 0) { i_error("istream-attachment: write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); failed = TRUE; } buffer_free(&buf); i_stream_unref(&base64_input); o_stream_unref(&output); if (input->v_offset != part->temp_output->offset && !failed) { /* write the rest of the data to the message stream */ extra_buf = buffer_create_dynamic(default_pool, 1024); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { buffer_append(extra_buf, data, size); i_stream_skip(input, size); } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); failed = TRUE; } } i_stream_unref(&input); if (failed) { i_close_fd(&outfd); return -1; } /* successfully wrote it. switch to using it. */ o_stream_destroy(&part->temp_output); i_close_fd(&part->temp_fd); part->temp_fd = outfd; if (extra_buf != NULL) { stream_add_data(astream, extra_buf->data, extra_buf->used); buffer_free(&extra_buf); } return 0; } static int astream_part_finish(struct attachment_istream *astream, const char **error_r) { struct attachment_istream_part *part = &astream->part; struct istream_attachment_info info; struct istream *input; struct ostream *output; string_t *digest_str; const unsigned char *data; size_t size; int ret = 0; if (o_stream_nfinish(part->temp_output) < 0) { *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(part->temp_output), o_stream_get_error(part->temp_output)); return -1; } i_zero(&info); info.start_offset = astream->part.start_offset; /* base64_bytes contains how many valid base64 bytes there are so far. if the base64 ends properly, it'll specify how much of the MIME part is saved as an attachment. the rest of the data (typically linefeeds) is added back to main stream */ info.encoded_size = part->base64_bytes; /* get the hash before base64-decoder resets it */ digest_str = t_str_new(128); hash_format_write(astream->set.hash_format, digest_str); info.hash = str_c(digest_str); /* if it looks like we can decode base64 without any data loss, do it and write the decoded data to another temp file. */ if (!part->base64_failed) { if (part->base64_state == BASE64_STATE_0 && part->base64_bytes > 0) { /* there is no trailing LF or '=' characters, but it's not completely empty */ part->base64_state = BASE64_STATE_EOM; } if (part->base64_state == BASE64_STATE_EOM) { /* base64 data looks ok. */ if (astream_decode_base64(astream) < 0) part->base64_failed = TRUE; } else { part->base64_failed = TRUE; } } /* open attachment output file */ info.part = astream->cur_part; if (!part->base64_failed) { info.base64_blocks_per_line = part->base64_line_blocks; info.base64_have_crlf = part->base64_have_crlf; /* base64-decoder updated the hash, use it */ str_truncate(digest_str, 0); hash_format_write(astream->set.hash_format, digest_str); info.hash = str_c(digest_str); } else { /* couldn't decode base64, so write the entire MIME part as attachment */ info.encoded_size = part->temp_output->offset; } if (astream->set.open_attachment_ostream(&info, &output, error_r, astream->context) < 0) return -1; /* copy data to attachment from temp file */ input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE); while (i_stream_read_data(input, &data, &size, 0) > 0) { o_stream_nsend(output, data, size); i_stream_skip(input, size); } if (input->stream_errno != 0) { *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); ret = -1; } i_stream_destroy(&input); if (astream->set.close_attachment_ostream(output, ret == 0, error_r, astream->context) < 0) ret = -1; return ret; } static void astream_part_reset(struct attachment_istream *astream) { struct attachment_istream_part *part = &astream->part; if (part->temp_output != NULL) o_stream_destroy(&part->temp_output); if (part->temp_fd != -1) i_close_fd(&part->temp_fd); i_free_and_null(part->content_type); i_free_and_null(part->content_disposition); if (part->part_buf != NULL) buffer_free(&part->part_buf); i_zero(part); part->temp_fd = -1; hash_format_reset(astream->set.hash_format); } static int astream_end_of_part(struct attachment_istream *astream, const char **error_r) { struct attachment_istream_part *part = &astream->part; size_t old_size; int ret = 0; /* MIME part changed. we're now parsing the end of a boundary, possibly followed by message epilogue */ switch (part->state) { case MAIL_ATTACHMENT_STATE_NO: break; case MAIL_ATTACHMENT_STATE_MAYBE: /* MIME part wasn't large enough to be an attachment */ if (part->part_buf != NULL) { stream_add_data(astream, part->part_buf->data, part->part_buf->used); ret = part->part_buf->used > 0 ? 1 : 0; } break; case MAIL_ATTACHMENT_STATE_YES: old_size = astream->istream.pos - astream->istream.skip; if (astream_part_finish(astream, error_r) < 0) ret = -1; else { /* finished base64 may have added a few more trailing bytes to the stream */ ret = astream->istream.pos - astream->istream.skip - old_size; } break; } part->state = MAIL_ATTACHMENT_STATE_NO; astream_part_reset(astream); return ret; } static int astream_read_next(struct attachment_istream *astream, bool *retry_r) { struct istream_private *stream = &astream->istream; struct message_block block; size_t old_size, new_size; const char *error; int ret; *retry_r = FALSE; if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) return -2; old_size = stream->pos - stream->skip; switch (message_parser_parse_next_block(astream->parser, &block)) { case -1: /* done / error */ ret = astream_end_of_part(astream, &error); if (ret > 0) { /* final data */ new_size = stream->pos - stream->skip; return new_size - old_size; } stream->istream.eof = TRUE; stream->istream.stream_errno = stream->parent->stream_errno; if (ret < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EIO; } astream->cur_part = NULL; return -1; case 0: /* need more data */ return 0; default: break; } if (block.part != astream->cur_part && astream->cur_part != NULL) { /* end of a MIME part */ if (astream_end_of_part(astream, &error) < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EIO; return -1; } } astream->cur_part = block.part; if (block.hdr != NULL) { /* parsing a header */ astream_parse_header(astream, block.hdr); } else if (block.size == 0) { /* end of headers */ if (astream_want_attachment(astream, block.part)) { astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE; astream->part.start_offset = stream->parent->v_offset; } } else { astream_add_body(astream, &block); } new_size = stream->pos - stream->skip; *retry_r = new_size == old_size; return new_size - old_size; } static ssize_t i_stream_attachment_extractor_read(struct istream_private *stream) { struct attachment_istream *astream = (struct attachment_istream *)stream; bool retry; ssize_t ret; do { ret = astream_read_next(astream, &retry); } while (retry && astream->set.drain_parent_input); astream->retry_read = retry; return ret; } static void i_stream_attachment_extractor_close(struct iostream_private *stream, bool close_parent) { struct attachment_istream *astream = (struct attachment_istream *)stream; struct message_part *parts; if (astream->parser != NULL) { if (message_parser_deinit(&astream->parser, &parts) < 0) i_unreached(); /* we didn't use preparsed message_parts */ } hash_format_deinit_free(&astream->set.hash_format); if (astream->pool != NULL) pool_unref(&astream->pool); if (close_parent) i_stream_close(astream->istream.parent); } struct istream * i_stream_create_attachment_extractor(struct istream *input, struct istream_attachment_settings *set, void *context) { struct attachment_istream *astream; i_assert(set->min_size > 0); i_assert(set->hash_format != NULL); i_assert(set->open_attachment_ostream != NULL); i_assert(set->close_attachment_ostream != NULL); astream = i_new(struct attachment_istream, 1); astream->part.temp_fd = -1; astream->set = *set; astream->context = context; astream->retry_read = TRUE; /* make sure the caller doesn't try to double-free this */ set->hash_format = NULL; astream->istream.max_buffer_size = input->real_stream->max_buffer_size; astream->istream.read = i_stream_attachment_extractor_read; astream->istream.iostream.close = i_stream_attachment_extractor_close; astream->istream.istream.readable_fd = FALSE; astream->istream.istream.blocking = input->blocking; astream->istream.istream.seekable = FALSE; astream->pool = pool_alloconly_create("istream attachment", 1024); astream->parser = message_parser_init(astream->pool, input, 0, MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS | MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES); return i_stream_create(&astream->istream, input, i_stream_get_fd(input)); } bool i_stream_attachment_extractor_can_retry(struct istream *input) { struct attachment_istream *astream = (struct attachment_istream *)input->real_stream; return astream->retry_read; } dovecot-2.2.33.2/src/lib-mail/quoted-printable.h0000644000175000017500000000030213123174404016224 00000000000000#ifndef QUOTED_PRINTABLE_H #define QUOTED_PRINTABLE_H /* Decode MIME "Q" encoding. */ int quoted_printable_q_decode(const unsigned char *src, size_t src_size, buffer_t *dest); #endif dovecot-2.2.33.2/src/lib-mail/test-message-date.c0000644000175000017500000000371613165463624016303 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "message-date.h" #include "test-common.h" struct test_message_date { const char *input; time_t time; int tz_offset; bool ret; }; static void test_message_date_parse(void) { static const struct test_message_date tests[] = { #ifdef TIME_T_SIGNED { "Thu, 01 Jan 1970 01:59:59 +0200", -1, 2*60, TRUE }, { "Fri, 13 Dec 1901 20:45:53 +0000", -2147483647, 0, TRUE }, #endif #if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) { "Sun, 07 Feb 2106 06:28:15 +0000", 4294967295U, 0, TRUE }, #endif { "Wed, 07 Nov 2007 01:07:20 +0200", 1194390440, 2*60, TRUE }, { "Wed, 07 Nov 2007 01:07:20", 1194397640, 0, TRUE }, { "Thu, 01 Jan 1970 02:00:00 +0200", 0, 2*60, TRUE }, { "Tue, 19 Jan 2038 03:14:07 +0000", 2147483647, 0, TRUE }, { "Tue, 19 Jan 2038", 0, 0, FALSE }, /* June leap second */ { "Tue, 30 Jun 2015 23:59:59 +0300", 1435697999, 3*60, TRUE }, { "Tue, 30 Jun 2015 23:59:60 +0300", 1435697999, 3*60, TRUE }, { "Wed, 01 Jul 2015 00:00:00 +0300", 1435698000, 3*60, TRUE }, /* Invalid leap second */ { "Tue, 24 Jan 2017 15:59:60 +0300", 1485262799, 3*60, TRUE }, /* December leap second */ { "Sat, 31 Dec 2016 23:59:59 +0200", 1483221599, 2*60, TRUE }, { "Sat, 31 Dec 2016 23:59:60 +0200", 1483221599, 2*60, TRUE }, { "Sun, 01 Jan 2017 00:00:00 +0200", 1483221600, 2*60, TRUE }, }; unsigned int i; bool success; time_t t; int tz; bool ret; for (i = 0; i < N_ELEMENTS(tests); i++) { const struct test_message_date *test = &tests[i]; ret = message_date_parse((const unsigned char *)test->input, strlen(test->input), &t, &tz); success = (!ret && !test->ret) || (ret == test->ret && t == test->time && tz == test->tz_offset); test_out(t_strdup_printf("message_date_parse(%d)", i), success); } } int main(void) { static void (*test_functions[])(void) = { test_message_date_parse, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-binary-converter.h0000644000175000017500000000023013123174404017700 00000000000000#ifndef ISTREAM_BINARY_CONVERTER_H #define ISTREAM_BINARY_CONVERTER_H struct istream *i_stream_create_binary_converter(struct istream *input); #endif dovecot-2.2.33.2/src/lib-mail/message-id.c0000644000175000017500000000532113123174404014764 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "rfc822-parser.h" #include "message-id.h" static bool get_untokenized_msgid(const char **msgid_p, string_t *msgid) { struct rfc822_parser_context parser; int ret; rfc822_parser_init(&parser, (const unsigned char *)*msgid_p, strlen(*msgid_p), NULL); /* msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] id-left = dot-atom-text / no-fold-quote / obs-id-left id-right = dot-atom-text / no-fold-literal / obs-id-right no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE no-fold-literal = "[" *(dtext / quoted-pair) "]" */ rfc822_skip_lwsp(&parser); if (*parser.data == '"') ret = rfc822_parse_quoted_string(&parser, msgid); else ret = rfc822_parse_dot_atom(&parser, msgid); if (ret <= 0) return FALSE; if (*parser.data != '@') return FALSE; str_append_c(msgid, '@'); parser.data++; rfc822_skip_lwsp(&parser); if (rfc822_parse_dot_atom(&parser, msgid) <= 0) return FALSE; if (*parser.data != '>') return FALSE; *msgid_p = (const char *)parser.data + 1; return TRUE; } static void strip_lwsp(char *str) { /* @UNSAFE */ char *dest; /* find the first lwsp */ while (*str != ' ' && *str != '\t' && *str != '\r' && *str != '\n') { if (*str == '\0') return; str++; } for (dest = str; *str != '\0'; str++) { if (*str != ' ' && *str != '\t' && *str != '\r' && *str != '\n') *dest++ = *str; } *dest = '\0'; } const char *message_id_get_next(const char **msgid_p) { const char *msgid = *msgid_p; const char *p; string_t *str = NULL; bool found_at; if (*msgid_p == NULL) return NULL; for (;;) { /* skip until '<' */ while (*msgid != '<') { if (*msgid == '\0') { *msgid_p = msgid; return NULL; } msgid++; } msgid++; /* check it through quickly to see if it's already normalized */ p = msgid; found_at = FALSE; for (;; p++) { if ((unsigned char)*p >= 'A') /* matches most */ continue; if (*p == '@') found_at = TRUE; if (*p == '>' || *p == '"' || *p == '(' || *p == '[') break; if (*p == '\0') { *msgid_p = p; return NULL; } } if (*p == '>') { *msgid_p = p+1; if (found_at) { char *s; s = p_strdup_until(unsafe_data_stack_pool, msgid, p); strip_lwsp(s); return s; } } else { /* ok, do it the slow way */ *msgid_p = msgid; if (str == NULL) { /* allocate only once, so we don't leak with multiple invalid message IDs */ str = t_str_new(256); } if (get_untokenized_msgid(msgid_p, str)) return str_c(str); } /* invalid message id, see if there's another valid one */ msgid = *msgid_p; } } dovecot-2.2.33.2/src/lib-mail/message-part-serialize.c0000644000175000017500000001504313165463624017340 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "message-parser.h" #include "message-part-serialize.h" /* root part root's first children children's first children ... root's next children ... part unsigned int flags (not root part) uoff_t physical_pos uoff_t header_physical_size uoff_t header_virtual_size uoff_t body_physical_size uoff_t body_virtual_size (flags & (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_MESSAGE_RFC822)) unsigned int body_lines (flags & (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_MESSAGE_RFC822)) unsigned int children_count */ #define MINIMUM_SERIALIZED_SIZE \ (sizeof(unsigned int) + sizeof(uoff_t) * 4) struct deserialize_context { pool_t pool; const unsigned char *data, *end; uoff_t pos; const char *error; }; static void part_serialize(struct message_part *part, buffer_t *dest, unsigned int *children_count_r) { unsigned int count, children_count; size_t children_offset; bool root = part->parent == NULL; count = 0; while (part != NULL) { /* create serialized part */ buffer_append(dest, &part->flags, sizeof(part->flags)); if (root) root = FALSE; else { buffer_append(dest, &part->physical_pos, sizeof(part->physical_pos)); } buffer_append(dest, &part->header_size.physical_size, sizeof(part->header_size.physical_size)); buffer_append(dest, &part->header_size.virtual_size, sizeof(part->header_size.virtual_size)); buffer_append(dest, &part->body_size.physical_size, sizeof(part->body_size.physical_size)); buffer_append(dest, &part->body_size.virtual_size, sizeof(part->body_size.virtual_size)); if ((part->flags & (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) { buffer_append(dest, &part->body_size.lines, sizeof(part->body_size.lines)); } if ((part->flags & (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) { children_offset = buffer_get_used_size(dest); children_count = 0; buffer_append(dest, &children_count, sizeof(children_count)); if (part->children != NULL) { part_serialize(part->children, dest, &children_count); buffer_write(dest, children_offset, &children_count, sizeof(children_count)); } } else { i_assert(part->children == NULL); } count++; part = part->next; } *children_count_r = count; } void message_part_serialize(struct message_part *part, buffer_t *dest) { unsigned int children_count; part_serialize(part, dest, &children_count); } static bool read_next(struct deserialize_context *ctx, void *buffer, size_t buffer_size) { if (ctx->data + buffer_size > ctx->end) { ctx->error = "Not enough data"; return FALSE; } memcpy(buffer, ctx->data, buffer_size); ctx->data += buffer_size; return TRUE; } static bool ATTR_NULL(2) message_part_deserialize_part(struct deserialize_context *ctx, struct message_part *parent, unsigned int siblings, struct message_part **part_r) { struct message_part *p, *part, *first_part, **next_part; unsigned int children_count; uoff_t pos; bool root = parent == NULL; first_part = NULL; next_part = NULL; while (siblings > 0) { siblings--; part = p_new(ctx->pool, struct message_part, 1); part->parent = parent; for (p = parent; p != NULL; p = p->parent) p->children_count++; if (!read_next(ctx, &part->flags, sizeof(part->flags))) return FALSE; if (root) root = FALSE; else { if (!read_next(ctx, &part->physical_pos, sizeof(part->physical_pos))) return FALSE; } if (part->physical_pos < ctx->pos) { ctx->error = "physical_pos less than expected"; return FALSE; } if (!read_next(ctx, &part->header_size.physical_size, sizeof(part->header_size.physical_size))) return FALSE; if (!read_next(ctx, &part->header_size.virtual_size, sizeof(part->header_size.virtual_size))) return FALSE; if (part->header_size.virtual_size < part->header_size.physical_size) { ctx->error = "header_size.virtual_size too small"; return FALSE; } if (!read_next(ctx, &part->body_size.physical_size, sizeof(part->body_size.physical_size))) return FALSE; if (!read_next(ctx, &part->body_size.virtual_size, sizeof(part->body_size.virtual_size))) return FALSE; if ((part->flags & (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) { if (!read_next(ctx, &part->body_size.lines, sizeof(part->body_size.lines))) return FALSE; } if (part->body_size.virtual_size < part->body_size.physical_size) { ctx->error = "body_size.virtual_size too small"; return FALSE; } if ((part->flags & (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) { if (!read_next(ctx, &children_count, sizeof(children_count))) return FALSE; } else { children_count = 0; } if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) { /* Only one child is possible */ if (children_count == 0) { ctx->error = "message/rfc822 part has no children"; return FALSE; } if (children_count != 1) { ctx->error = "message/rfc822 part " "has multiple children"; return FALSE; } } if (children_count > 0) { /* our children must be after our physical_pos+header and the last child must be within our size. */ ctx->pos = part->physical_pos + part->header_size.physical_size; pos = ctx->pos + part->body_size.physical_size; if (!message_part_deserialize_part(ctx, part, children_count, &part->children)) return FALSE; if (ctx->pos > pos) { ctx->error = "child part location exceeds our size"; return FALSE; } ctx->pos = pos; /* save it for above check for parent */ } if (first_part == NULL) first_part = part; if (next_part != NULL) *next_part = part; next_part = &part->next; } *part_r = first_part; return TRUE; } struct message_part * message_part_deserialize(pool_t pool, const void *data, size_t size, const char **error_r) { struct deserialize_context ctx; struct message_part *part; i_zero(&ctx); ctx.pool = pool; ctx.data = data; ctx.end = ctx.data + size; if (!message_part_deserialize_part(&ctx, NULL, 1, &part)) { *error_r = ctx.error; return NULL; } if (ctx.data != ctx.end) { *error_r = "Too much data"; return NULL; } return part; } dovecot-2.2.33.2/src/lib-mail/message-size.h0000644000175000017500000000161513123174404015351 00000000000000#ifndef MESSAGE_SIZE_H #define MESSAGE_SIZE_H struct message_size { uoff_t physical_size; uoff_t virtual_size; unsigned int lines; }; /* Calculate size of message header. Leave the input point to first character in body. */ int message_get_header_size(struct istream *input, struct message_size *hdr, bool *has_nuls_r); /* Calculate size of message body. */ int message_get_body_size(struct istream *input, struct message_size *body, bool *has_nuls_r); /* Sum contents of src into dest. */ void message_size_add(struct message_size *dest, const struct message_size *src); /* Skip number of virtual bytes from buffer. last_cr_r is set to TRUE if the last character we skipped was '\r', meaning that the next character should be '\n', which shouldn't be treated as "\r\n". */ int message_skip_virtual(struct istream *input, uoff_t virtual_skip, bool *last_cr_r); #endif dovecot-2.2.33.2/src/lib-mail/message-parser.h0000644000175000017500000001001013165463624015673 00000000000000#ifndef MESSAGE_PARSER_H #define MESSAGE_PARSER_H #include "message-header-parser.h" #include "message-part.h" enum message_parser_flags { /* Don't return message bodies in message_blocks. */ MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK = 0x01, /* Buggy software creates Content-Type: headers without Mime-Version: header. By default we allow this and assume message is MIME if Content-Type: is found. This flag disables this. */ MESSAGE_PARSER_FLAG_MIME_VERSION_STRICT = 0x02, /* Return multipart (preamble and epilogue) blocks */ MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS = 0x04, /* Return --boundary lines */ MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES = 0x08 }; struct message_parser_ctx; struct message_block { /* Message part this block belongs to */ struct message_part *part; /* non-NULL if a header line was read */ struct message_header_line *hdr; /* hdr = NULL, size = 0 block returned at the end of headers for the empty line between header and body (unless the header is truncated). Later on data and size>0 is returned for blocks of mail body that is read (see message_parser_flags for what is actually returned) */ const unsigned char *data; size_t size; }; /* called once with hdr = NULL at the end of headers */ typedef void message_part_header_callback_t(struct message_part *part, struct message_header_line *hdr, void *context); extern message_part_header_callback_t *null_message_part_header_callback; /* Initialize message parser. part_spool specifies where struct message_parts are allocated from. */ struct message_parser_ctx * message_parser_init(pool_t part_pool, struct istream *input, enum message_header_parser_flags hdr_flags, enum message_parser_flags flags); /* Use preparsed parts to speed up parsing. */ struct message_parser_ctx * message_parser_init_from_parts(struct message_part *parts, struct istream *input, enum message_header_parser_flags hdr_flags, enum message_parser_flags flags); /* Returns 0 if parts were returned, -1 we used preparsed parts and they didn't match the current message */ int message_parser_deinit(struct message_parser_ctx **ctx, struct message_part **parts_r); /* Same as message_parser_deinit(), but return an error message describing why the preparsed parts didn't match the message. This can also safely be called even when preparsed parts weren't used - it'll always just return success in that case. */ int message_parser_deinit_from_parts(struct message_parser_ctx **_ctx, struct message_part **parts_r, const char **error_r); /* Read the next block of a message. Returns 1 if block is returned, 0 if input stream is non-blocking and more data needs to be read, -1 when all is done or error occurred (see stream's error status). */ int message_parser_parse_next_block(struct message_parser_ctx *ctx, struct message_block *block_r); /* Read and parse header. */ void message_parser_parse_header(struct message_parser_ctx *ctx, struct message_size *hdr_size, message_part_header_callback_t *callback, void *context) ATTR_NULL(4); #define message_parser_parse_header(ctx, hdr_size, callback, context) \ message_parser_parse_header(ctx, hdr_size + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct message_part *, \ struct message_header_line *, typeof(context))), \ (message_part_header_callback_t *)callback, context) /* Read and parse body. If message is a MIME multipart or message/rfc822 message, hdr_callback is called for all headers. body_callback is called for the body content. */ void message_parser_parse_body(struct message_parser_ctx *ctx, message_part_header_callback_t *hdr_callback, void *context) ATTR_NULL(3); #define message_parser_parse_body(ctx, callback, context) \ message_parser_parse_body(ctx, \ (message_part_header_callback_t *)callback, \ (void *)((char *)context + CALLBACK_TYPECHECK(callback, \ void (*)(struct message_part *, \ struct message_header_line *, typeof(context))))) #endif dovecot-2.2.33.2/src/lib-mail/message-part-serialize.h0000644000175000017500000000071113123174404017326 00000000000000#ifndef MESSAGE_PART_SERIALIZE_H #define MESSAGE_PART_SERIALIZE_H struct message_part; struct message_size; /* Serialize message part. */ void message_part_serialize(struct message_part *part, buffer_t *dest); /* Generate struct message_part from serialized data. Returns NULL and sets error if any problems are detected. */ struct message_part * message_part_deserialize(pool_t pool, const void *data, size_t size, const char **error_r); #endif dovecot-2.2.33.2/src/lib-mail/test-istream-qp-encoder.c0000644000175000017500000001266413165463624017445 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-qp.h" static const struct { const void *input; const char *output; int stream_errno; } tests[] = { { "", "", 0 }, { "short test", "short test", 0 }, { "C'est une cha\xc3\xaene de test simple", "C'est une cha=C3=AEne de test simple", 0 }, { "wrap after 76 characters wrap after 76 characters wrap after 76 characters wrap after 76 characters", "wrap after 76 characters wrap after 76 characters wrap after 76 character=\r\ns wrap after 76 characters", 0 }, { /* the string is split up to avoid C compilers thinking \x99ed as escape */ "P\xc5\x99" "edstavitel\xc3\xa9 francouzsk\xc3\xa9ho lidu, ustanoveni v " "N\xc3\xa1rodn\xc3\xadm shrom\xc3\xa1\xc5\xbe" "d\xc4\x9bn\xc3\xad, domn" "\xc3\xadvaj\xc3\xad" "ce se, \xc5\xbe" "e nev\xc4\x9b" "domost, zapomenut\xc3" "\xad nebo pohrd\xc3\xa1n\xc3\xad lidsk\xc3\xbdmi pr\xc3\xa1vy jsou j" "edin\xc3\xbdmi p\xc5\x99\xc3\xad\xc4\x8dinami ve\xc5\x99" "ejn\xc3\xb" "dch ne\xc5\xa1t\xc4\x9bst\xc3\xad a zkorumpov\xc3\xa1n\xc3\xad vl" "\xc3\xa1" "d, rozhodli se vylo\xc5\xbeit v slavnostn\xc3\xad Deklara" "ci p\xc5\x99irozen\xc3\xa1, nezciziteln\xc3\xa1 a posv\xc3\xa1tn\xc3" "\xa1 pr\xc3\xa1va \xc4\x8dlov\xc4\x9bka za t\xc3\xadm \xc3\xba\xc4" "\x8d" "elem, aby tato Deklarace, neust\xc3\xa1le jsouc p\xc5\x99" "ed o" "\xc4\x8" "dima v\xc5\xa1" "em \xc4\x8dlen\xc5\xafm lidsk\xc3\xa9 spo" "le\xc4\x8dnosti, uv\xc3\xa1" "d\xc4\x9bla jim st\xc3\xa1le na pam\xc4" "\x9b\xc5\xa5 jejich pr\xc3\xa1va a jejich povinnosti; aby \xc4\x8din" "y z\xc3\xa1konod\xc3\xa1rn\xc3\xa9 moci a \xc4\x8diny v\xc3\xbdkonn" "\xc3\xa9 moci mohly b\xc3\xbdt v ka\xc5\xbe" "d\xc3\xa9 chv\xc3\xadli p" "orovn\xc3\xa1v\xc3\xa1ny s \xc3\xba\xc4\x8d" "elem ka\xc5\xbe" "d\xc3" "\xa9 politick\xc3\xa9 instituce a byly v d\xc5\xafsledku toho chov" "\xc3\xa1ny je\xc5\xa1t\xc4\x9b v\xc3\xad" "ce v \xc3\xba" "ct\xc4" "\x9b; aby po\xc5\xbe" "adavky ob\xc4\x8d" "an\xc5\xaf, kdy\xc5\xbe s" "e budou nap\xc5\x99\xc3\xad\xc5\xa1t\xc4\x9b zakl\xc3\xa1" "dat na j" "ednoduch\xc3\xb" "dch a nepop\xc3\xadrateln\xc3\xbd" "ch z\xc3\xa1sa" "d\xc3\xa1" "ch, sm\xc4\x9b\xc5\x99ovaly v\xc5\xb" "edy k zachov\xc3" "\xa1n\xc3\xad \xc3\xbastavy a ku blahu v\xc5\xa1" "ech.", "P=C5=99edstavitel=C3=A9 francouzsk=C3=A9ho lidu, ustanoveni v N=C3=A1rodn=\r\n" "=C3=ADm shrom=C3=A1=C5=BEd=C4=9Bn=C3=AD, domn=C3=ADvaj=C3=ADce se, =C5=BE=\r\n" "e nev=C4=9Bdomost, zapomenut=C3=AD nebo pohrd=C3=A1n=C3=AD lidsk=C3=BDmi=20=\r\n" "pr=C3=A1vy jsou jedin=C3=BDmi p=C5=99=C3=AD=C4=8Dinami ve=C5=99ejn=C3=0Bd=\r\n" "ch ne=C5=A1t=C4=9Bst=C3=AD a zkorumpov=C3=A1n=C3=AD vl=C3=A1d, rozhodli=20=\r\n" "se vylo=C5=BEit v slavnostn=C3=AD Deklaraci p=C5=99irozen=C3=A1, nezcizit=\r\n" "eln=C3=A1 a posv=C3=A1tn=C3=A1 pr=C3=A1va =C4=8Dlov=C4=9Bka za t=C3=ADm=20=\r\n" "=C3=BA=C4=8Delem, aby tato Deklarace, neust=C3=A1le jsouc p=C5=99ed o=C4=\r\n" "=08dima v=C5=A1em =C4=8Dlen=C5=AFm lidsk=C3=A9 spole=C4=8Dnosti, uv=C3=A1=\r\n" "d=C4=9Bla jim st=C3=A1le na pam=C4=9B=C5=A5 jejich pr=C3=A1va a jejich po=\r\n" "vinnosti; aby =C4=8Diny z=C3=A1konod=C3=A1rn=C3=A9 moci a =C4=8Diny v=C3=\r\n" "=BDkonn=C3=A9 moci mohly b=C3=BDt v ka=C5=BEd=C3=A9 chv=C3=ADli porovn=C3=\r\n" "=A1v=C3=A1ny s =C3=BA=C4=8Delem ka=C5=BEd=C3=A9 politick=C3=A9 instituce=20=\r\n" "a byly v d=C5=AFsledku toho chov=C3=A1ny je=C5=A1t=C4=9B v=C3=ADce v =C3=\r\n" "=BAct=C4=9B; aby po=C5=BEadavky ob=C4=8Dan=C5=AF, kdy=C5=BE se budou nap=\r\n" "=C5=99=C3=AD=C5=A1t=C4=9B zakl=C3=A1dat na jednoduch=C3=0Bdch a nepop=C3=\r\n" "=ADrateln=C3=BDch z=C3=A1sad=C3=A1ch, sm=C4=9B=C5=99ovaly v=C5=0Bedy k za=\r\n" "chov=C3=A1n=C3=AD =C3=BAstavy a ku blahu v=C5=A1ech.", 0 }, }; static void encode_test(const char *qp_input, const char *output, int stream_errno, unsigned int buffer_size) { size_t qp_input_len = strlen(qp_input); struct istream *input_data, *input; const unsigned char *data; size_t i, size; string_t *str = t_str_new(32); int ret = 0; input_data = test_istream_create_data(qp_input, qp_input_len); test_istream_set_max_buffer_size(input_data, buffer_size); test_istream_set_allow_eof(input_data, FALSE); input = i_stream_create_qp_encoder(input_data, 0); for (i = 1; i <= qp_input_len; i++) { test_istream_set_size(input_data, i); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } if (ret == -1 && stream_errno != 0) break; test_assert(ret == 0); } if (ret == 0) { test_istream_set_allow_eof(input_data, TRUE); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } } test_assert(ret == -1); test_assert(input->stream_errno == stream_errno); test_assert(strcmp(str_c(str), output) == 0); i_stream_unref(&input); i_stream_unref(&input_data); } static void test_istream_qp_encoder(void) { unsigned int i, j; for (i = 0; i < N_ELEMENTS(tests); i++) { test_begin(t_strdup_printf("istream qp encoder %u", i+1)); for (j = 1; j < 10; j++) T_BEGIN { encode_test(tests[i].input, tests[i].output, tests[i].stream_errno, j); } T_END; test_end(); } } int main(void) { static void (*test_functions[])(void) = { test_istream_qp_encoder, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/message-part.h0000644000175000017500000000276513123174404015354 00000000000000#ifndef MESSAGE_PART_H #define MESSAGE_PART_H #include "message-size.h" struct message_part_data; /* Note that these flags are used directly by message-parser-serialize, so existing flags can't be changed without breaking backwards compatibility */ enum message_part_flags { MESSAGE_PART_FLAG_MULTIPART = 0x01, MESSAGE_PART_FLAG_MULTIPART_DIGEST = 0x02, MESSAGE_PART_FLAG_MESSAGE_RFC822 = 0x04, /* content-type: text/... */ MESSAGE_PART_FLAG_TEXT = 0x08, MESSAGE_PART_FLAG_UNUSED = 0x10, /* message part header or body contains NULs */ MESSAGE_PART_FLAG_HAS_NULS = 0x20, /* Mime-Version header exists. */ MESSAGE_PART_FLAG_IS_MIME = 0x40 }; struct message_part { struct message_part *parent; struct message_part *next; struct message_part *children; uoff_t physical_pos; /* absolute position from beginning of message */ struct message_size header_size; struct message_size body_size; struct message_part_data *data; /* total number of message_parts under children */ unsigned int children_count; enum message_part_flags flags; void *context; }; /* Return index number for the message part. The indexes are in the same order as they exist in the flat RFC822 message. The root part is 0, its first child is 1 and so on. */ unsigned int message_part_to_idx(const struct message_part *part); /* Find message part by its index number, or return NULL if the index doesn't exist. */ struct message_part * message_part_by_idx(struct message_part *parts, unsigned int idx); #endif dovecot-2.2.33.2/src/lib-mail/istream-nonuls.c0000644000175000017500000000404713123174404015732 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-nonuls.h" struct nonuls_istream { struct istream_private istream; char replace_chr; }; static int i_stream_read_parent(struct istream_private *stream) { ssize_t ret; if (i_stream_get_data_size(stream->parent) > 0) return 1; ret = i_stream_read(stream->parent); if (ret <= 0) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } i_assert(i_stream_get_data_size(stream->parent) != 0); return 1; } static ssize_t i_stream_nonuls_read(struct istream_private *stream) { struct nonuls_istream *nstream = (struct nonuls_istream *)stream; const unsigned char *data, *p; size_t i, size, avail_size; int ret; if ((ret = i_stream_read_parent(stream)) <= 0) return ret; data = i_stream_get_data(stream->parent, &size); if (!i_stream_try_alloc(stream, size, &avail_size)) return -2; if (size > avail_size) size = avail_size; i_assert(size > 0); p = memchr(data, '\0', size); if (p == NULL) { /* no NULs in this block */ memcpy(stream->w_buffer+stream->pos, data, size); } else { i = p-data; memcpy(stream->w_buffer+stream->pos, data, i); for (; i < size; i++) { stream->w_buffer[stream->pos+i] = data[i] == '\0' ? nstream->replace_chr : data[i]; } } stream->pos += size; i_stream_skip(stream->parent, size); return size; } struct istream *i_stream_create_nonuls(struct istream *input, char replace_chr) { struct nonuls_istream *nstream; nstream = i_new(struct nonuls_istream, 1); nstream->istream.max_buffer_size = input->real_stream->max_buffer_size; nstream->istream.stream_size_passthrough = TRUE; nstream->istream.read = i_stream_nonuls_read; nstream->istream.istream.readable_fd = FALSE; nstream->istream.istream.blocking = input->blocking; nstream->istream.istream.seekable = FALSE; nstream->replace_chr = replace_chr; return i_stream_create(&nstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-mail/test-ostream-dot.c0000644000175000017500000000346013165463624016176 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "ostream-dot.h" #include "test-common.h" struct dot_test { const char *input; const char *output; }; static void test_ostream_dot_one(const struct dot_test *test) { struct istream *test_input; struct ostream *output, *test_output; buffer_t *output_data; const unsigned char *data; size_t size; ssize_t ret; test_input = test_istream_create(test->input); output_data = buffer_create_dynamic(pool_datastack_create(), 1024); test_output = o_stream_create_buffer(output_data); output = o_stream_create_dot(test_output, FALSE); while ((ret = i_stream_read(test_input)) > 0 || ret == -2) { data = i_stream_get_data(test_input, &size); ret = o_stream_send(output, data, size); test_assert(ret >= 0); if (ret <= 0) break; i_stream_skip(test_input, ret); } test_assert(test_input->eof); test_assert(o_stream_flush(output) > 0); o_stream_unref(&output); o_stream_unref(&test_output); test_assert(strcmp(str_c(output_data), test->output) == 0); i_stream_unref(&test_input); } static void test_ostream_dot(void) { static struct dot_test tests[] = { { "foo\r\n.\r\n", "foo\r\n..\r\n.\r\n" }, { "foo\n.\n", "foo\r\n..\r\n.\r\n" }, { ".foo\r\n.\r\nfoo\r\n", "..foo\r\n..\r\nfoo\r\n.\r\n" }, { ".foo\n.\nfoo\n", "..foo\r\n..\r\nfoo\r\n.\r\n" }, { "\r\n", "\r\n.\r\n" }, { "\n", "\r\n.\r\n" }, { "", "\r\n.\r\n" }, }; unsigned int i; for (i = 0; i < N_ELEMENTS(tests); i++) { test_begin(t_strdup_printf("dot ostream[%d]:", i)); test_ostream_dot_one(&tests[i]); test_end(); } } int main(void) { static void (*test_functions[])(void) = { test_ostream_dot, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-qp-decoder.c0000644000175000017500000000535413165463624015767 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "qp-decoder.h" #include "test-common.h" struct test_quoted_printable_decode_data { const char *input; const char *output; size_t error_pos; int ret; }; static void test_qp_decoder(void) { #define WHITESPACE10 " \t \t \t" #define WHITESPACE70 WHITESPACE10 WHITESPACE10 WHITESPACE10 WHITESPACE10 WHITESPACE10 WHITESPACE10 WHITESPACE10 static struct test_quoted_printable_decode_data tests[] = { { "foo \r\nbar=\n", "foo\r\nbar", 0, 0 }, { "foo\t=\nbar", "foo\tbar", 0, 0 }, { "foo = \n=01", "foo \001", 0, 0 }, { "foo =\t\r\nbar", "foo bar", 0, 0 }, { "foo =\r\n=01", "foo \001", 0, 0 }, { "foo \nbar=\r\n", "foo\r\nbar", 0, 0 }, { "=0A=0D ", "\n\r", 0, 0 }, { "foo_bar", "foo_bar", 0, 0 }, { "\n\n", "\r\n\r\n", 0, 0 }, { "\r\n\n\n\r\n", "\r\n\r\n\r\n\r\n", 0, 0 }, { "foo=", "foo=", 4, -1 }, { "foo= \t", "foo= \t", 6, -1 }, { "foo= \r", "foo= \r", 6, -1 }, { "foo= \r bar", "foo= \r bar", 6, -1 }, { "foo=A", "foo=A", 5, -1 }, { "foo=Ax", "foo=Ax", 5, -1 }, { "foo=Ax=xy", "foo=Ax=xy", 5, -1 }, /* above 76 whitespaces is invalid and gets truncated (at 77th whitespace because of the current implementation) */ { WHITESPACE70" 7\n", WHITESPACE70" 7\r\n", 0, 0 }, { WHITESPACE70" 8\n", WHITESPACE70" 8\r\n", 77, -1 }, { WHITESPACE70" 9\n", WHITESPACE70" 9\r\n", 78, -1 }, { WHITESPACE70" 0\n", WHITESPACE70" 0\r\n", 79, -1 } }; string_t *str; unsigned int i, j; test_begin("qp-decoder"); str = t_str_new(128); for (i = 0; i < N_ELEMENTS(tests); i++) { const char *input = tests[i].input; struct qp_decoder *qp = qp_decoder_init(str); size_t error_pos; const char *error; int ret; /* try all at once */ ret = qp_decoder_more(qp, (const void *)input, strlen(input), &error_pos, &error); if (qp_decoder_finish(qp, &error) < 0 && ret == 0) { error_pos = strlen(input); ret = -1; } test_assert_idx(ret == tests[i].ret, i); test_assert_idx(ret == 0 || error_pos == tests[i].error_pos, i); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); /* try in small pieces */ str_truncate(str, 0); ret = 0; for (j = 0; input[j] != '\0'; j++) { unsigned char c = input[j]; if (qp_decoder_more(qp, &c, 1, &error_pos, &error) < 0) ret = -1; } if (qp_decoder_finish(qp, &error) < 0) ret = -1; test_assert_idx(ret == tests[i].ret, i); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); qp_decoder_deinit(&qp); str_truncate(str, 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_qp_decoder, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-nonuls.h0000644000175000017500000000030713123174404015732 00000000000000#ifndef ISTREAM_DOT_H #define ISTREAM_DOT_H /* Translate all NUL characters to the specified replace_chr. */ struct istream *i_stream_create_nonuls(struct istream *input, char replace_chr); #endif dovecot-2.2.33.2/src/lib-mail/istream-attachment-extractor.h0000644000175000017500000000443613123174404020564 00000000000000#ifndef ISTREAM_ATTACHMENT_H #define ISTREAM_ATTACHMENT_H struct istream_attachment_header { struct message_part *part; const char *content_type, *content_disposition; }; struct istream_attachment_info { const char *hash; /* offset within input stream where the attachment starts */ uoff_t start_offset; /* original (base64-encoded) size of the attachment */ uoff_t encoded_size; unsigned int base64_blocks_per_line; bool base64_have_crlf; const struct message_part *part; }; struct istream_attachment_settings { /* Minimum size of of a MIME part to be saved separately. */ uoff_t min_size; /* Format to use when calculating attachment's hash. */ struct hash_format *hash_format; /* Set this to TRUE if parent stream can be read from as long as wanted. This is useful when parsing attachments, which the extractor hides from read() output, so they would return a lot of 0. On the other hand if you have a tee-istream, it's not a good idea to let it get to "buffer full" state. */ bool drain_parent_input; /* Returns TRUE if message part is wanted to be stored as separate attachment. If NULL, assume we want the attachment. */ bool (*want_attachment)(const struct istream_attachment_header *hdr, void *context); /* Create a temporary file. */ int (*open_temp_fd)(void *context); /* Create output stream for attachment */ int (*open_attachment_ostream)(struct istream_attachment_info *info, struct ostream **output_r, const char **error_r, void *context); /* Finish output stream. If success==FALSE, *error contains the error and the error shouldn't be replaced (other than maybe enhanced). Otherwise, if close_attachment_ostream() fails and returns -1, it should also set *error. */ int (*close_attachment_ostream)(struct ostream *output, bool success, const char **error, void *context); }; struct istream * i_stream_create_attachment_extractor(struct istream *input, struct istream_attachment_settings *set, void *context) ATTR_NULL(3); /* Returns TRUE if the last read returned 0 only because drain_parent_input=FALSE and we didn't have anything to return, but retrying a read from parent stream could give something the next time. */ bool i_stream_attachment_extractor_can_retry(struct istream *input); #endif dovecot-2.2.33.2/src/lib-mail/mail-html2text.h0000644000175000017500000000110413123174404015621 00000000000000#ifndef MAIL_HTML2TEXT_H #define MAIL_HTML2TEXT_H enum mail_html2text_flags { MAIL_HTML2TEXT_FLAG_SKIP_QUOTED = 0x01 }; struct mail_html2text * mail_html2text_init(enum mail_html2text_flags flags); void mail_html2text_more(struct mail_html2text *ht, const unsigned char *data, size_t size, buffer_t *output); void mail_html2text_deinit(struct mail_html2text **ht); static inline bool mail_html2text_content_type_match(const char *content_type) { return strcasecmp(content_type, "text/html") == 0 || strcasecmp(content_type, "application/xhtml+xml") == 0; } #endif dovecot-2.2.33.2/src/lib-mail/test-rfc2231-parser.c0000644000175000017500000000211413165463624016307 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "test-common.h" static void test_rfc2231_parser(void) { const char *input = "; key4*=us-ascii''foo" "; key*2=ba%" "; key2*0=a" "; key3*0*=us-ascii'en'xyz" "; key*0=\"foo\"" "; key2*1*=b%25" "; key3*1=plop%" "; key*1=baz"; const char *output[] = { "key", "foobazba%", "key2*", "''ab%25", "key3*", "us-ascii'en'xyzplop%25", "key4*", "us-ascii''foo", NULL }; struct rfc822_parser_context parser; const char *const *result; unsigned int i; test_begin("rfc2231 parser"); rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); test_assert(rfc2231_parse(&parser, &result) == 0); for (i = 0; output[i] != NULL && result[i] != NULL; i++) test_assert(strcmp(output[i], result[i]) == 0); test_assert(output[i] == NULL && result[i] == NULL); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_rfc2231_parser, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/rfc2231-parser.h0000644000175000017500000000075013123174404015330 00000000000000#ifndef RFC2231_PARSER_H #define RFC2231_PARSER_H /* Parse all content parameters using rfc822_parse_content_param() and return them as a NULL-terminated [key, value] array. RFC 2231-style continuations are merged to a single key. Returns -1 if some of the input was invalid (but valid key/value pairs are still returned), 0 if everything looked ok. */ int ATTR_NOWARN_UNUSED_RESULT rfc2231_parse(struct rfc822_parser_context *ctx, const char *const **result_r); #endif dovecot-2.2.33.2/src/lib-mail/qp-decoder.h0000644000175000017500000000154613123174404015003 00000000000000#ifndef QP_DECODER_H #define QP_DECODER_H /* Initialize quoted-printable decoder. Write all the decoded output to dest. */ struct qp_decoder *qp_decoder_init(buffer_t *dest); void qp_decoder_deinit(struct qp_decoder **qp); /* Translate more quoted printable data into binary. Returns 0 if input was valid, -1 if there were some decoding errors (which were skipped over). LFs without preceding CR are returned as CRLF (but =0A isn't). */ int qp_decoder_more(struct qp_decoder *qp, const unsigned char *src, size_t src_size, size_t *invalid_src_pos_r, const char **error_r); /* Finish decoding any pending input. Returns the same as qp_decoder_more(). This function also resets the entire decoder state, so the same decoder can be used to decode more data if wanted. */ int qp_decoder_finish(struct qp_decoder *qp, const char **error_r); #endif dovecot-2.2.33.2/src/lib-mail/ostream-dot.c0000644000175000017500000001203313123174404015202 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream-private.h" #include "ostream-dot.h" enum dot_ostream_state { STREAM_STATE_INIT = 0, STREAM_STATE_NONE, STREAM_STATE_CR, STREAM_STATE_CRLF, STREAM_STATE_DONE }; struct dot_ostream { struct ostream_private ostream; enum dot_ostream_state state; bool force_extra_crlf; }; static int o_stream_dot_flush(struct ostream_private *stream) { struct dot_ostream *dstream = (struct dot_ostream *)stream; int ret; if (o_stream_get_buffer_avail_size(stream->parent) < 5) { /* make space for the dot line */ if ((ret = o_stream_flush(stream->parent)) <= 0) { if (ret < 0) o_stream_copy_error_from_parent(stream); return ret; } } if (dstream->state == STREAM_STATE_DONE) ; else if (dstream->state == STREAM_STATE_CRLF && !dstream->force_extra_crlf) { ret = o_stream_send(stream->parent, ".\r\n", 3); i_assert(ret == 3); } else { ret = o_stream_send(stream->parent, "\r\n.\r\n", 5); i_assert(ret == 5); } dstream->state = STREAM_STATE_DONE; if ((ret = o_stream_flush(stream->parent)) < 0) o_stream_copy_error_from_parent(stream); return ret; } static void o_stream_dot_close(struct iostream_private *stream, bool close_parent) { struct dot_ostream *dstream = (struct dot_ostream *)stream; if (close_parent) o_stream_close(dstream->ostream.parent); } static ssize_t o_stream_dot_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct dot_ostream *dstream = (struct dot_ostream *)stream; ARRAY(struct const_iovec) iov_arr; const struct const_iovec *iov_new; size_t max_bytes, sent, added; unsigned int count, i; ssize_t ret; i_assert(dstream->state != STREAM_STATE_DONE); if ((ret=o_stream_flush(stream->parent)) <= 0) { /* error / we still couldn't flush existing data to parent stream. */ o_stream_copy_error_from_parent(stream); return ret; } /* check for dots */ t_array_init(&iov_arr, iov_count + 32); max_bytes = o_stream_get_buffer_avail_size(stream->parent); i_assert(max_bytes > 0); /* FIXME: not supported currently */ sent = added = 0; for (i = 0; i < iov_count && max_bytes > 0; i++) { size_t size = iov[i].iov_len, chunk; const char *data = iov[i].iov_base, *p, *pend; struct const_iovec iovn; p = data; pend = CONST_PTR_OFFSET(data, size); for (; p < pend && (size_t)(p-data) < (max_bytes-2); p++) { char add = 0; switch (dstream->state) { /* none */ case STREAM_STATE_NONE: switch (*p) { case '\n': dstream->state = STREAM_STATE_CRLF; /* add missing CR */ add = '\r'; break; case '\r': dstream->state = STREAM_STATE_CR; break; } break; /* got CR */ case STREAM_STATE_CR: switch (*p) { case '\r': break; case '\n': dstream->state = STREAM_STATE_CRLF; break; default: dstream->state = STREAM_STATE_NONE; break; } break; /* got CRLF, or the first line */ case STREAM_STATE_INIT: case STREAM_STATE_CRLF: switch (*p) { case '\r': dstream->state = STREAM_STATE_CR; break; case '\n': dstream->state = STREAM_STATE_CRLF; /* add missing CR */ add = '\r'; break; case '.': /* add dot */ add = '.'; /* fall through */ default: dstream->state = STREAM_STATE_NONE; break; } break; case STREAM_STATE_DONE: i_unreached(); } if (add != 0) { chunk = (size_t)(p - data); if (chunk > 0) { /* forward chunk to new iovec */ iovn.iov_base = data; iovn.iov_len = chunk; array_append(&iov_arr, &iovn, 1); data = p; max_bytes -= chunk; sent += chunk; } /* insert byte (substitute one with pair) */ data++; iovn.iov_base = (add == '\r' ? "\r\n" : ".."); iovn.iov_len = 2; array_append(&iov_arr, &iovn, 1); max_bytes -= 2; added++; sent++; } } if (max_bytes == 0) break; chunk = ((size_t)(p-data) >= (max_bytes-2) ? max_bytes - 2 : (size_t)(p - data)); if (chunk > 0) { iovn.iov_base = data; iovn.iov_len = chunk; array_append(&iov_arr, &iovn, 1); max_bytes -= chunk; sent += chunk; } } /* send */ iov_new = array_get(&iov_arr, &count); if (count == 0) { ret = 0; } else if ((ret=o_stream_sendv(stream->parent, iov_new, count)) <= 0) { i_assert(ret < 0); o_stream_copy_error_from_parent(stream); return -1; } /* all must be sent */ i_assert((size_t)ret == sent + added); stream->ostream.offset += sent; return sent; } struct ostream * o_stream_create_dot(struct ostream *output, bool force_extra_crlf) { struct dot_ostream *dstream; dstream = i_new(struct dot_ostream, 1); dstream->ostream.sendv = o_stream_dot_sendv; dstream->ostream.iostream.close = o_stream_dot_close; dstream->ostream.flush = o_stream_dot_flush; dstream->ostream.max_buffer_size = output->real_stream->max_buffer_size; dstream->force_extra_crlf = force_extra_crlf; return o_stream_create(&dstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib-mail/message-address.h0000644000175000017500000000246513165463624016043 00000000000000#ifndef MESSAGE_ADDRESS_H #define MESSAGE_ADDRESS_H /* group: ... ; will be stored like: {name = NULL, NULL, "group", NULL}, ..., {NULL, NULL, NULL, NULL} */ struct message_address { struct message_address *next; /* display-name */ const char *name; /* route string contains the @ prefix */ const char *route; /* local-part */ const char *mailbox; const char *domain; /* there were errors when parsing this address */ bool invalid_syntax; }; /* Parse message addresses from given data. If fill_missing is TRUE, missing mailbox and domain are set to MISSING_MAILBOX and MISSING_DOMAIN strings. Otherwise they're set to "". Note that giving an empty string will return NULL since there are no addresses. */ struct message_address * message_address_parse(pool_t pool, const unsigned char *data, size_t size, unsigned int max_addresses, bool fill_missing); void message_address_write(string_t *str, const struct message_address *addr); /* Returns TRUE if header is known to be an address */ bool message_header_is_address(const char *hdr_name); /* Parse address+detail@domain into address@domain and detail using given delimiter string. */ void message_detail_address_parse(const char *delimiter_string, const char *address, const char **username_r, const char **detail_r); #endif dovecot-2.2.33.2/src/lib-mail/test-message-header-encode.c0000644000175000017500000001213113165463624020040 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "buffer.h" #include "str.h" #include "message-header-encode.h" #include "test-common.h" static bool verify_q(const char *str, unsigned int i, bool starts_with_a) { unsigned int line_start = i, char_count = 0; if (strncmp(str+i, "\n\t", 2) == 0) { i += 2; line_start = i - 1; } for (;;) { if (strncmp(str+i, "=?utf-8?q?", 10) != 0) return FALSE; i += 10; if (starts_with_a) { if (str[i] != 'a') return FALSE; starts_with_a = FALSE; i++; } while (strncmp(str+i, "?=", 2) != 0) { if (strncmp(str+i, "=C3=A4", 6) != 0) return FALSE; i += 6; char_count++; } i += 2; if (i - line_start > 76) return FALSE; if (str[i] == '\0') break; if (strncmp(str+i, "\n\t", 2) != 0) return FALSE; i += 2; line_start = i - 1; } return char_count == 40; } static void test_message_header_encode_q(void) { string_t *input = t_str_new(100); string_t *str = t_str_new(512); unsigned int i, j, skip; test_begin("message header encode q"); str_append_c(input, 'a'); for (i = 0; i < 40; i++) str_append(input, "\xC3\xA4"); for (i = 0; i < 80; i++) { for (skip = 0; skip < 2; skip++) { str_truncate(str, 0); for (j = 1; j < i; j++) str_append_c(str, 'X'); if (i != 0) str_append_c(str, ' '); message_header_encode_q(str_data(input) + skip, str_len(input) - skip, str, i == 0 ? 0 : i+1); test_assert(verify_q(str_c(str), i, !skip)); } } test_end(); } static bool verify_b(const char *str, unsigned int i, bool starts_with_a) { unsigned int line_start = i, start, j, char_count = 0; char bufdata[1000]; buffer_t buf; buffer_create_from_data(&buf, bufdata, sizeof(bufdata)); if (strncmp(str+i, "\n\t", 2) == 0) { i += 2; line_start = i - 1; } for (;;) { if (strncmp(str+i, "=?utf-8?b?", 10) != 0) return FALSE; i += 10; start = i; for (; str[i] != '?'; i++) { if (str[i] == '\0') return FALSE; } buffer_set_used_size(&buf, 0); if (base64_decode(str+start, i-start, NULL, &buf) < 0) return FALSE; i++; if (!starts_with_a) j = 0; else { if (bufdata[0] != 'a') return FALSE; starts_with_a = FALSE; j = 1; } for (; j < buf.used; j += 2) { if (bufdata[j] != '\xc3' || bufdata[j+1] != '\xa4') return FALSE; char_count++; } if (j != buf.used) return FALSE; if (str[i++] != '=') return FALSE; if (i - line_start > 76) return FALSE; if (str[i] == '\0') break; if (strncmp(str+i, "\n\t", 2) != 0) return FALSE; i += 2; line_start = i - 1; } return char_count == 40; } static void test_message_header_encode_b(void) { string_t *input = t_str_new(100); string_t *str = t_str_new(512); unsigned int i, j, skip; test_begin("message header encode b"); str_append_c(input, 'a'); for (i = 0; i < 40; i++) str_append(input, "\xC3\xA4"); for (i = 0; i < 80; i++) { for (skip = 0; skip < 2; skip++) { str_truncate(str, 0); for (j = 1; j < i; j++) str_append_c(str, 'X'); if (i != 0) str_append_c(str, ' '); message_header_encode_b(str_data(input) + skip, str_len(input) - skip, str, i == 0 ? 0 : i+1); test_assert(verify_b(str_c(str), i, !skip)); } } test_end(); } static void test_message_header_encode(void) { const char *data[] = { "a b", "a b", "a bc\xC3\xA4""de f", "a =?utf-8?q?bc=C3=A4de?= f", "a \xC3\xA4\xC3\xA4 \xC3\xA4 b", "a =?utf-8?b?w6TDpCDDpA==?= b", "\xC3\xA4 a \xC3\xA4", "=?utf-8?q?=C3=A4_a_=C3=A4?=", "\xC3\xA4\xC3\xA4 a \xC3\xA4", "=?utf-8?b?w6TDpCBhIMOk?=", "=", "=", "?", "?", "a=?", "a=?", "=?", "=?utf-8?q?=3D=3F?=", "=?x", "=?utf-8?q?=3D=3Fx?=", "a\n=?", "a\n\t=?utf-8?q?=3D=3F?=", "a\t=?", "a\t=?utf-8?q?=3D=3F?=", "a =?", "a =?utf-8?q?=3D=3F?=", "foo\001bar", "=?utf-8?q?foo=01bar?=", "\x01\x02\x03\x04\x05\x06\x07\x08", "=?utf-8?b?AQIDBAUGBwg=?=", "a\r\n b", "a\r\n b", "a\r\n\tb", "a\r\n\tb", "a\r\nb", "a\r\n\tb", "a\n b", "a\n b", "a\n b", "a\n b", "a\nb", "a\n\tb", "a\r\n", "a", "a\n", "a", "foo\n \001bar", "foo\n =?utf-8?q?=01bar?=", "foo\001\n bar", "=?utf-8?q?foo=01?=\n bar" }; string_t *str = t_str_new(128); unsigned int i; test_begin("message header encode"); for (i = 0; i < N_ELEMENTS(data); i += 2) { str_truncate(str, 0); message_header_encode(data[i], str); test_assert(strcmp(str_c(str), data[i+1]) == 0); } test_end(); } static void test_message_header_encode_data(void) { string_t *str = t_str_new(128); static unsigned char nuls[10] = { 0, }; test_begin("message header encode data"); message_header_encode_data(nuls, 1, str); test_assert(strcmp(str_c(str), "=?utf-8?q?=00?=") == 0); str_truncate(str, 0); message_header_encode_data(nuls, sizeof(nuls), str); test_assert(strcmp(str_c(str), "=?utf-8?b?AAAAAAAAAAAAAA==?=") == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_header_encode_q, test_message_header_encode_b, test_message_header_encode, test_message_header_encode_data, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-istream-qp-decoder.c0000644000175000017500000000445313165463624017430 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-qp.h" static const struct { const char *input; const char *output; int ret; } tests[] = { { "p=C3=A4=C3=A4t=C3=B6s", "p\xC3\xA4\xC3\xA4t\xC3\xB6s", 0 }, { "p=c3=a4=c3=a4t=c3=b6s= \n", "p\xC3\xA4\xC3\xA4t\xC3\xB6s", 0 }, { "p=c3=a4= \t \n=c3=\r\n=a4t= \r\n=c3=b6s", "p\xC3\xA4\xC3\xA4t\xC3\xB6s", 0 }, { "p=c3=a4\rasdf", "p\xC3\xA4", -1 }, { "p=c", "p", -1 }, { "p=A", "p", -1 }, { "p=Ax", "p", -1 }, { "p=c3=a4=c3=a4t=c3=b6s= ", "p\xC3\xA4\xC3\xA4t\xC3\xB6s", -1 } }; static void decode_test(const char *qp_input, const char *output, bool broken_input, unsigned int buffer_size) { size_t qp_input_len = strlen(qp_input); struct istream *input_data, *input; const unsigned char *data; size_t i, size; string_t *str = t_str_new(32); int ret = 0; input_data = test_istream_create_data(qp_input, qp_input_len); test_istream_set_max_buffer_size(input_data, buffer_size); test_istream_set_allow_eof(input_data, FALSE); input = i_stream_create_qp_decoder(input_data); for (i = 1; i <= qp_input_len; i++) { test_istream_set_size(input_data, i); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } if (ret == -1 && broken_input) break; test_assert(ret == 0); } if (ret == 0) { test_istream_set_allow_eof(input_data, TRUE); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } } test_assert(ret == -1); test_assert((input->stream_errno == 0 && !broken_input) || (input->stream_errno == EINVAL && broken_input)); test_assert(strcmp(str_c(str), output) == 0); i_stream_unref(&input); i_stream_unref(&input_data); } static void test_istream_qp_decoder(void) { unsigned int i, j; for (i = 0; i < N_ELEMENTS(tests); i++) { test_begin(t_strdup_printf("istream qp decoder %u", i+1)); for (j = 1; j < 10; j++) T_BEGIN { decode_test(tests[i].input, tests[i].output, tests[i].ret == -1, j); } T_END; test_end(); } } int main(void) { static void (*test_functions[])(void) = { test_istream_qp_decoder, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/message-binary-part.h0000644000175000017500000000200113123174404016615 00000000000000#ifndef MESSAGE_BINARY_PART_H #define MESSAGE_BINARY_PART_H struct message_binary_part { struct message_binary_part *next; /* Absolute position from beginning of message. This can be used to match the part to struct message_part. */ uoff_t physical_pos; /* Decoded binary header/body size. The binary header size may differ from message_part's, because Content-Transfer-Encoding is changed to "binary". */ uoff_t binary_hdr_size; uoff_t binary_body_size; /* BODYSTRUCTURE for text/ and message/rfc822 parts includes lines count. Decoding may change these numbers. */ unsigned int binary_body_lines_count; }; /* Serialize message binary_part. */ void message_binary_part_serialize(const struct message_binary_part *parts, buffer_t *dest); /* Generate struct message_binary_part from serialized data. Returns 0 if ok, -1 if any problems are detected. */ int message_binary_part_deserialize(pool_t pool, const void *data, size_t size, struct message_binary_part **parts_r); #endif dovecot-2.2.33.2/src/lib-mail/test-qp-encoder.c0000644000175000017500000001040113165463624015766 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "qp-encoder.h" #include "test-common.h" struct test_quoted_printable_encode_data { const void *input; size_t input_len; const char *output; }; static void test_qp_encoder(void) { static struct test_quoted_printable_encode_data tests[] = { { "", 0, "" }, { "a", 1, "a" }, { "a b \r c d", 9, "a b =0D c d" }, { "a b \n c d", 9, "a b \r\n c d" }, { "test wrap at max 20 characters tab\ttoo", 38, "test wrap at max=20=\r\n20 characters tab=09=\r\ntoo" }, { "Invalid UTF-8 sequence in \x99", 27, "Invalid UTF-8 sequ=\r\nence in =99" }, { "keep CRLF\r\non two lines", 23, "keep CRLF\r\non two lines" }, }; string_t *str; unsigned int i, j; test_begin("qp-encoder"); str = t_str_new(128); for (i = 0; i < N_ELEMENTS(tests); i++) { const unsigned char *input = tests[i].input; struct qp_encoder *qp = qp_encoder_init(str, 20, 0); /* try all at once */ qp_encoder_more(qp, input, tests[i].input_len); qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); /* try in small pieces */ str_truncate(str, 0); for (j = 0; j < tests[i].input_len; j++) { unsigned char c = input[j]; qp_encoder_more(qp, &c, 1); } qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); qp_encoder_deinit(&qp); str_truncate(str, 0); } test_end(); } static void test_qp_encoder_binary(void) { static struct test_quoted_printable_encode_data tests[] = { { "\0nil\0delimited\0string\0", 22, "=00nil=00delimited=\r\n=00string=00" }, { "\xef\x4e\xc5\xe0\x31\x66\xd7\xef\xae\x12\x7d\x45\x1e\x05\xc7\x2a", 16, "=EFN=C5=E01f=D7=EF=\r\n=AE=12}E=1E=05=C7*" }, }; string_t *str; unsigned int i, j; test_begin("qp-encoder (binary safe)"); str = t_str_new(128); for (i = 0; i < N_ELEMENTS(tests); i++) { const unsigned char *input = tests[i].input; struct qp_encoder *qp = qp_encoder_init(str, 20, QP_ENCODER_FLAG_BINARY_DATA); /* try all at once */ qp_encoder_more(qp, input, tests[i].input_len); qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); /* try in small pieces */ str_truncate(str, 0); for (j = 0; j < tests[i].input_len; j++) { unsigned char c = input[j]; qp_encoder_more(qp, &c, 1); } qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); qp_encoder_deinit(&qp); str_truncate(str, 0); } test_end(); } static void test_qp_encoder_header(void) { static struct test_quoted_printable_encode_data tests[] = { { "simple", 6, "=?utf-8?Q?simple?=" }, { "J'esuis de paris caf\xc3\xa9", 22, "=?utf-8?Q?J'esuis_de_paris_caf=C3=A9?=" }, { "hello_world", 11, "=?utf-8?Q?hello=5Fworld?=" }, { "make sure this wraps and that the actual lines are not longer than maximum length including preamble", 100, "=?utf-8?Q?make_sure_this_wraps_and_that_the_actual_lines_are_not_longer_t?=\r\n" " =?utf-8?Q?han_maximum_length_including_preamble?=" }, }; string_t *str; unsigned int i, j; test_begin("qp-encoder (header format)"); str = t_str_new(128); for (i = 0; i < N_ELEMENTS(tests); i++) { const unsigned char *input = tests[i].input; struct qp_encoder *qp = qp_encoder_init(str, 75, QP_ENCODER_FLAG_HEADER_FORMAT); /* try all at once */ qp_encoder_more(qp, input, tests[i].input_len); qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); /* try in small pieces */ str_truncate(str, 0); for (j = 0; j < tests[i].input_len; j++) { unsigned char c = input[j]; qp_encoder_more(qp, &c, 1); } qp_encoder_finish(qp); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); qp_encoder_deinit(&qp); str_truncate(str, 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_qp_encoder, test_qp_encoder_binary, test_qp_encoder_header, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/ostream-dot.h0000644000175000017500000000121013123174404015202 00000000000000#ifndef OSTREAM_DOT_H #define OSTREAM_DOT_H /* Create output stream for writing SMTP DATA style message: Add additional "." to lines that start with ".". Write a line that contains only "." upon o_stream_flush(). (This is also called at close(), but it shouldn't be relied on since it could fail due to output buffer being full.) If output ends with CRLF, force_extra_crlf controls whether additional CRLF is written before the "." line. This parameter should match i_stream_create_dot()'s send_last_lf parameter (reversed). */ struct ostream *o_stream_create_dot(struct ostream *output, bool force_extra_crlf); #endif dovecot-2.2.33.2/src/lib-mail/mail-user-hash.c0000644000175000017500000000270713165463624015605 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "md5.h" #include "str.h" #include "var-expand.h" #include "mail-user-hash.h" unsigned int mail_user_hash(const char *username, const char *format) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; unsigned char md5[MD5_RESULTLEN]; unsigned int i, hash = 0; if (strcmp(format, "%u") == 0) { /* fast path */ md5_get_digest(username, strlen(username), md5); } else if (strcmp(format, "%Lu") == 0) { /* almost as fast path */ T_BEGIN { md5_get_digest(t_str_lcase(username), strlen(username), md5); } T_END; } else T_BEGIN { string_t *str = t_str_new(128); tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = username; tab[1].value = t_strcut(username, '@'); tab[2].value = strchr(username, '@'); if (tab[2].value != NULL) tab[2].value++; var_expand(str, format, tab); md5_get_digest(str_data(str), str_len(str), md5); } T_END; for (i = 0; i < sizeof(hash); i++) hash = (hash << CHAR_BIT) | md5[i]; if (hash == 0) { /* Make sure we don't return the hash as 0, since it's often treated in a special way that won't work well. For example trying to insert it into a hash table will assert-crash. */ hash = 1; } return hash; } dovecot-2.2.33.2/src/lib-mail/qp-encoder.h0000644000175000017500000000203413123174404015006 00000000000000#ifndef QP_ENCODER_H #define QP_ENCODER_H 1 enum qp_encoder_flag { /* encode spaces as underscores, encode crlfs, adds =?utf-8?q?..?= encapsulation */ QP_ENCODER_FLAG_HEADER_FORMAT = 0x1, /* treat input as true binary, no lf => crlf conversion, only CRLF is preserved */ QP_ENCODER_FLAG_BINARY_DATA = 0x2, }; /* Initialize quoted-printable encoder. Write all the encoded output to dest. */ struct qp_encoder *qp_encoder_init(string_t *dest, unsigned int max_length, enum qp_encoder_flag flags); void qp_encoder_deinit(struct qp_encoder **qp); /* Translate more (binary) data into quoted printable. If QP_ENCODER_FLAG_BINARY_DATA is not set, text is assumed to be in UTF-8 (but not enforced). No other character sets are supported. */ void qp_encoder_more(struct qp_encoder *qp, const void *src, size_t src_size); /* Finish encoding any pending input. This function also resets the entire encoder state, so the same encoder can be used to encode more data if wanted. */ void qp_encoder_finish(struct qp_encoder *qp); #endif dovecot-2.2.33.2/src/lib-mail/message-header-parser.c0000644000175000017500000002465013165463624017133 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "message-size.h" #include "message-header-parser.h" struct message_header_parser_ctx { struct message_header_line line; struct istream *input; struct message_size *hdr_size; string_t *name; buffer_t *value_buf; enum message_header_parser_flags flags; unsigned int skip_line:1; unsigned int has_nuls:1; }; struct message_header_parser_ctx * message_parse_header_init(struct istream *input, struct message_size *hdr_size, enum message_header_parser_flags flags) { struct message_header_parser_ctx *ctx; ctx = i_new(struct message_header_parser_ctx, 1); ctx->input = input; ctx->hdr_size = hdr_size; ctx->name = str_new(default_pool, 128); ctx->flags = flags; ctx->value_buf = buffer_create_dynamic(default_pool, 4096); i_stream_ref(input); if (hdr_size != NULL) i_zero(hdr_size); return ctx; } void message_parse_header_deinit(struct message_header_parser_ctx **_ctx) { struct message_header_parser_ctx *ctx = *_ctx; i_stream_unref(&ctx->input); buffer_free(&ctx->value_buf); str_free(&ctx->name); i_free(ctx); *_ctx = NULL; } int message_parse_header_next(struct message_header_parser_ctx *ctx, struct message_header_line **hdr_r) { struct message_header_line *line = &ctx->line; const unsigned char *msg; size_t i, size, startpos, colon_pos, parse_size, skip = 0; int ret; bool continued, continues, last_no_newline, last_crlf; bool no_newline, crlf_newline; *hdr_r = NULL; if (line->eoh) return -1; if (line->continues) colon_pos = 0; else { /* new header line */ line->name_offset = ctx->input->v_offset; colon_pos = UINT_MAX; buffer_set_used_size(ctx->value_buf, 0); } no_newline = FALSE; crlf_newline = FALSE; continued = line->continues; continues = FALSE; for (startpos = 0;;) { ret = i_stream_read_data(ctx->input, &msg, &size, startpos+1); if (ret >= 0) { /* we want to know one byte in advance to find out if it's multiline header */ parse_size = size == 0 ? 0 : size-1; } else { parse_size = size; } if (ret <= 0 && startpos == parse_size) { if (ret == -1) { if (startpos > 0) { /* header ended unexpectedly. */ no_newline = TRUE; skip = startpos; break; } /* error / EOF with no bytes */ i_assert(skip == 0); return -1; } if (size > 0 && !ctx->skip_line && !continued && (msg[0] == '\n' || (msg[0] == '\r' && size > 1 && msg[1] == '\n'))) { /* end of headers - this mostly happens just with mbox where headers are read separately from body */ size = 0; if (ctx->hdr_size != NULL) ctx->hdr_size->lines++; if (msg[0] == '\r') { skip = 2; crlf_newline = TRUE; } else { skip = 1; if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; } break; } if (ret == 0 && !ctx->input->eof) { /* stream is nonblocking - need more data */ i_assert(skip == 0); return 0; } i_assert(size > 0); /* a) line is larger than input buffer b) header ended unexpectedly */ if (ret == -2) { /* go back to last LWSP if found. */ size_t min_pos = !continued ? colon_pos : 0; for (i = size-1; i > min_pos; i--) { if (IS_LWSP(msg[i])) { size = i; break; } } if (i == min_pos && (msg[size-1] == '\r' || msg[size-1] == '\n')) { /* we may or may not have a full header, but we don't know until we get the next character. leave out the linefeed and finish the header on the next run. */ size--; if (size > 0 && msg[size-1] == '\r') size--; } /* the buffer really has to be more than 2 to avoid CRLF looping forever */ i_assert(size > 0); continues = TRUE; } no_newline = TRUE; skip = size; break; } /* find ':' */ if (colon_pos == UINT_MAX) { for (i = startpos; i < parse_size; i++) { if (msg[i] > ':') continue; if (msg[i] == ':' && !ctx->skip_line) { colon_pos = i; line->full_value_offset = ctx->input->v_offset + i + 1; break; } if (msg[i] == '\n') { /* end of headers, or error */ break; } if (msg[i] == '\0') ctx->has_nuls = TRUE; } } else { i = startpos; } /* find '\n' */ for (; i < parse_size; i++) { if (msg[i] <= '\n') { if (msg[i] == '\n') break; if (msg[i] == '\0') ctx->has_nuls = TRUE; } } if (i < parse_size && i+1 == size && ret == -2) { /* we don't know if the line continues. */ i++; } else if (i < parse_size) { /* got a line */ if (ctx->skip_line) { /* skipping a line with a huge header name */ if (ctx->hdr_size != NULL) { ctx->hdr_size->lines++; ctx->hdr_size->physical_size += i + 1; ctx->hdr_size->virtual_size += i + 1; } if (i == 0 || msg[i-1] != '\r') { /* missing CR */ if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; } i_stream_skip(ctx->input, i + 1); startpos = 0; ctx->skip_line = FALSE; continue; } continues = i+1 < size && IS_LWSP(msg[i+1]); if (ctx->hdr_size != NULL) ctx->hdr_size->lines++; if (i == 0 || msg[i-1] != '\r') { /* missing CR */ if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; size = i; } else { size = i-1; crlf_newline = TRUE; } skip = i+1; break; } startpos = i; } last_crlf = line->crlf_newline && (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_DROP_CR) == 0; last_no_newline = line->no_newline || (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0; line->continues = continues; line->continued = continued; line->crlf_newline = crlf_newline; line->no_newline = no_newline; if (size == 0 && !continued) { /* end of headers */ line->eoh = TRUE; line->name_len = line->value_len = line->full_value_len = 0; line->name = ""; line->value = line->full_value = NULL; line->middle = NULL; line->middle_len = 0; line->full_value_offset = line->name_offset; line->continues = FALSE; } else if (line->continued) { line->value = msg; line->value_len = size; } else if (colon_pos == UINT_MAX) { /* missing ':', assume the whole line is name */ line->value = NULL; line->value_len = 0; str_truncate(ctx->name, 0); buffer_append(ctx->name, msg, size); line->name = str_c(ctx->name); line->name_len = str_len(ctx->name); line->middle = NULL; line->middle_len = 0; } else { size_t pos; line->value = msg + colon_pos+1; line->value_len = size - colon_pos - 1; if (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP) { /* get value. skip all LWSP after ':'. Note that RFC2822 doesn't say we should, but history behind it.. Exception to this is if the value consists only of LWSP, then skip only the one LWSP after ':'. */ for (pos = 0; pos < line->value_len; pos++) { if (!IS_LWSP(line->value[pos])) break; } if (pos == line->value_len) { /* everything was LWSP */ if (line->value_len > 0 && IS_LWSP(line->value[0])) pos = 1; } } else { pos = line->value_len > 0 && IS_LWSP(line->value[0]) ? 1 : 0; } line->value += pos; line->value_len -= pos; line->full_value_offset += pos; /* get name, skip LWSP before ':' */ while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1])) colon_pos--; str_truncate(ctx->name, 0); /* use buffer_append() so the name won't be truncated if there are NULs. */ buffer_append(ctx->name, msg, colon_pos); str_append_c(ctx->name, '\0'); /* keep middle stored also in ctx->name so it's available with use_full_value */ line->middle = msg + colon_pos; line->middle_len = (size_t)(line->value - line->middle); str_append_n(ctx->name, line->middle, line->middle_len); line->name = str_c(ctx->name); line->name_len = colon_pos; line->middle = str_data(ctx->name) + line->name_len + 1; } if (!line->continued) { /* first header line. make a copy of the line since we can't really trust input stream not to lose it. */ buffer_append(ctx->value_buf, line->value, line->value_len); line->value = line->full_value = ctx->value_buf->data; line->full_value_len = line->value_len; } else if (line->use_full_value) { /* continue saving the full value. */ if (last_no_newline) { /* line is longer than fit into our buffer, so we were forced to break it into multiple message_header_lines */ } else { if (last_crlf) buffer_append_c(ctx->value_buf, '\r'); buffer_append_c(ctx->value_buf, '\n'); } if ((ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) && line->value_len > 0 && line->value[0] != ' ' && IS_LWSP(line->value[0])) { buffer_append_c(ctx->value_buf, ' '); buffer_append(ctx->value_buf, line->value + 1, line->value_len - 1); } else { buffer_append(ctx->value_buf, line->value, line->value_len); } line->full_value = buffer_get_data(ctx->value_buf, &line->full_value_len); } else { /* we didn't want full_value, and this is a continued line. */ line->full_value = NULL; line->full_value_len = 0; } /* always reset it */ line->use_full_value = FALSE; if (ctx->hdr_size != NULL) { ctx->hdr_size->physical_size += skip; ctx->hdr_size->virtual_size += skip; } i_stream_skip(ctx->input, skip); *hdr_r = line; return 1; } bool message_parse_header_has_nuls(const struct message_header_parser_ctx *ctx) { return ctx->has_nuls; } #undef message_parse_header void message_parse_header(struct istream *input, struct message_size *hdr_size, enum message_header_parser_flags flags, message_header_callback_t *callback, void *context) { struct message_header_parser_ctx *hdr_ctx; struct message_header_line *hdr; int ret; hdr_ctx = message_parse_header_init(input, hdr_size, flags); while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) callback(hdr, context); i_assert(ret != 0); message_parse_header_deinit(&hdr_ctx); /* call after the final skipping */ callback(NULL, context); } void message_header_line_write(buffer_t *output, const struct message_header_line *hdr) { if (!hdr->continued) { buffer_append(output, hdr->name, strlen(hdr->name)); buffer_append(output, hdr->middle, hdr->middle_len); } buffer_append(output, hdr->value, hdr->value_len); if (!hdr->no_newline) { if (hdr->crlf_newline) buffer_append_c(output, '\r'); buffer_append_c(output, '\n'); } } dovecot-2.2.33.2/src/lib-mail/test-istream-attachment.c0000644000175000017500000003301113165463624017525 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "sha1.h" #include "hash-format.h" #include "safe-mkstemp.h" #include "istream.h" #include "istream-crlf.h" #include "istream-attachment-extractor.h" #include "istream-attachment-connector.h" #include "ostream.h" #include "test-common.h" #include #include #define BINARY_TEXT_LONG "we have\ra lot \nof \0binary stuff in here\n" \ "b adjig sadjg jasidgjiaehga3wht8a3w8ghxjc dsgad hasdghsd gasd ds" \ "jdsoga sjdga0w3tjhawjgsertniq3n5oqerjqw2r89q23h awhrqh835r8a" #define BINARY_TEXT_LONG_BASE64 \ "d2UgaGF2ZQ1hIGxvdCAKb2YgAGJpbmFyeSBzdHVmZiBpbiBoZXJlCmIgYWRqaWcgc2FkamcgamFz\r\n" \ "aWRnamlhZWhnYTN3aHQ4YTN3OGdoeGpjIGRzZ2FkIGhhc2RnaHNkIGdhc2QgZHNqZHNvZ2Egc2pk\r\n" \ "Z2EwdzN0amhhd2pnc2VydG5pcTNuNW9xZXJqcXcycjg5cTIzaCBhd2hycWg4MzVyOGE=" #define BINARY_TEXT_SHORT "eh" #define BINARY_TEXT_SHORT_BASE64 "ZWg=" static const char mail_input[] = "MIME-Version: 1.0\r\n" "Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n" "\r\n" "mime header\r\n" "\r\n--bound\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Type: text/plain\r\n" "\r\n" BINARY_TEXT_LONG_BASE64 "\r\n--bound\r\n" "Content-Type: text/plain\r\n" "Content-Transfer-Encoding: base64\r\n" "\r\n" BINARY_TEXT_SHORT_BASE64 "\r\n--bound--\r\n"; static const char mail_output[] = "MIME-Version: 1.0\r\n" "Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n" "\r\n" "mime header\r\n" "\r\n--bound\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Type: text/plain\r\n" "\r\n" "\r\n--bound\r\n" "Content-Type: text/plain\r\n" "Content-Transfer-Encoding: base64\r\n" "\r\n" "\r\n--bound--\r\n"; static const char *mail_broken_input_body_prefix = "MIME-Version: 1.0\r\n" "Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n" "\r\n" "--bound\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Type: text/plain\r\n" "\r\n"; static const char *mail_broken_input_bodies[] = { /* broken base64 input */ "Zm9vCg=\n", "Zm9vCg\n", "Zm9vC\n", /* extra whitespace */ "Zm9v\n Zm9v\n", "Zm9v \nZm9v\n", /* mixed LF vs CRLFs */ "Zm9vYmFy\r\nZm9vYmFy\n", "Zm9vYmFy\nZm9vYmFy\r\n", /* line length increases */ "Zm9v\nZm9vYmFy\n", "Zm9v\nZm9vCg==", "Zm9v\nZm9vYgo=" }; static const char *mail_nonbroken_input_bodies[] = { /* suffixes with explicit '=' end */ "Zm9vCg==", "Zm9vCg==\n", "Zm9vCg==\r\n", "Zm9vCg==\nfoo\n", "Zm9vCg==\r\nfoo\n", "Zm9vCg== \t\t\n\n", /* suffixes with shorter line length */ "Zm9vYmFy\nZm9v\n", "Zm9vYmFy\r\nZm9v\r\n", "Zm9vYmFy\nZm9v\nfoo\n", "Zm9vYmFy\r\nZm9v\r\nfoo\n", "Zm9vYmFy\nZm9v\n \t\t\n\n", /* suffixes with empty line */ "Zm9v\n\n", "Zm9v\r\n\r\n", "Zm9v\n\nfoo\n" "Zm9v\r\n\nfoo\n" "Zm9v\r\n\r\nfoo\n" #if 0 /* the whitespace here could be handled as suffixes, but for now they're not: */ "Zm9v ", "Zm9v \n" #endif }; struct attachment { size_t buffer_offset; uoff_t start_offset; uoff_t encoded_size, decoded_size; unsigned int base64_blocks_per_line; }; static buffer_t *attachment_data; static ARRAY(struct attachment) attachments; static int test_open_temp_fd(void *context ATTR_UNUSED) { string_t *str = t_str_new(128); int fd; str_append(str, "/tmp/dovecot-test."); fd = safe_mkstemp(str, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) i_fatal("safe_mkstemp(%s) failed: %m", str_c(str)); i_unlink(str_c(str)); return fd; } static int test_open_attachment_ostream(struct istream_attachment_info *info, struct ostream **output_r, const char **error_r ATTR_UNUSED, void *context ATTR_UNUSED) { struct attachment *a; if (attachment_data == NULL) attachment_data = buffer_create_dynamic(default_pool, 1024); if (!array_is_created(&attachments)) i_array_init(&attachments, 8); a = array_append_space(&attachments); a->buffer_offset = attachment_data->used; a->start_offset = info->start_offset; a->encoded_size = info->encoded_size; a->base64_blocks_per_line = info->base64_blocks_per_line; test_assert(strlen(info->hash) == 160/8*2); /* sha1 size */ *output_r = o_stream_create_buffer(attachment_data); if (o_stream_seek(*output_r, a->buffer_offset) < 0) i_unreached(); return 0; } static int test_open_attachment_ostream_error(struct istream_attachment_info *info ATTR_UNUSED, struct ostream **output_r ATTR_UNUSED, const char **error_r, void *context ATTR_UNUSED) { *error_r = "test open error"; return -1; } static int test_close_attachment_ostream(struct ostream *output, bool success, const char **error_r ATTR_UNUSED, void *context ATTR_UNUSED) { struct attachment *a; i_assert(success); a = array_idx_modifiable(&attachments, array_count(&attachments)-1); a->decoded_size = output->offset - a->buffer_offset; if (o_stream_nfinish(output) < 0) i_unreached(); o_stream_destroy(&output); return 0; } static int test_close_attachment_ostream_error(struct ostream *output, bool success, const char **error, void *context ATTR_UNUSED) { if (success) *error = "test output error"; o_stream_ignore_last_errors(output); o_stream_destroy(&output); return -1; } static struct istream * test_build_original_istream(struct istream *base_input, uoff_t msg_size) { struct istream_attachment_connector *conn; const unsigned char *data = attachment_data->data; const struct attachment *a; struct istream *input; uoff_t data_size = attachment_data->used; const char *error; conn = istream_attachment_connector_begin(base_input, msg_size); array_foreach(&attachments, a) { input = i_stream_create_from_data(data, a->decoded_size); if (istream_attachment_connector_add(conn, input, a->start_offset, a->encoded_size, a->base64_blocks_per_line, TRUE, &error) < 0) i_unreached(); i_stream_unref(&input); i_assert(a->decoded_size <= data_size); data += a->decoded_size; data_size -= a->decoded_size; } i_assert(data_size == 0); return istream_attachment_connector_finish(&conn); } static void get_istream_attachment_settings(struct istream_attachment_settings *set_r) { const char *error; i_zero(set_r); set_r->min_size = 1; set_r->drain_parent_input = TRUE; set_r->open_temp_fd = test_open_temp_fd; set_r->open_attachment_ostream = test_open_attachment_ostream; set_r->close_attachment_ostream= test_close_attachment_ostream; if (hash_format_init("%{sha1}", &set_r->hash_format, &error) < 0) i_unreached(); } static int test_input_stream(struct istream *file_input) { struct istream_attachment_settings set; struct istream *input, *input2; const unsigned char *data; size_t size; struct sha1_ctxt hash; uoff_t msg_size, orig_msg_size; buffer_t *base_buf; unsigned char hash_file[SHA1_RESULTLEN], hash_attached[SHA1_RESULTLEN]; int ret = 0; /* get hash when directly reading input */ input = i_stream_create_crlf(file_input); sha1_init(&hash); while (i_stream_read_data(input, &data, &size, 0) > 0) { sha1_loop(&hash, data, size); i_stream_skip(input, size); } sha1_result(&hash, hash_file); msg_size = orig_msg_size = input->v_offset; i_stream_unref(&input); /* read through attachment extractor */ get_istream_attachment_settings(&set); i_stream_seek(file_input, 0); input = i_stream_create_crlf(file_input); input2 = i_stream_create_attachment_extractor(input, &set, NULL); i_stream_unref(&input); base_buf = buffer_create_dynamic(default_pool, 1024); while (i_stream_read_data(input2, &data, &size, 0) > 0) { buffer_append(base_buf, data, size); i_stream_skip(input2, size); } i_stream_unref(&input2); /* rebuild the original stream and see if the hash matches */ for (unsigned int i = 0; i < 2; i++) { input2 = i_stream_create_from_data(base_buf->data, base_buf->used); input = test_build_original_istream(input2, msg_size); i_stream_unref(&input2); sha1_init(&hash); while (i_stream_read_more(input, &data, &size) > 0) { sha1_loop(&hash, data, size); i_stream_skip(input, size); } test_assert_idx(input->eof && input->stream_errno == 0, i); sha1_result(&hash, hash_attached); i_stream_unref(&input); if (memcmp(hash_file, hash_attached, SHA1_RESULTLEN) != 0) ret = -1; /* try again without knowing the message's size */ msg_size = (uoff_t)-1; } /* try with a wrong message size */ for (int i = 0; i < 2; i++) { input2 = i_stream_create_from_data(base_buf->data, base_buf->used); input = test_build_original_istream(input2, orig_msg_size + (i == 0 ? 1 : -1)); i_stream_unref(&input2); while (i_stream_read_more(input, &data, &size) > 0) i_stream_skip(input, size); test_assert(input->stream_errno == (i == 0 ? EPIPE : EINVAL)); i_stream_unref(&input); } buffer_free(&base_buf); if (attachment_data != NULL) buffer_free(&attachment_data); if (array_is_created(&attachments)) array_free(&attachments); return ret; } static void test_istream_attachment(void) { struct istream_attachment_settings set; struct istream *datainput, *input; const unsigned char *data; size_t i, size; int ret; test_begin("istream attachment"); datainput = test_istream_create_data(mail_input, sizeof(mail_input)); test_istream_set_allow_eof(datainput, FALSE); get_istream_attachment_settings(&set); input = i_stream_create_attachment_extractor(datainput, &set, NULL); for (i = 1; i <= sizeof(mail_input); i++) { test_istream_set_size(datainput, i); while ((ret = i_stream_read(input)) > 0) ; test_assert(ret == 0); } test_istream_set_allow_eof(datainput, TRUE); while ((ret = i_stream_read(input)) > 0) ; test_assert(ret == -1); data = i_stream_get_data(input, &size); test_assert(size == sizeof(mail_output) && memcmp(data, mail_output, size) == 0); data = attachment_data->data; test_assert(attachment_data->used == sizeof(BINARY_TEXT_LONG)-1 + strlen(BINARY_TEXT_SHORT)); test_assert(memcmp(data, BINARY_TEXT_LONG, sizeof(BINARY_TEXT_LONG)-1) == 0); test_assert(memcmp(data + sizeof(BINARY_TEXT_LONG)-1, BINARY_TEXT_SHORT, strlen(BINARY_TEXT_SHORT)) == 0); i_stream_unref(&input); i_stream_unref(&datainput); if (attachment_data != NULL) buffer_free(&attachment_data); if (array_is_created(&attachments)) array_free(&attachments); test_end(); } static bool test_istream_attachment_extractor_one(const char *body, int err_type) { const size_t prefix_len = strlen(mail_broken_input_body_prefix); struct istream_attachment_settings set; struct istream *datainput, *input; char *mail_text; const unsigned char *data; size_t size; int ret; bool unchanged; mail_text = i_strconcat(mail_broken_input_body_prefix, body, NULL); datainput = test_istream_create_data(mail_text, strlen(mail_text)); get_istream_attachment_settings(&set); if (err_type == 1) set.open_attachment_ostream = test_open_attachment_ostream_error; else if (err_type == 2) set.close_attachment_ostream = test_close_attachment_ostream_error; input = i_stream_create_attachment_extractor(datainput, &set, NULL); while ((ret = i_stream_read(input)) > 0) ; if (err_type != 0) { test_assert(ret == -1 && input->stream_errno == EIO); unchanged = FALSE; goto cleanup; } test_assert(ret == -1 && input->stream_errno == 0); data = i_stream_get_data(input, &size); i_assert(size >= prefix_len && memcmp(data, mail_broken_input_body_prefix, prefix_len) == 0); data += prefix_len; size -= prefix_len; i_assert(attachment_data != NULL); unchanged = attachment_data->used <= strlen(body) && memcmp(attachment_data->data, body, attachment_data->used) == 0 && strlen(body) - attachment_data->used == size && memcmp(data, body + attachment_data->used, size) == 0; cleanup: if (attachment_data != NULL) buffer_free(&attachment_data); if (array_is_created(&attachments)) array_free(&attachments); i_stream_unref(&input); i_stream_unref(&datainput); i_free(mail_text); return unchanged; } static void test_istream_attachment_extractor(void) { unsigned int i; test_begin("istream attachment extractor"); for (i = 0; i < N_ELEMENTS(mail_broken_input_bodies); i++) test_assert(test_istream_attachment_extractor_one(mail_broken_input_bodies[i], 0)); for (i = 0; i < N_ELEMENTS(mail_nonbroken_input_bodies); i++) test_assert(!test_istream_attachment_extractor_one(mail_nonbroken_input_bodies[i], 0)); test_end(); } static void test_istream_attachment_extractor_error(void) { unsigned int i; test_begin("istream attachment extractor error"); for (int err_type = 1; err_type <= 2; err_type++) { for (i = 0; i < N_ELEMENTS(mail_broken_input_bodies); i++) test_istream_attachment_extractor_one(mail_broken_input_bodies[i], err_type); for (i = 0; i < N_ELEMENTS(mail_nonbroken_input_bodies); i++) test_istream_attachment_extractor_one(mail_nonbroken_input_bodies[i], err_type); } test_end(); } static void test_istream_attachment_connector(void) { struct istream *input; test_begin("istream attachment connector"); input = i_stream_create_from_data(mail_input, sizeof(mail_input)); test_assert(test_input_stream(input) == 0); i_stream_unref(&input); test_end(); } static int test_input_file(const char *path) { struct istream *file_input; int ret = 0; lib_init(); file_input = i_stream_create_file(path, 64); if (test_input_stream(file_input) < 0) { fprintf(stderr, "istream-attachment-extractor: mismatch on file %s\n", path); ret = -1; } i_stream_unref(&file_input); lib_deinit(); return ret; } int main(int argc, char *argv[]) { static void (*test_functions[])(void) = { test_istream_attachment, test_istream_attachment_extractor, test_istream_attachment_extractor_error, test_istream_attachment_connector, NULL }; if (argc > 1) return test_input_file(argv[1]) < 0 ? 1 : 0; else return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-message-header-hash.c0000644000175000017500000000612313165463624017532 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "md5.h" #include "message-header-hash.h" static const char test_input_with_nuls[] = { "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" "\x20!?x??yz\x7f\x80\x90\xff-plop\xff" }; static const struct { const char *input; unsigned int version; const char *output; } tests[] = { { "???hi???", 1, "???hi???" }, { test_input_with_nuls, 2, "?\t\n? !?x?yz?-plop?" }, { "?hi?", 2, "?hi?" }, { "\x01hi\x01", 2, "?hi?" }, { "???hi???", 2, "?hi?" }, { "\x01?hi??\x01", 2, "?hi?" }, { "?\t?hi?\t?", 2, "?\t?hi?\t?" }, { "\n\nhi\n\n", 2, "\n\nhi\n\n" }, { "", 2, "" }, { " ", 2, " " }, { " ", 2, " " }, { "? ? ? hi \x01\x02 \x03 ", 2, "? ? ? hi ? ? " }, { test_input_with_nuls, 3, "?\t\n?!?x?yz?-plop?" }, { "\n\nhi\n\n", 3, "\n\nhi\n\n" }, { "", 3, "" }, { " ", 3, "" }, { " ", 3, "" }, { " ? ", 3, "?" }, { "? ? ? hi \x01\x02 \x03 ", 3, "???hi??" }, { " \t \t", 3, "\t\t" }, { test_input_with_nuls, 4, "?\n?!?x?yz?-plop?" }, { "\n\nhi\n\n", 4, "\n\nhi\n\n" }, { "", 4, "" }, { " ", 4, "" }, { " \t \t", 4, "" }, { "foo\t\t", 4, "foo" }, }; static void test_message_header_hash_more(void) { struct message_header_hash_context ctx; struct md5_context md5_ctx; unsigned char md5_input[MD5_RESULTLEN], md5_output[MD5_RESULTLEN]; test_begin("message_header_hash_more"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { size_t input_len = tests[i].input == test_input_with_nuls ? sizeof(test_input_with_nuls)-1 : strlen(tests[i].input); md5_init(&md5_ctx); i_zero(&ctx); message_header_hash_more(&ctx, &hash_method_md5, &md5_ctx, tests[i].version, (const unsigned char *)tests[i].input, input_len); md5_final(&md5_ctx, md5_input); md5_init(&md5_ctx); md5_update(&md5_ctx, tests[i].output, strlen(tests[i].output)); md5_final(&md5_ctx, md5_output); test_assert_idx(memcmp(md5_input, md5_output, MD5_RESULTLEN) == 0, i); /* single byte at a time */ md5_init(&md5_ctx); i_zero(&ctx); for (unsigned int j = 0; j < input_len; j++) { unsigned char chr = tests[i].input[j]; message_header_hash_more(&ctx, &hash_method_md5, &md5_ctx, tests[i].version, &chr, 1); } md5_final(&md5_ctx, md5_input); test_assert_idx(memcmp(md5_input, md5_output, MD5_RESULTLEN) == 0, i); /* random number of chars at a time */ md5_init(&md5_ctx); i_zero(&ctx); for (unsigned int j = 0; j < input_len; ) { const unsigned char *input_part = (const unsigned char *)tests[i].input + j; unsigned int len = rand() % (input_len - j) + 1; message_header_hash_more(&ctx, &hash_method_md5, &md5_ctx, tests[i].version, input_part, len); j += len; } md5_final(&md5_ctx, md5_input); test_assert_idx(memcmp(md5_input, md5_output, MD5_RESULTLEN) == 0, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_header_hash_more, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-message-snippet.c0000644000175000017500000000546013165463624017046 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "message-snippet.h" #include "test-common.h" static struct { const char *input; unsigned int max_snippet_chars; const char *output; } tests[] = { { "Content-Type: text/plain\n" "\n" "1234567890 234567890", 12, "1234567890 2" }, { "Content-Type: text/plain\n" "\n" "line1\n>quote2\nline2\n", 100, "line1 line2" }, { "Content-Type: text/plain\n" "\n" "line1\n>quote2\n> quote3\n > line4\n\n \t\t \nline5\n \t ", 100, "line1 > line4 line5" }, { "Content-Type: text/plain; charset=utf-8\n" "\n" "hyv\xC3\xA4\xC3\xA4 p\xC3\xA4iv\xC3\xA4\xC3\xA4", 11, "hyv\xC3\xA4\xC3\xA4 p\xC3\xA4iv\xC3\xA4" }, { "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: quoted-printable\n" "\n" "hyv=C3=A4=C3=A4 p=C3=A4iv=C3=A4=C3=A4", 11, "hyv\xC3\xA4\xC3\xA4 p\xC3\xA4iv\xC3\xA4" }, { "Content-Transfer-Encoding: quoted-printable\n" "Content-Type: text/html;\n" " charset=utf-8\n" "\n" "Hi,

How =\n" "is it going?
quoted text is ignored
\n" "> -foo\n" "

=\n", 100, "Hi, How is it going? > -foo" }, { "Content-Transfer-Encoding: quoted-printable\n" "Content-Type: application/xhtml+xml;\n" " charset=utf-8\n" "\n" "Hi,

How =\n" "is it going?
quoted text is ignored
\n" "> -foo\n" "

=\n", 100, "Hi, How is it going? > -foo" }, }; static void test_message_snippet(void) { string_t *str = t_str_new(128); struct istream *input; unsigned int i; test_begin("message snippet"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); input = i_stream_create_from_data(tests[i].input, strlen(tests[i].input)); test_assert_idx(message_snippet_generate(input, tests[i].max_snippet_chars, str) == 0, i); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); i_stream_destroy(&input); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_snippet, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/quoted-printable.c0000644000175000017500000000175213123174404016231 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "quoted-printable.h" int quoted_printable_q_decode(const unsigned char *src, size_t src_size, buffer_t *dest) { char hexbuf[3]; size_t src_pos, next; bool errors = FALSE; hexbuf[2] = '\0'; next = 0; for (src_pos = 0; src_pos < src_size; src_pos++) { if (src[src_pos] != '_' && src[src_pos] != '=') continue; buffer_append(dest, src + next, src_pos - next); next = src_pos; if (src[src_pos] == '_') { buffer_append_c(dest, ' '); next++; continue; } if (src_pos+2 >= src_size) break; /* = */ hexbuf[0] = src[src_pos+1]; hexbuf[1] = src[src_pos+2]; if (hex_to_binary(hexbuf, dest) == 0) { src_pos += 2; next = src_pos+1; } else { /* non-hex data, show as-is */ errors = TRUE; next = src_pos; } } buffer_append(dest, src + next, src_size - next); return errors ? -1 : 0; } dovecot-2.2.33.2/src/lib-mail/test-message-parser.c0000644000175000017500000005602713165463624016665 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "message-parser.h" #include "test-common.h" static const char test_msg[] = "Return-Path: \n" "Subject: Hello world\n" "From: Test User \n" "To: Another User \n" "Message-Id: <1.2.3.4@example>\n" "Mime-Version: 1.0\n" "Date: Sun, 23 May 2007 04:58:08 +0300\n" "Content-Type: multipart/signed; micalg=pgp-sha1;\n" " protocol=\"application/pgp-signature\";\n" " boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n" "\n" "--=-GNQXLhuj24Pl1aCkk4/d\n" "Content-Type: text/plain\n" "Content-Transfer-Encoding: quoted-printable\n" "\n" "There was a day=20\n" "a happy=20day\n" "\n" "--=-GNQXLhuj24Pl1aCkk4/d\n" "Content-Type: application/pgp-signature; name=signature.asc\n" "\n" "-----BEGIN PGP SIGNATURE-----\n" "Version: GnuPG v1.2.4 (GNU/Linux)\n" "\n" "invalid\n" "-----END PGP SIGNATURE-----\n" "\n" "--=-GNQXLhuj24Pl1aCkk4/d--\n" "\n" "\n"; #define TEST_MSG_LEN (sizeof(test_msg)-1) static bool msg_parts_cmp(struct message_part *p1, struct message_part *p2) { while (p1 != NULL || p2 != NULL) { if ((p1 != NULL) != (p2 != NULL)) return FALSE; if ((p1->children != NULL) != (p2->children != NULL)) return FALSE; if (p1->children) { if (!msg_parts_cmp(p1->children, p2->children)) return FALSE; } if (p1->physical_pos != p2->physical_pos || p1->header_size.physical_size != p2->header_size.physical_size || p1->header_size.virtual_size != p2->header_size.virtual_size || p1->header_size.lines != p2->header_size.lines || p1->body_size.physical_size != p2->body_size.physical_size || p1->body_size.virtual_size != p2->body_size.virtual_size || p1->body_size.lines != p2->body_size.lines || p1->flags != p2->flags) return FALSE; p1 = p1->next; p2 = p2->next; } return TRUE; } static void test_parsed_parts(struct istream *input, struct message_part *parts) { struct message_parser_ctx *parser; struct message_block block; struct message_part *parts2; uoff_t i, input_size; const char *error; int ret; i_stream_seek(input, 0); if (i_stream_get_size(input, TRUE, &input_size) < 0) i_unreached(); parser = message_parser_init_from_parts(parts, input, 0, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); for (i = 1; i <= input_size*2+1; i++) { test_istream_set_size(input, i/2); if (i > TEST_MSG_LEN*2) test_istream_set_allow_eof(input, TRUE); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; } test_assert(message_parser_deinit_from_parts(&parser, &parts2, &error) == 0); test_assert(msg_parts_cmp(parts, parts2)); } static void test_message_parser_small_blocks(void) { struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *parts2; struct message_block block; unsigned int i, end_of_headers_idx; string_t *output; pool_t pool; int ret; test_begin("message parser in small blocks"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(test_msg); output = t_str_new(128); /* full parsing */ parser = message_parser_init(pool, input, 0, MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS | MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { if (block.hdr != NULL) message_header_line_write(output, block.hdr); else if (block.size > 0) str_append_n(output, block.data, block.size); } test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(strcmp(test_msg, str_c(output)) == 0); /* parsing in small blocks */ i_stream_seek(input, 0); test_istream_set_allow_eof(input, FALSE); parser = message_parser_init(pool, input, 0, 0); for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { test_istream_set_size(input, i/2); if (i > TEST_MSG_LEN*2) test_istream_set_allow_eof(input, TRUE); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert((ret == 0 && i <= TEST_MSG_LEN*2) || (ret < 0 && i > TEST_MSG_LEN*2)); } test_assert(message_parser_deinit(&parser, &parts2) == 0); test_assert(msg_parts_cmp(parts, parts2)); /* parsing in small blocks from preparsed parts */ i_stream_seek(input, 0); test_istream_set_allow_eof(input, FALSE); end_of_headers_idx = (strstr(test_msg, "\n-----") - test_msg); parser = message_parser_init_from_parts(parts, input, 0, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { test_istream_set_size(input, i/2); if (i > TEST_MSG_LEN*2) test_istream_set_allow_eof(input, TRUE); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert((ret == 0 && i/2 <= end_of_headers_idx) || (ret < 0 && i/2 > end_of_headers_idx)); } test_assert(message_parser_deinit(&parser, &parts2) == 0); test_assert(msg_parts_cmp(parts, parts2)); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_truncated_mime_headers(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\":foo\"\n" "\n" "--:foo\n" "--:foo\n" "Content-Type: text/plain\n" "--:foo\n" "Content-Type: text/plain\r\n" "--:foo\n" "Content-Type: text/html\n" "--:foo--\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *part; struct message_block block; pool_t pool; int ret; test_begin("message parser truncated mime headers"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 48); test_assert(parts->header_size.virtual_size == 48+2); test_assert(parts->body_size.lines == 8); test_assert(parts->body_size.physical_size == 112); test_assert(parts->body_size.virtual_size == 112+7); test_assert(parts->children->physical_pos == 55); test_assert(parts->children->header_size.physical_size == 0); test_assert(parts->children->body_size.physical_size == 0); test_assert(parts->children->body_size.lines == 0); test_assert(parts->children->next->physical_pos == 62); test_assert(parts->children->next->header_size.physical_size == 24); test_assert(parts->children->next->header_size.virtual_size == 24); test_assert(parts->children->next->header_size.lines == 0); test_assert(parts->children->next->next->physical_pos == 94); test_assert(parts->children->next->next->header_size.physical_size == 24); test_assert(parts->children->next->next->header_size.virtual_size == 24); test_assert(parts->children->next->next->header_size.lines == 0); test_assert(parts->children->next->next->next->physical_pos == 127); test_assert(parts->children->next->next->next->header_size.physical_size == 23); test_assert(parts->children->next->next->next->header_size.virtual_size == 23); test_assert(parts->children->next->next->next->header_size.lines == 0); for (part = parts->children; part != NULL; part = part->next) { test_assert(part->body_size.physical_size == 0); test_assert(part->body_size.virtual_size == 0); } test_assert(parts->children->next->next->next->next == NULL); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_truncated_mime_headers2(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"ab\"\n" "\n" "--ab\n" "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--ab\n" "Content-Type: text/plain\n" "\n" "--a\n\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser truncated mime headers 2"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 46); test_assert(parts->header_size.virtual_size == 46+2); test_assert(parts->body_size.lines == 8); test_assert(parts->body_size.physical_size == 86); test_assert(parts->body_size.virtual_size == 86+8); test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->physical_pos == 51); test_assert(parts->children->header_size.lines == 1); test_assert(parts->children->header_size.physical_size == 44); test_assert(parts->children->header_size.virtual_size == 44+1); test_assert(parts->children->body_size.lines == 0); test_assert(parts->children->body_size.physical_size == 0); test_assert(parts->children->children == NULL); test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->next->physical_pos == 101); test_assert(parts->children->next->header_size.lines == 2); test_assert(parts->children->next->header_size.physical_size == 26); test_assert(parts->children->next->header_size.virtual_size == 26+2); test_assert(parts->children->next->body_size.lines == 2); test_assert(parts->children->next->body_size.physical_size == 5); test_assert(parts->children->next->body_size.virtual_size == 5+2); test_assert(parts->children->next->children == NULL); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_truncated_mime_headers3(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"ab\"\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser truncated mime headers 3"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 1); test_assert(parts->header_size.physical_size == 45); test_assert(parts->header_size.virtual_size == 45+1); test_assert(parts->body_size.lines == 0); test_assert(parts->body_size.physical_size == 0); test_assert(parts->children == NULL); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_empty_multipart(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"ab\"\n" "\n" "body\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser empty multipart"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 46); test_assert(parts->header_size.virtual_size == 46+2); test_assert(parts->body_size.lines == 1); test_assert(parts->body_size.physical_size == 5); test_assert(parts->body_size.virtual_size == 5+1); test_assert(parts->children == NULL); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_duplicate_mime_boundary(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--a\n" "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--a\n" "Content-Type: text/plain\n" "\n" "body\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser duplicate mime boundary"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 45); test_assert(parts->header_size.virtual_size == 45+2); test_assert(parts->body_size.lines == 7); test_assert(parts->body_size.physical_size == 84); test_assert(parts->body_size.virtual_size == 84+7); test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->physical_pos == 49); test_assert(parts->children->header_size.lines == 2); test_assert(parts->children->header_size.physical_size == 45); test_assert(parts->children->header_size.virtual_size == 45+2); test_assert(parts->children->body_size.lines == 4); test_assert(parts->children->body_size.physical_size == 35); test_assert(parts->children->body_size.virtual_size == 35+4); test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->children->physical_pos == 98); test_assert(parts->children->children->header_size.lines == 2); test_assert(parts->children->children->header_size.physical_size == 26); test_assert(parts->children->children->header_size.virtual_size == 26+2); test_assert(parts->children->children->body_size.lines == 1); test_assert(parts->children->children->body_size.physical_size == 5); test_assert(parts->children->children->body_size.virtual_size == 5+1); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_garbage_suffix_mime_boundary(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--ab\n" "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--ac\n" "Content-Type: text/plain\n" "\n" "body\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser garbage suffix mime boundary"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 45); test_assert(parts->header_size.virtual_size == 45+2); test_assert(parts->body_size.lines == 7); test_assert(parts->body_size.physical_size == 86); test_assert(parts->body_size.virtual_size == 86+7); test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->physical_pos == 50); test_assert(parts->children->header_size.lines == 2); test_assert(parts->children->header_size.physical_size == 45); test_assert(parts->children->header_size.virtual_size == 45+2); test_assert(parts->children->body_size.lines == 4); test_assert(parts->children->body_size.physical_size == 36); test_assert(parts->children->body_size.virtual_size == 36+4); test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->children->physical_pos == 100); test_assert(parts->children->children->header_size.lines == 2); test_assert(parts->children->children->header_size.physical_size == 26); test_assert(parts->children->children->header_size.virtual_size == 26+2); test_assert(parts->children->children->body_size.lines == 1); test_assert(parts->children->children->body_size.physical_size == 5); test_assert(parts->children->children->body_size.virtual_size == 5+1); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_continuing_mime_boundary(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--a\n" "Content-Type: multipart/mixed; boundary=\"ab\"\n" "\n" "--ab\n" "Content-Type: text/plain\n" "\n" "body\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; int ret; test_begin("message parser continuing mime boundary"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->header_size.lines == 2); test_assert(parts->header_size.physical_size == 45); test_assert(parts->header_size.virtual_size == 45+2); test_assert(parts->body_size.lines == 7); test_assert(parts->body_size.physical_size == 86); test_assert(parts->body_size.virtual_size == 86+7); test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->physical_pos == 49); test_assert(parts->children->header_size.lines == 2); test_assert(parts->children->header_size.physical_size == 46); test_assert(parts->children->header_size.virtual_size == 46+2); test_assert(parts->children->body_size.lines == 4); test_assert(parts->children->body_size.physical_size == 36); test_assert(parts->children->body_size.virtual_size == 36+4); test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(parts->children->children->physical_pos == 100); test_assert(parts->children->children->header_size.lines == 2); test_assert(parts->children->children->header_size.physical_size == 26); test_assert(parts->children->children->header_size.virtual_size == 26+2); test_assert(parts->children->children->body_size.lines == 1); test_assert(parts->children->children->body_size.physical_size == 5); test_assert(parts->children->children->body_size.virtual_size == 5+1); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_continuing_truncated_mime_boundary(void) { static const char input_msg[] = "Content-Type: multipart/mixed; boundary=\"a\"\n" "\n" "--a\n" "Content-Type: multipart/mixed; boundary=\"ab\"\n" "MIME-Version: 1.0\n" "--ab\n" "Content-Type: text/plain\n" "\n" "--ab--\n" "--a--\n\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *part; struct message_block block; pool_t pool; int ret; test_begin("message parser continuing truncated mime boundary"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); message_parser_deinit(&parser, &parts); part = parts; test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(part->header_size.lines == 2); test_assert(part->header_size.physical_size == 45); test_assert(part->header_size.virtual_size == 45+2); test_assert(part->body_size.lines == 9); test_assert(part->body_size.physical_size == 112); test_assert(part->body_size.virtual_size == 112+9); part = parts->children; test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); test_assert(part->physical_pos == 49); test_assert(part->header_size.lines == 1); test_assert(part->header_size.physical_size == 45+17); test_assert(part->header_size.virtual_size == 45+17+1); test_assert(part->body_size.lines == 0); test_assert(part->body_size.physical_size == 0); test_assert(part->children == NULL); /* this will not be a child, since the header was truncated. I guess we could make it, but it would complicate the message-parser even more. */ part = parts->children->next; test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(part->physical_pos == 117); test_assert(part->header_size.lines == 1); test_assert(part->header_size.physical_size == 25); test_assert(part->header_size.virtual_size == 25+1); test_assert(part->body_size.lines == 0); test_assert(part->body_size.physical_size == 0); test_assert(part->children == NULL); part = parts->children->next->next; test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); test_assert(part->header_size.lines == 0); test_assert(part->header_size.physical_size == 0); test_assert(part->body_size.lines == 0); test_assert(part->body_size.physical_size == 0); test_assert(part->children == NULL); test_assert(part->next == NULL); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } static void test_message_parser_no_eoh(void) { static const char input_msg[] = "a:b\n"; struct message_parser_ctx *parser; struct istream *input; struct message_part *parts; struct message_block block; pool_t pool; test_begin("message parser no EOH"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(input_msg); parser = message_parser_init(pool, input, 0, 0); test_assert(message_parser_parse_next_block(parser, &block) > 0 && block.hdr != NULL && strcmp(block.hdr->name, "a") == 0 && block.hdr->value_len == 1 && block.hdr->value[0] == 'b'); test_assert(message_parser_parse_next_block(parser, &block) > 0 && block.hdr == NULL && block.size == 0); test_assert(message_parser_parse_next_block(parser, &block) < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); test_parsed_parts(input, parts); i_stream_unref(&input); pool_unref(&pool); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_parser_small_blocks, test_message_parser_truncated_mime_headers, test_message_parser_truncated_mime_headers2, test_message_parser_truncated_mime_headers3, test_message_parser_empty_multipart, test_message_parser_duplicate_mime_boundary, test_message_parser_garbage_suffix_mime_boundary, test_message_parser_continuing_mime_boundary, test_message_parser_continuing_truncated_mime_boundary, test_message_parser_no_eoh, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/qp-encoder.c0000644000175000017500000000652313123174404015010 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "istream-private.h" #include "qp-encoder.h" #include struct qp_encoder { const char *linebreak; string_t *dest; size_t line_len; size_t max_len; enum qp_encoder_flag flags; bool add_header_preamble:1; bool cr_last:1; }; struct qp_encoder * qp_encoder_init(string_t *dest, unsigned int max_len, enum qp_encoder_flag flags) { i_assert(max_len > 0); if ((flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0 && (flags & QP_ENCODER_FLAG_BINARY_DATA) != 0) i_panic("qp encoder cannot do header format with binary data"); struct qp_encoder *qp = i_new(struct qp_encoder, 1); qp->flags = flags; qp->dest = dest; qp->max_len = max_len; if ((flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) { qp->linebreak = "?=\r\n =?utf-8?Q?"; qp->add_header_preamble = TRUE; } else { qp->linebreak = "=\r\n"; } return qp; } void qp_encoder_deinit(struct qp_encoder **qp) { i_free(*qp); } static inline void qp_encode_or_break(struct qp_encoder *qp, unsigned char c) { bool encode = FALSE; if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) { if (c == ' ') c = '_'; else if (c != '\t' && (c == '?' || c == '_' || c == '=' || c < 33 || c > 126)) encode = TRUE; } else if (c != ' ' && c != '\t' && (c == '=' || c < 33 || c > 126)) { encode = TRUE; } /* Include terminating = as well */ if ((c == ' ' || c == '\t') && qp->line_len + 4 >= qp->max_len) { const char *ptr = strchr(qp->linebreak, '\n'); str_printfa(qp->dest, "=%02X%s", c, qp->linebreak); if (ptr != NULL) qp->line_len = strlen(ptr+1); else qp->line_len = 0; return; } /* Include terminating = as well */ if (qp->line_len + (encode?4:2) >= qp->max_len) { str_append(qp->dest, qp->linebreak); qp->line_len = 0; } if (encode) { str_printfa(qp->dest, "=%02X", c); qp->line_len += 3; } else { str_append_c(qp->dest, c); qp->line_len += 1; } } void qp_encoder_more(struct qp_encoder *qp, const void *_src, size_t src_size) { const unsigned char *src = _src; i_assert(_src != NULL || src_size == 0); if (src_size == 0) return; if (qp->add_header_preamble) { size_t used = qp->dest->used; qp->add_header_preamble = FALSE; str_append(qp->dest, "=?utf-8?Q?"); qp->line_len = qp->dest->used - used; } for(unsigned int i = 0; i < src_size; i++) { unsigned char c = src[i]; /* if input is not binary data and we encounter newline convert it as crlf, or if the last byte was CR, preserve CRLF */ if (c == '\n' && ((qp->flags & (QP_ENCODER_FLAG_BINARY_DATA|QP_ENCODER_FLAG_HEADER_FORMAT)) == 0 || qp->cr_last)) { str_append_c(qp->dest, '\r'); str_append_c(qp->dest, '\n'); /* reset line length here */ qp->line_len = 0; qp->cr_last = FALSE; continue; } else if (qp->cr_last) { qp_encode_or_break(qp, '\r'); qp->cr_last = FALSE; } if (c == '\r') { qp->cr_last = TRUE; } else { qp_encode_or_break(qp, c); } } } void qp_encoder_finish(struct qp_encoder *qp) { if (qp->cr_last) qp_encode_or_break(qp, '\r'); if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0 && !qp->add_header_preamble) str_append(qp->dest, "?="); if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) qp->add_header_preamble = TRUE; qp->line_len = 0; qp->cr_last = FALSE; } dovecot-2.2.33.2/src/lib-mail/message-decoder.c0000644000175000017500000002515613165463624016020 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "str.h" #include "unichar.h" #include "charset-utf8.h" #include "qp-decoder.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "message-parser.h" #include "message-header-decode.h" #include "message-decoder.h" /* base64 takes max 4 bytes per character, q-p takes max 3. */ #define MAX_ENCODING_BUF_SIZE 3 struct message_decoder_context { enum message_decoder_flags flags; normalizer_func_t *normalizer; struct message_part *prev_part; struct message_header_line hdr; buffer_t *buf, *buf2; char *charset_trans_charset; struct charset_translation *charset_trans; char translation_buf[CHARSET_MAX_PENDING_BUF_SIZE]; size_t translation_size; struct qp_decoder *qp; buffer_t *encoding_buf; char *content_type, *content_charset; enum message_cte message_cte; unsigned int binary_input:1; }; static void message_decode_body_init_charset(struct message_decoder_context *ctx, struct message_part *part); struct message_decoder_context * message_decoder_init(normalizer_func_t *normalizer, enum message_decoder_flags flags) { struct message_decoder_context *ctx; ctx = i_new(struct message_decoder_context, 1); ctx->flags = flags; ctx->normalizer = normalizer; ctx->buf = buffer_create_dynamic(default_pool, 8192); ctx->buf2 = buffer_create_dynamic(default_pool, 8192); ctx->encoding_buf = buffer_create_dynamic(default_pool, 128); return ctx; } void message_decoder_deinit(struct message_decoder_context **_ctx) { struct message_decoder_context *ctx = *_ctx; *_ctx = NULL; if (ctx->charset_trans != NULL) charset_to_utf8_end(&ctx->charset_trans); if (ctx->qp != NULL) qp_decoder_deinit(&ctx->qp); buffer_free(&ctx->encoding_buf); buffer_free(&ctx->buf); buffer_free(&ctx->buf2); i_free(ctx->charset_trans_charset); i_free(ctx->content_type); i_free(ctx->content_charset); i_free(ctx); } void message_decoder_set_return_binary(struct message_decoder_context *ctx, bool set) { if (set) ctx->flags |= MESSAGE_DECODER_FLAG_RETURN_BINARY; else ctx->flags &= ~MESSAGE_DECODER_FLAG_RETURN_BINARY; message_decode_body_init_charset(ctx, ctx->prev_part); } enum message_cte message_decoder_parse_cte(struct message_header_line *hdr) { struct rfc822_parser_context parser; enum message_cte message_cte; string_t *value; value = t_str_new(64); rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); (void)rfc822_parse_mime_token(&parser, value); message_cte = MESSAGE_CTE_UNKNOWN; switch (str_len(value)) { case 4: if (i_memcasecmp(str_data(value), "7bit", 4) == 0 || i_memcasecmp(str_data(value), "8bit", 4) == 0) message_cte = MESSAGE_CTE_78BIT; break; case 6: if (i_memcasecmp(str_data(value), "base64", 6) == 0) message_cte = MESSAGE_CTE_BASE64; else if (i_memcasecmp(str_data(value), "binary", 6) == 0) message_cte = MESSAGE_CTE_BINARY; break; case 16: if (i_memcasecmp(str_data(value), "quoted-printable", 16) == 0) message_cte = MESSAGE_CTE_QP; break; } return message_cte; } static void parse_content_type(struct message_decoder_context *ctx, struct message_header_line *hdr) { struct rfc822_parser_context parser; const char *const *results; string_t *str; int ret; if (ctx->content_type != NULL) return; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(64); ret = rfc822_parse_content_type(&parser, str); ctx->content_type = i_strdup(str_c(str)); if (ret < 0) return; rfc2231_parse(&parser, &results); for (; *results != NULL; results += 2) { if (strcasecmp(results[0], "charset") == 0) { ctx->content_charset = i_strdup(results[1]); break; } } } static bool message_decode_header(struct message_decoder_context *ctx, struct message_header_line *hdr, struct message_block *output) { size_t value_len; if (hdr->continues) { hdr->use_full_value = TRUE; return FALSE; } T_BEGIN { if (hdr->name_len == 12 && strcasecmp(hdr->name, "Content-Type") == 0) parse_content_type(ctx, hdr); if (hdr->name_len == 25 && strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0) ctx->message_cte = message_decoder_parse_cte(hdr); } T_END; buffer_set_used_size(ctx->buf, 0); message_header_decode_utf8(hdr->full_value, hdr->full_value_len, ctx->buf, ctx->normalizer); value_len = ctx->buf->used; if (ctx->normalizer != NULL) { (void)ctx->normalizer(hdr->name, hdr->name_len, ctx->buf); buffer_append_c(ctx->buf, '\0'); } else { if (!uni_utf8_get_valid_data((const unsigned char *)hdr->name, hdr->name_len, ctx->buf)) buffer_append_c(ctx->buf, '\0'); } ctx->hdr = *hdr; ctx->hdr.full_value = ctx->buf->data; ctx->hdr.full_value_len = value_len; ctx->hdr.value_len = 0; if (ctx->buf->used != value_len) { ctx->hdr.name = CONST_PTR_OFFSET(ctx->buf->data, ctx->hdr.full_value_len); ctx->hdr.name_len = ctx->buf->used - 1 - value_len; } output->hdr = &ctx->hdr; return TRUE; } static void translation_buf_decode(struct message_decoder_context *ctx, const unsigned char **data, size_t *size) { unsigned char trans_buf[CHARSET_MAX_PENDING_BUF_SIZE+1]; size_t data_wanted, skip; size_t trans_size, orig_size; /* @UNSAFE: move the previously untranslated bytes to trans_buf and see if we have now enough data to get the next character translated */ memcpy(trans_buf, ctx->translation_buf, ctx->translation_size); data_wanted = sizeof(trans_buf) - ctx->translation_size; if (data_wanted > *size) data_wanted = *size; memcpy(trans_buf + ctx->translation_size, *data, data_wanted); orig_size = trans_size = ctx->translation_size + data_wanted; (void)charset_to_utf8(ctx->charset_trans, trans_buf, &trans_size, ctx->buf2); if (trans_size <= ctx->translation_size) { /* need more data to finish the translation. */ i_assert(orig_size < CHARSET_MAX_PENDING_BUF_SIZE); memcpy(ctx->translation_buf, trans_buf, orig_size); ctx->translation_size = orig_size; *data += *size; *size = 0; return; } skip = trans_size - ctx->translation_size; i_assert(*size >= skip); *data += skip; *size -= skip; ctx->translation_size = 0; } static void message_decode_body_init_charset(struct message_decoder_context *ctx, struct message_part *part) { ctx->binary_input = ctx->content_charset == NULL && (ctx->flags & MESSAGE_DECODER_FLAG_RETURN_BINARY) != 0 && (part->flags & (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_MESSAGE_RFC822)) == 0; if (ctx->binary_input) return; if (ctx->charset_trans != NULL && ctx->content_charset != NULL && strcasecmp(ctx->content_charset, ctx->charset_trans_charset) == 0) { /* already have the correct translation selected */ charset_to_utf8_reset(ctx->charset_trans); return; } if (ctx->charset_trans != NULL) charset_to_utf8_end(&ctx->charset_trans); i_free_and_null(ctx->charset_trans_charset); ctx->charset_trans_charset = i_strdup(ctx->content_charset != NULL ? ctx->content_charset : "UTF-8"); if (charset_to_utf8_begin(ctx->charset_trans_charset, ctx->normalizer, &ctx->charset_trans) < 0) ctx->charset_trans = charset_utf8_to_utf8_begin(ctx->normalizer); } static bool message_decode_body(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { const unsigned char *data = NULL; size_t pos = 0, size = 0; const char *error; int ret; if (ctx->encoding_buf->used != 0) buffer_append(ctx->encoding_buf, input->data, input->size); switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: /* just skip this body */ return FALSE; case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: i_assert(ctx->encoding_buf->used == 0); data = input->data; size = pos = input->size; break; case MESSAGE_CTE_QP: { i_assert(ctx->encoding_buf->used == 0); buffer_set_used_size(ctx->buf, 0); if (ctx->qp == NULL) ctx->qp = qp_decoder_init(ctx->buf); (void)qp_decoder_more(ctx->qp, input->data, input->size, &pos, &error); data = ctx->buf->data; size = ctx->buf->used; /* eat away all input. qp-decoder buffers it internally. */ pos = input->size; break; } case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { ret = base64_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { ret = base64_decode(input->data, input->size, &pos, ctx->buf); } if (ret < 0) { /* corrupted base64 data, don't bother with the rest of it */ return FALSE; } if (ret == 0) { /* end of base64 input */ pos = input->size; buffer_set_used_size(ctx->encoding_buf, 0); } data = ctx->buf->data; size = ctx->buf->used; break; } if (ctx->encoding_buf->used != 0) buffer_delete(ctx->encoding_buf, 0, pos); else if (pos != input->size) { buffer_append(ctx->encoding_buf, input->data + pos, input->size - pos); } if (ctx->binary_input) { output->data = data; output->size = size; } else { buffer_set_used_size(ctx->buf2, 0); if (ctx->translation_size != 0) translation_buf_decode(ctx, &data, &size); pos = size; (void)charset_to_utf8(ctx->charset_trans, data, &pos, ctx->buf2); if (pos != size) { ctx->translation_size = size - pos; i_assert(ctx->translation_size <= sizeof(ctx->translation_buf)); memcpy(ctx->translation_buf, data + pos, ctx->translation_size); } output->data = ctx->buf2->data; output->size = ctx->buf2->used; } output->hdr = NULL; return TRUE; } bool message_decoder_decode_next_block(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { if (input->part != ctx->prev_part) { /* MIME part changed. */ message_decoder_decode_reset(ctx); } output->part = input->part; ctx->prev_part = input->part; if (input->hdr != NULL) { output->size = 0; return message_decode_header(ctx, input->hdr, output); } else if (input->size != 0) return message_decode_body(ctx, input, output); else { output->hdr = NULL; output->size = 0; message_decode_body_init_charset(ctx, input->part); return TRUE; } } const char * message_decoder_current_content_type(struct message_decoder_context *ctx) { return ctx->content_type; } void message_decoder_decode_reset(struct message_decoder_context *ctx) { const char *error; if (ctx->qp != NULL) (void)qp_decoder_finish(ctx->qp, &error); i_free_and_null(ctx->content_type); i_free_and_null(ctx->content_charset); ctx->message_cte = MESSAGE_CTE_78BIT; buffer_set_used_size(ctx->encoding_buf, 0); } dovecot-2.2.33.2/src/lib-mail/test-message-id.c0000644000175000017500000000200113165463624015744 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "message-id.h" #include "test-common.h" static void test_message_id_get_next(void) { const char *input[] = { "", ",skipped,", "(c) < (c) foo (c) @ (c) bar (c) > (c)", "<\"foo 2\"@bar>" }; const char *output[] = { "foo@bar", NULL, "foo@bar", "foo2@bar2", NULL, "foo@bar", NULL, "foo 2@bar", NULL }; const char *msgid, *next_msgid; unsigned int i, j; test_begin("message id parser"); for (i = 0, j = 0; i < N_ELEMENTS(input); i++) { msgid = input[i]; while ((next_msgid = message_id_get_next(&msgid)) != NULL) { if (output[j] == NULL) break; test_assert(strcmp(output[j], next_msgid) == 0); j++; } test_assert(output[j++] == NULL && next_msgid == NULL); } test_assert(j == N_ELEMENTS(output)); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_id_get_next, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-qp.h0000644000175000017500000000044513123174404015037 00000000000000#ifndef ISTREAM_QP_H #define ISTREAM_QP_H #include "qp-encoder.h" #define ISTREAM_QP_ENCODER_MAX_LINE_LENGTH 75 struct istream *i_stream_create_qp_decoder(struct istream *input); struct istream *i_stream_create_qp_encoder(struct istream *input, enum qp_encoder_flag flags); #endif dovecot-2.2.33.2/src/lib-mail/test-message-search.c0000644000175000017500000000347513165463624016635 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "unichar.h" #include "message-parser.h" #include "message-search.h" #include "test-common.h" static void test_message_search_more_get_decoded(void) { const char input[] = "p\xC3\xB6\xC3\xB6"; const unsigned char text_plain[] = "text/plain; charset=utf-8"; struct message_search_context *ctx1, *ctx2; struct message_block raw_block, decoded_block; struct message_header_line hdr; struct message_part part; unsigned int i; test_begin("message_search_more_get_decoded()"); ctx1 = message_search_init("p\xC3\xA4\xC3\xA4", NULL, 0); ctx2 = message_search_init("p\xC3\xB6\xC3\xB6", NULL, 0); i_zero(&raw_block); raw_block.part = ∂ /* feed the Content-Type header */ i_zero(&hdr); hdr.name = "Content-Type"; hdr.name_len = strlen(hdr.name); hdr.value = hdr.full_value = text_plain; hdr.value_len = hdr.full_value_len = sizeof(text_plain)-1; raw_block.hdr = &hdr; test_assert(!message_search_more_get_decoded(ctx1, &raw_block, &decoded_block)); test_assert(!message_search_more_decoded(ctx2, &decoded_block)); /* EOH */ raw_block.hdr = NULL; test_assert(!message_search_more_get_decoded(ctx1, &raw_block, &decoded_block)); test_assert(!message_search_more_decoded(ctx2, &decoded_block)); /* body */ raw_block.size = 1; for (i = 0; input[i] != '\0'; i++) { raw_block.data = (const void *)&input[i]; test_assert(!message_search_more_get_decoded(ctx1, &raw_block, &decoded_block)); test_assert(message_search_more_decoded(ctx2, &decoded_block) == (input[i+1] == '\0')); } message_search_deinit(&ctx1); message_search_deinit(&ctx2); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_search_more_get_decoded, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-dot.c0000644000175000017500000001320013165463624015204 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-dot.h" struct dot_istream { struct istream_private istream; char pending[3]; /* max. \r\n */ /* how far in string "\r\n.\r" are we */ unsigned int state; /* state didn't actually start with \r */ unsigned int state_no_cr:1; /* state didn't contain \n either (only at the beginnign of stream) */ unsigned int state_no_lf:1; /* we've seen the "." line, keep returning EOF */ unsigned int dot_eof:1; unsigned int send_last_lf:1; }; static int i_stream_dot_read_some(struct dot_istream *dstream) { struct istream_private *stream = &dstream->istream; size_t size, avail; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else if (ret < 0 && stream->parent->eof) { /* we didn't see "." line */ io_stream_set_error(&stream->iostream, "dot-input stream ends without '.' line"); stream->istream.stream_errno = EPIPE; } return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); } if (!i_stream_try_alloc(stream, size, &avail)) return -2; return 1; } static bool flush_pending(struct dot_istream *dstream, size_t *destp) { struct istream_private *stream = &dstream->istream; size_t dest = *destp; unsigned int i = 0; for (; dstream->pending[i] != '\0' && dest < stream->buffer_size; i++) stream->w_buffer[dest++] = dstream->pending[i]; memmove(dstream->pending, dstream->pending + i, sizeof(dstream->pending) - i); *destp = dest; return dest < stream->buffer_size; } static bool flush_dot_state(struct dot_istream *dstream, size_t *destp) { unsigned int i = 0; if (!dstream->state_no_cr) dstream->pending[i++] = '\r'; if (dstream->state_no_lf) dstream->state_no_lf = FALSE; else if (dstream->state > 1) dstream->pending[i++] = '\n'; dstream->pending[i] = '\0'; if (dstream->state != 4) dstream->state = 0; else { /* \r\n.\r seen, go back to \r state */ dstream->state = 1; } return flush_pending(dstream, destp); } static void i_stream_dot_eof(struct dot_istream *dstream, size_t *destp) { if (dstream->send_last_lf) { dstream->state = 2; (void)flush_dot_state(dstream, destp); } dstream->dot_eof = TRUE; } static ssize_t i_stream_dot_return(struct istream_private *stream, size_t dest, ssize_t ret) { if (dest != stream->pos) { i_assert(dest > stream->pos); ret = dest - stream->pos; stream->pos = dest; } return ret; } static ssize_t i_stream_dot_read(struct istream_private *stream) { /* @UNSAFE */ struct dot_istream *dstream = (struct dot_istream *)stream; const unsigned char *data; size_t i, dest, size, avail; ssize_t ret, ret1; if (dstream->pending[0] != '\0') { if (!i_stream_try_alloc(stream, 1, &avail)) return -2; dest = stream->pos; (void)flush_pending(dstream, &dest); } else { dest = stream->pos; } if (dstream->dot_eof) { stream->istream.eof = TRUE; return i_stream_dot_return(stream, dest, -1); } /* we have to update stream->pos before reading more data */ ret1 = i_stream_dot_return(stream, dest, 0); if ((ret = i_stream_dot_read_some(dstream)) <= 0) { if (stream->istream.stream_errno != 0) return -1; if (ret1 != 0) return ret1; dest = stream->pos; if (ret == -1 && dstream->state != 0) (void)flush_dot_state(dstream, &dest); return i_stream_dot_return(stream, dest, ret); } dest = stream->pos; data = i_stream_get_data(stream->parent, &size); for (i = 0; i < size && dest < stream->buffer_size; i++) { switch (dstream->state) { case 0: break; case 1: /* CR seen */ if (data[i] == '\n') dstream->state++; else { if (!flush_dot_state(dstream, &dest)) goto end; } break; case 2: /* [CR]LF seen */ if (data[i] == '.') dstream->state++; else { if (!flush_dot_state(dstream, &dest)) goto end; } break; case 3: /* [CR]LF. seen */ if (data[i] == '\r') dstream->state++; else if (data[i] == '\n') { /* EOF */ i_stream_dot_eof(dstream, &dest); i++; goto end; } else { /* drop the initial dot */ if (!flush_dot_state(dstream, &dest)) goto end; } break; case 4: /* [CR]LF.CR seen */ if (data[i] == '\n') { /* EOF */ i_stream_dot_eof(dstream, &dest); i++; goto end; } else { /* drop the initial dot */ if (!flush_dot_state(dstream, &dest)) goto end; } } if (dstream->state == 0) { if (data[i] == '\r') { dstream->state = 1; dstream->state_no_cr = FALSE; } else if (data[i] == '\n') { dstream->state = 2; dstream->state_no_cr = TRUE; } else { stream->w_buffer[dest++] = data[i]; } } } end: i_stream_skip(stream->parent, i); ret = i_stream_dot_return(stream, dest, 0) + ret1; if (ret == 0) return i_stream_dot_read(stream); i_assert(ret > 0); return ret; } struct istream *i_stream_create_dot(struct istream *input, bool send_last_lf) { struct dot_istream *dstream; dstream = i_new(struct dot_istream, 1); dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; dstream->istream.read = i_stream_dot_read; dstream->istream.istream.readable_fd = FALSE; dstream->istream.istream.blocking = input->blocking; dstream->istream.istream.seekable = FALSE; dstream->send_last_lf = send_last_lf; dstream->state = 2; dstream->state_no_cr = TRUE; dstream->state_no_lf = TRUE; return i_stream_create(&dstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-mail/istream-dot.h0000644000175000017500000000055613123174404015210 00000000000000#ifndef ISTREAM_DOT_H #define ISTREAM_DOT_H /* Create input stream for reading SMTP DATA style message: Drop initial "." from lines beginning with it. Return EOF on line that contains only ".". If send_last_lf=FALSE, the trailing [CR]LF before "." line isn't returned. */ struct istream *i_stream_create_dot(struct istream *input, bool send_last_lf); #endif dovecot-2.2.33.2/src/lib-mail/mail-user-hash.h0000644000175000017500000000040413165463624015602 00000000000000#ifndef MAIL_USER_HASH #define MAIL_USER_HASH /* Return a hash for username, based on given format. The format can use %n, %d and %u variables. The returned hash is never 0. */ unsigned int mail_user_hash(const char *username, const char *format); #endif dovecot-2.2.33.2/src/lib-mail/test-message-header-decode.c0000644000175000017500000000626213165463624020036 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "charset-utf8.h" #include "message-header-encode.h" #include "message-header-decode.h" #include "test-common.h" bool charset_is_utf8(const char *charset ATTR_UNUSED) { return TRUE; } int charset_to_utf8_begin(const char *charset ATTR_UNUSED, normalizer_func_t *normalizer ATTR_UNUSED, struct charset_translation **t_r ATTR_UNUSED) { return 0; } void charset_to_utf8_end(struct charset_translation **t ATTR_UNUSED) {} enum charset_result charset_to_utf8(struct charset_translation *t ATTR_UNUSED, const unsigned char *src, size_t *src_size, buffer_t *dest) { buffer_append(dest, src, *src_size); return CHARSET_RET_OK; } static void test_message_header_decode(void) { static const char *data[] = { " \t=?utf-8?q?=c3=a4?= =?utf-8?q?=c3=a4?= b \t\r\n ", "\xC3\xA4\xC3\xA4 b \t\r\n ", "a =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 b", "a =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 b", "a =?utf-8?q?=c3=a4?=\t\t\r\n =?utf-8?q?=c3=a4?= b", "a \xC3\xA4\xC3\xA4 b", "a =?utf-8?q?=c3=a4?= x =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 x \xC3\xA4 b", "a =?utf-8?b?w6TDpCDDpA==?= b", "a \xC3\xA4\xC3\xA4 \xC3\xA4 b", "=?utf-8?b?w6Qgw6Q=?=", "\xC3\xA4 \xC3\xA4", }; string_t *dest; unsigned int i; test_begin("message header decode"); dest = t_str_new(256); for (i = 0; i < N_ELEMENTS(data); i += 2) { str_truncate(dest, 0); message_header_decode_utf8((const unsigned char *)data[i], strlen(data[i]), dest, FALSE); test_assert(strcmp(str_c(dest), data[i+1]) == 0); } test_end(); } static void test_message_header_decode_read_overflow(void) { const unsigned char input[] = "=?utf-8?Q?=EF?="; string_t *dest = t_str_new(32); test_begin("message header decode read overflow"); message_header_decode_utf8(input, sizeof(input)-2, dest, NULL); test_end(); } static void test_message_header_decode_encode_random(void) { string_t *encoded, *decoded; unsigned char buf[1024]; unsigned int i, j, buflen; test_begin("message header encode & decode randomly"); encoded = t_str_new(256); decoded = t_str_new(256); for (i = 0; i < 1000; i++) { /* fill only with 7bit data so we don't have to worry about the data being valid UTF-8 */ for (j = 0; j < sizeof(buf); j++) buf[j] = rand() % 128; buflen = rand() % sizeof(buf); str_truncate(encoded, 0); str_truncate(decoded, 0); /* test Q */ message_header_encode_q(buf, buflen, encoded, 0); message_header_decode_utf8(encoded->data, encoded->used, decoded, NULL); test_assert(decoded->used == buflen && memcmp(decoded->data, buf, buflen) == 0); /* test B */ str_truncate(encoded, 0); str_truncate(decoded, 0); message_header_encode_b(buf, buflen, encoded, 0); message_header_decode_utf8(encoded->data, encoded->used, decoded, NULL); test_assert(decoded->used == buflen && memcmp(decoded->data, buf, buflen) == 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_header_decode, test_message_header_decode_read_overflow, test_message_header_decode_encode_random, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-mbox-from.c0000644000175000017500000000521613165463624015647 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "utc-offset.h" #include "mbox-from.h" #include "test-common.h" #include struct test_mbox_from_parse_output { time_t time; int tz_offset; const char *sender; int ret; }; static void test_mbox_from_parse(void) { static const char *input[] = { "user@domain Thu Nov 29 23:33:09 1973 +0200", "user@domain Thu Nov 29 19:33:09 1973 -0200", "\"user name\"@domain Fri Jan 2 10:13:52 UTC 1970 +0000", "user Fri Jan 2 10:14 1970 +0000", "user Fri, 2 Jan 1970 10:14:00 +0000", "user Fri, 2 Jan 1970 10:14 +0000", " Fri Jan 2 10:14 1970 +0000", "user Fri, 2 Foo 1970 10:14:00", "Fri Jan 2 10:14 1970 +0000", "user Fri Jan x 10:14 1970 +0000", "user Fri Jan 2 0:14 1970 +0000", "user Fri Jan 2 xx:14 1970 +0000", "user Fri Jan 2 10: 1970 +0000", "user Fri Jan 2 10:xx 1970 +0000", "user Fri Jan 2 10:xx +0000", }; static struct test_mbox_from_parse_output output[] = { { 123456789, 2*60, "user@domain", 0 }, { 123456789, -2*60, "user@domain", 0 }, { 123232, 0, "\"user name\"@domain", 0 }, { 123240, 0, "user", 0 }, { 123240, 0, "user", 0 }, { 123240, 0, "user", 0 }, { 123240, 0, "", 0 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, { 0, 0, NULL, -1 }, }; unsigned int i, j; size_t len; struct tm *tm; char *sender; bool success; time_t t; int tz, ret; for (j = 0; j < 2; j++) { for (i = 0; i < N_ELEMENTS(input); i++) { len = strlen(input[i]) - j*6; ret = mbox_from_parse((const unsigned char *)input[i], len, &t, &tz, &sender); success = (ret < 0 && output[i].ret < 0) || (ret == output[i].ret && t == output[i].time && tz == output[i].tz_offset && strcmp(sender, output[i].sender) == 0); i_free(sender); test_out(t_strdup_printf("mbox_from_parse(%d,%d)", j, i), success); /* prepare for testing without timezone */ if (output[i].ret == 0) { output[i].time += output[i].tz_offset*60; tm = localtime(&output[i].time); output[i].tz_offset = utc_offset(tm, output[i].time); output[i].time -= output[i].tz_offset*60; } } } } static void test_mbox_from_create(void) { time_t t = 1234567890; int tz; test_begin("mbox_from_create()"); tz = utc_offset(localtime(&t), t) * -60; test_assert(strcmp(mbox_from_create("user", t+tz), "From user Fri Feb 13 23:31:30 2009\n") == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mbox_from_parse, test_mbox_from_create, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-message-decoder.c0000644000175000017500000001145113165463624016766 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "charset-utf8.h" #include "message-parser.h" #include "message-header-decode.h" #include "message-decoder.h" #include "test-common.h" void message_header_decode_utf8(const unsigned char *data, size_t size, buffer_t *dest, normalizer_func_t *normalizer ATTR_UNUSED) { buffer_append(dest, data, size); } static void test_message_decoder(void) { struct message_decoder_context *ctx; struct message_part part; struct message_header_line hdr; struct message_block input, output; test_begin("message decoder"); i_zero(&part); i_zero(&input); memset(&output, 0xff, sizeof(output)); input.part = ∂ ctx = message_decoder_init(NULL, 0); i_zero(&hdr); hdr.name = "Content-Transfer-Encoding"; hdr.name_len = strlen(hdr.name); hdr.full_value = (const void *)"quoted-printable"; hdr.full_value_len = strlen((const char *)hdr.full_value); input.hdr = &hdr; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 0); input.hdr = NULL; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); input.hdr = NULL; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); input.data = (const void *)"foo "; input.size = strlen((const char *)input.data); test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 3); test_assert(memcmp(output.data, "foo", 3) == 0); input.data = (const void *)"bar"; input.size = strlen((const char *)input.data); test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 14); test_assert(memcmp(output.data, " bar", 14) == 0); /* partial text - \xC3\xA4 in quoted-printable. we should get a single UTF-8 letter as result */ input.data = (const void *)"="; input.size = 1; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 0); input.data = (const void *)"C"; input.size = 1; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 0); input.data = (const void *)"3"; input.size = 1; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 0); input.data = (const void *)"=A"; input.size = 2; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 0); input.data = (const void *)"4"; input.size = 1; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(output.size == 2); test_assert(memcmp(output.data, "\xC3\xA4", 2) == 0); message_decoder_deinit(&ctx); test_end(); } static void test_message_decoder_current_content_type(void) { struct message_decoder_context *ctx; struct message_part part, part2, part3; struct message_header_line hdr; struct message_block input, output; test_begin("message_decoder_current_content_type()"); i_zero(&part); part2 = part3 = part; i_zero(&input); memset(&output, 0xff, sizeof(output)); input.part = ∂ ctx = message_decoder_init(NULL, 0); test_assert(message_decoder_current_content_type(ctx) == NULL); /* multipart/mixed */ i_zero(&hdr); hdr.name = "Content-Type"; hdr.name_len = strlen(hdr.name); hdr.full_value = (const void *)"multipart/mixed; boundary=x"; hdr.full_value_len = strlen((const char *)hdr.full_value); input.hdr = &hdr; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); input.hdr = NULL; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(strcmp(message_decoder_current_content_type(ctx), "multipart/mixed") == 0); /* child 1 */ input.part = &part2; hdr.full_value = (const void *)"text/plain"; hdr.full_value_len = strlen((const char *)hdr.full_value); input.hdr = &hdr; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); input.hdr = NULL; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(strcmp(message_decoder_current_content_type(ctx), "text/plain") == 0); /* child 2 */ input.part = &part3; hdr.full_value = (const void *)"application/pdf"; hdr.full_value_len = strlen((const char *)hdr.full_value); input.hdr = &hdr; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); input.hdr = NULL; test_assert(message_decoder_decode_next_block(ctx, &input, &output)); test_assert(strcmp(message_decoder_current_content_type(ctx), "application/pdf") == 0); /* reset */ message_decoder_decode_reset(ctx); test_assert(message_decoder_current_content_type(ctx) == NULL); message_decoder_deinit(&ctx); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_decoder, test_message_decoder_current_content_type, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-attachment-connector.h0000644000175000017500000000243213123174404020535 00000000000000#ifndef ISTREAM_ATTACHMENT_CONNECTOR_H #define ISTREAM_ATTACHMENT_CONNECTOR_H /* Start building a message stream. The base_input contains the message without attachments. The final stream must be exactly msg_size bytes. If the original msg_size isn't known, it can be set to (uoff_t)-1. */ struct istream_attachment_connector * istream_attachment_connector_begin(struct istream *base_input, uoff_t msg_size); /* Add the given input stream as attachment. The attachment starts at the given start_offset in the (original) message. If base64_blocks_per_line is non-zero, the input is base64-encoded with the given settings. The (resulting base64-encoded) input must have exactly encoded_size bytes. Returns 0 if the input was ok, -1 if we've already reached msg_size or attachment offsets/sizes aren't valid. */ int istream_attachment_connector_add(struct istream_attachment_connector *conn, struct istream *decoded_input, uoff_t start_offset, uoff_t encoded_size, unsigned int base64_blocks_per_line, bool base64_have_crlf, const char **error_r); struct istream * istream_attachment_connector_finish(struct istream_attachment_connector **conn); void istream_attachment_connector_abort(struct istream_attachment_connector **conn); #endif dovecot-2.2.33.2/src/lib-mail/test-istream-dot.c0000644000175000017500000001361113165463624016167 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "istream-dot.h" #include "test-common.h" struct dot_test { const char *input; const char *output; const char *parent_input; }; static void test_istream_dot_one(const struct dot_test *test, bool send_last_lf, bool test_bufsize) { struct istream *test_input, *input; const unsigned char *data; size_t size; unsigned int i; size_t outsize, input_len, output_len; string_t *str; uoff_t offset; int ret; test_input = test_istream_create(test->input); input = i_stream_create_dot(test_input, send_last_lf); input_len = strlen(test->input); output_len = strlen(test->output); if (!send_last_lf && (test->input[input_len-1] == '\n' || strstr(test->input, "\n.\n") != NULL || strstr(test->input, "\n.\r\n") != NULL)) { if (output_len > 0 && test->output[output_len-1] == '\n') { output_len--; if (output_len > 0 && test->output[output_len-1] == '\r') output_len--; } } str = t_str_new(256); if (!test_bufsize) { outsize = 1; i = 0; i_stream_set_max_buffer_size(input, outsize); test_istream_set_size(test_input, 1); while ((ret = i_stream_read(input)) != -1) { switch (ret) { case -2: i_stream_set_max_buffer_size(input, ++outsize); offset = test_input->v_offset; /* seek one byte backwards so stream gets reset */ i_stream_seek(test_input, offset - 1); /* go back to original position */ test_istream_set_size(test_input, offset); i_stream_skip(test_input, 1); /* and finally allow reading one more byte */ test_istream_set_size(test_input, offset + 1); break; case 0: test_istream_set_size(test_input, ++i); break; default: test_assert(ret > 0); data = i_stream_get_data(input, &size); str_append_n(str, data, size); i_stream_skip(input, size); } } test_istream_set_size(test_input, input_len); (void)i_stream_read(test_input); } else { test_istream_set_size(test_input, input_len); size = 0; for (i = 1; i < output_len; i++) { i_stream_set_max_buffer_size(input, i); test_assert(i_stream_read(input) == 1); test_assert(i_stream_read(input) == -2); data = i_stream_get_data(input, &size); test_assert(memcmp(data, test->output, size) == 0); } i_stream_set_max_buffer_size(input, i+2); if (size < output_len) test_assert(i_stream_read(input) == 1); test_assert(i_stream_read(input) == -1); data = i_stream_get_data(input, &size); if (size > 0) str_append_n(str, data, size); } test_assert(input->stream_errno == 0); test_assert(str_len(str) == output_len); test_assert(memcmp(str_data(str), test->output, output_len) == 0); /* read the data after the '.' line and verify it's still there */ i_stream_set_max_buffer_size(test_input, (size_t)-1); (void)i_stream_read(test_input); data = i_stream_get_data(test_input, &size); test_assert(size == strlen(test->parent_input)); if (size > 0) test_assert(memcmp(data, test->parent_input, size) == 0); i_stream_unref(&test_input); i_stream_unref(&input); } static void test_istream_dot_error(const char *input_str, bool test_bufsize) { struct istream *test_input, *input; unsigned int i; size_t outsize, input_len; uoff_t offset; int ret; test_input = test_istream_create(input_str); input = i_stream_create_dot(test_input, FALSE); input_len = strlen(input_str); if (!test_bufsize) { outsize = 1; i = 0; i_stream_set_max_buffer_size(input, outsize); test_istream_set_size(test_input, 1); while ((ret = i_stream_read(input)) != -1) { switch (ret) { case -2: i_stream_set_max_buffer_size(input, ++outsize); offset = test_input->v_offset; /* seek one byte backwards so stream gets reset */ i_stream_seek(test_input, offset - 1); /* go back to original position */ test_istream_set_size(test_input, offset); i_stream_skip(test_input, 1); /* and finally allow reading one more byte */ test_istream_set_size(test_input, offset + 1); break; case 0: test_istream_set_size(test_input, ++i); break; default: test_assert(ret > 0); } } test_istream_set_size(test_input, input_len); (void)i_stream_read(test_input); } else { test_istream_set_size(test_input, input_len); for (i = 1; i <= input_len; i++) { i_stream_set_max_buffer_size(input, i); (void)i_stream_read(input); (void)i_stream_read(input); } i_stream_set_max_buffer_size(input, i+1); (void)i_stream_read(input); } test_assert(input->stream_errno == EPIPE); i_stream_unref(&test_input); i_stream_unref(&input); } static void test_istream_dot(void) { static struct dot_test tests[] = { { "..foo\n..\n.foo\n.\nfoo", ".foo\n.\nfoo\n", "foo" }, { "..foo\r\n..\r\n.foo\r\n.\r\nfoo", ".foo\r\n.\r\nfoo\r\n", "foo" }, { "\r.\r\n.\r\n", "\r.\r\n", "" }, { "\n\r.\r\r\n.\r\n", "\n\r.\r\r\n", "" }, { "\r\n.\rfoo\n.\n", "\r\n\rfoo\n", "" }, { "\r\n.\r\n", "\r\n", "" }, { "\n.\r\n", "\n", "" }, { "\n.\n", "\n", "" }, { ".\r\n", "", "" }, { ".\n", "", "" } }; static const char *error_tests[] = { "", ".", "..", ".\r", ".\rx", "..\r\n", "\r.", "\r.\r", "\r.\rx", "\r.\r\n", "\r.\n", "\r..\n", "\r\n", "\r\n.", "\r\n.\r", "\r\n.\rx", "\r\n.\rx\n", "\r\n..\r\n", "\n", "\n.", "\n.\r", "\n.\rx", "\n..\r\n" }; unsigned int i; test_begin("dot istream"); for (i = 0; i < N_ELEMENTS(tests); i++) { test_istream_dot_one(&tests[i], TRUE, TRUE); test_istream_dot_one(&tests[i], TRUE, FALSE); test_istream_dot_one(&tests[i], FALSE, TRUE); test_istream_dot_one(&tests[i], FALSE, FALSE); } for (i = 0; i < N_ELEMENTS(error_tests); i++) { test_istream_dot_error(error_tests[i], FALSE); test_istream_dot_error(error_tests[i], TRUE); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_istream_dot, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-mail-html2text.c0000644000175000017500000000540513165463624016614 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "mail-html2text.h" #include "test-common.h" static struct { const char *input; const char *output; } tests[] = { { "&&aaaaaaaaaa", "" }, { "a&<♣>b", "a&<\xE2\x99\xA3>b" }, { "&", "" }, { "&", "" }, { "ab", "a b" }, { "abc", "a b c" }, { "ab", "ab" }, { "ab", "a b" }, { "abc", "a b c" }, { "a
second level
ignored
b", "a b" }, { "a]] >b]]>c", "ac" }, { "afoo', '&', ';', '\\', '\'', '"', '/' }; unsigned char s[2]; ht = mail_html2text_init(0); for (unsigned int i = 0; i < 100; i++) { s[0] = valid_chars[rand() % N_ELEMENTS(valid_chars)]; s[1] = valid_chars[rand() % N_ELEMENTS(valid_chars)]; mail_html2text_more(ht, s, rand()%2+1, str); } mail_html2text_deinit(&ht); str_truncate(str, 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_mail_html2text, test_mail_html2text_random, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/test-istream-binary-converter.c0000644000175000017500000001422213165463624020671 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "buffer.h" #include "str.h" #include "sha1.h" #include "istream.h" #include "istream-crlf.h" #include "istream-binary-converter.h" #include "test-common.h" #include #define BINARY_TEXT_LONG "we have\ra lot \nof \0binary stuff in here\n" \ "b adjig sadjg jasidgjiaehga3wht8a3w8ghxjc dsgad hasdghsd gasd ds" \ "jdsoga sjdga0w3tjhawjgsertniq3n5oqerjqw2r89q23h awhrqh835r8a" #define BINARY_TEXT_LONG_BASE64 \ "d2UgaGF2ZQ1hIGxvdCAKb2YgAGJpbmFyeSBzdHVmZiBpbiBoZXJlCmIgYWRqaWcgc2FkamcgamFz\r\n" \ "aWRnamlhZWhnYTN3aHQ4YTN3OGdoeGpjIGRzZ2FkIGhhc2RnaHNkIGdhc2QgZHNqZHNvZ2Egc2pk\r\n" \ "Z2EwdzN0amhhd2pnc2VydG5pcTNuNW9xZXJqcXcycjg5cTIzaCBhd2hycWg4MzVyOGE=" #define BINARY_TEXT_SHORT "eh" #define BINARY_TEXT_SHORT_BASE64 "ZWg=" static const char mail_input_mime[] = "MIME-Version: 1.0\r\n" "Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n" "\r\n" "mime header\r\n" "\r\n--bound\r\n" "Content-Transfer-Encoding: binary\r\n" "Content-Type: text/plain\r\n" "\r\n" BINARY_TEXT_LONG "\r\n--bound\r\n" "Content-Type: text/plain\r\n" "Content-Transfer-Encoding: binary\r\n" "\r\n" BINARY_TEXT_SHORT "\n--bound\r\n" "Content-Type: text/plain\r\n" "\r\n" "hello world\r\n" "\r\n--bound--\r\n"; static const char mail_output_mime[] = "MIME-Version: 1.0\r\n" "Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n" "\r\n" "mime header\r\n" "\r\n--bound\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Type: text/plain\r\n" "\r\n" BINARY_TEXT_LONG_BASE64 "\r\n--bound\r\n" "Content-Type: text/plain\r\n" "Content-Transfer-Encoding: base64\r\n" "\r\n" BINARY_TEXT_SHORT_BASE64 "\n--bound\r\n" "Content-Type: text/plain\r\n" "\r\n" "hello world\r\n" "\r\n--bound--\r\n"; static const char mail_input_root_hdr[] = "MIME-Version: 1.0\r\n" "Content-Transfer-Encoding: binary\r\n" "Content-Type: text/plain\r\n" "\r\n"; static const char mail_output_root_hdr[] = "MIME-Version: 1.0\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Type: text/plain\r\n" "\r\n"; static const char mail_root_nonbinary[] = "MIME-Version: 1.0\r\n" "Content-Type: text/plain\r\n" "\r\n" "hello\n\n"; static void test_istream_binary_converter_test(const char *mail_input, unsigned int mail_input_len, const char *mail_output, unsigned int mail_output_len, unsigned int idx) { struct istream *datainput, *input; const unsigned char *data; size_t i, size; int ret; datainput = test_istream_create_data(mail_input, mail_input_len); test_istream_set_allow_eof(datainput, FALSE); input = i_stream_create_binary_converter(datainput); for (i = 1; i <= mail_input_len; i++) { test_istream_set_size(datainput, i); while ((ret = i_stream_read(input)) > 0) ; test_assert_idx(ret == 0, idx); } test_istream_set_allow_eof(datainput, TRUE); while ((ret = i_stream_read(input)) > 0) ; test_assert_idx(ret == -1, idx); data = i_stream_get_data(input, &size); test_assert_idx(size == mail_output_len && memcmp(data, mail_output, size) == 0, idx); i_stream_unref(&input); i_stream_unref(&datainput); } static void test_istream_binary_converter_mime(void) { test_begin("istream binary converter in mime parts"); test_istream_binary_converter_test(mail_input_mime, sizeof(mail_input_mime)-1, mail_output_mime, sizeof(mail_output_mime)-1, 0); test_end(); } static void test_istream_binary_converter_root(void) { buffer_t *inbuf = buffer_create_dynamic(pool_datastack_create(), 512); buffer_t *outbuf = buffer_create_dynamic(pool_datastack_create(), 512); const char *const suffixes[] = { "\n", "\r\n", "\n\r\n\n\n" }; unsigned int i; unsigned int input_hdr_len = sizeof(mail_input_root_hdr)-1; test_begin("istream binary converter in root"); buffer_append(inbuf, mail_input_root_hdr, input_hdr_len); buffer_append(outbuf, mail_output_root_hdr, sizeof(mail_output_root_hdr)-1); for (i = 0; i < N_ELEMENTS(suffixes); i++) { buffer_set_used_size(inbuf, input_hdr_len); buffer_set_used_size(outbuf, sizeof(mail_output_root_hdr)-1); buffer_append(inbuf, BINARY_TEXT_SHORT, sizeof(BINARY_TEXT_SHORT)-1); buffer_append(inbuf, suffixes[i], strlen(suffixes[i])); base64_encode(CONST_PTR_OFFSET(inbuf->data, input_hdr_len), inbuf->used - input_hdr_len, outbuf); test_istream_binary_converter_test(inbuf->data, inbuf->used, outbuf->data, outbuf->used, i); } test_end(); } static void test_istream_binary_converter_root_nonbinary(void) { test_begin("istream binary converter in root having non-binary"); test_istream_binary_converter_test(mail_root_nonbinary, sizeof(mail_root_nonbinary)-1, mail_root_nonbinary, sizeof(mail_root_nonbinary)-1, 0); test_end(); } static int test_input_file(const char *path) { struct istream *file_input, *input, *input2; const unsigned char *data; size_t size; struct sha1_ctxt hash; unsigned char hash_file[SHA1_RESULTLEN], hash_converter[SHA1_RESULTLEN]; int ret = 0; lib_init(); file_input = i_stream_create_file(path, 64); /* get hash when directly reading input */ input = i_stream_create_crlf(file_input); sha1_init(&hash); while (i_stream_read_data(input, &data, &size, 0) > 0) { sha1_loop(&hash, data, size); i_stream_skip(input, size); } sha1_result(&hash, hash_file); i_stream_unref(&input); /* get hash when going through converter */ i_stream_seek(file_input, 0); input = i_stream_create_crlf(file_input); input2 = i_stream_create_binary_converter(input); sha1_init(&hash); while (i_stream_read_data(input2, &data, &size, 0) > 0) { sha1_loop(&hash, data, size); i_stream_skip(input2, size); } sha1_result(&hash, hash_converter); i_stream_unref(&input2); i_stream_unref(&input); if (memcmp(hash_file, hash_converter, SHA1_RESULTLEN) != 0) { fprintf(stderr, "istream-binary-converter: mismatch on file %s\n", path); ret = 1; } i_stream_unref(&file_input); lib_deinit(); return ret; } int main(int argc, char *argv[]) { static void (*test_functions[])(void) = { test_istream_binary_converter_mime, test_istream_binary_converter_root, test_istream_binary_converter_root_nonbinary, NULL }; if (argc > 1) return test_input_file(argv[1]); else return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/rfc2231-parser.c0000644000175000017500000001031013123174404015314 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" struct rfc2231_parameter { const char *key, *value; unsigned int idx; bool extended; }; static int rfc2231_parameter_cmp(const struct rfc2231_parameter *r1, const struct rfc2231_parameter *r2) { int ret; ret = strcmp(r1->key, r2->key); if (ret != 0) return ret; return r1->idx < r2->idx ? -1 : (r1-> idx > r2->idx ? 1 : 0); } static void rfc2231_escape(string_t *dest, const char *src) { for (; *src != '\0'; src++) { if (*src == '%') str_append(dest, "%25"); else str_append_c(dest, *src); } } int rfc2231_parse(struct rfc822_parser_context *ctx, const char *const **result_r) { ARRAY_TYPE(const_string) result; ARRAY(struct rfc2231_parameter) rfc2231_params_arr; struct rfc2231_parameter rfc2231_param; const struct rfc2231_parameter *rfc2231_params; const char *key, *value, *p, *p2; string_t *str; unsigned int i, j, count, next, next_idx; bool ok, have_extended, broken = FALSE; int ret; /* Get a list of all parameters. RFC 2231 uses key*[*]=value pairs, which we want to merge to a key[*]=value pair. Save them to a separate array. */ i_zero(&rfc2231_param); t_array_init(&result, 8); t_array_init(&rfc2231_params_arr, 8); while ((ret = rfc822_parse_content_param(ctx, &key, &value)) != 0) { if (ret < 0) { /* try to continue anyway.. */ broken = TRUE; if (ctx->data == ctx->end) break; ctx->data++; continue; } p = strchr(key, '*'); if (p != NULL) { p2 = p; if (p[1] != '\0') { p++; rfc2231_param.idx = 0; for (; *p >= '0' && *p <= '9'; p++) { rfc2231_param.idx = rfc2231_param.idx*10 + *p - '0'; } } if (*p != '*') rfc2231_param.extended = FALSE; else { rfc2231_param.extended = TRUE; p++; } if (*p != '\0') p = NULL; else { rfc2231_param.key = t_strdup_until(key, p2); rfc2231_param.value = value; array_append(&rfc2231_params_arr, &rfc2231_param, 1); } } if (p == NULL) { array_append(&result, &key, 1); array_append(&result, &value, 1); } } if (array_count(&rfc2231_params_arr) == 0) { /* No RFC 2231 parameters */ array_append_zero(&result); /* NULL-terminate */ *result_r = array_idx(&result, 0); return broken ? -1 : 0; } /* Merge the RFC 2231 parameters. Since their order isn't guaranteed to be ascending, start by sorting them. */ array_sort(&rfc2231_params_arr, rfc2231_parameter_cmp); rfc2231_params = array_get(&rfc2231_params_arr, &count); /* keys are now sorted primarily by their name and secondarily by their index. If any indexes are missing, fallback to assuming these aren't RFC 2231 encoded parameters. */ str = t_str_new(64); for (i = 0; i < count; i = next) { ok = TRUE; have_extended = FALSE; next_idx = 0; for (j = i; j < count; j++) { if (strcasecmp(rfc2231_params[i].key, rfc2231_params[j].key) != 0) break; if (rfc2231_params[j].idx != next_idx) { /* missing indexes */ ok = FALSE; } if (rfc2231_params[j].extended) have_extended = TRUE; next_idx++; } next = j; if (!ok) { /* missing indexes */ for (j = i; j < next; j++) { key = t_strdup_printf( rfc2231_params[j].extended ? "%s*%u*" : "%s*%u", rfc2231_params[j].key, rfc2231_params[j].idx); array_append(&result, &key, 1); array_append(&result, &rfc2231_params[j].value, 1); } } else { /* everything was successful */ str_truncate(str, 0); if (!rfc2231_params[i].extended && have_extended) str_append(str, "''"); for (j = i; j < next; j++) { if (!rfc2231_params[j].extended && have_extended) { rfc2231_escape(str, rfc2231_params[j].value); } else { str_append(str, rfc2231_params[j].value); } } key = rfc2231_params[i].key; if (have_extended) key = t_strconcat(key, "*", NULL); value = t_strdup(str_c(str)); array_append(&result, &key, 1); array_append(&result, &value, 1); } } array_append_zero(&result); /* NULL-terminate */ *result_r = array_idx(&result, 0); return broken ? -1 : 0; } dovecot-2.2.33.2/src/lib-mail/message-header-hash.c0000644000175000017500000000402013147010711016530 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash-method.h" #include "message-header-hash.h" void message_header_hash_more(struct message_header_hash_context *ctx, const struct hash_method *method, void *context, unsigned int version, const unsigned char *data, size_t size) { size_t i, start; i_assert(version >= 1 && version <= MESSAGE_HEADER_HASH_MAX_VERSION); if (version == 1) { method->loop(context, data, size); return; } /* - Dovecot IMAP replaces NULs with 0x80 character. - Dovecot POP3 with outlook-no-nuls workaround replaces NULs with 0x80 character. - Zimbra replaces 8bit chars with '?' in header fetches, but not body fetches. - Yahoo replaces 8bit chars with '?' in partial header fetches, but not POP3 TOP. UTF-8 character sequence writes only a single '?' So we'll just replace all control and 8bit chars with '?' and remove any repeated '?', which hopefully will satisfy everybody. Also: - Zimbra removes trailing spaces and tabs from IMAP BODY[HEADER], but not IMAP BODY[] or POP3 TOP. Just strip away all spaces with version 3 and tabs also with version 4. */ for (i = start = 0; i < size; i++) { bool cur_is_questionmark = FALSE; switch (data[i]) { case ' ': if (version >= 3) { /* strip away spaces */ method->loop(context, data + start, i-start); start = i+1; } break; case '\t': if (version >= 4) { /* strip away tabs */ method->loop(context, data + start, i-start); start = i+1; } break; case '\n': break; default: if (data[i] < 0x20 || data[i] >= 0x7f || data[i] == '?') { /* remove repeated '?' */ if (start < i || !ctx->prev_was_questionmark) { method->loop(context, data + start, i-start); method->loop(context, "?", 1); } start = i+1; cur_is_questionmark = TRUE; } break; } ctx->prev_was_questionmark = cur_is_questionmark; } method->loop(context, data + start, i-start); } dovecot-2.2.33.2/src/lib-mail/html-entities.h0000644000175000017500000001215613123174404015545 00000000000000{ "quot", 0x0022 }, { "amp", 0x0026 }, { "apos", 0x0027 }, { "lt", 0x003C }, { "gt", 0x003E }, { "nbsp", 0x00A0 }, { "iexcl", 0x00A1 }, { "cent", 0x00A2 }, { "pound", 0x00A3 }, { "curren", 0x00A4 }, { "yen", 0x00A5 }, { "brvbar", 0x00A6 }, { "sect", 0x00A7 }, { "uml", 0x00A8 }, { "copy", 0x00A9 }, { "ordf", 0x00AA }, { "laquo", 0x00AB }, { "not", 0x00AC }, { "shy", 0x00AD }, { "reg", 0x00AE }, { "macr", 0x00AF }, { "deg", 0x00B0 }, { "plusmn", 0x00B1 }, { "sup2", 0x00B2 }, { "sup3", 0x00B3 }, { "acute", 0x00B4 }, { "micro", 0x00B5 }, { "para", 0x00B6 }, { "middot", 0x00B7 }, { "cedil", 0x00B8 }, { "sup1", 0x00B9 }, { "ordm", 0x00BA }, { "raquo", 0x00BB }, { "frac14", 0x00BC }, { "frac12", 0x00BD }, { "frac34", 0x00BE }, { "iquest", 0x00BF }, { "Agrave", 0x00C0 }, { "Aacute", 0x00C1 }, { "Acirc", 0x00C2 }, { "Atilde", 0x00C3 }, { "Auml", 0x00C4 }, { "Aring", 0x00C5 }, { "AElig", 0x00C6 }, { "Ccedil", 0x00C7 }, { "Egrave", 0x00C8 }, { "Eacute", 0x00C9 }, { "Ecirc", 0x00CA }, { "Euml", 0x00CB }, { "Igrave", 0x00CC }, { "Iacute", 0x00CD }, { "Icirc", 0x00CE }, { "Iuml", 0x00CF }, { "ETH", 0x00D0 }, { "Ntilde", 0x00D1 }, { "Ograve", 0x00D2 }, { "Oacute", 0x00D3 }, { "Ocirc", 0x00D4 }, { "Otilde", 0x00D5 }, { "Ouml", 0x00D6 }, { "times", 0x00D7 }, { "Oslash", 0x00D8 }, { "Ugrave", 0x00D9 }, { "Uacute", 0x00DA }, { "Ucirc", 0x00DB }, { "Uuml", 0x00DC }, { "Yacute", 0x00DD }, { "THORN", 0x00DE }, { "szlig", 0x00DF }, { "agrave", 0x00E0 }, { "aacute", 0x00E1 }, { "acirc", 0x00E2 }, { "atilde", 0x00E3 }, { "auml", 0x00E4 }, { "aring", 0x00E5 }, { "aelig", 0x00E6 }, { "ccedil", 0x00E7 }, { "egrave", 0x00E8 }, { "eacute", 0x00E9 }, { "ecirc", 0x00EA }, { "euml", 0x00EB }, { "igrave", 0x00EC }, { "iacute", 0x00ED }, { "icirc", 0x00EE }, { "iuml", 0x00EF }, { "eth", 0x00F0 }, { "ntilde", 0x00F1 }, { "ograve", 0x00F2 }, { "oacute", 0x00F3 }, { "ocirc", 0x00F4 }, { "otilde", 0x00F5 }, { "ouml", 0x00F6 }, { "divide", 0x00F7 }, { "oslash", 0x00F8 }, { "ugrave", 0x00F9 }, { "uacute", 0x00FA }, { "ucirc", 0x00FB }, { "uuml", 0x00FC }, { "yacute", 0x00FD }, { "thorn", 0x00FE }, { "yuml", 0x00FF }, { "OElig", 0x0152 }, { "oelig", 0x0153 }, { "Scaron", 0x0160 }, { "scaron", 0x0161 }, { "Yuml", 0x0178 }, { "fnof", 0x0192 }, { "circ", 0x02C6 }, { "tilde", 0x02DC }, { "Alpha", 0x0391 }, { "Beta", 0x0392 }, { "Gamma", 0x0393 }, { "Delta", 0x0394 }, { "Epsilon", 0x0395 }, { "Zeta", 0x0396 }, { "Eta", 0x0397 }, { "Theta", 0x0398 }, { "Iota", 0x0399 }, { "Kappa", 0x039A }, { "Lambda", 0x039B }, { "Mu", 0x039C }, { "Nu", 0x039D }, { "Xi", 0x039E }, { "Omicron", 0x039F }, { "Pi", 0x03A0 }, { "Rho", 0x03A1 }, { "Sigma", 0x03A3 }, { "Tau", 0x03A4 }, { "Upsilon", 0x03A5 }, { "Phi", 0x03A6 }, { "Chi", 0x03A7 }, { "Psi", 0x03A8 }, { "Omega", 0x03A9 }, { "alpha", 0x03B1 }, { "beta", 0x03B2 }, { "gamma", 0x03B3 }, { "delta", 0x03B4 }, { "epsilon", 0x03B5 }, { "zeta", 0x03B6 }, { "eta", 0x03B7 }, { "theta", 0x03B8 }, { "iota", 0x03B9 }, { "kappa", 0x03BA }, { "lambda", 0x03BB }, { "mu", 0x03BC }, { "nu", 0x03BD }, { "xi", 0x03BE }, { "omicron", 0x03BF }, { "pi", 0x03C0 }, { "rho", 0x03C1 }, { "sigmaf", 0x03C2 }, { "sigma", 0x03C3 }, { "tau", 0x03C4 }, { "upsilon", 0x03C5 }, { "phi", 0x03C6 }, { "chi", 0x03C7 }, { "psi", 0x03C8 }, { "omega", 0x03C9 }, { "thetasym", 0x03D1 }, { "upsih", 0x03D2 }, { "piv", 0x03D6 }, { "ensp", 0x2002 }, { "emsp", 0x2003 }, { "thinsp", 0x2009 }, { "zwnj", 0x200C }, { "zwj", 0x200D }, { "lrm", 0x200E }, { "rlm", 0x200F }, { "ndash", 0x2013 }, { "mdash", 0x2014 }, { "lsquo", 0x2018 }, { "rsquo", 0x2019 }, { "sbquo", 0x201A }, { "ldquo", 0x201C }, { "rdquo", 0x201D }, { "bdquo", 0x201E }, { "dagger", 0x2020 }, { "Dagger", 0x2021 }, { "bull", 0x2022 }, { "hellip", 0x2026 }, { "permil", 0x2030 }, { "prime", 0x2032 }, { "Prime", 0x2033 }, { "lsaquo", 0x2039 }, { "rsaquo", 0x203A }, { "oline", 0x203E }, { "frasl", 0x2044 }, { "euro", 0x20AC }, { "image", 0x2111 }, { "weierp", 0x2118 }, { "real", 0x211C }, { "trade", 0x2122 }, { "alefsym", 0x2135 }, { "larr", 0x2190 }, { "uarr", 0x2191 }, { "rarr", 0x2192 }, { "darr", 0x2193 }, { "harr", 0x2194 }, { "crarr", 0x21B5 }, { "lArr", 0x21D0 }, { "uArr", 0x21D1 }, { "rArr", 0x21D2 }, { "dArr", 0x21D3 }, { "hArr", 0x21D4 }, { "forall", 0x2200 }, { "part", 0x2202 }, { "exist", 0x2203 }, { "empty", 0x2205 }, { "nabla", 0x2207 }, { "isin", 0x2208 }, { "notin", 0x2209 }, { "ni", 0x220B }, { "prod", 0x220F }, { "sum", 0x2211 }, { "minus", 0x2212 }, { "lowast", 0x2217 }, { "radic", 0x221A }, { "prop", 0x221D }, { "infin", 0x221E }, { "ang", 0x2220 }, { "and", 0x2227 }, { "or", 0x2228 }, { "cap", 0x2229 }, { "cup", 0x222A }, { "int", 0x222B }, { "there4", 0x2234 }, { "sim", 0x223C }, { "cong", 0x2245 }, { "asymp", 0x2248 }, { "ne", 0x2260 }, { "equiv", 0x2261 }, { "le", 0x2264 }, { "ge", 0x2265 }, { "sub", 0x2282 }, { "sup", 0x2283 }, { "nsub", 0x2284 }, { "sube", 0x2286 }, { "supe", 0x2287 }, { "oplus", 0x2295 }, { "otimes", 0x2297 }, { "perp", 0x22A5 }, { "sdot", 0x22C5 }, { "lceil", 0x2308 }, { "rceil", 0x2309 }, { "lfloor", 0x230A }, { "rfloor", 0x230B }, { "lang", 0x27E8 }, { "rang", 0x27E9 }, { "loz", 0x25CA }, { "spades", 0x2660 }, { "clubs", 0x2663 }, { "hearts", 0x2665 }, { "diams", 0x2666 } dovecot-2.2.33.2/src/lib-mail/message-header-hash.h0000644000175000017500000000067613147010711016552 00000000000000#ifndef MESSAGE_HEADER_HASH_H #define MESSAGE_HEADER_HASH_H #define MESSAGE_HEADER_HASH_MAX_VERSION 4 struct hash_method; struct message_header_hash_context { bool prev_was_questionmark; }; /* Initialize ctx with zeros. */ void message_header_hash_more(struct message_header_hash_context *ctx, const struct hash_method *method, void *context, unsigned int version, const unsigned char *data, size_t size); #endif dovecot-2.2.33.2/src/lib-mail/test-message-address.c0000644000175000017500000003471713165463624017020 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "message-address.h" #include "test-common.h" static bool cmp_addr(const struct message_address *a1, const struct message_address *a2) { return null_strcmp(a1->name, a2->name) == 0 && null_strcmp(a1->route, a2->route) == 0 && null_strcmp(a1->mailbox, a2->mailbox) == 0 && null_strcmp(a1->domain, a2->domain) == 0 && a1->invalid_syntax == a2->invalid_syntax; } static const struct message_address * test_parse_address(const char *input, bool fill_missing) { /* duplicate the input (without trailing NUL) so valgrind notices if there's any out-of-bounds access */ size_t input_len = strlen(input); unsigned char *input_dup = i_malloc(input_len); memcpy(input_dup, input, input_len); const struct message_address *addr = message_address_parse(pool_datastack_create(), input_dup, input_len, UINT_MAX, fill_missing); i_free(input_dup); return addr; } static void test_message_address(void) { static const struct test { const char *input; const char *wanted_output; const char *wanted_filled_output; struct message_address addr; struct message_address filled_addr; } tests[] = { /* user@domain -> */ { "user@domain", "", NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, { "\"user\"@domain", "", NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, { "\"user name\"@domain", "<\"user name\"@domain>", NULL, { NULL, NULL, NULL, "user name", "domain", FALSE }, { NULL, NULL, NULL, "user name", "domain", FALSE } }, { "\"user@na\\\\me\"@domain", "<\"user@na\\\\me\"@domain>", NULL, { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, { NULL, NULL, NULL, "user@na\\me", "domain", FALSE } }, { "\"user\\\"name\"@domain", "<\"user\\\"name\"@domain>", NULL, { NULL, NULL, NULL, "user\"name", "domain", FALSE }, { NULL, NULL, NULL, "user\"name", "domain", FALSE } }, { "\"\"@domain", "<\"\"@domain>", NULL, { NULL, NULL, NULL, "", "domain", FALSE }, { NULL, NULL, NULL, "", "domain", FALSE } }, { "user", "", "", { NULL, NULL, NULL, "user", "", TRUE }, { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE } }, { "@domain", "<\"\"@domain>", "", { NULL, NULL, NULL, "", "domain", TRUE }, { NULL, NULL, NULL, "MISSING_MAILBOX", "domain", TRUE } }, /* Display Name -> Display Name */ { "Display Name", "\"Display Name\"", "\"Display Name\" ", { NULL, "Display Name", NULL, "", "", TRUE }, { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, { "\"Display Name\"", "\"Display Name\"", "\"Display Name\" ", { NULL, "Display Name", NULL, "", "", TRUE }, { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, { "Display \"Name\"", "\"Display Name\"", "\"Display Name\" ", { NULL, "Display Name", NULL, "", "", TRUE }, { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, { "\"Display\" \"Name\"", "\"Display Name\"", "\"Display Name\" ", { NULL, "Display Name", NULL, "", "", TRUE }, { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, { "\"\"", "", "", { NULL, "", NULL, "", "", TRUE }, { NULL, "", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, /* -> */ { "", NULL, NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, { "<\"user\"@domain>", "", NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, { "<\"user name\"@domain>", NULL, NULL, { NULL, NULL, NULL, "user name", "domain", FALSE }, { NULL, NULL, NULL, "user name", "domain", FALSE } }, { "<\"user@na\\\\me\"@domain>", NULL, NULL, { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, { NULL, NULL, NULL, "user@na\\me", "domain", FALSE } }, { "<\"user\\\"name\"@domain>", NULL, NULL, { NULL, NULL, NULL, "user\"name", "domain", FALSE }, { NULL, NULL, NULL, "user\"name", "domain", FALSE } }, { "<\"\"@domain>", NULL, NULL, { NULL, NULL, NULL, "", "domain", FALSE }, { NULL, NULL, NULL, "", "domain", FALSE } }, { "", NULL, "", { NULL, NULL, NULL, "user", "", TRUE }, { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE } }, { "<@route>", "<@route:\"\">", "", { NULL, NULL, "@route", "", "", TRUE }, { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, /* user@domain (Display Name) -> "Display Name" */ { "user@domain (DisplayName)", "DisplayName ", NULL, { NULL, "DisplayName", NULL, "user", "domain", FALSE }, { NULL, "DisplayName", NULL, "user", "domain", FALSE } }, { "user@domain (Display Name)", "\"Display Name\" ", NULL, { NULL, "Display Name", NULL, "user", "domain", FALSE }, { NULL, "Display Name", NULL, "user", "domain", FALSE } }, { "user@domain (Display\"Name)", "\"Display\\\"Name\" ", NULL, { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, { NULL, "Display\"Name", NULL, "user", "domain", FALSE } }, { "user (Display Name)", "\"Display Name\" ", "\"Display Name\" ", { NULL, "Display Name", NULL, "user", "", TRUE }, { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE } }, { "@domain (Display Name)", "\"Display Name\" <\"\"@domain>", "\"Display Name\" ", { NULL, "Display Name", NULL, "", "domain", TRUE }, { NULL, "Display Name", NULL, "MISSING_MAILBOX", "domain", TRUE } }, { "user@domain ()", "", NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, /* Display Name -> "Display Name" */ { "DisplayName ", NULL, NULL, { NULL, "DisplayName", NULL, "user", "domain", FALSE }, { NULL, "DisplayName", NULL, "user", "domain", FALSE } }, { "Display Name ", "\"Display Name\" ", NULL, { NULL, "Display Name", NULL, "user", "domain", FALSE }, { NULL, "Display Name", NULL, "user", "domain", FALSE } }, { "\"Display Name\" ", NULL, NULL, { NULL, "Display Name", NULL, "user", "domain", FALSE }, { NULL, "Display Name", NULL, "user", "domain", FALSE } }, { "\"Display\\\"Name\" ", NULL, NULL, { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, { NULL, "Display\"Name", NULL, "user", "domain", FALSE } }, { "Display Name ", "\"Display Name\" ", "\"Display Name\" ", { NULL, "Display Name", NULL, "user", "", TRUE }, { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE } }, { "\"\" ", "", NULL, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE } }, /* <@route:user@domain> -> <@route:user@domain> */ { "<@route:user@domain>", NULL, NULL, { NULL, NULL, "@route", "user", "domain", FALSE }, { NULL, NULL, "@route", "user", "domain", FALSE } }, { "<@route,@route2:user@domain>", NULL, NULL, { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, { NULL, NULL, "@route,@route2", "user", "domain", FALSE } }, { "<@route@route2:user@domain>", "<@route,@route2:user@domain>", NULL, { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, { NULL, NULL, "@route,@route2", "user", "domain", FALSE } }, { "<@route@route2:user>", "<@route,@route2:user>", "<@route,@route2:user@MISSING_DOMAIN>", { NULL, NULL, "@route,@route2", "user", "", TRUE }, { NULL, NULL, "@route,@route2", "user", "MISSING_DOMAIN", TRUE } }, { "<@route@route2:\"\"@domain>", "<@route,@route2:\"\"@domain>", NULL, { NULL, NULL, "@route,@route2", "", "domain", FALSE }, { NULL, NULL, "@route,@route2", "", "domain", FALSE } }, /* Display Name <@route:user@domain> -> "Display Name" <@route:user@domain> */ { "Display Name <@route:user@domain>", "\"Display Name\" <@route:user@domain>", NULL, { NULL, "Display Name", "@route", "user", "domain", FALSE }, { NULL, "Display Name", "@route", "user", "domain", FALSE } }, { "Display Name <@route,@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL, { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE } }, { "Display Name <@route@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL, { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE } }, { "Display Name <@route@route2:user>", "\"Display Name\" <@route,@route2:user>", "\"Display Name\" <@route,@route2:user@MISSING_DOMAIN>", { NULL, "Display Name", "@route,@route2", "user", "", TRUE }, { NULL, "Display Name", "@route,@route2", "user", "MISSING_DOMAIN", TRUE } }, { "Display Name <@route@route2:\"\"@domain>", "\"Display Name\" <@route,@route2:\"\"@domain>", NULL, { NULL, "Display Name", "@route,@route2", "", "domain", FALSE }, { NULL, "Display Name", "@route,@route2", "", "domain", FALSE } }, /* other tests: */ { "\"foo: ;,\" ", NULL, NULL, { NULL, "foo: ;,", NULL, "user", "domain", FALSE }, { NULL, "foo: ;,", NULL, "user", "domain", FALSE } }, { "<>", "", "", { NULL, NULL, NULL, "", "", TRUE }, { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, { "<@>", "", "", { NULL, NULL, NULL, "", "", TRUE }, { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE } }, }; static struct message_address group_prefix = { NULL, NULL, NULL, "group", NULL, FALSE }; static struct message_address group_suffix = { NULL, NULL, NULL, NULL, NULL, FALSE }; const struct message_address *addr; string_t *str, *group; const char *wanted_string; unsigned int i; test_begin("message address parsing"); str = t_str_new(128); group = t_str_new(256); for (i = 0; i < N_ELEMENTS(tests)*2; i++) { const struct test *test = &tests[i/2]; const struct message_address *test_wanted_addr; bool fill_missing = i%2 != 0; test_wanted_addr = !fill_missing ? &test->addr : &test->filled_addr; addr = test_parse_address(test->input, fill_missing); test_assert_idx(addr != NULL && addr->next == NULL && cmp_addr(addr, test_wanted_addr), i); /* test the address alone */ str_truncate(str, 0); message_address_write(str, addr); if (fill_missing && test->wanted_filled_output != NULL) wanted_string = test->wanted_filled_output; else if (test->wanted_output != NULL) wanted_string = test->wanted_output; else wanted_string = test->input; test_assert_idx(strcmp(str_c(str), wanted_string) == 0, i); /* test the address as a list of itself */ for (unsigned int list_length = 2; list_length <= 5; list_length++) { str_truncate(group, 0); str_append(group, test->input); for (unsigned int j = 1; j < list_length; j++) { if ((j % 2) == 0) str_append(group, ","); else str_append(group, " , \n "); str_append(group, test->input); } addr = test_parse_address(str_c(group), fill_missing); for (unsigned int j = 0; j < list_length; j++) { test_assert_idx(addr != NULL && cmp_addr(addr, test_wanted_addr), i); if (addr != NULL) addr = addr->next; } test_assert_idx(addr == NULL, i); } /* test the address as a group of itself */ for (unsigned int list_length = 1; list_length <= 5; list_length++) { str_truncate(group, 0); str_printfa(group, "group: %s", test->input); for (unsigned int j = 1; j < list_length; j++) { if ((j % 2) == 0) str_append(group, ","); else str_append(group, " , \n "); str_append(group, test->input); } str_append_c(group, ';'); addr = test_parse_address(str_c(group), fill_missing); test_assert(addr != NULL && cmp_addr(addr, &group_prefix)); addr = addr->next; for (unsigned int j = 0; j < list_length; j++) { test_assert_idx(addr != NULL && cmp_addr(addr, test_wanted_addr), i); if (addr != NULL) addr = addr->next; } test_assert_idx(addr != NULL && addr->next == NULL && cmp_addr(addr, &group_suffix), i); } } test_end(); test_begin("message address parsing with empty group"); str_truncate(group, 0); str_append(group, "group:;"); addr = test_parse_address(str_c(group), FALSE); str_truncate(str, 0); message_address_write(str, addr); test_assert(addr != NULL && cmp_addr(addr, &group_prefix)); addr = addr->next; test_assert(addr != NULL && addr->next == NULL && cmp_addr(addr, &group_suffix)); test_assert(strcmp(str_c(str), "group:;") == 0); test_end(); } static void test_message_delim(void) { test_begin("message address detail parsing"); struct { const char *delimiters; const char *address; const char *username; const char *detail; } tests[] = { { "", "", "", "" }, { "", "test", "test", "" }, { "", "test+address", "test+address", "" }, { "", "test:address", "test:address", "" }, { "", "test-address:another+delim", "test-address:another+delim", "" }, { "", "test@domain", "test@domain", "" }, { "", "test+address@domain", "test+address@domain", "" }, { "", "test:address@domain", "test:address@domain", "" }, { "", "test-address:another+delim@domain", "test-address:another+delim@domain", "" }, { "+-:", "", "", "" }, { "+-:", "test", "test", "" }, { "+-:", "test+address", "test+address", "" }, { "+-:", "test+-:address", "test", "address" }, { "+-:", "test+-:address@domain", "test@domain", "address" }, }; for(size_t i = 0; i < N_ELEMENTS(tests); i++) { const char *username, *detail; message_detail_address_parse(tests[i].delimiters, tests[i].address, &username, &detail); test_assert_idx(strcmp(username, tests[i].username) == 0, i); test_assert_idx(strcmp(detail, tests[i].detail) == 0, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_address, test_message_delim, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/istream-qp-decoder.c0000644000175000017500000001022013165463624016440 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "qp-decoder.h" #include "istream-private.h" #include "istream-qp.h" struct qp_decoder_istream { struct istream_private istream; buffer_t *buf; struct qp_decoder *qp; }; static void i_stream_qp_decoder_close(struct iostream_private *stream, bool close_parent) { struct qp_decoder_istream *bstream = (struct qp_decoder_istream *)stream; if (bstream->qp != NULL) qp_decoder_deinit(&bstream->qp); if (bstream->buf != NULL) buffer_free(&bstream->buf); if (close_parent) i_stream_close(bstream->istream.parent); } static ssize_t i_stream_qp_decoder_read(struct istream_private *stream) { struct qp_decoder_istream *bstream = (struct qp_decoder_istream *)stream; const unsigned char *data; size_t size, error_pos, max_buffer_size; const char *error; int ret; max_buffer_size = i_stream_get_max_buffer_size(&stream->istream); for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= bstream->buf->used); buffer_delete(bstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = bstream->buf->data; i_assert(stream->pos <= bstream->buf->used); if (stream->pos >= max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decoded, return as much of it as we can */ if (bstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ new_pos = I_MIN(bstream->buf->used, max_buffer_size); bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } /* need to read more input */ ret = i_stream_read_data(stream->parent, &data, &size, 0); if (ret <= 0) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; if (ret != -1 || stream->istream.stream_errno != 0) return ret; /* end of quoted-printable stream. verify that the ending is ok. */ if (qp_decoder_finish(bstream->qp, &error) == 0) { i_assert(bstream->buf->used == 0); return -1; } io_stream_set_error(&stream->iostream, "Invalid quoted-printable input trailer: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (qp_decoder_more(bstream->qp, data, size, &error_pos, &error) < 0) { i_assert(error_pos < size); io_stream_set_error(&stream->iostream, "Invalid quoted-printable input 0x%s: %s", binary_to_hex(data+error_pos, I_MIN(size-error_pos, 8)), error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } } static void i_stream_qp_decoder_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct qp_decoder_istream *bstream = (struct qp_decoder_istream *)stream; const char *error; if (v_offset < stream->istream.v_offset) { /* seeking backwards - go back to beginning and seek forward from there. */ stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; i_stream_seek(stream->parent, 0); (void)qp_decoder_finish(bstream->qp, &error); buffer_set_used_size(bstream->buf, 0); } i_stream_default_seek_nonseekable(stream, v_offset, mark); } struct istream *i_stream_create_qp_decoder(struct istream *input) { struct qp_decoder_istream *bstream; bstream = i_new(struct qp_decoder_istream, 1); bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; bstream->buf = buffer_create_dynamic(default_pool, 128); bstream->qp = qp_decoder_init(bstream->buf); bstream->istream.iostream.close = i_stream_qp_decoder_close; bstream->istream.read = i_stream_qp_decoder_read; bstream->istream.seek = i_stream_qp_decoder_seek; bstream->istream.istream.readable_fd = FALSE; bstream->istream.istream.blocking = input->blocking; bstream->istream.istream.seekable = input->seekable; return i_stream_create(&bstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-mail/message-id.h0000644000175000017500000000034413123174404014771 00000000000000#ifndef MESSAGE_ID_H #define MESSAGE_ID_H /* Returns the next valid message ID from a given Message-ID header. The return value is allocated from data stack. */ const char *message_id_get_next(const char **msgid_p); #endif dovecot-2.2.33.2/src/lib-mail/test-message-part.c0000644000175000017500000000513613165463624016332 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "message-parser.h" #include "test-common.h" static const char test_msg[] = "From user@domain Fri Feb 22 17:06:23 2008\n" "From: user@domain.org\n" "Date: Sat, 24 Mar 2007 23:00:00 +0200\n" "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"foo bar\"\n" "\n" "Root MIME prologue\n" "\n" "--foo bar\n" "Content-Type: text/x-myown; charset=us-ascii\n" "\n" "hello\n" "\n" "--foo bar\n" "Content-Type: message/rfc822\n" "\n" "From: sub@domain.org\n" "Date: Sun, 12 Aug 2012 12:34:56 +0300\n" "Subject: submsg\n" "Content-Type: multipart/alternative; boundary=\"sub1\"\n" "\n" "Sub MIME prologue\n" "--sub1\n" "Content-Type: text/html\n" "\n" "

Hello world

\n" "\n" "--sub1\n" "Content-Type: multipart/alternative; boundary=\"sub2\"\n" "\n" "--sub2\n" "Content-Type: multipart/alternative; boundary=\"sub3\"\n" "\n" "--sub3\n" "\n" "sub3 text\n" "--sub3\n" "\n" "sub3 text2\n" "--sub3--\n" "\n" "sub2 text\n" "--sub2\n" "\n" "sub2 text2\n" "--sub1--\n" "Sub MIME epilogue\n" "\n" "--foo bar\n" "Content-Type: text/plain\n" "\n" "Another part\n" "--foo bar--\n" "Root MIME epilogue\n" "\n"; #define TEST_MSG_LEN (sizeof(test_msg)-1) static void test_message_part_idx(void) { struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *part, *prev_part; struct message_block block; unsigned int i, prev_idx = 0, part_idx; pool_t pool; int ret; test_begin("message part indexes"); pool = pool_alloconly_create("message parser", 10240); input = i_stream_create_from_data(test_msg, TEST_MSG_LEN); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { part_idx = message_part_to_idx(block.part); test_assert(part_idx >= prev_idx); prev_idx = part_idx; } test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); part = message_part_by_idx(parts, 0); test_assert(part == parts); test_assert(message_part_by_idx(parts, 1) == parts->children); for (i = 1; i < 11; i++) { prev_part = part; part = message_part_by_idx(parts, i); test_assert(part != NULL); test_assert(part != NULL && message_part_to_idx(part) == i); test_assert(part != NULL && prev_part != NULL && prev_part->physical_pos < part->physical_pos); } test_assert(message_part_by_idx(parts, i) == NULL); i_stream_unref(&input); pool_unref(&pool); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_message_part_idx, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-mail/Makefile.am0000644000175000017500000001473713123174404014651 00000000000000noinst_LTLIBRARIES = libmail.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset libmail_la_SOURCES = \ istream-attachment-connector.c \ istream-attachment-extractor.c \ istream-binary-converter.c \ istream-dot.c \ istream-header-filter.c \ istream-nonuls.c \ istream-qp-decoder.c \ istream-qp-encoder.c \ mail-html2text.c \ mail-user-hash.c \ mbox-from.c \ message-address.c \ message-binary-part.c \ message-date.c \ message-decoder.c \ message-header-decode.c \ message-header-encode.c \ message-header-hash.c \ message-header-parser.c \ message-id.c \ message-parser.c \ message-part.c \ message-part-data.c \ message-part-serialize.c \ message-search.c \ message-size.c \ message-snippet.c \ ostream-dot.c \ qp-decoder.c \ qp-encoder.c \ quoted-printable.c \ rfc2231-parser.c \ rfc822-parser.c noinst_HEADERS = \ html-entities.h headers = \ istream-attachment-connector.h \ istream-attachment-extractor.h \ istream-binary-converter.h \ istream-dot.h \ istream-header-filter.h \ istream-nonuls.h \ istream-qp.h \ mail-user-hash.h \ mbox-from.h \ mail-html2text.h \ mail-types.h \ message-address.h \ message-binary-part.h \ message-date.h \ message-decoder.h \ message-header-decode.h \ message-header-encode.h \ message-header-hash.h \ message-header-parser.h \ message-id.h \ message-parser.h \ message-part.h \ message-part-data.h \ message-part-serialize.h \ message-search.h \ message-size.h \ message-snippet.h \ ostream-dot.h \ qp-decoder.h \ qp-encoder.h \ quoted-printable.h \ rfc2231-parser.h \ rfc822-parser.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-istream-dot \ test-istream-attachment \ test-istream-binary-converter \ test-istream-header-filter \ test-istream-qp-decoder \ test-istream-qp-encoder \ test-mail-html2text \ test-mbox-from \ test-message-address \ test-message-date \ test-message-decoder \ test-message-header-decode \ test-message-header-encode \ test-message-header-hash \ test-message-header-parser \ test-message-id \ test-message-parser \ test-message-part \ test-message-search \ test-message-snippet \ test-ostream-dot \ test-qp-decoder \ test-qp-encoder \ test-quoted-printable \ test-rfc2231-parser \ test-rfc822-parser noinst_PROGRAMS = $(test_programs) test_libs = \ $(noinst_LTLIBRARIES) \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_istream_dot_SOURCES = test-istream-dot.c test_istream_dot_LDADD = $(test_libs) test_istream_dot_DEPENDENCIES = $(test_deps) test_istream_qp_decoder_SOURCES = test-istream-qp-decoder.c test_istream_qp_decoder_LDADD = $(test_libs) test_istream_qp_decoder_DEPENDENCIES = $(test_deps) test_istream_qp_encoder_SOURCES = test-istream-qp-encoder.c test_istream_qp_encoder_LDADD = $(test_libs) test_istream_qp_encoder_DEPENDENCIES = $(test_deps) test_istream_binary_converter_SOURCES = test-istream-binary-converter.c test_istream_binary_converter_LDADD = $(test_libs) test_istream_binary_converter_DEPENDENCIES = $(test_deps) test_istream_attachment_SOURCES = test-istream-attachment.c test_istream_attachment_LDADD = $(test_libs) test_istream_attachment_DEPENDENCIES = $(test_deps) test_istream_header_filter_SOURCES = test-istream-header-filter.c test_istream_header_filter_LDADD = $(test_libs) test_istream_header_filter_DEPENDENCIES = $(test_deps) test_mbox_from_SOURCES = test-mbox-from.c test_mbox_from_LDADD = $(test_libs) test_mbox_from_DEPENDENCIES = $(test_deps) test_message_address_SOURCES = test-message-address.c test_message_address_LDADD = $(test_libs) test_message_address_DEPENDENCIES = $(test_deps) test_message_date_SOURCES = test-message-date.c test_message_date_LDADD = $(test_libs) test_message_date_DEPENDENCIES = $(test_deps) test_message_decoder_SOURCES = test-message-decoder.c test_message_decoder_LDADD = $(test_libs) ../lib-charset/libcharset.la test_message_decoder_DEPENDENCIES = $(test_deps) ../lib-charset/libcharset.la test_message_header_decode_SOURCES = test-message-header-decode.c test_message_header_decode_LDADD = $(test_libs) test_message_header_decode_DEPENDENCIES = $(test_deps) test_message_header_encode_SOURCES = test-message-header-encode.c test_message_header_encode_LDADD = $(test_libs) test_message_header_encode_DEPENDENCIES = $(test_deps) test_message_header_hash_SOURCES = test-message-header-hash.c test_message_header_hash_LDADD = $(test_libs) test_message_header_hash_DEPENDENCIES = $(test_deps) test_message_header_parser_SOURCES = test-message-header-parser.c test_message_header_parser_LDADD = $(test_libs) test_message_header_parser_DEPENDENCIES = $(test_deps) test_message_id_SOURCES = test-message-id.c test_message_id_LDADD = $(test_libs) test_message_id_DEPENDENCIES = $(test_deps) test_message_parser_SOURCES = test-message-parser.c test_message_parser_LDADD = $(test_libs) test_message_parser_DEPENDENCIES = $(test_deps) test_message_part_SOURCES = test-message-part.c test_message_part_LDADD = $(test_libs) test_message_part_DEPENDENCIES = $(test_deps) test_message_search_SOURCES = test-message-search.c test_message_search_LDADD = $(test_libs) ../lib-charset/libcharset.la test_message_search_DEPENDENCIES = $(test_deps) ../lib-charset/libcharset.la test_message_snippet_SOURCES = test-message-snippet.c test_message_snippet_LDADD = $(test_message_decoder_LDADD) test_message_snippet_DEPENDENCIES = $(test_deps) test_mail_html2text_SOURCES = test-mail-html2text.c test_mail_html2text_LDADD = $(test_libs) test_mail_html2text_DEPENDENCIES = $(test_deps) test_ostream_dot_SOURCES = test-ostream-dot.c test_ostream_dot_LDADD = $(test_libs) test_ostream_dot_DEPENDENCIES = $(test_deps) test_qp_decoder_SOURCES = test-qp-decoder.c test_qp_decoder_LDADD = $(test_libs) test_qp_decoder_DEPENDENCIES = $(test_deps) test_qp_encoder_SOURCES = test-qp-encoder.c test_qp_encoder_LDADD = $(test_libs) test_qp_encoder_DEPENDENCIES = $(test_deps) test_quoted_printable_SOURCES = test-quoted-printable.c test_quoted_printable_LDADD = $(test_libs) test_quoted_printable_DEPENDENCIES = $(test_deps) test_rfc2231_parser_SOURCES = test-rfc2231-parser.c test_rfc2231_parser_LDADD = $(test_libs) test_rfc2231_parser_DEPENDENCIES = $(test_deps) test_rfc822_parser_SOURCES = test-rfc822-parser.c test_rfc822_parser_LDADD = $(test_libs) test_rfc822_parser_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-mail/message-header-decode.c0000644000175000017500000001036013165463624017053 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "buffer.h" #include "unichar.h" #include "charset-utf8.h" #include "quoted-printable.h" #include "message-header-decode.h" static size_t message_header_decode_encoded(const unsigned char *data, size_t size, buffer_t *decodebuf, size_t *charsetlen_r) { #define QCOUNT 3 unsigned int num = 0; size_t i, start_pos[QCOUNT]; /* data should contain "charset?encoding?text?=" */ for (i = 0; i < size; i++) { if (data[i] == '?') { start_pos[num++] = i; if (num == QCOUNT) break; } } if (i+1 >= size || data[i+1] != '=') { /* invalid block */ return 0; } buffer_append(decodebuf, data, start_pos[0]); buffer_append_c(decodebuf, '\0'); *charsetlen_r = decodebuf->used; switch (data[start_pos[0]+1]) { case 'q': case 'Q': if (quoted_printable_q_decode(data + start_pos[1] + 1, start_pos[2] - start_pos[1] - 1, decodebuf) < 0) { /* we skipped over some invalid data */ } break; case 'b': case 'B': if (base64_decode(data + start_pos[1] + 1, start_pos[2] - start_pos[1] - 1, NULL, decodebuf) < 0) { /* contains invalid data. show what we got so far. */ } break; default: /* unknown encoding */ return 0; } return start_pos[2] + 2; } static bool is_only_lwsp(const unsigned char *data, size_t size) { size_t i; for (i = 0; i < size; i++) { if (!(data[i] == ' ' || data[i] == '\t' || data[i] == '\r' || data[i] == '\n')) return FALSE; } return TRUE; } void message_header_decode(const unsigned char *data, size_t size, message_header_decode_callback_t *callback, void *context) { buffer_t *decodebuf = NULL; size_t charsetlen = 0; size_t pos, start_pos, ret; /* =?charset?Q|B?text?= */ start_pos = 0; for (pos = 0; pos + 1 < size; ) { if (data[pos] != '=' || data[pos+1] != '?') { pos++; continue; } /* encoded string beginning */ if (pos != start_pos && !is_only_lwsp(data+start_pos, pos-start_pos)) { /* send the unencoded data so far */ if (!callback(data + start_pos, pos - start_pos, NULL, context)) { start_pos = size; break; } } if (decodebuf == NULL) { decodebuf = buffer_create_dynamic(default_pool, size - pos); } else { buffer_set_used_size(decodebuf, 0); } pos += 2; ret = message_header_decode_encoded(data + pos, size - pos, decodebuf, &charsetlen); if (ret == 0) { start_pos = pos-2; continue; } pos += ret; if (decodebuf->used > charsetlen) { /* decodebuf contains NUL */ if (!callback(CONST_PTR_OFFSET(decodebuf->data, charsetlen), decodebuf->used - charsetlen, decodebuf->data, context)) { start_pos = size; break; } } start_pos = pos; } if (size != start_pos) { i_assert(size > start_pos); (void)callback(data + start_pos, size - start_pos, NULL, context); } if (decodebuf != NULL) buffer_free(&decodebuf); } struct decode_utf8_context { buffer_t *dest; normalizer_func_t *normalizer; unsigned int changed:1; }; static bool decode_utf8_callback(const unsigned char *data, size_t size, const char *charset, void *context) { struct decode_utf8_context *ctx = context; struct charset_translation *t; if (charset == NULL || charset_is_utf8(charset)) { /* ASCII / UTF-8 */ if (ctx->normalizer != NULL) { (void)ctx->normalizer(data, size, ctx->dest); } else { if (uni_utf8_get_valid_data(data, size, ctx->dest)) buffer_append(ctx->dest, data, size); } return TRUE; } if (charset_to_utf8_begin(charset, ctx->normalizer, &t) < 0) { /* data probably still contains some valid ASCII characters. append them. */ if (uni_utf8_get_valid_data(data, size, ctx->dest)) buffer_append(ctx->dest, data, size); return TRUE; } /* ignore any errors */ (void)charset_to_utf8(t, data, &size, ctx->dest); charset_to_utf8_end(&t); return TRUE; } void message_header_decode_utf8(const unsigned char *data, size_t size, buffer_t *dest, normalizer_func_t *normalizer) { struct decode_utf8_context ctx; i_zero(&ctx); ctx.dest = dest; ctx.normalizer = normalizer; message_header_decode(data, size, decode_utf8_callback, &ctx); } dovecot-2.2.33.2/src/lib-mail/message-part-data.h0000644000175000017500000000350513165463624016267 00000000000000#ifndef MESSAGE_PART_DATA_H #define MESSAGE_PART_DATA_H #include "message-part.h" #define MESSAGE_PART_DEFAULT_CHARSET "us-ascii" struct message_header_line; struct message_part_param { const char *name; const char *value; }; struct message_part_envelope { const char *date, *subject; struct message_address *from, *sender, *reply_to; struct message_address *to, *cc, *bcc; const char *in_reply_to, *message_id; }; struct message_part_data { const char *content_type, *content_subtype; const struct message_part_param *content_type_params; unsigned int content_type_params_count; const char *content_transfer_encoding; const char *content_id; const char *content_description; const char *content_disposition; const struct message_part_param *content_disposition_params; unsigned int content_disposition_params_count; const char *content_md5; const char *const *content_language; const char *content_location; struct message_part_envelope *envelope; }; extern const char *message_part_envelope_headers[]; /* * */ /* Returns TRUE if this message part has content-type "text/plain", chaset "us-ascii" and content-tranfer-encoding "7bit" */ bool message_part_data_is_plain_7bit(const struct message_part *part) ATTR_PURE; /* Returns TRUE if this message part has a filename. The filename is returned in filename_r. */ bool message_part_data_get_filename(const struct message_part *part, const char **filename_r); /* * Header parsing */ /* Update envelope data based from given header field */ void message_part_envelope_parse_from_header(pool_t pool, struct message_part_envelope **_data, struct message_header_line *hdr); /* Parse a single header. Note that this modifies part->context. */ void message_part_data_parse_from_header(pool_t pool, struct message_part *part, struct message_header_line *hdr); #endif dovecot-2.2.33.2/src/lib-auth/0002755000175000017500000000000013172375610012710 500000000000000dovecot-2.2.33.2/src/lib-auth/Makefile.in0000644000175000017500000005457513172375572014722 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-auth ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libauth_la_LIBADD = am_libauth_la_OBJECTS = auth-client.lo auth-client-request.lo \ auth-master.lo auth-server-connection.lo libauth_la_OBJECTS = $(am_libauth_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libauth_la_SOURCES) DIST_SOURCES = $(libauth_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libauth.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master libauth_la_SOURCES = \ auth-client.c \ auth-client-request.c \ auth-master.c \ auth-server-connection.c headers = \ auth-client.h \ auth-client-interface.h \ auth-client-private.h \ auth-client-request.h \ auth-master.h \ auth-server-connection.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-auth/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-auth/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libauth.la: $(libauth_la_OBJECTS) $(libauth_la_DEPENDENCIES) $(EXTRA_libauth_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libauth_la_OBJECTS) $(libauth_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-client-request.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-master.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-server-connection.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-auth/auth-client-private.h0000644000175000017500000000054613165463624016676 00000000000000#ifndef AUTH_CLIENT_PRIVATE_H #define AUTH_CLIENT_PRIVATE_H #include "auth-client.h" struct auth_client { char *auth_socket_path; unsigned int client_pid; struct auth_server_connection *conn; auth_connect_notify_callback_t *connect_notify_callback; void *connect_notify_context; unsigned int request_id_counter; unsigned int debug:1; }; #endif dovecot-2.2.33.2/src/lib-auth/auth-master.c0000644000175000017500000004420313165463624015234 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "array.h" #include "ioloop.h" #include "eacces-error.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "master-interface.h" #include "auth-master.h" #include #define AUTH_PROTOCOL_MAJOR 1 #define AUTH_PROTOCOL_MINOR 0 #define AUTH_MASTER_IDLE_SECS 60 #define MAX_INBUF_SIZE 8192 #define MAX_OUTBUF_SIZE 1024 #define DEFAULT_USERDB_LOOKUP_PREFIX "userdb lookup" struct auth_master_connection { char *auth_socket_path; enum auth_master_flags flags; int fd; struct ioloop *ioloop, *prev_ioloop; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; const char *prefix; unsigned int request_counter; bool (*reply_callback)(const char *cmd, const char *const *args, void *context); void *reply_context; unsigned int sent_handshake:1; unsigned int handshaked:1; unsigned int aborted:1; }; struct auth_master_lookup_ctx { struct auth_master_connection *conn; const char *user; const char *expected_reply; int return_value; pool_t pool; const char **fields; }; struct auth_master_user_list_ctx { struct auth_master_connection *conn; string_t *username; bool finished; bool failed; }; static void auth_input(struct auth_master_connection *conn); struct auth_master_connection * auth_master_init(const char *auth_socket_path, enum auth_master_flags flags) { struct auth_master_connection *conn; conn = i_new(struct auth_master_connection, 1); conn->auth_socket_path = i_strdup(auth_socket_path); conn->fd = -1; conn->flags = flags; conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX; return conn; } static void auth_connection_close(struct auth_master_connection *conn) { if (conn->to != NULL) timeout_remove(&conn->to); if (conn->fd != -1) { if (close(conn->fd) < 0) i_error("close(%s) failed: %m", conn->auth_socket_path); conn->fd = -1; } conn->sent_handshake = FALSE; conn->handshaked = FALSE; } void auth_master_deinit(struct auth_master_connection **_conn) { struct auth_master_connection *conn = *_conn; *_conn = NULL; auth_connection_close(conn); i_free(conn->auth_socket_path); i_free(conn); } const char *auth_master_get_socket_path(struct auth_master_connection *conn) { return conn->auth_socket_path; } static void auth_request_lookup_abort(struct auth_master_connection *conn) { io_loop_stop(conn->ioloop); conn->aborted = TRUE; } static int auth_input_handshake(struct auth_master_connection *conn) { const char *line, *const *tmp; while ((line = i_stream_next_line(conn->input)) != NULL) { tmp = t_strsplit_tabescaped(line); if (strcmp(tmp[0], "VERSION") == 0 && tmp[1] != NULL && tmp[2] != NULL) { if (strcmp(tmp[1], dec2str(AUTH_PROTOCOL_MAJOR)) != 0) { i_error("userdb lookup: " "Auth protocol version mismatch " "(%s vs %d)", tmp[1], AUTH_PROTOCOL_MAJOR); auth_request_lookup_abort(conn); return -1; } } else if (strcmp(tmp[0], "SPID") == 0) { conn->handshaked = TRUE; break; } } return 0; } static int parse_reply(const char *cmd, const char *const *args, const char *expected_reply, const char *user, bool debug) { if (strcmp(cmd, expected_reply) == 0) return 1; if (strcmp(cmd, "NOTFOUND") == 0) return 0; if (strcmp(cmd, "FAIL") == 0) { if (*args == NULL) { i_error("user %s: Auth %s lookup failed", user, expected_reply); } else if (debug) { i_debug("user %s: Auth %s lookup returned temporary failure: %s", user, expected_reply, *args); } return -2; } i_error("Unknown reply: %s", cmd); return -1; } static const char *const *args_hide_passwords(const char *const *args) { ARRAY_TYPE(const_string) new_args; const char *p, *p2; unsigned int i; /* if there are any keys that contain "pass" string */ for (i = 0; args[i] != NULL; i++) { p = strstr(args[i], "pass"); if (p != NULL && p < strchr(args[i], '=')) break; } if (args[i] == NULL) return args; /* there are. replace their values with */ t_array_init(&new_args, i + 16); array_append(&new_args, args, i); for (; args[i] != NULL; i++) { p = strstr(args[i], "pass"); p2 = strchr(args[i], '='); if (p != NULL && p < p2) { p = t_strconcat(t_strdup_until(args[i], p2), "=", NULL); array_append(&new_args, &p, 1); } else { array_append(&new_args, &args[i], 1); } } array_append_zero(&new_args); return array_idx(&new_args, 0); } static bool auth_lookup_reply_callback(const char *cmd, const char *const *args, void *context) { struct auth_master_lookup_ctx *ctx = context; unsigned int i, len; bool debug = (ctx->conn->flags & AUTH_MASTER_FLAG_DEBUG) != 0; io_loop_stop(ctx->conn->ioloop); ctx->return_value = parse_reply(cmd, args, ctx->expected_reply, ctx->user, debug); len = str_array_length(args); if (ctx->return_value >= 0) { ctx->fields = p_new(ctx->pool, const char *, len + 1); for (i = 0; i < len; i++) ctx->fields[i] = str_tabunescape(p_strdup(ctx->pool, args[i])); } else { /* put the reason string into first field */ ctx->fields = p_new(ctx->pool, const char *, 2); for (i = 0; i < len; i++) { if (strncmp(args[i], "reason=", 7) == 0) { ctx->fields[0] = p_strdup(ctx->pool, args[i] + 7); break; } } } if (debug) { args = args_hide_passwords(args); i_debug("auth %s input: %s", ctx->expected_reply, t_strarray_join(args, " ")); } return TRUE; } static bool auth_handle_line(struct auth_master_connection *conn, const char *line) { const char *cmd, *const *args, *id, *wanted_id; args = t_strsplit_tab(line); cmd = *args; args++; if (*args == NULL) id = ""; else { id = *args; args++; } wanted_id = dec2str(conn->request_counter); if (strcmp(id, wanted_id) == 0) return conn->reply_callback(cmd, args, conn->reply_context); if (strcmp(cmd, "CUID") == 0) { i_error("%s: %s is an auth client socket. " "It should be a master socket.", conn->prefix, conn->auth_socket_path); } else { i_error("%s: BUG: Unexpected input: %s", conn->prefix, line); } auth_request_lookup_abort(conn); return FALSE; } static void auth_input(struct auth_master_connection *conn) { const char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ i_error("%s: Disconnected unexpectedly", conn->prefix); auth_request_lookup_abort(conn); return; case -2: /* buffer full */ i_error("%s: BUG: Received more than %d bytes", conn->prefix, MAX_INBUF_SIZE); auth_request_lookup_abort(conn); return; } if (!conn->handshaked) { if (auth_input_handshake(conn) < 0) return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { io_loop_set_current(conn->prev_ioloop); ret = auth_handle_line(conn, line); io_loop_set_current(conn->ioloop); } T_END; if (!ret) return; } } static int auth_master_connect(struct auth_master_connection *conn) { int fd; i_assert(conn->fd == -1); /* max. 1 second wait here. */ fd = net_connect_unix_with_retries(conn->auth_socket_path, 1000); if (fd == -1) { if (errno == EACCES) { i_error("userdb lookup: %s", eacces_error_get("connect", conn->auth_socket_path)); } else { i_error("userdb lookup: connect(%s) failed: %m", conn->auth_socket_path); } return -1; } conn->fd = fd; return 0; } static void auth_request_timeout(struct auth_master_connection *conn) { if (!conn->handshaked) i_error("%s: Connecting timed out", conn->prefix); else i_error("%s: Request timed out", conn->prefix); auth_request_lookup_abort(conn); } static void auth_idle_timeout(struct auth_master_connection *conn) { auth_connection_close(conn); } static void auth_master_set_io(struct auth_master_connection *conn) { if (conn->ioloop != NULL) return; if (conn->to != NULL) timeout_remove(&conn->to); conn->prev_ioloop = current_ioloop; conn->ioloop = io_loop_create(); conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE); conn->io = io_add(conn->fd, IO_READ, auth_input, conn); conn->to = timeout_add(1000*MASTER_AUTH_LOOKUP_TIMEOUT_SECS, auth_request_timeout, conn); lib_signals_reset_ioloop(); } static void auth_master_unset_io(struct auth_master_connection *conn) { if (conn->prev_ioloop != NULL) { io_loop_set_current(conn->prev_ioloop); lib_signals_reset_ioloop(); } if (conn->ioloop != NULL) { io_loop_set_current(conn->ioloop); timeout_remove(&conn->to); io_remove(&conn->io); i_stream_unref(&conn->input); o_stream_unref(&conn->output); io_loop_destroy(&conn->ioloop); } if ((conn->flags & AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT) == 0) { if (conn->prev_ioloop == NULL) auth_connection_close(conn); else { i_assert(conn->to == NULL); conn->to = timeout_add(1000*AUTH_MASTER_IDLE_SECS, auth_idle_timeout, conn); } } } static bool is_valid_string(const char *str) { const char *p; /* make sure we're not sending any characters that have a special meaning. */ for (p = str; *p != '\0'; p++) { if (*p == '\t' || *p == '\n' || *p == '\r') return FALSE; } return TRUE; } static int auth_master_run_cmd_pre(struct auth_master_connection *conn, const char *cmd) { const char *str; if (conn->fd == -1) { if (auth_master_connect(conn) < 0) return -1; i_assert(conn->fd != -1); } auth_master_set_io(conn); o_stream_cork(conn->output); if (!conn->sent_handshake) { str = t_strdup_printf("VERSION\t%d\t%d\n", AUTH_PROTOCOL_MAJOR, AUTH_PROTOCOL_MINOR); o_stream_nsend_str(conn->output, str); conn->sent_handshake = TRUE; } o_stream_nsend_str(conn->output, cmd); o_stream_uncork(conn->output); if (o_stream_nfinish(conn->output) < 0) { i_error("write(auth socket) failed: %s", o_stream_get_error(conn->output)); auth_master_unset_io(conn); auth_connection_close(conn); return -1; } return 0; } static int auth_master_run_cmd_post(struct auth_master_connection *conn) { auth_master_unset_io(conn); if (conn->aborted) { conn->aborted = FALSE; auth_connection_close(conn); return -1; } return 0; } static int auth_master_run_cmd(struct auth_master_connection *conn, const char *cmd) { if (auth_master_run_cmd_pre(conn, cmd) < 0) return -1; io_loop_run(conn->ioloop); return auth_master_run_cmd_post(conn); } static unsigned int auth_master_next_request_id(struct auth_master_connection *conn) { if (++conn->request_counter == 0) { /* avoid zero */ conn->request_counter++; } return conn->request_counter; } static void auth_user_info_export(string_t *str, const struct auth_user_info *info) { if (info->service != NULL) { str_append(str, "\tservice="); str_append(str, info->service); } if (info->local_ip.family != 0) str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip)); if (info->local_port != 0) str_printfa(str, "\tlport=%d", info->local_port); if (info->remote_ip.family != 0) str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip)); if (info->remote_port != 0) str_printfa(str, "\trport=%d", info->remote_port); if (info->debug) str_append(str, "\tdebug"); } int auth_master_user_lookup(struct auth_master_connection *conn, const char *user, const struct auth_user_info *info, pool_t pool, const char **username_r, const char *const **fields_r) { struct auth_master_lookup_ctx ctx; string_t *str; if (!is_valid_string(user) || !is_valid_string(info->service)) { /* non-allowed characters, the user can't exist */ *username_r = NULL; *fields_r = NULL; return 0; } i_zero(&ctx); ctx.conn = conn; ctx.return_value = -1; ctx.pool = pool; ctx.expected_reply = "USER"; ctx.user = user; conn->reply_callback = auth_lookup_reply_callback; conn->reply_context = &ctx; str = t_str_new(128); str_printfa(str, "USER\t%u\t%s", auth_master_next_request_id(conn), user); auth_user_info_export(str, info); str_append_c(str, '\n'); conn->prefix = t_strdup_printf("userdb lookup(%s)", user); (void)auth_master_run_cmd(conn, str_c(str)); conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX; if (ctx.return_value <= 0 || ctx.fields[0] == NULL) { *username_r = NULL; *fields_r = ctx.fields != NULL ? ctx.fields : p_new(pool, const char *, 1); if (ctx.return_value > 0) { i_error("Userdb lookup didn't return username"); ctx.return_value = -2; } } else { *username_r = ctx.fields[0]; *fields_r = ctx.fields + 1; } conn->reply_context = NULL; return ctx.return_value; } void auth_user_fields_parse(const char *const *fields, pool_t pool, struct auth_user_reply *reply_r) { i_zero(reply_r); reply_r->uid = (uid_t)-1; reply_r->gid = (gid_t)-1; p_array_init(&reply_r->extra_fields, pool, 64); for (; *fields != NULL; fields++) { if (strncmp(*fields, "uid=", 4) == 0) { if (str_to_uid(*fields + 4, &reply_r->uid) < 0) i_error("Invalid uid in reply"); } else if (strncmp(*fields, "gid=", 4) == 0) { if (str_to_gid(*fields + 4, &reply_r->gid) < 0) i_error("Invalid gid in reply"); } else if (strncmp(*fields, "home=", 5) == 0) reply_r->home = p_strdup(pool, *fields + 5); else if (strncmp(*fields, "chroot=", 7) == 0) reply_r->chroot = p_strdup(pool, *fields + 7); else if (strcmp(*fields, "anonymous") == 0) reply_r->anonymous = TRUE; else { const char *field = p_strdup(pool, *fields); array_append(&reply_r->extra_fields, &field, 1); } } } int auth_master_pass_lookup(struct auth_master_connection *conn, const char *user, const struct auth_user_info *info, pool_t pool, const char *const **fields_r) { struct auth_master_lookup_ctx ctx; string_t *str; if (!is_valid_string(user) || !is_valid_string(info->service)) { /* non-allowed characters, the user can't exist */ *fields_r = NULL; return 0; } i_zero(&ctx); ctx.conn = conn; ctx.return_value = -1; ctx.pool = pool; ctx.expected_reply = "PASS"; ctx.user = user; conn->reply_callback = auth_lookup_reply_callback; conn->reply_context = &ctx; str = t_str_new(128); str_printfa(str, "PASS\t%u\t%s", auth_master_next_request_id(conn), user); auth_user_info_export(str, info); str_append_c(str, '\n'); conn->prefix = t_strdup_printf("passdb lookup(%s)", user); (void)auth_master_run_cmd(conn, str_c(str)); conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX; *fields_r = ctx.fields != NULL ? ctx.fields : p_new(pool, const char *, 1); conn->reply_context = NULL; return ctx.return_value; } struct auth_master_cache_ctx { struct auth_master_connection *conn; unsigned int count; bool failed; }; static bool auth_cache_flush_reply_callback(const char *cmd, const char *const *args, void *context) { struct auth_master_cache_ctx *ctx = context; if (strcmp(cmd, "OK") != 0) ctx->failed = TRUE; else if (args[0] == NULL || str_to_uint(args[0], &ctx->count) < 0) ctx->failed = TRUE; io_loop_stop(ctx->conn->ioloop); return TRUE; } int auth_master_cache_flush(struct auth_master_connection *conn, const char *const *users, unsigned int *count_r) { struct auth_master_cache_ctx ctx; string_t *str; i_zero(&ctx); ctx.conn = conn; conn->reply_callback = auth_cache_flush_reply_callback; conn->reply_context = &ctx; str = t_str_new(128); str_printfa(str, "CACHE-FLUSH\t%u", auth_master_next_request_id(conn)); if (users != NULL) { for (; *users != NULL; users++) { str_append_c(str, '\t'); str_append_tabescaped(str, *users); } } str_append_c(str, '\n'); conn->prefix = "auth cache flush"; (void)auth_master_run_cmd(conn, str_c(str)); conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX; conn->reply_context = NULL; *count_r = ctx.count; return ctx.failed ? -1 : 0; } static bool auth_user_list_reply_callback(const char *cmd, const char *const *args, void *context) { struct auth_master_user_list_ctx *ctx = context; timeout_reset(ctx->conn->to); str_truncate(ctx->username, 0); io_loop_stop(ctx->conn->ioloop); if (strcmp(cmd, "DONE") == 0) { if (args[0] != NULL && strcmp(args[0], "fail") == 0) { i_error("User listing returned failure"); ctx->failed = TRUE; } ctx->finished = TRUE; } else if (strcmp(cmd, "LIST") == 0 && args[0] != NULL) { /* we'll just read all the users into memory. otherwise we'd have to use a separate connection for listing and there's a higher chance of a failure since the connection could be open to dovecot-auth for a long time. */ str_append(ctx->username, args[0]); } else { i_error("User listing returned invalid input"); ctx->failed = TRUE; } return FALSE; } struct auth_master_user_list_ctx * auth_master_user_list_init(struct auth_master_connection *conn, const char *user_mask, const struct auth_user_info *info) { struct auth_master_user_list_ctx *ctx; string_t *str; ctx = i_new(struct auth_master_user_list_ctx, 1); ctx->conn = conn; ctx->username = str_new(default_pool, 128); conn->reply_callback = auth_user_list_reply_callback; conn->reply_context = ctx; str = t_str_new(128); str_printfa(str, "LIST\t%u", auth_master_next_request_id(conn)); if (*user_mask != '\0') str_printfa(str, "\tuser=%s", user_mask); if (info != NULL) auth_user_info_export(str, info); str_append_c(str, '\n'); conn->prefix = "userdb list"; if (auth_master_run_cmd_pre(conn, str_c(str)) < 0) ctx->failed = TRUE; if (conn->prev_ioloop != NULL) io_loop_set_current(conn->prev_ioloop); conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX; return ctx; } const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx) { const char *line; if (ctx->conn->input == NULL) return NULL; /* try to read already buffered input */ line = i_stream_next_line(ctx->conn->input); if (line != NULL) { T_BEGIN { auth_handle_line(ctx->conn, line); } T_END; } else { /* wait for more data */ io_loop_set_current(ctx->conn->ioloop); io_loop_run(ctx->conn->ioloop); io_loop_set_current(ctx->conn->prev_ioloop); } if (ctx->finished || ctx->failed || ctx->conn->aborted) return NULL; return str_c(ctx->username); } int auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx) { struct auth_master_user_list_ctx *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; auth_master_run_cmd_post(ctx->conn); str_free(&ctx->username); i_free(ctx); return ret; } dovecot-2.2.33.2/src/lib-auth/auth-master.h0000644000175000017500000000474613165463624015251 00000000000000#ifndef AUTH_MASTER_H #define AUTH_MASTER_H #include "net.h" enum auth_master_flags { /* Enable logging debug information */ AUTH_MASTER_FLAG_DEBUG = 0x01, /* Don't disconnect from auth socket when idling */ AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT = 0x02 }; struct auth_user_info { const char *service; struct ip_addr local_ip, remote_ip; in_port_t local_port, remote_port; bool debug; }; struct auth_user_reply { uid_t uid; gid_t gid; const char *home, *chroot; ARRAY_TYPE(const_string) extra_fields; unsigned int anonymous:1; }; struct auth_master_connection * auth_master_init(const char *auth_socket_path, enum auth_master_flags flags); void auth_master_deinit(struct auth_master_connection **conn); /* Returns the auth_socket_path */ const char *auth_master_get_socket_path(struct auth_master_connection *conn); /* Do a USER lookup. Returns -2 = user-specific error, -1 = internal error, 0 = user not found, 1 = ok. When returning -1 and fields[0] isn't NULL, it contains an error message that should be shown to user. */ int auth_master_user_lookup(struct auth_master_connection *conn, const char *user, const struct auth_user_info *info, pool_t pool, const char **username_r, const char *const **fields_r); /* Do a PASS lookup (the actual password isn't returned). */ int auth_master_pass_lookup(struct auth_master_connection *conn, const char *user, const struct auth_user_info *info, pool_t pool, const char *const **fields_r); /* Flush authentication cache for everyone (users=NULL) or only for specified users. Returns number of users flushed from cache. */ int auth_master_cache_flush(struct auth_master_connection *conn, const char *const *users, unsigned int *count_r); /* Parse userdb extra fields into auth_user_reply structure. */ void auth_user_fields_parse(const char *const *fields, pool_t pool, struct auth_user_reply *reply_r); /* Iterate through all users. If user_mask is non-NULL, it contains a string with wildcards ('*', '?') that the auth server MAY use to limit what users are returned (but it may as well return all users anyway). */ struct auth_master_user_list_ctx * auth_master_user_list_init(struct auth_master_connection *conn, const char *user_mask, const struct auth_user_info *info) ATTR_NULL(3); const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx); /* Returns -1 if anything failed, 0 if ok */ int auth_master_user_list_deinit(struct auth_master_user_list_ctx **ctx); #endif dovecot-2.2.33.2/src/lib-auth/auth-client-request.c0000644000175000017500000001631413165463624016707 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "auth-client-private.h" #include "auth-server-connection.h" #include "auth-client-request.h" struct auth_client_request { pool_t pool; struct auth_server_connection *conn; unsigned int id; time_t created; struct auth_request_info request_info; auth_request_callback_t *callback; void *context; }; static void auth_server_send_new_request(struct auth_server_connection *conn, struct auth_client_request *request) { struct auth_request_info *info = &request->request_info; string_t *str; str = t_str_new(512); str_printfa(str, "AUTH\t%u\t", request->id); str_append_tabescaped(str, info->mech); str_append(str, "\tservice="); str_append_tabescaped(str, info->service); if ((info->flags & AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP) != 0) str_append(str, "\tfinal-resp-ok"); if ((info->flags & AUTH_REQUEST_FLAG_SECURED) != 0) str_append(str, "\tsecured"); if ((info->flags & AUTH_REQUEST_FLAG_NO_PENALTY) != 0) str_append(str, "\tno-penalty"); if ((info->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0) str_append(str, "\tvalid-client-cert"); if ((info->flags & AUTH_REQUEST_FLAG_DEBUG) != 0) str_append(str, "\tdebug"); if (info->session_id != NULL) { str_append(str, "\tsession="); str_append_tabescaped(str, info->session_id); } if (info->cert_username != NULL) { str_append(str, "\tcert_username="); str_append_tabescaped(str, info->cert_username); } if (info->local_ip.family != 0) str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip)); if (info->remote_ip.family != 0) str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip)); if (info->local_port != 0) str_printfa(str, "\tlport=%u", info->local_port); if (info->remote_port != 0) str_printfa(str, "\trport=%u", info->remote_port); /* send the real_* variants only when they differ from the unreal ones */ if (info->real_local_ip.family != 0 && !net_ip_compare(&info->real_local_ip, &info->local_ip)) { str_printfa(str, "\treal_lip=%s", net_ip2addr(&info->real_local_ip)); } if (info->real_remote_ip.family != 0 && !net_ip_compare(&info->real_remote_ip, &info->remote_ip)) { str_printfa(str, "\treal_rip=%s", net_ip2addr(&info->real_remote_ip)); } if (info->real_local_port != 0 && info->real_local_port != info->local_port) str_printfa(str, "\treal_lport=%u", info->real_local_port); if (info->real_remote_port != 0 && info->real_remote_port != info->remote_port) str_printfa(str, "\treal_rport=%u", info->real_remote_port); if (info->local_name != NULL && *info->local_name != '\0') { str_append(str, "\tlocal_name="); str_append_tabescaped(str, info->local_name); } if (info->client_id != NULL && *info->client_id != '\0') { str_append(str, "\tclient_id="); str_append_tabescaped(str, info->client_id); } if (info->forward_fields != NULL && *info->forward_fields != '\0') { str_append(str, "\tforward_fields="); str_append_tabescaped(str, info->forward_fields); } if (info->initial_resp_base64 != NULL) { str_append(str, "\tresp="); str_append_tabescaped(str, info->initial_resp_base64); } str_append_c(str, '\n'); if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) i_error("Error sending request to auth server: %m"); } struct auth_client_request * auth_client_request_new(struct auth_client *client, const struct auth_request_info *request_info, auth_request_callback_t *callback, void *context) { struct auth_client_request *request; pool_t pool; pool = pool_alloconly_create("auth client request", 512); request = p_new(pool, struct auth_client_request, 1); request->pool = pool; request->conn = client->conn; request->request_info = *request_info; request->request_info.mech = p_strdup(pool, request_info->mech); request->request_info.service = p_strdup(pool, request_info->service); request->request_info.session_id = p_strdup_empty(pool, request_info->session_id); request->request_info.cert_username = p_strdup_empty(pool, request_info->cert_username); request->request_info.initial_resp_base64 = p_strdup_empty(pool, request_info->initial_resp_base64); request->callback = callback; request->context = context; request->id = auth_server_connection_add_request(request->conn, request); request->created = ioloop_time; T_BEGIN { auth_server_send_new_request(request->conn, request); } T_END; return request; } void auth_client_request_continue(struct auth_client_request *request, const char *data_base64) { struct const_iovec iov[3]; const char *prefix; prefix = t_strdup_printf("CONT\t%u\t", request->id); iov[0].iov_base = prefix; iov[0].iov_len = strlen(prefix); iov[1].iov_base = data_base64; iov[1].iov_len = strlen(data_base64); iov[2].iov_base = "\n"; iov[2].iov_len = 1; if (o_stream_sendv(request->conn->output, iov, 3) < 0) i_error("Error sending continue request to auth server: %m"); } static void ATTR_NULL(3, 4) call_callback(struct auth_client_request *request, enum auth_request_status status, const char *data_base64, const char *const *args) { auth_request_callback_t *callback = request->callback; if (status != AUTH_REQUEST_STATUS_CONTINUE) request->callback = NULL; callback(request, status, data_base64, args, request->context); } void auth_client_request_abort(struct auth_client_request **_request) { struct auth_client_request *request = *_request; *_request = NULL; auth_client_send_cancel(request->conn->client, request->id); call_callback(request, AUTH_REQUEST_STATUS_ABORT, NULL, NULL); } unsigned int auth_client_request_get_id(struct auth_client_request *request) { return request->id; } unsigned int auth_client_request_get_server_pid(struct auth_client_request *request) { return request->conn->server_pid; } const char *auth_client_request_get_cookie(struct auth_client_request *request) { return request->conn->cookie; } bool auth_client_request_is_aborted(struct auth_client_request *request) { return request->callback == NULL; } time_t auth_client_request_get_create_time(struct auth_client_request *request) { return request->created; } void auth_client_request_server_input(struct auth_client_request *request, enum auth_request_status status, const char *const *args) { const char *const *tmp, *base64_data = NULL; if (request->callback == NULL) { /* aborted already */ return; } switch (status) { case AUTH_REQUEST_STATUS_OK: for (tmp = args; *tmp != NULL; tmp++) { if (strncmp(*tmp, "resp=", 5) == 0) { base64_data = *tmp + 5; break; } } break; case AUTH_REQUEST_STATUS_CONTINUE: base64_data = args[0]; args = NULL; break; case AUTH_REQUEST_STATUS_FAIL: case AUTH_REQUEST_STATUS_INTERNAL_FAIL: case AUTH_REQUEST_STATUS_ABORT: break; } call_callback(request, status, base64_data, args); if (status != AUTH_REQUEST_STATUS_CONTINUE) pool_unref(&request->pool); } void auth_client_send_cancel(struct auth_client *client, unsigned int id) { const char *str = t_strdup_printf("CANCEL\t%u\n", id); if (o_stream_send_str(client->conn->output, str) < 0) i_error("Error sending request to auth server: %m"); } dovecot-2.2.33.2/src/lib-auth/auth-server-connection.c0000644000175000017500000003200313165463624017377 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "net.h" #include "strescape.h" #include "eacces-error.h" #include "auth-client-private.h" #include "auth-client-request.h" #include "auth-server-connection.h" #include #define AUTH_SERVER_CONN_MAX_LINE_LENGTH AUTH_CLIENT_MAX_LINE_LENGTH #define AUTH_HANDSHAKE_TIMEOUT (30*1000) #define AUTH_SERVER_RECONNECT_TIMEOUT_SECS 5 static void auth_server_connection_reconnect(struct auth_server_connection *conn, const char *disconnect_reason); static int auth_server_input_mech(struct auth_server_connection *conn, const char *const *args) { struct auth_mech_desc mech_desc; if (conn->handshake_received) { i_error("BUG: Authentication server already sent handshake"); return -1; } if (args[0] == NULL) { i_error("BUG: Authentication server sent broken MECH line"); return -1; } i_zero(&mech_desc); mech_desc.name = p_strdup(conn->pool, args[0]); if (strcmp(mech_desc.name, "PLAIN") == 0) conn->has_plain_mech = TRUE; for (args++; *args != NULL; args++) { if (strcmp(*args, "private") == 0) mech_desc.flags |= MECH_SEC_PRIVATE; else if (strcmp(*args, "anonymous") == 0) mech_desc.flags |= MECH_SEC_ANONYMOUS; else if (strcmp(*args, "plaintext") == 0) mech_desc.flags |= MECH_SEC_PLAINTEXT; else if (strcmp(*args, "dictionary") == 0) mech_desc.flags |= MECH_SEC_DICTIONARY; else if (strcmp(*args, "active") == 0) mech_desc.flags |= MECH_SEC_ACTIVE; else if (strcmp(*args, "forward-secrecy") == 0) mech_desc.flags |= MECH_SEC_FORWARD_SECRECY; else if (strcmp(*args, "mutual-auth") == 0) mech_desc.flags |= MECH_SEC_MUTUAL_AUTH; } array_append(&conn->available_auth_mechs, &mech_desc, 1); return 0; } static int auth_server_input_spid(struct auth_server_connection *conn, const char *const *args) { if (conn->handshake_received) { i_error("BUG: Authentication server already sent handshake"); return -1; } if (str_to_uint(args[0], &conn->server_pid) < 0) { i_error("BUG: Authentication server sent invalid PID"); return -1; } return 0; } static int auth_server_input_cuid(struct auth_server_connection *conn, const char *const *args) { if (conn->handshake_received) { i_error("BUG: Authentication server already sent handshake"); return -1; } if (args[0] == NULL || str_to_uint(args[0], &conn->connect_uid) < 0) { i_error("BUG: Authentication server sent broken CUID line"); return -1; } return 0; } static int auth_server_input_cookie(struct auth_server_connection *conn, const char *const *args) { if (conn->cookie != NULL) { i_error("BUG: Authentication server already sent cookie"); return -1; } conn->cookie = p_strdup(conn->pool, args[0]); return 0; } static int auth_server_input_done(struct auth_server_connection *conn) { if (array_count(&conn->available_auth_mechs) == 0) { i_error("BUG: Authentication server returned no mechanisms"); return -1; } if (conn->cookie == NULL) { i_error("BUG: Authentication server didn't send a cookie"); return -1; } if (conn->to != NULL) timeout_remove(&conn->to); conn->handshake_received = TRUE; if (conn->client->connect_notify_callback != NULL) { conn->client->connect_notify_callback(conn->client, TRUE, conn->client->connect_notify_context); } return 0; } static int auth_server_lookup_request(struct auth_server_connection *conn, const char *id_arg, bool remove, struct auth_client_request **request_r) { struct auth_client_request *request; unsigned int id; if (id_arg == NULL || str_to_uint(id_arg, &id) < 0) { i_error("BUG: Authentication server input missing ID"); return -1; } request = hash_table_lookup(conn->requests, POINTER_CAST(id)); if (request == NULL) { i_error("BUG: Authentication server sent unknown id %u", id); return -1; } if (remove || auth_client_request_is_aborted(request)) hash_table_remove(conn->requests, POINTER_CAST(id)); *request_r = request; return 0; } static int auth_server_input_ok(struct auth_server_connection *conn, const char *const *args) { struct auth_client_request *request; if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0) return -1; auth_client_request_server_input(request, AUTH_REQUEST_STATUS_OK, args + 1); return 0; } static int auth_server_input_cont(struct auth_server_connection *conn, const char *const *args) { struct auth_client_request *request; if (str_array_length(args) < 2) { i_error("BUG: Authentication server sent broken CONT line"); return -1; } if (auth_server_lookup_request(conn, args[0], FALSE, &request) < 0) return -1; auth_client_request_server_input(request, AUTH_REQUEST_STATUS_CONTINUE, args + 1); return 0; } static int auth_server_input_fail(struct auth_server_connection *conn, const char *const *args) { struct auth_client_request *request; if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0) return -1; auth_client_request_server_input(request, AUTH_REQUEST_STATUS_FAIL, args + 1); return 0; } static int auth_server_connection_input_line(struct auth_server_connection *conn, const char *line) { const char *const *args; if (conn->client->debug) i_debug("auth input: %s", line); args = t_strsplit_tabescaped(line); if (args[0] == NULL) { i_error("Auth server sent empty line"); return -1; } if (strcmp(args[0], "OK") == 0) return auth_server_input_ok(conn, args + 1); else if (strcmp(args[0], "CONT") == 0) return auth_server_input_cont(conn, args + 1); else if (strcmp(args[0], "FAIL") == 0) return auth_server_input_fail(conn, args + 1); else if (strcmp(args[0], "MECH") == 0) return auth_server_input_mech(conn, args + 1); else if (strcmp(args[0], "SPID") == 0) return auth_server_input_spid(conn, args + 1); else if (strcmp(args[0], "CUID") == 0) return auth_server_input_cuid(conn, args + 1); else if (strcmp(args[0], "COOKIE") == 0) return auth_server_input_cookie(conn, args + 1); else if (strcmp(args[0], "DONE") == 0) return auth_server_input_done(conn); else { i_error("Auth server sent unknown command: %s", args[0]); return -1; } } static void auth_server_connection_input(struct auth_server_connection *conn) { struct istream *input; const char *line, *error; int ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ error = conn->input->stream_errno != 0 ? strerror(conn->input->stream_errno) : "EOF"; auth_server_connection_reconnect(conn, error); return; case -2: /* buffer full - can't happen unless auth is buggy */ i_error("BUG: Auth server sent us more than %d bytes of data", AUTH_SERVER_CONN_MAX_LINE_LENGTH); auth_server_connection_disconnect(conn, "buffer full"); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication server not compatible with " "this client (mixed old and new binaries?)"); auth_server_connection_disconnect(conn, "incompatible server"); return; } conn->version_received = TRUE; } input = conn->input; i_stream_ref(input); while ((line = i_stream_next_line(input)) != NULL && !input->closed) { T_BEGIN { ret = auth_server_connection_input_line(conn, line); } T_END; if (ret < 0) { auth_server_connection_disconnect(conn, t_strdup_printf( "Received broken input: %s", line)); break; } } i_stream_unref(&input); } struct auth_server_connection * auth_server_connection_init(struct auth_client *client) { struct auth_server_connection *conn; pool_t pool; pool = pool_alloconly_create("auth server connection", 1024); conn = p_new(pool, struct auth_server_connection, 1); conn->pool = pool; conn->client = client; conn->fd = -1; hash_table_create_direct(&conn->requests, pool, 100); i_array_init(&conn->available_auth_mechs, 8); return conn; } static void auth_server_connection_remove_requests(struct auth_server_connection *conn, const char *disconnect_reason) { static const char *const temp_failure_args[] = { "temp", NULL }; struct hash_iterate_context *iter; void *key; struct auth_client_request *request; time_t created, oldest = 0; unsigned int request_count = 0; if (hash_table_count(conn->requests) == 0) return; iter = hash_table_iterate_init(conn->requests); while (hash_table_iterate(iter, conn->requests, &key, &request)) { if (!auth_client_request_is_aborted(request)) { request_count++; created = auth_client_request_get_create_time(request); if (oldest > created || oldest == 0) oldest = created; } auth_client_request_server_input(request, AUTH_REQUEST_STATUS_INTERNAL_FAIL, temp_failure_args); } hash_table_iterate_deinit(&iter); hash_table_clear(conn->requests, FALSE); if (request_count > 0) { i_warning("Auth connection closed with %u pending requests " "(max %u secs, pid=%s, %s)", request_count, (unsigned int)(ioloop_time - oldest), my_pid, disconnect_reason); } } void auth_server_connection_disconnect(struct auth_server_connection *conn, const char *reason) { conn->handshake_received = FALSE; conn->version_received = FALSE; conn->has_plain_mech = FALSE; conn->server_pid = 0; conn->connect_uid = 0; conn->cookie = NULL; array_clear(&conn->available_auth_mechs); if (conn->to != NULL) timeout_remove(&conn->to); if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(auth server connection) failed: %m"); conn->fd = -1; } auth_server_connection_remove_requests(conn, reason); if (conn->client->connect_notify_callback != NULL) { conn->client->connect_notify_callback(conn->client, FALSE, conn->client->connect_notify_context); } } static void auth_server_reconnect_timeout(struct auth_server_connection *conn) { (void)auth_server_connection_connect(conn); } static void auth_server_connection_reconnect(struct auth_server_connection *conn, const char *disconnect_reason) { time_t next_connect; auth_server_connection_disconnect(conn, disconnect_reason); next_connect = conn->last_connect + AUTH_SERVER_RECONNECT_TIMEOUT_SECS; conn->to = timeout_add(ioloop_time >= next_connect ? 0 : (next_connect - ioloop_time) * 1000, auth_server_reconnect_timeout, conn); } void auth_server_connection_deinit(struct auth_server_connection **_conn) { struct auth_server_connection *conn = *_conn; *_conn = NULL; auth_server_connection_disconnect(conn, "deinitializing"); i_assert(hash_table_count(conn->requests) == 0); hash_table_destroy(&conn->requests); array_free(&conn->available_auth_mechs); pool_unref(&conn->pool); } static void auth_client_handshake_timeout(struct auth_server_connection *conn) { i_error("Timeout waiting for handshake from auth server. " "my pid=%u, input bytes=%"PRIuUOFF_T, conn->client->client_pid, conn->input->v_offset); auth_server_connection_reconnect(conn, "auth server timeout"); } int auth_server_connection_connect(struct auth_server_connection *conn) { const char *handshake; int fd; i_assert(conn->fd == -1); conn->last_connect = ioloop_time; if (conn->to != NULL) timeout_remove(&conn->to); /* max. 1 second wait here. */ fd = net_connect_unix_with_retries(conn->client->auth_socket_path, 1000); if (fd == -1) { if (errno == EACCES) { i_error("auth: %s", eacces_error_get("connect", conn->client->auth_socket_path)); } else { i_error("auth: connect(%s) failed: %m", conn->client->auth_socket_path); } return -1; } conn->fd = fd; conn->io = io_add(fd, IO_READ, auth_server_connection_input, conn); conn->input = i_stream_create_fd(fd, AUTH_SERVER_CONN_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); handshake = t_strdup_printf("VERSION\t%u\t%u\nCPID\t%u\n", AUTH_CLIENT_PROTOCOL_MAJOR_VERSION, AUTH_CLIENT_PROTOCOL_MINOR_VERSION, conn->client->client_pid); if (o_stream_send_str(conn->output, handshake) < 0) { i_warning("Error sending handshake to auth server: %s", o_stream_get_error(conn->output)); auth_server_connection_disconnect(conn, o_stream_get_error(conn->output)); return -1; } conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT, auth_client_handshake_timeout, conn); return 0; } unsigned int auth_server_connection_add_request(struct auth_server_connection *conn, struct auth_client_request *request) { unsigned int id; id = ++conn->client->request_id_counter; if (id == 0) { /* wrapped - ID 0 not allowed */ id = ++conn->client->request_id_counter; } i_assert(hash_table_lookup(conn->requests, POINTER_CAST(id)) == NULL); hash_table_insert(conn->requests, POINTER_CAST(id), request); return id; } dovecot-2.2.33.2/src/lib-auth/auth-client.h0000644000175000017500000000725413123174404015216 00000000000000#ifndef AUTH_CLIENT_H #define AUTH_CLIENT_H #include "net.h" #include "auth-client-interface.h" struct auth_client; struct auth_client_request; enum auth_request_flags { AUTH_REQUEST_FLAG_SECURED = 0x01, AUTH_REQUEST_FLAG_VALID_CLIENT_CERT = 0x02, /* Skip penalty checks for this request */ AUTH_REQUEST_FLAG_NO_PENALTY = 0x04, /* Support final SASL response */ AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP = 0x08, /* Enable auth_debug=yes logging for this request */ AUTH_REQUEST_FLAG_DEBUG = 0x10 }; enum auth_request_status { AUTH_REQUEST_STATUS_ABORT = -3, AUTH_REQUEST_STATUS_INTERNAL_FAIL = -2, AUTH_REQUEST_STATUS_FAIL = -1, AUTH_REQUEST_STATUS_CONTINUE, AUTH_REQUEST_STATUS_OK }; struct auth_mech_desc { char *name; enum mech_security_flags flags; }; struct auth_connect_id { unsigned int server_pid; unsigned int connect_uid; }; struct auth_request_info { const char *mech; const char *service; const char *session_id; const char *cert_username; const char *local_name; const char *client_id; const char *forward_fields; enum auth_request_flags flags; struct ip_addr local_ip, remote_ip, real_local_ip, real_remote_ip; in_port_t local_port, remote_port, real_local_port, real_remote_port; const char *initial_resp_base64; }; typedef void auth_request_callback_t(struct auth_client_request *request, enum auth_request_status status, const char *data_base64, const char *const *args, void *context); typedef void auth_connect_notify_callback_t(struct auth_client *client, bool connected, void *context); /* Create new authentication client. */ struct auth_client * auth_client_init(const char *auth_socket_path, unsigned int client_pid, bool debug); void auth_client_deinit(struct auth_client **client); void auth_client_connect(struct auth_client *client); void auth_client_disconnect(struct auth_client *client, const char *reason); bool auth_client_is_connected(struct auth_client *client); bool auth_client_is_disconnected(struct auth_client *client); void auth_client_set_connect_notify(struct auth_client *client, auth_connect_notify_callback_t *callback, void *context) ATTR_NULL(2, 3); const struct auth_mech_desc * auth_client_get_available_mechs(struct auth_client *client, unsigned int *mech_count); const struct auth_mech_desc * auth_client_find_mech(struct auth_client *client, const char *name); /* Return current connection's identifiers. */ void auth_client_get_connect_id(struct auth_client *client, unsigned int *server_pid_r, unsigned int *connect_uid_r); /* Create a new authentication request. callback is called whenever something happens for the request. */ struct auth_client_request * auth_client_request_new(struct auth_client *client, const struct auth_request_info *request_info, auth_request_callback_t *callback, void *context) ATTR_NULL(4); /* Continue authentication. Call when reply->result == AUTH_CLIENT_REQUEST_CONTINUE */ void auth_client_request_continue(struct auth_client_request *request, const char *data_base64); /* Abort ongoing authentication request. */ void auth_client_request_abort(struct auth_client_request **request); /* Return ID of this request. */ unsigned int auth_client_request_get_id(struct auth_client_request *request); /* Return the PID of the server that handled this request. */ unsigned int auth_client_request_get_server_pid(struct auth_client_request *request); /* Return cookie of the server that handled this request. */ const char *auth_client_request_get_cookie(struct auth_client_request *request); /* Tell auth process to drop specified request from memory */ void auth_client_send_cancel(struct auth_client *client, unsigned int id); #endif dovecot-2.2.33.2/src/lib-auth/auth-server-connection.h0000644000175000017500000000203713165463624017410 00000000000000#ifndef AUTH_SERVER_CONNECTION_H #define AUTH_SERVER_CONNECTION_H struct auth_server_connection { pool_t pool; struct auth_client *client; int fd; time_t last_connect; struct io *io; struct timeout *to; struct istream *input; struct ostream *output; unsigned int server_pid; unsigned int connect_uid; char *cookie; ARRAY(struct auth_mech_desc) available_auth_mechs; /* id => request */ HASH_TABLE(void *, struct auth_client_request *) requests; unsigned int version_received:1; unsigned int handshake_received:1; unsigned int has_plain_mech:1; }; struct auth_server_connection * auth_server_connection_init(struct auth_client *client); void auth_server_connection_deinit(struct auth_server_connection **conn); int auth_server_connection_connect(struct auth_server_connection *conn); void auth_server_connection_disconnect(struct auth_server_connection *conn, const char *reason); unsigned int auth_server_connection_add_request(struct auth_server_connection *conn, struct auth_client_request *request); #endif dovecot-2.2.33.2/src/lib-auth/auth-client-interface.h0000644000175000017500000000162313165463624017161 00000000000000#ifndef AUTH_CLIENT_INTERFACE_H #define AUTH_CLIENT_INTERFACE_H /* Major version changes are not backwards compatible, minor version numbers can be ignored. */ #define AUTH_CLIENT_PROTOCOL_MAJOR_VERSION 1 #define AUTH_CLIENT_PROTOCOL_MINOR_VERSION 1 /* GSSAPI can use quite large packets */ #define AUTH_CLIENT_MAX_LINE_LENGTH 16384 enum mech_security_flags { /* Don't advertise this as available SASL mechanism (eg. APOP) */ MECH_SEC_PRIVATE = 0x0001, /* Anonymous authentication */ MECH_SEC_ANONYMOUS = 0x0002, /* Transfers plaintext passwords */ MECH_SEC_PLAINTEXT = 0x0004, /* Subject to passive (dictionary) attack */ MECH_SEC_DICTIONARY = 0x0008, /* Subject to active (non-dictionary) attack */ MECH_SEC_ACTIVE = 0x0010, /* Provides forward secrecy between sessions */ MECH_SEC_FORWARD_SECRECY = 0x0020, /* Provides mutual authentication */ MECH_SEC_MUTUAL_AUTH = 0x0040 }; #endif dovecot-2.2.33.2/src/lib-auth/Makefile.am0000644000175000017500000000065713123174404014664 00000000000000noinst_LTLIBRARIES = libauth.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master libauth_la_SOURCES = \ auth-client.c \ auth-client-request.c \ auth-master.c \ auth-server-connection.c headers = \ auth-client.h \ auth-client-interface.h \ auth-client-private.h \ auth-client-request.h \ auth-master.h \ auth-server-connection.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-auth/auth-client.c0000644000175000017500000000440613123174404015205 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "auth-client-private.h" #include "auth-server-connection.h" struct auth_client * auth_client_init(const char *auth_socket_path, unsigned int client_pid, bool debug) { struct auth_client *client; client = i_new(struct auth_client, 1); client->client_pid = client_pid; client->auth_socket_path = i_strdup(auth_socket_path); client->debug = debug; client->conn = auth_server_connection_init(client); (void)auth_server_connection_connect(client->conn); return client; } void auth_client_deinit(struct auth_client **_client) { struct auth_client *client = *_client; *_client = NULL; auth_server_connection_deinit(&client->conn); i_free(client->auth_socket_path); i_free(client); } void auth_client_connect(struct auth_client *client) { if (client->conn->fd == -1) (void)auth_server_connection_connect(client->conn); } void auth_client_disconnect(struct auth_client *client, const char *reason) { auth_server_connection_disconnect(client->conn, reason); } bool auth_client_is_connected(struct auth_client *client) { return client->conn->handshake_received; } bool auth_client_is_disconnected(struct auth_client *client) { return client->conn->fd == -1; } void auth_client_set_connect_notify(struct auth_client *client, auth_connect_notify_callback_t *callback, void *context) { client->connect_notify_callback = callback; client->connect_notify_context = context; } const struct auth_mech_desc * auth_client_get_available_mechs(struct auth_client *client, unsigned int *mech_count) { i_assert(auth_client_is_connected(client)); return array_get(&client->conn->available_auth_mechs, mech_count); } const struct auth_mech_desc * auth_client_find_mech(struct auth_client *client, const char *name) { const struct auth_mech_desc *mech; array_foreach(&client->conn->available_auth_mechs, mech) { if (strcasecmp(mech->name, name) == 0) return mech; } return NULL; } void auth_client_get_connect_id(struct auth_client *client, unsigned int *server_pid_r, unsigned int *connect_uid_r) { i_assert(auth_client_is_connected(client)); *server_pid_r = client->conn->server_pid; *connect_uid_r = client->conn->connect_uid; } dovecot-2.2.33.2/src/lib-auth/auth-client-request.h0000644000175000017500000000063313123174404016676 00000000000000#ifndef AUTH_CLIENT_REQUEST_H #define AUTH_CLIENT_REQUEST_H struct auth_server_connection; bool auth_client_request_is_aborted(struct auth_client_request *request); time_t auth_client_request_get_create_time(struct auth_client_request *request); void auth_client_request_server_input(struct auth_client_request *request, enum auth_request_status status, const char *const *args); #endif dovecot-2.2.33.2/src/imap-hibernate/0002755000175000017500000000000013172375612014072 500000000000000dovecot-2.2.33.2/src/imap-hibernate/Makefile.in0000644000175000017500000005456413172375572016100 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = imap-hibernate$(EXEEXT) subdir = src/imap-hibernate ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_imap_hibernate_OBJECTS = imap-client.$(OBJEXT) \ imap-hibernate-client.$(OBJEXT) \ imap-hibernate-settings.$(OBJEXT) \ imap-master-connection.$(OBJEXT) main.$(OBJEXT) imap_hibernate_OBJECTS = $(am_imap_hibernate_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(imap_hibernate_SOURCES) DIST_SOURCES = $(imap_hibernate_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-imap imap_hibernate_LDADD = $(LIBDOVECOT) imap_hibernate_DEPENDENCIES = $(LIBDOVECOT_DEPS) imap_hibernate_SOURCES = \ imap-client.c \ imap-hibernate-client.c \ imap-hibernate-settings.c \ imap-master-connection.c \ main.c noinst_HEADERS = \ imap-client.h \ imap-hibernate-client.h \ imap-master-connection.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/imap-hibernate/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/imap-hibernate/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list imap-hibernate$(EXEEXT): $(imap_hibernate_OBJECTS) $(imap_hibernate_DEPENDENCIES) $(EXTRA_imap_hibernate_DEPENDENCIES) @rm -f imap-hibernate$(EXEEXT) $(AM_V_CCLD)$(LINK) $(imap_hibernate_OBJECTS) $(imap_hibernate_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-hibernate-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-hibernate-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-master-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/imap-hibernate/imap-hibernate-settings.c0000644000175000017500000000234013123174404020666 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include #include /* */ static struct file_listener_settings imap_hibernate_unix_listeners_array[] = { { "imap-hibernate", 0600, "", "" } }; static struct file_listener_settings *imap_hibernate_unix_listeners[] = { &imap_hibernate_unix_listeners_array[0] }; static buffer_t imap_hibernate_unix_listeners_buf = { imap_hibernate_unix_listeners, sizeof(imap_hibernate_unix_listeners), { NULL, } }; /* */ struct service_settings imap_hibernate_service_settings = { .name = "imap-hibernate", .protocol = "imap", .type = "", .executable = "imap-hibernate", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_hibernate_unix_listeners_buf, sizeof(imap_hibernate_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; dovecot-2.2.33.2/src/imap-hibernate/imap-hibernate-client.h0000644000175000017500000000032313123174404020310 00000000000000#ifndef IMAP_HIBERNATE_CLIENT_H #define IMAP_HIBERNATE_CLIENT_H void imap_hibernate_client_create(int fd, bool debug); void imap_hibernate_clients_init(void); void imap_hibernate_clients_deinit(void); #endif dovecot-2.2.33.2/src/imap-hibernate/imap-client.h0000644000175000017500000000153713123174404016361 00000000000000#ifndef IMAP_CLIENT_H #define IMAP_CLIENT_H #include "net.h" struct imap_client_state { /* required: */ const char *username, *mail_log_prefix; /* optional: */ const char *session_id, *userdb_fields, *stats; struct ip_addr local_ip, remote_ip; time_t session_created; uid_t uid; gid_t gid; dev_t peer_dev; ino_t peer_ino; char *tag; const unsigned char *state; size_t state_size; unsigned int imap_idle_notify_interval; bool idle_cmd; bool have_notify_fd; bool anvil_sent; }; struct imap_client * imap_client_create(int fd, const struct imap_client_state *state); void imap_client_add_notify_fd(struct imap_client *client, int fd); void imap_client_create_finish(struct imap_client *client); void imap_client_destroy(struct imap_client **_client, const char *reason); void imap_clients_init(void); void imap_clients_deinit(void); #endif dovecot-2.2.33.2/src/imap-hibernate/main.c0000644000175000017500000000272713123174404015100 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "imap-client.h" #include "imap-hibernate-client.h" #include "imap-master-connection.h" static bool debug = FALSE; static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); imap_hibernate_client_create(conn->fd, debug); } int main(int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_UPDATE_PROCTITLE; const char *error; int c; master_service = master_service_init("imap-hibernate", service_flags, &argc, &argv, "D"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': debug = TRUE; break; default: return FATAL_DEFAULT; } } if (master_service_settings_read_simple(master_service, NULL, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "imap-hibernate: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); imap_clients_init(); imap_master_connections_init(); imap_hibernate_clients_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); imap_master_connections_deinit(); imap_hibernate_clients_deinit(); imap_clients_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/imap-hibernate/imap-master-connection.c0000644000175000017500000000632713123174404020530 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "connection.h" #include "imap-master-connection.h" #define IMAP_MASTER_CONNECTION_TIMEOUT_MSECS 30000 struct imap_master_connection { struct connection conn; struct timeout *to; imap_master_connection_send_callback_t *send_callback; imap_master_connection_read_callback_t *read_callback; void *context; }; static struct connection_list *master_clients; static void imap_master_connection_timeout(struct imap_master_connection *conn) { i_error("Timeout communicating with %s (version %sreceived)", conn->conn.name, conn->conn.version_received ? "" : "not "); imap_master_connection_deinit(&conn); } int imap_master_connection_init(const char *path, imap_master_connection_send_callback_t *send_callback, imap_master_connection_read_callback_t *read_callback, void *context, struct imap_master_connection **conn_r, const char **error_r) { struct imap_master_connection *conn; conn = i_new(struct imap_master_connection, 1); conn->send_callback = send_callback; conn->read_callback = read_callback; conn->context = context; connection_init_client_unix(master_clients, &conn->conn, path); if (connection_client_connect(&conn->conn) < 0) { int ret = errno == EAGAIN ? 0 : -1; *error_r = t_strdup_printf( "net_connect_unix(%s) failed: %m", path); connection_deinit(&conn->conn); i_free(conn); return ret; } conn->to = timeout_add(IMAP_MASTER_CONNECTION_TIMEOUT_MSECS, imap_master_connection_timeout, conn); *conn_r = conn; return 1; } void imap_master_connection_deinit(struct imap_master_connection **_conn) { struct imap_master_connection *conn = *_conn; *_conn = NULL; if (conn->read_callback != NULL) conn->read_callback(conn->context, "-"); timeout_remove(&conn->to); connection_deinit(&conn->conn); i_free(conn); } static void imap_master_client_destroy(struct connection *_conn) { struct imap_master_connection *conn = (struct imap_master_connection *)_conn; imap_master_connection_deinit(&conn); } static int imap_master_client_input_line(struct connection *_conn, const char *line) { struct imap_master_connection *conn = (struct imap_master_connection *)_conn; if (!_conn->version_received) { if (connection_input_line_default(_conn, line) < 0) return -1; conn->send_callback(conn->context, _conn->output); return 1; } else { imap_master_connection_read_callback_t *read_callback = conn->read_callback; conn->read_callback = NULL; read_callback(conn->context, line); /* we're finished now with this connection - disconnect it */ return -1; } } static struct connection_settings client_set = { .service_name_in = "imap-master", .service_name_out = "imap-master", .major_version = 1, .minor_version = 0, .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE }; static const struct connection_vfuncs client_vfuncs = { .destroy = imap_master_client_destroy, .input_line = imap_master_client_input_line }; void imap_master_connections_init(void) { master_clients = connection_list_init(&client_set, &client_vfuncs); } void imap_master_connections_deinit(void) { connection_list_deinit(&master_clients); } dovecot-2.2.33.2/src/imap-hibernate/imap-client.c0000644000175000017500000005036613165463624016373 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "fd-set-nonblock.h" #include "fdpass.h" #include "hostpid.h" #include "connection.h" #include "iostream.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "priorityq.h" #include "base64.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "master-service.h" #include "master-service-settings.h" #include "imap-keepalive.h" #include "imap-master-connection.h" #include "imap-client.h" #include #define IMAP_MASTER_SOCKET_NAME "imap-master" /* we only need enough for "DONE\r\n IDLE\r\n" */ #define IMAP_MAX_INBUF 12 + 1 + 128 /* DONE\r\nIDLE\r\n + ' ' + */ #define IMAP_MAX_OUTBUF 1024 /* If client has sent input and we can't recreate imap process in this many seconds, disconnect the client. */ #define IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS 10 /* If there's a change notification and we can't recreate imap process in this many seconds, disconnect the client. */ #define IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS (60*5) /* How often to try to unhibernate clients. */ #define IMAP_UNHIBERNATE_RETRY_MSECS 10 #define IMAP_CLIENT_BUFFER_FULL_ERROR "Client output buffer is full" enum imap_client_input_state { IMAP_CLIENT_INPUT_STATE_UNKNOWN, IMAP_CLIENT_INPUT_STATE_BAD, IMAP_CLIENT_INPUT_STATE_DONE_LF, IMAP_CLIENT_INPUT_STATE_DONE_CRLF, IMAP_CLIENT_INPUT_STATE_DONEIDLE }; struct imap_client_notify { int fd; struct io *io; }; struct imap_client { struct priorityq_item item; struct imap_client *prev, *next; pool_t pool; struct imap_client_state state; ARRAY(struct imap_client_notify) notifys; time_t move_back_start; struct timeout *to_move_back; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_keepalive; struct imap_master_connection *master_conn; struct ioloop_context *ioloop_ctx; const char *log_prefix; unsigned int next_read_threshold; bool bad_done, idle_done; bool unhibernate_queued; bool input_pending; }; static struct imap_client *imap_clients; static struct priorityq *unhibernate_queue; static struct timeout *to_unhibernate; static const char imap_still_here_text[] = "* OK Still here\r\n"; static void imap_client_stop(struct imap_client *client); void imap_client_destroy(struct imap_client **_client, const char *reason); static void imap_client_add_idle_keepalive_timeout(struct imap_client *client); static void imap_clients_unhibernate(void *context); static void imap_client_disconnected(struct imap_client **_client) { struct imap_client *client = *_client; const char *reason; reason = io_stream_get_disconnect_reason(client->input, client->output); imap_client_destroy(_client, reason); } static void imap_client_parse_userdb_fields(struct imap_client *client, const char **auth_user_r) { const char *const *field; unsigned int i; *auth_user_r = NULL; if (client->state.userdb_fields == NULL) return; field = t_strsplit_tabescaped(client->state.userdb_fields); for (i = 0; field[i] != NULL; i++) { if (strncmp(field[i], "auth_user=", 10) == 0) *auth_user_r = field[i] + 10; } } static void imap_client_move_back_send_callback(void *context, struct ostream *output) { struct imap_client *client = context; const struct imap_client_state *state = &client->state; string_t *str = t_str_new(256); const unsigned char *input_data; size_t input_size; ssize_t ret; str_append_tabescaped(str, state->username); if (state->session_id != NULL) { str_append(str, "\tsession="); str_append_tabescaped(str, state->session_id); } if (state->session_created != 0) { str_printfa(str, "\tsession_created=%s", dec2str(state->session_created)); } if (state->tag != NULL) str_printfa(str, "\ttag=%s", client->state.tag); if (state->local_ip.family != 0) str_printfa(str, "\tlip=%s", net_ip2addr(&state->local_ip)); if (state->remote_ip.family != 0) str_printfa(str, "\trip=%s", net_ip2addr(&state->remote_ip)); if (state->userdb_fields != NULL) { str_append(str, "\tuserdb_fields="); str_append_tabescaped(str, state->userdb_fields); } if (major(state->peer_dev) != 0 || minor(state->peer_dev) != 0) { str_printfa(str, "\tpeer_dev_major=%lu\tpeer_dev_minor=%lu", (unsigned long)major(state->peer_dev), (unsigned long)minor(state->peer_dev)); } if (state->peer_ino != 0) str_printfa(str, "\tpeer_ino=%llu", (unsigned long long)state->peer_ino); if (state->state_size > 0) { str_append(str, "\tstate="); base64_encode(state->state, state->state_size, str); } input_data = i_stream_get_data(client->input, &input_size); if (input_size > 0) { str_append(str, "\tclient_input="); base64_encode(input_data, input_size, str); } i_assert(o_stream_get_buffer_used_size(client->output) == 0); if (client->idle_done) { if (client->bad_done) str_append(str, "\tbad-done"); } else if (client->state.idle_cmd) { /* IDLE continues after sending changes */ str_append(str, "\tidle-continue"); } str_append_c(str, '\n'); /* send the fd first */ ret = fd_send(o_stream_get_fd(output), client->fd, str_data(str), 1); if (ret < 0) { i_error("fd_send(%s) failed: %m", o_stream_get_name(output)); imap_client_destroy(&client, "Failed to recreate imap process"); return; } i_assert(ret > 0); o_stream_nsend(output, str_data(str) + 1, str_len(str) - 1); } static void imap_client_move_back_read_callback(void *context, const char *line) { struct imap_client *client = context; if (line[0] != '+') { /* failed - FIXME: retry later? */ imap_client_destroy(&client, t_strdup_printf( "Failed to recreate imap process: %s", line+1)); } else { imap_client_destroy(&client, NULL); } } static bool imap_client_try_move_back(struct imap_client *client) { const struct master_service_settings *master_set; const char *path, *error; int ret; if (o_stream_get_buffer_used_size(client->output) > 0) { /* there is data buffered, so we have to disconnect you */ imap_client_destroy(&client, IMAP_CLIENT_BUFFER_FULL_ERROR); return TRUE; } master_set = master_service_settings_get(master_service); path = t_strconcat(master_set->base_dir, "/"IMAP_MASTER_SOCKET_NAME, NULL); ret = imap_master_connection_init(path, imap_client_move_back_send_callback, imap_client_move_back_read_callback, client, &client->master_conn, &error); if (ret > 0) { /* success */ imap_client_stop(client); return TRUE; } else if (ret < 0) { /* failed to connect to the imap-master socket */ imap_client_destroy(&client, error); return TRUE; } int max_secs = client->input_pending ? IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS; if (ioloop_time - client->move_back_start > max_secs) { /* we've waited long enough */ imap_client_destroy(&client, error); return TRUE; } return FALSE; } static void imap_client_move_back(struct imap_client *client) { if (imap_client_try_move_back(client)) return; /* imap-master socket is busy. retry in a while. */ if (client->move_back_start == 0) client->move_back_start = ioloop_time; client->unhibernate_queued = TRUE; priorityq_add(unhibernate_queue, &client->item); if (to_unhibernate == NULL) { to_unhibernate = timeout_add_short(IMAP_UNHIBERNATE_RETRY_MSECS, imap_clients_unhibernate, NULL); } } static enum imap_client_input_state imap_client_input_parse(const unsigned char *data, size_t size, const char **tag_r) { const unsigned char *tag_start, *tag_end; enum imap_client_input_state state = IMAP_CLIENT_INPUT_STATE_DONE_LF; /* skip over DONE[\r]\n */ if (i_memcasecmp(data, "DONE", I_MIN(size, 4)) != 0) return IMAP_CLIENT_INPUT_STATE_BAD; if (size <= 4) return IMAP_CLIENT_INPUT_STATE_UNKNOWN; data += 4; size -= 4; if (data[0] == '\r') { state = IMAP_CLIENT_INPUT_STATE_DONE_CRLF; data++; size--; } if (size == 0) return IMAP_CLIENT_INPUT_STATE_UNKNOWN; if (data[0] != '\n') return IMAP_CLIENT_INPUT_STATE_BAD; data++; size--; if (size == 0) return state; tag_start = data; /* skip over tag */ while(data[0] != ' ' && data[0] != '\r' && data[0] != '\t' ) { data++; size--; } tag_end = data; if (size == 0) return state; if (data[0] != ' ') return IMAP_CLIENT_INPUT_STATE_BAD; data++; size--; /* skip over IDLE[\r]\n - checking this assumes that the DONE and IDLE are sent in the same IP packet, otherwise we'll unnecessarily recreate the imap process and immediately resume IDLE there. if this becomes an issue we could add a small delay to the imap process creation and wait for the IDLE command during it. */ if (size <= 4 || i_memcasecmp(data, "IDLE", 4) != 0) return state; data += 4; size -= 4; if (data[0] == '\r') { data++; size--; } if (size == 1 && data[0] == '\n') { *tag_r = t_strdup_until(tag_start, tag_end); return IMAP_CLIENT_INPUT_STATE_DONEIDLE; } return state; } static void imap_client_input_idle_cmd(struct imap_client *client) { char *old_tag; const char *new_tag; const char *output; const unsigned char *data; size_t size; bool done = TRUE; int ret; /* we should read either DONE or disconnection. also handle if client sends DONE\nIDLE simply to recreate the IDLE. */ ret = i_stream_read_data(client->input, &data, &size, client->next_read_threshold); if (size == 0) { if (ret < 0) imap_client_disconnected(&client); return; } client->next_read_threshold = 0; switch (imap_client_input_parse(data, size, &new_tag)) { case IMAP_CLIENT_INPUT_STATE_UNKNOWN: /* we haven't received a full DONE[\r]\n yet - wait */ client->next_read_threshold = size; return; case IMAP_CLIENT_INPUT_STATE_BAD: /* invalid input - return this to the imap process */ client->bad_done = TRUE; break; case IMAP_CLIENT_INPUT_STATE_DONE_LF: i_stream_skip(client->input, 4+1); break; case IMAP_CLIENT_INPUT_STATE_DONE_CRLF: i_stream_skip(client->input, 4+2); break; case IMAP_CLIENT_INPUT_STATE_DONEIDLE: /* we received DONE+IDLE, so the client simply wanted to notify us that it's still there. continue hibernation. */ old_tag = client->state.tag; client->state.tag = i_strdup(new_tag); output = t_strdup_printf("%s OK Idle completed.\r\n+ idling\r\n", old_tag); i_free(old_tag); ret = o_stream_flush(client->output); if (ret > 0) ret = o_stream_send_str(client->output, output); if (ret < 0) { imap_client_disconnected(&client); return; } if ((size_t)ret != strlen(output)) { /* disconnect */ imap_client_destroy(&client, IMAP_CLIENT_BUFFER_FULL_ERROR); return; } else { done = FALSE; i_stream_skip(client->input, size); } break; } if (done) { client->idle_done = TRUE; client->input_pending = TRUE; imap_client_move_back(client); } else imap_client_add_idle_keepalive_timeout(client); } static void imap_client_input_nonidle(struct imap_client *client) { if (i_stream_read(client->input) < 0) imap_client_disconnected(&client); else { client->input_pending = TRUE; imap_client_move_back(client); } } static void imap_client_input_notify(struct imap_client *client) { imap_client_move_back(client); } static void keepalive_timeout(struct imap_client *client) { ssize_t ret; /* do not send this if there is data buffered */ if ((ret = o_stream_flush(client->output)) < 0) { imap_client_disconnected(&client); return; } else if (ret == 0) return; ret = o_stream_send_str(client->output, imap_still_here_text); if (ret < 0) { imap_client_disconnected(&client); return; } /* ostream buffer size is definitely large enough for this text */ i_assert((size_t)ret == strlen(imap_still_here_text)); imap_client_add_idle_keepalive_timeout(client); } static void imap_client_add_idle_keepalive_timeout(struct imap_client *client) { unsigned int interval = client->state.imap_idle_notify_interval; if (interval == 0) return; interval = imap_keepalive_interval_msecs(client->state.username, &client->state.remote_ip, interval); if (client->to_keepalive!= NULL) timeout_remove(&client->to_keepalive); client->to_keepalive = timeout_add(interval, keepalive_timeout, client); } static const struct var_expand_table * imap_client_get_var_expand_table(struct imap_client *client) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 's', NULL, "service" }, { 'h', NULL, "home" }, { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 'i', NULL, "uid" }, { '\0', NULL, "gid" }, { '\0', NULL, "session" }, { '\0', NULL, "auth_user" }, { '\0', NULL, "auth_username" }, { '\0', NULL, "auth_domain" }, /* NOTE: keep this synced with lib-storage's mail_user_var_expand_table() */ { '\0', NULL, NULL } }; struct var_expand_table *tab; const char *auth_user; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = client->state.username; tab[1].value = t_strcut(client->state.username, '@'); tab[2].value = strchr(client->state.username, '@'); if (tab[2].value != NULL) tab[2].value++; tab[3].value = "imap-hibernate"; tab[4].value = NULL; /* we shouldn't need this */ tab[5].value = client->state.local_ip.family == 0 ? NULL : net_ip2addr(&client->state.local_ip); tab[6].value = client->state.remote_ip.family == 0 ? NULL : net_ip2addr(&client->state.remote_ip); tab[7].value = my_pid; tab[8].value = dec2str(client->state.uid); tab[9].value = dec2str(client->state.gid); tab[10].value = client->state.session_id; imap_client_parse_userdb_fields(client, &auth_user); if (auth_user == NULL) { tab[11].value = tab[0].value; tab[12].value = tab[1].value; tab[13].value = tab[2].value; } else { tab[11].value = auth_user; tab[12].value = t_strcut(auth_user, '@'); tab[13].value = strchr(auth_user, '@'); if (tab[13].value != NULL) tab[13].value++; } return tab; } static const char * imap_client_var_expand_func_userdb(const char *data, void *context) { const char *const *fields = context; const char *field_name = t_strdup_printf("%s=",t_strcut(data, ':')); const char *default_value = i_strchr_to_next(data, ':'); const char *value = NULL; for(;*fields != NULL; fields++) { if (strncmp(*fields, field_name, strlen(field_name)) == 0) { value = *fields+strlen(field_name); break; } } return value != NULL ? value : default_value; } static void imap_client_io_activate_user(struct imap_client *client) { i_set_failure_prefix("%s", client->log_prefix); } static void imap_client_io_deactivate_user(struct imap_client *client ATTR_UNUSED) { i_set_failure_prefix("imap-hibernate: "); } static const char *imap_client_get_anvil_userip_ident(struct imap_client_state *state) { if (state->remote_ip.family == 0) return NULL; return t_strconcat(net_ip2addr(&state->remote_ip), "/", str_tabescape(state->username), NULL); } struct imap_client * imap_client_create(int fd, const struct imap_client_state *state) { const struct var_expand_func_table funcs[] = { { "userdb", imap_client_var_expand_func_userdb }, { NULL, NULL } }; struct imap_client *client; pool_t pool = pool_alloconly_create("imap client", 256); void *statebuf; const char *ident; i_assert(state->username != NULL); i_assert(state->mail_log_prefix != NULL); fd_set_nonblock(fd, TRUE); /* it should already be, but be sure */ client = p_new(pool, struct imap_client, 1); client->pool = pool; client->fd = fd; client->input = i_stream_create_fd(fd, IMAP_MAX_INBUF, FALSE); client->output = o_stream_create_fd(fd, IMAP_MAX_OUTBUF, FALSE); client->state = *state; client->state.username = p_strdup(pool, state->username); client->state.session_id = p_strdup(pool, state->session_id); client->state.userdb_fields = p_strdup(pool, state->userdb_fields); client->state.stats = p_strdup(pool, state->stats); if (state->state_size > 0) { client->state.state = statebuf = p_malloc(pool, state->state_size); memcpy(statebuf, state->state, state->state_size); client->state.state_size = state->state_size; } T_BEGIN { string_t *str; char **fields = p_strsplit_tabescaped(unsafe_data_stack_pool, client->state.userdb_fields); str = t_str_new(256); var_expand_with_funcs(str, state->mail_log_prefix, imap_client_get_var_expand_table(client), funcs, fields); client->log_prefix = p_strdup(pool, str_c(str)); } T_END; ident = imap_client_get_anvil_userip_ident(&client->state); if (ident != NULL) { master_service_anvil_send(master_service, t_strconcat( "CONNECT\t", my_pid, "\timap/", ident, "\n", NULL)); client->state.anvil_sent = TRUE; } p_array_init(&client->notifys, pool, 2); DLLIST_PREPEND(&imap_clients, client); return client; } static void imap_client_stop(struct imap_client *client) { struct imap_client_notify *notify; if (client->unhibernate_queued) priorityq_remove(unhibernate_queue, &client->item); if (client->io != NULL) io_remove(&client->io); if (client->to_keepalive != NULL) timeout_remove(&client->to_keepalive); array_foreach_modifiable(&client->notifys, notify) { if (notify->io != NULL) io_remove(¬ify->io); if (notify->fd != -1) i_close_fd(¬ify->fd); } } void imap_client_destroy(struct imap_client **_client, const char *reason) { struct imap_client *client = *_client; *_client = NULL; if (reason != NULL) { /* the client input/output bytes don't count the DONE+IDLE by imap-hibernate, but that shouldn't matter much. */ i_info("%s %s", reason, client->state.stats); } if (client->state.anvil_sent) { master_service_anvil_send(master_service, t_strconcat( "DISCONNECT\t", my_pid, "\timap/", imap_client_get_anvil_userip_ident(&client->state), "\n", NULL)); } if (client->ioloop_ctx != NULL) { io_loop_context_remove_callbacks(client->ioloop_ctx, imap_client_io_activate_user, imap_client_io_deactivate_user, client); imap_client_io_deactivate_user(client); io_loop_context_unref(&client->ioloop_ctx); } if (client->state.tag != NULL) i_free(client->state.tag); DLLIST_REMOVE(&imap_clients, client); imap_client_stop(client); i_stream_destroy(&client->input); o_stream_destroy(&client->output); i_close_fd(&client->fd); pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); } void imap_client_add_notify_fd(struct imap_client *client, int fd) { struct imap_client_notify *notify; notify = array_append_space(&client->notifys); notify->fd = fd; } void imap_client_create_finish(struct imap_client *client) { struct imap_client_notify *notify; client->ioloop_ctx = io_loop_context_new(current_ioloop); io_loop_context_add_callbacks(client->ioloop_ctx, imap_client_io_activate_user, imap_client_io_deactivate_user, client); imap_client_io_activate_user(client); if (client->state.idle_cmd) { client->io = io_add(client->fd, IO_READ, imap_client_input_idle_cmd, client); } else { client->io = io_add(client->fd, IO_READ, imap_client_input_nonidle, client); } imap_client_add_idle_keepalive_timeout(client); array_foreach_modifiable(&client->notifys, notify) { notify->io = io_add(notify->fd, IO_READ, imap_client_input_notify, client); } } static int client_unhibernate_cmp(const void *p1, const void *p2) { const struct imap_client *c1 = p1, *c2 = p2; time_t t1, t2; t1 = c1->move_back_start + (c1->input_pending ? IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS); t2 = c2->move_back_start + (c2->input_pending ? IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS); if (t1 < t2) return -1; if (t1 > t2) return 1; return 0; } static void imap_clients_unhibernate(void *context ATTR_UNUSED) { struct priorityq_item *item; while ((item = priorityq_pop(unhibernate_queue)) != NULL) { struct imap_client *client = (struct imap_client *)item; if (!imap_client_try_move_back(client)) return; } timeout_remove(&to_unhibernate); } void imap_clients_init(void) { unhibernate_queue = priorityq_init(client_unhibernate_cmp, 64); } void imap_clients_deinit(void) { while (imap_clients != NULL) { struct imap_client *client = imap_clients; imap_client_io_activate_user(client); imap_client_destroy(&client, "Shutting down"); } if (to_unhibernate != NULL) timeout_remove(&to_unhibernate); priorityq_deinit(&unhibernate_queue); } dovecot-2.2.33.2/src/imap-hibernate/imap-hibernate-client.c0000644000175000017500000001777313165463624020337 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "connection.h" #include "istream.h" #include "istream-unix.h" #include "ostream.h" #include "buffer.h" #include "base64.h" #include "strescape.h" #include "master-service.h" #include "imap-client.h" #include "imap-hibernate-client.h" struct imap_hibernate_client { struct connection conn; struct imap_client *imap_client; bool imap_client_created; bool finished; bool debug; }; struct imap_hibernate_input { /* input we've already read from the IMAP client. */ buffer_t *client_input; /* IMAP connection state */ buffer_t *state; }; static struct connection_list *hibernate_clients = NULL; static void imap_hibernate_client_destroy(struct connection *conn) { struct imap_hibernate_client *client = (struct imap_hibernate_client *)conn; if (!client->imap_client_created) master_service_client_connection_destroyed(master_service); else if (client->finished) imap_client_create_finish(client->imap_client); connection_deinit(conn); i_free(conn); } static int imap_hibernate_client_parse_input(const char *const *args, pool_t pool, struct imap_client_state *state_r, const char **error_r) { const char *key, *value; unsigned int peer_dev_major = 0, peer_dev_minor = 0; i_zero(state_r); if (args[0] == NULL) { *error_r = "Missing username in input"; return -1; } state_r->username = args[0]; args++; if (args[0] == NULL) { *error_r = "Missing mail_log_prefix in input"; return -1; } state_r->mail_log_prefix = args[0]; args++; for (; *args != NULL; args++) { value = strchr(*args, '='); if (value != NULL) key = t_strdup_until(*args, value++); else { key = *args; value = ""; } if (strcmp(key, "lip") == 0) { if (net_addr2ip(value, &state_r->local_ip) < 0) { *error_r = t_strdup_printf( "Invalid lip value: %s", value); return -1; } } else if (strcmp(key, "rip") == 0) { if (net_addr2ip(value, &state_r->remote_ip) < 0) { *error_r = t_strdup_printf( "Invalid rip value: %s", value); return -1; } } else if (strcmp(key, "peer_dev_major") == 0) { if (str_to_uint(value, &peer_dev_major) < 0) { *error_r = t_strdup_printf( "Invalid peer_dev_major value: %s", value); return -1; } } else if (strcmp(key, "peer_dev_minor") == 0) { if (str_to_uint(value, &peer_dev_minor) < 0) { *error_r = t_strdup_printf( "Invalid peer_dev_minor value: %s", value); return -1; } } else if (strcmp(key, "peer_ino") == 0) { if (str_to_ino(value, &state_r->peer_ino) < 0) { *error_r = t_strdup_printf( "Invalid peer_ino value: %s", value); return -1; } } else if (strcmp(key, "uid") == 0) { if (str_to_uid(value, &state_r->uid) < 0) { *error_r = t_strdup_printf( "Invalid uid value: %s", value); return -1; } } else if (strcmp(key, "gid") == 0) { if (str_to_gid(value, &state_r->gid) < 0) { *error_r = t_strdup_printf( "Invalid gid value: %s", value); return -1; } } else if (strcmp(key, "stats") == 0) { state_r->stats = value; } else if (strcmp(key, "idle-cmd") == 0) { state_r->idle_cmd = TRUE; } else if (strcmp(key, "session") == 0) { state_r->session_id = value; } else if (strcmp(key, "session_created") == 0) { if (str_to_time(value, &state_r->session_created) < 0) { *error_r = t_strdup_printf( "Invalid session_created value: %s", value); return -1; } } else if (strcmp(key, "userdb_fields") == 0) { state_r->userdb_fields = value; } else if (strcmp(key, "notify_fd") == 0) { state_r->have_notify_fd = TRUE; } else if (strcmp(key, "idle_notify_interval") == 0) { if (str_to_uint(value, &state_r->imap_idle_notify_interval) < 0) { *error_r = t_strdup_printf( "Invalid idle_notify_interval value: %s", value); return -1; } } else if (strcmp(key, "tag") == 0) { state_r->tag = i_strdup(value); } else if (strcmp(key, "state") == 0) { buffer_t *state_buf; state_buf = buffer_create_dynamic(pool, 1024); if (base64_decode(value, strlen(value), NULL, state_buf) < 0) { *error_r = t_strdup_printf( "Invalid state base64 value: %s", value); return -1; } state_r->state = state_buf->data; state_r->state_size = state_buf->used; } } if (state_r->tag == NULL) { *error_r = "Missing tag"; return -1; } if (peer_dev_major != 0 || peer_dev_minor != 0) state_r->peer_dev = makedev(peer_dev_major, peer_dev_minor); return 0; } static int imap_hibernate_client_input_args(struct connection *conn, const char *const *args, int fd, pool_t pool) { struct imap_hibernate_client *client = (struct imap_hibernate_client *)conn; struct imap_client_state state; const char *error; if (imap_hibernate_client_parse_input(args, pool, &state, &error) < 0) { i_error("Failed to parse client input: %s", error); o_stream_send_str(conn->output, t_strdup_printf( "-Failed to parse client input: %s\n", error)); return -1; } client->imap_client = imap_client_create(fd, &state); /* the transferred imap client fd is now counted as the client. */ client->imap_client_created = TRUE; return state.have_notify_fd ? 0 : 1; } static int imap_hibernate_client_input_line(struct connection *conn, const char *line) { struct imap_hibernate_client *client = (struct imap_hibernate_client *)conn; int fd = -1, ret; if (!conn->version_received) { if (connection_verify_version(conn, t_strsplit_tabescaped(line)) < 0) return -1; conn->version_received = TRUE; return 1; } if (client->finished) { i_error("Received unexpected line: %s", line); return -1; } if (client->imap_client == NULL) { char *const *args; pool_t pool; fd = i_stream_unix_get_read_fd(conn->input); if (fd == -1) { i_error("IMAP client fd not received"); return -1; } pool = pool_alloconly_create("client cmd", 1024); args = p_strsplit_tabescaped(pool, line); ret = imap_hibernate_client_input_args(conn, (void *)args, fd, pool); if (ret >= 0 && client->debug) i_debug("Create client with input: %s", line); pool_unref(&pool); } else { fd = i_stream_unix_get_read_fd(conn->input); if (fd == -1) { i_error("IMAP notify fd not received (input: %s)", line); ret = -1; } else if (line[0] != '\0') { i_error("Expected empty notify fd line from client, but got: %s", line); o_stream_send_str(conn->output, "Expected empty notify fd line"); ret = -1; } else { imap_client_add_notify_fd(client->imap_client, fd); ret = 1; } } if (ret < 0) { if (client->imap_client != NULL) imap_client_destroy(&client->imap_client, NULL); if (fd != -1) i_close_fd(&fd); return -1; } else if (ret == 0) { /* still need to read another fd */ i_stream_unix_set_read_fd(conn->input); } else { /* finished - wait for disconnection from imap before finishing. this way the old imap process will have time to destroy itself before we have a chance to create another one. */ client->finished = TRUE; } o_stream_send_str(conn->output, "+\n"); return 1; } void imap_hibernate_client_create(int fd, bool debug) { struct imap_hibernate_client *client; client = i_new(struct imap_hibernate_client, 1); client->debug = debug; connection_init_server(hibernate_clients, &client->conn, "imap-hibernate", fd, fd); i_assert(client->conn.input == NULL); client->conn.input = i_stream_create_unix(fd, (size_t)-1); i_stream_unix_set_read_fd(client->conn.input); } static struct connection_settings client_set = { .service_name_in = "imap-hibernate", .service_name_out = "imap-hibernate", .major_version = 1, .minor_version = 0, .input_max_size = 0, /* don't auto-create istream */ .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs client_vfuncs = { .destroy = imap_hibernate_client_destroy, .input_line = imap_hibernate_client_input_line }; void imap_hibernate_clients_init(void) { hibernate_clients = connection_list_init(&client_set, &client_vfuncs); } void imap_hibernate_clients_deinit(void) { connection_list_deinit(&hibernate_clients); } dovecot-2.2.33.2/src/imap-hibernate/imap-master-connection.h0000644000175000017500000000136713123174404020534 00000000000000#ifndef IMAP_MASTER_CONNECTION_H #define IMAP_MASTER_CONNECTION_H struct imap_master_connection; typedef void imap_master_connection_send_callback_t(void *context, struct ostream *output); typedef void imap_master_connection_read_callback_t(void *context, const char *reply); /* Returns 1 = success, 0 = retry later, -1 = error */ int imap_master_connection_init(const char *path, imap_master_connection_send_callback_t *send_callback, imap_master_connection_read_callback_t *read_callback, void *context, struct imap_master_connection **conn_r, const char **error_r); void imap_master_connection_deinit(struct imap_master_connection **conn); void imap_master_connections_init(void); void imap_master_connections_deinit(void); #endif dovecot-2.2.33.2/src/imap-hibernate/Makefile.am0000644000175000017500000000102613165463624016046 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = imap-hibernate AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-imap imap_hibernate_LDADD = $(LIBDOVECOT) imap_hibernate_DEPENDENCIES = $(LIBDOVECOT_DEPS) imap_hibernate_SOURCES = \ imap-client.c \ imap-hibernate-client.c \ imap-hibernate-settings.c \ imap-master-connection.c \ main.c noinst_HEADERS = \ imap-client.h \ imap-hibernate-client.h \ imap-master-connection.h dovecot-2.2.33.2/src/dict/0002755000175000017500000000000013172375612012130 500000000000000dovecot-2.2.33.2/src/dict/Makefile.in0000644000175000017500000005474013172375572014132 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = dict$(EXEEXT) subdir = src/dict ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_dict_OBJECTS = dict-connection.$(OBJEXT) dict-commands.$(OBJEXT) \ dict-settings.$(OBJEXT) main.$(OBJEXT) dict_OBJECTS = $(am_dict_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = ../lib-dict/libdict_backend.a \ $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = dict_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(dict_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dict_SOURCES) DIST_SOURCES = $(dict_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-sql \ -DDICT_MODULE_DIR=\""$(moduledir)/dict"\" \ -DPKG_RUNDIR=\""$(rundir)"\" dict_LDFLAGS = -export-dynamic libs = \ ../lib-dict/libdict_backend.a \ $(LIBDOVECOT_SQL) dict_LDADD = \ $(libs) \ $(LIBDOVECOT) \ $(DICT_LIBS) \ $(SQL_LIBS) dict_DEPENDENCIES = $(libs) $(LIBDOVECOT_DEPS) dict_SOURCES = \ dict-connection.c \ dict-commands.c \ dict-settings.c \ main.c noinst_HEADERS = \ dict-connection.h \ dict-commands.h \ dict-settings.h \ main.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dict/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dict/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list dict$(EXEEXT): $(dict_OBJECTS) $(dict_DEPENDENCIES) $(EXTRA_dict_DEPENDENCIES) @rm -f dict$(EXEEXT) $(AM_V_CCLD)$(dict_LINK) $(dict_OBJECTS) $(dict_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-commands.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/dict/dict-connection.h0000644000175000017500000000237313165463624015307 00000000000000#ifndef DICT_CONNECTION_H #define DICT_CONNECTION_H #include "dict.h" struct dict_connection_transaction { unsigned int id; struct dict_connection *conn; struct dict_transaction_context *ctx; }; struct dict_connection { struct dict_connection *prev, *next; struct dict_server *server; int refcount; char *username; char *name; struct dict *dict; enum dict_data_type value_type; unsigned int minor_version; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_input; struct timeout *to_unref; /* There are only a few transactions per client, so keeping them in array is fast enough */ ARRAY(struct dict_connection_transaction) transactions; ARRAY(struct dict_connection_cmd *) cmds; unsigned int async_id_counter; unsigned int destroyed:1; }; struct dict_connection *dict_connection_create(int fd); void dict_connection_destroy(struct dict_connection *conn); void dict_connection_ref(struct dict_connection *conn); bool dict_connection_unref(struct dict_connection *conn); void dict_connection_unref_safe(struct dict_connection *conn); void dict_connection_continue_input(struct dict_connection *conn); unsigned int dict_connections_current_count(void); void dict_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/dict/main.h0000644000175000017500000000011713123174404013132 00000000000000#ifndef MAIN_H #define MAIN_H void dict_proctitle_update_later(void); #endif dovecot-2.2.33.2/src/dict/main.c0000644000175000017500000001010613165463624013137 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "ioloop.h" #include "randgen.h" #include "str.h" #include "hostpid.h" #include "timing.h" #include "process-title.h" #include "env-util.h" #include "module-dir.h" #include "master-service.h" #include "master-service-settings.h" #include "sql-api.h" #include "dict.h" #include "dict-client.h" #include "dict-commands.h" #include "dict-connection.h" #include "dict-settings.h" #include "main.h" static struct module *modules; static struct timeout *to_proctitle; static bool proctitle_updated; static void add_timing_string(string_t *str, struct timing *timing, const char *name) { str_printfa(str, ", %u %s:%llu/%llu/%llu/%llu", timing_get_count(timing), name, (unsigned long long)timing_get_min(timing)/1000, (unsigned long long)timing_get_avg(timing)/1000, (unsigned long long)timing_get_95th(timing)/1000, (unsigned long long)timing_get_max(timing)/1000); timing_reset(timing); } static void dict_proctitle_update(void *context ATTR_UNUSED) { string_t *str = t_str_new(128); if (!proctitle_updated) timeout_remove(&to_proctitle); str_printfa(str, "[%u clients", dict_connections_current_count()); add_timing_string(str, cmd_stats.lookups, "lookups"); add_timing_string(str, cmd_stats.iterations, "iters"); add_timing_string(str, cmd_stats.commits, "commits"); str_append_c(str, ']'); process_title_set(str_c(str)); proctitle_updated = FALSE; } void dict_proctitle_update_later(void) { if (!dict_settings->verbose_proctitle) return; if (to_proctitle == NULL) to_proctitle = timeout_add(1000, dict_proctitle_update, NULL); proctitle_updated = TRUE; } static void dict_die(void) { /* hope that other processes relying on us will die first. */ } static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); (void)dict_connection_create(conn->fd); } static void main_preinit(void) { /* Maybe needed. Have to open /dev/urandom before possible chrooting. */ random_init(); /* Load built-in SQL drivers (if any) */ sql_drivers_init(); sql_drivers_register_all(); #ifdef HAVE_CDB dict_driver_register(&dict_driver_cdb); #endif restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } static void main_init(void) { struct module_dir_load_settings mod_set; void **sets; sets = master_service_settings_get_others(master_service); dict_settings = sets[0]; if (*dict_settings->dict_db_config != '\0') { /* for berkeley db library */ env_put(t_strconcat("DB_CONFIG=", dict_settings->dict_db_config, NULL)); } i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; modules = module_dir_load(DICT_MODULE_DIR, NULL, &mod_set); module_dir_init(modules); /* Register only after loading modules. They may contain SQL drivers, which we'll need to register. */ dict_drivers_register_all(); dict_commands_init(); } static void main_deinit(void) { if (to_proctitle != NULL) timeout_remove(&to_proctitle); dict_connections_destroy_all(); dict_drivers_unregister_all(); dict_commands_deinit(); module_dir_unload(&modules); sql_drivers_deinit(); random_deinit(); } int main(int argc, char *argv[]) { const enum master_service_flags service_flags = 0; const struct setting_parser_info *set_roots[] = { &dict_setting_parser_info, NULL }; const char *error; master_service = master_service_init("dict", service_flags, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, t_strdup_printf("dict(%s): ", my_pid)); main_preinit(); master_service_set_die_callback(master_service, dict_die); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/dict/dict-commands.h0000644000175000017500000000067413123174404014740 00000000000000#ifndef DICT_COMMANDS_H #define DICT_COMMANDS_H struct dict_connection; struct dict_command_stats { struct timing *lookups; struct timing *iterations; struct timing *commits; }; extern struct dict_command_stats cmd_stats; int dict_command_input(struct dict_connection *conn, const char *line); void dict_connection_cmds_output_more(struct dict_connection *conn); void dict_commands_init(void); void dict_commands_deinit(void); #endif dovecot-2.2.33.2/src/dict/dict-commands.c0000644000175000017500000004260213165463624014743 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "timing.h" #include "time-util.h" #include "dict-client.h" #include "dict-settings.h" #include "dict-connection.h" #include "dict-commands.h" #include "main.h" #define DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION 1 #define DICT_CLIENT_PROTOCOL_UNORDERED_MIN_VERSION 1 #define DICT_OUTPUT_OPTIMAL_SIZE 1024 struct dict_cmd_func { enum dict_protocol_cmd cmd; int (*func)(struct dict_connection_cmd *cmd, const char *line); }; struct dict_connection_cmd { const struct dict_cmd_func *cmd; struct dict_connection *conn; struct timeval start_timeval; char *reply; struct dict_iterate_context *iter; enum dict_iterate_flags iter_flags; unsigned int async_reply_id; unsigned int trans_id; /* obsolete */ }; struct dict_command_stats cmd_stats; static int cmd_iterate_flush(struct dict_connection_cmd *cmd); static void dict_connection_cmd_output_more(struct dict_connection_cmd *cmd); static void dict_connection_cmd_free(struct dict_connection_cmd *cmd) { if (cmd->iter != NULL) (void)dict_iterate_deinit(&cmd->iter); i_free(cmd->reply); if (dict_connection_unref(cmd->conn)) dict_connection_continue_input(cmd->conn); i_free(cmd); } static void dict_connection_cmd_remove(struct dict_connection_cmd *cmd) { struct dict_connection_cmd *const *cmds; unsigned int i, count; cmds = array_get(&cmd->conn->cmds, &count); for (i = 0; i < count; i++) { if (cmds[i] == cmd) { array_delete(&cmd->conn->cmds, i, 1); dict_connection_cmd_free(cmd); return; } } i_unreached(); } static void dict_connection_cmds_flush(struct dict_connection *conn) { struct dict_connection_cmd *cmd, *const *first_cmdp; i_assert(conn->minor_version < DICT_CLIENT_PROTOCOL_UNORDERED_MIN_VERSION); dict_connection_ref(conn); while (array_count(&conn->cmds) > 0) { first_cmdp = array_idx(&conn->cmds, 0); cmd = *first_cmdp; i_assert(cmd->async_reply_id == 0); /* we may be able to start outputting iterations now. */ if (cmd->iter != NULL) (void)cmd_iterate_flush(cmd); if (cmd->reply == NULL) { /* command not finished yet */ break; } o_stream_nsend_str(conn->output, cmd->reply); dict_connection_cmd_remove(cmd); } dict_connection_unref_safe(conn); } static void dict_connection_cmd_try_flush(struct dict_connection_cmd **_cmd) { struct dict_connection_cmd *cmd = *_cmd; *_cmd = NULL; if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_UNORDERED_MIN_VERSION) { dict_connection_cmds_flush(cmd->conn); return; } i_assert(cmd->async_reply_id != 0); i_assert(cmd->reply != NULL); o_stream_nsend_str(cmd->conn->output, t_strdup_printf("%c%u\t%s", DICT_PROTOCOL_REPLY_ASYNC_REPLY, cmd->async_reply_id, cmd->reply)); dict_connection_cmd_remove(cmd); } static void dict_connection_cmd_async(struct dict_connection_cmd *cmd) { if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_UNORDERED_MIN_VERSION) return; i_assert(cmd->async_reply_id == 0); cmd->async_reply_id = ++cmd->conn->async_id_counter; if (cmd->async_reply_id == 0) cmd->async_reply_id = ++cmd->conn->async_id_counter; o_stream_nsend_str(cmd->conn->output, t_strdup_printf("%c%u\n", DICT_PROTOCOL_REPLY_ASYNC_ID, cmd->async_reply_id)); } static void cmd_stats_update(struct dict_connection_cmd *cmd, struct timing *timing) { long long diff; if (!dict_settings->verbose_proctitle) return; diff = timeval_diff_usecs(&ioloop_timeval, &cmd->start_timeval); if (diff < 0) diff = 0; timing_add_usecs(timing, diff); dict_proctitle_update_later(); } static void dict_cmd_reply_handle_timings(struct dict_connection_cmd *cmd, string_t *str, struct timing *timing) { io_loop_time_refresh(); cmd_stats_update(cmd, timing); if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION) return; str_printfa(str, "\t%ld\t%u\t%ld\t%u", (long)cmd->start_timeval.tv_sec, (unsigned int)cmd->start_timeval.tv_usec, (long)ioloop_timeval.tv_sec, (unsigned int)ioloop_timeval.tv_usec); } static void cmd_lookup_write_reply(struct dict_connection_cmd *cmd, const char *const *values, string_t *str) { string_t *tmp; i_assert(values[0] != NULL); if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_VERSION_MIN_MULTI_OK || values[1] == NULL) { str_append_c(str, DICT_PROTOCOL_REPLY_OK); str_append_tabescaped(str, values[0]); return; } /* the results get double-tabescaped so they end up becoming a single parameter */ tmp = t_str_new(128); for (unsigned int i = 0; values[i] != NULL; i++) { str_append_c(tmp, '\t'); str_append_tabescaped(tmp, values[i]); } str_append_c(str, DICT_PROTOCOL_REPLY_MULTI_OK); str_append_tabescaped(str, str_c(tmp) + 1); } static void cmd_lookup_callback(const struct dict_lookup_result *result, void *context) { struct dict_connection_cmd *cmd = context; string_t *str = t_str_new(128); if (result->ret > 0) { cmd_lookup_write_reply(cmd, result->values, str); } else if (result->ret == 0) { str_append_c(str, DICT_PROTOCOL_REPLY_NOTFOUND); } else { i_error("%s", result->error); str_append_c(str, DICT_PROTOCOL_REPLY_FAIL); str_append_tabescaped(str, result->error); } dict_cmd_reply_handle_timings(cmd, str, cmd_stats.lookups); str_append_c(str, '\n'); cmd->reply = i_strdup(str_c(str)); dict_connection_cmd_try_flush(&cmd); } static int cmd_lookup(struct dict_connection_cmd *cmd, const char *line) { /* */ dict_connection_cmd_async(cmd); dict_lookup_async(cmd->conn->dict, line, cmd_lookup_callback, cmd); return 1; } static bool dict_connection_flush_if_full(struct dict_connection *conn) { if (o_stream_get_buffer_used_size(conn->output) > DICT_OUTPUT_OPTIMAL_SIZE) { if (o_stream_flush(conn->output) <= 0) { /* continue later when there's more space in output buffer */ o_stream_set_flush_pending(conn->output, TRUE); return FALSE; } /* flushed everything, continue */ } return TRUE; } static int cmd_iterate_flush(struct dict_connection_cmd *cmd) { string_t *str; const char *key, *value; if (!dict_connection_flush_if_full(cmd->conn)) return 0; str = t_str_new(256); while (dict_iterate(cmd->iter, &key, &value)) { str_truncate(str, 0); if (cmd->async_reply_id != 0) { str_append_c(str, DICT_PROTOCOL_REPLY_ASYNC_REPLY); str_printfa(str, "%u\t", cmd->async_reply_id); } str_append_c(str, DICT_PROTOCOL_REPLY_OK); str_append_tabescaped(str, key); str_append_c(str, '\t'); if ((cmd->iter_flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) str_append_tabescaped(str, value); str_append_c(str, '\n'); o_stream_nsend(cmd->conn->output, str_data(str), str_len(str)); if (!dict_connection_flush_if_full(cmd->conn)) return 0; } if (dict_iterate_has_more(cmd->iter)) { /* wait for the next iteration callback */ return 0; } str_truncate(str, 0); if (dict_iterate_deinit(&cmd->iter) < 0) str_append_c(str, DICT_PROTOCOL_REPLY_FAIL); dict_cmd_reply_handle_timings(cmd, str, cmd_stats.iterations); str_append_c(str, '\n'); cmd->reply = i_strdup(str_c(str)); return 1; } static void cmd_iterate_callback(void *context) { struct dict_connection_cmd *cmd = context; struct dict_connection *conn = cmd->conn; dict_connection_ref(conn); o_stream_cork(conn->output); dict_connection_cmd_output_more(cmd); o_stream_uncork(conn->output); dict_connection_unref_safe(conn); } static int cmd_iterate(struct dict_connection_cmd *cmd, const char *line) { const char *const *args; unsigned int flags; uint64_t max_rows; args = t_strsplit_tabescaped(line); if (str_array_length(args) < 3 || str_to_uint(args[0], &flags) < 0 || str_to_uint64(args[1], &max_rows) < 0) { i_error("dict client: ITERATE: broken input"); return -1; } dict_connection_cmd_async(cmd); /* */ flags |= DICT_ITERATE_FLAG_ASYNC; cmd->iter = dict_iterate_init_multiple(cmd->conn->dict, args+2, flags); cmd->iter_flags = flags; if (max_rows > 0) dict_iterate_set_limit(cmd->iter, max_rows); dict_iterate_set_async_callback(cmd->iter, cmd_iterate_callback, cmd); dict_connection_cmd_output_more(cmd); return 1; } static struct dict_connection_transaction * dict_connection_transaction_lookup(struct dict_connection *conn, unsigned int id) { struct dict_connection_transaction *transaction; if (!array_is_created(&conn->transactions)) return NULL; array_foreach_modifiable(&conn->transactions, transaction) { if (transaction->id == id) return transaction; } return NULL; } static void dict_connection_transaction_array_remove(struct dict_connection *conn, unsigned int id) { const struct dict_connection_transaction *transactions; unsigned int i, count; transactions = array_get(&conn->transactions, &count); for (i = 0; i < count; i++) { if (transactions[i].id == id) { i_assert(transactions[i].ctx == NULL); array_delete(&conn->transactions, i, 1); return; } } i_unreached(); } static int cmd_begin(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; unsigned int id; if (str_to_uint(line, &id) < 0) { i_error("dict client: Invalid transaction ID %s", line); return -1; } if (dict_connection_transaction_lookup(cmd->conn, id) != NULL) { i_error("dict client: Transaction ID %u already exists", id); return -1; } if (!array_is_created(&cmd->conn->transactions)) i_array_init(&cmd->conn->transactions, 4); /* */ trans = array_append_space(&cmd->conn->transactions); trans->id = id; trans->conn = cmd->conn; trans->ctx = dict_transaction_begin(cmd->conn->dict); return 0; } static int dict_connection_transaction_lookup_parse(struct dict_connection *conn, const char *line, struct dict_connection_transaction **trans_r) { unsigned int id; if (str_to_uint(line, &id) < 0) { i_error("dict client: Invalid transaction ID %s", line); return -1; } *trans_r = dict_connection_transaction_lookup(conn, id); if (*trans_r == NULL) { i_error("dict client: Transaction ID %u doesn't exist", id); return -1; } return 0; } static void cmd_commit_finish(struct dict_connection_cmd *cmd, int ret, bool async) { string_t *str = t_str_new(64); char chr; switch (ret) { case 1: chr = DICT_PROTOCOL_REPLY_OK; break; case 0: chr = DICT_PROTOCOL_REPLY_NOTFOUND; break; case DICT_COMMIT_RET_WRITE_UNCERTAIN: chr = DICT_PROTOCOL_REPLY_WRITE_UNCERTAIN; break; case DICT_COMMIT_RET_FAILED: default: chr = DICT_PROTOCOL_REPLY_FAIL; break; } if (async) { str_printfa(str, "%c%c%u", DICT_PROTOCOL_REPLY_ASYNC_COMMIT, chr, cmd->trans_id); } else { str_printfa(str, "%c%u", chr, cmd->trans_id); } dict_cmd_reply_handle_timings(cmd, str, cmd_stats.commits); str_append_c(str, '\n'); cmd->reply = i_strdup(str_c(str)); dict_connection_transaction_array_remove(cmd->conn, cmd->trans_id); dict_connection_cmd_try_flush(&cmd); } static void cmd_commit_callback(int ret, void *context) { struct dict_connection_cmd *cmd = context; cmd_commit_finish(cmd, ret, FALSE); } static void cmd_commit_callback_async(int ret, void *context) { struct dict_connection_cmd *cmd = context; cmd_commit_finish(cmd, ret, TRUE); } static int cmd_commit(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; if (dict_connection_transaction_lookup_parse(cmd->conn, line, &trans) < 0) return -1; cmd->trans_id = trans->id; dict_connection_cmd_async(cmd); dict_transaction_commit_async(&trans->ctx, cmd_commit_callback, cmd); return 1; } static int cmd_commit_async(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; if (dict_connection_transaction_lookup_parse(cmd->conn, line, &trans) < 0) return -1; cmd->trans_id = trans->id; dict_connection_cmd_async(cmd); dict_transaction_commit_async(&trans->ctx, cmd_commit_callback_async, cmd); return 1; } static int cmd_rollback(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; if (dict_connection_transaction_lookup_parse(cmd->conn, line, &trans) < 0) return -1; dict_transaction_rollback(&trans->ctx); dict_connection_transaction_array_remove(cmd->conn, trans->id); return 0; } static int cmd_set(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; const char *const *args; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) != 3) { i_error("dict client: SET: broken input"); return -1; } if (dict_connection_transaction_lookup_parse(cmd->conn, args[0], &trans) < 0) return -1; dict_set(trans->ctx, args[1], args[2]); return 0; } static int cmd_unset(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; const char *const *args; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) != 2) { i_error("dict client: UNSET: broken input"); return -1; } if (dict_connection_transaction_lookup_parse(cmd->conn, args[0], &trans) < 0) return -1; dict_unset(trans->ctx, args[1]); return 0; } static int cmd_append(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; const char *const *args; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) != 3) { i_error("dict client: APPEND: broken input"); return -1; } if (dict_connection_transaction_lookup_parse(cmd->conn, args[0], &trans) < 0) return -1; dict_append(trans->ctx, args[1], args[2]); return 0; } static int cmd_atomic_inc(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; const char *const *args; long long diff; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) != 3 || str_to_llong(args[2], &diff) < 0) { i_error("dict client: ATOMIC_INC: broken input"); return -1; } if (dict_connection_transaction_lookup_parse(cmd->conn, args[0], &trans) < 0) return -1; dict_atomic_inc(trans->ctx, args[1], diff); return 0; } static int cmd_timestamp(struct dict_connection_cmd *cmd, const char *line) { struct dict_connection_transaction *trans; const char *const *args; long long tv_sec; unsigned int tv_nsec; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) != 3 || str_to_llong(args[1], &tv_sec) < 0 || str_to_uint(args[2], &tv_nsec) < 0) { i_error("dict client: TIMESTAMP: broken input"); return -1; } if (dict_connection_transaction_lookup_parse(cmd->conn, args[0], &trans) < 0) return -1; struct timespec ts = { .tv_sec = tv_sec, .tv_nsec = tv_nsec }; dict_transaction_set_timestamp(trans->ctx, &ts); return 0; } static const struct dict_cmd_func cmds[] = { { DICT_PROTOCOL_CMD_LOOKUP, cmd_lookup }, { DICT_PROTOCOL_CMD_ITERATE, cmd_iterate }, { DICT_PROTOCOL_CMD_BEGIN, cmd_begin }, { DICT_PROTOCOL_CMD_COMMIT, cmd_commit }, { DICT_PROTOCOL_CMD_COMMIT_ASYNC, cmd_commit_async }, { DICT_PROTOCOL_CMD_ROLLBACK, cmd_rollback }, { DICT_PROTOCOL_CMD_SET, cmd_set }, { DICT_PROTOCOL_CMD_UNSET, cmd_unset }, { DICT_PROTOCOL_CMD_APPEND, cmd_append }, { DICT_PROTOCOL_CMD_ATOMIC_INC, cmd_atomic_inc }, { DICT_PROTOCOL_CMD_TIMESTAMP, cmd_timestamp }, { 0, NULL } }; static const struct dict_cmd_func *dict_command_find(enum dict_protocol_cmd cmd) { unsigned int i; for (i = 0; cmds[i].cmd != '\0'; i++) { if (cmds[i].cmd == cmd) return &cmds[i]; } return NULL; } int dict_command_input(struct dict_connection *conn, const char *line) { const struct dict_cmd_func *cmd_func; struct dict_connection_cmd *cmd; int ret; cmd_func = dict_command_find((enum dict_protocol_cmd)*line); if (cmd_func == NULL) { i_error("dict client: Unknown command %c", *line); return -1; } cmd = i_new(struct dict_connection_cmd, 1); cmd->conn = conn; cmd->cmd = cmd_func; cmd->start_timeval = ioloop_timeval; array_append(&conn->cmds, &cmd, 1); dict_connection_ref(conn); if ((ret = cmd_func->func(cmd, line + 1)) <= 0) { dict_connection_cmd_remove(cmd); return ret; } return 0; } static bool dict_connection_cmds_try_output_more(struct dict_connection *conn) { struct dict_connection_cmd *const *cmdp, *cmd; /* only iterators may be returning a lot of data */ array_foreach(&conn->cmds, cmdp) { cmd = *cmdp; if (cmd->iter == NULL) { /* not an iterator */ } else if (cmd_iterate_flush(cmd) == 0) { /* unfinished */ } else { dict_connection_cmd_try_flush(&cmd); /* cmd should be freed now, restart output */ return TRUE; } if (conn->minor_version < DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION) break; /* try to flush the rest */ } return FALSE; } void dict_connection_cmds_output_more(struct dict_connection *conn) { while (array_count(&conn->cmds) > 0) { if (!dict_connection_cmds_try_output_more(conn)) break; } } static void dict_connection_cmd_output_more(struct dict_connection_cmd *cmd) { struct dict_connection_cmd *const *first_cmdp; if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION) { first_cmdp = array_idx(&cmd->conn->cmds, 0); if (*first_cmdp != cmd) return; } (void)dict_connection_cmds_try_output_more(cmd->conn); } void dict_commands_init(void) { cmd_stats.lookups = timing_init(); cmd_stats.iterations = timing_init(); cmd_stats.commits = timing_init(); } void dict_commands_deinit(void) { timing_deinit(&cmd_stats.lookups); timing_deinit(&cmd_stats.iterations); timing_deinit(&cmd_stats.commits); } dovecot-2.2.33.2/src/dict/dict-connection.c0000644000175000017500000001741613165463624015306 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "master-service.h" #include "dict-client.h" #include "dict-settings.h" #include "dict-commands.h" #include "dict-connection.h" #include #define DICT_CONN_MAX_PENDING_COMMANDS 1000 static struct dict_connection *dict_connections; static unsigned int dict_connections_count = 0; static int dict_connection_parse_handshake(struct dict_connection *conn, const char *line) { const char *username, *name, *value_type; unsigned int value_type_num; if (*line++ != DICT_PROTOCOL_CMD_HELLO) return -1; /* check major version */ if (*line++ - '0' != DICT_CLIENT_PROTOCOL_MAJOR_VERSION || *line++ != '\t') return -1; /* read minor version */ if (str_parse_uint(line, &conn->minor_version, &line) < 0) return -1; if (*line++ != '\t') return -1; /* get value type */ value_type = line; while (*line != '\t' && *line != '\0') line++; if (*line++ != '\t') return -1; if (str_to_uint(t_strdup_until(value_type, line - 1), &value_type_num) < 0) return -1; if (value_type_num >= DICT_DATA_TYPE_LAST) return -1; conn->value_type = (enum dict_data_type)value_type_num; /* get username */ username = line; while (*line != '\t' && *line != '\0') line++; if (*line++ != '\t') return -1; conn->username = i_strdup_until(username, line - 1); /* the rest is dict name. since we're looking it with getenv(), disallow all funny characters that might confuse it, just in case. */ name = line; while (*line > ' ' && *line != '=') line++; if (*line != '\0') return -1; conn->name = i_strdup(name); return 0; } static int dict_connection_dict_init(struct dict_connection *conn) { const char *const *strlist; unsigned int i, count; const char *uri, *error; if (!array_is_created(&dict_settings->dicts)) { i_error("dict client: No dictionaries configured"); return -1; } strlist = array_get(&dict_settings->dicts, &count); for (i = 0; i < count; i += 2) { if (strcmp(strlist[i], conn->name) == 0) break; } if (i == count) { i_error("dict client: Unconfigured dictionary name '%s'", conn->name); return -1; } uri = strlist[i+1]; if (dict_init(uri, conn->value_type, conn->username, dict_settings->base_dir, &conn->dict, &error) < 0) { /* dictionary initialization failed */ i_error("Failed to initialize dictionary '%s': %s", conn->name, error); return -1; } return 0; } static void dict_connection_input_more(struct dict_connection *conn) { const char *line; int ret; if (conn->to_input != NULL) timeout_remove(&conn->to_input); while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = dict_command_input(conn, line); } T_END; if (ret < 0) { dict_connection_destroy(conn); break; } if (array_count(&conn->cmds) >= DICT_CONN_MAX_PENDING_COMMANDS) { io_remove(&conn->io); if (conn->to_input != NULL) timeout_remove(&conn->to_input); break; } } } static void dict_connection_input(struct dict_connection *conn) { const char *line; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ dict_connection_destroy(conn); return; case -2: /* buffer full */ i_error("dict client: Sent us more than %d bytes", (int)DICT_CLIENT_MAX_LINE_LENGTH); dict_connection_destroy(conn); return; } if (conn->username == NULL) { /* handshake not received yet */ if ((line = i_stream_next_line(conn->input)) == NULL) return; if (dict_connection_parse_handshake(conn, line) < 0) { i_error("dict client: Broken handshake"); dict_connection_destroy(conn); return; } if (dict_connection_dict_init(conn)) { dict_connection_destroy(conn); return; } } dict_connection_input_more(conn); } void dict_connection_continue_input(struct dict_connection *conn) { if (conn->io != NULL || conn->destroyed) return; conn->io = io_add(conn->fd, IO_READ, dict_connection_input, conn); if (conn->to_input == NULL) conn->to_input = timeout_add_short(0, dict_connection_input_more, conn); } static int dict_connection_output(struct dict_connection *conn) { struct ostream *output = conn->output; int ret; o_stream_cork(output); if ((ret = o_stream_flush(conn->output)) < 0) { dict_connection_destroy(conn); ret = 1; } else if (ret > 0) { dict_connection_cmds_output_more(conn); } o_stream_uncork(output); return ret; } struct dict_connection *dict_connection_create(int fd) { struct dict_connection *conn; conn = i_new(struct dict_connection, 1); conn->refcount = 1; conn->fd = fd; conn->input = i_stream_create_fd(fd, DICT_CLIENT_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_fd(fd, 128*1024, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_set_flush_callback(conn->output, dict_connection_output, conn); conn->io = io_add(fd, IO_READ, dict_connection_input, conn); i_array_init(&conn->cmds, DICT_CONN_MAX_PENDING_COMMANDS); dict_connections_count++; DLLIST_PREPEND(&dict_connections, conn); return conn; } void dict_connection_ref(struct dict_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } bool dict_connection_unref(struct dict_connection *conn) { struct dict_connection_transaction *transaction; i_assert(conn->refcount > 0); if (--conn->refcount > 0) return TRUE; i_assert(array_count(&conn->cmds) == 0); /* we should have only transactions that haven't been committed or rollbacked yet. close those before dict is deinitialized. */ if (array_is_created(&conn->transactions)) { array_foreach_modifiable(&conn->transactions, transaction) { if (transaction->ctx != NULL) dict_transaction_rollback(&transaction->ctx); } } if (conn->dict != NULL) dict_deinit(&conn->dict); if (array_is_created(&conn->transactions)) array_free(&conn->transactions); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); array_free(&conn->cmds); i_free(conn->name); i_free(conn->username); i_free(conn); master_service_client_connection_destroyed(master_service); return FALSE; } static void dict_connection_unref_safe_callback(struct dict_connection *conn) { if (conn->to_unref != NULL) timeout_remove(&conn->to_unref); (void)dict_connection_unref(conn); } void dict_connection_unref_safe(struct dict_connection *conn) { if (conn->refcount == 1) { /* delayed unref to make sure we don't try to call dict_deinit() from a dict-callback. that's too much trouble for each dict driver to be able to handle. */ if (conn->to_unref == NULL) { conn->to_unref = timeout_add_short(0, dict_connection_unref_safe_callback, conn); } } else { (void)dict_connection_unref(conn); } } void dict_connection_destroy(struct dict_connection *conn) { i_assert(!conn->destroyed); i_assert(conn->to_unref == NULL); i_assert(dict_connections_count > 0); dict_connections_count--; conn->destroyed = TRUE; DLLIST_REMOVE(&dict_connections, conn); if (conn->to_input != NULL) timeout_remove(&conn->to_input); if (conn->io != NULL) io_remove(&conn->io); i_stream_close(conn->input); o_stream_close(conn->output); if (close(conn->fd) < 0) i_error("close(dict client) failed: %m"); conn->fd = -1; /* the connection is closed, but there may still be commands left running. finish them, even if the calling client can't be notified about whether they succeeded (clients may not even care). flush the command output here in case we were waiting on iteration output. */ dict_connection_cmds_output_more(conn); dict_connection_unref(conn); } unsigned int dict_connections_current_count(void) { return dict_connections_count; } void dict_connections_destroy_all(void) { while (dict_connections != NULL) dict_connection_destroy(dict_connections); } dovecot-2.2.33.2/src/dict/dict-settings.c0000644000175000017500000000551313123174404014767 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "dict-settings.h" /* */ static struct file_listener_settings dict_unix_listeners_array[] = { { "dict", 0600, "", "" } }; static struct file_listener_settings *dict_unix_listeners[] = { &dict_unix_listeners_array[0] }; static buffer_t dict_unix_listeners_buf = { dict_unix_listeners, sizeof(dict_unix_listeners), { NULL, } }; static struct file_listener_settings dict_async_unix_listeners_array[] = { { "dict-async", 0600, "", "" } }; static struct file_listener_settings *dict_async_unix_listeners[] = { &dict_async_unix_listeners_array[0] }; static buffer_t dict_async_unix_listeners_buf = { dict_async_unix_listeners, sizeof(dict_async_unix_listeners), { NULL, } }; /* */ struct service_settings dict_service_settings = { .name = "dict", .protocol = "", .type = "", .executable = "dict", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dict_unix_listeners_buf, sizeof(dict_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; struct service_settings dict_async_service_settings = { .name = "dict-async", .protocol = "", .type = "", .executable = "dict", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dict_async_unix_listeners_buf, sizeof(dict_async_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct dict_server_settings, name), NULL } static const struct setting_define dict_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, dict_db_config), { SET_STRLIST, "dict", offsetof(struct dict_server_settings, dicts), NULL }, SETTING_DEFINE_LIST_END }; const struct dict_server_settings dict_default_settings = { .base_dir = PKG_RUNDIR, .verbose_proctitle = FALSE, .dict_db_config = "", .dicts = ARRAY_INIT }; const struct setting_parser_info dict_setting_parser_info = { .module_name = "dict", .defines = dict_setting_defines, .defaults = &dict_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct dict_server_settings), .parent_offset = (size_t)-1 }; const struct dict_server_settings *dict_settings; dovecot-2.2.33.2/src/dict/dict-settings.h0000644000175000017500000000050013123174404014763 00000000000000#ifndef DICT_SETTINGS_H #define DICT_SETTINGS_H struct dict_server_settings { const char *base_dir; bool verbose_proctitle; const char *dict_db_config; ARRAY(const char *) dicts; }; extern const struct setting_parser_info dict_setting_parser_info; extern const struct dict_server_settings *dict_settings; #endif dovecot-2.2.33.2/src/dict/Makefile.am0000644000175000017500000000130013165463624014077 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = dict AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-sql \ -DDICT_MODULE_DIR=\""$(moduledir)/dict"\" \ -DPKG_RUNDIR=\""$(rundir)"\" dict_LDFLAGS = -export-dynamic libs = \ ../lib-dict/libdict_backend.a \ $(LIBDOVECOT_SQL) dict_LDADD = \ $(libs) \ $(LIBDOVECOT) \ $(DICT_LIBS) \ $(SQL_LIBS) dict_DEPENDENCIES = $(libs) $(LIBDOVECOT_DEPS) dict_SOURCES = \ dict-connection.c \ dict-commands.c \ dict-settings.c \ main.c noinst_HEADERS = \ dict-connection.h \ dict-commands.h \ dict-settings.h \ main.h dovecot-2.2.33.2/src/lib-fs/0002755000175000017500000000000013172375610012357 500000000000000dovecot-2.2.33.2/src/lib-fs/fs-sis.c0000644000175000017500000002113513165463624013654 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "ostream-cmp.h" #include "fs-sis-common.h" #define FS_SIS_REQUIRED_PROPS \ (FS_PROPERTY_FASTCOPY | FS_PROPERTY_STAT) struct sis_fs { struct fs fs; }; struct sis_fs_file { struct fs_file file; struct sis_fs *fs; enum fs_open_mode open_mode; struct fs_file *hash_file; struct istream *hash_input; struct ostream *fs_output; char *hash, *hash_path; bool opened; }; static struct fs *fs_sis_alloc(void) { struct sis_fs *fs; fs = i_new(struct sis_fs, 1); fs->fs = fs_class_sis; return &fs->fs; } static int fs_sis_init(struct fs *_fs, const char *args, const struct fs_settings *set) { enum fs_properties props; const char *parent_name, *parent_args, *error; if (*args == '\0') { fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } parent_args = strchr(args, ':'); if (parent_args == NULL) { parent_name = args; parent_args = ""; } else { parent_name = t_strdup_until(args, parent_args); parent_args++; } if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s", error); return -1; } props = fs_get_properties(_fs->parent); if ((props & FS_SIS_REQUIRED_PROPS) != FS_SIS_REQUIRED_PROPS) { fs_set_error(_fs, "%s backend can't be used with SIS", parent_name); return -1; } return 0; } static void fs_sis_deinit(struct fs *_fs) { struct sis_fs *fs = (struct sis_fs *)_fs; if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs); } static struct fs_file * fs_sis_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct sis_fs *fs = (struct sis_fs *)_fs; struct sis_fs_file *file; const char *dir, *hash; file = i_new(struct sis_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->fs = fs; file->open_mode = mode; if (mode == FS_OPEN_MODE_APPEND) { fs_set_error(_fs, "APPEND mode not supported"); return &file->file; } if (fs_sis_path_parse(_fs, path, &dir, &hash) < 0) { fs_set_error(_fs, "Invalid path"); return &file->file; } /* if hashes/ already exists, open it */ file->hash_path = i_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash); file->hash_file = fs_file_init(_fs->parent, file->hash_path, FS_OPEN_MODE_READONLY); file->hash_input = fs_read_stream(file->hash_file, IO_BLOCK_SIZE); if (i_stream_read(file->hash_input) == -1) { /* doesn't exist */ if (errno != ENOENT) { i_error("fs-sis: Couldn't read hash file %s: %m", file->hash_path); } i_stream_destroy(&file->hash_input); } file->file.parent = fs_file_init(_fs->parent, path, mode | flags); return &file->file; } static void fs_sis_file_deinit(struct fs_file *_file) { struct sis_fs_file *file = (struct sis_fs_file *)_file; fs_file_deinit(&file->hash_file); fs_file_deinit(&_file->parent); i_free(file->hash); i_free(file->hash_path); i_free(file->file.path); i_free(file); } static void fs_sis_file_close(struct fs_file *_file) { struct sis_fs_file *file = (struct sis_fs_file *)_file; if (file->hash_input != NULL) i_stream_unref(&file->hash_input); fs_file_close(file->hash_file); fs_file_close(_file->parent); } static bool fs_sis_try_link(struct sis_fs_file *file) { const struct stat *st; struct stat st2; if (i_stream_stat(file->hash_input, FALSE, &st) < 0) return FALSE; /* we can use the existing file */ if (fs_copy(file->hash_file, file->file.parent) < 0) { if (errno != ENOENT && errno != EMLINK) i_error("fs-sis: %s", fs_file_last_error(file->hash_file)); /* failed to use link(), continue as if it hadn't been equal */ return FALSE; } if (fs_stat(file->file.parent, &st2) < 0) { i_error("fs-sis: %s", fs_file_last_error(file->file.parent)); if (fs_delete(file->file.parent) < 0) i_error("fs-sis: %s", fs_file_last_error(file->file.parent)); return FALSE; } if (st->st_ino != st2.st_ino) { /* the hashes/ file was already replaced with something else */ if (fs_delete(file->file.parent) < 0) i_error("fs-sis: %s", fs_file_last_error(file->file.parent)); return FALSE; } return TRUE; } static void fs_sis_replace_hash_file(struct sis_fs_file *file) { struct fs *super_fs = file->file.parent->fs; struct fs_file *temp_file; const char *hash_fname; string_t *temp_path; int ret; if (file->hash_input == NULL) { /* hash file didn't exist previously. we should be able to create it with link() */ if (fs_copy(file->file.parent, file->hash_file) < 0) { if (errno == EEXIST) { /* the file was just created. it's probably a duplicate, but it's too much trouble trying to deduplicate it anymore */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } } return; } temp_path = t_str_new(256); hash_fname = strrchr(file->hash_path, '/'); if (hash_fname == NULL) hash_fname = file->hash_path; else { str_append_n(temp_path, file->hash_path, (hash_fname-file->hash_path) + 1); hash_fname++; } str_printfa(temp_path, "%s%s.tmp", super_fs->set.temp_file_prefix, hash_fname); /* replace existing hash file atomically */ temp_file = fs_file_init(super_fs, str_c(temp_path), FS_OPEN_MODE_READONLY); ret = fs_copy(file->file.parent, temp_file); if (ret < 0 && errno == EEXIST) { /* either someone's racing us or it's a stale file. try to continue. */ if (fs_delete(temp_file) < 0 && errno != ENOENT) i_error("fs-sis: %s", fs_last_error(super_fs)); ret = fs_copy(file->file.parent, temp_file); } if (ret < 0) { i_error("fs-sis: %s", fs_last_error(super_fs)); fs_file_deinit(&temp_file); return; } if (fs_rename(temp_file, file->hash_file) < 0) { if (errno == ENOENT) { /* apparently someone else just renamed it. ignore. */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } (void)fs_delete(temp_file); } fs_file_deinit(&temp_file); } static int fs_sis_write(struct fs_file *_file, const void *data, size_t size) { struct sis_fs_file *file = (struct sis_fs_file *)_file; if (_file->parent == NULL) return -1; if (file->hash_input != NULL && stream_cmp_block(file->hash_input, data, size) && i_stream_is_eof(file->hash_input)) { /* try to use existing file */ if (fs_sis_try_link(file)) return 0; } if (fs_write(_file->parent, data, size) < 0) return -1; T_BEGIN { fs_sis_replace_hash_file(file); } T_END; return 0; } static void fs_sis_write_stream(struct fs_file *_file) { struct sis_fs_file *file = (struct sis_fs_file *)_file; i_assert(_file->output == NULL); if (_file->parent == NULL) { _file->output = o_stream_create_error_str(EINVAL, "%s", fs_file_last_error(_file)); } else { file->fs_output = fs_write_stream(_file->parent); if (file->hash_input == NULL) { _file->output = file->fs_output; o_stream_ref(_file->output); } else { /* compare if files are equal */ _file->output = o_stream_create_cmp(file->fs_output, file->hash_input); } } o_stream_set_name(_file->output, _file->path); } static int fs_sis_write_stream_finish(struct fs_file *_file, bool success) { struct sis_fs_file *file = (struct sis_fs_file *)_file; if (!success) { if (_file->parent != NULL) fs_write_stream_abort_parent(_file, &file->fs_output); o_stream_unref(&_file->output); return -1; } if (file->hash_input != NULL && o_stream_cmp_equals(_file->output) && i_stream_is_eof(file->hash_input)) { o_stream_unref(&_file->output); if (fs_sis_try_link(file)) { fs_write_stream_abort_parent(_file, &file->fs_output); return 1; } } if (_file->output != NULL) o_stream_unref(&_file->output); if (fs_write_stream_finish(_file->parent, &file->fs_output) < 0) return -1; T_BEGIN { fs_sis_replace_hash_file(file); } T_END; return 1; } static int fs_sis_delete(struct fs_file *_file) { T_BEGIN { fs_sis_try_unlink_hash_file(_file->fs, _file->parent); } T_END; return fs_delete(_file->parent); } const struct fs fs_class_sis = { .name = "sis", .v = { fs_sis_alloc, fs_sis_init, fs_sis_deinit, fs_wrapper_get_properties, fs_sis_file_init, fs_sis_file_deinit, fs_sis_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_wrapper_get_metadata, fs_wrapper_prefetch, fs_wrapper_read, fs_wrapper_read_stream, fs_sis_write, fs_sis_write_stream, fs_sis_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_wrapper_stat, fs_wrapper_copy, fs_wrapper_rename, fs_sis_delete, fs_wrapper_iter_init, NULL, NULL, NULL, fs_wrapper_get_nlinks, } }; dovecot-2.2.33.2/src/lib-fs/Makefile.in0000644000175000017500000006223713172375573014364 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-fs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libfs_la_LIBADD = am_libfs_la_OBJECTS = fs-api.lo fs-dict.lo fs-metawrap.lo \ fs-randomfail.lo fs-posix.lo fs-test.lo fs-test-async.lo \ fs-sis.lo fs-sis-common.lo fs-sis-queue.lo fs-wrapper.lo \ istream-fs-file.lo istream-fs-stats.lo istream-metawrap.lo \ ostream-metawrap.lo ostream-cmp.lo libfs_la_OBJECTS = $(am_libfs_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-fs-metawrap$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_fs_metawrap_OBJECTS = test-fs-metawrap.$(OBJEXT) test_fs_metawrap_OBJECTS = $(am_test_fs_metawrap_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(test_deps) $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libfs_la_SOURCES) $(test_fs_metawrap_SOURCES) DIST_SOURCES = $(libfs_la_SOURCES) $(test_fs_metawrap_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libfs.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DMODULE_DIR=\""$(moduledir)"\" libfs_la_SOURCES = \ fs-api.c \ fs-dict.c \ fs-metawrap.c \ fs-randomfail.c \ fs-posix.c \ fs-test.c \ fs-test-async.c \ fs-sis.c \ fs-sis-common.c \ fs-sis-queue.c \ fs-wrapper.c \ istream-fs-file.c \ istream-fs-stats.c \ istream-metawrap.c \ ostream-metawrap.c \ ostream-cmp.c headers = \ fs-api.h \ fs-api-private.h \ fs-sis-common.h \ fs-wrapper.h \ fs-test.h \ istream-fs-file.h \ istream-fs-stats.h \ istream-metawrap.h \ ostream-metawrap.h \ ostream-cmp.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-fs-metawrap test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-dict/libdict.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_libs = \ $(test_deps) \ $(MODULE_LIBS) test_fs_metawrap_SOURCES = test-fs-metawrap.c test_fs_metawrap_LDADD = $(test_libs) test_fs_metawrap_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-fs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-fs/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libfs.la: $(libfs_la_OBJECTS) $(libfs_la_DEPENDENCIES) $(EXTRA_libfs_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libfs_la_OBJECTS) $(libfs_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-fs-metawrap$(EXEEXT): $(test_fs_metawrap_OBJECTS) $(test_fs_metawrap_DEPENDENCIES) $(EXTRA_test_fs_metawrap_DEPENDENCIES) @rm -f test-fs-metawrap$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_fs_metawrap_OBJECTS) $(test_fs_metawrap_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-metawrap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-randomfail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-sis-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-sis-queue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-sis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-test-async.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-test.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-wrapper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-fs-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-fs-stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-metawrap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-cmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-metawrap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fs-metawrap.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-fs/istream-fs-file.c0000644000175000017500000000343613165463624015443 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "fs-api-private.h" #include "istream-fs-file.h" struct fs_file_istream { struct istream_private istream; struct fs_file *file; }; static void i_stream_fs_file_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct fs_file_istream *fstream = (struct fs_file_istream *)stream; if (fstream->istream.parent != NULL) i_stream_destroy(&fstream->istream.parent); fs_file_deinit(&fstream->file); } static ssize_t i_stream_fs_file_read(struct istream_private *stream) { struct fs_file_istream *fstream = (struct fs_file_istream *)stream; struct istream *input; if (fstream->istream.parent == NULL) { input = fs_read_stream(fstream->file, i_stream_get_max_buffer_size(&stream->istream)); i_stream_init_parent(stream, input); i_stream_unref(&input); } i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); return i_stream_read_copy_from_parent(&stream->istream); } struct istream * i_stream_create_fs_file(struct fs_file **file, size_t max_buffer_size) { struct fs_file_istream *fstream; struct istream *input; fstream = i_new(struct fs_file_istream, 1); fstream->file = *file; fstream->istream.iostream.close = i_stream_fs_file_close; fstream->istream.max_buffer_size = max_buffer_size; fstream->istream.read = i_stream_fs_file_read; fstream->istream.stream_size_passthrough = TRUE; fstream->istream.istream.blocking = ((*file)->flags & FS_OPEN_FLAG_ASYNC) == 0; fstream->istream.istream.seekable = ((*file)->flags & FS_OPEN_FLAG_SEEKABLE) != 0; input = i_stream_create(&fstream->istream, NULL, -1); i_stream_set_name(input, fs_file_path(*file)); *file = NULL; return input; } dovecot-2.2.33.2/src/lib-fs/ostream-metawrap.c0000644000175000017500000000363413165463624015744 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream-private.h" #include "ostream-metawrap.h" struct metawrap_ostream { struct ostream_private ostream; void (*write_callback)(void *); void *context; }; static void o_stream_metawrap_call_callback(struct metawrap_ostream *mstream) { void (*write_callback)(void *) = mstream->write_callback; if (write_callback != NULL) { mstream->write_callback = NULL; write_callback(mstream->context); /* metadata headers aren't counted as part of the offset */ mstream->ostream.ostream.offset = 0; } } static ssize_t o_stream_metawrap_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct metawrap_ostream *mstream = (struct metawrap_ostream *)stream; ssize_t ret; o_stream_metawrap_call_callback(mstream); if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) o_stream_copy_error_from_parent(stream); else stream->ostream.offset += ret; return ret; } static off_t o_stream_metawrap_send_istream(struct ostream_private *_outstream, struct istream *instream) { struct metawrap_ostream *outstream = (struct metawrap_ostream *)_outstream; off_t ret; o_stream_metawrap_call_callback(outstream); if ((ret = o_stream_send_istream(_outstream->parent, instream)) < 0) o_stream_copy_error_from_parent(_outstream); else _outstream->ostream.offset += ret; return ret; } struct ostream * o_stream_create_metawrap(struct ostream *output, void (*write_callback)(void *), void *context) { struct metawrap_ostream *mstream; mstream = i_new(struct metawrap_ostream, 1); mstream->ostream.sendv = o_stream_metawrap_sendv; mstream->ostream.send_istream = o_stream_metawrap_send_istream; mstream->write_callback = write_callback; mstream->context = context; return o_stream_create(&mstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib-fs/fs-test.h0000644000175000017500000000153713123174404014033 00000000000000#ifndef FS_TEST_H #define FS_TEST_H #include "fs-api-private.h" struct test_fs { struct fs fs; enum fs_properties properties; ARRAY_TYPE(const_string) iter_files; }; struct test_fs_file { struct fs_file file; enum fs_open_mode mode; fs_file_async_callback_t *async_callback; void *async_context; buffer_t *contents; struct istream *input; struct test_fs_file *copy_src; bool prefetched; bool locked; bool exists; bool seekable; bool closed; bool io_failure; bool wait_async; }; struct test_fs_iter { struct fs_iter iter; char *prefix, *prev_dir; unsigned int prefix_len, idx; bool failed; }; struct test_fs *test_fs_get(struct fs *fs); struct test_fs_file *test_fs_file_get(struct fs *fs, const char *path); void test_fs_async(const char *test_name, enum fs_properties properties, const char *driver, const char *args); #endif dovecot-2.2.33.2/src/lib-fs/fs-randomfail.c0000644000175000017500000003222013165463624015167 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "istream-private.h" #include "istream-concat.h" #include "istream-failure-at.h" #include "ostream-failure-at.h" #include "ostream.h" #include "fs-api-private.h" #define RANDOMFAIL_ERROR "Random failure injection" static const char *fs_op_names[FS_OP_COUNT] = { "wait", "metadata", "prefetch", "read", "write", "lock", "exists", "stat", "copy", "rename", "delete", "iter" }; struct randomfail_fs { struct fs fs; unsigned int op_probability[FS_OP_COUNT]; uoff_t range_start[FS_OP_COUNT], range_end[FS_OP_COUNT]; }; struct randomfail_fs_file { struct fs_file file; struct fs_file *super_read; struct istream *input; bool op_pending[FS_OP_COUNT]; struct ostream *super_output; }; struct randomfail_fs_iter { struct fs_iter iter; struct fs_iter *super; unsigned int fail_pos; }; static struct fs *fs_randomfail_alloc(void) { struct randomfail_fs *fs; fs = i_new(struct randomfail_fs, 1); fs->fs = fs_class_randomfail; return &fs->fs; } static bool fs_op_find(const char *str, enum fs_op *op_r) { enum fs_op op; for (op = 0; op < FS_OP_COUNT; op++) { if (strcmp(fs_op_names[op], str) == 0) { *op_r = op; return TRUE; } } return FALSE; } static bool fs_randomfail_add_probability(struct randomfail_fs *fs, const char *key, const char *value, const char **error_r) { unsigned int num; enum fs_op op; bool invalid_value = FALSE; if (str_to_uint(value, &num) < 0 || num > 100) invalid_value = TRUE; if (fs_op_find(key, &op)) { if (invalid_value) { *error_r = "Invalid probability value"; return -1; } fs->op_probability[op] = num; return 1; } if (strcmp(key, "all") == 0) { if (invalid_value) { *error_r = "Invalid probability value"; return -1; } for (op = 0; op < FS_OP_COUNT; op++) fs->op_probability[op] = num; return 1; } return 0; } static int fs_randomfail_add_probability_range(struct randomfail_fs *fs, const char *key, const char *value, const char **error_r) { enum fs_op op; const char *p; uoff_t num1, num2; if (strcmp(key, "read-range") == 0) op = FS_OP_READ; else if (strcmp(key, "write-range") == 0) op = FS_OP_WRITE; else if (strcmp(key, "iter-range") == 0) op = FS_OP_ITER; else return 0; p = strchr(value, '-'); if (p == NULL) { if (str_to_uoff(value, &num1) < 0) { *error_r = "Invalid range value"; return -1; } num2 = num1; } else if (str_to_uoff(t_strdup_until(value, p), &num1) < 0 || str_to_uoff(p+1, &num2) < 0 || num1 > num2) { *error_r = "Invalid range values"; return -1; } fs->range_start[op] = num1; fs->range_end[op] = num2; return 1; } static int fs_randomfail_parse_params(struct randomfail_fs *fs, const char *params, const char **error_r) { const char *const *tmp; int ret; for (tmp = t_strsplit_spaces(params, ","); *tmp != NULL; tmp++) { const char *key = *tmp; const char *value = strchr(key, '='); if (value == NULL) { *error_r = "Missing '='"; return -1; } key = t_strdup_until(key, value++); if ((ret = fs_randomfail_add_probability(fs, key, value, error_r)) != 0) { if (ret < 0) return -1; continue; } if ((ret = fs_randomfail_add_probability_range(fs, key, value, error_r)) != 0) { if (ret < 0) return -1; continue; } *error_r = t_strdup_printf("Unknown key '%s'", key); return -1; } return 0; } static int fs_randomfail_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct randomfail_fs *fs = (struct randomfail_fs *)_fs; const char *p, *parent_name, *parent_args, *error; p = strchr(args, ':'); if (p == NULL) { fs_set_error(_fs, "Randomfail parameters missing"); return -1; } if (fs_randomfail_parse_params(fs, t_strdup_until(args, p++), &error) < 0) { fs_set_error(_fs, "Invalid randomfail parameters: %s", error); return -1; } args = p; if (*args == '\0') { fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } parent_args = strchr(args, ':'); if (parent_args == NULL) { parent_name = args; parent_args = ""; } else { parent_name = t_strdup_until(args, parent_args); parent_args++; } if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s", error); return -1; } return 0; } static void fs_randomfail_deinit(struct fs *_fs) { struct randomfail_fs *fs = (struct randomfail_fs *)_fs; if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs); } static enum fs_properties fs_randomfail_get_properties(struct fs *_fs) { return fs_get_properties(_fs->parent); } static struct fs_file * fs_randomfail_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct randomfail_fs_file *file; file = i_new(struct randomfail_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->file.parent = fs_file_init(_fs->parent, path, mode | flags); return &file->file; } static void fs_randomfail_file_deinit(struct fs_file *_file) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; fs_file_deinit(&file->file.parent); i_free(file->file.path); i_free(file); } static bool fs_random_fail(struct fs *_fs, int divider, enum fs_op op) { struct randomfail_fs *fs = (struct randomfail_fs *)_fs; if (fs->op_probability[op] == 0) return FALSE; if ((unsigned int)(rand() % (100*divider)) <= fs->op_probability[op]) { fs_set_error(_fs, RANDOMFAIL_ERROR); return TRUE; } return FALSE; } static bool fs_file_random_fail_begin(struct randomfail_fs_file *file, enum fs_op op) { if (!file->op_pending[op]) { if (fs_random_fail(file->file.fs, 2, op)) return TRUE; } file->op_pending[op] = TRUE; return FALSE; } static int fs_file_random_fail_end(struct randomfail_fs_file *file, int ret, enum fs_op op) { if (ret == 0 || errno != EAGAIN) { if (fs_random_fail(file->file.fs, 2, op)) return -1; file->op_pending[op] = FALSE; } return ret; } static bool fs_random_fail_range(struct fs *_fs, enum fs_op op, uoff_t *offset_r) { struct randomfail_fs *fs = (struct randomfail_fs *)_fs; if (!fs_random_fail(_fs, 1, op)) return FALSE; *offset_r = fs->range_start[op] + rand() % (fs->range_end[op] - fs->range_start[op] + 1); return TRUE; } static int fs_randomfail_get_metadata(struct fs_file *_file, const ARRAY_TYPE(fs_metadata) **metadata_r) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_METADATA)) return -1; ret = fs_get_metadata(_file->parent, metadata_r); return fs_file_random_fail_end(file, ret, FS_OP_METADATA); } static bool fs_randomfail_prefetch(struct fs_file *_file, uoff_t length) { if (fs_random_fail(_file->fs, 1, FS_OP_PREFETCH)) return TRUE; return fs_prefetch(_file->parent, length); } static ssize_t fs_randomfail_read(struct fs_file *_file, void *buf, size_t size) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_READ)) return -1; ret = fs_read(_file->parent, buf, size); if (fs_file_random_fail_end(file, ret < 0 ? -1 : 0, FS_OP_READ) < 0) return -1; return ret; } static struct istream * fs_randomfail_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct istream *input, *input2; uoff_t offset; input = fs_read_stream(_file->parent, max_buffer_size); if (!fs_random_fail_range(_file->fs, FS_OP_READ, &offset)) return input; input2 = i_stream_create_failure_at(input, offset, RANDOMFAIL_ERROR); i_stream_unref(&input); return input2; } static int fs_randomfail_write(struct fs_file *_file, const void *data, size_t size) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_WRITE)) return -1; ret = fs_write(_file->parent, data, size); return fs_file_random_fail_end(file, ret, FS_OP_EXISTS); } static void fs_randomfail_write_stream(struct fs_file *_file) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; uoff_t offset; i_assert(_file->output == NULL); file->super_output = fs_write_stream(_file->parent); if (!fs_random_fail_range(_file->fs, FS_OP_WRITE, &offset)) _file->output = file->super_output; else { _file->output = o_stream_create_failure_at(file->super_output, offset, RANDOMFAIL_ERROR); } } static int fs_randomfail_write_stream_finish(struct fs_file *_file, bool success) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; if (_file->output != NULL) { if (_file->output == file->super_output) _file->output = NULL; else o_stream_unref(&_file->output); if (!success) { fs_write_stream_abort_parent(_file, &file->super_output); return -1; } if (fs_random_fail(_file->fs, 1, FS_OP_WRITE)) { fs_write_stream_abort_error(_file->parent, &file->super_output, RANDOMFAIL_ERROR); return -1; } } return fs_write_stream_finish(_file->parent, &file->super_output); } static int fs_randomfail_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r) { if (fs_random_fail(_file->fs, 1, FS_OP_LOCK)) return -1; return fs_lock(_file->parent, secs, lock_r); } static void fs_randomfail_unlock(struct fs_lock *_lock ATTR_UNUSED) { i_unreached(); } static int fs_randomfail_exists(struct fs_file *_file) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_EXISTS)) return -1; ret = fs_exists(_file->parent); return fs_file_random_fail_end(file, ret, FS_OP_EXISTS); } static int fs_randomfail_stat(struct fs_file *_file, struct stat *st_r) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_STAT)) return -1; ret = fs_stat(_file->parent, st_r); return fs_file_random_fail_end(file, ret, FS_OP_STAT); } static int fs_randomfail_get_nlinks(struct fs_file *_file, nlink_t *nlinks_r) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_STAT)) return -1; ret = fs_get_nlinks(_file->parent, nlinks_r); return fs_file_random_fail_end(file, ret, FS_OP_STAT); } static int fs_randomfail_copy(struct fs_file *_src, struct fs_file *_dest) { struct randomfail_fs_file *dest = (struct randomfail_fs_file *)_dest; int ret; if (fs_file_random_fail_begin(dest, FS_OP_COPY)) return -1; if (_src != NULL) ret = fs_copy(_src->parent, _dest->parent); else ret = fs_copy_finish_async(_dest->parent); return fs_file_random_fail_end(dest, ret, FS_OP_COPY); } static int fs_randomfail_rename(struct fs_file *_src, struct fs_file *_dest) { struct randomfail_fs_file *dest = (struct randomfail_fs_file *)_dest; int ret; if (fs_file_random_fail_begin(dest, FS_OP_RENAME)) return -1; ret = fs_rename(_src->parent, _dest->parent); return fs_file_random_fail_end(dest, ret, FS_OP_RENAME); } static int fs_randomfail_delete(struct fs_file *_file) { struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file; int ret; if (fs_file_random_fail_begin(file, FS_OP_DELETE)) return -1; ret = fs_delete(_file->parent); return fs_file_random_fail_end(file, ret, FS_OP_DELETE); } static struct fs_iter * fs_randomfail_iter_init(struct fs *_fs, const char *path, enum fs_iter_flags flags) { struct randomfail_fs_iter *iter; uoff_t pos; iter = i_new(struct randomfail_fs_iter, 1); iter->iter.fs = _fs; iter->iter.flags = flags; iter->super = fs_iter_init(_fs->parent, path, flags); if (fs_random_fail_range(_fs, FS_OP_ITER, &pos)) iter->fail_pos = pos + 1; return &iter->iter; } static const char *fs_randomfail_iter_next(struct fs_iter *_iter) { struct randomfail_fs_iter *iter = (struct randomfail_fs_iter *)_iter; const char *fname; if (iter->fail_pos > 0) { if (iter->fail_pos == 1) return NULL; iter->fail_pos--; } iter->super->async_callback = _iter->async_callback; iter->super->async_context = _iter->async_context; fname = fs_iter_next(iter->super); _iter->async_have_more = iter->super->async_have_more; return fname; } static int fs_randomfail_iter_deinit(struct fs_iter *_iter) { struct randomfail_fs_iter *iter = (struct randomfail_fs_iter *)_iter; int ret; ret = fs_iter_deinit(&iter->super); if (iter->fail_pos == 1) { fs_set_error(_iter->fs, RANDOMFAIL_ERROR); errno = EIO; ret = -1; } i_free(iter); return ret; } const struct fs fs_class_randomfail = { .name = "randomfail", .v = { fs_randomfail_alloc, fs_randomfail_init, fs_randomfail_deinit, fs_randomfail_get_properties, fs_randomfail_file_init, fs_randomfail_file_deinit, fs_wrapper_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_randomfail_get_metadata, fs_randomfail_prefetch, fs_randomfail_read, fs_randomfail_read_stream, fs_randomfail_write, fs_randomfail_write_stream, fs_randomfail_write_stream_finish, fs_randomfail_lock, fs_randomfail_unlock, fs_randomfail_exists, fs_randomfail_stat, fs_randomfail_copy, fs_randomfail_rename, fs_randomfail_delete, fs_randomfail_iter_init, fs_randomfail_iter_next, fs_randomfail_iter_deinit, NULL, fs_randomfail_get_nlinks, } }; dovecot-2.2.33.2/src/lib-fs/fs-test.c0000644000175000017500000002173113165463624014037 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "ostream.h" #include "test-common.h" #include "fs-test.h" static struct fs *fs_test_alloc(void) { struct test_fs *fs; fs = i_new(struct test_fs, 1); fs->fs = fs_class_test; i_array_init(&fs->iter_files, 32); return &fs->fs; } static int fs_test_init(struct fs *_fs ATTR_UNUSED, const char *args ATTR_UNUSED, const struct fs_settings *set ATTR_UNUSED) { return 0; } static void fs_test_deinit(struct fs *_fs) { struct test_fs *fs = (struct test_fs *)_fs; array_free(&fs->iter_files); i_free(fs); } static enum fs_properties fs_test_get_properties(struct fs *_fs) { struct test_fs *fs = (struct test_fs *)_fs; return fs->properties; } static struct fs_file * fs_test_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct test_fs_file *file; file = i_new(struct test_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->file.flags = flags; file->mode = mode; file->contents = buffer_create_dynamic(default_pool, 1024); file->exists = TRUE; file->seekable = TRUE; file->wait_async = (flags & FS_OPEN_FLAG_ASYNC) != 0; return &file->file; } static void fs_test_file_deinit(struct fs_file *_file) { struct test_fs_file *file = (struct test_fs_file *)_file; buffer_free(&file->contents); i_free(file->file.path); i_free(file); } static void fs_test_file_close(struct fs_file *_file) { struct test_fs_file *file = (struct test_fs_file *)_file; file->closed = TRUE; } static const char *fs_test_file_get_path(struct fs_file *_file) { return _file->path; } static void fs_test_set_async_callback(struct fs_file *_file, fs_file_async_callback_t *callback, void *context) { struct test_fs_file *file = (struct test_fs_file *)_file; file->async_callback = callback; file->async_context = context; } static int fs_test_wait_async(struct fs *_fs ATTR_UNUSED) { return 0; } static void fs_test_set_metadata(struct fs_file *_file, const char *key, const char *value) { fs_default_set_metadata(_file, key, value); } static int fs_test_get_metadata(struct fs_file *_file, const ARRAY_TYPE(fs_metadata) **metadata_r) { struct test_fs_file *file = (struct test_fs_file *)_file; if (file->wait_async) { fs_set_error_async(_file->fs); return -1; } if (file->io_failure) { errno = EIO; return -1; } fs_metadata_init(_file); *metadata_r = &_file->metadata; return 0; } static bool fs_test_prefetch(struct fs_file *_file ATTR_UNUSED, uoff_t length ATTR_UNUSED) { struct test_fs_file *file = (struct test_fs_file *)_file; file->prefetched = TRUE; return TRUE; } static void fs_test_stream_destroyed(struct test_fs_file *file) { i_assert(file->input != NULL); file->input = NULL; } static struct istream * fs_test_read_stream(struct fs_file *_file, size_t max_buffer_size ATTR_UNUSED) { struct test_fs_file *file = (struct test_fs_file *)_file; struct istream *input; i_assert(file->input == NULL); if (!file->exists) return i_stream_create_error(ENOENT); if (file->io_failure) return i_stream_create_error(EIO); input = test_istream_create_data(file->contents->data, file->contents->used); i_stream_add_destroy_callback(input, fs_test_stream_destroyed, file); if (!file->seekable) input->seekable = FALSE; file->input = input; return input; } static void fs_test_write_stream(struct fs_file *_file) { struct test_fs_file *file = (struct test_fs_file *)_file; i_assert(_file->output == NULL); buffer_set_used_size(file->contents, 0); _file->output = o_stream_create_buffer(file->contents); } static int fs_test_write_stream_finish(struct fs_file *_file, bool success) { struct test_fs_file *file = (struct test_fs_file *)_file; if (_file->output != NULL) o_stream_destroy(&_file->output); if (file->wait_async) { fs_set_error_async(_file->fs); return 0; } if (file->io_failure) success = FALSE; if (!success) buffer_set_used_size(file->contents, 0); return success ? 1 : -1; } static int fs_test_lock(struct fs_file *_file, unsigned int secs ATTR_UNUSED, struct fs_lock **lock_r) { struct test_fs_file *file = (struct test_fs_file *)_file; if (file->locked) return 0; file->locked = TRUE; *lock_r = i_new(struct fs_lock, 1); (*lock_r)->file = _file; return 1; } static void fs_test_unlock(struct fs_lock *lock) { struct test_fs_file *file = (struct test_fs_file *)lock->file; file->locked = FALSE; i_free(lock); } static int fs_test_exists(struct fs_file *_file) { struct test_fs_file *file = (struct test_fs_file *)_file; if (file->wait_async) { fs_set_error_async(_file->fs); return -1; } if (file->io_failure) { errno = EIO; return -1; } return file->exists ? 1 : 0; } static int fs_test_stat(struct fs_file *_file, struct stat *st_r) { struct test_fs_file *file = (struct test_fs_file *)_file; if (file->wait_async) { fs_set_error_async(_file->fs); return -1; } if (file->io_failure) { errno = EIO; return -1; } if (!file->exists) { errno = ENOENT; return -1; } i_zero(st_r); st_r->st_size = file->contents->used; return 0; } static int fs_test_copy(struct fs_file *_src, struct fs_file *_dest) { struct test_fs_file *src; struct test_fs_file *dest = (struct test_fs_file *)_dest; if (_src != NULL) dest->copy_src = test_fs_file_get(_src->fs, fs_file_path(_src)); src = dest->copy_src; if (dest->wait_async) { fs_set_error_async(_dest->fs); return -1; } dest->copy_src = NULL; if (dest->io_failure) { errno = EIO; return -1; } if (!src->exists) { errno = ENOENT; return -1; } buffer_set_used_size(dest->contents, 0); buffer_append_buf(dest->contents, src->contents, 0, (size_t)-1); dest->exists = TRUE; return 0; } static int fs_test_rename(struct fs_file *_src, struct fs_file *_dest) { struct test_fs_file *src = (struct test_fs_file *)_src; struct test_fs_file *dest = (struct test_fs_file *)_dest; if (src->wait_async || dest->wait_async) { fs_set_error_async(_dest->fs); return -1; } if (fs_test_copy(_src, _dest) < 0) return -1; src->exists = FALSE; return 0; } static int fs_test_delete(struct fs_file *_file) { struct test_fs_file *file = (struct test_fs_file *)_file; if (file->wait_async) { fs_set_error_async(_file->fs); return -1; } if (!file->exists) { errno = ENOENT; return -1; } return 0; } static struct fs_iter * fs_test_iter_init(struct fs *_fs, const char *path, enum fs_iter_flags flags) { struct test_fs *fs = (struct test_fs *)_fs; struct test_fs_iter *iter; iter = i_new(struct test_fs_iter, 1); iter->iter.fs = _fs; iter->iter.flags = flags; iter->prefix = i_strdup(path); iter->prefix_len = strlen(iter->prefix); iter->prev_dir = i_strdup(""); array_sort(&fs->iter_files, i_strcmp_p); return &iter->iter; } static const char *fs_test_iter_next(struct fs_iter *_iter) { struct test_fs_iter *iter = (struct test_fs_iter *)_iter; struct test_fs *fs = (struct test_fs *)_iter->fs; const char *const *files, *p; unsigned int count; size_t len, prev_dir_len = strlen(iter->prev_dir); files = array_get(&fs->iter_files, &count); for (; iter->idx < count; iter->idx++) { const char *fname = files[iter->idx]; if (strncmp(fname, iter->prefix, iter->prefix_len) != 0) continue; p = strrchr(fname, '/'); if ((_iter->flags & FS_ITER_FLAG_DIRS) == 0) { if (p == NULL) return fname; if (p[1] == '\0') continue; /* dir/ */ return p+1; } if (p == NULL) continue; len = p - fname; if (len == 0) continue; if (len == prev_dir_len && strncmp(fname, iter->prev_dir, len) == 0) continue; i_free(iter->prev_dir); iter->prev_dir = i_strndup(fname, len); return iter->prev_dir; } return NULL; } static int fs_test_iter_deinit(struct fs_iter *_iter) { struct test_fs_iter *iter = (struct test_fs_iter *)_iter; int ret = iter->failed ? -1 : 0; i_free(iter->prefix); i_free(iter); return ret; } struct test_fs *test_fs_get(struct fs *fs) { while (strcmp(fs->name, "test") != 0) { i_assert(fs->parent != NULL); fs = fs->parent; } return (struct test_fs *)fs; } struct test_fs_file *test_fs_file_get(struct fs *fs, const char *path) { struct fs_file *file; fs = &test_fs_get(fs)->fs; for (file = fs->files;; file = file->next) { i_assert(file != NULL); if (strcmp(fs_file_path(file), path) == 0) break; } return (struct test_fs_file *)file; } const struct fs fs_class_test = { .name = "test", .v = { fs_test_alloc, fs_test_init, fs_test_deinit, fs_test_get_properties, fs_test_file_init, fs_test_file_deinit, fs_test_file_close, fs_test_file_get_path, fs_test_set_async_callback, fs_test_wait_async, fs_test_set_metadata, fs_test_get_metadata, fs_test_prefetch, NULL, fs_test_read_stream, NULL, fs_test_write_stream, fs_test_write_stream_finish, fs_test_lock, fs_test_unlock, fs_test_exists, fs_test_stat, fs_test_copy, fs_test_rename, fs_test_delete, fs_test_iter_init, fs_test_iter_next, fs_test_iter_deinit, NULL, NULL, } }; dovecot-2.2.33.2/src/lib-fs/fs-metawrap.c0000644000175000017500000003376513165463624014712 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "istream.h" #include "istream-private.h" #include "istream-concat.h" #include "istream-metawrap.h" #include "ostream.h" #include "ostream-metawrap.h" #include "iostream-temp.h" #include "fs-api-private.h" struct metawrap_fs { struct fs fs; bool wrap_metadata; }; struct metawrap_fs_file { struct fs_file file; struct metawrap_fs *fs; struct fs_file *super_read; enum fs_open_mode open_mode; struct istream *input; bool metadata_read; struct ostream *super_output; struct ostream *temp_output; string_t *metadata_header; uoff_t metadata_write_size; bool metadata_changed_since_write; }; static struct fs *fs_metawrap_alloc(void) { struct metawrap_fs *fs; fs = i_new(struct metawrap_fs, 1); fs->fs = fs_class_metawrap; return &fs->fs; } static int fs_metawrap_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct metawrap_fs *fs = (struct metawrap_fs *)_fs; const char *parent_name, *parent_args, *error; if (*args == '\0') { fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } parent_args = strchr(args, ':'); if (parent_args == NULL) { parent_name = args; parent_args = ""; } else { parent_name = t_strdup_until(args, parent_args); parent_args++; } if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s", error); return -1; } if ((fs_get_properties(_fs->parent) & FS_PROPERTY_METADATA) == 0) fs->wrap_metadata = TRUE; return 0; } static void fs_metawrap_deinit(struct fs *_fs) { struct metawrap_fs *fs = (struct metawrap_fs *)_fs; if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs); } static enum fs_properties fs_metawrap_get_properties(struct fs *_fs) { const struct metawrap_fs *fs = (const struct metawrap_fs *)_fs; enum fs_properties props; props = fs_get_properties(_fs->parent); if (fs->wrap_metadata) { /* we don't have a quick stat() to see the file's size, because of the metadata header */ props &= ~FS_PROPERTY_STAT; /* Copying can copy the whole metadata. */ props |= FS_PROPERTY_COPY_METADATA; } return props; } static struct fs_file * fs_metawrap_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct metawrap_fs *fs = (struct metawrap_fs *)_fs; struct metawrap_fs_file *file; file = i_new(struct metawrap_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->fs = fs; file->open_mode = mode; /* avoid unnecessarily creating two seekable streams */ flags &= ~FS_OPEN_FLAG_SEEKABLE; file->file.parent = fs_file_init(_fs->parent, path, mode | flags); if (file->fs->wrap_metadata && mode == FS_OPEN_MODE_READONLY && (flags & FS_OPEN_FLAG_ASYNC) == 0) { /* use async stream for parent, so fs_read_stream() won't create another seekable stream unneededly */ file->super_read = fs_file_init(_fs->parent, path, mode | flags | FS_OPEN_FLAG_ASYNC); } else { file->super_read = file->file.parent; } fs_metadata_init(&file->file); return &file->file; } static void fs_metawrap_file_deinit(struct fs_file *_file) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (file->super_read != _file->parent && file->super_read != NULL) fs_file_deinit(&file->super_read); if (file->metadata_header != NULL) str_free(&file->metadata_header); fs_file_deinit(&_file->parent); i_free(file->file.path); i_free(file); } static void fs_metawrap_file_close(struct fs_file *_file) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (file->input != NULL) i_stream_unref(&file->input); if (file->super_read != NULL) fs_file_close(file->super_read); if (_file->parent != NULL) fs_file_close(_file->parent); } static void fs_metawrap_set_metadata(struct fs_file *_file, const char *key, const char *value) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (!file->fs->wrap_metadata) fs_set_metadata(_file->parent, key, value); else { fs_default_set_metadata(_file, key, value); file->metadata_changed_since_write = TRUE; } } static int fs_metawrap_get_metadata(struct fs_file *_file, const ARRAY_TYPE(fs_metadata) **metadata_r) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; ssize_t ret; char c; if (!file->fs->wrap_metadata) return fs_get_metadata(_file->parent, metadata_r); if (file->metadata_read) { /* we have the metadata */ } else if (file->input == NULL) { if (fs_read(_file, &c, 1) < 0) return -1; } else { /* use the existing istream to read it */ while ((ret = i_stream_read(file->input)) == 0) { if (file->metadata_read) break; i_assert(!file->input->blocking); if (fs_wait_async(_file->fs) < 0) return -1; } if (ret == -1 && file->input->stream_errno != 0) { fs_set_error(_file->fs, "read(%s) failed: %s", i_stream_get_name(file->input), i_stream_get_error(file->input)); return -1; } i_assert(file->metadata_read); } *metadata_r = &_file->metadata; return 0; } static bool fs_metawrap_prefetch(struct fs_file *_file, uoff_t length) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (!file->fs->wrap_metadata) return fs_prefetch(_file->parent, length); else return fs_prefetch(file->super_read, length); } static ssize_t fs_metawrap_read(struct fs_file *_file, void *buf, size_t size) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (!file->fs->wrap_metadata) return fs_read(_file->parent, buf, size); return fs_read_via_stream(_file, buf, size); } static void fs_metawrap_callback(const char *key, const char *value, void *context) { struct metawrap_fs_file *file = context; if (key == NULL) { file->metadata_read = TRUE; return; } T_BEGIN { key = str_tabunescape(t_strdup_noconst(key)); value = str_tabunescape(t_strdup_noconst(value)); fs_default_set_metadata(&file->file, key, value); } T_END; } static struct istream * fs_metawrap_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; struct istream *input; if (!file->fs->wrap_metadata) return fs_read_stream(_file->parent, max_buffer_size); if (file->input != NULL) { i_stream_ref(file->input); i_stream_seek(file->input, 0); return file->input; } input = fs_read_stream(file->super_read, max_buffer_size); file->input = i_stream_create_metawrap(input, fs_metawrap_callback, file); i_stream_unref(&input); i_stream_ref(file->input); return file->input; } static int fs_metawrap_write(struct fs_file *_file, const void *data, size_t size) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; if (!file->fs->wrap_metadata) return fs_write(_file->parent, data, size); return fs_write_via_stream(_file, data, size); } static void fs_metawrap_append_metadata(struct metawrap_fs_file *file, string_t *str) { const struct fs_metadata *metadata; array_foreach(&file->file.metadata, metadata) { if (strncmp(metadata->key, FS_METADATA_INTERNAL_PREFIX, strlen(FS_METADATA_INTERNAL_PREFIX)) == 0) continue; str_append_tabescaped(str, metadata->key); str_append_c(str, ':'); str_append_tabescaped(str, metadata->value); str_append_c(str, '\n'); } str_append_c(str, '\n'); } static void fs_metawrap_write_metadata_to(struct metawrap_fs_file *file, struct ostream *output) { string_t *str = t_str_new(256); ssize_t ret; fs_metawrap_append_metadata(file, str); file->metadata_write_size = str_len(str); ret = o_stream_send(output, str_data(str), str_len(str)); if (ret < 0) o_stream_close(output); else i_assert((size_t)ret == str_len(str)); file->metadata_changed_since_write = FALSE; } static void fs_metawrap_write_metadata(void *context) { struct metawrap_fs_file *file = context; fs_metawrap_write_metadata_to(file, file->file.output); } static void fs_metawrap_write_stream(struct fs_file *_file) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; i_assert(_file->output == NULL); if (!file->fs->wrap_metadata) { file->super_output = fs_write_stream(_file->parent); _file->output = file->super_output; } else { file->temp_output = iostream_temp_create_named(_file->fs->temp_path_prefix, IOSTREAM_TEMP_FLAG_TRY_FD_DUP, fs_file_path(_file)); _file->output = o_stream_create_metawrap(file->temp_output, fs_metawrap_write_metadata, file); } } static struct istream * fs_metawrap_create_updated_istream(struct metawrap_fs_file *file, struct istream *input) { struct istream *input2, *inputs[3]; if (file->metadata_header != NULL) str_truncate(file->metadata_header, 0); else file->metadata_header = str_new(default_pool, 1024); fs_metawrap_append_metadata(file, file->metadata_header); inputs[0] = i_stream_create_from_data(str_data(file->metadata_header), str_len(file->metadata_header)); i_stream_seek(input, file->metadata_write_size); inputs[1] = i_stream_create_limit(input, (uoff_t)-1); inputs[2] = NULL; input2 = i_stream_create_concat(inputs); i_stream_unref(&inputs[0]); i_stream_unref(&inputs[1]); file->metadata_write_size = str_len(file->metadata_header); return input2; } static int fs_metawrap_write_stream_finish(struct fs_file *_file, bool success) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; struct istream *input; int ret; if (_file->output != NULL) { if (_file->output == file->super_output) _file->output = NULL; else o_stream_unref(&_file->output); } if (!success) { if (file->super_output != NULL) { /* no metawrap */ i_assert(file->temp_output == NULL); fs_write_stream_abort_parent(_file, &file->super_output); } else { i_assert(file->temp_output != NULL); o_stream_destroy(&file->temp_output); } return -1; } if (file->super_output != NULL) { /* no metawrap */ i_assert(file->temp_output == NULL); return fs_write_stream_finish(_file->parent, &file->super_output); } if (file->temp_output == NULL) { /* finishing up */ i_assert(file->super_output == NULL); return fs_write_stream_finish_async(_file->parent); } /* finish writing the temporary file */ if (file->temp_output->offset == 0) { /* empty file */ fs_metawrap_write_metadata_to(file, file->temp_output); } input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE); if (file->metadata_changed_since_write) { /* we'll need to recreate the metadata. do this by creating a new istream combining the new metadata header and the old body. */ struct istream *input2 = input; input = fs_metawrap_create_updated_istream(file, input); i_stream_unref(&input2); } file->super_output = fs_write_stream(_file->parent); (void)o_stream_send_istream(file->super_output, input); if (input->stream_errno != 0) { fs_write_stream_abort_error(_file->parent, &file->super_output, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); ret = -1; } else if (file->super_output->stream_errno != 0) { fs_write_stream_abort_error(_file->parent, &file->super_output, "write(%s) failed: %s", o_stream_get_name(file->super_output), o_stream_get_error(file->super_output)); ret = -1; } else { i_assert(i_stream_is_eof(input)); /* because of the "end of metadata" LF, there's always at least 1 byte */ i_assert(file->super_output->offset > 0); ret = fs_write_stream_finish(_file->parent, &file->super_output); } i_stream_unref(&input); return ret; } static int fs_metawrap_stat(struct fs_file *_file, struct stat *st_r) { struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file; struct istream *input; uoff_t input_size; ssize_t ret; if (!file->fs->wrap_metadata) return fs_stat(_file->parent, st_r); if (file->metadata_write_size != 0) { /* fs_stat() after a write. we can do this quickly. */ if (fs_stat(_file->parent, st_r) < 0) return -1; if ((uoff_t)st_r->st_size < file->metadata_write_size) { fs_set_error(_file->fs, "Just-written %s shrank unexpectedly " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", fs_file_path(_file), st_r->st_size, file->metadata_write_size); return -1; } st_r->st_size -= file->metadata_write_size; return 0; } if (file->input == NULL) input = fs_read_stream(_file, IO_BLOCK_SIZE); else { input = file->input; i_stream_ref(input); } if ((ret = i_stream_get_size(input, TRUE, &input_size)) < 0) { fs_set_error(_file->fs, "i_stream_get_size(%s) failed: %s", fs_file_path(_file), i_stream_get_error(input)); i_stream_unref(&input); return -1; } i_stream_unref(&input); if (ret == 0) { /* we shouldn't get here */ fs_set_error(_file->fs, "i_stream_get_size(%s) returned size as unknown", fs_file_path(_file)); errno = EIO; return -1; } if (fs_stat(_file->parent, st_r) < 0) { i_assert(errno != EAGAIN); /* read should have caught this */ return -1; } st_r->st_size = input_size; return 0; } static int fs_metawrap_copy(struct fs_file *_src, struct fs_file *_dest) { struct metawrap_fs_file *dest = (struct metawrap_fs_file *)_dest; if (!dest->fs->wrap_metadata || !_dest->metadata_changed) return fs_wrapper_copy(_src, _dest); else return fs_default_copy(_src, _dest); } const struct fs fs_class_metawrap = { .name = "metawrap", .v = { fs_metawrap_alloc, fs_metawrap_init, fs_metawrap_deinit, fs_metawrap_get_properties, fs_metawrap_file_init, fs_metawrap_file_deinit, fs_metawrap_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_metawrap_set_metadata, fs_metawrap_get_metadata, fs_metawrap_prefetch, fs_metawrap_read, fs_metawrap_read_stream, fs_metawrap_write, fs_metawrap_write_stream, fs_metawrap_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_metawrap_stat, fs_metawrap_copy, fs_wrapper_rename, fs_wrapper_delete, fs_wrapper_iter_init, fs_wrapper_iter_next, fs_wrapper_iter_deinit, NULL, fs_wrapper_get_nlinks, } }; dovecot-2.2.33.2/src/lib-fs/fs-sis-common.h0000644000175000017500000000046113123174404015133 00000000000000#ifndef FS_SIS_COMMON_H #define FS_SIS_COMMON_H #include "fs-api-private.h" #define HASH_DIR_NAME "hashes" int fs_sis_path_parse(struct fs *fs, const char *path, const char **dir_r, const char **hash_r); void fs_sis_try_unlink_hash_file(struct fs *sis_fs, struct fs_file *super_file); #endif dovecot-2.2.33.2/src/lib-fs/fs-wrapper.h0000644000175000017500000000325213165463624014543 00000000000000#ifndef FS_WRAPPER_H #define FS_WRAPPER_H enum fs_properties fs_wrapper_get_properties(struct fs *fs); void fs_wrapper_file_close(struct fs_file *file); const char *fs_wrapper_file_get_path(struct fs_file *file); void fs_wrapper_set_async_callback(struct fs_file *file, fs_file_async_callback_t *callback, void *context); int fs_wrapper_wait_async(struct fs *fs); void fs_wrapper_set_metadata(struct fs_file *file, const char *key, const char *value); int fs_wrapper_get_metadata(struct fs_file *file, const ARRAY_TYPE(fs_metadata) **metadata_r); bool fs_wrapper_prefetch(struct fs_file *file, uoff_t length); ssize_t fs_wrapper_read(struct fs_file *file, void *buf, size_t size); struct istream * fs_wrapper_read_stream(struct fs_file *file, size_t max_buffer_size); int fs_wrapper_write(struct fs_file *file, const void *data, size_t size); void fs_wrapper_write_stream(struct fs_file *file); int fs_wrapper_write_stream_finish(struct fs_file *file, bool success); int fs_wrapper_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r); void fs_wrapper_unlock(struct fs_lock *_lock); int fs_wrapper_exists(struct fs_file *file); int fs_wrapper_stat(struct fs_file *file, struct stat *st_r); int fs_wrapper_get_nlinks(struct fs_file *file, nlink_t *nlinks_r); int fs_wrapper_copy(struct fs_file *src, struct fs_file *dest); int fs_wrapper_rename(struct fs_file *src, struct fs_file *dest); int fs_wrapper_delete(struct fs_file *file); struct fs_iter * fs_wrapper_iter_init(struct fs *fs, const char *path, enum fs_iter_flags flags); const char *fs_wrapper_iter_next(struct fs_iter *iter); int fs_wrapper_iter_deinit(struct fs_iter *iter); #endif dovecot-2.2.33.2/src/lib-fs/istream-metawrap.h0000644000175000017500000000075213123174404015726 00000000000000#ifndef ISTREAM_METAWRAP_H #define ISTREAM_METAWRAP_H typedef void metawrap_callback_t(const char *key, const char *value, void *context); /* Input stream is in format "key:value\nkey2:value2\n...\n\ncontents. The given callback is called for each key/value metadata pair, and the returned stream will skip over the metadata and return only the contents. */ struct istream * i_stream_create_metawrap(struct istream *input, metawrap_callback_t *callback, void *context); #endif dovecot-2.2.33.2/src/lib-fs/fs-dict.c0000644000175000017500000002020213165463624013773 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "guid.h" #include "hex-binary.h" #include "base64.h" #include "istream.h" #include "ostream.h" #include "dict.h" #include "fs-api-private.h" enum fs_dict_value_encoding { FS_DICT_VALUE_ENCODING_RAW, FS_DICT_VALUE_ENCODING_HEX, FS_DICT_VALUE_ENCODING_BASE64 }; struct dict_fs { struct fs fs; struct dict *dict; char *path_prefix; enum fs_dict_value_encoding encoding; }; struct dict_fs_file { struct fs_file file; pool_t pool; const char *key, *value; buffer_t *write_buffer; }; struct dict_fs_iter { struct fs_iter iter; struct dict_iterate_context *dict_iter; }; static struct fs *fs_dict_alloc(void) { struct dict_fs *fs; fs = i_new(struct dict_fs, 1); fs->fs = fs_class_dict; return &fs->fs; } static int fs_dict_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct dict_fs *fs = (struct dict_fs *)_fs; struct dict_settings dict_set; const char *p, *encoding_str, *error; p = strchr(args, ':'); if (p == NULL) { fs_set_error(_fs, "':' missing in args"); return -1; } encoding_str = t_strdup_until(args, p++); if (strcmp(encoding_str, "raw") == 0) fs->encoding = FS_DICT_VALUE_ENCODING_RAW; else if (strcmp(encoding_str, "hex") == 0) fs->encoding = FS_DICT_VALUE_ENCODING_HEX; else if (strcmp(encoding_str, "base64") == 0) fs->encoding = FS_DICT_VALUE_ENCODING_BASE64; else { fs_set_error(_fs, "Unknown value encoding '%s'", encoding_str); return -1; } i_zero(&dict_set); dict_set.username = set->username; dict_set.base_dir = set->base_dir; if (dict_init_full(p, &dict_set, &fs->dict, &error) < 0) { fs_set_error(_fs, "dict_init(%s) failed: %s", args, error); return -1; } return 0; } static void fs_dict_deinit(struct fs *_fs) { struct dict_fs *fs = (struct dict_fs *)_fs; dict_deinit(&fs->dict); i_free(fs); } static enum fs_properties fs_dict_get_properties(struct fs *fs ATTR_UNUSED) { return FS_PROPERTY_ITER | FS_PROPERTY_RELIABLEITER; } static struct fs_file * fs_dict_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags ATTR_UNUSED) { struct dict_fs *fs = (struct dict_fs *)_fs; struct dict_fs_file *file; guid_128_t guid; pool_t pool; i_assert(mode != FS_OPEN_MODE_APPEND); /* not supported */ i_assert(mode != FS_OPEN_MODE_CREATE); /* not supported */ pool = pool_alloconly_create("fs dict file", 128); file = p_new(pool, struct dict_fs_file, 1); file->pool = pool; file->file.fs = _fs; if (mode != FS_OPEN_MODE_CREATE_UNIQUE_128) file->file.path = p_strdup(pool, path); else { guid_128_generate(guid); file->file.path = p_strdup_printf(pool, "%s/%s", path, guid_128_to_string(guid)); } file->key = fs->path_prefix == NULL ? p_strdup(pool, file->file.path) : p_strconcat(pool, fs->path_prefix, file->file.path, NULL); return &file->file; } static void fs_dict_file_deinit(struct fs_file *_file) { struct dict_fs_file *file = (struct dict_fs_file *)_file; i_assert(_file->output == NULL); pool_unref(&file->pool); } static bool fs_dict_prefetch(struct fs_file *_file ATTR_UNUSED, uoff_t length ATTR_UNUSED) { /* once async dict_lookup() is implemented, we want to start it here */ return TRUE; } static int fs_dict_lookup(struct dict_fs_file *file) { struct dict_fs *fs = (struct dict_fs *)file->file.fs; int ret; if (file->value != NULL) return 0; ret = dict_lookup(fs->dict, file->pool, file->key, &file->value); if (ret > 0) return 0; else if (ret < 0) { errno = EIO; fs_set_error(&fs->fs, "Dict lookup failed"); return -1; } else { errno = ENOENT; fs_set_error(&fs->fs, "Dict key doesn't exist"); return -1; } } static struct istream * fs_dict_read_stream(struct fs_file *_file, size_t max_buffer_size ATTR_UNUSED) { struct dict_fs_file *file = (struct dict_fs_file *)_file; struct istream *input; if (fs_dict_lookup(file) < 0) input = i_stream_create_error_str(errno, "%s", fs_last_error(_file->fs)); else input = i_stream_create_from_data(file->value, strlen(file->value)); i_stream_set_name(input, file->key); return input; } static void fs_dict_write_stream(struct fs_file *_file) { struct dict_fs_file *file = (struct dict_fs_file *)_file; i_assert(_file->output == NULL); file->write_buffer = buffer_create_dynamic(file->pool, 128); _file->output = o_stream_create_buffer(file->write_buffer); o_stream_set_name(_file->output, file->key); } static void fs_dict_write_rename_if_needed(struct dict_fs_file *file) { struct dict_fs *fs = (struct dict_fs *)file->file.fs; const char *new_fname; new_fname = fs_metadata_find(&file->file.metadata, FS_METADATA_WRITE_FNAME); if (new_fname == NULL) return; file->file.path = p_strdup(file->pool, new_fname); file->key = fs->path_prefix == NULL ? p_strdup(file->pool, new_fname) : p_strconcat(file->pool, fs->path_prefix, new_fname, NULL); } static int fs_dict_write_stream_finish(struct fs_file *_file, bool success) { struct dict_fs_file *file = (struct dict_fs_file *)_file; struct dict_fs *fs = (struct dict_fs *)_file->fs; struct dict_transaction_context *trans; o_stream_destroy(&_file->output); if (!success) return -1; fs_dict_write_rename_if_needed(file); trans = dict_transaction_begin(fs->dict); switch (fs->encoding) { case FS_DICT_VALUE_ENCODING_RAW: dict_set(trans, file->key, str_c(file->write_buffer)); break; case FS_DICT_VALUE_ENCODING_HEX: { string_t *hex = t_str_new(file->write_buffer->used * 2 + 1); binary_to_hex_append(hex, file->write_buffer->data, file->write_buffer->used); dict_set(trans, file->key, str_c(hex)); break; } case FS_DICT_VALUE_ENCODING_BASE64: { const size_t base64_size = MAX_BASE64_ENCODED_SIZE(file->write_buffer->used); string_t *base64 = t_str_new(base64_size); base64_encode(file->write_buffer->data, file->write_buffer->used, base64); dict_set(trans, file->key, str_c(base64)); } } if (dict_transaction_commit(&trans) < 0) { errno = EIO; fs_set_error(_file->fs, "Dict transaction commit failed"); return -1; } return 1; } static int fs_dict_stat(struct fs_file *_file, struct stat *st_r) { struct dict_fs_file *file = (struct dict_fs_file *)_file; i_zero(st_r); if (fs_dict_lookup(file) < 0) return -1; st_r->st_size = strlen(file->value); return 0; } static int fs_dict_delete(struct fs_file *_file) { struct dict_fs_file *file = (struct dict_fs_file *)_file; struct dict_fs *fs = (struct dict_fs *)_file->fs; struct dict_transaction_context *trans; trans = dict_transaction_begin(fs->dict); dict_unset(trans, file->key); if (dict_transaction_commit(&trans) < 0) { errno = EIO; fs_set_error(_file->fs, "Dict transaction commit failed"); return -1; } return 0; } static struct fs_iter * fs_dict_iter_init(struct fs *_fs, const char *path, enum fs_iter_flags flags) { struct dict_fs *fs = (struct dict_fs *)_fs; struct dict_fs_iter *iter; iter = i_new(struct dict_fs_iter, 1); iter->iter.fs = _fs; iter->iter.flags = flags; if (fs->path_prefix != NULL) path = t_strconcat(fs->path_prefix, path, NULL); iter->dict_iter = dict_iterate_init(fs->dict, path, 0); return &iter->iter; } static const char *fs_dict_iter_next(struct fs_iter *_iter) { struct dict_fs_iter *iter = (struct dict_fs_iter *)_iter; const char *key, *value; if (!dict_iterate(iter->dict_iter, &key, &value)) return NULL; return key; } static int fs_dict_iter_deinit(struct fs_iter *_iter) { struct dict_fs_iter *iter = (struct dict_fs_iter *)_iter; int ret; ret = dict_iterate_deinit(&iter->dict_iter); if (ret < 0) fs_set_error(_iter->fs, "Dict iteration failed"); i_free(iter); return ret; } const struct fs fs_class_dict = { .name = "dict", .v = { fs_dict_alloc, fs_dict_init, fs_dict_deinit, fs_dict_get_properties, fs_dict_file_init, fs_dict_file_deinit, NULL, NULL, NULL, NULL, fs_default_set_metadata, NULL, fs_dict_prefetch, NULL, fs_dict_read_stream, NULL, fs_dict_write_stream, fs_dict_write_stream_finish, NULL, NULL, NULL, fs_dict_stat, fs_default_copy, NULL, fs_dict_delete, fs_dict_iter_init, fs_dict_iter_next, fs_dict_iter_deinit, NULL, NULL } }; dovecot-2.2.33.2/src/lib-fs/istream-fs-file.h0000644000175000017500000000063513123174404015433 00000000000000#ifndef ISTREAM_FS_FILE_H #define ISTREAM_FS_FILE_H struct fs_file; /* Open the given file only when something is actually tried to be read from the stream. The file is automatically deinitialized when the stream is destroyed (which is why it's also set to NULL so it's not accidentally double-freed). */ struct istream * i_stream_create_fs_file(struct fs_file **file, size_t max_buffer_size); #endif dovecot-2.2.33.2/src/lib-fs/fs-api.c0000644000175000017500000006657513165463624013650 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "module-dir.h" #include "llist.h" #include "str.h" #include "hash-method.h" #include "istream.h" #include "istream-seekable.h" #include "ostream.h" #include "timing.h" #include "time-util.h" #include "istream-fs-stats.h" #include "fs-api-private.h" struct fs_api_module_register fs_api_module_register = { 0 }; static struct module *fs_modules = NULL; static ARRAY(const struct fs *) fs_classes; static void fs_classes_init(void); static int fs_alloc(const struct fs *fs_class, const char *args, const struct fs_settings *set, struct fs **fs_r, const char **error_r) { struct fs *fs; int ret; fs = fs_class->v.alloc(); fs->refcount = 1; fs->last_error = str_new(default_pool, 64); fs->set.debug = set->debug; fs->set.enable_timing = set->enable_timing; i_array_init(&fs->module_contexts, 5); T_BEGIN { ret = fs_class->v.init(fs, args, set); } T_END; if (ret < 0) { /* a bit kludgy way to allow data stack frame usage in normal conditions but still be able to return error message from data stack. */ *error_r = t_strdup_printf("%s: %s", fs_class->name, fs_last_error(fs)); fs_unref(&fs); return -1; } fs->username = i_strdup(set->username); fs->session_id = i_strdup(set->session_id); *fs_r = fs; return 0; } void fs_class_register(const struct fs *fs_class) { if (!array_is_created(&fs_classes)) fs_classes_init(); array_append(&fs_classes, &fs_class, 1); } static void fs_classes_deinit(void) { array_free(&fs_classes); } static void fs_classes_init(void) { i_array_init(&fs_classes, 8); fs_class_register(&fs_class_dict); fs_class_register(&fs_class_posix); fs_class_register(&fs_class_randomfail); fs_class_register(&fs_class_metawrap); fs_class_register(&fs_class_sis); fs_class_register(&fs_class_sis_queue); fs_class_register(&fs_class_test); lib_atexit(fs_classes_deinit); } static const struct fs *fs_class_find(const char *driver) { const struct fs *const *classp; if (!array_is_created(&fs_classes)) fs_classes_init(); array_foreach(&fs_classes, classp) { if (strcmp((*classp)->name, driver) == 0) return *classp; } return NULL; } static void fs_class_deinit_modules(void) { module_dir_unload(&fs_modules); } static const char *fs_driver_module_name(const char *driver) { return t_str_replace(driver, '-', '_'); } static void fs_class_try_load_plugin(const char *driver) { const char *module_name = t_strdup_printf("fs_%s", fs_driver_module_name(driver)); struct module *module; struct module_dir_load_settings mod_set; const struct fs *fs_class; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.ignore_missing = TRUE; fs_modules = module_dir_load_missing(fs_modules, MODULE_DIR, module_name, &mod_set); module_dir_init(fs_modules); module = module_dir_find(fs_modules, module_name); fs_class = module == NULL ? NULL : module_get_symbol(module, t_strdup_printf( "fs_class_%s", fs_driver_module_name(driver))); if (fs_class != NULL) fs_class_register(fs_class); lib_atexit(fs_class_deinit_modules); } int fs_init(const char *driver, const char *args, const struct fs_settings *set, struct fs **fs_r, const char **error_r) { const struct fs *fs_class; const char *temp_file_prefix; fs_class = fs_class_find(driver); if (fs_class == NULL) { T_BEGIN { fs_class_try_load_plugin(driver); } T_END; fs_class = fs_class_find(driver); } if (fs_class == NULL) { *error_r = t_strdup_printf("Unknown fs driver: %s", driver); return -1; } if (fs_alloc(fs_class, args, set, fs_r, error_r) < 0) return -1; temp_file_prefix = set->temp_file_prefix != NULL ? set->temp_file_prefix : ".temp.dovecot"; (*fs_r)->temp_path_prefix = i_strconcat(set->temp_dir, "/", temp_file_prefix, NULL); return 0; } void fs_deinit(struct fs **fs) { fs_unref(fs); } void fs_ref(struct fs *fs) { i_assert(fs->refcount > 0); fs->refcount++; } void fs_unref(struct fs **_fs) { struct fs *fs = *_fs; string_t *last_error = fs->last_error; struct array module_contexts_arr = fs->module_contexts.arr; unsigned int i; i_assert(fs->refcount > 0); *_fs = NULL; if (--fs->refcount > 0) return; if (fs->files_open_count > 0) { i_panic("fs-%s: %u files still open (first = %s)", fs->name, fs->files_open_count, fs_file_path(fs->files)); } i_assert(fs->files == NULL); i_free(fs->username); i_free(fs->session_id); i_free(fs->temp_path_prefix); for (i = 0; i < FS_OP_COUNT; i++) { if (fs->stats.timings[i] != NULL) timing_deinit(&fs->stats.timings[i]); } T_BEGIN { fs->v.deinit(fs); } T_END; array_free_i(&module_contexts_arr); str_free(&last_error); } struct fs *fs_get_parent(struct fs *fs) { return fs->parent; } const char *fs_get_driver(struct fs *fs) { return fs->name; } const char *fs_get_root_driver(struct fs *fs) { while (fs->parent != NULL) fs = fs->parent; return fs->name; } struct fs_file *fs_file_init(struct fs *fs, const char *path, int mode_flags) { struct fs_file *file; i_assert(path != NULL); i_assert((mode_flags & FS_OPEN_FLAG_ASYNC_NOQUEUE) == 0 || (mode_flags & FS_OPEN_FLAG_ASYNC) != 0); T_BEGIN { file = fs->v.file_init(fs, path, mode_flags & FS_OPEN_MODE_MASK, mode_flags & ~FS_OPEN_MODE_MASK); } T_END; file->flags = mode_flags & ~FS_OPEN_MODE_MASK; fs->files_open_count++; DLLIST_PREPEND(&fs->files, file); fs_set_metadata(file, FS_METADATA_ORIG_PATH, path); return file; } void fs_file_deinit(struct fs_file **_file) { struct fs_file *file = *_file; pool_t metadata_pool = file->metadata_pool; i_assert(file->fs->files_open_count > 0); *_file = NULL; fs_file_close(file); DLLIST_REMOVE(&file->fs->files, file); file->fs->files_open_count--; T_BEGIN { file->fs->v.file_deinit(file); } T_END; if (metadata_pool != NULL) pool_unref(&metadata_pool); } void fs_file_close(struct fs_file *file) { i_assert(!file->writing_stream); i_assert(file->output == NULL); if (file->pending_read_input != NULL) i_stream_unref(&file->pending_read_input); if (file->seekable_input != NULL) i_stream_unref(&file->seekable_input); if (file->copy_input != NULL) { i_stream_unref(&file->copy_input); fs_write_stream_abort_error(file, &file->copy_output, "fs_file_close(%s)", o_stream_get_name(file->copy_output)); } i_free_and_null(file->write_digest); if (file->fs->v.file_close != NULL) T_BEGIN { file->fs->v.file_close(file); } T_END; } enum fs_properties fs_get_properties(struct fs *fs) { return fs->v.get_properties(fs); } void fs_metadata_init(struct fs_file *file) { if (file->metadata_pool == NULL) { i_assert(!array_is_created(&file->metadata)); file->metadata_pool = pool_alloconly_create("fs metadata", 1024); p_array_init(&file->metadata, file->metadata_pool, 8); } } void fs_metadata_init_or_clear(struct fs_file *file) { if (file->metadata_pool == NULL) fs_metadata_init(file); else T_BEGIN { const struct fs_metadata *md; ARRAY_TYPE(fs_metadata) internal_metadata; t_array_init(&internal_metadata, 4); array_foreach(&file->metadata, md) { if (strncmp(md->key, FS_METADATA_INTERNAL_PREFIX, strlen(FS_METADATA_INTERNAL_PREFIX)) == 0) array_append(&internal_metadata, md, 1); } array_clear(&file->metadata); array_append_array(&file->metadata, &internal_metadata); } T_END; } static struct fs_metadata * fs_metadata_find_md(const ARRAY_TYPE(fs_metadata) *metadata, const char *key) { struct fs_metadata *md; array_foreach_modifiable(metadata, md) { if (strcmp(md->key, key) == 0) return md; } return NULL; } void fs_default_set_metadata(struct fs_file *file, const char *key, const char *value) { struct fs_metadata *metadata; fs_metadata_init(file); metadata = fs_metadata_find_md(&file->metadata, key); if (metadata == NULL) { metadata = array_append_space(&file->metadata); metadata->key = p_strdup(file->metadata_pool, key); } metadata->value = p_strdup(file->metadata_pool, value); } const char *fs_metadata_find(const ARRAY_TYPE(fs_metadata) *metadata, const char *key) { const struct fs_metadata *md; if (!array_is_created(metadata)) return NULL; md = fs_metadata_find_md(metadata, key); return md == NULL ? NULL : md->value; } void fs_set_metadata(struct fs_file *file, const char *key, const char *value) { i_assert(key != NULL); i_assert(value != NULL); if (file->fs->v.set_metadata != NULL) T_BEGIN { file->fs->v.set_metadata(file, key, value); if (strncmp(key, FS_METADATA_INTERNAL_PREFIX, strlen(FS_METADATA_INTERNAL_PREFIX)) == 0) { /* internal metadata change, which isn't stored. */ } else { file->metadata_changed = TRUE; } } T_END; } static void fs_file_timing_start(struct fs_file *file, enum fs_op op) { if (!file->fs->set.enable_timing) return; if (file->timing_start[op].tv_sec == 0) { if (gettimeofday(&file->timing_start[op], NULL) < 0) i_fatal("gettimeofday() failed: %m"); } } static void fs_timing_end(struct timing **timing, const struct timeval *start_tv) { struct timeval now; long long diff; if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); diff = timeval_diff_usecs(&now, start_tv); if (diff > 0) { if (*timing == NULL) *timing = timing_init(); timing_add_usecs(*timing, diff); } } void fs_file_timing_end(struct fs_file *file, enum fs_op op) { if (!file->fs->set.enable_timing || file->timing_start[op].tv_sec == 0) return; fs_timing_end(&file->fs->stats.timings[op], &file->timing_start[op]); /* don't count this again */ file->timing_start[op].tv_sec = 0; } int fs_get_metadata(struct fs_file *file, const ARRAY_TYPE(fs_metadata) **metadata_r) { int ret; if (file->fs->v.get_metadata == NULL) { fs_set_error(file->fs, "Metadata not supported by backend"); return -1; } if (!file->read_or_prefetch_counted && !file->lookup_metadata_counted) { file->lookup_metadata_counted = TRUE; file->fs->stats.lookup_metadata_count++; fs_file_timing_start(file, FS_OP_METADATA); } T_BEGIN { ret = file->fs->v.get_metadata(file, metadata_r); } T_END; if (!(ret < 0 && errno == EAGAIN)) fs_file_timing_end(file, FS_OP_METADATA); return ret; } int fs_lookup_metadata(struct fs_file *file, const char *key, const char **value_r) { const ARRAY_TYPE(fs_metadata) *metadata; if (fs_get_metadata(file, &metadata) < 0) return -1; *value_r = fs_metadata_find(metadata, key); return *value_r != NULL ? 1 : 0; } const char *fs_file_path(struct fs_file *file) { return file->fs->v.get_path == NULL ? file->path : file->fs->v.get_path(file); } struct fs *fs_file_fs(struct fs_file *file) { return file->fs; } static void ATTR_FORMAT(2, 0) fs_set_verror(struct fs *fs, const char *fmt, va_list args) { /* the error is always kept in the parentmost fs */ if (fs->parent != NULL) fs_set_verror(fs->parent, fmt, args); else { str_truncate(fs->last_error, 0); str_vprintfa(fs->last_error, fmt, args); } } const char *fs_last_error(struct fs *fs) { /* the error is always kept in the parentmost fs */ if (fs->parent != NULL) return fs_last_error(fs->parent); if (str_len(fs->last_error) == 0) return "BUG: Unknown fs error"; return str_c(fs->last_error); } const char *fs_file_last_error(struct fs_file *file) { return fs_last_error(file->fs); } bool fs_prefetch(struct fs_file *file, uoff_t length) { bool ret; if (!file->read_or_prefetch_counted) { file->read_or_prefetch_counted = TRUE; file->fs->stats.prefetch_count++; fs_file_timing_start(file, FS_OP_PREFETCH); } T_BEGIN { ret = file->fs->v.prefetch(file, length); } T_END; fs_file_timing_end(file, FS_OP_PREFETCH); return ret; } ssize_t fs_read_via_stream(struct fs_file *file, void *buf, size_t size) { const unsigned char *data; size_t data_size; ssize_t ret; i_assert(size > 0); if (file->pending_read_input == NULL) file->pending_read_input = fs_read_stream(file, size+1); ret = i_stream_read_data(file->pending_read_input, &data, &data_size, size-1); if (ret == 0) { fs_set_error_async(file->fs); return -1; } if (ret < 0 && file->pending_read_input->stream_errno != 0) { fs_set_error(file->fs, "read(%s) failed: %s", i_stream_get_name(file->pending_read_input), i_stream_get_error(file->pending_read_input)); } else { ret = I_MIN(size, data_size); memcpy(buf, data, ret); } i_stream_unref(&file->pending_read_input); return ret; } ssize_t fs_read(struct fs_file *file, void *buf, size_t size) { int ret; if (!file->read_or_prefetch_counted) { file->read_or_prefetch_counted = TRUE; file->fs->stats.read_count++; fs_file_timing_start(file, FS_OP_READ); } if (file->fs->v.read != NULL) { T_BEGIN { ret = file->fs->v.read(file, buf, size); } T_END; if (!(ret < 0 && errno == EAGAIN)) fs_file_timing_end(file, FS_OP_READ); return ret; } /* backend didn't bother to implement read(), but we can do it with streams. */ return fs_read_via_stream(file, buf, size); } struct istream *fs_read_stream(struct fs_file *file, size_t max_buffer_size) { struct istream *input, *inputs[2]; const unsigned char *data; size_t size; ssize_t ret; bool want_seekable = FALSE; if (!file->read_or_prefetch_counted) { file->read_or_prefetch_counted = TRUE; file->fs->stats.read_count++; fs_file_timing_start(file, FS_OP_READ); } if (file->seekable_input != NULL) { /* allow multiple open streams, each in a different position */ input = i_stream_create_limit(file->seekable_input, (uoff_t)-1); i_stream_seek(input, 0); return input; } T_BEGIN { input = file->fs->v.read_stream(file, max_buffer_size); } T_END; if (input->stream_errno != 0) { /* read failed already */ fs_file_timing_end(file, FS_OP_READ); return input; } if (file->fs->set.enable_timing) { struct istream *input2 = i_stream_create_fs_stats(input, file); i_stream_unref(&input); input = input2; } if ((file->flags & FS_OPEN_FLAG_SEEKABLE) != 0) want_seekable = TRUE; else if ((file->flags & FS_OPEN_FLAG_ASYNC) == 0 && !input->blocking) want_seekable = TRUE; if (want_seekable && !input->seekable) { /* need to make the stream seekable */ inputs[0] = input; inputs[1] = NULL; input = i_stream_create_seekable_path(inputs, max_buffer_size, file->fs->temp_path_prefix); i_stream_set_name(input, i_stream_get_name(inputs[0])); i_stream_unref(&inputs[0]); } file->seekable_input = input; i_stream_ref(file->seekable_input); if ((file->flags & FS_OPEN_FLAG_ASYNC) == 0 && !input->blocking) { /* read the whole input stream before returning */ while ((ret = i_stream_read_data(input, &data, &size, 0)) >= 0) { i_stream_skip(input, size); if (ret == 0) { if (fs_wait_async(file->fs) < 0) { input->stream_errno = errno; input->eof = TRUE; break; } } } i_stream_seek(input, 0); } return input; } int fs_write_via_stream(struct fs_file *file, const void *data, size_t size) { struct ostream *output; ssize_t ret; int err; if (!file->write_pending) { output = fs_write_stream(file); if ((ret = o_stream_send(output, data, size)) < 0) { err = errno; fs_write_stream_abort_error(file, &output, "fs_write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); errno = err; return -1; } i_assert((size_t)ret == size); ret = fs_write_stream_finish(file, &output); } else { ret = fs_write_stream_finish_async(file); } if (ret == 0) { fs_set_error_async(file->fs); file->write_pending = TRUE; return -1; } file->write_pending = FALSE; return ret < 0 ? -1 : 0; } int fs_write(struct fs_file *file, const void *data, size_t size) { int ret; if (file->fs->v.write != NULL) { fs_file_timing_start(file, FS_OP_WRITE); T_BEGIN { ret = file->fs->v.write(file, data, size); } T_END; if (!(ret < 0 && errno == EAGAIN)) { file->fs->stats.write_count++; file->fs->stats.write_bytes += size; fs_file_timing_end(file, FS_OP_WRITE); } return ret; } /* backend didn't bother to implement write(), but we can do it with streams. */ return fs_write_via_stream(file, data, size); } struct ostream *fs_write_stream(struct fs_file *file) { i_assert(!file->writing_stream); i_assert(file->output == NULL); file->writing_stream = TRUE; file->fs->stats.write_count++; T_BEGIN { file->fs->v.write_stream(file); } T_END; i_assert(file->output != NULL); o_stream_cork(file->output); return file->output; } static int fs_write_stream_finish_int(struct fs_file *file, bool success) { int ret; i_assert(file->writing_stream); fs_file_timing_start(file, FS_OP_WRITE); T_BEGIN { ret = file->fs->v.write_stream_finish(file, success); } T_END; if (ret != 0) { fs_file_timing_end(file, FS_OP_WRITE); file->metadata_changed = FALSE; } else { /* write didn't finish yet. this shouldn't happen if we indicated a failure. */ i_assert(success); } if (ret != 0) { i_assert(file->output == NULL); file->writing_stream = FALSE; } return ret; } int fs_write_stream_finish(struct fs_file *file, struct ostream **output) { bool success = TRUE; i_assert(*output == file->output || *output == NULL); i_assert(output != &file->output); *output = NULL; if (file->output != NULL) { o_stream_uncork(file->output); if (o_stream_nfinish(file->output) < 0) { fs_set_error(file->fs, "write(%s) failed: %s", o_stream_get_name(file->output), o_stream_get_error(file->output)); success = FALSE; } file->fs->stats.write_bytes += file->output->offset; } return fs_write_stream_finish_int(file, success); } int fs_write_stream_finish_async(struct fs_file *file) { return fs_write_stream_finish_int(file, TRUE); } static void fs_write_stream_abort_int(struct fs_file *file, struct ostream **output) { int ret; i_assert(*output == file->output); i_assert(file->output != NULL); i_assert(output != &file->output); *output = NULL; o_stream_ignore_last_errors(file->output); /* make sure we don't have an old error lying around */ ret = fs_write_stream_finish_int(file, FALSE); i_assert(ret != 0); } void fs_write_stream_abort_error(struct fs_file *file, struct ostream **output, const char *error_fmt, ...) { va_list args; va_start(args, error_fmt); fs_set_verror(file->fs, error_fmt, args); fs_write_stream_abort_int(file, output); va_end(args); } void fs_write_stream_abort(struct fs_file *file, struct ostream **output) { fs_write_stream_abort_error(file, output, "Write aborted"); } void fs_write_stream_abort_parent(struct fs_file *file, struct ostream **output) { i_assert(file->parent != NULL); i_assert(fs_file_last_error(file->parent) != NULL); fs_write_stream_abort_int(file->parent, output); } void fs_write_set_hash(struct fs_file *file, const struct hash_method *method, const void *digest) { file->write_digest_method = method; i_free(file->write_digest); file->write_digest = i_malloc(method->digest_size); memcpy(file->write_digest, digest, method->digest_size); } void fs_file_set_async_callback(struct fs_file *file, fs_file_async_callback_t *callback, void *context) { if (file->fs->v.set_async_callback != NULL) file->fs->v.set_async_callback(file, callback, context); else callback(context); } int fs_wait_async(struct fs *fs) { int ret; /* recursion not allowed */ i_assert(fs->prev_ioloop == NULL); if (fs->v.wait_async == NULL) ret = 0; else T_BEGIN { fs->prev_ioloop = current_ioloop; ret = fs->v.wait_async(fs); i_assert(current_ioloop == fs->prev_ioloop); fs->prev_ioloop = NULL; } T_END; return ret; } bool fs_switch_ioloop(struct fs *fs) { bool ret = FALSE; if (fs->v.switch_ioloop != NULL) { T_BEGIN { ret = fs->v.switch_ioloop(fs); } T_END; } else if (fs->parent != NULL) { ret = fs_switch_ioloop(fs->parent); } return ret; } int fs_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r) { int ret; T_BEGIN { ret = file->fs->v.lock(file, secs, lock_r); } T_END; return ret; } void fs_unlock(struct fs_lock **_lock) { struct fs_lock *lock = *_lock; *_lock = NULL; T_BEGIN { lock->file->fs->v.unlock(lock); } T_END; } int fs_exists(struct fs_file *file) { struct stat st; int ret; if (file->fs->v.exists == NULL) { /* fallback to stat() */ if (fs_stat(file, &st) == 0) return 1; else return errno == ENOENT ? 0 : -1; } fs_file_timing_start(file, FS_OP_EXISTS); T_BEGIN { ret = file->fs->v.exists(file); } T_END; if (!(ret < 0 && errno == EAGAIN)) { file->fs->stats.exists_count++; fs_file_timing_end(file, FS_OP_EXISTS); } return ret; } int fs_stat(struct fs_file *file, struct stat *st_r) { int ret; if (file->fs->v.stat == NULL) { fs_set_error(file->fs, "fs_stat() not supported"); return -1; } if (!file->read_or_prefetch_counted && !file->lookup_metadata_counted && !file->stat_counted) { file->stat_counted = TRUE; file->fs->stats.stat_count++; fs_file_timing_start(file, FS_OP_STAT); } T_BEGIN { ret = file->fs->v.stat(file, st_r); } T_END; if (!(ret < 0 && errno == EAGAIN)) fs_file_timing_end(file, FS_OP_STAT); return ret; } int fs_get_nlinks(struct fs_file *file, nlink_t *nlinks_r) { int ret; if (file->fs->v.get_nlinks == NULL) { struct stat st; if (fs_stat(file, &st) < 0) return -1; *nlinks_r = st.st_nlink; return 0; } if (!file->read_or_prefetch_counted && !file->lookup_metadata_counted && !file->stat_counted) { file->stat_counted = TRUE; file->fs->stats.stat_count++; fs_file_timing_start(file, FS_OP_STAT); } T_BEGIN { ret = file->fs->v.get_nlinks(file, nlinks_r); } T_END; if (!(ret < 0 && errno == EAGAIN)) fs_file_timing_end(file, FS_OP_STAT); return ret; } int fs_default_copy(struct fs_file *src, struct fs_file *dest) { int tmp_errno; /* we're going to be counting this as read+write, so remove the copy_count we just added */ dest->fs->stats.copy_count--; if (dest->copy_src != NULL) { i_assert(src == NULL || src == dest->copy_src); if (dest->copy_output == NULL) { i_assert(dest->copy_input == NULL); if (fs_write_stream_finish_async(dest) <= 0) return -1; dest->copy_src = NULL; return 0; } } else { dest->copy_src = src; dest->copy_input = fs_read_stream(src, IO_BLOCK_SIZE); dest->copy_output = fs_write_stream(dest); } while (o_stream_send_istream(dest->copy_output, dest->copy_input) > 0) ; if (dest->copy_input->stream_errno != 0) { fs_write_stream_abort_error(dest, &dest->copy_output, "read(%s) failed: %s", i_stream_get_name(dest->copy_input), i_stream_get_error(dest->copy_input)); errno = dest->copy_input->stream_errno; i_stream_unref(&dest->copy_input); return -1; } if (dest->copy_output->stream_errno != 0) { /* errno might not survive abort error */ tmp_errno = dest->copy_output->stream_errno; fs_write_stream_abort_error(dest, &dest->copy_output, "write(%s) failed: %s", o_stream_get_name(dest->copy_output), o_stream_get_error(dest->copy_output)); errno = tmp_errno; i_stream_unref(&dest->copy_input); return -1; } if (!dest->copy_input->eof) { fs_set_error_async(dest->fs); return -1; } i_stream_unref(&dest->copy_input); if (fs_write_stream_finish(dest, &dest->copy_output) <= 0) return -1; dest->copy_src = NULL; return 0; } int fs_copy(struct fs_file *src, struct fs_file *dest) { int ret; i_assert(src->fs == dest->fs); if (src->fs->v.copy == NULL) { fs_set_error(src->fs, "fs_copy() not supported"); return -1; } fs_file_timing_start(dest, FS_OP_COPY); T_BEGIN { ret = src->fs->v.copy(src, dest); } T_END; if (!(ret < 0 && errno == EAGAIN)) { fs_file_timing_end(dest, FS_OP_COPY); dest->fs->stats.copy_count++; dest->metadata_changed = FALSE; } return ret; } int fs_copy_finish_async(struct fs_file *dest) { int ret; T_BEGIN { ret = dest->fs->v.copy(NULL, dest); } T_END; if (!(ret < 0 && errno == EAGAIN)) { fs_file_timing_end(dest, FS_OP_COPY); dest->fs->stats.copy_count++; dest->metadata_changed = FALSE; } return ret; } int fs_rename(struct fs_file *src, struct fs_file *dest) { int ret; i_assert(src->fs == dest->fs); fs_file_timing_start(dest, FS_OP_RENAME); T_BEGIN { ret = src->fs->v.rename(src, dest); } T_END; if (!(ret < 0 && errno == EAGAIN)) { dest->fs->stats.rename_count++; fs_file_timing_end(dest, FS_OP_RENAME); } return ret; } int fs_delete(struct fs_file *file) { int ret; i_assert(!file->writing_stream); fs_file_timing_start(file, FS_OP_DELETE); T_BEGIN { ret = file->fs->v.delete_file(file); } T_END; if (!(ret < 0 && errno == EAGAIN)) { file->fs->stats.delete_count++; fs_file_timing_end(file, FS_OP_DELETE); } return ret; } struct fs_iter * fs_iter_init(struct fs *fs, const char *path, enum fs_iter_flags flags) { struct fs_iter *iter; struct timeval now = ioloop_timeval; i_assert((flags & FS_ITER_FLAG_OBJECTIDS) == 0 || (fs_get_properties(fs) & FS_PROPERTY_OBJECTIDS) != 0); fs->stats.iter_count++; if (fs->set.enable_timing) { if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); } if (fs->v.iter_init == NULL) { iter = i_new(struct fs_iter, 1); iter->fs = fs; } else T_BEGIN { iter = fs->v.iter_init(fs, path, flags); } T_END; iter->start_time = now; DLLIST_PREPEND(&fs->iters, iter); return iter; } int fs_iter_deinit(struct fs_iter **_iter) { struct fs_iter *iter = *_iter; int ret; *_iter = NULL; DLLIST_REMOVE(&iter->fs->iters, iter); if (iter->fs->v.iter_deinit == NULL) { fs_set_error(iter->fs, "FS teration not supported"); i_free(iter); ret = -1; } else T_BEGIN { ret = iter->fs->v.iter_deinit(iter); } T_END; return ret; } const char *fs_iter_next(struct fs_iter *iter) { const char *ret; if (iter->fs->v.iter_next == NULL) return NULL; T_BEGIN { ret = iter->fs->v.iter_next(iter); } T_END; if (iter->start_time.tv_sec != 0 && (ret != NULL || !fs_iter_have_more(iter))) { /* first result returned - count this as the finish time, since we don't want to count the time caller spends on this iteration. */ fs_timing_end(&iter->fs->stats.timings[FS_OP_ITER], &iter->start_time); /* don't count this again */ iter->start_time.tv_sec = 0; } return ret; } void fs_iter_set_async_callback(struct fs_iter *iter, fs_file_async_callback_t *callback, void *context) { iter->async_callback = callback; iter->async_context = context; } bool fs_iter_have_more(struct fs_iter *iter) { return iter->async_have_more; } const struct fs_stats *fs_get_stats(struct fs *fs) { return &fs->stats; } void fs_set_error(struct fs *fs, const char *fmt, ...) { va_list args; va_start(args, fmt); fs_set_verror(fs, fmt, args); va_end(args); } void fs_set_critical(struct fs *fs, const char *fmt, ...) { va_list args; va_start(args, fmt); fs_set_verror(fs, fmt, args); i_error("fs-%s: %s", fs->name, fs_last_error(fs)); va_end(args); } void fs_set_error_async(struct fs *fs) { fs_set_error(fs, "Asynchronous operation in progress"); errno = EAGAIN; } static uint64_t fs_stats_count_ops(const struct fs_stats *stats, const enum fs_op ops[], unsigned int ops_count) { uint64_t ret = 0; for (unsigned int i = 0; i < ops_count; i++) { if (stats->timings[ops[i]] != NULL) ret += timing_get_sum(stats->timings[ops[i]]); } return ret; } uint64_t fs_stats_get_read_usecs(const struct fs_stats *stats) { const enum fs_op read_ops[] = { FS_OP_METADATA, FS_OP_PREFETCH, FS_OP_READ, FS_OP_EXISTS, FS_OP_STAT, FS_OP_ITER }; return fs_stats_count_ops(stats, read_ops, N_ELEMENTS(read_ops)); } uint64_t fs_stats_get_write_usecs(const struct fs_stats *stats) { const enum fs_op write_ops[] = { FS_OP_WRITE, FS_OP_COPY, FS_OP_DELETE }; return fs_stats_count_ops(stats, write_ops, N_ELEMENTS(write_ops)); } dovecot-2.2.33.2/src/lib-fs/ostream-metawrap.h0000644000175000017500000000026513123174404015733 00000000000000#ifndef OSTREAM_METAWRAP_H #define OSTREAM_METAWRAP_H struct ostream * o_stream_create_metawrap(struct ostream *output, void (*write_callback)(void *), void *context); #endif dovecot-2.2.33.2/src/lib-fs/test-fs-metawrap.c0000644000175000017500000000371213165463624015654 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "fs-test.h" #include "test-common.h" static const struct fs_settings fs_set; static void test_fs_metawrap_stat(void) { struct fs *fs; struct fs_file *file; struct test_fs_file *test_file; struct istream *input; struct stat st; const char *error; unsigned int i; test_begin("fs metawrap stat"); if (fs_init("metawrap", "test", &fs_set, &fs, &error) < 0) i_fatal("fs_init() failed: %s", error); for (i = 0; i < 2; i++) { file = fs_file_init(fs, "foo", FS_OPEN_MODE_READONLY); test_file = test_fs_file_get(fs, "foo"); str_append(test_file->contents, "key:value\n\n12345678901234567890"); if (i == 0) { input = fs_read_stream(file, 2); test_istream_set_max_buffer_size(test_file->input, 2); } else { input = NULL; } test_assert_idx(fs_stat(file, &st) == 0 && st.st_size == 20, i); if (input != NULL) i_stream_unref(&input); fs_file_deinit(&file); } fs_deinit(&fs); test_end(); } static void test_fs_metawrap_async(void) { test_fs_async("metawrap", FS_PROPERTY_METADATA, "metawrap", "test"); test_fs_async("metawrap passthrough", 0, "metawrap", "test"); test_fs_async("double-metawrap", FS_PROPERTY_METADATA, "metawrap", "metawrap:test"); } static void test_fs_metawrap_write_empty(void) { struct fs *fs; const char *error; test_begin("fs metawrap write empty file"); if (fs_init("metawrap", "test", &fs_set, &fs, &error) < 0) i_fatal("fs_init() failed: %s", error); struct fs_file *file = fs_file_init(fs, "foo", FS_OPEN_MODE_REPLACE); struct ostream *output = fs_write_stream(file); test_assert(fs_write_stream_finish(file, &output) > 0); fs_file_deinit(&file); fs_deinit(&fs); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_fs_metawrap_stat, test_fs_metawrap_async, test_fs_metawrap_write_empty, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-fs/ostream-cmp.c0000644000175000017500000000422413165463624014677 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "ostream-private.h" #include "ostream-cmp.h" struct cmp_ostream { struct ostream_private ostream; struct istream *input; bool equals; }; static void o_stream_cmp_close(struct iostream_private *stream, bool close_parent) { struct cmp_ostream *cstream = (struct cmp_ostream *)stream; if (cstream->input == NULL) return; i_stream_unref(&cstream->input); (void)o_stream_flush(&cstream->ostream.ostream); if (close_parent) o_stream_close(cstream->ostream.parent); } bool stream_cmp_block(struct istream *input, const unsigned char *data, size_t size) { const unsigned char *indata; size_t insize, max; while (size > 0) { (void)i_stream_read_data(input, &indata, &insize, size-1); max = I_MIN(insize, size); if (insize == 0 || memcmp(data, indata, max) != 0) return FALSE; data += max; size -= max; i_stream_skip(input, max); } return TRUE; } static ssize_t o_stream_cmp_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct cmp_ostream *cstream = (struct cmp_ostream *)stream; unsigned int i; ssize_t ret; if (cstream->equals) { for (i = 0; i < iov_count; i++) { if (!stream_cmp_block(cstream->input, iov[i].iov_base, iov[i].iov_len)) { cstream->equals = FALSE; break; } } } if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { o_stream_copy_error_from_parent(stream); return -1; } stream->ostream.offset += ret; return ret; } struct ostream * o_stream_create_cmp(struct ostream *output, struct istream *input) { struct cmp_ostream *cstream; cstream = i_new(struct cmp_ostream, 1); cstream->ostream.sendv = o_stream_cmp_sendv; cstream->ostream.iostream.close = o_stream_cmp_close; cstream->input = input; cstream->equals = TRUE; i_stream_ref(input); return o_stream_create(&cstream->ostream, output, o_stream_get_fd(output)); } bool o_stream_cmp_equals(struct ostream *_output) { struct cmp_ostream *cstream = (struct cmp_ostream *)_output->real_stream; return cstream->equals; } dovecot-2.2.33.2/src/lib-fs/fs-posix.c0000644000175000017500000005575113165463624014233 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "guid.h" #include "istream.h" #include "ostream.h" #include "safe-mkstemp.h" #include "mkdir-parents.h" #include "write-full.h" #include "file-lock.h" #include "file-dotlock.h" #include "fs-api-private.h" #include #include #include #include #include #define FS_POSIX_DOTLOCK_STALE_TIMEOUT_SECS (60*10) #define MAX_MKDIR_RETRY_COUNT 5 #define FS_DEFAULT_MODE 0600 enum fs_posix_lock_method { FS_POSIX_LOCK_METHOD_FLOCK, FS_POSIX_LOCK_METHOD_DOTLOCK }; struct posix_fs { struct fs fs; char *temp_file_prefix, *root_path, *path_prefix; size_t temp_file_prefix_len; enum fs_posix_lock_method lock_method; mode_t mode; bool mode_auto; bool have_dirs; bool disable_fsync; }; struct posix_fs_file { struct fs_file file; char *temp_path, *full_path; int fd; enum fs_open_mode open_mode; enum fs_open_flags open_flags; buffer_t *write_buf; bool seek_to_beginning; }; struct posix_fs_lock { struct fs_lock lock; struct file_lock *file_lock; struct dotlock *dotlock; }; struct posix_fs_iter { struct fs_iter iter; char *path; DIR *dir; int err; }; static struct fs *fs_posix_alloc(void) { struct posix_fs *fs; fs = i_new(struct posix_fs, 1); fs->fs = fs_class_posix; return &fs->fs; } static int fs_posix_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct posix_fs *fs = (struct posix_fs *)_fs; const char *const *tmp; fs->temp_file_prefix = set->temp_file_prefix != NULL ? i_strdup(set->temp_file_prefix) : i_strdup("temp.dovecot."); fs->temp_file_prefix_len = strlen(fs->temp_file_prefix); fs->root_path = i_strdup(set->root_path); fs->fs.set.temp_file_prefix = fs->temp_file_prefix; fs->fs.set.root_path = fs->root_path; fs->lock_method = FS_POSIX_LOCK_METHOD_FLOCK; fs->mode = FS_DEFAULT_MODE; tmp = t_strsplit_spaces(args, ": "); for (; *tmp != NULL; tmp++) { const char *arg = *tmp; if (strcmp(arg, "lock=flock") == 0) fs->lock_method = FS_POSIX_LOCK_METHOD_FLOCK; else if (strcmp(arg, "lock=dotlock") == 0) fs->lock_method = FS_POSIX_LOCK_METHOD_DOTLOCK; else if (strncmp(arg, "prefix=", 7) == 0) { unsigned int len = strlen(arg + 7); i_free(fs->path_prefix); if (len > 0 && arg[7+len-1] != '/') fs->path_prefix = i_strconcat(arg + 7, "/", NULL); else fs->path_prefix = i_strdup(arg + 7); } else if (strcmp(arg, "mode=auto") == 0) { fs->mode_auto = TRUE; } else if (strcmp(arg, "dirs") == 0) { fs->have_dirs = TRUE; } else if (strcmp(arg, "no-fsync") == 0) { fs->disable_fsync = TRUE; } else if (strncmp(arg, "mode=", 5) == 0) { unsigned int mode; if (str_to_uint_oct(arg+5, &mode) < 0) { fs_set_error(_fs, "Invalid mode value: %s", arg+5); return -1; } fs->mode = mode & 0666; if (fs->mode == 0) { fs_set_error(_fs, "Invalid mode: %s", arg+5); return -1; } } else { fs_set_error(_fs, "Unknown arg '%s'", arg); return -1; } } return 0; } static void fs_posix_deinit(struct fs *_fs) { struct posix_fs *fs = (struct posix_fs *)_fs; i_free(fs->temp_file_prefix); i_free(fs->root_path); i_free(fs->path_prefix); i_free(fs); } static enum fs_properties fs_posix_get_properties(struct fs *_fs) { struct posix_fs *fs = (struct posix_fs *)_fs; enum fs_properties props = FS_PROPERTY_LOCKS | FS_PROPERTY_FASTCOPY | FS_PROPERTY_RENAME | FS_PROPERTY_STAT | FS_PROPERTY_ITER | FS_PROPERTY_RELIABLEITER; /* FS_PROPERTY_DIRECTORIES is not returned normally because fs_delete() automatically rmdir()s parents. For backwards compatibility (especially with SIS code) we'll do it that way, but optionally with "dirs" parameter enable them. This is especially important to be able to use doveadm fs commands to delete empty directories. */ if (fs->have_dirs) props |= FS_PROPERTY_DIRECTORIES; return props; } static int fs_posix_get_mode(struct posix_fs *fs, const char *path, mode_t *mode_r) { struct stat st; const char *p; *mode_r = fs->mode; while (stat(path, &st) < 0) { if (errno != ENOENT) { fs_set_error(&fs->fs, "stat(%s) failed: %m", path); return -1; } p = strrchr(path, '/'); if (p != NULL) path = t_strdup_until(path, p); else if (strcmp(path, ".") != 0) path = "."; else return 0; } if ((st.st_mode & S_ISGID) != 0) { /* setgid set - copy mode from parent */ *mode_r = st.st_mode & 0666; } return 0; } static int fs_posix_mkdir_parents(struct posix_fs *fs, const char *path) { const char *dir, *fname; mode_t mode, dir_mode; fname = strrchr(path, '/'); if (fname == NULL) return 1; dir = t_strdup_until(path, fname); if (fs_posix_get_mode(fs, dir, &mode) < 0) return -1; dir_mode = mode; if ((dir_mode & 0600) != 0) dir_mode |= 0100; if ((dir_mode & 0060) != 0) dir_mode |= 0010; if ((dir_mode & 0006) != 0) dir_mode |= 0001; if (mkdir_parents(dir, dir_mode) == 0) return 0; else if (errno == EEXIST) return 1; else { fs_set_error(&fs->fs, "mkdir_parents(%s) failed: %m", dir); return -1; } } static int fs_posix_rmdir_parents(struct posix_fs *fs, const char *path) { const char *p; if (fs->have_dirs) return 0; if (fs->root_path == NULL && fs->path_prefix == NULL) return 0; while ((p = strrchr(path, '/')) != NULL) { path = t_strdup_until(path, p); if ((fs->root_path != NULL && strcmp(path, fs->root_path) == 0) || (fs->path_prefix != NULL && strncmp(path, fs->path_prefix, strlen(path)) == 0)) break; if (rmdir(path) == 0) { /* success, continue to parent */ } else if (errno == ENOTEMPTY || errno == EEXIST) { /* there are other entries in this directory */ break; } else if (errno == EBUSY || errno == ENOENT) { /* some other not-unexpected error */ break; } else { fs_set_error(&fs->fs, "rmdir(%s) failed: %m", path); return -1; } } return 0; } static int fs_posix_create(struct posix_fs_file *file) { struct posix_fs *fs = (struct posix_fs *)file->file.fs; string_t *str = t_str_new(256); const char *slash; unsigned int try_count = 0; mode_t mode; int fd; i_assert(file->temp_path == NULL); if ((slash = strrchr(file->full_path, '/')) != NULL) { str_append_n(str, file->full_path, slash - file->full_path); if (fs_posix_get_mode(fs, str_c(str), &mode) < 0) return -1; str_append_c(str, '/'); } else { if (fs_posix_get_mode(fs, ".", &mode) < 0) return -1; } str_append(str, fs->temp_file_prefix); fd = safe_mkstemp_hostpid(str, mode, (uid_t)-1, (gid_t)-1); while (fd == -1 && errno == ENOENT && try_count <= MAX_MKDIR_RETRY_COUNT) { if (fs_posix_mkdir_parents(fs, str_c(str)) < 0) return -1; fd = safe_mkstemp_hostpid(str, mode, (uid_t)-1, (gid_t)-1); try_count++; } if (fd == -1) { fs_set_error(&fs->fs, "safe_mkstemp(%s) failed: %m", str_c(str)); return -1; } file->temp_path = i_strdup(str_c(str)); return fd; } static int fs_posix_open(struct posix_fs_file *file) { struct posix_fs *fs = (struct posix_fs *)file->file.fs; const char *path = file->full_path; i_assert(file->fd == -1); switch (file->open_mode) { case FS_OPEN_MODE_READONLY: file->fd = open(path, O_RDONLY); if (file->fd == -1) fs_set_error(&fs->fs, "open(%s) failed: %m", path); break; case FS_OPEN_MODE_APPEND: file->fd = open(path, O_RDWR | O_APPEND); if (file->fd == -1) fs_set_error(&fs->fs, "open(%s) failed: %m", path); break; case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_REPLACE: T_BEGIN { file->fd = fs_posix_create(file); } T_END; break; } if (file->fd == -1) return -1; return 0; } static struct fs_file * fs_posix_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct posix_fs *fs = (struct posix_fs *)_fs; struct posix_fs_file *file; guid_128_t guid; file = i_new(struct posix_fs_file, 1); file->file.fs = _fs; if (mode != FS_OPEN_MODE_CREATE_UNIQUE_128) file->file.path = i_strdup(path); else { guid_128_generate(guid); file->file.path = i_strdup_printf("%s/%s", path, guid_128_to_string(guid)); } file->full_path = fs->path_prefix == NULL ? i_strdup(file->file.path) : i_strconcat(fs->path_prefix, file->file.path, NULL); file->open_mode = mode; file->open_flags = flags; file->fd = -1; return &file->file; } static void fs_posix_file_close(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; if (file->fd != -1 && file->file.output == NULL) { if (close(file->fd) < 0) { fs_set_critical(file->file.fs, "close(%s) failed: %m", file->full_path); } file->fd = -1; } } static void fs_posix_file_deinit(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; i_assert(_file->output == NULL); switch (file->open_mode) { case FS_OPEN_MODE_READONLY: case FS_OPEN_MODE_APPEND: break; case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_REPLACE: if (file->temp_path == NULL) break; /* failed to create/replace this. delete the temp file */ if (unlink(file->temp_path) < 0) { fs_set_critical(_file->fs, "unlink(%s) failed: %m", file->temp_path); } break; } i_free(file->temp_path); i_free(file->full_path); i_free(file->file.path); i_free(file); } static int fs_posix_open_for_read(struct posix_fs_file *file) { i_assert(file->file.output == NULL); i_assert(file->temp_path == NULL); if (file->fd == -1) { if (fs_posix_open(file) < 0) return -1; } return 0; } static bool fs_posix_prefetch(struct fs_file *_file, uoff_t length ATTR_UNUSED) { struct posix_fs_file *file = (struct posix_fs_file *)_file; if (fs_posix_open_for_read(file) < 0) return TRUE; /* HAVE_POSIX_FADVISE alone isn't enough for CentOS 4.9 */ #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) if (posix_fadvise(file->fd, 0, length, POSIX_FADV_WILLNEED) < 0) { i_error("posix_fadvise(%s) failed: %m", file->full_path); return TRUE; } #endif return FALSE; } static ssize_t fs_posix_read(struct fs_file *_file, void *buf, size_t size) { struct posix_fs_file *file = (struct posix_fs_file *)_file; ssize_t ret; if (fs_posix_open_for_read(file) < 0) return -1; if (file->seek_to_beginning) { file->seek_to_beginning = FALSE; if (lseek(file->fd, 0, SEEK_SET) < 0) { fs_set_critical(_file->fs, "lseek(%s, 0) failed: %m", file->full_path); return -1; } } ret = read(file->fd, buf, size); if (ret < 0) fs_set_error(_file->fs, "read(%s) failed: %m", file->full_path); fs_posix_file_close(_file); return ret; } static struct istream * fs_posix_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct posix_fs_file *file = (struct posix_fs_file *)_file; struct istream *input; if (fs_posix_open_for_read(file) < 0) input = i_stream_create_error_str(errno, "%s", fs_last_error(_file->fs)); else { /* the stream could live even after the fs_file */ input = i_stream_create_fd_autoclose(&file->fd, max_buffer_size); } i_stream_set_name(input, file->full_path); return input; } static void fs_posix_write_rename_if_needed(struct posix_fs_file *file) { struct posix_fs *fs = (struct posix_fs *)file->file.fs; const char *new_fname, *new_prefix, *p; new_fname = fs_metadata_find(&file->file.metadata, FS_METADATA_WRITE_FNAME); if (new_fname == NULL) return; p = strrchr(file->file.path, '/'); if (p == NULL) new_prefix = ""; else new_prefix = t_strdup_until(file->file.path, p+1); i_free(file->file.path); file->file.path = i_strconcat(new_prefix, new_fname, NULL); i_free(file->full_path); file->full_path = fs->path_prefix == NULL ? i_strdup(file->file.path) : i_strconcat(fs->path_prefix, file->file.path, NULL); } static int fs_posix_write_finish(struct posix_fs_file *file) { struct posix_fs *fs = (struct posix_fs *)file->file.fs; int ret, old_errno; if ((file->open_flags & FS_OPEN_FLAG_FSYNC) != 0 && !fs->disable_fsync) { if (fdatasync(file->fd) < 0) { fs_set_error(file->file.fs, "fdatasync(%s) failed: %m", file->full_path); return -1; } } fs_posix_write_rename_if_needed(file); switch (file->open_mode) { case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_CREATE: if ((ret = link(file->temp_path, file->full_path)) < 0) { fs_set_error(file->file.fs, "link(%s, %s) failed: %m", file->temp_path, file->full_path); } old_errno = errno; if (unlink(file->temp_path) < 0) { fs_set_error(file->file.fs, "unlink(%s) failed: %m", file->temp_path); } errno = old_errno; if (ret < 0) { fs_posix_file_close(&file->file); i_free_and_null(file->temp_path); return -1; } break; case FS_OPEN_MODE_REPLACE: if (rename(file->temp_path, file->full_path) < 0) { fs_set_error(file->file.fs, "rename(%s, %s) failed: %m", file->temp_path, file->full_path); return -1; } break; default: i_unreached(); } i_free_and_null(file->temp_path); file->seek_to_beginning = TRUE; /* allow opening the file after writing to it */ file->open_mode = FS_OPEN_MODE_READONLY; return 0; } static int fs_posix_write(struct fs_file *_file, const void *data, size_t size) { struct posix_fs_file *file = (struct posix_fs_file *)_file; ssize_t ret; if (file->fd == -1) { if (fs_posix_open(file) < 0) return -1; i_assert(file->fd != -1); } if (file->open_mode != FS_OPEN_MODE_APPEND) { if (write_full(file->fd, data, size) < 0) { fs_set_error(_file->fs, "write(%s) failed: %m", file->full_path); return -1; } return fs_posix_write_finish(file); } /* atomic append - it should either succeed or fail */ ret = write(file->fd, data, size); if (ret < 0) { fs_set_error(_file->fs, "write(%s) failed: %m", file->full_path); return -1; } if ((size_t)ret != size) { fs_set_error(_file->fs, "write(%s) returned %"PRIuSIZE_T"/%"PRIuSIZE_T, file->full_path, (size_t)ret, size); errno = ENOSPC; return -1; } return 0; } static void fs_posix_write_stream(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; i_assert(_file->output == NULL); if (file->open_mode == FS_OPEN_MODE_APPEND) { file->write_buf = buffer_create_dynamic(default_pool, 1024*32); _file->output = o_stream_create_buffer(file->write_buf); } else if (file->fd == -1 && fs_posix_open(file) < 0) { _file->output = o_stream_create_error_str(errno, "%s", fs_file_last_error(_file)); } else { i_assert(file->fd != -1); _file->output = o_stream_create_fd_file(file->fd, (uoff_t)-1, FALSE); } o_stream_set_name(_file->output, file->full_path); } static int fs_posix_write_stream_finish(struct fs_file *_file, bool success) { struct posix_fs_file *file = (struct posix_fs_file *)_file; int ret = success ? 0 : -1; o_stream_destroy(&_file->output); switch (file->open_mode) { case FS_OPEN_MODE_APPEND: if (ret == 0) { ret = fs_posix_write(_file, file->write_buf->data, file->write_buf->used); } buffer_free(&file->write_buf); break; case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_CREATE_UNIQUE_128: case FS_OPEN_MODE_REPLACE: if (ret == 0) ret = fs_posix_write_finish(file); break; case FS_OPEN_MODE_READONLY: i_unreached(); } return ret < 0 ? -1 : 1; } static int fs_posix_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r) { struct posix_fs_file *file = (struct posix_fs_file *)_file; struct posix_fs *fs = (struct posix_fs *)_file->fs; struct dotlock_settings dotlock_set; struct posix_fs_lock fs_lock, *ret_lock; int ret = -1; i_zero(&fs_lock); fs_lock.lock.file = _file; switch (fs->lock_method) { case FS_POSIX_LOCK_METHOD_FLOCK: #ifndef HAVE_FLOCK fs_set_error(_file->fs, "flock() not supported by OS " "(for file %s)", file->full_path); #else if (secs == 0) { ret = file_try_lock(file->fd, file->full_path, F_WRLCK, FILE_LOCK_METHOD_FLOCK, &fs_lock.file_lock); } else { ret = file_wait_lock(file->fd, file->full_path, F_WRLCK, FILE_LOCK_METHOD_FLOCK, secs, &fs_lock.file_lock); } if (ret < 0) { fs_set_error(_file->fs, "flock(%s) failed: %m", file->full_path); } #endif break; case FS_POSIX_LOCK_METHOD_DOTLOCK: i_zero(&dotlock_set); dotlock_set.stale_timeout = FS_POSIX_DOTLOCK_STALE_TIMEOUT_SECS; dotlock_set.use_excl_lock = TRUE; dotlock_set.timeout = secs; ret = file_dotlock_create(&dotlock_set, file->full_path, secs == 0 ? 0 : DOTLOCK_CREATE_FLAG_NONBLOCK, &fs_lock.dotlock); if (ret < 0) { fs_set_error(_file->fs, "file_dotlock_create(%s) failed: %m", file->full_path); } break; } if (ret <= 0) return ret; ret_lock = i_new(struct posix_fs_lock, 1); *ret_lock = fs_lock; *lock_r = &ret_lock->lock; return 1; } static void fs_posix_unlock(struct fs_lock *_lock) { struct posix_fs_lock *lock = (struct posix_fs_lock *)_lock; if (lock->file_lock != NULL) file_unlock(&lock->file_lock); if (lock->dotlock != NULL) file_dotlock_delete(&lock->dotlock); i_free(lock); } static int fs_posix_exists(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; struct stat st; if (stat(file->full_path, &st) < 0) { if (errno != ENOENT) { fs_set_error(_file->fs, "stat(%s) failed: %m", file->full_path); return -1; } return 0; } return 1; } static int fs_posix_stat(struct fs_file *_file, struct stat *st_r) { struct posix_fs_file *file = (struct posix_fs_file *)_file; /* in case output != NULL it means that we're still writing to the file and fs_stat() shouldn't stat the unfinished file. this is done by fs-sis after fs_copy(). */ if (file->fd != -1 && _file->output == NULL) { if (fstat(file->fd, st_r) < 0) { fs_set_error(_file->fs, "fstat(%s) failed: %m", file->full_path); return -1; } } else { if (stat(file->full_path, st_r) < 0) { fs_set_error(_file->fs, "stat(%s) failed: %m", file->full_path); return -1; } } return 0; } static int fs_posix_copy(struct fs_file *_src, struct fs_file *_dest) { struct posix_fs_file *src = (struct posix_fs_file *)_src; struct posix_fs_file *dest = (struct posix_fs_file *)_dest; struct posix_fs *fs = (struct posix_fs *)_src->fs; unsigned int try_count = 0; int ret; ret = link(src->full_path, dest->full_path); if (errno == EEXIST && dest->open_mode == FS_OPEN_MODE_REPLACE) { /* destination file already exists - replace it */ i_unlink_if_exists(dest->full_path); ret = link(src->full_path, dest->full_path); } while (ret < 0 && errno == ENOENT && try_count <= MAX_MKDIR_RETRY_COUNT) { if (fs_posix_mkdir_parents(fs, dest->full_path) < 0) return -1; ret = link(src->full_path, dest->full_path); try_count++; } if (ret < 0) { fs_set_error(_src->fs, "link(%s, %s) failed: %m", src->full_path, dest->full_path); return -1; } return 0; } static int fs_posix_rename(struct fs_file *_src, struct fs_file *_dest) { struct posix_fs *fs = (struct posix_fs *)_src->fs; struct posix_fs_file *src = (struct posix_fs_file *)_src; struct posix_fs_file *dest = (struct posix_fs_file *)_dest; unsigned int try_count = 0; int ret; ret = rename(src->full_path, dest->full_path); while (ret < 0 && errno == ENOENT && try_count <= MAX_MKDIR_RETRY_COUNT) { if (fs_posix_mkdir_parents(fs, dest->full_path) < 0) return -1; ret = rename(src->full_path, dest->full_path); try_count++; } if (ret < 0) { fs_set_error(_src->fs, "rename(%s, %s) failed: %m", src->full_path, dest->full_path); return -1; } return 0; } static int fs_posix_delete(struct fs_file *_file) { struct posix_fs_file *file = (struct posix_fs_file *)_file; struct posix_fs *fs = (struct posix_fs *)_file->fs; if (unlink(file->full_path) < 0) { if (!UNLINK_EISDIR(errno)) { fs_set_error(_file->fs, "unlink(%s) failed: %m", file->full_path); return -1; } /* attempting to delete a directory. convert it to rmdir() automatically. */ if (rmdir(file->full_path) < 0) { fs_set_error(_file->fs, "rmdir(%s) failed: %m", file->full_path); return -1; } } (void)fs_posix_rmdir_parents(fs, file->full_path); return 0; } static struct fs_iter * fs_posix_iter_init(struct fs *_fs, const char *path, enum fs_iter_flags flags) { struct posix_fs *fs = (struct posix_fs *)_fs; struct posix_fs_iter *iter; iter = i_new(struct posix_fs_iter, 1); iter->iter.fs = _fs; iter->iter.flags = flags; iter->path = fs->path_prefix == NULL ? i_strdup(path) : i_strconcat(fs->path_prefix, path, NULL); if (iter->path[0] == '\0') { i_free(iter->path); iter->path = i_strdup("."); } iter->dir = opendir(iter->path); if (iter->dir == NULL && errno != ENOENT) { iter->err = errno; fs_set_error(_fs, "opendir(%s) failed: %m", iter->path); } return &iter->iter; } static bool fs_posix_iter_want(struct posix_fs_iter *iter, const char *fname) { bool ret; T_BEGIN { const char *path = t_strdup_printf("%s/%s", iter->path, fname); struct stat st; if (stat(path, &st) < 0) ret = FALSE; else if (!S_ISDIR(st.st_mode)) ret = (iter->iter.flags & FS_ITER_FLAG_DIRS) == 0; else ret = (iter->iter.flags & FS_ITER_FLAG_DIRS) != 0; } T_END; return ret; } static const char *fs_posix_iter_next(struct fs_iter *_iter) { struct posix_fs_iter *iter = (struct posix_fs_iter *)_iter; struct posix_fs *fs = (struct posix_fs *)_iter->fs; struct dirent *d; if (iter->dir == NULL) return NULL; errno = 0; for (; (d = readdir(iter->dir)) != NULL; errno = 0) { if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) continue; if (strncmp(d->d_name, fs->temp_file_prefix, fs->temp_file_prefix_len) == 0) continue; #ifdef HAVE_DIRENT_D_TYPE switch (d->d_type) { case DT_UNKNOWN: if (!fs_posix_iter_want(iter, d->d_name)) break; /* fall through */ case DT_REG: case DT_LNK: if ((iter->iter.flags & FS_ITER_FLAG_DIRS) == 0) return d->d_name; break; case DT_DIR: if ((iter->iter.flags & FS_ITER_FLAG_DIRS) != 0) return d->d_name; break; default: break; } #else if (fs_posix_iter_want(iter, d->d_name)) return d->d_name; #endif } if (errno != 0) { iter->err = errno; fs_set_error(_iter->fs, "readdir(%s) failed: %m", iter->path); } return NULL; } static int fs_posix_iter_deinit(struct fs_iter *_iter) { struct posix_fs_iter *iter = (struct posix_fs_iter *)_iter; int ret = 0; if (iter->dir != NULL && closedir(iter->dir) < 0 && iter->err == 0) { iter->err = errno; fs_set_error(_iter->fs, "closedir(%s) failed: %m", iter->path); } if (iter->err != 0) { errno = iter->err; ret = -1; } i_free(iter->path); i_free(iter); return ret; } const struct fs fs_class_posix = { .name = "posix", .v = { fs_posix_alloc, fs_posix_init, fs_posix_deinit, fs_posix_get_properties, fs_posix_file_init, fs_posix_file_deinit, fs_posix_file_close, NULL, NULL, NULL, fs_default_set_metadata, NULL, fs_posix_prefetch, fs_posix_read, fs_posix_read_stream, fs_posix_write, fs_posix_write_stream, fs_posix_write_stream_finish, fs_posix_lock, fs_posix_unlock, fs_posix_exists, fs_posix_stat, fs_posix_copy, fs_posix_rename, fs_posix_delete, fs_posix_iter_init, fs_posix_iter_next, fs_posix_iter_deinit, NULL, NULL, } }; dovecot-2.2.33.2/src/lib-fs/istream-fs-stats.h0000644000175000017500000000025013123174404015643 00000000000000#ifndef ISTREAM_FS_STATS_H #define ISTREAM_FS_STATS_H struct fs_file; struct istream * i_stream_create_fs_stats(struct istream *input, struct fs_file *file); #endif dovecot-2.2.33.2/src/lib-fs/fs-api-private.h0000644000175000017500000001206513165463624015306 00000000000000#ifndef FS_API_PRIVATE_H #define FS_API_PRIVATE_H #include "fs-api.h" #include "fs-wrapper.h" #include "module-context.h" #include struct fs_api_module_register { unsigned int id; }; union fs_api_module_context { struct fs_api_module_register *reg; }; extern struct fs_api_module_register fs_api_module_register; struct fs_vfuncs { struct fs *(*alloc)(void); int (*init)(struct fs *fs, const char *args, const struct fs_settings *set); void (*deinit)(struct fs *fs); enum fs_properties (*get_properties)(struct fs *fs); struct fs_file *(*file_init)(struct fs *fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags); void (*file_deinit)(struct fs_file *file); void (*file_close)(struct fs_file *file); const char *(*get_path)(struct fs_file *file); void (*set_async_callback)(struct fs_file *file, fs_file_async_callback_t *callback, void *context); int (*wait_async)(struct fs *fs); void (*set_metadata)(struct fs_file *file, const char *key, const char *value); int (*get_metadata)(struct fs_file *file, const ARRAY_TYPE(fs_metadata) **metadata_r); bool (*prefetch)(struct fs_file *file, uoff_t length); ssize_t (*read)(struct fs_file *file, void *buf, size_t size); struct istream *(*read_stream)(struct fs_file *file, size_t max_buffer_size); int (*write)(struct fs_file *file, const void *data, size_t size); void (*write_stream)(struct fs_file *file); /* After write_stream_finish() is called once, all the following (async) calls will have success==TRUE. */ int (*write_stream_finish)(struct fs_file *file, bool success); int (*lock)(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r); void (*unlock)(struct fs_lock *lock); int (*exists)(struct fs_file *file); int (*stat)(struct fs_file *file, struct stat *st_r); int (*copy)(struct fs_file *src, struct fs_file *dest); int (*rename)(struct fs_file *src, struct fs_file *dest); int (*delete_file)(struct fs_file *file); struct fs_iter *(*iter_init)(struct fs *fs, const char *path, enum fs_iter_flags flags); const char *(*iter_next)(struct fs_iter *iter); int (*iter_deinit)(struct fs_iter *iter); bool (*switch_ioloop)(struct fs *fs); int (*get_nlinks)(struct fs_file *file, nlink_t *nlinks_r); }; struct fs { struct fs *parent; /* for wrapper filesystems */ const char *name; struct fs_vfuncs v; char *temp_path_prefix; int refcount; char *username, *session_id; struct fs_settings set; string_t *last_error; /* may be used by fs_wait_async() to do the waiting */ struct ioloop *wait_ioloop, *prev_ioloop; unsigned int files_open_count; struct fs_file *files; struct fs_iter *iters; struct fs_stats stats; ARRAY(union fs_api_module_context *) module_contexts; }; struct fs_file { /* linked list of all files */ struct fs_file *prev, *next; struct fs_file *parent; /* for wrapper filesystems */ struct fs *fs; struct ostream *output; char *path; enum fs_open_flags flags; struct istream *seekable_input; struct istream *pending_read_input; const struct hash_method *write_digest_method; void *write_digest; pool_t metadata_pool; ARRAY_TYPE(fs_metadata) metadata; struct fs_file *copy_src; struct istream *copy_input; struct ostream *copy_output; struct timeval timing_start[FS_OP_COUNT]; unsigned int write_pending:1; unsigned int writing_stream:1; unsigned int metadata_changed:1; unsigned int read_or_prefetch_counted:1; unsigned int lookup_metadata_counted:1; unsigned int stat_counted:1; }; struct fs_lock { struct fs_file *file; }; struct fs_iter { /* linked list of all iters */ struct fs_iter *prev, *next; struct fs *fs; enum fs_iter_flags flags; struct timeval start_time; bool async_have_more; fs_file_async_callback_t *async_callback; void *async_context; }; extern const struct fs fs_class_dict; extern const struct fs fs_class_posix; extern const struct fs fs_class_randomfail; extern const struct fs fs_class_metawrap; extern const struct fs fs_class_sis; extern const struct fs fs_class_sis_queue; extern const struct fs fs_class_test; void fs_class_register(const struct fs *fs_class); void fs_set_error(struct fs *fs, const char *fmt, ...) ATTR_FORMAT(2, 3); void fs_set_critical(struct fs *fs, const char *fmt, ...) ATTR_FORMAT(2, 3); void fs_set_error_async(struct fs *fs); ssize_t fs_read_via_stream(struct fs_file *file, void *buf, size_t size); int fs_write_via_stream(struct fs_file *file, const void *data, size_t size); void fs_metadata_init(struct fs_file *file); void fs_metadata_init_or_clear(struct fs_file *file); void fs_default_set_metadata(struct fs_file *file, const char *key, const char *value); const char *fs_metadata_find(const ARRAY_TYPE(fs_metadata) *metadata, const char *key); int fs_default_copy(struct fs_file *src, struct fs_file *dest); void fs_file_timing_end(struct fs_file *file, enum fs_op op); /* Same as fs_write_stream_abort_error(), except it closes the *parent* file and error is left untouched */ void fs_write_stream_abort_parent(struct fs_file *file, struct ostream **output); #endif dovecot-2.2.33.2/src/lib-fs/ostream-cmp.h0000644000175000017500000000102213123174404014662 00000000000000#ifndef OSTREAM_CMP_H #define OSTREAM_CMP_H /* Compare given input stream to output being written to output stream. */ struct ostream * o_stream_create_cmp(struct ostream *output, struct istream *input); /* Returns TRUE if input and output are equal so far. If the caller needs to know if the files are entirely equal, it should check also if input stream is at EOF. */ bool o_stream_cmp_equals(struct ostream *output); bool stream_cmp_block(struct istream *input, const unsigned char *data, size_t size); #endif dovecot-2.2.33.2/src/lib-fs/istream-fs-stats.c0000644000175000017500000000267713123174404015655 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fs-api-private.h" #include "istream-private.h" #include "istream-fs-stats.h" struct fs_stats_istream { struct istream_private istream; struct fs_file *file; }; static ssize_t i_stream_fs_stats_read(struct istream_private *stream) { struct fs_stats_istream *sstream = (struct fs_stats_istream *)stream; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); if (ret > 0) { /* count the first returned bytes as the finish time, since we don't want to count the time caller spends on processing this stream. (only the first fs_file_timing_end() call actually does anything - the others are ignored.) */ fs_file_timing_end(sstream->file, FS_OP_READ); } return ret; } struct istream * i_stream_create_fs_stats(struct istream *input, struct fs_file *file) { struct fs_stats_istream *sstream; sstream = i_new(struct fs_stats_istream, 1); sstream->file = file; sstream->istream.max_buffer_size = input->real_stream->max_buffer_size; sstream->istream.stream_size_passthrough = TRUE; sstream->istream.read = i_stream_fs_stats_read; sstream->istream.istream.blocking = input->blocking; sstream->istream.istream.seekable = input->seekable; return i_stream_create(&sstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-fs/fs-wrapper.c0000644000175000017500000000722713165463624014544 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fs-api-private.h" #include "ostream.h" struct wrapper_fs_iter { struct fs_iter iter; struct fs_iter *parent; }; enum fs_properties fs_wrapper_get_properties(struct fs *fs) { return fs_get_properties(fs->parent); } void fs_wrapper_file_close(struct fs_file *file) { fs_file_close(file->parent); } const char *fs_wrapper_file_get_path(struct fs_file *file) { return fs_file_path(file->parent); } void fs_wrapper_set_async_callback(struct fs_file *file, fs_file_async_callback_t *callback, void *context) { fs_file_set_async_callback(file->parent, callback, context); } int fs_wrapper_wait_async(struct fs *fs) { return fs_wait_async(fs->parent); } void fs_wrapper_set_metadata(struct fs_file *file, const char *key, const char *value) { fs_set_metadata(file->parent, key, value); } int fs_wrapper_get_metadata(struct fs_file *file, const ARRAY_TYPE(fs_metadata) **metadata_r) { return fs_get_metadata(file->parent, metadata_r); } bool fs_wrapper_prefetch(struct fs_file *file, uoff_t length) { return fs_prefetch(file->parent, length); } ssize_t fs_wrapper_read(struct fs_file *file, void *buf, size_t size) { return fs_read(file->parent, buf, size); } struct istream * fs_wrapper_read_stream(struct fs_file *file, size_t max_buffer_size) { return fs_read_stream(file->parent, max_buffer_size); } int fs_wrapper_write(struct fs_file *file, const void *data, size_t size) { return fs_write(file->parent, data, size); } void fs_wrapper_write_stream(struct fs_file *file) { i_assert(file->output == NULL); file->output = fs_write_stream(file->parent); } int fs_wrapper_write_stream_finish(struct fs_file *file, bool success) { if (!success) { fs_write_stream_abort_parent(file, &file->output); return -1; } if (fs_write_stream_finish(file->parent, &file->output) < 0) return -1; return 1; } int fs_wrapper_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r) { return fs_lock(file->parent, secs, lock_r); } void fs_wrapper_unlock(struct fs_lock *_lock ATTR_UNUSED) { i_unreached(); } int fs_wrapper_exists(struct fs_file *file) { return fs_exists(file->parent); } int fs_wrapper_stat(struct fs_file *file, struct stat *st_r) { return fs_stat(file->parent, st_r); } int fs_wrapper_get_nlinks(struct fs_file *file, nlink_t *nlinks_r) { return fs_get_nlinks(file->parent, nlinks_r); } int fs_wrapper_copy(struct fs_file *src, struct fs_file *dest) { if (src != NULL) return fs_copy(src->parent, dest->parent); else return fs_copy_finish_async(dest->parent); } int fs_wrapper_rename(struct fs_file *src, struct fs_file *dest) { return fs_rename(src->parent, dest->parent); } int fs_wrapper_delete(struct fs_file *file) { return fs_delete(file->parent); } struct fs_iter * fs_wrapper_iter_init(struct fs *fs, const char *path, enum fs_iter_flags flags) { struct wrapper_fs_iter *iter; iter = i_new(struct wrapper_fs_iter, 1); iter->iter.fs = fs; iter->iter.flags = flags; iter->parent = fs_iter_init(fs->parent, path, flags); return &iter->iter; } const char *fs_wrapper_iter_next(struct fs_iter *_iter) { struct wrapper_fs_iter *iter = (struct wrapper_fs_iter *)_iter; const char *fname; iter->parent->async_callback = _iter->async_callback; iter->parent->async_context = _iter->async_context; fname = fs_iter_next(iter->parent); _iter->async_have_more = iter->parent->async_have_more; return fname; } int fs_wrapper_iter_deinit(struct fs_iter *_iter) { struct wrapper_fs_iter *iter = (struct wrapper_fs_iter *)_iter; int ret; ret = fs_iter_deinit(&iter->parent); i_free(iter); return ret; } dovecot-2.2.33.2/src/lib-fs/fs-test-async.c0000644000175000017500000000461713165463624015156 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "ostream.h" #include "fs-test.h" #include "test-common.h" static void test_fs_async_write(const char *test_name, struct fs *fs) { struct fs_file *file; struct test_fs_file *test_file; struct ostream *output; unsigned int i; test_begin(t_strdup_printf("%s: async write", test_name)); for (i = 0; i < 3; i++) { file = fs_file_init(fs, "foo", FS_OPEN_MODE_REPLACE | FS_OPEN_FLAG_ASYNC); output = fs_write_stream(file); o_stream_nsend_str(output, "12345"); if (i < 2) { test_assert(fs_write_stream_finish(file, &output) == 0); test_assert(output == NULL); test_assert(fs_write_stream_finish_async(file) == 0); } test_file = test_fs_file_get(fs, "foo"); test_file->wait_async = FALSE; switch (i) { case 0: test_assert(fs_write_stream_finish_async(file) > 0); test_assert(test_file->contents->used > 0); break; case 1: test_file->io_failure = TRUE; test_assert(fs_write_stream_finish_async(file) < 0); test_assert(test_file->contents->used == 0); break; case 2: fs_write_stream_abort(file, &output); test_assert(test_file->contents->used == 0); break; } fs_file_deinit(&file); } test_end(); } static void test_fs_async_copy(const char *test_name, struct fs *fs) { struct fs_file *src, *dest; struct test_fs_file *test_file; test_begin(t_strdup_printf("%s: async copy", test_name)); src = fs_file_init(fs, "foo", FS_OPEN_MODE_REPLACE); test_assert(fs_write(src, "source", 6) == 0); dest = fs_file_init(fs, "bar", FS_OPEN_MODE_REPLACE | FS_OPEN_FLAG_ASYNC); test_assert(fs_copy(src, dest) == -1 && errno == EAGAIN); test_file = test_fs_file_get(fs, "bar"); test_file->wait_async = FALSE; test_assert(fs_copy_finish_async(dest) == 0); test_assert(test_file->contents->used > 0); fs_file_deinit(&dest); fs_file_deinit(&src); test_end(); } void test_fs_async(const char *test_name, enum fs_properties properties, const char *driver, const char *args) { struct fs_settings fs_set; struct fs *fs; struct test_fs *test_fs; const char *error; i_zero(&fs_set); if (fs_init(driver, args, &fs_set, &fs, &error) < 0) i_fatal("fs_init() failed: %s", error); test_fs = test_fs_get(fs); test_fs->properties = properties; test_fs_async_write(test_name, fs); test_fs_async_copy(test_name, fs); fs_deinit(&fs); } dovecot-2.2.33.2/src/lib-fs/istream-metawrap.c0000644000175000017500000001104013165463624015724 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-metawrap.h" #define METAWRAP_MAX_METADATA_LINE_LEN 8192 struct metawrap_istream { struct istream_private istream; metawrap_callback_t *callback; void *context; uoff_t start_offset, pending_seek; bool in_metadata; }; static int metadata_header_read(struct metawrap_istream *mstream) { char *line, *p; while ((line = i_stream_read_next_line(mstream->istream.parent)) != NULL) { if (*line == '\0') { mstream->callback(NULL, NULL, mstream->context); return 1; } p = strchr(line, ':'); if (p == NULL) { io_stream_set_error(&mstream->istream.iostream, "Metadata header line is missing ':' at offset %"PRIuUOFF_T, mstream->istream.istream.v_offset); mstream->istream.istream.stream_errno = EINVAL; return -1; } *p++ = '\0'; mstream->callback(line, p, mstream->context); } if (mstream->istream.parent->eof) { if (mstream->istream.parent->stream_errno != 0) { mstream->istream.istream.stream_errno = mstream->istream.parent->stream_errno; } else { io_stream_set_error(&mstream->istream.iostream, "Metadata header is missing ending line at offset %"PRIuUOFF_T, mstream->istream.istream.v_offset); mstream->istream.istream.stream_errno = EINVAL; return -1; } mstream->istream.istream.eof = TRUE; return -1; } i_assert(!mstream->istream.parent->blocking); return 0; } static ssize_t i_stream_metawrap_read(struct istream_private *stream) { struct metawrap_istream *mstream = (struct metawrap_istream *)stream; int ret; i_stream_seek(stream->parent, mstream->start_offset + stream->istream.v_offset); if (mstream->in_metadata) { size_t prev_max_size = i_stream_get_max_buffer_size(stream->parent); i_stream_set_max_buffer_size(stream->parent, METAWRAP_MAX_METADATA_LINE_LEN); ret = metadata_header_read(mstream); i_stream_set_max_buffer_size(stream->parent, prev_max_size); i_assert(stream->istream.v_offset == 0); mstream->start_offset = stream->parent->v_offset; if (ret <= 0) return ret; /* this stream is kind of silently skipping over the metadata */ stream->abs_start_offset += mstream->start_offset; mstream->in_metadata = FALSE; if (mstream->pending_seek != 0) { i_stream_seek(&stream->istream, mstream->pending_seek); return i_stream_read(&stream->istream); } } /* after metadata header it's all just passthrough */ return i_stream_read_copy_from_parent(&stream->istream); } static void i_stream_metawrap_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct metawrap_istream *mstream = (struct metawrap_istream *)stream; if (!mstream->in_metadata) { /* already read through metadata. we can skip directly. */ stream->istream.v_offset = v_offset; mstream->pending_seek = 0; } else { /* we need to read through the metadata first */ mstream->pending_seek = v_offset; stream->istream.v_offset = 0; } stream->skip = stream->pos = 0; } static int i_stream_metawrap_stat(struct istream_private *stream, bool exact) { struct metawrap_istream *mstream = (struct metawrap_istream *)stream; const struct stat *st; int ret; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; if (mstream->in_metadata) { ret = i_stream_read(&stream->istream); if (ret < 0 && stream->istream.stream_errno != 0) return -1; if (ret == 0) { stream->statbuf.st_size = -1; return 0; } } i_assert((uoff_t)stream->statbuf.st_size >= mstream->start_offset); stream->statbuf.st_size -= mstream->start_offset; return 0; } struct istream * i_stream_create_metawrap(struct istream *input, metawrap_callback_t *callback, void *context) { struct metawrap_istream *mstream; mstream = i_new(struct metawrap_istream, 1); mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; mstream->istream.read = i_stream_metawrap_read; mstream->istream.seek = i_stream_metawrap_seek; mstream->istream.stat = input->seekable ? i_stream_metawrap_stat : NULL; /* we can't set abs_start_offset early enough so that it would get passed to our child istreams. */ mstream->istream.istream.readable_fd = FALSE; mstream->istream.istream.blocking = input->blocking; mstream->istream.istream.seekable = input->seekable; mstream->in_metadata = TRUE; mstream->callback = callback; mstream->context = context; return i_stream_create(&mstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib-fs/fs-sis-queue.c0000644000175000017500000001132713123174404014765 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "fs-sis-common.h" #define QUEUE_DIR_NAME "queue" struct sis_queue_fs { struct fs fs; char *queue_dir; }; struct sis_queue_fs_file { struct fs_file file; struct sis_queue_fs *fs; }; static struct fs *fs_sis_queue_alloc(void) { struct sis_queue_fs *fs; fs = i_new(struct sis_queue_fs, 1); fs->fs = fs_class_sis_queue; return &fs->fs; } static int fs_sis_queue_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; const char *p, *parent_name, *parent_args, *error; /* :[:] */ p = strchr(args, ':'); if (p == NULL || p[1] == '\0') { fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } fs->queue_dir = i_strdup_until(args, p); parent_name = p + 1; parent_args = strchr(parent_name, ':'); if (parent_args == NULL) parent_args = ""; else parent_name = t_strdup_until(parent_name, parent_args++); if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s", error); return -1; } return 0; } static void fs_sis_queue_deinit(struct fs *_fs) { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs->queue_dir); i_free(fs); } static struct fs_file * fs_sis_queue_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs; struct sis_queue_fs_file *file; file = i_new(struct sis_queue_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->fs = fs; if (mode == FS_OPEN_MODE_APPEND) fs_set_error(_fs, "APPEND mode not supported"); else file->file.parent = fs_file_init(_fs->parent, path, mode | flags); return &file->file; } static void fs_sis_queue_file_deinit(struct fs_file *_file) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; if (_file->parent != NULL) fs_file_deinit(&_file->parent); i_free(file->file.path); i_free(file); } static void fs_sis_queue_add(struct sis_queue_fs_file *file) { struct sis_queue_fs *fs = (struct sis_queue_fs *)file->file.fs; struct fs_file *queue_file; const char *fname, *path, *queue_path; path = fs_file_path(&file->file); fname = strrchr(path, '/'); if (fname != NULL) fname++; else fname = path; queue_path = t_strdup_printf("%s/%s", fs->queue_dir, fname); queue_file = fs_file_init(fs->fs.parent, queue_path, FS_OPEN_MODE_CREATE); if (fs_write(queue_file, "", 0) < 0 && errno != EEXIST) i_error("fs-sis-queue: %s", fs_file_last_error(queue_file)); fs_file_deinit(&queue_file); } static int fs_sis_queue_write(struct fs_file *_file, const void *data, size_t size) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; if (_file->parent == NULL) return -1; if (fs_write(_file->parent, data, size) < 0) return -1; T_BEGIN { fs_sis_queue_add(file); } T_END; return 0; } static void fs_sis_queue_write_stream(struct fs_file *_file) { i_assert(_file->output == NULL); if (_file->parent == NULL) { _file->output = o_stream_create_error_str(EINVAL, "%s", fs_file_last_error(_file)); } else { _file->output = fs_write_stream(_file->parent); } o_stream_set_name(_file->output, _file->path); } static int fs_sis_queue_write_stream_finish(struct fs_file *_file, bool success) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; if (!success) { if (_file->parent != NULL) fs_write_stream_abort_parent(_file, &_file->output); return -1; } if (fs_write_stream_finish(_file->parent, &_file->output) < 0) return -1; T_BEGIN { fs_sis_queue_add(file); } T_END; return 1; } static int fs_sis_queue_delete(struct fs_file *_file) { T_BEGIN { fs_sis_try_unlink_hash_file(_file->fs, _file->parent); } T_END; return fs_delete(_file->parent); } const struct fs fs_class_sis_queue = { .name = "sis-queue", .v = { fs_sis_queue_alloc, fs_sis_queue_init, fs_sis_queue_deinit, fs_wrapper_get_properties, fs_sis_queue_file_init, fs_sis_queue_file_deinit, fs_wrapper_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_wrapper_get_metadata, fs_wrapper_prefetch, fs_wrapper_read, fs_wrapper_read_stream, fs_sis_queue_write, fs_sis_queue_write_stream, fs_sis_queue_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_wrapper_stat, fs_wrapper_copy, fs_wrapper_rename, fs_sis_queue_delete, fs_wrapper_iter_init, NULL, NULL, NULL, fs_wrapper_get_nlinks, } }; dovecot-2.2.33.2/src/lib-fs/fs-sis-common.c0000644000175000017500000000264313123174404015132 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fs-sis-common.h" #include int fs_sis_path_parse(struct fs *fs, const char *path, const char **dir_r, const char **hash_r) { const char *fname, *p; fname = strrchr(path, '/'); if (fname == NULL) { *dir_r = "."; fname = path; } else { *dir_r = t_strdup_until(path, fname); fname++; } /* assume filename begins with "-" */ p = strchr(fname, '-'); if (p == NULL) { fs_set_error(fs, "open(%s) failed: " "Filenames must begin with '-'", path); return -1; } *hash_r = t_strdup_until(fname, p); return 0; } void fs_sis_try_unlink_hash_file(struct fs *sis_fs, struct fs_file *super_file) { struct fs_file *hash_file; struct stat st1, st2; const char *dir, *hash, *hash_path; if (fs_sis_path_parse(sis_fs, super_file->path, &dir, &hash) == 0 && fs_stat(super_file, &st1) == 0 && st1.st_nlink == 2) { /* this may be the last link. if hashes/ file is the same, delete it. */ hash_path = t_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash); hash_file = fs_file_init(super_file->fs, hash_path, FS_OPEN_MODE_READONLY); if (fs_stat(hash_file, &st2) == 0 && st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev)) { if (fs_delete(hash_file) < 0) i_error("%s", fs_last_error(hash_file->fs)); } fs_file_deinit(&hash_file); } } dovecot-2.2.33.2/src/lib-fs/Makefile.am0000644000175000017500000000235613165463624014344 00000000000000noinst_LTLIBRARIES = libfs.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DMODULE_DIR=\""$(moduledir)"\" libfs_la_SOURCES = \ fs-api.c \ fs-dict.c \ fs-metawrap.c \ fs-randomfail.c \ fs-posix.c \ fs-test.c \ fs-test-async.c \ fs-sis.c \ fs-sis-common.c \ fs-sis-queue.c \ fs-wrapper.c \ istream-fs-file.c \ istream-fs-stats.c \ istream-metawrap.c \ ostream-metawrap.c \ ostream-cmp.c headers = \ fs-api.h \ fs-api-private.h \ fs-sis-common.h \ fs-wrapper.h \ fs-test.h \ istream-fs-file.h \ istream-fs-stats.h \ istream-metawrap.h \ ostream-metawrap.h \ ostream-cmp.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) noinst_PROGRAMS = $(test_programs) test_programs = \ test-fs-metawrap test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-dict/libdict.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_libs = \ $(test_deps) \ $(MODULE_LIBS) test_fs_metawrap_SOURCES = test-fs-metawrap.c test_fs_metawrap_LDADD = $(test_libs) test_fs_metawrap_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-fs/fs-api.h0000644000175000017500000003555213165463624013644 00000000000000#ifndef FS_API_H #define FS_API_H struct stat; struct fs; struct fs_file; struct fs_lock; struct hash_method; /* Metadata with this prefix shouldn't actually be sent to storage. */ #define FS_METADATA_INTERNAL_PREFIX ":/X-Dovecot-fs-api-" /* fs_write*() may return a hex-encoded object ID after write is finished. This can be later on used to optimize reads by setting it before reading the file. */ #define FS_METADATA_OBJECTID FS_METADATA_INTERNAL_PREFIX"ObjectID" /* Calling this before fs_write_stream_finish() allows renaming the filename. This can be useful if you don't know the final filename before writing it (e.g. filename contains the file size). */ #define FS_METADATA_WRITE_FNAME FS_METADATA_INTERNAL_PREFIX"WriteFilename" /* Original path of the file. The path that's eventually visible to a fs backend may be something different, e.g. object ID. This allows the backend to still access the original path. */ #define FS_METADATA_ORIG_PATH FS_METADATA_INTERNAL_PREFIX"OrigPath" enum fs_properties { FS_PROPERTY_METADATA = 0x01, FS_PROPERTY_LOCKS = 0x02, FS_PROPERTY_FASTCOPY = 0x04, FS_PROPERTY_RENAME = 0x08, FS_PROPERTY_STAT = 0x10, /* Iteration is possible */ FS_PROPERTY_ITER = 0x20, /* Iteration always returns all of the files (instead of possibly slightly out of date view) */ FS_PROPERTY_RELIABLEITER= 0x40, /* Backend uses directories, which aren't automatically deleted when its children are deleted. */ FS_PROPERTY_DIRECTORIES = 0x80, FS_PROPERTY_WRITE_HASH_MD5 = 0x100, FS_PROPERTY_WRITE_HASH_SHA256 = 0x200, /* fs_copy() will copy the metadata if fs_set_metadata() hasn't been explicitly called. */ FS_PROPERTY_COPY_METADATA = 0x400, /* Backend support asynchronous file operations. */ FS_PROPERTY_ASYNC = 0x800, /* Backend supports FS_ITER_FLAG_OBJECTIDS. */ FS_PROPERTY_OBJECTIDS = 0x1000, /* fs_copy() is fast even when file's metadata is changed */ FS_PROPERTY_FASTCOPY_CHANGED_METADATA = 0x2000, }; enum fs_open_mode { /* Open only for reading, or fail with ENOENT if it doesn't exist */ FS_OPEN_MODE_READONLY, /* Create a new file, fail with EEXIST if it already exists */ FS_OPEN_MODE_CREATE, /* Create a new file with a new unique name. The generated name is a 128bit hex-encoded string. The fs_open()'s path parameter specifies only the directory where the file is created to. */ FS_OPEN_MODE_CREATE_UNIQUE_128, /* Create or replace a file */ FS_OPEN_MODE_REPLACE, /* Append to existing file, fail with ENOENT if it doesn't exist */ FS_OPEN_MODE_APPEND #define FS_OPEN_MODE_MASK 0x0f }; enum fs_open_flags { /* File is important and writing must call fsync() or have equivalent behavior. */ FS_OPEN_FLAG_FSYNC = 0x10, /* Asynchronous writes: fs_write() will fail with EAGAIN if it needs to be called again (the retries can use size=0). For streams fs_write_stream_finish() may request retrying with 0. Asynchronous reads: fs_read() will fail with EAGAIN if it's not finished and fs_read_stream() returns a nonblocking stream. */ FS_OPEN_FLAG_ASYNC = 0x20, /* fs_read_stream() must return a seekable input stream */ FS_OPEN_FLAG_SEEKABLE = 0x40, /* Backend should handle this file's operations immediately without any additional command queueing. The caller is assumed to be the one doing any rate limiting if needed. This flag can only be used with ASYNC flag, synchronous requests are never queued. */ FS_OPEN_FLAG_ASYNC_NOQUEUE = 0x80 }; enum fs_iter_flags { /* Iterate only directories, not files */ FS_ITER_FLAG_DIRS = 0x01, /* Request asynchronous iteration. */ FS_ITER_FLAG_ASYNC = 0x02, /* Instead of returning object names, return /. If this isn't supported, the is returned empty. The object IDs are always hex-encoded data. This flag can be used only if FS_PROPERTY_OBJECTIDS is enabled. */ FS_ITER_FLAG_OBJECTIDS = 0x04, /* Explicitly disable all caching for this iteration (if anything happens to be enabled). This should be used only in situations where the iteration is used to fix something that is broken, e.g. doveadm force-resync. */ FS_ITER_FLAG_NOCACHE = 0x08 }; enum fs_op { FS_OP_WAIT, FS_OP_METADATA, FS_OP_PREFETCH, FS_OP_READ, FS_OP_WRITE, FS_OP_LOCK, FS_OP_EXISTS, FS_OP_STAT, FS_OP_COPY, FS_OP_RENAME, FS_OP_DELETE, FS_OP_ITER, FS_OP_COUNT }; struct fs_settings { /* Username and session ID are mainly used for debugging/logging, but may also be useful for other purposes if they exist (they may be NULL). */ const char *username; const char *session_id; /* Dovecot instance's base_dir */ const char *base_dir; /* Directory where temporary files can be created at any time (e.g. /tmp or mail_temp_dir) */ const char *temp_dir; /* SSL client settings. */ const struct ssl_iostream_settings *ssl_client_set; /* Automatically try to rmdir() directories up to this path when deleting files. */ const char *root_path; /* When creating temporary files, use this prefix (to avoid conflicts with existing files). */ const char *temp_file_prefix; /* If the backend needs to do DNS lookups, use this dns_client for them. */ struct dns_client *dns_client; /* Enable debugging */ bool debug; /* Enable timing statistics */ bool enable_timing; }; struct fs_stats { /* Number of fs_prefetch() calls. Counted only if fs_read*() hasn't already been called for the file (which would be pretty pointless to do). */ unsigned int prefetch_count; /* Number of fs_read*() calls. Counted only if fs_prefetch() hasn't already been called for the file. */ unsigned int read_count; /* Number of fs_lookup_metadata() calls. Counted only if neither fs_read*() nor fs_prefetch() has been called for the file. */ unsigned int lookup_metadata_count; /* Number of fs_stat() calls. Counted only if none of the above has been called (because the stat result should be cached). */ unsigned int stat_count; /* Number of fs_write*() calls. */ unsigned int write_count; /* Number of fs_exists() calls, which actually went to the backend instead of being handled by fs_stat() call due to fs_exists() not being implemented. */ unsigned int exists_count; /* Number of fs_delete() calls. */ unsigned int delete_count; /* Number of fs_copy() calls. If backend doesn't implement copying operation but falls back to regular read+write instead, this count isn't increased but the read+write counters are. */ unsigned int copy_count; /* Number of fs_rename() calls. */ unsigned int rename_count; /* Number of fs_iter_init() calls. */ unsigned int iter_count; /* Number of bytes written by fs_write*() calls. */ uint64_t write_bytes; /* Cumulative sum of usecs spent on calls - set only if fs_settings.enable_timing=TRUE */ struct timing *timings[FS_OP_COUNT]; }; struct fs_metadata { const char *key; const char *value; }; ARRAY_DEFINE_TYPE(fs_metadata, struct fs_metadata); typedef void fs_file_async_callback_t(void *context); int fs_init(const char *driver, const char *args, const struct fs_settings *set, struct fs **fs_r, const char **error_r); /* same as fs_unref() */ void fs_deinit(struct fs **fs); void fs_ref(struct fs *fs); void fs_unref(struct fs **fs); /* Returns the parent filesystem (if this is a wrapper fs) or NULL if there's no parent. */ struct fs *fs_get_parent(struct fs *fs); /* Returns the filesystem's driver name. */ const char *fs_get_driver(struct fs *fs); /* Returns the root fs's driver name (bypassing all wrapper fses) */ const char *fs_get_root_driver(struct fs *fs); struct fs_file *fs_file_init(struct fs *fs, const char *path, int mode_flags); void fs_file_deinit(struct fs_file **file); /* If the file has an input streams open, close them. */ void fs_file_close(struct fs_file *file); /* Return properties supported by backend. */ enum fs_properties fs_get_properties(struct fs *fs); /* Add/replace metadata when saving a file. This makes sense only when the file is being created/replaced. */ void fs_set_metadata(struct fs_file *file, const char *key, const char *value); /* Return file's all metadata. */ int fs_get_metadata(struct fs_file *file, const ARRAY_TYPE(fs_metadata) **metadata_r); /* Wrapper to fs_get_metadata() to lookup a specific key. Returns 1 if value_r is set, 0 if key wasn't found, -1 if error. */ int fs_lookup_metadata(struct fs_file *file, const char *key, const char **value_r); /* Returns the path given to fs_open(). If file was opened with FS_OPEN_MODE_CREATE_UNIQUE_128 and the write has already finished, return the path including the generated filename. */ const char *fs_file_path(struct fs_file *file); /* Returns the file's fs. */ struct fs *fs_file_fs(struct fs_file *file); /* Return the error message for the last failed operation. */ const char *fs_last_error(struct fs *fs); /* Convenience function for the above. Errors aren't preserved across files. */ const char *fs_file_last_error(struct fs_file *file); /* Try to asynchronously prefetch file into memory. Returns TRUE if file is already in memory (i.e. caller should handle this file before prefetching more), FALSE if not. The length is a hint of how much the caller expects to read, but it may be more or less (0=whole file). */ bool fs_prefetch(struct fs_file *file, uoff_t length); /* Returns >0 if something was read, -1 if error (errno is set). */ ssize_t fs_read(struct fs_file *file, void *buf, size_t size); /* Returns a stream for reading from file. Multiple streams can be opened, and caller must destroy the streams before closing the file. */ struct istream *fs_read_stream(struct fs_file *file, size_t max_buffer_size); /* Returns 0 if ok, -1 if error (errno is set). Note: With CREATE/REPLACE mode files you can call fs_write() only once, the file creation is finished by it. CREATE can return EEXIST here, if the destination file was already created. With APPEND mode each fs_write() atomically appends the given data to file. */ int fs_write(struct fs_file *file, const void *data, size_t size); /* Write to file via output stream. The stream will be destroyed by fs_write_stream_finish/abort. The returned ostream is already corked and it doesn't need to be uncorked. */ struct ostream *fs_write_stream(struct fs_file *file); /* Finish writing via stream, calling also o_stream_nfinish() on the stream and handling any pending errors. The file will be created/replaced/appended only after this call, same as with fs_write(). Anything written to the stream won't be visible earlier. Returns 1 if ok, 0 if async write isn't finished yet (retry calling fs_write_stream_finish_async()), -1 if error */ int fs_write_stream_finish(struct fs_file *file, struct ostream **output); int fs_write_stream_finish_async(struct fs_file *file); /* Abort writing via stream. Anything written to the stream is discarded. o_stream_ignore_last_errors() is called on the output stream so the caller doesn't need to do it. This must not be called after fs_write_stream_finish(), i.e. it can't be used to abort a pending async write. */ void fs_write_stream_abort(struct fs_file *file, struct ostream **output); void fs_write_stream_abort_error(struct fs_file *file, struct ostream **output, const char *error_fmt, ...) ATTR_FORMAT(3, 4); /* Set a hash to the following write. The storage can then verify that the input data matches the specified hash, or fail if it doesn't. Typically implemented by Content-MD5 header. */ void fs_write_set_hash(struct fs_file *file, const struct hash_method *method, const void *digest); /* Call the specified callback whenever the file can be read/written to. May call the callback immediately. */ void fs_file_set_async_callback(struct fs_file *file, fs_file_async_callback_t *callback, void *context); /* Wait until some file can be read/written to more before returning. It's an error to call this when there are no pending async operations. Returns 0 if ok, -1 if timed out. */ int fs_wait_async(struct fs *fs); /* Switch the fs to the current ioloop. This can be used to do fs_wait_async() among other IO work. Returns TRUE if there is actually some work that can be waited on. */ bool fs_switch_ioloop(struct fs *fs) ATTR_NOWARN_UNUSED_RESULT; /* Returns 1 if file exists, 0 if not, -1 if error occurred. */ int fs_exists(struct fs_file *file); /* Delete a file. Returns 0 if file was actually deleted by us, -1 if error. */ int fs_delete(struct fs_file *file); /* Returns 0 if ok, -1 if error occurred (e.g. errno=ENOENT). All fs backends may not support all stat fields. */ int fs_stat(struct fs_file *file, struct stat *st_r); /* Get number of links to the file. This is the same as using fs_stat()'s st_nlinks field, except not all backends support returning it via fs_stat(). Returns 0 if ok, -1 if error occurred. */ int fs_get_nlinks(struct fs_file *file, nlink_t *nlinks_r); /* Copy an object with possibly updated metadata. Destination parent directories are created automatically. Returns 0 if ok, -1 if error occurred. */ int fs_copy(struct fs_file *src, struct fs_file *dest); /* Try to finish asynchronous fs_copy(). Returns the same as fs_copy(). */ int fs_copy_finish_async(struct fs_file *dest); /* Atomically rename a file. Destination parent directories are created automatically. Returns 0 if ok, -1 if error occurred. */ int fs_rename(struct fs_file *src, struct fs_file *dest); /* Exclusively lock a file. If file is already locked, wait for it for given number of seconds (0 = fail immediately). Returns 1 if locked, 0 if wait timed out, -1 if error. */ int fs_lock(struct fs_file *file, unsigned int secs, struct fs_lock **lock_r); void fs_unlock(struct fs_lock **lock); /* Iterate through all files or directories in the given directory. Doesn't recurse to child directories. It's not an error to iterate a nonexistent directory. */ struct fs_iter * fs_iter_init(struct fs *fs, const char *path, enum fs_iter_flags flags); /* Returns 0 if ok, -1 if iteration failed. */ int fs_iter_deinit(struct fs_iter **iter); /* Returns the next filename. */ const char *fs_iter_next(struct fs_iter *iter); /* For asynchronous iterations: Specify the callback that is called whenever there's more data available for reading. */ void fs_iter_set_async_callback(struct fs_iter *iter, fs_file_async_callback_t *callback, void *context); /* For asynchronous iterations: If fs_iter_next() returns NULL, use this function to determine if you should wait for more data or finish up. */ bool fs_iter_have_more(struct fs_iter *iter); /* Return the filesystem's fs_stats. Note that each wrapper filesystem keeps track of its own fs_stats calls. You can use fs_get_parent() to get to the filesystem whose stats you want to see. */ const struct fs_stats *fs_get_stats(struct fs *fs); /* Helper functions to count number of usecs for read/write operations. */ uint64_t fs_stats_get_read_usecs(const struct fs_stats *stats); uint64_t fs_stats_get_write_usecs(const struct fs_stats *stats); #endif dovecot-2.2.33.2/src/replication/0002755000175000017500000000000013172375612013516 500000000000000dovecot-2.2.33.2/src/replication/Makefile.in0000644000175000017500000005065513172375575015524 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/replication ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = aggregator replicator noinst_HEADERS = \ replication-common.h all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/replication/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/replication/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(HEADERS) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/replication/replication-common.h0000644000175000017500000000220713123174404017375 00000000000000#ifndef REPLICATION_COMMON_H #define REPLICATION_COMMON_H enum replication_priority { /* user is fully replicated, as far as we know */ REPLICATION_PRIORITY_NONE = 0, /* flag changes, expunges, etc. */ REPLICATION_PRIORITY_LOW, /* new emails */ REPLICATION_PRIORITY_HIGH, /* synchronously wait for new emails to be replicated */ REPLICATION_PRIORITY_SYNC }; static inline const char * replicator_priority_to_str(enum replication_priority priority) { switch (priority) { case REPLICATION_PRIORITY_NONE: return "none"; case REPLICATION_PRIORITY_LOW: return "low"; case REPLICATION_PRIORITY_HIGH: return "high"; case REPLICATION_PRIORITY_SYNC: return "sync"; } i_unreached(); } static inline int replication_priority_parse(const char *str, enum replication_priority *priority_r) { if (strcmp(str, "none") == 0) *priority_r = REPLICATION_PRIORITY_NONE; else if (strcmp(str, "low") == 0) *priority_r = REPLICATION_PRIORITY_LOW; else if (strcmp(str, "high") == 0) *priority_r = REPLICATION_PRIORITY_HIGH; else if (strcmp(str, "sync") == 0) *priority_r = REPLICATION_PRIORITY_SYNC; else return -1; return 0; } #endif dovecot-2.2.33.2/src/replication/replicator/0002755000175000017500000000000013172375612015662 500000000000000dovecot-2.2.33.2/src/replication/replicator/Makefile.in0000644000175000017500000005606313172375575017667 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = replicator$(EXEEXT) subdir = src/replication/replicator ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_replicator_OBJECTS = doveadm-connection.$(OBJEXT) \ dsync-client.$(OBJEXT) replicator.$(OBJEXT) \ replicator-brain.$(OBJEXT) replicator-queue.$(OBJEXT) \ replicator-queue-auth.$(OBJEXT) replicator-settings.$(OBJEXT) \ notify-connection.$(OBJEXT) replicator_OBJECTS = $(am_replicator_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = replicator_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(replicator_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(replicator_SOURCES) DIST_SOURCES = $(replicator_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/replication \ -DPKG_STATEDIR=\""$(statedir)"\" replicator_LDFLAGS = -export-dynamic replicator_LDADD = $(LIBDOVECOT) replicator_DEPENDENCIES = $(LIBDOVECOT_DEPS) replicator_SOURCES = \ doveadm-connection.c \ dsync-client.c \ replicator.c \ replicator-brain.c \ replicator-queue.c \ replicator-queue-auth.c \ replicator-settings.c \ notify-connection.c noinst_HEADERS = \ doveadm-connection.h \ dsync-client.h \ replicator-brain.h \ replicator-queue.h \ replicator-settings.h \ notify-connection.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/replication/replicator/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/replication/replicator/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list replicator$(EXEEXT): $(replicator_OBJECTS) $(replicator_DEPENDENCIES) $(EXTRA_replicator_DEPENDENCIES) @rm -f replicator$(EXEEXT) $(AM_V_CCLD)$(replicator_LINK) $(replicator_OBJECTS) $(replicator_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator-brain.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator-queue-auth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator-queue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/replication/replicator/replicator-brain.h0000644000175000017500000000112313123174404021173 00000000000000#ifndef REPLICATOR_BRAIN_H #define REPLICATOR_BRAIN_H struct replicator_settings; struct replicator_queue; struct replicator_brain * replicator_brain_init(struct replicator_queue *queue, const struct replicator_settings *set); void replicator_brain_deinit(struct replicator_brain **brain); struct replicator_queue * replicator_brain_get_queue(struct replicator_brain *brain); const struct replicator_settings * replicator_brain_get_settings(struct replicator_brain *brain); const ARRAY_TYPE(dsync_client) * replicator_brain_get_dsync_clients(struct replicator_brain *brain); #endif dovecot-2.2.33.2/src/replication/replicator/doveadm-connection.c0000644000175000017500000002331413165463624021526 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "connection.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "wildcard-match.h" #include "master-service.h" #include "replicator-brain.h" #include "replicator-queue.h" #include "replicator-settings.h" #include "dsync-client.h" #include "doveadm-connection.h" #include #define REPLICATOR_DOVEADM_MAJOR_VERSION 1 #define REPLICATOR_DOVEADM_MINOR_VERSION 0 struct doveadm_connection { struct connection conn; struct replicator_brain *brain; }; static struct connection_list *doveadm_connections; static int client_input_status_overview(struct doveadm_connection *client) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); struct replicator_queue_iter *iter; struct replicator_user *user; enum replication_priority priority; unsigned int pending_counts[REPLICATION_PRIORITY_SYNC+1]; unsigned int user_count, next_secs, pending_failed_count; unsigned int pending_full_resync_count, waiting_failed_count; string_t *str = t_str_new(256); memset(pending_counts, 0, sizeof(pending_counts)); pending_failed_count = 0; waiting_failed_count = 0; pending_full_resync_count = 0; user_count = 0; iter = replicator_queue_iter_init(queue); while ((user = replicator_queue_iter_next(iter)) != NULL) { if (user->priority != REPLICATION_PRIORITY_NONE) pending_counts[user->priority]++; else if (replicator_queue_want_sync_now(queue, user, &next_secs)) { if (user->last_sync_failed) pending_failed_count++; else pending_full_resync_count++; } else { if (user->last_sync_failed) waiting_failed_count++; } user_count++; } replicator_queue_iter_deinit(&iter); for (priority = REPLICATION_PRIORITY_SYNC; priority > 0; priority--) { str_printfa(str, "Queued '%s' requests\t%u\n", replicator_priority_to_str(priority), pending_counts[priority]); } str_printfa(str, "Queued 'failed' requests\t%u\n", pending_failed_count); str_printfa(str, "Queued 'full resync' requests\t%u\n", pending_full_resync_count); str_printfa(str, "Waiting 'failed' requests\t%u\n", waiting_failed_count); str_printfa(str, "Total number of known users\t%u\n", user_count); str_append_c(str, '\n'); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); return 0; } static int client_input_status(struct doveadm_connection *client, const char *const *args) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); struct replicator_queue_iter *iter; struct replicator_user *user; const char *mask = args[0]; string_t *str = t_str_new(128); if (mask == NULL) return client_input_status_overview(client); iter = replicator_queue_iter_init(queue); while ((user = replicator_queue_iter_next(iter)) != NULL) { if (!wildcard_match(user->username, mask)) continue; str_truncate(str, 0); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); str_append(str, replicator_priority_to_str(user->priority)); str_printfa(str, "\t%lld\t%lld\t%d\t%lld\n", (long long)user->last_fast_sync, (long long)user->last_full_sync, user->last_sync_failed, (long long)user->last_successful_sync); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); } replicator_queue_iter_deinit(&iter); o_stream_nsend(client->conn.output, "\n", 1); return 0; } static int client_input_status_dsyncs(struct doveadm_connection *client) { string_t *str = t_str_new(256); const ARRAY_TYPE(dsync_client) *clients; struct dsync_client *const *clientp; const char *username; clients = replicator_brain_get_dsync_clients(client->brain); array_foreach(clients, clientp) { username = dsync_client_get_username(*clientp); if (username != NULL) { str_append_tabescaped(str, username); str_append_c(str, '\t'); switch (dsync_client_get_type(*clientp)) { case DSYNC_TYPE_FULL: str_append(str, "full"); break; case DSYNC_TYPE_NORMAL: str_append(str, "normal"); break; case DSYNC_TYPE_INCREMENTAL: str_append(str, "incremental"); break; } } else { str_append(str, "\t-"); } str_append_c(str, '\t'); str_append_tabescaped(str, dsync_client_get_state(*clientp)); str_append_c(str, '\n'); } str_append_c(str, '\n'); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); return 0; } static int client_input_replicate(struct doveadm_connection *client, const char *const *args) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); struct replicator_queue_iter *iter; struct replicator_user *user; const char *usermask; enum replication_priority priority; unsigned int match_count; bool full; /* | */ if (str_array_length(args) != 3) { i_error("%s: REPLICATE: Invalid parameters", client->conn.name); return -1; } if (replication_priority_parse(args[0], &priority) < 0) { o_stream_nsend_str(client->conn.output, "-Invalid priority\n"); return 0; } full = strchr(args[1], 'f') != NULL; usermask = args[2]; if (strchr(usermask, '*') == NULL && strchr(usermask, '?') == NULL) { user = replicator_queue_add(queue, usermask, priority); if (full) user->force_full_sync = TRUE; o_stream_nsend_str(client->conn.output, "+1\n"); return 0; } match_count = 0; iter = replicator_queue_iter_init(queue); while ((user = replicator_queue_iter_next(iter)) != NULL) { if (!wildcard_match(user->username, usermask)) continue; user = replicator_queue_add(queue, user->username, priority); if (full) user->force_full_sync = TRUE; match_count++; } replicator_queue_iter_deinit(&iter); o_stream_nsend_str(client->conn.output, t_strdup_printf("+%u\n", match_count)); return 0; } static int client_input_add(struct doveadm_connection *client, const char *const *args) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); const struct replicator_settings *set = replicator_brain_get_settings(client->brain); /* */ if (str_array_length(args) != 1) { i_error("%s: ADD: Invalid parameters", client->conn.name); return -1; } if (strchr(args[0], '*') == NULL && strchr(args[0], '?') == NULL) { (void)replicator_queue_add(queue, args[0], REPLICATION_PRIORITY_NONE); } else { replicator_queue_add_auth_users(queue, set->auth_socket_path, args[0], ioloop_time); } o_stream_nsend_str(client->conn.output, "+\n"); return 0; } static int client_input_remove(struct doveadm_connection *client, const char *const *args) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); struct replicator_user *user; /* */ if (str_array_length(args) != 1) { i_error("%s: REMOVE: Invalid parameters", client->conn.name); return -1; } user = replicator_queue_lookup(queue, args[0]); if (user == NULL) o_stream_nsend_str(client->conn.output, "-User not found\n"); else { replicator_queue_remove(queue, &user); o_stream_nsend_str(client->conn.output, "+\n"); } return 0; } static int client_input_notify(struct doveadm_connection *client, const char *const *args) { struct replicator_queue *queue = replicator_brain_get_queue(client->brain); struct replicator_user *user; /* */ if (str_array_length(args) < 3) { i_error("%s: NOTIFY: Invalid parameters", client->conn.name); return -1; } user = replicator_queue_add(queue, args[0], REPLICATION_PRIORITY_NONE); if (args[1][0] == 'f') user->last_full_sync = ioloop_time; user->last_fast_sync = ioloop_time; user->last_update = ioloop_time; if (args[2][0] != '\0') { i_free(user->state); user->state = i_strdup(args[2]); } o_stream_nsend_str(client->conn.output, "+\n"); return 0; } static int client_input_args(struct connection *conn, const char *const *args) { struct doveadm_connection *client = (struct doveadm_connection *)conn; const char *cmd = args[0]; if (cmd == NULL) { i_error("%s: Empty command", conn->name); return 0; } args++; if (strcmp(cmd, "STATUS") == 0) return client_input_status(client, args); else if (strcmp(cmd, "STATUS-DSYNC") == 0) return client_input_status_dsyncs(client); else if (strcmp(cmd, "REPLICATE") == 0) return client_input_replicate(client, args); else if (strcmp(cmd, "ADD") == 0) return client_input_add(client, args); else if (strcmp(cmd, "REMOVE") == 0) return client_input_remove(client, args); else if (strcmp(cmd, "NOTIFY") == 0) return client_input_notify(client, args); i_error("%s: Unknown command: %s", conn->name, cmd); return -1; } static void client_destroy(struct connection *conn) { struct doveadm_connection *client = (struct doveadm_connection *)conn; connection_deinit(&client->conn); i_free(client); master_service_client_connection_destroyed(master_service); } void doveadm_connection_create(struct replicator_brain *brain, int fd) { struct doveadm_connection *client; client = i_new(struct doveadm_connection, 1); client->brain = brain; connection_init_server(doveadm_connections, &client->conn, "(doveadm client)", fd, fd); } static struct connection_settings doveadm_conn_set = { .service_name_in = "replicator-doveadm-client", .service_name_out = "replicator-doveadm-server", .major_version = REPLICATOR_DOVEADM_MAJOR_VERSION, .minor_version = REPLICATOR_DOVEADM_MINOR_VERSION, .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs doveadm_conn_vfuncs = { .destroy = client_destroy, .input_args = client_input_args }; void doveadm_connections_init(void) { doveadm_connections = connection_list_init(&doveadm_conn_set, &doveadm_conn_vfuncs); } void doveadm_connections_deinit(void) { connection_list_deinit(&doveadm_connections); } dovecot-2.2.33.2/src/replication/replicator/replicator-queue.c0000644000175000017500000003023513165463624021240 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "hash.h" #include "replicator-queue.h" #include #include struct replicator_sync_lookup { struct replicator_user *user; replicator_sync_callback_t *callback; void *context; bool wait_for_next_push; }; struct replicator_queue { struct priorityq *user_queue; /* username => struct replicator_user* */ HASH_TABLE(char *, struct replicator_user *) user_hash; ARRAY(struct replicator_sync_lookup) sync_lookups; unsigned int full_sync_interval; unsigned int failure_resync_interval; void (*change_callback)(void *context); void *change_context; }; struct replicator_queue_iter { struct replicator_queue *queue; struct hash_iterate_context *iter; }; static int user_priority_cmp(const void *p1, const void *p2) { const struct replicator_user *user1 = p1, *user2 = p2; if (user1->priority > user2->priority) return -1; if (user1->priority < user2->priority) return 1; if (user1->priority != REPLICATION_PRIORITY_NONE) { /* there is something to replicate */ if (user1->last_fast_sync < user2->last_fast_sync) return -1; if (user1->last_fast_sync > user2->last_fast_sync) return 1; } else if (user1->last_sync_failed != user2->last_sync_failed) { /* resync failures first */ if (user1->last_sync_failed) return -1; else return 1; } else if (user1->last_sync_failed) { /* both have failed. resync failures with fast-sync timestamp */ if (user1->last_fast_sync < user2->last_fast_sync) return -1; if (user1->last_fast_sync > user2->last_fast_sync) return 1; } else { /* nothing to replicate, but do still periodic full syncs */ if (user1->last_full_sync < user2->last_full_sync) return -1; if (user1->last_full_sync > user2->last_full_sync) return 1; } return 0; } struct replicator_queue * replicator_queue_init(unsigned int full_sync_interval, unsigned int failure_resync_interval) { struct replicator_queue *queue; queue = i_new(struct replicator_queue, 1); queue->full_sync_interval = full_sync_interval; queue->failure_resync_interval = failure_resync_interval; queue->user_queue = priorityq_init(user_priority_cmp, 1024); hash_table_create(&queue->user_hash, default_pool, 1024, str_hash, strcmp); i_array_init(&queue->sync_lookups, 32); return queue; } void replicator_queue_deinit(struct replicator_queue **_queue) { struct replicator_queue *queue = *_queue; struct priorityq_item *item; *_queue = NULL; queue->change_callback = NULL; while ((item = priorityq_pop(queue->user_queue)) != NULL) { struct replicator_user *user = (struct replicator_user *)item; user->popped = TRUE; replicator_queue_remove(queue, &user); } priorityq_deinit(&queue->user_queue); hash_table_destroy(&queue->user_hash); i_assert(array_count(&queue->sync_lookups) == 0); array_free(&queue->sync_lookups); i_free(queue); } void replicator_queue_set_change_callback(struct replicator_queue *queue, void (*callback)(void *context), void *context) { queue->change_callback = callback; queue->change_context = context; } struct replicator_user * replicator_queue_lookup(struct replicator_queue *queue, const char *username) { return hash_table_lookup(queue->user_hash, username); } static struct replicator_user * replicator_queue_add_int(struct replicator_queue *queue, const char *username, enum replication_priority priority) { struct replicator_user *user; user = replicator_queue_lookup(queue, username); if (user == NULL) { user = i_new(struct replicator_user, 1); user->username = i_strdup(username); hash_table_insert(queue->user_hash, user->username, user); } else { if (user->priority > priority) { /* user already has a higher priority than this */ return user; } if (!user->popped) priorityq_remove(queue->user_queue, &user->item); } user->priority = priority; user->last_update = ioloop_time; if (!user->popped) priorityq_add(queue->user_queue, &user->item); return user; } struct replicator_user * replicator_queue_add(struct replicator_queue *queue, const char *username, enum replication_priority priority) { struct replicator_user *user; user = replicator_queue_add_int(queue, username, priority); if (queue->change_callback != NULL) queue->change_callback(queue->change_context); return user; } void replicator_queue_add_sync(struct replicator_queue *queue, const char *username, replicator_sync_callback_t *callback, void *context) { struct replicator_user *user; struct replicator_sync_lookup *lookup; user = replicator_queue_add_int(queue, username, REPLICATION_PRIORITY_SYNC); lookup = array_append_space(&queue->sync_lookups); lookup->user = user; lookup->callback = callback; lookup->context = context; lookup->wait_for_next_push = user->popped; if (queue->change_callback != NULL) queue->change_callback(queue->change_context); } void replicator_queue_remove(struct replicator_queue *queue, struct replicator_user **_user) { struct replicator_user *user = *_user; *_user = NULL; if (!user->popped) priorityq_remove(queue->user_queue, &user->item); hash_table_remove(queue->user_hash, user->username); i_free(user->state); i_free(user->username); i_free(user); if (queue->change_callback != NULL) queue->change_callback(queue->change_context); } bool replicator_queue_want_sync_now(struct replicator_queue *queue, struct replicator_user *user, unsigned int *next_secs_r) { time_t next_sync; if (user->priority != REPLICATION_PRIORITY_NONE) return TRUE; if (user->last_sync_failed) { next_sync = user->last_fast_sync + queue->failure_resync_interval; } else { next_sync = user->last_full_sync + queue->full_sync_interval; } if (next_sync <= ioloop_time) return TRUE; *next_secs_r = next_sync - ioloop_time; return FALSE; } struct replicator_user * replicator_queue_pop(struct replicator_queue *queue, unsigned int *next_secs_r) { struct priorityq_item *item; struct replicator_user *user; item = priorityq_peek(queue->user_queue); if (item == NULL) { /* no users defined. we shouldn't normally get here */ *next_secs_r = 3600; return NULL; } user = (struct replicator_user *)item; if (!replicator_queue_want_sync_now(queue, user, next_secs_r)) { /* we don't want to sync the user yet */ return NULL; } priorityq_remove(queue->user_queue, &user->item); user->popped = TRUE; return user; } static void replicator_queue_handle_sync_lookups(struct replicator_queue *queue, struct replicator_user *user) { struct replicator_sync_lookup *lookups; ARRAY(struct replicator_sync_lookup) callbacks; unsigned int i, count; bool success = !user->last_sync_failed; t_array_init(&callbacks, 8); lookups = array_get_modifiable(&queue->sync_lookups, &count); for (i = 0; i < count; ) { if (lookups[i].user != user) i++; else if (lookups[i].wait_for_next_push) { /* another sync request came while user was being replicated */ i_assert(user->priority == REPLICATION_PRIORITY_SYNC); lookups[i].wait_for_next_push = FALSE; i++; } else { array_append(&callbacks, &lookups[i], 1); array_delete(&queue->sync_lookups, i, 1); } } array_foreach_modifiable(&callbacks, lookups) lookups->callback(success, lookups->context); } void replicator_queue_push(struct replicator_queue *queue, struct replicator_user *user) { i_assert(user->popped); priorityq_add(queue->user_queue, &user->item); user->popped = FALSE; T_BEGIN { replicator_queue_handle_sync_lookups(queue, user); } T_END; } static int replicator_queue_import_line(struct replicator_queue *queue, const char *line) { const char *const *args, *username, *state; unsigned int priority; struct replicator_user *user, tmp_user; /* */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < 7) return -1; i_zero(&tmp_user); username = args[0]; state = t_strdup_noconst(args[6]); if (username[0] == '\0' || str_to_uint(args[1], &priority) < 0 || str_to_time(args[2], &tmp_user.last_update) < 0 || str_to_time(args[3], &tmp_user.last_fast_sync) < 0 || str_to_time(args[4], &tmp_user.last_full_sync) < 0) return -1; tmp_user.priority = priority; tmp_user.last_sync_failed = args[5][0] != '0'; if (str_array_length(args) >= 8) { if (str_to_time(args[7], &tmp_user.last_successful_sync) < 0) return -1; } else { tmp_user.last_successful_sync = 0; /* On-disk format didn't have this yet */ } user = hash_table_lookup(queue->user_hash, username); if (user != NULL) { if (user->last_update > tmp_user.last_update) { /* we already have a newer state */ return 0; } if (user->last_update == tmp_user.last_update) { /* either one of these could be newer. use the one with higher priority. */ if (user->priority > tmp_user.priority) return 0; } } user = replicator_queue_add(queue, username, tmp_user.priority); user->last_update = tmp_user.last_update; user->last_fast_sync = tmp_user.last_fast_sync; user->last_full_sync = tmp_user.last_full_sync; user->last_successful_sync = tmp_user.last_successful_sync; user->last_sync_failed = tmp_user.last_sync_failed; i_free(user->state); user->state = i_strdup(state); return 0; } int replicator_queue_import(struct replicator_queue *queue, const char *path) { struct istream *input; const char *line; int fd, ret = 0; fd = open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", path); return -1; } input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { T_BEGIN { ret = replicator_queue_import_line(queue, line); } T_END; if (ret < 0) { i_error("Corrupted replicator record in %s: %s", path, line); break; } } if (input->stream_errno != 0) { i_error("read(%s) failed: %s", path, i_stream_get_error(input)); ret = -1; } i_stream_destroy(&input); return ret; } static void replicator_queue_export_user(struct replicator_user *user, string_t *str) { str_append_tabescaped(str, user->username); str_printfa(str, "\t%d\t%lld\t%lld\t%lld\t%d\t", (int)user->priority, (long long)user->last_update, (long long)user->last_fast_sync, (long long)user->last_full_sync, user->last_sync_failed); if (user->state != NULL) str_append_tabescaped(str, user->state); str_printfa(str, "\t%lld\n", (long long)user->last_successful_sync); } int replicator_queue_export(struct replicator_queue *queue, const char *path) { struct replicator_queue_iter *iter; struct replicator_user *user; struct ostream *output; string_t *str; int fd, ret = 0; fd = creat(path, 0600); if (fd == -1) { i_error("creat(%s) failed: %m", path); return -1; } output = o_stream_create_fd_file_autoclose(&fd, 0); o_stream_cork(output); str = t_str_new(128); iter = replicator_queue_iter_init(queue); while ((user = replicator_queue_iter_next(iter)) != NULL) { str_truncate(str, 0); replicator_queue_export_user(user, str); if (o_stream_send(output, str_data(str), str_len(str)) < 0) break; } replicator_queue_iter_deinit(&iter); if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", path, o_stream_get_error(output)); ret = -1; } o_stream_destroy(&output); return ret; } struct replicator_queue_iter * replicator_queue_iter_init(struct replicator_queue *queue) { struct replicator_queue_iter *iter; iter = i_new(struct replicator_queue_iter, 1); iter->queue = queue; iter->iter = hash_table_iterate_init(queue->user_hash); return iter; } struct replicator_user * replicator_queue_iter_next(struct replicator_queue_iter *iter) { struct replicator_user *user; char *username; if (!hash_table_iterate(iter->iter, iter->queue->user_hash, &username, &user)) return NULL; return user; } void replicator_queue_iter_deinit(struct replicator_queue_iter **_iter) { struct replicator_queue_iter *iter = *_iter; *_iter = NULL; hash_table_iterate_deinit(&iter->iter); i_free(iter); } dovecot-2.2.33.2/src/replication/replicator/notify-connection.c0000644000175000017500000001122513165463624021415 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "master-service.h" #include "replicator-queue.h" #include "notify-connection.h" #include #define MAX_INBUF_SIZE (1024*64) #define NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION 1 #define NOTIFY_CLIENT_PROTOCOL_MINOR_VERSION 0 struct notify_connection { struct notify_connection *prev, *next; int refcount; int fd; struct io *io; struct istream *input; struct ostream *output; struct replicator_queue *queue; unsigned int version_received:1; unsigned int destroyed:1; }; struct notify_sync_request { struct notify_connection *conn; unsigned int id; }; static struct notify_connection *connections; static void notify_connection_destroy(struct notify_connection *conn); static void notify_sync_callback(bool success, void *context) { struct notify_sync_request *request = context; o_stream_nsend_str(request->conn->output, t_strdup_printf( "%c\t%u\n", success ? '+' : '-', request->id)); notify_connection_unref(&request->conn); i_free(request); } static int notify_connection_input_line(struct notify_connection *conn, const char *line) { struct notify_sync_request *request; const char *const *args; enum replication_priority priority; unsigned int id; /* U \t \t [\t ] */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < 2) { i_error("notify client sent invalid input: %s", line); return -1; } if (strcmp(args[0], "U") != 0) { i_error("notify client sent unknown command: %s", args[0]); return -1; } if (replication_priority_parse(args[2], &priority) < 0) { i_error("notify client sent invalid priority: %s", args[2]); return -1; } if (priority != REPLICATION_PRIORITY_SYNC) (void)replicator_queue_add(conn->queue, args[1], priority); else if (args[3] == NULL || str_to_uint(args[3], &id) < 0) { i_error("notify client sent invalid sync id: %s", line); return -1; } else { request = i_new(struct notify_sync_request, 1); request->conn = conn; request->id = id; notify_connection_ref(conn); replicator_queue_add_sync(conn->queue, args[1], notify_sync_callback, request); } return 0; } static void notify_connection_input(struct notify_connection *conn) { const char *line; int ret; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Client connection sent too much data"); notify_connection_destroy(conn); return; case -1: notify_connection_destroy(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, "replicator-notify", NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Notify client not compatible with this server " "(mixed old and new binaries?)"); notify_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = notify_connection_input_line(conn, line); } T_END; if (ret < 0) { notify_connection_destroy(conn); break; } } } struct notify_connection * notify_connection_create(int fd, struct replicator_queue *queue) { struct notify_connection *conn; i_assert(fd >= 0); conn = i_new(struct notify_connection, 1); conn->refcount = 1; conn->queue = queue; conn->fd = fd; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); conn->io = io_add(fd, IO_READ, notify_connection_input, conn); conn->queue = queue; DLLIST_PREPEND(&connections, conn); return conn; } static void notify_connection_destroy(struct notify_connection *conn) { if (conn->destroyed) return; conn->destroyed = TRUE; DLLIST_REMOVE(&connections, conn); io_remove(&conn->io); i_stream_close(conn->input); o_stream_close(conn->output); if (close(conn->fd) < 0) i_error("close(notify connection) failed: %m"); conn->fd = -1; notify_connection_unref(&conn); master_service_client_connection_destroyed(master_service); } void notify_connection_ref(struct notify_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } void notify_connection_unref(struct notify_connection **_conn) { struct notify_connection *conn = *_conn; i_assert(conn->refcount > 0); *_conn = NULL; if (--conn->refcount > 0) return; notify_connection_destroy(conn); i_stream_unref(&conn->input); o_stream_unref(&conn->output); i_free(conn); } void notify_connections_destroy_all(void) { while (connections != NULL) notify_connection_destroy(connections); } dovecot-2.2.33.2/src/replication/replicator/replicator-queue-auth.c0000644000175000017500000000231413123174404022161 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "auth-master.h" #include "replicator-queue.h" #define REPLICATOR_AUTH_SERVICE_NAME "replicator" void replicator_queue_add_auth_users(struct replicator_queue *queue, const char *auth_socket_path, const char *usermask, time_t last_update) { struct auth_master_connection *auth_conn; struct auth_master_user_list_ctx *ctx; struct auth_user_info user_info; struct replicator_user *user; const char *username; auth_conn = auth_master_init(auth_socket_path, AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT); i_zero(&user_info); user_info.service = REPLICATOR_AUTH_SERVICE_NAME; /* add all users into replication queue, so that we can start doing full syncs for everyone whose state can't be found */ ctx = auth_master_user_list_init(auth_conn, usermask, &user_info); while ((username = auth_master_user_list_next(ctx)) != NULL) { user = replicator_queue_add(queue, username, REPLICATION_PRIORITY_NONE); user->last_update = last_update; } if (auth_master_user_list_deinit(&ctx) < 0) i_error("listing users failed, can't replicate existing data"); auth_master_deinit(&auth_conn); } dovecot-2.2.33.2/src/replication/replicator/doveadm-connection.h0000644000175000017500000000036213123174404021516 00000000000000#ifndef DOVEADM_CONNECTION_H #define DOVEADM_CONNECTION_H struct replicator_brain; void doveadm_connection_create(struct replicator_brain *brain, int fd); void doveadm_connections_init(void); void doveadm_connections_deinit(void); #endif dovecot-2.2.33.2/src/replication/replicator/notify-connection.h0000644000175000017500000000053713123174404021413 00000000000000#ifndef NOTIFY_CONNECTION_H #define NOTIFY_CONNECTION_H struct replicator_queue; struct notify_connection * notify_connection_create(int fd, struct replicator_queue *queue); void notify_connection_ref(struct notify_connection *conn); void notify_connection_unref(struct notify_connection **conn); void notify_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/replication/replicator/replicator-settings.c0000644000175000017500000000445613123174404021747 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "replicator-settings.h" /* */ static struct file_listener_settings replicator_unix_listeners_array[] = { { "replicator", 0600, "$default_internal_user", "" }, { "replicator-doveadm", 0, "$default_internal_user", "" } }; static struct file_listener_settings *replicator_unix_listeners[] = { &replicator_unix_listeners_array[0], &replicator_unix_listeners_array[1] }; static buffer_t replicator_unix_listeners_buf = { replicator_unix_listeners, sizeof(replicator_unix_listeners), { NULL, } }; /* */ struct service_settings replicator_service_settings = { .name = "replicator", .protocol = "", .type = "", .executable = "replicator", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &replicator_unix_listeners_buf, sizeof(replicator_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct replicator_settings, name), NULL } static const struct setting_define replicator_setting_defines[] = { DEF(SET_STR, auth_socket_path), DEF(SET_STR, doveadm_socket_path), DEF(SET_STR, replication_dsync_parameters), DEF(SET_TIME, replication_full_sync_interval), DEF(SET_UINT, replication_max_conns), SETTING_DEFINE_LIST_END }; const struct replicator_settings replicator_default_settings = { .auth_socket_path = "auth-userdb", .doveadm_socket_path = "doveadm-server", .replication_dsync_parameters = "-d -N -l 30 -U", .replication_full_sync_interval = 60*60*24, .replication_max_conns = 10 }; const struct setting_parser_info replicator_setting_parser_info = { .module_name = "replicator", .defines = replicator_setting_defines, .defaults = &replicator_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct replicator_settings), .parent_offset = (size_t)-1 }; const struct replicator_settings *replicator_settings; dovecot-2.2.33.2/src/replication/replicator/replicator-settings.h0000644000175000017500000000065413123174404021750 00000000000000#ifndef REPLICATOR_SETTINGS_H #define REPLICATOR_SETTINGS_H struct replicator_settings { const char *auth_socket_path; const char *doveadm_socket_path; const char *replication_dsync_parameters; unsigned int replication_full_sync_interval; unsigned int replication_max_conns; }; extern const struct setting_parser_info replicator_setting_parser_info; extern const struct replicator_settings *replicator_settings; #endif dovecot-2.2.33.2/src/replication/replicator/replicator.c0000644000175000017500000000665313123174404020112 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "restrict-access.h" #include "auth-master.h" #include "master-service.h" #include "master-service-settings.h" #include "notify-connection.h" #include "doveadm-connection.h" #include "replicator-brain.h" #include "replicator-queue.h" #include "replicator-settings.h" #define REPLICATOR_DB_DUMP_INTERVAL_MSECS (1000*60*15) /* if syncing fails, try again in 5 minutes */ #define REPLICATOR_FAILURE_RESYNC_INTERVAL_SECS (60*5) #define REPLICATOR_DB_FNAME "replicator.db" static struct replicator_queue *queue; static struct replicator_brain *brain; static const struct master_service_settings *service_set; static const struct replicator_settings *set; static struct timeout *to_dump; static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); if (strcmp(conn->name, "replicator-doveadm") == 0) doveadm_connection_create(brain, conn->fd); else (void)notify_connection_create(conn->fd, queue); } static void replication_add_users(struct replicator_queue *queue) { const char *path; replicator_queue_add_auth_users(queue, set->auth_socket_path, "*", 0); /* add updates from replicator db, if it exists */ path = t_strconcat(service_set->state_dir, "/"REPLICATOR_DB_FNAME, NULL); (void)replicator_queue_import(queue, path); } static void ATTR_NULL(1) replicator_dump_timeout(void *context ATTR_UNUSED) { const char *path; path = t_strconcat(service_set->state_dir, "/"REPLICATOR_DB_FNAME, NULL); (void)replicator_queue_export(queue, path); } static void main_init(void) { void **sets; service_set = master_service_settings_get(master_service); sets = master_service_settings_get_others(master_service); set = sets[0]; queue = replicator_queue_init(set->replication_full_sync_interval, REPLICATOR_FAILURE_RESYNC_INTERVAL_SECS); replication_add_users(queue); to_dump = timeout_add(REPLICATOR_DB_DUMP_INTERVAL_MSECS, replicator_dump_timeout, (void *)NULL); brain = replicator_brain_init(queue, set); doveadm_connections_init(); } static void main_deinit(void) { const char *path; doveadm_connections_deinit(); notify_connections_destroy_all(); replicator_brain_deinit(&brain); timeout_remove(&to_dump); path = t_strconcat(service_set->state_dir, "/"REPLICATOR_DB_FNAME, NULL); (void)replicator_queue_export(queue, path); replicator_queue_deinit(&queue); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &replicator_setting_parser_info, NULL }; const enum master_service_flags service_flags = MASTER_SERVICE_FLAG_NO_IDLE_DIE; const char *error; master_service = master_service_init("replicator", service_flags, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "replicator: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); /* finish init before we get list of users from auth, because that can take long enough for master process to kill us otherwise. */ master_service_init_finish(master_service); main_init(); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/replication/replicator/dsync-client.h0000644000175000017500000000166613123174404020346 00000000000000#ifndef DSYNC_CLIENT_H #define DSYNC_CLIENT_H struct dsync_client; enum dsync_reply { DSYNC_REPLY_OK, DSYNC_REPLY_FAIL, DSYNC_REPLY_NOUSER }; enum dsync_type { DSYNC_TYPE_FULL, DSYNC_TYPE_NORMAL, DSYNC_TYPE_INCREMENTAL }; ARRAY_DEFINE_TYPE(dsync_client, struct dsync_client *); typedef void dsync_callback_t(enum dsync_reply reply, const char *state, void *context); struct dsync_client * dsync_client_init(const char *path, const char *dsync_params); void dsync_client_deinit(struct dsync_client **conn); void dsync_client_sync(struct dsync_client *conn, const char *username, const char *state, bool full, dsync_callback_t *callback, void *context); bool dsync_client_is_busy(struct dsync_client *conn); const char *dsync_client_get_username(struct dsync_client *conn); enum dsync_type dsync_client_get_type(struct dsync_client *conn); const char *dsync_client_get_state(struct dsync_client *conn); #endif dovecot-2.2.33.2/src/replication/replicator/dsync-client.c0000644000175000017500000001515613165463624020353 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "dsync-client.h" #include #define DSYNC_FAIL_TIMEOUT_MSECS (1000*5) #define DOVEADM_HANDSHAKE "VERSION\tdoveadm-server\t1\t0\n" struct dsync_client { char *path; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; char *dsync_params; char *username; char *state; enum dsync_type sync_type; dsync_callback_t *callback; void *context; time_t last_connect_failure; unsigned int handshaked:1; unsigned int cmd_sent:1; }; struct dsync_client * dsync_client_init(const char *path, const char *dsync_params) { struct dsync_client *client; client = i_new(struct dsync_client, 1); client->path = i_strdup(path); client->fd = -1; client->dsync_params = i_strdup(dsync_params); return client; } static void dsync_callback(struct dsync_client *client, const char *state, enum dsync_reply reply) { dsync_callback_t *callback = client->callback; void *context = client->context; if (client->to != NULL) timeout_remove(&client->to); client->callback = NULL; client->context = NULL; /* make sure callback doesn't try to reuse this connection, since we can't currently handle it */ i_assert(!client->cmd_sent); client->cmd_sent = TRUE; callback(reply, state, context); client->cmd_sent = FALSE; } static void dsync_close(struct dsync_client *client) { client->cmd_sent = FALSE; client->handshaked = FALSE; i_free_and_null(client->state); i_free_and_null(client->username); if (client->fd == -1) return; io_remove(&client->io); o_stream_destroy(&client->output); i_stream_destroy(&client->input); if (close(client->fd) < 0) i_error("close(dsync) failed: %m"); client->fd = -1; } static void dsync_disconnect(struct dsync_client *client) { dsync_close(client); if (client->callback != NULL) dsync_callback(client, "", DSYNC_REPLY_FAIL); } void dsync_client_deinit(struct dsync_client **_client) { struct dsync_client *client = *_client; *_client = NULL; dsync_disconnect(client); i_free(client->dsync_params); i_free(client->path); i_free(client); } static int dsync_input_line(struct dsync_client *client, const char *line) { const char *state; if (!client->handshaked) { if (strcmp(line, "+") != 0) { i_error("%s: Unexpected handshake: %s", client->path, line); return -1; } client->handshaked = TRUE; return 0; } if (client->callback == NULL) { i_error("%s: Unexpected input: %s", client->path, line); return -1; } if (client->state == NULL) { client->state = i_strdup(t_strcut(line, '\t')); return 0; } state = t_strdup(client->state); line = t_strdup(line); dsync_close(client); if (line[0] == '+') dsync_callback(client, state, DSYNC_REPLY_OK); else if (line[0] == '-') { if (strcmp(line+1, "NOUSER") == 0) dsync_callback(client, "", DSYNC_REPLY_NOUSER); else dsync_callback(client, "", DSYNC_REPLY_FAIL); } else { i_error("%s: Invalid input: %s", client->path, line); return -1; } /* FIXME: disconnect after each request for now. doveadm server's getopt() handling seems to break otherwise. also with multiple UIDs doveadm-server fails because setid() fails */ return -1; } static void dsync_input(struct dsync_client *client) { const char *line; while ((line = i_stream_read_next_line(client->input)) != NULL) { if (dsync_input_line(client, line) < 0) { dsync_disconnect(client); return; } } if (client->input->eof) dsync_disconnect(client); } static int dsync_connect(struct dsync_client *client) { if (client->fd != -1) return 0; if (client->last_connect_failure == ioloop_time) return -1; client->fd = net_connect_unix(client->path); if (client->fd == -1) { i_error("net_connect_unix(%s) failed: %m", client->path); client->last_connect_failure = ioloop_time; return -1; } client->last_connect_failure = 0; client->io = io_add(client->fd, IO_READ, dsync_input, client); client->input = i_stream_create_fd(client->fd, (size_t)-1, FALSE); client->output = o_stream_create_fd(client->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); o_stream_nsend_str(client->output, DOVEADM_HANDSHAKE); return 0; } static void dsync_fail_timeout(struct dsync_client *client) { dsync_disconnect(client); } void dsync_client_sync(struct dsync_client *client, const char *username, const char *state, bool full, dsync_callback_t *callback, void *context) { string_t *cmd; unsigned int pos; char *p; i_assert(callback != NULL); i_assert(!dsync_client_is_busy(client)); client->username = i_strdup(username); client->cmd_sent = TRUE; client->callback = callback; client->context = context; if (full) client->sync_type = DSYNC_TYPE_FULL; else if (state != NULL && state[0] != '\0') client->sync_type = DSYNC_TYPE_INCREMENTAL; else client->sync_type = DSYNC_TYPE_NORMAL; if (dsync_connect(client) < 0) { i_assert(client->to == NULL); client->to = timeout_add(DSYNC_FAIL_TIMEOUT_MSECS, dsync_fail_timeout, client); } else { /* [] */ cmd = t_str_new(256); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, username); str_append(cmd, "\tsync\t"); pos = str_len(cmd); /* insert the parameters. we can do it simply by converting spaces into tabs, it's unlikely we'll ever need anything more complex here. */ str_append(cmd, client->dsync_params); p = str_c_modifiable(cmd) + pos; for (; *p != '\0'; p++) { if (*p == ' ') *p = '\t'; } if (full) str_append(cmd, "\t-f"); str_append(cmd, "\t-s\t"); if (state != NULL) str_append(cmd, state); str_append_c(cmd, '\n'); o_stream_nsend(client->output, str_data(cmd), str_len(cmd)); } } bool dsync_client_is_busy(struct dsync_client *client) { return client->cmd_sent; } const char *dsync_client_get_username(struct dsync_client *conn) { return conn->username; } enum dsync_type dsync_client_get_type(struct dsync_client *conn) { return conn->sync_type; } const char *dsync_client_get_state(struct dsync_client *conn) { if (conn->fd == -1) { if (conn->last_connect_failure == 0) return "Not connected"; return t_strdup_printf("Failed to connect to '%s' - last attempt %ld secs ago", conn->path, (long)(ioloop_time - conn->last_connect_failure)); } if (!dsync_client_is_busy(conn)) return "Idle"; if (!conn->handshaked) return "Waiting for handshake"; if (conn->state == NULL) return "Waiting for dsync to finish"; else return "Waiting for dsync to finish (second line)"; } dovecot-2.2.33.2/src/replication/replicator/replicator-queue.h0000644000175000017500000000637213165463624021252 00000000000000#ifndef REPLICATOR_QUEUE_H #define REPLICATOR_QUEUE_H #include "priorityq.h" #include "replication-common.h" struct replicator_user { struct priorityq_item item; char *username; /* dsync state for incremental syncing */ char *state; /* last time this user's state was updated */ time_t last_update; /* last_fast_sync is always >= last_full_sync. */ time_t last_fast_sync, last_full_sync, last_successful_sync; enum replication_priority priority; /* User isn't currently in replication queue */ unsigned int popped:1; /* Last replication sync failed */ unsigned int last_sync_failed:1; /* Force a full sync on the next replication */ unsigned int force_full_sync:1; }; typedef void replicator_sync_callback_t(bool success, void *context); struct replicator_queue * replicator_queue_init(unsigned int full_sync_interval, unsigned int failure_resync_interval); void replicator_queue_deinit(struct replicator_queue **queue); /* Call the specified callback when data is added/removed/moved in queue via _add(), _add_sync() or _remove() functions (not push/pop). */ void replicator_queue_set_change_callback(struct replicator_queue *queue, void (*callback)(void *context), void *context); /* Lookup an existing user */ struct replicator_user * replicator_queue_lookup(struct replicator_queue *queue, const char *username); /* Add a user to queue and return it. If the user already exists, it's updated only if the new priority is higher. */ struct replicator_user * replicator_queue_add(struct replicator_queue *queue, const char *username, enum replication_priority priority); void replicator_queue_add_sync(struct replicator_queue *queue, const char *username, replicator_sync_callback_t *callback, void *context); /* Remove user from replication queue and free it. */ void replicator_queue_remove(struct replicator_queue *queue, struct replicator_user **user); /* Return the next user from replication queue, and remove it from the queue. If there's nothing to be replicated currently, returns NULL and sets next_secs_r to when there should be more work to do. */ struct replicator_user * replicator_queue_pop(struct replicator_queue *queue, unsigned int *next_secs_r); /* Add user back to queue. */ void replicator_queue_push(struct replicator_queue *queue, struct replicator_user *user); int replicator_queue_import(struct replicator_queue *queue, const char *path); int replicator_queue_export(struct replicator_queue *queue, const char *path); /* Returns TRUE if user replication can be started now, FALSE if not. When returning FALSE, next_secs_r is set to user's next replication time. */ bool replicator_queue_want_sync_now(struct replicator_queue *queue, struct replicator_user *user, unsigned int *next_secs_r); /* Iterate through all users in the queue. */ struct replicator_queue_iter * replicator_queue_iter_init(struct replicator_queue *queue); struct replicator_user * replicator_queue_iter_next(struct replicator_queue_iter *iter); void replicator_queue_iter_deinit(struct replicator_queue_iter **iter); void replicator_queue_add_auth_users(struct replicator_queue *queue, const char *auth_socket_path, const char *usermask, time_t last_update); #endif dovecot-2.2.33.2/src/replication/replicator/replicator-brain.c0000644000175000017500000001175113165463624021211 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "dsync-client.h" #include "replicator-settings.h" #include "replicator-queue.h" #include "replicator-brain.h" struct replicator_sync_context { struct replicator_brain *brain; struct replicator_user *user; }; struct replicator_brain { pool_t pool; struct replicator_queue *queue; const struct replicator_settings *set; struct timeout *to; ARRAY_TYPE(dsync_client) dsync_clients; unsigned int deinitializing:1; }; static void replicator_brain_fill(struct replicator_brain *brain); static void replicator_brain_queue_changed(void *context) { struct replicator_brain *brain = context; replicator_brain_fill(brain); } struct replicator_brain * replicator_brain_init(struct replicator_queue *queue, const struct replicator_settings *set) { struct replicator_brain *brain; pool_t pool; pool = pool_alloconly_create("replication brain", 1024); brain = p_new(pool, struct replicator_brain, 1); brain->pool = pool; brain->queue = queue; brain->set = set; p_array_init(&brain->dsync_clients, pool, 16); replicator_queue_set_change_callback(queue, replicator_brain_queue_changed, brain); replicator_brain_fill(brain); return brain; } void replicator_brain_deinit(struct replicator_brain **_brain) { struct replicator_brain *brain = *_brain; struct dsync_client **connp; *_brain = NULL; brain->deinitializing = TRUE; array_foreach_modifiable(&brain->dsync_clients, connp) dsync_client_deinit(connp); if (brain->to != NULL) timeout_remove(&brain->to); pool_unref(&brain->pool); } struct replicator_queue * replicator_brain_get_queue(struct replicator_brain *brain) { return brain->queue; } const struct replicator_settings * replicator_brain_get_settings(struct replicator_brain *brain) { return brain->set; } const ARRAY_TYPE(dsync_client) * replicator_brain_get_dsync_clients(struct replicator_brain *brain) { return &brain->dsync_clients; } static struct dsync_client * get_dsync_client(struct replicator_brain *brain) { struct dsync_client *const *connp, *conn = NULL; array_foreach(&brain->dsync_clients, connp) { if (!dsync_client_is_busy(*connp)) return *connp; } if (array_count(&brain->dsync_clients) == brain->set->replication_max_conns) return NULL; conn = dsync_client_init(brain->set->doveadm_socket_path, brain->set->replication_dsync_parameters); array_append(&brain->dsync_clients, &conn, 1); return conn; } static void dsync_callback(enum dsync_reply reply, const char *state, void *context) { struct replicator_sync_context *ctx = context; if (reply == DSYNC_REPLY_NOUSER) { /* user no longer exists, remove from replication */ replicator_queue_remove(ctx->brain->queue, &ctx->user); } else { i_free(ctx->user->state); ctx->user->state = i_strdup_empty(state); ctx->user->last_sync_failed = reply != DSYNC_REPLY_OK; if (reply == DSYNC_REPLY_OK) ctx->user->last_successful_sync = ioloop_time; replicator_queue_push(ctx->brain->queue, ctx->user); } if (!ctx->brain->deinitializing) replicator_brain_fill(ctx->brain); i_free(ctx); } static bool dsync_replicate(struct replicator_brain *brain, struct replicator_user *user) { struct replicator_sync_context *ctx; struct dsync_client *conn; time_t next_full_sync; bool full; conn = get_dsync_client(brain); if (conn == NULL) return FALSE; next_full_sync = user->last_full_sync + brain->set->replication_full_sync_interval; full = next_full_sync <= ioloop_time; /* update the sync times immediately. if the replication fails we still wouldn't want it to be retried immediately. */ user->last_fast_sync = ioloop_time; if (full || user->force_full_sync) { user->last_full_sync = ioloop_time; user->force_full_sync = FALSE; } /* reset priority also. if more updates arrive during replication we'll do another replication to make sure nothing gets lost */ user->priority = REPLICATION_PRIORITY_NONE; ctx = i_new(struct replicator_sync_context, 1); ctx->brain = brain; ctx->user = user; dsync_client_sync(conn, user->username, user->state, full, dsync_callback, ctx); return TRUE; } static void replicator_brain_timeout(struct replicator_brain *brain) { timeout_remove(&brain->to); replicator_brain_fill(brain); } static bool replicator_brain_fill_next(struct replicator_brain *brain) { struct replicator_user *user; unsigned int next_secs; user = replicator_queue_pop(brain->queue, &next_secs); if (user == NULL) { /* nothing more to do */ if (brain->to != NULL) timeout_remove(&brain->to); brain->to = timeout_add(next_secs * 1000, replicator_brain_timeout, brain); return FALSE; } if (!dsync_replicate(brain, user)) { /* all connections were full, put the user back to queue */ replicator_queue_push(brain->queue, user); return FALSE; } /* replication started for the user */ return TRUE; } static void replicator_brain_fill(struct replicator_brain *brain) { while (replicator_brain_fill_next(brain)) ; } dovecot-2.2.33.2/src/replication/replicator/Makefile.am0000644000175000017500000000136213165463624017641 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = replicator AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/replication \ -DPKG_STATEDIR=\""$(statedir)"\" replicator_LDFLAGS = -export-dynamic replicator_LDADD = $(LIBDOVECOT) replicator_DEPENDENCIES = $(LIBDOVECOT_DEPS) replicator_SOURCES = \ doveadm-connection.c \ dsync-client.c \ replicator.c \ replicator-brain.c \ replicator-queue.c \ replicator-queue-auth.c \ replicator-settings.c \ notify-connection.c noinst_HEADERS = \ doveadm-connection.h \ dsync-client.h \ replicator-brain.h \ replicator-queue.h \ replicator-settings.h \ notify-connection.h dovecot-2.2.33.2/src/replication/aggregator/0002755000175000017500000000000013172375612015640 500000000000000dovecot-2.2.33.2/src/replication/aggregator/Makefile.in0000644000175000017500000005476413172375575017653 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = aggregator$(EXEEXT) subdir = src/replication/aggregator ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_aggregator_OBJECTS = aggregator.$(OBJEXT) \ aggregator-settings.$(OBJEXT) notify-connection.$(OBJEXT) \ replicator-connection.$(OBJEXT) aggregator_OBJECTS = $(am_aggregator_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = aggregator_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(aggregator_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(aggregator_SOURCES) DIST_SOURCES = $(aggregator_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/replication \ -DPKG_STATEDIR=\""$(statedir)"\" aggregator_LDFLAGS = -export-dynamic aggregator_LDADD = $(LIBDOVECOT) aggregator_DEPENDENCIES = $(LIBDOVECOT_DEPS) aggregator_SOURCES = \ aggregator.c \ aggregator-settings.c \ notify-connection.c \ replicator-connection.c noinst_HEADERS = \ aggregator-settings.h \ notify-connection.h \ replicator-connection.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/replication/aggregator/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/replication/aggregator/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list aggregator$(EXEEXT): $(aggregator_OBJECTS) $(aggregator_DEPENDENCIES) $(EXTRA_aggregator_DEPENDENCIES) @rm -f aggregator$(EXEEXT) $(AM_V_CCLD)$(aggregator_LINK) $(aggregator_OBJECTS) $(aggregator_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aggregator-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aggregator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replicator-connection.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/replication/aggregator/notify-connection.c0000644000175000017500000000673113165463624021401 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "strescape.h" #include "master-service.h" #include "replication-common.h" #include "replicator-connection.h" #include "notify-connection.h" #define MAX_INBUF_SIZE 8192 #define CONNECTION_IS_FIFO(conn) \ ((conn)->output == NULL) struct notify_connection { struct notify_connection *prev, *next; int refcount; int fd; struct io *io; struct istream *input; struct ostream *output; }; static struct notify_connection *conns = NULL; static void notify_connection_unref(struct notify_connection *conn); static void notify_connection_destroy(struct notify_connection *conn); static bool notify_input_error(struct notify_connection *conn) { if (CONNECTION_IS_FIFO(conn)) return TRUE; notify_connection_destroy(conn); return FALSE; } void notify_connection_sync_callback(bool success, void *context) { struct notify_connection *conn = context; o_stream_nsend_str(conn->output, success ? "+\n" : "-\n"); notify_connection_unref(conn); } static int notify_input_line(struct notify_connection *conn, const char *line) { const char *const *args; enum replication_priority priority; /* \t */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < 2) { i_error("Client sent invalid input"); return -1; } if (replication_priority_parse(args[1], &priority) < 0) { i_error("Client sent invalid priority: %s", args[1]); return -1; } if (priority != REPLICATION_PRIORITY_SYNC) replicator_connection_notify(replicator, args[0], priority); else { conn->refcount++; replicator_connection_notify_sync(replicator, args[0], conn); } return 0; } static void notify_input(struct notify_connection *conn) { const char *line; int ret; switch (i_stream_read(conn->input)) { case -2: /* buffer full */ i_error("Client sent too long line"); (void)notify_input_error(conn); return; case -1: /* disconnected */ notify_connection_destroy(conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = notify_input_line(conn, line); } T_END; if (ret < 0) { if (!notify_input_error(conn)) return; } } } void notify_connection_create(int fd, bool fifo) { struct notify_connection *conn; conn = i_new(struct notify_connection, 1); conn->refcount = 1; conn->fd = fd; conn->io = io_add(fd, IO_READ, notify_input, conn); conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); if (!fifo) { conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); } DLLIST_PREPEND(&conns, conn); } static void notify_connection_unref(struct notify_connection *conn) { i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; i_stream_destroy(&conn->input); if (conn->output != NULL) o_stream_destroy(&conn->output); i_free(conn); } static void notify_connection_destroy(struct notify_connection *conn) { i_assert(conn->fd != -1); if (!CONNECTION_IS_FIFO(conn)) master_service_client_connection_destroyed(master_service); DLLIST_REMOVE(&conns, conn); io_remove(&conn->io); i_stream_close(conn->input); if (conn->output != NULL) o_stream_close(conn->output); net_disconnect(conn->fd); conn->fd = -1; notify_connection_unref(conn); } void notify_connections_destroy_all(void) { while (conns != NULL) notify_connection_destroy(conns); } dovecot-2.2.33.2/src/replication/aggregator/replicator-connection.c0000644000175000017500000002004713165463624022231 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "buffer.h" #include "hash.h" #include "llist.h" #include "strescape.h" #include "replicator-connection.h" #define MAX_INBUF_SIZE 1024 #define REPLICATOR_RECONNECT_MSECS 5000 #define REPLICATOR_MEMBUF_MAX_SIZE 1024*1024 #define REPLICATOR_HANDSHAKE "VERSION\treplicator-notify\t1\t0\n" struct replicator_connection { char *path; struct ip_addr *ips; unsigned int ips_count, ip_idx; in_port_t port; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; buffer_t *queue[REPLICATION_PRIORITY_SYNC + 1]; HASH_TABLE(void *, void *) requests; unsigned int request_id_counter; replicator_sync_callback_t *callback; }; static void replicator_connection_disconnect(struct replicator_connection *conn); static int replicator_input_line(struct replicator_connection *conn, const char *line) { void *context; unsigned int id; /* <+|-> \t */ if ((line[0] != '+' && line[0] != '-') || line[1] != '\t' || str_to_uint(line+2, &id) < 0 || id == 0) { i_error("Replicator sent invalid input: %s", line); return -1; } context = hash_table_lookup(conn->requests, POINTER_CAST(id)); if (context == NULL) { i_error("Replicator sent invalid ID: %u", id); return -1; } hash_table_remove(conn->requests, POINTER_CAST(id)); conn->callback(line[0] == '+', context); return 0; } static void replicator_input(struct replicator_connection *conn) { const char *line; switch (i_stream_read(conn->input)) { case -2: /* buffer full */ i_error("Replicator sent too long line"); replicator_connection_disconnect(conn); return; case -1: /* disconnected */ replicator_connection_disconnect(conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) (void)replicator_input_line(conn, line); } static bool replicator_send_buf(struct replicator_connection *conn, buffer_t *buf) { const unsigned char *data = buf->data; size_t len = IO_BLOCK_SIZE; /* try to send about IO_BLOCK_SIZE amount of data, but only full lines */ if (len > buf->used) len = buf->used; for (;; len++) { i_assert(len < buf->used); /* there is always LF */ if (data[len] == '\n') { len++; break; } } if (o_stream_send(conn->output, data, len) < 0) { replicator_connection_disconnect(conn); return FALSE; } buffer_delete(buf, 0, len); return TRUE; } static int replicator_output(struct replicator_connection *conn) { enum replication_priority p; if (o_stream_flush(conn->output) < 0) { replicator_connection_disconnect(conn); return 1; } for (p = REPLICATION_PRIORITY_SYNC;;) { if (o_stream_get_buffer_used_size(conn->output) > 0) { o_stream_set_flush_pending(conn->output, TRUE); break; } /* output buffer is empty, send more data */ if (conn->queue[p]->used > 0) { if (!replicator_send_buf(conn, conn->queue[p])) break; } else { if (p == REPLICATION_PRIORITY_LOW) break; p--; } } return 1; } static void replicator_connection_connect(struct replicator_connection *conn) { unsigned int n; int fd = -1; if (conn->fd != -1) return; if (conn->port == 0) { fd = net_connect_unix(conn->path); if (fd == -1) i_error("net_connect_unix(%s) failed: %m", conn->path); } else { for (n = 0; n < conn->ips_count; n++) { unsigned int idx = conn->ip_idx; conn->ip_idx = (conn->ip_idx + 1) % conn->ips_count; fd = net_connect_ip(&conn->ips[idx], conn->port, NULL); if (fd != -1) break; i_error("connect(%s, %u) failed: %m", net_ip2addr(&conn->ips[idx]), conn->port); } } if (fd == -1) { if (conn->to == NULL) { conn->to = timeout_add(REPLICATOR_RECONNECT_MSECS, replicator_connection_connect, conn); } return; } if (conn->to != NULL) timeout_remove(&conn->to); conn->fd = fd; conn->io = io_add(fd, IO_READ, replicator_input, conn); conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_nsend_str(conn->output, REPLICATOR_HANDSHAKE); o_stream_set_flush_callback(conn->output, replicator_output, conn); } static void replicator_abort_all_requests(struct replicator_connection *conn) { struct hash_iterate_context *iter; void *key, *value; iter = hash_table_iterate_init(conn->requests); while (hash_table_iterate(iter, conn->requests, &key, &value)) conn->callback(FALSE, value); hash_table_iterate_deinit(&iter); hash_table_clear(conn->requests, TRUE); } static void replicator_connection_disconnect(struct replicator_connection *conn) { if (conn->fd == -1) return; replicator_abort_all_requests(conn); io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); net_disconnect(conn->fd); conn->fd = -1; } static struct replicator_connection *replicator_connection_create(void) { struct replicator_connection *conn; unsigned int i; conn = i_new(struct replicator_connection, 1); conn->fd = -1; hash_table_create_direct(&conn->requests, default_pool, 0); for (i = REPLICATION_PRIORITY_LOW; i <= REPLICATION_PRIORITY_SYNC; i++) conn->queue[i] = buffer_create_dynamic(default_pool, 1024); return conn; } struct replicator_connection * replicator_connection_create_unix(const char *path, replicator_sync_callback_t *callback) { struct replicator_connection *conn; conn = replicator_connection_create(); conn->callback = callback; conn->path = i_strdup(path); return conn; } struct replicator_connection * replicator_connection_create_inet(const struct ip_addr *ips, unsigned int ips_count, in_port_t port, replicator_sync_callback_t *callback) { struct replicator_connection *conn; conn = replicator_connection_create(); conn->callback = callback; conn->ips = i_new(struct ip_addr, ips_count); memcpy(conn->ips, ips, sizeof(*ips) * ips_count); conn->ips_count = ips_count; conn->port = port; return conn; } void replicator_connection_destroy(struct replicator_connection **_conn) { struct replicator_connection *conn = *_conn; unsigned int i; *_conn = NULL; replicator_connection_disconnect(conn); for (i = REPLICATION_PRIORITY_LOW; i <= REPLICATION_PRIORITY_SYNC; i++) buffer_free(&conn->queue[i]); if (conn->to != NULL) timeout_remove(&conn->to); hash_table_destroy(&conn->requests); i_free(conn); } static void replicator_send(struct replicator_connection *conn, enum replication_priority priority, const char *data) { size_t data_len = strlen(data); if (conn->fd != -1 && o_stream_get_buffer_used_size(conn->output) == 0) { /* we can send data immediately */ o_stream_nsend(conn->output, data, data_len); } else if (conn->queue[priority]->used + data_len >= REPLICATOR_MEMBUF_MAX_SIZE) { /* FIXME: compress duplicates, start writing to file */ } else { /* queue internally to separate queues */ buffer_append(conn->queue[priority], data, data_len); if (conn->output != NULL) o_stream_set_flush_pending(conn->output, TRUE); } } void replicator_connection_notify(struct replicator_connection *conn, const char *username, enum replication_priority priority) { const char *priority_str = ""; replicator_connection_connect(conn); switch (priority) { case REPLICATION_PRIORITY_NONE: case REPLICATION_PRIORITY_SYNC: i_unreached(); case REPLICATION_PRIORITY_LOW: priority_str = "low"; break; case REPLICATION_PRIORITY_HIGH: priority_str = "high"; break; } T_BEGIN { replicator_send(conn, priority, t_strdup_printf( "U\t%s\t%s\n", str_tabescape(username), priority_str)); } T_END; } void replicator_connection_notify_sync(struct replicator_connection *conn, const char *username, void *context) { unsigned int id; replicator_connection_connect(conn); id = ++conn->request_id_counter; if (id == 0) id++; hash_table_insert(conn->requests, POINTER_CAST(id), context); T_BEGIN { replicator_send(conn, REPLICATION_PRIORITY_SYNC, t_strdup_printf( "U\t%s\tsync\t%u\n", str_tabescape(username), id)); } T_END; } dovecot-2.2.33.2/src/replication/aggregator/aggregator.c0000644000175000017500000000377213123174404020045 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "aggregator-settings.h" #include "notify-connection.h" #include "replicator-connection.h" struct replicator_connection *replicator; static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); notify_connection_create(conn->fd, conn->fifo); } static void main_preinit(void) { struct ip_addr *ips; unsigned int ips_count; const struct aggregator_settings *set; void **sets; int ret; sets = master_service_settings_get_others(master_service); set = sets[0]; if (set->replicator_port != 0) { ret = net_gethostbyname(set->replicator_host, &ips, &ips_count); if (ret != 0) { i_fatal("replicator_host: gethostbyname(%s) failed: %s", set->replicator_host, net_gethosterror(ret)); } replicator = replicator_connection_create_inet(ips, ips_count, set->replicator_port, notify_connection_sync_callback); } else { replicator = replicator_connection_create_unix(set->replicator_host, notify_connection_sync_callback); } } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &aggregator_setting_parser_info, NULL }; const char *error; master_service = master_service_init("aggregator", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "aggregator: "); main_preinit(); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); master_service_init_finish(master_service); master_service_run(master_service, client_connected); notify_connections_destroy_all(); replicator_connection_destroy(&replicator); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/replication/aggregator/replicator-connection.h0000644000175000017500000000155513123174404022226 00000000000000#ifndef REPLICATOR_CONNECTION_H #define REPLICATOR_CONNECTION_H #include "replication-common.h" typedef void replicator_sync_callback_t(bool success, void *context); struct replicator_connection * replicator_connection_create_unix(const char *path, replicator_sync_callback_t *callback); struct replicator_connection * replicator_connection_create_inet(const struct ip_addr *ips, unsigned int ips_count, in_port_t port, replicator_sync_callback_t *callback); void replicator_connection_destroy(struct replicator_connection **conn); void replicator_connection_notify(struct replicator_connection *conn, const char *username, enum replication_priority priority); void replicator_connection_notify_sync(struct replicator_connection *conn, const char *username, void *context); extern struct replicator_connection *replicator; #endif dovecot-2.2.33.2/src/replication/aggregator/notify-connection.h0000644000175000017500000000034213123174404021363 00000000000000#ifndef NOTIFY_CONNECTION_H #define NOTIFY_CONNECTION_H void notify_connection_create(int fd, bool fifo); void notify_connections_destroy_all(void); void notify_connection_sync_callback(bool success, void *context); #endif dovecot-2.2.33.2/src/replication/aggregator/aggregator-settings.h0000644000175000017500000000044613123174404021703 00000000000000#ifndef AGGREGATOR_SETTINGS_H #define AGGREGATOR_SETTINGS_H struct aggregator_settings { const char *replicator_host; in_port_t replicator_port; }; extern const struct setting_parser_info aggregator_setting_parser_info; extern const struct aggregator_settings *aggregator_settings; #endif dovecot-2.2.33.2/src/replication/aggregator/aggregator-settings.c0000644000175000017500000000451413123174404021676 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "aggregator-settings.h" /* */ static struct file_listener_settings aggregator_unix_listeners_array[] = { { "replication-notify", 0600, "", "" } }; static struct file_listener_settings *aggregator_unix_listeners[] = { &aggregator_unix_listeners_array[0] }; static buffer_t aggregator_unix_listeners_buf = { aggregator_unix_listeners, sizeof(aggregator_unix_listeners), { NULL, } }; static struct file_listener_settings aggregator_fifo_listeners_array[] = { { "replication-notify-fifo", 0600, "", "" } }; static struct file_listener_settings *aggregator_fifo_listeners[] = { &aggregator_fifo_listeners_array[0] }; static buffer_t aggregator_fifo_listeners_buf = { aggregator_fifo_listeners, sizeof(aggregator_fifo_listeners), { NULL, } }; /* */ struct service_settings aggregator_service_settings = { .name = "aggregator", .protocol = "", .type = "", .executable = "aggregator", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = ".", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &aggregator_unix_listeners_buf, sizeof(aggregator_unix_listeners[0]) } }, .fifo_listeners = { { &aggregator_fifo_listeners_buf, sizeof(aggregator_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct aggregator_settings, name), NULL } static const struct setting_define aggregator_setting_defines[] = { DEF(SET_STR, replicator_host), DEF(SET_IN_PORT, replicator_port), SETTING_DEFINE_LIST_END }; const struct aggregator_settings aggregator_default_settings = { .replicator_host = "replicator", .replicator_port = 0 }; const struct setting_parser_info aggregator_setting_parser_info = { .module_name = "aggregator", .defines = aggregator_setting_defines, .defaults = &aggregator_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct aggregator_settings), .parent_offset = (size_t)-1 }; const struct aggregator_settings *aggregator_settings; dovecot-2.2.33.2/src/replication/aggregator/Makefile.am0000644000175000017500000000114113165463624017612 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = aggregator AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/replication \ -DPKG_STATEDIR=\""$(statedir)"\" aggregator_LDFLAGS = -export-dynamic aggregator_LDADD = $(LIBDOVECOT) aggregator_DEPENDENCIES = $(LIBDOVECOT_DEPS) aggregator_SOURCES = \ aggregator.c \ aggregator-settings.c \ notify-connection.c \ replicator-connection.c noinst_HEADERS = \ aggregator-settings.h \ notify-connection.h \ replicator-connection.h dovecot-2.2.33.2/src/replication/Makefile.am0000644000175000017500000000011213123174404015452 00000000000000SUBDIRS = aggregator replicator noinst_HEADERS = \ replication-common.h dovecot-2.2.33.2/src/indexer/0002755000175000017500000000000013172375612012643 500000000000000dovecot-2.2.33.2/src/indexer/Makefile.in0000644000175000017500000005662013172375572014644 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = indexer$(EXEEXT) indexer-worker$(EXEEXT) subdir = src/indexer ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_indexer_OBJECTS = indexer.$(OBJEXT) indexer-client.$(OBJEXT) \ indexer-queue.$(OBJEXT) indexer-settings.$(OBJEXT) \ worker-connection.$(OBJEXT) worker-pool.$(OBJEXT) indexer_OBJECTS = $(am_indexer_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_indexer_worker_OBJECTS = indexer-worker.$(OBJEXT) \ indexer-worker-settings.$(OBJEXT) master-connection.$(OBJEXT) indexer_worker_OBJECTS = $(am_indexer_worker_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(indexer_SOURCES) $(indexer_worker_SOURCES) DIST_SOURCES = $(indexer_SOURCES) $(indexer_worker_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -DPKG_RUNDIR=\""$(rundir)"\" indexer_LDADD = $(LIBDOVECOT) indexer_DEPENDENCIES = $(LIBDOVECOT_DEPS) indexer_SOURCES = \ indexer.c \ indexer-client.c \ indexer-queue.c \ indexer-settings.c \ worker-connection.c \ worker-pool.c indexer_worker_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) indexer_worker_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) indexer_worker_SOURCES = \ indexer-worker.c \ indexer-worker-settings.c \ master-connection.c noinst_HEADERS = \ indexer.h \ indexer-client.h \ indexer-queue.h \ master-connection.h \ worker-connection.h \ worker-pool.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/indexer/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/indexer/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list indexer$(EXEEXT): $(indexer_OBJECTS) $(indexer_DEPENDENCIES) $(EXTRA_indexer_DEPENDENCIES) @rm -f indexer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(indexer_OBJECTS) $(indexer_LDADD) $(LIBS) indexer-worker$(EXEEXT): $(indexer_worker_OBJECTS) $(indexer_worker_DEPENDENCIES) $(EXTRA_indexer_worker_DEPENDENCIES) @rm -f indexer-worker$(EXEEXT) $(AM_V_CCLD)$(LINK) $(indexer_worker_OBJECTS) $(indexer_worker_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer-queue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer-worker-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer-worker.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-pool.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/indexer/master-connection.c0000644000175000017500000002234713165463624016370 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "write-full.h" #include "strescape.h" #include "process-title.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mail-storage-service.h" #include "mail-search-build.h" #include "master-connection.h" #include #define INDEXER_PROTOCOL_MAJOR_VERSION 1 #define INDEXER_PROTOCOL_MINOR_VERSION 0 #define INDEXER_WORKER_HANDSHAKE "VERSION\tindexer-worker-master\t1\t0\n%u\n" #define INDEXER_MASTER_NAME "indexer-master-worker" struct master_connection { struct mail_storage_service_ctx *storage_service; int fd; struct io *io; struct istream *input; struct ostream *output; unsigned int version_received:1; }; static void ATTR_NULL(1, 2) indexer_worker_refresh_proctitle(const char *username, const char *mailbox, uint32_t seq1, uint32_t seq2) { if (!master_service_settings_get(master_service)->verbose_proctitle) return; if (username == NULL) process_title_set("[idling]"); else if (seq1 == 0) process_title_set(t_strdup_printf("[%s %s]", username, mailbox)); else { process_title_set(t_strdup_printf("[%s %s - %u/%u]", username, mailbox, seq1, seq2)); } } static int index_mailbox_precache(struct master_connection *conn, struct mailbox *box) { struct mail_storage *storage = mailbox_get_storage(box); const char *username = mail_storage_get_user(storage)->username; const char *box_vname = mailbox_get_vname(box); struct mailbox_status status; struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; struct mailbox_metadata metadata; uint32_t seq, first_uid = 0, last_uid = 0; char percentage_str[2+1+1]; unsigned int counter = 0, max, percentage, percentage_sent = 0; int ret = 0; if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS, &metadata) < 0) { i_error("Mailbox %s: Precache-fields lookup failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); return -1; } if (mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ, &status) < 0) { i_error("Mailbox %s: Status lookup failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); return -1; } seq = status.last_cached_seq + 1; trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC); mailbox_transaction_set_reason(trans, "indexing"); search_args = mail_search_build_init(); mail_search_build_add_seqset(search_args, seq, status.messages); ctx = mailbox_search_init(trans, search_args, NULL, metadata.precache_fields, NULL); mail_search_args_unref(&search_args); max = status.messages + 1 - seq; while (mailbox_search_next(ctx, &mail)) { if (first_uid == 0) first_uid = mail->uid; last_uid = mail->uid; mail_precache(mail); if (++counter % 100 == 0) { percentage = counter*100 / max; if (percentage != percentage_sent && percentage < 100) { percentage_sent = percentage; if (i_snprintf(percentage_str, sizeof(percentage_str), "%u\n", percentage) < 0) i_unreached(); (void)write_full(conn->fd, percentage_str, strlen(percentage_str)); } indexer_worker_refresh_proctitle(username, box_vname, counter, max); } } if (mailbox_search_deinit(&ctx) < 0) { i_error("Mailbox %s: Mail search failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; } const char *uids = first_uid == 0 ? "" : t_strdup_printf(" (UIDs %u..%u)", first_uid, last_uid); if (mailbox_transaction_commit(&trans) < 0) { i_error("Mailbox %s: Transaction commit failed: %s" " (attempted to index %u messages%s)", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL), counter, uids); ret = -1; } else { i_info("Indexed %u messages in %s%s", counter, mailbox_get_vname(box), uids); } return ret; } static int index_mailbox(struct master_connection *conn, struct mail_user *user, const char *mailbox, unsigned int max_recent_msgs, const char *what) { struct mail_namespace *ns; struct mailbox *box; struct mailbox_status status; const char *path, *errstr; enum mail_error error; enum mailbox_sync_flags sync_flags = MAILBOX_SYNC_FLAG_FULL_READ; int ret; ns = mail_namespace_find(user->namespaces, mailbox); box = mailbox_alloc(ns->list, mailbox, 0); mailbox_set_reason(box, "indexing"); ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path); if (ret < 0) { i_error("Getting path to mailbox %s failed: %s", mailbox, mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return -1; } if (ret == 0) { i_info("Indexes disabled for mailbox %s, skipping", mailbox); mailbox_free(&box); return 0; } ret = 0; if (max_recent_msgs != 0) { /* index only if there aren't too many recent messages. don't bother syncing the mailbox, that alone can take a while with large maildirs. */ if (mailbox_open(box) < 0) { i_error("Opening mailbox %s failed: %s", mailbox, mailbox_get_last_internal_error(box, NULL)); ret = -1; } else { mailbox_get_open_status(box, STATUS_RECENT, &status); } if (ret < 0 || status.recent > max_recent_msgs) { mailbox_free(&box); return ret; } } if (strchr(what, 'o') != NULL) sync_flags |= MAILBOX_SYNC_FLAG_OPTIMIZE; if (mailbox_sync(box, sync_flags) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_NOTFOUND) { i_error("Syncing mailbox %s failed: %s", mailbox, errstr); } else if (user->mail_debug) { i_debug("Syncing mailbox %s failed: %s", mailbox, errstr); } ret = -1; } else if (strchr(what, 'i') != NULL) { if (index_mailbox_precache(conn, box) < 0) ret = -1; } mailbox_free(&box); return ret; } static int master_connection_input_line(struct master_connection *conn, const char *line) { const char *const *args = t_strsplit_tabescaped(line); struct mail_storage_service_input input; struct mail_storage_service_user *service_user; struct mail_user *user; const char *str, *error; unsigned int max_recent_msgs; int ret; /* [i][o] */ if (str_array_length(args) != 5 || str_to_uint(args[3], &max_recent_msgs) < 0 || args[4][0] == '\0') { i_error("Invalid input from master: %s", line); return -1; } i_zero(&input); input.module = "mail"; input.service = "indexer-worker"; input.username = args[0]; /* if session-id is given, use it as a prefix to a unique session ID. we can't use the session-id directly or stats process will complain about duplicates. (especially LMTP would use the same session-id for multiple users' indexing at the same time.) */ if (args[2][0] != '\0') input.session_id_prefix = args[2]; if (mail_storage_service_lookup_next(conn->storage_service, &input, &service_user, &user, &error) <= 0) { i_error("User %s lookup failed: %s", args[0], error); ret = -1; } else { indexer_worker_refresh_proctitle(user->username, args[1], 0, 0); ret = index_mailbox(conn, user, args[1], max_recent_msgs, args[4]); /* refresh proctitle before a potentially long-running user unref */ indexer_worker_refresh_proctitle(user->username, "(deinit)", 0, 0); mail_user_unref(&user); mail_storage_service_user_unref(&service_user); indexer_worker_refresh_proctitle(NULL, NULL, 0, 0); } str = ret < 0 ? "-1\n" : "100\n"; return write_full(conn->fd, str, strlen(str)); } static void master_connection_input(struct master_connection *conn) { const char *line; int ret; if (i_stream_read(conn->input) < 0) { master_service_stop(master_service); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_MASTER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer master not compatible with this master " "(mixed old and new binaries?)"); master_service_stop(master_service); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = master_connection_input_line(conn, line); } T_END; if (ret < 0) { master_service_stop(master_service); break; } } } struct master_connection * master_connection_create(int fd, struct mail_storage_service_ctx *storage_service) { struct master_connection *conn; const char *handshake; conn = i_new(struct master_connection, 1); conn->storage_service = storage_service; conn->fd = fd; conn->io = io_add(conn->fd, IO_READ, master_connection_input, conn); conn->input = i_stream_create_fd(conn->fd, (size_t)-1, FALSE); handshake = t_strdup_printf(INDEXER_WORKER_HANDSHAKE, master_service_get_process_limit(master_service)); (void)write_full(conn->fd, handshake, strlen(handshake)); return conn; } void master_connection_destroy(struct master_connection **_conn) { struct master_connection *conn = *_conn; *_conn = NULL; io_remove(&conn->io); i_stream_destroy(&conn->input); if (close(conn->fd) < 0) i_error("close(master conn) failed: %m"); i_free(conn); master_service_client_connection_destroyed(master_service); } dovecot-2.2.33.2/src/indexer/worker-connection.c0000644000175000017500000001501713165463624016402 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "aqueue.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "master-service.h" #include "indexer-queue.h" #include "worker-connection.h" #include #define INDEXER_PROTOCOL_MAJOR_VERSION 1 #define INDEXER_PROTOCOL_MINOR_VERSION 0 #define INDEXER_MASTER_HANDSHAKE "VERSION\tindexer-master-worker\t1\t0\n" #define INDEXER_WORKER_NAME "indexer-worker-master" struct worker_connection { int refcount; char *socket_path; indexer_status_callback_t *callback; int fd; struct io *io; struct istream *input; struct ostream *output; char *request_username; ARRAY(void *) request_contexts; struct aqueue *request_queue; unsigned int process_limit; unsigned int version_received:1; }; struct worker_connection * worker_connection_create(const char *socket_path, indexer_status_callback_t *callback) { struct worker_connection *conn; conn = i_new(struct worker_connection, 1); conn->refcount = 1; conn->socket_path = i_strdup(socket_path); conn->callback = callback; conn->fd = -1; i_array_init(&conn->request_contexts, 32); conn->request_queue = aqueue_init(&conn->request_contexts.arr); return conn; } static void worker_connection_unref(struct worker_connection *conn) { i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; aqueue_deinit(&conn->request_queue); array_free(&conn->request_contexts); i_free(conn->socket_path); i_free(conn); } static void worker_connection_disconnect(struct worker_connection *conn) { unsigned int i, count = aqueue_count(conn->request_queue); if (conn->fd != -1) { io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(%s) failed: %m", conn->socket_path); conn->fd = -1; } /* cancel any pending requests */ if (count > 0) { i_error("Indexer worker disconnected, " "discarding %u requests for %s", count, conn->request_username); } /* conn->callback() can try to destroy us */ conn->refcount++; for (i = 0; i < count; i++) { void *const *contextp = array_idx(&conn->request_contexts, aqueue_idx(conn->request_queue, 0)); void *context = *contextp; aqueue_delete_tail(conn->request_queue); conn->callback(-1, context); } i_free_and_null(conn->request_username); worker_connection_unref(conn); } void worker_connection_destroy(struct worker_connection **_conn) { struct worker_connection *conn = *_conn; *_conn = NULL; worker_connection_disconnect(conn); worker_connection_unref(conn); } static int worker_connection_input_line(struct worker_connection *conn, const char *line) { void *const *contextp, *context; int percentage; if (aqueue_count(conn->request_queue) == 0) { i_error("Input from worker without pending requests: %s", line); return -1; } if (str_to_int(line, &percentage) < 0 || percentage < -1 || percentage > 100) { i_error("Invalid input from worker: %s", line); return -1; } contextp = array_idx(&conn->request_contexts, aqueue_idx(conn->request_queue, 0)); context = *contextp; if (percentage < 0 || percentage == 100) { /* the request is finished */ aqueue_delete_tail(conn->request_queue); if (aqueue_count(conn->request_queue) == 0) i_free_and_null(conn->request_username); } conn->callback(percentage, context); return 0; } static void worker_connection_input(struct worker_connection *conn) { const char *line; if (i_stream_read(conn->input) < 0) { worker_connection_disconnect(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_WORKER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer worker not compatible with this master " "(mixed old and new binaries?)"); worker_connection_disconnect(conn); return; } conn->version_received = TRUE; } if (conn->process_limit == 0) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (str_to_uint(line, &conn->process_limit) < 0 || conn->process_limit == 0) { i_error("Indexer worker sent invalid handshake: %s", line); worker_connection_disconnect(conn); return; } } while ((line = i_stream_next_line(conn->input)) != NULL) { if (worker_connection_input_line(conn, line) < 0) { worker_connection_disconnect(conn); break; } } } int worker_connection_connect(struct worker_connection *conn) { i_assert(conn->fd == -1); conn->fd = net_connect_unix(conn->socket_path); if (conn->fd == -1) { i_error("connect(%s) failed: %m", conn->socket_path); return -1; } conn->io = io_add(conn->fd, IO_READ, worker_connection_input, conn); conn->input = i_stream_create_fd(conn->fd, (size_t)-1, FALSE); conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_nsend_str(conn->output, INDEXER_MASTER_HANDSHAKE); return 0; } bool worker_connection_is_connected(struct worker_connection *conn) { return conn->fd != -1; } bool worker_connection_get_process_limit(struct worker_connection *conn, unsigned int *limit_r) { if (conn->process_limit == 0) return FALSE; *limit_r = conn->process_limit; return TRUE; } void worker_connection_request(struct worker_connection *conn, const struct indexer_request *request, void *context) { i_assert(worker_connection_is_connected(conn)); i_assert(context != NULL); i_assert(request->index || request->optimize); if (conn->request_username == NULL) conn->request_username = i_strdup(request->username); else { i_assert(strcmp(conn->request_username, request->username) == 0); } aqueue_append(conn->request_queue, &context); T_BEGIN { string_t *str = t_str_new(128); str_append_tabescaped(str, request->username); str_append_c(str, '\t'); str_append_tabescaped(str, request->mailbox); str_append_c(str, '\t'); if (request->session_id != NULL) str_append_tabescaped(str, request->session_id); str_printfa(str, "\t%u\t", request->max_recent_msgs); if (request->index) str_append_c(str, 'i'); if (request->optimize) str_append_c(str, 'o'); str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); } T_END; } bool worker_connection_is_busy(struct worker_connection *conn) { return aqueue_count(conn->request_queue) > 0; } const char *worker_connection_get_username(struct worker_connection *conn) { return conn->request_username; } dovecot-2.2.33.2/src/indexer/indexer-settings.c0000644000175000017500000000232513123174404016213 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include extern const struct setting_parser_info service_setting_parser_info; /* */ static struct file_listener_settings indexer_unix_listeners_array[] = { { "indexer", 0666, "", "" } }; static struct file_listener_settings *indexer_unix_listeners[] = { &indexer_unix_listeners_array[0] }; static buffer_t indexer_unix_listeners_buf = { indexer_unix_listeners, sizeof(indexer_unix_listeners), { NULL, } }; /* */ struct service_settings indexer_service_settings = { .name = "indexer", .protocol = "", .type = "", .executable = "indexer", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &indexer_unix_listeners_buf, sizeof(indexer_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; dovecot-2.2.33.2/src/indexer/indexer-queue.c0000644000175000017500000001621713123174404015504 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "hash.h" #include "indexer-queue.h" struct indexer_queue { indexer_status_callback_t *callback; void (*listen_callback)(struct indexer_queue *); /* username+mailbox -> indexer_request */ HASH_TABLE(struct indexer_request *, struct indexer_request *) requests; struct indexer_request *head, *tail; }; static unsigned int indexer_request_hash(const struct indexer_request *request) { return str_hash(request->username) ^ str_hash(request->mailbox); } static int indexer_request_cmp(const struct indexer_request *r1, const struct indexer_request *r2) { return strcmp(r1->username, r2->username) == 0 && strcmp(r1->mailbox, r2->mailbox) == 0 ? 0 : 1; } struct indexer_queue * indexer_queue_init(indexer_status_callback_t *callback) { struct indexer_queue *queue; queue = i_new(struct indexer_queue, 1); queue->callback = callback; hash_table_create(&queue->requests, default_pool, 0, indexer_request_hash, indexer_request_cmp); return queue; } void indexer_queue_deinit(struct indexer_queue **_queue) { struct indexer_queue *queue = *_queue; *_queue = NULL; i_assert(indexer_queue_is_empty(queue)); hash_table_destroy(&queue->requests); i_free(queue); } void indexer_queue_set_listen_callback(struct indexer_queue *queue, void (*callback)(struct indexer_queue *)) { queue->listen_callback = callback; } static struct indexer_request * indexer_queue_lookup(struct indexer_queue *queue, const char *username, const char *mailbox) { struct indexer_request lookup_request; lookup_request.username = (void *)username; lookup_request.mailbox = (void *)mailbox; return hash_table_lookup(queue->requests, &lookup_request); } static void request_add_context(struct indexer_request *request, void *context) { if (context == NULL) return; if (!array_is_created(&request->contexts)) i_array_init(&request->contexts, 2); array_append(&request->contexts, &context, 1); } static struct indexer_request * indexer_queue_append_request(struct indexer_queue *queue, bool append, const char *username, const char *mailbox, const char *session_id, unsigned int max_recent_msgs, void *context) { struct indexer_request *request; request = indexer_queue_lookup(queue, username, mailbox); if (request == NULL) { request = i_new(struct indexer_request, 1); request->username = i_strdup(username); request->mailbox = i_strdup(mailbox); request->session_id = i_strdup(session_id); request->max_recent_msgs = max_recent_msgs; request_add_context(request, context); hash_table_insert(queue->requests, request, request); } else { if (request->max_recent_msgs > max_recent_msgs) request->max_recent_msgs = max_recent_msgs; request_add_context(request, context); if (request->working) { /* we're already indexing this mailbox. */ if (append) request->reindex_tail = TRUE; else request->reindex_head = TRUE; return request; } if (append) { /* keep the request in its old position */ return request; } /* move request to beginning of the queue */ DLLIST2_REMOVE(&queue->head, &queue->tail, request); } if (append) DLLIST2_APPEND(&queue->head, &queue->tail, request); else DLLIST2_PREPEND(&queue->head, &queue->tail, request); return request; } static void indexer_queue_append_finish(struct indexer_queue *queue) { if (queue->listen_callback != NULL) queue->listen_callback(queue); indexer_refresh_proctitle(); } void indexer_queue_append(struct indexer_queue *queue, bool append, const char *username, const char *mailbox, const char *session_id, unsigned int max_recent_msgs, void *context) { struct indexer_request *request; request = indexer_queue_append_request(queue, append, username, mailbox, session_id, max_recent_msgs, context); request->index = TRUE; indexer_queue_append_finish(queue); } void indexer_queue_append_optimize(struct indexer_queue *queue, const char *username, const char *mailbox, void *context) { struct indexer_request *request; request = indexer_queue_append_request(queue, TRUE, username, mailbox, NULL, 0, context); request->optimize = TRUE; indexer_queue_append_finish(queue); } struct indexer_request *indexer_queue_request_peek(struct indexer_queue *queue) { return queue->head; } void indexer_queue_request_remove(struct indexer_queue *queue) { struct indexer_request *request = queue->head; i_assert(request != NULL); DLLIST2_REMOVE(&queue->head, &queue->tail, request); } static void indexer_queue_request_status_int(struct indexer_queue *queue, struct indexer_request *request, int percentage) { void *const *contextp; unsigned int i; for (i = 0; i < request->working_context_idx; i++) { contextp = array_idx(&request->contexts, i); queue->callback(percentage, *contextp); } } void indexer_queue_request_status(struct indexer_queue *queue, struct indexer_request *request, int percentage) { i_assert(percentage >= 0 && percentage < 100); indexer_queue_request_status_int(queue, request, percentage); } void indexer_queue_request_work(struct indexer_request *request) { request->working = TRUE; request->working_context_idx = !array_is_created(&request->contexts) ? 0 : array_count(&request->contexts); } void indexer_queue_request_finish(struct indexer_queue *queue, struct indexer_request **_request, bool success) { struct indexer_request *request = *_request; *_request = NULL; indexer_queue_request_status_int(queue, request, success ? 100 : -1); if (request->reindex_head || request->reindex_tail) { i_assert(request->working); request->working = FALSE; request->reindex_head = FALSE; request->reindex_tail = FALSE; if (request->working_context_idx > 0) { array_delete(&request->contexts, 0, request->working_context_idx); } if (request->reindex_head) DLLIST2_PREPEND(&queue->head, &queue->tail, request); else DLLIST2_APPEND(&queue->head, &queue->tail, request); return; } hash_table_remove(queue->requests, request); if (array_is_created(&request->contexts)) array_free(&request->contexts); i_free(request->username); i_free(request->mailbox); i_free(request); indexer_refresh_proctitle(); } void indexer_queue_cancel_all(struct indexer_queue *queue) { struct indexer_request *request; struct hash_iterate_context *iter; /* remove all reindex-markers so when the current requests finish (or are cancelled) we don't try to retry them (especially during deinit where it crashes) */ iter = hash_table_iterate_init(queue->requests); while (hash_table_iterate(iter, queue->requests, &request, &request)) request->reindex_head = request->reindex_tail = FALSE; hash_table_iterate_deinit(&iter); while ((request = indexer_queue_request_peek(queue)) != NULL) { indexer_queue_request_remove(queue); indexer_queue_request_finish(queue, &request, FALSE); } } bool indexer_queue_is_empty(struct indexer_queue *queue) { return queue->head == NULL; } unsigned int indexer_queue_count(struct indexer_queue *queue) { return hash_table_count(queue->requests); } dovecot-2.2.33.2/src/indexer/indexer-worker-settings.c0000644000175000017500000000231113123174404017515 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings indexer_worker_unix_listeners_array[] = { { "indexer-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *indexer_worker_unix_listeners[] = { &indexer_worker_unix_listeners_array[0] }; static buffer_t indexer_worker_unix_listeners_buf = { indexer_worker_unix_listeners, sizeof(indexer_worker_unix_listeners), { NULL, } }; /* */ struct service_settings indexer_worker_service_settings = { .name = "indexer-worker", .protocol = "", .type = "", .executable = "indexer-worker", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 10, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &indexer_worker_unix_listeners_buf, sizeof(indexer_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; dovecot-2.2.33.2/src/indexer/master-connection.h0000644000175000017500000000036113123174404016352 00000000000000#ifndef MASTER_CONNECTION_H #define MASTER_CONNECTION_H struct master_connection * master_connection_create(int fd, struct mail_storage_service_ctx *storage_service); void master_connection_destroy(struct master_connection **conn); #endif dovecot-2.2.33.2/src/indexer/indexer-queue.h0000644000175000017500000000457013165463624015523 00000000000000#ifndef INDEXER_QUEUE_H #define INDEXER_QUEUE_H #include "indexer.h" struct indexer_request { struct indexer_request *prev, *next; char *username; char *mailbox; char *session_id; unsigned int max_recent_msgs; /* index messages in this mailbox */ unsigned int index:1; /* optimize this mailbox */ unsigned int optimize:1; /* currently indexing this mailbox */ unsigned int working:1; /* after indexing is finished, add this request back to the queue and reindex it (i.e. a new indexing request came while we were working.) */ unsigned int reindex_head:1; unsigned int reindex_tail:1; /* when working finished, call this number of contexts and leave the rest to the reindexing. */ unsigned int working_context_idx; ARRAY(void *) contexts; }; struct indexer_queue *indexer_queue_init(indexer_status_callback_t *callback); void indexer_queue_deinit(struct indexer_queue **queue); /* The callback is called whenever a new request is added to the queue. */ void indexer_queue_set_listen_callback(struct indexer_queue *queue, void (*callback)(struct indexer_queue *)); void indexer_queue_append(struct indexer_queue *queue, bool append, const char *username, const char *mailbox, const char *session_id, unsigned int max_recent_msgs, void *context); void indexer_queue_append_optimize(struct indexer_queue *queue, const char *username, const char *mailbox, void *context); void indexer_queue_cancel_all(struct indexer_queue *queue); bool indexer_queue_is_empty(struct indexer_queue *queue); unsigned int indexer_queue_count(struct indexer_queue *queue); /* Return the next request from the queue, without removing it. */ struct indexer_request *indexer_queue_request_peek(struct indexer_queue *queue); /* Remove the next request from the queue. You must call indexer_queue_request_finish() to free its memory. */ void indexer_queue_request_remove(struct indexer_queue *queue); /* Give a status update about how far the indexing is going on. */ void indexer_queue_request_status(struct indexer_queue *queue, struct indexer_request *request, int percentage); /* Start working on a request */ void indexer_queue_request_work(struct indexer_request *request); /* Finish the request and free its memory. */ void indexer_queue_request_finish(struct indexer_queue *queue, struct indexer_request **request, bool success); #endif dovecot-2.2.33.2/src/indexer/worker-pool.c0000644000175000017500000001172013123174404015176 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "llist.h" #include "master-service.h" #include "worker-connection.h" #include "worker-pool.h" #define MAX_WORKER_IDLE_SECS (60*5) struct worker_connection_list { struct worker_connection_list *prev, *next; struct worker_connection *conn; time_t last_use; }; struct worker_pool { char *socket_path; indexer_status_callback_t *callback; unsigned int connection_count; struct worker_connection_list *busy_list, *idle_list; }; static void worker_connection_list_free(struct worker_pool *pool, struct worker_connection_list *list); struct worker_pool * worker_pool_init(const char *socket_path, indexer_status_callback_t *callback) { struct worker_pool *pool; pool = i_new(struct worker_pool, 1); pool->socket_path = i_strdup(socket_path); pool->callback = callback; return pool; } void worker_pool_deinit(struct worker_pool **_pool) { struct worker_pool *pool = *_pool; *_pool = NULL; while (pool->busy_list != NULL) { struct worker_connection_list *list = pool->busy_list; DLLIST_REMOVE(&pool->busy_list, list); worker_connection_list_free(pool, list); } while (pool->idle_list != NULL) { struct worker_connection_list *list = pool->idle_list; DLLIST_REMOVE(&pool->idle_list, list); worker_connection_list_free(pool, list); } i_free(pool->socket_path); i_free(pool); } bool worker_pool_have_busy_connections(struct worker_pool *pool) { return pool->busy_list != NULL; } static int worker_pool_add_connection(struct worker_pool *pool) { struct worker_connection *conn; struct worker_connection_list *list; conn = worker_connection_create(pool->socket_path, pool->callback); if (worker_connection_connect(conn) < 0) { worker_connection_destroy(&conn); return -1; } i_assert(pool->idle_list == NULL); list = i_new(struct worker_connection_list, 1); list->conn = conn; list->last_use = ioloop_time; pool->idle_list = list; pool->connection_count++; return 0; } static void worker_connection_list_free(struct worker_pool *pool, struct worker_connection_list *list) { i_assert(pool->connection_count > 0); pool->connection_count--; worker_connection_destroy(&list->conn); i_free(list); } static unsigned int worker_pool_find_max_connections(struct worker_pool *pool) { struct worker_connection_list *list; unsigned int limit; i_assert(pool->idle_list == NULL); if (pool->busy_list == NULL) return 1; for (list = pool->busy_list; list != NULL; list = list->next) { if (worker_connection_get_process_limit(list->conn, &limit)) return limit; } /* we have at least one connection that has already been created, but without having handshaked yet. wait until it's finished. */ return 0; } bool worker_pool_get_connection(struct worker_pool *pool, struct worker_connection **conn_r) { struct worker_connection_list *list; unsigned int max_connections; while (pool->idle_list != NULL && !worker_connection_is_connected(pool->idle_list->conn)) { list = pool->idle_list; DLLIST_REMOVE(&pool->idle_list, list); worker_connection_list_free(pool, list); } if (pool->idle_list == NULL) { max_connections = worker_pool_find_max_connections(pool); if (pool->connection_count >= max_connections) return FALSE; if (worker_pool_add_connection(pool) < 0) return FALSE; i_assert(pool->idle_list != NULL); } list = pool->idle_list; DLLIST_REMOVE(&pool->idle_list, list); DLLIST_PREPEND(&pool->busy_list, list); *conn_r = list->conn; return TRUE; } static void worker_pool_kill_idle_connections(struct worker_pool *pool) { struct worker_connection_list *list, *next; time_t kill_timestamp; kill_timestamp = ioloop_time - MAX_WORKER_IDLE_SECS; for (list = pool->idle_list; list != NULL; list = next) { next = list->next; if (list->last_use < kill_timestamp) { DLLIST_REMOVE(&pool->idle_list, list); worker_connection_list_free(pool, list); } } } void worker_pool_release_connection(struct worker_pool *pool, struct worker_connection *conn) { struct worker_connection_list *list; if (worker_connection_is_busy(conn)) { /* not finished with all queued requests yet */ return; } for (list = pool->busy_list; list != NULL; list = list->next) { if (list->conn == conn) break; } i_assert(list != NULL); DLLIST_REMOVE(&pool->busy_list, list); if (!worker_connection_is_connected(conn)) worker_connection_list_free(pool, list); else { DLLIST_PREPEND(&pool->idle_list, list); list->last_use = ioloop_time; worker_pool_kill_idle_connections(pool); } } struct worker_connection * worker_pool_find_username_connection(struct worker_pool *pool, const char *username) { struct worker_connection_list *list; const char *worker_user; for (list = pool->busy_list; list != NULL; list = list->next) { worker_user = worker_connection_get_username(list->conn); if (worker_user != NULL && strcmp(worker_user, username) == 0) return list->conn; } return NULL; } dovecot-2.2.33.2/src/indexer/worker-pool.h0000644000175000017500000000120713123174404015202 00000000000000#ifndef WORKER_POOL_H #define WORKER_POOL_H #include "indexer.h" struct worker_connection; struct worker_pool * worker_pool_init(const char *socket_path, indexer_status_callback_t *callback); void worker_pool_deinit(struct worker_pool **pool); bool worker_pool_have_busy_connections(struct worker_pool *pool); bool worker_pool_get_connection(struct worker_pool *pool, struct worker_connection **conn_r); void worker_pool_release_connection(struct worker_pool *pool, struct worker_connection *conn); struct worker_connection * worker_pool_find_username_connection(struct worker_pool *pool, const char *username); #endif dovecot-2.2.33.2/src/indexer/indexer-worker.c0000644000175000017500000000463013123174404015665 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "mail-storage-service.h" #include "mail-storage-settings.h" #include "master-service.h" #include "master-service-settings.h" #include "master-connection.h" static struct master_connection *master_conn; static struct mail_storage_service_ctx *storage_service; static void client_connected(struct master_service_connection *conn) { if (master_conn != NULL) { i_error("indexer-worker must be configured with client_limit=1"); return; } master_service_client_connection_accept(conn); master_conn = master_connection_create(conn->fd, storage_service); } static void drop_privileges(void) { struct restrict_access_settings set; const char *error; /* by default we don't drop any privileges, but keep running as root. */ restrict_access_get_env(&set); if (set.uid != 0) { /* open config connection before dropping privileges */ struct master_service_settings_input input; struct master_service_settings_output output; i_zero(&input); input.module = "mail"; input.service = "indexer-worker"; (void)master_service_settings_read(master_service, &input, &output, &error); } restrict_access_by_env(NULL, FALSE); } int main(int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; enum mail_storage_service_flags storage_service_flags = MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT | MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP | MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT; int c; master_service = master_service_init("indexer-worker", service_flags, &argc, &argv, "D"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS; break; default: return FATAL_DEFAULT; } } drop_privileges(); master_service_init_log(master_service, "indexer-worker: "); storage_service = mail_storage_service_init(master_service, NULL, storage_service_flags); restrict_access_allow_coredumps(TRUE); master_service_init_finish(master_service); master_service_run(master_service, client_connected); if (master_conn != NULL) master_connection_destroy(&master_conn); mail_storage_service_deinit(&storage_service); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/indexer/indexer.h0000644000175000017500000000034313123174404014360 00000000000000#ifndef INDEXER_H #define INDEXER_H /* percentage: -1 = failed, 0..99 = indexing in progress, 100 = done */ typedef void indexer_status_callback_t(int percentage, void *context); void indexer_refresh_proctitle(void); #endif dovecot-2.2.33.2/src/indexer/indexer.c0000644000175000017500000001013513165463624014366 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "restrict-access.h" #include "process-title.h" #include "master-service.h" #include "master-service-settings.h" #include "indexer-client.h" #include "indexer-queue.h" #include "worker-pool.h" #include "worker-connection.h" struct worker_request { struct worker_connection *conn; struct indexer_request *request; }; static const struct master_service_settings *set; static struct indexer_queue *queue; static struct worker_pool *worker_pool; static struct timeout *to_send_more; void indexer_refresh_proctitle(void) { if (!set->verbose_proctitle) return; process_title_set(t_strdup_printf("[%u clients, %u requests]", indexer_clients_get_count(), indexer_queue_count(queue))); } static bool idle_die(void) { return indexer_queue_is_empty(queue) && !worker_pool_have_busy_connections(worker_pool); } static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); (void)indexer_client_create(conn->fd, queue); } static void worker_send_request(struct worker_connection *conn, struct indexer_request *request) { struct worker_request *wrequest; wrequest = i_new(struct worker_request, 1); wrequest->conn = conn; wrequest->request = request; indexer_queue_request_work(request); worker_connection_request(conn, request, wrequest); } static void queue_try_send_more(struct indexer_queue *queue) { struct worker_connection *conn; struct indexer_request *request; if (to_send_more != NULL) timeout_remove(&to_send_more); while ((request = indexer_queue_request_peek(queue)) != NULL) { conn = worker_pool_find_username_connection(worker_pool, request->username); if (conn != NULL) { /* there is already a worker handling this user. it must be the one doing the indexing. use the same connection for sending this next request. */ } else { /* try to find an empty worker */ if (!worker_pool_get_connection(worker_pool, &conn)) break; } indexer_queue_request_remove(queue); worker_send_request(conn, request); } } static void queue_listen_callback(struct indexer_queue *queue) { queue_try_send_more(queue); } static void worker_status_callback(int percentage, void *context) { struct worker_request *request = context; if (percentage >= 0 && percentage < 100) { indexer_queue_request_status(queue, request->request, percentage); return; } indexer_queue_request_finish(queue, &request->request, percentage == 100); if (worker_pool != NULL) /* not in deinit */ worker_pool_release_connection(worker_pool, request->conn); i_free(request); /* if this was the last request for the connection, we can send more through it. delay it a bit, since we may be coming here from worker_connection_disconnect() and we want to finish it up. */ if (to_send_more == NULL) to_send_more = timeout_add_short(0, queue_try_send_more, queue); } int main(int argc, char *argv[]) { const char *error; master_service = master_service_init("indexer", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, NULL, &error) < 0) i_fatal("Error reading configuration: %s", error); set = master_service_settings_get(master_service); master_service_init_log(master_service, "indexer: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); master_service_set_idle_die_callback(master_service, idle_die); queue = indexer_queue_init(indexer_client_status_callback); indexer_queue_set_listen_callback(queue, queue_listen_callback); worker_pool = worker_pool_init("indexer-worker", worker_status_callback); master_service_init_finish(master_service); master_service_run(master_service, client_connected); indexer_queue_cancel_all(queue); indexer_clients_destroy_all(); worker_pool_deinit(&worker_pool); indexer_queue_deinit(&queue); if (to_send_more != NULL) timeout_remove(&to_send_more); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/indexer/indexer-client.h0000644000175000017500000000050113123174404015630 00000000000000#ifndef INDEXER_CLIENT_H #define INDEXER_CLIENT_H struct indexer_queue; struct indexer_client * indexer_client_create(int fd, struct indexer_queue *queue); void indexer_client_status_callback(int percentage, void *context); unsigned int indexer_clients_get_count(void); void indexer_clients_destroy_all(void); #endif dovecot-2.2.33.2/src/indexer/indexer-client.c0000644000175000017500000001464613165463624015655 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "master-service.h" #include "indexer-queue.h" #include "indexer-client.h" #include #define MAX_INBUF_SIZE (1024*64) #define INDEXER_CLIENT_PROTOCOL_MAJOR_VERSION 1 #define INDEXER_CLIENT_PROTOCOL_MINOR_VERSION 0 struct indexer_client { struct indexer_client *prev, *next; int refcount; struct indexer_queue *queue; int fd; struct istream *input; struct ostream *output; struct io *io; unsigned int version_received:1; unsigned int handshaked:1; unsigned int destroyed:1; }; struct indexer_client_request { struct indexer_client *client; unsigned int tag; }; static struct indexer_client *clients = NULL; static unsigned int clients_count = 0; static void indexer_client_destroy(struct indexer_client *client); static void indexer_client_ref(struct indexer_client *client); static void indexer_client_unref(struct indexer_client *client); static const char *const* indexer_client_next_line(struct indexer_client *client) { const char *line; char **args; unsigned int i; line = i_stream_next_line(client->input); if (line == NULL) return NULL; args = p_strsplit(pool_datastack_create(), line, "\t"); for (i = 0; args[i] != NULL; i++) args[i] = str_tabunescape(args[i]); return (void *)args; } static int indexer_client_request_queue(struct indexer_client *client, bool append, const char *const *args, const char **error_r) { struct indexer_client_request *ctx = NULL; const char *session_id = NULL; unsigned int tag, max_recent_msgs; /* [ []] */ if (str_array_length(args) < 3) { *error_r = "Wrong parameter count"; return -1; } if (str_to_uint(args[0], &tag) < 0) { *error_r = "Invalid tag"; return -1; } if (args[3] == NULL) max_recent_msgs = 0; else if (str_to_uint(args[3], &max_recent_msgs) < 0) { *error_r = "Invalid max_recent_msgs"; return -1; } else { session_id = args[4]; } if (tag != 0) { ctx = i_new(struct indexer_client_request, 1); ctx->client = client; ctx->tag = tag; indexer_client_ref(client); } indexer_queue_append(client->queue, append, args[1], args[2], session_id, max_recent_msgs, ctx); o_stream_nsend_str(client->output, t_strdup_printf("%u\tOK\n", tag)); return 0; } static int indexer_client_request_optimize(struct indexer_client *client, const char *const *args, const char **error_r) { struct indexer_client_request *ctx = NULL; unsigned int tag; /* */ if (str_array_length(args) != 3) { *error_r = "Wrong parameter count"; return -1; } if (str_to_uint(args[0], &tag) < 0) { *error_r = "Invalid tag"; return -1; } if (tag != 0) { ctx = i_new(struct indexer_client_request, 1); ctx->client = client; ctx->tag = tag; indexer_client_ref(client); } indexer_queue_append_optimize(client->queue, args[1], args[2], ctx); o_stream_nsend_str(client->output, t_strdup_printf("%u\tOK\n", tag)); return 0; } static int indexer_client_request(struct indexer_client *client, const char *const *args, const char **error_r) { const char *cmd = args[0]; args++; if (strcmp(cmd, "APPEND") == 0) return indexer_client_request_queue(client, TRUE, args, error_r); else if (strcmp(cmd, "PREPEND") == 0) return indexer_client_request_queue(client, FALSE, args, error_r); else if (strcmp(cmd, "OPTIMIZE") == 0) return indexer_client_request_optimize(client, args, error_r); else { *error_r = t_strconcat("Unknown command: ", cmd, NULL); return -1; } } static void indexer_client_input(struct indexer_client *client) { const char *line, *const *args, *error; switch (i_stream_read(client->input)) { case -2: i_error("BUG: Client connection sent too much data"); indexer_client_destroy(client); return; case -1: indexer_client_destroy(client); return; } if (!client->version_received) { if ((line = i_stream_next_line(client->input)) == NULL) return; if (!version_string_verify(line, "indexer", INDEXER_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Client not compatible with this server " "(mixed old and new binaries?)"); indexer_client_destroy(client); return; } client->version_received = TRUE; } while ((args = indexer_client_next_line(client)) != NULL) { if (args[0] != NULL) { if (indexer_client_request(client, args, &error) < 0) { i_error("Client input error: %s", error); indexer_client_destroy(client); break; } } } } void indexer_client_status_callback(int percentage, void *context) { struct indexer_client_request *ctx = context; T_BEGIN { o_stream_nsend_str(ctx->client->output, t_strdup_printf("%u\t%d\n", ctx->tag, percentage)); } T_END; if (percentage < 0 || percentage == 100) { indexer_client_unref(ctx->client); i_free(ctx); } } struct indexer_client * indexer_client_create(int fd, struct indexer_queue *queue) { struct indexer_client *client; client = i_new(struct indexer_client, 1); client->refcount = 1; client->queue = queue; client->fd = fd; client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); client->io = io_add(fd, IO_READ, indexer_client_input, client); DLLIST_PREPEND(&clients, client); clients_count++; indexer_refresh_proctitle(); return client; } static void indexer_client_destroy(struct indexer_client *client) { if (client->destroyed) return; client->destroyed = TRUE; DLLIST_REMOVE(&clients, client); io_remove(&client->io); i_stream_close(client->input); o_stream_close(client->output); if (close(client->fd) < 0) i_error("close(client) failed: %m"); client->fd = -1; indexer_client_unref(client); clients_count--; master_service_client_connection_destroyed(master_service); indexer_refresh_proctitle(); } static void indexer_client_ref(struct indexer_client *client) { i_assert(client->refcount > 0); client->refcount++; } static void indexer_client_unref(struct indexer_client *client) { i_assert(client->refcount > 0); if (--client->refcount > 0) return; i_stream_destroy(&client->input); o_stream_destroy(&client->output); i_free(client); } unsigned int indexer_clients_get_count(void) { return clients_count; } void indexer_clients_destroy_all(void) { while (clients != NULL) indexer_client_destroy(clients); } dovecot-2.2.33.2/src/indexer/worker-connection.h0000644000175000017500000000254413123174404016375 00000000000000#ifndef WORKER_CONNECTION_H #define WORKER_CONNECTION_H #include "indexer.h" struct indexer_request; struct worker_connection * worker_connection_create(const char *socket_path, indexer_status_callback_t *callback); void worker_connection_destroy(struct worker_connection **conn); int worker_connection_connect(struct worker_connection *conn); /* Returns TRUE if worker is connected to (not necessarily handshaked yet) */ bool worker_connection_is_connected(struct worker_connection *conn); /* After initial handshake the worker process tells how many of its kind can be at maximum. This returns the value, of FALSE if handshake isn't finished yet. */ bool worker_connection_get_process_limit(struct worker_connection *conn, unsigned int *limit_r); /* Send a new indexing request for username+mailbox. The status callback is called as necessary with the given context. Requests can be queued, but only for the same username. */ void worker_connection_request(struct worker_connection *conn, const struct indexer_request *request, void *context); /* Returns TRUE if a request is being handled. */ bool worker_connection_is_busy(struct worker_connection *conn); /* Returns username of the currently pending requests, or NULL if there are none. */ const char *worker_connection_get_username(struct worker_connection *conn); #endif dovecot-2.2.33.2/src/indexer/Makefile.am0000644000175000017500000000162013165463624014617 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = indexer indexer-worker AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -DPKG_RUNDIR=\""$(rundir)"\" indexer_LDADD = $(LIBDOVECOT) indexer_DEPENDENCIES = $(LIBDOVECOT_DEPS) indexer_SOURCES = \ indexer.c \ indexer-client.c \ indexer-queue.c \ indexer-settings.c \ worker-connection.c \ worker-pool.c indexer_worker_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) indexer_worker_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) indexer_worker_SOURCES = \ indexer-worker.c \ indexer-worker-settings.c \ master-connection.c noinst_HEADERS = \ indexer.h \ indexer-client.h \ indexer-queue.h \ master-connection.h \ worker-connection.h \ worker-pool.h dovecot-2.2.33.2/src/util/0002755000175000017500000000000013172375612012162 500000000000000dovecot-2.2.33.2/src/util/Makefile.in0000644000175000017500000006156413172375575014171 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = rawlog$(EXEEXT) script$(EXEEXT) \ script-login$(EXEEXT) $(am__EXEEXT_1) gdbhelper$(EXEEXT) \ maildirlock$(EXEEXT) subdir = src/util ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @TCPWRAPPERS_TRUE@am__EXEEXT_1 = tcpwrap$(EXEEXT) am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_gdbhelper_OBJECTS = gdbhelper.$(OBJEXT) gdbhelper_OBJECTS = $(am_gdbhelper_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_maildirlock_OBJECTS = maildirlock.$(OBJEXT) maildirlock_OBJECTS = $(am_maildirlock_OBJECTS) am_rawlog_OBJECTS = rawlog.$(OBJEXT) rawlog_OBJECTS = $(am_rawlog_OBJECTS) am_script_OBJECTS = script.$(OBJEXT) script_OBJECTS = $(am_script_OBJECTS) am_script_login_OBJECTS = script-login.$(OBJEXT) script_login_OBJECTS = $(am_script_login_OBJECTS) am__tcpwrap_SOURCES_DIST = tcpwrap.c tcpwrap-settings.c @TCPWRAPPERS_TRUE@am_tcpwrap_OBJECTS = tcpwrap.$(OBJEXT) \ @TCPWRAPPERS_TRUE@ tcpwrap-settings.$(OBJEXT) tcpwrap_OBJECTS = $(am_tcpwrap_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(gdbhelper_SOURCES) $(maildirlock_SOURCES) \ $(rawlog_SOURCES) $(script_SOURCES) $(script_login_SOURCES) \ $(tcpwrap_SOURCES) DIST_SOURCES = $(gdbhelper_SOURCES) $(maildirlock_SOURCES) \ $(rawlog_SOURCES) $(script_SOURCES) $(script_login_SOURCES) \ $(am__tcpwrap_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/auth \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" rawlog_LDADD = $(LIBDOVECOT) rawlog_DEPENDENCIES = $(LIBDOVECOT_DEPS) rawlog_SOURCES = \ rawlog.c script_login_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) script_login_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) script_login_SOURCES = \ script-login.c script_LDADD = $(LIBDOVECOT) script_DEPENDENCIES = $(LIBDOVECOT_DEPS) script_SOURCES = \ script.c @TCPWRAPPERS_TRUE@TCPWRAP_BIN = tcpwrap @TCPWRAPPERS_TRUE@tcpwrap_LDADD = $(LIBDOVECOT) $(LIBWRAP_LIBS) @TCPWRAPPERS_TRUE@tcpwrap_DEPENDENCIES = $(LIBDOVECOT_DEPS) @TCPWRAPPERS_TRUE@tcpwrap_SOURCES = \ @TCPWRAPPERS_TRUE@ tcpwrap.c \ @TCPWRAPPERS_TRUE@ tcpwrap-settings.c gdbhelper_LDADD = $(LIBDOVECOT) gdbhelper_DEPENDENCIES = $(LIBDOVECOT_DEPS) gdbhelper_SOURCES = \ gdbhelper.c maildirlock_LDADD = $(LIBDOVECOT) maildirlock_DEPENDENCIES = $(LIBDOVECOT_DEPS) maildirlock_SOURCES = \ maildirlock.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/util/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/util/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list gdbhelper$(EXEEXT): $(gdbhelper_OBJECTS) $(gdbhelper_DEPENDENCIES) $(EXTRA_gdbhelper_DEPENDENCIES) @rm -f gdbhelper$(EXEEXT) $(AM_V_CCLD)$(LINK) $(gdbhelper_OBJECTS) $(gdbhelper_LDADD) $(LIBS) maildirlock$(EXEEXT): $(maildirlock_OBJECTS) $(maildirlock_DEPENDENCIES) $(EXTRA_maildirlock_DEPENDENCIES) @rm -f maildirlock$(EXEEXT) $(AM_V_CCLD)$(LINK) $(maildirlock_OBJECTS) $(maildirlock_LDADD) $(LIBS) rawlog$(EXEEXT): $(rawlog_OBJECTS) $(rawlog_DEPENDENCIES) $(EXTRA_rawlog_DEPENDENCIES) @rm -f rawlog$(EXEEXT) $(AM_V_CCLD)$(LINK) $(rawlog_OBJECTS) $(rawlog_LDADD) $(LIBS) script$(EXEEXT): $(script_OBJECTS) $(script_DEPENDENCIES) $(EXTRA_script_DEPENDENCIES) @rm -f script$(EXEEXT) $(AM_V_CCLD)$(LINK) $(script_OBJECTS) $(script_LDADD) $(LIBS) script-login$(EXEEXT): $(script_login_OBJECTS) $(script_login_DEPENDENCIES) $(EXTRA_script_login_DEPENDENCIES) @rm -f script-login$(EXEEXT) $(AM_V_CCLD)$(LINK) $(script_login_OBJECTS) $(script_login_LDADD) $(LIBS) tcpwrap$(EXEEXT): $(tcpwrap_OBJECTS) $(tcpwrap_DEPENDENCIES) $(EXTRA_tcpwrap_DEPENDENCIES) @rm -f tcpwrap$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tcpwrap_OBJECTS) $(tcpwrap_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbhelper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildirlock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rawlog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-login.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpwrap-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpwrap.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/util/script-login.c0000644000175000017500000001527713165463624014675 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "env-util.h" #include "execv-const.h" #include "fdpass.h" #include "restrict-access.h" #include "str.h" #include "strescape.h" #include "settings-parser.h" #include "mail-storage-service.h" #include "master-interface.h" #include "master-service.h" #include "master-service-settings.h" #include #define SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR 1 #define SCRIPT_LOGIN_READ_TIMEOUT_SECS 10 #define ENV_USERDB_KEYS "USERDB_KEYS" #define SCRIPT_COMM_FD 3 static const char **exec_args; static bool drop_to_userdb_privileges = FALSE; static void client_connected(struct master_service_connection *conn) { enum mail_storage_service_flags flags = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; string_t *instr, *keys; const char **args, *key, *value, *error, *version_line, *data_line; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; struct mail_storage_service_user *user; char buf[1024]; unsigned int i, socket_count; int fd = -1; ssize_t ret; alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); net_set_nonblock(conn->fd, FALSE); instr = t_str_new(1024); ret = fd_read(conn->fd, buf, sizeof(buf), &fd); while (ret > 0) { str_append_n(instr, buf, ret); if (buf[ret-1] == '\n' && strchr(str_c(instr), '\n')[1] != '\0') { str_truncate(instr, str_len(instr)-1); break; } ret = read(conn->fd, buf, sizeof(buf)); } version_line = str_c(instr); data_line = strchr(version_line, '\n'); if (data_line != NULL) version_line = t_strdup_until(version_line, data_line++); else version_line = NULL; if (ret > 0 || version_line != NULL) { if (version_line == NULL || !version_string_verify(version_line, "script-login", SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } if (ret <= 0) { if (ret < 0) i_fatal("read() failed: %m"); else i_fatal("read() failed: disconnected"); } if (fd == -1) i_fatal("client fd not received"); alarm(0); /* put everything to environment */ env_clean(); keys = t_str_new(256); args = t_strsplit_tab(data_line); if (str_array_length(args) < 3) i_fatal("Missing input fields"); i = 0; i_zero(&input); input.module = "mail"; /* need to get mail_uid, mail_gid */ input.service = "script-login"; (void)net_addr2ip(args[i++], &input.local_ip); (void)net_addr2ip(args[i++], &input.remote_ip); input.username = args[i++]; input.userdb_fields = args + i; env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL)); env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL)); env_put(t_strconcat("USER=", input.username, NULL)); for (; args[i] != NULL; i++) { args[i] = str_tabunescape(t_strdup_noconst(args[i])); value = strchr(args[i], '='); if (value != NULL) { key = t_str_ucase(t_strdup_until(args[i], value)); env_put(t_strconcat(key, value, NULL)); str_printfa(keys, "%s ", key); } } env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL)); master_service_init_log(master_service, t_strdup_printf("script-login(%s): ", input.username)); if (drop_to_userdb_privileges) { service_ctx = mail_storage_service_init(master_service, NULL, flags); if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) i_fatal("%s", error); mail_storage_service_restrict_setenv(service_ctx, user); /* we can't exec anything in a chroot */ env_remove("RESTRICT_CHROOT"); restrict_access_by_env(getenv("HOME"), TRUE); } if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_fatal("close() failed: %m"); if (conn->fd != SCRIPT_COMM_FD) { if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) i_fatal("dup2() failed: %m"); if (close(conn->fd) < 0) i_fatal("close() failed: %m"); } /* close all listener sockets */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); execvp_const(exec_args[0], exec_args); } static void script_execute_finish(void) { const char *keys_str, *username, *const *keys, *value; string_t *reply = t_str_new(512); ssize_t ret; keys_str = getenv(ENV_USERDB_KEYS); if (keys_str == NULL) i_fatal(ENV_USERDB_KEYS" environment missing"); username = getenv("USER"); if (username == NULL) i_fatal("USER environment missing"); str_append(reply, username); for (keys = t_strsplit_spaces(keys_str, " "); *keys != NULL; keys++) { value = getenv(t_str_ucase(*keys)); if (value != NULL) { str_append_c(reply, '\t'); str_append_tabescaped(reply, t_strconcat(t_str_lcase(*keys), "=", value, NULL)); } } str_append_c(reply, '\n'); /* finish by sending the fd to the mail process */ ret = fd_send(SCRIPT_COMM_FD, STDOUT_FILENO, str_data(reply), str_len(reply)); if (ret == (ssize_t)str_len(reply)) { /* success */ } else { if (ret < 0) i_error("fd_send() failed: %m"); else i_error("fd_send() sent partial output"); /* exit with 0 even though we failed. non-0 exit just makes master log an unnecessary error. */ } } int main(int argc, char *argv[]) { enum master_service_flags flags = 0; int i, c; if (getenv(MASTER_IS_PARENT_ENV) == NULL) flags |= MASTER_SERVICE_FLAG_STANDALONE; master_service = master_service_init("script-login", flags, &argc, &argv, "+d"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'd': drop_to_userdb_privileges = TRUE; break; default: return FATAL_DEFAULT; } } argc -= optind; argv += optind; master_service_init_log(master_service, "script-login: "); if (!drop_to_userdb_privileges && (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { /* drop to privileges defined by service settings */ restrict_access_by_env(NULL, FALSE); } master_service_init_finish(master_service); master_service_set_service_count(master_service, 1); if ((flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) { /* The last post-login script is calling us to finish login */ script_execute_finish(); } else { if (argv[0] == NULL) i_fatal("Missing script path"); exec_args = i_new(const char *, argc + 2); for (i = 0; i < argc; i++) exec_args[i] = argv[i]; exec_args[i] = PKG_LIBEXECDIR"/script-login"; exec_args[i+1] = NULL; if (exec_args[0][0] != '/') { exec_args[0] = t_strconcat(PKG_LIBEXECDIR"/", exec_args[0], NULL); } master_service_run(master_service, client_connected); } master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/util/rawlog.c0000644000175000017500000002611213165463624013544 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "fd-set-nonblock.h" #include "net.h" #include "str.h" #include "write-full.h" #include "istream.h" #include "ostream.h" #include "process-title.h" #include "restrict-access.h" #include "time-util.h" #include "master-service.h" #include #include #include #include #define OUTBUF_THRESHOLD IO_BLOCK_SIZE #define RAWLOG_TIMEOUT_FLUSH_MSECS 1000 static struct ioloop *ioloop; enum rawlog_flags { RAWLOG_FLAG_LOG_INPUT = 0x01, RAWLOG_FLAG_LOG_OUTPUT = 0x02, RAWLOG_FLAG_LOG_TIMESTAMPS = 0x04, RAWLOG_FLAG_LOG_BOUNDARIES = 0x10, RAWLOG_FLAG_LOG_IP_IN_FILENAME = 0x20 }; struct rawlog_proxy { int client_in_fd, client_out_fd, server_fd; struct io *client_io, *server_io; struct ostream *client_output, *server_output; struct timeout *to_flush; struct ostream *in_output, *out_output; enum rawlog_flags flags; bool prev_lf_in, prev_lf_out; }; static void rawlog_proxy_destroy(struct rawlog_proxy *proxy) { if (proxy->in_output != NULL) { o_stream_uncork(proxy->in_output); if (o_stream_nfinish(proxy->in_output) < 0) { i_error("write(in) failed: %s", o_stream_get_error(proxy->in_output)); } o_stream_destroy(&proxy->in_output); } if (proxy->out_output != NULL) { o_stream_uncork(proxy->out_output); if (o_stream_nfinish(proxy->out_output) < 0) { i_error("write(out) failed: %s", o_stream_get_error(proxy->out_output)); } o_stream_destroy(&proxy->out_output); } if (proxy->client_io != NULL) io_remove(&proxy->client_io); if (proxy->server_io != NULL) io_remove(&proxy->server_io); if (proxy->to_flush != NULL) timeout_remove(&proxy->to_flush); o_stream_destroy(&proxy->client_output); o_stream_destroy(&proxy->server_output); if (close(proxy->client_in_fd) < 0) i_error("close(client_in_fd) failed: %m"); if (close(proxy->client_out_fd) < 0) i_error("close(client_out_fd) failed: %m"); if (close(proxy->server_fd) < 0) i_error("close(server_fd) failed: %m"); i_free(proxy); io_loop_stop(ioloop); } static void write_with_timestamps(struct ostream *output, bool *prev_lf, const unsigned char *data, size_t size) { T_BEGIN { const char *timestamp = t_strdup_printf("%ld.%06lu ", (long)ioloop_timeval.tv_sec, (unsigned long)ioloop_timeval.tv_usec); string_t *str = t_str_new(size + 128); size_t i; if (*prev_lf) str_append(str, timestamp); for (i = 0; i < size; i++) { str_append_c(str, data[i]); if (data[i] == '\n' && i+1 != size) str_append(str, timestamp); } *prev_lf = data[i-1] == '\n'; o_stream_nsend(output, str_data(str), str_len(str)); } T_END; } static void proxy_flush_timeout(struct rawlog_proxy *proxy) { bool flushed = TRUE; if (o_stream_flush(proxy->in_output) == 0) flushed = FALSE; if (o_stream_flush(proxy->out_output) == 0) flushed = FALSE; if (flushed) timeout_remove(&proxy->to_flush); } static void proxy_write_data(struct rawlog_proxy *proxy, struct ostream *output, bool *prev_lf, const void *data, size_t size) { if (output == NULL || output->closed || size == 0) return; if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) o_stream_nsend_str(output, "<<<\n"); if ((proxy->flags & RAWLOG_FLAG_LOG_TIMESTAMPS) != 0) write_with_timestamps(output, prev_lf, data, size); else o_stream_nsend(output, data, size); if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) o_stream_nsend_str(output, ">>>\n"); if (proxy->to_flush == NULL) { proxy->to_flush = timeout_add(RAWLOG_TIMEOUT_FLUSH_MSECS, proxy_flush_timeout, proxy); } } static void proxy_write_in(struct rawlog_proxy *proxy, const void *data, size_t size) { proxy_write_data(proxy, proxy->in_output, &proxy->prev_lf_in, data, size); } static void proxy_write_out(struct rawlog_proxy *proxy, const void *data, size_t size) { proxy_write_data(proxy, proxy->out_output, &proxy->prev_lf_out, data, size); } static void server_input(struct rawlog_proxy *proxy) { unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret; if (o_stream_get_buffer_used_size(proxy->client_output) > OUTBUF_THRESHOLD) { /* client's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->server_io); return; } ret = net_receive(proxy->server_fd, buf, sizeof(buf)); if (ret > 0) { o_stream_nsend(proxy->client_output, buf, ret); proxy_write_out(proxy, buf, ret); } else if (ret <= 0) rawlog_proxy_destroy(proxy); } static void client_input(struct rawlog_proxy *proxy) { unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret; if (o_stream_get_buffer_used_size(proxy->server_output) > OUTBUF_THRESHOLD) { /* proxy's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->client_io); return; } ret = net_receive(proxy->client_in_fd, buf, sizeof(buf)); if (ret > 0) { o_stream_nsend(proxy->server_output, buf, ret); proxy_write_in(proxy, buf, ret); } else if (ret < 0) rawlog_proxy_destroy(proxy); } static int server_output(struct rawlog_proxy *proxy) { if (o_stream_flush(proxy->server_output) < 0) { rawlog_proxy_destroy(proxy); return 1; } if (proxy->client_io == NULL && o_stream_get_buffer_used_size(proxy->server_output) < OUTBUF_THRESHOLD) { /* there's again space in proxy's output buffer, so we can read more from client. */ proxy->client_io = io_add(proxy->client_in_fd, IO_READ, client_input, proxy); } return 1; } static int client_output(struct rawlog_proxy *proxy) { if (o_stream_flush(proxy->client_output) < 0) { rawlog_proxy_destroy(proxy); return 1; } if (proxy->server_io == NULL && o_stream_get_buffer_used_size(proxy->client_output) < OUTBUF_THRESHOLD) { /* there's again space in client's output buffer, so we can read more from proxy. */ proxy->server_io = io_add(proxy->server_fd, IO_READ, server_input, proxy); } return 1; } static void proxy_open_logs(struct rawlog_proxy *proxy, const char *path, const char *ip_addr) { const char *fname, *timestamp; string_t *path_prefix; int fd; timestamp = t_strflocaltime("%Y%m%d-%H%M%S", time(NULL)); path_prefix = t_str_new(128); str_printfa(path_prefix, "%s/", path); if (ip_addr != NULL && (proxy->flags & RAWLOG_FLAG_LOG_IP_IN_FILENAME) != 0) str_printfa(path_prefix, "%s-", ip_addr); str_printfa(path_prefix, "%s-%s", timestamp, dec2str(getpid())); if ((proxy->flags & RAWLOG_FLAG_LOG_INPUT) != 0) { fname = t_strdup_printf("%s.in", str_c(path_prefix)); fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd == -1) { i_error("rawlog_open: creat(%s): %m", fname); return; } proxy->in_output = o_stream_create_fd_file_autoclose(&fd, 0); o_stream_cork(proxy->in_output); } if ((proxy->flags & RAWLOG_FLAG_LOG_OUTPUT) != 0) { fname = t_strdup_printf("%s.out", str_c(path_prefix)); fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd == -1) { i_error("rawlog_open: creat(%s): %m", fname); o_stream_destroy(&proxy->in_output); return; } proxy->out_output = o_stream_create_fd_file_autoclose(&fd, 0); o_stream_cork(proxy->out_output); } } static struct rawlog_proxy * rawlog_proxy_create(int client_in_fd, int client_out_fd, int server_fd, const char *path, const char *ip_addr, enum rawlog_flags flags) { struct rawlog_proxy *proxy; proxy = i_new(struct rawlog_proxy, 1); proxy->server_fd = server_fd; proxy->server_output = o_stream_create_fd(server_fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(proxy->server_output, TRUE); o_stream_set_flush_callback(proxy->server_output, server_output, proxy); proxy->server_io = io_add(server_fd, IO_READ, server_input, proxy); proxy->client_in_fd = client_in_fd; proxy->client_out_fd = client_out_fd; proxy->client_output = o_stream_create_fd(client_out_fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(proxy->client_output, TRUE); proxy->client_io = io_add(proxy->client_in_fd, IO_READ, client_input, proxy); o_stream_set_flush_callback(proxy->client_output, client_output, proxy); fd_set_nonblock(client_in_fd, TRUE); fd_set_nonblock(client_out_fd, TRUE); proxy->flags = flags; proxy->prev_lf_in = proxy->prev_lf_out = TRUE; proxy_open_logs(proxy, path, ip_addr); return proxy; } static void rawlog_open(enum rawlog_flags flags) { const char *chroot_dir, *home, *path; struct stat st; int sfd[2]; pid_t pid; chroot_dir = getenv("RESTRICT_CHROOT"); home = getenv("HOME"); if (chroot_dir != NULL) home = t_strconcat(chroot_dir, home, NULL); else if (home == NULL) home = "."; /* see if we want rawlog */ path = t_strconcat(home, "/dovecot.rawlog", NULL); if (lstat(path, &st) < 0) { if (errno != ENOENT) i_warning("lstat() failed for %s: %m", path); else if (getenv("DEBUG") != NULL) i_info("rawlog: %s doesn't exist", path); return; } if (!S_ISDIR(st.st_mode)) { if (getenv("DEBUG") != NULL) i_info("rawlog: %s is not a directory", path); return; } if (chroot_dir != NULL) { /* we'll chroot soon. skip over the chroot in the path. */ path += strlen(chroot_dir); } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) i_fatal("socketpair() failed: %m"); fd_set_nonblock(sfd[0], TRUE); fd_set_nonblock(sfd[1], TRUE); pid = fork(); if (pid < 0) i_fatal("fork() failed: %m"); if (pid > 0) { /* parent */ if (dup2(sfd[1], 0) < 0) i_fatal("dup2(sfd, 0)"); if (dup2(sfd[1], 1) < 0) i_fatal("dup2(sfd, 1)"); i_close_fd(&sfd[0]); i_close_fd(&sfd[1]); return; } i_close_fd(&sfd[1]); restrict_access_by_env(getenv("HOME"), TRUE); process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"), dec2str(getppid()))); ioloop = io_loop_create(); (void)rawlog_proxy_create(0, 1, sfd[0], path, getenv("IP"), flags); io_loop_run(ioloop); io_loop_destroy(&ioloop); lib_deinit(); exit(0); } int main(int argc, char *argv[]) { char *executable, *p; enum rawlog_flags flags = RAWLOG_FLAG_LOG_INPUT | RAWLOG_FLAG_LOG_OUTPUT; int c; master_service = master_service_init("rawlog", 0, &argc, &argv, "+f:bIt"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'f': if (strcmp(optarg, "in") == 0) flags &= ~RAWLOG_FLAG_LOG_OUTPUT; else if (strcmp(optarg, "out") == 0) flags &= ~RAWLOG_FLAG_LOG_INPUT; else i_fatal("Invalid filter: %s", optarg); break; case 'b': flags |= RAWLOG_FLAG_LOG_BOUNDARIES; break; case 'I': flags |= RAWLOG_FLAG_LOG_IP_IN_FILENAME; break; case 't': flags |= RAWLOG_FLAG_LOG_TIMESTAMPS; break; default: return FATAL_DEFAULT; } } argc -= optind; argv += optind; if (argc < 1) i_fatal("Usage: rawlog [-f in|out] [-I] [-b] [-t] "); master_service_init_log(master_service, "rawlog: "); master_service_init_finish(master_service); executable = argv[0]; rawlog_open(flags); /* hide the executable path, it's ugly */ p = strrchr(argv[0], '/'); if (p != NULL) argv[0] = p+1; execv(executable, argv); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); /* not reached */ return FATAL_EXEC; } dovecot-2.2.33.2/src/util/maildirlock.c0000644000175000017500000000473413123174404014536 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "ioloop.h" #include "write-full.h" #include "file-dotlock.h" #include "index/maildir/maildir-uidlist.h" #include #include #include static struct dotlock_settings dotlock_settings = { .stale_timeout = MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT, .use_io_notify = TRUE }; static struct ioloop *ioloop; static void sig_die(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { io_loop_stop(ioloop); } static int maildir_lock(const char *path, unsigned int timeout, struct dotlock **dotlock_r) { dotlock_settings.timeout = timeout; dotlock_settings.use_excl_lock = getenv("DOTLOCK_USE_EXCL") != NULL; dotlock_settings.nfs_flush = getenv("MAIL_NFS_STORAGE") != NULL; path = t_strconcat(path, "/" MAILDIR_UIDLIST_NAME, NULL); return file_dotlock_create(&dotlock_settings, path, 0, dotlock_r); } int main(int argc, const char *argv[]) { struct dotlock *dotlock; unsigned int timeout; pid_t pid; int fd[2], ret; char c; if (argc != 3) { fprintf(stderr, "Usage: maildirlock \n" " - SIGTERM will release the lock.\n"); return 1; } if (pipe(fd) != 0) { fprintf(stderr, "pipe() failed: %s", strerror(errno)); return 1; } pid = fork(); if (pid == (pid_t)-1) { fprintf(stderr, "fork() failed: %s", strerror(errno)); return 1; } /* call lib_init() only after fork so that PID gets set correctly */ lib_init(); lib_signals_init(); ioloop = io_loop_create(); lib_signals_set_handler(SIGINT, LIBSIG_FLAG_DELAYED, sig_die, NULL); lib_signals_set_handler(SIGTERM, LIBSIG_FLAG_DELAYED, sig_die, NULL); if (pid != 0) { i_close_fd(&fd[1]); ret = read(fd[0], &c, 1); if (ret < 0) { i_error("read(pipe) failed: %m"); return 1; } if (ret != 1) { /* locking timed out */ return 1; } printf("%s", dec2str(pid)); return 0; } /* child process - stdout has to be closed so that caller knows when to stop reading it. */ if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (str_to_uint(argv[2], &timeout) < 0) i_fatal("Invalid timeout value: %s", argv[2]); if (maildir_lock(argv[1], timeout, &dotlock) <= 0) return 1; /* locked - send a byte */ if (write_full(fd[1], "", 1) < 0) i_fatal("write(pipe) failed: %m"); io_loop_run(ioloop); file_dotlock_delete(&dotlock); lib_signals_deinit(); io_loop_destroy(&ioloop); lib_deinit(); return 0; } dovecot-2.2.33.2/src/util/tcpwrap-settings.c0000644000175000017500000000132313123174404015551 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include #ifdef HAVE_LIBWRAP struct service_settings tcpwrap_service_settings = { .name = "tcpwrap", .protocol = "", .type = "", .executable = "tcpwrap", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #endif dovecot-2.2.33.2/src/util/script.c0000644000175000017500000001611013123174404013537 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "env-util.h" #include "execv-const.h" #include "write-full.h" #include "restrict-access.h" #include "master-interface.h" #include "master-service.h" #include #include #include #include #define SCRIPT_MAJOR_VERSION 4 #define SCRIPT_READ_TIMEOUT_SECS 10 static ARRAY_TYPE(const_string) exec_args; static const char **accepted_envs; static void script_verify_version(const char *line) { if (line == NULL || !version_string_verify(line, "script", SCRIPT_MAJOR_VERSION)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } static void exec_child(struct master_service_connection *conn, const char *const *args, const char *const *envs) { unsigned int i, socket_count; if (dup2(conn->fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(conn->fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); /* close all fds */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); if (close(conn->fd) < 0) i_error("close(conn->fd) failed: %m"); for (; *args != NULL; args++) { const char *arg = t_str_tabunescape(*args); array_append(&exec_args, &arg, 1); } array_append_zero(&exec_args); env_clean(); if (envs != NULL) { for(; *envs != NULL; envs++) env_put(*envs); } args = array_idx(&exec_args, 0); execvp_const(args[0], args); } static bool client_exec_script(struct master_service_connection *conn) { ARRAY_TYPE(const_string) envs; const char *const *args; string_t *input; void *buf; size_t prev_size, scanpos; bool header_complete = FALSE, noreply = FALSE; ssize_t ret; int status; pid_t pid; net_set_nonblock(conn->fd, FALSE); input = buffer_create_dynamic(pool_datastack_create(), IO_BLOCK_SIZE); /* Input contains: VERSION .. [alarm= ] "noreply" | "-" (or anything really) arg 1 arg 2 ... DATA This is quite a horrible protocol. If alarm is specified, it MUST be before "noreply". If "noreply" isn't given, something other string (typically "-") must be given which is eaten away. */ alarm(SCRIPT_READ_TIMEOUT_SECS); scanpos = 1; while (!header_complete) { const unsigned char *pos, *end; prev_size = input->used; buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE); /* peek in socket input buffer */ ret = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK); if (ret <= 0) { buffer_set_used_size(input, prev_size); if (strchr(str_c(input), '\n') != NULL) script_verify_version(t_strcut(str_c(input), '\n')); if (ret < 0) i_fatal("recv(MSG_PEEK) failed: %m"); i_fatal("recv(MSG_PEEK) failed: disconnected"); } /* scan for final \n\n */ pos = CONST_PTR_OFFSET(input->data, scanpos); end = CONST_PTR_OFFSET(input->data, prev_size + ret); for (; pos < end; pos++) { if (pos[-1] == '\n' && pos[0] == '\n') { header_complete = TRUE; pos++; break; } } scanpos = pos - (const unsigned char *)input->data; /* read data for real (up to and including \n\n) */ ret = recv(conn->fd, buf, scanpos-prev_size, 0); if (prev_size+ret != scanpos) { if (ret < 0) i_fatal("recv() failed: %m"); if (ret == 0) i_fatal("recv() failed: disconnected"); i_fatal("recv() failed: size of definitive recv() differs from peek"); } buffer_set_used_size(input, scanpos); } alarm(0); /* drop the last two LFs */ buffer_set_used_size(input, scanpos-2); args = t_strsplit(str_c(input), "\n"); script_verify_version(*args); args++; t_array_init(&envs, 16); if (*args != NULL) { const char *p; if (strncmp(*args, "alarm=", 6) == 0) { unsigned int seconds; if (str_to_uint(*args + 6, &seconds) < 0) i_fatal("invalid alarm option"); alarm(seconds); args++; } while (strncmp(*args, "env_", 4) == 0) { const char *envname, *env; env = t_str_tabunescape(*args+4); p = strchr(env, '='); if (p == NULL) i_fatal("invalid environment variable"); envname = t_strdup_until(*args+4, p); if (str_array_find(accepted_envs, envname)) array_append(&envs, &env, 1); args++; } if (strcmp(*args, "noreply") == 0) { noreply = TRUE; } if (**args == '\0') i_fatal("empty options"); args++; } array_append_zero(&envs); if (noreply) { /* no need to fork and check exit status */ exec_child(conn, args, array_idx(&envs, 0)); i_unreached(); } if ((pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); return FALSE; } if (pid == 0) { /* child */ exec_child(conn, args, array_idx(&envs, 0)); i_unreached(); } /* parent */ /* check script exit status */ if (waitpid(pid, &status, 0) < 0) { i_error("waitpid() failed: %m"); return FALSE; } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret != 0) { i_error("Script terminated abnormally, exit status %d", (int)ret); return FALSE; } } else if (WIFSIGNALED(status)) { i_error("Script terminated abnormally, signal %d", WTERMSIG(status)); return FALSE; } else if (WIFSTOPPED(status)) { i_fatal("Script stopped, signal %d", WSTOPSIG(status)); return FALSE; } else { i_fatal("Script terminated abnormally, return status %d", status); return FALSE; } return TRUE; } static void client_connected(struct master_service_connection *conn) { char response[2]; response[0] = client_exec_script(conn) ? '+' : '-'; response[1] = '\n'; if (write_full(conn->fd, &response, 2) < 0) i_error("write(response) failed: %m"); } int main(int argc, char *argv[]) { ARRAY_TYPE(const_string) aenvs; const char *binary; const char *const *envs; int c, i; master_service = master_service_init("script", 0, &argc, &argv, "+e:"); t_array_init(&aenvs, 16); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'e': envs = t_strsplit_spaces(optarg,", \t"); while (*envs != NULL) { array_append(&aenvs, envs, 1); envs++; } break; default: return FATAL_DEFAULT; } } argc -= optind; argv += optind; array_append_zero(&aenvs); accepted_envs = p_strarray_dup(default_pool, array_idx(&aenvs, 0)); master_service_init_log(master_service, "script: "); if (argv[0] == NULL) i_fatal("Missing script path"); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); master_service_init_finish(master_service); master_service_set_service_count(master_service, 1); if (argv[0][0] == '/') binary = argv[0]; else binary = t_strconcat(PKG_LIBEXECDIR"/", argv[0], NULL); i_array_init(&exec_args, argc + 16); array_append(&exec_args, &binary, 1); for (i = 1; i < argc; i++) { const char *arg = argv[i]; array_append(&exec_args, &arg, 1); } master_service_run(master_service, client_connected); array_free(&exec_args); i_free(accepted_envs); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/util/gdbhelper.c0000644000175000017500000000324613123174404014175 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include #include #include #include #include int main(int argc, char *argv[]) { pid_t pid = fork(); const char *path, *cmd; int fd_in[2], fd_out[2], fd_log, status; if (argc < 2) i_fatal("Usage: gdbhelper []"); switch (pid) { case 1: i_fatal("fork() failed: %m"); case 0: /* child */ (void)execvp(argv[1], argv+1); i_fatal("execvp(%s) failed: %m", argv[1]); default: if (pipe(fd_in) < 0 || pipe(fd_out) < 0) i_fatal("pipe() failed: %m"); cmd = "handle SIGPIPE nostop\n" "handle SIGALRM nostop\n" "handle SIG32 nostop\n" "cont\n" "bt full\n" "quit\n"; if (write(fd_in[1], cmd, strlen(cmd)) < 0) i_fatal("write() failed: %m"); if (dup2(fd_in[0], 0) < 0 || dup2(fd_out[1], 1) < 0 || dup2(fd_out[1], 2) < 0) i_fatal("dup2() failed: %m"); cmd = t_strdup_printf("gdb %s %s", argv[1], dec2str(pid)); if (system(cmd) < 0) i_fatal("system() failed: %m"); if (wait(&status) < 0) i_fatal("wait() failed: %m"); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { char buf[1024]; ssize_t ret; path = t_strdup_printf("/tmp/gdbhelper.%s.%s", dec2str(time(NULL)), dec2str(pid)); fd_log = open(path, O_CREAT | O_WRONLY, 0600); if (fd_log < 0) i_fatal("open(%s) failed: %m", path); while ((ret = read(fd_out[0], buf, sizeof(buf))) > 0) { if (write(fd_log, buf, ret) < 0) i_fatal("write(%s) failed: %m", path); } if (ret < 0) i_fatal("read(pipe) failed: %m"); i_close_fd(&fd_log); } } return 0; } dovecot-2.2.33.2/src/util/tcpwrap.c0000644000175000017500000000612413165463624013732 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "fdpass.h" #include "write-full.h" #include "restrict-access.h" #include "master-service.h" #include #include #include struct tcpwrap_client { int fd; struct io *io; struct timeout *to; }; #define INPUT_TIMEOUT_MSECS (1000*10) /* for tcpwrap library */ int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; static struct tcpwrap_client *tcpwrap_client = NULL; static void tcpwrap_client_destroy(struct tcpwrap_client **client); static void tcpwrap_client_handle(struct tcpwrap_client *client, int check_fd, const char *daemon_name) { struct request_info request; request_init(&request, RQ_DAEMON, daemon_name, RQ_FILE, check_fd, 0); fromhost(&request); if (!hosts_access(&request)) (void)write_full(client->fd, "0\n", 2); else (void)write_full(client->fd, "1\n", 2); exit(0); } static void tcpwrap_client_input(struct tcpwrap_client *client) { unsigned char buf[1024]; ssize_t ret; int check_fd = -1; ret = fd_read(client->fd, buf, sizeof(buf), &check_fd); if (ret <= 0) { i_error("fd_read() failed: %m"); } else if (ret > 1 && (size_t)ret < sizeof(buf) && buf[ret-1] == '\n') { tcpwrap_client_handle(client, check_fd, t_strndup(buf, ret-1)); } else { i_error("Invalid input from client"); } if (check_fd != -1) { if (close(check_fd) < 0) i_error("close(fdread fd) failed: %m"); } tcpwrap_client_destroy(&client); } static void tcpwrap_client_timeout(struct tcpwrap_client *client) { tcpwrap_client_destroy(&client); } static struct tcpwrap_client *tcpwrap_client_create(int fd) { struct tcpwrap_client *client; client = i_new(struct tcpwrap_client, 1); client->fd = fd; client->io = io_add(fd, IO_READ, tcpwrap_client_input, client); client->to = timeout_add(INPUT_TIMEOUT_MSECS, tcpwrap_client_timeout, client); return client; } static void tcpwrap_client_destroy(struct tcpwrap_client **_client) { struct tcpwrap_client *client = *_client; *_client = NULL; timeout_remove(&client->to); io_remove(&client->io); if (close(client->fd) < 0) i_error("close() failed: %m"); i_free(client); tcpwrap_client = NULL; master_service_client_connection_destroyed(master_service); } static void client_connected(struct master_service_connection *conn) { if (tcpwrap_client != NULL) { i_error("tcpwrap must be configured with client_limit=1"); return; } master_service_client_connection_accept(conn); tcpwrap_client = tcpwrap_client_create(conn->fd); } int main(int argc, char *argv[]) { master_service = master_service_init("tcpwrap", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; master_service_init_log(master_service, "tcpwrap: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); master_service_init_finish(master_service); master_service_run(master_service, client_connected); if (tcpwrap_client != NULL) tcpwrap_client_destroy(&tcpwrap_client); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/util/Makefile.am0000644000175000017500000000251513165463624014142 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = \ rawlog \ script \ script-login \ $(TCPWRAP_BIN) \ gdbhelper \ maildirlock AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/auth \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" rawlog_LDADD = $(LIBDOVECOT) rawlog_DEPENDENCIES = $(LIBDOVECOT_DEPS) rawlog_SOURCES = \ rawlog.c script_login_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) script_login_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) script_login_SOURCES = \ script-login.c script_LDADD = $(LIBDOVECOT) script_DEPENDENCIES = $(LIBDOVECOT_DEPS) script_SOURCES = \ script.c if TCPWRAPPERS TCPWRAP_BIN = tcpwrap tcpwrap_LDADD = $(LIBDOVECOT) $(LIBWRAP_LIBS) tcpwrap_DEPENDENCIES = $(LIBDOVECOT_DEPS) tcpwrap_SOURCES = \ tcpwrap.c \ tcpwrap-settings.c endif gdbhelper_LDADD = $(LIBDOVECOT) gdbhelper_DEPENDENCIES = $(LIBDOVECOT_DEPS) gdbhelper_SOURCES = \ gdbhelper.c maildirlock_LDADD = $(LIBDOVECOT) maildirlock_DEPENDENCIES = $(LIBDOVECOT_DEPS) maildirlock_SOURCES = \ maildirlock.c dovecot-2.2.33.2/src/imap-urlauth/0002755000175000017500000000000013172375612013615 500000000000000dovecot-2.2.33.2/src/imap-urlauth/Makefile.in0000644000175000017500000012322513172375572015612 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = imap-urlauth-login$(EXEEXT) \ imap-urlauth$(EXEEXT) imap-urlauth-worker$(EXEEXT) subdir = src/imap-urlauth ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_imap_urlauth_OBJECTS = imap_urlauth-imap-urlauth.$(OBJEXT) \ imap_urlauth-imap-urlauth-client.$(OBJEXT) \ imap_urlauth-imap-urlauth-settings.$(OBJEXT) imap_urlauth_OBJECTS = $(am_imap_urlauth_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = imap_urlauth_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(imap_urlauth_LDFLAGS) $(LDFLAGS) -o $@ am_imap_urlauth_login_OBJECTS = \ imap_urlauth_login-imap-urlauth-login.$(OBJEXT) \ imap_urlauth_login-imap-urlauth-login-settings.$(OBJEXT) imap_urlauth_login_OBJECTS = $(am_imap_urlauth_login_OBJECTS) am_imap_urlauth_worker_OBJECTS = \ imap_urlauth_worker-imap-urlauth-worker.$(OBJEXT) \ imap_urlauth_worker-imap-urlauth-worker-settings.$(OBJEXT) imap_urlauth_worker_OBJECTS = $(am_imap_urlauth_worker_OBJECTS) imap_urlauth_worker_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(imap_urlauth_worker_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(imap_urlauth_SOURCES) $(imap_urlauth_login_SOURCES) \ $(imap_urlauth_worker_SOURCES) DIST_SOURCES = $(imap_urlauth_SOURCES) $(imap_urlauth_login_SOURCES) \ $(imap_urlauth_worker_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # imap-urlauth-login imap_urlauth_login_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common imap_urlauth_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) imap_urlauth_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN_DEPS) \ $(LIBDOVECOT_DEPS) imap_urlauth_login_SOURCES = \ imap-urlauth-login.c \ imap-urlauth-login-settings.c # imap-urlauth imap_urlauth_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -DPKG_RUNDIR=\""$(rundir)"\" imap_urlauth_LDFLAGS = -export-dynamic imap_urlauth_LDADD = $(LIBDOVECOT) imap_urlauth_DEPENDENCIES = $(LIBDOVECOT_DEPS) imap_urlauth_SOURCES = \ imap-urlauth.c \ imap-urlauth-client.c \ imap-urlauth-settings.c # imap-urlauth-worker imap_urlauth_worker_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-imap-urlauth \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/login-common imap_urlauth_worker_LDFLAGS = -export-dynamic urlauth_libs = \ $(top_builddir)/src/lib-imap-urlauth/libimap-urlauth.la imap_urlauth_worker_LDADD = $(urlauth_libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT) imap_urlauth_worker_DEPENDENCIES = $(urlauth_libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_DEPS) imap_urlauth_worker_SOURCES = \ imap-urlauth-worker.c \ imap-urlauth-worker-settings.c noinst_HEADERS = \ imap-urlauth-client.h \ imap-urlauth-common.h \ imap-urlauth-settings.h \ imap-urlauth-login-settings.h \ imap-urlauth-worker-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/imap-urlauth/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/imap-urlauth/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list imap-urlauth$(EXEEXT): $(imap_urlauth_OBJECTS) $(imap_urlauth_DEPENDENCIES) $(EXTRA_imap_urlauth_DEPENDENCIES) @rm -f imap-urlauth$(EXEEXT) $(AM_V_CCLD)$(imap_urlauth_LINK) $(imap_urlauth_OBJECTS) $(imap_urlauth_LDADD) $(LIBS) imap-urlauth-login$(EXEEXT): $(imap_urlauth_login_OBJECTS) $(imap_urlauth_login_DEPENDENCIES) $(EXTRA_imap_urlauth_login_DEPENDENCIES) @rm -f imap-urlauth-login$(EXEEXT) $(AM_V_CCLD)$(LINK) $(imap_urlauth_login_OBJECTS) $(imap_urlauth_login_LDADD) $(LIBS) imap-urlauth-worker$(EXEEXT): $(imap_urlauth_worker_OBJECTS) $(imap_urlauth_worker_DEPENDENCIES) $(EXTRA_imap_urlauth_worker_DEPENDENCIES) @rm -f imap-urlauth-worker$(EXEEXT) $(AM_V_CCLD)$(imap_urlauth_worker_LINK) $(imap_urlauth_worker_OBJECTS) $(imap_urlauth_worker_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth-imap-urlauth-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth-imap-urlauth-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth-imap-urlauth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< imap_urlauth-imap-urlauth.o: imap-urlauth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth.o -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth.Tpo -c -o imap_urlauth-imap-urlauth.o `test -f 'imap-urlauth.c' || echo '$(srcdir)/'`imap-urlauth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth.c' object='imap_urlauth-imap-urlauth.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth.o `test -f 'imap-urlauth.c' || echo '$(srcdir)/'`imap-urlauth.c imap_urlauth-imap-urlauth.obj: imap-urlauth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth.obj -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth.Tpo -c -o imap_urlauth-imap-urlauth.obj `if test -f 'imap-urlauth.c'; then $(CYGPATH_W) 'imap-urlauth.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth.c' object='imap_urlauth-imap-urlauth.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth.obj `if test -f 'imap-urlauth.c'; then $(CYGPATH_W) 'imap-urlauth.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth.c'; fi` imap_urlauth-imap-urlauth-client.o: imap-urlauth-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth-client.o -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth-client.Tpo -c -o imap_urlauth-imap-urlauth-client.o `test -f 'imap-urlauth-client.c' || echo '$(srcdir)/'`imap-urlauth-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth-client.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-client.c' object='imap_urlauth-imap-urlauth-client.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth-client.o `test -f 'imap-urlauth-client.c' || echo '$(srcdir)/'`imap-urlauth-client.c imap_urlauth-imap-urlauth-client.obj: imap-urlauth-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth-client.obj -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth-client.Tpo -c -o imap_urlauth-imap-urlauth-client.obj `if test -f 'imap-urlauth-client.c'; then $(CYGPATH_W) 'imap-urlauth-client.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-client.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth-client.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-client.c' object='imap_urlauth-imap-urlauth-client.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth-client.obj `if test -f 'imap-urlauth-client.c'; then $(CYGPATH_W) 'imap-urlauth-client.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-client.c'; fi` imap_urlauth-imap-urlauth-settings.o: imap-urlauth-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth-settings.o -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Tpo -c -o imap_urlauth-imap-urlauth-settings.o `test -f 'imap-urlauth-settings.c' || echo '$(srcdir)/'`imap-urlauth-settings.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-settings.c' object='imap_urlauth-imap-urlauth-settings.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth-settings.o `test -f 'imap-urlauth-settings.c' || echo '$(srcdir)/'`imap-urlauth-settings.c imap_urlauth-imap-urlauth-settings.obj: imap-urlauth-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth-imap-urlauth-settings.obj -MD -MP -MF $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Tpo -c -o imap_urlauth-imap-urlauth-settings.obj `if test -f 'imap-urlauth-settings.c'; then $(CYGPATH_W) 'imap-urlauth-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-settings.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Tpo $(DEPDIR)/imap_urlauth-imap-urlauth-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-settings.c' object='imap_urlauth-imap-urlauth-settings.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth-imap-urlauth-settings.obj `if test -f 'imap-urlauth-settings.c'; then $(CYGPATH_W) 'imap-urlauth-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-settings.c'; fi` imap_urlauth_login-imap-urlauth-login.o: imap-urlauth-login.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_login-imap-urlauth-login.o -MD -MP -MF $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Tpo -c -o imap_urlauth_login-imap-urlauth-login.o `test -f 'imap-urlauth-login.c' || echo '$(srcdir)/'`imap-urlauth-login.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Tpo $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-login.c' object='imap_urlauth_login-imap-urlauth-login.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_login-imap-urlauth-login.o `test -f 'imap-urlauth-login.c' || echo '$(srcdir)/'`imap-urlauth-login.c imap_urlauth_login-imap-urlauth-login.obj: imap-urlauth-login.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_login-imap-urlauth-login.obj -MD -MP -MF $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Tpo -c -o imap_urlauth_login-imap-urlauth-login.obj `if test -f 'imap-urlauth-login.c'; then $(CYGPATH_W) 'imap-urlauth-login.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-login.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Tpo $(DEPDIR)/imap_urlauth_login-imap-urlauth-login.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-login.c' object='imap_urlauth_login-imap-urlauth-login.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_login-imap-urlauth-login.obj `if test -f 'imap-urlauth-login.c'; then $(CYGPATH_W) 'imap-urlauth-login.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-login.c'; fi` imap_urlauth_login-imap-urlauth-login-settings.o: imap-urlauth-login-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_login-imap-urlauth-login-settings.o -MD -MP -MF $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Tpo -c -o imap_urlauth_login-imap-urlauth-login-settings.o `test -f 'imap-urlauth-login-settings.c' || echo '$(srcdir)/'`imap-urlauth-login-settings.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Tpo $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-login-settings.c' object='imap_urlauth_login-imap-urlauth-login-settings.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_login-imap-urlauth-login-settings.o `test -f 'imap-urlauth-login-settings.c' || echo '$(srcdir)/'`imap-urlauth-login-settings.c imap_urlauth_login-imap-urlauth-login-settings.obj: imap-urlauth-login-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_login-imap-urlauth-login-settings.obj -MD -MP -MF $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Tpo -c -o imap_urlauth_login-imap-urlauth-login-settings.obj `if test -f 'imap-urlauth-login-settings.c'; then $(CYGPATH_W) 'imap-urlauth-login-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-login-settings.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Tpo $(DEPDIR)/imap_urlauth_login-imap-urlauth-login-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-login-settings.c' object='imap_urlauth_login-imap-urlauth-login-settings.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_login-imap-urlauth-login-settings.obj `if test -f 'imap-urlauth-login-settings.c'; then $(CYGPATH_W) 'imap-urlauth-login-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-login-settings.c'; fi` imap_urlauth_worker-imap-urlauth-worker.o: imap-urlauth-worker.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_worker-imap-urlauth-worker.o -MD -MP -MF $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Tpo -c -o imap_urlauth_worker-imap-urlauth-worker.o `test -f 'imap-urlauth-worker.c' || echo '$(srcdir)/'`imap-urlauth-worker.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Tpo $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-worker.c' object='imap_urlauth_worker-imap-urlauth-worker.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_worker-imap-urlauth-worker.o `test -f 'imap-urlauth-worker.c' || echo '$(srcdir)/'`imap-urlauth-worker.c imap_urlauth_worker-imap-urlauth-worker.obj: imap-urlauth-worker.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_worker-imap-urlauth-worker.obj -MD -MP -MF $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Tpo -c -o imap_urlauth_worker-imap-urlauth-worker.obj `if test -f 'imap-urlauth-worker.c'; then $(CYGPATH_W) 'imap-urlauth-worker.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-worker.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Tpo $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-worker.c' object='imap_urlauth_worker-imap-urlauth-worker.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_worker-imap-urlauth-worker.obj `if test -f 'imap-urlauth-worker.c'; then $(CYGPATH_W) 'imap-urlauth-worker.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-worker.c'; fi` imap_urlauth_worker-imap-urlauth-worker-settings.o: imap-urlauth-worker-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_worker-imap-urlauth-worker-settings.o -MD -MP -MF $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Tpo -c -o imap_urlauth_worker-imap-urlauth-worker-settings.o `test -f 'imap-urlauth-worker-settings.c' || echo '$(srcdir)/'`imap-urlauth-worker-settings.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Tpo $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-worker-settings.c' object='imap_urlauth_worker-imap-urlauth-worker-settings.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_worker-imap-urlauth-worker-settings.o `test -f 'imap-urlauth-worker-settings.c' || echo '$(srcdir)/'`imap-urlauth-worker-settings.c imap_urlauth_worker-imap-urlauth-worker-settings.obj: imap-urlauth-worker-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT imap_urlauth_worker-imap-urlauth-worker-settings.obj -MD -MP -MF $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Tpo -c -o imap_urlauth_worker-imap-urlauth-worker-settings.obj `if test -f 'imap-urlauth-worker-settings.c'; then $(CYGPATH_W) 'imap-urlauth-worker-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-worker-settings.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Tpo $(DEPDIR)/imap_urlauth_worker-imap-urlauth-worker-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap-urlauth-worker-settings.c' object='imap_urlauth_worker-imap-urlauth-worker-settings.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(imap_urlauth_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o imap_urlauth_worker-imap-urlauth-worker-settings.obj `if test -f 'imap-urlauth-worker-settings.c'; then $(CYGPATH_W) 'imap-urlauth-worker-settings.c'; else $(CYGPATH_W) '$(srcdir)/imap-urlauth-worker-settings.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-worker-settings.c0000644000175000017500000000456213123174404021453 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "imap-urlauth-worker-settings.h" #include #include /* */ static struct file_listener_settings imap_urlauth_worker_unix_listeners_array[] = { { "imap-urlauth-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *imap_urlauth_worker_unix_listeners[] = { &imap_urlauth_worker_unix_listeners_array[0] }; static buffer_t imap_urlauth_worker_unix_listeners_buf = { imap_urlauth_worker_unix_listeners, sizeof(imap_urlauth_worker_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_worker_service_settings = { .name = "imap-urlauth-worker", .protocol = "imap", .type = "", .executable = "imap-urlauth-worker", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_worker_unix_listeners_buf, sizeof(imap_urlauth_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_urlauth_worker_settings, name), NULL } static const struct setting_define imap_urlauth_worker_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, imap_urlauth_host), DEF(SET_IN_PORT, imap_urlauth_port), SETTING_DEFINE_LIST_END }; const struct imap_urlauth_worker_settings imap_urlauth_worker_default_settings = { .verbose_proctitle = FALSE, .imap_urlauth_host = "", .imap_urlauth_port = 143 }; static const struct setting_parser_info *imap_urlauth_worker_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info imap_urlauth_worker_setting_parser_info = { .module_name = "imap-urlauth-worker", .defines = imap_urlauth_worker_setting_defines, .defaults = &imap_urlauth_worker_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_urlauth_worker_settings), .parent_offset = (size_t)-1, .dependencies = imap_urlauth_worker_setting_dependencies }; dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-common.h0000644000175000017500000000043713123174404017576 00000000000000#ifndef IMAP_URLAUTH_COMMON_H #define IMAP_URLAUTH_COMMON_H #include "lib.h" #include "imap-urlauth-client.h" #include "imap-urlauth-settings.h" extern bool verbose_proctitle; extern struct mail_storage_service_ctx *storage_service; void imap_urlauth_refresh_proctitle(void); #endif dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-settings.h0000644000175000017500000000075613123174404020152 00000000000000#ifndef IMAP_URLAUTH_SETTINGS_H #define IMAP_URLAUTH_SETTINGS_H struct mail_user_settings; struct imap_urlauth_settings { const char *base_dir; bool mail_debug; bool verbose_proctitle; /* imap_urlauth: */ const char *imap_urlauth_logout_format; const char *imap_urlauth_submit_user; const char *imap_urlauth_stream_user; }; extern const struct imap_urlauth_settings imap_urlauth_default_settings; extern const struct setting_parser_info imap_urlauth_setting_parser_info; #endif dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-login.c0000644000175000017500000001165513165463624017430 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "str.h" #include "strescape.h" #include "base64.h" #include "net.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "master-service.h" #include "auth-client.h" #include "client-common.h" #include "imap-urlauth-login-settings.h" #define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 1 #define IMAP_URLAUTH_PROTOCOL_MINOR_VERSION 0 struct imap_urlauth_client { struct client common; const struct imap_urlauth_login_settings *set; unsigned int version_received:1; }; static void imap_urlauth_client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply ATTR_UNUSED, const char *text ATTR_UNUSED) { if (result != CLIENT_AUTH_RESULT_SUCCESS) { /* failed or otherwise invalid status */ client_send_raw(client, "FAILED\n"); client_destroy(client, "Disconnected: Authentication failed"); } else { /* authentication succeeded */ } } static void imap_urlauth_client_handle_input(struct client *client) { #define AUTH_ARG_COUNT 5 struct imap_urlauth_client *uauth_client = (struct imap_urlauth_client *)client; struct net_unix_cred cred; const char *line; const char *const *args; pid_t pid; if (!uauth_client->version_received) { if ((line = i_stream_next_line(client->input)) == NULL) return; if (!version_string_verify(line, "imap-urlauth", IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION)) { i_error("IMAP URLAUTH client not compatible with this server " "(mixed old and new binaries?) %s", line); client_destroy(client, "Disconnected: Version mismatch"); return; } uauth_client->version_received = TRUE; } if ((line = i_stream_next_line(client->input)) == NULL) return; /* read authentication info from input; "AUTH"\t\t\t\t */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < AUTH_ARG_COUNT || strcmp(args[0], "AUTH") != 0 || str_to_pid(args[1], &pid) < 0) { i_error("IMAP URLAUTH client sent unexpected AUTH input: %s", line); client_destroy(client, "Disconnected: Unexpected input"); return; } /* verify session pid if possible */ if (net_getunixcred(client->fd, &cred) == 0 && cred.pid != (pid_t)-1 && pid != cred.pid) { i_error("IMAP URLAUTH client sent invalid session pid %ld in AUTH request: " "it did not match peer credentials (pid=%ld, uid=%ld)", (long)pid, (long)cred.pid, (long)cred.uid); client_destroy(client, "Disconnected: Invalid AUTH request"); return; } T_BEGIN { string_t *auth_data = t_str_new(128); string_t *init_resp; unsigned int i; str_append(auth_data, "imap"); for (i = 1; i < AUTH_ARG_COUNT; i++) { str_append_c(auth_data, '\0'); str_append(auth_data, args[i]); } init_resp = t_str_new(256); base64_encode(str_data(auth_data), str_len(auth_data), init_resp); (void)client_auth_begin(client, "DOVECOT-TOKEN", str_c(init_resp)); } T_END; } static void imap_urlauth_client_input(struct client *client) { if (!client_read(client)) return; client_ref(client); o_stream_cork(client->output); if (!auth_client_is_connected(auth_client)) { /* we're not currently connected to auth process - don't allow any commands */ if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); client->input_blocked = TRUE; } else { imap_urlauth_client_handle_input(client); } o_stream_uncork(client->output); client_unref(&client); } static struct client *imap_urlauth_client_alloc(pool_t pool) { struct imap_urlauth_client *uauth_client; uauth_client = p_new(pool, struct imap_urlauth_client, 1); return &uauth_client->common; } static void imap_urlauth_client_create (struct client *client, void **other_sets) { struct imap_urlauth_client *uauth_client = (struct imap_urlauth_client *)client; uauth_client->set = other_sets[0]; client->io = io_add(client->fd, IO_READ, client_input, client); } static void imap_urlauth_login_preinit(void) { login_set_roots = imap_urlauth_login_setting_roots; } static void imap_urlauth_login_init(void) { } static void imap_urlauth_login_deinit(void) { clients_destroy_all(); } static struct client_vfuncs imap_urlauth_vfuncs = { imap_urlauth_client_alloc, imap_urlauth_client_create, NULL, NULL, NULL, NULL, NULL, NULL, imap_urlauth_client_input, NULL, NULL, imap_urlauth_client_auth_result, NULL, NULL, NULL, NULL, client_common_send_raw_data, NULL, client_common_default_free, }; static const struct login_binary imap_urlauth_login_binary = { .protocol = "imap-urlauth", .process_name = "imap-urlauth-login", .default_login_socket = LOGIN_TOKEN_DEFAULT_SOCKET, .client_vfuncs = &imap_urlauth_vfuncs, .preinit = imap_urlauth_login_preinit, .init = imap_urlauth_login_init, .deinit = imap_urlauth_login_deinit, }; int main(int argc, char *argv[]) { return login_binary_run(&imap_urlauth_login_binary, argc, argv); } dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-client.c0000644000175000017500000002227313165463624017574 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "imap-urlauth-common.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "fdpass.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "eacces-error.h" #include "llist.h" #include "hostpid.h" #include "execv-const.h" #include "env-util.h" #include "var-expand.h" #include "restrict-access.h" #include "master-service.h" #include "master-interface.h" #include #include #define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 1 #define IMAP_URLAUTH_PROTOCOL_MINOR_VERSION 0 #define IMAP_URLAUTH_WORKER_SOCKET "imap-urlauth-worker" /* max. length of input lines (URLs) */ #define MAX_INBUF_SIZE 2048 /* Disconnect client after idling this many milliseconds */ #define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000) #define USER_EXECUTABLE "imap-urlauth-worker" #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) struct client *imap_urlauth_clients; unsigned int imap_urlauth_client_count; static int client_worker_connect(struct client *client); static void client_worker_disconnect(struct client *client); static void client_worker_input(struct client *client); int client_create(const char *username, int fd_in, int fd_out, const struct imap_urlauth_settings *set, struct client **client_r) { struct client *client; const char *app; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); client = i_new(struct client, 1); client->fd_in = fd_in; client->fd_out = fd_out; client->fd_ctrl = -1; client->set = set; if (client_worker_connect(client) < 0) { i_free(client); return -1; } /* determine user's special privileges */ i_array_init(&client->access_apps, 4); if (username != NULL) { if (set->imap_urlauth_submit_user != NULL && strcmp(set->imap_urlauth_submit_user, username) == 0) { if (set->mail_debug) i_debug("User %s has URLAUTH submit access", username); app = "submit+"; array_append(&client->access_apps, &app, 1); } if (set->imap_urlauth_stream_user != NULL && strcmp(set->imap_urlauth_stream_user, username) == 0) { if (set->mail_debug) i_debug("User %s has URLAUTH stream access", username); app = "stream"; array_append(&client->access_apps, &app, 1); } } if (username != NULL) client->username = i_strdup(username); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); imap_urlauth_client_count++; DLLIST_PREPEND(&imap_urlauth_clients, client); imap_urlauth_refresh_proctitle(); *client_r = client; return 0; } void client_send_line(struct client *client, const char *fmt, ...) { va_list va; ssize_t ret; if (client->output->closed) return; va_start(va, fmt); T_BEGIN { string_t *str; str = t_str_new(256); str_vprintfa(str, fmt, va); str_append(str, "\n"); ret = o_stream_send(client->output, str_data(str), str_len(str)); i_assert(ret < 0 || (size_t)ret == str_len(str)); } T_END; va_end(va); } static int client_worker_connect(struct client *client) { static const char handshake[] = "VERSION\timap-urlauth-worker\t1\t0\n"; const char *socket_path; ssize_t ret; unsigned char data; socket_path = t_strconcat(client->set->base_dir, "/"IMAP_URLAUTH_WORKER_SOCKET, NULL); if (client->set->mail_debug) i_debug("Connecting to worker socket %s", socket_path); client->fd_ctrl = net_connect_unix_with_retries(socket_path, 1000); if (client->fd_ctrl < 0) { if (errno == EACCES) { i_error("imap-urlauth-client: %s", eacces_error_get("net_connect_unix", socket_path)); } else { i_error("imap-urlauth-client: net_connect_unix(%s) failed: %m", socket_path); } return -1; } /* transfer one or two fds */ data = (client->fd_in == client->fd_out ? '0' : '1'); ret = fd_send(client->fd_ctrl, client->fd_in, &data, sizeof(data)); if (ret > 0 && client->fd_in != client->fd_out) { data = '0'; ret = fd_send(client->fd_ctrl, client->fd_out, &data, sizeof(data)); } if (ret <= 0) { if (ret < 0) { i_error("fd_send(%s, %d) failed: %m", socket_path, client->fd_ctrl); } else { i_error("fd_send(%s, %d) failed to send byte", socket_path, client->fd_ctrl); } client_worker_disconnect(client); return -1; } client->ctrl_output = o_stream_create_fd(client->fd_ctrl, (size_t)-1, FALSE); /* send protocol version handshake */ if (o_stream_send_str(client->ctrl_output, handshake) < 0) { i_error("Error sending handshake to imap-urlauth worker: %m"); client_worker_disconnect(client); return -1; } client->ctrl_input = i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE, FALSE); client->ctrl_io = io_add(client->fd_ctrl, IO_READ, client_worker_input, client); return 0; } void client_worker_disconnect(struct client *client) { client->worker_state = IMAP_URLAUTH_WORKER_STATE_INACTIVE; if (client->ctrl_io != NULL) io_remove(&client->ctrl_io); if (client->ctrl_output != NULL) o_stream_destroy(&client->ctrl_output); if (client->ctrl_input != NULL) i_stream_destroy(&client->ctrl_input); if (client->fd_ctrl >= 0) { net_disconnect(client->fd_ctrl); client->fd_ctrl = -1; } } static int client_worker_input_line(struct client *client, const char *response) { const char *const *apps; unsigned int count, i; bool restart; string_t *str; int ret; switch (client->worker_state) { case IMAP_URLAUTH_WORKER_STATE_INACTIVE: if (strcasecmp(response, "OK") != 0) { client_disconnect(client, "Worker handshake failed"); return -1; } client->worker_state = IMAP_URLAUTH_WORKER_STATE_CONNECTED; str = t_str_new(256); str_append(str, "ACCESS\t"); if (client->username != NULL) str_append_tabescaped(str, client->username); if (client->set->mail_debug) str_append(str, "\tdebug"); if (array_count(&client->access_apps) > 0) { str_append(str, "\tapps="); apps = array_get(&client->access_apps, &count); str_append(str, apps[0]); for (i = 1; i < count; i++) { str_append_c(str, ','); str_append_tabescaped(str, apps[i]); } } str_append(str, "\n"); ret = o_stream_send(client->ctrl_output, str_data(str), str_len(str)); i_assert(ret < 0 || (size_t)ret == str_len(str)); if (ret < 0) { client_disconnect(client, "Failed to send ACCESS control command to worker"); return -1; } break; case IMAP_URLAUTH_WORKER_STATE_CONNECTED: if (strcasecmp(response, "OK") != 0) { client_disconnect(client, "Failed to negotiate access parameters"); return -1; } client->worker_state = IMAP_URLAUTH_WORKER_STATE_ACTIVE; break; case IMAP_URLAUTH_WORKER_STATE_ACTIVE: restart = TRUE; if (strcasecmp(response, "DISCONNECTED") == 0) { /* worker detected client disconnect */ restart = FALSE; } else if (strcasecmp(response, "FINISHED") != 0) { /* unknown response */ client_disconnect(client, "Worker finished with unknown response"); return -1; } if (client->set->mail_debug) i_debug("Worker finished successfully"); if (restart) { /* connect to new worker for accessing different user */ client_worker_disconnect(client); if (client_worker_connect(client) < 0) { client_disconnect(client, "Failed to connect to new worker"); return -1; } /* indicate success of "END" command */ client_send_line(client, "OK"); } else { client_disconnect(client, "Client disconnected"); } return -1; default: i_unreached(); } return 0; } void client_worker_input(struct client *client) { struct istream *input = client->ctrl_input; const char *line; if (input->closed) { /* disconnected */ client_disconnect(client, "Worker disconnected unexpectedly"); return; } switch (i_stream_read(input)) { case -1: /* disconnected */ client_disconnect(client, "Worker disconnected unexpectedly"); return; case -2: /* input buffer full */ client_disconnect(client, "Worker sent too large input"); return; } while ((line = i_stream_next_line(input)) != NULL) { if (client_worker_input_line(client, line) < 0) return; } } void client_destroy(struct client *client, const char *reason) { i_set_failure_prefix("%s: ", master_service_get_name(master_service)); if (!client->disconnected) { if (reason == NULL) reason = "Connection closed"; i_info("Disconnected: %s", reason); } imap_urlauth_client_count--; DLLIST_REMOVE(&imap_urlauth_clients, client); if (client->to_idle != NULL) timeout_remove(&client->to_idle); client_worker_disconnect(client); o_stream_destroy(&client->output); fd_close_maybe_stdio(&client->fd_in, &client->fd_out); if (client->username != NULL) i_free(client->username); array_free(&client->access_apps); i_free(client); master_service_client_connection_destroyed(master_service); imap_urlauth_refresh_proctitle(); } static void client_destroy_timeout(struct client *client) { client_destroy(client, NULL); } void client_disconnect(struct client *client, const char *reason) { if (client->disconnected) return; client->disconnected = TRUE; i_info("Disconnected: %s", reason); client->to_idle = timeout_add(0, client_destroy_timeout, client); } void clients_destroy_all(void) { while (imap_urlauth_clients != NULL) client_destroy(imap_urlauth_clients, "Server shutting down."); } dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-client.h0000644000175000017500000000215213165463624017573 00000000000000#ifndef IMAP_URLAUTH_CLIENT_H #define IMAP_URLAUTH_CLIENT_H struct client; struct mail_storage; enum imap_urlauth_worker_state { IMAP_URLAUTH_WORKER_STATE_INACTIVE = 0, IMAP_URLAUTH_WORKER_STATE_CONNECTED, IMAP_URLAUTH_WORKER_STATE_ACTIVE, }; struct client { struct client *prev, *next; int fd_in, fd_out, fd_ctrl; struct io *ctrl_io; struct ostream *output, *ctrl_output; struct istream *ctrl_input; struct timeout *to_idle; char *username; ARRAY_TYPE(const_string) access_apps; /* settings: */ const struct imap_urlauth_settings *set; enum imap_urlauth_worker_state worker_state; unsigned int disconnected:1; }; extern struct client *imap_urlauth_clients; extern unsigned int imap_urlauth_client_count; int client_create(const char *username, int fd_in, int fd_out, const struct imap_urlauth_settings *set, struct client **client_r); void client_destroy(struct client *client, const char *reason); void client_send_line(struct client *client, const char *fmt, ...) ATTR_FORMAT(2, 3); void client_disconnect(struct client *client, const char *reason); void clients_destroy_all(void); #endif dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-login-settings.h0000644000175000017500000000024213123174404021246 00000000000000#ifndef IMAP_URLAUTH_LOGIN_SETTINGS_H #define IMAP_URLAUTH_LOGIN_SETTINGS_H extern const struct setting_parser_info *imap_urlauth_login_setting_roots[]; #endif dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-worker.c0000644000175000017500000006357613165463624017642 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "fdpass.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "str-sanitize.h" #include "strescape.h" #include "llist.h" #include "hostpid.h" #include "var-expand.h" #include "process-title.h" #include "randgen.h" #include "restrict-access.h" #include "settings-parser.h" #include "master-service.h" #include "master-interface.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "imap-url.h" #include "imap-msgpart-url.h" #include "imap-urlauth.h" #include "imap-urlauth-fetch.h" #include "imap-urlauth-worker-settings.h" #include #include #define MAX_CTRL_HANDSHAKE 255 /* max. length of input lines (URLs) */ #define MAX_INBUF_SIZE 2048 /* Disconnect client after idling this many milliseconds */ #define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000) #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) #define IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION 1 #define IMAP_URLAUTH_WORKER_PROTOCOL_MINOR_VERSION 0 struct client { struct client *prev, *next; int fd_in, fd_out, fd_ctrl; struct io *io, *ctrl_io; struct istream *input, *ctrl_input; struct ostream *output, *ctrl_output; struct timeout *to_idle; char *access_user; ARRAY_TYPE(string) access_apps; struct mail_storage_service_user *service_user; struct mail_user *mail_user; struct imap_urlauth_context *urlauth_ctx; struct imap_msgpart_url *url; struct istream *msg_part_input; uoff_t msg_part_size; /* settings: */ const struct imap_urlauth_worker_settings *set; const struct mail_storage_settings *mail_set; unsigned int debug:1; unsigned int finished:1; unsigned int waiting_input:1; unsigned int version_received:1; unsigned int access_received:1; unsigned int access_anonymous:1; }; static bool verbose_proctitle = FALSE; static struct mail_storage_service_ctx *storage_service; struct client *imap_urlauth_worker_clients; unsigned int imap_urlauth_worker_client_count; static void client_destroy(struct client *client); static void client_abort(struct client *client, const char *reason); static int client_run_url(struct client *client); static void client_input(struct client *client); static bool client_handle_input(struct client *client); static int client_output(struct client *client); static void client_ctrl_input(struct client *client); static void imap_urlauth_worker_refresh_proctitle(void) { struct client *client = imap_urlauth_worker_clients; string_t *title; if (!verbose_proctitle) return; title = t_str_new(128); str_append_c(title, '['); switch (imap_urlauth_worker_client_count) { case 0: str_append(title, "idling"); break; case 1: if (client->mail_user == NULL) str_append(title, client->access_user); else { str_append(title, client->access_user); str_append(title, "->"); str_append(title, client->mail_user->username); } break; default: str_printfa(title, "%u connections", imap_urlauth_worker_client_count); break; } str_append_c(title, ']'); process_title_set(str_c(title)); } static void client_idle_timeout(struct client *client) { if (client->url != NULL) { client_abort(client, "Session closed for inactivity in reading our output"); } else { client_destroy(client); } } static struct client *client_create(int fd) { struct client *client; /* always use nonblocking I/O */ net_set_nonblock(fd, TRUE); client = i_new(struct client, 1); i_array_init(&client->access_apps, 16); client->fd_in = -1; client->fd_out = -1; client->fd_ctrl = fd; client->access_anonymous = TRUE; /* default until overridden */ client->ctrl_io = io_add(fd, IO_READ, client_ctrl_input, client); client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, client_idle_timeout, client); imap_urlauth_worker_client_count++; DLLIST_PREPEND(&imap_urlauth_worker_clients, client); imap_urlauth_worker_refresh_proctitle(); return client; } static struct client * client_create_standalone(const char *access_user, const char *const *access_applications, int fd_in, int fd_out, bool debug) { struct client *client; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); client = i_new(struct client, 1); i_array_init(&client->access_apps, 16); client->fd_in = fd_in; client->fd_out = fd_out; client->fd_ctrl = -1; if (access_user != NULL && *access_user != '\0') client->access_user = i_strdup(access_user); else { client->access_user = i_strdup("anonymous"); client->access_anonymous = TRUE; } if (access_applications != NULL) { const char *const *apps = access_applications; for (; *apps != NULL; apps++) { char *app = i_strdup(*apps); array_append(&client->access_apps, &app, 1); } } client->debug = debug; client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); client->io = io_add(fd_in, IO_READ, client_input, client); client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, client_idle_timeout, client); o_stream_set_flush_callback(client->output, client_output, client); imap_urlauth_worker_client_count++; DLLIST_PREPEND(&imap_urlauth_worker_clients, client); i_set_failure_prefix("imap-urlauth[%s](%s): ", my_pid, client->access_user); return client; } static void client_abort(struct client *client, const char *reason) { i_error("%s", reason); client_destroy(client); } static void client_destroy(struct client *client) { char **app; i_set_failure_prefix("imap-urlauth[%s](%s): ", my_pid, client->access_user); if (client->url != NULL) { /* deinitialize url */ i_stream_close(client->input); o_stream_close(client->output); (void)client_run_url(client); i_assert(client->url == NULL); } imap_urlauth_worker_client_count--; DLLIST_REMOVE(&imap_urlauth_worker_clients, client); if (client->urlauth_ctx != NULL) imap_urlauth_deinit(&client->urlauth_ctx); if (client->mail_user != NULL) mail_user_unref(&client->mail_user); if (client->io != NULL) io_remove(&client->io); if (client->ctrl_io != NULL) io_remove(&client->ctrl_io); if (client->to_idle != NULL) timeout_remove(&client->to_idle); if (client->input != NULL) i_stream_destroy(&client->input); if (client->output != NULL) o_stream_destroy(&client->output); if (client->ctrl_input != NULL) i_stream_destroy(&client->ctrl_input); if (client->ctrl_output != NULL) o_stream_destroy(&client->ctrl_output); fd_close_maybe_stdio(&client->fd_in, &client->fd_out); if (client->fd_ctrl >= 0) net_disconnect(client->fd_ctrl); if (client->service_user != NULL) mail_storage_service_user_unref(&client->service_user); i_free(client->access_user); array_foreach_modifiable(&client->access_apps, app) i_free(*app); array_free(&client->access_apps); i_free(client); imap_urlauth_worker_refresh_proctitle(); master_service_client_connection_destroyed(master_service); } static int client_run_url(struct client *client) { const unsigned char *data; size_t size; ssize_t ret = 0; while (i_stream_read_data(client->msg_part_input, &data, &size, 0) > 0) { if ((ret = o_stream_send(client->output, data, size)) < 0) break; i_stream_skip(client->msg_part_input, ret); if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) return 0; } } if (client->output->closed || ret < 0) { imap_msgpart_url_free(&client->url); return -1; } if (client->msg_part_input->eof) { (void)o_stream_send(client->output, "\n", 1); imap_msgpart_url_free(&client->url); return 1; } return 0; } static void clients_destroy_all(void) { while (imap_urlauth_worker_clients != NULL) client_destroy(imap_urlauth_worker_clients); } static void ATTR_FORMAT(2, 3) client_send_line(struct client *client, const char *fmt, ...) { va_list va; ssize_t ret; if (client->output->closed) return; va_start(va, fmt); T_BEGIN { string_t *str; str = t_str_new(256); str_vprintfa(str, fmt, va); str_append(str, "\n"); ret = o_stream_send(client->output, str_data(str), str_len(str)); i_assert(ret < 0 || (size_t)ret == str_len(str)); } T_END; va_end(va); } static int client_fetch_urlpart(struct client *client, const char *url, enum imap_urlauth_fetch_flags url_flags, const char **bpstruct_r, bool *binary_with_nuls_r, const char **errormsg_r) { const char *error; struct imap_msgpart_open_result mpresult; enum mail_error error_code; int ret; *bpstruct_r = NULL; *errormsg_r = NULL; *binary_with_nuls_r = FALSE; ret = imap_urlauth_fetch(client->urlauth_ctx, url, &client->url, &error_code, &error); if (ret <= 0) { if (ret < 0) return -1; error = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s", url, error); if (client->debug) i_debug("%s", error); /* don't leak info about existence/accessibility of mailboxes */ if (error_code == MAIL_ERROR_PARAMS) *errormsg_r = error; return 0; } if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) imap_msgpart_url_set_decode_to_binary(client->url); if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) { ret = imap_msgpart_url_get_bodypartstructure(client->url, bpstruct_r, &error); if (ret <= 0) { *errormsg_r = t_strdup_printf( "Failed to read URLAUTH \"%s\": %s", url, error); if (client->debug) i_debug("%s", *errormsg_r); return ret; } } /* if requested, read the message part the URL points to */ if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 || (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) { ret = imap_msgpart_url_read_part(client->url, &mpresult, &error); if (ret <= 0) { *errormsg_r = t_strdup_printf( "Failed to read URLAUTH \"%s\": %s", url, error); if (client->debug) i_debug("%s", *errormsg_r); return ret; } client->msg_part_size = mpresult.size; client->msg_part_input = mpresult.input; *binary_with_nuls_r = mpresult.binary_decoded_input_has_nuls; } return 1; } static int client_fetch_url(struct client *client, const char *url, enum imap_urlauth_fetch_flags url_flags) { string_t *response; const char *bpstruct, *errormsg; bool binary_with_nuls; int ret; i_assert(client->url == NULL); client->msg_part_size = 0; client->msg_part_input = NULL; if (client->debug) i_debug("Fetching URLAUTH %s", url); /* fetch URL */ ret = client_fetch_urlpart(client, url, url_flags, &bpstruct, &binary_with_nuls, &errormsg); if (ret <= 0) { /* fetch failed */ if (client->url != NULL) imap_msgpart_url_free(&client->url); /* don't send error details to anonymous users: just to be sure that no information about the target user account is unduly leaked. */ if (client->access_anonymous || errormsg == NULL) client_send_line(client, "NO"); else { client_send_line(client, "NO\terror=%s", str_tabescape(errormsg)); } if (ret < 0) { /* fetch failed badly */ client_abort(client, "Session aborted: Fatal failure while fetching URL"); } return 0; } response = t_str_new(256); str_append(response, "OK"); if (binary_with_nuls) str_append(response, "\thasnuls"); if (bpstruct != NULL) { str_append(response, "\tbpstruct="); str_append(response, str_tabescape(bpstruct)); if (client->debug) { i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct); } } /* return content */ o_stream_cork(client->output); if (client->msg_part_size == 0 || client->msg_part_input == NULL) { /* empty */ str_append(response, "\t0"); client_send_line(client, "%s", str_c(response)); imap_msgpart_url_free(&client->url); client->url = NULL; if (client->debug) i_debug("Fetched URLAUTH yielded empty result"); } else { /* actual content */ str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size); client_send_line(client, "%s", str_c(response)); if (client->debug) { i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes " "of %smessage data", client->msg_part_size, (binary_with_nuls ? "binary " : "")); } if (client_run_url(client) < 0) { client_abort(client, "Session aborted: Fatal failure while transferring URL"); return 0; } } if (client->url != NULL) { /* URL not finished */ o_stream_set_flush_pending(client->output, TRUE); client->waiting_input = TRUE; } o_stream_uncork(client->output); return client->url != NULL ? 0 : 1; } static int client_handle_command(struct client *client, const char *cmd, const char *const *args, const char **error_r) { int ret; *error_r = NULL; /* "URL"["\tbody"]["\tbinary"]["\tbpstruct"]"\t": fetch URL (meta)data */ if (strcmp(cmd, "URL") == 0) { enum imap_urlauth_fetch_flags url_flags = 0; const char *url; if (*args == NULL) { *error_r = "URL: Missing URL parameter"; return -1; } url = *args; args++; while (*args != NULL) { if (strcasecmp(*args, "body") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODY; else if (strcasecmp(*args, "binary") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BINARY; else if (strcasecmp(*args, "bpstruct") == 0) url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE; args++; } if (url_flags == 0) url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY; T_BEGIN { ret = client_fetch_url(client, url, url_flags); } T_END; return ret; } /* "END": unselect current user (closes worker) */ if (strcmp(cmd, "END") == 0) { if (args[0] != NULL) { *error_r = "END: Invalid number of parameters"; return -1; } client->finished = TRUE; if (client->ctrl_output != NULL) (void)o_stream_send_str(client->ctrl_output, "FINISHED\n"); client_destroy(client); return 0; } *error_r = t_strconcat("Unknown or inappropriate command: ", cmd, NULL); return -1; } static int client_handle_user_command(struct client *client, const char *cmd, const char *const *args, const char **error_r) { struct mail_storage_service_input input; struct imap_urlauth_worker_settings *set; struct mail_storage_service_user *user; struct imap_urlauth_config config; struct mail_user *mail_user; const char *error; unsigned int count; int ret; /* "USER\t" */ *error_r = NULL; /* check command syntax */ if (strcmp(cmd, "USER") != 0) { *error_r = t_strconcat("Unknown or inappropriate command: ", cmd, NULL); return -1; } if (args[0] == NULL || args[1] != NULL) { *error_r = "USER: Invalid number of parameters"; return -1; } /* lookup user */ i_zero(&input); input.module = "imap-urlauth-worker"; input.service = "imap-urlauth-worker"; input.username = args[0]; if (client->debug) i_debug("Looking up user %s", input.username); ret = mail_storage_service_lookup_next(storage_service, &input, &user, &mail_user, &error); if (ret < 0) { i_error("Failed to lookup user %s: %s", input.username, error); client_abort(client, "Session aborted: Failed to lookup user"); return 0; } else if (ret == 0) { if (client->debug) i_debug("User %s doesn't exist", input.username); client_send_line(client, "NO"); return 1; } client->debug = mail_user->mail_debug = client->debug || mail_user->mail_debug; /* drop privileges */ restrict_access_allow_coredumps(TRUE); set = mail_storage_service_user_get_set(user)[1]; settings_var_expand(&imap_urlauth_worker_setting_parser_info, set, mail_user->pool, mail_user_var_expand_table(mail_user)); if (set->verbose_proctitle) { verbose_proctitle = TRUE; imap_urlauth_worker_refresh_proctitle(); } client->service_user = user; client->mail_user = mail_user; client->set = set; if (client->debug) { i_debug("Found user account `%s' on behalf of user `%s'", mail_user->username, client->access_user); } /* initialize urlauth context */ if (*set->imap_urlauth_host == '\0') { i_error("imap_urlauth_host setting is not configured for user %s", mail_user->username); client_send_line(client, "NO"); client_abort(client, "Session aborted: URLAUTH not configured"); return 0; } i_zero(&config); config.url_host = set->imap_urlauth_host; config.url_port = set->imap_urlauth_port; config.access_user = client->access_user; config.access_anonymous = client->access_anonymous; config.access_applications = (const void *)array_get(&client->access_apps, &count); client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config); if (client->debug) { i_debug("Providing access to user account `%s' on behalf of `%s'", mail_user->username, client->access_user); } i_set_failure_prefix("imap-urlauth[%s](%s->%s): ", my_pid, client->access_user, mail_user->username); client_send_line(client, "OK"); return 1; } static bool client_handle_input(struct client *client) { const char *line, *cmd, *error; int ret; if (client->url != NULL) { /* we're still processing a URL. wait until it's finished. */ io_remove(&client->io); client->io = NULL; client->waiting_input = TRUE; return TRUE; } if (client->io == NULL) { client->io = io_add(client->fd_in, IO_READ, client_input, client); } client->waiting_input = FALSE; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -1: /* disconnected */ if (client->ctrl_output != NULL) (void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n"); client_destroy(client); return FALSE; case -2: /* line too long, kill it */ client_abort(client, "Session aborted: Input line too long"); return FALSE; } while ((line = i_stream_next_line(client->input)) != NULL) { const char *const *args = t_strsplit_tabescaped(line); if (args[0] == NULL) continue; cmd = args[0]; args++; if (client->mail_user == NULL) ret = client_handle_user_command(client, cmd, args, &error); else ret = client_handle_command(client, cmd, args, &error); if (ret <= 0) { if (ret == 0) break; i_error("Client input error: %s", error); client_abort(client, "Session aborted: Unexpected input"); return FALSE; } } return TRUE; } static void client_input(struct client *client) { (void)client_handle_input(client); } static int client_output(struct client *client) { o_stream_cork(client->output); if (o_stream_flush(client->output) < 0) { if (client->ctrl_output != NULL) (void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n"); client_destroy(client); return 1; } timeout_reset(client->to_idle); if (client->url != NULL) { if (client_run_url(client) < 0) { client_destroy(client); return 1; } if (client->url == NULL && client->waiting_input) { if (!client_handle_input(client)) { /* client got destroyed */ return 1; } } } o_stream_uncork(client->output); if (client->url != NULL) { /* url not finished yet */ return 0; } else if (client->io == NULL) { /* data still in output buffer, get back here to add IO */ return 0; } else { return 1; } } static int client_ctrl_read_fds(struct client *client) { unsigned char data = 0; ssize_t ret = 1; if (client->fd_in == -1) { ret = fd_read(client->fd_ctrl, &data, sizeof(data), &client->fd_in); if (ret > 0 && data == '0') client->fd_out = client->fd_in; } if (ret > 0 && client->fd_out == -1) { ret = fd_read(client->fd_ctrl, &data, sizeof(data), &client->fd_out); } if (ret == 0) { /* unexpectedly disconnected */ client_destroy(client); return 0; } else if (ret < 0) { if (errno == EAGAIN) return 0; i_error("fd_read() failed: %m"); return -1; } else if (data != '0') { i_error("fd_read() returned invalid byte 0x%2x", data); return -1; } if (client->fd_in == -1 || client->fd_out == -1) { i_error("Handshake is missing a file descriptor"); return -1; } client->ctrl_input = i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE, FALSE); client->ctrl_output = o_stream_create_fd(client->fd_ctrl, (size_t)-1, FALSE); return 1; } static void client_ctrl_input(struct client *client) { const char *const *args; const char *line; int ret; timeout_reset(client->to_idle); if (client->fd_in == -1 || client->fd_out == -1) { if ((ret = client_ctrl_read_fds(client)) <= 0) { if (ret < 0) client_abort(client, "FD Transfer failed"); return; } } switch (i_stream_read(client->ctrl_input)) { case -1: /* disconnected */ client_destroy(client); return; case -2: /* line too long, kill it */ client_abort(client, "Control session aborted: Input line too long"); return; } if (!client->version_received) { if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; if (!version_string_verify(line, "imap-urlauth-worker", IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("imap-urlauth-worker client not compatible with this server " "(mixed old and new binaries?) %s", line); client_abort(client, "Control session aborted: Version mismatch"); return; } client->version_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } } if (client->access_received) { client_abort(client, "Control session aborted: Unexpected input"); return; } if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; args = t_strsplit_tabescaped(line); if (*args == NULL || strcmp(*args, "ACCESS") != 0) { i_error("Invalid control command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; if (*args == NULL) { i_error("Invalid ACCESS command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } i_assert(client->access_user == NULL); if (**args != '\0') { client->access_user = i_strdup(*args); client->access_anonymous = FALSE; } else { client->access_user = i_strdup("anonymous"); client->access_anonymous = TRUE; } i_set_failure_prefix("imap-urlauth[%s](%s): ", my_pid, client->access_user); args++; while (*args != NULL) { /* debug */ if (strcasecmp(*args, "debug") == 0) { client->debug = TRUE; /* apps=[,access_apps, &app, 1); if (client->debug) { i_debug("User %s has URLAUTH %s access", client->access_user, app); } apps++; } } else { i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; } client->access_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(client->fd_out, (size_t)-1, FALSE); client->io = io_add(client->fd_in, IO_READ, client_input, client); o_stream_set_flush_callback(client->output, client_output, client); if (client->debug) { i_debug("Worker activated for access by user %s", client->access_user); } } static void imap_urlauth_worker_die(void) { /* do nothing */ } static void main_stdio_run(const char *access_user, const char *const *access_applications) { bool debug; debug = getenv("DEBUG") != NULL; access_user = access_user != NULL ? access_user : getenv("USER"); if (access_user == NULL && IS_STANDALONE()) access_user = getlogin(); if (access_user == NULL) i_fatal("USER environment missing"); (void)client_create_standalone(access_user, access_applications, STDIN_FILENO, STDOUT_FILENO, debug); } static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); (void)client_create(conn->fd); } int main(int argc, char *argv[]) { static const struct setting_parser_info *set_roots[] = { &imap_urlauth_worker_setting_parser_info, NULL }; enum master_service_flags service_flags = 0; enum mail_storage_service_flags storage_service_flags = MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; ARRAY_TYPE (const_string) access_apps; const char *access_user = NULL; int c; if (IS_STANDALONE()) { service_flags |= MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_STD_CLIENT; } else { service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT; } master_service = master_service_init("imap-urlauth-worker", service_flags, &argc, &argv, "a:"); t_array_init(&access_apps, 4); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'a': { const char *app = t_strdup(optarg); array_append(&access_apps, &app, 1); break; } default: return FATAL_DEFAULT; } } if ( optind < argc ) { access_user = argv[optind++]; } if (optind != argc) { i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); } master_service_init_log(master_service, t_strdup_printf("imap-urlauth[%s]: ", my_pid)); master_service_set_die_callback(master_service, imap_urlauth_worker_die); random_init(); storage_service = mail_storage_service_init(master_service, set_roots, storage_service_flags); master_service_init_finish(master_service); /* fake that we're running, so we know if client was destroyed while handling its initial input */ io_loop_set_running(current_ioloop); if (IS_STANDALONE()) { T_BEGIN { if (array_count(&access_apps) > 0) { (void)array_append_space(&access_apps); main_stdio_run(access_user, array_idx(&access_apps,0)); } else { main_stdio_run(access_user, NULL); } } T_END; } else { io_loop_set_running(current_ioloop); } if (io_loop_is_running(current_ioloop)) master_service_run(master_service, client_connected); clients_destroy_all(); mail_storage_service_deinit(&storage_service); random_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-worker-settings.h0000644000175000017500000000064513123174404021456 00000000000000#ifndef IMAP_URLAUTH_SETTINGS_H #define IMAP_URLAUTH_SETTINGS_H struct mail_user_settings; struct imap_urlauth_worker_settings { bool verbose_proctitle; /* imap_urlauth: */ const char *imap_urlauth_host; in_port_t imap_urlauth_port; }; extern const struct imap_urlauth_worker_settings imap_urlauth_worker_default_settings; extern const struct setting_parser_info imap_urlauth_worker_setting_parser_info; #endif dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-settings.c0000644000175000017500000000455713123174404020150 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "imap-urlauth-settings.h" #include #include /* */ static struct file_listener_settings imap_urlauth_unix_listeners_array[] = { { "token-login/imap-urlauth", 0666, "", "" } }; static struct file_listener_settings *imap_urlauth_unix_listeners[] = { &imap_urlauth_unix_listeners_array[0] }; static buffer_t imap_urlauth_unix_listeners_buf = { imap_urlauth_unix_listeners, sizeof(imap_urlauth_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_service_settings = { .name = "imap-urlauth", .protocol = "imap", .type = "", .executable = "imap-urlauth", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_unix_listeners_buf, sizeof(imap_urlauth_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_urlauth_settings, name), NULL } static const struct setting_define imap_urlauth_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_BOOL, mail_debug), DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, imap_urlauth_logout_format), DEF(SET_STR, imap_urlauth_submit_user), DEF(SET_STR, imap_urlauth_stream_user), SETTING_DEFINE_LIST_END }; const struct imap_urlauth_settings imap_urlauth_default_settings = { .base_dir = PKG_RUNDIR, .mail_debug = FALSE, .verbose_proctitle = FALSE, .imap_urlauth_logout_format = "in=%i out=%o", .imap_urlauth_submit_user = NULL, .imap_urlauth_stream_user = NULL }; static const struct setting_parser_info *imap_urlauth_setting_dependencies[] = { NULL }; const struct setting_parser_info imap_urlauth_setting_parser_info = { .module_name = "imap-urlauth", .defines = imap_urlauth_setting_defines, .defaults = &imap_urlauth_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_urlauth_settings), .parent_offset = (size_t)-1, .dependencies = imap_urlauth_setting_dependencies }; dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth-login-settings.c0000644000175000017500000000374613123174404021255 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "login-settings.h" #include "imap-urlauth-login-settings.h" #include /* */ static struct file_listener_settings imap_urlauth_login_unix_listeners_array[] = { { "imap-urlauth", 0666, "", "" } }; static struct file_listener_settings *imap_urlauth_login_unix_listeners[] = { &imap_urlauth_login_unix_listeners_array[0] }; static buffer_t imap_urlauth_login_unix_listeners_buf = { imap_urlauth_login_unix_listeners, sizeof(imap_urlauth_login_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_login_service_settings = { .name = "imap-urlauth-login", .protocol = "imap", .type = "login", .executable = "imap-urlauth-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "token-login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_login_unix_listeners_buf, sizeof(imap_urlauth_login_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; static const struct setting_define imap_urlauth_login_setting_defines[] = { SETTING_DEFINE_LIST_END }; static const struct setting_parser_info *imap_urlauth_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; const struct setting_parser_info imap_urlauth_login_setting_parser_info = { .module_name = "imap-urlauth-login", .defines = imap_urlauth_login_setting_defines, .type_offset = (size_t)-1, .parent_offset = (size_t)-1, .dependencies = imap_urlauth_login_setting_dependencies }; const struct setting_parser_info *imap_urlauth_login_setting_roots[] = { &login_setting_parser_info, &imap_urlauth_login_setting_parser_info, NULL }; dovecot-2.2.33.2/src/imap-urlauth/Makefile.am0000644000175000017500000000411613165463624015574 00000000000000pkglibexecdir = $(libexecdir)/dovecot # Refer to comment in imap-urlauth.c for info on what these binaries are for. pkglibexec_PROGRAMS = imap-urlauth-login imap-urlauth imap-urlauth-worker # imap-urlauth-login imap_urlauth_login_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common imap_urlauth_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) imap_urlauth_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN_DEPS) \ $(LIBDOVECOT_DEPS) imap_urlauth_login_SOURCES = \ imap-urlauth-login.c \ imap-urlauth-login-settings.c # imap-urlauth imap_urlauth_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -DPKG_RUNDIR=\""$(rundir)"\" imap_urlauth_LDFLAGS = -export-dynamic imap_urlauth_LDADD = $(LIBDOVECOT) imap_urlauth_DEPENDENCIES = $(LIBDOVECOT_DEPS) imap_urlauth_SOURCES = \ imap-urlauth.c \ imap-urlauth-client.c \ imap-urlauth-settings.c # imap-urlauth-worker imap_urlauth_worker_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-imap-urlauth \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/login-common imap_urlauth_worker_LDFLAGS = -export-dynamic urlauth_libs = \ $(top_builddir)/src/lib-imap-urlauth/libimap-urlauth.la imap_urlauth_worker_LDADD = $(urlauth_libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT) imap_urlauth_worker_DEPENDENCIES = $(urlauth_libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_DEPS) imap_urlauth_worker_SOURCES = \ imap-urlauth-worker.c \ imap-urlauth-worker-settings.c noinst_HEADERS = \ imap-urlauth-client.h \ imap-urlauth-common.h \ imap-urlauth-settings.h \ imap-urlauth-login-settings.h \ imap-urlauth-worker-settings.h dovecot-2.2.33.2/src/imap-urlauth/imap-urlauth.c0000644000175000017500000001763213165463624016323 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ /* The imap-urlauth service provides URLAUTH access between different accounts. If user A has an URLAUTH that references a mail from user B, it makes a connection to the imap-urlauth service to access user B's mail store to retrieve the mail. The authentication and authorization of the URLAUTH is performed whithin this service. Because access to the mailbox and the associated mailbox keys is necessary to retrieve the message and for verification of the URLAUTH, the urlauth services need root privileges. To mitigate security concerns, the retrieval and verification of the URLs is performed in a worker service that drops root privileges and acts as target user B. The imap-urlauth service thus consists of three separate stages: - imap-urlauth-login: This is the login service which operates identical to imap-login and pop3-login equivalents, except for the fact that only token authentication is allowed. It verifies that the connecting client is an IMAP service acting on behaf of an authenticated user. - imap-urlauth: Once the client is authenticated, the connection gets passed to the imap-urlauth service (as implemented here). The goal of this stage is to prevent the need for re-authenticating to the imap-urlauth service when the clients wants to switch to a different target user. It normally runs as $default_internal_user and starts workers to perform the actual work. To start a worker, the imap-urlauth service establishes a control connection to the imap-urlauth-worker service. In the handshake phase of the control protocol, the connection of the client is passed to the worker. Once the worker finishes, a new worker is started and the client connection is tranfered to it, unless the client is disconnected. - imap-urlauth-worker: The worker handles the URLAUTH requests from the client, so this is where the mail store of the target user is accessed. The worker starts as root. In the protocol interaction the client first indicates what the target user is. The worker then performs a userdb lookup and drops privileges. The client can then submit URLAUTH requests, which are limited to that user. Once the client wants to access a different user, the worker terminates and the imap-urlauth service starts a new worker for the next target user. */ #include "imap-urlauth-common.h" #include "lib-signals.h" #include "ioloop.h" #include "buffer.h" #include "istream.h" #include "ostream.h" #include "abspath.h" #include "base64.h" #include "str.h" #include "process-title.h" #include "auth-master.h" #include "master-service.h" #include "master-service-settings.h" #include "master-login.h" #include "master-interface.h" #include "var-expand.h" #include #include #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) bool verbose_proctitle = FALSE; static struct master_login *master_login = NULL; static const struct imap_urlauth_settings *imap_urlauth_settings; void imap_urlauth_refresh_proctitle(void) { struct client *client; string_t *title = t_str_new(128); if (!verbose_proctitle) return; str_append_c(title, '['); switch (imap_urlauth_client_count) { case 0: str_append(title, "idling"); break; case 1: client = imap_urlauth_clients; str_append(title, client->username); break; default: str_printfa(title, "%u connections", imap_urlauth_client_count); break; } str_append_c(title, ']'); process_title_set(str_c(title)); } static void imap_urlauth_die(void) { /* do nothing. imap_urlauth connections typically die pretty quick anyway. */ } static int client_create_from_input(const char *username, int fd_in, int fd_out) { struct client *client; if (client_create(username, fd_in, fd_out, imap_urlauth_settings, &client) < 0) return -1; if (!IS_STANDALONE()) client_send_line(client, "OK"); return 0; } static void main_stdio_run(const char *username) { username = username != NULL ? username : getenv("USER"); if (username == NULL && IS_STANDALONE()) username = getlogin(); if (username == NULL) i_fatal("USER environment missing"); (void)client_create_from_input(username, STDIN_FILENO, STDOUT_FILENO); } static void login_client_connected(const struct master_login_client *client, const char *username, const char *const *extra_fields) { const char *msg = "NO\n"; struct auth_user_reply reply; struct net_unix_cred cred; auth_user_fields_parse(extra_fields, pool_datastack_create(), &reply); /* check peer credentials if possible */ if (reply.uid != (uid_t)-1 && net_getunixcred(client->fd, &cred) == 0 && reply.uid != cred.uid) { i_error("Peer's credentials (uid=%ld) do not match " "the user that logged in (uid=%ld).", (long)cred.uid, (long)reply.uid); if (write(client->fd, msg, strlen(msg)) < 0) { /* ignored */ } net_disconnect(client->fd); return; } if (reply.anonymous) username = NULL; if (client_create_from_input(username, client->fd, client->fd) < 0) net_disconnect(client->fd); } static void login_client_failed(const struct master_login_client *client, const char *errormsg ATTR_UNUSED) { const char *msg = "NO\n"; if (write(client->fd, msg, strlen(msg)) < 0) { /* ignored */ } } static void client_connected(struct master_service_connection *conn) { /* when running standalone, we shouldn't even get here */ i_assert(master_login != NULL); master_service_client_connection_accept(conn); master_login_add(master_login, conn->fd); } int main(int argc, char *argv[]) { static const struct setting_parser_info *set_roots[] = { &imap_urlauth_setting_parser_info, NULL }; struct master_login_settings login_set; struct master_service_settings_input input; struct master_service_settings_output output; void **sets; enum master_service_flags service_flags = 0; const char *error = NULL, *username = NULL; const char *auth_socket_path = "auth-master"; int c; i_zero(&login_set); login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT; if (IS_STANDALONE() && getuid() == 0 && net_getpeername(1, NULL, NULL) == 0) { printf("NO imap_urlauth binary must not be started from " "inetd, use imap-urlauth-login instead.\n"); return 1; } if (IS_STANDALONE()) { service_flags |= MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_STD_CLIENT; } else { service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; } master_service = master_service_init("imap-urlauth", service_flags, &argc, &argv, "a:"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; default: return FATAL_DEFAULT; } } master_service_init_log(master_service, "imap-urlauth: "); i_zero(&input); input.roots = set_roots; input.module = "imap-urlauth"; input.service = "imap-urlauth"; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); sets = master_service_settings_get_others(master_service); imap_urlauth_settings = sets[0]; if (imap_urlauth_settings->verbose_proctitle) verbose_proctitle = TRUE; login_set.auth_socket_path = t_abspath(auth_socket_path); login_set.callback = login_client_connected; login_set.failure_callback = login_client_failed; master_service_init_finish(master_service); master_service_set_die_callback(master_service, imap_urlauth_die); /* fake that we're running, so we know if client was destroyed while handling its initial input */ io_loop_set_running(current_ioloop); if (IS_STANDALONE()) { T_BEGIN { main_stdio_run(username); } T_END; } else { master_login = master_login_init(master_service, &login_set); io_loop_set_running(current_ioloop); } if (io_loop_is_running(current_ioloop)) master_service_run(master_service, client_connected); clients_destroy_all(); if (master_login != NULL) master_login_deinit(&master_login); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/lda/0002755000175000017500000000000013172375612011745 500000000000000dovecot-2.2.33.2/src/lda/Makefile.in0000644000175000017500000005443213172375572013745 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = dovecot-lda$(EXEEXT) subdir = src/lda ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_dovecot_lda_OBJECTS = main.$(OBJEXT) dovecot_lda_OBJECTS = $(am_dovecot_lda_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = dovecot_lda_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(dovecot_lda_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dovecot_lda_SOURCES) DIST_SOURCES = $(dovecot_lda_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw dovecot_lda_LDFLAGS = -export-dynamic dovecot_lda_LDADD = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) dovecot_lda_DEPENDENCIES = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) dovecot_lda_SOURCES = \ main.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lda/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lda/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list dovecot-lda$(EXEEXT): $(dovecot_lda_OBJECTS) $(dovecot_lda_DEPENDENCIES) $(EXTRA_dovecot_lda_DEPENDENCIES) @rm -f dovecot-lda$(EXEEXT) $(AM_V_CCLD)$(dovecot_lda_LINK) $(dovecot_lda_OBJECTS) $(dovecot_lda_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-exec-local install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-exec-local install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkglibexecPROGRAMS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile install-exec-local: rm -f $(DESTDIR)$(pkglibexecdir)/deliver $(LN_S) dovecot-lda $(DESTDIR)$(pkglibexecdir)/deliver # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lda/main.c0000644000175000017500000003262413165463624012765 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "ioloop.h" #include "env-util.h" #include "fd-set-nonblock.h" #include "istream.h" #include "istream-seekable.h" #include "abspath.h" #include "safe-mkstemp.h" #include "eacces-error.h" #include "ipwd.h" #include "str.h" #include "str-sanitize.h" #include "strescape.h" #include "unichar.h" #include "rfc822-parser.h" #include "message-address.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "raw-storage.h" #include "mail-deliver.h" #include "mail-send.h" #include "mbox-from.h" #include "lda-settings.h" #include #include #define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON" /* After buffer grows larger than this, create a temporary file to /tmp where to read the mail. */ #define MAIL_MAX_MEMORY_BUFFER (1024*128) static const char *wanted_headers[] = { "From", "To", "Message-ID", "Subject", "Return-Path", NULL }; static const char *escape_local_part(const char *local_part) { const char *p; /* if local_part isn't dot-atom-text, we need to return quoted-string dot-atom-text = 1*atext *("." 1*atext) */ for (p = local_part; *p != '\0'; p++) { if (!IS_ATEXT(*p) && *p != '.') break; } if (*p != '\0' || *local_part == '.' || (p != local_part && p[-1] == '.')) local_part = t_strdup_printf("\"%s\"", str_escape(local_part)); return local_part; } static const char *address_sanitize(const char *address) { struct message_address *addr; const char *ret, *mailbox; pool_t pool; pool = pool_alloconly_create("address sanitizer", 256); addr = message_address_parse(pool, (const unsigned char *)address, strlen(address), 1, FALSE); if (addr == NULL || addr->mailbox == NULL || addr->domain == NULL || *addr->mailbox == '\0') ret = DEFAULT_ENVELOPE_SENDER; else { mailbox = escape_local_part(addr->mailbox); if (*addr->domain == '\0') ret = t_strdup(mailbox); else ret = t_strdup_printf("%s@%s", mailbox, addr->domain); } pool_unref(&pool); return ret; } static int seekable_fd_callback(const char **path_r, void *context) { struct mail_deliver_context *ctx = context; string_t *path; int fd; path = t_str_new(128); mail_user_set_get_temp_prefix(path, ctx->dest_user->set); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static struct istream * create_raw_stream(struct mail_deliver_context *ctx, int fd, time_t *mtime_r) { struct istream *input, *input2, *input_list[2]; const unsigned char *data; char *sender = NULL; size_t i, size; int ret, tz; *mtime_r = (time_t)-1; fd_set_nonblock(fd, FALSE); input = i_stream_create_fd(fd, 4096, FALSE); input->blocking = TRUE; /* If input begins with a From-line, drop it */ ret = i_stream_read_data(input, &data, &size, 5); if (ret > 0 && size >= 5 && memcmp(data, "From ", 5) == 0) { /* skip until the first LF */ i_stream_skip(input, 5); while (i_stream_read_data(input, &data, &size, 0) > 0) { for (i = 0; i < size; i++) { if (data[i] == '\n') break; } if (i != size) { (void)mbox_from_parse(data, i, mtime_r, &tz, &sender); i_stream_skip(input, i + 1); break; } i_stream_skip(input, size); } } if (sender != NULL && ctx->src_envelope_sender == NULL) { /* use the envelope sender from From_-line, but only if it hasn't been specified with -f already. */ ctx->src_envelope_sender = p_strdup(ctx->pool, sender); } i_free(sender); if (input->v_offset == 0) { input2 = input; i_stream_ref(input2); } else { input2 = i_stream_create_limit(input, (uoff_t)-1); } i_stream_unref(&input); input_list[0] = input2; input_list[1] = NULL; input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER, seekable_fd_callback, ctx); i_stream_unref(&input2); return input; } static struct mail * lda_raw_mail_open(struct mail_deliver_context *ctx, const char *path) { struct mail_user *raw_mail_user; struct mailbox *box; struct mailbox_transaction_context *t; struct mail *mail; struct mailbox_header_lookup_ctx *headers_ctx; struct istream *input; void **sets; const char *envelope_sender; time_t mtime; int ret; sets = master_service_settings_get_others(master_service); raw_mail_user = raw_storage_create_from_set(ctx->dest_user->set_info, sets[0]); envelope_sender = ctx->src_envelope_sender != NULL ? ctx->src_envelope_sender : DEFAULT_ENVELOPE_SENDER; if (path == NULL) { input = create_raw_stream(ctx, 0, &mtime); i_stream_set_name(input, "stdin"); ret = raw_mailbox_alloc_stream(raw_mail_user, input, mtime, envelope_sender, &box); i_stream_unref(&input); } else { ret = raw_mailbox_alloc_path(raw_mail_user, path, (time_t)-1, envelope_sender, &box); } if (ret < 0) { i_fatal("Can't open delivery mail as raw: %s", mailbox_get_last_internal_error(box, NULL)); } mail_user_unref(&raw_mail_user); t = mailbox_transaction_begin(box, 0); headers_ctx = mailbox_header_lookup_init(box, wanted_headers); mail = mail_alloc(t, 0, headers_ctx); mailbox_header_lookup_unref(&headers_ctx); mail_set_seq(mail, 1); return mail; } static void lda_set_dest_addr(struct mail_deliver_context *ctx, const char *user, const char *destaddr_source) { if (ctx->dest_addr == NULL && *ctx->set->lda_original_recipient_header != '\0') { ctx->dest_addr = mail_deliver_get_address(ctx->src_mail, ctx->set->lda_original_recipient_header); destaddr_source = t_strconcat( ctx->set->lda_original_recipient_header, " header", NULL); } if (ctx->dest_addr == NULL) { ctx->dest_addr = strchr(user, '@') != NULL ? user : t_strconcat(user, "@", ctx->set->hostname, NULL); destaddr_source = "user@hostname"; } if (ctx->final_dest_addr == NULL) ctx->final_dest_addr = ctx->dest_addr; if (ctx->dest_user->mail_debug) { i_debug("Destination address: %s (source: %s)", ctx->dest_addr, destaddr_source); } } static void failure_exit_callback(int *status) { /* we want all our exit codes to be sysexits.h compatible. if we failed because of a logging related error, we most likely aren't writing to stderr, so try writing there to give some kind of a clue what's wrong. FATAL_LOGOPEN failure already wrote to stderr, so don't duplicate it. */ switch (*status) { case FATAL_LOGWRITE: fputs("Failed to write to log file", stderr); break; case FATAL_LOGERROR: fputs("Internal logging error", stderr); break; case FATAL_LOGOPEN: case FATAL_OUTOFMEM: case FATAL_EXEC: case FATAL_DEFAULT: break; default: return; } *status = EX_TEMPFAIL; } static void print_help(void) { printf( "Usage: dovecot-lda [-c ] [-a
] [-d ] [-p ]\n" " [-f ] [-m ] [-e] [-k]\n"); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &lda_setting_parser_info, NULL }; struct mail_deliver_context ctx; enum mail_storage_service_flags service_flags = 0; const char *user, *errstr, *path; struct lda_settings *lda_set; struct mail_storage_service_ctx *storage_service; struct mail_storage_service_user *service_user; struct mail_storage_service_input service_input; struct mail_storage *storage; const char *user_source = "", *destaddr_source = ""; uid_t process_euid; bool stderr_rejection = FALSE; int ret, c; enum mail_error error; if (getuid() != geteuid() && geteuid() == 0) { /* running setuid - don't allow this if the binary is executable by anyone */ struct stat st; if (stat(argv[0], &st) < 0) { fprintf(stderr, "stat(%s) failed: %s\n", argv[0], strerror(errno)); return EX_TEMPFAIL; } else if ((st.st_mode & 1) != 0 && (st.st_mode & 04000) != 0) { fprintf(stderr, "%s must not be both world-executable " "and setuid-root. This allows root exploits. " "See http://wiki2.dovecot.org/LDA#multipleuids\n", argv[0]); return EX_TEMPFAIL; } } i_set_failure_exit_callback(failure_exit_callback); master_service = master_service_init("lda", MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR, &argc, &argv, "a:d:ef:m:p:r:"); i_zero(&ctx); ctx.session = mail_deliver_session_init(); ctx.pool = ctx.session->pool; ctx.dest_mailbox_name = "INBOX"; ctx.timeout_secs = LDA_SUBMISSION_TIMEOUT_SECS; path = NULL; user = getenv("USER"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'a': /* original recipient address */ ctx.dest_addr = optarg; destaddr_source = "-a parameter"; break; case 'd': /* destination user */ user = optarg; service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; break; case 'e': stderr_rejection = TRUE; break; case 'f': /* envelope sender address */ ctx.src_envelope_sender = p_strdup(ctx.pool, address_sanitize(optarg)); break; case 'm': /* destination mailbox. Ignore -m "". This allows doing -m ${extension} in Postfix to handle user+mailbox */ if (*optarg != '\0') T_BEGIN { if (!uni_utf8_str_is_valid(optarg)) { i_fatal("Mailbox name not UTF-8: %s", optarg); } ctx.dest_mailbox_name = optarg; } T_END; break; case 'p': /* input path */ path = t_abspath(optarg); break; case 'r': /* final recipient address */ ctx.final_dest_addr = optarg; break; default: print_help(); return EX_USAGE; } } if (optind != argc) { print_help(); i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); } process_euid = geteuid(); if ((service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) ; else if (process_euid != 0) { /* we're non-root. get our username and possibly our home. */ struct passwd pw; const char *home; home = getenv("HOME"); if (user != NULL && home != NULL) { /* no need for a pw lookup */ user_source = "USER environment"; } else if ((ret = i_getpwuid(process_euid, &pw)) > 0) { user = t_strdup(pw.pw_name); if (home == NULL) env_put(t_strconcat("HOME=", pw.pw_dir, NULL)); user_source = "passwd lookup for process euid"; } else if (ret < 0) { /* temporary failure */ i_fatal("getpwuid() failed: %m"); } else if (user == NULL) { i_fatal_status(EX_USAGE, "Couldn't lookup our username (uid=%s)", dec2str(process_euid)); } } else { i_fatal_status(EX_USAGE, "destination user parameter (-d user) not given"); } master_service_init_finish(master_service); i_zero(&service_input); service_input.module = "lda"; service_input.service = "lda"; service_input.username = user; service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT | MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS; storage_service = mail_storage_service_init(master_service, set_roots, service_flags); mail_deliver_hooks_init(); /* set before looking up the user (or ideally we'd do this between _lookup() and _next(), but don't bother) */ ctx.delivery_time_started = ioloop_timeval; ret = mail_storage_service_lookup_next(storage_service, &service_input, &service_user, &ctx.dest_user, &errstr); if (ret <= 0) { if (ret < 0) i_fatal("%s", errstr); return EX_NOUSER; } #ifdef SIGXFSZ lib_signals_ignore(SIGXFSZ, TRUE); #endif lda_set = mail_storage_service_user_get_set(service_user)[1]; settings_var_expand(&lda_setting_parser_info, lda_set, ctx.dest_user->pool, mail_user_var_expand_table(ctx.dest_user)); ctx.set = lda_set; if (ctx.dest_user->mail_debug && *user_source != '\0') { i_debug("userdb lookup skipped, username taken from %s", user_source); } ctx.src_mail = lda_raw_mail_open(&ctx, path); lda_set_dest_addr(&ctx, user, destaddr_source); if (mail_deliver(&ctx, &storage) < 0) { if (ctx.tempfail_error != NULL) { errstr = ctx.tempfail_error; error = MAIL_ERROR_TEMP; } else if (storage != NULL) { errstr = mail_storage_get_last_error(storage, &error); } else { /* This shouldn't happen */ i_error("BUG: Saving failed to unknown storage"); return EX_TEMPFAIL; } if (stderr_rejection) { /* write to stderr also for tempfails so that MTA can log the reason if it wants to. */ fprintf(stderr, "%s\n", errstr); } if (error != MAIL_ERROR_NOQUOTA || ctx.set->quota_full_tempfail) { /* Saving to INBOX should always work unless we're over quota. If it didn't, it's probably a configuration problem. */ return EX_TEMPFAIL; } ctx.mailbox_full = TRUE; ctx.dsn = TRUE; /* we'll have to reply with permanent failure */ mail_deliver_log(&ctx, "rejected: %s", str_sanitize(errstr, 512)); if (stderr_rejection) return EX_NOPERM; ret = mail_send_rejection(&ctx, user, errstr); if (ret != 0) return ret < 0 ? EX_TEMPFAIL : ret; /* ok, rejection sent */ } { struct mailbox_transaction_context *t = ctx.src_mail->transaction; struct mailbox *box = ctx.src_mail->box; mail_free(&ctx.src_mail); mailbox_transaction_rollback(&t); mailbox_free(&box); } mail_user_unref(&ctx.dest_user); mail_deliver_session_deinit(&ctx.session); mail_storage_service_user_unref(&service_user); mail_storage_service_deinit(&storage_service); master_service_deinit(&master_service); return EX_OK; } dovecot-2.2.33.2/src/lda/Makefile.am0000644000175000017500000000145113165463624013723 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = dovecot-lda AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw dovecot_lda_LDFLAGS = -export-dynamic dovecot_lda_LDADD = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) dovecot_lda_DEPENDENCIES = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) dovecot_lda_SOURCES = \ main.c install-exec-local: rm -f $(DESTDIR)$(pkglibexecdir)/deliver $(LN_S) dovecot-lda $(DESTDIR)$(pkglibexecdir)/deliver dovecot-2.2.33.2/src/master/0002755000175000017500000000000013172375612012500 500000000000000dovecot-2.2.33.2/src/master/service.c0000644000175000017500000004444213165463624014235 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "ioloop.h" #include "array.h" #include "aqueue.h" #include "hash.h" #include "str.h" #include "net.h" #include "master-service.h" #include "master-service-settings.h" #include "service.h" #include "service-anvil.h" #include "service-process.h" #include "service-monitor.h" #include #include #define SERVICE_DIE_TIMEOUT_MSECS (1000*6) #define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2 HASH_TABLE_TYPE(pid_process) service_pids; void service_error(struct service *service, const char *format, ...) { va_list args; va_start(args, format); i_error("service(%s): %s", service->set->name, t_strdup_vprintf(format, args)); va_end(args); } static struct service_listener * service_create_file_listener(struct service *service, enum service_listener_type type, const struct file_listener_settings *set, const char **error_r) { struct service_listener *l; const char *set_name; gid_t gid; l = p_new(service->list->pool, struct service_listener, 1); l->service = service; l->type = type; l->fd = -1; l->set.fileset.set = set; l->name = strrchr(set->path, '/'); if (l->name != NULL) l->name++; else l->name = set->path; if (get_uidgid(set->user, &l->set.fileset.uid, &gid, error_r) < 0) set_name = "user"; else if (get_gid(set->group, &l->set.fileset.gid, error_r) < 0) set_name = "group"; else return l; *error_r = t_strdup_printf( "%s (See service %s { %s_listener %s { %s } } setting)", *error_r, service->set->name, type == SERVICE_LISTENER_UNIX ? "unix" : "fifo", set->path, set_name); return NULL; } static int resolve_ip(const char *address, const struct ip_addr **ips_r, unsigned int *ips_count_r, const char **error_r) { struct ip_addr *ip_list; unsigned int ips_count; int ret; if (address == NULL || strcmp(address, "*") == 0) { /* IPv4 any */ ip_list = t_new(struct ip_addr, 1); net_get_ip_any4(ip_list); *ips_r = ip_list; *ips_count_r = 1; return 0; } if (strcmp(address, "::") == 0 || strcmp(address, "[::]") == 0) { /* IPv6 any */ ip_list = t_new(struct ip_addr, 1); net_get_ip_any6(ip_list); *ips_r = ip_list; *ips_count_r = 1; return 0; } /* Return the first IP if there happens to be multiple. */ ret = net_gethostbyname(address, &ip_list, &ips_count); if (ret != 0) { *error_r = t_strdup_printf("Can't resolve address %s: %s", address, net_gethosterror(ret)); return -1; } if (ips_count < 1) { *error_r = t_strdup_printf("No IPs for address: %s", address); return -1; } *ips_r = ip_list; *ips_count_r = ips_count; return 0; } static struct service_listener * service_create_one_inet_listener(struct service *service, const struct inet_listener_settings *set, const char *address, const struct ip_addr *ip) { struct service_listener *l; i_assert(set->port != 0); l = p_new(service->list->pool, struct service_listener, 1); l->service = service; l->type = SERVICE_LISTENER_INET; l->fd = -1; l->set.inetset.set = set; l->set.inetset.ip = *ip; l->inet_address = p_strdup(service->list->pool, address); l->name = set->name; return l; } static int service_create_inet_listeners(struct service *service, const struct inet_listener_settings *set, const char **error_r) { static struct service_listener *l; const char *const *tmp, *addresses; const struct ip_addr *ips; unsigned int i, ips_count; bool ssl_disabled = strcmp(service->set->master_set->ssl, "no") == 0; if (set->port == 0) { /* disabled */ return 0; } if (*set->address != '\0') addresses = set->address; else { /* use the default listen address */ addresses = service->set->master_set->listen; } tmp = t_strsplit_spaces(addresses, ", "); for (; *tmp != NULL; tmp++) { const char *address = *tmp; if (set->ssl && ssl_disabled) continue; if (resolve_ip(address, &ips, &ips_count, error_r) < 0) return -1; for (i = 0; i < ips_count; i++) { l = service_create_one_inet_listener(service, set, address, &ips[i]); array_append(&service->listeners, &l, 1); } service->have_inet_listeners = TRUE; } return 0; } static int service_get_groups(const char *groups, pool_t pool, const char **gids_r, const char **error_r) { const char *const *tmp; string_t *str; gid_t gid; str = t_str_new(64); for (tmp = t_strsplit(groups, ","); *tmp != NULL; tmp++) { if (get_gid(*tmp, &gid, error_r) < 0) return -1; if (str_len(str) > 0) str_append_c(str, ','); str_append(str, dec2str(gid)); } *gids_r = p_strdup(pool, str_c(str)); return 0; } static struct service * service_create(pool_t pool, const struct service_settings *set, struct service_list *service_list, const char **error_r) { struct file_listener_settings *const *unix_listeners; struct file_listener_settings *const *fifo_listeners; struct inet_listener_settings *const *inet_listeners; struct service *service; struct service_listener *l; unsigned int i, unix_count, fifo_count, inet_count; service = p_new(pool, struct service, 1); service->list = service_list; service->set = set; service->throttle_secs = SERVICE_STARTUP_FAILURE_THROTTLE_MIN_SECS; service->client_limit = set->client_limit != 0 ? set->client_limit : set->master_set->default_client_limit; if (set->service_count > 0 && service->client_limit > set->service_count) service->client_limit = set->service_count; service->vsz_limit = set->vsz_limit != (uoff_t)-1 ? set->vsz_limit : set->master_set->default_vsz_limit; service->idle_kill = set->idle_kill != 0 ? set->idle_kill : set->master_set->default_idle_kill; service->type = service->set->parsed_type; if (set->process_limit == 0) { /* use default */ service->process_limit = set->master_set->default_process_limit; } else { service->process_limit = set->process_limit; } /* default gid to user's primary group */ if (get_uidgid(set->user, &service->uid, &service->gid, error_r) < 0) { switch (set->user_default) { case SERVICE_USER_DEFAULT_NONE: *error_r = t_strdup_printf( "%s (See service %s { user } setting)", *error_r, set->name); break; case SERVICE_USER_DEFAULT_INTERNAL: *error_r = t_strconcat(*error_r, " (See default_internal_user setting)", NULL); break; case SERVICE_USER_DEFAULT_LOGIN: *error_r = t_strconcat(*error_r, " (See default_login_user setting)", NULL); break; } return NULL; } if (*set->group != '\0') { if (get_gid(set->group, &service->gid, error_r) < 0) { *error_r = t_strdup_printf( "%s (See service %s { group } setting)", *error_r, set->name); return NULL; } } if (get_gid(set->privileged_group, &service->privileged_gid, error_r) < 0) { *error_r = t_strdup_printf( "%s (See service %s { privileged_group } setting)", *error_r, set->name); return NULL; } if (*set->extra_groups != '\0') { if (service_get_groups(set->extra_groups, pool, &service->extra_gids, error_r) < 0) { *error_r = t_strdup_printf( "%s (See service %s { extra_groups } setting)", *error_r, set->name); return NULL; } } /* set these later, so if something fails we don't have to worry about closing them */ service->log_fd[0] = -1; service->log_fd[1] = -1; service->status_fd[0] = -1; service->status_fd[1] = -1; service->master_dead_pipe_fd[0] = -1; service->master_dead_pipe_fd[1] = -1; service->log_process_internal_fd = -1; service->login_notify_fd = -1; if (service->type == SERVICE_TYPE_ANVIL) { service->status_fd[0] = service_anvil_global->status_fd[0]; service->status_fd[1] = service_anvil_global->status_fd[1]; } if (array_is_created(&set->unix_listeners)) unix_listeners = array_get(&set->unix_listeners, &unix_count); else { unix_listeners = NULL; unix_count = 0; } if (array_is_created(&set->fifo_listeners)) fifo_listeners = array_get(&set->fifo_listeners, &fifo_count); else { fifo_listeners = NULL; fifo_count = 0; } if (array_is_created(&set->inet_listeners)) inet_listeners = array_get(&set->inet_listeners, &inet_count); else { inet_listeners = NULL; inet_count = 0; } if (unix_count == 0 && service->type == SERVICE_TYPE_CONFIG) { *error_r = "Service must have unix listeners"; return NULL; } p_array_init(&service->listeners, pool, unix_count + fifo_count + inet_count); for (i = 0; i < unix_count; i++) { if (unix_listeners[i]->mode == 0) { /* disabled */ continue; } l = service_create_file_listener(service, SERVICE_LISTENER_UNIX, unix_listeners[i], error_r); if (l == NULL) return NULL; array_append(&service->listeners, &l, 1); } for (i = 0; i < fifo_count; i++) { if (fifo_listeners[i]->mode == 0) { /* disabled */ continue; } l = service_create_file_listener(service, SERVICE_LISTENER_FIFO, fifo_listeners[i], error_r); if (l == NULL) return NULL; array_append(&service->listeners, &l, 1); } for (i = 0; i < inet_count; i++) { if (service_create_inet_listeners(service, inet_listeners[i], error_r) < 0) return NULL; } service->executable = set->executable; if (access(t_strcut(service->executable, ' '), X_OK) < 0) { *error_r = t_strdup_printf("access(%s) failed: %m", t_strcut(service->executable, ' ')); return NULL; } return service; } struct service * service_lookup(struct service_list *service_list, const char *name) { struct service *const *services; array_foreach(&service_list->services, services) { struct service *service = *services; if (strcmp(service->set->name, name) == 0) return service; } return NULL; } struct service * service_lookup_type(struct service_list *service_list, enum service_type type) { struct service *const *services; array_foreach(&service_list->services, services) { struct service *service = *services; if (service->type == type) return service; } return NULL; } static bool service_want(struct service_settings *set) { char *const *proto; if (*set->executable == '\0') { /* silently allow service {} blocks for disabled extensions (e.g. service managesieve {} block without pigeonhole installed) */ return FALSE; } if (*set->protocol == '\0') return TRUE; for (proto = set->master_set->protocols_split; *proto != NULL; proto++) { if (strcmp(*proto, set->protocol) == 0) return TRUE; } return FALSE; } static int services_create_real(const struct master_settings *set, pool_t pool, struct service_list **services_r, const char **error_r) { struct service_list *service_list; struct service *service; struct service_settings *const *service_settings; const char *error; unsigned int i, count; service_list = p_new(pool, struct service_list, 1); service_list->refcount = 1; service_list->pool = pool; service_list->service_set = master_service_settings_get(master_service); service_list->set_pool = master_service_settings_detach(master_service); service_list->set = set; service_list->master_log_fd[0] = -1; service_list->master_log_fd[1] = -1; service_list->master_fd = -1; service_settings = array_get(&set->services, &count); p_array_init(&service_list->services, pool, count); for (i = 0; i < count; i++) { if (!service_want(service_settings[i])) continue; service = service_create(pool, service_settings[i], service_list, &error); if (service == NULL) { *error_r = t_strdup_printf("service(%s) %s", service_settings[i]->name, error); return -1; } switch (service->type) { case SERVICE_TYPE_LOG: if (service_list->log != NULL) { *error_r = "Multiple log services specified"; return -1; } service_list->log = service; break; case SERVICE_TYPE_CONFIG: if (service_list->config != NULL) { *error_r = "Multiple config services specified"; return -1; } service_list->config = service; break; case SERVICE_TYPE_ANVIL: if (service_list->anvil != NULL) { *error_r = "Multiple anvil services specified"; return -1; } service_list->anvil = service; break; default: break; } array_append(&service_list->services, &service, 1); } if (service_list->log == NULL) { *error_r = "log service not specified"; return -1; } if (service_list->config == NULL) { *error_r = "config process not specified"; return -1; } *services_r = service_list; return 0; } int services_create(const struct master_settings *set, struct service_list **services_r, const char **error_r) { pool_t pool; pool = pool_alloconly_create("services pool", 32768); if (services_create_real(set, pool, services_r, error_r) < 0) { pool_unref(&pool); return -1; } return 0; } void service_signal(struct service *service, int signo) { struct service_process *process = service->processes; unsigned int count = 0; for (; process != NULL; process = process->next) { i_assert(process->service == service); if (!SERVICE_PROCESS_IS_INITIALIZED(process) && signo != SIGKILL) { /* too early to signal it */ continue; } if (kill(process->pid, signo) == 0) count++; else if (errno != ESRCH) { service_error(service, "kill(%s, %d) failed: %m", dec2str(process->pid), signo); } } if (count > 0) { i_warning("Sent %s to %u %s processes", signo == SIGTERM ? "SIGTERM" : "SIGKILL", count, service->set->name); } } static void service_login_notify_send(struct service *service) { service->last_login_notify_time = ioloop_time; if (service->to_login_notify != NULL) timeout_remove(&service->to_login_notify); service_signal(service, SIGUSR1); } static void service_login_notify_timeout(struct service *service) { service_login_notify_send(service); } void service_login_notify(struct service *service, bool all_processes_full) { enum master_login_state state; int diff; if (service->last_login_full_notify == all_processes_full || service->login_notify_fd == -1) return; /* change the state always immediately. it's cheap. */ service->last_login_full_notify = all_processes_full; state = all_processes_full ? MASTER_LOGIN_STATE_FULL : MASTER_LOGIN_STATE_NONFULL; if (lseek(service->login_notify_fd, state, SEEK_SET) < 0) service_error(service, "lseek(notify fd) failed: %m"); /* but don't send signal to processes too often */ diff = ioloop_time - service->last_login_notify_time; if (diff < SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS) { if (service->to_login_notify != NULL) return; diff = (SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS - diff) * 1000; service->to_login_notify = timeout_add(diff, service_login_notify_timeout, service); } else { service_login_notify_send(service); } } static void services_kill_timeout(struct service_list *service_list) { struct service *const *services, *log_service; bool sigterm_log; int sig; if (!service_list->sigterm_sent || !service_list->sigterm_sent_to_log) sig = SIGTERM; else sig = SIGKILL; sigterm_log = service_list->sigterm_sent; service_list->sigterm_sent = TRUE; i_warning("Processes aren't dying after reload, sending %s.", sig == SIGTERM ? "SIGTERM" : "SIGKILL"); log_service = NULL; array_foreach(&service_list->services, services) { struct service *service = *services; if (service->type == SERVICE_TYPE_LOG) log_service = service; else service_signal(service, sig); } /* kill log service later so it could still have a chance of logging something */ if (log_service != NULL && sigterm_log) { service_signal(log_service, sig); service_list->sigterm_sent_to_log = TRUE; } } void services_destroy(struct service_list *service_list, bool wait) { /* make sure we log if child processes died unexpectedly */ service_list->destroying = TRUE; services_monitor_reap_children(); services_monitor_stop(service_list, wait); if (service_list->refcount > 1 && service_list->service_set->shutdown_clients) { service_list->to_kill = timeout_add(SERVICE_DIE_TIMEOUT_MSECS, services_kill_timeout, service_list); } service_list->destroyed = TRUE; service_list_unref(service_list); } void service_list_ref(struct service_list *service_list) { i_assert(service_list->refcount > 0); service_list->refcount++; } void service_list_unref(struct service_list *service_list) { i_assert(service_list->refcount > 0); if (--service_list->refcount > 0) return; if (service_list->to_kill != NULL) timeout_remove(&service_list->to_kill); pool_unref(&service_list->set_pool); pool_unref(&service_list->pool); } const char *services_get_config_socket_path(struct service_list *service_list) { struct service_listener *const *listeners; unsigned int count; listeners = array_get(&service_list->config->listeners, &count); i_assert(count > 0); return listeners[0]->set.fileset.set->path; } static void service_throttle_timeout(struct service *service) { timeout_remove(&service->to_throttle); service_monitor_listen_start(service); } static void service_drop_listener_connections(struct service *service) { struct service_listener *const *listenerp; int fd; array_foreach(&service->listeners, listenerp) { switch ((*listenerp)->type) { case SERVICE_LISTENER_UNIX: case SERVICE_LISTENER_INET: if ((*listenerp)->fd == -1) { /* already stopped listening */ break; } while ((fd = net_accept((*listenerp)->fd, NULL, NULL)) >= 0) i_close_fd(&fd); break; case SERVICE_LISTENER_FIFO: break; } } } void service_throttle(struct service *service, unsigned int secs) { if (service->to_throttle != NULL || service->list->destroyed) return; if (service->processes == NULL) service_drop_listener_connections(service); service_monitor_listen_stop(service); service->to_throttle = timeout_add(secs * 1000, service_throttle_timeout, service); } void services_throttle_time_sensitives(struct service_list *list, unsigned int secs) { struct service *const *services; array_foreach(&list->services, services) { struct service *service = *services; if (service->type == SERVICE_TYPE_UNKNOWN) service_throttle(service, secs); } } void service_pids_init(void) { hash_table_create_direct(&service_pids, default_pool, 0); } void service_pids_deinit(void) { struct hash_iterate_context *iter; void *key; struct service_process *process; /* free all child process information */ iter = hash_table_iterate_init(service_pids); while (hash_table_iterate(iter, service_pids, &key, &process)) service_process_destroy(process); hash_table_iterate_deinit(&iter); hash_table_destroy(&service_pids); } dovecot-2.2.33.2/src/master/Makefile.in0000644000175000017500000005716013172375574014503 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ sbin_PROGRAMS = dovecot$(EXEEXT) subdir = src/master ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" PROGRAMS = $(sbin_PROGRAMS) am__dovecot_SOURCES_DIST = capabilities-posix.c dup2-array.c main.c \ master-client.c master-settings.c service-anvil.c \ service-listen.c service-log.c service-monitor.c \ service-process.c service-process-notify.c service.c \ sd-daemon.c @HAVE_SYSTEMD_TRUE@am__objects_1 = sd-daemon.$(OBJEXT) am_dovecot_OBJECTS = capabilities-posix.$(OBJEXT) dup2-array.$(OBJEXT) \ main.$(OBJEXT) master-client.$(OBJEXT) \ master-settings.$(OBJEXT) service-anvil.$(OBJEXT) \ service-listen.$(OBJEXT) service-log.$(OBJEXT) \ service-monitor.$(OBJEXT) service-process.$(OBJEXT) \ service-process-notify.$(OBJEXT) service.$(OBJEXT) \ $(am__objects_1) dovecot_OBJECTS = $(am_dovecot_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dovecot_SOURCES) DIST_SOURCES = $(am__dovecot_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @HAVE_SYSTEMD_TRUE@SYSTEMD_SOURCES = sd-daemon.c AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DBINDIR=\""$(bindir)"\" dovecot_LDADD = \ $(LIBCAP) \ $(LIBDOVECOT) dovecot_DEPENDENCIES = $(LIBDOVECOT_DEPS) dovecot_SOURCES = \ capabilities-posix.c \ dup2-array.c \ main.c \ master-client.c \ master-settings.c \ service-anvil.c \ service-listen.c \ service-log.c \ service-monitor.c \ service-process.c \ service-process-notify.c \ service.c \ $(SYSTEMD_SOURCES) noinst_HEADERS = \ capabilities.h \ common.h \ dup2-array.h \ master-client.h \ master-settings.h \ sd-daemon.h \ service-anvil.h \ service-listen.h \ service-log.h \ service-monitor.h \ service-process.h \ service-process-notify.h \ service.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/master/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/master/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list dovecot$(EXEEXT): $(dovecot_OBJECTS) $(dovecot_DEPENDENCIES) $(EXTRA_dovecot_DEPENDENCIES) @rm -f dovecot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dovecot_OBJECTS) $(dovecot_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/capabilities-posix.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dup2-array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sd-daemon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-anvil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-listen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-monitor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-process-notify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service-process.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-sbinPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-sbinPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-sbinPROGRAMS cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-sbinPROGRAMS install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/master/service-anvil.c0000644000175000017500000001326613165463624015344 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "ioloop.h" #include "fd-close-on-exec.h" #include "fd-set-nonblock.h" #include "fdpass.h" #include "service.h" #include "service-process.h" #include "service-process-notify.h" #include "service-anvil.h" #include #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" struct service_anvil_global *service_anvil_global; static void service_list_anvil_discard_input_stop(struct service_anvil_global *anvil) { if (anvil->io_blocking != NULL) { io_remove(&anvil->io_blocking); io_remove(&anvil->io_nonblocking); } } static void anvil_input_fd_discard(struct service_anvil_global *anvil, int fd) { char buf[1024]; ssize_t ret; ret = read(fd, buf, sizeof(buf)); if (ret <= 0) { i_error("read(anvil fd) failed: %m"); service_list_anvil_discard_input_stop(anvil); } } static void anvil_input_blocking_discard(struct service_anvil_global *anvil) { anvil_input_fd_discard(anvil, anvil->blocking_fd[0]); } static void anvil_input_nonblocking_discard(struct service_anvil_global *anvil) { anvil_input_fd_discard(anvil, anvil->nonblocking_fd[0]); } static void service_list_anvil_discard_input(struct service_anvil_global *anvil) { if (anvil->io_blocking != NULL) return; anvil->io_blocking = io_add(anvil->blocking_fd[0], IO_READ, anvil_input_blocking_discard, anvil); anvil->io_nonblocking = io_add(anvil->nonblocking_fd[0], IO_READ, anvil_input_nonblocking_discard, anvil); } static int anvil_send_handshake(int fd, const char **error_r) { ssize_t ret; ret = write(fd, ANVIL_HANDSHAKE, strlen(ANVIL_HANDSHAKE)); if (ret < 0) { *error_r = t_strdup_printf("write(anvil) failed: %m"); return -1; } if (ret == 0) { *error_r = t_strdup_printf("write(anvil) returned EOF"); return -1; } /* this is a pipe, it either wrote all of it or nothing */ i_assert((size_t)ret == strlen(ANVIL_HANDSHAKE)); return 0; } static int service_process_write_anvil_kill(int fd, struct service_process *process) { const char *data; data = t_strdup_printf("KILL\t%s\n", dec2str(process->pid)); if (write(fd, data, strlen(data)) < 0) { if (errno != EAGAIN) i_error("write(anvil process) failed: %m"); return -1; } return 0; } void service_anvil_monitor_start(struct service_list *service_list) { struct service *service; if (service_anvil_global->process_count == 0) service_list_anvil_discard_input(service_anvil_global); else { service = service_lookup_type(service_list, SERVICE_TYPE_ANVIL); (void)service_process_create(service); } } void service_anvil_process_created(struct service_process *process) { struct service_anvil_global *anvil = service_anvil_global; const char *error; service_anvil_global->pid = process->pid; service_anvil_global->uid = process->uid; service_anvil_global->process_count++; service_list_anvil_discard_input_stop(anvil); if (anvil_send_handshake(anvil->blocking_fd[1], &error) < 0 || anvil_send_handshake(anvil->nonblocking_fd[1], &error) < 0) service_error(process->service, "%s", error); } void service_anvil_process_destroyed(struct service_process *process) { i_assert(service_anvil_global->process_count > 0); if (--service_anvil_global->process_count == 0) service_list_anvil_discard_input(service_anvil_global); if (service_anvil_global->pid == process->pid) service_anvil_global->pid = 0; service_anvil_global->restarted = TRUE; } void service_anvil_send_log_fd(void) { ssize_t ret; char b; if (service_anvil_global->process_count == 0) return; ret = fd_send(service_anvil_global->log_fdpass_fd[1], services->anvil->log_fd[1], &b, 1); if (ret < 0) i_error("fd_send(anvil log fd) failed: %m"); else if (ret == 0) i_error("fd_send(anvil log fd) failed: disconnected"); } void service_anvil_global_init(void) { struct service_anvil_global *anvil; anvil = i_new(struct service_anvil_global, 1); if (pipe(anvil->status_fd) < 0) i_fatal("pipe() failed: %m"); if (pipe(anvil->blocking_fd) < 0) i_fatal("pipe() failed: %m"); if (pipe(anvil->nonblocking_fd) < 0) i_fatal("pipe() failed: %m"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, anvil->log_fdpass_fd) < 0) i_fatal("socketpair() failed: %m"); fd_set_nonblock(anvil->status_fd[0], TRUE); fd_set_nonblock(anvil->status_fd[1], TRUE); fd_set_nonblock(anvil->nonblocking_fd[1], TRUE); fd_close_on_exec(anvil->status_fd[0], TRUE); fd_close_on_exec(anvil->status_fd[1], TRUE); fd_close_on_exec(anvil->blocking_fd[0], TRUE); fd_close_on_exec(anvil->blocking_fd[1], TRUE); fd_close_on_exec(anvil->nonblocking_fd[0], TRUE); fd_close_on_exec(anvil->nonblocking_fd[1], TRUE); fd_close_on_exec(anvil->log_fdpass_fd[0], TRUE); fd_close_on_exec(anvil->log_fdpass_fd[1], TRUE); anvil->kills = service_process_notify_init(anvil->nonblocking_fd[1], service_process_write_anvil_kill); service_anvil_global = anvil; } void service_anvil_global_deinit(void) { struct service_anvil_global *anvil = service_anvil_global; service_list_anvil_discard_input_stop(anvil); service_process_notify_deinit(&anvil->kills); if (close(anvil->log_fdpass_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->log_fdpass_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->blocking_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->blocking_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->nonblocking_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->nonblocking_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->status_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->status_fd[1]) < 0) i_error("close(anvil) failed: %m"); i_free(anvil); service_anvil_global = NULL; } dovecot-2.2.33.2/src/master/service-process-notify.h0000644000175000017500000000073513123174404017206 00000000000000#ifndef SERVICE_PROCESS_NOTIFY_H #define SERVICE_PROCESS_NOTIFY_H typedef int service_process_notify_callback_t(int fd, struct service_process *process); struct service_process_notify * service_process_notify_init(int fd, service_process_notify_callback_t *write_callback); void service_process_notify_deinit(struct service_process_notify **notify); void service_process_notify_add(struct service_process_notify *notify, struct service_process *process); #endif dovecot-2.2.33.2/src/master/sd-daemon.c0000644000175000017500000002633413123174404014431 00000000000000/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** Copyright 2010 Lennart Poettering Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***/ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sd-daemon.h" int sd_listen_fds(int unset_environment) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else int r, fd; const char *e; char *p = NULL; unsigned long l; if (!(e = getenv("LISTEN_PID"))) { r = 0; goto finish; } errno = 0; l = strtoul(e, &p, 10); if (errno != 0) { r = -errno; goto finish; } if (!p || *p || l <= 0) { r = -EINVAL; goto finish; } /* Is this for us? */ if (getpid() != (pid_t) l) { r = 0; goto finish; } if (!(e = getenv("LISTEN_FDS"))) { r = 0; goto finish; } errno = 0; l = strtoul(e, &p, 10); if (errno != 0) { r = -errno; goto finish; } if (!p || *p) { r = -EINVAL; goto finish; } for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { int flags; if ((flags = fcntl(fd, F_GETFD)) < 0) { r = -errno; goto finish; } if (flags & FD_CLOEXEC) continue; if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { r = -errno; goto finish; } } r = (int) l; finish: if (unset_environment) { unsetenv("LISTEN_PID"); unsetenv("LISTEN_FDS"); } return r; #endif } int sd_is_fifo(int fd, const char *path) { struct stat st_fd; if (fd < 0) return -EINVAL; memset(&st_fd, 0, sizeof(st_fd)); if (fstat(fd, &st_fd) < 0) return -errno; if (!S_ISFIFO(st_fd.st_mode)) return 0; if (path) { struct stat st_path; memset(&st_path, 0, sizeof(st_path)); if (stat(path, &st_path) < 0) { if (errno == ENOENT || errno == ENOTDIR) return 0; return -errno; } return st_path.st_dev == st_fd.st_dev && st_path.st_ino == st_fd.st_ino; } return 1; } static int sd_is_socket_internal(int fd, int type, int listening) { struct stat st_fd; if (fd < 0 || type < 0) return -EINVAL; if (fstat(fd, &st_fd) < 0) return -errno; if (!S_ISSOCK(st_fd.st_mode)) return 0; if (type != 0) { int other_type = 0; socklen_t l = sizeof(other_type); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) return -errno; if (l != sizeof(other_type)) return -EINVAL; if (other_type != type) return 0; } if (listening >= 0) { int accepting = 0; socklen_t l = sizeof(accepting); if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) return -errno; if (l != sizeof(accepting)) return -EINVAL; if (!accepting != !listening) return 0; } return 1; } union sockaddr_union { struct sockaddr sa; struct sockaddr_in in4; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_storage storage; }; int sd_is_socket(int fd, int family, int type, int listening) { int r; if (family < 0) return -EINVAL; if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) return r; if (family > 0) { union sockaddr_union sockaddr; socklen_t l; memset(&sockaddr, 0, sizeof(sockaddr)); l = sizeof(sockaddr); if (getsockname(fd, &sockaddr.sa, &l) < 0) return -errno; if (l < sizeof(sa_family_t)) return -EINVAL; return sockaddr.sa.sa_family == family; } return 1; } int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { union sockaddr_union sockaddr; socklen_t l; int r; if (family != 0 && family != AF_INET && family != AF_INET6) return -EINVAL; if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); l = sizeof(sockaddr); if (getsockname(fd, &sockaddr.sa, &l) < 0) return -errno; if (l < sizeof(sa_family_t)) return -EINVAL; if (sockaddr.sa.sa_family != AF_INET && sockaddr.sa.sa_family != AF_INET6) return 0; if (family > 0) if (sockaddr.sa.sa_family != family) return 0; if (port > 0) { if (sockaddr.sa.sa_family == AF_INET) { if (l < sizeof(struct sockaddr_in)) return -EINVAL; return htons(port) == sockaddr.in4.sin_port; } else { if (l < sizeof(struct sockaddr_in6)) return -EINVAL; return htons(port) == sockaddr.in6.sin6_port; } } return 1; } int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { union sockaddr_union sockaddr; socklen_t l; int r; if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); l = sizeof(sockaddr); if (getsockname(fd, &sockaddr.sa, &l) < 0) return -errno; if (l < sizeof(sa_family_t)) return -EINVAL; if (sockaddr.sa.sa_family != AF_UNIX) return 0; if (path) { if (length <= 0) length = strlen(path); if (length <= 0) /* Unnamed socket */ return l == offsetof(struct sockaddr_un, sun_path); if (path[0]) /* Normal path socket */ return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && memcmp(path, sockaddr.un.sun_path, length+1) == 0; else /* Abstract namespace socket */ return (l == offsetof(struct sockaddr_un, sun_path) + length) && memcmp(path, sockaddr.un.sun_path, length) == 0; } return 1; } int sd_notify(int unset_environment, const char *state) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) return 0; #else int fd = -1, r; struct msghdr msghdr; struct iovec iovec; union sockaddr_union sockaddr; const char *e; if (!state) { r = -EINVAL; goto finish; } if (!(e = getenv("NOTIFY_SOCKET"))) return 0; /* Must be an abstract socket, or an absolute path */ if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { r = -EINVAL; goto finish; } if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { r = -errno; goto finish; } memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sa.sa_family = AF_UNIX; strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); if (sockaddr.un.sun_path[0] == '@') sockaddr.un.sun_path[0] = 0; memset(&iovec, 0, sizeof(iovec)); iovec.iov_base = (char*) state; iovec.iov_len = strlen(state); memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = &sockaddr; msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) msghdr.msg_namelen = sizeof(struct sockaddr_un); msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { r = -errno; goto finish; } r = 1; finish: if (unset_environment) unsetenv("NOTIFY_SOCKET"); if (fd >= 0) close(fd); return r; #endif } int sd_notifyf(int unset_environment, const char *format, ...) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else va_list ap; char *p = NULL; int r; va_start(ap, format); r = vasprintf(&p, format, ap); va_end(ap); if (r < 0 || !p) return -ENOMEM; r = sd_notify(unset_environment, p); free(p); return r; #endif } int sd_booted(void) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else struct stat a, b; /* We simply test whether the systemd cgroup hierarchy is * mounted */ if (lstat("/sys/fs/cgroup", &a) < 0) return 0; if (lstat("/sys/fs/cgroup/systemd", &b) < 0) return 0; return a.st_dev != b.st_dev; #endif } dovecot-2.2.33.2/src/master/service-listen.h0000644000175000017500000000125313123174404015514 00000000000000#ifndef SERVICE_LISTEN_H #define SERVICE_LISTEN_H /* Start listening in all services. Returns -1 for fatal failures, 0 if some of the addresses are already being used or path for unix socket was lost, 1 if all is ok. It's safe to call this function multiple times. */ int services_listen(struct service_list *service_list); /* Move common listener fds from old_services to new_services, close those that aren't needed anymore and finally call services_listen() to add missing listeners. */ int services_listen_using(struct service_list *new_service_list, struct service_list *old_service_list); int service_listener_listen(struct service_listener *l); #endif dovecot-2.2.33.2/src/master/service-log.c0000644000175000017500000001067713123174404015004 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "aqueue.h" #include "hash.h" #include "ioloop.h" #include "fd-close-on-exec.h" #include "fd-set-nonblock.h" #include "service.h" #include "service-process.h" #include "service-process-notify.h" #include "service-anvil.h" #include "service-log.h" #include static int service_log_fds_init(const char *log_prefix, int log_fd[2], buffer_t *handshake_buf) { struct log_service_handshake handshake; ssize_t ret; i_assert(log_fd[0] == -1); if (pipe(log_fd) < 0) { i_error("pipe() failed: %m"); return -1; } fd_close_on_exec(log_fd[0], TRUE); fd_close_on_exec(log_fd[1], TRUE); i_zero(&handshake); handshake.log_magic = MASTER_LOG_MAGIC; handshake.prefix_len = strlen(log_prefix); buffer_set_used_size(handshake_buf, 0); buffer_append(handshake_buf, &handshake, sizeof(handshake)); buffer_append(handshake_buf, log_prefix, strlen(log_prefix)); ret = write(log_fd[1], handshake_buf->data, handshake_buf->used); if (ret < 0) { i_error("write(log handshake) failed: %m"); return -1; } if ((size_t)ret != handshake_buf->used) { i_error("write(log handshake) didn't write everything"); return -1; } return 0; } static int service_process_write_log_bye(int fd, struct service_process *process) { const char *data; if (process->service->log_process_internal_fd == -1) { /* another log process was just destroyed */ return 0; } data = t_strdup_printf("%d %s BYE\n", process->service->log_process_internal_fd, dec2str(process->pid)); if (write(fd, data, strlen(data)) < 0) { if (errno != EAGAIN) i_error("write(log process) failed: %m"); return -1; } return 0; } int services_log_init(struct service_list *service_list) { struct service *const *services; const char *log_prefix; buffer_t *handshake_buf; ssize_t ret = 0; int fd; handshake_buf = buffer_create_dynamic(default_pool, 256); if (service_log_fds_init(MASTER_LOG_PREFIX_NAME, service_list->master_log_fd, handshake_buf) < 0) ret = -1; else fd_set_nonblock(service_list->master_log_fd[1], TRUE); i_assert(service_list->log_byes == NULL); service_list->log_byes = service_process_notify_init(service_list->master_log_fd[1], service_process_write_log_bye); fd = MASTER_LISTEN_FD_FIRST + 1; array_foreach(&service_list->services, services) { struct service *service = *services; if (service->type == SERVICE_TYPE_LOG) continue; log_prefix = t_strconcat(service->set->name, ": ", NULL); if (service_log_fds_init(log_prefix, service->log_fd, handshake_buf) < 0) { ret = -1; break; } service->log_process_internal_fd = fd++; } buffer_free(&handshake_buf); if (ret < 0) { services_log_deinit(service_list); return -1; } service_anvil_send_log_fd(); return 0; } void services_log_deinit(struct service_list *service_list) { struct service *const *services; unsigned int i, count; services = array_get(&service_list->services, &count); for (i = 0; i < count; i++) { if (services[i]->log_fd[0] != -1) { if (close(services[i]->log_fd[0]) < 0) { service_error(services[i], "close(log_fd) failed: %m"); } if (close(services[i]->log_fd[1]) < 0) { service_error(services[i], "close(log_fd) failed: %m"); } services[i]->log_fd[0] = -1; services[i]->log_fd[1] = -1; services[i]->log_process_internal_fd = -1; } } if (service_list->log_byes != NULL) service_process_notify_deinit(&service_list->log_byes); if (service_list->master_log_fd[0] != -1) { if (close(service_list->master_log_fd[0]) < 0) i_error("close(master log fd) failed: %m"); if (close(service_list->master_log_fd[1]) < 0) i_error("close(master log fd) failed: %m"); service_list->master_log_fd[0] = -1; service_list->master_log_fd[1] = -1; } } void services_log_dup2(ARRAY_TYPE(dup2) *dups, struct service_list *service_list, unsigned int first_fd, unsigned int *fd_count) { struct service *const *services; unsigned int n = 0; /* master log fd is always the first one */ dup2_append(dups, service_list->master_log_fd[0], first_fd); n++; *fd_count += 1; array_foreach(&service_list->services, services) { struct service *service = *services; if (service->log_fd[1] == -1) continue; i_assert((int)(first_fd + n) == service->log_process_internal_fd); dup2_append(dups, service->log_fd[0], first_fd + n); n++; *fd_count += 1; } } dovecot-2.2.33.2/src/master/common.h0000644000175000017500000000107113123174404014046 00000000000000#ifndef COMMON_H #define COMMON_H #include "lib.h" #include "master-interface.h" #include "master-settings.h" extern uid_t master_uid; extern gid_t master_gid; extern bool core_dumps_disabled; extern const char *ssl_manual_key_password; extern int global_master_dead_pipe_fd[2]; extern struct service_list *services; extern bool startup_finished; void process_exec(const char *cmd) ATTR_NORETURN; int get_uidgid(const char *user, uid_t *uid_r, gid_t *gid_r, const char **error_r); int get_gid(const char *group, gid_t *gid_r, const char **error_r); #endif dovecot-2.2.33.2/src/master/capabilities.h0000644000175000017500000000025513123174404015212 00000000000000#ifndef CAPABILITIES_H #define CAPABILITIES_H #if defined(HAVE_LIBCAP) void drop_capabilities(void); #else static inline void drop_capabilities(void) {} #endif #endif dovecot-2.2.33.2/src/master/service-listen.c0000644000175000017500000002766013165463624015534 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "fd-set-nonblock.h" #include "fd-close-on-exec.h" #include "ioloop.h" #include "net.h" #ifdef HAVE_SYSTEMD #include "sd-daemon.h" #endif #include "service.h" #include "service-listen.h" #include #include #include #include #define MIN_BACKLOG 4 static unsigned int service_get_backlog(struct service *service) { unsigned int backlog; i_assert(service->process_limit > 0); i_assert(service->client_limit > 0); /* as unlikely as it is, avoid overflows */ if (service->client_limit > INT_MAX / service->process_limit) backlog = INT_MAX; else backlog = service->process_limit * service->client_limit; return I_MAX(backlog, MIN_BACKLOG); } static int service_file_chown(const struct service_listener *l) { uid_t uid = l->set.fileset.uid; uid_t gid = l->set.fileset.gid; if ((uid == (uid_t)-1 || uid == master_uid) && (gid == (gid_t)-1 || gid == master_gid)) return 0; if (chown(l->set.fileset.set->path, uid, gid) < 0) { service_error(l->service, "chown(%s, %lld, %lld) failed: %m", l->set.fileset.set->path, (long long)uid, (long long)gid); return -1; } return 0; } static int service_unix_listener_listen(struct service_listener *l) { struct service *service = l->service; const struct file_listener_settings *set = l->set.fileset.set; mode_t old_umask; int fd, i; old_umask = umask((set->mode ^ 0777) & 0777); for (i = 0;; i++) { fd = net_listen_unix(set->path, service_get_backlog(service)); if (fd != -1) break; if (errno == EISDIR || errno == ENOENT) { /* looks like the path doesn't exist. */ return 0; } if (errno != EADDRINUSE) { service_error(service, "net_listen_unix(%s) failed: %m", set->path); return -1; } /* already in use - see if it really exists. after 3 times just fail here. */ fd = net_connect_unix(set->path); if (fd != -1 || errno != ECONNREFUSED || i >= 3) { if (fd != -1) i_close_fd(&fd); service_error(service, "Socket already exists: %s", set->path); return 0; } /* delete and try again */ if (unlink(set->path) < 0 && errno != ENOENT) { service_error(service, "unlink(%s) failed: %m", set->path); return -1; } } umask(old_umask); i_assert(fd != -1); if (service_file_chown(l) < 0) { i_close_fd(&fd); return -1; } net_set_nonblock(fd, TRUE); fd_close_on_exec(fd, TRUE); l->fd = fd; return 1; } static int service_fifo_listener_listen(struct service_listener *l) { struct service *service = l->service; const struct file_listener_settings *set = l->set.fileset.set; unsigned int i; mode_t old_umask; int fd, ret; for (i = 0;; i++) { old_umask = umask((set->mode ^ 0777) & 0777); ret = mkfifo(set->path, set->mode); umask(old_umask); if (ret == 0) break; if (ret < 0 && (errno != EEXIST || i == 1)) { service_error(service, "mkfifo(%s) failed: %m", set->path); return -1; } if (unlink(set->path) < 0) { service_error(service, "unlink(%s) failed: %m", set->path); return -1; } } if (service_file_chown(l) < 0) return -1; /* open as RDWR, so that even if the last writer closes, we won't get EOF errors */ fd = open(set->path, O_RDWR | O_NONBLOCK); if (fd == -1) { service_error(service, "open(%s) failed: %m", set->path); return -1; } fd_close_on_exec(fd, TRUE); l->fd = fd; return 1; } #ifdef HAVE_SYSTEMD static int systemd_listen_fd(const struct ip_addr *ip, in_port_t port, int *fd_r) { static int sd_fds = -1; int fd, fd_max; if (sd_fds < 0) { sd_fds = sd_listen_fds(0); if (sd_fds < 0) { i_error("sd_listen_fds() failed: %s", strerror(-sd_fds)); return -1; } } fd_max = SD_LISTEN_FDS_START + sd_fds - 1; for (fd = SD_LISTEN_FDS_START; fd <= fd_max; fd++) { if (sd_is_socket_inet(fd, ip->family, SOCK_STREAM, 1, port)) { *fd_r = fd; return 0; } } /* when systemd didn't provide a usable socket, fall back to the regular socket creation code */ *fd_r = -1; return 0; } #endif static int service_inet_listener_listen(struct service_listener *l) { struct service *service = l->service; enum net_listen_flags flags = 0; const struct inet_listener_settings *set = l->set.inetset.set; in_port_t port = set->port; int fd; #ifdef HAVE_SYSTEMD if (systemd_listen_fd(&l->set.inetset.ip, port, &fd) < 0) return -1; if (fd == -1) #endif { if (set->reuse_port) flags |= NET_LISTEN_FLAG_REUSEPORT; fd = net_listen_full(&l->set.inetset.ip, &port, &flags, service_get_backlog(service)); if (fd < 0) { service_error(service, "listen(%s, %u) failed: %m", l->inet_address, set->port); return errno == EADDRINUSE ? 0 : -1; } l->reuse_port = (flags & NET_LISTEN_FLAG_REUSEPORT) != 0; } net_set_nonblock(fd, TRUE); fd_close_on_exec(fd, TRUE); l->fd = fd; return 1; } int service_listener_listen(struct service_listener *l) { switch (l->type) { case SERVICE_LISTENER_UNIX: return service_unix_listener_listen(l); case SERVICE_LISTENER_FIFO: return service_fifo_listener_listen(l); case SERVICE_LISTENER_INET: return service_inet_listener_listen(l); } i_unreached(); } static int service_listen(struct service *service) { struct service_listener *const *listeners; int ret = 1, ret2 = 0; array_foreach(&service->listeners, listeners) { struct service_listener *l = *listeners; if (l->fd != -1) continue; ret2 = service_listener_listen(l); if (ret2 < ret) ret = ret2; } return ret; } #ifdef HAVE_SYSTEMD static int get_socket_info(int fd, unsigned int *family, in_port_t *port) { union sockaddr_union { struct sockaddr sa; struct sockaddr_in in4; struct sockaddr_in6 in6; } sockaddr; socklen_t l; // FIXME(Stephan): why -1? if (port) *port = -1; if (family) *family = -1; i_zero(&sockaddr); l = sizeof(sockaddr); if (getsockname(fd, &sockaddr.sa, &l) < 0) return -errno; if (family) *family = sockaddr.sa.sa_family; if (port) { if (sockaddr.sa.sa_family == AF_INET) { if (l < sizeof(struct sockaddr_in)) return -EINVAL; *port = ntohs(sockaddr.in4.sin_port); } else { if (l < sizeof(struct sockaddr_in6)) return -EINVAL; *port = ntohs(sockaddr.in6.sin6_port); } } return 0; } static int services_verify_systemd(struct service_list *service_list) { struct service *const *services; static int sd_fds = -1; int fd, fd_max; if (sd_fds < 0) { sd_fds = sd_listen_fds(0); if (sd_fds == -1) { i_error("sd_listen_fds() failed: %m"); return -1; } } fd_max = SD_LISTEN_FDS_START + sd_fds - 1; for (fd = SD_LISTEN_FDS_START; fd <= fd_max; fd++) { if (sd_is_socket_inet(fd, 0, SOCK_STREAM, 1, 0) > 0) { int found = FALSE; in_port_t port; unsigned int family; get_socket_info(fd, &family, &port); array_foreach(&service_list->services, services) { struct service_listener *const *listeners; array_foreach(&(*services)->listeners, listeners) { struct service_listener *l = *listeners; if (l->type != SERVICE_LISTENER_INET) continue; if (l->set.inetset.set->port == port && l->set.inetset.ip.family == family) { found = TRUE; break; } } if (found) break; } if (!found) { i_error("systemd listens on port %d, but it's not configured in Dovecot. Closing.",port); if (shutdown(fd, SHUT_RDWR) < 0 && errno != ENOTCONN) i_error("shutdown() failed: %m"); if (dup2(dev_null_fd, fd) < 0) i_error("dup2() failed: %m"); } } } return 0; } #endif static int services_listen_master(struct service_list *service_list) { const char *path; mode_t old_umask; path = t_strdup_printf("%s/master", service_list->set->base_dir); old_umask = umask(0600 ^ 0777); service_list->master_fd = net_listen_unix(path, 16); if (service_list->master_fd == -1 && errno == EADDRINUSE) { /* already in use. all the other sockets were fine, so just delete this and retry. */ i_unlink_if_exists(path); service_list->master_fd = net_listen_unix(path, 16); } umask(old_umask); if (service_list->master_fd == -1) { i_error("net_listen_unix(%s) failed: %m", path); return 0; } fd_close_on_exec(service_list->master_fd, TRUE); return 1; } int services_listen(struct service_list *service_list) { struct service *const *services; int ret = 1, ret2; array_foreach(&service_list->services, services) { ret2 = service_listen(*services); if (ret2 < ret) ret = ret2; } /* reloading config wants to continue even when we're returning 0. */ if (ret >= 0) { ret2 = services_listen_master(service_list); if (ret2 < ret) ret = ret2; } #ifdef HAVE_SYSTEMD if (ret > 0) services_verify_systemd(service_list); #endif return ret; } static int listener_equals(const struct service_listener *l1, const struct service_listener *l2) { if (l1->type != l2->type) return FALSE; switch (l1->type) { case SERVICE_LISTENER_UNIX: case SERVICE_LISTENER_FIFO: /* We could just keep using the same listener, but it's more likely to cause problems if old process accepts a connection before it knows that it should die. So just always unlink and recreate unix/fifo listeners. */ return FALSE; case SERVICE_LISTENER_INET: if (memcmp(&l1->set.inetset.ip, &l2->set.inetset.ip, sizeof(l1->set.inetset.ip)) != 0) return FALSE; if (l1->set.inetset.set->port != l2->set.inetset.set->port) return FALSE; return TRUE; } return FALSE; } int services_listen_using(struct service_list *new_service_list, struct service_list *old_service_list) { struct service *const *services, *old_service, *new_service; ARRAY(struct service_listener *) new_listeners_arr; ARRAY(struct service_listener *) old_listeners_arr; struct service_listener *const *new_listeners, *const *old_listeners; unsigned int i, j, count, new_count, old_count; /* rescue anvil's UNIX socket listener */ new_service = service_lookup_type(new_service_list, SERVICE_TYPE_ANVIL); old_service = service_lookup_type(old_service_list, SERVICE_TYPE_ANVIL); if (old_service != NULL && new_service != NULL) { new_listeners = array_get(&new_service->listeners, &new_count); old_listeners = array_get(&old_service->listeners, &old_count); for (i = 0; i < old_count && i < new_count; i++) { if (new_listeners[i]->type != old_listeners[i]->type) break; } if (i != new_count && i != old_count) { i_error("Can't change anvil's listeners on the fly"); return -1; } for (i = 0; i < new_count; i++) { new_listeners[i]->fd = old_listeners[i]->fd; old_listeners[i]->fd = -1; } } /* first create an arrays of all listeners to make things easier */ t_array_init(&new_listeners_arr, 64); services = array_get(&new_service_list->services, &count); for (i = 0; i < count; i++) array_append_array(&new_listeners_arr, &services[i]->listeners); t_array_init(&old_listeners_arr, 64); services = array_get(&old_service_list->services, &count); for (i = 0; i < count; i++) array_append_array(&old_listeners_arr, &services[i]->listeners); /* then start moving fds */ new_listeners = array_get(&new_listeners_arr, &new_count); old_listeners = array_get(&old_listeners_arr, &old_count); for (i = 0; i < new_count; i++) { for (j = 0; j < old_count; j++) { if (old_listeners[j]->fd != -1 && listener_equals(new_listeners[i], old_listeners[j])) { new_listeners[i]->fd = old_listeners[j]->fd; old_listeners[j]->fd = -1; break; } } } /* close what's left */ for (j = 0; j < old_count; j++) { if (old_listeners[j]->fd == -1) continue; if (close(old_listeners[j]->fd) < 0) i_error("close(listener) failed: %m"); switch (old_listeners[j]->type) { case SERVICE_LISTENER_UNIX: case SERVICE_LISTENER_FIFO: { i_unlink(old_listeners[j]->set.fileset.set->path); break; } case SERVICE_LISTENER_INET: break; } } /* and let services_listen() deal with the remaining fds */ return services_listen(new_service_list); } dovecot-2.2.33.2/src/master/service-process.c0000644000175000017500000003750313165463624015711 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "aqueue.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "write-full.h" #include "base64.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "llist.h" #include "hostpid.h" #include "env-util.h" #include "fd-close-on-exec.h" #include "restrict-access.h" #include "restrict-process-size.h" #include "eacces-error.h" #include "master-service.h" #include "master-service-settings.h" #include "dup2-array.h" #include "service.h" #include "service-anvil.h" #include "service-listen.h" #include "service-log.h" #include "service-process-notify.h" #include "service-process.h" #include #include #include #include #include static void service_reopen_inet_listeners(struct service *service) { struct service_listener *const *listeners; unsigned int i, count; int old_fd; listeners = array_get(&service->listeners, &count); for (i = 0; i < count; i++) { if (!listeners[i]->reuse_port || listeners[i]->fd == -1) continue; old_fd = listeners[i]->fd; listeners[i]->fd = -1; if (service_listener_listen(listeners[i]) < 0) listeners[i]->fd = old_fd; } } static void service_dup_fds(struct service *service) { struct service_listener *const *listeners; ARRAY_TYPE(dup2) dups; string_t *listener_settings; int fd = MASTER_LISTEN_FD_FIRST; unsigned int i, count, socket_listener_count; /* stdin/stdout is already redirected to /dev/null. Other master fds should have been opened with fd_close_on_exec() so we don't have to worry about them. because the destination fd might be another one's source fd we have to be careful not to overwrite anything. dup() the fd when needed */ socket_listener_count = 0; listeners = array_get(&service->listeners, &count); t_array_init(&dups, count + 10); switch (service->type) { case SERVICE_TYPE_LOG: i_assert(fd == MASTER_LISTEN_FD_FIRST); services_log_dup2(&dups, service->list, fd, &socket_listener_count); fd += socket_listener_count; break; case SERVICE_TYPE_ANVIL: dup2_append(&dups, service_anvil_global->log_fdpass_fd[0], MASTER_ANVIL_LOG_FDPASS_FD); /* nonblocking anvil fd must be the first one. anvil treats it as the master's fd */ dup2_append(&dups, service_anvil_global->nonblocking_fd[0], fd++); dup2_append(&dups, service_anvil_global->blocking_fd[0], fd++); socket_listener_count += 2; break; default: break; } /* add listeners */ listener_settings = t_str_new(256); for (i = 0; i < count; i++) { if (listeners[i]->fd != -1) { str_truncate(listener_settings, 0); str_append_tabescaped(listener_settings, listeners[i]->name); if (listeners[i]->type == SERVICE_LISTENER_INET) { if (listeners[i]->set.inetset.set->ssl) str_append(listener_settings, "\tssl"); if (listeners[i]->set.inetset.set->haproxy) str_append(listener_settings, "\thaproxy"); } dup2_append(&dups, listeners[i]->fd, fd++); env_put(t_strdup_printf("SOCKET%d_SETTINGS=%s", socket_listener_count, str_c(listener_settings))); socket_listener_count++; } } if (service->login_notify_fd != -1) { dup2_append(&dups, service->login_notify_fd, MASTER_LOGIN_NOTIFY_FD); } switch (service->type) { case SERVICE_TYPE_LOG: case SERVICE_TYPE_ANVIL: case SERVICE_TYPE_CONFIG: dup2_append(&dups, dev_null_fd, MASTER_ANVIL_FD); break; case SERVICE_TYPE_UNKNOWN: case SERVICE_TYPE_LOGIN: case SERVICE_TYPE_STARTUP: dup2_append(&dups, service_anvil_global->blocking_fd[1], MASTER_ANVIL_FD); break; } dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD); if (service->type != SERVICE_TYPE_ANVIL) { dup2_append(&dups, service->master_dead_pipe_fd[1], MASTER_DEAD_FD); } else { dup2_append(&dups, global_master_dead_pipe_fd[1], MASTER_DEAD_FD); } if (service->type == SERVICE_TYPE_LOG) { /* keep stderr as-is. this is especially important when log_path=/dev/stderr, but might be helpful even in other situations for logging startup errors */ } else { /* set log file to stderr. dup2() here immediately so that we can set up logging to it without causing any log messages to be lost. */ i_assert(service->log_fd[1] != -1); env_put("LOG_SERVICE=1"); if (dup2(service->log_fd[1], STDERR_FILENO) < 0) i_fatal("dup2(log fd) failed: %m"); i_set_failure_internal(); } /* make sure we don't leak syslog fd. try to do it as late as possible, but also before dup2()s in case syslog fd is one of them. */ closelog(); if (dup2_array(&dups) < 0) i_fatal("service(%s): dup2s failed", service->set->name); i_assert(fd == MASTER_LISTEN_FD_FIRST + (int)socket_listener_count); env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count)); } static void drop_privileges(struct service *service) { struct restrict_access_settings rset; bool disallow_root; size_t len; if (service->vsz_limit != 0) restrict_process_size(service->vsz_limit); restrict_access_init(&rset); rset.uid = service->uid; rset.gid = service->gid; rset.privileged_gid = service->privileged_gid; rset.chroot_dir = *service->set->chroot == '\0' ? NULL : service->set->chroot; if (rset.chroot_dir != NULL) { /* drop trailing / if it exists */ len = strlen(rset.chroot_dir); if (rset.chroot_dir[len-1] == '/') rset.chroot_dir = t_strndup(rset.chroot_dir, len-1); } rset.extra_groups = service->extra_gids; restrict_access_set_env(&rset); if (service->set->drop_priv_before_exec) { disallow_root = service->type == SERVICE_TYPE_LOGIN; restrict_access(&rset, NULL, disallow_root); } } static void service_process_setup_config_environment(struct service *service) { const struct master_service_settings *set = service->list->service_set; switch (service->type) { case SERVICE_TYPE_CONFIG: env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=", service->config_file_path, NULL)); break; case SERVICE_TYPE_LOG: /* give the log's configuration directly, so it won't depend on config process */ env_put("DOVECONF_ENV=1"); env_put(t_strconcat("LOG_PATH=", set->log_path, NULL)); env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL)); env_put(t_strconcat("DEBUG_LOG_PATH=", set->debug_log_path, NULL)); env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL)); env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL)); if (set->verbose_proctitle) env_put("VERBOSE_PROCTITLE=1"); env_put("SSL=no"); break; default: env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=", services_get_config_socket_path(service->list), NULL)); break; } } static void service_process_setup_environment(struct service *service, unsigned int uid, const char *hostdomain) { master_service_env_clean(); env_put(MASTER_IS_PARENT_ENV"=1"); service_process_setup_config_environment(service); env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", service->client_limit)); env_put(t_strdup_printf(MASTER_PROCESS_LIMIT_ENV"=%u", service->process_limit)); env_put(t_strdup_printf(MASTER_PROCESS_MIN_AVAIL_ENV"=%u", service->set->process_min_avail)); env_put(t_strdup_printf(MASTER_SERVICE_IDLE_KILL_ENV"=%u", service->idle_kill)); if (service->set->service_count != 0) { env_put(t_strdup_printf(MASTER_SERVICE_COUNT_ENV"=%u", service->set->service_count)); } env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid)); env_put(t_strdup_printf(MY_HOSTNAME_ENV"=%s", my_hostname)); env_put(t_strdup_printf(MY_HOSTDOMAIN_ENV"=%s", hostdomain)); if (!service->set->master_set->version_ignore) env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION); if (ssl_manual_key_password != NULL && service->have_inet_listeners) { /* manually given SSL password. give it only to services that have inet listeners. */ env_put(t_strconcat(MASTER_SSL_KEY_PASSWORD_ENV"=", ssl_manual_key_password, NULL)); } if (service->type == SERVICE_TYPE_ANVIL && service_anvil_global->restarted) env_put("ANVIL_RESTARTED=1"); } static void service_process_status_timeout(struct service_process *process) { service_error(process->service, "Initial status notification not received in %d " "seconds, killing the process", SERVICE_FIRST_STATUS_TIMEOUT_SECS); if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) { service_error(process->service, "kill(%s, SIGKILL) failed: %m", dec2str(process->pid)); } timeout_remove(&process->to_status); } struct service_process *service_process_create(struct service *service) { static unsigned int uid_counter = 0; struct service_process *process; unsigned int uid = ++uid_counter; const char *hostdomain; pid_t pid; bool process_forked; i_assert(service->status_fd[0] != -1); if (service->to_throttle != NULL) { /* throttling service, don't create new processes */ return NULL; } if (service->list->destroying) { /* these services are being destroyed, no point in creating new processes now */ return NULL; } /* look this up before fork()ing so that it gets cached for all the future lookups. */ hostdomain = my_hostdomain(); if (service->type == SERVICE_TYPE_ANVIL && service_anvil_global->pid != 0) { pid = service_anvil_global->pid; uid = service_anvil_global->uid; process_forked = FALSE; } else { pid = fork(); process_forked = TRUE; service->list->fork_counter++; } if (pid < 0) { int fork_errno = errno; rlim_t limit; const char *limit_str = ""; if (fork_errno == EAGAIN && restrict_get_process_limit(&limit) == 0) { limit_str = t_strdup_printf(" (ulimit -u %llu reached?)", (unsigned long long)limit); } errno = fork_errno; service_error(service, "fork() failed: %m%s", limit_str); return NULL; } if (pid == 0) { /* child */ service_process_setup_environment(service, uid, hostdomain); service_reopen_inet_listeners(service); service_dup_fds(service); drop_privileges(service); process_exec(service->executable); } i_assert(hash_table_lookup(service_pids, POINTER_CAST(pid)) == NULL); process = i_new(struct service_process, 1); process->service = service; process->refcount = 1; process->pid = pid; process->uid = uid; if (process_forked) { process->to_status = timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000, service_process_status_timeout, process); } process->available_count = service->client_limit; service->process_count++; service->process_avail++; DLLIST_PREPEND(&service->processes, process); service_list_ref(service->list); hash_table_insert(service_pids, POINTER_CAST(process->pid), process); if (service->type == SERVICE_TYPE_ANVIL && process_forked) service_anvil_process_created(process); return process; } void service_process_destroy(struct service_process *process) { struct service *service = process->service; struct service_list *service_list = service->list; DLLIST_REMOVE(&service->processes, process); hash_table_remove(service_pids, POINTER_CAST(process->pid)); if (process->available_count > 0) service->process_avail--; service->process_count--; i_assert(service->process_avail <= service->process_count); if (process->to_status != NULL) timeout_remove(&process->to_status); if (process->to_idle != NULL) timeout_remove(&process->to_idle); if (service->list->log_byes != NULL) service_process_notify_add(service->list->log_byes, process); process->destroyed = TRUE; service_process_unref(process); if (service->process_count < service->process_limit && service->type == SERVICE_TYPE_LOGIN) service_login_notify(service, FALSE); service_list_unref(service_list); } void service_process_ref(struct service_process *process) { i_assert(process->refcount > 0); process->refcount++; } void service_process_unref(struct service_process *process) { i_assert(process->refcount > 0); if (--process->refcount > 0) return; i_assert(process->destroyed); i_free(process); } static const char * get_exit_status_message(struct service *service, enum fatal_exit_status status) { string_t *str; switch (status) { case FATAL_LOGOPEN: return "Can't open log file"; case FATAL_LOGWRITE: return "Can't write to log file"; case FATAL_LOGERROR: return "Internal logging error"; case FATAL_OUTOFMEM: str = t_str_new(128); str_append(str, "Out of memory"); if (service->vsz_limit != 0) { str_printfa(str, " (service %s { vsz_limit=%u MB }, " "you may need to increase it)", service->set->name, (unsigned int)(service->vsz_limit/1024/1024)); } if (getenv("CORE_OUTOFMEM") == NULL) str_append(str, " - set CORE_OUTOFMEM=1 environment to get core dump"); return str_c(str); case FATAL_EXEC: return "exec() failed"; case FATAL_DEFAULT: return "Fatal failure"; } return NULL; } static void log_coredump(struct service *service, string_t *str, int status) { #ifdef WCOREDUMP int signum = WTERMSIG(status); if (WCOREDUMP(status)) { str_append(str, " (core dumped)"); return; } if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS) return; /* let's try to figure out why we didn't get a core dump */ if (core_dumps_disabled) { str_printfa(str, " (core dumps disabled)"); return; } #ifndef HAVE_PR_SET_DUMPABLE if (!service->set->drop_priv_before_exec && service->uid != 0) { str_printfa(str, " (core not dumped - set service %s " "{ drop_priv_before_exec=yes })", service->set->name); return; } if (*service->set->privileged_group != '\0' && service->uid != 0) { str_printfa(str, " (core not dumped - service %s " "{ privileged_group } prevented it)", service->set->name); return; } #else if (!service->set->login_dump_core && service->type == SERVICE_TYPE_LOGIN) { str_printfa(str, " (core not dumped - add -D parameter to " "service %s { executable }", service->set->name); return; } #endif if (service->set->chroot[0] != '\0') { str_printfa(str, " (core not dumped - try to clear " "service %s { chroot = } )", service->set->name); return; } str_append(str, " (core not dumped)"); #endif } static void service_process_get_status_error(string_t *str, struct service_process *process, int status, bool *default_fatal_r) { struct service *service = process->service; const char *msg; *default_fatal_r = FALSE; str_printfa(str, "service(%s): child %s ", service->set->name, dec2str(process->pid)); if (WIFSIGNALED(status)) { str_printfa(str, "killed with signal %d", WTERMSIG(status)); log_coredump(service, str, status); return; } if (!WIFEXITED(status)) { str_printfa(str, "died with status %d", status); return; } status = WEXITSTATUS(status); if (status == 0) { str_truncate(str, 0); return; } str_printfa(str, "returned error %d", status); msg = get_exit_status_message(service, status); if (msg != NULL) str_printfa(str, " (%s)", msg); if (status == FATAL_DEFAULT) *default_fatal_r = TRUE; } static void service_process_log(struct service_process *process, bool default_fatal, const char *str) { const char *data; if (process->service->log_fd[1] == -1) { i_error("%s", str); return; } /* log it via the log process in charge of handling this process's logging */ data = t_strdup_printf("%d %s %s %s\n", process->service->log_process_internal_fd, dec2str(process->pid), default_fatal ? "DEFAULT-FATAL" : "FATAL", str); if (write(process->service->list->master_log_fd[1], data, strlen(data)) < 0) { i_error("write(log process) failed: %m"); i_error("%s", str); } } void service_process_log_status_error(struct service_process *process, int status) { if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { /* fast path */ return; } T_BEGIN { string_t *str = t_str_new(256); bool default_fatal; service_process_get_status_error(str, process, status, &default_fatal); if (str_len(str) > 0) service_process_log(process, default_fatal, str_c(str)); } T_END; } dovecot-2.2.33.2/src/master/main.c0000644000175000017500000005101213165463624013510 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "ioloop.h" #include "lib-signals.h" #include "fd-close-on-exec.h" #include "array.h" #include "write-full.h" #include "env-util.h" #include "hostpid.h" #include "abspath.h" #include "ipwd.h" #include "str.h" #include "execv-const.h" #include "restrict-process-size.h" #include "master-instance.h" #include "master-service.h" #include "master-service-settings.h" #include "askpass.h" #include "capabilities.h" #include "master-client.h" #include "service.h" #include "service-anvil.h" #include "service-listen.h" #include "service-monitor.h" #include "service-process.h" #include "service-log.h" #include "dovecot-version.h" #include #include #include #include #define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf" #define MASTER_SERVICE_NAME "master" #define FATAL_FILENAME "master-fatal.lastlog" #define MASTER_PID_FILE_NAME "master.pid" #define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3) uid_t master_uid; gid_t master_gid; bool core_dumps_disabled; const char *ssl_manual_key_password; int global_master_dead_pipe_fd[2]; struct service_list *services; bool startup_finished = FALSE; static char *pidfile_path; static struct master_instance_list *instances; static struct timeout *to_instance; static failure_callback_t *orig_fatal_callback; static failure_callback_t *orig_error_callback; static const struct setting_parser_info *set_roots[] = { &master_setting_parser_info, NULL }; void process_exec(const char *cmd) { const char *executable, *p, **argv; argv = t_strsplit(cmd, " "); executable = argv[0]; /* hide the path, it's ugly */ p = strrchr(argv[0], '/'); if (p != NULL) argv[0] = p+1; /* prefix with dovecot/ */ argv[0] = t_strdup_printf("%s/%s", services->set->instance_name, argv[0]); if (strncmp(argv[0], PACKAGE, strlen(PACKAGE)) != 0) argv[0] = t_strconcat(PACKAGE"-", argv[0], NULL); (void)execv_const(executable, argv); } int get_uidgid(const char *user, uid_t *uid_r, gid_t *gid_r, const char **error_r) { struct passwd pw; if (*user == '\0') { *uid_r = (uid_t)-1; *gid_r = (gid_t)-1; return 0; } switch (i_getpwnam(user, &pw)) { case -1: *error_r = t_strdup_printf("getpwnam(%s) failed: %m", user); return -1; case 0: *error_r = t_strdup_printf("User doesn't exist: %s", user); return -1; default: *uid_r = pw.pw_uid; *gid_r = pw.pw_gid; return 0; } } int get_gid(const char *group, gid_t *gid_r, const char **error_r) { struct group gr; if (*group == '\0') { *gid_r = (gid_t)-1; return 0; } switch (i_getgrnam(group, &gr)) { case -1: *error_r = t_strdup_printf("getgrnam(%s) failed: %m", group); return -1; case 0: *error_r = t_strdup_printf("Group doesn't exist: %s", group); return -1; default: *gid_r = gr.gr_gid; return 0; } } static void ATTR_NORETURN ATTR_FORMAT(2, 0) master_fatal_callback(const struct failure_context *ctx, const char *format, va_list args) { const char *path, *str; va_list args2; pid_t pid; int fd; /* if we already forked a child process, this isn't fatal for the main process and there's no need to write the fatal file. */ if (str_to_pid(my_pid, &pid) < 0) i_unreached(); if (getpid() == pid) { /* write the error message to a file (we're chdired to base dir) */ path = t_strconcat(FATAL_FILENAME, NULL); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd != -1) { VA_COPY(args2, args); str = t_strdup_vprintf(format, args2); va_end(args2); (void)write_full(fd, str, strlen(str)); i_close_fd(&fd); } } orig_fatal_callback(ctx, format, args); abort(); /* just to silence the noreturn attribute warnings */ } static void ATTR_NORETURN ATTR_FORMAT(2, 0) startup_fatal_handler(const struct failure_context *ctx, const char *fmt, va_list args) { va_list args2; VA_COPY(args2, args); fprintf(stderr, "%s%s\n", failure_log_type_prefixes[ctx->type], t_strdup_vprintf(fmt, args2)); va_end(args2); orig_fatal_callback(ctx, fmt, args); abort(); } static void ATTR_FORMAT(2, 0) startup_error_handler(const struct failure_context *ctx, const char *fmt, va_list args) { va_list args2; VA_COPY(args2, args); fprintf(stderr, "%s%s\n", failure_log_type_prefixes[ctx->type], t_strdup_vprintf(fmt, args2)); va_end(args2); orig_error_callback(ctx, fmt, args); } static void fatal_log_check(const struct master_settings *set) { const char *path; char buf[1024]; ssize_t ret; int fd; path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL); fd = open(path, O_RDONLY); if (fd == -1) return; ret = read(fd, buf, sizeof(buf)-1); if (ret < 0) i_error("read(%s) failed: %m", path); else { buf[ret] = '\0'; fprintf(stderr, "Last died with error (see error log for more " "information): %s\n", buf); } i_close_fd(&fd); i_unlink(path); } static bool pid_file_read(const char *path, pid_t *pid_r) { char buf[32]; int fd; ssize_t ret; bool found; *pid_r = (pid_t)-1; fd = open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return FALSE; i_fatal("open(%s) failed: %m", path); } ret = read(fd, buf, sizeof(buf)-1); if (ret <= 0) { if (ret == 0) i_error("Empty PID file in %s, overriding", path); else i_fatal("read(%s) failed: %m", path); found = FALSE; } else { if (buf[ret-1] == '\n') ret--; buf[ret] = '\0'; if (str_to_pid(buf, pid_r) < 0) { i_error("PID file contains invalid PID value"); found = FALSE; } else { found = !(*pid_r == getpid() || (kill(*pid_r, 0) < 0 && errno == ESRCH)); } } i_close_fd(&fd); return found; } static void pid_file_check_running(const char *path) { pid_t pid; if (!pid_file_read(path, &pid)) return; i_fatal("Dovecot is already running with PID %s " "(read from %s)", dec2str(pid), path); } static void create_pid_file(const char *path) { const char *pid; int fd; pid = t_strconcat(dec2str(getpid()), "\n", NULL); fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd == -1) i_fatal("open(%s) failed: %m", path); if (write_full(fd, pid, strlen(pid)) < 0) i_fatal("write() failed in %s: %m", path); i_close_fd(&fd); } static void create_config_symlink(const struct master_settings *set) { const char *base_config_path; base_config_path = t_strconcat(set->base_dir, "/"PACKAGE".conf", NULL); i_unlink_if_exists(base_config_path); if (symlink(services->config->config_file_path, base_config_path) < 0) { i_error("symlink(%s, %s) failed: %m", services->config->config_file_path, base_config_path); } } static void instance_update_now(struct master_instance_list *list) { int ret; ret = master_instance_list_set_name(list, services->set->base_dir, services->set->instance_name); if (ret == 0) { /* duplicate instance names. allow without warning.. */ (void)master_instance_list_update(list, services->set->base_dir); } if (to_instance != NULL) timeout_remove(&to_instance); to_instance = timeout_add((3600*12 + rand()%(60*30)) * 1000, instance_update_now, list); } static void instance_update(const struct master_settings *set) { const char *path; path = t_strconcat(set->state_dir, "/"MASTER_INSTANCE_FNAME, NULL); instances = master_instance_list_init(path); instance_update_now(instances); } static void sig_settings_reload(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { struct master_service_settings_input input; struct master_service_settings_output output; const struct master_settings *set; void **sets; struct service_list *new_services; struct service *service; const char *error; i_warning("SIGHUP received - reloading configuration"); /* see if hostname changed */ hostpid_init(); if (services->config->process_avail == 0) { /* we can't reload config if there's no config process. */ if (service_process_create(services->config) == NULL) { i_error("Can't reload configuration because " "we couldn't create a config process"); return; } } i_zero(&input); input.roots = set_roots; input.module = MASTER_SERVICE_NAME; input.config_path = services_get_config_socket_path(services); if (master_service_settings_read(master_service, &input, &output, &error) < 0) { i_error("Error reading configuration: %s", error); return; } sets = master_service_settings_get_others(master_service); set = sets[0]; if (services_create(set, &new_services, &error) < 0) { /* new configuration is invalid, keep the old */ i_error("Config reload failed: %s", error); return; } new_services->config->config_file_path = p_strdup(new_services->pool, services->config->config_file_path); /* switch to new configuration. */ services_monitor_stop(services, FALSE); if (services_listen_using(new_services, services) < 0) { services_monitor_start(services); return; } /* anvil never dies. it just gets moved to the new services list */ service = service_lookup_type(services, SERVICE_TYPE_ANVIL); if (service != NULL) { while (service->processes != NULL) service_process_destroy(service->processes); } services_destroy(services, FALSE); services = new_services; services_monitor_start(services); } static void sig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { service_signal(services->log, SIGUSR1); master_service_init_log(master_service, "master: "); i_set_fatal_handler(master_fatal_callback); } static void sig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { services_monitor_reap_children(); } static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED) { i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)", si->si_signo, dec2str(si->si_pid), dec2str(si->si_uid), lib_signal_code_to_str(si->si_signo, si->si_code)); /* make sure new processes won't be created by the currently running ioloop. */ services->destroying = TRUE; master_service_stop(master_service); } static struct master_settings *master_settings_read(void) { struct master_service_settings_input input; struct master_service_settings_output output; const char *error; i_zero(&input); input.roots = set_roots; input.module = "master"; input.parse_full_config = TRUE; input.preserve_environment = TRUE; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); return master_service_settings_get_others(master_service)[0]; } static void main_log_startup(char **protocols) { #define STARTUP_STRING PACKAGE_NAME" v"DOVECOT_VERSION_FULL" starting up" string_t *str = t_str_new(128); rlim_t core_limit; str_append(str, STARTUP_STRING); if (protocols[0] == NULL) str_append(str, " without any protocols"); else { str_printfa(str, " for %s", t_strarray_join((const char **)protocols, ", ")); } core_dumps_disabled = restrict_get_core_limit(&core_limit) == 0 && core_limit == 0; if (core_dumps_disabled) str_append(str, " (core dumps disabled)"); i_info("%s", str_c(str)); } static void master_set_process_limit(void) { struct service *const *servicep; unsigned int process_limit = 0; rlim_t nproc; /* we'll just count all the processes that can exist and set the process limit so that we won't reach it. it's usually higher than needed, since we'd only need to set it high enough for each separate UID not to reach the limit, but this is difficult to guess: mail processes should probably be counted together for a common vmail user (unless system users are being used), but we can't really guess what the mail processes are. */ array_foreach(&services->services, servicep) process_limit += (*servicep)->process_limit; if (restrict_get_process_limit(&nproc) == 0 && process_limit > nproc) restrict_process_count(process_limit); } static void main_init(const struct master_settings *set) { master_set_process_limit(); drop_capabilities(); /* deny file access from everyone else except owner */ (void)umask(0077); main_log_startup(set->protocols_split); lib_signals_init(); lib_signals_ignore(SIGPIPE, TRUE); lib_signals_ignore(SIGALRM, FALSE); lib_signals_set_handler(SIGHUP, LIBSIG_FLAGS_SAFE, sig_settings_reload, NULL); lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE, sig_log_reopen, NULL); lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE, sig_reap_children, NULL); lib_signals_set_handler(SIGINT, LIBSIG_FLAGS_SAFE, sig_die, NULL); lib_signals_set_handler(SIGTERM, LIBSIG_FLAGS_SAFE, sig_die, NULL); create_pid_file(pidfile_path); create_config_symlink(set); instance_update(set); master_clients_init(); services_monitor_start(services); startup_finished = TRUE; } static void global_dead_pipe_close(void) { if (close(global_master_dead_pipe_fd[0]) < 0) i_error("close(global dead pipe) failed: %m"); if (close(global_master_dead_pipe_fd[1]) < 0) i_error("close(global dead pipe) failed: %m"); global_master_dead_pipe_fd[0] = -1; global_master_dead_pipe_fd[1] = -1; } static void main_deinit(void) { master_clients_deinit(); instance_update_now(instances); timeout_remove(&to_instance); master_instance_list_deinit(&instances); /* kill services and wait for them to die before unlinking pid file */ global_dead_pipe_close(); services_destroy(services, TRUE); i_unlink(pidfile_path); i_free(pidfile_path); service_anvil_global_deinit(); service_pids_deinit(); } static const char *get_full_config_path(struct service_list *list) { const char *path; path = master_service_get_config_path(master_service); if (*path == '/') return path; return p_strdup(list->pool, t_abspath(path)); } static void master_time_moved(time_t old_time, time_t new_time) { unsigned long secs; if (new_time >= old_time) return; /* time moved backwards. disable launching new service processes until */ secs = old_time - new_time + 1; if (secs > SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS) secs = SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS; services_throttle_time_sensitives(services, secs); i_warning("Time moved backwards by %lu seconds, " "waiting for %lu secs until new services are launched again.", (unsigned long)(old_time - new_time), secs); } static void daemonize(void) { pid_t pid; pid = fork(); if (pid < 0) i_fatal("fork() failed: %m"); if (pid != 0) _exit(0); if (setsid() < 0) i_fatal("setsid() failed: %m"); /* update my_pid */ hostpid_init(); } static void print_help(void) { fprintf(stderr, "Usage: dovecot [-F] [-c ] [-p] [-n] [-a] [--help] [--version]\n" " [--build-options] [--hostdomain] [reload] [stop]\n"); } static void print_build_options(void) { printf("Build options:" #ifdef IOLOOP_EPOLL " ioloop=epoll" #endif #ifdef IOLOOP_KQUEUE " ioloop=kqueue" #endif #ifdef IOLOOP_POLL " ioloop=poll" #endif #ifdef IOLOOP_SELECT " ioloop=select" #endif #ifdef IOLOOP_NOTIFY_INOTIFY " notify=inotify" #endif #ifdef IOLOOP_NOTIFY_KQUEUE " notify=kqueue" #endif #ifdef HAVE_IPV6 " ipv6" #endif #ifdef HAVE_GNUTLS " gnutls" #endif #ifdef HAVE_OPENSSL " openssl" #endif " io_block_size=%u" "\nMail storages: "MAIL_STORAGES"\n" #ifdef SQL_DRIVER_PLUGINS "SQL driver plugins:" #else "SQL drivers:" #endif #ifdef BUILD_CASSANDRA " cassandra" #endif #ifdef BUILD_MYSQL " mysql" #endif #ifdef BUILD_PGSQL " postgresql" #endif #ifdef BUILD_SQLITE " sqlite" #endif "\nPassdb:" #ifdef PASSDB_BSDAUTH " bsdauth" #endif #ifdef PASSDB_CHECKPASSWORD " checkpassword" #endif #ifdef PASSDB_LDAP " ldap" #endif #ifdef PASSDB_PAM " pam" #endif #ifdef PASSDB_PASSWD " passwd" #endif #ifdef PASSDB_PASSWD_FILE " passwd-file" #endif #ifdef PASSDB_SHADOW " shadow" #endif #ifdef PASSDB_SQL " sql" #endif #ifdef PASSDB_VPOPMAIL " vpopmail" #endif "\nUserdb:" #ifdef USERDB_CHECKPASSWORD " checkpassword" #endif #ifdef USERDB_LDAP " ldap" #ifndef BUILTIN_LDAP "(plugin)" #endif #endif #ifdef USERDB_NSS " nss" #endif #ifdef USERDB_PASSWD " passwd" #endif #ifdef USERDB_PREFETCH " prefetch" #endif #ifdef USERDB_PASSWD_FILE " passwd-file" #endif #ifdef USERDB_SQL " sql" #endif #ifdef USERDB_STATIC " static" #endif #ifdef USERDB_VPOPMAIL " vpopmail" #endif "\n", IO_BLOCK_SIZE); } int main(int argc, char *argv[]) { struct master_settings *set; const char *error, *doveconf_arg = NULL; failure_callback_t *orig_info_callback, *orig_debug_callback; bool foreground = FALSE, ask_key_pass = FALSE; bool doubleopts[argc]; int i, c; #ifdef DEBUG if (getenv("GDB") == NULL) fd_debug_verify_leaks(3, 1024); #endif /* drop -- prefix from all --args. ugly, but the only way that it works with standard getopt() in all OSes.. */ for (i = 1; i < argc; i++) { if (strncmp(argv[i], "--", 2) == 0) { if (argv[i][2] == '\0') break; argv[i] += 2; doubleopts[i] = TRUE; } else { doubleopts[i] = FALSE; } } master_service = master_service_init(MASTER_SERVICE_NAME, MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR, &argc, &argv, "+Fanp"); i_unset_failure_prefix(); io_loop_set_time_moved_callback(current_ioloop, master_time_moved); master_uid = geteuid(); master_gid = getegid(); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'F': foreground = TRUE; break; case 'a': doveconf_arg = "-a"; break; case 'n': doveconf_arg = "-n"; break; case 'p': /* Ask SSL private key password */ ask_key_pass = TRUE; break; default: if (!master_service_parse_option(master_service, c, optarg)) { print_help(); exit(FATAL_DEFAULT); } break; } } i_assert(optind > 0 && optind <= argc); if (doveconf_arg != NULL) { const char **args; args = t_new(const char *, 5); args[0] = DOVECOT_CONFIG_BIN_PATH; args[1] = doveconf_arg; args[2] = "-c"; args[3] = master_service_get_config_path(master_service); args[4] = NULL; execv_const(args[0], args); } if (optind == argc) { /* starting Dovecot */ } else if (!doubleopts[optind]) { /* dovecot xx -> doveadm xx */ (void)execv(BINDIR"/doveadm", argv); i_fatal("execv("BINDIR"/doveadm) failed: %m"); } else if (strcmp(argv[optind], "version") == 0) { printf("%s\n", DOVECOT_VERSION_FULL); return 0; } else if (strcmp(argv[optind], "hostdomain") == 0) { printf("%s\n", my_hostdomain()); return 0; } else if (strcmp(argv[optind], "build-options") == 0) { print_build_options(); return 0; } else if (strcmp(argv[optind], "log-error") == 0) { fprintf(stderr, "Writing to error logs and killing myself..\n"); argv[optind] = "log test"; (void)execv(BINDIR"/doveadm", argv); i_fatal("execv("BINDIR"/doveadm) failed: %m"); } else if (strcmp(argv[optind], "help") == 0) { print_help(); return 0; } else { print_help(); i_fatal("Unknown argument: --%s", argv[optind]); } if (pipe(global_master_dead_pipe_fd) < 0) i_fatal("pipe() failed: %m"); fd_close_on_exec(global_master_dead_pipe_fd[0], TRUE); fd_close_on_exec(global_master_dead_pipe_fd[1], TRUE); set = master_settings_read(); if (ask_key_pass) { ssl_manual_key_password = t_askpass("Give the password for SSL keys: "); } if (dup2(dev_null_fd, STDIN_FILENO) < 0) i_fatal("dup2(dev_null_fd) failed: %m"); if (!foreground && dup2(dev_null_fd, STDOUT_FILENO) < 0) i_fatal("dup2(dev_null_fd) failed: %m"); pidfile_path = i_strconcat(set->base_dir, "/"MASTER_PID_FILE_NAME, NULL); master_service_init_log(master_service, "master: "); i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback, &orig_info_callback, &orig_debug_callback); i_set_fatal_handler(startup_fatal_handler); i_set_error_handler(startup_error_handler); pid_file_check_running(pidfile_path); master_settings_do_fixes(set); fatal_log_check(set); const struct master_service_settings *service_set = master_service_settings_get(master_service); master_service_import_environment(service_set->import_environment); master_service_env_clean(); /* create service structures from settings. if there are any errors in service configuration we'll catch it here. */ service_pids_init(); service_anvil_global_init(); if (services_create(set, &services, &error) < 0) i_fatal("%s", error); services->config->config_file_path = get_full_config_path(services); /* if any listening fails, fail completely */ if (services_listen(services) <= 0) i_fatal("Failed to start listeners"); if (chdir(set->base_dir) < 0) i_fatal("chdir(%s) failed: %m", set->base_dir); i_set_fatal_handler(master_fatal_callback); i_set_error_handler(orig_error_callback); if (!foreground) daemonize(); T_BEGIN { main_init(set); } T_END; master_service_run(master_service, NULL); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/master/service.h0000644000175000017500000001322013165463624014230 00000000000000#ifndef SERVICE_H #define SERVICE_H #include "net.h" #include "master-settings.h" /* If a service process doesn't send its first status notification in this many seconds, kill the process */ #define SERVICE_FIRST_STATUS_TIMEOUT_SECS 30 #define SERVICE_STARTUP_FAILURE_THROTTLE_MIN_SECS 2 #define SERVICE_STARTUP_FAILURE_THROTTLE_MAX_SECS 60 enum service_listener_type { SERVICE_LISTENER_UNIX, SERVICE_LISTENER_FIFO, SERVICE_LISTENER_INET }; struct service_listener { struct service *service; enum service_listener_type type; int fd; /* may be -1 */ struct io *io; const char *name; const char *inet_address; union { struct { const struct file_listener_settings *set; uid_t uid; gid_t gid; } fileset; struct { const struct inet_listener_settings *set; struct ip_addr ip; } inetset; } set; bool reuse_port; }; struct service { struct service_list *list; enum service_type type; const struct service_settings *set; const char *config_file_path; const char *executable; uid_t uid; gid_t gid; gid_t privileged_gid; const char *extra_gids; /* comma-separated list */ /* all listeners, even those that aren't currently listening */ ARRAY(struct service_listener *) listeners; /* linked list of all processes belonging to this service */ struct service_process *processes; /* number of processes currently created for this service */ unsigned int process_count; /* number of processes currently accepting new clients */ unsigned int process_avail; /* max number of processes allowed */ unsigned int process_limit; /* Maximum number of client connections a process can handle. */ unsigned int client_limit; /* Kill idling processes after this many seconds. */ unsigned int idle_kill; /* set->vsz_limit or set->master_set->default_client_limit */ uoff_t vsz_limit; /* log process pipe file descriptors. */ int log_fd[2]; /* fd that log process sees log_fd[0] as. can be used to identify service name when sending commands via master_log_fd. */ int log_process_internal_fd; /* status report pipe file descriptors */ int status_fd[2]; struct io *io_status; int master_dead_pipe_fd[2]; unsigned int throttle_secs; time_t exit_failure_last; unsigned int exit_failures_in_sec; /* Login process's notify fd. We change its seek position to communicate state to login processes. */ int login_notify_fd; time_t last_login_notify_time; struct timeout *to_login_notify; /* if a process fails before servicing its first request, assume it's broken and start throtting new process creations */ struct timeout *to_throttle; /* when process_limit is reached, wait for a while until we actually start dropping pending connections */ struct timeout *to_drop; /* prefork processes up to process_min_avail if there's time */ struct timeout *to_prefork; unsigned int prefork_counter; /* Last time a "dropping client connections" warning was logged */ time_t last_drop_warning; /* all processes are in use and new connections are coming */ unsigned int listen_pending:1; /* service is currently listening for new connections */ unsigned int listening:1; /* TRUE if service has at least one inet_listener */ unsigned int have_inet_listeners:1; /* service_login_notify()'s last notification state */ unsigned int last_login_full_notify:1; /* service has exited at least once with exit code 0 */ unsigned int have_successful_exits:1; }; struct service_list { pool_t pool; pool_t set_pool; int refcount; struct timeout *to_kill; unsigned int fork_counter; const struct master_settings *set; const struct master_service_settings *service_set; struct service *config; struct service *log; struct service *anvil; struct file_listener_settings master_listener_set; struct io *io_master; int master_fd; /* nonblocking log fds usd by master */ int master_log_fd[2]; struct service_process_notify *log_byes; ARRAY(struct service *) services; unsigned int destroying:1; unsigned int destroyed:1; unsigned int sigterm_sent:1; unsigned int sigterm_sent_to_log:1; }; HASH_TABLE_DEFINE_TYPE(pid_process, void *, struct service_process *); extern HASH_TABLE_TYPE(pid_process) service_pids; /* Create all services from settings */ int services_create(const struct master_settings *set, struct service_list **services_r, const char **error_r); /* Destroy services */ void services_destroy(struct service_list *service_list, bool wait); void service_list_ref(struct service_list *service_list); void service_list_unref(struct service_list *service_list); /* Return path to configuration process socket. */ const char *services_get_config_socket_path(struct service_list *service_list); /* Send a signal to all processes in a given service */ void service_signal(struct service *service, int signo); /* Notify all processes (if necessary) that no more connections can be handled by the service without killing existing connections (TRUE) or that they can be (FALSE). */ void service_login_notify(struct service *service, bool all_processes_full); /* Prevent service from launching new processes for a while. */ void service_throttle(struct service *service, unsigned int secs); /* Time moved backwards. Throttle services that care about time. */ void services_throttle_time_sensitives(struct service_list *list, unsigned int secs); /* Find service by name. */ struct service * service_lookup(struct service_list *service_list, const char *name); /* Find service by type */ struct service * service_lookup_type(struct service_list *service_list, enum service_type type); void service_error(struct service *service, const char *format, ...) ATTR_FORMAT(2, 3); void service_pids_init(void); void service_pids_deinit(void); #endif dovecot-2.2.33.2/src/master/service-anvil.h0000644000175000017500000000165413123174404015334 00000000000000#ifndef SERVICE_ANVIL_H #define SERVICE_ANVIL_H struct service_anvil_global { pid_t pid; unsigned int uid; int status_fd[2]; /* passed to child processes */ int blocking_fd[2]; /* used by master process to notify about dying processes */ int nonblocking_fd[2]; /* master process sends new log fds to anvil via this unix socket */ int log_fdpass_fd[2]; struct service_process_notify *kills; struct io *io_blocking, *io_nonblocking; unsigned int process_count; /* anvil crashed and we're now restarting it */ bool restarted; }; extern struct service_anvil_global *service_anvil_global; void service_anvil_monitor_start(struct service_list *service_list); void service_anvil_process_created(struct service_process *process); void service_anvil_process_destroyed(struct service_process *process); void service_anvil_send_log_fd(void); void service_anvil_global_init(void); void service_anvil_global_deinit(void); #endif dovecot-2.2.33.2/src/master/master-client.c0000644000175000017500000001077513165463624015346 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "connection.h" #include "service.h" #include "service-process.h" #include "service-monitor.h" #include "master-client.h" struct master_client { struct connection conn; }; static void master_client_service_status_output(string_t *str, const struct service *service) { str_append_tabescaped(str, service->set->name); str_printfa(str, "\t%u\t%u\t%u\t%u\t%u\t%ld\t%u\t%ld\t%c\t%c\n", service->process_count, service->process_avail, service->process_limit, service->client_limit, service->to_throttle == NULL ? 0 : service->throttle_secs, (long)service->exit_failure_last, service->exit_failures_in_sec, (long)service->last_drop_warning, service->listen_pending ? 'y' : 'n', service->listening ? 'y' : 'n'); } static int master_client_service_status(struct master_client *client) { struct service *const *servicep; string_t *str = t_str_new(128); array_foreach(&services->services, servicep) { str_truncate(str, 0); master_client_service_status_output(str, *servicep); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); } o_stream_nsend_str(client->conn.output, "\n"); return 1; } static void master_client_process_output(string_t *str, const struct service_process *process) { str_append_tabescaped(str, process->service->set->name); str_printfa(str, "\t%ld\t%u\t%u\t%ld\t%ld\t%ld\n", (long)process->pid, process->available_count, process->total_count, (long)process->idle_start, (long)process->last_status_update, (long)process->last_kill_sent); } static int master_client_process_status(struct master_client *client, const char *const *args) { struct service *const *servicep; struct service_process *p; string_t *str = t_str_new(128); array_foreach(&services->services, servicep) { if (args[0] != NULL && !str_array_find(args, (*servicep)->set->name)) continue; for (p = (*servicep)->processes; p != NULL; p = p->next) { str_truncate(str, 0); master_client_process_output(str, p); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); } } o_stream_nsend_str(client->conn.output, "\n"); return 1; } static int master_client_stop(struct master_client *client, const char *const *args) { struct service *service; const char *reply = "+\n"; for (unsigned int i = 0; args[i] != NULL; i++) { service = service_lookup(services, args[i]); if (service == NULL) reply = t_strdup_printf("-Unknown service: %s\n", args[i]); else service_monitor_stop_close(service); } o_stream_send_str(client->conn.output, reply); return 1; } static int master_client_input_args(struct connection *conn, const char *const *args) { struct master_client *client = (struct master_client *)conn; const char *cmd = args[0]; if (cmd == NULL) { i_error("%s: Empty command", conn->name); return 0; } args++; if (strcmp(cmd, "SERVICE-STATUS") == 0) return master_client_service_status(client); if (strcmp(cmd, "PROCESS-STATUS") == 0) return master_client_process_status(client, args); if (strcmp(cmd, "STOP") == 0) return master_client_stop(client, args); i_error("%s: Unknown command: %s", conn->name, cmd); return -1; } static void master_client_destroy(struct connection *conn) { struct master_client *client = (struct master_client *)conn; connection_deinit(conn); i_free(client); } static const struct connection_settings master_conn_set = { .service_name_in = "master-client", .service_name_out = "master-server", .major_version = 1, .minor_version = 0, .input_max_size = 1024, .output_max_size = 1024, .client = FALSE }; static const struct connection_vfuncs master_conn_vfuncs = { .destroy = master_client_destroy, .input_args = master_client_input_args }; static struct connection_list *master_connections; void master_client_connected(struct service_list *service_list) { struct master_client *client; int fd; fd = net_accept(service_list->master_fd, NULL, NULL); if (fd < 0) { if (fd == -2) i_error("net_accept() failed: %m"); return; } client = i_new(struct master_client, 1); connection_init_server(master_connections, &client->conn, "master-client", fd, fd); } void master_clients_init(void) { master_connections = connection_list_init(&master_conn_set, &master_conn_vfuncs); } void master_clients_deinit(void) { connection_list_deinit(&master_connections); } dovecot-2.2.33.2/src/master/service-monitor.h0000644000175000017500000000111113123174404015676 00000000000000#ifndef SERVICE_MONITOR_H #define SERVICE_MONITOR_H /* Start listening and monitoring services. */ void services_monitor_start(struct service_list *service_list); /* Stop services. */ void services_monitor_stop(struct service_list *service_list, bool wait); /* Call after SIGCHLD has been detected */ void services_monitor_reap_children(void); void service_monitor_stop(struct service *service); void service_monitor_stop_close(struct service *service); void service_monitor_listen_start(struct service *service); void service_monitor_listen_stop(struct service *service); #endif dovecot-2.2.33.2/src/master/capabilities-posix.c0000644000175000017500000000133613123174404016346 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "capabilities.h" #ifdef HAVE_LIBCAP #include void drop_capabilities(void) { /* the capabilities that we *need* in order to operate */ static cap_value_t suidcaps[] = { CAP_CHOWN, CAP_KILL, CAP_SYS_CHROOT, CAP_SETUID, CAP_SETGID, CAP_NET_BIND_SERVICE, /* we may want to open any config/log files */ CAP_DAC_OVERRIDE }; cap_t caps; caps = cap_init(); cap_clear(caps); cap_set_flag(caps, CAP_PERMITTED, N_ELEMENTS(suidcaps), suidcaps, CAP_SET); cap_set_flag(caps, CAP_EFFECTIVE, N_ELEMENTS(suidcaps), suidcaps, CAP_SET); cap_set_proc(caps); cap_free(caps); } #endif dovecot-2.2.33.2/src/master/service-log.h0000644000175000017500000000053213123174404014776 00000000000000#ifndef SERVICE_LOG_H #define SERVICE_LOG_H #include "dup2-array.h" int services_log_init(struct service_list *service_list); void services_log_deinit(struct service_list *service_list); void services_log_dup2(ARRAY_TYPE(dup2) *dups, struct service_list *service_list, unsigned int first_fd, unsigned int *fd_count); #endif dovecot-2.2.33.2/src/master/service-process-notify.c0000644000175000017500000000516513165463624017216 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "aqueue.h" #include "ioloop.h" #include "service.h" #include "service-process.h" #include "service-process-notify.h" struct service_process_notify { service_process_notify_callback_t *write_callback; int fd; struct io *io_write; struct aqueue *process_queue; ARRAY(struct service_process *) processes; }; struct service_process_notify * service_process_notify_init(int fd, service_process_notify_callback_t *write_callback) { struct service_process_notify *notify; notify = i_new(struct service_process_notify, 1); notify->fd = fd; notify->write_callback = write_callback; i_array_init(¬ify->processes, 64); notify->process_queue = aqueue_init(¬ify->processes.arr); return notify; } static void service_process_notify_reset(struct service_process_notify *notify) { struct service_process *const *processes, *process; unsigned int i, count; if (notify->io_write == NULL) return; processes = array_idx_modifiable(¬ify->processes, 0); count = aqueue_count(notify->process_queue); for (i = 0; i < count; i++) { process = processes[aqueue_idx(notify->process_queue, i)]; service_process_unref(process); } aqueue_clear(notify->process_queue); array_clear(¬ify->processes); io_remove(¬ify->io_write); } static void notify_flush(struct service_process_notify *notify) { struct service_process *const *processes, *process; while (aqueue_count(notify->process_queue) > 0) { processes = array_idx_modifiable(¬ify->processes, 0); process = processes[aqueue_idx(notify->process_queue, 0)]; if (notify->write_callback(notify->fd, process) < 0) { if (errno != EAGAIN) service_process_notify_reset(notify); return; } service_process_unref(process); aqueue_delete_tail(notify->process_queue); } io_remove(¬ify->io_write); } void service_process_notify_deinit(struct service_process_notify **_notify) { struct service_process_notify *notify = *_notify; *_notify = NULL; service_process_notify_reset(notify); if (notify->io_write != NULL) io_remove(¬ify->io_write); aqueue_deinit(¬ify->process_queue); array_free(¬ify->processes); i_free(notify); } void service_process_notify_add(struct service_process_notify *notify, struct service_process *process) { if (notify->write_callback(notify->fd, process) < 0) { if (errno != EAGAIN) return; if (notify->io_write == NULL) { notify->io_write = io_add(notify->fd, IO_WRITE, notify_flush, notify); } aqueue_append(notify->process_queue, &process); service_process_ref(process); } } dovecot-2.2.33.2/src/master/service-process.h0000644000175000017500000000244513165463624015713 00000000000000#ifndef SERVICE_PROCESS_H #define SERVICE_PROCESS_H struct service_process { struct service_process *prev, *next; struct service *service; int refcount; pid_t pid; /* uid is used to check for old/invalid status messages */ unsigned int uid; /* number of new connections process is currently accepting */ unsigned int available_count; /* number of connections process has ever accepted */ unsigned int total_count; /* time when process started idling, or 0 if we're not idling */ time_t idle_start; /* kill process if it hits idle timeout */ struct timeout *to_idle; /* time when we last received a status update */ time_t last_status_update; /* time when we last sent SIGINT to process */ time_t last_kill_sent; /* kill the process if it doesn't send initial status notification */ struct timeout *to_status; unsigned int destroyed:1; }; #define SERVICE_PROCESS_IS_INITIALIZED(process) \ ((process)->to_status == NULL) struct service_process *service_process_create(struct service *service); void service_process_destroy(struct service_process *process); void service_process_ref(struct service_process *process); void service_process_unref(struct service_process *process); void service_process_log_status_error(struct service_process *process, int status); #endif dovecot-2.2.33.2/src/master/master-settings.c0000644000175000017500000005321313165463624015722 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "env-util.h" #include "istream.h" #include "net.h" #include "str.h" #include "ipwd.h" #include "mkdir-parents.h" #include "safe-mkdir.h" #include "restrict-process-size.h" #include "settings-parser.h" #include "master-settings.h" #include #include #include #include #include static bool master_settings_verify(void *_set, pool_t pool, const char **error_r); extern const struct setting_parser_info service_setting_parser_info; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct file_listener_settings, name), NULL } static const struct setting_define file_listener_setting_defines[] = { DEF(SET_STR, path), DEF(SET_UINT_OCT, mode), DEF(SET_STR, user), DEF(SET_STR, group), SETTING_DEFINE_LIST_END }; static const struct file_listener_settings file_listener_default_settings = { .path = "", .mode = 0600, .user = "", .group = "", }; static const struct setting_parser_info file_listener_setting_parser_info = { .defines = file_listener_setting_defines, .defaults = &file_listener_default_settings, .type_offset = offsetof(struct file_listener_settings, path), .struct_size = sizeof(struct file_listener_settings), .parent_offset = (size_t)-1, .parent = &service_setting_parser_info }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct inet_listener_settings, name), NULL } static const struct setting_define inet_listener_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, address), DEF(SET_IN_PORT, port), DEF(SET_BOOL, ssl), DEF(SET_BOOL, reuse_port), DEF(SET_BOOL, haproxy), SETTING_DEFINE_LIST_END }; static const struct inet_listener_settings inet_listener_default_settings = { .name = "", .address = "", .port = 0, .ssl = FALSE, .reuse_port = FALSE, .haproxy = FALSE }; static const struct setting_parser_info inet_listener_setting_parser_info = { .defines = inet_listener_setting_defines, .defaults = &inet_listener_default_settings, .type_offset = offsetof(struct inet_listener_settings, name), .struct_size = sizeof(struct inet_listener_settings), .parent_offset = (size_t)-1, .parent = &service_setting_parser_info }; #undef DEF #undef DEFLIST #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct service_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct service_settings, field), defines } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, offsetof(struct service_settings, field), defines } static const struct setting_define service_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, protocol), DEF(SET_STR, type), DEF(SET_STR, executable), DEF(SET_STR, user), DEF(SET_STR, group), DEF(SET_STR, privileged_group), DEF(SET_STR, extra_groups), DEF(SET_STR, chroot), DEF(SET_BOOL, drop_priv_before_exec), DEF(SET_UINT, process_min_avail), DEF(SET_UINT, process_limit), DEF(SET_UINT, client_limit), DEF(SET_UINT, service_count), DEF(SET_TIME, idle_kill), DEF(SET_SIZE, vsz_limit), DEFLIST_UNIQUE(unix_listeners, "unix_listener", &file_listener_setting_parser_info), DEFLIST_UNIQUE(fifo_listeners, "fifo_listener", &file_listener_setting_parser_info), DEFLIST_UNIQUE(inet_listeners, "inet_listener", &inet_listener_setting_parser_info), SETTING_DEFINE_LIST_END }; static const struct service_settings service_default_settings = { .name = "", .protocol = "", .type = "", .executable = "", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; const struct setting_parser_info service_setting_parser_info = { .defines = service_setting_defines, .defaults = &service_default_settings, .type_offset = offsetof(struct service_settings, name), .struct_size = sizeof(struct service_settings), .parent_offset = offsetof(struct service_settings, master_set), .parent = &master_setting_parser_info }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct master_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, offsetof(struct master_settings, field), defines } static const struct setting_define master_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, state_dir), DEF(SET_STR, libexec_dir), DEF(SET_STR, instance_name), DEF(SET_STR, protocols), DEF(SET_STR, listen), DEF(SET_ENUM, ssl), DEF(SET_STR, default_internal_user), DEF(SET_STR, default_login_user), DEF(SET_UINT, default_process_limit), DEF(SET_UINT, default_client_limit), DEF(SET_TIME, default_idle_kill), DEF(SET_SIZE, default_vsz_limit), DEF(SET_BOOL, version_ignore), DEF(SET_UINT, first_valid_uid), DEF(SET_UINT, last_valid_uid), DEF(SET_UINT, first_valid_gid), DEF(SET_UINT, last_valid_gid), DEFLIST_UNIQUE(services, "service", &service_setting_parser_info), SETTING_DEFINE_LIST_END }; static const struct master_settings master_default_settings = { .base_dir = PKG_RUNDIR, .state_dir = PKG_STATEDIR, .libexec_dir = PKG_LIBEXECDIR, .instance_name = PACKAGE, .protocols = "imap pop3 lmtp", .listen = "*, ::", .ssl = "yes:no:required", .default_internal_user = "dovecot", .default_login_user = "dovenull", .default_process_limit = 100, .default_client_limit = 1000, .default_idle_kill = 60, .default_vsz_limit = 256*1024*1024, .version_ignore = FALSE, .first_valid_uid = 500, .last_valid_uid = 0, .first_valid_gid = 1, .last_valid_gid = 0, #ifndef CONFIG_BINARY .services = ARRAY_INIT #else .services = { { &config_all_services_buf, sizeof(struct service_settings *) } }, #endif }; const struct setting_parser_info master_setting_parser_info = { .module_name = "master", .defines = master_setting_defines, .defaults = &master_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct master_settings), .parent_offset = (size_t)-1, .check_func = master_settings_verify }; /* */ static void expand_user(const char **user, enum service_user_default *default_r, const struct master_settings *set) { /* $variable expansion is typically done by doveconf, but these variables can come from built-in settings, so we need to expand them here */ if (strcmp(*user, "$default_internal_user") == 0) { *user = set->default_internal_user; *default_r = SERVICE_USER_DEFAULT_INTERNAL; } else if (strcmp(*user, "$default_login_user") == 0) { *user = set->default_login_user; *default_r = SERVICE_USER_DEFAULT_LOGIN; } else { *default_r = SERVICE_USER_DEFAULT_NONE; } } static bool fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l, pool_t pool, const struct master_settings *master_set, ARRAY_TYPE(const_string) *all_listeners, const char **error_r) { struct file_listener_settings *const *sets; size_t base_dir_len = strlen(master_set->base_dir); enum service_user_default user_default; if (!array_is_created(l)) return TRUE; array_foreach(l, sets) { struct file_listener_settings *set = *sets; if (set->path[0] == '\0') { *error_r = "path must not be empty"; return FALSE; } expand_user(&set->user, &user_default, master_set); if (*set->path != '/') { set->path = p_strconcat(pool, master_set->base_dir, "/", set->path, NULL); } else if (strncmp(set->path, master_set->base_dir, base_dir_len) == 0 && set->path[base_dir_len] == '/') { i_warning("You should remove base_dir prefix from " "unix_listener: %s", set->path); } if (set->mode != 0) array_append(all_listeners, &set->path, 1); } return TRUE; } static void add_inet_listeners(ARRAY_TYPE(inet_listener_settings) *l, ARRAY_TYPE(const_string) *all_listeners) { struct inet_listener_settings *const *sets; const char *str; if (!array_is_created(l)) return; array_foreach(l, sets) { struct inet_listener_settings *set = *sets; if (set->port != 0) { str = t_strdup_printf("%u:%s", set->port, set->address); array_append(all_listeners, &str, 1); } } } static bool master_settings_parse_type(struct service_settings *set, const char **error_r) { if (*set->type == '\0') set->parsed_type = SERVICE_TYPE_UNKNOWN; else if (strcmp(set->type, "log") == 0) set->parsed_type = SERVICE_TYPE_LOG; else if (strcmp(set->type, "config") == 0) set->parsed_type = SERVICE_TYPE_CONFIG; else if (strcmp(set->type, "anvil") == 0) set->parsed_type = SERVICE_TYPE_ANVIL; else if (strcmp(set->type, "login") == 0) set->parsed_type = SERVICE_TYPE_LOGIN; else if (strcmp(set->type, "startup") == 0) set->parsed_type = SERVICE_TYPE_STARTUP; else { *error_r = t_strconcat("Unknown service type: ", set->type, NULL); return FALSE; } return TRUE; } static void service_set_login_dump_core(struct service_settings *set) { const char *p; if (set->parsed_type != SERVICE_TYPE_LOGIN) return; p = strstr(set->executable, " -D"); if (p != NULL && (p[3] == '\0' || p[3] == ' ')) set->login_dump_core = TRUE; } static bool services_have_protocol(struct master_settings *set, const char *name) { struct service_settings *const *services; array_foreach(&set->services, services) { struct service_settings *service = *services; if (strcmp(service->protocol, name) == 0) return TRUE; } return FALSE; } #ifdef CONFIG_BINARY static const struct service_settings * master_default_settings_get_service(const char *name) { extern struct master_settings master_default_settings; struct service_settings *const *setp; array_foreach(&master_default_settings.services, setp) { if (strcmp((*setp)->name, name) == 0) return *setp; } return NULL; } #endif static unsigned int service_get_client_limit(struct master_settings *set, const char *name) { struct service_settings *const *servicep; array_foreach(&set->services, servicep) { if (strcmp((*servicep)->name, name) == 0) { if ((*servicep)->client_limit != 0) return (*servicep)->client_limit; else return set->default_client_limit; } } return set->default_client_limit; } static bool master_settings_verify(void *_set, pool_t pool, const char **error_r) { static int warned_auth = FALSE, warned_anvil = FALSE; struct master_settings *set = _set; struct service_settings *const *services; const char *const *strings; ARRAY_TYPE(const_string) all_listeners; struct passwd pw; unsigned int i, j, count, client_limit, process_limit; unsigned int max_auth_client_processes, max_anvil_client_processes; size_t len; #ifdef CONFIG_BINARY const struct service_settings *default_service; #else rlim_t fd_limit; const char *max_client_limit_source = "default_client_limit"; unsigned int max_client_limit = set->default_client_limit; #endif if (*set->listen == '\0') { *error_r = "listen can't be set empty"; return FALSE; } len = strlen(set->base_dir); if (len > 0 && set->base_dir[len-1] == '/') { /* drop trailing '/' */ set->base_dir = p_strndup(pool, set->base_dir, len - 1); } if (set->last_valid_uid != 0 && set->first_valid_uid > set->last_valid_uid) { *error_r = "first_valid_uid can't be larger than last_valid_uid"; return FALSE; } if (set->last_valid_gid != 0 && set->first_valid_gid > set->last_valid_gid) { *error_r = "first_valid_gid can't be larger than last_valid_gid"; return FALSE; } if (i_getpwnam(set->default_login_user, &pw) == 0) { *error_r = t_strdup_printf("default_login_user doesn't exist: %s", set->default_login_user); return FALSE; } if (i_getpwnam(set->default_internal_user, &pw) == 0) { *error_r = t_strdup_printf("default_internal_user doesn't exist: %s", set->default_internal_user); return FALSE; } /* check that we have at least one service. the actual service structure validity is checked later while creating them. */ if (!array_is_created(&set->services) || array_count(&set->services) == 0) { *error_r = "No services defined"; return FALSE; } services = array_get(&set->services, &count); for (i = 0; i < count; i++) { struct service_settings *service = services[i]; if (*service->name == '\0') { *error_r = t_strdup_printf( "Service #%d is missing name", i); return FALSE; } if (!master_settings_parse_type(service, error_r)) return FALSE; for (j = 0; j < i; j++) { if (strcmp(service->name, services[j]->name) == 0) { *error_r = t_strdup_printf( "Duplicate service name: %s", service->name); return FALSE; } } expand_user(&service->user, &service->user_default, set); service_set_login_dump_core(service); } set->protocols_split = p_strsplit_spaces(pool, set->protocols, " "); if (set->protocols_split[0] != NULL && strcmp(set->protocols_split[0], "none") == 0 && set->protocols_split[1] == NULL) set->protocols_split[0] = NULL; for (i = 0; set->protocols_split[i] != NULL; i++) { if (!services_have_protocol(set, set->protocols_split[i])) { *error_r = t_strdup_printf("protocols: " "Unknown protocol: %s", set->protocols_split[i]); return FALSE; } } t_array_init(&all_listeners, 64); max_auth_client_processes = 0; max_anvil_client_processes = 2; /* blocking, nonblocking pipes */ for (i = 0; i < count; i++) { struct service_settings *service = services[i]; if (*service->protocol != '\0' && !str_array_find((const char **)set->protocols_split, service->protocol)) { /* protocol not enabled, ignore its settings */ continue; } if (*service->executable != '/' && *service->executable != '\0') { service->executable = p_strconcat(pool, set->libexec_dir, "/", service->executable, NULL); } if (*service->chroot != '/' && *service->chroot != '\0') { service->chroot = p_strconcat(pool, set->base_dir, "/", service->chroot, NULL); } if (service->drop_priv_before_exec && *service->chroot != '\0') { *error_r = t_strdup_printf("service(%s): " "drop_priv_before_exec=yes can't be " "used with chroot", service->name); return FALSE; } process_limit = service->process_limit; if (process_limit == 0) process_limit = set->default_process_limit; if (service->process_min_avail > process_limit) { *error_r = t_strdup_printf("service(%s): " "process_min_avail is higher than process_limit", service->name); return FALSE; } if (service->vsz_limit < 1024*1024 && service->vsz_limit != 0) { *error_r = t_strdup_printf("service(%s): " "vsz_limit is too low", service->name); return FALSE; } #ifdef CONFIG_BINARY default_service = master_default_settings_get_service(service->name); if (default_service != NULL && default_service->process_limit_1 && process_limit > 1) { *error_r = t_strdup_printf("service(%s): " "process_limit must be 1", service->name); return FALSE; } #else if (max_client_limit < service->client_limit) { max_client_limit = service->client_limit; max_client_limit_source = t_strdup_printf( "service %s { client_limit }", service->name); } #endif if (*service->protocol != '\0') { /* each imap/pop3/lmtp process can use up a connection, although if service_count=1 it's only temporary */ if (service->service_count != 1 || strcmp(service->type, "login") == 0) max_auth_client_processes += process_limit; } if (strcmp(service->type, "login") == 0 || strcmp(service->name, "auth") == 0) max_anvil_client_processes += process_limit; if (!fix_file_listener_paths(&service->unix_listeners, pool, set, &all_listeners, error_r)) { *error_r = t_strdup_printf("service(%s): unix_listener: %s", service->name, *error_r); return FALSE; } if (!fix_file_listener_paths(&service->fifo_listeners, pool, set, &all_listeners, error_r)) { *error_r = t_strdup_printf("service(%s): fifo_listener: %s", service->name, *error_r); return FALSE; } add_inet_listeners(&service->inet_listeners, &all_listeners); } client_limit = service_get_client_limit(set, "auth"); if (client_limit < max_auth_client_processes && !warned_auth) { warned_auth = TRUE; i_warning("service auth { client_limit=%u } is lower than " "required under max. load (%u)", client_limit, max_auth_client_processes); } client_limit = service_get_client_limit(set, "anvil"); if (client_limit < max_anvil_client_processes && !warned_anvil) { warned_anvil = TRUE; i_warning("service anvil { client_limit=%u } is lower than " "required under max. load (%u)", client_limit, max_anvil_client_processes); } #ifndef CONFIG_BINARY if (restrict_get_fd_limit(&fd_limit) == 0 && fd_limit < (rlim_t)max_client_limit) { i_warning("fd limit (ulimit -n) is lower than required " "under max. load (%u < %u), because of %s", (unsigned int)fd_limit, max_client_limit, max_client_limit_source); } #endif /* check for duplicate listeners */ array_sort(&all_listeners, i_strcmp_p); strings = array_get(&all_listeners, &count); for (i = 1; i < count; i++) { if (strcmp(strings[i-1], strings[i]) == 0) { *error_r = t_strdup_printf("duplicate listener: %s", strings[i]); return FALSE; } } return TRUE; } /* */ static bool login_want_core_dumps(const struct master_settings *set, gid_t *gid_r) { struct service_settings *const *services; const char *error; bool cores = FALSE; uid_t uid; *gid_r = (gid_t)-1; array_foreach(&set->services, services) { struct service_settings *service = *services; if (service->parsed_type == SERVICE_TYPE_LOGIN) { if (service->login_dump_core) cores = TRUE; (void)get_uidgid(service->user, &uid, gid_r, &error); if (*service->group != '\0') (void)get_gid(service->group, gid_r, &error); } } return cores; } static bool settings_have_auth_unix_listeners_in(const struct master_settings *set, const char *dir) { struct service_settings *const *services; struct file_listener_settings *const *uls; size_t dir_len = strlen(dir); array_foreach(&set->services, services) { struct service_settings *service = *services; if (array_is_created(&service->unix_listeners)) { array_foreach(&service->unix_listeners, uls) { struct file_listener_settings *u = *uls; if (strncmp(u->path, dir, dir_len) == 0 && u->path[dir_len] == '/') return TRUE; } } } return FALSE; } static void unlink_sockets(const char *path, const char *prefix) { DIR *dirp; struct dirent *dp; struct stat st; string_t *str; size_t prefix_len; dirp = opendir(path); if (dirp == NULL) { i_error("opendir(%s) failed: %m", path); return; } prefix_len = strlen(prefix); str = t_str_new(256); while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.') continue; if (strncmp(dp->d_name, prefix, prefix_len) != 0) continue; str_truncate(str, 0); str_printfa(str, "%s/%s", path, dp->d_name); if (lstat(str_c(str), &st) < 0) { if (errno != ENOENT) i_error("lstat(%s) failed: %m", str_c(str)); continue; } if (!S_ISSOCK(st.st_mode)) continue; /* try to avoid unlinking sockets if someone's already listening in them. do this only at startup, because when SIGHUPing a child process might catch the new connection before it notices that it's supposed to die. */ if (!startup_finished) { int fd = net_connect_unix(str_c(str)); if (fd != -1 || errno != ECONNREFUSED) { i_fatal("Dovecot is already running? " "Socket already exists: %s", str_c(str)); } } i_unlink_if_exists(str_c(str)); } (void)closedir(dirp); } static void mkdir_login_dir(const struct master_settings *set, const char *login_dir) { mode_t mode; gid_t gid; if (settings_have_auth_unix_listeners_in(set, login_dir)) { /* we are not using external authentication, so make sure the login directory exists with correct permissions and it's empty. with external auth we wouldn't want to delete existing sockets or break the permissions required by the auth server. */ mode = login_want_core_dumps(set, &gid) ? 0770 : 0750; if (safe_mkdir(login_dir, mode, master_uid, gid) == 0) { i_warning("Corrected permissions for login directory " "%s", login_dir); } unlink_sockets(login_dir, ""); } else { /* still make sure that login directory exists */ if (mkdir(login_dir, 0755) < 0 && errno != EEXIST) i_fatal("mkdir(%s) failed: %m", login_dir); } } void master_settings_do_fixes(const struct master_settings *set) { const char *empty_dir; struct stat st; /* since base dir is under /var/run by default, it may have been deleted. */ if (mkdir_parents(set->base_dir, 0755) < 0 && errno != EEXIST) i_fatal("mkdir(%s) failed: %m", set->base_dir); /* allow base_dir to be a symlink, so don't use lstat() */ if (stat(set->base_dir, &st) < 0) i_fatal("stat(%s) failed: %m", set->base_dir); if (!S_ISDIR(st.st_mode)) i_fatal("%s is not a directory", set->base_dir); if ((st.st_mode & 0755) != 0755) { i_warning("Fixing permissions of %s to be world-readable", set->base_dir); if (chmod(set->base_dir, 0755) < 0) i_error("chmod(%s) failed: %m", set->base_dir); } /* Make sure our permanent state directory exists */ if (mkdir_parents(set->state_dir, 0755) < 0 && errno != EEXIST) i_fatal("mkdir(%s) failed: %m", set->state_dir); mkdir_login_dir(set, t_strconcat(set->base_dir, "/login", NULL)); mkdir_login_dir(set, t_strconcat(set->base_dir, "/token-login", NULL)); empty_dir = t_strconcat(set->base_dir, "/empty", NULL); if (safe_mkdir(empty_dir, 0755, master_uid, getegid()) == 0) { i_warning("Corrected permissions for empty directory " "%s", empty_dir); } } dovecot-2.2.33.2/src/master/service-monitor.c0000644000175000017500000005251713165463624015724 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "ioloop.h" #include "fd-close-on-exec.h" #include "hash.h" #include "str.h" #include "safe-mkstemp.h" #include "time-util.h" #include "master-client.h" #include "service.h" #include "service-process.h" #include "service-process-notify.h" #include "service-anvil.h" #include "service-log.h" #include "service-monitor.h" #include #include #include #include #define SERVICE_DROP_WARN_INTERVAL_SECS 1 #define SERVICE_DROP_TIMEOUT_MSECS (10*1000) #define MAX_DIE_WAIT_MSECS 5000 #define SERVICE_MAX_EXIT_FAILURES_IN_SEC 10 #define SERVICE_PREFORK_MAX_AT_ONCE 10 static void service_monitor_start_extra_avail(struct service *service); static void service_status_more(struct service_process *process, const struct master_status *status); static void service_monitor_listen_start_force(struct service *service); static void service_process_kill_idle(struct service_process *process) { struct service *service = process->service; struct master_status status; i_assert(process->available_count == service->client_limit); if (service->process_avail <= service->set->process_min_avail) { /* we don't have any extra idling processes anymore. */ timeout_remove(&process->to_idle); } else if (process->last_kill_sent > process->last_status_update+1) { service_error(service, "Process %s is ignoring idle SIGINT", dec2str(process->pid)); /* assume this process is busy */ i_zero(&status); service_status_more(process, &status); process->available_count = 0; } else { if (kill(process->pid, SIGINT) < 0 && errno != ESRCH) { service_error(service, "kill(%s, SIGINT) failed: %m", dec2str(process->pid)); } process->last_kill_sent = ioloop_time; } } static void service_status_more(struct service_process *process, const struct master_status *status) { struct service *service = process->service; process->total_count += process->available_count - status->available_count; process->idle_start = 0; if (process->to_idle != NULL) timeout_remove(&process->to_idle); if (status->available_count != 0) return; /* process used up all of its clients */ i_assert(service->process_avail > 0); service->process_avail--; if (service->type == SERVICE_TYPE_LOGIN && service->process_avail == 0 && service->process_count == service->process_limit) service_login_notify(service, TRUE); /* we may need to start more */ service_monitor_start_extra_avail(service); service_monitor_listen_start(service); } static void service_status_less(struct service_process *process, const struct master_status *status) { struct service *service = process->service; if (process->available_count == 0) { /* process can accept more clients again */ if (service->process_avail++ == 0) service_monitor_listen_stop(service); i_assert(service->process_avail <= service->process_count); } if (status->available_count == service->client_limit) { process->idle_start = ioloop_time; if (service->process_avail > service->set->process_min_avail && process->to_idle == NULL && service->idle_kill != UINT_MAX) { /* we have more processes than we really need. add a bit of randomness so that we don't send the signal to all of them at once */ process->to_idle = timeout_add((service->idle_kill * 1000) + (rand() % 100)*10, service_process_kill_idle, process); } } if (service->type == SERVICE_TYPE_LOGIN) service_login_notify(service, FALSE); } static void service_status_input_one(struct service *service, const struct master_status *status) { struct service_process *process; process = hash_table_lookup(service_pids, POINTER_CAST(status->pid)); if (process == NULL) { /* we've probably wait()ed it away already. ignore */ return; } if (process->uid != status->uid || process->service != service) { /* a) Process was closed and another process was created with the same PID, but we're still receiving status update from the old process. b) Some process is trying to corrupt our internal state by trying to pretend to be someone else. We could use stronger randomness here, but the worst they can do is DoS and there are already more serious problems if someone is able to do this.. */ service_error(service, "Ignoring invalid update from child %s " "(UID=%u)", dec2str(status->pid), status->uid); return; } process->last_status_update = ioloop_time; if (process->to_status != NULL) { /* first status notification */ timeout_remove(&process->to_status); } if (process->available_count == status->available_count) return; if (process->available_count > status->available_count) { /* process started servicing some more clients */ service_status_more(process, status); } else { /* process finished servicing some clients */ service_status_less(process, status); } process->available_count = status->available_count; } static void service_status_input(struct service *service) { struct master_status status[1024/sizeof(struct master_status)]; unsigned int i, count; ssize_t ret; ret = read(service->status_fd[0], &status, sizeof(status)); if (ret <= 0) { if (ret == 0) service_error(service, "read(status) failed: EOF"); else if (errno != EAGAIN) service_error(service, "read(status) failed: %m"); else return; service_monitor_stop(service); return; } if ((ret % sizeof(struct master_status)) != 0) { service_error(service, "service sent partial status update " "(%d bytes)", (int)ret); return; } count = ret / sizeof(struct master_status); for (i = 0; i < count; i++) service_status_input_one(service, &status[i]); } static void service_monitor_throttle(struct service *service) { if (service->to_throttle != NULL) return; i_assert(service->throttle_secs > 0); service_error(service, "command startup failed, throttling for %u secs", service->throttle_secs); service_throttle(service, service->throttle_secs); service->throttle_secs *= 2; if (service->throttle_secs > SERVICE_STARTUP_FAILURE_THROTTLE_MAX_SECS) service->throttle_secs = SERVICE_STARTUP_FAILURE_THROTTLE_MAX_SECS; } static void service_drop_timeout(struct service *service) { struct service_listener *const *lp; int fd; i_assert(service->process_avail == 0); /* drop all pending connections */ array_foreach(&service->listeners, lp) { while ((fd = net_accept((*lp)->fd, NULL, NULL)) > 0) net_disconnect(fd); } service_monitor_listen_start_force(service); service->listen_pending = TRUE; } static void service_monitor_listen_pending(struct service *service) { i_assert(service->process_avail == 0); service_monitor_listen_stop(service); service->listen_pending = TRUE; service->to_drop = timeout_add(SERVICE_DROP_TIMEOUT_MSECS, service_drop_timeout, service); } static void service_drop_connections(struct service_listener *l) { struct service *service = l->service; const char *limit_name; unsigned int limit; int fd; if (service->last_drop_warning + SERVICE_DROP_WARN_INTERVAL_SECS <= ioloop_time) { service->last_drop_warning = ioloop_time; if (service->process_limit > 1) { limit_name = "process_limit"; limit = service->process_limit; } else if (service->set->service_count == 1) { i_assert(service->client_limit == 1); limit_name = "client_limit/service_count"; limit = 1; } else { limit_name = "client_limit"; limit = service->client_limit; } i_warning("service(%s): %s (%u) reached, " "client connections are being dropped", service->set->name, limit_name, limit); } if (service->type == SERVICE_TYPE_LOGIN) { /* reached process limit, notify processes that they need to start killing existing connections if they reach connection limit */ service_login_notify(service, TRUE); service_monitor_listen_pending(service); } else if (!service->listen_pending) { /* maybe this is a temporary peak, stop for a while and see if it goes away */ service_monitor_listen_pending(service); } else { /* this has been happening for a while now. just accept and close the connection, so it's clear that this is happening because of the limit, rather than because the service processes aren't answering fast enough */ fd = net_accept(l->fd, NULL, NULL); if (fd > 0) net_disconnect(fd); } } static void service_accept(struct service_listener *l) { struct service *service = l->service; i_assert(service->process_avail == 0); if (service->process_count == service->process_limit) { /* we've reached our limits, new clients will have to wait until there are more processes available */ service_drop_connections(l); return; } /* create a child process and let it accept() this connection */ if (service_process_create(service) == NULL) service_monitor_throttle(service); else service_monitor_listen_stop(service); } static bool service_monitor_start_count(struct service *service, unsigned int limit) { unsigned int i, count; i_assert(service->set->process_min_avail >= service->process_avail); count = service->set->process_min_avail - service->process_avail; if (service->process_count + count > service->process_limit) count = service->process_limit - service->process_count; if (count > limit) count = limit; for (i = 0; i < count; i++) { if (service_process_create(service) == NULL) { service_monitor_throttle(service); break; } } if (i > 0) { /* we created some processes, they'll do the listening now */ service_monitor_listen_stop(service); } return i == count; } static void service_monitor_prefork_timeout(struct service *service) { /* don't prefork more processes if other more important processes had been forked while we were waiting for this timeout (= master seems busy) */ if (service->list->fork_counter != service->prefork_counter) { service->prefork_counter = service->list->fork_counter; return; } if (service->process_avail < service->set->process_min_avail) { if (service_monitor_start_count(service, SERVICE_PREFORK_MAX_AT_ONCE) && service->process_avail < service->set->process_min_avail) return; } timeout_remove(&service->to_prefork); } static void service_monitor_start_extra_avail(struct service *service) { if (service->process_avail >= service->set->process_min_avail || service->list->destroying) return; if (service->process_avail == 0) { /* quickly start one process now */ if (!service_monitor_start_count(service, 1)) return; if (service->process_avail >= service->set->process_min_avail) return; } if (service->to_prefork == NULL) { /* ioloop handles timeouts before fds (= SIGCHLD callback), so let the first timeout handler call simply update the fork counter and the second one check if we're busy or not. */ service->to_prefork = timeout_add_short(0, service_monitor_prefork_timeout, service); } } static void service_monitor_listen_start_force(struct service *service) { struct service_listener *const *listeners; service->listening = TRUE; service->listen_pending = FALSE; if (service->to_drop != NULL) timeout_remove(&service->to_drop); array_foreach(&service->listeners, listeners) { struct service_listener *l = *listeners; if (l->io == NULL && l->fd != -1) l->io = io_add(l->fd, IO_READ, service_accept, l); } } void service_monitor_listen_start(struct service *service) { if (service->process_avail > 0 || (service->process_count == service->process_limit && service->listen_pending)) return; service_monitor_listen_start_force(service); } void service_monitor_listen_stop(struct service *service) { struct service_listener *const *listeners; array_foreach(&service->listeners, listeners) { struct service_listener *l = *listeners; if (l->io != NULL) io_remove(&l->io); } service->listening = FALSE; service->listen_pending = FALSE; if (service->to_drop != NULL) timeout_remove(&service->to_drop); } static int service_login_create_notify_fd(struct service *service) { int fd, ret; if (service->login_notify_fd != -1) return 0; T_BEGIN { string_t *prefix = t_str_new(128); const char *path; str_append(prefix, service->set->master_set->base_dir); str_append(prefix, "/login-master-notify"); fd = safe_mkstemp(prefix, 0600, (uid_t)-1, (gid_t)-1); path = str_c(prefix); if (fd == -1) { service_error(service, "safe_mkstemp(%s) failed: %m", path); } else if (unlink(path) < 0) { service_error(service, "unlink(%s) failed: %m", path); } else { fd_close_on_exec(fd, TRUE); service->login_notify_fd = fd; } } T_END; ret = fd == -1 ? -1 : 0; if (fd != service->login_notify_fd) i_close_fd(&fd); return ret; } void services_monitor_start(struct service_list *service_list) { struct service *const *services; if (services_log_init(service_list) < 0) return; service_anvil_monitor_start(service_list); if (service_list->io_master == NULL && service_list->master_fd != -1) { service_list->io_master = io_add(service_list->master_fd, IO_READ, master_client_connected, service_list); } array_foreach(&service_list->services, services) { struct service *service = *services; if (service->type == SERVICE_TYPE_LOGIN) { if (service_login_create_notify_fd(service) < 0) continue; } if (service->master_dead_pipe_fd[0] == -1) { if (pipe(service->master_dead_pipe_fd) < 0) { service_error(service, "pipe() failed: %m"); continue; } fd_close_on_exec(service->master_dead_pipe_fd[0], TRUE); fd_close_on_exec(service->master_dead_pipe_fd[1], TRUE); } if (service->status_fd[0] == -1) { /* we haven't yet created status pipe */ if (pipe(service->status_fd) < 0) { service_error(service, "pipe() failed: %m"); continue; } net_set_nonblock(service->status_fd[0], TRUE); fd_close_on_exec(service->status_fd[0], TRUE); net_set_nonblock(service->status_fd[1], TRUE); fd_close_on_exec(service->status_fd[1], TRUE); } if (service->io_status == NULL) { service->io_status = io_add(service->status_fd[0], IO_READ, service_status_input, service); } service_monitor_start_extra_avail(service); service_monitor_listen_start(service); } if (service_list->log->status_fd[0] != -1) { if (service_process_create(service_list->log) != NULL) service_monitor_listen_stop(service_list->log); } /* start up a process for startup-services */ array_foreach(&service_list->services, services) { struct service *service = *services; if (service->type == SERVICE_TYPE_STARTUP && service->status_fd[0] != -1) { if (service_process_create(service) != NULL) service_monitor_listen_stop(service); } } } static void service_monitor_close_dead_pipe(struct service *service) { if (service->master_dead_pipe_fd[0] != -1) { i_close_fd(&service->master_dead_pipe_fd[0]); i_close_fd(&service->master_dead_pipe_fd[1]); } } void service_monitor_stop(struct service *service) { int i; if (service->io_status != NULL) io_remove(&service->io_status); if (service->status_fd[0] != -1 && service->type != SERVICE_TYPE_ANVIL) { for (i = 0; i < 2; i++) { if (close(service->status_fd[i]) < 0) { service_error(service, "close(status fd) failed: %m"); } service->status_fd[i] = -1; } } service_monitor_close_dead_pipe(service); if (service->login_notify_fd != -1) { if (close(service->login_notify_fd) < 0) { service_error(service, "close(login notify fd) failed: %m"); } service->login_notify_fd = -1; } if (service->to_login_notify != NULL) timeout_remove(&service->to_login_notify); service_monitor_listen_stop(service); if (service->to_throttle != NULL) timeout_remove(&service->to_throttle); if (service->to_prefork != NULL) timeout_remove(&service->to_prefork); } void service_monitor_stop_close(struct service *service) { struct service_listener *const *listeners; service_monitor_stop(service); array_foreach(&service->listeners, listeners) { struct service_listener *l = *listeners; if (l->fd != -1) i_close_fd(&l->fd); } } static void services_monitor_wait(struct service_list *service_list) { struct service *const *servicep; struct timeval tv_start; bool finished; io_loop_time_refresh(); tv_start = ioloop_timeval; for (;;) { finished = TRUE; services_monitor_reap_children(); array_foreach(&service_list->services, servicep) { if ((*servicep)->status_fd[0] != -1) service_status_input(*servicep); if ((*servicep)->process_avail > 0) finished = FALSE; } io_loop_time_refresh(); if (finished || timeval_diff_msecs(&ioloop_timeval, &tv_start) > MAX_DIE_WAIT_MSECS) break; usleep(100000); } } static bool service_processes_close_listeners(struct service *service) { struct service_process *process = service->processes; bool ret = FALSE; for (; process != NULL; process = process->next) { if (kill(process->pid, SIGQUIT) == 0) ret = TRUE; else if (errno != ESRCH) { service_error(service, "kill(%s, SIGQUIT) failed: %m", dec2str(process->pid)); } } return ret; } static bool service_list_processes_close_listeners(struct service_list *service_list) { struct service *const *servicep; bool ret = FALSE; array_foreach(&service_list->services, servicep) { if (service_processes_close_listeners(*servicep)) ret = TRUE; } return ret; } static void services_monitor_wait_and_kill(struct service_list *service_list) { /* we've notified all children that the master is dead. now wait for the children to either die or to tell that they're no longer listening for new connections. */ services_monitor_wait(service_list); /* Even if the waiting stopped early because all the process_avail==0, it can mean that there are processes that have the listener socket open (just not actively being listened to). We'll need to make sure that those sockets are closed before we exit, so that a restart won't fail. Do this by sending SIGQUIT to all the child processes that are left, which are handled by lib-master to immediately close the listener in the signal handler itself. */ if (service_list_processes_close_listeners(service_list)) { /* SIGQUITs were sent. wait a little bit to make sure they're also processed before quitting. */ usleep(100000); } } void services_monitor_stop(struct service_list *service_list, bool wait) { struct service *const *services; array_foreach(&service_list->services, services) service_monitor_close_dead_pipe(*services); if (wait) services_monitor_wait_and_kill(service_list); if (service_list->io_master != NULL) io_remove(&service_list->io_master); if (service_list->master_fd != -1) i_close_fd(&service_list->master_fd); array_foreach(&service_list->services, services) service_monitor_stop(*services); services_log_deinit(service_list); } static bool service_process_failure(struct service_process *process, int status) { struct service *service = process->service; bool throttle; service_process_log_status_error(process, status); throttle = process->to_status != NULL; if (!throttle && !service->have_successful_exits) { /* this service has seen no successful exits yet. try to avoid failure storms by throttling the service if it only keeps failing rapidly. this is no longer done after one success to avoid intentional DoSing, in case attacker finds a way to quickly crash his own session. */ if (service->exit_failure_last != ioloop_time) { service->exit_failure_last = ioloop_time; service->exit_failures_in_sec = 0; } if (++service->exit_failures_in_sec > SERVICE_MAX_EXIT_FAILURES_IN_SEC) throttle = TRUE; } service_process_notify_add(service_anvil_global->kills, process); return throttle; } void services_monitor_reap_children(void) { struct service_process *process; struct service *service; pid_t pid; int status; bool service_stopped, throttle; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { process = hash_table_lookup(service_pids, POINTER_CAST(pid)); if (process == NULL) { i_error("waitpid() returned unknown PID %s", dec2str(pid)); continue; } service = process->service; if (status == 0) { /* success - one success resets all failures */ service->have_successful_exits = TRUE; service->exit_failures_in_sec = 0; service->throttle_secs = SERVICE_STARTUP_FAILURE_THROTTLE_MIN_SECS; throttle = FALSE; } else { throttle = service_process_failure(process, status); } if (service->type == SERVICE_TYPE_ANVIL) service_anvil_process_destroyed(process); /* if we're reloading, we may get here with a service list that's going to be destroyed after this process is destroyed. keep the list referenced until we're done. */ service_list_ref(service->list); service_process_destroy(process); if (throttle) service_monitor_throttle(service); service_stopped = service->status_fd[0] == -1; if (!service_stopped && !service->list->destroying) { service_monitor_start_extra_avail(service); /* if there are no longer listening processes, start listening for more */ if (service->to_throttle != NULL) { /* throttling */ } else if (service == service->list->log && service->process_count == 0) { /* log service must always be running */ if (service_process_create(service) == NULL) service_monitor_throttle(service); } else { service_monitor_listen_start(service); } } service_list_unref(service->list); } } dovecot-2.2.33.2/src/master/dup2-array.h0000644000175000017500000000035513123174404014550 00000000000000#ifndef DUP2_ARRAY_H #define DUP2_ARRAY_H struct dup2 { int fd_src, fd_dest; }; ARRAY_DEFINE_TYPE(dup2, struct dup2); void dup2_append(ARRAY_TYPE(dup2) *dups, int fd_src, int fd_dest); int dup2_array(ARRAY_TYPE(dup2) *dups); #endif dovecot-2.2.33.2/src/master/sd-daemon.h0000644000175000017500000002412213123174404014427 00000000000000/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ #ifndef foosddaemonhfoo #define foosddaemonhfoo /*** Copyright 2010 Lennart Poettering Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***/ #include #include #ifdef __cplusplus extern "C" { #endif /* Reference implementation of a few systemd related interfaces for writing daemons. These interfaces are trivial to implement. To simplify porting we provide this reference implementation. Applications are welcome to reimplement the algorithms described here if they do not want to include these two source files. The following functionality is provided: - Support for logging with log levels on stderr - File descriptor passing for socket-based activation - Daemon startup and status notification - Detection of systemd boots You may compile this with -DDISABLE_SYSTEMD to disable systemd support. This makes all those calls NOPs that are directly related to systemd (i.e. only sd_is_xxx() will stay useful). Since this is drop-in code we don't want any of our symbols to be exported in any case. Hence we declare hidden visibility for all of them. You may find an up-to-date version of these source files online: http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c This should compile on non-Linux systems, too, but with the exception of the sd_is_xxx() calls all functions will become NOPs. See sd-daemon(7) for more information. */ #ifndef _sd_printf_attr_ #if __GNUC__ >= 4 #define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) #else #define _sd_printf_attr_(a,b) #endif #endif #ifndef _sd_hidden_ #if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS) #define _sd_hidden_ __attribute__ ((visibility("hidden"))) #else #define _sd_hidden_ #endif #endif /* Log levels for usage on stderr: fprintf(stderr, SD_NOTICE "Hello World!\n"); This is similar to printk() usage in the kernel. */ #define SD_EMERG "<0>" /* system is unusable */ #define SD_ALERT "<1>" /* action must be taken immediately */ #define SD_CRIT "<2>" /* critical conditions */ #define SD_ERR "<3>" /* error conditions */ #define SD_WARNING "<4>" /* warning conditions */ #define SD_NOTICE "<5>" /* normal but significant condition */ #define SD_INFO "<6>" /* informational */ #define SD_DEBUG "<7>" /* debug-level messages */ /* The first passed file descriptor is fd 3 */ #define SD_LISTEN_FDS_START 3 /* Returns how many file descriptors have been passed, or a negative errno code on failure. Optionally, removes the $LISTEN_FDS and $LISTEN_PID file descriptors from the environment (recommended, but problematic in threaded environments). If r is the return value of this function you'll find the file descriptors passed as fds SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative errno style error code on failure. This function call ensures that the FD_CLOEXEC flag is set for the passed file descriptors, to make sure they are not passed on to child processes. If FD_CLOEXEC shall not be set, the caller needs to unset it after this call for all file descriptors that are used. See sd_listen_fds(3) for more information. */ int sd_listen_fds(int unset_environment) _sd_hidden_; /* Helper call for identifying a passed file descriptor. Returns 1 if the file descriptor is a FIFO in the file system stored under the specified path, 0 otherwise. If path is NULL a path name check will not be done and the call only verifies if the file descriptor refers to a FIFO. Returns a negative errno style error code on failure. See sd_is_fifo(3) for more information. */ int sd_is_fifo(int fd, const char *path) _sd_hidden_; /* Helper call for identifying a passed file descriptor. Returns 1 if the file descriptor is a socket of the specified family (AF_INET, ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If family is 0 a socket family check will not be done. If type is 0 a socket type check will not be done and the call only verifies if the file descriptor refers to a socket. If listening is > 0 it is verified that the socket is in listening mode. (i.e. listen() has been called) If listening is == 0 it is verified that the socket is not in listening mode. If listening is < 0 no listening mode check is done. Returns a negative errno style error code on failure. See sd_is_socket(3) for more information. */ int sd_is_socket(int fd, int family, int type, int listening) _sd_hidden_; /* Helper call for identifying a passed file descriptor. Returns 1 if the file descriptor is an Internet socket, of the specified family (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version check is not done. If type is 0 a socket type check will not be done. If port is 0 a socket port check will not be done. The listening flag is used the same way as in sd_is_socket(). Returns a negative errno style error code on failure. See sd_is_socket_inet(3) for more information. */ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) _sd_hidden_; /* Helper call for identifying a passed file descriptor. Returns 1 if the file descriptor is an AF_UNIX socket of the specified type (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 a socket type check will not be done. If path is NULL a socket path check will not be done. For normal AF_UNIX sockets set length to 0. For abstract namespace sockets set length to the length of the socket name (including the initial 0 byte), and pass the full socket path in path (including the initial 0 byte). The listening flag is used the same way as in sd_is_socket(). Returns a negative errno style error code on failure. See sd_is_socket_unix(3) for more information. */ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) _sd_hidden_; /* Informs systemd about changed daemon state. This takes a number of newline separated environment-style variable assignments in a string. The following variables are known: READY=1 Tells systemd that daemon startup is finished (only relevant for services of Type=notify). The passed argument is a boolean "1" or "0". Since there is little value in signalling non-readiness the only value daemons should send is "READY=1". STATUS=... Passes a single-line status string back to systemd that describes the daemon state. This is free-from and can be used for various purposes: general state feedback, fsck-like programs could pass completion percentages and failing programs could pass a human readable error message. Example: "STATUS=Completed 66% of file system check..." ERRNO=... If a daemon fails, the errno-style error code, formatted as string. Example: "ERRNO=2" for ENOENT. BUSERROR=... If a daemon fails, the D-Bus error-style error code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" MAINPID=... The main pid of a daemon, in case systemd did not fork off the process itself. Example: "MAINPID=4711" Daemons can choose to send additional variables. However, it is recommened to prefix variable names not listed above with X_. Returns a negative errno-style error code on failure. Returns > 0 if systemd could be notified, 0 if it couldn't possibly because systemd is not running. Example: When a daemon finished starting up, it could issue this call to notify systemd about it: sd_notify(0, "READY=1"); See sd_notifyf() for more complete examples. See sd_notify(3) for more information. */ int sd_notify(int unset_environment, const char *state) _sd_hidden_; /* Similar to sd_notify() but takes a format string. Example 1: A daemon could send the following after initialization: sd_notifyf(0, "READY=1\n" "STATUS=Processing requests...\n" "MAINPID=%lu", (unsigned long) getpid()); Example 2: A daemon could send the following shortly before exiting, on failure: sd_notifyf(0, "STATUS=Failed to start up: %s\n" "ERRNO=%i", strerror(errno), errno); See sd_notifyf(3) for more information. */ int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_; /* Returns > 0 if the system was booted with systemd. Returns < 0 on error. Returns 0 if the system was not booted with systemd. Note that all of the functions above handle non-systemd boots just fine. You should NOT protect them with a call to this function. Also note that this function checks whether the system, not the user session is controlled by systemd. However the functions above work for both session and system services. See sd_booted(3) for more information. */ int sd_booted(void) _sd_hidden_; #ifdef __cplusplus } #endif #endif dovecot-2.2.33.2/src/master/dup2-array.c0000644000175000017500000000276613123174404014553 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fd-close-on-exec.h" #include "dup2-array.h" #include void dup2_append(ARRAY_TYPE(dup2) *dups, int fd_src, int fd_dest) { struct dup2 d; i_assert(fd_src >= 0); i_assert(fd_dest >= 0); d.fd_src = fd_src; d.fd_dest = fd_dest; array_append(dups, &d, 1); } int dup2_array(ARRAY_TYPE(dup2) *dups_arr) { struct dup2 *dups; bool *moved, moves; unsigned int i, j, count, conflict; int fd; dups = array_get_modifiable(dups_arr, &count); moved = t_new(bool, count); for (;;) { conflict = count; moves = FALSE; for (i = 0; i < count; i++) { if (moved[i]) continue; for (j = 0; j < count; j++) { if (dups[j].fd_src == dups[i].fd_dest && !moved[j]) { conflict = j; break; } } if (j == count) { /* no conflicts, move it */ moved[i] = TRUE; moves = TRUE; if (dup2(dups[i].fd_src, dups[i].fd_dest) < 0) { i_error("dup2(%d, %d) failed: %m", dups[i].fd_src, dups[i].fd_dest); return -1; } } } if (conflict == count) break; if (moves) { /* it's possible that the conflicting fd was moved already. try again. */ continue; } /* ok, we have to dup() */ fd = dup(dups[conflict].fd_src); if (fd == -1) { i_error("dup(%d) failed: %m", dups[conflict].fd_src); return -1; } fd_close_on_exec(fd, TRUE); dups[conflict].fd_src = fd; } return 0; } dovecot-2.2.33.2/src/master/master-settings.h0000644000175000017500000000144613165463543015730 00000000000000#ifndef MASTER_SETTINGS_H #define MASTER_SETTINGS_H #include "service-settings.h" struct master_settings { const char *base_dir; const char *state_dir; const char *libexec_dir; const char *instance_name; const char *protocols; const char *listen; const char *ssl; const char *default_internal_user; const char *default_login_user; unsigned int default_process_limit; unsigned int default_client_limit; unsigned int default_idle_kill; uoff_t default_vsz_limit; bool version_ignore; unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; ARRAY_TYPE(service_settings) services; char **protocols_split; }; extern const struct setting_parser_info master_setting_parser_info; void master_settings_do_fixes(const struct master_settings *set); #endif dovecot-2.2.33.2/src/master/master-client.h0000644000175000017500000000027513123174404015332 00000000000000#ifndef MASTER_CLIENT_H #define MASTER_CLIENT_H void master_client_connected(struct service_list *service_list); void master_clients_init(void); void master_clients_deinit(void); #endif dovecot-2.2.33.2/src/master/Makefile.am0000644000175000017500000000173213165463624014460 00000000000000pkglibexecdir = $(libexecdir)/dovecot sbin_PROGRAMS = dovecot if HAVE_SYSTEMD SYSTEMD_SOURCES = sd-daemon.c endif AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DBINDIR=\""$(bindir)"\" dovecot_LDADD = \ $(LIBCAP) \ $(LIBDOVECOT) dovecot_DEPENDENCIES = $(LIBDOVECOT_DEPS) dovecot_SOURCES = \ capabilities-posix.c \ dup2-array.c \ main.c \ master-client.c \ master-settings.c \ service-anvil.c \ service-listen.c \ service-log.c \ service-monitor.c \ service-process.c \ service-process-notify.c \ service.c \ $(SYSTEMD_SOURCES) noinst_HEADERS = \ capabilities.h \ common.h \ dup2-array.h \ master-client.h \ master-settings.h \ sd-daemon.h \ service-anvil.h \ service-listen.h \ service-log.h \ service-monitor.h \ service-process.h \ service-process-notify.h \ service.h dovecot-2.2.33.2/src/lib-program-client/0002755000175000017500000000000013172375611014673 500000000000000dovecot-2.2.33.2/src/lib-program-client/test-program-client-remote.c0000644000175000017500000002254013165463624022155 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-lib.h" #include "mempool.h" #include "buffer.h" #include "str.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "net.h" #include "iostream-temp.h" #include "program-client.h" #include static const char *TEST_SOCKET = "program-client-test.sock"; static const char *pclient_test_io_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" "Praesent vehicula ac leo vel placerat. Nullam placerat \n" "volutpat leo, sed ultricies felis pulvinar quis. Nam \n" "tempus, augue ut tempor cursus, neque felis commodo lacus, \n" "sit amet tincidunt arcu justo vel augue. Proin dapibus \n" "vulputate maximus. Mauris congue lacus felis, sed varius \n" "leo finibus sagittis. Cum sociis natoque penatibus et magnis \n" "dis parturient montes, nascetur ridiculus mus. Aliquam \n" "laoreet arcu a hendrerit consequat. Duis vitae erat tellus."; static struct program_client_settings pc_set = { .client_connect_timeout_msecs = 1000, .input_idle_timeout_msecs = 5000, .debug = TRUE, }; static struct test_server { struct ioloop *ioloop; struct io *io; struct timeout *to; struct test_client *client; int listen_fd; } test_globals; struct test_client { pool_t pool; int fd; struct io *io; struct istream *in; struct ostream *out; struct ostream *os_body; struct istream *body; ARRAY_TYPE(const_string) args; enum { CLIENT_STATE_INIT, CLIENT_STATE_VERSION, CLIENT_STATE_ARGS, CLIENT_STATE_BODY } state; }; static void test_program_client_destroy(struct test_client **_client) { struct test_client *client = *_client; *_client = NULL; if (o_stream_nfinish(client->out) != 0) i_error("output error: %s", o_stream_get_error(client->out)); if (client->io != NULL) io_remove(&client->io); o_stream_unref(&client->out); i_stream_unref(&client->in); if (client->os_body != NULL) o_stream_unref(&client->os_body); if (client->body != NULL) i_stream_unref(&client->body); close(client->fd); pool_unref(&client->pool); test_globals.client = NULL; } static int test_program_input_handle(struct test_client *client, const char *line) { int cmp, ret; const char *arg; const unsigned char *data; size_t siz; switch(client->state) { case CLIENT_STATE_INIT: test_assert((cmp = strncmp(line, "VERSION\tscript\t", 15)) == 0); if (cmp == 0) { client->state = CLIENT_STATE_VERSION; } else return -1; break; case CLIENT_STATE_VERSION: test_assert((cmp = strcmp(line, "-")) == 0); if (cmp == 0) client->state = CLIENT_STATE_ARGS; else return -1; break; case CLIENT_STATE_ARGS: if (strcmp(line, "") == 0) { array_append_zero(&client->args); client->state = CLIENT_STATE_BODY; return 0; } arg = p_strdup(client->pool, line); array_append(&client->args, &arg, 1); break; case CLIENT_STATE_BODY: client->os_body = iostream_temp_create_named("/tmp", 0, "test_program_input body"); do { data = i_stream_get_data(client->in, &siz); o_stream_nsend(client->os_body, data, siz); i_stream_skip(client->in, siz); } while ((ret = i_stream_read(client->in))>0); if (ret == 0 && !client->in->eof) break; if (ret == -1 && !client->in->eof) return -1; if (o_stream_nfinish(client->os_body)<0) { i_fatal("Could not write response: %s", o_stream_get_error(client->os_body)); } client->body = iostream_temp_finish(&client->os_body, -1); return 1; } return 0; } static void test_program_run(struct test_client *client) { int ret; const char *const *args; unsigned int count; const unsigned char *data; size_t siz; timeout_remove(&test_globals.to); args = array_get(&client->args, &count); test_assert(count > 0); if (strcmp(args[0], "test_program_success")==0) { /* return hello world */ test_assert(count >= 3); o_stream_nsend_str(client->out, t_strdup_printf("%s %s\n+\n", args[1], args[2])); } else if (strcmp(args[0], "test_program_io")==0) { do { data = i_stream_get_data(client->body, &siz); o_stream_nsend(client->out, data, siz); i_stream_skip(client->body, siz); } while((ret = i_stream_read(client->body))>0); o_stream_nsend_str(client->out, "+\n"); } else if (strcmp(args[0], "test_program_failure")==0) { o_stream_nsend_str(client->out, "-\n"); } test_program_client_destroy(&client); } static void test_program_input(struct test_client *client) { const char *line = ""; if (client->state == CLIENT_STATE_BODY) { if (test_program_input_handle(client, NULL)==0 && !client->in->eof) return; } else { line = i_stream_read_next_line(client->in); if ((line == NULL && !client->in->eof) || (line != NULL && test_program_input_handle(client, line) == 0)) return; } if (client->in->eof) { io_remove(&client->io); /* incur slight delay to check if the connection gets prematurely closed */ test_globals.to = timeout_add_short(100, test_program_run, client); } if (client->state != CLIENT_STATE_BODY) { if (client->in->eof) i_warning("Client prematurely disconnected"); else i_warning("Client sent invalid line: %s", line); } } static void test_program_connected(struct test_server *server) { int fd; i_assert(server->client == NULL); fd = net_accept(server->listen_fd, NULL, NULL); /* makes no sense on unix */ if (fd < 0) i_fatal("Failed to accept connection: %m"); pool_t pool = pool_alloconly_create("test_program client", 1024); struct test_client *client = p_new(pool, struct test_client, 1); client->pool = pool; client->fd = fd; client->in = i_stream_create_fd(fd, -1, FALSE); client->out = o_stream_create_fd(fd, -1, FALSE); client->io = io_add_istream(client->in, test_program_input, client); p_array_init(&client->args, client->pool, 2); server->client = client; } static void test_program_setup(void) { test_begin("test_program_setup"); test_globals.ioloop = io_loop_create(); io_loop_set_current(test_globals.ioloop); /* create listener */ test_globals.listen_fd = net_listen_unix_unlink_stale(TEST_SOCKET, 100); if (test_globals.listen_fd < 0) i_fatal("Cannot create unix listener: %m"); test_globals.io = io_add(test_globals.listen_fd, IO_READ, test_program_connected, &test_globals); test_end(); } static void test_program_teardown(void) { test_begin("test_program_teardown"); if (test_globals.client != NULL) test_program_client_destroy(&test_globals.client); io_remove(&test_globals.io); i_close_fd(&test_globals.listen_fd); io_loop_destroy(&test_globals.ioloop); i_unlink(TEST_SOCKET); test_end(); } static void test_program_async_callback(int result, int *ret) { *ret = result; io_loop_stop(current_ioloop); } static void test_program_success(void) { test_begin("test_program_success"); int ret; const char *const args[] = { "test_program_success", "hello", "world", NULL }; struct program_client *pc = program_client_remote_create(TEST_SOCKET, args, &pc_set, FALSE); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = test_ostream_create(output); program_client_set_output(pc, os); program_client_run_async(pc, test_program_async_callback, &ret); io_loop_run(current_ioloop); test_assert(ret == 1); test_assert(strcmp(str_c(output), "hello world\n") == 0); program_client_destroy(&pc); o_stream_unref(&os); buffer_free(&output); test_end(); } static void test_program_io(void) { test_begin("test_program_io (async)"); int ret; const char *const args[] = { "test_program_io", NULL }; struct program_client *pc = program_client_remote_create(TEST_SOCKET, args, &pc_set, FALSE); struct istream *is = test_istream_create(pclient_test_io_string); program_client_set_input(pc, is); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = test_ostream_create(output); program_client_set_output(pc, os); program_client_run_async(pc, test_program_async_callback, &ret); io_loop_run(current_ioloop); test_assert(ret == 1); test_assert(strcmp(str_c(output), pclient_test_io_string) == 0); program_client_destroy(&pc); i_stream_unref(&is); o_stream_unref(&os); buffer_free(&output); test_end(); } static void test_program_failure(void) { test_begin("test_program_failure"); int ret; const char *const args[] = { "test_program_failure", NULL }; struct program_client *pc = program_client_remote_create(TEST_SOCKET, args, &pc_set, FALSE); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = test_ostream_create(output); program_client_set_output(pc, os); program_client_run_async(pc, test_program_async_callback, &ret); io_loop_run(current_ioloop); test_assert(ret == 0); program_client_destroy(&pc); o_stream_unref(&os); buffer_free(&output); test_end(); } static void test_program_noreply(void) { test_begin("test_program_noreply"); int ret; const char *const args[] = { "test_program_success", "hello", "world", NULL }; struct program_client *pc = program_client_remote_create(TEST_SOCKET, args, &pc_set, TRUE); program_client_run_async(pc, test_program_async_callback, &ret); io_loop_run(current_ioloop); test_assert(ret == 1); program_client_destroy(&pc); test_end(); } int main(void) { void (*tests[])(void) = { test_program_setup, test_program_success, test_program_io, test_program_failure, test_program_noreply, test_program_teardown, NULL }; return test_run(tests); } dovecot-2.2.33.2/src/lib-program-client/Makefile.in0000644000175000017500000006175613172375573016704 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-program-client ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libprogram_client_la_LIBADD = am_libprogram_client_la_OBJECTS = program-client.lo \ program-client-local.lo program-client-remote.lo libprogram_client_la_OBJECTS = $(am_libprogram_client_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-program-client-local$(EXEEXT) \ test-program-client-remote$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) test_program_client_local_SOURCES = test-program-client-local.c test_program_client_local_OBJECTS = \ test-program-client-local.$(OBJEXT) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = libprogram_client.la ../lib-test/libtest.la \ ../lib/liblib.la $(am__DEPENDENCIES_1) test_program_client_local_DEPENDENCIES = $(am__DEPENDENCIES_2) test_program_client_remote_SOURCES = test-program-client-remote.c test_program_client_remote_OBJECTS = \ test-program-client-remote.$(OBJEXT) test_program_client_remote_DEPENDENCIES = $(am__DEPENDENCIES_2) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libprogram_client_la_SOURCES) test-program-client-local.c \ test-program-client-remote.c DIST_SOURCES = $(libprogram_client_la_SOURCES) \ test-program-client-local.c test-program-client-remote.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libprogram_client.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libprogram_client_la_SOURCES = \ program-client.c \ program-client-local.c \ program-client-remote.c headers = \ program-client.h noinst_HEADERS = \ program-client-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-program-client-local \ test-program-client-remote test_libs = \ libprogram_client.la \ ../lib-test/libtest.la \ ../lib/liblib.la \ $(MODULE_LIBS) test_program_client_local_SOURCE = test-program-client-local.c test_program_client_local_LDADD = $(test_libs) test_program_client_remote_SOURCE = test-program-client-remote.c test_program_client_remote_LDADD = $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-program-client/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-program-client/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libprogram_client.la: $(libprogram_client_la_OBJECTS) $(libprogram_client_la_DEPENDENCIES) $(EXTRA_libprogram_client_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libprogram_client_la_OBJECTS) $(libprogram_client_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-program-client-local$(EXEEXT): $(test_program_client_local_OBJECTS) $(test_program_client_local_DEPENDENCIES) $(EXTRA_test_program_client_local_DEPENDENCIES) @rm -f test-program-client-local$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_program_client_local_OBJECTS) $(test_program_client_local_LDADD) $(LIBS) test-program-client-remote$(EXEEXT): $(test_program_client_remote_OBJECTS) $(test_program_client_remote_DEPENDENCIES) $(EXTRA_test_program_client_remote_DEPENDENCIES) @rm -f test-program-client-remote$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_program_client_remote_OBJECTS) $(test_program_client_remote_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/program-client-local.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/program-client-remote.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/program-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-program-client-local.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-program-client-remote.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if test "$$bin" = "test-program-client-local"; then \ if ! env NOVALGRIND=yes $(RUN_TEST) ./$$bin; then exit 1; fi; \ else \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ fi \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-program-client/program-client.h0000644000175000017500000000523413165463624017715 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #ifndef PROGRAM_CLIENT_H #define PROGRAM_CLIENT_H #include "restrict-access.h" struct program_client; struct program_client_settings { unsigned int client_connect_timeout_msecs; unsigned int input_idle_timeout_msecs; /* initialize with restrict_access_init(&set.restrict_set); */ struct restrict_access_settings restrict_set; const char *home; bool allow_root:1; bool debug:1; bool drop_stderr:1; }; typedef void program_client_fd_callback_t(void *context, struct istream *input); typedef void program_client_callback_t(int, void *); struct program_client *program_client_local_create(const char *bin_path, const char *const *args, const struct program_client_settings *set); struct program_client *program_client_remote_create(const char *socket_path, const char *const *args, const struct program_client_settings *set, bool noreply); int program_client_create(const char *uri, const char *const *args, const struct program_client_settings *set, bool noreply, struct program_client **pc_r, const char **error_r); void program_client_destroy(struct program_client **_pclient); void program_client_set_input(struct program_client *pclient, struct istream *input); void program_client_set_output(struct program_client *pclient, struct ostream *output); void program_client_set_output_seekable(struct program_client *pclient, const char *temp_prefix); struct istream *program_client_get_output_seekable(struct program_client *pclient); void program_client_switch_ioloop(struct program_client *pclient); /* Program provides side-channel output through an extra fd */ void program_client_set_extra_fd(struct program_client *pclient, int fd, program_client_fd_callback_t * callback, void *context); #define program_client_set_extra_fd(pclient, fd, callback, context) \ program_client_set_extra_fd(pclient, fd + \ CALLBACK_TYPECHECK(callback, \ void (*)(typeof(context), struct istream *input)), \ (program_client_fd_callback_t *)callback, context) void program_client_set_env(struct program_client *pclient, const char *name, const char *value); /* Since script service cannot return system exit code, the exit value shall be -1, 0, or 1. -1 is internal error, 0 is failure and 1 is success */ int program_client_run(struct program_client *pclient); void program_client_run_async(struct program_client *pclient, program_client_callback_t *, void*); #define program_client_run_async(pclient, callback, context) \ program_client_run_async(pclient, (program_client_callback_t*)callback, (char*)context + \ CALLBACK_TYPECHECK(callback, \ void (*)(int, typeof(context)))) #endif dovecot-2.2.33.2/src/lib-program-client/program-client-private.h0000644000175000017500000000367413165463624021373 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #ifndef PROGRAM_CLIENT_PRIVATE_H #define PROGRAM_CLIENT_PRIVATE_H #include "program-client.h" enum program_client_error { PROGRAM_CLIENT_ERROR_NONE, PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT, PROGRAM_CLIENT_ERROR_RUN_TIMEOUT, PROGRAM_CLIENT_ERROR_IO, PROGRAM_CLIENT_ERROR_OTHER }; struct program_client_extra_fd { struct program_client *pclient; int child_fd, parent_fd; struct istream *input; struct io *io; program_client_fd_callback_t *callback; void *context; }; struct program_client { pool_t pool; struct program_client_settings set; char *path; const char **args; ARRAY_TYPE(const_string) envs; int fd_in, fd_out; struct io *io; struct timeout *to; struct timeval start_time; struct istream *input, *program_input, *seekable_output; struct ostream *output, *program_output; char *temp_prefix; ARRAY(struct program_client_extra_fd) extra_fds; program_client_callback_t *callback; void *context; bool other_error; enum program_client_error error; int exit_code; int (*connect) (struct program_client * pclient); int (*close_output) (struct program_client * pclient); void (*switch_ioloop) (struct program_client * pclient); void (*disconnect) (struct program_client * pclient, bool force); void (*destroy) (struct program_client * pclient); bool debug:1; bool disconnected:1; bool output_seekable:1; bool destroying:1; }; void program_client_init(struct program_client *pclient, pool_t pool, const char *path, const char *const *args, const struct program_client_settings *set); void program_client_init_streams(struct program_client *pclient); int program_client_connected(struct program_client *pclient); void program_client_fail(struct program_client *pclient, enum program_client_error error); void program_client_program_input(struct program_client *pclient); void program_client_disconnected(struct program_client *pclient); #endif dovecot-2.2.33.2/src/lib-program-client/program-client-remote.c0000644000175000017500000002115113165463624021175 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "strescape.h" #include "array.h" #include "net.h" #include "write-full.h" #include "eacces-error.h" #include "istream-private.h" #include "ostream.h" #include "program-client-private.h" #include #include #include #define PROGRAM_CLIENT_VERSION_MAJOR "4" #define PROGRAM_CLIENT_VERSION_MINOR "0" #define PROGRAM_CLIENT_VERSION_STRING "VERSION\tscript\t" \ PROGRAM_CLIENT_VERSION_MAJOR "\t" \ PROGRAM_CLIENT_VERSION_MINOR "\n" /* * Script client input stream */ struct program_client_istream { struct istream_private istream; struct stat statbuf; struct program_client *client; }; static void program_client_istream_destroy(struct iostream_private *stream) { struct program_client_istream *scstream = (struct program_client_istream *) stream; i_stream_unref(&scstream->istream.parent); } static ssize_t program_client_istream_read(struct istream_private *stream) { struct program_client_istream *scstream = (struct program_client_istream *) stream; size_t pos, reserved; ssize_t ret = 0; i_stream_skip(stream->parent, stream->skip); stream->skip = 0; stream->buffer = i_stream_get_data(stream->parent, &pos); reserved = 0; if (stream->buffer != NULL && pos >= 1) { /* retain/hide potential return code at end of buffer */ reserved = (stream->buffer[pos - 1] == '\n' && pos > 1 ? 2 : 1); pos -= reserved; } if (stream->parent->eof) { if (pos == 0) i_stream_skip(stream->parent, reserved); stream->istream.eof = TRUE; ret = -1; } else do { if ((ret = i_stream_read(stream->parent)) == -2) { return -2; /* input buffer full */ } if (ret == 0 || (ret < 0 && !stream->parent->eof)) break; stream->istream.stream_errno = stream->parent->stream_errno; stream->buffer = i_stream_get_data(stream->parent, &pos); if (stream->parent->eof) { /* Check return code at EOF */ if (stream->buffer != NULL && pos >= 2 && stream->buffer[pos - 1] == '\n') { switch (stream->buffer[pos - 2]) { case '+': scstream->client->exit_code = 1; break; case '-': scstream->client->exit_code = 0; break; default: scstream->client->exit_code = -1; } } else { scstream->client->exit_code = -1; } } if (stream->buffer != NULL && pos >= 1) { /* retain/hide potential return code at end of buffer */ size_t old_reserved = reserved; ssize_t reserve_mod; reserved = (stream->buffer[pos - 1] == '\n' && pos > 1 ? 2 : 1); reserve_mod = reserved - old_reserved; pos -= reserved; if (ret >= reserve_mod) { ret -= reserve_mod; } } if (ret <= 0 && stream->parent->eof) { /* Parent EOF and not more data to return; EOF here as well */ if (pos == 0) i_stream_skip(stream->parent, reserved); stream->istream.eof = TRUE; ret = -1; } } while (ret == 0); stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); return ret; } static void ATTR_NORETURN program_client_istream_sync(struct istream_private *stream ATTR_UNUSED) { i_panic("program_client_istream sync() not implemented"); } static int program_client_istream_stat(struct istream_private *stream, bool exact) { struct program_client_istream *scstream = (struct program_client_istream *) stream; const struct stat *st; int ret; /* Stat the original stream */ ret = i_stream_stat(stream->parent, exact, &st); if (ret < 0 || st->st_size == -1 || !exact) return ret; scstream->statbuf = *st; scstream->statbuf.st_size = -1; return ret; } static struct istream *program_client_istream_create(struct program_client *program_client, struct istream *input) { struct program_client_istream *scstream; scstream = i_new(struct program_client_istream, 1); scstream->client = program_client; scstream->istream.max_buffer_size = input->real_stream->max_buffer_size; scstream->istream.iostream.destroy = program_client_istream_destroy; scstream->istream.read = program_client_istream_read; scstream->istream.sync = program_client_istream_sync; scstream->istream.stat = program_client_istream_stat; scstream->istream.istream.readable_fd = FALSE; scstream->istream.istream.blocking = input->blocking; scstream->istream.istream.seekable = FALSE; i_stream_seek(input, 0); return i_stream_create(&scstream->istream, input, -1); } /* * Program client */ struct program_client_remote { struct program_client client; bool noreply:1; }; static void program_client_remote_connected(struct program_client *pclient) { struct program_client_remote *prclient = (struct program_client_remote *) pclient; const char **args = pclient->args; string_t *str; io_remove(&pclient->io); program_client_init_streams(pclient); if (!prclient->noreply) { struct istream *is = pclient->program_input; pclient->program_input = program_client_istream_create(pclient, pclient->program_input); i_stream_unref(&is); } str = t_str_new(1024); str_append(str, PROGRAM_CLIENT_VERSION_STRING); if (array_is_created(&pclient->envs)) { const char *const *env; array_foreach(&pclient->envs, env) { str_append(str, "env_"); str_append_tabescaped(str, *env); str_append_c(str, '\n'); } } if (prclient->noreply) str_append(str, "noreply\n"); else str_append(str, "-\n"); if (args != NULL) { for(; *args != NULL; args++) { str_append_tabescaped(str, *args); str_append_c(str, '\n'); } } str_append_c(str, '\n'); if (o_stream_send(pclient->program_output, str_data(str), str_len(str)) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(pclient->program_output), o_stream_get_error(pclient->program_output)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return; } (void)program_client_connected(pclient); } static int program_client_remote_connect(struct program_client *pclient) { struct program_client_remote *prclient = (struct program_client_remote *) pclient; int fd; if ((fd = net_connect_unix_with_retries(pclient->path, 1000)) < 0) { switch (errno) { case EACCES: i_error("%s", eacces_error_get("net_connect_unix", pclient->path)); return -1; default: i_error("net_connect_unix(%s) failed: %m", pclient->path); return -1; } } net_set_nonblock(fd, TRUE); pclient->fd_in = (prclient->noreply && pclient->output == NULL && !pclient->output_seekable ? -1 : fd); pclient->fd_out = fd; pclient->io = io_add(fd, IO_WRITE, program_client_remote_connected, pclient); return 0; } static int program_client_remote_close_output(struct program_client *pclient) { int fd_out = pclient->fd_out, fd_in = pclient->fd_in; pclient->fd_out = -1; /* Shutdown output; program stdin will get EOF */ if (fd_out >= 0) { if (fd_in >= 0) { if (shutdown(fd_out, SHUT_WR) < 0 && errno != ENOTCONN) { i_error("shutdown(%s, SHUT_WR) failed: %m", pclient->path); return -1; } } else if (close(fd_out) < 0) { i_error("close(%s) failed: %m", pclient->path); return -1; } } return 1; } static void program_client_remote_disconnect(struct program_client *pclient, bool force) { struct program_client_remote *prclient = (struct program_client_remote *)pclient; if (pclient->error == PROGRAM_CLIENT_ERROR_NONE && !prclient->noreply && pclient->program_input != NULL && !force) { const unsigned char *data; size_t size; /* Skip any remaining program output and parse the exit code */ while (i_stream_read_more (pclient->program_input, &data, &size) > 0) { i_stream_skip(pclient->program_input, size); } /* Get exit code */ if (!pclient->program_input->eof) pclient->exit_code = -1; } else { pclient->exit_code = 1; } program_client_disconnected(pclient); } static void program_client_remote_switch_ioloop(struct program_client *pclient ATTR_UNUSED) { } struct program_client * program_client_remote_create(const char *socket_path, const char *const *args, const struct program_client_settings *set, bool noreply) { struct program_client_remote *pclient; pool_t pool; pool = pool_alloconly_create("program client remote", 1024); pclient = p_new(pool, struct program_client_remote, 1); program_client_init(&pclient->client, pool, socket_path, args, set); pclient->client.connect = program_client_remote_connect; pclient->client.close_output = program_client_remote_close_output; pclient->client.disconnect = program_client_remote_disconnect; pclient->client.switch_ioloop = program_client_remote_switch_ioloop; pclient->noreply = noreply; return &pclient->client; } dovecot-2.2.33.2/src/lib-program-client/program-client-local.c0000644000175000017500000003136113165463624021000 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "env-util.h" #include "execv-const.h" #include "array.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "restrict-access.h" #include "child-wait.h" #include "time-util.h" #include "program-client-private.h" #include #include #include #include #include #include #include #define KILL_TIMEOUT 5000 struct program_client_local { struct program_client client; struct child_wait *child_wait; struct timeout *to_kill; pid_t pid; int status; bool exited:1; bool stopping:1; bool sent_term:1; }; static void program_client_local_waitchild(const struct child_wait_status *, struct program_client_local *); static void program_client_local_disconnect(struct program_client *pclient, bool force); static void program_client_local_exited(struct program_client_local *plclient); static void exec_child(const char *bin_path, const char *const *args, ARRAY_TYPE(const_string) *envs, int in_fd, int out_fd, int *extra_fds, bool drop_stderr) { ARRAY_TYPE(const_string) exec_args; /* Setup stdin/stdout */ if (in_fd < 0) in_fd = dev_null_fd; if (out_fd < 0) out_fd = dev_null_fd; if (in_fd != STDIN_FILENO && dup2(in_fd, STDIN_FILENO) < 0) i_fatal("dup2(stdin) failed: %m"); if (out_fd != STDOUT_FILENO && dup2(out_fd, STDOUT_FILENO) < 0) i_fatal("dup2(stdout) failed: %m"); if (in_fd != STDIN_FILENO && in_fd != dev_null_fd && close(in_fd) < 0) i_error("close(in_fd) failed: %m"); if (out_fd != STDOUT_FILENO && out_fd != dev_null_fd && (out_fd != in_fd) && close(out_fd) < 0) i_error("close(out_fd) failed: %m"); /* Drop stderr if requested */ if (drop_stderr) { if (dup2(dev_null_fd, STDERR_FILENO) < 0) i_fatal("dup2(stderr) failed: %m"); } /* Setup extra fds */ if (extra_fds != NULL) { int *efd; for(efd = extra_fds; *efd != -1; efd += 2) { i_assert(efd[1] != STDIN_FILENO); i_assert(efd[1] != STDOUT_FILENO); i_assert(efd[1] != STDERR_FILENO); if (efd[0] != efd[1]) { if (dup2(efd[0], efd[1]) < 0) i_fatal("dup2(extra_fd=%d) failed: %m", efd[1]); } } for(efd = extra_fds; *efd != -1; efd += 2) { if (efd[0] != efd[1] && efd[0] != STDIN_FILENO && efd[0] != STDOUT_FILENO && efd[0] != STDERR_FILENO) { if (close(efd[0]) < 0) i_error("close(extra_fd=%d) failed: %m", efd[1]); } } } /* Compose argv */ t_array_init(&exec_args, 16); array_append(&exec_args, &bin_path, 1); if (args != NULL) { for(; *args != NULL; args++) array_append(&exec_args, args, 1); } (void) array_append_space(&exec_args); /* Setup environment */ env_clean(); if (array_is_created(envs)) { const char *const *env; array_foreach(envs, env) { env_put(*env); } } /* Execute */ args = array_idx(&exec_args, 0); execvp_const(args[0], args); } static void program_client_local_waitchild(const struct child_wait_status *status, struct program_client_local *plclient) { i_assert(plclient->pid == status->pid); plclient->status = status->status; plclient->exited = TRUE; plclient->pid = -1; if (plclient->stopping) program_client_local_exited(plclient); else program_client_program_input(&plclient->client); } static int program_client_local_connect(struct program_client *pclient) { struct program_client_local *plclient = (struct program_client_local *)pclient; int fd_in[2] = { -1, -1 }, fd_out[2] = {-1, -1}; struct program_client_extra_fd *efds = NULL; int *parent_extra_fds = NULL, *child_extra_fds = NULL; unsigned int xfd_count = 0, i; /* create normal I/O fds */ if (pclient->input != NULL) { if (pipe(fd_in) < 0) { i_error("pipe(in) failed: %m"); return -1; } } if (pclient->output != NULL || pclient->output_seekable) { if (pipe(fd_out) < 0) { i_error("pipe(out) failed: %m"); return -1; } } /* create pipes for additional output through side-channel fds */ if (array_is_created(&pclient->extra_fds)) { int extra_fd[2]; efds = array_get_modifiable(&pclient->extra_fds, &xfd_count); if (xfd_count > 0) { i_assert(xfd_count < INT_MAX); parent_extra_fds = t_new(int, xfd_count); child_extra_fds = t_new(int, xfd_count * 2 + 1); for(i = 0; i < xfd_count; i++) { if (pipe(extra_fd) < 0) { i_error("pipe(extra=%d) failed: %m", extra_fd[1]); return -1; } parent_extra_fds[i] = extra_fd[0]; child_extra_fds[i * 2 + 0] = extra_fd[1]; child_extra_fds[i * 2 + 1] = efds[i].child_fd; } child_extra_fds[xfd_count * 2] = -1; } } /* fork child */ if ((plclient->pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); /* clean up */ if (fd_in[0] >= 0 && close(fd_in[0]) < 0) { i_error("close(pipe:in:rd) failed: %m"); } if (fd_in[1] >= 0 && close(fd_in[1]) < 0) { i_error("close(pipe:in:wr) failed: %m"); } if (fd_out[0] >= 0 && close(fd_out[0]) < 0) { i_error("close(pipe:out:rd) failed: %m"); } if (fd_out[1] >= 0 && close(fd_out[1]) < 0) { i_error("close(pipe:out:wr) failed: %m"); } for(i = 0; i < xfd_count; i++) { if (close(child_extra_fds[i * 2]) < 0) { i_error("close(pipe:extra=%d:wr) failed: %m", child_extra_fds[i * 2 + 1]); } if (close(parent_extra_fds[i]) < 0) { i_error("close(pipe:extra=%d:rd) failed: %m", child_extra_fds[i * 2 + 1]); } } return -1; } if (plclient->pid == 0) { /* child */ if (fd_in[1] >= 0 && close(fd_in[1]) < 0) i_error("close(pipe:in:wr) failed: %m"); if (fd_out[0] >= 0 && close(fd_out[0]) < 0) i_error("close(pipe:out:rd) failed: %m"); for(i = 0; i < xfd_count; i++) { if (close(parent_extra_fds[i]) < 0) { i_error("close(pipe:extra=%d:rd) failed: %m", child_extra_fds[i * 2 + 1]); } } /* if we want to allow root, then we will not drop root privileges */ pclient->set.restrict_set.drop_setuid_root = !pclient->set.allow_root; restrict_access(&pclient->set.restrict_set, pclient->set.home, !pclient->set.allow_root); exec_child(pclient->path, pclient->args, &pclient->envs, fd_in[0], fd_out[1], child_extra_fds, pclient->set.drop_stderr); i_unreached(); } /* parent */ if (fd_in[0] >= 0 && close(fd_in[0]) < 0) i_error("close(pipe:in:rd) failed: %m"); if (fd_out[1] >= 0 && close(fd_out[1]) < 0) i_error("close(pipe:out:wr) failed: %m"); if (fd_in[1] >= 0) { net_set_nonblock(fd_in[1], TRUE); pclient->fd_out = fd_in[1]; } if (fd_out[0] >= 0) { net_set_nonblock(fd_out[0], TRUE); pclient->fd_in = fd_out[0]; } for(i = 0; i < xfd_count; i++) { if (close(child_extra_fds[i * 2]) < 0) { i_error("close(pipe:extra=%d:wr) failed: %m", child_extra_fds[i * 2 + 1]); } net_set_nonblock(parent_extra_fds[i], TRUE); efds[i].parent_fd = parent_extra_fds[i]; } program_client_init_streams(pclient); plclient->child_wait = child_wait_new_with_pid(plclient->pid, program_client_local_waitchild, plclient); return program_client_connected(pclient); } static int program_client_local_close_output(struct program_client *pclient) { int fd_out = pclient->fd_out; pclient->fd_out = -1; /* Shutdown output; program stdin will get EOF */ if (fd_out >= 0 && close(fd_out) < 0) { i_error("close(%s) failed: %m", pclient->path); return -1; } return 1; } static void program_client_local_exited(struct program_client_local *plclient) { if (plclient->to_kill != NULL) timeout_remove(&plclient->to_kill); if (plclient->child_wait != NULL) child_wait_free(&plclient->child_wait); struct program_client *pclient = &plclient->client; plclient->exited = TRUE; plclient->pid = -1; /* Evaluate child exit status */ pclient->exit_code = -1; if (WIFEXITED(plclient->status)) { /* Exited */ int exit_code = WEXITSTATUS(plclient->status); if (exit_code != 0) { i_info("program `%s' terminated with non-zero exit code %d", pclient->path, exit_code); pclient->exit_code = 0; } else { pclient->exit_code = 1; } } else if (WIFSIGNALED(plclient->status)) { /* Killed with a signal */ if (plclient->sent_term) { i_error("program `%s' was forcibly terminated with signal %d", pclient->path, WTERMSIG(plclient->status)); } else { i_error("program `%s' terminated abnormally, signal %d", pclient->path, WTERMSIG(plclient->status)); } } else if (WIFSTOPPED(plclient->status)) { /* Stopped */ i_error("program `%s' stopped, signal %d", pclient->path, WSTOPSIG(plclient->status)); } else { /* Something else */ i_error("program `%s' terminated abnormally, return status %d", pclient->path, plclient->status); } program_client_disconnected(pclient); } static void program_client_local_kill(struct program_client_local *plclient) { /* time to die */ if (plclient->to_kill != NULL) timeout_remove(&plclient->to_kill); i_assert(plclient->pid != (pid_t)-1); if (plclient->client.error == PROGRAM_CLIENT_ERROR_NONE) plclient->client.error = PROGRAM_CLIENT_ERROR_RUN_TIMEOUT; if (plclient->sent_term) { /* no need for this anymore */ child_wait_free(&plclient->child_wait); /* Timed out again */ if (plclient->client.debug) { i_debug("program `%s' (%d) did not die after %d milliseconds: " "sending KILL signal", plclient->client.path, plclient->pid, KILL_TIMEOUT); } /* Kill it brutally now, it should die right away */ if (kill(plclient->pid, SIGKILL) < 0) { i_error("failed to send SIGKILL signal to program `%s'", plclient->client.path); } else if (waitpid(plclient->pid, &plclient->status, 0) < 0) { i_error("waitpid(%s) failed: %m", plclient->client.path); } program_client_local_exited(plclient); return; } if (plclient->client.debug) i_debug("program `%s'(%d) execution timed out after %u milliseconds: " "sending TERM signal", plclient->client.path, plclient->pid, plclient->client.set.input_idle_timeout_msecs); /* send sigterm, keep on waiting */ plclient->sent_term = TRUE; /* Kill child gently first */ if (kill(plclient->pid, SIGTERM) < 0) { i_error("failed to send SIGTERM signal to program `%s'", plclient->client.path); (void)kill(plclient->pid, SIGKILL); program_client_local_exited(plclient); return; } i_assert(plclient->child_wait != NULL); plclient->to_kill = timeout_add_short(KILL_TIMEOUT, program_client_local_kill, plclient); } static void program_client_local_disconnect(struct program_client *pclient, bool force) { struct program_client_local *plclient = (struct program_client_local *) pclient; pid_t pid = plclient->pid; unsigned long runtime, timeout = 0; if (plclient->exited) { program_client_local_exited(plclient); return; } if (plclient->stopping) return; plclient->stopping = TRUE; if (pid < 0) { /* program never started */ pclient->exit_code = 0; program_client_local_exited(plclient); return; } /* make sure it hasn't already been reaped */ if (waitpid(plclient->pid, &plclient->status, WNOHANG) > 0) { program_client_local_exited(plclient); return; } /* Calculate timeout */ runtime = timeval_diff_msecs(&ioloop_timeval, &pclient->start_time); if (!force && pclient->set.input_idle_timeout_msecs > 0 && runtime < pclient->set.input_idle_timeout_msecs) timeout = pclient->set.input_idle_timeout_msecs - runtime; if (pclient->debug) { i_debug("waiting for program `%s' to finish after %lu msecs", pclient->path, runtime); } force = force || (timeout == 0 && pclient->set.input_idle_timeout_msecs > 0); if (!force) { if (timeout > 0) plclient->to_kill = timeout_add_short(timeout, program_client_local_kill, plclient); } else { program_client_local_kill(plclient); } } static void program_client_local_destroy(struct program_client *pclient ATTR_UNUSED) { child_wait_deinit(); } static void program_client_local_switch_ioloop(struct program_client *pclient) { struct program_client_local *plclient = (struct program_client_local *)pclient; if (plclient->to_kill != NULL) plclient->to_kill = io_loop_move_timeout(&plclient->to_kill); lib_signals_reset_ioloop(); } struct program_client * program_client_local_create(const char *bin_path, const char *const *args, const struct program_client_settings *set) { struct program_client_local *pclient; pool_t pool; pool = pool_alloconly_create("program client local", 1024); pclient = p_new(pool, struct program_client_local, 1); program_client_init(&pclient->client, pool, bin_path, args, set); pclient->client.connect = program_client_local_connect; pclient->client.close_output = program_client_local_close_output; pclient->client.switch_ioloop = program_client_local_switch_ioloop; pclient->client.disconnect = program_client_local_disconnect; pclient->client.destroy = program_client_local_destroy; pclient->pid = -1; child_wait_init(); return &pclient->client; } dovecot-2.2.33.2/src/lib-program-client/test-program-client-local.c0000644000175000017500000001062613165463624021756 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-lib.h" #include "buffer.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "lib-signals.h" #include "program-client.h" static const char *pclient_test_io_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" "Praesent vehicula ac leo vel placerat. Nullam placerat \n" "volutpat leo, sed ultricies felis pulvinar quis. Nam \n" "tempus, augue ut tempor cursus, neque felis commodo lacus, \n" "sit amet tincidunt arcu justo vel augue. Proin dapibus \n" "vulputate maximus. Mauris congue lacus felis, sed varius \n" "leo finibus sagittis. Cum sociis natoque penatibus et magnis \n" "dis parturient montes, nascetur ridiculus mus. Aliquam \n" "laoreet arcu a hendrerit consequat. Duis vitae erat tellus."; static struct program_client_settings pc_set = { .client_connect_timeout_msecs = 5000, .input_idle_timeout_msecs = 1000, .debug = TRUE, .restrict_set = { .uid = (uid_t)-1, .gid = (gid_t)-1, }, /* we need to permit root when running make check as root */ .allow_root = TRUE, }; static void test_program_success(void) { test_begin("test_program_success"); const char *const args[] = { "hello", "world", NULL }; struct program_client *pc = program_client_local_create("/bin/echo", args, &pc_set); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = o_stream_create_buffer(output); program_client_set_output(pc, os); test_assert(program_client_run(pc) == 1); test_assert(strcmp(str_c(output), "hello world\n") == 0); program_client_destroy(&pc); o_stream_unref(&os); buffer_free(&output); test_end(); } static void test_program_io_sync(void) { test_begin("test_program_io (sync)"); const char *const args[] = { NULL }; struct program_client *pc = program_client_local_create("/bin/cat", args, &pc_set); struct istream *is = test_istream_create(pclient_test_io_string); program_client_set_input(pc, is); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = o_stream_create_buffer(output); program_client_set_output(pc, os); test_assert(program_client_run(pc) == 1); test_assert(strcmp(str_c(output), pclient_test_io_string) == 0); program_client_destroy(&pc); i_stream_unref(&is); o_stream_unref(&os); buffer_free(&output); test_end(); } static void test_program_io_async_callback(int result, int *ret) { *ret = result; test_assert(result == 1); io_loop_stop(current_ioloop); } static void test_program_io_async(void) { test_begin("test_program_io (async)"); int ret = -2; struct ioloop *prev_ioloop = current_ioloop; struct ioloop *ioloop = io_loop_create(); const char *const args[] = { NULL }; struct program_client *pc = program_client_local_create("/bin/cat", args, &pc_set); lib_signals_reset_ioloop(); struct istream *is = test_istream_create(pclient_test_io_string); program_client_set_input(pc, is); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = o_stream_create_buffer(output); program_client_set_output(pc, os); program_client_run_async(pc, test_program_io_async_callback, &ret); if (ret == -2) io_loop_run(ioloop); test_assert(strcmp(str_c(output), pclient_test_io_string) == 0); program_client_destroy(&pc); i_stream_unref(&is); o_stream_unref(&os); buffer_free(&output); io_loop_set_current(prev_ioloop); lib_signals_reset_ioloop(); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); test_end(); } static void test_program_failure(void) { test_begin("test_program_failure"); const char *const args[] = { NULL }; struct program_client *pc = program_client_local_create("/bin/false", args, &pc_set); buffer_t *output = buffer_create_dynamic(default_pool, 16); struct ostream *os = o_stream_create_buffer(output); program_client_set_output(pc, os); test_assert(program_client_run(pc) == 0); test_assert(strcmp(str_c(output), "") == 0); program_client_destroy(&pc); o_stream_unref(&os); buffer_free(&output); test_end(); } int main(void) { int ret; void (*tests[])(void) = { test_program_success, test_program_io_sync, test_program_io_async, test_program_failure, NULL }; struct ioloop *ioloop = io_loop_create(); lib_signals_init(); ret = test_run(tests); lib_signals_deinit(); io_loop_destroy(&ioloop); return ret; } dovecot-2.2.33.2/src/lib-program-client/program-client.c0000644000175000017500000004123413165463624017710 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "safe-mkstemp.h" #include "istream-private.h" #include "istream-seekable.h" #include "ostream.h" #include "lib-signals.h" #include "program-client-private.h" #include #define MAX_OUTPUT_BUFFER_SIZE 16384 #define MAX_OUTPUT_MEMORY_BUFFER (1024*128) static void program_client_callback(struct program_client *pclient, int result, void *context) { /* do not call callback when destroying */ if (pclient->destroying) return; program_client_callback_t *callback = pclient->callback; i_assert(pclient->callback != NULL); callback(result, context); } static int program_client_seekable_fd_callback(const char **path_r, void *context) { struct program_client *pclient = (struct program_client *)context; string_t *path; int fd; path = t_str_new(128); str_append(path, pclient->temp_prefix); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static void program_client_timeout(struct program_client *pclient) { i_error("program `%s' execution timed out (> %u msecs)", pclient->path, pclient->set.input_idle_timeout_msecs); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_RUN_TIMEOUT); } static void program_client_connect_timeout(struct program_client *pclient) { i_error("program `%s' socket connection timed out (> %u msecs)", pclient->path, pclient->set.client_connect_timeout_msecs); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT); } static int program_client_connect(struct program_client *pclient) { if (pclient->set.client_connect_timeout_msecs != 0) { pclient->to = timeout_add(pclient->set.client_connect_timeout_msecs, program_client_connect_timeout, pclient); } return pclient->connect(pclient); } static int program_client_close_output(struct program_client *pclient) { int ret; if (pclient->program_output != NULL) o_stream_destroy(&pclient->program_output); if ((ret = pclient->close_output(pclient)) < 0) return -1; pclient->program_output = NULL; return ret; } static void program_client_disconnect_extra_fds(struct program_client *pclient) { struct program_client_extra_fd *efds; unsigned int i, count; if (!array_is_created(&pclient->extra_fds)) return; efds = array_get_modifiable(&pclient->extra_fds, &count); for(i = 0; i < count; i++) { if (efds[i].input != NULL) i_stream_unref(&efds[i].input); if (efds[i].io != NULL) io_remove(&efds[i].io); if (efds[i].parent_fd != -1 && close(efds[i].parent_fd) < 0) i_error("close(fd=%d) failed: %m", efds[i].parent_fd); } } void program_client_disconnected(struct program_client *pclient) { if (pclient->program_input != NULL) { if (pclient->output_seekable) i_stream_unref(&pclient->program_input); else i_stream_destroy(&pclient->program_input); } if (pclient->program_output != NULL) o_stream_destroy(&pclient->program_output); if (pclient->io != NULL) io_remove(&pclient->io); if (pclient->fd_in != -1 && close(pclient->fd_in) < 0) i_error("close(%s) failed: %m", pclient->path); if (pclient->fd_out != -1 && pclient->fd_out != pclient->fd_in && close(pclient->fd_out) < 0) i_error("close(%s/out) failed: %m", pclient->path); pclient->fd_in = pclient->fd_out = -1; pclient->disconnected = TRUE; if (pclient->other_error && pclient->error == PROGRAM_CLIENT_ERROR_NONE) { pclient->error = PROGRAM_CLIENT_ERROR_OTHER; } program_client_callback(pclient, pclient->error != PROGRAM_CLIENT_ERROR_NONE ? -1 : pclient->exit_code, pclient->context); } static void program_client_disconnect(struct program_client *pclient, bool force) { int ret; if (pclient->disconnected) return; pclient->disconnected = TRUE; if (pclient->to != NULL) timeout_remove(&pclient->to); if (pclient->io != NULL) io_remove(&pclient->io); if ((ret = program_client_close_output(pclient)) < 0) pclient->other_error = TRUE; program_client_disconnect_extra_fds(pclient); pclient->disconnect(pclient, force); } void program_client_fail(struct program_client *pclient, enum program_client_error error) { if (pclient->error != PROGRAM_CLIENT_ERROR_NONE) return; pclient->error = error; program_client_disconnect(pclient, TRUE); } static bool program_client_input_pending(struct program_client *pclient) { struct program_client_extra_fd *efds = NULL; unsigned int count, i; if (pclient->program_input != NULL && !pclient->program_input->closed && !i_stream_is_eof(pclient->program_input)) { return TRUE; } if (array_is_created(&pclient->extra_fds)) { efds = array_get_modifiable(&pclient->extra_fds, &count); for(i = 0; i < count; i++) { if (efds[i].input != NULL && !efds[i].input->closed && !i_stream_is_eof(efds[i].input)) { return TRUE; } } } return FALSE; } static int program_client_program_output(struct program_client *pclient) { struct istream *input = pclient->input; struct ostream *output = pclient->program_output; const unsigned char *data; size_t size; int ret = 0; if ((ret = o_stream_flush(output)) <= 0) { if (ret < 0) { i_error("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); } return ret; } if (input != NULL && output != NULL) { do { while ((data=i_stream_get_data(input, &size)) != NULL) { ssize_t sent; if ((sent=o_stream_send(output, data, size)) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return -1; } if (sent == 0) return 0; i_stream_skip(input, sent); } } while ((ret = i_stream_read(input)) > 0); if (ret == 0) return 1; if (ret < 0) { if (input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return -1; } else if (!i_stream_have_bytes_left(input)) { i_stream_unref(&pclient->input); input = NULL; if ((ret = o_stream_flush(output)) <= 0) { if (ret < 0) { i_error("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); } return ret; } } } } if (input == NULL) { if (!program_client_input_pending(pclient)) { program_client_disconnect(pclient, FALSE); } else if (program_client_close_output(pclient) < 0) { program_client_fail(pclient, PROGRAM_CLIENT_ERROR_OTHER); } } return 1; } void program_client_program_input(struct program_client *pclient) { struct istream *input = pclient->program_input; struct ostream *output = pclient->output; const unsigned char *data; size_t size; int ret = 0; if (pclient->output_seekable && pclient->seekable_output == NULL) { struct istream *input_list[2] = { input, NULL }; input = i_stream_create_seekable(input_list, MAX_OUTPUT_MEMORY_BUFFER, program_client_seekable_fd_callback, pclient); i_stream_unref(&pclient->program_input); pclient->program_input = input; pclient->seekable_output = input; i_stream_ref(pclient->seekable_output); } if (input != NULL) { while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { if (output != NULL) { ssize_t sent; if ((sent=o_stream_send(output, data, size)) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return; } if (sent == 0) return; size = (size_t)sent; } i_stream_skip(input, size); } if (ret < 0) { if (input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return; } else { if (!program_client_input_pending(pclient)) { program_client_disconnect(pclient, FALSE); return; } } } if (program_client_input_pending(pclient)) return; if (pclient->program_input != NULL && !input->eof) { program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO); return; } } program_client_disconnect(pclient, FALSE); } static void program_client_extra_fd_input(struct program_client_extra_fd *efd) { struct program_client *pclient = efd->pclient; i_assert(efd->callback != NULL); efd->callback(efd->context, efd->input); if (efd->input->closed || i_stream_is_eof(efd->input)) { if (!program_client_input_pending(pclient)) program_client_disconnect(pclient, FALSE); } } int program_client_connected(struct program_client *pclient) { int ret = 1; pclient->start_time = ioloop_timeval; if (pclient->to != NULL) timeout_remove(&pclient->to); if (pclient->set.input_idle_timeout_msecs != 0) { pclient->to = timeout_add(pclient->set.input_idle_timeout_msecs, program_client_timeout, pclient); } /* run output */ if (pclient->program_output != NULL && (ret = program_client_program_output(pclient)) == 0) { if (pclient->program_output != NULL) { o_stream_set_flush_callback(pclient->program_output, program_client_program_output, pclient); } } return ret; } void program_client_init(struct program_client *pclient, pool_t pool, const char *path, const char *const *args, const struct program_client_settings *set) { pclient->pool = pool; pclient->path = p_strdup(pool, path); if (args != NULL) pclient->args = p_strarray_dup(pool, args); pclient->set = *set; pclient->debug = set->debug; pclient->fd_in = -1; pclient->fd_out = -1; } void program_client_set_input(struct program_client *pclient, struct istream *input) { if (pclient->input != NULL) i_stream_unref(&pclient->input); if (input != NULL) i_stream_ref(input); pclient->input = input; } void program_client_set_output(struct program_client *pclient, struct ostream *output) { if (pclient->output != NULL) o_stream_unref(&pclient->output); if (output != NULL) o_stream_ref(output); pclient->output = output; pclient->output_seekable = FALSE; i_free(pclient->temp_prefix); } void program_client_set_output_seekable(struct program_client *pclient, const char *temp_prefix) { if (pclient->output != NULL) o_stream_unref(&pclient->output); pclient->temp_prefix = i_strdup(temp_prefix); pclient->output_seekable = TRUE; } struct istream *program_client_get_output_seekable(struct program_client *pclient) { struct istream *input = pclient->seekable_output; pclient->seekable_output = NULL; i_stream_seek(input, 0); return input; } #undef program_client_set_extra_fd void program_client_set_extra_fd(struct program_client *pclient, int fd, program_client_fd_callback_t *callback, void *context) { struct program_client_extra_fd *efds; struct program_client_extra_fd *efd = NULL; unsigned int i, count; i_assert(fd > 1); if (!array_is_created(&pclient->extra_fds)) p_array_init(&pclient->extra_fds, pclient->pool, 2); efds = array_get_modifiable(&pclient->extra_fds, &count); for(i = 0; i < count; i++) { if (efds[i].child_fd == fd) { efd = &efds[i]; break; } } if (efd == NULL) { efd = array_append_space(&pclient->extra_fds); efd->pclient = pclient; efd->child_fd = fd; efd->parent_fd = -1; } efd->callback = callback; efd->context = context; } void program_client_set_env(struct program_client *pclient, const char *name, const char *value) { const char *env; if (!array_is_created(&pclient->envs)) p_array_init(&pclient->envs, pclient->pool, 16); env = p_strdup_printf(pclient->pool, "%s=%s", name, value); array_append(&pclient->envs, &env, 1); } void program_client_init_streams(struct program_client *pclient) { /* Create streams for normal program I/O */ if (pclient->fd_out >= 0) { pclient->program_output = o_stream_create_fd(pclient->fd_out, MAX_OUTPUT_BUFFER_SIZE, FALSE); o_stream_set_name(pclient->program_output, "program stdin"); } if (pclient->fd_in >= 0) { struct istream *input; input = i_stream_create_fd(pclient->fd_in, (size_t)-1, FALSE); pclient->program_input = input; i_stream_set_name(pclient->program_input, "program stdout"); pclient->io = io_add(pclient->fd_in, IO_READ, program_client_program_input, pclient); } /* Create streams for additional output through side-channel fds */ if (array_is_created(&pclient->extra_fds)) { struct program_client_extra_fd *efds = NULL; unsigned int count, i; efds = array_get_modifiable(&pclient->extra_fds, &count); for(i = 0; i < count; i++) { i_assert(efds[i].parent_fd >= 0); efds[i].input = i_stream_create_fd (efds[i].parent_fd, (size_t)-1, FALSE); i_stream_set_name(efds[i].input, t_strdup_printf("program output fd=%d", efds[i].child_fd)); efds[i].io = io_add(efds[i].parent_fd, IO_READ, program_client_extra_fd_input, &efds[i]); } } } void program_client_destroy(struct program_client **_pclient) { struct program_client *pclient = *_pclient; *_pclient = NULL; pclient->destroying = TRUE; pclient->callback = NULL; program_client_disconnect(pclient, TRUE); i_assert(pclient->callback == NULL); if (pclient->input != NULL) i_stream_unref(&pclient->input); if (pclient->program_input != NULL) i_stream_unref(&pclient->program_input); if (pclient->program_output != NULL) o_stream_unref(&pclient->program_output); if (pclient->output != NULL) o_stream_unref(&pclient->output); if (pclient->seekable_output != NULL) i_stream_unref(&pclient->seekable_output); if (pclient->io != NULL) io_remove(&pclient->io); i_free(pclient->temp_prefix); if (pclient->destroy != NULL) pclient->destroy(pclient); pool_unref(&pclient->pool); } void program_client_switch_ioloop(struct program_client *pclient) { if (pclient->input != NULL) i_stream_switch_ioloop(pclient->input); if (pclient->program_input != NULL) i_stream_switch_ioloop(pclient->program_input); if (pclient->seekable_output != NULL) i_stream_switch_ioloop(pclient->seekable_output); if (pclient->output != NULL) o_stream_switch_ioloop(pclient->output); if (pclient->program_output != NULL) o_stream_switch_ioloop(pclient->program_output); if (pclient->to != NULL) pclient->to = io_loop_move_timeout(&pclient->to); if (pclient->io != NULL) pclient->io = io_loop_move_io(&pclient->io); pclient->switch_ioloop(pclient); } int program_client_create(const char *uri, const char *const *args, const struct program_client_settings *set, bool noreply, struct program_client **pc_r, const char **error_r) { if (strncmp(uri, "exec:", 5) == 0) { *pc_r = program_client_local_create( uri+5, args, set); return 0; } else if (strncmp(uri, "unix:", 5) == 0) { *pc_r = program_client_remote_create( uri+5, args, set, noreply); return 0; } else { *error_r = t_strdup_printf( "Unsupported program client scheme '%s'", t_strcut(uri, ':')); return -1; } } static void program_client_run_callback(int result, int *context) { *context = result; io_loop_stop(current_ioloop); } int program_client_run(struct program_client *pclient) { int ret = -2; struct ioloop *prev_ioloop = current_ioloop; struct ioloop *ioloop = io_loop_create(); program_client_switch_ioloop(pclient); program_client_run_async(pclient, program_client_run_callback, &ret); if (ret == -2) { io_loop_run(ioloop); } io_loop_set_current(prev_ioloop); program_client_switch_ioloop(pclient); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); if (pclient->error != PROGRAM_CLIENT_ERROR_NONE) return -1; return pclient->exit_code; } #undef program_client_run_async void program_client_run_async(struct program_client *pclient, program_client_callback_t *callback, void *context) { int ret; i_assert(callback != NULL); pclient->disconnected = FALSE; pclient->exit_code = 1; pclient->error = PROGRAM_CLIENT_ERROR_NONE; pclient->callback = callback; pclient->context = context; if ((ret = program_client_connect(pclient)) >= 0) { /* run output */ if (ret > 0 && pclient->program_output != NULL && (ret = o_stream_flush(pclient->program_output)) == 0) { o_stream_set_flush_callback (pclient->program_output, program_client_program_output, pclient); } if (ret < 0) { i_error("write(%s) failed: %s", o_stream_get_name(pclient->program_output), o_stream_get_error(pclient->program_output)); pclient->error = PROGRAM_CLIENT_ERROR_IO; program_client_callback(pclient, ret, context); return; } } } dovecot-2.2.33.2/src/lib-program-client/Makefile.am0000644000175000017500000000210113165463624016643 00000000000000noinst_LTLIBRARIES = libprogram_client.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libprogram_client_la_SOURCES = \ program-client.c \ program-client-local.c \ program-client-remote.c headers = \ program-client.h noinst_HEADERS = \ program-client-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-program-client-local \ test-program-client-remote noinst_PROGRAMS = $(test_programs) test_libs = \ libprogram_client.la \ ../lib-test/libtest.la \ ../lib/liblib.la \ $(MODULE_LIBS) test_program_client_local_SOURCE = test-program-client-local.c test_program_client_local_LDADD = $(test_libs) test_program_client_remote_SOURCE = test-program-client-remote.c test_program_client_remote_LDADD = $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if test "$$bin" = "test-program-client-local"; then \ if ! env NOVALGRIND=yes $(RUN_TEST) ./$$bin; then exit 1; fi; \ else \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ fi \ done dovecot-2.2.33.2/src/lib-otp/0002755000175000017500000000000013172375611012552 500000000000000dovecot-2.2.33.2/src/lib-otp/Makefile.in0000644000175000017500000004756713172375573014567 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-otp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libotp_a_AR = $(AR) $(ARFLAGS) libotp_a_LIBADD = am_libotp_a_OBJECTS = otp-dictionary.$(OBJEXT) otp-hash.$(OBJEXT) \ otp-parity.$(OBJEXT) otp-parse.$(OBJEXT) libotp_a_OBJECTS = $(am_libotp_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libotp_a_SOURCES) DIST_SOURCES = $(libotp_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libotp.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libotp_a_SOURCES = \ otp-dictionary.c \ otp-hash.c \ otp-parity.c \ otp-parse.c noinst_HEADERS = \ otp.h \ otp-dictionary.h \ otp-hash.h \ otp-parity.h \ otp-parse.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-otp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-otp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libotp.a: $(libotp_a_OBJECTS) $(libotp_a_DEPENDENCIES) $(EXTRA_libotp_a_DEPENDENCIES) $(AM_V_at)-rm -f libotp.a $(AM_V_AR)$(libotp_a_AR) libotp.a $(libotp_a_OBJECTS) $(libotp_a_LIBADD) $(AM_V_at)$(RANLIB) libotp.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otp-dictionary.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otp-hash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otp-parity.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otp-parse.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(HEADERS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-otp/otp-parse.h0000644000175000017500000000117413123174404014547 00000000000000#ifndef OTP_PARSE_H #define OTP_PARSE_H int otp_read_hex(const char *data, const char **endptr, unsigned char *hash); int otp_read_words(const char *data, const char **endptr, unsigned char *hash); int otp_read_new_params(const char *data, const char **endptr, struct otp_state *state); int otp_parse_response(const char *data, unsigned char *hash, bool hex); int otp_parse_init_response(const char *data, struct otp_state *new_state, unsigned char *hash, bool hex, const char **error); int otp_parse_dbentry(const char *text, struct otp_state *state); const char *otp_print_dbentry(const struct otp_state *state); #endif dovecot-2.2.33.2/src/lib-otp/otp-dictionary.h0000644000175000017500000000014213123174404015574 00000000000000#ifndef OTP_DICTIONARY_H #define OTP_DICTIONARY_H int otp_lookup_word(const char *word); #endif dovecot-2.2.33.2/src/lib-otp/otp.h0000644000175000017500000000055613123174404013442 00000000000000#ifndef OTP_H #define OTP_H #define OTP_MAX_SEED_LEN 16 #define OTP_MAX_WORD_LEN 4 #define OTP_WORDS_NUMBER 6 #define OTP_HASH_SIZE 8 struct otp_state { unsigned int algo; int seq; unsigned char hash[OTP_HASH_SIZE]; char seed[OTP_MAX_SEED_LEN + 1]; }; #include "otp-hash.h" #include "otp-dictionary.h" #include "otp-parity.h" #include "otp-parse.h" #endif dovecot-2.2.33.2/src/lib-otp/otp-parity.c0000644000175000017500000000175413123174404014744 00000000000000/* * OTP parity table. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "otp.h" const unsigned char parity_table[256] = { 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, }; dovecot-2.2.33.2/src/lib-otp/otp-hash.c0000644000175000017500000000716013123174404014354 00000000000000/* * OTP hash generaion. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "md4.h" #include "md5.h" #include "sha1.h" #include "otp.h" struct digest { const char *name; void (*init)(void *ctx); void (*update)(void *ctx, const void *data, const size_t size); void (*final)(void *ctx, void *res); void (*otp_final)(void *ctx, void *res); }; struct digest_context { const struct digest *digest; union { struct md4_context md4_ctx; struct md5_context md5_ctx; struct sha1_ctxt sha1_ctx; } ctx; }; static void md4_fold(struct md4_context *ctx, void *res) { uint32_t tmp[4], *p = res; md4_final(ctx, (unsigned char *) tmp); *p++ = tmp[0] ^ tmp[2]; *p = tmp[1] ^ tmp[3]; } static void md5_fold(struct md5_context *ctx, void *res) { uint32_t tmp[4], *p = res; md5_final(ctx, (unsigned char *) tmp); *p++ = tmp[0] ^ tmp[2]; *p = tmp[1] ^ tmp[3]; } /* * Sometimes I simply can't look at code generated by gcc. */ static inline uint32_t swab_uint32(uint32_t val) { #if defined(__GNUC__) && defined(__i386__) asm("xchgb %b0, %h0\n" "rorl $16, %0\n" "xchgb %b0, %h0\n" :"=q" (val) : "0" (val)); #else val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val >> 24) & 0xff); #endif return val; } static void sha1_fold(struct sha1_ctxt *ctx, void *res) { uint32_t tmp[5], *p = res; sha1_result(ctx, tmp); *p++ = swab_uint32(tmp[0] ^ tmp[2] ^ tmp[4]); *p = swab_uint32(tmp[1] ^ tmp[3]); } #define F_INIT(name) ((void (*)(void *)) (name)) #define F_UPDATE(name) ((void (*)(void *, const void *, size_t)) (name)) #define F_FINAL(name) ((void (*)(void *, void *)) (name)) #define F_FOLD(name) ((void (*)(void *, void *)) (name)) static const struct digest digests[] = { { "md4", F_INIT(md4_init), F_UPDATE(md4_update), F_FINAL(md4_final), F_FOLD(md4_fold) }, { "md5", F_INIT(md5_init), F_UPDATE(md5_update), F_FINAL(md5_final), F_FOLD(md5_fold) }, { "sha1", F_INIT(sha1_init), F_UPDATE(sha1_loop), F_FINAL(sha1_result), F_FOLD(sha1_fold) }, }; #undef F_INIT #undef F_UPDATE #undef F_FINAL #undef F_FOLD const char *digest_name(unsigned int algo) { i_assert(algo < N_ELEMENTS(digests)); return digests[algo].name; } int digest_find(const char *name) { unsigned int i; for (i = 0; i < N_ELEMENTS(digests); i++) if (strcmp(name, digests[i].name) == 0) return i; return -1; } void digest_init(struct digest_context *ctx, const unsigned int algo) { i_assert(algo < N_ELEMENTS(digests)); ctx->digest = digests + algo; ctx->digest->init(&ctx->ctx); } void digest_update(struct digest_context *ctx, const void *data, const size_t size) { ctx->digest->update(&ctx->ctx, data, size); } void digest_final(struct digest_context *ctx, unsigned char *result) { ctx->digest->final(&ctx->ctx, result); } void digest_otp_final(struct digest_context *ctx, unsigned char *result) { ctx->digest->otp_final(&ctx->ctx, result); } void otp_hash(unsigned int algo, const char *seed, const char *passphrase, unsigned int step, unsigned char *result) { struct digest_context ctx; digest_init(&ctx, algo); digest_update(&ctx, seed, strlen(seed)); digest_update(&ctx, passphrase, strlen(passphrase)); digest_otp_final(&ctx, result); while (step-- > 0) { digest_init(&ctx, algo); digest_update(&ctx, result, OTP_HASH_SIZE); digest_otp_final(&ctx, result); } } void otp_next_hash(unsigned int algo, const unsigned char *prev, unsigned char *result) { struct digest_context ctx; digest_init(&ctx, algo); digest_update(&ctx, prev, OTP_HASH_SIZE); digest_otp_final(&ctx, result); } dovecot-2.2.33.2/src/lib-otp/otp-hash.h0000644000175000017500000000133513123174404014357 00000000000000#ifndef OTP_HASH_H #define OTP_HASH_H struct digest_context; enum { OTP_HASH_MD4, OTP_HASH_MD5, OTP_HASH_SHA1, }; int digest_find(const char *name); void digest_init(struct digest_context *ctx, const unsigned int algo); void digest_update(struct digest_context *ctx, const void *data, const size_t size); void digest_final(struct digest_context *ctx, unsigned char *result); void digest_otp_final(struct digest_context *ctx, unsigned char *result); const char *digest_name(unsigned int algo); void otp_hash(unsigned int algo, const char *seed, const char *passphrase, unsigned int step, unsigned char *result); void otp_next_hash(unsigned int algo, const unsigned char *prev, unsigned char *result); #endif dovecot-2.2.33.2/src/lib-otp/otp-parse.c0000644000175000017500000001131613165463624014554 00000000000000/* * OTP extended response parser. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "buffer.h" #include "str.h" #include "strfuncs.h" #include "hex-binary.h" #include "otp.h" #include #define IS_LWS(c) ((c) == ' ' || (c) == '\t') static inline const char *otp_skip_lws(const char *data) { while (*data && IS_LWS(*data)) data++; return data; } static inline int otp_check_tail(const char *data) { data = otp_skip_lws(data); return *data != 0; } int otp_read_hex(const char *data, const char **endptr, unsigned char *hash) { string_t *str; buffer_t buf; unsigned int i = 0; if (data == NULL) return -1; str = t_str_new(18); buffer_create_from_data(&buf, hash, OTP_HASH_SIZE); while (*data) { char c = *data; if (i_isxdigit(c)) { str_append_c(str, c); if (++i == OTP_HASH_SIZE * 2) { data++; break; } } else if (!IS_LWS(c)) { *endptr = data; return -1; } data++; } *endptr = data; if (i < OTP_HASH_SIZE * 2) return -1; return hex_to_binary(str_c(str), &buf); } #define add_word() do { \ tmp = otp_lookup_word(str_c(word)); \ buffer_append(&buf, &tmp, sizeof(tmp)); \ count++; \ } while (0) int otp_read_words(const char *data, const char **endptr, unsigned char *hash) { bool space = FALSE; unsigned int len = 0, count = 0; unsigned int parity = 0, bits[OTP_WORDS_NUMBER], tmp; string_t *word; buffer_t buf; if (data == NULL) return -1; word = t_str_new(8); data = otp_skip_lws(data); buffer_create_from_data(&buf, bits, sizeof(bits)); for (; *data && (count < OTP_WORDS_NUMBER); data++) { char c = *data; if (space) { if (IS_LWS(c)) continue; else if (i_isalpha(c)) { str_append_c(word, c); space = FALSE; len = 1; continue; } } else { if (i_isalpha(c)) { if (++len > OTP_MAX_WORD_LEN) { count = 0; break; } str_append_c(word, c); continue; } else if (IS_LWS(c)) { add_word(); str_truncate(word, 0); space = TRUE; continue; } } break; } if ((str_len(word) > 0) && (count == OTP_WORDS_NUMBER - 1)) add_word(); if (endptr) *endptr = data; if (count < OTP_WORDS_NUMBER) return -1; hash[0] = bits[0] >> 3; hash[1] = ((bits[0] & 7) << 5) | (bits[1] >> 6); hash[2] = ((bits[1] & 0x3f) << 2) | (bits[2] >> 9); hash[3] = (bits[2] >> 1) & 0xff; hash[4] = ((bits[2] & 3) << 7) | (bits[3] >> 4); hash[5] = ((bits[3] & 15) << 4) | (bits[4] >> 7); hash[6] = ((bits[4] & 0x7f) << 1) | (bits[5] >> 10); hash[7] = (bits[5] >> 2) & 0xff; parity = bits[5] & 3; return otp_parity(hash) != parity; } int otp_read_new_params(const char *data, const char **endptr, struct otp_state *state) { const char *p, *s; unsigned int i = 0; int algo; s = p = data; while ((*p != 0) && !IS_LWS(*p)) p++; if (*p == 0) return -1; algo = digest_find(t_strdup_until(s, p++)); if (algo < 0) return -2; state->algo = algo; s = p; if (str_parse_int(s, &state->seq, &p) < 0 || !IS_LWS(*p)) return -3; p++; while (i_isalnum(*p) && (i < OTP_MAX_SEED_LEN)) state->seed[i++] = i_tolower(*p++); state->seed[i] = 0; *endptr = p; return 0; } int otp_parse_response(const char *data, unsigned char *hash, bool hex) { const char *end; int ret = hex ? otp_read_hex(data, &end, hash) : otp_read_words(data, &end, hash); if (ret < 0) return ret; return otp_check_tail(end); } int otp_parse_init_response(const char *data, struct otp_state *new_state, unsigned char *hash, bool hex, const char **error) { const char *end; int ret = hex ? otp_read_hex(data, &end, hash) : otp_read_words(data, &end, hash); if (ret < 0) { *error = "invalid current OTP"; return ret; } end = otp_skip_lws(end); if (*end++ != ':') { *error = "missing colon"; return -1; } ret = otp_read_new_params(end, &end, new_state); if (ret < 0) { *error = "invalid OTP parameters"; return -1; } end = otp_skip_lws(end); if (*end++ != ':') { *error = "missing colon"; return -1; } ret = hex ? otp_read_hex(end, &end, new_state->hash) : otp_read_words(end, &end, new_state->hash); if (ret < 0) { *error = "invalid new OTP"; return -1; } if (otp_check_tail(end) != 0) { *error = "trailing garbage found"; return -1; } return 0; } int otp_parse_dbentry(const char *text, struct otp_state *state) { const char *end; int ret; ret = otp_read_new_params(text, &end, state); if (ret != 0) return ret; if (*end++ != ' ') return -1; return otp_read_hex(end, &end, state->hash); } const char *otp_print_dbentry(const struct otp_state *state) { return t_strdup_printf("%s %d %s %s", digest_name(state->algo), state->seq, state->seed, binary_to_hex(state->hash, 8)); } dovecot-2.2.33.2/src/lib-otp/otp-parity.h0000644000175000017500000000043413123174404014743 00000000000000#ifndef OTP_PARITY_H #define OTP_PARITY_H extern const unsigned char parity_table[256]; static inline unsigned int otp_parity(unsigned char *data) { unsigned int i, parity = 0; for (i = 0; i < OTP_HASH_SIZE; i++) parity += parity_table[*data++]; return parity & 3; } #endif dovecot-2.2.33.2/src/lib-otp/otp-dictionary.c0000644000175000017500000011356613123174404015606 00000000000000/* * OTP standard dictionary. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "otp.h" #include struct hint { const short l, u; }; static const struct hint hints[] = { { 0, 114 }, /* A */ { 114, 292 }, /* B */ { 292, 415 }, /* C */ { 415, 528 }, /* D */ { 528, 575 }, /* E */ { 575, 687 }, /* F */ { 687, 792 }, /* G */ { 792, 922 }, /* H */ { 922, 958 }, /* I */ { 958, 1014 }, /* J */ { 1014, 1055 }, /* K */ { 1055, 1189 }, /* L */ { 1189, 1315 }, /* M */ { 1315, 1383 }, /* N */ { 1383, 1446 }, /* O */ { 1446, 1482 }, /* P */ { 1482, 1486 }, /* Q */ { 1486, 1597 }, /* R */ { 1597, 1777 }, /* S */ { 1777, 1900 }, /* T */ { 1900, 1911 }, /* U */ { 1911, 1937 }, /* V */ { 1937, 2028 }, /* W */ { 0, 0 }, /* X */ { 2028, 2048 }, /* Y */ }; struct word { const short value; const char word[4]; }; static const struct word dictionary[2048] = { { 0, "A" }, { 1, "ABE" }, { 571, "ABED" }, { 572, "ABEL" }, { 573, "ABET" }, { 574, "ABLE" }, { 575, "ABUT" }, { 2, "ACE" }, { 576, "ACHE" }, { 577, "ACID" }, { 578, "ACME" }, { 579, "ACRE" }, { 3, "ACT" }, { 580, "ACTA" }, { 581, "ACTS" }, { 4, "AD" }, { 5, "ADA" }, { 582, "ADAM" }, { 6, "ADD" }, { 583, "ADDS" }, { 584, "ADEN" }, { 585, "AFAR" }, { 586, "AFRO" }, { 587, "AGEE" }, { 7, "AGO" }, { 588, "AHEM" }, { 589, "AHOY" }, { 8, "AID" }, { 590, "AIDA" }, { 591, "AIDE" }, { 592, "AIDS" }, { 9, "AIM" }, { 10, "AIR" }, { 593, "AIRY" }, { 594, "AJAR" }, { 595, "AKIN" }, { 596, "ALAN" }, { 597, "ALEC" }, { 598, "ALGA" }, { 599, "ALIA" }, { 11, "ALL" }, { 600, "ALLY" }, { 601, "ALMA" }, { 602, "ALOE" }, { 12, "ALP" }, { 603, "ALSO" }, { 604, "ALTO" }, { 605, "ALUM" }, { 606, "ALVA" }, { 13, "AM" }, { 607, "AMEN" }, { 608, "AMES" }, { 609, "AMID" }, { 610, "AMMO" }, { 611, "AMOK" }, { 612, "AMOS" }, { 613, "AMRA" }, { 14, "AMY" }, { 15, "AN" }, { 16, "ANA" }, { 17, "AND" }, { 614, "ANDY" }, { 615, "ANEW" }, { 18, "ANN" }, { 616, "ANNA" }, { 617, "ANNE" }, { 19, "ANT" }, { 618, "ANTE" }, { 619, "ANTI" }, { 20, "ANY" }, { 21, "APE" }, { 22, "APS" }, { 23, "APT" }, { 620, "AQUA" }, { 621, "ARAB" }, { 24, "ARC" }, { 622, "ARCH" }, { 25, "ARE" }, { 623, "AREA" }, { 624, "ARGO" }, { 625, "ARID" }, { 26, "ARK" }, { 27, "ARM" }, { 626, "ARMY" }, { 28, "ART" }, { 627, "ARTS" }, { 628, "ARTY" }, { 29, "AS" }, { 30, "ASH" }, { 629, "ASIA" }, { 31, "ASK" }, { 630, "ASKS" }, { 32, "AT" }, { 33, "ATE" }, { 631, "ATOM" }, { 34, "AUG" }, { 35, "AUK" }, { 632, "AUNT" }, { 633, "AURA" }, { 634, "AUTO" }, { 36, "AVE" }, { 635, "AVER" }, { 636, "AVID" }, { 637, "AVIS" }, { 638, "AVON" }, { 639, "AVOW" }, { 640, "AWAY" }, { 37, "AWE" }, { 38, "AWK" }, { 39, "AWL" }, { 40, "AWN" }, { 641, "AWRY" }, { 41, "AX" }, { 42, "AYE" }, { 642, "BABE" }, { 643, "BABY" }, { 644, "BACH" }, { 645, "BACK" }, { 43, "BAD" }, { 646, "BADE" }, { 44, "BAG" }, { 45, "BAH" }, { 647, "BAIL" }, { 648, "BAIT" }, { 649, "BAKE" }, { 650, "BALD" }, { 651, "BALE" }, { 652, "BALI" }, { 653, "BALK" }, { 654, "BALL" }, { 655, "BALM" }, { 46, "BAM" }, { 47, "BAN" }, { 656, "BAND" }, { 657, "BANE" }, { 658, "BANG" }, { 659, "BANK" }, { 48, "BAR" }, { 660, "BARB" }, { 661, "BARD" }, { 662, "BARE" }, { 663, "BARK" }, { 664, "BARN" }, { 665, "BARR" }, { 666, "BASE" }, { 667, "BASH" }, { 668, "BASK" }, { 669, "BASS" }, { 49, "BAT" }, { 670, "BATE" }, { 671, "BATH" }, { 672, "BAWD" }, { 673, "BAWL" }, { 50, "BAY" }, { 51, "BE" }, { 674, "BEAD" }, { 675, "BEAK" }, { 676, "BEAM" }, { 677, "BEAN" }, { 678, "BEAR" }, { 679, "BEAT" }, { 680, "BEAU" }, { 681, "BECK" }, { 52, "BED" }, { 53, "BEE" }, { 682, "BEEF" }, { 683, "BEEN" }, { 684, "BEER" }, { 685, "BEET" }, { 54, "BEG" }, { 686, "BELA" }, { 687, "BELL" }, { 688, "BELT" }, { 55, "BEN" }, { 689, "BEND" }, { 690, "BENT" }, { 691, "BERG" }, { 692, "BERN" }, { 693, "BERT" }, { 694, "BESS" }, { 695, "BEST" }, { 56, "BET" }, { 696, "BETA" }, { 697, "BETH" }, { 57, "BEY" }, { 698, "BHOY" }, { 699, "BIAS" }, { 58, "BIB" }, { 59, "BID" }, { 700, "BIDE" }, { 701, "BIEN" }, { 60, "BIG" }, { 702, "BILE" }, { 703, "BILK" }, { 704, "BILL" }, { 61, "BIN" }, { 705, "BIND" }, { 706, "BING" }, { 707, "BIRD" }, { 62, "BIT" }, { 708, "BITE" }, { 709, "BITS" }, { 710, "BLAB" }, { 711, "BLAT" }, { 712, "BLED" }, { 713, "BLEW" }, { 714, "BLOB" }, { 715, "BLOC" }, { 716, "BLOT" }, { 717, "BLOW" }, { 718, "BLUE" }, { 719, "BLUM" }, { 720, "BLUR" }, { 721, "BOAR" }, { 722, "BOAT" }, { 63, "BOB" }, { 723, "BOCA" }, { 724, "BOCK" }, { 725, "BODE" }, { 726, "BODY" }, { 64, "BOG" }, { 727, "BOGY" }, { 728, "BOHR" }, { 729, "BOIL" }, { 730, "BOLD" }, { 731, "BOLO" }, { 732, "BOLT" }, { 733, "BOMB" }, { 65, "BON" }, { 734, "BONA" }, { 735, "BOND" }, { 736, "BONE" }, { 737, "BONG" }, { 738, "BONN" }, { 739, "BONY" }, { 66, "BOO" }, { 740, "BOOK" }, { 741, "BOOM" }, { 742, "BOON" }, { 743, "BOOT" }, { 67, "BOP" }, { 744, "BORE" }, { 745, "BORG" }, { 746, "BORN" }, { 747, "BOSE" }, { 748, "BOSS" }, { 749, "BOTH" }, { 750, "BOUT" }, { 68, "BOW" }, { 751, "BOWL" }, { 69, "BOY" }, { 752, "BOYD" }, { 753, "BRAD" }, { 754, "BRAE" }, { 755, "BRAG" }, { 756, "BRAN" }, { 757, "BRAY" }, { 758, "BRED" }, { 759, "BREW" }, { 760, "BRIG" }, { 761, "BRIM" }, { 762, "BROW" }, { 70, "BUB" }, { 763, "BUCK" }, { 71, "BUD" }, { 764, "BUDD" }, { 765, "BUFF" }, { 72, "BUG" }, { 766, "BULB" }, { 767, "BULK" }, { 768, "BULL" }, { 73, "BUM" }, { 74, "BUN" }, { 769, "BUNK" }, { 770, "BUNT" }, { 771, "BUOY" }, { 772, "BURG" }, { 773, "BURL" }, { 774, "BURN" }, { 775, "BURR" }, { 776, "BURT" }, { 777, "BURY" }, { 75, "BUS" }, { 778, "BUSH" }, { 779, "BUSS" }, { 780, "BUST" }, { 781, "BUSY" }, { 76, "BUT" }, { 77, "BUY" }, { 78, "BY" }, { 79, "BYE" }, { 782, "BYTE" }, { 80, "CAB" }, { 783, "CADY" }, { 784, "CAFE" }, { 785, "CAGE" }, { 786, "CAIN" }, { 787, "CAKE" }, { 81, "CAL" }, { 788, "CALF" }, { 789, "CALL" }, { 790, "CALM" }, { 82, "CAM" }, { 791, "CAME" }, { 83, "CAN" }, { 792, "CANE" }, { 793, "CANT" }, { 84, "CAP" }, { 85, "CAR" }, { 794, "CARD" }, { 795, "CARE" }, { 796, "CARL" }, { 797, "CARR" }, { 798, "CART" }, { 799, "CASE" }, { 800, "CASH" }, { 801, "CASK" }, { 802, "CAST" }, { 86, "CAT" }, { 803, "CAVE" }, { 87, "CAW" }, { 804, "CEIL" }, { 805, "CELL" }, { 806, "CENT" }, { 807, "CERN" }, { 808, "CHAD" }, { 809, "CHAR" }, { 810, "CHAT" }, { 811, "CHAW" }, { 812, "CHEF" }, { 813, "CHEN" }, { 814, "CHEW" }, { 815, "CHIC" }, { 816, "CHIN" }, { 817, "CHOU" }, { 818, "CHOW" }, { 819, "CHUB" }, { 820, "CHUG" }, { 821, "CHUM" }, { 822, "CITE" }, { 823, "CITY" }, { 824, "CLAD" }, { 825, "CLAM" }, { 826, "CLAN" }, { 827, "CLAW" }, { 828, "CLAY" }, { 829, "CLOD" }, { 830, "CLOG" }, { 831, "CLOT" }, { 832, "CLUB" }, { 833, "CLUE" }, { 834, "COAL" }, { 835, "COAT" }, { 836, "COCA" }, { 837, "COCK" }, { 838, "COCO" }, { 88, "COD" }, { 839, "CODA" }, { 840, "CODE" }, { 841, "CODY" }, { 842, "COED" }, { 89, "COG" }, { 843, "COIL" }, { 844, "COIN" }, { 845, "COKE" }, { 90, "COL" }, { 846, "COLA" }, { 847, "COLD" }, { 848, "COLT" }, { 849, "COMA" }, { 850, "COMB" }, { 851, "COME" }, { 91, "CON" }, { 92, "COO" }, { 852, "COOK" }, { 853, "COOL" }, { 854, "COON" }, { 855, "COOT" }, { 93, "COP" }, { 856, "CORD" }, { 857, "CORE" }, { 858, "CORK" }, { 859, "CORN" }, { 860, "COST" }, { 94, "COT" }, { 861, "COVE" }, { 95, "COW" }, { 862, "COWL" }, { 96, "COY" }, { 863, "CRAB" }, { 864, "CRAG" }, { 865, "CRAM" }, { 866, "CRAY" }, { 867, "CREW" }, { 868, "CRIB" }, { 869, "CROW" }, { 870, "CRUD" }, { 97, "CRY" }, { 98, "CUB" }, { 871, "CUBA" }, { 872, "CUBE" }, { 99, "CUE" }, { 873, "CUFF" }, { 874, "CULL" }, { 875, "CULT" }, { 876, "CUNY" }, { 100, "CUP" }, { 101, "CUR" }, { 877, "CURB" }, { 878, "CURD" }, { 879, "CURE" }, { 880, "CURL" }, { 881, "CURT" }, { 102, "CUT" }, { 882, "CUTS" }, { 103, "DAB" }, { 104, "DAD" }, { 883, "DADE" }, { 884, "DALE" }, { 105, "DAM" }, { 885, "DAME" }, { 106, "DAN" }, { 886, "DANA" }, { 887, "DANE" }, { 888, "DANG" }, { 889, "DANK" }, { 107, "DAR" }, { 890, "DARE" }, { 891, "DARK" }, { 892, "DARN" }, { 893, "DART" }, { 894, "DASH" }, { 895, "DATA" }, { 896, "DATE" }, { 897, "DAVE" }, { 898, "DAVY" }, { 899, "DAWN" }, { 108, "DAY" }, { 900, "DAYS" }, { 901, "DEAD" }, { 902, "DEAF" }, { 903, "DEAL" }, { 904, "DEAN" }, { 905, "DEAR" }, { 906, "DEBT" }, { 907, "DECK" }, { 109, "DEE" }, { 908, "DEED" }, { 909, "DEEM" }, { 910, "DEER" }, { 911, "DEFT" }, { 912, "DEFY" }, { 110, "DEL" }, { 913, "DELL" }, { 111, "DEN" }, { 914, "DENT" }, { 915, "DENY" }, { 112, "DES" }, { 916, "DESK" }, { 113, "DEW" }, { 917, "DIAL" }, { 918, "DICE" }, { 114, "DID" }, { 115, "DIE" }, { 919, "DIED" }, { 920, "DIET" }, { 116, "DIG" }, { 921, "DIME" }, { 117, "DIN" }, { 922, "DINE" }, { 923, "DING" }, { 924, "DINT" }, { 118, "DIP" }, { 925, "DIRE" }, { 926, "DIRT" }, { 927, "DISC" }, { 928, "DISH" }, { 929, "DISK" }, { 930, "DIVE" }, { 119, "DO" }, { 931, "DOCK" }, { 120, "DOE" }, { 932, "DOES" }, { 121, "DOG" }, { 933, "DOLE" }, { 934, "DOLL" }, { 935, "DOLT" }, { 936, "DOME" }, { 122, "DON" }, { 937, "DONE" }, { 938, "DOOM" }, { 939, "DOOR" }, { 940, "DORA" }, { 941, "DOSE" }, { 123, "DOT" }, { 942, "DOTE" }, { 943, "DOUG" }, { 944, "DOUR" }, { 945, "DOVE" }, { 124, "DOW" }, { 946, "DOWN" }, { 947, "DRAB" }, { 948, "DRAG" }, { 949, "DRAM" }, { 950, "DRAW" }, { 951, "DREW" }, { 952, "DRUB" }, { 953, "DRUG" }, { 954, "DRUM" }, { 125, "DRY" }, { 955, "DUAL" }, { 126, "DUB" }, { 956, "DUCK" }, { 957, "DUCT" }, { 127, "DUD" }, { 128, "DUE" }, { 958, "DUEL" }, { 959, "DUET" }, { 129, "DUG" }, { 960, "DUKE" }, { 961, "DULL" }, { 962, "DUMB" }, { 130, "DUN" }, { 963, "DUNE" }, { 964, "DUNK" }, { 965, "DUSK" }, { 966, "DUST" }, { 967, "DUTY" }, { 968, "EACH" }, { 131, "EAR" }, { 969, "EARL" }, { 970, "EARN" }, { 971, "EASE" }, { 972, "EAST" }, { 973, "EASY" }, { 132, "EAT" }, { 974, "EBEN" }, { 975, "ECHO" }, { 133, "ED" }, { 976, "EDDY" }, { 977, "EDEN" }, { 978, "EDGE" }, { 979, "EDGY" }, { 980, "EDIT" }, { 981, "EDNA" }, { 134, "EEL" }, { 982, "EGAN" }, { 135, "EGG" }, { 136, "EGO" }, { 983, "ELAN" }, { 984, "ELBA" }, { 137, "ELI" }, { 138, "ELK" }, { 985, "ELLA" }, { 139, "ELM" }, { 986, "ELSE" }, { 140, "ELY" }, { 141, "EM" }, { 987, "EMIL" }, { 988, "EMIT" }, { 989, "EMMA" }, { 142, "END" }, { 990, "ENDS" }, { 991, "ERIC" }, { 992, "EROS" }, { 143, "EST" }, { 144, "ETC" }, { 145, "EVA" }, { 146, "EVE" }, { 993, "EVEN" }, { 994, "EVER" }, { 995, "EVIL" }, { 147, "EWE" }, { 148, "EYE" }, { 996, "EYED" }, { 997, "FACE" }, { 998, "FACT" }, { 149, "FAD" }, { 999, "FADE" }, { 1000, "FAIL" }, { 1001, "FAIN" }, { 1002, "FAIR" }, { 1003, "FAKE" }, { 1004, "FALL" }, { 1005, "FAME" }, { 150, "FAN" }, { 1006, "FANG" }, { 151, "FAR" }, { 1007, "FARM" }, { 1008, "FAST" }, { 152, "FAT" }, { 1009, "FATE" }, { 1010, "FAWN" }, { 153, "FAY" }, { 1011, "FEAR" }, { 1012, "FEAT" }, { 154, "FED" }, { 155, "FEE" }, { 1013, "FEED" }, { 1014, "FEEL" }, { 1015, "FEET" }, { 1016, "FELL" }, { 1017, "FELT" }, { 1018, "FEND" }, { 1019, "FERN" }, { 1020, "FEST" }, { 1021, "FEUD" }, { 156, "FEW" }, { 157, "FIB" }, { 1022, "FIEF" }, { 158, "FIG" }, { 1023, "FIGS" }, { 1024, "FILE" }, { 1025, "FILL" }, { 1026, "FILM" }, { 159, "FIN" }, { 1027, "FIND" }, { 1028, "FINE" }, { 1029, "FINK" }, { 160, "FIR" }, { 1030, "FIRE" }, { 1031, "FIRM" }, { 1032, "FISH" }, { 1033, "FISK" }, { 1034, "FIST" }, { 161, "FIT" }, { 1035, "FITS" }, { 1036, "FIVE" }, { 1037, "FLAG" }, { 1038, "FLAK" }, { 1039, "FLAM" }, { 1040, "FLAT" }, { 1041, "FLAW" }, { 1042, "FLEA" }, { 1043, "FLED" }, { 1044, "FLEW" }, { 1045, "FLIT" }, { 162, "FLO" }, { 1046, "FLOC" }, { 1047, "FLOG" }, { 1048, "FLOW" }, { 1049, "FLUB" }, { 1050, "FLUE" }, { 163, "FLY" }, { 1051, "FOAL" }, { 1052, "FOAM" }, { 164, "FOE" }, { 165, "FOG" }, { 1053, "FOGY" }, { 1054, "FOIL" }, { 1055, "FOLD" }, { 1056, "FOLK" }, { 1057, "FOND" }, { 1058, "FONT" }, { 1059, "FOOD" }, { 1060, "FOOL" }, { 1061, "FOOT" }, { 166, "FOR" }, { 1062, "FORD" }, { 1063, "FORE" }, { 1064, "FORK" }, { 1065, "FORM" }, { 1066, "FORT" }, { 1067, "FOSS" }, { 1068, "FOUL" }, { 1069, "FOUR" }, { 1070, "FOWL" }, { 1071, "FRAU" }, { 1072, "FRAY" }, { 1073, "FRED" }, { 1074, "FREE" }, { 1075, "FRET" }, { 1076, "FREY" }, { 1077, "FROG" }, { 1078, "FROM" }, { 167, "FRY" }, { 1079, "FUEL" }, { 1080, "FULL" }, { 168, "FUM" }, { 1081, "FUME" }, { 169, "FUN" }, { 1082, "FUND" }, { 1083, "FUNK" }, { 170, "FUR" }, { 1084, "FURY" }, { 1085, "FUSE" }, { 1086, "FUSS" }, { 171, "GAB" }, { 172, "GAD" }, { 1087, "GAFF" }, { 173, "GAG" }, { 1088, "GAGE" }, { 1089, "GAIL" }, { 1090, "GAIN" }, { 1091, "GAIT" }, { 174, "GAL" }, { 1092, "GALA" }, { 1093, "GALE" }, { 1094, "GALL" }, { 1095, "GALT" }, { 175, "GAM" }, { 1096, "GAME" }, { 1097, "GANG" }, { 176, "GAP" }, { 1098, "GARB" }, { 1099, "GARY" }, { 177, "GAS" }, { 1100, "GASH" }, { 1101, "GATE" }, { 1102, "GAUL" }, { 1103, "GAUR" }, { 1104, "GAVE" }, { 1105, "GAWK" }, { 178, "GAY" }, { 1106, "GEAR" }, { 179, "GEE" }, { 180, "GEL" }, { 1107, "GELD" }, { 181, "GEM" }, { 1108, "GENE" }, { 1109, "GENT" }, { 1110, "GERM" }, { 182, "GET" }, { 1111, "GETS" }, { 1112, "GIBE" }, { 1113, "GIFT" }, { 183, "GIG" }, { 184, "GIL" }, { 1114, "GILD" }, { 1115, "GILL" }, { 1116, "GILT" }, { 185, "GIN" }, { 1117, "GINA" }, { 1118, "GIRD" }, { 1119, "GIRL" }, { 1120, "GIST" }, { 1121, "GIVE" }, { 1122, "GLAD" }, { 1123, "GLEE" }, { 1124, "GLEN" }, { 1125, "GLIB" }, { 1126, "GLOB" }, { 1127, "GLOM" }, { 1128, "GLOW" }, { 1129, "GLUE" }, { 1130, "GLUM" }, { 1131, "GLUT" }, { 186, "GO" }, { 1132, "GOAD" }, { 1133, "GOAL" }, { 1134, "GOAT" }, { 1135, "GOER" }, { 1136, "GOES" }, { 1137, "GOLD" }, { 1138, "GOLF" }, { 1139, "GONE" }, { 1140, "GONG" }, { 1141, "GOOD" }, { 1142, "GOOF" }, { 1143, "GORE" }, { 1144, "GORY" }, { 1145, "GOSH" }, { 187, "GOT" }, { 1146, "GOUT" }, { 1147, "GOWN" }, { 1148, "GRAB" }, { 1149, "GRAD" }, { 1150, "GRAY" }, { 1151, "GREG" }, { 1152, "GREW" }, { 1153, "GREY" }, { 1154, "GRID" }, { 1155, "GRIM" }, { 1156, "GRIN" }, { 1157, "GRIT" }, { 1158, "GROW" }, { 1159, "GRUB" }, { 1160, "GULF" }, { 1161, "GULL" }, { 188, "GUM" }, { 189, "GUN" }, { 1162, "GUNK" }, { 1163, "GURU" }, { 190, "GUS" }, { 1164, "GUSH" }, { 1165, "GUST" }, { 191, "GUT" }, { 192, "GUY" }, { 1166, "GWEN" }, { 1167, "GWYN" }, { 193, "GYM" }, { 194, "GYP" }, { 195, "HA" }, { 1168, "HAAG" }, { 1169, "HAAS" }, { 1170, "HACK" }, { 196, "HAD" }, { 1171, "HAIL" }, { 1172, "HAIR" }, { 197, "HAL" }, { 1173, "HALE" }, { 1174, "HALF" }, { 1175, "HALL" }, { 1176, "HALO" }, { 1177, "HALT" }, { 198, "HAM" }, { 199, "HAN" }, { 1178, "HAND" }, { 1179, "HANG" }, { 1180, "HANK" }, { 1181, "HANS" }, { 200, "HAP" }, { 1182, "HARD" }, { 1183, "HARK" }, { 1184, "HARM" }, { 1185, "HART" }, { 201, "HAS" }, { 1186, "HASH" }, { 1187, "HAST" }, { 202, "HAT" }, { 1188, "HATE" }, { 1189, "HATH" }, { 1190, "HAUL" }, { 1191, "HAVE" }, { 203, "HAW" }, { 1192, "HAWK" }, { 204, "HAY" }, { 1193, "HAYS" }, { 205, "HE" }, { 1194, "HEAD" }, { 1195, "HEAL" }, { 1196, "HEAR" }, { 1197, "HEAT" }, { 1198, "HEBE" }, { 1199, "HECK" }, { 1200, "HEED" }, { 1201, "HEEL" }, { 1202, "HEFT" }, { 1203, "HELD" }, { 1204, "HELL" }, { 1205, "HELM" }, { 206, "HEM" }, { 207, "HEN" }, { 208, "HER" }, { 1206, "HERB" }, { 1207, "HERD" }, { 1208, "HERE" }, { 1209, "HERO" }, { 1210, "HERS" }, { 1211, "HESS" }, { 209, "HEW" }, { 1212, "HEWN" }, { 210, "HEY" }, { 211, "HI" }, { 1213, "HICK" }, { 212, "HID" }, { 1214, "HIDE" }, { 1215, "HIGH" }, { 1216, "HIKE" }, { 1217, "HILL" }, { 1218, "HILT" }, { 213, "HIM" }, { 1219, "HIND" }, { 1220, "HINT" }, { 214, "HIP" }, { 1221, "HIRE" }, { 215, "HIS" }, { 1222, "HISS" }, { 216, "HIT" }, { 1223, "HIVE" }, { 217, "HO" }, { 218, "HOB" }, { 1224, "HOBO" }, { 219, "HOC" }, { 1225, "HOCK" }, { 220, "HOE" }, { 1226, "HOFF" }, { 221, "HOG" }, { 1227, "HOLD" }, { 1228, "HOLE" }, { 1229, "HOLM" }, { 1230, "HOLT" }, { 1231, "HOME" }, { 1232, "HONE" }, { 1233, "HONK" }, { 1234, "HOOD" }, { 1235, "HOOF" }, { 1236, "HOOK" }, { 1237, "HOOT" }, { 222, "HOP" }, { 1238, "HORN" }, { 1239, "HOSE" }, { 1240, "HOST" }, { 223, "HOT" }, { 1241, "HOUR" }, { 1242, "HOVE" }, { 224, "HOW" }, { 1243, "HOWE" }, { 1244, "HOWL" }, { 1245, "HOYT" }, { 225, "HUB" }, { 1246, "HUCK" }, { 226, "HUE" }, { 1247, "HUED" }, { 1248, "HUFF" }, { 227, "HUG" }, { 1249, "HUGE" }, { 1250, "HUGH" }, { 1251, "HUGO" }, { 228, "HUH" }, { 1252, "HULK" }, { 1253, "HULL" }, { 229, "HUM" }, { 1254, "HUNK" }, { 1255, "HUNT" }, { 1256, "HURD" }, { 1257, "HURL" }, { 1258, "HURT" }, { 1259, "HUSH" }, { 230, "HUT" }, { 1260, "HYDE" }, { 1261, "HYMN" }, { 231, "I" }, { 1262, "IBIS" }, { 1263, "ICON" }, { 232, "ICY" }, { 233, "IDA" }, { 1264, "IDEA" }, { 1265, "IDLE" }, { 234, "IF" }, { 1266, "IFFY" }, { 235, "IKE" }, { 236, "ILL" }, { 1267, "INCA" }, { 1268, "INCH" }, { 237, "INK" }, { 238, "INN" }, { 1269, "INTO" }, { 239, "IO" }, { 240, "ION" }, { 1270, "IONS" }, { 1271, "IOTA" }, { 1272, "IOWA" }, { 241, "IQ" }, { 242, "IRA" }, { 243, "IRE" }, { 1273, "IRIS" }, { 244, "IRK" }, { 1274, "IRMA" }, { 1275, "IRON" }, { 245, "IS" }, { 1276, "ISLE" }, { 246, "IT" }, { 1277, "ITCH" }, { 1278, "ITEM" }, { 247, "ITS" }, { 1279, "IVAN" }, { 248, "IVY" }, { 249, "JAB" }, { 1280, "JACK" }, { 1281, "JADE" }, { 250, "JAG" }, { 1282, "JAIL" }, { 1283, "JAKE" }, { 251, "JAM" }, { 252, "JAN" }, { 1284, "JANE" }, { 253, "JAR" }, { 1285, "JAVA" }, { 254, "JAW" }, { 255, "JAY" }, { 1286, "JEAN" }, { 1287, "JEFF" }, { 1288, "JERK" }, { 1289, "JESS" }, { 1290, "JEST" }, { 256, "JET" }, { 1291, "JIBE" }, { 257, "JIG" }, { 1292, "JILL" }, { 1293, "JILT" }, { 258, "JIM" }, { 1294, "JIVE" }, { 259, "JO" }, { 1295, "JOAN" }, { 260, "JOB" }, { 1296, "JOBS" }, { 1297, "JOCK" }, { 261, "JOE" }, { 1298, "JOEL" }, { 1299, "JOEY" }, { 262, "JOG" }, { 1300, "JOHN" }, { 1301, "JOIN" }, { 1302, "JOKE" }, { 1303, "JOLT" }, { 263, "JOT" }, { 1304, "JOVE" }, { 264, "JOY" }, { 1305, "JUDD" }, { 1306, "JUDE" }, { 1307, "JUDO" }, { 1308, "JUDY" }, { 265, "JUG" }, { 1309, "JUJU" }, { 1310, "JUKE" }, { 1311, "JULY" }, { 1312, "JUNE" }, { 1313, "JUNK" }, { 1314, "JUNO" }, { 1315, "JURY" }, { 1316, "JUST" }, { 266, "JUT" }, { 1317, "JUTE" }, { 1318, "KAHN" }, { 1319, "KALE" }, { 1320, "KANE" }, { 1321, "KANT" }, { 1322, "KARL" }, { 1323, "KATE" }, { 267, "KAY" }, { 1324, "KEEL" }, { 1325, "KEEN" }, { 268, "KEG" }, { 269, "KEN" }, { 1326, "KENO" }, { 1327, "KENT" }, { 1328, "KERN" }, { 1329, "KERR" }, { 270, "KEY" }, { 1330, "KEYS" }, { 1331, "KICK" }, { 271, "KID" }, { 1332, "KILL" }, { 272, "KIM" }, { 273, "KIN" }, { 1333, "KIND" }, { 1334, "KING" }, { 1335, "KIRK" }, { 1336, "KISS" }, { 274, "KIT" }, { 1337, "KITE" }, { 1338, "KLAN" }, { 1339, "KNEE" }, { 1340, "KNEW" }, { 1341, "KNIT" }, { 1342, "KNOB" }, { 1343, "KNOT" }, { 1344, "KNOW" }, { 1345, "KOCH" }, { 1346, "KONG" }, { 1347, "KUDO" }, { 1348, "KURD" }, { 1349, "KURT" }, { 1350, "KYLE" }, { 275, "LA" }, { 276, "LAB" }, { 277, "LAC" }, { 1351, "LACE" }, { 1352, "LACK" }, { 1353, "LACY" }, { 278, "LAD" }, { 1354, "LADY" }, { 279, "LAG" }, { 1355, "LAID" }, { 1356, "LAIN" }, { 1357, "LAIR" }, { 1358, "LAKE" }, { 280, "LAM" }, { 1359, "LAMB" }, { 1360, "LAME" }, { 1361, "LAND" }, { 1362, "LANE" }, { 1363, "LANG" }, { 281, "LAP" }, { 1364, "LARD" }, { 1365, "LARK" }, { 1366, "LASS" }, { 1367, "LAST" }, { 1368, "LATE" }, { 1369, "LAUD" }, { 1370, "LAVA" }, { 282, "LAW" }, { 1371, "LAWN" }, { 1372, "LAWS" }, { 283, "LAY" }, { 1373, "LAYS" }, { 284, "LEA" }, { 1374, "LEAD" }, { 1375, "LEAF" }, { 1376, "LEAK" }, { 1377, "LEAN" }, { 1378, "LEAR" }, { 285, "LED" }, { 286, "LEE" }, { 1379, "LEEK" }, { 1380, "LEER" }, { 1381, "LEFT" }, { 287, "LEG" }, { 288, "LEN" }, { 1382, "LEND" }, { 1383, "LENS" }, { 1384, "LENT" }, { 289, "LEO" }, { 1385, "LEON" }, { 1386, "LESK" }, { 1387, "LESS" }, { 1388, "LEST" }, { 290, "LET" }, { 1389, "LETS" }, { 291, "LEW" }, { 1390, "LIAR" }, { 1391, "LICE" }, { 1392, "LICK" }, { 292, "LID" }, { 293, "LIE" }, { 1393, "LIED" }, { 1394, "LIEN" }, { 1395, "LIES" }, { 1396, "LIEU" }, { 1397, "LIFE" }, { 1398, "LIFT" }, { 1399, "LIKE" }, { 1400, "LILA" }, { 1401, "LILT" }, { 1402, "LILY" }, { 1403, "LIMA" }, { 1404, "LIMB" }, { 1405, "LIME" }, { 294, "LIN" }, { 1406, "LIND" }, { 1407, "LINE" }, { 1408, "LINK" }, { 1409, "LINT" }, { 1410, "LION" }, { 295, "LIP" }, { 1411, "LISA" }, { 1412, "LIST" }, { 296, "LIT" }, { 1413, "LIVE" }, { 297, "LO" }, { 1414, "LOAD" }, { 1415, "LOAF" }, { 1416, "LOAM" }, { 1417, "LOAN" }, { 298, "LOB" }, { 1418, "LOCK" }, { 1419, "LOFT" }, { 299, "LOG" }, { 1420, "LOGE" }, { 1421, "LOIS" }, { 1422, "LOLA" }, { 1423, "LONE" }, { 1424, "LONG" }, { 1425, "LOOK" }, { 1426, "LOON" }, { 1427, "LOOT" }, { 300, "LOP" }, { 1428, "LORD" }, { 1429, "LORE" }, { 301, "LOS" }, { 1430, "LOSE" }, { 1431, "LOSS" }, { 1432, "LOST" }, { 302, "LOT" }, { 303, "LOU" }, { 1433, "LOUD" }, { 1434, "LOVE" }, { 304, "LOW" }, { 1435, "LOWE" }, { 305, "LOY" }, { 1436, "LUCK" }, { 1437, "LUCY" }, { 306, "LUG" }, { 1438, "LUGE" }, { 1439, "LUKE" }, { 1440, "LULU" }, { 1441, "LUND" }, { 1442, "LUNG" }, { 1443, "LURA" }, { 1444, "LURE" }, { 1445, "LURK" }, { 1446, "LUSH" }, { 1447, "LUST" }, { 307, "LYE" }, { 1448, "LYLE" }, { 1449, "LYNN" }, { 1450, "LYON" }, { 1451, "LYRA" }, { 308, "MA" }, { 309, "MAC" }, { 1452, "MACE" }, { 310, "MAD" }, { 1453, "MADE" }, { 311, "MAE" }, { 1454, "MAGI" }, { 1455, "MAID" }, { 1456, "MAIL" }, { 1457, "MAIN" }, { 1458, "MAKE" }, { 1459, "MALE" }, { 1460, "MALI" }, { 1461, "MALL" }, { 1462, "MALT" }, { 312, "MAN" }, { 1463, "MANA" }, { 1464, "MANN" }, { 1465, "MANY" }, { 313, "MAO" }, { 314, "MAP" }, { 1466, "MARC" }, { 1467, "MARE" }, { 1468, "MARK" }, { 1469, "MARS" }, { 1470, "MART" }, { 1471, "MARY" }, { 1472, "MASH" }, { 1473, "MASK" }, { 1474, "MASS" }, { 1475, "MAST" }, { 315, "MAT" }, { 1476, "MATE" }, { 1477, "MATH" }, { 1478, "MAUL" }, { 316, "MAW" }, { 317, "MAY" }, { 1479, "MAYO" }, { 318, "ME" }, { 1480, "MEAD" }, { 1481, "MEAL" }, { 1482, "MEAN" }, { 1483, "MEAT" }, { 1484, "MEEK" }, { 1485, "MEET" }, { 319, "MEG" }, { 320, "MEL" }, { 1486, "MELD" }, { 1487, "MELT" }, { 1488, "MEMO" }, { 321, "MEN" }, { 1489, "MEND" }, { 1490, "MENU" }, { 1491, "MERT" }, { 1492, "MESH" }, { 1493, "MESS" }, { 322, "MET" }, { 323, "MEW" }, { 1494, "MICE" }, { 324, "MID" }, { 1495, "MIKE" }, { 1496, "MILD" }, { 1497, "MILE" }, { 1498, "MILK" }, { 1499, "MILL" }, { 1500, "MILT" }, { 1501, "MIMI" }, { 325, "MIN" }, { 1502, "MIND" }, { 1503, "MINE" }, { 1504, "MINI" }, { 1505, "MINK" }, { 1506, "MINT" }, { 1507, "MIRE" }, { 1508, "MISS" }, { 1509, "MIST" }, { 326, "MIT" }, { 1510, "MITE" }, { 1511, "MITT" }, { 1512, "MOAN" }, { 1513, "MOAT" }, { 327, "MOB" }, { 1514, "MOCK" }, { 328, "MOD" }, { 1515, "MODE" }, { 329, "MOE" }, { 1516, "MOLD" }, { 1517, "MOLE" }, { 1518, "MOLL" }, { 1519, "MOLT" }, { 1520, "MONA" }, { 1521, "MONK" }, { 1522, "MONT" }, { 330, "MOO" }, { 1523, "MOOD" }, { 1524, "MOON" }, { 1525, "MOOR" }, { 1526, "MOOT" }, { 331, "MOP" }, { 1527, "MORE" }, { 1528, "MORN" }, { 1529, "MORT" }, { 332, "MOS" }, { 1530, "MOSS" }, { 1531, "MOST" }, { 333, "MOT" }, { 1532, "MOTH" }, { 1533, "MOVE" }, { 334, "MOW" }, { 1534, "MUCH" }, { 1535, "MUCK" }, { 335, "MUD" }, { 1536, "MUDD" }, { 1537, "MUFF" }, { 336, "MUG" }, { 1538, "MULE" }, { 1539, "MULL" }, { 337, "MUM" }, { 1540, "MURK" }, { 1541, "MUSH" }, { 1542, "MUST" }, { 1543, "MUTE" }, { 1544, "MUTT" }, { 338, "MY" }, { 1545, "MYRA" }, { 1546, "MYTH" }, { 339, "NAB" }, { 340, "NAG" }, { 1547, "NAGY" }, { 1548, "NAIL" }, { 1549, "NAIR" }, { 1550, "NAME" }, { 341, "NAN" }, { 342, "NAP" }, { 1551, "NARY" }, { 1552, "NASH" }, { 343, "NAT" }, { 1553, "NAVE" }, { 1554, "NAVY" }, { 344, "NAY" }, { 345, "NE" }, { 1555, "NEAL" }, { 1556, "NEAR" }, { 1557, "NEAT" }, { 1558, "NECK" }, { 346, "NED" }, { 347, "NEE" }, { 1559, "NEED" }, { 1560, "NEIL" }, { 1561, "NELL" }, { 1562, "NEON" }, { 1563, "NERO" }, { 1564, "NESS" }, { 1565, "NEST" }, { 348, "NET" }, { 349, "NEW" }, { 1566, "NEWS" }, { 1567, "NEWT" }, { 350, "NIB" }, { 1568, "NIBS" }, { 1569, "NICE" }, { 1570, "NICK" }, { 351, "NIL" }, { 1571, "NILE" }, { 1572, "NINA" }, { 1573, "NINE" }, { 352, "NIP" }, { 353, "NIT" }, { 354, "NO" }, { 1574, "NOAH" }, { 355, "NOB" }, { 356, "NOD" }, { 1575, "NODE" }, { 1576, "NOEL" }, { 1577, "NOLL" }, { 357, "NON" }, { 1578, "NONE" }, { 1579, "NOOK" }, { 1580, "NOON" }, { 358, "NOR" }, { 1581, "NORM" }, { 1582, "NOSE" }, { 359, "NOT" }, { 1583, "NOTE" }, { 1584, "NOUN" }, { 360, "NOV" }, { 1585, "NOVA" }, { 361, "NOW" }, { 362, "NU" }, { 1586, "NUDE" }, { 1587, "NULL" }, { 1588, "NUMB" }, { 363, "NUN" }, { 364, "NUT" }, { 365, "O" }, { 366, "OAF" }, { 367, "OAK" }, { 368, "OAR" }, { 369, "OAT" }, { 1589, "OATH" }, { 1590, "OBEY" }, { 1591, "OBOE" }, { 370, "ODD" }, { 371, "ODE" }, { 1592, "ODIN" }, { 372, "OF" }, { 373, "OFF" }, { 374, "OFT" }, { 375, "OH" }, { 1593, "OHIO" }, { 376, "OIL" }, { 1594, "OILY" }, { 1595, "OINT" }, { 377, "OK" }, { 1596, "OKAY" }, { 1597, "OLAF" }, { 378, "OLD" }, { 1598, "OLDY" }, { 1599, "OLGA" }, { 1600, "OLIN" }, { 1601, "OMAN" }, { 1602, "OMEN" }, { 1603, "OMIT" }, { 379, "ON" }, { 1604, "ONCE" }, { 380, "ONE" }, { 1605, "ONES" }, { 1606, "ONLY" }, { 1607, "ONTO" }, { 1608, "ONUS" }, { 381, "OR" }, { 1609, "ORAL" }, { 382, "ORB" }, { 383, "ORE" }, { 1610, "ORGY" }, { 384, "ORR" }, { 385, "OS" }, { 1611, "OSLO" }, { 1612, "OTIS" }, { 386, "OTT" }, { 1613, "OTTO" }, { 1614, "OUCH" }, { 387, "OUR" }, { 1615, "OUST" }, { 388, "OUT" }, { 1616, "OUTS" }, { 389, "OVA" }, { 1617, "OVAL" }, { 1618, "OVEN" }, { 1619, "OVER" }, { 390, "OW" }, { 391, "OWE" }, { 392, "OWL" }, { 1620, "OWLY" }, { 393, "OWN" }, { 1621, "OWNS" }, { 394, "OX" }, { 395, "PA" }, { 396, "PAD" }, { 397, "PAL" }, { 398, "PAM" }, { 399, "PAN" }, { 400, "PAP" }, { 401, "PAR" }, { 402, "PAT" }, { 403, "PAW" }, { 404, "PAY" }, { 405, "PEA" }, { 406, "PEG" }, { 407, "PEN" }, { 408, "PEP" }, { 409, "PER" }, { 410, "PET" }, { 411, "PEW" }, { 412, "PHI" }, { 413, "PI" }, { 414, "PIE" }, { 415, "PIN" }, { 416, "PIT" }, { 417, "PLY" }, { 418, "PO" }, { 419, "POD" }, { 420, "POE" }, { 421, "POP" }, { 422, "POT" }, { 423, "POW" }, { 424, "PRO" }, { 425, "PRY" }, { 426, "PUB" }, { 427, "PUG" }, { 428, "PUN" }, { 429, "PUP" }, { 430, "PUT" }, { 1622, "QUAD" }, { 1623, "QUIT" }, { 431, "QUO" }, { 1624, "QUOD" }, { 1625, "RACE" }, { 1626, "RACK" }, { 1627, "RACY" }, { 1628, "RAFT" }, { 432, "RAG" }, { 1629, "RAGE" }, { 1630, "RAID" }, { 1631, "RAIL" }, { 1632, "RAIN" }, { 1633, "RAKE" }, { 433, "RAM" }, { 434, "RAN" }, { 1634, "RANK" }, { 1635, "RANT" }, { 435, "RAP" }, { 1636, "RARE" }, { 1637, "RASH" }, { 436, "RAT" }, { 1638, "RATE" }, { 1639, "RAVE" }, { 437, "RAW" }, { 438, "RAY" }, { 1640, "RAYS" }, { 1641, "READ" }, { 1642, "REAL" }, { 1643, "REAM" }, { 1644, "REAR" }, { 439, "REB" }, { 1645, "RECK" }, { 440, "RED" }, { 1646, "REED" }, { 1647, "REEF" }, { 1648, "REEK" }, { 1649, "REEL" }, { 1650, "REID" }, { 1651, "REIN" }, { 1652, "RENA" }, { 1653, "REND" }, { 1654, "RENT" }, { 441, "REP" }, { 1655, "REST" }, { 442, "RET" }, { 443, "RIB" }, { 1656, "RICE" }, { 1657, "RICH" }, { 1658, "RICK" }, { 444, "RID" }, { 1659, "RIDE" }, { 1660, "RIFT" }, { 445, "RIG" }, { 1661, "RILL" }, { 446, "RIM" }, { 1662, "RIME" }, { 1663, "RING" }, { 1664, "RINK" }, { 447, "RIO" }, { 448, "RIP" }, { 1665, "RISE" }, { 1666, "RISK" }, { 1667, "RITE" }, { 1668, "ROAD" }, { 1669, "ROAM" }, { 1670, "ROAR" }, { 449, "ROB" }, { 1671, "ROBE" }, { 1672, "ROCK" }, { 450, "ROD" }, { 1673, "RODE" }, { 451, "ROE" }, { 1674, "ROIL" }, { 1675, "ROLL" }, { 1676, "ROME" }, { 452, "RON" }, { 1677, "ROOD" }, { 1678, "ROOF" }, { 1679, "ROOK" }, { 1680, "ROOM" }, { 1681, "ROOT" }, { 1682, "ROSA" }, { 1683, "ROSE" }, { 1684, "ROSS" }, { 1685, "ROSY" }, { 453, "ROT" }, { 1686, "ROTH" }, { 1687, "ROUT" }, { 1688, "ROVE" }, { 454, "ROW" }, { 1689, "ROWE" }, { 1690, "ROWS" }, { 455, "ROY" }, { 456, "RUB" }, { 1691, "RUBE" }, { 1692, "RUBY" }, { 1693, "RUDE" }, { 1694, "RUDY" }, { 457, "RUE" }, { 458, "RUG" }, { 1695, "RUIN" }, { 1696, "RULE" }, { 459, "RUM" }, { 460, "RUN" }, { 1697, "RUNG" }, { 1698, "RUNS" }, { 1699, "RUNT" }, { 1700, "RUSE" }, { 1701, "RUSH" }, { 1702, "RUSK" }, { 1703, "RUSS" }, { 1704, "RUST" }, { 1705, "RUTH" }, { 461, "RYE" }, { 462, "SAC" }, { 1706, "SACK" }, { 463, "SAD" }, { 1707, "SAFE" }, { 464, "SAG" }, { 1708, "SAGE" }, { 1709, "SAID" }, { 1710, "SAIL" }, { 465, "SAL" }, { 1711, "SALE" }, { 1712, "SALK" }, { 1713, "SALT" }, { 466, "SAM" }, { 1714, "SAME" }, { 467, "SAN" }, { 1715, "SAND" }, { 1716, "SANE" }, { 1717, "SANG" }, { 1718, "SANK" }, { 468, "SAP" }, { 1719, "SARA" }, { 469, "SAT" }, { 1720, "SAUL" }, { 1721, "SAVE" }, { 470, "SAW" }, { 471, "SAY" }, { 1722, "SAYS" }, { 1723, "SCAN" }, { 1724, "SCAR" }, { 1725, "SCAT" }, { 1726, "SCOT" }, { 472, "SEA" }, { 1727, "SEAL" }, { 1728, "SEAM" }, { 1729, "SEAR" }, { 1730, "SEAT" }, { 473, "SEC" }, { 474, "SEE" }, { 1731, "SEED" }, { 1732, "SEEK" }, { 1733, "SEEM" }, { 1734, "SEEN" }, { 1735, "SEES" }, { 1736, "SELF" }, { 1737, "SELL" }, { 475, "SEN" }, { 1738, "SEND" }, { 1739, "SENT" }, { 476, "SET" }, { 1740, "SETS" }, { 477, "SEW" }, { 1741, "SEWN" }, { 1742, "SHAG" }, { 1743, "SHAM" }, { 1744, "SHAW" }, { 1745, "SHAY" }, { 478, "SHE" }, { 1746, "SHED" }, { 1747, "SHIM" }, { 1748, "SHIN" }, { 1749, "SHOD" }, { 1750, "SHOE" }, { 1751, "SHOT" }, { 1752, "SHOW" }, { 1753, "SHUN" }, { 1754, "SHUT" }, { 479, "SHY" }, { 1755, "SICK" }, { 1756, "SIDE" }, { 1757, "SIFT" }, { 1758, "SIGH" }, { 1759, "SIGN" }, { 1760, "SILK" }, { 1761, "SILL" }, { 1762, "SILO" }, { 1763, "SILT" }, { 480, "SIN" }, { 1764, "SINE" }, { 1765, "SING" }, { 1766, "SINK" }, { 481, "SIP" }, { 482, "SIR" }, { 1767, "SIRE" }, { 483, "SIS" }, { 484, "SIT" }, { 1768, "SITE" }, { 1769, "SITS" }, { 1770, "SITU" }, { 1771, "SKAT" }, { 1772, "SKEW" }, { 485, "SKI" }, { 1773, "SKID" }, { 1774, "SKIM" }, { 1775, "SKIN" }, { 1776, "SKIT" }, { 486, "SKY" }, { 1777, "SLAB" }, { 1778, "SLAM" }, { 1779, "SLAT" }, { 1780, "SLAY" }, { 1781, "SLED" }, { 1782, "SLEW" }, { 1783, "SLID" }, { 1784, "SLIM" }, { 1785, "SLIT" }, { 1786, "SLOB" }, { 1787, "SLOG" }, { 1788, "SLOT" }, { 1789, "SLOW" }, { 1790, "SLUG" }, { 1791, "SLUM" }, { 1792, "SLUR" }, { 487, "SLY" }, { 1793, "SMOG" }, { 1794, "SMUG" }, { 1795, "SNAG" }, { 1796, "SNOB" }, { 1797, "SNOW" }, { 1798, "SNUB" }, { 1799, "SNUG" }, { 488, "SO" }, { 1800, "SOAK" }, { 1801, "SOAR" }, { 489, "SOB" }, { 1802, "SOCK" }, { 490, "SOD" }, { 1803, "SODA" }, { 1804, "SOFA" }, { 1805, "SOFT" }, { 1806, "SOIL" }, { 1807, "SOLD" }, { 1808, "SOME" }, { 491, "SON" }, { 1809, "SONG" }, { 1810, "SOON" }, { 1811, "SOOT" }, { 492, "SOP" }, { 1812, "SORE" }, { 1813, "SORT" }, { 1814, "SOUL" }, { 1815, "SOUR" }, { 493, "SOW" }, { 1816, "SOWN" }, { 494, "SOY" }, { 495, "SPA" }, { 496, "SPY" }, { 1817, "STAB" }, { 1818, "STAG" }, { 1819, "STAN" }, { 1820, "STAR" }, { 1821, "STAY" }, { 1822, "STEM" }, { 1823, "STEW" }, { 1824, "STIR" }, { 1825, "STOW" }, { 1826, "STUB" }, { 1827, "STUN" }, { 497, "SUB" }, { 1828, "SUCH" }, { 498, "SUD" }, { 1829, "SUDS" }, { 499, "SUE" }, { 1830, "SUIT" }, { 1831, "SULK" }, { 500, "SUM" }, { 1832, "SUMS" }, { 501, "SUN" }, { 1833, "SUNG" }, { 1834, "SUNK" }, { 502, "SUP" }, { 1835, "SURE" }, { 1836, "SURF" }, { 1837, "SWAB" }, { 1838, "SWAG" }, { 1839, "SWAM" }, { 1840, "SWAN" }, { 1841, "SWAT" }, { 1842, "SWAY" }, { 1843, "SWIM" }, { 1844, "SWUM" }, { 503, "TAB" }, { 1845, "TACK" }, { 1846, "TACT" }, { 504, "TAD" }, { 505, "TAG" }, { 1847, "TAIL" }, { 1848, "TAKE" }, { 1849, "TALE" }, { 1850, "TALK" }, { 1851, "TALL" }, { 506, "TAN" }, { 1852, "TANK" }, { 507, "TAP" }, { 508, "TAR" }, { 1853, "TASK" }, { 1854, "TATE" }, { 1855, "TAUT" }, { 509, "TEA" }, { 1856, "TEAL" }, { 1857, "TEAM" }, { 1858, "TEAR" }, { 1859, "TECH" }, { 510, "TED" }, { 511, "TEE" }, { 1860, "TEEM" }, { 1861, "TEEN" }, { 1862, "TEET" }, { 1863, "TELL" }, { 512, "TEN" }, { 1864, "TEND" }, { 1865, "TENT" }, { 1866, "TERM" }, { 1867, "TERN" }, { 1868, "TESS" }, { 1869, "TEST" }, { 1870, "THAN" }, { 1871, "THAT" }, { 513, "THE" }, { 1872, "THEE" }, { 1873, "THEM" }, { 1874, "THEN" }, { 1875, "THEY" }, { 1876, "THIN" }, { 1877, "THIS" }, { 1878, "THUD" }, { 1879, "THUG" }, { 514, "THY" }, { 515, "TIC" }, { 1880, "TICK" }, { 1881, "TIDE" }, { 1882, "TIDY" }, { 516, "TIE" }, { 1883, "TIED" }, { 1884, "TIER" }, { 1885, "TILE" }, { 1886, "TILL" }, { 1887, "TILT" }, { 517, "TIM" }, { 1888, "TIME" }, { 518, "TIN" }, { 1889, "TINA" }, { 1890, "TINE" }, { 1891, "TINT" }, { 1892, "TINY" }, { 519, "TIP" }, { 1893, "TIRE" }, { 520, "TO" }, { 1894, "TOAD" }, { 521, "TOE" }, { 522, "TOG" }, { 1895, "TOGO" }, { 1896, "TOIL" }, { 1897, "TOLD" }, { 1898, "TOLL" }, { 523, "TOM" }, { 524, "TON" }, { 1899, "TONE" }, { 1900, "TONG" }, { 1901, "TONY" }, { 525, "TOO" }, { 1902, "TOOK" }, { 1903, "TOOL" }, { 1904, "TOOT" }, { 526, "TOP" }, { 1905, "TORE" }, { 1906, "TORN" }, { 1907, "TOTE" }, { 1908, "TOUR" }, { 1909, "TOUT" }, { 527, "TOW" }, { 1910, "TOWN" }, { 528, "TOY" }, { 1911, "TRAG" }, { 1912, "TRAM" }, { 1913, "TRAY" }, { 1914, "TREE" }, { 1915, "TREK" }, { 1916, "TRIG" }, { 1917, "TRIM" }, { 1918, "TRIO" }, { 1919, "TROD" }, { 1920, "TROT" }, { 1921, "TROY" }, { 1922, "TRUE" }, { 529, "TRY" }, { 530, "TUB" }, { 1923, "TUBA" }, { 1924, "TUBE" }, { 1925, "TUCK" }, { 1926, "TUFT" }, { 531, "TUG" }, { 532, "TUM" }, { 533, "TUN" }, { 1927, "TUNA" }, { 1928, "TUNE" }, { 1929, "TUNG" }, { 1930, "TURF" }, { 1931, "TURN" }, { 1932, "TUSK" }, { 1933, "TWIG" }, { 1934, "TWIN" }, { 1935, "TWIT" }, { 534, "TWO" }, { 1936, "ULAN" }, { 535, "UN" }, { 1937, "UNIT" }, { 536, "UP" }, { 1938, "URGE" }, { 537, "US" }, { 538, "USE" }, { 1939, "USED" }, { 1940, "USER" }, { 1941, "USES" }, { 1942, "UTAH" }, { 1943, "VAIL" }, { 1944, "VAIN" }, { 1945, "VALE" }, { 539, "VAN" }, { 1946, "VARY" }, { 1947, "VASE" }, { 1948, "VAST" }, { 540, "VAT" }, { 1949, "VEAL" }, { 1950, "VEDA" }, { 1951, "VEIL" }, { 1952, "VEIN" }, { 1953, "VEND" }, { 1954, "VENT" }, { 1955, "VERB" }, { 1956, "VERY" }, { 541, "VET" }, { 1957, "VETO" }, { 1958, "VICE" }, { 542, "VIE" }, { 1959, "VIEW" }, { 1960, "VINE" }, { 1961, "VISE" }, { 1962, "VOID" }, { 1963, "VOLT" }, { 1964, "VOTE" }, { 1965, "WACK" }, { 543, "WAD" }, { 1966, "WADE" }, { 544, "WAG" }, { 1967, "WAGE" }, { 1968, "WAIL" }, { 1969, "WAIT" }, { 1970, "WAKE" }, { 1971, "WALE" }, { 1972, "WALK" }, { 1973, "WALL" }, { 1974, "WALT" }, { 1975, "WAND" }, { 1976, "WANE" }, { 1977, "WANG" }, { 1978, "WANT" }, { 545, "WAR" }, { 1979, "WARD" }, { 1980, "WARM" }, { 1981, "WARN" }, { 1982, "WART" }, { 546, "WAS" }, { 1983, "WASH" }, { 1984, "WAST" }, { 1985, "WATS" }, { 1986, "WATT" }, { 1987, "WAVE" }, { 1988, "WAVY" }, { 547, "WAY" }, { 1989, "WAYS" }, { 548, "WE" }, { 1990, "WEAK" }, { 1991, "WEAL" }, { 1992, "WEAN" }, { 1993, "WEAR" }, { 549, "WEB" }, { 550, "WED" }, { 551, "WEE" }, { 1994, "WEED" }, { 1995, "WEEK" }, { 1996, "WEIR" }, { 1997, "WELD" }, { 1998, "WELL" }, { 1999, "WELT" }, { 2000, "WENT" }, { 2001, "WERE" }, { 2002, "WERT" }, { 2003, "WEST" }, { 552, "WET" }, { 2004, "WHAM" }, { 2005, "WHAT" }, { 2006, "WHEE" }, { 2007, "WHEN" }, { 2008, "WHET" }, { 553, "WHO" }, { 2009, "WHOA" }, { 2010, "WHOM" }, { 554, "WHY" }, { 2011, "WICK" }, { 2012, "WIFE" }, { 2013, "WILD" }, { 2014, "WILL" }, { 555, "WIN" }, { 2015, "WIND" }, { 2016, "WINE" }, { 2017, "WING" }, { 2018, "WINK" }, { 2019, "WINO" }, { 2020, "WIRE" }, { 2021, "WISE" }, { 2022, "WISH" }, { 556, "WIT" }, { 2023, "WITH" }, { 557, "WOK" }, { 2024, "WOLF" }, { 558, "WON" }, { 2025, "WONT" }, { 559, "WOO" }, { 2026, "WOOD" }, { 2027, "WOOL" }, { 2028, "WORD" }, { 2029, "WORE" }, { 2030, "WORK" }, { 2031, "WORM" }, { 2032, "WORN" }, { 2033, "WOVE" }, { 560, "WOW" }, { 2034, "WRIT" }, { 561, "WRY" }, { 562, "WU" }, { 2035, "WYNN" }, { 2036, "YALE" }, { 563, "YAM" }, { 2037, "YANG" }, { 2038, "YANK" }, { 564, "YAP" }, { 2039, "YARD" }, { 2040, "YARN" }, { 565, "YAW" }, { 2041, "YAWL" }, { 2042, "YAWN" }, { 566, "YE" }, { 567, "YEA" }, { 2043, "YEAH" }, { 2044, "YEAR" }, { 2045, "YELL" }, { 568, "YES" }, { 569, "YET" }, { 2046, "YOGA" }, { 2047, "YOKE" }, { 570, "YOU" }, }; int otp_lookup_word(const char *word) { int l, u, idx, c; int first = *word - 'A'; if ((first < 0) || (first > 'Y' - 'A')) return -1; l = hints[first].l; u = hints[first].u; while (l < u) { idx = (l + u) / 2; c = strncmp(word, dictionary[idx].word, 4); if (c < 0) u = idx; else if (c > 0) l = idx + 1; else return dictionary[idx].value; } return -1; } dovecot-2.2.33.2/src/lib-otp/Makefile.am0000644000175000017500000000036713165463624014536 00000000000000noinst_LIBRARIES = libotp.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libotp_a_SOURCES = \ otp-dictionary.c \ otp-hash.c \ otp-parity.c \ otp-parse.c noinst_HEADERS = \ otp.h \ otp-dictionary.h \ otp-hash.h \ otp-parity.h \ otp-parse.h dovecot-2.2.33.2/src/doveadm/0002755000175000017500000000000013172375612012624 500000000000000dovecot-2.2.33.2/src/doveadm/doveadm-mail-deduplicate.c0000644000175000017500000001401013165463624017525 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-storage.h" #include "mail-search-build.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" struct uidlist { struct uidlist *next; uint32_t uid; }; struct deduplicate_cmd_context { struct doveadm_mail_cmd_context ctx; bool by_msgid; }; static int cmd_deduplicate_uidlist(struct doveadm_mail_cmd_context *_ctx, struct mailbox *box, struct uidlist *uidlist) { struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct mail_search_arg *arg; struct mail *mail; ARRAY_TYPE(seq_range) uids; int ret = 0; /* the uidlist is reversed with oldest mails at the end. we'll delete everything but the oldest mail. */ if (uidlist->next == NULL) return 0; t_array_init(&uids, 8); for (; uidlist->next != NULL; uidlist = uidlist->next) seq_range_array_add(&uids, uidlist->uid); search_args = mail_search_build_init(); arg = mail_search_build_add(search_args, SEARCH_UIDSET); arg->value.seqset = uids; trans = mailbox_transaction_begin(box, 0); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) mail_expunge(mail); if (mailbox_search_deinit(&search_ctx) < 0) { i_error("Searching mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } if (mailbox_transaction_commit(&trans) < 0) { i_error("Committing mailbox '%s' transaction failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } return ret; } static int cmd_deduplicate_box(struct doveadm_mail_cmd_context *_ctx, const struct mailbox_info *info, struct mail_search_args *search_args) { struct deduplicate_cmd_context *ctx = (struct deduplicate_cmd_context *)_ctx; struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; enum mail_error error; pool_t pool; HASH_TABLE(const char *, struct uidlist *) hash; const char *key, *errstr; struct uidlist *value; int ret = 0; if (doveadm_mail_iter_init(_ctx, info, search_args, 0, NULL, FALSE, &iter) < 0) return -1; pool = pool_alloconly_create("deduplicate", 10240); hash_table_create(&hash, pool, 0, str_hash, strcmp); while (doveadm_mail_iter_next(iter, &mail)) { if (ctx->by_msgid) { if (mail_get_first_header(mail, "Message-ID", &key) < 0) { errstr = mailbox_get_last_internal_error(mail->box, &error); if (error == MAIL_ERROR_NOTFOUND) continue; i_error("Couldn't lookup Message-ID: for UID=%u: %s", mail->uid, errstr); doveadm_mail_failed_error(_ctx, error); ret = -1; break; } } else { if (mail_get_special(mail, MAIL_FETCH_GUID, &key) < 0) { errstr = mailbox_get_last_internal_error(mail->box, &error); if (error == MAIL_ERROR_NOTFOUND) continue; i_error("Couldn't lookup GUID: for UID=%u: %s", mail->uid, errstr); doveadm_mail_failed_error(_ctx, error); ret = -1; break; } } if (key != NULL && *key != '\0') { value = p_new(pool, struct uidlist, 1); value->uid = mail->uid; value->next = hash_table_lookup(hash, key); if (value->next == NULL) { key = p_strdup(pool, key); hash_table_insert(hash, key, value); } else { hash_table_update(hash, key, value); } } } if (doveadm_mail_iter_deinit_keep_box(&iter, &box) < 0) ret = -1; if (ret == 0) { struct hash_iterate_context *iter; iter = hash_table_iterate_init(hash); while (hash_table_iterate(iter, hash, &key, &value)) { T_BEGIN { if (cmd_deduplicate_uidlist(_ctx, box, value) < 0) ret = -1; } T_END; } hash_table_iterate_deinit(&iter); } hash_table_destroy(&hash); pool_unref(&pool); if (mailbox_sync(box, 0) < 0) { i_error("Syncing mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } mailbox_free(&box); return ret; } static int cmd_deduplicate_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_deduplicate_box(ctx, info, ctx->search_args) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_deduplicate_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("deduplicate"); ctx->search_args = doveadm_mail_build_search_args(args); } static bool cmd_deduplicate_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct deduplicate_cmd_context *ctx = (struct deduplicate_cmd_context *)_ctx; switch (c) { case 'm': ctx->by_msgid = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_deduplicate_alloc(void) { struct deduplicate_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct deduplicate_cmd_context); ctx->ctx.getopt_args = "m"; ctx->ctx.v.parse_arg = cmd_deduplicate_parse_arg; ctx->ctx.v.init = cmd_deduplicate_init; ctx->ctx.v.run = cmd_deduplicate_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_deduplicate_ver2 = { .name = "deduplicate", .mail_cmd = cmd_deduplicate_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-m] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('m', "by-msgid", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-util.h0000644000175000017500000000213213165463543015306 00000000000000#ifndef DOVEADM_UTIL_H #define DOVEADM_UTIL_H #include "net.h" #define DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR 1 #define DOVEADM_SERVER_PROTOCOL_VERSION_MINOR 1 #define DOVEADM_SERVER_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-server\t1\t1" #define DOVEADM_CLIENT_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-client\t1\t1" extern bool doveadm_verbose, doveadm_debug, doveadm_server; const char *unixdate2str(time_t timestamp); const char *doveadm_plugin_getenv(const char *name); int doveadm_connect(const char *path); int doveadm_tcp_connect(const char *target, in_port_t default_port); int doveadm_connect_with_default_port(const char *path, in_port_t default_port); void doveadm_load_modules(void); void doveadm_unload_modules(void); bool doveadm_has_unloaded_plugin(const char *name); char doveadm_log_type_to_char(enum log_type type) ATTR_PURE; bool doveadm_log_type_from_char(char c, enum log_type *type_r); /* Similar to strcmp(), except "camel case" == "camel-case" == "camelCase". Otherwise the comparison is case-sensitive. */ int i_strccdascmp(const char *a, const char *b) ATTR_PURE; #endif dovecot-2.2.33.2/src/doveadm/doveadm-who.c0000644000175000017500000002157213144653606015130 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "istream.h" #include "wildcard-match.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "doveadm.h" #include "doveadm-print.h" #include "doveadm-who.h" #include #include struct who_user { const char *username; const char *service; ARRAY(struct ip_addr) ips; ARRAY(pid_t) pids; unsigned int connection_count; }; static void who_user_ip(const struct who_user *user, struct ip_addr *ip_r) { if (array_count(&user->ips) == 0) i_zero(ip_r); else { const struct ip_addr *ip = array_idx(&user->ips, 0); *ip_r = *ip; } } static unsigned int who_user_hash(const struct who_user *user) { struct ip_addr ip; unsigned int hash = str_hash(user->service); if (user->username[0] != '\0') hash += str_hash(user->username); else { who_user_ip(user, &ip); hash += net_ip_hash(&ip); } return hash; } static int who_user_cmp(const struct who_user *user1, const struct who_user *user2) { if (strcmp(user1->username, user2->username) != 0) return 1; if (strcmp(user1->service, user2->service) != 0) return 1; if (user1->username[0] == '\0') { /* tracking only IP addresses, not usernames */ struct ip_addr ip1, ip2; who_user_ip(user1, &ip1); who_user_ip(user2, &ip2); return net_ip_cmp(&ip1, &ip2); } return 0; } static bool who_user_has_ip(const struct who_user *user, const struct ip_addr *ip) { const struct ip_addr *ex_ip; array_foreach(&user->ips, ex_ip) { if (net_ip_compare(ex_ip, ip)) return TRUE; } return FALSE; } static int who_parse_line(const char *line, struct who_line *line_r) { const char *const *args = t_strsplit_tabescaped(line); const char *ident = args[0]; const char *pid_str = args[1]; const char *refcount_str = args[2]; const char *p, *ip_str; i_zero(line_r); /* ident = service/ip/username (imap, pop3) or service/username (lmtp) */ p = strchr(ident, '/'); if (p == NULL) return -1; if (str_to_pid(pid_str, &line_r->pid) < 0) return -1; line_r->service = t_strdup_until(ident, p++); line_r->username = strchr(p, '/'); if (line_r->username == NULL) { /* no IP */ line_r->username = p; } else { ip_str = t_strdup_until(p, line_r->username++); (void)net_addr2ip(ip_str, &line_r->ip); } if (str_to_uint(refcount_str, &line_r->refcount) < 0) return -1; return 0; } static bool who_user_has_pid(struct who_user *user, pid_t pid) { const pid_t *ex_pid; array_foreach(&user->pids, ex_pid) { if (*ex_pid == pid) return TRUE; } return FALSE; } static void who_aggregate_line(struct who_context *ctx, const struct who_line *line) { struct who_user *user, lookup_user; lookup_user.username = line->username; lookup_user.service = line->service; user = hash_table_lookup(ctx->users, &lookup_user); if (user == NULL) { user = p_new(ctx->pool, struct who_user, 1); user->username = p_strdup(ctx->pool, line->username); user->service = p_strdup(ctx->pool, line->service); p_array_init(&user->ips, ctx->pool, 3); p_array_init(&user->pids, ctx->pool, 8); hash_table_insert(ctx->users, user, user); } user->connection_count += line->refcount; if (line->ip.family != 0 && !who_user_has_ip(user, &line->ip)) array_append(&user->ips, &line->ip, 1); if (!who_user_has_pid(user, line->pid)) array_append(&user->pids, &line->pid, 1); } int who_parse_args(struct who_context *ctx, const char *const *masks) { struct ip_addr net_ip; unsigned int i, net_bits; for (i = 0; masks[i] != NULL; i++) { if (net_parse_range(masks[i], &net_ip, &net_bits) == 0) { if (ctx->filter.net_bits != 0) { i_error("Multiple network masks not supported"); doveadm_exit_code = EX_USAGE; return -1; } ctx->filter.net_ip = net_ip; ctx->filter.net_bits = net_bits; } else { if (ctx->filter.username != NULL) { i_error("Multiple username masks not supported"); doveadm_exit_code = EX_USAGE; return -1; } ctx->filter.username = masks[i]; } } return 0; } void who_lookup(struct who_context *ctx, who_callback_t *callback) { #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" #define ANVIL_CMD ANVIL_HANDSHAKE"CONNECT-DUMP\n" struct istream *input; const char *line; int fd; fd = doveadm_connect(ctx->anvil_path); net_set_nonblock(fd, FALSE); if (write(fd, ANVIL_CMD, strlen(ANVIL_CMD)) < 0) i_fatal("write(%s) failed: %m", ctx->anvil_path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { if (*line == '\0') break; T_BEGIN { struct who_line who_line; if (who_parse_line(line, &who_line) < 0) i_error("Invalid input: %s", line); else callback(ctx, &who_line); } T_END; } if (input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->anvil_path, i_stream_get_error(input)); } i_stream_destroy(&input); } static bool who_user_filter_match(const struct who_user *user, const struct who_filter *filter) { if (filter->username != NULL) { if (!wildcard_match_icase(user->username, filter->username)) return FALSE; } if (filter->net_bits > 0) { const struct ip_addr *ip; bool ret = FALSE; array_foreach(&user->ips, ip) { if (net_is_in_network(ip, &filter->net_ip, filter->net_bits)) { ret = TRUE; break; } } if (!ret) return FALSE; } return TRUE; } static void who_print_user(const struct who_user *user) { const struct ip_addr *ip; const pid_t *pid; string_t *str = t_str_new(256); doveadm_print(user->username); doveadm_print(dec2str(user->connection_count)); doveadm_print(user->service); str_append_c(str, '('); array_foreach(&user->pids, pid) str_printfa(str, "%ld ", (long)*pid); if (str_len(str) > 1) str_truncate(str, str_len(str)-1); str_append_c(str, ')'); doveadm_print(str_c(str)); str_truncate(str, 0); str_append_c(str, '('); array_foreach(&user->ips, ip) str_printfa(str, "%s ", net_ip2addr(ip)); if (str_len(str) > 1) str_truncate(str, str_len(str)-1); str_append_c(str, ')'); doveadm_print(str_c(str)); } static void who_print(struct who_context *ctx) { struct hash_iterate_context *iter; struct who_user *user; doveadm_print_header("username", "username", 0); doveadm_print_header("connections", "#", DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY); doveadm_print_header("service", "proto", 0); doveadm_print_header("pids", "(pids)", 0); doveadm_print_header("ips", "(ips)", 0); iter = hash_table_iterate_init(ctx->users); while (hash_table_iterate(iter, ctx->users, &user, &user)) { if (who_user_filter_match(user, &ctx->filter)) T_BEGIN { who_print_user(user); } T_END; } hash_table_iterate_deinit(&iter); } bool who_line_filter_match(const struct who_line *line, const struct who_filter *filter) { if (filter->username != NULL) { if (!wildcard_match_icase(line->username, filter->username)) return FALSE; } if (filter->net_bits > 0) { if (!net_is_in_network(&line->ip, &filter->net_ip, filter->net_bits)) return FALSE; } return TRUE; } static void who_print_line(struct who_context *ctx, const struct who_line *line) { unsigned int i; if (!who_line_filter_match(line, &ctx->filter)) return; for (i = 0; i < line->refcount; i++) T_BEGIN { doveadm_print(line->username); doveadm_print(line->service); doveadm_print(dec2str(line->pid)); doveadm_print(net_ip2addr(&line->ip)); } T_END; } static void cmd_who(struct doveadm_cmd_context *cctx) { const char *const *masks; struct who_context ctx; bool separate_connections = FALSE; i_zero(&ctx); if (!doveadm_cmd_param_str(cctx, "socket-path", &(ctx.anvil_path))) ctx.anvil_path = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL); (void)doveadm_cmd_param_bool(cctx, "separate-connections", &separate_connections); ctx.pool = pool_alloconly_create("who users", 10240); hash_table_create(&ctx.users, ctx.pool, 0, who_user_hash, who_user_cmp); if (doveadm_cmd_param_array(cctx, "mask", &masks)) { if (who_parse_args(&ctx, masks) != 0) { hash_table_destroy(&ctx.users); pool_unref(&ctx.pool); return; } } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); if (!separate_connections) { who_lookup(&ctx, who_aggregate_line); who_print(&ctx); } else { doveadm_print_header("username", "username", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header("service", "proto", 0); doveadm_print_header_simple("pid"); doveadm_print_header_simple("ip"); who_lookup(&ctx, who_print_line); } hash_table_destroy(&ctx.users); pool_unref(&ctx.pool); } struct doveadm_cmd_ver2 doveadm_cmd_who_ver2 = { .name = "who", .cmd = cmd_who, .usage = "[-a ] [-1] [] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a',"socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('1',"separate-connections", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0',"mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-sis.c0000644000175000017500000001664413165463624015137 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hex-binary.h" #include "hostpid.h" #include "randgen.h" #include "read-full.h" #include "fs-sis-common.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include #include #include /* Files are in /ha/sh/- They may be hard linked to hashes/ */ static const char *sis_get_dir(const char *rootdir, const char *hash) { if (strlen(hash) < 4 || strchr(hash, '/') != NULL) i_fatal("Invalid hash in filename: %s", hash); return t_strdup_printf("%s/%c%c/%c%c", rootdir, hash[0], hash[1], hash[2], hash[3]); } static int file_contents_equal(const char *path1, const char *path2, ino_t *path2_inode_r) { struct stat st1, st2; int fd1, fd2, ret = -1; *path2_inode_r = 0; /* do a byte-by-byte comparison for the files to find out if they're the same or if this is a hash collision */ fd1 = open(path1, O_RDONLY); if (fd1 == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", path1); return -1; } fd2 = open(path2, O_RDONLY); if (fd2 == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", path2); i_close_fd(&fd1); return -1; } if (fstat(fd1, &st1) < 0) i_error("fstat(%s) failed: %m", path1); else if (fstat(fd2, &st2) < 0) i_error("fstat(%s) failed: %m", path1); else if (st1.st_size != st2.st_size) ret = 0; else { /* @UNSAFE: sizes match. compare. */ unsigned char buf1[IO_BLOCK_SIZE], buf2[IO_BLOCK_SIZE]; ssize_t ret1; int ret2; while ((ret1 = read(fd1, buf1, sizeof(buf1))) > 0) { if ((ret2 = read_full(fd2, buf2, ret1)) <= 0) { if (ret2 < 0) i_error("read(%s) failed: %m", path2); else ret = 0; break; } if (memcmp(buf1, buf2, ret1) != 0) { ret = 0; break; } } if (ret1 < 0) i_error("read(%s) failed: %m", path1); else if (ret1 == 0) ret = 1; *path2_inode_r = st2.st_ino; } if (close(fd1) < 0) i_error("close(%s) failed: %m", path1); if (close(fd2) < 0) i_error("close(%s) failed: %m", path2); return ret; } static int hardlink_replace(const char *src, const char *dest, ino_t src_inode) { const char *p, *destdir, *tmppath; unsigned char randbuf[8]; struct stat st; p = strrchr(dest, '/'); i_assert(p != NULL); destdir = t_strdup_until(dest, p); random_fill_weak(randbuf, sizeof(randbuf)); tmppath = t_strdup_printf("%s/temp.%s.%s.%s", destdir, my_hostname, my_pid, binary_to_hex(randbuf, sizeof(randbuf))); if (link(src, tmppath) < 0) { if (errno == EMLINK) return 0; i_error("link(%s, %s) failed: %m", src, tmppath); return -1; } if (stat(tmppath, &st) < 0) { i_error("stat(%s) failed: %m", tmppath); return -1; } if (st.st_ino != src_inode) { i_unlink(tmppath); return 0; } if (rename(tmppath, dest) < 0) { i_error("rename(%s, %s) failed: %m", src, tmppath); i_unlink(tmppath); return -1; } return 1; } static int sis_try_deduplicate(const char *rootdir, const char *fname) { const char *p, *hash, *hashdir, *path, *hashes_dir, *hashes_path; struct stat st; ino_t inode; int ret; /* fname should be in - format */ p = strchr(fname, '-'); i_assert(p != NULL); hash = t_strdup_until(fname, p); hashdir = sis_get_dir(rootdir, hash); path = t_strdup_printf("%s/%s", hashdir, fname); hashes_dir = t_strconcat(hashdir, "/", HASH_DIR_NAME, NULL); hashes_path = t_strconcat(hashes_dir, "/", hash, NULL); if (link(path, hashes_path) == 0) { /* first file with this hash. we're done */ return 0; } if (errno == ENOENT) { /* either path was already deleted or hashes dir doesn't exist */ if (mkdir(hashes_dir, 0700) < 0) { if (errno == EEXIST) return 0; i_error("mkdir(%s) failed: %m", hashes_dir); return -1; } /* try again */ if (link(path, hashes_path) == 0 || errno == ENOENT) return 0; } if (errno != EEXIST) { i_error("link(%s, %s) failed: %m", path, hashes_path); return -1; } /* need to do a byte-by-byte comparison. but check first if someone else already had deduplicated the file. */ if (stat(path, &st) < 0) { if (errno == ENOENT) { /* just got deleted */ return 0; } i_error("stat(%s) failed: %m", path); return -1; } if (st.st_nlink > 1) { /* already deduplicated */ return 0; } ret = file_contents_equal(path, hashes_path, &inode); if (ret < 0) { if (errno == ENOENT) { /* either path or hashes_path was deleted. */ return sis_try_deduplicate(rootdir, fname); } return -1; } if (ret > 0) { /* equal, replace with hard link */ ret = hardlink_replace(hashes_path, path, inode); if (ret > 0) return 0; else if (ret < 0) return -1; /* too many hard links or inode changed */ } /* replace hashes link with this */ return hardlink_replace(path, hashes_path, st.st_ino) < 0 ? -1 : 0; } static void cmd_sis_deduplicate(int argc, char *argv[]) { const char *rootdir, *queuedir; DIR *dir; struct dirent *d; struct stat st, first_st; string_t *path; size_t dir_len; int ret; if (argc < 3) help(&doveadm_cmd_sis_deduplicate); /* go through the filenames in the queue dir and see if we can deduplicate them. */ rootdir = argv[1]; queuedir = argv[2]; if (stat(rootdir, &st) < 0) i_fatal("stat(%s) failed: %m", rootdir); path = t_str_new(256); str_append(path, queuedir); str_append_c(path, '/'); dir_len = str_len(path); dir = opendir(queuedir); if (dir == NULL) i_fatal("opendir(%s) failed: %m", queuedir); first_st.st_size = -1; while ((d = readdir(dir)) != NULL) { if (d->d_name[0] == '.') continue; str_truncate(path, dir_len); str_append(path, d->d_name); if (first_st.st_size < 0) { if (stat(str_c(path), &first_st) < 0) i_fatal("stat(%s) failed: %m", str_c(path)); } if (strchr(d->d_name, '-') == NULL || first_st.st_size != 0) { i_fatal("%s is not a valid sis-queue file, " "is the queue directory correct?", str_c(path)); } T_BEGIN { ret = sis_try_deduplicate(rootdir, d->d_name); } T_END; if (ret == 0) i_unlink(str_c(path)); } if (closedir(dir) < 0) i_error("closedir(%s) failed: %m", queuedir); } static void cmd_sis_find(int argc, char *argv[]) { const char *rootdir, *path, *hash; DIR *dir; struct dirent *d; struct stat st; string_t *str; size_t dir_len, hash_len; if (argc < 3 || strlen(argv[2]) < 4) help(&doveadm_cmd_sis_find); rootdir = argv[1]; if (stat(rootdir, &st) < 0) { if (errno == ENOENT) i_fatal("Attachment dir doesn't exist: %s", rootdir); i_fatal("stat(%s) failed: %m", rootdir); } hash = argv[2]; hash_len = strlen(hash); path = sis_get_dir(rootdir, hash); str = t_str_new(256); str_append(str, path); str_append_c(str, '/'); dir_len = str_len(str); dir = opendir(path); if (dir == NULL) { if (errno == ENOENT) return; i_fatal("opendir(%s) failed: %m", path); } doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); doveadm_print_header("path", "path", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); while ((d = readdir(dir)) != NULL) { if (strncmp(d->d_name, hash, hash_len) == 0) { str_truncate(str, dir_len); str_append(str, d->d_name); doveadm_print(str_c(str)); } } if (closedir(dir) < 0) i_error("closedir(%s) failed: %m", path); } struct doveadm_cmd doveadm_cmd_sis_deduplicate = { cmd_sis_deduplicate, "sis deduplicate", " " }; struct doveadm_cmd doveadm_cmd_sis_find = { cmd_sis_find, "sis find", " " }; dovecot-2.2.33.2/src/doveadm/doveadm-mail.c0000644000175000017500000007253713165463624015266 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "lib-signals.h" #include "ioloop.h" #include "istream.h" #include "istream-dot.h" #include "istream-seekable.h" #include "str.h" #include "unichar.h" #include "module-dir.h" #include "wildcard-match.h" #include "master-service.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-settings.h" #include "mail-storage-service.h" #include "mail-storage-hooks.h" #include "mail-search-build.h" #include "mail-search-parser.h" #include "mailbox-list-iter.h" #include "client-connection.h" #include "doveadm.h" #include "doveadm-settings.h" #include "doveadm-print.h" #include "doveadm-dsync.h" #include "doveadm-mail.h" #include #define DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS (5*60*1000) ARRAY_TYPE(doveadm_mail_cmd) doveadm_mail_cmds; void (*hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx); struct doveadm_mail_cmd_module_register doveadm_mail_cmd_module_register = { 0 }; char doveadm_mail_cmd_hide = '\0'; static int killed_signo = 0; bool doveadm_is_killed(void) { return killed_signo != 0; } int doveadm_killed_signo(void) { return killed_signo; } void doveadm_mail_failed_error(struct doveadm_mail_cmd_context *ctx, enum mail_error error) { int exit_code = EX_TEMPFAIL; switch (error) { case MAIL_ERROR_NONE: i_unreached(); case MAIL_ERROR_TEMP: case MAIL_ERROR_UNAVAILABLE: break; case MAIL_ERROR_NOTPOSSIBLE: case MAIL_ERROR_EXISTS: case MAIL_ERROR_CONVERSION: case MAIL_ERROR_INVALIDDATA: exit_code = DOVEADM_EX_NOTPOSSIBLE; break; case MAIL_ERROR_PARAMS: exit_code = EX_USAGE; break; case MAIL_ERROR_PERM: exit_code = EX_NOPERM; break; case MAIL_ERROR_NOQUOTA: exit_code = EX_CANTCREAT; break; case MAIL_ERROR_NOTFOUND: exit_code = DOVEADM_EX_NOTFOUND; break; case MAIL_ERROR_EXPUNGED: case MAIL_ERROR_INUSE: break; case MAIL_ERROR_LIMIT: exit_code = DOVEADM_EX_NOTPOSSIBLE; break; case MAIL_ERROR_LOOKUP_ABORTED: break; } /* tempfail overrides all other exit codes, otherwise use whatever error happened first */ if (ctx->exit_code == 0 || exit_code == EX_TEMPFAIL) ctx->exit_code = exit_code; } void doveadm_mail_failed_storage(struct doveadm_mail_cmd_context *ctx, struct mail_storage *storage) { enum mail_error error; mail_storage_get_last_error(storage, &error); doveadm_mail_failed_error(ctx, error); } void doveadm_mail_failed_mailbox(struct doveadm_mail_cmd_context *ctx, struct mailbox *box) { doveadm_mail_failed_storage(ctx, mailbox_get_storage(box)); } void doveadm_mail_failed_list(struct doveadm_mail_cmd_context *ctx, struct mailbox_list *list) { enum mail_error error; mailbox_list_get_last_error(list, &error); doveadm_mail_failed_error(ctx, error); } struct doveadm_mail_cmd_context * doveadm_mail_cmd_alloc_size(size_t size) { struct doveadm_mail_cmd_context *ctx; pool_t pool; i_assert(size >= sizeof(struct doveadm_mail_cmd_context)); pool = pool_alloconly_create("doveadm mail cmd", 1024); ctx = p_malloc(pool, size); ctx->pool = pool; ctx->cmd_input_fd = -1; return ctx; } static int cmd_purge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { struct mail_namespace *ns; struct mail_storage *storage; int ret = 0; for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE || ns->alias_for != NULL) continue; storage = mail_namespace_get_default_storage(ns); if (mail_storage_purge(storage) < 0) { i_error("Purging namespace '%s' failed: %s", ns->prefix, mail_storage_get_last_internal_error(storage, NULL)); doveadm_mail_failed_storage(ctx, storage); ret = -1; } } return ret; } static struct doveadm_mail_cmd_context *cmd_purge_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_purge_run; return ctx; } static void doveadm_mail_cmd_input_input(struct doveadm_mail_cmd_context *ctx) { const unsigned char *data; size_t size; while (i_stream_read_more(ctx->cmd_input, &data, &size) > 0) i_stream_skip(ctx->cmd_input, size); if (!ctx->cmd_input->eof) return; if (ctx->cmd_input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(ctx->cmd_input), i_stream_get_error(ctx->cmd_input)); } io_loop_stop(current_ioloop); } static void doveadm_mail_cmd_input_timeout(struct doveadm_mail_cmd_context *ctx) { struct istream *input; input = i_stream_create_error_str(ETIMEDOUT, "Timed out in %u secs", DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS/1000); i_stream_set_name(input, i_stream_get_name(ctx->cmd_input)); i_stream_destroy(&ctx->cmd_input); ctx->cmd_input = input; ctx->exit_code = EX_TEMPFAIL; io_loop_stop(current_ioloop); } static void doveadm_mail_cmd_input_read(struct doveadm_mail_cmd_context *ctx) { struct ioloop *ioloop; struct io *io; struct timeout *to; ioloop = io_loop_create(); io = io_add(ctx->cmd_input_fd, IO_READ, doveadm_mail_cmd_input_input, ctx); to = timeout_add(DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS, doveadm_mail_cmd_input_timeout, ctx); /* read the pending input from stream. */ io_loop_set_running(ioloop); doveadm_mail_cmd_input_input(ctx); if (io_loop_is_running(ioloop)) io_loop_run(ioloop); io_remove(&io); timeout_remove(&to); io_loop_destroy(&ioloop); i_assert(ctx->cmd_input->eof); i_stream_seek(ctx->cmd_input, 0); } void doveadm_mail_get_input(struct doveadm_mail_cmd_context *ctx) { struct istream *inputs[2]; if (ctx->cmd_input != NULL) return; if (!ctx->cli && ctx->conn == NULL) { ctx->cmd_input = i_stream_create_error_str(EINVAL, "Input stream missing (provide with file parameter)"); return; } if (ctx->conn != NULL) inputs[0] = i_stream_create_dot(ctx->conn->input, FALSE); else { inputs[0] = i_stream_create_fd(STDIN_FILENO, 1024*1024, FALSE); i_stream_set_name(inputs[0], "stdin"); } inputs[1] = NULL; ctx->cmd_input_fd = i_stream_get_fd(inputs[0]); ctx->cmd_input = i_stream_create_seekable_path(inputs, 1024*256, "/tmp/doveadm."); i_stream_set_name(ctx->cmd_input, i_stream_get_name(inputs[0])); i_stream_unref(&inputs[0]); doveadm_mail_cmd_input_read(ctx); } struct mailbox * doveadm_mailbox_find(struct mail_user *user, const char *mailbox) { struct mail_namespace *ns; if (!uni_utf8_str_is_valid(mailbox)) { i_fatal_status(EX_DATAERR, "Mailbox name not valid UTF-8: %s", mailbox); } ns = mail_namespace_find(user->namespaces, mailbox); return mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_IGNORE_ACLS); } struct mail_search_args * doveadm_mail_build_search_args(const char *const args[]) { struct mail_search_parser *parser; struct mail_search_args *sargs; const char *error, *charset = "UTF-8"; parser = mail_search_parser_init_cmdline(args); if (mail_search_build(mail_search_register_get_human(), parser, &charset, &sargs, &error) < 0) i_fatal("%s", error); mail_search_parser_deinit(&parser); return sargs; } static int cmd_force_resync_box(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info) { struct mailbox *box; int ret = 0; box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, ctx->cmd->name); if (mailbox_open(box) < 0) { i_error("Opening mailbox %s failed: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(ctx, box); ret = -1; } else if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FORCE_RESYNC | MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) < 0) { i_error("Forcing a resync on mailbox %s failed: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(ctx, box); ret = -1; } mailbox_free(&box); return ret; } static int cmd_force_resync_prerun(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, struct mail_storage_service_user *service_user, const char **error_r) { if (mail_storage_service_user_set_setting(service_user, "mailbox_list_index_very_dirty_syncs", "no", error_r) <= 0) i_unreached(); return 0; } static int cmd_force_resync_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_STAR_WITHIN_NS; const enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_MASK_ALL; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; int ret = 0; iter = mailbox_list_iter_init_namespaces(user->namespaces, ctx->args, ns_mask, iter_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) T_BEGIN { if (cmd_force_resync_box(ctx, info) < 0) ret = -1; } T_END; } if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Listing mailboxes failed: %s", mailbox_list_get_last_internal_error(user->namespaces->list, NULL)); doveadm_mail_failed_list(ctx, user->namespaces->list); ret = -1; } return ret; } static void cmd_force_resync_init(struct doveadm_mail_cmd_context *_ctx ATTR_UNUSED, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("force-resync"); } static struct doveadm_mail_cmd_context *cmd_force_resync_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.init = cmd_force_resync_init; ctx->v.run = cmd_force_resync_run; ctx->v.prerun = cmd_force_resync_prerun; return ctx; } static void doveadm_cctx_to_storage_service_input(const struct doveadm_cmd_context *cctx, struct mail_storage_service_input *input_r) { i_zero(input_r); input_r->service = "doveadm"; input_r->remote_ip = cctx->remote_ip; input_r->remote_port = cctx->remote_port; input_r->local_ip = cctx->local_ip; input_r->local_port = cctx->local_port; input_r->username = cctx->username; } static int doveadm_mail_next_user(struct doveadm_mail_cmd_context *ctx, const struct doveadm_cmd_context *cctx, const char **error_r) { struct mail_storage_service_input input; const char *error, *ip; int ret; ip = net_ip2addr(&cctx->remote_ip); if (ip[0] == '\0') i_set_failure_prefix("doveadm(%s): ", cctx->username); else i_set_failure_prefix("doveadm(%s,%s): ", ip, cctx->username); doveadm_cctx_to_storage_service_input(cctx, &input); if (ctx->cmd_input != NULL) i_stream_seek(ctx->cmd_input, 0); /* see if we want to execute this command via (another) doveadm server */ ret = doveadm_mail_server_user(ctx, &input, error_r); if (ret != 0) return ret; ret = mail_storage_service_lookup(ctx->storage_service, &input, &ctx->cur_service_user, &error); if (ret <= 0) { if (ret < 0) { *error_r = t_strdup_printf("User lookup failed: %s", error); } return ret; } if (ctx->v.prerun != NULL) { if (ctx->v.prerun(ctx, ctx->cur_service_user, error_r) < 0) { mail_storage_service_user_unref(&ctx->cur_service_user); return -1; } } ret = mail_storage_service_next(ctx->storage_service, ctx->cur_service_user, &ctx->cur_mail_user); if (ret < 0) { *error_r = "User init failed"; mail_storage_service_user_unref(&ctx->cur_service_user); return ret; } if (ctx->v.run(ctx, ctx->cur_mail_user) < 0) { i_assert(ctx->exit_code != 0); } mail_user_unref(&ctx->cur_mail_user); mail_storage_service_user_unref(&ctx->cur_service_user); return 1; } static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED) { killed_signo = si->si_signo; } int doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx, const struct doveadm_cmd_context *cctx, const char **error_r) { i_assert(cctx->username != NULL); doveadm_cctx_to_storage_service_input(cctx, &ctx->storage_service_input); ctx->cur_client_ip = cctx->remote_ip; ctx->cur_username = cctx->username; ctx->storage_service = mail_storage_service_init(master_service, NULL, ctx->service_flags); ctx->v.init(ctx, ctx->args); if (hook_doveadm_mail_init != NULL) hook_doveadm_mail_init(ctx); lib_signals_set_handler(SIGINT, 0, sig_die, NULL); lib_signals_set_handler(SIGTERM, 0, sig_die, NULL); return doveadm_mail_next_user(ctx, cctx, error_r); } static void doveadm_mail_all_users(struct doveadm_mail_cmd_context *ctx, struct doveadm_cmd_context *cctx, const char *wildcard_user) { unsigned int user_idx; const char *ip, *user, *error; int ret; ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; doveadm_cctx_to_storage_service_input(cctx, &ctx->storage_service_input); ctx->storage_service = mail_storage_service_init(master_service, NULL, ctx->service_flags); lib_signals_set_handler(SIGINT, 0, sig_die, NULL); lib_signals_set_handler(SIGTERM, 0, sig_die, NULL); ctx->v.init(ctx, ctx->args); mail_storage_service_all_init_mask(ctx->storage_service, wildcard_user != NULL ? wildcard_user : ""); if (hook_doveadm_mail_init != NULL) hook_doveadm_mail_init(ctx); user_idx = 0; while ((ret = ctx->v.get_next_user(ctx, &user)) > 0) { if (wildcard_user != NULL) { if (!wildcard_match_icase(user, wildcard_user)) continue; } cctx->username = user; ctx->cur_username = user; doveadm_print_sticky("username", user); T_BEGIN { ret = doveadm_mail_next_user(ctx, cctx, &error); if (ret < 0) i_error("%s", error); else if (ret == 0) i_info("User no longer exists, skipping"); } T_END; if (ret == -1) break; if (doveadm_verbose) { if (++user_idx % 100 == 0) { printf("\r%d", user_idx); fflush(stdout); } } if (killed_signo != 0) { i_warning("Killed with signal %d", killed_signo); ret = -1; break; } } if (doveadm_verbose) printf("\n"); ip = net_ip2addr(&ctx->cur_client_ip); if (ip[0] == '\0') i_set_failure_prefix("doveadm: "); else i_set_failure_prefix("doveadm(%s): ", ip); if (ret < 0) { i_error("Failed to iterate through some users"); ctx->exit_code = EX_TEMPFAIL; } } static void doveadm_mail_cmd_init_noop(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[] ATTR_UNUSED) { } static int doveadm_mail_cmd_get_next_user(struct doveadm_mail_cmd_context *ctx, const char **username_r) { if (ctx->users_list_input == NULL) return mail_storage_service_all_next(ctx->storage_service, username_r); *username_r = i_stream_read_next_line(ctx->users_list_input); if (ctx->users_list_input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(ctx->users_list_input), i_stream_get_error(ctx->users_list_input)); return -1; } return *username_r != NULL ? 1 : 0; } static void doveadm_mail_cmd_deinit_noop(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED) { } struct doveadm_mail_cmd_context * doveadm_mail_cmd_init(const struct doveadm_mail_cmd *cmd, const struct doveadm_settings *set) { struct doveadm_mail_cmd_context *ctx; ctx = cmd->alloc(); ctx->set = set; ctx->cmd = cmd; if (ctx->v.init == NULL) ctx->v.init = doveadm_mail_cmd_init_noop; if (ctx->v.get_next_user == NULL) ctx->v.get_next_user = doveadm_mail_cmd_get_next_user; if (ctx->v.deinit == NULL) ctx->v.deinit = doveadm_mail_cmd_deinit_noop; p_array_init(&ctx->module_contexts, ctx->pool, 5); return ctx; } static struct doveadm_mail_cmd_context * doveadm_mail_cmdline_init(const struct doveadm_mail_cmd *cmd) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_init(cmd, doveadm_settings); ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; if (doveadm_debug) ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG; return ctx; } static void doveadm_mail_cmd_exec(struct doveadm_mail_cmd_context *ctx, struct doveadm_cmd_context *cctx, const char *wildcard_user) { int ret; const char *error; if (ctx->v.preinit != NULL) ctx->v.preinit(ctx); ctx->iterate_single_user = !ctx->iterate_all_users && wildcard_user == NULL; if (doveadm_print_is_initialized() && (!ctx->iterate_single_user || ctx->add_username_header)) { doveadm_print_header("username", "Username", DOVEADM_PRINT_HEADER_FLAG_STICKY | DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } if (ctx->iterate_single_user) { if (ctx->cur_username == NULL) i_fatal_status(EX_USAGE, "USER environment is missing and -u option not used"); if (!ctx->cli) { /* we may access multiple users */ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP; } if (ctx->add_username_header) doveadm_print_sticky("username", cctx->username); ret = doveadm_mail_single_user(ctx, cctx, &error); if (ret < 0) { /* user lookup/init failed somehow */ doveadm_exit_code = EX_TEMPFAIL; i_error("%s", error); } else if (ret == 0) { doveadm_exit_code = EX_NOUSER; i_error("User doesn't exist"); } } else { ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP; doveadm_mail_all_users(ctx, cctx, wildcard_user); } if (ctx->search_args != NULL) mail_search_args_unref(&ctx->search_args); doveadm_mail_server_flush(); ctx->v.deinit(ctx); doveadm_print_flush(); /* service deinit unloads mail plugins, so do it late */ mail_storage_service_deinit(&ctx->storage_service); if (ctx->exit_code != 0) doveadm_exit_code = ctx->exit_code; } static void doveadm_mail_cmd_free(struct doveadm_mail_cmd_context *ctx) { if (ctx->users_list_input != NULL) i_stream_unref(&ctx->users_list_input); if (ctx->cmd_input != NULL) i_stream_unref(&ctx->cmd_input); pool_unref(&ctx->pool); } static void doveadm_mail_cmd(const struct doveadm_mail_cmd *cmd, int argc, char *argv[]) { struct doveadm_cmd_context cctx; struct doveadm_mail_cmd_context *ctx; const char *getopt_args, *wildcard_user; int c; ctx = doveadm_mail_cmdline_init(cmd); ctx->full_args = (const void *)(argv + 1); ctx->cli = TRUE; ctx->cur_username = getenv("USER"); i_zero(&cctx); getopt_args = "AF:S:u:"; /* keep context's getopt_args first in case it contains '+' */ if (ctx->getopt_args != NULL) getopt_args = t_strconcat(ctx->getopt_args, getopt_args, NULL); i_assert(master_getopt_str_is_valid(getopt_args)); wildcard_user = NULL; while ((c = getopt(argc, argv, getopt_args)) > 0) { switch (c) { case 'A': ctx->iterate_all_users = TRUE; break; case 'S': doveadm_settings->doveadm_socket_path = optarg; if (doveadm_settings->doveadm_worker_count == 0) doveadm_settings->doveadm_worker_count = 1; break; case 'u': ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; ctx->cur_username = optarg; if (strchr(ctx->cur_username, '*') != NULL || strchr(ctx->cur_username, '?') != NULL) { wildcard_user = ctx->cur_username; ctx->cur_username = NULL; } break; case 'F': ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; wildcard_user = "*"; ctx->users_list_input = i_stream_create_file(optarg, 1024); break; default: if (ctx->v.parse_arg == NULL || !ctx->v.parse_arg(ctx, c)) doveadm_mail_help(cmd); } } argv += optind; if (argv[0] != NULL && cmd->usage_args == NULL) { i_fatal_status(EX_USAGE, "doveadm %s: Unknown parameter: %s", cmd->name, argv[0]); } ctx->args = (const void *)argv; cctx.username = ctx->cur_username; doveadm_mail_cmd_exec(ctx, &cctx, wildcard_user); doveadm_mail_cmd_free(ctx); } static bool doveadm_mail_cmd_try_find_multi_word(const struct doveadm_mail_cmd *cmd, const char *cmdname, int *argc, const char *const **argv) { size_t len; if (*argc < 2) return FALSE; *argc -= 1; *argv += 1; len = strlen((*argv)[0]); if (strncmp(cmdname, (*argv)[0], len) != 0) return FALSE; if (cmdname[len] == ' ') { /* more args */ return doveadm_mail_cmd_try_find_multi_word(cmd, cmdname + len + 1, argc, argv); } if (cmdname[len] != '\0') return FALSE; /* match */ return TRUE; } const struct doveadm_mail_cmd * doveadm_mail_cmd_find_from_argv(const char *cmd_name, int *argc, const char *const **argv) { const struct doveadm_mail_cmd *cmd; size_t cmd_name_len; const char *const *orig_argv; int orig_argc; i_assert(*argc > 0); cmd_name_len = strlen(cmd_name); array_foreach(&doveadm_mail_cmds, cmd) { if (strcmp(cmd->name, cmd_name) == 0) return cmd; /* see if it matches a multi-word command */ if (strncmp(cmd->name, cmd_name, cmd_name_len) == 0 && cmd->name[cmd_name_len] == ' ') { const char *subcmd = cmd->name + cmd_name_len + 1; orig_argc = *argc; orig_argv = *argv; if (doveadm_mail_cmd_try_find_multi_word(cmd, subcmd, argc, argv)) return cmd; *argc = orig_argc; *argv = orig_argv; } } return NULL; } bool doveadm_mail_try_run(const char *cmd_name, int argc, char *argv[]) { const struct doveadm_mail_cmd *cmd; cmd = doveadm_mail_cmd_find_from_argv(cmd_name, &argc, (void *)&argv); if (cmd == NULL) return FALSE; doveadm_mail_cmd(cmd, argc, argv); return TRUE; } void doveadm_mail_register_cmd(const struct doveadm_mail_cmd *cmd) { /* for now we'll just assume that cmd will be permanently in memory */ array_append(&doveadm_mail_cmds, cmd, 1); } const struct doveadm_mail_cmd *doveadm_mail_cmd_find(const char *cmd_name) { const struct doveadm_mail_cmd *cmd; array_foreach(&doveadm_mail_cmds, cmd) { if (strcmp(cmd->name, cmd_name) == 0) return cmd; } return NULL; } void doveadm_mail_usage(string_t *out) { const struct doveadm_mail_cmd *cmd; array_foreach(&doveadm_mail_cmds, cmd) { if (cmd->usage_args == &doveadm_mail_cmd_hide) continue; str_printfa(out, "%s\t"DOVEADM_CMD_MAIL_USAGE_PREFIX, cmd->name); if (cmd->usage_args != NULL) str_append(out, cmd->usage_args); str_append_c(out, '\n'); } } void doveadm_mail_help(const struct doveadm_mail_cmd *cmd) { fprintf(stderr, "doveadm %s "DOVEADM_CMD_MAIL_USAGE_PREFIX" %s\n", cmd->name, cmd->usage_args == NULL ? "" : cmd->usage_args); exit(EX_USAGE); } void doveadm_mail_try_help_name(const char *cmd_name) { const struct doveadm_cmd_ver2 *cmd2; const struct doveadm_mail_cmd *cmd; cmd2 = doveadm_cmd_find_ver2(cmd_name); if (cmd2 != NULL) help_ver2(cmd2); cmd = doveadm_mail_cmd_find(cmd_name); if (cmd != NULL) doveadm_mail_help(cmd); } bool doveadm_mail_has_subcommands(const char *cmd_name) { const struct doveadm_mail_cmd *cmd; size_t len = strlen(cmd_name); array_foreach(&doveadm_mail_cmds, cmd) { if (strncmp(cmd->name, cmd_name, len) == 0 && cmd->name[len] == ' ') return TRUE; } return FALSE; } void doveadm_mail_help_name(const char *cmd_name) { doveadm_mail_try_help_name(cmd_name); i_fatal("Missing help for command %s", cmd_name); } static struct doveadm_cmd_ver2 doveadm_cmd_force_resync_ver2 = { .name = "force-resync", .mail_cmd = cmd_force_resync_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox-mask", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; static struct doveadm_cmd_ver2 doveadm_cmd_purge_ver2 = { .name = "purge", .mail_cmd = cmd_purge_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAMS_END }; static struct doveadm_mail_cmd *mail_commands[] = { &cmd_batch, &cmd_dsync_backup, &cmd_dsync_mirror, &cmd_dsync_server }; static struct doveadm_cmd_ver2 *mail_commands_ver2[] = { &doveadm_cmd_mailbox_metadata_set_ver2, &doveadm_cmd_mailbox_metadata_unset_ver2, &doveadm_cmd_mailbox_metadata_get_ver2, &doveadm_cmd_mailbox_metadata_list_ver2, &doveadm_cmd_mailbox_status_ver2, &doveadm_cmd_mailbox_list_ver2, &doveadm_cmd_mailbox_create_ver2, &doveadm_cmd_mailbox_delete_ver2, &doveadm_cmd_mailbox_rename_ver2, &doveadm_cmd_mailbox_subscribe_ver2, &doveadm_cmd_mailbox_unsubscribe_ver2, &doveadm_cmd_mailbox_update_ver2, &doveadm_cmd_mailbox_path_ver2, &doveadm_cmd_fetch_ver2, &doveadm_cmd_save_ver2, &doveadm_cmd_index_ver2, &doveadm_cmd_altmove_ver2, &doveadm_cmd_deduplicate_ver2, &doveadm_cmd_expunge_ver2, &doveadm_cmd_flags_add_ver2, &doveadm_cmd_flags_remove_ver2, &doveadm_cmd_flags_replace_ver2, &doveadm_cmd_import_ver2, &doveadm_cmd_force_resync_ver2, &doveadm_cmd_purge_ver2, &doveadm_cmd_search_ver2, &doveadm_cmd_copy_ver2, &doveadm_cmd_move_ver2 }; void doveadm_mail_init(void) { struct module_dir_load_settings mod_set; unsigned int i; i_array_init(&doveadm_mail_cmds, 32); for (i = 0; i < N_ELEMENTS(mail_commands); i++) doveadm_mail_register_cmd(mail_commands[i]); for (i = 0; i < N_ELEMENTS(mail_commands_ver2); i++) doveadm_cmd_register_ver2(mail_commands_ver2[i]); i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = doveadm_debug; mod_set.binary_name = "doveadm"; /* load all configured mail plugins */ mail_storage_service_modules = module_dir_load_missing(mail_storage_service_modules, doveadm_settings->mail_plugin_dir, doveadm_settings->mail_plugins, &mod_set); /* keep mail_storage_init() referenced so that its _deinit() doesn't try to free doveadm plugins' hooks too early. */ mail_storage_init(); } void doveadm_mail_deinit(void) { mail_storage_deinit(); array_free(&doveadm_mail_cmds); } void doveadm_cmd_ver2_to_mail_cmd_wrapper(struct doveadm_cmd_context *cctx) { struct doveadm_mail_cmd_context *mctx; const char *wildcard_user; const char *fieldstr; ARRAY_TYPE(const_string) pargv, full_args; int i; struct doveadm_mail_cmd mail_cmd = { cctx->cmd->mail_cmd, cctx->cmd->name, cctx->cmd->usage }; if (!cctx->cli) { mctx = doveadm_mail_cmd_init(&mail_cmd, doveadm_settings); /* doveadm-server always does userdb lookups */ mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; } else { mctx = doveadm_mail_cmdline_init(&mail_cmd); } mctx->cur_username = cctx->username; mctx->iterate_all_users = FALSE; wildcard_user = NULL; p_array_init(&full_args, mctx->pool, 8); p_array_init(&pargv, mctx->pool, 8); for(i=0;iargc;i++) { const struct doveadm_cmd_param *arg = &cctx->argv[i]; if (!arg->value_set) continue; if (strcmp(arg->name, "all-users") == 0) { if (cctx->tcp_server) mctx->add_username_header = TRUE; else mctx->iterate_all_users = arg->value.v_bool; fieldstr = "-A"; array_append(&full_args, &fieldstr, 1); } else if (strcmp(arg->name, "socket-path") == 0) { doveadm_settings->doveadm_socket_path = arg->value.v_string; if (doveadm_settings->doveadm_worker_count == 0) doveadm_settings->doveadm_worker_count = 1; } else if (strcmp(arg->name, "user") == 0) { mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; if (!cctx->tcp_server) mctx->cur_username = arg->value.v_string; fieldstr = "-u"; array_append(&full_args, &fieldstr, 1); array_append(&full_args, &arg->value.v_string, 1); if (strchr(arg->value.v_string, '*') != NULL || strchr(arg->value.v_string, '?') != NULL) { if (cctx->tcp_server) mctx->add_username_header = TRUE; else { wildcard_user = arg->value.v_string; mctx->cur_username = NULL; } } else if (!cctx->tcp_server) { cctx->username = mctx->cur_username; } } else if (strcmp(arg->name, "user-file") == 0) { mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; wildcard_user = "*"; mctx->users_list_input = arg->value.v_istream; fieldstr = "-F"; array_append(&full_args, &fieldstr, 1); fieldstr = ""; /* value doesn't really matter */ array_append(&full_args, &fieldstr, 1); i_stream_ref(mctx->users_list_input); } else if (strcmp(arg->name, "field") == 0 || strcmp(arg->name, "flag") == 0) { /* mailbox status, fetch, flags: convert an array into a single space-separated parameter (alternative to fieldstr) */ fieldstr = p_array_const_string_join(mctx->pool, &arg->value.v_array, " "); array_append(&pargv, &fieldstr, 1); } else if (strcmp(arg->name, "file") == 0) { /* input for doveadm_mail_get_input(), used by e.g. save */ if (mctx->cmd_input != NULL) { i_error("Only one file input allowed: %s", arg->name); doveadm_mail_cmd_free(mctx); doveadm_exit_code = EX_USAGE; return; } mctx->cmd_input = arg->value.v_istream; i_stream_ref(mctx->cmd_input); /* Keep all named special parameters above this line */ } else if (mctx->v.parse_arg != NULL && arg->short_opt != '\0') { const char *short_opt_str = p_strdup_printf( mctx->pool, "-%c", arg->short_opt); optarg = (char*)arg->value.v_string; mctx->v.parse_arg(mctx, arg->short_opt); array_append(&full_args, &short_opt_str, 1); if (arg->type == CMD_PARAM_STR) array_append(&full_args, &arg->value.v_string, 1); } else if ((arg->flags & CMD_PARAM_FLAG_POSITIONAL) != 0) { /* feed this into pargv */ if (arg->type == CMD_PARAM_ARRAY) array_append_array(&pargv, &arg->value.v_array); else if (arg->type == CMD_PARAM_STR) array_append(&pargv, &arg->value.v_string, 1); } else { doveadm_exit_code = EX_USAGE; i_error("invalid parameter: %s", arg->name); doveadm_mail_cmd_free(mctx); return; } } const char *dashdash = "--"; array_append(&full_args, &dashdash, 1); array_append_zero(&pargv); /* All the -parameters need to be included in full_args so that they're sent to doveadm-server. */ unsigned int args_pos = array_count(&full_args); array_append_array(&full_args, &pargv); mctx->args = array_idx(&full_args, args_pos); mctx->full_args = array_idx(&full_args, 0); mctx->cli = cctx->cli; mctx->conn = cctx->conn; doveadm_mail_cmd_exec(mctx, cctx, wildcard_user); doveadm_mail_cmd_free(mctx); } dovecot-2.2.33.2/src/doveadm/doveadm-print-tab.c0000644000175000017500000000331113165463624016224 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream.h" #include "doveadm-print-private.h" struct doveadm_print_tab_context { unsigned int header_idx, header_count; unsigned int header_written:1; }; static struct doveadm_print_tab_context ctx; static void doveadm_print_tab_flush_header(void) { if (!ctx.header_written) { if (!doveadm_print_hide_titles) o_stream_nsend(doveadm_print_ostream, "\n", 1); ctx.header_written = TRUE; } } static void doveadm_print_tab_header(const struct doveadm_print_header *hdr) { ctx.header_count++; if (!doveadm_print_hide_titles) { if (ctx.header_count > 1) o_stream_nsend(doveadm_print_ostream, "\t", 1); o_stream_nsend_str(doveadm_print_ostream, hdr->title); } } static void doveadm_print_tab_print(const char *value) { doveadm_print_tab_flush_header(); if (ctx.header_idx > 0) o_stream_nsend(doveadm_print_ostream, "\t", 1); o_stream_nsend_str(doveadm_print_ostream, value); if (++ctx.header_idx == ctx.header_count) { ctx.header_idx = 0; o_stream_nsend(doveadm_print_ostream, "\n", 1); } } static void doveadm_print_tab_print_stream(const unsigned char *value, size_t size) { if (size == 0) { doveadm_print_tab_print(""); return; } doveadm_print_tab_flush_header(); if (ctx.header_idx > 0) o_stream_nsend(doveadm_print_ostream, "\t", 1); o_stream_nsend(doveadm_print_ostream, value, size); } static void doveadm_print_tab_flush(void) { doveadm_print_tab_flush_header(); } struct doveadm_print_vfuncs doveadm_print_tab_vfuncs = { "tab", NULL, NULL, doveadm_print_tab_header, doveadm_print_tab_print, doveadm_print_tab_print_stream, doveadm_print_tab_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-dump-dcrypt-file.c0000644000175000017500000000500713123174404017502 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dcrypt.h" #include "istream.h" #include "istream-decrypt.h" #include "dcrypt-iostream.h" #include "doveadm-dump.h" #include static int get_digest(const char *digest, struct dcrypt_private_key **priv_key_r ATTR_UNUSED, const char **error_r ATTR_UNUSED, void *context) { const char **digest_r = (const char**)context; *digest_r = t_strdup(digest); return 0; } static void dcrypt_istream_dump_metadata(const struct istream *stream) { enum io_stream_encrypt_flags flags = i_stream_encrypt_get_flags(stream); if ((flags & IO_STREAM_ENC_INTEGRITY_HMAC) != 0) printf("flags: IO_STREAM_ENC_INTEGRITY_HMAC\n"); if ((flags & IO_STREAM_ENC_INTEGRITY_AEAD) != 0) printf("flags: IO_STREAM_ENC_INTEGRITY_AEAD\n"); if ((flags & IO_STREAM_ENC_INTEGRITY_NONE) != 0) printf("flags: IO_STREAM_ENC_INTEGRITY_NONE\n"); if ((flags & IO_STREAM_ENC_VERSION_1) != 0) printf("flags: IO_STREAM_ENC_VERSION_1\n"); enum decrypt_istream_format format = i_stream_encrypt_get_format(stream); switch (format) { case DECRYPT_FORMAT_V1: printf("format: DECRYPT_FORMAT_V1\n"); break; case DECRYPT_FORMAT_V2: printf("format: DECRYPT_FORMAT_V2\n"); break; } } static bool dcrypt_file_dump_metadata(const char *filename, bool print) { bool ret = FALSE; struct istream *is = i_stream_create_file(filename, IO_BLOCK_SIZE); const char *key_digest = NULL; struct istream *ds = i_stream_create_decrypt_callback(is, get_digest, &key_digest); ssize_t size = i_stream_read(ds); i_assert(size < 0); if (key_digest != NULL) { ret = TRUE; if (print) { dcrypt_istream_dump_metadata(ds); printf("decrypt key digest: %s\n", key_digest); } } else if (print && ds->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(ds), i_stream_get_error(ds)); } i_stream_unref(&ds); i_stream_unref(&is); return ret; } static bool test_dump_dcrypt_file(const char *path) { if (!dcrypt_initialize("openssl", NULL, NULL)) return FALSE; bool ret = dcrypt_file_dump_metadata(path, FALSE); dcrypt_deinitialize(); return ret; } static void cmd_dump_dcrypt_file(int argc ATTR_UNUSED, char *argv[]) { const char *error = NULL; if (!dcrypt_initialize("openssl", NULL, &error)) i_fatal("dcrypt_initialize failed: %s", error); (void)dcrypt_file_dump_metadata(argv[1], TRUE); dcrypt_deinitialize(); } struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_file = { "dcrypt-file", test_dump_dcrypt_file, cmd_dump_dcrypt_file }; dovecot-2.2.33.2/src/doveadm/Makefile.in0000644000175000017500000012427713172375572014631 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = doveadm$(EXEEXT) pkglibexec_PROGRAMS = doveadm-server$(EXEEXT) noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/doveadm ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" am__EXEEXT_1 = test-doveadm-util$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) am__objects_1 = doveadm-auth.$(OBJEXT) doveadm-dict.$(OBJEXT) \ doveadm-director.$(OBJEXT) doveadm-fs.$(OBJEXT) \ doveadm-instance.$(OBJEXT) doveadm-kick.$(OBJEXT) \ doveadm-log.$(OBJEXT) doveadm-master.$(OBJEXT) \ doveadm-mount.$(OBJEXT) doveadm-mutf7.$(OBJEXT) \ doveadm-penalty.$(OBJEXT) doveadm-proxy.$(OBJEXT) \ doveadm-replicator.$(OBJEXT) doveadm-sis.$(OBJEXT) \ doveadm-stats.$(OBJEXT) doveadm-who.$(OBJEXT) am__objects_2 = doveadm-dsync.$(OBJEXT) doveadm-mail.$(OBJEXT) \ doveadm-mail-altmove.$(OBJEXT) doveadm-mail-batch.$(OBJEXT) \ doveadm-mail-deduplicate.$(OBJEXT) \ doveadm-mail-expunge.$(OBJEXT) doveadm-mail-fetch.$(OBJEXT) \ doveadm-mail-flags.$(OBJEXT) doveadm-mail-import.$(OBJEXT) \ doveadm-mail-index.$(OBJEXT) doveadm-mail-iter.$(OBJEXT) \ doveadm-mail-mailbox.$(OBJEXT) \ doveadm-mail-mailbox-metadata.$(OBJEXT) \ doveadm-mail-mailbox-status.$(OBJEXT) \ doveadm-mail-copymove.$(OBJEXT) \ doveadm-mailbox-list-iter.$(OBJEXT) \ doveadm-mail-save.$(OBJEXT) doveadm-mail-search.$(OBJEXT) \ doveadm-mail-server.$(OBJEXT) am__objects_3 = doveadm-dump.$(OBJEXT) doveadm-dump-dbox.$(OBJEXT) \ doveadm-dump-index.$(OBJEXT) doveadm-dump-log.$(OBJEXT) \ doveadm-dump-mailboxlog.$(OBJEXT) \ doveadm-dump-thread.$(OBJEXT) \ doveadm-dump-dcrypt-file.$(OBJEXT) \ doveadm-dump-dcrypt-key.$(OBJEXT) doveadm-zlib.$(OBJEXT) am__objects_4 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ doveadm-cmd.$(OBJEXT) doveadm-print.$(OBJEXT) \ doveadm-settings.$(OBJEXT) doveadm-util.$(OBJEXT) \ server-connection.$(OBJEXT) doveadm-print-formatted.$(OBJEXT) am_doveadm_OBJECTS = $(am__objects_4) doveadm.$(OBJEXT) \ doveadm-print-flow.$(OBJEXT) doveadm-print-pager.$(OBJEXT) \ doveadm-print-tab.$(OBJEXT) doveadm-print-table.$(OBJEXT) \ doveadm-print-json.$(OBJEXT) doveadm-pw.$(OBJEXT) doveadm_OBJECTS = $(am_doveadm_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_doveadm_server_OBJECTS = $(am__objects_4) \ doveadm-auth-server.$(OBJEXT) client-connection.$(OBJEXT) \ client-connection-http.$(OBJEXT) \ doveadm-print-server.$(OBJEXT) doveadm-print-json.$(OBJEXT) \ main.$(OBJEXT) doveadm_server_OBJECTS = $(am_doveadm_server_OBJECTS) am_test_doveadm_util_OBJECTS = test-doveadm-util.$(OBJEXT) test_doveadm_util_OBJECTS = $(am_test_doveadm_util_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(doveadm_SOURCES) $(doveadm_server_SOURCES) \ $(test_doveadm_util_SOURCES) DIST_SOURCES = $(doveadm_SOURCES) $(doveadm_server_SOURCES) \ $(test_doveadm_util_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm SUBDIRS = dsync AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-dcrypt \ -I$(top_srcdir)/src/auth \ -DMODULEDIR=\""$(moduledir)"\" \ -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \ -DDOVEADM_MODULEDIR=\""$(doveadm_moduledir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DBINDIR=\""$(bindir)"\" \ -DMANDIR=\""$(mandir)"\" cmd_pw_libs = \ ../auth/libpassword.a \ ../lib-ntlm/libntlm.a \ ../lib-otp/libotp.a libs = \ dsync/libdsync.la \ ../lib-compression/libcompression.la doveadm_LDADD = \ $(libs) \ $(cmd_pw_libs) \ $(CRYPT_LIBS) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) doveadm_DEPENDENCIES = \ $(libs) \ $(cmd_pw_libs) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) doveadm_server_LDADD = \ $(libs) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) doveadm_server_DEPENDENCIES = \ $(libs) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) doveadm_common_cmds = \ doveadm-auth.c \ doveadm-dict.c \ doveadm-director.c \ doveadm-fs.c \ doveadm-instance.c \ doveadm-kick.c \ doveadm-log.c \ doveadm-master.c \ doveadm-mount.c \ doveadm-mutf7.c \ doveadm-penalty.c \ doveadm-proxy.c \ doveadm-replicator.c \ doveadm-sis.c \ doveadm-stats.c \ doveadm-who.c doveadm_common_mail_cmds = \ doveadm-dsync.c \ doveadm-mail.c \ doveadm-mail-altmove.c \ doveadm-mail-batch.c \ doveadm-mail-deduplicate.c \ doveadm-mail-expunge.c \ doveadm-mail-fetch.c \ doveadm-mail-flags.c \ doveadm-mail-import.c \ doveadm-mail-index.c \ doveadm-mail-iter.c \ doveadm-mail-mailbox.c \ doveadm-mail-mailbox-metadata.c \ doveadm-mail-mailbox-status.c \ doveadm-mail-copymove.c \ doveadm-mailbox-list-iter.c \ doveadm-mail-save.c \ doveadm-mail-search.c \ doveadm-mail-server.c # these aren't actually useful in doveadm-server, but plugins may implement # both dumping and some other commands inside a single plugin. not having the # dump functions in doveadm-server fails to load such plugins. doveadm_common_dump_cmds = \ doveadm-dump.c \ doveadm-dump-dbox.c \ doveadm-dump-index.c \ doveadm-dump-log.c \ doveadm-dump-mailboxlog.c \ doveadm-dump-thread.c \ doveadm-dump-dcrypt-file.c \ doveadm-dump-dcrypt-key.c \ doveadm-zlib.c common = \ $(doveadm_common_cmds) \ $(doveadm_common_mail_cmds) \ $(doveadm_common_dump_cmds) \ doveadm-cmd.c \ doveadm-print.c \ doveadm-settings.c \ doveadm-util.c \ server-connection.c \ doveadm-print-formatted.c doveadm_SOURCES = \ $(common) \ doveadm.c \ doveadm-print-flow.c \ doveadm-print-pager.c \ doveadm-print-tab.c \ doveadm-print-table.c \ doveadm-print-json.c \ doveadm-pw.c doveadm_server_SOURCES = \ $(common) \ doveadm-auth-server.c \ client-connection.c \ client-connection-http.c \ doveadm-print-server.c \ doveadm-print-json.c \ main.c pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ doveadm.h \ doveadm-cmd.h \ doveadm-dsync.h \ doveadm-dump.h \ doveadm-mail.h \ doveadm-mail-iter.h \ doveadm-mailbox-list-iter.h \ doveadm-print.h \ doveadm-print-private.h \ doveadm-settings.h \ doveadm-util.h noinst_HEADERS = \ client-connection.h \ client-connection-private.h \ server-connection.h \ doveadm-server.h \ doveadm-who.h test_programs = \ test-doveadm-util test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_doveadm_util_SOURCES = test-doveadm-util.c test_doveadm_util_LDADD = doveadm-util.o $(test_libs) $(MODULE_LIBS) test_doveadm_util_DEPENDENCIES = $(test_deps) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/doveadm/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/doveadm/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list doveadm$(EXEEXT): $(doveadm_OBJECTS) $(doveadm_DEPENDENCIES) $(EXTRA_doveadm_DEPENDENCIES) @rm -f doveadm$(EXEEXT) $(AM_V_CCLD)$(LINK) $(doveadm_OBJECTS) $(doveadm_LDADD) $(LIBS) doveadm-server$(EXEEXT): $(doveadm_server_OBJECTS) $(doveadm_server_DEPENDENCIES) $(EXTRA_doveadm_server_DEPENDENCIES) @rm -f doveadm-server$(EXEEXT) $(AM_V_CCLD)$(LINK) $(doveadm_server_OBJECTS) $(doveadm_server_LDADD) $(LIBS) test-doveadm-util$(EXEEXT): $(test_doveadm_util_OBJECTS) $(test_doveadm_util_DEPENDENCIES) $(EXTRA_test_doveadm_util_DEPENDENCIES) @rm -f test-doveadm-util$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_doveadm_util_OBJECTS) $(test_doveadm_util_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-connection-http.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-auth-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-auth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-cmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dict.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-director.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dsync.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-dbox.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-dcrypt-file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-dcrypt-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-index.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-mailboxlog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-thread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-fs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-instance.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-kick.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-altmove.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-batch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-copymove.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-deduplicate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-expunge.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-fetch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-flags.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-import.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-index.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-iter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-mailbox-metadata.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-mailbox-status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-mailbox.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-save.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-search.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mailbox-list-iter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-master.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mount.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mutf7.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-penalty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-flow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-formatted.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-pager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-tab.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print-table.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-proxy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-pw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-replicator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sis.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-stats.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-who.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-zlib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-doveadm-util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-binPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS clean-pkglibexecPROGRAMS mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-binPROGRAMS install-exec-local \ install-pkglibexecPROGRAMS install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-binPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS clean-pkglibexecPROGRAMS cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-exec-local install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile install-exec-local: rm -f $(DESTDIR)$(bindir)/dsync $(LN_S) doveadm $(DESTDIR)$(bindir)/dsync check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/doveadm/doveadm-print-json.c0000644000175000017500000000713213123174404016421 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "json-parser.h" #include "client-connection.h" #include "doveadm-server.h" #include "doveadm-print.h" #include "doveadm-print-private.h" struct doveadm_print_json_context { unsigned int header_idx, header_count; bool first_row; bool in_stream; bool flushed; ARRAY(struct doveadm_print_header) headers; pool_t pool; string_t *str; }; static struct doveadm_print_json_context ctx; static void doveadm_print_json_flush_internal(void); static void doveadm_print_json_init(void) { i_zero(&ctx); ctx.pool = pool_alloconly_create("doveadm json print", 1024); ctx.str = str_new(ctx.pool, 256); p_array_init(&ctx.headers, ctx.pool, 1); ctx.first_row = TRUE; ctx.in_stream = FALSE; } static void doveadm_print_json_header(const struct doveadm_print_header *hdr) { struct doveadm_print_header *lhdr; lhdr = array_append_space(&ctx.headers); lhdr->key = p_strdup(ctx.pool, hdr->key); lhdr->flags = hdr->flags; ctx.header_count++; } static void doveadm_print_json_value_header(const struct doveadm_print_header *hdr) { // get header name if (ctx.header_idx == 0) { if (ctx.first_row == TRUE) { ctx.first_row = FALSE; str_append_c(ctx.str, '['); } else { str_append_c(ctx.str, ','); } str_append_c(ctx.str, '{'); } else { str_append_c(ctx.str, ','); } str_append_c(ctx.str, '"'); json_append_escaped(ctx.str, hdr->key); str_append_c(ctx.str, '"'); str_append_c(ctx.str, ':'); } static void doveadm_print_json_value_footer(void) { if (++ctx.header_idx == ctx.header_count) { ctx.header_idx = 0; str_append_c(ctx.str, '}'); doveadm_print_json_flush_internal(); } } static void doveadm_print_json_print(const char *value) { const struct doveadm_print_header *hdr = array_idx(&ctx.headers, ctx.header_idx); doveadm_print_json_value_header(hdr); if (value == NULL) { str_append(ctx.str, "null"); } else if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_NUMBER) != 0) { i_assert(str_is_float(value, '\0')); str_append(ctx.str, value); } else { str_append_c(ctx.str, '"'); json_append_escaped(ctx.str, value); str_append_c(ctx.str, '"'); } doveadm_print_json_value_footer(); } static void doveadm_print_json_print_stream(const unsigned char *value, size_t size) { if (!ctx.in_stream) { const struct doveadm_print_header *hdr = array_idx(&ctx.headers, ctx.header_idx); doveadm_print_json_value_header(hdr); i_assert((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_NUMBER) == 0); str_append_c(ctx.str, '"'); ctx.in_stream = TRUE; } if (size == 0) { str_append_c(ctx.str, '"'); doveadm_print_json_value_footer(); ctx.in_stream = FALSE; return; } json_append_escaped_data(ctx.str, value, size); if (str_len(ctx.str) >= IO_BLOCK_SIZE) doveadm_print_json_flush_internal(); } static void doveadm_print_json_flush_internal(void) { o_stream_nsend(doveadm_print_ostream, str_data(ctx.str), str_len(ctx.str)); str_truncate(ctx.str, 0); } static void doveadm_print_json_flush(void) { if (ctx.flushed) return; ctx.flushed = TRUE; if (ctx.first_row == FALSE) str_append_c(ctx.str,']'); else { str_append_c(ctx.str,'['); str_append_c(ctx.str,']'); } doveadm_print_json_flush_internal(); } static void doveadm_print_json_deinit(void) { pool_unref(&ctx.pool); } struct doveadm_print_vfuncs doveadm_print_json_vfuncs = { "json", doveadm_print_json_init, doveadm_print_json_deinit, doveadm_print_json_header, doveadm_print_json_print, doveadm_print_json_print_stream, doveadm_print_json_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-dsync.h0000644000175000017500000000040213123174404015434 00000000000000#ifndef DOVEADM_DSYNC_H #define DOVEADM_DSYNC_H extern struct doveadm_mail_cmd cmd_dsync_mirror; extern struct doveadm_mail_cmd cmd_dsync_backup; extern struct doveadm_mail_cmd cmd_dsync_server; void doveadm_dsync_main(int *_argc, char **_argv[]); #endif dovecot-2.2.33.2/src/doveadm/doveadm-dump.c0000644000175000017500000000457713123174404015275 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "doveadm.h" #include "doveadm-dump.h" #include #include static ARRAY(const struct doveadm_cmd_dump *) dumps; void doveadm_dump_register(const struct doveadm_cmd_dump *dump) { array_append(&dumps, &dump, 1); } static const struct doveadm_cmd_dump * dump_find_name(const char *name) { const struct doveadm_cmd_dump *const *dumpp; array_foreach(&dumps, dumpp) { if (strcmp((*dumpp)->name, name) == 0) return *dumpp; } return NULL; } static const struct doveadm_cmd_dump * dump_find_test(const char *path) { const struct doveadm_cmd_dump *const *dumpp; array_foreach(&dumps, dumpp) { if ((*dumpp)->test(path)) return *dumpp; } return NULL; } static void cmd_dump(int argc, char *argv[]) { const struct doveadm_cmd_dump *dump; const char *type = NULL; int c; while ((c = getopt(argc, argv, "t:")) > 0) { switch (c) { case 't': type = optarg; break; default: help(&doveadm_cmd_dump); } } if (optind == argc) help(&doveadm_cmd_dump); optind--; argc -= optind; argv += optind; dump = type != NULL ? dump_find_name(type) : dump_find_test(argv[1]); if (dump == NULL) { if (type != NULL) { print_dump_types(); i_fatal_status(EX_USAGE, "Unknown type: %s", type); } else { i_fatal_status(EX_DATAERR, "Can't autodetect file type: %s", argv[1]); } } else { if (type == NULL) printf("Detected file type: %s\n", dump->name); } dump->cmd(argc, argv); } struct doveadm_cmd doveadm_cmd_dump = { cmd_dump, "dump", "[-t ] " }; static const struct doveadm_cmd_dump *dumps_builtin[] = { &doveadm_cmd_dump_dbox, &doveadm_cmd_dump_index, &doveadm_cmd_dump_log, &doveadm_cmd_dump_mailboxlog, &doveadm_cmd_dump_thread, &doveadm_cmd_dump_zlib, &doveadm_cmd_dump_dcrypt_file, &doveadm_cmd_dump_dcrypt_key }; void print_dump_types(void) { unsigned int i; fprintf(stderr, "Available dump types: %s", dumps_builtin[0]->name); for (i = 1; i < N_ELEMENTS(dumps_builtin); i++) fprintf(stderr, " %s", dumps_builtin[i]->name); fprintf(stderr, "\n"); } void doveadm_dump_init(void) { unsigned int i; i_array_init(&dumps, N_ELEMENTS(dumps_builtin) + 8); for (i = 0; i < N_ELEMENTS(dumps_builtin); i++) doveadm_dump_register(dumps_builtin[i]); } void doveadm_dump_deinit(void) { array_free(&dumps); } dovecot-2.2.33.2/src/doveadm/doveadm-print-pager.c0000644000175000017500000000531313165463624016560 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream.h" #include "doveadm-print-private.h" struct doveadm_print_pager_header { const char *title; enum doveadm_print_header_flags flags; }; struct doveadm_print_pager_context { pool_t pool; ARRAY(struct doveadm_print_pager_header) headers; unsigned int header_idx; unsigned int streaming:1; unsigned int first_page:1; }; static struct doveadm_print_pager_context *ctx; static void doveadm_print_pager_header(const struct doveadm_print_header *hdr) { struct doveadm_print_pager_header *fhdr; fhdr = array_append_space(&ctx->headers); fhdr->flags = hdr->flags; fhdr->title = p_strdup(ctx->pool, hdr->title); } static void pager_next_hdr(void) { if (++ctx->header_idx == array_count(&ctx->headers)) { ctx->header_idx = 0; } } static void doveadm_print_pager_print(const char *value) { const struct doveadm_print_pager_header *hdr = array_idx(&ctx->headers, ctx->header_idx); if (ctx->header_idx == 0 && !ctx->first_page) { o_stream_nsend(doveadm_print_ostream, "\f\n", 2); } ctx->first_page = FALSE; if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE) == 0) { o_stream_nsend_str(doveadm_print_ostream, hdr->title); o_stream_nsend(doveadm_print_ostream, ": ", 2); } o_stream_nsend_str(doveadm_print_ostream, value); o_stream_nsend(doveadm_print_ostream, "\n", 1); pager_next_hdr(); } static void doveadm_print_pager_print_stream(const unsigned char *value, size_t size) { const struct doveadm_print_pager_header *hdr = array_idx(&ctx->headers, ctx->header_idx); if (!ctx->streaming) { ctx->streaming = TRUE; if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE) == 0) { o_stream_nsend_str(doveadm_print_ostream, hdr->title); o_stream_nsend(doveadm_print_ostream, ":\n", 2); } } o_stream_nsend(doveadm_print_ostream, value, size); if (size == 0) { pager_next_hdr(); ctx->streaming = FALSE; } } static void doveadm_print_pager_init(void) { pool_t pool; pool = pool_alloconly_create("doveadm print pager", 1024); ctx = p_new(pool, struct doveadm_print_pager_context, 1); ctx->pool = pool; ctx->first_page = TRUE; p_array_init(&ctx->headers, pool, 16); } static void doveadm_print_pager_flush(void) { if (ctx->header_idx != 0) { o_stream_nsend(doveadm_print_ostream, "\n", 1); ctx->header_idx = 0; } } static void doveadm_print_pager_deinit(void) { pool_unref(&ctx->pool); ctx = NULL; } struct doveadm_print_vfuncs doveadm_print_pager_vfuncs = { DOVEADM_PRINT_TYPE_PAGER, doveadm_print_pager_init, doveadm_print_pager_deinit, doveadm_print_pager_header, doveadm_print_pager_print, doveadm_print_pager_print_stream, doveadm_print_pager_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-mailbox-metadata.c0000644000175000017500000002706413165463624020470 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-namespace.h" #include "mail-storage.h" #include "doveadm-print.h" #include "doveadm-mail.h" #include "doveadm-mailbox-list-iter.h" struct metadata_cmd_context { struct doveadm_mail_cmd_context ctx; const char *mailbox; enum mail_attribute_type key_type; const char *key; struct mail_attribute_value value; bool empty_mailbox_name; bool allow_empty_mailbox_name; }; static int cmd_mailbox_metadata_open_mailbox(struct metadata_cmd_context *mctx, struct mail_user *user, const char *op, struct mail_namespace **ns_r, struct mailbox **box_r) { mctx->empty_mailbox_name = mctx->mailbox[0] == '\0'; if (mctx->empty_mailbox_name) { if (!mctx->allow_empty_mailbox_name) { i_error("Failed to %s: %s", op, "mailbox name cannot be empty"); mctx->ctx.exit_code = EX_USAGE; return -1; } /* server attribute */ *ns_r = mail_namespace_find_inbox(user->namespaces); *box_r = mailbox_alloc((*ns_r)->list, "INBOX", 0); mctx->key = t_strconcat(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER, mctx->key, NULL); } else { /* mailbox attributes */ *ns_r = mail_namespace_find(user->namespaces, mctx->mailbox); *box_r = mailbox_alloc((*ns_r)->list, mctx->mailbox, 0); } mailbox_set_reason(*box_r, mctx->ctx.cmd->name); if (mailbox_open(*box_r) < 0) { i_error("Failed to open mailbox: %s", mailbox_get_last_internal_error(*box_r, NULL)); doveadm_mail_failed_mailbox(&mctx->ctx, *box_r); mailbox_free(box_r); return -1; } return 0; } static int cmd_mailbox_metadata_set_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; struct mailbox_transaction_context *trans; int ret; ret = cmd_mailbox_metadata_open_mailbox(ctx, user, "set attribute", &ns, &box); if (ret != 0) return ret; trans = mailbox_transaction_begin(box, ctx->empty_mailbox_name ? MAILBOX_TRANSACTION_FLAG_EXTERNAL : 0); ret = ctx->value.value == NULL ? mailbox_attribute_unset(trans, ctx->key_type, ctx->key) : mailbox_attribute_set(trans, ctx->key_type, ctx->key, &ctx->value); if (ret < 0) { i_error("Failed to set attribute: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); mailbox_transaction_rollback(&trans); } else if (mailbox_transaction_commit(&trans) < 0) { i_error("Failed to commit transaction: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } mailbox_free(&box); return ret; } static void cmd_mailbox_metadata_parse_key(const char *arg, enum mail_attribute_type *type_r, const char **key_r) { arg = t_str_lcase(arg); if (strncmp(arg, "/private/", 9) == 0) { *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE; *key_r = arg + 9; } else if (strncmp(arg, "/shared/", 8) == 0) { *type_r = MAIL_ATTRIBUTE_TYPE_SHARED; *key_r = arg + 8; } else if (strcmp(arg, "/private") == 0) { *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE; *key_r = ""; } else if (strcmp(arg, "/shared") == 0) { *type_r = MAIL_ATTRIBUTE_TYPE_SHARED; *key_r = ""; } else { i_fatal_status(EX_USAGE, "Invalid metadata key '%s': " "Must begin with /private or /shared", arg); } } static void cmd_mailbox_metadata_set_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; const char *key; if (str_array_length(args) != 3) doveadm_mail_help_name("mailbox metadata set"); cmd_mailbox_metadata_parse_key(args[1], &ctx->key_type, &key); ctx->mailbox = p_strdup(_ctx->pool, args[0]); ctx->key = p_strdup(_ctx->pool, key); ctx->value.value = p_strdup(_ctx->pool, args[2]); } static bool cmd_mailbox_metadata_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; switch (c) { case 's': ctx->allow_empty_mailbox_name = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_mailbox_metadata_set_alloc(void) { struct metadata_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct metadata_cmd_context); ctx->ctx.v.init = cmd_mailbox_metadata_set_init; ctx->ctx.v.parse_arg = cmd_mailbox_metadata_parse_arg; ctx->ctx.v.run = cmd_mailbox_metadata_set_run; return &ctx->ctx; } static void cmd_mailbox_metadata_unset_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; const char *key; if (str_array_length(args) != 2) doveadm_mail_help_name("mailbox metadata unset"); cmd_mailbox_metadata_parse_key(args[1], &ctx->key_type, &key); ctx->mailbox = p_strdup(_ctx->pool, args[0]); ctx->key = p_strdup(_ctx->pool, key); } static struct doveadm_mail_cmd_context *cmd_mailbox_metadata_unset_alloc(void) { struct metadata_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct metadata_cmd_context); ctx->ctx.v.init = cmd_mailbox_metadata_unset_init; ctx->ctx.v.parse_arg = cmd_mailbox_metadata_parse_arg; ctx->ctx.v.run = cmd_mailbox_metadata_set_run; return &ctx->ctx; } static int cmd_mailbox_metadata_get_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; struct mailbox_transaction_context *trans; struct mail_attribute_value value; int ret; ret = cmd_mailbox_metadata_open_mailbox(ctx, user, "get attribute", &ns, &box); if (ret != 0) return ret; trans = mailbox_transaction_begin(box, 0); ret = mailbox_attribute_get_stream(trans, ctx->key_type, ctx->key, &value); if (ret < 0) { i_error("Failed to get attribute: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); } else if (ret == 0) { /* not found, print as empty */ doveadm_print(""); } else if (value.value_stream != NULL) { doveadm_print_istream(value.value_stream); } else { doveadm_print(value.value); } (void)mailbox_transaction_commit(&trans); mailbox_free(&box); return ret; } static void cmd_mailbox_metadata_get_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; const char *key; if (str_array_length(args) != 2) doveadm_mail_help_name("mailbox metadata get"); cmd_mailbox_metadata_parse_key(args[1], &ctx->key_type, &key); ctx->mailbox = p_strdup(_ctx->pool, args[0]); ctx->key = p_strdup(_ctx->pool, key); doveadm_print_header("value", "value", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } static struct doveadm_mail_cmd_context *cmd_mailbox_metadata_get_alloc(void) { struct metadata_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct metadata_cmd_context); ctx->ctx.v.init = cmd_mailbox_metadata_get_init; ctx->ctx.v.parse_arg = cmd_mailbox_metadata_parse_arg; ctx->ctx.v.run = cmd_mailbox_metadata_get_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx; } static int cmd_mailbox_metadata_list_run_iter(struct metadata_cmd_context *ctx, struct mailbox *box, enum mail_attribute_type type) { struct mailbox_attribute_iter *iter; const char *key; iter = mailbox_attribute_iter_init(box, type, ctx->key); while ((key = mailbox_attribute_iter_next(iter)) != NULL) doveadm_print(key); if (mailbox_attribute_iter_deinit(&iter) < 0) { i_error("Mailbox %s: Failed to iterate mailbox attributes: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); return -1; } return 0; } static int cmd_mailbox_metadata_list_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; int ret = 0; ret = cmd_mailbox_metadata_open_mailbox(ctx, user, "list attributes", &ns, &box); if (ret != 0) return ret; if (ctx->key[0] == '\0' || ctx->key_type == MAIL_ATTRIBUTE_TYPE_PRIVATE) { if (cmd_mailbox_metadata_list_run_iter(ctx, box, MAIL_ATTRIBUTE_TYPE_PRIVATE) < 0) { doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } if (ctx->key[0] == '\0' || ctx->key_type == MAIL_ATTRIBUTE_TYPE_SHARED) { if (cmd_mailbox_metadata_list_run_iter(ctx, box, MAIL_ATTRIBUTE_TYPE_SHARED) < 0) { doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } mailbox_free(&box); return ret; } static void cmd_mailbox_metadata_list_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct metadata_cmd_context *ctx = (struct metadata_cmd_context *)_ctx; const char *key = NULL; if (args[0] == NULL) doveadm_mail_help_name("mailbox metadata list"); if (args[1] != NULL) cmd_mailbox_metadata_parse_key(args[1], &ctx->key_type, &key); ctx->mailbox = p_strdup(_ctx->pool, args[0]); ctx->key = key == NULL ? "" : p_strdup(_ctx->pool, key); doveadm_print_header("key", "key", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } static struct doveadm_mail_cmd_context *cmd_mailbox_metadata_list_alloc(void) { struct metadata_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct metadata_cmd_context); ctx->ctx.v.init = cmd_mailbox_metadata_list_init; ctx->ctx.v.parse_arg = cmd_mailbox_metadata_parse_arg; ctx->ctx.v.run = cmd_mailbox_metadata_list_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_set_ver2 = { .name = "mailbox metadata set", .mail_cmd = cmd_mailbox_metadata_set_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "allow-empty-mailbox-name", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "value", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_unset_ver2 = { .name = "mailbox metadata unset", .mail_cmd = cmd_mailbox_metadata_unset_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "allow-empty-mailbox-name", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_get_ver2 = { .name = "mailbox metadata get", .mail_cmd = cmd_mailbox_metadata_get_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "allow-empty-mailbox-name", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_list_ver2 = { .name = "mailbox metadata list", .mail_cmd = cmd_mailbox_metadata_list_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "allow-empty-mailbox-name", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key-prefix", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-dump-dbox.c0000644000175000017500000001312213165463624016224 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hex-dec.h" #include "istream.h" #include "index/dbox-common/dbox-file.h" #include "doveadm-dump.h" #include #include #include static void dump_timestamp(struct istream *input, const char *name, const char *value) { time_t t; if (strcmp(value, "0") == 0) t = 0; else { t = hex2dec((const void *)value, strlen(value)); if (t == 0) { i_fatal("Invalid %s at %"PRIuUOFF_T": %s", name, input->v_offset, value); } } printf("%s = %ld (%s)\n", name, (long)t, unixdate2str(t)); } static uoff_t dump_size(struct istream *input, const char *name, const char *value) { uoff_t size; if (strcmp(value, "0") == 0) size = 0; else { size = hex2dec((const void *)value, strlen(value)); if (size == 0) { i_fatal("Invalid %s at %"PRIuUOFF_T": %s", name, input->v_offset, value); } } printf("%s = %"PRIuUOFF_T"\n", name, size); return size; } static unsigned int dump_file_hdr(struct istream *input) { const char *line, *const *arg, *version; unsigned int msg_hdr_size = 0; if ((line = i_stream_read_next_line(input)) == NULL) i_fatal("Empty file"); arg = t_strsplit(line, " "); /* check version */ version = *arg; if (version == NULL || !str_is_numeric(version, ' ')) i_fatal("%s is not a dbox file", i_stream_get_name(input)); if (strcmp(version, "2") != 0) i_fatal("Unsupported dbox file version %s", version); arg++; for (; *arg != NULL; arg++) { switch (**arg) { case DBOX_HEADER_MSG_HEADER_SIZE: msg_hdr_size = hex2dec((const void *)(*arg + 1), strlen(*arg + 1)); if (msg_hdr_size == 0) { i_fatal("Invalid msg_header_size header: %s", *arg + 1); } printf("file.msg_header_size = %u\n", msg_hdr_size); break; case DBOX_HEADER_CREATE_STAMP: dump_timestamp(input, "file.create_stamp", *arg + 1); break; default: printf("file.unknown-%c = %s\n", **arg, *arg + 1); break; } } if (msg_hdr_size == 0) i_fatal("Missing msg_header_size in file header"); return msg_hdr_size; } static bool dump_msg_hdr(struct istream *input, unsigned int hdr_size, uoff_t *msg_size_r) { struct dbox_message_header hdr; const unsigned char *data; size_t size; uoff_t msg_size; if (i_stream_read_data(input, &data, &size, hdr_size-1) <= 0) { if (size == 0) return FALSE; i_fatal("Partial message header read at %"PRIuUOFF_T": " "%"PRIuSIZE_T" bytes", input->v_offset, size); } printf("offset %"PRIuUOFF_T":\n", input->v_offset); if (hdr_size < sizeof(hdr)) i_fatal("file.hdr_size too small: %u", hdr_size); memcpy(&hdr, data, sizeof(hdr)); if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) i_fatal("dbox wrong pre-magic at %"PRIuUOFF_T, input->v_offset); msg_size = dump_size(input, "msg.size", t_strndup(hdr.message_size_hex, sizeof(hdr.message_size_hex))); i_stream_skip(input, hdr_size); *msg_size_r = msg_size; return TRUE; } static void dump_msg_metadata(struct istream *input) { struct dbox_metadata_header hdr; const unsigned char *data; size_t size; const char *line; /* verify magic */ if (i_stream_read_data(input, &data, &size, sizeof(hdr)-1) <= 0) { i_fatal("dbox missing metadata at %"PRIuUOFF_T, input->v_offset); } memcpy(&hdr, data, sizeof(hdr)); if (memcmp(hdr.magic_post, DBOX_MAGIC_POST, sizeof(hdr.magic_post)) != 0) i_fatal("dbox wrong post-magic at %"PRIuUOFF_T, input->v_offset); i_stream_skip(input, sizeof(hdr)); /* dump the metadata */ for (;;) { if ((line = i_stream_read_next_line(input)) == NULL) i_fatal("dbox metadata ended unexpectedly at EOF"); if (*line == '\0') break; switch (*line) { case DBOX_METADATA_GUID: printf("msg.guid = %s\n", line + 1); break; case DBOX_METADATA_POP3_UIDL: printf("msg.pop3-uidl = %s\n", line + 1); break; case DBOX_METADATA_POP3_ORDER: printf("msg.pop3-order = %s\n", line + 1); break; case DBOX_METADATA_RECEIVED_TIME: dump_timestamp(input, "msg.received", line + 1); break; case DBOX_METADATA_PHYSICAL_SIZE: (void)dump_size(input, "msg.physical-size", line + 1); break; case DBOX_METADATA_VIRTUAL_SIZE: (void)dump_size(input, "msg.virtual-size", line + 1); break; case DBOX_METADATA_EXT_REF: printf("msg.ext-ref = %s\n", line + 1); break; case DBOX_METADATA_ORIG_MAILBOX: printf("msg.orig-mailbox = %s\n", line + 1); break; case DBOX_METADATA_OLDV1_EXPUNGED: case DBOX_METADATA_OLDV1_FLAGS: case DBOX_METADATA_OLDV1_KEYWORDS: case DBOX_METADATA_OLDV1_SAVE_TIME: case DBOX_METADATA_OLDV1_SPACE: printf("msg.obsolete-%c = %s\n", *line, line + 1); break; } } } static bool dump_msg(struct istream *input, unsigned int hdr_size) { uoff_t msg_size; if (!dump_msg_hdr(input, hdr_size, &msg_size)) return FALSE; i_stream_skip(input, msg_size); dump_msg_metadata(input); return TRUE; } static void cmd_dump_dbox(int argc ATTR_UNUSED, char *argv[]) { struct istream *input; int fd; unsigned int hdr_size; bool ret; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_name(input, argv[1]); hdr_size = dump_file_hdr(input); do { printf("\n"); T_BEGIN { ret = dump_msg(input, hdr_size); } T_END; } while (ret); i_stream_destroy(&input); } static bool test_dump_dbox(const char *path) { const char *p; p = strrchr(path, '/'); if (p == NULL) p = path; else p++; return strncmp(p, "m.", 2) == 0 || strncmp(p, "u.", 2) == 0; } struct doveadm_cmd_dump doveadm_cmd_dump_dbox = { "dbox", test_dump_dbox, cmd_dump_dbox }; dovecot-2.2.33.2/src/doveadm/doveadm-print-flow.c0000644000175000017500000000502513165463624016431 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ostream.h" #include "doveadm-print-private.h" struct doveadm_print_flow_header { const char *title; enum doveadm_print_header_flags flags; }; struct doveadm_print_flow_context { pool_t pool; ARRAY(struct doveadm_print_flow_header) headers; unsigned int header_idx; unsigned int streaming:1; }; static struct doveadm_print_flow_context *ctx; static void doveadm_print_flow_header(const struct doveadm_print_header *hdr) { struct doveadm_print_flow_header *fhdr; fhdr = array_append_space(&ctx->headers); fhdr->title = p_strdup(ctx->pool, hdr->title); fhdr->flags = hdr->flags; } static void flow_next_hdr(void) { if (++ctx->header_idx < array_count(&ctx->headers)) o_stream_nsend(doveadm_print_ostream, " ", 1); else { ctx->header_idx = 0; o_stream_nsend(doveadm_print_ostream, "\n", 1); } } static void doveadm_print_flow_print_heder(const struct doveadm_print_flow_header *hdr) { if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE) == 0) { o_stream_nsend_str(doveadm_print_ostream, hdr->title); o_stream_nsend(doveadm_print_ostream, "=", 1); } } static void doveadm_print_flow_print(const char *value) { const struct doveadm_print_flow_header *hdr = array_idx(&ctx->headers, ctx->header_idx); doveadm_print_flow_print_heder(hdr); o_stream_nsend_str(doveadm_print_ostream, value); flow_next_hdr(); } static void doveadm_print_flow_print_stream(const unsigned char *value, size_t size) { const struct doveadm_print_flow_header *hdr = array_idx(&ctx->headers, ctx->header_idx); if (!ctx->streaming) { ctx->streaming = TRUE; doveadm_print_flow_print_heder(hdr); } o_stream_nsend(doveadm_print_ostream, value, size); if (size == 0) { flow_next_hdr(); ctx->streaming = FALSE; } } static void doveadm_print_flow_init(void) { pool_t pool; pool = pool_alloconly_create("doveadm print flow", 1024); ctx = p_new(pool, struct doveadm_print_flow_context, 1); ctx->pool = pool; p_array_init(&ctx->headers, pool, 16); } static void doveadm_print_flow_flush(void) { if (ctx->header_idx != 0) { o_stream_nsend(doveadm_print_ostream, "\n", 1); ctx->header_idx = 0; } } static void doveadm_print_flow_deinit(void) { pool_unref(&ctx->pool); ctx = NULL; } struct doveadm_print_vfuncs doveadm_print_flow_vfuncs = { "flow", doveadm_print_flow_init, doveadm_print_flow_deinit, doveadm_print_flow_header, doveadm_print_flow_print, doveadm_print_flow_print_stream, doveadm_print_flow_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-altmove.c0000644000175000017500000001070013123174404016700 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index.h" #include "mail-storage.h" #include "mail-namespace.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" struct altmove_cmd_context { struct doveadm_mail_cmd_context ctx; bool reverse; }; static int cmd_altmove_box(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info, struct mail_search_args *search_args, bool reverse) { struct doveadm_mail_iter *iter; struct mail *mail; enum modify_type modify_type = !reverse ? MODIFY_ADD : MODIFY_REMOVE; if (doveadm_mail_iter_init(ctx, info, search_args, 0, NULL, FALSE, &iter) < 0) return -1; while (doveadm_mail_iter_next(iter, &mail)) { if (doveadm_debug) { i_debug("altmove: box=%s uid=%u", info->vname, mail->uid); } mail_update_flags(mail, modify_type, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_BACKEND); } return doveadm_mail_iter_deinit_sync(&iter); } static int ns_purge(struct doveadm_mail_cmd_context *ctx, struct mail_namespace *ns, struct mail_storage *storage) { if (mail_storage_purge(storage) < 0) { i_error("Purging namespace '%s' failed: %s", ns->prefix, mail_storage_get_last_internal_error(storage, NULL)); doveadm_mail_failed_storage(ctx, storage); return -1; } return 0; } static int cmd_altmove_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct altmove_cmd_context *ctx = (struct altmove_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; struct mail_namespace *ns, *prev_ns = NULL; ARRAY(struct mail_storage *) purged_storages; struct mail_storage *const *storages, *ns_storage, *prev_storage = NULL; unsigned int i, count; int ret = 0; t_array_init(&purged_storages, 8); iter = doveadm_mailbox_list_iter_init(_ctx, user, _ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { ns_storage = mail_namespace_get_default_storage(info->ns); if (ns_storage != prev_storage) { if (prev_storage != NULL) { if (ns_purge(_ctx, prev_ns, prev_storage) < 0) ret = -1; array_append(&purged_storages, &prev_storage, 1); } prev_storage = ns_storage; prev_ns = info->ns; } if (cmd_altmove_box(_ctx, info, _ctx->search_args, ctx->reverse) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; if (prev_storage != NULL) { if (ns_purge(_ctx, prev_ns, prev_storage) < 0) ret = -1; array_append(&purged_storages, &prev_storage, 1); } /* make sure all private storages have been purged */ storages = array_get(&purged_storages, &count); for (ns = user->namespaces; ns != NULL; ns = ns->next) { if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE) continue; ns_storage = mail_namespace_get_default_storage(ns); for (i = 0; i < count; i++) { if (ns_storage == storages[i]) break; } if (i == count) { if (ns_purge(_ctx, ns, ns_storage) < 0) ret = -1; array_append(&purged_storages, &ns_storage, 1); storages = array_get(&purged_storages, &count); } } return ret; } static void cmd_altmove_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("altmove"); ctx->search_args = doveadm_mail_build_search_args(args); } static bool cmd_mailbox_altmove_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct altmove_cmd_context *ctx = (struct altmove_cmd_context *)_ctx; switch (c) { case 'r': ctx->reverse = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_altmove_alloc(void) { struct altmove_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct altmove_cmd_context); ctx->ctx.getopt_args = "r"; ctx->ctx.v.parse_arg = cmd_mailbox_altmove_parse_arg; ctx->ctx.v.init = cmd_altmove_init; ctx->ctx.v.run = cmd_altmove_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_altmove_ver2 = { .name = "altmove", .mail_cmd = cmd_altmove_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-r] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('r', "reverse", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-print-server.c0000644000175000017500000000333013123174404016752 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "client-connection.h" #include "doveadm-print-private.h" struct doveadm_print_server_context { unsigned int header_idx, header_count; string_t *str; }; static struct doveadm_print_server_context ctx; static void doveadm_print_server_flush(void); static void doveadm_print_server_init(void) { ctx.str = str_new(default_pool, 256); } static void doveadm_print_server_deinit(void) { str_free(&ctx.str); } static void doveadm_print_server_header(const struct doveadm_print_header *hdr ATTR_UNUSED) { /* no need to transfer these. the client should already know what it's getting */ ctx.header_count++; } static void doveadm_print_server_print(const char *value) { str_append_tabescaped(ctx.str, value); str_append_c(ctx.str, '\t'); if (++ctx.header_idx == ctx.header_count) { ctx.header_idx = 0; doveadm_print_server_flush(); } } static void doveadm_print_server_print_stream(const unsigned char *value, size_t size) { if (size == 0) { doveadm_print_server_print(""); return; } str_append_tabescaped_n(ctx.str, value, size); if (str_len(ctx.str) >= IO_BLOCK_SIZE) doveadm_print_server_flush(); } static void doveadm_print_server_flush(void) { o_stream_nsend(doveadm_print_ostream, str_data(ctx.str), str_len(ctx.str)); str_truncate(ctx.str, 0); } struct doveadm_print_vfuncs doveadm_print_server_vfuncs = { DOVEADM_PRINT_TYPE_SERVER, doveadm_print_server_init, doveadm_print_server_deinit, doveadm_print_server_header, doveadm_print_server_print, doveadm_print_server_print_stream, doveadm_print_server_flush }; dovecot-2.2.33.2/src/doveadm/doveadm.c0000644000175000017500000002410313165463624014330 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "ostream.h" #include "env-util.h" #include "execv-const.h" #include "dict.h" #include "master-service-private.h" #include "master-service-settings.h" #include "settings-parser.h" #include "doveadm-print-private.h" #include "doveadm-dump.h" #include "doveadm-mail.h" #include "doveadm-settings.h" #include "doveadm-dsync.h" #include "doveadm.h" #include const struct doveadm_print_vfuncs *doveadm_print_vfuncs_all[] = { &doveadm_print_flow_vfuncs, &doveadm_print_tab_vfuncs, &doveadm_print_table_vfuncs, &doveadm_print_pager_vfuncs, &doveadm_print_json_vfuncs, &doveadm_print_formatted_vfuncs, NULL }; bool doveadm_verbose_proctitle; int doveadm_exit_code = 0; static void failure_exit_callback(int *status) { enum fatal_exit_status fatal_status = *status; switch (fatal_status) { case FATAL_LOGWRITE: case FATAL_LOGERROR: case FATAL_LOGOPEN: case FATAL_OUTOFMEM: case FATAL_EXEC: case FATAL_DEFAULT: *status = EX_TEMPFAIL; break; } } static void doveadm_usage_compress_lines(FILE *out, const char *str, const char *prefix) { const char *cmd, *args, *p, *short_name, *sub_name; const char *prev_name = "", *prev_sub_name = ""; const char **lines; unsigned int i, count; size_t prefix_len = strlen(prefix); /* split lines */ lines = (void *)p_strsplit(pool_datastack_create(), str, "\n"); for (count = 0; lines[count] != NULL; count++) ; /* sort lines */ i_qsort(lines, count, sizeof(*lines), i_strcmp_p); /* print lines, compress subcommands into a single line */ for (i = 0; i < count; i++) { args = strchr(lines[i], '\t'); if (args == NULL) { cmd = lines[i]; args = ""; } else { cmd = t_strdup_until(lines[i], args); args++; } if (*prefix != '\0') { if (strncmp(cmd, prefix, prefix_len) != 0 || cmd[prefix_len] != ' ') continue; cmd += prefix_len + 1; } p = strchr(cmd, ' '); if (p == NULL) { if (*prev_name != '\0') { fprintf(out, "\n"); prev_name = ""; } fprintf(out, USAGE_CMDNAME_FMT" %s\n", cmd, args); } else { short_name = t_strdup_until(cmd, p); if (strcmp(prev_name, short_name) != 0) { if (*prev_name != '\0') fprintf(out, "\n"); fprintf(out, USAGE_CMDNAME_FMT" %s", short_name, t_strcut(p + 1, ' ')); prev_name = short_name; prev_sub_name = ""; } else { sub_name = t_strcut(p + 1, ' '); if (strcmp(prev_sub_name, sub_name) != 0) { fprintf(out, "|%s", sub_name); prev_sub_name = sub_name; } } } } if (*prev_name != '\0') fprintf(out, "\n"); } static void ATTR_NORETURN usage_to(FILE *out, const char *prefix) { const struct doveadm_cmd_ver2 *cmd2; const struct doveadm_cmd *cmd; string_t *str = t_str_new(1024); fprintf(out, "usage: doveadm [-Dv] [-f ] "); if (*prefix != '\0') fprintf(out, "%s ", prefix); fprintf(out, " []\n"); array_foreach(&doveadm_cmds, cmd) str_printfa(str, "%s\t%s\n", cmd->name, cmd->short_usage); array_foreach(&doveadm_cmds_ver2, cmd2) str_printfa(str, "%s\t%s\n", cmd2->name, cmd2->usage); doveadm_mail_usage(str); doveadm_usage_compress_lines(out, str_c(str), prefix); exit(EX_USAGE); } void usage(void) { usage_to(stderr, ""); } static void ATTR_NORETURN help_to(const struct doveadm_cmd *cmd, FILE *out) { fprintf(out, "doveadm %s %s\n", cmd->name, cmd->short_usage); exit(EX_USAGE); } void help(const struct doveadm_cmd *cmd) { help_to(cmd, stdout); } static void ATTR_NORETURN help_to_ver2(const struct doveadm_cmd_ver2 *cmd, FILE *out) { fprintf(out, "doveadm %s %s\n", cmd->name, cmd->usage); exit(EX_USAGE); } void help_ver2(const struct doveadm_cmd_ver2 *cmd) { help_to_ver2(cmd, stdout); } static void cmd_help(int argc ATTR_UNUSED, char *argv[]) { const char *man_argv[3]; if (argv[1] == NULL) usage_to(stdout, ""); env_put("MANPATH="MANDIR); man_argv[0] = "man"; man_argv[1] = t_strconcat("doveadm-", argv[1], NULL); man_argv[2] = NULL; execvp_const(man_argv[0], man_argv); } static struct doveadm_cmd doveadm_cmd_help = { cmd_help, "help", "" }; static void cmd_config(int argc ATTR_UNUSED, char *argv[]) { env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=", master_service_get_config_path(master_service), NULL)); argv[0] = BINDIR"/doveconf"; (void)execv(argv[0], argv); i_fatal("execv(%s) failed: %m", argv[0]); } static struct doveadm_cmd doveadm_cmd_config = { cmd_config, "config", "[doveconf parameters]" }; static void cmd_exec(int argc ATTR_UNUSED, char *argv[]); static struct doveadm_cmd doveadm_cmd_exec = { cmd_exec, "exec", " [binary parameters]" }; static void cmd_exec(int argc ATTR_UNUSED, char *argv[]) { const char *path, *binary = argv[1]; if (binary == NULL) help(&doveadm_cmd_exec); path = t_strdup_printf("%s/%s", doveadm_settings->libexec_dir, binary); argv++; argv[0] = t_strdup_noconst(path); (void)execv(argv[0], argv); i_fatal("execv(%s) failed: %m", argv[0]); } static bool doveadm_try_run(const char *cmd_name, int argc, const char *const argv[]) { const struct doveadm_cmd *cmd; cmd = doveadm_cmd_find_with_args(cmd_name, &argc, &argv); if (cmd == NULL) return FALSE; cmd->cmd(argc, (char **)argv); return TRUE; } static bool doveadm_has_subcommands(const char *cmd_name) { const struct doveadm_cmd_ver2 *cmd2; const struct doveadm_cmd *cmd; size_t len = strlen(cmd_name); array_foreach(&doveadm_cmds, cmd) { if (strncmp(cmd->name, cmd_name, len) == 0 && cmd->name[len] == ' ') return TRUE; } array_foreach(&doveadm_cmds_ver2, cmd2) { if (strncmp(cmd2->name, cmd_name, len) == 0 && cmd2->name[len] == ' ') return TRUE; } return doveadm_mail_has_subcommands(cmd_name); } static void doveadm_read_settings(void) { static const struct setting_parser_info *set_roots[] = { &doveadm_setting_parser_info, NULL }; struct master_service_settings_input input; struct master_service_settings_output output; const struct doveadm_settings *set; const char *error; i_zero(&input); input.roots = set_roots; input.module = "doveadm"; input.service = "doveadm"; input.preserve_user = TRUE; input.preserve_home = TRUE; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); service_set = master_service_settings_get(master_service); service_set = settings_dup(&master_service_setting_parser_info, service_set, pool_datastack_create()); doveadm_verbose_proctitle = service_set->verbose_proctitle; set = master_service_settings_get_others(master_service)[0]; doveadm_settings = settings_dup(&doveadm_setting_parser_info, set, pool_datastack_create()); doveadm_settings->parsed_features = set->parsed_features; /* copy this value by hand */ } static struct doveadm_cmd *doveadm_cmdline_commands[] = { &doveadm_cmd_help, &doveadm_cmd_config, &doveadm_cmd_exec, &doveadm_cmd_dump, &doveadm_cmd_pw, &doveadm_cmd_zlibconnect }; int main(int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; struct doveadm_cmd_context cctx; const char *cmd_name; unsigned int i; bool quick_init = FALSE; int c; i_zero(&cctx); cctx.cli = TRUE; i_set_failure_exit_callback(failure_exit_callback); doveadm_dsync_main(&argc, &argv); /* "+" is GNU extension to stop at the first non-option. others just accept -+ option. */ master_service = master_service_init("doveadm", service_flags, &argc, &argv, "+Df:hv"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': doveadm_debug = TRUE; doveadm_verbose = TRUE; break; case 'f': doveadm_print_init(optarg); break; case 'h': doveadm_print_hide_titles = TRUE; break; case 'v': doveadm_verbose = TRUE; break; default: return FATAL_DEFAULT; } } cmd_name = argv[optind]; if (cmd_name != NULL && strcmp(cmd_name, "help") == 0 && argv[optind+1] != NULL) { /* "help cmd" doesn't need any configuration */ quick_init = TRUE; } else { doveadm_read_settings(); } master_service_init_log(master_service, "doveadm: "); doveadm_cmds_init(); for (i = 0; i < N_ELEMENTS(doveadm_cmdline_commands); i++) doveadm_register_cmd(doveadm_cmdline_commands[i]); doveadm_register_auth_commands(); doveadm_cmd_register_ver2(&doveadm_cmd_stats_top_ver2); if (cmd_name != NULL && (quick_init || strcmp(cmd_name, "config") == 0 || strcmp(cmd_name, "stop") == 0 || strcmp(cmd_name, "reload") == 0)) { /* special case commands: even if there is something wrong with the config (e.g. mail_plugins), don't fail these commands */ quick_init = TRUE; } else { quick_init = FALSE; doveadm_print_ostream = o_stream_create_fd(STDOUT_FILENO, 0, FALSE); o_stream_set_no_error_handling(doveadm_print_ostream, TRUE); doveadm_dump_init(); doveadm_mail_init(); dict_drivers_register_builtin(); doveadm_load_modules(); if (cmd_name == NULL) { /* show usage after registering all plugins */ usage_to(stdout, ""); } } argc -= optind; argv += optind; i_getopt_reset(); master_service_init_finish(master_service); if (!doveadm_debug) { /* disable debugging unless -D is given */ i_set_debug_file("/dev/null"); } /* this has to be done here because proctitle hack can break the env pointer */ cctx.username = getenv("USER"); if (!doveadm_cmd_try_run_ver2(cmd_name, argc, (const char**)argv, &cctx) && !doveadm_try_run(cmd_name, argc, (const char **)argv) && !doveadm_mail_try_run(cmd_name, argc, argv)) { if (doveadm_has_subcommands(cmd_name)) usage_to(stdout, cmd_name); if (doveadm_has_unloaded_plugin(cmd_name)) { i_fatal("Unknown command '%s', but plugin %s exists. " "Try to set mail_plugins=%s", cmd_name, cmd_name, cmd_name); } usage(); } if (!quick_init) { doveadm_mail_deinit(); doveadm_dump_deinit(); doveadm_unload_modules(); dict_drivers_unregister_builtin(); doveadm_print_deinit(); o_stream_unref(&doveadm_print_ostream); } doveadm_cmds_deinit(); master_service_deinit(&master_service); return doveadm_exit_code; } dovecot-2.2.33.2/src/doveadm/server-connection.h0000644000175000017500000000221013123174404016341 00000000000000#ifndef SERVER_CONNECTION_H #define SERVER_CONNECTION_H #define SERVER_EXIT_CODE_DISCONNECTED 1000 struct doveadm_server; struct server_connection; struct ssl_iostream; typedef void server_cmd_callback_t(int exit_code, const char *error, void *context); int server_connection_create(struct doveadm_server *server, struct server_connection **conn_r); void server_connection_destroy(struct server_connection **conn); /* Return the server given to create() */ struct doveadm_server * server_connection_get_server(struct server_connection *conn); void server_connection_cmd(struct server_connection *conn, const char *line, struct istream *cmd_input, server_cmd_callback_t *callback, void *context); /* Returns TRUE if no command is being processed */ bool server_connection_is_idle(struct server_connection *conn); /* Extract iostreams from connection. Afterwards the server_connection simply waits for itself to be destroyed. */ void server_connection_extract(struct server_connection *conn, struct istream **istream_r, struct ostream **ostream_r, struct ssl_iostream **ssl_iostream_r); #endif dovecot-2.2.33.2/src/doveadm/doveadm.h0000644000175000017500000000123013123174404014316 00000000000000#ifndef DOVEADM_H #define DOVEADM_H #include #include "doveadm-util.h" #include "doveadm-cmd.h" #include "doveadm-settings.h" #define USAGE_CMDNAME_FMT " %-12s" #define DOVEADM_EX_NOTFOUND EX_NOHOST #define DOVEADM_EX_NOTPOSSIBLE EX_DATAERR #define DOVEADM_EX_UNKNOWN -1 extern bool doveadm_verbose_proctitle; extern int doveadm_exit_code; void usage(void) ATTR_NORETURN; void help(const struct doveadm_cmd *cmd) ATTR_NORETURN; void help_ver2(const struct doveadm_cmd_ver2 *cmd) ATTR_NORETURN; void doveadm_master_send_signal(int signo); const char *doveadm_exit_code_to_str(int code); int doveadm_str_to_exit_code(const char *reason); #endif dovecot-2.2.33.2/src/doveadm/doveadm-kick.c0000644000175000017500000001367513123174404015250 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "hash.h" #include "doveadm.h" #include "doveadm-who.h" #include "doveadm-print.h" #include #include #include #include struct kick_user { const char *username; bool kick_me; /* true if username and/or ip[/mask] matches. ignored when the -f switch is given. */ }; struct kick_pid { pid_t pid; ARRAY(struct kick_user) users; bool kick; }; struct kick_context { struct who_context who; HASH_TABLE(void *, struct kick_pid *) pids; bool cli; bool force_kick; ARRAY(const char *) kicked_users; }; static void kick_aggregate_line(struct who_context *_ctx, const struct who_line *line) { struct kick_context *ctx = (struct kick_context *)_ctx; const bool user_match = who_line_filter_match(line, &ctx->who.filter); struct kick_pid *k_pid; struct kick_user new_user, *user; i_zero(&new_user); k_pid = hash_table_lookup(ctx->pids, POINTER_CAST(line->pid)); if (k_pid == NULL) { k_pid = p_new(ctx->who.pool, struct kick_pid, 1); k_pid->pid = line->pid; p_array_init(&k_pid->users, ctx->who.pool, 5); hash_table_insert(ctx->pids, POINTER_CAST(line->pid), k_pid); } array_foreach_modifiable(&k_pid->users, user) { if (strcmp(line->username, user->username) == 0) { if (user_match) user->kick_me = TRUE; return; } } new_user.username = p_strdup(ctx->who.pool, line->username); new_user.kick_me = user_match; array_append(&k_pid->users, &new_user, 1); } static bool kick_pid_want_kicked(struct kick_context *ctx, const struct kick_pid *k_pid, bool *show_warning) { unsigned int kick_count = 0; const struct kick_user *user; if (array_count(&k_pid->users) == 1) { user = array_idx(&k_pid->users, 0); if (!user->kick_me) return FALSE; } else { array_foreach(&k_pid->users, user) { if (user->kick_me) kick_count++; } if (kick_count == 0) return FALSE; if (kick_count < array_count(&k_pid->users) && !ctx->force_kick) { array_foreach(&k_pid->users, user) { if (!user->kick_me) { array_append(&ctx->kicked_users, &user->username, 1); } } *show_warning = TRUE; return FALSE; } } return TRUE; } static void kick_print_kicked(struct kick_context *ctx, const bool show_warning) { unsigned int i, count; const char *const *users; if (array_count(&ctx->kicked_users) == 0) { if (ctx->cli) printf("no users kicked\n"); doveadm_exit_code = DOVEADM_EX_NOTFOUND; return; } if (ctx->cli) { if (show_warning) { printf("warning: other connections would also be " "kicked from following users:\n"); } else { printf("kicked connections from the following users:\n"); } } array_sort(&ctx->kicked_users, i_strcmp_p); users = array_get(&ctx->kicked_users, &count); doveadm_print(users[0]); for (i = 1; i < count; i++) { if (strcmp(users[i-1], users[i]) != 0) doveadm_print(users[i]); } if (ctx->cli) printf("\n"); if (show_warning) printf("Use the '-f' option to enforce the disconnect.\n"); } static void kick_users(struct kick_context *ctx) { bool show_enforce_warning = FALSE; struct hash_iterate_context *iter; void *key; struct kick_pid *k_pid; const struct kick_user *user; p_array_init(&ctx->kicked_users, ctx->who.pool, 10); iter = hash_table_iterate_init(ctx->pids); while (hash_table_iterate(iter, ctx->pids, &key, &k_pid)) { if (kick_pid_want_kicked(ctx, k_pid, &show_enforce_warning)) k_pid->kick = TRUE; } hash_table_iterate_deinit(&iter); if (show_enforce_warning) { kick_print_kicked(ctx, show_enforce_warning); return; } iter = hash_table_iterate_init(ctx->pids); while (hash_table_iterate(iter, ctx->pids, &key, &k_pid)) { if (!k_pid->kick) continue; if (kill(k_pid->pid, SIGTERM) < 0 && errno != ESRCH) { fprintf(stderr, "kill(%s, SIGTERM) failed: %m\n", dec2str(k_pid->pid)); } else { array_foreach(&k_pid->users, user) { array_append(&ctx->kicked_users, &user->username, 1); } } } hash_table_iterate_deinit(&iter); kick_print_kicked(ctx, show_enforce_warning); } static void cmd_kick(struct doveadm_cmd_context *cctx) { const char *const *masks; struct kick_context ctx; i_zero(&ctx); if (!doveadm_cmd_param_str(cctx, "socket-path", &(ctx.who.anvil_path))) ctx.who.anvil_path = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL); (void)doveadm_cmd_param_bool(cctx, "force", &(ctx.force_kick)); if (!doveadm_cmd_param_array(cctx, "mask", &masks)) { doveadm_exit_code = EX_USAGE; i_error("user and/or ip[/bits] must be specified."); return; } ctx.cli = cctx->cli; if (!ctx.cli) { /* force-kick is a pretty ugly option. its output can't be nicely translated to an API reply. it also wouldn't be very useful in scripts, only for preventing a new admin from accidentally kicking too many users. it's also useful only in a non-recommended setup where processes are handling multiple connections. so for now we'll preserve the option for CLI, but always do a force-kick with non-CLI. */ ctx.force_kick = TRUE; } ctx.who.pool = pool_alloconly_create("kick pids", 10240); hash_table_create_direct(&ctx.pids, ctx.who.pool, 0); if (who_parse_args(&ctx.who, masks)!=0) { hash_table_destroy(&ctx.pids); pool_unref(&ctx.who.pool); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{result} "); doveadm_print_header_simple("result"); who_lookup(&ctx.who, kick_aggregate_line); kick_users(&ctx); hash_table_destroy(&ctx.pids); pool_unref(&ctx.who.pool); } struct doveadm_cmd_ver2 doveadm_cmd_kick_ver2 = { .name = "kick", .cmd = cmd_kick, .usage = "[-a ] [|]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a',"socket-path",CMD_PARAM_STR,0) DOVEADM_CMD_PARAM('f',"force",CMD_PARAM_BOOL,0) DOVEADM_CMD_PARAM('\0',"mask",CMD_PARAM_ARRAY,CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-log.c0000644000175000017500000002343113167162046015106 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "time-util.h" #include "master-service-private.h" #include "master-service-settings.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include #include #include #include #define LAST_LOG_TYPE LOG_TYPE_PANIC #define TEST_LOG_MSG_PREFIX "This is Dovecot's " #define LOG_ERRORS_FNAME "log-errors" #define LOG_TIMESTAMP_FORMAT "%b %d %H:%M:%S" extern struct doveadm_cmd doveadm_cmd_log[]; static void ATTR_NULL(2) cmd_log_test(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { struct failure_context ctx; unsigned int i; master_service->log_initialized = FALSE; master_service->flags |= MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR; master_service_init_log(master_service, "doveadm: "); i_zero(&ctx); for (i = 0; i < LAST_LOG_TYPE; i++) { const char *prefix = failure_log_type_prefixes[i]; /* add timestamp so that syslog won't just write "repeated message" text */ ctx.type = i; i_log_type(&ctx, TEST_LOG_MSG_PREFIX"%s log (%u)", t_str_lcase(t_strcut(prefix, ':')), (unsigned int)ioloop_time); } } static void cmd_log_reopen(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { doveadm_master_send_signal(SIGUSR1); } struct log_find_file { const char *path; uoff_t size; /* 1 << enum log_type */ unsigned int mask; }; struct log_find_context { pool_t pool; HASH_TABLE(char *, struct log_find_file *) files; }; static void cmd_log_find_add(struct log_find_context *ctx, const char *path, enum log_type type) { struct log_find_file *file; char *key; file = hash_table_lookup(ctx->files, path); if (file == NULL) { file = p_new(ctx->pool, struct log_find_file, 1); file->path = key = p_strdup(ctx->pool, path); hash_table_insert(ctx->files, key, file); } file->mask |= 1 << type; } static void cmd_log_find_syslog_files(struct log_find_context *ctx, const char *path) { struct log_find_file *file; DIR *dir; struct dirent *d; struct stat st; char *key; string_t *full_path; size_t dir_len; dir = opendir(path); if (dir == NULL) { i_error("opendir(%s) failed: %m", path); return; } full_path = t_str_new(256); str_append(full_path, path); str_append_c(full_path, '/'); dir_len = str_len(full_path); while ((d = readdir(dir)) != NULL) { if (d->d_name[0] == '.') continue; str_truncate(full_path, dir_len); str_append(full_path, d->d_name); if (stat(str_c(full_path), &st) < 0) continue; if (S_ISDIR(st.st_mode)) { /* recursively go through all subdirectories */ cmd_log_find_syslog_files(ctx, str_c(full_path)); } else if (hash_table_lookup(ctx->files, str_c(full_path)) == NULL) { file = p_new(ctx->pool, struct log_find_file, 1); file->size = st.st_size; file->path = key = p_strdup(ctx->pool, str_c(full_path)); hash_table_insert(ctx->files, key, file); } } (void)closedir(dir); } static bool log_type_find(const char *str, enum log_type *type_r) { unsigned int i; size_t len = strlen(str); for (i = 0; i < LAST_LOG_TYPE; i++) { if (strncasecmp(str, failure_log_type_prefixes[i], len) == 0 && failure_log_type_prefixes[i][len] == ':') { *type_r = i; return TRUE; } } return FALSE; } static void cmd_log_find_syslog_file_messages(struct log_find_file *file) { struct istream *input; const char *line, *p; enum log_type type; int fd; fd = open(file->path, O_RDONLY); if (fd == -1) return; input = i_stream_create_fd_autoclose(&fd, 1024); i_stream_seek(input, file->size); while ((line = i_stream_read_next_line(input)) != NULL) { p = strstr(line, TEST_LOG_MSG_PREFIX); if (p == NULL) continue; p += strlen(TEST_LOG_MSG_PREFIX); /* log */ T_BEGIN { if (log_type_find(t_strcut(p, ' '), &type)) file->mask |= 1 << type; } T_END; } i_stream_destroy(&input); } static void cmd_log_find_syslog_messages(struct log_find_context *ctx) { struct hash_iterate_context *iter; struct stat st; char *key; struct log_find_file *file; iter = hash_table_iterate_init(ctx->files); while (hash_table_iterate(iter, ctx->files, &key, &file)) { if (stat(file->path, &st) < 0 || (uoff_t)st.st_size <= file->size) continue; cmd_log_find_syslog_file_messages(file); } hash_table_iterate_deinit(&iter); } static void cmd_log_find_syslog(struct log_find_context *ctx, int argc, char *argv[]) { const char *log_dir; struct stat st; if (argc > 1) log_dir = argv[1]; else if (stat("/var/log", &st) == 0 && S_ISDIR(st.st_mode)) log_dir = "/var/log"; else if (stat("/var/adm", &st) == 0 && S_ISDIR(st.st_mode)) log_dir = "/var/adm"; else return; printf("Looking for log files from %s\n", log_dir); cmd_log_find_syslog_files(ctx, log_dir); cmd_log_test(0, NULL); /* give syslog some time to write the messages to files */ sleep(1); cmd_log_find_syslog_messages(ctx); } static void cmd_log_find(int argc, char *argv[]) { const struct master_service_settings *set; const char *log_file_path; struct log_find_context ctx; unsigned int i; i_zero(&ctx); ctx.pool = pool_alloconly_create("log file", 1024*32); hash_table_create(&ctx.files, ctx.pool, 0, str_hash, strcmp); /* first get the paths that we know are used */ set = master_service_settings_get(master_service); log_file_path = set->log_path; if (strcmp(log_file_path, "syslog") == 0) log_file_path = ""; if (*log_file_path != '\0') { cmd_log_find_add(&ctx, log_file_path, LOG_TYPE_WARNING); cmd_log_find_add(&ctx, log_file_path, LOG_TYPE_ERROR); cmd_log_find_add(&ctx, log_file_path, LOG_TYPE_FATAL); } if (strcmp(set->info_log_path, "syslog") != 0) { if (*set->info_log_path != '\0') log_file_path = set->info_log_path; if (*log_file_path != '\0') cmd_log_find_add(&ctx, log_file_path, LOG_TYPE_INFO); } if (strcmp(set->debug_log_path, "syslog") != 0) { if (*set->debug_log_path != '\0') log_file_path = set->debug_log_path; if (*log_file_path != '\0') cmd_log_find_add(&ctx, log_file_path, LOG_TYPE_DEBUG); } if (*set->log_path == '\0' || strcmp(set->log_path, "syslog") == 0 || strcmp(set->info_log_path, "syslog") == 0 || strcmp(set->debug_log_path, "syslog") == 0) { /* at least some logs were logged via syslog */ cmd_log_find_syslog(&ctx, argc, argv); } /* print them */ for (i = 0; i < LAST_LOG_TYPE; i++) { struct hash_iterate_context *iter; char *key; struct log_find_file *file; bool found = FALSE; iter = hash_table_iterate_init(ctx.files); while (hash_table_iterate(iter, ctx.files, &key, &file)) { if ((file->mask & (1 << i)) != 0) { printf("%s%s\n", failure_log_type_prefixes[i], file->path); found = TRUE; } } hash_table_iterate_deinit(&iter); if (!found) printf("%sNot found\n", failure_log_type_prefixes[i]); } } static const char *t_cmd_log_error_trim(const char *orig) { size_t pos; /* Trim whitespace from suffix and remove ':' if it exists */ for (pos = strlen(orig); pos > 0; pos--) { if (orig[pos-1] != ' ') { if (orig[pos-1] == ':') pos--; break; } } return orig[pos] == '\0' ? orig : t_strndup(orig, pos); } static void cmd_log_error_write(const char *const *args, time_t min_timestamp) { /* */ const char *type_prefix = "?"; unsigned int type; time_t t; /* find type's prefix */ for (type = 0; type < LOG_TYPE_COUNT; type++) { if (strcmp(args[0], failure_log_type_names[type]) == 0) { type_prefix = failure_log_type_prefixes[type]; break; } } if (str_to_time(args[1], &t) < 0) { i_error("Invalid timestamp: %s", args[1]); t = 0; } if (t >= min_timestamp) { doveadm_print(t_strflocaltime(LOG_TIMESTAMP_FORMAT, t)); doveadm_print(t_cmd_log_error_trim(args[2])); doveadm_print(t_cmd_log_error_trim(type_prefix)); doveadm_print(args[3]); } } static void cmd_log_errors(int argc, char *argv[]) { struct istream *input; const char *path, *line, *const *args; time_t min_timestamp = 0; int c, fd; while ((c = getopt(argc, argv, "s:")) > 0) { switch (c) { case 's': if (str_to_time(optarg, &min_timestamp) < 0) i_fatal("Invalid timestamp: %s", optarg); break; default: help(&doveadm_cmd_log[3]); } } argv += optind - 1; if (argv[1] != NULL) help(&doveadm_cmd_log[3]); path = t_strconcat(doveadm_settings->base_dir, "/"LOG_ERRORS_FNAME, NULL); fd = net_connect_unix(path); if (fd == -1) i_fatal("net_connect_unix(%s) failed: %m", path); net_set_nonblock(fd, FALSE); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{timestamp} %{type}: %{prefix}: %{text}\n"); doveadm_print_header_simple("timestamp"); doveadm_print_header_simple("prefix"); doveadm_print_header_simple("type"); doveadm_print_header_simple("text"); while ((line = i_stream_read_next_line(input)) != NULL) T_BEGIN { args = t_strsplit_tabescaped(line); if (str_array_length(args) == 4) cmd_log_error_write(args, min_timestamp); else { i_error("Invalid input from log: %s", line); doveadm_exit_code = EX_PROTOCOL; } } T_END; i_stream_destroy(&input); } struct doveadm_cmd doveadm_cmd_log[] = { { cmd_log_test, "log test", "" }, { cmd_log_reopen, "log reopen", "" }, { cmd_log_find, "log find", "[]" }, }; struct doveadm_cmd_ver2 doveadm_cmd_log_errors_ver2 = { .name = "log errors", .usage = "[-s ]", .old_cmd = cmd_log_errors, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('s', "since", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }; void doveadm_register_log_commands(void) { unsigned int i; doveadm_cmd_register_ver2(&doveadm_cmd_log_errors_ver2); for (i = 0; i < N_ELEMENTS(doveadm_cmd_log); i++) doveadm_register_cmd(&doveadm_cmd_log[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-mail-mailbox-status.c0000644000175000017500000002113213165463624020221 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-search.h" #include "doveadm-print.h" #include "doveadm-mail.h" #include "doveadm-mailbox-list-iter.h" #define ALL_STATUS_ITEMS \ (STATUS_MESSAGES | STATUS_RECENT | \ STATUS_UIDNEXT | STATUS_UIDVALIDITY | \ STATUS_UNSEEN | STATUS_HIGHESTMODSEQ) #define ALL_METADATA_ITEMS \ (MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_GUID | \ MAILBOX_METADATA_FIRST_SAVE_DATE) #define TOTAL_STATUS_ITEMS \ (STATUS_MESSAGES | STATUS_RECENT | STATUS_UNSEEN) #define TOTAL_METADATA_ITEMS \ (MAILBOX_METADATA_VIRTUAL_SIZE) struct status_cmd_context { struct doveadm_mail_cmd_context ctx; struct mail_search_args *search_args; enum mailbox_status_items status_items; enum mailbox_metadata_items metadata_items; struct mailbox_status total_status; struct mailbox_metadata total_metadata; unsigned int total_sum:1; }; static void status_parse_fields(struct status_cmd_context *ctx, const char *const *fields) { if (*fields == NULL) i_fatal_status(EX_USAGE, "No status fields"); for (; *fields != NULL; fields++) { const char *field = *fields; if (strcmp(field, "all") == 0) { if (ctx->total_sum) { ctx->status_items |= TOTAL_STATUS_ITEMS; ctx->metadata_items |= TOTAL_METADATA_ITEMS; } else { ctx->status_items |= ALL_STATUS_ITEMS; ctx->metadata_items |= ALL_METADATA_ITEMS; } } else if (strcmp(field, "messages") == 0) ctx->status_items |= STATUS_MESSAGES; else if (strcmp(field, "recent") == 0) ctx->status_items |= STATUS_RECENT; else if (strcmp(field, "uidnext") == 0) ctx->status_items |= STATUS_UIDNEXT; else if (strcmp(field, "uidvalidity") == 0) ctx->status_items |= STATUS_UIDVALIDITY; else if (strcmp(field, "unseen") == 0) ctx->status_items |= STATUS_UNSEEN; else if (strcmp(field, "highestmodseq") == 0) ctx->status_items |= STATUS_HIGHESTMODSEQ; else if (strcmp(field, "vsize") == 0) ctx->metadata_items |= MAILBOX_METADATA_VIRTUAL_SIZE; else if (strcmp(field, "guid") == 0) ctx->metadata_items |= MAILBOX_METADATA_GUID; else if (strcmp(field, "firstsaved") == 0) ctx->metadata_items |= MAILBOX_METADATA_FIRST_SAVE_DATE; else { i_fatal_status(EX_USAGE, "Unknown status field: %s", field); } if (ctx->total_sum && ((ctx->status_items & ~TOTAL_STATUS_ITEMS) != 0 || (ctx->metadata_items & ~TOTAL_METADATA_ITEMS) != 0)) { i_fatal_status(EX_USAGE, "Status field %s can't be used with -t", field); } } } static void ATTR_NULL(2) status_output(struct status_cmd_context *ctx, struct mailbox *box, const struct mailbox_status *status, const struct mailbox_metadata *metadata) { if (box != NULL) doveadm_print(mailbox_get_vname(box)); if ((ctx->status_items & STATUS_MESSAGES) != 0) doveadm_print_num(status->messages); if ((ctx->status_items & STATUS_RECENT) != 0) doveadm_print_num(status->recent); if ((ctx->status_items & STATUS_UIDNEXT) != 0) doveadm_print_num(status->uidnext); if ((ctx->status_items & STATUS_UIDVALIDITY) != 0) doveadm_print_num(status->uidvalidity); if ((ctx->status_items & STATUS_UNSEEN) != 0) doveadm_print_num(status->unseen); if ((ctx->status_items & STATUS_HIGHESTMODSEQ) != 0) doveadm_print_num(status->highest_modseq); if ((ctx->metadata_items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) doveadm_print_num(metadata->virtual_size); if ((ctx->metadata_items & MAILBOX_METADATA_GUID) != 0) doveadm_print(guid_128_to_string(metadata->guid)); if ((ctx->metadata_items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) doveadm_print_num(metadata->first_save_date); } static void status_sum(struct status_cmd_context *ctx, const struct mailbox_status *status, const struct mailbox_metadata *metadata) { struct mailbox_status *dest = &ctx->total_status; dest->messages += status->messages; dest->recent += status->recent; dest->unseen += status->unseen; ctx->total_metadata.virtual_size += metadata->virtual_size; } static int status_mailbox(struct status_cmd_context *ctx, const struct mailbox_info *info) { struct mailbox *box; struct mailbox_status status; struct mailbox_metadata metadata; box = doveadm_mailbox_find(ctx->ctx.cur_mail_user, info->vname); mailbox_set_reason(box, ctx->ctx.cmd->name); if (mailbox_get_status(box, ctx->status_items, &status) < 0 || mailbox_get_metadata(box, ctx->metadata_items, &metadata) < 0) { i_error("Mailbox %s: Failed to lookup mailbox status: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, box); mailbox_free(&box); return -1; } if (!ctx->total_sum) status_output(ctx, box, &status, &metadata); else status_sum(ctx, &status, &metadata); mailbox_free(&box); return 0; } static int cmd_mailbox_status_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct status_cmd_context *ctx = (struct status_cmd_context *)_ctx; enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; i_zero(&ctx->total_status); i_zero(&ctx->total_metadata); iter = doveadm_mailbox_list_iter_init(_ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) { T_BEGIN { if (status_mailbox(ctx, info) < 0) ret = -1; } T_END; } if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; if (ctx->total_sum) { status_output(ctx, NULL, &ctx->total_status, &ctx->total_metadata); } return ret; } static void cmd_mailbox_status_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct status_cmd_context *ctx = (struct status_cmd_context *)_ctx; const char *fields = args[0]; if (fields == NULL || args[1] == NULL) doveadm_mail_help_name("mailbox status"); status_parse_fields(ctx, t_strsplit_spaces(fields, " ")); ctx->search_args = doveadm_mail_mailbox_search_args_build(args+1); if (!ctx->total_sum) { doveadm_print_header("mailbox", "mailbox", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } if ((ctx->status_items & STATUS_MESSAGES) != 0) doveadm_print_header_simple("messages"); if ((ctx->status_items & STATUS_RECENT) != 0) doveadm_print_header_simple("recent"); if ((ctx->status_items & STATUS_UIDNEXT) != 0) doveadm_print_header_simple("uidnext"); if ((ctx->status_items & STATUS_UIDVALIDITY) != 0) doveadm_print_header_simple("uidvalidity"); if ((ctx->status_items & STATUS_UNSEEN) != 0) doveadm_print_header_simple("unseen"); if ((ctx->status_items & STATUS_HIGHESTMODSEQ) != 0) doveadm_print_header_simple("highestmodseq"); if ((ctx->metadata_items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) doveadm_print_header_simple("vsize"); if ((ctx->metadata_items & MAILBOX_METADATA_GUID) != 0) doveadm_print_header_simple("guid"); if ((ctx->metadata_items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) doveadm_print_header_simple("firstsaved"); } static void cmd_mailbox_status_deinit(struct doveadm_mail_cmd_context *_ctx) { struct status_cmd_context *ctx = (struct status_cmd_context *)_ctx; if (ctx->search_args != NULL) mail_search_args_unref(&ctx->search_args); } static bool cmd_mailbox_status_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct status_cmd_context *ctx = (struct status_cmd_context *)_ctx; switch (c) { case 't': ctx->total_sum = TRUE; break; case 'f': break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_mailbox_status_alloc(void) { struct status_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct status_cmd_context); ctx->ctx.getopt_args = "t"; ctx->ctx.v.parse_arg = cmd_mailbox_status_parse_arg; ctx->ctx.v.init = cmd_mailbox_status_init; ctx->ctx.v.deinit = cmd_mailbox_status_deinit; ctx->ctx.v.run = cmd_mailbox_status_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_mailbox_status_ver2 = { .name = "mailbox status", .mail_cmd = cmd_mailbox_status_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX" [...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('t', "total-sum", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('f', "field", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('\0', "fieldstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL | CMD_PARAM_FLAG_DO_NOT_EXPOSE) /* FIXME: horrible hack, remove me when possible */ DOVEADM_CMD_PARAM('\0', "mailbox-mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-mailbox.c0000644000175000017500000006030013123174404016665 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "unichar.h" #include "imap-utf7.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-search-build.h" #include "doveadm-print.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail.h" #include struct doveadm_mailbox_cmd_context { struct doveadm_mail_cmd_context ctx; bool subscriptions; }; struct mailbox_cmd_context { struct doveadm_mailbox_cmd_context ctx; ARRAY_TYPE(const_string) mailboxes; }; struct create_cmd_context { struct doveadm_mailbox_cmd_context ctx; ARRAY_TYPE(const_string) mailboxes; struct mailbox_update update; }; struct delete_cmd_context { struct doveadm_mailbox_cmd_context ctx; ARRAY_TYPE(const_string) mailboxes; bool recursive; bool require_empty; bool unsafe; }; struct rename_cmd_context { struct doveadm_mailbox_cmd_context ctx; const char *oldname, *newname; }; struct list_cmd_context { struct doveadm_mailbox_cmd_context ctx; struct mail_search_args *search_args; bool mutf7; }; struct update_cmd_context { struct doveadm_mailbox_cmd_context ctx; const char *mailbox; struct mailbox_update update; }; struct path_cmd_context { struct doveadm_mailbox_cmd_context ctx; const char *mailbox; enum mailbox_list_path_type path_type; }; static const char *mailbox_list_path_type_names[] = { "dir", "alt-dir", "mailbox", "alt-mailbox", "control", "index", "index-private" }; void doveadm_mailbox_args_check(const char *const args[]) { unsigned int i; for (i = 0; args[i] != NULL; i++) { if (!uni_utf8_str_is_valid(args[i])) { i_fatal_status(EX_DATAERR, "Mailbox name not valid UTF-8: %s", args[i]); } } } static bool cmd_mailbox_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct doveadm_mailbox_cmd_context *ctx = (struct doveadm_mailbox_cmd_context *)_ctx; switch (c) { case 's': ctx->subscriptions = TRUE; break; default: return FALSE; } return TRUE; } #define doveadm_mailbox_cmd_alloc(type) \ (type *)doveadm_mailbox_cmd_alloc_size(sizeof(type)) static struct doveadm_mail_cmd_context * doveadm_mailbox_cmd_alloc_size(size_t size) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc_size(size); ctx->getopt_args = "s"; ctx->v.parse_arg = cmd_mailbox_parse_arg; return ctx; } static bool cmd_mailbox_list_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct list_cmd_context *ctx = (struct list_cmd_context *)_ctx; switch (c) { case '7': ctx->mutf7 = TRUE; break; case '8': ctx->mutf7 = FALSE; break; case 's': ctx->ctx.subscriptions = TRUE; break; default: return FALSE; } return TRUE; } static int cmd_mailbox_list_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct list_cmd_context *ctx = (struct list_cmd_context *)_ctx; enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; string_t *str = t_str_new(256); if (ctx->ctx.subscriptions) iter_flags |= MAILBOX_LIST_ITER_SELECT_SUBSCRIBED; iter = doveadm_mailbox_list_iter_full_init(_ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) { if (!ctx->mutf7) doveadm_print(info->vname); else { str_truncate(str, 0); if (imap_utf8_to_utf7(info->vname, str) < 0) i_unreached(); doveadm_print(str_c(str)); } } if (doveadm_mailbox_list_iter_deinit(&iter) < 0) return -1; return 0; } struct mail_search_args * doveadm_mail_mailbox_search_args_build(const char *const args[]) { struct mail_search_args *search_args; struct mail_search_arg *arg; enum mail_search_arg_type type; unsigned int i; doveadm_mailbox_args_check(args); search_args = mail_search_build_init(); for (i = 0; args[i] != NULL; i++) { if (strchr(args[i], '*') != NULL || strchr(args[i], '%') != NULL) type = SEARCH_MAILBOX_GLOB; else type = SEARCH_MAILBOX; arg = mail_search_build_add(search_args, type); arg->value.str = p_strdup(search_args->pool, args[i]); } if (i > 1) { struct mail_search_arg *subargs = search_args->args; search_args->args = NULL; arg = mail_search_build_add(search_args, SEARCH_OR); arg->value.subargs = subargs; } return search_args; } static void cmd_mailbox_list_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct list_cmd_context *ctx = (struct list_cmd_context *)_ctx; doveadm_print_header("mailbox", "mailbox", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); ctx->search_args = doveadm_mail_mailbox_search_args_build(args); } static void cmd_mailbox_list_deinit(struct doveadm_mail_cmd_context *_ctx) { struct list_cmd_context *ctx = (struct list_cmd_context *)_ctx; if (ctx->search_args != NULL) mail_search_args_unref(&ctx->search_args); } static struct doveadm_mail_cmd_context *cmd_mailbox_list_alloc(void) { struct list_cmd_context *ctx; ctx = doveadm_mailbox_cmd_alloc(struct list_cmd_context); ctx->ctx.ctx.v.init = cmd_mailbox_list_init; ctx->ctx.ctx.v.deinit = cmd_mailbox_list_deinit; ctx->ctx.ctx.v.run = cmd_mailbox_list_run; ctx->ctx.ctx.v.parse_arg = cmd_mailbox_list_parse_arg; ctx->ctx.ctx.getopt_args = "78s"; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx.ctx; } static int cmd_mailbox_create_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct create_cmd_context *ctx = (struct create_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; const char *const *namep; int ret = 0; array_foreach(&ctx->mailboxes, namep) { const char *name = *namep; size_t len; bool directory = FALSE; ns = mail_namespace_find(user->namespaces, name); len = strlen(name); if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) { name = t_strndup(name, len-1); directory = TRUE; } box = mailbox_alloc(ns->list, name, 0); mailbox_set_reason(box, _ctx->cmd->name); if (mailbox_create(box, &ctx->update, directory) < 0) { i_error("Can't create mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } if (ctx->ctx.subscriptions) { if (mailbox_set_subscribed(box, TRUE) < 0) { i_error("Can't subscribe to mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } mailbox_free(&box); } return ret; } static void cmd_mailbox_create_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct mailbox_cmd_context *ctx = (struct mailbox_cmd_context *)_ctx; const char *name; unsigned int i; if (args[0] == NULL) doveadm_mail_help_name("mailbox create"); doveadm_mailbox_args_check(args); for (i = 0; args[i] != NULL; i++) { name = p_strdup(ctx->ctx.ctx.pool, args[i]); array_append(&ctx->mailboxes, &name, 1); } } static bool cmd_mailbox_create_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct create_cmd_context *ctx = (struct create_cmd_context *)_ctx; switch (c) { case 'g': if (guid_128_from_string(optarg, ctx->update.mailbox_guid) < 0) doveadm_mail_help_name("mailbox create"); break; case 's': ctx->ctx.subscriptions = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_mailbox_create_alloc(void) { struct create_cmd_context *ctx; ctx = doveadm_mailbox_cmd_alloc(struct create_cmd_context); ctx->ctx.ctx.v.init = cmd_mailbox_create_init; ctx->ctx.ctx.v.run = cmd_mailbox_create_run; ctx->ctx.ctx.v.parse_arg = cmd_mailbox_create_parse_arg; ctx->ctx.ctx.getopt_args = "g:s"; p_array_init(&ctx->mailboxes, ctx->ctx.ctx.pool, 16); return &ctx->ctx.ctx; } static int i_strcmp_reverse_p(const char *const *s1, const char *const *s2) { return -strcmp(*s1, *s2); } static int get_child_mailboxes(struct mail_user *user, ARRAY_TYPE(const_string) *mailboxes, const char *name) { struct mailbox_list_iterate_context *iter; struct mail_namespace *ns; const struct mailbox_info *info; const char *pattern, *child_name; ns = mail_namespace_find(user->namespaces, name); pattern = name[0] == '\0' ? "*" : t_strdup_printf("%s%c*", name, mail_namespace_get_sep(ns)); iter = mailbox_list_iter_init(ns->list, pattern, MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { child_name = t_strdup(info->vname); array_append(mailboxes, &child_name, 1); } return mailbox_list_iter_deinit(&iter); } static int cmd_mailbox_delete_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct delete_cmd_context *ctx = (struct delete_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; struct mail_storage *storage; const char *const *namep; ARRAY_TYPE(const_string) recursive_mailboxes; const ARRAY_TYPE(const_string) *mailboxes = &ctx->mailboxes; enum mailbox_flags mailbox_flags = 0; int ret = 0, ret2; if (ctx->unsafe) mailbox_flags |= MAILBOX_FLAG_DELETE_UNSAFE; if (ctx->recursive) { t_array_init(&recursive_mailboxes, 32); array_foreach(&ctx->mailboxes, namep) { if (get_child_mailboxes(user, &recursive_mailboxes, *namep) < 0) { doveadm_mail_failed_error(_ctx, MAIL_ERROR_TEMP); ret = -1; } if ((*namep)[0] != '\0') array_append(&recursive_mailboxes, namep, 1); } array_sort(&recursive_mailboxes, i_strcmp_reverse_p); mailboxes = &recursive_mailboxes; } array_foreach(mailboxes, namep) { const char *name = *namep; ns = mail_namespace_find(user->namespaces, name); box = mailbox_alloc(ns->list, name, mailbox_flags); mailbox_set_reason(box, _ctx->cmd->name); storage = mailbox_get_storage(box); ret2 = ctx->require_empty ? mailbox_delete_empty(box) : mailbox_delete(box); if (ret2 < 0) { i_error("Can't delete mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } if (ctx->ctx.subscriptions) { if (mailbox_set_subscribed(box, FALSE) < 0) { i_error("Can't unsubscribe mailbox %s: %s", name, mail_storage_get_last_internal_error(storage, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } mailbox_free(&box); } return ret; } static void cmd_mailbox_delete_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct delete_cmd_context *ctx = (struct delete_cmd_context *)_ctx; const char *name; unsigned int i; if (args[0] == NULL) doveadm_mail_help_name("mailbox delete"); doveadm_mailbox_args_check(args); for (i = 0; args[i] != NULL; i++) { name = p_strdup(ctx->ctx.ctx.pool, args[i]); array_append(&ctx->mailboxes, &name, 1); } array_sort(&ctx->mailboxes, i_strcmp_reverse_p); } static bool cmd_mailbox_delete_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct delete_cmd_context *ctx = (struct delete_cmd_context *)_ctx; switch (c) { case 'r': ctx->recursive = TRUE; break; case 's': ctx->ctx.subscriptions = TRUE; break; case 'e': ctx->require_empty = TRUE; break; case 'Z': ctx->unsafe = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_mailbox_delete_alloc(void) { struct delete_cmd_context *ctx; ctx = doveadm_mailbox_cmd_alloc(struct delete_cmd_context); ctx->ctx.ctx.v.init = cmd_mailbox_delete_init; ctx->ctx.ctx.v.run = cmd_mailbox_delete_run; ctx->ctx.ctx.v.parse_arg = cmd_mailbox_delete_parse_arg; ctx->ctx.ctx.getopt_args = "ersZ"; p_array_init(&ctx->mailboxes, ctx->ctx.ctx.pool, 16); return &ctx->ctx.ctx; } static int cmd_mailbox_rename_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct rename_cmd_context *ctx = (struct rename_cmd_context *)_ctx; struct mail_namespace *oldns, *newns; struct mailbox *oldbox, *newbox; const char *oldname = ctx->oldname; const char *newname = ctx->newname; int ret = 0; oldns = mail_namespace_find(user->namespaces, oldname); newns = mail_namespace_find(user->namespaces, newname); oldbox = mailbox_alloc(oldns->list, oldname, 0); newbox = mailbox_alloc(newns->list, newname, 0); mailbox_set_reason(oldbox, _ctx->cmd->name); mailbox_set_reason(newbox, _ctx->cmd->name); if (mailbox_rename(oldbox, newbox) < 0) { i_error("Can't rename mailbox %s to %s: %s", oldname, newname, mailbox_get_last_internal_error(oldbox, NULL)); doveadm_mail_failed_mailbox(_ctx, oldbox); ret = -1; } if (ctx->ctx.subscriptions) { if (mailbox_set_subscribed(oldbox, FALSE) < 0) { i_error("Can't unsubscribe mailbox %s: %s", ctx->oldname, mailbox_get_last_internal_error(oldbox, NULL)); doveadm_mail_failed_mailbox(_ctx, oldbox); ret = -1; } if (mailbox_set_subscribed(newbox, TRUE) < 0) { i_error("Can't subscribe to mailbox %s: %s", ctx->newname, mailbox_get_last_internal_error(newbox, NULL)); doveadm_mail_failed_mailbox(_ctx, newbox); ret = -1; } } mailbox_free(&oldbox); mailbox_free(&newbox); return ret; } static void cmd_mailbox_rename_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct rename_cmd_context *ctx = (struct rename_cmd_context *)_ctx; if (str_array_length(args) != 2) doveadm_mail_help_name("mailbox rename"); doveadm_mailbox_args_check(args); ctx->oldname = p_strdup(ctx->ctx.ctx.pool, args[0]); ctx->newname = p_strdup(ctx->ctx.ctx.pool, args[1]); } static struct doveadm_mail_cmd_context *cmd_mailbox_rename_alloc(void) { struct rename_cmd_context *ctx; ctx = doveadm_mailbox_cmd_alloc(struct rename_cmd_context); ctx->ctx.ctx.v.init = cmd_mailbox_rename_init; ctx->ctx.ctx.v.run = cmd_mailbox_rename_run; return &ctx->ctx.ctx; } static int cmd_mailbox_subscribe_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct mailbox_cmd_context *ctx = (struct mailbox_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; const char *const *namep; int ret = 0; array_foreach(&ctx->mailboxes, namep) { const char *name = *namep; ns = mail_namespace_find(user->namespaces, name); box = mailbox_alloc(ns->list, name, 0); mailbox_set_reason(box, _ctx->cmd->name); if (mailbox_set_subscribed(box, ctx->ctx.subscriptions) < 0) { i_error("Can't %s mailbox %s: %s", name, ctx->ctx.subscriptions ? "subscribe to" : "unsubscribe", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } mailbox_free(&box); } return ret; } static void cmd_mailbox_subscribe_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct mailbox_cmd_context *ctx = (struct mailbox_cmd_context *)_ctx; const char *name; unsigned int i; if (args[0] == NULL) { doveadm_mail_help_name(ctx->ctx.subscriptions ? "mailbox subscribe" : "mailbox unsubscribe"); } doveadm_mailbox_args_check(args); for (i = 0; args[i] != NULL; i++) { name = p_strdup(ctx->ctx.ctx.pool, args[i]); array_append(&ctx->mailboxes, &name, 1); } } static struct doveadm_mail_cmd_context * cmd_mailbox_subscriptions_alloc(bool subscriptions) { struct mailbox_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct mailbox_cmd_context); ctx->ctx.subscriptions = subscriptions; ctx->ctx.ctx.v.parse_arg = cmd_mailbox_parse_arg; ctx->ctx.ctx.v.init = cmd_mailbox_subscribe_init; ctx->ctx.ctx.v.run = cmd_mailbox_subscribe_run; p_array_init(&ctx->mailboxes, ctx->ctx.ctx.pool, 16); return &ctx->ctx.ctx; } static struct doveadm_mail_cmd_context *cmd_mailbox_subscribe_alloc(void) { return cmd_mailbox_subscriptions_alloc(TRUE); } static struct doveadm_mail_cmd_context *cmd_mailbox_unsubscribe_alloc(void) { return cmd_mailbox_subscriptions_alloc(FALSE); } static void cmd_mailbox_update_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct update_cmd_context *ctx = (struct update_cmd_context *)_ctx; if (str_array_length(args) != 1) doveadm_mail_help_name("mailbox update"); doveadm_mailbox_args_check(args); ctx->mailbox = args[0]; if ((ctx->update.min_first_recent_uid != 0 || ctx->update.min_next_uid != 0) && ctx->update.min_first_recent_uid > ctx->update.min_next_uid) { i_fatal_status(EX_DATAERR, "min_first_recent_uid > min_next_uid"); } } static bool cmd_mailbox_update_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct update_cmd_context *ctx = (struct update_cmd_context *)_ctx; switch (c) { case 'g': if (guid_128_from_string(optarg, ctx->update.mailbox_guid) < 0) doveadm_mail_help_name("mailbox update"); break; case 'V': if (str_to_uint32(optarg, &(ctx->update.uid_validity)) < 0) doveadm_mail_help_name("mailbox update"); break; case 'N': if (str_to_uint32(optarg, &(ctx->update.min_next_uid)) < 0) doveadm_mail_help_name("mailbox update"); break; case 'R': if (str_to_uint32(optarg, &(ctx->update.min_first_recent_uid)) < 0) doveadm_mail_help_name("mailbox update"); break; case 'H': if (str_to_uint64(optarg, &(ctx->update.min_highest_modseq)) < 0) doveadm_mail_help_name("mailbox update"); break; case 'P': if (str_to_uint64(optarg, &(ctx->update.min_highest_pvt_modseq)) < 0) doveadm_mail_help_name("mailbox update"); break; default: return FALSE; } return TRUE; } static int cmd_mailbox_update_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct update_cmd_context *ctx = (struct update_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; enum mail_error mail_error; int ret = 0; ns = mail_namespace_find(user->namespaces, ctx->mailbox); box = mailbox_alloc(ns->list, ctx->mailbox, 0); mailbox_set_reason(box, _ctx->cmd->name); if ((ret = mailbox_update(box, &(ctx->update))) != 0) { i_error("Cannot update %s: %s", ctx->mailbox, mailbox_get_last_internal_error(box, &mail_error)); doveadm_mail_failed_error(_ctx, mail_error); } mailbox_free(&box); return ret; } static struct doveadm_mail_cmd_context *cmd_mailbox_update_alloc(void) { struct update_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct update_cmd_context); ctx->ctx.ctx.v.parse_arg = cmd_mailbox_update_parse_arg; ctx->ctx.ctx.v.init = cmd_mailbox_update_init; ctx->ctx.ctx.v.run = cmd_mailbox_update_run; return &ctx->ctx.ctx; } static void cmd_mailbox_path_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct update_cmd_context *ctx = (struct update_cmd_context *)_ctx; if (str_array_length(args) != 1) doveadm_mail_help_name("mailbox path"); doveadm_mailbox_args_check(args); ctx->mailbox = args[0]; doveadm_print_header("path", "path", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } static bool mailbox_list_path_type_name_parse(const char *name, enum mailbox_list_path_type *type_r) { enum mailbox_list_path_type type; for (type = 0; type < N_ELEMENTS(mailbox_list_path_type_names); type++) { if (strcmp(mailbox_list_path_type_names[type], name) == 0) { *type_r = type; return TRUE; } } return FALSE; } static bool cmd_mailbox_path_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct path_cmd_context *ctx = (struct path_cmd_context *)_ctx; switch (c) { case 't': if (!mailbox_list_path_type_name_parse(optarg, &ctx->path_type)) doveadm_mail_help_name("mailbox path"); break; default: return FALSE; } return TRUE; } static int cmd_mailbox_path_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct path_cmd_context *ctx = (struct path_cmd_context *)_ctx; struct mail_namespace *ns; enum mail_error mail_error; const char *storage_name, *path; int ret; ns = mail_namespace_find(user->namespaces, ctx->mailbox); storage_name = mailbox_list_get_storage_name(ns->list, ctx->mailbox); ret = mailbox_list_get_path(ns->list, storage_name, ctx->path_type, &path); if (ret < 0) { i_error("Failed to lookup mailbox %s path: %s", ctx->mailbox, mailbox_list_get_last_internal_error(ns->list, &mail_error)); doveadm_mail_failed_error(_ctx, mail_error); } else if (ret > 0) { doveadm_print(path); } return ret; } static struct doveadm_mail_cmd_context *cmd_mailbox_path_alloc(void) { struct path_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct path_cmd_context); ctx->path_type = MAILBOX_LIST_PATH_TYPE_INDEX; ctx->ctx.ctx.v.parse_arg = cmd_mailbox_path_parse_arg; ctx->ctx.ctx.v.init = cmd_mailbox_path_init; ctx->ctx.ctx.v.run = cmd_mailbox_path_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx.ctx; } struct doveadm_cmd_ver2 doveadm_cmd_mailbox_list_ver2 = { .name = "mailbox list", .mail_cmd = cmd_mailbox_list_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-7|-8] [-s] [ [...]]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('7', "mutf7", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('8', "utf8", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('s', "subscriptions", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox-mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_create_ver2 = { .name = "mailbox create", .mail_cmd = cmd_mailbox_create_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] [-g ] [...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "subscriptions", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('g', "guid", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_delete_ver2 = { .name = "mailbox delete", .mail_cmd = cmd_mailbox_delete_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-e] [-r] [-s] [-Z] [...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('e', "require-empty", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('s', "subscriptions", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('r', "recursive", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('Z', "unsafe", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_rename_ver2 = { .name = "mailbox rename", .mail_cmd = cmd_mailbox_rename_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-s] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('s', "subscriptions", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "new-name", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_subscribe_ver2 = { .name = "mailbox subscribe", .mail_cmd = cmd_mailbox_subscribe_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX" [...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_unsubscribe_ver2 = { .name = "mailbox unsubscribe", .mail_cmd = cmd_mailbox_unsubscribe_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX" [...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_update_ver2 = { .name = "mailbox update", .mail_cmd = cmd_mailbox_update_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[--mailbox-guid guid] [--uid-validity uid] [--min-next-uid uid] [--min-first-recent-uid uid] [--min-highest-modseq seq] [--min-highest-pvt-modseq seq] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('g', "mailbox-guid", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('V', "uid-validity", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('N', "min-next-uid", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('R', "min-first-recent-uid", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('H', "min-highest-modseq", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('P', "min-highest-pvt-modseq", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mailbox_path_ver2 = { .name = "mailbox path", .mail_cmd = cmd_mailbox_path_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-t ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('t', "type", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-fs.c0000644000175000017500000004200313165463624014735 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "md5.h" #include "sha2.h" #include "hash-method.h" #include "hex-binary.h" #include "istream.h" #include "ostream.h" #include "iostream-ssl.h" #include "fs-api.h" #include "doveadm.h" #include "doveadm-print.h" #include #include static void fs_cmd_help(doveadm_command_t *cmd); static void cmd_fs_delete(int argc, char *argv[]); static struct fs * cmd_fs_init(int *argc, char **argv[], int own_arg_count, doveadm_command_t *cmd) { struct ssl_iostream_settings ssl_set; struct fs_settings fs_set; struct fs *fs; const char *error; if (own_arg_count > 0) { if (*argc != 3 + own_arg_count) fs_cmd_help(cmd); } else { if (*argc <= 3) fs_cmd_help(cmd); } i_zero(&ssl_set); ssl_set.ca_dir = doveadm_settings->ssl_client_ca_dir; ssl_set.ca_file = doveadm_settings->ssl_client_ca_file; ssl_set.verbose = doveadm_debug; i_zero(&fs_set); fs_set.ssl_client_set = &ssl_set; fs_set.temp_dir = "/tmp"; fs_set.base_dir = doveadm_settings->base_dir; fs_set.debug = doveadm_debug; if (fs_init((*argv)[1], (*argv)[2], &fs_set, &fs, &error) < 0) i_fatal("fs_init() failed: %s", error); *argc += 3; *argv += 3; return fs; } static void cmd_fs_get(int argc, char *argv[]) { struct fs *fs; struct fs_file *file; struct istream *input; const unsigned char *data; size_t size; ssize_t ret; doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); doveadm_print_header("content", "content", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_get); file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY); input = fs_read_stream(file, IO_BLOCK_SIZE); while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { doveadm_print_stream(data, size); i_stream_skip(input, size); } doveadm_print_stream("", 0); i_assert(ret == -1); if (input->stream_errno == ENOENT) { i_error("%s doesn't exist: %s", fs_file_path(file), i_stream_get_error(input)); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else if (input->stream_errno != 0) { i_error("read(%s) failed: %s", fs_file_path(file), i_stream_get_error(input)); doveadm_exit_code = EX_TEMPFAIL; } i_stream_unref(&input); fs_file_deinit(&file); fs_deinit(&fs); } static void cmd_fs_put(int argc, char *argv[]) { struct fs *fs; enum fs_properties props; const char *src_path, *dest_path; struct fs_file *file; struct istream *input; struct ostream *output; buffer_t *hash = NULL; off_t ret; int c; while ((c = getopt(argc, argv, "h:")) > 0) { switch (c) { case 'h': hash = buffer_create_dynamic(pool_datastack_create(), 32); if (hex_to_binary(optarg, hash) < 0) i_fatal("Invalid -h parameter: Hash not in hex"); break; default: fs_cmd_help(cmd_fs_put); } } argc -= optind-1; argv += optind-1; fs = cmd_fs_init(&argc, &argv, 2, cmd_fs_put); src_path = argv[0]; dest_path = argv[1]; file = fs_file_init(fs, dest_path, FS_OPEN_MODE_REPLACE); props = fs_get_properties(fs); if (hash == NULL) ; else if (hash->used == hash_method_md5.digest_size) { if ((props & FS_PROPERTY_WRITE_HASH_MD5) == 0) i_fatal("fs backend doesn't support MD5 hashes"); fs_write_set_hash(file, hash_method_lookup(hash_method_md5.name), hash->data); } else if (hash->used == hash_method_sha256.digest_size) { if ((props & FS_PROPERTY_WRITE_HASH_SHA256) == 0) i_fatal("fs backend doesn't support SHA256 hashes"); fs_write_set_hash(file, hash_method_lookup(hash_method_sha256.name), hash->data); } output = fs_write_stream(file); input = i_stream_create_file(src_path, IO_BLOCK_SIZE); if ((ret = o_stream_send_istream(output, input)) < 0) { if (output->stream_errno != 0) { i_error("write(%s) failed: %s", dest_path, o_stream_get_error(output)); } else { i_error("read(%s) failed: %s", src_path, i_stream_get_error(input)); } doveadm_exit_code = EX_TEMPFAIL; } i_stream_destroy(&input); if (fs_write_stream_finish(file, &output) < 0) { i_error("fs_write_stream_finish() failed: %s", fs_file_last_error(file)); doveadm_exit_code = EX_TEMPFAIL; } fs_file_deinit(&file); fs_deinit(&fs); } static void cmd_fs_copy(int argc, char *argv[]) { struct fs *fs; struct fs_file *src_file, *dest_file; const char *src_path, *dest_path; fs = cmd_fs_init(&argc, &argv, 2, cmd_fs_copy); src_path = argv[0]; dest_path = argv[1]; src_file = fs_file_init(fs, src_path, FS_OPEN_MODE_READONLY); dest_file = fs_file_init(fs, dest_path, FS_OPEN_MODE_REPLACE); if (fs_copy(src_file, dest_file) == 0) ; else if (errno == ENOENT) { i_error("%s doesn't exist: %s", src_path, fs_last_error(fs)); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else { i_error("fs_copy(%s, %s) failed: %s", src_path, dest_path, fs_last_error(fs)); doveadm_exit_code = EX_TEMPFAIL; } fs_file_deinit(&src_file); fs_file_deinit(&dest_file); fs_deinit(&fs); } static void cmd_fs_stat(int argc, char *argv[]) { struct fs *fs; struct fs_file *file; struct stat st; fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_stat); file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY); doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{path} size=%{size}"); doveadm_print_header_simple("path"); doveadm_print_header("size", "size", DOVEADM_PRINT_HEADER_FLAG_NUMBER); if (fs_stat(file, &st) == 0) { doveadm_print(fs_file_path(file)); doveadm_print(dec2str(st.st_size)); } else if (errno == ENOENT) { i_error("%s doesn't exist: %s", fs_file_path(file), fs_file_last_error(file)); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else { i_error("fs_stat(%s) failed: %s", fs_file_path(file), fs_file_last_error(file)); doveadm_exit_code = EX_TEMPFAIL; } fs_file_deinit(&file); fs_deinit(&fs); } static void cmd_fs_metadata(int argc, char *argv[]) { struct fs *fs; struct fs_file *file; const struct fs_metadata *m; const ARRAY_TYPE(fs_metadata) *metadata; fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_metadata); file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY); doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{key}=%{value}\n"); doveadm_print_header_simple("key"); doveadm_print_header_simple("value"); if (fs_get_metadata(file, &metadata) == 0) { array_foreach(metadata, m) { doveadm_print(m->key); doveadm_print(m->value); } } else if (errno == ENOENT) { i_error("%s doesn't exist: %s", fs_file_path(file), fs_file_last_error(file)); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else { i_error("fs_stat(%s) failed: %s", fs_file_path(file), fs_file_last_error(file)); doveadm_exit_code = EX_TEMPFAIL; } fs_file_deinit(&file); fs_deinit(&fs); } struct fs_delete_ctx { struct fs *fs; const char *path_prefix; unsigned int files_count; struct fs_file **files; }; static int cmd_fs_delete_ctx_run(struct fs_delete_ctx *ctx) { unsigned int i; int ret = 0; for (i = 0; i < ctx->files_count; i++) { if (ctx->files[i] == NULL) ; else if (fs_delete(ctx->files[i]) == 0) fs_file_deinit(&ctx->files[i]); else if (errno == EAGAIN) { if (ret == 0) ret = 1; } else if (errno == ENOENT) { i_error("%s doesn't exist: %s", fs_file_path(ctx->files[i]), fs_file_last_error(ctx->files[i])); doveadm_exit_code = DOVEADM_EX_NOTFOUND; ret = -1; } else { i_error("fs_delete(%s) failed: %s", fs_file_path(ctx->files[i]), fs_file_last_error(ctx->files[i])); doveadm_exit_code = EX_TEMPFAIL; ret = -1; } } return ret; } static int doveadm_fs_delete_async_fname(struct fs_delete_ctx *ctx, const char *fname) { unsigned int i; int ret; for (i = 0; i < ctx->files_count; i++) { if (ctx->files[i] != NULL) continue; ctx->files[i] = fs_file_init(ctx->fs, t_strdup_printf("%s%s", ctx->path_prefix, fname), FS_OPEN_MODE_READONLY | FS_OPEN_FLAG_ASYNC | FS_OPEN_FLAG_ASYNC_NOQUEUE); fname = NULL; break; } if ((ret = cmd_fs_delete_ctx_run(ctx)) < 0) return -1; if (fname != NULL) { if (ret > 0 && fs_wait_async(ctx->fs) < 0) { i_error("fs_wait_async() failed: %s", fs_last_error(ctx->fs)); doveadm_exit_code = EX_TEMPFAIL; return -1;; } return doveadm_fs_delete_async_fname(ctx, fname); } return 0; } static void doveadm_fs_delete_async_finish(struct fs_delete_ctx *ctx) { unsigned int i; while (doveadm_exit_code == 0 && cmd_fs_delete_ctx_run(ctx) > 0) { if (fs_wait_async(ctx->fs) < 0) { i_error("fs_wait_async() failed: %s", fs_last_error(ctx->fs)); doveadm_exit_code = EX_TEMPFAIL; break; } } for (i = 0; i < ctx->files_count; i++) { if (ctx->files[i] != NULL) fs_file_deinit(&ctx->files[i]); } } static void cmd_fs_delete_dir_recursive(struct fs *fs, unsigned int async_count, const char *path_prefix) { struct fs_iter *iter; ARRAY_TYPE(const_string) fnames; struct fs_delete_ctx ctx; const char *fname, *const *fnamep; int ret; i_zero(&ctx); ctx.fs = fs; ctx.path_prefix = path_prefix; ctx.files_count = I_MAX(async_count, 1); ctx.files = t_new(struct fs_file *, ctx.files_count); /* delete subdirs first. all fs backends can't handle recursive lookups, so save the list first. */ t_array_init(&fnames, 8); iter = fs_iter_init(fs, path_prefix, FS_ITER_FLAG_DIRS); while ((fname = fs_iter_next(iter)) != NULL) { /* append "/" so that if FS_PROPERTY_DIRECTORIES is set, we'll include the "/" suffix in the filename when deleting it. */ fname = t_strconcat(fname, "/", NULL); array_append(&fnames, &fname, 1); } if (fs_iter_deinit(&iter) < 0) { i_error("fs_iter_deinit(%s) failed: %s", path_prefix, fs_last_error(fs)); doveadm_exit_code = EX_TEMPFAIL; } array_foreach(&fnames, fnamep) T_BEGIN { cmd_fs_delete_dir_recursive(fs, async_count, t_strdup_printf("%s%s", path_prefix, *fnamep)); } T_END; /* delete files. again because we're doing this asynchronously finish the iteration first. */ if ((fs_get_properties(fs) & FS_PROPERTY_DIRECTORIES) != 0) { /* we need to explicitly delete also the directories */ } else { array_clear(&fnames); } iter = fs_iter_init(fs, path_prefix, 0); while ((fname = fs_iter_next(iter)) != NULL) { fname = t_strdup(fname); array_append(&fnames, &fname, 1); } if (fs_iter_deinit(&iter) < 0) { i_error("fs_iter_deinit(%s) failed: %s", path_prefix, fs_last_error(fs)); doveadm_exit_code = EX_TEMPFAIL; } array_foreach(&fnames, fnamep) { T_BEGIN { ret = doveadm_fs_delete_async_fname(&ctx, *fnamep); } T_END; if (ret < 0) break; } doveadm_fs_delete_async_finish(&ctx); } static void cmd_fs_delete_recursive_path(struct fs *fs, const char *path, unsigned int async_count) { struct fs_file *file; size_t path_len; path_len = strlen(path); if (path_len > 0 && path[path_len-1] != '/') path = t_strconcat(path, "/", NULL); cmd_fs_delete_dir_recursive(fs, async_count, path); if ((fs_get_properties(fs) & FS_PROPERTY_DIRECTORIES) != 0) { /* delete the root itself */ file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY); if (fs_delete(file) < 0) { i_error("fs_delete(%s) failed: %s", fs_file_path(file), fs_file_last_error(file)); doveadm_exit_code = EX_TEMPFAIL; } fs_file_deinit(&file); } } static void cmd_fs_delete_recursive(int argc, char *argv[], unsigned int async_count) { struct fs *fs; unsigned int i; fs = cmd_fs_init(&argc, &argv, 0, cmd_fs_delete); for (i = 0; argv[i] != NULL; i++) cmd_fs_delete_recursive_path(fs, argv[i], async_count); fs_deinit(&fs); } static void cmd_fs_delete_paths(int argc, char *argv[], unsigned int async_count) { struct fs *fs; struct fs_delete_ctx ctx; unsigned int i; int ret; fs = cmd_fs_init(&argc, &argv, 0, cmd_fs_delete); i_zero(&ctx); ctx.fs = fs; ctx.path_prefix = ""; ctx.files_count = I_MAX(async_count, 1); ctx.files = t_new(struct fs_file *, ctx.files_count); for (i = 0; argv[i] != NULL; i++) { T_BEGIN { ret = doveadm_fs_delete_async_fname(&ctx, argv[i]); } T_END; if (ret < 0) break; } doveadm_fs_delete_async_finish(&ctx); fs_deinit(&fs); } static void cmd_fs_delete(int argc, char *argv[]) { bool recursive = FALSE; unsigned int async_count = 0; int c; while ((c = getopt(argc, argv, "Rn:")) > 0) { switch (c) { case 'R': recursive = TRUE; break; case 'n': if (str_to_uint(optarg, &async_count) < 0) i_fatal("Invalid -n parameter: %s", optarg); break; default: fs_cmd_help(cmd_fs_delete); } } argc -= optind-1; argv += optind-1; if (recursive) cmd_fs_delete_recursive(argc, argv, async_count); else cmd_fs_delete_paths(argc, argv, async_count); } static void cmd_fs_iter_full(int argc, char *argv[], enum fs_iter_flags flags, doveadm_command_t *cmd) { struct fs *fs; struct fs_iter *iter; const char *fname; fs = cmd_fs_init(&argc, &argv, 1, cmd); doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{path}\n"); doveadm_print_header_simple("path"); iter = fs_iter_init(fs, argv[0], flags); while ((fname = fs_iter_next(iter)) != NULL) { doveadm_print(fname); } if (fs_iter_deinit(&iter) < 0) { i_error("fs_iter_deinit(%s) failed: %s", argv[0], fs_last_error(fs)); doveadm_exit_code = EX_TEMPFAIL; } fs_deinit(&fs); } static void cmd_fs_iter(int argc, char *argv[]) { cmd_fs_iter_full(argc, argv, 0, cmd_fs_iter); } static void cmd_fs_iter_dirs(int argc, char *argv[]) { cmd_fs_iter_full(argc, argv, FS_ITER_FLAG_DIRS, cmd_fs_iter_dirs); } struct doveadm_cmd_ver2 doveadm_cmd_fs[] = { { .name = "fs get", .old_cmd = cmd_fs_get, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs put", .old_cmd = cmd_fs_put, .usage = "[-h ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('h', "hash", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "input-path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs copy", .old_cmd = cmd_fs_copy, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "source-path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "destination-path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs stat", .old_cmd = cmd_fs_stat, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs metadata", .old_cmd = cmd_fs_metadata, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs delete", .old_cmd = cmd_fs_delete, .usage = "[-R] [-n ] [ ...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('R', "recursive", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('n', "max-parallel", CMD_PARAM_INT64, 0) DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs iter", .old_cmd = cmd_fs_iter, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fs iter-dirs", .old_cmd = cmd_fs_iter_dirs, .usage = " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "fs-driver", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "fs-args", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "path", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END } }; static void fs_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_fs); i++) { if (doveadm_cmd_fs[i].old_cmd == cmd) help_ver2(&doveadm_cmd_fs[i]); } i_unreached(); } void doveadm_register_fs_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_fs); i++) doveadm_cmd_register_ver2(&doveadm_cmd_fs[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-stats.c0000644000175000017500000003747513165463624015504 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "ioloop.h" #include "istream.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "write-full.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include #include struct top_line { char *id; /* [headers_count] */ const char **prev_values, **cur_values; unsigned int flip:1; }; struct top_context { const char *path; int fd; struct istream *input; const char *sort_type; char **headers; unsigned int headers_count; pool_t prev_pool, cur_pool; /* id => struct top_line. */ HASH_TABLE(char *, struct top_line *) sessions; ARRAY(struct top_line *) lines; int (*lines_sort)(struct top_line *const *, struct top_line *const *); unsigned int last_update_idx, user_idx; unsigned int sort_idx1, sort_idx2; unsigned int flip:1; }; static struct top_context *sort_ctx = NULL; static const char *disk_input_field = "disk_input"; static const char *disk_output_field = "disk_output"; static char ** p_read_next_line(pool_t pool, struct istream *input) { const char *line; char **args; unsigned int i; line = i_stream_read_next_line(input); if (line == NULL) return NULL; args = p_strsplit(pool, line, "\t"); for (i = 0; args[i] != NULL; i++) args[i] = str_tabunescape(args[i]); return args; } static const char *const* read_next_line(struct istream *input) { return (const void *)p_read_next_line(pool_datastack_create(), input); } static void stats_dump(const char *path, const char *cmd) { struct istream *input; const char *const *args; unsigned int i; int fd; fd = doveadm_connect(path); net_set_nonblock(fd, FALSE); if (write_full(fd, cmd, strlen(cmd)) < 0) i_fatal("write(%s) failed: %m", path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); /* read header */ args = read_next_line(input); if (args == NULL) i_fatal("read(%s) unexpectedly disconnected", path); if (*args == NULL) i_info("no statistics available"); else { for (; *args != NULL; args++) doveadm_print_header_simple(*args); /* read lines */ do { T_BEGIN { args = read_next_line(input); if (args != NULL && args[0] == NULL) args = NULL; if (args != NULL) { for (i = 0; args[i] != NULL; i++) doveadm_print(args[i]); } } T_END; } while (args != NULL); } if (input->stream_errno != 0) i_fatal("read(%s) failed: %s", path, i_stream_get_error(input)); i_stream_destroy(&input); } static void doveadm_cmd_stats_dump(struct doveadm_cmd_context* cctx) { const char *path, *cmd; const char *args[3] = {0}; if (!doveadm_cmd_param_str(cctx, "socket-path", &path)) path = t_strconcat(doveadm_settings->base_dir, "/stats", NULL); if (!doveadm_cmd_param_str(cctx, "type", &args[0])) { i_error("Missing type parameter"); doveadm_exit_code = EX_USAGE; return; } /* purely optional */ if (!doveadm_cmd_param_str(cctx, "filter", &args[1])) args[1] = NULL; cmd = t_strdup_printf("EXPORT\t%s\n", t_strarray_join(args, "\t")); doveadm_print_init(DOVEADM_PRINT_TYPE_TAB); stats_dump(path, cmd); return; } static void stats_line_set_prev_values(struct top_context *ctx, const struct top_line *old_line, struct top_line *line) { const char **values; unsigned int i; if (old_line->prev_values == NULL || strcmp(old_line->cur_values[ctx->last_update_idx], line->cur_values[ctx->last_update_idx]) != 0) { values = old_line->cur_values; } else { values = old_line->prev_values; } /* line hasn't been updated, keep old values */ line->prev_values = p_new(ctx->cur_pool, const char *, ctx->headers_count); for (i = 0; i < ctx->headers_count; i++) line->prev_values[i] = p_strdup(ctx->cur_pool, values[i]); } static void stats_read(struct top_context *ctx) { struct top_line *old_line, *line; unsigned int i; char **args; /* read lines */ while ((args = p_read_next_line(ctx->cur_pool, ctx->input)) != NULL) { if (args[0] == NULL) { /* end of stats */ return; } if (str_array_length((void *)args) != ctx->headers_count) i_fatal("read(%s): invalid stats line", ctx->path); line = p_new(ctx->cur_pool, struct top_line, 1); line->id = args[0]; line->flip = ctx->flip; line->cur_values = p_new(ctx->cur_pool, const char *, ctx->headers_count); for (i = 0; i < ctx->headers_count; i++) line->cur_values[i] = args[i]; old_line = hash_table_lookup(ctx->sessions, line->id); if (old_line != NULL) { stats_line_set_prev_values(ctx, old_line, line); array_append(&ctx->lines, &line, 1); } hash_table_insert(ctx->sessions, line->id, line); } if (ctx->input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->path, i_stream_get_error(ctx->input)); } i_fatal("read(%s): unexpected EOF", ctx->path); } static void stats_drop_stale(struct top_context *ctx) { struct hash_iterate_context *iter; char *id; struct top_line *line; iter = hash_table_iterate_init(ctx->sessions); while (hash_table_iterate(iter, ctx->sessions, &id, &line)) { if (line->flip != ctx->flip) hash_table_remove(ctx->sessions, id); } hash_table_iterate_deinit(&iter); } static int get_double(const char *str, double *num_r) { char *p; *num_r = strtod(str, &p); return *p == '\0' ? 0 : -1; } static double sort_cpu_diff(const struct top_line *line) { double prev, cur, diff, prev_time, cur_time, time_multiplier; if (get_double(line->prev_values[sort_ctx->last_update_idx], &prev_time) < 0 || get_double(line->cur_values[sort_ctx->last_update_idx], &cur_time) < 0) i_fatal("sorting: invalid last_update value"); time_multiplier = (cur_time - prev_time) * 100; if (get_double(line->prev_values[sort_ctx->sort_idx1], &prev) < 0 || get_double(line->cur_values[sort_ctx->sort_idx1], &cur) < 0) i_fatal("sorting: not a double"); diff = cur - prev; if (sort_ctx->sort_idx2 != 0) { if (get_double(line->prev_values[sort_ctx->sort_idx2], &prev) < 0 || get_double(line->cur_values[sort_ctx->sort_idx2], &cur) < 0) i_fatal("sorting: not a double"); diff += cur - prev; } return diff * time_multiplier; } static int sort_cpu(struct top_line *const *l1, struct top_line *const *l2) { double d1, d2; d1 = sort_cpu_diff(*l1); d2 = sort_cpu_diff(*l2); if (d1 < d2) return -1; if (d1 > d2) return 1; return strcmp((*l1)->cur_values[sort_ctx->user_idx], (*l2)->cur_values[sort_ctx->user_idx]); } static double sort_num_diff(const struct top_line *line) { uint64_t prev, cur, diff; if (str_to_uint64(line->prev_values[sort_ctx->sort_idx1], &prev) < 0 || str_to_uint64(line->cur_values[sort_ctx->sort_idx1], &cur) < 0) i_fatal("sorting: not a number"); diff = cur - prev; if (sort_ctx->sort_idx2 != 0) { if (str_to_uint64(line->prev_values[sort_ctx->sort_idx2], &prev) < 0 || str_to_uint64(line->cur_values[sort_ctx->sort_idx2], &cur) < 0) i_fatal("sorting: not a number"); diff += cur - prev; } return diff; } static int sort_num(struct top_line *const *l1, struct top_line *const *l2) { uint64_t n1, n2; n1 = sort_num_diff(*l1); n2 = sort_num_diff(*l2); if (n1 < n2) return -1; if (n1 > n2) return 1; return strcmp((*l1)->cur_values[sort_ctx->user_idx], (*l2)->cur_values[sort_ctx->user_idx]); } static bool stats_header_find(struct top_context *ctx, const char *name, unsigned int *idx_r) { unsigned int i; for (i = 0; ctx->headers[i] != NULL; i++) { if (strcmp(ctx->headers[i], name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } static void stats_top_get_sorting(struct top_context *ctx) { if (stats_header_find(ctx, ctx->sort_type, &ctx->sort_idx1)) return; if (strcmp(ctx->sort_type, "cpu") == 0) { if (!stats_header_find(ctx, "user_cpu", &ctx->sort_idx1) || !stats_header_find(ctx, "sys_cpu", &ctx->sort_idx2)) i_fatal("cpu sort type is missing fields"); return; } if (strcmp(ctx->sort_type, "disk") == 0) { if (!stats_header_find(ctx, disk_input_field, &ctx->sort_idx1) || !stats_header_find(ctx, disk_output_field, &ctx->sort_idx2)) i_fatal("disk sort type is missing fields"); return; } i_fatal("unknown sort type: %s", ctx->sort_type); } static bool stats_top_round(struct top_context *ctx) { #define TOP_CMD "EXPORT\tsession\tconnected\n" const char *const *args; pool_t tmp_pool; if (write_full(ctx->fd, TOP_CMD, strlen(TOP_CMD)) < 0) i_fatal("write(%s) failed: %m", ctx->path); /* read header */ if (ctx->headers != NULL) { args = read_next_line(ctx->input); if (args == NULL) i_fatal("read(%s) unexpectedly disconnected", ctx->path); if (*args == NULL) return TRUE; if (str_array_length(args) != ctx->headers_count) i_fatal("headers changed"); } else { ctx->headers = p_read_next_line(default_pool, ctx->input); if (ctx->headers == NULL) i_fatal("read(%s) unexpectedly disconnected", ctx->path); if (*ctx->headers == NULL) { i_free_and_null(ctx->headers); return FALSE; } ctx->headers_count = str_array_length((void *)ctx->headers); if (!stats_header_find(ctx, "last_update", &ctx->last_update_idx)) i_fatal("last_update header missing"); if (!stats_header_find(ctx, "user", &ctx->user_idx)) i_fatal("user header missing"); stats_top_get_sorting(ctx); } array_clear(&ctx->lines); p_clear(ctx->prev_pool); tmp_pool = ctx->prev_pool; ctx->prev_pool = ctx->cur_pool; ctx->cur_pool = tmp_pool; ctx->flip = !ctx->flip; stats_read(ctx); stats_drop_stale(ctx); sort_ctx = ctx; array_sort(&ctx->lines, *ctx->lines_sort); sort_ctx = NULL; return TRUE; } static void stats_top_output_diff(struct top_context *ctx, const struct top_line *line, unsigned int i) { uint64_t prev_num, cur_num; double prev_double, cur_double, prev_time, cur_time; char numstr[MAX_INT_STRLEN]; if (str_to_uint64(line->prev_values[i], &prev_num) == 0 && str_to_uint64(line->cur_values[i], &cur_num) == 0) { if (i_snprintf(numstr, sizeof(numstr), "%llu", (unsigned long long)(cur_num - prev_num)) < 0) i_unreached(); doveadm_print(numstr); } else if (get_double(line->prev_values[i], &prev_double) == 0 && get_double(line->cur_values[i], &cur_double) == 0 && get_double(line->prev_values[ctx->last_update_idx], &prev_time) == 0 && get_double(line->cur_values[ctx->last_update_idx], &cur_time) == 0) { /* %CPU */ if (i_snprintf(numstr, sizeof(numstr), "%d", (int)((cur_double - prev_double) * (cur_time - prev_time) * 100)) < 0) i_unreached(); doveadm_print(numstr); } else { doveadm_print(line->cur_values[i]); } } static void stats_top_output(struct top_context *ctx) { static const char *names[] = { "user", "service", "user_cpu", "sys_cpu", "", "" }; struct winsize ws; struct top_line *const *lines; unsigned int i, j, row, maxrow, count, indexes[N_ELEMENTS(names)]; names[4] = disk_input_field; names[5] = disk_output_field; /* ANSI clear screen and move cursor to top of screen */ printf("\x1b[2J\x1b[1;1H"); fflush(stdout); doveadm_print_deinit(); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("USER", "USER", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("SERVICE"); doveadm_print_header_simple("%CPU"); doveadm_print_header_simple("%SYS"); doveadm_print_header_simple("DISKIN"); doveadm_print_header_simple("DISKOUT"); if (!stats_top_round(ctx)) { /* no connections yet */ return; } for (i = 0; i < N_ELEMENTS(names); i++) { if (!stats_header_find(ctx, names[i], &indexes[i])) indexes[i] = UINT_MAX; } if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) ws.ws_row = 24; maxrow = ws.ws_row-1; lines = array_get(&ctx->lines, &count); for (i = 0, row = 1; row < maxrow && i < count; i++, row++) { for (j = 0; j < N_ELEMENTS(names); j++) { if (indexes[j] == UINT_MAX) doveadm_print("?"); else stats_top_output_diff(ctx, lines[i], indexes[j]); } } } static void stats_top_start(struct top_context *ctx) { struct timeout *to; stats_top_output(ctx); to = timeout_add(1000, stats_top_output, ctx); io_loop_run(current_ioloop); timeout_remove(&to); } static void stats_top(const char *path, const char *sort_type) { struct top_context ctx; i_zero(&ctx); ctx.path = path; ctx.fd = doveadm_connect(path); ctx.prev_pool = pool_alloconly_create("stats top", 1024*16); ctx.cur_pool = pool_alloconly_create("stats top", 1024*16); i_array_init(&ctx.lines, 128); hash_table_create(&ctx.sessions, default_pool, 0, str_hash, strcmp); net_set_nonblock(ctx.fd, FALSE); ctx.input = i_stream_create_fd(ctx.fd, (size_t)-1, FALSE); if (strstr(sort_type, "cpu") != NULL) ctx.lines_sort = sort_cpu; else ctx.lines_sort = sort_num; ctx.sort_type = sort_type; stats_top_start(&ctx); i_stream_destroy(&ctx.input); hash_table_destroy(&ctx.sessions); array_free(&ctx.lines); pool_unref(&ctx.prev_pool); pool_unref(&ctx.cur_pool); i_close_fd(&ctx.fd); } static void stats_reset(const char *path, const char **items ATTR_UNUSED) { const char **ptr ATTR_UNUSED; int fd,ret; string_t *cmd; struct istream *input; const char *line; fd = doveadm_connect(path); net_set_nonblock(fd, FALSE); input = i_stream_create_fd(fd, (size_t)-1, FALSE); cmd = t_str_new(10); str_append(cmd, "RESET"); /* XXX: Not supported yet. for(ptr = items; *ptr; ptr++) { str_append_c(cmd, '\t'); str_append(cmd, *ptr); } */ str_append_c(cmd, '\n'); /* send command */ ret = write_full(fd, str_c(cmd), str_len(cmd)); if (ret < 0) { i_close_fd(&fd); i_error("write(%s) failed: %m", path); return; } line = i_stream_read_next_line(input); if (line == NULL) { i_error("read(%s) failed: %s", path, i_stream_get_error(input)); } else if (strncmp(line, "OK", 2) != 0) { i_error("%s",line); } else { i_info("Stats reset"); } i_stream_destroy(&input); i_close_fd(&fd); } static void cmd_stats_top(int argc, char *argv[]) { const char *path, *sort_type; int c; path = t_strconcat(doveadm_settings->base_dir, "/stats", NULL); while ((c = getopt(argc, argv, "bs:")) > 0) { switch (c) { case 'b': disk_input_field = "read_bytes"; disk_output_field = "write_bytes"; break; case 's': path = optarg; break; default: help_ver2(&doveadm_cmd_stats_top_ver2); } } argv += optind - 1; if (argv[1] == NULL) sort_type = "disk"; else if (argv[2] != NULL) help_ver2(&doveadm_cmd_stats_top_ver2); else sort_type = argv[1]; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); stats_top(path, sort_type); } static void cmd_stats_reset(int argc, char *argv[]) { const char *path; int c; path = t_strconcat(doveadm_settings->base_dir, "/stats", NULL); while((c = getopt(argc, argv, "s:")) > 0) { switch (c) { case 's': path = optarg; break; default: help_ver2(&doveadm_cmd_stats_reset_ver2); } } argv += optind - 1; /* items is now argv */ /* if (optind >= argc) { i_fatal("missing item(s) to reset"); } */ stats_reset(path, (const char**)argv); } struct doveadm_cmd_ver2 doveadm_cmd_stats_dump_ver2 = { .cmd = doveadm_cmd_stats_dump, .name = "stats dump", .usage = "[-s ] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('s', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "type", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "filter", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_stats_top_ver2 = { .old_cmd = cmd_stats_top, .name = "stats top", .usage = "[-s ] [-b] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('s', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('b', "show-disk-io", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "field", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_stats_reset_ver2 = { .old_cmd = cmd_stats_reset, .name = "stats reset", .usage = "[-s ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('s', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-instance.c0000644000175000017500000000740013123174404016120 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "master-instance.h" #include "master-service-settings.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include #include extern struct doveadm_cmd doveadm_cmd_instance[]; static void instance_cmd_help(doveadm_command_t *cmd) ATTR_NORETURN; static bool pid_file_read(const char *path) { char buf[32]; int fd; ssize_t ret; pid_t pid; bool found = FALSE; fd = open(path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", path); return FALSE; } ret = read(fd, buf, sizeof(buf)); if (ret < 0) i_error("read(%s) failed: %m", path); else if (ret > 0 && buf[ret-1] == '\n') { buf[ret-1] = '\0'; if (str_to_pid(buf, &pid) == 0) { found = !(pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)); } } i_close_fd(&fd); return found; } static void cmd_instance_list(int argc, char *argv[]) { struct master_instance_list *list; struct master_instance_list_iter *iter; const struct master_instance *inst; const char *instance_path, *pidfile_path; bool show_config = FALSE; int c; while ((c = getopt(argc, argv, "c")) > 0) { switch (c) { case 'c': show_config = TRUE; break; default: help(&doveadm_cmd_instance[0]); } } argv += optind; if (!show_config) { doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("path", "path", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("name"); doveadm_print_header_simple("last used"); doveadm_print_header_simple("running"); } instance_path = t_strconcat(service_set->state_dir, "/"MASTER_INSTANCE_FNAME, NULL); list = master_instance_list_init(instance_path); iter = master_instance_list_iterate_init(list); while ((inst = master_instance_iterate_list_next(iter)) != NULL) { if (argv[0] != NULL && strcmp(argv[0], inst->name) != 0) continue; if (show_config) { printf("%s\n", inst->config_path == NULL ? "" : inst->config_path); continue; } doveadm_print(inst->base_dir); doveadm_print(inst->name); doveadm_print(unixdate2str(inst->last_used)); pidfile_path = t_strconcat(inst->base_dir, "/master.pid", NULL); if (pid_file_read(pidfile_path)) doveadm_print("yes"); else doveadm_print("no"); } master_instance_iterate_list_deinit(&iter); master_instance_list_deinit(&list); } static void cmd_instance_remove(int argc, char *argv[]) { struct master_instance_list *list; const struct master_instance *inst; const char *base_dir, *instance_path; int ret; if (argc != 2) instance_cmd_help(cmd_instance_remove); instance_path = t_strconcat(service_set->state_dir, "/"MASTER_INSTANCE_FNAME, NULL); list = master_instance_list_init(instance_path); inst = master_instance_list_find_by_name(list, argv[1]); base_dir = inst != NULL ? inst->base_dir : argv[1]; if ((ret = master_instance_list_remove(list, base_dir)) < 0) { i_error("Failed to remove instance"); doveadm_exit_code = EX_TEMPFAIL; } else if (ret == 0) { i_error("Instance already didn't exist"); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } master_instance_list_deinit(&list); } struct doveadm_cmd doveadm_cmd_instance[] = { { cmd_instance_list, "instance list", "[-c] []" }, { cmd_instance_remove, "instance remove", " | " } }; static void instance_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_instance); i++) { if (doveadm_cmd_instance[i].cmd == cmd) help(&doveadm_cmd_instance[i]); } i_unreached(); } void doveadm_register_instance_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_instance); i++) doveadm_register_cmd(&doveadm_cmd_instance[i]); } dovecot-2.2.33.2/src/doveadm/client-connection-private.h0000644000175000017500000000071013165463624017777 00000000000000#ifndef CLIENT_CONNECTION_PRIVATE_H #define CLIENT_CONNECTION_PRIVATE_H #include "client-connection.h" bool doveadm_client_is_allowed_command(const struct doveadm_settings *set, const char *cmd_name); int client_connection_init(struct client_connection *conn, int fd); void client_connection_set_proctitle(struct client_connection *conn, const char *text); void doveadm_http_server_init(void); void doveadm_http_server_deinit(void); #endif dovecot-2.2.33.2/src/doveadm/doveadm-cmd.c0000644000175000017500000003500213165463624015071 00000000000000/* Copyright (c) 2009-2r016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "str.h" #include "net.h" #include "doveadm-cmd.h" #include "doveadm.h" #include #include #include static struct doveadm_cmd *doveadm_commands[] = { &doveadm_cmd_mailbox_mutf7, &doveadm_cmd_sis_deduplicate, &doveadm_cmd_sis_find, }; static struct doveadm_cmd_ver2 *doveadm_commands_ver2[] = { &doveadm_cmd_service_stop_ver2, &doveadm_cmd_service_status_ver2, &doveadm_cmd_process_status_ver2, &doveadm_cmd_stop_ver2, &doveadm_cmd_reload_ver2, &doveadm_cmd_stats_dump_ver2, &doveadm_cmd_stats_reset_ver2, &doveadm_cmd_penalty_ver2, &doveadm_cmd_kick_ver2, &doveadm_cmd_who_ver2 }; static const struct exit_code_str { int code; const char *str; } exit_code_strings[] = { { DOVEADM_EX_UNKNOWN, "UNKNOWN" }, { EX_TEMPFAIL, "TEMPFAIL" }, { EX_USAGE, "USAGE" }, { EX_NOUSER, "NOUSER" }, { EX_NOPERM, "NOPERM" }, { EX_PROTOCOL, "PROTOCOL" }, { EX_DATAERR, "DATAERR" }, { DOVEADM_EX_NOTFOUND, "NOTFOUND" } }; ARRAY_TYPE(doveadm_cmd) doveadm_cmds; ARRAY_TYPE(doveadm_cmd_ver2) doveadm_cmds_ver2; ARRAY_DEFINE_TYPE(getopt_option_array, struct option); const char *doveadm_exit_code_to_str(int code) { for(size_t i = 0; i < N_ELEMENTS(exit_code_strings); i++) { const struct exit_code_str *ptr = &exit_code_strings[i]; if (ptr->code == code) return ptr->str; } return "UNKNOWN"; } int doveadm_str_to_exit_code(const char *reason) { for(size_t i = 0; i < N_ELEMENTS(exit_code_strings); i++) { const struct exit_code_str *ptr = &exit_code_strings[i]; if (strcmp(ptr->str, reason) == 0) return ptr->code; } return DOVEADM_EX_UNKNOWN; } void doveadm_register_cmd(const struct doveadm_cmd *cmd) { array_append(&doveadm_cmds, cmd, 1); } void doveadm_cmd_register_ver2(struct doveadm_cmd_ver2 *cmd) { if (cmd->cmd == NULL) { if (cmd->mail_cmd != NULL) cmd->cmd = doveadm_cmd_ver2_to_mail_cmd_wrapper; else if (cmd->old_cmd != NULL) cmd->cmd = doveadm_cmd_ver2_to_cmd_wrapper; else i_unreached(); } array_append(&doveadm_cmds_ver2, cmd, 1); } const struct doveadm_cmd_ver2 *doveadm_cmd_find_ver2(const char *cmd_name) { const struct doveadm_cmd_ver2 *cmd; array_foreach(&doveadm_cmds_ver2, cmd) { if (strcmp(cmd_name, cmd->name)==0) return cmd; } return NULL; } const struct doveadm_cmd_ver2 * doveadm_cmd_find_with_args_ver2(const char *cmd_name, int *argc, const char *const *argv[]) { int i, k; const struct doveadm_cmd_ver2 *cmd; const char *cptr; for(i=0;i<*argc;i++) { if (strcmp((*argv)[i],cmd_name)==0) break; } i_assert(i != *argc); array_foreach(&doveadm_cmds_ver2, cmd) { cptr = cmd->name; /* cannot reuse i here because this needs be done more than once */ for (k=0; *cptr != '\0' && i+k < *argc; k++) { size_t alen = strlen((*argv)[i+k]); /* make sure we don't overstep */ if (strlen(cptr) < alen) break; /* did not match */ if (strncmp(cptr, (*argv)[i+k], alen) != 0) break; /* do not accept abbreviations */ if (cptr[alen] != ' ' && cptr[alen] != '\0') break; cptr += alen; if (*cptr != '\0') cptr++; /* consume space */ } /* name was fully consumed */ if (*cptr == '\0') { if (k > 1) { *argc -= k-1; *argv += k-1; } return cmd; } } return NULL; } static bool doveadm_cmd_find_multi_word(const char *cmdname, int *_argc, const char *const *_argv[]) { int argc = *_argc; const char *const *argv = *_argv; size_t len; if (argc < 2) return NULL; len = strlen(argv[1]); if (strncmp(cmdname, argv[1], len) != 0) return NULL; argc--; argv++; if (cmdname[len] == ' ') { /* more args */ if (!doveadm_cmd_find_multi_word(cmdname + len + 1, &argc, &argv)) return FALSE; } else { if (cmdname[len] != '\0') return FALSE; } *_argc = argc; *_argv = argv; return TRUE; } const struct doveadm_cmd * doveadm_cmd_find_with_args(const char *cmd_name, int *argc, const char *const *argv[]) { const struct doveadm_cmd *cmd; size_t cmd_name_len; i_assert(*argc > 0); cmd_name_len = strlen(cmd_name); array_foreach(&doveadm_cmds, cmd) { if (strcmp(cmd->name, cmd_name) == 0) return cmd; /* see if it matches a multi-word command */ if (strncmp(cmd->name, cmd_name, cmd_name_len) == 0 && cmd->name[cmd_name_len] == ' ') { const char *subcmd_name = cmd->name + cmd_name_len + 1; if (doveadm_cmd_find_multi_word(subcmd_name, argc, argv)) return cmd; } } return NULL; } void doveadm_cmds_init(void) { unsigned int i; i_array_init(&doveadm_cmds, 32); i_array_init(&doveadm_cmds_ver2, 2); for (i = 0; i < N_ELEMENTS(doveadm_commands); i++) doveadm_register_cmd(doveadm_commands[i]); for (i = 0; i < N_ELEMENTS(doveadm_commands_ver2); i++) doveadm_cmd_register_ver2(doveadm_commands_ver2[i]); doveadm_register_director_commands(); doveadm_register_instance_commands(); doveadm_register_mount_commands(); doveadm_register_proxy_commands(); doveadm_register_log_commands(); doveadm_register_replicator_commands(); doveadm_register_dict_commands(); doveadm_register_fs_commands(); } void doveadm_cmds_deinit(void) { array_free(&doveadm_cmds); array_free(&doveadm_cmds_ver2); } static const struct doveadm_cmd_param* doveadm_cmd_param_get(const struct doveadm_cmd_context *cctx, const char *name) { i_assert(cctx != NULL); i_assert(cctx->argv != NULL); for(int i = 0; i < cctx->argc; i++) { if (strcmp(cctx->argv[i].name, name) == 0 && cctx->argv[i].value_set) return &(cctx->argv[i]); } return NULL; } bool doveadm_cmd_param_bool(const struct doveadm_cmd_context *cctx, const char *name, bool *value_r) { const struct doveadm_cmd_param *param; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_BOOL) { *value_r = param->value.v_bool; return TRUE; } return FALSE; } bool doveadm_cmd_param_int64(const struct doveadm_cmd_context *cctx, const char *name, int64_t *value_r) { const struct doveadm_cmd_param *param; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_INT64) { *value_r = param->value.v_int64; return TRUE; } return FALSE; } bool doveadm_cmd_param_str(const struct doveadm_cmd_context *cctx, const char *name, const char **value_r) { const struct doveadm_cmd_param *param; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_STR) { *value_r = param->value.v_string; return TRUE; } return FALSE; } bool doveadm_cmd_param_ip(const struct doveadm_cmd_context *cctx, const char *name, struct ip_addr *value_r) { const struct doveadm_cmd_param *param; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_IP) { memcpy(value_r, ¶m->value.v_ip, sizeof(struct ip_addr)); return TRUE; } return FALSE; } bool doveadm_cmd_param_array(const struct doveadm_cmd_context *cctx, const char *name, const char *const **value_r) { const struct doveadm_cmd_param *param; unsigned int count; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_ARRAY) { *value_r = array_get(¶m->value.v_array, &count); /* doveadm_cmd_params_null_terminate_arrays() should have been called, which guarantees that we're NULL-terminated */ i_assert((*value_r)[count] == NULL); return TRUE; } return FALSE; } bool doveadm_cmd_param_istream(const struct doveadm_cmd_context *cctx, const char *name, struct istream **value_r) { const struct doveadm_cmd_param *param; if ((param = doveadm_cmd_param_get(cctx, name))==NULL) return FALSE; if (param->type == CMD_PARAM_ISTREAM) { *value_r = param->value.v_istream; return TRUE; } return FALSE; } void doveadm_cmd_params_clean(ARRAY_TYPE(doveadm_cmd_param_arr_t) *pargv) { struct doveadm_cmd_param *param; array_foreach_modifiable(pargv, param) { if (param->type == CMD_PARAM_ISTREAM && param->value.v_istream != NULL) i_stream_destroy(&(param->value.v_istream)); } array_clear(pargv); } void doveadm_cmd_params_null_terminate_arrays(ARRAY_TYPE(doveadm_cmd_param_arr_t) *pargv) { struct doveadm_cmd_param *param; array_foreach_modifiable(pargv, param) { if (param->type == CMD_PARAM_ARRAY && array_is_created(¶m->value.v_array)) { array_append_zero(¶m->value.v_array); array_delete(¶m->value.v_array, array_count(¶m->value.v_array)-1, 1); } } } static void doveadm_cmd_params_to_argv(const char *name, int pargc, const struct doveadm_cmd_param* params, ARRAY_TYPE(const_string) *argv) { bool array_add_opt; int i; const char * const * cptr; i_assert(array_count(argv) == 0); array_append(argv, &name, 1); ARRAY_TYPE(const_string) pargv; t_array_init(&pargv, 8); for(i=0;i 0) { const char *dashdash = "--"; array_append(argv, &dashdash, 1); array_append_array(argv, &pargv); } array_append_zero(argv); } void doveadm_cmd_ver2_to_cmd_wrapper(struct doveadm_cmd_context *cctx) { unsigned int pargc; const char **pargv; i_assert(cctx->cmd->old_cmd != NULL); ARRAY_TYPE(const_string) nargv; t_array_init(&nargv, 8); doveadm_cmd_params_to_argv(cctx->cmd->name, cctx->argc, cctx->argv, &nargv); pargv = array_get_modifiable(&nargv, &pargc); i_getopt_reset(); cctx->cmd->old_cmd(pargc-1, (char**)pargv); } static void doveadm_build_options(const struct doveadm_cmd_param par[], string_t *shortopts, ARRAY_TYPE(getopt_option_array) *longopts) { for(size_t i=0; par[i].name != NULL; i++) { struct option longopt; i_zero(&longopt); longopt.name = par[i].name; if (par[i].short_opt != '\0') { longopt.val = par[i].short_opt; str_append_c(shortopts, par[i].short_opt); if (par[i].type != CMD_PARAM_BOOL) str_append_c(shortopts, ':'); } if (par[i].type != CMD_PARAM_BOOL) longopt.has_arg = 1; array_append(longopts, &longopt, 1); } array_append_zero(longopts); } static void doveadm_fill_param(struct doveadm_cmd_param *param, const char *value, pool_t pool) { param->value_set = TRUE; switch(param->type) { case CMD_PARAM_BOOL: param->value.v_bool = TRUE; break; case CMD_PARAM_INT64: if (str_to_int64(value, ¶m->value.v_int64) != 0) { param->value_set = FALSE; } break; case CMD_PARAM_IP: if (net_addr2ip(value, ¶m->value.v_ip) != 0) { param->value_set = FALSE; } break; case CMD_PARAM_STR: param->value.v_string = p_strdup(pool, value); break; case CMD_PARAM_ARRAY: if (!array_is_created(¶m->value.v_array)) p_array_init(¶m->value.v_array, pool, 8); const char *val = p_strdup(pool, value); array_append(¶m->value.v_array, &val, 1); break; case CMD_PARAM_ISTREAM: { struct istream *is; if (strcmp(value,"-") == 0) { is = i_stream_create_fd(STDIN_FILENO, IO_BLOCK_SIZE, FALSE); } else { is = i_stream_create_file(value, IO_BLOCK_SIZE); } param->value.v_istream = is; } } } bool doveadm_cmd_try_run_ver2(const char *cmd_name, int argc, const char *const argv[], struct doveadm_cmd_context *cctx) { const struct doveadm_cmd_ver2 *cmd; cmd = doveadm_cmd_find_with_args_ver2(cmd_name, &argc, &argv); if (cmd == NULL) return FALSE; cctx->cmd = cmd; if (doveadm_cmd_run_ver2(argc, argv, cctx) < 0) doveadm_exit_code = EX_USAGE; return TRUE; } int doveadm_cmd_run_ver2(int argc, const char *const argv[], struct doveadm_cmd_context *cctx) { struct doveadm_cmd_param *param; ARRAY_TYPE(doveadm_cmd_param_arr_t) pargv; ARRAY_TYPE(getopt_option_array) opts; unsigned int pargc; int c,li; pool_t pool = pool_datastack_create(); string_t *optbuf = str_new(pool, 64); p_array_init(&opts, pool, 4); // build parameters doveadm_build_options(cctx->cmd->parameters, optbuf, &opts); p_array_init(&pargv, pool, 20); for(pargc=0;cctx->cmd->parameters[pargc].name != NULL;pargc++) { param = array_append_space(&pargv); memcpy(param, &(cctx->cmd->parameters[pargc]), sizeof(struct doveadm_cmd_param)); param->value_set = FALSE; } i_assert(pargc == array_count(&opts)-1); /* opts is NULL-terminated */ while((c = getopt_long(argc, (char*const*)argv, str_c(optbuf), array_idx(&opts, 0), &li)) > -1) { switch(c) { case 0: for(unsigned int i = 0; i < array_count(&pargv); i++) { const struct option *opt = array_idx(&opts,li); param = array_idx_modifiable(&pargv,i); if (opt->name == param->name) doveadm_fill_param(param, optarg, pool); } break; case '?': case ':': doveadm_cmd_params_clean(&pargv); return -1; default: // hunt the option for(unsigned int i = 0; i < pargc; i++) { const struct option *longopt = array_idx(&opts,i); if (longopt->val == c) doveadm_fill_param(array_idx_modifiable(&pargv,i), optarg, pool); } } } /* process positional arguments */ for(;optindflags & CMD_PARAM_FLAG_POSITIONAL) != 0 && (ptr->value_set == FALSE || ptr->type == CMD_PARAM_ARRAY)) { doveadm_fill_param(ptr, argv[optind], pool); found = TRUE; break; } } if (!found) { i_error("Extraneous arguments found"); doveadm_cmd_params_clean(&pargv); return -1; } } doveadm_cmd_params_null_terminate_arrays(&pargv); cctx->argv = array_get_modifiable(&pargv, &pargc); cctx->argc = pargc; cctx->cmd->cmd(cctx); doveadm_cmd_params_clean(&pargv); return 0; } dovecot-2.2.33.2/src/doveadm/doveadm-director.c0000644000175000017500000007156413165463624016156 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "md5.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "net.h" #include "istream.h" #include "write-full.h" #include "master-service.h" #include "auth-master.h" #include "mail-user-hash.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include struct director_context { const char *socket_path; const char *users_path; const char *tag; const char *user; const char *host; const char *ip; const char *port; const char *vhost_count; const char *passdb_field; struct istream *users_input; struct istream *input; bool explicit_socket_path; bool hash_map, user_map, force_flush; int64_t max_parallel; }; struct user_list { struct user_list *next; const char *name; }; HASH_TABLE_DEFINE_TYPE(user_list, void *, struct user_list *); static void director_cmd_help(const struct doveadm_cmd_ver2 *); static int director_get_host(const char *host, struct ip_addr **ips_r, unsigned int *ips_count_r) ATTR_WARN_UNUSED_RESULT; static void director_send(struct director_context *ctx, const char *data) { if (write_full(i_stream_get_fd(ctx->input), data, strlen(data)) < 0) i_fatal("write(%s) failed: %m", ctx->socket_path); } static void director_connect(struct director_context *ctx) { #define DIRECTOR_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" const char *line; int fd; fd = doveadm_connect(ctx->socket_path); net_set_nonblock(fd, FALSE); ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); director_send(ctx, DIRECTOR_HANDSHAKE); alarm(5); line = i_stream_read_next_line(ctx->input); alarm(0); if (line == NULL) { if (ctx->input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->socket_path, i_stream_get_error(ctx->input)); } else if (ctx->input->eof) { i_fatal("%s disconnected", ctx->socket_path); } else { i_fatal("read(%s) timed out (is director configured?)", ctx->socket_path); } } if (!version_string_verify(line, "director-doveadm", 1)) { i_fatal_status(EX_PROTOCOL, "%s not a compatible director-doveadm socket", ctx->socket_path); } } static void director_disconnect(struct director_context *ctx) { if (ctx->input != NULL) { if (ctx->input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->socket_path, i_stream_get_error(ctx->input)); } i_stream_destroy(&ctx->input); } } static struct director_context * cmd_director_init(struct doveadm_cmd_context *cctx) { struct director_context *ctx; ctx = t_new(struct director_context, 1); if (!doveadm_cmd_param_str(cctx, "socket-path", &(ctx->socket_path))) ctx->socket_path = t_strconcat(doveadm_settings->base_dir, "/director-admin", NULL); else ctx->explicit_socket_path = TRUE; if (!doveadm_cmd_param_bool(cctx, "user-map", &(ctx->user_map))) ctx->user_map = FALSE; if (!doveadm_cmd_param_bool(cctx, "hash-map", &(ctx->hash_map))) ctx->hash_map = FALSE; if (!doveadm_cmd_param_bool(cctx, "force-flush", &(ctx->force_flush))) ctx->force_flush = FALSE; if (!doveadm_cmd_param_istream(cctx, "users-file", &(ctx->users_input))) ctx->users_input = NULL; if (!doveadm_cmd_param_str(cctx, "tag", &(ctx->tag))) ctx->tag = NULL; if (!doveadm_cmd_param_str(cctx, "user", &(ctx->user))) ctx->user = NULL; if (!doveadm_cmd_param_str(cctx, "host", &(ctx->host))) ctx->host = NULL; if (!doveadm_cmd_param_str(cctx, "ip", &(ctx->ip))) ctx->ip = NULL; if (!doveadm_cmd_param_str(cctx, "port", &(ctx->port))) ctx->port = NULL; if (!doveadm_cmd_param_str(cctx, "vhost-count", &(ctx->vhost_count))) ctx->vhost_count = NULL; if (!doveadm_cmd_param_str(cctx, "passdb-field", &(ctx->passdb_field))) ctx->passdb_field = NULL; if (!doveadm_cmd_param_int64(cctx, "max-parallel", &(ctx->max_parallel))) ctx->max_parallel = 0; if (!ctx->user_map) director_connect(ctx); return ctx; } static void director_disconnected(struct director_context *ctx) { i_assert(ctx->input->eof); if (ctx->input->stream_errno != 0) { i_error("read(%s) failed: %s", ctx->socket_path, i_stream_get_error(ctx->input)); } else { i_error("%s unexpectedly disconnected", ctx->socket_path); } doveadm_exit_code = EX_TEMPFAIL; } static void cmd_director_status_user(struct director_context *ctx) { const char *line, *const *args; unsigned int expires; director_send(ctx, t_strdup_printf("USER-LOOKUP\t%s\t%s\n", ctx->user, ctx->tag != NULL ? ctx->tag : "")); line = i_stream_read_next_line(ctx->input); if (line == NULL) { director_disconnected(ctx); return; } args = t_strsplit_tabescaped(line); if (str_array_length(args) != 4 || str_to_uint(args[1], &expires) < 0) { i_error("Invalid reply from director"); doveadm_exit_code = EX_PROTOCOL; return; } doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_header_simple("status"); doveadm_print_header_simple("expires"); doveadm_print_header_simple("hashed"); doveadm_print_header_simple("initial-config"); doveadm_print_formatted_set_format("Current: %{status} (expires %{expires})\n" \ "Hashed: %{hashed}\n" \ "Initial config: %{initial-config}\n"); if (args[0][0] != '\0') { doveadm_print(args[0]); doveadm_print(unixdate2str(expires)); } else { doveadm_print("n/a"); doveadm_print("-1"); } doveadm_print(args[2]); doveadm_print(args[3]); director_disconnect(ctx); } static void cmd_director_status(struct doveadm_cmd_context *cctx) { struct director_context *ctx; const char *line, *const *args; ctx = cmd_director_init(cctx); if (ctx->user != NULL) { cmd_director_status_user(ctx); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("mail server ip"); doveadm_print_header_simple("tag"); doveadm_print_header_simple("vhosts"); doveadm_print_header_simple("state"); doveadm_print_header("state-changed", "state changed", 0); doveadm_print_header_simple("users"); director_send(ctx, "HOST-LIST\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { unsigned int arg_count; time_t ts; args = t_strsplit_tabescaped(line); arg_count = str_array_length(args); if (arg_count >= 6) { /* ip vhosts users tag updown updown-ts */ doveadm_print(args[0]); doveadm_print(args[3]); doveadm_print(args[1]); doveadm_print(args[4][0] == 'D' ? "down" : "up"); if (str_to_time(args[5], &ts) < 0 || ts <= 0) doveadm_print("-"); else doveadm_print(unixdate2str(ts)); doveadm_print(args[2]); } } T_END; } if (line == NULL) director_disconnected(ctx); director_disconnect(ctx); } static void user_list_add(const char *username, pool_t pool, HASH_TABLE_TYPE(user_list) users) { struct user_list *user, *old_user; unsigned int user_hash; user = p_new(pool, struct user_list, 1); user->name = p_strdup(pool, username); user_hash = mail_user_hash(username, doveadm_settings->director_username_hash); old_user = hash_table_lookup(users, POINTER_CAST(user_hash)); if (old_user != NULL) user->next = old_user; hash_table_insert(users, POINTER_CAST(user_hash), user); } static void ATTR_NULL(1) userdb_get_user_list(const char *auth_socket_path, pool_t pool, HASH_TABLE_TYPE(user_list) users) { struct auth_master_user_list_ctx *ctx; struct auth_master_connection *conn; const char *username; if (auth_socket_path == NULL) { auth_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-userdb", NULL); } conn = auth_master_init(auth_socket_path, 0); ctx = auth_master_user_list_init(conn, "", NULL); while ((username = auth_master_user_list_next(ctx)) != NULL) user_list_add(username, pool, users); if (auth_master_user_list_deinit(&ctx) < 0) { i_error("user listing failed"); doveadm_exit_code = EX_TEMPFAIL; } auth_master_deinit(&conn); } static void user_file_get_user_list(struct istream *input, pool_t pool, HASH_TABLE_TYPE(user_list) users) { const char *username; while ((username = i_stream_read_next_line(input)) != NULL) user_list_add(username, pool, users); } static int director_get_host(const char *host, struct ip_addr **ips_r, unsigned int *ips_count_r) { struct ip_addr ip; int ret = 0; if (net_addr2ip(host, &ip) == 0) { *ips_r = t_new(struct ip_addr, 1); **ips_r = ip; *ips_count_r = 1; } else { ret = net_gethostbyname(host, ips_r, ips_count_r); if (ret != 0) { i_error("gethostname(%s) failed: %s", host, net_gethosterror(ret)); doveadm_exit_code = EX_TEMPFAIL; return ret; } } return ret; } static bool ip_find(const struct ip_addr *ips, unsigned int ips_count, const struct ip_addr *match_ip) { unsigned int i; for (i = 0; i < ips_count; i++) { if (net_ip_compare(&ips[i], match_ip)) return TRUE; } return FALSE; } static void cmd_director_map(struct doveadm_cmd_context *cctx) { struct director_context *ctx; const char *line, *const *args; struct ip_addr *ips, user_ip; pool_t pool; HASH_TABLE_TYPE(user_list) users; struct user_list *user; unsigned int ips_count, user_hash, expires; ctx = cmd_director_init(cctx); if ((ctx->hash_map || ctx->user_map) && ctx->host == NULL) { director_cmd_help(cctx->cmd); return; } if (ctx->user_map) { /* user -> hash mapping */ user_hash = mail_user_hash(ctx->host, doveadm_settings->director_username_hash); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("hash", "hash", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); doveadm_print(t_strdup_printf("%u", user_hash)); director_disconnect(ctx); return; } if (ctx->host == NULL || ctx->hash_map) ips_count = 0; else if (director_get_host(ctx->host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } pool = pool_alloconly_create("director map users", 1024*128); hash_table_create_direct(&users, pool, 0); if (ctx->users_input == NULL) userdb_get_user_list(NULL, pool, users); else user_file_get_user_list(ctx->users_input, pool, users); if (ctx->hash_map) { /* hash -> usernames mapping */ if (str_to_uint(ctx->host, &user_hash) < 0) i_fatal("Invalid username hash: %s", ctx->host); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("user", "user", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); user = hash_table_lookup(users, POINTER_CAST(user_hash)); for (; user != NULL; user = user->next) doveadm_print(user->name); goto deinit; } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("user", "user", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("hash"); doveadm_print_header_simple("mail server ip"); doveadm_print_header_simple("expire time"); if (ips_count != 1) director_send(ctx, "USER-LIST\n"); else { director_send(ctx, t_strdup_printf( "USER-LIST\t%s\n", net_ip2addr(&ips[0]))); } while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { args = t_strsplit_tabescaped(line); if (str_array_length(args) < 3 || str_to_uint(args[0], &user_hash) < 0 || str_to_uint(args[1], &expires) < 0 || net_addr2ip(args[2], &user_ip) < 0) { i_error("Invalid USER-LIST reply: %s", line); doveadm_exit_code = EX_PROTOCOL; } else if (ips_count == 0 || ip_find(ips, ips_count, &user_ip)) { user = hash_table_lookup(users, POINTER_CAST(user_hash)); if (user == NULL) { doveadm_print(""); doveadm_print(args[0]); doveadm_print(args[2]); doveadm_print(unixdate2str(expires)); } for (; user != NULL; user = user->next) { doveadm_print(user->name); doveadm_print(args[0]); doveadm_print(args[2]); doveadm_print(unixdate2str(expires)); } } } T_END; } if (line == NULL) director_disconnected(ctx); deinit: director_disconnect(ctx); hash_table_destroy(&users); pool_unref(&pool); } static void cmd_director_add_or_update(struct doveadm_cmd_context *cctx, bool update) { const char *director_cmd = update ? "HOST-UPDATE" : "HOST-SET"; struct director_context *ctx; struct ip_addr *ips; unsigned int i, ips_count, vhost_count = UINT_MAX; const char *line, *host; string_t *cmd; ctx = cmd_director_init(cctx); if (ctx->tag != NULL && ctx->tag[0] == '\0') ctx->tag = NULL; if (ctx->host == NULL) { director_cmd_help(cctx->cmd); return; } if (ctx->vhost_count != NULL) { if (str_to_uint(ctx->vhost_count, &vhost_count) < 0) { director_cmd_help(cctx->cmd); return; } } else if (update) { director_cmd_help(cctx->cmd); return; } if (str_to_uint(ctx->host, &i) == 0) { /* host is a number. this would translate to an IP address, which is probably a mistake. */ i_error("Invalid host '%s'", ctx->host); director_cmd_help(cctx->cmd); return; } host = ctx->host; if (ctx->tag == NULL) { ctx->tag = strchr(ctx->host, '@'); if (ctx->tag != NULL) host = t_strdup_until(ctx->host, ctx->tag++); } if (director_get_host(host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } cmd = t_str_new(128); for (i = 0; i < ips_count; i++) { str_truncate(cmd, 0); str_printfa(cmd, "%s\t%s", director_cmd, net_ip2addr(&ips[i])); if (ctx->tag != NULL) str_printfa(cmd, "@%s", ctx->tag); if (vhost_count != UINT_MAX) str_printfa(cmd, "\t%u", vhost_count); str_append_c(cmd, '\n'); director_send(ctx, str_c(cmd)); } for (i = 0; i < ips_count; i++) { line = i_stream_read_next_line(ctx->input); if (line == NULL) director_disconnected(ctx); else if (strcmp(line, "OK") != 0) { i_error("%s: %s\n", net_ip2addr(&ips[i]), strcmp(line, "NOTFOUND") == 0 ? "doesn't exist" : line); doveadm_exit_code = EX_TEMPFAIL; } else if (doveadm_verbose) { i_info("%s: OK\n", net_ip2addr(&ips[i])); } } director_disconnect(ctx); } static void cmd_director_add(struct doveadm_cmd_context *cctx) { cmd_director_add_or_update(cctx, FALSE); } static void cmd_director_update(struct doveadm_cmd_context *cctx) { cmd_director_add_or_update(cctx, TRUE); } static void cmd_director_ipcmd(const char *cmd_name, const char *success_result, struct doveadm_cmd_context *cctx) { struct director_context *ctx; struct ip_addr *ips; unsigned int i, ips_count; const char *host, *line; ctx = cmd_director_init(cctx); host = ctx->host; if (host == NULL) { director_cmd_help(cctx->cmd); return; } if (director_get_host(host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } for (i = 0; i < ips_count; i++) { director_send(ctx, t_strdup_printf( "%s\t%s\n", cmd_name, net_ip2addr(&ips[i]))); } for (i = 0; i < ips_count; i++) { line = i_stream_read_next_line(ctx->input); if (line != NULL && strcmp(line, "NOTFOUND") == 0) { i_error("%s: doesn't exist\n", net_ip2addr(&ips[i])); if (doveadm_exit_code == 0) doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "OK") != 0) { i_error("%s: %s\n", net_ip2addr(&ips[i]), line); doveadm_exit_code = EX_TEMPFAIL; } else if (doveadm_verbose) { i_info("%s: %s\n", net_ip2addr(&ips[i]), success_result); } } director_disconnect(ctx); } static void cmd_director_remove(struct doveadm_cmd_context *cctx) { cmd_director_ipcmd("HOST-REMOVE", "removed", cctx); } static void cmd_director_up(struct doveadm_cmd_context *cctx) { cmd_director_ipcmd("HOST-UP", "up", cctx); } static void cmd_director_down(struct doveadm_cmd_context *cctx) { cmd_director_ipcmd("HOST-DOWN", "down", cctx); } static void cmd_director_move(struct doveadm_cmd_context *cctx) { struct director_context *ctx; struct ip_addr *ips; unsigned int ips_count, user_hash; const char *line, *ip_str; ctx = cmd_director_init(cctx); if (ctx->user == NULL || ctx->host == NULL) { director_cmd_help(cctx->cmd); return; } user_hash = mail_user_hash(ctx->user, doveadm_settings->director_username_hash); if (director_get_host(ctx->host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } ip_str = net_ip2addr(&ips[0]); director_send(ctx, t_strdup_printf( "USER-MOVE\t%u\t%s\n", user_hash, ip_str)); line = i_stream_read_next_line(ctx->input); if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "OK") == 0) { if (doveadm_verbose) i_info("User hash %u moved to %s\n", user_hash, ip_str); } else if (strcmp(line, "NOTFOUND") == 0) { i_error("Host '%s' doesn't exist", ip_str); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else if (strcmp(line, "TRYAGAIN") == 0) { i_error("User is already being moved, " "wait a while for it to be finished"); doveadm_exit_code = EX_TEMPFAIL; } else { i_error("failed: %s", line); doveadm_exit_code = EX_TEMPFAIL; } director_disconnect(ctx); } static void cmd_director_kick(struct doveadm_cmd_context *cctx) { struct director_context *ctx; const char *line; string_t *cmd = t_str_new(64); ctx = cmd_director_init(cctx); if (ctx->user == NULL) { director_cmd_help(cctx->cmd); return; } if (ctx->passdb_field == NULL) { str_append(cmd, "USER-KICK\t"); str_append_tabescaped(cmd, ctx->user); str_append_c(cmd, '\n'); } else { str_append(cmd, "USER-KICK-ALT\t"); str_append_tabescaped(cmd, ctx->passdb_field); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, ctx->user); str_append_c(cmd, '\n'); } director_send(ctx, str_c(cmd)); line = i_stream_read_next_line(ctx->input); if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "OK") == 0) { if (doveadm_verbose) i_info("User %s kicked", ctx->user); } else { i_error("failed: %s", line); doveadm_exit_code = EX_TEMPFAIL; } director_disconnect(ctx); } static void cmd_director_flush_all(struct director_context *ctx) { const char *line; if (ctx->force_flush) line = "HOST-FLUSH\n"; else if (ctx->max_parallel > 0) { line = t_strdup_printf("HOST-RESET-USERS\t\t%lld\n", (long long)ctx->max_parallel); } else { line = "HOST-RESET-USERS\n"; } director_send(ctx, line); line = i_stream_read_next_line(ctx->input); if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "OK") != 0) { i_error("failed: %s", line); doveadm_exit_code = EX_TEMPFAIL; } else if (doveadm_verbose) i_info("flushed"); director_disconnect(ctx); } static void cmd_director_flush(struct doveadm_cmd_context *cctx) { struct director_context *ctx; struct ip_addr *ips; unsigned int i, ips_count; struct ip_addr ip; const char *line; string_t *cmd; ctx = cmd_director_init(cctx); if (ctx->host == NULL) { director_cmd_help(cctx->cmd); return; } if (strcmp(ctx->host, "all") == 0) { cmd_director_flush_all(ctx); return; } if (net_addr2ip(ctx->host, &ip) == 0) { ips = &ip; ips_count = 1; } else if (director_get_host(ctx->host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } cmd = t_str_new(64); for (i = 0; i < ips_count; i++) { ip = ips[i]; str_truncate(cmd, 0); if (ctx->force_flush) str_printfa(cmd, "HOST-FLUSH\t%s\n", net_ip2addr(&ip)); else { str_printfa(cmd, "HOST-RESET-USERS\t%s", net_ip2addr(&ip)); if (ctx->max_parallel > 0) { str_printfa(cmd, "\t%lld", (long long)ctx->max_parallel); } str_append_c(cmd, '\n'); } director_send(ctx, str_c(cmd)); } for (i = 0; i < ips_count; i++) { line = i_stream_read_next_line(ctx->input); if (line != NULL && strcmp(line, "NOTFOUND") == 0) { i_warning("%s: doesn't exist", net_ip2addr(&ips[i])); if (doveadm_exit_code == 0) doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "OK") != 0) { i_warning("%s: %s", net_ip2addr(&ips[i]), line); doveadm_exit_code = EX_TEMPFAIL; } else if (doveadm_verbose) { i_info("%s: flushed", net_ip2addr(&ips[i])); } } director_disconnect(ctx); } static void cmd_director_dump(struct doveadm_cmd_context *cctx) { struct director_context *ctx; const char *line, *const *args; ctx = cmd_director_init(cctx); doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); if (ctx->explicit_socket_path) doveadm_print_formatted_set_format("doveadm director %{command} -a %{socket-path} %{host} %{vhost_count}\n"); else doveadm_print_formatted_set_format("doveadm director %{command} %{host} %{vhost_count}\n"); doveadm_print_header_simple("command"); doveadm_print_header_simple("socket-path"); doveadm_print_header_simple("host"); doveadm_print_header_simple("vhost_count"); director_send(ctx, "HOST-LIST\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { args = t_strsplit_tabescaped(line); if (str_array_length(args) >= 2) { const char *host = args[0]; const char *tag = args[3]; /* this is guaranteed to be at least NULL */ if (tag != NULL && *tag != '\0') host = t_strdup_printf("%s@%s", host, tag); doveadm_print("add"); doveadm_print(ctx->socket_path); doveadm_print(host); doveadm_print(args[1]); } } T_END; } director_send(ctx, "HOST-LIST-REMOVED\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; doveadm_print("remove"); doveadm_print(ctx->socket_path); doveadm_print(line); doveadm_print(""); } if (line == NULL) director_disconnected(ctx); director_disconnect(ctx); } static void director_read_ok_reply(struct director_context *ctx) { const char *line; line = i_stream_read_next_line(ctx->input); if (line == NULL) { director_disconnected(ctx); } else if (strcmp(line, "NOTFOUND") == 0) { i_error("Not found"); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else if (strcmp(line, "OK") != 0) { i_error("Failed: %s", line); doveadm_exit_code = EX_TEMPFAIL; } } static void cmd_director_ring_add(struct doveadm_cmd_context *cctx) { struct director_context *ctx; struct ip_addr ip; in_port_t port = 0; string_t *str = t_str_new(64); ctx = cmd_director_init(cctx); if (ctx->ip == NULL || net_addr2ip(ctx->ip, &ip) < 0 || (ctx->port && net_str2port(ctx->port, &port) < 0)) { director_cmd_help(cctx->cmd); return; } str_printfa(str, "DIRECTOR-ADD\t%s", net_ip2addr(&ip)); if (port != 0) str_printfa(str, "\t%u", port); str_append_c(str, '\n'); director_send(ctx, str_c(str)); director_read_ok_reply(ctx); director_disconnect(ctx); } static void cmd_director_ring_remove(struct doveadm_cmd_context *cctx) { struct director_context *ctx; struct ip_addr ip; string_t *str = t_str_new(64); in_port_t port = 0; ctx = cmd_director_init(cctx); if (ctx->ip == NULL || net_addr2ip(ctx->ip, &ip) < 0 || (ctx->port != NULL && net_str2port(ctx->port, &port) < 0)) { director_cmd_help(cctx->cmd); return; } str_printfa(str, "DIRECTOR-REMOVE\t%s", net_ip2addr(&ip)); if (port != 0) str_printfa(str, "\t%u", port); str_append_c(str, '\n'); director_send(ctx, str_c(str)); director_read_ok_reply(ctx); director_disconnect(ctx); } static void cmd_director_ring_status(struct doveadm_cmd_context *cctx) { struct director_context *ctx; const char *line, *const *args; unsigned long l; ctx = cmd_director_init(cctx); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("director ip"); doveadm_print_header_simple("port"); doveadm_print_header_simple("type"); doveadm_print_header_simple("last failed"); doveadm_print_header_simple("status"); director_send(ctx, "DIRECTOR-LIST\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { args = t_strsplit_tabescaped(line); if (str_array_length(args) >= 5 && str_to_ulong(args[3], &l) == 0) { doveadm_print(args[0]); doveadm_print(args[1]); doveadm_print(args[2]); if (l == 0) doveadm_print("never"); else doveadm_print(unixdate2str(l)); doveadm_print(args[4]); } } T_END; } if (line == NULL) director_disconnected(ctx); director_disconnect(ctx); } struct doveadm_cmd_ver2 doveadm_cmd_director[] = { { .name = "director status", .cmd = cmd_director_status, .usage = "[-a ] [] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "tag", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director map", .cmd = cmd_director_map, .usage = "[-a ] [-f ] [-h | -u] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('f', "users-file", CMD_PARAM_ISTREAM, 0) DOVEADM_CMD_PARAM('h', "hash-map", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('u', "user-map", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director add", .cmd = cmd_director_add, .usage = "[-a ] [-t ] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('t', "tag", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "vhost-count", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director update", .cmd = cmd_director_update, .usage = "[-a ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "vhost-count", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director up", .cmd = cmd_director_up, .usage = "[-a ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director down", .cmd = cmd_director_down, .usage = "[-a ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director remove", .cmd = cmd_director_remove, .usage = "[-a ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director move", .cmd = cmd_director_move, .usage = "[-a ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director kick", .cmd = cmd_director_kick, .usage = "[-a ] [-f ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('f', "passdb-field", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director flush", .cmd = cmd_director_flush, .usage = "[-a ] [-F] [--max-parallel ] |all", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('F', "force-flush", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "max-parallel", CMD_PARAM_INT64, 0) DOVEADM_CMD_PARAM('\0', "host", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director dump", .cmd = cmd_director_dump, .usage = "[-a ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }, { .name = "director ring add", .cmd = cmd_director_ring_add, .usage = "[-a ] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "ip", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "port", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director ring remove", .cmd = cmd_director_ring_remove, .usage = "[-a ] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "ip", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "port", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "director ring status", .cmd = cmd_director_ring_status, .usage = "[-a ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END } }; static void director_cmd_help(const struct doveadm_cmd_ver2 *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_director); i++) { if (doveadm_cmd_director[i].cmd == cmd->cmd) help_ver2(&doveadm_cmd_director[i]); } i_unreached(); } void doveadm_register_director_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_director); i++) doveadm_cmd_register_ver2(&doveadm_cmd_director[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-print.h0000644000175000017500000000314613123174404015460 00000000000000#ifndef DOVEADM_PRINT_H #define DOVEADM_PRINT_H #define DOVEADM_PRINT_TYPE_TAB "tab" #define DOVEADM_PRINT_TYPE_FLOW "flow" #define DOVEADM_PRINT_TYPE_PAGER "pager" #define DOVEADM_PRINT_TYPE_TABLE "table" #define DOVEADM_PRINT_TYPE_SERVER "server" #define DOVEADM_PRINT_TYPE_JSON "json" #define DOVEADM_PRINT_TYPE_FORMATTED "formatted" enum doveadm_print_header_flags { DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY = 0x01, DOVEADM_PRINT_HEADER_FLAG_STICKY = 0x02, DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE = 0x04, DOVEADM_PRINT_HEADER_FLAG_EXPAND = 0x08, DOVEADM_PRINT_HEADER_FLAG_NUMBER = 0x10 }; extern const struct doveadm_print_vfuncs *doveadm_print_vfuncs_all[]; extern bool doveadm_print_hide_titles; /* points to either stdout or to doveadm-server's TCP connection */ extern struct ostream *doveadm_print_ostream; bool doveadm_print_is_initialized(void); void doveadm_print_header(const char *key, const char *title, enum doveadm_print_header_flags flags); void doveadm_print_header_simple(const char *key_title); void doveadm_print(const char *value); void doveadm_print_num(uintmax_t value); /* Stream for same field continues until len=0 */ void doveadm_print_stream(const void *value, size_t size); /* Print the whole input stream. Returns 0 if ok, -1 if stream read() failed */ int doveadm_print_istream(struct istream *input); void doveadm_print_sticky(const char *key, const char *value); void doveadm_print_flush(void); void doveadm_print_unstick_headers(void); void doveadm_print_init(const char *name); void doveadm_print_deinit(void); void doveadm_print_formatted_set_format(const char *format); #endif dovecot-2.2.33.2/src/doveadm/dsync/0002755000175000017500000000000013172375613013745 500000000000000dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-state.h0000644000175000017500000000132613123174404017714 00000000000000#ifndef DSYNC_MAILBOX_STATE_H #define DSYNC_MAILBOX_STATE_H #include "guid.h" struct dsync_mailbox_state { guid_128_t mailbox_guid; uint32_t last_uidvalidity; uint32_t last_common_uid; uint64_t last_common_modseq; uint64_t last_common_pvt_modseq; uint32_t last_messages_count; bool changes_during_sync; }; ARRAY_DEFINE_TYPE(dsync_mailbox_state, struct dsync_mailbox_state); HASH_TABLE_DEFINE_TYPE(dsync_mailbox_state, uint8_t *, struct dsync_mailbox_state *); void dsync_mailbox_states_export(const HASH_TABLE_TYPE(dsync_mailbox_state) states, string_t *output); int dsync_mailbox_states_import(HASH_TABLE_TYPE(dsync_mailbox_state) states, pool_t pool, const char *input, const char **error_r); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-ibc-private.h0000644000175000017500000000566413165463624017374 00000000000000#ifndef DSYNC_IBC_PRIVATE_H #define DSYNC_IBC_PRIVATE_H #include "dsync-ibc.h" struct dsync_ibc_vfuncs { void (*deinit)(struct dsync_ibc *ibc); void (*send_handshake)(struct dsync_ibc *ibc, const struct dsync_ibc_settings *set); enum dsync_ibc_recv_ret (*recv_handshake)(struct dsync_ibc *ibc, const struct dsync_ibc_settings **set_r); void (*send_end_of_list)(struct dsync_ibc *ibc, enum dsync_ibc_eol_type type); void (*send_mailbox_state)(struct dsync_ibc *ibc, const struct dsync_mailbox_state *state); enum dsync_ibc_recv_ret (*recv_mailbox_state)(struct dsync_ibc *ibc, struct dsync_mailbox_state *state_r); void (*send_mailbox_tree_node)(struct dsync_ibc *ibc, const char *const *name, const struct dsync_mailbox_node *node); enum dsync_ibc_recv_ret (*recv_mailbox_tree_node)(struct dsync_ibc *ibc, const char *const **name_r, const struct dsync_mailbox_node **node_r); void (*send_mailbox_deletes)(struct dsync_ibc *ibc, const struct dsync_mailbox_delete *deletes, unsigned int count, char hierarchy_sep); enum dsync_ibc_recv_ret (*recv_mailbox_deletes)(struct dsync_ibc *ibc, const struct dsync_mailbox_delete **deletes_r, unsigned int *count_r, char *hierarchy_sep_r); void (*send_mailbox)(struct dsync_ibc *ibc, const struct dsync_mailbox *dsync_box); enum dsync_ibc_recv_ret (*recv_mailbox)(struct dsync_ibc *ibc, const struct dsync_mailbox **dsync_box_r); void (*send_mailbox_attribute)(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute *attr); enum dsync_ibc_recv_ret (*recv_mailbox_attribute)(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute **attr_r); void (*send_change)(struct dsync_ibc *ibc, const struct dsync_mail_change *change); enum dsync_ibc_recv_ret (*recv_change)(struct dsync_ibc *ibc, const struct dsync_mail_change **change_r); void (*send_mail_request)(struct dsync_ibc *ibc, const struct dsync_mail_request *request); enum dsync_ibc_recv_ret (*recv_mail_request)(struct dsync_ibc *ibc, const struct dsync_mail_request **request_r); void (*send_mail)(struct dsync_ibc *ibc, const struct dsync_mail *mail); enum dsync_ibc_recv_ret (*recv_mail)(struct dsync_ibc *ibc, struct dsync_mail **mail_r); void (*send_finish)(struct dsync_ibc *ibc, const char *error, enum mail_error mail_error, bool require_full_resync); enum dsync_ibc_recv_ret (*recv_finish)(struct dsync_ibc *ibc, const char **error_r, enum mail_error *mail_error_r, bool *require_full_resync_r); void (*close_mail_streams)(struct dsync_ibc *ibc); bool (*is_send_queue_full)(struct dsync_ibc *ibc); bool (*has_pending_data)(struct dsync_ibc *ibc); }; struct dsync_ibc { struct dsync_ibc_vfuncs v; io_callback_t *io_callback; void *io_context; unsigned int failed:1; unsigned int timeout:1; }; #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-transaction-log-scan.h0000644000175000017500000000252613123174404021174 00000000000000#ifndef DSYNC_TRANSACTION_LOG_SCAN_H #define DSYNC_TRANSACTION_LOG_SCAN_H HASH_TABLE_DEFINE_TYPE(dsync_uid_mail_change, void *, struct dsync_mail_change *); HASH_TABLE_DEFINE_TYPE(dsync_attr_change, struct dsync_mailbox_attribute *, struct dsync_mailbox_attribute *); struct mail_index_view; struct dsync_transaction_log_scan; int dsync_transaction_log_scan_init(struct mail_index_view *view, struct mail_index_view *pvt_view, uint32_t highest_wanted_uid, uint64_t modseq, uint64_t pvt_modseq, struct dsync_transaction_log_scan **scan_r, bool *pvt_too_old_r); HASH_TABLE_TYPE(dsync_uid_mail_change) dsync_transaction_log_scan_get_hash(struct dsync_transaction_log_scan *scan); HASH_TABLE_TYPE(dsync_attr_change) dsync_transaction_log_scan_get_attr_hash(struct dsync_transaction_log_scan *scan); /* Returns TRUE if the entire transaction log was scanned */ bool dsync_transaction_log_scan_has_all_changes(struct dsync_transaction_log_scan *scan); /* If the given UID has been expunged after the initial log scan, create/update a change record for it and return it. */ struct dsync_mail_change * dsync_transaction_log_scan_find_new_expunge(struct dsync_transaction_log_scan *scan, uint32_t uid); void dsync_transaction_log_scan_deinit(struct dsync_transaction_log_scan **scan); #endif dovecot-2.2.33.2/src/doveadm/dsync/Makefile.in0000644000175000017500000007151413172375572015744 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/doveadm/dsync ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am_libdovecot_dsync_la_OBJECTS = libdovecot_dsync_la_OBJECTS = $(am_libdovecot_dsync_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_dsync_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_dsync_la_LDFLAGS) \ $(LDFLAGS) -o $@ libdsync_la_LIBADD = am_libdsync_la_OBJECTS = dsync-brain.lo dsync-brain-mailbox.lo \ dsync-brain-mailbox-tree.lo dsync-brain-mailbox-tree-sync.lo \ dsync-brain-mails.lo dsync-deserializer.lo dsync-mail.lo \ dsync-mailbox.lo dsync-mailbox-import.lo \ dsync-mailbox-export.lo dsync-mailbox-state.lo \ dsync-mailbox-tree.lo dsync-mailbox-tree-fill.lo \ dsync-mailbox-tree-sync.lo dsync-serializer.lo dsync-ibc.lo \ dsync-ibc-stream.lo dsync-ibc-pipe.lo \ dsync-transaction-log-scan.lo libdsync_la_OBJECTS = $(am_libdsync_la_OBJECTS) am__EXEEXT_1 = test-dsync-mailbox-tree-sync$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_dsync_mailbox_tree_sync_OBJECTS = \ test-dsync-mailbox-tree-sync.$(OBJEXT) test_dsync_mailbox_tree_sync_OBJECTS = \ $(am_test_dsync_mailbox_tree_sync_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_dsync_la_SOURCES) $(libdsync_la_SOURCES) \ $(test_dsync_mailbox_tree_sync_SOURCES) DIST_SOURCES = $(libdovecot_dsync_la_SOURCES) $(libdsync_la_SOURCES) \ $(test_dsync_mailbox_tree_sync_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkglib_LTLIBRARIES = libdovecot-dsync.la noinst_LTLIBRARIES = libdsync.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage libdsync_la_SOURCES = \ dsync-brain.c \ dsync-brain-mailbox.c \ dsync-brain-mailbox-tree.c \ dsync-brain-mailbox-tree-sync.c \ dsync-brain-mails.c \ dsync-deserializer.c \ dsync-mail.c \ dsync-mailbox.c \ dsync-mailbox-import.c \ dsync-mailbox-export.c \ dsync-mailbox-state.c \ dsync-mailbox-tree.c \ dsync-mailbox-tree-fill.c \ dsync-mailbox-tree-sync.c \ dsync-serializer.c \ dsync-ibc.c \ dsync-ibc-stream.c \ dsync-ibc-pipe.c \ dsync-transaction-log-scan.c libdovecot_dsync_la_SOURCES = libdovecot_dsync_la_LIBADD = libdsync.la ../../lib-storage/libdovecot-storage.la ../../lib-dovecot/libdovecot.la libdovecot_dsync_la_DEPENDENCIES = libdsync.la libdovecot_dsync_la_LDFLAGS = -export-dynamic pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ dsync-brain.h \ dsync-ibc.h noinst_HEADERS = \ dsync-brain-private.h \ dsync-mail.h \ dsync-mailbox.h \ dsync-mailbox-import.h \ dsync-mailbox-export.h \ dsync-mailbox-state.h \ dsync-mailbox-tree.h \ dsync-mailbox-tree-private.h \ dsync-serializer.h \ dsync-deserializer.h \ dsync-ibc-private.h \ dsync-transaction-log-scan.h test_programs = \ test-dsync-mailbox-tree-sync test_libs = \ ../../lib-test/libtest.la \ ../../lib/liblib.la test_dsync_mailbox_tree_sync_SOURCES = test-dsync-mailbox-tree-sync.c test_dsync_mailbox_tree_sync_LDADD = dsync-mailbox-tree-sync.lo dsync-mailbox-tree.lo $(test_libs) test_dsync_mailbox_tree_sync_DEPENDENCIES = $(pkglib_LTLIBRARIES) $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/doveadm/dsync/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/doveadm/dsync/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-dsync.la: $(libdovecot_dsync_la_OBJECTS) $(libdovecot_dsync_la_DEPENDENCIES) $(EXTRA_libdovecot_dsync_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_dsync_la_LINK) -rpath $(pkglibdir) $(libdovecot_dsync_la_OBJECTS) $(libdovecot_dsync_la_LIBADD) $(LIBS) libdsync.la: $(libdsync_la_OBJECTS) $(libdsync_la_DEPENDENCIES) $(EXTRA_libdsync_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libdsync_la_OBJECTS) $(libdsync_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-dsync-mailbox-tree-sync$(EXEEXT): $(test_dsync_mailbox_tree_sync_OBJECTS) $(test_dsync_mailbox_tree_sync_DEPENDENCIES) $(EXTRA_test_dsync_mailbox_tree_sync_DEPENDENCIES) @rm -f test-dsync-mailbox-tree-sync$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_dsync_mailbox_tree_sync_OBJECTS) $(test_dsync_mailbox_tree_sync_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-brain-mailbox-tree-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-brain-mailbox-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-brain-mailbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-brain-mails.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-brain.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-deserializer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-ibc-pipe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-ibc-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-ibc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-export.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-import.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-state.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-tree-fill.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-tree-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-mailbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-serializer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsync-transaction-log-scan.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-dsync-mailbox-tree-sync.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/doveadm/dsync/dsync-deserializer.c0000644000175000017500000001137313123174404017623 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "dsync-serializer.h" #include "dsync-deserializer.h" struct dsync_deserializer { pool_t pool; const char *name; const char *const *required_fields; const char *const *keys; unsigned int *required_field_indexes; unsigned int required_field_count; }; struct dsync_deserializer_decoder { pool_t pool; struct dsync_deserializer *deserializer; const char *const *values; unsigned int values_count; }; static bool field_find(const char *const *names, const char *name, unsigned int *idx_r) { unsigned int i; for (i = 0; names[i] != NULL; i++) { if (strcmp(names[i], name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } int dsync_deserializer_init(const char *name, const char *const *required_fields, const char *header_line, struct dsync_deserializer **deserializer_r, const char **error_r) { struct dsync_deserializer *deserializer; const char **dup_required_fields; unsigned int i, required_count; pool_t pool; *deserializer_r = NULL; pool = pool_alloconly_create("dsync deserializer", 1024); deserializer = p_new(pool, struct dsync_deserializer, 1); deserializer->pool = pool; deserializer->name = p_strdup(pool, name); deserializer->keys = (void *)p_strsplit_tabescaped(pool, header_line); deserializer->required_field_count = required_count = required_fields == NULL ? 0 : str_array_length(required_fields); dup_required_fields = p_new(pool, const char *, required_count + 1); deserializer->required_field_indexes = p_new(pool, unsigned int, required_count + 1); for (i = 0; i < required_count; i++) { dup_required_fields[i] = p_strdup(pool, required_fields[i]); if (!field_find(deserializer->keys, required_fields[i], &deserializer->required_field_indexes[i])) { *error_r = t_strdup_printf( "Header missing required field %s", required_fields[i]); pool_unref(&pool); return -1; } } deserializer->required_fields = dup_required_fields; *deserializer_r = deserializer; return 0; } void dsync_deserializer_deinit(struct dsync_deserializer **_deserializer) { struct dsync_deserializer *deserializer = *_deserializer; *_deserializer = NULL; pool_unref(&deserializer->pool); } int dsync_deserializer_decode_begin(struct dsync_deserializer *deserializer, const char *input, struct dsync_deserializer_decoder **decoder_r, const char **error_r) { struct dsync_deserializer_decoder *decoder; unsigned int i; char **values; pool_t pool; *decoder_r = NULL; pool = pool_alloconly_create("dsync deserializer decode", 1024); decoder = p_new(pool, struct dsync_deserializer_decoder, 1); decoder->pool = pool; decoder->deserializer = deserializer; values = p_strsplit_tabescaped(pool, input); /* fix NULLs */ for (i = 0; values[i] != NULL; i++) { if (values[i][0] == NULL_CHR) { /* NULL? */ if (values[i][1] == '\0') values[i] = NULL; else values[i] += 1; } } decoder->values_count = i; /* see if all required fields exist */ for (i = 0; i < deserializer->required_field_count; i++) { unsigned int ridx = deserializer->required_field_indexes[i]; if (ridx >= decoder->values_count || values[ridx] == NULL) { *error_r = t_strdup_printf("Missing required field %s", deserializer->required_fields[i]); pool_unref(&pool); return -1; } } decoder->values = (void *)values; *decoder_r = decoder; return 0; } static bool dsync_deserializer_find_field(struct dsync_deserializer *deserializer, const char *key, unsigned int *idx_r) { unsigned int i; for (i = 0; deserializer->keys[i] != NULL; i++) { if (strcmp(deserializer->keys[i], key) == 0) { *idx_r = i; return TRUE; } } return FALSE; } bool dsync_deserializer_decode_try(struct dsync_deserializer_decoder *decoder, const char *key, const char **value_r) { unsigned int idx; if (!dsync_deserializer_find_field(decoder->deserializer, key, &idx) || idx >= decoder->values_count) { *value_r = NULL; return FALSE; } else { *value_r = decoder->values[idx]; return *value_r != NULL; } } const char * dsync_deserializer_decode_get(struct dsync_deserializer_decoder *decoder, const char *key) { const char *value; if (!dsync_deserializer_decode_try(decoder, key, &value)) { i_panic("dsync_deserializer_decode_get() " "used for non-required key %s", key); } return value; } const char * dsync_deserializer_decoder_get_name(struct dsync_deserializer_decoder *decoder) { return decoder->deserializer->name; } void dsync_deserializer_decode_finish(struct dsync_deserializer_decoder **_decoder) { struct dsync_deserializer_decoder *decoder = *_decoder; *_decoder = NULL; pool_unref(&decoder->pool); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain-mailbox-tree-sync.c0000644000175000017500000001605313123174404021574 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-namespace.h" #include "mail-storage.h" #include "dsync-mailbox-tree.h" #include "dsync-brain-private.h" static int sync_create_box(struct dsync_brain *brain, struct mailbox *box, const guid_128_t mailbox_guid, uint32_t uid_validity, enum mail_error *error_r) { struct mailbox_metadata metadata; struct mailbox_update update; enum mail_error error; const char *errstr; int ret; i_zero(&update); memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid)); update.uid_validity = uid_validity; if (mailbox_create(box, &update, FALSE) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_EXISTS) { i_error("Can't create mailbox %s: %s", mailbox_get_vname(box), errstr); *error_r = error; return -1; } } if (brain->no_mail_sync) { /* trust that create worked, we can't actually open it and verify. */ return 0; } /* sync the mailbox so we can look up its latest status */ if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Can't sync mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, error_r)); return -1; } /* verify that the GUID is what we wanted. if it's not, it probably means that the mailbox had already been created. then we'll use the GUID that is higher. mismatching UIDVALIDITY is handled later, because we choose it by checking which mailbox has more messages */ if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("Can't get mailbox GUID %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, error_r)); return -1; } ret = memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid)); /* if THEIR guid is bigger than OUR guid, and we are not doing backup in either direction, OR GUID did not match and we are receiving backup, try change the mailbox GUID. */ if ((ret > 0 && !brain->backup_recv && !brain->backup_send) || (ret != 0 && brain->backup_recv)) { if (brain->debug) { i_debug("brain %c: Changing mailbox %s GUID %s -> %s", brain->master_brain ? 'M' : 'S', mailbox_get_vname(box), guid_128_to_string(metadata.guid), guid_128_to_string(mailbox_guid)); } i_zero(&update); memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid)); if (mailbox_update(box, &update) < 0) { i_error("Can't update mailbox GUID %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, error_r)); return -1; } /* verify that the update worked */ if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("Can't get mailbox GUID %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, error_r)); return -1; } if (memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid)) != 0) { i_error("Backend didn't update mailbox %s GUID", mailbox_get_vname(box)); *error_r = MAIL_ERROR_TEMP; return -1; } } else if (ret < 0) { if (brain->debug) { i_debug("brain %c: Other brain should change mailbox " "%s GUID %s -> %s", brain->master_brain ? 'M' : 'S', mailbox_get_vname(box), guid_128_to_string(mailbox_guid), guid_128_to_string(metadata.guid)); } } return 0; } int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain, const struct dsync_mailbox_tree_sync_change *change, enum mail_error *error_r) { struct mailbox *box = NULL, *destbox; const char *errstr, *func_name = NULL, *storage_name; enum mail_error error; int ret = -1; if (brain->backup_send) { i_assert(brain->no_backup_overwrite); return 0; } switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: /* make sure we're deleting the correct mailbox */ ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid, &box, &errstr, error_r); if (ret < 0) { i_error("Mailbox sync: Couldn't allocate mailbox %s GUID %s: %s", change->full_name, guid_128_to_string(change->mailbox_guid), errstr); return -1; } if (ret == 0) { dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Mailbox %s GUID %s deletion conflict: %s", change->full_name, guid_128_to_string(change->mailbox_guid), errstr)); return 0; } break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: storage_name = mailbox_list_get_storage_name(change->ns->list, change->full_name); if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0) return 0; errstr = mailbox_list_get_last_internal_error(change->ns->list, &error); if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_EXISTS) { dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Mailbox %s mailbox_list_delete_dir conflict: %s", change->full_name, errstr)); return 0; } else { i_error("Mailbox sync: mailbox_list_delete_dir failed: %s", errstr); *error_r = error; return -1; } default: box = mailbox_alloc(change->ns->list, change->full_name, 0); break; } mailbox_skip_create_name_restrictions(box, TRUE); switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX: ret = sync_create_box(brain, box, change->mailbox_guid, change->uid_validity, error_r); mailbox_free(&box); return ret; case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR: ret = mailbox_create(box, NULL, TRUE); if (ret < 0 && mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) { /* it doesn't matter if somebody else created this directory or we automatically did while creating its child mailbox. it's there now anyway and we don't gain anything by treating this failure any differently from success. */ ret = 0; } func_name = "mailbox_create"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: ret = mailbox_delete(box); func_name = "mailbox_delete"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: i_unreached(); case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME: destbox = mailbox_alloc(change->ns->list, change->rename_dest_name, 0); mailbox_skip_create_name_restrictions(destbox, TRUE); ret = mailbox_rename(box, destbox); func_name = "mailbox_rename"; mailbox_free(&destbox); break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE: ret = mailbox_set_subscribed(box, TRUE); func_name = "mailbox_set_subscribed"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE: ret = mailbox_set_subscribed(box, FALSE); func_name = "mailbox_set_subscribed"; break; } if (ret < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error == MAIL_ERROR_EXISTS || error == MAIL_ERROR_NOTFOUND) { /* mailbox was already created or was already deleted. let the next sync figure out what to do */ dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Mailbox %s %s conflict: %s", mailbox_get_vname(box), func_name, errstr)); ret = 0; } else { i_error("Mailbox %s sync: %s failed: %s", mailbox_get_vname(box), func_name, errstr); *error_r = error; } } mailbox_free(&box); return ret; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain.h0000644000175000017500000001213213165463543016246 00000000000000#ifndef DSYNC_BRAIN_H #define DSYNC_BRAIN_H #include "guid.h" #include "mail-error.h" struct mail_namespace; struct mail_user; struct dsync_ibc; enum dsync_brain_flags { DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS = 0x01, DSYNC_BRAIN_FLAG_BACKUP_SEND = 0x02, DSYNC_BRAIN_FLAG_BACKUP_RECV = 0x04, DSYNC_BRAIN_FLAG_DEBUG = 0x08, DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES= 0x10, /* Sync everything but the actual mails (e.g. mailbox creates, deletes) */ DSYNC_BRAIN_FLAG_NO_MAIL_SYNC = 0x20, /* Used with BACKUP_SEND/RECV: Don't force the Use the two-way syncing algorithm, but don't actually modify anything locally. (Useful during migration.) */ DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE = 0x40, /* Run storage purge on the remote after syncing. Useful with e.g. a nightly doveadm backup. */ DSYNC_BRAIN_FLAG_PURGE_REMOTE = 0x80, /* Don't prefetch mail bodies until they're actually needed. This works only with pipe ibc. It's useful if most of the mails can be copied directly within filesystem without having to read them. */ DSYNC_BRAIN_FLAG_NO_MAIL_PREFETCH = 0x100, /* Disable mailbox renaming logic. This is just a kludge that should be removed once the renaming logic has no more bugs.. */ DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES = 0x200, /* Add MAILBOX_TRANSACTION_FLAG_NO_NOTIFY to transactions. */ DSYNC_BRAIN_FLAG_NO_NOTIFY = 0x400, /* Workaround missing Date/Message-ID headers */ DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND = 0x800, }; enum dsync_brain_sync_type { DSYNC_BRAIN_SYNC_TYPE_UNKNOWN, /* Go through all mailboxes to make sure everything is synced */ DSYNC_BRAIN_SYNC_TYPE_FULL, /* Go through all mailboxes that have changed (based on UIDVALIDITY, UIDNEXT, HIGHESTMODSEQ). If both sides have had equal amount of changes in some mailbox, it may get incorrectly skipped. */ DSYNC_BRAIN_SYNC_TYPE_CHANGED, /* Use saved state to find out what has changed. */ DSYNC_BRAIN_SYNC_TYPE_STATE }; struct dsync_brain_settings { const char *process_title_prefix; /* Sync only these namespaces */ ARRAY(struct mail_namespace *) sync_namespaces; /* Sync only this mailbox name */ const char *sync_box; /* Use this virtual \All mailbox to be able to copy mails with the same GUID instead of saving them twice. With most storages this results in less disk space usage. */ const char *virtual_all_box; /* Sync only this mailbox GUID */ guid_128_t sync_box_guid; /* Exclude these mailboxes from the sync. They can contain '*' wildcards and be \special-use flags. */ const char *const *exclude_mailboxes; /* Alternative character to use in mailbox names where the original character cannot be used. */ char mailbox_alt_char; /* Sync only mails with received timestamp at least this high. */ time_t sync_since_timestamp; /* Sync only mails with received timestamp less or equal than this */ time_t sync_until_timestamp; /* Don't sync mails larger than this. */ uoff_t sync_max_size; /* Sync only mails which contains / doesn't contain this flag. '-' at the beginning means this flag must not exist. */ const char *sync_flag; /* Headers to hash (defaults to Date, Message-ID) */ const char *const *hashed_headers; /* If non-zero, use dsync lock file for this user */ unsigned int lock_timeout_secs; /* If non-zero, importing will attempt to commit transaction after saving this many messages. */ unsigned int import_commit_msgs_interval; /* Input state for DSYNC_BRAIN_SYNC_TYPE_STATE */ const char *state; }; struct dsync_brain * dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc, enum dsync_brain_sync_type sync_type, enum dsync_brain_flags flags, const struct dsync_brain_settings *set); struct dsync_brain * dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc, bool local, const char *process_title_prefix); /* Returns 0 if everything was successful, -1 if syncing failed in some way */ int dsync_brain_deinit(struct dsync_brain **brain, enum mail_error *error_r); /* Returns TRUE if brain needs to run more, FALSE if it's finished. changed_r is TRUE if anything happened during this run. */ bool dsync_brain_run(struct dsync_brain *brain, bool *changed_r); /* Returns TRUE if brain has failed, and there's no point in continuing. */ bool dsync_brain_has_failed(struct dsync_brain *brain); /* Returns the current sync state string, which can be given as parameter to dsync_brain_master_init() to quickly sync only the new changes. */ void dsync_brain_get_state(struct dsync_brain *brain, string_t *output); /* Returns the sync type that was used. Mainly useful with slave brain. */ enum dsync_brain_sync_type dsync_brain_get_sync_type(struct dsync_brain *brain); /* If there were any unexpected changes during the sync, return the reason for them. Otherwise return NULL. If remote_only_r=TRUE, this brain itself didn't see any changes, but the remote brain did. */ const char *dsync_brain_get_unexpected_changes_reason(struct dsync_brain *brain, bool *remote_only_r); /* Returns TRUE if we want to sync this namespace. */ bool dsync_brain_want_namespace(struct dsync_brain *brain, struct mail_namespace *ns); #endif dovecot-2.2.33.2/src/doveadm/dsync/test-dsync-mailbox-tree-sync.c0000644000175000017500000005056313165463624021477 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "sha1.h" #include "str.h" #include "mailbox-list-private.h" #include "dsync-mailbox-tree-private.h" #include "test-common.h" #include #define MAX_DEPTH 4 #define TEST_NAMESPACE_NAME "INBOX" static struct mail_namespace inbox_namespace = { .prefix = TEST_NAMESPACE_NAME"/", .prefix_len = sizeof(TEST_NAMESPACE_NAME)-1 + 1 }; char mail_namespace_get_sep(struct mail_namespace *ns ATTR_UNUSED) { return '/'; } void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r) { unsigned char sha[SHA1_RESULTLEN]; sha1_get_digest(name, strlen(name), sha); memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha))); } static struct dsync_mailbox_node * node_create(struct dsync_mailbox_tree *tree, unsigned int counter, const char *name, unsigned int last_renamed_or_created) { struct dsync_mailbox_node *node; node = dsync_mailbox_tree_get(tree, name); memcpy(node->mailbox_guid, &counter, sizeof(counter)); node->uid_validity = counter; node->existence = DSYNC_MAILBOX_NODE_EXISTS; node->last_renamed_or_created = last_renamed_or_created; return node; } static struct dsync_mailbox_node * random_node_create(struct dsync_mailbox_tree *tree, unsigned int counter, const char *name) { return node_create(tree, counter, name, rand() % 10); } static void nodes_create(struct dsync_mailbox_tree *tree, unsigned int *counter, const char *const *names) { for (; *names != NULL; names++) { *counter += 1; node_create(tree, *counter, *names, 0); } } static void nodes_delete(struct dsync_mailbox_tree *tree, unsigned int *counter, const char *const *names) { struct dsync_mailbox_node *node; for (; *names != NULL; names++) { *counter += 1; node = node_create(tree, *counter, *names, 0); node->existence = DSYNC_MAILBOX_NODE_DELETED; } } static void create_random_nodes(struct dsync_mailbox_tree *tree, const char *parent_name, unsigned int depth, unsigned int *counter) { unsigned int parent_len, i, nodes_count = 1 + rand() % 3; string_t *str; if (depth == MAX_DEPTH) return; str = t_str_new(32); if (*parent_name != '\0') str_printfa(str, "%s/", parent_name); parent_len = str_len(str); for (i = 0; i < nodes_count; i++) { *counter += 1; str_truncate(str, parent_len); str_printfa(str, "%u.%u", depth, i); random_node_create(tree, *counter, str_c(str)); create_random_nodes(tree, str_c(str), depth+1, counter); } } static struct dsync_mailbox_tree *create_random_tree(void) { struct dsync_mailbox_tree *tree; unsigned int counter = 0; tree = dsync_mailbox_tree_init('/', '_'); create_random_nodes(tree, "", 0, &counter); return tree; } static void test_tree_nodes_fixup(struct dsync_mailbox_node **pos, unsigned int *newguid_counter) { struct dsync_mailbox_node *node; for (node = *pos; node != NULL; node = node->next) { if (node->sync_delayed_guid_change) { /* the real code will pick one of the GUIDs. we don't really care which one gets picked, so we'll just change them to the same new one */ memcpy(node->mailbox_guid, newguid_counter, sizeof(*newguid_counter)); node->uid_validity = *newguid_counter; *newguid_counter += 1; } if (node->existence == DSYNC_MAILBOX_NODE_DELETED) node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; test_tree_nodes_fixup(&node->first_child, newguid_counter); if (node->existence != DSYNC_MAILBOX_NODE_EXISTS && node->first_child == NULL) { /* nonexistent node, drop it */ *pos = node->next; } else { pos = &node->next; } } } static void test_tree_fixup(struct dsync_mailbox_tree *tree) { unsigned int newguid_counter = INT_MAX; test_tree_nodes_fixup(&tree->root.first_child, &newguid_counter); } static void nodes_dump(const struct dsync_mailbox_node *node, unsigned int depth) { unsigned int i; for (; node != NULL; node = node->next) { for (i = 0; i < depth; i++) printf(" "); printf("%-*s guid:%.5s uidv:%u %d%d %ld\n", 40-depth, node->name, guid_128_to_string(node->mailbox_guid), node->uid_validity, node->existence, node->subscribed, (long)node->last_renamed_or_created); nodes_dump(node->first_child, depth+1); } } static void trees_dump(struct dsync_mailbox_tree *tree1, struct dsync_mailbox_tree *tree2) { printf("tree1:\n"); nodes_dump(tree1->root.first_child, 1); printf("tree2:\n"); nodes_dump(tree2->root.first_child, 1); } static void test_trees_nofree(struct dsync_mailbox_tree *tree1, struct dsync_mailbox_tree **_tree2) { struct dsync_mailbox_tree *tree2 = *_tree2; struct dsync_mailbox_tree *orig_tree1, *orig_tree2; struct dsync_mailbox_tree_sync_ctx *ctx; struct dsync_mailbox_node *dup_node1, *dup_node2; const struct dsync_mailbox_tree_sync_change *change; orig_tree1 = dsync_mailbox_tree_dup(tree1); orig_tree2 = dsync_mailbox_tree_dup(tree2); /* test tree1 -> tree2 */ dsync_mailbox_tree_build_guid_hash(tree1, &dup_node1, &dup_node2); dsync_mailbox_tree_build_guid_hash(tree2, &dup_node1, &dup_node2); ctx = dsync_mailbox_trees_sync_init(tree1, tree2, DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY, DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG); while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) { } dsync_mailbox_trees_sync_deinit(&ctx); test_tree_fixup(tree1); test_tree_fixup(tree2); if (!dsync_mailbox_trees_equal(tree1, tree2)) { test_assert(FALSE); trees_dump(tree1, tree2); } /* test tree2 -> tree1 */ dsync_mailbox_tree_build_guid_hash(orig_tree1, &dup_node1, &dup_node2); dsync_mailbox_tree_build_guid_hash(orig_tree2, &dup_node1, &dup_node2); ctx = dsync_mailbox_trees_sync_init(orig_tree2, orig_tree1, DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY, 0); while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) { } dsync_mailbox_trees_sync_deinit(&ctx); test_tree_fixup(orig_tree1); test_tree_fixup(orig_tree2); if (!dsync_mailbox_trees_equal(orig_tree1, orig_tree2)) { test_assert(FALSE); trees_dump(orig_tree1, orig_tree2); } /* make sure both directions produced equal trees */ if (!dsync_mailbox_trees_equal(tree1, orig_tree1)) { test_assert(FALSE); trees_dump(tree1, orig_tree1); } dsync_mailbox_tree_deinit(_tree2); dsync_mailbox_tree_deinit(&orig_tree1); dsync_mailbox_tree_deinit(&orig_tree2); } static void test_tree_nodes_add_namespace(struct dsync_mailbox_node *node, struct mail_namespace *ns) { for (; node != NULL; node = node->next) { node->ns = ns; test_tree_nodes_add_namespace(node->first_child, ns); } } static void test_tree_add_namespace(struct dsync_mailbox_tree *tree, struct mail_namespace *ns) { struct dsync_mailbox_node *node, *n; node = dsync_mailbox_tree_get(tree, TEST_NAMESPACE_NAME); node->existence = DSYNC_MAILBOX_NODE_EXISTS; i_assert(tree->root.first_child == node); i_assert(node->first_child == NULL); node->first_child = node->next; for (n = node->first_child; n != NULL; n = n->next) n->parent = node; node->next = NULL; test_tree_nodes_add_namespace(&tree->root, ns); } static void test_trees(struct dsync_mailbox_tree *tree1, struct dsync_mailbox_tree *tree2) { struct dsync_mailbox_tree *tree1_dup, *tree2_dup; tree1_dup = dsync_mailbox_tree_dup(tree1); tree2_dup = dsync_mailbox_tree_dup(tree2); /* test without namespace prefix */ test_trees_nofree(tree1, &tree2); dsync_mailbox_tree_deinit(&tree1); /* test with namespace prefix */ test_tree_add_namespace(tree1_dup, &inbox_namespace); test_tree_add_namespace(tree2_dup, &inbox_namespace); test_trees_nofree(tree1_dup, &tree2_dup); dsync_mailbox_tree_deinit(&tree1_dup); } static void test_dsync_mailbox_tree_sync_creates(void) { static const char *common_nodes[] = { "foo", "foo/bar", NULL }; static const char *create1_nodes[] = { "bar", "foo/baz", NULL }; static const char *create2_nodes[] = { "foo/xyz", "foo/bar/3", NULL }; struct dsync_mailbox_tree *tree1, *tree2; unsigned int counter = 0; test_begin("dsync mailbox tree sync creates"); tree1 = dsync_mailbox_tree_init('/', '_'); nodes_create(tree1, &counter, common_nodes); tree2 = dsync_mailbox_tree_dup(tree1); nodes_create(tree1, &counter, create1_nodes); nodes_create(tree2, &counter, create2_nodes); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_deletes(void) { static const char *common_nodes[] = { "1", "2", "3", "2/s1", "2/s2", "x/y", NULL }; static const char *delete1_nodes[] = { "1", "2", NULL }; static const char *delete2_nodes[] = { "2/s1", "x/y", NULL }; struct dsync_mailbox_tree *tree1, *tree2; unsigned int counter = 0; test_begin("dsync mailbox tree sync deletes"); tree1 = dsync_mailbox_tree_init('/', '_'); nodes_create(tree1, &counter, common_nodes); tree2 = dsync_mailbox_tree_dup(tree1); nodes_delete(tree1, &counter, delete1_nodes); nodes_delete(tree2, &counter, delete2_nodes); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames1(void) { static const char *common_nodes[] = { "1", "2", "3", "2/s1", "2/s2", "x/y", "3/s3", NULL }; struct dsync_mailbox_tree *tree1, *tree2; struct dsync_mailbox_node *node; unsigned int counter = 0; test_begin("dsync mailbox tree sync renames 1"); tree1 = dsync_mailbox_tree_init('/', '_'); nodes_create(tree1, &counter, common_nodes); tree2 = dsync_mailbox_tree_dup(tree1); node = dsync_mailbox_tree_get(tree1, "1"); node->name = "a"; node->last_renamed_or_created = 1000; node = dsync_mailbox_tree_get(tree2, "2"); node->name = "b"; node->last_renamed_or_created = 1000; node = dsync_mailbox_tree_get(tree1, "3/s3"); node->name = "z"; node->last_renamed_or_created = 1000; dsync_mailbox_tree_node_detach(node); dsync_mailbox_tree_node_attach(node, &tree1->root); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames2(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 2"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1", 1); node_create(tree1, 2, "0/1/2", 3); node_create(tree2, 1, "0", 0); node_create(tree2, 2, "0/1/2", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames3(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 3"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/2", 1); node_create(tree1, 2, "0/3", 1); node_create(tree2, 1, "0/4/5", 0); node_create(tree2, 2, "1", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames4(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 4"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/b", 0); node_create(tree1, 2, "c", 2); node_create(tree2, 2, "0/a", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames5(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 5"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "b", 0); node_create(tree1, 2, "c", 2); node_create(tree2, 2, "0/a", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames6(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 6"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1", 0); node_create(tree1, 2, "0/2", 1); node_create(tree2, 1, "0", 1); node_create(tree2, 2, "0/3", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames7(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 7"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/2", 0); node_create(tree2, 1, "1/2", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames8(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 8"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1", 0); node_create(tree1, 2, "0/2", 1); node_create(tree2, 1, "0", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames9(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 9"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1/2", 0); node_create(tree1, 2, "0/3", 1); node_create(tree2, 1, "0", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames10(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 10"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1", 0); node_create(tree1, 3, "0/2/3", 0); node_create(tree2, 1, "0", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames11(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 11"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/1", 2); node_create(tree1, 0, "0/1/2", 0); node_create(tree2, 1, "0", 1); node_create(tree2, 0, "0/1/2", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames12(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 12"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/2", 0); node_create(tree1, 2, "1", 0); node_create(tree1, 3, "1/4", 0); node_create(tree1, 4, "1/4/5", 1); node_create(tree2, 1, "1", 2); node_create(tree2, 2, "1/4", 3); node_create(tree2, 3, "1/4/6", 4); node_create(tree2, 4, "1/3", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames13(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 13"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 4, "0.0/1.0/2.1", 0); node_create(tree1, 5, "0.1", 2); node_create(tree1, 6, "0.1/1.0", 2); node_create(tree1, 7, "0.1/1.0/2.0", 8); node_create(tree2, 5, "0.1/1.0", 5); node_create(tree2, 6, "0.1/1.0/2.0", 8); node_create(tree2, 7, "0.1/1.1", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames14(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 14"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "1", 0); node_create(tree1, 2, "1/2", 0); node_create(tree1, 3, "1/2/4", 1); node_create(tree2, 1, "1/2", 3); node_create(tree2, 2, "1/2/5", 4); node_create(tree2, 3, "1/2/4", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames15(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 15"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "1", 0); node_create(tree2, 2, "1", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames16(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 16"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "1/2", 4); node_create(tree1, 2, "1", 2); node_create(tree2, 1, "2", 1); node_create(tree2, 2, "1/2", 3); node_create(tree2, 3, "1", 5); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames17(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 17"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "1", 1); node_create(tree2, 1, "1/2", 0); node_create(tree2, 2, "1", 2); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames18(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 18"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 2, "a", 5); node_create(tree1, 4, "a/c", 2); node_create(tree1, 5, "b", 6); node_create(tree2, 1, "a", 7); node_create(tree2, 2, "b", 3); node_create(tree2, 3, "b/c", 4); node_create(tree2, 4, "d", 1); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames19(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 19"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "0/2/1", 1); node_create(tree1, 2, "0/4", 3); node_create(tree1, 3, "0/2", 2); node_create(tree2, 1, "1", 0); node_create(tree2, 2, "1/3", 4); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_renames20(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 20"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "1", 0); node_create(tree1, 2, "0", 0); node_create(tree1, 3, "0/2", 0); /* rename 0 -> 1/0 */ node_create(tree2, 1, "1", 0); node_create(tree2, 2, "1/0", 1); node_create(tree2, 3, "1/0/2", 0); test_trees_nofree(tree1, &tree2); test_assert(tree1->root.first_child != NULL && tree1->root.first_child->next == NULL); dsync_mailbox_tree_deinit(&tree1); test_end(); } static void test_dsync_mailbox_tree_sync_renames21(void) { #if 0 /* FIXME: we can't currently test this without crashing */ struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 21"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 1, "INBOX", 0); node_create(tree1, 2, "foo", 0); /* swap INBOX and foo - the INBOX name is important since it's treated specially */ node_create(tree2, 1, "foo", 0); node_create(tree2, 2, "INBOX", 1); test_trees(tree1, tree2); test_end(); #endif } static void test_dsync_mailbox_tree_sync_renames22(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync renames 22"); tree1 = dsync_mailbox_tree_init('/', '_'); tree2 = dsync_mailbox_tree_init('/', '_'); node_create(tree1, 3, "p/a", 0); node_create(tree1, 0, "p/2", 0); node_create(tree1, 5, "p/2/h", 0); node_create(tree2, 4, "p/1/z", 0); node_create(tree2, 1, "p/2", 0); node_create(tree2, 2, "p/2/a", 0); node_create(tree2, 5, "p/2/y", 0); node_create(tree2, 3, "p/3", 0); test_trees(tree1, tree2); test_end(); } static void test_dsync_mailbox_tree_sync_random(void) { struct dsync_mailbox_tree *tree1, *tree2; test_begin("dsync mailbox tree sync random"); tree1 = create_random_tree(); tree2 = create_random_tree(); test_trees(tree1, tree2); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_dsync_mailbox_tree_sync_creates, test_dsync_mailbox_tree_sync_deletes, test_dsync_mailbox_tree_sync_renames1, test_dsync_mailbox_tree_sync_renames2, test_dsync_mailbox_tree_sync_renames3, test_dsync_mailbox_tree_sync_renames4, test_dsync_mailbox_tree_sync_renames5, test_dsync_mailbox_tree_sync_renames6, test_dsync_mailbox_tree_sync_renames7, test_dsync_mailbox_tree_sync_renames8, test_dsync_mailbox_tree_sync_renames9, test_dsync_mailbox_tree_sync_renames10, test_dsync_mailbox_tree_sync_renames11, test_dsync_mailbox_tree_sync_renames12, test_dsync_mailbox_tree_sync_renames13, test_dsync_mailbox_tree_sync_renames14, test_dsync_mailbox_tree_sync_renames15, test_dsync_mailbox_tree_sync_renames16, test_dsync_mailbox_tree_sync_renames17, test_dsync_mailbox_tree_sync_renames18, test_dsync_mailbox_tree_sync_renames19, test_dsync_mailbox_tree_sync_renames20, test_dsync_mailbox_tree_sync_renames21, test_dsync_mailbox_tree_sync_renames22, test_dsync_mailbox_tree_sync_random, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-tree-private.h0000644000175000017500000000232613123174404021204 00000000000000#ifndef DSYNC_MAILBOX_TREE_PRIVATE_H #define DSYNC_MAILBOX_TREE_PRIVATE_H #include "dsync-mailbox-tree.h" struct dsync_mailbox_tree { pool_t pool; char sep, sep_str[2], remote_sep, alt_char; /* root node isn't part of the real mailbox tree. its name is "" and it has no siblings */ struct dsync_mailbox_node root; unsigned int iter_count; ARRAY(struct dsync_mailbox_delete) deletes; /* guid_128_t => struct dsync_mailbox_node */ HASH_TABLE(uint8_t *, struct dsync_mailbox_node *) name128_hash; HASH_TABLE(uint8_t *, struct dsync_mailbox_node *) name128_remotesep_hash; HASH_TABLE(uint8_t *, struct dsync_mailbox_node *) guid_hash; }; void dsync_mailbox_tree_build_name128_hash(struct dsync_mailbox_tree *tree); int dsync_mailbox_node_name_cmp(struct dsync_mailbox_node *const *n1, struct dsync_mailbox_node *const *n2); void dsync_mailbox_tree_node_attach(struct dsync_mailbox_node *node, struct dsync_mailbox_node *parent); void dsync_mailbox_tree_node_detach(struct dsync_mailbox_node *node); struct dsync_mailbox_tree * dsync_mailbox_tree_dup(const struct dsync_mailbox_tree *src); bool dsync_mailbox_trees_equal(struct dsync_mailbox_tree *tree1, struct dsync_mailbox_tree *tree2); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-export.c0000644000175000017500000007255713165463624020141 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "istream.h" #include "mail-index-modseq.h" #include "mail-storage-private.h" #include "mail-search-build.h" #include "dsync-transaction-log-scan.h" #include "dsync-mail.h" #include "dsync-mailbox.h" #include "dsync-mailbox-export.h" struct dsync_mail_guid_instances { ARRAY_TYPE(seq_range) seqs; bool requested; bool searched; }; struct dsync_mailbox_exporter { pool_t pool; struct mailbox *box; struct dsync_transaction_log_scan *log_scan; uint32_t last_common_uid; struct mailbox_header_lookup_ctx *wanted_headers; struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; unsigned int search_pos, search_count; unsigned int hdr_hash_version; const char *const *hashed_headers; /* GUID => instances */ HASH_TABLE(char *, struct dsync_mail_guid_instances *) export_guids; ARRAY_TYPE(seq_range) requested_uids; ARRAY_TYPE(seq_range) search_uids; ARRAY_TYPE(seq_range) expunged_seqs; ARRAY_TYPE(const_string) expunged_guids; unsigned int expunged_guid_idx; /* uint32_t UID => struct dsync_mail_change */ HASH_TABLE(void *, struct dsync_mail_change *) changes; /* changes sorted by UID */ ARRAY(struct dsync_mail_change *) sorted_changes; unsigned int change_idx; uint32_t highest_changed_uid; struct mailbox_attribute_iter *attr_iter; struct hash_iterate_context *attr_change_iter; enum mail_attribute_type attr_type; struct dsync_mailbox_attribute attr; struct dsync_mail_change change; struct dsync_mail dsync_mail; const char *error; enum mail_error mail_error; unsigned int body_search_initialized:1; unsigned int auto_export_mails:1; unsigned int mails_have_guids:1; unsigned int minimal_dmail_fill:1; unsigned int return_all_mails:1; unsigned int export_received_timestamps:1; unsigned int export_virtual_sizes:1; unsigned int no_hdr_hashes:1; }; static int dsync_mail_error(struct dsync_mailbox_exporter *exporter, struct mail *mail, const char *field) { const char *errstr; enum mail_error error; errstr = mailbox_get_last_internal_error(exporter->box, &error); if (error == MAIL_ERROR_EXPUNGED) return 0; exporter->mail_error = error; exporter->error = p_strdup_printf(exporter->pool, "Can't lookup %s for UID=%u: %s", field, mail->uid, errstr); return -1; } static bool final_keyword_check(struct dsync_mail_change *change, const char *name, char *type_r) { const char *const *changes; unsigned int i, count; *type_r = KEYWORD_CHANGE_FINAL; changes = array_get(&change->keyword_changes, &count); for (i = 0; i < count; i++) { if (strcmp(changes[i]+1, name) != 0) continue; switch (changes[i][0]) { case KEYWORD_CHANGE_ADD: /* replace with ADD_AND_FINAL */ array_delete(&change->keyword_changes, i, 1); *type_r = KEYWORD_CHANGE_ADD_AND_FINAL; return FALSE; case KEYWORD_CHANGE_REMOVE: /* a final keyword is marked as removed. this shouldn't normally happen. */ array_delete(&change->keyword_changes, i, 1); return FALSE; case KEYWORD_CHANGE_ADD_AND_FINAL: case KEYWORD_CHANGE_FINAL: /* no change */ return TRUE; } } return FALSE; } static void search_update_flag_changes(struct dsync_mailbox_exporter *exporter, struct mail *mail, struct dsync_mail_change *change) { const char *const *keywords; unsigned int i; char type; i_assert((change->add_flags & change->remove_flags) == 0); change->modseq = mail_get_modseq(mail); change->pvt_modseq = mail_get_pvt_modseq(mail); change->final_flags = mail_get_flags(mail); keywords = mail_get_keywords(mail); if (!array_is_created(&change->keyword_changes) && keywords[0] != NULL) { p_array_init(&change->keyword_changes, exporter->pool, str_array_length(keywords)); } for (i = 0; keywords[i] != NULL; i++) { /* add the final keyword if it's not already there as +keyword */ if (!final_keyword_check(change, keywords[i], &type)) { const char *keyword_change = p_strdup_printf(exporter->pool, "%c%s", type, keywords[i]); array_append(&change->keyword_changes, &keyword_change, 1); } } } static int exporter_get_guids(struct dsync_mailbox_exporter *exporter, struct mail *mail, const char **guid_r, const char **hdr_hash_r) { *guid_r = ""; *hdr_hash_r = NULL; /* always try to get GUID, even if we're also getting header hash */ if (mail_get_special(mail, MAIL_FETCH_GUID, guid_r) < 0) return dsync_mail_error(exporter, mail, "GUID"); if (!exporter->mails_have_guids) { /* get header hash also */ if (exporter->no_hdr_hashes) { *hdr_hash_r = ""; return 1; } if (dsync_mail_get_hdr_hash(mail, exporter->hdr_hash_version, exporter->hashed_headers, hdr_hash_r) < 0) return dsync_mail_error(exporter, mail, "hdr-stream"); return 1; } else if (**guid_r == '\0') { exporter->mail_error = MAIL_ERROR_TEMP; exporter->error = "Backend doesn't support GUIDs, " "sync with header hashes instead"; return -1; } else { /* GUIDs are required, we don't need header hash */ return 1; } } static int search_update_flag_change_guid(struct dsync_mailbox_exporter *exporter, struct mail *mail) { struct dsync_mail_change *change, *log_change; const char *guid, *hdr_hash; int ret; change = hash_table_lookup(exporter->changes, POINTER_CAST(mail->uid)); if (change != NULL) { i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE); } else { i_assert(exporter->return_all_mails); change = p_new(exporter->pool, struct dsync_mail_change, 1); change->uid = mail->uid; change->type = DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE; hash_table_insert(exporter->changes, POINTER_CAST(mail->uid), change); } if ((ret = exporter_get_guids(exporter, mail, &guid, &hdr_hash)) < 0) return -1; if (ret == 0) { /* the message was expunged during export */ i_zero(change); change->type = DSYNC_MAIL_CHANGE_TYPE_EXPUNGE; change->uid = mail->uid; /* find its GUID from log if possible */ log_change = dsync_transaction_log_scan_find_new_expunge( exporter->log_scan, mail->uid); if (log_change != NULL) change->guid = log_change->guid; } else { change->guid = *guid == '\0' ? "" : p_strdup(exporter->pool, guid); change->hdr_hash = p_strdup(exporter->pool, hdr_hash); search_update_flag_changes(exporter, mail, change); } return 0; } static struct dsync_mail_change * export_save_change_get(struct dsync_mailbox_exporter *exporter, uint32_t uid) { struct dsync_mail_change *change; change = hash_table_lookup(exporter->changes, POINTER_CAST(uid)); if (change == NULL) { change = p_new(exporter->pool, struct dsync_mail_change, 1); change->uid = uid; hash_table_insert(exporter->changes, POINTER_CAST(uid), change); } else { /* move flag changes into a save. this happens only when last_common_uid isn't known */ i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE); i_assert(exporter->last_common_uid == 0); } change->type = DSYNC_MAIL_CHANGE_TYPE_SAVE; return change; } static void export_add_mail_instance(struct dsync_mailbox_exporter *exporter, struct dsync_mail_change *change, uint32_t seq) { struct dsync_mail_guid_instances *instances; if (exporter->auto_export_mails && !exporter->mails_have_guids) { /* GUIDs not supported, mail is requested by UIDs */ seq_range_array_add(&exporter->requested_uids, change->uid); return; } if (*change->guid == '\0') { /* mail UIDs are manually requested */ i_assert(!exporter->mails_have_guids); return; } instances = hash_table_lookup(exporter->export_guids, change->guid); if (instances == NULL) { instances = p_new(exporter->pool, struct dsync_mail_guid_instances, 1); p_array_init(&instances->seqs, exporter->pool, 2); hash_table_insert(exporter->export_guids, p_strdup(exporter->pool, change->guid), instances); if (exporter->auto_export_mails) instances->requested = TRUE; } seq_range_array_add(&instances->seqs, seq); } static int search_add_save(struct dsync_mailbox_exporter *exporter, struct mail *mail) { struct dsync_mail_change *change; const char *guid, *hdr_hash; enum mail_fetch_field wanted_fields = MAIL_FETCH_GUID; time_t received_timestamp = 0; uoff_t virtual_size = (uoff_t)-1; int ret; /* update wanted fields in case we didn't already set them for the search */ if (exporter->export_received_timestamps) wanted_fields |= MAIL_FETCH_RECEIVED_DATE; if (exporter->export_virtual_sizes) wanted_fields |= MAIL_FETCH_VIRTUAL_SIZE; mail_add_temp_wanted_fields(mail, wanted_fields, exporter->wanted_headers); /* If message is already expunged here, just skip it */ if ((ret = exporter_get_guids(exporter, mail, &guid, &hdr_hash)) <= 0) return ret; if (exporter->export_received_timestamps) { if (mail_get_received_date(mail, &received_timestamp) < 0) return dsync_mail_error(exporter, mail, "received-time"); if (received_timestamp == 0) { /* don't allow timestamps to be zero. we want to have asserts verify that the timestamp is set properly. */ received_timestamp = 1; } } if (exporter->export_virtual_sizes) { if (mail_get_virtual_size(mail, &virtual_size) < 0) return dsync_mail_error(exporter, mail, "virtual-size"); i_assert(virtual_size != (uoff_t)-1); } change = export_save_change_get(exporter, mail->uid); change->guid = *guid == '\0' ? "" : p_strdup(exporter->pool, guid); change->hdr_hash = p_strdup(exporter->pool, hdr_hash); change->received_timestamp = received_timestamp; change->virtual_size = virtual_size; search_update_flag_changes(exporter, mail, change); export_add_mail_instance(exporter, change, mail->seq); return 1; } static void dsync_mailbox_export_add_flagchange_uids(struct dsync_mailbox_exporter *exporter, ARRAY_TYPE(seq_range) *uids) { struct hash_iterate_context *iter; void *key; struct dsync_mail_change *change; iter = hash_table_iterate_init(exporter->changes); while (hash_table_iterate(iter, exporter->changes, &key, &change)) { if (change->type == DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE) seq_range_array_add(uids, change->uid); } hash_table_iterate_deinit(&iter); } static void dsync_mailbox_export_drop_expunged_flag_changes(struct dsync_mailbox_exporter *exporter) { struct hash_iterate_context *iter; void *key; struct dsync_mail_change *change; /* any flag changes for UIDs above last_common_uid weren't found by mailbox search, which means they were already expunged. for some reason the log scanner found flag changes for the message, but not the expunge. just remove these. */ iter = hash_table_iterate_init(exporter->changes); while (hash_table_iterate(iter, exporter->changes, &key, &change)) { if (change->type == DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE && change->uid > exporter->last_common_uid) hash_table_remove(exporter->changes, key); } hash_table_iterate_deinit(&iter); } static void dsync_mailbox_export_search(struct dsync_mailbox_exporter *exporter) { struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct mail_search_arg *sarg; struct mail *mail; enum mail_fetch_field wanted_fields = 0; struct mailbox_header_lookup_ctx *wanted_headers = NULL; int ret = 0; search_args = mail_search_build_init(); sarg = mail_search_build_add(search_args, SEARCH_UIDSET); p_array_init(&sarg->value.seqset, search_args->pool, 1); if (exporter->return_all_mails || exporter->last_common_uid == 0) { /* we want to know about all mails */ seq_range_array_add_range(&sarg->value.seqset, 1, (uint32_t)-1); } else { /* lookup GUIDs for messages with flag changes */ dsync_mailbox_export_add_flagchange_uids(exporter, &sarg->value.seqset); /* lookup new messages */ seq_range_array_add_range(&sarg->value.seqset, exporter->last_common_uid + 1, (uint32_t)-1); } if (exporter->last_common_uid == 0) { /* we're syncing all mails, so we can request the wanted fields for all the mails */ wanted_fields = MAIL_FETCH_GUID; wanted_headers = exporter->wanted_headers; } exporter->trans = mailbox_transaction_begin(exporter->box, MAILBOX_TRANSACTION_FLAG_SYNC); search_ctx = mailbox_search_init(exporter->trans, search_args, NULL, wanted_fields, wanted_headers); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { T_BEGIN { if (mail->uid <= exporter->last_common_uid) ret = search_update_flag_change_guid(exporter, mail); else ret = search_add_save(exporter, mail); } T_END; if (ret < 0) break; } i_assert(ret >= 0 || exporter->error != NULL); dsync_mailbox_export_drop_expunged_flag_changes(exporter); if (mailbox_search_deinit(&search_ctx) < 0 && exporter->error == NULL) { exporter->error = p_strdup_printf(exporter->pool, "Mail search failed: %s", mailbox_get_last_internal_error(exporter->box, &exporter->mail_error)); } } static int dsync_mail_change_p_uid_cmp(struct dsync_mail_change *const *c1, struct dsync_mail_change *const *c2) { if ((*c1)->uid < (*c2)->uid) return -1; if ((*c1)->uid > (*c2)->uid) return 1; return 0; } static void dsync_mailbox_export_sort_changes(struct dsync_mailbox_exporter *exporter) { struct hash_iterate_context *iter; void *key; struct dsync_mail_change *change; p_array_init(&exporter->sorted_changes, exporter->pool, hash_table_count(exporter->changes)); iter = hash_table_iterate_init(exporter->changes); while (hash_table_iterate(iter, exporter->changes, &key, &change)) array_append(&exporter->sorted_changes, &change, 1); hash_table_iterate_deinit(&iter); array_sort(&exporter->sorted_changes, dsync_mail_change_p_uid_cmp); } static void dsync_mailbox_export_attr_init(struct dsync_mailbox_exporter *exporter, enum mail_attribute_type type) { exporter->attr_iter = mailbox_attribute_iter_init(exporter->box, type, ""); exporter->attr_type = type; } static void dsync_mailbox_export_log_scan(struct dsync_mailbox_exporter *exporter, struct dsync_transaction_log_scan *log_scan) { HASH_TABLE_TYPE(dsync_uid_mail_change) log_changes; struct hash_iterate_context *iter; void *key; struct dsync_mail_change *change, *dup_change; log_changes = dsync_transaction_log_scan_get_hash(log_scan); if (dsync_transaction_log_scan_has_all_changes(log_scan)) { /* we tried to access too old/invalid modseqs. to make sure no changes get lost, we need to send all of the messages */ exporter->return_all_mails = TRUE; } /* clone the hash table, since we're changing it. */ hash_table_create_direct(&exporter->changes, exporter->pool, hash_table_count(log_changes)); iter = hash_table_iterate_init(log_changes); while (hash_table_iterate(iter, log_changes, &key, &change)) { dup_change = p_new(exporter->pool, struct dsync_mail_change, 1); *dup_change = *change; hash_table_insert(exporter->changes, key, dup_change); if (exporter->highest_changed_uid < change->uid) exporter->highest_changed_uid = change->uid; } hash_table_iterate_deinit(&iter); } struct dsync_mailbox_exporter * dsync_mailbox_export_init(struct mailbox *box, struct dsync_transaction_log_scan *log_scan, uint32_t last_common_uid, enum dsync_mailbox_exporter_flags flags, unsigned int hdr_hash_version, const char *const *hashed_headers) { struct dsync_mailbox_exporter *exporter; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"dsync mailbox export", 4096); exporter = p_new(pool, struct dsync_mailbox_exporter, 1); exporter->pool = pool; exporter->box = box; exporter->log_scan = log_scan; exporter->last_common_uid = last_common_uid; exporter->auto_export_mails = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_AUTO_EXPORT_MAILS) != 0; exporter->mails_have_guids = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_MAILS_HAVE_GUIDS) != 0; exporter->minimal_dmail_fill = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_MINIMAL_DMAIL_FILL) != 0; exporter->export_received_timestamps = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_TIMESTAMPS) != 0; exporter->export_virtual_sizes = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_VSIZES) != 0; exporter->hdr_hash_version = hdr_hash_version; exporter->no_hdr_hashes = (flags & DSYNC_MAILBOX_EXPORTER_FLAG_NO_HDR_HASHES) != 0; exporter->hashed_headers = hashed_headers; p_array_init(&exporter->requested_uids, pool, 16); p_array_init(&exporter->search_uids, pool, 16); hash_table_create(&exporter->export_guids, pool, 0, str_hash, strcmp); p_array_init(&exporter->expunged_seqs, pool, 16); p_array_init(&exporter->expunged_guids, pool, 16); if (!exporter->mails_have_guids && !exporter->no_hdr_hashes) exporter->wanted_headers = dsync_mail_get_hash_headers(box, exporter->hashed_headers); /* first scan transaction log and save any expunges and flag changes */ dsync_mailbox_export_log_scan(exporter, log_scan); /* get saves and also find GUIDs for flag changes */ dsync_mailbox_export_search(exporter); /* get the changes sorted by UID */ dsync_mailbox_export_sort_changes(exporter); dsync_mailbox_export_attr_init(exporter, MAIL_ATTRIBUTE_TYPE_PRIVATE); return exporter; } static int dsync_mailbox_export_iter_next_nonexistent_attr(struct dsync_mailbox_exporter *exporter) { struct dsync_mailbox_attribute *attr; struct mail_attribute_value value; while (hash_table_iterate(exporter->attr_change_iter, dsync_transaction_log_scan_get_attr_hash(exporter->log_scan), &attr, &attr)) { if (attr->exported || !attr->deleted) continue; /* lookup the value mainly to get its last_change value. */ if (mailbox_attribute_get_stream(exporter->trans, attr->type, attr->key, &value) < 0) { exporter->error = p_strdup_printf(exporter->pool, "Mailbox attribute %s lookup failed: %s", attr->key, mailbox_get_last_internal_error(exporter->box, &exporter->mail_error)); break; } if ((value.flags & MAIL_ATTRIBUTE_VALUE_FLAG_READONLY) != 0) { if (value.value_stream != NULL) i_stream_unref(&value.value_stream); continue; } attr->last_change = value.last_change; if (value.value != NULL || value.value_stream != NULL) { attr->value = p_strdup(exporter->pool, value.value); attr->value_stream = value.value_stream; attr->deleted = FALSE; } attr->exported = TRUE; exporter->attr = *attr; return 1; } hash_table_iterate_deinit(&exporter->attr_change_iter); return 0; } static int dsync_mailbox_export_iter_next_attr(struct dsync_mailbox_exporter *exporter) { HASH_TABLE_TYPE(dsync_attr_change) attr_changes; struct dsync_mailbox_attribute lookup_attr, *attr; struct dsync_mailbox_attribute *attr_change; const char *key; struct mail_attribute_value value; bool export_all_attrs; export_all_attrs = exporter->return_all_mails || exporter->last_common_uid == 0; attr_changes = dsync_transaction_log_scan_get_attr_hash(exporter->log_scan); lookup_attr.type = exporter->attr_type; /* note that the order of processing may be important for some attributes. for example sieve can't set a script active until it's first been created */ while ((key = mailbox_attribute_iter_next(exporter->attr_iter)) != NULL) { lookup_attr.key = key; attr_change = hash_table_lookup(attr_changes, &lookup_attr); if (attr_change == NULL && !export_all_attrs) continue; if (mailbox_attribute_get_stream(exporter->trans, exporter->attr_type, key, &value) < 0) { exporter->error = p_strdup_printf(exporter->pool, "Mailbox attribute %s lookup failed: %s", key, mailbox_get_last_internal_error(exporter->box, &exporter->mail_error)); return -1; } if ((value.flags & MAIL_ATTRIBUTE_VALUE_FLAG_READONLY) != 0) { /* readonly attributes can't be changed, no point in exporting them */ if (value.value_stream != NULL) i_stream_unref(&value.value_stream); continue; } if (value.value == NULL && value.value_stream == NULL && (attr_change == NULL || !attr_change->deleted)) { /* the attribute was just deleted? skip for this sync. */ continue; } if (attr_change != NULL && attr_change->exported) { /* duplicate attribute returned. shouldn't normally happen, but don't crash. */ i_warning("Ignoring duplicate attributes '%s'", key); continue; } attr = &exporter->attr; i_zero(attr); attr->type = exporter->attr_type; attr->value = p_strdup(exporter->pool, value.value); attr->value_stream = value.value_stream; attr->last_change = value.last_change; if (attr_change != NULL) { attr_change->exported = TRUE; attr->key = attr_change->key; attr->deleted = attr_change->deleted && !DSYNC_ATTR_HAS_VALUE(attr); attr->modseq = attr_change->modseq; } else { attr->key = p_strdup(exporter->pool, key); } return 1; } if (mailbox_attribute_iter_deinit(&exporter->attr_iter) < 0) { exporter->error = p_strdup_printf(exporter->pool, "Mailbox attribute iteration failed: %s", mailbox_get_last_internal_error(exporter->box, &exporter->mail_error)); return -1; } if (exporter->attr_type == MAIL_ATTRIBUTE_TYPE_PRIVATE) { /* export shared attributes */ dsync_mailbox_export_attr_init(exporter, MAIL_ATTRIBUTE_TYPE_SHARED); return dsync_mailbox_export_iter_next_attr(exporter); } exporter->attr_change_iter = hash_table_iterate_init(attr_changes); return dsync_mailbox_export_iter_next_nonexistent_attr(exporter); } int dsync_mailbox_export_next_attr(struct dsync_mailbox_exporter *exporter, const struct dsync_mailbox_attribute **attr_r) { int ret; if (exporter->error != NULL) return -1; if (exporter->attr.value_stream != NULL) i_stream_unref(&exporter->attr.value_stream); if (exporter->attr_iter != NULL) { ret = dsync_mailbox_export_iter_next_attr(exporter); } else { ret = dsync_mailbox_export_iter_next_nonexistent_attr(exporter); } if (ret > 0) *attr_r = &exporter->attr; return ret; } int dsync_mailbox_export_next(struct dsync_mailbox_exporter *exporter, const struct dsync_mail_change **change_r) { struct dsync_mail_change *const *changes; unsigned int count; if (exporter->error != NULL) return -1; changes = array_get(&exporter->sorted_changes, &count); if (exporter->change_idx == count) return 0; *change_r = changes[exporter->change_idx++]; return 1; } static int dsync_mailbox_export_body_search_init(struct dsync_mailbox_exporter *exporter) { struct mail_search_args *search_args; struct mail_search_arg *sarg; struct hash_iterate_context *iter; const struct seq_range *uids; char *guid; const char *const_guid; enum mail_fetch_field wanted_fields; struct dsync_mail_guid_instances *instances; const struct seq_range *range; unsigned int i, count; uint32_t seq, seq1, seq2; i_assert(exporter->search_ctx == NULL); search_args = mail_search_build_init(); sarg = mail_search_build_add(search_args, SEARCH_SEQSET); p_array_init(&sarg->value.seqset, search_args->pool, 128); /* get a list of messages we want to fetch. if there are more than one instance for a GUID, use the first one. */ iter = hash_table_iterate_init(exporter->export_guids); while (hash_table_iterate(iter, exporter->export_guids, &guid, &instances)) { if (!instances->requested || array_count(&instances->seqs) == 0) continue; uids = array_idx(&instances->seqs, 0); seq = uids[0].seq1; if (!instances->searched) { instances->searched = TRUE; seq_range_array_add(&sarg->value.seqset, seq); } else if (seq_range_exists(&exporter->expunged_seqs, seq)) { /* we're on a second round, refetching expunged messages */ seq_range_array_remove(&instances->seqs, seq); seq_range_array_remove(&exporter->expunged_seqs, seq); if (array_count(&instances->seqs) == 0) { /* no instances left */ const_guid = guid; array_append(&exporter->expunged_guids, &const_guid, 1); continue; } uids = array_idx(&instances->seqs, 0); seq = uids[0].seq1; seq_range_array_add(&sarg->value.seqset, seq); } } hash_table_iterate_deinit(&iter); /* add requested UIDs */ range = array_get(&exporter->requested_uids, &count); for (i = 0; i < count; i++) { mailbox_get_seq_range(exporter->box, range[i].seq1, range[i].seq2, &seq1, &seq2); seq_range_array_add_range(&sarg->value.seqset, seq1, seq2); } array_clear(&exporter->search_uids); array_append_array(&exporter->search_uids, &exporter->requested_uids); array_clear(&exporter->requested_uids); wanted_fields = MAIL_FETCH_GUID | MAIL_FETCH_SAVE_DATE; if (!exporter->minimal_dmail_fill) { wanted_fields |= MAIL_FETCH_RECEIVED_DATE | MAIL_FETCH_UIDL_BACKEND | MAIL_FETCH_POP3_ORDER | MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY; } exporter->search_count += seq_range_count(&sarg->value.seqset); exporter->search_ctx = mailbox_search_init(exporter->trans, search_args, NULL, wanted_fields, NULL); mail_search_args_unref(&search_args); return array_count(&sarg->value.seqset) > 0 ? 1 : 0; } static void dsync_mailbox_export_body_search_deinit(struct dsync_mailbox_exporter *exporter) { if (exporter->search_ctx == NULL) return; if (mailbox_search_deinit(&exporter->search_ctx) < 0 && exporter->error == NULL) { exporter->error = p_strdup_printf(exporter->pool, "Mail search failed: %s", mailbox_get_last_internal_error(exporter->box, &exporter->mail_error)); } } static int dsync_mailbox_export_mail(struct dsync_mailbox_exporter *exporter, struct mail *mail) { struct dsync_mail_guid_instances *instances; const char *error_field; if (dsync_mail_fill(mail, exporter->minimal_dmail_fill, &exporter->dsync_mail, &error_field) < 0) return dsync_mail_error(exporter, mail, error_field); instances = *exporter->dsync_mail.guid == '\0' ? NULL : hash_table_lookup(exporter->export_guids, exporter->dsync_mail.guid); if (instances != NULL) { /* GUID found */ } else if (exporter->dsync_mail.uid != 0) { /* mail requested by UID */ } else { exporter->mail_error = MAIL_ERROR_TEMP; exporter->error = p_strdup_printf(exporter->pool, "GUID unexpectedly changed for UID=%u GUID=%s", mail->uid, exporter->dsync_mail.guid); return -1; } if (!seq_range_exists(&exporter->search_uids, mail->uid)) exporter->dsync_mail.uid = 0; else exporter->dsync_mail.guid = ""; /* this message was successfully returned, don't try retrying it */ if (instances != NULL) array_clear(&instances->seqs); return 1; } void dsync_mailbox_export_want_mail(struct dsync_mailbox_exporter *exporter, const struct dsync_mail_request *request) { struct dsync_mail_guid_instances *instances; i_assert(!exporter->auto_export_mails); if (request->guid == NULL) { i_assert(request->uid > 0); seq_range_array_add(&exporter->requested_uids, request->uid); return; } instances = hash_table_lookup(exporter->export_guids, request->guid); if (instances == NULL) { exporter->mail_error = MAIL_ERROR_TEMP; exporter->error = p_strdup_printf(exporter->pool, "Remote requested unexpected GUID %s", request->guid); return; } instances->requested = TRUE; } int dsync_mailbox_export_next_mail(struct dsync_mailbox_exporter *exporter, const struct dsync_mail **mail_r) { struct mail *mail; const char *const *guids; unsigned int count; int ret; if (exporter->error != NULL) return -1; if (!exporter->body_search_initialized) { exporter->body_search_initialized = TRUE; if (dsync_mailbox_export_body_search_init(exporter) < 0) { i_assert(exporter->error != NULL); return -1; } } while (mailbox_search_next(exporter->search_ctx, &mail)) { exporter->search_pos++; if ((ret = dsync_mailbox_export_mail(exporter, mail)) > 0) { *mail_r = &exporter->dsync_mail; return 1; } if (ret < 0) { i_assert(exporter->error != NULL); return -1; } /* the message was expunged. if the GUID has another instance, try sending it later. */ seq_range_array_add(&exporter->expunged_seqs, mail->seq); } /* if some instances of messages were expunged, retry fetching them with other instances */ dsync_mailbox_export_body_search_deinit(exporter); if ((ret = dsync_mailbox_export_body_search_init(exporter)) < 0) { i_assert(exporter->error != NULL); return -1; } if (ret > 0) { /* not finished yet */ return dsync_mailbox_export_next_mail(exporter, mail_r); } /* finished with messages. if there are any expunged messages, return them */ guids = array_get(&exporter->expunged_guids, &count); if (exporter->expunged_guid_idx < count) { i_zero(&exporter->dsync_mail); exporter->dsync_mail.guid = guids[exporter->expunged_guid_idx++]; *mail_r = &exporter->dsync_mail; return 1; } return 0; } int dsync_mailbox_export_deinit(struct dsync_mailbox_exporter **_exporter, const char **errstr_r, enum mail_error *error_r) { struct dsync_mailbox_exporter *exporter = *_exporter; *_exporter = NULL; if (exporter->attr_iter != NULL) (void)mailbox_attribute_iter_deinit(&exporter->attr_iter); dsync_mailbox_export_body_search_deinit(exporter); (void)mailbox_transaction_commit(&exporter->trans); if (exporter->wanted_headers != NULL) mailbox_header_lookup_unref(&exporter->wanted_headers); if (exporter->attr.value_stream != NULL) i_stream_unref(&exporter->attr.value_stream); hash_table_destroy(&exporter->export_guids); hash_table_destroy(&exporter->changes); i_assert((exporter->error != NULL) == (exporter->mail_error != 0)); *error_r = exporter->mail_error; *errstr_r = t_strdup(exporter->error); pool_unref(&exporter->pool); return *errstr_r != NULL ? -1 : 0; } const char *dsync_mailbox_export_get_proctitle(struct dsync_mailbox_exporter *exporter) { if (exporter->search_ctx == NULL) return ""; return t_strdup_printf("%u/%u", exporter->search_pos, exporter->search_count); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-tree-sync.c0000644000175000017500000013714413123174404020510 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "buffer.h" #include "str.h" #include "md5.h" #include "hex-binary.h" #include "aqueue.h" #include "hash.h" #include "dsync-brain-private.h" #include "dsync-mailbox-tree-private.h" #define TEMP_MAX_NAME_LEN 100 #define TEMP_SUFFIX_MAX_LEN (sizeof("temp-")-1 + 8) #define TEMP_SUFFIX_FORMAT "temp-%x" struct dsync_mailbox_tree_bfs_iter { struct dsync_mailbox_tree *tree; ARRAY(struct dsync_mailbox_node *) queue_arr; struct aqueue *queue; struct dsync_mailbox_node *cur; }; struct dsync_mailbox_tree_sync_ctx { pool_t pool; struct dsync_mailbox_tree *local_tree, *remote_tree; enum dsync_mailbox_trees_sync_type sync_type; enum dsync_mailbox_trees_sync_flags sync_flags; unsigned int combined_mailboxes_count; ARRAY(struct dsync_mailbox_tree_sync_change) changes; unsigned int change_idx; bool failed; }; static struct dsync_mailbox_tree_bfs_iter * dsync_mailbox_tree_bfs_iter_init(struct dsync_mailbox_tree *tree) { struct dsync_mailbox_tree_bfs_iter *iter; iter = i_new(struct dsync_mailbox_tree_bfs_iter, 1); iter->tree = tree; i_array_init(&iter->queue_arr, 32); iter->queue = aqueue_init(&iter->queue_arr.arr); iter->cur = tree->root.first_child; return iter; } static bool dsync_mailbox_tree_bfs_iter_next(struct dsync_mailbox_tree_bfs_iter *iter, struct dsync_mailbox_node **node_r) { struct dsync_mailbox_node *const *nodep; if (iter->cur == NULL) { if (aqueue_count(iter->queue) == 0) return FALSE; nodep = array_idx(&iter->queue_arr, aqueue_idx(iter->queue, 0)); iter->cur = *nodep; aqueue_delete_tail(iter->queue); } *node_r = iter->cur; if (iter->cur->first_child != NULL) aqueue_append(iter->queue, &iter->cur->first_child); iter->cur = iter->cur->next; return TRUE; } static void dsync_mailbox_tree_bfs_iter_deinit(struct dsync_mailbox_tree_bfs_iter **_iter) { struct dsync_mailbox_tree_bfs_iter *iter = *_iter; *_iter = NULL; aqueue_deinit(&iter->queue); array_free(&iter->queue_arr); i_free(iter); } static void sync_add_dir_change(struct dsync_mailbox_tree_sync_ctx *ctx, const struct dsync_mailbox_node *node, enum dsync_mailbox_tree_sync_type type) { struct dsync_mailbox_tree_sync_change *change; const char *name; i_assert(ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL); name = dsync_mailbox_node_get_full_name(ctx->local_tree, node); change = array_append_space(&ctx->changes); change->type = type; change->ns = node->ns; change->full_name = p_strdup(ctx->pool, name); } static void sync_add_create_change(struct dsync_mailbox_tree_sync_ctx *ctx, const struct dsync_mailbox_node *node, const char *name) { struct dsync_mailbox_tree_sync_change *change; i_assert(ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL); change = array_append_space(&ctx->changes); change->type = DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX; change->ns = node->ns; change->full_name = p_strdup(ctx->pool, name); memcpy(change->mailbox_guid, node->mailbox_guid, sizeof(change->mailbox_guid)); change->uid_validity = node->uid_validity; } static void sort_siblings(ARRAY_TYPE(dsync_mailbox_node) *siblings) { struct dsync_mailbox_node *const *nodes; unsigned int i, count; array_sort(siblings, dsync_mailbox_node_name_cmp); nodes = array_get(siblings, &count); if (count == 0) return; nodes[0]->parent->first_child = nodes[0]; for (i = 1; i < count; i++) nodes[i-1]->next = nodes[i]; nodes[count-1]->next = NULL; } static void sync_set_node_deleted(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node) { const uint8_t *guid_p; /* for the rest of this sync assume that the mailbox has already been deleted */ guid_p = node->mailbox_guid; hash_table_remove(tree->guid_hash, guid_p); node->existence = DSYNC_MAILBOX_NODE_DELETED; memset(node->mailbox_guid, 0, sizeof(node->mailbox_guid)); node->uid_validity = 0; } static void sync_delete_mailbox_node(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, const char *reason) { struct dsync_mailbox_tree_sync_change *change; const char *name; if ((ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG) != 0 && tree == ctx->local_tree) { i_debug("brain %c: Deleting mailbox '%s' (GUID %s): %s", (ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN) != 0 ? 'M' : 'S', dsync_mailbox_node_get_full_name(tree, node), guid_128_to_string(node->mailbox_guid), reason); } if (tree == ctx->local_tree && node->existence != DSYNC_MAILBOX_NODE_DELETED) { /* delete this mailbox locally */ i_assert(ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL); change = array_append_space(&ctx->changes); change->type = DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX; change->ns = node->ns; name = dsync_mailbox_node_get_full_name(tree, node); change->full_name = p_strdup(ctx->pool, name); memcpy(change->mailbox_guid, node->mailbox_guid, sizeof(change->mailbox_guid)); } sync_set_node_deleted(tree, node); } static void sync_delete_mailbox(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, const char *reason) { struct dsync_mailbox_tree *other_tree; struct dsync_mailbox_node *other_node; const uint8_t *guid_p; other_tree = tree == ctx->local_tree ? ctx->remote_tree : ctx->local_tree; guid_p = node->mailbox_guid; other_node = hash_table_lookup(other_tree->guid_hash, guid_p); if (other_node == NULL) { /* doesn't exist / already deleted */ } else { sync_delete_mailbox_node(ctx, other_tree, other_node, reason); } sync_delete_mailbox_node(ctx, tree, node, reason); } static void sync_tree_sort_and_delete_mailboxes(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, bool twoway_sync) { struct dsync_mailbox_tree_bfs_iter *iter; struct dsync_mailbox_node *node, *parent = NULL; ARRAY_TYPE(dsync_mailbox_node) siblings; t_array_init(&siblings, 64); iter = dsync_mailbox_tree_bfs_iter_init(tree); while (dsync_mailbox_tree_bfs_iter_next(iter, &node)) { if (node->parent != parent) { sort_siblings(&siblings); array_clear(&siblings); parent = node->parent; } if (node->existence == DSYNC_MAILBOX_NODE_DELETED && !dsync_mailbox_node_is_dir(node)) { if (twoway_sync) { /* this mailbox was deleted. delete it from the other side as well */ sync_delete_mailbox(ctx, tree, node, "Mailbox has been deleted"); } else { /* treat the node as if it didn't exist. it'll get either recreated or deleted later. in any case this function must handle all existence=DELETED mailbox nodes by changing them into directories (setting GUID=0) or we'll assert-crash later */ sync_set_node_deleted(tree, node); } } ctx->combined_mailboxes_count++; array_append(&siblings, &node, 1); } sort_siblings(&siblings); dsync_mailbox_tree_bfs_iter_deinit(&iter); } static bool node_names_equal(const struct dsync_mailbox_node *n1, const struct dsync_mailbox_node *n2) { while (n1 != NULL && n2 != NULL) { if (strcmp(n1->name, n2->name) != 0) return FALSE; n1 = n1->parent; n2 = n2->parent; } return n1 == NULL && n2 == NULL; } static void dsync_mailbox_tree_node_attach_sorted(struct dsync_mailbox_node *node, struct dsync_mailbox_node *parent) { struct dsync_mailbox_node **p; node->parent = parent; for (p = &parent->first_child; *p != NULL; p = &(*p)->next) { if (dsync_mailbox_node_name_cmp(p, &node) > 0) break; } node->next = *p; *p = node; } static void dsync_mailbox_tree_node_move_sorted(struct dsync_mailbox_node *node, struct dsync_mailbox_node *parent) { /* detach from old parent */ dsync_mailbox_tree_node_detach(node); /* attach to new parent */ dsync_mailbox_tree_node_attach_sorted(node, parent); } static struct dsync_mailbox_node * sorted_tree_get(struct dsync_mailbox_tree *tree, const char *name) { struct dsync_mailbox_node *node, *parent, *ret; node = ret = dsync_mailbox_tree_get(tree, name); while (node->parent != NULL && node->existence == DSYNC_MAILBOX_NODE_NONEXISTENT) { parent = node->parent; dsync_mailbox_tree_node_detach(node); dsync_mailbox_tree_node_attach_sorted(node, parent); node = parent; } return ret; } static struct dsync_mailbox_node * sync_node_new(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node **pos, struct dsync_mailbox_node *parent, const struct dsync_mailbox_node *src) { struct dsync_mailbox_node *node; node = p_new(tree->pool, struct dsync_mailbox_node, 1); node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; node->name = p_strdup(tree->pool, src->name); node->sync_temporary_name = src->sync_temporary_name; node->ns = src->ns; node->parent = parent; node->next = *pos; *pos = node; return node; } static struct dsync_mailbox_node * sorted_tree_get_by_node_name(struct dsync_mailbox_tree *tree, struct dsync_mailbox_tree *other_tree, struct dsync_mailbox_node *other_node) { const char *parent_name; if (other_node == &other_tree->root) return &tree->root; parent_name = dsync_mailbox_node_get_full_name(other_tree, other_node); return sorted_tree_get(tree, parent_name); } static bool node_has_child(struct dsync_mailbox_node *parent, const char *name) { struct dsync_mailbox_node *node; for (node = parent->first_child; node != NULL; node = node->next) { if (strcmp(node->name, name) == 0) return TRUE; } return FALSE; } static bool node_has_existent_children(struct dsync_mailbox_node *node, bool dirs_ok) { for (node = node->first_child; node != NULL; node = node->next) { if (node->existence == DSYNC_MAILBOX_NODE_EXISTS && (dirs_ok || !dsync_mailbox_node_is_dir(node))) return TRUE; if (node_has_existent_children(node, dirs_ok)) return TRUE; } return FALSE; } static bool node_is_existent(struct dsync_mailbox_node *node) { if (node->existence == DSYNC_MAILBOX_NODE_EXISTS) return TRUE; return node_has_existent_children(node, TRUE); } static bool sync_node_is_namespace_prefix(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node) { const char *full_name; size_t prefix_len = node->ns == NULL ? 0 : node->ns->prefix_len; if (strcmp(node->name, "INBOX") == 0 && node->parent == &tree->root) return TRUE; if (prefix_len == 0) return FALSE; full_name = dsync_mailbox_node_get_full_name(tree, node); if (node->ns->prefix[prefix_len-1] == mail_namespace_get_sep(node->ns)) prefix_len--; return strncmp(full_name, node->ns->prefix, prefix_len) == 0 && full_name[prefix_len] == '\0'; } static void sync_rename_node_to_temp(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, struct dsync_mailbox_node *new_parent, const char **reason_r) { struct dsync_mailbox_tree_sync_change *change; const char *old_name, *new_name, *p; char name[TEMP_MAX_NAME_LEN+1]; buffer_t buf; size_t prefix_len, max_prefix_len; unsigned int counter = 1; i_assert(!sync_node_is_namespace_prefix(tree, node)); buffer_create_from_data(&buf, name, sizeof(name)); max_prefix_len = TEMP_MAX_NAME_LEN - TEMP_SUFFIX_MAX_LEN - 1; if (node->sync_temporary_name) { /* the source name was also a temporary name. drop the - from it */ p = strrchr(node->name, '-'); i_assert(p != NULL); if (max_prefix_len > (size_t)(p - node->name)) max_prefix_len = p - node->name; } str_append_n(&buf, node->name, max_prefix_len); str_append_c(&buf, '-'); prefix_len = buf.used; do { str_truncate(&buf, prefix_len); str_printfa(&buf, TEMP_SUFFIX_FORMAT, counter++); /* the generated name is quite unlikely to exist, but check anyway.. */ } while (node_has_child(node->parent, str_c(&buf))); old_name = tree != ctx->local_tree ? NULL : dsync_mailbox_node_get_full_name(tree, node); *reason_r = t_strdup_printf("Renamed '%s' to '%s'", node->name, str_c(&buf)); node->name = p_strdup(tree->pool, str_c(&buf)); node->sync_temporary_name = TRUE; node->last_renamed_or_created = 0; dsync_mailbox_tree_node_move_sorted(node, new_parent); if (tree == ctx->local_tree && node_is_existent(node)) { /* we're modifying a local tree. remember this change. */ new_name = dsync_mailbox_node_get_full_name(tree, node); i_assert(ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL); i_assert(strcmp(old_name, "INBOX") != 0); change = array_append_space(&ctx->changes); change->type = DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME; change->ns = node->ns; change->full_name = p_strdup(ctx->pool, old_name); change->rename_dest_name = p_strdup(ctx->pool, new_name); } } static bool node_has_parent(struct dsync_mailbox_node *node, struct dsync_mailbox_node *parent) { for (; node != NULL; node = node->parent) { if (node == parent) return TRUE; } return FALSE; } static void sync_rename_node(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *temp_node, struct dsync_mailbox_node *node, const struct dsync_mailbox_node *other_node, const char **reason_r) { struct dsync_mailbox_tree_sync_change *change; struct dsync_mailbox_tree *other_tree; struct dsync_mailbox_node *parent; const char *name, *other_name; i_assert(node != NULL); i_assert(other_node != NULL); /* move/rename node in the tree, so that its position/name is identical to other_node (in other_tree). temp_node's name is changed to temporary name (i.e. it assumes that node's name becomes temp_node's original name) */ other_tree = tree == ctx->local_tree ? ctx->remote_tree : ctx->local_tree; parent = sorted_tree_get_by_node_name(tree, other_tree, other_node->parent); if (node_has_parent(parent, node)) { /* don't introduce a loop. temporarily rename node under root. */ sync_rename_node_to_temp(ctx, tree, node, &tree->root, reason_r); *reason_r = t_strconcat(*reason_r, " (Don't introduce loop)", NULL); return; } sync_rename_node_to_temp(ctx, tree, temp_node, temp_node->parent, reason_r); /* get the old name before it's modified */ name = dsync_mailbox_node_get_full_name(tree, node); /* set the new name */ *reason_r = t_strdup_printf("%s + Renamed '%s' to '%s'", *reason_r, name, other_node->name); node->name = p_strdup(tree->pool, other_node->name); node->sync_temporary_name = other_node->sync_temporary_name; node->last_renamed_or_created = other_node->last_renamed_or_created; /* change node's parent if necessary. in any case detach+reattach it sorted, because the nodes must be sorted by name, and the node's name (or its parent) changed. */ dsync_mailbox_tree_node_move_sorted(node, parent); if (tree == ctx->local_tree && node_is_existent(node)) { /* we're modifying a local tree. remember this change. */ other_name = dsync_mailbox_node_get_full_name(other_tree, other_node); i_assert(ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL); i_assert(strcmp(name, "INBOX") != 0); change = array_append_space(&ctx->changes); change->type = DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME; change->ns = node->ns; change->full_name = p_strdup(ctx->pool, name); change->rename_dest_name = p_strdup(ctx->pool, other_name); } } static int node_mailbox_guids_cmp(struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { int ret; while (node1 != NULL && node2 != NULL) { if (node1->existence == DSYNC_MAILBOX_NODE_EXISTS && node2->existence != DSYNC_MAILBOX_NODE_EXISTS) return -1; if (node2->existence == DSYNC_MAILBOX_NODE_EXISTS && node1->existence != DSYNC_MAILBOX_NODE_EXISTS) return 1; ret = memcmp(node1->mailbox_guid, node2->mailbox_guid, sizeof(node1->mailbox_guid)); if (ret != 0) return ret; ret = node_mailbox_guids_cmp(node1->first_child, node2->first_child); if (ret != 0) return ret; node1 = node1->next; node2 = node2->next; } if (node1 == NULL && node2 == NULL) return 0; return node1 != NULL ? -1 : 1; } static int node_mailbox_names_cmp(struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { int ret; while (node1 != NULL && node2 != NULL) { ret = strcmp(node1->name, node2->name); if (ret != 0) return ret; ret = node_mailbox_names_cmp(node1->first_child, node2->first_child); if (ret != 0) return ret; node1 = node1->next; node2 = node2->next; } if (node1 == NULL && node2 == NULL) return 0; return node1 != NULL ? -1 : 1; } static int node_mailbox_trees_cmp(struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { int ret; ret = node_mailbox_guids_cmp(node1, node2); if (ret == 0) { /* only a directory name changed and all the timestamps are equal. just pick the alphabetically smaller. */ ret = node_mailbox_names_cmp(node1, node2); } i_assert(ret != 0); return ret; } static time_t nodes_get_timestamp(struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { time_t ts = 0; /* avoid using temporary names in case all the timestamps are 0 */ if (node1 != NULL && !node1->sync_temporary_name) ts = node1->last_renamed_or_created + 1; if (node2 != NULL && !node2->sync_temporary_name && ts <= node2->last_renamed_or_created) ts = node2->last_renamed_or_created + 1; return ts; } static bool sync_node_is_namespace_root(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node) { if (node == NULL) return FALSE; if (node == &tree->root) return TRUE; return sync_node_is_namespace_prefix(tree, node); } static bool ATTR_NULL(3, 4) sync_rename_lower_ts(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_node1, struct dsync_mailbox_node *remote_node1, struct dsync_mailbox_node *local_node2, struct dsync_mailbox_node *remote_node2, const char **reason_r) { time_t local_ts, remote_ts; /* We're scanning the tree at the position of local_node1 and remote_node2. They have identical names. We also know that local_node1&remote_node1 and local_node2&remote_node2 are "the same" either because their GUIDs or (in case of one being a directory) their childrens' GUIDs match. We don't know where local_node2 or remote_node1 are located in the mailbox tree, or if they exist at all. Note that node1 and node2 may be the same node pointers. */ i_assert(strcmp(local_node1->name, remote_node2->name) == 0); if (sync_node_is_namespace_root(ctx->remote_tree, remote_node1) || sync_node_is_namespace_root(ctx->remote_tree, remote_node2) || sync_node_is_namespace_root(ctx->local_tree, local_node1) || sync_node_is_namespace_root(ctx->local_tree, local_node2)) { local_node1->sync_delayed_guid_change = TRUE; remote_node2->sync_delayed_guid_change = TRUE; *reason_r = "Can't rename namespace prefixes - will be merged later"; return FALSE; } local_ts = nodes_get_timestamp(local_node1, local_node2); remote_ts = nodes_get_timestamp(remote_node1, remote_node2); if (ctx->sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) local_ts = remote_ts+1; else if (ctx->sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) remote_ts = local_ts+1; /* The algorithm must be deterministic regardless of the sync direction, so in case the timestamps are equal we need to resort to looking at the other data. We'll start by looking at the nodes' mailbox GUIDs, but if both of them don't exist continue looking into their children. */ if (local_ts > remote_ts || (local_ts == remote_ts && node_mailbox_trees_cmp(local_node1, remote_node2) < 0)) { /* local nodes have a higher timestamp. we only want to do renames where the destination parent is the current node's (local_node1/remote_node2) parent. */ /* Numbers are GUIDs, letters are mailbox names: local 1A <-name conflict-> remote 2A local 2B <- potentially -> remote 1[BC] Here we want to preserve the local 1A & 2B names: */ if (local_node2 == NULL) { /* local : 1A remote: 1B, 2A -> 2A-temp, 1A */ sync_rename_node(ctx, ctx->remote_tree, remote_node2, remote_node1, local_node1, reason_r); *reason_r = t_strconcat(*reason_r, "(local: local_node2=NULL)", NULL); return TRUE; } else if (remote_node1 == remote_node2) { /* FIXME: this fixes an infinite loop when in rename2 test, think it through why :) */ *reason_r = "local: remote_node1=remote_node2"; } else if (remote_node1 != NULL) { /* a) local_node1->parent == local_node2->parent local : 1A, 2B remote: 1B, 2A -> 2A-temp, 1A(, 2B) remote: 1C, 2A -> 2B, 1A remote: 1C, 2A, 3B -> 2A-temp, 1A(, 3B-temp, 2B) b) local_node1->parent != local_node2->parent local : 1X/A, 2Y/B remote: 1Y/B, 2X/A -> 2X/A-temp, 1X/A(, 2Y/B) remote: 1Z/C, 2X/A -> 2X/A-temp, 1X/A remote: 1Z/C, 2X/A, 3Y/B -> 2X/A-temp, 1X/A We can handle all of these more easily by simply always renaming 2 to a temporary name and handling it when we reach B handling. */ sync_rename_node(ctx, ctx->remote_tree, remote_node2, remote_node1, local_node1, reason_r); *reason_r = t_strconcat(*reason_r, "(local: remote_node1=NULL)", NULL); return TRUE; } else if (node_has_parent(local_node1, local_node2) && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) { /* node2 is a parent of node1, but it should be vice versa */ sync_rename_node_to_temp(ctx, ctx->local_tree, local_node1, local_node2->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(local: node2 parent of node1)", NULL); return TRUE; } else if (node_has_parent(local_node2, local_node1) && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) { /* node1 is a parent of node2, but it should be vice versa */ sync_rename_node_to_temp(ctx, ctx->local_tree, local_node2, local_node1->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(local: node1 parent of node2)", NULL); return TRUE; } else if (local_node1->existence == DSYNC_MAILBOX_NODE_EXISTS) { sync_rename_node_to_temp(ctx, ctx->remote_tree, remote_node2, remote_node2->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(local: local_node1 exists)", NULL); return TRUE; } else { /* local : 1A, 2B remote: 2A -> (2B) remote: 2A, 3B -> (3B-temp, 2B) */ *reason_r = "local: unchanged"; } } else { /* remote nodes have a higher timestamp */ if (remote_node1 == NULL) { sync_rename_node(ctx, ctx->local_tree, local_node1, local_node2, remote_node2, reason_r); *reason_r = t_strconcat(*reason_r, "(remote: remote_node1=NULL)", NULL); return TRUE; } else if (local_node2 == local_node1) { *reason_r = "remote: remote_node2=remote_node1"; } else if (local_node2 != NULL) { sync_rename_node(ctx, ctx->local_tree, local_node1, local_node2, remote_node2, reason_r); *reason_r = t_strconcat(*reason_r, "(remote: local_node2=NULL)", NULL); return TRUE; } else if (node_has_parent(remote_node1, remote_node2) && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) { sync_rename_node_to_temp(ctx, ctx->remote_tree, remote_node1, remote_node2->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(remote: node2 parent of node1)", NULL); return TRUE; } else if (node_has_parent(remote_node2, remote_node1) && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) { sync_rename_node_to_temp(ctx, ctx->remote_tree, remote_node2, remote_node1->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(remote: node1 parent of node2)", NULL); return TRUE; } else if (remote_node2->existence == DSYNC_MAILBOX_NODE_EXISTS) { sync_rename_node_to_temp(ctx, ctx->local_tree, local_node1, local_node1->parent, reason_r); *reason_r = t_strconcat(*reason_r, "(remote: remote_node2 exists)", NULL); return TRUE; } else { *reason_r = "remote: unchanged"; } } return FALSE; } static bool sync_rename_conflict(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_node1, struct dsync_mailbox_node *remote_node2, const char **reason_r) { struct dsync_mailbox_node *local_node2, *remote_node1; const uint8_t *guid_p; bool ret; guid_p = local_node1->mailbox_guid; remote_node1 = hash_table_lookup(ctx->remote_tree->guid_hash, guid_p); guid_p = remote_node2->mailbox_guid; local_node2 = hash_table_lookup(ctx->local_tree->guid_hash, guid_p); if ((remote_node1 != NULL && remote_node1->existence == DSYNC_MAILBOX_NODE_EXISTS) || (local_node2 != NULL && local_node2->existence == DSYNC_MAILBOX_NODE_EXISTS)) { /* conflicting name, rename the one with lower timestamp */ ret = sync_rename_lower_ts(ctx, local_node1, remote_node1, local_node2, remote_node2, reason_r); *reason_r = t_strconcat("conflicting name: ", *reason_r, NULL); return ret; } else if (dsync_mailbox_node_is_dir(local_node1) || dsync_mailbox_node_is_dir(remote_node2)) { /* one of the nodes is a directory, and the other is a mailbox that doesn't exist on the other side. there is no conflict, we'll just need to create the mailbox later. */ *reason_r = "mailbox not selectable yet"; return FALSE; } else { /* both nodes are mailboxes that don't exist on the other side. we'll merge these mailboxes together later and change their GUIDs and UIDVALIDITYs to be the same */ local_node1->sync_delayed_guid_change = TRUE; remote_node2->sync_delayed_guid_change = TRUE; *reason_r = "GUIDs conflict - will be merged later"; return FALSE; } } static struct dsync_mailbox_node * sync_find_branch(struct dsync_mailbox_tree *tree, struct dsync_mailbox_tree *other_tree, struct dsync_mailbox_node *dir_node) { struct dsync_mailbox_node *node, *other_node; const uint8_t *guid_p; for (node = dir_node->first_child; node != NULL; node = node->next) { if (dsync_mailbox_node_is_dir(node)) { other_node = sync_find_branch(tree, other_tree, node); if (other_node != NULL) return other_node; } else { guid_p = node->mailbox_guid; other_node = hash_table_lookup(other_tree->guid_hash, guid_p); if (other_node != NULL) return other_node->parent; } } return NULL; } static bool sync_rename_directory(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_node1, struct dsync_mailbox_node *remote_node2, const char **reason_r) { struct dsync_mailbox_node *remote_node1, *local_node2; /* see if we can find matching mailbox branches based on the nodes' child mailboxes (with GUIDs). we can then rename the entire branch. don't try to do this for namespace prefixes though. */ remote_node1 = sync_find_branch(ctx->local_tree, ctx->remote_tree, local_node1); local_node2 = sync_find_branch(ctx->remote_tree, ctx->local_tree, remote_node2); if (remote_node1 == NULL || local_node2 == NULL) { *reason_r = "Directory rename branch not found"; return FALSE; } if (node_names_equal(remote_node1, local_node2)) { *reason_r = "Directory name paths are equal"; return FALSE; } return sync_rename_lower_ts(ctx, local_node1, remote_node1, local_node2, remote_node2, reason_r); } static bool sync_rename_mailboxes(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_parent, struct dsync_mailbox_node *remote_parent) { struct dsync_mailbox_node **local_nodep = &local_parent->first_child; struct dsync_mailbox_node **remote_nodep = &remote_parent->first_child; struct dsync_mailbox_node *local_node, *remote_node; const char *reason; string_t *debug = NULL; bool changed; if ((ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG) != 0) debug = t_str_new(128); /* the nodes are sorted by their names. */ while (*local_nodep != NULL || *remote_nodep != NULL) { local_node = *local_nodep; remote_node = *remote_nodep; if (local_node == NULL || (remote_node != NULL && strcmp(local_node->name, remote_node->name) > 0)) { /* add a missing local node */ local_node = sync_node_new(ctx->local_tree, local_nodep, local_parent, remote_node); } if (remote_node == NULL || strcmp(remote_node->name, local_node->name) > 0) { /* add a missing remote node */ remote_node = sync_node_new(ctx->remote_tree, remote_nodep, remote_parent, local_node); } i_assert(strcmp(local_node->name, remote_node->name) == 0); if (debug != NULL) { str_truncate(debug, 0); str_append(debug, "Mailbox "); dsync_mailbox_node_append_full_name(debug, ctx->local_tree, local_node); str_printfa(debug, ": local=%s/%ld/%d, remote=%s/%ld/%d", guid_128_to_string(local_node->mailbox_guid), (long)local_node->last_renamed_or_created, local_node->existence, guid_128_to_string(remote_node->mailbox_guid), (long)remote_node->last_renamed_or_created, remote_node->existence); } if (dsync_mailbox_node_is_dir(local_node) && dsync_mailbox_node_is_dir(remote_node)) { /* both nodes are directories (or other side is nonexistent). see if we can match them by their child mailboxes */ changed = sync_rename_directory(ctx, local_node, remote_node, &reason); } else if (dsync_mailbox_node_guids_equal(local_node, remote_node)) { /* mailboxes are equal, no need to rename */ reason = "Mailboxes are equal"; changed = FALSE; } else { /* mailbox naming conflict */ changed = sync_rename_conflict(ctx, local_node, remote_node, &reason); } /* handle children, if there are any */ if (debug != NULL) { i_debug("brain %c: %s: %s", (ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN) != 0 ? 'M' : 'S', str_c(debug), reason); } if (!changed) T_BEGIN { changed = sync_rename_mailboxes(ctx, local_node, remote_node); } T_END; if (changed) return TRUE; local_nodep = &local_node->next; remote_nodep = &remote_node->next; } return FALSE; } static bool mailbox_node_hash_first_child(struct dsync_mailbox_node *node, struct md5_context *md5) { for (node = node->first_child; node != NULL; node = node->next) { if (node->existence == DSYNC_MAILBOX_NODE_EXISTS) { md5_update(md5, node->mailbox_guid, sizeof(node->mailbox_guid)); md5_update(md5, node->name, strlen(node->name)); return TRUE; } if (node->first_child != NULL) { if (mailbox_node_hash_first_child(node, md5)) return TRUE; } } return FALSE; } static const char * mailbox_node_generate_suffix(struct dsync_mailbox_node *node) { struct md5_context md5; unsigned char digest[MD5_RESULTLEN]; if (!dsync_mailbox_node_is_dir(node)) return guid_128_to_string(node->mailbox_guid); md5_init(&md5); if (!mailbox_node_hash_first_child(node, &md5)) i_unreached(); /* we would have deleted it */ md5_final(&md5, digest); return binary_to_hex(digest, sizeof(digest)); } static void suffix_inc(string_t *str) { char *data; size_t i; data = str_c_modifiable(str) + str_len(str)-1; for (i = str_len(str); i > 0; i--, data--) { if ((*data >= '0' && *data < '9') || (*data >= 'a' && *data < 'f')) { *data += 1; return; } else if (*data == '9') { *data = 'a'; return; } else if (*data != 'f') { i_unreached(); } } i_unreached(); } static void sync_rename_temp_mailbox_node(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, const char **reason_r) { const char *p, *new_suffix; string_t *str = t_str_new(256); size_t max_prefix_len; /* The name is currently -. Both sides need to use equivalent names, so we'll replace the if possible with a) mailbox GUID, b) sha1 of childrens' (GUID|name)s. In the very unlikely case of such name already existing, just increase the last letters until it's not found. */ new_suffix = mailbox_node_generate_suffix(node); p = strrchr(node->name, '-'); i_assert(p != NULL); p++; max_prefix_len = TEMP_MAX_NAME_LEN - strlen(new_suffix) - 1; if (max_prefix_len > (size_t)(p-node->name)) max_prefix_len = p-node->name; str_append_n(str, node->name, max_prefix_len); str_append(str, new_suffix); while (node_has_child(node->parent, str_c(str))) suffix_inc(str); *reason_r = t_strdup_printf("Renamed '%s' to '%s'", dsync_mailbox_node_get_full_name(tree, node), str_c(str)); node->name = p_strdup(tree->pool, str_c(str)); dsync_mailbox_tree_node_move_sorted(node, node->parent); node->sync_temporary_name = FALSE; } static void sync_rename_delete_node_dirs(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node) { struct dsync_mailbox_node *child; for (child = node->first_child; child != NULL; child = child->next) sync_rename_delete_node_dirs(ctx, tree, child); if (tree == ctx->local_tree && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL && node->existence != DSYNC_MAILBOX_NODE_NONEXISTENT) { sync_add_dir_change(ctx, node, DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR); } node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; node->sync_temporary_name = FALSE; } static bool sync_rename_temp_mailboxes(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, bool *renames_r) { const char *reason; for (; node != NULL; node = node->next) { while (sync_rename_temp_mailboxes(ctx, tree, node->first_child, renames_r)) ; if (!node->sync_temporary_name) { } else if (dsync_mailbox_node_is_dir(node) && (node->first_child == NULL || !node_has_existent_children(node, FALSE))) { /* we can just delete this directory and any child directories it may have */ if ((ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG) != 0) { i_debug("brain %c: %s mailbox %s: Delete directory-only tree", (ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN) != 0 ? 'M' : 'S', tree == ctx->local_tree ? "local" : "remote", dsync_mailbox_node_get_full_name(tree, node)); } sync_rename_delete_node_dirs(ctx, tree, node); } else { T_BEGIN { *renames_r = TRUE; sync_rename_temp_mailbox_node(tree, node, &reason); if ((ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG) != 0) { i_debug("brain %c: %s mailbox %s: %s", (ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN) != 0 ? 'M' : 'S', tree == ctx->local_tree ? "local" : "remote", dsync_mailbox_node_get_full_name(tree, node), reason); } } T_END; return TRUE; } } return FALSE; } static int dsync_mailbox_tree_handle_renames(struct dsync_mailbox_tree_sync_ctx *ctx, bool *renames_r) { unsigned int max_renames, count = 0; bool changed; *renames_r = FALSE; max_renames = ctx->combined_mailboxes_count * 3; do { T_BEGIN { changed = sync_rename_mailboxes(ctx, &ctx->local_tree->root, &ctx->remote_tree->root); } T_END; if ((ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG) != 0 && changed) { i_debug("brain %c: -- Mailbox renamed, restart sync --", (ctx->sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN) != 0 ? 'M' : 'S'); } } while (changed && ++count <= max_renames); if (changed) { i_error("BUG: Mailbox renaming algorithm got into a potentially infinite loop, aborting"); return -1; } while (sync_rename_temp_mailboxes(ctx, ctx->local_tree, &ctx->local_tree->root, renames_r)) ; while (sync_rename_temp_mailboxes(ctx, ctx->remote_tree, &ctx->remote_tree->root, renames_r)) ; return 0; } static bool sync_is_wrong_mailbox(struct dsync_mailbox_node *node, const struct dsync_mailbox_node *wanted_node, const char **reason_r) { if (wanted_node->existence != DSYNC_MAILBOX_NODE_EXISTS) { *reason_r = wanted_node->existence == DSYNC_MAILBOX_NODE_DELETED ? "Mailbox has been deleted" : "Mailbox doesn't exist"; return TRUE; } if (node->uid_validity != wanted_node->uid_validity) { *reason_r = t_strdup_printf("UIDVALIDITY changed (%u -> %u)", wanted_node->uid_validity, node->uid_validity); return TRUE; } if (node->uid_next > wanted_node->uid_next) { /* we can't lower the UIDNEXT */ *reason_r = t_strdup_printf("UIDNEXT is too high (%u > %u)", node->uid_next, wanted_node->uid_next); return TRUE; } if (memcmp(node->mailbox_guid, wanted_node->mailbox_guid, sizeof(node->mailbox_guid)) != 0) { *reason_r = "GUID changed"; return TRUE; } return FALSE; } static void sync_delete_wrong_mailboxes_branch(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, const struct dsync_mailbox_tree *wanted_tree, struct dsync_mailbox_node *node, const struct dsync_mailbox_node *wanted_node) { const char *reason; int ret; while (node != NULL && wanted_node != NULL) { if (node->first_child != NULL) { sync_delete_wrong_mailboxes_branch(ctx, tree, wanted_tree, node->first_child, wanted_node->first_child); } ret = strcmp(node->name, wanted_node->name); if (ret < 0) { /* node shouldn't exist */ if (node->existence == DSYNC_MAILBOX_NODE_EXISTS && !dsync_mailbox_node_is_dir(node)) { sync_delete_mailbox_node(ctx, tree, node, "Mailbox doesn't exist"); } node = node->next; } else if (ret > 0) { /* wanted_node doesn't exist. it's created later. */ wanted_node = wanted_node->next; } else T_BEGIN { if (sync_is_wrong_mailbox(node, wanted_node, &reason) && node->existence == DSYNC_MAILBOX_NODE_EXISTS && !dsync_mailbox_node_is_dir(node)) sync_delete_mailbox_node(ctx, tree, node, reason); node = node->next; wanted_node = wanted_node->next; } T_END; } for (; node != NULL; node = node->next) { /* node and its children shouldn't exist */ if (node->first_child != NULL) { sync_delete_wrong_mailboxes_branch(ctx, tree, wanted_tree, node->first_child, NULL); } if (node->existence == DSYNC_MAILBOX_NODE_EXISTS && !dsync_mailbox_node_is_dir(node)) sync_delete_mailbox_node(ctx, tree, node, "Mailbox doesn't exist"); } } static void sync_delete_wrong_mailboxes(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree, const struct dsync_mailbox_tree *wanted_tree) { sync_delete_wrong_mailboxes_branch(ctx, tree, wanted_tree, tree->root.first_child, wanted_tree->root.first_child); } static void sync_create_mailboxes(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_tree *tree) { struct dsync_mailbox_tree *other_tree; struct dsync_mailbox_tree_iter *iter; struct dsync_mailbox_node *node, *other_node; const char *name; const uint8_t *guid_p; other_tree = tree == ctx->local_tree ? ctx->remote_tree : ctx->local_tree; iter = dsync_mailbox_tree_iter_init(tree); while (dsync_mailbox_tree_iter_next(iter, &name, &node)) { /* make sure the renaming handled everything */ i_assert(!node->sync_temporary_name); if (dsync_mailbox_node_is_dir(node)) continue; i_assert(node->existence == DSYNC_MAILBOX_NODE_EXISTS); guid_p = node->mailbox_guid; other_node = hash_table_lookup(other_tree->guid_hash, guid_p); if (other_node == NULL) other_node = sorted_tree_get(other_tree, name); if (dsync_mailbox_node_is_dir(other_node)) { /* create a missing mailbox */ other_node->existence = DSYNC_MAILBOX_NODE_EXISTS; other_node->ns = node->ns; other_node->uid_validity = node->uid_validity; memcpy(other_node->mailbox_guid, node->mailbox_guid, sizeof(other_node->mailbox_guid)); if (other_tree == ctx->local_tree) sync_add_create_change(ctx, other_node, name); } else if (!guid_128_equals(node->mailbox_guid, other_node->mailbox_guid)) { /* mailbox with same name exists both locally and remotely, but they have different GUIDs and neither side has the other's GUID. typically this means that both sides had autocreated some mailboxes (e.g. INBOX). we'll just change the GUID for one of them. */ i_assert(node->existence == DSYNC_MAILBOX_NODE_EXISTS); i_assert(node->ns == other_node->ns); if (other_tree == ctx->local_tree) sync_add_create_change(ctx, node, name); } else { /* existing mailbox. mismatching UIDVALIDITY is handled later while syncing the mailbox. */ i_assert(node->existence == DSYNC_MAILBOX_NODE_EXISTS); i_assert(node->ns == other_node->ns); } } dsync_mailbox_tree_iter_deinit(&iter); } static void sync_subscription(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_node, struct dsync_mailbox_node *remote_node) { bool use_local; if (ctx->sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) use_local = TRUE; else if (ctx->sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) use_local = FALSE; else if (local_node->last_subscription_change > remote_node->last_subscription_change) use_local = TRUE; else if (local_node->last_subscription_change < remote_node->last_subscription_change) use_local = FALSE; else { /* local and remote have equal timestamps. prefer to subscribe rather than unsubscribe. */ use_local = local_node->subscribed; } if (use_local) { /* use local subscription state */ remote_node->subscribed = local_node->subscribed; } else { /* use remote subscription state */ local_node->subscribed = remote_node->subscribed; sync_add_dir_change(ctx, local_node, local_node->subscribed ? DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE : DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE); } } static void sync_mailbox_child_dirs(struct dsync_mailbox_tree_sync_ctx *ctx, struct dsync_mailbox_node *local_parent, struct dsync_mailbox_node *remote_parent) { struct dsync_mailbox_node **local_nodep = &local_parent->first_child; struct dsync_mailbox_node **remote_nodep = &remote_parent->first_child; struct dsync_mailbox_node *local_node, *remote_node; int ret; /* NOTE: the nodes are always sorted. renaming created all of the interesting nodes, but it may have left some extra nonexistent nodes lying around, which we will delete. */ while (*local_nodep != NULL && *remote_nodep != NULL) { local_node = *local_nodep; remote_node = *remote_nodep; ret = strcmp(local_node->name, remote_node->name); if (ret < 0) { i_assert(!node_is_existent(local_node)); *local_nodep = local_node->next; continue; } if (ret > 0) { i_assert(!node_is_existent(remote_node)); *remote_nodep = remote_node->next; continue; } if (local_node->existence == DSYNC_MAILBOX_NODE_EXISTS && remote_node->existence == DSYNC_MAILBOX_NODE_NONEXISTENT && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) { /* create to remote */ remote_node->existence = DSYNC_MAILBOX_NODE_EXISTS; } if (remote_node->existence == DSYNC_MAILBOX_NODE_EXISTS && local_node->existence == DSYNC_MAILBOX_NODE_NONEXISTENT && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) { /* create to local */ local_node->existence = DSYNC_MAILBOX_NODE_EXISTS; sync_add_dir_change(ctx, local_node, DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR); } /* create/delete child dirs */ sync_mailbox_child_dirs(ctx, local_node, remote_node); if (local_node->subscribed != remote_node->subscribed) sync_subscription(ctx, local_node, remote_node); if (local_node->existence == DSYNC_MAILBOX_NODE_DELETED && !node_has_existent_children(local_node, TRUE) && remote_node->existence == DSYNC_MAILBOX_NODE_EXISTS && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) { /* delete from remote */ i_assert(!node_has_existent_children(remote_node, TRUE)); remote_node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; } if (remote_node->existence == DSYNC_MAILBOX_NODE_DELETED && !node_has_existent_children(remote_node, TRUE) && local_node->existence == DSYNC_MAILBOX_NODE_EXISTS && ctx->sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) { /* delete from local */ i_assert(!node_has_existent_children(local_node, TRUE)); local_node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; sync_add_dir_change(ctx, local_node, DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR); } local_nodep = &local_node->next; remote_nodep = &remote_node->next; } while (*local_nodep != NULL) { i_assert(!node_is_existent(*local_nodep)); *local_nodep = (*local_nodep)->next; } while (*remote_nodep != NULL) { i_assert(!node_is_existent(*remote_nodep)); *remote_nodep = (*remote_nodep)->next; } } static void sync_mailbox_dirs(struct dsync_mailbox_tree_sync_ctx *ctx) { sync_mailbox_child_dirs(ctx, &ctx->local_tree->root, &ctx->remote_tree->root); } static void dsync_mailbox_tree_update_child_timestamps(struct dsync_mailbox_node *node, time_t parent_timestamp) { time_t ts; if (node->last_renamed_or_created < parent_timestamp) node->last_renamed_or_created = parent_timestamp; ts = node->last_renamed_or_created; for (node = node->first_child; node != NULL; node = node->next) dsync_mailbox_tree_update_child_timestamps(node, ts); } struct dsync_mailbox_tree_sync_ctx * dsync_mailbox_trees_sync_init(struct dsync_mailbox_tree *local_tree, struct dsync_mailbox_tree *remote_tree, enum dsync_mailbox_trees_sync_type sync_type, enum dsync_mailbox_trees_sync_flags sync_flags) { struct dsync_mailbox_tree_sync_ctx *ctx; unsigned int rename_counter = 0; bool renames; pool_t pool; i_assert(hash_table_is_created(local_tree->guid_hash)); i_assert(hash_table_is_created(remote_tree->guid_hash)); pool = pool_alloconly_create(MEMPOOL_GROWING"dsync mailbox trees sync", 1024*64); ctx = p_new(pool, struct dsync_mailbox_tree_sync_ctx, 1); ctx->pool = pool; ctx->local_tree = local_tree; ctx->remote_tree = remote_tree; ctx->sync_type = sync_type; ctx->sync_flags = sync_flags; i_array_init(&ctx->changes, 128); again: renames = FALSE; ctx->combined_mailboxes_count = 0; sync_tree_sort_and_delete_mailboxes(ctx, remote_tree, sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY); sync_tree_sort_and_delete_mailboxes(ctx, local_tree, sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY); dsync_mailbox_tree_update_child_timestamps(&local_tree->root, 0); dsync_mailbox_tree_update_child_timestamps(&remote_tree->root, 0); if ((sync_flags & DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES) == 0) { if (dsync_mailbox_tree_handle_renames(ctx, &renames) < 0) { ctx->failed = TRUE; return ctx; } } /* if we're not doing a two-way sync, delete now any mailboxes, which a) shouldn't exist, b) doesn't have a matching GUID/UIDVALIDITY, c) has a too high UIDNEXT */ if (sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) sync_delete_wrong_mailboxes(ctx, remote_tree, local_tree); else if (sync_type == DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) sync_delete_wrong_mailboxes(ctx, local_tree, remote_tree); if (sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL) sync_create_mailboxes(ctx, remote_tree); if (sync_type != DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE) sync_create_mailboxes(ctx, local_tree); if (renames && rename_counter++ <= ctx->combined_mailboxes_count*3) { /* this rename algorithm is just horrible. we're retrying this because the final sync_rename_temp_mailbox_node() calls give different names to local & remote mailbox trees. something's not right here, but this looping is better than a crash in sync_mailbox_dirs() due to trees not matching. */ goto again; } sync_mailbox_dirs(ctx); return ctx; } const struct dsync_mailbox_tree_sync_change * dsync_mailbox_trees_sync_next(struct dsync_mailbox_tree_sync_ctx *ctx) { if (ctx->change_idx == array_count(&ctx->changes)) return NULL; return array_idx(&ctx->changes, ctx->change_idx++); } int dsync_mailbox_trees_sync_deinit(struct dsync_mailbox_tree_sync_ctx **_ctx) { struct dsync_mailbox_tree_sync_ctx *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; array_free(&ctx->changes); pool_unref(&ctx->pool); return ret; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-serializer.h0000644000175000017500000000120313123174404017306 00000000000000#ifndef DSYNC_SERIALIZER_H #define DSYNC_SERIALIZER_H #define NULL_CHR '\002' struct dsync_serializer *dsync_serializer_init(const char *const keys[]); void dsync_serializer_deinit(struct dsync_serializer **serializer); const char * dsync_serializer_encode_header_line(struct dsync_serializer *serializer); struct dsync_serializer_encoder * dsync_serializer_encode_begin(struct dsync_serializer *serializer); void dsync_serializer_encode_add(struct dsync_serializer_encoder *encoder, const char *key, const char *value); void dsync_serializer_encode_finish(struct dsync_serializer_encoder **encoder, string_t *output); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-tree-fill.c0000644000175000017500000002675413123174404020466 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "guid.h" #include "str.h" #include "wildcard-match.h" #include "mailbox-log.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mailbox-list-iter.h" #include "dsync-mailbox-tree-private.h" static int dsync_mailbox_tree_add_node(struct dsync_mailbox_tree *tree, const struct mailbox_info *info, struct dsync_mailbox_node **node_r) { struct dsync_mailbox_node *node; node = dsync_mailbox_tree_get(tree, info->vname); if (node->ns == info->ns) ; else if (node->ns == NULL) { i_assert(tree->root.ns == NULL); node->ns = info->ns; } else { i_error("Mailbox '%s' exists in two namespaces: '%s' and '%s'", info->vname, node->ns->prefix, info->ns->prefix); return -1; } *node_r = node; return 0; } static int dsync_mailbox_tree_add_exists_node(struct dsync_mailbox_tree *tree, const struct mailbox_info *info, struct dsync_mailbox_node **node_r, enum mail_error *error_r) { if (dsync_mailbox_tree_add_node(tree, info, node_r) < 0) { *error_r = MAIL_ERROR_TEMP; return -1; } (*node_r)->existence = DSYNC_MAILBOX_NODE_EXISTS; return 0; } static int dsync_mailbox_tree_get_selectable(struct mailbox *box, struct mailbox_metadata *metadata_r, struct mailbox_status *status_r) { /* try the fast path */ if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, metadata_r) < 0) return -1; if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0) return -1; i_assert(!guid_128_is_empty(metadata_r->guid)); if (status_r->uidvalidity != 0) return 0; /* no UIDVALIDITY assigned yet. syncing a mailbox should add it. */ if (mailbox_sync(box, 0) < 0) return -1; if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0) return -1; i_assert(status_r->uidvalidity != 0); return 0; } static int dsync_mailbox_tree_add(struct dsync_mailbox_tree *tree, const struct mailbox_info *info, const guid_128_t box_guid, enum mail_error *error_r) { struct dsync_mailbox_node *node; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_status status; const char *errstr; enum mail_error error; int ret = 0; if ((info->flags & MAILBOX_NONEXISTENT) != 0) return 0; if ((info->flags & MAILBOX_NOSELECT) != 0) { return !guid_128_is_empty(box_guid) ? 0 : dsync_mailbox_tree_add_exists_node(tree, info, &node, error_r); } /* get GUID and UIDVALIDITY for selectable mailbox */ box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY); if (dsync_mailbox_tree_get_selectable(box, &metadata, &status) < 0) { errstr = mailbox_get_last_internal_error(box, &error); switch (error) { case MAIL_ERROR_NOTFOUND: /* mailbox was just deleted? */ break; case MAIL_ERROR_NOTPOSSIBLE: /* invalid mbox files? ignore */ break; default: i_error("Failed to access mailbox %s: %s", info->vname, errstr); *error_r = error; ret = -1; } mailbox_free(&box); return ret; } mailbox_free(&box); if (!guid_128_is_empty(box_guid) && !guid_128_equals(box_guid, metadata.guid)) { /* unwanted mailbox */ return 0; } if (dsync_mailbox_tree_add_exists_node(tree, info, &node, error_r) < 0) return -1; memcpy(node->mailbox_guid, metadata.guid, sizeof(node->mailbox_guid)); node->uid_validity = status.uidvalidity; node->uid_next = status.uidnext; return 0; } static struct dsync_mailbox_node * dsync_mailbox_tree_find_sha(struct dsync_mailbox_tree *tree, struct mail_namespace *ns, const guid_128_t sha128) { struct dsync_mailbox_node *node; if (!hash_table_is_created(tree->name128_hash)) dsync_mailbox_tree_build_name128_hash(tree); node = hash_table_lookup(tree->name128_hash, sha128); return node == NULL || node->ns != ns ? NULL : node; } static int dsync_mailbox_tree_add_change_timestamps(struct dsync_mailbox_tree *tree, struct mail_namespace *ns) { struct dsync_mailbox_node *node; struct dsync_mailbox_delete *del; struct mailbox_log *log; struct mailbox_log_iter *iter; const struct mailbox_log_record *rec; const uint8_t *guid_p; time_t timestamp; log = mailbox_list_get_changelog(ns->list); if (log == NULL) return 0; iter = mailbox_log_iter_init(log); while ((rec = mailbox_log_iter_next(iter)) != NULL) { node = rec->type == MAILBOX_LOG_RECORD_DELETE_MAILBOX ? NULL : dsync_mailbox_tree_find_sha(tree, ns, rec->mailbox_guid); timestamp = mailbox_log_record_get_timestamp(rec); switch (rec->type) { case MAILBOX_LOG_RECORD_DELETE_MAILBOX: guid_p = rec->mailbox_guid; if (hash_table_lookup(tree->guid_hash, guid_p) != NULL) { /* mailbox still exists. maybe it was restored from backup or something. */ break; } del = array_append_space(&tree->deletes); del->type = DSYNC_MAILBOX_DELETE_TYPE_MAILBOX; del->timestamp = timestamp; memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid)); break; case MAILBOX_LOG_RECORD_DELETE_DIR: if (node != NULL && node->existence == DSYNC_MAILBOX_NODE_EXISTS) { /* directory exists again, skip it */ break; } /* we don't know what directory name was deleted, just its hash. if the name still exists on the other dsync side, it can match this deletion to the name. */ del = array_append_space(&tree->deletes); del->type = DSYNC_MAILBOX_DELETE_TYPE_DIR; del->timestamp = timestamp; memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid)); break; case MAILBOX_LOG_RECORD_CREATE_DIR: if (node == NULL) { /* directory has been deleted again, skip it */ break; } /* notify the remote that we want to keep this directory created (unless remote has a newer delete timestamp) */ node->last_renamed_or_created = timestamp; break; case MAILBOX_LOG_RECORD_RENAME: if (node != NULL) node->last_renamed_or_created = timestamp; break; case MAILBOX_LOG_RECORD_SUBSCRIBE: if (node != NULL) node->last_subscription_change = timestamp; break; case MAILBOX_LOG_RECORD_UNSUBSCRIBE: if (node != NULL) { node->last_subscription_change = timestamp; break; } /* The mailbox is already deleted, but it may still exist on the other side (even the subscription alone). */ del = array_append_space(&tree->deletes); del->type = DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE; del->timestamp = timestamp; memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid)); break; } } if (mailbox_log_iter_deinit(&iter) < 0) { i_error("Mailbox log iteration for namespace '%s' failed", ns->prefix); return -1; } return 0; } static int dsync_mailbox_tree_fix_guid_duplicate(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { struct mailbox *box; struct mailbox_update update; struct dsync_mailbox_node *change_node; const char *change_vname; int ret = 0; i_zero(&update); guid_128_generate(update.mailbox_guid); /* just in case the duplication exists in both sides, make them choose the same node */ if (strcmp(dsync_mailbox_node_get_full_name(tree, node1), dsync_mailbox_node_get_full_name(tree, node2)) <= 0) change_node = node1; else change_node = node2; change_vname = dsync_mailbox_node_get_full_name(tree, change_node); i_error("Duplicate mailbox GUID %s for mailboxes %s and %s - " "giving a new GUID %s to %s", guid_128_to_string(node1->mailbox_guid), dsync_mailbox_node_get_full_name(tree, node1), dsync_mailbox_node_get_full_name(tree, node2), guid_128_to_string(update.mailbox_guid), change_vname); i_assert(node1->ns != NULL && node2->ns != NULL); box = mailbox_alloc(change_node->ns->list, change_vname, 0); if (mailbox_update(box, &update) < 0) { i_error("Couldn't update mailbox %s GUID: %s", change_vname, mailbox_get_last_internal_error(box, NULL)); ret = -1; } else { memcpy(change_node->mailbox_guid, update.mailbox_guid, sizeof(change_node->mailbox_guid)); } mailbox_free(&box); return ret; } static bool dsync_mailbox_info_is_wanted(const struct mailbox_info *info, const char *box_name, const char *const *exclude_mailboxes) { const char *const *info_specialuses; unsigned int i; if (exclude_mailboxes == NULL && (box_name == NULL || box_name[0] != '\\')) return TRUE; info_specialuses = info->special_use == NULL ? NULL : t_strsplit(info->special_use, " "); /* include */ if (box_name != NULL && box_name[0] == '\\') { if (info_specialuses == NULL || !str_array_icase_find(info_specialuses, box_name)) return FALSE; } /* exclude */ if (exclude_mailboxes == NULL) return TRUE; for (i = 0; exclude_mailboxes[i] != NULL; i++) { const char *exclude = exclude_mailboxes[i]; if (exclude[0] == '\\') { /* special-use */ if (info_specialuses != NULL && str_array_icase_find(info_specialuses, exclude)) return FALSE; } else { /* mailbox with wildcards */ if (wildcard_match(info->vname, exclude)) return FALSE; } } return TRUE; } int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree, struct mail_namespace *ns, const char *box_name, const guid_128_t box_guid, const char *const *exclude_mailboxes, enum mail_error *error_r) { const enum mailbox_list_iter_flags list_flags = /* FIXME: we'll skip symlinks, because we can't handle them currently. in future we could detect them and create them by creating the symlink. */ MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES; const enum mailbox_list_iter_flags subs_list_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct mailbox_list_iterate_context *iter; struct dsync_mailbox_node *node, *dup_node1, *dup_node2; const struct mailbox_info *info; const char *list_pattern = box_name != NULL && box_name[0] != '\\' ? box_name : "*"; int ret = 0; i_assert(mail_namespace_get_sep(ns) == tree->sep); /* assign namespace to its root, so it gets copied to children */ if (ns->prefix_len > 0) { node = dsync_mailbox_tree_get(tree, t_strndup(ns->prefix, ns->prefix_len-1)); node->ns = ns; } else { tree->root.ns = ns; } /* first add all of the existing mailboxes */ iter = mailbox_list_iter_init(ns->list, list_pattern, list_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (dsync_mailbox_info_is_wanted(info, box_name, exclude_mailboxes)) { if (dsync_mailbox_tree_add(tree, info, box_guid, error_r) < 0) ret = -1; } } T_END; if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Mailbox listing for namespace '%s' failed: %s", ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r)); ret = -1; } /* add subscriptions */ iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (dsync_mailbox_tree_add_node(tree, info, &node) == 0) node->subscribed = TRUE; else { *error_r = MAIL_ERROR_TEMP; ret = -1; } } if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Mailbox listing for namespace '%s' failed: %s", ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r)); ret = -1; } if (ret < 0) return -1; while (dsync_mailbox_tree_build_guid_hash(tree, &dup_node1, &dup_node2) < 0) { if (dsync_mailbox_tree_fix_guid_duplicate(tree, dup_node1, dup_node2) < 0) return -1; } /* add timestamps */ if (dsync_mailbox_tree_add_change_timestamps(tree, ns) < 0) return -1; return 0; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain-private.h0000644000175000017500000001234513165463624017724 00000000000000#ifndef DSYNC_BRAIN_PRIVATE_H #define DSYNC_BRAIN_PRIVATE_H #include "hash.h" #include "dsync-brain.h" #include "dsync-mailbox.h" #include "dsync-mailbox-state.h" #define DSYNC_LOCK_FILENAME ".dovecot-sync.lock" struct dsync_mailbox_tree_sync_change; enum dsync_state { DSYNC_STATE_MASTER_RECV_HANDSHAKE, DSYNC_STATE_SLAVE_RECV_HANDSHAKE, /* if sync_type=STATE, the master brain knows the saved "last common mailbox state". this state is sent to the slave. */ DSYNC_STATE_MASTER_SEND_LAST_COMMON, DSYNC_STATE_SLAVE_RECV_LAST_COMMON, /* both sides send their mailbox trees */ DSYNC_STATE_SEND_MAILBOX_TREE, DSYNC_STATE_SEND_MAILBOX_TREE_DELETES, DSYNC_STATE_RECV_MAILBOX_TREE, DSYNC_STATE_RECV_MAILBOX_TREE_DELETES, /* master decides in which order mailboxes are synced (it knows the slave's mailboxes by looking at the received mailbox tree) */ DSYNC_STATE_MASTER_SEND_MAILBOX, DSYNC_STATE_SLAVE_RECV_MAILBOX, /* once mailbox is selected, the mails inside it are synced. after the mails are synced, another mailbox is synced. */ DSYNC_STATE_SYNC_MAILS, DSYNC_STATE_FINISH, DSYNC_STATE_DONE }; enum dsync_box_state { DSYNC_BOX_STATE_MAILBOX, DSYNC_BOX_STATE_CHANGES, DSYNC_BOX_STATE_ATTRIBUTES, DSYNC_BOX_STATE_MAIL_REQUESTS, DSYNC_BOX_STATE_MAILS, DSYNC_BOX_STATE_RECV_LAST_COMMON, DSYNC_BOX_STATE_DONE }; struct dsync_brain { pool_t pool; struct mail_user *user; struct dsync_ibc *ibc; const char *process_title_prefix; ARRAY(struct mail_namespace *) sync_namespaces; const char *sync_box; struct mailbox *virtual_all_box; guid_128_t sync_box_guid; const char *const *exclude_mailboxes; enum dsync_brain_sync_type sync_type; time_t sync_since_timestamp; time_t sync_until_timestamp; uoff_t sync_max_size; const char *sync_flag; char alt_char; unsigned int import_commit_msgs_interval; unsigned int hdr_hash_version; unsigned int lock_timeout; int lock_fd; const char *lock_path; struct file_lock *lock; char hierarchy_sep; struct dsync_mailbox_tree *local_mailbox_tree; struct dsync_mailbox_tree *remote_mailbox_tree; struct dsync_mailbox_tree_iter *local_tree_iter; enum dsync_state state, pre_box_state; enum dsync_box_state box_recv_state; enum dsync_box_state box_send_state; unsigned int proctitle_update_counter; struct dsync_transaction_log_scan *log_scan; struct dsync_mailbox_importer *box_importer; struct dsync_mailbox_exporter *box_exporter; struct mailbox *box; struct dsync_mailbox local_dsync_box, remote_dsync_box; pool_t dsync_box_pool; /* list of mailbox states for master brain: given to brain at init and for slave brain: received from DSYNC_STATE_SLAVE_RECV_LAST_COMMON */ HASH_TABLE_TYPE(dsync_mailbox_state) mailbox_states; /* DSYNC_STATE_MASTER_SEND_LAST_COMMON: current send position */ struct hash_iterate_context *mailbox_states_iter; /* state of the mailbox we're currently syncing, changed at init and deinit */ struct dsync_mailbox_state mailbox_state; /* new states for synced mailboxes */ ARRAY_TYPE(dsync_mailbox_state) remote_mailbox_states; const char *changes_during_sync; enum mail_error mail_error; const char *const *hashed_headers; unsigned int master_brain:1; unsigned int mail_requests:1; unsigned int backup_send:1; unsigned int backup_recv:1; unsigned int purge:1; unsigned int debug:1; unsigned int sync_visible_namespaces:1; unsigned int no_mail_sync:1; unsigned int no_backup_overwrite:1; unsigned int no_mail_prefetch:1; unsigned int no_mailbox_renames:1; unsigned int changes_during_remote_sync:1; unsigned int require_full_resync:1; unsigned int verbose_proctitle:1; unsigned int no_notify:1; unsigned int failed:1; unsigned int empty_hdr_workaround:1; }; extern const char *dsync_box_state_names[DSYNC_BOX_STATE_DONE+1]; void dsync_brain_mailbox_trees_init(struct dsync_brain *brain); void dsync_brain_send_mailbox_tree(struct dsync_brain *brain); void dsync_brain_send_mailbox_tree_deletes(struct dsync_brain *brain); bool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain); bool dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain *brain); int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain, const struct dsync_mailbox_tree_sync_change *change, enum mail_error *error_r); void dsync_brain_sync_mailbox_deinit(struct dsync_brain *brain); int dsync_brain_mailbox_alloc(struct dsync_brain *brain, const guid_128_t guid, struct mailbox **box_r, const char **errstr_r, enum mail_error *error_r); bool dsync_brain_mailbox_update_pre(struct dsync_brain *brain, struct mailbox *box, const struct dsync_mailbox *local_box, const struct dsync_mailbox *remote_box, const char **reason_r); bool dsync_boxes_need_sync(struct dsync_brain *brain, const struct dsync_mailbox *box1, const struct dsync_mailbox *box2); void dsync_brain_sync_init_box_states(struct dsync_brain *brain); void dsync_brain_set_changes_during_sync(struct dsync_brain *brain, const char *reason); void dsync_brain_master_send_mailbox(struct dsync_brain *brain); bool dsync_brain_slave_recv_mailbox(struct dsync_brain *brain); int dsync_brain_sync_mailbox_open(struct dsync_brain *brain, const struct dsync_mailbox *remote_dsync_box); bool dsync_brain_sync_mails(struct dsync_brain *brain); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain-mailbox.c0000644000175000017500000006473613165463624017713 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "mail-cache-private.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "dsync-ibc.h" #include "dsync-mailbox-tree.h" #include "dsync-mailbox-import.h" #include "dsync-mailbox-export.h" #include "dsync-transaction-log-scan.h" #include "dsync-brain-private.h" static int ns_mailbox_try_alloc(struct dsync_brain *brain, struct mail_namespace *ns, const guid_128_t guid, struct mailbox **box_r, const char **errstr_r, enum mail_error *error_r) { enum mailbox_flags flags = 0; struct mailbox *box; enum mailbox_existence existence; int ret; if (brain->backup_send) { /* make sure mailbox isn't modified */ flags |= MAILBOX_FLAG_READONLY; } box = mailbox_alloc_guid(ns->list, guid, flags); ret = mailbox_exists(box, FALSE, &existence); if (ret < 0) { *errstr_r = mailbox_get_last_internal_error(box, error_r); mailbox_free(&box); return -1; } if (existence != MAILBOX_EXISTENCE_SELECT) { mailbox_free(&box); *errstr_r = existence == MAILBOX_EXISTENCE_NONE ? "Mailbox was already deleted" : "Mailbox is no longer selectable"; return 0; } *box_r = box; return 1; } int dsync_brain_mailbox_alloc(struct dsync_brain *brain, const guid_128_t guid, struct mailbox **box_r, const char **errstr_r, enum mail_error *error_r) { struct mail_namespace *ns; int ret; *box_r = NULL; for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) { if (!dsync_brain_want_namespace(brain, ns)) continue; if ((ret = ns_mailbox_try_alloc(brain, ns, guid, box_r, errstr_r, error_r)) != 0) return ret; } return 0; } static void dsync_mailbox_cache_field_dup(ARRAY_TYPE(mailbox_cache_field) *dest, const ARRAY_TYPE(mailbox_cache_field) *src, pool_t pool) { const struct mailbox_cache_field *src_field; struct mailbox_cache_field *dest_field; p_array_init(dest, pool, array_count(src)); array_foreach(src, src_field) { dest_field = array_append_space(dest); dest_field->name = p_strdup(pool, src_field->name); dest_field->decision = src_field->decision; dest_field->last_used = src_field->last_used; } } static const struct dsync_mailbox_state * dsync_mailbox_state_find(struct dsync_brain *brain, const guid_128_t mailbox_guid) { const uint8_t *guid_p; guid_p = mailbox_guid; return hash_table_lookup(brain->mailbox_states, guid_p); } static void dsync_mailbox_state_remove(struct dsync_brain *brain, const guid_128_t mailbox_guid) { const uint8_t *guid_p; guid_p = mailbox_guid; if (hash_table_lookup(brain->mailbox_states, guid_p) != NULL) hash_table_remove(brain->mailbox_states, guid_p); } void dsync_brain_sync_init_box_states(struct dsync_brain *brain) { if (brain->backup_send) { /* we have an exporter, but no importer. */ brain->box_send_state = DSYNC_BOX_STATE_ATTRIBUTES; brain->box_recv_state = brain->mail_requests ? DSYNC_BOX_STATE_MAIL_REQUESTS : DSYNC_BOX_STATE_RECV_LAST_COMMON; } else if (brain->backup_recv) { /* we have an importer, but no exporter */ brain->box_send_state = brain->mail_requests ? DSYNC_BOX_STATE_MAIL_REQUESTS : DSYNC_BOX_STATE_DONE; brain->box_recv_state = DSYNC_BOX_STATE_ATTRIBUTES; } else { brain->box_send_state = DSYNC_BOX_STATE_ATTRIBUTES; brain->box_recv_state = DSYNC_BOX_STATE_ATTRIBUTES; } } static void dsync_brain_sync_mailbox_init(struct dsync_brain *brain, struct mailbox *box, const struct dsync_mailbox *local_dsync_box, bool wait_for_remote_box) { const struct dsync_mailbox_state *state; i_assert(brain->box_importer == NULL); i_assert(brain->box_exporter == NULL); i_assert(box->synced); brain->box = box; brain->pre_box_state = brain->state; if (wait_for_remote_box) { brain->box_send_state = DSYNC_BOX_STATE_MAILBOX; brain->box_recv_state = DSYNC_BOX_STATE_MAILBOX; } else { dsync_brain_sync_init_box_states(brain); } brain->local_dsync_box = *local_dsync_box; if (brain->dsync_box_pool != NULL) p_clear(brain->dsync_box_pool); else { brain->dsync_box_pool = pool_alloconly_create(MEMPOOL_GROWING"dsync brain box pool", 2048); } dsync_mailbox_cache_field_dup(&brain->local_dsync_box.cache_fields, &local_dsync_box->cache_fields, brain->dsync_box_pool); i_zero(&brain->remote_dsync_box); state = dsync_mailbox_state_find(brain, local_dsync_box->mailbox_guid); if (state != NULL) brain->mailbox_state = *state; else { i_zero(&brain->mailbox_state); memcpy(brain->mailbox_state.mailbox_guid, local_dsync_box->mailbox_guid, sizeof(brain->mailbox_state.mailbox_guid)); brain->mailbox_state.last_uidvalidity = local_dsync_box->uid_validity; } } static void dsync_brain_sync_mailbox_init_remote(struct dsync_brain *brain, const struct dsync_mailbox *remote_dsync_box) { enum dsync_mailbox_import_flags import_flags = 0; const struct dsync_mailbox_state *state; uint32_t last_common_uid; uint64_t last_common_modseq, last_common_pvt_modseq; i_assert(brain->box_importer == NULL); i_assert(brain->log_scan != NULL); i_assert(memcmp(brain->local_dsync_box.mailbox_guid, remote_dsync_box->mailbox_guid, sizeof(remote_dsync_box->mailbox_guid)) == 0); brain->remote_dsync_box = *remote_dsync_box; dsync_mailbox_cache_field_dup(&brain->remote_dsync_box.cache_fields, &remote_dsync_box->cache_fields, brain->dsync_box_pool); state = dsync_mailbox_state_find(brain, remote_dsync_box->mailbox_guid); if (state != NULL) { last_common_uid = state->last_common_uid; last_common_modseq = state->last_common_modseq; last_common_pvt_modseq = state->last_common_pvt_modseq; } else { last_common_uid = 0; last_common_modseq = 0; last_common_pvt_modseq = 0; } if (brain->mail_requests) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_WANT_MAIL_REQUESTS; if (brain->master_brain) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MASTER_BRAIN; if (brain->backup_recv && !brain->no_backup_overwrite) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_REVERT_LOCAL_CHANGES; if (brain->debug) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_DEBUG; if (brain->local_dsync_box.have_save_guids && (remote_dsync_box->have_save_guids || (brain->backup_recv && remote_dsync_box->have_guids))) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MAILS_HAVE_GUIDS; if (brain->local_dsync_box.have_only_guid128 || remote_dsync_box->have_only_guid128) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128; if (brain->no_notify) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY; if (brain->empty_hdr_workaround) import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND; brain->box_importer = brain->backup_send ? NULL : dsync_mailbox_import_init(brain->box, brain->virtual_all_box, brain->log_scan, last_common_uid, last_common_modseq, last_common_pvt_modseq, remote_dsync_box->uid_next, remote_dsync_box->first_recent_uid, remote_dsync_box->highest_modseq, remote_dsync_box->highest_pvt_modseq, brain->sync_since_timestamp, brain->sync_until_timestamp, brain->sync_max_size, brain->sync_flag, brain->import_commit_msgs_interval, import_flags, brain->hdr_hash_version, brain->hashed_headers); } int dsync_brain_sync_mailbox_open(struct dsync_brain *brain, const struct dsync_mailbox *remote_dsync_box) { struct mailbox_status status; enum dsync_mailbox_exporter_flags exporter_flags = 0; uint32_t last_common_uid, highest_wanted_uid; uint64_t last_common_modseq, last_common_pvt_modseq; const char *desync_reason = ""; bool pvt_too_old; int ret; i_assert(brain->log_scan == NULL); i_assert(brain->box_exporter == NULL); last_common_uid = brain->mailbox_state.last_common_uid; last_common_modseq = brain->mailbox_state.last_common_modseq; last_common_pvt_modseq = brain->mailbox_state.last_common_pvt_modseq; highest_wanted_uid = last_common_uid == 0 ? (uint32_t)-1 : last_common_uid; ret = dsync_transaction_log_scan_init(brain->box->view, brain->box->view_pvt, highest_wanted_uid, last_common_modseq, last_common_pvt_modseq, &brain->log_scan, &pvt_too_old); if (ret < 0) { i_error("Failed to read transaction log for mailbox %s", mailbox_get_vname(brain->box)); brain->failed = TRUE; return -1; } if (ret == 0) { if (pvt_too_old) { desync_reason = t_strdup_printf( "Private modseq %llu no longer in transaction log", (unsigned long long)last_common_pvt_modseq); } else { desync_reason = t_strdup_printf( "Modseq %llu no longer in transaction log", (unsigned long long)last_common_modseq); } } if (last_common_uid != 0) { mailbox_get_open_status(brain->box, STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ | STATUS_HIGHESTPVTMODSEQ, &status); /* if last_common_* is higher than our current ones it means that the incremental sync state is stale and we need to do a full resync */ if (status.uidnext < last_common_uid) { desync_reason = t_strdup_printf("uidnext %u < %u", status.uidnext, last_common_uid); ret = 0; } else if (status.highest_modseq < last_common_modseq) { desync_reason = t_strdup_printf("highest_modseq %llu < %llu", (unsigned long long)status.highest_modseq, (unsigned long long)last_common_modseq); ret = 0; } else if (status.highest_pvt_modseq < last_common_pvt_modseq) { desync_reason = t_strdup_printf("highest_pvt_modseq %llu < %llu", (unsigned long long)status.highest_pvt_modseq, (unsigned long long)last_common_pvt_modseq); ret = 0; } } if (ret == 0) { i_warning("Failed to do incremental sync for mailbox %s, " "retry with a full sync (%s)", mailbox_get_vname(brain->box), desync_reason); dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Incremental sync failed: %s", desync_reason)); brain->require_full_resync = TRUE; return 0; } if (!brain->mail_requests) exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_AUTO_EXPORT_MAILS; if (remote_dsync_box->have_save_guids && (brain->local_dsync_box.have_save_guids || (brain->backup_send && brain->local_dsync_box.have_guids))) exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_MAILS_HAVE_GUIDS; if (brain->no_mail_prefetch) exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_MINIMAL_DMAIL_FILL; if (brain->sync_since_timestamp > 0 || brain->sync_until_timestamp > 0) exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_TIMESTAMPS; if (brain->sync_max_size > 0) exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_VSIZES; if (remote_dsync_box->messages_count == 0) { /* remote mailbox is empty - we don't really need to export header hashes since they're not going to match anything anyway. */ exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_NO_HDR_HASHES; } brain->box_exporter = brain->backup_recv ? NULL : dsync_mailbox_export_init(brain->box, brain->log_scan, last_common_uid, exporter_flags, brain->hdr_hash_version, brain->hashed_headers); dsync_brain_sync_mailbox_init_remote(brain, remote_dsync_box); return 1; } void dsync_brain_sync_mailbox_deinit(struct dsync_brain *brain) { enum mail_error error; i_assert(brain->box != NULL); array_append(&brain->remote_mailbox_states, &brain->mailbox_state, 1); if (brain->box_exporter != NULL) { const char *errstr; i_assert(brain->failed || brain->require_full_resync || brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_CHANGED); if (dsync_mailbox_export_deinit(&brain->box_exporter, &errstr, &error) < 0) i_error("Mailbox export failed: %s", errstr); } if (brain->box_importer != NULL) { uint32_t last_common_uid, last_messages_count; uint64_t last_common_modseq, last_common_pvt_modseq; const char *changes_during_sync; bool require_full_resync; i_assert(brain->failed); (void)dsync_mailbox_import_deinit(&brain->box_importer, FALSE, &last_common_uid, &last_common_modseq, &last_common_pvt_modseq, &last_messages_count, &changes_during_sync, &require_full_resync, &brain->mail_error); if (require_full_resync) brain->require_full_resync = TRUE; } if (brain->log_scan != NULL) dsync_transaction_log_scan_deinit(&brain->log_scan); mailbox_free(&brain->box); brain->state = brain->pre_box_state; } static int dsync_box_get(struct mailbox *box, struct dsync_mailbox *dsync_box_r, enum mail_error *error_r) { const enum mailbox_status_items status_items = STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_FIRST_RECENT_UID | STATUS_HIGHESTMODSEQ | STATUS_HIGHESTPVTMODSEQ; const enum mailbox_metadata_items metadata_items = MAILBOX_METADATA_CACHE_FIELDS | MAILBOX_METADATA_GUID; struct mailbox_status status; struct mailbox_metadata metadata; const char *errstr; enum mail_error error; /* get metadata first, since it may autocreate the mailbox */ if (mailbox_get_metadata(box, metadata_items, &metadata) < 0 || mailbox_get_status(box, status_items, &status) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_NOTPOSSIBLE) { /* Mailbox isn't selectable, try the next one. We should have already caught \Noselect mailboxes, but check them anyway here. The NOTPOSSIBLE check is mainly for invalid mbox files. */ return 0; } i_error("Failed to access mailbox %s: %s", mailbox_get_vname(box), errstr); *error_r = error; return -1; } i_assert(status.uidvalidity != 0 || status.messages == 0); i_zero(dsync_box_r); memcpy(dsync_box_r->mailbox_guid, metadata.guid, sizeof(dsync_box_r->mailbox_guid)); dsync_box_r->uid_validity = status.uidvalidity; dsync_box_r->uid_next = status.uidnext; dsync_box_r->messages_count = status.messages; dsync_box_r->first_recent_uid = status.first_recent_uid; dsync_box_r->highest_modseq = status.highest_modseq; dsync_box_r->highest_pvt_modseq = status.highest_pvt_modseq; dsync_mailbox_cache_field_dup(&dsync_box_r->cache_fields, metadata.cache_fields, pool_datastack_create()); dsync_box_r->have_guids = status.have_guids; dsync_box_r->have_save_guids = status.have_save_guids; dsync_box_r->have_only_guid128 = status.have_only_guid128; return 1; } static bool dsync_brain_has_mailbox_state_changed(struct dsync_brain *brain, const struct dsync_mailbox *dsync_box) { const struct dsync_mailbox_state *state; if (brain->sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE) return TRUE; state = dsync_mailbox_state_find(brain, dsync_box->mailbox_guid); return state == NULL || state->last_uidvalidity != dsync_box->uid_validity || state->last_common_uid+1 != dsync_box->uid_next || state->last_common_modseq != dsync_box->highest_modseq || state->last_common_pvt_modseq != dsync_box->highest_pvt_modseq || state->last_messages_count != dsync_box->messages_count; } static int dsync_brain_try_next_mailbox(struct dsync_brain *brain, struct mailbox **box_r, struct dsync_mailbox *dsync_box_r) { enum mailbox_flags flags = 0; struct dsync_mailbox dsync_box; struct mailbox *box; struct dsync_mailbox_node *node; const char *vname = NULL; enum mail_error error; bool synced = FALSE; int ret; *box_r = NULL; while (dsync_mailbox_tree_iter_next(brain->local_tree_iter, &vname, &node)) { if (node->existence == DSYNC_MAILBOX_NODE_EXISTS && !guid_128_is_empty(node->mailbox_guid)) break; vname = NULL; } if (vname == NULL) { /* no more mailboxes */ dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter); return -1; } if (brain->backup_send) { /* make sure mailbox isn't modified */ flags |= MAILBOX_FLAG_READONLY; } box = mailbox_alloc(node->ns->list, vname, flags); for (;;) { if ((ret = dsync_box_get(box, &dsync_box, &error)) <= 0) { if (ret < 0) { brain->mail_error = error; brain->failed = TRUE; } mailbox_free(&box); return ret; } /* if mailbox's last_common_* state equals the current state, we can skip the mailbox */ if (!dsync_brain_has_mailbox_state_changed(brain, &dsync_box)) { if (brain->debug) { i_debug("brain %c: Skipping mailbox %s with unchanged state " "uidvalidity=%u uidnext=%u highestmodseq=%llu highestpvtmodseq=%llu messages=%u", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box.mailbox_guid), dsync_box.uid_validity, dsync_box.uid_next, (unsigned long long)dsync_box.highest_modseq, (unsigned long long)dsync_box.highest_pvt_modseq, dsync_box.messages_count); } mailbox_free(&box); return 0; } if (synced) { /* ok, the mailbox really changed */ break; } /* mailbox appears to have changed. do a full sync here and get the state again */ if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Can't sync mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &brain->mail_error)); brain->failed = TRUE; mailbox_free(&box); return -1; } synced = TRUE; } *box_r = box; *dsync_box_r = dsync_box; return 1; } static bool dsync_brain_next_mailbox(struct dsync_brain *brain, struct mailbox **box_r, struct dsync_mailbox *dsync_box_r) { int ret; if (brain->no_mail_sync) return FALSE; while ((ret = dsync_brain_try_next_mailbox(brain, box_r, dsync_box_r)) == 0) ; return ret > 0; } void dsync_brain_master_send_mailbox(struct dsync_brain *brain) { struct dsync_mailbox dsync_box; struct mailbox *box; i_assert(brain->master_brain); i_assert(brain->box == NULL); if (!dsync_brain_next_mailbox(brain, &box, &dsync_box)) { brain->state = DSYNC_STATE_FINISH; dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX); return; } /* start exporting this mailbox (wait for remote to start importing) */ dsync_ibc_send_mailbox(brain->ibc, &dsync_box); dsync_brain_sync_mailbox_init(brain, box, &dsync_box, TRUE); brain->state = DSYNC_STATE_SYNC_MAILS; } bool dsync_boxes_need_sync(struct dsync_brain *brain, const struct dsync_mailbox *box1, const struct dsync_mailbox *box2) { if (brain->no_mail_sync) return FALSE; if (brain->sync_type != DSYNC_BRAIN_SYNC_TYPE_CHANGED) return TRUE; return box1->highest_modseq != box2->highest_modseq || box1->highest_pvt_modseq != box2->highest_pvt_modseq || box1->messages_count != box2->messages_count || box1->uid_next != box2->uid_next || box1->uid_validity != box2->uid_validity || box1->first_recent_uid != box2->first_recent_uid; } static int mailbox_cache_field_name_cmp(const struct mailbox_cache_field *f1, const struct mailbox_cache_field *f2) { return strcmp(f1->name, f2->name); } static void dsync_cache_fields_update(const struct dsync_mailbox *local_box, const struct dsync_mailbox *remote_box, struct mailbox_update *update) { ARRAY_TYPE(mailbox_cache_field) local_sorted, remote_sorted, changes; const struct mailbox_cache_field *local_fields, *remote_fields; unsigned int li, ri, local_count, remote_count; time_t drop_older_timestamp; int ret; if (array_count(&remote_box->cache_fields) == 0) { /* remote has no cached fields. there's nothing to update. */ return; } t_array_init(&local_sorted, array_count(&local_box->cache_fields)); t_array_init(&remote_sorted, array_count(&remote_box->cache_fields)); array_append_array(&local_sorted, &local_box->cache_fields); array_append_array(&remote_sorted, &remote_box->cache_fields); array_sort(&local_sorted, mailbox_cache_field_name_cmp); array_sort(&remote_sorted, mailbox_cache_field_name_cmp); if (array_count(&local_sorted) == 0) { /* local has no cached fields. set them to same as remote. */ array_append_zero(&remote_sorted); update->cache_updates = array_idx(&remote_sorted, 0); return; } /* figure out what to change */ local_fields = array_get(&local_sorted, &local_count); remote_fields = array_get(&remote_sorted, &remote_count); t_array_init(&changes, local_count + remote_count); drop_older_timestamp = ioloop_time - MAIL_CACHE_FIELD_DROP_SECS; for (li = ri = 0; li < local_count || ri < remote_count; ) { ret = li == local_count ? 1 : ri == remote_count ? -1 : strcmp(local_fields[li].name, remote_fields[ri].name); if (ret == 0) { /* field exists in both local and remote */ const struct mailbox_cache_field *lf = &local_fields[li]; const struct mailbox_cache_field *rf = &remote_fields[ri]; if (lf->last_used > rf->last_used || (lf->last_used == rf->last_used && lf->decision > rf->decision)) { /* use local decision and timestamp */ } else { array_append(&changes, rf, 1); } li++; ri++; } else if (ret < 0) { /* remote field doesn't exist */ li++; } else { /* local field doesn't exist */ if (remote_fields[ri].last_used < drop_older_timestamp) { /* field hasn't be used for a long time, remote will probably drop this soon as well */ } else { array_append(&changes, &remote_fields[ri], 1); } ri++; } } i_assert(li == local_count && ri == remote_count); if (array_count(&changes) > 0) { array_append_zero(&changes); update->cache_updates = array_idx(&changes, 0); } } bool dsync_brain_mailbox_update_pre(struct dsync_brain *brain, struct mailbox *box, const struct dsync_mailbox *local_box, const struct dsync_mailbox *remote_box, const char **reason_r) { struct mailbox_update update; const struct dsync_mailbox_state *state; bool ret = TRUE; *reason_r = NULL; i_zero(&update); if (local_box->uid_validity != remote_box->uid_validity) { /* Keep the UIDVALIDITY for the mailbox that has more messages. If they equal, use the higher UIDVALIDITY. */ if (remote_box->messages_count > local_box->messages_count || (remote_box->messages_count == local_box->messages_count && remote_box->uid_validity > local_box->uid_validity)) update.uid_validity = remote_box->uid_validity; state = dsync_mailbox_state_find(brain, local_box->mailbox_guid); if (state != NULL && state->last_common_uid > 0) { /* we can't continue syncing this mailbox in this session, because the other side already started sending mailbox changes, but not for all mails. */ dsync_mailbox_state_remove(brain, local_box->mailbox_guid); *reason_r = "UIDVALIDITY changed during a stateful sync, need to restart"; ret = FALSE; } } dsync_cache_fields_update(local_box, remote_box, &update); if (update.uid_validity == 0 && update.cache_updates == NULL) { /* no changes */ return ret; } if (mailbox_update(box, &update) < 0) { i_error("Couldn't update mailbox %s metadata: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &brain->mail_error)); brain->failed = TRUE; } return ret; } static void dsync_brain_slave_send_mailbox_lost(struct dsync_brain *brain, const struct dsync_mailbox *dsync_box, bool ignore) { struct dsync_mailbox delete_box; if (brain->debug) { i_debug("brain %c: We don't have mailbox %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box->mailbox_guid)); } i_zero(&delete_box); memcpy(delete_box.mailbox_guid, dsync_box->mailbox_guid, sizeof(delete_box.mailbox_guid)); t_array_init(&delete_box.cache_fields, 0); if (ignore) delete_box.mailbox_ignore = TRUE; else delete_box.mailbox_lost = TRUE; dsync_ibc_send_mailbox(brain->ibc, &delete_box); } bool dsync_brain_slave_recv_mailbox(struct dsync_brain *brain) { const struct dsync_mailbox *dsync_box; struct dsync_mailbox local_dsync_box; struct mailbox *box; const char *errstr, *resync_reason; enum mail_error error; int ret; bool resync; i_assert(!brain->master_brain); i_assert(brain->box == NULL); if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0) return FALSE; if (ret < 0) { brain->state = DSYNC_STATE_FINISH; return TRUE; } if (dsync_brain_mailbox_alloc(brain, dsync_box->mailbox_guid, &box, &errstr, &error) < 0) { i_error("Couldn't allocate mailbox GUID %s: %s", guid_128_to_string(dsync_box->mailbox_guid), errstr); brain->mail_error = error; brain->failed = TRUE; return TRUE; } if (box == NULL) { /* mailbox was probably deleted/renamed during sync */ if (brain->backup_send && brain->no_backup_overwrite) { if (brain->debug) { i_debug("brain %c: Ignore nonexistent " "mailbox GUID %s with -1 sync", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box->mailbox_guid)); } dsync_brain_slave_send_mailbox_lost(brain, dsync_box, TRUE); return TRUE; } //FIXME: verify this from log, and if not log an error. dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Mailbox GUID %s was lost", guid_128_to_string(dsync_box->mailbox_guid))); dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE); return TRUE; } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Can't sync mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &brain->mail_error)); mailbox_free(&box); brain->failed = TRUE; return TRUE; } if ((ret = dsync_box_get(box, &local_dsync_box, &error)) <= 0) { mailbox_free(&box); if (ret < 0) { brain->mail_error = error; brain->failed = TRUE; return TRUE; } /* another process just deleted this mailbox? */ if (brain->debug) { i_debug("brain %c: Skipping lost mailbox %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box->mailbox_guid)); } dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE); return TRUE; } i_assert(local_dsync_box.uid_validity != 0); i_assert(memcmp(dsync_box->mailbox_guid, local_dsync_box.mailbox_guid, sizeof(dsync_box->mailbox_guid)) == 0); resync = !dsync_brain_mailbox_update_pre(brain, box, &local_dsync_box, dsync_box, &resync_reason); if (!dsync_boxes_need_sync(brain, &local_dsync_box, dsync_box)) { /* no fields appear to have changed, skip this mailbox */ if (brain->debug) { i_debug("brain %c: Skipping unchanged mailbox %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box->mailbox_guid)); } dsync_ibc_send_mailbox(brain->ibc, &local_dsync_box); mailbox_free(&box); return TRUE; } /* start export/import */ dsync_brain_sync_mailbox_init(brain, box, &local_dsync_box, FALSE); if ((ret = dsync_brain_sync_mailbox_open(brain, dsync_box)) < 0) return TRUE; if (resync) dsync_brain_set_changes_during_sync(brain, resync_reason); if (ret == 0 || resync) { brain->require_full_resync = TRUE; dsync_brain_sync_mailbox_deinit(brain); dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE); return TRUE; } dsync_ibc_send_mailbox(brain->ibc, &local_dsync_box); brain->state = DSYNC_STATE_SYNC_MAILS; return TRUE; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-transaction-log-scan.c0000644000175000017500000004054013123174404021165 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-index-modseq.h" #include "mail-storage-private.h" #include "dsync-mail.h" #include "dsync-mailbox.h" #include "dsync-transaction-log-scan.h" struct dsync_transaction_log_scan { pool_t pool; HASH_TABLE_TYPE(dsync_uid_mail_change) changes; HASH_TABLE_TYPE(dsync_attr_change) attr_changes; struct mail_index_view *view; uint32_t highest_wanted_uid; uint32_t last_log_seq; uoff_t last_log_offset; bool returned_all_changes; }; static bool ATTR_NOWARN_UNUSED_RESULT export_change_get(struct dsync_transaction_log_scan *ctx, uint32_t uid, enum dsync_mail_change_type type, struct dsync_mail_change **change_r) { struct dsync_mail_change *change; const char *orig_guid; i_assert(uid > 0); i_assert(type != DSYNC_MAIL_CHANGE_TYPE_SAVE); *change_r = NULL; if (uid > ctx->highest_wanted_uid) return FALSE; change = hash_table_lookup(ctx->changes, POINTER_CAST(uid)); if (change == NULL) { /* first change for this UID */ change = p_new(ctx->pool, struct dsync_mail_change, 1); change->uid = uid; change->type = type; hash_table_insert(ctx->changes, POINTER_CAST(uid), change); } else if (type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { /* expunge overrides flag changes */ orig_guid = change->guid; i_zero(change); change->type = type; change->uid = uid; change->guid = orig_guid; } else if (change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { /* already expunged, this change doesn't matter */ return FALSE; } else { /* another flag update */ } *change_r = change; return TRUE; } static void log_add_expunge(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr) { const struct mail_transaction_expunge *rec = data, *end; struct dsync_mail_change *change; uint32_t uid; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* this is simply a request for expunge */ return; } end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { for (uid = rec->uid1; uid <= rec->uid2; uid++) { export_change_get(ctx, uid, DSYNC_MAIL_CHANGE_TYPE_EXPUNGE, &change); } } } static bool log_add_expunge_uid(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr, uint32_t uid) { const struct mail_transaction_expunge *rec = data, *end; struct dsync_mail_change *change; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* this is simply a request for expunge */ return FALSE; } end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { if (uid >= rec->uid1 && uid <= rec->uid2) { export_change_get(ctx, uid, DSYNC_MAIL_CHANGE_TYPE_EXPUNGE, &change); return TRUE; } } return FALSE; } static void log_add_expunge_guid(struct dsync_transaction_log_scan *ctx, struct mail_index_view *view, const void *data, const struct mail_transaction_header *hdr) { const struct mail_transaction_expunge_guid *rec = data, *end; struct dsync_mail_change *change; uint32_t seq; bool external; external = (hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0; end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { if (!external && mail_index_lookup_seq(view, rec->uid, &seq)) { /* expunge request that hasn't been actually done yet. we check non-external ones because they might have the GUID while external ones don't. */ continue; } if (export_change_get(ctx, rec->uid, DSYNC_MAIL_CHANGE_TYPE_EXPUNGE, &change) && !guid_128_is_empty(rec->guid_128)) T_BEGIN { change->guid = p_strdup(ctx->pool, guid_128_to_string(rec->guid_128)); } T_END; } } static bool log_add_expunge_guid_uid(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr, uint32_t uid) { const struct mail_transaction_expunge_guid *rec = data, *end; struct dsync_mail_change *change; /* we're assuming UID is already known to be expunged */ end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { if (rec->uid != uid) continue; if (!export_change_get(ctx, rec->uid, DSYNC_MAIL_CHANGE_TYPE_EXPUNGE, &change)) i_unreached(); if (!guid_128_is_empty(rec->guid_128)) T_BEGIN { change->guid = p_strdup(ctx->pool, guid_128_to_string(rec->guid_128)); } T_END; return TRUE; } return FALSE; } static void log_add_flag_update(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr) { const struct mail_transaction_flag_update *rec = data, *end; struct dsync_mail_change *change; uint32_t uid; end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { for (uid = rec->uid1; uid <= rec->uid2; uid++) { if (export_change_get(ctx, uid, DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE, &change)) { change->add_flags |= rec->add_flags; change->remove_flags &= ~rec->add_flags; change->remove_flags |= rec->remove_flags; change->add_flags &= ~rec->remove_flags; } } } } static void log_add_keyword_reset(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr) { const struct mail_transaction_keyword_reset *rec = data, *end; struct dsync_mail_change *change; uint32_t uid; end = CONST_PTR_OFFSET(data, hdr->size); for (; rec != end; rec++) { for (uid = rec->uid1; uid <= rec->uid2; uid++) { if (!export_change_get(ctx, uid, DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE, &change)) continue; change->keywords_reset = TRUE; if (array_is_created(&change->keyword_changes)) array_clear(&change->keyword_changes); } } } static void keywords_change_remove(struct dsync_mail_change *change, const char *name) { const char *const *changes; unsigned int i, count; changes = array_get(&change->keyword_changes, &count); for (i = 0; i < count; i++) { if (strcmp(changes[i]+1, name) == 0) { array_delete(&change->keyword_changes, i, 1); break; } } } static void log_add_keyword_update(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr) { const struct mail_transaction_keyword_update *rec = data; struct dsync_mail_change *change; const char *kw_name, *change_str; const uint32_t *uids, *end; unsigned int uids_offset; uint32_t uid; uids_offset = sizeof(*rec) + rec->name_size; if ((uids_offset % 4) != 0) uids_offset += 4 - (uids_offset % 4); kw_name = t_strndup((const void *)(rec+1), rec->name_size); switch (rec->modify_type) { case MODIFY_ADD: change_str = p_strdup_printf(ctx->pool, "%c%s", KEYWORD_CHANGE_ADD, kw_name); break; case MODIFY_REMOVE: change_str = p_strdup_printf(ctx->pool, "%c%s", KEYWORD_CHANGE_REMOVE, kw_name); break; default: i_unreached(); } uids = CONST_PTR_OFFSET(rec, uids_offset); end = CONST_PTR_OFFSET(rec, hdr->size); for (; uids < end; uids += 2) { for (uid = uids[0]; uid <= uids[1]; uid++) { if (!export_change_get(ctx, uid, DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE, &change)) continue; if (!array_is_created(&change->keyword_changes)) { p_array_init(&change->keyword_changes, ctx->pool, 4); } else { keywords_change_remove(change, kw_name); } array_append(&change->keyword_changes, &change_str, 1); } } } static void log_add_modseq_update(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr, bool pvt_scan) { const struct mail_transaction_modseq_update *rec = data, *end; struct dsync_mail_change *change; uint64_t modseq; /* update message's modseq, possibly by creating an empty flag change */ end = CONST_PTR_OFFSET(rec, hdr->size); for (; rec != end; rec++) { if (rec->uid == 0) { /* highestmodseq update */ continue; } if (!export_change_get(ctx, rec->uid, DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE, &change)) continue; modseq = rec->modseq_low32 | ((uint64_t)rec->modseq_high32 << 32); if (!pvt_scan) { if (change->modseq < modseq) change->modseq = modseq; } else { if (change->pvt_modseq < modseq) change->pvt_modseq = modseq; } } } static void log_add_attribute_update_key(struct dsync_transaction_log_scan *ctx, const char *attr_change, uint64_t modseq) { struct dsync_mailbox_attribute lookup_attr, *attr; i_assert(strlen(attr_change) > 2); /* checked by lib-index */ lookup_attr.type = attr_change[1] == 'p' ? MAIL_ATTRIBUTE_TYPE_PRIVATE : MAIL_ATTRIBUTE_TYPE_SHARED; lookup_attr.key = attr_change+2; attr = hash_table_lookup(ctx->attr_changes, &lookup_attr); if (attr == NULL) { attr = p_new(ctx->pool, struct dsync_mailbox_attribute, 1); attr->type = lookup_attr.type; attr->key = p_strdup(ctx->pool, lookup_attr.key); hash_table_insert(ctx->attr_changes, attr, attr); } attr->deleted = attr_change[0] == '-'; attr->modseq = modseq; } static void log_add_attribute_update(struct dsync_transaction_log_scan *ctx, const void *data, const struct mail_transaction_header *hdr, uint64_t modseq) { const char *attr_changes = data; unsigned int i; for (i = 0; i < hdr->size && attr_changes[i] != '\0'; ) { log_add_attribute_update_key(ctx, attr_changes+i, modseq); i += strlen(attr_changes+i) + 1; } } static int dsync_log_set(struct dsync_transaction_log_scan *ctx, struct mail_index_view *view, bool pvt_scan, struct mail_transaction_log_view *log_view, uint64_t modseq) { uint32_t log_seq, end_seq; uoff_t log_offset, end_offset; const char *reason; bool reset; int ret; end_seq = view->log_file_head_seq; end_offset = view->log_file_head_offset; if (modseq != 0 && mail_index_modseq_get_next_log_offset(view, modseq, &log_seq, &log_offset)) { /* scan the view only up to end of the current view. if there are more changes, we don't care about them until the next sync. */ ret = mail_transaction_log_view_set(log_view, log_seq, log_offset, end_seq, end_offset, &reset, &reason); if (ret != 0) return ret; } /* return everything we've got (until the end of the view) */ if (!pvt_scan) ctx->returned_all_changes = TRUE; if (mail_transaction_log_view_set_all(log_view) < 0) return -1; mail_transaction_log_view_get_prev_pos(log_view, &log_seq, &log_offset); if (log_seq > end_seq || (log_seq == end_seq && log_offset > end_offset)) { end_seq = log_seq; end_offset = log_offset; } ret = mail_transaction_log_view_set(log_view, log_seq, log_offset, end_seq, end_offset, &reset, &reason); if (ret == 0) { /* we shouldn't get here. _view_set_all() already reserved all the log files, the _view_set() only removed unwanted ones. */ i_error("%s: Couldn't set transaction log view (seq %u..%u): %s", view->index->filepath, log_seq, end_seq, reason); ret = -1; } if (ret < 0) return -1; if (modseq != 0) { /* we didn't see all the changes that we wanted to */ return 0; } return 1; } static int dsync_log_scan(struct dsync_transaction_log_scan *ctx, struct mail_index_view *view, uint64_t modseq, bool pvt_scan) { struct mail_transaction_log_view *log_view; const struct mail_transaction_header *hdr; const void *data; uint32_t file_seq, max_seq; uoff_t file_offset, max_offset; uint64_t cur_modseq; int ret; log_view = mail_transaction_log_view_open(view->index->log); if ((ret = dsync_log_set(ctx, view, pvt_scan, log_view, modseq)) < 0) { mail_transaction_log_view_close(&log_view); return -1; } /* read the log only up to current position in view */ max_seq = view->log_file_expunge_seq; max_offset = view->log_file_expunge_offset; mail_transaction_log_view_get_prev_pos(log_view, &file_seq, &file_offset); while (mail_transaction_log_view_next(log_view, &hdr, &data) > 0) { mail_transaction_log_view_get_prev_pos(log_view, &file_seq, &file_offset); if (file_offset >= max_offset && file_seq == max_seq) break; if ((hdr->type & MAIL_TRANSACTION_SYNC) != 0) { /* ignore changes done by dsync, unless we can get expunged message's GUID from it */ if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) != MAIL_TRANSACTION_EXPUNGE_GUID) continue; } switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: if (!pvt_scan) log_add_expunge(ctx, data, hdr); break; case MAIL_TRANSACTION_EXPUNGE_GUID: if (!pvt_scan) log_add_expunge_guid(ctx, view, data, hdr); break; case MAIL_TRANSACTION_FLAG_UPDATE: log_add_flag_update(ctx, data, hdr); break; case MAIL_TRANSACTION_KEYWORD_RESET: log_add_keyword_reset(ctx, data, hdr); break; case MAIL_TRANSACTION_KEYWORD_UPDATE: T_BEGIN { log_add_keyword_update(ctx, data, hdr); } T_END; break; case MAIL_TRANSACTION_MODSEQ_UPDATE: log_add_modseq_update(ctx, data, hdr, pvt_scan); break; case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: cur_modseq = mail_transaction_log_view_get_prev_modseq(log_view); log_add_attribute_update(ctx, data, hdr, cur_modseq); break; } } if (!pvt_scan) { ctx->last_log_seq = file_seq; ctx->last_log_offset = file_offset; } mail_transaction_log_view_close(&log_view); return ret; } static int dsync_mailbox_attribute_cmp(const struct dsync_mailbox_attribute *attr1, const struct dsync_mailbox_attribute *attr2) { if (attr1->type < attr2->type) return -1; if (attr1->type > attr2->type) return 1; return strcmp(attr1->key, attr2->key); } static unsigned int dsync_mailbox_attribute_hash(const struct dsync_mailbox_attribute *attr) { return str_hash(attr->key) ^ attr->type; } int dsync_transaction_log_scan_init(struct mail_index_view *view, struct mail_index_view *pvt_view, uint32_t highest_wanted_uid, uint64_t modseq, uint64_t pvt_modseq, struct dsync_transaction_log_scan **scan_r, bool *pvt_too_old_r) { struct dsync_transaction_log_scan *ctx; pool_t pool; int ret, ret2; *pvt_too_old_r = FALSE; pool = pool_alloconly_create(MEMPOOL_GROWING"dsync transaction log scan", 10240); ctx = p_new(pool, struct dsync_transaction_log_scan, 1); ctx->pool = pool; hash_table_create_direct(&ctx->changes, pool, 0); hash_table_create(&ctx->attr_changes, pool, 0, dsync_mailbox_attribute_hash, dsync_mailbox_attribute_cmp); ctx->view = view; ctx->highest_wanted_uid = highest_wanted_uid; if ((ret = dsync_log_scan(ctx, view, modseq, FALSE)) < 0) return -1; if (pvt_view != NULL) { if ((ret2 = dsync_log_scan(ctx, pvt_view, pvt_modseq, TRUE)) < 0) return -1; if (ret2 == 0) { ret = 0; *pvt_too_old_r = TRUE; } } *scan_r = ctx; return ret; } HASH_TABLE_TYPE(dsync_uid_mail_change) dsync_transaction_log_scan_get_hash(struct dsync_transaction_log_scan *scan) { return scan->changes; } HASH_TABLE_TYPE(dsync_attr_change) dsync_transaction_log_scan_get_attr_hash(struct dsync_transaction_log_scan *scan) { return scan->attr_changes; } bool dsync_transaction_log_scan_has_all_changes(struct dsync_transaction_log_scan *scan) { return scan->returned_all_changes; } struct dsync_mail_change * dsync_transaction_log_scan_find_new_expunge(struct dsync_transaction_log_scan *scan, uint32_t uid) { struct mail_transaction_log_view *log_view; const struct mail_transaction_header *hdr; const void *data; const char *reason; bool reset, found = FALSE; i_assert(uid > 0); if (scan->highest_wanted_uid < uid) scan->highest_wanted_uid = uid; log_view = mail_transaction_log_view_open(scan->view->index->log); if (mail_transaction_log_view_set(log_view, scan->last_log_seq, scan->last_log_offset, (uint32_t)-1, (uoff_t)-1, &reset, &reason) > 0) { while (!found && mail_transaction_log_view_next(log_view, &hdr, &data) > 0) { switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: if (log_add_expunge_uid(scan, data, hdr, uid)) found = TRUE; break; case MAIL_TRANSACTION_EXPUNGE_GUID: if (log_add_expunge_guid_uid(scan, data, hdr, uid)) found = TRUE; break; } } } mail_transaction_log_view_close(&log_view); return !found ? NULL : hash_table_lookup(scan->changes, POINTER_CAST(uid)); } void dsync_transaction_log_scan_deinit(struct dsync_transaction_log_scan **_scan) { struct dsync_transaction_log_scan *scan = *_scan; *_scan = NULL; hash_table_destroy(&scan->changes); hash_table_destroy(&scan->attr_changes); pool_unref(&scan->pool); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-deserializer.h0000644000175000017500000000206613123174404017627 00000000000000#ifndef DSYNC_DESERIALIZER_H #define DSYNC_DESERIALIZER_H struct dsync_deserializer; struct dsync_deserializer_decoder; int dsync_deserializer_init(const char *name, const char *const *required_fields, const char *header_line, struct dsync_deserializer **deserializer_r, const char **error_r); void dsync_deserializer_deinit(struct dsync_deserializer **deserializer); int dsync_deserializer_decode_begin(struct dsync_deserializer *deserializer, const char *input, struct dsync_deserializer_decoder **decoder_r, const char **error_r); bool dsync_deserializer_decode_try(struct dsync_deserializer_decoder *decoder, const char *key, const char **value_r); /* key must be in required fields. The return value is never NULL. */ const char * dsync_deserializer_decode_get(struct dsync_deserializer_decoder *decoder, const char *key); const char * dsync_deserializer_decoder_get_name(struct dsync_deserializer_decoder *decoder); void dsync_deserializer_decode_finish(struct dsync_deserializer_decoder **decoder); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain-mails.c0000644000175000017500000003101713165463624017347 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "dsync-ibc.h" #include "dsync-mail.h" #include "dsync-mailbox-import.h" #include "dsync-mailbox-export.h" #include "dsync-brain-private.h" const char *dsync_box_state_names[DSYNC_BOX_STATE_DONE+1] = { "mailbox", "changes", "attributes", "mail_requests", "mails", "recv_last_common", "done" }; static bool dsync_brain_master_sync_recv_mailbox(struct dsync_brain *brain) { const struct dsync_mailbox *dsync_box; const char *resync_reason; enum dsync_ibc_recv_ret ret; bool resync; i_assert(brain->master_brain); if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { i_error("Remote sent end-of-list instead of a mailbox"); brain->failed = TRUE; return TRUE; } if (memcmp(dsync_box->mailbox_guid, brain->local_dsync_box.mailbox_guid, sizeof(dsync_box->mailbox_guid)) != 0) { i_error("Remote sent mailbox with a wrong GUID"); brain->failed = TRUE; return TRUE; } if (dsync_box->mailbox_ignore) { /* ignore this box */ if (brain->debug) i_debug("brain %c: Ignoring missing remote box GUID %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(dsync_box->mailbox_guid)); dsync_brain_sync_mailbox_deinit(brain); return TRUE; } if (dsync_box->mailbox_lost) { /* remote lost the mailbox. it's probably already deleted, but verify it on next sync just to be sure */ dsync_brain_set_changes_during_sync(brain, t_strdup_printf( "Remote lost mailbox GUID %s (maybe it was just deleted?)", guid_128_to_string(dsync_box->mailbox_guid))); brain->require_full_resync = TRUE; dsync_brain_sync_mailbox_deinit(brain); return TRUE; } resync = !dsync_brain_mailbox_update_pre(brain, brain->box, &brain->local_dsync_box, dsync_box, &resync_reason); if (!dsync_boxes_need_sync(brain, &brain->local_dsync_box, dsync_box)) { /* no fields appear to have changed, skip this mailbox */ dsync_brain_sync_mailbox_deinit(brain); return TRUE; } if ((ret = dsync_brain_sync_mailbox_open(brain, dsync_box)) < 0) return TRUE; if (resync) dsync_brain_set_changes_during_sync(brain, resync_reason); if (ret == 0 || resync) { brain->require_full_resync = TRUE; brain->failed = TRUE; dsync_brain_sync_mailbox_deinit(brain); return TRUE; } dsync_brain_sync_init_box_states(brain); return TRUE; } static bool dsync_brain_recv_mailbox_attribute(struct dsync_brain *brain) { const struct dsync_mailbox_attribute *attr; struct istream *input; enum dsync_ibc_recv_ret ret; if ((ret = dsync_ibc_recv_mailbox_attribute(brain->ibc, &attr)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { brain->box_recv_state = DSYNC_BOX_STATE_CHANGES; return TRUE; } if (dsync_mailbox_import_attribute(brain->box_importer, attr) < 0) brain->failed = TRUE; input = attr->value_stream; if (input != NULL) i_stream_unref(&input); return TRUE; } static void dsync_brain_send_end_of_list(struct dsync_brain *brain, enum dsync_ibc_eol_type type) { i_assert(!brain->failed); dsync_ibc_send_end_of_list(brain->ibc, type); } static int dsync_brain_export_deinit(struct dsync_brain *brain) { const char *errstr; enum mail_error error; if (dsync_mailbox_export_deinit(&brain->box_exporter, &errstr, &error) < 0) { i_error("Exporting mailbox %s failed: %s", mailbox_get_vname(brain->box), errstr); brain->mail_error = error; brain->failed = TRUE; return -1; } return 0; } static void dsync_brain_send_mailbox_attribute(struct dsync_brain *brain) { const struct dsync_mailbox_attribute *attr; int ret; while ((ret = dsync_mailbox_export_next_attr(brain->box_exporter, &attr)) > 0) { if (dsync_ibc_send_mailbox_attribute(brain->ibc, attr) == 0) return; } if (ret < 0) { if (dsync_brain_export_deinit(brain) == 0) i_unreached(); return; } dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAILBOX_ATTRIBUTE); brain->box_send_state = DSYNC_BOX_STATE_CHANGES; } static bool dsync_brain_recv_mail_change(struct dsync_brain *brain) { const struct dsync_mail_change *change; enum dsync_ibc_recv_ret ret; if ((ret = dsync_ibc_recv_change(brain->ibc, &change)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { if (dsync_mailbox_import_changes_finish(brain->box_importer) < 0) brain->failed = TRUE; if (brain->mail_requests && brain->box_exporter != NULL) brain->box_recv_state = DSYNC_BOX_STATE_MAIL_REQUESTS; else brain->box_recv_state = DSYNC_BOX_STATE_MAILS; return TRUE; } if (dsync_mailbox_import_change(brain->box_importer, change) < 0) brain->failed = TRUE; return TRUE; } static void dsync_brain_send_mail_change(struct dsync_brain *brain) { const struct dsync_mail_change *change; int ret; while ((ret = dsync_mailbox_export_next(brain->box_exporter, &change)) > 0) { if (dsync_ibc_send_change(brain->ibc, change) == 0) return; } if (ret < 0) { if (dsync_brain_export_deinit(brain) == 0) i_unreached(); return; } dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAIL_CHANGES); if (brain->mail_requests && brain->box_importer != NULL) brain->box_send_state = DSYNC_BOX_STATE_MAIL_REQUESTS; else brain->box_send_state = DSYNC_BOX_STATE_MAILS; } static bool dsync_brain_recv_mail_request(struct dsync_brain *brain) { const struct dsync_mail_request *request; enum dsync_ibc_recv_ret ret; i_assert(brain->mail_requests); i_assert(brain->box_exporter != NULL); if ((ret = dsync_ibc_recv_mail_request(brain->ibc, &request)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { brain->box_recv_state = brain->box_importer != NULL ? DSYNC_BOX_STATE_MAILS : DSYNC_BOX_STATE_RECV_LAST_COMMON; return TRUE; } dsync_mailbox_export_want_mail(brain->box_exporter, request); return TRUE; } static bool dsync_brain_send_mail_request(struct dsync_brain *brain) { const struct dsync_mail_request *request; i_assert(brain->mail_requests); while ((request = dsync_mailbox_import_next_request(brain->box_importer)) != NULL) { if (dsync_ibc_send_mail_request(brain->ibc, request) == 0) return TRUE; } if (brain->box_recv_state < DSYNC_BOX_STATE_MAIL_REQUESTS) return FALSE; dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAIL_REQUESTS); if (brain->box_exporter != NULL) brain->box_send_state = DSYNC_BOX_STATE_MAILS; else { i_assert(brain->box_recv_state != DSYNC_BOX_STATE_DONE); brain->box_send_state = DSYNC_BOX_STATE_DONE; } return TRUE; } static void dsync_brain_sync_half_finished(struct dsync_brain *brain) { struct dsync_mailbox_state state; const char *changes_during_sync; bool require_full_resync; if (brain->box_recv_state < DSYNC_BOX_STATE_RECV_LAST_COMMON || brain->box_send_state < DSYNC_BOX_STATE_RECV_LAST_COMMON) return; /* finished with this mailbox */ i_zero(&state); memcpy(state.mailbox_guid, brain->local_dsync_box.mailbox_guid, sizeof(state.mailbox_guid)); state.last_uidvalidity = brain->local_dsync_box.uid_validity; if (brain->box_importer == NULL) { /* this mailbox didn't exist on remote */ state.last_common_uid = brain->local_dsync_box.uid_next-1; state.last_common_modseq = brain->local_dsync_box.highest_modseq; state.last_common_pvt_modseq = brain->local_dsync_box.highest_pvt_modseq; state.last_messages_count = brain->local_dsync_box.messages_count; } else { if (dsync_mailbox_import_deinit(&brain->box_importer, !brain->failed, &state.last_common_uid, &state.last_common_modseq, &state.last_common_pvt_modseq, &state.last_messages_count, &changes_during_sync, &require_full_resync, &brain->mail_error) < 0) { if (require_full_resync) { /* don't treat this as brain failure or the state won't be sent to the other brain. this also means we'll continue syncing the following mailboxes. */ brain->require_full_resync = TRUE; } else { brain->failed = TRUE; } } if (changes_during_sync != NULL) { state.changes_during_sync = TRUE; dsync_brain_set_changes_during_sync(brain, changes_during_sync); } } if (brain->require_full_resync) { state.last_uidvalidity = 0; state.changes_during_sync = TRUE; } brain->mailbox_state = state; dsync_ibc_send_mailbox_state(brain->ibc, &state); } static bool dsync_brain_recv_mail(struct dsync_brain *brain) { struct dsync_mail *mail; enum dsync_ibc_recv_ret ret; if ((ret = dsync_ibc_recv_mail(brain->ibc, &mail)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { brain->box_recv_state = DSYNC_BOX_STATE_RECV_LAST_COMMON; if (brain->box_exporter != NULL && brain->box_send_state >= DSYNC_BOX_STATE_RECV_LAST_COMMON) { if (dsync_brain_export_deinit(brain) < 0) return TRUE; } dsync_brain_sync_half_finished(brain); return TRUE; } if (brain->debug) { i_debug("brain %c: import mail uid %u guid %s", brain->master_brain ? 'M' : 'S', mail->uid, mail->guid); } if (dsync_mailbox_import_mail(brain->box_importer, mail) < 0) brain->failed = TRUE; if (mail->input != NULL) i_stream_unref(&mail->input); return TRUE; } static bool dsync_brain_send_mail(struct dsync_brain *brain) { const struct dsync_mail *mail; if (brain->mail_requests && brain->box_recv_state < DSYNC_BOX_STATE_MAILS) { /* wait for mail requests to finish. we could already start exporting, but then we're going to do quite a lot of separate searches. especially with pipe backend we'd do a separate search for each mail. */ return FALSE; } while (dsync_mailbox_export_next_mail(brain->box_exporter, &mail) > 0) { if (dsync_ibc_send_mail(brain->ibc, mail) == 0) return TRUE; } if (dsync_brain_export_deinit(brain) < 0) return TRUE; brain->box_send_state = DSYNC_BOX_STATE_DONE; dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAILS); dsync_brain_sync_half_finished(brain); return TRUE; } static bool dsync_brain_recv_last_common(struct dsync_brain *brain) { enum dsync_ibc_recv_ret ret; struct dsync_mailbox_state state; if ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) == 0) return FALSE; if (ret == DSYNC_IBC_RECV_RET_FINISHED) { i_error("Remote sent end-of-list instead of a mailbox state"); brain->failed = TRUE; return TRUE; } i_assert(brain->box_send_state == DSYNC_BOX_STATE_DONE); i_assert(memcmp(state.mailbox_guid, brain->local_dsync_box.mailbox_guid, sizeof(state.mailbox_guid)) == 0); /* normally the last_common_* values should be the same in local and remote, but during unexpected changes they may differ. use the values that are lower as the final state. */ if (brain->mailbox_state.last_common_uid > state.last_common_uid) brain->mailbox_state.last_common_uid = state.last_common_uid; if (brain->mailbox_state.last_common_modseq > state.last_common_modseq) brain->mailbox_state.last_common_modseq = state.last_common_modseq; if (brain->mailbox_state.last_common_pvt_modseq > state.last_common_pvt_modseq) brain->mailbox_state.last_common_pvt_modseq = state.last_common_pvt_modseq; if (state.changes_during_sync) brain->changes_during_remote_sync = TRUE; dsync_brain_sync_mailbox_deinit(brain); return TRUE; } bool dsync_brain_sync_mails(struct dsync_brain *brain) { bool changed = FALSE; i_assert(brain->box != NULL); switch (brain->box_recv_state) { case DSYNC_BOX_STATE_MAILBOX: changed = dsync_brain_master_sync_recv_mailbox(brain); break; case DSYNC_BOX_STATE_ATTRIBUTES: changed = dsync_brain_recv_mailbox_attribute(brain); break; case DSYNC_BOX_STATE_CHANGES: changed = dsync_brain_recv_mail_change(brain); break; case DSYNC_BOX_STATE_MAIL_REQUESTS: changed = dsync_brain_recv_mail_request(brain); break; case DSYNC_BOX_STATE_MAILS: changed = dsync_brain_recv_mail(brain); break; case DSYNC_BOX_STATE_RECV_LAST_COMMON: changed = dsync_brain_recv_last_common(brain); break; case DSYNC_BOX_STATE_DONE: break; } if (!dsync_ibc_is_send_queue_full(brain->ibc) && !brain->failed) { switch (brain->box_send_state) { case DSYNC_BOX_STATE_MAILBOX: /* wait for mailbox to be received first */ break; case DSYNC_BOX_STATE_ATTRIBUTES: dsync_brain_send_mailbox_attribute(brain); changed = TRUE; break; case DSYNC_BOX_STATE_CHANGES: dsync_brain_send_mail_change(brain); changed = TRUE; break; case DSYNC_BOX_STATE_MAIL_REQUESTS: if (dsync_brain_send_mail_request(brain)) changed = TRUE; break; case DSYNC_BOX_STATE_MAILS: if (dsync_brain_send_mail(brain)) changed = TRUE; break; case DSYNC_BOX_STATE_RECV_LAST_COMMON: i_unreached(); case DSYNC_BOX_STATE_DONE: break; } } return changed; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mail.c0000644000175000017500000000776513165463543016110 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hex-binary.h" #include "md5.h" #include "istream.h" #include "istream-crlf.h" #include "message-header-hash.h" #include "message-size.h" #include "mail-storage.h" #include "dsync-mail.h" struct mailbox_header_lookup_ctx * dsync_mail_get_hash_headers(struct mailbox *box, const char *const *hashed_headers) { return mailbox_header_lookup_init(box, hashed_headers); } int dsync_mail_get_hdr_hash(struct mail *mail, unsigned int version, const char *const *hashed_headers, const char **hdr_hash_r) { struct istream *hdr_input, *input; struct mailbox_header_lookup_ctx *hdr_ctx; struct message_header_hash_context hash_ctx; struct md5_context md5_ctx; unsigned char md5_result[MD5_RESULTLEN]; const unsigned char *data; size_t size; ssize_t sret; int ret = 0; hdr_ctx = mailbox_header_lookup_init(mail->box, hashed_headers); ret = mail_get_header_stream(mail, hdr_ctx, &hdr_input); mailbox_header_lookup_unref(&hdr_ctx); if (ret < 0) return -1; input = i_stream_create_lf(hdr_input); md5_init(&md5_ctx); i_zero(&hash_ctx); while ((sret = i_stream_read_more(input, &data, &size)) > 0) { message_header_hash_more(&hash_ctx, &hash_method_md5, &md5_ctx, version, data, size); i_stream_skip(input, size); } i_assert(sret == -1); if (input->stream_errno != 0) ret = -1; i_stream_unref(&input); md5_final(&md5_ctx, md5_result); *hdr_hash_r = binary_to_hex(md5_result, sizeof(md5_result)); return ret; } int dsync_mail_fill(struct mail *mail, bool minimal_fill, struct dsync_mail *dmail_r, const char **error_field_r) { const char *guid; i_zero(dmail_r); if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) < 0) { *error_field_r = "GUID"; return -1; } dmail_r->guid = guid; dmail_r->uid = mail->uid; dmail_r->input_mail = mail; dmail_r->input_mail_uid = mail->uid; if (mail_get_save_date(mail, &dmail_r->saved_date) < 0) { *error_field_r = "saved-date"; return -1; } if (!minimal_fill) return dsync_mail_fill_nonminimal(mail, dmail_r, error_field_r); dmail_r->minimal_fields = TRUE; return 0; } int dsync_mail_fill_nonminimal(struct mail *mail, struct dsync_mail *dmail_r, const char **error_field_r) { const char *str; if (mail_get_stream(mail, NULL, NULL, &dmail_r->input) < 0) { *error_field_r = "body"; return -1; } if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &dmail_r->pop3_uidl) < 0) { *error_field_r = "pop3-uidl"; return -1; } if (mail_get_special(mail, MAIL_FETCH_POP3_ORDER, &str) < 0) { *error_field_r = "pop3-order"; return -1; } if (*str != '\0') { if (str_to_uint(str, &dmail_r->pop3_order) < 0) i_unreached(); } if (mail_get_received_date(mail, &dmail_r->received_date) < 0) { *error_field_r = "received-date"; return -1; } return 0; } static void const_string_array_dup(pool_t pool, const ARRAY_TYPE(const_string) *src, ARRAY_TYPE(const_string) *dest) { const char *const *strings, *str; unsigned int i, count; if (!array_is_created(src)) return; strings = array_get(src, &count); if (count == 0) return; p_array_init(dest, pool, count); for (i = 0; i < count; i++) { str = p_strdup(pool, strings[i]); array_append(dest, &str, 1); } } void dsync_mail_change_dup(pool_t pool, const struct dsync_mail_change *src, struct dsync_mail_change *dest_r) { dest_r->type = src->type; dest_r->uid = src->uid; if (src->guid != NULL) { dest_r->guid = *src->guid == '\0' ? "" : p_strdup(pool, src->guid); } dest_r->hdr_hash = p_strdup(pool, src->hdr_hash); dest_r->modseq = src->modseq; dest_r->pvt_modseq = src->pvt_modseq; dest_r->add_flags = src->add_flags; dest_r->remove_flags = src->remove_flags; dest_r->final_flags = src->final_flags; dest_r->keywords_reset = src->keywords_reset; const_string_array_dup(pool, &src->keyword_changes, &dest_r->keyword_changes); dest_r->received_timestamp = src->received_timestamp; dest_r->virtual_size = src->virtual_size; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox.h0000644000175000017500000000212513165463543016607 00000000000000#ifndef DSYNC_MAILBOX_H #define DSYNC_MAILBOX_H #include "mail-storage.h" /* Mailbox that is going to be synced. Its name was already sent in the mailbox tree. */ struct dsync_mailbox { guid_128_t mailbox_guid; bool mailbox_lost; bool mailbox_ignore; bool have_guids, have_save_guids, have_only_guid128; uint32_t uid_validity, uid_next, messages_count, first_recent_uid; uint64_t highest_modseq, highest_pvt_modseq; ARRAY_TYPE(mailbox_cache_field) cache_fields; }; struct dsync_mailbox_attribute { enum mail_attribute_type type; const char *key; /* if both values are NULL = not looked up yet / deleted */ const char *value; struct istream *value_stream; time_t last_change; /* 0 = unknown */ uint64_t modseq; /* 0 = unknown */ bool deleted; /* attribute is known to have been deleted */ bool exported; /* internally used by exporting */ }; #define DSYNC_ATTR_HAS_VALUE(attr) \ ((attr)->value != NULL || (attr)->value_stream != NULL) void dsync_mailbox_attribute_dup(pool_t pool, const struct dsync_mailbox_attribute *src, struct dsync_mailbox_attribute *dest_r); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-export.h0000644000175000017500000000316013165463543020126 00000000000000#ifndef DSYNC_MAILBOX_EXPORT_H #define DSYNC_MAILBOX_EXPORT_H enum dsync_mailbox_exporter_flags { DSYNC_MAILBOX_EXPORTER_FLAG_AUTO_EXPORT_MAILS = 0x01, DSYNC_MAILBOX_EXPORTER_FLAG_MAILS_HAVE_GUIDS = 0x02, DSYNC_MAILBOX_EXPORTER_FLAG_MINIMAL_DMAIL_FILL = 0x04, DSYNC_MAILBOX_EXPORTER_FLAG_TIMESTAMPS = 0x08, DSYNC_MAILBOX_EXPORTER_FLAG_NO_HDR_HASHES = 0x20, DSYNC_MAILBOX_EXPORTER_FLAG_VSIZES = 0x40, }; struct dsync_mailbox_exporter * dsync_mailbox_export_init(struct mailbox *box, struct dsync_transaction_log_scan *log_scan, uint32_t last_common_uid, enum dsync_mailbox_exporter_flags flags, unsigned int hdr_hash_version, const char *const *hashed_headers); /* Returns 1 if attribute was returned, 0 if no more attributes, -1 on error */ int dsync_mailbox_export_next_attr(struct dsync_mailbox_exporter *exporter, const struct dsync_mailbox_attribute **attr_r); /* Returns 1 if change was returned, 0 if no more changes, -1 on error */ int dsync_mailbox_export_next(struct dsync_mailbox_exporter *exporter, const struct dsync_mail_change **change_r); void dsync_mailbox_export_want_mail(struct dsync_mailbox_exporter *exporter, const struct dsync_mail_request *request); /* Returns 1 if mail was returned, 0 if no more mails, -1 on error */ int dsync_mailbox_export_next_mail(struct dsync_mailbox_exporter *exporter, const struct dsync_mail **mail_r); int dsync_mailbox_export_deinit(struct dsync_mailbox_exporter **exporter, const char **errstr_r, enum mail_error *error_r); const char *dsync_mailbox_export_get_proctitle(struct dsync_mailbox_exporter *exporter); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox.c0000644000175000017500000000116113123174404016566 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "dsync-mailbox.h" void dsync_mailbox_attribute_dup(pool_t pool, const struct dsync_mailbox_attribute *src, struct dsync_mailbox_attribute *dest_r) { dest_r->type = src->type; dest_r->key = p_strdup(pool, src->key); dest_r->value = p_strdup(pool, src->value); if (src->value_stream != NULL) { dest_r->value_stream = src->value_stream; i_stream_ref(dest_r->value_stream); } dest_r->deleted = src->deleted; dest_r->last_change = src->last_change; dest_r->modseq = src->modseq; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-import.h0000644000175000017500000000446413165463543020127 00000000000000#ifndef DSYNC_MAILBOX_IMPORT_H #define DSYNC_MAILBOX_IMPORT_H #include "mail-error.h" enum dsync_mailbox_import_flags { DSYNC_MAILBOX_IMPORT_FLAG_MASTER_BRAIN = 0x01, DSYNC_MAILBOX_IMPORT_FLAG_WANT_MAIL_REQUESTS = 0x02, DSYNC_MAILBOX_IMPORT_FLAG_REVERT_LOCAL_CHANGES = 0x04, DSYNC_MAILBOX_IMPORT_FLAG_DEBUG = 0x08, DSYNC_MAILBOX_IMPORT_FLAG_MAILS_HAVE_GUIDS = 0x10, DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128 = 0x20, DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY = 0x40, DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND = 0x100 }; struct mailbox; struct dsync_mailbox_attribute; struct dsync_mail; struct dsync_mail_change; struct dsync_transaction_log_scan; struct dsync_mailbox_importer * dsync_mailbox_import_init(struct mailbox *box, struct mailbox *virtual_all_box, struct dsync_transaction_log_scan *log_scan, uint32_t last_common_uid, uint64_t last_common_modseq, uint64_t last_common_pvt_modseq, uint32_t remote_uid_next, uint32_t remote_first_recent_uid, uint64_t remote_highest_modseq, uint64_t remote_highest_pvt_modseq, time_t sync_since_timestamp, time_t sync_until_timestamp, uoff_t sync_max_size, const char *sync_flag, unsigned int commit_msgs_interval, enum dsync_mailbox_import_flags flags, unsigned int hdr_hash_version, const char *const *hashed_headers); int dsync_mailbox_import_attribute(struct dsync_mailbox_importer *importer, const struct dsync_mailbox_attribute *attr); int dsync_mailbox_import_change(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change); int dsync_mailbox_import_changes_finish(struct dsync_mailbox_importer *importer); const struct dsync_mail_request * dsync_mailbox_import_next_request(struct dsync_mailbox_importer *importer); int dsync_mailbox_import_mail(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail); int dsync_mailbox_import_deinit(struct dsync_mailbox_importer **importer, bool success, uint32_t *last_common_uid_r, uint64_t *last_common_modseq_r, uint64_t *last_common_pvt_modseq_r, uint32_t *last_messages_count_r, const char **changes_during_sync_r, bool *require_full_resync_r, enum mail_error *error_r); const char *dsync_mailbox_import_get_proctitle(struct dsync_mailbox_importer *importer); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain-mailbox-tree.c0000644000175000017500000003522213165463624020634 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-namespace.h" #include "dsync-ibc.h" #include "dsync-mailbox-tree.h" #include "dsync-brain-private.h" #include static void dsync_brain_check_namespaces(struct dsync_brain *brain) { struct mail_namespace *ns, *first_ns = NULL; char sep; i_assert(brain->hierarchy_sep == '\0'); for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) { if (!dsync_brain_want_namespace(brain, ns)) continue; sep = mail_namespace_get_sep(ns); if (first_ns == NULL) { brain->hierarchy_sep = sep; first_ns = ns; } else if (brain->hierarchy_sep != sep) { i_fatal("Synced namespaces have conflicting separators " "('%c' for prefix=\"%s\", '%c' for prefix=\"%s\")", brain->hierarchy_sep, first_ns->prefix, sep, ns->prefix); } } if (brain->hierarchy_sep != '\0') return; i_fatal("All your namespaces have a location setting. " "Only namespaces with empty location settings are converted. " "(One namespace should default to mail_location setting)"); } void dsync_brain_mailbox_trees_init(struct dsync_brain *brain) { struct mail_namespace *ns; dsync_brain_check_namespaces(brain); brain->local_mailbox_tree = dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char); /* we'll convert remote mailbox names to use our own separator */ brain->remote_mailbox_tree = dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char); /* fill the local mailbox tree */ for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) { if (!dsync_brain_want_namespace(brain, ns)) continue; if (dsync_mailbox_tree_fill(brain->local_mailbox_tree, ns, brain->sync_box, brain->sync_box_guid, brain->exclude_mailboxes, &brain->mail_error) < 0) { brain->failed = TRUE; break; } } brain->local_tree_iter = dsync_mailbox_tree_iter_init(brain->local_mailbox_tree); } void dsync_brain_send_mailbox_tree(struct dsync_brain *brain) { struct dsync_mailbox_node *node; enum dsync_ibc_send_ret ret; const char *full_name; char sep[2]; sep[0] = brain->hierarchy_sep; sep[1] = '\0'; while (dsync_mailbox_tree_iter_next(brain->local_tree_iter, &full_name, &node)) { T_BEGIN { const char *const *parts; if (brain->debug) { i_debug("brain %c: Local mailbox tree: %s %s", brain->master_brain ? 'M' : 'S', full_name, dsync_mailbox_node_to_string(node)); } parts = t_strsplit(full_name, sep); ret = dsync_ibc_send_mailbox_tree_node(brain->ibc, parts, node); } T_END; if (ret == DSYNC_IBC_SEND_RET_FULL) return; } dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter); dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_TREE); brain->state = DSYNC_STATE_SEND_MAILBOX_TREE_DELETES; } void dsync_brain_send_mailbox_tree_deletes(struct dsync_brain *brain) { const struct dsync_mailbox_delete *deletes; unsigned int count; deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree, &count); dsync_ibc_send_mailbox_deletes(brain->ibc, deletes, count, brain->hierarchy_sep); brain->state = DSYNC_STATE_RECV_MAILBOX_TREE; } static bool dsync_namespace_match_parts(struct mail_namespace *ns, const char *const *name_parts) { const char *part, *prefix = ns->prefix; size_t part_len; char ns_sep = mail_namespace_get_sep(ns); if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && strcmp(name_parts[0], "INBOX") == 0 && name_parts[1] == NULL) return TRUE; for (; *name_parts != NULL && *prefix != '\0'; name_parts++) { part = *name_parts; part_len = strlen(part); if (strncmp(prefix, part, part_len) != 0) return FALSE; if (prefix[part_len] != ns_sep) return FALSE; prefix += part_len + 1; } if (*name_parts != NULL) { /* namespace prefix found with a mailbox */ return TRUE; } if (*prefix == '\0') { /* namespace prefix itself matched */ return TRUE; } return FALSE; } static struct mail_namespace * dsync_find_namespace(struct dsync_brain *brain, const char *const *name_parts) { struct mail_namespace *ns, *best_ns = NULL; for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) { if (!dsync_brain_want_namespace(brain, ns)) continue; if (ns->prefix_len == 0) { /* prefix="" is the fallback namespace */ if (best_ns == NULL) best_ns = ns; } else if (dsync_namespace_match_parts(ns, name_parts)) { if (best_ns == NULL || best_ns->prefix_len < ns->prefix_len) best_ns = ns; } } return best_ns; } static bool dsync_is_valid_name(struct mail_namespace *ns, const char *vname) { struct mailbox *box; bool ret; box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY); ret = mailbox_verify_create_name(box) == 0; mailbox_free(&box); return ret; } static void dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname_str, char alt_char) { const char *old_vname; char *vname, list_sep = mailbox_list_get_hierarchy_sep(ns->list); guid_128_t guid; unsigned int i, start_pos; vname = str_c_modifiable(vname_str); if (strncmp(vname, ns->prefix, ns->prefix_len) == 0) start_pos = ns->prefix_len; else start_pos = 0; /* replace control chars */ for (i = start_pos; vname[i] != '\0'; i++) { if ((unsigned char)vname[i] < ' ') vname[i] = alt_char; } /* make it valid UTF8 */ if (!uni_utf8_str_is_valid(vname)) { old_vname = t_strdup(vname + start_pos); str_truncate(vname_str, start_pos); if (uni_utf8_get_valid_data((const void *)old_vname, strlen(old_vname), vname_str)) i_unreached(); vname = str_c_modifiable(vname_str); } if (dsync_is_valid_name(ns, vname)) return; /* 1) change any real separators to alt separators (this wouldn't be necessary with listescape, but don't bother detecting it) */ if (list_sep != mail_namespace_get_sep(ns)) { for (i = start_pos; vname[i] != '\0'; i++) { if (vname[i] == list_sep) vname[i] = alt_char; } if (dsync_is_valid_name(ns, vname)) return; } /* 2) '/' characters aren't valid without listescape */ if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') { for (i = start_pos; vname[i] != '\0'; i++) { if (vname[i] == '/') vname[i] = alt_char; } if (dsync_is_valid_name(ns, vname)) return; } /* 3) probably some reserved name (e.g. dbox-Mails) */ str_insert(vname_str, ns->prefix_len, "_"); if (dsync_is_valid_name(ns, str_c(vname_str))) return; /* 4) name is too long? just give up and generate a unique name */ guid_128_generate(guid); str_truncate(vname_str, 0); str_append(vname_str, ns->prefix); str_append(vname_str, guid_128_to_string(guid)); i_assert(dsync_is_valid_name(ns, str_c(vname_str))); } static int dsync_get_mailbox_name(struct dsync_brain *brain, const char *const *name_parts, const char **name_r, struct mail_namespace **ns_r) { struct mail_namespace *ns; const char *p; string_t *vname; char ns_sep; i_assert(*name_parts != NULL); ns = dsync_find_namespace(brain, name_parts); if (ns == NULL) return -1; ns_sep = mail_namespace_get_sep(ns); /* build the mailbox name */ vname = t_str_new(128); for (; *name_parts != NULL; name_parts++) { for (p = *name_parts; *p != '\0'; p++) { if (*p != ns_sep) str_append_c(vname, *p); else str_append_c(vname, brain->alt_char); } str_append_c(vname, ns_sep); } str_truncate(vname, str_len(vname)-1); dsync_fix_mailbox_name(ns, vname, brain->alt_char); *name_r = str_c(vname); *ns_r = ns; return 0; } static void dsync_brain_mailbox_trees_sync(struct dsync_brain *brain) { struct dsync_mailbox_tree_sync_ctx *ctx; const struct dsync_mailbox_tree_sync_change *change; enum dsync_mailbox_trees_sync_type sync_type; enum dsync_mailbox_trees_sync_flags sync_flags = (brain->debug ? DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG : 0) | (brain->master_brain ? DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN : 0) | (brain->no_mailbox_renames ? DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES : 0); if (brain->no_backup_overwrite) sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY; else if (brain->backup_send) sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL; else if (brain->backup_recv) sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE; else sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY; ctx = dsync_mailbox_trees_sync_init(brain->local_mailbox_tree, brain->remote_mailbox_tree, sync_type, sync_flags); while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) { if (dsync_brain_mailbox_tree_sync_change(brain, change, &brain->mail_error) < 0) { brain->failed = TRUE; break; } } if (dsync_mailbox_trees_sync_deinit(&ctx) < 0) brain->failed = TRUE; } bool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain) { const struct dsync_mailbox_node *remote_node; struct dsync_mailbox_node *node, *dup_node1, *dup_node2; const char *const *parts, *name; struct mail_namespace *ns; enum dsync_ibc_recv_ret ret; char sep[2]; bool changed = FALSE; sep[0] = brain->hierarchy_sep; sep[1] = '\0'; while ((ret = dsync_ibc_recv_mailbox_tree_node(brain->ibc, &parts, &remote_node)) > 0) { if (dsync_get_mailbox_name(brain, parts, &name, &ns) < 0) { i_error("Couldn't find namespace for mailbox %s", t_strarray_join(parts, sep)); brain->failed = TRUE; return TRUE; } if (brain->debug) { i_debug("brain %c: Remote mailbox tree: %s %s", brain->master_brain ? 'M' : 'S', t_strarray_join(parts, sep), dsync_mailbox_node_to_string(remote_node)); } node = dsync_mailbox_tree_get(brain->remote_mailbox_tree, name); node->ns = ns; dsync_mailbox_node_copy_data(node, remote_node); } if (ret != DSYNC_IBC_RECV_RET_FINISHED) return changed; if (dsync_mailbox_tree_build_guid_hash(brain->remote_mailbox_tree, &dup_node1, &dup_node2) < 0) { i_error("Remote sent duplicate mailbox GUID %s for mailboxes %s and %s", guid_128_to_string(dup_node1->mailbox_guid), dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree, dup_node1), dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree, dup_node2)); brain->failed = TRUE; } brain->state = DSYNC_STATE_RECV_MAILBOX_TREE_DELETES; return TRUE; } static void dsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree *tree, struct dsync_mailbox_tree *other_tree, const struct dsync_mailbox_delete *other_del, const struct dsync_mailbox_node **node_r, const char **status_r) { const struct dsync_mailbox_node *node; struct dsync_mailbox_node *other_node, *old_node; const char *name; /* see if we can find the deletion based on mailbox tree that should still have the mailbox */ node = *node_r = dsync_mailbox_tree_find_delete(tree, other_del); if (node == NULL) { *status_r = "not found"; return; } switch (other_del->type) { case DSYNC_MAILBOX_DELETE_TYPE_MAILBOX: /* mailbox is always deleted */ break; case DSYNC_MAILBOX_DELETE_TYPE_DIR: if (other_del->timestamp <= node->last_renamed_or_created) { /* we don't want to delete this directory, we already have a newer timestamp for it */ *status_r = "keep directory, we have a newer timestamp"; return; } break; case DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE: if (other_del->timestamp <= node->last_subscription_change) { /* we don't want to unsubscribe, since we already have a newer subscription timestamp */ *status_r = "keep subscription, we have a newer timestamp"; return; } break; } /* make a node for it in the other mailbox tree */ name = dsync_mailbox_node_get_full_name(tree, node); other_node = dsync_mailbox_tree_get(other_tree, name); if (other_node->existence == DSYNC_MAILBOX_NODE_EXISTS && (!guid_128_is_empty(other_node->mailbox_guid) || other_del->type != DSYNC_MAILBOX_DELETE_TYPE_MAILBOX)) { /* other side has already created a new mailbox or directory with this name, we can't delete it */ *status_r = "name has already been recreated"; return; } /* ok, mark the other node deleted */ if (other_del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) { memcpy(other_node->mailbox_guid, node->mailbox_guid, sizeof(other_node->mailbox_guid)); } if (other_node->ns != node->ns && other_node->ns != NULL) { /* namespace mismatch for this node. this shouldn't happen normally, but especially during some misconfigurations it's possible that one side has created mailboxes that conflict with another namespace's prefix. since we're here because one of the mailboxes was deleted, we'll just ignore this. */ *status_r = "namespace mismatch"; return; } other_node->ns = node->ns; if (other_del->type != DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE) { other_node->existence = DSYNC_MAILBOX_NODE_DELETED; *status_r = "marked as deleted"; } else { other_node->last_subscription_change = other_del->timestamp; other_node->subscribed = FALSE; *status_r = "marked as unsubscribed"; } if (dsync_mailbox_tree_guid_hash_add(other_tree, other_node, &old_node) < 0) i_unreached(); } bool dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain *brain) { const struct dsync_mailbox_node *node; const char *status; const struct dsync_mailbox_delete *deletes; unsigned int i, count; char sep; if (dsync_ibc_recv_mailbox_deletes(brain->ibc, &deletes, &count, &sep) == 0) return FALSE; /* apply remote's mailbox deletions based on our local tree */ dsync_mailbox_tree_set_remote_sep(brain->local_mailbox_tree, sep); for (i = 0; i < count; i++) { dsync_brain_mailbox_tree_add_delete(brain->local_mailbox_tree, brain->remote_mailbox_tree, &deletes[i], &node, &status); if (brain->debug) { const char *node_name = node == NULL ? "" : dsync_mailbox_node_get_full_name(brain->local_mailbox_tree, node); i_debug("brain %c: Remote mailbox tree deletion: guid=%s type=%s timestamp=%ld name=%s local update=%s", brain->master_brain ? 'M' : 'S', guid_128_to_string(deletes[i].guid), dsync_mailbox_delete_type_to_string(deletes[i].type), deletes[i].timestamp, node_name, status); } } /* apply local mailbox deletions based on remote tree */ deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree, &count); dsync_mailbox_tree_set_remote_sep(brain->remote_mailbox_tree, brain->hierarchy_sep); for (i = 0; i < count; i++) { dsync_brain_mailbox_tree_add_delete(brain->remote_mailbox_tree, brain->local_mailbox_tree, &deletes[i], &node, &status); } dsync_brain_mailbox_trees_sync(brain); brain->state = brain->master_brain ? DSYNC_STATE_MASTER_SEND_MAILBOX : DSYNC_STATE_SLAVE_RECV_MAILBOX; i_assert(brain->local_tree_iter == NULL); brain->local_tree_iter = dsync_mailbox_tree_iter_init(brain->local_mailbox_tree); return TRUE; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-ibc.c0000644000175000017500000001275113123174404015677 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dsync-mail.h" #include "dsync-ibc-private.h" void dsync_ibc_deinit(struct dsync_ibc **_ibc) { struct dsync_ibc *ibc = *_ibc; *_ibc = NULL; ibc->v.deinit(ibc); } void dsync_ibc_set_io_callback(struct dsync_ibc *ibc, io_callback_t *callback, void *context) { ibc->io_callback = callback; ibc->io_context = context; } void dsync_ibc_send_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings *set) { ibc->v.send_handshake(ibc, set); } enum dsync_ibc_recv_ret dsync_ibc_recv_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings **set_r) { return ibc->v.recv_handshake(ibc, set_r); } static enum dsync_ibc_send_ret dsync_ibc_send_ret(struct dsync_ibc *ibc) { return ibc->v.is_send_queue_full(ibc) ? DSYNC_IBC_SEND_RET_FULL : DSYNC_IBC_SEND_RET_OK; } enum dsync_ibc_send_ret dsync_ibc_send_end_of_list(struct dsync_ibc *ibc, enum dsync_ibc_eol_type type) { ibc->v.send_end_of_list(ibc, type); return dsync_ibc_send_ret(ibc); } enum dsync_ibc_send_ret dsync_ibc_send_mailbox_state(struct dsync_ibc *ibc, const struct dsync_mailbox_state *state) { T_BEGIN { ibc->v.send_mailbox_state(ibc, state); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_state(struct dsync_ibc *ibc, struct dsync_mailbox_state *state_r) { return ibc->v.recv_mailbox_state(ibc, state_r); } enum dsync_ibc_send_ret dsync_ibc_send_mailbox_tree_node(struct dsync_ibc *ibc, const char *const *name, const struct dsync_mailbox_node *node) { i_assert(*name != NULL); T_BEGIN { ibc->v.send_mailbox_tree_node(ibc, name, node); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_tree_node(struct dsync_ibc *ibc, const char *const **name_r, const struct dsync_mailbox_node **node_r) { return ibc->v.recv_mailbox_tree_node(ibc, name_r, node_r); } enum dsync_ibc_send_ret dsync_ibc_send_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete *deletes, unsigned int count, char hierarchy_sep) { T_BEGIN { ibc->v.send_mailbox_deletes(ibc, deletes, count, hierarchy_sep); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete **deletes_r, unsigned int *count_r, char *hierarchy_sep_r) { return ibc->v.recv_mailbox_deletes(ibc, deletes_r, count_r, hierarchy_sep_r); } enum dsync_ibc_send_ret dsync_ibc_send_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox *dsync_box) { T_BEGIN { ibc->v.send_mailbox(ibc, dsync_box); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox **dsync_box_r) { return ibc->v.recv_mailbox(ibc, dsync_box_r); } enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute *attr) { T_BEGIN { ibc->v.send_mailbox_attribute(ibc, attr); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute **attr_r) { return ibc->v.recv_mailbox_attribute(ibc, attr_r); } enum dsync_ibc_send_ret dsync_ibc_send_change(struct dsync_ibc *ibc, const struct dsync_mail_change *change) { i_assert(change->uid > 0); T_BEGIN { ibc->v.send_change(ibc, change); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_change(struct dsync_ibc *ibc, const struct dsync_mail_change **change_r) { return ibc->v.recv_change(ibc, change_r); } enum dsync_ibc_send_ret dsync_ibc_send_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request *request) { i_assert(request->guid != NULL || request->uid != 0); T_BEGIN { ibc->v.send_mail_request(ibc, request); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request **request_r) { return ibc->v.recv_mail_request(ibc, request_r); } enum dsync_ibc_send_ret dsync_ibc_send_mail(struct dsync_ibc *ibc, const struct dsync_mail *mail) { i_assert(*mail->guid != '\0' || mail->uid != 0); T_BEGIN { ibc->v.send_mail(ibc, mail); } T_END; return dsync_ibc_send_ret(ibc); } enum dsync_ibc_recv_ret dsync_ibc_recv_mail(struct dsync_ibc *ibc, struct dsync_mail **mail_r) { return ibc->v.recv_mail(ibc, mail_r); } void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error, enum mail_error mail_error, bool require_full_resync) { ibc->v.send_finish(ibc, error, mail_error, require_full_resync); } enum dsync_ibc_recv_ret dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r, enum mail_error *mail_error_r, bool *require_full_resync_r) { return ibc->v.recv_finish(ibc, error_r, mail_error_r, require_full_resync_r); } void dsync_ibc_close_mail_streams(struct dsync_ibc *ibc) { ibc->v.close_mail_streams(ibc); } bool dsync_ibc_has_failed(struct dsync_ibc *ibc) { return ibc->failed; } bool dsync_ibc_has_timed_out(struct dsync_ibc *ibc) { return ibc->timeout; } bool dsync_ibc_is_send_queue_full(struct dsync_ibc *ibc) { return ibc->v.is_send_queue_full(ibc); } bool dsync_ibc_has_pending_data(struct dsync_ibc *ibc) { return ibc->v.has_pending_data(ibc); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-tree.c0000644000175000017500000003411313165463624017541 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "mailbox-list-private.h" #include "dsync-mailbox-tree-private.h" struct dsync_mailbox_tree_iter { struct dsync_mailbox_tree *tree; struct dsync_mailbox_node *cur; string_t *name; }; struct dsync_mailbox_tree *dsync_mailbox_tree_init(char sep, char alt_char) { struct dsync_mailbox_tree *tree; pool_t pool; i_assert(sep != '\0'); pool = pool_alloconly_create(MEMPOOL_GROWING"dsync mailbox tree", 4096); tree = p_new(pool, struct dsync_mailbox_tree, 1); tree->pool = pool; tree->sep = tree->sep_str[0] = sep; tree->alt_char = alt_char; tree->root.name = ""; i_array_init(&tree->deletes, 32); return tree; } void dsync_mailbox_tree_deinit(struct dsync_mailbox_tree **_tree) { struct dsync_mailbox_tree *tree = *_tree; *_tree = NULL; if (hash_table_is_created(tree->name128_hash)) hash_table_destroy(&tree->name128_hash); if (hash_table_is_created(tree->guid_hash)) hash_table_destroy(&tree->guid_hash); array_free(&tree->deletes); pool_unref(&tree->pool); } static struct dsync_mailbox_node * dsync_mailbox_node_find(struct dsync_mailbox_node *nodes, const char *name) { for (; nodes != NULL; nodes = nodes->next) { if (strcmp(name, nodes->name) == 0) return nodes; } return NULL; } struct dsync_mailbox_node * dsync_mailbox_tree_lookup(struct dsync_mailbox_tree *tree, const char *full_name) { struct dsync_mailbox_node *node = &tree->root; T_BEGIN { const char *const *path; path = t_strsplit(full_name, tree->sep_str); for (; *path != NULL && node != NULL; path++) node = dsync_mailbox_node_find(node->first_child, *path); } T_END; return node; } void dsync_mailbox_tree_node_attach(struct dsync_mailbox_node *node, struct dsync_mailbox_node *parent) { node->parent = parent; node->next = parent->first_child; parent->first_child = node; } void dsync_mailbox_tree_node_detach(struct dsync_mailbox_node *node) { struct dsync_mailbox_node **p; for (p = &node->parent->first_child;; p = &(*p)->next) { if (*p == node) { *p = node->next; break; } } node->parent = NULL; } struct dsync_mailbox_node * dsync_mailbox_tree_get(struct dsync_mailbox_tree *tree, const char *full_name) { struct dsync_mailbox_node *parent = NULL, *node = &tree->root; i_assert(tree->iter_count == 0); T_BEGIN { const char *const *path; /* find the existing part */ path = t_strsplit(full_name, tree->sep_str); for (; *path != NULL; path++) { parent = node; node = dsync_mailbox_node_find(node->first_child, *path); if (node == NULL) break; } /* create the rest */ for (; *path != NULL; path++) { node = p_new(tree->pool, struct dsync_mailbox_node, 1); node->name = p_strdup(tree->pool, *path); node->ns = parent->ns; dsync_mailbox_tree_node_attach(node, parent); parent = node; } } T_END; return node; } static void node_get_full_name_recurse(const struct dsync_mailbox_tree *tree, const struct dsync_mailbox_node *node, string_t *str) { if (node->parent != &tree->root) node_get_full_name_recurse(tree, node->parent, str); str_append(str, node->name); str_append_c(str, tree->sep); } const char *dsync_mailbox_node_get_full_name(const struct dsync_mailbox_tree *tree, const struct dsync_mailbox_node *node) { string_t *str = t_str_new(128); dsync_mailbox_node_append_full_name(str, tree, node); return str_c(str); } void dsync_mailbox_node_append_full_name(string_t *str, const struct dsync_mailbox_tree *tree, const struct dsync_mailbox_node *node) { i_assert(node->parent != NULL); node_get_full_name_recurse(tree, node, str); /* remove the trailing separator */ str_truncate(str, str_len(str)-1); } void dsync_mailbox_node_copy_data(struct dsync_mailbox_node *dest, const struct dsync_mailbox_node *src) { memcpy(dest->mailbox_guid, src->mailbox_guid, sizeof(dest->mailbox_guid)); dest->uid_validity = src->uid_validity; dest->uid_next = src->uid_next; dest->existence = src->existence; dest->last_renamed_or_created = src->last_renamed_or_created; dest->subscribed = src->subscribed; dest->last_subscription_change = src->last_subscription_change; } struct dsync_mailbox_tree_iter * dsync_mailbox_tree_iter_init(struct dsync_mailbox_tree *tree) { struct dsync_mailbox_tree_iter *iter; iter = i_new(struct dsync_mailbox_tree_iter, 1); iter->tree = tree; iter->name = str_new(default_pool, 128); iter->cur = &tree->root; tree->iter_count++; return iter; } static size_t node_get_full_name_length(struct dsync_mailbox_node *node) { if (node->parent->parent == NULL) return strlen(node->name); else { return strlen(node->name) + 1 + node_get_full_name_length(node->parent); } } bool dsync_mailbox_tree_iter_next(struct dsync_mailbox_tree_iter *iter, const char **full_name_r, struct dsync_mailbox_node **node_r) { size_t len; if (iter->cur->first_child != NULL) iter->cur = iter->cur->first_child; else { while (iter->cur->next == NULL) { if (iter->cur == &iter->tree->root) return FALSE; iter->cur = iter->cur->parent; } iter->cur = iter->cur->next; len = iter->cur->parent == &iter->tree->root ? 0 : node_get_full_name_length(iter->cur->parent); str_truncate(iter->name, len); } if (str_len(iter->name) > 0) str_append_c(iter->name, iter->tree->sep); str_append(iter->name, iter->cur->name); *full_name_r = str_c(iter->name); *node_r = iter->cur; return TRUE; } void dsync_mailbox_tree_iter_deinit(struct dsync_mailbox_tree_iter **_iter) { struct dsync_mailbox_tree_iter *iter = *_iter; *_iter = NULL; i_assert(iter->tree->iter_count > 0); iter->tree->iter_count--; str_free(&iter->name); i_free(iter); } void dsync_mailbox_tree_build_name128_hash(struct dsync_mailbox_tree *tree) { struct dsync_mailbox_tree_iter *iter; struct dsync_mailbox_node *node; const char *name; guid_128_t *sha128; uint8_t *guid_p; i_assert(!hash_table_is_created(tree->name128_hash)); hash_table_create(&tree->name128_hash, tree->pool, 0, guid_128_hash, guid_128_cmp); iter = dsync_mailbox_tree_iter_init(tree); while (dsync_mailbox_tree_iter_next(iter, &name, &node)) { sha128 = p_new(tree->pool, guid_128_t, 1); mailbox_name_get_sha128(name, *sha128); guid_p = *sha128; hash_table_insert(tree->name128_hash, guid_p, node); } dsync_mailbox_tree_iter_deinit(&iter); } static const char * convert_name_to_remote_sep(struct dsync_mailbox_tree *tree, const char *name) { string_t *str = t_str_new(128); for (; *name != '\0'; name++) { if (*name == tree->sep) str_append_c(str, tree->remote_sep); else if (*name == tree->remote_sep) str_append_c(str, tree->alt_char); else str_append_c(str, *name); } return str_c(str); } static void dsync_mailbox_tree_build_name128_remotesep_hash(struct dsync_mailbox_tree *tree) { struct dsync_mailbox_tree_iter *iter; struct dsync_mailbox_node *node; const char *name; guid_128_t *sha128; uint8_t *guid_p; i_assert(tree->sep != tree->remote_sep); i_assert(!hash_table_is_created(tree->name128_remotesep_hash)); hash_table_create(&tree->name128_remotesep_hash, tree->pool, 0, guid_128_hash, guid_128_cmp); iter = dsync_mailbox_tree_iter_init(tree); while (dsync_mailbox_tree_iter_next(iter, &name, &node)) { sha128 = p_new(tree->pool, guid_128_t, 1); T_BEGIN { const char *remote_name = convert_name_to_remote_sep(tree, name); mailbox_name_get_sha128(remote_name, *sha128); } T_END; guid_p = *sha128; hash_table_insert(tree->name128_remotesep_hash, guid_p, node); } dsync_mailbox_tree_iter_deinit(&iter); } int dsync_mailbox_tree_guid_hash_add(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, struct dsync_mailbox_node **old_node_r) { struct dsync_mailbox_node *old_node; uint8_t *guid = node->mailbox_guid; if (guid_128_is_empty(node->mailbox_guid)) return 0; *old_node_r = old_node = hash_table_lookup(tree->guid_hash, guid); if (old_node == NULL) hash_table_insert(tree->guid_hash, guid, node); else if (old_node != node) return -1; return 0; } int dsync_mailbox_tree_build_guid_hash(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node **dup_node1_r, struct dsync_mailbox_node **dup_node2_r) { struct dsync_mailbox_tree_iter *iter; struct dsync_mailbox_node *node, *old_node; const char *name; int ret = 0; if (!hash_table_is_created(tree->guid_hash)) { hash_table_create(&tree->guid_hash, tree->pool, 0, guid_128_hash, guid_128_cmp); } iter = dsync_mailbox_tree_iter_init(tree); while (dsync_mailbox_tree_iter_next(iter, &name, &node)) { if (dsync_mailbox_tree_guid_hash_add(tree, node, &old_node) < 0) { *dup_node1_r = node; *dup_node2_r = old_node; ret = -1; } } dsync_mailbox_tree_iter_deinit(&iter); return ret; } struct dsync_mailbox_node * dsync_mailbox_tree_lookup_guid(struct dsync_mailbox_tree *tree, const guid_128_t guid) { const uint8_t *guid_p = guid; return hash_table_lookup(tree->guid_hash, guid_p); } const struct dsync_mailbox_delete * dsync_mailbox_tree_get_deletes(struct dsync_mailbox_tree *tree, unsigned int *count_r) { return array_get(&tree->deletes, count_r); } struct dsync_mailbox_node * dsync_mailbox_tree_find_delete(struct dsync_mailbox_tree *tree, const struct dsync_mailbox_delete *del) { const uint8_t *guid_p = del->guid; i_assert(hash_table_is_created(tree->guid_hash)); i_assert(tree->remote_sep != '\0'); if (del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) { /* find node by GUID */ return hash_table_lookup(tree->guid_hash, guid_p); } /* find node by name. this is a bit tricky, since the hierarchy separator may differ from ours. */ if (tree->sep == tree->remote_sep) { if (!hash_table_is_created(tree->name128_hash)) dsync_mailbox_tree_build_name128_hash(tree); return hash_table_lookup(tree->name128_hash, guid_p); } else { if (!hash_table_is_created(tree->name128_remotesep_hash)) dsync_mailbox_tree_build_name128_remotesep_hash(tree); return hash_table_lookup(tree->name128_remotesep_hash, guid_p); } } void dsync_mailbox_tree_set_remote_sep(struct dsync_mailbox_tree *tree, char remote_sep) { i_assert(tree->remote_sep == '\0'); tree->remote_sep = remote_sep; } static void dsync_mailbox_tree_dup_nodes(struct dsync_mailbox_tree *dest_tree, const struct dsync_mailbox_node *src, string_t *path) { size_t prefix_len = str_len(path); struct dsync_mailbox_node *node; if (prefix_len > 0) { str_append_c(path, dest_tree->sep); prefix_len++; } for (; src != NULL; src = src->next) { str_truncate(path, prefix_len); str_append(path, src->name); node = dsync_mailbox_tree_get(dest_tree, str_c(path)); node->ns = src->ns; memcpy(node->mailbox_guid, src->mailbox_guid, sizeof(node->mailbox_guid)); node->uid_validity = src->uid_validity; node->uid_next = src->uid_next; node->existence = src->existence; node->last_renamed_or_created = src->last_renamed_or_created; node->subscribed = src->subscribed; node->last_subscription_change = src->last_subscription_change; if (src->first_child != NULL) { dsync_mailbox_tree_dup_nodes(dest_tree, src->first_child, path); } } } struct dsync_mailbox_tree * dsync_mailbox_tree_dup(const struct dsync_mailbox_tree *src) { struct dsync_mailbox_tree *dest; string_t *str = t_str_new(128); dest = dsync_mailbox_tree_init(src->sep, src->alt_char); dsync_mailbox_tree_dup_nodes(dest, &src->root, str); return dest; } int dsync_mailbox_node_name_cmp(struct dsync_mailbox_node *const *n1, struct dsync_mailbox_node *const *n2) { return strcmp((*n1)->name, (*n2)->name); } static bool dsync_mailbox_nodes_equal(const struct dsync_mailbox_node *node1, const struct dsync_mailbox_node *node2) { return strcmp(node1->name, node2->name) == 0 && node1->ns == node2->ns && memcmp(node1->mailbox_guid, node2->mailbox_guid, sizeof(node1->mailbox_guid)) == 0 && node1->uid_validity == node2->uid_validity && node1->existence == node2->existence && node1->subscribed == node2->subscribed; } static bool dsync_mailbox_branches_equal(struct dsync_mailbox_node *node1, struct dsync_mailbox_node *node2) { /* this function is used only for unit tests, so performance doesn't really matter */ struct dsync_mailbox_node *n, **snodes1, **snodes2; unsigned int i, count; for (n = node1, count = 0; n != NULL; n = n->next) count++; for (n = node2, i = 0; n != NULL; n = n->next) i++; if (i != count) return FALSE; if (count == 0) return TRUE; /* sort the trees by name */ snodes1 = t_new(struct dsync_mailbox_node *, count); snodes2 = t_new(struct dsync_mailbox_node *, count); for (n = node1, i = 0; n != NULL; n = n->next, i++) snodes1[i] = n; for (n = node2, i = 0; n != NULL; n = n->next, i++) snodes2[i] = n; i_qsort(snodes1, count, sizeof(*snodes1), dsync_mailbox_node_name_cmp); i_qsort(snodes2, count, sizeof(*snodes2), dsync_mailbox_node_name_cmp); for (i = 0; i < count; i++) { if (!dsync_mailbox_nodes_equal(snodes1[i], snodes2[i])) return FALSE; if (!dsync_mailbox_branches_equal(snodes1[i]->first_child, snodes2[i]->first_child)) return FALSE; } return TRUE; } bool dsync_mailbox_trees_equal(struct dsync_mailbox_tree *tree1, struct dsync_mailbox_tree *tree2) { bool ret; T_BEGIN { ret = dsync_mailbox_branches_equal(&tree1->root, &tree2->root); } T_END; return ret; } const char *dsync_mailbox_node_to_string(const struct dsync_mailbox_node *node) { return t_strdup_printf("guid=%s uid_validity=%u uid_next=%u subs=%s last_change=%ld last_subs=%ld", guid_128_to_string(node->mailbox_guid), node->uid_validity, node->uid_next, node->subscribed ? "yes" : "no", (long)node->last_renamed_or_created, (long)node->last_subscription_change); } const char * dsync_mailbox_delete_type_to_string(enum dsync_mailbox_delete_type type) { switch (type) { case DSYNC_MAILBOX_DELETE_TYPE_MAILBOX: return "mailbox"; case DSYNC_MAILBOX_DELETE_TYPE_DIR: return "dir"; case DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE: return "unsubscribe"; } return t_strdup_printf("unknown #%u", type); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-ibc-stream.c0000644000175000017500000020446713165463624017212 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fd-set-nonblock.h" #include "safe-mkstemp.h" #include "ioloop.h" #include "istream.h" #include "istream-seekable.h" #include "istream-dot.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "master-service.h" #include "mail-cache.h" #include "mail-storage-private.h" #include "dsync-serializer.h" #include "dsync-deserializer.h" #include "dsync-mail.h" #include "dsync-mailbox.h" #include "dsync-mailbox-state.h" #include "dsync-mailbox-tree.h" #include "dsync-ibc-private.h" #define DSYNC_IBC_STREAM_OUTBUF_THROTTLE_SIZE (1024*128) #define DSYNC_PROTOCOL_VERSION_MAJOR 3 #define DSYNC_PROTOCOL_VERSION_MINOR 5 #define DSYNC_HANDSHAKE_VERSION "VERSION\tdsync\t3\t5\n" #define DSYNC_PROTOCOL_MINOR_HAVE_ATTRIBUTES 1 #define DSYNC_PROTOCOL_MINOR_HAVE_SAVE_GUID 2 #define DSYNC_PROTOCOL_MINOR_HAVE_FINISH 3 #define DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V2 4 #define DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V3 5 enum item_type { ITEM_NONE, ITEM_DONE, ITEM_HANDSHAKE, ITEM_MAILBOX_STATE, ITEM_MAILBOX_TREE_NODE, ITEM_MAILBOX_DELETE, ITEM_MAILBOX, ITEM_MAILBOX_ATTRIBUTE, ITEM_MAIL_CHANGE, ITEM_MAIL_REQUEST, ITEM_MAIL, ITEM_FINISH, ITEM_MAILBOX_CACHE_FIELD, ITEM_END_OF_LIST }; #define END_OF_LIST_LINE "." static const struct { /* full human readable name of the item */ const char *name; /* unique character identifying the item */ char chr; const char *required_keys; const char *optional_keys; unsigned int min_minor_version; } items[ITEM_END_OF_LIST+1] = { { NULL, '\0', NULL, NULL, 0 }, { .name = "done", .chr = 'X', .optional_keys = "" }, { .name = "handshake", .chr = 'H', .required_keys = "hostname", .optional_keys = "sync_ns_prefix sync_box sync_box_guid sync_type " "debug sync_visible_namespaces exclude_mailboxes " "send_mail_requests backup_send backup_recv lock_timeout " "no_mail_sync no_mailbox_renames no_backup_overwrite purge_remote " "no_notify sync_since_timestamp sync_max_size sync_flags sync_until_timestamp " "virtual_all_box empty_hdr_workaround import_commit_msgs_interval " "hashed_headers" }, { .name = "mailbox_state", .chr = 'S', .required_keys = "mailbox_guid last_uidvalidity last_common_uid " "last_common_modseq last_common_pvt_modseq", .optional_keys = "last_messages_count changes_during_sync" }, { .name = "mailbox_tree_node", .chr = 'N', .required_keys = "name existence", .optional_keys = "mailbox_guid uid_validity uid_next " "last_renamed_or_created subscribed last_subscription_change" }, { .name = "mailbox_delete", .chr = 'D', .required_keys = "hierarchy_sep", .optional_keys = "mailboxes dirs unsubscribes" }, { .name = "mailbox", .chr = 'B', .required_keys = "mailbox_guid uid_validity uid_next messages_count " "first_recent_uid highest_modseq highest_pvt_modseq", .optional_keys = "mailbox_lost mailbox_ignore " "cache_fields have_guids have_save_guids have_only_guid128" }, { .name = "mailbox_attribute", .chr = 'A', .required_keys = "type key", .optional_keys = "value stream deleted last_change modseq", .min_minor_version = DSYNC_PROTOCOL_MINOR_HAVE_ATTRIBUTES }, { .name = "mail_change", .chr = 'C', .required_keys = "type uid", .optional_keys = "guid hdr_hash modseq pvt_modseq " "add_flags remove_flags final_flags " "keywords_reset keyword_changes received_timestamp virtual_size" }, { .name = "mail_request", .chr = 'R', .optional_keys = "guid uid" }, { .name = "mail", .chr = 'M', .optional_keys = "guid uid pop3_uidl pop3_order received_date saved_date stream" }, { .name = "finish", .chr = 'F', .optional_keys = "error mail_error require_full_resync", .min_minor_version = DSYNC_PROTOCOL_MINOR_HAVE_FINISH }, { .name = "mailbox_cache_field", .chr = 'c', .required_keys = "name decision", .optional_keys = "last_used" }, { "end_of_list", '\0', NULL, NULL, 0 } }; struct dsync_ibc_stream { struct dsync_ibc ibc; char *name, *temp_path_prefix; unsigned int timeout_secs; struct istream *input; struct ostream *output; struct io *io; struct timeout *to; unsigned int minor_version; struct dsync_serializer *serializers[ITEM_END_OF_LIST]; struct dsync_deserializer *deserializers[ITEM_END_OF_LIST]; pool_t ret_pool; struct dsync_deserializer_decoder *cur_decoder; struct istream *value_output, *value_input; struct dsync_mail *cur_mail; struct dsync_mailbox_attribute *cur_attr; char value_output_last; enum item_type last_recv_item, last_sent_item; unsigned int last_recv_item_eol:1; unsigned int last_sent_item_eol:1; unsigned int version_received:1; unsigned int handshake_received:1; unsigned int has_pending_data:1; unsigned int finish_received:1; unsigned int done_received:1; unsigned int stopped:1; }; static const char *dsync_ibc_stream_get_state(struct dsync_ibc_stream *ibc) { if (!ibc->version_received) return "version not received"; else if (!ibc->handshake_received) return "handshake not received"; return t_strdup_printf("last sent=%s%s, last recv=%s%s", items[ibc->last_sent_item].name, ibc->last_sent_item_eol ? " (EOL)" : "", items[ibc->last_recv_item].name, ibc->last_recv_item_eol ? " (EOL)" : ""); } static void dsync_ibc_stream_stop(struct dsync_ibc_stream *ibc) { ibc->stopped = TRUE; i_stream_close(ibc->input); o_stream_close(ibc->output); io_loop_stop(current_ioloop); } static int dsync_ibc_stream_read_mail_stream(struct dsync_ibc_stream *ibc) { do { i_stream_skip(ibc->value_input, i_stream_get_data_size(ibc->value_input)); } while (i_stream_read(ibc->value_input) > 0); if (ibc->value_input->eof) { if (ibc->value_input->stream_errno != 0) { i_error("dsync(%s): read(%s) failed: %s (%s)", ibc->name, i_stream_get_name(ibc->value_input), i_stream_get_error(ibc->value_input), dsync_ibc_stream_get_state(ibc)); dsync_ibc_stream_stop(ibc); return -1; } /* finished reading the mail stream */ i_assert(ibc->value_input->eof); i_stream_seek(ibc->value_input, 0); ibc->has_pending_data = TRUE; ibc->value_input = NULL; return 1; } return 0; } static void dsync_ibc_stream_input(struct dsync_ibc_stream *ibc) { timeout_reset(ibc->to); if (ibc->value_input != NULL) { if (dsync_ibc_stream_read_mail_stream(ibc) == 0) return; } o_stream_cork(ibc->output); ibc->ibc.io_callback(ibc->ibc.io_context); o_stream_uncork(ibc->output); } static int dsync_ibc_stream_send_value_stream(struct dsync_ibc_stream *ibc) { const unsigned char *data; unsigned char add; size_t i, size; int ret; while ((ret = i_stream_read_data(ibc->value_output, &data, &size, 0)) > 0) { add = '\0'; for (i = 0; i < size; i++) { if (data[i] == '.' && ((i == 0 && ibc->value_output_last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; break; } } if (i > 0) { o_stream_nsend(ibc->output, data, i); ibc->value_output_last = data[i-1]; i_stream_skip(ibc->value_output, i); } if (o_stream_get_buffer_used_size(ibc->output) >= 4096) { if ((ret = o_stream_flush(ibc->output)) < 0) { dsync_ibc_stream_stop(ibc); return -1; } if (ret == 0) { /* continue later */ o_stream_set_flush_pending(ibc->output, TRUE); return 0; } } if (add != '\0') { o_stream_nsend(ibc->output, &add, 1); ibc->value_output_last = add; } } i_assert(ret == -1); if (ibc->value_output->stream_errno != 0) { i_error("dsync(%s): read(%s) failed: %s (%s)", ibc->name, i_stream_get_name(ibc->value_output), i_stream_get_error(ibc->value_output), dsync_ibc_stream_get_state(ibc)); dsync_ibc_stream_stop(ibc); return -1; } /* finished sending the stream. use "CRLF." instead of "LF." just in case we're sending binary data that ends with CR. */ o_stream_nsend_str(ibc->output, "\r\n.\r\n"); i_stream_unref(&ibc->value_output); return 1; } static int dsync_ibc_stream_output(struct dsync_ibc_stream *ibc) { struct ostream *output = ibc->output; int ret; o_stream_cork(ibc->output); if ((ret = o_stream_flush(output)) < 0) ret = 1; else if (ibc->value_output != NULL) { if (dsync_ibc_stream_send_value_stream(ibc) < 0) ret = 1; } timeout_reset(ibc->to); if (!dsync_ibc_is_send_queue_full(&ibc->ibc)) ibc->ibc.io_callback(ibc->ibc.io_context); o_stream_uncork(ibc->output); return ret; } static void dsync_ibc_stream_timeout(struct dsync_ibc_stream *ibc) { i_error("dsync(%s): I/O has stalled, no activity for %u seconds (%s)", ibc->name, ibc->timeout_secs, dsync_ibc_stream_get_state(ibc)); ibc->ibc.timeout = TRUE; dsync_ibc_stream_stop(ibc); } static void dsync_ibc_stream_init(struct dsync_ibc_stream *ibc) { unsigned int i; ibc->io = io_add_istream(ibc->input, dsync_ibc_stream_input, ibc); o_stream_set_no_error_handling(ibc->output, TRUE); o_stream_set_flush_callback(ibc->output, dsync_ibc_stream_output, ibc); ibc->to = timeout_add(ibc->timeout_secs * 1000, dsync_ibc_stream_timeout, ibc); o_stream_cork(ibc->output); o_stream_nsend_str(ibc->output, DSYNC_HANDSHAKE_VERSION); /* initialize serializers and send their headers to remote */ for (i = ITEM_DONE + 1; i < ITEM_END_OF_LIST; i++) T_BEGIN { const char *keys; keys = items[i].required_keys == NULL ? items[i].optional_keys : t_strconcat(items[i].required_keys, " ", items[i].optional_keys, NULL); if (keys != NULL) { i_assert(items[i].chr != '\0'); ibc->serializers[i] = dsync_serializer_init(t_strsplit_spaces(keys, " ")); o_stream_nsend(ibc->output, &items[i].chr, 1); o_stream_nsend_str(ibc->output, dsync_serializer_encode_header_line(ibc->serializers[i])); } } T_END; o_stream_nsend_str(ibc->output, ".\n"); o_stream_uncork(ibc->output); } static void dsync_ibc_stream_deinit(struct dsync_ibc *_ibc) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; unsigned int i; for (i = ITEM_DONE + 1; i < ITEM_END_OF_LIST; i++) { if (ibc->serializers[i] != NULL) dsync_serializer_deinit(&ibc->serializers[i]); if (ibc->deserializers[i] != NULL) dsync_deserializer_deinit(&ibc->deserializers[i]); } if (ibc->cur_decoder != NULL) dsync_deserializer_decode_finish(&ibc->cur_decoder); if (ibc->value_output != NULL) i_stream_unref(&ibc->value_output); else { /* If the remote has not told us that they are closing we notify remote that we're closing. this is mainly to avoid "read() failed: EOF" errors on failing dsyncs. Avoid a race condition: We do not tell the remote we are closing if they have already told us because they close the connection after sending ITEM_DONE and will not be ever receive anything else from us unless it just happens to get combined into the same packet as a previous response and is already in the buffer. */ if (!ibc->done_received && !ibc->finish_received) { o_stream_nsend_str(ibc->output, t_strdup_printf("%c\n", items[ITEM_DONE].chr)); } (void)o_stream_nfinish(ibc->output); } timeout_remove(&ibc->to); if (ibc->io != NULL) io_remove(&ibc->io); i_stream_destroy(&ibc->input); o_stream_destroy(&ibc->output); pool_unref(&ibc->ret_pool); i_free(ibc->temp_path_prefix); i_free(ibc->name); i_free(ibc); } static int dsync_ibc_stream_next_line(struct dsync_ibc_stream *ibc, const char **line_r) { string_t *error; const char *line; ssize_t ret; line = i_stream_next_line(ibc->input); if (line != NULL) { *line_r = line; return 1; } /* try reading some */ if ((ret = i_stream_read(ibc->input)) == -1) { if (ibc->stopped) return -1; error = t_str_new(128); if (ibc->input->stream_errno != 0) { str_printfa(error, "read(%s) failed: %s", ibc->name, i_stream_get_error(ibc->input)); } else { i_assert(ibc->input->eof); str_printfa(error, "read(%s) failed: EOF", ibc->name); } str_printfa(error, " (%s)", dsync_ibc_stream_get_state(ibc)); i_error("%s", str_c(error)); dsync_ibc_stream_stop(ibc); return -1; } i_assert(ret >= 0); *line_r = i_stream_next_line(ibc->input); if (*line_r == NULL) { ibc->has_pending_data = FALSE; return 0; } ibc->has_pending_data = TRUE; return 1; } static void ATTR_FORMAT(3, 4) ATTR_NULL(2) dsync_ibc_input_error(struct dsync_ibc_stream *ibc, struct dsync_deserializer_decoder *decoder, const char *fmt, ...) { va_list args; const char *error; va_start(args, fmt); error = t_strdup_vprintf(fmt, args); if (decoder == NULL) i_error("dsync(%s): %s", ibc->name, error); else { i_error("dsync(%s): %s: %s", ibc->name, dsync_deserializer_decoder_get_name(decoder), error); } va_end(args); dsync_ibc_stream_stop(ibc); } static void dsync_ibc_stream_send_string(struct dsync_ibc_stream *ibc, const string_t *str) { i_assert(ibc->value_output == NULL); o_stream_nsend(ibc->output, str_data(str), str_len(str)); } static int seekable_fd_callback(const char **path_r, void *context) { struct dsync_ibc_stream *ibc = context; string_t *path; int fd; path = t_str_new(128); str_append(path, ibc->temp_path_prefix); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static struct istream * dsync_ibc_stream_input_stream(struct dsync_ibc_stream *ibc) { struct istream *inputs[2]; inputs[0] = i_stream_create_dot(ibc->input, FALSE); inputs[1] = NULL; ibc->value_input = i_stream_create_seekable(inputs, MAIL_READ_FULL_BLOCK_SIZE, seekable_fd_callback, ibc); i_stream_unref(&inputs[0]); return ibc->value_input; } static int dsync_ibc_check_missing_deserializers(struct dsync_ibc_stream *ibc) { unsigned int i; int ret = 0; for (i = ITEM_DONE + 1; i < ITEM_END_OF_LIST; i++) { if (ibc->deserializers[i] == NULL && ibc->minor_version >= items[i].min_minor_version && (items[i].required_keys != NULL || items[i].optional_keys != NULL)) { dsync_ibc_input_error(ibc, NULL, "Remote didn't handshake deserializer for %s", items[i].name); ret = -1; } } return ret; } static bool dsync_ibc_stream_handshake(struct dsync_ibc_stream *ibc, const char *line) { enum item_type item = ITEM_NONE; const char *const *required_keys, *error; unsigned int i; if (ibc->handshake_received) return TRUE; if (!ibc->version_received) { if (!version_string_verify_full(line, "dsync", DSYNC_PROTOCOL_VERSION_MAJOR, &ibc->minor_version)) { dsync_ibc_input_error(ibc, NULL, "Remote dsync doesn't use compatible protocol"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } ibc->version_received = TRUE; return FALSE; } if (strcmp(line, END_OF_LIST_LINE) == 0) { /* finished handshaking */ if (dsync_ibc_check_missing_deserializers(ibc) < 0) return FALSE; ibc->handshake_received = TRUE; ibc->last_recv_item = ITEM_HANDSHAKE; return FALSE; } for (i = 1; i < ITEM_END_OF_LIST; i++) { if (items[i].chr == line[0]) { item = i; break; } } if (item == ITEM_NONE) { /* unknown deserializer, ignore */ return FALSE; } required_keys = items[item].required_keys == NULL ? NULL : t_strsplit(items[item].required_keys, " "); if (dsync_deserializer_init(items[item].name, required_keys, line + 1, &ibc->deserializers[item], &error) < 0) { dsync_ibc_input_error(ibc, NULL, "Remote sent invalid handshake for %s: %s", items[item].name, error); } return FALSE; } static enum dsync_ibc_recv_ret dsync_ibc_stream_input_next(struct dsync_ibc_stream *ibc, enum item_type item, struct dsync_deserializer_decoder **decoder_r) { enum item_type line_item = ITEM_NONE; const char *line, *error; unsigned int i; i_assert(ibc->value_input == NULL); timeout_reset(ibc->to); do { if (dsync_ibc_stream_next_line(ibc, &line) <= 0) return DSYNC_IBC_RECV_RET_TRYAGAIN; } while (!dsync_ibc_stream_handshake(ibc, line)); ibc->last_recv_item = item; ibc->last_recv_item_eol = FALSE; if (strcmp(line, END_OF_LIST_LINE) == 0) { /* end of this list */ ibc->last_recv_item_eol = TRUE; return DSYNC_IBC_RECV_RET_FINISHED; } if (line[0] == items[ITEM_DONE].chr) { /* remote cleanly closed the connection, possibly because of some failure (which it should have logged). we don't want to log any stream errors anyway after this. */ ibc->done_received = TRUE; dsync_ibc_stream_stop(ibc); return DSYNC_IBC_RECV_RET_TRYAGAIN; } for (i = 1; i < ITEM_END_OF_LIST; i++) { if (*line == items[i].chr) { line_item = i; break; } } if (line_item != item) { dsync_ibc_input_error(ibc, NULL, "Received unexpected input %c != %c", *line, items[item].chr); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (ibc->cur_decoder != NULL) dsync_deserializer_decode_finish(&ibc->cur_decoder); if (dsync_deserializer_decode_begin(ibc->deserializers[item], line+1, &ibc->cur_decoder, &error) < 0) { dsync_ibc_input_error(ibc, NULL, "Invalid input to %s: %s", items[item].name, error); return DSYNC_IBC_RECV_RET_TRYAGAIN; } *decoder_r = ibc->cur_decoder; return DSYNC_IBC_RECV_RET_OK; } static struct dsync_serializer_encoder * dsync_ibc_send_encode_begin(struct dsync_ibc_stream *ibc, enum item_type item) { ibc->last_sent_item = item; ibc->last_sent_item_eol = FALSE; return dsync_serializer_encode_begin(ibc->serializers[item]); } static void dsync_ibc_stream_send_handshake(struct dsync_ibc *_ibc, const struct dsync_ibc_settings *set) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); char sync_type[2]; str_append_c(str, items[ITEM_HANDSHAKE].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_HANDSHAKE); dsync_serializer_encode_add(encoder, "hostname", set->hostname); if (set->sync_ns_prefixes != NULL) { dsync_serializer_encode_add(encoder, "sync_ns_prefix", set->sync_ns_prefixes); } if (set->sync_box != NULL) dsync_serializer_encode_add(encoder, "sync_box", set->sync_box); if (set->virtual_all_box != NULL) { dsync_serializer_encode_add(encoder, "virtual_all_box", set->virtual_all_box); } if (set->exclude_mailboxes != NULL) { string_t *substr = t_str_new(64); unsigned int i; for (i = 0; set->exclude_mailboxes[i] != NULL; i++) { if (i != 0) str_append_c(substr, '\t'); str_append_tabescaped(substr, set->exclude_mailboxes[i]); } dsync_serializer_encode_add(encoder, "exclude_mailboxes", str_c(substr)); } if (!guid_128_is_empty(set->sync_box_guid)) { dsync_serializer_encode_add(encoder, "sync_box_guid", guid_128_to_string(set->sync_box_guid)); } sync_type[0] = sync_type[1] = '\0'; switch (set->sync_type) { case DSYNC_BRAIN_SYNC_TYPE_UNKNOWN: break; case DSYNC_BRAIN_SYNC_TYPE_FULL: sync_type[0] = 'f'; break; case DSYNC_BRAIN_SYNC_TYPE_CHANGED: sync_type[0] = 'c'; break; case DSYNC_BRAIN_SYNC_TYPE_STATE: sync_type[0] = 's'; break; } if (sync_type[0] != '\0') dsync_serializer_encode_add(encoder, "sync_type", sync_type); if (set->lock_timeout > 0) { dsync_serializer_encode_add(encoder, "lock_timeout", t_strdup_printf("%u", set->lock_timeout)); } if (set->import_commit_msgs_interval > 0) { dsync_serializer_encode_add(encoder, "import_commit_msgs_interval", t_strdup_printf("%u", set->import_commit_msgs_interval)); } if (set->sync_since_timestamp > 0) { dsync_serializer_encode_add(encoder, "sync_since_timestamp", t_strdup_printf("%ld", (long)set->sync_since_timestamp)); } if (set->sync_until_timestamp > 0) { dsync_serializer_encode_add(encoder, "sync_until_timestamp", t_strdup_printf("%ld", (long)set->sync_since_timestamp)); } if (set->sync_max_size > 0) { dsync_serializer_encode_add(encoder, "sync_max_size", t_strdup_printf("%llu", (unsigned long long)set->sync_max_size)); } if (set->sync_flags != NULL) { dsync_serializer_encode_add(encoder, "sync_flags", set->sync_flags); } if ((set->brain_flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0) dsync_serializer_encode_add(encoder, "send_mail_requests", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0) dsync_serializer_encode_add(encoder, "backup_send", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0) dsync_serializer_encode_add(encoder, "backup_recv", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_DEBUG) != 0) dsync_serializer_encode_add(encoder, "debug", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES) != 0) dsync_serializer_encode_add(encoder, "sync_visible_namespaces", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_NO_MAIL_SYNC) != 0) dsync_serializer_encode_add(encoder, "no_mail_sync", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES) != 0) dsync_serializer_encode_add(encoder, "no_mailbox_renames", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE) != 0) dsync_serializer_encode_add(encoder, "no_backup_overwrite", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_PURGE_REMOTE) != 0) dsync_serializer_encode_add(encoder, "purge_remote", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_NO_NOTIFY) != 0) dsync_serializer_encode_add(encoder, "no_notify", ""); if ((set->brain_flags & DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND) != 0) dsync_serializer_encode_add(encoder, "empty_hdr_workaround", ""); /* this can be NULL in slave */ string_t *str2 = t_str_new(32); if (set->hashed_headers != NULL) { for(const char *const *ptr = set->hashed_headers; *ptr != NULL; ptr++) { str_append_tabescaped(str2, *ptr); str_append_c(str2, '\t'); } } dsync_serializer_encode_add(encoder, "hashed_headers", str_c(str2)); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_handshake(struct dsync_ibc *_ibc, const struct dsync_ibc_settings **set_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; struct dsync_ibc_settings *set; const char *value; pool_t pool = ibc->ret_pool; enum dsync_ibc_recv_ret ret; ret = dsync_ibc_stream_input_next(ibc, ITEM_HANDSHAKE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) { if (ret != DSYNC_IBC_RECV_RET_TRYAGAIN) { i_error("dsync(%s): Unexpected input in handshake", ibc->name); dsync_ibc_stream_stop(ibc); } return DSYNC_IBC_RECV_RET_TRYAGAIN; } p_clear(pool); set = p_new(pool, struct dsync_ibc_settings, 1); value = dsync_deserializer_decode_get(decoder, "hostname"); set->hostname = p_strdup(pool, value); /* now that we know the remote's hostname, use it for the stream's name */ i_free(ibc->name); ibc->name = i_strdup(set->hostname); if (dsync_deserializer_decode_try(decoder, "sync_ns_prefix", &value)) set->sync_ns_prefixes = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "sync_box", &value)) set->sync_box = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "virtual_all_box", &value)) set->virtual_all_box = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "sync_box_guid", &value) && guid_128_from_string(value, set->sync_box_guid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid sync_box_guid: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "exclude_mailboxes", &value) && *value != '\0') { char **boxes = p_strsplit_tabescaped(pool, value); set->exclude_mailboxes = (const void *)boxes; } if (dsync_deserializer_decode_try(decoder, "sync_type", &value)) { switch (value[0]) { case 'f': set->sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL; break; case 'c': set->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED; break; case 's': set->sync_type = DSYNC_BRAIN_SYNC_TYPE_STATE; break; default: dsync_ibc_input_error(ibc, decoder, "Unknown sync_type: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "lock_timeout", &value)) { if (str_to_uint(value, &set->lock_timeout) < 0 || set->lock_timeout == 0) { dsync_ibc_input_error(ibc, decoder, "Invalid lock_timeout: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "import_commit_msgs_interval", &value)) { if (str_to_uint(value, &set->import_commit_msgs_interval) < 0 || set->import_commit_msgs_interval == 0) { dsync_ibc_input_error(ibc, decoder, "Invalid import_commit_msgs_interval: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "sync_since_timestamp", &value)) { if (str_to_time(value, &set->sync_since_timestamp) < 0 || set->sync_since_timestamp == 0) { dsync_ibc_input_error(ibc, decoder, "Invalid sync_since_timestamp: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "sync_until_timestamp", &value)) { if (str_to_time(value, &set->sync_until_timestamp) < 0 || set->sync_until_timestamp == 0) { dsync_ibc_input_error(ibc, decoder, "Invalid sync_until_timestamp: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "sync_max_size", &value)) { if (str_to_uoff(value, &set->sync_max_size) < 0 || set->sync_max_size == 0) { dsync_ibc_input_error(ibc, decoder, "Invalid sync_max_size: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } } if (dsync_deserializer_decode_try(decoder, "sync_flags", &value)) set->sync_flags = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "send_mail_requests", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS; if (dsync_deserializer_decode_try(decoder, "backup_send", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND; if (dsync_deserializer_decode_try(decoder, "backup_recv", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV; if (dsync_deserializer_decode_try(decoder, "debug", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_DEBUG; if (dsync_deserializer_decode_try(decoder, "sync_visible_namespaces", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES; if (dsync_deserializer_decode_try(decoder, "no_mail_sync", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_NO_MAIL_SYNC; if (dsync_deserializer_decode_try(decoder, "no_mailbox_renames", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES; if (dsync_deserializer_decode_try(decoder, "no_backup_overwrite", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE; if (dsync_deserializer_decode_try(decoder, "purge_remote", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_PURGE_REMOTE; if (dsync_deserializer_decode_try(decoder, "no_notify", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_NO_NOTIFY; if (dsync_deserializer_decode_try(decoder, "empty_hdr_workaround", &value)) set->brain_flags |= DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND; if (dsync_deserializer_decode_try(decoder, "hashed_headers", &value)) set->hashed_headers = (const char*const*)p_strsplit_tabescaped(pool, value); set->hdr_hash_v2 = ibc->minor_version >= DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V2; set->hdr_hash_v3 = ibc->minor_version >= DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V3; *set_r = set; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_end_of_list(struct dsync_ibc *_ibc, enum dsync_ibc_eol_type type) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; i_assert(ibc->value_output == NULL); switch (type) { case DSYNC_IBC_EOL_MAILBOX_ATTRIBUTE: if (ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_ATTRIBUTES) return; break; default: break; } ibc->last_sent_item_eol = TRUE; o_stream_nsend_str(ibc->output, END_OF_LIST_LINE"\n"); } static void dsync_ibc_stream_send_mailbox_state(struct dsync_ibc *_ibc, const struct dsync_mailbox_state *state) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); str_append_c(str, items[ITEM_MAILBOX_STATE].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAILBOX_STATE); dsync_serializer_encode_add(encoder, "mailbox_guid", guid_128_to_string(state->mailbox_guid)); dsync_serializer_encode_add(encoder, "last_uidvalidity", dec2str(state->last_uidvalidity)); dsync_serializer_encode_add(encoder, "last_common_uid", dec2str(state->last_common_uid)); dsync_serializer_encode_add(encoder, "last_common_modseq", dec2str(state->last_common_modseq)); dsync_serializer_encode_add(encoder, "last_common_pvt_modseq", dec2str(state->last_common_pvt_modseq)); dsync_serializer_encode_add(encoder, "last_messages_count", dec2str(state->last_messages_count)); if (state->changes_during_sync) dsync_serializer_encode_add(encoder, "changes_during_sync", ""); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mailbox_state(struct dsync_ibc *_ibc, struct dsync_mailbox_state *state_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; const char *value; enum dsync_ibc_recv_ret ret; i_zero(state_r); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAILBOX_STATE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; value = dsync_deserializer_decode_get(decoder, "mailbox_guid"); if (guid_128_from_string(value, state_r->mailbox_guid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid mailbox_guid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "last_uidvalidity"); if (str_to_uint32(value, &state_r->last_uidvalidity) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_uidvalidity"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "last_common_uid"); if (str_to_uint32(value, &state_r->last_common_uid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_common_uid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "last_common_modseq"); if (str_to_uint64(value, &state_r->last_common_modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_common_modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "last_common_pvt_modseq"); if (str_to_uint64(value, &state_r->last_common_pvt_modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_common_pvt_modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "last_messages_count", &value) && str_to_uint32(value, &state_r->last_messages_count) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_messages_count"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "changes_during_sync", &value)) state_r->changes_during_sync = TRUE; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_mailbox_tree_node(struct dsync_ibc *_ibc, const char *const *name, const struct dsync_mailbox_node *node) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str, *namestr; i_assert(*name != NULL); str = t_str_new(128); str_append_c(str, items[ITEM_MAILBOX_TREE_NODE].chr); /* convert all hierarchy separators to tabs. mailbox names really aren't supposed to have any tabs, but escape them anyway if there are. */ namestr = t_str_new(128); for (; *name != NULL; name++) { str_append_tabescaped(namestr, *name); str_append_c(namestr, '\t'); } str_truncate(namestr, str_len(namestr)-1); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAILBOX_TREE_NODE); dsync_serializer_encode_add(encoder, "name", str_c(namestr)); switch (node->existence) { case DSYNC_MAILBOX_NODE_NONEXISTENT: dsync_serializer_encode_add(encoder, "existence", "n"); break; case DSYNC_MAILBOX_NODE_EXISTS: dsync_serializer_encode_add(encoder, "existence", "y"); break; case DSYNC_MAILBOX_NODE_DELETED: dsync_serializer_encode_add(encoder, "existence", "d"); break; } if (!guid_128_is_empty(node->mailbox_guid)) { dsync_serializer_encode_add(encoder, "mailbox_guid", guid_128_to_string(node->mailbox_guid)); } if (node->uid_validity != 0) { dsync_serializer_encode_add(encoder, "uid_validity", dec2str(node->uid_validity)); } if (node->uid_next != 0) { dsync_serializer_encode_add(encoder, "uid_next", dec2str(node->uid_next)); } if (node->last_renamed_or_created != 0) { dsync_serializer_encode_add(encoder, "last_renamed_or_created", dec2str(node->last_renamed_or_created)); } if (node->last_subscription_change != 0) { dsync_serializer_encode_add(encoder, "last_subscription_change", dec2str(node->last_subscription_change)); } if (node->subscribed) dsync_serializer_encode_add(encoder, "subscribed", ""); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mailbox_tree_node(struct dsync_ibc *_ibc, const char *const **name_r, const struct dsync_mailbox_node **node_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; struct dsync_mailbox_node *node; const char *value; enum dsync_ibc_recv_ret ret; ret = dsync_ibc_stream_input_next(ibc, ITEM_MAILBOX_TREE_NODE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; p_clear(ibc->ret_pool); node = p_new(ibc->ret_pool, struct dsync_mailbox_node, 1); value = dsync_deserializer_decode_get(decoder, "name"); if (*value == '\0') { dsync_ibc_input_error(ibc, decoder, "Empty name"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } *name_r = (void *)p_strsplit_tabescaped(ibc->ret_pool, value); value = dsync_deserializer_decode_get(decoder, "existence"); switch (*value) { case 'n': node->existence = DSYNC_MAILBOX_NODE_NONEXISTENT; break; case 'y': node->existence = DSYNC_MAILBOX_NODE_EXISTS; break; case 'd': node->existence = DSYNC_MAILBOX_NODE_DELETED; break; } if (dsync_deserializer_decode_try(decoder, "mailbox_guid", &value) && guid_128_from_string(value, node->mailbox_guid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid mailbox_guid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "uid_validity", &value) && str_to_uint32(value, &node->uid_validity) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid_validity"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "uid_next", &value) && str_to_uint32(value, &node->uid_next) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid_next"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "last_renamed_or_created", &value) && str_to_time(value, &node->last_renamed_or_created) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_renamed_or_created"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "last_subscription_change", &value) && str_to_time(value, &node->last_subscription_change) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_subscription_change"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "subscribed", &value)) node->subscribed = TRUE; *node_r = node; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_encode_delete(string_t *str, struct dsync_serializer_encoder *encoder, const struct dsync_mailbox_delete *deletes, unsigned int count, const char *key, enum dsync_mailbox_delete_type type) { unsigned int i; str_truncate(str, 0); for (i = 0; i < count; i++) { if (deletes[i].type == type) { str_append(str, guid_128_to_string(deletes[i].guid)); str_printfa(str, " %ld ", (long)deletes[i].timestamp); } } if (str_len(str) > 0) { str_truncate(str, str_len(str)-1); dsync_serializer_encode_add(encoder, key, str_c(str)); } } static void dsync_ibc_stream_send_mailbox_deletes(struct dsync_ibc *_ibc, const struct dsync_mailbox_delete *deletes, unsigned int count, char hierarchy_sep) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str, *substr; char sep[2]; str = t_str_new(128); str_append_c(str, items[ITEM_MAILBOX_DELETE].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAILBOX_DELETE); sep[0] = hierarchy_sep; sep[1] = '\0'; dsync_serializer_encode_add(encoder, "hierarchy_sep", sep); substr = t_str_new(128); dsync_ibc_stream_encode_delete(substr, encoder, deletes, count, "mailboxes", DSYNC_MAILBOX_DELETE_TYPE_MAILBOX); dsync_ibc_stream_encode_delete(substr, encoder, deletes, count, "dirs", DSYNC_MAILBOX_DELETE_TYPE_DIR); dsync_ibc_stream_encode_delete(substr, encoder, deletes, count, "unsubscribes", DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } ARRAY_DEFINE_TYPE(dsync_mailbox_delete, struct dsync_mailbox_delete); static int decode_mailbox_deletes(ARRAY_TYPE(dsync_mailbox_delete) *deletes, const char *value, enum dsync_mailbox_delete_type type) { struct dsync_mailbox_delete *del; const char *const *tmp; unsigned int i; tmp = t_strsplit(value, " "); for (i = 0; tmp[i] != NULL; i += 2) { del = array_append_space(deletes); del->type = type; if (guid_128_from_string(tmp[i], del->guid) < 0) return -1; if (tmp[i+1] == NULL || str_to_time(tmp[i+1], &del->timestamp) < 0) return -1; } return 0; } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mailbox_deletes(struct dsync_ibc *_ibc, const struct dsync_mailbox_delete **deletes_r, unsigned int *count_r, char *hierarchy_sep_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; ARRAY_TYPE(dsync_mailbox_delete) deletes; const char *value; enum dsync_ibc_recv_ret ret; ret = dsync_ibc_stream_input_next(ibc, ITEM_MAILBOX_DELETE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; p_clear(ibc->ret_pool); p_array_init(&deletes, ibc->ret_pool, 16); value = dsync_deserializer_decode_get(decoder, "hierarchy_sep"); if (strlen(value) != 1) { dsync_ibc_input_error(ibc, decoder, "Invalid hierarchy_sep"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } *hierarchy_sep_r = value[0]; if (dsync_deserializer_decode_try(decoder, "mailboxes", &value) && decode_mailbox_deletes(&deletes, value, DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid mailboxes"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "dirs", &value) && decode_mailbox_deletes(&deletes, value, DSYNC_MAILBOX_DELETE_TYPE_DIR) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid dirs"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "unsubscribes", &value) && decode_mailbox_deletes(&deletes, value, DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid dirs"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } *deletes_r = array_get(&deletes, count_r); return DSYNC_IBC_RECV_RET_OK; } static const char * get_cache_fields(struct dsync_ibc_stream *ibc, const struct dsync_mailbox *dsync_box) { struct dsync_serializer_encoder *encoder; string_t *str; const struct mailbox_cache_field *cache_fields; unsigned int i, count; char decision[3]; cache_fields = array_get(&dsync_box->cache_fields, &count); if (count == 0) return ""; str = t_str_new(128); for (i = 0; i < count; i++) { const struct mailbox_cache_field *field = &cache_fields[i]; encoder = dsync_serializer_encode_begin(ibc->serializers[ITEM_MAILBOX_CACHE_FIELD]); dsync_serializer_encode_add(encoder, "name", field->name); memset(decision, 0, sizeof(decision)); switch (field->decision & ~MAIL_CACHE_DECISION_FORCED) { case MAIL_CACHE_DECISION_NO: decision[0] = 'n'; break; case MAIL_CACHE_DECISION_TEMP: decision[0] = 't'; break; case MAIL_CACHE_DECISION_YES: decision[0] = 'y'; break; } i_assert(decision[0] != '\0'); if ((field->decision & MAIL_CACHE_DECISION_FORCED) != 0) decision[1] = 'F'; dsync_serializer_encode_add(encoder, "decision", decision); if (field->last_used != 0) { dsync_serializer_encode_add(encoder, "last_used", dec2str(field->last_used)); } dsync_serializer_encode_finish(&encoder, str); } if (i > 0) { /* remove the trailing LF */ str_truncate(str, str_len(str)-1); } return str_c(str); } static void dsync_ibc_stream_send_mailbox(struct dsync_ibc *_ibc, const struct dsync_mailbox *dsync_box) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); const char *value; str_append_c(str, items[ITEM_MAILBOX].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAILBOX); dsync_serializer_encode_add(encoder, "mailbox_guid", guid_128_to_string(dsync_box->mailbox_guid)); if (dsync_box->mailbox_lost) dsync_serializer_encode_add(encoder, "mailbox_lost", ""); if (dsync_box->mailbox_ignore) dsync_serializer_encode_add(encoder, "mailbox_ignore", ""); if (dsync_box->have_guids) dsync_serializer_encode_add(encoder, "have_guids", ""); if (dsync_box->have_save_guids) dsync_serializer_encode_add(encoder, "have_save_guids", ""); if (dsync_box->have_only_guid128) dsync_serializer_encode_add(encoder, "have_only_guid128", ""); dsync_serializer_encode_add(encoder, "uid_validity", dec2str(dsync_box->uid_validity)); dsync_serializer_encode_add(encoder, "uid_next", dec2str(dsync_box->uid_next)); dsync_serializer_encode_add(encoder, "messages_count", dec2str(dsync_box->messages_count)); dsync_serializer_encode_add(encoder, "first_recent_uid", dec2str(dsync_box->first_recent_uid)); dsync_serializer_encode_add(encoder, "highest_modseq", dec2str(dsync_box->highest_modseq)); dsync_serializer_encode_add(encoder, "highest_pvt_modseq", dec2str(dsync_box->highest_pvt_modseq)); value = get_cache_fields(ibc, dsync_box); if (value != NULL) dsync_serializer_encode_add(encoder, "cache_fields", value); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static int parse_cache_field(struct dsync_ibc_stream *ibc, struct dsync_mailbox *box, const char *value) { struct dsync_deserializer_decoder *decoder; struct mailbox_cache_field field; const char *error; int ret = 0; if (dsync_deserializer_decode_begin(ibc->deserializers[ITEM_MAILBOX_CACHE_FIELD], value, &decoder, &error) < 0) { dsync_ibc_input_error(ibc, NULL, "cache_field: Invalid input: %s", error); return -1; } i_zero(&field); value = dsync_deserializer_decode_get(decoder, "name"); field.name = p_strdup(ibc->ret_pool, value); value = dsync_deserializer_decode_get(decoder, "decision"); switch (*value) { case 'n': field.decision = MAIL_CACHE_DECISION_NO; break; case 't': field.decision = MAIL_CACHE_DECISION_TEMP; break; case 'y': field.decision = MAIL_CACHE_DECISION_YES; break; default: dsync_ibc_input_error(ibc, decoder, "Invalid decision: %s", value); ret = -1; break; } if (value[1] == 'F') field.decision |= MAIL_CACHE_DECISION_FORCED; if (dsync_deserializer_decode_try(decoder, "last_used", &value) && str_to_time(value, &field.last_used) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_used"); ret = -1; } array_append(&box->cache_fields, &field, 1); dsync_deserializer_decode_finish(&decoder); return ret; } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mailbox(struct dsync_ibc *_ibc, const struct dsync_mailbox **dsync_box_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; pool_t pool = ibc->ret_pool; struct dsync_deserializer_decoder *decoder; struct dsync_mailbox *box; const char *value; enum dsync_ibc_recv_ret ret; p_clear(pool); box = p_new(pool, struct dsync_mailbox, 1); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAILBOX, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; value = dsync_deserializer_decode_get(decoder, "mailbox_guid"); if (guid_128_from_string(value, box->mailbox_guid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid mailbox_guid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "mailbox_lost", &value)) box->mailbox_lost = TRUE; if (dsync_deserializer_decode_try(decoder, "mailbox_ignore", &value)) box->mailbox_ignore = TRUE; if (dsync_deserializer_decode_try(decoder, "have_guids", &value)) box->have_guids = TRUE; if (dsync_deserializer_decode_try(decoder, "have_save_guids", &value) || (box->have_guids && ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_SAVE_GUID)) box->have_save_guids = TRUE; if (dsync_deserializer_decode_try(decoder, "have_only_guid128", &value)) box->have_only_guid128 = TRUE; value = dsync_deserializer_decode_get(decoder, "uid_validity"); if (str_to_uint32(value, &box->uid_validity) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid_validity"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "uid_next"); if (str_to_uint32(value, &box->uid_next) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid_next"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "messages_count"); if (str_to_uint32(value, &box->messages_count) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid messages_count"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "first_recent_uid"); if (str_to_uint32(value, &box->first_recent_uid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid first_recent_uid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "highest_modseq"); if (str_to_uint64(value, &box->highest_modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid highest_modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "highest_pvt_modseq"); if (str_to_uint64(value, &box->highest_pvt_modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid highest_pvt_modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } p_array_init(&box->cache_fields, pool, 32); if (dsync_deserializer_decode_try(decoder, "cache_fields", &value)) { const char *const *fields = t_strsplit(value, "\n"); for (; *fields != NULL; fields++) { if (parse_cache_field(ibc, box, *fields) < 0) return DSYNC_IBC_RECV_RET_TRYAGAIN; } } *dsync_box_r = box; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_mailbox_attribute(struct dsync_ibc *_ibc, const struct dsync_mailbox_attribute *attr) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); char type[2]; if (ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_ATTRIBUTES) return; str_append_c(str, items[ITEM_MAILBOX_ATTRIBUTE].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAILBOX_ATTRIBUTE); type[0] = type[1] = '\0'; switch (attr->type) { case MAIL_ATTRIBUTE_TYPE_PRIVATE: type[0] = 'p'; break; case MAIL_ATTRIBUTE_TYPE_SHARED: type[0] = 's'; break; } i_assert(type[0] != '\0'); dsync_serializer_encode_add(encoder, "type", type); dsync_serializer_encode_add(encoder, "key", attr->key); if (attr->value != NULL) dsync_serializer_encode_add(encoder, "value", attr->value); else if (attr->value_stream != NULL) dsync_serializer_encode_add(encoder, "stream", ""); if (attr->deleted) dsync_serializer_encode_add(encoder, "deleted", ""); if (attr->last_change != 0) { dsync_serializer_encode_add(encoder, "last_change", dec2str(attr->last_change)); } if (attr->modseq != 0) { dsync_serializer_encode_add(encoder, "modseq", dec2str(attr->modseq)); } dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); if (attr->value_stream != NULL) { ibc->value_output_last = '\0'; ibc->value_output = attr->value_stream; i_stream_ref(ibc->value_output); (void)dsync_ibc_stream_send_value_stream(ibc); } } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mailbox_attribute(struct dsync_ibc *_ibc, const struct dsync_mailbox_attribute **attr_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; pool_t pool = ibc->ret_pool; struct dsync_deserializer_decoder *decoder; struct dsync_mailbox_attribute *attr; const char *value; enum dsync_ibc_recv_ret ret; if (ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_ATTRIBUTES) return DSYNC_IBC_RECV_RET_FINISHED; if (ibc->value_input != NULL) { /* wait until the mail's stream has been read */ return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (ibc->cur_attr != NULL) { /* finished reading the stream, return the mail now */ *attr_r = ibc->cur_attr; ibc->cur_attr = NULL; return DSYNC_IBC_RECV_RET_OK; } p_clear(pool); attr = p_new(pool, struct dsync_mailbox_attribute, 1); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAILBOX_ATTRIBUTE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; value = dsync_deserializer_decode_get(decoder, "type"); switch (*value) { case 'p': attr->type = MAIL_ATTRIBUTE_TYPE_PRIVATE; break; case 's': attr->type = MAIL_ATTRIBUTE_TYPE_SHARED; break; default: dsync_ibc_input_error(ibc, decoder, "Invalid type: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "key"); attr->key = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "deleted", &value)) attr->deleted = TRUE; if (dsync_deserializer_decode_try(decoder, "last_change", &value) && str_to_time(value, &attr->last_change) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid last_change"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "modseq", &value) && str_to_uint64(value, &attr->modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } /* NOTE: stream reading must be the last here, because reading a large stream will be finished later by return TRYAGAIN. We need to deserialize all the other fields before that or they'll get lost. */ if (dsync_deserializer_decode_try(decoder, "stream", &value)) { attr->value_stream = dsync_ibc_stream_input_stream(ibc); if (dsync_ibc_stream_read_mail_stream(ibc) <= 0) { ibc->cur_attr = attr; return DSYNC_IBC_RECV_RET_TRYAGAIN; } /* already finished reading the stream */ i_assert(ibc->value_input == NULL); } else if (dsync_deserializer_decode_try(decoder, "value", &value)) attr->value = p_strdup(pool, value); *attr_r = attr; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_change(struct dsync_ibc *_ibc, const struct dsync_mail_change *change) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); char type[2]; str_append_c(str, items[ITEM_MAIL_CHANGE].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAIL_CHANGE); type[0] = type[1] = '\0'; switch (change->type) { case DSYNC_MAIL_CHANGE_TYPE_SAVE: type[0] = 's'; break; case DSYNC_MAIL_CHANGE_TYPE_EXPUNGE: type[0] = 'e'; break; case DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE: type[0] = 'f'; break; } i_assert(type[0] != '\0'); dsync_serializer_encode_add(encoder, "type", type); dsync_serializer_encode_add(encoder, "uid", dec2str(change->uid)); if (change->guid != NULL) dsync_serializer_encode_add(encoder, "guid", change->guid); if (change->hdr_hash != NULL) { dsync_serializer_encode_add(encoder, "hdr_hash", change->hdr_hash); } if (change->modseq != 0) { dsync_serializer_encode_add(encoder, "modseq", dec2str(change->modseq)); } if (change->pvt_modseq != 0) { dsync_serializer_encode_add(encoder, "pvt_modseq", dec2str(change->pvt_modseq)); } if (change->add_flags != 0) { dsync_serializer_encode_add(encoder, "add_flags", t_strdup_printf("%x", change->add_flags)); } if (change->remove_flags != 0) { dsync_serializer_encode_add(encoder, "remove_flags", t_strdup_printf("%x", change->remove_flags)); } if (change->final_flags != 0) { dsync_serializer_encode_add(encoder, "final_flags", t_strdup_printf("%x", change->final_flags)); } if (change->keywords_reset) dsync_serializer_encode_add(encoder, "keywords_reset", ""); if (array_is_created(&change->keyword_changes) && array_count(&change->keyword_changes) > 0) { string_t *kw_str = t_str_new(128); const char *const *changes; unsigned int i, count; changes = array_get(&change->keyword_changes, &count); str_append_tabescaped(kw_str, changes[0]); for (i = 1; i < count; i++) { str_append_c(kw_str, '\t'); str_append_tabescaped(kw_str, changes[i]); } dsync_serializer_encode_add(encoder, "keyword_changes", str_c(kw_str)); } if (change->received_timestamp > 0) { dsync_serializer_encode_add(encoder, "received_timestamp", t_strdup_printf("%lx", (unsigned long)change->received_timestamp)); } if (change->virtual_size > 0) { dsync_serializer_encode_add(encoder, "virtual_size", t_strdup_printf("%llx", (unsigned long long)change->virtual_size)); } dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_change(struct dsync_ibc *_ibc, const struct dsync_mail_change **change_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; pool_t pool = ibc->ret_pool; struct dsync_deserializer_decoder *decoder; struct dsync_mail_change *change; const char *value; unsigned int uintval; unsigned long long ullongval; enum dsync_ibc_recv_ret ret; p_clear(pool); change = p_new(pool, struct dsync_mail_change, 1); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAIL_CHANGE, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; value = dsync_deserializer_decode_get(decoder, "type"); switch (*value) { case 's': change->type = DSYNC_MAIL_CHANGE_TYPE_SAVE; break; case 'e': change->type = DSYNC_MAIL_CHANGE_TYPE_EXPUNGE; break; case 'f': change->type = DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE; break; default: dsync_ibc_input_error(ibc, decoder, "Invalid type: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } value = dsync_deserializer_decode_get(decoder, "uid"); if (str_to_uint32(value, &change->uid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "guid", &value)) change->guid = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "hdr_hash", &value)) change->hdr_hash = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "modseq", &value) && str_to_uint64(value, &change->modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "pvt_modseq", &value) && str_to_uint64(value, &change->pvt_modseq) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid pvt_modseq"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "add_flags", &value)) { if (str_to_uint_hex(value, &uintval) < 0 || uintval > (uint8_t)-1) { dsync_ibc_input_error(ibc, decoder, "Invalid add_flags: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } change->add_flags = uintval; } if (dsync_deserializer_decode_try(decoder, "remove_flags", &value)) { if (str_to_uint_hex(value, &uintval) < 0 || uintval > (uint8_t)-1) { dsync_ibc_input_error(ibc, decoder, "Invalid remove_flags: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } change->remove_flags = uintval; } if (dsync_deserializer_decode_try(decoder, "final_flags", &value)) { if (str_to_uint_hex(value, &uintval) < 0 || uintval > (uint8_t)-1) { dsync_ibc_input_error(ibc, decoder, "Invalid final_flags: %s", value); return DSYNC_IBC_RECV_RET_TRYAGAIN; } change->final_flags = uintval; } if (dsync_deserializer_decode_try(decoder, "keywords_reset", &value)) change->keywords_reset = TRUE; if (dsync_deserializer_decode_try(decoder, "keyword_changes", &value) && *value != '\0') { const char *const *changes = t_strsplit_tabescaped(value); unsigned int i, count = str_array_length(changes); p_array_init(&change->keyword_changes, pool, count); for (i = 0; i < count; i++) { value = p_strdup(pool, changes[i]); array_append(&change->keyword_changes, &value, 1); } } if (dsync_deserializer_decode_try(decoder, "received_timestamp", &value)) { if (str_to_ullong_hex(value, &ullongval) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid received_timestamp"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } change->received_timestamp = ullongval; } if (dsync_deserializer_decode_try(decoder, "virtual_size", &value)) { if (str_to_ullong_hex(value, &ullongval) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid virtual_size"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } change->virtual_size = ullongval; } *change_r = change; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_mail_request(struct dsync_ibc *_ibc, const struct dsync_mail_request *request) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); str_append_c(str, items[ITEM_MAIL_REQUEST].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAIL_REQUEST); if (request->guid != NULL) dsync_serializer_encode_add(encoder, "guid", request->guid); if (request->uid != 0) { dsync_serializer_encode_add(encoder, "uid", dec2str(request->uid)); } dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mail_request(struct dsync_ibc *_ibc, const struct dsync_mail_request **request_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; struct dsync_mail_request *request; const char *value; enum dsync_ibc_recv_ret ret; p_clear(ibc->ret_pool); request = p_new(ibc->ret_pool, struct dsync_mail_request, 1); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAIL_REQUEST, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; if (dsync_deserializer_decode_try(decoder, "guid", &value)) request->guid = p_strdup(ibc->ret_pool, value); if (dsync_deserializer_decode_try(decoder, "uid", &value) && str_to_uint32(value, &request->uid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } *request_r = request; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_mail(struct dsync_ibc *_ibc, const struct dsync_mail *mail) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); i_assert(!mail->minimal_fields); i_assert(ibc->value_output == NULL); str_append_c(str, items[ITEM_MAIL].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_MAIL); if (mail->guid != NULL) dsync_serializer_encode_add(encoder, "guid", mail->guid); if (mail->uid != 0) dsync_serializer_encode_add(encoder, "uid", dec2str(mail->uid)); if (mail->pop3_uidl != NULL) { dsync_serializer_encode_add(encoder, "pop3_uidl", mail->pop3_uidl); } if (mail->pop3_order > 0) { dsync_serializer_encode_add(encoder, "pop3_order", dec2str(mail->pop3_order)); } if (mail->received_date > 0) { dsync_serializer_encode_add(encoder, "received_date", dec2str(mail->received_date)); } if (mail->saved_date != 0) { dsync_serializer_encode_add(encoder, "saved_date", dec2str(mail->saved_date)); } if (mail->input != NULL) dsync_serializer_encode_add(encoder, "stream", ""); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); if (mail->input != NULL) { ibc->value_output_last = '\0'; ibc->value_output = mail->input; i_stream_ref(ibc->value_output); (void)dsync_ibc_stream_send_value_stream(ibc); } } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_mail(struct dsync_ibc *_ibc, struct dsync_mail **mail_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; pool_t pool = ibc->ret_pool; struct dsync_deserializer_decoder *decoder; struct dsync_mail *mail; const char *value; enum dsync_ibc_recv_ret ret; if (ibc->value_input != NULL) { /* wait until the mail's stream has been read */ return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (ibc->cur_mail != NULL) { /* finished reading the stream, return the mail now */ *mail_r = ibc->cur_mail; ibc->cur_mail = NULL; return DSYNC_IBC_RECV_RET_OK; } p_clear(pool); mail = p_new(pool, struct dsync_mail, 1); ret = dsync_ibc_stream_input_next(ibc, ITEM_MAIL, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; if (dsync_deserializer_decode_try(decoder, "guid", &value)) mail->guid = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "uid", &value) && str_to_uint32(value, &mail->uid) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid uid"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "pop3_uidl", &value)) mail->pop3_uidl = p_strdup(pool, value); if (dsync_deserializer_decode_try(decoder, "pop3_order", &value) && str_to_uint(value, &mail->pop3_order) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid pop3_order"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "received_date", &value) && str_to_time(value, &mail->received_date) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid received_date"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "saved_date", &value) && str_to_time(value, &mail->saved_date) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid saved_date"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "stream", &value)) { mail->input = dsync_ibc_stream_input_stream(ibc); if (dsync_ibc_stream_read_mail_stream(ibc) <= 0) { ibc->cur_mail = mail; return DSYNC_IBC_RECV_RET_TRYAGAIN; } /* already finished reading the stream */ i_assert(ibc->value_input == NULL); } *mail_r = mail; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_send_finish(struct dsync_ibc *_ibc, const char *error, enum mail_error mail_error, bool require_full_resync) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_serializer_encoder *encoder; string_t *str = t_str_new(128); str_append_c(str, items[ITEM_FINISH].chr); encoder = dsync_ibc_send_encode_begin(ibc, ITEM_FINISH); if (error != NULL) dsync_serializer_encode_add(encoder, "error", error); if (mail_error != 0) { dsync_serializer_encode_add(encoder, "mail_error", dec2str(mail_error)); } if (require_full_resync) dsync_serializer_encode_add(encoder, "require_full_resync", ""); dsync_serializer_encode_finish(&encoder, str); dsync_ibc_stream_send_string(ibc, str); } static enum dsync_ibc_recv_ret dsync_ibc_stream_recv_finish(struct dsync_ibc *_ibc, const char **error_r, enum mail_error *mail_error_r, bool *require_full_resync_r) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; struct dsync_deserializer_decoder *decoder; const char *value; enum dsync_ibc_recv_ret ret; int i = 0; *error_r = NULL; *mail_error_r = 0; *require_full_resync_r = FALSE; p_clear(ibc->ret_pool); if (ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_FINISH) return DSYNC_IBC_RECV_RET_OK; ret = dsync_ibc_stream_input_next(ibc, ITEM_FINISH, &decoder); if (ret != DSYNC_IBC_RECV_RET_OK) return ret; if (dsync_deserializer_decode_try(decoder, "error", &value)) *error_r = p_strdup(ibc->ret_pool, value); if (dsync_deserializer_decode_try(decoder, "mail_error", &value) && str_to_int(value, &i) < 0) { dsync_ibc_input_error(ibc, decoder, "Invalid mail_error"); return DSYNC_IBC_RECV_RET_TRYAGAIN; } if (dsync_deserializer_decode_try(decoder, "require_full_resync", &value)) *require_full_resync_r = TRUE; *mail_error_r = i; ibc->finish_received = TRUE; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_stream_close_mail_streams(struct dsync_ibc *_ibc) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; if (ibc->value_output != NULL) { i_stream_unref(&ibc->value_output); dsync_ibc_stream_stop(ibc); } } static bool dsync_ibc_stream_is_send_queue_full(struct dsync_ibc *_ibc) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; size_t bytes; if (ibc->value_output != NULL) return TRUE; bytes = o_stream_get_buffer_used_size(ibc->output); if (bytes < DSYNC_IBC_STREAM_OUTBUF_THROTTLE_SIZE) return FALSE; o_stream_set_flush_pending(ibc->output, TRUE); return TRUE; } static bool dsync_ibc_stream_has_pending_data(struct dsync_ibc *_ibc) { struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc; return ibc->has_pending_data; } static const struct dsync_ibc_vfuncs dsync_ibc_stream_vfuncs = { dsync_ibc_stream_deinit, dsync_ibc_stream_send_handshake, dsync_ibc_stream_recv_handshake, dsync_ibc_stream_send_end_of_list, dsync_ibc_stream_send_mailbox_state, dsync_ibc_stream_recv_mailbox_state, dsync_ibc_stream_send_mailbox_tree_node, dsync_ibc_stream_recv_mailbox_tree_node, dsync_ibc_stream_send_mailbox_deletes, dsync_ibc_stream_recv_mailbox_deletes, dsync_ibc_stream_send_mailbox, dsync_ibc_stream_recv_mailbox, dsync_ibc_stream_send_mailbox_attribute, dsync_ibc_stream_recv_mailbox_attribute, dsync_ibc_stream_send_change, dsync_ibc_stream_recv_change, dsync_ibc_stream_send_mail_request, dsync_ibc_stream_recv_mail_request, dsync_ibc_stream_send_mail, dsync_ibc_stream_recv_mail, dsync_ibc_stream_send_finish, dsync_ibc_stream_recv_finish, dsync_ibc_stream_close_mail_streams, dsync_ibc_stream_is_send_queue_full, dsync_ibc_stream_has_pending_data }; struct dsync_ibc * dsync_ibc_init_stream(struct istream *input, struct ostream *output, const char *name, const char *temp_path_prefix, unsigned int timeout_secs) { struct dsync_ibc_stream *ibc; ibc = i_new(struct dsync_ibc_stream, 1); ibc->ibc.v = dsync_ibc_stream_vfuncs; ibc->input = input; ibc->output = output; ibc->name = i_strdup(name); ibc->temp_path_prefix = i_strdup(temp_path_prefix); ibc->timeout_secs = timeout_secs; ibc->ret_pool = pool_alloconly_create("ibc stream data", 2048); dsync_ibc_stream_init(ibc); return &ibc->ibc; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-import.c0000644000175000017500000027001413165463624020116 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "hex-binary.h" #include "istream.h" #include "seq-range-array.h" #include "imap-util.h" #include "mail-storage-private.h" #include "mail-search-build.h" #include "dsync-transaction-log-scan.h" #include "dsync-mail.h" #include "dsync-mailbox.h" #include "dsync-mailbox-import.h" struct importer_mail { const char *guid; uint32_t uid; }; struct importer_new_mail { /* linked list of mails for this GUID */ struct importer_new_mail *next; /* if non-NULL, this mail exists in both local and remote. this link points to the other side. */ struct importer_new_mail *link; const char *guid; struct dsync_mail_change *change; /* the final UID for the message */ uint32_t final_uid; /* the original local UID, or 0 if exists only remotely */ uint32_t local_uid; /* the original remote UID, or 0 if exists only remotely */ uint32_t remote_uid; /* UID for the mail in the virtual \All mailbox */ uint32_t virtual_all_uid; unsigned int uid_in_local:1; unsigned int uid_is_usable:1; unsigned int skip:1; unsigned int expunged:1; unsigned int copy_failed:1; unsigned int saved:1; }; /* for quickly testing that two-way sync doesn't actually do any unexpected modifications. */ #define IMPORTER_DEBUG_CHANGE(importer) /*i_assert(!importer->master_brain)*/ HASH_TABLE_DEFINE_TYPE(guid_new_mail, const char *, struct importer_new_mail *); HASH_TABLE_DEFINE_TYPE(uid_new_mail, void *, struct importer_new_mail *); struct dsync_mailbox_importer { pool_t pool; struct mailbox *box; uint32_t last_common_uid; uint64_t last_common_modseq, last_common_pvt_modseq; uint32_t remote_uid_next; uint32_t remote_first_recent_uid; uint64_t remote_highest_modseq, remote_highest_pvt_modseq; time_t sync_since_timestamp; time_t sync_until_timestamp; uoff_t sync_max_size; enum mailbox_transaction_flags transaction_flags; unsigned int hdr_hash_version; unsigned int commit_msgs_interval; const char *const *hashed_headers; enum mail_flags sync_flag; const char *sync_keyword; bool sync_flag_dontwant; struct mailbox_transaction_context *trans, *ext_trans; struct mail_search_context *search_ctx; struct mail *mail, *ext_mail; struct mailbox *virtual_all_box; struct mailbox_transaction_context *virtual_trans; struct mail *virtual_mail; struct mail *cur_mail; const char *cur_guid; const char *cur_hdr_hash; /* UID => struct dsync_mail_change */ HASH_TABLE_TYPE(dsync_uid_mail_change) local_changes; HASH_TABLE_TYPE(dsync_attr_change) local_attr_changes; ARRAY_TYPE(seq_range) maybe_expunge_uids; ARRAY(struct dsync_mail_change *) maybe_saves; /* GUID => struct importer_new_mail */ HASH_TABLE_TYPE(guid_new_mail) import_guids; /* UID => struct importer_new_mail */ HASH_TABLE_TYPE(uid_new_mail) import_uids; ARRAY(struct importer_new_mail *) newmails; ARRAY_TYPE(uint32_t) wanted_uids; ARRAY_TYPE(uint32_t) saved_uids; uint32_t highest_wanted_uid; ARRAY(struct dsync_mail_request) mail_requests; unsigned int mail_request_idx; uint32_t prev_uid, next_local_seq, local_uid_next; uint64_t local_initial_highestmodseq, local_initial_highestpvtmodseq; unsigned int import_pos, import_count; unsigned int first_unsaved_idx, saves_since_commit; enum mail_error mail_error; unsigned int failed:1; unsigned int require_full_resync:1; unsigned int debug:1; unsigned int stateful_import:1; unsigned int last_common_uid_found:1; unsigned int cur_uid_has_change:1; unsigned int cur_mail_skip:1; unsigned int local_expunged_guids_set:1; unsigned int new_uids_assigned:1; unsigned int want_mail_requests:1; unsigned int master_brain:1; unsigned int revert_local_changes:1; unsigned int mails_have_guids:1; unsigned int mails_use_guid128:1; unsigned int delete_mailbox:1; unsigned int empty_hdr_workaround:1; }; static const char *dsync_mail_change_type_names[] = { "save", "expunge", "flag-change" }; static bool dsync_mailbox_save_newmails(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail, struct importer_new_mail *all_newmails, bool remote_mail); static int dsync_mailbox_import_commit(struct dsync_mailbox_importer *importer, bool final); static void ATTR_FORMAT(2, 3) imp_debug(struct dsync_mailbox_importer *importer, const char *fmt, ...) { va_list args; if (importer->debug) T_BEGIN { va_start(args, fmt); i_debug("brain %c: Import %s: %s", importer->master_brain ? 'M' : 'S', mailbox_get_vname(importer->box), t_strdup_vprintf(fmt, args)); va_end(args); } T_END; } static void dsync_import_unexpected_state(struct dsync_mailbox_importer *importer, const char *error) { if (!importer->stateful_import) { i_error("Mailbox %s: %s", mailbox_get_vname(importer->box), error); } else { i_warning("Mailbox %s doesn't match previous state: %s " "(dsync must be run again without the state)", mailbox_get_vname(importer->box), error); } importer->require_full_resync = TRUE; } static void dsync_mailbox_import_search_init(struct dsync_mailbox_importer *importer) { struct mail_search_args *search_args; struct mail_search_arg *sarg; search_args = mail_search_build_init(); sarg = mail_search_build_add(search_args, SEARCH_UIDSET); p_array_init(&sarg->value.seqset, search_args->pool, 128); seq_range_array_add_range(&sarg->value.seqset, importer->last_common_uid+1, (uint32_t)-1); importer->search_ctx = mailbox_search_init(importer->trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); if (mailbox_search_next(importer->search_ctx, &importer->cur_mail)) importer->next_local_seq = importer->cur_mail->seq; /* this flag causes cur_guid to be looked up later */ importer->cur_mail_skip = TRUE; } static void dsync_mailbox_import_transaction_begin(struct dsync_mailbox_importer *importer) { const enum mailbox_transaction_flags ext_trans_flags = importer->transaction_flags | MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS; importer->trans = mailbox_transaction_begin(importer->box, importer->transaction_flags); importer->ext_trans = mailbox_transaction_begin(importer->box, ext_trans_flags); importer->mail = mail_alloc(importer->trans, 0, NULL); importer->ext_mail = mail_alloc(importer->ext_trans, 0, NULL); } struct dsync_mailbox_importer * dsync_mailbox_import_init(struct mailbox *box, struct mailbox *virtual_all_box, struct dsync_transaction_log_scan *log_scan, uint32_t last_common_uid, uint64_t last_common_modseq, uint64_t last_common_pvt_modseq, uint32_t remote_uid_next, uint32_t remote_first_recent_uid, uint64_t remote_highest_modseq, uint64_t remote_highest_pvt_modseq, time_t sync_since_timestamp, time_t sync_until_timestamp, uoff_t sync_max_size, const char *sync_flag, unsigned int commit_msgs_interval, enum dsync_mailbox_import_flags flags, unsigned int hdr_hash_version, const char *const *hashed_headers) { struct dsync_mailbox_importer *importer; struct mailbox_status status; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"dsync mailbox importer", 10240); importer = p_new(pool, struct dsync_mailbox_importer, 1); importer->pool = pool; importer->box = box; importer->virtual_all_box = virtual_all_box; importer->last_common_uid = last_common_uid; importer->last_common_modseq = last_common_modseq; importer->last_common_pvt_modseq = last_common_pvt_modseq; importer->last_common_uid_found = last_common_uid != 0 || last_common_modseq != 0; importer->remote_uid_next = remote_uid_next; importer->remote_first_recent_uid = remote_first_recent_uid; importer->remote_highest_modseq = remote_highest_modseq; importer->remote_highest_pvt_modseq = remote_highest_pvt_modseq; importer->sync_since_timestamp = sync_since_timestamp; importer->sync_until_timestamp = sync_until_timestamp; importer->sync_max_size = sync_max_size; importer->stateful_import = importer->last_common_uid_found; importer->hashed_headers = hashed_headers; if (sync_flag != NULL) { if (sync_flag[0] == '-') { importer->sync_flag_dontwant = TRUE; sync_flag++; } if (sync_flag[0] == '\\') importer->sync_flag = imap_parse_system_flag(sync_flag); else importer->sync_keyword = p_strdup(pool, sync_flag); } importer->commit_msgs_interval = commit_msgs_interval; importer->transaction_flags = MAILBOX_TRANSACTION_FLAG_SYNC; if ((flags & DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY) != 0) importer->transaction_flags |= MAILBOX_TRANSACTION_FLAG_NO_NOTIFY; hash_table_create(&importer->import_guids, pool, 0, str_hash, strcmp); hash_table_create_direct(&importer->import_uids, pool, 0); i_array_init(&importer->maybe_expunge_uids, 16); i_array_init(&importer->maybe_saves, 128); i_array_init(&importer->newmails, 128); i_array_init(&importer->wanted_uids, 128); i_array_init(&importer->saved_uids, 128); dsync_mailbox_import_transaction_begin(importer); if ((flags & DSYNC_MAILBOX_IMPORT_FLAG_WANT_MAIL_REQUESTS) != 0) { i_array_init(&importer->mail_requests, 128); importer->want_mail_requests = TRUE; } importer->master_brain = (flags & DSYNC_MAILBOX_IMPORT_FLAG_MASTER_BRAIN) != 0; importer->revert_local_changes = (flags & DSYNC_MAILBOX_IMPORT_FLAG_REVERT_LOCAL_CHANGES) != 0; importer->debug = (flags & DSYNC_MAILBOX_IMPORT_FLAG_DEBUG) != 0; importer->mails_have_guids = (flags & DSYNC_MAILBOX_IMPORT_FLAG_MAILS_HAVE_GUIDS) != 0; importer->mails_use_guid128 = (flags & DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128) != 0; importer->hdr_hash_version = hdr_hash_version; importer->empty_hdr_workaround = (flags & DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND) != 0; mailbox_get_open_status(importer->box, STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ | STATUS_HIGHESTPVTMODSEQ, &status); importer->local_uid_next = status.uidnext; importer->local_initial_highestmodseq = status.highest_modseq; importer->local_initial_highestpvtmodseq = status.highest_pvt_modseq; dsync_mailbox_import_search_init(importer); if (!importer->stateful_import) ; else if (importer->local_uid_next <= last_common_uid) { dsync_import_unexpected_state(importer, t_strdup_printf( "local UIDNEXT %u <= last common UID %u", importer->local_uid_next, last_common_uid)); } else if (importer->local_initial_highestmodseq < last_common_modseq) { dsync_import_unexpected_state(importer, t_strdup_printf( "local HIGHESTMODSEQ %llu < last common HIGHESTMODSEQ %llu", (unsigned long long)importer->local_initial_highestmodseq, (unsigned long long)last_common_modseq)); } else if (importer->local_initial_highestpvtmodseq < last_common_pvt_modseq) { dsync_import_unexpected_state(importer, t_strdup_printf( "local HIGHESTMODSEQ %llu < last common HIGHESTMODSEQ %llu", (unsigned long long)importer->local_initial_highestpvtmodseq, (unsigned long long)last_common_pvt_modseq)); } importer->local_changes = dsync_transaction_log_scan_get_hash(log_scan); importer->local_attr_changes = dsync_transaction_log_scan_get_attr_hash(log_scan); return importer; } static int dsync_mailbox_import_lookup_attr(struct dsync_mailbox_importer *importer, enum mail_attribute_type type, const char *key, struct dsync_mailbox_attribute **attr_r) { struct dsync_mailbox_attribute lookup_attr, *attr; const struct dsync_mailbox_attribute *attr_change; struct mail_attribute_value value; *attr_r = NULL; if (mailbox_attribute_get_stream(importer->trans, type, key, &value) < 0) { i_error("Mailbox %s: Failed to get attribute %s: %s", mailbox_get_vname(importer->box), key, mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; return -1; } lookup_attr.type = type; lookup_attr.key = key; attr_change = hash_table_lookup(importer->local_attr_changes, &lookup_attr); if (attr_change == NULL && value.value == NULL && value.value_stream == NULL) { /* we have no knowledge of this attribute */ return 0; } attr = t_new(struct dsync_mailbox_attribute, 1); attr->type = type; attr->key = key; attr->value = value.value; attr->value_stream = value.value_stream; attr->last_change = value.last_change; if (attr_change != NULL) { attr->deleted = attr_change->deleted && !DSYNC_ATTR_HAS_VALUE(attr); attr->modseq = attr_change->modseq; } *attr_r = attr; return 0; } static int dsync_istreams_cmp(struct istream *input1, struct istream *input2, int *cmp_r) { const unsigned char *data1, *data2; size_t size1, size2, size; *cmp_r = -1; /* quiet gcc */ for (;;) { (void)i_stream_read_data(input1, &data1, &size1, 0); (void)i_stream_read_data(input2, &data2, &size2, 0); if (size1 == 0 || size2 == 0) break; size = I_MIN(size1, size2); *cmp_r = memcmp(data1, data2, size); if (*cmp_r != 0) return 0; i_stream_skip(input1, size); i_stream_skip(input2, size); } if (input1->stream_errno != 0) { errno = input1->stream_errno; i_error("read(%s) failed: %s", i_stream_get_name(input1), i_stream_get_error(input1)); return -1; } if (input2->stream_errno != 0) { errno = input2->stream_errno; i_error("read(%s) failed: %s", i_stream_get_name(input2), i_stream_get_error(input2)); return -1; } if (size1 == 0 && size2 == 0) *cmp_r = 0; else *cmp_r = size1 == 0 ? -1 : 1; return 0; } static int dsync_attributes_cmp_values(const struct dsync_mailbox_attribute *attr1, const struct dsync_mailbox_attribute *attr2, int *cmp_r) { struct istream *input1, *input2; int ret; i_assert(attr1->value_stream != NULL || attr1->value != NULL); i_assert(attr2->value_stream != NULL || attr2->value != NULL); if (attr1->value != NULL && attr2->value != NULL) { *cmp_r = strcmp(attr1->value, attr2->value); return 0; } /* at least one of them is a stream. make both of them streams. */ input1 = attr1->value_stream != NULL ? attr1->value_stream : i_stream_create_from_data(attr1->value, strlen(attr1->value)); input2 = attr2->value_stream != NULL ? attr2->value_stream : i_stream_create_from_data(attr2->value, strlen(attr2->value)); i_stream_seek(input1, 0); i_stream_seek(input2, 0); ret = dsync_istreams_cmp(input1, input2, cmp_r); if (attr1->value_stream == NULL) i_stream_unref(&input1); if (attr2->value_stream == NULL) i_stream_unref(&input2); return ret; } static int dsync_attributes_cmp(const struct dsync_mailbox_attribute *attr, const struct dsync_mailbox_attribute *local_attr, int *cmp_r) { if (DSYNC_ATTR_HAS_VALUE(attr) && !DSYNC_ATTR_HAS_VALUE(local_attr)) { /* remote has a value and local doesn't -> use it */ *cmp_r = 1; return 0; } else if (!DSYNC_ATTR_HAS_VALUE(attr) && DSYNC_ATTR_HAS_VALUE(local_attr)) { /* remote doesn't have a value, bt local does -> skip */ *cmp_r = -1; return 0; } return dsync_attributes_cmp_values(attr, local_attr, cmp_r); } static int dsync_mailbox_import_attribute_real(struct dsync_mailbox_importer *importer, const struct dsync_mailbox_attribute *attr, const struct dsync_mailbox_attribute *local_attr, const char **result_r) { struct mail_attribute_value value; int cmp; bool ignore = FALSE; i_assert(DSYNC_ATTR_HAS_VALUE(attr) || attr->deleted); if (attr->deleted && (local_attr == NULL || !DSYNC_ATTR_HAS_VALUE(local_attr))) { /* attribute doesn't exist on either side -> ignore */ *result_r = "Nonexistent in both sides"; return 0; } if (local_attr == NULL) { /* we haven't seen this locally -> use whatever remote has */ *result_r = "Nonexistent locally"; } else if (local_attr->modseq <= importer->last_common_modseq && attr->modseq > importer->last_common_modseq && importer->last_common_modseq > 0) { /* we're doing incremental syncing, and we can see that the attribute was changed remotely, but not locally -> use it */ *result_r = "Changed remotely"; } else if (local_attr->modseq > importer->last_common_modseq && attr->modseq <= importer->last_common_modseq && importer->last_common_modseq > 0) { /* we're doing incremental syncing, and we can see that the attribute was changed locally, but not remotely -> ignore */ *result_r = "Changed locally"; ignore = TRUE; } else if (attr->last_change > local_attr->last_change) { /* remote has a newer timestamp -> use it */ *result_r = "Remote has newer timestamp"; } else if (attr->last_change < local_attr->last_change) { /* remote has an older timestamp -> ignore */ *result_r = "Local has newer timestamp"; ignore = TRUE; } else { /* the timestamps are the same. now we're down to guessing the right answer, unless the values are actually equal, so check that first. next try to use modseqs, but if even they are the same, fallback to just picking one based on the value. */ if (dsync_attributes_cmp(attr, local_attr, &cmp) < 0) { importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; return -1; } if (cmp == 0) { /* identical scripts */ *result_r = "Unchanged value"; return 0; } if (attr->modseq > local_attr->modseq) { /* remote has a higher modseq -> use it */ *result_r = "Remote has newer modseq"; } else if (attr->modseq < local_attr->modseq) { /* remote has an older modseq -> ignore */ *result_r = "Local has newer modseq"; ignore = TRUE; } else if (cmp < 0) { ignore = TRUE; *result_r = "Value changed, but unknown which is newer - picking local"; } else { *result_r = "Value changed, but unknown which is newer - picking remote"; } } if (ignore) return 0; i_zero(&value); value.value = attr->value; value.value_stream = attr->value_stream; value.last_change = attr->last_change; if (mailbox_attribute_set(importer->trans, attr->type, attr->key, &value) < 0) { i_error("Mailbox %s: Failed to set attribute %s: %s", mailbox_get_vname(importer->box), attr->key, mailbox_get_last_internal_error(importer->box, NULL)); /* the attributes aren't vital, don't fail everything just because of them. */ } return 0; } int dsync_mailbox_import_attribute(struct dsync_mailbox_importer *importer, const struct dsync_mailbox_attribute *attr) { struct dsync_mailbox_attribute *local_attr; const char *result = ""; int ret; if (dsync_mailbox_import_lookup_attr(importer, attr->type, attr->key, &local_attr) < 0) ret = -1; else { ret = dsync_mailbox_import_attribute_real(importer, attr, local_attr, &result); if (local_attr != NULL && local_attr->value_stream != NULL) i_stream_unref(&local_attr->value_stream); } imp_debug(importer, "Import attribute %s: %s", attr->key, ret < 0 ? "failed" : result); return ret; } static void dsync_mail_error(struct dsync_mailbox_importer *importer, struct mail *mail, const char *field) { const char *errstr; enum mail_error error; errstr = mailbox_get_last_internal_error(importer->box, &error); if (error == MAIL_ERROR_EXPUNGED) return; i_error("Mailbox %s: Can't lookup %s for UID=%u: %s", mailbox_get_vname(mail->box), field, mail->uid, errstr); importer->mail_error = error; importer->failed = TRUE; } static bool dsync_mail_change_guid_equals(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change, const char *guid, const char **cmp_guid_r) { guid_128_t guid_128, change_guid_128; if (change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { if (guid_128_from_string(change->guid, change_guid_128) < 0) i_unreached(); } else if (importer->mails_use_guid128) { mail_generate_guid_128_hash(change->guid, change_guid_128); } else { if (cmp_guid_r != NULL) *cmp_guid_r = change->guid; return strcmp(change->guid, guid) == 0; } mail_generate_guid_128_hash(guid, guid_128); if (memcmp(change_guid_128, guid_128, GUID_128_SIZE) != 0) { if (cmp_guid_r != NULL) { *cmp_guid_r = t_strdup_printf("%s(guid128, orig=%s)", binary_to_hex(change_guid_128, sizeof(change_guid_128)), change->guid); } return FALSE; } return TRUE; } static int importer_try_next_mail(struct dsync_mailbox_importer *importer, uint32_t wanted_uid) { struct mail_private *pmail; const char *hdr_hash; if (importer->cur_mail == NULL) { /* end of search */ return -1; } while (importer->cur_mail->seq < importer->next_local_seq || importer->cur_mail->uid < wanted_uid) { if (!importer->cur_uid_has_change && !importer->last_common_uid_found) { /* this message exists locally, but remote didn't send expunge-change for it. if the message's uid <= last-common-uid, it should be deleted */ seq_range_array_add(&importer->maybe_expunge_uids, importer->cur_mail->uid); } importer->cur_mail_skip = FALSE; if (!mailbox_search_next(importer->search_ctx, &importer->cur_mail)) { importer->cur_mail = NULL; importer->cur_guid = NULL; importer->cur_hdr_hash = NULL; return -1; } importer->cur_uid_has_change = FALSE; } importer->cur_uid_has_change = importer->cur_mail->uid == wanted_uid; if (importer->mails_have_guids) { if (mail_get_special(importer->cur_mail, MAIL_FETCH_GUID, &importer->cur_guid) < 0) { dsync_mail_error(importer, importer->cur_mail, "GUID"); return 0; } } else { if (dsync_mail_get_hdr_hash(importer->cur_mail, importer->hdr_hash_version, importer->hashed_headers, &hdr_hash) < 0) { dsync_mail_error(importer, importer->cur_mail, "header hash"); return 0; } pmail = (struct mail_private *)importer->cur_mail; importer->cur_hdr_hash = p_strdup(pmail->pool, hdr_hash); importer->cur_guid = ""; } /* make sure next_local_seq gets updated in case we came here because of min_uid */ importer->next_local_seq = importer->cur_mail->seq; return 1; } static bool importer_next_mail(struct dsync_mailbox_importer *importer, uint32_t wanted_uid) { int ret; for (;;) { T_BEGIN { ret = importer_try_next_mail(importer, wanted_uid); } T_END; if (ret != 0 || importer->failed) break; importer->next_local_seq = importer->cur_mail->seq + 1; } return ret > 0; } static int importer_mail_cmp(const struct importer_mail *m1, const struct importer_mail *m2) { int ret; if (m1->guid == NULL) return 1; if (m2->guid == NULL) return -1; ret = strcmp(m1->guid, m2->guid); if (ret != 0) return ret; if (m1->uid < m2->uid) return -1; if (m1->uid > m2->uid) return 1; return 0; } static void newmail_link(struct dsync_mailbox_importer *importer, struct importer_new_mail *newmail, uint32_t remote_uid) { struct importer_new_mail *first_mail, **last, *mail, *link = NULL; if (*newmail->guid != '\0') { first_mail = hash_table_lookup(importer->import_guids, newmail->guid); if (first_mail == NULL) { /* first mail for this GUID */ hash_table_insert(importer->import_guids, newmail->guid, newmail); return; } } else { if (remote_uid == 0) { /* mail exists only locally. we don't want to request it, and we'll assume it has no duplicate instances. */ return; } first_mail = hash_table_lookup(importer->import_uids, POINTER_CAST(remote_uid)); if (first_mail == NULL) { /* first mail for this UID */ hash_table_insert(importer->import_uids, POINTER_CAST(remote_uid), newmail); return; } } /* 1) add the newmail to the end of the linked list 2) find our link FIXME: this loop is slow if the same GUID has a ton of instances. Could it be improved in some way? */ last = &first_mail->next; for (mail = first_mail; mail != NULL; mail = mail->next) { if (mail->final_uid == newmail->final_uid) mail->uid_is_usable = TRUE; if (link == NULL && mail->link == NULL && mail->uid_in_local != newmail->uid_in_local) link = mail; last = &mail->next; } *last = newmail; if (link != NULL && newmail->link == NULL) { link->link = newmail; newmail->link = link; } } static void dsync_mailbox_revert_existing_uid(struct dsync_mailbox_importer *importer, uint32_t uid, const char *reason) { i_assert(importer->revert_local_changes); /* UID either already exists or UIDNEXT is too high. we can't set the wanted UID, so we'll need to delete the whole mailbox and resync */ i_warning("Deleting mailbox '%s': UID=%u already exists locally for a different mail: %s", mailbox_get_vname(importer->box), uid, reason); importer->delete_mailbox = TRUE; importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } static bool dsync_mailbox_try_save_cur(struct dsync_mailbox_importer *importer, struct dsync_mail_change *save_change) { struct importer_mail m1, m2; struct importer_new_mail *newmail; int diff; bool remote_saved; i_zero(&m1); if (importer->cur_mail != NULL) { m1.guid = importer->mails_have_guids ? importer->cur_guid : importer->cur_hdr_hash; m1.uid = importer->cur_mail->uid; } i_zero(&m2); if (save_change != NULL) { m2.guid = importer->mails_have_guids ? save_change->guid : save_change->hdr_hash; m2.uid = save_change->uid; i_assert(save_change->type != DSYNC_MAIL_CHANGE_TYPE_EXPUNGE); } if (importer->empty_hdr_workaround && !importer->mails_have_guids && importer->cur_mail != NULL && save_change != NULL && (dsync_mail_hdr_hash_is_empty(m1.guid) || dsync_mail_hdr_hash_is_empty(m2.guid))) { /* one of the headers is empty. assume it's broken and that the header matches what we have currently. */ diff = 0; } else { diff = importer_mail_cmp(&m1, &m2); } if (diff < 0) { /* add a record for local mail */ i_assert(importer->cur_mail != NULL); if (importer->revert_local_changes) { if (save_change == NULL && importer->cur_mail->uid >= importer->remote_uid_next) { dsync_mailbox_revert_existing_uid(importer, importer->cur_mail->uid, t_strdup_printf("higher than remote's UIDs (remote UIDNEXT=%u)", importer->remote_uid_next)); return TRUE; } mail_expunge(importer->cur_mail); importer->cur_mail_skip = TRUE; importer->next_local_seq++; return FALSE; } newmail = p_new(importer->pool, struct importer_new_mail, 1); newmail->guid = p_strdup(importer->pool, importer->cur_guid); newmail->final_uid = importer->cur_mail->uid; newmail->local_uid = importer->cur_mail->uid; newmail->uid_in_local = TRUE; newmail->uid_is_usable = newmail->final_uid >= importer->remote_uid_next; remote_saved = FALSE; } else if (diff > 0) { i_assert(save_change != NULL); newmail = p_new(importer->pool, struct importer_new_mail, 1); newmail->guid = save_change->guid; newmail->final_uid = save_change->uid; newmail->remote_uid = save_change->uid; newmail->uid_in_local = FALSE; newmail->uid_is_usable = newmail->final_uid >= importer->local_uid_next; if (!newmail->uid_is_usable && importer->revert_local_changes) { dsync_mailbox_revert_existing_uid(importer, newmail->final_uid, t_strdup_printf("UID >= local UIDNEXT=%u", importer->local_uid_next)); return TRUE; } remote_saved = TRUE; } else { /* identical */ i_assert(importer->cur_mail != NULL); i_assert(save_change != NULL); newmail = p_new(importer->pool, struct importer_new_mail, 1); newmail->guid = save_change->guid; newmail->final_uid = importer->cur_mail->uid; newmail->local_uid = importer->cur_mail->uid; newmail->remote_uid = save_change->uid; newmail->uid_in_local = TRUE; newmail->uid_is_usable = TRUE; newmail->link = newmail; remote_saved = TRUE; } if (newmail->uid_in_local) { importer->cur_mail_skip = TRUE; importer->next_local_seq++; } /* NOTE: assumes save_change is allocated from importer pool */ newmail->change = save_change; array_append(&importer->newmails, &newmail, 1); newmail_link(importer, newmail, save_change == NULL ? 0 : save_change->uid); return remote_saved; } static bool ATTR_NULL(2) dsync_mailbox_try_save(struct dsync_mailbox_importer *importer, struct dsync_mail_change *save_change) { if (importer->cur_mail_skip) { if (!importer_next_mail(importer, 0) && save_change == NULL) return FALSE; } return dsync_mailbox_try_save_cur(importer, save_change); } static void dsync_mailbox_save(struct dsync_mailbox_importer *importer, struct dsync_mail_change *save_change) { while (!dsync_mailbox_try_save(importer, save_change)) ; } static bool dsync_import_set_mail(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { const char *guid, *cmp_guid; if (!mail_set_uid(importer->mail, change->uid)) return FALSE; if (change->guid == NULL) { /* GUID is unknown */ return TRUE; } if (*change->guid == '\0') { /* backend doesn't support GUIDs. if hdr_hash is set, we could verify it, but since this message really is supposed to match, it's probably too much trouble. */ return TRUE; } /* verify that GUID matches, just in case */ if (mail_get_special(importer->mail, MAIL_FETCH_GUID, &guid) < 0) { dsync_mail_error(importer, importer->mail, "GUID"); return FALSE; } if (!dsync_mail_change_guid_equals(importer, change, guid, &cmp_guid)) { dsync_import_unexpected_state(importer, t_strdup_printf( "Unexpected GUID mismatch for UID=%u: %s != %s", change->uid, guid, cmp_guid)); return FALSE; } return TRUE; } static bool dsync_check_cur_guid(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { const char *cmp_guid; if (change->guid == NULL || change->guid[0] == '\0' || importer->cur_guid[0] == '\0') return TRUE; if (!dsync_mail_change_guid_equals(importer, change, importer->cur_guid, &cmp_guid)) { dsync_import_unexpected_state(importer, t_strdup_printf( "Unexpected GUID mismatch (2) for UID=%u: %s != %s", change->uid, importer->cur_guid, cmp_guid)); return FALSE; } return TRUE; } static void merge_flags(uint32_t local_final, uint32_t local_add, uint32_t local_remove, uint32_t remote_final, uint32_t remote_add, uint32_t remote_remove, uint32_t pvt_mask, bool prefer_remote, bool prefer_pvt_remote, uint32_t *change_add_r, uint32_t *change_remove_r, bool *remote_changed, bool *remote_pvt_changed) { uint32_t combined_add, combined_remove, conflict_flags; uint32_t local_wanted, remote_wanted, conflict_pvt_flags; /* resolve conflicts */ conflict_flags = local_add & remote_remove; if (conflict_flags != 0) { conflict_pvt_flags = conflict_flags & pvt_mask; conflict_flags &= ~pvt_mask; if (prefer_remote) local_add &= ~conflict_flags; else remote_remove &= ~conflict_flags; if (prefer_pvt_remote) local_add &= ~conflict_pvt_flags; else remote_remove &= ~conflict_pvt_flags; } conflict_flags = local_remove & remote_add; if (conflict_flags != 0) { conflict_pvt_flags = conflict_flags & pvt_mask; conflict_flags &= ~pvt_mask; if (prefer_remote) local_remove &= ~conflict_flags; else remote_add &= ~conflict_flags; if (prefer_pvt_remote) local_remove &= ~conflict_pvt_flags; else remote_add &= ~conflict_pvt_flags; } combined_add = local_add|remote_add; combined_remove = local_remove|remote_remove; i_assert((combined_add & combined_remove) == 0); /* don't change flags that are currently identical in both sides */ conflict_flags = local_final ^ remote_final; combined_add &= conflict_flags; combined_remove &= conflict_flags; /* see if there are conflicting final flags */ local_wanted = (local_final|combined_add) & ~combined_remove; remote_wanted = (remote_final|combined_add) & ~combined_remove; conflict_flags = local_wanted ^ remote_wanted; if (conflict_flags != 0) { if (prefer_remote && prefer_pvt_remote) local_wanted = remote_wanted; else if (prefer_remote && !prefer_pvt_remote) { local_wanted = (local_wanted & pvt_mask) | (remote_wanted & ~pvt_mask); } else if (!prefer_remote && prefer_pvt_remote) { local_wanted = (local_wanted & ~pvt_mask) | (remote_wanted & pvt_mask); } } *change_add_r = local_wanted & ~local_final; *change_remove_r = local_final & ~local_wanted; if ((local_wanted & ~pvt_mask) != (remote_final & ~pvt_mask)) *remote_changed = TRUE; if ((local_wanted & pvt_mask) != (remote_final & pvt_mask)) *remote_pvt_changed = TRUE; } static bool keyword_find(ARRAY_TYPE(const_string) *keywords, const char *name, unsigned int *idx_r) { const char *const *names; unsigned int i, count; names = array_get(keywords, &count); for (i = 0; i < count; i++) { if (strcmp(names[i], name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } static void keywords_append(ARRAY_TYPE(const_string) *dest, const ARRAY_TYPE(const_string) *keywords, uint32_t bits, unsigned int start_idx) { const char *const *namep; unsigned int i; for (i = 0; i < 32; i++) { if ((bits & (1U << i)) == 0) continue; namep = array_idx(keywords, start_idx+i); array_append(dest, namep, 1); } } static void merge_keywords(struct mail *mail, const ARRAY_TYPE(const_string) *local_changes, const ARRAY_TYPE(const_string) *remote_changes, bool prefer_remote, bool *remote_changed, bool *remote_pvt_changed) { /* local_changes and remote_changes are assumed to have no duplicates names */ uint32_t *local_add, *local_remove, *local_final; uint32_t *remote_add, *remote_remove, *remote_final; uint32_t *change_add, *change_remove; ARRAY_TYPE(const_string) all_keywords, add_keywords, remove_keywords; const char *const *changes, *name, *const *local_keywords; struct mail_keywords *kw; unsigned int i, count, name_idx, array_size; local_keywords = mail_get_keywords(mail); /* we'll assign a common index for each keyword name and place the changes to separate bit arrays. */ if (array_is_created(remote_changes)) changes = array_get(remote_changes, &count); else { changes = NULL; count = 0; } array_size = str_array_length(local_keywords) + count; if (array_is_created(local_changes)) array_size += array_count(local_changes); if (array_size == 0) { /* this message has no keywords */ return; } t_array_init(&all_keywords, array_size); t_array_init(&add_keywords, array_size); t_array_init(&remove_keywords, array_size); /* @UNSAFE: create large enough arrays to fit all keyword indexes. */ array_size = (array_size+31)/32; local_add = t_new(uint32_t, array_size); local_remove = t_new(uint32_t, array_size); local_final = t_new(uint32_t, array_size); remote_add = t_new(uint32_t, array_size); remote_remove = t_new(uint32_t, array_size); remote_final = t_new(uint32_t, array_size); change_add = t_new(uint32_t, array_size); change_remove = t_new(uint32_t, array_size); /* get remote changes */ for (i = 0; i < count; i++) { name = changes[i]+1; name_idx = array_count(&all_keywords); array_append(&all_keywords, &name, 1); switch (changes[i][0]) { case KEYWORD_CHANGE_ADD: remote_add[name_idx/32] |= 1U << (name_idx%32); break; case KEYWORD_CHANGE_REMOVE: remote_remove[name_idx/32] |= 1U << (name_idx%32); break; case KEYWORD_CHANGE_FINAL: remote_final[name_idx/32] |= 1U << (name_idx%32); break; case KEYWORD_CHANGE_ADD_AND_FINAL: remote_add[name_idx/32] |= 1U << (name_idx%32); remote_final[name_idx/32] |= 1U << (name_idx%32); break; } } /* get local changes. use existing indexes for names when they exist. */ if (array_is_created(local_changes)) changes = array_get(local_changes, &count); else { changes = NULL; count = 0; } for (i = 0; i < count; i++) { name = changes[i]+1; if (!keyword_find(&all_keywords, name, &name_idx)) { name_idx = array_count(&all_keywords); array_append(&all_keywords, &name, 1); } switch (changes[i][0]) { case KEYWORD_CHANGE_ADD: case KEYWORD_CHANGE_ADD_AND_FINAL: local_add[name_idx/32] |= 1U << (name_idx%32); break; case KEYWORD_CHANGE_REMOVE: local_remove[name_idx/32] |= 1U << (name_idx%32); break; case KEYWORD_CHANGE_FINAL: break; } } for (i = 0; local_keywords[i] != NULL; i++) { name = local_keywords[i]; if (!keyword_find(&all_keywords, name, &name_idx)) { name_idx = array_count(&all_keywords); array_append(&all_keywords, &name, 1); } local_final[name_idx/32] |= 1U << (name_idx%32); } i_assert(array_count(&all_keywords) <= array_size*32); array_size = (array_count(&all_keywords)+31) / 32; /* merge keywords */ for (i = 0; i < array_size; i++) { merge_flags(local_final[i], local_add[i], local_remove[i], remote_final[i], remote_add[i], remote_remove[i], 0, prefer_remote, prefer_remote, &change_add[i], &change_remove[i], remote_changed, remote_pvt_changed); if (change_add[i] != 0) { keywords_append(&add_keywords, &all_keywords, change_add[i], i*32); } if (change_remove[i] != 0) { keywords_append(&remove_keywords, &all_keywords, change_remove[i], i*32); } } /* apply changes */ if (array_count(&add_keywords) > 0) { array_append_zero(&add_keywords); kw = mailbox_keywords_create_valid(mail->box, array_idx(&add_keywords, 0)); mail_update_keywords(mail, MODIFY_ADD, kw); mailbox_keywords_unref(&kw); } if (array_count(&remove_keywords) > 0) { array_append_zero(&remove_keywords); kw = mailbox_keywords_create_valid(mail->box, array_idx(&remove_keywords, 0)); mail_update_keywords(mail, MODIFY_REMOVE, kw); mailbox_keywords_unref(&kw); } } static void dsync_mailbox_import_replace_flags(struct mail *mail, const struct dsync_mail_change *change) { ARRAY_TYPE(const_string) keywords; struct mail_keywords *kw; const char *const *changes, *name; unsigned int i, count; if (array_is_created(&change->keyword_changes)) changes = array_get(&change->keyword_changes, &count); else { changes = NULL; count = 0; } t_array_init(&keywords, count+1); for (i = 0; i < count; i++) { switch (changes[i][0]) { case KEYWORD_CHANGE_ADD: case KEYWORD_CHANGE_FINAL: case KEYWORD_CHANGE_ADD_AND_FINAL: name = changes[i]+1; array_append(&keywords, &name, 1); break; case KEYWORD_CHANGE_REMOVE: break; } } array_append_zero(&keywords); kw = mailbox_keywords_create_valid(mail->box, array_idx(&keywords, 0)); mail_update_keywords(mail, MODIFY_REPLACE, kw); mailbox_keywords_unref(&kw); mail_update_flags(mail, MODIFY_REPLACE, change->add_flags | change->final_flags); if (mail_get_modseq(mail) < change->modseq) mail_update_modseq(mail, change->modseq); if (mail_get_pvt_modseq(mail) < change->pvt_modseq) mail_update_pvt_modseq(mail, change->pvt_modseq); } static void dsync_mailbox_import_flag_change(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { const struct dsync_mail_change *local_change; enum mail_flags local_add, local_remove; uint32_t change_add, change_remove; uint64_t new_modseq; ARRAY_TYPE(const_string) local_keyword_changes = ARRAY_INIT; struct mail *mail; bool prefer_remote, prefer_pvt_remote; bool remote_changed = FALSE, remote_pvt_changed = FALSE; i_assert((change->add_flags & change->remove_flags) == 0); if (importer->cur_mail != NULL && importer->cur_mail->uid == change->uid) { if (!dsync_check_cur_guid(importer, change)) return; mail = importer->cur_mail; } else { if (!dsync_import_set_mail(importer, change)) return; mail = importer->mail; } if (importer->revert_local_changes) { /* dsync backup: just make the local look like remote. */ dsync_mailbox_import_replace_flags(mail, change); return; } local_change = hash_table_lookup(importer->local_changes, POINTER_CAST(change->uid)); if (local_change == NULL) { local_add = local_remove = 0; } else { local_add = local_change->add_flags; local_remove = local_change->remove_flags; local_keyword_changes = local_change->keyword_changes; } if (mail_get_modseq(mail) < change->modseq) prefer_remote = TRUE; else if (mail_get_modseq(mail) > change->modseq) prefer_remote = FALSE; else { /* identical modseq, we'll just have to pick one. Note that both brains need to pick the same one, otherwise they become unsynced. */ prefer_remote = !importer->master_brain; } if (mail_get_pvt_modseq(mail) < change->pvt_modseq) prefer_pvt_remote = TRUE; else if (mail_get_pvt_modseq(mail) > change->pvt_modseq) prefer_pvt_remote = FALSE; else prefer_pvt_remote = !importer->master_brain; /* merge flags */ merge_flags(mail_get_flags(mail), local_add, local_remove, change->final_flags, change->add_flags, change->remove_flags, mailbox_get_private_flags_mask(mail->box), prefer_remote, prefer_pvt_remote, &change_add, &change_remove, &remote_changed, &remote_pvt_changed); if (change_add != 0) mail_update_flags(mail, MODIFY_ADD, change_add); if (change_remove != 0) mail_update_flags(mail, MODIFY_REMOVE, change_remove); /* merge keywords */ merge_keywords(mail, &local_keyword_changes, &change->keyword_changes, prefer_remote, &remote_changed, &remote_pvt_changed); /* update modseqs. try to anticipate when we have to increase modseq to get it closer to what remote has (although we can't guess it exactly correctly) */ new_modseq = change->modseq; if (remote_changed && new_modseq <= importer->remote_highest_modseq) new_modseq = importer->remote_highest_modseq+1; if (mail_get_modseq(mail) < new_modseq) mail_update_modseq(mail, new_modseq); new_modseq = change->pvt_modseq; if (remote_pvt_changed && new_modseq <= importer->remote_highest_pvt_modseq) new_modseq = importer->remote_highest_pvt_modseq+1; if (mail_get_pvt_modseq(mail) < new_modseq) mail_update_pvt_modseq(mail, new_modseq); } static bool dsync_mail_change_have_keyword(const struct dsync_mail_change *change, const char *keyword) { const char *const *strp; if (!array_is_created(&change->keyword_changes)) return FALSE; array_foreach(&change->keyword_changes, strp) { switch ((*strp)[0]) { case KEYWORD_CHANGE_FINAL: case KEYWORD_CHANGE_ADD_AND_FINAL: if (strcasecmp((*strp)+1, keyword) == 0) return TRUE; break; default: break; } } return FALSE; } static bool dsync_mailbox_import_want_change(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change, const char **result_r) { if (importer->sync_since_timestamp > 0) { i_assert(change->received_timestamp > 0); if (change->received_timestamp < importer->sync_since_timestamp) { /* mail has too old timestamp - skip it */ *result_r = "Ignoring missing local mail with too old timestamp"; return FALSE; } } if (importer->sync_until_timestamp > 0) { i_assert(change->received_timestamp > 0); if (change->received_timestamp > importer->sync_until_timestamp) { /* mail has too new timestamp - skip it */ *result_r = "Ignoring missing local mail with too new timestamp"; return FALSE; } } if (importer->sync_max_size > 0) { i_assert(change->virtual_size != (uoff_t)-1); if (change->virtual_size < importer->sync_max_size) { /* mail is too large - skip it */ *result_r = "Ignoring missing local mail with too large size"; return FALSE; } } if (importer->sync_flag != 0) { bool have_flag = (change->final_flags & importer->sync_flag) != 0; if (have_flag && importer->sync_flag_dontwant) { *result_r = "Ignoring missing local mail that doesn't have wanted flags"; return FALSE; } if (!have_flag && !importer->sync_flag_dontwant) { *result_r = "Ignoring missing local mail that has unwanted flags"; return FALSE; } } if (importer->sync_keyword != NULL) { bool have_kw = dsync_mail_change_have_keyword(change, importer->sync_keyword); if (have_kw && importer->sync_flag_dontwant) { *result_r = "Ignoring missing local mail that doesn't have wanted keywords"; return FALSE; } if (!have_kw && !importer->sync_flag_dontwant) { *result_r = "Ignoring missing local mail that has unwanted keywords"; return FALSE; } } return TRUE; } static void dsync_mailbox_import_save(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { struct dsync_mail_change *save; const char *result; i_assert(change->guid != NULL); if (change->uid == importer->last_common_uid) { /* we've already verified that the GUID matches. apply flag changes if there are any. */ i_assert(!importer->last_common_uid_found); dsync_mailbox_import_flag_change(importer, change); return; } if (!dsync_mailbox_import_want_change(importer, change, &result)) return; save = p_new(importer->pool, struct dsync_mail_change, 1); dsync_mail_change_dup(importer->pool, change, save); if (importer->last_common_uid_found) { /* this is a new mail. its UID may or may not conflict with an existing local mail, we'll figure it out later. */ i_assert(change->uid > importer->last_common_uid); dsync_mailbox_save(importer, save); } else { /* the local mail is expunged. we'll decide later if we want to save this mail locally or expunge it form remote. */ i_assert(change->uid > importer->last_common_uid); i_assert(importer->cur_mail == NULL || change->uid < importer->cur_mail->uid); array_append(&importer->maybe_saves, &save, 1); } } static void dsync_mailbox_import_expunge(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { if (importer->last_common_uid_found) { /* expunge the message, unless its GUID unexpectedly doesn't match */ i_assert(change->uid <= importer->last_common_uid); if (dsync_import_set_mail(importer, change)) mail_expunge(importer->mail); } else if (importer->cur_mail == NULL || change->uid < importer->cur_mail->uid) { /* already expunged locally, we can ignore this. uid=last_common_uid if we managed to verify from transaction log that the GUIDs match */ i_assert(change->uid >= importer->last_common_uid); } else if (change->uid == importer->last_common_uid) { /* already verified that the GUID matches */ i_assert(importer->cur_mail->uid == change->uid); if (dsync_check_cur_guid(importer, change)) mail_expunge(importer->cur_mail); } else { /* we don't know yet if we should expunge this message or not. queue it until we do. */ i_assert(change->uid > importer->last_common_uid); seq_range_array_add(&importer->maybe_expunge_uids, change->uid); } } static void dsync_mailbox_rewind_search(struct dsync_mailbox_importer *importer) { /* If there are local mails after last_common_uid which we skipped while trying to match the next message, we need to now go back */ if (importer->cur_mail != NULL && importer->cur_mail->uid <= importer->last_common_uid+1) return; importer->cur_mail = NULL; importer->cur_guid = NULL; importer->cur_hdr_hash = NULL; importer->next_local_seq = 0; (void)mailbox_search_deinit(&importer->search_ctx); dsync_mailbox_import_search_init(importer); } static void dsync_mailbox_common_uid_found(struct dsync_mailbox_importer *importer) { struct dsync_mail_change *const *saves; struct seq_range_iter iter; unsigned int n, i, count; uint32_t uid; if (importer->debug) T_BEGIN { string_t *expunges = t_str_new(64); imap_write_seq_range(expunges, &importer->maybe_expunge_uids); imp_debug(importer, "Last common UID=%u. Delayed expunges=%s", importer->last_common_uid, str_c(expunges)); } T_END; importer->last_common_uid_found = TRUE; dsync_mailbox_rewind_search(importer); /* expunge the messages whose expunge-decision we delayed previously */ seq_range_array_iter_init(&iter, &importer->maybe_expunge_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { if (uid > importer->last_common_uid) { /* we expunge messages only up to last_common_uid, ignore the rest */ break; } if (mail_set_uid(importer->mail, uid)) mail_expunge(importer->mail); } /* handle pending saves */ saves = array_get(&importer->maybe_saves, &count); for (i = 0; i < count; i++) { if (saves[i]->uid > importer->last_common_uid) { imp_debug(importer, "Delayed save UID=%u: Save", saves[i]->uid); dsync_mailbox_save(importer, saves[i]); } else { imp_debug(importer, "Delayed save UID=%u: Ignore", saves[i]->uid); } } } static int dsync_mailbox_import_match_msg(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change, const char **result_r) { const char *hdr_hash, *cmp_guid; if (*change->guid != '\0' && *importer->cur_guid != '\0') { /* we have GUIDs, verify them */ if (dsync_mail_change_guid_equals(importer, change, importer->cur_guid, &cmp_guid)) { *result_r = "GUIDs match"; return 1; } else { *result_r = t_strdup_printf("GUIDs don't match (%s vs %s)", importer->cur_guid, cmp_guid); return 0; } } /* verify hdr_hash if it exists */ if (change->hdr_hash == NULL) { i_assert(*importer->cur_guid == '\0'); if (change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { /* the message was already expunged, so we don't know its header. return "unknown". */ *result_r = "Unknown match for expunge"; return -1; } i_error("Mailbox %s: GUIDs not supported, " "sync with header hashes instead", mailbox_get_vname(importer->box)); importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; *result_r = "Error, invalid parameters"; return -1; } if (dsync_mail_get_hdr_hash(importer->cur_mail, importer->hdr_hash_version, importer->hashed_headers, &hdr_hash) < 0) { dsync_mail_error(importer, importer->cur_mail, "hdr-stream"); *result_r = "Error fetching header stream"; return -1; } if (importer->empty_hdr_workaround && (dsync_mail_hdr_hash_is_empty(change->hdr_hash) || dsync_mail_hdr_hash_is_empty(hdr_hash))) { *result_r = "Empty headers found with workaround enabled - assuming a match"; return 1; } else if (strcmp(change->hdr_hash, hdr_hash) == 0) { *result_r = "Headers hashes match"; return 1; } else { *result_r = t_strdup_printf("Headers hashes don't match (%s vs %s)", change->hdr_hash, hdr_hash); return 0; } } static bool dsync_mailbox_find_common_expunged_uid(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change, const char **result_r) { const struct dsync_mail_change *local_change; if (*change->guid == '\0') { /* remote doesn't support GUIDs, can't verify expunge */ *result_r = "GUIDs not supported, can't verify expunge"; return FALSE; } /* local message is expunged. see if we can find its GUID from transaction log and check if the GUIDs match. The GUID in log is a 128bit GUID, so we may need to convert the remote's GUID string to 128bit GUID first. */ local_change = hash_table_lookup(importer->local_changes, POINTER_CAST(change->uid)); if (local_change == NULL || local_change->guid == NULL) { *result_r = "Expunged local mail's GUID not found"; return FALSE; } i_assert(local_change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE); if (dsync_mail_change_guid_equals(importer, local_change, change->guid, NULL)) { importer->last_common_uid = change->uid; *result_r = "Expunged local mail's GUID matches remote"; } else if (change->type != DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { dsync_mailbox_common_uid_found(importer); *result_r = "Expunged local mail's GUID doesn't match remote GUID"; } else { /* GUID mismatch for two expunged mails. dsync can't update GUIDs for already expunged messages, so we can't immediately determine that the rest of the messages are a mismatch. so for now we'll just skip over this pair. */ *result_r = "Expunged mails' GUIDs don't match - delaying decision"; /* NOTE: the return value here doesn't matter, because the only caller that checks for it never reaches this code path */ } return TRUE; } static void dsync_mailbox_revert_missing(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { i_assert(importer->revert_local_changes); /* mail exists on remote, but not locally. we'll need to insert this mail back, which means deleting the whole mailbox and resyncing. */ i_warning("Deleting mailbox '%s': UID=%u GUID=%s is missing locally", mailbox_get_vname(importer->box), change->uid, change->guid); importer->delete_mailbox = TRUE; importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } static void dsync_mailbox_find_common_uid(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change, const char **result_r) { int ret; i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE || ((change->received_timestamp > 0 || (importer->sync_since_timestamp == 0 && importer->sync_until_timestamp == 0)) && (change->virtual_size != (uoff_t)-1 || importer->sync_max_size == 0))); /* try to find the matching local mail */ if (!importer_next_mail(importer, change->uid)) { /* no more local mails. we can still try to match expunged mails though. */ if (change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { /* mail doesn't exist remotely either, don't bother looking it up locally. */ *result_r = "Expunged mail not found locally"; return; } i_assert(change->guid != NULL); if (!dsync_mailbox_import_want_change(importer, change, result_r)) ; else if (importer->local_uid_next <= change->uid) { dsync_mailbox_common_uid_found(importer); *result_r = "Mail's UID is above local UIDNEXT"; } else if (importer->revert_local_changes) { dsync_mailbox_revert_missing(importer, change); *result_r = "Reverting local change by deleting mailbox"; } else if (!dsync_mailbox_find_common_expunged_uid(importer, change, result_r)) { /* it's unknown if this mail existed locally and was expunged. since we don't want to lose any mails, assume that we need to preserve the mail. use the last message with a matching GUID as the last common UID. */ dsync_mailbox_common_uid_found(importer); } *result_r = t_strdup_printf("%s - No more local mails found", *result_r); return; } if (change->guid == NULL) { /* we can't know if this UID matches */ i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE); *result_r = "Expunged mail has no GUID, can't verify it"; return; } if (importer->cur_mail->uid == change->uid) { /* we have a matching local UID. check GUID to see if it's really the same mail or not */ if ((ret = dsync_mailbox_import_match_msg(importer, change, result_r)) < 0) { /* unknown */ return; } if (ret > 0) { importer->last_common_uid = change->uid; } else if (!importer->revert_local_changes) { /* mismatch - found the first non-common UID */ dsync_mailbox_common_uid_found(importer); } else { /* mismatch and we want to revert local changes - need to delete the mailbox. */ dsync_mailbox_revert_existing_uid(importer, change->uid, *result_r); } return; } /* mail exists remotely, but doesn't exist locally. */ if (!dsync_mailbox_import_want_change(importer, change, result_r)) return; if (importer->revert_local_changes && change->type != DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { dsync_mailbox_revert_missing(importer, change); *result_r = "Reverting local change by deleting mailbox"; } else { (void)dsync_mailbox_find_common_expunged_uid(importer, change, result_r); } *result_r = t_strdup_printf("%s (next local mail UID=%u)", *result_r, importer->cur_mail == NULL ? 0 : importer->cur_mail->uid); } int dsync_mailbox_import_change(struct dsync_mailbox_importer *importer, const struct dsync_mail_change *change) { const char *result; i_assert(!importer->new_uids_assigned); i_assert(importer->prev_uid < change->uid); importer->prev_uid = change->uid; if (importer->failed) return -1; if (importer->require_full_resync) return 0; if (!importer->last_common_uid_found) { result = NULL; dsync_mailbox_find_common_uid(importer, change, &result); i_assert(result != NULL); } else { result = "New mail"; } imp_debug(importer, "Import change type=%s GUID=%s UID=%u hdr_hash=%s result=%s", dsync_mail_change_type_names[change->type], change->guid != NULL ? change->guid : "", change->uid, change->hdr_hash != NULL ? change->hdr_hash : "", result); if (importer->failed) return -1; if (importer->require_full_resync) return 0; if (importer->last_common_uid_found) { /* a) uid <= last_common_uid for flag changes and expunges. this happens only when last_common_uid was originally given as parameter to importer. when we're finding the last_common_uid ourself, uid>last_common_uid always in here, because last_common_uid_found=TRUE only after we find the first mismatch. b) uid > last_common_uid for i) new messages, ii) expunges that were sent "just in case" */ if (change->uid <= importer->last_common_uid) { i_assert(change->type != DSYNC_MAIL_CHANGE_TYPE_SAVE); } else if (change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE) { /* ignore */ return 0; } else { i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_SAVE); } } else { /* a) uid < last_common_uid can never happen */ i_assert(change->uid >= importer->last_common_uid); /* b) uid = last_common_uid if we've verified that the messages' GUIDs match so far. c) uid > last_common_uid: i) TYPE_EXPUNGE change has GUID=NULL, so we couldn't verify yet if it matches our local message, ii) local message is expunged and we couldn't find its GUID */ if (change->uid > importer->last_common_uid) { i_assert(change->type == DSYNC_MAIL_CHANGE_TYPE_EXPUNGE || importer->cur_mail == NULL || change->uid < importer->cur_mail->uid); } } switch (change->type) { case DSYNC_MAIL_CHANGE_TYPE_SAVE: dsync_mailbox_import_save(importer, change); break; case DSYNC_MAIL_CHANGE_TYPE_EXPUNGE: dsync_mailbox_import_expunge(importer, change); break; case DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE: i_assert(importer->last_common_uid_found); dsync_mailbox_import_flag_change(importer, change); break; } return importer->failed ? -1 : 0; } static int importer_new_mail_final_uid_cmp(struct importer_new_mail *const *newmail1, struct importer_new_mail *const *newmail2) { if ((*newmail1)->final_uid < (*newmail2)->final_uid) return -1; if ((*newmail1)->final_uid > (*newmail2)->final_uid) return 1; return 0; } static void dsync_mailbox_import_assign_new_uids(struct dsync_mailbox_importer *importer) { struct importer_new_mail *newmail, *const *newmailp; uint32_t common_uid_next, new_uid; common_uid_next = I_MAX(importer->local_uid_next, importer->remote_uid_next); array_foreach_modifiable(&importer->newmails, newmailp) { newmail = *newmailp; if (newmail->skip) { /* already assigned */ i_assert(newmail->final_uid != 0); continue; } /* figure out what UID to use for the mail */ if (newmail->uid_is_usable) { /* keep the UID */ new_uid = newmail->final_uid; } else if (newmail->link != NULL && newmail->link->uid_is_usable) { /* we can use the linked message's UID and expunge this mail */ new_uid = newmail->link->final_uid; } else { i_assert(!importer->revert_local_changes); new_uid = common_uid_next++; imp_debug(importer, "UID %u isn't usable, assigning new UID %u", newmail->final_uid, new_uid); } newmail->final_uid = new_uid; if (newmail->link != NULL && newmail->link != newmail) { /* skip processing the linked mail */ newmail->link->skip = TRUE; } } importer->last_common_uid = common_uid_next-1; importer->new_uids_assigned = TRUE; /* Sort the newmails by their final_uid. This is used for tracking whether an intermediate commit is allowed. */ array_sort(&importer->newmails, importer_new_mail_final_uid_cmp); } static int dsync_mailbox_import_local_uid(struct dsync_mailbox_importer *importer, struct mail *mail, uint32_t uid, const char *guid, struct dsync_mail *dmail_r) { const char *error_field, *errstr; enum mail_error error; if (!mail_set_uid(mail, uid)) return 0; /* NOTE: Errors are logged, but they don't cause the entire import to fail. */ if (dsync_mail_fill(mail, TRUE, dmail_r, &error_field) < 0) { errstr = mailbox_get_last_internal_error(mail->box, &error); if (error == MAIL_ERROR_EXPUNGED) return 0; i_error("Mailbox %s: Can't lookup %s for UID=%u: %s", mailbox_get_vname(importer->box), error_field, uid, errstr); return -1; } if (*guid != '\0' && strcmp(guid, dmail_r->guid) != 0) { dsync_import_unexpected_state(importer, t_strdup_printf( "Unexpected GUID mismatch (3) for UID=%u: %s != %s", uid, dmail_r->guid, guid)); return -1; } return 1; } static void dsync_mailbox_import_saved_uid(struct dsync_mailbox_importer *importer, uint32_t uid) { i_assert(importer->search_ctx == NULL); if (importer->highest_wanted_uid < uid) importer->highest_wanted_uid = uid; array_append(&importer->wanted_uids, &uid, 1); } static void dsync_mailbox_import_update_first_saved(struct dsync_mailbox_importer *importer) { struct importer_new_mail *const *newmails; unsigned int count; newmails = array_get(&importer->newmails, &count); while (importer->first_unsaved_idx < count) { if (!newmails[importer->first_unsaved_idx]->saved) break; importer->first_unsaved_idx++; } } static void dsync_mailbox_import_saved_newmail(struct dsync_mailbox_importer *importer, struct importer_new_mail *newmail) { dsync_mailbox_import_saved_uid(importer, newmail->final_uid); newmail->saved = TRUE; dsync_mailbox_import_update_first_saved(importer); importer->saves_since_commit++; /* we can commit only if all the upcoming mails will have UIDs that are larger than we're committing. Note that if any existing UIDs have been changed, the new UID is usually higher than anything that is being saved so we can't do an intermediate commit. It's too much extra work to try to handle that situation. So here this never happens, because then array_count(wanted_uids) is always higher than first_unsaved_idx. */ if (importer->saves_since_commit >= importer->commit_msgs_interval && importer->first_unsaved_idx == array_count(&importer->wanted_uids)) { if (dsync_mailbox_import_commit(importer, FALSE) < 0) importer->failed = TRUE; importer->saves_since_commit = 0; } } static bool dsync_msg_change_uid(struct dsync_mailbox_importer *importer, uint32_t old_uid, uint32_t new_uid) { struct mail_save_context *save_ctx; IMPORTER_DEBUG_CHANGE(importer); if (!mail_set_uid(importer->mail, old_uid)) return FALSE; save_ctx = mailbox_save_alloc(importer->ext_trans); mailbox_save_copy_flags(save_ctx, importer->mail); mailbox_save_set_uid(save_ctx, new_uid); if (mailbox_move(&save_ctx, importer->mail) < 0) return FALSE; dsync_mailbox_import_saved_uid(importer, new_uid); return TRUE; } static bool dsync_mailbox_import_change_uid(struct dsync_mailbox_importer *importer, ARRAY_TYPE(seq_range) *unwanted_uids, uint32_t wanted_uid) { const struct seq_range *range; unsigned int count, n; struct seq_range_iter iter; uint32_t uid; /* optimize by first trying to use the latest UID */ range = array_get(unwanted_uids, &count); if (count == 0) return FALSE; if (dsync_msg_change_uid(importer, range[count-1].seq2, wanted_uid)) { seq_range_array_remove(unwanted_uids, range[count-1].seq2); return TRUE; } if (mailbox_get_last_mail_error(importer->box) == MAIL_ERROR_EXPUNGED) seq_range_array_remove(unwanted_uids, range[count-1].seq2); /* now try to use any of them by iterating through them. (would be easier&faster to just iterate backwards, but probably too much trouble to add such API) */ n = 0; seq_range_array_iter_init(&iter, unwanted_uids); while (seq_range_array_iter_nth(&iter, n++, &uid)) { if (dsync_msg_change_uid(importer, uid, wanted_uid)) { seq_range_array_remove(unwanted_uids, uid); return TRUE; } if (mailbox_get_last_mail_error(importer->box) == MAIL_ERROR_EXPUNGED) seq_range_array_remove(unwanted_uids, uid); } return FALSE; } static bool dsync_mailbox_import_try_local(struct dsync_mailbox_importer *importer, struct importer_new_mail *all_newmails, ARRAY_TYPE(seq_range) *local_uids, ARRAY_TYPE(seq_range) *wanted_uids) { ARRAY_TYPE(seq_range) assigned_uids, unwanted_uids; struct seq_range_iter local_iter, wanted_iter; unsigned int local_n, wanted_n; uint32_t local_uid, wanted_uid; struct importer_new_mail *mail; struct dsync_mail dmail; if (array_count(local_uids) == 0) return FALSE; local_n = wanted_n = 0; seq_range_array_iter_init(&local_iter, local_uids); seq_range_array_iter_init(&wanted_iter, wanted_uids); /* wanted_uids contains UIDs that need to exist at the end. those that don't already exist in local_uids have a higher UID than any existing local UID */ t_array_init(&assigned_uids, array_count(wanted_uids)); t_array_init(&unwanted_uids, 8); while (seq_range_array_iter_nth(&local_iter, local_n++, &local_uid)) { if (seq_range_array_iter_nth(&wanted_iter, wanted_n, &wanted_uid)) { if (local_uid == wanted_uid) { /* we have exactly the UID we want. keep it. */ seq_range_array_add(&assigned_uids, wanted_uid); wanted_n++; continue; } i_assert(local_uid < wanted_uid); } /* we no longer want this local UID. */ seq_range_array_add(&unwanted_uids, local_uid); } /* reuse as many existing messages as possible by changing their UIDs */ while (seq_range_array_iter_nth(&wanted_iter, wanted_n, &wanted_uid)) { if (!dsync_mailbox_import_change_uid(importer, &unwanted_uids, wanted_uid)) break; seq_range_array_add(&assigned_uids, wanted_uid); wanted_n++; } /* expunge all unwanted messages */ local_n = 0; seq_range_array_iter_init(&local_iter, &unwanted_uids); while (seq_range_array_iter_nth(&local_iter, local_n++, &local_uid)) { IMPORTER_DEBUG_CHANGE(importer); if (mail_set_uid(importer->mail, local_uid)) mail_expunge(importer->mail); } /* mark mails whose UIDs we got to be skipped over later */ for (mail = all_newmails; mail != NULL; mail = mail->next) { if (!mail->skip && seq_range_exists(&assigned_uids, mail->final_uid)) mail->skip = TRUE; } if (!seq_range_array_iter_nth(&wanted_iter, wanted_n, &wanted_uid)) { /* we've assigned all wanted UIDs */ return TRUE; } /* try to find one existing message that we can use to copy to the other instances */ local_n = 0; seq_range_array_iter_init(&local_iter, local_uids); while (seq_range_array_iter_nth(&local_iter, local_n++, &local_uid)) { if (dsync_mailbox_import_local_uid(importer, importer->mail, local_uid, all_newmails->guid, &dmail) > 0) { if (dsync_mailbox_save_newmails(importer, &dmail, all_newmails, FALSE)) return TRUE; } } return FALSE; } static bool dsync_mailbox_import_try_virtual_all(struct dsync_mailbox_importer *importer, struct importer_new_mail *all_newmails) { struct dsync_mail dmail; if (all_newmails->virtual_all_uid == 0) return FALSE; if (dsync_mailbox_import_local_uid(importer, importer->virtual_mail, all_newmails->virtual_all_uid, all_newmails->guid, &dmail) > 0) { if (dsync_mailbox_save_newmails(importer, &dmail, all_newmails, FALSE)) return TRUE; } return FALSE; } static bool dsync_mailbox_import_handle_mail(struct dsync_mailbox_importer *importer, struct importer_new_mail *all_newmails) { ARRAY_TYPE(seq_range) local_uids, wanted_uids; struct dsync_mail_request *request; struct importer_new_mail *mail; const char *request_guid = NULL; uint32_t request_uid = 0; i_assert(all_newmails != NULL); /* get the list of the current local UIDs and the wanted UIDs. find the first remote instance that we can request in case there are no local instances */ t_array_init(&local_uids, 8); t_array_init(&wanted_uids, 8); for (mail = all_newmails; mail != NULL; mail = mail->next) { if (mail->uid_in_local) seq_range_array_add(&local_uids, mail->local_uid); else if (request_guid == NULL) { if (*mail->guid != '\0') request_guid = mail->guid; request_uid = mail->remote_uid; i_assert(request_uid != 0); } if (!mail->skip) seq_range_array_add(&wanted_uids, mail->final_uid); } i_assert(array_count(&wanted_uids) > 0); if (!dsync_mailbox_import_try_local(importer, all_newmails, &local_uids, &wanted_uids) && !dsync_mailbox_import_try_virtual_all(importer, all_newmails)) { /* no local instance. request from remote */ IMPORTER_DEBUG_CHANGE(importer); if (importer->want_mail_requests) { request = array_append_space(&importer->mail_requests); request->guid = request_guid; request->uid = request_uid; } return FALSE; } /* successfully handled all the mails locally */ importer->import_pos++; return TRUE; } static void dsync_mailbox_import_find_virtual_uids(struct dsync_mailbox_importer *importer) { struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct importer_new_mail *newmail; struct mail *mail; const char *guid; if (mailbox_sync(importer->virtual_all_box, 0) < 0) { i_error("Couldn't sync \\All mailbox '%s': %s", mailbox_get_vname(importer->virtual_all_box), mailbox_get_last_internal_error(importer->virtual_all_box, NULL)); return; } search_args = mail_search_build_init(); mail_search_build_add_all(search_args); importer->virtual_trans = mailbox_transaction_begin(importer->virtual_all_box, importer->transaction_flags); search_ctx = mailbox_search_init(importer->virtual_trans, search_args, NULL, MAIL_FETCH_GUID, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) < 0) { /* ignore errors */ continue; } newmail = hash_table_lookup(importer->import_guids, guid); if (newmail != NULL && newmail->virtual_all_uid == 0) newmail->virtual_all_uid = mail->uid; } if (mailbox_search_deinit(&search_ctx) < 0) { i_error("Couldn't search \\All mailbox '%s': %s", mailbox_get_vname(importer->virtual_all_box), mailbox_get_last_internal_error(importer->virtual_all_box, NULL)); } importer->virtual_mail = mail_alloc(importer->virtual_trans, 0, NULL); } static void dsync_mailbox_import_handle_local_mails(struct dsync_mailbox_importer *importer) { struct hash_iterate_context *iter; const char *key; void *key2; struct importer_new_mail *mail; if (importer->virtual_all_box != NULL && hash_table_count(importer->import_guids) > 0) { /* find UIDs in \All mailbox for all wanted GUIDs. */ dsync_mailbox_import_find_virtual_uids(importer); } iter = hash_table_iterate_init(importer->import_guids); while (hash_table_iterate(iter, importer->import_guids, &key, &mail)) { T_BEGIN { if (dsync_mailbox_import_handle_mail(importer, mail)) hash_table_remove(importer->import_guids, key); } T_END; } hash_table_iterate_deinit(&iter); iter = hash_table_iterate_init(importer->import_uids); while (hash_table_iterate(iter, importer->import_uids, &key2, &mail)) { T_BEGIN { if (dsync_mailbox_import_handle_mail(importer, mail)) hash_table_remove(importer->import_uids, key2); } T_END; } hash_table_iterate_deinit(&iter); } int dsync_mailbox_import_changes_finish(struct dsync_mailbox_importer *importer) { i_assert(!importer->new_uids_assigned); if (!importer->last_common_uid_found) { /* handle pending expunges and flag updates */ dsync_mailbox_common_uid_found(importer); } /* skip common local mails */ (void)importer_next_mail(importer, importer->last_common_uid+1); /* if there are any local mails left, add them to newmails list */ while (importer->cur_mail != NULL && !importer->failed) (void)dsync_mailbox_try_save(importer, NULL); if (importer->search_ctx != NULL) { if (mailbox_search_deinit(&importer->search_ctx) < 0) { i_error("Mailbox %s: Search failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; } } importer->import_count = hash_table_count(importer->import_guids) + hash_table_count(importer->import_uids); dsync_mailbox_import_assign_new_uids(importer); /* save mails from local sources where possible, request the rest from remote */ if (!importer->failed) dsync_mailbox_import_handle_local_mails(importer); return importer->failed ? -1 : 0; } const struct dsync_mail_request * dsync_mailbox_import_next_request(struct dsync_mailbox_importer *importer) { const struct dsync_mail_request *requests; unsigned int count; requests = array_get(&importer->mail_requests, &count); if (importer->mail_request_idx == count) return NULL; return &requests[importer->mail_request_idx++]; } static const char *const * dsync_mailbox_get_final_keywords(const struct dsync_mail_change *change) { ARRAY_TYPE(const_string) keywords; const char *const *changes; unsigned int i, count; if (!array_is_created(&change->keyword_changes)) return NULL; changes = array_get(&change->keyword_changes, &count); t_array_init(&keywords, count); for (i = 0; i < count; i++) { if (changes[i][0] == KEYWORD_CHANGE_ADD || changes[i][0] == KEYWORD_CHANGE_ADD_AND_FINAL) { const char *name = changes[i]+1; array_append(&keywords, &name, 1); } } if (array_count(&keywords) == 0) return NULL; array_append_zero(&keywords); return array_idx(&keywords, 0); } static void dsync_mailbox_save_set_metadata(struct dsync_mailbox_importer *importer, struct mail_save_context *save_ctx, const struct dsync_mail_change *change) { const char *const *keyword_names; struct mail_keywords *keywords; keyword_names = dsync_mailbox_get_final_keywords(change); keywords = keyword_names == NULL ? NULL : mailbox_keywords_create_valid(importer->box, keyword_names); mailbox_save_set_flags(save_ctx, change->final_flags, keywords); if (keywords != NULL) mailbox_keywords_unref(&keywords); if (change->modseq > 1) { (void)mailbox_enable(importer->box, MAILBOX_FEATURE_CONDSTORE); mailbox_save_set_min_modseq(save_ctx, change->modseq); } /* FIXME: if there already are private flags, they get lost because saving can't handle updating private index. they get added on the next sync though. if this is fixed here, set min_pvt_modseq also. */ } static int dsync_msg_try_copy(struct dsync_mailbox_importer *importer, struct mail_save_context **save_ctx_p, struct importer_new_mail **all_newmails_forcopy) { struct importer_new_mail *inst; for (inst = *all_newmails_forcopy; inst != NULL; inst = inst->next) { if (inst->uid_in_local && !inst->copy_failed && mail_set_uid(importer->mail, inst->local_uid)) { if (mailbox_copy(save_ctx_p, importer->mail) < 0) { inst->copy_failed = TRUE; return -1; } *all_newmails_forcopy = inst; return 1; } } *all_newmails_forcopy = NULL; return 0; } static void dsync_mailbox_save_set_nonminimal(struct mail_save_context *save_ctx, const struct dsync_mail *mail) { if (mail->pop3_uidl != NULL && *mail->pop3_uidl != '\0') mailbox_save_set_pop3_uidl(save_ctx, mail->pop3_uidl); if (mail->pop3_order > 0) mailbox_save_set_pop3_order(save_ctx, mail->pop3_order); mailbox_save_set_received_date(save_ctx, mail->received_date, 0); } static struct mail_save_context * dsync_mailbox_save_init(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail, struct importer_new_mail *newmail) { struct mail_save_context *save_ctx; save_ctx = mailbox_save_alloc(importer->ext_trans); mailbox_save_set_uid(save_ctx, newmail->final_uid); if (*mail->guid != '\0') mailbox_save_set_guid(save_ctx, mail->guid); if (mail->saved_date != 0) mailbox_save_set_save_date(save_ctx, mail->saved_date); dsync_mailbox_save_set_metadata(importer, save_ctx, newmail->change); if (!mail->minimal_fields) dsync_mailbox_save_set_nonminimal(save_ctx, mail); return save_ctx; } static bool dsync_mailbox_save_body(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail, struct importer_new_mail *newmail, struct importer_new_mail **all_newmails_forcopy, bool remote_mail) { struct mail_save_context *save_ctx; struct istream *input; ssize_t ret; bool save_failed = FALSE; /* try to save the mail by copying an existing mail */ save_ctx = dsync_mailbox_save_init(importer, mail, newmail); if ((ret = dsync_msg_try_copy(importer, &save_ctx, all_newmails_forcopy)) < 0) { if (save_ctx == NULL) save_ctx = dsync_mailbox_save_init(importer, mail, newmail); } if (ret <= 0 && mail->input_mail != NULL) { /* copy using the source mail */ i_assert(mail->input_mail->uid == mail->input_mail_uid); if (mailbox_copy(&save_ctx, mail->input_mail) == 0) ret = 1; else { ret = -1; save_ctx = dsync_mailbox_save_init(importer, mail, newmail); } } if (ret > 0) { i_assert(save_ctx == NULL); dsync_mailbox_import_saved_newmail(importer, newmail); return TRUE; } /* fallback to saving from remote stream */ if (!remote_mail) { /* the mail isn't remote yet. we were just trying to copy a local mail to avoid downloading the remote mail. */ mailbox_save_cancel(&save_ctx); return FALSE; } if (mail->minimal_fields) { struct dsync_mail mail2; const char *error_field; i_assert(mail->input_mail != NULL); if (dsync_mail_fill_nonminimal(mail->input_mail, &mail2, &error_field) < 0) { i_error("Mailbox %s: Failed to read mail %s uid=%u: %s", mailbox_get_vname(importer->box), error_field, mail->uid, mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; mailbox_save_cancel(&save_ctx); return TRUE; } dsync_mailbox_save_set_nonminimal(save_ctx, &mail2); input = mail2.input; } else { input = mail->input; } if (input == NULL) { /* it was just expunged in remote, skip it */ mailbox_save_cancel(&save_ctx); return TRUE; } i_stream_seek(input, 0); if (mailbox_save_begin(&save_ctx, input) < 0) { i_error("Mailbox %s: Saving failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; return TRUE; } while ((ret = i_stream_read(input)) > 0 || ret == -2) { if (mailbox_save_continue(save_ctx) < 0) { save_failed = TRUE; ret = -1; break; } } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("Mailbox %s: read(msg input) failed: %s", mailbox_get_vname(importer->box), i_stream_get_error(input)); mailbox_save_cancel(&save_ctx); importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } else if (save_failed) { i_error("Mailbox %s: Saving failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); mailbox_save_cancel(&save_ctx); importer->failed = TRUE; } else { i_assert(input->eof); if (mailbox_save_finish(&save_ctx) < 0) { i_error("Mailbox %s: Saving failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; } else { dsync_mailbox_import_saved_newmail(importer, newmail); } } return TRUE; } static bool dsync_mailbox_save_newmails(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail, struct importer_new_mail *all_newmails, bool remote_mail) { struct importer_new_mail *newmail, *all_newmails_forcopy; bool ret = TRUE; /* if all_newmails list is large, avoid scanning through the uninteresting ones for each newmail */ all_newmails_forcopy = all_newmails; /* save all instances of the message */ for (newmail = all_newmails; newmail != NULL && ret; newmail = newmail->next) { if (!newmail->skip) T_BEGIN { if (!dsync_mailbox_save_body(importer, mail, newmail, &all_newmails_forcopy, remote_mail)) ret = FALSE; } T_END; } return ret; } int dsync_mailbox_import_mail(struct dsync_mailbox_importer *importer, const struct dsync_mail *mail) { struct importer_new_mail *all_newmails; i_assert(mail->input == NULL || mail->input->seekable); i_assert(importer->new_uids_assigned); if (importer->failed) return -1; if (importer->require_full_resync) return 0; imp_debug(importer, "Import mail body for GUID=%s UID=%u", mail->guid, mail->uid); all_newmails = *mail->guid != '\0' ? hash_table_lookup(importer->import_guids, mail->guid) : hash_table_lookup(importer->import_uids, POINTER_CAST(mail->uid)); if (all_newmails == NULL) { if (importer->want_mail_requests) { i_error("Mailbox %s: Remote sent unwanted message body for " "GUID=%s UID=%u", mailbox_get_vname(importer->box), mail->guid, mail->uid); } else { imp_debug(importer, "Skip unwanted mail body for " "GUID=%s UID=%u", mail->guid, mail->uid); } return 0; } if (*mail->guid != '\0') hash_table_remove(importer->import_guids, mail->guid); else { hash_table_remove(importer->import_uids, POINTER_CAST(mail->uid)); } importer->import_pos++; if (!dsync_mailbox_save_newmails(importer, mail, all_newmails, TRUE)) i_unreached(); return importer->failed ? -1 : 0; } static int reassign_uids_in_seq_range(struct dsync_mailbox_importer *importer, const ARRAY_TYPE(seq_range) *unwanted_uids) { struct mailbox *box = importer->box; const enum mailbox_transaction_flags trans_flags = MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS; struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_arg *arg; struct mail_search_context *search_ctx; struct mail_save_context *save_ctx; struct mail *mail; unsigned int renumber_count = 0; int ret = 1; if (array_count(unwanted_uids) == 0) return 1; if (importer->debug) T_BEGIN { string_t *str = t_str_new(256); imap_write_seq_range(str, unwanted_uids); imp_debug(importer, "Reassign UIDs: %s", str_c(str)); } T_END; search_args = mail_search_build_init(); arg = mail_search_build_add(search_args, SEARCH_UIDSET); p_array_init(&arg->value.seqset, search_args->pool, array_count(unwanted_uids)); array_append_array(&arg->value.seqset, unwanted_uids); trans = mailbox_transaction_begin(box, trans_flags); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { save_ctx = mailbox_save_alloc(trans); mailbox_save_copy_flags(save_ctx, mail); if (mailbox_move(&save_ctx, mail) < 0) { i_error("Mailbox %s: Couldn't move mail within mailbox: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &importer->mail_error)); ret = -1; } else if (ret > 0) { ret = 0; } renumber_count++; } if (mailbox_search_deinit(&search_ctx) < 0) { i_error("Mailbox %s: mail search failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &importer->mail_error)); ret = -1; } if (mailbox_transaction_commit(&trans) < 0) { i_error("Mailbox %s: UID reassign commit failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, &importer->mail_error)); ret = -1; } if (ret == 0) { imp_debug(importer, "Mailbox %s: Change during sync: " "Renumbered %u of %u unwanted UIDs", mailbox_get_vname(box), renumber_count, array_count(unwanted_uids)); } return ret; } static int reassign_unwanted_uids(struct dsync_mailbox_importer *importer, const char **changes_during_sync_r) { ARRAY_TYPE(seq_range) unwanted_uids; const uint32_t *wanted_uids, *saved_uids; uint32_t highest_seen_uid; unsigned int i, wanted_count, saved_count; int ret = 0; wanted_uids = array_get(&importer->wanted_uids, &wanted_count); saved_uids = array_get(&importer->saved_uids, &saved_count); i_assert(wanted_count == saved_count); if (wanted_count == 0) return 0; /* wanted_uids contains the UIDs we tried to save mails with. if nothing changed during dsync, we should have the expected UIDs (saved_uids) and all is well. if any new messages got inserted during dsync, we'll need to fix up the UIDs and let the next dsync fix up the other side. for example: remote uids = 5,7,9 = wanted_uids remote uidnext = 12 locally added new uid=5 -> saved_uids = 10,7,9 we'll now need to reassign UIDs 5 and 10. to be fully future-proof we'll reassign all UIDs between [original local uidnext .. highest UID we think we know] that aren't in saved_uids. */ /* create uidset for the list of UIDs we don't want to exist */ t_array_init(&unwanted_uids, 8); highest_seen_uid = I_MAX(importer->remote_uid_next-1, importer->highest_wanted_uid); i_assert(importer->local_uid_next <= highest_seen_uid); seq_range_array_add_range(&unwanted_uids, importer->local_uid_next, highest_seen_uid); for (i = 0; i < wanted_count; i++) { i_assert(i < wanted_count); if (saved_uids[i] == wanted_uids[i]) seq_range_array_remove(&unwanted_uids, saved_uids[i]); } ret = reassign_uids_in_seq_range(importer, &unwanted_uids); if (ret == 0) { *changes_during_sync_r = t_strdup_printf( "%u UIDs changed due to UID conflicts", seq_range_count(&unwanted_uids)); /* conflicting changes during sync, revert our last-common-uid back to a safe value. */ importer->last_common_uid = importer->local_uid_next - 1; } return ret < 0 ? -1 : 0; } static int dsync_mailbox_import_commit(struct dsync_mailbox_importer *importer, bool final) { struct mail_transaction_commit_changes changes; struct seq_range_iter iter; uint32_t uid; unsigned int n; int ret = importer->failed ? -1 : 0; mail_free(&importer->mail); mail_free(&importer->ext_mail); /* commit saves */ if (mailbox_transaction_commit_get_changes(&importer->ext_trans, &changes) < 0) { i_error("Mailbox %s: Save commit failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); /* removed wanted_uids that weren't actually saved */ array_delete(&importer->wanted_uids, array_count(&importer->saved_uids), array_count(&importer->wanted_uids) - array_count(&importer->saved_uids)); mailbox_transaction_rollback(&importer->trans); ret = -1; } else { /* remember the UIDs that were successfully saved */ if (importer->debug) T_BEGIN { string_t *str = t_str_new(256); imap_write_seq_range(str, &changes.saved_uids); imp_debug(importer, "Saved UIDs: %s", str_c(str)); } T_END; seq_range_array_iter_init(&iter, &changes.saved_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) array_append(&importer->saved_uids, &uid, 1); pool_unref(&changes.pool); /* commit flag changes and expunges */ if (mailbox_transaction_commit(&importer->trans) < 0) { i_error("Mailbox %s: Commit failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); ret = -1; } } if (!final) dsync_mailbox_import_transaction_begin(importer); return ret; } static int dsync_mailbox_import_finish(struct dsync_mailbox_importer *importer, const char **changes_during_sync_r) { struct mailbox_update update; int ret; ret = dsync_mailbox_import_commit(importer, TRUE); if (ret == 0) { /* update mailbox metadata if we successfully saved everything. */ i_zero(&update); update.min_next_uid = importer->remote_uid_next; update.min_first_recent_uid = I_MIN(importer->last_common_uid+1, importer->remote_first_recent_uid); update.min_highest_modseq = importer->remote_highest_modseq; update.min_highest_pvt_modseq = importer->remote_highest_pvt_modseq; imp_debug(importer, "Finish update: min_next_uid=%u " "min_first_recent_uid=%u min_highest_modseq=%llu " "min_highest_pvt_modseq=%llu", update.min_next_uid, update.min_first_recent_uid, (unsigned long long)update.min_highest_modseq, (unsigned long long)update.min_highest_pvt_modseq); if (mailbox_update(importer->box, &update) < 0) { i_error("Mailbox %s: Update failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); ret = -1; } } /* sync mailbox to finish flag changes and expunges. */ if (mailbox_sync(importer->box, 0) < 0) { i_error("Mailbox %s: Sync failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); ret = -1; } if (ret == 0) { /* give new UIDs to messages that got saved with unwanted UIDs. do it only if the whole transaction succeeded. */ if (reassign_unwanted_uids(importer, changes_during_sync_r) < 0) ret = -1; } return ret; } static void dsync_mailbox_import_check_missing_guid_imports(struct dsync_mailbox_importer *importer) { struct hash_iterate_context *iter; const char *key; struct importer_new_mail *mail; iter = hash_table_iterate_init(importer->import_guids); while (hash_table_iterate(iter, importer->import_guids, &key, &mail)) { for (; mail != NULL; mail = mail->next) { if (mail->skip) continue; i_error("Mailbox %s: Remote didn't send mail GUID=%s (UID=%u)", mailbox_get_vname(importer->box), mail->guid, mail->remote_uid); importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } } hash_table_iterate_deinit(&iter); } static void dsync_mailbox_import_check_missing_uid_imports(struct dsync_mailbox_importer *importer) { struct hash_iterate_context *iter; void *key; struct importer_new_mail *mail; iter = hash_table_iterate_init(importer->import_uids); while (hash_table_iterate(iter, importer->import_uids, &key, &mail)) { for (; mail != NULL; mail = mail->next) { if (mail->skip) continue; i_error("Mailbox %s: Remote didn't send mail UID=%u", mailbox_get_vname(importer->box), mail->remote_uid); importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } } hash_table_iterate_deinit(&iter); } int dsync_mailbox_import_deinit(struct dsync_mailbox_importer **_importer, bool success, uint32_t *last_common_uid_r, uint64_t *last_common_modseq_r, uint64_t *last_common_pvt_modseq_r, uint32_t *last_messages_count_r, const char **changes_during_sync_r, bool *require_full_resync_r, enum mail_error *error_r) { struct dsync_mailbox_importer *importer = *_importer; struct mailbox_status status; int ret; *_importer = NULL; *changes_during_sync_r = NULL; *require_full_resync_r = importer->require_full_resync; if ((!success || importer->require_full_resync) && !importer->failed) { importer->mail_error = MAIL_ERROR_TEMP; importer->failed = TRUE; } if (!importer->new_uids_assigned && !importer->failed) dsync_mailbox_import_assign_new_uids(importer); if (!importer->failed) { dsync_mailbox_import_check_missing_guid_imports(importer); dsync_mailbox_import_check_missing_uid_imports(importer); } if (importer->search_ctx != NULL) { if (mailbox_search_deinit(&importer->search_ctx) < 0) { i_error("Mailbox %s: Search failed: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; } } if (dsync_mailbox_import_finish(importer, changes_during_sync_r) < 0) importer->failed = TRUE; if (importer->virtual_mail != NULL) mail_free(&importer->virtual_mail); if (importer->virtual_trans != NULL) (void)mailbox_transaction_commit(&importer->virtual_trans); hash_table_destroy(&importer->import_guids); hash_table_destroy(&importer->import_uids); array_free(&importer->maybe_expunge_uids); array_free(&importer->maybe_saves); array_free(&importer->wanted_uids); array_free(&importer->saved_uids); array_free(&importer->newmails); if (array_is_created(&importer->mail_requests)) array_free(&importer->mail_requests); *last_common_uid_r = importer->last_common_uid; if (*changes_during_sync_r == NULL) { *last_common_modseq_r = importer->remote_highest_modseq; *last_common_pvt_modseq_r = importer->remote_highest_pvt_modseq; } else { /* local changes occurred during dsync. we exported changes up to local_initial_highestmodseq, so all of the changes have happened after it. we want the next run to see those changes, so return it as the last common modseq */ *last_common_modseq_r = importer->local_initial_highestmodseq; *last_common_pvt_modseq_r = importer->local_initial_highestpvtmodseq; } if (importer->delete_mailbox) { if (mailbox_delete(importer->box) < 0) { i_error("Couldn't delete mailbox %s: %s", mailbox_get_vname(importer->box), mailbox_get_last_internal_error(importer->box, &importer->mail_error)); importer->failed = TRUE; } *last_messages_count_r = 0; } else { mailbox_get_open_status(importer->box, STATUS_MESSAGES, &status); *last_messages_count_r = status.messages; } i_assert(importer->failed == (importer->mail_error != 0)); ret = importer->failed ? -1 : 0; *error_r = importer->mail_error; pool_unref(&importer->pool); return ret; } const char *dsync_mailbox_import_get_proctitle(struct dsync_mailbox_importer *importer) { if (importer->search_ctx != NULL) return ""; return t_strdup_printf("%u/%u", importer->import_pos, importer->import_count); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-mail.h0000644000175000017500000000627213165463543016105 00000000000000#ifndef DSYNC_MAIL_H #define DSYNC_MAIL_H #include "mail-types.h" struct md5_context; struct mail; struct mailbox; struct dsync_mail { /* either GUID="" or uid=0 */ const char *guid; uint32_t uid; time_t saved_date; /* If non-NULL, we're syncing within the dsync process using ibc-pipe. This mail can be used to mailbox_copy() the mail. */ struct mail *input_mail; /* Verify that this equals to input_mail->uid */ uint32_t input_mail_uid; /* TRUE if the following fields aren't set, because minimal_fill=TRUE parameter was used. */ bool minimal_fields; const char *pop3_uidl; unsigned int pop3_order; time_t received_date; /* Input stream containing the message text, or NULL if all instances of the message were already expunged from this mailbox. */ struct istream *input; }; struct dsync_mail_request { /* either GUID=NULL or uid=0 */ const char *guid; uint32_t uid; }; enum dsync_mail_change_type { DSYNC_MAIL_CHANGE_TYPE_SAVE, DSYNC_MAIL_CHANGE_TYPE_EXPUNGE, DSYNC_MAIL_CHANGE_TYPE_FLAG_CHANGE }; #define KEYWORD_CHANGE_ADD '+' #define KEYWORD_CHANGE_REMOVE '-' #define KEYWORD_CHANGE_FINAL '=' #define KEYWORD_CHANGE_ADD_AND_FINAL '&' struct dsync_mail_change { enum dsync_mail_change_type type; uint32_t uid; /* Message's GUID: - for expunges either 128bit hex or NULL if unknown - "" if backend doesn't support GUIDs */ const char *guid; /* If GUID is "", this contains hash of the message header, otherwise NULL */ const char *hdr_hash; /* Message's current modseq (saves, flag changes) */ uint64_t modseq; /* Message's current private modseq (for private flags in shared mailboxes, otherwise 0) */ uint64_t pvt_modseq; /* List of flag/keyword changes: (saves, flag changes) */ /* Flags added/removed since last sync, and final flags containing flags that exist now but haven't changed */ uint8_t add_flags, remove_flags, final_flags; uint8_t add_pvt_flags, remove_pvt_flags; /* Remove all keywords before applying changes. This is used only with old transaction logs, new ones never reset keywords (just explicitly remove unwanted keywords) */ bool keywords_reset; /* +add, -remove, =final, &add_and_final. */ ARRAY_TYPE(const_string) keyword_changes; /* Received timestamp for saves, if brain.sync_since/until_timestamp is set */ time_t received_timestamp; /* Mail's size for saves if brain.sync_max_size is set, (uoff_t)-1 otherwise. */ uoff_t virtual_size; }; struct mailbox_header_lookup_ctx * dsync_mail_get_hash_headers(struct mailbox *box, const char *const *hashed_headers); int dsync_mail_get_hdr_hash(struct mail *mail, unsigned int version, const char *const *hashed_headers, const char **hdr_hash_r); static inline bool dsync_mail_hdr_hash_is_empty(const char *hdr_hash) { /* md5(\n) */ return strcmp(hdr_hash, "68b329da9893e34099c7d8ad5cb9c940") == 0; } int dsync_mail_fill(struct mail *mail, bool minimal_fill, struct dsync_mail *dmail_r, const char **error_field_r); int dsync_mail_fill_nonminimal(struct mail *mail, struct dsync_mail *dmail_r, const char **error_field_r); void dsync_mail_change_dup(pool_t pool, const struct dsync_mail_change *src, struct dsync_mail_change *dest_r); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-brain.c0000644000175000017500000006107113165463624016247 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "hostpid.h" #include "str.h" #include "file-create-locked.h" #include "process-title.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-namespace.h" #include "dsync-mailbox-tree.h" #include "dsync-ibc.h" #include "dsync-brain-private.h" #include "dsync-mailbox-import.h" #include "dsync-mailbox-export.h" #include enum dsync_brain_title { DSYNC_BRAIN_TITLE_NONE = 0, DSYNC_BRAIN_TITLE_LOCKING, }; static const char *dsync_state_names[] = { "master_recv_handshake", "slave_recv_handshake", "master_send_last_common", "slave_recv_last_common", "send_mailbox_tree", "send_mailbox_tree_deletes", "recv_mailbox_tree", "recv_mailbox_tree_deletes", "master_send_mailbox", "slave_recv_mailbox", "sync_mails", "finish", "done" }; static void dsync_brain_mailbox_states_dump(struct dsync_brain *brain); static const char * dsync_brain_get_proctitle_full(struct dsync_brain *brain, enum dsync_brain_title title) { string_t *str = t_str_new(128); const char *import_title, *export_title; str_append_c(str, '['); if (brain->process_title_prefix != NULL) str_append(str, brain->process_title_prefix); str_append(str, brain->user->username); if (brain->box == NULL) { str_append_c(str, ' '); str_append(str, dsync_state_names[brain->state]); } else { str_append_c(str, ' '); str_append(str, mailbox_get_vname(brain->box)); import_title = brain->box_importer == NULL ? "" : dsync_mailbox_import_get_proctitle(brain->box_importer); export_title = brain->box_exporter == NULL ? "" : dsync_mailbox_export_get_proctitle(brain->box_exporter); if (import_title[0] == '\0' && export_title[0] == '\0') { str_printfa(str, " send:%s recv:%s", dsync_box_state_names[brain->box_send_state], dsync_box_state_names[brain->box_recv_state]); } else { if (import_title[0] != '\0') { str_append(str, " import:"); str_append(str, import_title); } if (export_title[0] != '\0') { str_append(str, " export:"); str_append(str, export_title); } } } switch (title) { case DSYNC_BRAIN_TITLE_NONE: break; case DSYNC_BRAIN_TITLE_LOCKING: str_append(str, " locking "DSYNC_LOCK_FILENAME); break; } str_append_c(str, ']'); return str_c(str); } static const char *dsync_brain_get_proctitle(struct dsync_brain *brain) { return dsync_brain_get_proctitle_full(brain, DSYNC_BRAIN_TITLE_NONE); } static void dsync_brain_run_io(void *context) { struct dsync_brain *brain = context; bool changed, try_pending; if (dsync_ibc_has_failed(brain->ibc)) { io_loop_stop(current_ioloop); brain->failed = TRUE; return; } try_pending = TRUE; do { if (!dsync_brain_run(brain, &changed)) { io_loop_stop(current_ioloop); break; } if (changed) try_pending = TRUE; else if (try_pending) { if (dsync_ibc_has_pending_data(brain->ibc)) changed = TRUE; try_pending = FALSE; } } while (changed); } static struct dsync_brain * dsync_brain_common_init(struct mail_user *user, struct dsync_ibc *ibc) { struct dsync_brain *brain; const struct master_service_settings *service_set; pool_t pool; service_set = master_service_settings_get(master_service); mail_user_ref(user); pool = pool_alloconly_create("dsync brain", 10240); brain = p_new(pool, struct dsync_brain, 1); brain->pool = pool; brain->user = user; brain->ibc = ibc; brain->sync_type = DSYNC_BRAIN_SYNC_TYPE_UNKNOWN; brain->lock_fd = -1; brain->verbose_proctitle = service_set->verbose_proctitle; hash_table_create(&brain->mailbox_states, pool, 0, guid_128_hash, guid_128_cmp); p_array_init(&brain->remote_mailbox_states, pool, 64); return brain; } static void dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags) { brain->mail_requests = (flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0; brain->backup_send = (flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0; brain->backup_recv = (flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0; brain->debug = (flags & DSYNC_BRAIN_FLAG_DEBUG) != 0; brain->sync_visible_namespaces = (flags & DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES) != 0; brain->no_mail_sync = (flags & DSYNC_BRAIN_FLAG_NO_MAIL_SYNC) != 0; brain->no_backup_overwrite = (flags & DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE) != 0; brain->no_mail_prefetch = (flags & DSYNC_BRAIN_FLAG_NO_MAIL_PREFETCH) != 0; brain->no_mailbox_renames = (flags & DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES) != 0; brain->no_notify = (flags & DSYNC_BRAIN_FLAG_NO_NOTIFY) != 0; brain->empty_hdr_workaround = (flags & DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND) != 0; } static void dsync_brain_open_virtual_all_box(struct dsync_brain *brain, const char *vname) { struct mail_namespace *ns; ns = mail_namespace_find(brain->user->namespaces, vname); brain->virtual_all_box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY); } struct dsync_brain * dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc, enum dsync_brain_sync_type sync_type, enum dsync_brain_flags flags, const struct dsync_brain_settings *set) { struct dsync_ibc_settings ibc_set; struct dsync_brain *brain; struct mail_namespace *const *nsp; string_t *sync_ns_str = NULL; const char *error; i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_UNKNOWN); i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE || (set->state != NULL && *set->state != '\0')); i_assert(N_ELEMENTS(dsync_state_names) == DSYNC_STATE_DONE+1); brain = dsync_brain_common_init(user, ibc); brain->process_title_prefix = p_strdup(brain->pool, set->process_title_prefix); brain->sync_type = sync_type; if (array_count(&set->sync_namespaces) > 0) { sync_ns_str = t_str_new(128); p_array_init(&brain->sync_namespaces, brain->pool, array_count(&set->sync_namespaces)); array_foreach(&set->sync_namespaces, nsp) { str_append(sync_ns_str, (*nsp)->prefix); str_append_c(sync_ns_str, '\n'); array_append(&brain->sync_namespaces, nsp, 1); } str_delete(sync_ns_str, str_len(sync_ns_str)-1, 1); } brain->alt_char = set->mailbox_alt_char == '\0' ? '_' : set->mailbox_alt_char; brain->sync_since_timestamp = set->sync_since_timestamp; brain->sync_until_timestamp = set->sync_until_timestamp; brain->sync_max_size = set->sync_max_size; brain->sync_flag = p_strdup(brain->pool, set->sync_flag); brain->sync_box = p_strdup(brain->pool, set->sync_box); brain->exclude_mailboxes = set->exclude_mailboxes == NULL ? NULL : p_strarray_dup(brain->pool, set->exclude_mailboxes); memcpy(brain->sync_box_guid, set->sync_box_guid, sizeof(brain->sync_box_guid)); brain->lock_timeout = set->lock_timeout_secs; brain->import_commit_msgs_interval = set->import_commit_msgs_interval; brain->master_brain = TRUE; brain->hashed_headers = (const char*const*)p_strarray_dup(brain->pool, set->hashed_headers); dsync_brain_set_flags(brain, flags); if (set->virtual_all_box != NULL) dsync_brain_open_virtual_all_box(brain, set->virtual_all_box); if (sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE) ; else if (dsync_mailbox_states_import(brain->mailbox_states, brain->pool, set->state, &error) < 0) { hash_table_clear(brain->mailbox_states, FALSE); i_error("Saved sync state is invalid, " "falling back to full sync: %s", error); brain->sync_type = sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL; } else { if (brain->debug) { i_debug("brain %c: Imported mailbox states:", brain->master_brain ? 'M' : 'S'); dsync_brain_mailbox_states_dump(brain); } } dsync_brain_mailbox_trees_init(brain); i_zero(&ibc_set); ibc_set.hostname = my_hostdomain(); ibc_set.sync_ns_prefixes = sync_ns_str == NULL ? NULL : str_c(sync_ns_str); ibc_set.sync_box = set->sync_box; ibc_set.virtual_all_box = set->virtual_all_box; ibc_set.exclude_mailboxes = set->exclude_mailboxes; ibc_set.sync_since_timestamp = set->sync_since_timestamp; ibc_set.sync_until_timestamp = set->sync_until_timestamp; ibc_set.sync_max_size = set->sync_max_size; ibc_set.sync_flags = set->sync_flag; memcpy(ibc_set.sync_box_guid, set->sync_box_guid, sizeof(ibc_set.sync_box_guid)); ibc_set.sync_type = sync_type; ibc_set.hdr_hash_v2 = TRUE; ibc_set.lock_timeout = set->lock_timeout_secs; ibc_set.import_commit_msgs_interval = set->import_commit_msgs_interval; ibc_set.hashed_headers = set->hashed_headers; /* reverse the backup direction for the slave */ ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND | DSYNC_BRAIN_FLAG_BACKUP_RECV); if ((flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0) ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV; else if ((flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0) ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND; dsync_ibc_send_handshake(ibc, &ibc_set); dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain); brain->state = DSYNC_STATE_MASTER_RECV_HANDSHAKE; if (brain->verbose_proctitle) process_title_set(dsync_brain_get_proctitle(brain)); return brain; } struct dsync_brain * dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc, bool local, const char *process_title_prefix) { struct dsync_ibc_settings ibc_set; struct dsync_brain *brain; brain = dsync_brain_common_init(user, ibc); brain->process_title_prefix = p_strdup(brain->pool, process_title_prefix); brain->state = DSYNC_STATE_SLAVE_RECV_HANDSHAKE; if (local) { /* both master and slave are running within the same process, update the proctitle only for master. */ brain->verbose_proctitle = FALSE; } i_zero(&ibc_set); ibc_set.hdr_hash_v2 = TRUE; ibc_set.hostname = my_hostdomain(); dsync_ibc_send_handshake(ibc, &ibc_set); if (brain->verbose_proctitle) process_title_set(dsync_brain_get_proctitle(brain)); dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain); return brain; } static void dsync_brain_purge(struct dsync_brain *brain) { struct mail_namespace *ns; struct mail_storage *storage; for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) { if (!dsync_brain_want_namespace(brain, ns)) continue; storage = mail_namespace_get_default_storage(ns); if (mail_storage_purge(storage) < 0) { i_error("Purging namespace '%s' failed: %s", ns->prefix, mail_storage_get_last_internal_error(storage, NULL)); } } } int dsync_brain_deinit(struct dsync_brain **_brain, enum mail_error *error_r) { struct dsync_brain *brain = *_brain; int ret; *_brain = NULL; if (dsync_ibc_has_timed_out(brain->ibc)) { i_error("Timeout during state=%s%s", dsync_state_names[brain->state], brain->state != DSYNC_STATE_SYNC_MAILS ? "" : t_strdup_printf(" (send=%s recv=%s)", dsync_box_state_names[brain->box_send_state], dsync_box_state_names[brain->box_recv_state])); } if (dsync_ibc_has_failed(brain->ibc) || brain->state != DSYNC_STATE_DONE) brain->failed = TRUE; dsync_ibc_close_mail_streams(brain->ibc); if (brain->purge && !brain->failed) dsync_brain_purge(brain); if (brain->box != NULL) dsync_brain_sync_mailbox_deinit(brain); if (brain->virtual_all_box != NULL) mailbox_free(&brain->virtual_all_box); if (brain->local_tree_iter != NULL) dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter); if (brain->local_mailbox_tree != NULL) dsync_mailbox_tree_deinit(&brain->local_mailbox_tree); if (brain->remote_mailbox_tree != NULL) dsync_mailbox_tree_deinit(&brain->remote_mailbox_tree); if (brain->mailbox_states_iter != NULL) hash_table_iterate_deinit(&brain->mailbox_states_iter); hash_table_destroy(&brain->mailbox_states); if (brain->dsync_box_pool != NULL) pool_unref(&brain->dsync_box_pool); if (brain->lock_fd != -1) { /* unlink the lock file before it gets unlocked */ i_unlink(brain->lock_path); file_lock_free(&brain->lock); i_close_fd(&brain->lock_fd); } ret = brain->failed ? -1 : 0; mail_user_unref(&brain->user); *error_r = !brain->failed ? 0 : (brain->mail_error == 0 ? MAIL_ERROR_TEMP : brain->mail_error); pool_unref(&brain->pool); return ret; } static int dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname) { const struct file_create_settings lock_set = { .lock_timeout_secs = brain->lock_timeout, .lock_method = FILE_LOCK_METHOD_FCNTL, }; const char *home, *error; bool created; int ret; if ((ret = strcmp(remote_hostname, my_hostdomain())) < 0) { /* locking done by remote */ return 0; } if (ret == 0 && !brain->master_brain) { /* running dsync within the same server. locking done by master brain. */ return 0; } if ((ret = mail_user_get_home(brain->user, &home)) < 0) { i_error("Couldn't look up user's home dir"); return -1; } if (ret == 0) { i_error("User has no home directory"); return -1; } if (brain->verbose_proctitle) process_title_set(dsync_brain_get_proctitle_full(brain, DSYNC_BRAIN_TITLE_LOCKING)); brain->lock_path = p_strconcat(brain->pool, home, "/"DSYNC_LOCK_FILENAME, NULL); brain->lock_fd = file_create_locked(brain->lock_path, &lock_set, &brain->lock, &created, &error); if (brain->lock_fd == -1) i_error("Couldn't lock %s: %s", brain->lock_path, error); if (brain->verbose_proctitle) process_title_set(dsync_brain_get_proctitle(brain)); return brain->lock_fd == -1 ? -1 : 0; } static void dsync_brain_set_hdr_hash_version(struct dsync_brain *brain, const struct dsync_ibc_settings *ibc_set) { if (ibc_set->hdr_hash_v3) brain->hdr_hash_version = 3; else if (ibc_set->hdr_hash_v2) brain->hdr_hash_version = 3; else brain->hdr_hash_version = 1; } static bool dsync_brain_master_recv_handshake(struct dsync_brain *brain) { const struct dsync_ibc_settings *ibc_set; i_assert(brain->master_brain); if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0) return FALSE; if (brain->lock_timeout > 0) { if (dsync_brain_lock(brain, ibc_set->hostname) < 0) { brain->failed = TRUE; return FALSE; } } dsync_brain_set_hdr_hash_version(brain, ibc_set); brain->state = brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE ? DSYNC_STATE_MASTER_SEND_LAST_COMMON : DSYNC_STATE_SEND_MAILBOX_TREE; return TRUE; } static bool dsync_brain_slave_recv_handshake(struct dsync_brain *brain) { const struct dsync_ibc_settings *ibc_set; struct mail_namespace *ns; const char *const *prefixes; i_assert(!brain->master_brain); if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0) return FALSE; dsync_brain_set_hdr_hash_version(brain, ibc_set); if (ibc_set->lock_timeout > 0) { brain->lock_timeout = ibc_set->lock_timeout; if (dsync_brain_lock(brain, ibc_set->hostname) < 0) { brain->failed = TRUE; return FALSE; } } if (ibc_set->sync_ns_prefixes != NULL) { p_array_init(&brain->sync_namespaces, brain->pool, 4); prefixes = t_strsplit(ibc_set->sync_ns_prefixes, "\n"); if (prefixes[0] == NULL) { /* ugly workaround for strsplit API: there was one prefix="" entry */ static const char *empty_prefix[] = { "", NULL }; prefixes = empty_prefix; } for (; *prefixes != NULL; prefixes++) { ns = mail_namespace_find(brain->user->namespaces, *prefixes); if (ns == NULL) { i_error("Namespace not found: '%s'", *prefixes); brain->failed = TRUE; return FALSE; } array_append(&brain->sync_namespaces, &ns, 1); } } brain->sync_box = p_strdup(brain->pool, ibc_set->sync_box); brain->exclude_mailboxes = ibc_set->exclude_mailboxes == NULL ? NULL : p_strarray_dup(brain->pool, ibc_set->exclude_mailboxes); brain->sync_since_timestamp = ibc_set->sync_since_timestamp; brain->sync_until_timestamp = ibc_set->sync_until_timestamp; brain->sync_max_size = ibc_set->sync_max_size; brain->sync_flag = p_strdup(brain->pool, ibc_set->sync_flags); memcpy(brain->sync_box_guid, ibc_set->sync_box_guid, sizeof(brain->sync_box_guid)); i_assert(brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_UNKNOWN); brain->sync_type = ibc_set->sync_type; dsync_brain_set_flags(brain, ibc_set->brain_flags); if (ibc_set->hashed_headers != NULL) brain->hashed_headers = p_strarray_dup(brain->pool, (const char*const*)ibc_set->hashed_headers); /* this flag is only set on the remote slave brain */ brain->purge = (ibc_set->brain_flags & DSYNC_BRAIN_FLAG_PURGE_REMOTE) != 0; if (ibc_set->virtual_all_box != NULL) dsync_brain_open_virtual_all_box(brain, ibc_set->virtual_all_box); dsync_brain_mailbox_trees_init(brain); if (brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE) brain->state = DSYNC_STATE_SLAVE_RECV_LAST_COMMON; else brain->state = DSYNC_STATE_SEND_MAILBOX_TREE; return TRUE; } static void dsync_brain_master_send_last_common(struct dsync_brain *brain) { struct dsync_mailbox_state *state; uint8_t *guid; enum dsync_ibc_send_ret ret = DSYNC_IBC_SEND_RET_OK; i_assert(brain->master_brain); if (brain->mailbox_states_iter == NULL) { brain->mailbox_states_iter = hash_table_iterate_init(brain->mailbox_states); } for (;;) { if (ret == DSYNC_IBC_SEND_RET_FULL) return; if (!hash_table_iterate(brain->mailbox_states_iter, brain->mailbox_states, &guid, &state)) break; ret = dsync_ibc_send_mailbox_state(brain->ibc, state); } hash_table_iterate_deinit(&brain->mailbox_states_iter); dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_STATE); brain->state = DSYNC_STATE_SEND_MAILBOX_TREE; } static void dsync_mailbox_state_add(struct dsync_brain *brain, const struct dsync_mailbox_state *state) { struct dsync_mailbox_state *dupstate; uint8_t *guid_p; dupstate = p_new(brain->pool, struct dsync_mailbox_state, 1); *dupstate = *state; guid_p = dupstate->mailbox_guid; hash_table_insert(brain->mailbox_states, guid_p, dupstate); } static bool dsync_brain_slave_recv_last_common(struct dsync_brain *brain) { struct dsync_mailbox_state state; enum dsync_ibc_recv_ret ret; bool changed = FALSE; i_assert(!brain->master_brain); while ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) > 0) { dsync_mailbox_state_add(brain, &state); changed = TRUE; } if (ret == DSYNC_IBC_RECV_RET_FINISHED) { brain->state = DSYNC_STATE_SEND_MAILBOX_TREE; changed = TRUE; } return changed; } static bool dsync_brain_finish(struct dsync_brain *brain) { const char *error; enum mail_error mail_error; bool require_full_resync; enum dsync_ibc_recv_ret ret; if (!brain->master_brain) { dsync_ibc_send_finish(brain->ibc, brain->failed ? "dsync failed" : NULL, brain->mail_error, brain->require_full_resync); brain->state = DSYNC_STATE_DONE; return TRUE; } ret = dsync_ibc_recv_finish(brain->ibc, &error, &mail_error, &require_full_resync); if (ret == DSYNC_IBC_RECV_RET_TRYAGAIN) return FALSE; if (error != NULL) { i_error("Remote dsync failed: %s", error); brain->failed = TRUE; if (mail_error != 0 && (brain->mail_error == 0 || brain->mail_error == MAIL_ERROR_TEMP)) brain->mail_error = mail_error; } if (require_full_resync) brain->require_full_resync = TRUE; brain->state = DSYNC_STATE_DONE; return TRUE; } static bool dsync_brain_run_real(struct dsync_brain *brain, bool *changed_r) { enum dsync_state orig_state = brain->state; enum dsync_box_state orig_box_recv_state = brain->box_recv_state; enum dsync_box_state orig_box_send_state = brain->box_send_state; bool changed = FALSE, ret = TRUE; if (brain->failed) return FALSE; switch (brain->state) { case DSYNC_STATE_MASTER_RECV_HANDSHAKE: changed = dsync_brain_master_recv_handshake(brain); break; case DSYNC_STATE_SLAVE_RECV_HANDSHAKE: changed = dsync_brain_slave_recv_handshake(brain); break; case DSYNC_STATE_MASTER_SEND_LAST_COMMON: dsync_brain_master_send_last_common(brain); changed = TRUE; break; case DSYNC_STATE_SLAVE_RECV_LAST_COMMON: changed = dsync_brain_slave_recv_last_common(brain); break; case DSYNC_STATE_SEND_MAILBOX_TREE: dsync_brain_send_mailbox_tree(brain); changed = TRUE; break; case DSYNC_STATE_RECV_MAILBOX_TREE: changed = dsync_brain_recv_mailbox_tree(brain); break; case DSYNC_STATE_SEND_MAILBOX_TREE_DELETES: dsync_brain_send_mailbox_tree_deletes(brain); changed = TRUE; break; case DSYNC_STATE_RECV_MAILBOX_TREE_DELETES: changed = dsync_brain_recv_mailbox_tree_deletes(brain); break; case DSYNC_STATE_MASTER_SEND_MAILBOX: dsync_brain_master_send_mailbox(brain); changed = TRUE; break; case DSYNC_STATE_SLAVE_RECV_MAILBOX: changed = dsync_brain_slave_recv_mailbox(brain); break; case DSYNC_STATE_SYNC_MAILS: changed = dsync_brain_sync_mails(brain); break; case DSYNC_STATE_FINISH: changed = dsync_brain_finish(brain); break; case DSYNC_STATE_DONE: changed = TRUE; ret = FALSE; break; } if (brain->verbose_proctitle) { if (orig_state != brain->state || orig_box_recv_state != brain->box_recv_state || orig_box_send_state != brain->box_send_state || ++brain->proctitle_update_counter % 100 == 0) process_title_set(dsync_brain_get_proctitle(brain)); } *changed_r = changed; return brain->failed ? FALSE : ret; } bool dsync_brain_run(struct dsync_brain *brain, bool *changed_r) { bool ret; *changed_r = FALSE; if (dsync_ibc_has_failed(brain->ibc)) { brain->failed = TRUE; return FALSE; } T_BEGIN { ret = dsync_brain_run_real(brain, changed_r); } T_END; return ret; } static void dsync_brain_mailbox_states_dump(struct dsync_brain *brain) { struct hash_iterate_context *iter; struct dsync_mailbox_state *state; uint8_t *guid; iter = hash_table_iterate_init(brain->mailbox_states); while (hash_table_iterate(iter, brain->mailbox_states, &guid, &state)) { i_debug("brain %c: Mailbox %s state: uidvalidity=%u uid=%u modseq=%llu pvt_modseq=%llu messages=%u changes_during_sync=%d", brain->master_brain ? 'M' : 'S', guid_128_to_string(guid), state->last_uidvalidity, state->last_common_uid, (unsigned long long)state->last_common_modseq, (unsigned long long)state->last_common_pvt_modseq, state->last_messages_count, state->changes_during_sync); } hash_table_iterate_deinit(&iter); } void dsync_brain_get_state(struct dsync_brain *brain, string_t *output) { struct hash_iterate_context *iter; struct dsync_mailbox_node *node; const struct dsync_mailbox_state *new_state; struct dsync_mailbox_state *state; const uint8_t *guid_p; uint8_t *guid; if (brain->require_full_resync) return; /* update mailbox states */ array_foreach(&brain->remote_mailbox_states, new_state) { guid_p = new_state->mailbox_guid; state = hash_table_lookup(brain->mailbox_states, guid_p); if (state != NULL) *state = *new_state; else dsync_mailbox_state_add(brain, new_state); } /* remove nonexistent mailboxes */ iter = hash_table_iterate_init(brain->mailbox_states); while (hash_table_iterate(iter, brain->mailbox_states, &guid, &state)) { node = dsync_mailbox_tree_lookup_guid(brain->local_mailbox_tree, guid); if (node == NULL || node->existence != DSYNC_MAILBOX_NODE_EXISTS) { if (brain->debug) { i_debug("brain %c: Removed state for deleted mailbox %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(guid)); } hash_table_remove(brain->mailbox_states, guid); } } hash_table_iterate_deinit(&iter); if (brain->debug) { i_debug("brain %c: Exported mailbox states:", brain->master_brain ? 'M' : 'S'); dsync_brain_mailbox_states_dump(brain); } dsync_mailbox_states_export(brain->mailbox_states, output); } enum dsync_brain_sync_type dsync_brain_get_sync_type(struct dsync_brain *brain) { return brain->sync_type; } bool dsync_brain_has_failed(struct dsync_brain *brain) { return brain->failed; } const char *dsync_brain_get_unexpected_changes_reason(struct dsync_brain *brain, bool *remote_only_r) { if (brain->changes_during_sync == NULL && brain->changes_during_remote_sync) { *remote_only_r = TRUE; return "Remote notified that changes happened during sync"; } *remote_only_r = FALSE; return brain->changes_during_sync; } bool dsync_brain_want_namespace(struct dsync_brain *brain, struct mail_namespace *ns) { struct mail_namespace *const *nsp; if (array_is_created(&brain->sync_namespaces)) { array_foreach(&brain->sync_namespaces, nsp) { if (ns == *nsp) return TRUE; } return FALSE; } if (ns->alias_for != NULL) { /* always skip aliases */ return FALSE; } if (brain->sync_visible_namespaces) { if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0) return TRUE; if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_LIST_CHILDREN)) != 0) return TRUE; return FALSE; } else { return strcmp(ns->unexpanded_set->location, SETTING_STRVAR_UNEXPANDED) == 0; } } void dsync_brain_set_changes_during_sync(struct dsync_brain *brain, const char *reason) { if (brain->debug) { i_debug("brain %c: Change during sync: %s", brain->master_brain ? 'M' : 'S', reason); } if (brain->changes_during_sync == NULL) brain->changes_during_sync = p_strdup(brain->pool, reason); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-serializer.c0000644000175000017500000000552013123174404017307 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "dsync-serializer.h" struct dsync_serializer { pool_t pool; const char *const *keys; unsigned int keys_count; }; struct dsync_serializer_encoder { pool_t pool; struct dsync_serializer *serializer; ARRAY_TYPE(const_string) values; }; struct dsync_serializer *dsync_serializer_init(const char *const keys[]) { struct dsync_serializer *serializer; pool_t pool; const char **dup_keys; unsigned int i, count; pool = pool_alloconly_create("dsync serializer", 512); serializer = p_new(pool, struct dsync_serializer, 1); serializer->pool = pool; count = str_array_length(keys); dup_keys = p_new(pool, const char *, count + 1); for (i = 0; i < count; i++) dup_keys[i] = p_strdup(pool, keys[i]); serializer->keys = dup_keys; serializer->keys_count = count; return serializer; } void dsync_serializer_deinit(struct dsync_serializer **_serializer) { struct dsync_serializer *serializer = *_serializer; *_serializer = NULL; pool_unref(&serializer->pool); } const char * dsync_serializer_encode_header_line(struct dsync_serializer *serializer) { string_t *str = t_str_new(128); unsigned int i; for (i = 0; serializer->keys[i] != NULL; i++) { if (i > 0) str_append_c(str, '\t'); str_append_tabescaped(str, serializer->keys[i]); } str_append_c(str, '\n'); return str_c(str); } struct dsync_serializer_encoder * dsync_serializer_encode_begin(struct dsync_serializer *serializer) { struct dsync_serializer_encoder *encoder; pool_t pool = pool_alloconly_create("dsync serializer encode", 1024); encoder = p_new(pool, struct dsync_serializer_encoder, 1); encoder->pool = pool; encoder->serializer = serializer; p_array_init(&encoder->values, pool, serializer->keys_count); return encoder; } void dsync_serializer_encode_add(struct dsync_serializer_encoder *encoder, const char *key, const char *value) { unsigned int i; for (i = 0; encoder->serializer->keys[i] != NULL; i++) { if (strcmp(encoder->serializer->keys[i], key) == 0) { value = p_strdup(encoder->pool, value); array_idx_set(&encoder->values, i, &value); return; } } i_panic("Unknown key: %s", key); } void dsync_serializer_encode_finish(struct dsync_serializer_encoder **_encoder, string_t *output) { struct dsync_serializer_encoder *encoder = *_encoder; const char *const *values; unsigned int i, count; *_encoder = NULL; values = array_get(&encoder->values, &count); for (i = 0; i < count; i++) { if (i > 0) str_append_c(output, '\t'); if (values[i] == NULL) str_append_c(output, NULL_CHR); else { if (values[i][0] == NULL_CHR) str_append_c(output, NULL_CHR); str_append_tabescaped(output, values[i]); } } str_append_c(output, '\n'); pool_unref(&encoder->pool); } dovecot-2.2.33.2/src/doveadm/dsync/dsync-ibc-pipe.c0000644000175000017500000003767013165463624016654 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "dsync-mail.h" #include "dsync-mailbox.h" #include "dsync-mailbox-state.h" #include "dsync-mailbox-tree.h" #include "dsync-ibc-private.h" enum item_type { ITEM_END_OF_LIST, ITEM_HANDSHAKE, ITEM_MAILBOX_STATE, ITEM_MAILBOX_TREE_NODE, ITEM_MAILBOX_DELETE, ITEM_MAILBOX, ITEM_MAILBOX_ATTRIBUTE, ITEM_MAIL_CHANGE, ITEM_MAIL_REQUEST, ITEM_MAIL, ITEM_FINISH }; struct item { enum item_type type; pool_t pool; union { struct dsync_ibc_settings set; struct dsync_mailbox_state state; struct dsync_mailbox_node node; guid_128_t mailbox_guid; struct dsync_mailbox dsync_box; struct dsync_mailbox_attribute attr; struct dsync_mail_change change; struct dsync_mail_request request; struct dsync_mail mail; struct { const struct dsync_mailbox_delete *deletes; unsigned int count; char hierarchy_sep; } mailbox_delete; struct { const char *error; enum mail_error mail_error; bool require_full_resync; } finish; } u; }; struct dsync_ibc_pipe { struct dsync_ibc ibc; ARRAY(pool_t) pools; ARRAY(struct item) item_queue; struct dsync_ibc_pipe *remote; pool_t pop_pool; struct item pop_item; }; static pool_t dsync_ibc_pipe_get_pool(struct dsync_ibc_pipe *pipe) { pool_t *pools, ret; unsigned int count; pools = array_get_modifiable(&pipe->pools, &count); if (count == 0) return pool_alloconly_create(MEMPOOL_GROWING"pipe item pool", 1024); ret = pools[count-1]; array_delete(&pipe->pools, count-1, 1); p_clear(ret); return ret; } static struct item * ATTR_NOWARN_UNUSED_RESULT dsync_ibc_pipe_push_item(struct dsync_ibc_pipe *pipe, enum item_type type) { struct item *item; item = array_append_space(&pipe->item_queue); item->type = type; switch (type) { case ITEM_END_OF_LIST: case ITEM_MAILBOX_STATE: case ITEM_MAILBOX_DELETE: break; case ITEM_HANDSHAKE: case ITEM_MAILBOX: case ITEM_MAILBOX_TREE_NODE: case ITEM_MAILBOX_ATTRIBUTE: case ITEM_MAIL_CHANGE: case ITEM_MAIL_REQUEST: case ITEM_MAIL: case ITEM_FINISH: item->pool = dsync_ibc_pipe_get_pool(pipe); break; } return item; } static struct item * dsync_ibc_pipe_pop_item(struct dsync_ibc_pipe *pipe, enum item_type type) { struct item *item; if (array_count(&pipe->item_queue) == 0) return NULL; item = array_idx_modifiable(&pipe->item_queue, 0); i_assert(item->type == type); pipe->pop_item = *item; array_delete(&pipe->item_queue, 0, 1); item = NULL; if (pipe->pop_pool != NULL) pool_unref(&pipe->pop_pool); pipe->pop_pool = pipe->pop_item.pool; return &pipe->pop_item; } static bool dsync_ibc_pipe_try_pop_eol(struct dsync_ibc_pipe *pipe) { const struct item *item; if (array_count(&pipe->item_queue) == 0) return FALSE; item = array_idx(&pipe->item_queue, 0); if (item->type != ITEM_END_OF_LIST) return FALSE; array_delete(&pipe->item_queue, 0, 1); return TRUE; } static void dsync_ibc_pipe_deinit(struct dsync_ibc *ibc) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; pool_t *poolp; if (pipe->remote != NULL) { i_assert(pipe->remote->remote == pipe); pipe->remote->remote = NULL; } if (pipe->pop_pool != NULL) pool_unref(&pipe->pop_pool); array_foreach_modifiable(&pipe->item_queue, item) { if (item->pool != NULL) pool_unref(&item->pool); } array_foreach_modifiable(&pipe->pools, poolp) pool_unref(poolp); array_free(&pipe->pools); array_free(&pipe->item_queue); i_free(pipe); } static void dsync_ibc_pipe_send_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings *set) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_HANDSHAKE); item->u.set = *set; item->u.set.sync_ns_prefixes = p_strdup(item->pool, set->sync_ns_prefixes); item->u.set.sync_box = p_strdup(item->pool, set->sync_box); item->u.set.virtual_all_box = p_strdup(item->pool, set->virtual_all_box); item->u.set.exclude_mailboxes = set->exclude_mailboxes == NULL ? NULL : p_strarray_dup(item->pool, set->exclude_mailboxes); memcpy(item->u.set.sync_box_guid, set->sync_box_guid, sizeof(item->u.set.sync_box_guid)); item->u.set.sync_since_timestamp = set->sync_since_timestamp; item->u.set.sync_until_timestamp = set->sync_until_timestamp; item->u.set.sync_max_size = set->sync_max_size; item->u.set.sync_flags = p_strdup(item->pool, set->sync_flags); } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings **set_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_pop_item(pipe, ITEM_HANDSHAKE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *set_r = &item->u.set; return DSYNC_IBC_RECV_RET_OK; } static bool dsync_ibc_pipe_is_send_queue_full(struct dsync_ibc *ibc) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; return array_count(&pipe->remote->item_queue) > 0; } static bool dsync_ibc_pipe_has_pending_data(struct dsync_ibc *ibc) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; return array_count(&pipe->item_queue) > 0; } static void dsync_ibc_pipe_send_end_of_list(struct dsync_ibc *ibc, enum dsync_ibc_eol_type type ATTR_UNUSED) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; dsync_ibc_pipe_push_item(pipe->remote, ITEM_END_OF_LIST); } static void dsync_ibc_pipe_send_mailbox_state(struct dsync_ibc *ibc, const struct dsync_mailbox_state *state) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAILBOX_STATE); item->u.state = *state; } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mailbox_state(struct dsync_ibc *ibc, struct dsync_mailbox_state *state_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAILBOX_STATE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *state_r = item->u.state; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mailbox_tree_node(struct dsync_ibc *ibc, const char *const *name, const struct dsync_mailbox_node *node) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAILBOX_TREE_NODE); /* a little bit kludgy way to send it */ item->u.node.name = (void *)p_strarray_dup(item->pool, name); dsync_mailbox_node_copy_data(&item->u.node, node); } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mailbox_tree_node(struct dsync_ibc *ibc, const char *const **name_r, const struct dsync_mailbox_node **node_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAILBOX_TREE_NODE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *name_r = (void *)item->u.node.name; item->u.node.name = NULL; *node_r = &item->u.node; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete *deletes, unsigned int count, char hierarchy_sep) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAILBOX_DELETE); /* we'll assume that the deletes are permanent. this works for now.. */ /* a little bit kludgy way to send it */ item->u.mailbox_delete.deletes = deletes; item->u.mailbox_delete.count = count; item->u.mailbox_delete.hierarchy_sep = hierarchy_sep; } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete **deletes_r, unsigned int *count_r, char *hierarchy_sep_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAILBOX_DELETE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *deletes_r = item->u.mailbox_delete.deletes; *count_r = item->u.mailbox_delete.count; *hierarchy_sep_r = item->u.mailbox_delete.hierarchy_sep; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox *dsync_box) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; const struct mailbox_cache_field *cf; struct mailbox_cache_field *ncf; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAILBOX); item->u.dsync_box = *dsync_box; p_array_init(&item->u.dsync_box.cache_fields, item->pool, array_count(&dsync_box->cache_fields)); array_foreach(&dsync_box->cache_fields, cf) { ncf = array_append_space(&item->u.dsync_box.cache_fields); ncf->name = p_strdup(item->pool, cf->name); ncf->decision = cf->decision; ncf->last_used = cf->last_used; } } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox **dsync_box_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAILBOX); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *dsync_box_r = &item->u.dsync_box; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute *attr) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAILBOX_ATTRIBUTE); dsync_mailbox_attribute_dup(item->pool, attr, &item->u.attr); } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute **attr_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAILBOX_ATTRIBUTE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *attr_r = &item->u.attr; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_change(struct dsync_ibc *ibc, const struct dsync_mail_change *change) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAIL_CHANGE); dsync_mail_change_dup(item->pool, change, &item->u.change); } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_change(struct dsync_ibc *ibc, const struct dsync_mail_change **change_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAIL_CHANGE); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *change_r = &item->u.change; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request *request) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAIL_REQUEST); item->u.request.guid = p_strdup(item->pool, request->guid); item->u.request.uid = request->uid; } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request **request_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAIL_REQUEST); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *request_r = &item->u.request; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_mail(struct dsync_ibc *ibc, const struct dsync_mail *mail) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_MAIL); item->u.mail.guid = p_strdup(item->pool, mail->guid); item->u.mail.uid = mail->uid; item->u.mail.pop3_uidl = p_strdup(item->pool, mail->pop3_uidl); item->u.mail.pop3_order = mail->pop3_order; item->u.mail.received_date = mail->received_date; if (mail->input != NULL) { item->u.mail.input = mail->input; i_stream_ref(mail->input); } item->u.mail.input_mail = mail->input_mail; item->u.mail.input_mail_uid = mail->input_mail_uid; } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_mail(struct dsync_ibc *ibc, struct dsync_mail **mail_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; if (dsync_ibc_pipe_try_pop_eol(pipe)) return DSYNC_IBC_RECV_RET_FINISHED; item = dsync_ibc_pipe_pop_item(pipe, ITEM_MAIL); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *mail_r = &item->u.mail; return DSYNC_IBC_RECV_RET_OK; } static void dsync_ibc_pipe_send_finish(struct dsync_ibc *ibc, const char *error, enum mail_error mail_error, bool require_full_resync) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_FINISH); item->u.finish.error = p_strdup(item->pool, error); item->u.finish.mail_error = mail_error; item->u.finish.require_full_resync = require_full_resync; } static enum dsync_ibc_recv_ret dsync_ibc_pipe_recv_finish(struct dsync_ibc *ibc, const char **error_r, enum mail_error *mail_error_r, bool *require_full_resync_r) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; struct item *item; item = dsync_ibc_pipe_pop_item(pipe, ITEM_FINISH); if (item == NULL) return DSYNC_IBC_RECV_RET_TRYAGAIN; *error_r = item->u.finish.error; *mail_error_r = item->u.finish.mail_error; *require_full_resync_r = item->u.finish.require_full_resync; return DSYNC_IBC_RECV_RET_OK; } static void pipe_close_mail_streams(struct dsync_ibc_pipe *pipe) { struct item *item; if (array_count(&pipe->item_queue) > 0) { item = array_idx_modifiable(&pipe->item_queue, 0); if (item->type == ITEM_MAIL && item->u.mail.input != NULL) i_stream_unref(&item->u.mail.input); } } static void dsync_ibc_pipe_close_mail_streams(struct dsync_ibc *ibc) { struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc; pipe_close_mail_streams(pipe); pipe_close_mail_streams(pipe->remote); } static const struct dsync_ibc_vfuncs dsync_ibc_pipe_vfuncs = { dsync_ibc_pipe_deinit, dsync_ibc_pipe_send_handshake, dsync_ibc_pipe_recv_handshake, dsync_ibc_pipe_send_end_of_list, dsync_ibc_pipe_send_mailbox_state, dsync_ibc_pipe_recv_mailbox_state, dsync_ibc_pipe_send_mailbox_tree_node, dsync_ibc_pipe_recv_mailbox_tree_node, dsync_ibc_pipe_send_mailbox_deletes, dsync_ibc_pipe_recv_mailbox_deletes, dsync_ibc_pipe_send_mailbox, dsync_ibc_pipe_recv_mailbox, dsync_ibc_pipe_send_mailbox_attribute, dsync_ibc_pipe_recv_mailbox_attribute, dsync_ibc_pipe_send_change, dsync_ibc_pipe_recv_change, dsync_ibc_pipe_send_mail_request, dsync_ibc_pipe_recv_mail_request, dsync_ibc_pipe_send_mail, dsync_ibc_pipe_recv_mail, dsync_ibc_pipe_send_finish, dsync_ibc_pipe_recv_finish, dsync_ibc_pipe_close_mail_streams, dsync_ibc_pipe_is_send_queue_full, dsync_ibc_pipe_has_pending_data }; static struct dsync_ibc_pipe * dsync_ibc_pipe_alloc(void) { struct dsync_ibc_pipe *pipe; pipe = i_new(struct dsync_ibc_pipe, 1); pipe->ibc.v = dsync_ibc_pipe_vfuncs; i_array_init(&pipe->pools, 4); i_array_init(&pipe->item_queue, 4); return pipe; } void dsync_ibc_init_pipe(struct dsync_ibc **ibc1_r, struct dsync_ibc **ibc2_r) { struct dsync_ibc_pipe *pipe1, *pipe2; pipe1 = dsync_ibc_pipe_alloc(); pipe2 = dsync_ibc_pipe_alloc(); pipe1->remote = pipe2; pipe2->remote = pipe1; *ibc1_r = &pipe1->ibc; *ibc2_r = &pipe2->ibc; } dovecot-2.2.33.2/src/doveadm/dsync/dsync-ibc.h0000644000175000017500000001403113165463543015710 00000000000000#ifndef DSYNC_IBC_H #define DSYNC_IBC_H /* dsync inter-brain communicator */ #include "ioloop.h" #include "guid.h" #include "mail-error.h" #include "dsync-brain.h" struct dsync_mailbox; struct dsync_mailbox_state; struct dsync_mailbox_node; struct dsync_mailbox_delete; struct dsync_mailbox_attribute; struct dsync_mail; struct dsync_mail_change; struct dsync_mail_request; enum dsync_ibc_send_ret { DSYNC_IBC_SEND_RET_OK = 1, /* send queue is full, stop sending more */ DSYNC_IBC_SEND_RET_FULL = 0 }; enum dsync_ibc_recv_ret { DSYNC_IBC_RECV_RET_FINISHED = -1, /* try again / error (the error handling delayed until io callback) */ DSYNC_IBC_RECV_RET_TRYAGAIN = 0, DSYNC_IBC_RECV_RET_OK = 1 }; enum dsync_ibc_eol_type { DSYNC_IBC_EOL_MAILBOX_STATE, DSYNC_IBC_EOL_MAILBOX_TREE, DSYNC_IBC_EOL_MAILBOX_ATTRIBUTE, DSYNC_IBC_EOL_MAILBOX, DSYNC_IBC_EOL_MAIL_CHANGES, DSYNC_IBC_EOL_MAIL_REQUESTS, DSYNC_IBC_EOL_MAILS }; struct dsync_ibc_settings { /* Server hostname. Used for determining which server does the locking. */ const char *hostname; /* if non-NULL, sync only these namespaces (LF-separated) */ const char *sync_ns_prefixes; /* if non-NULL, sync only this mailbox name */ const char *sync_box; /* if non-NULL, use this mailbox for finding messages with GUIDs and copying them instead of saving them again. */ const char *virtual_all_box; /* if non-empty, sync only this mailbox GUID */ guid_128_t sync_box_guid; /* Exclude these mailboxes from the sync. They can contain '*' wildcards and be \special-use flags. */ const char *const *exclude_mailboxes; /* Sync only mails with received timestamp at least this high. */ time_t sync_since_timestamp; /* Sync only mails with received timestamp less or equal than this */ time_t sync_until_timestamp; /* Don't sync mails larger than this. */ uoff_t sync_max_size; /* Sync only mails with specified flags. */ const char *sync_flags; /* Hashed headers */ const char *const *hashed_headers; enum dsync_brain_sync_type sync_type; enum dsync_brain_flags brain_flags; bool hdr_hash_v2; bool hdr_hash_v3; unsigned int lock_timeout; unsigned int import_commit_msgs_interval; }; void dsync_ibc_init_pipe(struct dsync_ibc **ibc1_r, struct dsync_ibc **ibc2_r); struct dsync_ibc * dsync_ibc_init_stream(struct istream *input, struct ostream *output, const char *name, const char *temp_path_prefix, unsigned int timeout_secs); void dsync_ibc_deinit(struct dsync_ibc **ibc); /* I/O callback is called whenever new data is available. It's also called on errors, so check first the error status. */ void dsync_ibc_set_io_callback(struct dsync_ibc *ibc, io_callback_t *callback, void *context); void dsync_ibc_send_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings *set); enum dsync_ibc_recv_ret dsync_ibc_recv_handshake(struct dsync_ibc *ibc, const struct dsync_ibc_settings **set_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_end_of_list(struct dsync_ibc *ibc, enum dsync_ibc_eol_type type); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox_state(struct dsync_ibc *ibc, const struct dsync_mailbox_state *state); enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_state(struct dsync_ibc *ibc, struct dsync_mailbox_state *state_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox_tree_node(struct dsync_ibc *ibc, const char *const *name, const struct dsync_mailbox_node *node); enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_tree_node(struct dsync_ibc *ibc, const char *const **name_r, const struct dsync_mailbox_node **node_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete *deletes, unsigned int count, char hierarchy_sep); enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_deletes(struct dsync_ibc *ibc, const struct dsync_mailbox_delete **deletes_r, unsigned int *count_r, char *hierarchy_sep_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox *dsync_box); enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox(struct dsync_ibc *ibc, const struct dsync_mailbox **dsync_box_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute *attr); enum dsync_ibc_recv_ret dsync_ibc_recv_mailbox_attribute(struct dsync_ibc *ibc, const struct dsync_mailbox_attribute **attr_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_change(struct dsync_ibc *ibc, const struct dsync_mail_change *change); enum dsync_ibc_recv_ret dsync_ibc_recv_change(struct dsync_ibc *ibc, const struct dsync_mail_change **change_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request *request); enum dsync_ibc_recv_ret dsync_ibc_recv_mail_request(struct dsync_ibc *ibc, const struct dsync_mail_request **request_r); enum dsync_ibc_send_ret ATTR_NOWARN_UNUSED_RESULT dsync_ibc_send_mail(struct dsync_ibc *ibc, const struct dsync_mail *mail); enum dsync_ibc_recv_ret dsync_ibc_recv_mail(struct dsync_ibc *ibc, struct dsync_mail **mail_r); void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error, enum mail_error mail_error, bool require_full_resync); enum dsync_ibc_recv_ret dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r, enum mail_error *mail_error_r, bool *require_full_resync_r); /* Close any mail input streams that are kept open. This needs to be called before the mail is attempted to be freed (usually on error conditions). */ void dsync_ibc_close_mail_streams(struct dsync_ibc *ibc); bool dsync_ibc_has_failed(struct dsync_ibc *ibc); bool dsync_ibc_has_timed_out(struct dsync_ibc *ibc); bool dsync_ibc_is_send_queue_full(struct dsync_ibc *ibc); bool dsync_ibc_has_pending_data(struct dsync_ibc *ibc); #endif dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-state.c0000644000175000017500000000760713147010711017713 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "base64.h" #include "crc32.h" #include "hash.h" #include "dsync-mailbox-state.h" #define DSYNC_STATE_MAJOR_VERSION 1 #define DSYNC_STATE_MINOR_VERSION 0 #define V0_MAILBOX_SIZE (GUID_128_SIZE + 4 + 4 + 8 + 8) #define MAILBOX_SIZE (GUID_128_SIZE + 4 + 4 + 8 + 8 + 4) static void put_uint32(buffer_t *output, uint32_t num) { uint8_t tmp[sizeof(uint32_t)]; cpu32_to_le_unaligned(num, tmp); buffer_append(output, tmp, sizeof(tmp)); } void dsync_mailbox_states_export(const HASH_TABLE_TYPE(dsync_mailbox_state) states, string_t *output) { struct hash_iterate_context *iter; struct dsync_mailbox_state *state; uint8_t *guid; buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 128); uint32_t crc = 0; buffer_append_c(buf, DSYNC_STATE_MAJOR_VERSION); buffer_append_c(buf, DSYNC_STATE_MINOR_VERSION); buffer_append_c(buf, '\0'); buffer_append_c(buf, '\0'); iter = hash_table_iterate_init(states); while (hash_table_iterate(iter, states, &guid, &state)) { buffer_append(buf, state->mailbox_guid, sizeof(state->mailbox_guid)); put_uint32(buf, state->last_uidvalidity); put_uint32(buf, state->last_common_uid); put_uint32(buf, state->last_common_modseq & 0xffffffffU); put_uint32(buf, state->last_common_modseq >> 32); put_uint32(buf, state->last_common_pvt_modseq & 0xffffffffU); put_uint32(buf, state->last_common_pvt_modseq >> 32); put_uint32(buf, state->last_messages_count); /* v1 */ if (buf->used % 3 == 0) { crc = crc32_data_more(crc, buf->data, buf->used); base64_encode(buf->data, buf->used, output); buffer_set_used_size(buf, 0); } } hash_table_iterate_deinit(&iter); crc = crc32_data_more(crc, buf->data, buf->used); put_uint32(buf, crc); base64_encode(buf->data, buf->used, output); } static int dsync_mailbox_states_retry_import_v0(const buffer_t *buf) { const unsigned char *data = buf->data; /* v0 had no version header and no last_messages_count */ if ((buf->used-4) % V0_MAILBOX_SIZE != 0 || le32_to_cpu_unaligned(data + buf->used-4) != crc32_data(data, buf->used-4)) return -1; /* looks like valid v0 format, silently treat it as empty state */ return 0; } int dsync_mailbox_states_import(HASH_TABLE_TYPE(dsync_mailbox_state) states, pool_t pool, const char *input, const char **error_r) { struct dsync_mailbox_state *state; buffer_t *buf; uint8_t *guid_p; const unsigned char *data; size_t pos; unsigned int i, count; buf = buffer_create_dynamic(pool_datastack_create(), strlen(input)); if (base64_decode(input, strlen(input), &pos, buf) < 0) { *error_r = "Invalid base64 data"; return -1; } /* v1: 4 byte header, mailboxes[], CRC32 */ data = buf->data; if (buf->used == 4 && le32_to_cpu_unaligned(data) == 0) { /* v0: Empty state */ return 0; } if (buf->used < 8) { *error_r = "Input too small"; return -1; } if ((buf->used-8) % MAILBOX_SIZE != 0) { *error_r = "Invalid input size"; return dsync_mailbox_states_retry_import_v0(buf); } if (le32_to_cpu_unaligned(data + buf->used-4) != crc32_data(data, buf->used-4)) { *error_r = "CRC32 mismatch"; return dsync_mailbox_states_retry_import_v0(buf); } data += 4; count = (buf->used-8) / MAILBOX_SIZE; for (i = 0; i < count; i++, data += MAILBOX_SIZE) { state = p_new(pool, struct dsync_mailbox_state, 1); memcpy(state->mailbox_guid, data, GUID_128_SIZE); state->last_uidvalidity = le32_to_cpu_unaligned(data + GUID_128_SIZE); state->last_common_uid = le32_to_cpu_unaligned(data + GUID_128_SIZE + 4); state->last_common_modseq = le64_to_cpu_unaligned(data + GUID_128_SIZE + 8); state->last_common_pvt_modseq = le64_to_cpu_unaligned(data + GUID_128_SIZE + 16); state->last_messages_count = le32_to_cpu_unaligned(data + GUID_128_SIZE + 24); guid_p = state->mailbox_guid; hash_table_insert(states, guid_p, state); } return 0; } dovecot-2.2.33.2/src/doveadm/dsync/Makefile.am0000644000175000017500000000377313123174404015720 00000000000000pkglib_LTLIBRARIES = libdovecot-dsync.la noinst_LTLIBRARIES = libdsync.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage libdsync_la_SOURCES = \ dsync-brain.c \ dsync-brain-mailbox.c \ dsync-brain-mailbox-tree.c \ dsync-brain-mailbox-tree-sync.c \ dsync-brain-mails.c \ dsync-deserializer.c \ dsync-mail.c \ dsync-mailbox.c \ dsync-mailbox-import.c \ dsync-mailbox-export.c \ dsync-mailbox-state.c \ dsync-mailbox-tree.c \ dsync-mailbox-tree-fill.c \ dsync-mailbox-tree-sync.c \ dsync-serializer.c \ dsync-ibc.c \ dsync-ibc-stream.c \ dsync-ibc-pipe.c \ dsync-transaction-log-scan.c libdovecot_dsync_la_SOURCES = libdovecot_dsync_la_LIBADD = libdsync.la ../../lib-storage/libdovecot-storage.la ../../lib-dovecot/libdovecot.la libdovecot_dsync_la_DEPENDENCIES = libdsync.la libdovecot_dsync_la_LDFLAGS = -export-dynamic pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ dsync-brain.h \ dsync-ibc.h noinst_HEADERS = \ dsync-brain-private.h \ dsync-mail.h \ dsync-mailbox.h \ dsync-mailbox-import.h \ dsync-mailbox-export.h \ dsync-mailbox-state.h \ dsync-mailbox-tree.h \ dsync-mailbox-tree-private.h \ dsync-serializer.h \ dsync-deserializer.h \ dsync-ibc-private.h \ dsync-transaction-log-scan.h test_programs = \ test-dsync-mailbox-tree-sync noinst_PROGRAMS = $(test_programs) test_libs = \ ../../lib-test/libtest.la \ ../../lib/liblib.la test_dsync_mailbox_tree_sync_SOURCES = test-dsync-mailbox-tree-sync.c test_dsync_mailbox_tree_sync_LDADD = dsync-mailbox-tree-sync.lo dsync-mailbox-tree.lo $(test_libs) test_dsync_mailbox_tree_sync_DEPENDENCIES = $(pkglib_LTLIBRARIES) $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/doveadm/dsync/dsync-mailbox-tree.h0000644000175000017500000001704013165463624017546 00000000000000#ifndef DSYNC_MAILBOX_TREE_H #define DSYNC_MAILBOX_TREE_H #include "guid.h" #include "mail-error.h" struct mail_namespace; struct dsync_brain; enum dsync_mailbox_trees_sync_type { /* two-way sync for both mailboxes */ DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY, /* make remote tree look exactly like the local tree */ DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL, /* make local tree look exactly like the remote tree */ DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE }; enum dsync_mailbox_trees_sync_flags { /* Enable debugging */ DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG = 0x01, /* Show ourself as "master brain" in the debug output */ DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN = 0x02, /* Disable mailbox renaming logic. This is just a kludge that should be removed once the renaming logic has no more bugs.. */ DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES = 0x04 }; enum dsync_mailbox_node_existence { /* this is just a filler node for children or for subscription deletion */ DSYNC_MAILBOX_NODE_NONEXISTENT = 0, /* if mailbox GUID is set, the mailbox exists. otherwise the directory exists. */ DSYNC_MAILBOX_NODE_EXISTS, /* if mailbox GUID is set, the mailbox has been deleted. otherwise the directory has been deleted. */ DSYNC_MAILBOX_NODE_DELETED }; struct dsync_mailbox_node { struct dsync_mailbox_node *parent, *next, *first_child; /* namespace where this node belongs to */ struct mail_namespace *ns; /* this node's name (not including parents) */ const char *name; /* mailbox GUID, or full of zeros if this is about a directory name */ guid_128_t mailbox_guid; /* mailbox's UIDVALIDITY/UIDNEXT (may be 0 if not assigned yet) */ uint32_t uid_validity, uid_next; /* existence of this mailbox/directory. doesn't affect subscription state. */ enum dsync_mailbox_node_existence existence; /* last time the mailbox/directory was created/renamed, 0 if not known */ time_t last_renamed_or_created; /* last time the subscription state was changed, 0 if not known */ time_t last_subscription_change; /* is this mailbox or directory subscribed? */ unsigned int subscribed:1; /* Internal syncing flags: */ unsigned int sync_delayed_guid_change:1; unsigned int sync_temporary_name:1; }; ARRAY_DEFINE_TYPE(dsync_mailbox_node, struct dsync_mailbox_node *); #define dsync_mailbox_node_guids_equal(node1, node2) \ (memcmp((node1)->mailbox_guid, (node2)->mailbox_guid, \ sizeof(guid_128_t)) == 0) #define dsync_mailbox_node_is_dir(node) \ guid_128_is_empty((node)->mailbox_guid) enum dsync_mailbox_delete_type { /* Delete mailbox by given GUID */ DSYNC_MAILBOX_DELETE_TYPE_MAILBOX = 1, /* Delete mailbox directory by given SHA1 name */ DSYNC_MAILBOX_DELETE_TYPE_DIR, /* Unsubscribe mailbox by given SHA1 name */ DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE, }; struct dsync_mailbox_delete { enum dsync_mailbox_delete_type type; guid_128_t guid; time_t timestamp; }; enum dsync_mailbox_tree_sync_type { DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX, DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR, DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX, DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR, /* Rename given mailbox name and its children */ DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME, DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE, DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE }; struct dsync_mailbox_tree_sync_change { enum dsync_mailbox_tree_sync_type type; /* for all types: */ struct mail_namespace *ns; const char *full_name; /* for create_box and delete_box: */ guid_128_t mailbox_guid; /* for create_box: */ uint32_t uid_validity; /* for rename: */ const char *rename_dest_name; }; struct dsync_mailbox_tree *dsync_mailbox_tree_init(char sep, char alt_char); void dsync_mailbox_tree_deinit(struct dsync_mailbox_tree **tree); /* Lookup a mailbox node by name. Returns NULL if not known. */ struct dsync_mailbox_node * dsync_mailbox_tree_lookup(struct dsync_mailbox_tree *tree, const char *full_name); /* Lookup a mailbox node by GUID. Returns NULL if not known. The mailbox GUID hash must have been build before calling this. */ struct dsync_mailbox_node * dsync_mailbox_tree_lookup_guid(struct dsync_mailbox_tree *tree, const guid_128_t guid); /* Lookup or create a mailbox node by name. */ struct dsync_mailbox_node * dsync_mailbox_tree_get(struct dsync_mailbox_tree *tree, const char *full_name); /* Returns full name for the given mailbox node. */ const char *dsync_mailbox_node_get_full_name(const struct dsync_mailbox_tree *tree, const struct dsync_mailbox_node *node); void dsync_mailbox_node_append_full_name(string_t *str, const struct dsync_mailbox_tree *tree, const struct dsync_mailbox_node *node); /* Copy everything from src to dest, except name and hierarchy pointers */ void dsync_mailbox_node_copy_data(struct dsync_mailbox_node *dest, const struct dsync_mailbox_node *src); /* Add nodes to tree from the given namespace. If box_name or box_guid is non-NULL, add only that mailbox to the tree. */ int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree, struct mail_namespace *ns, const char *box_name, const guid_128_t box_guid, const char *const *exclude_mailboxes, enum mail_error *error_r); /* Return all known deleted mailboxes and directories. */ const struct dsync_mailbox_delete * dsync_mailbox_tree_get_deletes(struct dsync_mailbox_tree *tree, unsigned int *count_r); /* Return mailbox node for a given delete record, or NULL if it doesn't exist. The delete record is intended to come from another tree, possibly with a different hierarchy separator. dsync_mailbox_tree_build_guid_hash() must have been called before this. */ struct dsync_mailbox_node * dsync_mailbox_tree_find_delete(struct dsync_mailbox_tree *tree, const struct dsync_mailbox_delete *del); /* Build GUID lookup hash, if it's not already built. Returns 0 if ok, -1 if there are duplicate GUIDs. The nodes with the duplicate GUIDs are returned. */ int dsync_mailbox_tree_build_guid_hash(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node **dup_node1_r, struct dsync_mailbox_node **dup_node2_r); /* Manually add a new node to hash. */ int dsync_mailbox_tree_guid_hash_add(struct dsync_mailbox_tree *tree, struct dsync_mailbox_node *node, struct dsync_mailbox_node **old_node_r); /* Set remote separator used for directory deletions in dsync_mailbox_tree_find_delete() */ void dsync_mailbox_tree_set_remote_sep(struct dsync_mailbox_tree *tree, char remote_sep); /* Iterate through all nodes in a tree (depth-first) */ struct dsync_mailbox_tree_iter * dsync_mailbox_tree_iter_init(struct dsync_mailbox_tree *tree); bool dsync_mailbox_tree_iter_next(struct dsync_mailbox_tree_iter *iter, const char **full_name_r, struct dsync_mailbox_node **node_r); void dsync_mailbox_tree_iter_deinit(struct dsync_mailbox_tree_iter **iter); /* Sync local and remote trees so at the end they're exactly the same. Return changes done to local tree. */ struct dsync_mailbox_tree_sync_ctx * dsync_mailbox_trees_sync_init(struct dsync_mailbox_tree *local_tree, struct dsync_mailbox_tree *remote_tree, enum dsync_mailbox_trees_sync_type sync_type, enum dsync_mailbox_trees_sync_flags sync_flags); const struct dsync_mailbox_tree_sync_change * dsync_mailbox_trees_sync_next(struct dsync_mailbox_tree_sync_ctx *ctx); int dsync_mailbox_trees_sync_deinit(struct dsync_mailbox_tree_sync_ctx **ctx); const char *dsync_mailbox_node_to_string(const struct dsync_mailbox_node *node); const char * dsync_mailbox_delete_type_to_string(enum dsync_mailbox_delete_type type); #endif dovecot-2.2.33.2/src/doveadm/doveadm-mail-iter.h0000644000175000017500000000166213123174404016210 00000000000000#ifndef DOVEADM_MAIL_ITER_H #define DOVEADM_MAIL_ITER_H #include "mailbox-list-iter.h" struct doveadm_mail_iter; struct doveadm_mail_cmd_context; int doveadm_mail_iter_init(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info, struct mail_search_args *search_args, enum mail_fetch_field wanted_fields, const char *const *wanted_headers, bool readonly, struct doveadm_mail_iter **iter_r) ATTR_NULL(6); int doveadm_mail_iter_deinit(struct doveadm_mail_iter **iter); int doveadm_mail_iter_deinit_sync(struct doveadm_mail_iter **iter); int doveadm_mail_iter_deinit_keep_box(struct doveadm_mail_iter **iter, struct mailbox **box_r); void doveadm_mail_iter_deinit_rollback(struct doveadm_mail_iter **iter); struct mailbox *doveadm_mail_iter_get_mailbox(struct doveadm_mail_iter *iter); bool doveadm_mail_iter_next(struct doveadm_mail_iter *iter, struct mail **mail_r); #endif dovecot-2.2.33.2/src/doveadm/doveadm-who.h0000644000175000017500000000140113123174404015111 00000000000000#ifndef DOVEADM_WHO_H #define DOVEADM_WHO_H struct who_line { const char *username; const char *service; struct ip_addr ip; pid_t pid; unsigned int refcount; }; struct who_filter { const char *username; struct ip_addr net_ip; unsigned int net_bits; }; struct who_context { const char *anvil_path; struct who_filter filter; pool_t pool; HASH_TABLE(struct who_user *, struct who_user *) users; }; typedef void who_callback_t(struct who_context *ctx, const struct who_line *line); int who_parse_args(struct who_context *ctx, const char *const *masks); void who_lookup(struct who_context *ctx, who_callback_t *callback); bool who_line_filter_match(const struct who_line *line, const struct who_filter *filter); #endif /* DOVEADM_WHO_H */ dovecot-2.2.33.2/src/doveadm/main.c0000644000175000017500000000724013165463624013640 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-access.h" #include "process-title.h" #include "master-service.h" #include "master-service-settings.h" #include "settings-parser.h" #include "dict.h" #include "client-connection.h" #include "client-connection-private.h" #include "doveadm-settings.h" #include "doveadm-dump.h" #include "doveadm-mail.h" #include "doveadm-print-private.h" #include "doveadm-server.h" #include "ostream.h" const struct doveadm_print_vfuncs *doveadm_print_vfuncs_all[] = { &doveadm_print_server_vfuncs, &doveadm_print_json_vfuncs, NULL }; struct client_connection *doveadm_client; bool doveadm_verbose_proctitle; int doveadm_exit_code = 0; static void doveadm_die(void) { /* do nothing. doveadm connections should be over soon. */ } static void client_connected(struct master_service_connection *conn) { if (doveadm_client != NULL) { i_error("doveadm server can handle only a single client"); return; } master_service_client_connection_accept(conn); if (strcmp(conn->name, "http") == 0) { doveadm_client = client_connection_create_http(conn->fd, conn->ssl); } else { doveadm_client = client_connection_create(conn->fd, conn->listen_fd, conn->ssl); } } void help(const struct doveadm_cmd *cmd) { i_fatal("Client sent invalid command. Usage: %s %s", cmd->name, cmd->short_usage); } void help_ver2(const struct doveadm_cmd_ver2 *cmd) { i_fatal("Client sent invalid command. Usage: %s %s", cmd->name, cmd->usage); } static void main_preinit(void) { restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } static void main_init(void) { doveadm_server = TRUE; doveadm_settings = master_service_settings_get_others(master_service)[0]; doveadm_settings = settings_dup(&doveadm_setting_parser_info, doveadm_settings, pool_datastack_create()); doveadm_verbose_proctitle = master_service_settings_get(master_service)->verbose_proctitle; if (doveadm_verbose_proctitle) process_title_set("[idling]"); doveadm_http_server_init(); doveadm_cmds_init(); doveadm_register_auth_server_commands(); doveadm_dump_init(); doveadm_mail_init(); dict_drivers_register_builtin(); doveadm_load_modules(); } static void main_deinit(void) { if (doveadm_client != NULL) client_connection_destroy(&doveadm_client); doveadm_mail_deinit(); doveadm_dump_deinit(); doveadm_unload_modules(); dict_drivers_unregister_builtin(); doveadm_print_deinit(); doveadm_cmds_deinit(); doveadm_http_server_deinit(); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &doveadm_setting_parser_info, NULL }; enum master_service_flags service_flags = MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; struct master_service_settings_input input; struct master_service_settings_output output; const char *error; int c; master_service = master_service_init("doveadm", service_flags, &argc, &argv, "D"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': doveadm_debug = TRUE; doveadm_verbose = TRUE; break; default: return FATAL_DEFAULT; } } i_zero(&input); input.roots = set_roots; input.module = "doveadm"; input.service = "doveadm"; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "doveadm: "); main_preinit(); master_service_set_die_callback(master_service, doveadm_die); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/doveadm/doveadm-print.c0000644000175000017500000001045113165463624015463 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "doveadm-print-private.h" struct doveadm_print_header_context { const char *key; char *sticky_value; bool sticky; }; struct doveadm_print_context { pool_t pool; ARRAY(struct doveadm_print_header_context) headers; const struct doveadm_print_vfuncs *v; unsigned int header_idx; bool print_stream_open; }; bool doveadm_print_hide_titles = FALSE; struct ostream *doveadm_print_ostream = NULL; static struct doveadm_print_context *ctx; bool doveadm_print_is_initialized(void) { return ctx != NULL; } void doveadm_print_header(const char *key, const char *title, enum doveadm_print_header_flags flags) { struct doveadm_print_header hdr; struct doveadm_print_header_context *hdr_ctx; i_assert(title != NULL); i_zero(&hdr); hdr.key = key; hdr.title = title; hdr.flags = flags; ctx->v->header(&hdr); hdr_ctx = array_append_space(&ctx->headers); hdr_ctx->key = p_strdup(ctx->pool, key); hdr_ctx->sticky = (flags & DOVEADM_PRINT_HEADER_FLAG_STICKY) != 0; } void doveadm_print_header_simple(const char *key_title) { doveadm_print_header(key_title, key_title, 0); } static void doveadm_print_sticky_headers(void) { const struct doveadm_print_header_context *headers; unsigned int count; headers = array_get(&ctx->headers, &count); i_assert(count > 0); for (;;) { if (ctx->header_idx == count) ctx->header_idx = 0; else if (headers[ctx->header_idx].sticky) { ctx->v->print(headers[ctx->header_idx].sticky_value); ctx->header_idx++; } else { break; } } } void doveadm_print(const char *value) { i_assert(!ctx->print_stream_open); doveadm_print_sticky_headers(); ctx->v->print(value); ctx->header_idx++; } void doveadm_print_num(uintmax_t value) { T_BEGIN { doveadm_print(dec2str(value)); } T_END; } void doveadm_print_stream(const void *value, size_t size) { if (!ctx->print_stream_open) { doveadm_print_sticky_headers(); ctx->print_stream_open = TRUE; } ctx->v->print_stream(value, size); if (size == 0) { ctx->header_idx++; ctx->print_stream_open = FALSE; } } int doveadm_print_istream(struct istream *input) { const unsigned char *data; size_t size; ssize_t ret; while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { doveadm_print_stream(data, size); i_stream_skip(input, size); } i_assert(ret == -1); doveadm_print_stream("", 0); if (input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return -1; } return 0; } void doveadm_print_sticky(const char *key, const char *value) { struct doveadm_print_header_context *hdr; if (ctx == NULL) { /* command doesn't really print anything */ return; } array_foreach_modifiable(&ctx->headers, hdr) { if (strcmp(hdr->key, key) == 0) { i_free(hdr->sticky_value); hdr->sticky_value = i_strdup(value); return; } } i_unreached(); } void doveadm_print_flush(void) { if (ctx != NULL && ctx->v->flush != NULL) ctx->v->flush(); o_stream_uncork(doveadm_print_ostream); o_stream_cork(doveadm_print_ostream); } void doveadm_print_unstick_headers(void) { struct doveadm_print_header_context *hdr; if (ctx != NULL) { array_foreach_modifiable(&ctx->headers, hdr) hdr->sticky = FALSE; } } void doveadm_print_init(const char *name) { pool_t pool; unsigned int i; if (ctx != NULL) { /* already forced the type */ return; } pool = pool_alloconly_create("doveadm print", 1024); ctx = p_new(pool, struct doveadm_print_context, 1); ctx->pool = pool; p_array_init(&ctx->headers, pool, 16); for (i = 0; doveadm_print_vfuncs_all[i] != NULL; i++) { if (strcmp(doveadm_print_vfuncs_all[i]->name, name) == 0) { ctx->v = doveadm_print_vfuncs_all[i]; break; } } if (ctx->v == NULL) i_fatal("Unknown print formatter: %s", name); if (ctx->v->init != NULL) ctx->v->init(); } void doveadm_print_deinit(void) { struct doveadm_print_header_context *hdr; if (ctx == NULL) return; if (ctx->v->flush != NULL && doveadm_print_ostream != NULL) { ctx->v->flush(); o_stream_uncork(doveadm_print_ostream); } if (ctx->v->deinit != NULL) ctx->v->deinit(); array_foreach_modifiable(&ctx->headers, hdr) i_free(hdr->sticky_value); pool_unref(&ctx->pool); ctx = NULL; } dovecot-2.2.33.2/src/doveadm/doveadm-mailbox-list-iter.c0000644000175000017500000001241613123174404017664 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "mail-search.h" #include "mail-namespace.h" #include "mailbox-list.h" #include "doveadm-mail.h" #include "doveadm-mailbox-list-iter.h" struct doveadm_mailbox_list_iter { struct mail_user *user; struct doveadm_mail_cmd_context *ctx; struct mail_search_args *search_args; enum mailbox_list_iter_flags iter_flags; struct mailbox_list_iterate_context *iter; struct mailbox_info info; ARRAY_TYPE(const_string) patterns; unsigned int pattern_idx; bool only_selectable; }; static bool search_args_get_mailbox_patterns(const struct mail_search_arg *args, ARRAY_TYPE(const_string) *patterns, bool *have_guid, bool *have_wildcards) { const struct mail_search_arg *subargs; for (; args != NULL; args = args->next) { switch (args->type) { case SEARCH_OR: /* we don't currently try to optimize OR. */ break; case SEARCH_SUB: case SEARCH_INTHREAD: subargs = args->value.subargs; for (; subargs != NULL; subargs = subargs->next) { if (!search_args_get_mailbox_patterns(subargs, patterns, have_guid, have_wildcards)) return FALSE; } break; case SEARCH_MAILBOX_GLOB: *have_wildcards = TRUE; /* fall through */ case SEARCH_MAILBOX: if (args->match_not) { array_clear(patterns); return FALSE; } array_append(patterns, &args->value.str, 1); break; case SEARCH_MAILBOX_GUID: *have_guid = TRUE; break; default: break; } } return TRUE; } static struct doveadm_mailbox_list_iter * doveadm_mailbox_list_iter_init_nsmask(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, struct mail_search_args *search_args, enum mailbox_list_iter_flags iter_flags, enum mail_namespace_type ns_mask) { static const char *all_pattern = "*"; struct doveadm_mailbox_list_iter *iter; bool have_guid = FALSE, have_wildcards = FALSE; iter = i_new(struct doveadm_mailbox_list_iter, 1); iter->ctx = ctx; iter->search_args = search_args; iter->user = user; i_array_init(&iter->patterns, 16); (void)search_args_get_mailbox_patterns(search_args->args, &iter->patterns, &have_guid, &have_wildcards); if (array_count(&iter->patterns) == 0) { iter_flags |= MAILBOX_LIST_ITER_SKIP_ALIASES; if (have_guid) { ns_mask |= MAIL_NAMESPACE_TYPE_SHARED | MAIL_NAMESPACE_TYPE_PUBLIC; } array_append(&iter->patterns, &all_pattern, 1); } else if (have_wildcards) { iter_flags |= MAILBOX_LIST_ITER_STAR_WITHIN_NS; ns_mask |= MAIL_NAMESPACE_TYPE_SHARED | MAIL_NAMESPACE_TYPE_PUBLIC; } else { /* just return the listed mailboxes without actually iterating through. this also allows accessing mailboxes without lookup ACL right */ return iter; } array_append_zero(&iter->patterns); iter->only_selectable = TRUE; iter->iter_flags = iter_flags; iter->iter = mailbox_list_iter_init_namespaces(user->namespaces, array_idx(&iter->patterns, 0), ns_mask, iter_flags); return iter; } struct doveadm_mailbox_list_iter * doveadm_mailbox_list_iter_init(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, struct mail_search_args *search_args, enum mailbox_list_iter_flags iter_flags) { enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_PRIVATE; return doveadm_mailbox_list_iter_init_nsmask(ctx, user, search_args, iter_flags, ns_mask); } struct doveadm_mailbox_list_iter * doveadm_mailbox_list_iter_full_init(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, struct mail_search_args *search_args, enum mailbox_list_iter_flags iter_flags) { enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_MASK_ALL; struct doveadm_mailbox_list_iter *iter; iter = doveadm_mailbox_list_iter_init_nsmask(ctx, user, search_args, iter_flags, ns_mask); iter->only_selectable = FALSE; return iter; } int doveadm_mailbox_list_iter_deinit(struct doveadm_mailbox_list_iter **_iter) { struct doveadm_mailbox_list_iter *iter = *_iter; enum mail_error error; int ret; *_iter = NULL; if (iter->iter == NULL) ret = 0; else if ((ret = mailbox_list_iter_deinit(&iter->iter)) < 0) { i_error("Listing mailboxes failed: %s", mailbox_list_get_last_internal_error(iter->user->namespaces->list, &error)); doveadm_mail_failed_error(iter->ctx, error); } array_free(&iter->patterns); i_free(iter); return ret; } const struct mailbox_info * doveadm_mailbox_list_iter_next(struct doveadm_mailbox_list_iter *iter) { const struct mailbox_info *info; const char *const *patterns; unsigned int count; while (iter->iter == NULL) { patterns = array_get(&iter->patterns, &count); if (iter->pattern_idx == count) return NULL; iter->info.vname = patterns[iter->pattern_idx++]; iter->info.ns = mail_namespace_find(iter->user->namespaces, iter->info.vname); return &iter->info; } while ((info = mailbox_list_iter_next(iter->iter)) != NULL) { char sep = mail_namespace_get_sep(info->ns); if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) { if (iter->only_selectable) continue; } if (mail_search_args_match_mailbox(iter->search_args, info->vname, sep)) break; } return info; } dovecot-2.2.33.2/src/doveadm/client-connection.c0000644000175000017500000004622013167162046016324 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "str.h" #include "base64.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "process-title.h" #include "settings-parser.h" #include "iostream-ssl.h" #include "ostream-multiplex.h" #include "master-service.h" #include "master-service-ssl.h" #include "master-service-settings.h" #include "mail-storage-service.h" #include "doveadm-util.h" #include "doveadm-server.h" #include "doveadm-mail.h" #include "doveadm-print.h" #include "doveadm-settings.h" #include "client-connection-private.h" #include #define MAX_INBUF_SIZE (1024*1024) static void client_connection_input(struct client_connection *conn); static failure_callback_t *orig_error_callback, *orig_fatal_callback; static failure_callback_t *orig_info_callback, *orig_debug_callback = NULL; static bool log_recursing = FALSE; static void ATTR_FORMAT(2, 0) doveadm_server_log_handler(const struct failure_context *ctx, const char *format, va_list args) { if (!log_recursing && doveadm_client != NULL && doveadm_client->log_out != NULL) T_BEGIN { /* prevent re-entering this code if any of the following code causes logging */ log_recursing = TRUE; char c = doveadm_log_type_to_char(ctx->type); const char *ptr,*start; bool corked = o_stream_is_corked(doveadm_client->log_out); va_list va; va_copy(va, args); string_t *str = t_str_new(128); str_vprintfa(str, format, va); va_end(va); start = str_c(str); if (!corked) o_stream_cork(doveadm_client->log_out); while((ptr = strchr(start, '\n'))!=NULL) { o_stream_nsend(doveadm_client->log_out, &c, 1); o_stream_nsend(doveadm_client->log_out, start, ptr-start+1); str_delete(str, 0, ptr-start+1); } if (str->used > 0) { o_stream_nsend(doveadm_client->log_out, &c, 1); o_stream_nsend(doveadm_client->log_out, str->data, str->used); o_stream_nsend(doveadm_client->log_out, "\n", 1); } o_stream_uncork(doveadm_client->log_out); if (corked) o_stream_cork(doveadm_client->log_out); log_recursing = FALSE; } T_END; switch(ctx->type) { case LOG_TYPE_DEBUG: orig_debug_callback(ctx, format, args); break; case LOG_TYPE_INFO: orig_info_callback(ctx, format, args); break; case LOG_TYPE_WARNING: case LOG_TYPE_ERROR: orig_error_callback(ctx, format, args); break; default: i_unreached(); } } static void doveadm_server_capture_logs(void) { i_assert(orig_debug_callback == NULL); i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback, &orig_info_callback, &orig_debug_callback); i_set_error_handler(doveadm_server_log_handler); i_set_info_handler(doveadm_server_log_handler); i_set_debug_handler(doveadm_server_log_handler); } static void doveadm_server_restore_logs(void) { i_assert(orig_debug_callback != NULL); i_set_error_handler(orig_error_callback); i_set_info_handler(orig_info_callback); i_set_debug_handler(orig_debug_callback); orig_fatal_callback = NULL; orig_error_callback = NULL; orig_info_callback = NULL; orig_debug_callback = NULL; } static void doveadm_cmd_server_post(struct client_connection *conn, const char *cmd_name) { const char *str = NULL; if (doveadm_exit_code == 0) { o_stream_nsend(conn->output, "\n+\n", 3); return; } str = doveadm_exit_code_to_str(doveadm_exit_code); if (str != NULL) { o_stream_nsend_str(conn->output, t_strdup_printf("\n-%s\n", str)); } else { o_stream_nsend_str(conn->output, "\n-\n"); i_error("BUG: Command '%s' returned unknown error code %d", cmd_name, doveadm_exit_code); } } static void doveadm_cmd_server_run_ver2(struct client_connection *conn, int argc, const char *const argv[], struct doveadm_cmd_context *cctx) { i_getopt_reset(); if (doveadm_cmd_run_ver2(argc, argv, cctx) < 0) doveadm_exit_code = EX_USAGE; doveadm_cmd_server_post(conn, cctx->cmd->name); } static void doveadm_cmd_server_run(struct client_connection *conn, int argc, const char *const argv[], const struct doveadm_cmd *cmd) { i_getopt_reset(); cmd->cmd(argc, (char **)argv); doveadm_cmd_server_post(conn, cmd->name); } static int doveadm_mail_cmd_server_parse(const struct doveadm_mail_cmd *cmd, const struct doveadm_settings *set, int argc, const char *const argv[], struct doveadm_cmd_context *cctx, struct doveadm_mail_cmd_context **mctx_r) { struct doveadm_mail_cmd_context *mctx; const char *getopt_args; bool add_username_header = FALSE; int c; mctx = doveadm_mail_cmd_init(cmd, set); mctx->full_args = argv+1; mctx->proxying = TRUE; mctx->cur_username = cctx->username; mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; if (doveadm_debug) mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG; i_getopt_reset(); getopt_args = t_strconcat("AF:S:u:", mctx->getopt_args, NULL); while ((c = getopt(argc, (char **)argv, getopt_args)) > 0) { switch (c) { case 'A': case 'F': add_username_header = TRUE; break; case 'S': /* ignore */ break; case 'u': if (strchr(optarg, '*') != NULL || strchr(optarg, '?') != NULL) add_username_header = TRUE; break; default: if ((mctx->v.parse_arg == NULL || !mctx->v.parse_arg(mctx, c))) { i_error("doveadm %s: " "Client sent unknown parameter: %c", cmd->name, c); mctx->v.deinit(mctx); pool_unref(&mctx->pool); return -1; } } } if (argv[optind] != NULL && cmd->usage_args == NULL) { i_error("doveadm %s: Client sent unknown parameter: %s", cmd->name, argv[optind]); mctx->v.deinit(mctx); pool_unref(&mctx->pool); return -1; } mctx->args = argv+optind; if (mctx->cur_username != NULL) { if (strchr(mctx->cur_username, '*') != NULL || strchr(mctx->cur_username, '?') != NULL) { add_username_header = TRUE; } } if (doveadm_print_is_initialized() && add_username_header) { doveadm_print_header("username", "Username", DOVEADM_PRINT_HEADER_FLAG_STICKY | DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); doveadm_print_sticky("username", cctx->username); } *mctx_r = mctx; return 0; } static void doveadm_mail_cmd_server_run(struct client_connection *conn, struct doveadm_mail_cmd_context *mctx, struct doveadm_cmd_context *cctx) { const char *error; int ret; mctx->conn = conn; o_stream_cork(conn->output); if (mctx->v.preinit != NULL) mctx->v.preinit(mctx); ret = doveadm_mail_single_user(mctx, cctx, &error); doveadm_mail_server_flush(); mctx->v.deinit(mctx); doveadm_print_flush(); mail_storage_service_deinit(&mctx->storage_service); if (ret < 0) { i_error("%s: %s", mctx->cmd->name, error); o_stream_nsend(conn->output, "\n-\n", 3); } else if (ret == 0) { o_stream_nsend_str(conn->output, "\n-NOUSER\n"); } else if (mctx->exit_code != 0) { /* maybe not an error, but not a full success either */ o_stream_nsend_str(conn->output, t_strdup_printf("\n-%u\n", mctx->exit_code)); } else { o_stream_nsend(conn->output, "\n+\n", 3); } o_stream_uncork(conn->output); pool_unref(&mctx->pool); } bool doveadm_client_is_allowed_command(const struct doveadm_settings *set, const char *cmd_name) { bool ret = FALSE; if (*set->doveadm_allowed_commands == '\0') return TRUE; T_BEGIN { const char *const *cmds = t_strsplit(set->doveadm_allowed_commands, ","); for (; *cmds != NULL; cmds++) { if (strcmp(*cmds, cmd_name) == 0) { ret = TRUE; break; } } } T_END; return ret; } static int doveadm_cmd_handle(struct client_connection *conn, const char *cmd_name, int argc, const char *const argv[], struct doveadm_cmd_context *cctx) { struct ioloop *ioloop, *prev_ioloop = current_ioloop; const struct doveadm_cmd *cmd = NULL; const struct doveadm_mail_cmd *mail_cmd; struct doveadm_mail_cmd_context *mctx; const struct doveadm_cmd_ver2 *cmd_ver2; if ((cmd_ver2 = doveadm_cmd_find_with_args_ver2(cmd_name, &argc, &argv)) == NULL) { mail_cmd = doveadm_mail_cmd_find(cmd_name); if (mail_cmd == NULL) { cmd = doveadm_cmd_find_with_args(cmd_name, &argc, &argv); if (cmd == NULL) { i_error("doveadm: Client sent unknown command: %s", cmd_name); return -1; } } else { if (doveadm_mail_cmd_server_parse(mail_cmd, conn->set, argc, argv, cctx, &mctx) < 0) return -1; } } else { cctx->cmd = cmd_ver2; } /* some commands will want to call io_loop_run(), but we're already running one and we can't call the original one recursively, so create a new ioloop. */ ioloop = io_loop_create(); lib_signals_reset_ioloop(); if (cmd_ver2 != NULL) doveadm_cmd_server_run_ver2(conn, argc, argv, cctx); else if (cmd != NULL) doveadm_cmd_server_run(conn, argc, argv, cmd); else doveadm_mail_cmd_server_run(conn, mctx, cctx); io_loop_set_current(prev_ioloop); lib_signals_reset_ioloop(); o_stream_switch_ioloop(conn->output); if (conn->log_out != NULL) o_stream_switch_ioloop(conn->log_out); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); /* clear all headers */ doveadm_print_deinit(); doveadm_print_init(DOVEADM_PRINT_TYPE_SERVER); return doveadm_exit_code == 0 ? 0 : -1; } static bool client_handle_command(struct client_connection *conn, char **args) { struct doveadm_cmd_context cctx; const char *flags, *cmd_name; unsigned int argc; for (argc = 0; args[argc] != NULL; argc++) args[argc] = str_tabunescape(args[argc]); if (argc < 3) { i_error("doveadm client: No command given"); return FALSE; } i_zero(&cctx); cctx.cli = FALSE; cctx.tcp_server = TRUE; cctx.local_ip = conn->local_ip; cctx.remote_ip = conn->remote_ip; cctx.local_port = conn->local_port; cctx.remote_port = conn->remote_port; cctx.conn = conn; doveadm_exit_code = 0; flags = args[0]; cctx.username = args[1]; cmd_name = args[2]; doveadm_debug = FALSE; doveadm_verbose = FALSE; for (; *flags != '\0'; flags++) { switch (*flags) { case 'D': doveadm_debug = TRUE; doveadm_verbose = TRUE; break; case 'v': doveadm_verbose = TRUE; break; default: i_error("doveadm client: Unknown flag: %c", *flags); return FALSE; } } if (!doveadm_client_is_allowed_command(conn->set, cmd_name)) { i_error("doveadm client isn't allowed to use command: %s", cmd_name); return FALSE; } client_connection_set_proctitle(conn, cmd_name); o_stream_cork(conn->output); if (doveadm_cmd_handle(conn, cmd_name, argc-2, (const char**)(args+2), &cctx) < 0) o_stream_nsend(conn->output, "\n-\n", 3); o_stream_uncork(conn->output); client_connection_set_proctitle(conn, ""); /* flush the output and possibly run next command */ net_set_nonblock(conn->fd, FALSE); (void)o_stream_flush(conn->output); net_set_nonblock(conn->fd, TRUE); return TRUE; } static int client_connection_authenticate(struct client_connection *conn) { const char *line, *pass; buffer_t *plain; const unsigned char *data; size_t size; if ((line = i_stream_read_next_line(conn->input)) == NULL) { if (conn->input->eof) return -1; return 0; } if (*conn->set->doveadm_password == '\0') { i_error("doveadm_password not set, " "remote authentication disabled"); return -1; } /* FIXME: some day we should probably let auth process do this and support all kinds of authentication */ if (strncmp(line, "PLAIN\t", 6) != 0) { i_error("doveadm client attempted non-PLAIN authentication: %s", line); return -1; } plain = buffer_create_dynamic(pool_datastack_create(), 128); if (base64_decode(line + 6, strlen(line + 6), NULL, plain) < 0) { i_error("doveadm client sent invalid base64 auth PLAIN data"); return -1; } data = plain->data; size = plain->used; if (size < 10 || data[0] != '\0' || memcmp(data+1, "doveadm", 7) != 0 || data[8] != '\0') { i_error("doveadm client didn't authenticate as 'doveadm'"); return -1; } pass = t_strndup(data + 9, size - 9); if (strlen(pass) != strlen(conn->set->doveadm_password) || !mem_equals_timing_safe(pass, conn->set->doveadm_password, strlen(pass))) { i_error("doveadm client authenticated with wrong password"); return -1; } return 1; } static void client_log_disconnect_error(struct client_connection *conn) { const char *error; error = conn->ssl_iostream == NULL ? NULL : ssl_iostream_get_last_error(conn->ssl_iostream); if (error == NULL) { error = conn->input->stream_errno == 0 ? "EOF" : strerror(conn->input->stream_errno); } i_error("doveadm client disconnected before handshake: %s", error); } static void client_connection_input(struct client_connection *conn) { const char *line; bool ok = TRUE; int ret; unsigned int minor; if (!conn->handshaked) { if ((line = i_stream_read_next_line(conn->input)) == NULL) { if (conn->input->eof || conn->input->stream_errno != 0) { client_log_disconnect_error(conn); client_connection_destroy(&conn); } return; } if (!version_string_verify_full(line, "doveadm-server", DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR, &minor)) { i_error("doveadm client not compatible with this server " "(mixed old and new binaries?)"); client_connection_destroy(&conn); return; } if (minor > 0) { /* send version reply */ o_stream_nsend_str(conn->output, DOVEADM_CLIENT_PROTOCOL_VERSION_LINE"\n"); conn->use_multiplex = TRUE; } conn->handshaked = TRUE; } if (!conn->authenticated) { if ((ret = client_connection_authenticate(conn)) <= 0) { if (ret < 0) { o_stream_nsend(conn->output, "-\n", 2); client_connection_destroy(&conn); } return; } o_stream_nsend(conn->output, "+\n", 2); conn->authenticated = TRUE; } if (!conn->io_setup) { conn->io_setup = TRUE; if (conn->use_multiplex) { struct ostream *os = conn->output; conn->output = o_stream_create_multiplex(os, (size_t)-1); o_stream_set_name(conn->output, o_stream_get_name(os)); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_unref(&os); conn->log_out = o_stream_multiplex_add_channel(conn->output, DOVEADM_LOG_CHANNEL_ID); o_stream_set_no_error_handling(conn->log_out, TRUE); o_stream_set_name(conn->log_out, t_strdup_printf("%s (log)", o_stream_get_name(conn->output))); doveadm_server_capture_logs(); } doveadm_print_ostream = conn->output; } while (ok && !conn->input->closed && (line = i_stream_read_next_line(conn->input)) != NULL) { T_BEGIN { char **args; args = p_strsplit(pool_datastack_create(), line, "\t"); ok = client_handle_command(conn, args); } T_END; } if (conn->input->eof || conn->input->stream_errno != 0 || !ok) client_connection_destroy(&conn); } static int client_connection_read_settings(struct client_connection *conn) { const struct setting_parser_info *set_roots[] = { &doveadm_setting_parser_info, NULL }; struct master_service_settings_input input; struct master_service_settings_output output; const char *error; void *set; i_zero(&input); input.roots = set_roots; input.service = "doveadm"; input.local_ip = conn->local_ip; input.remote_ip = conn->remote_ip; if (master_service_settings_read(master_service, &input, &output, &error) < 0) { i_error("Error reading configuration: %s", error); return -1; } set = master_service_settings_get_others(master_service)[0]; conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool); return 0; } static int client_connection_init_ssl(struct client_connection *conn) { const char *error; if (master_service_ssl_init(master_service, &conn->input, &conn->output, &conn->ssl_iostream, &error) < 0) { i_error("SSL init failed: %s", error); return -1; } if (ssl_iostream_handshake(conn->ssl_iostream) < 0) { i_error("SSL handshake failed: %s", ssl_iostream_get_last_error(conn->ssl_iostream)); return -1; } return 0; } static void client_connection_send_auth_handshake(struct client_connection * conn, int listen_fd) { const char *listen_path; struct stat st; /* we'll have to do this with stat(), because at least in Linux fstat() always returns mode as 0777 */ if (net_getunixname(listen_fd, &listen_path) == 0 && stat(listen_path, &st) == 0 && S_ISSOCK(st.st_mode) && (st.st_mode & 0777) == 0600) { /* no need for client to authenticate */ conn->authenticated = TRUE; o_stream_nsend(conn->output, "+\n", 2); } else { o_stream_nsend(conn->output, "-\n", 2); } } int client_connection_init(struct client_connection *conn, int fd) { const char *ip; conn->fd = fd; (void)net_getsockname(fd, &conn->local_ip, &conn->local_port); (void)net_getpeername(fd, &conn->remote_ip, &conn->remote_port); ip = net_ip2addr(&conn->remote_ip); if (ip[0] != '\0') i_set_failure_prefix("doveadm(%s): ", ip); if (client_connection_read_settings(conn) < 0) { client_connection_destroy(&conn); return -1; } return 0; } struct client_connection * client_connection_create(int fd, int listen_fd, bool ssl) { struct client_connection *conn; pool_t pool; pool = pool_alloconly_create("doveadm client", 1024*16); conn = p_new(pool, struct client_connection, 1); conn->pool = pool; if (client_connection_init(conn, fd) < 0) return NULL; doveadm_print_init(DOVEADM_PRINT_TYPE_SERVER); conn->name = conn->remote_ip.family == 0 ? "" : p_strdup(pool, net_ip2addr(&conn->remote_ip)); conn->io = io_add(fd, IO_READ, client_connection_input, conn); conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_set_name(conn->input, conn->name); o_stream_set_name(conn->output, conn->name); o_stream_set_no_error_handling(conn->output, TRUE); if (ssl) { if (client_connection_init_ssl(conn) < 0) { client_connection_destroy(&conn); return NULL; } } client_connection_send_auth_handshake(conn, listen_fd); client_connection_set_proctitle(conn, ""); return conn; } void client_connection_destroy(struct client_connection **_conn) { struct client_connection *conn = *_conn; *_conn = NULL; doveadm_print_deinit(); if (conn->ssl_iostream != NULL) ssl_iostream_destroy(&conn->ssl_iostream); if (conn->output != NULL) o_stream_destroy(&conn->output); if (conn->io != NULL) { io_remove(&conn->io); } if (conn->input != NULL) { i_stream_destroy(&conn->input); } if (conn->log_out != NULL) { doveadm_server_restore_logs(); o_stream_unref(&conn->log_out); } if (conn->fd > 0 && close(conn->fd) < 0) i_error("close(client) failed: %m"); pool_unref(&conn->pool); doveadm_print_ostream = NULL; doveadm_client = NULL; master_service_client_connection_destroyed(master_service); if (doveadm_verbose_proctitle) process_title_set("[idling]"); } void client_connection_set_proctitle(struct client_connection *conn, const char *text) { const char *str; if (!doveadm_verbose_proctitle) return; if (text[0] == '\0') str = t_strdup_printf("[%s]", conn->name); else str = t_strdup_printf("[%s %s]", conn->name, text); process_title_set(str); } dovecot-2.2.33.2/src/doveadm/doveadm-proxy.c0000644000175000017500000001176513144653606015517 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "strescape.h" #include "ipc-client.h" #include "doveadm.h" #include "doveadm-print.h" #include #include struct proxy_context { struct ipc_client *ipc; const char *username_field; }; extern struct doveadm_cmd_ver2 doveadm_cmd_proxy[]; static void proxy_cmd_help(doveadm_command_t *cmd) ATTR_NORETURN; static struct proxy_context * cmd_proxy_init(int argc, char *argv[], const char *getopt_args, doveadm_command_t *cmd) { struct proxy_context *ctx; const char *socket_path; int c; ctx = t_new(struct proxy_context, 1); socket_path = t_strconcat(doveadm_settings->base_dir, "/ipc", NULL); while ((c = getopt(argc, argv, getopt_args)) > 0) { switch (c) { case 'a': socket_path = optarg; break; case 'f': ctx->username_field = optarg; break; default: proxy_cmd_help(cmd); } } ctx->ipc = ipc_client_init(socket_path); return ctx; } static void cmd_proxy_list_header(const char *const *args) { struct { const char *key; const char *title; } header_map[] = { { "service", "proto" }, { "src-ip", "src ip" }, { "dest-ip", "dest ip" }, { "dest-port", "port" }, }; for (unsigned int i = 0; args[i] != NULL; i++) { const char *arg = args[i]; if (strcmp(arg, "username") == 0 || strncmp(arg, "user_", 5) == 0) { doveadm_print_header(arg, arg, DOVEADM_PRINT_HEADER_FLAG_EXPAND); continue; } const char *title = arg; for (unsigned int j = 0; j < N_ELEMENTS(header_map); j++) { if (strcmp(header_map[j].key, arg) == 0) { title = header_map[j].title; break; } } doveadm_print_header(arg, title, 0); } } static void cmd_proxy_list_callback(enum ipc_client_cmd_state state, const char *data, void *context) { bool *seen_header = context; switch (state) { case IPC_CLIENT_CMD_STATE_REPLY: { const char *const *args = t_strsplit_tabescaped(data); if (!*seen_header) { cmd_proxy_list_header(args); *seen_header = TRUE; } else { for (; *args != NULL; args++) doveadm_print(*args); } return; } case IPC_CLIENT_CMD_STATE_OK: break; case IPC_CLIENT_CMD_STATE_ERROR: i_error("LIST-FULL failed: %s", data); break; } io_loop_stop(current_ioloop); } static void cmd_proxy_list(int argc, char *argv[]) { struct proxy_context *ctx; bool seen_header = FALSE; ctx = cmd_proxy_init(argc, argv, "a:", cmd_proxy_list); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); io_loop_set_running(current_ioloop); ipc_client_cmd(ctx->ipc, "proxy\t*\tLIST-FULL", cmd_proxy_list_callback, &seen_header); if (io_loop_is_running(current_ioloop)) io_loop_run(current_ioloop); ipc_client_deinit(&ctx->ipc); } static void cmd_proxy_kick_callback(enum ipc_client_cmd_state state, const char *data, void *context ATTR_UNUSED) { switch (state) { case IPC_CLIENT_CMD_STATE_REPLY: return; case IPC_CLIENT_CMD_STATE_OK: if (data[0] == '\0') data = "0"; doveadm_print(data); break; case IPC_CLIENT_CMD_STATE_ERROR: i_error("KICK failed: %s", data); doveadm_exit_code = EX_TEMPFAIL; break; } io_loop_stop(current_ioloop); } static void cmd_proxy_kick(int argc, char *argv[]) { struct proxy_context *ctx; string_t *cmd; ctx = cmd_proxy_init(argc, argv, "a:f:", cmd_proxy_kick); if (argv[optind] == NULL) { proxy_cmd_help(cmd_proxy_kick); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); doveadm_print_formatted_set_format("%{count} connections kicked\n"); doveadm_print_header_simple("count"); cmd = t_str_new(128); str_append(cmd, "proxy\t*\t"); if (ctx->username_field == NULL) str_append(cmd, "KICK"); else { str_append(cmd, "KICK-ALT\t"); str_append_tabescaped(cmd, ctx->username_field); } for (; argv[optind] != NULL; optind++) { str_append_c(cmd, '\t'); str_append_tabescaped(cmd, argv[optind]); } ipc_client_cmd(ctx->ipc, str_c(cmd), cmd_proxy_kick_callback, NULL); io_loop_run(current_ioloop); ipc_client_deinit(&ctx->ipc); } struct doveadm_cmd_ver2 doveadm_cmd_proxy[] = { { .name = "proxy list", .usage = "[-a ]", .old_cmd = cmd_proxy_list, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }, { .name = "proxy kick", .usage = "[-a ] [-f ] [...]", .old_cmd = cmd_proxy_kick, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('f', "passdb-field", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END } }; static void proxy_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_proxy); i++) { if (doveadm_cmd_proxy[i].old_cmd == cmd) help_ver2(&doveadm_cmd_proxy[i]); } i_unreached(); } void doveadm_register_proxy_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_proxy); i++) doveadm_cmd_register_ver2(&doveadm_cmd_proxy[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-mail-fetch.c0000644000175000017500000004267213165463624016352 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "message-address.h" #include "message-size.h" #include "message-parser.h" #include "message-header-decode.h" #include "message-decoder.h" #include "imap-util.h" #include "mail-user.h" #include "mail-storage.h" #include "mail-search.h" #include "mail-namespace.h" #include "imap-msgpart.h" #include "doveadm-print.h" #include "doveadm-mail.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include struct fetch_cmd_context { struct doveadm_mail_cmd_context ctx; struct mail *mail; ARRAY(struct fetch_field) fields; ARRAY_TYPE(const_string) header_fields; enum mail_fetch_field wanted_fields; const struct fetch_field *cur_field; /* if print() returns -1, log this error if non-NULL. otherwise log the storage error. */ const char *print_error; }; struct fetch_field { const char *name; enum mail_fetch_field wanted_fields; int (*print)(struct fetch_cmd_context *ctx); }; static int fetch_user(struct fetch_cmd_context *ctx) { doveadm_print(ctx->ctx.cur_mail_user->username); return 0; } static int fetch_mailbox(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_mailbox_guid(struct fetch_cmd_context *ctx) { struct mailbox_metadata metadata; if (mailbox_get_metadata(ctx->mail->box, MAILBOX_METADATA_GUID, &metadata) < 0) return -1; doveadm_print(guid_128_to_string(metadata.guid)); return 0; } static int fetch_seq(struct fetch_cmd_context *ctx) { doveadm_print_num(ctx->mail->seq); return 0; } static int fetch_uid(struct fetch_cmd_context *ctx) { doveadm_print_num(ctx->mail->uid); return 0; } static int fetch_guid(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_flags(struct fetch_cmd_context *ctx) { string_t *str = t_str_new(64); imap_write_flags(str, mail_get_flags(ctx->mail), mail_get_keywords(ctx->mail)); doveadm_print(str_c(str)); return 0; } static int fetch_modseq(struct fetch_cmd_context *ctx) { doveadm_print_num(mail_get_modseq(ctx->mail)); return 0; } static void fetch_set_istream_error(struct fetch_cmd_context *ctx, struct istream *input) { ctx->print_error = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); } static int fetch_hdr(struct fetch_cmd_context *ctx) { struct istream *input; struct message_size hdr_size; int ret; if (mail_get_hdr_stream(ctx->mail, &hdr_size, &input) < 0) return -1; input = i_stream_create_limit(input, hdr_size.physical_size); if ((ret = doveadm_print_istream(input) < 0)) fetch_set_istream_error(ctx, input); i_stream_unref(&input); return ret; } static int fetch_hdr_field(struct fetch_cmd_context *ctx) { const char *const *value, *filter, *name = ctx->cur_field->name; string_t *str = t_str_new(256); bool add_lf = FALSE; filter = strchr(name, '.'); if (filter != NULL) name = t_strdup_until(name, filter++); if (filter != NULL && strcmp(filter, "utf8") == 0) { if (mail_get_headers_utf8(ctx->mail, name, &value) < 0) return -1; } else { if (mail_get_headers(ctx->mail, name, &value) < 0) return -1; } for (; *value != NULL; value++) { if (add_lf) str_append_c(str, '\n'); str_append(str, *value); add_lf = TRUE; } if (filter == NULL || strcmp(filter, "utf8") == 0) { /* print the header as-is */ } else if (strcmp(filter, "address") == 0 || strcmp(filter, "address_name") == 0 || strcmp(filter, "address_name.utf8") == 0) { struct message_address *addr; addr = message_address_parse(pool_datastack_create(), str_data(str), str_len(str), UINT_MAX, FALSE); str_truncate(str, 0); add_lf = FALSE; for (; addr != NULL; addr = addr->next) { if (add_lf) str_append_c(str, '\n'); if (strcmp(filter, "address") == 0) { if (addr->mailbox != NULL) str_append(str, addr->mailbox); if (addr->domain != NULL) { str_append_c(str, '@'); str_append(str, addr->domain); } } else if (addr->name != NULL) { if (strcmp(filter, "address_name") == 0) str_append(str, addr->name); else { message_header_decode_utf8( (const void *)addr->name, strlen(addr->name), str, NULL); } } add_lf = TRUE; } } else { i_fatal("Unknown header filter: %s", filter); } doveadm_print(str_c(str)); return 0; } static int fetch_body_field(struct fetch_cmd_context *ctx) { const char *name = ctx->cur_field->name; struct imap_msgpart *msgpart; struct imap_msgpart_open_result result; bool binary; int ret; binary = strncmp(name, "binary.", 7) == 0; name += binary ? 7 : 5; if (imap_msgpart_parse(name, &msgpart) < 0) i_unreached(); /* we already verified this was ok */ if (binary) imap_msgpart_set_decode_to_binary(msgpart); if (imap_msgpart_open(ctx->mail, msgpart, &result) < 0) { imap_msgpart_free(&msgpart); return -1; } if ((ret = doveadm_print_istream(result.input) < 0)) fetch_set_istream_error(ctx, result.input); i_stream_unref(&result.input); imap_msgpart_free(&msgpart); return ret; } static int fetch_body(struct fetch_cmd_context *ctx) { struct istream *input; struct message_size hdr_size; int ret; if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0) return -1; i_stream_skip(input, hdr_size.physical_size); if ((ret = doveadm_print_istream(input) < 0)) fetch_set_istream_error(ctx, input); return ret; } static int fetch_body_snippet(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_BODY_SNIPPET, &value) < 0) return -1; /* [0] contains the snippet algorithm, skip over it */ i_assert(value[0] != '\0'); doveadm_print(value + 1); return 0; } static int fetch_text(struct fetch_cmd_context *ctx) { struct istream *input; int ret; if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0) return -1; if ((ret = doveadm_print_istream(input) < 0)) fetch_set_istream_error(ctx, input); return ret; } static int fetch_text_utf8(struct fetch_cmd_context *ctx) { struct istream *input; struct message_parser_ctx *parser; struct message_decoder_context *decoder; struct message_block raw_block, block; struct message_part *parts; int ret = 0; if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0) return -1; parser = message_parser_init(pool_datastack_create(), input, MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE, 0); decoder = message_decoder_init(NULL, 0); while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) { if (!message_decoder_decode_next_block(decoder, &raw_block, &block)) continue; if (block.hdr == NULL) { if (block.size > 0) doveadm_print_stream(block.data, block.size); } else if (block.hdr->eoh) doveadm_print_stream("\n", 1); else { i_assert(block.hdr->name_len > 0); doveadm_print_stream(block.hdr->name, block.hdr->name_len); doveadm_print_stream(": ", 2); if (block.hdr->full_value_len > 0) { doveadm_print_stream(block.hdr->full_value, block.hdr->full_value_len); } doveadm_print_stream("\n", 1); } } i_assert(ret != 0); message_decoder_deinit(&decoder); (void)message_parser_deinit(&parser, &parts); doveadm_print_stream("", 0); if (input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return -1; } return 0; } static int fetch_size_physical(struct fetch_cmd_context *ctx) { uoff_t size; if (mail_get_physical_size(ctx->mail, &size) < 0) return -1; doveadm_print_num(size); return 0; } static int fetch_size_virtual(struct fetch_cmd_context *ctx) { uoff_t size; if (mail_get_virtual_size(ctx->mail, &size) < 0) return -1; doveadm_print_num(size); return 0; } static int fetch_date_received(struct fetch_cmd_context *ctx) { time_t t; if (mail_get_received_date(ctx->mail, &t) < 0) return -1; doveadm_print(unixdate2str(t)); return 0; } static int fetch_date_sent(struct fetch_cmd_context *ctx) { time_t t; int tz; char chr; if (mail_get_date(ctx->mail, &t, &tz) < 0) return -1; chr = tz < 0 ? '-' : '+'; if (tz < 0) tz = -tz; doveadm_print(t_strdup_printf("%s (%c%02u%02u)", unixdate2str(t), chr, tz/60, tz%60)); return 0; } static int fetch_date_saved(struct fetch_cmd_context *ctx) { time_t t; if (mail_get_save_date(ctx->mail, &t) < 0) return -1; doveadm_print(unixdate2str(t)); return 0; } static int fetch_date_received_unixtime(struct fetch_cmd_context *ctx) { time_t t; if (mail_get_received_date(ctx->mail, &t) < 0) return -1; doveadm_print(dec2str(t)); return 0; } static int fetch_date_sent_unixtime(struct fetch_cmd_context *ctx) { time_t t; int tz; if (mail_get_date(ctx->mail, &t, &tz) < 0) return -1; doveadm_print(dec2str(t)); return 0; } static int fetch_date_saved_unixtime(struct fetch_cmd_context *ctx) { time_t t; if (mail_get_save_date(ctx->mail, &t) < 0) return -1; doveadm_print(dec2str(t)); return 0; } static int fetch_imap_envelope(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_ENVELOPE, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_imap_body(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODY, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_imap_bodystructure(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_pop3_uidl(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_BACKEND, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_pop3_order(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_POP3_ORDER, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_refcount(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_REFCOUNT, &value) < 0) return -1; doveadm_print(value); return 0; } static int fetch_storageid(struct fetch_cmd_context *ctx) { const char *value; if (mail_get_special(ctx->mail, MAIL_FETCH_STORAGE_ID, &value) < 0) return -1; doveadm_print(value); return 0; } static const struct fetch_field fetch_fields[] = { { "user", 0, fetch_user }, { "mailbox", 0, fetch_mailbox }, { "mailbox-guid", 0, fetch_mailbox_guid }, { "seq", 0, fetch_seq }, { "uid", 0, fetch_uid }, { "guid", 0, fetch_guid }, { "flags", MAIL_FETCH_FLAGS, fetch_flags }, { "modseq", 0, fetch_modseq }, { "hdr", MAIL_FETCH_STREAM_HEADER, fetch_hdr }, { "body", MAIL_FETCH_STREAM_BODY, fetch_body }, { "body.snippet", MAIL_FETCH_BODY_SNIPPET, fetch_body_snippet }, { "text", MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, fetch_text }, { "text.utf8", MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, fetch_text_utf8 }, { "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical }, { "size.virtual", MAIL_FETCH_VIRTUAL_SIZE, fetch_size_virtual }, { "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received }, { "date.sent", MAIL_FETCH_DATE, fetch_date_sent }, { "date.saved", MAIL_FETCH_SAVE_DATE, fetch_date_saved }, { "date.received.unixtime", MAIL_FETCH_RECEIVED_DATE, fetch_date_received_unixtime }, { "date.sent.unixtime", MAIL_FETCH_DATE, fetch_date_sent_unixtime }, { "date.saved.unixtime", MAIL_FETCH_SAVE_DATE, fetch_date_saved_unixtime }, { "imap.envelope", MAIL_FETCH_IMAP_ENVELOPE, fetch_imap_envelope }, { "imap.body", MAIL_FETCH_IMAP_BODY, fetch_imap_body }, { "imap.bodystructure", MAIL_FETCH_IMAP_BODYSTRUCTURE, fetch_imap_bodystructure }, { "pop3.uidl", MAIL_FETCH_UIDL_BACKEND, fetch_pop3_uidl }, { "pop3.order", MAIL_FETCH_POP3_ORDER, fetch_pop3_order }, { "refcount", MAIL_FETCH_REFCOUNT, fetch_refcount }, { "storageid", MAIL_FETCH_STORAGE_ID, fetch_storageid } }; static const struct fetch_field *fetch_field_find(const char *name) { unsigned int i; for (i = 0; i < N_ELEMENTS(fetch_fields); i++) { if (strcmp(fetch_fields[i].name, name) == 0) return &fetch_fields[i]; } return NULL; } static void print_fetch_fields(void) { unsigned int i; fprintf(stderr, "Available fetch fields: hdr. body.
binary.
%s", fetch_fields[0].name); for (i = 1; i < N_ELEMENTS(fetch_fields); i++) fprintf(stderr, " %s", fetch_fields[i].name); fprintf(stderr, "\n"); } static void parse_fetch_fields(struct fetch_cmd_context *ctx, const char *str) { const char *const *fields, *name; const struct fetch_field *field; struct fetch_field hdr_field, body_field; struct imap_msgpart *msgpart; i_zero(&hdr_field); hdr_field.print = fetch_hdr_field; i_zero(&body_field); body_field.print = fetch_body_field; t_array_init(&ctx->fields, 32); t_array_init(&ctx->header_fields, 32); fields = t_strsplit_spaces(str, " "); for (; *fields != NULL; fields++) { name = t_str_lcase(*fields); doveadm_print_header_simple(name); if ((field = fetch_field_find(name)) != NULL) { ctx->wanted_fields |= field->wanted_fields; array_append(&ctx->fields, field, 1); } else if (strncmp(name, "hdr.", 4) == 0) { name += 4; hdr_field.name = name; array_append(&ctx->fields, &hdr_field, 1); name = t_strcut(name, '.'); array_append(&ctx->header_fields, &name, 1); } else if (strncmp(name, "body.", 5) == 0 || strncmp(name, "binary.", 7) == 0) { bool binary = strncmp(name, "binary.", 7) == 0; body_field.name = t_strarray_join(t_strsplit(name, ","), " "); name += binary ? 7 : 5; if (imap_msgpart_parse(name, &msgpart) < 0) { print_fetch_fields(); i_fatal("Unknown fetch section: %s", name); } array_append(&ctx->fields, &body_field, 1); ctx->wanted_fields |= imap_msgpart_get_fetch_data(msgpart); imap_msgpart_free(&msgpart); } else { print_fetch_fields(); i_fatal("Unknown fetch field: %s", name); } } array_append_zero(&ctx->header_fields); } static int cmd_fetch_mail(struct fetch_cmd_context *ctx) { const struct fetch_field *field; struct mail *mail = ctx->mail; int ret = 0; array_foreach(&ctx->fields, field) { ctx->cur_field = field; if (field->print(ctx) < 0) { i_error("fetch(%s) failed for box=%s uid=%u: %s", field->name, mailbox_get_vname(mail->box), mail->uid, ctx->print_error != NULL ? ctx->print_error : mailbox_get_last_internal_error(mail->box, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, mail->box); ctx->print_error = NULL; ret = -1; } } return ret; } static int cmd_fetch_box(struct fetch_cmd_context *ctx, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; int ret = 0; if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, ctx->wanted_fields, array_idx(&ctx->header_fields, 0), FALSE, &iter) < 0) return -1; while (doveadm_mail_iter_next(iter, &ctx->mail)) { T_BEGIN { if (cmd_fetch_mail(ctx) < 0) ret = -1; } T_END; } if (doveadm_mail_iter_deinit(&iter) < 0) ret = -1; return ret; } static int cmd_fetch_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(_ctx, user, _ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_fetch_box(ctx, info) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_fetch_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx; const char *fetch_fields = args[0]; if (fetch_fields == NULL || args[1] == NULL) doveadm_mail_help_name("fetch"); parse_fetch_fields(ctx, fetch_fields); _ctx->search_args = doveadm_mail_build_search_args(args + 1); } static struct doveadm_mail_cmd_context *cmd_fetch_alloc(void) { struct fetch_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct fetch_cmd_context); ctx->ctx.v.init = cmd_fetch_init; ctx->ctx.v.run = cmd_fetch_run; doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_fetch_ver2 = { .name = "fetch", .mail_cmd = cmd_fetch_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX" ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "field", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('\0', "fieldstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL | CMD_PARAM_FLAG_DO_NOT_EXPOSE) /* FIXME: horrible hack, remove me when possible */ DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/client-connection-http.c0000644000175000017500000006501213165463624017305 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "compat.h" #include "lib-signals.h" #include "base64.h" #include "ioloop.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "settings-parser.h" #include "iostream-ssl.h" #include "iostream-temp.h" #include "istream-seekable.h" #include "master-service.h" #include "master-service-ssl.h" #include "master-service-settings.h" #include "mail-storage-service.h" #include "http-server.h" #include "http-request.h" #include "http-response.h" #include "http-url.h" #include "doveadm-util.h" #include "doveadm-server.h" #include "doveadm-mail.h" #include "doveadm-print.h" #include "doveadm-settings.h" #include "client-connection-private.h" #include "json-parser.h" #include #include struct client_connection_http { struct client_connection client; struct http_server_connection *http_client; struct http_server_request *http_server_request; const struct http_request *http_request; struct http_server_response *http_response; struct json_parser *json_parser; const struct doveadm_cmd_ver2 *cmd; struct doveadm_cmd_param *cmd_param; struct ioloop *ioloop; ARRAY_TYPE(doveadm_cmd_param_arr_t) pargv; int method_err; char *method_id; bool first_row; bool value_is_array; enum { JSON_STATE_INIT, JSON_STATE_COMMAND, JSON_STATE_COMMAND_NAME, JSON_STATE_COMMAND_PARAMETERS, JSON_STATE_COMMAND_PARAMETER_KEY, JSON_STATE_COMMAND_PARAMETER_VALUE, JSON_STATE_COMMAND_PARAMETER_VALUE_ARRAY, JSON_STATE_COMMAND_PARAMETER_VALUE_ISTREAM_CONSUME, JSON_STATE_COMMAND_ID, JSON_STATE_COMMAND_DONE, JSON_STATE_DONE } json_state; }; typedef void doveadm_server_handler_t(struct client_connection_http *ctx); struct doveadm_http_server_mount { const char *verb; const char *path; doveadm_server_handler_t *handler; bool auth; }; static struct http_server *doveadm_http_server; static void doveadm_http_server_handle_request(void *context, struct http_server_request *req); static void doveadm_http_server_connection_destroy(void *context, const char *reason); static const struct http_server_callbacks doveadm_http_callbacks = { .connection_destroy = doveadm_http_server_connection_destroy, .handle_request = doveadm_http_server_handle_request }; static void doveadm_http_server_options_handler(struct client_connection_http *); static void doveadm_http_server_print_mounts(struct client_connection_http *); static void doveadm_http_server_send_api_v1(struct client_connection_http *); static void doveadm_http_server_read_request_v1(struct client_connection_http *); static struct doveadm_http_server_mount doveadm_http_server_mounts[] = { { .verb = "OPTIONS", .path = NULL, .handler = doveadm_http_server_options_handler, .auth = FALSE }, { .verb = "GET", .path = "/", .handler = doveadm_http_server_print_mounts, .auth = TRUE }, { .verb = "GET", .path = "/doveadm/v1", .handler = doveadm_http_server_send_api_v1, .auth = TRUE }, { .verb = "POST", .path = "/doveadm/v1", .handler = doveadm_http_server_read_request_v1, .auth = TRUE } }; static void doveadm_http_server_send_response(void *context); struct client_connection * client_connection_create_http(int fd, bool ssl) { struct client_connection_http *conn; pool_t pool; pool = pool_alloconly_create("doveadm client", 1024*16); conn = p_new(pool, struct client_connection_http, 1); conn->client.pool = pool; if (client_connection_init(&conn->client, fd) < 0) return NULL; conn->http_client = http_server_connection_create(doveadm_http_server, fd, fd, ssl, &doveadm_http_callbacks, conn); conn->client.fd = -1; return &conn->client; } static void doveadm_http_server_connection_destroy(void *context, const char *reason ATTR_UNUSED) { struct client_connection *conn = context; client_connection_destroy(&conn); } static void doveadm_http_server_request_destroy(void *context) { struct client_connection_http *conn = context; struct http_server_response *resp = http_server_request_get_response(conn->http_server_request); if (resp != NULL) { const char *agent; const char *url; int status; const char *reason; uoff_t size; http_server_response_get_status(resp, &status, &reason); size = http_server_response_get_total_size(resp); agent = http_request_header_get(conn->http_request, "User-Agent"); if (agent == NULL) agent = ""; url = http_url_create(conn->http_request->target.url); i_info("doveadm: %s %s %s \"%s %s HTTP/%d.%d\" %d %"PRIuUOFF_T" \"%s\" \"%s\"", net_ip2addr(&conn->client.remote_ip), "-", "-", conn->http_request->method, conn->http_request->target.url->path, conn->http_request->version_major, conn->http_request->version_minor, status, size, url, agent); } if (conn->json_parser != NULL) { const char *error ATTR_UNUSED; (void)json_parser_deinit(&conn->json_parser, &error); // we've already failed, ignore error } if (conn->client.output != NULL) o_stream_set_no_error_handling(conn->client.output, TRUE); http_server_request_unref(&(conn->http_server_request)); http_server_switch_ioloop(doveadm_http_server); http_server_connection_unref(&(conn->http_client)); } static void doveadm_http_server_json_error(void *context, const char *error) { struct client_connection_http *conn = context; string_t *escaped; o_stream_nsend_str(conn->client.output,"[\"error\",{\"type\":\""); escaped = str_new(conn->client.pool, 10); json_append_escaped(escaped, error); o_stream_nsend_str(conn->client.output,str_c(escaped)); o_stream_nsend_str(conn->client.output,"\", \"exitCode\":"); str_truncate(escaped,0); str_printfa(escaped,"%d", doveadm_exit_code); o_stream_nsend_str(conn->client.output, str_c(escaped)); o_stream_nsend_str(conn->client.output,"},\""); str_truncate(escaped,0); if (conn->method_id != NULL) { json_append_escaped(escaped, conn->method_id); o_stream_nsend_str(conn->client.output, str_c(escaped)); } o_stream_nsend_str(conn->client.output,"\"]"); } static void doveadm_http_server_json_success(void *context, struct istream *result) { struct client_connection_http *conn = context; string_t *escaped; escaped = str_new(conn->client.pool, 10); o_stream_nsend_str(conn->client.output,"[\"doveadmResponse\","); if (o_stream_send_istream(conn->client.output, result) < 0) { if (conn->client.output->stream_errno != 0) { i_fatal("write(%s) failed: %s", o_stream_get_name(conn->client.output), o_stream_get_error(conn->client.output)); } else if (result->stream_errno != 0) { i_fatal("read(%s) failed: %s", i_stream_get_name(result), i_stream_get_error(result)); } else i_unreached(); /* either it's output or input error */ } o_stream_nsend_str(conn->client.output,",\""); if (conn->method_id != NULL) { json_append_escaped(escaped, conn->method_id); o_stream_nsend_str(conn->client.output, str_c(escaped)); } o_stream_nsend_str(conn->client.output,"\"]"); } static int doveadm_http_server_istream_read(struct client_connection_http *conn) { const unsigned char *data; size_t size; while (i_stream_read_more(conn->cmd_param->value.v_istream, &data, &size) > 0) i_stream_skip(conn->cmd_param->value.v_istream, size); if (!conn->cmd_param->value.v_istream->eof) return 0; if (conn->cmd_param->value.v_istream->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(conn->cmd_param->value.v_istream), i_stream_get_error(conn->cmd_param->value.v_istream)); conn->method_err = 400; return -1; } return 1; } /** * this is to ensure we can handle arrays and other special parameter types */ static int doveadm_http_server_json_parse_next(struct client_connection_http *conn, enum json_type *type, const char **value) { int rc; const char *tmp; if (conn->json_state == JSON_STATE_COMMAND_PARAMETER_VALUE_ISTREAM_CONSUME) { rc = doveadm_http_server_istream_read(conn); if (rc != 1) return rc; conn->json_state = JSON_STATE_COMMAND_PARAMETER_KEY; } else if (conn->json_state == JSON_STATE_COMMAND_PARAMETER_VALUE_ARRAY) { /* reading through parameters in an array */ while ((rc = json_parse_next(conn->json_parser, type, value)) > 0) { if (*type == JSON_TYPE_ARRAY_END) break; if (*type != JSON_TYPE_STRING) return -2; tmp = p_strdup(conn->client.pool,*value); array_append(&conn->cmd_param->value.v_array, &tmp, 1); } if (rc <= 0) return rc; conn->json_state = JSON_STATE_COMMAND_PARAMETER_KEY; } else if (conn->json_state == JSON_STATE_COMMAND_PARAMETER_VALUE) { if (conn->cmd_param->type == CMD_PARAM_ISTREAM) { struct istream* is[2] = {0}; rc = json_parse_next_stream(conn->json_parser, &is[0]); if (rc != 1) return rc; conn->cmd_param->value.v_istream = i_stream_create_seekable_path(is, IO_BLOCK_SIZE, "/tmp/doveadm."); i_stream_unref(&is[0]); conn->cmd_param->value_set = TRUE; conn->json_state = JSON_STATE_COMMAND_PARAMETER_VALUE_ISTREAM_CONSUME; return doveadm_http_server_json_parse_next(conn, type, value); } rc = json_parse_next(conn->json_parser, type, value); if (rc != 1) return rc; if (conn->cmd_param->type == CMD_PARAM_ARRAY) { p_array_init(&conn->cmd_param->value.v_array, conn->client.pool, 1); conn->cmd_param->value_set = TRUE; if (*type == JSON_TYPE_ARRAY) { /* start of array */ conn->value_is_array = TRUE; conn->json_state = JSON_STATE_COMMAND_PARAMETER_VALUE_ARRAY; return doveadm_http_server_json_parse_next(conn, type, value); } if (*type != JSON_TYPE_STRING) { /* FIXME: should handle other than string too */ return -2; } tmp = p_strdup(conn->client.pool,*value); array_append(&conn->cmd_param->value.v_array, &tmp, 1); } else { conn->cmd_param->value_set = TRUE; switch(conn->cmd_param->type) { case CMD_PARAM_BOOL: conn->cmd_param->value.v_bool = (strcmp(*value,"true")==0); break; case CMD_PARAM_INT64: if (str_to_int64(*value, &conn->cmd_param->value.v_int64) != 0) { conn->method_err = 400; } break; case CMD_PARAM_IP: if (net_addr2ip(*value, &conn->cmd_param->value.v_ip) != 0) { conn->method_err = 400; } break; case CMD_PARAM_STR: conn->cmd_param->value.v_string = p_strdup(conn->client.pool, *value); break; default: break; } } conn->json_state = JSON_STATE_COMMAND_PARAMETER_KEY; } return json_parse_next(conn->json_parser, type, value); /* just get next */ } static void doveadm_http_server_command_execute(struct client_connection_http *conn) { struct doveadm_cmd_context cctx; /* final preflight check */ if (conn->method_err == 0 && !doveadm_client_is_allowed_command(conn->client.set, conn->cmd->name)) conn->method_err = 403; if (conn->method_err != 0) { if (conn->method_err == 404) { doveadm_http_server_json_error(conn, "unknownMethod"); } else if (conn->method_err == 403) { doveadm_http_server_json_error(conn, "unAuthorized"); } else if (conn->method_err == 400) { doveadm_http_server_json_error(conn, "invalidRequest"); } else { doveadm_http_server_json_error(conn, "internalError"); } return; } struct istream *is; const char *user; struct ioloop *ioloop,*prev_ioloop = current_ioloop; i_zero(&cctx); // create iostream doveadm_print_ostream = iostream_temp_create("/tmp/doveadm.", 0); cctx.cmd = conn->cmd; if ((cctx.cmd->flags & CMD_FLAG_NO_PRINT) == 0) doveadm_print_init(DOVEADM_PRINT_TYPE_JSON); /* then call it */ doveadm_cmd_params_null_terminate_arrays(&conn->pargv); cctx.argv = array_get(&conn->pargv, (unsigned int*)&cctx.argc); ioloop = io_loop_create(); lib_signals_reset_ioloop(); doveadm_exit_code = 0; cctx.cli = FALSE; cctx.local_ip = conn->client.local_ip; cctx.local_port = conn->client.local_port; cctx.remote_ip = conn->client.remote_ip; cctx.remote_port = conn->client.remote_port; if (doveadm_cmd_param_str(&cctx, "user", &user)) i_info("Executing command '%s' as '%s'", cctx.cmd->name, user); else i_info("Executing command '%s'", cctx.cmd->name); client_connection_set_proctitle(&conn->client, cctx.cmd->name); cctx.cmd->cmd(&cctx); client_connection_set_proctitle(&conn->client, ""); io_loop_set_current(prev_ioloop); lib_signals_reset_ioloop(); o_stream_switch_ioloop(conn->client.output); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); if ((cctx.cmd->flags & CMD_FLAG_NO_PRINT) == 0) doveadm_print_deinit(); if (o_stream_nfinish(doveadm_print_ostream)<0) { i_info("Error writing output in command %s: %s", conn->cmd->name, o_stream_get_error(conn->client.output)); doveadm_exit_code = EX_TEMPFAIL; } is = iostream_temp_finish(&doveadm_print_ostream, 4096); if (conn->first_row == TRUE) { conn->first_row = FALSE; } else { o_stream_nsend_str(conn->client.output,","); } if (doveadm_exit_code != 0) { if (doveadm_exit_code == 0 || doveadm_exit_code == EX_TEMPFAIL) i_error("Command %s failed", conn->cmd->name); doveadm_http_server_json_error(conn, "exitCode"); } else { doveadm_http_server_json_success(conn, is); } i_stream_unref(&is); } static void doveadm_http_server_read_request_v1(struct client_connection_http *conn) { enum json_type type; const char *value, *error; const struct doveadm_cmd_ver2 *ccmd; struct doveadm_cmd_param *par; int rc; bool found; if (conn->json_parser == NULL) conn->json_parser = json_parser_init_flags(conn->client.input, JSON_PARSER_NO_ROOT_OBJECT); while((rc = doveadm_http_server_json_parse_next(conn, &type, &value)) == 1) { if (conn->json_state == JSON_STATE_INIT) { if (type != JSON_TYPE_ARRAY) break; conn->json_state = JSON_STATE_COMMAND; conn->first_row = TRUE; o_stream_nsend_str(conn->client.output,"["); } else if (conn->json_state == JSON_STATE_COMMAND) { if (type == JSON_TYPE_ARRAY_END) { conn->json_state = JSON_STATE_DONE; continue; } if (type != JSON_TYPE_ARRAY) break; conn->method_err = 0; p_free_and_null(conn->client.pool, conn->method_id); conn->cmd = NULL; doveadm_cmd_params_clean(&conn->pargv); conn->json_state = JSON_STATE_COMMAND_NAME; } else if (conn->json_state == JSON_STATE_COMMAND_NAME) { if (type != JSON_TYPE_STRING) break; /* see if we can find it */ found = FALSE; array_foreach(&doveadm_cmds_ver2, ccmd) { if (i_strccdascmp(ccmd->name, value) == 0) { conn->cmd = ccmd; found = TRUE; break; } } if (!found) { json_parse_skip_next(conn->json_parser); conn->json_state = JSON_STATE_COMMAND_ID; conn->method_err = 404; } else { struct doveadm_cmd_param *param; /* initialize pargv */ for(int pargc=0;conn->cmd->parameters[pargc].name != NULL;pargc++) { param = array_append_space(&conn->pargv); memcpy(param, &(conn->cmd->parameters[pargc]), sizeof(struct doveadm_cmd_param)); param->value_set = FALSE; } conn->json_state = JSON_STATE_COMMAND_PARAMETERS; } } else if (conn->json_state == JSON_STATE_COMMAND_PARAMETERS) { if (type == JSON_TYPE_OBJECT_END) { conn->json_state = JSON_STATE_COMMAND_ID; continue; } if (type != JSON_TYPE_OBJECT) break; conn->json_state = JSON_STATE_COMMAND_PARAMETER_KEY; } else if (conn->json_state == JSON_STATE_COMMAND_PARAMETER_KEY) { if (type == JSON_TYPE_OBJECT_END) { conn->json_state = JSON_STATE_COMMAND_ID; continue; } // can happen... if (type != JSON_TYPE_OBJECT_KEY && type != JSON_TYPE_STRING) break; /* go hunting */ found = FALSE; array_foreach_modifiable(&conn->pargv, par) { if (i_strccdascmp(par->name, value) == 0) { /* it's already set, cannot have same key twice in json */ if (par->value_set) break; conn->cmd_param = par; found = TRUE; break; } } /* skip parameters if error has already occured */ if (!found || conn->method_err != 0) { json_parse_skip_next(conn->json_parser); conn->json_state = JSON_STATE_COMMAND_PARAMETER_KEY; conn->method_err = 400; } else { if (conn->cmd_param->value_set) { // FIXME: should be returned as error to client, not logged i_info("Parameter %s already set", conn->cmd_param->name); break; } conn->value_is_array = FALSE; conn->json_state = JSON_STATE_COMMAND_PARAMETER_VALUE; } } else if (conn->json_state == JSON_STATE_COMMAND_ID) { if (type != JSON_TYPE_STRING) break; conn->method_id = p_strdup(conn->client.pool, value); conn->json_state = JSON_STATE_COMMAND_DONE; } else if (conn->json_state == JSON_STATE_COMMAND_DONE) { /* should be end of array */ if (type != JSON_TYPE_ARRAY_END) break; doveadm_http_server_command_execute(conn); conn->json_state = JSON_STATE_COMMAND; } else if (conn->json_state == JSON_STATE_DONE) { // FIXME: should be returned as error to client, not logged i_info("Got unexpected elements in JSON data"); continue; } } if (!conn->client.input->eof && rc == 0) return; io_remove(&conn->client.io); doveadm_cmd_params_clean(&conn->pargv); if (rc == -2 || (rc == 1 && conn->json_state != JSON_STATE_DONE)) { /* this will happen if the parser above runs into unexpected element, but JSON is OK */ http_server_request_fail_close(conn->http_server_request, 400, "Unexpected element in input"); // FIXME: should be returned as error to client, not logged i_info("unexpected element"); return; } if (conn->client.input->stream_errno != 0) { http_server_request_fail_close(conn->http_server_request, 400, "Client disconnected"); i_info("read(client) failed: %s", i_stream_get_error(conn->client.input)); return; } if (json_parser_deinit(&conn->json_parser, &error) != 0) { // istream JSON parsing failures do not count as errors http_server_request_fail_close(conn->http_server_request, 400, "Invalid JSON input"); // FIXME: should be returned as error to client, not logged i_info("JSON parse error: %s", error); return; } o_stream_nsend_str(conn->client.output,"]"); doveadm_http_server_send_response(conn); } static void doveadm_http_server_camelcase_value(string_t *value) { size_t i,k; char *ptr = str_c_modifiable(value); for(i=0,k=0; i < strlen(ptr);) { if (ptr[i] == ' ' || ptr[i] == '-') { i++; ptr[k++] = i_toupper(ptr[i++]); } else ptr[k++] = ptr[i++]; } str_truncate(value, k); } static void doveadm_http_server_send_api_v1(struct client_connection_http *conn) { string_t *tmp = str_new(conn->client.pool, 8); size_t i,k; const struct doveadm_cmd_ver2 *cmd; const struct doveadm_cmd_param *par; bool sent; o_stream_nsend_str(conn->client.output,"[\n"); for(i = 0; i < array_count(&doveadm_cmds_ver2); i++) { cmd = array_idx(&doveadm_cmds_ver2, i); if (i>0) o_stream_nsend_str(conn->client.output, ",\n"); o_stream_nsend_str(conn->client.output, "\t{\"command\":\""); json_append_escaped(tmp, cmd->name); doveadm_http_server_camelcase_value(tmp); o_stream_nsend_str(conn->client.output, str_c(tmp)); o_stream_nsend_str(conn->client.output, "\", \"parameters\":["); sent = FALSE; for(k=0;cmd->parameters[k].name != NULL; k++) { str_truncate(tmp, 0); par = &(cmd->parameters[k]); if ((par->flags & CMD_PARAM_FLAG_DO_NOT_EXPOSE) != 0) continue; if (sent) o_stream_nsend_str(conn->client.output, ",\n"); else o_stream_nsend_str(conn->client.output, "\n"); sent = TRUE; o_stream_nsend_str(conn->client.output, "\t\t{\"name\":\""); json_append_escaped(tmp, par->name); doveadm_http_server_camelcase_value(tmp); o_stream_nsend_str(conn->client.output, str_c(tmp)); o_stream_nsend_str(conn->client.output, "\",\"type\":\""); switch(par->type) { case CMD_PARAM_BOOL: o_stream_nsend_str(conn->client.output, "boolean"); break; case CMD_PARAM_INT64: o_stream_nsend_str(conn->client.output, "integer"); break; case CMD_PARAM_ARRAY: o_stream_nsend_str(conn->client.output, "array"); break; case CMD_PARAM_IP: case CMD_PARAM_ISTREAM: case CMD_PARAM_STR: o_stream_nsend_str(conn->client.output, "string"); } o_stream_nsend_str(conn->client.output, "\"}"); } if (k>0) o_stream_nsend_str(conn->client.output,"\n\t"); o_stream_nsend_str(conn->client.output,"]}"); str_truncate(tmp, 0); } o_stream_nsend_str(conn->client.output,"\n]"); doveadm_http_server_send_response(conn); } static void doveadm_http_server_options_handler(struct client_connection_http *conn) { http_server_response_add_header(conn->http_response, "Access-Control-Allow-Origin", "*"); http_server_response_add_header(conn->http_response, "Access-Control-Allow-Methods", "GET, POST, OPTIONS"); http_server_response_add_header(conn->http_response, "Access-Control-Allow-Request-Headers", "Content-Type, X-API-Key, Authorization"); http_server_response_add_header(conn->http_response, "Access-Control-Allow-Headers", "Content-Type, WWW-Authenticate"); doveadm_http_server_send_response(conn); } static void doveadm_http_server_print_mounts(struct client_connection_http *conn) { o_stream_nsend_str(conn->client.output,"[\n"); for(size_t i = 0; i < N_ELEMENTS(doveadm_http_server_mounts); i++) { if (i>0) o_stream_nsend_str(conn->client.output,",\n"); o_stream_nsend_str(conn->client.output,"{\"method\":\""); if (doveadm_http_server_mounts[i].verb == NULL) o_stream_nsend_str(conn->client.output,"*"); else o_stream_nsend_str(conn->client.output,doveadm_http_server_mounts[i].verb); o_stream_nsend_str(conn->client.output,"\",\"path\":\""); if (doveadm_http_server_mounts[i].path == NULL) o_stream_nsend_str(conn->client.output,"*"); else o_stream_nsend_str(conn->client.output,doveadm_http_server_mounts[i].path); o_stream_nsend_str(conn->client.output,"\"}"); } o_stream_nsend_str(conn->client.output,"\n]"); doveadm_http_server_send_response(conn); } static bool doveadm_http_server_authorize_request(struct client_connection_http *conn) { bool auth = FALSE; struct http_auth_credentials creds; /* no authentication specified */ if (doveadm_settings->doveadm_api_key[0] == '\0' && *conn->client.set->doveadm_password == '\0') { conn->http_response = http_server_response_create(conn->http_server_request, 500, "Internal Server Error"); i_error("No authentication defined in configuration. Add API key or password"); return FALSE; } if (http_server_request_get_auth(conn->http_server_request, &creds) == 1) { /* see if the mech is supported */ if (strcasecmp(creds.scheme, "Basic") == 0 && *conn->client.set->doveadm_password != '\0') { string_t *b64_value = str_new(conn->client.pool, 32); char *value = p_strdup_printf(conn->client.pool, "doveadm:%s", conn->client.set->doveadm_password); base64_encode(value, strlen(value), b64_value); if (creds.data != NULL && strcmp(creds.data, str_c(b64_value)) == 0) auth = TRUE; else i_error("Invalid authentication attempt to HTTP API"); } else if (strcasecmp(creds.scheme, "X-Dovecot-API") == 0 && doveadm_settings->doveadm_api_key[0] != '\0') { string_t *b64_value = str_new(conn->client.pool, 32); base64_encode(doveadm_settings->doveadm_api_key, strlen(doveadm_settings->doveadm_api_key), b64_value); if (creds.data != NULL && strcmp(creds.data, str_c(b64_value)) == 0) auth = TRUE; else i_error("Invalid authentication attempt to HTTP API"); } else i_error("Unsupported authentication scheme to HTTP API"); } if (auth == FALSE) { conn->http_response = http_server_response_create(conn->http_server_request, 401, "Authentication required"); if (doveadm_settings->doveadm_api_key[0] != '\0') http_server_response_add_header(conn->http_response, "WWW-Authenticate", "X-Dovecot-API" ); if (*conn->client.set->doveadm_password != '\0') http_server_response_add_header(conn->http_response, "WWW-Authenticate", "Basic Realm=\"doveadm\"" ); } return auth; } static void doveadm_http_server_handle_request(void *context, struct http_server_request *req) { struct client_connection_http *conn = context; conn->http_server_request = req; conn->http_request = http_server_request_get(req); struct doveadm_http_server_mount *ep = NULL; http_server_connection_ref(conn->http_client); http_server_request_set_destroy_callback(req, doveadm_http_server_request_destroy, conn); http_server_request_ref(conn->http_server_request); for(size_t i = 0; i < N_ELEMENTS(doveadm_http_server_mounts); i++) { if (doveadm_http_server_mounts[i].verb == NULL || strcmp(conn->http_request->method, doveadm_http_server_mounts[i].verb) == 0) { if (doveadm_http_server_mounts[i].path == NULL || strcmp(conn->http_request->target.url->path, doveadm_http_server_mounts[i].path) == 0) { ep = &doveadm_http_server_mounts[i]; break; } } } if (ep == NULL) { http_server_request_fail_close(req, 404, "Path Not Found"); return; } if (ep->auth == TRUE && !doveadm_http_server_authorize_request(conn)) { doveadm_http_server_send_response(conn); return; } conn->http_response = http_server_response_create(req, 200, "OK"); http_server_response_add_header(conn->http_response, "Content-Type", "application/json; charset=utf-8"); if (strcmp(conn->http_request->method, "POST") == 0) { /* handle request */ conn->client.input = conn->http_request->payload; i_stream_set_name(conn->client.input, net_ip2addr(&conn->client.remote_ip)); i_stream_ref(conn->client.input); conn->client.io = io_add_istream(conn->client.input, *ep->handler, conn); conn->client.output = iostream_temp_create_named ("/tmp/doveadm.", 0, net_ip2addr(&conn->client.remote_ip)); p_array_init(&conn->pargv, conn->client.pool, 5); ep->handler(conn); } else { conn->client.output = iostream_temp_create_named ("/tmp/doveadm.", 0, net_ip2addr(&conn->client.remote_ip)); ep->handler(conn); } } static void doveadm_http_server_send_response(void *context) { struct client_connection_http *conn = context; if (conn->client.output != NULL) { if (o_stream_nfinish(conn->client.output) == -1) { i_info("error writing output: %s", o_stream_get_error(conn->client.output)); o_stream_destroy(&conn->client.output); http_server_response_update_status(conn->http_response, 500, "Internal server error"); } else { // send the payload response struct istream *is; is = iostream_temp_finish(&conn->client.output, IO_BLOCK_SIZE); http_server_response_set_payload(conn->http_response, is); i_stream_unref(&is); } } // submit response http_server_response_submit_close(conn->http_response); } static struct http_server_settings http_server_set = { .max_client_idle_time_msecs = 0, .max_pipelined_requests = 0 }; void doveadm_http_server_init(void) { http_server_set.rawlog_dir = doveadm_settings->doveadm_http_rawlog_dir; doveadm_http_server = http_server_init(&http_server_set); } void doveadm_http_server_deinit(void) { http_server_deinit(&doveadm_http_server); } dovecot-2.2.33.2/src/doveadm/doveadm-mail-search.c0000644000175000017500000000537113123174404016506 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "doveadm-print.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" #include static int cmd_search_box(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; struct mailbox_metadata metadata; const char *guid_str; int ret = 0; if (doveadm_mail_iter_init(ctx, info, ctx->search_args, 0, NULL, FALSE, &iter) < 0) return -1; box = doveadm_mail_iter_get_mailbox(iter); if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("Couldn't get mailbox '%s' GUID: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; doveadm_mail_failed_mailbox(ctx, box); } else { guid_str = guid_128_to_string(metadata.guid); while (doveadm_mail_iter_next(iter, &mail)) { doveadm_print(guid_str); T_BEGIN { doveadm_print(dec2str(mail->uid)); } T_END; } } if (doveadm_mail_iter_deinit(&iter) < 0) ret = -1; return ret; } static int cmd_search_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_search_box(ctx, info) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_search_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("search"); doveadm_print_header("mailbox-guid", "mailbox-guid", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); doveadm_print_header("uid", "uid", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); ctx->search_args = doveadm_mail_build_search_args(args); } static struct doveadm_mail_cmd_context *cmd_search_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.init = cmd_search_init; ctx->v.run = cmd_search_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return ctx; } struct doveadm_cmd_ver2 doveadm_cmd_search_ver2 = { .name = "search", .mail_cmd = cmd_search_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-auth.c0000644000175000017500000004650213165463624015276 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "askpass.h" #include "base64.h" #include "hex-binary.h" #include "str.h" #include "var-expand.h" #include "wildcard-match.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "auth-client.h" #include "auth-master.h" #include "auth-server-connection.h" #include "master-auth.h" #include "master-login-auth.h" #include "mail-storage-service.h" #include "mail-user.h" #include "doveadm.h" #include "doveadm-print.h" #include #include struct authtest_input { pool_t pool; const char *username; const char *master_user; const char *password; struct auth_user_info info; bool success; struct auth_client_request *request; struct master_auth_request master_auth_req; unsigned int auth_id; unsigned int auth_pid; const char *auth_cookie; }; static void auth_cmd_help(doveadm_command_t *cmd); static struct auth_master_connection * doveadm_get_auth_master_conn(const char *auth_socket_path) { enum auth_master_flags flags = 0; if (doveadm_debug) flags |= AUTH_MASTER_FLAG_DEBUG; return auth_master_init(auth_socket_path, flags); } static int cmd_user_input(struct auth_master_connection *conn, const struct authtest_input *input, const char *show_field, bool userdb) { const char *lookup_name = userdb ? "userdb lookup" : "passdb lookup"; pool_t pool; const char *updated_username = NULL, *const *fields, *p; int ret; pool = pool_alloconly_create("auth master lookup", 1024); if (userdb) { ret = auth_master_user_lookup(conn, input->username, &input->info, pool, &updated_username, &fields); } else { ret = auth_master_pass_lookup(conn, input->username, &input->info, pool, &fields); } if (ret < 0) { if (fields[0] == NULL) i_error("%s failed for %s", lookup_name, input->username); else { i_error("%s failed for %s: %s", lookup_name, input->username, fields[0]); } ret = -1; } else if (ret == 0) { fprintf(show_field == NULL ? stdout : stderr, "%s: user %s doesn't exist\n", lookup_name, input->username); } else if (show_field != NULL) { size_t show_field_len = strlen(show_field); for (; *fields; fields++) { if (strncmp(*fields, show_field, show_field_len) == 0 && (*fields)[show_field_len] == '=') printf("%s\n", *fields + show_field_len + 1); } } else { printf("%s: %s\n", userdb ? "userdb" : "passdb", input->username); if (updated_username != NULL) printf(" %-10s: %s\n", "user", updated_username); for (; *fields; fields++) { p = strchr(*fields, '='); if (p == NULL) printf(" %-10s\n", *fields); else { printf(" %-10s: %s\n", t_strcut(*fields, '='), p + 1); } } } return ret; } static void auth_callback(struct auth_client_request *request ATTR_UNUSED, enum auth_request_status status, const char *data_base64 ATTR_UNUSED, const char *const *args, void *context) { struct authtest_input *input = context; input->request = NULL; input->auth_id = auth_client_request_get_id(request); input->auth_pid = auth_client_request_get_server_pid(request); input->auth_cookie = input->pool == NULL ? NULL : p_strdup(input->pool, auth_client_request_get_cookie(request)); if (!io_loop_is_running(current_ioloop)) return; if (status == 0) i_fatal("passdb expects SASL continuation"); switch (status) { case AUTH_REQUEST_STATUS_ABORT: i_unreached(); case AUTH_REQUEST_STATUS_INTERNAL_FAIL: case AUTH_REQUEST_STATUS_FAIL: printf("passdb: %s auth failed\n", input->username); break; case AUTH_REQUEST_STATUS_CONTINUE: printf("passdb: %s auth unexpectedly requested continuation\n", input->username); break; case AUTH_REQUEST_STATUS_OK: input->success = TRUE; printf("passdb: %s auth succeeded\n", input->username); break; } if (args != NULL && *args != NULL) { printf("extra fields:\n"); for (; *args != NULL; args++) printf(" %s\n", *args); } io_loop_stop(current_ioloop); } static void auth_connected(struct auth_client *client, bool connected, void *context) { struct authtest_input *input = context; struct auth_request_info info; string_t *init_resp, *base64_resp; if (!connected) i_fatal("Couldn't connect to auth socket"); init_resp = t_str_new(128); str_append(init_resp, input->username); str_append_c(init_resp, '\0'); if (input->master_user != NULL) str_append(init_resp, input->master_user); else str_append(init_resp, input->username); str_append_c(init_resp, '\0'); str_append(init_resp, input->password); base64_resp = t_str_new(128); base64_encode(str_data(init_resp), str_len(init_resp), base64_resp); i_zero(&info); info.mech = "PLAIN"; info.service = input->info.service; info.local_ip = input->info.local_ip; info.local_port = input->info.local_port; info.remote_ip = input->info.remote_ip; info.remote_port = input->info.remote_port; info.initial_resp_base64 = str_c(base64_resp); if (doveadm_settings->auth_debug) info.flags |= AUTH_REQUEST_FLAG_DEBUG; input->request = auth_client_request_new(client, &info, auth_callback, input); } static void cmd_auth_input(const char *auth_socket_path, struct authtest_input *input) { struct auth_client *client; if (auth_socket_path == NULL) { auth_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-client", NULL); } client = auth_client_init(auth_socket_path, getpid(), FALSE); auth_client_set_connect_notify(client, auth_connected, input); if (!auth_client_is_disconnected(client)) io_loop_run(current_ioloop); auth_client_set_connect_notify(client, NULL, NULL); auth_client_deinit(&client); } static void auth_user_info_parse(struct auth_user_info *info, const char *arg) { if (strncmp(arg, "service=", 8) == 0) info->service = arg + 8; else if (strncmp(arg, "lip=", 4) == 0) { if (net_addr2ip(arg + 4, &info->local_ip) < 0) i_fatal("lip: Invalid ip"); } else if (strncmp(arg, "rip=", 4) == 0) { if (net_addr2ip(arg + 4, &info->remote_ip) < 0) i_fatal("rip: Invalid ip"); } else if (strncmp(arg, "lport=", 6) == 0) { if (net_str2port(arg + 6, &info->local_port) < 0) i_fatal("lport: Invalid port number"); } else if (strncmp(arg, "rport=", 6) == 0) { if (net_str2port(arg + 6, &info->remote_port) < 0) i_fatal("rport: Invalid port number"); } else { i_fatal("Unknown -x argument: %s", arg); } } static void cmd_user_list(struct auth_master_connection *conn, const struct authtest_input *input, char *const *users) { struct auth_master_user_list_ctx *ctx; const char *username, *user_mask = "*"; unsigned int i; if (users[0] != NULL && users[1] == NULL) user_mask = users[0]; ctx = auth_master_user_list_init(conn, user_mask, &input->info); while ((username = auth_master_user_list_next(ctx)) != NULL) { for (i = 0; users[i] != NULL; i++) { if (wildcard_match_icase(username, users[i])) break; } if (users[i] != NULL) printf("%s\n", username); } if (auth_master_user_list_deinit(&ctx) < 0) i_fatal("user listing failed"); } static void cmd_auth_cache_flush(int argc, char *argv[]) { const char *master_socket_path = NULL; struct auth_master_connection *conn; unsigned int count; int c; while ((c = getopt(argc, argv, "a:")) > 0) { switch (c) { case 'a': master_socket_path = optarg; break; default: auth_cmd_help(cmd_auth_cache_flush); } } argv += optind; if (master_socket_path == NULL) { master_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-master", NULL); } conn = doveadm_get_auth_master_conn(master_socket_path); if (auth_master_cache_flush(conn, (void *)argv, &count) < 0) { i_error("Cache flush failed"); doveadm_exit_code = EX_TEMPFAIL; } else { printf("%u cache entries flushed\n", count); } auth_master_deinit(&conn); } static void authtest_input_init(struct authtest_input *input) { i_zero(input); input->info.service = "doveadm"; input->info.debug = doveadm_settings->auth_debug; } static void cmd_auth_test(int argc, char *argv[]) { const char *auth_socket_path = NULL; struct authtest_input input; int c; authtest_input_init(&input); while ((c = getopt(argc, argv, "a:M:x:")) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; case 'M': input.master_user = optarg; break; case 'x': auth_user_info_parse(&input.info, optarg); break; default: auth_cmd_help(cmd_auth_test); } } if (optind == argc) auth_cmd_help(cmd_auth_test); input.username = argv[optind++]; input.password = argv[optind] != NULL ? argv[optind++] : t_askpass("Password: "); if (argv[optind] != NULL) i_fatal("Unexpected parameter: %s", argv[optind]); cmd_auth_input(auth_socket_path, &input); if (!input.success) doveadm_exit_code = EX_NOPERM; } static void master_auth_callback(const char *const *auth_args, const char *errormsg, void *context) { struct authtest_input *input = context; unsigned int i; io_loop_stop(current_ioloop); if (errormsg != NULL) { i_error("userdb lookup failed: %s", errormsg); return; } printf("userdb extra fields:\n"); for (i = 0; auth_args[i] != NULL; i++) printf(" %s\n", auth_args[i]); input->success = TRUE; } static void cmd_auth_master_input(const char *auth_master_socket_path, struct authtest_input *input) { struct master_login_auth *master_auth; struct master_auth_request master_auth_req; buffer_t buf; i_zero(&master_auth_req); master_auth_req.tag = 1; master_auth_req.auth_pid = input->auth_pid; master_auth_req.auth_id = input->auth_id; master_auth_req.client_pid = getpid(); master_auth_req.local_ip = input->info.local_ip; master_auth_req.remote_ip = input->info.remote_ip; buffer_create_from_data(&buf, master_auth_req.cookie, sizeof(master_auth_req.cookie)); if (strlen(input->auth_cookie) == MASTER_AUTH_COOKIE_SIZE*2) (void)hex_to_binary(input->auth_cookie, &buf); input->success = FALSE; master_auth = master_login_auth_init(auth_master_socket_path, FALSE); io_loop_set_running(current_ioloop); master_login_auth_request(master_auth, &master_auth_req, master_auth_callback, input); if (io_loop_is_running(current_ioloop)) io_loop_run(current_ioloop); master_login_auth_deinit(&master_auth); } static void cmd_auth_login(int argc, char *argv[]) { const char *auth_login_socket_path, *auth_master_socket_path; struct auth_client *auth_client; struct authtest_input input; int c; authtest_input_init(&input); auth_login_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-login", NULL); auth_master_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-master", NULL); while ((c = getopt(argc, argv, "a:m:M:x:")) > 0) { switch (c) { case 'a': auth_login_socket_path = optarg; break; case 'm': auth_master_socket_path = optarg; break; case 'M': input.master_user = optarg; break; case 'x': auth_user_info_parse(&input.info, optarg); break; default: auth_cmd_help(cmd_auth_login); } } if (optind == argc) auth_cmd_help(cmd_auth_login); input.pool = pool_alloconly_create("auth login", 256); input.username = argv[optind++]; input.password = argv[optind] != NULL ? argv[optind++] : t_askpass("Password: "); if (argv[optind] != NULL) i_fatal("Unexpected parameter: %s", argv[optind]); /* authenticate */ auth_client = auth_client_init(auth_login_socket_path, getpid(), FALSE); auth_client_set_connect_notify(auth_client, auth_connected, &input); if (!auth_client_is_disconnected(auth_client)) io_loop_run(current_ioloop); auth_client_set_connect_notify(auth_client, NULL, NULL); /* finish login with userdb lookup */ if (input.success) cmd_auth_master_input(auth_master_socket_path, &input); if (!input.success) doveadm_exit_code = EX_NOPERM; auth_client_deinit(&auth_client); pool_unref(&input.pool); } static void cmd_auth_lookup(int argc, char *argv[]) { const char *auth_socket_path = doveadm_settings->auth_socket_path; struct auth_master_connection *conn; struct authtest_input input; const char *show_field = NULL; bool first = TRUE; int c, ret; authtest_input_init(&input); while ((c = getopt(argc, argv, "a:f:x:")) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; case 'f': show_field = optarg; break; case 'x': auth_user_info_parse(&input.info, optarg); break; default: auth_cmd_help(cmd_auth_lookup); } } if (optind == argc) auth_cmd_help(cmd_auth_lookup); conn = doveadm_get_auth_master_conn(auth_socket_path); while ((input.username = argv[optind++]) != NULL) { if (first) first = FALSE; else putchar('\n'); ret = cmd_user_input(conn, &input, show_field, FALSE); switch (ret) { case -1: doveadm_exit_code = EX_TEMPFAIL; break; case 0: doveadm_exit_code = EX_NOUSER; break; } } auth_master_deinit(&conn); } static void cmd_user_mail_input_field(const char *key, const char *value, const char *show_field) { if (show_field == NULL) { doveadm_print(key); doveadm_print(value); } else if (strcmp(show_field, key) == 0) { printf("%s\n", value); } } static void cmd_user_mail_print_fields(const struct authtest_input *input, struct mail_user *user, const char *const *userdb_fields, const char *show_field) { const struct mail_storage_settings *mail_set; const char *key, *value; unsigned int i; if (strcmp(input->username, user->username) != 0) cmd_user_mail_input_field("user", user->username, show_field); cmd_user_mail_input_field("uid", user->set->mail_uid, show_field); cmd_user_mail_input_field("gid", user->set->mail_gid, show_field); cmd_user_mail_input_field("home", user->set->mail_home, show_field); mail_set = mail_user_set_get_storage_set(user); cmd_user_mail_input_field("mail", mail_set->mail_location, show_field); if (userdb_fields != NULL) { for (i = 0; userdb_fields[i] != NULL; i++) { value = strchr(userdb_fields[i], '='); if (value != NULL) key = t_strdup_until(userdb_fields[i], value++); else { key = userdb_fields[i]; value = ""; } if (strcmp(key, "uid") != 0 && strcmp(key, "gid") != 0 && strcmp(key, "home") != 0 && strcmp(key, "mail") != 0) cmd_user_mail_input_field(key, value, show_field); } } } static int cmd_user_mail_input(struct mail_storage_service_ctx *storage_service, const struct authtest_input *input, const char *show_field, const char *expand_field) { struct mail_storage_service_input service_input; struct mail_storage_service_user *service_user; struct mail_user *user; const char *error, *const *userdb_fields; pool_t pool; int ret; i_zero(&service_input); service_input.module = "mail"; service_input.service = input->info.service; service_input.username = input->username; service_input.local_ip = input->info.local_ip; service_input.local_port = input->info.local_port; service_input.remote_ip = input->info.remote_ip; service_input.remote_port = input->info.remote_port; service_input.debug = input->info.debug; pool = pool_alloconly_create("userdb fields", 1024); mail_storage_service_save_userdb_fields(storage_service, pool, &userdb_fields); if ((ret = mail_storage_service_lookup_next(storage_service, &service_input, &service_user, &user, &error)) <= 0) { pool_unref(&pool); if (ret < 0) return -1; fprintf(show_field == NULL && expand_field == NULL ? stdout : stderr, "\nuserdb lookup: user %s doesn't exist\n", input->username); return 0; } if (expand_field == NULL) cmd_user_mail_print_fields(input, user, userdb_fields, show_field); else { string_t *str = t_str_new(128); var_expand_with_funcs(str, expand_field, mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); printf("%s\n", str_c(str)); } mail_user_unref(&user); mail_storage_service_user_unref(&service_user); pool_unref(&pool); return 1; } static void cmd_user(int argc, char *argv[]) { const char *auth_socket_path = doveadm_settings->auth_socket_path; struct auth_master_connection *conn; struct authtest_input input; const char *show_field = NULL, *expand_field = NULL; struct mail_storage_service_ctx *storage_service = NULL; unsigned int i; bool have_wildcards, userdb_only = FALSE, first = TRUE; int c, ret; authtest_input_init(&input); while ((c = getopt(argc, argv, "a:e:f:ux:")) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; case 'e': expand_field = optarg; break; case 'f': show_field = optarg; break; case 'u': userdb_only = TRUE; break; case 'x': auth_user_info_parse(&input.info, optarg); break; default: auth_cmd_help(cmd_user); } } if (expand_field != NULL && userdb_only) { i_error("-e can't be used with -u"); doveadm_exit_code = EX_USAGE; return; } if (expand_field != NULL && show_field != NULL) { i_error("-e can't be used with -f"); doveadm_exit_code = EX_USAGE; return; } if (optind == argc) auth_cmd_help(cmd_user); conn = doveadm_get_auth_master_conn(auth_socket_path); have_wildcards = FALSE; for (i = optind; argv[i] != NULL; i++) { if (strchr(argv[i], '*') != NULL || strchr(argv[i], '?') != NULL) { have_wildcards = TRUE; break; } } if (have_wildcards) { cmd_user_list(conn, &input, argv + optind); auth_master_deinit(&conn); return; } if (!userdb_only) { storage_service = mail_storage_service_init(master_service, NULL, MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES | MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS); mail_storage_service_set_auth_conn(storage_service, conn); conn = NULL; if (show_field == NULL && expand_field == NULL) { doveadm_print_init(DOVEADM_PRINT_TYPE_TAB); doveadm_print_header_simple("field"); doveadm_print_header_simple("value"); } } while ((input.username = argv[optind++]) != NULL) { if (first) first = FALSE; else putchar('\n'); ret = !userdb_only ? cmd_user_mail_input(storage_service, &input, show_field, expand_field) : cmd_user_input(conn, &input, show_field, TRUE); switch (ret) { case -1: doveadm_exit_code = EX_TEMPFAIL; break; case 0: doveadm_exit_code = EX_NOUSER; break; } } if (storage_service != NULL) mail_storage_service_deinit(&storage_service); if (conn != NULL) auth_master_deinit(&conn); } struct doveadm_cmd doveadm_cmd_auth[] = { { cmd_auth_test, "auth test", "[-a ] [-x ] [-M ] []" }, { cmd_auth_login, "auth login", "[-a ] [-m ] [-x ] [-M ] []" }, { cmd_auth_lookup, "auth lookup", "[-a ] [-x ] [-f field] [...]" }, { cmd_auth_cache_flush, "auth cache flush", "[-a ] [ [...]]" }, { cmd_user, "user", "[-a ] [-x ] [-f field] [-e ] [-u] [...]" } }; static void auth_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_auth); i++) { if (doveadm_cmd_auth[i].cmd == cmd) help(&doveadm_cmd_auth[i]); } i_unreached(); } void doveadm_register_auth_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_auth); i++) doveadm_register_cmd(&doveadm_cmd_auth[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-mutf7.c0000644000175000017500000000223313123174404015355 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-utf7.h" #include "doveadm.h" #include #include static void cmd_mailbox_mutf7(int argc, char *argv[]) { string_t *str; bool from_utf8; unsigned int i; int c; from_utf8 = TRUE; while ((c = getopt(argc, argv, "78")) > 0) { switch (c) { case '7': from_utf8 = FALSE; break; case '8': from_utf8 = TRUE; break; default: help(&doveadm_cmd_mailbox_mutf7); } } argv += optind; if (argv[0] == NULL) help(&doveadm_cmd_mailbox_mutf7); str = t_str_new(128); for (i = 0; argv[i] != NULL; i++) { str_truncate(str, 0); if (from_utf8) { if (imap_utf8_to_utf7(argv[i], str) < 0) { i_error("Mailbox name not valid UTF-8: %s", argv[i]); doveadm_exit_code = EX_DATAERR; } } else { if (imap_utf7_to_utf8(argv[i], str) < 0) { i_error("Mailbox name not valid mUTF-7: %s", argv[i]); doveadm_exit_code = EX_DATAERR; } } printf("%s\n", str_c(str)); } } struct doveadm_cmd doveadm_cmd_mailbox_mutf7 = { cmd_mailbox_mutf7, "mailbox mutf7", "[-7|-8] [...]" }; dovecot-2.2.33.2/src/doveadm/doveadm-penalty.c0000644000175000017500000000645013144653606016005 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "istream.h" #include "hash.h" #include "strescape.h" #include "time-util.h" #include "doveadm.h" #include "doveadm-print.h" #include struct penalty_line { struct ip_addr ip; unsigned int penalty; time_t last_penalty, last_update; }; struct penalty_context { const char *anvil_path; struct ip_addr net_ip; unsigned int net_bits; }; static void penalty_parse_line(const char *line, struct penalty_line *line_r) { const char *const *args = t_strsplit_tabescaped(line); const char *ident = args[0]; const char *penalty_str = args[1]; const char *last_penalty_str = args[2]; const char *last_update_str = args[3]; i_zero(line_r); (void)net_addr2ip(ident, &line_r->ip); if (str_to_uint(penalty_str, &line_r->penalty) < 0 || str_to_time(last_penalty_str, &line_r->last_penalty) < 0 || str_to_time(last_update_str, &line_r->last_update) < 0) i_fatal("Read invalid penalty line: %s", line); } static void penalty_print_line(struct penalty_context *ctx, const struct penalty_line *line) { if (ctx->net_bits > 0) { if (!net_is_in_network(&line->ip, &ctx->net_ip, ctx->net_bits)) return; } doveadm_print(net_ip2addr(&line->ip)); doveadm_print(dec2str(line->penalty)); doveadm_print(unixdate2str(line->last_penalty)); doveadm_print(t_strflocaltime("%H:%M:%S", line->last_update)); } static void penalty_lookup(struct penalty_context *ctx) { #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" #define ANVIL_CMD ANVIL_HANDSHAKE"PENALTY-DUMP\n" struct istream *input; const char *line; int fd; fd = doveadm_connect(ctx->anvil_path); net_set_nonblock(fd, FALSE); if (write(fd, ANVIL_CMD, strlen(ANVIL_CMD)) < 0) i_fatal("write(%s) failed: %m", ctx->anvil_path); input = i_stream_create_fd_autoclose(&fd, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { if (*line == '\0') break; T_BEGIN { struct penalty_line penalty_line; penalty_parse_line(line, &penalty_line); penalty_print_line(ctx, &penalty_line); } T_END; } if (input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->anvil_path, i_stream_get_error(input)); } i_stream_destroy(&input); } static void cmd_penalty(struct doveadm_cmd_context *cctx) { struct penalty_context ctx; const char *netmask; i_zero(&ctx); if (!doveadm_cmd_param_str(cctx, "socket-path", &(ctx.anvil_path))) ctx.anvil_path = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL); if (doveadm_cmd_param_str(cctx, "netmask", &netmask)) { if (net_parse_range(netmask, &ctx.net_ip, &ctx.net_bits) != 0) { doveadm_exit_code = EX_USAGE; i_error("Invalid netmask '%s' given", netmask); return; } } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("IP"); doveadm_print_header_simple("penalty"); doveadm_print_header_simple("last_penalty"); doveadm_print_header_simple("last_update"); penalty_lookup(&ctx); } struct doveadm_cmd_ver2 doveadm_cmd_penalty_ver2 = { .name = "penalty", .cmd = cmd_penalty, .usage = "[-a ] []", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a',"socket-path", CMD_PARAM_STR,0) DOVEADM_CMD_PARAM('\0',"netmask", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-server.h0000644000175000017500000000051613123174404015630 00000000000000#ifndef DOVEADM_SERVER_H #define DOVEADM_SERVER_H extern struct client_connection *doveadm_client; extern struct doveadm_print_vfuncs doveadm_print_server_vfuncs; struct doveadm_server { const char *name; struct ssl_iostream_context *ssl_ctx; ARRAY(struct server_connection *) connections; ARRAY_TYPE(string) queue; }; #endif dovecot-2.2.33.2/src/doveadm/doveadm-settings.c0000644000175000017500000001241613165463543016172 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "doveadm-settings.h" static bool doveadm_settings_check(void *_set, pool_t pool, const char **error_r); /* */ static struct file_listener_settings doveadm_unix_listeners_array[] = { { "doveadm-server", 0600, "", "" } }; static struct file_listener_settings *doveadm_unix_listeners[] = { &doveadm_unix_listeners_array[0] }; static buffer_t doveadm_unix_listeners_buf = { doveadm_unix_listeners, sizeof(doveadm_unix_listeners), { NULL, } }; /* */ struct service_settings doveadm_service_settings = { .name = "doveadm", .protocol = "", .type = "", .executable = "doveadm-server", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &doveadm_unix_listeners_buf, sizeof(doveadm_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct doveadm_settings, name), NULL } static const struct setting_define doveadm_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, libexec_dir), DEF(SET_STR, mail_plugins), DEF(SET_STR, mail_plugin_dir), DEF(SET_BOOL, auth_debug), DEF(SET_STR, auth_socket_path), DEF(SET_STR, doveadm_socket_path), DEF(SET_UINT, doveadm_worker_count), DEF(SET_IN_PORT, doveadm_port), { SET_ALIAS, "doveadm_proxy_port", 0, NULL }, DEF(SET_STR, doveadm_username), DEF(SET_STR, doveadm_password), DEF(SET_STR, doveadm_allowed_commands), DEF(SET_STR, dsync_alt_char), DEF(SET_STR, dsync_remote_cmd), DEF(SET_STR, ssl_client_ca_dir), DEF(SET_STR, ssl_client_ca_file), DEF(SET_STR, director_username_hash), DEF(SET_STR, doveadm_api_key), DEF(SET_STR, dsync_features), DEF(SET_UINT, dsync_commit_msgs_interval), DEF(SET_STR, doveadm_http_rawlog_dir), DEF(SET_STR, dsync_hashed_headers), { SET_STRLIST, "plugin", offsetof(struct doveadm_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; const struct doveadm_settings doveadm_default_settings = { .base_dir = PKG_RUNDIR, .libexec_dir = PKG_LIBEXECDIR, .mail_plugins = "", .mail_plugin_dir = MODULEDIR, .auth_debug = FALSE, .auth_socket_path = "auth-userdb", .doveadm_socket_path = "doveadm-server", .doveadm_worker_count = 0, .doveadm_port = 0, .doveadm_username = "doveadm", .doveadm_password = "", .doveadm_allowed_commands = "", .dsync_alt_char = "_", .dsync_remote_cmd = "ssh -l%{login} %{host} doveadm dsync-server -u%u -U", .dsync_features = "", .dsync_hashed_headers = "Date Message-ID", .dsync_commit_msgs_interval = 100, .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .director_username_hash = "%Lu", .doveadm_api_key = "", .doveadm_http_rawlog_dir = "", .plugin_envs = ARRAY_INIT }; static const struct setting_parser_info *doveadm_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info doveadm_setting_parser_info = { .module_name = "doveadm", .defines = doveadm_setting_defines, .defaults = &doveadm_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct doveadm_settings), .parent_offset = (size_t)-1, .check_func = doveadm_settings_check, .dependencies = doveadm_setting_dependencies }; struct doveadm_settings *doveadm_settings; const struct master_service_settings *service_set; static void fix_base_path(struct doveadm_settings *set, pool_t pool, const char **str) { if (*str != NULL && **str != '\0' && **str != '/') *str = p_strconcat(pool, set->base_dir, "/", *str, NULL); } /* */ struct dsync_feature_list { const char *name; enum dsync_features num; }; static const struct dsync_feature_list dsync_feature_list[] = { { "empty-header-workaround", DSYNC_FEATURE_EMPTY_HDR_WORKAROUND }, { NULL, 0 } }; static int dsync_settings_parse_features(struct doveadm_settings *set, const char **error_r) { enum dsync_features features = 0; const struct dsync_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->dsync_features, " ,"); for (; *str != NULL; str++) { list = dsync_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("dsync_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool doveadm_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct doveadm_settings *set = _set; #ifndef CONFIG_BINARY fix_base_path(set, pool, &set->auth_socket_path); fix_base_path(set, pool, &set->doveadm_socket_path); #endif if (*set->dsync_hashed_headers == '\0') { *error_r = "dsync_hashed_headers must not be empty"; return FALSE; } if (*set->dsync_alt_char == '\0') { *error_r = "dsync_alt_char must not be empty"; return FALSE; } if (dsync_settings_parse_features(set, error_r) != 0) return FALSE; return TRUE; } /* */ dovecot-2.2.33.2/src/doveadm/client-connection.h0000644000175000017500000000140613165463624016332 00000000000000#ifndef CLIENT_CONNECTION_H #define CLIENT_CONNECTION_H #include "net.h" #define DOVEADM_LOG_CHANNEL_ID 'L' struct client_connection { pool_t pool; int fd; const char *name; struct io *io; struct istream *input; struct ostream *output; struct ostream *log_out; struct ssl_iostream *ssl_iostream; struct ip_addr local_ip, remote_ip; in_port_t local_port, remote_port; const struct doveadm_settings *set; unsigned int handshaked:1; unsigned int authenticated:1; unsigned int io_setup:1; unsigned int use_multiplex:1; }; struct client_connection * client_connection_create(int fd, int listen_fd, bool ssl); struct client_connection * client_connection_create_http(int fd, bool ssl); void client_connection_destroy(struct client_connection **conn); #endif dovecot-2.2.33.2/src/doveadm/doveadm-pw.c0000644000175000017500000000643513165463624014764 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "password-scheme.h" #include "randgen.h" #include "doveadm.h" #include "askpass.h" #include "module-dir.h" #include #include #include #include #define DEFAULT_SCHEME "CRAM-MD5" static struct module *modules = NULL; static void cmd_pw(int argc, char *argv[]) { const char *hash = NULL; const char *user = NULL; const char *scheme = NULL; const char *plaintext = NULL; const char *test_hash = NULL; bool list_schemes = FALSE, reverse_verify = FALSE; unsigned int rounds = 0; int c; struct module_dir_load_settings mod_set; random_init(); password_schemes_init(); i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; mod_set.ignore_dlopen_errors = TRUE; mod_set.debug = doveadm_debug; modules = module_dir_load_missing(modules, AUTH_MODULE_DIR, NULL, &mod_set); module_dir_init(modules); while ((c = getopt(argc, argv, "lp:r:s:t:u:V")) > 0) { switch (c) { case 'l': list_schemes = 1; break; case 'p': plaintext = optarg; break; case 'r': if (str_to_uint(optarg, &rounds) < 0) i_fatal("Invalid number of rounds: %s", optarg); break; case 's': scheme = optarg; break; case 't': test_hash = optarg; reverse_verify = TRUE; break; case 'u': user = optarg; break; case 'V': reverse_verify = TRUE; break; case '?': default: help(&doveadm_cmd_pw); } } if (list_schemes) { const struct password_scheme *const *schemes; unsigned int i, count; schemes = array_get(&password_schemes, &count); for (i = 0; i < count; i++) printf("%s ", schemes[i]->name); printf("\n"); exit(0); } if (argc != optind) help(&doveadm_cmd_pw); scheme = scheme == NULL ? DEFAULT_SCHEME : t_str_ucase(scheme); if (rounds > 0) password_set_encryption_rounds(rounds); if (test_hash != NULL && plaintext == NULL) plaintext = t_askpass("Enter password to verify: "); while (plaintext == NULL) { const char *check; static int lives = 3; plaintext = t_askpass("Enter new password: "); check = t_askpass("Retype new password: "); if (strcmp(plaintext, check) != 0) { i_error("Passwords don't match!"); if (--lives == 0) exit(1); plaintext = NULL; } } if (!password_generate_encoded(plaintext, user, scheme, &hash)) i_fatal("Unknown scheme: %s", scheme); if (reverse_verify) { const unsigned char *raw_password; size_t size; const char *error; if (test_hash != NULL) { scheme = password_get_scheme(&test_hash); if (scheme == NULL) i_fatal("Missing {scheme} prefix from hash"); hash = test_hash; } if (password_decode(hash, scheme, &raw_password, &size, &error) <= 0) i_fatal("reverse decode check failed: %s", error); if (password_verify(plaintext, user, scheme, raw_password, size, &error) <= 0) { i_fatal("reverse password verification check failed: %s", error); } printf("{%s}%s (verified)\n", scheme, hash); } else { printf("{%s}%s\n", scheme, hash); } module_dir_unload(&modules); password_schemes_deinit(); random_deinit(); } struct doveadm_cmd doveadm_cmd_pw = { cmd_pw, "pw", "[-l] [-p plaintext] [-r rounds] [-s scheme] [-t hash] [-u user] [-V]" }; dovecot-2.2.33.2/src/doveadm/doveadm-util.c0000644000175000017500000001157013165463624015307 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "time-util.h" #include "master-service.h" #include "module-dir.h" #include "doveadm-settings.h" #include "doveadm-mail.h" #include "doveadm-util.h" #include #include #include #include #define DOVEADM_TCP_CONNECT_TIMEOUT_SECS 30 bool doveadm_verbose = FALSE, doveadm_debug = FALSE, doveadm_server = FALSE; static struct module *modules = NULL; void doveadm_load_modules(void) { struct module_dir_load_settings mod_set; /* some doveadm plugins have dependencies to mail plugins. we can load only those whose dependencies have been loaded earlier, the rest are ignored. */ i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = doveadm_debug; mod_set.ignore_dlopen_errors = TRUE; modules = module_dir_load_missing(modules, DOVEADM_MODULEDIR, NULL, &mod_set); module_dir_init(modules); } void doveadm_unload_modules(void) { module_dir_unload(&modules); } bool doveadm_has_unloaded_plugin(const char *name) { struct module *module; DIR *dir; struct dirent *d; const char *plugin_name; size_t name_len = strlen(name); bool found = FALSE; /* first check that it's not actually loaded */ for (module = modules; module != NULL; module = module->next) { if (strcmp(module_get_plugin_name(module), name) == 0) return FALSE; } dir = opendir(DOVEADM_MODULEDIR); if (dir == NULL) return FALSE; while ((d = readdir(dir)) != NULL) { plugin_name = module_file_get_name(d->d_name); if (strncmp(plugin_name, "doveadm_", 8) == 0) plugin_name += 8; if (strncmp(plugin_name, name, name_len) == 0 && (plugin_name[name_len] == '\0' || strcmp(plugin_name + name_len, "_plugin") == 0)) { found = TRUE; break; } } (void)closedir(dir); return found; } const char *unixdate2str(time_t timestamp) { return t_strflocaltime("%Y-%m-%d %H:%M:%S", timestamp); } const char *doveadm_plugin_getenv(const char *name) { const char *const *envs; unsigned int i, count; if (!array_is_created(&doveadm_settings->plugin_envs)) return NULL; envs = array_get(&doveadm_settings->plugin_envs, &count); for (i = 0; i < count; i += 2) { if (strcmp(envs[i], name) == 0) return envs[i+1]; } return NULL; } static int doveadm_tcp_connect_port(const char *host, in_port_t port) { struct ip_addr *ips; unsigned int ips_count; int ret, fd; alarm(DOVEADM_TCP_CONNECT_TIMEOUT_SECS); ret = net_gethostbyname(host, &ips, &ips_count); if (ret != 0) { i_fatal("Lookup of host %s failed: %s", host, net_gethosterror(ret)); } fd = net_connect_ip_blocking(&ips[0], port, NULL); if (fd == -1) { i_fatal("connect(%s:%u) failed: %m", net_ip2addr(&ips[0]), port); } alarm(0); return fd; } int doveadm_tcp_connect(const char *target, in_port_t default_port) { const char *host; in_port_t port; if (net_str2hostport(target, default_port, &host, &port) < 0) { i_fatal("Port not known for %s. Either set proxy_port " "or use %s:port", target, target); } return doveadm_tcp_connect_port(host, port); } int doveadm_connect_with_default_port(const char *path, in_port_t default_port) { int fd; /* we'll assume UNIX sockets typically have an absolute path, or at the very least '/' somewhere. */ if (strchr(path, '/') == NULL) fd = doveadm_tcp_connect(path, default_port); else { fd = net_connect_unix(path); if (fd == -1) i_fatal("net_connect_unix(%s) failed: %m", path); } return fd; } int doveadm_connect(const char *path) { return doveadm_connect_with_default_port(path, 0); } int i_strccdascmp(const char *a, const char *b) { while(*a && *b) { if ((*a == ' ' || *a == '-') && *a != *b && *b != ' ' && *b != '-') { if (i_toupper(*(a+1)) == *(b)) a++; else break; } else if ((*b == ' ' || *b == '-') && *a != *b && *a != ' ' && *a != '-') { if (*a == i_toupper(*(b+1))) b++; else break; } else if (!((*a == ' ' || *a == '-') && (*b == ' ' || *b == '-')) && (*a != *b)) break; a++; b++; } return *a-*b; } char doveadm_log_type_to_char(enum log_type type) { switch(type) { case LOG_TYPE_DEBUG: return '\x01'; case LOG_TYPE_INFO: return '\x02'; case LOG_TYPE_WARNING: return '\x03'; case LOG_TYPE_ERROR: return '\x04'; case LOG_TYPE_FATAL: return '\x05'; case LOG_TYPE_PANIC: return '\x06'; default: i_unreached(); } } bool doveadm_log_type_from_char(char c, enum log_type *type_r) { switch(c) { case '\x01': *type_r = LOG_TYPE_DEBUG; break; case '\x02': *type_r = LOG_TYPE_INFO; break; case '\x03': *type_r = LOG_TYPE_WARNING; break; case '\x04': *type_r = LOG_TYPE_ERROR; break; case '\x05': *type_r = LOG_TYPE_FATAL; break; case '\x06': *type_r = LOG_TYPE_PANIC; break; default: *type_r = LOG_TYPE_WARNING; return FALSE; } return TRUE; } dovecot-2.2.33.2/src/doveadm/doveadm-mail-copymove.c0000644000175000017500000001533513165463624017116 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "mail-namespace.h" #include "doveadm-print.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" #include struct copy_cmd_context { struct doveadm_mail_cmd_context ctx; const char *source_username; struct mail_storage_service_user *source_service_user; struct mail_user *source_user; const char *destname; bool move; }; static int cmd_copy_box(struct copy_cmd_context *ctx, struct mailbox *destbox, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; struct mailbox_transaction_context *desttrans; struct mail_save_context *save_ctx; struct mail *mail; int ret = 0, ret2; if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, 0, NULL, FALSE, &iter) < 0) return -1; /* use a separately committed transaction for each mailbox. this guarantees that mails aren't expunged without actually having been copied. */ desttrans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); while (doveadm_mail_iter_next(iter, &mail)) { save_ctx = mailbox_save_alloc(desttrans); mailbox_save_copy_flags(save_ctx, mail); if (ctx->move) ret2 = mailbox_move(&save_ctx, mail); else ret2 = mailbox_copy(&save_ctx, mail); if (ret2 < 0) { i_error("%s message UID %u from '%s' failed: %s", ctx->move ? "Moving" : "Copying", mail->uid, info->vname, mailbox_get_last_internal_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); ret = -1; } } if (mailbox_transaction_commit(&desttrans) < 0) { i_error("Committing %s mails failed: %s", ctx->move ? "moved" : "copied", mailbox_get_last_internal_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); /* rollback expunges */ doveadm_mail_iter_deinit_rollback(&iter); ret = -1; } else { if (doveadm_mail_iter_deinit_sync(&iter) < 0) ret = -1; } return ret; } static void cmd_copy_alloc_source_user(struct copy_cmd_context *ctx) { struct mail_storage_service_input input; const char *error; input = ctx->ctx.storage_service_input; input.username = ctx->source_username; if (mail_storage_service_lookup_next(ctx->ctx.storage_service, &input, &ctx->source_service_user, &ctx->source_user, &error) < 0) i_fatal("Couldn't lookup user %s: %s", input.username, error); } static int cmd_copy_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct copy_cmd_context *ctx = (struct copy_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; struct mail_user *src_user; struct mail_namespace *ns; struct mailbox *destbox; const struct mailbox_info *info; int ret = 0; if (ctx->source_username != NULL && ctx->source_user == NULL) cmd_copy_alloc_source_user(ctx); ns = mail_namespace_find(user->namespaces, ctx->destname); destbox = mailbox_alloc(ns->list, ctx->destname, MAILBOX_FLAG_SAVEONLY); mailbox_set_reason(destbox, _ctx->cmd->name); if (mailbox_open(destbox) < 0) { i_error("Can't open mailbox '%s': %s", ctx->destname, mailbox_get_last_internal_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); mailbox_free(&destbox); return -1; } src_user = ctx->source_user != NULL ? ctx->source_user : user; iter = doveadm_mailbox_list_iter_init(_ctx, src_user, _ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_copy_box(ctx, destbox, info) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; if (mailbox_sync(destbox, 0) < 0) { i_error("Syncing mailbox '%s' failed: %s", ctx->destname, mailbox_get_last_internal_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); ret = -1; } mailbox_free(&destbox); return ret; } static void cmd_copy_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct copy_cmd_context *ctx = (struct copy_cmd_context *)_ctx; const char *destname = args[0], *cmdname = ctx->move ? "move" : "copy"; if (destname == NULL || args[1] == NULL) doveadm_mail_help_name(cmdname); args++; if (args[0] != NULL && args[1] != NULL && strcasecmp(args[0], "user") == 0) { if ((_ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) i_fatal("Use -u parameter to specify destination user"); ctx->source_username = p_strdup(_ctx->pool, args[1]); args += 2; } ctx->destname = p_strdup(ctx->ctx.pool, destname); _ctx->search_args = doveadm_mail_build_search_args(args); if (ctx->move) expunge_search_args_check(ctx->ctx.search_args, cmdname); } static void cmd_copy_deinit(struct doveadm_mail_cmd_context *_ctx) { struct copy_cmd_context *ctx = (struct copy_cmd_context *)_ctx; if (ctx->source_user != NULL) { mail_storage_service_user_unref(&ctx->source_service_user); mail_user_unref(&ctx->source_user); } } static struct doveadm_mail_cmd_context *cmd_copy_alloc(void) { struct copy_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct copy_cmd_context); ctx->ctx.v.init = cmd_copy_init; ctx->ctx.v.deinit = cmd_copy_deinit; ctx->ctx.v.run = cmd_copy_run; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_move_alloc(void) { struct copy_cmd_context *ctx; ctx = (struct copy_cmd_context *)cmd_copy_alloc(); ctx->move = TRUE; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_copy_ver2 = { .name = "copy", .mail_cmd = cmd_copy_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " [user ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "destination-mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "source-type", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "source-user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_move_ver2 = { .name = "move", .mail_cmd = cmd_move_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " [user ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "destination-mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "source-type", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "source-user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-dump-dcrypt-key.c0000644000175000017500000001311713165463624017367 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dcrypt.h" #include "dcrypt-iostream.h" #include "ostream-encrypt.h" #include "istream-private.h" #include "istream-decrypt.h" #include "doveadm-dump.h" #include "hex-binary.h" #include "buffer.h" #include "str.h" #include #include #include #include #include #define KEY_BUF_SIZE 4096 static void dcrypt_dump_public_key_metadata(const char *buf) { const char *error = NULL; struct dcrypt_public_key *pub_key; bool ret = dcrypt_key_load_public(&pub_key, buf, &error); if (ret == FALSE) { i_error("dcrypt_key_load_public failed: %s", error); return; } enum dcrypt_key_type key_type = dcrypt_key_type_public(pub_key); if (key_type == DCRYPT_KEY_RSA) printf("key type: DCRYPT_KEY_RSA\n"); else if (key_type == DCRYPT_KEY_EC) printf("key type: DCRYPT_KEY_EC\n"); string_t *hash = t_str_new(128); if (!dcrypt_key_id_public(pub_key, "sha256", hash, &error)) { i_error("dcrypt_key_id_public failed: %s", error); } else { const char *v2_hash = binary_to_hex(hash->data, hash->used); printf("v2 hash: %s\n", v2_hash); if (key_type == DCRYPT_KEY_EC) { buffer_set_used_size(hash, 0); if (!dcrypt_key_id_public_old(pub_key, hash, &error)) { i_error("dcrypt_key_id_public_old failed: %s", error); } else { const char *v1_hash = binary_to_hex(hash->data, hash->used); printf("v1 hash: %s\n", v1_hash); } } } dcrypt_key_unref_public(&pub_key); } static void dcrypt_dump_private_key_metadata(const char *buf) { const char *error = NULL; struct dcrypt_private_key *priv_key; bool ret = dcrypt_key_load_private(&priv_key, buf, NULL, NULL, &error); if (ret == FALSE) { i_error("dcrypt_key_load_private failed: %s", error); return; } enum dcrypt_key_type key_type = dcrypt_key_type_private(priv_key); if (key_type == DCRYPT_KEY_RSA) printf("key type: DCRYPT_KEY_RSA\n"); else if (key_type == DCRYPT_KEY_EC) printf("key type: DCRYPT_KEY_EC\n"); string_t *hash = t_str_new(128); if (!dcrypt_key_id_private(priv_key, "sha256", hash, &error)) { i_error("dcrypt_key_id_private failed: %s", error); } else { const char *v2_hash = binary_to_hex(hash->data, hash->used); printf("v2 hash: %s\n", v2_hash); if (key_type == DCRYPT_KEY_EC) { buffer_set_used_size(hash, 0); if (!dcrypt_key_id_private_old(priv_key, hash, &error)) { i_error("dcrypt_key_id_private_old failed: %s", error); } else { const char *v1_hash = binary_to_hex(hash->data, hash->used); printf("v1 hash: %s\n", v1_hash); } } } dcrypt_key_unref_private(&priv_key); } static bool dcrypt_key_dump_metadata(const char *filename, bool print) { bool ret = TRUE; int fd = open(filename, O_RDONLY); if (fd < 0) { if (print) i_error("open(%s) failed: %m", filename); return FALSE; } char buf[KEY_BUF_SIZE+1]; ssize_t res = read(fd, buf, KEY_BUF_SIZE); if (res < 0) { if (print) i_error("read(%s) failed: %m", filename); i_close_fd(&fd); return FALSE; } i_close_fd(&fd); buf[res] = '\0'; enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type encryption_type; const char *encryption_key_hash; const char *key_hash; const char *error; ret = dcrypt_key_string_get_info(buf, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); if (ret == FALSE) { if (print) i_error("dcrypt_key_string_get_info failed: %s", error); return FALSE; } if (!print) return TRUE; switch (format) { case DCRYPT_FORMAT_PEM: printf("format: DCRYPT_FORMAT_PEM\n"); break; case DCRYPT_FORMAT_DOVECOT: printf("format: DCRYPT_FORMAT_DOVECOT\n"); break; } switch (version) { case DCRYPT_KEY_VERSION_1: printf("version: DCRYPT_KEY_VERSION_1\n"); break; case DCRYPT_KEY_VERSION_2: printf("version: DCRYPT_KEY_VERSION_2\n"); break; case DCRYPT_KEY_VERSION_NA: printf("version: DCRYPT_KEY_VERSION_NA\n"); break; } switch (kind) { case DCRYPT_KEY_KIND_PUBLIC: printf("kind: DCRYPT_KEY_KIND_PUBLIC\n"); break; case DCRYPT_KEY_KIND_PRIVATE: printf("kind: DCRYPT_KEY_KIND_PRIVATE\n"); break; } switch (encryption_type) { case DCRYPT_KEY_ENCRYPTION_TYPE_NONE: printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_NONE\n"); break; case DCRYPT_KEY_ENCRYPTION_TYPE_KEY: printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_KEY\n"); break; case DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD: printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD\n"); break; } if (encryption_key_hash != NULL) printf("encryption_key_hash: %s\n", encryption_key_hash); if (key_hash != NULL) printf("key_hash: %s\n", key_hash); const char *data = t_str_rtrim(buf, "\r\n\t "); switch (kind) { case DCRYPT_KEY_KIND_PUBLIC: dcrypt_dump_public_key_metadata(data); break; case DCRYPT_KEY_KIND_PRIVATE: if (encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) dcrypt_dump_private_key_metadata(data); break; } return TRUE; } static bool test_dump_dcrypt_key(const char *path) { if (!dcrypt_initialize("openssl", NULL, NULL)) return FALSE; bool ret = dcrypt_key_dump_metadata(path, FALSE); dcrypt_deinitialize(); return ret; } static void cmd_dump_dcrypt_key(int argc ATTR_UNUSED, char *argv[]) { const char *error = NULL; if (!dcrypt_initialize("openssl", NULL, &error)) i_fatal("dcrypt_initialize: %s", error); (void)dcrypt_key_dump_metadata(argv[1], TRUE); dcrypt_deinitialize(); } struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_key = { "dcrypt-key", test_dump_dcrypt_key, cmd_dump_dcrypt_key }; dovecot-2.2.33.2/src/doveadm/doveadm-mount.c0000644000175000017500000000730013165463624015470 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "master-service-settings.h" #include "mountpoint-list.h" #include "doveadm.h" #include "doveadm-print.h" extern struct doveadm_cmd doveadm_cmd_mount[]; static void mount_cmd_help(doveadm_command_t *cmd) ATTR_NORETURN; static struct mountpoint_list *mountpoint_list_get(void) { const char *perm_path, *state_path; perm_path = t_strconcat(service_set->state_dir, "/"MOUNTPOINT_LIST_FNAME, NULL); state_path = t_strconcat(service_set->base_dir, "/"MOUNTPOINT_LIST_FNAME, NULL); return mountpoint_list_init(perm_path, state_path); } static void cmd_mount_list(int argc, char *argv[]) { struct mountpoint_list *mountpoints; struct mountpoint_list_iter *iter; struct mountpoint_list_rec *rec; bool mounts_known; if (argc > 2) mount_cmd_help(cmd_mount_list); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple(" "); doveadm_print_header("path", "path", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("state"); mountpoints = mountpoint_list_get(); mounts_known = mountpoint_list_update_mounted(mountpoints) == 0; iter = mountpoint_list_iter_init(mountpoints); while ((rec = mountpoint_list_iter_next(iter)) != NULL) { if (argv[1] != NULL && strcmp(argv[1], rec->mount_path) != 0) continue; if (mounts_known && MOUNTPOINT_WRONGLY_NOT_MOUNTED(rec)) doveadm_print("!"); else doveadm_print(" "); doveadm_print(!rec->wildcard ? rec->mount_path : t_strconcat(rec->mount_path, "*", NULL)); doveadm_print(rec->state); } mountpoint_list_iter_deinit(&iter); mountpoint_list_deinit(&mountpoints); } static bool mount_path_get_wildcard(const char **path) { unsigned int len; len = strlen(*path); if (len > 0 && (*path)[len-1] == '*') { *path = t_strndup(*path, len-1); return TRUE; } else { return FALSE; } } static void cmd_mount_add(int argc, char *argv[]) { struct mountpoint_list *mountpoints; struct mountpoint_list_rec rec; int ret = 0; if (argc > 3) mount_cmd_help(cmd_mount_add); mountpoints = mountpoint_list_get(); if (argv[1] == NULL) { ret = mountpoint_list_add_missing(mountpoints, MOUNTPOINT_STATE_DEFAULT, mountpoint_list_default_ignore_prefixes, mountpoint_list_default_ignore_types); } else { i_zero(&rec); rec.mount_path = argv[1]; rec.state = argv[2] != NULL ? argv[2] : MOUNTPOINT_STATE_DEFAULT; if (mount_path_get_wildcard(&rec.mount_path)) rec.wildcard = TRUE; mountpoint_list_add(mountpoints, &rec); } if (mountpoint_list_save(mountpoints) < 0) ret = -1; mountpoint_list_deinit(&mountpoints); if (ret < 0) doveadm_exit_code = EX_TEMPFAIL; } static void cmd_mount_remove(int argc, char *argv[]) { struct mountpoint_list *mountpoints; const char *mount_path; if (argc != 2) mount_cmd_help(cmd_mount_remove); mount_path = argv[1]; (void)mount_path_get_wildcard(&mount_path); mountpoints = mountpoint_list_get(); if (!mountpoint_list_remove(mountpoints, mount_path)) i_error("Mountpoint not found: %s", mount_path); else (void)mountpoint_list_save(mountpoints); mountpoint_list_deinit(&mountpoints); } struct doveadm_cmd doveadm_cmd_mount[] = { { cmd_mount_list, "mount list", "[]" }, { cmd_mount_add, "mount add", "[ []]" }, { cmd_mount_remove, "mount remove", "" } }; static void mount_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_mount); i++) { if (doveadm_cmd_mount[i].cmd == cmd) help(&doveadm_cmd_mount[i]); } i_unreached(); } void doveadm_register_mount_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_mount); i++) doveadm_register_cmd(&doveadm_cmd_mount[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-mail-iter.c0000644000175000017500000001047313165463624016216 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "mail-namespace.h" #include "mail-search.h" #include "doveadm-mail.h" #include "doveadm-mail-iter.h" struct doveadm_mail_iter { struct doveadm_mail_cmd_context *ctx; struct mail_search_args *search_args; struct mailbox *box; struct mailbox_transaction_context *t; struct mail_search_context *search_ctx; bool killed; }; int doveadm_mail_iter_init(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info, struct mail_search_args *search_args, enum mail_fetch_field wanted_fields, const char *const *wanted_headers, bool readonly, struct doveadm_mail_iter **iter_r) { struct doveadm_mail_iter *iter; struct mailbox_header_lookup_ctx *headers_ctx; const char *errstr; enum mail_error error; enum mailbox_flags readonly_flag = readonly ? MAILBOX_FLAG_READONLY : 0; iter = i_new(struct doveadm_mail_iter, 1); iter->ctx = ctx; iter->box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_IGNORE_ACLS | readonly_flag); mailbox_set_reason(iter->box, ctx->cmd->name); iter->search_args = search_args; if (mailbox_sync(iter->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { errstr = mailbox_get_last_internal_error(iter->box, &error); if (error == MAIL_ERROR_NOTFOUND) { /* just ignore this mailbox */ *iter_r = iter; return 0; } i_error("Syncing mailbox %s failed: %s", info->vname, errstr); doveadm_mail_failed_mailbox(ctx, iter->box); mailbox_free(&iter->box); i_free(iter); return -1; } headers_ctx = wanted_headers == NULL || wanted_headers[0] == NULL ? NULL : mailbox_header_lookup_init(iter->box, wanted_headers); mail_search_args_init(search_args, iter->box, FALSE, NULL); iter->t = mailbox_transaction_begin(iter->box, 0); iter->search_ctx = mailbox_search_init(iter->t, search_args, NULL, wanted_fields, headers_ctx); *iter_r = iter; return 0; } static int doveadm_mail_iter_deinit_transaction(struct doveadm_mail_iter *iter, bool commit) { int ret = 0; if (iter->search_ctx != NULL) { if (mailbox_search_deinit(&iter->search_ctx) < 0) { i_error("Searching mailbox %s failed: %s", mailbox_get_vname(iter->box), mailbox_get_last_internal_error(iter->box, NULL)); ret = -1; } } if (iter->t == NULL) ; else if (commit) { if (mailbox_transaction_commit(&iter->t) < 0) { i_error("Committing mailbox %s failed: %s", mailbox_get_vname(iter->box), mailbox_get_last_internal_error(iter->box, NULL)); ret = -1; } } else { mailbox_transaction_rollback(&iter->t); } mail_search_args_deinit(iter->search_args); return ret; } static int doveadm_mail_iter_deinit_full(struct doveadm_mail_iter **_iter, bool sync, bool commit, bool keep_box) { struct doveadm_mail_iter *iter = *_iter; int ret; *_iter = NULL; ret = doveadm_mail_iter_deinit_transaction(iter, commit); if (ret == 0 && sync) { ret = mailbox_sync(iter->box, 0); if (ret < 0) { i_error("Mailbox %s: Mailbox sync failed: %s", mailbox_get_vname(iter->box), mailbox_get_last_internal_error(iter->box, NULL)); } } if (ret < 0) doveadm_mail_failed_mailbox(iter->ctx, iter->box); else if (iter->killed) { iter->ctx->exit_code = EX_TEMPFAIL; ret = -1; } if (!keep_box) mailbox_free(&iter->box); i_free(iter); return ret; } int doveadm_mail_iter_deinit(struct doveadm_mail_iter **_iter) { return doveadm_mail_iter_deinit_full(_iter, FALSE, TRUE, FALSE); } int doveadm_mail_iter_deinit_sync(struct doveadm_mail_iter **_iter) { return doveadm_mail_iter_deinit_full(_iter, TRUE, TRUE, FALSE); } int doveadm_mail_iter_deinit_keep_box(struct doveadm_mail_iter **iter, struct mailbox **box_r) { *box_r = (*iter)->box; return doveadm_mail_iter_deinit_full(iter, FALSE, TRUE, TRUE); } void doveadm_mail_iter_deinit_rollback(struct doveadm_mail_iter **_iter) { (void)doveadm_mail_iter_deinit_full(_iter, FALSE, FALSE, FALSE); } bool doveadm_mail_iter_next(struct doveadm_mail_iter *iter, struct mail **mail_r) { if (iter->search_ctx == NULL) return FALSE; if (doveadm_is_killed()) { iter->killed = TRUE; return FALSE; } return mailbox_search_next(iter->search_ctx, mail_r); } struct mailbox *doveadm_mail_iter_get_mailbox(struct doveadm_mail_iter *iter) { return iter->box; } dovecot-2.2.33.2/src/doveadm/doveadm-zlib.c0000644000175000017500000001136713165463624015276 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "istream-zlib.h" #include "ostream-zlib.h" #include "module-dir.h" #include "master-service.h" #include "doveadm-dump.h" #include #include #include static bool test_dump_imapzlib(const char *path) { const char *p; char buf[4096]; int fd, ret; bool match = FALSE; p = strrchr(path, '.'); if (p == NULL || (strcmp(p, ".in") != 0 && strcmp(p, ".out") != 0)) return FALSE; fd = open(path, O_RDONLY); if (fd == -1) return FALSE; ret = read(fd, buf, sizeof(buf)-1); if (ret > 0) { buf[ret] = '\0'; (void)str_lcase(buf); match = strstr(buf, " ok begin compression.") != NULL || strstr(buf, " compress deflate") != NULL; } i_close_fd(&fd); return match; } #ifdef HAVE_ZLIB static void cmd_dump_imapzlib(int argc ATTR_UNUSED, char *argv[]) { struct istream *input, *input2; const unsigned char *data; size_t size; const char *line; int fd; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); input = i_stream_create_fd_autoclose(&fd, 1024*32); while ((line = i_stream_read_next_line(input)) != NULL) { /* skip tag */ printf("%s\r\n", line); while (*line != ' ' && *line != '\0') line++; if (*line == '\0') continue; line++; if (strcmp(line, "OK Begin compression.") == 0 || strcasecmp(line, "COMPRESS DEFLATE") == 0) break; } input2 = i_stream_create_deflate(input, TRUE); i_stream_unref(&input); while (i_stream_read_data(input2, &data, &size, 0) != -1) { if (fwrite(data, 1, size, stdout) != size) break; i_stream_skip(input2, size); } i_stream_unref(&input2); fflush(stdout); } struct client { int fd; struct io *io_client, *io_server; struct istream *input; struct ostream *output; bool compressed; }; static void client_input(struct client *client) { struct istream *input; struct ostream *output; unsigned char buf[1024]; ssize_t ret; ret = read(STDIN_FILENO, buf, sizeof(buf)); if (ret == 0) { if (client->compressed) { master_service_stop(master_service); return; } /* start compression */ i_info(""); input = i_stream_create_deflate(client->input, TRUE); output = o_stream_create_deflate(client->output, 6); i_stream_unref(&client->input); o_stream_unref(&client->output); client->input = input; client->output = output; client->compressed = TRUE; return; } if (ret < 0) i_fatal("read(stdin) failed: %m"); o_stream_nsend(client->output, buf, ret); } static void server_input(struct client *client) { const unsigned char *data; size_t size; if (i_stream_read(client->input) == -1) { if (client->input->stream_errno != 0) { i_fatal("read(server) failed: %s", i_stream_get_error(client->input)); } i_info("Server disconnected"); master_service_stop(master_service); return; } data = i_stream_get_data(client->input, &size); if (write(STDOUT_FILENO, data, size) < 0) i_fatal("write(stdout) failed: %m"); i_stream_skip(client->input, size); } static void cmd_zlibconnect(int argc ATTR_UNUSED, char *argv[]) { struct client client; struct ip_addr *ips; unsigned int ips_count; in_port_t port = 143; int fd, ret; if (argv[1] == NULL || (argv[2] != NULL && net_str2port(argv[2], &port) < 0)) help(&doveadm_cmd_zlibconnect); ret = net_gethostbyname(argv[1], &ips, &ips_count); if (ret != 0) { i_fatal("Host %s lookup failed: %s", argv[1], net_gethosterror(ret)); } if ((fd = net_connect_ip(&ips[0], port, NULL)) == -1) i_fatal("connect(%s, %u) failed: %m", argv[1], port); i_info("Connected to %s port %u. Ctrl-D starts compression", net_ip2addr(&ips[0]), port); i_zero(&client); client.fd = fd; client.input = i_stream_create_fd(fd, (size_t)-1, FALSE); client.output = o_stream_create_fd(fd, 0, FALSE); o_stream_set_no_error_handling(client.output, TRUE); client.io_client = io_add(STDIN_FILENO, IO_READ, client_input, &client); client.io_server = io_add(fd, IO_READ, server_input, &client); master_service_run(master_service, NULL); io_remove(&client.io_client); io_remove(&client.io_server); i_stream_unref(&client.input); o_stream_unref(&client.output); if (close(fd) < 0) i_fatal("close() failed: %m"); } #else static void cmd_dump_imapzlib(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { i_fatal("Dovecot compiled without zlib support"); } static void cmd_zlibconnect(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { i_fatal("Dovecot compiled without zlib support"); } #endif struct doveadm_cmd_dump doveadm_cmd_dump_zlib = { "imapzlib", test_dump_imapzlib, cmd_dump_imapzlib }; struct doveadm_cmd doveadm_cmd_zlibconnect = { cmd_zlibconnect, "zlibconnect", " []" }; dovecot-2.2.33.2/src/doveadm/doveadm-settings.h0000644000175000017500000000221513165463543016173 00000000000000#ifndef DOVEADM_SETTINGS_H #define DOVEADM_SETTINGS_H #include "net.h" /* */ enum dsync_features { DSYNC_FEATURE_EMPTY_HDR_WORKAROUND = 0x1, }; /* */ struct doveadm_settings { const char *base_dir; const char *libexec_dir; const char *mail_plugins; const char *mail_plugin_dir; bool auth_debug; const char *auth_socket_path; const char *doveadm_socket_path; unsigned int doveadm_worker_count; in_port_t doveadm_port; const char *doveadm_username; const char *doveadm_password; const char *doveadm_allowed_commands; const char *dsync_alt_char; const char *dsync_remote_cmd; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; const char *director_username_hash; const char *doveadm_api_key; const char *dsync_features; const char *dsync_hashed_headers; unsigned int dsync_commit_msgs_interval; const char *doveadm_http_rawlog_dir; enum dsync_features parsed_features; ARRAY(const char *) plugin_envs; }; extern const struct setting_parser_info doveadm_setting_parser_info; extern struct doveadm_settings *doveadm_settings; extern const struct master_service_settings *service_set; #endif dovecot-2.2.33.2/src/doveadm/doveadm-cmd.h0000644000175000017500000001321413123174404015064 00000000000000#ifndef DOVEADM_CMD_H #define DOVEADM_CMD_H #include "net.h" #define DOVEADM_CMD_PARAMS_START .parameters = (const struct doveadm_cmd_param[]){ #define DOVEADM_CMD_PARAM(optP, nameP, typeP, flagP ) { .short_opt = optP, .name = nameP, .type = typeP, .flags = flagP }, #define DOVEADM_CMD_PARAMS_END { .short_opt = '\0', .name = NULL, .type = CMD_PARAM_BOOL, .flags = CMD_PARAM_FLAG_NONE } } struct doveadm_cmd_ver2; struct doveadm_cmd_context; struct doveadm_mail_cmd_context; typedef void doveadm_command_t(int argc, char *argv[]); typedef enum { CMD_PARAM_BOOL = 0, /* value will contain 1 (not pointer) */ CMD_PARAM_INT64, /* ditto but contains number (not pointer) */ CMD_PARAM_IP, /* value contains struct ip_addr */ CMD_PARAM_STR, /* value contains const char* */ CMD_PARAM_ARRAY, /* value contains const char*[] */ CMD_PARAM_ISTREAM /* value contains struct istream* */ } doveadm_cmd_param_t; typedef enum { CMD_PARAM_FLAG_NONE = 0x0, CMD_PARAM_FLAG_POSITIONAL = 0x1, CMD_PARAM_FLAG_DO_NOT_EXPOSE = 0x2, } doveadm_cmd_param_flag_t; typedef enum { CMD_FLAG_NONE = 0x0, CMD_FLAG_HIDDEN = 0x1, CMD_FLAG_NO_PRINT = 0x2, } doveadm_cmd_flag_t; struct doveadm_cmd_param { char short_opt; const char *name; doveadm_cmd_param_t type; bool value_set; struct { bool v_bool; int64_t v_int64; const char* v_string; ARRAY_TYPE(const_string) v_array; struct ip_addr v_ip; struct istream* v_istream; } value; doveadm_cmd_param_flag_t flags; }; ARRAY_DEFINE_TYPE(doveadm_cmd_param_arr_t, struct doveadm_cmd_param); typedef void doveadm_command_ver2_t(struct doveadm_cmd_context *cctx); struct doveadm_cmd { doveadm_command_t *cmd; const char *name; const char *short_usage; }; struct doveadm_cmd_ver2 { doveadm_command_ver2_t *cmd; doveadm_command_t *old_cmd; struct doveadm_mail_cmd_context *(*mail_cmd)(void); const char *name; const char *usage; doveadm_cmd_flag_t flags; const struct doveadm_cmd_param *parameters; }; struct doveadm_cmd_context { const struct doveadm_cmd_ver2 *cmd; /* for help */ int argc; const struct doveadm_cmd_param *argv; const char *username; bool cli; bool tcp_server; struct ip_addr local_ip, remote_ip; in_port_t local_port, remote_port; struct client_connection *conn; }; ARRAY_DEFINE_TYPE(doveadm_cmd, struct doveadm_cmd); extern ARRAY_TYPE(doveadm_cmd) doveadm_cmds; ARRAY_DEFINE_TYPE(doveadm_cmd_ver2, struct doveadm_cmd_ver2); extern ARRAY_TYPE(doveadm_cmd_ver2) doveadm_cmds_ver2; extern struct doveadm_cmd doveadm_cmd_dump; extern struct doveadm_cmd doveadm_cmd_pw; extern struct doveadm_cmd doveadm_cmd_mailbox_mutf7; extern struct doveadm_cmd doveadm_cmd_sis_deduplicate; extern struct doveadm_cmd doveadm_cmd_sis_find; extern struct doveadm_cmd doveadm_cmd_zlibconnect; void doveadm_register_cmd(const struct doveadm_cmd *cmd); const struct doveadm_cmd * doveadm_cmd_find_with_args(const char *cmd_name, int *argc, const char *const *argv[]); void doveadm_register_auth_commands(void); void doveadm_register_auth_server_commands(void); void doveadm_register_director_commands(void); void doveadm_register_proxy_commands(void); void doveadm_register_log_commands(void); void doveadm_register_instance_commands(void); void doveadm_register_mount_commands(void); void doveadm_register_replicator_commands(void); void doveadm_register_dict_commands(void); void doveadm_register_fs_commands(void); void doveadm_cmds_init(void); void doveadm_cmds_deinit(void); void doveadm_cmd_ver2_to_cmd_wrapper(struct doveadm_cmd_context *cctx); void doveadm_cmd_ver2_to_mail_cmd_wrapper(struct doveadm_cmd_context *cctx); void doveadm_cmd_register_ver2(struct doveadm_cmd_ver2 *cmd); const struct doveadm_cmd_ver2 * doveadm_cmd_find_with_args_ver2(const char *cmd_name, int *argc, const char *const *argv[]); const struct doveadm_cmd_ver2 *doveadm_cmd_find_ver2(const char *cmd_name); /* Returns FALSE if cmd_name doesn't exist, TRUE if it exists. */ bool doveadm_cmd_try_run_ver2(const char *cmd_name, int argc, const char *const argv[], struct doveadm_cmd_context *cctx); /* Returns 0 if success, -1 if parameters were invalid. */ int doveadm_cmd_run_ver2(int argc, const char *const argv[], struct doveadm_cmd_context *cctx); bool doveadm_cmd_param_bool(const struct doveadm_cmd_context *cctx, const char *name, bool *value_r); bool doveadm_cmd_param_int64(const struct doveadm_cmd_context *cctx, const char *name, int64_t *value_r); bool doveadm_cmd_param_str(const struct doveadm_cmd_context *cctx, const char *name, const char **value_r); bool doveadm_cmd_param_ip(const struct doveadm_cmd_context *cctx, const char *name, struct ip_addr *value_r); bool doveadm_cmd_param_array(const struct doveadm_cmd_context *cctx, const char *name, const char *const **value_r); bool doveadm_cmd_param_istream(const struct doveadm_cmd_context *cctx, const char *name, struct istream **value_r); void doveadm_cmd_params_clean(ARRAY_TYPE(doveadm_cmd_param_arr_t) *pargv); void doveadm_cmd_params_null_terminate_arrays(ARRAY_TYPE(doveadm_cmd_param_arr_t) *pargv); extern struct doveadm_cmd_ver2 doveadm_cmd_service_stop_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_service_status_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_process_status_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_stop_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_reload_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_stats_reset_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_stats_dump_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_stats_top_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_penalty_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_kick_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_who_ver2; #endif dovecot-2.2.33.2/src/doveadm/doveadm-dict.c0000644000175000017500000002062713165463624015260 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dict.h" #include "doveadm.h" #include "doveadm-print.h" #include #include static void dict_cmd_help(doveadm_command_t *cmd); static int cmd_dict_init_full(int *argc, char **argv[], int own_arg_count, int key_arg_idx, doveadm_command_t *cmd, enum dict_iterate_flags *iter_flags, struct dict **dict_r) { const char *getopt_args = iter_flags == NULL ? "u:" : "1Ru:V"; struct dict *dict; const char *dict_uri, *error, *username = ""; int c; while ((c = getopt(*argc, *argv, getopt_args)) > 0) { switch (c) { case '1': i_assert(iter_flags != NULL); *iter_flags |= DICT_ITERATE_FLAG_EXACT_KEY; break; case 'R': i_assert(iter_flags != NULL); *iter_flags |= DICT_ITERATE_FLAG_RECURSE; break; case 'V': i_assert(iter_flags != NULL); *iter_flags |= DICT_ITERATE_FLAG_NO_VALUE; break; case 'u': username = optarg; break; default: dict_cmd_help(cmd); } } *argc -= optind; *argv += optind; if (*argc != 1 + own_arg_count) dict_cmd_help(cmd); dict_uri = (*argv)[0]; *argc += 1; *argv += 1; if (key_arg_idx >= 0) { const char *key = (*argv)[key_arg_idx]; if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) != 0 && strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) != 0) { i_error("Key must begin with '"DICT_PATH_PRIVATE "' or '"DICT_PATH_SHARED"': %s", key); doveadm_exit_code = EX_USAGE; return -1; } if (username[0] == '\0' && strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) { i_error("-u must be specified for "DICT_PATH_PRIVATE" keys"); doveadm_exit_code = EX_USAGE; return -1; } } dict_drivers_register_builtin(); if (dict_init(dict_uri, DICT_DATA_TYPE_STRING, username, doveadm_settings->base_dir, &dict, &error) < 0) { i_error("dict_init(%s) failed: %s", dict_uri, error); doveadm_exit_code = EX_TEMPFAIL; return -1; } *dict_r = dict; return 0; } static int cmd_dict_init(int *argc, char **argv[], int own_arg_count, int key_arg_idx, doveadm_command_t *cmd, struct dict **dict_r) { return cmd_dict_init_full(argc, argv, own_arg_count, key_arg_idx, cmd, NULL, dict_r); } struct doveadm_dict_ctx { pool_t pool; int ret; const char *const *values; const char *error; }; static void dict_lookup_callback(const struct dict_lookup_result *result, void *context) { struct doveadm_dict_ctx *ctx = context; ctx->ret = result->ret; ctx->values = result->values == NULL ? NULL : p_strarray_dup(ctx->pool, result->values); ctx->error = p_strdup(ctx->pool, result->error); } static void cmd_dict_get(int argc, char *argv[]) { struct doveadm_dict_ctx ctx; struct dict *dict; if (cmd_dict_init(&argc, &argv, 1, 0, cmd_dict_get, &dict) < 0) return; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("value", "", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); i_zero(&ctx); ctx.pool = pool_alloconly_create("doveadm dict lookup", 512); ctx.ret = -2; dict_lookup_async(dict, argv[0], dict_lookup_callback, &ctx); while (ctx.ret == -2) dict_wait(dict); if (ctx.ret < 0) { i_error("dict_lookup(%s) failed: %s", argv[0], ctx.error); doveadm_exit_code = EX_TEMPFAIL; } else if (ctx.ret == 0) { i_error("%s doesn't exist", argv[0]); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } else { unsigned int i, values_count = str_array_length(ctx.values); for (i = 1; i < values_count; i++) doveadm_print_header("value", "", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); for (i = 0; i < values_count; i++) doveadm_print(ctx.values[i]); } pool_unref(&ctx.pool); dict_deinit(&dict); } static void cmd_dict_set(int argc, char *argv[]) { struct dict *dict; struct dict_transaction_context *trans; if (cmd_dict_init(&argc, &argv, 2, 0, cmd_dict_set, &dict) < 0) return; trans = dict_transaction_begin(dict); dict_set(trans, argv[0], argv[1]); if (dict_transaction_commit(&trans) <= 0) { i_error("dict_transaction_commit() failed"); doveadm_exit_code = EX_TEMPFAIL; } dict_deinit(&dict); } static void cmd_dict_unset(int argc, char *argv[]) { struct dict *dict; struct dict_transaction_context *trans; if (cmd_dict_init(&argc, &argv, 1, 0, cmd_dict_unset, &dict) < 0) return; trans = dict_transaction_begin(dict); dict_unset(trans, argv[0]); if (dict_transaction_commit(&trans) <= 0) { i_error("dict_transaction_commit() failed"); doveadm_exit_code = EX_TEMPFAIL; } dict_deinit(&dict); } static void cmd_dict_inc(int argc, char *argv[]) { struct dict *dict; struct dict_transaction_context *trans; long long diff; int ret; if (cmd_dict_init(&argc, &argv, 2, 0, cmd_dict_inc, &dict) < 0) return; if (str_to_llong(argv[1], &diff) < 0) { i_error("Invalid diff: %s", argv[1]); doveadm_exit_code = EX_USAGE; dict_deinit(&dict); return; } trans = dict_transaction_begin(dict); dict_atomic_inc(trans, argv[0], diff); ret = dict_transaction_commit(&trans); if (ret < 0) { i_error("dict_transaction_commit() failed"); doveadm_exit_code = EX_TEMPFAIL; } else if (ret == 0) { i_error("%s doesn't exist", argv[0]); doveadm_exit_code = DOVEADM_EX_NOTFOUND; } dict_deinit(&dict); } static void cmd_dict_iter(int argc, char *argv[]) { struct dict *dict; struct dict_iterate_context *iter; enum dict_iterate_flags iter_flags = 0; const char *key, *value; if (cmd_dict_init_full(&argc, &argv, 1, 0, cmd_dict_iter, &iter_flags, &dict) < 0) return; doveadm_print_init(DOVEADM_PRINT_TYPE_TAB); doveadm_print_header_simple("key"); if ((iter_flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) doveadm_print_header_simple("value"); iter = dict_iterate_init(dict, argv[0], iter_flags); while (dict_iterate(iter, &key, &value)) { doveadm_print(key); if ((iter_flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) doveadm_print(value); } if (dict_iterate_deinit(&iter) < 0) { i_error("dict_iterate_deinit(%s) failed", argv[0]); doveadm_exit_code = EX_TEMPFAIL; } dict_deinit(&dict); } static struct doveadm_cmd_ver2 doveadm_cmd_dict[] = { { .name = "dict get", .old_cmd = cmd_dict_get, .usage = "[-u ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "dict set", .old_cmd = cmd_dict_set, .usage = "[-u ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "value", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "dict unset", .old_cmd = cmd_dict_unset, .usage = "[-u ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "dict inc", .old_cmd = cmd_dict_inc, .usage = "[-u ] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "difference", CMD_PARAM_INT64, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "dict iter", .old_cmd = cmd_dict_iter, .usage = "[-u ] [-1RV] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('1', "exact", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('R', "recurse", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('V', "no-value", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "prefix", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END } }; static void dict_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_dict); i++) { if (doveadm_cmd_dict[i].old_cmd == cmd) help_ver2(&doveadm_cmd_dict[i]); } i_unreached(); } void doveadm_register_dict_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_dict); i++) doveadm_cmd_register_ver2(&doveadm_cmd_dict[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-dump-index.c0000644000175000017500000006216113165463624016406 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hex-binary.h" #include "file-lock.h" #include "message-parser.h" #include "message-part-serialize.h" #include "mail-index-private.h" #include "mail-cache-private.h" #include "mail-index-modseq.h" #include "doveadm-dump.h" #include #include struct index_vsize_header { uint64_t vsize; uint32_t highest_uid; uint32_t message_count; }; struct maildir_index_header { uint32_t new_check_time, new_mtime, new_mtime_nsecs; uint32_t cur_check_time, cur_mtime, cur_mtime_nsecs; uint32_t uidlist_mtime, uidlist_mtime_nsecs, uidlist_size; }; struct mbox_index_header { uint64_t sync_size; uint32_t sync_mtime; uint8_t dirty_flag; uint8_t unused[3]; uint8_t mailbox_guid[16]; }; struct sdbox_index_header { uint32_t rebuild_count; guid_128_t mailbox_guid; uint8_t flags; uint8_t unused[3]; }; struct mdbox_index_header { uint32_t map_uid_validity; guid_128_t mailbox_guid; uint8_t flags; uint8_t unused[3]; }; struct mdbox_mail_index_record { uint32_t map_uid; uint32_t save_date; }; struct obox_mail_index_record { unsigned char guid[GUID_128_SIZE]; unsigned char oid[GUID_128_SIZE]; }; struct mobox_mail_index_header { uint32_t rebuild_count; uint32_t map_uid_validity; uint8_t unused[4]; guid_128_t mailbox_guid; }; struct mobox_mail_index_record { uint32_t map_uid; uint32_t save_date; }; struct mobox_map_mail_index_header { uint32_t rebuild_count; }; struct mobox_map_mail_index_record { uint32_t offset; uint32_t size; guid_128_t oid; }; struct mailbox_list_index_header { uint8_t refresh_flag; /* array of { uint32_t id; char name[]; } */ }; struct mailbox_list_index_record { uint32_t name_id; uint32_t parent_uid; guid_128_t guid; uint32_t uid_validity; }; struct mailbox_list_index_msgs_record { uint32_t messages; uint32_t unseen; uint32_t recent; uint32_t uidnext; }; struct mailbox_index_vsize { uint64_t vsize; uint32_t highest_uid; uint32_t message_count; }; struct fts_index_header { uint32_t last_indexed_uid; uint32_t settings_checksum; uint32_t unused; }; struct virtual_mail_index_header { uint32_t change_counter; uint32_t mailbox_count; uint32_t highest_mailbox_id; uint32_t search_args_crc32; }; struct virtual_mail_index_mailbox_record { uint32_t id; uint32_t name_len; uint32_t uid_validity; uint32_t next_uid; uint64_t highest_modseq; }; struct virtual_mail_index_record { uint32_t mailbox_id; uint32_t real_uid; }; struct mdbox_mail_index_map_record { uint32_t file_id; uint32_t offset; uint32_t size; }; static void dump_hdr(struct mail_index *index) { const struct mail_index_header *hdr = &index->map->hdr; unsigned int i; printf("version .................. = %u.%u\n", hdr->major_version, hdr->minor_version); printf("base header size ......... = %u\n", hdr->base_header_size); printf("header size .............. = %u\n", hdr->header_size); printf("record size .............. = %u\n", hdr->record_size); printf("compat flags ............. = %u\n", hdr->compat_flags); printf("index id ................. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid)); printf("flags .................... = %u\n", hdr->flags); printf("uid validity ............. = %u (%s)\n", hdr->uid_validity, unixdate2str(hdr->uid_validity)); printf("next uid ................. = %u\n", hdr->next_uid); printf("messages count ........... = %u\n", hdr->messages_count); printf("seen messages count ...... = %u\n", hdr->seen_messages_count); printf("deleted messages count ... = %u\n", hdr->deleted_messages_count); printf("first recent uid ......... = %u\n", hdr->first_recent_uid); printf("first unseen uid lowwater = %u\n", hdr->first_unseen_uid_lowwater); printf("first deleted uid lowwater = %u\n", hdr->first_deleted_uid_lowwater); printf("log file seq ............. = %u\n", hdr->log_file_seq); if (hdr->minor_version == 0) { printf("log file int offset ...... = %u\n", hdr->log_file_tail_offset); printf("log file ext offset ...... = %u\n", hdr->log_file_head_offset); } else { printf("log file tail offset ..... = %u\n", hdr->log_file_tail_offset); printf("log file head offset ..... = %u\n", hdr->log_file_head_offset); } if (hdr->minor_version >= 3) { printf("log2 rotate time ......... = %u (%s)\n", hdr->log2_rotate_time, unixdate2str(hdr->log2_rotate_time)); printf("last temp file scan ...... = %u (%s)\n", hdr->last_temp_file_scan, unixdate2str(hdr->last_temp_file_scan)); } printf("day stamp ................ = %u (%s)\n", hdr->day_stamp, unixdate2str(hdr->day_stamp)); for (i = 0; i < N_ELEMENTS(hdr->day_first_uid); i++) printf("day first uid[%u] ......... = %u\n", i, hdr->day_first_uid[i]); } static void dump_list_header(const void *data, size_t size) { const struct mailbox_list_index_header *hdr = data; const void *name_start, *p; size_t i, len; uint32_t id; printf(" - refresh_flag = %d\n", hdr->refresh_flag); for (i = sizeof(*hdr); i < size; ) { /* get id */ if (i + sizeof(id) > size) { printf(" - corrupted\n"); break; } memcpy(&id, CONST_PTR_OFFSET(data, i), sizeof(id)); i += sizeof(id); if (id == 0) break; /* get name */ p = memchr(CONST_PTR_OFFSET(data, i), '\0', size-i); if (p == NULL) { printf(" - corrupted\n"); break; } name_start = CONST_PTR_OFFSET(data, i); len = (const char *)p - (const char *)name_start; printf(" - %d : %.*s\n", id, (int)len, (const char *)name_start); i += len + 1; } } static void dump_box_name_header(const void *data, size_t size) { char *dest = t_malloc0(size + 1); memcpy(dest, data, size); for (size_t i = 0; i < size; i++) { if (dest[i] == '\0') dest[i] = '\n'; } printf(" %s\n", t_strarray_join(t_strsplit(dest, "\n"), "\n ")); } static void dump_extension_header(struct mail_index *index, const struct mail_index_ext *ext) { const void *data; void *buf; if (strcmp(ext->name, MAIL_INDEX_EXT_KEYWORDS) == 0) return; /* add some padding, since we don't bother to handle undersized headers correctly */ buf = t_malloc0(MALLOC_ADD(ext->hdr_size, 128)); data = CONST_PTR_OFFSET(index->map->hdr_base, ext->hdr_offset); memcpy(buf, data, ext->hdr_size); data = buf; if (strcmp(ext->name, "hdr-vsize") == 0) { const struct index_vsize_header *hdr = data; printf("header\n"); printf(" - highest uid . = %u\n", hdr->highest_uid); printf(" - message count = %u\n", hdr->message_count); printf(" - vsize ....... = %llu\n", (unsigned long long)hdr->vsize); } else if (strcmp(ext->name, "maildir") == 0) { const struct maildir_index_header *hdr = data; printf("header\n"); printf(" - new_check_time .... = %s\n", unixdate2str(hdr->new_check_time)); printf(" - new_mtime ......... = %s\n", unixdate2str(hdr->new_mtime)); printf(" - new_mtime_nsecs ... = %u\n", hdr->new_mtime_nsecs); printf(" - cur_check_time .... = %s\n", unixdate2str(hdr->cur_check_time)); printf(" - cur_mtime ......... = %s\n", unixdate2str(hdr->cur_mtime)); printf(" - cur_mtime_nsecs.... = %u\n", hdr->cur_mtime_nsecs); printf(" - uidlist_mtime ..... = %s\n", unixdate2str(hdr->uidlist_mtime)); printf(" - uidlist_mtime_nsecs = %u\n", hdr->uidlist_mtime_nsecs); printf(" - uidlist_size ...... = %u\n", hdr->uidlist_size); } else if (strcmp(ext->name, "mbox") == 0) { const struct mbox_index_header *hdr = data; printf("header\n"); printf(" - sync_mtime . = %s\n", unixdate2str(hdr->sync_mtime)); printf(" - sync_size .. = %llu\n", (unsigned long long)hdr->sync_size); printf(" - dirty_flag . = %d\n", hdr->dirty_flag); printf(" - mailbox_guid = %s\n", guid_128_to_string(hdr->mailbox_guid)); } else if (strcmp(ext->name, "mdbox-hdr") == 0) { const struct mdbox_index_header *hdr = data; printf("header\n"); printf(" - map_uid_validity .. = %u\n", hdr->map_uid_validity); printf(" - mailbox_guid ...... = %s\n", guid_128_to_string(hdr->mailbox_guid)); printf(" - flags ............. = 0x%x\n", hdr->flags); } else if (strcmp(ext->name, "dbox-hdr") == 0) { const struct sdbox_index_header *hdr = data; printf("header\n"); printf(" - rebuild_count . = %u\n", hdr->rebuild_count); printf(" - mailbox_guid .. = %s\n", guid_128_to_string(hdr->mailbox_guid)); printf(" - flags ......... = 0x%x\n", hdr->flags); } else if (strcmp(ext->name, "mobox-hdr") == 0) { const struct mobox_mail_index_header *hdr = data; printf("header\n"); printf(" - rebuild_count .. = %u\n", hdr->rebuild_count); printf(" - map_uid_validity .. = %u\n", hdr->map_uid_validity); printf(" - mailbox_guid ...... = %s\n", guid_128_to_string(hdr->mailbox_guid)); } else if (strcmp(ext->name, "mobox-map") == 0) { const struct mobox_map_mail_index_header *hdr = data; printf("header\n"); printf(" - rebuild_count .. = %u\n", hdr->rebuild_count); } else if (strcmp(ext->name, "modseq") == 0) { const struct mail_index_modseq_header *hdr = data; printf("header\n"); printf(" - highest_modseq = %llu\n", (unsigned long long)hdr->highest_modseq); printf(" - log_seq ...... = %u\n", hdr->log_seq); printf(" - log_offset ... = %u\n", hdr->log_offset); } else if (strcmp(ext->name, "fts") == 0) { const struct fts_index_header *hdr = data; printf("header\n"); printf(" - last_indexed_uid ..... = %u\n", hdr->last_indexed_uid); printf(" - settings_checksum .... = %u\n", hdr->settings_checksum); } else if (strcmp(ext->name, "virtual") == 0) { const struct virtual_mail_index_header *hdr = data; const struct virtual_mail_index_mailbox_record *rec; const unsigned char *name; unsigned int i; printf("header\n"); printf(" - change_counter ... = %u\n", hdr->change_counter); printf(" - mailbox_count .... = %u\n", hdr->mailbox_count); printf(" - highest_mailbox_id = %u\n", hdr->highest_mailbox_id); printf(" - search_args_crc32 = %u\n", hdr->search_args_crc32); rec = CONST_PTR_OFFSET(hdr, sizeof(*hdr)); name = CONST_PTR_OFFSET(rec, sizeof(*rec) * hdr->mailbox_count); for (i = 0; i < hdr->mailbox_count; i++, rec++) { printf("mailbox %s:\n", t_strndup(name, rec->name_len)); printf(" - id ........... = %u\n", rec->id); printf(" - uid_validity . = %u\n", rec->uid_validity); printf(" - next_uid ..... = %u\n", rec->next_uid); printf(" - highest_modseq = %llu\n", (unsigned long long)rec->highest_modseq); name += rec->name_len; } } else if (strcmp(ext->name, "list") == 0) { printf("header ........ = %s\n", binary_to_hex(data, ext->hdr_size)); dump_list_header(data, ext->hdr_size); } else if (strcmp(ext->name, "box-name") == 0) { printf("header ........ = %s\n", binary_to_hex(data, ext->hdr_size)); dump_box_name_header(data, ext->hdr_size); } else { printf("header ........ = %s\n", binary_to_hex(data, ext->hdr_size)); } } static void dump_extensions(struct mail_index *index) { const struct mail_index_ext *extensions; unsigned int i, count; if (array_is_created(&index->map->extensions)) extensions = array_get(&index->map->extensions, &count); else count = 0; if (count == 0) { printf("no extensions\n"); return; } for (i = 0; i < count; i++) { const struct mail_index_ext *ext = &extensions[i]; printf("-- Extension %u --\n", i); printf("name ........ = %s\n", ext->name); printf("hdr_size .... = %u\n", ext->hdr_size); printf("reset_id .... = %u\n", ext->reset_id); printf("record_offset = %u\n", ext->record_offset); printf("record_size . = %u\n", ext->record_size); printf("record_align = %u\n", ext->record_align); if (ext->hdr_size > 0) T_BEGIN { dump_extension_header(index, ext); } T_END; } } static void dump_keywords(struct mail_index *index) { const unsigned int *kw_indexes; const char *const *keywords; unsigned int i, count; printf("-- Keywords --\n"); if (!array_is_created(&index->map->keyword_idx_map)) return; kw_indexes = array_get(&index->map->keyword_idx_map, &count); if (count == 0) return; keywords = array_idx(&index->keywords, 0); for (i = 0; i < count; i++) printf("%3u = %s\n", i, keywords[kw_indexes[i]]); } static const char *cache_decision2str(enum mail_cache_decision_type type) { const char *str; switch (type & ~MAIL_CACHE_DECISION_FORCED) { case MAIL_CACHE_DECISION_NO: str = "no"; break; case MAIL_CACHE_DECISION_TEMP: str = "tmp"; break; case MAIL_CACHE_DECISION_YES: str = "yes"; break; default: return t_strdup_printf("0x%x", type); } if ((type & MAIL_CACHE_DECISION_FORCED) != 0) str = t_strconcat(str, "!", NULL); return str; } #define CACHE_TYPE_IS_FIXED_SIZE(type) \ ((type) == MAIL_CACHE_FIELD_FIXED_SIZE || \ (type) == MAIL_CACHE_FIELD_BITMASK) static const char *cache_type2str(enum mail_cache_field_type type) { switch (type) { case MAIL_CACHE_FIELD_FIXED_SIZE: return "fix"; case MAIL_CACHE_FIELD_VARIABLE_SIZE: return "var"; case MAIL_CACHE_FIELD_STRING: return "str"; case MAIL_CACHE_FIELD_BITMASK: return "bit"; case MAIL_CACHE_FIELD_HEADER: return "hdr"; default: return t_strdup_printf("0x%x", type); } } static void dump_cache_hdr(struct mail_cache *cache) { const struct mail_cache_header *hdr; const struct mail_cache_field *fields, *field; unsigned int i, count, cache_idx; (void)mail_cache_open_and_verify(cache); if (MAIL_CACHE_IS_UNUSABLE(cache)) { printf("cache is unusable\n"); return; } hdr = cache->hdr; printf("major version ........ = %u\n", hdr->major_version); printf("minor version ........ = %u\n", hdr->minor_version); printf("indexid .............. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid)); printf("file_seq ............. = %u (%s) (%d compressions)\n", hdr->file_seq, unixdate2str(hdr->file_seq), hdr->file_seq - hdr->indexid); printf("continued_record_count = %u\n", hdr->continued_record_count); printf("record_count ......... = %u\n", hdr->record_count); printf("used_file_size (old) . = %u\n", hdr->backwards_compat_used_file_size); printf("deleted_record_count . = %u\n", hdr->deleted_record_count); printf("field_header_offset .. = %u (0x%08x nontranslated)\n", mail_index_offset_to_uint32(hdr->field_header_offset), hdr->field_header_offset); printf("-- Cache fields --\n"); fields = mail_cache_register_get_list(cache, pool_datastack_create(), &count); printf( " # Name Type Size Dec Last used\n"); for (i = 0; i < cache->file_fields_count; i++) { cache_idx = cache->file_field_map[i]; field = &fields[cache_idx]; printf("%2u: %-44s %-4s ", i, field->name, cache_type2str(field->type)); if (field->field_size != (uint32_t)-1 || CACHE_TYPE_IS_FIXED_SIZE(field->type)) printf("%4u ", field->field_size); else printf(" - "); printf("%-4s %.16s\n", cache_decision2str(field->decision), unixdate2str(field->last_used)); } } static void dump_message_part(string_t *str, const struct message_part *part) { for (; part != NULL; part = part->next) { str_append_c(str, '('); str_printfa(str, "pos=%"PRIuUOFF_T" ", part->physical_pos); str_printfa(str, "hdr.p=%"PRIuUOFF_T" ", part->header_size.physical_size); str_printfa(str, "hdr.v=%"PRIuUOFF_T" ", part->header_size.virtual_size); str_printfa(str, "body.p=%"PRIuUOFF_T" ", part->body_size.physical_size); str_printfa(str, "body.v=%"PRIuUOFF_T" ", part->body_size.virtual_size); str_printfa(str, "flags=%x", part->flags); if (part->children != NULL) { str_append_c(str, ' '); dump_message_part(str, part->children); } str_append_c(str, ')'); } } static void dump_cache_mime_parts(string_t *str, const void *data, unsigned int size) { const struct message_part *part; const char *error; str_append_c(str, ' '); part = message_part_deserialize(pool_datastack_create(), data, size, &error); if (part == NULL) { str_printfa(str, "error: %s", error); return; } dump_message_part(str, part); } static void dump_cache(struct mail_cache_view *cache_view, unsigned int seq) { struct mail_cache_lookup_iterate_ctx iter; const struct mail_cache_record *prev_rec = NULL; const struct mail_cache_field *field; struct mail_cache_iterate_field iter_field; const void *data; unsigned int size; string_t *str; int ret; str = t_str_new(512); mail_cache_lookup_iter_init(cache_view, seq, &iter); while ((ret = mail_cache_lookup_iter_next(&iter, &iter_field)) > 0) { if (iter.rec != prev_rec) { printf(" - cache offset=%u size=%u, prev_offset = %u\n", iter.offset, iter.rec->size, iter.rec->prev_offset); prev_rec = iter.rec; } field = &cache_view->cache->fields[iter_field.field_idx].field; data = iter_field.data; size = iter_field.size; str_truncate(str, 0); str_printfa(str, " - %s: ", field->name); switch (field->type) { case MAIL_CACHE_FIELD_FIXED_SIZE: if (size == sizeof(uint32_t)) { uint32_t value; memcpy(&value, data, sizeof(value)); str_printfa(str, "%u ", value); } else if (size == sizeof(uint64_t)) { uint64_t value; memcpy(&value, data, sizeof(value)); str_printfa(str, "%llu ", (unsigned long long)value); } /* fall through */ case MAIL_CACHE_FIELD_VARIABLE_SIZE: case MAIL_CACHE_FIELD_BITMASK: str_printfa(str, "(%s)", binary_to_hex(data, size)); if (strcmp(field->name, "mime.parts") == 0) dump_cache_mime_parts(str, data, size); break; case MAIL_CACHE_FIELD_STRING: if (size > 0) str_printfa(str, "%.*s", (int)size, (const char *)data); break; case MAIL_CACHE_FIELD_HEADER: { const uint32_t *lines = data; int i; for (i = 0;; i++) { if (size < sizeof(uint32_t)) { if (i == 0 && size == 0) { /* header doesn't exist */ break; } str_append(str, "\n - BROKEN: header field doesn't end with 0 line"); size = 0; break; } size -= sizeof(uint32_t); data = CONST_PTR_OFFSET(data, sizeof(uint32_t)); if (lines[i] == 0) break; if (i > 0) str_append(str, ", "); str_printfa(str, "%u", lines[i]); } if (i == 1 && size > 0 && ((const char *)data)[size-1] == '\n') size--; if (size > 0) str_printfa(str, ": %.*s", (int)size, (const char *)data); break; } case MAIL_CACHE_FIELD_COUNT: i_unreached(); break; } printf("%s\n", str_c(str)); } if (ret < 0) printf(" - broken cache\n"); } static const char *flags2str(enum mail_flags flags) { string_t *str; str = t_str_new(64); str_append_c(str, '('); if ((flags & MAIL_SEEN) != 0) str_append(str, "Seen "); if ((flags & MAIL_ANSWERED) != 0) str_append(str, "Answered "); if ((flags & MAIL_FLAGGED) != 0) str_append(str, "Flagged "); if ((flags & MAIL_DELETED) != 0) str_append(str, "Deleted "); if ((flags & MAIL_DRAFT) != 0) str_append(str, "Draft "); if (str_len(str) == 1) return ""; str_truncate(str, str_len(str)-1); str_append_c(str, ')'); return str_c(str); } static void dump_record(struct mail_index_view *view, unsigned int seq) { struct mail_index *index = mail_index_view_get_index(view); const struct mail_index_record *rec; const struct mail_index_registered_ext *ext; const void *data; unsigned int i, ext_count; string_t *str; bool expunged; rec = mail_index_lookup(view, seq); printf("RECORD: seq=%u, uid=%u, flags=0x%02x %s\n", seq, rec->uid, rec->flags, flags2str(rec->flags)); str = t_str_new(256); ext = array_get(&index->extensions, &ext_count); for (i = 0; i < ext_count; i++) { mail_index_lookup_ext(view, seq, i, &data, &expunged); if (data == NULL || ext[i].record_size == 0) continue; str_truncate(str, 0); str_printfa(str, " - ext %d %-10s: ", i, ext[i].name); if (ext[i].record_size == sizeof(uint16_t) && ext[i].record_align == sizeof(uint16_t)) str_printfa(str, "%10u", *((const uint16_t *)data)); else if (ext[i].record_size == sizeof(uint32_t) && ext[i].record_align == sizeof(uint32_t)) str_printfa(str, "%10u", *((const uint32_t *)data)); else if (ext[i].record_size == sizeof(uint64_t) && ext[i].record_align == sizeof(uint64_t)) { uint64_t value = *((const uint64_t *)data); str_printfa(str, "%10llu", (unsigned long long)value); } else { str_append(str, " "); } str_printfa(str, " (%s)", binary_to_hex(data, ext[i].record_size)); printf("%s\n", str_c(str)); if (strcmp(ext[i].name, "virtual") == 0) { const struct virtual_mail_index_record *vrec = data; printf(" : mailbox_id = %u\n", vrec->mailbox_id); printf(" : real_uid = %u\n", vrec->real_uid); } else if (strcmp(ext[i].name, "map") == 0) { const struct mdbox_mail_index_map_record *mrec = data; printf(" : file_id = %u\n", mrec->file_id); printf(" : offset = %u\n", mrec->offset); printf(" : size = %u\n", mrec->size); } else if (strcmp(ext[i].name, "mdbox") == 0) { const struct mdbox_mail_index_record *drec = data; printf(" : map_uid = %u\n", drec->map_uid); printf(" : save_date = %u (%s)\n", drec->save_date, unixdate2str(drec->save_date)); } else if (strcmp(ext[i].name, "obox") == 0) { const struct obox_mail_index_record *orec = data; printf(" : guid = %s\n", guid_128_to_string(orec->guid)); printf(" : oid = %s\n", binary_to_hex(orec->oid, ext[i].record_size - sizeof(orec->guid))); } else if (strcmp(ext[i].name, "mobox") == 0) { const struct mobox_mail_index_record *orec = data; printf(" : map_uid = %u\n", orec->map_uid); printf(" : save_date = %u (%s)\n", orec->save_date, unixdate2str(orec->save_date)); } else if (strcmp(ext[i].name, "mobox-map") == 0) { const struct mobox_map_mail_index_record *orec = data; printf(" : offset = %u\n", orec->offset); printf(" : size = %u\n", orec->size); printf(" : oid = %s\n", guid_128_to_string(orec->oid)); } else if (strcmp(ext[i].name, "list") == 0) { const struct mailbox_list_index_record *lrec = data; printf(" : name_id = %u\n", lrec->name_id); printf(" : parent_uid = %u\n", lrec->parent_uid); printf(" : guid = %s\n", guid_128_to_string(lrec->guid)); printf(" : uid_validity = %u\n", lrec->uid_validity); } else if (strcmp(ext[i].name, "msgs") == 0) { const struct mailbox_list_index_msgs_record *lrec = data; printf(" : messages = %u\n", lrec->messages); printf(" : unseen = %u\n", lrec->unseen); printf(" : recent = %u\n", lrec->recent); printf(" : uidnext = %u\n", lrec->uidnext); } else if (strcmp(ext[i].name, "vsize") == 0) { const struct mailbox_index_vsize *vrec = data; printf(" : vsize = %llu\n", (unsigned long long)vrec->vsize); printf(" : highest_uid = %u\n", vrec->highest_uid); printf(" : message_count = %u\n", vrec->message_count); } } } static bool dir_has_index(const char *dir, const char *name) { struct stat st; return stat(t_strconcat(dir, "/", name, NULL), &st) == 0 || stat(t_strconcat(dir, "/", name, ".log", NULL), &st) == 0; } static struct mail_index *path_open_index(const char *path) { struct stat st; const char *p; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { if (dir_has_index(path, "dovecot.index")) return mail_index_alloc(path, "dovecot.index"); else if (dir_has_index(path, "dovecot.map.index")) return mail_index_alloc(path, "dovecot.map.index"); else return NULL; } else if ((p = strrchr(path, '/')) != NULL) return mail_index_alloc(t_strdup_until(path, p), p + 1); else return mail_index_alloc(".", path); } static void cmd_dump_index(int argc ATTR_UNUSED, char *argv[]) { struct mail_index *index; struct mail_index_view *view; struct mail_cache_view *cache_view; unsigned int seq, uid = 0; index = path_open_index(argv[1]); if (index == NULL || mail_index_open(index, MAIL_INDEX_OPEN_FLAG_READONLY) <= 0) i_fatal("Couldn't open index %s", argv[1]); if (argv[2] != NULL) { if (str_to_uint(argv[2], &uid) < 0) i_fatal("Invalid uid number %s", argv[2]); } view = mail_index_view_open(index); cache_view = mail_cache_view_open(index->cache, view); if (uid == 0) { printf("-- INDEX: %s\n", index->filepath); dump_hdr(index); dump_extensions(index); dump_keywords(index); printf("\n-- CACHE: %s\n", index->cache->filepath); dump_cache_hdr(index->cache); printf("\n-- RECORDS: %u\n", index->map->hdr.messages_count); } for (seq = 1; seq <= index->map->hdr.messages_count; seq++) { if (uid == 0 || mail_index_lookup(view, seq)->uid == uid) { T_BEGIN { dump_record(view, seq); dump_cache(cache_view, seq); printf("\n"); } T_END; } } mail_cache_view_close(&cache_view); mail_index_view_close(&view); mail_index_close(index); mail_index_free(&index); } static bool test_dump_index(const char *path) { struct mail_index *index; bool ret; index = path_open_index(path); if (index == NULL) return FALSE; ret = mail_index_open(index, MAIL_INDEX_OPEN_FLAG_READONLY) > 0; if (ret > 0) mail_index_close(index); mail_index_free(&index); return ret; } struct doveadm_cmd_dump doveadm_cmd_dump_index = { "index", test_dump_index, cmd_dump_index }; dovecot-2.2.33.2/src/doveadm/doveadm-auth-server.c0000644000175000017500000003553413165463624016605 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "wildcard-match.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "auth-client.h" #include "auth-master.h" #include "auth-server-connection.h" #include "master-auth.h" #include "master-login-auth.h" #include "mail-storage-service.h" #include "mail-user.h" #include "ostream.h" #include "json-parser.h" #include "doveadm.h" #include "doveadm-print.h" #include #include struct authtest_input { pool_t pool; const char *username; const char *master_user; const char *password; struct auth_user_info info; bool success; struct auth_client_request *request; struct master_auth_request master_auth_req; unsigned int auth_id; unsigned int auth_pid; const char *auth_cookie; }; static struct auth_master_connection * doveadm_get_auth_master_conn(const char *auth_socket_path) { enum auth_master_flags flags = 0; if (doveadm_debug) flags |= AUTH_MASTER_FLAG_DEBUG; return auth_master_init(auth_socket_path, flags); } static int cmd_user_input(struct auth_master_connection *conn, const struct authtest_input *input, const char *show_field, bool userdb) { const char *lookup_name = userdb ? "userdb lookup" : "passdb lookup"; pool_t pool; const char *updated_username = NULL, *const *fields, *p; int ret; pool = pool_alloconly_create("auth master lookup", 1024); if (userdb) { ret = auth_master_user_lookup(conn, input->username, &input->info, pool, &updated_username, &fields); } else { ret = auth_master_pass_lookup(conn, input->username, &input->info, pool, &fields); } if (ret < 0) { const char *msg; if (fields[0] == NULL) { msg = t_strdup_printf("\"error\":\"%s failed\"", lookup_name); } else { msg = t_strdup_printf("\"error\":\"%s failed: %s\"", lookup_name, fields[0]); } o_stream_nsend_str(doveadm_print_ostream, msg); ret = -1; } else if (ret == 0) { o_stream_nsend_str(doveadm_print_ostream, t_strdup_printf("\"error\":\"%s: user doesn't exist\"", lookup_name)); } else if (show_field != NULL) { size_t show_field_len = strlen(show_field); string_t *json_field = t_str_new(show_field_len+1); json_append_escaped(json_field, show_field); o_stream_nsend_str(doveadm_print_ostream, t_strdup_printf("\"%s\":", str_c(json_field))); for (; *fields != NULL; fields++) { if (strncmp(*fields, show_field, show_field_len) == 0 && (*fields)[show_field_len] == '=') { string_t *jsonval = t_str_new(32); json_append_escaped(jsonval, *fields + show_field_len + 1); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval)); o_stream_nsend_str(doveadm_print_ostream, "\""); } } } else { string_t *jsonval = t_str_new(64); o_stream_nsend_str(doveadm_print_ostream, "\"source\":\""); o_stream_nsend_str(doveadm_print_ostream, userdb ? "userdb\"" : "passdb\""); if (updated_username != NULL) { o_stream_nsend_str(doveadm_print_ostream, ",\"updated_username\":\""); str_truncate(jsonval, 0); json_append_escaped(jsonval, updated_username); o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval)); o_stream_nsend_str(doveadm_print_ostream, "\""); } for (; *fields != NULL; fields++) { const char *field = *fields; if (*field == '\0') continue; p = strchr(*fields, '='); str_truncate(jsonval, 0); if (p != NULL) { field = t_strcut(*fields, '='); } str_truncate(jsonval, 0); json_append_escaped(jsonval, field); o_stream_nsend_str(doveadm_print_ostream, ",\""); o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval)); o_stream_nsend_str(doveadm_print_ostream, "\":"); if (p != NULL) { str_truncate(jsonval, 0); json_append_escaped(jsonval, p+1); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval)); o_stream_nsend_str(doveadm_print_ostream, "\""); } else { o_stream_nsend_str(doveadm_print_ostream, "true"); } } } return ret; } static void auth_user_info_parse(struct auth_user_info *info, const char *arg) { if (strncmp(arg, "service=", 8) == 0) info->service = arg + 8; else if (strncmp(arg, "lip=", 4) == 0) { if (net_addr2ip(arg + 4, &info->local_ip) < 0) i_fatal("lip: Invalid ip"); } else if (strncmp(arg, "rip=", 4) == 0) { if (net_addr2ip(arg + 4, &info->remote_ip) < 0) i_fatal("rip: Invalid ip"); } else if (strncmp(arg, "lport=", 6) == 0) { if (net_str2port(arg + 6, &info->local_port) < 0) i_fatal("lport: Invalid port number"); } else if (strncmp(arg, "rport=", 6) == 0) { if (net_str2port(arg + 6, &info->remote_port) < 0) i_fatal("rport: Invalid port number"); } else { i_fatal("Unknown -x argument: %s", arg); } } static void cmd_user_list(struct auth_master_connection *conn, const struct authtest_input *input, char *const *users) { struct auth_master_user_list_ctx *ctx; const char *username, *user_mask = "*"; unsigned int i; if (users[0] != NULL && users[1] == NULL) user_mask = users[0]; ctx = auth_master_user_list_init(conn, user_mask, &input->info); while ((username = auth_master_user_list_next(ctx)) != NULL) { for (i = 0; users[i] != NULL; i++) { if (wildcard_match_icase(username, users[i])) break; } if (users[i] != NULL) printf("%s\n", username); } if (auth_master_user_list_deinit(&ctx) < 0) i_fatal("user listing failed"); } static void cmd_auth_cache_flush(int argc, char *argv[]) { const char *master_socket_path = NULL; struct auth_master_connection *conn; unsigned int count; int c; while ((c = getopt(argc, argv, "a:")) > 0) { switch (c) { case 'a': master_socket_path = optarg; break; default: doveadm_exit_code = EX_USAGE; return; } } argv += optind; if (master_socket_path == NULL) { master_socket_path = t_strconcat(doveadm_settings->base_dir, "/auth-master", NULL); } conn = doveadm_get_auth_master_conn(master_socket_path); if (auth_master_cache_flush(conn, (void *)argv, &count) < 0) { i_error("Cache flush failed"); doveadm_exit_code = EX_TEMPFAIL; } else { doveadm_print_init("formatted"); doveadm_print_formatted_set_format("%{entries} cache entries flushed\n"); doveadm_print_header_simple("entries"); doveadm_print_num(count); } auth_master_deinit(&conn); } static void cmd_user_mail_input_field(const char *key, const char *value, const char *show_field) { string_t *jvalue = t_str_new(128); if (show_field != NULL && strcmp(show_field, key) != 0) return; json_append_escaped(jvalue, key); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, str_c(jvalue)); o_stream_nsend_str(doveadm_print_ostream, "\":\""); str_truncate(jvalue, 0); json_append_escaped(jvalue, value); o_stream_nsend_str(doveadm_print_ostream, str_c(jvalue)); o_stream_nsend_str(doveadm_print_ostream, "\""); } static void cmd_user_mail_print_fields(const struct authtest_input *input, struct mail_user *user, const char *const *userdb_fields, const char *show_field) { const struct mail_storage_settings *mail_set; const char *key, *value; unsigned int i; if (strcmp(input->username, user->username) != 0) { cmd_user_mail_input_field("user", user->username, show_field); o_stream_nsend_str(doveadm_print_ostream, ","); } cmd_user_mail_input_field("uid", user->set->mail_uid, show_field); o_stream_nsend_str(doveadm_print_ostream, ","); cmd_user_mail_input_field("gid", user->set->mail_gid, show_field); o_stream_nsend_str(doveadm_print_ostream, ","); cmd_user_mail_input_field("home", user->set->mail_home, show_field); mail_set = mail_user_set_get_storage_set(user); o_stream_nsend_str(doveadm_print_ostream, ","); cmd_user_mail_input_field("mail", mail_set->mail_location, show_field); if (userdb_fields != NULL) { for (i = 0; userdb_fields[i] != NULL; i++) { value = strchr(userdb_fields[i], '='); if (value != NULL) key = t_strdup_until(userdb_fields[i], value++); else { key = userdb_fields[i]; value = ""; } if (strcmp(key, "uid") != 0 && strcmp(key, "gid") != 0 && strcmp(key, "home") != 0 && strcmp(key, "mail") != 0 && *key != '\0') { o_stream_nsend_str(doveadm_print_ostream, ","); cmd_user_mail_input_field(key, value, show_field); } } } } static int cmd_user_mail_input(struct mail_storage_service_ctx *storage_service, const struct authtest_input *input, const char *show_field, const char *expand_field) { struct mail_storage_service_input service_input; struct mail_storage_service_user *service_user; struct mail_user *user; const char *error, *const *userdb_fields; pool_t pool; int ret; i_zero(&service_input); service_input.module = "mail"; service_input.service = input->info.service; service_input.username = input->username; service_input.local_ip = input->info.local_ip; service_input.local_port = input->info.local_port; service_input.remote_ip = input->info.remote_ip; service_input.remote_port = input->info.remote_port; service_input.debug = input->info.debug; pool = pool_alloconly_create("userdb fields", 1024); mail_storage_service_save_userdb_fields(storage_service, pool, &userdb_fields); if ((ret = mail_storage_service_lookup_next(storage_service, &service_input, &service_user, &user, &error)) <= 0) { pool_unref(&pool); if (ret < 0) return -1; string_t *username = t_str_new(32); json_append_escaped(username, input->username); o_stream_nsend_str(doveadm_print_ostream, t_strdup_printf("\"error\":\"userdb lookup: user %s doesn't exist\"", str_c(username)) ); return 0; } if (expand_field == NULL) cmd_user_mail_print_fields(input, user, userdb_fields, show_field); else { string_t *str = t_str_new(128); var_expand_with_funcs(str, expand_field, mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); string_t *value = t_str_new(128); json_append_escaped(value, expand_field); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, str_c(value)); o_stream_nsend_str(doveadm_print_ostream, "\":\""); str_truncate(value, 0); json_append_escaped(value, str_c(str)); o_stream_nsend_str(doveadm_print_ostream, str_c(value)); o_stream_nsend_str(doveadm_print_ostream, "\""); } mail_user_unref(&user); mail_storage_service_user_unref(&service_user); pool_unref(&pool); return 1; } static void cmd_user_ver2(struct doveadm_cmd_context *cctx) { const char * const *optval; const char *auth_socket_path = NULL; struct auth_master_connection *conn; struct authtest_input input; const char *show_field = NULL, *expand_field = NULL; struct mail_storage_service_ctx *storage_service = NULL; bool have_wildcards, userdb_only = FALSE, first = TRUE; int ret; if (!doveadm_cmd_param_str(cctx, "socket-path", &auth_socket_path)) auth_socket_path = doveadm_settings->auth_socket_path; (void)doveadm_cmd_param_str(cctx, "expand-field", &expand_field); (void)doveadm_cmd_param_str(cctx, "field", &show_field); (void)doveadm_cmd_param_bool(cctx, "userdb-only", &userdb_only); i_zero(&input); if (doveadm_cmd_param_array(cctx, "auth-info", &optval)) for(;*optval != NULL; optval++) auth_user_info_parse(&input.info, *optval); if (!doveadm_cmd_param_array(cctx, "user-mask", &optval)) { doveadm_exit_code = EX_USAGE; i_error("No user(s) specified"); return; } if (expand_field != NULL && userdb_only) { i_error("-e can't be used with -u"); doveadm_exit_code = EX_USAGE; return; } if (expand_field != NULL && show_field != NULL) { i_error("-e can't be used with -f"); doveadm_exit_code = EX_USAGE; return; } conn = doveadm_get_auth_master_conn(auth_socket_path); have_wildcards = FALSE; for(const char *const *val = optval; *val != NULL; val++) { if (strchr(*val, '*') != NULL || strchr(*val, '?') != NULL) { have_wildcards = TRUE; break; } } if (have_wildcards) { cmd_user_list(conn, &input, (char*const*)optval); auth_master_deinit(&conn); return; } if (!userdb_only) { storage_service = mail_storage_service_init(master_service, NULL, MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES | MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS); mail_storage_service_set_auth_conn(storage_service, conn); conn = NULL; } string_t *json = t_str_new(64); o_stream_nsend_str(doveadm_print_ostream, "{"); input.info.local_ip = cctx->local_ip; input.info.local_port = cctx->local_port; input.info.remote_ip = cctx->remote_ip; input.info.remote_port = cctx->remote_port; for(const char *const *val = optval; *val != NULL; val++) { str_truncate(json, 0); json_append_escaped(json, *val); input.username = *val; if (first) first = FALSE; else o_stream_nsend_str(doveadm_print_ostream, ","); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, str_c(json)); o_stream_nsend_str(doveadm_print_ostream, "\""); o_stream_nsend_str(doveadm_print_ostream, ":{"); ret = !userdb_only ? cmd_user_mail_input(storage_service, &input, show_field, expand_field) : cmd_user_input(conn, &input, show_field, TRUE); o_stream_nsend_str(doveadm_print_ostream, "}"); switch (ret) { case -1: doveadm_exit_code = EX_TEMPFAIL; break; case 0: doveadm_exit_code = EX_NOUSER; break; } } o_stream_nsend_str(doveadm_print_ostream,"}"); if (storage_service != NULL) mail_storage_service_deinit(&storage_service); if (conn != NULL) auth_master_deinit(&conn); } static struct doveadm_cmd_ver2 doveadm_cmd_auth_server[] = { { .name = "auth cache flush", .old_cmd = cmd_auth_cache_flush, .usage = "[-a ] [ [...]]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "user", .cmd = cmd_user_ver2, .usage = "[-a ] [-x ] [-f field] [-e ] [-u] [...]", .flags = CMD_FLAG_NO_PRINT, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('x', "auth-info", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('f', "field", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('e', "expand-field", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('u', "userdb-only", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "user-mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END } }; void doveadm_register_auth_server_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_auth_server); i++) { doveadm_cmd_register_ver2(&doveadm_cmd_auth_server[i]); } } dovecot-2.2.33.2/src/doveadm/server-connection.c0000644000175000017500000004265713172375226016370 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "base64.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "istream-multiplex.h" #include "ostream.h" #include "ostream-dot.h" #include "str.h" #include "strescape.h" #include "iostream-ssl.h" #include "master-service.h" #include "master-service-settings.h" #include "settings-parser.h" #include "doveadm.h" #include "doveadm-print.h" #include "doveadm-util.h" #include "doveadm-server.h" #include "doveadm-settings.h" #include "server-connection.h" #include #include #define DOVEADM_LOG_CHANNEL_ID 'L' #define MAX_INBUF_SIZE (1024*32) enum server_reply_state { SERVER_REPLY_STATE_DONE = 0, SERVER_REPLY_STATE_PRINT, SERVER_REPLY_STATE_RET }; struct server_connection { struct doveadm_server *server; pool_t pool; struct doveadm_settings *set; int fd; unsigned int minor; struct io *io; struct io *io_log; struct istream *input; struct istream *log_input; struct ostream *output; struct ssl_iostream *ssl_iostream; struct timeout *to_input; struct istream *cmd_input; struct ostream *cmd_output; const char *delayed_cmd; server_cmd_callback_t *callback; void *context; enum server_reply_state state; unsigned int handshaked:1; unsigned int authenticated:1; unsigned int streaming:1; }; static struct server_connection *printing_conn = NULL; static ARRAY(struct doveadm_server *) print_pending_servers = ARRAY_INIT; static void server_connection_input(struct server_connection *conn); static bool server_connection_input_one(struct server_connection *conn); static void server_set_print_pending(struct doveadm_server *server) { struct doveadm_server *const *serverp; if (!array_is_created(&print_pending_servers)) i_array_init(&print_pending_servers, 16); array_foreach(&print_pending_servers, serverp) { if (*serverp == server) return; } array_append(&print_pending_servers, &server, 1); } static void server_print_connection_released(struct doveadm_server *server) { struct server_connection *const *conns; unsigned int i, count; conns = array_get(&server->connections, &count); for (i = 0; i < count; i++) { if (conns[i]->io != NULL) continue; conns[i]->io = io_add(conns[i]->fd, IO_READ, server_connection_input, conns[i]); conns[i]->to_input = timeout_add_short(0, server_connection_input, conns[i]); } } static void print_connection_released(void) { struct doveadm_server *const *serverp; printing_conn = NULL; if (!array_is_created(&print_pending_servers)) return; array_foreach(&print_pending_servers, serverp) server_print_connection_released(*serverp); array_free(&print_pending_servers); } static int server_connection_send_cmd_input_more(struct server_connection *conn) { off_t ret; /* ostream-dot writes only up to max buffer size, so keep it non-zero */ o_stream_set_max_buffer_size(conn->cmd_output, IO_BLOCK_SIZE); ret = o_stream_send_istream(conn->cmd_output, conn->cmd_input); o_stream_set_max_buffer_size(conn->cmd_output, (size_t)-1); if (ret >= 0 && i_stream_have_bytes_left(conn->cmd_input)) { o_stream_set_flush_pending(conn->cmd_output, TRUE); return 0; } if (conn->cmd_input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(conn->cmd_input), i_stream_get_error(conn->cmd_input)); } else if (conn->cmd_output->stream_errno != 0 || o_stream_flush(conn->cmd_output) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(conn->cmd_output), o_stream_get_error(conn->cmd_output)); } i_stream_destroy(&conn->cmd_input); o_stream_destroy(&conn->cmd_output); return ret < 0 ? -1 : 1; } static void server_connection_send_cmd_input(struct server_connection *conn) { if (conn->cmd_input == NULL) return; conn->cmd_output = o_stream_create_dot(conn->output, TRUE); (void)server_connection_send_cmd_input_more(conn); } static int server_connection_output(struct server_connection *conn) { int ret; o_stream_cork(conn->output); ret = o_stream_flush(conn->output); if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL) ret = server_connection_send_cmd_input_more(conn); o_stream_uncork(conn->output); if (ret < 0) server_connection_destroy(&conn); return ret; } static void server_connection_callback(struct server_connection *conn, int exit_code, const char *error) { server_cmd_callback_t *callback = conn->callback; conn->callback = NULL; callback(exit_code, error, conn->context); } static void stream_data(string_t *str, const unsigned char *data, size_t size) { str_truncate(str, 0); str_append_tabunescaped(str, data, size); doveadm_print_stream(str->data, str->used); } static void server_flush_field(struct server_connection *conn, string_t *str, const unsigned char *data, size_t size) { if (conn->streaming) { conn->streaming = FALSE; if (size > 0) stream_data(str, data, size); doveadm_print_stream("", 0); } else { str_truncate(str, 0); str_append_tabunescaped(str, data, size); doveadm_print(str_c(str)); } } static void server_handle_input(struct server_connection *conn, const unsigned char *data, size_t size) { string_t *str; size_t i, start; if (printing_conn == conn) { /* continue printing */ } else if (printing_conn == NULL) { printing_conn = conn; } else { /* someone else is printing. don't continue until it goes away */ server_set_print_pending(conn->server); io_remove(&conn->io); return; } if (data[size-1] == '\001') { /* last character is an escape */ size--; } str = t_str_new(128); for (i = start = 0; i < size; i++) { if (data[i] == '\n') { if (i != start) { i_error("doveadm server sent broken print input"); server_connection_destroy(&conn); return; } conn->state = SERVER_REPLY_STATE_RET; i_stream_skip(conn->input, i + 1); print_connection_released(); return; } if (data[i] == '\t') { server_flush_field(conn, str, data + start, i - start); start = i + 1; } } if (start != size) { conn->streaming = TRUE; stream_data(str, data + start, size - start); } i_stream_skip(conn->input, size); } static void server_connection_authenticated(struct server_connection *conn) { conn->authenticated = TRUE; if (conn->delayed_cmd != NULL) { o_stream_nsend_str(conn->output, conn->delayed_cmd); conn->delayed_cmd = NULL; server_connection_send_cmd_input(conn); } } static int server_connection_authenticate(struct server_connection *conn) { string_t *plain = t_str_new(128); string_t *cmd = t_str_new(128); if (*conn->set->doveadm_password == '\0') { i_error("doveadm_password not set, " "can't authenticate to remote server"); return -1; } str_append_c(plain, '\0'); str_append(plain, conn->set->doveadm_username); str_append_c(plain, '\0'); str_append(plain, conn->set->doveadm_password); str_append(cmd, "PLAIN\t"); base64_encode(plain->data, plain->used, cmd); str_append_c(cmd, '\n'); o_stream_nsend(conn->output, cmd->data, cmd->used); return 0; } static void server_log_disconnect_error(struct server_connection *conn) { const char *error; error = conn->ssl_iostream == NULL ? NULL : ssl_iostream_get_last_error(conn->ssl_iostream); if (error == NULL) { error = conn->input->stream_errno == 0 ? "EOF" : strerror(conn->input->stream_errno); } i_error("doveadm server disconnected before handshake: %s", error); } static void server_connection_print_log(struct server_connection *conn) { const char *line; struct failure_context ctx; i_zero(&ctx); while((line = i_stream_read_next_line(conn->log_input))!=NULL) { /* skip empty lines */ if (*line == '\0') continue; if (!doveadm_log_type_from_char(line[0], &ctx.type)) i_warning("Doveadm server sent invalid log type 0x%02x", line[0]); line++; i_log_type(&ctx, "remote(%s): %s", conn->server->name, line); } } static void server_connection_start_multiplex(struct server_connection *conn) { struct istream *is = conn->input; conn->input = i_stream_create_multiplex(is, MAX_INBUF_SIZE); i_stream_unref(&is); io_remove(&conn->io); conn->io = io_add_istream(conn->input, server_connection_input, conn); conn->log_input = i_stream_multiplex_add_channel(conn->input, DOVEADM_LOG_CHANNEL_ID); conn->io_log = io_add_istream(conn->log_input, server_connection_print_log, conn); i_stream_set_return_partial_line(conn->log_input, TRUE); } static void server_connection_input(struct server_connection *conn) { const char *line; if (conn->to_input != NULL) timeout_remove(&conn->to_input); if (!conn->handshaked || !conn->authenticated) { while((line = i_stream_read_next_line(conn->input)) != NULL) { if (strncmp(line, "VERSION\t", 8) == 0) { if (!version_string_verify_full(line, "doveadm-client", DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR, &conn->minor)) { i_error("doveadm server not compatible with this client" "(mixed old and new binaries?)"); server_connection_destroy(&conn); return; } continue; } if (strcmp(line, "+") == 0) { if (conn->minor > 0) server_connection_start_multiplex(conn); server_connection_authenticated(conn); break; } else if (strcmp(line, "-") == 0) { if (!conn->handshaked && server_connection_authenticate(conn) < 0) { server_connection_destroy(&conn); return; } else if (conn->handshaked) { i_error("doveadm authentication failed (%s)", line+1); server_connection_destroy(&conn); return; } } else { i_error("doveadm server sent invalid handshake: %s", line); server_connection_destroy(&conn); return; } conn->handshaked = TRUE; } if (line == NULL) { if (conn->input->eof || conn->input->stream_errno != 0) { server_log_disconnect_error(conn); server_connection_destroy(&conn); } } return; } if (i_stream_read(conn->input) < 0) { /* disconnected */ server_log_disconnect_error(conn); server_connection_destroy(&conn); return; } while (server_connection_input_one(conn)) ; } static bool server_connection_input_one(struct server_connection *conn) { const unsigned char *data; size_t size; const char *line; int exit_code; data = i_stream_get_data(conn->input, &size); if (size == 0) return FALSE; /* check logs */ if (conn->log_input != NULL) (void)server_connection_print_log(conn); switch (conn->state) { case SERVER_REPLY_STATE_DONE: i_error("doveadm server sent unexpected input"); server_connection_destroy(&conn); return FALSE; case SERVER_REPLY_STATE_PRINT: server_handle_input(conn, data, size); if (conn->state != SERVER_REPLY_STATE_RET) return FALSE; /* fall through */ case SERVER_REPLY_STATE_RET: line = i_stream_next_line(conn->input); if (line == NULL) return FALSE; if (line[0] == '+') server_connection_callback(conn, 0, ""); else if (line[0] == '-') { line++; exit_code = doveadm_str_to_exit_code(line); if (exit_code == DOVEADM_EX_UNKNOWN && str_to_int(line, &exit_code) < 0) { /* old doveadm-server */ exit_code = EX_TEMPFAIL; } server_connection_callback(conn, exit_code, line); } else { i_error("doveadm server sent broken input " "(expected cmd reply): %s", line); server_connection_destroy(&conn); return FALSE; } if (conn->callback == NULL) { /* we're finished, close the connection */ server_connection_destroy(&conn); return FALSE; } return TRUE; } i_unreached(); } static int server_connection_read_settings(struct server_connection *conn) { const struct setting_parser_info *set_roots[] = { &doveadm_setting_parser_info, NULL }; struct master_service_settings_input input; struct master_service_settings_output output; const char *error; in_port_t port; void *set; i_zero(&input); input.roots = set_roots; input.service = "doveadm"; (void)net_getsockname(conn->fd, &input.local_ip, &port); (void)net_getpeername(conn->fd, &input.remote_ip, &port); if (master_service_settings_read(master_service, &input, &output, &error) < 0) { i_error("Error reading configuration: %s", error); return -1; } set = master_service_settings_get_others(master_service)[0]; conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool); return 0; } static int server_connection_ssl_handshaked(const char **error_r, void *context) { struct server_connection *conn = context; const char *host, *p; host = conn->server->name; p = strrchr(host, ':'); if (p != NULL) host = t_strdup_until(host, p); if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, error_r) < 0) return -1; if (doveadm_debug) i_debug("%s: SSL handshake successful", conn->server->name); return 0; } static int server_connection_init_ssl(struct server_connection *conn) { struct ssl_iostream_settings ssl_set; const char *error; if (conn->server->ssl_ctx == NULL) return 0; i_zero(&ssl_set); ssl_set.verify_remote_cert = TRUE; ssl_set.require_valid_cert = TRUE; ssl_set.verbose_invalid_cert = TRUE; if (io_stream_create_ssl_client(conn->server->ssl_ctx, conn->server->name, &ssl_set, &conn->input, &conn->output, &conn->ssl_iostream, &error) < 0) { i_error("Couldn't initialize SSL client: %s", error); return -1; } ssl_iostream_set_handshake_callback(conn->ssl_iostream, server_connection_ssl_handshaked, conn); if (ssl_iostream_handshake(conn->ssl_iostream) < 0) { i_error("SSL handshake failed: %s", ssl_iostream_get_last_error(conn->ssl_iostream)); return -1; } return 0; } int server_connection_create(struct doveadm_server *server, struct server_connection **conn_r) { struct server_connection *conn; pool_t pool; pool = pool_alloconly_create("doveadm server connection", 1024*16); conn = p_new(pool, struct server_connection, 1); conn->pool = pool; conn->server = server; conn->fd = doveadm_connect_with_default_port(server->name, doveadm_settings->doveadm_port); net_set_nonblock(conn->fd, TRUE); conn->io = io_add(conn->fd, IO_READ, server_connection_input, conn); conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); o_stream_set_flush_callback(conn->output, server_connection_output, conn); i_stream_set_name(conn->input, server->name); o_stream_set_name(conn->output, server->name); array_append(&conn->server->connections, &conn, 1); if (server_connection_read_settings(conn) < 0 || server_connection_init_ssl(conn) < 0) { server_connection_destroy(&conn); return -1; } o_stream_set_no_error_handling(conn->output, TRUE); conn->state = SERVER_REPLY_STATE_DONE; o_stream_nsend_str(conn->output, DOVEADM_SERVER_PROTOCOL_VERSION_LINE"\n"); *conn_r = conn; return 0; } void server_connection_destroy(struct server_connection **_conn) { struct server_connection *conn = *_conn; struct server_connection *const *conns; const char *error; unsigned int i, count; *_conn = NULL; conns = array_get(&conn->server->connections, &count); for (i = 0; i < count; i++) { if (conns[i] == conn) { array_delete(&conn->server->connections, i, 1); break; } } if (conn->callback != NULL) { error = conn->ssl_iostream == NULL ? NULL : ssl_iostream_get_last_error(conn->ssl_iostream); if (error == NULL) { error = conn->input->stream_errno == 0 ? "EOF" : strerror(conn->input->stream_errno); } server_connection_callback(conn, SERVER_EXIT_CODE_DISCONNECTED, error); } if (printing_conn == conn) print_connection_released(); if (conn->to_input != NULL) timeout_remove(&conn->to_input); if (conn->input != NULL) i_stream_destroy(&conn->input); if (conn->output != NULL) o_stream_destroy(&conn->output); if (conn->cmd_input != NULL) i_stream_destroy(&conn->cmd_input); /* close cmd_output after its parent, so the "." isn't sent */ if (conn->cmd_output != NULL) o_stream_destroy(&conn->cmd_output); if (conn->ssl_iostream != NULL) ssl_iostream_unref(&conn->ssl_iostream); if (conn->io_log != NULL) io_remove(&conn->io_log); /* make sure all logs got consumed */ if (conn->log_input != NULL) { server_connection_print_log(conn); i_stream_unref(&conn->log_input); } if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { if (close(conn->fd) < 0) i_error("close(server) failed: %m"); } pool_unref(&conn->pool); } struct doveadm_server * server_connection_get_server(struct server_connection *conn) { return conn->server; } void server_connection_cmd(struct server_connection *conn, const char *line, struct istream *cmd_input, server_cmd_callback_t *callback, void *context) { i_assert(conn->delayed_cmd == NULL); conn->state = SERVER_REPLY_STATE_PRINT; if (cmd_input != NULL) { i_assert(conn->cmd_input == NULL); i_stream_ref(cmd_input); conn->cmd_input = cmd_input; } if (!conn->authenticated) conn->delayed_cmd = p_strdup(conn->pool, line); else { o_stream_nsend_str(conn->output, line); server_connection_send_cmd_input(conn); } conn->callback = callback; conn->context = context; } bool server_connection_is_idle(struct server_connection *conn) { return conn->callback == NULL; } void server_connection_extract(struct server_connection *conn, struct istream **istream_r, struct ostream **ostream_r, struct ssl_iostream **ssl_iostream_r) { *istream_r = conn->input; *ostream_r = conn->output; *ssl_iostream_r = conn->ssl_iostream; conn->input = NULL; conn->output = NULL; conn->ssl_iostream = NULL; if (conn->io != NULL) io_remove(&conn->io); conn->fd = -1; } dovecot-2.2.33.2/src/doveadm/doveadm-dump-mailboxlog.c0000644000175000017500000000452413147010711017414 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hex-binary.h" #include "mailbox-log.h" #include "doveadm-dump.h" #include #include #include #include static int dump_record(int fd) { off_t offset; ssize_t ret; struct mailbox_log_record rec; time_t timestamp; offset = lseek(fd, 0, SEEK_CUR); ret = read(fd, &rec, sizeof(rec)); if (ret == 0) return 0; if (ret != sizeof(rec)) { i_fatal("rec read() %"PRIuSIZE_T" != %"PRIuSIZE_T, ret, sizeof(rec)); } printf("#%"PRIuUOFF_T": ", offset); switch (rec.type) { case MAILBOX_LOG_RECORD_DELETE_MAILBOX: printf("delete-mailbox"); break; case MAILBOX_LOG_RECORD_DELETE_DIR: printf("delete-dir"); break; case MAILBOX_LOG_RECORD_RENAME: printf("rename"); break; case MAILBOX_LOG_RECORD_SUBSCRIBE: printf("subscribe"); break; case MAILBOX_LOG_RECORD_UNSUBSCRIBE: printf("unsubscribe"); break; case MAILBOX_LOG_RECORD_CREATE_DIR: printf("create-dir"); break; } printf(" %s", binary_to_hex(rec.mailbox_guid, sizeof(rec.mailbox_guid))); timestamp = be32_to_cpu_unaligned(rec.timestamp); printf(" (%s)\n", unixdate2str(timestamp)); return 1; } static void cmd_dump_mailboxlog(int argc ATTR_UNUSED, char *argv[]) { int fd, ret; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); do { T_BEGIN { ret = dump_record(fd); } T_END; } while (ret > 0); i_close_fd(&fd); } static bool test_dump_mailboxlog(const char *path) { const char *p; int fd; struct mailbox_log_record rec; bool ret = FALSE; p = strrchr(path, '.'); if (p == NULL || strcmp(p, ".log") != 0) return FALSE; fd = open(path, O_RDONLY); if (fd == -1) return FALSE; if (read(fd, &rec, sizeof(rec)) == sizeof(rec) && rec.padding[0] == 0 && rec.padding[1] == 0 && rec.padding[2] == 0) { enum mailbox_log_record_type type = rec.type; switch (type) { case MAILBOX_LOG_RECORD_DELETE_MAILBOX: case MAILBOX_LOG_RECORD_DELETE_DIR: case MAILBOX_LOG_RECORD_RENAME: case MAILBOX_LOG_RECORD_SUBSCRIBE: case MAILBOX_LOG_RECORD_UNSUBSCRIBE: case MAILBOX_LOG_RECORD_CREATE_DIR: ret = TRUE; break; } } i_close_fd(&fd); return ret; } struct doveadm_cmd_dump doveadm_cmd_dump_mailboxlog = { "mailboxlog", test_dump_mailboxlog, cmd_dump_mailboxlog }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-flags.c0000644000175000017500000001222613123174404016332 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-util.h" #include "mail-storage.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" #include struct flags_cmd_context { struct doveadm_mail_cmd_context ctx; enum modify_type modify_type; enum mail_flags flags; const char *const *keywords; }; static int cmd_flags_run_box(struct flags_cmd_context *ctx, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; struct mail_keywords *kw = NULL; if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, 0, NULL, FALSE, &iter) < 0) return -1; box = doveadm_mail_iter_get_mailbox(iter); if (ctx->keywords != NULL) { if (mailbox_keywords_create(box, ctx->keywords, &kw) < 0) { i_error("Invalid keywords: %s", mailbox_get_last_internal_error(box, NULL)); (void)doveadm_mail_iter_deinit(&iter); ctx->ctx.exit_code = DOVEADM_EX_NOTPOSSIBLE; return -1; } } while (doveadm_mail_iter_next(iter, &mail)) { mail_update_flags(mail, ctx->modify_type, ctx->flags); if (kw != NULL) mail_update_keywords(mail, ctx->modify_type, kw); } if (kw != NULL) mailbox_keywords_unref(&kw); return doveadm_mail_iter_deinit_sync(&iter); } static int cmd_flags_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct flags_cmd_context *ctx = (struct flags_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(_ctx, user, _ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_flags_run_box(ctx, info) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_flags_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct flags_cmd_context *ctx = (struct flags_cmd_context *)_ctx; const char *const *tmp; enum mail_flags flag; ARRAY_TYPE(const_string) keywords; if (args[0] == NULL || args[1] == NULL) { switch (ctx->modify_type) { case MODIFY_ADD: doveadm_mail_help_name("flags add"); case MODIFY_REMOVE: doveadm_mail_help_name("flags remove"); case MODIFY_REPLACE: doveadm_mail_help_name("flags replace"); } i_unreached(); } p_array_init(&keywords, _ctx->pool, 8); for (tmp = t_strsplit(args[0], " "); *tmp != NULL; tmp++) { const char *str = *tmp; if (str[0] == '\\') { flag = imap_parse_system_flag(str); if (flag == 0) i_fatal("Invalid system flag: %s", str); ctx->flags |= flag; } else { str = p_strdup(_ctx->pool, str); array_append(&keywords, &str, 1); } } if (array_count(&keywords) > 0 || ctx->modify_type == MODIFY_REPLACE) { array_append_zero(&keywords); ctx->keywords = array_idx(&keywords, 0); } _ctx->search_args = doveadm_mail_build_search_args(args+1); } static struct doveadm_mail_cmd_context * cmd_flag_alloc(enum modify_type modify_type) { struct flags_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct flags_cmd_context); ctx->modify_type = modify_type; ctx->ctx.v.init = cmd_flags_init; ctx->ctx.v.run = cmd_flags_run; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_flags_add_alloc(void) { return cmd_flag_alloc(MODIFY_ADD); } static struct doveadm_mail_cmd_context *cmd_flags_remove_alloc(void) { return cmd_flag_alloc(MODIFY_REMOVE); } static struct doveadm_mail_cmd_context *cmd_flags_replace_alloc(void) { return cmd_flag_alloc(MODIFY_REPLACE); } struct doveadm_cmd_ver2 doveadm_cmd_flags_add_ver2 = { .name = "flags add", .mail_cmd = cmd_flags_add_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "flag", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('\0', "flagstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL|CMD_PARAM_FLAG_DO_NOT_EXPOSE) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_flags_remove_ver2 = { .name = "flags remove", .mail_cmd = cmd_flags_remove_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "flag", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('\0', "flagstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL|CMD_PARAM_FLAG_DO_NOT_EXPOSE) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_flags_replace_ver2 = { .name = "flags replace", .mail_cmd = cmd_flags_replace_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "flag", CMD_PARAM_ARRAY, 0) DOVEADM_CMD_PARAM('\0', "flagstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL|CMD_PARAM_FLAG_DO_NOT_EXPOSE) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-print-formatted.c0000644000175000017500000000374613165463624017457 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "ostream.h" #include "client-connection.h" #include "doveadm-server.h" #include "doveadm-print.h" #include "doveadm-print-private.h" #include "var-expand.h" struct doveadm_print_formatted_context { pool_t pool; const char *format; ARRAY(struct var_expand_table) headers; string_t *buf; string_t *vbuf; unsigned int idx; }; static struct doveadm_print_formatted_context ctx; void doveadm_print_formatted_set_format(const char *format) { ctx.format = format; } static void doveadm_print_formatted_init(void) { i_zero(&ctx); ctx.pool = pool_alloconly_create("doveadm formatted print", 1024); ctx.buf = str_new(ctx.pool, 256); p_array_init(&ctx.headers, ctx.pool, 8); ctx.idx = 0; } static void doveadm_print_formatted_header(const struct doveadm_print_header *hdr) { struct var_expand_table entry; i_zero(&entry); entry.key = '\0'; entry.long_key = p_strdup(ctx.pool, hdr->key); entry.value = NULL; array_append(&ctx.headers, &entry, 1); } static void doveadm_print_formatted_flush(void) { o_stream_nsend(doveadm_print_ostream, str_data(ctx.buf), str_len(ctx.buf)); str_truncate(ctx.buf, 0); } static void doveadm_print_formatted_print(const char *value) { if (ctx.format == NULL) { i_fatal("formatted formatter cannot be used without a format."); } struct var_expand_table *entry = array_idx_modifiable(&ctx.headers, ctx.idx++); entry->value = value; if (ctx.idx >= array_count(&ctx.headers)) { var_expand(ctx.buf, ctx.format, array_idx(&ctx.headers,0)); doveadm_print_formatted_flush(); ctx.idx = 0; } } static void doveadm_print_formatted_deinit(void) { pool_unref(&ctx.pool); } struct doveadm_print_vfuncs doveadm_print_formatted_vfuncs = { "formatted", doveadm_print_formatted_init, doveadm_print_formatted_deinit, doveadm_print_formatted_header, doveadm_print_formatted_print, NULL, doveadm_print_formatted_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-expunge.c0000644000175000017500000001611013123174404016705 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-index.h" #include "mail-storage.h" #include "mail-search.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" struct expunge_cmd_context { struct doveadm_mail_cmd_context ctx; bool delete_empty_mailbox; }; static int cmd_expunge_box(struct doveadm_mail_cmd_context *_ctx, const struct mailbox_info *info, struct mail_search_args *search_args) { struct expunge_cmd_context *ctx = (struct expunge_cmd_context *)_ctx; struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; enum mail_error error; int ret = 0; if (doveadm_mail_iter_init(_ctx, info, search_args, 0, NULL, FALSE, &iter) < 0) return -1; while (doveadm_mail_iter_next(iter, &mail)) { if (doveadm_debug) { i_debug("expunge: box=%s uid=%u", info->vname, mail->uid); } mail_expunge(mail); } if (doveadm_mail_iter_deinit_keep_box(&iter, &box) < 0) ret = -1; else if (mailbox_sync(box, 0) < 0) { i_error("Syncing mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } if (ctx->delete_empty_mailbox && ret == 0) { if (mailbox_delete_empty(box) < 0) { error = mailbox_get_last_mail_error(box); if (error != MAIL_ERROR_EXISTS) { i_error("Deleting mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } else { if (mailbox_set_subscribed(box, FALSE) < 0) { i_error("Unsubscribing mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } } mailbox_free(&box); return ret; } static bool expunge_search_args_is_mailbox_ok(struct mail_search_arg *args); static bool expunge_search_args_is_mailbox_or_ok(struct mail_search_arg *args) { struct mail_search_arg *arg; for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_OR: if (!expunge_search_args_is_mailbox_or_ok(arg->value.subargs)) return FALSE; break; case SEARCH_SUB: case SEARCH_INTHREAD: if (!expunge_search_args_is_mailbox_ok(arg->value.subargs)) return FALSE; break; case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: break; default: return FALSE; } } return TRUE; } static bool expunge_search_args_is_mailbox_ok(struct mail_search_arg *args) { struct mail_search_arg *arg; bool have_or = FALSE; /* a) we find one mailbox here in the SUB block */ for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: return TRUE; case SEARCH_OR: have_or = TRUE; break; case SEARCH_SUB: case SEARCH_INTHREAD: if (expunge_search_args_is_mailbox_ok(arg->value.subargs)) return TRUE; break; default: break; } } /* b) there is at least one OR block, and all of the ORs must have mailbox */ if (!have_or) return FALSE; for (arg = args; arg != NULL; arg = arg->next) { if (arg->type == SEARCH_OR && !expunge_search_args_is_mailbox_or_ok(arg->value.subargs)) return FALSE; } return TRUE; } static bool expunge_search_args_is_msgset_ok(struct mail_search_arg *args); static bool expunge_search_args_is_msgset_or_ok(struct mail_search_arg *args) { struct mail_search_arg *arg; /* we're done if all OR branches contain something else besides MAILBOXes */ for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: return FALSE; case SEARCH_OR: if (!expunge_search_args_is_msgset_or_ok(arg->value.subargs)) return FALSE; break; case SEARCH_SUB: if (!expunge_search_args_is_msgset_ok(arg->value.subargs)) return FALSE; break; default: break; } } return TRUE; } static bool expunge_search_args_is_msgset_ok(struct mail_search_arg *args) { struct mail_search_arg *arg; /* all args can't be just MAILBOXes */ for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_MAILBOX: case SEARCH_MAILBOX_GUID: case SEARCH_MAILBOX_GLOB: break; case SEARCH_OR: /* if each OR branch has something else than just MAILBOXes, we're ok */ if (expunge_search_args_is_msgset_or_ok(arg->value.subargs)) return TRUE; break; case SEARCH_SUB: if (expunge_search_args_is_msgset_ok(arg->value.subargs)) return TRUE; break; default: return TRUE; } } return FALSE; } static int cmd_expunge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_expunge_box(ctx, info, ctx->search_args) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } void expunge_search_args_check(struct mail_search_args *args, const char *cmd) { if (!expunge_search_args_is_mailbox_ok(args->args)) { i_fatal_status(EX_USAGE, "%s: To avoid accidents, search query " "must contain MAILBOX in all search branches", cmd); } if (!expunge_search_args_is_msgset_ok(args->args)) { i_fatal_status(EX_USAGE, "%s: To avoid accidents, each branch in search query " "must contain something else besides MAILBOX " "(e.g. just add \"all\" if you want everything)", cmd); } mail_search_args_simplify(args); } static void cmd_expunge_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("expunge"); ctx->search_args = doveadm_mail_build_search_args(args); expunge_search_args_check(ctx->search_args, "expunge"); } static bool cmd_expunge_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct expunge_cmd_context *ctx = (struct expunge_cmd_context *)_ctx; switch (c) { case 'd': ctx->delete_empty_mailbox = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_expunge_alloc(void) { struct expunge_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct expunge_cmd_context); ctx->ctx.getopt_args = "d"; ctx->ctx.v.parse_arg = cmd_expunge_parse_arg; ctx->ctx.v.init = cmd_expunge_init; ctx->ctx.v.run = cmd_expunge_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_expunge_ver2 = { .name = "expunge", .mail_cmd = cmd_expunge_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-m] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('d', "delete-empty-mailbox", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-dump-log.c0000644000175000017500000003455513123174404016053 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "hex-binary.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include "doveadm-dump.h" #include static struct mail_transaction_ext_intro prev_intro; static void dump_hdr(struct istream *input, uint64_t *modseq_r, unsigned int *version_r) { struct mail_transaction_log_header hdr; const unsigned char *data; size_t size; int ret; ret = i_stream_read_bytes(input, &data, &size, sizeof(hdr)); if (ret < 0 && input->stream_errno != 0) i_fatal("read() failed: %s", i_stream_get_error(input)); if (ret <= 0) { i_fatal("file hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T, size, sizeof(hdr)); } memcpy(&hdr, data, sizeof(hdr)); if (hdr.hdr_size < sizeof(hdr)) { memset(PTR_OFFSET(&hdr, hdr.hdr_size), 0, sizeof(hdr) - hdr.hdr_size); } i_stream_skip(input, hdr.hdr_size); printf("version = %u.%u\n", hdr.major_version, hdr.minor_version); printf("hdr size = %u\n", hdr.hdr_size); printf("index id = %u\n", hdr.indexid); printf("file seq = %u\n", hdr.file_seq); printf("prev file = %u/%u\n", hdr.prev_file_seq, hdr.prev_file_offset); printf("create stamp = %u\n", hdr.create_stamp); printf("initial modseq = %llu\n", (unsigned long long)hdr.initial_modseq); printf("compat flags = %x\n", hdr.compat_flags); *modseq_r = hdr.initial_modseq; *version_r = MAIL_TRANSACTION_LOG_HDR_VERSION(&hdr); } static const char *log_record_type(unsigned int type) { const char *name; switch (type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: name = "expunge"; break; case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: name = "expunge-guid"; break; case MAIL_TRANSACTION_APPEND: name = "append"; break; case MAIL_TRANSACTION_FLAG_UPDATE: name = "flag-update"; break; case MAIL_TRANSACTION_HEADER_UPDATE: name = "header-update"; break; case MAIL_TRANSACTION_EXT_INTRO: name = "ext-intro"; break; case MAIL_TRANSACTION_EXT_RESET: name = "ext-reset"; break; case MAIL_TRANSACTION_EXT_HDR_UPDATE: name = "ext-hdr"; break; case MAIL_TRANSACTION_EXT_HDR_UPDATE32: name = "ext-hdr32"; break; case MAIL_TRANSACTION_EXT_REC_UPDATE: name = "ext-rec"; break; case MAIL_TRANSACTION_KEYWORD_UPDATE: name = "keyword-update"; break; case MAIL_TRANSACTION_KEYWORD_RESET: name = "keyword-reset"; break; case MAIL_TRANSACTION_EXT_ATOMIC_INC: name = "ext-atomic-inc"; break; case MAIL_TRANSACTION_MODSEQ_UPDATE: name = "modseq-update"; break; case MAIL_TRANSACTION_INDEX_DELETED: name = "index-deleted"; break; case MAIL_TRANSACTION_INDEX_UNDELETED: name = "index-undeleted"; break; case MAIL_TRANSACTION_BOUNDARY: name = "boundary"; break; case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: name = "attribute-update"; break; default: name = t_strdup_printf("unknown: %x", type); break; } if ((type & MAIL_TRANSACTION_EXTERNAL) != 0) name = t_strconcat(name, " (ext)", NULL); if ((type & MAIL_TRANSACTION_SYNC) != 0) name = t_strconcat(name, " (sync)", NULL); return name; } static void print_data(const void *data, size_t size) { size_t i; for (i = 0; i < size; i++) printf("%02x", ((const unsigned char *)data)[i]); if (size == 4) { const uint32_t *n = (const uint32_t *)data; printf(" (dec=%u)", *n); } } static void print_try_uint(const void *data, size_t size) { size_t i; switch (size) { case 1: { const uint8_t *n = data; printf("%u", *n); break; } case 2: { const uint16_t *n = data; uint32_t n16; memcpy(&n16, n, sizeof(n16)); printf("%u", n16); break; } case 4: { const uint32_t *n = data; uint32_t n32; memcpy(&n32, n, sizeof(n32)); printf("%u", n32); break; } case 8: { const uint64_t *n = data; uint64_t n64; memcpy(&n64, n, sizeof(n64)); printf("%llu", (unsigned long long)n64); break; } default: for (i = 0; i < size; i++) printf("%02x", ((const unsigned char *)data)[i]); } } #define HDRF(field) { \ #field, offsetof(struct mail_index_header, field), \ sizeof(((struct mail_index_header *)0)->field) } static struct { const char *name; unsigned int offset, size; } header_fields[] = { HDRF(minor_version), HDRF(base_header_size), HDRF(header_size), HDRF(record_size), HDRF(compat_flags), HDRF(indexid), HDRF(flags), HDRF(uid_validity), HDRF(next_uid), HDRF(messages_count), HDRF(unused_old_recent_messages_count), HDRF(seen_messages_count), HDRF(deleted_messages_count), HDRF(first_recent_uid), HDRF(first_unseen_uid_lowwater), HDRF(first_deleted_uid_lowwater), HDRF(log_file_seq), HDRF(log_file_tail_offset), HDRF(log_file_head_offset), HDRF(day_stamp) }; static void log_header_update(const struct mail_transaction_header_update *u, size_t data_size) { const void *data = u + 1; unsigned int offset = u->offset, size = u->size; unsigned int i; if (sizeof(*u) + size > data_size) { printf(" - offset = %u, size = %u (too large)\n", offset, size); return; } while (size > 0) { /* don't bother trying to handle header updates that include unknown/unexpected fields offsets/sizes */ for (i = 0; i < N_ELEMENTS(header_fields); i++) { if (header_fields[i].offset == offset && header_fields[i].size <= size) break; } if (i == N_ELEMENTS(header_fields)) { printf(" - offset = %u, size = %u: ", offset, size); print_data(data, size); printf("\n"); break; } printf(" - %s = ", header_fields[i].name); print_try_uint(data, header_fields[i].size); printf("\n"); data = CONST_PTR_OFFSET(data, header_fields[i].size); offset += header_fields[i].size; size -= header_fields[i].size; } } static void log_record_print(const struct mail_transaction_header *hdr, const void *data, size_t data_size, uint64_t *modseq) { unsigned int size = mail_index_offset_to_uint32(hdr->size) - sizeof(*hdr); switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: { const struct mail_transaction_expunge *exp = data; printf(" - uids="); for (; size > 0; size -= sizeof(*exp), exp++) { printf("%u-%u,", exp->uid1, exp->uid2); } printf("\n"); break; } case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: { const struct mail_transaction_expunge_guid *exp = data; for (; size > 0; size -= sizeof(*exp), exp++) { printf(" - uid=%u (guid ", exp->uid); print_data(exp->guid_128, sizeof(exp->guid_128)); printf(")\n"); } break; } case MAIL_TRANSACTION_APPEND: { const struct mail_index_record *rec = data; printf(" - uids="); for (; size > 0; size -= sizeof(*rec), rec++) { printf("%u", rec->uid); if (rec->flags != 0) printf(" (flags=%x)", rec->flags); printf(","); } printf("\n"); break; } case MAIL_TRANSACTION_FLAG_UPDATE: { const struct mail_transaction_flag_update *u = data; for (; size > 0; size -= sizeof(*u), u++) { printf(" - uids=%u-%u (flags +%x-%x, modseq_inc_flag=%d)\n", u->uid1, u->uid2, u->add_flags, u->remove_flags, u->modseq_inc_flag); } break; } case MAIL_TRANSACTION_HEADER_UPDATE: { const struct mail_transaction_header_update *u = data; log_header_update(u, data_size); break; } case MAIL_TRANSACTION_EXT_INTRO: { const struct mail_transaction_ext_intro *intro = data; prev_intro = *intro; printf(" - ext_id = %u\n", intro->ext_id); printf(" - reset_id = %u\n", intro->reset_id); printf(" - hdr_size = %u\n", intro->hdr_size); printf(" - record_size = %u\n", intro->record_size); printf(" - record_align = %u\n", intro->record_align); printf(" - flags = %u\n", intro->flags); printf(" - name_size = %u\n", intro->name_size); if (intro->name_size > 0) { const char *name = (const char *)(intro+1); printf(" - name = '%.*s'\n", intro->name_size, name); if (*modseq == 0 && intro->name_size == 6 && memcmp(name, "modseq", 6) == 0) *modseq = 1; } break; } case MAIL_TRANSACTION_EXT_RESET: { const struct mail_transaction_ext_reset *reset = data; printf(" - new_reset_id = %u\n", reset->new_reset_id); printf(" - preserve_data = %u\n", reset->preserve_data); break; } case MAIL_TRANSACTION_EXT_HDR_UPDATE: { const struct mail_transaction_ext_hdr_update *u = data; printf(" - offset = %u, size = %u", u->offset, u->size); if (sizeof(*u) + u->size <= data_size) { printf(": "); print_data(u + 1, u->size); } else { printf(" (too large)"); } printf("\n"); break; } case MAIL_TRANSACTION_EXT_HDR_UPDATE32: { const struct mail_transaction_ext_hdr_update32 *u = data; printf(" - offset = %u, size = %u", u->offset, u->size); if (sizeof(*u) + u->size <= data_size) { printf(": "); print_data(u + 1, u->size); } else { printf(" (too large)"); } printf("\n"); break; } case MAIL_TRANSACTION_EXT_REC_UPDATE: { const struct mail_transaction_ext_rec_update *rec = data, *end; size_t record_size; end = CONST_PTR_OFFSET(data, size); record_size = (sizeof(*rec) + prev_intro.record_size + 3) & ~3; while (rec < end) { printf(" - uid=%u: ", rec->uid); if (prev_intro.record_size <= (char*)end - (char *)(rec+1)) print_data(rec + 1, prev_intro.record_size); else printf("(record_size too large)"); printf("\n"); rec = CONST_PTR_OFFSET(rec, record_size); } break; } case MAIL_TRANSACTION_EXT_ATOMIC_INC: { const struct mail_transaction_ext_atomic_inc *rec = data, *end; end = CONST_PTR_OFFSET(data, size); for (; rec < end; rec++) { printf(" - uid=%u: ", rec->uid); if (rec->diff > 0) printf("+%d\n", rec->diff); else printf("%d\n", rec->diff); } break; } case MAIL_TRANSACTION_KEYWORD_UPDATE: { const struct mail_transaction_keyword_update *u = data; const uint32_t *uid; unsigned int uid_offset; printf(" - modify=%d, name=%.*s, uids=", u->modify_type, u->name_size, (const char *)(u+1)); uid_offset = sizeof(*u) + u->name_size + ((u->name_size % 4) == 0 ? 0 : 4 - (u->name_size%4)); uid = (const uint32_t *)((const char *)u + uid_offset); size -= uid_offset; for (; size > 0; size -= sizeof(*uid)*2, uid += 2) { printf("%u-%u,", uid[0], uid[1]); } printf("\n"); break; } case MAIL_TRANSACTION_KEYWORD_RESET: { const struct mail_transaction_keyword_reset *u = data; printf(" - uids="); for (; size > 0; size -= sizeof(*u), u++) { printf("%u-%u, ", u->uid1, u->uid2); } printf("\n"); break; } case MAIL_TRANSACTION_MODSEQ_UPDATE: { const struct mail_transaction_modseq_update *rec, *end; end = CONST_PTR_OFFSET(data, size); for (rec = data; rec < end; rec++) { printf(" - uid=%u modseq=%llu\n", rec->uid, ((unsigned long long)rec->modseq_high32 << 32) | rec->modseq_low32); } break; } case MAIL_TRANSACTION_INDEX_DELETED: case MAIL_TRANSACTION_INDEX_UNDELETED: break; case MAIL_TRANSACTION_BOUNDARY: { const struct mail_transaction_boundary *rec = data; printf(" - size=%u\n", rec->size); break; } case MAIL_TRANSACTION_ATTRIBUTE_UPDATE: { const char *keys = data; const uint32_t *extra; unsigned int i, extra_pos, extra_count = 0; for (i = 0; i < size && keys[i] != '\0'; ) { if (keys[i] == '+') extra_count++; extra_count++; i += strlen(keys+i) + 1; } if (i % sizeof(uint32_t) != 0) i += sizeof(uint32_t) - i%sizeof(uint32_t); extra = (const void *)(keys+i); if ((size-i) != extra_count*sizeof(uint32_t)) { printf(" - broken entry\n"); break; } extra_pos = 0; for (i = 0; i < size && keys[i] != '\0'; ) { printf(" - %s: %s/%s : timestamp=%s", keys[i] == '+' ? "add" : keys[i] == '-' ? "remove" : "?", keys[i+1] == 'p' ? "private" : keys[i+1] == 's' ? "shared" : "?error?", keys+i+2, unixdate2str(extra[extra_pos++])); if (keys[i] == '+') printf(" value_len=%u", extra[extra_pos++]); printf("\n"); i += strlen(keys+i) + 1; } break; } default: break; } } static int dump_record(struct istream *input, uint64_t *modseq, unsigned int version) { struct mail_transaction_header hdr; unsigned int hdr_size; const unsigned char *data; size_t size; int ret; ret = i_stream_read_bytes(input, &data, &size, sizeof(hdr)); if (ret < 0 && input->stream_errno != 0) i_fatal("read() failed: %s", i_stream_get_error(input)); if (ret <= 0) { if (size == 0) return 0; i_fatal("rec hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T, size, sizeof(hdr)); } memcpy(&hdr, data, sizeof(hdr)); hdr_size = mail_index_offset_to_uint32(hdr.size); if (hdr_size < sizeof(hdr)) { printf("record: offset=%"PRIuUOFF_T", " "type=%s, size=broken (%x)\n", input->v_offset, log_record_type(hdr.type), hdr.size); return 0; } printf("record: offset=%"PRIuUOFF_T", type=%s, size=%u", input->v_offset, log_record_type(hdr.type), hdr_size); i_stream_skip(input, sizeof(hdr)); size_t data_size = hdr_size - sizeof(hdr); ret = i_stream_read_bytes(input, &data, &size, data_size); if (ret < 0 && input->stream_errno != 0) i_fatal("read() failed: %s", i_stream_get_error(input)); if (ret <= 0) { i_fatal("rec data read() %"PRIuSIZE_T" != %"PRIuSIZE_T, size, data_size); } uint64_t prev_modseq = *modseq; mail_transaction_update_modseq(&hdr, data, modseq, version); if (*modseq > prev_modseq) printf(", modseq=%llu", (unsigned long long)*modseq); printf("\n"); log_record_print(&hdr, data, data_size, modseq); i_stream_skip(input, data_size); return 1; } static void cmd_dump_log(int argc ATTR_UNUSED, char *argv[]) { struct istream *input; uint64_t modseq; unsigned int version; int ret; input = i_stream_create_file(argv[1], (size_t)-1); dump_hdr(input, &modseq, &version); do { T_BEGIN { ret = dump_record(input, &modseq, version); } T_END; } while (ret > 0); i_stream_unref(&input); } static bool test_dump_log(const char *path) { struct mail_transaction_log_header hdr; const char *p; bool ret = FALSE; int fd; p = strrchr(path, '/'); if (p == NULL) p = path; p = strstr(p, ".log"); if (p == NULL || !(p[4] == '\0' || p[4] == '.')) return FALSE; fd = open(path, O_RDONLY); if (fd == -1) return FALSE; if (read(fd, &hdr, sizeof(hdr)) >= MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE && hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION && hdr.hdr_size >= MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) ret = TRUE; i_close_fd(&fd); return ret; } struct doveadm_cmd_dump doveadm_cmd_dump_log = { "log", test_dump_log, cmd_dump_log }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-batch.c0000644000175000017500000001136113123174404016316 00000000000000/* Copyright (c) 2012-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "doveadm-mail.h" #include struct batch_cmd_context { struct doveadm_mail_cmd_context ctx; ARRAY(struct doveadm_mail_cmd_context *) commands; }; static int cmd_batch_prerun(struct doveadm_mail_cmd_context *_ctx, struct mail_storage_service_user *service_user, const char **error_r) { struct batch_cmd_context *ctx = (struct batch_cmd_context *)_ctx; struct doveadm_mail_cmd_context *const *cmdp; int ret = 0; array_foreach(&ctx->commands, cmdp) { if ((*cmdp)->v.prerun != NULL && (*cmdp)->v.prerun(*cmdp, service_user, error_r) < 0) { ret = -1; break; } } return ret; } static int cmd_batch_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct batch_cmd_context *ctx = (struct batch_cmd_context *)_ctx; struct doveadm_mail_cmd_context *const *cmdp; int ret = 0; array_foreach(&ctx->commands, cmdp) { (*cmdp)->cur_mail_user = user; if ((*cmdp)->v.run(*cmdp, user) < 0) { i_assert((*cmdp)->exit_code != 0); _ctx->exit_code = (*cmdp)->exit_code; ret = -1; break; } (*cmdp)->cur_mail_user = NULL; } return ret; } static void cmd_batch_add(struct batch_cmd_context *batchctx, int argc, const char *const *argv) { struct doveadm_mail_cmd_context *subctx; const struct doveadm_cmd_ver2 *cmd_ver2; struct doveadm_mail_cmd tmpcmd; const struct doveadm_mail_cmd *cmd; const char *getopt_args; int c; cmd_ver2 = doveadm_cmd_find_with_args_ver2(argv[0], &argc, &argv); if (cmd_ver2 == NULL) cmd = doveadm_mail_cmd_find_from_argv(argv[0], &argc, &argv); else { i_zero(&tmpcmd); tmpcmd.usage_args = cmd_ver2->usage; tmpcmd.name = cmd_ver2->name; tmpcmd.alloc = cmd_ver2->mail_cmd; cmd = &tmpcmd; } if (cmd == NULL) i_fatal_status(EX_USAGE, "doveadm batch: '%s' mail command doesn't exist", argv[0]); subctx = doveadm_mail_cmd_init(cmd, doveadm_settings); subctx->full_args = argv + 1; subctx->service_flags |= batchctx->ctx.service_flags; i_getopt_reset(); getopt_args = subctx->getopt_args != NULL ? subctx->getopt_args : ""; while ((c = getopt(argc, (void *)argv, getopt_args)) > 0) { if (subctx->v.parse_arg == NULL || !subctx->v.parse_arg(subctx, c)) doveadm_mail_help(cmd); } argv += optind; if (argv[0] != NULL && cmd->usage_args == NULL) { i_fatal_status(EX_USAGE, "doveadm %s: Unknown parameter: %s", cmd->name, argv[0]); } subctx->args = argv; if (subctx->v.preinit != NULL) subctx->v.preinit(subctx); array_append(&batchctx->commands, &subctx, 1); } static void cmd_batch_preinit(struct doveadm_mail_cmd_context *_ctx) { const char *const *args = _ctx->args; struct batch_cmd_context *ctx = (struct batch_cmd_context *)_ctx; ARRAY_TYPE(const_string) sep_args; const char *sep = args[0]; unsigned int i, start; int argc; const char *const *argv; if (sep == NULL || args[1] == NULL) doveadm_mail_help_name("batch"); args++; p_array_init(&ctx->commands, _ctx->pool, 8); p_array_init(&sep_args, _ctx->pool, 16); for (i = start = 0;; i++) { if (args[i] != NULL && strcmp(args[i], sep) != 0) { array_append(&sep_args, &args[i], 1); continue; } if (i > start) { (void)array_append_space(&sep_args); argc = i - start; argv = array_idx(&sep_args, start); cmd_batch_add(ctx, argc, argv); start = i+1; } if (args[i] == NULL) break; } (void)array_append_space(&sep_args); } static void cmd_batch_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[] ATTR_UNUSED) { struct batch_cmd_context *ctx = (struct batch_cmd_context *)_ctx; struct doveadm_mail_cmd_context *const *cmdp; struct batch_cmd_context *subctx; array_foreach(&ctx->commands, cmdp) { subctx = (struct batch_cmd_context *)*cmdp; subctx->ctx.storage_service = _ctx->storage_service; if (subctx->ctx.v.init != NULL) subctx->ctx.v.init(&subctx->ctx, subctx->ctx.args); } } static void cmd_batch_deinit(struct doveadm_mail_cmd_context *_ctx) { struct batch_cmd_context *ctx = (struct batch_cmd_context *)_ctx; struct doveadm_mail_cmd_context *const *cmdp; array_foreach(&ctx->commands, cmdp) { if ((*cmdp)->v.deinit != NULL) (*cmdp)->v.deinit(*cmdp); } } static struct doveadm_mail_cmd_context *cmd_batch_alloc(void) { struct batch_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct batch_cmd_context); ctx->ctx.getopt_args = "+"; /* disable processing -args in the middle */ ctx->ctx.v.preinit = cmd_batch_preinit; ctx->ctx.v.init = cmd_batch_init; ctx->ctx.v.prerun = cmd_batch_prerun; ctx->ctx.v.run = cmd_batch_run; ctx->ctx.v.deinit = cmd_batch_deinit; return &ctx->ctx; } struct doveadm_mail_cmd cmd_batch = { cmd_batch_alloc, "batch", " [ [..]]" }; dovecot-2.2.33.2/src/doveadm/doveadm-mailbox-list-iter.h0000644000175000017500000000154013123174404017665 00000000000000#ifndef DOVEADM_MAILBOX_LIST_ITER_H #define DOVEADM_MAILBOX_LIST_ITER_H #include "mailbox-list-iter.h" struct doveadm_mail_cmd_context; /* List only selectable mailboxes */ struct doveadm_mailbox_list_iter * doveadm_mailbox_list_iter_init(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, struct mail_search_args *search_args, enum mailbox_list_iter_flags iter_flags); /* List all mailboxes */ struct doveadm_mailbox_list_iter * doveadm_mailbox_list_iter_full_init(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, struct mail_search_args *search_args, enum mailbox_list_iter_flags iter_flags); int doveadm_mailbox_list_iter_deinit(struct doveadm_mailbox_list_iter **iter); const struct mailbox_info * doveadm_mailbox_list_iter_next(struct doveadm_mailbox_list_iter *iter); #endif dovecot-2.2.33.2/src/doveadm/doveadm-dump-thread.c0000644000175000017500000000611613123174404016531 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mmap-util.h" #include "mail-index-private.h" #include "mail-index-strmap.h" #include "doveadm-dump.h" #include #include #include #include static uint32_t max_likely_index; static size_t dump_hdr(const struct mail_index_strmap_header *hdr) { printf("version = %u\n", hdr->version); printf("uid validity = %u\n", hdr->uid_validity); return sizeof(*hdr); } static int dump_record(const uint8_t **p, const uint8_t *end, uint32_t *uid) { uint32_t uid_diff, n, i, count, crc32, idx; size_t size; /* *count *count */ if (mail_index_unpack_num(p, end, &uid_diff) < 0) return -1; *uid += uid_diff; if (mail_index_unpack_num(p, end, &n) < 0) return -1; printf(" - uid %u: n=%u\n", *uid, n); count = n < 2 ? n + 1 : n; size = sizeof(crc32)*count + sizeof(idx)*count; if (*p + size > end) return -1; for (i = 0; i < count; i++) { if (i == 0) printf(" - message-id: "); else if (i == 1) { if (n == 1) printf(" - in-reply-to: "); else printf(" - references[1]: "); } else { printf(" - references[%u]: ", i); } memcpy(&crc32, *p + sizeof(crc32)*i, sizeof(crc32)); memcpy(&idx, *p + sizeof(crc32)*count + sizeof(idx)*i, sizeof(idx)); printf("crc32=%08x index=%u\n", crc32, idx); if (idx > max_likely_index) printf(" - index probably broken\n"); } *p += size; return 0; } static int dump_block(const uint8_t *data, const uint8_t *end, uint32_t *uid) { const uint8_t *p; uint32_t block_size; if (data + 4 >= end) return -1; memcpy(&block_size, data, sizeof(block_size)); block_size = mail_index_offset_to_uint32(block_size) >> 2; printf(" - block_size=%u\n", block_size); if (block_size == 0) { /* finished */ return -1; } if (data + sizeof(block_size) + block_size > end) { printf(" - broken!\n"); return -1; } p = data + sizeof(block_size); end = p + block_size; *uid += 1; while (p != end) { if (dump_record(&p, end, uid) < 0) { printf(" - broken\n"); return -1; } } return p - data; } static void cmd_dump_thread(int argc ATTR_UNUSED, char *argv[]) { unsigned int pos; const void *map, *end; struct stat st; uint32_t uid; int fd, ret; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); if (fstat(fd, &st) < 0) i_fatal("fstat(%s) failed: %m", argv[1]); max_likely_index = (st.st_size / 8) * 2; map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) i_fatal("mmap() failed: %m"); end = CONST_PTR_OFFSET(map, st.st_size); pos = dump_hdr(map); uid = 0; do { printf("block at offset %u:\n", pos); T_BEGIN { ret = dump_block(CONST_PTR_OFFSET(map, pos), end, &uid); pos += ret; } T_END; } while (ret > 0); i_close_fd(&fd); } static bool test_dump_thread(const char *path) { const char *p; p = strrchr(path, '.'); return p != NULL && strcmp(p, ".thread") == 0; } struct doveadm_cmd_dump doveadm_cmd_dump_thread = { "thread", test_dump_thread, cmd_dump_thread }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-save.c0000644000175000017500000000754213165463624016214 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "mail-storage.h" #include "doveadm-mail.h" struct save_cmd_context { struct doveadm_mail_cmd_context ctx; const char *mailbox; }; static int cmd_save_to_mailbox(struct save_cmd_context *ctx, struct mailbox *box, struct istream *input) { struct mail_storage *storage = mailbox_get_storage(box); struct mailbox_transaction_context *trans; struct mail_save_context *save_ctx; ssize_t ret; bool save_failed = FALSE; if (input->stream_errno != 0) { i_error("open(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); ctx->ctx.exit_code = EX_TEMPFAIL; return -1; } if (mailbox_open(box) < 0) { i_error("Failed to open mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); return -1; } trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); save_ctx = mailbox_save_alloc(trans); if (mailbox_save_begin(&save_ctx, input) < 0) { i_error("Saving failed: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); mailbox_transaction_rollback(&trans); return -1; } do { if (mailbox_save_continue(save_ctx) < 0) { save_failed = TRUE; ret = -1; break; } } while ((ret = i_stream_read(input)) > 0); i_assert(ret == -1); if (input->stream_errno != 0) { i_error("read(msg input) failed: %s", i_stream_get_error(input)); doveadm_mail_failed_error(&ctx->ctx, MAIL_ERROR_TEMP); } else if (save_failed) { i_error("Saving failed: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_save_finish(&save_ctx) < 0) { i_error("Saving failed: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_transaction_commit(&trans) < 0) { i_error("Save transaction commit failed: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else { ret = 0; } if (save_ctx != NULL) mailbox_save_cancel(&save_ctx); if (trans != NULL) mailbox_transaction_rollback(&trans); i_assert(input->eof); return ret < 0 ? -1 : 0; } static int cmd_save_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct save_cmd_context *ctx = (struct save_cmd_context *)_ctx; struct mail_namespace *ns; struct mailbox *box; int ret; ns = mail_namespace_find(user->namespaces, ctx->mailbox); box = mailbox_alloc(ns->list, ctx->mailbox, MAILBOX_FLAG_SAVEONLY); mailbox_set_reason(box, _ctx->cmd->name); ret = cmd_save_to_mailbox(ctx, box, _ctx->cmd_input); mailbox_free(&box); return ret; } static void cmd_save_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[] ATTR_UNUSED) { doveadm_mail_get_input(_ctx); } static bool cmd_mailbox_save_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct save_cmd_context *ctx = (struct save_cmd_context *)_ctx; switch (c) { case 'm': ctx->mailbox = optarg; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_save_alloc(void) { struct save_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct save_cmd_context); ctx->ctx.getopt_args = "m:"; ctx->ctx.v.parse_arg = cmd_mailbox_save_parse_arg; ctx->ctx.v.init = cmd_save_init; ctx->ctx.v.run = cmd_save_run; ctx->mailbox = "INBOX"; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_save_ver2 = { .name = "save", .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-m mailbox]", .mail_cmd = cmd_save_alloc, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('m', "mailbox", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "file", CMD_PARAM_ISTREAM, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/test-doveadm-util.c0000644000175000017500000000333513165463624016264 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "doveadm-settings.h" #include "doveadm-util.h" struct doveadm_settings *doveadm_settings; /* just to avoid linker error */ static void test_i_strccdascmp(void) { test_begin("i_strccdascmp()"); test_assert(i_strccdascmp("", "")==0); test_assert(i_strccdascmp("", "-")!=0); test_assert(i_strccdascmp("-", "")!=0); test_assert(i_strccdascmp("-", "-")==0); test_assert(i_strccdascmp("-\0baz", "-\0bar")==0); test_assert(i_strccdascmp("", "a")!=0); test_assert(i_strccdascmp("a", "")!=0); test_assert(i_strccdascmp("a", "a")==0); test_assert(i_strccdascmp("a-", "a-")==0); test_assert(i_strccdascmp("a-a", "a-a")==0); test_assert(i_strccdascmp("ca", "ba")!=0); test_assert(i_strccdascmp("camel case", "camel case")==0); test_assert(i_strccdascmp("camel case", "camel-case")==0); test_assert(i_strccdascmp("camel case", "camelCase")==0); test_assert(i_strccdascmp("camel case", "camel-case")==0); test_assert(i_strccdascmp("camel-case", "camel-case")==0); test_assert(i_strccdascmp("camelCase", "camel-case")==0); test_assert(i_strccdascmp("camel case", "camel Case")==-i_strccdascmp("camel Case", "camel case")); test_assert(i_strccdascmp("camel-case", "camel Case")==-i_strccdascmp("camel Case", "camel-case")); test_assert(i_strccdascmp("camel dase", "camel case")==-i_strccdascmp("camel case", "camel dase")); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_i_strccdascmp, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/doveadm/doveadm-replicator.c0000644000175000017500000002206013144653606016470 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "strescape.h" #include "istream.h" #include "write-full.h" #include "master-service.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include struct replicator_context { const char *socket_path; const char *priority; struct istream *input; bool full_sync; }; extern struct doveadm_cmd doveadm_cmd_replicator[]; static void replicator_cmd_help(doveadm_command_t *cmd) ATTR_NORETURN; static void replicator_send(struct replicator_context *ctx, const char *data) { if (write_full(i_stream_get_fd(ctx->input), data, strlen(data)) < 0) i_fatal("write(%s) failed: %m", ctx->socket_path); } static void replicator_connect(struct replicator_context *ctx) { #define REPLICATOR_HANDSHAKE "VERSION\treplicator-doveadm-client\t1\t0\n" const char *line; int fd; fd = doveadm_connect(ctx->socket_path); net_set_nonblock(fd, FALSE); ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); replicator_send(ctx, REPLICATOR_HANDSHAKE); alarm(5); line = i_stream_read_next_line(ctx->input); alarm(0); if (line == NULL) { if (ctx->input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->socket_path, i_stream_get_error(ctx->input)); } else if (ctx->input->eof) i_fatal("%s disconnected", ctx->socket_path); else i_fatal("read(%s) timed out", ctx->socket_path); } if (!version_string_verify(line, "replicator-doveadm-server", 1)) { i_fatal_status(EX_PROTOCOL, "%s not a compatible replicator-doveadm socket", ctx->socket_path); } } static void replicator_disconnect(struct replicator_context *ctx) { if (ctx->input->stream_errno != 0) { i_fatal("read(%s) failed: %s", ctx->socket_path, i_stream_get_error(ctx->input)); } i_stream_destroy(&ctx->input); } static struct replicator_context * cmd_replicator_init(int *argc, char **argv[], const char *getopt_args, doveadm_command_t *cmd) { struct replicator_context *ctx; int c; ctx = t_new(struct replicator_context, 1); ctx->socket_path = t_strconcat(doveadm_settings->base_dir, "/replicator-doveadm", NULL); while ((c = getopt(*argc, *argv, getopt_args)) > 0) { switch (c) { case 'a': ctx->socket_path = optarg; break; case 'f': ctx->full_sync = TRUE; break; case 'p': ctx->priority = optarg; break; default: replicator_cmd_help(cmd); } } *argc -= optind-1; *argv += optind-1; replicator_connect(ctx); return ctx; } static const char *time_ago(time_t t) { int diff = ioloop_time - t; if (t == 0) return "-"; return t_strdup_printf("%02d:%02d:%02d", diff/3600, (diff/60)%60, diff%60); } static void cmd_replicator_status_overview(struct replicator_context *ctx) { char *line, *value; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("field", "field", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); doveadm_print_header("value", "value", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); replicator_send(ctx, "STATUS\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; value = strchr(line, '\t'); if (value != NULL) *value++ = '\0'; else value = ""; doveadm_print(line); doveadm_print(value); } replicator_disconnect(ctx); } static void cmd_replicator_status(int argc, char *argv[]) { struct replicator_context *ctx; const char *line, *const *args; time_t last_fast, last_full, last_success; ctx = cmd_replicator_init(&argc, &argv, "a:", cmd_replicator_status); if (argv[1] == NULL) { cmd_replicator_status_overview(ctx); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("username", "username", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("priority"); doveadm_print_header_simple("fast sync"); doveadm_print_header_simple("full sync"); doveadm_print_header_simple("success sync"); doveadm_print_header_simple("failed"); replicator_send(ctx, t_strdup_printf("STATUS\t%s\n", str_tabescape(argv[1]))); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { args = t_strsplit_tabescaped(line); if (str_array_length(args) >= 5 && str_to_time(args[2], &last_fast) == 0 && str_to_time(args[3], &last_full) == 0 && str_to_time(args[5], &last_success) == 0) { doveadm_print(args[0]); doveadm_print(args[1]); doveadm_print(time_ago(last_fast)); doveadm_print(time_ago(last_full)); doveadm_print(time_ago(last_success)); doveadm_print(args[4][0] == '0' ? "-" : "y"); } } T_END; } if (line == NULL) { i_error("Replicator disconnected unexpectedly"); doveadm_exit_code = EX_TEMPFAIL; } replicator_disconnect(ctx); } static void cmd_replicator_dsync_status(int argc, char *argv[]) { struct replicator_context *ctx; const char *line; unsigned int i; ctx = cmd_replicator_init(&argc, &argv, "a:", cmd_replicator_dsync_status); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("username", "username", DOVEADM_PRINT_HEADER_FLAG_EXPAND); doveadm_print_header_simple("type"); doveadm_print_header_simple("status"); replicator_send(ctx, "STATUS-DSYNC\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { const char *const *args = t_strsplit_tabescaped(line); for (i = 0; i < 3; i++) { if (args[i] == NULL) break; doveadm_print(args[i]); } for (; i < 3; i++) doveadm_print(""); } T_END; } replicator_disconnect(ctx); } static void cmd_replicator_replicate(int argc, char *argv[]) { struct replicator_context *ctx; string_t *str; const char *line; if (argv[1] == NULL) replicator_cmd_help(cmd_replicator_replicate); ctx = cmd_replicator_init(&argc, &argv, "a:fp:", cmd_replicator_replicate); str = t_str_new(128); str_append(str, "REPLICATE\t"); if (ctx->priority == NULL) str_append_tabescaped(str, "low"); else str_append_tabescaped(str, ctx->priority); str_append_c(str, '\t'); if (ctx->full_sync) str_append_c(str, 'f'); str_append_c(str, '\t'); str_append_tabescaped(str, argv[1]); str_append_c(str, '\n'); replicator_send(ctx, str_c(str)); doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); doveadm_print_header("result", "result", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); line = i_stream_read_next_line(ctx->input); if (line == NULL) { i_error("Replicator disconnected unexpectedly"); doveadm_exit_code = EX_TEMPFAIL; } else if (line[0] != '+') { i_error("Replicator failed: %s", line+1); doveadm_exit_code = EX_USAGE; } else { doveadm_print(t_strdup_printf("%s users updated", line+1)); } replicator_disconnect(ctx); } static void cmd_replicator_add(int argc, char *argv[]) { struct replicator_context *ctx; string_t *str; const char *line; if (argv[1] == NULL) replicator_cmd_help(cmd_replicator_add); ctx = cmd_replicator_init(&argc, &argv, "a:", cmd_replicator_add); str = t_str_new(128); str_append(str, "ADD\t"); str_append_tabescaped(str, argv[1]); str_append_c(str, '\n'); replicator_send(ctx, str_c(str)); line = i_stream_read_next_line(ctx->input); if (line == NULL) { i_error("Replicator disconnected unexpectedly"); doveadm_exit_code = EX_TEMPFAIL; } else if (line[0] != '+') { i_error("Replicator failed: %s", line+1); doveadm_exit_code = EX_USAGE; } replicator_disconnect(ctx); } static void cmd_replicator_remove(int argc, char *argv[]) { struct replicator_context *ctx; string_t *str; const char *line; if (argv[1] == NULL) replicator_cmd_help(cmd_replicator_remove); ctx = cmd_replicator_init(&argc, &argv, "a:", cmd_replicator_remove); str = t_str_new(128); str_append(str, "REMOVE\t"); str_append_tabescaped(str, argv[1]); str_append_c(str, '\n'); replicator_send(ctx, str_c(str)); line = i_stream_read_next_line(ctx->input); if (line == NULL) { i_error("Replicator disconnected unexpectedly"); doveadm_exit_code = EX_TEMPFAIL; } else if (line[0] != '+') { i_error("Replicator failed: %s", line+1); doveadm_exit_code = EX_USAGE; } replicator_disconnect(ctx); } struct doveadm_cmd doveadm_cmd_replicator[] = { { cmd_replicator_status, "replicator status", "[-a ] []" }, { cmd_replicator_dsync_status, "replicator dsync-status", "[-a ]" }, { cmd_replicator_replicate, "replicator replicate", "[-a ] [-f] [-p ] " }, { cmd_replicator_add, "replicator add", "[-a ] " }, { cmd_replicator_remove, "replicator remove", "[-a ] " }, }; static void replicator_cmd_help(doveadm_command_t *cmd) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_replicator); i++) { if (doveadm_cmd_replicator[i].cmd == cmd) help(&doveadm_cmd_replicator[i]); } i_unreached(); } void doveadm_register_replicator_commands(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(doveadm_cmd_replicator); i++) doveadm_register_cmd(&doveadm_cmd_replicator[i]); } dovecot-2.2.33.2/src/doveadm/doveadm-master.c0000644000175000017500000001607013165463624015625 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "istream.h" #include "write-full.h" #include "doveadm.h" #include "doveadm-print.h" #include #include #include #define MASTER_PID_FILE_NAME "master.pid" static bool pid_file_read(const char *path, pid_t *pid_r) { char buf[32]; int fd; ssize_t ret; bool found; fd = open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return FALSE; i_fatal("open(%s) failed: %m", path); } ret = read(fd, buf, sizeof(buf)-1); if (ret <= 0) { if (ret == 0) i_error("Empty PID file in %s", path); else i_fatal("read(%s) failed: %m", path); found = FALSE; } else { if (buf[ret-1] == '\n') ret--; buf[ret] = '\0'; if (str_to_pid(buf, pid_r) < 0) found = FALSE; else { found = !(*pid_r == getpid() || (kill(*pid_r, 0) < 0 && errno == ESRCH)); } } i_close_fd(&fd); return found; } void doveadm_master_send_signal(int signo) { const char *pidfile_path; unsigned int i; pid_t pid; pidfile_path = t_strconcat(doveadm_settings->base_dir, "/"MASTER_PID_FILE_NAME, NULL); if (!pid_file_read(pidfile_path, &pid)) i_fatal("Dovecot is not running (read from %s)", pidfile_path); if (kill(pid, signo) < 0) i_fatal("kill(%s, %d) failed: %m", dec2str(pid), signo); if (signo == SIGTERM) { /* wait for a while for the process to die */ usleep(1000); for (i = 0; i < 30; i++) { if (kill(pid, 0) < 0) { if (errno != ESRCH) i_error("kill() failed: %m"); break; } usleep(100000); } } } static void cmd_stop(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { doveadm_master_send_signal(SIGTERM); } static void cmd_reload(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { doveadm_master_send_signal(SIGHUP); } static struct istream *master_service_send_cmd(const char *cmd) { const char *path; path = t_strconcat(doveadm_settings->base_dir, "/master", NULL); int fd = net_connect_unix(path); if (fd == -1) i_fatal("net_connect_unix(%s) failed: %m", path); net_set_nonblock(fd, FALSE); const char *str = t_strdup_printf("VERSION\tmaster-client\t1\t0\n%s\n", cmd); if (write_full(fd, str, strlen(str)) < 0) i_error("write(%s) failed: %m", path); return i_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE); } static struct istream * master_service_send_cmd_with_args(const char *cmd, const char *const *args) { string_t *str = t_str_new(128); str_append(str, cmd); if (args != NULL) { for (unsigned int i = 0; args[i] != NULL; i++) { str_append_c(str, '\t'); str_append_tabescaped(str, args[i]); } } return master_service_send_cmd(str_c(str)); } static void cmd_service_stop(struct doveadm_cmd_context *cctx) { const char *line, *const *services; if (!doveadm_cmd_param_array(cctx, "service", &services)) i_fatal("service parameter missing"); struct istream *input = master_service_send_cmd_with_args("STOP", services); alarm(5); if (i_stream_read_next_line(input) == NULL || (line = i_stream_read_next_line(input)) == NULL) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); doveadm_exit_code = EX_TEMPFAIL; } else if (line[0] == '-') { doveadm_exit_code = DOVEADM_EX_NOTFOUND; i_error("%s", line+1); } else if (line[0] != '+') { i_error("Unexpected input from %s: %s", i_stream_get_name(input), line); doveadm_exit_code = EX_TEMPFAIL; } alarm(0); i_stream_destroy(&input); } static void cmd_service_status(struct doveadm_cmd_context *cctx) { const char *line, *const *services; if (!doveadm_cmd_param_array(cctx, "service", &services)) services = NULL; struct istream *input = master_service_send_cmd("SERVICE-STATUS"); doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); doveadm_print_header_simple("name"); doveadm_print_header_simple("process_count"); doveadm_print_header_simple("process_avail"); doveadm_print_header_simple("process_limit"); doveadm_print_header_simple("client_limit"); doveadm_print_header_simple("throttle_secs"); doveadm_print_header_simple("exit_failure_last"); doveadm_print_header_simple("exit_failures_in_sec"); doveadm_print_header_simple("last_drop_warning"); doveadm_print_header_simple("listen_pending"); doveadm_print_header_simple("listening"); alarm(5); while ((line = i_stream_read_next_line(input)) != NULL) { if (line[0] == '\0') break; T_BEGIN { const char *const *args = t_strsplit_tabescaped(line); if (str_array_length(args) >= 11 && (services == NULL || str_array_find(services, args[0]))) { for (unsigned int i = 0; i < 11; i++) doveadm_print(args[i]); } } T_END; } if (line == NULL) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); doveadm_exit_code = EX_TEMPFAIL; } alarm(0); i_stream_destroy(&input); } static void cmd_process_status(struct doveadm_cmd_context *cctx) { const char *line, *const *services; if (!doveadm_cmd_param_array(cctx, "service", &services)) services = NULL; struct istream *input = master_service_send_cmd_with_args("PROCESS-STATUS", services); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("name"); doveadm_print_header_simple("pid"); doveadm_print_header_simple("available_count"); doveadm_print_header_simple("total_count"); doveadm_print_header_simple("idle_start"); doveadm_print_header_simple("last_status_update"); doveadm_print_header_simple("last_kill_sent"); alarm(5); while ((line = i_stream_read_next_line(input)) != NULL) { if (line[0] == '\0') break; T_BEGIN { const char *const *args = t_strsplit_tabescaped(line); if (str_array_length(args) >= 7) { for (unsigned int i = 0; i < 7; i++) doveadm_print(args[i]); } } T_END; } if (line == NULL) { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); doveadm_exit_code = EX_TEMPFAIL; } alarm(0); i_stream_destroy(&input); } struct doveadm_cmd_ver2 doveadm_cmd_stop_ver2 = { .old_cmd = cmd_stop, .name = "stop", .usage = "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_reload_ver2 = { .old_cmd = cmd_reload, .name = "reload", .usage = "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_service_stop_ver2 = { .cmd = cmd_service_stop, .name = "service stop", .usage = " [ [...]]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_service_status_ver2 = { .cmd = cmd_service_status, .name = "service status", .usage = "[ [...]]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_process_status_ver2 = { .cmd = cmd_process_status, .name = "process status", .usage = "[ [...]]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-print-table.c0000644000175000017500000001445313165463624016556 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "unichar.h" #include "doveadm-print-private.h" #include #include #include #include #define DEFAULT_COLUMNS 80 #define MIN_COLUMNS 30 #define MAX_BUFFER_LINES 100 struct doveadm_print_table_header { const char *key; const char *title; enum doveadm_print_header_flags flags; size_t min_length, max_length, length; }; struct doveadm_print_table_context { pool_t pool; ARRAY(struct doveadm_print_table_header) headers; ARRAY_TYPE(const_string) buffered_values; string_t *stream; unsigned int hdr_idx; unsigned int columns; unsigned int lengths_set:1; }; static struct doveadm_print_table_context *ctx; static void doveadm_print_table_header(const struct doveadm_print_header *hdr) { struct doveadm_print_table_header *thdr; thdr = array_append_space(&ctx->headers); thdr->key = p_strdup(ctx->pool, hdr->key); thdr->title = p_strdup(ctx->pool, hdr->title); thdr->length = thdr->max_length = thdr->min_length = strlen(hdr->title); thdr->flags = hdr->flags; } static void doveadm_calc_header_length(void) { struct doveadm_print_table_header *headers; const char *value, *const *values; unsigned int i, line, hdr_count, value_count, line_count; size_t len, max_length, orig_length, diff; ctx->lengths_set = TRUE; headers = array_get_modifiable(&ctx->headers, &hdr_count); values = array_get(&ctx->buffered_values, &value_count); i_assert((value_count % hdr_count) == 0); line_count = value_count / hdr_count; /* find min and max lengths of fields */ for (line = 0; line < line_count; line++) { for (i = 0; i < hdr_count; i++) { value = values[line*hdr_count + i]; len = value == NULL ? 0 : uni_utf8_strlen(value); if (headers[i].min_length > len) headers[i].min_length = len; if (headers[i].max_length < len) { headers[i].max_length = len; headers[i].length = len; } } } /* +1 for space between fields */ max_length = 0; for (i = 0; i < hdr_count; i++) max_length += headers[i].max_length + 1; max_length--; while (max_length > ctx->columns) { /* shrink something so we'll fit */ orig_length = max_length; for (i = hdr_count - 1;; i--) { diff = headers[i].length - headers[i].min_length; if (max_length - diff <= ctx->columns) { /* we can finish with this */ diff = max_length - ctx->columns; headers[i].length -= diff; max_length -= diff; break; } if (diff > 0) { /* take a bit off from it */ headers[i].length -= diff == 1 ? 1 : diff/2; } if (i == 0) break; } if (max_length == orig_length) { /* can't shrink it any more */ break; } } if (max_length < ctx->columns) { for (i = 0; i < hdr_count; i++) { if ((headers[i].flags & DOVEADM_PRINT_HEADER_FLAG_EXPAND) != 0) { i++; break; } } headers[i-1].length += (ctx->columns - max_length) / 2; } } static size_t utf8_correction(const char *str) { size_t i, len = 0; for (i = 0; str[i] != '\0'; i++) { if ((str[i] & 0xc0) == 0x80) len++; } return len; } static void doveadm_print_next(const char *value) { const struct doveadm_print_table_header *hdr; int value_padded_len; hdr = array_idx(&ctx->headers, ctx->hdr_idx); value_padded_len = hdr->length + utf8_correction(value); if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY) == 0) printf("%-*s", value_padded_len, value); else printf("%*s", value_padded_len, value); if (++ctx->hdr_idx == array_count(&ctx->headers)) { ctx->hdr_idx = 0; printf("\n"); } else { printf(" "); } } static void doveadm_print_headers(void) { const struct doveadm_print_table_header *headers; unsigned int i, count; if (doveadm_print_hide_titles) return; headers = array_get(&ctx->headers, &count); /* if all headers are hidden, don't print any of them */ for (i = 0; i < count; i++) { if ((headers[i].flags & DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE) == 0) break; } if (i == count) return; for (i = 0; i < count; i++) { if (i > 0) fprintf(stderr, " "); if ((headers[i].flags & DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY) == 0) { fprintf(stderr, "%-*s", (int)headers[i].length, headers[i].title); } else { fprintf(stderr, "%*s", (int)headers[i].length, headers[i].title); } } fprintf(stderr, "\n"); } static void doveadm_buffer_flush(void) { const char *const *valuep; doveadm_calc_header_length(); doveadm_print_headers(); array_foreach(&ctx->buffered_values, valuep) doveadm_print_next(*valuep); array_clear(&ctx->buffered_values); } static void doveadm_print_table_print(const char *value) { unsigned int line_count; if (!ctx->lengths_set) { line_count = array_count(&ctx->buffered_values) / array_count(&ctx->headers); if (line_count < MAX_BUFFER_LINES) { value = p_strdup(ctx->pool, value); array_append(&ctx->buffered_values, &value, 1); return; } doveadm_buffer_flush(); } doveadm_print_next(value); } static void doveadm_print_table_print_stream(const unsigned char *value, size_t size) { if (memchr(value, '\n', size) != NULL) i_fatal("table formatter doesn't support multi-line values"); if (size != 0) str_append_n(ctx->stream, value, size); else { doveadm_print_table_print(str_c(ctx->stream)); str_truncate(ctx->stream, 0); } } static void doveadm_print_table_flush(void) { if (!ctx->lengths_set && array_count(&ctx->headers) > 0) doveadm_buffer_flush(); } static void doveadm_print_table_init(void) { pool_t pool; struct winsize ws; pool = pool_alloconly_create("doveadm print table", 2048); ctx = p_new(pool, struct doveadm_print_table_context, 1); ctx->pool = pool; ctx->stream = str_new(default_pool, 128); p_array_init(&ctx->headers, pool, 16); i_array_init(&ctx->buffered_values, 64); ctx->columns = DEFAULT_COLUMNS; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { ctx->columns = ws.ws_col < MIN_COLUMNS ? MIN_COLUMNS : ws.ws_col; } } static void doveadm_print_table_deinit(void) { str_free(&ctx->stream); array_free(&ctx->buffered_values); pool_unref(&ctx->pool); ctx = NULL; } struct doveadm_print_vfuncs doveadm_print_table_vfuncs = { "table", doveadm_print_table_init, doveadm_print_table_deinit, doveadm_print_table_header, doveadm_print_table_print, doveadm_print_table_print_stream, doveadm_print_table_flush }; dovecot-2.2.33.2/src/doveadm/doveadm-mail.h0000644000175000017500000002055213165463624015261 00000000000000#ifndef DOVEADM_MAIL_H #define DOVEADM_MAIL_H #include #include "doveadm.h" #include "doveadm-util.h" #include "module-context.h" #include "mail-error.h" #include "mail-storage-service.h" struct mailbox; struct mailbox_list; struct mail_storage; struct mail_user; struct doveadm_mail_cmd_context; struct doveadm_mail_cmd_vfuncs { /* Parse one getopt() parameter. This is called for each parameter. */ bool (*parse_arg)(struct doveadm_mail_cmd_context *ctx, int c); /* Usually not needed. The preinit() is called just after parsing all parameters, but before any userdb lookups are done. This allows the preinit() to alter the userdb lookup behavior (especially service_flags). */ void (*preinit)(struct doveadm_mail_cmd_context *ctx); /* Initialize the command. Most importantly if the function prints anything, this should initialize the headers. It shouldn't however do any actual work. The init() is called also when doveadm is performing the work via doveadm-server, which could be running remotely with completely different Dovecot configuration. */ void (*init)(struct doveadm_mail_cmd_context *ctx, const char *const args[]); /* Usually not needed. When iterating through multiple users, use this function to get the next username. Overriding this is usually done only when there's a known username filter, such as the expire plugin. */ int (*get_next_user)(struct doveadm_mail_cmd_context *ctx, const char **username_r); /* Usually not needed. This is called between mail_storage_service_lookup() and mail_storage_service_next() for each user. */ int (*prerun)(struct doveadm_mail_cmd_context *ctx, struct mail_storage_service_user *service_user, const char **error_r); /* This is the main function which performs all the work for the command. This is called once per each user. */ int (*run)(struct doveadm_mail_cmd_context *ctx, struct mail_user *mail_user); /* Deinitialize the command. Called once at the end - even if preinit() or init() was never called. */ void (*deinit)(struct doveadm_mail_cmd_context *ctx); }; struct doveadm_mail_cmd_module_register { unsigned int id; }; union doveadm_mail_cmd_module_context { struct doveadm_mail_cmd_vfuncs super; struct doveadm_mail_cmd_module_register *reg; }; struct doveadm_mail_cmd_context { pool_t pool; const struct doveadm_mail_cmd *cmd; const char *const *args; /* args including -options */ const char *const *full_args; /* connection via doveadm-server */ struct client_connection *conn; const char *getopt_args; const struct doveadm_settings *set; enum mail_storage_service_flags service_flags; struct mail_storage_service_ctx *storage_service; struct mail_storage_service_input storage_service_input; /* search args aren't set for all mail commands */ struct mail_search_args *search_args; struct istream *users_list_input; struct ip_addr cur_client_ip; const char *cur_username; struct mail_storage_service_user *cur_service_user; struct mail_user *cur_mail_user; struct doveadm_mail_cmd_vfuncs v; struct istream *cmd_input; int cmd_input_fd; ARRAY(union doveadm_mail_cmd_module_context *) module_contexts; /* if non-zero, exit with this code */ int exit_code; /* This command is being called by a remote doveadm client. */ unsigned int proxying:1; /* We're handling only a single user */ unsigned int iterate_single_user:1; /* We're going through all users (not set for wildcard usernames) */ unsigned int iterate_all_users:1; /* Add username header to all replies */ unsigned int add_username_header:1; /* Running from CLI doveadm (not doveadm-server) */ unsigned int cli:1; }; struct doveadm_mail_cmd { struct doveadm_mail_cmd_context *(*alloc)(void); const char *name; const char *usage_args; }; ARRAY_DEFINE_TYPE(doveadm_mail_cmd, struct doveadm_mail_cmd); extern ARRAY_TYPE(doveadm_mail_cmd) doveadm_mail_cmds; extern void (*hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx); extern struct doveadm_mail_cmd_module_register doveadm_mail_cmd_module_register; extern char doveadm_mail_cmd_hide; bool doveadm_is_killed(void); int doveadm_killed_signo(void); bool doveadm_mail_try_run(const char *cmd_name, int argc, char *argv[]); void doveadm_mail_register_cmd(const struct doveadm_mail_cmd *cmd); const struct doveadm_mail_cmd *doveadm_mail_cmd_find(const char *cmd_name); void doveadm_mail_usage(string_t *out); void doveadm_mail_help(const struct doveadm_mail_cmd *cmd) ATTR_NORETURN; void doveadm_mail_help_name(const char *cmd_name) ATTR_NORETURN; void doveadm_mail_try_help_name(const char *cmd_name); bool doveadm_mail_has_subcommands(const char *cmd_name); void doveadm_mail_init(void); void doveadm_mail_deinit(void); const struct doveadm_mail_cmd * doveadm_mail_cmd_find_from_argv(const char *cmd_name, int *argc, const char *const **argv); struct doveadm_mail_cmd_context * doveadm_mail_cmd_init(const struct doveadm_mail_cmd *cmd, const struct doveadm_settings *set); int doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx, const struct doveadm_cmd_context *cctx, const char **error_r); int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, const struct mail_storage_service_input *input, const char **error_r); void doveadm_mail_server_flush(void); /* Request input stream to be read (from stdin). This must be called from the command's init() function. */ void doveadm_mail_get_input(struct doveadm_mail_cmd_context *ctx); struct mailbox * doveadm_mailbox_find(struct mail_user *user, const char *mailbox); struct mail_search_args * doveadm_mail_build_search_args(const char *const args[]); void doveadm_mailbox_args_check(const char *const args[]); struct mail_search_args * doveadm_mail_mailbox_search_args_build(const char *const args[]); void expunge_search_args_check(struct mail_search_args *args, const char *cmd); struct doveadm_mail_cmd_context * doveadm_mail_cmd_alloc_size(size_t size); #define doveadm_mail_cmd_alloc(type) \ (type *)doveadm_mail_cmd_alloc_size(sizeof(type)) void doveadm_mail_failed_error(struct doveadm_mail_cmd_context *ctx, enum mail_error error); void doveadm_mail_failed_storage(struct doveadm_mail_cmd_context *ctx, struct mail_storage *storage); void doveadm_mail_failed_mailbox(struct doveadm_mail_cmd_context *ctx, struct mailbox *box); void doveadm_mail_failed_list(struct doveadm_mail_cmd_context *ctx, struct mailbox_list *list); extern struct doveadm_mail_cmd cmd_batch; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_set_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_unset_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_get_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_metadata_list_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_status_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_list_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_create_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_delete_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_rename_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_subscribe_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_unsubscribe_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_fetch_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_save_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_index_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_altmove_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_deduplicate_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_expunge_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_flags_add_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_flags_remove_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_flags_replace_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_import_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_search_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_copy_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_move_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_update_ver2; extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_path_ver2; #define DOVEADM_CMD_MAIL_COMMON \ DOVEADM_CMD_PARAM('A', "all-users", CMD_PARAM_BOOL, 0) \ DOVEADM_CMD_PARAM('S', "socket-path", CMD_PARAM_STR, 0) \ DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) \ DOVEADM_CMD_PARAM('F', "user-file", CMD_PARAM_ISTREAM, 0) #define DOVEADM_CMD_MAIL_USAGE_PREFIX \ "[-u |-A] [-S ] " #endif dovecot-2.2.33.2/src/doveadm/doveadm-print-private.h0000644000175000017500000000154413123174404017130 00000000000000#ifndef DOVEADM_PRINT_PRIVATE_H #define DOVEADM_PRINT_PRIVATE_H #include "doveadm-print.h" struct doveadm_print_header { const char *key; const char *title; enum doveadm_print_header_flags flags; }; struct doveadm_print_vfuncs { const char *name; void (*init)(void); void (*deinit)(void); void (*header)(const struct doveadm_print_header *hdr); void (*print)(const char *value); void (*print_stream)(const unsigned char *value, size_t size); void (*flush)(void); }; extern struct doveadm_print_vfuncs doveadm_print_flow_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_tab_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_table_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_pager_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_json_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_formatted_vfuncs; #endif dovecot-2.2.33.2/src/doveadm/doveadm-dump.h0000644000175000017500000000143313123174404015266 00000000000000#ifndef DOVEADM_DUMP_H #define DOVEADM_DUMP_H #include "doveadm.h" struct doveadm_cmd_dump { const char *name; bool (*test)(const char *path); doveadm_command_t *cmd; }; extern struct doveadm_cmd_dump doveadm_cmd_dump_dbox; extern struct doveadm_cmd_dump doveadm_cmd_dump_index; extern struct doveadm_cmd_dump doveadm_cmd_dump_log; extern struct doveadm_cmd_dump doveadm_cmd_dump_mailboxlog; extern struct doveadm_cmd_dump doveadm_cmd_dump_thread; extern struct doveadm_cmd_dump doveadm_cmd_dump_zlib; extern struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_file; extern struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_key; void doveadm_dump_register(const struct doveadm_cmd_dump *dump); void print_dump_types(void); void doveadm_dump_init(void); void doveadm_dump_deinit(void); #endif dovecot-2.2.33.2/src/doveadm/Makefile.am0000644000175000017500000001054313165463624014604 00000000000000doveadm_moduledir = $(moduledir)/doveadm pkglibexecdir = $(libexecdir)/dovecot SUBDIRS = dsync bin_PROGRAMS = doveadm pkglibexec_PROGRAMS = doveadm-server AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap-storage \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-dcrypt \ -I$(top_srcdir)/src/auth \ -DMODULEDIR=\""$(moduledir)"\" \ -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \ -DDOVEADM_MODULEDIR=\""$(doveadm_moduledir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DBINDIR=\""$(bindir)"\" \ -DMANDIR=\""$(mandir)"\" cmd_pw_libs = \ ../auth/libpassword.a \ ../lib-ntlm/libntlm.a \ ../lib-otp/libotp.a libs = \ dsync/libdsync.la \ ../lib-compression/libcompression.la doveadm_LDADD = \ $(libs) \ $(cmd_pw_libs) \ $(CRYPT_LIBS) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) doveadm_DEPENDENCIES = \ $(libs) \ $(cmd_pw_libs) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) doveadm_server_LDADD = \ $(libs) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) doveadm_server_DEPENDENCIES = \ $(libs) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) doveadm_common_cmds = \ doveadm-auth.c \ doveadm-dict.c \ doveadm-director.c \ doveadm-fs.c \ doveadm-instance.c \ doveadm-kick.c \ doveadm-log.c \ doveadm-master.c \ doveadm-mount.c \ doveadm-mutf7.c \ doveadm-penalty.c \ doveadm-proxy.c \ doveadm-replicator.c \ doveadm-sis.c \ doveadm-stats.c \ doveadm-who.c doveadm_common_mail_cmds = \ doveadm-dsync.c \ doveadm-mail.c \ doveadm-mail-altmove.c \ doveadm-mail-batch.c \ doveadm-mail-deduplicate.c \ doveadm-mail-expunge.c \ doveadm-mail-fetch.c \ doveadm-mail-flags.c \ doveadm-mail-import.c \ doveadm-mail-index.c \ doveadm-mail-iter.c \ doveadm-mail-mailbox.c \ doveadm-mail-mailbox-metadata.c \ doveadm-mail-mailbox-status.c \ doveadm-mail-copymove.c \ doveadm-mailbox-list-iter.c \ doveadm-mail-save.c \ doveadm-mail-search.c \ doveadm-mail-server.c # these aren't actually useful in doveadm-server, but plugins may implement # both dumping and some other commands inside a single plugin. not having the # dump functions in doveadm-server fails to load such plugins. doveadm_common_dump_cmds = \ doveadm-dump.c \ doveadm-dump-dbox.c \ doveadm-dump-index.c \ doveadm-dump-log.c \ doveadm-dump-mailboxlog.c \ doveadm-dump-thread.c \ doveadm-dump-dcrypt-file.c \ doveadm-dump-dcrypt-key.c \ doveadm-zlib.c common = \ $(doveadm_common_cmds) \ $(doveadm_common_mail_cmds) \ $(doveadm_common_dump_cmds) \ doveadm-cmd.c \ doveadm-print.c \ doveadm-settings.c \ doveadm-util.c \ server-connection.c \ doveadm-print-formatted.c doveadm_SOURCES = \ $(common) \ doveadm.c \ doveadm-print-flow.c \ doveadm-print-pager.c \ doveadm-print-tab.c \ doveadm-print-table.c \ doveadm-print-json.c \ doveadm-pw.c doveadm_server_SOURCES = \ $(common) \ doveadm-auth-server.c \ client-connection.c \ client-connection-http.c \ doveadm-print-server.c \ doveadm-print-json.c \ main.c pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ doveadm.h \ doveadm-cmd.h \ doveadm-dsync.h \ doveadm-dump.h \ doveadm-mail.h \ doveadm-mail-iter.h \ doveadm-mailbox-list-iter.h \ doveadm-print.h \ doveadm-print-private.h \ doveadm-settings.h \ doveadm-util.h noinst_HEADERS = \ client-connection.h \ client-connection-private.h \ server-connection.h \ doveadm-server.h \ doveadm-who.h install-exec-local: rm -f $(DESTDIR)$(bindir)/dsync $(LN_S) doveadm $(DESTDIR)$(bindir)/dsync test_programs = \ test-doveadm-util noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_doveadm_util_SOURCES = test-doveadm-util.c test_doveadm_util_LDADD = doveadm-util.o $(test_libs) $(MODULE_LIBS) test_doveadm_util_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/doveadm/doveadm-dsync.c0000644000175000017500000011621513165463624015454 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "array.h" #include "execv-const.h" #include "fd-set-nonblock.h" #include "child-wait.h" #include "istream.h" #include "ostream.h" #include "iostream-ssl.h" #include "iostream-rawlog.h" #include "write-full.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "process-title.h" #include "settings-parser.h" #include "imap-util.h" #include "master-service.h" #include "master-service-settings.h" #include "master-service-ssl-settings.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-user.h" #include "mail-namespace.h" #include "mailbox-list-private.h" #include "doveadm-settings.h" #include "doveadm-mail.h" #include "doveadm-print.h" #include "doveadm-server.h" #include "client-connection.h" #include "server-connection.h" #include "dsync/dsync-brain.h" #include "dsync/dsync-ibc.h" #include "doveadm-dsync.h" #include #include #include #include #define DSYNC_COMMON_GETOPT_ARGS "+1a:dDEfg:I:l:m:n:NO:Pr:Rs:t:e:T:Ux:" #define DSYNC_REMOTE_CMD_EXIT_WAIT_SECS 30 /* The broken_char is mainly set to get a proper error message when trying to convert a mailbox with a name that can't be used properly translated between vname/storage_name and would otherwise be mixed up with a normal "mailbox doesn't exist" error message. This could be any control character, since none of them are allowed to be created in regular mailbox names. */ #define DSYNC_LIST_BROKEN_CHAR '\003' #define DSYNC_DEFAULT_IO_STREAM_TIMEOUT_SECS (60*10) enum dsync_run_type { DSYNC_RUN_TYPE_LOCAL, DSYNC_RUN_TYPE_STREAM, DSYNC_RUN_TYPE_CMD }; struct dsync_cmd_context { struct doveadm_mail_cmd_context ctx; enum dsync_brain_sync_type sync_type; const char *mailbox; const char *sync_flags; const char *virtual_all_box; guid_128_t mailbox_guid; const char *state_input, *rawlog_path; ARRAY_TYPE(const_string) exclude_mailboxes; ARRAY_TYPE(const_string) namespace_prefixes; time_t sync_since_timestamp; time_t sync_until_timestamp; uoff_t sync_max_size; unsigned int io_timeout_secs; const char *remote_name; const char *local_location; pid_t remote_pid; const char *const *remote_cmd_args; struct child_wait *child_wait; int exit_status; int fd_in, fd_out, fd_err; struct io *io_err; struct istream *input, *err_stream; struct ostream *output; size_t input_orig_bufsize, output_orig_bufsize; struct ssl_iostream_context *ssl_ctx; struct ssl_iostream *ssl_iostream; enum dsync_run_type run_type; struct server_connection *tcp_conn; const char *error; unsigned int lock_timeout; unsigned int import_commit_msgs_interval; unsigned int lock:1; unsigned int purge_remote:1; unsigned int sync_visible_namespaces:1; unsigned int default_replica_location:1; unsigned int oneway:1; unsigned int backup:1; unsigned int reverse_backup:1; unsigned int remote_user_prefix:1; unsigned int no_mail_sync:1; unsigned int no_mailbox_renames:1; unsigned int local_location_from_arg:1; unsigned int replicator_notify:1; unsigned int exited:1; unsigned int empty_hdr_workaround:1; }; static bool legacy_dsync = FALSE; static void remote_error_input(struct dsync_cmd_context *ctx) { const unsigned char *data; size_t size; const char *line; switch (i_stream_read(ctx->err_stream)) { case -2: data = i_stream_get_data(ctx->err_stream, &size); fprintf(stderr, "%.*s", (int)size, data); i_stream_skip(ctx->err_stream, size); break; case -1: if (ctx->io_err != NULL) io_remove(&ctx->io_err); break; default: while ((line = i_stream_next_line(ctx->err_stream)) != NULL) fprintf(stderr, "%s\n", line); break; } } static void run_cmd(struct dsync_cmd_context *ctx, const char *const *args) { int fd_in[2], fd_out[2], fd_err[2]; ctx->remote_cmd_args = p_strarray_dup(ctx->ctx.pool, args); if (pipe(fd_in) < 0 || pipe(fd_out) < 0 || pipe(fd_err) < 0) i_fatal("pipe() failed: %m"); ctx->remote_pid = fork(); switch (ctx->remote_pid) { case -1: i_fatal("fork() failed: %m"); case 0: /* child, which will execute the proxy server. stdin/stdout goes to pipes which we'll pass to proxy client. */ if (dup2(fd_in[0], STDIN_FILENO) < 0 || dup2(fd_out[1], STDOUT_FILENO) < 0 || dup2(fd_err[1], STDERR_FILENO) < 0) i_fatal("dup2() failed: %m"); i_close_fd(&fd_in[0]); i_close_fd(&fd_in[1]); i_close_fd(&fd_out[0]); i_close_fd(&fd_out[1]); i_close_fd(&fd_err[0]); i_close_fd(&fd_err[1]); execvp_const(args[0], args); default: /* parent */ break; } i_close_fd(&fd_in[0]); i_close_fd(&fd_out[1]); i_close_fd(&fd_err[1]); ctx->fd_in = fd_out[0]; ctx->fd_out = fd_in[1]; ctx->fd_err = fd_err[0]; if (ctx->remote_user_prefix) { const char *prefix = t_strdup_printf("%s\n", ctx->ctx.cur_username); if (write_full(ctx->fd_out, prefix, strlen(prefix)) < 0) i_fatal("write(remote out) failed: %m"); } fd_set_nonblock(ctx->fd_err, TRUE); ctx->err_stream = i_stream_create_fd(ctx->fd_err, IO_BLOCK_SIZE, FALSE); i_stream_set_return_partial_line(ctx->err_stream, TRUE); } static void mirror_get_remote_cmd_line(const char *const *argv, const char *const **cmd_args_r) { ARRAY_TYPE(const_string) cmd_args; unsigned int i; const char *p; i_assert(argv[0] != NULL); t_array_init(&cmd_args, 16); for (i = 0; argv[i] != NULL; i++) { p = argv[i]; array_append(&cmd_args, &p, 1); } if (legacy_dsync) { /* we're executing dsync */ p = "server"; } else { /* we're executing doveadm */ p = "dsync-server"; } array_append(&cmd_args, &p, 1); array_append_zero(&cmd_args); *cmd_args_r = array_idx(&cmd_args, 0); } static const char *const * get_ssh_cmd_args(const char *host, const char *login, const char *mail_user) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { '\0', NULL, "login" }, { '\0', NULL, "host" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; ARRAY_TYPE(const_string) cmd_args; string_t *str, *str2; const char *value, *const *args; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = mail_user; tab[1].value = login; tab[2].value = host; t_array_init(&cmd_args, 8); str = t_str_new(128); str2 = t_str_new(128); args = t_strsplit(doveadm_settings->dsync_remote_cmd, " "); for (; *args != NULL; args++) { if (strchr(*args, '%') == NULL) value = *args; else { /* some automation: if parameter's all %variables expand to empty, but the %variable isn't the only text in the parameter, skip it. */ str_truncate(str, 0); str_truncate(str2, 0); var_expand(str, *args, tab); var_expand(str2, *args, static_tab); if (strcmp(str_c(str), str_c(str2)) == 0 && str_len(str) > 0) continue; value = t_strdup(str_c(str)); } array_append(&cmd_args, &value, 1); } array_append_zero(&cmd_args); return array_idx(&cmd_args, 0); } static bool mirror_get_remote_cmd(struct dsync_cmd_context *ctx, const char *user, const char *const **cmd_args_r) { const char *p, *host, *const *argv = ctx->ctx.args; if (argv[1] != NULL) { /* more than one parameter, so it contains a full command (e.g. ssh host dsync) */ mirror_get_remote_cmd_line(argv, cmd_args_r); return TRUE; } /* if it begins with /[a-z0-9]+:/, it's a mail location (e.g. mdbox:~/mail) */ for (p = argv[0]; *p != '\0'; p++) { if (!i_isalnum(*p)) { if (*p == ':') return FALSE; break; } } if (strchr(argv[0], ' ') != NULL || strchr(argv[0], '/') != NULL) { /* a) the whole command is in one string. this is mainly for backwards compatibility. b) script/path */ mirror_get_remote_cmd_line(t_strsplit(argv[0], " "), cmd_args_r); return TRUE; } /* [user@]host */ host = strchr(argv[0], '@'); if (host != NULL) user = t_strdup_until(argv[0], host++); else host = argv[0]; /* we'll assume virtual users, so in user@host it really means not to give ssh a username, but to give dsync -u user parameter. */ *cmd_args_r = get_ssh_cmd_args(host, "", user); return TRUE; } static void doveadm_user_init_dsync(struct mail_user *user) { struct mail_namespace *ns; user->dsyncing = TRUE; for (ns = user->namespaces; ns != NULL; ns = ns->next) ns->list->set.broken_char = DSYNC_LIST_BROKEN_CHAR; } static bool paths_are_equal(struct mail_user *user1, struct mail_user *user2, enum mailbox_list_path_type type) { const char *path1, *path2; i_assert(user1->namespaces != NULL); i_assert(user2->namespaces != NULL); return mailbox_list_get_root_path(user1->namespaces->list, type, &path1) && mailbox_list_get_root_path(user2->namespaces->list, type, &path2) && strcmp(path1, path2) == 0; } static int cmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user, struct dsync_brain *brain, struct dsync_ibc *ibc2, const char **changes_during_sync_r, enum mail_error *mail_error_r) { struct dsync_brain *brain2; struct mail_user *user2; struct setting_parser_context *set_parser; const char *location; bool brain1_running, brain2_running, changed1, changed2; bool remote_only_changes; int ret; *mail_error_r = 0; if (ctx->local_location_from_arg) location = ctx->ctx.args[0]; else { i_assert(ctx->local_location != NULL); location = ctx->local_location; } i_set_failure_prefix("dsync(%s): ", user->username); /* update mail_location and create another user for the second location. */ set_parser = mail_storage_service_user_get_settings_parser(ctx->ctx.cur_service_user); if (settings_parse_keyvalue(set_parser, "mail_location", location) < 0) i_unreached(); ret = mail_storage_service_next(ctx->ctx.storage_service, ctx->ctx.cur_service_user, &user2); if (ret < 0) { ctx->ctx.exit_code = ret == -1 ? EX_TEMPFAIL : EX_CONFIG; return -1; } doveadm_user_init_dsync(user2); if (mail_namespaces_get_root_sep(user->namespaces) != mail_namespaces_get_root_sep(user2->namespaces)) { i_error("Mail locations must use the same " "virtual mailbox hierarchy separator " "(specify separator for the default namespace)"); ctx->ctx.exit_code = EX_CONFIG; mail_user_unref(&user2); return -1; } if (paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_MAILBOX) && paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_INDEX)) { i_error("Both source and destination mail_location " "points to same directory: %s", mailbox_list_get_root_forced(user->namespaces->list, MAILBOX_LIST_PATH_TYPE_MAILBOX)); ctx->ctx.exit_code = EX_CONFIG; mail_user_unref(&user2); return -1; } brain2 = dsync_brain_slave_init(user2, ibc2, TRUE, ""); mail_user_unref(&user2); brain1_running = brain2_running = TRUE; changed1 = changed2 = TRUE; while (brain1_running || brain2_running) { if (dsync_brain_has_failed(brain) || dsync_brain_has_failed(brain2)) break; if (doveadm_is_killed()) { i_warning("Killed with signal %d", doveadm_killed_signo()); break; } i_assert(changed1 || changed2); brain1_running = dsync_brain_run(brain, &changed1); brain2_running = dsync_brain_run(brain2, &changed2); } *changes_during_sync_r = t_strdup(dsync_brain_get_unexpected_changes_reason(brain2, &remote_only_changes)); if (dsync_brain_deinit(&brain2, mail_error_r) < 0) return -1; return doveadm_is_killed() ? -1 : 0; } static void cmd_dsync_remote_exited(const struct child_wait_status *status, struct dsync_cmd_context *ctx) { ctx->exited = TRUE; ctx->exit_status = status->status; io_loop_stop(current_ioloop); } static void cmd_dsync_wait_remote(struct dsync_cmd_context *ctx) { struct timeout *to; /* wait in ioloop for the remote process to die. while we're running we're also reading and printing all errors that still coming from it. */ to = timeout_add(DSYNC_REMOTE_CMD_EXIT_WAIT_SECS*1000, io_loop_stop, current_ioloop); io_loop_run(current_ioloop); timeout_remove(&to); if (!ctx->exited) { i_error("Remote command process isn't dying, killing it"); if (kill(ctx->remote_pid, SIGKILL) < 0 && errno != ESRCH) { i_error("kill(%ld, SIGKILL) failed: %m", (long)ctx->remote_pid); } } } static void cmd_dsync_log_remote_status(int status, bool remote_errors_logged, const char *const *remote_cmd_args) { if (status == -1) ; else if (WIFSIGNALED(status)) { i_error("Remote command died with signal %d: %s", WTERMSIG(status), t_strarray_join(remote_cmd_args, " ")); } else if (!WIFEXITED(status)) { i_error("Remote command failed with status %d: %s", status, t_strarray_join(remote_cmd_args, " ")); } else if (WEXITSTATUS(status) == EX_TEMPFAIL && remote_errors_logged) { /* remote most likely already logged the error. don't bother logging another line about it */ } else if (WEXITSTATUS(status) != 0) { i_error("Remote command returned error %d: %s", WEXITSTATUS(status), t_strarray_join(remote_cmd_args, " ")); } } static void cmd_dsync_run_remote(struct mail_user *user) { i_set_failure_prefix("dsync-local(%s): ", user->username); io_loop_run(current_ioloop); } static const char *const * parse_ssh_location(const char *location, const char *username) { const char *host, *login; host = strchr(location, '@'); if (host != NULL) login = t_strdup_until(location, host++); else { host = location; login = ""; } return get_ssh_cmd_args(host, login, username); } static struct dsync_ibc * cmd_dsync_ibc_stream_init(struct dsync_cmd_context *ctx, const char *name, const char *temp_prefix) { if (ctx->input == NULL) { fd_set_nonblock(ctx->fd_in, TRUE); fd_set_nonblock(ctx->fd_out, TRUE); ctx->input = i_stream_create_fd(ctx->fd_in, (size_t)-1, FALSE); ctx->output = o_stream_create_fd(ctx->fd_out, (size_t)-1, FALSE); } else { i_assert(ctx->fd_in == -1 && ctx->fd_out == -1); ctx->fd_in = i_stream_get_fd(ctx->input); ctx->fd_out = o_stream_get_fd(ctx->output); ctx->input_orig_bufsize = i_stream_get_max_buffer_size(ctx->input); ctx->output_orig_bufsize = o_stream_get_max_buffer_size(ctx->output); i_stream_set_max_buffer_size(ctx->input, (size_t)-1); o_stream_set_max_buffer_size(ctx->output, (size_t)-1); } if (ctx->rawlog_path != NULL) { iostream_rawlog_create_path(ctx->rawlog_path, &ctx->input, &ctx->output); } i_stream_ref(ctx->input); o_stream_ref(ctx->output); return dsync_ibc_init_stream(ctx->input, ctx->output, name, temp_prefix, ctx->io_timeout_secs); } static void dsync_replicator_notify(struct dsync_cmd_context *ctx, enum dsync_brain_sync_type sync_type, const char *state_str) { #define REPLICATOR_HANDSHAKE "VERSION\treplicator-doveadm-client\t1\t0\n" const char *path; string_t *str; int fd; path = t_strdup_printf("%s/replicator-doveadm", ctx->ctx.cur_mail_user->set->base_dir); fd = net_connect_unix(path); if (fd == -1) { if (errno == ECONNREFUSED || errno == ENOENT) { /* replicator not running on this server. ignore. */ return; } i_error("net_connect_unix(%s) failed: %m", path); return; } fd_set_nonblock(fd, FALSE); str = t_str_new(128); str_append(str, REPLICATOR_HANDSHAKE"NOTIFY\t"); str_append_tabescaped(str, ctx->ctx.cur_mail_user->username); str_append_c(str, '\t'); if (sync_type == DSYNC_BRAIN_SYNC_TYPE_FULL) str_append_c(str, 'f'); str_append_c(str, '\t'); str_append_tabescaped(str, state_str); str_append_c(str, '\n'); if (write_full(fd, str_data(str), str_len(str)) < 0) i_error("write(%s) failed: %m", path); /* we only wanted to notify replicator. we don't care enough about the answer to wait for it. */ if (close(fd) < 0) i_error("close(%s) failed: %m", path); } static int cmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; struct dsync_ibc *ibc, *ibc2 = NULL; struct dsync_brain *brain; struct dsync_brain_settings set; struct mail_namespace *ns; const char *const *strp; enum dsync_brain_flags brain_flags; enum mail_error mail_error = 0, mail_error2; bool remote_errors_logged = FALSE; const char *changes_during_sync, *changes_during_sync2 = NULL; bool remote_only_changes; int ret = 0; i_zero(&set); if (_ctx->cur_client_ip.family != 0) { /* include the doveadm client's IP address in the ps output */ set.process_title_prefix = t_strdup_printf( "%s ", net_ip2addr(&_ctx->cur_client_ip)); } set.sync_since_timestamp = ctx->sync_since_timestamp; set.sync_until_timestamp = ctx->sync_until_timestamp; if (set.sync_since_timestamp > 0 && set.sync_until_timestamp > 0 && set.sync_since_timestamp > set.sync_until_timestamp) { i_fatal("start date is later than end date"); } set.sync_max_size = ctx->sync_max_size; set.sync_box = ctx->mailbox; set.sync_flag = ctx->sync_flags; set.virtual_all_box = ctx->virtual_all_box; memcpy(set.sync_box_guid, ctx->mailbox_guid, sizeof(set.sync_box_guid)); set.lock_timeout_secs = ctx->lock_timeout; set.import_commit_msgs_interval = ctx->import_commit_msgs_interval; set.state = ctx->state_input; set.mailbox_alt_char = doveadm_settings->dsync_alt_char[0]; if (*doveadm_settings->dsync_hashed_headers == '\0') { i_error("dsync_hashed_headers must not be empty"); ctx->ctx.exit_code = EX_USAGE; return -1; } set.hashed_headers = t_strsplit_spaces(doveadm_settings->dsync_hashed_headers, " ,"); if (array_count(&ctx->exclude_mailboxes) > 0) { /* array is NULL-terminated in init() */ set.exclude_mailboxes = array_idx(&ctx->exclude_mailboxes, 0); } doveadm_user_init_dsync(user); t_array_init(&set.sync_namespaces, array_count(&ctx->namespace_prefixes)); array_foreach(&ctx->namespace_prefixes, strp) { ns = mail_namespace_find(user->namespaces, *strp); if (ns == NULL) { i_error("Namespace not found: '%s'", *strp); ctx->ctx.exit_code = EX_USAGE; return -1; } array_append(&set.sync_namespaces, &ns, 1); } if (ctx->run_type == DSYNC_RUN_TYPE_LOCAL) dsync_ibc_init_pipe(&ibc, &ibc2); else { string_t *temp_prefix = t_str_new(64); mail_user_set_get_temp_prefix(temp_prefix, user->set); ibc = cmd_dsync_ibc_stream_init(ctx, ctx->remote_name, str_c(temp_prefix)); if (ctx->fd_err != -1) { ctx->io_err = io_add(ctx->fd_err, IO_READ, remote_error_input, ctx); } } brain_flags = DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS; if (ctx->sync_visible_namespaces) brain_flags |= DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES; if (ctx->purge_remote) brain_flags |= DSYNC_BRAIN_FLAG_PURGE_REMOTE; if (ctx->no_mailbox_renames) brain_flags |= DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES; if (ctx->backup) { if (ctx->reverse_backup) brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV; else brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND; } if (ctx->no_mail_sync) brain_flags |= DSYNC_BRAIN_FLAG_NO_MAIL_SYNC; if (ctx->oneway) brain_flags |= DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE; if (ctx->empty_hdr_workaround) brain_flags |= DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND; if (doveadm_debug) brain_flags |= DSYNC_BRAIN_FLAG_DEBUG; child_wait_init(); brain = dsync_brain_master_init(user, ibc, ctx->sync_type, brain_flags, &set); switch (ctx->run_type) { case DSYNC_RUN_TYPE_LOCAL: if (cmd_dsync_run_local(ctx, user, brain, ibc2, &changes_during_sync2, &mail_error) < 0) ret = -1; break; case DSYNC_RUN_TYPE_CMD: ctx->child_wait = child_wait_new_with_pid(ctx->remote_pid, cmd_dsync_remote_exited, ctx); /* fall through */ case DSYNC_RUN_TYPE_STREAM: cmd_dsync_run_remote(user); break; } if (ctx->state_input != NULL) { string_t *state_str = t_str_new(128); dsync_brain_get_state(brain, state_str); doveadm_print(str_c(state_str)); } changes_during_sync = dsync_brain_get_unexpected_changes_reason(brain, &remote_only_changes); if (changes_during_sync != NULL || changes_during_sync2 != NULL) { /* don't log a warning when running via doveadm server (e.g. called by replicator) */ if (ctx->ctx.conn == NULL) { i_warning("Mailbox changes caused a desync. " "You may want to run dsync again: %s", changes_during_sync == NULL || (remote_only_changes && changes_during_sync2 != NULL) ? changes_during_sync2 : changes_during_sync); } ctx->ctx.exit_code = 2; } if (dsync_brain_deinit(&brain, &mail_error2) < 0) ret = -1; if (ret < 0) { /* tempfail is the default error. prefer to use a non-tempfail if that exists. */ if (mail_error2 != 0 && (mail_error == 0 || mail_error == MAIL_ERROR_TEMP)) mail_error = mail_error2; doveadm_mail_failed_error(&ctx->ctx, mail_error); } dsync_ibc_deinit(&ibc); if (ibc2 != NULL) dsync_ibc_deinit(&ibc2); if (ctx->ssl_iostream != NULL) ssl_iostream_destroy(&ctx->ssl_iostream); if (ctx->ssl_ctx != NULL) ssl_iostream_context_deinit(&ctx->ssl_ctx); if (ctx->input != NULL) { i_stream_set_max_buffer_size(ctx->input, ctx->input_orig_bufsize); i_stream_unref(&ctx->input); } if (ctx->output != NULL) { o_stream_set_max_buffer_size(ctx->output, ctx->output_orig_bufsize); o_stream_unref(&ctx->output); } if (ctx->fd_in != -1) { if (ctx->fd_out != ctx->fd_in) i_close_fd(&ctx->fd_out); i_close_fd(&ctx->fd_in); } /* print any final errors after the process has died. not closing stdin/stdout before wait() may cause the process to hang, but stderr shouldn't (at least with ssh) and we need stderr to be open to be able to print the final errors */ if (ctx->run_type == DSYNC_RUN_TYPE_CMD) { cmd_dsync_wait_remote(ctx); remote_error_input(ctx); remote_errors_logged = ctx->err_stream->v_offset > 0; i_stream_destroy(&ctx->err_stream); cmd_dsync_log_remote_status(ctx->exit_status, remote_errors_logged, ctx->remote_cmd_args); } else { i_assert(ctx->err_stream == NULL); } if (ctx->io_err != NULL) io_remove(&ctx->io_err); if (ctx->fd_err != -1) i_close_fd(&ctx->fd_err); if (ctx->child_wait != NULL) child_wait_free(&ctx->child_wait); child_wait_deinit(); return ret; } static void dsync_connected_callback(int exit_code, const char *error, void *context) { struct dsync_cmd_context *ctx = context; ctx->ctx.exit_code = exit_code; switch (exit_code) { case 0: server_connection_extract(ctx->tcp_conn, &ctx->input, &ctx->output, &ctx->ssl_iostream); break; case SERVER_EXIT_CODE_DISCONNECTED: ctx->ctx.exit_code = EX_TEMPFAIL; ctx->error = p_strdup_printf(ctx->ctx.pool, "Disconnected from remote: %s", error); break; case EX_NOUSER: ctx->error = "Unknown user in remote"; break; default: ctx->error = p_strdup_printf(ctx->ctx.pool, "Failed to start remote dsync-server command: " "Remote exit_code=%u %s", exit_code, error == NULL ? "" : error); break; } io_loop_stop(current_ioloop); } static int dsync_init_ssl_ctx(struct dsync_cmd_context *ctx, const struct mail_storage_settings *mail_set, const char **error_r) { struct ssl_iostream_settings ssl_set; if (ctx->ssl_ctx != NULL) return 0; i_zero(&ssl_set); ssl_set.ca_dir = mail_set->ssl_client_ca_dir; ssl_set.ca_file = mail_set->ssl_client_ca_file; ssl_set.verify_remote_cert = TRUE; ssl_set.crypto_device = mail_set->ssl_crypto_device; return ssl_iostream_context_init_client(&ssl_set, &ctx->ssl_ctx, error_r); } static int dsync_connect_tcp(struct dsync_cmd_context *ctx, const struct mail_storage_settings *mail_set, const char *target, bool ssl, const char **error_r) { struct doveadm_server *server; struct server_connection *conn; struct ioloop *ioloop; string_t *cmd; const char *error; server = p_new(ctx->ctx.pool, struct doveadm_server, 1); server->name = p_strdup(ctx->ctx.pool, target); if (ssl) { if (dsync_init_ssl_ctx(ctx, mail_set, &error) < 0) { *error_r = t_strdup_printf( "Couldn't initialize SSL context: %s", error); return -1; } server->ssl_ctx = ctx->ssl_ctx; } p_array_init(&server->connections, ctx->ctx.pool, 1); p_array_init(&server->queue, ctx->ctx.pool, 1); ioloop = io_loop_create(); if (doveadm_verbose_proctitle) { process_title_set(t_strdup_printf( "[dsync - connecting to %s]", server->name)); } if (server_connection_create(server, &conn) < 0) { *error_r = "Couldn't create server connection"; return -1; } /* [] */ cmd = t_str_new(256); if (doveadm_debug) str_append_c(cmd, 'D'); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, ctx->ctx.cur_username); str_append(cmd, "\tdsync-server\t-u"); str_append_tabescaped(cmd, ctx->ctx.cur_username); if (ctx->replicator_notify) str_append(cmd, "\t-U"); str_append_c(cmd, '\n'); if (doveadm_verbose_proctitle) { process_title_set(t_strdup_printf( "[dsync - running dsync-server on %s]", server->name)); } ctx->tcp_conn = conn; server_connection_cmd(conn, str_c(cmd), NULL, dsync_connected_callback, ctx); io_loop_run(ioloop); ctx->tcp_conn = NULL; if (array_count(&server->connections) > 0) server_connection_destroy(&conn); io_loop_destroy(&ioloop); if (ctx->error != NULL) { *error_r = ctx->error; ctx->error = NULL; return -1; } ctx->run_type = DSYNC_RUN_TYPE_STREAM; return 0; } static int parse_location(struct dsync_cmd_context *ctx, const struct mail_storage_settings *mail_set, const char *location, const char *const **remote_cmd_args_r, const char **error_r) { if (strncmp(location, "tcp:", 4) == 0) { /* TCP connection to remote dsync */ ctx->remote_name = location+4; return dsync_connect_tcp(ctx, mail_set, ctx->remote_name, FALSE, error_r); } if (strncmp(location, "tcps:", 5) == 0) { /* TCP+SSL connection to remote dsync */ ctx->remote_name = location+5; return dsync_connect_tcp(ctx, mail_set, ctx->remote_name, TRUE, error_r); } if (strncmp(location, "remote:", 7) == 0) { /* this is a remote (ssh) command */ ctx->remote_name = location+7; } else if (strncmp(location, "remoteprefix:", 13) == 0) { /* this is a remote (ssh) command with a "user\n" prefix sent before dsync actually starts */ ctx->remote_name = location+13; ctx->remote_user_prefix = TRUE; } else { /* local with e.g. maildir:path */ ctx->remote_name = NULL; return 0; } *remote_cmd_args_r = parse_ssh_location(ctx->remote_name, ctx->ctx.cur_username); return 0; } static int cmd_dsync_prerun(struct doveadm_mail_cmd_context *_ctx, struct mail_storage_service_user *service_user, const char **error_r) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; const char *const *remote_cmd_args = NULL; const struct mail_user_settings *user_set; const struct mail_storage_settings *mail_set; const char *username = ""; user_set = mail_storage_service_user_get_set(service_user)[0]; mail_set = mail_storage_service_user_get_mail_set(service_user); ctx->fd_in = -1; ctx->fd_out = -1; ctx->fd_err = -1; ctx->run_type = DSYNC_RUN_TYPE_LOCAL; ctx->remote_name = "remote"; if (ctx->default_replica_location) { ctx->local_location = mail_user_set_plugin_getenv(user_set, "mail_replica"); if (ctx->local_location == NULL || *ctx->local_location == '\0') { *error_r = "User has no mail_replica in userdb"; _ctx->exit_code = DOVEADM_EX_NOTFOUND; return -1; } } else { /* if we're executing remotely, give -u parameter if we also did a userdb lookup. */ if ((_ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) username = _ctx->cur_username; if (!mirror_get_remote_cmd(ctx, username, &remote_cmd_args)) { /* it's a mail_location */ if (_ctx->args[1] != NULL) doveadm_mail_help_name(_ctx->cmd->name); ctx->local_location = _ctx->args[0]; ctx->local_location_from_arg = TRUE; } } if (remote_cmd_args == NULL && ctx->local_location != NULL) { if (parse_location(ctx, mail_set, ctx->local_location, &remote_cmd_args, error_r) < 0) return -1; } if (remote_cmd_args != NULL) { /* do this before mail_storage_service_next() in case it drops process privileges */ run_cmd(ctx, remote_cmd_args); ctx->run_type = DSYNC_RUN_TYPE_CMD; } if (ctx->sync_visible_namespaces && ctx->run_type == DSYNC_RUN_TYPE_LOCAL) i_fatal("-N parameter requires syncing with remote host"); return 0; } static void cmd_dsync_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; if (ctx->default_replica_location) { if (args[0] != NULL) i_error("Don't give mail location with -d parameter"); } else { if (args[0] == NULL) doveadm_mail_help_name(_ctx->cmd->name); } if (array_count(&ctx->exclude_mailboxes) > 0) array_append_zero(&ctx->exclude_mailboxes); lib_signals_ignore(SIGHUP, TRUE); } static void cmd_dsync_preinit(struct doveadm_mail_cmd_context *ctx) { if ((ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR; } static bool cmd_mailbox_dsync_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; const char *str, *error; bool utc; switch (c) { case '1': ctx->oneway = TRUE; ctx->backup = TRUE; break; case 'a': ctx->virtual_all_box = optarg; break; case 'd': ctx->default_replica_location = TRUE; break; case 'D': ctx->no_mailbox_renames = TRUE; break; case 'E': /* dsync wrapper detection flag */ legacy_dsync = TRUE; break; case 'f': ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL; break; case 'O': { const char *str = optarg; if (strchr(str, ' ') != NULL) i_fatal("-O parameter doesn't support multiple flags currently"); if (str[0] == '-') str++; if (str[0] == '\\' && imap_parse_system_flag(str) == 0) i_fatal("Invalid system flag given for -O parameter: '%s'", str); ctx->sync_flags = optarg; break; } case 'g': if (optarg[0] == '\0') ctx->no_mail_sync = TRUE; else if (guid_128_from_string(optarg, ctx->mailbox_guid) < 0 || guid_128_is_empty(ctx->mailbox_guid)) i_fatal("Invalid -g parameter: %s", optarg); break; case 'l': ctx->lock = TRUE; if (str_to_uint(optarg, &ctx->lock_timeout) < 0) i_fatal("Invalid -l parameter: %s", optarg); break; case 'm': if (optarg[0] == '\0') ctx->no_mail_sync = TRUE; else ctx->mailbox = optarg; break; case 'x': str = optarg; array_append(&ctx->exclude_mailboxes, &str, 1); break; case 'n': str = optarg; array_append(&ctx->namespace_prefixes, &str, 1); break; case 'N': ctx->sync_visible_namespaces = TRUE; break; case 'P': ctx->purge_remote = TRUE; break; case 'r': ctx->rawlog_path = optarg; break; case 'R': ctx->reverse_backup = TRUE; break; case 's': if (ctx->sync_type != DSYNC_BRAIN_SYNC_TYPE_FULL && *optarg != '\0') ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_STATE; ctx->state_input = optarg; break; case 't': if (mail_parse_human_timestamp(optarg, &ctx->sync_since_timestamp, &utc) < 0) i_fatal("Invalid -t parameter: %s", optarg); break; case 'e': if (mail_parse_human_timestamp(optarg, &ctx->sync_until_timestamp, &utc) < 0) i_fatal("Invalid -e parameter: %s", optarg); break; case 'I': if (settings_get_size(optarg, &ctx->sync_max_size, &error) < 0) i_fatal("Invalid -I parameter '%s': %s", optarg, error); break; case 'T': if (str_to_uint(optarg, &ctx->io_timeout_secs) < 0) i_fatal("Invalid -T parameter: %s", optarg); break; case 'U': ctx->replicator_notify = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_dsync_alloc(void) { struct dsync_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct dsync_cmd_context); ctx->io_timeout_secs = DSYNC_DEFAULT_IO_STREAM_TIMEOUT_SECS; ctx->ctx.getopt_args = DSYNC_COMMON_GETOPT_ARGS; ctx->ctx.v.parse_arg = cmd_mailbox_dsync_parse_arg; ctx->ctx.v.preinit = cmd_dsync_preinit; ctx->ctx.v.init = cmd_dsync_init; ctx->ctx.v.prerun = cmd_dsync_prerun; ctx->ctx.v.run = cmd_dsync_run; ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED; doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); doveadm_print_header("state", "state", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); p_array_init(&ctx->exclude_mailboxes, ctx->ctx.pool, 4); p_array_init(&ctx->namespace_prefixes, ctx->ctx.pool, 4); if ((doveadm_settings->parsed_features & DSYNC_FEATURE_EMPTY_HDR_WORKAROUND) != 0) ctx->empty_hdr_workaround = TRUE; ctx->import_commit_msgs_interval = doveadm_settings->dsync_commit_msgs_interval; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_dsync_backup_alloc(void) { struct doveadm_mail_cmd_context *_ctx; struct dsync_cmd_context *ctx; _ctx = cmd_dsync_alloc(); ctx = (struct dsync_cmd_context *)_ctx; ctx->backup = TRUE; return _ctx; } static int cmd_dsync_server_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; struct dsync_ibc *ibc; struct dsync_brain *brain; string_t *temp_prefix, *state_str = NULL; enum dsync_brain_sync_type sync_type; const char *name, *process_title_prefix = ""; enum mail_error mail_error; if (_ctx->conn != NULL) { /* doveadm-server connection. start with a success reply. after that follows the regular dsync protocol. */ ctx->fd_in = ctx->fd_out = -1; ctx->input = _ctx->conn->input; ctx->output = _ctx->conn->output; o_stream_nsend(ctx->output, "\n+\n", 3); i_set_failure_prefix("dsync-server(%s): ", user->username); name = i_stream_get_name(ctx->input); if (_ctx->cur_client_ip.family != 0) { /* include the doveadm client's IP address in the ps output */ process_title_prefix = t_strdup_printf( "%s ", net_ip2addr(&_ctx->cur_client_ip)); } } else { /* the log messages go via stderr to the remote dsync, so the names are reversed */ i_set_failure_prefix("dsync-remote(%s): ", user->username); name = "local"; } doveadm_user_init_dsync(user); temp_prefix = t_str_new(64); mail_user_set_get_temp_prefix(temp_prefix, user->set); ibc = cmd_dsync_ibc_stream_init(ctx, name, str_c(temp_prefix)); brain = dsync_brain_slave_init(user, ibc, FALSE, process_title_prefix); io_loop_run(current_ioloop); if (ctx->replicator_notify) { state_str = t_str_new(128); dsync_brain_get_state(brain, state_str); } sync_type = dsync_brain_get_sync_type(brain); if (dsync_brain_deinit(&brain, &mail_error) < 0) doveadm_mail_failed_error(_ctx, mail_error); dsync_ibc_deinit(&ibc); if (_ctx->conn != NULL) { /* make sure nothing more is written by the generic doveadm connection code */ o_stream_close(_ctx->conn->output); } if (ctx->replicator_notify && _ctx->exit_code == 0) dsync_replicator_notify(ctx, sync_type, str_c(state_str)); return _ctx->exit_code == 0 ? 0 : -1; } static bool cmd_mailbox_dsync_server_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx; switch (c) { case 'E': /* dsync wrapper detection flag */ legacy_dsync = TRUE; break; case 'r': ctx->rawlog_path = optarg; break; case 'T': if (str_to_uint(optarg, &ctx->io_timeout_secs) < 0) i_fatal("Invalid -T parameter: %s", optarg); break; case 'U': ctx->replicator_notify = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_dsync_server_alloc(void) { struct dsync_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct dsync_cmd_context); ctx->io_timeout_secs = DSYNC_DEFAULT_IO_STREAM_TIMEOUT_SECS; ctx->ctx.getopt_args = "Er:T:U"; ctx->ctx.v.parse_arg = cmd_mailbox_dsync_server_parse_arg; ctx->ctx.v.run = cmd_dsync_server_run; ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED; ctx->fd_in = STDIN_FILENO; ctx->fd_out = STDOUT_FILENO; return &ctx->ctx; } struct doveadm_mail_cmd cmd_dsync_mirror = { cmd_dsync_alloc, "sync", "[-1fPRU] [-l ] [-r ] [-m ] [-g ] [-n | -N] [-x ] [-s ] [-t ] -d|" }; struct doveadm_mail_cmd cmd_dsync_backup = { cmd_dsync_backup_alloc, "backup", "[-fPRU] [-l ] [-r ] [-m ] [-g ] [-n | -N] [-x ] [-s ] [-t ] -d|" }; struct doveadm_mail_cmd cmd_dsync_server = { cmd_dsync_server_alloc, "dsync-server", &doveadm_mail_cmd_hide }; void doveadm_dsync_main(int *_argc, char **_argv[]) { int argc = *_argc; const char *getopt_str; char **argv = *_argv; char **new_argv, *mailbox = NULL, *alt_char = NULL, *username = NULL; char *p, *dup, new_flags[6]; int max_argc, src, dest, i, j; bool flag_f = FALSE, flag_R = FALSE, flag_m, flag_u, flag_C, has_arg; bool dsync_server = FALSE; p = strrchr(argv[0], '/'); if (p == NULL) p = argv[0]; if (strstr(p, "dsync") == NULL) return; /* @UNSAFE: this is called when the "doveadm" binary is called as "dsync" (for backwards compatibility) */ max_argc = argc + 7; new_argv = t_new(char *, max_argc); new_argv[0] = argv[0]; dest = 1; getopt_str = master_service_getopt_string(); /* add global doveadm flags */ for (src = 1; src < argc; src++) { if (argv[src][0] != '-') break; flag_m = FALSE; flag_C = FALSE; has_arg = FALSE; flag_u = FALSE; dup = t_strdup_noconst(argv[src]); for (i = j = 1; argv[src][i] != '\0'; i++) { switch (argv[src][i]) { case 'C': flag_C = TRUE; break; case 'f': flag_f = TRUE; break; case 'R': flag_R = TRUE; break; case 'm': flag_m = TRUE; break; case 'u': flag_u = TRUE; break; default: p = strchr(getopt_str, argv[src][i]); if (p != NULL && p[1] == ':') has_arg = TRUE; dup[j++] = argv[src][i]; break; } } if (j > 1) { dup[j++] = '\0'; new_argv[dest++] = dup; if (has_arg && src+1 < argc) new_argv[dest++] = argv[++src]; } if (flag_m) { if (src+1 == argc) i_fatal("-m missing parameter"); mailbox = argv[++src]; } if (flag_u) { if (src+1 == argc) i_fatal("-u missing parameter"); username = argv[++src]; } if (flag_C) { if (src+1 == argc) i_fatal("-C missing parameter"); alt_char = argv[++src]; } } if (alt_char != NULL) { new_argv[dest++] = "-o"; new_argv[dest++] = p_strconcat(pool_datastack_create(), "dsync_alt_char=", alt_char, NULL); } /* mirror|backup|server */ if (src == argc) i_fatal("Missing mirror or backup parameter"); if (strcmp(argv[src], "sync") == 0 || strcmp(argv[src], "dsync-server") == 0) { /* we're re-executing dsync due to doveconf. "backup" re-exec detection is later. */ return; } if (strcmp(argv[src], "mirror") == 0) new_argv[dest] = "sync"; else if (strcmp(argv[src], "backup") == 0) new_argv[dest] = "backup"; else if (strcmp(argv[src], "server") == 0) { new_argv[dest] = "dsync-server"; dsync_server = TRUE; } else i_fatal("Invalid parameter: %s", argv[src]); src++; dest++; if (src < argc && strncmp(argv[src], "-E", 2) == 0) { /* we're re-executing dsync due to doveconf */ return; } /* dsync flags */ new_flags[0] = '-'; new_flags[1] = 'E'; i = 2; if (!dsync_server) { if (flag_f) new_flags[i++] = 'f'; if (flag_R) new_flags[i++] = 'R'; if (mailbox != NULL) new_flags[i++] = 'm'; } i_assert((unsigned int)i < sizeof(new_flags)); new_flags[i] = '\0'; if (i > 1) { new_argv[dest++] = strdup(new_flags); if (mailbox != NULL) new_argv[dest++] = mailbox; } if (username != NULL) { new_argv[dest++] = "-u"; new_argv[dest++] = username; } /* rest of the parameters */ for (; src < argc; src++) new_argv[dest++] = argv[src]; i_assert(dest < max_argc); new_argv[dest] = NULL; legacy_dsync = TRUE; *_argc = dest; *_argv = new_argv; i_getopt_reset(); } dovecot-2.2.33.2/src/doveadm/doveadm-mail-server.c0000644000175000017500000002412413165463543016557 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "ioloop.h" #include "master-service.h" #include "auth-master.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "server-connection.h" #include "doveadm-settings.h" #include "doveadm-print.h" #include "doveadm-server.h" #include "doveadm-mail.h" #define DOVEADM_SERVER_CONNECTIONS_MAX 4 #define DOVEADM_SERVER_QUEUE_MAX 16 #define DOVEADM_MAIL_SERVER_FAILED() \ (internal_failure || master_service_is_killed(master_service)) struct doveadm_mail_server_cmd { struct server_connection *conn; char *username; }; static HASH_TABLE(char *, struct doveadm_server *) servers; static pool_t server_pool; static struct doveadm_mail_cmd_context *cmd_ctx; static bool internal_failure = FALSE; static void doveadm_mail_server_handle(struct server_connection *conn, const char *username); static struct doveadm_server * doveadm_server_get(struct doveadm_mail_cmd_context *ctx, const char *name) { struct doveadm_server *server; char *dup_name; if (!hash_table_is_created(servers)) { server_pool = pool_alloconly_create("doveadm servers", 1024*16); hash_table_create(&servers, server_pool, 0, str_hash, strcmp); } server = hash_table_lookup(servers, name); if (server == NULL) { server = p_new(server_pool, struct doveadm_server, 1); server->name = dup_name = p_strdup(server_pool, name); p_array_init(&server->connections, server_pool, ctx->set->doveadm_worker_count); p_array_init(&server->queue, server_pool, DOVEADM_SERVER_QUEUE_MAX); hash_table_insert(servers, dup_name, server); } return server; } static struct server_connection * doveadm_server_find_unused_conn(struct doveadm_server *server) { struct server_connection *const *connp; array_foreach(&server->connections, connp) { if (server_connection_is_idle(*connp)) return *connp; } return NULL; } static bool doveadm_server_have_used_connections(struct doveadm_server *server) { struct server_connection *const *connp; array_foreach(&server->connections, connp) { if (!server_connection_is_idle(*connp)) return TRUE; } return FALSE; } static void doveadm_cmd_callback(int exit_code, const char *error, void *context) { struct doveadm_mail_server_cmd *servercmd = context; struct doveadm_server *server = server_connection_get_server(servercmd->conn); const char *username = t_strdup(servercmd->username); i_free(servercmd->username); i_free(servercmd); switch (exit_code) { case 0: break; case SERVER_EXIT_CODE_DISCONNECTED: i_error("%s: Command %s failed for %s: %s", server->name, cmd_ctx->cmd->name, username, error); internal_failure = TRUE; io_loop_stop(current_ioloop); return; case EX_NOUSER: i_error("%s: No such user: %s", server->name, username); if (cmd_ctx->exit_code == 0) cmd_ctx->exit_code = EX_NOUSER; break; default: if (cmd_ctx->exit_code == 0 || exit_code == EX_TEMPFAIL) cmd_ctx->exit_code = exit_code; break; } if (array_count(&server->queue) > 0) { struct server_connection *conn; char *const *usernamep = array_idx(&server->queue, 0); char *username = *usernamep; conn = doveadm_server_find_unused_conn(server); if (conn != NULL) { array_delete(&server->queue, 0, 1); doveadm_mail_server_handle(conn, username); i_free(username); } } io_loop_stop(current_ioloop); } static void doveadm_mail_server_handle(struct server_connection *conn, const char *username) { struct doveadm_mail_server_cmd *servercmd; string_t *cmd; unsigned int i; /* [] */ cmd = t_str_new(256); if (doveadm_debug) str_append_c(cmd, 'D'); else if (doveadm_verbose) str_append_c(cmd, 'v'); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, username); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, cmd_ctx->cmd->name); for (i = 0; cmd_ctx->full_args[i] != NULL; i++) { str_append_c(cmd, '\t'); str_append_tabescaped(cmd, cmd_ctx->full_args[i]); } str_append_c(cmd, '\n'); servercmd = i_new(struct doveadm_mail_server_cmd, 1); servercmd->conn = conn; servercmd->username = i_strdup(username); server_connection_cmd(conn, str_c(cmd), cmd_ctx->cmd_input, doveadm_cmd_callback, servercmd); } static void doveadm_server_flush_one(struct doveadm_server *server) { unsigned int count = array_count(&server->queue); do { io_loop_run(current_ioloop); } while (array_count(&server->queue) == count && doveadm_server_have_used_connections(server) && !DOVEADM_MAIL_SERVER_FAILED()); } static int doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, const struct mail_storage_service_input *input, const char **user_r, const char **host_r, const char **error_r) { struct auth_master_connection *auth_conn; struct auth_user_info info; pool_t pool; const char *auth_socket_path, *proxy_host, *proxy_hostip, *const *fields; unsigned int i; in_port_t proxy_port; bool proxying; int ret; *user_r = input->username; *host_r = ctx->set->doveadm_socket_path; if (ctx->set->doveadm_port == 0) return 0; /* make sure we have an auth connection */ mail_storage_service_init_settings(ctx->storage_service, input); i_zero(&info); info.service = master_service_get_name(master_service); info.local_ip = input->local_ip; info.remote_ip = input->remote_ip; info.local_port = input->local_port; info.remote_port = input->remote_port; pool = pool_alloconly_create("auth lookup", 1024); auth_conn = mail_storage_service_get_auth_conn(ctx->storage_service); auth_socket_path = auth_master_get_socket_path(auth_conn); ret = auth_master_pass_lookup(auth_conn, input->username, &info, pool, &fields); if (ret < 0) { *error_r = fields[0] != NULL ? t_strdup(fields[0]) : "passdb lookup failed"; *error_r = t_strdup_printf("%s: %s (to see if user is proxied, " "because doveadm_port is set)", auth_socket_path, *error_r); } else if (ret == 0) { /* user not found from passdb. it could be in userdb though, so just continue with the default host */ } else { proxy_host = NULL; proxy_hostip = NULL; proxying = FALSE; proxy_port = ctx->set->doveadm_port; for (i = 0; fields[i] != NULL; i++) { if (strncmp(fields[i], "proxy", 5) == 0 && (fields[i][5] == '\0' || fields[i][5] == '=')) proxying = TRUE; else if (strncmp(fields[i], "host=", 5) == 0) proxy_host = fields[i]+5; else if (strncmp(fields[i], "hostip=", 7) == 0) proxy_hostip = fields[i]+7; else if (strncmp(fields[i], "user=", 5) == 0) *user_r = t_strdup(fields[i]+5); else if (strncmp(fields[i], "destuser=", 9) == 0) *user_r = t_strdup(fields[i]+9); else if (strncmp(fields[i], "port=", 5) == 0) { if (net_str2port(fields[i]+5, &proxy_port) < 0) proxy_port = 0; } } if (proxy_hostip != NULL) proxy_host = proxy_hostip; if (!proxying) ret = 0; else if (proxy_host == NULL) { *error_r = t_strdup_printf("%s: Proxy is missing destination host", auth_socket_path); if (strstr(auth_socket_path, "/auth-userdb") != NULL) { *error_r = t_strdup_printf( "%s (maybe set auth_socket_path=director-userdb)", *error_r); } ret = -1; } else { *host_r = t_strdup_printf("%s:%u", proxy_host, proxy_port); } } pool_unref(&pool); return ret; } int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, const struct mail_storage_service_input *input, const char **error_r) { struct doveadm_server *server; struct server_connection *conn; const char *user, *host; char *username_dup; int ret; i_assert(cmd_ctx == ctx || cmd_ctx == NULL); cmd_ctx = ctx; ret = doveadm_mail_server_user_get_host(ctx, input, &user, &host, error_r); if (ret < 0) return ret; if (ret == 0 && (ctx->set->doveadm_worker_count == 0 || doveadm_server)) { /* run it ourself */ return 0; } /* server sends the sticky headers for each row as well, so undo any sticks we might have added already */ doveadm_print_unstick_headers(); server = doveadm_server_get(ctx, host); conn = doveadm_server_find_unused_conn(server); if (conn != NULL) doveadm_mail_server_handle(conn, user); else if (array_count(&server->connections) < I_MAX(ctx->set->doveadm_worker_count, 1)) { if (server_connection_create(server, &conn) < 0) internal_failure = TRUE; else doveadm_mail_server_handle(conn, user); } else { if (array_count(&server->queue) >= DOVEADM_SERVER_QUEUE_MAX) doveadm_server_flush_one(server); username_dup = i_strdup(user); array_append(&server->queue, &username_dup, 1); } *error_r = "doveadm server failure"; return DOVEADM_MAIL_SERVER_FAILED() ? -1 : 1; } static struct doveadm_server *doveadm_server_find_used(void) { struct hash_iterate_context *iter; struct doveadm_server *ret = NULL; char *key; struct doveadm_server *server; iter = hash_table_iterate_init(servers); while (hash_table_iterate(iter, servers, &key, &server)) { if (doveadm_server_have_used_connections(server)) { ret = server; break; } } hash_table_iterate_deinit(&iter); return ret; } static void doveadm_servers_destroy_all_connections(void) { struct hash_iterate_context *iter; char *key; struct doveadm_server *server; iter = hash_table_iterate_init(servers); while (hash_table_iterate(iter, servers, &key, &server)) { while (array_count(&server->connections) > 0) { struct server_connection *const *connp, *conn; connp = array_idx(&server->connections, 0); conn = *connp; server_connection_destroy(&conn); } } hash_table_iterate_deinit(&iter); } void doveadm_mail_server_flush(void) { struct doveadm_server *server; if (!hash_table_is_created(servers)) { cmd_ctx = NULL; return; } while ((server = doveadm_server_find_used()) != NULL && !DOVEADM_MAIL_SERVER_FAILED()) doveadm_server_flush_one(server); doveadm_servers_destroy_all_connections(); if (master_service_is_killed(master_service)) i_error("Aborted"); if (DOVEADM_MAIL_SERVER_FAILED()) doveadm_mail_failed_error(cmd_ctx, MAIL_ERROR_TEMP); hash_table_destroy(&servers); pool_unref(&server_pool); cmd_ctx = NULL; } dovecot-2.2.33.2/src/doveadm/doveadm-mail-import.c0000644000175000017500000001742613165463624016572 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-mail-iter.h" #include "doveadm-mail.h" struct import_cmd_context { struct doveadm_mail_cmd_context ctx; const char *src_location; const char *src_username; struct mail_user *src_user; const char *dest_parent; bool subscribe; }; static const char * convert_vname_separators(const char *vname, char src_sep, char dest_sep) { string_t *str = t_str_new(128); for (; *vname != '\0'; vname++) { if (*vname == src_sep) str_append_c(str, dest_sep); else if (*vname == dest_sep) str_append_c(str, '_'); else str_append_c(str, *vname); } return str_c(str); } static int dest_mailbox_open_or_create(struct import_cmd_context *ctx, struct mail_user *user, const struct mailbox_info *info, struct mailbox **box_r) { struct mail_namespace *ns; struct mailbox *box; enum mail_error error; const char *name, *errstr; if (*ctx->dest_parent != '\0') { /* prefix destination mailbox name with given parent mailbox */ ns = mail_namespace_find(user->namespaces, ctx->dest_parent); } else { ns = mail_namespace_find(user->namespaces, info->vname); } name = convert_vname_separators(info->vname, mail_namespace_get_sep(info->ns), mail_namespace_get_sep(ns)); if (*ctx->dest_parent != '\0') { name = t_strdup_printf("%s%c%s", ctx->dest_parent, mail_namespace_get_sep(ns), name); } box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_SAVEONLY); mailbox_set_reason(box, ctx->ctx.cmd->name); if (mailbox_create(box, NULL, FALSE) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_EXISTS) { i_error("Couldn't create mailbox %s: %s", name, errstr); doveadm_mail_failed_mailbox(&ctx->ctx, box); mailbox_free(&box); return -1; } } if (ctx->subscribe) { if (mailbox_set_subscribed(box, TRUE) < 0) { i_error("Couldn't subscribe to mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); } } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Syncing mailbox %s failed: %s", name, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, box); mailbox_free(&box); return -1; } *box_r = box; return 0; } static int cmd_import_box_contents(struct doveadm_mail_iter *iter, struct mail *src_mail, struct mailbox *dest_box) { struct mail_save_context *save_ctx; struct mailbox_transaction_context *dest_trans; const char *mailbox = mailbox_get_vname(dest_box); int ret = 0; dest_trans = mailbox_transaction_begin(dest_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); do { if (doveadm_debug) { i_debug("import: box=%s uid=%u", mailbox, src_mail->uid); } save_ctx = mailbox_save_alloc(dest_trans); mailbox_save_copy_flags(save_ctx, src_mail); if (mailbox_copy(&save_ctx, src_mail) < 0) { i_error("Copying box=%s uid=%u failed: %s", mailbox, src_mail->uid, mailbox_get_last_internal_error(dest_box, NULL)); ret = -1; } } while (doveadm_mail_iter_next(iter, &src_mail)); if (mailbox_transaction_commit(&dest_trans) < 0) { i_error("Committing copied mails to %s failed: %s", mailbox, mailbox_get_last_internal_error(dest_box, NULL)); ret = -1; } return ret; } static int cmd_import_box(struct import_cmd_context *ctx, struct mail_user *dest_user, const struct mailbox_info *info, struct mail_search_args *search_args) { struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; int ret = 0; if (doveadm_mail_iter_init(&ctx->ctx, info, search_args, 0, NULL, TRUE, &iter) < 0) return -1; if (doveadm_mail_iter_next(iter, &mail)) { /* at least one mail matches in this mailbox */ if (dest_mailbox_open_or_create(ctx, dest_user, info, &box) < 0) ret = -1; else { if (cmd_import_box_contents(iter, mail, box) < 0) { doveadm_mail_failed_mailbox(&ctx->ctx, mail->box); ret = -1; } mailbox_free(&box); } } if (doveadm_mail_iter_deinit_sync(&iter) < 0) ret = -1; return ret; } static void cmd_import_init_source_user(struct import_cmd_context *ctx, struct mail_user *dest_user) { struct mail_storage_service_input input; struct mail_storage_service_user *service_user; struct mail_user *user; const char *error; /* create a user for accessing the source storage */ i_zero(&input); input.module = "mail"; input.username = ctx->src_username != NULL ? ctx->src_username : dest_user->username; input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES | MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS; if (mail_storage_service_lookup_next(ctx->ctx.storage_service, &input, &service_user, &user, &error) < 0) i_fatal("Import user initialization failed: %s", error); if (mail_namespaces_init_location(user, ctx->src_location, &error) < 0) i_fatal("Import namespace initialization failed: %s", error); ctx->src_user = user; mail_storage_service_user_unref(&service_user); } static int cmd_import_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; if (ctx->src_user == NULL) cmd_import_init_source_user(ctx, user); iter = doveadm_mailbox_list_iter_init(_ctx, ctx->src_user, _ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_import_box(ctx, user, info, _ctx->search_args) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_import_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx; if (str_array_length(args) < 3) doveadm_mail_help_name("import"); ctx->src_location = p_strdup(_ctx->pool, args[0]); ctx->dest_parent = p_strdup(_ctx->pool, args[1]); ctx->ctx.search_args = doveadm_mail_build_search_args(args+2); } static void cmd_import_deinit(struct doveadm_mail_cmd_context *_ctx) { struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx; if (ctx->src_user != NULL) mail_user_unref(&ctx->src_user); } static bool cmd_import_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx; switch (c) { case 'U': ctx->src_username = p_strdup(_ctx->pool, optarg); break; case 's': ctx->subscribe = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_import_alloc(void) { struct import_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct import_cmd_context); ctx->ctx.getopt_args = "s"; ctx->ctx.v.parse_arg = cmd_import_parse_arg; ctx->ctx.v.init = cmd_import_init; ctx->ctx.v.deinit = cmd_import_deinit; ctx->ctx.v.run = cmd_import_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_import_ver2 = { .name = "import", .mail_cmd = cmd_import_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-U source-user] [-s] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('U', "source-user", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('s', "subscribe", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "source-location", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "dest-parent-mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/doveadm/doveadm-mail-index.c0000644000175000017500000001766613165463624016375 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "net.h" #include "write-full.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-search-build.h" #include "mailbox-list-iter.h" #include "doveadm-settings.h" #include "doveadm-mail.h" #include #define INDEXER_SOCKET_NAME "indexer" #define INDEXER_HANDSHAKE "VERSION\tindexer\t1\t0\n" struct index_cmd_context { struct doveadm_mail_cmd_context ctx; int queue_fd; unsigned int max_recent_msgs; unsigned int queue:1; unsigned int have_wildcards:1; }; static int cmd_index_box_precache(struct mailbox *box) { struct mailbox_status status; struct mailbox_transaction_context *trans; struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; struct mailbox_metadata metadata; uint32_t seq; unsigned int counter = 0, max; int ret = 0; if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS, &metadata) < 0) { i_error("Mailbox %s: Precache-fields lookup failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); } if (mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ, &status) < 0) { i_error("Mailbox %s: Status lookup failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); return -1; } seq = status.last_cached_seq + 1; if (seq > status.messages) { if (doveadm_verbose) { i_info("%s: Cache is already up to date", mailbox_get_vname(box)); } return 0; } if (doveadm_verbose) { i_info("%s: Caching mails seq=%u..%u", mailbox_get_vname(box), seq, status.messages); } trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC); search_args = mail_search_build_init(); mail_search_build_add_seqset(search_args, seq, status.messages); ctx = mailbox_search_init(trans, search_args, NULL, metadata.precache_fields, NULL); mail_search_args_unref(&search_args); max = status.messages - seq + 1; while (mailbox_search_next(ctx, &mail)) { mail_precache(mail); if (doveadm_verbose && ++counter % 100 == 0) { printf("\r%u/%u", counter, max); fflush(stdout); } } if (doveadm_verbose) printf("\r%u/%u\n", counter, max); if (mailbox_search_deinit(&ctx) < 0) { i_error("Mailbox %s: Mail search failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; } if (mailbox_transaction_commit(&trans) < 0) { i_error("Mailbox %s: Transaction commit failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; } return ret; } static int cmd_index_box(struct index_cmd_context *ctx, const struct mailbox_info *info) { struct mailbox *box; struct mailbox_status status; int ret = 0; box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, ctx->ctx.cmd->name); if (ctx->max_recent_msgs != 0) { /* index only if there aren't too many recent messages. don't bother syncing the mailbox, that alone can take a while with large maildirs. */ if (mailbox_open(box) < 0) { i_error("Opening mailbox %s failed: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, box); mailbox_free(&box); return -1; } mailbox_get_open_status(box, STATUS_RECENT, &status); if (status.recent > ctx->max_recent_msgs) { mailbox_free(&box); return 0; } } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Syncing mailbox %s failed: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, box); ret = -1; } else { if (cmd_index_box_precache(box) < 0) { doveadm_mail_failed_mailbox(&ctx->ctx, box); ret = -1; } } mailbox_free(&box); return ret; } static void index_queue_connect(struct index_cmd_context *ctx) { const char *path; path = t_strconcat(doveadm_settings->base_dir, "/"INDEXER_SOCKET_NAME, NULL); ctx->queue_fd = net_connect_unix(path); if (ctx->queue_fd == -1) i_fatal("net_connect_unix(%s) failed: %m", path); if (write_full(ctx->queue_fd, INDEXER_HANDSHAKE, strlen(INDEXER_HANDSHAKE)) < 0) i_fatal("write(indexer) failed: %m"); } static void cmd_index_queue(struct index_cmd_context *ctx, struct mail_user *user, const char *mailbox) { if (ctx->queue_fd == -1) index_queue_connect(ctx); i_assert(ctx->queue_fd != -1); T_BEGIN { string_t *str = t_str_new(256); str_append(str, "APPEND\t0\t"); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); str_append_tabescaped(str, mailbox); str_printfa(str, "\t%u\n", ctx->max_recent_msgs); if (write_full(ctx->queue_fd, str_data(str), str_len(str)) < 0) i_fatal("write(indexer) failed: %m"); } T_END; } static int cmd_index_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct index_cmd_context *ctx = (struct index_cmd_context *)_ctx; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_STAR_WITHIN_NS; const enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_MASK_ALL; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; unsigned int i; int ret = 0; if (ctx->queue && !ctx->have_wildcards) { /* we can do this quickly without going through the mailboxes */ for (i = 0; _ctx->args[i] != NULL; i++) cmd_index_queue(ctx, user, _ctx->args[i]); return 0; } iter = mailbox_list_iter_init_namespaces(user->namespaces, _ctx->args, ns_mask, iter_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) T_BEGIN { if (ctx->queue) cmd_index_queue(ctx, user, info->vname); else { if (cmd_index_box(ctx, info) < 0) ret = -1; } } T_END; } if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Listing mailboxes failed: %s", mailbox_list_get_last_internal_error(user->namespaces->list, NULL)); doveadm_mail_failed_error(_ctx, MAIL_ERROR_TEMP); ret = -1; } return ret; } static void cmd_index_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct index_cmd_context *ctx = (struct index_cmd_context *)_ctx; unsigned int i; if (args[0] == NULL) doveadm_mail_help_name("index"); for (i = 0; args[i] != NULL; i++) { if (strchr(args[i], '*') != NULL || strchr(args[i], '%') != NULL) { ctx->have_wildcards = TRUE; break; } } } static void cmd_index_deinit(struct doveadm_mail_cmd_context *_ctx) { struct index_cmd_context *ctx = (struct index_cmd_context *)_ctx; if (ctx->queue_fd != -1) { net_disconnect(ctx->queue_fd); ctx->queue_fd = -1; } } static bool cmd_index_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct index_cmd_context *ctx = (struct index_cmd_context *)_ctx; switch (c) { case 'q': ctx->queue = TRUE; break; case 'n': if (str_to_uint(optarg, &ctx->max_recent_msgs) < 0) { i_fatal_status(EX_USAGE, "Invalid -n parameter number: %s", optarg); } break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_index_alloc(void) { struct index_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct index_cmd_context); ctx->queue_fd = -1; ctx->ctx.getopt_args = "qn:"; ctx->ctx.v.parse_arg = cmd_index_parse_arg; ctx->ctx.v.init = cmd_index_init; ctx->ctx.v.deinit = cmd_index_deinit; ctx->ctx.v.run = cmd_index_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_index_ver2 = { .name = "index", .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-q] [-n ] ", .mail_cmd = cmd_index_alloc, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('q',"queue",CMD_PARAM_BOOL,0) DOVEADM_CMD_PARAM('n',"max-recent",CMD_PARAM_STR,0) DOVEADM_CMD_PARAM('\0',"mailbox-mask",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; dovecot-2.2.33.2/src/lib-smtp/0002755000175000017500000000000013172375610012732 500000000000000dovecot-2.2.33.2/src/lib-smtp/Makefile.in0000644000175000017500000005360013172375573014731 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-smtp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libsmtp_la_LIBADD = am_libsmtp_la_OBJECTS = lmtp-client.lo libsmtp_la_OBJECTS = $(am_libsmtp_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsmtp_la_SOURCES) DIST_SOURCES = $(libsmtp_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libsmtp.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dns libsmtp_la_SOURCES = \ lmtp-client.c headers = \ lmtp-client.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-smtp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-smtp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsmtp.la: $(libsmtp_la_OBJECTS) $(libsmtp_la_DEPENDENCIES) $(EXTRA_libsmtp_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libsmtp_la_OBJECTS) $(libsmtp_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-client.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-smtp/lmtp-client.c0000644000175000017500000005723413165463624015264 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "dns-lookup.h" #include "lmtp-client.h" #include #define LMTP_MAX_LINE_LEN 1024 #define LMTP_CLIENT_DNS_LOOKUP_TIMEOUT_MSECS (60*1000) enum lmtp_input_state { LMTP_INPUT_STATE_GREET, LMTP_INPUT_STATE_LHLO, LMTP_INPUT_STATE_MAIL_FROM, LMTP_INPUT_STATE_RCPT_TO, LMTP_INPUT_STATE_DATA_CONTINUE, LMTP_INPUT_STATE_DATA, LMTP_INPUT_STATE_XCLIENT }; struct lmtp_rcpt { const char *address; lmtp_callback_t *rcpt_to_callback; lmtp_callback_t *data_callback; void *context; struct lmtp_recipient_params params; unsigned int data_called:1; unsigned int failed:1; }; struct lmtp_client { pool_t pool; int refcount; struct lmtp_client_settings set; const char *host; struct ip_addr ip; in_port_t port; enum lmtp_client_protocol protocol; enum lmtp_input_state input_state; const char *global_fail_string; string_t *input_multiline; const char **xclient_args; struct dns_lookup *dns_lookup; struct istream *input; struct ostream *output; struct io *io; struct timeout *to; int fd; void (*data_output_callback)(void *); void *data_output_context; lmtp_finish_callback_t *finish_callback; void *finish_context; const char *data_header; ARRAY(struct lmtp_rcpt) recipients; unsigned int rcpt_next_receive_idx; unsigned int rcpt_next_data_idx; unsigned int rcpt_next_send_idx; struct istream *data_input; unsigned char output_last; struct lmtp_client_times times; unsigned int running:1; unsigned int xclient_sent:1; unsigned int rcpt_to_successes:1; unsigned int output_finished:1; unsigned int finish_called:1; unsigned int global_remote_failure:1; }; static void lmtp_client_send_rcpts(struct lmtp_client *client); struct lmtp_client * lmtp_client_init(const struct lmtp_client_settings *set, lmtp_finish_callback_t *finish_callback, void *context) { struct lmtp_client *client; pool_t pool; i_assert(*set->mail_from == '<'); i_assert(*set->my_hostname != '\0'); pool = pool_alloconly_create("lmtp client", 512); client = p_new(pool, struct lmtp_client, 1); client->refcount = 1; client->pool = pool; client->set.mail_from = p_strdup(pool, set->mail_from); client->set.my_hostname = p_strdup(pool, set->my_hostname); client->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); client->set.source_ip = set->source_ip; client->set.source_port = set->source_port; client->set.proxy_ttl = set->proxy_ttl; client->set.proxy_timeout_secs = set->proxy_timeout_secs; client->set.timeout_secs = set->timeout_secs; client->finish_callback = finish_callback; client->finish_context = context; client->fd = -1; client->input_multiline = str_new(default_pool, 128); p_array_init(&client->recipients, pool, 16); return client; } void lmtp_client_close(struct lmtp_client *client) { if (client->dns_lookup != NULL) dns_lookup_abort(&client->dns_lookup); if (client->to != NULL) timeout_remove(&client->to); if (client->io != NULL) io_remove(&client->io); if (client->input != NULL) i_stream_close(client->input); if (client->output != NULL) o_stream_close(client->output); if (client->fd != -1) { net_disconnect(client->fd); client->fd = -1; } if (client->data_input != NULL) i_stream_unref(&client->data_input); client->output_finished = TRUE; if (!client->finish_called) { client->finish_called = TRUE; client->finish_callback(client->finish_context); } } static void lmtp_client_ref(struct lmtp_client *client) { client->refcount++; } static void lmtp_client_unref(struct lmtp_client **_client) { struct lmtp_client *client = *_client; *_client = NULL; i_assert(client->refcount > 0); if (--client->refcount > 0) return; i_assert(client->finish_called); if (client->input != NULL) i_stream_unref(&client->input); if (client->output != NULL) o_stream_unref(&client->output); str_free(&client->input_multiline); pool_unref(&client->pool); } void lmtp_client_deinit(struct lmtp_client **_client) { struct lmtp_client *client = *_client; *_client = NULL; lmtp_client_close(client); lmtp_client_unref(&client); } const char *lmtp_client_state_to_string(struct lmtp_client *client) { uoff_t size; switch (client->input_state) { case LMTP_INPUT_STATE_GREET: return "greeting"; case LMTP_INPUT_STATE_LHLO: return "LHLO"; case LMTP_INPUT_STATE_MAIL_FROM: return "MAIL FROM"; case LMTP_INPUT_STATE_RCPT_TO: return "RCPT TO"; case LMTP_INPUT_STATE_DATA_CONTINUE: return "DATA init"; case LMTP_INPUT_STATE_DATA: if (client->output_finished) return "DATA reply"; else if (i_stream_get_size(client->data_input, FALSE, &size) > 0) { return t_strdup_printf( "DATA (%"PRIuUOFF_T"/%"PRIuUOFF_T")", client->data_input->v_offset, size); } else { return t_strdup_printf("DATA (%"PRIuUOFF_T"/?)", client->data_input->v_offset); } case LMTP_INPUT_STATE_XCLIENT: return "XCLIENT"; } return "??"; } static void lmtp_client_fail_full(struct lmtp_client *client, const char *line, bool remote) { enum lmtp_client_result result; struct lmtp_rcpt *recipients; unsigned int i, count; client->global_fail_string = p_strdup(client->pool, line); client->global_remote_failure = remote; result = remote ? LMTP_CLIENT_RESULT_REMOTE_ERROR : LMTP_CLIENT_RESULT_INTERNAL_ERROR; lmtp_client_ref(client); recipients = array_get_modifiable(&client->recipients, &count); for (i = client->rcpt_next_receive_idx; i < count; i++) { recipients[i].rcpt_to_callback(result, line, recipients[i].context); recipients[i].failed = TRUE; } client->rcpt_next_receive_idx = count; for (i = client->rcpt_next_data_idx; i < count; i++) { if (!recipients[i].failed) { recipients[i].data_callback(result, line, recipients[i].context); } } client->rcpt_next_data_idx = count; lmtp_client_close(client); lmtp_client_unref(&client); } static void lmtp_client_fail_remote(struct lmtp_client *client, const char *line) { lmtp_client_fail_full(client, line, TRUE); } void lmtp_client_fail(struct lmtp_client *client, const char *line) { lmtp_client_fail_full(client, line, FALSE); } static int lmtp_client_rcpt_next(struct lmtp_client *client, const char *line) { struct lmtp_rcpt *rcpt; enum lmtp_client_result result; result = line[0] == '2' ? LMTP_CLIENT_RESULT_OK : LMTP_CLIENT_RESULT_REMOTE_ERROR; if (result == LMTP_CLIENT_RESULT_OK) client->rcpt_to_successes = TRUE; if (client->rcpt_next_receive_idx >= array_count(&client->recipients)) { lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 Received unexpected reply: %s", line)); return -1; } rcpt = array_idx_modifiable(&client->recipients, client->rcpt_next_receive_idx); client->rcpt_next_receive_idx++; rcpt->failed = result != LMTP_CLIENT_RESULT_OK; rcpt->rcpt_to_callback(result, line, rcpt->context); return 0; } static int lmtp_client_send_data_cmd(struct lmtp_client *client) { if (client->rcpt_next_receive_idx < array_count(&client->recipients)) return 0; if (client->global_fail_string != NULL) { lmtp_client_fail_full(client, client->global_fail_string, client->global_remote_failure); return -1; } else if (!client->rcpt_to_successes) { /* This error string shouldn't become visible anywhere */ lmtp_client_fail_full(client, "No valid recipients", FALSE); return -1; } else { client->input_state++; o_stream_nsend_str(client->output, "DATA\r\n"); return 0; } } static int lmtp_client_data_next(struct lmtp_client *client, const char *line) { struct lmtp_rcpt *rcpt; unsigned int i, count; enum lmtp_client_result result; rcpt = array_get_modifiable(&client->recipients, &count); for (i = client->rcpt_next_data_idx; i < count; i++) { if (rcpt[i].failed) { /* already called rcpt_to_callback with failure */ continue; } client->rcpt_next_data_idx = i + 1; rcpt[i].failed = line[0] != '2'; result = rcpt[i].failed ? LMTP_CLIENT_RESULT_REMOTE_ERROR : LMTP_CLIENT_RESULT_OK; rcpt[i].data_callback(result, line, rcpt[i].context); if (client->protocol == LMTP_CLIENT_PROTOCOL_LMTP) break; } if (client->rcpt_next_data_idx < count) return 0; o_stream_send_str(client->output, "QUIT\r\n"); lmtp_client_close(client); return -1; } static int lmtp_client_send_data(struct lmtp_client *client) { const unsigned char *data; unsigned char add; size_t i, size; bool sent_bytes = FALSE; int ret; if (client->output_finished) return 0; while ((ret = i_stream_read_data(client->data_input, &data, &size, 0)) > 0) { add = '\0'; for (i = 0; i < size; i++) { if (data[i] == '\n') { if ((i == 0 && client->output_last != '\r') || (i > 0 && data[i-1] != '\r')) { /* missing CR */ add = '\r'; break; } } else if (data[i] == '.' && ((i == 0 && client->output_last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; break; } } if (i > 0) { if (o_stream_send(client->output, data, i) < 0) break; client->output_last = data[i-1]; i_stream_skip(client->data_input, i); sent_bytes = TRUE; } if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) { /* continue later */ o_stream_set_flush_pending(client->output, TRUE); return 0; } } if (add != '\0') { if (o_stream_send(client->output, &add, 1) < 0) break; client->output_last = add; } } if (client->data_input->stream_errno != 0) { i_error("lmtp client: read(%s) failed: %s", i_stream_get_name(client->data_input), i_stream_get_error(client->data_input)); lmtp_client_fail(client, "451 4.3.0 Internal failure while reading DATA input"); return -1; } if (sent_bytes && client->data_output_callback != NULL) client->data_output_callback(client->data_output_context); if (ret == 0 || ret == -2) { /* -2 can happen with tee istreams */ return 0; } if (client->output_last != '\n') { /* didn't end with CRLF */ o_stream_nsend(client->output, "\r\n", 2); } o_stream_nsend(client->output, ".\r\n", 3); client->output_finished = TRUE; io_loop_time_refresh(); client->times.data_sent = ioloop_timeval; return 0; } static void lmtp_client_send_handshake(struct lmtp_client *client) { switch (client->protocol) { case LMTP_CLIENT_PROTOCOL_LMTP: o_stream_nsend_str(client->output, t_strdup_printf("LHLO %s\r\n", client->set.my_hostname)); break; case LMTP_CLIENT_PROTOCOL_SMTP: o_stream_nsend_str(client->output, t_strdup_printf("EHLO %s\r\n", client->set.my_hostname)); break; } } static int lmtp_input_get_reply_code(const char *line, int *reply_code_r, string_t *multiline) { if (!i_isdigit(line[0]) || !i_isdigit(line[1]) || !i_isdigit(line[2])) return -1; *reply_code_r = (line[0]-'0') * 100 + (line[1]-'0') * 10 + (line[2]-'0'); if (line[3] == ' ') { /* final reply */ return 1; } else if (line[3] == '-') { /* multiline reply. */ str_append(multiline, line); str_append_c(multiline, '\n'); return 0; } else { /* invalid input */ return -1; } } static void lmtp_client_parse_capabilities(struct lmtp_client *client, const char *lines) { const char *const *linep; for (linep = t_strsplit(lines, "\n"); *linep != NULL; linep++) { const char *line = *linep; line += 4; /* already checked this is valid */ if (strncasecmp(line, "XCLIENT ", 8) == 0) { client->xclient_args = (void *)p_strsplit(client->pool, line + 8, " "); } } } static bool lmtp_client_send_xclient(struct lmtp_client *client) { string_t *str; size_t empty_len; if (client->xclient_args == NULL) { /* not supported */ return FALSE; } if (client->xclient_sent) return FALSE; str = t_str_new(64); str_append(str, "XCLIENT"); empty_len = str_len(str); if (client->set.source_ip.family != 0 && str_array_icase_find(client->xclient_args, "ADDR")) str_printfa(str, " ADDR=%s", net_ip2addr(&client->set.source_ip)); if (client->set.source_port != 0 && str_array_icase_find(client->xclient_args, "PORT")) str_printfa(str, " PORT=%u", client->set.source_port); if (client->set.proxy_ttl != 0 && str_array_icase_find(client->xclient_args, "TTL")) str_printfa(str, " TTL=%u", client->set.proxy_ttl); if (client->set.proxy_timeout_secs != 0 && str_array_icase_find(client->xclient_args, "TIMEOUT")) str_printfa(str, " TIMEOUT=%u", client->set.proxy_timeout_secs); if (str_len(str) == empty_len) return FALSE; str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); return TRUE; } static int lmtp_client_input_line(struct lmtp_client *client, const char *line) { int ret, reply_code = 0; if ((ret = lmtp_input_get_reply_code(line, &reply_code, client->input_multiline)) <= 0) { if (ret == 0) return 0; lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 Received invalid input: %s", line)); return -1; } switch (client->input_state) { case LMTP_INPUT_STATE_GREET: if (reply_code != 220) { lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 Received invalid greeting: %s", line)); return -1; } client->times.banner_received = ioloop_timeval; lmtp_client_send_handshake(client); client->input_state = LMTP_INPUT_STATE_LHLO; break; case LMTP_INPUT_STATE_XCLIENT: if (reply_code != 220) { lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 XCLIENT failed: %s", line)); return -1; } lmtp_client_send_handshake(client); client->input_state = LMTP_INPUT_STATE_LHLO; break; case LMTP_INPUT_STATE_LHLO: if (reply_code != 250) { lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 LHLO failed: %s", line)); lmtp_client_fail(client, line); return -1; } str_append(client->input_multiline, line); lmtp_client_parse_capabilities(client, str_c(client->input_multiline)); if (lmtp_client_send_xclient(client)) { client->input_state = LMTP_INPUT_STATE_XCLIENT; client->xclient_sent = TRUE; break; } o_stream_nsend_str(client->output, t_strdup_printf( "MAIL FROM:%s\r\n", client->set.mail_from)); client->input_state++; break; case LMTP_INPUT_STATE_MAIL_FROM: if (reply_code != 250) { lmtp_client_fail(client, t_strdup_printf( "451 4.5.0 MAIL FROM failed: %s", line)); return -1; } client->input_state++; lmtp_client_send_rcpts(client); break; case LMTP_INPUT_STATE_RCPT_TO: if (lmtp_client_rcpt_next(client, line) < 0) return -1; if (client->data_input == NULL) break; if (lmtp_client_send_data_cmd(client) < 0) return -1; break; case LMTP_INPUT_STATE_DATA_CONTINUE: /* Start sending DATA */ if (strncmp(line, "354", 3) != 0) { lmtp_client_fail_remote(client, line); return -1; } client->input_state++; client->times.data_started = ioloop_timeval; if (client->data_header != NULL) o_stream_nsend_str(client->output, client->data_header); if (lmtp_client_send_data(client) < 0) return -1; break; case LMTP_INPUT_STATE_DATA: /* DATA replies */ if (lmtp_client_data_next(client, line) < 0) return -1; break; } return 1; } static void lmtp_client_input(struct lmtp_client *client) { const char *line; int ret; lmtp_client_ref(client); o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { T_BEGIN { ret = lmtp_client_input_line(client, line); } T_END; if (ret < 0) { o_stream_uncork(client->output); lmtp_client_unref(&client); return; } if (ret > 0) str_truncate(client->input_multiline, 0); } if (client->input->stream_errno == ENOBUFS) { lmtp_client_fail(client, "501 5.5.4 Command reply line too long"); } else if (client->input->stream_errno != 0) { i_error("lmtp client: read() failed: %s", i_stream_get_error(client->input)); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (read failure)"); } else if (client->input->eof) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in input)"); } o_stream_uncork(client->output); if (client->to != NULL) timeout_reset(client->to); lmtp_client_unref(&client); } static void lmtp_client_wait_connect(struct lmtp_client *client) { int err; err = net_geterror(client->fd); if (err != 0) { i_error("lmtp client: connect(%s, %u) failed: %s", client->host, client->port, strerror(err)); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect)"); return; } if (client->data_input == NULL && client->to != NULL) timeout_remove(&client->to); io_remove(&client->io); client->io = io_add(client->fd, IO_READ, lmtp_client_input, client); lmtp_client_input(client); } static void lmtp_client_connect_timeout(struct lmtp_client *client) { i_error("lmtp client: connect(%s, %u) failed: Timed out in %u secs", client->host, client->port, client->set.timeout_secs); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect timeout)"); } static int lmtp_client_output(struct lmtp_client *client) { int ret; lmtp_client_ref(client); o_stream_cork(client->output); if ((ret = o_stream_flush(client->output)) < 0) lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in output)"); else if (client->input_state == LMTP_INPUT_STATE_DATA) (void)lmtp_client_send_data(client); o_stream_uncork(client->output); if (client->to != NULL) timeout_reset(client->to); lmtp_client_unref(&client); return ret; } static int lmtp_client_connect(struct lmtp_client *client) { i_assert(client->fd == -1); client->times.connect_started = ioloop_timeval; client->fd = net_connect_ip(&client->ip, client->port, NULL); if (client->fd == -1) { i_error("lmtp client: connect(%s, %u) failed: %m", client->host, client->port); return -1; } client->input = i_stream_create_fd(client->fd, LMTP_MAX_LINE_LEN, FALSE); client->output = o_stream_create_fd(client->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); o_stream_set_flush_callback(client->output, lmtp_client_output, client); /* we're already sending data in ostream, so can't use IO_WRITE here */ client->io = io_add(client->fd, IO_READ, lmtp_client_wait_connect, client); if (client->set.timeout_secs > 0) { client->to = timeout_add(client->set.timeout_secs*1000, lmtp_client_connect_timeout, client); } return 0; } static void lmtp_client_dns_done(const struct dns_lookup_result *result, struct lmtp_client *client) { client->dns_lookup = NULL; if (result->ret != 0) { i_error("lmtp client: DNS lookup of %s failed: %s", client->host, result->error); if (client->running) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (DNS lookup)"); } } else { client->ip = result->ips[0]; if (lmtp_client_connect(client) < 0 && client->running) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect)"); } } } int lmtp_client_connect_tcp(struct lmtp_client *client, enum lmtp_client_protocol protocol, const char *host, in_port_t port) { struct dns_lookup_settings dns_lookup_set; struct ip_addr *ips; unsigned int ips_count; int ret; client->input_state = LMTP_INPUT_STATE_GREET; client->host = p_strdup(client->pool, host); client->port = port; client->protocol = protocol; if (*host == '\0') { i_error("lmtp client: host not given"); return -1; } i_zero(&dns_lookup_set); dns_lookup_set.dns_client_socket_path = client->set.dns_client_socket_path; dns_lookup_set.timeout_msecs = LMTP_CLIENT_DNS_LOOKUP_TIMEOUT_MSECS; if (net_addr2ip(host, &client->ip) == 0) { /* IP address */ } else if (dns_lookup_set.dns_client_socket_path == NULL) { /* no dns-client, use blocking lookup */ ret = net_gethostbyname(host, &ips, &ips_count); if (ret != 0) { i_error("lmtp client: DNS lookup of %s failed: %s", client->host, net_gethosterror(ret)); return -1; } client->ip = ips[0]; } else { if (dns_lookup(host, &dns_lookup_set, lmtp_client_dns_done, client, &client->dns_lookup) < 0) return -1; client->running = TRUE; return 0; } if (lmtp_client_connect(client) < 0) return -1; return 0; } void lmtp_client_set_data_header(struct lmtp_client *client, const char *str) { client->data_header = p_strdup(client->pool, str); } static void lmtp_append_xtext(string_t *dest, const char *str) { unsigned int i; for (i = 0; str[i] != '\0'; i++) { if (str[i] >= 33 && str[i] <= 126 && str[i] != '+' && str[i] != '=') str_append_c(dest, str[i]); else str_printfa(dest, "+%02X", str[i]); } } static void lmtp_client_send_rcpts(struct lmtp_client *client) { const struct lmtp_rcpt *rcpt; unsigned int i, count; string_t *str = t_str_new(128); rcpt = array_get(&client->recipients, &count); for (i = client->rcpt_next_send_idx; i < count; i++) { str_truncate(str, 0); str_printfa(str, "RCPT TO:<%s>", rcpt[i].address); if (rcpt->params.dsn_orcpt != NULL) { str_append(str, " ORCPT="); lmtp_append_xtext(str, rcpt->params.dsn_orcpt); } str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); } client->rcpt_next_send_idx = i; } void lmtp_client_add_rcpt(struct lmtp_client *client, const char *address, lmtp_callback_t *rcpt_to_callback, lmtp_callback_t *data_callback, void *context) { struct lmtp_recipient_params params; i_zero(¶ms); lmtp_client_add_rcpt_params(client, address, ¶ms, rcpt_to_callback, data_callback, context); } void lmtp_client_add_rcpt_params(struct lmtp_client *client, const char *address, const struct lmtp_recipient_params *params, lmtp_callback_t *rcpt_to_callback, lmtp_callback_t *data_callback, void *context) { struct lmtp_rcpt *rcpt; enum lmtp_client_result result; rcpt = array_append_space(&client->recipients); rcpt->address = p_strdup(client->pool, address); rcpt->params.dsn_orcpt = p_strdup(client->pool, params->dsn_orcpt); rcpt->rcpt_to_callback = rcpt_to_callback; rcpt->data_callback = data_callback; rcpt->context = context; if (client->global_fail_string != NULL) { /* we've already failed */ client->rcpt_next_receive_idx++; i_assert(client->rcpt_next_receive_idx == array_count(&client->recipients)); result = client->global_remote_failure ? LMTP_CLIENT_RESULT_REMOTE_ERROR : LMTP_CLIENT_RESULT_INTERNAL_ERROR; rcpt->failed = TRUE; rcpt_to_callback(result, client->global_fail_string, context); } else if (client->input_state == LMTP_INPUT_STATE_RCPT_TO) lmtp_client_send_rcpts(client); } static void lmtp_client_timeout(struct lmtp_client *client) { const char *line; line = t_strdup_printf(ERRSTR_TEMP_REMOTE_FAILURE " (Timed out after %u secs while waiting for reply to %s)", client->set.timeout_secs, lmtp_client_state_to_string(client)); lmtp_client_fail(client, line); } void lmtp_client_send(struct lmtp_client *client, struct istream *data_input) { i_assert(client->data_input == NULL); i_stream_ref(data_input); client->data_input = data_input; /* now we actually want to start doing I/O. start the timeout handling. */ if (client->set.timeout_secs > 0) { if (client->to != NULL) { /* still waiting for connect to finish */ timeout_remove(&client->to); } client->to = timeout_add(client->set.timeout_secs*1000, lmtp_client_timeout, client); } (void)lmtp_client_send_data_cmd(client); } void lmtp_client_send_more(struct lmtp_client *client) { if (client->input_state == LMTP_INPUT_STATE_DATA) { o_stream_cork(client->output); (void)lmtp_client_send_data(client); o_stream_uncork(client->output); } } void lmtp_client_set_data_output_callback(struct lmtp_client *client, void (*callback)(void *), void *context) { client->data_output_callback = callback; client->data_output_context = context; } const struct lmtp_client_times * lmtp_client_get_times(struct lmtp_client *client) { return &client->times; } dovecot-2.2.33.2/src/lib-smtp/lmtp-client.h0000644000175000017500000000775013165463547015273 00000000000000#ifndef LMTP_CLIENT_H #define LMTP_CLIENT_H #include "net.h" #define ERRSTR_TEMP_REMOTE_FAILURE "451 4.4.0 Remote server not answering" /* LMTP/SMTP client code. */ enum lmtp_client_protocol { LMTP_CLIENT_PROTOCOL_LMTP, LMTP_CLIENT_PROTOCOL_SMTP }; enum lmtp_client_result { /* Command succeeded */ LMTP_CLIENT_RESULT_OK = 1, /* Command failed because remote server returned an error */ LMTP_CLIENT_RESULT_REMOTE_ERROR = 0, /* Command failed because of an internal error (e.g. couldn't connect to remote) */ LMTP_CLIENT_RESULT_INTERNAL_ERROR = -1 }; struct lmtp_recipient_params { const char *dsn_orcpt; }; struct lmtp_client_times { struct timeval connect_started; struct timeval banner_received; struct timeval data_started; struct timeval data_sent; }; struct lmtp_client_settings { const char *my_hostname; /* The whole MAIL FROM line, including parameters */ const char *mail_from; const char *dns_client_socket_path; /* if remote server supports XCLIENT capability, send the these as ADDR/PORT/TTL/TIMEOUT */ struct ip_addr source_ip; in_port_t source_port; /* send TTL as this (default 0 means "don't send it") */ unsigned int proxy_ttl; /* remote is notified that the connection is going to be closed after this many seconds, so it should try to keep lock waits and such lower than this. */ unsigned int proxy_timeout_secs; /* Don't wait an answer from destination server longer than this many seconds (0 = unlimited) */ unsigned int timeout_secs; }; /* reply contains the reply coming from remote server, or NULL if it's a connection error. */ typedef void lmtp_callback_t(enum lmtp_client_result result, const char *reply, void *context); /* called when session is finished, either because all RCPT TOs failed or because all DATA replies have been received. */ typedef void lmtp_finish_callback_t(void *context); struct lmtp_client * lmtp_client_init(const struct lmtp_client_settings *set, lmtp_finish_callback_t *finish_callback, void *context); void lmtp_client_deinit(struct lmtp_client **client); int lmtp_client_connect_tcp(struct lmtp_client *client, enum lmtp_client_protocol protocol, const char *host, in_port_t port); void lmtp_client_close(struct lmtp_client *client); /* Add headers from given string before the rest of the data. The string must use CRLF line feeds and end with CRLF. */ void lmtp_client_set_data_header(struct lmtp_client *client, const char *str); /* Add recipient to the session. rcpt_to_callback is called once LMTP server replies with RCPT TO. If RCPT TO was a succees, data_callback is called when DATA replies. */ void lmtp_client_add_rcpt(struct lmtp_client *client, const char *address, lmtp_callback_t *rcpt_to_callback, lmtp_callback_t *data_callback, void *context); void lmtp_client_add_rcpt_params(struct lmtp_client *client, const char *address, const struct lmtp_recipient_params *params, lmtp_callback_t *rcpt_to_callback, lmtp_callback_t *data_callback, void *context); /* Start sending input stream as DATA. */ void lmtp_client_send(struct lmtp_client *client, struct istream *data_input); /* Call this function whenever input stream can potentially be read forward. This is useful with non-blocking istreams and tee-istreams. */ void lmtp_client_send_more(struct lmtp_client *client); /* Fail the connection with line as the reply to unfinished RCPT TO/DATA replies. This will be treated as an internal failure. */ void lmtp_client_fail(struct lmtp_client *client, const char *line); /* Return the state (command reply) the client is currently waiting for. */ const char *lmtp_client_state_to_string(struct lmtp_client *client); /* Call the given callback whenever client manages to send some more DATA output to client. */ void lmtp_client_set_data_output_callback(struct lmtp_client *client, void (*callback)(void *), void *context); /* Return LMTP client statistics. */ const struct lmtp_client_times * lmtp_client_get_times(struct lmtp_client *client); #endif dovecot-2.2.33.2/src/lib-smtp/Makefile.am0000644000175000017500000000035413165463624014713 00000000000000noinst_LTLIBRARIES = libsmtp.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dns libsmtp_la_SOURCES = \ lmtp-client.c headers = \ lmtp-client.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-sql/0002755000175000017500000000000013172375611012547 500000000000000dovecot-2.2.33.2/src/lib-sql/driver-sqlite.c0000644000175000017500000002624113165463624015434 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hex-binary.h" #include "sql-api-private.h" #ifdef BUILD_SQLITE #include /* retry time if db is busy (in ms) */ static const int sqlite_busy_timeout = 1000; struct sqlite_db { struct sql_db api; pool_t pool; const char *dbfile; sqlite3 *sqlite; unsigned int connected:1; int rc; }; struct sqlite_result { struct sql_result api; sqlite3_stmt *stmt; unsigned int cols; const char **row; }; struct sqlite_transaction_context { struct sql_transaction_context ctx; unsigned int failed:1; }; extern const struct sql_db driver_sqlite_db; extern const struct sql_result driver_sqlite_result; extern const struct sql_result driver_sqlite_error_result; static int driver_sqlite_connect(struct sql_db *_db) { struct sqlite_db *db = (struct sqlite_db *)_db; if (db->connected) return 1; db->rc = sqlite3_open(db->dbfile, &db->sqlite); if (db->rc == SQLITE_OK) { db->connected = TRUE; sqlite3_busy_timeout(db->sqlite, sqlite_busy_timeout); return 1; } else { i_error("sqlite: open(%s) failed: %s", db->dbfile, sqlite3_errmsg(db->sqlite)); sqlite3_close(db->sqlite); db->sqlite = NULL; return -1; } } static void driver_sqlite_disconnect(struct sql_db *_db) { struct sqlite_db *db = (struct sqlite_db *)_db; sqlite3_close(db->sqlite); db->sqlite = NULL; } static struct sql_db *driver_sqlite_init_v(const char *connect_string) { struct sqlite_db *db; pool_t pool; i_assert(connect_string != NULL); pool = pool_alloconly_create("sqlite driver", 512); db = p_new(pool, struct sqlite_db, 1); db->pool = pool; db->api = driver_sqlite_db; db->dbfile = p_strdup(db->pool, connect_string); db->connected = FALSE; return &db->api; } static void driver_sqlite_deinit_v(struct sql_db *_db) { struct sqlite_db *db = (struct sqlite_db *)_db; _db->no_reconnect = TRUE; sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED); sqlite3_close(db->sqlite); array_free(&_db->module_contexts); pool_unref(&db->pool); } static const char * driver_sqlite_escape_string(struct sql_db *_db ATTR_UNUSED, const char *string) { const char *p; char *dest, *destbegin; /* find the first ' */ for (p = string; *p != '\''; p++) { if (*p == '\0') return t_strdup_noconst(string); } /* @UNSAFE: escape ' with '' */ dest = destbegin = t_buffer_get((p - string) + strlen(string) * 2 + 1); memcpy(dest, string, p - string); dest += p - string; for (; *p != '\0'; p++) { *dest++ = *p; if (*p == '\'') *dest++ = *p; } *dest++ = '\0'; t_buffer_alloc(dest - destbegin); return destbegin; } static void driver_sqlite_exec(struct sql_db *_db, const char *query) { struct sqlite_db *db = (struct sqlite_db *)_db; if (driver_sqlite_connect(_db) < 0) return; db->rc = sqlite3_exec(db->sqlite, query, NULL, NULL, NULL); if (db->rc != SQLITE_OK) { i_error("sqlite: exec(%s) failed: %s (%d)", query, sqlite3_errmsg(db->sqlite), db->rc); } } static void driver_sqlite_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { struct sql_result *result; result = sql_query_s(db, query); result->callback = TRUE; callback(result, context); result->callback = FALSE; sql_result_unref(result); } static struct sql_result * driver_sqlite_query_s(struct sql_db *_db, const char *query) { struct sqlite_db *db = (struct sqlite_db *)_db; struct sqlite_result *result; int rc; result = i_new(struct sqlite_result, 1); if (driver_sqlite_connect(_db) < 0) { result->api = driver_sqlite_error_result; result->stmt = NULL; result->cols = 0; } else { rc = sqlite3_prepare(db->sqlite, query, -1, &result->stmt, NULL); if (rc == SQLITE_OK) { result->api = driver_sqlite_result; result->cols = sqlite3_column_count(result->stmt); result->row = i_new(const char *, result->cols); } else { result->api = driver_sqlite_error_result; result->stmt = NULL; result->cols = 0; } } result->api.db = _db; result->api.refcount = 1; return &result->api; } static void driver_sqlite_result_free(struct sql_result *_result) { struct sqlite_result *result = (struct sqlite_result *)_result; struct sqlite_db *db = (struct sqlite_db *) result->api.db; int rc; if (_result->callback) return; if (result->stmt != NULL) { if ((rc = sqlite3_finalize(result->stmt)) != SQLITE_OK) { i_warning("sqlite: finalize failed: %s (%d)", sqlite3_errmsg(db->sqlite), rc); } i_free(result->row); } i_free(result); } static int driver_sqlite_result_next_row(struct sql_result *_result) { struct sqlite_result *result = (struct sqlite_result *)_result; switch (sqlite3_step(result->stmt)) { case SQLITE_ROW: return 1; case SQLITE_DONE: return 0; default: return -1; } } static unsigned int driver_sqlite_result_get_fields_count(struct sql_result *_result) { struct sqlite_result *result = (struct sqlite_result *)_result; return result->cols; } static const char * driver_sqlite_result_get_field_name(struct sql_result *_result, unsigned int idx) { struct sqlite_result *result = (struct sqlite_result *)_result; return sqlite3_column_name(result->stmt, idx); } static int driver_sqlite_result_find_field(struct sql_result *_result, const char *field_name) { struct sqlite_result *result = (struct sqlite_result *)_result; unsigned int i; for (i = 0; i < result->cols; ++i) { const char *col = sqlite3_column_name(result->stmt, i); if (strcmp(col, field_name) == 0) return i; } return -1; } static const char * driver_sqlite_result_get_field_value(struct sql_result *_result, unsigned int idx) { struct sqlite_result *result = (struct sqlite_result *)_result; return (const char*)sqlite3_column_text(result->stmt, idx); } static const unsigned char * driver_sqlite_result_get_field_value_binary(struct sql_result *_result, unsigned int idx, size_t *size_r) { struct sqlite_result *result = (struct sqlite_result *)_result; *size_r = sqlite3_column_bytes(result->stmt, idx); return sqlite3_column_blob(result->stmt, idx); } static const char * driver_sqlite_result_find_field_value(struct sql_result *result, const char *field_name) { int idx; idx = driver_sqlite_result_find_field(result, field_name); if (idx < 0) return NULL; return driver_sqlite_result_get_field_value(result, idx); } static const char *const * driver_sqlite_result_get_values(struct sql_result *_result) { struct sqlite_result *result = (struct sqlite_result *)_result; unsigned int i; for (i = 0; i < result->cols; ++i) { result->row[i] = driver_sqlite_result_get_field_value(_result, i); } return (const char *const *)result->row; } static const char *driver_sqlite_result_get_error(struct sql_result *_result) { struct sqlite_result *result = (struct sqlite_result *)_result; struct sqlite_db *db = (struct sqlite_db *)result->api.db; return sqlite3_errmsg(db->sqlite); } static struct sql_transaction_context * driver_sqlite_transaction_begin(struct sql_db *_db) { struct sqlite_transaction_context *ctx; struct sqlite_db *db = (struct sqlite_db *)_db; ctx = i_new(struct sqlite_transaction_context, 1); ctx->ctx.db = _db; sql_exec(_db, "BEGIN TRANSACTION"); if (db->rc != SQLITE_OK) ctx->failed = TRUE; return &ctx->ctx; } static void driver_sqlite_transaction_rollback(struct sql_transaction_context *_ctx) { struct sqlite_transaction_context *ctx = (struct sqlite_transaction_context *)_ctx; sql_exec(_ctx->db, "ROLLBACK"); i_free(ctx); } static void driver_sqlite_transaction_commit(struct sql_transaction_context *_ctx, sql_commit_callback_t *callback, void *context) { struct sqlite_transaction_context *ctx = (struct sqlite_transaction_context *)_ctx; struct sqlite_db *db = (struct sqlite_db *)ctx->ctx.db; const char *errmsg; if (!ctx->failed) { sql_exec(_ctx->db, "COMMIT"); if (db->rc != SQLITE_OK) ctx->failed = TRUE; } if (ctx->failed) { errmsg = sqlite3_errmsg(db->sqlite); callback(errmsg, context); /* also does i_free(ctx) */ driver_sqlite_transaction_rollback(_ctx); } else { callback(NULL, context); i_free(ctx); } } static int driver_sqlite_transaction_commit_s(struct sql_transaction_context *_ctx, const char **error_r) { struct sqlite_transaction_context *ctx = (struct sqlite_transaction_context *)_ctx; struct sqlite_db *db = (struct sqlite_db *) ctx->ctx.db; if (ctx->failed) { /* also does i_free(ctx) */ driver_sqlite_transaction_rollback(_ctx); return -1; } sql_exec(_ctx->db, "COMMIT"); *error_r = sqlite3_errmsg(db->sqlite); i_free(ctx); return 0; } static void driver_sqlite_update(struct sql_transaction_context *_ctx, const char *query, unsigned int *affected_rows) { struct sqlite_transaction_context *ctx = (struct sqlite_transaction_context *)_ctx; struct sqlite_db *db = (struct sqlite_db *)ctx->ctx.db; if (ctx->failed) return; sql_exec(_ctx->db, query); if (db->rc != SQLITE_OK) ctx->failed = TRUE; else if (affected_rows != NULL) *affected_rows = sqlite3_changes(db->sqlite); } static const char * driver_sqlite_escape_blob(struct sql_db *_db ATTR_UNUSED, const unsigned char *data, size_t size) { string_t *str = t_str_new(128); str_append(str, "x'"); binary_to_hex_append(str, data, size); str_append_c(str, '\''); return str_c(str); } const struct sql_db driver_sqlite_db = { .name = "sqlite", .flags = SQL_DB_FLAG_BLOCKING, .v = { .init = driver_sqlite_init_v, .deinit = driver_sqlite_deinit_v, .connect = driver_sqlite_connect, .disconnect = driver_sqlite_disconnect, .escape_string = driver_sqlite_escape_string, .exec = driver_sqlite_exec, .query = driver_sqlite_query, .query_s = driver_sqlite_query_s, .transaction_begin = driver_sqlite_transaction_begin, .transaction_commit = driver_sqlite_transaction_commit, .transaction_commit_s = driver_sqlite_transaction_commit_s, .transaction_rollback = driver_sqlite_transaction_rollback, .update = driver_sqlite_update, .escape_blob = driver_sqlite_escape_blob, } }; const struct sql_result driver_sqlite_result = { .v = { .free = driver_sqlite_result_free, .next_row = driver_sqlite_result_next_row, .get_fields_count = driver_sqlite_result_get_fields_count, .get_field_name = driver_sqlite_result_get_field_name, .find_field = driver_sqlite_result_find_field, .get_field_value = driver_sqlite_result_get_field_value, .get_field_value_binary = driver_sqlite_result_get_field_value_binary, .find_field_value = driver_sqlite_result_find_field_value, .get_values = driver_sqlite_result_get_values, .get_error = driver_sqlite_result_get_error, } }; static int driver_sqlite_result_error_next_row(struct sql_result *result ATTR_UNUSED) { return -1; } const struct sql_result driver_sqlite_error_result = { .v = { .free = driver_sqlite_result_free, .next_row = driver_sqlite_result_error_next_row, .get_error = driver_sqlite_result_get_error, } }; const char *driver_sqlite_version = DOVECOT_ABI_VERSION; void driver_sqlite_init(void); void driver_sqlite_deinit(void); void driver_sqlite_init(void) { sql_driver_register(&driver_sqlite_db); } void driver_sqlite_deinit(void) { sql_driver_unregister(&driver_sqlite_db); } #endif dovecot-2.2.33.2/src/lib-sql/Makefile.in0000644000175000017500000011752313172375573014552 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_MYSQL_TRUE@@SQL_PLUGINS_TRUE@am__append_1 = mysql @BUILD_PGSQL_TRUE@@SQL_PLUGINS_TRUE@am__append_2 = pgsql @BUILD_SQLITE_TRUE@@SQL_PLUGINS_TRUE@am__append_3 = sqlite @BUILD_CASSANDRA_TRUE@@SQL_PLUGINS_TRUE@am__append_4 = cassandra subdir = src/lib-sql ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(sql_moduledir)" "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) \ $(sql_module_LTLIBRARIES) am__DEPENDENCIES_1 = @SQL_PLUGINS_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ @SQL_PLUGINS_FALSE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @SQL_PLUGINS_FALSE@ $(am__DEPENDENCIES_1) am_libdovecot_sql_la_OBJECTS = libdovecot_sql_la_OBJECTS = $(am_libdovecot_sql_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_sql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_sql_la_LDFLAGS) $(LDFLAGS) \ -o $@ @SQL_PLUGINS_TRUE@libdriver_cassandra_la_DEPENDENCIES = \ @SQL_PLUGINS_TRUE@ $(am__DEPENDENCIES_1) am__libdriver_cassandra_la_SOURCES_DIST = driver-cassandra.c @SQL_PLUGINS_TRUE@am_libdriver_cassandra_la_OBJECTS = \ @SQL_PLUGINS_TRUE@ libdriver_cassandra_la-driver-cassandra.lo libdriver_cassandra_la_OBJECTS = $(am_libdriver_cassandra_la_OBJECTS) libdriver_cassandra_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdriver_cassandra_la_LDFLAGS) \ $(LDFLAGS) -o $@ @BUILD_CASSANDRA_TRUE@@SQL_PLUGINS_TRUE@am_libdriver_cassandra_la_rpath = \ @BUILD_CASSANDRA_TRUE@@SQL_PLUGINS_TRUE@ -rpath \ @BUILD_CASSANDRA_TRUE@@SQL_PLUGINS_TRUE@ $(sql_moduledir) @SQL_PLUGINS_TRUE@libdriver_mysql_la_DEPENDENCIES = \ @SQL_PLUGINS_TRUE@ $(am__DEPENDENCIES_1) am__libdriver_mysql_la_SOURCES_DIST = driver-mysql.c @SQL_PLUGINS_TRUE@am_libdriver_mysql_la_OBJECTS = \ @SQL_PLUGINS_TRUE@ libdriver_mysql_la-driver-mysql.lo libdriver_mysql_la_OBJECTS = $(am_libdriver_mysql_la_OBJECTS) libdriver_mysql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdriver_mysql_la_LDFLAGS) \ $(LDFLAGS) -o $@ @BUILD_MYSQL_TRUE@@SQL_PLUGINS_TRUE@am_libdriver_mysql_la_rpath = \ @BUILD_MYSQL_TRUE@@SQL_PLUGINS_TRUE@ -rpath $(sql_moduledir) @SQL_PLUGINS_TRUE@libdriver_pgsql_la_DEPENDENCIES = \ @SQL_PLUGINS_TRUE@ $(am__DEPENDENCIES_1) am__libdriver_pgsql_la_SOURCES_DIST = driver-pgsql.c @SQL_PLUGINS_TRUE@am_libdriver_pgsql_la_OBJECTS = \ @SQL_PLUGINS_TRUE@ libdriver_pgsql_la-driver-pgsql.lo libdriver_pgsql_la_OBJECTS = $(am_libdriver_pgsql_la_OBJECTS) libdriver_pgsql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdriver_pgsql_la_LDFLAGS) \ $(LDFLAGS) -o $@ @BUILD_PGSQL_TRUE@@SQL_PLUGINS_TRUE@am_libdriver_pgsql_la_rpath = \ @BUILD_PGSQL_TRUE@@SQL_PLUGINS_TRUE@ -rpath $(sql_moduledir) @SQL_PLUGINS_TRUE@libdriver_sqlite_la_DEPENDENCIES = \ @SQL_PLUGINS_TRUE@ $(am__DEPENDENCIES_1) am__libdriver_sqlite_la_SOURCES_DIST = driver-sqlite.c @SQL_PLUGINS_TRUE@am_libdriver_sqlite_la_OBJECTS = \ @SQL_PLUGINS_TRUE@ libdriver_sqlite_la-driver-sqlite.lo libdriver_sqlite_la_OBJECTS = $(am_libdriver_sqlite_la_OBJECTS) libdriver_sqlite_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdriver_sqlite_la_LDFLAGS) \ $(LDFLAGS) -o $@ @BUILD_SQLITE_TRUE@@SQL_PLUGINS_TRUE@am_libdriver_sqlite_la_rpath = \ @BUILD_SQLITE_TRUE@@SQL_PLUGINS_TRUE@ -rpath $(sql_moduledir) libsql_la_LIBADD = am__libsql_la_SOURCES_DIST = sql-api.c sql-db-cache.c driver-mysql.c \ driver-pgsql.c driver-sqlite.c driver-cassandra.c \ driver-sqlpool.c am__objects_1 = sql-api.lo sql-db-cache.lo @SQL_PLUGINS_FALSE@am__objects_2 = driver-mysql.lo driver-pgsql.lo \ @SQL_PLUGINS_FALSE@ driver-sqlite.lo driver-cassandra.lo am_libsql_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ driver-sqlpool.lo nodist_libsql_la_OBJECTS = sql-drivers-register.lo libsql_la_OBJECTS = $(am_libsql_la_OBJECTS) \ $(nodist_libsql_la_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_sql_la_SOURCES) \ $(libdriver_cassandra_la_SOURCES) \ $(libdriver_mysql_la_SOURCES) $(libdriver_pgsql_la_SOURCES) \ $(libdriver_sqlite_la_SOURCES) $(libsql_la_SOURCES) \ $(nodist_libsql_la_SOURCES) DIST_SOURCES = $(libdovecot_sql_la_SOURCES) \ $(am__libdriver_cassandra_la_SOURCES_DIST) \ $(am__libdriver_mysql_la_SOURCES_DIST) \ $(am__libdriver_pgsql_la_SOURCES_DIST) \ $(am__libdriver_sqlite_la_SOURCES_DIST) \ $(am__libsql_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ # automake seems to force making this unconditional.. NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libsql.la SQL_DRIVER_PLUGINS = $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) @BUILD_MYSQL_TRUE@@SQL_PLUGINS_TRUE@MYSQL_LIB = libdriver_mysql.la @BUILD_PGSQL_TRUE@@SQL_PLUGINS_TRUE@PGSQL_LIB = libdriver_pgsql.la @BUILD_SQLITE_TRUE@@SQL_PLUGINS_TRUE@SQLITE_LIB = libdriver_sqlite.la @BUILD_CASSANDRA_TRUE@@SQL_PLUGINS_TRUE@CASSANDRA_LIB = libdriver_cassandra.la @SQL_PLUGINS_TRUE@sql_module_LTLIBRARIES = \ @SQL_PLUGINS_TRUE@ $(MYSQL_LIB) \ @SQL_PLUGINS_TRUE@ $(PGSQL_LIB) \ @SQL_PLUGINS_TRUE@ $(SQLITE_LIB) \ @SQL_PLUGINS_TRUE@ $(CASSANDRA_LIB) @SQL_PLUGINS_TRUE@sql_moduledir = $(moduledir) AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ $(SQL_CFLAGS) dist_sources = \ sql-api.c \ sql-db-cache.c @SQL_PLUGINS_FALSE@driver_sources = \ @SQL_PLUGINS_FALSE@ driver-mysql.c \ @SQL_PLUGINS_FALSE@ driver-pgsql.c \ @SQL_PLUGINS_FALSE@ driver-sqlite.c \ @SQL_PLUGINS_FALSE@ driver-cassandra.c libsql_la_SOURCES = \ $(dist_sources) \ $(driver_sources) \ driver-sqlpool.c nodist_libsql_la_SOURCES = sql-drivers-register.c deplibs = \ ../lib-dovecot/libdovecot.la @SQL_PLUGINS_TRUE@libdriver_mysql_la_LDFLAGS = -module -avoid-version @SQL_PLUGINS_TRUE@libdriver_mysql_la_LIBADD = $(MYSQL_LIBS) @SQL_PLUGINS_TRUE@libdriver_mysql_la_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQL_CFLAGS) @SQL_PLUGINS_TRUE@libdriver_mysql_la_SOURCES = driver-mysql.c @SQL_PLUGINS_TRUE@libdriver_pgsql_la_LDFLAGS = -module -avoid-version @SQL_PLUGINS_TRUE@libdriver_pgsql_la_LIBADD = $(PGSQL_LIBS) @SQL_PLUGINS_TRUE@libdriver_pgsql_la_CPPFLAGS = $(AM_CPPFLAGS) $(PGSQL_CFLAGS) @SQL_PLUGINS_TRUE@libdriver_pgsql_la_SOURCES = driver-pgsql.c @SQL_PLUGINS_TRUE@libdriver_sqlite_la_LDFLAGS = -module -avoid-version @SQL_PLUGINS_TRUE@libdriver_sqlite_la_LIBADD = $(SQLITE_LIBS) @SQL_PLUGINS_TRUE@libdriver_sqlite_la_CPPFLAGS = $(AM_CPPFLAGS) $(SQLITE_CFLAGS) @SQL_PLUGINS_TRUE@libdriver_sqlite_la_SOURCES = driver-sqlite.c @SQL_PLUGINS_TRUE@libdriver_cassandra_la_LDFLAGS = -module -avoid-version @SQL_PLUGINS_TRUE@libdriver_cassandra_la_LIBADD = $(CASSANDRA_LIBS) @SQL_PLUGINS_TRUE@libdriver_cassandra_la_CPPFLAGS = $(AM_CPPFLAGS) $(CASSANDRA_CFLAGS) @SQL_PLUGINS_TRUE@libdriver_cassandra_la_SOURCES = driver-cassandra.c @SQL_PLUGINS_FALSE@sql_libs = \ @SQL_PLUGINS_FALSE@ $(MYSQL_LIBS) \ @SQL_PLUGINS_FALSE@ $(PGSQL_LIBS) \ @SQL_PLUGINS_FALSE@ $(SQLITE_LIBS) \ @SQL_PLUGINS_FALSE@ $(CASSANDRA_LIBS) @SQL_PLUGINS_TRUE@sql_libs = pkglib_LTLIBRARIES = libdovecot-sql.la libdovecot_sql_la_SOURCES = libdovecot_sql_la_LIBADD = libsql.la $(deplibs) $(sql_libs) libdovecot_sql_la_DEPENDENCIES = libsql.la libdovecot_sql_la_LDFLAGS = -export-dynamic headers = \ sql-api.h \ sql-api-private.h \ sql-db-cache.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sql/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-sql/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-sql_moduleLTLIBRARIES: $(sql_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(sql_module_LTLIBRARIES)'; test -n "$(sql_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(sql_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sql_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(sql_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(sql_moduledir)"; \ } uninstall-sql_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(sql_module_LTLIBRARIES)'; test -n "$(sql_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(sql_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(sql_moduledir)/$$f"; \ done clean-sql_moduleLTLIBRARIES: -test -z "$(sql_module_LTLIBRARIES)" || rm -f $(sql_module_LTLIBRARIES) @list='$(sql_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-sql.la: $(libdovecot_sql_la_OBJECTS) $(libdovecot_sql_la_DEPENDENCIES) $(EXTRA_libdovecot_sql_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_sql_la_LINK) -rpath $(pkglibdir) $(libdovecot_sql_la_OBJECTS) $(libdovecot_sql_la_LIBADD) $(LIBS) libdriver_cassandra.la: $(libdriver_cassandra_la_OBJECTS) $(libdriver_cassandra_la_DEPENDENCIES) $(EXTRA_libdriver_cassandra_la_DEPENDENCIES) $(AM_V_CCLD)$(libdriver_cassandra_la_LINK) $(am_libdriver_cassandra_la_rpath) $(libdriver_cassandra_la_OBJECTS) $(libdriver_cassandra_la_LIBADD) $(LIBS) libdriver_mysql.la: $(libdriver_mysql_la_OBJECTS) $(libdriver_mysql_la_DEPENDENCIES) $(EXTRA_libdriver_mysql_la_DEPENDENCIES) $(AM_V_CCLD)$(libdriver_mysql_la_LINK) $(am_libdriver_mysql_la_rpath) $(libdriver_mysql_la_OBJECTS) $(libdriver_mysql_la_LIBADD) $(LIBS) libdriver_pgsql.la: $(libdriver_pgsql_la_OBJECTS) $(libdriver_pgsql_la_DEPENDENCIES) $(EXTRA_libdriver_pgsql_la_DEPENDENCIES) $(AM_V_CCLD)$(libdriver_pgsql_la_LINK) $(am_libdriver_pgsql_la_rpath) $(libdriver_pgsql_la_OBJECTS) $(libdriver_pgsql_la_LIBADD) $(LIBS) libdriver_sqlite.la: $(libdriver_sqlite_la_OBJECTS) $(libdriver_sqlite_la_DEPENDENCIES) $(EXTRA_libdriver_sqlite_la_DEPENDENCIES) $(AM_V_CCLD)$(libdriver_sqlite_la_LINK) $(am_libdriver_sqlite_la_rpath) $(libdriver_sqlite_la_OBJECTS) $(libdriver_sqlite_la_LIBADD) $(LIBS) libsql.la: $(libsql_la_OBJECTS) $(libsql_la_DEPENDENCIES) $(EXTRA_libsql_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libsql_la_OBJECTS) $(libsql_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver-cassandra.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver-mysql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver-pgsql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver-sqlite.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver-sqlpool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdriver_cassandra_la-driver-cassandra.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdriver_mysql_la-driver-mysql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdriver_pgsql_la-driver-pgsql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdriver_sqlite_la-driver-sqlite.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sql-api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sql-db-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sql-drivers-register.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libdriver_cassandra_la-driver-cassandra.lo: driver-cassandra.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_cassandra_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdriver_cassandra_la-driver-cassandra.lo -MD -MP -MF $(DEPDIR)/libdriver_cassandra_la-driver-cassandra.Tpo -c -o libdriver_cassandra_la-driver-cassandra.lo `test -f 'driver-cassandra.c' || echo '$(srcdir)/'`driver-cassandra.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdriver_cassandra_la-driver-cassandra.Tpo $(DEPDIR)/libdriver_cassandra_la-driver-cassandra.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='driver-cassandra.c' object='libdriver_cassandra_la-driver-cassandra.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_cassandra_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdriver_cassandra_la-driver-cassandra.lo `test -f 'driver-cassandra.c' || echo '$(srcdir)/'`driver-cassandra.c libdriver_mysql_la-driver-mysql.lo: driver-mysql.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdriver_mysql_la-driver-mysql.lo -MD -MP -MF $(DEPDIR)/libdriver_mysql_la-driver-mysql.Tpo -c -o libdriver_mysql_la-driver-mysql.lo `test -f 'driver-mysql.c' || echo '$(srcdir)/'`driver-mysql.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdriver_mysql_la-driver-mysql.Tpo $(DEPDIR)/libdriver_mysql_la-driver-mysql.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='driver-mysql.c' object='libdriver_mysql_la-driver-mysql.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdriver_mysql_la-driver-mysql.lo `test -f 'driver-mysql.c' || echo '$(srcdir)/'`driver-mysql.c libdriver_pgsql_la-driver-pgsql.lo: driver-pgsql.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_pgsql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdriver_pgsql_la-driver-pgsql.lo -MD -MP -MF $(DEPDIR)/libdriver_pgsql_la-driver-pgsql.Tpo -c -o libdriver_pgsql_la-driver-pgsql.lo `test -f 'driver-pgsql.c' || echo '$(srcdir)/'`driver-pgsql.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdriver_pgsql_la-driver-pgsql.Tpo $(DEPDIR)/libdriver_pgsql_la-driver-pgsql.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='driver-pgsql.c' object='libdriver_pgsql_la-driver-pgsql.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_pgsql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdriver_pgsql_la-driver-pgsql.lo `test -f 'driver-pgsql.c' || echo '$(srcdir)/'`driver-pgsql.c libdriver_sqlite_la-driver-sqlite.lo: driver-sqlite.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_sqlite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdriver_sqlite_la-driver-sqlite.lo -MD -MP -MF $(DEPDIR)/libdriver_sqlite_la-driver-sqlite.Tpo -c -o libdriver_sqlite_la-driver-sqlite.lo `test -f 'driver-sqlite.c' || echo '$(srcdir)/'`driver-sqlite.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdriver_sqlite_la-driver-sqlite.Tpo $(DEPDIR)/libdriver_sqlite_la-driver-sqlite.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='driver-sqlite.c' object='libdriver_sqlite_la-driver-sqlite.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdriver_sqlite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdriver_sqlite_la-driver-sqlite.lo `test -f 'driver-sqlite.c' || echo '$(srcdir)/'`driver-sqlite.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(sql_moduledir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @SQL_PLUGINS_FALSE@install-exec-local: clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-pkglibLTLIBRARIES clean-sql_moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS \ install-sql_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-exec-local install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES \ uninstall-sql_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ clean-sql_moduleLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-exec-local install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-sql_moduleLTLIBRARIES install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES \ uninstall-sql_moduleLTLIBRARIES .PRECIOUS: Makefile sql-drivers-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "sql-api.h"' >>$@ @SQL_PLUGINS_FALSE@ for i in $(sql_drivers) null; do \ @SQL_PLUGINS_FALSE@ if [ "$${i}" != "null" ]; then \ @SQL_PLUGINS_FALSE@ echo "extern struct sql_db driver_$${i}_db;" >>$@ ; \ @SQL_PLUGINS_FALSE@ fi; \ @SQL_PLUGINS_FALSE@ done echo 'void sql_drivers_register_all(void) {' >>$@ @SQL_PLUGINS_FALSE@ for i in $(sql_drivers) null; do \ @SQL_PLUGINS_FALSE@ if [ "$${i}" != "null" ]; then \ @SQL_PLUGINS_FALSE@ echo "sql_driver_register(&driver_$${i}_db);" >>$@ ; \ @SQL_PLUGINS_FALSE@ fi; \ @SQL_PLUGINS_FALSE@ done echo '}' >>$@ @SQL_PLUGINS_TRUE@install-exec-local: @SQL_PLUGINS_TRUE@ for d in auth dict; do \ @SQL_PLUGINS_TRUE@ $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \ @SQL_PLUGINS_TRUE@ for driver in $(SQL_DRIVER_PLUGINS); do \ @SQL_PLUGINS_TRUE@ rm -f $(DESTDIR)$(moduledir)/$$d/libdriver_$$driver.so; \ @SQL_PLUGINS_TRUE@ $(LN_S) ../libdriver_$$driver.so $(DESTDIR)$(moduledir)/$$d; \ @SQL_PLUGINS_TRUE@ done; \ @SQL_PLUGINS_TRUE@ done distclean-generic: rm -f Makefile sql-drivers-register.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-sql/sql-api-private.h0000644000175000017500000001513213165463624015662 00000000000000#ifndef SQL_API_PRIVATE_H #define SQL_API_PRIVATE_H #include "sql-api.h" #include "module-context.h" enum sql_db_state { /* not connected to database */ SQL_DB_STATE_DISCONNECTED, /* waiting for connection attempt to succeed or fail */ SQL_DB_STATE_CONNECTING, /* connected, allowing more queries */ SQL_DB_STATE_IDLE, /* connected, no more queries allowed */ SQL_DB_STATE_BUSY }; /* Minimum delay between reconnecting to same server */ #define SQL_CONNECT_MIN_DELAY 1 /* Maximum time to avoiding reconnecting to same server */ #define SQL_CONNECT_MAX_DELAY (60*30) /* If no servers are connected but a query is requested, try reconnecting to next server which has been disconnected longer than this (with a single server setup this is really the "max delay" and the SQL_CONNECT_MAX_DELAY is never used). */ #define SQL_CONNECT_RESET_DELAY 15 /* Abort connect() if it can't connect within this time. */ #define SQL_CONNECT_TIMEOUT_SECS 5 /* Abort queries after this many seconds */ #define SQL_QUERY_TIMEOUT_SECS 60 /* Default max. number of connections to create per host */ #define SQL_DEFAULT_CONNECTION_LIMIT 5 #define SQL_DB_IS_READY(db) \ ((db)->state == SQL_DB_STATE_IDLE) #define SQL_ERRSTR_NOT_CONNECTED "Not connected to database" struct sql_db_module_register { unsigned int id; }; union sql_db_module_context { struct sql_db_module_register *reg; }; extern struct sql_db_module_register sql_db_module_register; struct sql_transaction_query { struct sql_transaction_query *next; struct sql_transaction_context *trans; const char *query; unsigned int *affected_rows; }; struct sql_db_vfuncs { struct sql_db *(*init)(const char *connect_string); void (*deinit)(struct sql_db *db); int (*connect)(struct sql_db *db); void (*disconnect)(struct sql_db *db); const char *(*escape_string)(struct sql_db *db, const char *string); void (*exec)(struct sql_db *db, const char *query); void (*query)(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context); struct sql_result *(*query_s)(struct sql_db *db, const char *query); struct sql_transaction_context *(*transaction_begin)(struct sql_db *db); void (*transaction_commit)(struct sql_transaction_context *ctx, sql_commit_callback_t *callback, void *context); int (*transaction_commit_s)(struct sql_transaction_context *ctx, const char **error_r); void (*transaction_rollback)(struct sql_transaction_context *ctx); void (*update)(struct sql_transaction_context *ctx, const char *query, unsigned int *affected_rows); const char *(*escape_blob)(struct sql_db *db, const unsigned char *data, size_t size); void (*transaction_commit2)(struct sql_transaction_context *ctx, sql_commit2_callback_t *callback, void *context); struct sql_prepared_statement * (*prepared_statement_init)(struct sql_db *db, const char *query_template); void (*prepared_statement_deinit)(struct sql_prepared_statement *prep_stmt); struct sql_statement * (*statement_init)(struct sql_db *db, const char *query_template); struct sql_statement * (*statement_init_prepared)(struct sql_prepared_statement *prep_stmt); void (*statement_abort)(struct sql_statement *stmt); void (*statement_set_timestamp)(struct sql_statement *stmt, const struct timespec *ts); void (*statement_bind_str)(struct sql_statement *stmt, unsigned int column_idx, const char *value); void (*statement_bind_binary)(struct sql_statement *stmt, unsigned int column_idx, const void *value, size_t value_size); void (*statement_bind_int64)(struct sql_statement *stmt, unsigned int column_idx, int64_t value); void (*statement_query)(struct sql_statement *stmt, sql_query_callback_t *callback, void *context); struct sql_result *(*statement_query_s)(struct sql_statement *stmt); void (*update_stmt)(struct sql_transaction_context *ctx, struct sql_statement *stmt, unsigned int *affected_rows); }; struct sql_db { const char *name; enum sql_db_flags flags; struct sql_db_vfuncs v; ARRAY(union sql_db_module_context *) module_contexts; void (*state_change_callback)(struct sql_db *db, enum sql_db_state prev_state, void *context); void *state_change_context; enum sql_db_state state; /* last time we started connecting to this server (which may or may not have succeeded) */ time_t last_connect_try; unsigned int connect_delay; unsigned int connect_failure_count; struct timeout *to_reconnect; unsigned int no_reconnect:1; }; struct sql_result_vfuncs { void (*free)(struct sql_result *result); int (*next_row)(struct sql_result *result); unsigned int (*get_fields_count)(struct sql_result *result); const char *(*get_field_name)(struct sql_result *result, unsigned int idx); int (*find_field)(struct sql_result *result, const char *field_name); const char *(*get_field_value)(struct sql_result *result, unsigned int idx); const unsigned char * (*get_field_value_binary)(struct sql_result *result, unsigned int idx, size_t *size_r); const char *(*find_field_value)(struct sql_result *result, const char *field_name); const char *const *(*get_values)(struct sql_result *result); const char *(*get_error)(struct sql_result *result); void (*more)(struct sql_result **result, bool async, sql_query_callback_t *callback, void *context); }; struct sql_prepared_statement { struct sql_db *db; }; struct sql_statement { struct sql_db *db; pool_t pool; const char *query_template; ARRAY_TYPE(const_string) args; }; struct sql_field_map { enum sql_field_type type; size_t offset; }; struct sql_result { struct sql_result_vfuncs v; int refcount; struct sql_db *db; const struct sql_field_def *fields; unsigned int map_size; struct sql_field_map *map; void *fetch_dest; size_t fetch_dest_size; enum sql_result_error_type error_type; unsigned int failed:1; unsigned int failed_try_retry:1; unsigned int callback:1; }; struct sql_transaction_context { struct sql_db *db; /* commit() must use this query list if head is non-NULL. */ struct sql_transaction_query *head, *tail; }; ARRAY_DEFINE_TYPE(sql_drivers, const struct sql_db *); extern ARRAY_TYPE(sql_drivers) sql_drivers; extern struct sql_result sql_not_connected_result; struct sql_db * driver_sqlpool_init(const char *connect_string, const struct sql_db *driver); void sql_db_set_state(struct sql_db *db, enum sql_db_state state); void sql_transaction_add_query(struct sql_transaction_context *ctx, pool_t pool, const char *query, unsigned int *affected_rows); const char *sql_statement_get_query(struct sql_statement *stmt); #endif dovecot-2.2.33.2/src/lib-sql/sql-db-cache.c0000644000175000017500000000672113123174404015053 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "sql-api-private.h" #include "sql-db-cache.h" #define SQL_DB_CACHE_CONTEXT(obj) \ MODULE_CONTEXT(obj, sql_db_cache_module) struct sql_db_cache_context { union sql_db_module_context module_ctx; struct sql_db *prev, *next; /* These are set while refcount=0 */ struct sql_db_cache *cache; int refcount; char *key; void (*orig_deinit)(struct sql_db *db); }; struct sql_db_cache { HASH_TABLE(char *, struct sql_db *) dbs; unsigned int unused_count, max_unused_connections; struct sql_db *unused_tail, *unused_head; }; static MODULE_CONTEXT_DEFINE_INIT(sql_db_cache_module, &sql_db_module_register); static void sql_db_cache_db_deinit(struct sql_db *db) { struct sql_db_cache_context *ctx = SQL_DB_CACHE_CONTEXT(db); struct sql_db_cache_context *head_ctx; if (--ctx->refcount > 0) return; ctx->cache->unused_count++; if (ctx->cache->unused_tail == NULL) ctx->cache->unused_tail = db; else { head_ctx = SQL_DB_CACHE_CONTEXT(ctx->cache->unused_head); head_ctx->next = db; } ctx->prev = ctx->cache->unused_head; ctx->cache->unused_head = db; } static void sql_db_cache_unlink(struct sql_db_cache_context *ctx) { struct sql_db_cache_context *prev_ctx, *next_ctx; i_assert(ctx->refcount == 0); if (ctx->prev == NULL) ctx->cache->unused_tail = ctx->next; else { prev_ctx = SQL_DB_CACHE_CONTEXT(ctx->prev); prev_ctx->next = ctx->next; } if (ctx->next == NULL) ctx->cache->unused_head = ctx->prev; else { next_ctx = SQL_DB_CACHE_CONTEXT(ctx->next); next_ctx->prev = ctx->prev; } ctx->cache->unused_count--; } static void sql_db_cache_free_tail(struct sql_db_cache *cache) { struct sql_db *db; struct sql_db_cache_context *ctx; db = cache->unused_tail; ctx = SQL_DB_CACHE_CONTEXT(db); sql_db_cache_unlink(ctx); hash_table_remove(cache->dbs, ctx->key); i_free(ctx->key); ctx->orig_deinit(db); i_free(ctx); } static void sql_db_cache_drop_oldest(struct sql_db_cache *cache) { while (cache->unused_count >= cache->max_unused_connections) sql_db_cache_free_tail(cache); } struct sql_db * sql_db_cache_new(struct sql_db_cache *cache, const char *db_driver, const char *connect_string) { struct sql_db_cache_context *ctx; struct sql_db *db; char *key; key = i_strdup_printf("%s\t%s", db_driver, connect_string); db = hash_table_lookup(cache->dbs, key); if (db != NULL) { ctx = SQL_DB_CACHE_CONTEXT(db); if (ctx->refcount == 0) { sql_db_cache_unlink(ctx); ctx->prev = ctx->next = NULL; } i_free(key); } else { sql_db_cache_drop_oldest(cache); ctx = i_new(struct sql_db_cache_context, 1); ctx->cache = cache; ctx->key = key; db = sql_init(db_driver, connect_string); ctx->orig_deinit = db->v.deinit; db->v.deinit = sql_db_cache_db_deinit; MODULE_CONTEXT_SET(db, sql_db_cache_module, ctx); hash_table_insert(cache->dbs, ctx->key, db); } ctx->refcount++; return db; } struct sql_db_cache *sql_db_cache_init(unsigned int max_unused_connections) { struct sql_db_cache *cache; cache = i_new(struct sql_db_cache, 1); hash_table_create(&cache->dbs, default_pool, 0, str_hash, strcmp); cache->max_unused_connections = max_unused_connections; return cache; } void sql_db_cache_deinit(struct sql_db_cache **_cache) { struct sql_db_cache *cache = *_cache; *_cache = NULL; while (cache->unused_tail != NULL) sql_db_cache_free_tail(cache); hash_table_destroy(&cache->dbs); i_free(cache); } dovecot-2.2.33.2/src/lib-sql/driver-sqlpool.c0000644000175000017500000005366413165463624015635 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "ioloop.h" #include "sql-api-private.h" #include #define QUERY_TIMEOUT_SECS 6 struct sqlpool_host { char *connect_string; unsigned int connection_count; }; struct sqlpool_connection { struct sql_db *db; unsigned int host_idx; }; struct sqlpool_db { struct sql_db api; pool_t pool; const struct sql_db *driver; unsigned int connection_limit; ARRAY(struct sqlpool_host) hosts; /* all connections from all hosts */ ARRAY(struct sqlpool_connection) all_connections; /* index of last connection in all_connections that was used to send a query. */ unsigned int last_query_conn_idx; /* queued requests */ struct sqlpool_request *requests_head, *requests_tail; struct timeout *request_to; }; struct sqlpool_request { struct sqlpool_request *prev, *next; struct sqlpool_db *db; time_t created; unsigned int host_idx; unsigned int retry_count; /* requests are a) queries */ char *query; sql_query_callback_t *callback; void *context; /* b) transaction waiters */ struct sqlpool_transaction_context *trans; }; struct sqlpool_transaction_context { struct sql_transaction_context ctx; sql_commit_callback_t *callback; void *context; pool_t query_pool; struct sqlpool_request *commit_request; }; extern struct sql_db driver_sqlpool_db; static struct sqlpool_connection * sqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host, unsigned int host_idx); static void driver_sqlpool_query_callback(struct sql_result *result, struct sqlpool_request *request); static void driver_sqlpool_commit_callback(const char *error, struct sqlpool_transaction_context *ctx); static struct sqlpool_request * ATTR_NULL(2) sqlpool_request_new(struct sqlpool_db *db, const char *query) { struct sqlpool_request *request; request = i_new(struct sqlpool_request, 1); request->db = db; request->created = time(NULL); request->query = i_strdup(query); return request; } static void sqlpool_request_free(struct sqlpool_request **_request) { struct sqlpool_request *request = *_request; *_request = NULL; i_assert(request->prev == NULL && request->next == NULL); i_free(request->query); i_free(request); } static void sqlpool_request_abort(struct sqlpool_request **_request) { struct sqlpool_request *request = *_request; *_request = NULL; if (request->callback != NULL) request->callback(&sql_not_connected_result, request->context); i_assert(request->prev != NULL || request->db->requests_head == request); DLLIST2_REMOVE(&request->db->requests_head, &request->db->requests_tail, request); sqlpool_request_free(&request); } static struct sql_transaction_context * driver_sqlpool_new_conn_trans(struct sqlpool_transaction_context *trans, struct sql_db *conndb) { struct sql_transaction_context *conn_trans; struct sql_transaction_query *query; conn_trans = sql_transaction_begin(conndb); /* backend will use our queries list (we might still append more queries to the list) */ conn_trans->head = trans->ctx.head; conn_trans->tail = trans->ctx.tail; for (query = conn_trans->head; query != NULL; query = query->next) query->trans = conn_trans; return conn_trans; } static void sqlpool_request_handle_transaction(struct sql_db *conndb, struct sqlpool_transaction_context *trans) { struct sql_transaction_context *conn_trans; sqlpool_request_free(&trans->commit_request); conn_trans = driver_sqlpool_new_conn_trans(trans, conndb); sql_transaction_commit(&conn_trans, driver_sqlpool_commit_callback, trans); } static void sqlpool_request_send_next(struct sqlpool_db *db, struct sql_db *conndb) { struct sqlpool_request *request; if (db->requests_head == NULL || !SQL_DB_IS_READY(conndb)) return; request = db->requests_head; DLLIST2_REMOVE(&db->requests_head, &db->requests_tail, request); timeout_reset(db->request_to); if (request->query != NULL) { sql_query(conndb, request->query, driver_sqlpool_query_callback, request); } else if (request->trans != NULL) { sqlpool_request_handle_transaction(conndb, request->trans); } else { i_unreached(); } } static void sqlpool_reconnect(struct sql_db *conndb) { timeout_remove(&conndb->to_reconnect); (void)sql_connect(conndb); } static struct sqlpool_host * sqlpool_find_host_with_least_connections(struct sqlpool_db *db, unsigned int *host_idx_r) { struct sqlpool_host *hosts, *min = NULL; unsigned int i, count; hosts = array_get_modifiable(&db->hosts, &count); i_assert(count > 0); min = &hosts[0]; *host_idx_r = 0; for (i = 1; i < count; i++) { if (min->connection_count > hosts[i].connection_count) { min = &hosts[i]; *host_idx_r = i; } } return min; } static bool sqlpool_have_successful_connections(struct sqlpool_db *db) { const struct sqlpool_connection *conn; array_foreach(&db->all_connections, conn) { if (conn->db->state >= SQL_DB_STATE_IDLE) return TRUE; } return FALSE; } static void sqlpool_handle_connect_failed(struct sqlpool_db *db, struct sql_db *conndb) { struct sqlpool_host *host; unsigned int host_idx; if (conndb->connect_failure_count > 0) { /* increase delay between reconnections to this server */ conndb->connect_delay *= 5; if (conndb->connect_delay > SQL_CONNECT_MAX_DELAY) conndb->connect_delay = SQL_CONNECT_MAX_DELAY; } conndb->connect_failure_count++; /* reconnect after the delay */ if (conndb->to_reconnect != NULL) timeout_remove(&conndb->to_reconnect); conndb->to_reconnect = timeout_add(conndb->connect_delay * 1000, sqlpool_reconnect, conndb); /* if we have zero successful hosts and there still are hosts without connections, connect to one of them. */ if (!sqlpool_have_successful_connections(db)) { host = sqlpool_find_host_with_least_connections(db, &host_idx); if (host->connection_count == 0) (void)sqlpool_add_connection(db, host, host_idx); } } static void sqlpool_state_changed(struct sql_db *conndb, enum sql_db_state prev_state, void *context) { struct sqlpool_db *db = context; if (conndb->state == SQL_DB_STATE_IDLE) { conndb->connect_failure_count = 0; conndb->connect_delay = SQL_CONNECT_MIN_DELAY; sqlpool_request_send_next(db, conndb); } if (prev_state == SQL_DB_STATE_CONNECTING && conndb->state == SQL_DB_STATE_DISCONNECTED && !conndb->no_reconnect) sqlpool_handle_connect_failed(db, conndb); } static struct sqlpool_connection * sqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host, unsigned int host_idx) { struct sql_db *conndb; struct sqlpool_connection *conn; host->connection_count++; conndb = db->driver->v.init(host->connect_string); i_array_init(&conndb->module_contexts, 5); conndb->state_change_callback = sqlpool_state_changed; conndb->state_change_context = db; conndb->connect_delay = SQL_CONNECT_MIN_DELAY; conn = array_append_space(&db->all_connections); conn->host_idx = host_idx; conn->db = conndb; return conn; } static struct sqlpool_connection * sqlpool_add_new_connection(struct sqlpool_db *db) { struct sqlpool_host *host; unsigned int host_idx; host = sqlpool_find_host_with_least_connections(db, &host_idx); if (host->connection_count >= db->connection_limit) return NULL; else return sqlpool_add_connection(db, host, host_idx); } static const struct sqlpool_connection * sqlpool_find_available_connection(struct sqlpool_db *db, unsigned int unwanted_host_idx, bool *all_disconnected_r) { const struct sqlpool_connection *conns; unsigned int i, count; *all_disconnected_r = TRUE; conns = array_get(&db->all_connections, &count); for (i = 0; i < count; i++) { unsigned int idx = (i + db->last_query_conn_idx + 1) % count; struct sql_db *conndb = conns[idx].db; if (conns[idx].host_idx == unwanted_host_idx) continue; if (!SQL_DB_IS_READY(conndb) && conndb->to_reconnect == NULL) { /* see if we could reconnect to it immediately */ (void)sql_connect(conndb); } if (SQL_DB_IS_READY(conndb)) { db->last_query_conn_idx = idx; *all_disconnected_r = FALSE; return &conns[idx]; } if (conndb->state != SQL_DB_STATE_DISCONNECTED) *all_disconnected_r = FALSE; } return NULL; } static bool driver_sqlpool_get_connection(struct sqlpool_db *db, unsigned int unwanted_host_idx, const struct sqlpool_connection **conn_r) { const struct sqlpool_connection *conn, *conns; unsigned int i, count; bool all_disconnected; conn = sqlpool_find_available_connection(db, unwanted_host_idx, &all_disconnected); if (conn == NULL && unwanted_host_idx != UINT_MAX) { /* maybe there are no wanted hosts. use any of them. */ conn = sqlpool_find_available_connection(db, UINT_MAX, &all_disconnected); } if (conn == NULL && all_disconnected) { /* no connected connections. connect_delays may have gotten too high, reset all of them to see if some are still alive. */ conns = array_get(&db->all_connections, &count); for (i = 0; i < count; i++) { struct sql_db *conndb = conns[i].db; if (conndb->connect_delay > SQL_CONNECT_RESET_DELAY) conndb->connect_delay = SQL_CONNECT_RESET_DELAY; } conn = sqlpool_find_available_connection(db, UINT_MAX, &all_disconnected); } if (conn == NULL) { /* still nothing. try creating new connections */ conn = sqlpool_add_new_connection(db); if (conn != NULL) (void)sql_connect(conn->db); if (conn == NULL || !SQL_DB_IS_READY(conn->db)) return FALSE; } *conn_r = conn; return TRUE; } static bool driver_sqlpool_get_sync_connection(struct sqlpool_db *db, const struct sqlpool_connection **conn_r) { const struct sqlpool_connection *conns; unsigned int i, count; if (driver_sqlpool_get_connection(db, UINT_MAX, conn_r)) return TRUE; /* no idling connections, but maybe we can find one that's trying to connect to server, and we can use it once it's finished */ conns = array_get(&db->all_connections, &count); for (i = 0; i < count; i++) { if (conns[i].db->state == SQL_DB_STATE_CONNECTING) { *conn_r = &conns[i]; return TRUE; } } return FALSE; } static void driver_sqlpool_parse_hosts(struct sqlpool_db *db, const char *connect_string) { const char *const *args, *key, *value, *const *hostnamep; struct sqlpool_host *host; ARRAY_TYPE(const_string) hostnames, connect_args; t_array_init(&hostnames, 8); t_array_init(&connect_args, 32); /* connect string is a space separated list. it may contain backend-specific strings which we'll pass as-is. we'll only care about our own settings, plus the host settings. */ args = t_strsplit_spaces(connect_string, " "); for (; *args != NULL; args++) { value = strchr(*args, '='); if (value == NULL) { key = *args; value = ""; } else { key = t_strdup_until(*args, value); value++; } if (strcmp(key, "maxconns") == 0) { if (str_to_uint(value, &db->connection_limit) < 0) { i_fatal("Invalid value for maxconns: %s", value); } } else if (strcmp(key, "host") == 0) { array_append(&hostnames, &value, 1); } else { array_append(&connect_args, args, 1); } } /* build a new connect string without our settings or hosts */ array_append_zero(&connect_args); connect_string = t_strarray_join(array_idx(&connect_args, 0), " "); if (array_count(&hostnames) == 0) { /* no hosts specified. create a default one. */ host = array_append_space(&db->hosts); host->connect_string = i_strdup(connect_string); } else { if (*connect_string == '\0') connect_string = NULL; array_foreach(&hostnames, hostnamep) { host = array_append_space(&db->hosts); host->connect_string = i_strconcat("host=", *hostnamep, " ", connect_string, NULL); } } if (db->connection_limit == 0) db->connection_limit = SQL_DEFAULT_CONNECTION_LIMIT; } static void sqlpool_add_all_once(struct sqlpool_db *db) { struct sqlpool_host *host; unsigned int host_idx; for (;;) { host = sqlpool_find_host_with_least_connections(db, &host_idx); if (host->connection_count > 0) break; (void)sqlpool_add_connection(db, host, host_idx); } } struct sql_db * driver_sqlpool_init(const char *connect_string, const struct sql_db *driver) { struct sqlpool_db *db; i_assert(connect_string != NULL); db = i_new(struct sqlpool_db, 1); db->driver = driver; db->api = driver_sqlpool_db; db->api.flags = driver->flags; i_array_init(&db->hosts, 8); T_BEGIN { driver_sqlpool_parse_hosts(db, connect_string); } T_END; i_array_init(&db->all_connections, 16); /* connect to all databases so we can do load balancing immediately */ sqlpool_add_all_once(db); return &db->api; } static void driver_sqlpool_abort_requests(struct sqlpool_db *db) { while (db->requests_head != NULL) { struct sqlpool_request *request = db->requests_head; sqlpool_request_abort(&request); } if (db->request_to != NULL) timeout_remove(&db->request_to); } static void driver_sqlpool_deinit(struct sql_db *_db) { struct sqlpool_db *db = (struct sqlpool_db *)_db; struct sqlpool_host *host; struct sqlpool_connection *conn; array_foreach_modifiable(&db->all_connections, conn) sql_deinit(&conn->db); array_clear(&db->all_connections); driver_sqlpool_abort_requests(db); array_foreach_modifiable(&db->hosts, host) i_free(host->connect_string); i_assert(array_count(&db->all_connections) == 0); array_free(&db->hosts); array_free(&db->all_connections); array_free(&_db->module_contexts); i_free(db); } static int driver_sqlpool_connect(struct sql_db *_db) { struct sqlpool_db *db = (struct sqlpool_db *)_db; const struct sqlpool_connection *conn; int ret = -1, ret2; array_foreach(&db->all_connections, conn) { ret2 = conn->db->to_reconnect != NULL ? -1 : sql_connect(conn->db); if (ret2 > 0) ret = 1; else if (ret2 == 0 && ret < 0) ret = 0; } return ret; } static void driver_sqlpool_disconnect(struct sql_db *_db) { struct sqlpool_db *db = (struct sqlpool_db *)_db; const struct sqlpool_connection *conn; array_foreach(&db->all_connections, conn) sql_disconnect(conn->db); driver_sqlpool_abort_requests(db); } static const char * driver_sqlpool_escape_string(struct sql_db *_db, const char *string) { struct sqlpool_db *db = (struct sqlpool_db *)_db; const struct sqlpool_connection *conns; unsigned int i, count; /* use the first ready connection */ conns = array_get(&db->all_connections, &count); for (i = 0; i < count; i++) { if (SQL_DB_IS_READY(conns[i].db)) return sql_escape_string(conns[i].db, string); } /* no ready connections. just use the first one (we're guaranteed to always have one) */ return sql_escape_string(conns[0].db, string); } static void driver_sqlpool_timeout(struct sqlpool_db *db) { while (db->requests_head != NULL) { struct sqlpool_request *request = db->requests_head; if (request->created + SQL_QUERY_TIMEOUT_SECS > ioloop_time) break; i_error("%s: Query timed out " "(no free connections for %u secs): %s", db->driver->name, (unsigned int)(ioloop_time - request->created), request->query != NULL ? request->query : ""); sqlpool_request_abort(&request); } if (db->requests_head == NULL) timeout_remove(&db->request_to); } static void driver_sqlpool_prepend_request(struct sqlpool_db *db, struct sqlpool_request *request) { DLLIST2_PREPEND(&db->requests_head, &db->requests_tail, request); if (db->request_to == NULL) { db->request_to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000, driver_sqlpool_timeout, db); } } static void driver_sqlpool_append_request(struct sqlpool_db *db, struct sqlpool_request *request) { DLLIST2_APPEND(&db->requests_head, &db->requests_tail, request); if (db->request_to == NULL) { db->request_to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000, driver_sqlpool_timeout, db); } } static void driver_sqlpool_query_callback(struct sql_result *result, struct sqlpool_request *request) { struct sqlpool_db *db = request->db; const struct sqlpool_connection *conn = NULL; struct sql_db *conndb; if (result->failed_try_retry && request->retry_count < array_count(&db->hosts)) { i_warning("%s: Query failed, retrying: %s", db->driver->name, sql_result_get_error(result)); request->retry_count++; driver_sqlpool_prepend_request(db, request); if (driver_sqlpool_get_connection(request->db, request->host_idx, &conn)) { request->host_idx = conn->host_idx; sqlpool_request_send_next(db, conn->db); } } else { if (result->failed) { i_error("%s: Query failed, aborting: %s", db->driver->name, request->query); } conndb = result->db; if (request->callback != NULL) request->callback(result, request->context); sqlpool_request_free(&request); sqlpool_request_send_next(db, conndb); } } static void ATTR_NULL(3, 4) driver_sqlpool_query(struct sql_db *_db, const char *query, sql_query_callback_t *callback, void *context) { struct sqlpool_db *db = (struct sqlpool_db *)_db; struct sqlpool_request *request; const struct sqlpool_connection *conn; request = sqlpool_request_new(db, query); request->callback = callback; request->context = context; if (!driver_sqlpool_get_connection(db, UINT_MAX, &conn)) driver_sqlpool_append_request(db, request); else { request->host_idx = conn->host_idx; sql_query(conn->db, query, driver_sqlpool_query_callback, request); } } static void driver_sqlpool_exec(struct sql_db *_db, const char *query) { driver_sqlpool_query(_db, query, NULL, NULL); } static struct sql_result * driver_sqlpool_query_s(struct sql_db *_db, const char *query) { struct sqlpool_db *db = (struct sqlpool_db *)_db; const struct sqlpool_connection *conn; struct sql_result *result; if (!driver_sqlpool_get_sync_connection(db, &conn)) { sql_not_connected_result.refcount++; return &sql_not_connected_result; } result = sql_query_s(conn->db, query); if (result->failed_try_retry) { if (!driver_sqlpool_get_sync_connection(db, &conn)) return result; sql_result_unref(result); result = sql_query_s(conn->db, query); } return result; } static struct sql_transaction_context * driver_sqlpool_transaction_begin(struct sql_db *_db) { struct sqlpool_transaction_context *ctx; ctx = i_new(struct sqlpool_transaction_context, 1); ctx->ctx.db = _db; /* queue changes until commit. even if we did have a free connection now, don't use it or multiple open transactions could tie up all connections. */ ctx->query_pool = pool_alloconly_create("sqlpool transaction", 1024); return &ctx->ctx; } static void driver_sqlpool_transaction_free(struct sqlpool_transaction_context *ctx) { if (ctx->commit_request != NULL) sqlpool_request_abort(&ctx->commit_request); if (ctx->query_pool != NULL) pool_unref(&ctx->query_pool); i_free(ctx); } static void driver_sqlpool_commit_callback(const char *error, struct sqlpool_transaction_context *ctx) { ctx->callback(error, ctx->context); driver_sqlpool_transaction_free(ctx); } static void driver_sqlpool_transaction_commit(struct sql_transaction_context *_ctx, sql_commit_callback_t *callback, void *context) { struct sqlpool_transaction_context *ctx = (struct sqlpool_transaction_context *)_ctx; struct sqlpool_db *db = (struct sqlpool_db *)_ctx->db; const struct sqlpool_connection *conn; ctx->callback = callback; ctx->context = context; ctx->commit_request = sqlpool_request_new(db, NULL); ctx->commit_request->trans = ctx; if (driver_sqlpool_get_connection(db, UINT_MAX, &conn)) sqlpool_request_handle_transaction(conn->db, ctx); else driver_sqlpool_append_request(db, ctx->commit_request); } static int driver_sqlpool_transaction_commit_s(struct sql_transaction_context *_ctx, const char **error_r) { struct sqlpool_transaction_context *ctx = (struct sqlpool_transaction_context *)_ctx; struct sqlpool_db *db = (struct sqlpool_db *)_ctx->db; const struct sqlpool_connection *conn; struct sql_transaction_context *conn_trans; int ret; *error_r = NULL; if (!driver_sqlpool_get_sync_connection(db, &conn)) { *error_r = SQL_ERRSTR_NOT_CONNECTED; driver_sqlpool_transaction_free(ctx); return -1; } conn_trans = driver_sqlpool_new_conn_trans(ctx, conn->db); ret = sql_transaction_commit_s(&conn_trans, error_r); driver_sqlpool_transaction_free(ctx); return ret; } static void driver_sqlpool_transaction_rollback(struct sql_transaction_context *_ctx) { struct sqlpool_transaction_context *ctx = (struct sqlpool_transaction_context *)_ctx; driver_sqlpool_transaction_free(ctx); } static void driver_sqlpool_update(struct sql_transaction_context *_ctx, const char *query, unsigned int *affected_rows) { struct sqlpool_transaction_context *ctx = (struct sqlpool_transaction_context *)_ctx; /* we didn't get a connection for transaction immediately. queue updates until commit transfers all of these */ sql_transaction_add_query(&ctx->ctx, ctx->query_pool, query, affected_rows); } static const char * driver_sqlpool_escape_blob(struct sql_db *_db, const unsigned char *data, size_t size) { struct sqlpool_db *db = (struct sqlpool_db *)_db; const struct sqlpool_connection *conns; unsigned int i, count; /* use the first ready connection */ conns = array_get(&db->all_connections, &count); for (i = 0; i < count; i++) { if (SQL_DB_IS_READY(conns[i].db)) return sql_escape_blob(conns[i].db, data, size); } /* no ready connections. just use the first one (we're guaranteed to always have one) */ return sql_escape_blob(conns[0].db, data, size); } struct sql_db driver_sqlpool_db = { "", .v = { .deinit = driver_sqlpool_deinit, .connect = driver_sqlpool_connect, .disconnect = driver_sqlpool_disconnect, .escape_string = driver_sqlpool_escape_string, .exec = driver_sqlpool_exec, .query = driver_sqlpool_query, .query_s = driver_sqlpool_query_s, .transaction_begin = driver_sqlpool_transaction_begin, .transaction_commit = driver_sqlpool_transaction_commit, .transaction_commit_s = driver_sqlpool_transaction_commit_s, .transaction_rollback = driver_sqlpool_transaction_rollback, .update = driver_sqlpool_update, .escape_blob = driver_sqlpool_escape_blob, } }; dovecot-2.2.33.2/src/lib-sql/sql-db-cache.h0000644000175000017500000000057013123174404015054 00000000000000#ifndef SQL_DB_CACHE_H #define SQL_DB_CACHE_H struct sql_db_cache; /* Like sql_init(), but use a connection pool. */ struct sql_db * sql_db_cache_new(struct sql_db_cache *cache, const char *db_driver, const char *connect_string); struct sql_db_cache *sql_db_cache_init(unsigned int max_unused_connections); void sql_db_cache_deinit(struct sql_db_cache **cache); #endif dovecot-2.2.33.2/src/lib-sql/driver-mysql.c0000644000175000017500000004562113165463624015303 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "hex-binary.h" #include "str.h" #include "net.h" #include "sql-api-private.h" #ifdef BUILD_MYSQL #include #include #ifdef HAVE_ATTR_NULL /* ugly way to tell clang that mysql.h is a system header and we don't want to enable nonnull attributes for it by default.. */ # 4 "driver-mysql.c" 3 #endif #include #ifdef HAVE_ATTR_NULL # 4 "driver-mysql.c" 3 # line 20 #endif #include #define MYSQL_DEFAULT_READ_TIMEOUT_SECS 30 #define MYSQL_DEFAULT_WRITE_TIMEOUT_SECS 30 struct mysql_db { struct sql_db api; pool_t pool; const char *user, *password, *dbname, *host, *unix_socket; const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher; int ssl_verify_server_cert; const char *option_file, *option_group; in_port_t port; unsigned int client_flags; unsigned int connect_timeout, read_timeout, write_timeout; time_t last_success; MYSQL *mysql; unsigned int next_query_connection; unsigned int ssl_set:1; }; struct mysql_result { struct sql_result api; MYSQL_RES *result; MYSQL_ROW row; MYSQL_FIELD *fields; unsigned int fields_count; my_ulonglong affected_rows; }; struct mysql_transaction_context { struct sql_transaction_context ctx; pool_t query_pool; const char *error; unsigned int failed:1; }; extern const struct sql_db driver_mysql_db; extern const struct sql_result driver_mysql_result; extern const struct sql_result driver_mysql_error_result; static const char *mysql_prefix(struct mysql_db *db) { return db->host == NULL ? "mysql" : t_strdup_printf("mysql(%s)", db->host); } static int driver_mysql_connect(struct sql_db *_db) { struct mysql_db *db = (struct mysql_db *)_db; const char *unix_socket, *host; unsigned long client_flags = db->client_flags; unsigned int secs_used; time_t start_time; bool failed; i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED); sql_db_set_state(&db->api, SQL_DB_STATE_CONNECTING); if (db->host == NULL) { /* assume option_file overrides the host, or if not we'll just connect to localhost */ unix_socket = NULL; host = NULL; } else if (*db->host == '/') { unix_socket = db->host; host = NULL; } else { unix_socket = NULL; host = db->host; } if (db->option_file != NULL) { mysql_options(db->mysql, MYSQL_READ_DEFAULT_FILE, db->option_file); } mysql_options(db->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &db->connect_timeout); mysql_options(db->mysql, MYSQL_OPT_READ_TIMEOUT, &db->read_timeout); mysql_options(db->mysql, MYSQL_OPT_WRITE_TIMEOUT, &db->write_timeout); mysql_options(db->mysql, MYSQL_READ_DEFAULT_GROUP, db->option_group != NULL ? db->option_group : "client"); if (!db->ssl_set && (db->ssl_ca != NULL || db->ssl_ca_path != NULL)) { #ifdef HAVE_MYSQL_SSL mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert, db->ssl_ca, db->ssl_ca_path #ifdef HAVE_MYSQL_SSL_CIPHER , db->ssl_cipher #endif ); #ifdef HAVE_MYSQL_SSL_VERIFY_SERVER_CERT mysql_options(db->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (void *)&db->ssl_verify_server_cert); #endif db->ssl_set = TRUE; #else i_fatal("mysql: SSL support not compiled in " "(remove ssl_ca and ssl_ca_path settings)"); #endif } #ifdef CLIENT_MULTI_RESULTS client_flags |= CLIENT_MULTI_RESULTS; #endif /* CLIENT_MULTI_RESULTS allows the use of stored procedures */ start_time = time(NULL); failed = mysql_real_connect(db->mysql, host, db->user, db->password, db->dbname, db->port, unix_socket, client_flags) == NULL; secs_used = time(NULL) - start_time; if (failed) { /* connecting could have taken a while. make sure that any timeouts that get added soon will get a refreshed timestamp. */ io_loop_time_refresh(); if (db->api.connect_delay < secs_used) db->api.connect_delay = secs_used; sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED); i_error("%s: Connect failed to database (%s): %s - " "waiting for %u seconds before retry", mysql_prefix(db), db->dbname, mysql_error(db->mysql), db->api.connect_delay); return -1; } else { db->last_success = ioloop_time; sql_db_set_state(&db->api, SQL_DB_STATE_IDLE); return 1; } } static void driver_mysql_disconnect(struct sql_db *_db ATTR_UNUSED) { } static void driver_mysql_parse_connect_string(struct mysql_db *db, const char *connect_string) { const char *const *args, *name, *value; const char **field; db->ssl_cipher = "HIGH"; db->ssl_verify_server_cert = 0; /* FIXME: change to 1 for v2.3 */ db->connect_timeout = SQL_CONNECT_TIMEOUT_SECS; db->read_timeout = MYSQL_DEFAULT_READ_TIMEOUT_SECS; db->write_timeout = MYSQL_DEFAULT_WRITE_TIMEOUT_SECS; args = t_strsplit_spaces(connect_string, " "); for (; *args != NULL; args++) { value = strchr(*args, '='); if (value == NULL) { i_fatal("mysql: Missing value in connect string: %s", *args); } name = t_strdup_until(*args, value); value++; field = NULL; if (strcmp(name, "host") == 0 || strcmp(name, "hostaddr") == 0) field = &db->host; else if (strcmp(name, "user") == 0) field = &db->user; else if (strcmp(name, "password") == 0) field = &db->password; else if (strcmp(name, "dbname") == 0) field = &db->dbname; else if (strcmp(name, "port") == 0) { if (net_str2port(value, &db->port) < 0) i_fatal("mysql: Invalid port number: %s", value); } else if (strcmp(name, "client_flags") == 0) { if (str_to_uint(value, &db->client_flags) < 0) i_fatal("mysql: Invalid client flags: %s", value); } else if (strcmp(name, "connect_timeout") == 0) { if (str_to_uint(value, &db->connect_timeout) < 0) i_fatal("mysql: Invalid read_timeout: %s", value); } else if (strcmp(name, "read_timeout") == 0) { if (str_to_uint(value, &db->read_timeout) < 0) i_fatal("mysql: Invalid read_timeout: %s", value); } else if (strcmp(name, "write_timeout") == 0) { if (str_to_uint(value, &db->write_timeout) < 0) i_fatal("mysql: Invalid read_timeout: %s", value); } else if (strcmp(name, "ssl_cert") == 0) field = &db->ssl_cert; else if (strcmp(name, "ssl_key") == 0) field = &db->ssl_key; else if (strcmp(name, "ssl_ca") == 0) field = &db->ssl_ca; else if (strcmp(name, "ssl_ca_path") == 0) field = &db->ssl_ca_path; else if (strcmp(name, "ssl_cipher") == 0) field = &db->ssl_cipher; else if (strcmp(name, "ssl_verify_server_cert") == 0) { if (strcmp(value, "yes") == 0) db->ssl_verify_server_cert = 1; else if (strcmp(value, "no") == 0) db->ssl_verify_server_cert = 0; else i_fatal("mysql: Invalid boolean: %s", value); } else if (strcmp(name, "option_file") == 0) field = &db->option_file; else if (strcmp(name, "option_group") == 0) field = &db->option_group; else i_fatal("mysql: Unknown connect string: %s", name); if (field != NULL) *field = p_strdup(db->pool, value); } if (db->host == NULL && db->option_file == NULL) i_fatal("mysql: No hosts given in connect string"); db->mysql = mysql_init(NULL); if (db->mysql == NULL) i_fatal("mysql_init() failed"); } static struct sql_db *driver_mysql_init_v(const char *connect_string) { struct mysql_db *db; pool_t pool; pool = pool_alloconly_create("mysql driver", 1024); db = p_new(pool, struct mysql_db, 1); db->pool = pool; db->api = driver_mysql_db; T_BEGIN { driver_mysql_parse_connect_string(db, connect_string); } T_END; return &db->api; } static void driver_mysql_deinit_v(struct sql_db *_db) { struct mysql_db *db = (struct mysql_db *)_db; _db->no_reconnect = TRUE; sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED); mysql_close(db->mysql); array_free(&_db->module_contexts); pool_unref(&db->pool); } static int driver_mysql_do_query(struct mysql_db *db, const char *query) { if (mysql_query(db->mysql, query) == 0) return 0; /* failed */ switch (mysql_errno(db->mysql)) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED); break; default: break; } return -1; } static const char * driver_mysql_escape_string(struct sql_db *_db, const char *string) { struct mysql_db *db = (struct mysql_db *)_db; size_t len = strlen(string); char *to; if (_db->state == SQL_DB_STATE_DISCONNECTED) { /* try connecting */ (void)sql_connect(&db->api); } if (db->mysql == NULL) { /* FIXME: we don't have a valid connection, so fallback to using default escaping. the next query will most likely fail anyway so it shouldn't matter that much what we return here.. Anyway, this API needs changing so that the escaping function could already fail the query reliably. */ to = t_buffer_get(len * 2 + 1); len = mysql_escape_string(to, string, len); t_buffer_alloc(len + 1); return to; } to = t_buffer_get(len * 2 + 1); len = mysql_real_escape_string(db->mysql, to, string, len); t_buffer_alloc(len + 1); return to; } static void driver_mysql_exec(struct sql_db *_db, const char *query) { struct mysql_db *db = (struct mysql_db *)_db; if (driver_mysql_do_query(db, query) < 0) { i_error("%s: Query '%s' failed: %s", mysql_prefix(db), query, mysql_error(db->mysql)); } } static void driver_mysql_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { struct sql_result *result; result = sql_query_s(db, query); result->callback = TRUE; callback(result, context); result->callback = FALSE; sql_result_unref(result); } static struct sql_result * driver_mysql_query_s(struct sql_db *_db, const char *query) { struct mysql_db *db = (struct mysql_db *)_db; struct mysql_result *result; int ret; result = i_new(struct mysql_result, 1); result->api = driver_mysql_result; if (driver_mysql_do_query(db, query) < 0) result->api = driver_mysql_error_result; else { /* query ok */ result->affected_rows = mysql_affected_rows(db->mysql); result->result = mysql_store_result(db->mysql); #ifdef CLIENT_MULTI_RESULTS /* Because we've enabled CLIENT_MULTI_RESULTS, we need to read (ignore) extra results - there should not be any. ret is: -1 = done, >0 = error, 0 = more results. */ while ((ret = mysql_next_result(db->mysql)) == 0) ; #else ret = -1; #endif if (ret < 0 && (result->result != NULL || mysql_errno(db->mysql) == 0)) { /* ok */ } else { /* failed */ if (result->result != NULL) mysql_free_result(result->result); result->api = driver_mysql_error_result; } } result->api.db = _db; result->api.refcount = 1; return &result->api; } static void driver_mysql_result_free(struct sql_result *_result) { struct mysql_result *result = (struct mysql_result *)_result; i_assert(_result != &sql_not_connected_result); if (_result->callback) return; if (result->result != NULL) mysql_free_result(result->result); i_free(result); } static int driver_mysql_result_next_row(struct sql_result *_result) { struct mysql_result *result = (struct mysql_result *)_result; struct mysql_db *db = (struct mysql_db *)_result->db; int ret; if (result->result == NULL) { /* no results */ return 0; } result->row = mysql_fetch_row(result->result); if (result->row != NULL) ret = 1; else { if (mysql_errno(db->mysql) != 0) return -1; ret = 0; } db->last_success = ioloop_time; return ret; } static void driver_mysql_result_fetch_fields(struct mysql_result *result) { if (result->fields != NULL) return; result->fields_count = mysql_num_fields(result->result); result->fields = mysql_fetch_fields(result->result); } static unsigned int driver_mysql_result_get_fields_count(struct sql_result *_result) { struct mysql_result *result = (struct mysql_result *)_result; driver_mysql_result_fetch_fields(result); return result->fields_count; } static const char * driver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx) { struct mysql_result *result = (struct mysql_result *)_result; driver_mysql_result_fetch_fields(result); i_assert(idx < result->fields_count); return result->fields[idx].name; } static int driver_mysql_result_find_field(struct sql_result *_result, const char *field_name) { struct mysql_result *result = (struct mysql_result *)_result; unsigned int i; driver_mysql_result_fetch_fields(result); for (i = 0; i < result->fields_count; i++) { if (strcmp(result->fields[i].name, field_name) == 0) return i; } return -1; } static const char * driver_mysql_result_get_field_value(struct sql_result *_result, unsigned int idx) { struct mysql_result *result = (struct mysql_result *)_result; return (const char *)result->row[idx]; } static const unsigned char * driver_mysql_result_get_field_value_binary(struct sql_result *_result, unsigned int idx, size_t *size_r) { struct mysql_result *result = (struct mysql_result *)_result; unsigned long *lengths; lengths = mysql_fetch_lengths(result->result); *size_r = lengths[idx]; return (const void *)result->row[idx]; } static const char * driver_mysql_result_find_field_value(struct sql_result *result, const char *field_name) { int idx; idx = driver_mysql_result_find_field(result, field_name); if (idx < 0) return NULL; return driver_mysql_result_get_field_value(result, idx); } static const char *const * driver_mysql_result_get_values(struct sql_result *_result) { struct mysql_result *result = (struct mysql_result *)_result; return (const char *const *)result->row; } static const char *driver_mysql_result_get_error(struct sql_result *_result) { struct mysql_db *db = (struct mysql_db *)_result->db; const char *errstr; unsigned int idle_time; int err; err = mysql_errno(db->mysql); errstr = mysql_error(db->mysql); if ((err == CR_SERVER_GONE_ERROR || err == CR_SERVER_LOST) && db->last_success != 0) { idle_time = ioloop_time - db->last_success; errstr = t_strdup_printf("%s (idled for %u secs)", errstr, idle_time); } return errstr; } static struct sql_transaction_context * driver_mysql_transaction_begin(struct sql_db *db) { struct mysql_transaction_context *ctx; ctx = i_new(struct mysql_transaction_context, 1); ctx->ctx.db = db; ctx->query_pool = pool_alloconly_create("mysql transaction", 1024); return &ctx->ctx; } static void driver_mysql_transaction_commit(struct sql_transaction_context *ctx, sql_commit_callback_t *callback, void *context) { const char *error; if (sql_transaction_commit_s(&ctx, &error) < 0) callback(error, context); else callback(NULL, context); } static int ATTR_NULL(3) transaction_send_query(struct mysql_transaction_context *ctx, const char *query, unsigned int *affected_rows_r) { struct sql_result *_result; int ret = 0; if (ctx->failed) return -1; _result = sql_query_s(ctx->ctx.db, query); if (sql_result_next_row(_result) < 0) { ctx->error = sql_result_get_error(_result); ctx->failed = TRUE; ret = -1; } else if (affected_rows_r != NULL) { struct mysql_result *result = (struct mysql_result *)_result; i_assert(result->affected_rows != (my_ulonglong)-1); *affected_rows_r = result->affected_rows; } sql_result_unref(_result); return ret; } static int driver_mysql_try_commit_s(struct mysql_transaction_context *ctx) { struct sql_transaction_context *_ctx = &ctx->ctx; /* try to use a transaction in any case, even if it's not actually functional. */ if (transaction_send_query(ctx, "BEGIN", NULL) < 0) { if (_ctx->db->state != SQL_DB_STATE_DISCONNECTED) return -1; /* we got disconnected, retry */ return 0; } while (_ctx->head != NULL) { if (transaction_send_query(ctx, _ctx->head->query, _ctx->head->affected_rows) < 0) return -1; _ctx->head = _ctx->head->next; } if (transaction_send_query(ctx, "COMMIT", NULL) < 0) return -1; return 1; } static int driver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx, const char **error_r) { struct mysql_transaction_context *ctx = (struct mysql_transaction_context *)_ctx; struct mysql_db *db = (struct mysql_db *)_ctx->db; int ret = 1; *error_r = NULL; if (_ctx->head != NULL) { ret = driver_mysql_try_commit_s(ctx); *error_r = t_strdup(ctx->error); if (ret == 0) { i_info("%s: Disconnected from database, " "retrying commit", db->dbname); if (sql_connect(_ctx->db) >= 0) { ctx->failed = FALSE; ret = driver_mysql_try_commit_s(ctx); } } } sql_transaction_rollback(&_ctx); return ret <= 0 ? -1 : 0; } static void driver_mysql_transaction_rollback(struct sql_transaction_context *_ctx) { struct mysql_transaction_context *ctx = (struct mysql_transaction_context *)_ctx; pool_unref(&ctx->query_pool); i_free(ctx); } static void driver_mysql_update(struct sql_transaction_context *_ctx, const char *query, unsigned int *affected_rows) { struct mysql_transaction_context *ctx = (struct mysql_transaction_context *)_ctx; sql_transaction_add_query(&ctx->ctx, ctx->query_pool, query, affected_rows); } static const char * driver_mysql_escape_blob(struct sql_db *_db ATTR_UNUSED, const unsigned char *data, size_t size) { string_t *str = t_str_new(128); str_append(str, "X'"); binary_to_hex_append(str, data, size); str_append_c(str, '\''); return str_c(str); } const struct sql_db driver_mysql_db = { .name = "mysql", .flags = SQL_DB_FLAG_BLOCKING | SQL_DB_FLAG_POOLED, .v = { .init = driver_mysql_init_v, .deinit = driver_mysql_deinit_v, .connect = driver_mysql_connect, .disconnect = driver_mysql_disconnect, .escape_string = driver_mysql_escape_string, .exec = driver_mysql_exec, .query = driver_mysql_query, .query_s = driver_mysql_query_s, .transaction_begin = driver_mysql_transaction_begin, .transaction_commit = driver_mysql_transaction_commit, .transaction_commit_s = driver_mysql_transaction_commit_s, .transaction_rollback = driver_mysql_transaction_rollback, .update = driver_mysql_update, .escape_blob = driver_mysql_escape_blob, } }; const struct sql_result driver_mysql_result = { .v = { .free = driver_mysql_result_free, .next_row = driver_mysql_result_next_row, .get_fields_count = driver_mysql_result_get_fields_count, .get_field_name = driver_mysql_result_get_field_name, .find_field = driver_mysql_result_find_field, .get_field_value = driver_mysql_result_get_field_value, .get_field_value_binary = driver_mysql_result_get_field_value_binary, .find_field_value = driver_mysql_result_find_field_value, .get_values = driver_mysql_result_get_values, .get_error = driver_mysql_result_get_error, } }; static int driver_mysql_result_error_next_row(struct sql_result *result ATTR_UNUSED) { return -1; } const struct sql_result driver_mysql_error_result = { .v = { .free = driver_mysql_result_free, .next_row = driver_mysql_result_error_next_row, .get_error = driver_mysql_result_get_error, }, .failed_try_retry = TRUE }; const char *driver_mysql_version = DOVECOT_ABI_VERSION; void driver_mysql_init(void); void driver_mysql_deinit(void); void driver_mysql_init(void) { sql_driver_register(&driver_mysql_db); } void driver_mysql_deinit(void) { sql_driver_unregister(&driver_mysql_db); } #endif dovecot-2.2.33.2/src/lib-sql/sql-api.h0000644000175000017500000002224013165463624014210 00000000000000#ifndef SQL_API_H #define SQL_API_H /* This SQL API is designed to work asynchronously. The underlying drivers however may not. */ enum sql_db_flags { /* Set if queries are not executed asynchronously */ SQL_DB_FLAG_BLOCKING = 0x01, /* Set if database wants to use connection pooling */ SQL_DB_FLAG_POOLED = 0x02, /* Prepared statements are supported by the database. If they aren't, the functions can still be used, but they're just internally convered into regular statements. */ SQL_DB_FLAG_PREP_STATEMENTS = 0x04, }; enum sql_field_type { SQL_TYPE_STR, SQL_TYPE_UINT, SQL_TYPE_ULLONG, SQL_TYPE_BOOL }; struct sql_field_def { enum sql_field_type type; const char *name; size_t offset; }; enum sql_result_error_type { SQL_RESULT_ERROR_TYPE_UNKNOWN = 0, /* It's unknown whether write succeeded or not. This could be due to a timeout or a disconnection from server. */ SQL_RESULT_ERROR_TYPE_WRITE_UNCERTAIN }; enum sql_result_next { /* Row was returned */ SQL_RESULT_NEXT_OK = 1, /* There are no more rows */ SQL_RESULT_NEXT_LAST = 0, /* Error occurred - see sql_result_get_error*() */ SQL_RESULT_NEXT_ERROR = -1, /* There are more results - call sql_result_more() */ SQL_RESULT_NEXT_MORE = -99 }; #define SQL_DEF_STRUCT(name, struct_name, type, c_type) \ { (type) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, c_type), \ #name, offsetof(struct struct_name, name) } #define SQL_DEF_STRUCT_STR(name, struct_name) \ SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_STR, const char *) #define SQL_DEF_STRUCT_UINT(name, struct_name) \ SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_UINT, unsigned int) #define SQL_DEF_STRUCT_ULLONG(name, struct_name) \ SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_ULLONG, unsigned long long) #define SQL_DEF_STRUCT_BOOL(name, struct_name) \ SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_BOOL, bool) struct sql_db; struct sql_result; struct sql_commit_result { const char *error; enum sql_result_error_type error_type; }; typedef void sql_query_callback_t(struct sql_result *result, void *context); typedef void sql_commit_callback_t(const char *error, void *context); typedef void sql_commit2_callback_t(const struct sql_commit_result *result, void *context); void sql_drivers_init(void); void sql_drivers_deinit(void); /* register all built-in SQL drivers */ void sql_drivers_register_all(void); void sql_driver_register(const struct sql_db *driver); void sql_driver_unregister(const struct sql_db *driver); /* Initialize database connections. db_driver is the database driver name, eg. "mysql" or "pgsql". connect_string is driver-specific. */ struct sql_db *sql_init(const char *db_driver, const char *connect_string); void sql_deinit(struct sql_db **db); /* Returns SQL database state flags. */ enum sql_db_flags sql_get_flags(struct sql_db *db); /* Explicitly connect to the database. It's not required to call this function though. Returns -1 if we're not connected, 0 if we started connecting or 1 if we are fully connected now. */ int sql_connect(struct sql_db *db); /* Explicitly disconnect from database and abort pending auth requests. */ void sql_disconnect(struct sql_db *db); /* Escape the given string if needed and return it. */ const char *sql_escape_string(struct sql_db *db, const char *string); /* Escape the given data as a string. */ const char *sql_escape_blob(struct sql_db *db, const unsigned char *data, size_t size); /* Execute SQL query without waiting for results. */ void sql_exec(struct sql_db *db, const char *query); /* Execute SQL query and return result in callback. If fields list is given, the returned fields are validated to be of correct type, and you can use sql_result_next_row_get() */ void sql_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context); #define sql_query(db, query, callback, context) \ sql_query(db, query + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct sql_result *, typeof(context))), \ (sql_query_callback_t *)callback, context) /* Execute blocking SQL query and return result. */ struct sql_result *sql_query_s(struct sql_db *db, const char *query); struct sql_prepared_statement * sql_prepared_statement_init(struct sql_db *db, const char *query_template); void sql_prepared_statement_deinit(struct sql_prepared_statement **prep_stmt); struct sql_statement * sql_statement_init(struct sql_db *db, const char *query_template); struct sql_statement * sql_statement_init_prepared(struct sql_prepared_statement *prep_stmt); void sql_statement_abort(struct sql_statement **stmt); void sql_statement_set_timestamp(struct sql_statement *stmt, const struct timespec *ts); void sql_statement_bind_str(struct sql_statement *stmt, unsigned int column_idx, const char *value); void sql_statement_bind_binary(struct sql_statement *stmt, unsigned int column_idx, const void *value, size_t value_size); void sql_statement_bind_int64(struct sql_statement *stmt, unsigned int column_idx, int64_t value); void sql_statement_query(struct sql_statement **stmt, sql_query_callback_t *callback, void *context); #define sql_statement_query(stmt, callback, context) \ sql_statement_query(stmt, \ (sql_query_callback_t *)callback, context + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct sql_result *, typeof(context)))) struct sql_result *sql_statement_query_s(struct sql_statement **stmt); void sql_result_setup_fetch(struct sql_result *result, const struct sql_field_def *fields, void *dest, size_t dest_size); /* Go to next row. See enum sql_result_next. */ int sql_result_next_row(struct sql_result *result); /* If sql_result_next_row() returned SQL_RESULT_NEXT_MORE, this can be called to continue returning more results. The result is freed with this call, so it must not be accesed anymore until the callback is finished. */ void sql_result_more(struct sql_result **result, sql_query_callback_t *callback, void *context); #define sql_result_more(result, callback, context) \ sql_result_more(result + \ CALLBACK_TYPECHECK(callback, void (*)( \ struct sql_result *, typeof(context))), \ (sql_query_callback_t *)callback, context) /* Synchronous version of sql_result_more(). The result will be replaced with the new result. */ void sql_result_more_s(struct sql_result **result); void sql_result_ref(struct sql_result *result); /* Needs to be called only with sql_query_s() or when result has been explicitly referenced. */ void sql_result_unref(struct sql_result *result); /* Return number of fields in result. */ unsigned int sql_result_get_fields_count(struct sql_result *result); /* Return name of the given field index. */ const char *sql_result_get_field_name(struct sql_result *result, unsigned int idx); /* Return field index for given name, or -1 if not found. */ int sql_result_find_field(struct sql_result *result, const char *field_name); /* Returns value of given field as string. */ const char *sql_result_get_field_value(struct sql_result *result, unsigned int idx); const unsigned char * sql_result_get_field_value_binary(struct sql_result *result, unsigned int idx, size_t *size_r); const char *sql_result_find_field_value(struct sql_result *result, const char *field_name); /* Return all values of current row. */ const char *const *sql_result_get_values(struct sql_result *result); /* Return last error message in result. */ const char *sql_result_get_error(struct sql_result *result); enum sql_result_error_type sql_result_get_error_type(struct sql_result *result); /* Begin a new transaction. Currently you're limited to only one open transaction at a time. */ struct sql_transaction_context *sql_transaction_begin(struct sql_db *db); /* Commit transaction. */ void sql_transaction_commit(struct sql_transaction_context **ctx, sql_commit_callback_t *callback, void *context); #define sql_transaction_commit(ctx, callback, context) \ sql_transaction_commit(ctx + \ CALLBACK_TYPECHECK(callback, void (*)( \ const char *, typeof(context))), \ (sql_commit_callback_t *)callback, context) void sql_transaction_commit2(struct sql_transaction_context **ctx, sql_commit2_callback_t *callback, void *context); #define sql_transaction_commit2(ctx, callback, context) \ sql_transaction_commit2(ctx + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct sql_commit_result *, typeof(context))), \ (sql_commit2_callback_t *)callback, context) /* Synchronous commit. Returns 0 if ok, -1 if error. */ int sql_transaction_commit_s(struct sql_transaction_context **ctx, const char **error_r); void sql_transaction_rollback(struct sql_transaction_context **ctx); /* Execute query in given transaction. */ void sql_update(struct sql_transaction_context *ctx, const char *query); void sql_update_stmt(struct sql_transaction_context *ctx, struct sql_statement **stmt); /* Save the number of rows updated by this query. The value is set before commit callback is called. */ void sql_update_get_rows(struct sql_transaction_context *ctx, const char *query, unsigned int *affected_rows); void sql_update_stmt_get_rows(struct sql_transaction_context *ctx, struct sql_statement **stmt, unsigned int *affected_rows); #endif dovecot-2.2.33.2/src/lib-sql/sql-api.c0000644000175000017500000004424713165463624014216 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "str.h" #include "sql-api-private.h" #include struct default_sql_prepared_statement { struct sql_prepared_statement prep_stmt; char *query_template; }; struct sql_db_module_register sql_db_module_register = { 0 }; ARRAY_TYPE(sql_drivers) sql_drivers; void sql_drivers_init(void) { i_array_init(&sql_drivers, 8); } void sql_drivers_deinit(void) { array_free(&sql_drivers); } static const struct sql_db *sql_driver_lookup(const char *name) { const struct sql_db *const *drivers; unsigned int i, count; drivers = array_get(&sql_drivers, &count); for (i = 0; i < count; i++) { if (strcmp(drivers[i]->name, name) == 0) return drivers[i]; } return NULL; } void sql_driver_register(const struct sql_db *driver) { if (sql_driver_lookup(driver->name) != NULL) { i_fatal("sql_driver_register(%s): Already registered", driver->name); } array_append(&sql_drivers, &driver, 1); } void sql_driver_unregister(const struct sql_db *driver) { const struct sql_db *const *drivers; unsigned int i, count; drivers = array_get(&sql_drivers, &count); for (i = 0; i < count; i++) { if (drivers[i] == driver) { array_delete(&sql_drivers, i, 1); break; } } } struct sql_db *sql_init(const char *db_driver, const char *connect_string) { const struct sql_db *driver; struct sql_db *db; i_assert(connect_string != NULL); driver = sql_driver_lookup(db_driver); if (driver == NULL) i_fatal("Unknown database driver '%s'", db_driver); if ((driver->flags & SQL_DB_FLAG_POOLED) == 0) db = driver->v.init(connect_string); else db = driver_sqlpool_init(connect_string, driver); i_array_init(&db->module_contexts, 5); return db; } void sql_deinit(struct sql_db **_db) { struct sql_db *db = *_db; *_db = NULL; if (db->to_reconnect != NULL) timeout_remove(&db->to_reconnect); db->v.deinit(db); } enum sql_db_flags sql_get_flags(struct sql_db *db) { return db->flags; } int sql_connect(struct sql_db *db) { time_t now; switch (db->state) { case SQL_DB_STATE_DISCONNECTED: break; case SQL_DB_STATE_CONNECTING: return 0; default: return 1; } /* don't try reconnecting more than once a second */ now = time(NULL); if (db->last_connect_try + (time_t)db->connect_delay > now) return -1; db->last_connect_try = now; return db->v.connect(db); } void sql_disconnect(struct sql_db *db) { if (db->to_reconnect != NULL) timeout_remove(&db->to_reconnect); db->v.disconnect(db); } const char *sql_escape_string(struct sql_db *db, const char *string) { return db->v.escape_string(db, string); } const char *sql_escape_blob(struct sql_db *db, const unsigned char *data, size_t size) { return db->v.escape_blob(db, data, size); } void sql_exec(struct sql_db *db, const char *query) { db->v.exec(db, query); } #undef sql_query void sql_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { db->v.query(db, query, callback, context); } struct sql_result *sql_query_s(struct sql_db *db, const char *query) { return db->v.query_s(db, query); } static struct sql_prepared_statement * default_sql_prepared_statement_init(struct sql_db *db, const char *query_template) { struct default_sql_prepared_statement *prep_stmt; prep_stmt = i_new(struct default_sql_prepared_statement, 1); prep_stmt->prep_stmt.db = db; prep_stmt->query_template = i_strdup(query_template); return &prep_stmt->prep_stmt; } static void default_sql_prepared_statement_deinit(struct sql_prepared_statement *_prep_stmt) { struct default_sql_prepared_statement *prep_stmt = (struct default_sql_prepared_statement *)_prep_stmt; i_free(prep_stmt->query_template); i_free(prep_stmt); } static struct sql_statement * default_sql_statement_init_prepared(struct sql_prepared_statement *_stmt) { struct default_sql_prepared_statement *stmt = (struct default_sql_prepared_statement *)_stmt; return sql_statement_init(_stmt->db, stmt->query_template); } const char *sql_statement_get_query(struct sql_statement *stmt) { string_t *query = t_str_new(128); const char *const *args; unsigned int i, args_count, arg_pos = 0; args = array_get(&stmt->args, &args_count); for (i = 0; stmt->query_template[i] != '\0'; i++) { if (stmt->query_template[i] == '?') { if (arg_pos >= args_count || args[arg_pos] == NULL) { i_panic("lib-sql: Missing bind for arg #%u in statement: %s", arg_pos, stmt->query_template); } str_append(query, args[arg_pos++]); } else { str_append_c(query, stmt->query_template[i]); } } if (arg_pos != args_count) { i_panic("lib-sql: Too many bind args (%u) for statement: %s", args_count, stmt->query_template); } return str_c(query); } static void default_sql_statement_query(struct sql_statement *stmt, sql_query_callback_t *callback, void *context) { sql_query(stmt->db, sql_statement_get_query(stmt), callback, context); pool_unref(&stmt->pool); } static struct sql_result * default_sql_statement_query_s(struct sql_statement *stmt) { struct sql_result *result = sql_query_s(stmt->db, sql_statement_get_query(stmt)); pool_unref(&stmt->pool); return result; } static void default_sql_update_stmt(struct sql_transaction_context *ctx, struct sql_statement *stmt, unsigned int *affected_rows) { ctx->db->v.update(ctx, sql_statement_get_query(stmt), affected_rows); pool_unref(&stmt->pool); } struct sql_prepared_statement * sql_prepared_statement_init(struct sql_db *db, const char *query_template) { if (db->v.prepared_statement_init != NULL) return db->v.prepared_statement_init(db, query_template); else return default_sql_prepared_statement_init(db, query_template); } void sql_prepared_statement_deinit(struct sql_prepared_statement **_prep_stmt) { struct sql_prepared_statement *prep_stmt = *_prep_stmt; *_prep_stmt = NULL; if (prep_stmt->db->v.prepared_statement_deinit != NULL) prep_stmt->db->v.prepared_statement_deinit(prep_stmt); else default_sql_prepared_statement_deinit(prep_stmt); } static void sql_statement_init_fields(struct sql_statement *stmt, struct sql_db *db) { stmt->db = db; p_array_init(&stmt->args, stmt->pool, 8); } struct sql_statement * sql_statement_init(struct sql_db *db, const char *query_template) { struct sql_statement *stmt; if (db->v.statement_init != NULL) stmt = db->v.statement_init(db, query_template); else { pool_t pool = pool_alloconly_create("sql statement", 1024); stmt = p_new(pool, struct sql_statement, 1); stmt->pool = pool; } stmt->query_template = p_strdup(stmt->pool, query_template); sql_statement_init_fields(stmt, db); return stmt; } struct sql_statement * sql_statement_init_prepared(struct sql_prepared_statement *prep_stmt) { struct sql_statement *stmt; if (prep_stmt->db->v.statement_init_prepared == NULL) return default_sql_statement_init_prepared(prep_stmt); stmt = prep_stmt->db->v.statement_init_prepared(prep_stmt); sql_statement_init_fields(stmt, prep_stmt->db); return stmt; } void sql_statement_abort(struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_abort != NULL) stmt->db->v.statement_abort(stmt); pool_unref(&stmt->pool); } void sql_statement_set_timestamp(struct sql_statement *stmt, const struct timespec *ts) { if (stmt->db->v.statement_set_timestamp != NULL) stmt->db->v.statement_set_timestamp(stmt, ts); } void sql_statement_bind_str(struct sql_statement *stmt, unsigned int column_idx, const char *value) { const char *escaped_value = p_strdup_printf(stmt->pool, "'%s'", sql_escape_string(stmt->db, value)); array_idx_set(&stmt->args, column_idx, &escaped_value); if (stmt->db->v.statement_bind_str != NULL) stmt->db->v.statement_bind_str(stmt, column_idx, value); } void sql_statement_bind_binary(struct sql_statement *stmt, unsigned int column_idx, const void *value, size_t value_size) { const char *value_str = p_strdup_printf(stmt->pool, "%s", sql_escape_blob(stmt->db, value, value_size)); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_binary != NULL) { stmt->db->v.statement_bind_binary(stmt, column_idx, value, value_size); } } void sql_statement_bind_int64(struct sql_statement *stmt, unsigned int column_idx, int64_t value) { const char *value_str = p_strdup_printf(stmt->pool, "%lld", (long long)value); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_int64 != NULL) stmt->db->v.statement_bind_int64(stmt, column_idx, value); } #undef sql_statement_query void sql_statement_query(struct sql_statement **_stmt, sql_query_callback_t *callback, void *context) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_query != NULL) stmt->db->v.statement_query(stmt, callback, context); else default_sql_statement_query(stmt, callback, context); } struct sql_result *sql_statement_query_s(struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_query_s != NULL) return stmt->db->v.statement_query_s(stmt); else return default_sql_statement_query_s(stmt); } void sql_result_ref(struct sql_result *result) { result->refcount++; } void sql_result_unref(struct sql_result *result) { i_assert(result->refcount > 0); if (--result->refcount > 0) return; i_free(result->map); result->v.free(result); } static const struct sql_field_def * sql_field_def_find(const struct sql_field_def *fields, const char *name) { unsigned int i; for (i = 0; fields[i].name != NULL; i++) { if (strcasecmp(fields[i].name, name) == 0) return &fields[i]; } return NULL; } static void sql_result_build_map(struct sql_result *result, const struct sql_field_def *fields, size_t dest_size) { const struct sql_field_def *def; const char *name; unsigned int i, count, field_size = 0; count = sql_result_get_fields_count(result); result->map_size = count; result->map = i_new(struct sql_field_map, result->map_size); for (i = 0; i < count; i++) { name = sql_result_get_field_name(result, i); def = sql_field_def_find(fields, name); if (def != NULL) { result->map[i].type = def->type; result->map[i].offset = def->offset; switch (def->type) { case SQL_TYPE_STR: field_size = sizeof(const char *); break; case SQL_TYPE_UINT: field_size = sizeof(unsigned int); break; case SQL_TYPE_ULLONG: field_size = sizeof(unsigned long long); break; case SQL_TYPE_BOOL: field_size = sizeof(bool); break; } i_assert(def->offset + field_size <= dest_size); } else { result->map[i].offset = (size_t)-1; } } } void sql_result_setup_fetch(struct sql_result *result, const struct sql_field_def *fields, void *dest, size_t dest_size) { if (result->map == NULL) sql_result_build_map(result, fields, dest_size); result->fetch_dest = dest; result->fetch_dest_size = dest_size; } static void sql_result_fetch(struct sql_result *result) { unsigned int i, count; const char *value; void *ptr; memset(result->fetch_dest, 0, result->fetch_dest_size); count = result->map_size; for (i = 0; i < count; i++) { if (result->map[i].offset == (size_t)-1) continue; value = sql_result_get_field_value(result, i); ptr = STRUCT_MEMBER_P(result->fetch_dest, result->map[i].offset); switch (result->map[i].type) { case SQL_TYPE_STR: { *((const char **)ptr) = value; break; } case SQL_TYPE_UINT: { if (value != NULL && str_to_uint(value, (unsigned int *)ptr) < 0) i_error("sql: Value not uint: %s", value); break; } case SQL_TYPE_ULLONG: { if (value != NULL && str_to_ullong(value, (unsigned long long *)ptr) < 0) i_error("sql: Value not ullong: %s", value); break; } case SQL_TYPE_BOOL: { if (value != NULL && (*value == 't' || *value == '1')) *((bool *)ptr) = TRUE; break; } } } } int sql_result_next_row(struct sql_result *result) { int ret; if ((ret = result->v.next_row(result)) <= 0) return ret; if (result->fetch_dest != NULL) sql_result_fetch(result); return 1; } #undef sql_result_more void sql_result_more(struct sql_result **result, sql_query_callback_t *callback, void *context) { i_assert((*result)->v.more != NULL); (*result)->v.more(result, TRUE, callback, context); } static void sql_result_more_sync_callback(struct sql_result *result, void *context) { struct sql_result **dest_result = context; *dest_result = result; } void sql_result_more_s(struct sql_result **result) { i_assert((*result)->v.more != NULL); (*result)->v.more(result, FALSE, sql_result_more_sync_callback, result); /* the callback must have been called */ i_assert(*result != NULL); } unsigned int sql_result_get_fields_count(struct sql_result *result) { return result->v.get_fields_count(result); } const char *sql_result_get_field_name(struct sql_result *result, unsigned int idx) { return result->v.get_field_name(result, idx); } int sql_result_find_field(struct sql_result *result, const char *field_name) { return result->v.find_field(result, field_name); } const char *sql_result_get_field_value(struct sql_result *result, unsigned int idx) { return result->v.get_field_value(result, idx); } const unsigned char * sql_result_get_field_value_binary(struct sql_result *result, unsigned int idx, size_t *size_r) { return result->v.get_field_value_binary(result, idx, size_r); } const char *sql_result_find_field_value(struct sql_result *result, const char *field_name) { return result->v.find_field_value(result, field_name); } const char *const *sql_result_get_values(struct sql_result *result) { return result->v.get_values(result); } const char *sql_result_get_error(struct sql_result *result) { return result->v.get_error(result); } enum sql_result_error_type sql_result_get_error_type(struct sql_result *result) { return result->error_type; } static void sql_result_not_connected_free(struct sql_result *result ATTR_UNUSED) { } static int sql_result_not_connected_next_row(struct sql_result *result ATTR_UNUSED) { return -1; } static const char * sql_result_not_connected_get_error(struct sql_result *result ATTR_UNUSED) { return SQL_ERRSTR_NOT_CONNECTED; } struct sql_transaction_context *sql_transaction_begin(struct sql_db *db) { return db->v.transaction_begin(db); } struct sql_commit1_wrap_ctx { sql_commit_callback_t *callback; void *context; }; static void sql_commit1_wrap(const struct sql_commit_result *result, void *context) { struct sql_commit1_wrap_ctx *ctx = context; ctx->callback(result->error, ctx->context); i_free(ctx); } #undef sql_transaction_commit void sql_transaction_commit(struct sql_transaction_context **_ctx, sql_commit_callback_t *callback, void *context) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; if (ctx->db->v.transaction_commit != NULL) ctx->db->v.transaction_commit(ctx, callback, context); else { struct sql_commit1_wrap_ctx *wrap; wrap = i_new(struct sql_commit1_wrap_ctx, 1); wrap->callback = callback; wrap->context = context; ctx->db->v.transaction_commit2(ctx, sql_commit1_wrap, wrap); } } struct sql_commit2_wrap_ctx { sql_commit2_callback_t *callback; void *context; }; static void sql_commit2_wrap(const char *error, void *context) { struct sql_commit2_wrap_ctx *ctx = context; struct sql_commit_result result = { .error = error }; ctx->callback(&result, ctx->context); i_free(ctx); } #undef sql_transaction_commit2 void sql_transaction_commit2(struct sql_transaction_context **_ctx, sql_commit2_callback_t *callback, void *context) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; if (ctx->db->v.transaction_commit2 != NULL) ctx->db->v.transaction_commit2(ctx, callback, context); else { struct sql_commit2_wrap_ctx *wrap; wrap = i_new(struct sql_commit2_wrap_ctx, 1); wrap->callback = callback; wrap->context = context; ctx->db->v.transaction_commit(ctx, sql_commit2_wrap, wrap); } } int sql_transaction_commit_s(struct sql_transaction_context **_ctx, const char **error_r) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; return ctx->db->v.transaction_commit_s(ctx, error_r); } void sql_transaction_rollback(struct sql_transaction_context **_ctx) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; ctx->db->v.transaction_rollback(ctx); } void sql_update(struct sql_transaction_context *ctx, const char *query) { ctx->db->v.update(ctx, query, NULL); } void sql_update_stmt(struct sql_transaction_context *ctx, struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (ctx->db->v.update_stmt != NULL) ctx->db->v.update_stmt(ctx, stmt, NULL); else default_sql_update_stmt(ctx, stmt, NULL); } void sql_update_get_rows(struct sql_transaction_context *ctx, const char *query, unsigned int *affected_rows) { ctx->db->v.update(ctx, query, affected_rows); } void sql_update_stmt_get_rows(struct sql_transaction_context *ctx, struct sql_statement **_stmt, unsigned int *affected_rows) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (ctx->db->v.update_stmt != NULL) ctx->db->v.update_stmt(ctx, stmt, affected_rows); else default_sql_update_stmt(ctx, stmt, affected_rows); } void sql_db_set_state(struct sql_db *db, enum sql_db_state state) { enum sql_db_state old_state = db->state; if (db->state == state) return; db->state = state; if (db->state_change_callback != NULL) { db->state_change_callback(db, old_state, db->state_change_context); } } void sql_transaction_add_query(struct sql_transaction_context *ctx, pool_t pool, const char *query, unsigned int *affected_rows) { struct sql_transaction_query *tquery; tquery = p_new(pool, struct sql_transaction_query, 1); tquery->trans = ctx; tquery->query = p_strdup(pool, query); tquery->affected_rows = affected_rows; if (ctx->head == NULL) ctx->head = tquery; else ctx->tail->next = tquery; ctx->tail = tquery; } struct sql_result sql_not_connected_result = { .v = { sql_result_not_connected_free, sql_result_not_connected_next_row, NULL, NULL, NULL, NULL, NULL, NULL, NULL, sql_result_not_connected_get_error, NULL, }, .failed_try_retry = TRUE }; dovecot-2.2.33.2/src/lib-sql/driver-pgsql.c0000644000175000017500000007204113165463624015260 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "hex-binary.h" #include "str.h" #include "time-util.h" #include "sql-api-private.h" #ifdef BUILD_PGSQL #include #define PGSQL_DNS_WARN_MSECS 500 struct pgsql_db { struct sql_db api; pool_t pool; char *connect_string; char *host; PGconn *pg; struct io *io; struct timeout *to_connect; enum io_condition io_dir; struct pgsql_result *cur_result; struct ioloop *ioloop, *orig_ioloop; struct sql_result *sync_result; bool (*next_callback)(void *); void *next_context; char *error; const char *connect_state; unsigned int fatal_error:1; }; struct pgsql_binary_value { unsigned char *value; size_t size; }; struct pgsql_result { struct sql_result api; PGresult *pgres; struct timeout *to; unsigned int rownum, rows; unsigned int fields_count; const char **fields; const char **values; ARRAY(struct pgsql_binary_value) binary_values; sql_query_callback_t *callback; void *context; unsigned int timeout:1; }; struct pgsql_transaction_context { struct sql_transaction_context ctx; int refcount; sql_commit_callback_t *callback; void *context; pool_t query_pool; const char *error; unsigned int failed:1; }; extern const struct sql_db driver_pgsql_db; extern const struct sql_result driver_pgsql_result; static void result_finish(struct pgsql_result *result); static void transaction_update_callback(struct sql_result *result, struct sql_transaction_query *query); static const char *pgsql_prefix(struct pgsql_db *db) { return db->host == NULL ? "pgsql" : t_strdup_printf("pgsql(%s)", db->host); } static void driver_pgsql_set_state(struct pgsql_db *db, enum sql_db_state state) { i_assert(state == SQL_DB_STATE_BUSY || db->cur_result == NULL); /* switch back to original ioloop in case the caller wants to add/remove timeouts */ if (db->ioloop != NULL) io_loop_set_current(db->orig_ioloop); sql_db_set_state(&db->api, state); if (db->ioloop != NULL) io_loop_set_current(db->ioloop); } static bool driver_pgsql_next_callback(struct pgsql_db *db) { bool (*next_callback)(void *) = db->next_callback; void *next_context = db->next_context; if (next_callback == NULL) return FALSE; db->next_callback = NULL; db->next_context = NULL; return next_callback(next_context); } static void driver_pgsql_stop_io(struct pgsql_db *db) { if (db->io != NULL) { io_remove(&db->io); db->io_dir = 0; } } static void driver_pgsql_close(struct pgsql_db *db) { db->io_dir = 0; db->fatal_error = FALSE; driver_pgsql_stop_io(db); PQfinish(db->pg); db->pg = NULL; if (db->to_connect != NULL) timeout_remove(&db->to_connect); driver_pgsql_set_state(db, SQL_DB_STATE_DISCONNECTED); if (db->ioloop != NULL) { /* running a sync query, stop it */ io_loop_stop(db->ioloop); } driver_pgsql_next_callback(db); } static const char *last_error(struct pgsql_db *db) { const char *msg; size_t len; msg = PQerrorMessage(db->pg); if (msg == NULL) return "(no error set)"; /* Error message should contain trailing \n, we don't want it */ len = strlen(msg); return len == 0 || msg[len-1] != '\n' ? msg : t_strndup(msg, len-1); } static void connect_callback(struct pgsql_db *db) { enum io_condition io_dir = 0; int ret; driver_pgsql_stop_io(db); while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE) ; switch (ret) { case PGRES_POLLING_READING: db->connect_state = "wait for input"; io_dir = IO_READ; break; case PGRES_POLLING_WRITING: db->connect_state = "wait for output"; io_dir = IO_WRITE; break; case PGRES_POLLING_OK: break; case PGRES_POLLING_FAILED: i_error("%s: Connect failed to database %s: %s (state: %s)", pgsql_prefix(db), PQdb(db->pg), last_error(db), db->connect_state); driver_pgsql_close(db); return; } if (io_dir != 0) { db->io = io_add(PQsocket(db->pg), io_dir, connect_callback, db); db->io_dir = io_dir; } if (io_dir == 0) { db->connect_state = "connected"; if (db->to_connect != NULL) timeout_remove(&db->to_connect); driver_pgsql_set_state(db, SQL_DB_STATE_IDLE); if (db->ioloop != NULL) { /* driver_pgsql_sync_init() waiting for connection to finish */ io_loop_stop(db->ioloop); } } } static void driver_pgsql_connect_timeout(struct pgsql_db *db) { unsigned int secs = ioloop_time - db->api.last_connect_try; i_error("%s: Connect failed: Timeout after %u seconds (state: %s)", pgsql_prefix(db), secs, db->connect_state); driver_pgsql_close(db); } static int driver_pgsql_connect(struct sql_db *_db) { struct pgsql_db *db = (struct pgsql_db *)_db; struct timeval tv_start; int msecs; i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED); io_loop_time_refresh(); tv_start = ioloop_timeval; db->pg = PQconnectStart(db->connect_string); if (db->pg == NULL) { i_fatal("%s: PQconnectStart() failed (out of memory)", pgsql_prefix(db)); } if (PQstatus(db->pg) == CONNECTION_BAD) { i_error("%s: Connect failed to database %s: %s", pgsql_prefix(db), PQdb(db->pg), last_error(db)); driver_pgsql_close(db); return -1; } /* PQconnectStart() blocks on host name resolving. Log a warning if it takes too long. Also don't include time spent on that in the connect timeout (by refreshing ioloop time). */ io_loop_time_refresh(); msecs = timeval_diff_msecs(&ioloop_timeval, &tv_start); if (msecs > PGSQL_DNS_WARN_MSECS) { i_warning("%s: DNS lookup took %d.%03d s", pgsql_prefix(db), msecs/1000, msecs % 1000); } /* nonblocking connecting begins. */ if (PQsetnonblocking(db->pg, 1) < 0) i_error("%s: PQsetnonblocking() failed", pgsql_prefix(db)); i_assert(db->to_connect == NULL); db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000, driver_pgsql_connect_timeout, db); db->connect_state = "connecting"; db->io = io_add(PQsocket(db->pg), IO_WRITE, connect_callback, db); db->io_dir = IO_WRITE; driver_pgsql_set_state(db, SQL_DB_STATE_CONNECTING); return 0; } static void driver_pgsql_disconnect(struct sql_db *_db) { struct pgsql_db *db = (struct pgsql_db *)_db; if (db->cur_result != NULL && db->cur_result->to != NULL) { driver_pgsql_stop_io(db); result_finish(db->cur_result); } _db->no_reconnect = TRUE; driver_pgsql_close(db); _db->no_reconnect = FALSE; } static struct sql_db *driver_pgsql_init_v(const char *connect_string) { struct pgsql_db *db; db = i_new(struct pgsql_db, 1); db->connect_string = i_strdup(connect_string); db->api = driver_pgsql_db; T_BEGIN { const char *const *arg = t_strsplit(connect_string, " "); for (; *arg != NULL; arg++) { if (strncmp(*arg, "host=", 5) == 0) db->host = i_strdup(*arg + 5); } } T_END; return &db->api; } static void driver_pgsql_deinit_v(struct sql_db *_db) { struct pgsql_db *db = (struct pgsql_db *)_db; driver_pgsql_disconnect(_db); i_free(db->host); i_free(db->error); i_free(db->connect_string); array_free(&_db->module_contexts); i_free(db); } static void driver_pgsql_set_idle(struct pgsql_db *db) { i_assert(db->api.state == SQL_DB_STATE_BUSY); if (db->fatal_error) driver_pgsql_close(db); else if (!driver_pgsql_next_callback(db)) driver_pgsql_set_state(db, SQL_DB_STATE_IDLE); } static void consume_results(struct pgsql_db *db) { PGresult *pgres; driver_pgsql_stop_io(db); while (PQconsumeInput(db->pg)) { if (PQisBusy(db->pg)) { db->io = io_add(PQsocket(db->pg), IO_READ, consume_results, db); db->io_dir = IO_READ; return; } pgres = PQgetResult(db->pg); if (pgres == NULL) break; PQclear(pgres); } if (PQstatus(db->pg) == CONNECTION_BAD) driver_pgsql_close(db); else driver_pgsql_set_idle(db); } static void driver_pgsql_result_free(struct sql_result *_result) { struct pgsql_db *db = (struct pgsql_db *)_result->db; struct pgsql_result *result = (struct pgsql_result *)_result; bool success; i_assert(!result->api.callback); i_assert(db->cur_result == result); i_assert(result->callback == NULL); if (_result == db->sync_result) db->sync_result = NULL; db->cur_result = NULL; success = result->pgres != NULL && !db->fatal_error; if (result->pgres != NULL) { PQclear(result->pgres); result->pgres = NULL; } if (success) { /* we'll have to read the rest of the results as well */ i_assert(db->io == NULL); consume_results(db); } else { driver_pgsql_set_idle(db); } if (array_is_created(&result->binary_values)) { struct pgsql_binary_value *value; array_foreach_modifiable(&result->binary_values, value) PQfreemem(value->value); array_free(&result->binary_values); } i_free(result->fields); i_free(result->values); i_free(result); } static void result_finish(struct pgsql_result *result) { struct pgsql_db *db = (struct pgsql_db *)result->api.db; bool free_result = TRUE; i_assert(db->io == NULL); timeout_remove(&result->to); /* if connection to server was lost, we don't yet see that the connection is bad. we only see the fatal error, so assume it also means disconnection. */ if (PQstatus(db->pg) == CONNECTION_BAD || result->pgres == NULL || PQresultStatus(result->pgres) == PGRES_FATAL_ERROR) db->fatal_error = TRUE; if (db->fatal_error) { result->api.failed = TRUE; result->api.failed_try_retry = TRUE; } result->api.callback = TRUE; T_BEGIN { result->callback(&result->api, result->context); } T_END; result->api.callback = FALSE; free_result = db->sync_result != &result->api; if (db->ioloop != NULL) io_loop_stop(db->ioloop); i_assert(!free_result || result->api.refcount > 0); result->callback = NULL; if (free_result) sql_result_unref(&result->api); } static void get_result(struct pgsql_result *result) { struct pgsql_db *db = (struct pgsql_db *)result->api.db; driver_pgsql_stop_io(db); if (!PQconsumeInput(db->pg)) { result_finish(result); return; } if (PQisBusy(db->pg)) { db->io = io_add(PQsocket(db->pg), IO_READ, get_result, result); db->io_dir = IO_READ; return; } result->pgres = PQgetResult(db->pg); result_finish(result); } static void flush_callback(struct pgsql_result *result) { struct pgsql_db *db = (struct pgsql_db *)result->api.db; int ret; driver_pgsql_stop_io(db); ret = PQflush(db->pg); if (ret > 0) { db->io = io_add(PQsocket(db->pg), IO_WRITE, flush_callback, result); db->io_dir = IO_WRITE; return; } if (ret < 0) { result_finish(result); } else { /* all flushed */ get_result(result); } } static void query_timeout(struct pgsql_result *result) { struct pgsql_db *db = (struct pgsql_db *)result->api.db; driver_pgsql_stop_io(db); i_error("%s: Query timed out, aborting", pgsql_prefix(db)); result->timeout = TRUE; result_finish(result); } static void do_query(struct pgsql_result *result, const char *query) { struct pgsql_db *db = (struct pgsql_db *)result->api.db; int ret; i_assert(SQL_DB_IS_READY(&db->api)); i_assert(db->cur_result == NULL); i_assert(db->io == NULL); driver_pgsql_set_state(db, SQL_DB_STATE_BUSY); db->cur_result = result; result->to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000, query_timeout, result); if (!PQsendQuery(db->pg, query) || (ret = PQflush(db->pg)) < 0) { /* failed to send query */ result_finish(result); return; } if (ret > 0) { /* write blocks */ db->io = io_add(PQsocket(db->pg), IO_WRITE, flush_callback, result); db->io_dir = IO_WRITE; } else { get_result(result); } } static const char * driver_pgsql_escape_string(struct sql_db *_db, const char *string) { struct pgsql_db *db = (struct pgsql_db *)_db; size_t len = strlen(string); char *to; #ifdef HAVE_PQESCAPE_STRING_CONN if (db->api.state == SQL_DB_STATE_DISCONNECTED) { /* try connecting again */ (void)sql_connect(&db->api); } if (db->api.state != SQL_DB_STATE_DISCONNECTED) { int error; to = t_buffer_get(len * 2 + 1); len = PQescapeStringConn(db->pg, to, string, len, &error); } else #endif { to = t_buffer_get(len * 2 + 1); len = PQescapeString(to, string, len); } t_buffer_alloc(len + 1); return to; } static void exec_callback(struct sql_result *_result, void *context ATTR_UNUSED) { struct pgsql_db *db = (struct pgsql_db *)_result->db; i_error("%s: sql_exec() failed: %s", pgsql_prefix(db), last_error(db)); } static void driver_pgsql_exec(struct sql_db *db, const char *query) { struct pgsql_result *result; result = i_new(struct pgsql_result, 1); result->api = driver_pgsql_result; result->api.db = db; result->api.refcount = 1; result->callback = exec_callback; do_query(result, query); } static void driver_pgsql_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { struct pgsql_result *result; result = i_new(struct pgsql_result, 1); result->api = driver_pgsql_result; result->api.db = db; result->api.refcount = 1; result->callback = callback; result->context = context; do_query(result, query); } static void pgsql_query_s_callback(struct sql_result *result, void *context) { struct pgsql_db *db = context; db->sync_result = result; } static void driver_pgsql_sync_init(struct pgsql_db *db) { bool add_to_connect; db->orig_ioloop = current_ioloop; if (db->io == NULL) { db->ioloop = io_loop_create(); return; } i_assert(db->api.state == SQL_DB_STATE_CONNECTING); /* have to move our existing I/O and timeout handlers to new I/O loop */ io_remove(&db->io); if (db->to_connect != NULL) { timeout_remove(&db->to_connect); add_to_connect = TRUE; } else { add_to_connect = FALSE; } db->ioloop = io_loop_create(); if (add_to_connect) { db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000, driver_pgsql_connect_timeout, db); } db->io = io_add(PQsocket(db->pg), db->io_dir, connect_callback, db); /* wait for connecting to finish */ io_loop_run(db->ioloop); } static void driver_pgsql_sync_deinit(struct pgsql_db *db) { io_loop_destroy(&db->ioloop); } static struct sql_result * driver_pgsql_sync_query(struct pgsql_db *db, const char *query) { struct sql_result *result; i_assert(db->sync_result == NULL); switch (db->api.state) { case SQL_DB_STATE_CONNECTING: case SQL_DB_STATE_BUSY: i_unreached(); case SQL_DB_STATE_DISCONNECTED: sql_not_connected_result.refcount++; return &sql_not_connected_result; case SQL_DB_STATE_IDLE: break; } driver_pgsql_query(&db->api, query, pgsql_query_s_callback, db); if (db->sync_result == NULL) io_loop_run(db->ioloop); i_assert(db->io == NULL); result = db->sync_result; if (result == &sql_not_connected_result) { /* we don't end up in pgsql's free function, so sync_result won't be set to NULL if we don't do it here. */ db->sync_result = NULL; } else if (result == NULL) { result = &sql_not_connected_result; result->refcount++; } i_assert(db->io == NULL); return result; } static struct sql_result * driver_pgsql_query_s(struct sql_db *_db, const char *query) { struct pgsql_db *db = (struct pgsql_db *)_db; struct sql_result *result; driver_pgsql_sync_init(db); result = driver_pgsql_sync_query(db, query); driver_pgsql_sync_deinit(db); return result; } static int driver_pgsql_result_next_row(struct sql_result *_result) { struct pgsql_result *result = (struct pgsql_result *)_result; struct pgsql_db *db = (struct pgsql_db *)_result->db; if (result->rows != 0) { /* second time we're here */ if (++result->rownum < result->rows) return 1; /* end of this packet. see if there's more. FIXME: this may block, but the current API doesn't provide a non-blocking way to do this.. */ PQclear(result->pgres); result->pgres = PQgetResult(db->pg); if (result->pgres == NULL) return 0; } if (result->pgres == NULL) { _result->failed = TRUE; return -1; } switch (PQresultStatus(result->pgres)) { case PGRES_COMMAND_OK: /* no rows returned */ return 0; case PGRES_TUPLES_OK: result->rows = PQntuples(result->pgres); return result->rows > 0; case PGRES_EMPTY_QUERY: case PGRES_NONFATAL_ERROR: /* nonfatal error */ _result->failed = TRUE; return -1; default: /* treat as fatal error */ _result->failed = TRUE; db->fatal_error = TRUE; return -1; } } static void driver_pgsql_result_fetch_fields(struct pgsql_result *result) { unsigned int i; if (result->fields != NULL) return; /* @UNSAFE */ result->fields_count = PQnfields(result->pgres); result->fields = i_new(const char *, result->fields_count); for (i = 0; i < result->fields_count; i++) result->fields[i] = PQfname(result->pgres, i); } static unsigned int driver_pgsql_result_get_fields_count(struct sql_result *_result) { struct pgsql_result *result = (struct pgsql_result *)_result; driver_pgsql_result_fetch_fields(result); return result->fields_count; } static const char * driver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx) { struct pgsql_result *result = (struct pgsql_result *)_result; driver_pgsql_result_fetch_fields(result); i_assert(idx < result->fields_count); return result->fields[idx]; } static int driver_pgsql_result_find_field(struct sql_result *_result, const char *field_name) { struct pgsql_result *result = (struct pgsql_result *)_result; unsigned int i; driver_pgsql_result_fetch_fields(result); for (i = 0; i < result->fields_count; i++) { if (strcmp(result->fields[i], field_name) == 0) return i; } return -1; } static const char * driver_pgsql_result_get_field_value(struct sql_result *_result, unsigned int idx) { struct pgsql_result *result = (struct pgsql_result *)_result; if (PQgetisnull(result->pgres, result->rownum, idx)) return NULL; return PQgetvalue(result->pgres, result->rownum, idx); } static const unsigned char * driver_pgsql_result_get_field_value_binary(struct sql_result *_result, unsigned int idx, size_t *size_r) { struct pgsql_result *result = (struct pgsql_result *)_result; const char *value; struct pgsql_binary_value *binary_value; if (PQgetisnull(result->pgres, result->rownum, idx)) { *size_r = 0; return NULL; } value = PQgetvalue(result->pgres, result->rownum, idx); if (!array_is_created(&result->binary_values)) i_array_init(&result->binary_values, idx + 1); binary_value = array_idx_modifiable(&result->binary_values, idx); if (binary_value->value == NULL) { binary_value->value = PQunescapeBytea((const unsigned char *)value, &binary_value->size); } *size_r = binary_value->size; return binary_value->value; } static const char * driver_pgsql_result_find_field_value(struct sql_result *result, const char *field_name) { int idx; idx = driver_pgsql_result_find_field(result, field_name); if (idx < 0) return NULL; return driver_pgsql_result_get_field_value(result, idx); } static const char *const * driver_pgsql_result_get_values(struct sql_result *_result) { struct pgsql_result *result = (struct pgsql_result *)_result; unsigned int i; if (result->values == NULL) { driver_pgsql_result_fetch_fields(result); result->values = i_new(const char *, result->fields_count); } /* @UNSAFE */ for (i = 0; i < result->fields_count; i++) { result->values[i] = driver_pgsql_result_get_field_value(_result, i); } return result->values; } static const char *driver_pgsql_result_get_error(struct sql_result *_result) { struct pgsql_result *result = (struct pgsql_result *)_result; struct pgsql_db *db = (struct pgsql_db *)_result->db; const char *msg; size_t len; i_free_and_null(db->error); if (result->timeout) { db->error = i_strdup("Query timed out"); } else if (result->pgres == NULL) { /* connection error */ db->error = i_strdup(last_error(db)); } else { msg = PQresultErrorMessage(result->pgres); if (msg == NULL) return "(no error set)"; /* Error message should contain trailing \n, we don't want it */ len = strlen(msg); db->error = len == 0 || msg[len-1] != '\n' ? i_strdup(msg) : i_strndup(msg, len-1); } return db->error; } static struct sql_transaction_context * driver_pgsql_transaction_begin(struct sql_db *db) { struct pgsql_transaction_context *ctx; ctx = i_new(struct pgsql_transaction_context, 1); ctx->ctx.db = db; /* we need to be able to handle multiple open transactions, so at least for now just keep them in memory until commit time. */ ctx->query_pool = pool_alloconly_create("pgsql transaction", 1024); return &ctx->ctx; } static void driver_pgsql_transaction_free(struct pgsql_transaction_context *ctx) { pool_unref(&ctx->query_pool); i_free(ctx); } static void transaction_commit_callback(struct sql_result *result, struct pgsql_transaction_context *ctx) { if (sql_result_next_row(result) < 0) ctx->callback(sql_result_get_error(result), ctx->context); else ctx->callback(NULL, ctx->context); driver_pgsql_transaction_free(ctx); } static bool transaction_send_next(void *context) { struct pgsql_transaction_context *ctx = context; i_assert(!ctx->failed); if (ctx->ctx.db->state == SQL_DB_STATE_BUSY) { /* kludgy.. */ ctx->ctx.db->state = SQL_DB_STATE_IDLE; } else if (!SQL_DB_IS_READY(ctx->ctx.db)) { ctx->callback("Not connected", ctx->context); return FALSE; } if (ctx->ctx.head != NULL) { struct sql_transaction_query *query = ctx->ctx.head; ctx->ctx.head = ctx->ctx.head->next; sql_query(ctx->ctx.db, query->query, transaction_update_callback, query); } else { sql_query(ctx->ctx.db, "COMMIT", transaction_commit_callback, ctx); } return TRUE; } static void transaction_begin_callback(struct sql_result *result, struct pgsql_transaction_context *ctx) { struct pgsql_db *db = (struct pgsql_db *)result->db; i_assert(result->db == ctx->ctx.db); if (sql_result_next_row(result) < 0) { ctx->callback(sql_result_get_error(result), ctx->context); driver_pgsql_transaction_free(ctx); return; } i_assert(db->next_callback == NULL); db->next_callback = transaction_send_next; db->next_context = ctx; } static void transaction_update_callback(struct sql_result *result, struct sql_transaction_query *query) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)query->trans; struct pgsql_db *db = (struct pgsql_db *)result->db; if (sql_result_next_row(result) < 0) { ctx->callback(sql_result_get_error(result), ctx->context); driver_pgsql_transaction_free(ctx); return; } if (query->affected_rows != NULL) { struct pgsql_result *pg_result = (struct pgsql_result *)result; if (str_to_uint(PQcmdTuples(pg_result->pgres), query->affected_rows) < 0) i_unreached(); } i_assert(db->next_callback == NULL); db->next_callback = transaction_send_next; db->next_context = ctx; } static void transaction_trans_query_callback(struct sql_result *result, struct sql_transaction_query *query) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)query->trans; if (sql_result_next_row(result) < 0) { ctx->callback(sql_result_get_error(result), ctx->context); driver_pgsql_transaction_free(ctx); return; } if (query->affected_rows != NULL) { struct pgsql_result *pg_result = (struct pgsql_result *)result; if (str_to_uint(PQcmdTuples(pg_result->pgres), query->affected_rows) < 0) i_unreached(); } ctx->callback(NULL, ctx->context); driver_pgsql_transaction_free(ctx); } static void driver_pgsql_transaction_commit(struct sql_transaction_context *_ctx, sql_commit_callback_t *callback, void *context) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)_ctx; ctx->callback = callback; ctx->context = context; if (ctx->failed || _ctx->head == NULL) { callback(ctx->failed ? ctx->error : NULL, context); driver_pgsql_transaction_free(ctx); } else if (_ctx->head->next == NULL) { /* just a single query, send it */ sql_query(_ctx->db, _ctx->head->query, transaction_trans_query_callback, _ctx->head); } else { /* multiple queries, use a transaction */ i_assert(_ctx->db->v.query == driver_pgsql_query); sql_query(_ctx->db, "BEGIN", transaction_begin_callback, ctx); } } static void commit_multi_fail(struct pgsql_transaction_context *ctx, struct sql_result *result, const char *query) { ctx->failed = TRUE; ctx->error = t_strdup_printf("%s (query: %s)", sql_result_get_error(result), query); sql_result_unref(result); } static struct sql_result * driver_pgsql_transaction_commit_multi(struct pgsql_transaction_context *ctx) { struct pgsql_db *db = (struct pgsql_db *)ctx->ctx.db; struct sql_result *result; struct sql_transaction_query *query; result = driver_pgsql_sync_query(db, "BEGIN"); if (sql_result_next_row(result) < 0) { commit_multi_fail(ctx, result, "BEGIN"); return NULL; } sql_result_unref(result); /* send queries */ for (query = ctx->ctx.head; query != NULL; query = query->next) { result = driver_pgsql_sync_query(db, query->query); if (sql_result_next_row(result) < 0) { commit_multi_fail(ctx, result, query->query); break; } if (query->affected_rows != NULL) { struct pgsql_result *pg_result = (struct pgsql_result *)result; if (str_to_uint(PQcmdTuples(pg_result->pgres), query->affected_rows) < 0) i_unreached(); } sql_result_unref(result); } return driver_pgsql_sync_query(db, ctx->failed ? "ROLLBACK" : "COMMIT"); } static void driver_pgsql_try_commit_s(struct pgsql_transaction_context *ctx, const char **error_r) { struct sql_transaction_context *_ctx = &ctx->ctx; struct pgsql_db *db = (struct pgsql_db *)_ctx->db; struct sql_transaction_query *single_query = NULL; struct sql_result *result; if (_ctx->head->next == NULL) { /* just a single query, send it */ single_query = _ctx->head; result = sql_query_s(_ctx->db, single_query->query); } else { /* multiple queries, use a transaction */ driver_pgsql_sync_init(db); result = driver_pgsql_transaction_commit_multi(ctx); driver_pgsql_sync_deinit(db); } if (ctx->failed) { i_assert(ctx->error != NULL); *error_r = ctx->error; } else if (result != NULL) { if (sql_result_next_row(result) < 0) *error_r = sql_result_get_error(result); else if (single_query != NULL && single_query->affected_rows != NULL) { struct pgsql_result *pg_result = (struct pgsql_result *)result; if (str_to_uint(PQcmdTuples(pg_result->pgres), single_query->affected_rows) < 0) i_unreached(); } } if (result != NULL) sql_result_unref(result); } static int driver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx, const char **error_r) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)_ctx; struct pgsql_db *db = (struct pgsql_db *)_ctx->db; *error_r = NULL; if (_ctx->head != NULL) { driver_pgsql_try_commit_s(ctx, error_r); if (_ctx->db->state == SQL_DB_STATE_DISCONNECTED) { *error_r = t_strdup(*error_r); i_info("%s: Disconnected from database, " "retrying commit", pgsql_prefix(db)); if (sql_connect(_ctx->db) >= 0) { ctx->failed = FALSE; *error_r = NULL; driver_pgsql_try_commit_s(ctx, error_r); } } } driver_pgsql_transaction_free(ctx); return *error_r == NULL ? 0 : -1; } static void driver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)_ctx; driver_pgsql_transaction_free(ctx); } static void driver_pgsql_update(struct sql_transaction_context *_ctx, const char *query, unsigned int *affected_rows) { struct pgsql_transaction_context *ctx = (struct pgsql_transaction_context *)_ctx; sql_transaction_add_query(_ctx, ctx->query_pool, query, affected_rows); } static const char * driver_pgsql_escape_blob(struct sql_db *_db ATTR_UNUSED, const unsigned char *data, size_t size) { string_t *str = t_str_new(128); str_append(str, "E'\\x"); binary_to_hex_append(str, data, size); str_append_c(str, '\''); return str_c(str); } const struct sql_db driver_pgsql_db = { .name = "pgsql", .flags = SQL_DB_FLAG_POOLED, .v = { .init = driver_pgsql_init_v, .deinit = driver_pgsql_deinit_v, .connect = driver_pgsql_connect, .disconnect = driver_pgsql_disconnect, .escape_string = driver_pgsql_escape_string, .exec = driver_pgsql_exec, .query = driver_pgsql_query, .query_s = driver_pgsql_query_s, .transaction_begin = driver_pgsql_transaction_begin, .transaction_commit = driver_pgsql_transaction_commit, .transaction_commit_s = driver_pgsql_transaction_commit_s, .transaction_rollback = driver_pgsql_transaction_rollback, .update = driver_pgsql_update, .escape_blob = driver_pgsql_escape_blob, } }; const struct sql_result driver_pgsql_result = { .v = { .free = driver_pgsql_result_free, .next_row = driver_pgsql_result_next_row, .get_fields_count = driver_pgsql_result_get_fields_count, .get_field_name = driver_pgsql_result_get_field_name, .find_field = driver_pgsql_result_find_field, .get_field_value = driver_pgsql_result_get_field_value, .get_field_value_binary = driver_pgsql_result_get_field_value_binary, .find_field_value = driver_pgsql_result_find_field_value, .get_values = driver_pgsql_result_get_values, .get_error = driver_pgsql_result_get_error, } }; const char *driver_pgsql_version = DOVECOT_ABI_VERSION; void driver_pgsql_init(void); void driver_pgsql_deinit(void); void driver_pgsql_init(void) { sql_driver_register(&driver_pgsql_db); } void driver_pgsql_deinit(void) { sql_driver_unregister(&driver_pgsql_db); } #endif dovecot-2.2.33.2/src/lib-sql/driver-cassandra.c0000644000175000017500000017460713172375226016102 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hex-binary.h" #include "str.h" #include "ioloop.h" #include "net.h" #include "write-full.h" #include "time-util.h" #include "var-expand.h" #include "settings-parser.h" #include "sql-api-private.h" #ifdef BUILD_CASSANDRA #include #include #include #define IS_CONNECTED(db) \ ((db)->api.state != SQL_DB_STATE_DISCONNECTED && \ (db)->api.state != SQL_DB_STATE_CONNECTING) #define CASSANDRA_FALLBACK_WARN_INTERVAL_SECS 60 #define CASSANDRA_FALLBACK_FIRST_RETRY_MSECS 50 #define CASSANDRA_FALLBACK_MAX_RETRY_MSECS (1000*60) #define CASS_QUERY_DEFAULT_WARN_TIMEOUT_SECS 5 typedef void driver_cassandra_callback_t(CassFuture *future, void *context); enum cassandra_counter_type { CASSANDRA_COUNTER_TYPE_QUERY_SENT, CASSANDRA_COUNTER_TYPE_QUERY_RECV_OK, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_NO_HOSTS, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_QUEUE_FULL, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_CLIENT_TIMEOUT, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_SERVER_TIMEOUT, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_SERVER_UNAVAILABLE, CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_OTHER, CASSANDRA_COUNTER_TYPE_QUERY_SLOW, CASSANDRA_COUNTER_COUNT }; static const char *counter_names[CASSANDRA_COUNTER_COUNT] = { "sent", "recv_ok", "recv_err_no_hosts", "recv_err_queue_full", "recv_err_client_timeout", "recv_err_server_timeout", "recv_err_server_unavailable", "recv_err_other", "slow", }; enum cassandra_query_type { CASSANDRA_QUERY_TYPE_READ, CASSANDRA_QUERY_TYPE_READ_MORE, CASSANDRA_QUERY_TYPE_WRITE, CASSANDRA_QUERY_TYPE_DELETE, CASSANDRA_QUERY_TYPE_COUNT }; static const char *cassandra_query_type_names[CASSANDRA_QUERY_TYPE_COUNT] = { "read", "read-more", "write", "delete" }; struct cassandra_callback { unsigned int id; CassFuture *future; struct cassandra_db *db; driver_cassandra_callback_t *callback; void *context; }; struct cassandra_db { struct sql_db api; char *hosts, *keyspace, *user, *password; CassConsistency read_consistency, write_consistency, delete_consistency; CassConsistency read_fallback_consistency, write_fallback_consistency, delete_fallback_consistency; CassLogLevel log_level; bool debug_queries; bool latency_aware_routing; unsigned int protocol_version; unsigned int num_threads; unsigned int connect_timeout_secs, request_timeout_secs; unsigned int warn_timeout_secs; unsigned int heartbeat_interval_secs, idle_timeout_secs; unsigned int execution_retry_interval_msecs, execution_retry_times; unsigned int page_size; in_port_t port; CassCluster *cluster; CassSession *session; CassTimestampGen *timestamp_gen; int fd_pipe[2]; struct io *io_pipe; ARRAY(struct cassandra_sql_prepared_statement *) pending_prepares; ARRAY(struct cassandra_callback *) callbacks; ARRAY(struct cassandra_result *) results; unsigned int callback_ids; char *metrics_path; struct timeout *to_metrics; uint64_t counters[CASSANDRA_COUNTER_COUNT]; struct timeval primary_query_last_sent[CASSANDRA_QUERY_TYPE_COUNT]; time_t last_fallback_warning[CASSANDRA_QUERY_TYPE_COUNT]; unsigned int fallback_failures[CASSANDRA_QUERY_TYPE_COUNT]; /* for synchronous queries: */ struct ioloop *ioloop, *orig_ioloop; struct sql_result *sync_result; char *error; }; struct cassandra_result { struct sql_result api; CassStatement *statement; const CassResult *result; CassIterator *iterator; char *query; char *error; CassConsistency consistency, fallback_consistency; enum cassandra_query_type query_type; struct timeval page0_start_time, start_time, finish_time; unsigned int row_count, total_row_count, page_num; pool_t row_pool; ARRAY_TYPE(const_string) fields; ARRAY(size_t) field_sizes; sql_query_callback_t *callback; void *context; unsigned int is_prepared:1; unsigned int query_sent:1; unsigned int finished:1; unsigned int paging_continues:1; }; struct cassandra_transaction_context { struct sql_transaction_context ctx; int refcount; sql_commit2_callback_t *callback; void *context; struct cassandra_sql_statement *stmt; char *query; char *error; unsigned int begin_succeeded:1; unsigned int begin_failed:1; unsigned int failed:1; }; struct cassandra_sql_arg { unsigned int column_idx; char *value_str; unsigned char *value_binary; size_t value_binary_size; int64_t value_int64; }; struct cassandra_sql_statement { struct sql_statement stmt; struct cassandra_sql_prepared_statement *prep; CassStatement *cass_stmt; ARRAY(struct cassandra_sql_arg) pending_args; cass_int64_t pending_timestamp; struct cassandra_result *result; }; struct cassandra_sql_prepared_statement { struct sql_prepared_statement prep_stmt; char *query_template; /* NULL, until the prepare is asynchronously finished */ const CassPrepared *prepared; /* statements waiting for prepare to finish */ ARRAY(struct cassandra_sql_statement *) pending_statements; /* an error here will cause the prepare to be retried on the next execution attempt. */ char *error; bool pending; }; extern const struct sql_db driver_cassandra_db; extern const struct sql_result driver_cassandra_result; static struct { CassConsistency consistency; const char *name; } cass_consistency_names[] = { { CASS_CONSISTENCY_ANY, "any" }, { CASS_CONSISTENCY_ONE, "one" }, { CASS_CONSISTENCY_TWO, "two" }, { CASS_CONSISTENCY_THREE, "three" }, { CASS_CONSISTENCY_QUORUM, "" }, { CASS_CONSISTENCY_ALL, "all" }, { CASS_CONSISTENCY_LOCAL_QUORUM, "local-quorum" }, { CASS_CONSISTENCY_EACH_QUORUM, "each-quorum" }, { CASS_CONSISTENCY_SERIAL, "serial" }, { CASS_CONSISTENCY_LOCAL_SERIAL, "local-serial" }, { CASS_CONSISTENCY_LOCAL_ONE, "local-one" } }; static struct { CassLogLevel log_level; const char *name; } cass_log_level_names[] = { { CASS_LOG_CRITICAL, "critical" }, { CASS_LOG_ERROR, "error" }, { CASS_LOG_WARN, "warn" }, { CASS_LOG_INFO, "info" }, { CASS_LOG_DEBUG, "debug" }, { CASS_LOG_TRACE, "trace" } }; static void driver_cassandra_prepare_pending(struct cassandra_db *db); static void prepare_finish_pending_statements(struct cassandra_sql_prepared_statement *prep_stmt); static void driver_cassandra_result_send_query(struct cassandra_result *result); static void driver_cassandra_send_queries(struct cassandra_db *db); static void result_finish(struct cassandra_result *result); static int consistency_parse(const char *str, CassConsistency *consistency_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(cass_consistency_names); i++) { if (strcmp(cass_consistency_names[i].name, str) == 0) { *consistency_r = cass_consistency_names[i].consistency; return 0; } } return -1; } static int log_level_parse(const char *str, CassLogLevel *log_level_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(cass_log_level_names); i++) { if (strcmp(cass_log_level_names[i].name, str) == 0) { *log_level_r = cass_log_level_names[i].log_level; return 0; } } return -1; } static void driver_cassandra_set_state(struct cassandra_db *db, enum sql_db_state state) { /* switch back to original ioloop in case the caller wants to add/remove timeouts */ if (db->ioloop != NULL) io_loop_set_current(db->orig_ioloop); sql_db_set_state(&db->api, state); if (db->ioloop != NULL) io_loop_set_current(db->ioloop); } static void driver_cassandra_close(struct cassandra_db *db, const char *error) { struct cassandra_sql_prepared_statement *const *prep_stmtp; struct cassandra_result *const *resultp; if (db->io_pipe != NULL) io_remove(&db->io_pipe); if (db->fd_pipe[0] != -1) { i_close_fd(&db->fd_pipe[0]); i_close_fd(&db->fd_pipe[1]); } driver_cassandra_set_state(db, SQL_DB_STATE_DISCONNECTED); array_foreach(&db->pending_prepares, prep_stmtp) { (*prep_stmtp)->pending = FALSE; (*prep_stmtp)->error = i_strdup(error); prepare_finish_pending_statements(*prep_stmtp); } array_clear(&db->pending_prepares); while (array_count(&db->results) > 0) { resultp = array_idx(&db->results, 0); if ((*resultp)->error == NULL) (*resultp)->error = i_strdup(error); result_finish(*resultp); } if (db->ioloop != NULL) { /* running a sync query, stop it */ io_loop_stop(db->ioloop); } } static void driver_cassandra_log_error(CassFuture *future, const char *str) { const char *message; size_t size; cass_future_error_message(future, &message, &size); i_error("cassandra: %s: %.*s", str, (int)size, message); } static void driver_cassandra_future_callback(CassFuture *future ATTR_UNUSED, void *context) { struct cassandra_callback *cb = context; /* this isn't the main thread - communicate with main thread by writing the callback id to the pipe. note that we must not use almost any dovecot functions here because most of them are using data-stack, which isn't thread-safe. especially don't use i_error() here. */ if (write_full(cb->db->fd_pipe[1], &cb->id, sizeof(cb->id)) < 0) { const char *str = t_strdup_printf( "cassandra: write(pipe) failed: %s\n", strerror(errno)); (void)write_full(STDERR_FILENO, str, strlen(str)); } } static void cassandra_callback_run(struct cassandra_callback *cb) { cb->callback(cb->future, cb->context); cass_future_free(cb->future); i_free(cb); } static void driver_cassandra_input_id(struct cassandra_db *db, unsigned int id) { struct cassandra_callback *cb, *const *cbp; /* usually there are only a few callbacks, so don't bother with using a hash table */ array_foreach(&db->callbacks, cbp) { cb = *cbp; if (cb->id == id) { array_delete(&db->callbacks, array_foreach_idx(&db->callbacks, cbp), 1); cassandra_callback_run(cb); return; } } i_panic("cassandra: Received unknown ID %u", id); } static void driver_cassandra_input(struct cassandra_db *db) { unsigned int ids[1024]; ssize_t ret; ret = read(db->fd_pipe[0], ids, sizeof(ids)); if (ret < 0) i_error("cassandra: read(pipe) failed: %m"); else if (ret == 0) i_error("cassandra: read(pipe) failed: EOF"); else if (ret % sizeof(ids[0]) != 0) i_error("cassandra: read(pipe) returned wrong amount of data"); else { /* success */ unsigned int i, count = ret / sizeof(ids[0]); for (i = 0; i < count && db->api.state != SQL_DB_STATE_DISCONNECTED; i++) driver_cassandra_input_id(db, ids[i]); return; } driver_cassandra_close(db, "IPC pipe closed"); } static void driver_cassandra_set_callback(CassFuture *future, struct cassandra_db *db, driver_cassandra_callback_t *callback, void *context) { struct cassandra_callback *cb; cb = i_new(struct cassandra_callback, 1); cb->id = ++db->callback_ids; cb->future = future; cb->callback = callback; cb->context = context; cb->db = db; array_append(&db->callbacks, &cb, 1); cass_future_set_callback(future, driver_cassandra_future_callback, cb); } static void connect_callback(CassFuture *future, void *context) { struct cassandra_db *db = context; CassError rc; if ((rc = cass_future_error_code(future)) != CASS_OK) { driver_cassandra_log_error(future, "Couldn't connect to Cassandra"); driver_cassandra_close(db, "Couldn't connect to Cassandra"); return; } driver_cassandra_set_state(db, SQL_DB_STATE_IDLE); if (db->ioloop != NULL) { /* driver_cassandra_sync_init() waiting for connection to finish */ io_loop_stop(db->ioloop); } driver_cassandra_prepare_pending(db); driver_cassandra_send_queries(db); } static int driver_cassandra_connect(struct sql_db *_db) { struct cassandra_db *db = (struct cassandra_db *)_db; CassFuture *future; i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED); if (pipe(db->fd_pipe) < 0) { i_error("pipe() failed: %m"); return -1; } db->io_pipe = io_add(db->fd_pipe[0], IO_READ, driver_cassandra_input, db); driver_cassandra_set_state(db, SQL_DB_STATE_CONNECTING); future = cass_session_connect_keyspace(db->session, db->cluster, db->keyspace); driver_cassandra_set_callback(future, db, connect_callback, db); return 0; } static void driver_cassandra_disconnect(struct sql_db *_db) { struct cassandra_db *db = (struct cassandra_db *)_db; driver_cassandra_close(db, "Disconnected"); } static const char * driver_cassandra_escape_string(struct sql_db *db ATTR_UNUSED, const char *string) { string_t *escaped; unsigned int i; if (strchr(string, '\'') == NULL) return string; escaped = t_str_new(strlen(string)+10); for (i = 0; string[i] != '\0'; i++) { if (string[i] == '\'') str_append_c(escaped, '\''); str_append_c(escaped, string[i]); } return str_c(escaped); } static void driver_cassandra_parse_connect_string(struct cassandra_db *db, const char *connect_string) { const char *const *args, *key, *value, *error; string_t *hosts = t_str_new(64); bool read_fallback_set = FALSE, write_fallback_set = FALSE, delete_fallback_set = FALSE; db->log_level = CASS_LOG_WARN; db->read_consistency = CASS_CONSISTENCY_LOCAL_QUORUM; db->write_consistency = CASS_CONSISTENCY_LOCAL_QUORUM; db->delete_consistency = CASS_CONSISTENCY_LOCAL_QUORUM; db->connect_timeout_secs = SQL_CONNECT_TIMEOUT_SECS; db->request_timeout_secs = SQL_QUERY_TIMEOUT_SECS; db->warn_timeout_secs = CASS_QUERY_DEFAULT_WARN_TIMEOUT_SECS; args = t_strsplit_spaces(connect_string, " "); for (; *args != NULL; args++) { value = strchr(*args, '='); if (value == NULL) { i_fatal("cassandra: Missing value in connect string: %s", *args); } key = t_strdup_until(*args, value++); if (strcmp(key, "host") == 0) { if (str_len(hosts) > 0) str_append_c(hosts, ','); str_append(hosts, value); } else if (strcmp(key, "port") == 0) { if (net_str2port(value, &db->port) < 0) i_fatal("cassandra: Invalid port: %s", value); } else if (strcmp(key, "dbname") == 0 || strcmp(key, "keyspace") == 0) { i_free(db->keyspace); db->keyspace = i_strdup(value); } else if (strcmp(key, "user") == 0) { i_free(db->user); db->user = i_strdup(value); } else if (strcmp(key, "password") == 0) { i_free(db->password); db->password = i_strdup(value); } else if (strcmp(key, "read_consistency") == 0) { if (consistency_parse(value, &db->read_consistency) < 0) i_fatal("cassandra: Unknown read_consistency: %s", value); } else if (strcmp(key, "read_fallback_consistency") == 0) { if (consistency_parse(value, &db->read_fallback_consistency) < 0) i_fatal("cassandra: Unknown read_fallback_consistency: %s", value); read_fallback_set = TRUE; } else if (strcmp(key, "write_consistency") == 0) { if (consistency_parse(value, &db->write_consistency) < 0) i_fatal("cassandra: Unknown write_consistency: %s", value); } else if (strcmp(key, "write_fallback_consistency") == 0) { if (consistency_parse(value, &db->write_fallback_consistency) < 0) i_fatal("cassandra: Unknown write_fallback_consistency: %s", value); write_fallback_set = TRUE; } else if (strcmp(key, "delete_consistency") == 0) { if (consistency_parse(value, &db->delete_consistency) < 0) i_fatal("cassandra: Unknown delete_consistency: %s", value); } else if (strcmp(key, "delete_fallback_consistency") == 0) { if (consistency_parse(value, &db->delete_fallback_consistency) < 0) i_fatal("cassandra: Unknown delete_fallback_consistency: %s", value); delete_fallback_set = TRUE; } else if (strcmp(key, "log_level") == 0) { if (log_level_parse(value, &db->log_level) < 0) i_fatal("cassandra: Unknown log_level: %s", value); } else if (strcmp(key, "debug_queries") == 0) { db->debug_queries = TRUE; } else if (strcmp(key, "latency_aware_routing") == 0) { db->latency_aware_routing = TRUE; } else if (strcmp(key, "version") == 0) { if (str_to_uint(value, &db->protocol_version) < 0) i_fatal("cassandra: Invalid version: %s", value); } else if (strcmp(key, "num_threads") == 0) { if (str_to_uint(value, &db->num_threads) < 0) i_fatal("cassandra: Invalid num_threads: %s", value); } else if (strcmp(key, "heartbeat_interval") == 0) { if (settings_get_time(value, &db->heartbeat_interval_secs, &error) < 0) i_fatal("cassandra: Invalid heartbeat_interval '%s': %s", value, error); } else if (strcmp(key, "idle_timeout") == 0) { if (settings_get_time(value, &db->idle_timeout_secs, &error) < 0) i_fatal("cassandra: Invalid idle_timeout '%s': %s", value, error); } else if (strcmp(key, "connect_timeout") == 0) { if (settings_get_time(value, &db->connect_timeout_secs, &error) < 0) i_fatal("cassandra: Invalid connect_timeout '%s': %s", value, error); } else if (strcmp(key, "request_timeout") == 0) { if (settings_get_time(value, &db->request_timeout_secs, &error) < 0) i_fatal("cassandra: Invalid request_timeout '%s': %s", value, error); } else if (strcmp(key, "warn_timeout") == 0) { if (settings_get_time(value, &db->warn_timeout_secs, &error) < 0) i_fatal("cassandra: Invalid warn_timeout '%s': %s", value, error); } else if (strcmp(key, "metrics") == 0) { i_free(db->metrics_path); db->metrics_path = i_strdup(value); } else if (strcmp(key, "execution_retry_interval") == 0) { if (settings_get_time_msecs(value, &db->execution_retry_interval_msecs, &error) < 0) i_fatal("cassandra: Invalid execution_retry_interval '%s': %s", value, error); #ifndef HAVE_CASSANDRA_SPECULATIVE_POLICY i_fatal("cassandra: This cassandra version does not support execution_retry_interval"); #endif } else if (strcmp(key, "execution_retry_times") == 0) { if (str_to_uint(value, &db->execution_retry_times) < 0) i_fatal("cassandra: Invalid execution_retry_times %s", value); #ifndef HAVE_CASSANDRA_SPECULATIVE_POLICY i_fatal("cassandra: This cassandra version does not support execution_retry_times"); #endif } else if (strcmp(key, "page_size") == 0) { if (str_to_uint(value, &db->page_size) < 0) i_fatal("cassandra: Invalid page_size: %s", value); } else { i_fatal("cassandra: Unknown connect string: %s", key); } } if (!read_fallback_set) db->read_fallback_consistency = db->read_consistency; if (!write_fallback_set) db->write_fallback_consistency = db->write_consistency; if (!delete_fallback_set) db->delete_fallback_consistency = db->delete_consistency; if (str_len(hosts) == 0) i_fatal("cassandra: No hosts given in connect string"); if (db->keyspace == NULL) i_fatal("cassandra: No dbname given in connect string"); db->hosts = i_strdup(str_c(hosts)); } static void driver_cassandra_get_metrics_json(struct cassandra_db *db, string_t *dest) { #define ADD_UINT64(_struct, _field) \ str_printfa(dest, "\""#_field"\": %llu,", (unsigned long long)metrics._struct._field); #define ADD_DOUBLE(_struct, _field) \ str_printfa(dest, "\""#_field"\": %02lf,", metrics._struct._field); CassMetrics metrics; cass_session_get_metrics(db->session, &metrics); str_append(dest, "{ \"requests\": {"); ADD_UINT64(requests, min); ADD_UINT64(requests, max); ADD_UINT64(requests, mean); ADD_UINT64(requests, stddev); ADD_UINT64(requests, median); ADD_UINT64(requests, percentile_75th); ADD_UINT64(requests, percentile_95th); ADD_UINT64(requests, percentile_98th); ADD_UINT64(requests, percentile_99th); ADD_UINT64(requests, percentile_999th); ADD_DOUBLE(requests, mean_rate); ADD_DOUBLE(requests, one_minute_rate); ADD_DOUBLE(requests, five_minute_rate); ADD_DOUBLE(requests, fifteen_minute_rate); str_truncate(dest, str_len(dest)-1); str_append(dest, "}, \"stats\": {"); ADD_UINT64(stats, total_connections); ADD_UINT64(stats, available_connections); ADD_UINT64(stats, exceeded_pending_requests_water_mark); ADD_UINT64(stats, exceeded_write_bytes_water_mark); str_truncate(dest, str_len(dest)-1); str_append(dest, "}, \"errors\": {"); ADD_UINT64(errors, connection_timeouts); ADD_UINT64(errors, pending_request_timeouts); ADD_UINT64(errors, request_timeouts); str_truncate(dest, str_len(dest)-1); str_append(dest, "}, \"queries\": {"); for (unsigned int i = 0; i < CASSANDRA_COUNTER_COUNT; i++) { str_printfa(dest, "\"%s\": %llu,", counter_names[i], (unsigned long long)db->counters[i]); } str_truncate(dest, str_len(dest)-1); str_append(dest, "}}"); } static void driver_cassandra_metrics_write(struct cassandra_db *db) { struct var_expand_table tab[] = { { '\0', NULL, NULL } }; string_t *path = t_str_new(64); string_t *data; int fd; var_expand(path, db->metrics_path, tab); fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600); if (fd == -1) { i_error("creat(%s) failed: %m", str_c(path)); return; } data = t_str_new(1024); driver_cassandra_get_metrics_json(db, data); if (write_full(fd, str_data(data), str_len(data)) < 0) i_error("write(%s) failed: %m", str_c(path)); i_close_fd(&fd); } static struct sql_db *driver_cassandra_init_v(const char *connect_string) { struct cassandra_db *db; db = i_new(struct cassandra_db, 1); db->api = driver_cassandra_db; db->fd_pipe[0] = db->fd_pipe[1] = -1; T_BEGIN { driver_cassandra_parse_connect_string(db, connect_string); } T_END; cass_log_set_level(db->log_level); if (db->protocol_version > 0 && db->protocol_version < 4) { /* binding with column indexes requires v4 */ db->api.v.prepared_statement_init = NULL; db->api.v.prepared_statement_deinit = NULL; db->api.v.statement_init_prepared = NULL; } db->timestamp_gen = cass_timestamp_gen_monotonic_new(); db->cluster = cass_cluster_new(); cass_cluster_set_timestamp_gen(db->cluster, db->timestamp_gen); cass_cluster_set_connect_timeout(db->cluster, db->connect_timeout_secs * 1000); cass_cluster_set_request_timeout(db->cluster, db->request_timeout_secs * 1000); cass_cluster_set_contact_points(db->cluster, db->hosts); if (db->user != NULL && db->password != NULL) cass_cluster_set_credentials(db->cluster, db->user, db->password); if (db->port != 0) cass_cluster_set_port(db->cluster, db->port); if (db->protocol_version != 0) cass_cluster_set_protocol_version(db->cluster, db->protocol_version); if (db->num_threads != 0) cass_cluster_set_num_threads_io(db->cluster, db->num_threads); if (db->latency_aware_routing) cass_cluster_set_latency_aware_routing(db->cluster, cass_true); if (db->heartbeat_interval_secs != 0) cass_cluster_set_connection_heartbeat_interval(db->cluster, db->heartbeat_interval_secs); if (db->idle_timeout_secs != 0) cass_cluster_set_connection_idle_timeout(db->cluster, db->idle_timeout_secs); #ifdef HAVE_CASSANDRA_SPECULATIVE_POLICY if (db->execution_retry_times > 0 && db->execution_retry_interval_msecs > 0) cass_cluster_set_constant_speculative_execution_policy(db->cluster, db->execution_retry_interval_msecs, db->execution_retry_times); #endif db->session = cass_session_new(); if (db->metrics_path != NULL) db->to_metrics = timeout_add(1000, driver_cassandra_metrics_write, db); i_array_init(&db->results, 16); i_array_init(&db->callbacks, 16); i_array_init(&db->pending_prepares, 16); return &db->api; } static void driver_cassandra_deinit_v(struct sql_db *_db) { struct cassandra_db *db = (struct cassandra_db *)_db; driver_cassandra_close(db, "Deinitialized"); i_assert(array_count(&db->callbacks) == 0); array_free(&db->callbacks); i_assert(array_count(&db->results) == 0); array_free(&db->results); i_assert(array_count(&db->pending_prepares) == 0); array_free(&db->pending_prepares); cass_session_free(db->session); cass_cluster_free(db->cluster); cass_timestamp_gen_free(db->timestamp_gen); if (db->to_metrics != NULL) timeout_remove(&db->to_metrics); i_free(db->metrics_path); i_free(db->hosts); i_free(db->error); i_free(db->keyspace); i_free(db->user); i_free(db->password); array_free(&_db->module_contexts); i_free(db); } static void driver_cassandra_result_unlink(struct cassandra_db *db, struct cassandra_result *result) { struct cassandra_result *const *results; unsigned int i, count; results = array_get(&db->results, &count); for (i = 0; i < count; i++) { if (results[i] == result) { array_delete(&db->results, i, 1); return; } } i_unreached(); } static void driver_cassandra_log_result(struct cassandra_result *result, bool all_pages, long long reply_usecs) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; struct timeval now; unsigned int row_count; if (db->log_level < CASS_LOG_DEBUG && !db->debug_queries && reply_usecs/1000000 < db->warn_timeout_secs) return; if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); string_t *str = t_str_new(128); str_printfa(str, "cassandra: Finished %squery '%s' (", result->is_prepared ? "prepared " : "", result->query); if (all_pages) { str_printfa(str, "%u pages in total, ", result->page_num); row_count = result->total_row_count; } else { if (result->page_num > 0 || result->paging_continues) str_printfa(str, "page %u, ", result->page_num); row_count = result->row_count; } str_printfa(str, "%u rows, %lld+%lld us): %s", row_count, reply_usecs, timeval_diff_usecs(&now, &result->finish_time), result->error != NULL ? result->error : "success"); if (reply_usecs/1000000 >= db->warn_timeout_secs) { db->counters[CASSANDRA_COUNTER_TYPE_QUERY_SLOW]++; i_warning("%s", str_c(str)); } else { i_debug("%s", str_c(str)); } } static void driver_cassandra_result_free(struct sql_result *_result) { struct cassandra_db *db = (struct cassandra_db *)_result->db; struct cassandra_result *result = (struct cassandra_result *)_result; long long reply_usecs; i_assert(!result->api.callback); i_assert(result->callback == NULL); if (_result == db->sync_result) db->sync_result = NULL; reply_usecs = timeval_diff_usecs(&result->finish_time, &result->start_time); driver_cassandra_log_result(result, FALSE, reply_usecs); if (result->page_num > 0 && !result->paging_continues) { /* Multi-page query finishes now. Log a debug/warning summary message about it separate from the per-page messages. */ reply_usecs = timeval_diff_usecs(&result->finish_time, &result->page0_start_time); driver_cassandra_log_result(result, TRUE, reply_usecs); } if (result->result != NULL) cass_result_free(result->result); if (result->iterator != NULL) cass_iterator_free(result->iterator); if (result->statement != NULL) cass_statement_free(result->statement); if (result->row_pool != NULL) pool_unref(&result->row_pool); i_free(result->query); i_free(result->error); i_free(result); } static void result_finish(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; bool free_result = TRUE; result->finished = TRUE; result->finish_time = ioloop_timeval; driver_cassandra_result_unlink(db, result); i_assert((result->error != NULL) == (result->iterator == NULL)); result->api.callback = TRUE; T_BEGIN { result->callback(&result->api, result->context); } T_END; result->api.callback = FALSE; free_result = db->sync_result != &result->api; if (db->ioloop != NULL) io_loop_stop(db->ioloop); i_assert(!free_result || result->api.refcount > 0); result->callback = NULL; if (free_result) sql_result_unref(&result->api); } static void query_resend_with_fallback(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; time_t last_warning = ioloop_time - db->last_fallback_warning[result->query_type]; if (last_warning >= CASSANDRA_FALLBACK_WARN_INTERVAL_SECS) { i_warning("%s - retrying future %s queries with consistency %s (instead of %s)", result->error, cassandra_query_type_names[result->query_type], cass_consistency_string(result->fallback_consistency), cass_consistency_string(result->consistency)); db->last_fallback_warning[result->query_type] = ioloop_time; } i_free_and_null(result->error); db->fallback_failures[result->query_type]++; result->consistency = result->fallback_consistency; driver_cassandra_result_send_query(result); } static void counters_inc_error(struct cassandra_db *db, CassError error) { switch (error) { case CASS_ERROR_LIB_NO_HOSTS_AVAILABLE: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_NO_HOSTS]++; break; case CASS_ERROR_LIB_REQUEST_QUEUE_FULL: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_QUEUE_FULL]++; break; case CASS_ERROR_LIB_REQUEST_TIMED_OUT: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_CLIENT_TIMEOUT]++; break; case CASS_ERROR_SERVER_WRITE_TIMEOUT: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_SERVER_TIMEOUT]++; break; case CASS_ERROR_SERVER_UNAVAILABLE: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_SERVER_UNAVAILABLE]++; break; default: db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_ERR_OTHER]++; break; } } static void query_callback(CassFuture *future, void *context) { struct cassandra_result *result = context; struct cassandra_db *db = (struct cassandra_db *)result->api.db; CassError error = cass_future_error_code(future); if (error != CASS_OK) { const char *errmsg; size_t errsize; int msecs; cass_future_error_message(future, &errmsg, &errsize); i_free(result->error); msecs = timeval_diff_msecs(&ioloop_timeval, &result->start_time); counters_inc_error(db, error); /* Timeouts bring uncertainty whether the query succeeded or not. Also _SERVER_UNAVAILABLE could have actually written enough copies of the data for the query to succeed. */ result->api.error_type = error == CASS_ERROR_SERVER_WRITE_TIMEOUT || error == CASS_ERROR_SERVER_UNAVAILABLE || error == CASS_ERROR_LIB_REQUEST_TIMED_OUT ? SQL_RESULT_ERROR_TYPE_WRITE_UNCERTAIN : SQL_RESULT_ERROR_TYPE_UNKNOWN; result->error = i_strdup_printf("Query '%s' failed: %.*s (in %u.%03u secs%s)", result->query, (int)errsize, errmsg, msecs/1000, msecs%1000, result->page_num == 0 ? "" : t_strdup_printf(", page %u", result->page_num)); /* unavailable = cassandra server knows that there aren't enough nodes available. "All hosts in current policy attempted and were either unavailable or failed" no hosts available = The client library couldn't connect to enough cassanra nodes. Error message is the same as for "unavailable". write timeout = cassandra server couldn't reach all the needed nodes. this may be because it hasn't yet detected that the servers are down, or because the servers are just too busy. we'll try the fallback consistency to avoid unnecessary temporary errors. */ if ((error == CASS_ERROR_SERVER_UNAVAILABLE || error == CASS_ERROR_LIB_NO_HOSTS_AVAILABLE || error == CASS_ERROR_SERVER_WRITE_TIMEOUT) && result->fallback_consistency != result->consistency) { /* retry with fallback consistency */ query_resend_with_fallback(result); return; } result_finish(result); return; } db->counters[CASSANDRA_COUNTER_TYPE_QUERY_RECV_OK]++; if (result->fallback_consistency != result->consistency) { /* non-fallback query finished successfully. if there had been any fallbacks, reset them. */ db->fallback_failures[result->query_type] = 0; } result->result = cass_future_get_result(future); result->iterator = cass_iterator_from_result(result->result); result_finish(result); } static void driver_cassandra_init_statement(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; cass_statement_set_consistency(result->statement, result->consistency); #ifdef HAVE_CASSANDRA_SPECULATIVE_POLICY cass_statement_set_is_idempotent(result->statement, cass_true); #endif if (db->page_size > 0) cass_statement_set_paging_size(result->statement, db->page_size); } static void driver_cassandra_result_send_query(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; CassFuture *future; i_assert(result->statement != NULL); db->counters[CASSANDRA_COUNTER_TYPE_QUERY_SENT]++; if (result->query_type != CASSANDRA_QUERY_TYPE_READ_MORE) driver_cassandra_init_statement(result); future = cass_session_execute(db->session, result->statement); driver_cassandra_set_callback(future, db, query_callback, result); } static bool driver_cassandra_want_fallback_query(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; unsigned int failure_count = db->fallback_failures[result->query_type]; unsigned int i, msecs = CASSANDRA_FALLBACK_FIRST_RETRY_MSECS; struct timeval tv; if (failure_count == 0) return FALSE; /* double the retries every time. */ for (i = 1; i < failure_count; i++) { msecs *= 2; if (msecs >= CASSANDRA_FALLBACK_MAX_RETRY_MSECS) { msecs = CASSANDRA_FALLBACK_MAX_RETRY_MSECS; break; } } /* If last primary query sent timestamp + msecs is older than current time, we need to retry the primary query. Note that this practically prevents multiple primary queries from being attempted simultaneously, because the caller updates primary_query_last_sent immediately when returning. The only time when multiple primary queries can be running in parallel is when the earlier query is being slow and hasn't finished early enough. This could even be a wanted feature, since while the first query might have to wait for a timeout, Cassandra could have been fixed in the meantime and the second query finishes successfully. */ tv = db->primary_query_last_sent[result->query_type]; timeval_add_msecs(&tv, msecs); return timeval_cmp(&ioloop_timeval, &tv) < 0; } static int driver_cassandra_send_query(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; int ret; if (!SQL_DB_IS_READY(&db->api)) { if ((ret = sql_connect(&db->api)) <= 0) { if (ret < 0) driver_cassandra_close(db, "Couldn't connect to Cassandra"); return ret; } } if (result->page0_start_time.tv_sec == 0) result->page0_start_time = ioloop_timeval; result->start_time = ioloop_timeval; result->row_pool = pool_alloconly_create("cassandra result", 512); switch (result->query_type) { case CASSANDRA_QUERY_TYPE_READ: result->consistency = db->read_consistency; result->fallback_consistency = db->read_fallback_consistency; break; case CASSANDRA_QUERY_TYPE_READ_MORE: /* consistency is already set and we don't want to fallback at this point anymore. */ result->fallback_consistency = result->consistency; break; case CASSANDRA_QUERY_TYPE_WRITE: result->consistency = db->write_consistency; result->fallback_consistency = db->write_fallback_consistency; break; case CASSANDRA_QUERY_TYPE_DELETE: result->consistency = db->delete_consistency; result->fallback_consistency = db->delete_fallback_consistency; break; case CASSANDRA_QUERY_TYPE_COUNT: i_unreached(); } if (driver_cassandra_want_fallback_query(result)) result->consistency = result->fallback_consistency; else db->primary_query_last_sent[result->query_type] = ioloop_timeval; driver_cassandra_result_send_query(result); result->query_sent = TRUE; return 1; } static void driver_cassandra_send_queries(struct cassandra_db *db) { struct cassandra_result *const *results; unsigned int i, count; results = array_get(&db->results, &count); for (i = 0; i < count; i++) { if (!results[i]->query_sent && results[i]->statement != NULL) { if (driver_cassandra_send_query(results[i]) <= 0) break; } } } static void exec_callback(struct sql_result *_result ATTR_UNUSED, void *context ATTR_UNUSED) { } static struct cassandra_result * driver_cassandra_query_init(struct cassandra_db *db, const char *query, enum cassandra_query_type query_type, bool is_prepared, sql_query_callback_t *callback, void *context) { struct cassandra_result *result; result = i_new(struct cassandra_result, 1); result->api = driver_cassandra_result; result->api.db = &db->api; result->api.refcount = 1; result->callback = callback; result->context = context; result->query_type = query_type; result->query = i_strdup(query); result->is_prepared = is_prepared; array_append(&db->results, &result, 1); return result; } static void driver_cassandra_query_full(struct sql_db *_db, const char *query, enum cassandra_query_type query_type, sql_query_callback_t *callback, void *context) { struct cassandra_db *db = (struct cassandra_db *)_db; struct cassandra_result *result; result = driver_cassandra_query_init(db, query, query_type, FALSE, callback, context); result->statement = cass_statement_new(query, 0); (void)driver_cassandra_send_query(result); } static void driver_cassandra_exec(struct sql_db *db, const char *query) { driver_cassandra_query_full(db, query, CASSANDRA_QUERY_TYPE_WRITE, exec_callback, NULL); } static void driver_cassandra_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { driver_cassandra_query_full(db, query, CASSANDRA_QUERY_TYPE_READ, callback, context); } static void cassandra_query_s_callback(struct sql_result *result, void *context) { struct cassandra_db *db = context; db->sync_result = result; } static void driver_cassandra_sync_init(struct cassandra_db *db) { if (sql_connect(&db->api) < 0) return; db->orig_ioloop = current_ioloop; db->ioloop = io_loop_create(); if (IS_CONNECTED(db)) return; i_assert(db->api.state == SQL_DB_STATE_CONNECTING); db->io_pipe = io_loop_move_io(&db->io_pipe); /* wait for connecting to finish */ io_loop_run(db->ioloop); } static void driver_cassandra_sync_deinit(struct cassandra_db *db) { if (db->orig_ioloop == NULL) return; if (db->io_pipe != NULL) { io_loop_set_current(db->orig_ioloop); db->io_pipe = io_loop_move_io(&db->io_pipe); io_loop_set_current(db->ioloop); } io_loop_destroy(&db->ioloop); } static struct sql_result * driver_cassandra_sync_query(struct cassandra_db *db, const char *query, enum cassandra_query_type query_type) { struct sql_result *result; i_assert(db->sync_result == NULL); switch (db->api.state) { case SQL_DB_STATE_CONNECTING: case SQL_DB_STATE_BUSY: i_unreached(); case SQL_DB_STATE_DISCONNECTED: sql_not_connected_result.refcount++; return &sql_not_connected_result; case SQL_DB_STATE_IDLE: break; } driver_cassandra_query_full(&db->api, query, query_type, cassandra_query_s_callback, db); if (db->sync_result == NULL) { db->io_pipe = io_loop_move_io(&db->io_pipe); io_loop_run(db->ioloop); } result = db->sync_result; if (result == &sql_not_connected_result) { /* we don't end up in cassandra's free function, so sync_result won't be set to NULL if we don't do it here. */ db->sync_result = NULL; } else if (result == NULL) { result = &sql_not_connected_result; result->refcount++; } return result; } static struct sql_result * driver_cassandra_query_s(struct sql_db *_db, const char *query) { struct cassandra_db *db = (struct cassandra_db *)_db; struct sql_result *result; driver_cassandra_sync_init(db); result = driver_cassandra_sync_query(db, query, CASSANDRA_QUERY_TYPE_READ); driver_cassandra_sync_deinit(db); return result; } static int driver_cassandra_get_value(struct cassandra_result *result, const CassValue *value, const char **str_r, size_t *len_r) { const unsigned char *output; void *output_dup; size_t output_size; CassError rc; const char *type; if (cass_value_is_null(value)) { *str_r = NULL; return 0; } switch (cass_data_type_type(cass_value_data_type(value))) { case CASS_VALUE_TYPE_INT: { cass_int32_t num; rc = cass_value_get_int32(value, &num); if (rc == CASS_OK) { const char *str = t_strdup_printf("%d", num); output_size = strlen(str); output = (const void *)str; } type = "int32"; break; } case CASS_VALUE_TYPE_TIMESTAMP: case CASS_VALUE_TYPE_BIGINT: { cass_int64_t num; rc = cass_value_get_int64(value, &num); if (rc == CASS_OK) { const char *str = t_strdup_printf("%lld", (long long)num); output_size = strlen(str); output = (const void *)str; } type = "int64"; break; } default: rc = cass_value_get_bytes(value, &output, &output_size); type = "bytes"; break; } if (rc != CASS_OK) { i_free(result->error); result->error = i_strdup_printf("Couldn't get value as %s: %s", type, cass_error_desc(rc)); return -1; } output_dup = p_malloc(result->row_pool, output_size + 1); memcpy(output_dup, output, output_size); *str_r = output_dup; *len_r = output_size; return 0; } static int driver_cassandra_result_next_page(struct cassandra_result *result) { struct cassandra_db *db = (struct cassandra_db *)result->api.db; if (db->page_size == 0) { /* no paging */ return 0; } if (cass_result_has_more_pages(result->result) == cass_false) return 0; /* callers that don't support sql_query_more() will still get a useful error message. */ i_free(result->error); result->error = i_strdup("Paged query has more results, but not supported by the caller"); return SQL_RESULT_NEXT_MORE; } static int driver_cassandra_result_next_row(struct sql_result *_result) { struct cassandra_result *result = (struct cassandra_result *)_result; const CassRow *row; const CassValue *value; const char *str; size_t size; unsigned int i; int ret = 1; if (result->iterator == NULL) return -1; if (!cass_iterator_next(result->iterator)) return driver_cassandra_result_next_page(result); result->row_count++; result->total_row_count++; p_clear(result->row_pool); p_array_init(&result->fields, result->row_pool, 8); p_array_init(&result->field_sizes, result->row_pool, 8); row = cass_iterator_get_row(result->iterator); for (i = 0; (value = cass_row_get_column(row, i)) != NULL; i++) { if (driver_cassandra_get_value(result, value, &str, &size) < 0) { ret = -1; break; } array_append(&result->fields, &str, 1); array_append(&result->field_sizes, &size, 1); } return ret; } static void driver_cassandra_result_more(struct sql_result **_result, bool async, sql_query_callback_t *callback, void *context) { struct cassandra_db *db = (struct cassandra_db *)(*_result)->db; struct cassandra_result *new_result; struct cassandra_result *old_result = (struct cassandra_result *)*_result; /* Initialize the next page as a new sql_result */ new_result = driver_cassandra_query_init(db, old_result->query, CASSANDRA_QUERY_TYPE_READ_MORE, old_result->is_prepared, callback, context); /* Preserve the statement and update its paging state */ new_result->statement = old_result->statement; old_result->statement = NULL; cass_statement_set_paging_state(new_result->statement, old_result->result); old_result->paging_continues = TRUE; /* The caller did support paging. Clear out the "...not supported by the caller" error text, so it won't be in the debug log output. */ i_free_and_null(old_result->error); new_result->consistency = old_result->consistency; new_result->page_num = old_result->page_num + 1; new_result->page0_start_time = old_result->page0_start_time; new_result->total_row_count = old_result->total_row_count; sql_result_unref(*_result); *_result = NULL; if (async) (void)driver_cassandra_send_query(new_result); else { i_assert(db->api.state == SQL_DB_STATE_IDLE); driver_cassandra_sync_init(db); (void)driver_cassandra_send_query(new_result); if (new_result->result == NULL) { db->io_pipe = io_loop_move_io(&db->io_pipe); io_loop_run(db->ioloop); } driver_cassandra_sync_deinit(db); callback(&new_result->api, context); } } static unsigned int driver_cassandra_result_get_fields_count(struct sql_result *_result) { struct cassandra_result *result = (struct cassandra_result *)_result; return array_count(&result->fields); } static const char * driver_cassandra_result_get_field_name(struct sql_result *_result ATTR_UNUSED, unsigned int idx ATTR_UNUSED) { i_unreached(); } static int driver_cassandra_result_find_field(struct sql_result *_result ATTR_UNUSED, const char *field_name ATTR_UNUSED) { i_unreached(); } static const char * driver_cassandra_result_get_field_value(struct sql_result *_result, unsigned int idx) { struct cassandra_result *result = (struct cassandra_result *)_result; const char *const *strp; strp = array_idx(&result->fields, idx); return *strp; } static const unsigned char * driver_cassandra_result_get_field_value_binary(struct sql_result *_result ATTR_UNUSED, unsigned int idx ATTR_UNUSED, size_t *size_r ATTR_UNUSED) { struct cassandra_result *result = (struct cassandra_result *)_result; const char *const *strp; const size_t *sizep; strp = array_idx(&result->fields, idx); sizep = array_idx(&result->field_sizes, idx); *size_r = *sizep; return (const void *)*strp; } static const char * driver_cassandra_result_find_field_value(struct sql_result *result ATTR_UNUSED, const char *field_name ATTR_UNUSED) { i_unreached(); } static const char *const * driver_cassandra_result_get_values(struct sql_result *_result) { struct cassandra_result *result = (struct cassandra_result *)_result; return array_idx(&result->fields, 0); } static const char *driver_cassandra_result_get_error(struct sql_result *_result) { struct cassandra_result *result = (struct cassandra_result *)_result; if (result->error != NULL) return result->error; return "FIXME"; } static struct sql_transaction_context * driver_cassandra_transaction_begin(struct sql_db *db) { struct cassandra_transaction_context *ctx; ctx = i_new(struct cassandra_transaction_context, 1); ctx->ctx.db = db; ctx->refcount = 1; return &ctx->ctx; } static void driver_cassandra_transaction_unref(struct cassandra_transaction_context **_ctx) { struct cassandra_transaction_context *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->refcount > 0); if (--ctx->refcount > 0) return; i_free(ctx->query); i_free(ctx->error); i_free(ctx); } static void transaction_set_failed(struct cassandra_transaction_context *ctx, const char *error) { if (ctx->failed) { i_assert(ctx->error != NULL); } else { i_assert(ctx->error == NULL); ctx->failed = TRUE; ctx->error = i_strdup(error); } } static void transaction_commit_callback(struct sql_result *result, void *context) { struct cassandra_transaction_context *ctx = context; struct sql_commit_result commit_result; i_zero(&commit_result); if (sql_result_next_row(result) < 0) { commit_result.error = sql_result_get_error(result); commit_result.error_type = sql_result_get_error_type(result); } ctx->callback(&commit_result, ctx->context); driver_cassandra_transaction_unref(&ctx); } static void driver_cassandra_transaction_commit(struct sql_transaction_context *_ctx, sql_commit2_callback_t *callback, void *context) { struct cassandra_transaction_context *ctx = (struct cassandra_transaction_context *)_ctx; struct cassandra_db *db = (struct cassandra_db *)_ctx->db; enum cassandra_query_type query_type; struct sql_commit_result result; i_zero(&result); ctx->callback = callback; ctx->context = context; if (ctx->failed || (ctx->query == NULL && ctx->stmt == NULL)) { if (ctx->failed) result.error = ctx->error; callback(&result, context); driver_cassandra_transaction_unref(&ctx); return; } /* just a single query, send it */ const char *query = ctx->query != NULL ? ctx->query : sql_statement_get_query(&ctx->stmt->stmt); if (strncasecmp(query, "DELETE ", 7) == 0) query_type = CASSANDRA_QUERY_TYPE_DELETE; else query_type = CASSANDRA_QUERY_TYPE_WRITE; if (ctx->query != NULL) { driver_cassandra_query_full(_ctx->db, query, query_type, transaction_commit_callback, ctx); } else { ctx->stmt->result = driver_cassandra_query_init(db, query, query_type, TRUE, transaction_commit_callback, ctx); if (ctx->stmt->cass_stmt == NULL) { /* wait for prepare to finish */ } else { ctx->stmt->result->statement = ctx->stmt->cass_stmt; (void)driver_cassandra_send_query(ctx->stmt->result); pool_unref(&ctx->stmt->stmt.pool); } } } static void driver_cassandra_try_commit_s(struct cassandra_transaction_context *ctx) { struct sql_transaction_context *_ctx = &ctx->ctx; struct cassandra_db *db = (struct cassandra_db *)_ctx->db; struct sql_result *result = NULL; enum cassandra_query_type query_type; /* just a single query, send it */ if (strncasecmp(ctx->query, "DELETE ", 7) == 0) query_type = CASSANDRA_QUERY_TYPE_DELETE; else query_type = CASSANDRA_QUERY_TYPE_WRITE; driver_cassandra_sync_init(db); result = driver_cassandra_sync_query(db, ctx->query, query_type); driver_cassandra_sync_deinit(db); if (sql_result_next_row(result) < 0) transaction_set_failed(ctx, sql_result_get_error(result)); sql_result_unref(result); } static int driver_cassandra_transaction_commit_s(struct sql_transaction_context *_ctx, const char **error_r) { struct cassandra_transaction_context *ctx = (struct cassandra_transaction_context *)_ctx; if (ctx->stmt != NULL) { /* nothing should be using this - don't bother implementing */ i_panic("cassandra: sql_transaction_commit_s() not supported for prepared statements"); } if (ctx->query != NULL && !ctx->failed) driver_cassandra_try_commit_s(ctx); *error_r = t_strdup(ctx->error); i_assert(ctx->refcount == 1); i_assert((*error_r != NULL) == ctx->failed); driver_cassandra_transaction_unref(&ctx); return *error_r == NULL ? 0 : -1; } static void driver_cassandra_transaction_rollback(struct sql_transaction_context *_ctx) { struct cassandra_transaction_context *ctx = (struct cassandra_transaction_context *)_ctx; i_assert(ctx->refcount == 1); driver_cassandra_transaction_unref(&ctx); } static void driver_cassandra_update(struct sql_transaction_context *_ctx, const char *query, unsigned int *affected_rows) { struct cassandra_transaction_context *ctx = (struct cassandra_transaction_context *)_ctx; i_assert(affected_rows == NULL); if (ctx->query != NULL || ctx->stmt != NULL) { transaction_set_failed(ctx, "Multiple changes in transaction not supported"); return; } ctx->query = i_strdup(query); } static const char * driver_cassandra_escape_blob(struct sql_db *_db ATTR_UNUSED, const unsigned char *data, size_t size) { string_t *str = t_str_new(128); str_append(str, "0x"); binary_to_hex_append(str, data, size); return str_c(str); } static CassError driver_cassandra_bind_int(struct cassandra_sql_statement *stmt, unsigned int column_idx, int64_t value) { const CassDataType *data_type; CassValueType value_type; i_assert(stmt->prep != NULL); /* statements require exactly correct value type */ data_type = cass_prepared_parameter_data_type(stmt->prep->prepared, column_idx); value_type = cass_data_type_type(data_type); switch (value_type) { case CASS_VALUE_TYPE_INT: if (value < -2147483648 || value > 2147483647) return CASS_ERROR_LIB_INVALID_VALUE_TYPE; return cass_statement_bind_int32(stmt->cass_stmt, column_idx, value); case CASS_VALUE_TYPE_TIMESTAMP: case CASS_VALUE_TYPE_BIGINT: return cass_statement_bind_int64(stmt->cass_stmt, column_idx, value); case CASS_VALUE_TYPE_SMALL_INT: if (value < -32768 || value > 32767) return CASS_ERROR_LIB_INVALID_VALUE_TYPE; return cass_statement_bind_int16(stmt->cass_stmt, column_idx, value); case CASS_VALUE_TYPE_TINY_INT: if (value < -128 || value > 127) return CASS_ERROR_LIB_INVALID_VALUE_TYPE; return cass_statement_bind_int8(stmt->cass_stmt, column_idx, value); default: return CASS_ERROR_LIB_INVALID_VALUE_TYPE; } } static void prepare_finish_arg(struct cassandra_sql_statement *stmt, const struct cassandra_sql_arg *arg) { CassError rc; if (arg->value_str != NULL) { rc = cass_statement_bind_string(stmt->cass_stmt, arg->column_idx, arg->value_str); } else if (arg->value_binary != NULL) { rc = cass_statement_bind_bytes(stmt->cass_stmt, arg->column_idx, arg->value_binary, arg->value_binary_size); } else { rc = driver_cassandra_bind_int(stmt, arg->column_idx, arg->value_int64); } if (rc != CASS_OK) { i_error("cassandra: Statement '%s': Failed to bind column %u: %s", stmt->stmt.query_template, arg->column_idx, cass_error_desc(rc)); } } static void prepare_finish_statement(struct cassandra_sql_statement *stmt) { const struct cassandra_sql_arg *arg; if (stmt->prep->prepared == NULL) { i_assert(stmt->prep->error != NULL); if (stmt->result != NULL) { stmt->result->error = i_strdup(stmt->prep->error); result_finish(stmt->result); } return; } stmt->cass_stmt = cass_prepared_bind(stmt->prep->prepared); if (stmt->pending_timestamp != 0) { cass_statement_set_timestamp(stmt->cass_stmt, stmt->pending_timestamp); } if (array_is_created(&stmt->pending_args)) { array_foreach(&stmt->pending_args, arg) prepare_finish_arg(stmt, arg); } if (stmt->result != NULL) { stmt->result->statement = stmt->cass_stmt; (void)driver_cassandra_send_query(stmt->result); pool_unref(&stmt->stmt.pool); } } static void prepare_finish_pending_statements(struct cassandra_sql_prepared_statement *prep_stmt) { struct cassandra_sql_statement *const *stmtp; array_foreach(&prep_stmt->pending_statements, stmtp) prepare_finish_statement(*stmtp); array_clear(&prep_stmt->pending_statements); } static void prepare_callback(CassFuture *future, void *context) { struct cassandra_sql_prepared_statement *prep_stmt = context; CassError error = cass_future_error_code(future); if (error != CASS_OK) { const char *errmsg; size_t errsize; cass_future_error_message(future, &errmsg, &errsize); i_free(prep_stmt->error); prep_stmt->error = i_strndup(errmsg, errsize); } else { prep_stmt->prepared = cass_future_get_prepared(future); } prepare_finish_pending_statements(prep_stmt); } static void prepare_start(struct cassandra_sql_prepared_statement *prep_stmt) { struct cassandra_db *db = (struct cassandra_db *)prep_stmt->prep_stmt.db; CassFuture *future; if (!SQL_DB_IS_READY(&db->api)) { if (!prep_stmt->pending) { prep_stmt->pending = TRUE; array_append(&db->pending_prepares, &prep_stmt, 1); if (sql_connect(&db->api) < 0) i_unreached(); } return; } /* clear the current error in case we're retrying */ i_free_and_null(prep_stmt->error); future = cass_session_prepare(db->session, prep_stmt->query_template); driver_cassandra_set_callback(future, db, prepare_callback, prep_stmt); } static void driver_cassandra_prepare_pending(struct cassandra_db *db) { struct cassandra_sql_prepared_statement *const *prep_stmtp; i_assert(SQL_DB_IS_READY(&db->api)); array_foreach(&db->pending_prepares, prep_stmtp) { (*prep_stmtp)->pending = FALSE; prepare_start(*prep_stmtp); } array_clear(&db->pending_prepares); } static struct sql_prepared_statement * driver_cassandra_prepared_statement_init(struct sql_db *db, const char *query_template) { struct cassandra_sql_prepared_statement *prep_stmt = i_new(struct cassandra_sql_prepared_statement, 1); prep_stmt->prep_stmt.db = db; prep_stmt->query_template = i_strdup(query_template); i_array_init(&prep_stmt->pending_statements, 4); prepare_start(prep_stmt); return &prep_stmt->prep_stmt; } static void driver_cassandra_prepared_statement_deinit(struct sql_prepared_statement *_prep_stmt) { struct cassandra_sql_prepared_statement *prep_stmt = (struct cassandra_sql_prepared_statement *)_prep_stmt; i_assert(array_count(&prep_stmt->pending_statements) == 0); if (prep_stmt->prepared != NULL) cass_prepared_free(prep_stmt->prepared); array_free(&prep_stmt->pending_statements); i_free(prep_stmt->query_template); i_free(prep_stmt->error); i_free(prep_stmt); } static struct sql_statement * driver_cassandra_statement_init(struct sql_db *db ATTR_UNUSED, const char *query_template ATTR_UNUSED) { pool_t pool = pool_alloconly_create("cassandra sql statement", 1024); struct cassandra_sql_statement *stmt = p_new(pool, struct cassandra_sql_statement, 1); stmt->stmt.pool = pool; return &stmt->stmt; } static struct sql_statement * driver_cassandra_statement_init_prepared(struct sql_prepared_statement *_prep_stmt) { struct cassandra_sql_prepared_statement *prep_stmt = (struct cassandra_sql_prepared_statement *)_prep_stmt; pool_t pool = pool_alloconly_create("cassandra prepared sql statement", 1024); struct cassandra_sql_statement *stmt = p_new(pool, struct cassandra_sql_statement, 1); stmt->stmt.pool = pool; stmt->stmt.query_template = p_strdup(stmt->stmt.pool, prep_stmt->query_template); stmt->prep = prep_stmt; if (prep_stmt->prepared != NULL) { /* statement is already prepared. we can use it immediately. */ stmt->cass_stmt = cass_prepared_bind(prep_stmt->prepared); } else { if (prep_stmt->error != NULL) prepare_start(prep_stmt); /* need to wait until prepare is finished */ array_append(&prep_stmt->pending_statements, &stmt, 1); } return &stmt->stmt; } static void driver_cassandra_statement_abort(struct sql_statement *_stmt) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; if (stmt->cass_stmt != NULL) cass_statement_free(stmt->cass_stmt); } static void driver_cassandra_statement_set_timestamp(struct sql_statement *_stmt, const struct timespec *ts) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; cass_int64_t ts_usecs = (cass_int64_t)ts->tv_sec * 1000000ULL + ts->tv_nsec / 1000; if (stmt->cass_stmt != NULL) cass_statement_set_timestamp(stmt->cass_stmt, ts_usecs); else stmt->pending_timestamp = ts_usecs; } static struct cassandra_sql_arg * driver_cassandra_add_pending_arg(struct cassandra_sql_statement *stmt, unsigned int column_idx) { struct cassandra_sql_arg *arg; if (!array_is_created(&stmt->pending_args)) p_array_init(&stmt->pending_args, stmt->stmt.pool, 8); arg = array_append_space(&stmt->pending_args); arg->column_idx = column_idx; return arg; } static void driver_cassandra_statement_bind_str(struct sql_statement *_stmt, unsigned int column_idx, const char *value) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; if (stmt->cass_stmt != NULL) cass_statement_bind_string(stmt->cass_stmt, column_idx, value); else if (stmt->prep != NULL) { struct cassandra_sql_arg *arg = driver_cassandra_add_pending_arg(stmt, column_idx); arg->value_str = p_strdup(_stmt->pool, value); } } static void driver_cassandra_statement_bind_binary(struct sql_statement *_stmt, unsigned int column_idx, const void *value, size_t value_size) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; if (stmt->cass_stmt != NULL) { cass_statement_bind_bytes(stmt->cass_stmt, column_idx, value, value_size); } else if (stmt->prep != NULL) { struct cassandra_sql_arg *arg = driver_cassandra_add_pending_arg(stmt, column_idx); arg->value_binary = p_memdup(_stmt->pool, value, value_size); arg->value_binary_size = value_size; } } static void driver_cassandra_statement_bind_int64(struct sql_statement *_stmt, unsigned int column_idx, int64_t value) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; if (stmt->cass_stmt != NULL) driver_cassandra_bind_int(stmt, column_idx, value); else if (stmt->prep != NULL) { struct cassandra_sql_arg *arg = driver_cassandra_add_pending_arg(stmt, column_idx); arg->value_int64 = value; } } static void driver_cassandra_statement_query(struct sql_statement *_stmt, sql_query_callback_t *callback, void *context) { struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; struct cassandra_db *db = (struct cassandra_db *)_stmt->db; const char *query = sql_statement_get_query(_stmt); bool is_prepared = stmt->cass_stmt != NULL || stmt->prep != NULL; stmt->result = driver_cassandra_query_init(db, query, CASSANDRA_QUERY_TYPE_READ, is_prepared, callback, context); if (stmt->cass_stmt != NULL) { stmt->result->statement = stmt->cass_stmt; } else if (stmt->prep != NULL) { /* wait for prepare to finish */ return; } else { stmt->result->statement = cass_statement_new(query, 0); if (stmt->pending_timestamp != 0) { cass_statement_set_timestamp(stmt->result->statement, stmt->pending_timestamp); } } (void)driver_cassandra_send_query(stmt->result); pool_unref(&_stmt->pool); } static struct sql_result * driver_cassandra_statement_query_s(struct sql_statement *_stmt ATTR_UNUSED) { i_panic("cassandra: sql_statement_query_s() not supported"); } static void driver_cassandra_update_stmt(struct sql_transaction_context *_ctx, struct sql_statement *_stmt, unsigned int *affected_rows) { struct cassandra_transaction_context *ctx = (struct cassandra_transaction_context *)_ctx; struct cassandra_sql_statement *stmt = (struct cassandra_sql_statement *)_stmt; i_assert(affected_rows == NULL); if (ctx->query != NULL || ctx->stmt != NULL) { transaction_set_failed(ctx, "Multiple changes in transaction not supported"); return; } if (stmt->prep != NULL) ctx->stmt = stmt; else ctx->query = i_strdup(sql_statement_get_query(_stmt)); } const struct sql_db driver_cassandra_db = { .name = "cassandra", .flags = SQL_DB_FLAG_PREP_STATEMENTS, .v = { .init = driver_cassandra_init_v, .deinit = driver_cassandra_deinit_v, .connect = driver_cassandra_connect, .disconnect = driver_cassandra_disconnect, .escape_string = driver_cassandra_escape_string, .exec = driver_cassandra_exec, .query = driver_cassandra_query, .query_s = driver_cassandra_query_s, .transaction_begin = driver_cassandra_transaction_begin, .transaction_commit2 = driver_cassandra_transaction_commit, .transaction_commit_s = driver_cassandra_transaction_commit_s, .transaction_rollback = driver_cassandra_transaction_rollback, .update = driver_cassandra_update, .escape_blob = driver_cassandra_escape_blob, .prepared_statement_init = driver_cassandra_prepared_statement_init, .prepared_statement_deinit = driver_cassandra_prepared_statement_deinit, .statement_init = driver_cassandra_statement_init, .statement_init_prepared = driver_cassandra_statement_init_prepared, .statement_abort = driver_cassandra_statement_abort, .statement_set_timestamp = driver_cassandra_statement_set_timestamp, .statement_bind_str = driver_cassandra_statement_bind_str, .statement_bind_binary = driver_cassandra_statement_bind_binary, .statement_bind_int64 = driver_cassandra_statement_bind_int64, .statement_query = driver_cassandra_statement_query, .statement_query_s = driver_cassandra_statement_query_s, .update_stmt = driver_cassandra_update_stmt, } }; const struct sql_result driver_cassandra_result = { .v = { driver_cassandra_result_free, driver_cassandra_result_next_row, driver_cassandra_result_get_fields_count, driver_cassandra_result_get_field_name, driver_cassandra_result_find_field, driver_cassandra_result_get_field_value, driver_cassandra_result_get_field_value_binary, driver_cassandra_result_find_field_value, driver_cassandra_result_get_values, driver_cassandra_result_get_error, driver_cassandra_result_more, } }; const char *driver_cassandra_version = DOVECOT_ABI_VERSION; void driver_cassandra_init(void); void driver_cassandra_deinit(void); void driver_cassandra_init(void) { sql_driver_register(&driver_cassandra_db); } void driver_cassandra_deinit(void) { sql_driver_unregister(&driver_cassandra_db); } #endif dovecot-2.2.33.2/src/lib-sql/Makefile.am0000644000175000017500000000637713165463624014542 00000000000000noinst_LTLIBRARIES = libsql.la SQL_DRIVER_PLUGINS = # automake seems to force making this unconditional.. NOPLUGIN_LDFLAGS = if SQL_PLUGINS if BUILD_MYSQL MYSQL_LIB = libdriver_mysql.la SQL_DRIVER_PLUGINS += mysql endif if BUILD_PGSQL PGSQL_LIB = libdriver_pgsql.la SQL_DRIVER_PLUGINS += pgsql endif if BUILD_SQLITE SQLITE_LIB = libdriver_sqlite.la SQL_DRIVER_PLUGINS += sqlite endif if BUILD_CASSANDRA CASSANDRA_LIB = libdriver_cassandra.la SQL_DRIVER_PLUGINS += cassandra endif sql_module_LTLIBRARIES = \ $(MYSQL_LIB) \ $(PGSQL_LIB) \ $(SQLITE_LIB) \ $(CASSANDRA_LIB) sql_moduledir = $(moduledir) endif sql_drivers = @sql_drivers@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ $(SQL_CFLAGS) dist_sources = \ sql-api.c \ sql-db-cache.c if ! SQL_PLUGINS driver_sources = \ driver-mysql.c \ driver-pgsql.c \ driver-sqlite.c \ driver-cassandra.c endif libsql_la_SOURCES = \ $(dist_sources) \ $(driver_sources) \ driver-sqlpool.c nodist_libsql_la_SOURCES = sql-drivers-register.c deplibs = \ ../lib-dovecot/libdovecot.la if SQL_PLUGINS libdriver_mysql_la_LDFLAGS = -module -avoid-version libdriver_mysql_la_LIBADD = $(MYSQL_LIBS) libdriver_mysql_la_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQL_CFLAGS) libdriver_mysql_la_SOURCES = driver-mysql.c libdriver_pgsql_la_LDFLAGS = -module -avoid-version libdriver_pgsql_la_LIBADD = $(PGSQL_LIBS) libdriver_pgsql_la_CPPFLAGS = $(AM_CPPFLAGS) $(PGSQL_CFLAGS) libdriver_pgsql_la_SOURCES = driver-pgsql.c libdriver_sqlite_la_LDFLAGS = -module -avoid-version libdriver_sqlite_la_LIBADD = $(SQLITE_LIBS) libdriver_sqlite_la_CPPFLAGS = $(AM_CPPFLAGS) $(SQLITE_CFLAGS) libdriver_sqlite_la_SOURCES = driver-sqlite.c libdriver_cassandra_la_LDFLAGS = -module -avoid-version libdriver_cassandra_la_LIBADD = $(CASSANDRA_LIBS) libdriver_cassandra_la_CPPFLAGS = $(AM_CPPFLAGS) $(CASSANDRA_CFLAGS) libdriver_cassandra_la_SOURCES = driver-cassandra.c sql_libs = else sql_libs = \ $(MYSQL_LIBS) \ $(PGSQL_LIBS) \ $(SQLITE_LIBS) \ $(CASSANDRA_LIBS) endif pkglib_LTLIBRARIES = libdovecot-sql.la libdovecot_sql_la_SOURCES = libdovecot_sql_la_LIBADD = libsql.la $(deplibs) $(sql_libs) libdovecot_sql_la_DEPENDENCIES = libsql.la libdovecot_sql_la_LDFLAGS = -export-dynamic headers = \ sql-api.h \ sql-api-private.h \ sql-db-cache.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) sql-drivers-register.c: Makefile rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "sql-api.h"' >>$@ if ! SQL_PLUGINS for i in $(sql_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "extern struct sql_db driver_$${i}_db;" >>$@ ; \ fi; \ done endif echo 'void sql_drivers_register_all(void) {' >>$@ if ! SQL_PLUGINS for i in $(sql_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "sql_driver_register(&driver_$${i}_db);" >>$@ ; \ fi; \ done endif echo '}' >>$@ if SQL_PLUGINS install-exec-local: for d in auth dict; do \ $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \ for driver in $(SQL_DRIVER_PLUGINS); do \ rm -f $(DESTDIR)$(moduledir)/$$d/libdriver_$$driver.so; \ $(LN_S) ../libdriver_$$driver.so $(DESTDIR)$(moduledir)/$$d; \ done; \ done endif distclean-generic: rm -f Makefile sql-drivers-register.c dovecot-2.2.33.2/src/pop3/0002755000175000017500000000000013172375612012066 500000000000000dovecot-2.2.33.2/src/pop3/Makefile.in0000644000175000017500000006105013172375575014063 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = pop3$(EXEEXT) subdir = src/pop3 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_pop3_OBJECTS = main.$(OBJEXT) pop3-client.$(OBJEXT) \ pop3-commands.$(OBJEXT) pop3-settings.$(OBJEXT) pop3_OBJECTS = $(am_pop3_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = pop3_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(pop3_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(pop3_SOURCES) DIST_SOURCES = $(pop3_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage pop3_LDFLAGS = -export-dynamic pop3_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) pop3_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) pop3_SOURCES = \ main.c \ pop3-client.c \ pop3-commands.c \ pop3-settings.c headers = \ pop3-capability.h \ pop3-client.h \ pop3-commands.h \ pop3-common.h \ pop3-settings.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/pop3/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/pop3/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list pop3$(EXEEXT): $(pop3_OBJECTS) $(pop3_DEPENDENCIES) $(EXTRA_pop3_DEPENDENCIES) @rm -f pop3$(EXEEXT) $(AM_V_CCLD)$(pop3_LINK) $(pop3_OBJECTS) $(pop3_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-commands.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-settings.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/pop3/pop3-client.h0000644000175000017500000000765713165463624014334 00000000000000#ifndef POP3_CLIENT_H #define POP3_CLIENT_H #include "seq-range-array.h" struct client; struct mail_storage; struct mail_storage_service_ctx; typedef void command_func_t(struct client *client); #define MSGS_BITMASK_SIZE(client) \ (MALLOC_ADD((client)->messages_count, (CHAR_BIT-1)) / CHAR_BIT) /* Stop reading input when output buffer has this many bytes. Once the buffer size has dropped to half of it, start reading input again. */ #define POP3_OUTBUF_THROTTLE_SIZE 4096 #define POP3_CLIENT_OUTPUT_FULL(client) \ (o_stream_get_buffer_used_size((client)->output) >= POP3_OUTBUF_THROTTLE_SIZE) struct pop3_client_vfuncs { void (*destroy)(struct client *client, const char *reason); }; /* pop3_msn = 1..n in the POP3 protocol msgnum = 0..n-1 = pop3_msn-1 seq = 1..n = mail's sequence number in lib-storage. when pop3 sort ordering is used, msgnum_to_seq_map[] can be used for translation. */ struct client { struct client *prev, *next; struct pop3_client_vfuncs v; const char *session_id; int fd_in, fd_out; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_idle, *to_commit; command_func_t *cmd; void *cmd_context; pool_t pool; struct mail_storage_service_user *service_user; struct mail_user *user; struct mail_namespace *inbox_ns; struct mailbox *mailbox; struct mailbox_transaction_context *trans; struct mail_keywords *deleted_kw; struct timeout *to_session_dotlock_refresh; struct dotlock *session_dotlock; time_t last_input, last_output; unsigned int bad_counter; unsigned int highest_expunged_fetch_msgnum; unsigned int uid_validity; unsigned int messages_count; unsigned int deleted_count, seen_change_count; uoff_t total_size; uoff_t deleted_size; uint32_t last_seen_pop3_msn, lowest_retr_pop3_msn; /* All sequences currently visible in the mailbox. */ ARRAY_TYPE(seq_range) all_seqs; uint32_t highest_seq; /* [msgnum] contains mail seq. anything after it has seq = msgnum+1 */ uint32_t *msgnum_to_seq_map; uint32_t msgnum_to_seq_map_count; uoff_t top_bytes; uoff_t retr_bytes; unsigned int top_count; unsigned int retr_count; /* [msgnum] */ const char **message_uidls; uoff_t *message_sizes; /* [msgnum/8] & msgnum%8 */ unsigned char *deleted_bitmask; unsigned char *seen_bitmask; /* settings: */ const struct pop3_settings *set; const struct mail_storage_settings *mail_set; pool_t uidl_pool; enum uidl_keys uidl_keymask; /* Module-specific contexts. */ ARRAY(union pop3_module_context *) module_contexts; unsigned int destroyed:1; unsigned int disconnected:1; unsigned int deleted:1; unsigned int waiting_input:1; unsigned int anvil_sent:1; unsigned int message_uidls_save:1; unsigned int delete_success:1; }; struct pop3_module_register { unsigned int id; }; union pop3_module_context { struct pop3_client_vfuncs super; struct pop3_module_register *reg; }; extern struct pop3_module_register pop3_module_register; extern struct client *pop3_clients; extern unsigned int pop3_client_count; /* Create new client with specified input/output handles. socket specifies if the handle is a socket. */ struct client *client_create(int fd_in, int fd_out, const char *session_id, struct mail_user *user, struct mail_storage_service_user *service_user, const struct pop3_settings *set); int client_init_mailbox(struct client *client, const char **error_r); void client_destroy(struct client *client, const char *reason) ATTR_NULL(2); /* Disconnect client connection */ void client_disconnect(struct client *client, const char *reason); /* Send a line of data to client */ void client_send_line(struct client *client, const char *fmt, ...) ATTR_FORMAT(2, 3); void client_send_storage_error(struct client *client); bool client_handle_input(struct client *client); bool client_update_mails(struct client *client); void clients_destroy_all(struct mail_storage_service_ctx *storage_service); int pop3_lock_session(struct client *client); #endif dovecot-2.2.33.2/src/pop3/pop3-settings.h0000644000175000017500000000157413123174404014673 00000000000000#ifndef POP3_SETTINGS_H #define POP3_SETTINGS_H struct mail_user_settings; /* */ enum pop3_client_workarounds { WORKAROUND_OUTLOOK_NO_NULS = 0x01, WORKAROUND_OE_NS_EOH = 0x02 }; enum pop3_delete_type { POP3_DELETE_TYPE_EXPUNGE = 0, POP3_DELETE_TYPE_FLAG, }; /* */ struct pop3_settings { bool verbose_proctitle; const char *rawlog_dir; /* pop3: */ bool pop3_no_flag_updates; bool pop3_enable_last; bool pop3_reuse_xuidl; bool pop3_save_uidl; bool pop3_lock_session; bool pop3_fast_size_lookups; const char *pop3_client_workarounds; const char *pop3_logout_format; const char *pop3_uidl_duplicates; const char *pop3_deleted_flag; const char *pop3_delete_type; enum pop3_client_workarounds parsed_workarounds; enum pop3_delete_type parsed_delete_type; }; extern const struct setting_parser_info pop3_setting_parser_info; #endif dovecot-2.2.33.2/src/pop3/main.c0000644000175000017500000002525713167162046013106 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "pop3-common.h" #include "ioloop.h" #include "buffer.h" #include "istream.h" #include "ostream.h" #include "abspath.h" #include "base64.h" #include "str.h" #include "process-title.h" #include "restrict-access.h" #include "settings-parser.h" #include "master-service.h" #include "master-login.h" #include "master-interface.h" #include "var-expand.h" #include "mail-error.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-service.h" #include #include #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) static bool verbose_proctitle = FALSE; static struct mail_storage_service_ctx *storage_service; static struct master_login *master_login = NULL; pop3_client_created_func_t *hook_client_created = NULL; pop3_client_created_func_t * pop3_client_created_hook_set(pop3_client_created_func_t *new_hook) { pop3_client_created_func_t *old_hook = hook_client_created; hook_client_created = new_hook; return old_hook; } void pop3_refresh_proctitle(void) { struct client *client; string_t *title = t_str_new(128); if (!verbose_proctitle) return; str_append_c(title, '['); switch (pop3_client_count) { case 0: str_append(title, "idling"); break; case 1: client = pop3_clients; str_append(title, client->user->username); if (client->user->remote_ip != NULL) { str_append_c(title, ' '); str_append(title, net_ip2addr(client->user->remote_ip)); } if (client->destroyed) str_append(title, " (deinit)"); break; default: str_printfa(title, "%u connections", pop3_client_count); break; } str_append_c(title, ']'); process_title_set(str_c(title)); } static void pop3_die(void) { /* do nothing. pop3 connections typically die pretty quick anyway. */ } static void client_add_input(struct client *client, const buffer_t *buf) { struct ostream *output; if (buf != NULL && buf->used > 0) { if (!i_stream_add_data(client->input, buf->data, buf->used)) i_panic("Couldn't add client input to stream"); } output = client->output; o_stream_ref(output); o_stream_cork(output); (void)client_handle_input(client); o_stream_uncork(output); o_stream_unref(&output); } static int client_create_from_input(const struct mail_storage_service_input *input, int fd_in, int fd_out, struct client **client_r, const char **error_r) { const char *lookup_error_str = "-ERR [SYS/TEMP] "MAIL_ERRSTR_CRITICAL_MSG"\r\n"; struct mail_storage_service_user *user; struct mail_user *mail_user; struct pop3_settings *set; if (mail_storage_service_lookup_next(storage_service, input, &user, &mail_user, error_r) <= 0) { if (write(fd_out, lookup_error_str, strlen(lookup_error_str)) < 0) { /* ignored */ } return -1; } restrict_access_allow_coredumps(TRUE); set = mail_storage_service_user_get_set(user)[1]; if (set->verbose_proctitle) verbose_proctitle = TRUE; settings_var_expand(&pop3_setting_parser_info, set, mail_user->pool, mail_user_var_expand_table(mail_user)); *client_r = client_create(fd_in, fd_out, input->session_id, mail_user, user, set); return 0; } static int lock_session(struct client *client) { int ret; i_assert(client->user->namespaces != NULL); i_assert(client->set->pop3_lock_session); if ((ret = pop3_lock_session(client)) <= 0) { client_send_line(client, ret < 0 ? "-ERR [SYS/TEMP] Failed to create POP3 session lock." : "-ERR [IN-USE] Mailbox is locked by another POP3 session."); client_destroy(client, "Couldn't lock POP3 session"); return -1; } return 0; } #define MSG_BYE_INTERNAL_ERROR "-ERR "MAIL_ERRSTR_CRITICAL_MSG static int init_namespaces(struct client *client, bool already_logged_in) { const char *error; /* finish initializing the user (see comment in main()) */ if (mail_namespaces_init(client->user, &error) < 0) { if (!already_logged_in) client_send_line(client, MSG_BYE_INTERNAL_ERROR); i_error("%s", error); client_destroy(client, error); return -1; } i_assert(client->inbox_ns == NULL); client->inbox_ns = mail_namespace_find_inbox(client->user->namespaces); i_assert(client->inbox_ns != NULL); return 0; } static void add_input(struct client *client, const buffer_t *input_buf) { const char *error; /* * RFC 1939 requires that the session lock gets acquired before the * positive response is sent to the client indicating a transition * to the TRANSACTION state. * * Since the session lock is stored under the INBOX's storage * directory, the locking code requires that the namespaces are * initialized first. * * If the system administrator configured dovecot to not use session * locks, we can send back the positive response before the * potentially long-running namespace initialization occurs. This * avoids the client possibly timing out during authentication due * to storage initialization taking too long. */ if (client->set->pop3_lock_session) { if (init_namespaces(client, FALSE) < 0) return; /* no need to propagate an error */ if (lock_session(client) < 0) return; /* no need to propagate an error */ if (!IS_STANDALONE()) client_send_line(client, "+OK Logged in."); } else { if (!IS_STANDALONE()) client_send_line(client, "+OK Logged in."); if (init_namespaces(client, TRUE) < 0) return; /* no need to propagate an error */ } if (client_init_mailbox(client, &error) < 0) { i_error("%s", error); client_destroy(client, error); return; } client_add_input(client, input_buf); } static void main_stdio_run(const char *username) { struct client *client; struct mail_storage_service_input input; buffer_t *input_buf; const char *value, *error, *input_base64; i_zero(&input); input.module = input.service = "pop3"; input.username = username != NULL ? username : getenv("USER"); if (input.username == NULL && IS_STANDALONE()) input.username = getlogin(); if (input.username == NULL) i_fatal("USER environment missing"); if ((value = getenv("IP")) != NULL) (void)net_addr2ip(value, &input.remote_ip); if ((value = getenv("LOCAL_IP")) != NULL) (void)net_addr2ip(value, &input.local_ip); input_base64 = getenv("CLIENT_INPUT"); input_buf = input_base64 == NULL ? NULL : t_base64_decode_str(input_base64); if (client_create_from_input(&input, STDIN_FILENO, STDOUT_FILENO, &client, &error) < 0) i_fatal("%s", error); add_input(client, input_buf); /* client may be destroyed now */ } static void login_client_connected(const struct master_login_client *login_client, const char *username, const char *const *extra_fields) { struct client *client; struct mail_storage_service_input input; const char *error; buffer_t input_buf; i_zero(&input); input.module = input.service = "pop3"; input.local_ip = login_client->auth_req.local_ip; input.remote_ip = login_client->auth_req.remote_ip; input.username = username; input.userdb_fields = extra_fields; input.session_id = login_client->session_id; buffer_create_from_const_data(&input_buf, login_client->data, login_client->auth_req.data_size); if (client_create_from_input(&input, login_client->fd, login_client->fd, &client, &error) < 0) { int fd = login_client->fd; i_error("%s", error); i_close_fd(&fd); master_service_client_connection_destroyed(master_service); return; } add_input(client, &input_buf); /* client may be destroyed now */ } static void login_client_failed(const struct master_login_client *client, const char *errormsg) { const char *msg; msg = t_strdup_printf("-ERR [SYS/TEMP] %s\r\n", errormsg); if (write(client->fd, msg, strlen(msg)) < 0) { /* ignored */ } } static void client_connected(struct master_service_connection *conn) { /* when running standalone, we shouldn't even get here */ i_assert(master_login != NULL); master_service_client_connection_accept(conn); master_login_add(master_login, conn->fd); } int main(int argc, char *argv[]) { static const struct setting_parser_info *set_roots[] = { &pop3_setting_parser_info, NULL }; struct master_login_settings login_set; enum master_service_flags service_flags = 0; enum mail_storage_service_flags storage_service_flags = 0; const char *username = NULL, *auth_socket_path = "auth-master"; int c; i_zero(&login_set); login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT; if (IS_STANDALONE() && getuid() == 0 && net_getpeername(1, NULL, NULL) == 0) { printf("-ERR [SYS/PERM] pop3 binary must not be started from " "inetd, use pop3-login instead.\n"); return 1; } if (IS_STANDALONE()) { service_flags |= MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_STD_CLIENT; } else { service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT; } /* * We include MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES so that the * mail_user initialization is fast and we can quickly send back the * OK response to LOGIN/AUTHENTICATE. Otherwise we risk a very slow * namespace initialization to cause client timeouts on login. */ storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; master_service = master_service_init("pop3", service_flags, &argc, &argv, "a:t:u:"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'a': auth_socket_path = optarg; break; case 't': if (str_to_uint(optarg, &login_set.postlogin_timeout_secs) < 0 || login_set.postlogin_timeout_secs == 0) i_fatal("Invalid -t parameter: %s", optarg); break; case 'u': storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; username = optarg; break; default: return FATAL_DEFAULT; } } login_set.auth_socket_path = t_abspath(auth_socket_path); if (argv[optind] != NULL) login_set.postlogin_socket_path = t_abspath(argv[optind]); login_set.callback = login_client_connected; login_set.failure_callback = login_client_failed; master_service_set_die_callback(master_service, pop3_die); storage_service = mail_storage_service_init(master_service, set_roots, storage_service_flags); master_service_init_finish(master_service); /* fake that we're running, so we know if client was destroyed while handling its initial input */ io_loop_set_running(current_ioloop); if (IS_STANDALONE()) { T_BEGIN { main_stdio_run(username); } T_END; } else { master_login = master_login_init(master_service, &login_set); io_loop_set_running(current_ioloop); } if (io_loop_is_running(current_ioloop)) master_service_run(master_service, client_connected); clients_destroy_all(storage_service); if (master_login != NULL) master_login_deinit(&master_login); mail_storage_service_deinit(&storage_service); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/pop3/pop3-common.h0000644000175000017500000000116213123174404014314 00000000000000#ifndef POP3_COMMON_H #define POP3_COMMON_H enum uidl_keys { UIDL_UIDVALIDITY = 0x01, UIDL_UID = 0x02, UIDL_MD5 = 0x04, UIDL_FILE_NAME = 0x08, UIDL_GUID = 0x10 }; #include "lib.h" #include "pop3-client.h" #include "pop3-settings.h" typedef void pop3_client_created_func_t(struct client **client); extern pop3_client_created_func_t *hook_client_created; /* Sets the hook_client_created and returns the previous hook, which the new_hook should call if it's non-NULL. */ pop3_client_created_func_t * pop3_client_created_hook_set(pop3_client_created_func_t *new_hook); void pop3_refresh_proctitle(void); #endif dovecot-2.2.33.2/src/pop3/pop3-client.c0000644000175000017500000005755413165463624014330 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "pop3-common.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "iostream.h" #include "istream.h" #include "ostream.h" #include "iostream-rawlog.h" #include "crc32.h" #include "str.h" #include "llist.h" #include "hostpid.h" #include "file-dotlock.h" #include "var-expand.h" #include "master-service.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-autoexpunge.h" #include "pop3-commands.h" #include "mail-search-build.h" #include "mail-namespace.h" #include /* max. length of input command line (spec says 512) */ #define MAX_INBUF_SIZE 2048 /* Disconnect client when it sends too many bad commands in a row */ #define CLIENT_MAX_BAD_COMMANDS 20 /* Disconnect client after idling this many milliseconds */ #define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000) /* If client starts idling for this many milliseconds, commit the current transaction. This allows the mailbox to become unlocked. */ #define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000) #define POP3_LOCK_FNAME "dovecot-pop3-session.lock" #define POP3_SESSION_DOTLOCK_STALE_TIMEOUT_SECS (60*5) extern struct pop3_client_vfuncs pop3_client_vfuncs; struct pop3_module_register pop3_module_register = { 0 }; struct client *pop3_clients; unsigned int pop3_client_count; static enum mail_sort_type pop3_sort_program[] = { MAIL_SORT_POP3_ORDER, MAIL_SORT_END }; static const struct dotlock_settings session_dotlock_set = { .timeout = 10, .stale_timeout = POP3_SESSION_DOTLOCK_STALE_TIMEOUT_SECS, .lock_suffix = "", .use_io_notify = TRUE }; static void client_input(struct client *client); static int client_output(struct client *client); static void client_commit_timeout(struct client *client) { if (client->cmd != NULL) { /* Can't commit while commands are running */ return; } (void)mailbox_transaction_commit(&client->trans); client->trans = mailbox_transaction_begin(client->mailbox, 0); } static void client_idle_timeout(struct client *client) { if (client->cmd != NULL) { client_destroy(client, "Disconnected for inactivity in reading our output"); } else { client_send_line(client, "-ERR Disconnected for inactivity."); client_destroy(client, "Disconnected for inactivity"); } } static int pop3_mail_get_size(struct client *client, struct mail *mail, uoff_t *size_r) { int ret; if (!client->set->pop3_fast_size_lookups) return mail_get_virtual_size(mail, size_r); /* first try to get the virtual size */ mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; ret = mail_get_virtual_size(mail, size_r); mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (ret == 0) return 0; if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_LOOKUP_ABORTED) return -1; /* virtual size not available with a fast lookup. fallback to trying the physical size */ mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; ret = mail_get_physical_size(mail, size_r); mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (ret == 0) return 0; if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_LOOKUP_ABORTED) return -1; /* no way to quickly get the size. fallback to doing a slow virtual size lookup */ return mail_get_virtual_size(mail, size_r); } static void msgnum_to_seq_map_add(ARRAY_TYPE(uint32_t) *msgnum_to_seq_map, struct client *client, struct mail *mail, unsigned int msgnum) { uint32_t seq; if (mail->seq == msgnum+1) return; if (!array_is_created(msgnum_to_seq_map)) i_array_init(msgnum_to_seq_map, client->messages_count); /* add any messages between this and the previous one that had a POP3 order defined */ seq = array_count(msgnum_to_seq_map) + 1; for (; seq <= msgnum; seq++) array_append(msgnum_to_seq_map, &seq, 1); array_append(msgnum_to_seq_map, &mail->seq, 1); } static int read_mailbox(struct client *client, uint32_t *failed_uid_r) { struct mailbox_status status; struct mailbox_transaction_context *t; struct mail_search_args *search_args; struct mail_search_arg *sarg; struct mail_search_context *ctx; struct mail *mail; uoff_t size; ARRAY(uoff_t) message_sizes; ARRAY_TYPE(uint32_t) msgnum_to_seq_map = ARRAY_INIT; unsigned int msgnum; int ret = 1; *failed_uid_r = 0; mailbox_get_open_status(client->mailbox, STATUS_UIDVALIDITY, &status); client->uid_validity = status.uidvalidity; client->messages_count = status.messages; t = mailbox_transaction_begin(client->mailbox, 0); search_args = mail_search_build_init(); if (client->deleted_kw != NULL) { sarg = mail_search_build_add(search_args, SEARCH_KEYWORDS); sarg->match_not = TRUE; sarg->value.str = p_strdup(search_args->pool, client->set->pop3_deleted_flag); i_array_init(&client->all_seqs, 32); } else { mail_search_build_add_all(search_args); } mail_search_args_init(search_args, client->mailbox, TRUE, NULL); ctx = mailbox_search_init(t, search_args, pop3_sort_program, client->set->pop3_fast_size_lookups ? 0 : MAIL_FETCH_VIRTUAL_SIZE, NULL); mail_search_args_unref(&search_args); client->last_seen_pop3_msn = 0; client->total_size = 0; i_array_init(&message_sizes, client->messages_count); msgnum = 0; while (mailbox_search_next(ctx, &mail)) { if (pop3_mail_get_size(client, mail, &size) < 0) { ret = mail->expunged ? 0 : -1; *failed_uid_r = mail->uid; break; } if (array_is_created(&client->all_seqs)) seq_range_array_add(&client->all_seqs, mail->seq); msgnum_to_seq_map_add(&msgnum_to_seq_map, client, mail, msgnum); if ((mail_get_flags(mail) & MAIL_SEEN) != 0) client->last_seen_pop3_msn = msgnum + 1; client->total_size += size; if (client->highest_seq < mail->seq) client->highest_seq = mail->seq; array_append(&message_sizes, &size, 1); msgnum++; } if (mailbox_search_deinit(&ctx) < 0) ret = -1; if (ret <= 0) { /* commit the transaction instead of rollbacking to make sure we don't lose data (virtual sizes) added to cache file */ (void)mailbox_transaction_commit(&t); array_free(&message_sizes); if (array_is_created(&msgnum_to_seq_map)) array_free(&msgnum_to_seq_map); return ret; } i_assert(msgnum <= client->messages_count); client->messages_count = msgnum; if (!array_is_created(&client->all_seqs)) { i_array_init(&client->all_seqs, 1); seq_range_array_add_range(&client->all_seqs, 1, msgnum); } client->trans = t; client->message_sizes = buffer_free_without_data(&message_sizes.arr.buffer); if (array_is_created(&msgnum_to_seq_map)) { client->msgnum_to_seq_map_count = array_count(&msgnum_to_seq_map); client->msgnum_to_seq_map = buffer_free_without_data(&msgnum_to_seq_map.arr.buffer); } return 1; } static int init_pop3_deleted_flag(struct client *client, const char **error_r) { const char *deleted_keywords[2]; if (client->set->pop3_deleted_flag[0] == '\0') return 0; deleted_keywords[0] = client->set->pop3_deleted_flag; deleted_keywords[1] = NULL; if (mailbox_keywords_create(client->mailbox, deleted_keywords, &client->deleted_kw) < 0) { *error_r = t_strdup_printf( "pop3_deleted_flags: Invalid keyword '%s': %s", client->set->pop3_deleted_flag, mailbox_get_last_internal_error(client->mailbox, NULL)); return -1; } return 0; } static int init_mailbox(struct client *client, const char **error_r) { uint32_t failed_uid = 0, last_failed_uid = 0; int i, ret = -1; for (i = 0;; i++) { if (mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { ret = -1; break; } ret = read_mailbox(client, &failed_uid); if (ret > 0) return 0; if (i == 2) break; /* well, sync and try again. maybe it works the second time. */ last_failed_uid = failed_uid; failed_uid = 0; } if (ret < 0) { *error_r = mailbox_get_last_internal_error(client->mailbox, NULL); client_send_storage_error(client); } else { if (failed_uid == last_failed_uid && failed_uid != 0) { /* failed twice in same message */ *error_r = t_strdup_printf( "Getting size of message UID=%u failed", failed_uid); } else { *error_r = "Can't sync mailbox: " "Messages keep getting expunged"; } client_send_line(client, "-ERR [SYS/TEMP] Couldn't sync mailbox."); } return -1; } static enum uidl_keys parse_uidl_keymask(const char *format) { enum uidl_keys mask = 0; for (; *format != '\0'; format++) { if (format[0] == '%' && format[1] != '\0') { switch (var_get_key(++format)) { case 'v': mask |= UIDL_UIDVALIDITY; break; case 'u': mask |= UIDL_UID; break; case 'm': mask |= UIDL_MD5; break; case 'f': mask |= UIDL_FILE_NAME; break; case 'g': mask |= UIDL_GUID; break; } } } return mask; } static void pop3_lock_session_refresh(struct client *client) { if (file_dotlock_touch(client->session_dotlock) < 0) { client_send_line(client, "-ERR [SYS/TEMP] Couldn't update POP3 session lock."); client_destroy(client, "Couldn't lock POP3 session"); } } int pop3_lock_session(struct client *client) { const struct mail_storage_settings *mail_set = mail_storage_service_user_get_mail_set(client->service_user); struct dotlock_settings dotlock_set; enum mailbox_list_path_type type; const char *dir, *path; int ret; if (mailbox_list_get_root_path(client->inbox_ns->list, MAILBOX_LIST_PATH_TYPE_INDEX, &dir)) { type = MAILBOX_LIST_PATH_TYPE_INDEX; } else if (mailbox_list_get_root_path(client->inbox_ns->list, MAILBOX_LIST_PATH_TYPE_DIR, &dir)) { type = MAILBOX_LIST_PATH_TYPE_DIR; } else { i_error("pop3_lock_session: Storage has no root/index directory, " "can't create a POP3 session lock file"); return -1; } if (mailbox_list_mkdir_root(client->inbox_ns->list, dir, type) < 0) { i_error("pop3_lock_session: Couldn't create root directory %s: %s", dir, mailbox_list_get_last_internal_error(client->inbox_ns->list, NULL)); return -1; } path = t_strdup_printf("%s/"POP3_LOCK_FNAME, dir); dotlock_set = session_dotlock_set; dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; dotlock_set.nfs_flush = mail_set->mail_nfs_storage; ret = file_dotlock_create(&dotlock_set, path, 0, &client->session_dotlock); if (ret < 0) i_error("file_dotlock_create(%s) failed: %m", path); else if (ret > 0) { client->to_session_dotlock_refresh = timeout_add(POP3_SESSION_DOTLOCK_STALE_TIMEOUT_SECS*1000, pop3_lock_session_refresh, client); } return ret; } struct client *client_create(int fd_in, int fd_out, const char *session_id, struct mail_user *user, struct mail_storage_service_user *service_user, const struct pop3_settings *set) { struct client *client; pool_t pool; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); pool = pool_alloconly_create("pop3 client", 256); client = p_new(pool, struct client, 1); client->pool = pool; client->service_user = service_user; client->v = pop3_client_vfuncs; client->set = set; client->session_id = p_strdup(pool, session_id); client->fd_in = fd_in; client->fd_out = fd_out; client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); o_stream_set_flush_callback(client->output, client_output, client); if (set->rawlog_dir[0] != '\0') { (void)iostream_rawlog_create(set->rawlog_dir, &client->input, &client->output); } p_array_init(&client->module_contexts, client->pool, 5); client->io = io_add_istream(client->input, client_input, client); client->last_input = ioloop_time; client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, client_idle_timeout, client); client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS, client_commit_timeout, client); client->user = user; client->mail_set = mail_user_set_get_storage_set(user); client->uidl_keymask = parse_uidl_keymask(client->mail_set->pop3_uidl_format); if (client->uidl_keymask == 0) i_fatal("Invalid pop3_uidl_format"); if (var_has_key(set->pop3_logout_format, 'u', "uidl_change")) { /* logging uidl_change. we need hashes of the UIDLs */ client->message_uidls_save = TRUE; } else if (strcmp(set->pop3_uidl_duplicates, "allow") != 0) { /* UIDL duplicates aren't allowed, so we'll need to keep track of them */ client->message_uidls_save = TRUE; } pop3_client_count++; DLLIST_PREPEND(&pop3_clients, client); if (hook_client_created != NULL) hook_client_created(&client); return client; } int client_init_mailbox(struct client *client, const char **error_r) { enum mailbox_flags flags; const char *ident, *errmsg; /* refresh proctitle before a potentially long-running init_mailbox() */ pop3_refresh_proctitle(); flags = MAILBOX_FLAG_POP3_SESSION; if (!client->set->pop3_no_flag_updates) flags |= MAILBOX_FLAG_DROP_RECENT; client->mailbox = mailbox_alloc(client->inbox_ns->list, "INBOX", flags); mailbox_set_reason(client->mailbox, "POP3 INBOX"); if (mailbox_open(client->mailbox) < 0) { *error_r = t_strdup_printf("Couldn't open INBOX: %s", mailbox_get_last_internal_error(client->mailbox, NULL)); client_send_storage_error(client); return -1; } if (init_pop3_deleted_flag(client, &errmsg) < 0 || init_mailbox(client, &errmsg) < 0) { *error_r = t_strdup_printf("Couldn't init INBOX: %s", errmsg); return -1; } if (!client->set->pop3_no_flag_updates && client->messages_count > 0) client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client)); ident = mail_user_get_anvil_userip_ident(client->user); if (ident != NULL) { master_service_anvil_send(master_service, t_strconcat( "CONNECT\t", my_pid, "\tpop3/", ident, "\n", NULL)); client->anvil_sent = TRUE; } return 0; } static const char *client_build_uidl_change_string(struct client *client) { uint32_t i, old_hash, new_hash; unsigned int old_msg_count, new_msg_count; if (client->message_uidls == NULL) { /* UIDL command not given */ return ""; } /* 1..new-1 were probably left to mailbox by previous POP3 session */ old_msg_count = client->lowest_retr_pop3_msn > 0 ? client->lowest_retr_pop3_msn - 1 : client->messages_count; for (i = 0, old_hash = 0; i < old_msg_count; i++) old_hash ^= crc32_str(client->message_uidls[i]); /* assume all except deleted messages were sent to POP3 client */ if (!client->deleted) { for (i = 0, new_hash = 0; i < client->messages_count; i++) new_hash ^= crc32_str(client->message_uidls[i]); } else { for (i = 0, new_hash = 0; i < client->messages_count; i++) { if (client->deleted_bitmask[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) continue; new_hash ^= crc32_str(client->message_uidls[i]); } } new_msg_count = client->messages_count - client->deleted_count; if (old_hash == new_hash && old_msg_count == new_msg_count) return t_strdup_printf("%u/%08x", old_msg_count, old_hash); else { return t_strdup_printf("%u/%08x -> %u/%08x", old_msg_count, old_hash, new_msg_count, new_hash); } } static const char *client_stats(struct client *client) { static struct var_expand_table static_tab[] = { { 'p', NULL, "top_bytes" }, { 't', NULL, "top_count" }, { 'b', NULL, "retr_bytes" }, { 'r', NULL, "retr_count" }, { 'd', NULL, "deleted_count" }, { 'm', NULL, "message_count" }, { 's', NULL, "message_bytes" }, { 'i', NULL, "input" }, { 'o', NULL, "output" }, { 'u', NULL, "uidl_change" }, { '\0', NULL, "session" }, { 'd', NULL, "deleted_bytes" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; string_t *str; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = dec2str(client->top_bytes); tab[1].value = dec2str(client->top_count); tab[2].value = dec2str(client->retr_bytes); tab[3].value = dec2str(client->retr_count); tab[4].value = client->delete_success ? dec2str(client->deleted_count) : "0"; tab[5].value = dec2str(client->messages_count); tab[6].value = dec2str(client->total_size); tab[7].value = dec2str(client->input->v_offset); tab[8].value = dec2str(client->output->offset); if (var_has_key(client->set->pop3_logout_format, tab[9].key, tab[9].long_key)) tab[9].value = client_build_uidl_change_string(client); else tab[9].value = ""; tab[10].value = client->session_id; tab[11].value = client->delete_success ? dec2str(client->deleted_size) : "0"; str = t_str_new(128); var_expand(str, client->set->pop3_logout_format, tab); return str_c(str); } void client_destroy(struct client *client, const char *reason) { client->v.destroy(client, reason); } static void client_default_destroy(struct client *client, const char *reason) { i_assert(!client->destroyed); client->destroyed = TRUE; if (client->seen_change_count > 0) (void)client_update_mails(client); if (!client->disconnected) { if (reason == NULL) { reason = io_stream_get_disconnect_reason(client->input, client->output); } i_info("%s %s", reason, client_stats(client)); } if (client->cmd != NULL) { /* deinitialize command */ i_stream_close(client->input); o_stream_close(client->output); client->cmd(client); i_assert(client->cmd == NULL); } if (client->trans != NULL) { /* client didn't QUIT, but we still want to save any changes done in this transaction. especially the cached virtual message sizes. */ (void)mailbox_transaction_commit(&client->trans); } if (array_is_created(&client->all_seqs)) array_free(&client->all_seqs); if (client->deleted_kw != NULL) mailbox_keywords_unref(&client->deleted_kw); if (client->mailbox != NULL) mailbox_free(&client->mailbox); if (client->anvil_sent) { master_service_anvil_send(master_service, t_strconcat( "DISCONNECT\t", my_pid, "\tpop3/", mail_user_get_anvil_userip_ident(client->user), "\n", NULL)); } if (client->session_dotlock != NULL) file_dotlock_delete(&client->session_dotlock); if (client->to_session_dotlock_refresh != NULL) timeout_remove(&client->to_session_dotlock_refresh); if (client->uidl_pool != NULL) pool_unref(&client->uidl_pool); i_free(client->message_sizes); i_free(client->deleted_bitmask); i_free(client->seen_bitmask); i_free(client->msgnum_to_seq_map); if (client->io != NULL) io_remove(&client->io); timeout_remove(&client->to_idle); if (client->to_commit != NULL) timeout_remove(&client->to_commit); i_stream_destroy(&client->input); o_stream_destroy(&client->output); fd_close_maybe_stdio(&client->fd_in, &client->fd_out); /* Autoexpunging might run for a long time. Disconnect the client before it starts, and refresh proctitle so it's clear that it's doing autoexpunging. We've also sent DISCONNECT to anvil already, because this is background work and shouldn't really be counted as an active POP3 session for the user. */ pop3_refresh_proctitle(); mail_user_autoexpunge(client->user); mail_user_unref(&client->user); mail_storage_service_user_unref(&client->service_user); pop3_client_count--; DLLIST_REMOVE(&pop3_clients, client); pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); pop3_refresh_proctitle(); } static void client_destroy_timeout(struct client *client) { client_destroy(client, NULL); } void client_disconnect(struct client *client, const char *reason) { if (client->disconnected) return; client->disconnected = TRUE; i_info("Disconnected: %s %s", reason, client_stats(client)); (void)o_stream_flush(client->output); i_stream_close(client->input); o_stream_close(client->output); if (client->to_idle != NULL) timeout_remove(&client->to_idle); client->to_idle = timeout_add(0, client_destroy_timeout, client); } void client_send_line(struct client *client, const char *fmt, ...) { va_list va; ssize_t ret; if (client->output->closed) return; va_start(va, fmt); T_BEGIN { string_t *str; str = t_str_new(256); str_vprintfa(str, fmt, va); str_append(str, "\r\n"); ret = o_stream_send(client->output, str_data(str), str_len(str)); i_assert(ret < 0 || (size_t)ret == str_len(str)); } T_END; if (ret >= 0) { if (!POP3_CLIENT_OUTPUT_FULL(client)) client->last_output = ioloop_time; else if (client->io != NULL) { /* no more input until client has read our output */ io_remove(&client->io); /* If someone happens to flush output, we want to get our IO handler back in flush callback */ o_stream_set_flush_pending(client->output, TRUE); } } va_end(va); } void client_send_storage_error(struct client *client) { const char *errstr; enum mail_error error; if (mailbox_is_inconsistent(client->mailbox)) { client_send_line(client, "-ERR [SYS/TEMP] Mailbox is in inconsistent " "state, please relogin."); client_disconnect(client, "Mailbox is in inconsistent state."); return; } errstr = mailbox_get_last_error(client->mailbox, &error); switch (error) { case MAIL_ERROR_TEMP: case MAIL_ERROR_NOQUOTA: case MAIL_ERROR_INUSE: client_send_line(client, "-ERR [SYS/TEMP] %s", errstr); break; default: client_send_line(client, "-ERR [SYS/PERM] %s", errstr); break; } } bool client_handle_input(struct client *client) { char *line, *args; int ret; o_stream_cork(client->output); while (!client->output->closed && (line = i_stream_next_line(client->input)) != NULL) { args = strchr(line, ' '); if (args != NULL) *args++ = '\0'; T_BEGIN { ret = client_command_execute(client, line, args != NULL ? args : ""); } T_END; if (ret >= 0) { client->bad_counter = 0; if (client->cmd != NULL) { o_stream_set_flush_pending(client->output, TRUE); client->waiting_input = TRUE; break; } } else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) { client_send_line(client, "-ERR Too many bad commands."); client_disconnect(client, "Too many bad commands."); } } o_stream_uncork(client->output); if (client->output->closed) { client_destroy(client, NULL); return FALSE; } return TRUE; } static void client_input(struct client *client) { if (client->cmd != NULL) { /* we're still processing a command. wait until it's finished. */ io_remove(&client->io); client->waiting_input = TRUE; return; } client->waiting_input = FALSE; client->last_input = ioloop_time; timeout_reset(client->to_idle); if (client->to_commit != NULL) timeout_reset(client->to_commit); switch (i_stream_read(client->input)) { case -1: /* disconnected */ client_destroy(client, NULL); return; case -2: /* line too long, kill it */ client_send_line(client, "-ERR Input line too long."); client_destroy(client, "Input line too long"); return; } (void)client_handle_input(client); } static int client_output(struct client *client) { o_stream_cork(client->output); if (o_stream_flush(client->output) < 0) { client_destroy(client, NULL); return 1; } client->last_output = ioloop_time; timeout_reset(client->to_idle); if (client->to_commit != NULL) timeout_reset(client->to_commit); if (client->cmd != NULL) client->cmd(client); if (client->cmd == NULL) { if (o_stream_get_buffer_used_size(client->output) < POP3_OUTBUF_THROTTLE_SIZE/2 && client->io == NULL && !client->input->closed) { /* enable input again */ client->io = io_add_istream(client->input, client_input, client); } if (client->io != NULL && client->waiting_input) { if (!client_handle_input(client)) { /* client got destroyed */ return 1; } } } o_stream_uncork(client->output); if (client->cmd != NULL) { /* command not finished yet */ return 0; } else if (client->io == NULL) { /* data still in output buffer, get back here to add IO */ return 0; } else { return 1; } } void clients_destroy_all(struct mail_storage_service_ctx *storage_service) { while (pop3_clients != NULL) { if (pop3_clients->cmd == NULL) { client_send_line(pop3_clients, "-ERR [SYS/TEMP] Server shutting down."); } mail_storage_service_io_activate_user(pop3_clients->service_user); client_destroy(pop3_clients, "Server shutting down."); } mail_storage_service_io_deactivate(storage_service); } struct pop3_client_vfuncs pop3_client_vfuncs = { client_default_destroy }; dovecot-2.2.33.2/src/pop3/pop3-commands.c0000644000175000017500000005737413165463624014653 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "pop3-common.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "hash.h" #include "str.h" #include "var-expand.h" #include "message-size.h" #include "mail-storage.h" #include "mail-storage-settings.h" #include "mail-search-build.h" #include "pop3-capability.h" #include "pop3-commands.h" static enum mail_sort_type pop3_sort_program[] = { MAIL_SORT_POP3_ORDER, MAIL_SORT_END }; static uint32_t msgnum_to_seq(struct client *client, uint32_t msgnum) { return msgnum < client->msgnum_to_seq_map_count ? client->msgnum_to_seq_map[msgnum] : msgnum+1; } static const char *get_msgnum(struct client *client, const char *args, unsigned int *msgnum, bool thenspace) { unsigned int num; if (*args < '0' || *args > '9') { client_send_line(client, "-ERR Invalid message number: %s", args); return NULL; } if (str_parse_uint(args, &num, &args) < 0) { client_send_line(client, "-ERR Message number too large: %s", args); return NULL; } if (*args != (thenspace ? ' ' : '\0')) { client_send_line(client, "-ERR Noise after message number: %s", args); return NULL; } if (num == 0 || num > client->messages_count) { client_send_line(client, "-ERR There's no message %u.", num); return NULL; } num--; if (client->deleted) { if (client->deleted_bitmask[num / CHAR_BIT] & (1 << (num % CHAR_BIT))) { client_send_line(client, "-ERR Message is deleted."); return NULL; } } while (*args == ' ') args++; *msgnum = num; return args; } static const char *get_size(struct client *client, const char *args, uoff_t *size, bool thenspace) { uoff_t num; if (*args < '0' || *args > '9') { client_send_line(client, "-ERR Invalid size: %s", args); return NULL; } if (str_parse_uoff(args, &num, &args) < 0) { client_send_line(client, "-ERR Size too large: %s", args); return NULL; } if (*args != (thenspace ? ' ' : '\0')) { client_send_line(client, "-ERR Noise after size: %s", args); return NULL; } while (*args == ' ') args++; *size = num; return args; } static int cmd_capa(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "+OK\r\n"POP3_CAPABILITY_REPLY"."); return 1; } static int cmd_dele(struct client *client, const char *args) { unsigned int msgnum; if (get_msgnum(client, args, &msgnum, FALSE) == NULL) return -1; if (!client->deleted) { client->deleted_bitmask = i_malloc(MSGS_BITMASK_SIZE(client)); client->deleted = TRUE; } client->deleted_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT); client->deleted_count++; client->deleted_size += client->message_sizes[msgnum]; client_send_line(client, "+OK Marked to be deleted."); return 1; } struct cmd_list_context { unsigned int msgnum; }; static void cmd_list_callback(struct client *client) { struct cmd_list_context *ctx = client->cmd_context; for (; ctx->msgnum != client->messages_count; ctx->msgnum++) { if (client->output->closed) break; if (POP3_CLIENT_OUTPUT_FULL(client)) { /* buffer full */ return; } if (client->deleted) { if (client->deleted_bitmask[ctx->msgnum / CHAR_BIT] & (1 << (ctx->msgnum % CHAR_BIT))) continue; } client_send_line(client, "%u %"PRIuUOFF_T, ctx->msgnum+1, client->message_sizes[ctx->msgnum]); } client_send_line(client, "."); i_free(ctx); client->cmd = NULL; } static int cmd_list(struct client *client, const char *args) { struct cmd_list_context *ctx; if (*args == '\0') { ctx = i_new(struct cmd_list_context, 1); client_send_line(client, "+OK %u messages:", client->messages_count - client->deleted_count); client->cmd = cmd_list_callback; client->cmd_context = ctx; cmd_list_callback(client); } else { unsigned int msgnum; if (get_msgnum(client, args, &msgnum, FALSE) == NULL) return -1; client_send_line(client, "+OK %u %"PRIuUOFF_T, msgnum+1, client->message_sizes[msgnum]); } return 1; } static int cmd_last(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "+OK %u", client->last_seen_pop3_msn); return 1; } static int cmd_noop(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "+OK"); return 1; } static struct mail_search_args * pop3_search_build(struct client *client, uint32_t seq) { struct mail_search_args *search_args; struct mail_search_arg *sarg; search_args = mail_search_build_init(); if (seq == 0) { sarg = mail_search_build_add(search_args, SEARCH_SEQSET); sarg->value.seqset = client->all_seqs; } else { mail_search_build_add_seqset(search_args, seq, seq); } return search_args; } static int client_verify_ordering(struct client *client, struct mail *mail, uint32_t msgnum) { uint32_t seq; seq = msgnum_to_seq(client, msgnum); if (seq != mail->seq) { i_error("Message ordering changed unexpectedly " "(msg #%u: storage seq %u -> %u)", msgnum+1, seq, mail->seq); return -1; } return 0; } static void client_expunge(struct client *client, struct mail *mail) { switch (client->set->parsed_delete_type) { case POP3_DELETE_TYPE_EXPUNGE: mail_expunge(mail); break; case POP3_DELETE_TYPE_FLAG: i_assert(client->deleted_kw != NULL); mail_update_keywords(mail, MODIFY_ADD, client->deleted_kw); break; } } bool client_update_mails(struct client *client) { struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; ARRAY_TYPE(seq_range) deleted_msgs, seen_msgs; uint32_t msgnum, bit; bool ret = TRUE; if (mailbox_is_readonly(client->mailbox)) { /* silently ignore */ return TRUE; } /* translate msgnums to sequences (in case POP3 ordering is different) */ t_array_init(&deleted_msgs, 8); if (client->deleted_bitmask != NULL) { for (msgnum = 0; msgnum < client->messages_count; msgnum++) { bit = 1 << (msgnum % CHAR_BIT); if ((client->deleted_bitmask[msgnum / CHAR_BIT] & bit) != 0) seq_range_array_add(&deleted_msgs, msgnum_to_seq(client, msgnum)); } } t_array_init(&seen_msgs, 8); if (client->seen_bitmask != NULL) { for (msgnum = 0; msgnum < client->messages_count; msgnum++) { bit = 1 << (msgnum % CHAR_BIT); if ((client->seen_bitmask[msgnum / CHAR_BIT] & bit) != 0) seq_range_array_add(&seen_msgs, msgnum_to_seq(client, msgnum)); } } search_args = pop3_search_build(client, 0); ctx = mailbox_search_init(client->trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { if (seq_range_exists(&deleted_msgs, mail->seq)) client_expunge(client, mail); else if (seq_range_exists(&seen_msgs, mail->seq)) mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN); } client->seen_change_count = 0; if (mailbox_search_deinit(&ctx) < 0) ret = FALSE; return ret; } static int cmd_quit(struct client *client, const char *args ATTR_UNUSED) { if (client->deleted || client->seen_bitmask != NULL) { if (!client_update_mails(client)) { client_send_storage_error(client); client_disconnect(client, "Storage error during logout."); return 1; } } if (mailbox_transaction_commit(&client->trans) < 0 || mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_WRITE) < 0) { client_send_storage_error(client); client_disconnect(client, "Storage error during logout."); return 1; } else { client->delete_success = TRUE; } if (!client->deleted) client_send_line(client, "+OK Logging out."); else client_send_line(client, "+OK Logging out, messages deleted."); client_disconnect(client, "Logged out"); return 1; } struct fetch_context { struct mail *mail; struct istream *stream; uoff_t body_lines; uoff_t *byte_counter; uoff_t byte_counter_offset; unsigned char last; bool cr_skipped, in_body; }; static void fetch_deinit(struct fetch_context *ctx) { mail_free(&ctx->mail); i_free(ctx); } static void fetch_callback(struct client *client) { struct fetch_context *ctx = client->cmd_context; const unsigned char *data; unsigned char add; size_t i, size; int ret; while ((ctx->body_lines > 0 || !ctx->in_body) && i_stream_read_data(ctx->stream, &data, &size, 0) > 0) { if (size > 4096) size = 4096; add = '\0'; for (i = 0; i < size; i++) { if ((data[i] == '\r' || data[i] == '\n') && !ctx->in_body) { if (i == 0 && (ctx->last == '\0' || ctx->last == '\n')) ctx->in_body = TRUE; else if (i > 0 && data[i-1] == '\n') ctx->in_body = TRUE; } if (data[i] == '\n') { if ((i == 0 && ctx->last != '\r') || (i > 0 && data[i-1] != '\r')) { /* missing CR */ add = '\r'; break; } if (ctx->in_body) { if (--ctx->body_lines == 0) { i++; break; } } } else if (data[i] == '.' && ((i == 0 && ctx->last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; break; } else if (data[i] == '\0' && (client->set->parsed_workarounds & WORKAROUND_OUTLOOK_NO_NULS) != 0) { add = 0x80; break; } } if (i > 0) { if (o_stream_send(client->output, data, i) < 0) break; ctx->last = data[i-1]; i_stream_skip(ctx->stream, i); } if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) { /* continue later */ return; } } if (add != '\0') { if (o_stream_send(client->output, &add, 1) < 0) break; ctx->last = add; if (add == 0x80) i_stream_skip(ctx->stream, 1); } } if (ctx->last != '\n') { /* didn't end with CRLF */ o_stream_nsend(client->output, "\r\n", 2); } if (!ctx->in_body && (client->set->parsed_workarounds & WORKAROUND_OE_NS_EOH) != 0) { /* Add the missing end of headers line. */ o_stream_nsend(client->output, "\r\n", 2); } *ctx->byte_counter += client->output->offset - ctx->byte_counter_offset; client_send_line(client, "."); fetch_deinit(ctx); client->cmd = NULL; } static int client_reply_msg_expunged(struct client *client, unsigned int msgnum) { client_send_line(client, "-ERR Message %u expunged.", msgnum + 1); if (msgnum <= client->highest_expunged_fetch_msgnum) { /* client tried to fetch an expunged message again. treat this as error so we'll eventually disconnect the client instead of letting it loop forever. */ return -1; } client->highest_expunged_fetch_msgnum = msgnum; return 1; } static int fetch(struct client *client, unsigned int msgnum, uoff_t body_lines, const char *reason, uoff_t *byte_counter) { struct fetch_context *ctx; int ret; ctx = i_new(struct fetch_context, 1); ctx->byte_counter = byte_counter; ctx->byte_counter_offset = client->output->offset; ctx->mail = mail_alloc(client->trans, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); mail_set_seq(ctx->mail, msgnum_to_seq(client, msgnum)); if (mail_get_stream_because(ctx->mail, NULL, NULL, reason, &ctx->stream) < 0) { ret = client_reply_msg_expunged(client, msgnum); fetch_deinit(ctx); return ret; } if (body_lines == (uoff_t)-1 && client->seen_bitmask != NULL) { if ((mail_get_flags(ctx->mail) & MAIL_SEEN) == 0) { /* mark the message seen with RETR command */ client->seen_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT); client->seen_change_count++; } } ctx->body_lines = body_lines; if (body_lines == (uoff_t)-1) { client_send_line(client, "+OK %"PRIuUOFF_T" octets", client->message_sizes[msgnum]); } else { client_send_line(client, "+OK"); ctx->body_lines++; /* internally we count the empty line too */ } client->cmd = fetch_callback; client->cmd_context = ctx; fetch_callback(client); return 1; } static int cmd_retr(struct client *client, const char *args) { unsigned int msgnum; if (get_msgnum(client, args, &msgnum, FALSE) == NULL) return -1; if (client->lowest_retr_pop3_msn > msgnum+1 || client->lowest_retr_pop3_msn == 0) client->lowest_retr_pop3_msn = msgnum+1; if (client->last_seen_pop3_msn < msgnum+1) client->last_seen_pop3_msn = msgnum+1; client->retr_count++; return fetch(client, msgnum, (uoff_t)-1, "RETR", &client->retr_bytes); } static int cmd_rset(struct client *client, const char *args ATTR_UNUSED) { struct mail_search_context *search_ctx; struct mail *mail; struct mail_search_args *search_args; client->last_seen_pop3_msn = 0; if (client->deleted) { client->deleted = FALSE; memset(client->deleted_bitmask, 0, MSGS_BITMASK_SIZE(client)); client->deleted_count = 0; client->deleted_size = 0; } if (client->seen_change_count > 0) { memset(client->seen_bitmask, 0, MSGS_BITMASK_SIZE(client)); client->seen_change_count = 0; } if (client->set->pop3_enable_last) { /* remove all \Seen flags (as specified by RFC 1460) */ search_args = pop3_search_build(client, 0); search_ctx = mailbox_search_init(client->trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) mail_update_flags(mail, MODIFY_REMOVE, MAIL_SEEN); (void)mailbox_search_deinit(&search_ctx); (void)mailbox_transaction_commit(&client->trans); client->trans = mailbox_transaction_begin(client->mailbox, 0); } client_send_line(client, "+OK"); return 1; } static int cmd_stat(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "+OK %u %"PRIuUOFF_T, client->messages_count - client->deleted_count, client->total_size - client->deleted_size); return 1; } static int cmd_top(struct client *client, const char *args) { unsigned int msgnum; uoff_t max_lines; args = get_msgnum(client, args, &msgnum, TRUE); if (args == NULL) return -1; if (get_size(client, args, &max_lines, FALSE) == NULL) return -1; client->top_count++; return fetch(client, msgnum, max_lines, "TOP", &client->top_bytes); } struct cmd_uidl_context { struct mail_search_context *search_ctx; struct mail *mail; uint32_t msgnum; bool list_all; }; static int pop3_get_uid(struct client *client, struct mail *mail, string_t *str, bool *permanent_uidl_r) { static struct var_expand_table static_tab[] = { { 'v', NULL, "uidvalidity" }, { 'u', NULL, "uid" }, { 'm', NULL, "md5" }, { 'f', NULL, "filename" }, { 'g', NULL, "guid" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; char uid_str[MAX_INT_STRLEN]; const char *uidl; if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) == 0 && *uidl != '\0') { str_append(str, uidl); /* UIDL is already permanent */ *permanent_uidl_r = TRUE; return 0; } *permanent_uidl_r = FALSE; if (client->set->pop3_reuse_xuidl && mail_get_first_header(mail, "X-UIDL", &uidl) > 0) { str_append(str, uidl); return 0; } tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = t_strdup_printf("%u", client->uid_validity); if ((client->uidl_keymask & UIDL_UID) != 0) { if (i_snprintf(uid_str, sizeof(uid_str), "%u", mail->uid) < 0) i_unreached(); tab[1].value = uid_str; } if ((client->uidl_keymask & UIDL_MD5) != 0) { if (mail_get_special(mail, MAIL_FETCH_HEADER_MD5, &tab[2].value) < 0) { i_error("UIDL: Header MD5 lookup failed: %s", mailbox_get_last_internal_error(mail->box, NULL)); return -1; } else if (*tab[2].value == '\0') { i_error("UIDL: Header MD5 not found " "(pop3_uidl_format=%%m not supported by storage?)"); return -1; } } if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) { if (mail_get_special(mail, MAIL_FETCH_STORAGE_ID, &tab[3].value) < 0) { i_error("UIDL: File name lookup failed: %s", mailbox_get_last_internal_error(mail->box, NULL)); return -1; } else if (*tab[3].value == '\0') { i_error("UIDL: File name not found " "(pop3_uidl_format=%%f not supported by storage?)"); return -1; } } if ((client->uidl_keymask & UIDL_GUID) != 0) { if (mail_get_special(mail, MAIL_FETCH_GUID, &tab[4].value) < 0) { i_error("UIDL: Message GUID lookup failed: %s", mailbox_get_last_internal_error(mail->box, NULL)); return -1; } else if (*tab[4].value == '\0') { i_error("UIDL: Message GUID not found " "(pop3_uidl_format=%%g not supported by storage?)"); return -1; } } var_expand(str, client->mail_set->pop3_uidl_format, tab); return 0; } static bool list_uidls_saved_iter(struct client *client, struct cmd_uidl_context *ctx) { bool found = FALSE; while (ctx->msgnum < client->messages_count) { uint32_t msgnum = ctx->msgnum++; if (client->deleted) { if (client->deleted_bitmask[msgnum / CHAR_BIT] & (1 << (msgnum % CHAR_BIT))) continue; } found = TRUE; client_send_line(client, ctx->list_all ? "%u %s" : "+OK %u %s", msgnum+1, client->message_uidls[msgnum]); if (client->output->closed || !ctx->list_all) break; if (POP3_CLIENT_OUTPUT_FULL(client)) { /* output is being buffered, continue when there's more space */ return FALSE; } } /* finished */ client->cmd = NULL; if (ctx->list_all) client_send_line(client, "."); i_free(ctx); return found; } static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) { string_t *str; bool permanent_uidl, found = FALSE; bool failed = FALSE; if (client->message_uidls != NULL) return list_uidls_saved_iter(client, ctx); str = t_str_new(128); while (mailbox_search_next(ctx->search_ctx, &ctx->mail)) { uint32_t msgnum = ctx->msgnum++; if (client_verify_ordering(client, ctx->mail, msgnum) < 0) { failed = TRUE; break; } if (client->deleted) { if (client->deleted_bitmask[msgnum / CHAR_BIT] & (1 << (msgnum % CHAR_BIT))) continue; } found = TRUE; str_truncate(str, 0); if (pop3_get_uid(client, ctx->mail, str, &permanent_uidl) < 0) { failed = TRUE; break; } if (client->set->pop3_save_uidl && !permanent_uidl) mail_update_pop3_uidl(ctx->mail, str_c(str)); client_send_line(client, ctx->list_all ? "%u %s" : "+OK %u %s", msgnum+1, str_c(str)); if (client->output->closed) break; if (POP3_CLIENT_OUTPUT_FULL(client) && ctx->list_all) { /* output is being buffered, continue when there's more space */ return FALSE; } } /* finished */ (void)mailbox_search_deinit(&ctx->search_ctx); client->cmd = NULL; if (ctx->list_all && !failed) client_send_line(client, "."); i_free(ctx); if (failed) client_disconnect(client, "POP3 UIDLs couldn't be listed"); return found || failed; } static void cmd_uidl_callback(struct client *client) { struct cmd_uidl_context *ctx = client->cmd_context; (void)list_uids_iter(client, ctx); } HASH_TABLE_DEFINE_TYPE(uidl_counter, char *, void *); static void uidl_rename_duplicate(string_t *uidl, HASH_TABLE_TYPE(uidl_counter) prev_uidls) { char *key; void *value; unsigned int counter; while (hash_table_lookup_full(prev_uidls, str_c(uidl), &key, &value)) { /* duplicate. the value contains the number of duplicates. */ counter = POINTER_CAST_TO(value, unsigned int) + 1; hash_table_update(prev_uidls, key, POINTER_CAST(counter)); str_printfa(uidl, "-%u", counter); /* the second lookup really should return NULL, but just in case of some weird UIDLs do this as many times as needed */ } } static void client_uidls_save(struct client *client) { struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct mail *mail; HASH_TABLE_TYPE(uidl_counter) prev_uidls; const char **seq_uidls; string_t *str; char *uidl; enum mail_fetch_field wanted_fields; uint32_t msgnum; bool permanent_uidl, uidl_duplicates_rename, failed = FALSE; i_assert(client->message_uidls == NULL); search_args = pop3_search_build(client, 0); wanted_fields = 0; if ((client->uidl_keymask & UIDL_MD5) != 0) wanted_fields |= MAIL_FETCH_HEADER_MD5; search_ctx = mailbox_search_init(client->trans, search_args, NULL, wanted_fields, NULL); mail_search_args_unref(&search_args); uidl_duplicates_rename = strcmp(client->set->pop3_uidl_duplicates, "rename") == 0; if (uidl_duplicates_rename) hash_table_create(&prev_uidls, default_pool, 0, str_hash, strcmp); client->uidl_pool = pool_alloconly_create("message uidls", 1024); /* first read all the UIDLs into a temporary [seq] array */ seq_uidls = i_new(const char *, client->highest_seq); str = t_str_new(128); while (mailbox_search_next(search_ctx, &mail)) { str_truncate(str, 0); if (pop3_get_uid(client, mail, str, &permanent_uidl) < 0) { failed = TRUE; break; } if (uidl_duplicates_rename) uidl_rename_duplicate(str, prev_uidls); uidl = p_strdup(client->uidl_pool, str_c(str)); if (client->set->pop3_save_uidl && !permanent_uidl) mail_update_pop3_uidl(mail, uidl); i_assert(mail->seq <= client->highest_seq); seq_uidls[mail->seq-1] = uidl; if (uidl_duplicates_rename) hash_table_insert(prev_uidls, uidl, POINTER_CAST(1)); } (void)mailbox_search_deinit(&search_ctx); if (uidl_duplicates_rename) hash_table_destroy(&prev_uidls); if (failed) { pool_unref(&client->uidl_pool); i_free(seq_uidls); return; } /* map UIDLs to msgnums (in case POP3 sort ordering is different) */ client->message_uidls = p_new(client->uidl_pool, const char *, MALLOC_ADD(client->messages_count, 1)); for (msgnum = 0; msgnum < client->messages_count; msgnum++) { client->message_uidls[msgnum] = seq_uidls[msgnum_to_seq(client, msgnum) - 1]; } i_free(seq_uidls); } static struct cmd_uidl_context * cmd_uidl_init(struct client *client, uint32_t seq) { struct cmd_uidl_context *ctx; struct mail_search_args *search_args; enum mail_fetch_field wanted_fields; if (client->message_uidls_save && client->message_uidls == NULL && client->messages_count > 0) client_uidls_save(client); ctx = i_new(struct cmd_uidl_context, 1); ctx->list_all = seq == 0; if (client->message_uidls == NULL) { wanted_fields = 0; if ((client->uidl_keymask & UIDL_MD5) != 0) wanted_fields |= MAIL_FETCH_HEADER_MD5; search_args = pop3_search_build(client, seq); ctx->search_ctx = mailbox_search_init(client->trans, search_args, pop3_sort_program, wanted_fields, NULL); mail_search_args_unref(&search_args); } if (seq == 0) { client->cmd = cmd_uidl_callback; client->cmd_context = ctx; } return ctx; } static int cmd_uidl(struct client *client, const char *args) { struct cmd_uidl_context *ctx; uint32_t seq; if (*args == '\0') { client_send_line(client, "+OK"); ctx = cmd_uidl_init(client, 0); (void)list_uids_iter(client, ctx); } else { unsigned int msgnum; if (get_msgnum(client, args, &msgnum, FALSE) == NULL) return -1; seq = msgnum_to_seq(client, msgnum); ctx = cmd_uidl_init(client, seq); ctx->msgnum = msgnum; if (!list_uids_iter(client, ctx)) return client_reply_msg_expunged(client, msgnum); } return 1; } int client_command_execute(struct client *client, const char *name, const char *args) { /* keep the command uppercased */ name = t_str_ucase(name); while (*args == ' ') args++; switch (*name) { case 'C': if (strcmp(name, "CAPA") == 0) return cmd_capa(client, args); break; case 'D': if (strcmp(name, "DELE") == 0) return cmd_dele(client, args); break; case 'L': if (strcmp(name, "LIST") == 0) return cmd_list(client, args); if (strcmp(name, "LAST") == 0 && client->set->pop3_enable_last) return cmd_last(client, args); break; case 'N': if (strcmp(name, "NOOP") == 0) return cmd_noop(client, args); break; case 'Q': if (strcmp(name, "QUIT") == 0) return cmd_quit(client, args); break; case 'R': if (strcmp(name, "RETR") == 0) return cmd_retr(client, args); if (strcmp(name, "RSET") == 0) return cmd_rset(client, args); break; case 'S': if (strcmp(name, "STAT") == 0) return cmd_stat(client, args); break; case 'T': if (strcmp(name, "TOP") == 0) return cmd_top(client, args); break; case 'U': if (strcmp(name, "UIDL") == 0) return cmd_uidl(client, args); break; } client_send_line(client, "-ERR Unknown command: %s", name); return -1; } dovecot-2.2.33.2/src/pop3/pop3-settings.c0000644000175000017500000001156013123174404014662 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "pop3-settings.h" #include #include static bool pop3_settings_verify(void *_set, pool_t pool, const char **error_r); /* */ static struct file_listener_settings pop3_unix_listeners_array[] = { { "login/pop3", 0666, "", "" } }; static struct file_listener_settings *pop3_unix_listeners[] = { &pop3_unix_listeners_array[0] }; static buffer_t pop3_unix_listeners_buf = { pop3_unix_listeners, sizeof(pop3_unix_listeners), { NULL, } }; /* */ struct service_settings pop3_service_settings = { .name = "pop3", .protocol = "pop3", .type = "", .executable = "pop3", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &pop3_unix_listeners_buf, sizeof(pop3_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct pop3_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct pop3_settings, field), defines } static const struct setting_define pop3_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR_VARS, rawlog_dir), DEF(SET_BOOL, pop3_no_flag_updates), DEF(SET_BOOL, pop3_enable_last), DEF(SET_BOOL, pop3_reuse_xuidl), DEF(SET_BOOL, pop3_save_uidl), DEF(SET_BOOL, pop3_lock_session), DEF(SET_BOOL, pop3_fast_size_lookups), DEF(SET_STR, pop3_client_workarounds), DEF(SET_STR, pop3_logout_format), DEF(SET_ENUM, pop3_uidl_duplicates), DEF(SET_STR, pop3_deleted_flag), DEF(SET_ENUM, pop3_delete_type), SETTING_DEFINE_LIST_END }; static const struct pop3_settings pop3_default_settings = { .verbose_proctitle = FALSE, .rawlog_dir = "", .pop3_no_flag_updates = FALSE, .pop3_enable_last = FALSE, .pop3_reuse_xuidl = FALSE, .pop3_save_uidl = FALSE, .pop3_lock_session = FALSE, .pop3_fast_size_lookups = FALSE, .pop3_client_workarounds = "", .pop3_logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", .pop3_uidl_duplicates = "allow:rename", .pop3_deleted_flag = "", .pop3_delete_type = "default:expunge:flag" }; static const struct setting_parser_info *pop3_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info pop3_setting_parser_info = { .module_name = "pop3", .defines = pop3_setting_defines, .defaults = &pop3_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct pop3_settings), .parent_offset = (size_t)-1, .check_func = pop3_settings_verify, .dependencies = pop3_setting_dependencies }; /* */ struct pop3_client_workaround_list { const char *name; enum pop3_client_workarounds num; }; static const struct pop3_client_workaround_list pop3_client_workaround_list[] = { { "outlook-no-nuls", WORKAROUND_OUTLOOK_NO_NULS }, { "oe-ns-eoh", WORKAROUND_OE_NS_EOH }, { NULL, 0 } }; static int pop3_settings_parse_workarounds(struct pop3_settings *set, const char **error_r) { enum pop3_client_workarounds client_workarounds = 0; const struct pop3_client_workaround_list *list; const char *const *str; str = t_strsplit_spaces(set->pop3_client_workarounds, " ,"); for (; *str != NULL; str++) { list = pop3_client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { client_workarounds |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("pop3_client_workarounds: " "Unknown workaround: %s", *str); return -1; } } set->parsed_workarounds = client_workarounds; return 0; } static bool pop3_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct pop3_settings *set = _set; if (pop3_settings_parse_workarounds(set, error_r) < 0) return FALSE; if (strcmp(set->pop3_delete_type, "default") == 0) { if (set->pop3_deleted_flag[0] == '\0') set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE; else set->parsed_delete_type = POP3_DELETE_TYPE_FLAG; } else if (strcmp(set->pop3_delete_type, "expunge") == 0) { set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE; } else if (strcmp(set->pop3_delete_type, "flag") == 0) { if (set->pop3_deleted_flag[0] == '\0') { *error_r = "pop3_delete_type=flag, but pop3_deleted_flag not set"; return FALSE; } set->parsed_delete_type = POP3_DELETE_TYPE_FLAG; } else { *error_r = t_strdup_printf("pop3_delete_type: Unknown value '%s'", set->pop3_delete_type); return FALSE; } return TRUE; } /* */ dovecot-2.2.33.2/src/pop3/pop3-commands.h0000644000175000017500000000022613123174404014625 00000000000000#ifndef POP3_COMMANDS_H #define POP3_COMMANDS_H int client_command_execute(struct client *client, const char *name, const char *args); #endif dovecot-2.2.33.2/src/pop3/pop3-capability.h0000644000175000017500000000032213123174404015142 00000000000000#ifndef POP3_CAPABILITY_H #define POP3_CAPABILITY_H #define POP3_CAPABILITY_REPLY \ "CAPA\r\n" \ "TOP\r\n" \ "UIDL\r\n" \ "RESP-CODES\r\n" \ "PIPELINING\r\n" \ "AUTH-RESP-CODE\r\n" /* + SASL */ #endif dovecot-2.2.33.2/src/pop3/Makefile.am0000644000175000017500000000125613165463624014047 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = pop3 AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage pop3_LDFLAGS = -export-dynamic pop3_LDADD = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) pop3_DEPENDENCIES = \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) pop3_SOURCES = \ main.c \ pop3-client.c \ pop3-commands.c \ pop3-settings.c headers = \ pop3-capability.h \ pop3-client.h \ pop3-commands.h \ pop3-common.h \ pop3-settings.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-settings/0002755000175000017500000000000013172375610013607 500000000000000dovecot-2.2.33.2/src/lib-settings/Makefile.in0000644000175000017500000005667613172375573015626 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-settings ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libsettings_la_LIBADD = am_libsettings_la_OBJECTS = settings.lo settings-parser.lo libsettings_la_OBJECTS = $(am_libsettings_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-settings-parser$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_settings_parser_OBJECTS = test-settings-parser.$(OBJEXT) test_settings_parser_OBJECTS = $(am_test_settings_parser_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsettings_la_SOURCES) $(test_settings_parser_SOURCES) DIST_SOURCES = $(libsettings_la_SOURCES) \ $(test_settings_parser_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libsettings.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libsettings_la_SOURCES = \ settings.c \ settings-parser.c headers = \ settings.h \ settings-parser.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-settings-parser test_libs = \ libsettings.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_settings_parser_SOURCES = test-settings-parser.c test_settings_parser_LDADD = $(test_libs) test_settings_parser_DEPENDENCIES = $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-settings/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-settings/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsettings.la: $(libsettings_la_OBJECTS) $(libsettings_la_DEPENDENCIES) $(EXTRA_libsettings_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libsettings_la_OBJECTS) $(libsettings_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-settings-parser$(EXEEXT): $(test_settings_parser_OBJECTS) $(test_settings_parser_DEPENDENCIES) $(EXTRA_test_settings_parser_DEPENDENCIES) @rm -f test-settings-parser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_settings_parser_OBJECTS) $(test_settings_parser_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-settings-parser.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-settings/test-settings-parser.c0000644000175000017500000000647713165463624020023 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "test-common.h" static void test_settings_get_time(void) { struct { const char *input; unsigned int output; } tests[] = { { "0", 0 }, { "59s", 59 }, { "59 s", 59 }, { "59se", 59 }, { "59sec", 59 }, { "59secs", 59 }, { "59seco", 59 }, { "59secon", 59 }, { "59second", 59 }, { "59seconds", 59 }, { "123456 seconds", 123456 }, { "123m", 123*60 }, { "123 m", 123*60 }, { "123 mi", 123*60 }, { "123 min", 123*60 }, { "123 mins", 123*60 }, { "123 minu", 123*60 }, { "123 minut", 123*60 }, { "123 minute", 123*60 }, { "123 minutes", 123*60 }, { "123h", 123*60*60 }, { "123 h", 123*60*60 }, { "123 ho", 123*60*60 }, { "123 hou", 123*60*60 }, { "123 hour", 123*60*60 }, { "123 hours", 123*60*60 }, { "12d", 12*60*60*24 }, { "12 d", 12*60*60*24 }, { "12 da", 12*60*60*24 }, { "12 day", 12*60*60*24 }, { "12 days", 12*60*60*24 }, { "3w", 3*60*60*24*7 }, { "3 w", 3*60*60*24*7 }, { "3 we", 3*60*60*24*7 }, { "3 wee", 3*60*60*24*7 }, { "3 week", 3*60*60*24*7 }, { "3 weeks", 3*60*60*24*7 }, { "1000ms", 1 }, { "50000ms", 50 }, }; struct { const char *input; unsigned int output; } msecs_tests[] = { { "0ms", 0 }, { "1ms", 1 }, { "123456ms", 123456 }, { "123456 ms", 123456 }, { "123456mse", 123456 }, { "123456msec", 123456 }, { "123456msecs", 123456 }, { "123456mseco", 123456 }, { "123456msecon", 123456 }, { "123456msecond", 123456 }, { "123456mseconds", 123456 }, { "123456mil", 123456 }, { "123456mill", 123456 }, { "123456milli", 123456 }, { "123456millis", 123456 }, { "123456millisec", 123456 }, { "123456millisecs", 123456 }, { "123456milliseco", 123456 }, { "123456millisecon", 123456 }, { "123456millisecond", 123456 }, { "123456milliseconds", 123456 }, { "4294967295 ms", 4294967295 }, }; const char *secs_errors[] = { "-1", /* wrong spellings: */ "1ss", "1secss", "1secondss", "1ma", "1minsa", "1hu", "1hoursa", "1dd", "1days?", "1wa", "1weeksb", /* milliseconds: */ "1ms", "999ms", "1001ms", /* overflows: */ "7102 w", "4294967296 s", }; const char *msecs_errors[] = { "-1", /* wrong spellings: */ "1mis", "1mss", /* overflows: */ "8 w", "4294967296 ms", }; unsigned int i, secs, msecs; const char *error; test_begin("settings_get_time()"); for (i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(settings_get_time(tests[i].input, &secs, &error) == 0, i); test_assert_idx(secs == tests[i].output, i); test_assert_idx(settings_get_time_msecs(tests[i].input, &msecs, &error) == 0, i); test_assert_idx(msecs == tests[i].output*1000, i); } for (i = 0; i < N_ELEMENTS(msecs_tests); i++) { test_assert_idx(settings_get_time_msecs(msecs_tests[i].input, &msecs, &error) == 0, i); test_assert_idx(msecs == msecs_tests[i].output, i); } for (i = 0; i < N_ELEMENTS(secs_errors); i++) test_assert_idx(settings_get_time(secs_errors[i], &secs, &error) < 0, i); for (i = 0; i < N_ELEMENTS(msecs_errors); i++) test_assert_idx(settings_get_time_msecs(msecs_errors[i], &msecs, &error) < 0, i); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_settings_get_time, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-settings/settings.h0000644000175000017500000000431113123174404015527 00000000000000#ifndef SETTINGS_H #define SETTINGS_H enum setting_type { SET_STR, SET_INT, SET_BOOL }; struct setting_def { enum setting_type type; const char *name; size_t offset; }; #define DEF_STRUCT_STR(name, struct_name) \ { SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, const char *), \ #name, offsetof(struct struct_name, name) } #define DEF_STRUCT_INT(name, struct_name) \ { SET_INT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, unsigned int), \ #name, offsetof(struct struct_name, name) } #define DEF_STRUCT_BOOL(name, struct_name) \ { SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, bool), \ #name, offsetof(struct struct_name, name) } /* Return error message. When closing section, key = NULL, value = NULL. */ typedef const char *settings_callback_t(const char *key, const char *value, void *context); /* Return TRUE if we want to go inside the section */ typedef bool settings_section_callback_t(const char *type, const char *name, void *context, const char **errormsg); extern settings_section_callback_t *null_settings_section_callback; const char * parse_setting_from_defs(pool_t pool, const struct setting_def *defs, void *base, const char *key, const char *value); bool settings_read_i(const char *path, const char *section, settings_callback_t *callback, settings_section_callback_t *sect_callback, void *context, const char **error_r) ATTR_NULL(2, 4, 5); #define settings_read(path, section, callback, sect_callback, context, error_r) \ settings_read_i(path + \ CALLBACK_TYPECHECK(callback, const char *(*)( \ const char *, const char *, typeof(context))) + \ CALLBACK_TYPECHECK(sect_callback, bool (*)( \ const char *, const char *, typeof(context), \ const char **)), \ section, (settings_callback_t *)callback, \ (settings_section_callback_t *)sect_callback, context, error_r) #define settings_read_nosection(path, callback, context, error_r) \ settings_read_i(path + \ CALLBACK_TYPECHECK(callback, const char *(*)( \ const char *, const char *, typeof(context))), \ NULL, (settings_callback_t *)callback, NULL, context, error_r) #endif dovecot-2.2.33.2/src/lib-settings/settings.c0000644000175000017500000002143213165463624015540 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "strescape.h" #include "settings.h" #include #include #ifdef HAVE_GLOB_H # include #endif #ifndef GLOB_BRACE # define GLOB_BRACE 0 #endif #define SECTION_ERRORMSG "%s (section changed in %s at line %d)" struct input_stack { struct input_stack *prev; struct istream *input; const char *path; unsigned int linenum; }; settings_section_callback_t *null_settings_section_callback = NULL; static const char *get_bool(const char *value, bool *result) { if (strcasecmp(value, "yes") == 0) *result = TRUE; else if (strcasecmp(value, "no") == 0) *result = FALSE; else return t_strconcat("Invalid boolean: ", value, NULL); return NULL; } static const char *get_uint(const char *value, unsigned int *result) { int num; if (sscanf(value, "%i", &num) != 1 || num < 0) return t_strconcat("Invalid number: ", value, NULL); *result = num; return NULL; } const char * parse_setting_from_defs(pool_t pool, const struct setting_def *defs, void *base, const char *key, const char *value) { const struct setting_def *def; for (def = defs; def->name != NULL; def++) { if (strcmp(def->name, key) == 0) { void *ptr = STRUCT_MEMBER_P(base, def->offset); switch (def->type) { case SET_STR: *((char **)ptr) = p_strdup(pool, value); return NULL; case SET_INT: /* use %i so we can handle eg. 0600 as octal value with umasks */ return get_uint(value, (unsigned int *) ptr); case SET_BOOL: return get_bool(value, (bool *) ptr); } } } return t_strconcat("Unknown setting: ", key, NULL); } static const char * fix_relative_path(const char *path, struct input_stack *input) { const char *p; if (*path == '/') return path; p = strrchr(input->path, '/'); if (p == NULL) return path; return t_strconcat(t_strdup_until(input->path, p+1), path, NULL); } static int settings_add_include(const char *path, struct input_stack **inputp, bool ignore_errors, const char **error_r) { struct input_stack *tmp, *new_input; int fd; for (tmp = *inputp; tmp != NULL; tmp = tmp->prev) { if (strcmp(tmp->path, path) == 0) break; } if (tmp != NULL) { *error_r = t_strdup_printf("Recursive include file: %s", path); return -1; } if ((fd = open(path, O_RDONLY)) == -1) { if (ignore_errors) return 0; *error_r = t_strdup_printf("Couldn't open include file %s: %m", path); return -1; } new_input = t_new(struct input_stack, 1); new_input->prev = *inputp; new_input->path = t_strdup(path); new_input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(new_input->input, TRUE); *inputp = new_input; return 0; } static int settings_include(const char *pattern, struct input_stack **inputp, bool ignore_errors, const char **error_r) { #ifdef HAVE_GLOB glob_t globbers; unsigned int i; switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) { case 0: break; case GLOB_NOSPACE: *error_r = "glob() failed: Not enough memory"; return -1; case GLOB_ABORTED: *error_r = "glob() failed: Read error"; return -1; case GLOB_NOMATCH: if (ignore_errors) return 0; *error_r = "No matches"; return -1; default: *error_r = "glob() failed: Unknown error"; return -1; } /* iterate throuth the different files matching the globbing */ for (i = 0; i < globbers.gl_pathc; i++) { if (settings_add_include(globbers.gl_pathv[i], inputp, ignore_errors, error_r) < 0) return -1; } globfree(&globbers); return 0; #else return settings_add_include(pattern, inputp, ignore_errors, error_r); #endif } #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') bool settings_read_i(const char *path, const char *section, settings_callback_t *callback, settings_section_callback_t *sect_callback, void *context, const char **error_r) { /* pretty horrible code, but v2.0 will have this rewritten anyway.. */ struct input_stack root, *input; const char *errormsg, *next_section, *name, *last_section_path = NULL; char *line, *key, *p, quote; string_t *full_line; size_t len; int fd, last_section_line = 0, skip, sections, root_section; fd = open(path, O_RDONLY); if (fd < 0) { *error_r = t_strdup_printf( "Can't open configuration file %s: %m", path); return FALSE; } if (section == NULL) { skip = 0; next_section = NULL; } else { skip = 1; next_section = t_strcut(section, '/'); } i_zero(&root); root.path = path; input = &root; full_line = t_str_new(512); sections = 0; root_section = 0; errormsg = NULL; input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(input->input, TRUE); prevfile: while ((line = i_stream_read_next_line(input->input)) != NULL) { input->linenum++; /* @UNSAFE: line is modified */ /* skip whitespace */ while (IS_WHITE(*line)) line++; /* ignore comments or empty lines */ if (*line == '#' || *line == '\0') continue; /* strip away comments. pretty kludgy way really.. */ for (p = line; *p != '\0'; p++) { if (*p == '\'' || *p == '"') { quote = *p; for (p++; *p != quote && *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') p++; } if (*p == '\0') break; } else if (*p == '#') { if (!IS_WHITE(p[-1])) { i_warning("Configuration file %s line %u: " "Ambiguous '#' character in line, treating it as comment. " "Add a space before it to remove this warning.", input->path, input->linenum); } *p = '\0'; break; } } /* remove whitespace from end of line */ len = strlen(line); while (IS_WHITE(line[len-1])) len--; line[len] = '\0'; if (len > 0 && line[len-1] == '\\') { /* continues in next line */ len--; while (IS_WHITE(line[len-1])) len--; str_append_n(full_line, line, len); str_append_c(full_line, ' '); continue; } if (str_len(full_line) > 0) { str_append(full_line, line); line = str_c_modifiable(full_line); } /* a) key = value b) section_type [section_name] { c) } */ key = line; while (!IS_WHITE(*line) && *line != '\0' && *line != '=') line++; if (IS_WHITE(*line)) { *line++ = '\0'; while (IS_WHITE(*line)) line++; } if (strcmp(key, "!include_try") == 0 || strcmp(key, "!include") == 0) { if (settings_include(fix_relative_path(line, input), &input, strcmp(key, "!include_try") == 0, &errormsg) == 0) goto prevfile; } else if (*line == '=') { /* a) */ *line++ = '\0'; while (IS_WHITE(*line)) line++; len = strlen(line); if (len > 0 && ((*line == '"' && line[len-1] == '"') || (*line == '\'' && line[len-1] == '\''))) { line[len-1] = '\0'; line = str_unescape(line+1); } errormsg = skip ? NULL : callback(key, line, context); } else if (strcmp(key, "}") != 0 || *line != '\0') { /* b) + errors */ line[-1] = '\0'; if (*line == '{') name = ""; else { name = line; while (!IS_WHITE(*line) && *line != '\0') line++; if (*line != '\0') { *line++ = '\0'; while (IS_WHITE(*line)) line++; } } if (*line != '{') errormsg = "Expecting '='"; else { sections++; if (next_section != NULL && strcmp(next_section, name) == 0) { section += strlen(next_section); if (*section == '\0') { skip = 0; next_section = NULL; root_section = sections; } else { i_assert(*section == '/'); section++; next_section = t_strcut(section, '/'); } } if (skip > 0) skip++; else { skip = sect_callback == NULL ? 1 : !sect_callback(key, name, context, &errormsg); if (errormsg != NULL && last_section_line != 0) { errormsg = t_strdup_printf( SECTION_ERRORMSG, errormsg, last_section_path, last_section_line); } } last_section_path = input->path; last_section_line = input->linenum; } } else { /* c) */ if (sections == 0) errormsg = "Unexpected '}'"; else { if (skip > 0) skip--; else { i_assert(sect_callback != NULL); sect_callback(NULL, NULL, context, &errormsg); if (root_section == sections && errormsg == NULL) { /* we found the section, now quit */ break; } } last_section_path = input->path; last_section_line = input->linenum; sections--; } } if (errormsg != NULL) { *error_r = t_strdup_printf( "Error in configuration file %s line %d: %s", input->path, input->linenum, errormsg); break; } str_truncate(full_line, 0); } i_stream_destroy(&input->input); input = input->prev; if (line == NULL && input != NULL) goto prevfile; return errormsg == NULL; } dovecot-2.2.33.2/src/lib-settings/settings-parser.c0000644000175000017500000015626513165463624017047 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "net.h" #include "istream.h" #include "env-util.h" #include "execv-const.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "settings-parser.h" #include #include #include #include #include #include #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') struct setting_link { struct setting_link *parent; const struct setting_parser_info *info; const char *full_key; /* Points to array inside parent->set_struct. SET_DEFLIST : array of set_structs SET_STRLIST : array of const_strings */ ARRAY_TYPE(void_array) *array; /* Pointer to structure containing the values */ void *set_struct; /* Pointer to structure containing non-zero values for settings that have been changed. */ void *change_struct; /* SET_DEFLIST: array of change_structs */ ARRAY_TYPE(void_array) *change_array; }; struct setting_parser_context { pool_t set_pool, parser_pool; enum settings_parser_flags flags; bool str_vars_are_expanded; struct setting_link *roots; unsigned int root_count; HASH_TABLE(char *, struct setting_link *) links; unsigned int linenum; const char *error; const struct setting_parser_info *prev_info; }; static const struct setting_parser_info strlist_info = { .module_name = NULL, .defines = NULL, .defaults = NULL, .type_offset = (size_t)-1, .struct_size = 0, .parent_offset = (size_t)-1 }; HASH_TABLE_DEFINE_TYPE(setting_link, struct setting_link *, struct setting_link *); static void setting_parser_copy_defaults(struct setting_parser_context *ctx, const struct setting_parser_info *info, struct setting_link *link); static int settings_apply(struct setting_link *dest_link, const struct setting_link *src_link, pool_t pool, const char **conflict_key_r); struct setting_parser_context * settings_parser_init(pool_t set_pool, const struct setting_parser_info *root, enum settings_parser_flags flags) { return settings_parser_init_list(set_pool, &root, 1, flags); } static void copy_unique_defaults(struct setting_parser_context *ctx, const struct setting_define *def, struct setting_link *link) { ARRAY_TYPE(void_array) *arr = STRUCT_MEMBER_P(link->set_struct, def->offset); ARRAY_TYPE(void_array) *carr = NULL; struct setting_link *new_link; struct setting_parser_info info; const char *const *keyp, *key, *prefix; void *const *children; void *new_set, *new_changes = NULL; char *full_key; unsigned int i, count; if (!array_is_created(arr)) return; children = array_get(arr, &count); if (link->change_struct != NULL) { carr = STRUCT_MEMBER_P(link->change_struct, def->offset); i_assert(!array_is_created(carr)); p_array_init(carr, ctx->set_pool, count + 4); } p_array_init(arr, ctx->set_pool, count + 4); i_zero(&info); info = *def->list_info; for (i = 0; i < count; i++) T_BEGIN { new_set = p_malloc(ctx->set_pool, info.struct_size); array_append(arr, &new_set, 1); if (link->change_struct != NULL) { new_changes = p_malloc(ctx->set_pool, info.struct_size); array_append(carr, &new_changes, 1); } keyp = CONST_PTR_OFFSET(children[i], info.type_offset); key = settings_section_escape(*keyp); new_link = p_new(ctx->set_pool, struct setting_link, 1); prefix = link->full_key == NULL ? t_strconcat(def->key, SETTINGS_SEPARATOR_S, NULL) : t_strconcat(link->full_key, SETTINGS_SEPARATOR_S, def->key, SETTINGS_SEPARATOR_S,NULL); full_key = p_strconcat(ctx->set_pool, prefix, key, NULL); new_link->full_key = full_key; new_link->parent = link; new_link->info = def->list_info; new_link->array = arr; new_link->change_array = carr; new_link->set_struct = new_set; new_link->change_struct = new_changes; i_assert(hash_table_lookup(ctx->links, full_key) == NULL); hash_table_insert(ctx->links, full_key, new_link); info.defaults = children[i]; setting_parser_copy_defaults(ctx, &info, new_link); } T_END; } static void setting_parser_copy_defaults(struct setting_parser_context *ctx, const struct setting_parser_info *info, struct setting_link *link) { const struct setting_define *def; const char *p, **strp; if (info->defaults == NULL) return; memcpy(link->set_struct, info->defaults, info->struct_size); for (def = info->defines; def->key != NULL; def++) { switch (def->type) { case SET_ENUM: { /* fix enums by dropping everything after the first ':' */ strp = STRUCT_MEMBER_P(link->set_struct, def->offset); p = strchr(*strp, ':'); if (p != NULL) *strp = p_strdup_until(ctx->set_pool, *strp, p); break; } case SET_STR_VARS: { /* insert the unexpanded-character */ strp = STRUCT_MEMBER_P(link->set_struct, def->offset); if (*strp != NULL) { *strp = p_strconcat(ctx->set_pool, SETTING_STRVAR_UNEXPANDED, *strp, NULL); } break; } case SET_DEFLIST_UNIQUE: copy_unique_defaults(ctx, def, link); break; default: break; } } } struct setting_parser_context * settings_parser_init_list(pool_t set_pool, const struct setting_parser_info *const *roots, unsigned int count, enum settings_parser_flags flags) { struct setting_parser_context *ctx; unsigned int i; pool_t parser_pool; i_assert(count > 0); parser_pool = pool_alloconly_create(MEMPOOL_GROWING"settings parser", 1024); ctx = p_new(parser_pool, struct setting_parser_context, 1); ctx->set_pool = set_pool; ctx->parser_pool = parser_pool; ctx->flags = flags; /* use case-insensitive comparisons. this is mainly because settings may go through environment variables where their keys get uppercased. of course the alternative would be to not uppercase environment. probably doesn't make much difference which way is chosen. */ hash_table_create(&ctx->links, ctx->parser_pool, 0, strcase_hash, strcasecmp); ctx->root_count = count; ctx->roots = p_new(ctx->parser_pool, struct setting_link, count); for (i = 0; i < count; i++) { ctx->roots[i].info = roots[i]; if (roots[i]->struct_size == 0) continue; ctx->roots[i].set_struct = p_malloc(ctx->set_pool, roots[i]->struct_size); if ((flags & SETTINGS_PARSER_FLAG_TRACK_CHANGES) != 0) { ctx->roots[i].change_struct = p_malloc(ctx->set_pool, roots[i]->struct_size); } setting_parser_copy_defaults(ctx, roots[i], &ctx->roots[i]); } pool_ref(ctx->set_pool); return ctx; } void settings_parser_deinit(struct setting_parser_context **_ctx) { struct setting_parser_context *ctx = *_ctx; *_ctx = NULL; hash_table_destroy(&ctx->links); pool_unref(&ctx->set_pool); pool_unref(&ctx->parser_pool); } void *settings_parser_get(struct setting_parser_context *ctx) { i_assert(ctx->root_count == 1); return ctx->roots[0].set_struct; } void **settings_parser_get_list(const struct setting_parser_context *ctx) { unsigned int i; void **sets; sets = t_new(void *, ctx->root_count + 1); for (i = 0; i < ctx->root_count; i++) sets[i] = ctx->roots[i].set_struct; return sets; } void *settings_parser_get_changes(struct setting_parser_context *ctx) { i_assert(ctx->root_count == 1); return ctx->roots[0].change_struct; } const struct setting_parser_info *const * settings_parser_get_roots(const struct setting_parser_context *ctx) { const struct setting_parser_info **infos; unsigned int i; infos = t_new(const struct setting_parser_info *, ctx->root_count + 1); for (i = 0; i < ctx->root_count; i++) infos[i] = ctx->roots[i].info; return infos; } const char *settings_parser_get_error(struct setting_parser_context *ctx) { return ctx->error; } static const struct setting_define * setting_define_find(const struct setting_parser_info *info, const char *key) { const struct setting_define *list; for (list = info->defines; list->key != NULL; list++) { if (strcmp(list->key, key) == 0) return list; } return NULL; } static int get_bool(struct setting_parser_context *ctx, const char *value, bool *result_r) { /* FIXME: eventually we'd want to support only yes/no */ if (strcasecmp(value, "yes") == 0 || strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0) *result_r = TRUE; else if (strcasecmp(value, "no") == 0) *result_r = FALSE; else { ctx->error = p_strdup_printf(ctx->parser_pool, "Invalid boolean value: %s (use yes or no)", value); return -1; } return 0; } static int get_uint(struct setting_parser_context *ctx, const char *value, unsigned int *result_r) { if (str_to_uint(value, result_r) < 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "Invalid number %s: %s", value, str_num_error(value)); return -1; } return 0; } static int get_octal(struct setting_parser_context *ctx, const char *value, unsigned int *result_r) { unsigned long long octal; if (*value != '0') return get_uint(ctx, value, result_r); if (str_to_ullong_oct(value, &octal) < 0) { ctx->error = p_strconcat(ctx->parser_pool, "Invalid number: ", value, NULL); return -1; } *result_r = (unsigned int)octal; return 0; } static int settings_get_time_full(const char *str, unsigned int *interval_r, bool milliseconds, const char **error_r) { uintmax_t num, multiply = milliseconds ? 1000 : 1; const char *p; if (str_parse_uintmax(str, &num, &p) < 0) { *error_r = t_strconcat("Invalid time interval: ", str, NULL); return -1; } while (*p == ' ') p++; switch (i_toupper(*p)) { case 'S': multiply *= 1; if (strncasecmp(p, "secs", strlen(p)) == 0 || strncasecmp(p, "seconds", strlen(p)) == 0) p = ""; break; case 'M': multiply *= 60; if (strncasecmp(p, "mins", strlen(p)) == 0 || strncasecmp(p, "minutes", strlen(p)) == 0) p = ""; else if (strncasecmp(p, "msecs", strlen(p)) == 0 || strncasecmp(p, "mseconds", strlen(p)) == 0 || strncasecmp(p, "millisecs", strlen(p)) == 0 || strncasecmp(p, "milliseconds", strlen(p)) == 0) { if (milliseconds || (num % 1000) == 0) { if (!milliseconds) { /* allow ms also for seconds, as long as it's divisible by seconds */ num /= 1000; } multiply = 1; p = ""; break; } *error_r = t_strdup_printf( "Milliseconds not supported for this setting: %s", str); return -1; } break; case 'H': multiply *= 60*60; if (strncasecmp(p, "hours", strlen(p)) == 0) p = ""; break; case 'D': multiply *= 60*60*24; if (strncasecmp(p, "days", strlen(p)) == 0) p = ""; break; case 'W': multiply *= 60*60*24*7; if (strncasecmp(p, "weeks", strlen(p)) == 0) p = ""; break; } if (*p != '\0') { *error_r = t_strconcat("Invalid time interval: ", str, NULL); return -1; } if (num > UINT_MAX / multiply) { *error_r = t_strconcat("Time interval is too large: ", str, NULL); return -1; } *interval_r = num * multiply; return 0; } int settings_get_time(const char *str, unsigned int *secs_r, const char **error_r) { return settings_get_time_full(str, secs_r, FALSE, error_r); } int settings_get_time_msecs(const char *str, unsigned int *msecs_r, const char **error_r) { return settings_get_time_full(str, msecs_r, TRUE, error_r); } int settings_get_size(const char *str, uoff_t *bytes_r, const char **error_r) { uintmax_t num, multiply = 1; const char *p; if (str_parse_uintmax(str, &num, &p) < 0) { *error_r = t_strconcat("Invalid size: ", str, NULL); return -1; } while (*p == ' ') p++; switch (i_toupper(*p)) { case 'B': multiply = 1; p += 1; break; case 'K': multiply = 1024; p += 1; break; case 'M': multiply = 1024*1024; p += 1; break; case 'G': multiply = 1024*1024*1024; p += 1; break; case 'T': multiply = 1024ULL*1024*1024*1024; p += 1; break; } if (multiply > 1) { /* Allow: k, ki, kiB */ if (i_toupper(*p) == 'I') p++; if (i_toupper(*p) == 'B') p++; } if (*p != '\0') { *error_r = t_strconcat("Invalid size: ", str, NULL); return -1; } if (num > ((uoff_t)-1) / multiply) { *error_r = t_strconcat("Size is too large: ", str, NULL); return -1; } *bytes_r = num * multiply; return 0; } static int get_enum(struct setting_parser_context *ctx, const char *value, char **result_r, const char *allowed_values) { const char *p; while (allowed_values != NULL) { p = strchr(allowed_values, ':'); if (p == NULL) { if (strcmp(allowed_values, value) == 0) break; ctx->error = p_strconcat(ctx->parser_pool, "Invalid value: ", value, NULL); return -1; } if (strncmp(allowed_values, value, p - allowed_values) == 0 && value[p - allowed_values] == '\0') break; allowed_values = p + 1; } *result_r = p_strdup(ctx->set_pool, value); return 0; } static void setting_link_init_set_struct(struct setting_parser_context *ctx, struct setting_link *link) { void *ptr; link->set_struct = p_malloc(ctx->set_pool, link->info->struct_size); if ((ctx->flags & SETTINGS_PARSER_FLAG_TRACK_CHANGES) != 0) { link->change_struct = p_malloc(ctx->set_pool, link->info->struct_size); array_append(link->change_array, &link->change_struct, 1); } setting_parser_copy_defaults(ctx, link->info, link); array_append(link->array, &link->set_struct, 1); if (link->info->parent_offset != (size_t)-1 && link->parent != NULL) { ptr = STRUCT_MEMBER_P(link->set_struct, link->info->parent_offset); *((void **)ptr) = link->parent->set_struct; } } static int ATTR_NULL(2) setting_link_add(struct setting_parser_context *ctx, const struct setting_define *def, const struct setting_link *link_copy, char *key) { struct setting_link *link; link = hash_table_lookup(ctx->links, key); if (link != NULL) { if (link->parent == link_copy->parent && link->info == link_copy->info && (def == NULL || def->type == SET_DEFLIST_UNIQUE)) return 0; ctx->error = p_strconcat(ctx->parser_pool, key, " already exists", NULL); return -1; } link = p_new(ctx->parser_pool, struct setting_link, 1); *link = *link_copy; link->full_key = key; i_assert(hash_table_lookup(ctx->links, key) == NULL); hash_table_insert(ctx->links, key, link); if (link->info->struct_size != 0) setting_link_init_set_struct(ctx, link); return 0; } static int ATTR_NULL(3, 8) get_deflist(struct setting_parser_context *ctx, struct setting_link *parent, const struct setting_define *def, const struct setting_parser_info *info, const char *key, const char *value, ARRAY_TYPE(void_array) *result, ARRAY_TYPE(void_array) *change_result) { struct setting_link new_link; const char *const *list; char *full_key; i_assert(info->defines != NULL || info == &strlist_info); if (!array_is_created(result)) p_array_init(result, ctx->set_pool, 5); if (change_result != NULL && !array_is_created(change_result)) p_array_init(change_result, ctx->set_pool, 5); i_zero(&new_link); new_link.parent = parent; new_link.info = info; new_link.array = result; new_link.change_array = change_result; if (info == &strlist_info) { /* there are no sections below strlist, so allow referencing it without the key (e.g. plugin/foo instead of plugin/0/foo) */ full_key = p_strdup(ctx->parser_pool, key); if (setting_link_add(ctx, def, &new_link, full_key) < 0) return -1; } list = t_strsplit(value, ",\t "); for (; *list != NULL; list++) { if (**list == '\0') continue; full_key = p_strconcat(ctx->parser_pool, key, SETTINGS_SEPARATOR_S, *list, NULL); if (setting_link_add(ctx, def, &new_link, full_key) < 0) return -1; } return 0; } static int get_in_port_zero(struct setting_parser_context *ctx, const char *value, in_port_t *result_r) { if (net_str2port_zero(value, result_r) < 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "Invalid port number %s", value); return -1; } return 0; } static int settings_parse(struct setting_parser_context *ctx, struct setting_link *link, const struct setting_define *def, const char *key, const char *value) { void *ptr, *change_ptr; const void *ptr2; const char *error; while (def->type == SET_ALIAS) { i_assert(def != link->info->defines); def--; } ctx->prev_info = link->info; if (link->set_struct == NULL) setting_link_init_set_struct(ctx, link); change_ptr = link->change_struct == NULL ? NULL : STRUCT_MEMBER_P(link->change_struct, def->offset); ptr = STRUCT_MEMBER_P(link->set_struct, def->offset); switch (def->type) { case SET_BOOL: if (get_bool(ctx, value, (bool *)ptr) < 0) return -1; break; case SET_UINT: if (get_uint(ctx, value, (unsigned int *)ptr) < 0) return -1; break; case SET_UINT_OCT: if (get_octal(ctx, value, (unsigned int *)ptr) < 0) return -1; break; case SET_TIME: if (settings_get_time(value, (unsigned int *)ptr, &error) < 0) { ctx->error = p_strdup(ctx->parser_pool, error); return -1; } break; case SET_SIZE: if (settings_get_size(value, (uoff_t *)ptr, &error) < 0) { ctx->error = p_strdup(ctx->parser_pool, error); return -1; } break; case SET_IN_PORT: if (get_in_port_zero(ctx, value, (in_port_t *)ptr) < 0) return -1; break; case SET_STR: *((char **)ptr) = p_strdup(ctx->set_pool, value); break; case SET_STR_VARS: *((char **)ptr) = p_strconcat(ctx->set_pool, ctx->str_vars_are_expanded ? SETTING_STRVAR_EXPANDED : SETTING_STRVAR_UNEXPANDED, value, NULL); break; case SET_ENUM: /* get the available values from default string */ i_assert(link->info->defaults != NULL); ptr2 = CONST_STRUCT_MEMBER_P(link->info->defaults, def->offset); if (get_enum(ctx, value, (char **)ptr, *(const char *const *)ptr2) < 0) return -1; break; case SET_DEFLIST: case SET_DEFLIST_UNIQUE: ctx->prev_info = def->list_info; return get_deflist(ctx, link, def, def->list_info, key, value, (ARRAY_TYPE(void_array) *)ptr, (ARRAY_TYPE(void_array) *)change_ptr); case SET_STRLIST: { ctx->prev_info = &strlist_info; if (get_deflist(ctx, link, NULL, &strlist_info, key, value, (ARRAY_TYPE(void_array) *)ptr, NULL) < 0) return -1; break; } case SET_ALIAS: i_unreached(); break; } if (change_ptr != NULL) *((char *)change_ptr) = 1; return 0; } static bool settings_find_key_nth(struct setting_parser_context *ctx, const char *key, unsigned int *n, const struct setting_define **def_r, struct setting_link **link_r) { const struct setting_define *def; struct setting_link *link; const char *end, *parent_key; unsigned int i; /* try to find from roots */ for (i = *n; i < ctx->root_count; i++) { def = setting_define_find(ctx->roots[i].info, key); if (def != NULL) { *n = i + 1; *def_r = def; *link_r = &ctx->roots[i]; return TRUE; } } if (*n > ctx->root_count) return FALSE; *n += 1; /* try to find from links */ end = strrchr(key, SETTINGS_SEPARATOR); if (end == NULL) return FALSE; parent_key = t_strdup_until(key, end); link = hash_table_lookup(ctx->links, parent_key); if (link == NULL) { /* maybe this is the first strlist value */ unsigned int parent_n = 0; const struct setting_define *parent_def; struct setting_link *parent_link; if (!settings_find_key_nth(ctx, parent_key, &parent_n, &parent_def, &parent_link)) return FALSE; if (parent_def == NULL) { /* we'll get here with e.g. "plugin/a/b=val". not sure if we should ever do anything here.. */ if (parent_link->full_key == NULL || strcmp(parent_link->full_key, parent_key) != 0) return FALSE; } else { if (parent_def->type != SET_STRLIST) return FALSE; } /* setting parent_key=0 adds it to links list */ if (settings_parse_keyvalue(ctx, parent_key, "0") <= 0) return FALSE; link = hash_table_lookup(ctx->links, parent_key); i_assert(link != NULL); } *link_r = link; if (link->info == &strlist_info) { *def_r = NULL; return TRUE; } else { *def_r = setting_define_find(link->info, end + 1); return *def_r != NULL; } } static bool settings_find_key(struct setting_parser_context *ctx, const char *key, const struct setting_define **def_r, struct setting_link **link_r) { unsigned int n = 0; return settings_find_key_nth(ctx, key, &n, def_r, link_r); } static void settings_parse_strlist(struct setting_parser_context *ctx, struct setting_link *link, const char *key, const char *value) { void *const *items; void *vkey, *vvalue; unsigned int i, count; key = strrchr(key, SETTINGS_SEPARATOR) + 1; vvalue = p_strdup(ctx->set_pool, value); /* replace if it already exists */ items = array_get(link->array, &count); for (i = 0; i < count; i += 2) { if (strcmp(items[i], key) == 0) { array_idx_set(link->array, i + 1, &vvalue); return; } } vkey = p_strdup(ctx->set_pool, key); array_append(link->array, &vkey, 1); array_append(link->array, &vvalue, 1); } int settings_parse_keyvalue(struct setting_parser_context *ctx, const char *key, const char *value) { const struct setting_define *def; struct setting_link *link; unsigned int n = 0; ctx->error = NULL; ctx->prev_info = NULL; if (!settings_find_key_nth(ctx, key, &n, &def, &link)) { ctx->error = p_strconcat(ctx->parser_pool, "Unknown setting: ", key, NULL); return 0; } do { if (def == NULL) { i_assert(link->info == &strlist_info); settings_parse_strlist(ctx, link, key, value); return 1; } if (settings_parse(ctx, link, def, key, value) < 0) return -1; /* there may be more instances of the setting */ } while (settings_find_key_nth(ctx, key, &n, &def, &link)); return 1; } bool settings_parse_is_valid_key(struct setting_parser_context *ctx, const char *key) { const struct setting_define *def; struct setting_link *link; return settings_find_key(ctx, key, &def, &link); } const char *settings_parse_unalias(struct setting_parser_context *ctx, const char *key) { const struct setting_define *def; struct setting_link *link; if (!settings_find_key(ctx, key, &def, &link)) return NULL; if (def == NULL) { /* strlist */ i_assert(link->info == &strlist_info); return key; } while (def->type == SET_ALIAS) { i_assert(def != link->info->defines); def--; } return def->key; } const void * settings_parse_get_value(struct setting_parser_context *ctx, const char *key, enum setting_type *type_r) { const struct setting_define *def; struct setting_link *link; if (!settings_find_key(ctx, key, &def, &link)) return NULL; if (link->set_struct == NULL || def == NULL) return NULL; *type_r = def->type; return STRUCT_MEMBER_P(link->set_struct, def->offset); } bool settings_parse_is_changed(struct setting_parser_context *ctx, const char *key) { const struct setting_define *def; struct setting_link *link; const unsigned char *p; if (!settings_find_key(ctx, key, &def, &link)) return FALSE; if (link->change_struct == NULL || def == NULL) return FALSE; p = STRUCT_MEMBER_P(link->change_struct, def->offset); return *p; } int settings_parse_line(struct setting_parser_context *ctx, const char *line) { const char *key, *value; int ret; key = line; value = strchr(line, '='); if (value == NULL) { ctx->error = "Missing '='"; return -1; } if (key == value) { ctx->error = "Missing key name ('=' at the beginning of line)"; return -1; } T_BEGIN { key = t_strdup_until(key, value); ret = settings_parse_keyvalue(ctx, key, value + 1); } T_END; return ret; } const struct setting_parser_info * settings_parse_get_prev_info(struct setting_parser_context *ctx) { return ctx->prev_info; } static const char *settings_translate_lf(const char *value) { char *dest, *p; if (strchr(value, SETTING_STREAM_LF_CHAR[0]) == NULL) return value; dest = t_strdup_noconst(value); for (p = dest; *p != '\0'; p++) { if (*p == SETTING_STREAM_LF_CHAR[0]) *p = '\n'; } return dest; } int settings_parse_stream(struct setting_parser_context *ctx, struct istream *input) { bool ignore_unknown_keys = (ctx->flags & SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS) != 0; const char *line; int ret; while ((line = i_stream_next_line(input)) != NULL) { if (*line == '\0') { /* empty line finishes it */ return 0; } ctx->linenum++; if (ctx->linenum == 1 && strncmp(line, "ERROR ", 6) == 0) { ctx->error = p_strdup(ctx->parser_pool, line + 6); return -1; } T_BEGIN { line = settings_translate_lf(line); ret = settings_parse_line(ctx, line); } T_END; if (ret < 0 || (ret == 0 && !ignore_unknown_keys)) { ctx->error = p_strdup_printf(ctx->parser_pool, "Line %u: %s", ctx->linenum, ctx->error); return -1; } } return 1; } int settings_parse_stream_read(struct setting_parser_context *ctx, struct istream *input) { int ret; do { if ((ret = settings_parse_stream(ctx, input)) < 0) return -1; if (ret == 0) { /* empty line read */ return 0; } } while ((ret = i_stream_read(input)) > 0); switch (ret) { case -1: if (ctx->error != NULL) break; if (input->stream_errno != 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); } else if (input->v_offset == 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "read(%s) disconnected before receiving any data", i_stream_get_name(input)); } else { ctx->error = p_strdup_printf(ctx->parser_pool, "read(%s) disconnected before receiving " "end-of-settings line", i_stream_get_name(input)); } break; case -2: ctx->error = p_strdup_printf(ctx->parser_pool, "Line %u: line too long", ctx->linenum); break; case 0: /* blocks */ return 1; default: i_unreached(); } return -1; } int settings_parse_file(struct setting_parser_context *ctx, const char *path, size_t max_line_length) { struct istream *input; int fd, ret; fd = open(path, O_RDONLY); if (fd < 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "open(%s) failed: %m", path); return -1; } input = i_stream_create_fd_autoclose(&fd, max_line_length); i_stream_set_name(input, path); ret = settings_parse_stream_read(ctx, input); i_stream_unref(&input); return ret; } static int environ_cmp(char *const *s1, char *const *s2) { return -strcmp(*s1, *s2); } int settings_parse_environ(struct setting_parser_context *ctx) { char **environ = *env_get_environ_p(); ARRAY_TYPE(string) sorted_envs_arr; const char *key, *value; char *const *sorted_envs; unsigned int i, count; int ret = 0; if (environ == NULL) return 0; /* sort the settings first. this is necessary for putenv() implementations (e.g. valgrind) which change the order of strings in environ[] */ i_array_init(&sorted_envs_arr, 128); for (i = 0; environ[i] != NULL; i++) array_append(&sorted_envs_arr, &environ[i], 1); array_sort(&sorted_envs_arr, environ_cmp); sorted_envs = array_get(&sorted_envs_arr, &count); for (i = 0; i < count && ret == 0; i++) { value = strchr(sorted_envs[i], '='); if (value != NULL) T_BEGIN { key = t_strdup_until(sorted_envs[i], value++); key = t_str_lcase(key); if (settings_parse_keyvalue(ctx, key, value) < 0) { ctx->error = p_strdup_printf(ctx->parser_pool, "Invalid setting %s: %s", key, ctx->error); ret = -1; } } T_END; } array_free(&sorted_envs_arr); return ret; } int settings_parse_exec(struct setting_parser_context *ctx, const char *bin_path, const char *config_path, const char *service) { struct istream *input; pid_t pid; int ret, fd[2], status; if (pipe(fd) < 0) { i_error("pipe() failed: %m"); return -1; } pid = fork(); if (pid == (pid_t)-1) { i_error("fork() failed: %m"); i_close_fd(&fd[0]); i_close_fd(&fd[1]); return -1; } if (pid == 0) { /* child */ static const char *argv[] = { NULL, "-c", NULL, "-p", NULL, NULL }; argv[0] = bin_path; argv[2] = config_path; argv[4] = service; i_close_fd(&fd[0]); if (dup2(fd[1], STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); execv_const(argv[0], argv); } i_close_fd(&fd[1]); input = i_stream_create_fd_autoclose(&fd[0], (size_t)-1); i_stream_set_name(input, bin_path); ret = settings_parse_stream_read(ctx, input); i_stream_destroy(&input); if (waitpid(pid, &status, 0) < 0) { i_error("waitpid() failed: %m"); ret = -1; } else if (status != 0) { i_error("%s returned failure: %d", bin_path, status); ret = -1; } return ret; } static bool settings_check_dynamic(const struct setting_parser_info *info, pool_t pool, void *set, const char **error_r) { unsigned int i; if (info->dynamic_parsers == NULL) return TRUE; for (i = 0; info->dynamic_parsers[i].name != NULL; i++) { struct dynamic_settings_parser *dyn = &info->dynamic_parsers[i]; if (!settings_check(dyn->info, pool, PTR_OFFSET(set, dyn->struct_offset), error_r)) return FALSE; } return TRUE; } bool settings_check(const struct setting_parser_info *info, pool_t pool, void *set, const char **error_r) { const struct setting_define *def; const ARRAY_TYPE(void_array) *val; void *const *children; unsigned int i, count; if (info->check_func != NULL) { if (!info->check_func(set, pool, error_r)) return FALSE; } for (def = info->defines; def->key != NULL; def++) { if (!SETTING_TYPE_IS_DEFLIST(def->type)) continue; val = CONST_PTR_OFFSET(set, def->offset); if (!array_is_created(val)) continue; children = array_get(val, &count); for (i = 0; i < count; i++) { if (!settings_check(def->list_info, pool, children[i], error_r)) return FALSE; } } return settings_check_dynamic(info, pool, set, error_r); } bool settings_parser_check(struct setting_parser_context *ctx, pool_t pool, const char **error_r) { unsigned int i; for (i = 0; i < ctx->root_count; i++) { if (!settings_check(ctx->roots[i].info, pool, ctx->roots[i].set_struct, error_r)) return FALSE; } return TRUE; } void settings_parse_set_expanded(struct setting_parser_context *ctx, bool is_expanded) { ctx->str_vars_are_expanded = is_expanded; } void settings_parse_set_key_expandeded(struct setting_parser_context *ctx, pool_t pool, const char *key) { const struct setting_define *def; struct setting_link *link; const char **val; if (!settings_find_key(ctx, key, &def, &link)) return; if (def == NULL) { /* parent is strlist, no expansion needed */ i_assert(link->info == &strlist_info); return; } val = PTR_OFFSET(link->set_struct, def->offset); if (def->type == SET_STR_VARS && *val != NULL) { i_assert(**val == SETTING_STRVAR_UNEXPANDED[0] || **val == SETTING_STRVAR_EXPANDED[0]); *val = p_strconcat(pool, SETTING_STRVAR_EXPANDED, *val + 1, NULL); } } void settings_parse_set_keys_expandeded(struct setting_parser_context *ctx, pool_t pool, const char *const *keys) { for (; *keys != NULL; keys++) settings_parse_set_key_expandeded(ctx, pool, *keys); } static void ATTR_NULL(3, 4, 5) settings_var_expand_info(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table, const struct var_expand_func_table *func_table, void *func_context, string_t *str) { const struct setting_define *def; void *value, *const *children; unsigned int i, count; for (def = info->defines; def->key != NULL; def++) { value = PTR_OFFSET(set, def->offset); switch (def->type) { case SET_BOOL: case SET_UINT: case SET_UINT_OCT: case SET_TIME: case SET_SIZE: case SET_IN_PORT: case SET_STR: case SET_ENUM: case SET_STRLIST: case SET_ALIAS: break; case SET_STR_VARS: { const char **val = value; if (*val == NULL) break; if (table == NULL) { i_assert(**val == SETTING_STRVAR_EXPANDED[0] || **val == SETTING_STRVAR_UNEXPANDED[0]); *val += 1; } else if (**val == SETTING_STRVAR_UNEXPANDED[0]) { str_truncate(str, 0); var_expand_with_funcs(str, *val + 1, table, func_table, func_context); *val = p_strdup(pool, str_c(str)); } else { i_assert(**val == SETTING_STRVAR_EXPANDED[0]); *val += 1; } break; } case SET_DEFLIST: case SET_DEFLIST_UNIQUE: { const ARRAY_TYPE(void_array) *val = value; if (!array_is_created(val)) break; children = array_get(val, &count); for (i = 0; i < count; i++) { settings_var_expand_info(def->list_info, children[i], pool, table, func_table, func_context, str); } break; } } } } void settings_var_expand(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table) { settings_var_expand_with_funcs(info, set, pool, table, NULL, NULL); } void settings_var_expand_with_funcs(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table, const struct var_expand_func_table *func_table, void *func_context) { string_t *str; T_BEGIN { str = t_str_new(256); settings_var_expand_info(info, set, pool, table, func_table, func_context, str); } T_END; } void settings_parse_var_skip(struct setting_parser_context *ctx) { unsigned int i; for (i = 0; i < ctx->root_count; i++) { settings_var_expand_info(ctx->roots[i].info, ctx->roots[i].set_struct, NULL, NULL, NULL, NULL, NULL); } } bool settings_vars_have_key(const struct setting_parser_info *info, void *set, char var_key, const char *long_var_key, const char **key_r, const char **value_r) { const struct setting_define *def; const void *value; void *const *children; unsigned int i, count; for (def = info->defines; def->key != NULL; def++) { value = CONST_PTR_OFFSET(set, def->offset); switch (def->type) { case SET_BOOL: case SET_UINT: case SET_UINT_OCT: case SET_TIME: case SET_SIZE: case SET_IN_PORT: case SET_STR: case SET_ENUM: case SET_STRLIST: case SET_ALIAS: break; case SET_STR_VARS: { const char *const *val = value; if (*val == NULL) break; if (**val == SETTING_STRVAR_UNEXPANDED[0]) { if (var_has_key(*val + 1, var_key, long_var_key)) { *key_r = def->key; *value_r = *val + 1; return TRUE; } } else { i_assert(**val == SETTING_STRVAR_EXPANDED[0]); } break; } case SET_DEFLIST: case SET_DEFLIST_UNIQUE: { const ARRAY_TYPE(void_array) *val = value; if (!array_is_created(val)) break; children = array_get(val, &count); for (i = 0; i < count; i++) { if (settings_vars_have_key(def->list_info, children[i], var_key, long_var_key, key_r, value_r)) return TRUE; } break; } } } return FALSE; } static void settings_set_parent(const struct setting_parser_info *info, void *child, void *parent) { void **ptr; if (info->parent_offset == (size_t)-1) return; ptr = PTR_OFFSET(child, info->parent_offset); *ptr = parent; } static bool setting_copy(enum setting_type type, const void *src, void *dest, pool_t pool, bool keep_values) { switch (type) { case SET_BOOL: { const bool *src_bool = src; bool *dest_bool = dest; *dest_bool = *src_bool; break; } case SET_UINT: case SET_UINT_OCT: case SET_TIME: { const unsigned int *src_uint = src; unsigned int *dest_uint = dest; *dest_uint = *src_uint; break; } case SET_SIZE: { const uoff_t *src_size = src; uoff_t *dest_size = dest; *dest_size = *src_size; break; } case SET_IN_PORT: { const in_port_t *src_size = src; in_port_t *dest_size = dest; *dest_size = *src_size; break; } case SET_STR_VARS: case SET_STR: case SET_ENUM: { const char *const *src_str = src; const char **dest_str = dest; if (keep_values) *dest_str = *src_str; else *dest_str = p_strdup(pool, *src_str); break; } case SET_DEFLIST: case SET_DEFLIST_UNIQUE: return FALSE; case SET_STRLIST: { const ARRAY_TYPE(const_string) *src_arr = src; ARRAY_TYPE(const_string) *dest_arr = dest; const char *const *strings, *const *dest_strings, *dup; unsigned int i, j, count, dest_count; if (!array_is_created(src_arr)) break; strings = array_get(src_arr, &count); i_assert(count % 2 == 0); if (!array_is_created(dest_arr)) p_array_init(dest_arr, pool, count); dest_count = array_count(dest_arr); i_assert(dest_count % 2 == 0); for (i = 0; i < count; i += 2) { if (dest_count > 0) { dest_strings = array_idx(dest_arr, 0); for (j = 0; j < dest_count; j += 2) { if (strcmp(strings[i], dest_strings[j]) == 0) break; } if (j < dest_count) continue; } dup = keep_values ? strings[i] : p_strdup(pool, strings[i]); array_append(dest_arr, &dup, 1); dup = keep_values ? strings[i+1] : p_strdup(pool, strings[i+1]); array_append(dest_arr, &dup, 1); } break; } case SET_ALIAS: break; } return TRUE; } static void *settings_dup_full(const struct setting_parser_info *info, const void *set, pool_t pool, bool keep_values) { const struct setting_define *def; const void *src; void *dest_set, *dest, *const *children; unsigned int i, count; if (info->struct_size == 0) return NULL; /* don't just copy everything from set to dest_set. it may contain some non-setting fields allocated from the original pool. */ dest_set = p_malloc(pool, info->struct_size); for (def = info->defines; def->key != NULL; def++) { src = CONST_PTR_OFFSET(set, def->offset); dest = PTR_OFFSET(dest_set, def->offset); if (!setting_copy(def->type, src, dest, pool, keep_values)) { const ARRAY_TYPE(void_array) *src_arr = src; ARRAY_TYPE(void_array) *dest_arr = dest; void *child_set; if (!array_is_created(src_arr)) continue; children = array_get(src_arr, &count); p_array_init(dest_arr, pool, count); for (i = 0; i < count; i++) { child_set = settings_dup_full(def->list_info, children[i], pool, keep_values); array_append(dest_arr, &child_set, 1); settings_set_parent(def->list_info, child_set, dest_set); } } } return dest_set; } void *settings_dup(const struct setting_parser_info *info, const void *set, pool_t pool) { return settings_dup_full(info, set, pool, FALSE); } void *settings_dup_with_pointers(const struct setting_parser_info *info, const void *set, pool_t pool) { return settings_dup_full(info, set, pool, TRUE); } static void * settings_changes_dup(const struct setting_parser_info *info, const void *change_set, pool_t pool) { const struct setting_define *def; const void *src; void *dest_set, *dest, *const *children; unsigned int i, count; if (change_set == NULL || info->struct_size == 0) return NULL; dest_set = p_malloc(pool, info->struct_size); for (def = info->defines; def->key != NULL; def++) { src = CONST_PTR_OFFSET(change_set, def->offset); dest = PTR_OFFSET(dest_set, def->offset); switch (def->type) { case SET_BOOL: case SET_UINT: case SET_UINT_OCT: case SET_TIME: case SET_SIZE: case SET_IN_PORT: case SET_STR_VARS: case SET_STR: case SET_ENUM: case SET_STRLIST: *((char *)dest) = *((const char *)src); break; case SET_DEFLIST: case SET_DEFLIST_UNIQUE: { const ARRAY_TYPE(void_array) *src_arr = src; ARRAY_TYPE(void_array) *dest_arr = dest; void *child_set; if (!array_is_created(src_arr)) break; children = array_get(src_arr, &count); p_array_init(dest_arr, pool, count); for (i = 0; i < count; i++) { child_set = settings_changes_dup(def->list_info, children[i], pool); array_append(dest_arr, &child_set, 1); } break; } case SET_ALIAS: break; } } return dest_set; } static void info_update_real(pool_t pool, struct setting_parser_info *parent, const struct dynamic_settings_parser *parsers) { /* @UNSAFE */ ARRAY(struct setting_define) defines; ARRAY_TYPE(dynamic_settings_parser) dynamic_parsers; struct dynamic_settings_parser new_parser; const struct setting_define *cur_defines; struct setting_define *new_defines, new_define; void *parent_defaults; unsigned int i, j; size_t offset, new_struct_size; t_array_init(&defines, 128); /* add existing defines */ for (j = 0; parent->defines[j].key != NULL; j++) array_append(&defines, &parent->defines[j], 1); new_struct_size = MEM_ALIGN(parent->struct_size); /* add new dynamic defines */ for (i = 0; parsers[i].name != NULL; i++) { i_assert(parsers[i].info->parent == parent); cur_defines = parsers[i].info->defines; for (j = 0; cur_defines[j].key != NULL; j++) { new_define = cur_defines[j]; new_define.offset += new_struct_size; array_append(&defines, &new_define, 1); } new_struct_size += MEM_ALIGN(parsers[i].info->struct_size); } new_defines = p_new(pool, struct setting_define, array_count(&defines) + 1); memcpy(new_defines, array_idx(&defines, 0), sizeof(*parent->defines) * array_count(&defines)); parent->defines = new_defines; /* update defaults */ parent_defaults = p_malloc(pool, new_struct_size); memcpy(parent_defaults, parent->defaults, parent->struct_size); offset = MEM_ALIGN(parent->struct_size); for (i = 0; parsers[i].name != NULL; i++) { memcpy(PTR_OFFSET(parent_defaults, offset), parsers[i].info->defaults, parsers[i].info->struct_size); offset += MEM_ALIGN(parsers[i].info->struct_size); } parent->defaults = parent_defaults; /* update dynamic parsers list */ t_array_init(&dynamic_parsers, 32); if (parent->dynamic_parsers != NULL) { for (i = 0; parent->dynamic_parsers[i].name != NULL; i++) { array_append(&dynamic_parsers, &parent->dynamic_parsers[i], 1); } } offset = MEM_ALIGN(parent->struct_size); for (i = 0; parsers[i].name != NULL; i++) { new_parser = parsers[i]; new_parser.name = p_strdup(pool, new_parser.name); new_parser.struct_offset = offset; array_append(&dynamic_parsers, &new_parser, 1); offset += MEM_ALIGN(parsers[i].info->struct_size); } parent->dynamic_parsers = p_new(pool, struct dynamic_settings_parser, array_count(&dynamic_parsers) + 1); memcpy(parent->dynamic_parsers, array_idx(&dynamic_parsers, 0), sizeof(*parent->dynamic_parsers) * array_count(&dynamic_parsers)); parent->struct_size = new_struct_size; } void settings_parser_info_update(pool_t pool, struct setting_parser_info *parent, const struct dynamic_settings_parser *parsers) { if (parsers[0].name != NULL) T_BEGIN { info_update_real(pool, parent, parsers); } T_END; } static void settings_parser_update_children_parent(struct setting_parser_info *parent, pool_t pool) { struct setting_define *new_defs; struct setting_parser_info *new_info; unsigned int i, count; for (count = 0; parent->defines[count].key != NULL; count++) ; new_defs = p_new(pool, struct setting_define, count + 1); memcpy(new_defs, parent->defines, sizeof(*new_defs) * count); parent->defines = new_defs; for (i = 0; i < count; i++) { if (new_defs[i].list_info == NULL || new_defs[i].list_info->parent == NULL) continue; new_info = p_new(pool, struct setting_parser_info, 1); *new_info = *new_defs[i].list_info; new_info->parent = parent; new_defs[i].list_info = new_info; } } void settings_parser_dyn_update(pool_t pool, const struct setting_parser_info *const **_roots, const struct dynamic_settings_parser *dyn_parsers) { const struct setting_parser_info *const *roots = *_roots; const struct setting_parser_info *old_parent, **new_roots; struct setting_parser_info *new_parent, *new_info; struct dynamic_settings_parser *new_dyn_parsers; unsigned int i, count; /* settings_parser_info_update() modifies the parent structure. since we may be using the same structure later, we want it to be in its original state, so we'll have to copy all structures. */ old_parent = dyn_parsers[0].info->parent; new_parent = p_new(pool, struct setting_parser_info, 1); *new_parent = *old_parent; settings_parser_update_children_parent(new_parent, pool); /* update root */ for (count = 0; roots[count] != NULL; count++) ; new_roots = p_new(pool, const struct setting_parser_info *, count + 1); for (i = 0; i < count; i++) { if (roots[i] == old_parent) new_roots[i] = new_parent; else new_roots[i] = roots[i]; } *_roots = new_roots; /* update parent in dyn_parsers */ for (count = 0; dyn_parsers[count].name != NULL; count++) ; new_dyn_parsers = p_new(pool, struct dynamic_settings_parser, count + 1); for (i = 0; i < count; i++) { new_dyn_parsers[i] = dyn_parsers[i]; new_info = p_new(pool, struct setting_parser_info, 1); *new_info = *dyn_parsers[i].info; new_info->parent = new_parent; new_dyn_parsers[i].info = new_info; } settings_parser_info_update(pool, new_parent, new_dyn_parsers); } const void *settings_find_dynamic(const struct setting_parser_info *info, const void *base_set, const char *name) { unsigned int i; if (info->dynamic_parsers == NULL) return NULL; for (i = 0; info->dynamic_parsers[i].name != NULL; i++) { if (strcmp(info->dynamic_parsers[i].name, name) == 0) { return CONST_PTR_OFFSET(base_set, info->dynamic_parsers[i].struct_offset); } } return NULL; } static struct setting_link * settings_link_get_new(struct setting_parser_context *new_ctx, HASH_TABLE_TYPE(setting_link) links, struct setting_link *old_link) { struct setting_link *new_link; void *const *old_sets, **new_sets; unsigned int i, count, count2; size_t diff; new_link = hash_table_lookup(links, old_link); if (new_link != NULL) return new_link; i_assert(old_link->parent != NULL); i_assert(old_link->array != NULL); new_link = p_new(new_ctx->parser_pool, struct setting_link, 1); new_link->info = old_link->info; new_link->parent = settings_link_get_new(new_ctx, links, old_link->parent); /* find the array from parent struct */ diff = (char *)old_link->array - (char *)old_link->parent->set_struct; i_assert(diff + sizeof(*old_link->array) <= old_link->parent->info->struct_size); new_link->array = PTR_OFFSET(new_link->parent->set_struct, diff); if (old_link->set_struct != NULL) { /* find our struct from array */ old_sets = array_get(old_link->array, &count); new_sets = array_get_modifiable(new_link->array, &count2); i_assert(count == count2); for (i = 0; i < count; i++) { if (old_sets[i] == old_link->set_struct) { new_link->set_struct = new_sets[i]; break; } } i_assert(i < count); } i_assert(hash_table_lookup(links, old_link) == NULL); hash_table_insert(links, old_link, new_link); return new_link; } struct setting_parser_context * settings_parser_dup(const struct setting_parser_context *old_ctx, pool_t new_pool) { struct setting_parser_context *new_ctx; struct hash_iterate_context *iter; HASH_TABLE_TYPE(setting_link) links; struct setting_link *new_link, *value; char *key; unsigned int i; pool_t parser_pool; bool keep_values; /* if source and destination pools are the same, there's no need to duplicate values */ keep_values = new_pool == old_ctx->set_pool; pool_ref(new_pool); parser_pool = pool_alloconly_create(MEMPOOL_GROWING"dup settings parser", 1024); new_ctx = p_new(parser_pool, struct setting_parser_context, 1); new_ctx->set_pool = new_pool; new_ctx->parser_pool = parser_pool; new_ctx->flags = old_ctx->flags; new_ctx->str_vars_are_expanded = old_ctx->str_vars_are_expanded; new_ctx->linenum = old_ctx->linenum; new_ctx->error = p_strdup(new_ctx->parser_pool, old_ctx->error); new_ctx->prev_info = old_ctx->prev_info; hash_table_create_direct(&links, new_ctx->parser_pool, 0); new_ctx->root_count = old_ctx->root_count; new_ctx->roots = p_new(new_ctx->parser_pool, struct setting_link, new_ctx->root_count); for (i = 0; i < new_ctx->root_count; i++) { i_assert(old_ctx->roots[i].parent == NULL); i_assert(old_ctx->roots[i].array == NULL); new_ctx->roots[i].info = old_ctx->roots[i].info; new_ctx->roots[i].set_struct = settings_dup_full(old_ctx->roots[i].info, old_ctx->roots[i].set_struct, new_ctx->set_pool, keep_values); new_ctx->roots[i].change_struct = settings_changes_dup(old_ctx->roots[i].info, old_ctx->roots[i].change_struct, new_ctx->set_pool); hash_table_insert(links, &old_ctx->roots[i], &new_ctx->roots[i]); } hash_table_create(&new_ctx->links, new_ctx->parser_pool, 0, strcase_hash, strcasecmp); iter = hash_table_iterate_init(old_ctx->links); while (hash_table_iterate(iter, old_ctx->links, &key, &value)) { new_link = settings_link_get_new(new_ctx, links, value); key = p_strdup(new_ctx->parser_pool, key); hash_table_insert(new_ctx->links, key, new_link); } hash_table_iterate_deinit(&iter); hash_table_destroy(&links); return new_ctx; } static void * settings_changes_init(const struct setting_parser_info *info, const void *change_set, pool_t pool) { const struct setting_define *def; const ARRAY_TYPE(void_array) *src_arr; ARRAY_TYPE(void_array) *dest_arr; void *dest_set, *set, *const *children; unsigned int i, count; if (info->struct_size == 0) return NULL; dest_set = p_malloc(pool, info->struct_size); for (def = info->defines; def->key != NULL; def++) { if (!SETTING_TYPE_IS_DEFLIST(def->type)) continue; src_arr = CONST_PTR_OFFSET(change_set, def->offset); dest_arr = PTR_OFFSET(dest_set, def->offset); if (array_is_created(src_arr)) { children = array_get(src_arr, &count); i_assert(!array_is_created(dest_arr)); p_array_init(dest_arr, pool, count); for (i = 0; i < count; i++) { set = settings_changes_init(def->list_info, children[i], pool); array_append(dest_arr, &set, 1); } } } return dest_set; } static void settings_copy_deflist(const struct setting_define *def, const struct setting_link *src_link, struct setting_link *dest_link, pool_t pool) { const ARRAY_TYPE(void_array) *src_arr; ARRAY_TYPE(void_array) *dest_arr; void *const *children, *child_set; unsigned int i, count; src_arr = CONST_PTR_OFFSET(src_link->set_struct, def->offset); dest_arr = PTR_OFFSET(dest_link->set_struct, def->offset); if (!array_is_created(src_arr)) return; children = array_get(src_arr, &count); if (!array_is_created(dest_arr)) p_array_init(dest_arr, pool, count); for (i = 0; i < count; i++) { child_set = settings_dup(def->list_info, children[i], pool); array_append(dest_arr, &child_set, 1); settings_set_parent(def->list_info, child_set, dest_link->set_struct); } /* copy changes */ dest_arr = PTR_OFFSET(dest_link->change_struct, def->offset); if (!array_is_created(dest_arr)) p_array_init(dest_arr, pool, count); for (i = 0; i < count; i++) { child_set = settings_changes_init(def->list_info, children[i], pool); array_append(dest_arr, &child_set, 1); } } static int settings_copy_deflist_unique(const struct setting_define *def, const struct setting_link *src_link, struct setting_link *dest_link, pool_t pool, const char **conflict_key_r) { struct setting_link child_dest_link, child_src_link; const ARRAY_TYPE(void_array) *src_arr, *src_carr; ARRAY_TYPE(void_array) *dest_arr, *dest_carr; void *const *src_children, *const *src_cchildren; void *const *dest_children, *const *dest_cchildren, *child_set; const char *const *src_namep, *const *dest_namep; unsigned int i, j, src_count, dest_count, ccount; unsigned int type_offset; i_assert(def->list_info->type_offset != (size_t)-1); src_arr = CONST_PTR_OFFSET(src_link->set_struct, def->offset); src_carr = CONST_PTR_OFFSET(src_link->change_struct, def->offset); dest_arr = PTR_OFFSET(dest_link->set_struct, def->offset); dest_carr = PTR_OFFSET(dest_link->change_struct, def->offset); if (!array_is_created(src_arr)) return 0; type_offset = def->list_info->type_offset; i_zero(&child_dest_link); i_zero(&child_src_link); child_dest_link.info = child_src_link.info = def->list_info; src_children = array_get(src_arr, &src_count); src_cchildren = array_get(src_carr, &ccount); i_assert(src_count == ccount); if (!array_is_created(dest_arr)) { p_array_init(dest_arr, pool, src_count); p_array_init(dest_carr, pool, src_count); } for (i = 0; i < src_count; i++) { src_namep = CONST_PTR_OFFSET(src_children[i], type_offset); dest_children = array_get(dest_arr, &dest_count); dest_cchildren = array_get(dest_carr, &ccount); i_assert(dest_count == ccount); for (j = 0; j < dest_count; j++) { dest_namep = CONST_PTR_OFFSET(dest_children[j], type_offset); if (strcmp(*src_namep, *dest_namep) == 0) break; } if (j < dest_count && **src_namep != '\0') { /* merge */ child_src_link.set_struct = src_children[i]; child_src_link.change_struct = src_cchildren[i]; child_dest_link.set_struct = dest_children[j]; child_dest_link.change_struct = dest_cchildren[j]; if (settings_apply(&child_dest_link, &child_src_link, pool, conflict_key_r) < 0) return -1; } else { /* append */ child_set = settings_dup(def->list_info, src_children[i], pool); array_append(dest_arr, &child_set, 1); settings_set_parent(def->list_info, child_set, dest_link->set_struct); child_set = settings_changes_init(def->list_info, src_cchildren[i], pool); array_append(dest_carr, &child_set, 1); } } return 0; } static int settings_apply(struct setting_link *dest_link, const struct setting_link *src_link, pool_t pool, const char **conflict_key_r) { const struct setting_define *def; const void *src, *csrc; void *dest, *cdest; for (def = dest_link->info->defines; def->key != NULL; def++) { csrc = CONST_PTR_OFFSET(src_link->change_struct, def->offset); cdest = PTR_OFFSET(dest_link->change_struct, def->offset); if (def->type == SET_DEFLIST || def->type == SET_STRLIST) { /* just add the new values */ } else if (def->type == SET_DEFLIST_UNIQUE) { /* merge sections */ } else if (*((const char *)csrc) == 0) { /* unchanged */ continue; } else if (def->type == SET_ALIAS) { /* ignore aliases */ continue; } else if (*((const char *)cdest) != 0) { /* conflict */ if (conflict_key_r != NULL) { *conflict_key_r = def->key; return -1; } continue; } else { *((char *)cdest) = 1; } /* found a changed setting */ src = CONST_PTR_OFFSET(src_link->set_struct, def->offset); dest = PTR_OFFSET(dest_link->set_struct, def->offset); if (setting_copy(def->type, src, dest, pool, FALSE)) { /* non-list */ } else if (def->type == SET_DEFLIST) { settings_copy_deflist(def, src_link, dest_link, pool); } else { i_assert(def->type == SET_DEFLIST_UNIQUE); if (settings_copy_deflist_unique(def, src_link, dest_link, pool, conflict_key_r) < 0) return -1; } } return 0; } int settings_parser_apply_changes(struct setting_parser_context *dest, const struct setting_parser_context *src, pool_t pool, const char **conflict_key_r) { unsigned int i; i_assert(src->root_count == dest->root_count); for (i = 0; i < dest->root_count; i++) { i_assert(src->roots[i].info == dest->roots[i].info); if (settings_apply(&dest->roots[i], &src->roots[i], pool, conflict_key_r) < 0) return -1; } return 0; } const char *settings_section_escape(const char *name) { #define CHAR_NEED_ESCAPE(c) \ ((c) == '=' || (c) == SETTINGS_SEPARATOR || (c) == '\\' || (c) == ' ' || (c) == ',') string_t *str; unsigned int i; for (i = 0; name[i] != '\0'; i++) { if (CHAR_NEED_ESCAPE(name[i])) break; } if (name[i] == '\0') return name; str = t_str_new(i + strlen(name+i) + 8); str_append_n(str, name, i); for (; name[i] != '\0'; i++) { switch (name[i]) { case '=': str_append(str, "\\e"); break; case SETTINGS_SEPARATOR: str_append(str, "\\s"); break; case '\\': str_append(str, "\\\\"); break; case ' ': str_append(str, "\\_"); break; case ',': str_append(str, "\\+"); break; default: str_append_c(str, name[i]); break; } } return str_c(str); } dovecot-2.2.33.2/src/lib-settings/settings-parser.h0000644000175000017500000002521113165463624017036 00000000000000#ifndef SETTINGS_PARSER_H #define SETTINGS_PARSER_H struct var_expand_table; struct var_expand_func_table; #define SETTINGS_SEPARATOR '/' #define SETTINGS_SEPARATOR_S "/" /* STR_VARS pointer begins with either of these initially. Before actually using the variables all variables in all unexpanded strings need to be expanded. Afterwards the string pointers should be increased to skip the initial '1' so it'll be easy to use them. */ #define SETTING_STRVAR_UNEXPANDED "0" #define SETTING_STRVAR_EXPANDED "1" /* When parsing streams, this character is translated to LF. */ #define SETTING_STREAM_LF_CHAR "\003" enum setting_type { SET_BOOL, SET_UINT, SET_UINT_OCT, SET_TIME, SET_SIZE, SET_IN_PORT, /* internet port */ SET_STR, SET_STR_VARS, /* string with %variables */ SET_ENUM, SET_DEFLIST, /* of type array_t */ SET_DEFLIST_UNIQUE, SET_STRLIST, /* of type ARRAY_TYPE(const_string) */ SET_ALIAS /* alias name for above setting definition */ }; #define SETTING_TYPE_IS_DEFLIST(type) \ ((type) == SET_DEFLIST || (type) == SET_DEFLIST_UNIQUE) #define SETTING_DEFINE_LIST_END { 0, NULL, 0, NULL } struct setting_define { enum setting_type type; const char *key; size_t offset; const struct setting_parser_info *list_info; }; #define SETTING_DEFINE_STRUCT_BOOL(name, struct_name) \ { SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, bool), \ #name, offsetof(struct struct_name, name), NULL } #define SETTING_DEFINE_STRUCT_UINT(name, struct_name) \ { SET_UINT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, unsigned int), \ #name, offsetof(struct struct_name, name), NULL } #define SETTING_DEFINE_STRUCT_SIZE(name, struct_name) \ { SET_SIZE + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, uoff_t), \ #name, offsetof(struct struct_name, name), NULL } #define SETTING_DEFINE_STRUCT_TIME(name, struct_name) \ { SET_TIME + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, unsigned int), \ #name, offsetof(struct struct_name, name), NULL } #define SETTING_DEFINE_STRUCT_STR(name, struct_name) \ { SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, const char *), \ #name, offsetof(struct struct_name, name), NULL } #define SETTING_DEFINE_STRUCT_IN_PORT(name, struct_name) \ { SET_IN_PORT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, in_port_t), \ #name, offsetof(struct struct_name, name), NULL } struct setting_parser_info { const char *module_name; const struct setting_define *defines; const void *defaults; size_t type_offset; size_t struct_size; size_t parent_offset; const struct setting_parser_info *parent; bool (*check_func)(void *set, pool_t pool, const char **error_r); const struct setting_parser_info *const *dependencies; struct dynamic_settings_parser *dynamic_parsers; }; ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info); /* name=NULL-terminated list of parsers. These follow the static settings. After this list follows the actual settings. */ struct dynamic_settings_parser { const char *name; const struct setting_parser_info *info; size_t struct_offset; }; ARRAY_DEFINE_TYPE(dynamic_settings_parser, struct dynamic_settings_parser); enum settings_parser_flags { SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS = 0x01, SETTINGS_PARSER_FLAG_TRACK_CHANGES = 0x02 }; struct setting_parser_context; struct setting_parser_context * settings_parser_init(pool_t set_pool, const struct setting_parser_info *root, enum settings_parser_flags flags); struct setting_parser_context * settings_parser_init_list(pool_t set_pool, const struct setting_parser_info *const *roots, unsigned int count, enum settings_parser_flags flags); void settings_parser_deinit(struct setting_parser_context **ctx); /* Return pointer to root setting structure. */ void *settings_parser_get(struct setting_parser_context *ctx); /* If there are multiple roots, return a NULL-terminated list to all of their settings. */ void **settings_parser_get_list(const struct setting_parser_context *ctx); /* Like settings_parser_get(), but return change struct. */ void *settings_parser_get_changes(struct setting_parser_context *ctx); /* Returns the setting parser's roots (same as given to init()). */ const struct setting_parser_info *const * settings_parser_get_roots(const struct setting_parser_context *ctx); /* Return the last error. */ const char *settings_parser_get_error(struct setting_parser_context *ctx); /* Return the parser info used for the previously parsed line. */ const struct setting_parser_info * settings_parse_get_prev_info(struct setting_parser_context *ctx); /* Returns TRUE if the given key is a valid setting. */ bool settings_parse_is_valid_key(struct setting_parser_context *ctx, const char *key); /* If key is an alias, return the primary key name. If key exists, return key itself. If key doesn't exist, return NULL. */ const char *settings_parse_unalias(struct setting_parser_context *ctx, const char *key); /* Returns pointer to value for a key, or NULL if not found. */ const void * settings_parse_get_value(struct setting_parser_context *ctx, const char *key, enum setting_type *type_r); /* Returns TRUE if setting has been changed by this parser. */ bool settings_parse_is_changed(struct setting_parser_context *ctx, const char *key); /* Parse a single line. Returns 1 if OK, 0 if key is unknown, -1 if error. */ int settings_parse_line(struct setting_parser_context *ctx, const char *line); /* Parse key/value pair. Returns 1 if OK, 0 if key is unknown, -1 if error. */ int settings_parse_keyvalue(struct setting_parser_context *ctx, const char *key, const char *value); /* Parse data already read in input stream. */ int settings_parse_stream(struct setting_parser_context *ctx, struct istream *input); /* Read data from input stream and parser it. returns -1 = error, 0 = done, 1 = not finished yet (stream is non-blocking) */ int settings_parse_stream_read(struct setting_parser_context *ctx, struct istream *input); /* Open file and parse it. */ int settings_parse_file(struct setting_parser_context *ctx, const char *path, size_t max_line_length); int settings_parse_environ(struct setting_parser_context *ctx); /* Execute the given binary and wait for it to return the configuration. */ int settings_parse_exec(struct setting_parser_context *ctx, const char *bin_path, const char *config_path, const char *service); /* Call all check_func()s to see if currently parsed settings are valid. */ bool settings_parser_check(struct setting_parser_context *ctx, pool_t pool, const char **error_r); bool settings_check(const struct setting_parser_info *info, pool_t pool, void *set, const char **error_r); /* While parsing values, specifies if STR_VARS strings are already expanded. */ void settings_parse_set_expanded(struct setting_parser_context *ctx, bool is_expanded); /* Mark all the parsed settings with given keys as being already expanded. */ void settings_parse_set_key_expandeded(struct setting_parser_context *ctx, pool_t pool, const char *key); void settings_parse_set_keys_expandeded(struct setting_parser_context *ctx, pool_t pool, const char *const *keys); /* Update variable string pointers to skip over the '1' or '0'. This is mainly useful when you want to run settings_parser_check() without actually knowing what the variables are. */ void settings_parse_var_skip(struct setting_parser_context *ctx); /* Expand all unexpanded variables using the given table. Update the string pointers so that they can be used without skipping over the '1'. */ void settings_var_expand(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table); void settings_var_expand_with_funcs(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table, const struct var_expand_func_table *func_table, void *func_context); /* Go through all the settings and return the first one that has an unexpanded setting containing the given %key. */ bool settings_vars_have_key(const struct setting_parser_info *info, void *set, char var_key, const char *long_var_key, const char **key_r, const char **value_r); /* Duplicate the entire settings structure. */ void *settings_dup(const struct setting_parser_info *info, const void *set, pool_t pool); /* Same as settings_dup(), but assume that the old pointers can still be safely used. This saves memory since strings don't have to be duplicated. */ void *settings_dup_with_pointers(const struct setting_parser_info *info, const void *set, pool_t pool); /* Duplicate the entire setting parser. */ struct setting_parser_context * settings_parser_dup(const struct setting_parser_context *old_ctx, pool_t new_pool); /* parsers is a name=NULL -terminated list. The parsers are appended as dynamic_settings_list structures to their parent. All must have the same parent. The new structures are allocated from the given pool. */ void settings_parser_info_update(pool_t pool, struct setting_parser_info *parent, const struct dynamic_settings_parser *parsers); void settings_parser_dyn_update(pool_t pool, const struct setting_parser_info *const **roots, const struct dynamic_settings_parser *dyn_parsers); /* Return pointer to beginning of settings for given name, or NULL if there is no such registered name. */ const void *settings_find_dynamic(const struct setting_parser_info *info, const void *base_set, const char *name); /* Copy changed settings from src to dest. If conflict_key_r is not NULL and both src and dest have changed the same setting, return -1 and set the key name. If it's NULL, the old setting is kept. KLUDGE: For SET_STRLIST types if both source and destination have identical keys, the duplicates in the source side are ignored. This is required to make the current config code work correctly. */ int settings_parser_apply_changes(struct setting_parser_context *dest, const struct setting_parser_context *src, pool_t pool, const char **conflict_key_r); /* Return section name escaped */ const char *settings_section_escape(const char *name); /* Parse time interval string, return as seconds. */ int settings_get_time(const char *str, unsigned int *secs_r, const char **error_r); /* Parse time interval string, return as milliseconds. */ int settings_get_time_msecs(const char *str, unsigned int *msecs_r, const char **error_r); /* Parse size string, return as bytes. */ int settings_get_size(const char *str, uoff_t *bytes_r, const char **error_r); #endif dovecot-2.2.33.2/src/lib-settings/Makefile.am0000644000175000017500000000131513123174404015553 00000000000000noinst_LTLIBRARIES = libsettings.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test libsettings_la_SOURCES = \ settings.c \ settings-parser.c headers = \ settings.h \ settings-parser.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-settings-parser noinst_PROGRAMS = $(test_programs) test_libs = \ libsettings.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_settings_parser_SOURCES = test-settings-parser.c test_settings_parser_LDADD = $(test_libs) test_settings_parser_DEPENDENCIES = $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib/0002755000175000017500000000000013172375610011751 500000000000000dovecot-2.2.33.2/src/lib/iostream-rawlog.h0000644000175000017500000000203513123174404015146 00000000000000#ifndef IOSTREAM_RAWLOG_H #define IOSTREAM_RAWLOG_H enum iostream_rawlog_flags { IOSTREAM_RAWLOG_FLAG_AUTOCLOSE = 0x01, IOSTREAM_RAWLOG_FLAG_BUFFERED = 0x02, IOSTREAM_RAWLOG_FLAG_TIMESTAMP = 0x04 }; /* Create rawlog *.in and *.out files to the given directory. */ int ATTR_NOWARN_UNUSED_RESULT iostream_rawlog_create(const char *dir, struct istream **input, struct ostream **output); /* Create rawlog prefix.in and prefix.out files. */ int ATTR_NOWARN_UNUSED_RESULT iostream_rawlog_create_prefix(const char *prefix, struct istream **input, struct ostream **output); /* Create rawlog path, writing both input and output to the same file. */ int ATTR_NOWARN_UNUSED_RESULT iostream_rawlog_create_path(const char *path, struct istream **input, struct ostream **output); /* Create rawlog that appends to the given rawlog_output. Both input and output are written to the same stream. */ void iostream_rawlog_create_from_stream(struct ostream *rawlog_output, struct istream **input, struct ostream **output); #endif dovecot-2.2.33.2/src/lib/base32.h0000644000175000017500000000334013123174404013111 00000000000000#ifndef BASE32_H #define BASE32_H /* Translates binary data into base32 (RFC 4648, Section 6). The src must not point to dest buffer. The pad argument determines whether output is padded with '='. */ void base32_encode(bool pad, const void *src, size_t src_size, buffer_t *dest); /* Translates binary data into base32hex (RFC 4648, Section 7). The src must not point to dest buffer. The pad argument determines whether output is padded with '='. */ void base32hex_encode(bool pad, const void *src, size_t src_size, buffer_t *dest); /* Translates base32/base32hex data into binary and appends it to dest buffer. dest may point to same buffer as src. Returns 1 if all ok, 0 if end of base32 data found, -1 if data is invalid. Any whitespace characters are ignored. This function may be called multiple times for parsing the same stream. If src_pos is non-NULL, it's updated to first non-translated character in src. */ int base32_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) ATTR_NULL(4); int base32hex_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) ATTR_NULL(4); /* Decode given string to a buffer allocated from data stack. */ buffer_t *t_base32_decode_str(const char *str); buffer_t *t_base32hex_decode_str(const char *str); /* Returns TRUE if c is a valid base32 encoding character (excluding '=') */ bool base32_is_valid_char(char c); bool base32hex_is_valid_char(char c); /* max. buffer size required for base32_encode()/base32hex_encode() */ #define MAX_BASE32_ENCODED_SIZE(size) \ ((size) / 5 * 8 + 8) /* max. buffer size required for base32_decode()/base32hex_decode() */ #define MAX_BASE32_DECODED_SIZE(size) \ ((size) / 8 * 5 + 5) #endif dovecot-2.2.33.2/src/lib/data-stack.c0000644000175000017500000003737313165463624014071 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "data-stack.h" #ifdef HAVE_GC_GC_H # include #elif defined (HAVE_GC_H) # include #endif /* Initial stack size - this should be kept in a size that doesn't exceed in a normal use to avoid extra malloc()ing. */ #ifdef DEBUG # define INITIAL_STACK_SIZE (1024*10) #else # define INITIAL_STACK_SIZE (1024*32) #endif #ifdef DEBUG # define CLEAR_CHR 0xD5 /* D5 is mnemonic for "Data 5tack" */ # define SENTRY_COUNT (4*8) # define BLOCK_CANARY ((void *)0xBADBADD5BADBADD5) /* contains 'D5' */ # define BLOCK_CANARY_CHECK(block) i_assert((block)->canary == BLOCK_CANARY) # define ALLOC_SIZE(size) (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(size + SENTRY_COUNT)) #else # define CLEAR_CHR 0 # define BLOCK_CANARY NULL # define BLOCK_CANARY_CHECK(block) do { ; } while(0) # define ALLOC_SIZE(size) MEM_ALIGN(size) #endif struct stack_block { struct stack_block *next; size_t size, left, lowwater; /* NULL or a poison value, just in case something accesses the memory in front of an allocated area */ void *canary; /* unsigned char data[]; */ }; #define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block)) #define STACK_BLOCK_DATA(block) \ ((unsigned char *) (block) + SIZEOF_MEMBLOCK) /* current_frame_block contains last t_push()ed frames. After that new stack_frame_block is created and it's ->prev is set to current_frame_block. */ #define BLOCK_FRAME_COUNT 32 struct stack_frame_block { struct stack_frame_block *prev; struct stack_block *block[BLOCK_FRAME_COUNT]; size_t block_space_used[BLOCK_FRAME_COUNT]; size_t last_alloc_size[BLOCK_FRAME_COUNT]; #ifdef DEBUG const char *marker[BLOCK_FRAME_COUNT]; /* Fairly arbitrary profiling data */ unsigned long long alloc_bytes[BLOCK_FRAME_COUNT]; unsigned int alloc_count[BLOCK_FRAME_COUNT]; #endif }; unsigned int data_stack_frame = 0; static int frame_pos = BLOCK_FRAME_COUNT-1; /* in current_frame_block */ static struct stack_frame_block *current_frame_block; static struct stack_frame_block *unused_frame_blocks; static struct stack_block *current_block; /* block now used for allocation */ static struct stack_block *unused_block; /* largest unused block is kept here */ static struct stack_block *last_buffer_block; static size_t last_buffer_size; #ifdef DEBUG static bool clean_after_pop = TRUE; #else static bool clean_after_pop = FALSE; #endif static bool outofmem = FALSE; static union { struct stack_block block; unsigned char data[512]; } outofmem_area; static inline unsigned char *data_stack_after_last_alloc(struct stack_block *block) { return STACK_BLOCK_DATA(block) + (block->size - block->left); } static void data_stack_last_buffer_reset(bool preserve_data ATTR_UNUSED) { if (last_buffer_block != NULL) { #ifdef DEBUG unsigned char *last_alloc_end, *p, *pend; last_alloc_end = data_stack_after_last_alloc(current_block); p = last_alloc_end + MEM_ALIGN(sizeof(size_t)) + last_buffer_size; pend = last_alloc_end + ALLOC_SIZE(last_buffer_size); #endif /* reset t_buffer_get() mark - not really needed but makes it easier to notice if t_malloc()/t_push()/t_pop() is called between t_buffer_get() and t_buffer_alloc(). do this before we get to i_panic() to avoid recursive panics. */ last_buffer_block = NULL; #ifdef DEBUG while (p < pend) if (*p++ != CLEAR_CHR) i_panic("t_buffer_get(): buffer overflow"); if (!preserve_data) { p = last_alloc_end; memset(p, CLEAR_CHR, SENTRY_COUNT); } #endif } } unsigned int t_push(const char *marker) { struct stack_frame_block *frame_block; frame_pos++; if (frame_pos == BLOCK_FRAME_COUNT) { /* frame block full */ if (data_stack_frame == 0) { /* kludgy, but allow this before initialization */ frame_pos = 0; data_stack_init(); return t_push(marker); } frame_pos = 0; if (unused_frame_blocks == NULL) { /* allocate new block */ #ifndef USE_GC frame_block = calloc(sizeof(*frame_block), 1); #else frame_block = GC_malloc(sizeof(*frame_block)); #endif if (frame_block == NULL) { i_fatal_status(FATAL_OUTOFMEM, "t_push(): Out of memory"); } } else { /* use existing unused frame_block */ frame_block = unused_frame_blocks; unused_frame_blocks = unused_frame_blocks->prev; } frame_block->prev = current_frame_block; current_frame_block = frame_block; } data_stack_last_buffer_reset(FALSE); /* mark our current position */ current_frame_block->block[frame_pos] = current_block; current_frame_block->block_space_used[frame_pos] = current_block->left; current_frame_block->last_alloc_size[frame_pos] = 0; #ifdef DEBUG current_frame_block->marker[frame_pos] = marker; current_frame_block->alloc_bytes[frame_pos] = 0ULL; current_frame_block->alloc_count[frame_pos] = 0; #else (void)marker; /* only used for debugging */ #endif return data_stack_frame++; } unsigned int t_push_named(const char *format, ...) { unsigned int ret = t_push(NULL); #ifdef DEBUG va_list args; va_start(args, format); current_frame_block->marker[frame_pos] = p_strdup_vprintf(unsafe_data_stack_pool, format, args); va_end(args); #else (void)format; /* unused in non-DEBUG builds */ #endif return ret; } static void free_blocks(struct stack_block *block) { struct stack_block *next; /* free all the blocks, except if any of them is bigger than unused_block, replace it */ while (block != NULL) { BLOCK_CANARY_CHECK(block); next = block->next; if (clean_after_pop) memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size); if (unused_block == NULL || block->size > unused_block->size) { #ifndef USE_GC free(unused_block); #endif unused_block = block; } else { #ifndef USE_GC if (block != &outofmem_area.block) free(block); #endif } block = next; } } #ifdef DEBUG static void t_pop_verify(void) { struct stack_block *block; unsigned char *p; size_t pos, max_pos, used_size; block = current_frame_block->block[frame_pos]; pos = block->size - current_frame_block->block_space_used[frame_pos]; while (block != NULL) { BLOCK_CANARY_CHECK(block); used_size = block->size - block->left; p = STACK_BLOCK_DATA(block); while (pos < used_size) { size_t requested_size = *(size_t *)(p + pos); if (used_size - pos < requested_size) i_panic("data stack[%s]: saved alloc size broken", current_frame_block->marker[frame_pos]); max_pos = pos + ALLOC_SIZE(requested_size); pos += MEM_ALIGN(sizeof(size_t)) + requested_size; for (; pos < max_pos; pos++) { if (p[pos] != CLEAR_CHR) i_panic("data stack[%s]: buffer overflow", current_frame_block->marker[frame_pos]); } } /* if we had used t_buffer_get(), the rest of the buffer may not contain CLEAR_CHRs. but we've already checked all the allocations, so there's no need to check them anyway. */ block = block->next; pos = 0; } } #endif unsigned int t_pop(void) { struct stack_frame_block *frame_block; if (unlikely(frame_pos < 0)) i_panic("t_pop() called with empty stack"); data_stack_last_buffer_reset(FALSE); #ifdef DEBUG t_pop_verify(); #endif /* update the current block */ current_block = current_frame_block->block[frame_pos]; BLOCK_CANARY_CHECK(current_block); if (clean_after_pop) { size_t pos, used_size; pos = current_block->size - current_frame_block->block_space_used[frame_pos]; used_size = current_block->size - current_block->lowwater; i_assert(used_size >= pos); memset(STACK_BLOCK_DATA(current_block) + pos, CLEAR_CHR, used_size - pos); } current_block->left = current_frame_block->block_space_used[frame_pos]; current_block->lowwater = current_block->left; if (current_block->next != NULL) { /* free unused blocks */ free_blocks(current_block->next); current_block->next = NULL; } if (frame_pos > 0) frame_pos--; else { /* frame block is now unused, add it to unused list */ frame_pos = BLOCK_FRAME_COUNT-1; frame_block = current_frame_block; current_frame_block = frame_block->prev; frame_block->prev = unused_frame_blocks; unused_frame_blocks = frame_block; } return --data_stack_frame; } void t_pop_check(unsigned int *id) { if (unlikely(t_pop() != *id)) i_panic("Leaked t_pop() call"); *id = 0; } static struct stack_block *mem_block_alloc(size_t min_size) { struct stack_block *block; size_t prev_size, alloc_size; prev_size = current_block == NULL ? 0 : current_block->size; alloc_size = nearest_power(MALLOC_ADD(prev_size, min_size)); /* nearest_power() returns 2^n values, so alloc_size can't be anywhere close to SIZE_MAX */ #ifndef USE_GC block = malloc(SIZEOF_MEMBLOCK + alloc_size); #else block = GC_malloc(SIZEOF_MEMBLOCK + alloc_size); #endif if (unlikely(block == NULL)) { if (outofmem) { if (min_size > outofmem_area.block.left) abort(); return &outofmem_area.block; } outofmem = TRUE; i_panic("data stack: Out of memory when allocating %" PRIuSIZE_T" bytes", alloc_size + SIZEOF_MEMBLOCK); } block->size = alloc_size; block->left = 0; block->lowwater = block->size; block->next = NULL; block->canary = BLOCK_CANARY; #ifdef DEBUG memset(STACK_BLOCK_DATA(block), CLEAR_CHR, alloc_size); #endif return block; } static void *t_malloc_real(size_t size, bool permanent) { void *ret; size_t alloc_size; #ifdef DEBUG bool warn = FALSE; int old_errno = errno; #endif if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); if (unlikely(data_stack_frame == 0)) { /* kludgy, but allow this before initialization */ data_stack_init(); } BLOCK_CANARY_CHECK(current_block); /* allocate only aligned amount of memory so alignment comes always properly */ alloc_size = ALLOC_SIZE(size); #ifdef DEBUG if(permanent) { current_frame_block->alloc_bytes[frame_pos] += alloc_size; current_frame_block->alloc_count[frame_pos]++; } #endif data_stack_last_buffer_reset(TRUE); /* used for t_try_realloc() */ current_frame_block->last_alloc_size[frame_pos] = alloc_size; if (current_block->left < alloc_size) { struct stack_block *block; /* current block is full, see if we can use the unused_block */ if (unused_block != NULL && unused_block->size >= alloc_size) { block = unused_block; unused_block = NULL; } else { block = mem_block_alloc(alloc_size); #ifdef DEBUG warn = TRUE; #endif } block->left = block->size; block->next = NULL; current_block->next = block; current_block = block; } /* enough space in current block, use it */ ret = data_stack_after_last_alloc(current_block); if (current_block->left - alloc_size < current_block->lowwater) current_block->lowwater = current_block->left - alloc_size; if (permanent) current_block->left -= alloc_size; #ifdef DEBUG if (warn && getenv("DEBUG_SILENT") == NULL) { /* warn after allocation, so if i_warning() wants to allocate more memory we don't go to infinite loop */ i_warning("Growing data stack by %"PRIuSIZE_T" as " "'%s' reaches %llu bytes from %u allocations.", current_block->size, current_frame_block->marker[frame_pos], current_frame_block->alloc_bytes[frame_pos], current_frame_block->alloc_count[frame_pos]); } memcpy(ret, &size, sizeof(size)); ret = PTR_OFFSET(ret, MEM_ALIGN(sizeof(size))); /* make sure the sentry contains CLEAR_CHRs. it might not if we had used t_buffer_get(). */ memset(PTR_OFFSET(ret, size), CLEAR_CHR, MEM_ALIGN(size + SENTRY_COUNT) - size); /* we rely on errno not changing. it shouldn't. */ i_assert(errno == old_errno); #endif return ret; } void *t_malloc(size_t size) { return t_malloc_real(size, TRUE); } void *t_malloc0(size_t size) { void *mem; mem = t_malloc_real(size, TRUE); memset(mem, 0, size); return mem; } bool t_try_realloc(void *mem, size_t size) { size_t debug_adjust = 0, last_alloc_size; unsigned char *after_last_alloc; if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); BLOCK_CANARY_CHECK(current_block); last_alloc_size = current_frame_block->last_alloc_size[frame_pos]; /* see if we're trying to grow the memory we allocated last */ after_last_alloc = data_stack_after_last_alloc(current_block); #ifdef DEBUG debug_adjust = MEM_ALIGN(sizeof(size_t)); #endif if (after_last_alloc - last_alloc_size + debug_adjust == mem) { /* yeah, see if we have space to grow */ size_t new_alloc_size, alloc_growth; new_alloc_size = ALLOC_SIZE(size); alloc_growth = (new_alloc_size - last_alloc_size); #ifdef DEBUG size_t old_raw_size; /* sorry, non-C99 users - add braces if you need them */ old_raw_size = *(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t))); i_assert(ALLOC_SIZE(old_raw_size) == last_alloc_size); /* Only check one byte for over-run, that catches most offenders who are likely to use t_try_realloc() */ i_assert(((unsigned char*)mem)[old_raw_size] == CLEAR_CHR); #endif if (current_block->left >= alloc_growth) { /* just shrink the available size */ current_block->left -= alloc_growth; if (current_block->left < current_block->lowwater) current_block->lowwater = current_block->left; current_frame_block->last_alloc_size[frame_pos] = new_alloc_size; #ifdef DEBUG /* All reallocs are permanent by definition However, they don't count as a new allocation */ current_frame_block->alloc_bytes[frame_pos] += alloc_growth; *(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t))) = size; memset(PTR_OFFSET(mem, size), CLEAR_CHR, new_alloc_size - size - MEM_ALIGN(sizeof(size_t))); #endif return TRUE; } } return FALSE; } size_t t_get_bytes_available(void) { #ifndef DEBUG const unsigned int extra = MEM_ALIGN_SIZE-1; #else const unsigned int extra = MEM_ALIGN_SIZE-1 + SENTRY_COUNT + MEM_ALIGN(sizeof(size_t)); #endif BLOCK_CANARY_CHECK(current_block); return current_block->left < extra ? current_block->left : current_block->left - extra; } void *t_buffer_get(size_t size) { void *ret; ret = t_malloc_real(size, FALSE); last_buffer_size = size; last_buffer_block = current_block; return ret; } void *t_buffer_reget(void *buffer, size_t size) { size_t old_size; void *new_buffer; old_size = last_buffer_size; if (size <= old_size) return buffer; new_buffer = t_buffer_get(size); if (new_buffer != buffer) memcpy(new_buffer, buffer, old_size); return new_buffer; } void t_buffer_alloc(size_t size) { i_assert(last_buffer_block != NULL); i_assert(last_buffer_size >= size); i_assert(current_block->left >= size); /* we've already reserved the space, now we just mark it used */ (void)t_malloc_real(size, TRUE); } void t_buffer_alloc_last_full(void) { if (last_buffer_block != NULL) (void)t_malloc_real(last_buffer_size, TRUE); } void data_stack_set_clean_after_pop(bool enable ATTR_UNUSED) { #ifndef DEBUG clean_after_pop = enable; #endif } void data_stack_init(void) { if (data_stack_frame > 0) { /* already initialized (we did auto-initialization in t_malloc/t_push) */ return; } data_stack_frame = 1; outofmem_area.block.size = outofmem_area.block.left = sizeof(outofmem_area) - sizeof(outofmem_area.block); current_block = mem_block_alloc(INITIAL_STACK_SIZE); current_block->left = current_block->size; current_block->next = NULL; current_frame_block = NULL; unused_frame_blocks = NULL; frame_pos = BLOCK_FRAME_COUNT-1; last_buffer_block = NULL; last_buffer_size = 0; (void)t_push("data_stack_init"); } void data_stack_deinit(void) { (void)t_pop(); if (frame_pos != BLOCK_FRAME_COUNT-1) i_panic("Missing t_pop() call"); #ifndef USE_GC while (unused_frame_blocks != NULL) { struct stack_frame_block *frame_block = unused_frame_blocks; unused_frame_blocks = unused_frame_blocks->prev; free(frame_block); } free(current_block); free(unused_block); #endif unused_frame_blocks = NULL; current_block = NULL; unused_block = NULL; } dovecot-2.2.33.2/src/lib/istream-seekable.h0000644000175000017500000000200213123174404015241 00000000000000#ifndef ISTREAM_SEEKABLE_H #define ISTREAM_SEEKABLE_H /* Create a seekable stream from given NULL-terminated list of input streams. Try to keep it in memory, but use a temporary file if it's too large. When max_buffer_size is reached, fd_callback is called. It should return the fd and path of the created file. Typically the callback would also unlink the file before returning. */ struct istream * i_streams_merge(struct istream *input[], size_t max_buffer_size, int (*fd_callback)(const char **path_r, void *context), void *context) ATTR_NULL(4); /* Same as i_streams_merge(), but if all of the inputs are seekable already, create a concat stream instead. */ struct istream * i_stream_create_seekable(struct istream *input[], size_t max_buffer_size, int (*fd_callback)(const char **path_r, void *context), void *context) ATTR_NULL(4); struct istream * i_stream_create_seekable_path(struct istream *input[], size_t max_buffer_size, const char *temp_path_prefix); #endif dovecot-2.2.33.2/src/lib/ostream-file.c0000644000175000017500000006423313165463624014437 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "ioloop.h" #include "write-full.h" #include "net.h" #include "sendfile-util.h" #include "istream.h" #include "istream-private.h" #include "ostream-file-private.h" #include #include #ifdef HAVE_SYS_UIO_H # include #endif /* try to keep the buffer size within 4k..128k. ReiserFS may actually return 128k as optimal size. */ #define DEFAULT_OPTIMAL_BLOCK_SIZE IO_BLOCK_SIZE #define MAX_OPTIMAL_BLOCK_SIZE (128*1024) #define IS_STREAM_EMPTY(fstream) \ ((fstream)->head == (fstream)->tail && !(fstream)->full) #define MAX_SSIZE_T(size) \ ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX) static void stream_send_io(struct file_ostream *fstream); static void stream_closed(struct file_ostream *fstream) { if (fstream->io != NULL) io_remove(&fstream->io); if (fstream->autoclose_fd && fstream->fd != -1) { if (close(fstream->fd) < 0) { i_error("file_ostream.close(%s) failed: %m", o_stream_get_name(&fstream->ostream.ostream)); } } fstream->fd = -1; fstream->ostream.ostream.closed = TRUE; } void o_stream_file_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct file_ostream *fstream = (struct file_ostream *)stream; /* flush output before really closing it */ (void)o_stream_flush(&fstream->ostream.ostream); stream_closed(fstream); } static void o_stream_file_destroy(struct iostream_private *stream) { struct file_ostream *fstream = (struct file_ostream *)stream; i_free(fstream->buffer); } static size_t file_buffer_get_used_size(struct file_ostream *fstream) { if (fstream->head == fstream->tail) return fstream->full ? fstream->buffer_size : 0; else if (fstream->head < fstream->tail) { /* ...HXXXT... */ return fstream->tail - fstream->head; } else { /* XXXT...HXXX */ return fstream->tail + (fstream->buffer_size - fstream->head); } } static void update_buffer(struct file_ostream *fstream, size_t size) { size_t used; if (IS_STREAM_EMPTY(fstream) || size == 0) return; if (fstream->head < fstream->tail) { /* ...HXXXT... */ used = fstream->tail - fstream->head; i_assert(size <= used); fstream->head += size; } else { /* XXXT...HXXX */ used = fstream->buffer_size - fstream->head; if (size > used) { size -= used; i_assert(size <= fstream->tail); fstream->head = size; } else { fstream->head += size; } fstream->full = FALSE; } if (fstream->head == fstream->tail) fstream->head = fstream->tail = 0; if (fstream->head == fstream->buffer_size) fstream->head = 0; } static void o_stream_socket_cork(struct file_ostream *fstream) { if (fstream->ostream.corked && !fstream->socket_cork_set) { if (!fstream->no_socket_cork) { if (net_set_cork(fstream->fd, TRUE) < 0) fstream->no_socket_cork = TRUE; else fstream->socket_cork_set = TRUE; } } } static int o_stream_lseek(struct file_ostream *fstream) { off_t ret; if (fstream->real_offset == fstream->buffer_offset) return 0; ret = lseek(fstream->fd, (off_t)fstream->buffer_offset, SEEK_SET); if (ret < 0) { io_stream_set_error(&fstream->ostream.iostream, "lseek() failed: %m"); fstream->ostream.ostream.stream_errno = errno; return -1; } if (ret != (off_t)fstream->buffer_offset) { io_stream_set_error(&fstream->ostream.iostream, "lseek() returned wrong value"); fstream->ostream.ostream.stream_errno = EINVAL; return -1; } fstream->real_offset = fstream->buffer_offset; return 0; } ssize_t o_stream_file_writev(struct file_ostream *fstream, const struct const_iovec *iov, unsigned int iov_count) { ssize_t ret; size_t size, sent; unsigned int i; if (iov_count == 1) { i_assert(iov->iov_len > 0); if (!fstream->file || fstream->real_offset == fstream->buffer_offset) { ret = write(fstream->fd, iov->iov_base, iov->iov_len); if (ret > 0) fstream->real_offset += ret; } else { ret = pwrite(fstream->fd, iov->iov_base, iov->iov_len, fstream->buffer_offset); } } else { if (o_stream_lseek(fstream) < 0) return -1; sent = 0; while (iov_count > IOV_MAX) { size = 0; for (i = 0; i < IOV_MAX; i++) size += iov[i].iov_len; ret = writev(fstream->fd, (const struct iovec *)iov, IOV_MAX); if (ret != (ssize_t)size) { break; } fstream->real_offset += ret; fstream->buffer_offset += ret; sent += ret; iov += IOV_MAX; iov_count -= IOV_MAX; } if (iov_count <= IOV_MAX) { size = 0; for (i = 0; i < iov_count; i++) size += iov[i].iov_len; ret = writev(fstream->fd, (const struct iovec *)iov, iov_count); } if (ret > 0) { fstream->real_offset += ret; ret += sent; } else if (!fstream->file && sent > 0) { /* return what we managed to get sent */ ret = sent; } } return ret; } static ssize_t o_stream_file_writev_full(struct file_ostream *fstream, const struct const_iovec *iov, unsigned int iov_count) { ssize_t ret, ret2; size_t size, total_size; bool partial; unsigned int i; for (i = 0, total_size = 0; i < iov_count; i++) total_size += iov[i].iov_len; o_stream_socket_cork(fstream); ret = fstream->writev(fstream, iov, iov_count); partial = ret != (ssize_t)total_size; if (ret < 0) { if (fstream->file) { if (errno == EINTR) { /* automatically retry */ return o_stream_file_writev_full(fstream, iov, iov_count); } } else if (errno == EAGAIN || errno == EINTR) { /* try again later */ return 0; } fstream->ostream.ostream.stream_errno = errno; stream_closed(fstream); return -1; } if (unlikely(ret == 0 && fstream->file)) { /* assume out of disk space */ fstream->ostream.ostream.stream_errno = ENOSPC; stream_closed(fstream); return -1; } fstream->buffer_offset += ret; if (partial && fstream->file) { /* we failed to write everything to a file. either we ran out of disk space or we're writing to NFS. try to write the rest to resolve this. */ size = ret; while (iov_count > 0 && size >= iov->iov_len) { size -= iov->iov_len; iov++; iov_count--; } i_assert(iov_count > 0); if (size == 0) ret2 = o_stream_file_writev_full(fstream, iov, iov_count); else { /* write the first iov separately */ struct const_iovec new_iov; new_iov.iov_base = CONST_PTR_OFFSET(iov->iov_base, size); new_iov.iov_len = iov->iov_len - size; ret2 = o_stream_file_writev_full(fstream, &new_iov, 1); if (ret2 > 0) { i_assert((size_t)ret2 == new_iov.iov_len); /* write the rest */ if (iov_count > 1) { ret += ret2; ret2 = o_stream_file_writev_full(fstream, iov + 1, iov_count - 1); } } } i_assert(ret2 != 0); if (ret2 < 0) ret = ret2; else ret += ret2; } i_assert(ret < 0 || !fstream->file || (size_t)ret == total_size); return ret; } /* returns how much of vector was used */ static int o_stream_fill_iovec(struct file_ostream *fstream, struct const_iovec iov[2]) { if (IS_STREAM_EMPTY(fstream)) return 0; if (fstream->head < fstream->tail) { iov[0].iov_base = fstream->buffer + fstream->head; iov[0].iov_len = fstream->tail - fstream->head; return 1; } else { iov[0].iov_base = fstream->buffer + fstream->head; iov[0].iov_len = fstream->buffer_size - fstream->head; if (fstream->tail == 0) return 1; else { iov[1].iov_base = fstream->buffer; iov[1].iov_len = fstream->tail; return 2; } } } static int buffer_flush(struct file_ostream *fstream) { struct const_iovec iov[2]; int iov_len; ssize_t ret; iov_len = o_stream_fill_iovec(fstream, iov); if (iov_len > 0) { ret = o_stream_file_writev_full(fstream, iov, iov_len); if (ret < 0) return -1; update_buffer(fstream, ret); } return IS_STREAM_EMPTY(fstream) ? 1 : 0; } static void o_stream_file_cork(struct ostream_private *stream, bool set) { struct file_ostream *fstream = (struct file_ostream *)stream; int ret; if (stream->corked != set && !stream->ostream.closed) { if (set && fstream->io != NULL) io_remove(&fstream->io); else if (!set) { /* buffer flushing might close the stream */ ret = buffer_flush(fstream); if (fstream->io == NULL && (ret == 0 || fstream->flush_pending) && !stream->ostream.closed) { fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io, fstream); } } if (fstream->socket_cork_set) { i_assert(!set); if (net_set_cork(fstream->fd, FALSE) < 0) fstream->no_socket_cork = TRUE; fstream->socket_cork_set = FALSE; } stream->corked = set; } } static int o_stream_file_flush(struct ostream_private *stream) { struct file_ostream *fstream = (struct file_ostream *) stream; return buffer_flush(fstream); } static void o_stream_file_flush_pending(struct ostream_private *stream, bool set) { struct file_ostream *fstream = (struct file_ostream *) stream; fstream->flush_pending = set; if (set && !stream->corked && fstream->io == NULL) { fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io, fstream); } } static size_t get_unused_space(const struct file_ostream *fstream) { if (fstream->head > fstream->tail) { /* XXXT...HXXX */ return fstream->head - fstream->tail; } else if (fstream->head < fstream->tail) { /* ...HXXXT... */ return (fstream->buffer_size - fstream->tail) + fstream->head; } else { /* either fully unused or fully used */ return fstream->full ? 0 : fstream->buffer_size; } } static size_t o_stream_file_get_used_size(const struct ostream_private *stream) { const struct file_ostream *fstream = (const struct file_ostream *)stream; return fstream->buffer_size - get_unused_space(fstream); } static int o_stream_file_seek(struct ostream_private *stream, uoff_t offset) { struct file_ostream *fstream = (struct file_ostream *)stream; if (offset > OFF_T_MAX) { stream->ostream.stream_errno = EINVAL; return -1; } if (!fstream->file) { stream->ostream.stream_errno = ESPIPE; return -1; } if (buffer_flush(fstream) < 0) return -1; stream->ostream.offset = offset; fstream->buffer_offset = offset; return 1; } static void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes) { size_t size, new_size, end_size; size = nearest_power(fstream->buffer_size + bytes); if (size > fstream->ostream.max_buffer_size) { /* limit the size */ size = fstream->ostream.max_buffer_size; } else if (fstream->ostream.corked) { /* try to use optimal buffer size with corking */ new_size = I_MIN(fstream->optimal_block_size, fstream->ostream.max_buffer_size); if (new_size > size) size = new_size; } if (size <= fstream->buffer_size) return; fstream->buffer = i_realloc(fstream->buffer, fstream->buffer_size, size); if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) { /* move head forward to end of buffer */ end_size = fstream->buffer_size - fstream->head; memmove(fstream->buffer + size - end_size, fstream->buffer + fstream->head, end_size); fstream->head = size - end_size; } fstream->full = FALSE; fstream->buffer_size = size; } static void stream_send_io(struct file_ostream *fstream) { struct ostream *ostream = &fstream->ostream.ostream; int ret; /* Set flush_pending = FALSE first before calling the flush callback, and change it to TRUE only if callback returns 0. That way the callback can call o_stream_set_flush_pending() again and we don't forget it even if flush callback returns 1. */ fstream->flush_pending = FALSE; o_stream_ref(ostream); if (fstream->ostream.callback != NULL) ret = fstream->ostream.callback(fstream->ostream.context); else ret = o_stream_file_flush(&fstream->ostream); if (ret == 0) fstream->flush_pending = TRUE; if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) { if (fstream->io != NULL) { /* all sent */ io_remove(&fstream->io); } } else if (!fstream->ostream.ostream.closed) { /* Add the IO handler if it's not there already. Callback might have just returned 0 without there being any data to be sent. */ if (fstream->io == NULL) { fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io, fstream); } } o_stream_unref(&ostream); } static size_t o_stream_add(struct file_ostream *fstream, const void *data, size_t size) { size_t unused, sent; int i; unused = get_unused_space(fstream); if (unused < size) o_stream_grow_buffer(fstream, size-unused); sent = 0; for (i = 0; i < 2 && sent < size && !fstream->full; i++) { unused = fstream->tail >= fstream->head ? fstream->buffer_size - fstream->tail : fstream->head - fstream->tail; if (unused > size-sent) unused = size-sent; memcpy(fstream->buffer + fstream->tail, CONST_PTR_OFFSET(data, sent), unused); sent += unused; fstream->tail += unused; if (fstream->tail == fstream->buffer_size) fstream->tail = 0; if (fstream->head == fstream->tail && fstream->buffer_size > 0) fstream->full = TRUE; } if (sent != 0 && fstream->io == NULL && !fstream->ostream.corked && !fstream->file) { fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io, fstream); } return sent; } ssize_t o_stream_file_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct file_ostream *fstream = (struct file_ostream *)stream; size_t size, total_size, added, optimal_size; unsigned int i; ssize_t ret = 0; for (i = 0, size = 0; i < iov_count; i++) size += iov[i].iov_len; total_size = size; if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) { if (o_stream_file_flush(stream) < 0) return -1; } optimal_size = I_MIN(fstream->optimal_block_size, fstream->ostream.max_buffer_size); if (IS_STREAM_EMPTY(fstream) && (!stream->corked || size >= optimal_size)) { /* send immediately */ ret = o_stream_file_writev_full(fstream, iov, iov_count); if (ret < 0) return -1; size = ret; while (size > 0 && iov_count > 0 && size >= iov[0].iov_len) { size -= iov[0].iov_len; iov++; iov_count--; } if (iov_count == 0) i_assert(size == 0); else { added = o_stream_add(fstream, CONST_PTR_OFFSET(iov[0].iov_base, size), iov[0].iov_len - size); ret += added; if (added != iov[0].iov_len - size) { /* buffer full */ stream->ostream.offset += ret; return ret; } iov++; iov_count--; } } /* buffer it, at least partly */ for (i = 0; i < iov_count; i++) { added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len); ret += added; if (added != iov[i].iov_len) break; } stream->ostream.offset += ret; i_assert((size_t)ret <= total_size); i_assert((size_t)ret == total_size || !fstream->file); return ret; } static size_t o_stream_file_update_buffer(struct file_ostream *fstream, const void *data, size_t size, size_t pos) { size_t avail, copy_size; if (fstream->head < fstream->tail) { /* ...HXXXT... */ i_assert(pos < fstream->tail); avail = fstream->tail - pos; } else { /* XXXT...HXXX */ avail = fstream->buffer_size - pos; } copy_size = I_MIN(size, avail); memcpy(fstream->buffer + pos, data, copy_size); data = CONST_PTR_OFFSET(data, copy_size); size -= copy_size; if (size > 0 && fstream->head >= fstream->tail) { /* wraps to beginning of the buffer */ copy_size = I_MIN(size, fstream->tail); memcpy(fstream->buffer, data, copy_size); size -= copy_size; } return size; } static int o_stream_file_write_at(struct ostream_private *stream, const void *data, size_t size, uoff_t offset) { struct file_ostream *fstream = (struct file_ostream *)stream; size_t used, pos, skip, left; /* update buffer if the write overlaps it */ used = file_buffer_get_used_size(fstream); if (used > 0 && fstream->buffer_offset < offset + size && fstream->buffer_offset + used > offset) { if (fstream->buffer_offset <= offset) { /* updating from the beginning */ skip = 0; } else { skip = fstream->buffer_offset - offset; } pos = (fstream->head + offset + skip - fstream->buffer_offset) % fstream->buffer_size; left = o_stream_file_update_buffer(fstream, CONST_PTR_OFFSET(data, skip), size - skip, pos); if (left > 0) { /* didn't write all of it */ if (skip > 0) { /* we also have to write a prefix. don't bother with two syscalls, just write all of it in one pwrite(). */ } else { /* write only the suffix */ size_t update_count = size - left; data = CONST_PTR_OFFSET(data, update_count); size -= update_count; offset += update_count; } } else if (skip == 0) { /* everything done */ return 0; } else { /* still have to write prefix */ size = skip; } } /* we couldn't write everything to the buffer. flush the buffer and pwrite() the rest. */ if (o_stream_file_flush(stream) < 0) return -1; if (pwrite_full(fstream->fd, data, size, offset) < 0) { stream->ostream.stream_errno = errno; stream_closed(fstream); return -1; } return 0; } static off_t io_stream_sendfile(struct ostream_private *outstream, struct istream *instream, int in_fd, bool *sendfile_not_supported_r) { struct file_ostream *foutstream = (struct file_ostream *)outstream; uoff_t start_offset; uoff_t in_size, offset, send_size, v_offset; ssize_t ret; *sendfile_not_supported_r = FALSE; if ((ret = i_stream_get_size(instream, TRUE, &in_size)) <= 0) { outstream->ostream.stream_errno = ret == 0 ? ESPIPE : instream->stream_errno; return -1; } o_stream_socket_cork(foutstream); /* flush out any data in buffer */ if ((ret = buffer_flush(foutstream)) <= 0) return ret; if (o_stream_lseek(foutstream) < 0) return -1; start_offset = v_offset = instream->v_offset; do { offset = instream->real_stream->abs_start_offset + v_offset; send_size = in_size - v_offset; ret = safe_sendfile(foutstream->fd, in_fd, &offset, MAX_SSIZE_T(send_size)); if (ret <= 0) { if (ret == 0) break; if (foutstream->file) { if (errno == EINTR) { /* automatically retry */ continue; } } else { if (errno == EINTR || errno == EAGAIN) { ret = 0; break; } } if (errno == EINVAL) *sendfile_not_supported_r = TRUE; else { outstream->ostream.stream_errno = errno; /* close only if error wasn't because sendfile() isn't supported */ stream_closed(foutstream); } break; } v_offset += ret; foutstream->real_offset += ret; foutstream->buffer_offset += ret; outstream->ostream.offset += ret; } while ((uoff_t)ret != send_size); i_stream_seek(instream, v_offset); if (ret == 0) { /* we should be at EOF. if not, write more. */ i_assert(!foutstream->file || instream->v_offset - start_offset == in_size); if (i_stream_read(instream) > 0) { if (io_stream_sendfile(outstream, instream, in_fd, sendfile_not_supported_r) < 0) return -1; } } return ret < 0 ? -1 : (off_t)(instream->v_offset - start_offset); } static off_t io_stream_copy_backwards(struct ostream_private *outstream, struct istream *instream, uoff_t in_size) { struct file_ostream *foutstream = (struct file_ostream *)outstream; uoff_t in_start_offset, in_offset, in_limit, out_offset; const unsigned char *data; size_t buffer_size, size, read_size; ssize_t ret; i_assert(IS_STREAM_EMPTY(foutstream)); /* figure out optimal buffer size */ buffer_size = instream->real_stream->buffer_size; if (buffer_size == 0 || buffer_size > foutstream->buffer_size) { if (foutstream->optimal_block_size > foutstream->buffer_size) { o_stream_grow_buffer(foutstream, foutstream->optimal_block_size - foutstream->buffer_size); } buffer_size = foutstream->buffer_size; } in_start_offset = instream->v_offset; in_offset = in_limit = in_size; out_offset = outstream->ostream.offset + (in_offset - in_start_offset); while (in_offset > in_start_offset) { if (in_offset - in_start_offset <= buffer_size) read_size = in_offset - in_start_offset; else read_size = buffer_size; in_offset -= read_size; out_offset -= read_size; for (;;) { i_assert(in_offset <= in_limit); i_stream_seek(instream, in_offset); read_size = in_limit - in_offset; (void)i_stream_read_data(instream, &data, &size, read_size-1); if (size >= read_size) { size = read_size; if (instream->mmaped) { /* we'll have to write it through buffer or the file gets corrupted */ i_assert(size <= foutstream->buffer_size); memcpy(foutstream->buffer, data, size); data = foutstream->buffer; } break; } /* buffer too large probably, try with smaller */ read_size -= size; in_offset += read_size; out_offset += read_size; buffer_size -= read_size; } in_limit -= size; ret = pwrite_full(foutstream->fd, data, size, out_offset); if (ret < 0) { /* error */ outstream->ostream.stream_errno = errno; return -1; } i_stream_skip(instream, size); } outstream->ostream.offset += in_size - in_start_offset; return (off_t) (in_size - in_start_offset); } static off_t io_stream_copy_stream(struct ostream_private *outstream, struct istream *instream, bool same_stream) { uoff_t in_size; off_t in_abs_offset, ret = 0; if (same_stream) { /* copying data within same fd. we'll have to be careful with seeks and overlapping writes. */ if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0) { outstream->ostream.stream_errno = instream->stream_errno; return -1; } /* if we couldn't find out the size, it means that instream isn't a regular file_istream. we can be reasonably sure that we can copy it safely the regular way. (there's really no other possibility, other than failing completely.) */ } if (ret > 0) { i_assert(instream->v_offset <= in_size); in_abs_offset = instream->real_stream->abs_start_offset + instream->v_offset; ret = (off_t)outstream->ostream.offset - in_abs_offset; if (ret == 0) { /* copying data over itself. we don't really need to do that, just fake it. */ return in_size - instream->v_offset; } if (ret > 0 && in_size > (uoff_t)ret) { /* overlapping */ i_assert(instream->seekable); return io_stream_copy_backwards(outstream, instream, in_size); } } return io_stream_copy(&outstream->ostream, instream); } static off_t o_stream_file_send_istream(struct ostream_private *outstream, struct istream *instream) { struct file_ostream *foutstream = (struct file_ostream *)outstream; bool same_stream; int in_fd; off_t ret; bool sendfile_not_supported; in_fd = !instream->readable_fd ? -1 : i_stream_get_fd(instream); if (!foutstream->no_sendfile && in_fd != -1 && in_fd != foutstream->fd && instream->seekable) { ret = io_stream_sendfile(outstream, instream, in_fd, &sendfile_not_supported); if (ret >= 0 || !sendfile_not_supported) return ret; /* sendfile() not supported (with this fd), fallback to regular sending. */ foutstream->no_sendfile = TRUE; } same_stream = i_stream_get_fd(instream) == foutstream->fd && foutstream->fd != -1; return io_stream_copy_stream(outstream, instream, same_stream); } static void o_stream_file_switch_ioloop(struct ostream_private *stream) { struct file_ostream *fstream = (struct file_ostream *)stream; if (fstream->io != NULL) fstream->io = io_loop_move_io(&fstream->io); } struct ostream * o_stream_create_file_common(struct file_ostream *fstream, int fd, size_t max_buffer_size, bool autoclose_fd) { struct ostream *ostream; fstream->fd = fd; fstream->autoclose_fd = autoclose_fd; fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE; fstream->ostream.iostream.close = o_stream_file_close; fstream->ostream.iostream.destroy = o_stream_file_destroy; fstream->ostream.cork = o_stream_file_cork; fstream->ostream.flush = o_stream_file_flush; fstream->ostream.flush_pending = o_stream_file_flush_pending; fstream->ostream.get_used_size = o_stream_file_get_used_size; fstream->ostream.seek = o_stream_file_seek; fstream->ostream.sendv = o_stream_file_sendv; fstream->ostream.write_at = o_stream_file_write_at; fstream->ostream.send_istream = o_stream_file_send_istream; fstream->ostream.switch_ioloop = o_stream_file_switch_ioloop; fstream->writev = o_stream_file_writev; fstream->ostream.max_buffer_size = max_buffer_size; ostream = o_stream_create(&fstream->ostream, NULL, fd); if (max_buffer_size == 0) fstream->ostream.max_buffer_size = fstream->optimal_block_size; return ostream; } static void fstream_init_file(struct file_ostream *fstream) { struct stat st; fstream->no_sendfile = TRUE; if (fstat(fstream->fd, &st) < 0) return; if ((uoff_t)st.st_blksize > fstream->optimal_block_size) { /* use the optimal block size, but with a reasonable limit */ fstream->optimal_block_size = I_MIN(st.st_blksize, MAX_OPTIMAL_BLOCK_SIZE); } if (S_ISREG(st.st_mode)) { fstream->no_socket_cork = TRUE; fstream->file = TRUE; } } struct ostream * o_stream_create_fd(int fd, size_t max_buffer_size, bool autoclose_fd) { struct file_ostream *fstream; struct ostream *ostream; off_t offset; fstream = i_new(struct file_ostream, 1); ostream = o_stream_create_file_common (fstream, fd, max_buffer_size, autoclose_fd); offset = lseek(fd, 0, SEEK_CUR); if (offset >= 0) { ostream->offset = offset; fstream->real_offset = offset; fstream->buffer_offset = offset; fstream_init_file(fstream); } else { if (net_getsockname(fd, NULL, NULL) < 0) { fstream->no_sendfile = TRUE; fstream->no_socket_cork = TRUE; } } return ostream; } struct ostream * o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) { struct ostream *output; output = o_stream_create_fd(*fd, max_buffer_size, TRUE); *fd = -1; return output; } struct ostream * o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd) { struct file_ostream *fstream; struct ostream *ostream; if (offset == (uoff_t)-1) offset = lseek(fd, 0, SEEK_CUR); fstream = i_new(struct file_ostream, 1); ostream = o_stream_create_file_common(fstream, fd, 0, autoclose_fd); fstream_init_file(fstream); fstream->real_offset = offset; fstream->buffer_offset = offset; ostream->blocking = fstream->file; ostream->offset = offset; return ostream; } struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset) { struct ostream *output; output = o_stream_create_fd_file(*fd, offset, TRUE); *fd = -1; return output; } dovecot-2.2.33.2/src/lib/test-istream-tee.c0000644000175000017500000001015513165463624015236 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-tee.h" #define TEST_BUF_SIZE I_STREAM_MIN_SIZE #define TEST_STR_LEN (TEST_BUF_SIZE*3) #define CHILD_COUNT 5 static void test_istream_tee_tailing(const char *str) { struct istream *test_input, *child_input[CHILD_COUNT]; struct tee_istream *tee; unsigned int i, len, delta; test_input = test_istream_create(str); test_istream_set_max_buffer_size(test_input, TEST_BUF_SIZE); test_begin("istream tee tailing"); tee = tee_i_stream_create(test_input); for (i = 0; i < CHILD_COUNT; i++) child_input[i] = tee_i_stream_create_child(tee); test_istream_set_allow_eof(test_input, FALSE); delta = 1; for (len = 1; len < TEST_BUF_SIZE; len += delta) { test_istream_set_size(test_input, len); for (i = 0; i < CHILD_COUNT; i++) { test_assert_idx(i_stream_read(child_input[i]) == (int)delta, len); test_assert_idx(!tee_i_stream_child_is_waiting(child_input[i]), len); test_assert_idx(i_stream_read(child_input[i]) == 0, len); test_assert_idx(!tee_i_stream_child_is_waiting(child_input[i]), len); } delta = rand() % 32; /* may stand still */ if(delta > TEST_BUF_SIZE - len) delta = 1; } test_istream_set_size(test_input, len); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == (int)delta); test_assert(i_stream_read(child_input[i]) == -2); test_assert(!tee_i_stream_child_is_waiting(child_input[i])); } delta = 1; while ((len += delta) <= TEST_STR_LEN) { unsigned int lagger = rand() % CHILD_COUNT; test_istream_set_size(test_input, len); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == -2); test_assert(!tee_i_stream_child_is_waiting(child_input[i])); } for (i = 0; i < CHILD_COUNT; i++) { if (i == lagger) continue; i_stream_skip(child_input[i], delta); test_assert(i_stream_read(child_input[i]) == 0); test_assert(tee_i_stream_child_is_waiting(child_input[i])); } i_stream_skip(child_input[lagger], delta); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == (int)delta); test_assert(i_stream_read(child_input[i]) == -2); test_assert(!tee_i_stream_child_is_waiting(child_input[i])); } delta = rand() % 31 + 1; /* mustn't stand still */ if(delta > TEST_STR_LEN - len) delta = 1; } for (i = 0; i < CHILD_COUNT-1; i++) { i_stream_skip(child_input[i], 1); test_assert(i_stream_read(child_input[i]) == 0); test_assert(tee_i_stream_child_is_waiting(child_input[i])); } i_stream_skip(child_input[i], 1); test_assert(i_stream_read(child_input[i]) == 0); test_assert(!tee_i_stream_child_is_waiting(child_input[i])); test_istream_set_allow_eof(test_input, TRUE); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == -1); i_stream_unref(&child_input[i]); } i_stream_unref(&test_input); test_end(); } static void test_istream_tee_blocks(const char *str) { struct istream *test_input, *child_input[CHILD_COUNT]; struct tee_istream *tee; unsigned int i, j; test_input = test_istream_create(str); test_istream_set_max_buffer_size(test_input, TEST_BUF_SIZE); test_begin("istream tee blocks"); tee = tee_i_stream_create(test_input); for (i = 0; i < CHILD_COUNT; i++) child_input[i] = tee_i_stream_create_child(tee); test_istream_set_allow_eof(test_input, FALSE); for (j = 1; j <= 3; j++) { test_istream_set_size(test_input, TEST_BUF_SIZE*j); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == TEST_BUF_SIZE); i_stream_skip(child_input[i], TEST_BUF_SIZE); } } test_istream_set_allow_eof(test_input, TRUE); for (i = 0; i < CHILD_COUNT; i++) { test_assert(i_stream_read(child_input[i]) == -1); i_stream_unref(&child_input[i]); } i_stream_unref(&test_input); test_end(); } void test_istream_tee(void) { string_t *str; unsigned int i; str = str_new(default_pool, TEST_STR_LEN); for (i = 0; i < TEST_STR_LEN; i++) str_append_c(str, 'a' + i%26); test_istream_tee_tailing(str_c(str)); test_istream_tee_blocks(str_c(str)); str_free(&str); } dovecot-2.2.33.2/src/lib/file-create-locked.h0000644000175000017500000000303113165463543015461 00000000000000#ifndef FILE_CREATE_LOCKED_H #define FILE_CREATE_LOCKED_H #include "file-lock.h" struct file_create_settings { /* 0 = try locking without waiting */ unsigned int lock_timeout_secs; enum file_lock_method lock_method; /* 0 = 0600 */ int mode; /* 0 = default */ uid_t uid; /* 0 = default */ gid_t gid; const char *gid_origin; /* If parent directory doesn't exist, mkdir() it with this mode. 0 = don't mkdir(). The parent directories are assumed to be potentially rmdir() simultaneously, so the mkdir()+locking may be attempted multiple times. */ int mkdir_mode; /* 0 = default */ uid_t mkdir_uid; /* 0 = default */ gid_t mkdir_gid; const char *mkdir_gid_origin; }; /* Either open an existing file and lock it, or create the file locked. The creation is done by creating a temp file and link()ing it to path. If link() fails, opening is retried again. Returns fd on success, -1 on error. errno is preserved for the last failed syscall, so most importantly ENOENT could mean that the directory doesn't exist and EAGAIN means locking timed out. If this function is used to create lock files, file_lock_set_unlink_on_free() should be used for the resulting lock. It attempts to avoid unlinking the file if there are already other processes using the lock. That can help to avoid "Creating a locked file ... keeps failing" errors */ int file_create_locked(const char *path, const struct file_create_settings *set, struct file_lock **lock_r, bool *created_r, const char **error_r); #endif dovecot-2.2.33.2/src/lib/ioloop-iolist.h0000644000175000017500000000055313123174404014637 00000000000000#ifndef IOLOOP_IOLIST_H #define IOLOOP_IOLIST_H enum { IOLOOP_IOLIST_INPUT, IOLOOP_IOLIST_OUTPUT, IOLOOP_IOLIST_ERROR, IOLOOP_IOLIST_IOS_PER_FD }; struct io_list { struct io_file *ios[IOLOOP_IOLIST_IOS_PER_FD]; }; bool ioloop_iolist_add(struct io_list *list, struct io_file *io); bool ioloop_iolist_del(struct io_list *list, struct io_file *io); #endif dovecot-2.2.33.2/src/lib/str-table.h0000644000175000017500000000123113123174404013724 00000000000000#ifndef STR_TABLE_H #define STR_TABLE_H /* Hash table containing string -> refcount. */ struct str_table *str_table_init(void); void str_table_deinit(struct str_table **table); /* Returns TRUE if there are no referenced strings in the table. */ bool str_table_is_empty(struct str_table *table); /* Return string allocated from the strtable and increase its reference count. */ const char *str_table_ref(struct str_table *table, const char *str); /* Decrease string's reference count, freeing it if it reaches zero. The str pointer must have been returned by the str_table_ref(). */ void str_table_unref(struct str_table *table, const char **str); #endif dovecot-2.2.33.2/src/lib/home-expand.c0000644000175000017500000000234513123174404014236 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ipwd.h" #include "home-expand.h" int home_try_expand(const char **_path) { const char *path = *_path; const char *name, *home, *p; struct passwd pw; if (path == NULL || *path != '~') return 0; path++; if (*path == '/' || *path == '\0') { home = getenv("HOME"); if (*path != '\0') path++; } else { p = strchr(path, '/'); if (p == NULL) { name = path; path = ""; } else { name = t_strdup_until(path, p); path = p+1; } switch (i_getpwnam(name, &pw)) { case -1: i_error("getpwnam(%s) failed: %m", name); home = NULL; break; case 0: home = NULL; break; default: home = pw.pw_dir; break; } } if (home == NULL) return -1; if (*path == '\0') *_path = t_strdup(home); else *_path = t_strconcat(home, "/", path, NULL); return 0; } const char *home_expand(const char *path) { (void)home_try_expand(&path); return path; } const char *home_expand_tilde(const char *path, const char *home) { if (path == NULL || *path != '~') return path; if (path[1] == '\0') return home; if (path[1] != '/') return path; /* ~/ used */ return t_strconcat(home, path + 1, NULL); } dovecot-2.2.33.2/src/lib/ostream-failure-at.c0000644000175000017500000000673013123174404015534 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "ostream-private.h" #include "ostream-failure-at.h" struct failure_at_ostream { struct ostream_private ostream; char *error_string; uoff_t failure_offset; bool failed; }; static void o_stream_failure_at_destroy(struct iostream_private *stream) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; i_free(fstream->error_string); o_stream_unref(&fstream->ostream.parent); } static ssize_t o_stream_failure_at_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; unsigned int i; struct const_iovec *iov_dup; unsigned int iov_dup_count; uoff_t bytes_until_failure, blocking_bytes_count = 0; ssize_t ret; if (stream->ostream.blocking) { /* blocking ostream must return either a full success or a failure. if the current write would go past failure_offset, return a failure now before writing anything. */ for (i = 0; i < iov_count; i++) blocking_bytes_count += iov[i].iov_len; if (blocking_bytes_count > 0) { /* if we're exactly at the failure offset after this write, fail it only on the next write. */ blocking_bytes_count--; } } if (fstream->failure_offset <= stream->ostream.offset + blocking_bytes_count) { io_stream_set_error(&stream->iostream, "%s", fstream->error_string); stream->ostream.stream_errno = errno = EIO; fstream->failed = TRUE; return -1; } bytes_until_failure = fstream->failure_offset - stream->ostream.offset; iov_dup = i_new(struct const_iovec, iov_count); iov_dup_count = iov_count; for (i = 0; i < iov_count; i++) { iov_dup[i] = iov[i]; if (iov_dup[i].iov_len >= bytes_until_failure) { iov_dup[i].iov_len = bytes_until_failure; iov_dup_count = i+1; break; } } ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count); i_free(iov_dup); if (ret < 0) { o_stream_copy_error_from_parent(stream); return -1; } stream->ostream.offset += ret; return ret; } static int o_stream_failure_at_flush(struct ostream_private *stream) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; if (fstream->failed) { io_stream_set_error(&stream->iostream, "%s", fstream->error_string); stream->ostream.stream_errno = errno = EIO; return -1; } return o_stream_flush(stream->parent); } struct ostream * o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, const char *error_string) { struct failure_at_ostream *fstream; fstream = i_new(struct failure_at_ostream, 1); fstream->ostream.sendv = o_stream_failure_at_sendv; fstream->ostream.flush = o_stream_failure_at_flush; fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; fstream->failure_offset = failure_offset; fstream->error_string = i_strdup(error_string); return o_stream_create(&fstream->ostream, output, o_stream_get_fd(output)); } struct ostream * o_stream_create_failure_at_flush(struct ostream *output, const char *error_string) { struct failure_at_ostream *fstream; fstream = i_new(struct failure_at_ostream, 1); fstream->ostream.flush = o_stream_failure_at_flush; fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; fstream->error_string = i_strdup(error_string); fstream->failed = TRUE; return o_stream_create(&fstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib/ioloop.h0000644000175000017500000002253513165463543013355 00000000000000#ifndef IOLOOP_H #define IOLOOP_H #include #include struct io; struct timeout; struct ioloop; struct istream; enum io_condition { IO_READ = 0x01, IO_WRITE = 0x02, /* IO_ERROR can be used to check when writable pipe's reader side closes the pipe. For other uses IO_READ should work just as well. */ IO_ERROR = 0x04, /* internal */ IO_NOTIFY = 0x08 }; enum io_notify_result { /* Notify added successfully */ IO_NOTIFY_ADDED, /* Specified file doesn't exist, can't wait on it */ IO_NOTIFY_NOTFOUND, /* Can't add notify for specified file. Main reasons for this: a) No notify support at all, b) Only directory notifies supported */ IO_NOTIFY_NOSUPPORT }; typedef void io_callback_t(void *context); typedef void timeout_callback_t(void *context); typedef void io_loop_time_moved_callback_t(time_t old_time, time_t new_time); typedef void io_switch_callback_t(struct ioloop *prev_ioloop); /* Time when the I/O loop started calling handlers. Can be used instead of time(NULL). */ extern time_t ioloop_time; extern struct timeval ioloop_timeval; extern struct ioloop *current_ioloop; /* Number of microseconds spent on all the ioloops waiting for themselves. */ extern uint64_t ioloop_global_wait_usecs; /* You can create different handlers for IO_READ and IO_WRITE. IO_READ and IO_ERROR can't use different handlers (and there's no point anyway). Don't try to add multiple handlers for the same type. It's not checked and the behavior will be undefined. */ struct io *io_add(int fd, enum io_condition condition, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context) ATTR_NULL(5); #define io_add(fd, condition, callback, context) \ io_add(fd, condition, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (io_callback_t *)callback, context) enum io_notify_result io_add_notify(const char *path, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context, struct io **io_r) ATTR_NULL(3); #define io_add_notify(path, callback, context, io_r) \ io_add_notify(path, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (io_callback_t *)callback, context, io_r) struct io *io_add_istream(struct istream *input, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context) ATTR_NULL(3); #define io_add_istream(input, callback, context) \ io_add_istream(input, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (io_callback_t *)callback, context) /* Remove I/O handler, and set io pointer to NULL. */ void io_remove(struct io **io); /* Like io_remove(), but assume that the file descriptor is already closed. With some backends this simply frees the memory. */ void io_remove_closed(struct io **io); /* Make sure the I/O callback is called by io_loop_run() even if there isn't any input actually pending currently as seen by the OS. This may be useful if some of the input has already read into some internal buffer and the caller wants to handle it the same way as if the fd itself had input. */ void io_set_pending(struct io *io); /* Timeout handlers */ struct timeout * timeout_add(unsigned int msecs, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) ATTR_NULL(4); #define timeout_add(msecs, callback, context) \ timeout_add(msecs, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))) + \ COMPILE_ERROR_IF_TRUE(__builtin_constant_p(msecs) && \ ((msecs) > 0 && (msecs) < 1000)), \ (io_callback_t *)callback, context) struct timeout * timeout_add_short(unsigned int msecs, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) ATTR_NULL(4); #define timeout_add_short(msecs, callback, context) \ timeout_add_short(msecs, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (io_callback_t *)callback, context) struct timeout *timeout_add_absolute(const struct timeval *time, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) ATTR_NULL(4); #define timeout_add_absolute(time, callback, context) \ timeout_add_absolute(time, __FILE__, __LINE__ + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (io_callback_t *)callback, context) /* Remove timeout handler, and set timeout pointer to NULL. */ void timeout_remove(struct timeout **timeout); /* Reset timeout so it's next run after now+msecs. */ void timeout_reset(struct timeout *timeout); /* Refresh ioloop_time and ioloop_timeval variables. */ void io_loop_time_refresh(void); void io_loop_run(struct ioloop *ioloop); void io_loop_stop(struct ioloop *ioloop); /* safe to run in signal handler */ bool io_loop_is_running(struct ioloop *ioloop); /* call these if you wish to run the iteration only once */ void io_loop_set_running(struct ioloop *ioloop); void io_loop_handler_run(struct ioloop *ioloop); struct ioloop *io_loop_create(void); /* Specify the maximum number of fds we're expecting to use. */ void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds); /* Destroy I/O loop and set ioloop pointer to NULL. */ void io_loop_destroy(struct ioloop **ioloop); /* If time moves backwards or jumps forwards call the callback. */ void io_loop_set_time_moved_callback(struct ioloop *ioloop, io_loop_time_moved_callback_t *callback); /* Change the current_ioloop. */ void io_loop_set_current(struct ioloop *ioloop); /* Call the callback whenever ioloop is changed. */ void io_loop_add_switch_callback(io_switch_callback_t *callback); void io_loop_remove_switch_callback(io_switch_callback_t *callback); /* This context is used for all further I/O and timeout callbacks that are added until returning to ioloop. When a callback is called, this context is again activated. */ struct ioloop_context *io_loop_context_new(struct ioloop *ioloop); void io_loop_context_ref(struct ioloop_context *ctx); void io_loop_context_unref(struct ioloop_context **ctx); /* Call the activate callback when this context is activated (I/O callback is about to be called), and the deactivate callback when the context is deactivated (I/O callback has returned). You can add multiple callbacks. */ void io_loop_context_add_callbacks(struct ioloop_context *ctx, io_callback_t *activate, io_callback_t *deactivate, void *context); #define io_loop_context_add_callbacks(ctx, activate, deactivate, context) \ io_loop_context_add_callbacks(ctx, 1 ? (io_callback_t *)activate : \ CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) + \ CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ (io_callback_t *)deactivate, context) /* Remove callbacks with the given callbacks and context. */ void io_loop_context_remove_callbacks(struct ioloop_context *ctx, io_callback_t *activate, io_callback_t *deactivate, void *context); #define io_loop_context_remove_callbacks(ctx, activate, deactivate, context) \ io_loop_context_remove_callbacks(ctx, 1 ? (io_callback_t *)activate : \ CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) + \ CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ (io_callback_t *)deactivate, context) /* Returns the current context set to ioloop. */ struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop); /* Returns fd, which contains all of the ioloop's current notifications. When it becomes readable, there is a new notification. Calling this function stops the existing notifications in the ioloop from working anymore. This function's main idea is that the fd can be passed to another process, which can use it to find out if an interesting notification happens. Returns fd on success, -1 on error. */ int io_loop_extract_notify_fd(struct ioloop *ioloop); /* IO wait timers can be used to track how much time the io_wait_timer has spent on waiting in its ioloops. This is similar to io_loop_get_wait_usecs(), but it's easier to use when the wait time needs to be tracked across multiple ioloops. */ struct io_wait_timer * io_wait_timer_add(const char *source_filename, unsigned int source_linenum); #define io_wait_timer_add() \ io_wait_timer_add(__FILE__, __LINE__) struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **timer); void io_wait_timer_remove(struct io_wait_timer **timer); uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer); /* Move the given I/O into the current I/O loop if it's not already there. New I/O is returned, while the old one is freed. */ struct io *io_loop_move_io(struct io **io); /* Like io_loop_move_io(), but for timeouts. */ struct timeout *io_loop_move_timeout(struct timeout **timeout); /* Returns TRUE if any IOs have been added to the ioloop. */ bool io_loop_have_ios(struct ioloop *ioloop); /* Returns TRUE if there is a pending timeout that is going to be run immediately. */ bool io_loop_have_immediate_timeouts(struct ioloop *ioloop); /* Returns number of microseconds spent on the ioloop waiting itself. */ uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop); /* Return all io conditions added for the given fd. This needs to scan through all the file ios in the ioloop. */ enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd); #endif dovecot-2.2.33.2/src/lib/ostream-failure-at.h0000644000175000017500000000044413123174404015535 00000000000000#ifndef OSTREAM_FAILURE_AT_H #define OSTREAM_FAILURE_AT_H struct ostream * o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, const char *error_string); struct ostream * o_stream_create_failure_at_flush(struct ostream *output, const char *error_string); #endif dovecot-2.2.33.2/src/lib/rand.c0000644000175000017500000000116613165463624012770 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ /* Wrap srand() so that we can reproduce fuzzed tests */ #include "lib.h" static int seeded = 0; static unsigned int seed; static char const *env_seed; int rand_get_seed_count(void) { return seeded; } unsigned int rand_get_last_seed(void) { i_assert(seeded > 0); return seed; } void rand_set_seed(unsigned int s) { if (seeded == 0) { unsigned int seedval; env_seed = getenv("DOVECOT_SRAND"); if (env_seed != NULL && str_to_uint(env_seed, &seedval) >= 0) seed = seedval; } seeded++; if (env_seed == NULL) seed = s; srand(seed); } dovecot-2.2.33.2/src/lib/istream-private.h0000644000175000017500000000502213165463624015160 00000000000000#ifndef ISTREAM_PRIVATE_H #define ISTREAM_PRIVATE_H #include "istream.h" #include "iostream-private.h" #define I_STREAM_MIN_SIZE IO_BLOCK_SIZE struct io; struct istream_private { /* inheritance: */ struct iostream_private iostream; /* methods: */ ssize_t (*read)(struct istream_private *stream); void (*seek)(struct istream_private *stream, uoff_t v_offset, bool mark); void (*sync)(struct istream_private *stream); int (*stat)(struct istream_private *stream, bool exact); int (*get_size)(struct istream_private *stream, bool exact, uoff_t *size_r); void (*switch_ioloop)(struct istream_private *stream); /* data: */ struct istream istream; int fd; uoff_t abs_start_offset; struct stat statbuf; /* added by io_add_istream() -> i_stream_set_io() */ struct io *io; const unsigned char *buffer; unsigned char *w_buffer; /* may be NULL */ size_t buffer_size, max_buffer_size, init_buffer_size; size_t skip, pos, try_alloc_limit; struct istream *parent; /* for filter streams */ uoff_t parent_start_offset; /* parent stream's expected offset is kept here. i_stream_read() always seeks parent stream to here before calling read(). */ uoff_t parent_expected_offset; /* increased every time the stream is changed (e.g. seek, read). this way streams can check if their parent streams have been accessed behind them. */ unsigned int access_counter; string_t *line_str; /* for i_stream_next_line() if w_buffer == NULL */ unsigned int line_crlf:1; unsigned int return_nolf_line:1; unsigned int stream_size_passthrough:1; /* stream is parent's size */ unsigned int nonpersistent_buffers:1; }; struct istream * ATTR_NOWARN_UNUSED_RESULT i_stream_create(struct istream_private *stream, struct istream *parent, int fd) ATTR_NULL(2); /* Initialize parent lazily after i_stream_create() has already been called. */ void i_stream_init_parent(struct istream_private *_stream, struct istream *parent); void i_stream_compress(struct istream_private *stream); void i_stream_grow_buffer(struct istream_private *stream, size_t bytes); bool ATTR_NOWARN_UNUSED_RESULT i_stream_try_alloc(struct istream_private *stream, size_t wanted_size, size_t *size_r); void *i_stream_alloc(struct istream_private *stream, size_t size); ssize_t i_stream_read_copy_from_parent(struct istream *istream); void i_stream_default_seek_nonseekable(struct istream_private *stream, uoff_t v_offset, bool mark); void i_stream_set_io(struct istream *stream, struct io *io); void i_stream_unset_io(struct istream *stream, struct io *io); #endif dovecot-2.2.33.2/src/lib/mkdir-parents.c0000644000175000017500000001003013123174404014577 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "eacces-error.h" #include "mkdir-parents.h" #include "ipwd.h" #include #include #include static int ATTR_NULL(5) mkdir_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin) { string_t *str; mode_t old_mask; unsigned int i; int ret, fd = -1, orig_errno; for (i = 0;; i++) { old_mask = umask(0); ret = mkdir(path, mode); umask(old_mask); if (ret < 0) break; fd = open(path, O_RDONLY); if (fd != -1) break; if (errno != ENOENT || i == 3) { i_error("open(%s) failed: %m", path); return -1; } /* it was just rmdir()ed by someone else? retry */ } if (ret < 0) { if (errno == EISDIR || errno == ENOSYS) { /* EISDIR check is for BSD/OS which returns it if path contains '/' at the end and it exists. ENOSYS check is for NFS mount points. */ errno = EEXIST; } i_assert(fd == -1); return -1; } if (fchown(fd, uid, gid) < 0) { i_close_fd(&fd); orig_errno = errno; if (rmdir(path) < 0 && errno != ENOENT) i_error("rmdir(%s) failed: %m", path); errno = orig_errno; if (errno == EPERM && uid == (uid_t)-1) { i_error("%s", eperm_error_get_chgrp("fchown", path, gid, gid_origin)); return -1; } str = t_str_new(256); str_printfa(str, "fchown(%s, %ld", path, uid == (uid_t)-1 ? -1L : (long)uid); if (uid != (uid_t)-1) { struct passwd pw; if (i_getpwuid(uid, &pw) > 0) str_printfa(str, "(%s)", pw.pw_name); } str_printfa(str, ", %ld", gid == (gid_t)-1 ? -1L : (long)gid); if (gid != (gid_t)-1) { struct group gr; if (i_getgrgid(uid, &gr) > 0) str_printfa(str, "(%s)", gr.gr_name); } errno = orig_errno; i_error("%s) failed: %m", str_c(str)); return -1; } if (gid != (gid_t)-1 && (mode & S_ISGID) == 0) { /* make sure the directory doesn't have setgid bit enabled (in case its parent had) */ if (fchmod(fd, mode) < 0) { orig_errno = errno; if (rmdir(path) < 0 && errno != ENOENT) i_error("rmdir(%s) failed: %m", path); errno = orig_errno; i_error("fchmod(%s) failed: %m", path); i_close_fd(&fd); return -1; } } i_close_fd(&fd); return 0; } int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return mkdir_chown_full(path, mode, uid, gid, NULL); } int mkdir_chgrp(const char *path, mode_t mode, gid_t gid, const char *gid_origin) { return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin); } static int ATTR_NULL(5) mkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin) { const char *p; int ret; if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) { if (errno != ENOENT) return -1; /* doesn't exist, try recursively creating our parent dir */ p = strrchr(path, '/'); if (p == NULL || p == path) return -1; /* shouldn't happen */ T_BEGIN { ret = mkdir_parents_chown_full(t_strdup_until(path, p), mode, uid, gid, gid_origin); } T_END; if (ret < 0 && errno != EEXIST) return -1; /* should work now */ if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) return -1; } return 0; } int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return mkdir_parents_chown_full(path, mode, uid, gid, NULL); } int mkdir_parents_chgrp(const char *path, mode_t mode, gid_t gid, const char *gid_origin) { return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin); } int mkdir_parents(const char *path, mode_t mode) { return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1); } int stat_first_parent(const char *path, const char **root_dir_r, struct stat *st_r) { const char *p; while (stat(path, st_r) < 0) { if (errno != ENOENT || strcmp(path, "/") == 0) { *root_dir_r = path; return -1; } p = strrchr(path, '/'); if (p == NULL) path = "/"; else path = t_strdup_until(path, p); } *root_dir_r = path; return 0; } dovecot-2.2.33.2/src/lib/sendfile-util.h0000644000175000017500000000051613123174404014600 00000000000000#ifndef SENDFILE_UTIL_H #define SENDFILE_UTIL_H /* Wrapper for various sendfile()-like calls. Returns -1 and errno=EINVAL if it isn't supported for some reason (out_fd isn't a socket, offset is too large, or there simply is no sendfile()). */ ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count); #endif dovecot-2.2.33.2/src/lib/hmac-cram-md5.h0000644000175000017500000000052113123174404014343 00000000000000#ifndef HMAC_CRAM_MD5_H #define HMAC_CRAM_MD5_H #include "hmac.h" #define CRAM_MD5_CONTEXTLEN 32 void hmac_md5_get_cram_context(struct hmac_context *ctx, unsigned char context_digest[CRAM_MD5_CONTEXTLEN]); void hmac_md5_set_cram_context(struct hmac_context *ctx, const unsigned char context_digest[CRAM_MD5_CONTEXTLEN]); #endif dovecot-2.2.33.2/src/lib/Makefile.in0000644000175000017500000055410613172375574013760 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) liblib_la_LIBADD = am_liblib_la_OBJECTS = abspath.lo array.lo aqueue.lo askpass.lo \ backtrace-string.lo base32.lo base64.lo bits.lo \ bsearch-insert-pos.lo buffer.lo child-wait.lo compat.lo \ connection.lo crc32.lo data-stack.lo eacces-error.lo \ env-util.lo execv-const.lo failures.lo fd-close-on-exec.lo \ fd-set-nonblock.lo fdatasync-path.lo fdpass.lo file-cache.lo \ file-create-locked.lo file-copy.lo file-dotlock.lo \ file-lock.lo file-set-size.lo guid.lo hash.lo hash-format.lo \ hash-method.lo hash2.lo hex-binary.lo hex-dec.lo hmac.lo \ hmac-cram-md5.lo home-expand.lo hook-build.lo hostpid.lo \ imem.lo ipwd.lo iostream.lo iostream-rawlog.lo \ iostream-temp.lo iso8601-date.lo istream.lo \ istream-base64-decoder.lo istream-base64-encoder.lo \ istream-callback.lo istream-chain.lo istream-concat.lo \ istream-crlf.lo istream-data.lo istream-failure-at.lo \ istream-file.lo istream-hash.lo istream-jsonstr.lo \ istream-limit.lo istream-mmap.lo istream-multiplex.lo \ istream-rawlog.lo istream-seekable.lo istream-sized.lo \ istream-tee.lo istream-timeout.lo istream-unix.lo ioloop.lo \ ioloop-iolist.lo ioloop-notify-none.lo ioloop-notify-fd.lo \ ioloop-notify-inotify.lo ioloop-notify-kqueue.lo \ ioloop-poll.lo ioloop-select.lo ioloop-epoll.lo \ ioloop-kqueue.lo json-parser.lo json-tree.lo lib.lo \ lib-signals.lo log-throttle.lo md4.lo md5.lo mempool.lo \ mempool-alloconly.lo mempool-datastack.lo mempool-system.lo \ mempool-unsafe-datastack.lo mkdir-parents.lo mmap-anon.lo \ mmap-util.lo module-dir.lo mountpoint.lo net.lo \ nfs-workarounds.lo numpack.lo ostream.lo ostream-buffer.lo \ ostream-escaped.lo ostream-failure-at.lo ostream-file.lo \ ostream-hash.lo ostream-multiplex.lo ostream-null.lo \ ostream-rawlog.lo ostream-unix.lo pkcs5.lo primes.lo \ printf-format-fix.lo process-title.lo priorityq.lo randgen.lo \ rand.lo read-full.lo restrict-access.lo \ restrict-process-size.lo safe-memset.lo safe-mkdir.lo \ safe-mkstemp.lo sendfile-util.lo seq-range-array.lo sha1.lo \ sha2.lo sha3.lo str.lo str-find.lo str-sanitize.lo \ str-table.lo strescape.lo strfuncs.lo strnum.lo time-util.lo \ timing.lo unix-socket-create.lo unlink-directory.lo \ unlink-old-files.lo unichar.lo uri-util.lo utc-offset.lo \ utc-mktime.lo var-expand.lo var-expand-if.lo wildcard-match.lo \ write-full.lo liblib_la_OBJECTS = $(am_liblib_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-lib$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_lib_OBJECTS = test_lib-test-lib.$(OBJEXT) \ test_lib-test-array.$(OBJEXT) test_lib-test-aqueue.$(OBJEXT) \ test_lib-test-base32.$(OBJEXT) test_lib-test-base64.$(OBJEXT) \ test_lib-test-bits.$(OBJEXT) \ test_lib-test-bsearch-insert-pos.$(OBJEXT) \ test_lib-test-buffer.$(OBJEXT) \ test_lib-test-byteorder.$(OBJEXT) \ test_lib-test-crc32.$(OBJEXT) \ test_lib-test-data-stack.$(OBJEXT) \ test_lib-test-failures.$(OBJEXT) \ test_lib-test-file-create-locked.$(OBJEXT) \ test_lib-test-guid.$(OBJEXT) test_lib-test-hash.$(OBJEXT) \ test_lib-test-hash-format.$(OBJEXT) \ test_lib-test-hash-method.$(OBJEXT) \ test_lib-test-hex-binary.$(OBJEXT) \ test_lib-test-imem.$(OBJEXT) test_lib-test-ioloop.$(OBJEXT) \ test_lib-test-iso8601-date.$(OBJEXT) \ test_lib-test-iostream-temp.$(OBJEXT) \ test_lib-test-istream.$(OBJEXT) \ test_lib-test-istream-base64-decoder.$(OBJEXT) \ test_lib-test-istream-base64-encoder.$(OBJEXT) \ test_lib-test-istream-chain.$(OBJEXT) \ test_lib-test-istream-concat.$(OBJEXT) \ test_lib-test-istream-crlf.$(OBJEXT) \ test_lib-test-istream-failure-at.$(OBJEXT) \ test_lib-test-istream-multiplex.$(OBJEXT) \ test_lib-test-istream-seekable.$(OBJEXT) \ test_lib-test-istream-tee.$(OBJEXT) \ test_lib-test-istream-unix.$(OBJEXT) \ test_lib-test-json-parser.$(OBJEXT) \ test_lib-test-json-tree.$(OBJEXT) \ test_lib-test-llist.$(OBJEXT) \ test_lib-test-log-throttle.$(OBJEXT) \ test_lib-test-malloc-overflow.$(OBJEXT) \ test_lib-test-mempool.$(OBJEXT) \ test_lib-test-mempool-alloconly.$(OBJEXT) \ test_lib-test-pkcs5.$(OBJEXT) test_lib-test-net.$(OBJEXT) \ test_lib-test-numpack.$(OBJEXT) \ test_lib-test-ostream-buffer.$(OBJEXT) \ test_lib-test-ostream-escaped.$(OBJEXT) \ test_lib-test-ostream-failure-at.$(OBJEXT) \ test_lib-test-ostream-file.$(OBJEXT) \ test_lib-test-ostream-multiplex.$(OBJEXT) \ test_lib-test-multiplex.$(OBJEXT) \ test_lib-test-primes.$(OBJEXT) \ test_lib-test-printf-format-fix.$(OBJEXT) \ test_lib-test-priorityq.$(OBJEXT) \ test_lib-test-seq-range-array.$(OBJEXT) \ test_lib-test-str.$(OBJEXT) test_lib-test-strescape.$(OBJEXT) \ test_lib-test-strfuncs.$(OBJEXT) \ test_lib-test-strnum.$(OBJEXT) \ test_lib-test-str-find.$(OBJEXT) \ test_lib-test-str-sanitize.$(OBJEXT) \ test_lib-test-str-table.$(OBJEXT) \ test_lib-test-time-util.$(OBJEXT) \ test_lib-test-timing.$(OBJEXT) test_lib-test-unichar.$(OBJEXT) \ test_lib-test-utc-mktime.$(OBJEXT) \ test_lib-test-var-expand.$(OBJEXT) \ test_lib-test-wildcard-match.$(OBJEXT) test_lib_OBJECTS = $(am_test_lib_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(liblib_la_SOURCES) $(test_lib_SOURCES) DIST_SOURCES = $(liblib_la_SOURCES) $(test_lib_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = liblib.la BUILT_SOURCES = unicodemap.c EXTRA_DIST = unicodemap.c unicodemap.pl UnicodeData.txt liblib_la_SOURCES = \ abspath.c \ array.c \ aqueue.c \ askpass.c \ backtrace-string.c \ base32.c \ base64.c \ bits.c \ bsearch-insert-pos.c \ buffer.c \ child-wait.c \ compat.c \ connection.c \ crc32.c \ data-stack.c \ eacces-error.c \ env-util.c \ execv-const.c \ failures.c \ fd-close-on-exec.c \ fd-set-nonblock.c \ fdatasync-path.c \ fdpass.c \ file-cache.c \ file-create-locked.c \ file-copy.c \ file-dotlock.c \ file-lock.c \ file-set-size.c \ guid.c \ hash.c \ hash-format.c \ hash-method.c \ hash2.c \ hex-binary.c \ hex-dec.c \ hmac.c \ hmac-cram-md5.c \ home-expand.c \ hook-build.c \ hostpid.c \ imem.c \ ipwd.c \ iostream.c \ iostream-rawlog.c \ iostream-temp.c \ iso8601-date.c \ istream.c \ istream-base64-decoder.c \ istream-base64-encoder.c \ istream-callback.c \ istream-chain.c \ istream-concat.c \ istream-crlf.c \ istream-data.c \ istream-failure-at.c \ istream-file.c \ istream-hash.c \ istream-jsonstr.c \ istream-limit.c \ istream-mmap.c \ istream-multiplex.c \ istream-rawlog.c \ istream-seekable.c \ istream-sized.c \ istream-tee.c \ istream-timeout.c \ istream-unix.c \ ioloop.c \ ioloop-iolist.c \ ioloop-notify-none.c \ ioloop-notify-fd.c \ ioloop-notify-inotify.c \ ioloop-notify-kqueue.c \ ioloop-poll.c \ ioloop-select.c \ ioloop-epoll.c \ ioloop-kqueue.c \ json-parser.c \ json-tree.c \ lib.c \ lib-signals.c \ log-throttle.c \ md4.c \ md5.c \ mempool.c \ mempool-alloconly.c \ mempool-datastack.c \ mempool-system.c \ mempool-unsafe-datastack.c \ mkdir-parents.c \ mmap-anon.c \ mmap-util.c \ module-dir.c \ mountpoint.c \ net.c \ nfs-workarounds.c \ numpack.c \ ostream.c \ ostream-buffer.c \ ostream-escaped.c \ ostream-failure-at.c \ ostream-file.c \ ostream-hash.c \ ostream-multiplex.c \ ostream-null.c \ ostream-rawlog.c \ ostream-unix.c \ pkcs5.c \ primes.c \ printf-format-fix.c \ process-title.c \ priorityq.c \ randgen.c \ rand.c \ read-full.c \ restrict-access.c \ restrict-process-size.c \ safe-memset.c \ safe-mkdir.c \ safe-mkstemp.c \ sendfile-util.c \ seq-range-array.c \ sha1.c \ sha2.c \ sha3.c \ str.c \ str-find.c \ str-sanitize.c \ str-table.c \ strescape.c \ strfuncs.c \ strnum.c \ time-util.c \ timing.c \ unix-socket-create.c \ unlink-directory.c \ unlink-old-files.c \ unichar.c \ uri-util.c \ utc-offset.c \ utc-mktime.c \ var-expand.c \ var-expand-if.c \ wildcard-match.c \ write-full.c headers = \ abspath.h \ aqueue.h \ array.h \ array-decl.h \ askpass.h \ backtrace-string.h \ base32.h \ base64.h \ bits.h \ bsearch-insert-pos.h \ buffer.h \ byteorder.h \ child-wait.h \ compat.h \ connection.h \ crc32.h \ data-stack.h \ eacces-error.h \ env-util.h \ execv-const.h \ failures.h \ fd-close-on-exec.h \ fd-set-nonblock.h \ fdatasync-path.h \ fdpass.h \ file-cache.h \ file-create-locked.h \ file-copy.h \ file-dotlock.h \ file-lock.h \ file-set-size.h \ fsync-mode.h \ guid.h \ hash.h \ hash-decl.h \ hash-format.h \ hash-method.h \ hash2.h \ hex-binary.h \ hex-dec.h \ hmac.h \ hmac-cram-md5.h \ home-expand.h \ hook-build.h \ hostpid.h \ imem.h \ ipwd.h \ iostream.h \ iostream-private.h \ iostream-rawlog.h \ iostream-rawlog-private.h \ iostream-temp.h \ iso8601-date.h \ istream.h \ istream-base64.h \ istream-callback.h \ istream-chain.h \ istream-concat.h \ istream-crlf.h \ istream-failure-at.h \ istream-file-private.h \ istream-hash.h \ istream-jsonstr.h \ istream-multiplex.h \ istream-private.h \ istream-rawlog.h \ istream-seekable.h \ istream-sized.h \ istream-tee.h \ istream-timeout.h \ istream-unix.h \ ioloop.h \ ioloop-iolist.h \ ioloop-private.h \ ioloop-notify-fd.h \ json-parser.h \ json-tree.h \ lib.h \ lib-signals.h \ llist.h \ log-throttle.h \ macros.h \ md4.h \ md5.h \ malloc-overflow.h \ mempool.h \ mkdir-parents.h \ mmap-util.h \ module-context.h \ module-dir.h \ mountpoint.h \ net.h \ nfs-workarounds.h \ numpack.h \ ostream.h \ ostream-escaped.h \ ostream-failure-at.h \ ostream-file-private.h \ ostream-hash.h \ ostream-multiplex.h \ ostream-private.h \ ostream-null.h \ ostream-rawlog.h \ ostream-unix.h \ pkcs5.h \ primes.h \ printf-format-fix.h \ process-title.h \ priorityq.h \ rand.h \ randgen.h \ read-full.h \ restrict-access.h \ restrict-process-size.h \ safe-memset.h \ safe-mkdir.h \ safe-mkstemp.h \ sendfile-util.h \ seq-range-array.h \ sha-common.h \ sha1.h \ sha2.h \ sha3.h \ sort.h \ str.h \ str-find.h \ str-sanitize.h \ str-table.h \ strescape.h \ strfuncs.h \ strnum.h \ time-util.h \ timing.h \ unix-socket-create.h \ unlink-directory.h \ unlink-old-files.h \ unichar.h \ uri-util.h \ utc-offset.h \ utc-mktime.h \ var-expand.h \ var-expand-private.h \ wildcard-match.h \ write-full.h test_programs = test-lib test_lib_CPPFLAGS = \ -I$(top_srcdir)/src/lib-test test_libs = \ ../lib-test/libtest.la \ liblib.la test_lib_SOURCES = \ test-lib.c \ test-array.c \ test-aqueue.c \ test-base32.c \ test-base64.c \ test-bits.c \ test-bsearch-insert-pos.c \ test-buffer.c \ test-byteorder.c \ test-crc32.c \ test-data-stack.c \ test-failures.c \ test-file-create-locked.c \ test-guid.c \ test-hash.c \ test-hash-format.c \ test-hash-method.c \ test-hex-binary.c \ test-imem.c \ test-ioloop.c \ test-iso8601-date.c \ test-iostream-temp.c \ test-istream.c \ test-istream-base64-decoder.c \ test-istream-base64-encoder.c \ test-istream-chain.c \ test-istream-concat.c \ test-istream-crlf.c \ test-istream-failure-at.c \ test-istream-multiplex.c \ test-istream-seekable.c \ test-istream-tee.c \ test-istream-unix.c \ test-json-parser.c \ test-json-tree.c \ test-llist.c \ test-log-throttle.c \ test-malloc-overflow.c \ test-mempool.c \ test-mempool-alloconly.c \ test-pkcs5.c \ test-net.c \ test-numpack.c \ test-ostream-buffer.c \ test-ostream-escaped.c \ test-ostream-failure-at.c \ test-ostream-file.c \ test-ostream-multiplex.c \ test-multiplex.c \ test-primes.c \ test-printf-format-fix.c \ test-priorityq.c \ test-seq-range-array.c \ test-str.c \ test-strescape.c \ test-strfuncs.c \ test-strnum.c \ test-str-find.c \ test-str-sanitize.c \ test-str-table.c \ test-time-util.c \ test-timing.c \ test-unichar.c \ test-utc-mktime.c \ test-var-expand.c \ test-wildcard-match.c test_headers = \ test-lib.h test_lib_LDADD = $(test_libs) test_lib_DEPENDENCIES = $(test_libs) pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) noinst_HEADERS = $(test_headers) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } liblib.la: $(liblib_la_OBJECTS) $(liblib_la_DEPENDENCIES) $(EXTRA_liblib_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(liblib_la_OBJECTS) $(liblib_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-lib$(EXEEXT): $(test_lib_OBJECTS) $(test_lib_DEPENDENCIES) $(EXTRA_test_lib_DEPENDENCIES) @rm -f test-lib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_lib_OBJECTS) $(test_lib_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/abspath.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aqueue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/askpass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backtrace-string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch-insert-pos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child-wait.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc32.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eacces-error.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execv-const.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failures.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fd-close-on-exec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fd-set-nonblock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdatasync-path.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdpass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-copy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-create-locked.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-dotlock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-lock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-set-size.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-format.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-method.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex-binary.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex-dec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac-cram-md5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/home-expand.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hook-build.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostpid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-epoll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-iolist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-kqueue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-fd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-inotify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-kqueue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-none.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-poll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-select.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-rawlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-temp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipwd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iso8601-date.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-base64-decoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-base64-encoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-callback.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-chain.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-concat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-crlf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-failure-at.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-jsonstr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-limit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-mmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-multiplex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-rawlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-seekable.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-sized.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-tee.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-timeout.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib-signals.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log-throttle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md4.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-alloconly.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-datastack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-system.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-unsafe-datastack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkdir-parents.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap-anon.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-dir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountpoint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfs-workarounds.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/numpack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-buffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-escaped.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-failure-at.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-multiplex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-null.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-rawlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/primes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf-format-fix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/priorityq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process-title.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rand.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/randgen.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read-full.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/restrict-access.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/restrict-process-size.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-memset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-mkdir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-mkstemp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sendfile-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seq-range-array.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha3.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-find.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-sanitize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strescape.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strfuncs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnum.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-aqueue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-base32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-base64.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-bits.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-bsearch-insert-pos.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-byteorder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-crc32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-data-stack.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-failures.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-file-create-locked.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-guid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash-format.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash-method.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hex-binary.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-imem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ioloop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iostream-temp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iso8601-date.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-base64-decoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-base64-encoder.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-chain.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-concat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-crlf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-failure-at.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-multiplex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-seekable.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-tee.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-unix.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-json-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-json-tree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-lib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-llist.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-log-throttle.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-malloc-overflow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-mempool-alloconly.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-mempool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-multiplex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-net.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-numpack.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-escaped.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-failure-at.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-multiplex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-pkcs5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-primes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-printf-format-fix.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-priorityq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-seq-range-array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-find.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-sanitize.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-table.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strescape.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strfuncs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strnum.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-time-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-timing.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-unichar.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-utc-mktime.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-var-expand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-wildcard-match.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timing.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unichar.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix-socket-create.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unlink-directory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unlink-old-files.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utc-mktime.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utc-offset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var-expand-if.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var-expand.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildcard-match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write-full.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< test_lib-test-lib.o: test-lib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib.o -MD -MP -MF $(DEPDIR)/test_lib-test-lib.Tpo -c -o test_lib-test-lib.o `test -f 'test-lib.c' || echo '$(srcdir)/'`test-lib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib.Tpo $(DEPDIR)/test_lib-test-lib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib.c' object='test_lib-test-lib.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib.o `test -f 'test-lib.c' || echo '$(srcdir)/'`test-lib.c test_lib-test-lib.obj: test-lib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib.obj -MD -MP -MF $(DEPDIR)/test_lib-test-lib.Tpo -c -o test_lib-test-lib.obj `if test -f 'test-lib.c'; then $(CYGPATH_W) 'test-lib.c'; else $(CYGPATH_W) '$(srcdir)/test-lib.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib.Tpo $(DEPDIR)/test_lib-test-lib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib.c' object='test_lib-test-lib.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib.obj `if test -f 'test-lib.c'; then $(CYGPATH_W) 'test-lib.c'; else $(CYGPATH_W) '$(srcdir)/test-lib.c'; fi` test_lib-test-array.o: test-array.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-array.o -MD -MP -MF $(DEPDIR)/test_lib-test-array.Tpo -c -o test_lib-test-array.o `test -f 'test-array.c' || echo '$(srcdir)/'`test-array.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-array.Tpo $(DEPDIR)/test_lib-test-array.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-array.c' object='test_lib-test-array.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-array.o `test -f 'test-array.c' || echo '$(srcdir)/'`test-array.c test_lib-test-array.obj: test-array.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-array.obj -MD -MP -MF $(DEPDIR)/test_lib-test-array.Tpo -c -o test_lib-test-array.obj `if test -f 'test-array.c'; then $(CYGPATH_W) 'test-array.c'; else $(CYGPATH_W) '$(srcdir)/test-array.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-array.Tpo $(DEPDIR)/test_lib-test-array.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-array.c' object='test_lib-test-array.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-array.obj `if test -f 'test-array.c'; then $(CYGPATH_W) 'test-array.c'; else $(CYGPATH_W) '$(srcdir)/test-array.c'; fi` test_lib-test-aqueue.o: test-aqueue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-aqueue.o -MD -MP -MF $(DEPDIR)/test_lib-test-aqueue.Tpo -c -o test_lib-test-aqueue.o `test -f 'test-aqueue.c' || echo '$(srcdir)/'`test-aqueue.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-aqueue.Tpo $(DEPDIR)/test_lib-test-aqueue.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-aqueue.c' object='test_lib-test-aqueue.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-aqueue.o `test -f 'test-aqueue.c' || echo '$(srcdir)/'`test-aqueue.c test_lib-test-aqueue.obj: test-aqueue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-aqueue.obj -MD -MP -MF $(DEPDIR)/test_lib-test-aqueue.Tpo -c -o test_lib-test-aqueue.obj `if test -f 'test-aqueue.c'; then $(CYGPATH_W) 'test-aqueue.c'; else $(CYGPATH_W) '$(srcdir)/test-aqueue.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-aqueue.Tpo $(DEPDIR)/test_lib-test-aqueue.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-aqueue.c' object='test_lib-test-aqueue.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-aqueue.obj `if test -f 'test-aqueue.c'; then $(CYGPATH_W) 'test-aqueue.c'; else $(CYGPATH_W) '$(srcdir)/test-aqueue.c'; fi` test_lib-test-base32.o: test-base32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base32.o -MD -MP -MF $(DEPDIR)/test_lib-test-base32.Tpo -c -o test_lib-test-base32.o `test -f 'test-base32.c' || echo '$(srcdir)/'`test-base32.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base32.Tpo $(DEPDIR)/test_lib-test-base32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base32.c' object='test_lib-test-base32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base32.o `test -f 'test-base32.c' || echo '$(srcdir)/'`test-base32.c test_lib-test-base32.obj: test-base32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base32.obj -MD -MP -MF $(DEPDIR)/test_lib-test-base32.Tpo -c -o test_lib-test-base32.obj `if test -f 'test-base32.c'; then $(CYGPATH_W) 'test-base32.c'; else $(CYGPATH_W) '$(srcdir)/test-base32.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base32.Tpo $(DEPDIR)/test_lib-test-base32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base32.c' object='test_lib-test-base32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base32.obj `if test -f 'test-base32.c'; then $(CYGPATH_W) 'test-base32.c'; else $(CYGPATH_W) '$(srcdir)/test-base32.c'; fi` test_lib-test-base64.o: test-base64.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base64.o -MD -MP -MF $(DEPDIR)/test_lib-test-base64.Tpo -c -o test_lib-test-base64.o `test -f 'test-base64.c' || echo '$(srcdir)/'`test-base64.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base64.Tpo $(DEPDIR)/test_lib-test-base64.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base64.c' object='test_lib-test-base64.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base64.o `test -f 'test-base64.c' || echo '$(srcdir)/'`test-base64.c test_lib-test-base64.obj: test-base64.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base64.obj -MD -MP -MF $(DEPDIR)/test_lib-test-base64.Tpo -c -o test_lib-test-base64.obj `if test -f 'test-base64.c'; then $(CYGPATH_W) 'test-base64.c'; else $(CYGPATH_W) '$(srcdir)/test-base64.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base64.Tpo $(DEPDIR)/test_lib-test-base64.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base64.c' object='test_lib-test-base64.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base64.obj `if test -f 'test-base64.c'; then $(CYGPATH_W) 'test-base64.c'; else $(CYGPATH_W) '$(srcdir)/test-base64.c'; fi` test_lib-test-bits.o: test-bits.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bits.o -MD -MP -MF $(DEPDIR)/test_lib-test-bits.Tpo -c -o test_lib-test-bits.o `test -f 'test-bits.c' || echo '$(srcdir)/'`test-bits.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bits.Tpo $(DEPDIR)/test_lib-test-bits.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bits.c' object='test_lib-test-bits.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bits.o `test -f 'test-bits.c' || echo '$(srcdir)/'`test-bits.c test_lib-test-bits.obj: test-bits.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bits.obj -MD -MP -MF $(DEPDIR)/test_lib-test-bits.Tpo -c -o test_lib-test-bits.obj `if test -f 'test-bits.c'; then $(CYGPATH_W) 'test-bits.c'; else $(CYGPATH_W) '$(srcdir)/test-bits.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bits.Tpo $(DEPDIR)/test_lib-test-bits.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bits.c' object='test_lib-test-bits.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bits.obj `if test -f 'test-bits.c'; then $(CYGPATH_W) 'test-bits.c'; else $(CYGPATH_W) '$(srcdir)/test-bits.c'; fi` test_lib-test-bsearch-insert-pos.o: test-bsearch-insert-pos.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bsearch-insert-pos.o -MD -MP -MF $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo -c -o test_lib-test-bsearch-insert-pos.o `test -f 'test-bsearch-insert-pos.c' || echo '$(srcdir)/'`test-bsearch-insert-pos.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo $(DEPDIR)/test_lib-test-bsearch-insert-pos.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bsearch-insert-pos.c' object='test_lib-test-bsearch-insert-pos.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bsearch-insert-pos.o `test -f 'test-bsearch-insert-pos.c' || echo '$(srcdir)/'`test-bsearch-insert-pos.c test_lib-test-bsearch-insert-pos.obj: test-bsearch-insert-pos.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bsearch-insert-pos.obj -MD -MP -MF $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo -c -o test_lib-test-bsearch-insert-pos.obj `if test -f 'test-bsearch-insert-pos.c'; then $(CYGPATH_W) 'test-bsearch-insert-pos.c'; else $(CYGPATH_W) '$(srcdir)/test-bsearch-insert-pos.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo $(DEPDIR)/test_lib-test-bsearch-insert-pos.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bsearch-insert-pos.c' object='test_lib-test-bsearch-insert-pos.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bsearch-insert-pos.obj `if test -f 'test-bsearch-insert-pos.c'; then $(CYGPATH_W) 'test-bsearch-insert-pos.c'; else $(CYGPATH_W) '$(srcdir)/test-bsearch-insert-pos.c'; fi` test_lib-test-buffer.o: test-buffer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer.o -MD -MP -MF $(DEPDIR)/test_lib-test-buffer.Tpo -c -o test_lib-test-buffer.o `test -f 'test-buffer.c' || echo '$(srcdir)/'`test-buffer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer.Tpo $(DEPDIR)/test_lib-test-buffer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer.c' object='test_lib-test-buffer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer.o `test -f 'test-buffer.c' || echo '$(srcdir)/'`test-buffer.c test_lib-test-buffer.obj: test-buffer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer.obj -MD -MP -MF $(DEPDIR)/test_lib-test-buffer.Tpo -c -o test_lib-test-buffer.obj `if test -f 'test-buffer.c'; then $(CYGPATH_W) 'test-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer.Tpo $(DEPDIR)/test_lib-test-buffer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer.c' object='test_lib-test-buffer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer.obj `if test -f 'test-buffer.c'; then $(CYGPATH_W) 'test-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer.c'; fi` test_lib-test-byteorder.o: test-byteorder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-byteorder.o -MD -MP -MF $(DEPDIR)/test_lib-test-byteorder.Tpo -c -o test_lib-test-byteorder.o `test -f 'test-byteorder.c' || echo '$(srcdir)/'`test-byteorder.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-byteorder.Tpo $(DEPDIR)/test_lib-test-byteorder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-byteorder.c' object='test_lib-test-byteorder.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-byteorder.o `test -f 'test-byteorder.c' || echo '$(srcdir)/'`test-byteorder.c test_lib-test-byteorder.obj: test-byteorder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-byteorder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-byteorder.Tpo -c -o test_lib-test-byteorder.obj `if test -f 'test-byteorder.c'; then $(CYGPATH_W) 'test-byteorder.c'; else $(CYGPATH_W) '$(srcdir)/test-byteorder.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-byteorder.Tpo $(DEPDIR)/test_lib-test-byteorder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-byteorder.c' object='test_lib-test-byteorder.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-byteorder.obj `if test -f 'test-byteorder.c'; then $(CYGPATH_W) 'test-byteorder.c'; else $(CYGPATH_W) '$(srcdir)/test-byteorder.c'; fi` test_lib-test-crc32.o: test-crc32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-crc32.o -MD -MP -MF $(DEPDIR)/test_lib-test-crc32.Tpo -c -o test_lib-test-crc32.o `test -f 'test-crc32.c' || echo '$(srcdir)/'`test-crc32.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-crc32.Tpo $(DEPDIR)/test_lib-test-crc32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crc32.c' object='test_lib-test-crc32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-crc32.o `test -f 'test-crc32.c' || echo '$(srcdir)/'`test-crc32.c test_lib-test-crc32.obj: test-crc32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-crc32.obj -MD -MP -MF $(DEPDIR)/test_lib-test-crc32.Tpo -c -o test_lib-test-crc32.obj `if test -f 'test-crc32.c'; then $(CYGPATH_W) 'test-crc32.c'; else $(CYGPATH_W) '$(srcdir)/test-crc32.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-crc32.Tpo $(DEPDIR)/test_lib-test-crc32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crc32.c' object='test_lib-test-crc32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-crc32.obj `if test -f 'test-crc32.c'; then $(CYGPATH_W) 'test-crc32.c'; else $(CYGPATH_W) '$(srcdir)/test-crc32.c'; fi` test_lib-test-data-stack.o: test-data-stack.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-data-stack.o -MD -MP -MF $(DEPDIR)/test_lib-test-data-stack.Tpo -c -o test_lib-test-data-stack.o `test -f 'test-data-stack.c' || echo '$(srcdir)/'`test-data-stack.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-data-stack.Tpo $(DEPDIR)/test_lib-test-data-stack.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-data-stack.c' object='test_lib-test-data-stack.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-data-stack.o `test -f 'test-data-stack.c' || echo '$(srcdir)/'`test-data-stack.c test_lib-test-data-stack.obj: test-data-stack.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-data-stack.obj -MD -MP -MF $(DEPDIR)/test_lib-test-data-stack.Tpo -c -o test_lib-test-data-stack.obj `if test -f 'test-data-stack.c'; then $(CYGPATH_W) 'test-data-stack.c'; else $(CYGPATH_W) '$(srcdir)/test-data-stack.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-data-stack.Tpo $(DEPDIR)/test_lib-test-data-stack.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-data-stack.c' object='test_lib-test-data-stack.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-data-stack.obj `if test -f 'test-data-stack.c'; then $(CYGPATH_W) 'test-data-stack.c'; else $(CYGPATH_W) '$(srcdir)/test-data-stack.c'; fi` test_lib-test-failures.o: test-failures.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-failures.o -MD -MP -MF $(DEPDIR)/test_lib-test-failures.Tpo -c -o test_lib-test-failures.o `test -f 'test-failures.c' || echo '$(srcdir)/'`test-failures.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-failures.Tpo $(DEPDIR)/test_lib-test-failures.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-failures.c' object='test_lib-test-failures.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-failures.o `test -f 'test-failures.c' || echo '$(srcdir)/'`test-failures.c test_lib-test-failures.obj: test-failures.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-failures.obj -MD -MP -MF $(DEPDIR)/test_lib-test-failures.Tpo -c -o test_lib-test-failures.obj `if test -f 'test-failures.c'; then $(CYGPATH_W) 'test-failures.c'; else $(CYGPATH_W) '$(srcdir)/test-failures.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-failures.Tpo $(DEPDIR)/test_lib-test-failures.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-failures.c' object='test_lib-test-failures.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-failures.obj `if test -f 'test-failures.c'; then $(CYGPATH_W) 'test-failures.c'; else $(CYGPATH_W) '$(srcdir)/test-failures.c'; fi` test_lib-test-file-create-locked.o: test-file-create-locked.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-create-locked.o -MD -MP -MF $(DEPDIR)/test_lib-test-file-create-locked.Tpo -c -o test_lib-test-file-create-locked.o `test -f 'test-file-create-locked.c' || echo '$(srcdir)/'`test-file-create-locked.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-create-locked.Tpo $(DEPDIR)/test_lib-test-file-create-locked.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-create-locked.c' object='test_lib-test-file-create-locked.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-create-locked.o `test -f 'test-file-create-locked.c' || echo '$(srcdir)/'`test-file-create-locked.c test_lib-test-file-create-locked.obj: test-file-create-locked.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-create-locked.obj -MD -MP -MF $(DEPDIR)/test_lib-test-file-create-locked.Tpo -c -o test_lib-test-file-create-locked.obj `if test -f 'test-file-create-locked.c'; then $(CYGPATH_W) 'test-file-create-locked.c'; else $(CYGPATH_W) '$(srcdir)/test-file-create-locked.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-create-locked.Tpo $(DEPDIR)/test_lib-test-file-create-locked.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-create-locked.c' object='test_lib-test-file-create-locked.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-create-locked.obj `if test -f 'test-file-create-locked.c'; then $(CYGPATH_W) 'test-file-create-locked.c'; else $(CYGPATH_W) '$(srcdir)/test-file-create-locked.c'; fi` test_lib-test-guid.o: test-guid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-guid.o -MD -MP -MF $(DEPDIR)/test_lib-test-guid.Tpo -c -o test_lib-test-guid.o `test -f 'test-guid.c' || echo '$(srcdir)/'`test-guid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-guid.Tpo $(DEPDIR)/test_lib-test-guid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-guid.c' object='test_lib-test-guid.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-guid.o `test -f 'test-guid.c' || echo '$(srcdir)/'`test-guid.c test_lib-test-guid.obj: test-guid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-guid.obj -MD -MP -MF $(DEPDIR)/test_lib-test-guid.Tpo -c -o test_lib-test-guid.obj `if test -f 'test-guid.c'; then $(CYGPATH_W) 'test-guid.c'; else $(CYGPATH_W) '$(srcdir)/test-guid.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-guid.Tpo $(DEPDIR)/test_lib-test-guid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-guid.c' object='test_lib-test-guid.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-guid.obj `if test -f 'test-guid.c'; then $(CYGPATH_W) 'test-guid.c'; else $(CYGPATH_W) '$(srcdir)/test-guid.c'; fi` test_lib-test-hash.o: test-hash.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash.Tpo -c -o test_lib-test-hash.o `test -f 'test-hash.c' || echo '$(srcdir)/'`test-hash.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash.Tpo $(DEPDIR)/test_lib-test-hash.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash.c' object='test_lib-test-hash.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash.o `test -f 'test-hash.c' || echo '$(srcdir)/'`test-hash.c test_lib-test-hash.obj: test-hash.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash.Tpo -c -o test_lib-test-hash.obj `if test -f 'test-hash.c'; then $(CYGPATH_W) 'test-hash.c'; else $(CYGPATH_W) '$(srcdir)/test-hash.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash.Tpo $(DEPDIR)/test_lib-test-hash.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash.c' object='test_lib-test-hash.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash.obj `if test -f 'test-hash.c'; then $(CYGPATH_W) 'test-hash.c'; else $(CYGPATH_W) '$(srcdir)/test-hash.c'; fi` test_lib-test-hash-format.o: test-hash-format.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-format.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash-format.Tpo -c -o test_lib-test-hash-format.o `test -f 'test-hash-format.c' || echo '$(srcdir)/'`test-hash-format.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-format.Tpo $(DEPDIR)/test_lib-test-hash-format.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-format.c' object='test_lib-test-hash-format.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-format.o `test -f 'test-hash-format.c' || echo '$(srcdir)/'`test-hash-format.c test_lib-test-hash-format.obj: test-hash-format.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-format.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash-format.Tpo -c -o test_lib-test-hash-format.obj `if test -f 'test-hash-format.c'; then $(CYGPATH_W) 'test-hash-format.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-format.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-format.Tpo $(DEPDIR)/test_lib-test-hash-format.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-format.c' object='test_lib-test-hash-format.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-format.obj `if test -f 'test-hash-format.c'; then $(CYGPATH_W) 'test-hash-format.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-format.c'; fi` test_lib-test-hash-method.o: test-hash-method.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-method.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash-method.Tpo -c -o test_lib-test-hash-method.o `test -f 'test-hash-method.c' || echo '$(srcdir)/'`test-hash-method.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-method.Tpo $(DEPDIR)/test_lib-test-hash-method.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-method.c' object='test_lib-test-hash-method.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-method.o `test -f 'test-hash-method.c' || echo '$(srcdir)/'`test-hash-method.c test_lib-test-hash-method.obj: test-hash-method.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-method.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash-method.Tpo -c -o test_lib-test-hash-method.obj `if test -f 'test-hash-method.c'; then $(CYGPATH_W) 'test-hash-method.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-method.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-method.Tpo $(DEPDIR)/test_lib-test-hash-method.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-method.c' object='test_lib-test-hash-method.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-method.obj `if test -f 'test-hash-method.c'; then $(CYGPATH_W) 'test-hash-method.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-method.c'; fi` test_lib-test-hex-binary.o: test-hex-binary.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hex-binary.o -MD -MP -MF $(DEPDIR)/test_lib-test-hex-binary.Tpo -c -o test_lib-test-hex-binary.o `test -f 'test-hex-binary.c' || echo '$(srcdir)/'`test-hex-binary.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hex-binary.Tpo $(DEPDIR)/test_lib-test-hex-binary.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hex-binary.c' object='test_lib-test-hex-binary.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hex-binary.o `test -f 'test-hex-binary.c' || echo '$(srcdir)/'`test-hex-binary.c test_lib-test-hex-binary.obj: test-hex-binary.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hex-binary.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hex-binary.Tpo -c -o test_lib-test-hex-binary.obj `if test -f 'test-hex-binary.c'; then $(CYGPATH_W) 'test-hex-binary.c'; else $(CYGPATH_W) '$(srcdir)/test-hex-binary.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hex-binary.Tpo $(DEPDIR)/test_lib-test-hex-binary.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hex-binary.c' object='test_lib-test-hex-binary.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hex-binary.obj `if test -f 'test-hex-binary.c'; then $(CYGPATH_W) 'test-hex-binary.c'; else $(CYGPATH_W) '$(srcdir)/test-hex-binary.c'; fi` test_lib-test-imem.o: test-imem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-imem.o -MD -MP -MF $(DEPDIR)/test_lib-test-imem.Tpo -c -o test_lib-test-imem.o `test -f 'test-imem.c' || echo '$(srcdir)/'`test-imem.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-imem.Tpo $(DEPDIR)/test_lib-test-imem.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-imem.c' object='test_lib-test-imem.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-imem.o `test -f 'test-imem.c' || echo '$(srcdir)/'`test-imem.c test_lib-test-imem.obj: test-imem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-imem.obj -MD -MP -MF $(DEPDIR)/test_lib-test-imem.Tpo -c -o test_lib-test-imem.obj `if test -f 'test-imem.c'; then $(CYGPATH_W) 'test-imem.c'; else $(CYGPATH_W) '$(srcdir)/test-imem.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-imem.Tpo $(DEPDIR)/test_lib-test-imem.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-imem.c' object='test_lib-test-imem.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-imem.obj `if test -f 'test-imem.c'; then $(CYGPATH_W) 'test-imem.c'; else $(CYGPATH_W) '$(srcdir)/test-imem.c'; fi` test_lib-test-ioloop.o: test-ioloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ioloop.o -MD -MP -MF $(DEPDIR)/test_lib-test-ioloop.Tpo -c -o test_lib-test-ioloop.o `test -f 'test-ioloop.c' || echo '$(srcdir)/'`test-ioloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ioloop.Tpo $(DEPDIR)/test_lib-test-ioloop.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ioloop.c' object='test_lib-test-ioloop.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ioloop.o `test -f 'test-ioloop.c' || echo '$(srcdir)/'`test-ioloop.c test_lib-test-ioloop.obj: test-ioloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ioloop.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ioloop.Tpo -c -o test_lib-test-ioloop.obj `if test -f 'test-ioloop.c'; then $(CYGPATH_W) 'test-ioloop.c'; else $(CYGPATH_W) '$(srcdir)/test-ioloop.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ioloop.Tpo $(DEPDIR)/test_lib-test-ioloop.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ioloop.c' object='test_lib-test-ioloop.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ioloop.obj `if test -f 'test-ioloop.c'; then $(CYGPATH_W) 'test-ioloop.c'; else $(CYGPATH_W) '$(srcdir)/test-ioloop.c'; fi` test_lib-test-iso8601-date.o: test-iso8601-date.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iso8601-date.o -MD -MP -MF $(DEPDIR)/test_lib-test-iso8601-date.Tpo -c -o test_lib-test-iso8601-date.o `test -f 'test-iso8601-date.c' || echo '$(srcdir)/'`test-iso8601-date.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iso8601-date.Tpo $(DEPDIR)/test_lib-test-iso8601-date.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iso8601-date.c' object='test_lib-test-iso8601-date.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iso8601-date.o `test -f 'test-iso8601-date.c' || echo '$(srcdir)/'`test-iso8601-date.c test_lib-test-iso8601-date.obj: test-iso8601-date.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iso8601-date.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iso8601-date.Tpo -c -o test_lib-test-iso8601-date.obj `if test -f 'test-iso8601-date.c'; then $(CYGPATH_W) 'test-iso8601-date.c'; else $(CYGPATH_W) '$(srcdir)/test-iso8601-date.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iso8601-date.Tpo $(DEPDIR)/test_lib-test-iso8601-date.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iso8601-date.c' object='test_lib-test-iso8601-date.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iso8601-date.obj `if test -f 'test-iso8601-date.c'; then $(CYGPATH_W) 'test-iso8601-date.c'; else $(CYGPATH_W) '$(srcdir)/test-iso8601-date.c'; fi` test_lib-test-iostream-temp.o: test-iostream-temp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-temp.o -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-temp.Tpo -c -o test_lib-test-iostream-temp.o `test -f 'test-iostream-temp.c' || echo '$(srcdir)/'`test-iostream-temp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-temp.Tpo $(DEPDIR)/test_lib-test-iostream-temp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-temp.c' object='test_lib-test-iostream-temp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-temp.o `test -f 'test-iostream-temp.c' || echo '$(srcdir)/'`test-iostream-temp.c test_lib-test-iostream-temp.obj: test-iostream-temp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-temp.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-temp.Tpo -c -o test_lib-test-iostream-temp.obj `if test -f 'test-iostream-temp.c'; then $(CYGPATH_W) 'test-iostream-temp.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-temp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-temp.Tpo $(DEPDIR)/test_lib-test-iostream-temp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-temp.c' object='test_lib-test-iostream-temp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-temp.obj `if test -f 'test-iostream-temp.c'; then $(CYGPATH_W) 'test-iostream-temp.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-temp.c'; fi` test_lib-test-istream.o: test-istream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream.Tpo -c -o test_lib-test-istream.o `test -f 'test-istream.c' || echo '$(srcdir)/'`test-istream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream.Tpo $(DEPDIR)/test_lib-test-istream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream.c' object='test_lib-test-istream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream.o `test -f 'test-istream.c' || echo '$(srcdir)/'`test-istream.c test_lib-test-istream.obj: test-istream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream.Tpo -c -o test_lib-test-istream.obj `if test -f 'test-istream.c'; then $(CYGPATH_W) 'test-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-istream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream.Tpo $(DEPDIR)/test_lib-test-istream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream.c' object='test_lib-test-istream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream.obj `if test -f 'test-istream.c'; then $(CYGPATH_W) 'test-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-istream.c'; fi` test_lib-test-istream-base64-decoder.o: test-istream-base64-decoder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-decoder.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo -c -o test_lib-test-istream-base64-decoder.o `test -f 'test-istream-base64-decoder.c' || echo '$(srcdir)/'`test-istream-base64-decoder.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-decoder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-decoder.c' object='test_lib-test-istream-base64-decoder.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-decoder.o `test -f 'test-istream-base64-decoder.c' || echo '$(srcdir)/'`test-istream-base64-decoder.c test_lib-test-istream-base64-decoder.obj: test-istream-base64-decoder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-decoder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo -c -o test_lib-test-istream-base64-decoder.obj `if test -f 'test-istream-base64-decoder.c'; then $(CYGPATH_W) 'test-istream-base64-decoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-decoder.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-decoder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-decoder.c' object='test_lib-test-istream-base64-decoder.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-decoder.obj `if test -f 'test-istream-base64-decoder.c'; then $(CYGPATH_W) 'test-istream-base64-decoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-decoder.c'; fi` test_lib-test-istream-base64-encoder.o: test-istream-base64-encoder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-encoder.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo -c -o test_lib-test-istream-base64-encoder.o `test -f 'test-istream-base64-encoder.c' || echo '$(srcdir)/'`test-istream-base64-encoder.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-encoder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-encoder.c' object='test_lib-test-istream-base64-encoder.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-encoder.o `test -f 'test-istream-base64-encoder.c' || echo '$(srcdir)/'`test-istream-base64-encoder.c test_lib-test-istream-base64-encoder.obj: test-istream-base64-encoder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-encoder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo -c -o test_lib-test-istream-base64-encoder.obj `if test -f 'test-istream-base64-encoder.c'; then $(CYGPATH_W) 'test-istream-base64-encoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-encoder.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-encoder.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-encoder.c' object='test_lib-test-istream-base64-encoder.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-encoder.obj `if test -f 'test-istream-base64-encoder.c'; then $(CYGPATH_W) 'test-istream-base64-encoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-encoder.c'; fi` test_lib-test-istream-chain.o: test-istream-chain.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-chain.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-chain.Tpo -c -o test_lib-test-istream-chain.o `test -f 'test-istream-chain.c' || echo '$(srcdir)/'`test-istream-chain.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-chain.Tpo $(DEPDIR)/test_lib-test-istream-chain.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-chain.c' object='test_lib-test-istream-chain.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-chain.o `test -f 'test-istream-chain.c' || echo '$(srcdir)/'`test-istream-chain.c test_lib-test-istream-chain.obj: test-istream-chain.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-chain.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-chain.Tpo -c -o test_lib-test-istream-chain.obj `if test -f 'test-istream-chain.c'; then $(CYGPATH_W) 'test-istream-chain.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-chain.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-chain.Tpo $(DEPDIR)/test_lib-test-istream-chain.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-chain.c' object='test_lib-test-istream-chain.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-chain.obj `if test -f 'test-istream-chain.c'; then $(CYGPATH_W) 'test-istream-chain.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-chain.c'; fi` test_lib-test-istream-concat.o: test-istream-concat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-concat.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-concat.Tpo -c -o test_lib-test-istream-concat.o `test -f 'test-istream-concat.c' || echo '$(srcdir)/'`test-istream-concat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-concat.Tpo $(DEPDIR)/test_lib-test-istream-concat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-concat.c' object='test_lib-test-istream-concat.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-concat.o `test -f 'test-istream-concat.c' || echo '$(srcdir)/'`test-istream-concat.c test_lib-test-istream-concat.obj: test-istream-concat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-concat.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-concat.Tpo -c -o test_lib-test-istream-concat.obj `if test -f 'test-istream-concat.c'; then $(CYGPATH_W) 'test-istream-concat.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-concat.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-concat.Tpo $(DEPDIR)/test_lib-test-istream-concat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-concat.c' object='test_lib-test-istream-concat.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-concat.obj `if test -f 'test-istream-concat.c'; then $(CYGPATH_W) 'test-istream-concat.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-concat.c'; fi` test_lib-test-istream-crlf.o: test-istream-crlf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-crlf.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-crlf.Tpo -c -o test_lib-test-istream-crlf.o `test -f 'test-istream-crlf.c' || echo '$(srcdir)/'`test-istream-crlf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-crlf.Tpo $(DEPDIR)/test_lib-test-istream-crlf.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-crlf.c' object='test_lib-test-istream-crlf.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-crlf.o `test -f 'test-istream-crlf.c' || echo '$(srcdir)/'`test-istream-crlf.c test_lib-test-istream-crlf.obj: test-istream-crlf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-crlf.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-crlf.Tpo -c -o test_lib-test-istream-crlf.obj `if test -f 'test-istream-crlf.c'; then $(CYGPATH_W) 'test-istream-crlf.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-crlf.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-crlf.Tpo $(DEPDIR)/test_lib-test-istream-crlf.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-crlf.c' object='test_lib-test-istream-crlf.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-crlf.obj `if test -f 'test-istream-crlf.c'; then $(CYGPATH_W) 'test-istream-crlf.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-crlf.c'; fi` test_lib-test-istream-failure-at.o: test-istream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-failure-at.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-failure-at.Tpo -c -o test_lib-test-istream-failure-at.o `test -f 'test-istream-failure-at.c' || echo '$(srcdir)/'`test-istream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-failure-at.Tpo $(DEPDIR)/test_lib-test-istream-failure-at.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-failure-at.c' object='test_lib-test-istream-failure-at.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-failure-at.o `test -f 'test-istream-failure-at.c' || echo '$(srcdir)/'`test-istream-failure-at.c test_lib-test-istream-failure-at.obj: test-istream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-failure-at.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-failure-at.Tpo -c -o test_lib-test-istream-failure-at.obj `if test -f 'test-istream-failure-at.c'; then $(CYGPATH_W) 'test-istream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-failure-at.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-failure-at.Tpo $(DEPDIR)/test_lib-test-istream-failure-at.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-failure-at.c' object='test_lib-test-istream-failure-at.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-failure-at.obj `if test -f 'test-istream-failure-at.c'; then $(CYGPATH_W) 'test-istream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-failure-at.c'; fi` test_lib-test-istream-multiplex.o: test-istream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-multiplex.Tpo -c -o test_lib-test-istream-multiplex.o `test -f 'test-istream-multiplex.c' || echo '$(srcdir)/'`test-istream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-multiplex.Tpo $(DEPDIR)/test_lib-test-istream-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-multiplex.c' object='test_lib-test-istream-multiplex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-multiplex.o `test -f 'test-istream-multiplex.c' || echo '$(srcdir)/'`test-istream-multiplex.c test_lib-test-istream-multiplex.obj: test-istream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-multiplex.Tpo -c -o test_lib-test-istream-multiplex.obj `if test -f 'test-istream-multiplex.c'; then $(CYGPATH_W) 'test-istream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-multiplex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-multiplex.Tpo $(DEPDIR)/test_lib-test-istream-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-multiplex.c' object='test_lib-test-istream-multiplex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-multiplex.obj `if test -f 'test-istream-multiplex.c'; then $(CYGPATH_W) 'test-istream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-multiplex.c'; fi` test_lib-test-istream-seekable.o: test-istream-seekable.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-seekable.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-seekable.Tpo -c -o test_lib-test-istream-seekable.o `test -f 'test-istream-seekable.c' || echo '$(srcdir)/'`test-istream-seekable.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-seekable.Tpo $(DEPDIR)/test_lib-test-istream-seekable.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-seekable.c' object='test_lib-test-istream-seekable.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-seekable.o `test -f 'test-istream-seekable.c' || echo '$(srcdir)/'`test-istream-seekable.c test_lib-test-istream-seekable.obj: test-istream-seekable.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-seekable.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-seekable.Tpo -c -o test_lib-test-istream-seekable.obj `if test -f 'test-istream-seekable.c'; then $(CYGPATH_W) 'test-istream-seekable.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-seekable.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-seekable.Tpo $(DEPDIR)/test_lib-test-istream-seekable.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-seekable.c' object='test_lib-test-istream-seekable.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-seekable.obj `if test -f 'test-istream-seekable.c'; then $(CYGPATH_W) 'test-istream-seekable.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-seekable.c'; fi` test_lib-test-istream-tee.o: test-istream-tee.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-tee.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-tee.Tpo -c -o test_lib-test-istream-tee.o `test -f 'test-istream-tee.c' || echo '$(srcdir)/'`test-istream-tee.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-tee.Tpo $(DEPDIR)/test_lib-test-istream-tee.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-tee.c' object='test_lib-test-istream-tee.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-tee.o `test -f 'test-istream-tee.c' || echo '$(srcdir)/'`test-istream-tee.c test_lib-test-istream-tee.obj: test-istream-tee.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-tee.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-tee.Tpo -c -o test_lib-test-istream-tee.obj `if test -f 'test-istream-tee.c'; then $(CYGPATH_W) 'test-istream-tee.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-tee.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-tee.Tpo $(DEPDIR)/test_lib-test-istream-tee.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-tee.c' object='test_lib-test-istream-tee.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-tee.obj `if test -f 'test-istream-tee.c'; then $(CYGPATH_W) 'test-istream-tee.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-tee.c'; fi` test_lib-test-istream-unix.o: test-istream-unix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-unix.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-unix.Tpo -c -o test_lib-test-istream-unix.o `test -f 'test-istream-unix.c' || echo '$(srcdir)/'`test-istream-unix.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-unix.Tpo $(DEPDIR)/test_lib-test-istream-unix.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-unix.c' object='test_lib-test-istream-unix.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-unix.o `test -f 'test-istream-unix.c' || echo '$(srcdir)/'`test-istream-unix.c test_lib-test-istream-unix.obj: test-istream-unix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-unix.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-unix.Tpo -c -o test_lib-test-istream-unix.obj `if test -f 'test-istream-unix.c'; then $(CYGPATH_W) 'test-istream-unix.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-unix.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-unix.Tpo $(DEPDIR)/test_lib-test-istream-unix.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-unix.c' object='test_lib-test-istream-unix.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-unix.obj `if test -f 'test-istream-unix.c'; then $(CYGPATH_W) 'test-istream-unix.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-unix.c'; fi` test_lib-test-json-parser.o: test-json-parser.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-parser.o -MD -MP -MF $(DEPDIR)/test_lib-test-json-parser.Tpo -c -o test_lib-test-json-parser.o `test -f 'test-json-parser.c' || echo '$(srcdir)/'`test-json-parser.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-parser.Tpo $(DEPDIR)/test_lib-test-json-parser.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-parser.c' object='test_lib-test-json-parser.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-parser.o `test -f 'test-json-parser.c' || echo '$(srcdir)/'`test-json-parser.c test_lib-test-json-parser.obj: test-json-parser.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-parser.obj -MD -MP -MF $(DEPDIR)/test_lib-test-json-parser.Tpo -c -o test_lib-test-json-parser.obj `if test -f 'test-json-parser.c'; then $(CYGPATH_W) 'test-json-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-json-parser.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-parser.Tpo $(DEPDIR)/test_lib-test-json-parser.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-parser.c' object='test_lib-test-json-parser.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-parser.obj `if test -f 'test-json-parser.c'; then $(CYGPATH_W) 'test-json-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-json-parser.c'; fi` test_lib-test-json-tree.o: test-json-tree.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-tree.o -MD -MP -MF $(DEPDIR)/test_lib-test-json-tree.Tpo -c -o test_lib-test-json-tree.o `test -f 'test-json-tree.c' || echo '$(srcdir)/'`test-json-tree.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-tree.Tpo $(DEPDIR)/test_lib-test-json-tree.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-tree.c' object='test_lib-test-json-tree.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-tree.o `test -f 'test-json-tree.c' || echo '$(srcdir)/'`test-json-tree.c test_lib-test-json-tree.obj: test-json-tree.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-tree.obj -MD -MP -MF $(DEPDIR)/test_lib-test-json-tree.Tpo -c -o test_lib-test-json-tree.obj `if test -f 'test-json-tree.c'; then $(CYGPATH_W) 'test-json-tree.c'; else $(CYGPATH_W) '$(srcdir)/test-json-tree.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-tree.Tpo $(DEPDIR)/test_lib-test-json-tree.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-tree.c' object='test_lib-test-json-tree.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-tree.obj `if test -f 'test-json-tree.c'; then $(CYGPATH_W) 'test-json-tree.c'; else $(CYGPATH_W) '$(srcdir)/test-json-tree.c'; fi` test_lib-test-llist.o: test-llist.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-llist.o -MD -MP -MF $(DEPDIR)/test_lib-test-llist.Tpo -c -o test_lib-test-llist.o `test -f 'test-llist.c' || echo '$(srcdir)/'`test-llist.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-llist.Tpo $(DEPDIR)/test_lib-test-llist.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-llist.c' object='test_lib-test-llist.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-llist.o `test -f 'test-llist.c' || echo '$(srcdir)/'`test-llist.c test_lib-test-llist.obj: test-llist.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-llist.obj -MD -MP -MF $(DEPDIR)/test_lib-test-llist.Tpo -c -o test_lib-test-llist.obj `if test -f 'test-llist.c'; then $(CYGPATH_W) 'test-llist.c'; else $(CYGPATH_W) '$(srcdir)/test-llist.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-llist.Tpo $(DEPDIR)/test_lib-test-llist.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-llist.c' object='test_lib-test-llist.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-llist.obj `if test -f 'test-llist.c'; then $(CYGPATH_W) 'test-llist.c'; else $(CYGPATH_W) '$(srcdir)/test-llist.c'; fi` test_lib-test-log-throttle.o: test-log-throttle.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-log-throttle.o -MD -MP -MF $(DEPDIR)/test_lib-test-log-throttle.Tpo -c -o test_lib-test-log-throttle.o `test -f 'test-log-throttle.c' || echo '$(srcdir)/'`test-log-throttle.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-log-throttle.Tpo $(DEPDIR)/test_lib-test-log-throttle.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-log-throttle.c' object='test_lib-test-log-throttle.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-log-throttle.o `test -f 'test-log-throttle.c' || echo '$(srcdir)/'`test-log-throttle.c test_lib-test-log-throttle.obj: test-log-throttle.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-log-throttle.obj -MD -MP -MF $(DEPDIR)/test_lib-test-log-throttle.Tpo -c -o test_lib-test-log-throttle.obj `if test -f 'test-log-throttle.c'; then $(CYGPATH_W) 'test-log-throttle.c'; else $(CYGPATH_W) '$(srcdir)/test-log-throttle.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-log-throttle.Tpo $(DEPDIR)/test_lib-test-log-throttle.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-log-throttle.c' object='test_lib-test-log-throttle.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-log-throttle.obj `if test -f 'test-log-throttle.c'; then $(CYGPATH_W) 'test-log-throttle.c'; else $(CYGPATH_W) '$(srcdir)/test-log-throttle.c'; fi` test_lib-test-malloc-overflow.o: test-malloc-overflow.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-malloc-overflow.o -MD -MP -MF $(DEPDIR)/test_lib-test-malloc-overflow.Tpo -c -o test_lib-test-malloc-overflow.o `test -f 'test-malloc-overflow.c' || echo '$(srcdir)/'`test-malloc-overflow.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-malloc-overflow.Tpo $(DEPDIR)/test_lib-test-malloc-overflow.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-malloc-overflow.c' object='test_lib-test-malloc-overflow.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-malloc-overflow.o `test -f 'test-malloc-overflow.c' || echo '$(srcdir)/'`test-malloc-overflow.c test_lib-test-malloc-overflow.obj: test-malloc-overflow.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-malloc-overflow.obj -MD -MP -MF $(DEPDIR)/test_lib-test-malloc-overflow.Tpo -c -o test_lib-test-malloc-overflow.obj `if test -f 'test-malloc-overflow.c'; then $(CYGPATH_W) 'test-malloc-overflow.c'; else $(CYGPATH_W) '$(srcdir)/test-malloc-overflow.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-malloc-overflow.Tpo $(DEPDIR)/test_lib-test-malloc-overflow.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-malloc-overflow.c' object='test_lib-test-malloc-overflow.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-malloc-overflow.obj `if test -f 'test-malloc-overflow.c'; then $(CYGPATH_W) 'test-malloc-overflow.c'; else $(CYGPATH_W) '$(srcdir)/test-malloc-overflow.c'; fi` test_lib-test-mempool.o: test-mempool.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool.o -MD -MP -MF $(DEPDIR)/test_lib-test-mempool.Tpo -c -o test_lib-test-mempool.o `test -f 'test-mempool.c' || echo '$(srcdir)/'`test-mempool.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool.Tpo $(DEPDIR)/test_lib-test-mempool.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool.c' object='test_lib-test-mempool.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool.o `test -f 'test-mempool.c' || echo '$(srcdir)/'`test-mempool.c test_lib-test-mempool.obj: test-mempool.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool.obj -MD -MP -MF $(DEPDIR)/test_lib-test-mempool.Tpo -c -o test_lib-test-mempool.obj `if test -f 'test-mempool.c'; then $(CYGPATH_W) 'test-mempool.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool.Tpo $(DEPDIR)/test_lib-test-mempool.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool.c' object='test_lib-test-mempool.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool.obj `if test -f 'test-mempool.c'; then $(CYGPATH_W) 'test-mempool.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool.c'; fi` test_lib-test-mempool-alloconly.o: test-mempool-alloconly.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-alloconly.o -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo -c -o test_lib-test-mempool-alloconly.o `test -f 'test-mempool-alloconly.c' || echo '$(srcdir)/'`test-mempool-alloconly.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo $(DEPDIR)/test_lib-test-mempool-alloconly.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-alloconly.c' object='test_lib-test-mempool-alloconly.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-alloconly.o `test -f 'test-mempool-alloconly.c' || echo '$(srcdir)/'`test-mempool-alloconly.c test_lib-test-mempool-alloconly.obj: test-mempool-alloconly.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-alloconly.obj -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo -c -o test_lib-test-mempool-alloconly.obj `if test -f 'test-mempool-alloconly.c'; then $(CYGPATH_W) 'test-mempool-alloconly.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-alloconly.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo $(DEPDIR)/test_lib-test-mempool-alloconly.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-alloconly.c' object='test_lib-test-mempool-alloconly.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-alloconly.obj `if test -f 'test-mempool-alloconly.c'; then $(CYGPATH_W) 'test-mempool-alloconly.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-alloconly.c'; fi` test_lib-test-pkcs5.o: test-pkcs5.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-pkcs5.o -MD -MP -MF $(DEPDIR)/test_lib-test-pkcs5.Tpo -c -o test_lib-test-pkcs5.o `test -f 'test-pkcs5.c' || echo '$(srcdir)/'`test-pkcs5.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-pkcs5.Tpo $(DEPDIR)/test_lib-test-pkcs5.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-pkcs5.c' object='test_lib-test-pkcs5.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-pkcs5.o `test -f 'test-pkcs5.c' || echo '$(srcdir)/'`test-pkcs5.c test_lib-test-pkcs5.obj: test-pkcs5.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-pkcs5.obj -MD -MP -MF $(DEPDIR)/test_lib-test-pkcs5.Tpo -c -o test_lib-test-pkcs5.obj `if test -f 'test-pkcs5.c'; then $(CYGPATH_W) 'test-pkcs5.c'; else $(CYGPATH_W) '$(srcdir)/test-pkcs5.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-pkcs5.Tpo $(DEPDIR)/test_lib-test-pkcs5.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-pkcs5.c' object='test_lib-test-pkcs5.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-pkcs5.obj `if test -f 'test-pkcs5.c'; then $(CYGPATH_W) 'test-pkcs5.c'; else $(CYGPATH_W) '$(srcdir)/test-pkcs5.c'; fi` test_lib-test-net.o: test-net.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-net.o -MD -MP -MF $(DEPDIR)/test_lib-test-net.Tpo -c -o test_lib-test-net.o `test -f 'test-net.c' || echo '$(srcdir)/'`test-net.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-net.Tpo $(DEPDIR)/test_lib-test-net.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-net.c' object='test_lib-test-net.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-net.o `test -f 'test-net.c' || echo '$(srcdir)/'`test-net.c test_lib-test-net.obj: test-net.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-net.obj -MD -MP -MF $(DEPDIR)/test_lib-test-net.Tpo -c -o test_lib-test-net.obj `if test -f 'test-net.c'; then $(CYGPATH_W) 'test-net.c'; else $(CYGPATH_W) '$(srcdir)/test-net.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-net.Tpo $(DEPDIR)/test_lib-test-net.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-net.c' object='test_lib-test-net.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-net.obj `if test -f 'test-net.c'; then $(CYGPATH_W) 'test-net.c'; else $(CYGPATH_W) '$(srcdir)/test-net.c'; fi` test_lib-test-numpack.o: test-numpack.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-numpack.o -MD -MP -MF $(DEPDIR)/test_lib-test-numpack.Tpo -c -o test_lib-test-numpack.o `test -f 'test-numpack.c' || echo '$(srcdir)/'`test-numpack.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-numpack.Tpo $(DEPDIR)/test_lib-test-numpack.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-numpack.c' object='test_lib-test-numpack.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-numpack.o `test -f 'test-numpack.c' || echo '$(srcdir)/'`test-numpack.c test_lib-test-numpack.obj: test-numpack.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-numpack.obj -MD -MP -MF $(DEPDIR)/test_lib-test-numpack.Tpo -c -o test_lib-test-numpack.obj `if test -f 'test-numpack.c'; then $(CYGPATH_W) 'test-numpack.c'; else $(CYGPATH_W) '$(srcdir)/test-numpack.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-numpack.Tpo $(DEPDIR)/test_lib-test-numpack.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-numpack.c' object='test_lib-test-numpack.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-numpack.obj `if test -f 'test-numpack.c'; then $(CYGPATH_W) 'test-numpack.c'; else $(CYGPATH_W) '$(srcdir)/test-numpack.c'; fi` test_lib-test-ostream-buffer.o: test-ostream-buffer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-buffer.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-buffer.Tpo -c -o test_lib-test-ostream-buffer.o `test -f 'test-ostream-buffer.c' || echo '$(srcdir)/'`test-ostream-buffer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-buffer.Tpo $(DEPDIR)/test_lib-test-ostream-buffer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-buffer.c' object='test_lib-test-ostream-buffer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-buffer.o `test -f 'test-ostream-buffer.c' || echo '$(srcdir)/'`test-ostream-buffer.c test_lib-test-ostream-buffer.obj: test-ostream-buffer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-buffer.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-buffer.Tpo -c -o test_lib-test-ostream-buffer.obj `if test -f 'test-ostream-buffer.c'; then $(CYGPATH_W) 'test-ostream-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-buffer.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-buffer.Tpo $(DEPDIR)/test_lib-test-ostream-buffer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-buffer.c' object='test_lib-test-ostream-buffer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-buffer.obj `if test -f 'test-ostream-buffer.c'; then $(CYGPATH_W) 'test-ostream-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-buffer.c'; fi` test_lib-test-ostream-escaped.o: test-ostream-escaped.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-escaped.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-escaped.Tpo -c -o test_lib-test-ostream-escaped.o `test -f 'test-ostream-escaped.c' || echo '$(srcdir)/'`test-ostream-escaped.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-escaped.Tpo $(DEPDIR)/test_lib-test-ostream-escaped.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-escaped.c' object='test_lib-test-ostream-escaped.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-escaped.o `test -f 'test-ostream-escaped.c' || echo '$(srcdir)/'`test-ostream-escaped.c test_lib-test-ostream-escaped.obj: test-ostream-escaped.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-escaped.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-escaped.Tpo -c -o test_lib-test-ostream-escaped.obj `if test -f 'test-ostream-escaped.c'; then $(CYGPATH_W) 'test-ostream-escaped.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-escaped.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-escaped.Tpo $(DEPDIR)/test_lib-test-ostream-escaped.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-escaped.c' object='test_lib-test-ostream-escaped.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-escaped.obj `if test -f 'test-ostream-escaped.c'; then $(CYGPATH_W) 'test-ostream-escaped.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-escaped.c'; fi` test_lib-test-ostream-failure-at.o: test-ostream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-failure-at.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo -c -o test_lib-test-ostream-failure-at.o `test -f 'test-ostream-failure-at.c' || echo '$(srcdir)/'`test-ostream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo $(DEPDIR)/test_lib-test-ostream-failure-at.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-failure-at.c' object='test_lib-test-ostream-failure-at.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-failure-at.o `test -f 'test-ostream-failure-at.c' || echo '$(srcdir)/'`test-ostream-failure-at.c test_lib-test-ostream-failure-at.obj: test-ostream-failure-at.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-failure-at.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo -c -o test_lib-test-ostream-failure-at.obj `if test -f 'test-ostream-failure-at.c'; then $(CYGPATH_W) 'test-ostream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-failure-at.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo $(DEPDIR)/test_lib-test-ostream-failure-at.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-failure-at.c' object='test_lib-test-ostream-failure-at.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-failure-at.obj `if test -f 'test-ostream-failure-at.c'; then $(CYGPATH_W) 'test-ostream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-failure-at.c'; fi` test_lib-test-ostream-file.o: test-ostream-file.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-file.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-file.Tpo -c -o test_lib-test-ostream-file.o `test -f 'test-ostream-file.c' || echo '$(srcdir)/'`test-ostream-file.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-file.Tpo $(DEPDIR)/test_lib-test-ostream-file.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-file.c' object='test_lib-test-ostream-file.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-file.o `test -f 'test-ostream-file.c' || echo '$(srcdir)/'`test-ostream-file.c test_lib-test-ostream-file.obj: test-ostream-file.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-file.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-file.Tpo -c -o test_lib-test-ostream-file.obj `if test -f 'test-ostream-file.c'; then $(CYGPATH_W) 'test-ostream-file.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-file.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-file.Tpo $(DEPDIR)/test_lib-test-ostream-file.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-file.c' object='test_lib-test-ostream-file.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-file.obj `if test -f 'test-ostream-file.c'; then $(CYGPATH_W) 'test-ostream-file.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-file.c'; fi` test_lib-test-ostream-multiplex.o: test-ostream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo -c -o test_lib-test-ostream-multiplex.o `test -f 'test-ostream-multiplex.c' || echo '$(srcdir)/'`test-ostream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo $(DEPDIR)/test_lib-test-ostream-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-multiplex.c' object='test_lib-test-ostream-multiplex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-multiplex.o `test -f 'test-ostream-multiplex.c' || echo '$(srcdir)/'`test-ostream-multiplex.c test_lib-test-ostream-multiplex.obj: test-ostream-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo -c -o test_lib-test-ostream-multiplex.obj `if test -f 'test-ostream-multiplex.c'; then $(CYGPATH_W) 'test-ostream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-multiplex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo $(DEPDIR)/test_lib-test-ostream-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-multiplex.c' object='test_lib-test-ostream-multiplex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-multiplex.obj `if test -f 'test-ostream-multiplex.c'; then $(CYGPATH_W) 'test-ostream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-multiplex.c'; fi` test_lib-test-multiplex.o: test-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-multiplex.Tpo -c -o test_lib-test-multiplex.o `test -f 'test-multiplex.c' || echo '$(srcdir)/'`test-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-multiplex.Tpo $(DEPDIR)/test_lib-test-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-multiplex.c' object='test_lib-test-multiplex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-multiplex.o `test -f 'test-multiplex.c' || echo '$(srcdir)/'`test-multiplex.c test_lib-test-multiplex.obj: test-multiplex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-multiplex.Tpo -c -o test_lib-test-multiplex.obj `if test -f 'test-multiplex.c'; then $(CYGPATH_W) 'test-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-multiplex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-multiplex.Tpo $(DEPDIR)/test_lib-test-multiplex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-multiplex.c' object='test_lib-test-multiplex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-multiplex.obj `if test -f 'test-multiplex.c'; then $(CYGPATH_W) 'test-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-multiplex.c'; fi` test_lib-test-primes.o: test-primes.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-primes.o -MD -MP -MF $(DEPDIR)/test_lib-test-primes.Tpo -c -o test_lib-test-primes.o `test -f 'test-primes.c' || echo '$(srcdir)/'`test-primes.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-primes.Tpo $(DEPDIR)/test_lib-test-primes.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-primes.c' object='test_lib-test-primes.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-primes.o `test -f 'test-primes.c' || echo '$(srcdir)/'`test-primes.c test_lib-test-primes.obj: test-primes.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-primes.obj -MD -MP -MF $(DEPDIR)/test_lib-test-primes.Tpo -c -o test_lib-test-primes.obj `if test -f 'test-primes.c'; then $(CYGPATH_W) 'test-primes.c'; else $(CYGPATH_W) '$(srcdir)/test-primes.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-primes.Tpo $(DEPDIR)/test_lib-test-primes.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-primes.c' object='test_lib-test-primes.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-primes.obj `if test -f 'test-primes.c'; then $(CYGPATH_W) 'test-primes.c'; else $(CYGPATH_W) '$(srcdir)/test-primes.c'; fi` test_lib-test-printf-format-fix.o: test-printf-format-fix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-printf-format-fix.o -MD -MP -MF $(DEPDIR)/test_lib-test-printf-format-fix.Tpo -c -o test_lib-test-printf-format-fix.o `test -f 'test-printf-format-fix.c' || echo '$(srcdir)/'`test-printf-format-fix.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-printf-format-fix.Tpo $(DEPDIR)/test_lib-test-printf-format-fix.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-printf-format-fix.c' object='test_lib-test-printf-format-fix.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-printf-format-fix.o `test -f 'test-printf-format-fix.c' || echo '$(srcdir)/'`test-printf-format-fix.c test_lib-test-printf-format-fix.obj: test-printf-format-fix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-printf-format-fix.obj -MD -MP -MF $(DEPDIR)/test_lib-test-printf-format-fix.Tpo -c -o test_lib-test-printf-format-fix.obj `if test -f 'test-printf-format-fix.c'; then $(CYGPATH_W) 'test-printf-format-fix.c'; else $(CYGPATH_W) '$(srcdir)/test-printf-format-fix.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-printf-format-fix.Tpo $(DEPDIR)/test_lib-test-printf-format-fix.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-printf-format-fix.c' object='test_lib-test-printf-format-fix.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-printf-format-fix.obj `if test -f 'test-printf-format-fix.c'; then $(CYGPATH_W) 'test-printf-format-fix.c'; else $(CYGPATH_W) '$(srcdir)/test-printf-format-fix.c'; fi` test_lib-test-priorityq.o: test-priorityq.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-priorityq.o -MD -MP -MF $(DEPDIR)/test_lib-test-priorityq.Tpo -c -o test_lib-test-priorityq.o `test -f 'test-priorityq.c' || echo '$(srcdir)/'`test-priorityq.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-priorityq.Tpo $(DEPDIR)/test_lib-test-priorityq.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-priorityq.c' object='test_lib-test-priorityq.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-priorityq.o `test -f 'test-priorityq.c' || echo '$(srcdir)/'`test-priorityq.c test_lib-test-priorityq.obj: test-priorityq.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-priorityq.obj -MD -MP -MF $(DEPDIR)/test_lib-test-priorityq.Tpo -c -o test_lib-test-priorityq.obj `if test -f 'test-priorityq.c'; then $(CYGPATH_W) 'test-priorityq.c'; else $(CYGPATH_W) '$(srcdir)/test-priorityq.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-priorityq.Tpo $(DEPDIR)/test_lib-test-priorityq.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-priorityq.c' object='test_lib-test-priorityq.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-priorityq.obj `if test -f 'test-priorityq.c'; then $(CYGPATH_W) 'test-priorityq.c'; else $(CYGPATH_W) '$(srcdir)/test-priorityq.c'; fi` test_lib-test-seq-range-array.o: test-seq-range-array.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-range-array.o -MD -MP -MF $(DEPDIR)/test_lib-test-seq-range-array.Tpo -c -o test_lib-test-seq-range-array.o `test -f 'test-seq-range-array.c' || echo '$(srcdir)/'`test-seq-range-array.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-range-array.Tpo $(DEPDIR)/test_lib-test-seq-range-array.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-range-array.c' object='test_lib-test-seq-range-array.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-range-array.o `test -f 'test-seq-range-array.c' || echo '$(srcdir)/'`test-seq-range-array.c test_lib-test-seq-range-array.obj: test-seq-range-array.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-range-array.obj -MD -MP -MF $(DEPDIR)/test_lib-test-seq-range-array.Tpo -c -o test_lib-test-seq-range-array.obj `if test -f 'test-seq-range-array.c'; then $(CYGPATH_W) 'test-seq-range-array.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-range-array.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-range-array.Tpo $(DEPDIR)/test_lib-test-seq-range-array.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-range-array.c' object='test_lib-test-seq-range-array.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-range-array.obj `if test -f 'test-seq-range-array.c'; then $(CYGPATH_W) 'test-seq-range-array.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-range-array.c'; fi` test_lib-test-str.o: test-str.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str.o -MD -MP -MF $(DEPDIR)/test_lib-test-str.Tpo -c -o test_lib-test-str.o `test -f 'test-str.c' || echo '$(srcdir)/'`test-str.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str.Tpo $(DEPDIR)/test_lib-test-str.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str.c' object='test_lib-test-str.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str.o `test -f 'test-str.c' || echo '$(srcdir)/'`test-str.c test_lib-test-str.obj: test-str.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str.Tpo -c -o test_lib-test-str.obj `if test -f 'test-str.c'; then $(CYGPATH_W) 'test-str.c'; else $(CYGPATH_W) '$(srcdir)/test-str.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str.Tpo $(DEPDIR)/test_lib-test-str.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str.c' object='test_lib-test-str.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str.obj `if test -f 'test-str.c'; then $(CYGPATH_W) 'test-str.c'; else $(CYGPATH_W) '$(srcdir)/test-str.c'; fi` test_lib-test-strescape.o: test-strescape.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strescape.o -MD -MP -MF $(DEPDIR)/test_lib-test-strescape.Tpo -c -o test_lib-test-strescape.o `test -f 'test-strescape.c' || echo '$(srcdir)/'`test-strescape.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strescape.Tpo $(DEPDIR)/test_lib-test-strescape.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strescape.c' object='test_lib-test-strescape.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strescape.o `test -f 'test-strescape.c' || echo '$(srcdir)/'`test-strescape.c test_lib-test-strescape.obj: test-strescape.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strescape.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strescape.Tpo -c -o test_lib-test-strescape.obj `if test -f 'test-strescape.c'; then $(CYGPATH_W) 'test-strescape.c'; else $(CYGPATH_W) '$(srcdir)/test-strescape.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strescape.Tpo $(DEPDIR)/test_lib-test-strescape.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strescape.c' object='test_lib-test-strescape.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strescape.obj `if test -f 'test-strescape.c'; then $(CYGPATH_W) 'test-strescape.c'; else $(CYGPATH_W) '$(srcdir)/test-strescape.c'; fi` test_lib-test-strfuncs.o: test-strfuncs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strfuncs.o -MD -MP -MF $(DEPDIR)/test_lib-test-strfuncs.Tpo -c -o test_lib-test-strfuncs.o `test -f 'test-strfuncs.c' || echo '$(srcdir)/'`test-strfuncs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strfuncs.Tpo $(DEPDIR)/test_lib-test-strfuncs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strfuncs.c' object='test_lib-test-strfuncs.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strfuncs.o `test -f 'test-strfuncs.c' || echo '$(srcdir)/'`test-strfuncs.c test_lib-test-strfuncs.obj: test-strfuncs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strfuncs.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strfuncs.Tpo -c -o test_lib-test-strfuncs.obj `if test -f 'test-strfuncs.c'; then $(CYGPATH_W) 'test-strfuncs.c'; else $(CYGPATH_W) '$(srcdir)/test-strfuncs.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strfuncs.Tpo $(DEPDIR)/test_lib-test-strfuncs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strfuncs.c' object='test_lib-test-strfuncs.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strfuncs.obj `if test -f 'test-strfuncs.c'; then $(CYGPATH_W) 'test-strfuncs.c'; else $(CYGPATH_W) '$(srcdir)/test-strfuncs.c'; fi` test_lib-test-strnum.o: test-strnum.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strnum.o -MD -MP -MF $(DEPDIR)/test_lib-test-strnum.Tpo -c -o test_lib-test-strnum.o `test -f 'test-strnum.c' || echo '$(srcdir)/'`test-strnum.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strnum.Tpo $(DEPDIR)/test_lib-test-strnum.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strnum.c' object='test_lib-test-strnum.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strnum.o `test -f 'test-strnum.c' || echo '$(srcdir)/'`test-strnum.c test_lib-test-strnum.obj: test-strnum.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strnum.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strnum.Tpo -c -o test_lib-test-strnum.obj `if test -f 'test-strnum.c'; then $(CYGPATH_W) 'test-strnum.c'; else $(CYGPATH_W) '$(srcdir)/test-strnum.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strnum.Tpo $(DEPDIR)/test_lib-test-strnum.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strnum.c' object='test_lib-test-strnum.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strnum.obj `if test -f 'test-strnum.c'; then $(CYGPATH_W) 'test-strnum.c'; else $(CYGPATH_W) '$(srcdir)/test-strnum.c'; fi` test_lib-test-str-find.o: test-str-find.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-find.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-find.Tpo -c -o test_lib-test-str-find.o `test -f 'test-str-find.c' || echo '$(srcdir)/'`test-str-find.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-find.Tpo $(DEPDIR)/test_lib-test-str-find.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-find.c' object='test_lib-test-str-find.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-find.o `test -f 'test-str-find.c' || echo '$(srcdir)/'`test-str-find.c test_lib-test-str-find.obj: test-str-find.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-find.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-find.Tpo -c -o test_lib-test-str-find.obj `if test -f 'test-str-find.c'; then $(CYGPATH_W) 'test-str-find.c'; else $(CYGPATH_W) '$(srcdir)/test-str-find.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-find.Tpo $(DEPDIR)/test_lib-test-str-find.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-find.c' object='test_lib-test-str-find.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-find.obj `if test -f 'test-str-find.c'; then $(CYGPATH_W) 'test-str-find.c'; else $(CYGPATH_W) '$(srcdir)/test-str-find.c'; fi` test_lib-test-str-sanitize.o: test-str-sanitize.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-sanitize.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-sanitize.Tpo -c -o test_lib-test-str-sanitize.o `test -f 'test-str-sanitize.c' || echo '$(srcdir)/'`test-str-sanitize.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-sanitize.Tpo $(DEPDIR)/test_lib-test-str-sanitize.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-sanitize.c' object='test_lib-test-str-sanitize.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-sanitize.o `test -f 'test-str-sanitize.c' || echo '$(srcdir)/'`test-str-sanitize.c test_lib-test-str-sanitize.obj: test-str-sanitize.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-sanitize.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-sanitize.Tpo -c -o test_lib-test-str-sanitize.obj `if test -f 'test-str-sanitize.c'; then $(CYGPATH_W) 'test-str-sanitize.c'; else $(CYGPATH_W) '$(srcdir)/test-str-sanitize.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-sanitize.Tpo $(DEPDIR)/test_lib-test-str-sanitize.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-sanitize.c' object='test_lib-test-str-sanitize.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-sanitize.obj `if test -f 'test-str-sanitize.c'; then $(CYGPATH_W) 'test-str-sanitize.c'; else $(CYGPATH_W) '$(srcdir)/test-str-sanitize.c'; fi` test_lib-test-str-table.o: test-str-table.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-table.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-table.Tpo -c -o test_lib-test-str-table.o `test -f 'test-str-table.c' || echo '$(srcdir)/'`test-str-table.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-table.Tpo $(DEPDIR)/test_lib-test-str-table.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-table.c' object='test_lib-test-str-table.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-table.o `test -f 'test-str-table.c' || echo '$(srcdir)/'`test-str-table.c test_lib-test-str-table.obj: test-str-table.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-table.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-table.Tpo -c -o test_lib-test-str-table.obj `if test -f 'test-str-table.c'; then $(CYGPATH_W) 'test-str-table.c'; else $(CYGPATH_W) '$(srcdir)/test-str-table.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-table.Tpo $(DEPDIR)/test_lib-test-str-table.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-table.c' object='test_lib-test-str-table.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-table.obj `if test -f 'test-str-table.c'; then $(CYGPATH_W) 'test-str-table.c'; else $(CYGPATH_W) '$(srcdir)/test-str-table.c'; fi` test_lib-test-time-util.o: test-time-util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-time-util.o -MD -MP -MF $(DEPDIR)/test_lib-test-time-util.Tpo -c -o test_lib-test-time-util.o `test -f 'test-time-util.c' || echo '$(srcdir)/'`test-time-util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-time-util.Tpo $(DEPDIR)/test_lib-test-time-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-time-util.c' object='test_lib-test-time-util.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-time-util.o `test -f 'test-time-util.c' || echo '$(srcdir)/'`test-time-util.c test_lib-test-time-util.obj: test-time-util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-time-util.obj -MD -MP -MF $(DEPDIR)/test_lib-test-time-util.Tpo -c -o test_lib-test-time-util.obj `if test -f 'test-time-util.c'; then $(CYGPATH_W) 'test-time-util.c'; else $(CYGPATH_W) '$(srcdir)/test-time-util.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-time-util.Tpo $(DEPDIR)/test_lib-test-time-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-time-util.c' object='test_lib-test-time-util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-time-util.obj `if test -f 'test-time-util.c'; then $(CYGPATH_W) 'test-time-util.c'; else $(CYGPATH_W) '$(srcdir)/test-time-util.c'; fi` test_lib-test-timing.o: test-timing.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-timing.o -MD -MP -MF $(DEPDIR)/test_lib-test-timing.Tpo -c -o test_lib-test-timing.o `test -f 'test-timing.c' || echo '$(srcdir)/'`test-timing.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-timing.Tpo $(DEPDIR)/test_lib-test-timing.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-timing.c' object='test_lib-test-timing.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-timing.o `test -f 'test-timing.c' || echo '$(srcdir)/'`test-timing.c test_lib-test-timing.obj: test-timing.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-timing.obj -MD -MP -MF $(DEPDIR)/test_lib-test-timing.Tpo -c -o test_lib-test-timing.obj `if test -f 'test-timing.c'; then $(CYGPATH_W) 'test-timing.c'; else $(CYGPATH_W) '$(srcdir)/test-timing.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-timing.Tpo $(DEPDIR)/test_lib-test-timing.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-timing.c' object='test_lib-test-timing.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-timing.obj `if test -f 'test-timing.c'; then $(CYGPATH_W) 'test-timing.c'; else $(CYGPATH_W) '$(srcdir)/test-timing.c'; fi` test_lib-test-unichar.o: test-unichar.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-unichar.o -MD -MP -MF $(DEPDIR)/test_lib-test-unichar.Tpo -c -o test_lib-test-unichar.o `test -f 'test-unichar.c' || echo '$(srcdir)/'`test-unichar.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-unichar.Tpo $(DEPDIR)/test_lib-test-unichar.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-unichar.c' object='test_lib-test-unichar.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-unichar.o `test -f 'test-unichar.c' || echo '$(srcdir)/'`test-unichar.c test_lib-test-unichar.obj: test-unichar.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-unichar.obj -MD -MP -MF $(DEPDIR)/test_lib-test-unichar.Tpo -c -o test_lib-test-unichar.obj `if test -f 'test-unichar.c'; then $(CYGPATH_W) 'test-unichar.c'; else $(CYGPATH_W) '$(srcdir)/test-unichar.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-unichar.Tpo $(DEPDIR)/test_lib-test-unichar.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-unichar.c' object='test_lib-test-unichar.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-unichar.obj `if test -f 'test-unichar.c'; then $(CYGPATH_W) 'test-unichar.c'; else $(CYGPATH_W) '$(srcdir)/test-unichar.c'; fi` test_lib-test-utc-mktime.o: test-utc-mktime.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-utc-mktime.o -MD -MP -MF $(DEPDIR)/test_lib-test-utc-mktime.Tpo -c -o test_lib-test-utc-mktime.o `test -f 'test-utc-mktime.c' || echo '$(srcdir)/'`test-utc-mktime.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-utc-mktime.Tpo $(DEPDIR)/test_lib-test-utc-mktime.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-utc-mktime.c' object='test_lib-test-utc-mktime.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-utc-mktime.o `test -f 'test-utc-mktime.c' || echo '$(srcdir)/'`test-utc-mktime.c test_lib-test-utc-mktime.obj: test-utc-mktime.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-utc-mktime.obj -MD -MP -MF $(DEPDIR)/test_lib-test-utc-mktime.Tpo -c -o test_lib-test-utc-mktime.obj `if test -f 'test-utc-mktime.c'; then $(CYGPATH_W) 'test-utc-mktime.c'; else $(CYGPATH_W) '$(srcdir)/test-utc-mktime.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-utc-mktime.Tpo $(DEPDIR)/test_lib-test-utc-mktime.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-utc-mktime.c' object='test_lib-test-utc-mktime.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-utc-mktime.obj `if test -f 'test-utc-mktime.c'; then $(CYGPATH_W) 'test-utc-mktime.c'; else $(CYGPATH_W) '$(srcdir)/test-utc-mktime.c'; fi` test_lib-test-var-expand.o: test-var-expand.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-var-expand.o -MD -MP -MF $(DEPDIR)/test_lib-test-var-expand.Tpo -c -o test_lib-test-var-expand.o `test -f 'test-var-expand.c' || echo '$(srcdir)/'`test-var-expand.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-var-expand.Tpo $(DEPDIR)/test_lib-test-var-expand.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-var-expand.c' object='test_lib-test-var-expand.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-var-expand.o `test -f 'test-var-expand.c' || echo '$(srcdir)/'`test-var-expand.c test_lib-test-var-expand.obj: test-var-expand.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-var-expand.obj -MD -MP -MF $(DEPDIR)/test_lib-test-var-expand.Tpo -c -o test_lib-test-var-expand.obj `if test -f 'test-var-expand.c'; then $(CYGPATH_W) 'test-var-expand.c'; else $(CYGPATH_W) '$(srcdir)/test-var-expand.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-var-expand.Tpo $(DEPDIR)/test_lib-test-var-expand.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-var-expand.c' object='test_lib-test-var-expand.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-var-expand.obj `if test -f 'test-var-expand.c'; then $(CYGPATH_W) 'test-var-expand.c'; else $(CYGPATH_W) '$(srcdir)/test-var-expand.c'; fi` test_lib-test-wildcard-match.o: test-wildcard-match.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-wildcard-match.o -MD -MP -MF $(DEPDIR)/test_lib-test-wildcard-match.Tpo -c -o test_lib-test-wildcard-match.o `test -f 'test-wildcard-match.c' || echo '$(srcdir)/'`test-wildcard-match.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-wildcard-match.Tpo $(DEPDIR)/test_lib-test-wildcard-match.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-wildcard-match.c' object='test_lib-test-wildcard-match.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-wildcard-match.o `test -f 'test-wildcard-match.c' || echo '$(srcdir)/'`test-wildcard-match.c test_lib-test-wildcard-match.obj: test-wildcard-match.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-wildcard-match.obj -MD -MP -MF $(DEPDIR)/test_lib-test-wildcard-match.Tpo -c -o test_lib-test-wildcard-match.obj `if test -f 'test-wildcard-match.c'; then $(CYGPATH_W) 'test-wildcard-match.c'; else $(CYGPATH_W) '$(srcdir)/test-wildcard-match.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-wildcard-match.Tpo $(DEPDIR)/test_lib-test-wildcard-match.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-wildcard-match.c' object='test_lib-test-wildcard-match.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-wildcard-match.obj `if test -f 'test-wildcard-match.c'; then $(CYGPATH_W) 'test-wildcard-match.c'; else $(CYGPATH_W) '$(srcdir)/test-wildcard-match.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile UnicodeData.txt: test -f UnicodeData.txt || wget https://dovecot.org/res/UnicodeData.txt $(srcdir)/unicodemap.c: unicodemap.pl UnicodeData.txt perl $(srcdir)/unicodemap.pl < UnicodeData.txt > $@ check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib/ostream-multiplex.h0000644000175000017500000000045213165463543015541 00000000000000#ifndef OSTREAM_MULTIPLEX #define OSTREAM_MULTIPLEX 1 struct ostream *o_stream_create_multiplex(struct ostream *parent, size_t bufsize); struct ostream *o_stream_multiplex_add_channel(struct ostream *stream, uint8_t cid); uint8_t o_stream_multiplex_get_channel_id(struct ostream *stream); #endif dovecot-2.2.33.2/src/lib/md5.h0000644000175000017500000000152713123174404012524 00000000000000/* * This is an OpenSSL-compatible implementation of the RSA Data Security, * Inc. MD5 Message-Digest Algorithm. * * Written by Solar Designer in 2001, and placed in * the public domain. See md5.c for more information. */ #ifndef MD5_H #define MD5_H #include "hash-method.h" #define MD5_RESULTLEN (128/8) struct md5_context { uint_fast32_t lo, hi; uint_fast32_t a, b, c, d; unsigned char buffer[64]; uint_fast32_t block[MD5_RESULTLEN]; }; void md5_init(struct md5_context *ctx); void md5_update(struct md5_context *ctx, const void *data, size_t size); void md5_final(struct md5_context *ctx, unsigned char result[STATIC_ARRAY MD5_RESULTLEN]); void md5_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY MD5_RESULTLEN]); extern const struct hash_method hash_method_md5; #endif dovecot-2.2.33.2/src/lib/ioloop-notify-fd.h0000644000175000017500000000121613123174404015230 00000000000000#ifndef IOLOOP_NOTIFY_FD_H #define IOLOOP_NOTIFY_FD_H /* common notify code for fd-based notifications (dnotify, inotify) */ struct io_notify { struct io io; /* use a doubly linked list so that io_remove() is quick */ struct io_notify *prev, *next; int fd; }; struct ioloop_notify_fd_context { struct io_notify *notifies; }; struct io * io_notify_fd_add(struct ioloop_notify_fd_context *ctx, int fd, io_callback_t *callback, void *context) ATTR_NULL(4); void io_notify_fd_free(struct ioloop_notify_fd_context *ctx, struct io_notify *io); struct io_notify * io_notify_fd_find(struct ioloop_notify_fd_context *ctx, int fd); #endif dovecot-2.2.33.2/src/lib/test-numpack.c0000644000175000017500000000373213165463624014460 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "numpack.h" static struct test { uint64_t input; uint8_t output[10]; unsigned int output_size; } enc_tests[] = { { 0xffffffff, { 0xff, 0xff, 0xff, 0xff, 0xf }, 5 }, { 0, { 0 }, 1 }, { 0x7f, { 0x7f }, 1 }, { 0x80, { 0x80, 1 }, 2 }, { 0x81, { 0x81, 1 }, 2 }, { 0xdeadbeefcafe, { 0xfe, 0x95, 0xbf, 0xf7, 0xdb, 0xd5, 0x37 }, 7 }, { 0xffffffffffffffff, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }, 10 }, { 0xfffffffe, { 0xfe, 0xff, 0xff, 0xff, 0xf }, 5 }, }; static struct fail { uint8_t input[11]; unsigned int input_size; } dec_fails[] = { { { 0 }, 0 }, /* has no termination byte */ { { 0x80 }, 1 }, /* ditto */ { { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, 10 }, /* ditto*/ { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2 }, 10 }, /* overflow */ { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, 11 }, /* ditto */ }; void test_numpack(void) { buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 32); unsigned int i; const uint8_t *p, *end; uint64_t num; uint64_t magic=0x9669699669969669; test_begin("numpack (good)"); for (i = 0; i < N_ELEMENTS(enc_tests); i++) { buffer_set_used_size(buf, 0); numpack_encode(buf, enc_tests[i].input); test_assert_idx(buf->used == enc_tests[i].output_size, i); test_assert_idx(memcmp(buf->data, enc_tests[i].output, enc_tests[i].output_size) == 0, i); p = buf->data; end = p + buf->used; test_assert_idx(numpack_decode(&p, end, &num) == 0, i); test_assert_idx(num == enc_tests[i].input, i); } test_end(); test_begin("numpack (bad)"); for (i = 0; i < N_ELEMENTS(dec_fails); i++) { p = dec_fails[i].input; end = p + dec_fails[i].input_size; num = magic; test_assert_idx(numpack_decode(&p, end, &num) == -1, i); test_assert_idx(p == dec_fails[i].input && num == magic, i); } test_end(); } dovecot-2.2.33.2/src/lib/hash2.h0000644000175000017500000000337713123174404013051 00000000000000#ifndef HASH2_H #define HASH2_H struct hash2_iter { struct hash2_value *value, *next_value; unsigned int key_hash; }; /* Returns hash code for the key. */ typedef unsigned int hash2_key_callback_t(const void *key); /* Returns TRUE if the key matches the value. */ typedef bool hash2_cmp_callback_t(const void *key, const void *value, void *context); /* Create a new hash table. If initial_size is 0, the default value is used. */ struct hash2_table * hash2_create(unsigned int initial_size, unsigned int value_size, hash2_key_callback_t *key_hash_cb, hash2_cmp_callback_t *key_compare_cb, void *context) ATTR_NULL(5); void hash2_destroy(struct hash2_table **hash); /* Remove all nodes from hash table. */ void hash2_clear(struct hash2_table *hash); void *hash2_lookup(const struct hash2_table *hash, const void *key) ATTR_PURE; /* Iterate through all nodes with the given hash. iter must initially be zero-filled. */ void *hash2_iterate(const struct hash2_table *hash, unsigned int key_hash, struct hash2_iter *iter); /* Insert node to the hash table and returns pointer to the value that can be written to. Assumes it doesn't already exist (or that a duplicate entry is wanted). */ void *hash2_insert(struct hash2_table *hash, const void *key); /* Like hash2_insert(), but insert directly using a hash. */ void *hash2_insert_hash(struct hash2_table *hash, unsigned int key_hash); /* Remove a node. */ void hash2_remove(struct hash2_table *hash, const void *key); /* Remove the last node iterator returned. Iterating continues from the next node. */ void hash2_remove_iter(struct hash2_table *hash, struct hash2_iter *iter); /* Return the number of nodes in hash table. */ unsigned int hash2_count(const struct hash2_table *hash) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/connection.c0000644000175000017500000003016013165463624014177 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "istream-unix.h" #include "ostream.h" #include "ostream-unix.h" #include "iostream.h" #include "net.h" #include "strescape.h" #include "llist.h" #include "time-util.h" #include "connection.h" #include static void connection_idle_timeout(struct connection *conn) { conn->disconnect_reason = CONNECTION_DISCONNECT_IDLE_TIMEOUT; conn->list->v.destroy(conn); } static void connection_connect_timeout(struct connection *conn) { conn->disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT; conn->list->v.destroy(conn); } void connection_input_default(struct connection *conn) { const char *line; struct istream *input; struct ostream *output; int ret = 0; switch (connection_input_read(conn)) { case -1: return; case 0: /* allow calling this function for buffered input */ case 1: break; default: i_unreached(); } input = conn->input; output = conn->output; i_stream_ref(input); if (output != NULL) { o_stream_ref(output); o_stream_cork(output); } while (!input->closed && (line = i_stream_next_line(input)) != NULL) { T_BEGIN { ret = conn->list->v.input_line(conn, line); } T_END; if (ret <= 0) break; } if (output != NULL) { o_stream_uncork(output); o_stream_unref(&output); } if (ret < 0 && !input->closed) { conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT; conn->list->v.destroy(conn); } i_stream_unref(&input); } int connection_verify_version(struct connection *conn, const char *const *args) { unsigned int recv_major_version; /* VERSION service_name major version minor version */ if (str_array_length(args) != 4 || strcmp(args[0], "VERSION") != 0 || str_to_uint(args[2], &recv_major_version) < 0 || str_to_uint(args[3], &conn->minor_version) < 0) { i_error("%s didn't reply with a valid VERSION line", conn->name); return -1; } if (strcmp(args[1], conn->list->set.service_name_in) != 0) { i_error("%s: Connected to wrong socket type. " "We want '%s', but received '%s'", conn->name, conn->list->set.service_name_in, args[1]); return -1; } if (recv_major_version != conn->list->set.major_version) { i_error("%s: Socket supports major version %u, " "but we support only %u (mixed old and new binaries?)", conn->name, recv_major_version, conn->list->set.major_version); return -1; } return 0; } int connection_input_line_default(struct connection *conn, const char *line) { const char *const *args; args = t_strsplit_tabescaped(line); if (!conn->version_received) { if (connection_verify_version(conn, args) < 0) return -1; conn->version_received = TRUE; return 1; } if (args[0] == NULL && !conn->list->set.allow_empty_args_input) { i_error("%s: Unexpectedly received empty line", conn->name); return -1; } return conn->list->v.input_args(conn, args); } static void connection_init_streams(struct connection *conn) { const struct connection_settings *set = &conn->list->set; i_assert(conn->io == NULL); i_assert(conn->input == NULL); i_assert(conn->output == NULL); i_assert(conn->to == NULL); conn->version_received = set->major_version == 0; if (set->input_max_size != 0) { if (conn->unix_socket) conn->input = i_stream_create_unix(conn->fd_in, set->input_max_size); else conn->input = i_stream_create_fd(conn->fd_in, set->input_max_size, FALSE); i_stream_set_name(conn->input, conn->name); conn->io = io_add_istream(conn->input, *conn->list->v.input, conn); } else { conn->io = io_add(conn->fd_in, IO_READ, *conn->list->v.input, conn); } if (set->output_max_size != 0) { if (conn->unix_socket) conn->output = o_stream_create_unix(conn->fd_out, set->output_max_size); else conn->output = o_stream_create_fd(conn->fd_out, set->output_max_size, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_set_name(conn->output, conn->name); } if (set->input_idle_timeout_secs != 0) { conn->to = timeout_add(set->input_idle_timeout_secs*1000, connection_idle_timeout, conn); } if (set->major_version != 0 && !set->dont_send_version) { o_stream_nsend_str(conn->output, t_strdup_printf( "VERSION\t%s\t%u\t%u\n", set->service_name_out, set->major_version, set->minor_version)); } } static void connection_client_connected(struct connection *conn, bool success) { i_assert(conn->list->set.client); conn->connect_finished = ioloop_timeval; if (success) connection_init_streams(conn); if (conn->list->v.client_connected != NULL) conn->list->v.client_connected(conn, success); if (!success) { conn->disconnect_reason = CONNECTION_DISCONNECT_CONN_CLOSED; conn->list->v.destroy(conn); } } void connection_init_server(struct connection_list *list, struct connection *conn, const char *name, int fd_in, int fd_out) { i_assert(name != NULL); i_assert(!list->set.client); conn->list = list; conn->name = i_strdup(name); conn->fd_in = fd_in; conn->fd_out = fd_out; connection_init_streams(conn); DLLIST_PREPEND(&list->connections, conn); list->connections_count++; } void connection_init_client_ip(struct connection_list *list, struct connection *conn, const struct ip_addr *ip, in_port_t port) { i_assert(list->set.client); conn->fd_in = conn->fd_out = -1; conn->list = list; conn->name = i_strdup_printf("%s:%u", net_ip2addr(ip), port); conn->ip = *ip; conn->port = port; DLLIST_PREPEND(&list->connections, conn); list->connections_count++; } void connection_init_client_unix(struct connection_list *list, struct connection *conn, const char *path) { i_assert(list->set.client); conn->fd_in = conn->fd_out = -1; conn->list = list; conn->name = i_strdup(path); conn->unix_socket = TRUE; DLLIST_PREPEND(&list->connections, conn); list->connections_count++; } void connection_init_from_streams(struct connection_list *list, struct connection *conn, const char *name, struct istream *input, struct ostream *output) { i_assert(name != NULL); conn->list = list; conn->name = i_strdup(name); conn->fd_in = i_stream_get_fd(input); conn->fd_out = o_stream_get_fd(output); i_assert(conn->fd_in >= 0); i_assert(conn->fd_out >= 0); i_assert(conn->io == NULL); i_assert(conn->input == NULL); i_assert(conn->output == NULL); i_assert(conn->to == NULL); conn->input = input; i_stream_ref(conn->input); i_stream_set_name(conn->input, conn->name); conn->output = output; o_stream_ref(conn->output); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_set_name(conn->output, conn->name); conn->io = io_add_istream(conn->input, *list->v.input, conn); DLLIST_PREPEND(&list->connections, conn); list->connections_count++; if (list->v.client_connected != NULL) list->v.client_connected(conn, TRUE); } static void connection_socket_connected(struct connection *conn) { io_remove(&conn->io); if (conn->to != NULL) timeout_remove(&conn->to); errno = net_geterror(conn->fd_in); connection_client_connected(conn, errno == 0); } int connection_client_connect(struct connection *conn) { const struct connection_settings *set = &conn->list->set; int fd; i_assert(conn->list->set.client); i_assert(conn->fd_in == -1); if (conn->port != 0) fd = net_connect_ip(&conn->ip, conn->port, NULL); else if (conn->list->set.unix_client_connect_msecs == 0) fd = net_connect_unix(conn->name); else fd = net_connect_unix_with_retries(conn->name, conn->list->set.unix_client_connect_msecs); if (fd == -1) return -1; conn->fd_in = conn->fd_out = fd; conn->connect_started = ioloop_timeval; if (conn->port != 0 || conn->list->set.delayed_unix_client_connected_callback) { conn->io = io_add(conn->fd_out, IO_WRITE, connection_socket_connected, conn); if (set->client_connect_timeout_msecs != 0) { conn->to = timeout_add(set->client_connect_timeout_msecs, connection_connect_timeout, conn); } } else { connection_client_connected(conn, TRUE); } return 0; } void connection_disconnect(struct connection *conn) { conn->last_input = 0; i_zero(&conn->last_input_tv); if (conn->to != NULL) timeout_remove(&conn->to); if (conn->io != NULL) io_remove(&conn->io); if (conn->input != NULL) { i_stream_close(conn->input); i_stream_destroy(&conn->input); } if (conn->output != NULL) { o_stream_close(conn->output); o_stream_destroy(&conn->output); } fd_close_maybe_stdio(&conn->fd_in, &conn->fd_out); } void connection_deinit(struct connection *conn) { i_assert(conn->list->connections_count > 0); conn->list->connections_count--; DLLIST_REMOVE(&conn->list->connections, conn); connection_disconnect(conn); i_free(conn->name); } int connection_input_read(struct connection *conn) { conn->last_input = ioloop_time; conn->last_input_tv = ioloop_timeval; if (conn->to != NULL) timeout_reset(conn->to); switch (i_stream_read(conn->input)) { case -2: /* buffer full */ switch (conn->list->set.input_full_behavior) { case CONNECTION_BEHAVIOR_DESTROY: conn->disconnect_reason = CONNECTION_DISCONNECT_BUFFER_FULL; conn->list->v.destroy(conn); return -1; case CONNECTION_BEHAVIOR_ALLOW: return -2; } i_unreached(); case -1: /* disconnected */ conn->disconnect_reason = CONNECTION_DISCONNECT_CONN_CLOSED; conn->list->v.destroy(conn); return -1; case 0: /* nothing new read */ return 0; default: /* something was read */ return 1; } } const char *connection_disconnect_reason(struct connection *conn) { switch (conn->disconnect_reason) { case CONNECTION_DISCONNECT_DEINIT: return "Deinitializing"; case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: { unsigned int msecs = conn->list->set.client_connect_timeout_msecs; return t_strdup_printf("connect() timed out in %u.%03u secs", msecs/1000, msecs%1000); } case CONNECTION_DISCONNECT_IDLE_TIMEOUT: return "Idle timeout"; case CONNECTION_DISCONNECT_CONN_CLOSED: if (conn->input == NULL) return t_strdup_printf("connect() failed: %m"); /* fall through */ case CONNECTION_DISCONNECT_NOT: case CONNECTION_DISCONNECT_BUFFER_FULL: return io_stream_get_disconnect_reason(conn->input, conn->output); } i_unreached(); } const char *connection_input_timeout_reason(struct connection *conn) { if (conn->last_input_tv.tv_sec != 0) { int diff = timeval_diff_msecs(&ioloop_timeval, &conn->last_input_tv); return t_strdup_printf("No input for %u.%03u secs", diff/1000, diff%1000); } else if (conn->connect_finished.tv_sec != 0) { int diff = timeval_diff_msecs(&ioloop_timeval, &conn->connect_finished); return t_strdup_printf( "No input since connected %u.%03u secs ago", diff/1000, diff%1000); } else { int diff = timeval_diff_msecs(&ioloop_timeval, &conn->connect_started); return t_strdup_printf("connect() timed out after %u.%03u secs", diff/1000, diff%1000); } } void connection_switch_ioloop(struct connection *conn) { if (conn->io != NULL) conn->io = io_loop_move_io(&conn->io); if (conn->to != NULL) conn->to = io_loop_move_timeout(&conn->to); if (conn->input != NULL) i_stream_switch_ioloop(conn->input); if (conn->output != NULL) o_stream_switch_ioloop(conn->output); } struct connection_list * connection_list_init(const struct connection_settings *set, const struct connection_vfuncs *vfuncs) { struct connection_list *list; i_assert(vfuncs->input != NULL || set->input_full_behavior != CONNECTION_BEHAVIOR_ALLOW); i_assert(set->major_version == 0 || (set->service_name_in != NULL && set->service_name_out != NULL && set->output_max_size != 0)); list = i_new(struct connection_list, 1); list->set = *set; list->v = *vfuncs; if (list->v.input == NULL) list->v.input = connection_input_default; if (list->v.input_line == NULL) list->v.input_line = connection_input_line_default; return list; } void connection_list_deinit(struct connection_list **_list) { struct connection_list *list = *_list; struct connection *conn; *_list = NULL; while (list->connections != NULL) { conn = list->connections; conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT; list->v.destroy(conn); i_assert(conn != list->connections); } i_free(list); } dovecot-2.2.33.2/src/lib/istream-sized.h0000644000175000017500000000314313123174404014613 00000000000000#ifndef ISTREAM_SIZED_H #define ISTREAM_SIZED_H struct istream_sized_error_data { /* Stream's current v_offset */ uoff_t v_offset; /* How many more bytes are being added within this read() */ size_t new_bytes; /* What's the original wanted size. */ uoff_t wanted_size; /* TRUE if we're at EOF now */ bool eof; }; typedef const char * istream_sized_callback_t(const struct istream_sized_error_data *data, void *context); /* Assume that input stream is exactly the given size. If the stream is too small, fail with stream_errno=EPIPE. If stream is too large, fail with stream_errno=EINVAL. */ struct istream *i_stream_create_sized(struct istream *input, uoff_t size); struct istream *i_stream_create_sized_range(struct istream *input, uoff_t offset, uoff_t size); /* Like i_stream_create_sized*(), but allow input stream's size to be larger. */ struct istream *i_stream_create_min_sized(struct istream *input, uoff_t min_size); struct istream *i_stream_create_min_sized_range(struct istream *input, uoff_t offset, uoff_t min_size); /* Same as i_stream_create_sized(), but set the error message via the callback. */ struct istream * i_stream_create_sized_with_callback(struct istream *input, uoff_t size, istream_sized_callback_t *error_callback, void *context); #define i_stream_create_sized_with_callback(input, size, error_callback, context) \ i_stream_create_sized_with_callback(input, size + \ CALLBACK_TYPECHECK(error_callback, \ const char *(*)(const struct istream_sized_error_data *, typeof(context))), \ (istream_sized_callback_t *)error_callback, context) #endif dovecot-2.2.33.2/src/lib/istream-unix.h0000644000175000017500000000121313123174404014454 00000000000000#ifndef ISTREAM_UNIX_H #define ISTREAM_UNIX_H struct istream *i_stream_create_unix(int fd, size_t max_buffer_size); /* Start trying to read a file descriptor from the UNIX socket. */ void i_stream_unix_set_read_fd(struct istream *input); /* Stop trying to read a file descriptor from the UNIX socket. */ void i_stream_unix_unset_read_fd(struct istream *input); /* Returns the fd that the last i_stream_read() received, or -1 if no fd was received. This function must be called before i_stream_unix_set_read_fd() is called again after successfully receiving a file descriptor. */ int i_stream_unix_get_read_fd(struct istream *input); #endif dovecot-2.2.33.2/src/lib/var-expand-private.h0000644000175000017500000000333413165463543015565 00000000000000#ifndef VAR_EXPAND_PRIVATE_H #define VAR_EXPAND_PRIVATE_H 1 struct var_expand_context { /* current variables */ const struct var_expand_table *table; /* caller provided function table */ const struct var_expand_func_table *func_table; /* caller provided context */ void *context; /* last offset, negative counts from end*/ int offset; /* last width, negative counts from end */ int width; /* last zero padding */ bool zero_padding:1; }; /* this can be used to register a *global* function that is prepended to function table. These can be used to register some special handling for keys. you can call var_expand_with_funcs if you need to expand something inside here. return -1 on error, 0 on unknown variable, 1 on success */ typedef int var_expand_extension_func_t(struct var_expand_context *ctx, const char *key, const char *field, const char **result_r, const char **error_r); struct var_expand_extension_func_table { const char *key; var_expand_extension_func_t *func; }; int var_expand_long(struct var_expand_context *ctx, const void *key_start, size_t key_len, const char **var_r, const char **error_r); void var_expand_extensions_init(void); void var_expand_extensions_deinit(void); /* Functions registered here are placed before in-built functions, so you can include your own implementation of something. Be careful. Use NULL terminated list. */ void var_expand_register_func_array(const struct var_expand_extension_func_table *funcs); void var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs); int var_expand_if(struct var_expand_context *ctx, const char *key, const char *field, const char **result_r, const char **error_r); #endif dovecot-2.2.33.2/src/lib/istream-concat.h0000644000175000017500000000026213123174404014743 00000000000000#ifndef ISTREAM_CONCAT_H #define ISTREAM_CONCAT_H /* Concatenate input streams into a single stream. */ struct istream *i_stream_create_concat(struct istream *input[]); #endif dovecot-2.2.33.2/src/lib/test-base64.c0000644000175000017500000000441213165463624014102 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "base64.h" static void test_base64_encode(void) { static const char *input[] = { "hello world", "foo barits", "just niin" }; static const char *output[] = { "aGVsbG8gd29ybGQ=", "Zm9vIGJhcml0cw==", "anVzdCBuaWlu" }; string_t *str; unsigned int i; test_begin("base64_encode()"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { str_truncate(str, 0); base64_encode(input[i], strlen(input[i]), str); test_assert(strcmp(output[i], str_c(str)) == 0); } test_end(); } struct test_base64_decode_output { const char *text; int ret; unsigned int src_pos; }; static void test_base64_decode(void) { static const char *input[] = { "\taGVsbG8gd29ybGQ=", "\nZm9v\n \tIGJh \t\ncml0cw==", " anVzdCBuaWlu \n", "aGVsb", "aGVsb!!!!!", "aGVs!!!!!" }; static const struct test_base64_decode_output output[] = { { "hello world", 0, UINT_MAX }, { "foo barits", 0, UINT_MAX }, { "just niin", 1, UINT_MAX }, { "hel", 1, 4 }, { "hel", -1, 4 }, { "hel", -1, 4 } }; string_t *str; unsigned int i; size_t src_pos; int ret; test_begin("base64_decode()"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { str_truncate(str, 0); src_pos = 0; ret = base64_decode(input[i], strlen(input[i]), &src_pos, str); test_assert(output[i].ret == ret && strcmp(output[i].text, str_c(str)) == 0 && (src_pos == output[i].src_pos || (output[i].src_pos == UINT_MAX && src_pos == strlen(input[i])))); } test_end(); } static void test_base64_random(void) { string_t *str, *dest; char buf[10]; unsigned int i, j, max; str = t_str_new(256); dest = t_str_new(256); test_begin("base64 encode/decode with random input"); for (i = 0; i < 1000; i++) { max = rand() % sizeof(buf); for (j = 0; j < max; j++) buf[j] = rand(); str_truncate(str, 0); str_truncate(dest, 0); base64_encode(buf, max, str); test_assert(base64_decode(str_data(str), str_len(str), NULL, dest) >= 0); test_assert(str_len(dest) == max && memcmp(buf, str_data(dest), max) == 0); } test_end(); } void test_base64(void) { test_base64_encode(); test_base64_decode(); test_base64_random(); } dovecot-2.2.33.2/src/lib/ioloop-notify-kqueue.c0000644000175000017500000001266413165463624016155 00000000000000/* * BSD kqueue() based ioloop notify handler. * * Copyright (c) 2005 Vaclav Haisman */ #define _GNU_SOURCE #include "lib.h" #ifdef IOLOOP_NOTIFY_KQUEUE #include "ioloop-private.h" #include "llist.h" #include "fd-close-on-exec.h" #include #include #include #include #include #include /* kevent.udata's type just has to be different in NetBSD than in FreeBSD and OpenBSD.. */ #ifdef __NetBSD__ # define MY_EV_SET(a, b, c, d, e, f, g) \ EV_SET(a, b, c, d, e, f, (intptr_t)g) #else # define MY_EV_SET(a, b, c, d, e, f, g) \ EV_SET(a, b, c, d, e, f, g) #endif struct io_notify { struct io io; int refcount; int fd; struct io_notify *prev, *next; }; struct ioloop_notify_handler_context { int kq; struct io *event_io; struct io_notify *notifies; }; static void io_loop_notify_free(struct ioloop_notify_handler_context *ctx, struct io_notify *io) { DLLIST_REMOVE(&ctx->notifies, io); i_free(io); } static void event_callback(struct ioloop_notify_handler_context *ctx) { struct io_notify *io; struct kevent events[64]; struct timespec ts; int i, ret; ts.tv_sec = 0; ts.tv_nsec = 0; ret = kevent(ctx->kq, NULL, 0, events, N_ELEMENTS(events), &ts); if (ret <= 0) { if (ret == 0 || errno == EINTR) return; i_fatal("kevent(notify) failed: %m"); } if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday() failed: %m"); ioloop_time = ioloop_timeval.tv_sec; for (i = 0; i < ret; i++) { io = (void *)events[i].udata; i_assert(io->refcount >= 1); io->refcount++; } for (i = 0; i < ret; i++) { io = (void *)events[i].udata; /* there can be multiple events for a single io. call the callback only once if that happens. */ if (io->refcount == 2 && io->io.callback != NULL) io_loop_call_io(&io->io); if (--io->refcount == 0) io_loop_notify_free(ctx, io); } } static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void) { struct ioloop_notify_handler_context *ctx; ctx = current_ioloop->notify_handler_context = i_new(struct ioloop_notify_handler_context, 1); ctx->kq = kqueue(); if (ctx->kq < 0) i_fatal("kqueue(notify) failed: %m"); fd_close_on_exec(ctx->kq, TRUE); return ctx; } void io_loop_notify_handler_deinit(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; while (ctx->notifies != NULL) { struct io_notify *io = ctx->notifies; struct io *_io = &io->io; i_warning("I/O notify leak: %p (%s:%u, fd %d)", (void *)_io->callback, _io->source_filename, _io->source_linenum, io->fd); io_remove(&_io); } if (ctx->event_io) io_remove(&ctx->event_io); if (close(ctx->kq) < 0) i_error("close(kqueue notify) failed: %m"); i_free(ctx); } #undef io_add_notify enum io_notify_result io_add_notify(const char *path, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context, struct io **io_r) { struct ioloop_notify_handler_context *ctx = current_ioloop->notify_handler_context; struct kevent ev; struct io_notify *io; int fd; if (ctx == NULL) ctx = io_loop_notify_handler_init(); fd = open(path, O_RDONLY); if (fd == -1) { /* ESTALE could happen with NFS. Don't bother giving an error message then. */ if (errno != ENOENT && errno != ESTALE) i_error("open(%s) for kq notify failed: %m", path); return IO_NOTIFY_NOTFOUND; } fd_close_on_exec(fd, TRUE); io = i_new(struct io_notify, 1); io->io.condition = IO_NOTIFY; io->io.source_filename = source_filename; io->io.source_linenum = source_linenum; io->io.callback = callback; io->io.context = context; io->io.ioloop = current_ioloop; io->refcount = 1; io->fd = fd; /* EV_CLEAR flag is needed because the EVFILT_VNODE filter reports event state transitions and not the current state. With this flag, the same event is only returned once. */ MY_EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { i_error("kevent(%d, %s) for notify failed: %m", fd, path); i_close_fd(&fd); i_free(io); return IO_NOTIFY_NOSUPPORT; } if (ctx->event_io == NULL) { ctx->event_io = io_add(ctx->kq, IO_READ, event_callback, io->io.ioloop->notify_handler_context); } DLLIST_PREPEND(&ctx->notifies, io); *io_r = &io->io; return IO_NOTIFY_ADDED; } void io_loop_notify_remove(struct io *_io) { struct ioloop_notify_handler_context *ctx = _io->ioloop->notify_handler_context; struct io_notify *io = (struct io_notify *)_io; struct kevent ev; MY_EV_SET(&ev, io->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, 0) < 0) i_error("kevent(%d) for notify remove failed: %m", io->fd); if (close(io->fd) < 0) i_error("close(%d) for notify remove failed: %m", io->fd); io->fd = -1; if (--io->refcount == 0) io_loop_notify_free(ctx, io); } int io_loop_extract_notify_fd(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; struct io_notify *io; int fd, new_kq; if (ctx == NULL || ctx->kq == -1) return -1; new_kq = kqueue(); if (new_kq < 0) { i_error("kqueue(notify) failed: %m"); return -1; } for (io = ctx->notifies; io != NULL; io = io->next) io->fd = -1; if (ctx->event_io != NULL) io_remove(&ctx->event_io); fd = ctx->kq; ctx->kq = new_kq; return fd; } #endif dovecot-2.2.33.2/src/lib/fdpass.c0000644000175000017500000001345513123174404013315 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* fdpass.c - File descriptor passing between processes via UNIX sockets This isn't fully portable, but pretty much all UNIXes nowadays should support this. If you're having runtime problems with fd_read(), check the end of fd_read() and play with the if condition. If you're having problems with fd_send(), try defining BUGGY_CMSG_MACROS. If this file doesn't compile at all, you should check if this is supported in your system at all. It may require some extra #define to enable it. If not, you're pretty much out of luck. Cygwin didn't last I checked. */ #define _XPG4_2 #if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__) # define _XOPEN_SOURCE 4 /* for IRIX */ #endif #if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED) # define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */ #endif #ifdef HAVE_CONFIG_H # include "lib.h" #else # define i_assert(x) #endif #include #include #include #include #include #include #include "fdpass.h" #ifndef HAVE_CONFIG_H struct const_iovec { const void *iov_base; size_t iov_len; }; #endif /* RFC 2292 defines CMSG_*() macros, but some operating systems don't have them so we'll define our own if they don't exist. CMSG_LEN(data) is used to calculate size of sizeof(struct cmsghdr) + sizeof(data) and padding between them. CMSG_SPACE(data) also calculates the padding needed after the data, in case multiple objects are sent. cmsghdr contains cmsg_len field and two integers. cmsg_len is sometimes defined as sockaddr_t and sometimes size_t, so it can be either 32bit or 64bit. This padding is added by compiler in sizeof(struct cmsghdr). Padding required by CMSG_DATA() can vary. Usually it wants size_t or 32bit. With Solaris it's in _CMSG_DATA_ALIGNMENT (32bit), we assume others want size_t. We don't really need CMSG_SPACE() to be exactly correct, because currently we send only one object at a time. But anyway I'm trying to keep that correct in case it's sometimes needed.. */ #ifdef BUGGY_CMSG_MACROS /* Some OSes have broken CMSG macros in 64bit systems. The macros use 64bit alignment while kernel uses 32bit alignment. */ # undef CMSG_SPACE # undef CMSG_LEN # undef CMSG_DATA # define CMSG_DATA(cmsg) ((char *)((cmsg) + 1)) # define _CMSG_DATA_ALIGNMENT 4 # define _CMSG_HDR_ALIGNMENT 4 #endif #ifndef CMSG_SPACE # define MY_ALIGN(len, align) \ (((len) + align - 1) & ~(align - 1)) /* Alignment between cmsghdr and data */ # ifndef _CMSG_DATA_ALIGNMENT # define _CMSG_DATA_ALIGNMENT sizeof(size_t) # endif /* Alignment between data and next cmsghdr */ # ifndef _CMSG_HDR_ALIGNMENT # define _CMSG_HDR_ALIGNMENT sizeof(size_t) # endif # define CMSG_SPACE(len) \ (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + \ MY_ALIGN(len, _CMSG_HDR_ALIGNMENT)) # define CMSG_LEN(len) \ (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + (len)) #endif #ifdef SCM_RIGHTS ssize_t fd_send(int handle, int send_fd, const void *data, size_t size) { struct msghdr msg; struct const_iovec iov; struct cmsghdr *cmsg; char buf[CMSG_SPACE(sizeof(int))]; /* at least one byte is required to be sent with fd passing */ i_assert(size > 0 && size < INT_MAX); memset(&msg, 0, sizeof(struct msghdr)); iov.iov_base = data; iov.iov_len = size; msg.msg_iov = (void *)&iov; msg.msg_iovlen = 1; if (send_fd != -1) { /* set the control and controllen before CMSG_FIRSTHDR(). */ memset(buf, 0, sizeof(buf)); msg.msg_control = buf; msg.msg_controllen = sizeof(buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); /* set the real length we want to use. Do it after all is set just in case CMSG macros required the extra padding in the end. */ msg.msg_controllen = cmsg->cmsg_len; } return sendmsg(handle, &msg, 0); } #ifdef LINUX20 /* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some attacks possible so don't do it unless you really have to. */ # define CHECK_CMSG(cmsg) ((cmsg) != NULL) #else # define CHECK_CMSG(cmsg) \ ((cmsg) != NULL && \ (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \ (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS) #endif ssize_t fd_read(int handle, void *data, size_t size, int *fd) { struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; ssize_t ret; char buf[CMSG_SPACE(sizeof(int))]; i_assert(size > 0 && size < INT_MAX); memset(&msg, 0, sizeof (struct msghdr)); iov.iov_base = data; iov.iov_len = size; msg.msg_iov = &iov; msg.msg_iovlen = 1; memset(buf, 0, sizeof(buf)); msg.msg_control = buf; msg.msg_controllen = sizeof(buf); ret = recvmsg(handle, &msg, 0); if (ret <= 0) { *fd = -1; return ret; } /* at least one byte transferred - we should have the fd now. do extra checks to make sure it really is an fd that is being transferred to avoid potential DoS conditions. some systems don't set all these values correctly however so CHECK_CMSG() is somewhat system dependent */ cmsg = CMSG_FIRSTHDR(&msg); if (!CHECK_CMSG(cmsg)) *fd = -1; else memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); return ret; } #else # ifdef __GNUC__ # warning SCM_RIGHTS not supported, privilege separation not possible # endif ssize_t fd_send(int handle ATTR_UNUSED, int send_fd ATTR_UNUSED, const void *data ATTR_UNUSED, size_t size ATTR_UNUSED) { errno = ENOSYS; return -1; } ssize_t fd_read(int handle ATTR_UNUSED, void *data ATTR_UNUSED, size_t size ATTR_UNUSED, int *fd ATTR_UNUSED) { errno = ENOSYS; return -1; } #endif dovecot-2.2.33.2/src/lib/unix-socket-create.c0000644000175000017500000000135713123174404015545 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "unix-socket-create.h" #include #include int unix_socket_create(const char *path, int mode, uid_t uid, gid_t gid, int backlog) { mode_t old_umask; int fd; old_umask = umask(0777 ^ mode); fd = net_listen_unix_unlink_stale(path, backlog); umask(old_umask); if (fd < 0) { i_error("net_listen_unix(%s) failed: %m", path); return -1; } if (uid != (uid_t)-1 || gid != (gid_t)-1) { /* set correct permissions */ if (chown(path, uid, gid) < 0) { i_error("chown(%s, %s, %s) failed: %m", path, dec2str(uid), dec2str(gid)); i_close_fd(&fd); return -1; } } return fd; } dovecot-2.2.33.2/src/lib/uri-util.c0000644000175000017500000005346113165463624013623 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "net.h" #include "uri-util.h" #include /* * Generic URI parsing. * * [URI-GEN] RFC3986 Appendix A: * * host = IP-literal / IPv4address / reg-name * port = *DIGIT * reg-name = *( unreserved / pct-encoded / sub-delims ) * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * pct-encoded = "%" HEXDIG HEXDIG * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" * IP-literal = "[" ( IPv6address / IPvFuture ) "]" * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) * IPv6address = 6( h16 ":" ) ls32 * / "::" 5( h16 ":" ) ls32 * / [ h16 ] "::" 4( h16 ":" ) ls32 * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 * / [ *4( h16 ":" ) h16 ] "::" ls32 * / [ *5( h16 ":" ) h16 ] "::" h16 * / [ *6( h16 ":" ) h16 ] "::" * h16 = 1*4HEXDIG * ls32 = ( h16 ":" h16 ) / IPv4address * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet * dec-octet = DIGIT ; 0-9 * / %x31-39 DIGIT ; 10-99 * / "1" 2DIGIT ; 100-199 * / "2" %x30-34 DIGIT ; 200-249 * / "25" %x30-35 ; 250-255 */ #define URI_MAX_SCHEME_NAME_LEN 64 /* Character lookup table * * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0] * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" [bit1] * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2] * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3] * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/" * [bit0|bit1|bit3|bit5] * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4] * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6] * */ #define CHAR_MASK_UNRESERVED (1<<0) #define CHAR_MASK_SUB_DELIMS (1<<1) #define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3)) #define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)) #define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4)) #define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6)) static unsigned const char _uri_char_lookup[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70 }; static inline int _decode_hex_digit(const unsigned char digit) { switch (digit) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return digit - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return digit - 'a' + 0x0a; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return digit - 'A' + 0x0A; } return -1; } static int uri_parse_pct_encoded_data(struct uri_parser *parser, const unsigned char **p, const unsigned char *pend, unsigned char *ch_r) ATTR_NULL(3) { int value; if (**p != '%' || (pend != NULL && *p >= pend)) return 0; *p += 1; if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) { parser->error = "Unexpected URI boundary after '%'"; return -1; } if ((value = _decode_hex_digit(**p)) < 0) { parser->error = p_strdup_printf(parser->pool, "Expecting hex digit after '%%', but found '%c'", **p); return -1; } *ch_r = (value & 0x0f) << 4; *p += 1; if ((value = _decode_hex_digit(**p)) < 0) { parser->error = p_strdup_printf(parser->pool, "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p); return -1; } *ch_r |= (value & 0x0f); *p += 1; if (!parser->allow_pct_nul && *ch_r == '\0') { parser->error = "Percent encoding is not allowed to encode NUL character"; return -1; } return 1; } int uri_parse_pct_encoded(struct uri_parser *parser, unsigned char *ch_r) { return uri_parse_pct_encoded_data (parser, &parser->cur, parser->end, ch_r); } static int uri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r) { int ret; if ((ret=uri_parse_pct_encoded(parser, ch_r)) != 0) return ret; if ((*parser->cur & 0x80) != 0) return 0; if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) { *ch_r = *parser->cur; parser->cur++; return 1; } return 0; } int uri_parse_unreserved(struct uri_parser *parser, string_t *part) { int len = 0; while (parser->cur < parser->end) { int ret; unsigned char ch = 0; if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0) return -1; if (ret == 0) break; if (part != NULL) str_append_c(part, ch); len++; } return len > 0 ? 1 : 0; } bool uri_data_decode(struct uri_parser *parser, const char *data, const char *until, const char **decoded_r) { const unsigned char *p = (const unsigned char *)data; const unsigned char *pend = (const unsigned char *)until; string_t *decoded; int ret; if (pend == NULL) { /* NULL means unlimited; solely rely on '\0' */ pend = (const unsigned char *)(size_t)-1; } if (p >= pend || *p == '\0') { if (decoded_r != NULL) *decoded_r = ""; return TRUE; } decoded = uri_parser_get_tmpbuf(parser, 256); while (p < pend && *p != '\0') { unsigned char ch; if ((ret=uri_parse_pct_encoded_data (parser, &p, NULL, &ch)) != 0) { if (ret < 0) return FALSE; str_append_c(decoded, ch); } else { str_append_c(decoded, *p); p++; } } if (decoded_r != NULL) *decoded_r = p_strdup(parser->pool, str_c(decoded)); return TRUE; } int uri_cut_scheme(const char **uri_p, const char **scheme_r) { const char *p = *uri_p; size_t len = 1; /* RFC 3968: * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ if (!i_isalpha(*p)) return -1; p++; while (len < URI_MAX_SCHEME_NAME_LEN && *p != '\0') { if (!i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.') break; p++; len++; } if (*p != ':') return -1; if (scheme_r != NULL) *scheme_r = t_strdup_until(*uri_p, p); *uri_p = p + 1; return 0; } int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r) { const char *p; if (parser->cur >= parser->end) return 0; p = (const char *)parser->cur; if (uri_cut_scheme(&p, scheme_r) < 0) return 0; parser->cur = (const unsigned char *)p; if (!parser->pool->datastack_pool) *scheme_r = p_strdup(parser->pool, *scheme_r); return 1; } static int uri_parse_dec_octet(struct uri_parser *parser, string_t *literal, uint8_t *octet_r) ATTR_NULL(2) { unsigned int octet = 0; int count = 0; /* RFC 3986: * * dec-octet = DIGIT ; 0-9 * / %x31-39 DIGIT ; 10-99 * / "1" 2DIGIT ; 100-199 * / "2" %x30-34 DIGIT ; 200-249 * / "25" %x30-35 ; 250-255 */ while (parser->cur < parser->end && i_isdigit(*parser->cur)) { octet = octet * 10 + (parser->cur[0] - '0'); if (octet > 255) return -1; if (literal != NULL) str_append_c(literal, *parser->cur); parser->cur++; count++; } if (count > 0) { *octet_r = octet; return 1; } return 0; } static int uri_parse_ipv4address(struct uri_parser *parser, string_t *literal, struct in_addr *ip4_r) ATTR_NULL(2,3) { uint8_t octet; uint32_t ip = 0; int ret; int i; /* RFC 3986: * * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet */ if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0) return ret; ip = octet; for (i = 0; i < 3 && parser->cur < parser->end; i++) { if (*parser->cur != '.') return -1; if (literal != NULL) str_append_c(literal, '.'); parser->cur++; if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0) return -1; ip = (ip << 8) + octet; } if (ip4_r != NULL) ip4_r->s_addr = htonl(ip); return 1; } static int uri_parse_reg_name(struct uri_parser *parser, string_t *reg_name) ATTR_NULL(2) { /* RFC 3986: * * reg-name = *( unreserved / pct-encoded / sub-delims ) */ while (parser->cur < parser->end) { int ret; unsigned char c; /* unreserved / pct-encoded */ if ((ret = uri_parse_unreserved_char(parser, &c)) < 0) return -1; if (ret > 0) { if (reg_name != NULL) str_append_c(reg_name, c); continue; } /* sub-delims */ c = *parser->cur; if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) { if (reg_name != NULL) str_append_c(reg_name, *parser->cur); parser->cur++; continue; } break; } return 0; } #ifdef HAVE_IPV6 static int uri_parse_ip_literal(struct uri_parser *parser, string_t *literal, struct in6_addr *ip6_r) ATTR_NULL(2,3) { const unsigned char *p; const char *address; struct in6_addr ip6; int ret; /* IP-literal = "[" ( IPv6address / IPvFuture ) "]" * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) * IPv6address = ; Syntax not relevant: parsed using inet_pton() */ /* "[" already verified */ /* Scan for end of address */ for (p = parser->cur+1; p < parser->end; p++) { if (*p == ']') break; } if (p >= parser->end || *p != ']') { parser->error = "Expecting ']' at end of IP-literal"; return -1; } if (literal != NULL) str_append_n(literal, parser->cur, p-parser->cur+1); address = t_strdup_until(parser->cur+1, p); parser->cur = p + 1; if (*address == '\0') { parser->error = "Empty IPv6 host address"; return -1; } if (*address == 'v') { parser->error = p_strdup_printf(parser->pool, "Future IP host address '%s' not supported", address); return -1; } if ((ret = inet_pton(AF_INET6, address, &ip6)) <= 0) { parser->error = p_strdup_printf(parser->pool, "Invalid IPv6 host address '%s'", address); return -1; } if (ip6_r != NULL) *ip6_r = ip6; return 1; } #endif static int uri_parse_host(struct uri_parser *parser, struct uri_authority *auth) ATTR_NULL(2) { const unsigned char *preserve; struct in_addr ip4; struct in6_addr ip6; string_t *literal = NULL; int ret; /* RFC 3986: * * host = IP-literal / IPv4address / reg-name */ literal = uri_parser_get_tmpbuf(parser, 256); /* IP-literal / */ if (parser->cur < parser->end && *parser->cur == '[') { #ifdef HAVE_IPV6 if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0) return -1; if (auth != NULL) { auth->host_literal = p_strdup(parser->pool, str_c(literal)); auth->host_ip.family = AF_INET6; auth->host_ip.u.ip6 = ip6; auth->have_host_ip = TRUE; } return 1; #else parser->error = "IPv6 host address is not supported"; return -1; #endif } /* IPv4address / * * If it fails to parse, we try to parse it as a reg-name */ preserve = parser->cur; if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) { if (auth != NULL) { auth->host_literal = p_strdup(parser->pool, str_c(literal)); auth->host_ip.family = AF_INET; auth->host_ip.u.ip4 = ip4; auth->have_host_ip = TRUE; } return ret; } parser->cur = preserve; str_truncate(literal, 0); /* reg-name */ if (uri_parse_reg_name(parser, literal) < 0) return -1; if (auth != NULL) { auth->host_literal = p_strdup(parser->pool, str_c(literal)); auth->have_host_ip = FALSE; } return 0; } static int uri_parse_port(struct uri_parser *parser, struct uri_authority *auth) ATTR_NULL(2) { const unsigned char *first; in_port_t port; /* RFC 3986: * * port = *DIGIT */ first = parser->cur; while (parser->cur < parser->end && i_isdigit(*parser->cur)) parser->cur++; if (parser->cur == first) return 0; if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) { parser->error = "Invalid port number"; return -1; } if (auth != NULL) { auth->port = port; auth->have_port = TRUE; } return 1; } int uri_parse_authority(struct uri_parser *parser, struct uri_authority *auth) { const unsigned char *p; int ret; /* * authority = [ userinfo "@" ] host [ ":" port ] */ if (auth != NULL) i_zero(auth); /* Scan ahead to check whether there is a [userinfo "@"] uri component */ for (p = parser->cur; p < parser->end; p++){ /* refuse 8bit characters */ if ((*p & 0x80) != 0) break; /* break at first delimiter */ if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0) break; } /* Extract userinfo */ if (p < parser->end && *p == '@') { if (auth != NULL) auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p); parser->cur = p+1; } /* host */ if (uri_parse_host(parser, auth) < 0) return -1; if (parser->cur == parser->end) return 1; switch (*parser->cur) { case ':': case '/': case '?': case '#': break; default: parser->error = "Invalid host identifier"; return -1; } /* [":" port] */ if (*parser->cur == ':') { parser->cur++; if ((ret = uri_parse_port(parser, auth)) < 0) return ret; if (parser->cur == parser->end) return 1; switch (*parser->cur) { case '/': case '?': case '#': break; default: parser->error = "Invalid host port"; return -1; } } return 1; } int uri_parse_slashslash_authority(struct uri_parser *parser, struct uri_authority *auth) { /* "//" authority */ if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' || parser->cur[1] != '/') return 0; parser->cur += 2; return uri_parse_authority(parser, auth); } int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r) { const unsigned char *first = parser->cur; int ret; while (parser->cur < parser->end) { if (*parser->cur == '%') { unsigned char ch = 0; if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) return -1; if (ret > 0) continue; } if ((*parser->cur & 0x80) != 0 || (_uri_char_lookup[*parser->cur] & CHAR_MASK_PCHAR) == 0) break; parser->cur++; } if (parser->cur < parser->end && *parser->cur != '/' && *parser->cur != '?' && *parser->cur != '#' ) { parser->error = "Path component contains invalid character"; return -1; } if (first == parser->cur) return 0; if (segment_r != NULL) *segment_r = p_strdup_until(parser->pool, first, parser->cur); return 1; } int uri_parse_path(struct uri_parser *parser, int *relative_r, const char *const **path_r) { const unsigned char *pbegin = parser->cur; ARRAY_TYPE(const_string) segments; const char *segment = NULL; unsigned int count; int relative = 1; int ret; count = 0; if (path_r != NULL) p_array_init(&segments, parser->pool, 16); else i_zero(&segments); /* check for a leading '/' and indicate absolute path when it is present */ if (parser->cur < parser->end && *parser->cur == '/') { parser->cur++; relative = 0; } /* parse first segment */ if ((ret = uri_parse_path_segment(parser, &segment)) < 0) return -1; for (;;) { if (ret > 0) { /* strip dot segments */ if (segment[0] == '.') { if (segment[1] == '.') { if (segment[2] == '\0') { /* '..' -> skip and... */ segment = NULL; /* ... pop last segment (if any) */ if (count > 0) { if (path_r != NULL) { i_assert(count == array_count(&segments)); array_delete(&segments, count-1, 1); } count--; } else if ( relative > 0 ) { relative++; } } } else if (segment[1] == '\0') { /* '.' -> skip */ segment = NULL; } } } else { segment = ""; } if (segment != NULL) { if (path_r != NULL) array_append(&segments, &segment, 1); count++; } if (parser->cur >= parser->end || *parser->cur != '/') break; parser->cur++; /* parse next path segment */ if ((ret = uri_parse_path_segment(parser, &segment)) < 0) return -1; } if (relative_r != NULL) *relative_r = relative; if (path_r != NULL) *path_r = NULL; if (parser->cur == pbegin) { /* path part of URI is empty */ return 0; } if (path_r != NULL) { /* special treatment for a trailing '..' or '.' */ if (segment == NULL) { segment = ""; array_append(&segments, &segment, 1); } array_append_zero(&segments); *path_r = array_get(&segments, &count); } if (parser->cur < parser->end && *parser->cur != '?' && *parser->cur != '#') { parser->error = "Path component contains invalid character"; return -1; } return 1; } int uri_parse_query(struct uri_parser *parser, const char **query_r) { const unsigned char *first = parser->cur; int ret; /* RFC 3986: * * URI = { ... } [ "?" query ] { ... } * query = *( pchar / "/" / "?" ) * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" */ if (parser->cur >= parser->end || *parser->cur != '?') return 0; parser->cur++; while (parser->cur < parser->end) { if (*parser->cur == '%') { unsigned char ch = 0; if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) return -1; if (ret > 0) continue; } if ((*parser->cur & 0x80) != 0 || (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0) break; parser->cur++; } if (parser->cur < parser->end && *parser->cur != '#') { parser->error = "Query component contains invalid character"; return -1; } if (query_r != NULL) *query_r = p_strdup_until(parser->pool, first+1, parser->cur); return 1; } int uri_parse_fragment(struct uri_parser *parser, const char **fragment_r) { const unsigned char *first = parser->cur; int ret; /* RFC 3986: * * URI = { ... } [ "#" fragment ] * fragment = *( pchar / "/" / "?" ) * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" */ if (parser->cur >= parser->end || *parser->cur != '#') return 0; parser->cur++; while (parser->cur < parser->end) { if (*parser->cur == '%') { unsigned char ch = 0; if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) return -1; if (ret > 0) continue; } if ((*parser->cur & 0x80) != 0 || (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0) break; parser->cur++; } if (parser->cur < parser->end) { parser->error = "Fragment component contains invalid character"; return -1; } if (fragment_r != NULL) *fragment_r = p_strdup_until(parser->pool, first+1, parser->cur); return 1; } void uri_parser_init(struct uri_parser *parser, pool_t pool, const char *data) { parser->pool = pool; parser->begin = parser->cur = (unsigned char *)data; parser->end = (unsigned char *)data + strlen(data); parser->error = NULL; parser->tmpbuf = NULL; } string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size) { if (parser->tmpbuf == NULL) parser->tmpbuf = str_new(parser->pool, size); else str_truncate(parser->tmpbuf, 0); return parser->tmpbuf; } /* * Generic URI construction */ static void uri_data_encode(string_t *out, const unsigned char esc_table[256], unsigned char esc_mask, const char *esc_extra, const char *data) { const unsigned char *p = (const unsigned char *)data; while (*p != '\0') { if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 || strchr(esc_extra, (char)*p) != NULL) { str_printfa(out, "%%%02x", *p); } else { str_append_c(out, *p); } p++; } } void uri_append_scheme(string_t *out, const char *scheme) { str_append(out, scheme); str_append_c(out, ':'); } void uri_append_user_data(string_t *out, const char *esc, const char *data) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data); } void uri_append_userinfo(string_t *out, const char *userinfo) { uri_append_user_data(out, "", userinfo); str_append_c(out, '@'); } void uri_append_host_name(string_t *out, const char *name) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, "", name); } void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip) { const char *addr = net_ip2addr(host_ip); if (host_ip->family == AF_INET) { str_append(out, addr); return; } i_assert(host_ip->family == AF_INET6); str_append_c(out, '['); str_append(out, addr); str_append_c(out, ']'); } void uri_append_port(string_t *out, in_port_t port) { str_printfa(out, ":%u", port); } void uri_append_path_segment_data(string_t *out, const char *esc, const char *data) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data); } void uri_append_path_segment(string_t *out, const char *segment) { str_append_c(out, '/'); if (*segment != '\0') uri_append_path_data(out, "", segment); } void uri_append_path_data(string_t *out, const char *esc, const char *data) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data); } void uri_append_path(string_t *out, const char *path) { str_append_c(out, '/'); if (*path != '\0') uri_append_path_data(out, "", path); } void uri_append_query_data(string_t *out, const char *esc, const char *data) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); } void uri_append_query(string_t *out, const char *query) { str_append_c(out, '?'); if (*query != '\0') uri_append_query_data(out, "", query); } void uri_append_fragment_data(string_t *out, const char *esc, const char *data) { uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); } void uri_append_fragment(string_t *out, const char *fragment) { str_append_c(out, '#'); if (*fragment != '\0') uri_append_fragment_data(out, "", fragment); } dovecot-2.2.33.2/src/lib/fdpass.h0000644000175000017500000000117513123174404013316 00000000000000#ifndef FDPASS_H #define FDPASS_H /* Send data and send_fd (unless it's -1) via sendmsg(). Returns number of bytes sent, or -1 on error. If at least 1 byte was sent, the send_fd was also sent. */ ssize_t fd_send(int handle, int send_fd, const void *data, size_t size); /* Receive data and fd via recvmsg(). Returns number of bytes read, 0 on disconnection, or -1 on error. If at least 1 byte was read, the fd is also returned (if it had been sent). If there was no fd received, it's set to -1. See test-istream-unix.c for different test cases. */ ssize_t fd_read(int handle, void *data, size_t size, int *fd_r); #endif dovecot-2.2.33.2/src/lib/unix-socket-create.h0000644000175000017500000000024313123174404015543 00000000000000#ifndef UNIX_SOCKET_CREATE_H #define UNIX_SOCKET_CREATE_H int unix_socket_create(const char *path, int mode, uid_t uid, gid_t gid, int backlog); #endif dovecot-2.2.33.2/src/lib/numpack.c0000644000175000017500000000174413123174404013471 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "numpack.h" void numpack_encode(buffer_t *buf, uint64_t num) { /* number continues as long as the highest bit is set */ while (num >= 0x80) { buffer_append_c(buf, (num & 0x7f) | 0x80); num >>= 7; } buffer_append_c(buf, num); } int numpack_decode(const uint8_t **p, const uint8_t *end, uint64_t *num_r) { const uint8_t *c = *p; uint64_t value = 0; unsigned int bits = 0; while (bits < 64) { if (c == end) return -1; value |= (uint64_t)(*c & 0x7f) << bits; if (*c < 0x80) break; bits += 7; c++; } bits += bits_required8(*c); if (bits > 64) /* overflow */ return -1; *p = c + 1; *num_r = value; return 0; } int numpack_decode32(const uint8_t **p, const uint8_t *end, uint32_t *num_r) { uint64_t num; if (numpack_decode(p, end, &num) < 0) return -1; if (num > 4294967295U) return -1; *num_r = (uint32_t)num; return 0; } dovecot-2.2.33.2/src/lib/file-cache.c0000644000175000017500000002042513165463624014023 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "mmap-util.h" #include "file-cache.h" #include struct file_cache { int fd; char *path; buffer_t *page_bitmask; void *mmap_base; size_t mmap_length; size_t read_highwater; }; struct file_cache *file_cache_new(int fd) { return file_cache_new_path(fd, ""); } struct file_cache *file_cache_new_path(int fd, const char *path) { struct file_cache *cache; cache = i_new(struct file_cache, 1); cache->fd = fd; cache->path = i_strdup(path); cache->page_bitmask = buffer_create_dynamic(default_pool, 128); return cache; } void file_cache_free(struct file_cache **_cache) { struct file_cache *cache = *_cache; *_cache = NULL; if (cache->mmap_base != NULL) { if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0) i_error("munmap_anon(%s) failed: %m", cache->path); } buffer_free(&cache->page_bitmask); i_free(cache->path); i_free(cache); } void file_cache_set_fd(struct file_cache *cache, int fd) { cache->fd = fd; file_cache_invalidate(cache, 0, cache->mmap_length); } int file_cache_set_size(struct file_cache *cache, uoff_t size) { size_t page_size = mmap_get_page_size(); uoff_t diff; void *new_base; i_assert(page_size > 0); diff = size % page_size; if (diff != 0) size += page_size - diff; i_assert((size % page_size) == 0); if (size <= cache->mmap_length) return 0; if (size > (size_t)-1) { i_error("file_cache_set_size(%s, %"PRIuUOFF_T"): size too large", cache->path, size); return -1; } /* grow mmaping */ if (cache->mmap_base == NULL) { cache->mmap_base = mmap_anon(size); if (cache->mmap_base == MAP_FAILED) { i_error("mmap_anon(%s, %"PRIuUOFF_T") failed: %m", cache->path, size); cache->mmap_base = NULL; cache->mmap_length = 0; return -1; } } else { new_base = mremap_anon(cache->mmap_base, cache->mmap_length, size, MREMAP_MAYMOVE); if (new_base == MAP_FAILED) { i_error("mremap_anon(%s, %"PRIuUOFF_T") failed: %m", cache->path, size); return -1; } cache->mmap_base = new_base; } cache->mmap_length = size; return 0; } ssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size) { size_t page_size = mmap_get_page_size(); size_t poffset, psize, dest_offset, dest_size; unsigned char *bits, *dest; ssize_t ret; i_assert(page_size > 0); if (size > SSIZE_T_MAX) { /* make sure our calculations won't overflow. most likely we'll be reading less data, but allow it anyway so caller doesn't have to deal with any extra checks. */ size = SSIZE_T_MAX; } if (offset >= (uoff_t)-1 - size) size = (uoff_t)-1 - offset; if (offset + size > cache->mmap_length && offset + size - cache->mmap_length > 1024*1024) { /* growing more than a megabyte, make sure that the file is large enough so we don't allocate memory more than needed */ struct stat st; if (fstat(cache->fd, &st) < 0) { if (errno != ESTALE) i_error("fstat(%s) failed: %m", cache->path); return -1; } if (offset + size > (uoff_t)st.st_size) { if (offset >= (uoff_t)st.st_size) return 0; size = (uoff_t)st.st_size - offset; } } if (file_cache_set_size(cache, offset + size) < 0) return -1; poffset = offset / page_size; psize = (offset + size + page_size-1) / page_size - poffset; i_assert(psize > 0); bits = buffer_get_space_unsafe(cache->page_bitmask, 0, (poffset + psize + CHAR_BIT - 1) / CHAR_BIT); dest_offset = poffset * page_size; dest = PTR_OFFSET(cache->mmap_base, dest_offset); dest_size = page_size; while (psize > 0) { if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) { /* page is already in cache */ dest_offset += page_size; if (dest_offset <= cache->read_highwater) { psize--; poffset++; dest += page_size; continue; } /* this is the last partially cached block. use the caching only if we don't want to read past read_highwater */ if (offset + size <= cache->read_highwater) { i_assert(psize == 1); break; } /* mark the block noncached again and read it */ bits[poffset / CHAR_BIT] &= ~(1 << (poffset % CHAR_BIT)); dest_offset -= page_size; } ret = pread(cache->fd, dest, dest_size, dest_offset); if (ret <= 0) { if (ret < 0) return -1; /* EOF. mark the last block as cached even if it isn't completely. read_highwater tells us how far we've actually made. */ if (dest_offset == cache->read_highwater) { i_assert(poffset == cache->read_highwater / page_size); bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT); } return dest_offset <= offset ? 0 : dest_offset - offset < size ? dest_offset - offset : size; } dest += ret; dest_offset += ret; if (cache->read_highwater < dest_offset) { unsigned int high_poffset = cache->read_highwater / page_size; /* read_highwater needs to be updated. if we didn't just read that block, we can't trust anymore that we have it cached */ bits[high_poffset / CHAR_BIT] &= ~(1 << (high_poffset % CHAR_BIT)); cache->read_highwater = dest_offset; } if ((size_t)ret != dest_size) { /* partial read - probably EOF but make sure. */ dest_size -= ret; continue; } bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT); dest_size = page_size; psize--; poffset++; } return size; } const void *file_cache_get_map(struct file_cache *cache, size_t *size_r) { *size_r = cache->read_highwater; return cache->mmap_base; } void file_cache_write(struct file_cache *cache, const void *data, size_t size, uoff_t offset) { size_t page_size = mmap_get_page_size(); unsigned char *bits; unsigned int first_page, last_page; i_assert(page_size > 0); i_assert((uoff_t)-1 - offset > size); if (file_cache_set_size(cache, offset + size) < 0) { /* couldn't grow mapping. just make sure the written memory area is invalidated then. */ file_cache_invalidate(cache, offset, size); return; } memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size); if (cache->read_highwater < offset + size) { unsigned int page = cache->read_highwater / page_size; bits = buffer_get_space_unsafe(cache->page_bitmask, page / CHAR_BIT, 1); *bits &= ~(1 << (page % CHAR_BIT)); cache->read_highwater = offset + size; } /* mark fully written pages cached */ if (size >= page_size) { first_page = offset / page_size; last_page = (offset + size) / page_size; if ((offset % page_size) != 0) first_page++; bits = buffer_get_space_unsafe(cache->page_bitmask, 0, last_page / CHAR_BIT + 1); for (; first_page < last_page; first_page++) { bits[first_page / CHAR_BIT] |= 1 << (first_page % CHAR_BIT); } } } void file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size) { size_t page_size = mmap_get_page_size(); unsigned char *bits, mask; unsigned int i; if (offset >= cache->read_highwater || size == 0) return; i_assert(page_size > 0); if (size > cache->read_highwater - offset) { /* ignore anything after read highwater */ size = cache->read_highwater - offset; } if (size >= cache->read_highwater) { /* we're invalidating everything up to read highwater. drop the highwater position. */ cache->read_highwater = offset & ~(page_size-1); } size = (offset + size + page_size-1) / page_size; offset /= page_size; i_assert(size > offset); size -= offset; if (size != 1) { /* tell operating system that we don't need the memory anymore and it may free it. don't bother to do it for single pages, there's a good chance that they get re-read back immediately. */ (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size), size * page_size, MADV_DONTNEED); } bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT, 1 + (size + CHAR_BIT - 1) / CHAR_BIT); /* set the first byte */ for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) { mask |= 1 << i; size--; } *bits++ &= ~mask; /* set the middle bytes */ memset(bits, 0, size / CHAR_BIT); bits += size / CHAR_BIT; size %= CHAR_BIT; /* set the last byte */ if (size > 0) { for (i = 0, mask = 0; i < size; i++) mask |= 1 << i; *bits &= ~mask; } } dovecot-2.2.33.2/src/lib/backtrace-string.h0000644000175000017500000000026713123174404015262 00000000000000#ifndef BACKTRACE_STRING_H #define BACKTRACE_STRING_H /* Returns 0 if ok, -1 if failure. */ int backtrace_append(string_t *str); int backtrace_get(const char **backtrace_r); #endif dovecot-2.2.33.2/src/lib/iostream-private.h0000644000175000017500000000317413165463624015345 00000000000000#ifndef IOSTREAM_PRIVATE_H #define IOSTREAM_PRIVATE_H #include "iostream.h" /* This file is private to input stream and output stream implementations */ struct iostream_destroy_callback { void (*callback)(void *context); void *context; }; struct iostream_private { int refcount; char *name; char *error; void (*close)(struct iostream_private *streami, bool close_parent); void (*destroy)(struct iostream_private *stream); void (*set_max_buffer_size)(struct iostream_private *stream, size_t max_size); ARRAY(struct iostream_destroy_callback) destroy_callbacks; }; void io_stream_init(struct iostream_private *stream); void io_stream_ref(struct iostream_private *stream); void io_stream_unref(struct iostream_private *stream); void io_stream_close(struct iostream_private *stream, bool close_parent); void io_stream_set_max_buffer_size(struct iostream_private *stream, size_t max_size); void io_stream_add_destroy_callback(struct iostream_private *stream, void (*callback)(void *), void *context); void io_stream_remove_destroy_callback(struct iostream_private *stream, void (*callback)(void *)); /* Set a specific error for the stream. This shouldn't be used for regular syscall errors where stream's errno is enough, since it's used by default. The stream errno must always be set even if the error string is also set. Setting this error replaces the previously set error. */ void io_stream_set_error(struct iostream_private *stream, const char *fmt, ...) ATTR_FORMAT(2, 3); void io_stream_set_verror(struct iostream_private *stream, const char *fmt, va_list args) ATTR_FORMAT(2, 0); #endif dovecot-2.2.33.2/src/lib/test-net.c0000644000175000017500000001152113165463624013603 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "net.h" struct test_net_is_in_network_input { const char *ip; const char *net; unsigned int bits; bool ret; }; static void test_net_is_in_network(void) { static struct test_net_is_in_network_input input[] = { { "1.2.3.4", "1.2.3.4", 32, TRUE }, { "1.2.3.4", "1.2.3.3", 32, FALSE }, { "1.2.3.4", "1.2.3.5", 32, FALSE }, { "1.2.3.4", "1.2.2.4", 32, FALSE }, { "1.2.3.4", "1.1.3.4", 32, FALSE }, { "1.2.3.4", "0.2.3.4", 32, FALSE }, { "1.2.3.253", "1.2.3.254", 31, FALSE }, { "1.2.3.254", "1.2.3.254", 31, TRUE }, { "1.2.3.255", "1.2.3.254", 31, TRUE }, { "1.2.3.255", "1.2.3.0", 24, TRUE }, { "1.2.255.255", "1.2.254.0", 23, TRUE }, { "255.255.255.255", "128.0.0.0", 1, TRUE }, { "255.255.255.255", "127.0.0.0", 1, FALSE } #ifdef HAVE_IPV6 , { "1234:5678::abcf", "1234:5678::abce", 127, TRUE }, { "1234:5678::abcd", "1234:5678::abce", 127, FALSE }, { "123e::ffff", "123e::0", 15, TRUE }, { "::ffff:1.2.3.4", "1.2.3.4", 32, TRUE }, { "::ffff:1.2.3.4", "1.2.3.3", 32, FALSE }, { "::ffff:1.2.3.4", "::ffff:1.2.3.4", 0, FALSE } #endif }; struct ip_addr ip, net_ip; unsigned int i; test_begin("net_is_in_network()"); for (i = 0; i < N_ELEMENTS(input); i++) { test_assert(net_addr2ip(input[i].ip, &ip) == 0); test_assert(net_addr2ip(input[i].net, &net_ip) == 0); test_assert_idx(net_is_in_network(&ip, &net_ip, input[i].bits) == input[i].ret, i); } /* make sure non-IPv4 and non-IPv6 ip_addrs fail */ test_assert(net_addr2ip("127.0.0.1", &ip) == 0); net_ip = ip; net_ip.family = 0; test_assert(!net_is_in_network(&ip, &net_ip, 0)); test_assert(!net_is_in_network(&net_ip, &ip, 0)); #ifdef HAVE_IPV6 test_assert(net_addr2ip("::1", &ip) == 0); net_ip = ip; net_ip.family = 0; test_assert(!net_is_in_network(&ip, &net_ip, 0)); test_assert(!net_is_in_network(&net_ip, &ip, 0)); #endif test_end(); } static void test_net_ip2addr(void) { struct ip_addr ip; test_begin("net_ip2addr()"); test_assert(net_addr2ip("127.0.0.1", &ip) == 0 && ip.family == AF_INET && ntohl(ip.u.ip4.s_addr) == (0x7f000001)); #ifdef HAVE_IPV6 test_assert(net_addr2ip("::5", &ip) == 0 && ip.family == AF_INET6 && ip.u.ip6.s6_addr[15] == 5); test_assert(net_addr2ip("[::5]", &ip) == 0 && ip.family == AF_INET6 && ip.u.ip6.s6_addr[15] == 5); ip.family = 123; test_assert(net_addr2ip("abc", &ip) < 0 && ip.family == 123); #endif test_end(); } static void test_net_str2hostport(void) { const char *host; in_port_t port; test_begin("net_str2hostport()"); /* [IPv6] */ test_assert(net_str2hostport("[1::4]", 0, &host, &port) == 0 && strcmp(host, "1::4") == 0 && port == 0); test_assert(net_str2hostport("[1::4]", 1234, &host, &port) == 0 && strcmp(host, "1::4") == 0 && port == 1234); test_assert(net_str2hostport("[1::4]:78", 1234, &host, &port) == 0 && strcmp(host, "1::4") == 0 && port == 78); host = NULL; test_assert(net_str2hostport("[1::4]:", 1234, &host, &port) < 0 && host == NULL); test_assert(net_str2hostport("[1::4]:0", 1234, &host, &port) < 0 && host == NULL); test_assert(net_str2hostport("[1::4]:x", 1234, &host, &port) < 0 && host == NULL); /* IPv6 */ test_assert(net_str2hostport("1::4", 0, &host, &port) == 0 && strcmp(host, "1::4") == 0 && port == 0); test_assert(net_str2hostport("1::4", 1234, &host, &port) == 0 && strcmp(host, "1::4") == 0 && port == 1234); /* host */ test_assert(net_str2hostport("foo", 0, &host, &port) == 0 && strcmp(host, "foo") == 0 && port == 0); test_assert(net_str2hostport("foo", 1234, &host, &port) == 0 && strcmp(host, "foo") == 0 && port == 1234); test_assert(net_str2hostport("foo:78", 1234, &host, &port) == 0 && strcmp(host, "foo") == 0 && port == 78); host = NULL; test_assert(net_str2hostport("foo:", 1234, &host, &port) < 0 && host == NULL); test_assert(net_str2hostport("foo:0", 1234, &host, &port) < 0 && host == NULL); test_assert(net_str2hostport("foo:x", 1234, &host, &port) < 0 && host == NULL); /* edge cases with multiple ':' - currently these don't return errors, but perhaps they should. */ test_assert(net_str2hostport("foo::78", 1234, &host, &port) == 0 && strcmp(host, "foo::78") == 0 && port == 1234); test_assert(net_str2hostport("::foo:78", 1234, &host, &port) == 0 && strcmp(host, "::foo:78") == 0 && port == 1234); test_assert(net_str2hostport("[::foo]:78", 1234, &host, &port) == 0 && strcmp(host, "::foo") == 0 && port == 78); test_assert(net_str2hostport("[::::]", 1234, &host, &port) == 0 && strcmp(host, "::::") == 0 && port == 1234); test_assert(net_str2hostport("[::::]:78", 1234, &host, &port) == 0 && strcmp(host, "::::") == 0 && port == 78); test_end(); } void test_net(void) { test_net_is_in_network(); test_net_ip2addr(); test_net_str2hostport(); } dovecot-2.2.33.2/src/lib/unlink-directory.h0000644000175000017500000000103413165463624015345 00000000000000#ifndef UNLINK_DIRECTORY_H #define UNLINK_DIRECTORY_H enum unlink_directory_flags { /* After unlinking all files, rmdir() the directory itself */ UNLINK_DIRECTORY_FLAG_RMDIR = 0x01, /* Don't unlink any files beginning with "." */ UNLINK_DIRECTORY_FLAG_SKIP_DOTFILES = 0x02, /* Don't recurse into subdirectories */ UNLINK_DIRECTORY_FLAG_FILES_ONLY = 0x04 }; /* Unlink directory and/or everything under it. Returns 0 if successful, -1 if error. */ int unlink_directory(const char *dir, enum unlink_directory_flags flags); #endif dovecot-2.2.33.2/src/lib/ipwd.c0000644000175000017500000000410513123174404012770 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #define _POSIX_PTHREAD_SEMANTICS /* for Solaris */ #include "lib.h" #include "ipwd.h" #include #define PWBUF_MIN_SIZE 128 #define GRBUF_MIN_SIZE 128 static void *pwbuf = NULL, *grbuf = NULL; static size_t pwbuf_size, grbuf_size; static void pw_init(void) { size_t old_pwbuf_size = pwbuf_size; if (pwbuf == NULL || errno == ERANGE) { pwbuf_size = nearest_power(old_pwbuf_size + 1); if (pwbuf_size < PWBUF_MIN_SIZE) pwbuf_size = PWBUF_MIN_SIZE; pwbuf = i_realloc(pwbuf, old_pwbuf_size, pwbuf_size); } } static void gr_init(void) { size_t old_grbuf_size = grbuf_size; if (grbuf == NULL || errno == ERANGE) { grbuf_size = nearest_power(old_grbuf_size + 1); if (grbuf_size < PWBUF_MIN_SIZE) grbuf_size = PWBUF_MIN_SIZE; grbuf = i_realloc(grbuf, old_grbuf_size, grbuf_size); } } void ipwd_deinit(void) { i_free_and_null(pwbuf); i_free_and_null(grbuf); } int i_getpwnam(const char *name, struct passwd *pwd_r) { struct passwd *result; errno = 0; do { pw_init(); errno = getpwnam_r(name, pwd_r, pwbuf, pwbuf_size, &result); } while (errno == ERANGE); if (result != NULL) return 1; if (errno == EINVAL) { /* FreeBSD fails here when name="user@domain" */ return 0; } return errno == 0 ? 0 : -1; } int i_getpwuid(uid_t uid, struct passwd *pwd_r) { struct passwd *result; errno = 0; do { pw_init(); errno = getpwuid_r(uid, pwd_r, pwbuf, pwbuf_size, &result); } while (errno == ERANGE); if (result != NULL) return 1; return errno == 0 ? 0 : -1; } int i_getgrnam(const char *name, struct group *grp_r) { struct group *result; errno = 0; do { gr_init(); errno = getgrnam_r(name, grp_r, grbuf, grbuf_size, &result); } while (errno == ERANGE); if (result != NULL) return 1; return errno == 0 ? 0 : -1; } int i_getgrgid(gid_t gid, struct group *grp_r) { struct group *result; errno = 0; do { gr_init(); errno = getgrgid_r(gid, grp_r, grbuf, grbuf_size, &result); } while (errno == ERANGE); if (result != NULL) return 1; return errno == 0 ? 0 : -1; } dovecot-2.2.33.2/src/lib/str.c0000644000175000017500000000651513165463624012657 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "printf-format-fix.h" #include "str.h" #include string_t *str_new(pool_t pool, size_t initial_size) { /* never allocate a 0 byte size buffer. this is especially important when str_c() is called on an empty string from a different stack frame (see the comment in buffer.c about this). */ return buffer_create_dynamic(pool, I_MAX(initial_size, 1)); } string_t *str_new_const(pool_t pool, const char *str, size_t len) { string_t *ret; i_assert(str[len] == '\0'); ret = p_new(pool, buffer_t, 1); buffer_create_from_const_data(ret, str, len + 1); str_truncate(ret, len); return ret; } string_t *t_str_new(size_t initial_size) { return str_new(pool_datastack_create(), initial_size); } string_t *t_str_new_const(const char *str, size_t len) { return str_new_const(pool_datastack_create(), str, len); } void str_free(string_t **str) { buffer_free(str); } static void str_add_nul(string_t *str) { const unsigned char *data = str_data(str); size_t len = str_len(str); size_t alloc = buffer_get_size(str); if (len == alloc || data[len] != '\0') { buffer_write(str, len, "", 1); /* remove the \0 - we don't want to keep it */ buffer_set_used_size(str, len); } } char *str_free_without_data(string_t **str) { str_add_nul(*str); return buffer_free_without_data(str); } const char *str_c(string_t *str) { str_add_nul(str); return buffer_get_data(str, NULL); } char *str_c_modifiable(string_t *str) { str_add_nul(str); return buffer_get_modifiable_data(str, NULL); } bool str_equals(const string_t *str1, const string_t *str2) { if (str1->used != str2->used) return FALSE; return memcmp(str1->data, str2->data, str1->used) == 0; } void str_append_n(string_t *str, const void *cstr, size_t max_len) { size_t len; len = 0; while (len < max_len && ((const char *)cstr)[len] != '\0') len++; buffer_append(str, cstr, len); } void str_printfa(string_t *str, const char *fmt, ...) { va_list args; va_start(args, fmt); str_vprintfa(str, fmt, args); va_end(args); } void str_vprintfa(string_t *str, const char *fmt, va_list args) { #define SNPRINTF_INITIAL_EXTRA_SIZE 128 va_list args2; char *tmp; size_t init_size; size_t pos = str->used; int ret, ret2; VA_COPY(args2, args); /* the format string is modified only if %m exists in it. it happens only in error conditions, so don't try to t_push() here since it'll just slow down the normal code path. */ fmt = printf_format_fix_get_len(fmt, &init_size); init_size += SNPRINTF_INITIAL_EXTRA_SIZE; /* @UNSAFE */ if (pos+init_size > buffer_get_writable_size(str) && pos < buffer_get_writable_size(str)) { /* avoid growing buffer larger if possible. this is also required if buffer isn't dynamically growing. */ init_size = buffer_get_writable_size(str)-pos; } tmp = buffer_get_space_unsafe(str, pos, init_size); ret = vsnprintf(tmp, init_size, fmt, args); i_assert(ret >= 0); if ((unsigned int)ret >= init_size) { /* didn't fit with the first guess. now we know the size, so try again. */ tmp = buffer_get_space_unsafe(str, pos, ret + 1); ret2 = vsnprintf(tmp, ret + 1, fmt, args2); i_assert(ret2 == ret); } va_end(args2); /* drop the unused data, including terminating NUL */ buffer_set_used_size(str, pos + ret); } dovecot-2.2.33.2/src/lib/test-istream-multiplex.c0000644000175000017500000002431513167162046016503 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "ioloop.h" #include "fd-set-nonblock.h" #include "str.h" #include "crc32.h" #include "randgen.h" #include "istream-private.h" #include "istream-multiplex.h" #include "ostream.h" #include static void test_istream_multiplex_simple(void) { test_begin("istream multiplex (simple)"); static const char data[] = "\x00\x00\x00\x00\x06Hello\x00" "\x01\x00\x00\x00\x03Wor" "\x00\x00\x00\x00\x00" "\x01\x00\x00\x00\x03ld\x00"; static const size_t data_len = sizeof(data)-1; struct istream *input = test_istream_create_data(data, data_len); size_t siz; struct istream *chan0 = i_stream_create_multiplex(input, (size_t)-1); struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); /* nothing to read until the first byte */ for (size_t i = 0; i <= 1+4; i++) { test_istream_set_size(input, i); test_assert(i_stream_read(chan0) == 0); test_assert(i_stream_read(chan1) == 0); } /* partial read of the first packet */ size_t input_max = 1+4+3; test_istream_set_size(input, input_max); test_assert(i_stream_read(chan0) == 3); test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hel", 3) == 0 && siz == 3); test_assert(i_stream_read(chan1) == 0); /* read the rest of the first packet and the second packet. read chan1 before chan0 to see that it works. */ input_max += 3 + 1+4+3; test_istream_set_size(input, input_max); test_assert(i_stream_read(chan1) == 3); test_assert(i_stream_read(chan0) == 3); test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && siz == 6); test_assert(memcmp(i_stream_get_data(chan1, &siz), "Wor", 3) == 0 && siz == 3); /* 0-sized packet is ignored */ input_max += 1+4; test_istream_set_size(input, input_max); test_assert(i_stream_read(chan0) == 0); test_assert(i_stream_read(chan1) == 0); /* read the final packet */ input_max += 1+4+3; i_assert(input_max == data_len); test_istream_set_size(input, input_max); test_assert(i_stream_read(chan0) == 0); test_assert(i_stream_read(chan1) == 3); /* we should have the final data in all channels now */ test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && siz == 6); test_assert(memcmp(i_stream_get_data(chan1, &siz), "World\0", 6) == 0 && siz == 6); /* all channels should return EOF */ test_assert(i_stream_read(chan0) == -1 && chan0->stream_errno == 0); i_stream_unref(&chan0); test_assert(i_stream_read(chan1) == -1 && chan1->stream_errno == 0); i_stream_unref(&chan1); i_stream_unref(&input); test_end(); } static void test_istream_multiplex_maxbuf(void) { test_begin("istream multiplex (maxbuf)"); static const char data[] = "\x00\x00\x00\x00\x06Hello\x00" "\x01\x00\x00\x00\x06World\x00"; static const size_t data_len = sizeof(data)-1; struct istream *input = test_istream_create_data(data, data_len); size_t siz; struct istream *chan0 = i_stream_create_multiplex(input, 5); struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); /* we get data for channel 0 and congest */ test_assert(i_stream_read(chan1) == 0); /* we read data for channel 0 */ test_assert(i_stream_read(chan0) == 5); /* and now it's congested */ test_assert(i_stream_read(chan0) == -2); test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello", 5) == 0 && siz == 5); /* consume data */ i_stream_skip(chan0, 5); /* we read data for channel 1 */ test_assert(i_stream_read(chan1) == 5); test_assert(memcmp(i_stream_get_data(chan1, &siz), "World", 5) == 0 && siz == 5); /* consume data */ i_stream_skip(chan1, 5); /* read last byte */ test_assert(i_stream_read(chan0) == 1); /* now we get byte for channel 1 */ test_assert(i_stream_read(chan0) == 0); /* now we read byte for channel 1 */ test_assert(i_stream_read(chan1) == 1); /* and everything should return EOF now */ test_assert(i_stream_read(chan1) == -1); test_assert(i_stream_read(chan0) == -1); i_stream_unref(&chan0); i_stream_unref(&chan1); i_stream_unref(&input); test_end(); } static void test_istream_multiplex_random(void) { const unsigned int max_channel = 6; const unsigned int packets_count = 30; test_begin("istream multiplex (random)"); unsigned int i; uoff_t bytes_written = 0, bytes_read = 0; buffer_t *buf = buffer_create_dynamic(default_pool, 10240); uint32_t input_crc[max_channel]; uint32_t output_crc[max_channel]; memset(input_crc, 0, sizeof(input_crc)); memset(output_crc, 0, sizeof(output_crc)); for (i = 0; i < packets_count; i++) { unsigned int len = 1 + rand() % 1024; unsigned char packet_data[len]; uint32_t len_be = cpu32_to_be(len); unsigned int channel = rand() % max_channel; random_fill(packet_data, len); input_crc[channel] = crc32_data_more(input_crc[channel], packet_data, len); buffer_append_c(buf, channel); buffer_append(buf, &len_be, sizeof(len_be)); buffer_append(buf, packet_data, len); bytes_written += len; } struct istream *input = test_istream_create_data(buf->data, buf->used); struct istream *chan[max_channel]; chan[0] = i_stream_create_multiplex(input, 1024/4); for (i = 1; i < max_channel; i++) chan[i] = i_stream_multiplex_add_channel(chan[0], i); test_istream_set_size(input, 0); /* read from each stream, 1 byte at a time */ size_t input_size = 0; int max_ret = -3; unsigned int read_max_channel = max_channel/2; bool something_read = FALSE; for (i = 0;;) { ssize_t ret = i_stream_read(chan[i]); if (max_ret < ret) max_ret = ret; if (ret > 0) { size_t size; const unsigned char *data = i_stream_get_data(chan[i], &size); output_crc[i] = crc32_data_more(output_crc[i], data, size); bytes_read += size; test_assert((size_t)ret == size); i_stream_skip(chan[i], size); something_read = TRUE; } if (++i < read_max_channel) ; else if (max_ret == 0 && !something_read && read_max_channel < max_channel) { read_max_channel++; } else { if (max_ret <= -1) { test_assert(max_ret == -1); break; } if (max_ret == 0) test_istream_set_size(input, ++input_size); i = 0; max_ret = -3; something_read = FALSE; read_max_channel = max_channel/2; } } test_assert(bytes_read == bytes_written); for (i = 0; i < max_channel; i++) { test_assert_idx(input_crc[i] == output_crc[i], i); test_assert_idx(i_stream_read(chan[i]) == -1 && chan[i]->stream_errno == 0, i); i_stream_unref(&chan[i]); } i_stream_unref(&input); buffer_free(&buf); test_end(); } static unsigned int channel_counter[2] = {0, 0}; static const char *msgs[] = { "", "a", "bb", "ccc", "dddd", "eeeee", "ffffff" }; static void test_istream_multiplex_stream_read(struct istream *channel) { uint8_t cid = i_stream_multiplex_get_channel_id(channel); const char *line; size_t siz; if (i_stream_read(channel) < 0) return; while((line = i_stream_next_line(channel)) != NULL) { siz = strlen(line); test_assert_idx(siz > 0 && siz < N_ELEMENTS(msgs), channel_counter[cid]); if (siz > 0 && siz < N_ELEMENTS(msgs)) { test_assert_idx(strcmp(line, msgs[siz]) == 0, channel_counter[cid]); } channel_counter[cid]++; } if (channel_counter[0] > 100 && channel_counter[1] > 100) io_loop_stop(current_ioloop); } static void test_send_msg(struct ostream *os, uint8_t cid, const char *msg) { uint32_t len = cpu32_to_be(strlen(msg) + 1); const struct const_iovec iov[] = { { &cid, sizeof(cid) }, { &len, sizeof(len) }, { msg, strlen(msg) }, { "\n", 1 } /* newline added for i_stream_next_line */ }; o_stream_nsendv(os, iov, N_ELEMENTS(iov)); } static void test_istream_multiplex_stream_write(struct ostream *channel) { size_t rounds = rand() % 10; for(size_t i = 0; i < rounds; i++) { uint8_t cid = rand() % 2; test_send_msg(channel, cid, msgs[1 + rand() % (N_ELEMENTS(msgs) - 1)]); } } static void test_istream_multiplex_stream(void) { test_begin("istream multiplex (stream)"); struct ioloop *ioloop = io_loop_create(); io_loop_set_current(ioloop); int fds[2]; test_assert(pipe(fds) == 0); fd_set_nonblock(fds[0], TRUE); fd_set_nonblock(fds[1], TRUE); struct ostream *os = o_stream_create_fd(fds[1], (size_t)-1, FALSE); struct istream *is = i_stream_create_fd(fds[0], 10 + rand() % 10, FALSE); struct istream *chan0 = i_stream_create_multiplex(is, (size_t)-1); struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); struct io *io0 = io_add_istream(chan0, test_istream_multiplex_stream_read, chan0); struct io *io1 = io_add_istream(chan1, test_istream_multiplex_stream_read, chan1); struct io *io2 = io_add(fds[1], IO_WRITE, test_istream_multiplex_stream_write, os); io_loop_run(current_ioloop); io_remove(&io0); io_remove(&io1); io_remove(&io2); i_stream_unref(&chan1); i_stream_unref(&chan0); i_stream_unref(&is); test_assert(o_stream_nfinish(os) == 0); o_stream_unref(&os); io_loop_destroy(&ioloop); i_close_fd(&fds[0]); i_close_fd(&fds[1]); test_end(); } static void test_istream_multiplex_close_channel(void) { test_begin("istream multiplex (close channel)"); static const char *data = "\x00\x00\x00\x00\x06Hello\x00" "\x01\x00\x00\x00\x06World\x00"; static const size_t data_len = 22; struct istream *input = test_istream_create_data(data, data_len); size_t siz; struct istream *chan0 = i_stream_create_multiplex(input, (size_t)-1); struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); i_stream_unref(&chan1); test_assert(i_stream_read(chan0) == 6); test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && siz == 6); i_stream_unref(&chan0); i_stream_unref(&input); input = test_istream_create_data(data, data_len); chan0 = i_stream_create_multiplex(input, (size_t)-1); chan1 = i_stream_multiplex_add_channel(chan0, 1); /* this is needed to populate chan1 data */ (void)i_stream_read(chan0); i_stream_unref(&chan0); test_assert(i_stream_read(chan1) == 6); test_assert(memcmp(i_stream_get_data(chan1, &siz), "World\0", 6) == 0 && siz == 6); i_stream_unref(&chan1); i_stream_unref(&input); test_end(); } void test_istream_multiplex(void) { random_init(); test_istream_multiplex_simple(); test_istream_multiplex_maxbuf(); test_istream_multiplex_random(); test_istream_multiplex_stream(); test_istream_multiplex_close_channel(); random_deinit(); } dovecot-2.2.33.2/src/lib/mmap-anon.c0000644000175000017500000000762713123174404013724 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "mmap-util.h" #include #ifndef MAP_ANONYMOUS # ifdef MAP_ANON # define MAP_ANONYMOUS MAP_ANON # else # define MAP_ANONYMOUS 0 # endif #endif #ifndef HAVE_LINUX_MREMAP #include "fd-close-on-exec.h" #include #define MMAP_SIGNATURE 0xdeadbeef #define PAGE_ALIGN(size) \ (((size) + (size_t)page_size-1) & ~(size_t)(page_size-1)) struct anon_header { unsigned int signature; size_t size; }; static int page_size = 0; static int header_size = 0; static int zero_fd = -1; static void movable_mmap_init(void) { #if MAP_ANONYMOUS == 0 /* mmap()ing /dev/zero should be the same with some platforms */ zero_fd = open("/dev/zero", O_RDWR); if (zero_fd == -1) i_fatal("Can't open /dev/zero for creating anonymous mmap: %m"); fd_close_on_exec(zero_fd, TRUE); #endif page_size = getpagesize(); header_size = page_size; } void *mmap_anon(size_t length) { struct anon_header *hdr; void *base; if (header_size == 0) movable_mmap_init(); /* we need extra page to store the pieces which construct the full mmap. also allocate only page-aligned mmap sizes. */ length = PAGE_ALIGN(length + header_size); base = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, zero_fd, 0); if (base == MAP_FAILED) return MAP_FAILED; /* initialize the header */ hdr = base; hdr->signature = MMAP_SIGNATURE; hdr->size = length - header_size; return (char *) hdr + header_size; } static void *mremap_move(struct anon_header *hdr, size_t new_size) { void *new_base; char *p; size_t block_size, old_size; new_base = mmap_anon(new_size); if (new_base == MAP_FAILED) return MAP_FAILED; /* If we're moving large memory areas, it takes less memory to copy the memory pages in smaller blocks. */ old_size = hdr->size; block_size = 1024*1024; p = (char *) hdr + header_size + hdr->size; do { if (block_size > old_size) block_size = old_size; p -= block_size; old_size -= block_size; memcpy((char *) new_base + old_size, p, block_size); if (munmap((void *) p, block_size) < 0) i_panic("munmap() failed: %m"); } while (old_size != 0); if (munmap((void *) hdr, header_size) < 0) i_panic("munmap() failed: %m"); return new_base; } void *mremap_anon(void *old_address, size_t old_size ATTR_UNUSED, size_t new_size, unsigned long flags) { struct anon_header *hdr; if (old_address == NULL || old_address == MAP_FAILED) { errno = EINVAL; return MAP_FAILED; } hdr = (struct anon_header *) ((char *) old_address - header_size); if (hdr->signature != MMAP_SIGNATURE) i_panic("movable_mremap(): Invalid old_address"); new_size = PAGE_ALIGN(new_size); if (new_size > hdr->size) { /* grow */ if ((flags & MREMAP_MAYMOVE) == 0) { errno = ENOMEM; return MAP_FAILED; } return mremap_move(hdr, new_size); } if (new_size < hdr->size) { /* shrink */ if (munmap((void *) ((char *) hdr + header_size + new_size), hdr->size - new_size) < 0) i_panic("munmap() failed: %m"); hdr->size = new_size; } return old_address; } int munmap_anon(void *start, size_t length ATTR_UNUSED) { struct anon_header *hdr; if (start == NULL || start == MAP_FAILED) { errno = EINVAL; return -1; } hdr = (struct anon_header *) ((char *) start - header_size); if (hdr->signature != MMAP_SIGNATURE) i_panic("movable_munmap(): Invalid address"); if (munmap((void *) hdr, hdr->size + header_size) < 0) i_panic("munmap() failed: %m"); return 0; } #else void *mmap_anon(size_t length) { return mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); } void *mremap_anon(void *old_address, size_t old_size, size_t new_size, unsigned long flags) { return mremap(old_address, old_size, new_size, flags); } int munmap_anon(void *start, size_t length) { return munmap(start, length); } #endif dovecot-2.2.33.2/src/lib/istream-jsonstr.h0000644000175000017500000000027513123174404015202 00000000000000#ifndef ISTREAM_JSONSTR_H #define ISTREAM_JSONSTR_H /* Parse input until '"' is reached. Unescape JSON \x codes. */ struct istream *i_stream_create_jsonstr(struct istream *input); #endif dovecot-2.2.33.2/src/lib/sha1.c0000644000175000017500000002213413123174404012663 00000000000000/* $KAME: sha1.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) * based on: http://csrc.nist.gov/fips/fip180-1.txt * implemented by Jun-ichiro itojun Itoh */ #include "lib.h" #include "sha1.h" #include "safe-memset.h" /* constant table */ static uint32_t SHA1_K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; #define K(t) SHA1_K[(t) / 20] #define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) #define F1(b, c, d) (((b) ^ (c)) ^ (d)) #define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) #define F3(b, c, d) (((b) ^ (c)) ^ (d)) #define S(n, x) (((x) << (n)) | ((x) >> (32 - n))) #define H(n) (ctxt->h.b32[(n)]) #define COUNT (ctxt->count) #define BCOUNT (ctxt->c.b64[0] / 8) #define W(n) (ctxt->m.b32[(n)]) #define PUTBYTE(x) { \ ctxt->m.b8[(COUNT % 64)] = (x); \ COUNT++; \ COUNT %= 64; \ ctxt->c.b64[0] += 8; \ if (COUNT % 64 == 0) \ sha1_step(ctxt); \ } #define PUTPAD(x) { \ ctxt->m.b8[(COUNT % 64)] = (x); \ COUNT++; \ COUNT %= 64; \ if (COUNT % 64 == 0) \ sha1_step(ctxt); \ } static void sha1_step(struct sha1_ctxt *); static void ATTR_UNSIGNED_WRAPS sha1_step(struct sha1_ctxt *ctxt) { uint32_t a, b, c, d, e; size_t t, s; uint32_t tmp; #if !WORDS_BIGENDIAN struct sha1_ctxt tctxt; memmove(&tctxt.m.b8[0], &ctxt->m.b8[0], 64); ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2]; ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0]; ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6]; ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4]; ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10]; ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8]; ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14]; ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12]; ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18]; ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16]; ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22]; ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20]; ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26]; ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24]; ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30]; ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28]; ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34]; ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32]; ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38]; ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36]; ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42]; ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40]; ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46]; ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44]; ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50]; ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48]; ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54]; ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52]; ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58]; ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56]; ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62]; ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60]; #endif a = H(0); b = H(1); c = H(2); d = H(3); e = H(4); for (t = 0; t < 20; t++) { s = t & 0x0f; if (t >= 16) { W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); } tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 20; t < 40; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 40; t < 60; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 60; t < 80; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } H(0) = H(0) + a; H(1) = H(1) + b; H(2) = H(2) + c; H(3) = H(3) + d; H(4) = H(4) + e; memset(&ctxt->m.b8[0], 0, 64); } /*------------------------------------------------------------*/ void sha1_init(struct sha1_ctxt *ctxt) { memset(ctxt, 0, sizeof(struct sha1_ctxt)); H(0) = 0x67452301; H(1) = 0xefcdab89; H(2) = 0x98badcfe; H(3) = 0x10325476; H(4) = 0xc3d2e1f0; } void sha1_pad(struct sha1_ctxt *ctxt) { size_t padlen; /*pad length in bytes*/ size_t padstart; PUTPAD(0x80); padstart = COUNT % 64; padlen = 64 - padstart; if (padlen < 8) { memset(&ctxt->m.b8[padstart], 0, padlen); COUNT += padlen; COUNT %= 64; sha1_step(ctxt); padstart = COUNT % 64; /* should be 0 */ padlen = 64 - padstart; /* should be 64 */ } memset(&ctxt->m.b8[padstart], 0, padlen - 8); COUNT += (padlen - 8); COUNT %= 64; #if WORDS_BIGENDIAN PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]); #else PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]); #endif } void sha1_loop(struct sha1_ctxt *ctxt, const void *input, size_t len) { const unsigned char *input_c = input; size_t gaplen; size_t gapstart; size_t off; size_t copysiz; off = 0; while (off < len) { gapstart = COUNT % 64; gaplen = 64 - gapstart; copysiz = (gaplen < len - off) ? gaplen : len - off; memmove(&ctxt->m.b8[gapstart], &input_c[off], copysiz); COUNT += copysiz; COUNT %= 64; ctxt->c.b64[0] += copysiz * 8; if (COUNT % 64 == 0) sha1_step(ctxt); off += copysiz; } } void sha1_result(struct sha1_ctxt *ctxt, void *digest0) { uint8_t *digest; digest = (uint8_t *)digest0; sha1_pad(ctxt); #if WORDS_BIGENDIAN memmove(digest, &ctxt->h.b8[0], 20); #else digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; #endif safe_memset(ctxt, 0, sizeof(struct sha1_ctxt)); } void sha1_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY SHA1_RESULTLEN]) { struct sha1_ctxt ctx; sha1_init(&ctx); sha1_loop(&ctx, data, size); sha1_result(&ctx, result); } static void hash_method_init_sha1(void *context) { sha1_init(context); } static void hash_method_loop_sha1(void *context, const void *data, size_t size) { sha1_loop(context, data, size); } static void hash_method_result_sha1(void *context, unsigned char *result_r) { sha1_result(context, result_r); } const struct hash_method hash_method_sha1 = { "sha1", sizeof(struct sha1_ctxt), SHA1_RESULTLEN, hash_method_init_sha1, hash_method_loop_sha1, hash_method_result_sha1 }; dovecot-2.2.33.2/src/lib/istream-callback.c0000644000175000017500000000600413123174404015223 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream-private.h" #include "istream-callback.h" struct callback_istream { struct istream_private istream; istream_callback_read_t *callback; void *context; buffer_t *buf; size_t prev_pos; }; static void i_stream_callback_destroy(struct iostream_private *stream) { struct callback_istream *cstream = (struct callback_istream *)stream; buffer_free(&cstream->buf); } static ssize_t i_stream_callback_read(struct istream_private *stream) { struct callback_istream *cstream = (struct callback_istream *)stream; size_t pos; if (cstream->callback == NULL) { /* already returned EOF / error */ stream->istream.eof = TRUE; return -1; } if (stream->skip > 0) { buffer_delete(cstream->buf, 0, stream->skip); stream->pos -= stream->skip; cstream->prev_pos -= stream->skip; stream->skip = 0; } i_assert(cstream->buf->used >= cstream->prev_pos); pos = cstream->prev_pos; if (cstream->buf->used > pos) { /* data was added outside the callback */ } else if (!cstream->callback(cstream->buf, cstream->context)) { /* EOF / error */ stream->istream.eof = TRUE; cstream->callback = NULL; if (cstream->buf->used == pos || stream->istream.stream_errno != 0) return -1; /* EOF was returned with some data still added to the buffer. return the buffer first and EOF only on the next call. */ } else if (cstream->buf->used == pos) { /* buffer full */ i_assert(cstream->buf->used > 0); return -2; } i_assert(cstream->buf->used > pos); stream->buffer = cstream->buf->data; cstream->prev_pos = stream->pos = cstream->buf->used; return cstream->buf->used - pos; } #undef i_stream_create_callback struct istream * i_stream_create_callback(istream_callback_read_t *callback, void *context) { struct callback_istream *cstream; struct istream *istream; i_assert(callback != NULL); cstream = i_new(struct callback_istream, 1); cstream->callback = callback; cstream->context = context; cstream->buf = buffer_create_dynamic(default_pool, 1024); cstream->istream.iostream.destroy = i_stream_callback_destroy; cstream->istream.read = i_stream_callback_read; istream = i_stream_create(&cstream->istream, NULL, -1); istream->blocking = TRUE; return istream; } void i_stream_callback_append(struct istream *input, const void *data, size_t size) { struct callback_istream *cstream = (struct callback_istream *)input->real_stream; buffer_append(cstream->buf, data, size); } void i_stream_callback_append_str(struct istream *input, const char *str) { i_stream_callback_append(input, str, strlen(str)); } buffer_t *i_stream_callback_get_buffer(struct istream *input) { struct callback_istream *cstream = (struct callback_istream *)input->real_stream; return cstream->buf; } void i_stream_callback_set_error(struct istream *input, int stream_errno, const char *error) { input->stream_errno = stream_errno; io_stream_set_error(&input->real_stream->iostream, "%s", error); } dovecot-2.2.33.2/src/lib/utc-mktime.h0000644000175000017500000000052113123174404014107 00000000000000#ifndef UTC_MKTIME_H #define UTC_MKTIME_H #include /* Like mktime(), but assume that tm is in UTC. Unlike mktime(), values in tm fields must be in valid range. Leap second is accepted any time though since utc_mktime is often used before applying the time zone offset. */ time_t utc_mktime(const struct tm *tm); #endif dovecot-2.2.33.2/src/lib/mkdir-parents.h0000644000175000017500000000261613123174404014617 00000000000000#ifndef MKDIR_PARENTS_H #define MKDIR_PARENTS_H #include /* Create path and all the directories under it if needed. Permissions for existing directories isn't changed. Returns 0 if ok. If directory already exists, returns -1 with errno=EEXIST. */ int mkdir_parents(const char *path, mode_t mode); /* Like mkdir_parents(), but use the given uid/gid for newly created directories. (uid_t)-1 or (gid_t)-1 can be used to indicate that it doesn't need to be changed. If gid isn't (gid_t)-1 and the parent directory had setgid-bit enabled, it's removed unless explicitly included in the mode. */ int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); /* Like mkdir_parents_chown(), but change only group. If chown() fails with EACCES, use gid_origin in the error message. */ int mkdir_parents_chgrp(const char *path, mode_t mode, gid_t gid, const char *gid_origin); /* Like mkdir_parents_chown(), but don't actually create any parents. */ int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int mkdir_chgrp(const char *path, mode_t mode, gid_t gid, const char *gid_origin); /* stat() the path or its first parent that exists. Returns 0 if ok, -1 if failed. root_dir is set to the last stat()ed directory (on success and on failure). */ int stat_first_parent(const char *path, const char **root_dir_r, struct stat *st_r); #endif dovecot-2.2.33.2/src/lib/lib-signals.h0000644000175000017500000000364613165463624014262 00000000000000#ifndef LIB_SIGNALS_H #define LIB_SIGNALS_H #include enum libsig_flags { /* Signal handler will be called later from IO loop when it's safe to do any kind of work */ LIBSIG_FLAG_DELAYED = 0x01, /* Restart syscalls instead of having them fail with EINTR */ LIBSIG_FLAG_RESTART = 0x02 }; #define LIBSIG_FLAGS_SAFE (LIBSIG_FLAG_DELAYED | LIBSIG_FLAG_RESTART) typedef void signal_handler_t(const siginfo_t *si, void *context); /* Number of times a "termination signal" has been received. These signals are SIGINT, SIGQUIT and SIGTERM. Callers can compare this to their saved previous value to see if a syscall returning EINTR should be treated as someone wanting to end the process or just some internal signal that should be ignored, such as SIGCHLD. This is marked as volatile so that compiler won't optimize away its comparisons. It may not work perfectly everywhere, such as when accessing it isn't atomic, so you shouldn't heavily rely on its actual value. */ extern volatile unsigned int signal_term_counter; /* Convert si_code to string */ const char *lib_signal_code_to_str(int signo, int sicode); /* Set signal handler for specific signal. */ void lib_signals_set_handler(int signo, enum libsig_flags flags, signal_handler_t *handler, void *context) ATTR_NULL(4); /* Ignore given signal. */ void lib_signals_ignore(int signo, bool restart_syscalls); void lib_signals_unset_handler(int signo, signal_handler_t *handler, void *context) ATTR_NULL(3); /* Remove and add the internal I/O handler back. This is necessary to get the delayed signals to work when using multiple I/O loops. */ void lib_signals_reset_ioloop(void); /* Log a syscall error inside a (non-delayed) signal handler where i_error() is unsafe. errno number will be appended to the prefix. */ void lib_signals_syscall_error(const char *prefix); void lib_signals_init(void); void lib_signals_deinit(void); #endif dovecot-2.2.33.2/src/lib/test-malloc-overflow.c0000644000175000017500000000600513165463624016126 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" static void test_malloc_overflow_multiply(void) { const struct { size_t a, b; } tests[] = { { 0, SIZE_MAX }, { 1, SIZE_MAX }, { SIZE_MAX/2, 2 }, }; test_begin("MALLOC_MULTIPLY()"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(MALLOC_MULTIPLY(tests[i].a, tests[i].b) == tests[i].a * tests[i].b, i); test_assert_idx(MALLOC_MULTIPLY(tests[i].b, tests[i].a) == tests[i].b * tests[i].a, i); } test_end(); } static void test_malloc_overflow_add(void) { const struct { size_t a, b; } tests[] = { { 0, SIZE_MAX }, { 1, SIZE_MAX-1 }, { SIZE_MAX/2+1, SIZE_MAX/2 }, }; unsigned short n = 2; test_begin("MALLOC_ADD()"); /* check that no compiler warning is given */ test_assert(MALLOC_ADD(2, n) == 2U+n); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(MALLOC_ADD(tests[i].a, tests[i].b) == tests[i].a + tests[i].b, i); test_assert_idx(MALLOC_ADD(tests[i].b, tests[i].a) == tests[i].b + tests[i].a, i); } test_end(); } void test_malloc_overflow(void) { test_malloc_overflow_multiply(); test_malloc_overflow_add(); } static enum fatal_test_state fatal_malloc_overflow_multiply(unsigned int *stage) { const struct { size_t a, b; } mul_tests[] = { { SIZE_MAX/2+1, 2 }, }; unsigned int i; switch (*stage) { case 0: test_begin("MALLOC_MULTIPLY() overflows"); i_error("%"PRIuSIZE_T, MALLOC_MULTIPLY((size_t)SIZE_MAX/2, (uint8_t)3)); break; case 1: i_error("%"PRIuSIZE_T, MALLOC_MULTIPLY((uint8_t)3, (size_t)SIZE_MAX/2)); break; } *stage -= 2; if (*stage >= N_ELEMENTS(mul_tests)*2) { *stage -= N_ELEMENTS(mul_tests)*2; if (*stage == 0) test_end(); return FATAL_TEST_FINISHED; } i = *stage / 2; if (*stage % 2 == 0) i_error("%"PRIuSIZE_T, MALLOC_MULTIPLY(mul_tests[i].a, mul_tests[i].b)); else i_error("%"PRIuSIZE_T, MALLOC_MULTIPLY(mul_tests[i].b, mul_tests[i].a)); return FATAL_TEST_FAILURE; } static enum fatal_test_state fatal_malloc_overflow_add(unsigned int *stage) { const struct { size_t a, b; } add_tests[] = { { SIZE_MAX, 1 }, { SIZE_MAX/2+1, SIZE_MAX/2+1 }, }; unsigned int i; switch (*stage) { case 0: test_begin("MALLOC_ADD() overflows"); i_error("%"PRIuSIZE_T, MALLOC_ADD((size_t)SIZE_MAX, (uint8_t)1)); break; case 1: i_error("%"PRIuSIZE_T, MALLOC_ADD((uint8_t)1, (size_t)SIZE_MAX)); break; } *stage -= 2; if (*stage >= N_ELEMENTS(add_tests)*2) { *stage -= N_ELEMENTS(add_tests)*2; if (*stage == 0) test_end(); return FATAL_TEST_FINISHED; } i = *stage / 2; if (*stage % 2 == 0) i_error("%"PRIuSIZE_T, MALLOC_ADD(add_tests[i].a, add_tests[i].b)); else i_error("%"PRIuSIZE_T, MALLOC_ADD(add_tests[i].b, add_tests[i].a)); return FATAL_TEST_FAILURE; } enum fatal_test_state fatal_malloc_overflow(unsigned int stage) { enum fatal_test_state state; state = fatal_malloc_overflow_multiply(&stage); if (state != FATAL_TEST_FINISHED) return state; return fatal_malloc_overflow_add(&stage); } dovecot-2.2.33.2/src/lib/mountpoint.c0000644000175000017500000001772413123174404014254 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mountpoint.h" #include #ifdef HAVE_SYS_VMOUNT_H # include # include /* AIX */ # define MOUNTPOINT_AIX_MNTCTL #elif defined(HAVE_STATVFS_MNTFROMNAME) # include /* NetBSD 3.0+, FreeBSD 5.0+ */ # define STATVFS_STR "statvfs" # define MOUNTPOINT_STATVFS #elif defined(HAVE_STATFS_MNTFROMNAME) # include /* Older BSDs */ # include # define statvfs statfs # define STATVFS_STR "statfs" # define MOUNTPOINT_STATVFS #elif defined(HAVE_MNTENT_H) # include # include /* Linux */ # define MOUNTPOINT_LINUX #elif defined(HAVE_SYS_MNTTAB_H) # include # include /* Solaris */ # include # define MOUNTPOINT_SOLARIS #else # define MOUNTPOINT_UNKNOWN #endif #ifdef MOUNTPOINT_SOLARIS # define MTAB_PATH MNTTAB /* Solaris */ #else # define MTAB_PATH "/etc/mtab" /* Linux */ #endif /* AIX doesn't have these defined */ #ifndef MNTTYPE_SWAP # define MNTTYPE_SWAP "swap" #endif #ifndef MNTTYPE_IGNORE # define MNTTYPE_IGNORE "ignore" #endif #ifndef MNTTYPE_JFS # define MNTTYPE_JFS "jfs" #endif #ifndef MNTTYPE_NFS # define MNTTYPE_NFS "nfs" #endif /* Linux sometimes has mtab entry for "rootfs" as well as the real root entry. Skip the rootfs. */ #ifndef MNTTYPE_ROOTFS # define MNTTYPE_ROOTFS "rootfs" #endif #ifdef MOUNTPOINT_STATVFS static int mountpoint_get_statvfs(const char *path, pool_t pool, struct mountpoint *point_r) { struct statvfs buf; i_zero(point_r); if (statvfs(path, &buf) < 0) { if (errno == ENOENT) return 0; i_error(STATVFS_STR"(%s) failed: %m", path); return -1; } point_r->device_path = p_strdup(pool, buf.f_mntfromname); point_r->mount_path = p_strdup(pool, buf.f_mntonname); #ifdef __osf__ /* Tru64 */ point_r->type = p_strdup(pool, getvfsbynumber(buf.f_type)); #else point_r->type = p_strdup(pool, buf.f_fstypename); #endif point_r->block_size = buf.f_bsize; return 1; } #endif int mountpoint_get(const char *path, pool_t pool, struct mountpoint *point_r) { #ifdef MOUNTPOINT_UNKNOWN i_zero(point_r); errno = ENOSYS; return -1; #elif defined (MOUNTPOINT_STATVFS) /* BSDs, Tru64 */ return mountpoint_get_statvfs(path, pool, point_r); #else /* find via mount iteration */ struct mountpoint_iter *iter; const struct mountpoint *mnt; struct stat st; i_zero(point_r); if (stat(path, &st) < 0) { if (errno == ENOENT) return 0; i_error("stat(%s) failed: %m", path); return -1; } iter = mountpoint_iter_init(); while ((mnt = mountpoint_iter_next(iter)) != NULL) { if (minor(st.st_dev) == minor(mnt->dev) && major(st.st_dev) == major(mnt->dev)) break; } if (mnt != NULL) { point_r->device_path = p_strdup(pool, mnt->device_path); point_r->mount_path = p_strdup(pool, mnt->mount_path); point_r->type = p_strdup(pool, mnt->type); point_r->dev = mnt->dev; point_r->block_size = st.st_blksize; } if (mountpoint_iter_deinit(&iter) < 0 && mnt == NULL) return -1; return mnt != NULL ? 1 : 0; #endif } struct mountpoint_iter { #ifdef MOUNTPOINT_AIX_MNTCTL char *mtab; struct vmount *vmt; int count; #elif defined(MOUNTPOINT_SOLARIS) || defined(MOUNTPOINT_LINUX) FILE *f; #elif defined(HAVE_GETMNTINFO) /* BSDs */ #ifndef __NetBSD__ struct statfs *fs; #else struct statvfs *fs; #endif int count; #endif struct mountpoint mnt; bool failed; }; struct mountpoint_iter *mountpoint_iter_init(void) { struct mountpoint_iter *iter = i_new(struct mountpoint_iter, 1); #ifdef MOUNTPOINT_AIX_MNTCTL unsigned int size = STATIC_MTAB_SIZE; char *mtab; int count; mtab = t_buffer_get(size); while ((count = mntctl(MCTL_QUERY, size, mtab)) == 0) { size = *(unsigned int *)mtab; mtab = t_buffer_get(size); } if (count < 0) { i_error("mntctl(MCTL_QUERY) failed: %m"); iter->failed = TRUE; return iter; } iter->count = count; iter->mtab = i_malloc(size); memcpy(iter->mtab, mtab, size); iter->vmt = (void *)iter->mtab; #elif defined(MOUNTPOINT_SOLARIS) iter->f = fopen(MTAB_PATH, "r"); if (iter->f == NULL) { i_error("fopen(%s) failed: %m", MTAB_PATH); iter->failed = TRUE; return iter; } resetmnttab(iter->f); #elif defined(MOUNTPOINT_LINUX) iter->f = setmntent(MTAB_PATH, "r"); if (iter->f == NULL) { i_error("setmntent(%s) failed: %m", MTAB_PATH); iter->failed = TRUE; } #elif defined(HAVE_GETMNTINFO) /* BSDs */ iter->count = getmntinfo(&iter->fs, MNT_NOWAIT); if (iter->count < 0) { i_error("getmntinfo() failed: %m"); iter->failed = TRUE; } #else iter->failed = TRUE; #endif return iter; } const struct mountpoint *mountpoint_iter_next(struct mountpoint_iter *iter) { #ifdef MOUNTPOINT_AIX_MNTCTL struct vmount *vmt = iter->vmt; char *vmt_base = (char *)vmt; char *vmt_object, *vmt_stub, *vmt_hostname; struct stat vst; if (iter->count == 0) return NULL; iter->count--; iter->vmt = PTR_OFFSET(vmt, vmt->vmt_length); vmt_hostname = vmt_base + vmt->vmt_data[VMT_HOSTNAME].vmt_off; vmt_object = vmt_base + vmt->vmt_data[VMT_OBJECT].vmt_off; vmt_stub = vmt_base + vmt->vmt_data[VMT_STUB].vmt_off; i_zero(&iter->mnt); switch (vmt->vmt_gfstype) { case MNT_NFS: case MNT_NFS3: case MNT_NFS4: case MNT_RFS4: iter->mnt.device_path = t_strconcat(vmt_hostname, ":", vmt_object, NULL); iter->mnt.mount_path = vmt_stub; iter->mnt.type = MNTTYPE_NFS; break; case MNT_J2: case MNT_JFS: iter->mnt.device_path = vmt_object; iter->mnt.mount_path = vmt_stub; iter->mnt.type = MNTTYPE_JFS; break; default: /* unwanted filesystem */ return mountpoint_iter_next(iter); } if (stat(iter->mnt.mount_path, &vst) == 0) { iter->mnt.dev = vst.st_dev; iter->mnt.block_size = vst.st_blksize; } return &iter->mnt; #elif defined (MOUNTPOINT_SOLARIS) union { struct mnttab ent; struct extmnttab ext; } ent; if (iter->f == NULL) return NULL; i_zero(&iter->mnt); while ((getextmntent(iter->f, &ent.ext, sizeof(ent.ext))) == 0) { if (hasmntopt(&ent.ent, MNTOPT_IGNORE) != NULL) continue; /* mnt_type contains tmpfs with swap */ if (strcmp(ent.ent.mnt_special, MNTTYPE_SWAP) == 0) continue; iter->mnt.device_path = ent.ent.mnt_special; iter->mnt.mount_path = ent.ent.mnt_mountp; iter->mnt.type = ent.ent.mnt_fstype; iter->mnt.dev = makedev(ent.ext.mnt_major, ent.ext.mnt_minor); return &iter->mnt; } return NULL; #elif defined (MOUNTPOINT_LINUX) const struct mntent *ent; struct stat st; if (iter->f == NULL) return NULL; i_zero(&iter->mnt); while ((ent = getmntent(iter->f)) != NULL) { if (strcmp(ent->mnt_type, MNTTYPE_SWAP) == 0 || strcmp(ent->mnt_type, MNTTYPE_IGNORE) == 0 || strcmp(ent->mnt_type, MNTTYPE_ROOTFS) == 0) continue; iter->mnt.device_path = ent->mnt_fsname; iter->mnt.mount_path = ent->mnt_dir; iter->mnt.type = ent->mnt_type; if (stat(ent->mnt_dir, &st) == 0) { iter->mnt.dev = st.st_dev; iter->mnt.block_size = st.st_blksize; } return &iter->mnt; } return NULL; #elif defined(HAVE_GETMNTINFO) /* BSDs */ while (iter->count > 0) { #ifndef __NetBSD__ struct statfs *fs = iter->fs; #else struct statvfs *fs = iter->fs; #endif iter->fs++; iter->count--; iter->mnt.device_path = fs->f_mntfromname; iter->mnt.mount_path = fs->f_mntonname; #ifdef __osf__ /* Tru64 */ iter->mnt.type = getvfsbynumber(fs->f_type); #else iter->mnt.type = fs->f_fstypename; #endif iter->mnt.block_size = fs->f_bsize; return &iter->mnt; } return NULL; #else return NULL; #endif } int mountpoint_iter_deinit(struct mountpoint_iter **_iter) { struct mountpoint_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; #ifdef MOUNTPOINT_AIX_MNTCTL i_free(iter->mtab); #elif defined (MOUNTPOINT_SOLARIS) if (iter->f != NULL) fclose(iter->f); #elif defined (MOUNTPOINT_LINUX) if (iter->f != NULL) endmntent(iter->f); #endif i_free(iter); return ret; } dovecot-2.2.33.2/src/lib/primes.c0000644000175000017500000000134113123174404013323 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "primes.h" static const unsigned int primes[] = { #define PRIME_SKIP_COUNT 3 17, 37, 67, 131, 257, /* next from 2^8 */ 521, 1031, 2053, 4099, 8209, 16411, 32771, 65537, /* next from 2^16 */ 131101, 262147, 524309, 1048583, 2097169, 4194319, 8388617, 16777259, /* next from 2^24 */ 33554467, 67108879, 134217757, 268435459, 536870923, 1073741827, 2147483659U, 4294967291U /* previous from 2^32 */ }; unsigned int primes_closest(unsigned int num) { unsigned int i; for (i = 31; i > PRIME_SKIP_COUNT; i--) { if ((num & (1U << i)) != 0) return primes[i - PRIME_SKIP_COUNT]; } return primes[0]; } dovecot-2.2.33.2/src/lib/test-str.c0000644000175000017500000000566313123174404013624 00000000000000/* Copyright (c) 2012-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" static void test_str_append(void) { string_t *str = t_str_new(32); string_t *str2 = t_str_new(32); test_begin("str_append_*()"); str_append(str, "foo"); str_append_c(str, '|'); str_append_c(str, '\0'); test_assert(str->used == 5 && memcmp(str_data(str), "foo|\0", 5) == 0); str_append(str2, "sec"); str_append_c(str2, '\0'); str_append(str2, "ond"); str_append_str(str, str2); test_assert(str->used == 5+7 && memcmp(str_data(str), "foo|\0sec\0ond", 5+7) == 0); test_end(); } static void test_str_c(void) { string_t *str; unsigned int i, j; test_begin("str_c()"); str = t_str_new(0); T_BEGIN { (void)str_c(str); } T_END; for (i = 0; i < 32; i++) T_BEGIN { str = t_str_new(15); for (j = 0; j < i; j++) str_append_c(str, 'x'); T_BEGIN { (void)str_c(str); } T_END; } T_END; test_end(); } static void test_str_insert(void) { string_t *str = t_str_new(32); test_begin("str_insert()"); str_insert(str, 0, "foo"); str_insert(str, 3, ">"); str_insert(str, 3, "bar"); str_insert(str, 0, "<"); test_assert(str->used == 8 && memcmp(str_data(str), "", 8) == 0); str_insert(str, 10, "!"); test_assert(str->used == 11 && memcmp(str_data(str), "\0\0!", 11) == 0); test_end(); } static void test_str_delete(void) { string_t *str = t_str_new(32); test_begin("str_delete()"); str_delete(str, 0, 100); str_append(str, "123456"); str_delete(str, 0, 1); str_delete(str, 4, 1); str_delete(str, 1, 1); test_assert(str->used == 3 && memcmp(str_data(str), "245", 3) == 0); str_delete(str, 1, 2); test_assert(str->used == 1 && memcmp(str_data(str), "2", 1) == 0); str_append(str, "bar"); str_delete(str, 1, 100); test_assert(str->used == 1 && memcmp(str_data(str), "2", 1) == 0); test_end(); } static void test_str_append_n(void) { string_t *str = t_str_new(32); test_begin("str_append_n()"); str_append_n(str, "foo", 0); test_assert(str->used == 0); str_append_n(str, "\0foo", 4); test_assert(str->used == 0); str_append_n(str, "foo", 3); test_assert(str->used == 3 && memcmp(str_data(str), "foo", 3) == 0); str_truncate(str, 0); str_append_n(str, "foo", 2); test_assert(str->used == 2 && memcmp(str_data(str), "fo", 2) == 0); str_truncate(str, 0); str_append_n(str, "foo\0bar", 7); test_assert(str->used == 3 && memcmp(str_data(str), "foo", 3) == 0); str_truncate(str, 0); test_end(); } static void test_str_truncate(void) { string_t *str = t_str_new(8); int i; test_begin("str_truncate()"); str_append(str, "123456"); for (i = 100; i >= 6; i--) { str_truncate(str, i); test_assert_idx(str_len(str) == 6, i); } for (; i >= 0; i--) { str_truncate(str, i); test_assert_idx(str_len(str) == (unsigned int)i, i); } test_end(); } void test_str(void) { test_str_append(); test_str_c(); test_str_insert(); test_str_delete(); test_str_append_n(); test_str_truncate(); } dovecot-2.2.33.2/src/lib/test-priorityq.c0000644000175000017500000000532513165463624015064 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "priorityq.h" struct pq_test_item { struct priorityq_item item; int num; }; static int cmp_int(const void *p1, const void *p2) { const struct pq_test_item *i1 = p1, *i2 = p2; return i1->num - i2->num; } void test_priorityq(void) { #define PQ_MAX_ITEMS 100 static const int input[] = { 1, 2, 3, 4, 5, 6, 7, 8, -1, 8, 7, 6, 5, 4, 3, 2, 1, -1, 8, 7, 5, 6, 1, 3, 4, 2, -1, -1 }; static const int output[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; struct pq_test_item *item, items[PQ_MAX_ITEMS]; struct priorityq_item *const *all_items; unsigned int i, j; struct priorityq *pq; pool_t pool; int prev; pool = pool_alloconly_create("priorityq items", 1024); /* simple tests with popping only */ test_begin("priorityq"); for (i = 0; input[i] != -1; i++) { p_clear(pool); pq = priorityq_init(cmp_int, 1); for (j = 0; input[i] != -1; i++, j++) { test_assert(priorityq_count(pq) == j); item = p_new(pool, struct pq_test_item, 1); item->num = input[i]; priorityq_add(pq, &item->item); } all_items = priorityq_items(pq); test_assert(priorityq_count(pq) == N_ELEMENTS(output)); item = (struct pq_test_item *)all_items[0]; test_assert(item->num == output[0]); for (j = 1; j < N_ELEMENTS(output); j++) { item = (struct pq_test_item *)all_items[j]; test_assert(item->num > output[0]); test_assert(item->num <= output[N_ELEMENTS(output)-1]); } for (j = 0; j < N_ELEMENTS(output); j++) { test_assert(priorityq_count(pq) == N_ELEMENTS(output) - j); item = (struct pq_test_item *)priorityq_peek(pq); i_assert(item != NULL); test_assert(output[j] == item->num); item = (struct pq_test_item *)priorityq_pop(pq); i_assert(item != NULL); test_assert(output[j] == item->num); } test_assert(priorityq_count(pq) == 0); test_assert(priorityq_peek(pq) == NULL); test_assert(priorityq_pop(pq) == NULL); priorityq_deinit(&pq); } test_end(); /* randomized tests, remove elements */ test_begin("priorityq randomized"); for (i = 0; i < 100; i++) { pq = priorityq_init(cmp_int, 1); for (j = 0; j < PQ_MAX_ITEMS; j++) { items[j].num = rand(); priorityq_add(pq, &items[j].item); } for (j = 0; j < PQ_MAX_ITEMS; j++) { if (rand() % 3 == 0) { priorityq_remove(pq, &items[j].item); items[j].num = -1; } } prev = 0; while (priorityq_count(pq) > 0) { item = (struct pq_test_item *)priorityq_pop(pq); i_assert(item != NULL); test_assert(item->num >= 0 && prev <= item->num); prev = item->num; item->num = -1; } for (j = 0; j < PQ_MAX_ITEMS; j++) { test_assert(items[j].num == -1); } priorityq_deinit(&pq); } test_end(); pool_unref(&pool); } dovecot-2.2.33.2/src/lib/unlink-old-files.c0000644000175000017500000000326113123174404015203 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "unlink-old-files.h" #include #include #include #include #include static int unlink_old_files_real(const char *dir, const char *prefix, time_t min_time) { DIR *dirp; struct dirent *d; struct stat st; string_t *path; size_t prefix_len, dir_len; dirp = opendir(dir); if (dirp == NULL) { if (errno != ENOENT) i_error("opendir(%s) failed: %m", dir); return -1; } /* update atime immediately, so if this scanning is done based on atime it won't be done by multiple processes if the scan is slow */ if (utime(dir, NULL) < 0 && errno != ENOENT) i_error("utime(%s) failed: %m", dir); path = t_str_new(256); str_printfa(path, "%s/", dir); dir_len = str_len(path); prefix_len = strlen(prefix); while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { /* skip . and .. */ continue; } if (strncmp(d->d_name, prefix, prefix_len) != 0) continue; str_truncate(path, dir_len); str_append(path, d->d_name); if (stat(str_c(path), &st) < 0) { if (errno != ENOENT) i_error("stat(%s) failed: %m", str_c(path)); } else if (!S_ISDIR(st.st_mode) && st.st_ctime < min_time) { i_unlink_if_exists(str_c(path)); } } if (closedir(dirp) < 0) i_error("closedir(%s) failed: %m", dir); return 0; } int unlink_old_files(const char *dir, const char *prefix, time_t min_time) { int ret; T_BEGIN { ret = unlink_old_files_real(dir, prefix, min_time); } T_END; return ret; } dovecot-2.2.33.2/src/lib/wildcard-match.h0000644000175000017500000000052013123174404014712 00000000000000#ifndef WILDCARD_MATCH_H #define WILDCARD_MATCH_H /* Returns TRUE if mask matches data. mask can contain '*' and '?' wildcards. */ bool wildcard_match(const char *data, const char *mask); /* Like wildcard_match(), but match ASCII characters case-insensitively. */ bool wildcard_match_icase(const char *data, const char *mask); #endif dovecot-2.2.33.2/src/lib/seq-range-array.c0000644000175000017500000003105113123174404015023 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "seq-range-array.h" static bool ATTR_NOWARN_UNUSED_RESULT seq_range_lookup(const ARRAY_TYPE(seq_range) *array, uint32_t seq, unsigned int *idx_r) { const struct seq_range *data; unsigned int idx, left_idx, right_idx, count; data = array_get(array, &count); i_assert(count < INT_MAX); idx = 0; left_idx = 0; right_idx = count; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (data[idx].seq1 <= seq) { if (data[idx].seq2 >= seq) { /* it's already in the range */ *idx_r = idx; return TRUE; } left_idx = idx+1; } else { right_idx = idx; } } if (left_idx > idx) idx++; *idx_r = idx; return FALSE; } bool seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq) { struct seq_range *data, value; unsigned int idx, count; value.seq1 = value.seq2 = seq; data = array_get_modifiable(array, &count); if (count == 0) { array_append(array, &value, 1); return FALSE; } /* quick checks */ if (data[count-1].seq2 < seq) { if (data[count-1].seq2 == seq-1) { /* grow last range */ data[count-1].seq2 = seq; } else { array_append(array, &value, 1); } return FALSE; } if (data[0].seq1 > seq) { if (data[0].seq1-1 == seq) { /* grow down first range */ data[0].seq1 = seq; } else { array_insert(array, 0, &value, 1); } return FALSE; } /* somewhere in the middle, array is sorted so find it with binary search */ if (seq_range_lookup(array, seq, &idx)) return TRUE; /* idx == count couldn't happen because we already handle it above */ i_assert(idx < count && data[idx].seq1 >= seq); i_assert(data[idx].seq1 > seq || data[idx].seq2 < seq); if (data[idx].seq1 == seq+1) { data[idx].seq1 = seq; if (idx > 0 && data[idx-1].seq2 == seq-1) { /* merge */ data[idx-1].seq2 = data[idx].seq2; array_delete(array, idx, 1); } } else { if (idx > 0 && data[idx-1].seq2 == seq-1) idx--; if (data[idx].seq2 == seq-1) { i_assert(idx+1 < count); /* already handled above */ data[idx].seq2 = seq; if (data[idx+1].seq1 == seq+1) { /* merge */ data[idx+1].seq1 = data[idx].seq1; array_delete(array, idx, 1); } } else { array_insert(array, idx, &value, 1); } } return FALSE; } void seq_range_array_add_with_init(ARRAY_TYPE(seq_range) *array, unsigned int init_count, uint32_t seq) { if (!array_is_created(array)) i_array_init(array, init_count); seq_range_array_add(array, seq); } static void seq_range_array_add_range_internal(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2, unsigned int *r_count) { struct seq_range *data, value; unsigned int idx1, idx2, count; seq_range_lookup(array, seq1, &idx1); seq_range_lookup(array, seq2, &idx2); data = array_get_modifiable(array, &count); if (r_count != NULL) { /* Find number we're adding by counting the number we're not adding, and subtracting that from the nominal range. */ unsigned int added = seq2+1 - seq1; unsigned int countidx2 = idx2; unsigned int overcounted = 0u, notadded = 0u; unsigned int i; if (idx1 == count) { /* not in a range as too far right */ } else if (seq1 < data[idx1].seq1) { /* not in a range, to the left of a real range */ } else { /* count the whole of this range, which is an overcount */ overcounted += seq1 - data[idx1].seq1; /* fencepost check: equality means the whole range is valid, therefore there's no overcounting. Result = 0 overcount */ } if (idx2 == count) { /* not in a range as too far right */ } else if (seq2 < data[idx2].seq1) { /* not in a range, to the left of a real range */ } else { /* count the whole of this range, which is an overcount */ overcounted += data[idx2].seq2 - seq2; countidx2++; /* may become == count i.e. past the end */ /* fencepost check: equality means the whole range is valid, therefore there's no overcounting. Result = 0 overcount. */ } /* Now count how many we're not adding */ for (i = idx1; i < countidx2; i++) notadded += data[i].seq2+1 - data[i].seq1; /* Maybe the not added tally included some over-counting too */ added -= (notadded - overcounted); *r_count = added; } if (idx1 > 0 && data[idx1-1].seq2+1 == seq1) idx1--; if (idx1 == idx2 && (idx2 == count || (seq2 < (uint32_t)-1 && data[idx2].seq1 > seq2+1)) && (idx1 == 0 || data[idx1-1].seq2 < seq1-1)) { /* no overlapping */ value.seq1 = seq1; value.seq2 = seq2; array_insert(array, idx1, &value, 1); } else { i_assert(idx1 < count); if (seq1 < data[idx1].seq1) data[idx1].seq1 = seq1; if (seq2 > data[idx1].seq2) { /* merge */ if (idx2 == count || data[idx2].seq1 > seq2+1) idx2--; if (seq2 >= data[idx2].seq2) { data[idx1].seq2 = seq2; } else { data[idx1].seq2 = data[idx2].seq2; } array_delete(array, idx1 + 1, idx2 - idx1); } } } void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { seq_range_array_add_range_internal(array, seq1, seq2, NULL); } unsigned int seq_range_array_add_range_count(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { unsigned int count; seq_range_array_add_range_internal(array, seq1, seq2, &count); return count; } void seq_range_array_merge(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { const struct seq_range *range; if (array_count(dest) == 0) { array_append_array(dest, src); return; } array_foreach(src, range) seq_range_array_add_range(dest, range->seq1, range->seq2); } bool seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq) { struct seq_range *data, value; unsigned int idx, left_idx, right_idx, count; if (!array_is_created(array)) return FALSE; data = array_get_modifiable(array, &count); if (count == 0) return FALSE; /* quick checks */ if (seq > data[count-1].seq2 || seq < data[0].seq1) { /* outside the range */ return FALSE; } if (data[count-1].seq2 == seq) { /* shrink last range */ if (data[count-1].seq1 != data[count-1].seq2) data[count-1].seq2--; else array_delete(array, count-1, 1); return TRUE; } if (data[0].seq1 == seq) { /* shrink up first range */ if (data[0].seq1 != data[0].seq2) data[0].seq1++; else array_delete(array, 0, 1); return TRUE; } /* somewhere in the middle, array is sorted so find it with binary search */ i_assert(count < INT_MAX); left_idx = 0; right_idx = count; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (data[idx].seq1 > seq) right_idx = idx; else if (data[idx].seq2 < seq) left_idx = idx+1; else { /* found it */ if (data[idx].seq1 == seq) { if (data[idx].seq1 == data[idx].seq2) { /* a single sequence range. remove it entirely */ array_delete(array, idx, 1); } else { /* shrink the range */ data[idx].seq1++; } } else if (data[idx].seq2 == seq) { /* shrink the range */ data[idx].seq2--; } else { /* split the sequence range */ value.seq1 = seq + 1; value.seq2 = data[idx].seq2; data[idx].seq2 = seq - 1; array_insert(array, idx + 1, &value, 1); } return TRUE; } } return FALSE; } unsigned int seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { const struct seq_range *data; unsigned int idx, idx2, count, remove_count = 0; /* remove first and last. this makes sure that everything between can simply be deleted with array_delete(). FIXME: it would be faster if we did only one binary lookup here and handled the splitting ourself.. */ if (seq_range_array_remove(array, seq1)) remove_count++; if (seq1 == seq2) return remove_count; seq1++; if (seq_range_array_remove(array, seq2--)) remove_count++; if (seq1 > seq2) return remove_count; /* find the beginning */ data = array_get(array, &count); seq_range_lookup(array, seq1, &idx); if (idx == count) return remove_count; i_assert(data[idx].seq1 >= seq1); for (idx2 = idx; idx2 < count; idx2++) { if (data[idx2].seq1 > seq2) break; remove_count += data[idx2].seq2 - data[idx2].seq1 + 1; } array_delete(array, idx, idx2-idx); return remove_count; } unsigned int seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { unsigned int ret = 0; const struct seq_range *src_range; array_foreach(src, src_range) { ret += seq_range_array_remove_range(dest, src_range->seq1, src_range->seq2); } return ret; } void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array, uint32_t n, uint32_t count) { struct seq_range_iter iter; uint32_t seq1, seq2; if (count == 0) return; seq_range_array_iter_init(&iter, array); if (!seq_range_array_iter_nth(&iter, n, &seq1)) { /* n points beyond array */ return; } if (count-1 >= (uint32_t)-1 - n || !seq_range_array_iter_nth(&iter, n + (count-1), &seq2)) { /* count points beyond array */ seq2 = (uint32_t)-1; } seq_range_array_remove_range(array, seq1, seq2); } unsigned int seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { const struct seq_range *src_range; unsigned int i, count, ret = 0; uint32_t last_seq = 0; src_range = array_get(src, &count); for (i = 0; i < count; i++) { if (last_seq + 1 < src_range[i].seq1) { ret += seq_range_array_remove_range(dest, last_seq + 1, src_range[i].seq1 - 1); } last_seq = src_range[i].seq2; } if (last_seq != (uint32_t)-1) { ret += seq_range_array_remove_range(dest, last_seq + 1, (uint32_t)-1); } return ret; } bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq) { unsigned int idx; return seq_range_lookup(array, seq, &idx); } bool seq_range_array_have_common(const ARRAY_TYPE(seq_range) *array1, const ARRAY_TYPE(seq_range) *array2) { const struct seq_range *range1, *range2; unsigned int i1, i2, count1, count2; range1 = array_get(array1, &count1); range2 = array_get(array2, &count2); for (i1 = i2 = 0; i1 < count1 && i2 < count2; ) { if (range1[i1].seq1 <= range2[i2].seq2 && range1[i1].seq2 >= range2[i2].seq1) return TRUE; if (range1[i1].seq1 < range2[i2].seq1) i1++; else i2++; } return FALSE; } unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array) { const struct seq_range *range; unsigned int seq_count = 0; array_foreach(array, range) seq_count += range->seq2 - range->seq1 + 1; return seq_count; } void seq_range_array_invert(ARRAY_TYPE(seq_range) *array, uint32_t min_seq, uint32_t max_seq) { struct seq_range *range, value; unsigned int i, count; uint32_t prev_min_seq; if (array_is_created(array)) range = array_get_modifiable(array, &count); else { range = NULL; count = 0; } if (count == 0) { /* empty -> full */ if (!array_is_created(array)) i_array_init(array, 4); value.seq1 = min_seq; value.seq2 = max_seq; array_append(array, &value, 1); return; } i_assert(range[0].seq1 >= min_seq); i_assert(range[count-1].seq2 <= max_seq); if (range[0].seq1 == min_seq && range[0].seq2 == max_seq) { /* full -> empty */ array_clear(array); return; } for (i = 0; i < count; ) { prev_min_seq = min_seq; min_seq = range[i].seq2; if (range[i].seq1 == prev_min_seq) { array_delete(array, i, 1); range = array_get_modifiable(array, &count); } else { range[i].seq2 = range[i].seq1 - 1; range[i].seq1 = prev_min_seq; i++; } if (min_seq >= max_seq) { /* max_seq is reached. the rest of the array should be empty. we'll return here, because min_seq++ may wrap to 0. */ i_assert(min_seq == max_seq); i_assert(i == count); return; } min_seq++; } if (min_seq <= max_seq) { value.seq1 = min_seq; value.seq2 = max_seq; array_append(array, &value, 1); } } void seq_range_array_iter_init(struct seq_range_iter *iter_r, const ARRAY_TYPE(seq_range) *array) { i_zero(iter_r); iter_r->array = array; } bool seq_range_array_iter_nth(struct seq_range_iter *iter, unsigned int n, uint32_t *seq_r) { const struct seq_range *range; unsigned int i, count, diff; if (n < iter->prev_n) { /* iterating backwards, don't bother optimizing */ iter->prev_n = 0; iter->prev_idx = 0; } range = array_get(iter->array, &count); for (i = iter->prev_idx; i < count; i++) { diff = range[i].seq2 - range[i].seq1; if (n <= iter->prev_n + diff) { i_assert(n >= iter->prev_n); *seq_r = range[i].seq1 + (n - iter->prev_n); iter->prev_idx = i; return TRUE; } iter->prev_n += diff + 1; } iter->prev_idx = i; return FALSE; } dovecot-2.2.33.2/src/lib/seq-range-array.h0000644000175000017500000000574513123174404015043 00000000000000#ifndef SEQ_RANGE_ARRAY_H #define SEQ_RANGE_ARRAY_H struct seq_range { uint32_t seq1, seq2; }; ARRAY_DEFINE_TYPE(seq_range, struct seq_range); struct seq_range_iter { const ARRAY_TYPE(seq_range) *array; unsigned int prev_n, prev_idx; }; static inline uint32_t ATTR_PURE seq_range_length(struct seq_range *range) { i_assert(range->seq2 >= range->seq1); return range->seq2 - range->seq1 + 1; } /* Add sequence to range. If the array isn't created yet, create it with initial size of init_count. */ bool ATTR_NOWARN_UNUSED_RESULT seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq); /* Like seq_range_array_add(), but if the array isn't already initialized do it with i_array_init(). */ void seq_range_array_add_with_init(ARRAY_TYPE(seq_range) *array, unsigned int init_count, uint32_t seq); void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2); unsigned int seq_range_array_add_range_count(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2); void seq_range_array_merge(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src); /* Remove the given sequrence from range. Returns TRUE if it was found. */ bool ATTR_NOWARN_UNUSED_RESULT seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq); /* Remove a sequence range. Returns number of sequences actually removed. */ unsigned int ATTR_NOWARN_UNUSED_RESULT seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2); unsigned int ATTR_NOWARN_UNUSED_RESULT seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src); /* Remove count number of sequences from the nth sequence (0 = first). */ void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array, uint32_t n, uint32_t count); /* Remove sequences from dest that don't exist in src. */ unsigned int ATTR_NOWARN_UNUSED_RESULT seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src); /* Returns TRUE if sequence exists in the range. */ bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq) ATTR_PURE; /* Returns TRUE if arrays have common sequences. */ bool seq_range_array_have_common(const ARRAY_TYPE(seq_range) *array1, const ARRAY_TYPE(seq_range) *array2) ATTR_PURE; /* Return number of sequences in the range. */ unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array) ATTR_PURE; /* Invert the sequence range. For example 5:6 -> min_seq:4,7:max_seq. The array must not have any sequences outside min_seq..max_seq or this function will assert-crash. */ void seq_range_array_invert(ARRAY_TYPE(seq_range) *array, uint32_t min_seq, uint32_t max_seq); void seq_range_array_iter_init(struct seq_range_iter *iter_r, const ARRAY_TYPE(seq_range) *array); /* Get the nth sequence (0 = first). Returns FALSE if idx is too large. */ bool seq_range_array_iter_nth(struct seq_range_iter *iter, unsigned int n, uint32_t *seq_r); #endif dovecot-2.2.33.2/src/lib/str-table.c0000644000175000017500000000316413123174404013726 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "str-table.h" struct str_table { HASH_TABLE(char *, void *) hash; }; struct str_table *str_table_init(void) { struct str_table *table; table = i_new(struct str_table, 1); hash_table_create(&table->hash, default_pool, 0, str_hash, strcmp); return table; } void str_table_deinit(struct str_table **_table) { struct str_table *table = *_table; struct hash_iterate_context *iter; char *key; void *value; *_table = NULL; iter = hash_table_iterate_init(table->hash); while (hash_table_iterate(iter, table->hash, &key, &value)) i_free(key); hash_table_iterate_deinit(&iter); hash_table_destroy(&table->hash); i_free(table); } bool str_table_is_empty(struct str_table *table) { return hash_table_count(table->hash) == 0; } const char *str_table_ref(struct str_table *table, const char *str) { char *key; void *value; unsigned int ref; if (!hash_table_lookup_full(table->hash, str, &key, &value)) { key = i_strdup(str); ref = 1; } else { ref = POINTER_CAST_TO(value, unsigned int); i_assert(ref > 0); ref++; } hash_table_update(table->hash, key, POINTER_CAST(ref)); return key; } void str_table_unref(struct str_table *table, const char **str) { char *key; void *value; unsigned int ref; if (!hash_table_lookup_full(table->hash, *str, &key, &value)) i_unreached(); ref = POINTER_CAST_TO(value, unsigned int); i_assert(ref > 0); if (--ref > 0) hash_table_update(table->hash, key, POINTER_CAST(ref)); else { hash_table_remove(table->hash, key); i_free(key); } *str = NULL; } dovecot-2.2.33.2/src/lib/safe-mkstemp.c0000644000175000017500000000510413165463624014434 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "hex-binary.h" #include "randgen.h" #include "hostpid.h" #include "eacces-error.h" #include "safe-mkstemp.h" #include #include #include static int ATTR_NULL(5) safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin) { size_t prefix_len; struct stat st; unsigned char randbuf[8]; mode_t old_umask; int fd; prefix_len = str_len(prefix); for (;;) { do { random_fill_weak(randbuf, sizeof(randbuf)); str_truncate(prefix, prefix_len); str_append(prefix, binary_to_hex(randbuf, sizeof(randbuf))); } while (lstat(str_c(prefix), &st) == 0); if (errno != ENOENT) { i_error("stat(%s) failed: %m", str_c(prefix)); str_truncate(prefix, prefix_len); return -1; } old_umask = umask(0666 ^ mode); fd = open(str_c(prefix), O_RDWR | O_EXCL | O_CREAT, 0666); umask(old_umask); if (fd != -1) break; if (errno != EEXIST) { if (errno != ENOENT && errno != EACCES) i_error("open(%s) failed: %m", str_c(prefix)); str_truncate(prefix, prefix_len); return -1; } } if (uid == (uid_t)-1 && gid == (gid_t)-1) return fd; if (fchown(fd, uid, gid) < 0) { if (errno == EPERM) { i_error("%s", eperm_error_get_chgrp("fchown", str_c(prefix), gid, gid_origin)); } else { i_error("fchown(%s, %ld, %ld) failed: %m", str_c(prefix), uid == (uid_t)-1 ? -1L : (long)uid, gid == (gid_t)-1 ? -1L : (long)gid); } i_close_fd(&fd); i_unlink(str_c(prefix)); str_truncate(prefix, prefix_len); return -1; } return fd; } int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid) { return safe_mkstemp_full(prefix, mode, uid, gid, NULL); } int safe_mkstemp_group(string_t *prefix, mode_t mode, gid_t gid, const char *gid_origin) { return safe_mkstemp_full(prefix, mode, (uid_t)-1, gid, gid_origin); } int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid) { size_t orig_prefix_len = str_len(prefix); int fd; str_printfa(prefix, "%s.%s.", my_hostname, my_pid); if ((fd = safe_mkstemp(prefix, mode, uid, gid)) == -1) str_truncate(prefix, orig_prefix_len); return fd; } int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode, gid_t gid, const char *gid_origin) { size_t orig_prefix_len = str_len(prefix); int fd; str_printfa(prefix, "%s.%s.", my_hostname, my_pid); if ((fd = safe_mkstemp_group(prefix, mode, gid, gid_origin)) == -1) str_truncate(prefix, orig_prefix_len); return fd; } dovecot-2.2.33.2/src/lib/ostream-private.h0000644000175000017500000000347413165463624015177 00000000000000#ifndef OSTREAM_PRIVATE_H #define OSTREAM_PRIVATE_H #include "ostream.h" #include "iostream-private.h" struct ostream_private { /* inheritance: */ struct iostream_private iostream; /* methods: */ void (*cork)(struct ostream_private *stream, bool set); int (*flush)(struct ostream_private *stream); void (*set_flush_callback)(struct ostream_private *stream, stream_flush_callback_t *callback, void *context); void (*flush_pending)(struct ostream_private *stream, bool set); size_t (*get_used_size)(const struct ostream_private *stream); int (*seek)(struct ostream_private *stream, uoff_t offset); ssize_t (*sendv)(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count); int (*write_at)(struct ostream_private *stream, const void *data, size_t size, uoff_t offset); off_t (*send_istream)(struct ostream_private *outstream, struct istream *instream); void (*switch_ioloop)(struct ostream_private *stream); /* data: */ struct ostream ostream; size_t max_buffer_size; struct ostream *parent; /* for filter streams */ int fd; stream_flush_callback_t *callback; void *context; unsigned int corked:1; unsigned int closing:1; unsigned int last_errors_not_checked:1; unsigned int error_handling_disabled:1; }; struct ostream * o_stream_create(struct ostream_private *_stream, struct ostream *parent, int fd) ATTR_NULL(2); off_t io_stream_copy(struct ostream *outstream, struct istream *instream); void o_stream_copy_error_from_parent(struct ostream_private *_stream); /* This should be called before sending data to parent stream. It makes sure that the parent stream's output buffer doesn't become too large. Returns 1 if more data can be safely added, 0 if not, -1 if error. */ int o_stream_flush_parent_if_needed(struct ostream_private *_stream); #endif dovecot-2.2.33.2/src/lib/home-expand.h0000644000175000017500000000070113123174404014235 00000000000000#ifndef HOME_EXPAND_H #define HOME_EXPAND_H /* expand ~/ or ~user/ in beginning of path. If user is unknown, the original path is returned without modification. */ const char *home_expand(const char *path); /* Returns 0 if ok, -1 if user wasn't found. */ int home_try_expand(const char **path); /* Expand ~/ in the beginning of the path with the give home directory. */ const char *home_expand_tilde(const char *path, const char *home); #endif dovecot-2.2.33.2/src/lib/test-data-stack.c0000644000175000017500000001463213165463624015037 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "data-stack.h" static void test_ds_buffers(void) { test_begin("data-stack buffer growth"); T_BEGIN { size_t i; unsigned char *p; size_t left = t_get_bytes_available(); while (left < 10000) { t_malloc(left); /* force a new block */ left = t_get_bytes_available(); } left -= 64; /* make room for the sentry if DEBUG */ p = t_buffer_get(1); p[0] = 1; for (i = 2; i <= left; i++) { /* grow it */ unsigned char *p2 = t_buffer_get(i); test_assert_idx(p == p2, i); p[i-1] = i; test_assert_idx(p[i-2] == (unsigned char)(i-1), i); } /* now fix it permanently */ t_buffer_alloc_last_full(); test_assert(t_get_bytes_available() < 64 + MEM_ALIGN(1)); } T_END; test_end(); test_begin("data-stack buffer interruption"); T_BEGIN { void *b = t_buffer_get(1000); void *a = t_malloc(1); void *b2 = t_buffer_get(1001); test_assert(a == b); /* expected, not guaranteed */ test_assert(b2 != b); } T_END; test_end(); test_begin("data-stack buffer with reallocs"); T_BEGIN { size_t bigleft = t_get_bytes_available(); size_t i; for (i = 1; i < bigleft-64; i += rand()%32) T_BEGIN { unsigned char *p, *p2; size_t left; t_malloc(i); left = t_get_bytes_available(); /* The most useful idx for the assert is 'left' */ test_assert_idx(left <= bigleft-i, left); p = t_buffer_get(left/2); p[0] = 'Z'; p[left/2 - 1] = 'Z'; p2 = t_buffer_get(left + left/2); test_assert_idx(p != p2, left); test_assert_idx(p[0] == 'Z', left); test_assert_idx(p[left/2 -1] == 'Z', left); } T_END; } T_END; test_end(); } static void test_ds_realloc() { test_begin("data-stack realloc"); T_BEGIN { size_t i; unsigned char *p; size_t left = t_get_bytes_available(); while (left < 10000) { t_malloc(left); /* force a new block */ left = t_get_bytes_available(); } left -= 64; /* make room for the sentry if DEBUG */ p = t_malloc(1); p[0] = 1; for (i = 2; i <= left; i++) { /* grow it */ test_assert_idx(t_try_realloc(p, i), i); p[i-1] = i; test_assert_idx(p[i-2] == (unsigned char)(i-1), i); } test_assert(t_get_bytes_available() < 64 + MEM_ALIGN(1)); } T_END; test_end(); } static void test_ds_recurse(int depth, int number, size_t size) { int i; char **ps; char tag[2] = { depth+1, '\0' }; int try_fails = 0; unsigned int t_id = t_push_named("test_ds_recurse[%i]", depth); ps = t_buffer_get(sizeof(char *) * number); i_assert(ps != NULL); t_buffer_alloc(sizeof(char *) * number); for (i = 0; i < number; i++) { ps[i] = t_malloc(size/2); bool re = t_try_realloc(ps[i], size); i_assert(ps[i] != NULL); if (!re) { try_fails++; ps[i] = t_malloc(size); } /* drop our own canaries */ memset(ps[i], tag[0], size); ps[i][size-2] = 0; } /* Do not expect a high failure rate from t_try_realloc */ test_assert_idx(try_fails <= number / 20, depth); /* Now recurse... */ if(depth>0) test_ds_recurse(depth-1, number, size); /* Test our canaries are still intact */ for (i = 0; i < number; i++) { test_assert_idx(strspn(ps[i], tag) == size - 2, i); test_assert_idx(ps[i][size-1] == tag[0], i); } test_assert_idx(t_id == t_pop(), depth); } static void test_ds_recursive(int count, int depth) { int i; test_begin("data-stack recursive"); for(i = 0; i < count; i++) T_BEGIN { int number=rand()%100+50; int size=rand()%100+50; test_ds_recurse(depth, number, size); } T_END; test_end(); } void test_data_stack(void) { test_ds_buffers(); test_ds_realloc(); test_ds_recursive(20, 80); } enum fatal_test_state fatal_data_stack(unsigned int stage) { #ifdef DEBUG /* If we abort, then we'll be left with a dangling t_push() keep a record of our temporary stack id, so we can clean up. */ static unsigned int t_id = 999999999; static unsigned char *undo_ptr = NULL; static unsigned char undo_data; static bool things_are_messed_up = FALSE; if (stage != 0) { /* Presume that we need to clean up from the prior test: undo the evil write, then we will be able to t_pop cleanly, and finally we can end the test stanza. */ if (things_are_messed_up || undo_ptr == NULL) return FATAL_TEST_ABORT; /* abort, things are messed up with t_pop */ *undo_ptr = undo_data; undo_ptr = NULL; /* t_pop musn't abort, that would cause recursion */ things_are_messed_up = TRUE; if (t_id != 999999999 && t_pop() != t_id) return FATAL_TEST_ABORT; /* abort, things are messed up with us */ things_are_messed_up = FALSE; t_id = 999999999; test_end(); } switch(stage) { case 0: { unsigned char *p; test_begin("fatal data-stack underrun"); t_id = t_push_named("fatal_data_stack underrun"); size_t left = t_get_bytes_available(); p = t_malloc(left-80); /* will fit */ p = t_malloc(100); /* won't fit, will get new block */ int seek = 0; /* Seek back for the canary, don't assume endianness */ while(seek > -60 && ((p[seek+1] != 0xDB) || ((p[seek] != 0xBA || p[seek+2] != 0xAD) && (p[seek+2] != 0xBA || p[seek] != 0xAD)))) seek--; if (seek <= -60) return FATAL_TEST_ABORT; /* abort, couldn't find header */ undo_ptr = p + seek; undo_data = *undo_ptr; *undo_ptr = '*'; /* t_malloc will panic block header corruption */ (void)t_malloc(10); return FATAL_TEST_FAILURE; } case 1: case 2: { test_begin(stage == 1 ? "fatal t_malloc overrun near" : "fatal t_malloc overrun far"); t_id = t_push_named(stage == 1 ? "fatal t_malloc overrun first" : "fatal t_malloc overrun far"); unsigned char *p = t_malloc(10); undo_ptr = p + 10 + (stage == 1 ? 0 : 8*4-1); /* presumes sentry size */ undo_data = *undo_ptr; *undo_ptr = '*'; /* t_pop will now fail */ (void)t_pop(); t_id = 999999999; /* We're FUBAR, mustn't pop next entry */ return FATAL_TEST_FAILURE; } case 3: case 4: { test_begin(stage == 3 ? "fatal t_buffer_get overrun near" : "fatal t_buffer_get overrun far"); t_id = t_push_named(stage == 3 ? "fatal t_buffer overrun near" : "fatal t_buffer_get overrun far"); unsigned char *p = t_buffer_get(10); undo_ptr = p + 10 + (stage == 3 ? 0 : 8*4-1); undo_data = *undo_ptr; *undo_ptr = '*'; /* t_pop will now fail */ (void)t_pop(); t_id = 999999999; /* We're FUBAR, mustn't pop next entry */ return FATAL_TEST_FAILURE; } default: things_are_messed_up = TRUE; return FATAL_TEST_FINISHED; } #else return stage == 0 ? FATAL_TEST_FINISHED : FATAL_TEST_ABORT; #endif } dovecot-2.2.33.2/src/lib/test-ostream-escaped.c0000644000175000017500000000524513167162046016073 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "ostream.h" #include "ostream-escaped.h" #include "json-parser.h" static void test_ostream_escaped_json(void) { struct ostream *os_sink; struct ostream *os_encode; struct const_iovec iov[2]; string_t *str = t_str_new(64); test_begin("test_ostream_escaped_json()"); os_sink = o_stream_create_buffer(str); os_encode = o_stream_create_escaped(os_sink, ostream_escaped_json_format); /* test sending iovec */ iov[0].iov_base = "hello"; iov[0].iov_len = 5; iov[1].iov_base = ", world"; iov[1].iov_len = 7; test_assert(o_stream_sendv(os_encode, iov, 2) == 12); test_assert(os_encode->offset == 12); test_assert(os_sink->offset == 12); test_assert(strcmp(str_c(str), "hello, world") == 0); /* reset buffer */ str_truncate(str, 0); os_sink->offset = 0; os_encode->offset = 0; /* test shrinking ostream-escaped's max buffer size */ o_stream_set_max_buffer_size(os_encode, 10); o_stream_set_max_buffer_size(os_sink, 100); test_assert(o_stream_send(os_encode, "\x15\x00!\x00\x15\x11" "123456", 12) == 12); test_assert(os_encode->offset == 12); test_assert(os_sink->offset == 2*6 + 1 + 3*6 + 6); test_assert(strcmp(str_c(str), "\\u0015\\u0000!\\u0000\\u0015\\u0011123456") == 0); /* reset buffer */ str_truncate(str, 0); os_sink->offset = 0; os_encode->offset = 0; /* test shrinking sink's max buffer size */ o_stream_set_max_buffer_size(os_encode, 100); o_stream_set_max_buffer_size(os_sink, 10); const char *partial_input = "\x15!\x01?#&"; ssize_t ret = o_stream_send_str(os_encode, partial_input); test_assert(ret < 6); /* send the rest */ o_stream_set_max_buffer_size(os_sink, 100); ret += o_stream_send_str(os_encode, partial_input + ret); test_assert(ret == (ssize_t)strlen(partial_input)); test_assert((ssize_t)os_encode->offset == ret); test_assert(os_sink->offset == str_len(str)); test_assert(strcmp(str_c(str), "\\u0015!\\u0001?#&") == 0); o_stream_unref(&os_encode); o_stream_unref(&os_sink); test_end(); } static void test_ostream_escaped_hex(void) { struct ostream *os_sink; struct ostream *os_encode; string_t *str = t_str_new(64); os_sink = o_stream_create_buffer(str); os_encode = o_stream_create_escaped(os_sink, ostream_escaped_hex_format); test_begin("test_ostream_escaped_hex()"); test_assert(o_stream_send_str(os_encode, "hello, world") == 12); test_assert(o_stream_flush(os_encode) == 1); test_assert(strcmp(str_c(str), "68656c6c6f2c20776f726c64") == 0); o_stream_unref(&os_encode); o_stream_unref(&os_sink); test_end(); } void test_ostream_escaped(void) { test_ostream_escaped_json(); test_ostream_escaped_hex(); } dovecot-2.2.33.2/src/lib/istream-multiplex.c0000644000175000017500000001751313167162046015530 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "istream-private.h" #include "istream-multiplex.h" /* all multiplex packets are [1 byte cid][4 byte length][data] */ struct multiplex_istream; struct multiplex_ichannel { struct istream_private istream; struct multiplex_istream *mstream; uint8_t cid; size_t pending_pos; bool closed:1; }; struct multiplex_istream { struct istream *parent; /* channel 0 is main channel */ uint8_t cur_channel; unsigned int remain; size_t bufsize; ARRAY(struct multiplex_ichannel *) channels; bool blocking:1; }; static ssize_t i_stream_multiplex_ichannel_read(struct istream_private *stream); static struct multiplex_ichannel * get_channel(struct multiplex_istream *mstream, uint8_t cid) { struct multiplex_ichannel **channelp; i_assert(mstream != NULL); array_foreach_modifiable(&mstream->channels, channelp) { if (*channelp != NULL && (*channelp)->cid == cid) return *channelp; } return NULL; } static void propagate_error(struct multiplex_istream *mstream, int stream_errno) { struct multiplex_ichannel **channelp; array_foreach_modifiable(&mstream->channels, channelp) if (*channelp != NULL) (*channelp)->istream.istream.stream_errno = stream_errno; } static void propagate_eof(struct multiplex_istream *mstream) { struct multiplex_ichannel **channelp; array_foreach_modifiable(&mstream->channels, channelp) { if (*channelp != NULL) { (*channelp)->istream.istream.eof = TRUE; } } } static ssize_t i_stream_multiplex_read(struct multiplex_istream *mstream, struct multiplex_ichannel *req_channel) { const unsigned char *data; size_t len = 0, used, wanted, avail; ssize_t ret, got = 0; if (mstream->parent == NULL) { req_channel->istream.istream.eof = TRUE; return -1; } data = i_stream_get_data(mstream->parent, &len); if (len == 0 && mstream->parent->closed) { req_channel->istream.istream.eof = TRUE; return -1; } if (((mstream->remain > 0 && len == 0) || (mstream->remain == 0 && len < 5)) && (ret = i_stream_read(mstream->parent)) <= 0) { propagate_error(mstream, mstream->parent->stream_errno); if (mstream->parent->eof) propagate_eof(mstream); return ret; } for(;;) { data = i_stream_get_data(mstream->parent, &len); if (len == 0) { if (got == 0 && mstream->blocking) { /* can't return 0 with blocking istreams, so try again from the beginning. */ return i_stream_multiplex_read(mstream, req_channel); } break; } if (mstream->remain > 0) { struct multiplex_ichannel *channel = get_channel(mstream, mstream->cur_channel); wanted = I_MIN(len, mstream->remain); /* is it open? */ if (channel != NULL && !channel->closed) { struct istream_private *stream = &channel->istream; stream->pos += channel->pending_pos; bool alloc_ret = i_stream_try_alloc(stream, wanted, &avail); stream->pos -= channel->pending_pos; if (!alloc_ret) { i_stream_set_input_pending(&stream->istream, TRUE); if (channel->cid != req_channel->cid) return 0; if (got > 0) break; return -2; } used = I_MIN(wanted, avail); /* dump into buffer */ if (channel->cid != req_channel->cid) { i_assert(stream->pos + channel->pending_pos + used <= stream->buffer_size); memcpy(stream->w_buffer + stream->pos + channel->pending_pos, data, used); channel->pending_pos += used; i_stream_set_input_pending(&stream->istream, TRUE); } else { i_assert(stream->pos + used <= stream->buffer_size); memcpy(stream->w_buffer + stream->pos, data, used); stream->pos += used; got += used; } } else { used = wanted; } mstream->remain -= used; i_stream_skip(mstream->parent, used); /* see if there is more to read */ continue; } if (mstream->remain == 0) { /* need more data */ if (len < 5) { ret = i_stream_multiplex_ichannel_read(&req_channel->istream); if (ret > 0) got += ret; break; } /* channel ID */ mstream->cur_channel = data[0]; /* data length */ mstream->remain = be32_to_cpu_unaligned(data+1); i_stream_skip(mstream->parent, 5); } } propagate_error(mstream, mstream->parent->stream_errno); if (mstream->parent->eof) propagate_eof(mstream); return got; } static ssize_t i_stream_multiplex_ichannel_read(struct istream_private *stream) { struct multiplex_ichannel *channel = (struct multiplex_ichannel*)stream; /* if previous multiplex read dumped data for us actually serve it here. */ if (channel->pending_pos > 0) { ssize_t ret = channel->pending_pos; stream->pos += channel->pending_pos; channel->pending_pos = 0; return ret; } return i_stream_multiplex_read(channel->mstream, channel); } static void i_stream_multiplex_ichannel_switch_ioloop(struct istream_private *stream) { struct multiplex_ichannel *channel = (struct multiplex_ichannel*)stream; i_stream_switch_ioloop(channel->mstream->parent); } static void i_stream_multiplex_ichannel_close(struct iostream_private *stream, bool close_parent) { struct multiplex_ichannel *const *channelp; struct multiplex_ichannel *channel = (struct multiplex_ichannel*)stream; channel->closed = TRUE; if (close_parent) { array_foreach(&channel->mstream->channels, channelp) if (*channelp != NULL && !(*channelp)->closed) return; i_stream_close(channel->mstream->parent); } } static void i_stream_multiplex_try_destroy(struct multiplex_istream *mstream) { struct multiplex_ichannel **channelp; /* can't do anything until they are all closed */ array_foreach_modifiable(&mstream->channels, channelp) if (*channelp != NULL) return; i_stream_unref(&mstream->parent); array_free(&mstream->channels); i_free(mstream); } static void i_stream_multiplex_ichannel_destroy(struct iostream_private *stream) { struct multiplex_ichannel **channelp; struct multiplex_ichannel *channel = (struct multiplex_ichannel*)stream; i_stream_multiplex_ichannel_close(stream, TRUE); i_free(channel->istream.w_buffer); array_foreach_modifiable(&channel->mstream->channels, channelp) { if (*channelp == channel) { *channelp = NULL; break; } } i_stream_multiplex_try_destroy(channel->mstream); } static struct istream * i_stream_add_channel_real(struct multiplex_istream *mstream, uint8_t cid) { struct multiplex_ichannel *channel = i_new(struct multiplex_ichannel, 1); channel->cid = cid; channel->mstream = mstream; channel->istream.read = i_stream_multiplex_ichannel_read; channel->istream.switch_ioloop = i_stream_multiplex_ichannel_switch_ioloop; channel->istream.iostream.close = i_stream_multiplex_ichannel_close; channel->istream.iostream.destroy = i_stream_multiplex_ichannel_destroy; channel->istream.max_buffer_size = mstream->bufsize; channel->istream.istream.blocking = mstream->blocking; if (cid == 0) channel->istream.fd = i_stream_get_fd(mstream->parent); else channel->istream.fd = -1; array_append(&channel->mstream->channels, &channel, 1); return i_stream_create(&channel->istream, NULL, channel->istream.fd); } struct istream *i_stream_multiplex_add_channel(struct istream *stream, uint8_t cid) { struct multiplex_ichannel *chan = (struct multiplex_ichannel *)stream->real_stream; i_assert(get_channel(chan->mstream, cid) == NULL); return i_stream_add_channel_real(chan->mstream, cid); } struct istream *i_stream_create_multiplex(struct istream *parent, size_t bufsize) { struct multiplex_istream *mstream; mstream = i_new(struct multiplex_istream, 1); mstream->parent = parent; mstream->bufsize = bufsize; mstream->blocking = parent->blocking; i_array_init(&mstream->channels, 8); i_stream_ref(parent); return i_stream_add_channel_real(mstream, 0); } uint8_t i_stream_multiplex_get_channel_id(struct istream *stream) { struct multiplex_ichannel *channel = (struct multiplex_ichannel *)stream->real_stream; return channel->cid; } dovecot-2.2.33.2/src/lib/iostream-temp.h0000644000175000017500000000233013123174404014616 00000000000000#ifndef IOSTREAM_TEMP_H #define IOSTREAM_TEMP_H enum iostream_temp_flags { /* if o_stream_send_istream() is called with a readable fd, don't actually copy the input stream, just have iostream_temp_finish() return a new iostream pointing to the fd dup()ed */ IOSTREAM_TEMP_FLAG_TRY_FD_DUP = 0x01 }; /* Start writing to given output stream. The data is initially written to memory, and later to a temporary file that is immediately unlinked. */ struct ostream *iostream_temp_create(const char *temp_path_prefix, enum iostream_temp_flags flags); struct ostream *iostream_temp_create_named(const char *temp_path_prefix, enum iostream_temp_flags flags, const char *name); struct ostream *iostream_temp_create_sized(const char *temp_path_prefix, enum iostream_temp_flags flags, const char *name, size_t max_mem_size); /* Finished writing to stream. Return input stream for it and free the output stream. (It's also possible to abort iostream-temp by simply destroying the ostream.) */ struct istream *iostream_temp_finish(struct ostream **output, size_t max_buffer_size); /* For internal testing: */ int o_stream_temp_move_to_memory(struct ostream *output); #endif dovecot-2.2.33.2/src/lib/test-lib.c0000644000175000017500000000326213165463624013566 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" int main(void) { static void (*test_functions[])(void) = { test_aqueue, test_array, test_base32, test_base64, test_bits, test_bsearch_insert_pos, test_buffer, test_byteorder, test_crc32, test_data_stack, test_failures, test_file_create_locked, test_guid, test_hash, test_hash_format, test_hash_method, test_hex_binary, test_imem, test_ioloop, test_iso8601_date, test_iostream_temp, test_istream, test_istream_base64_decoder, test_istream_base64_encoder, test_istream_chain, test_istream_concat, test_istream_crlf, test_istream_failure_at, test_istream_multiplex, test_istream_seekable, test_istream_tee, test_istream_unix, test_json_parser, test_json_tree, test_llist, test_log_throttle, test_malloc_overflow, test_mempool, test_mempool_alloconly, test_net, test_numpack, test_pkcs5_pbkdf2, test_ostream_buffer, test_ostream_escaped, test_ostream_failure_at, test_ostream_file, test_ostream_multiplex, test_multiplex, test_primes, test_printf_format_fix, test_priorityq, test_seq_range_array, test_str, test_strescape, test_strfuncs, test_strnum, test_str_find, test_str_sanitize, test_str_table, test_time_util, test_timing, test_unichar, test_utc_mktime, test_var_expand, test_wildcard_match, NULL }; static enum fatal_test_state (*fatal_functions[])(unsigned int) = { fatal_array, fatal_data_stack, fatal_malloc_overflow, fatal_mempool, fatal_mempool_alloconly, fatal_printf_format_fix, NULL }; return test_run_with_fatals(test_functions, fatal_functions); } dovecot-2.2.33.2/src/lib/hash.h0000644000175000017500000001734413165463624013001 00000000000000#ifndef HASH_H #define HASH_H struct hash_table; #ifdef HAVE_TYPEOF # define HASH_VALUE_CAST(table) (typeof((table)._value)) #else # define HASH_VALUE_CAST(table) #endif /* Returns hash code. */ typedef unsigned int hash_callback_t(const void *p); /* Returns 0 if the pointers are equal. */ typedef int hash_cmp_callback_t(const void *p1, const void *p2); /* Create a new hash table. If initial_size is 0, the default value is used. table_pool is used to allocate/free large hash tables, node_pool is used for smaller allocations and can also be alloconly pool. The pools must not be free'd before hash_table_destroy() is called. */ void hash_table_create(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size, hash_callback_t *hash_cb, hash_cmp_callback_t *key_compare_cb); #if defined (__GNUC__) && !defined(__cplusplus) # define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ ({(void)COMPILE_ERROR_IF_TRUE( \ sizeof((*table)._key) != sizeof(void *) || \ sizeof((*table)._value) != sizeof(void *)); \ (void)COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ int (*)(typeof((*table)._key), typeof((*table)._key))) && \ !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ int (*)(typeof((*table)._const_key), typeof((*table)._const_key)))); \ (void)COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(&hash_cb), \ unsigned int (*)(typeof((*table)._key))) && \ !__builtin_types_compatible_p(typeof(&hash_cb), \ unsigned int (*)(typeof((*table)._const_key)))); \ hash_table_create(&(*table)._table, pool, size, \ (hash_callback_t *)hash_cb, \ (hash_cmp_callback_t *)key_cmp_cb);}) #else # define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ hash_table_create(&(*table)._table, pool, size, \ (hash_callback_t *)hash_cb, \ (hash_cmp_callback_t *)key_cmp_cb) #endif /* Create hash table where comparisons are done directly with the pointers. */ void hash_table_create_direct(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size); #if defined (__GNUC__) && !defined(__cplusplus) # define hash_table_create_direct(table, pool, size) \ ({(void)COMPILE_ERROR_IF_TRUE( \ sizeof((*table)._key) != sizeof(void *) || \ sizeof((*table)._value) != sizeof(void *)); \ hash_table_create_direct(&(*table)._table, pool, size);}) #else # define hash_table_create_direct(table, pool, size) \ hash_table_create_direct(&(*table)._table, pool, size) #endif #define hash_table_is_created(table) \ ((table)._table != NULL) void hash_table_destroy(struct hash_table **table); #define hash_table_destroy(table) \ hash_table_destroy(&(*table)._table) /* Remove all nodes from hash table. If free_collisions is TRUE, the memory allocated from node_pool is freed, or discarded with alloconly pools. WARNING: If you p_clear() the node_pool, the free_collisions must be TRUE. */ void hash_table_clear(struct hash_table *table, bool free_collisions); #define hash_table_clear(table, free_collisions) \ hash_table_clear((table)._table, free_collisions) void *hash_table_lookup(const struct hash_table *table, const void *key) ATTR_PURE; #define hash_table_lookup(table, key) \ HASH_VALUE_CAST(table)hash_table_lookup((table)._table, \ (const void *)((const char *)(key) + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._key, (table)._const_key, key))) bool hash_table_lookup_full(const struct hash_table *table, const void *lookup_key, void **orig_key_r, void **value_r); #ifndef __cplusplus # define hash_table_lookup_full(table, lookup_key, orig_key_r, value_r) \ hash_table_lookup_full((table)._table, \ (void *)((const char *)(lookup_key) + \ COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, lookup_key)), \ (void *)((orig_key_r) + \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, orig_key_r) + \ COMPILE_ERROR_IF_TRUE(sizeof(*(orig_key_r)) != sizeof(void *))), \ (void *)((value_r) + \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r) + \ COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)))) #else /* C++ requires (void **) casting, but that's not possible with strict aliasing, so .. we'll just disable the type checks */ # define hash_table_lookup_full(table, lookup_key, orig_key_r, value_r) \ hash_table_lookup_full((table)._table, lookup_key, orig_key_r, value_r) #endif /* Insert/update node in hash table. The difference is that hash_table_insert() replaces the key in table to given one, while hash_table_update() doesnt. */ void hash_table_insert(struct hash_table *table, void *key, void *value); void hash_table_update(struct hash_table *table, void *key, void *value); #define hash_table_insert(table, key, value) \ hash_table_insert((table)._table, \ (void *)((char*)(key) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key)), \ (void *)((char*)(value) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value))) #define hash_table_update(table, key, value) \ hash_table_update((table)._table, \ (void *)((char *)(key) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key)), \ (void *)((char *)(value) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value))) bool hash_table_try_remove(struct hash_table *table, const void *key); #define hash_table_try_remove(table, key) \ hash_table_try_remove((table)._table, \ (const void *)((const char *)(key) + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, key))) #define hash_table_remove(table, key) \ STMT_START { \ if (unlikely(!hash_table_try_remove(table, key))) \ i_panic("key not found from hash"); \ } STMT_END unsigned int hash_table_count(const struct hash_table *table) ATTR_PURE; #define hash_table_count(table) \ hash_table_count((table)._table) /* Iterates through all nodes in hash table. You may safely call hash_table_*() functions while iterating, but if you add any new nodes, they may or may not be called for in this iteration. */ struct hash_iterate_context *hash_table_iterate_init(struct hash_table *table); #define hash_table_iterate_init(table) \ hash_table_iterate_init((table)._table) bool hash_table_iterate(struct hash_iterate_context *ctx, void **key_r, void **value_r); #ifndef __cplusplus # define hash_table_iterate(ctx, table, key_r, value_r) \ hash_table_iterate(ctx, \ (void *)((key_r) + \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, key_r) + \ COMPILE_ERROR_IF_TRUE(sizeof(*(key_r)) != sizeof(void *)) + \ COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *))), \ (void *)((value_r) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r))) #else /* C++ requires (void **) casting, but that's not possible with strict aliasing, so .. we'll just disable the type checks */ # define hash_table_iterate(ctx, table, key_r, value_r) \ hash_table_iterate(ctx, key_r, value_r) #endif void hash_table_iterate_deinit(struct hash_iterate_context **ctx); /* Hash table isn't resized, and removed nodes aren't removed from the list while hash table is freezed. Supports nesting. */ void hash_table_freeze(struct hash_table *table); void hash_table_thaw(struct hash_table *table); #define hash_table_freeze(table) \ hash_table_freeze((table)._table) #define hash_table_thaw(table) \ hash_table_thaw((table)._table) /* Copy all nodes from one hash table to another */ void hash_table_copy(struct hash_table *dest, struct hash_table *src); #define hash_table_copy(table1, table2) \ hash_table_copy((table1)._table, (table2)._table) /* hash function for strings */ unsigned int str_hash(const char *p) ATTR_PURE; unsigned int strcase_hash(const char *p) ATTR_PURE; /* a generic hash for a given memory block */ unsigned int mem_hash(const void *p, unsigned int size) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/md4.c0000644000175000017500000001636513165463624012537 00000000000000/* * MD4 (RFC-1320) message digest. * Modified from MD5 code by Andrey Panin * * Written by Solar Designer in 2001, and placed in * the public domain. There's absolutely no warranty. * * This differs from Colin Plumb's older public domain implementation in * that no 32-bit integer data type is required, there's no compile-time * endianness configuration, and the function prototypes match OpenSSL's. * The primary goals are portability and ease of use. * * This implementation is meant to be fast, but not as fast as possible. * Some known optimizations are not included to reduce source code size * and avoid compile-time configuration. */ #include "lib.h" #include "safe-memset.h" #include "md4.h" /* * The basic MD4 functions. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) /* * The MD4 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, s) \ (a) += f((b), (c), (d)) + (x); \ (a) = ((a) << (s)) | ((a) >> (32 - (s))) /* * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * * The check for little-endian architectures which tolerate unaligned * memory accesses is just an optimization. Nothing will break if it * doesn't work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) /* uint_fast32_t might be 64 bit, and thus may read 4 more bytes * beyond the end of the buffer. So only read precisely 32 bits */ #define SET(n) \ (*(const uint32_t *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ (uint_fast32_t)ptr[(n) * 4] | \ ((uint_fast32_t)ptr[(n) * 4 + 1] << 8) | \ ((uint_fast32_t)ptr[(n) * 4 + 2] << 16) | \ ((uint_fast32_t)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There're no alignment requirements. */ static const void * ATTR_NOWARN_UNUSED_RESULT ATTR_UNSIGNED_WRAPS body(struct md4_context *ctx, const void *data, size_t size) { const unsigned char *ptr; uint32_t a, b, c, d; uint32_t saved_a, saved_b, saved_c, saved_d; ptr = data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; /* Round 1 */ STEP(F, a, b, c, d, SET( 0), 3); STEP(F, d, a, b, c, SET( 1), 7); STEP(F, c, d, a, b, SET( 2), 11); STEP(F, b, c, d, a, SET( 3), 19); STEP(F, a, b, c, d, SET( 4), 3); STEP(F, d, a, b, c, SET( 5), 7); STEP(F, c, d, a, b, SET( 6), 11); STEP(F, b, c, d, a, SET( 7), 19); STEP(F, a, b, c, d, SET( 8), 3); STEP(F, d, a, b, c, SET( 9), 7); STEP(F, c, d, a, b, SET(10), 11); STEP(F, b, c, d, a, SET(11), 19); STEP(F, a, b, c, d, SET(12), 3); STEP(F, d, a, b, c, SET(13), 7); STEP(F, c, d, a, b, SET(14), 11); STEP(F, b, c, d, a, SET(15), 19); /* Round 2 */ STEP(G, a, b, c, d, GET( 0) + 0x5A827999, 3); STEP(G, d, a, b, c, GET( 4) + 0x5A827999, 5); STEP(G, c, d, a, b, GET( 8) + 0x5A827999, 9); STEP(G, b, c, d, a, GET(12) + 0x5A827999, 13); STEP(G, a, b, c, d, GET( 1) + 0x5A827999, 3); STEP(G, d, a, b, c, GET( 5) + 0x5A827999, 5); STEP(G, c, d, a, b, GET( 9) + 0x5A827999, 9); STEP(G, b, c, d, a, GET(13) + 0x5A827999, 13); STEP(G, a, b, c, d, GET( 2) + 0x5A827999, 3); STEP(G, d, a, b, c, GET( 6) + 0x5A827999, 5); STEP(G, c, d, a, b, GET(10) + 0x5A827999, 9); STEP(G, b, c, d, a, GET(14) + 0x5A827999, 13); STEP(G, a, b, c, d, GET( 3) + 0x5A827999, 3); STEP(G, d, a, b, c, GET( 7) + 0x5A827999, 5); STEP(G, c, d, a, b, GET(11) + 0x5A827999, 9); STEP(G, b, c, d, a, GET(15) + 0x5A827999, 13); /* Round 3 */ STEP(H, a, b, c, d, GET( 0) + 0x6ED9EBA1, 3); STEP(H, d, a, b, c, GET( 8) + 0x6ED9EBA1, 9); STEP(H, c, d, a, b, GET( 4) + 0x6ED9EBA1, 11); STEP(H, b, c, d, a, GET(12) + 0x6ED9EBA1, 15); STEP(H, a, b, c, d, GET( 2) + 0x6ED9EBA1, 3); STEP(H, d, a, b, c, GET(10) + 0x6ED9EBA1, 9); STEP(H, c, d, a, b, GET( 6) + 0x6ED9EBA1, 11); STEP(H, b, c, d, a, GET(14) + 0x6ED9EBA1, 15); STEP(H, a, b, c, d, GET( 1) + 0x6ED9EBA1, 3); STEP(H, d, a, b, c, GET( 9) + 0x6ED9EBA1, 9); STEP(H, c, d, a, b, GET( 5) + 0x6ED9EBA1, 11); STEP(H, b, c, d, a, GET(13) + 0x6ED9EBA1, 15); STEP(H, a, b, c, d, GET( 3) + 0x6ED9EBA1, 3); STEP(H, d, a, b, c, GET(11) + 0x6ED9EBA1, 9); STEP(H, c, d, a, b, GET( 7) + 0x6ED9EBA1, 11); STEP(H, b, c, d, a, GET(15) + 0x6ED9EBA1, 15); a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return ptr; } void md4_init(struct md4_context *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->lo = 0; ctx->hi = 0; } void md4_update(struct md4_context *ctx, const void *data, size_t size) { /* @UNSAFE */ uint_fast32_t saved_lo; unsigned long used, free; saved_lo = ctx->lo; if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) ctx->hi++; ctx->hi += size >> 29; used = saved_lo & 0x3f; if (used) { free = 64 - used; if (size < free) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, free); data = (const unsigned char *) data + free; size -= free; body(ctx, ctx->buffer, 64); } if (size >= 64) { data = body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } void md4_final(struct md4_context *ctx, unsigned char result[STATIC_ARRAY MD4_RESULTLEN]) { /* @UNSAFE */ unsigned long used, free; used = ctx->lo & 0x3f; ctx->buffer[used++] = 0x80; free = 64 - used; if (free < 8) { memset(&ctx->buffer[used], 0, free); body(ctx, ctx->buffer, 64); used = 0; free = 64; } memset(&ctx->buffer[used], 0, free - 8); ctx->lo <<= 3; ctx->buffer[56] = ctx->lo; ctx->buffer[57] = ctx->lo >> 8; ctx->buffer[58] = ctx->lo >> 16; ctx->buffer[59] = ctx->lo >> 24; ctx->buffer[60] = ctx->hi; ctx->buffer[61] = ctx->hi >> 8; ctx->buffer[62] = ctx->hi >> 16; ctx->buffer[63] = ctx->hi >> 24; body(ctx, ctx->buffer, 64); result[0] = ctx->a; result[1] = ctx->a >> 8; result[2] = ctx->a >> 16; result[3] = ctx->a >> 24; result[4] = ctx->b; result[5] = ctx->b >> 8; result[6] = ctx->b >> 16; result[7] = ctx->b >> 24; result[8] = ctx->c; result[9] = ctx->c >> 8; result[10] = ctx->c >> 16; result[11] = ctx->c >> 24; result[12] = ctx->d; result[13] = ctx->d >> 8; result[14] = ctx->d >> 16; result[15] = ctx->d >> 24; i_zero_safe(ctx); } void md4_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY MD4_RESULTLEN]) { struct md4_context ctx; md4_init(&ctx); md4_update(&ctx, data, size); md4_final(&ctx, result); } static void hash_method_init_md4(void *context) { md4_init(context); } static void hash_method_loop_md4(void *context, const void *data, size_t size) { md4_update(context, data, size); } static void hash_method_result_md4(void *context, unsigned char *result_r) { md4_final(context, result_r); } const struct hash_method hash_method_md4 = { "md4", sizeof(struct md4_context), MD4_RESULTLEN, hash_method_init_md4, hash_method_loop_md4, hash_method_result_md4 }; dovecot-2.2.33.2/src/lib/iso8601-date.c0000644000175000017500000001636713123174404014066 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "utc-offset.h" #include "utc-mktime.h" #include "iso8601-date.h" #include /* RFC3339/ISO8601 date-time syntax date-fullyear = 4DIGIT date-month = 2DIGIT ; 01-12 date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on ; month/year time-hour = 2DIGIT ; 00-23 time-minute = 2DIGIT ; 00-59 time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second ; rules time-secfrac = "." 1*DIGIT time-numoffset = ("+" / "-") time-hour ":" time-minute time-offset = "Z" / time-numoffset partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] full-date = date-fullyear "-" date-month "-" date-mday full-time = partial-time time-offset date-time = full-date "T" full-time */ struct iso8601_date_parser { const unsigned char *cur, *end; struct tm tm; int timezone_offset; }; static inline int iso8601_date_parse_number(struct iso8601_date_parser *parser, int digits, int *number_r) { int i; if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) return 0; *number_r = parser->cur[0] - '0'; parser->cur++; for (i=0; i < digits-1; i++) { if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) return -1; *number_r = ((*number_r) * 10) + parser->cur[0] - '0'; parser->cur++; } return 1; } static int iso8601_date_parse_secfrac(struct iso8601_date_parser *parser) { /* time-secfrac = "." 1*DIGIT NOTE: Currently not applied anywhere, so fraction is just skipped. */ /* "." */ if (parser->cur >= parser->end || parser->cur[0] != '.') return 0; parser->cur++; /* 1DIGIT */ if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) return -1; parser->cur++; /* *DIGIT */ while (parser->cur < parser->end && i_isdigit(parser->cur[0])) parser->cur++; return 1; } static int is08601_date_parse_time_offset(struct iso8601_date_parser *parser) { int tz_sign = 1, tz_hour = 0, tz_min = 0; /* time-offset = "Z" / time-numoffset time-numoffset = ("+" / "-") time-hour ":" time-minute time-hour = 2DIGIT ; 00-23 time-minute = 2DIGIT ; 00-59 */ if (parser->cur >= parser->end) return 0; /* time-offset = "Z" / time-numoffset */ switch (parser->cur[0]) { case '-': tz_sign = -1; /* fall through */ case '+': parser->cur++; /* time-hour = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &tz_hour) <= 0) return -1; if (tz_hour > 23) return -1; /* ":" */ if (parser->cur >= parser->end || parser->cur[0] != ':') return -1; parser->cur++; /* time-minute = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &tz_min) <= 0) return -1; if (tz_min > 59) return -1; break; case 'Z': case 'z': parser->cur++; break; default: return -1; } parser->timezone_offset = tz_sign*(tz_hour*60 + tz_min); return 1; } static int is08601_date_parse_full_time(struct iso8601_date_parser *parser) { /* full-time = partial-time time-offset partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] time-hour = 2DIGIT ; 00-23 time-minute = 2DIGIT ; 00-59 time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second ; rules */ /* time-hour = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0) return -1; /* ":" */ if (parser->cur >= parser->end || parser->cur[0] != ':') return -1; parser->cur++; /* time-minute = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0) return -1; /* ":" */ if (parser->cur >= parser->end || parser->cur[0] != ':') return -1; parser->cur++; /* time-second = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0) return -1; /* [time-secfrac] */ if (iso8601_date_parse_secfrac(parser) < 0) return -1; /* time-offset */ if (is08601_date_parse_time_offset(parser) <= 0) return -1; return 1; } static int is08601_date_parse_full_date(struct iso8601_date_parser *parser) { /* full-date = date-fullyear "-" date-month "-" date-mday date-fullyear = 4DIGIT date-month = 2DIGIT ; 01-12 date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on ; month/year */ /* date-fullyear = 4DIGIT */ if (iso8601_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0) return -1; if (parser->tm.tm_year < 1900) return -1; parser->tm.tm_year -= 1900; /* "-" */ if (parser->cur >= parser->end || parser->cur[0] != '-') return -1; parser->cur++; /* date-month = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mon) <= 0) return -1; parser->tm.tm_mon -= 1; /* "-" */ if (parser->cur >= parser->end || parser->cur[0] != '-') return -1; parser->cur++; /* time-second = 2DIGIT */ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0) return -1; return 1; } static int iso8601_date_parse_date_time(struct iso8601_date_parser *parser) { /* date-time = full-date "T" full-time */ /* full-date */ if (is08601_date_parse_full_date(parser) <= 0) return -1; /* "T" */ if (parser->cur >= parser->end || (parser->cur[0] != 'T' && parser->cur[0] != 't')) return -1; parser->cur++; /* full-time */ if (is08601_date_parse_full_time(parser) <= 0) return -1; if (parser->cur != parser->end) return -1; return 1; } static bool iso8601_date_do_parse(const unsigned char *data, size_t size, struct tm *tm_r, time_t *timestamp_r, int *timezone_offset_r) { struct iso8601_date_parser parser; time_t timestamp; i_zero(&parser); parser.cur = data; parser.end = data + size; if (iso8601_date_parse_date_time(&parser) <= 0) return FALSE; parser.tm.tm_isdst = -1; timestamp = utc_mktime(&parser.tm); if (timestamp == (time_t)-1) return FALSE; *timezone_offset_r = parser.timezone_offset; *tm_r = parser.tm; *timestamp_r = timestamp - parser.timezone_offset * 60; return TRUE; } bool iso8601_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r, int *timezone_offset_r) { struct tm tm; return iso8601_date_do_parse(data, size, &tm, timestamp_r, timezone_offset_r); } bool iso8601_date_parse_tm(const unsigned char *data, size_t size, struct tm *tm_r, int *timezone_offset_r) { time_t timestamp; return iso8601_date_do_parse(data, size, tm_r, ×tamp, timezone_offset_r); } const char *iso8601_date_create_tm(struct tm *tm, int timezone_offset) { const char *time_offset; if (timezone_offset == INT_MAX) time_offset = "Z"; else { char sign = '+'; if (timezone_offset < 0) { timezone_offset = -timezone_offset; sign = '-'; } time_offset = t_strdup_printf("%c%02d:%02d", sign, timezone_offset / 60, timezone_offset % 60); } return t_strdup_printf("%04d-%02d-%02dT%02d:%02d:%02d%s", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, time_offset); } const char *iso8601_date_create(time_t timestamp) { struct tm *tm; int timezone_offset; tm = localtime(×tamp); timezone_offset = utc_offset(tm, timestamp); return iso8601_date_create_tm(tm, timezone_offset); } dovecot-2.2.33.2/src/lib/sort.h0000644000175000017500000000116113165463624013033 00000000000000#ifndef SORT_H #define SORT_H #define INTEGER_CMP(name, type) \ static inline int name(const type *i1, const type *i2) \ { \ if (*i1 < *i2) \ return -1; \ else if (*i1 > *i2) \ return 1; \ else \ return 0; \ } INTEGER_CMP(uint64_cmp, uint64_t) INTEGER_CMP(uint32_cmp, uint32_t) #define i_qsort(base, nmemb, size, cmp) \ qsort(base, nmemb, size + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*base) *), \ typeof(const typeof(*base) *))), \ (int (*)(const void *, const void *))cmp) #endif dovecot-2.2.33.2/src/lib/test-hash.c0000644000175000017500000000217513165463624013745 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "hash.h" static void test_hash_random_pool(pool_t pool) { #define KEYMAX 100000 HASH_TABLE(void *, void *) hash; unsigned int *keys; unsigned int i, key, keyidx, delidx; keys = i_new(unsigned int, KEYMAX); keyidx = 0; hash_table_create_direct(&hash, pool, 0); for (i = 0; i < KEYMAX; i++) { key = (rand() % KEYMAX) + 1; if (rand() % 5 > 0) { if (hash_table_lookup(hash, POINTER_CAST(key)) == NULL) { hash_table_insert(hash, POINTER_CAST(key), POINTER_CAST(1)); keys[keyidx++] = key; } } else if (keyidx > 0) { delidx = rand() % keyidx; hash_table_remove(hash, POINTER_CAST(keys[delidx])); memmove(&keys[delidx], &keys[delidx+1], (keyidx-delidx-1) * sizeof(*keys)); keyidx--; } } for (i = 0; i < keyidx; i++) hash_table_remove(hash, POINTER_CAST(keys[i])); hash_table_destroy(&hash); i_free(keys); } void test_hash(void) { pool_t pool; test_hash_random_pool(default_pool); pool = pool_alloconly_create("test hash", 1024); test_hash_random_pool(pool); pool_unref(&pool); } dovecot-2.2.33.2/src/lib/test-multiplex.c0000644000175000017500000001055713167162046015044 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "ioloop.h" #include "str.h" #include "fd-set-nonblock.h" #include "istream.h" #include "istream-multiplex.h" #include "ostream.h" #include "ostream-multiplex.h" #include "ostream.h" #include "randgen.h" #include struct test_channel { int fds[2]; unsigned int cid; struct istream *in; struct ostream *out; struct io *io; struct istream *in_alt; struct ostream *out_alt; struct io *io_alt; buffer_t *received; buffer_t *received_alt; unsigned int counter; }; static struct test_channel test_channel[2]; static void test_multiplex_channel_write(struct test_channel *channel) { unsigned char buf[128]; size_t len = rand() % sizeof(buf); random_fill(buf, len); o_stream_nsend(channel->out, buf, len); o_stream_nsend(channel->out_alt, buf, len); } static void test_multiplex_stream_write(struct ostream *channel ATTR_UNUSED) { if (test_channel[0].received->used > 1000 && test_channel[1].received->used > 1000) io_loop_stop(current_ioloop); else test_multiplex_channel_write(&test_channel[rand() % 2]); } static void test_istream_multiplex_stream_read(struct test_channel *channel) { const unsigned char *data = NULL; size_t siz = 0; if (i_stream_read(channel->in) > 0) { data = i_stream_get_data(channel->in, &siz); buffer_append(channel->received, data, siz); i_stream_skip(channel->in, siz); } } static void test_istream_read_alt(struct test_channel *channel) { const unsigned char *data = NULL; size_t siz = 0; if (i_stream_read(channel->in_alt) > 0) { data = i_stream_get_data(channel->in_alt, &siz); buffer_append(channel->received_alt, data, siz); i_stream_skip(channel->in_alt, siz); } } static void setup_channel(struct test_channel *channel, struct istream *is, struct ostream *os) { /* setup first channel */ channel->in = is; channel->out = os; channel->io = io_add_istream(is, test_istream_multiplex_stream_read, channel); test_assert(pipe(channel->fds) == 0); fd_set_nonblock(channel->fds[0], TRUE); fd_set_nonblock(channel->fds[1], TRUE); channel->in_alt = i_stream_create_fd(channel->fds[0], (size_t)-1, FALSE); channel->out_alt = o_stream_create_fd(channel->fds[1], IO_BLOCK_SIZE, FALSE); channel->io_alt = io_add_istream(channel->in_alt, test_istream_read_alt, channel); channel->received = buffer_create_dynamic(default_pool, 32768); channel->received_alt = buffer_create_dynamic(default_pool, 32768); } static void teardown_channel(struct test_channel *channel) { test_assert(memcmp(channel->received->data, channel->received_alt->data, channel->received->used) == 0); test_assert(channel->received->used == channel->received_alt->used); buffer_free(&channel->received); buffer_free(&channel->received_alt); io_remove(&channel->io); io_remove(&channel->io_alt); i_stream_unref(&channel->in); test_assert(o_stream_nfinish(channel->out) == 0); o_stream_unref(&channel->out); i_stream_unref(&channel->in_alt); test_assert(o_stream_nfinish(channel->out_alt) == 0); o_stream_unref(&channel->out_alt); i_close_fd(&channel->fds[0]); i_close_fd(&channel->fds[1]); } static void test_multiplex_stream(void) { test_begin("test multiplex (stream)"); struct ioloop *ioloop = io_loop_create(); io_loop_set_current(ioloop); int fds[2]; test_assert(pipe(fds) == 0); fd_set_nonblock(fds[0], TRUE); fd_set_nonblock(fds[1], TRUE); struct ostream *os = o_stream_create_fd(fds[1], (size_t)-1, FALSE); struct istream *is = i_stream_create_fd(fds[0], (size_t)-1, FALSE); struct istream *ichan0 = i_stream_create_multiplex(is, (size_t)-1); struct istream *ichan1 = i_stream_multiplex_add_channel(ichan0, 1); i_stream_unref(&is); struct ostream *ochan0 = o_stream_create_multiplex(os, 1024); struct ostream *ochan1 = o_stream_multiplex_add_channel(ochan0, 1); o_stream_unref(&os); struct io *io = io_add(fds[1], IO_WRITE, test_multiplex_stream_write, os); setup_channel(&test_channel[0], ichan0, ochan0); setup_channel(&test_channel[1], ichan1, ochan1); test_channel[0].cid = 0; test_channel[1].cid = 1; io_loop_run(current_ioloop); io_remove(&io); teardown_channel(&test_channel[0]); teardown_channel(&test_channel[1]); io_loop_destroy(&ioloop); i_close_fd(&fds[0]); i_close_fd(&fds[1]); test_end(); } void test_multiplex(void) { random_init(); test_multiplex_stream(); random_deinit(); } dovecot-2.2.33.2/src/lib/json-parser.h0000644000175000017500000000361313123174404014300 00000000000000#ifndef JSON_PARSER_H #define JSON_PARSER_H enum json_type { /* { key: */ JSON_TYPE_OBJECT_KEY, /* : { new object */ JSON_TYPE_OBJECT, /* } (not returned for the root object) */ JSON_TYPE_OBJECT_END, JSON_TYPE_ARRAY, JSON_TYPE_ARRAY_END, JSON_TYPE_STRING, JSON_TYPE_NUMBER, JSON_TYPE_TRUE, JSON_TYPE_FALSE, JSON_TYPE_NULL }; enum json_parser_flags { /* By default we assume that the input is an object and parsing skips the root level "{" and "}". If this flag is set, it's possible to parse any other type of JSON values directly. */ JSON_PARSER_NO_ROOT_OBJECT = 0x01 }; /* Parse JSON tokens from the input stream. */ struct json_parser *json_parser_init(struct istream *input); struct json_parser *json_parser_init_flags(struct istream *input, enum json_parser_flags flags); int json_parser_deinit(struct json_parser **parser, const char **error_r); /* Parse the next token. Returns 1 if found, 0 if more input stream is non-blocking and needs more input, -1 if input stream is at EOF. */ int json_parse_next(struct json_parser *parser, enum json_type *type_r, const char **value_r); /* Skip the next object value. If it's an object, its members are also skipped. */ void json_parse_skip_next(struct json_parser *parser); /* Return the following string as input stream. Returns 1 if ok, 0 if input stream is non-blocking and needs more input, -1 if the next token isn't a string (call json_parse_next()). */ int json_parse_next_stream(struct json_parser *parser, struct istream **input_r); /* Append data to already opened JSON string. src should be valid UTF-8 data. */ void json_append_escaped(string_t *dest, const char *src); /* Same as json_append_escaped(), but append non-\0 terminated input. */ void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size); void ostream_escaped_json_format(string_t *dest, unsigned char src); #endif dovecot-2.2.33.2/src/lib/test-istream-chain.c0000644000175000017500000001302113123174404015523 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "istream-private.h" #include "istream-chain.h" static void test_istream_chain_basic(void) { struct istream *input, *test_input, *test_input2; struct istream_chain *chain; const unsigned char *data; size_t size; test_begin("istream chain"); test_input = test_istream_create("stream1"); test_input2 = test_istream_create("STREAM2"); input = i_stream_create_chain(&chain); /* no input */ test_assert(i_stream_read(input) == 0); /* stream1 input */ i_stream_chain_append(chain, test_input); test_assert(i_stream_read(input) == 7); data = i_stream_get_data(input, &size); test_assert(size == 7 && memcmp(data, "stream1", 7) == 0); test_assert(i_stream_read(input) == 0); data = i_stream_get_data(input, &size); test_assert(size == 7 && memcmp(data, "stream1", 7) == 0); /* STREAM2 input */ i_stream_chain_append(chain, test_input2); test_assert(i_stream_read(input) == 7); data = i_stream_get_data(input, &size); test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); test_assert(i_stream_read(input) == 0); data = i_stream_get_data(input, &size); test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); /* EOF */ i_stream_chain_append_eof(chain); test_assert(i_stream_read(input) == -1 && input->eof && input->stream_errno == 0); data = i_stream_get_data(input, &size); test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); i_stream_unref(&input); test_assert(i_stream_is_eof(test_input)); test_assert(i_stream_is_eof(test_input2)); i_stream_unref(&test_input); i_stream_unref(&test_input2); test_end(); } static void test_istream_chain_early_end(void) { struct istream *input, *test_input; struct istream_chain *chain; test_begin("istream chain early end"); test_input = test_istream_create("string"); test_istream_set_size(test_input, 3); test_istream_set_allow_eof(test_input, FALSE); input = i_stream_create_chain(&chain); i_stream_chain_append(chain, test_input); test_assert(i_stream_read(input) == 3); test_istream_set_size(test_input, 5); test_assert(i_stream_read(input) == 2); /* with current implementation we could skip less than 5 and have v_offset<5, but I don't think that can work in all situations. the normal case is anyway that we'll read everything up until some point and skip over all the data up to there. */ i_stream_skip(input, 5); i_stream_unref(&input); test_assert(test_input->v_offset == 5); i_stream_unref(&test_input); test_end(); } static void test_istream_chain_accumulate(void) { struct istream *input; struct istream *test_input, *test_input2, *test_input3, *test_input4, *test_input5; struct istream_chain *chain; const unsigned char *data; size_t size; test_begin("istream chain accumulate"); test_input = test_istream_create("aaaaaaaaaaaaaaaaaaaa"); test_input2 = test_istream_create("bbbbbbbbbbbbbbbbbbbbbbbbb"); test_input3 = test_istream_create("cccccccccccccccccccccccccccccc"); test_input4 = test_istream_create("ddddddddddddddddddddddddd"); test_input5 = test_istream_create("eeeeeeeeeeeeeeeeeeee"); input = i_stream_create_chain(&chain); /* no input */ test_assert(i_stream_read(input) == 0); /* first stream */ i_stream_chain_append(chain, test_input); test_assert(i_stream_read_data(input, &data, &size, 0) == 1); test_assert(size == 20); test_assert(memcmp(data, "aaaaaaaaaaaaaaaaaaaa", 20) == 0); /* partially skip */ i_stream_skip(input, 12); /* second stream */ i_stream_chain_append(chain, test_input2); test_assert(i_stream_read_data(input, &data, &size, 10) == 1); test_assert(size == 33); test_assert(memcmp(data, "aaaaaaaa" "bbbbbbbbbbbbbbbbbbbbbbbbb", 33) == 0); /* partially skip */ i_stream_skip(input, 12); /* third stream */ i_stream_chain_append(chain, test_input3); test_assert(i_stream_read_data(input, &data, &size, 25) == 1); test_assert(size == 51); test_assert(memcmp(data, "bbbbbbbbbbbbbbbbbbbbb" "cccccccccccccccccccccccccccccc", 51) == 0); /* partially skip */ i_stream_skip(input, 12); /* forth stream */ i_stream_chain_append(chain, test_input4); test_assert(i_stream_read_data(input, &data, &size, 40) == 1); test_assert(size == 64); test_assert(memcmp(data, "bbbbbbbbb" "cccccccccccccccccccccccccccccc" "ddddddddddddddddddddddddd", 64) == 0); /* partially skip */ i_stream_skip(input, 6); /* fifth stream */ i_stream_chain_append(chain, test_input5); test_assert(i_stream_read_data(input, &data, &size, 60) == 1); test_assert(size == 78); test_assert(memcmp(data, "bbb" "cccccccccccccccccccccccccccccc" "ddddddddddddddddddddddddd" "eeeeeeeeeeeeeeeeeeee", 78) == 0); /* EOF */ i_stream_chain_append_eof(chain); test_assert(i_stream_read(input) == -1); test_assert(input->eof && input->stream_errno == 0); test_assert(i_stream_read_data(input, &data, &size, 78) == -1); test_assert(size == 78); test_assert(memcmp(data, "bbb" "cccccccccccccccccccccccccccccc" "ddddddddddddddddddddddddd" "eeeeeeeeeeeeeeeeeeee", 78) == 0); /* skip rest */ i_stream_skip(input, 78); test_assert(i_stream_read(input) == -1); test_assert(input->eof && input->stream_errno == 0); data = i_stream_get_data(input, &size); test_assert(size == 0); i_stream_unref(&input); i_stream_unref(&test_input); i_stream_unref(&test_input2); i_stream_unref(&test_input3); i_stream_unref(&test_input4); i_stream_unref(&test_input5); test_end(); } void test_istream_chain(void) { test_istream_chain_basic(); test_istream_chain_early_end(); test_istream_chain_accumulate(); } dovecot-2.2.33.2/src/lib/printf-format-fix.c0000644000175000017500000000454113123174404015405 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "printf-format-fix.h" static const char * fix_format_real(const char *fmt, const char *p, size_t *len_r) { const char *errstr; char *buf; size_t len1, len2, len3; i_assert((size_t)(p - fmt) < INT_MAX); i_assert(p[0] == '%' && p[1] == 'm'); errstr = strerror(errno); /* we'll assume that there's only one %m in the format string. this simplifies the code and there's really no good reason to have it multiple times. Callers can trap this case themselves. */ len1 = p - fmt; len2 = strlen(errstr); len3 = strlen(p + 2); /* @UNSAFE */ buf = t_buffer_get(len1 + len2 + len3 + 1); memcpy(buf, fmt, len1); memcpy(buf + len1, errstr, len2); memcpy(buf + len1 + len2, p + 2, len3 + 1); *len_r = len1 + len2 + len3; return buf; } static const char * printf_format_fix_noalloc(const char *format, size_t *len_r) { static const char *printf_skip_chars = "# -+'I.*0123456789hlLjzt"; /* as a tiny optimization keep the most commonly used conversion modifiers first, so strchr() stops early. */ static const char *printf_allowed_conversions = "sudcioxXp%eEfFgGaA"; const char *ret, *p, *p2; p = ret = format; while ((p2 = strchr(p, '%')) != NULL) { p = p2+1; while (*p != '\0' && strchr(printf_skip_chars, *p) != NULL) p++; if (*p == '\0') { i_panic("%% modifier missing in '%s'", format); } else if (strchr(printf_allowed_conversions, *p) != NULL) { /* allow & ignore */ } else { switch (*p) { case 'n': i_panic("%%n modifier used"); case 'm': if (ret != format) i_panic("%%m used twice"); ret = fix_format_real(format, p-1, len_r); break; default: i_panic("Unsupported %%%c modifier", *p); } } p++; } if (ret == format) *len_r = p - format + strlen(p); return ret; } const char *printf_format_fix_get_len(const char *format, size_t *len_r) { const char *ret; ret = printf_format_fix_noalloc(format, len_r); if (ret != format) t_buffer_alloc(*len_r + 1); return ret; } const char *printf_format_fix(const char *format) { const char *ret; size_t len; ret = printf_format_fix_noalloc(format, &len); if (ret != format) t_buffer_alloc(len + 1); return ret; } const char *printf_format_fix_unsafe(const char *format) { size_t len; return printf_format_fix_noalloc(format, &len); } dovecot-2.2.33.2/src/lib/utc-offset.h0000644000175000017500000000024413123174404014111 00000000000000#ifndef UTC_OFFSET_H #define UTC_OFFSET_H #include /* Returns given time's offset to UTC in minutes. */ int utc_offset(struct tm *tm, time_t t); #endif dovecot-2.2.33.2/src/lib/bsearch-insert-pos.c0000644000175000017500000000205113123174404015533 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #undef bsearch_insert_pos bool bsearch_insert_pos(const void *key, const void *base, unsigned int nmemb, size_t size, int (*cmp)(const void *, const void *), unsigned int *idx_r) { const void *p; unsigned int idx, left_idx, right_idx; int ret; i_assert(nmemb < INT_MAX); idx = 0; left_idx = 0; right_idx = nmemb; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; p = CONST_PTR_OFFSET(base, idx * size); ret = cmp(key, p); if (ret > 0) left_idx = idx+1; else if (ret < 0) right_idx = idx; else { *idx_r = idx; return TRUE; } } if (left_idx > idx) idx++; *idx_r = idx; return FALSE; } bool array_bsearch_insert_pos_i(const struct array *array, const void *key, int (*cmp)(const void *, const void *), unsigned int *idx_r) { return bsearch_insert_pos(key, array->buffer->data, array_count_i(array), array->element_size, cmp, idx_r); } dovecot-2.2.33.2/src/lib/mempool.c0000644000175000017500000000071713123174404013502 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" size_t pool_get_exp_grown_size(pool_t pool, size_t old_size, size_t min_size) { size_t exp_size, easy_size; i_assert(old_size < min_size); exp_size = nearest_power(min_size); easy_size = old_size + p_get_max_easy_alloc_size(pool); if (easy_size < exp_size && easy_size >= min_size) exp_size = easy_size; i_assert(exp_size >= min_size); return exp_size; } dovecot-2.2.33.2/src/lib/iostream.c0000644000175000017500000000606113165463624013666 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "iostream-private.h" static void io_stream_default_close(struct iostream_private *stream ATTR_UNUSED, bool close_parent ATTR_UNUSED) { } static void io_stream_default_destroy(struct iostream_private *stream ATTR_UNUSED) { } void io_stream_init(struct iostream_private *stream) { if (stream->close == NULL) stream->close = io_stream_default_close; if (stream->destroy == NULL) stream->destroy = io_stream_default_destroy; stream->refcount = 1; } void io_stream_ref(struct iostream_private *stream) { stream->refcount++; } void io_stream_unref(struct iostream_private *stream) { const struct iostream_destroy_callback *dc; i_assert(stream->refcount > 0); if (--stream->refcount != 0) return; stream->close(stream, FALSE); stream->destroy(stream); if (array_is_created(&stream->destroy_callbacks)) { array_foreach(&stream->destroy_callbacks, dc) dc->callback(dc->context); array_free(&stream->destroy_callbacks); } i_free(stream->error); i_free(stream->name); i_free(stream); } void io_stream_close(struct iostream_private *stream, bool close_parent) { stream->close(stream, close_parent); } void io_stream_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { stream->set_max_buffer_size(stream, max_size); } void io_stream_add_destroy_callback(struct iostream_private *stream, void (*callback)(void *), void *context) { struct iostream_destroy_callback *dc; if (!array_is_created(&stream->destroy_callbacks)) i_array_init(&stream->destroy_callbacks, 2); dc = array_append_space(&stream->destroy_callbacks); dc->callback = callback; dc->context = context; } void io_stream_remove_destroy_callback(struct iostream_private *stream, void (*callback)(void *)) { const struct iostream_destroy_callback *dcs; unsigned int i, count; dcs = array_get(&stream->destroy_callbacks, &count); for (i = 0; i < count; i++) { if (dcs[i].callback == callback) { array_delete(&stream->destroy_callbacks, i, 1); return; } } i_unreached(); } void io_stream_set_error(struct iostream_private *stream, const char *fmt, ...) { va_list args; va_start(args, fmt); io_stream_set_verror(stream, fmt, args); va_end(args); } void io_stream_set_verror(struct iostream_private *stream, const char *fmt, va_list args) { i_free(stream->error); stream->error = i_strdup_vprintf(fmt, args); } const char *io_stream_get_disconnect_reason(struct istream *input, struct ostream *output) { const char *errstr; if (input != NULL && input->stream_errno != 0) { errno = input->stream_errno; errstr = i_stream_get_error(input); } else if (output != NULL && output->stream_errno != 0) { errno = output->stream_errno; errstr = o_stream_get_error(output); } else { errno = 0; errstr = ""; } if (errno == 0 || errno == EPIPE) return "Connection closed"; else return t_strdup_printf("Connection closed: %s", errstr); } dovecot-2.2.33.2/src/lib/hmac.c0000644000175000017500000000455113123174404012742 00000000000000/* * HMAC (RFC-2104) implementation. * * Copyright (c) 2004 Andrey Panin * Copyright (c) 2011-2016 Florian Zeitz * * This software is released under the MIT license. */ #include "lib.h" #include "hmac.h" #include "safe-memset.h" #include "buffer.h" void hmac_init(struct hmac_context *_ctx, const unsigned char *key, size_t key_len, const struct hash_method *meth) { struct hmac_context_priv *ctx = &_ctx->u.priv; int i; unsigned char k_ipad[64]; unsigned char k_opad[64]; unsigned char hashedkey[meth->digest_size]; i_assert(meth->context_size <= HMAC_MAX_CONTEXT_SIZE); ctx->hash = meth; if (key_len > 64) { meth->init(ctx->ctx); meth->loop(ctx->ctx, key, key_len); meth->result(ctx->ctx, hashedkey); key = hashedkey; key_len = meth->digest_size; } memcpy(k_ipad, key, key_len); memset(k_ipad + key_len, 0, 64 - key_len); memcpy(k_opad, k_ipad, 64); for (i = 0; i < 64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } meth->init(ctx->ctx); meth->loop(ctx->ctx, k_ipad, 64); meth->init(ctx->ctxo); meth->loop(ctx->ctxo, k_opad, 64); safe_memset(k_ipad, 0, 64); safe_memset(k_opad, 0, 64); } void hmac_final(struct hmac_context *_ctx, unsigned char *digest) { struct hmac_context_priv *ctx = &_ctx->u.priv; ctx->hash->result(ctx->ctx, digest); ctx->hash->loop(ctx->ctxo, digest, ctx->hash->digest_size); ctx->hash->result(ctx->ctxo, digest); } buffer_t *t_hmac_data(const struct hash_method *meth, const unsigned char *key, size_t key_len, const void *data, size_t data_len) { struct hmac_context ctx; i_assert(meth != NULL); i_assert(key != NULL && key_len > 0); i_assert(data != NULL || data_len == 0); buffer_t *res = buffer_create_dynamic(pool_datastack_create(), meth->digest_size); hmac_init(&ctx, key, key_len, meth); if (data_len > 0) hmac_update(&ctx, data, data_len); unsigned char *buf = buffer_get_space_unsafe(res, 0, meth->digest_size); hmac_final(&ctx, buf); return res; } buffer_t *t_hmac_buffer(const struct hash_method *meth, const unsigned char *key, size_t key_len, const buffer_t *data) { return t_hmac_data(meth, key, key_len, data->data, data->used); } buffer_t *t_hmac_str(const struct hash_method *meth, const unsigned char *key, size_t key_len, const char *data) { return t_hmac_data(meth, key, key_len, data, strlen(data)); } dovecot-2.2.33.2/src/lib/sha2.c0000644000175000017500000003413013123174404012663 00000000000000/* * FIPS 180-2 SHA-224/256/384/512 implementation * Last update: 02/02/2007 * Issue date: 04/30/2005 * * Copyright (C) 2005, 2007 Olivier Gay * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "lib.h" #include "sha2.h" #define SHFR(x, n) (x >> n) #define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) #define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) #define CH(x, y, z) ((x & y) ^ (~x & z)) #define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) #define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) #define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) #define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) #define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) #define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) #define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) #define UNPACK32(x, str) \ { \ *((str) + 3) = (uint8_t) ((x) ); \ *((str) + 2) = (uint8_t) ((x) >> 8); \ *((str) + 1) = (uint8_t) ((x) >> 16); \ *((str) + 0) = (uint8_t) ((x) >> 24); \ } #define PACK32(str, x) \ { \ *(x) = ((uint32_t) *((str) + 3) ) \ | ((uint32_t) *((str) + 2) << 8) \ | ((uint32_t) *((str) + 1) << 16) \ | ((uint32_t) *((str) + 0) << 24); \ } #define UNPACK64(x, str) \ { \ *((str) + 7) = (uint8_t) ((x) ); \ *((str) + 6) = (uint8_t) ((x) >> 8); \ *((str) + 5) = (uint8_t) ((x) >> 16); \ *((str) + 4) = (uint8_t) ((x) >> 24); \ *((str) + 3) = (uint8_t) ((x) >> 32); \ *((str) + 2) = (uint8_t) ((x) >> 40); \ *((str) + 1) = (uint8_t) ((x) >> 48); \ *((str) + 0) = (uint8_t) ((x) >> 56); \ } #define PACK64(str, x) \ { \ *(x) = ((uint64_t) *((str) + 7) ) \ | ((uint64_t) *((str) + 6) << 8) \ | ((uint64_t) *((str) + 5) << 16) \ | ((uint64_t) *((str) + 4) << 24) \ | ((uint64_t) *((str) + 3) << 32) \ | ((uint64_t) *((str) + 2) << 40) \ | ((uint64_t) *((str) + 1) << 48) \ | ((uint64_t) *((str) + 0) << 56); \ } #define SHA256_SCR(i) \ { \ w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + SHA256_F3(w[i - 15]) + w[i - 16]; \ } #define SHA512_SCR(i) \ { \ w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ + SHA512_F3(w[i - 15]) + w[i - 16]; \ } static const uint32_t sha256_h0[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; static const uint64_t sha512_h0[8] = {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; static const uint32_t sha256_k[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; static const uint64_t sha512_k[80] = {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; /* SHA-256 functions */ static void ATTR_UNSIGNED_WRAPS sha256_transf(struct sha256_ctx *ctx, const unsigned char *data, size_t block_nb) { uint32_t w[64]; uint32_t wv[8]; uint32_t t1, t2; const unsigned char *sub_block; int i,j; for (i = 0; i < (int) block_nb; i++) { sub_block = data + (i << 6); for (j = 0; j < 16; j++) { PACK32(&sub_block[j << 2], &w[j]); } for (j = 16; j < 64; j++) { SHA256_SCR(j); } for (j = 0; j < 8; j++) { wv[j] = ctx->h[j]; } for (j = 0; j < 64; j++) { t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); wv[7] = wv[6]; wv[6] = wv[5]; wv[5] = wv[4]; wv[4] = wv[3] + t1; wv[3] = wv[2]; wv[2] = wv[1]; wv[1] = wv[0]; wv[0] = t1 + t2; } for (j = 0; j < 8; j++) { ctx->h[j] += wv[j]; } } } void sha256_init(struct sha256_ctx *ctx) { int i; for (i = 0; i < 8; i++) { ctx->h[i] = sha256_h0[i]; } ctx->len = 0; ctx->tot_len = 0; } void sha256_loop(struct sha256_ctx *ctx, const void *data, size_t len) { const unsigned char *shifted_message; size_t block_nb; size_t new_len, rem_len, tmp_len; tmp_len = SHA256_BLOCK_SIZE - ctx->len; rem_len = len < tmp_len ? len : tmp_len; memcpy(&ctx->block[ctx->len], data, rem_len); if (ctx->len + len < SHA256_BLOCK_SIZE) { ctx->len += len; return; } new_len = len - rem_len; block_nb = new_len / SHA256_BLOCK_SIZE; shifted_message = CONST_PTR_OFFSET(data, rem_len); sha256_transf(ctx, ctx->block, 1); sha256_transf(ctx, shifted_message, block_nb); rem_len = new_len % SHA256_BLOCK_SIZE; memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); ctx->len = rem_len; ctx->tot_len += (block_nb + 1) << 6; } void sha256_result(struct sha256_ctx *ctx, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) { size_t block_nb; size_t pm_len; size_t len_b; int i; block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE))); len_b = (ctx->tot_len + ctx->len) << 3; pm_len = block_nb << 6; memset(ctx->block + ctx->len, 0, pm_len - ctx->len); ctx->block[ctx->len] = 0x80; UNPACK32(len_b, ctx->block + pm_len - 4); sha256_transf(ctx, ctx->block, block_nb); for (i = 0 ; i < 8; i++) { UNPACK32(ctx->h[i], &digest[i << 2]); } } void sha256_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) { struct sha256_ctx ctx; sha256_init(&ctx); sha256_loop(&ctx, data, size); sha256_result(&ctx, digest); } /* SHA-512 functions */ static void ATTR_UNSIGNED_WRAPS sha512_transf(struct sha512_ctx *ctx, const unsigned char *data, size_t block_nb) { uint64_t w[80]; uint64_t wv[8]; uint64_t t1, t2; const unsigned char *sub_block; int i, j; for (i = 0; i < (int) block_nb; i++) { sub_block = data + (i << 7); for (j = 0; j < 16; j++) { PACK64(&sub_block[j << 3], &w[j]); } for (j = 16; j < 80; j++) { SHA512_SCR(j); } for (j = 0; j < 8; j++) { wv[j] = ctx->h[j]; } for (j = 0; j < 80; j++) { t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j]; t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); wv[7] = wv[6]; wv[6] = wv[5]; wv[5] = wv[4]; wv[4] = wv[3] + t1; wv[3] = wv[2]; wv[2] = wv[1]; wv[1] = wv[0]; wv[0] = t1 + t2; } for (j = 0; j < 8; j++) { ctx->h[j] += wv[j]; } } } void sha512_init(struct sha512_ctx *ctx) { int i; for (i = 0; i < 8; i++) { ctx->h[i] = sha512_h0[i]; } ctx->len = 0; ctx->tot_len = 0; } void sha512_loop(struct sha512_ctx *ctx, const void *data, size_t len) { const unsigned char *shifted_message; size_t block_nb; size_t new_len, rem_len, tmp_len; tmp_len = SHA512_BLOCK_SIZE - ctx->len; rem_len = len < tmp_len ? len : tmp_len; memcpy(&ctx->block[ctx->len], data, rem_len); if (ctx->len + len < SHA512_BLOCK_SIZE) { ctx->len += len; return; } new_len = len - rem_len; block_nb = new_len / SHA512_BLOCK_SIZE; shifted_message = CONST_PTR_OFFSET(data, rem_len); sha512_transf(ctx, ctx->block, 1); sha512_transf(ctx, shifted_message, block_nb); rem_len = new_len % SHA512_BLOCK_SIZE; memcpy(ctx->block, &shifted_message[block_nb << 7], rem_len); ctx->len = rem_len; ctx->tot_len += (block_nb + 1) << 7; } void sha512_result(struct sha512_ctx *ctx, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) { unsigned int block_nb; unsigned int pm_len; size_t len_b; int i; block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) < (ctx->len % SHA512_BLOCK_SIZE)); len_b = (ctx->tot_len + ctx->len) << 3; pm_len = block_nb << 7; memset(ctx->block + ctx->len, 0, pm_len - ctx->len); ctx->block[ctx->len] = 0x80; UNPACK32(len_b, ctx->block + pm_len - 4); sha512_transf(ctx, ctx->block, block_nb); for (i = 0 ; i < 8; i++) { UNPACK64(ctx->h[i], &digest[i << 3]); } } void sha512_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) { struct sha512_ctx ctx; sha512_init(&ctx); sha512_loop(&ctx, data, size); sha512_result(&ctx, digest); } static void hash_method_init_sha256(void *context) { sha256_init(context); } static void hash_method_loop_sha256(void *context, const void *data, size_t size) { sha256_loop(context, data, size); } static void hash_method_result_sha256(void *context, unsigned char *result_r) { sha256_result(context, result_r); } const struct hash_method hash_method_sha256 = { "sha256", sizeof(struct sha256_ctx), SHA256_RESULTLEN, hash_method_init_sha256, hash_method_loop_sha256, hash_method_result_sha256 }; static void hash_method_init_sha512(void *context) { sha512_init(context); } static void hash_method_loop_sha512(void *context, const void *data, size_t size) { sha512_loop(context, data, size); } static void hash_method_result_sha512(void *context, unsigned char *result_r) { sha512_result(context, result_r); } const struct hash_method hash_method_sha512 = { "sha512", sizeof(struct sha512_ctx), SHA512_RESULTLEN, hash_method_init_sha512, hash_method_loop_sha512, hash_method_result_sha512 }; dovecot-2.2.33.2/src/lib/test-istream-crlf.c0000644000175000017500000000544613165463624015416 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-crlf.h" static void test_istream_crlf_input(const char *input) { string_t *output; const unsigned char *data; size_t size = 0; ssize_t ret1, ret2; unsigned int i, j, pos, input_len = strlen(input); struct istream *istream, *crlf_istream; output = t_str_new(256); for (j = 0; j < 4; j++) { istream = i_stream_create_from_data(input, input_len); str_truncate(output, 0); if (j%2 == 0) { /* drop CRs */ crlf_istream = i_stream_create_lf(istream); for (i = 0; i < input_len; i++) { if (input[i] == '\r' && (i == input_len-1 || input[i+1] == '\n')) ; else str_append_c(output, input[i]); } } else { /* add missing CRs */ crlf_istream = i_stream_create_crlf(istream); for (i = 0; i < input_len; i++) { if (input[i] == '\n' && (i == 0 || input[i-1] != '\r')) str_append_c(output, '\r'); str_append_c(output, input[i]); } } pos = 0; for (i = 1; i <= input_len; i++) { if (j >= 2) { i_stream_unref(&istream); i_stream_unref(&crlf_istream); istream = i_stream_create_from_data(input, input_len); crlf_istream = j%2 == 0 ? i_stream_create_lf(istream) : i_stream_create_crlf(istream); pos = 0; } istream->real_stream->pos = i; ret1 = i_stream_read(crlf_istream); if (crlf_istream->real_stream->buffer_size != 0) { /* this is pretty evil */ crlf_istream->real_stream->buffer_size = I_MAX(crlf_istream->real_stream->pos, i); } ret2 = i_stream_read(crlf_istream); data = i_stream_get_data(crlf_istream, &size); if (ret1 > 0 || ret2 > 0) { ret1 = I_MAX(ret1, 0) + I_MAX(ret2, 0); test_assert(pos + (unsigned int)ret1 == size); pos += ret1; } if (size > 0) test_assert_idx(memcmp(data, str_data(output), size) == 0, j*10000+i); } test_assert_idx(size == str_len(output), j*10000+i); i_stream_unref(&crlf_istream); i_stream_unref(&istream); } } void test_istream_crlf(void) { const char *input[] = { "\rfoo", "foo\nbar\r\nbaz\r\r\n", "\r\nfoo", "\r\r\n", "\nfoo" }; unsigned int i; test_begin("istream crlf"); for (i = 0; i < N_ELEMENTS(input); i++) test_istream_crlf_input(input[i]); test_end(); #define ISTREAM_CRLF_TEST_REPS 1000 test_begin("istream crlf(random)"); for (i = 0; i < ISTREAM_CRLF_TEST_REPS; i++) T_BEGIN { char buf[100]; size_t len = 0; while (len < sizeof(buf) - 1) { switch(rand()%16) { case 0: goto outahere; case 1: buf[len] = '\r'; break; case 2: buf[len] = '\n'; break; default: buf[len]= '.'; break; } len++; } outahere: buf[len] = '\0'; if (len > 0) test_istream_crlf_input(buf); } T_END; test_end(); } dovecot-2.2.33.2/src/lib/ostream-hash.c0000644000175000017500000000261013123174404014417 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash-method.h" #include "ostream-private.h" #include "ostream-hash.h" struct hash_ostream { struct ostream_private ostream; const struct hash_method *method; void *hash_context; }; static ssize_t o_stream_hash_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct hash_ostream *hstream = (struct hash_ostream *)stream; unsigned int i; size_t bytes_left, block_len; ssize_t ret; if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { o_stream_copy_error_from_parent(stream); return -1; } if (ret > 0) { bytes_left = ret; for (i = 0; i < iov_count && bytes_left > 0; i++) { block_len = iov[i].iov_len <= bytes_left ? iov[i].iov_len : bytes_left; hstream->method->loop(hstream->hash_context, iov[i].iov_base, block_len); bytes_left -= block_len; } } stream->ostream.offset += ret; return ret; } struct ostream * o_stream_create_hash(struct ostream *output, const struct hash_method *method, void *hash_context) { struct hash_ostream *hstream; hstream = i_new(struct hash_ostream, 1); hstream->ostream.sendv = o_stream_hash_sendv; hstream->method = method; hstream->hash_context = hash_context; return o_stream_create(&hstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib/file-create-locked.c0000644000175000017500000001115113147010712015440 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "safe-mkstemp.h" #include "mkdir-parents.h" #include "file-lock.h" #include "file-create-locked.h" #include #include #include /* Try mkdir() + lock creation multiple times. This allows the lock file creation to work even while the directory is simultaneously being rmdir()ed. */ #define MAX_MKDIR_COUNT 10 #define MAX_RETRY_COUNT 1000 static int try_lock_existing(int fd, const char *path, const struct file_create_settings *set, struct file_lock **lock_r, const char **error_r) { struct stat st1, st2; int ret; if (fstat(fd, &st1) < 0) { *error_r = t_strdup_printf("fstat(%s) failed: %m", path); return -1; } if (file_wait_lock_error(fd, path, F_WRLCK, set->lock_method, set->lock_timeout_secs, lock_r, error_r) <= 0) return -1; if (stat(path, &st2) == 0) { ret = st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev) ? 1 : 0; } else if (errno == ENOENT) { ret = 0; } else { *error_r = t_strdup_printf("stat(%s) failed: %m", path); ret = -1; } if (ret <= 0) { /* the fd is closed next - no need to unlock */ file_lock_free(lock_r); } return ret; } static int try_mkdir(const char *path, const struct file_create_settings *set, const char **error_r) { uid_t uid = set->mkdir_uid != 0 ? set->mkdir_uid : (uid_t)-1; gid_t gid = set->mkdir_gid != 0 ? set->mkdir_gid : (gid_t)-1; const char *p = strrchr(path, '/'); if (p == NULL) return 0; const char *dir = t_strdup_until(path, p); int ret; if (uid != (uid_t)-1) ret = mkdir_parents_chown(dir, set->mkdir_mode, uid, gid); else { ret = mkdir_parents_chgrp(dir, set->mkdir_mode, gid, set->gid_origin); } if (ret < 0 && errno != EEXIST) { *error_r = t_strdup_printf("mkdir_parents(%s) failed: %m", dir); return -1; } return 1; } static int try_create_new(const char *path, const struct file_create_settings *set, int *fd_r, struct file_lock **lock_r, const char **error_r) { string_t *temp_path = t_str_new(128); int fd, orig_errno, ret = 1; int mode = set->mode != 0 ? set->mode : 0600; uid_t uid = set->uid != 0 ? set->uid : (uid_t)-1; uid_t gid = set->gid != 0 ? set->gid : (gid_t)-1; str_append(temp_path, path); for (unsigned int i = 0; ret > 0; i++) { if (uid != (uid_t)-1) fd = safe_mkstemp(temp_path, mode, uid, gid); else fd = safe_mkstemp_group(temp_path, mode, gid, set->gid_origin); if (fd != -1 || errno != ENOENT || set->mkdir_mode == 0 || i >= MAX_MKDIR_COUNT) break; int orig_errno = errno; if ((ret = try_mkdir(path, set, error_r)) < 0) return -1; errno = orig_errno; } if (fd == -1) { *error_r = t_strdup_printf("safe_mkstemp(%s) failed: %m", path); return -1; } ret = -1; if (file_try_lock_error(fd, str_c(temp_path), F_WRLCK, set->lock_method, lock_r, error_r) <= 0) { } else if (link(str_c(temp_path), path) < 0) { if (errno == EEXIST) { /* just created by somebody else */ ret = 0; } else if (errno == ENOENT) { /* nobody should be deleting the temp file unless the entire directory is deleted. */ *error_r = t_strdup_printf( "Temporary file %s was unexpectedly deleted", str_c(temp_path)); } else { *error_r = t_strdup_printf("link(%s, %s) failed: %m", str_c(temp_path), path); } file_lock_free(lock_r); } else { file_lock_set_path(*lock_r, path); i_unlink_if_exists(str_c(temp_path)); *fd_r = fd; return 1; } orig_errno = errno; i_close_fd(&fd); i_unlink_if_exists(str_c(temp_path)); errno = orig_errno; return ret; } int file_create_locked(const char *path, const struct file_create_settings *set, struct file_lock **lock_r, bool *created_r, const char **error_r) { unsigned int i; int fd, ret; for (i = 0; i < MAX_RETRY_COUNT; i++) { fd = open(path, O_RDWR); if (fd != -1) { ret = try_lock_existing(fd, path, set, lock_r, error_r); if (ret > 0) { /* successfully locked an existing file */ *created_r = FALSE; return fd; } i_close_fd(&fd); if (ret < 0) return -1; } else if (errno != ENOENT) { *error_r = t_strdup_printf("open(%s) failed: %m", path); return -1; } else { /* try to create the file */ ret = try_create_new(path, set, &fd, lock_r, error_r); if (ret < 0) return -1; if (ret > 0) { /* successfully created a new locked file */ *created_r = TRUE; return fd; } /* the file was just created - try again opening and locking it */ } } *error_r = t_strdup_printf("Creating a locked file %s keeps failing", path); errno = EINVAL; return -1; } dovecot-2.2.33.2/src/lib/istream-base64.h0000644000175000017500000000037513123174404014565 00000000000000#ifndef ISTREAM_BASE64_H #define ISTREAM_BASE64_H struct istream * i_stream_create_base64_encoder(struct istream *input, unsigned int chars_per_line, bool crlf); struct istream * i_stream_create_base64_decoder(struct istream *input); #endif dovecot-2.2.33.2/src/lib/istream-data.c0000644000175000017500000000264713123174404014411 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" static ssize_t i_stream_data_read(struct istream_private *stream) { stream->istream.eof = TRUE; return -1; } static void i_stream_data_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { stream->skip = v_offset; stream->istream.v_offset = v_offset; } struct istream *i_stream_create_from_data(const void *data, size_t size) { struct istream_private *stream; stream = i_new(struct istream_private, 1); stream->buffer = data; stream->pos = size; stream->max_buffer_size = (size_t)-1; stream->read = i_stream_data_read; stream->seek = i_stream_data_seek; stream->istream.readable_fd = FALSE; stream->istream.blocking = TRUE; stream->istream.seekable = TRUE; i_stream_create(stream, NULL, -1); stream->statbuf.st_size = size; i_stream_set_name(&stream->istream, "(buffer)"); return &stream->istream; } static void i_stream_copied_data_free(void *data) { i_free(data); } struct istream * i_stream_create_copy_from_data(const void *data, size_t size) { struct istream *stream; void *buffer; if (size == 0) { buffer = ""; } else { buffer = i_malloc(size); memcpy(buffer, data, size); } stream = i_stream_create_from_data(buffer, size); if (size > 0) { i_stream_add_destroy_callback (stream, i_stream_copied_data_free, buffer); } return stream; } dovecot-2.2.33.2/src/lib/buffer.c0000644000175000017500000002276213165463624013322 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "buffer.h" struct real_buffer { /* public: */ const unsigned char *r_buffer; size_t used; /* private: */ unsigned char *w_buffer; size_t dirty, alloc; pool_t pool; unsigned int alloced:1; unsigned int dynamic:1; }; typedef int buffer_check_sizes[COMPILE_ERROR_IF_TRUE(sizeof(struct real_buffer) > sizeof(buffer_t)) ?1:1]; static void buffer_alloc(struct real_buffer *buf, size_t size) { i_assert(buf->w_buffer == NULL || buf->alloced); if (size == buf->alloc) return; i_assert(size > buf->alloc); if (buf->w_buffer == NULL) buf->w_buffer = p_malloc(buf->pool, size); else buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, size); buf->alloc = size; buf->r_buffer = buf->w_buffer; buf->alloced = TRUE; } static inline void buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size) { unsigned int extra; size_t new_size; if (unlikely((size_t)-1 - pos < data_size)) { i_panic("Buffer write out of range (%"PRIuSIZE_T " + %"PRIuSIZE_T")", pos, data_size); } new_size = pos + data_size; if (new_size > buf->used && buf->used < buf->dirty) { /* clear used..dirty area */ size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size); memset(buf->w_buffer + buf->used, 0, max - buf->used); } /* always keep +1 byte allocated available in case str_c() is called for this buffer. this is mainly for cases where the buffer is allocated from data stack, and str_c() is called in a separate stack frame. */ extra = buf->dynamic ? 1 : 0; if (new_size + extra > buf->alloc) { if (unlikely(!buf->dynamic)) { i_panic("Buffer full (%"PRIuSIZE_T" > %"PRIuSIZE_T", " "pool %s)", pos + data_size, buf->alloc, buf->pool == NULL ? "" : pool_get_name(buf->pool)); } buffer_alloc(buf, pool_get_exp_grown_size(buf->pool, buf->alloc, new_size + extra)); } #if 0 else if (new_size > buf->used && buf->alloced && !buf->pool->alloconly_pool && !buf->pool->datastack_pool) { void *new_buf; /* buffer's size increased: move the buffer's memory elsewhere. this should help catch bugs where old pointers are tried to be used to access the buffer's memory */ new_buf = p_malloc(buf->pool, buf->alloc); memcpy(new_buf, buf->w_buffer, buf->alloc); p_free(buf->pool, buf->w_buffer); buf->w_buffer = new_buf; buf->r_buffer = new_buf; } #endif if (new_size > buf->used) buf->used = new_size; i_assert(buf->used <= buf->alloc); } #undef buffer_create_from_data void buffer_create_from_data(buffer_t *buffer, void *data, size_t size) { struct real_buffer *buf; i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); buf = (struct real_buffer *)buffer; i_zero(buf); buf->alloc = size; buf->r_buffer = buf->w_buffer = data; /* clear the whole memory area. unnecessary usually, but if the buffer is used by e.g. str_c() it tries to access uninitialized memory */ memset(data, 0, size); } #undef buffer_create_from_const_data void buffer_create_from_const_data(buffer_t *buffer, const void *data, size_t size) { struct real_buffer *buf; i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); buf = (struct real_buffer *)buffer; i_zero(buf); buf->used = buf->alloc = size; buf->r_buffer = data; i_assert(buf->w_buffer == NULL); } buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size) { struct real_buffer *buf; buf = p_new(pool, struct real_buffer, 1); buf->pool = pool; buf->dynamic = TRUE; /* buffer_alloc() reserves +1 for str_c() NIL, so add +1 here to init_size so we can actually write that much to the buffer without realloc */ buffer_alloc(buf, init_size+1); return (buffer_t *)buf; } void buffer_free(buffer_t **_buf) { struct real_buffer *buf = (struct real_buffer *)*_buf; *_buf = NULL; if (buf->alloced) p_free(buf->pool, buf->w_buffer); if (buf->pool != NULL) p_free(buf->pool, buf); } void *buffer_free_without_data(buffer_t **_buf) { struct real_buffer *buf = (struct real_buffer *)*_buf; void *data; *_buf = NULL; data = buf->w_buffer; p_free(buf->pool, buf); return data; } pool_t buffer_get_pool(const buffer_t *_buf) { const struct real_buffer *buf = (const struct real_buffer *)_buf; return buf->pool; } void buffer_reset(buffer_t *buf) { buffer_set_used_size(buf, 0); } void buffer_write(buffer_t *_buf, size_t pos, const void *data, size_t data_size) { struct real_buffer *buf = (struct real_buffer *)_buf; buffer_check_limits(buf, pos, data_size); if (data_size > 0) memcpy(buf->w_buffer + pos, data, data_size); } void buffer_append(buffer_t *buf, const void *data, size_t data_size) { buffer_write(buf, buf->used, data, data_size); } void buffer_append_c(buffer_t *buf, unsigned char chr) { buffer_append(buf, &chr, 1); } void buffer_insert(buffer_t *_buf, size_t pos, const void *data, size_t data_size) { struct real_buffer *buf = (struct real_buffer *)_buf; if (pos >= buf->used) buffer_write(_buf, pos, data, data_size); else { buffer_copy(_buf, pos + data_size, _buf, pos, (size_t)-1); memcpy(buf->w_buffer + pos, data, data_size); } } void buffer_delete(buffer_t *_buf, size_t pos, size_t size) { struct real_buffer *buf = (struct real_buffer *)_buf; size_t end_size; if (pos >= buf->used) return; end_size = buf->used - pos; if (size < end_size) { /* delete from between */ end_size -= size; memmove(buf->w_buffer + pos, buf->w_buffer + pos + size, end_size); } else { /* delete the rest of the buffer */ end_size = 0; } buffer_set_used_size(_buf, pos + end_size); } void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size) { struct real_buffer *buf = (struct real_buffer *)_buf; buffer_check_limits(buf, pos, data_size); memset(buf->w_buffer + pos, 0, data_size); } void buffer_append_zero(buffer_t *buf, size_t data_size) { buffer_write_zero(buf, buf->used, data_size); } void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size) { struct real_buffer *buf = (struct real_buffer *)_buf; if (pos >= buf->used) buffer_write_zero(_buf, pos, data_size); else { buffer_copy(_buf, pos + data_size, _buf, pos, (size_t)-1); memset(buf->w_buffer + pos, 0, data_size); } } void buffer_copy(buffer_t *_dest, size_t dest_pos, const buffer_t *_src, size_t src_pos, size_t copy_size) { struct real_buffer *dest = (struct real_buffer *)_dest; const struct real_buffer *src = (const struct real_buffer *)_src; size_t max_size; i_assert(src_pos <= src->used); max_size = src->used - src_pos; if (copy_size > max_size) copy_size = max_size; buffer_check_limits(dest, dest_pos, copy_size); if (src == dest) { memmove(dest->w_buffer + dest_pos, src->r_buffer + src_pos, copy_size); } else { memcpy(dest->w_buffer + dest_pos, src->r_buffer + src_pos, copy_size); } } void buffer_append_buf(buffer_t *dest, const buffer_t *src, size_t src_pos, size_t copy_size) { buffer_copy(dest, dest->used, src, src_pos, copy_size); } void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size) { struct real_buffer *buf = (struct real_buffer *)_buf; buffer_check_limits(buf, pos, size); return buf->w_buffer + pos; } void *buffer_append_space_unsafe(buffer_t *buf, size_t size) { return buffer_get_space_unsafe(buf, buf->used, size); } void *buffer_get_modifiable_data(const buffer_t *_buf, size_t *used_size_r) { const struct real_buffer *buf = (const struct real_buffer *)_buf; if (used_size_r != NULL) *used_size_r = buf->used; return buf->w_buffer; } void buffer_set_used_size(buffer_t *_buf, size_t used_size) { struct real_buffer *buf = (struct real_buffer *)_buf; i_assert(used_size <= buf->alloc); if (buf->used > buf->dirty) buf->dirty = buf->used; buf->used = used_size; } size_t buffer_get_size(const buffer_t *_buf) { const struct real_buffer *buf = (const struct real_buffer *)_buf; return buf->alloc; } size_t buffer_get_writable_size(const buffer_t *_buf) { const struct real_buffer *buf = (const struct real_buffer *)_buf; if (!buf->dynamic || buf->alloc == 0) return buf->alloc; /* we reserve +1 for str_c() NUL in buffer_check_limits(), so don't include that in our return value. otherwise the caller might increase the buffer's alloc size unnecessarily when it just wants to access the entire buffer. */ return buf->alloc-1; } bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2) { if (buf1->used != buf2->used) return FALSE; return memcmp(buf1->data, buf2->data, buf1->used) == 0; } void buffer_verify_pool(buffer_t *_buf) { const struct real_buffer *buf = (const struct real_buffer *)_buf; void *ret; if (buf->pool != NULL && buf->pool->datastack_pool && buf->alloc > 0) { /* this doesn't really do anything except verify the stack frame */ ret = p_realloc(buf->pool, buf->w_buffer, buf->alloc, buf->alloc); i_assert(ret == buf->w_buffer); } } void buffer_truncate_rshift_bits(buffer_t *buf, size_t bits) { /* no-op if it's shorten than bits in any case.. */ if (buf->used * 8 < bits) return; if (bits > 0) { /* truncate it to closest byte boundary */ size_t bytes = ((bits + 7) & -8U)/8; /* remainding bits */ bits = bits % 8; buffer_set_used_size(buf, I_MIN(bytes, buf->used)); unsigned char *ptr = buffer_get_modifiable_data(buf, &bytes); /* right shift over byte array */ if (bits > 0) { for(size_t i=bytes-1;i>0;i--) ptr[i] = (ptr[i]>>(8-bits)) + ((ptr[i-1]&(0xff>>(bits)))<>(8-bits); } } else { buffer_set_used_size(buf, 0); } } dovecot-2.2.33.2/src/lib/sha3.c0000644000175000017500000002113113165463624012674 00000000000000/* ------------------------------------------------------------------------- * Works when compiled for either 32-bit or 64-bit targets, optimized for * 64 bit. * * Canonical implementation of Init/Update/Finalize for SHA-3 byte input. * * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added. * * Based on code from http://keccak.noekeon.org/ . * * I place the code that I wrote into public domain, free to use. * * I would appreciate if you give credits to this work if you used it to * write or test * your code. * * Aug 2015. Andrey Jivsov. crypto@brainhub.org * * Modified for Dovecot oy use * Oct 2016. Aki Tuomi * ---------------------------------------------------------------------- */ #include "lib.h" #include "sha3.h" #include #include #include #if defined(_MSC_VER) #define SHA3_CONST(x) x #else #define SHA3_CONST(x) x##L #endif /* The following state definition should normally be in a separate * header file */ #ifndef SHA3_ROTL64 #define SHA3_ROTL64(x, y) \ (((x) << (y)) | ((x) >> ((sizeof(uint64_t)*8) - (y)))) #endif static const uint64_t keccakf_rndc[24] = { SHA3_CONST(0x0000000000000001UL), SHA3_CONST(0x0000000000008082UL), SHA3_CONST(0x800000000000808aUL), SHA3_CONST(0x8000000080008000UL), SHA3_CONST(0x000000000000808bUL), SHA3_CONST(0x0000000080000001UL), SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008009UL), SHA3_CONST(0x000000000000008aUL), SHA3_CONST(0x0000000000000088UL), SHA3_CONST(0x0000000080008009UL), SHA3_CONST(0x000000008000000aUL), SHA3_CONST(0x000000008000808bUL), SHA3_CONST(0x800000000000008bUL), SHA3_CONST(0x8000000000008089UL), SHA3_CONST(0x8000000000008003UL), SHA3_CONST(0x8000000000008002UL), SHA3_CONST(0x8000000000000080UL), SHA3_CONST(0x000000000000800aUL), SHA3_CONST(0x800000008000000aUL), SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008080UL), SHA3_CONST(0x0000000080000001UL), SHA3_CONST(0x8000000080008008UL) }; static const unsigned keccakf_rotc[24] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 }; static const unsigned keccakf_piln[24] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 }; /* generally called after SHA3_KECCAK_SPONGE_WORDS-ctx->capacityWords words * are XORed into the state s */ static void keccakf(uint64_t s[25]) { int i, j, round; uint64_t t, bc[5]; #define KECCAK_ROUNDS 24 for(round = 0; round < KECCAK_ROUNDS; round++) { /* Theta */ for(i = 0; i < 5; i++) bc[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]; for(i = 0; i < 5; i++) { t = bc[(i + 4) % 5] ^ SHA3_ROTL64(bc[(i + 1) % 5], 1); for(j = 0; j < 25; j += 5) s[j + i] ^= t; } /* Rho Pi */ t = s[1]; for(i = 0; i < 24; i++) { j = keccakf_piln[i]; bc[0] = s[j]; s[j] = SHA3_ROTL64(t, keccakf_rotc[i]); t = bc[0]; } /* Chi */ for(j = 0; j < 25; j += 5) { for(i = 0; i < 5; i++) bc[i] = s[j + i]; for(i = 0; i < 5; i++) s[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; } /* Iota */ s[0] ^= keccakf_rndc[round]; } } /* *************************** Public Inteface ************************ */ void sha3_256_init(void *context) { struct sha3_ctx *ctx = context; i_zero(ctx); ctx->capacityWords = 2 * 256 / (8 * sizeof(uint64_t)); } void sha3_512_init(void *context) { struct sha3_ctx *ctx = context; i_zero(ctx); ctx->capacityWords = 2 * 512 / (8 * sizeof(uint64_t)); } void sha3_loop(void *context, const void *data, size_t len) { struct sha3_ctx *ctx = context; /* 0...7 -- how much is needed to have a word */ unsigned old_tail = (8 - ctx->byteIndex) & 7; size_t words; unsigned tail; size_t i; const uint8_t *buf = data; i_assert(ctx->byteIndex < 8); i_assert(ctx->wordIndex < sizeof(ctx->s) / sizeof(ctx->s[0])); if(len < old_tail) { /* have no complete word or haven't started * the word yet */ /* endian-independent code follows: */ while (len > 0) { len--; ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8); } i_assert(ctx->byteIndex < 8); return; } if(old_tail != 0) { /* will have one word to process */ /* endian-independent code follows: */ len -= old_tail; while (old_tail > 0) { old_tail--; ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8); } /* now ready to add saved to the sponge */ ctx->s[ctx->wordIndex] ^= ctx->saved; i_assert(ctx->byteIndex == 8); ctx->byteIndex = 0; ctx->saved = 0; if(++ctx->wordIndex == (SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords)) { keccakf(ctx->s); ctx->wordIndex = 0; } } /* now work in full words directly from input */ i_assert(ctx->byteIndex == 0); words = len / sizeof(uint64_t); tail = len - words * sizeof(uint64_t); for(i = 0; i < words; i++, buf += sizeof(uint64_t)) { const uint64_t t = (uint64_t) (buf[0]) | ((uint64_t) (buf[1]) << 8 * 1) | ((uint64_t) (buf[2]) << 8 * 2) | ((uint64_t) (buf[3]) << 8 * 3) | ((uint64_t) (buf[4]) << 8 * 4) | ((uint64_t) (buf[5]) << 8 * 5) | ((uint64_t) (buf[6]) << 8 * 6) | ((uint64_t) (buf[7]) << 8 * 7); #if defined(__x86_64__ ) || defined(__i386__) i_assert(memcmp(&t, buf, 8) == 0); #endif ctx->s[ctx->wordIndex] ^= t; if(++ctx->wordIndex == (SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords)) { keccakf(ctx->s); ctx->wordIndex = 0; } } /* finally, save the partial word */ i_assert(ctx->byteIndex == 0 && tail < 8); while (tail > 0) { tail--; ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8); } i_assert(ctx->byteIndex < 8); } /* This is simply the 'update' with the padding block. * The padding block is 0x01 || 0x00* || 0x80. First 0x01 and last 0x80 * bytes are always present, but they can be the same byte. */ static void sha3_finalize(struct sha3_ctx *ctx) { /* Append 2-bit suffix 01, per SHA-3 spec. Instead of 1 for padding we * use 1<<2 below. The 0x02 below corresponds to the suffix 01. * Overall, we feed 0, then 1, and finally 1 to start padding. Without * M || 01, we would simply use 1 to start padding. */ /* SHA3 version */ ctx->s[ctx->wordIndex] ^= (ctx->saved ^ ((uint64_t) ((uint64_t) (0x02 | (1 << 2)) << ((ctx->byteIndex) * 8)))); ctx->s[SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords - 1] ^= SHA3_CONST(0x8000000000000000UL); keccakf(ctx->s); #ifdef WORDS_BIGENDIAN { unsigned i; for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) { const unsigned t1 = (uint32_t) ctx->s[i]; const unsigned t2 = (uint32_t) ((ctx->s[i] >> 16) >> 16); ctx->sb[i * 8 + 0] = (uint8_t) (t1); ctx->sb[i * 8 + 1] = (uint8_t) (t1 >> 8); ctx->sb[i * 8 + 2] = (uint8_t) (t1 >> 16); ctx->sb[i * 8 + 3] = (uint8_t) (t1 >> 24); ctx->sb[i * 8 + 4] = (uint8_t) (t2); ctx->sb[i * 8 + 5] = (uint8_t) (t2 >> 8); ctx->sb[i * 8 + 6] = (uint8_t) (t2 >> 16); ctx->sb[i * 8 + 7] = (uint8_t) (t2 >> 24); } } #endif } void sha3_256_result(void *context, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) { struct sha3_ctx *ctx = context; sha3_finalize(ctx); memcpy(digest, ctx->sb, SHA256_RESULTLEN); } void sha3_512_result(void *context, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) { struct sha3_ctx *ctx = context; sha3_finalize(ctx); memcpy(digest, ctx->sb, SHA512_RESULTLEN); } void sha3_256_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) { struct sha3_ctx ctx; sha3_256_init(&ctx); sha3_loop(&ctx, data, size); sha3_256_result(&ctx, digest); } void sha3_512_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) { struct sha3_ctx ctx; sha3_512_init(&ctx); sha3_loop(&ctx, data, size); sha3_512_result(&ctx, digest); } static void hash_method_init_sha3_256(void *context) { sha3_256_init(context); } static void hash_method_loop_sha3(void *context, const void *data, size_t size) { sha3_loop(context, data, size); } static void hash_method_result_sha3_256(void *context, unsigned char *result_r) { sha3_256_result(context, result_r); } const struct hash_method hash_method_sha3_256 = { "sha3-256", sizeof(struct sha3_ctx), SHA256_RESULTLEN, hash_method_init_sha3_256, hash_method_loop_sha3, hash_method_result_sha3_256 }; static void hash_method_init_sha3_512(void *context) { sha3_512_init(context); } static void hash_method_result_sha3_512(void *context, unsigned char *result_r) { sha3_512_result(context, result_r); } const struct hash_method hash_method_sha3_512 = { "sha3-512", sizeof(struct sha3_ctx), SHA512_RESULTLEN, hash_method_init_sha3_512, hash_method_loop_sha3, hash_method_result_sha3_512 }; dovecot-2.2.33.2/src/lib/ostream-file-private.h0000644000175000017500000000225113165463624016104 00000000000000#ifndef OSTREAM_FILE_PRIVATE_H #define OSTREAM_FILE_PRIVATE_H #include "ostream-private.h" struct file_ostream { struct ostream_private ostream; ssize_t (*writev)(struct file_ostream *fstream, const struct const_iovec *iov, unsigned int iov_count); int fd; struct io *io; uoff_t buffer_offset; uoff_t real_offset; unsigned char *buffer; /* ring-buffer */ size_t buffer_size, optimal_block_size; size_t head, tail; /* first unsent/unused byte */ unsigned int full:1; /* if head == tail, is buffer empty or full? */ unsigned int file:1; unsigned int flush_pending:1; unsigned int socket_cork_set:1; unsigned int no_socket_cork:1; unsigned int no_sendfile:1; unsigned int autoclose_fd:1; }; struct ostream * o_stream_create_file_common(struct file_ostream *fstream, int fd, size_t max_buffer_size, bool autoclose_fd); ssize_t o_stream_file_writev(struct file_ostream *fstream, const struct const_iovec *iov, unsigned int iov_size); ssize_t o_stream_file_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count); void o_stream_file_close(struct iostream_private *stream, bool close_parent); #endif dovecot-2.2.33.2/src/lib/ipwd.h0000644000175000017500000000116013123174404012773 00000000000000#ifndef IPWD_H #define IPWD_H #include #include /* Replacements for standard getpw/gr*(), fixing their ability to report errors properly. As with standard getpw/gr*(), second call overwrites data used by the first one. Functions return 1 if user/group is found, 0 if not or -1 if error (with errno set). */ int i_getpwnam(const char *name, struct passwd *pwd_r); int i_getpwuid(uid_t uid, struct passwd *pwd_r); int i_getgrnam(const char *name, struct group *grp_r); int i_getgrgid(gid_t gid, struct group *grp_r); /* Free memory used by above functions. */ void ipwd_deinit(void); #endif dovecot-2.2.33.2/src/lib/json-tree.h0000644000175000017500000000231413165463624013753 00000000000000#ifndef JSON_TREE_H #define JSON_TREE_H #include "json-parser.h" struct json_tree_node { /* object key, or NULL if we're in a list */ const char *key; struct json_tree_node *parent, *next; enum json_type value_type; struct { /* for JSON_TYPE_OBJECT and JSON_TYPE_ARRAY */ struct json_tree_node *child; /* for other types */ const char *str; } value; }; struct json_tree *json_tree_init(void); void json_tree_deinit(struct json_tree **tree); /* Append data to a tree. The type/value should normally come from json-parser. Returns 0 on success, -1 if the input was invalid (which should never happen if it's coming from json-parser). */ int json_tree_append(struct json_tree *tree, enum json_type type, const char *value); /* Return the root node. */ struct json_tree_node *json_tree_root(struct json_tree *tree); /* Find a node with the specified key (from node's siblings) */ struct json_tree_node * json_tree_find_key(struct json_tree_node *node, const char *key); /* Find an object node (from an array), which contains the specified key=value in its values. */ struct json_tree_node * json_tree_find_child_with(struct json_tree_node *node, const char *key, const char *value); #endif dovecot-2.2.33.2/src/lib/sha3.h0000644000175000017500000000600513123174404012671 00000000000000/* * FIPS 180-2 SHA-224/256/384/512 implementation * Last update: 02/02/2007 * Issue date: 04/30/2005 * * Copyright (C) 2005, 2007 Olivier Gay * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef SHA3_H #define SHA3_H #include "hash-method.h" #include "sha-common.h" #define SHA3_KECCAK_SPONGE_WORDS \ (((1600)/8/*bits to byte*/)/sizeof(uint64_t)) struct sha3_ctx { uint64_t saved; /* the portion of the input message that we * didn't consume yet */ union { /* Keccak's state */ uint64_t s[SHA3_KECCAK_SPONGE_WORDS]; uint8_t sb[SHA3_KECCAK_SPONGE_WORDS * 8]; }; unsigned byteIndex; /* 0..7--the next byte after the set one * (starts from 0; 0--none are buffered) */ unsigned wordIndex; /* 0..24--the next word to integrate input * (starts from 0) */ unsigned capacityWords; /* the double size of the hash output in * words (e.g. 16 for Keccak 512) */ }; void sha3_256_init(void *context); void sha3_256_result(void *context, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); void sha3_256_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); void sha3_512_init(void *context); void sha3_512_result(void *context, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); void sha3_512_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); void sha3_loop(void *context, const void *data, size_t len); extern const struct hash_method hash_method_sha3_256; extern const struct hash_method hash_method_sha3_512; #endif dovecot-2.2.33.2/src/lib/test-istream-base64-decoder.c0000644000175000017500000000374413165463624017156 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-base64.h" struct { const char *input; const char *output; } tests[] = { { "aGVsbG8gd29ybGQ=", "hello world" }, { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world" }, { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n", "hello world" }, }; static void decode_test(const char *base64_input, const char *output, bool broken_input) { unsigned int base64_input_len = strlen(base64_input); struct istream *input_data, *input; const unsigned char *data; size_t i, size; int ret = 0; input_data = test_istream_create_data(base64_input, base64_input_len); test_istream_set_allow_eof(input_data, FALSE); input = i_stream_create_base64_decoder(input_data); for (i = 1; i <= base64_input_len; i++) { test_istream_set_size(input_data, i); while ((ret = i_stream_read(input)) > 0) ; if (ret == -1 && broken_input) break; test_assert(ret == 0); } if (ret == 0) { test_istream_set_allow_eof(input_data, TRUE); while ((ret = i_stream_read(input)) > 0) ; } test_assert(ret == -1); test_assert((input->stream_errno == 0 && !broken_input) || (input->stream_errno == EINVAL && broken_input)); data = i_stream_get_data(input, &size); test_assert(size == strlen(output)); if (size > 0) test_assert(memcmp(data, output, size) == 0); i_stream_unref(&input); i_stream_unref(&input_data); } void test_istream_base64_decoder(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(tests); i++) { test_begin(t_strdup_printf("istream base64 decoder %u", i+1)); decode_test(tests[i].input, tests[i].output, FALSE); test_end(); } test_begin("istream base64 decoder error"); decode_test("foo", "", TRUE); decode_test("Zm9vC", "foo", TRUE); decode_test("Zm9v!", "foo", TRUE); decode_test("Zm9!v", "", TRUE); decode_test("Zm9 v", "", TRUE); decode_test("Zm 9v", "", TRUE); decode_test("Z m9v", "", TRUE); test_end(); } dovecot-2.2.33.2/src/lib/compat.c0000644000175000017500000001226213123174404013313 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "config.h" #undef HAVE_CONFIG_H /* Linux needs the _XOPEN_SOURCE define, but others don't. It needs to be defined before unistd.h, so we need the above config.h include hack.. */ #ifdef PREAD_WRAPPERS # define _XOPEN_SOURCE 500 /* Linux */ #endif #define IN_COMPAT_C #include "lib.h" #include #include #include #include #include #include #ifndef INADDR_NONE # define INADDR_NONE INADDR_BROADCAST #endif #if !defined (HAVE_STRCASECMP) && !defined (HAVE_STRICMP) int i_my_strcasecmp(const char *s1, const char *s2) { while (*s1 != '\0' && i_toupper(*s1) == i_toupper(*s2)) { s1++; s2++; } return i_toupper(*s1) - i_toupper(*s2); } int i_my_strncasecmp(const char *s1, const char *s2, size_t max_chars) { while (max_chars > 1 && *s1 != '\0' && i_toupper(*s1) == i_toupper(*s2)) { s1++; s2++; max_chars--; } return i_toupper(*s1) - i_toupper(*s2); } #endif #ifndef HAVE_INET_ATON int i_my_inet_aton(const char *cp, struct in_addr *inp) { in_addr_t addr; addr = inet_addr(cp); if (addr == INADDR_NONE) return 0; inp->s_addr = addr; return 1; } #endif #ifndef HAVE_VSYSLOG void i_my_vsyslog(int priority, const char *format, va_list args) { T_BEGIN { syslog(priority, "%s", t_strdup_vprintf(format, args)); } T_END; } #endif #ifndef HAVE_GETPAGESIZE int i_my_getpagesize(void) { #ifdef _SC_PAGESIZE return sysconf(_SC_PAGESIZE); #else # ifdef __GNUC__ # warning Guessing page size to be 4096 # endif return 4096; #endif } #endif #ifndef HAVE_WRITEV ssize_t i_my_writev(int fd, const struct iovec *iov, int iov_len) { size_t written; ssize_t ret; int i; written = 0; for (i = 0; i < iov_len; i++, iov++) { ret = write(fd, iov->iov_base, iov->iov_len); if (ret < 0) return -1; written += ret; if ((size_t)ret != iov->iov_len) break; } if (written > SSIZE_T_MAX) { errno = ERANGE; return -1; } return (ssize_t)written; } #endif #if !defined(HAVE_PREAD) || defined(PREAD_BROKEN) ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset) { ssize_t ret; off_t old_offset; old_offset = lseek(fd, 0, SEEK_CUR); if (old_offset == -1) return -1; if (lseek(fd, offset, SEEK_SET) < 0) return -1; ret = read(fd, buf, count); if (ret < 0) return -1; if (lseek(fd, old_offset, SEEK_SET) < 0) return -1; return ret; } ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset) { ssize_t ret; off_t old_offset; old_offset = lseek(fd, 0, SEEK_CUR); if (old_offset == -1) return -1; if (lseek(fd, offset, SEEK_SET) < 0) return -1; ret = write(fd, buf, count); if (ret < 0) return -1; if (lseek(fd, old_offset, SEEK_SET) < 0) return -1; return ret; } #elif defined(PREAD_WRAPPERS) ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset) { ssize_t ret; ret = pread(fd, buf, count, offset); return ret; } ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset) { return pwrite(fd, buf, count, offset); } #endif #ifndef HAVE_SETEUID int i_my_seteuid(uid_t euid) { #ifdef HAVE_SETREUID /* HP-UX at least doesn't have seteuid() but has setreuid() */ return setreuid(-1, euid); #else # error Missing seteuid functionality #endif } #endif #ifndef HAVE_SETEGID int i_my_setegid(gid_t egid) { #ifdef HAVE_SETRESGID /* HP-UX at least doesn't have setegid() but has setresgid() */ return setresgid(-1, egid, -1); #else # error Missing setegid functionality #endif } #endif #ifndef HAVE_LIBGEN_H char *i_my_basename(char *path) { char *p; /* note that this isn't POSIX-compliant basename() replacement. too much trouble without any gain. */ p = strrchr(path, '/'); return p == NULL ? path : p + 1; } #endif #ifdef HAVE_OLD_VSNPRINTF #undef vsnprintf int i_my_vsnprintf(char *str, size_t size, const char *format, va_list ap) { size_t tmp_size; char *tmp; int ret; /* On overflow HP-UX returns -1, IRIX and Tru64 return size-1. */ ret = vsnprintf(str, size, format, ap); if (ret >= 0 && (size_t)ret+1 != size) return ret; /* see if data stack has enough available space for it */ tmp_size = t_get_bytes_available(); if (tmp_size > size) { tmp = t_buffer_get(tmp_size); ret = vsnprintf(tmp, tmp_size, format, ap); if (ret >= 0 && (size_t)ret+1 != tmp_size) { if (size > 0) { memcpy(str, tmp, size-1); str[size-1] = '\0'; } return ret; } } else { tmp_size = size; } /* try to allocate enough memory to get it to fit. */ do { tmp_size = nearest_power(tmp_size+1); tmp = i_malloc(tmp_size); ret = vsnprintf(tmp, tmp_size, format, ap); if (ret >= 0 && (size_t)ret+1 != tmp_size) { if (size > 0) { memcpy(str, tmp, size-1); str[size-1] = '\0'; } i_free(tmp); return ret; } i_free(tmp); } while (tmp_size < 1024*1024); i_panic("my_vsnprintf(): Output string too big"); } #endif #ifndef HAVE_CLOCK_GETTIME int i_my_clock_gettime(int clk_id, struct timespec *tp) { struct timeval tv; i_assert(clk_id == CLOCK_REALTIME); /* fallback to using microseconds */ if (gettimeofday(&tv, NULL) < 0) return -1; tp->tv_sec = tv.tv_sec; tp->tv_nsec = tv.tv_usec * 1000; return 0; } #endif dovecot-2.2.33.2/src/lib/test-lib.h0000644000175000017500000000434013165463624013571 00000000000000#ifndef TEST_LIB #define TEST_LIB #include "lib.h" #include "test-common.h" void test_aqueue(void); void test_array(void); enum fatal_test_state fatal_array(unsigned int); void test_base32(void); void test_base64(void); void test_bits(void); void test_bsearch_insert_pos(void); void test_buffer(void); void test_byteorder(void); void test_crc32(void); void test_data_stack(void); enum fatal_test_state fatal_data_stack(unsigned int); void test_failures(void); void test_file_create_locked(void); void test_guid(void); void test_hash(void); void test_hash_format(void); void test_hash_method(void); void test_hex_binary(void); void test_imem(void); void test_ioloop(void); void test_iso8601_date(void); void test_iostream_temp(void); void test_istream(void); void test_istream_base64_decoder(void); void test_istream_base64_encoder(void); void test_istream_chain(void); void test_istream_concat(void); void test_istream_crlf(void); void test_istream_failure_at(void); void test_istream_multiplex(void); void test_istream_seekable(void); void test_istream_tee(void); void test_istream_unix(void); void test_json_parser(void); void test_json_tree(void); void test_llist(void); void test_log_throttle(void); void test_malloc_overflow(void); enum fatal_test_state fatal_malloc_overflow(unsigned int); void test_mempool(void); enum fatal_test_state fatal_mempool(unsigned int); void test_mempool_alloconly(void); enum fatal_test_state fatal_mempool_alloconly(unsigned int); void test_multiplex(void); void test_pkcs5_pbkdf2(void); void test_net(void); void test_numpack(void); void test_ostream_buffer(void); void test_ostream_escaped(void); void test_ostream_failure_at(void); void test_ostream_file(void); void test_ostream_multiplex(void); void test_primes(void); void test_printf_format_fix(void); enum fatal_test_state fatal_printf_format_fix(unsigned int); void test_priorityq(void); void test_seq_range_array(void); void test_str(void); void test_strescape(void); void test_strfuncs(void); void test_strnum(void); void test_str_find(void); void test_str_sanitize(void); void test_str_table(void); void test_time_util(void); void test_timing(void); void test_unichar(void); void test_utc_mktime(void); void test_var_expand(void); void test_wildcard_match(void); #endif dovecot-2.2.33.2/src/lib/test-json-parser.c0000644000175000017500000001730213165463624015263 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "json-parser.h" #define TYPE_SKIP 100 #define TYPE_STREAM 101 static const char json_input[] = "{\n" "\t\"key\"\t:\t\t\"string\"," " \"key2\" : 1234, \n" "\"key3\":true," "\"key4\":false," "\"skip1\": \"jsifjaisfjiasji\"," "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]}," "\"key5\":null," "\"key6\": {}," "\"key7\": {" " \"sub1\":\"value\"" "}," "\"key8\": {" " \"sub2\":-12.456,\n" " \"sub3\":12.456e9,\n" " \"sub4\":0.456e-789" "}," "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"," "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"," "\"key11\": []," "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]" "}\n"; static struct { enum json_type type; const char *value; } json_output[] = { { JSON_TYPE_OBJECT_KEY, "key" }, { JSON_TYPE_STRING, "string" }, { JSON_TYPE_OBJECT_KEY, "key2" }, { JSON_TYPE_NUMBER, "1234" }, { JSON_TYPE_OBJECT_KEY, "key3" }, { JSON_TYPE_TRUE, "true" }, { JSON_TYPE_OBJECT_KEY, "key4" }, { JSON_TYPE_FALSE, "false" }, { JSON_TYPE_OBJECT_KEY, "skip1" }, { TYPE_SKIP, NULL }, { JSON_TYPE_OBJECT_KEY, "skip2" }, { TYPE_SKIP, NULL }, { JSON_TYPE_OBJECT_KEY, "key5" }, { JSON_TYPE_NULL, NULL }, { JSON_TYPE_OBJECT_KEY, "key6" }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key7" }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "sub1" }, { JSON_TYPE_STRING, "value" }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key8" }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "sub2" }, { JSON_TYPE_NUMBER, "-12.456" }, { JSON_TYPE_OBJECT_KEY, "sub3" }, { JSON_TYPE_NUMBER, "12.456e9" }, { JSON_TYPE_OBJECT_KEY, "sub4" }, { JSON_TYPE_NUMBER, "0.456e-789" }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key9" }, { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" }, { JSON_TYPE_OBJECT_KEY, "key10" }, { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" }, { JSON_TYPE_OBJECT_KEY, "key11" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key12" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_STRING, "foo" }, { JSON_TYPE_NUMBER, "5.24" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_TRUE, "true" }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "aobj" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_ARRAY_END, NULL } }; static int stream_read_value(struct istream **input, const char **value_r) { const unsigned char *data; size_t size; ssize_t ret; while ((ret = i_stream_read(*input)) > 0) ; if (ret == 0) return 0; i_assert(ret == -1); if ((*input)->stream_errno != 0) return -1; data = i_stream_get_data(*input, &size); *value_r = t_strndup(data, size); i_stream_unref(input); return 1; } static void test_json_parser_success(bool full_size) { struct json_parser *parser; struct istream *input, *jsoninput = NULL; enum json_type type; const char *value, *error; unsigned int i, pos, json_input_len = strlen(json_input); int ret = 0; test_begin(full_size ? "json parser" : "json parser (nonblocking)"); input = test_istream_create_data(json_input, json_input_len); test_istream_set_allow_eof(input, FALSE); parser = json_parser_init(input); i = full_size ? json_input_len : 0; for (pos = 0; i <= json_input_len; i++) { test_istream_set_size(input, i); for (;;) { value = NULL; if (pos < N_ELEMENTS(json_output) && json_output[pos].type == (enum json_type)TYPE_SKIP) { json_parse_skip_next(parser); pos++; continue; } else if (pos == N_ELEMENTS(json_output) || json_output[pos].type != (enum json_type)TYPE_STREAM) { ret = json_parse_next(parser, &type, &value); } else { ret = jsoninput != NULL ? 1 : json_parse_next_stream(parser, &jsoninput); if (ret > 0 && jsoninput != NULL) ret = stream_read_value(&jsoninput, &value); type = TYPE_STREAM; } if (ret <= 0) break; i_assert(pos < N_ELEMENTS(json_output)); test_assert(json_output[pos].type == type); test_assert(null_strcmp(json_output[pos].value, value) == 0); pos++; } test_assert(ret == 0); } test_assert(pos == N_ELEMENTS(json_output)); test_istream_set_allow_eof(input, TRUE); test_assert(json_parse_next(parser, &type, &value) == -1); i_stream_unref(&input); test_assert(json_parser_deinit(&parser, &error) == 0); test_end(); } static void test_json_parser_skip_array(void) { static const char *test_input = "[ 1, {\"foo\": 1 }, 2, \"bar\", 3, 1.234, 4, [], 5, [[]], 6, true ]"; struct json_parser *parser; struct istream *input; enum json_type type; const char *value, *error; int i; test_begin("json parser skip array"); input = test_istream_create_data(test_input, strlen(test_input)); parser = json_parser_init_flags(input, JSON_PARSER_NO_ROOT_OBJECT); test_assert(json_parse_next(parser, &type, &value) > 0 && type == JSON_TYPE_ARRAY); for (i = 1; i <= 6; i++) { test_assert(json_parse_next(parser, &type, &value) > 0 && type == JSON_TYPE_NUMBER && atoi(value) == i); json_parse_skip_next(parser); } test_assert(json_parse_next(parser, &type, &value) > 0 && type == JSON_TYPE_ARRAY_END); test_assert(json_parser_deinit(&parser, &error) == 0); i_stream_unref(&input); test_end(); } static int test_json_parse_input(const char *test_input, enum json_parser_flags flags) { struct json_parser *parser; struct istream *input; enum json_type type; const char *value, *error; int ret = 0; input = test_istream_create_data(test_input, strlen(test_input)); parser = json_parser_init_flags(input, flags); while (json_parse_next(parser, &type, &value) > 0) ret++; if (json_parser_deinit(&parser, &error) < 0) ret = -1; i_stream_unref(&input); return ret; } static void test_json_parser_primitive_values(void) { static struct { const char *str; int ret; } test_inputs[] = { { "\"hello\"", 1 }, { "null", 1 }, { "1234", 1 }, { "1234.1234", 1 }, { "{}", 2 }, { "[]", 2 }, { "true", 1 }, { "false", 1 } }; unsigned int i; test_begin("json_parser (primitives)"); for (i = 0; i < N_ELEMENTS(test_inputs); i++) test_assert_idx(test_json_parse_input(test_inputs[i].str, JSON_PARSER_NO_ROOT_OBJECT) == test_inputs[i].ret, i); test_end(); } static void test_json_parser_errors(void) { static const char *test_inputs[] = { "{", "{:}", "{\"foo\":}", "{\"foo\" []}", "{\"foo\": [1}", "{\"foo\": [1,]}", "{\"foo\": [1,]}", "{\"foo\": 1,}", "{\"foo\": 1.}}", "{\"foo\": 1},{}" }; unsigned int i; test_begin("json parser error handling"); for (i = 0; i < N_ELEMENTS(test_inputs); i++) test_assert_idx(test_json_parse_input(test_inputs[i], 0) < 0, i); test_end(); } static void test_json_append_escaped(void) { string_t *str = t_str_new(32); test_begin("json_append_escaped()"); json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4"); test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\xC3\xA4") == 0); test_end(); } static void test_json_append_escaped_data(void) { static const unsigned char test_input[] = "\b\f\r\n\t\"\\\000\001\002-\xC3\xA4"; string_t *str = t_str_new(32); test_begin("json_append_escaped()"); json_append_escaped_data(str, test_input, sizeof(test_input)-1); test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0000\\u0001\\u0002-\xC3\xA4") == 0); test_end(); } void test_json_parser(void) { test_json_parser_success(TRUE); test_json_parser_success(FALSE); test_json_parser_skip_array(); test_json_parser_primitive_values(); test_json_parser_errors(); test_json_append_escaped(); test_json_append_escaped_data(); } dovecot-2.2.33.2/src/lib/safe-mkdir.h0000644000175000017500000000057313123174404014061 00000000000000#ifndef SAFE_MKDIR_H #define SAFE_MKDIR_H /* Either create a directory or make sure that it already exists with given permissions. If anything fails, the i_fatal() is called. Returns 1 if directory was created, 2 if it already existed with correct permissions, 0 if we changed permissions. */ int safe_mkdir(const char *dir, mode_t mode, uid_t uid, gid_t gid); #endif dovecot-2.2.33.2/src/lib/sendfile-util.c0000644000175000017500000000627513123174404014603 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* kludge a bit to remove _FILE_OFFSET_BITS definition from config.h. It's required to be able to include sys/sendfile.h with Linux. */ #include "config.h" #undef HAVE_CONFIG_H #ifdef HAVE_LINUX_SENDFILE # undef _FILE_OFFSET_BITS #endif #include "lib.h" #include "sendfile-util.h" #ifdef HAVE_LINUX_SENDFILE #include ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) { /* REMEBER: uoff_t and off_t may not be of same size. */ off_t safe_offset; ssize_t ret; if (count == 0) return 0; /* make sure given offset fits into off_t */ if (sizeof(off_t) * CHAR_BIT == 32) { /* 32bit off_t */ if (*offset >= 2147483647L) { errno = EINVAL; return -1; } if (count > 2147483647L - *offset) count = 2147483647L - *offset; } else { /* they're most likely the same size. if not, fix this code later */ i_assert(sizeof(off_t) == sizeof(uoff_t)); if (*offset >= OFF_T_MAX) { errno = EINVAL; return -1; } if (count > OFF_T_MAX - *offset) count = OFF_T_MAX - *offset; } safe_offset = (off_t)*offset; ret = sendfile(out_fd, in_fd, &safe_offset, count); /* ret=0 : trying to read past EOF, errno = EPIPE : remote is gone */ *offset = (uoff_t)safe_offset; return ret; } #elif defined(HAVE_FREEBSD_SENDFILE) #include #include ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) { struct sf_hdtr hdtr; off_t sbytes; int ret; i_assert(count <= SSIZE_T_MAX); if (count == 0) { /* if count=0 is passed to sendfile(), it sends everything from in_fd until EOF. We don't want that. */ return 0; } i_zero(&hdtr); ret = sendfile(in_fd, out_fd, *offset, count, &hdtr, &sbytes, 0); *offset += sbytes; if (ret == 0 || (ret < 0 && errno == EAGAIN && sbytes > 0)) return (ssize_t)sbytes; else { if (errno == ENOTSOCK) { /* out_fd wasn't a socket. behave as if sendfile() wasn't supported at all. */ errno = EINVAL; } return -1; } } #elif defined (HAVE_SOLARIS_SENDFILE) #include #include "net.h" ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) { ssize_t ret; off_t s_offset; i_assert(count <= SSIZE_T_MAX); if (count == 0) return 0; /* NOTE: if outfd is not a socket, some Solaris versions will kernel panic */ s_offset = (off_t)*offset; ret = sendfile(out_fd, in_fd, &s_offset, count); if (ret < 0) { /* if remote is gone, EPIPE is returned */ if (errno == EINVAL) { /* most likely trying to read past EOF */ ret = 0; } else if (errno == EAFNOSUPPORT || errno == EOPNOTSUPP) { /* not supported, return Linux-like EINVAL so caller sees only consistent errnos. */ errno = EINVAL; } else if (s_offset != (off_t)*offset) { /* some data was sent, return it */ i_assert(s_offset > (off_t)*offset); ret = s_offset - (off_t)*offset; } } *offset = (uoff_t)s_offset; i_assert(ret < 0 || (size_t)ret <= count); return ret; } #else ssize_t safe_sendfile(int out_fd ATTR_UNUSED, int in_fd ATTR_UNUSED, uoff_t *offset ATTR_UNUSED, size_t count ATTR_UNUSED) { errno = EINVAL; return -1; } #endif dovecot-2.2.33.2/src/lib/safe-mkdir.c0000644000175000017500000000400213123174404014043 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "safe-mkdir.h" #include #include #include int safe_mkdir(const char *dir, mode_t mode, uid_t uid, gid_t gid) { struct stat st; int fd, ret = 2, changed_ret = 0; if (lstat(dir, &st) < 0) { if (errno != ENOENT) i_fatal("lstat() failed for %s: %m", dir); if (mkdir(dir, mode) < 0) { if (errno != EEXIST) i_fatal("Can't create directory %s: %m", dir); } else { /* created it */ ret = changed_ret = 1; } } /* use fchown() and fchmod() just to make sure we aren't following symbolic links. */ fd = open(dir, O_RDONLY); if (fd == -1) i_fatal("open() failed for %s: %m", dir); if (fstat(fd, &st) < 0) i_fatal("fstat() failed for %s: %m", dir); if (!S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) i_fatal("Not a directory %s", dir); /* change the file owner first, since it's the only user one who can mess up with the file mode. */ if ((st.st_uid != uid && uid != (uid_t)-1) || (st.st_gid != gid && gid != (gid_t)-1)) { if (fchown(fd, uid, gid) < 0) i_fatal("fchown() failed for %s: %m", dir); ret = changed_ret; } if ((st.st_mode & 07777) != mode) { if (fchmod(fd, mode) < 0) i_fatal("chmod() failed for %s: %m", dir); ret = changed_ret; } if (close(fd) < 0) i_fatal("close() failed for %s: %m", dir); /* paranoia: make sure we succeeded in everything. */ if (lstat(dir, &st) < 0) i_fatal("lstat() check failed for %s: %m", dir); if (!S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) i_fatal("Not a directory %s", dir); if ((st.st_mode & 07777) != mode) { i_fatal("safe_mkdir() failed: %s (%o) is still not mode %o", dir, (int)st.st_mode, (int)mode); } if ((st.st_uid != uid && uid != (uid_t)-1) || (st.st_gid != gid && gid != (gid_t)-1)) { i_fatal("safe_mkdir() failed: %s (%s, %s) " "is still not owned by %s.%s", dir, dec2str(st.st_uid), dec2str(st.st_gid), dec2str(uid), dec2str(gid)); } return ret; } dovecot-2.2.33.2/src/lib/ostream-rawlog.c0000644000175000017500000000457713165463624015020 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "iostream-rawlog-private.h" #include "ostream-private.h" #include "ostream-rawlog.h" struct rawlog_ostream { struct ostream_private ostream; struct rawlog_iostream riostream; }; static void o_stream_rawlog_close(struct iostream_private *stream, bool close_parent) { struct rawlog_ostream *rstream = (struct rawlog_ostream *)stream; (void)o_stream_flush(rstream->ostream.parent); iostream_rawlog_close(&rstream->riostream); if (close_parent) o_stream_close(rstream->ostream.parent); } static ssize_t o_stream_rawlog_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct rawlog_ostream *rstream = (struct rawlog_ostream *)stream; unsigned int i; ssize_t ret, bytes; if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { o_stream_copy_error_from_parent(stream); return -1; } bytes = ret; for (i = 0; i < iov_count && bytes > 0; i++) { if (iov[i].iov_len < (size_t)bytes) { iostream_rawlog_write(&rstream->riostream, iov[i].iov_base, iov[i].iov_len); bytes -= iov[i].iov_len; } else { iostream_rawlog_write(&rstream->riostream, iov[i].iov_base, bytes); break; } } stream->ostream.offset += ret; return ret; } struct ostream * o_stream_create_rawlog(struct ostream *output, const char *rawlog_path, int rawlog_fd, enum iostream_rawlog_flags flags) { struct ostream *rawlog_output; bool autoclose_fd = (flags & IOSTREAM_RAWLOG_FLAG_AUTOCLOSE) != 0; i_assert(rawlog_path != NULL); i_assert(rawlog_fd != -1); rawlog_output = o_stream_create_fd(rawlog_fd, 0, autoclose_fd); o_stream_set_name(rawlog_output, t_strdup_printf("rawlog(%s)", rawlog_path)); return o_stream_create_rawlog_from_stream(output, rawlog_output, flags); } struct ostream * o_stream_create_rawlog_from_stream(struct ostream *output, struct ostream *rawlog_output, enum iostream_rawlog_flags flags) { struct rawlog_ostream *rstream; rstream = i_new(struct rawlog_ostream, 1); rstream->ostream.sendv = o_stream_rawlog_sendv; rstream->ostream.iostream.close = o_stream_rawlog_close; rstream->riostream.rawlog_output = rawlog_output; iostream_rawlog_init(&rstream->riostream, flags, FALSE); return o_stream_create(&rstream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib/array.h0000644000175000017500000003067513165463624013176 00000000000000#ifndef ARRAY_H #define ARRAY_H /* Array is a buffer accessible using fixed size elements. As long as the compiler provides a typeof() operator, the array provides type safety. If a wrong type is tried to be added to the array, or if the array's contents are tried to be used using a wrong type, the compiler will give a warning. Example usage: struct foo { ARRAY(struct bar) bars; ... }; i_array_init(&foo->bars, 10); struct bar *bar = array_idx(&foo->bars, 5); struct baz *baz = array_idx(&foo->bars, 5); // compiler warning If you want to pass an array as a parameter to a function, you'll need to create a type for the array using ARRAY_DEFINE_TYPE() and use the type in the parameter using ARRAY_TYPE(). Any arrays that you want to be passing around, such as structure members as in the above example, must also be defined using ARRAY_TYPE() too, rather than ARRAY(). Example: ARRAY_DEFINE_TYPE(foo, struct foo); void do_foo(ARRAY_TYPE(foo) *foos) { struct foo *foo = array_idx(foos, 0); } struct foo_manager { ARRAY_TYPE(foo) foos; // pedantically, ARRAY(struct foo) is a different type }; // ... do_foo(&my_foo_manager->foos); // No compiler warning about mismatched types */ #include "array-decl.h" #include "buffer.h" #define p_array_init(array, pool, init_count) \ array_create(array, pool, sizeof(**(array)->v), init_count) #define i_array_init(array, init_count) \ p_array_init(array, default_pool, init_count) #define t_array_init(array, init_count) \ p_array_init(array, pool_datastack_create(), init_count) #ifdef HAVE_TYPEOF # define ARRAY_TYPE_CAST_CONST(array) \ (typeof(*(array)->v)) # define ARRAY_TYPE_CAST_MODIFIABLE(array) \ (typeof(*(array)->v_modifiable)) # define ARRAY_TYPE_CHECK(array, data) \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ **(array)->v_modifiable, *(data)) # define ARRAY_TYPES_CHECK(array1, array2) \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ **(array1)->v_modifiable, **(array2)->v_modifiable) #else # define ARRAY_TYPE_CAST_CONST(array) # define ARRAY_TYPE_CAST_MODIFIABLE(array) # define ARRAY_TYPE_CHECK(array, data) 0 # define ARRAY_TYPES_CHECK(array1, array2) 0 #endif /* usage: struct foo *foo; array_foreach(foo_arr, foo) { .. } */ #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) # define array_foreach(array, elem) \ for (const void *elem ## __foreach_end = \ (const char *)(elem = *(array)->v) + (array)->arr.buffer->used; \ elem != elem ## __foreach_end; (elem)++) # define array_foreach_modifiable(array, elem) \ for (const void *elem ## _end = \ (const char *)(elem = ARRAY_TYPE_CAST_MODIFIABLE(array) \ buffer_get_modifiable_data((array)->arr.buffer, NULL)) + \ (array)->arr.buffer->used; \ elem != elem ## _end; (elem)++) #else # define array_foreach(array, elem) \ for (elem = *(array)->v; \ elem != CONST_PTR_OFFSET(*(array)->v, (array)->arr.buffer->used); \ (elem)++) # define array_foreach_modifiable(array, elem) \ for (elem = ARRAY_TYPE_CAST_MODIFIABLE(array) \ buffer_get_modifiable_data((array)->arr.buffer, NULL); \ elem != CONST_PTR_OFFSET(*(array)->v, (array)->arr.buffer->used); \ (elem)++) #endif #define array_ptr_to_idx(array, elem) \ ((elem) - (array)->v[0]) #define array_foreach_idx(array, elem) \ array_ptr_to_idx(array, elem) static inline void array_create_from_buffer_i(struct array *array, buffer_t *buffer, size_t element_size) { array->buffer = buffer; array->element_size = element_size; } #define array_create_from_buffer(array, buffer, element_size) \ array_create_from_buffer_i(&(array)->arr, buffer, element_size) static inline void array_create_i(struct array *array, pool_t pool, size_t element_size, unsigned int init_count) { buffer_t *buffer; buffer = buffer_create_dynamic(pool, init_count * element_size); array_create_from_buffer_i(array, buffer, element_size); } #define array_create(array, pool, element_size, init_count) \ array_create_i(&(array)->arr, pool, element_size, init_count) static inline void array_free_i(struct array *array) { buffer_free(&array->buffer); } #define array_free(array) \ array_free_i(&(array)->arr) static inline bool array_is_created_i(const struct array *array) { return array->buffer != NULL; } #define array_is_created(array) \ array_is_created_i(&(array)->arr) static inline pool_t ATTR_PURE array_get_pool_i(struct array *array) { return buffer_get_pool(array->buffer); } #define array_get_pool(array) \ array_get_pool_i(&(array)->arr) static inline void array_clear_i(struct array *array) { buffer_set_used_size(array->buffer, 0); } #define array_clear(array) \ array_clear_i(&(array)->arr) static inline unsigned int ATTR_PURE array_count_i(const struct array *array) { return array->buffer->used / array->element_size; } #define array_count(array) \ array_count_i(&(array)->arr) /* No need for the real count if all we're doing is comparing againts 0 */ #define array_is_empty(array) \ ((array)->arr.buffer->used == 0) #define array_not_empty(array) \ ((array)->arr.buffer->used > 0) static inline void array_append_i(struct array *array, const void *data, unsigned int count) { buffer_append(array->buffer, data, count * array->element_size); } #define array_append(array, data, count) \ array_append_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ data, count) static inline void array_append_array_i(struct array *dest_array, const struct array *src_array) { i_assert(dest_array->element_size == src_array->element_size); buffer_append_buf(dest_array->buffer, src_array->buffer, 0, (size_t)-1); } #define array_append_array(dest_array, src_array) \ array_append_array_i(&(dest_array)->arr + ARRAY_TYPES_CHECK(dest_array, src_array), \ &(src_array)->arr) static inline void array_insert_i(struct array *array, unsigned int idx, const void *data, unsigned int count) { buffer_insert(array->buffer, idx * array->element_size, data, count * array->element_size); } #define array_insert(array, idx, data, count) \ array_insert_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ idx, data, count) static inline void array_delete_i(struct array *array, unsigned int idx, unsigned int count) { buffer_delete(array->buffer, idx * array->element_size, count * array->element_size); } #define array_delete(array, idx, count) \ array_delete_i(&(array)->arr, idx, count) static inline const void * array_get_i(const struct array *array, unsigned int *count_r) { *count_r = array_count_i(array); return array->buffer->data; } #define array_get(array, count) \ ARRAY_TYPE_CAST_CONST(array)array_get_i(&(array)->arr, count) /* Re: i_assert() vs. pure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51971#c1 */ static inline const void * ATTR_PURE array_idx_i(const struct array *array, unsigned int idx) { i_assert(idx * array->element_size < array->buffer->used); return CONST_PTR_OFFSET(array->buffer->data, idx * array->element_size); } #define array_idx(array, idx) \ ARRAY_TYPE_CAST_CONST(array)array_idx_i(&(array)->arr, idx) static inline void * array_get_modifiable_i(struct array *array, unsigned int *count_r) { *count_r = array_count_i(array); return buffer_get_modifiable_data(array->buffer, NULL); } #define array_get_modifiable(array, count) \ ARRAY_TYPE_CAST_MODIFIABLE(array) \ array_get_modifiable_i(&(array)->arr, count) void *array_idx_modifiable_i(struct array *array, unsigned int idx); #define array_idx_modifiable(array, idx) \ ARRAY_TYPE_CAST_MODIFIABLE(array) \ array_idx_modifiable_i(&(array)->arr, idx) void array_idx_set_i(struct array *array, unsigned int idx, const void *data); #define array_idx_set(array, idx, data) \ array_idx_set_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ idx, data) void array_idx_clear_i(struct array *array, unsigned int idx); #define array_idx_clear(array, idx) \ array_idx_clear_i(&(array)->arr, idx) static inline void * array_append_space_i(struct array *array) { void *data; data = buffer_append_space_unsafe(array->buffer, array->element_size); memset(data, 0, array->element_size); return data; } #define array_append_space(array) \ ARRAY_TYPE_CAST_MODIFIABLE(array)array_append_space_i(&(array)->arr) #define array_append_zero(array) \ (void)array_append_space_i(&(array)->arr) void *array_insert_space_i(struct array *array, unsigned int idx); #define array_insert_space(array, idx) \ ARRAY_TYPE_CAST_MODIFIABLE(array) \ array_insert_space_i(&(array)->arr, idx) static inline void array_copy(struct array *dest, unsigned int dest_idx, const struct array *src, unsigned int src_idx, unsigned int count) { i_assert(dest->element_size == src->element_size); buffer_copy(dest->buffer, dest_idx * dest->element_size, src->buffer, src_idx * src->element_size, count * dest->element_size); } /* Exchange ownership of two arrays, which should have been allocated from the same pool/context. Useful for updating an array with a replacement. Can also do it with uninitialised arrays (which will have .element_size == 0). */ static inline void array_swap_i(struct array *array1, struct array *array2) { buffer_t *buffer = array1->buffer; size_t elsize = array1->element_size; array1->buffer = array2->buffer; array1->element_size = array2->element_size; array2->buffer = buffer; array2->element_size = elsize; } #define array_swap(array1, array2) \ array_swap_i(&(array1)->arr + ARRAY_TYPES_CHECK(array1, array2), \ &(array2)->arr) bool array_cmp_i(const struct array *array1, const struct array *array2) ATTR_PURE; #define array_cmp(array1, array2) \ array_cmp_i(&(array1)->arr, &(array2)->arr) /* Test equality via a comparator */ bool array_equal_fn_i(const struct array *array1, const struct array *array2, int (*cmp)(const void*, const void *)) ATTR_PURE; #define array_equal_fn(array1, array2, cmp) \ array_equal_fn_i(&(array1)->arr + \ ARRAY_TYPES_CHECK(array1, array2), \ &(array2)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ typeof(*(array2)->v))), \ (int (*)(const void *, const void *))cmp) bool array_equal_fn_ctx_i(const struct array *array1, const struct array *array2, int (*cmp)(const void*, const void *, const void *), const void *context) ATTR_PURE; /* Same, but with a context pointer. context can't be void* as ``const typeof(context)'' won't compile, so ``const typeof(*context)*'' is required instead, and that requires a complete type. */ #define array_equal_fn_ctx(array1, array2, cmp, ctx) \ array_equal_fn_ctx_i(&(array1)->arr + \ ARRAY_TYPES_CHECK(array1, array2), \ &(array2)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ typeof(*(array2)->v), \ const typeof(*ctx)*)), \ (int (*)(const void *, const void *, const void *))cmp, \ ctx) void array_reverse_i(struct array *array); #define array_reverse(array) \ array_reverse_i(&(array)->arr) void array_sort_i(struct array *array, int (*cmp)(const void *, const void *)); #define array_sort(array, cmp) \ array_sort_i(&(array)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array)->v), \ typeof(*(array)->v))), \ (int (*)(const void *, const void *))cmp) void *array_bsearch_i(struct array *array, const void *key, int (*cmp)(const void *, const void *)); #define array_bsearch(array, key, cmp) \ ARRAY_TYPE_CAST_MODIFIABLE(array)array_bsearch_i(&(array)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ typeof(*(array)->v))), \ (const void *)key, (int (*)(const void *, const void *))cmp) /* Returns pointer to first element for which cmp(key,elem)==0, or NULL */ const void *array_lsearch_i(const struct array *array, const void *key, int (*cmp)(const void *, const void *)); static inline void *array_lsearch_modifiable_i(struct array *array, const void *key, int (*cmp)(const void *, const void *)) { return (void *)array_lsearch_i(array, key, cmp); } #define ARRAY_LSEARCH_CALL(modifiable, array, key, cmp) \ array_lsearch##modifiable##i( \ &(array)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ typeof(*(array)->v))), \ (const void *)key, \ (int (*)(const void *, const void *))cmp) #define array_lsearch(array, key, cmp) \ ARRAY_TYPE_CAST_CONST(array)ARRAY_LSEARCH_CALL(_, array, key, cmp) #define array_lsearch_modifiable(array, key, cmp) \ ARRAY_TYPE_CAST_MODIFIABLE(array)ARRAY_LSEARCH_CALL(_modifiable_, array, key, cmp) #endif dovecot-2.2.33.2/src/lib/execv-const.h0000644000175000017500000000053413123174404014272 00000000000000#ifndef EXECV_CONST_H #define EXECV_CONST_H /* Just like execv() and execvp(), except argv points to const strings. Also if calling execv*() fails, these functions call i_fatal(). */ void execv_const(const char *path, const char *const argv[]) ATTR_NORETURN; void execvp_const(const char *file, const char *const argv[]) ATTR_NORETURN; #endif dovecot-2.2.33.2/src/lib/hook-build.h0000644000175000017500000000117713123174404014075 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #ifndef HOOK_BUILD_H #define HOOK_BUILD_H 1 struct hook_build_context; struct hook_stack; /* Initialize new hook building context, vfuncs should point to the functions table that is being manipulated, and size should be the size of this table. */ struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size); /* This is called after a hook may have updated vfuncs */ void hook_build_update(struct hook_build_context *ctx, void *_vlast); /* Free memory used by build context */ void hook_build_deinit(struct hook_build_context **_ctx); #endif dovecot-2.2.33.2/src/lib/hmac-cram-md5.c0000644000175000017500000000313113123174404014336 00000000000000/* * CRAM-MD5 (RFC 2195) compatibility code * Copyright (c) 2003 Joshua Goodall * * This software is released under the MIT license. */ #include "lib.h" #include "md5.h" #include "hmac-cram-md5.h" void hmac_md5_get_cram_context(struct hmac_context *_hmac_ctx, unsigned char context_digest[CRAM_MD5_CONTEXTLEN]) { struct hmac_context_priv *hmac_ctx = &_hmac_ctx->u.priv; unsigned char *cdp; struct md5_context *ctx = (void*)hmac_ctx->ctx; struct md5_context *ctxo = (void*)hmac_ctx->ctxo; #define CDPUT(p, c) STMT_START { \ *(p)++ = (c) & 0xff; \ *(p)++ = (c) >> 8 & 0xff; \ *(p)++ = (c) >> 16 & 0xff; \ *(p)++ = (c) >> 24 & 0xff; \ } STMT_END cdp = context_digest; CDPUT(cdp, ctxo->a); CDPUT(cdp, ctxo->b); CDPUT(cdp, ctxo->c); CDPUT(cdp, ctxo->d); CDPUT(cdp, ctx->a); CDPUT(cdp, ctx->b); CDPUT(cdp, ctx->c); CDPUT(cdp, ctx->d); } void hmac_md5_set_cram_context(struct hmac_context *_hmac_ctx, const unsigned char context_digest[CRAM_MD5_CONTEXTLEN]) { struct hmac_context_priv *hmac_ctx = &_hmac_ctx->u.priv; const unsigned char *cdp; struct md5_context *ctx = (void*)hmac_ctx->ctx; struct md5_context *ctxo = (void*)hmac_ctx->ctxo; #define CDGET(p, c) STMT_START { \ (c) = (*p++); \ (c) += (*p++ << 8); \ (c) += (*p++ << 16); \ (c) += ((uint32_t)(*p++) << 24); \ } STMT_END cdp = context_digest; CDGET(cdp, ctxo->a); CDGET(cdp, ctxo->b); CDGET(cdp, ctxo->c); CDGET(cdp, ctxo->d); CDGET(cdp, ctx->a); CDGET(cdp, ctx->b); CDGET(cdp, ctx->c); CDGET(cdp, ctx->d); ctxo->lo = ctx->lo = 64; ctxo->hi = ctx->hi = 0; } dovecot-2.2.33.2/src/lib/priorityq.c0000644000175000017500000000737113123174404014077 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "priorityq.h" /* Macros for moving inside an array implementation of binary tree where [0] is the root. */ #define PARENT_IDX(idx) \ (((idx) - 1) / 2) #define LEFT_CHILD_IDX(idx) \ ((idx) * 2 + 1) #define RIGHT_CHILD_IDX(idx) \ ((idx) * 2 + 2) struct priorityq { priorityq_cmp_callback_t *cmp_callback; ARRAY(struct priorityq_item *) items; }; struct priorityq * priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size) { struct priorityq *pq; pq = i_new(struct priorityq, 1); pq->cmp_callback = cmp_callback; i_array_init(&pq->items, init_size); return pq; } void priorityq_deinit(struct priorityq **_pq) { struct priorityq *pq = *_pq; *_pq = NULL; array_free(&pq->items); i_free(pq); } unsigned int priorityq_count(const struct priorityq *pq) { return array_count(&pq->items); } static void heap_items_swap(struct priorityq_item **items, unsigned int idx1, unsigned int idx2) { struct priorityq_item *tmp; /* swap the item indexes */ i_assert(items[idx1]->idx == idx1); i_assert(items[idx2]->idx == idx2); items[idx1]->idx = idx2; items[idx2]->idx = idx1; /* swap the item pointers */ tmp = items[idx1]; items[idx1] = items[idx2]; items[idx2] = tmp; } static unsigned int heap_item_bubble_up(struct priorityq *pq, unsigned int idx) { struct priorityq_item **items; unsigned int parent_idx, count; items = array_get_modifiable(&pq->items, &count); while (idx > 0) { parent_idx = PARENT_IDX(idx); i_assert(idx < count); if (pq->cmp_callback(items[idx], items[parent_idx]) >= 0) break; /* wrong order - swap */ heap_items_swap(items, idx, parent_idx); idx = parent_idx; } return idx; } static void heap_item_bubble_down(struct priorityq *pq, unsigned int idx) { struct priorityq_item **items; unsigned int left_idx, right_idx, min_child_idx, count; items = array_get_modifiable(&pq->items, &count); while ((left_idx = LEFT_CHILD_IDX(idx)) < count) { right_idx = RIGHT_CHILD_IDX(idx); if (right_idx >= count || pq->cmp_callback(items[left_idx], items[right_idx]) < 0) min_child_idx = left_idx; else min_child_idx = right_idx; if (pq->cmp_callback(items[min_child_idx], items[idx]) >= 0) break; /* wrong order - swap */ heap_items_swap(items, idx, min_child_idx); idx = min_child_idx; } } void priorityq_add(struct priorityq *pq, struct priorityq_item *item) { item->idx = array_count(&pq->items); array_append(&pq->items, &item, 1); (void)heap_item_bubble_up(pq, item->idx); } static void priorityq_remove_idx(struct priorityq *pq, unsigned int idx) { struct priorityq_item **items; unsigned int count; items = array_get_modifiable(&pq->items, &count); i_assert(idx < count); /* move last item over the removed one and fix the heap */ count--; heap_items_swap(items, idx, count); array_delete(&pq->items, count, 1); if (count > 0 && idx != count) { if (idx > 0) idx = heap_item_bubble_up(pq, idx); heap_item_bubble_down(pq, idx); } } void priorityq_remove(struct priorityq *pq, struct priorityq_item *item) { priorityq_remove_idx(pq, item->idx); item->idx = UINT_MAX; } struct priorityq_item *priorityq_peek(struct priorityq *pq) { struct priorityq_item *const *items; if (array_count(&pq->items) == 0) return NULL; items = array_idx(&pq->items, 0); return items[0]; } struct priorityq_item *priorityq_pop(struct priorityq *pq) { struct priorityq_item *item; item = priorityq_peek(pq); if (item != NULL) { priorityq_remove_idx(pq, 0); item->idx = UINT_MAX; } return item; } struct priorityq_item *const *priorityq_items(struct priorityq *pq) { if (array_count(&pq->items) == 0) return NULL; return array_idx(&pq->items, 0); } dovecot-2.2.33.2/src/lib/test-llist.c0000644000175000017500000001064113123174404014133 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "llist.h" struct dllist { struct dllist *prev, *next; }; static void test_dllist(void) { struct dllist *head = NULL, *l4, *l3, *l2, *l1; struct dllist empty = { NULL, NULL }; l4 = t_new(struct dllist, 1); l3 = t_new(struct dllist, 1); l2 = t_new(struct dllist, 1); l1 = t_new(struct dllist, 1); test_begin("dllist"); DLLIST_PREPEND(&head, l4); test_assert(head == l4); test_assert(l4->prev == NULL && l4->next == NULL); DLLIST_PREPEND(&head, l3); test_assert(head == l3); test_assert(l3->prev == NULL && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); DLLIST_PREPEND(&head, l2); DLLIST_PREPEND(&head, l1); /* remove from middle */ DLLIST_REMOVE(&head, l2); test_assert(l2->prev == NULL && l2->next == NULL); test_assert(head == l1); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); /* remove from head */ DLLIST_REMOVE(&head, l1); test_assert(l1->prev == NULL && l1->next == NULL); test_assert(head == l3); test_assert(l3->prev == NULL && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); /* remove from tail */ DLLIST_PREPEND(&head, l1); DLLIST_REMOVE(&head, l4); test_assert(l4->prev == NULL && l4->next == NULL); test_assert(head == l1); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == NULL); /* removal of an entry not in the list shouldn't cause the list to break */ DLLIST_REMOVE(&head, &empty); test_assert(head == l1); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == NULL); /* remove last two */ DLLIST_REMOVE(&head, l1); DLLIST_REMOVE(&head, l3); test_assert(l3->prev == NULL && l3->next == NULL); test_assert(head == NULL); test_end(); } static void test_dllist2(void) { struct dllist *head = NULL, *tail = NULL, *l4, *l3, *l2, *l1; struct dllist empty = { NULL, NULL }; l4 = t_new(struct dllist, 1); l3 = t_new(struct dllist, 1); l2 = t_new(struct dllist, 1); l1 = t_new(struct dllist, 1); test_begin("dllist"); /* prepend to empty */ DLLIST2_PREPEND(&head, &tail, l3); test_assert(head == l3 && tail == l3); test_assert(l3->next == NULL && l3->prev == NULL); /* remove last */ DLLIST2_REMOVE(&head, &tail, l3); test_assert(head == NULL && tail == NULL); test_assert(l3->next == NULL && l3->prev == NULL); /* append to empty */ DLLIST2_APPEND(&head, &tail, l3); test_assert(head == l3 && tail == l3); test_assert(l3->next == NULL && l3->prev == NULL); /* prepend */ DLLIST2_PREPEND(&head, &tail, l2); test_assert(head == l2 && tail == l3); test_assert(l2->prev == NULL && l2->next == l3); test_assert(l3->prev == l2 && l3->next == NULL); /* append */ DLLIST2_APPEND(&head, &tail, l4); test_assert(head == l2 && tail == l4); test_assert(l2->prev == NULL && l2->next == l3); test_assert(l3->prev == l2 && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); DLLIST2_PREPEND(&head, &tail, l1); /* remove from middle */ DLLIST2_REMOVE(&head, &tail, l2); test_assert(l2->prev == NULL && l2->next == NULL); test_assert(head == l1 && tail == l4); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); /* remove from head */ DLLIST2_REMOVE(&head, &tail, l1); test_assert(l1->prev == NULL && l1->next == NULL); test_assert(head == l3 && tail == l4); test_assert(l3->prev == NULL && l3->next == l4); test_assert(l4->prev == l3 && l4->next == NULL); /* remove from tail */ DLLIST2_PREPEND(&head, &tail, l1); DLLIST2_REMOVE(&head, &tail, l4); test_assert(l4->prev == NULL && l4->next == NULL); test_assert(head == l1 && tail == l3); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == NULL); /* removal of an entry not in the list shouldn't cause the list to break */ DLLIST2_REMOVE(&head, &tail, &empty); test_assert(head == l1); test_assert(head == l1 && tail == l3); test_assert(l1->prev == NULL && l1->next == l3); test_assert(l3->prev == l1 && l3->next == NULL); /* remove last two */ DLLIST2_REMOVE(&head, &tail, l1); DLLIST2_REMOVE(&head, &tail, l3); test_assert(l3->prev == NULL && l3->next == NULL); test_assert(head == NULL && tail == NULL); test_end(); } void test_llist(void) { test_dllist(); test_dllist2(); } dovecot-2.2.33.2/src/lib/crc32.h0000644000175000017500000000045213123174404012747 00000000000000#ifndef CRC32_H #define CRC32_H uint32_t crc32_data(const void *data, size_t size) ATTR_PURE; uint32_t crc32_str(const char *str) ATTR_PURE; uint32_t crc32_data_more(uint32_t crc, const void *data, size_t size) ATTR_PURE; uint32_t crc32_str_more(uint32_t crc, const char *str) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/file-dotlock.h0000644000175000017500000000740613165463624014430 00000000000000#ifndef FILE_DOTLOCK_H #define FILE_DOTLOCK_H #include #include struct dotlock; struct dotlock_settings { /* Dotlock files are created by first creating a temp file and then link()ing it to the dotlock. temp_prefix specifies the prefix to use for temp files. It may contain a full path. Default is ".temp.hostname.pid.". */ const char *temp_prefix; /* Use this suffix for dotlock filenames. Default is ".lock". */ const char *lock_suffix; /* Abort after this many seconds. */ unsigned int timeout; /* Override the lock file when it and the file we're protecting is older than stale_timeout. */ unsigned int stale_timeout; /* Callback is called once in a while. stale is set to TRUE if stale lock is detected and will be overridden in secs_left. If callback returns FALSE then, the lock will not be overridden. */ bool (*callback)(unsigned int secs_left, bool stale, void *context); void *context; /* Rely on O_EXCL locking to work instead of using hardlinks. It's faster, but doesn't work with all NFS implementations. */ unsigned int use_excl_lock:1; /* Flush NFS attribute cache before stating files. */ unsigned int nfs_flush:1; /* Use io_add_notify() to speed up finding out when an existing dotlock is deleted */ unsigned int use_io_notify:1; }; enum dotlock_create_flags { /* If lock already exists, fail immediately */ DOTLOCK_CREATE_FLAG_NONBLOCK = 0x01, /* Don't actually create the lock file, only make sure it doesn't exist. This is racy, so you shouldn't rely on it much. */ DOTLOCK_CREATE_FLAG_CHECKONLY = 0x02 }; enum dotlock_replace_flags { /* Check that lock file hasn't been overridden before renaming. */ DOTLOCK_REPLACE_FLAG_VERIFY_OWNER = 0x01, /* Don't close the file descriptor. */ DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD = 0x02 }; /* Create dotlock. Returns 1 if successful, 0 if timeout or -1 if error. When returning 0, errno is also set to EAGAIN. */ int file_dotlock_create(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, struct dotlock **dotlock_r); /* Delete the dotlock file. Returns 1 if successful, 0 if the file had already been deleted or reused by someone else, -1 if I/O error. */ int ATTR_NOWARN_UNUSED_RESULT file_dotlock_delete(struct dotlock **dotlock); /* Use dotlock as the new content for file. This provides read safety without locks, but it's not very good for large files. Returns fd for lock file. If locking timed out, returns -1 and errno = EAGAIN. */ int file_dotlock_open(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, struct dotlock **dotlock_r); /* Like file_dotlock_open(), but use the given file permissions. */ int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, mode_t mode, uid_t uid, gid_t gid, struct dotlock **dotlock_r); int file_dotlock_open_group(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, mode_t mode, gid_t gid, const char *gid_origin, struct dotlock **dotlock_r); /* Replaces the file dotlock protects with the dotlock file itself. */ int file_dotlock_replace(struct dotlock **dotlock, enum dotlock_replace_flags flags); /* Update dotlock's mtime. If you're keeping the dotlock for a long time, it's a good idea to update it once in a while so others won't override it. If the timestamp is less than a second old, it's not updated. */ int file_dotlock_touch(struct dotlock *dotlock); /* Returns TRUE if the lock is still ok, FALSE if it's been overridden. */ bool file_dotlock_is_locked(struct dotlock *dotlock); /* Returns the lock file path. */ const char *file_dotlock_get_lock_path(struct dotlock *dotlock); #endif dovecot-2.2.33.2/src/lib/log-throttle.h0000644000175000017500000000225713123174404014464 00000000000000#ifndef LOG_THROTTLE_H #define LOG_THROTTLE_H struct log_throttle_settings { /* Start throttling after we reach this many log events/interval. */ unsigned int throttle_at_max_per_interval; /* Throttling continues until there's only this many or below log events/interval. */ unsigned int unthrottle_at_max_per_interval; /* Interval unit in milliseconds. The throttled-callback is also called at this interval. Default (0) is 1000 milliseconds. */ unsigned int interval_msecs; }; typedef void log_throttle_callback_t(unsigned int new_events_count, void *context); struct log_throttle * log_throttle_init(const struct log_throttle_settings *set, log_throttle_callback_t *callback, void *context); #define log_throttle_init(set, callback, context) \ log_throttle_init(set + \ CALLBACK_TYPECHECK(callback, void (*)(unsigned int, typeof(context))), \ (log_throttle_callback_t *)callback, context) void log_throttle_deinit(struct log_throttle **throttle); /* Increase event count. Returns TRUE if the event should be logged, FALSE if it's throttled. ioloop_timeval is used to determine the current time. */ bool log_throttle_accept(struct log_throttle *throttle); #endif dovecot-2.2.33.2/src/lib/istream-tee.c0000644000175000017500000001530413165463624014262 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-tee.h" struct tee_istream { struct istream *input; struct tee_child_istream *children; uoff_t max_read_offset; }; struct tee_child_istream { struct istream_private istream; struct tee_istream *tee; struct tee_child_istream *next; unsigned int last_read_waiting:1; }; static void tee_streams_update_buffer(struct tee_istream *tee) { struct tee_child_istream *tstream = tee->children; const unsigned char *data; size_t size, old_used; data = i_stream_get_data(tee->input, &size); for (; tstream != NULL; tstream = tstream->next) { if (tstream->istream.istream.closed) { tstream->istream.skip = tstream->istream.pos = 0; continue; } old_used = tstream->istream.pos - tstream->istream.skip; tstream->istream.buffer = data; i_assert(tstream->istream.istream.v_offset >= tee->input->v_offset); tstream->istream.skip = tstream->istream.istream.v_offset - tee->input->v_offset; i_assert(tstream->istream.skip + old_used <= size); tstream->istream.pos = tstream->istream.skip + old_used; tstream->istream.parent_expected_offset = tee->input->v_offset; tstream->istream.access_counter = tee->input->real_stream->access_counter; } } static void tee_streams_skip(struct tee_istream *tee) { struct tee_child_istream *tstream = tee->children; size_t min_skip; min_skip = (size_t)-1; for (; tstream != NULL; tstream = tstream->next) { if (tstream->istream.skip < min_skip && !tstream->istream.istream.closed) min_skip = tstream->istream.skip; } if (min_skip > 0 && min_skip != (size_t)-1) { i_stream_skip(tee->input, min_skip); tee_streams_update_buffer(tee); } } static void i_stream_tee_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; tee_streams_skip(tstream->tee); } static void i_stream_tee_destroy(struct iostream_private *stream) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; struct tee_istream *tee = tstream->tee; struct tee_child_istream **p; if (tstream->istream.istream.v_offset > tee->max_read_offset) tee->max_read_offset = tstream->istream.istream.v_offset; for (p = &tee->children; *p != NULL; p = &(*p)->next) { if (*p == tstream) { *p = tstream->next; break; } } if (tee->children == NULL) { /* last child. the tee is now destroyed */ i_assert(tee->input->v_offset <= tee->max_read_offset); i_stream_skip(tee->input, tee->max_read_offset - tee->input->v_offset); i_stream_unref(&tee->input); i_free(tee); } else { tee_streams_skip(tstream->tee); } } static void i_stream_tee_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; tstream->istream.max_buffer_size = max_size; i_stream_set_max_buffer_size(tstream->tee->input, max_size); } static ssize_t i_stream_tee_read(struct istream_private *stream) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; struct istream *input = tstream->tee->input; const unsigned char *data; size_t size; uoff_t last_high_offset; ssize_t ret; tstream->last_read_waiting = FALSE; if (stream->buffer == NULL) { /* initial read */ tee_streams_update_buffer(tstream->tee); } data = i_stream_get_data(input, &size); /* last_high_offset contains how far we have read this child tee stream so far. input->v_offset + size contains how much is available in the parent stream without having to read more. */ last_high_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (stream->pos == size) { /* we've read everything, need to read more */ i_assert(last_high_offset == input->v_offset + size); tee_streams_skip(tstream->tee); ret = i_stream_read(input); if (ret <= 0) { size = i_stream_get_data_size(input); if (ret == -2 && stream->skip != 0) { /* someone else is holding the data, wait for it */ tstream->last_read_waiting = TRUE; return 0; } stream->istream.stream_errno = input->stream_errno; stream->istream.eof = input->eof; return ret; } tee_streams_update_buffer(tstream->tee); data = i_stream_get_data(input, &size); } else { /* there's still some data available from parent */ i_assert(last_high_offset < input->v_offset + size); tee_streams_update_buffer(tstream->tee); i_assert(stream->pos < size); } i_assert(stream->buffer == data); ret = size - stream->pos; i_assert(ret > 0); stream->pos = size; i_assert(stream->istream.v_offset + (stream->pos - stream->skip) == input->v_offset + size); return ret; } static int i_stream_tee_stat(struct istream_private *stream, bool exact) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; const struct stat *st; if (i_stream_stat(tstream->tee->input, exact, &st) < 0) return -1; stream->statbuf = *st; return 0; } static void i_stream_tee_sync(struct istream_private *stream) { struct tee_child_istream *tstream = (struct tee_child_istream *)stream; tee_streams_skip(tstream->tee); if (i_stream_get_data_size(tstream->tee->input) != 0) { i_panic("tee-istream: i_stream_sync() called " "with data still buffered"); } i_stream_sync(tstream->tee->input); } struct tee_istream *tee_i_stream_create(struct istream *input) { struct tee_istream *tee; tee = i_new(struct tee_istream, 1); if (input->v_offset == 0) { i_stream_ref(input); tee->input = input; } else { tee->input = i_stream_create_limit(input, (uoff_t)-1); } return tee; } struct istream *tee_i_stream_create_child(struct tee_istream *tee) { struct tee_child_istream *tstream; struct istream *ret, *input = tee->input; tstream = i_new(struct tee_child_istream, 1); tstream->tee = tee; tstream->istream.max_buffer_size = input->real_stream->max_buffer_size; tstream->istream.iostream.close = i_stream_tee_close; tstream->istream.iostream.destroy = i_stream_tee_destroy; tstream->istream.iostream.set_max_buffer_size = i_stream_tee_set_max_buffer_size; tstream->istream.read = i_stream_tee_read; tstream->istream.stat = i_stream_tee_stat; tstream->istream.sync = i_stream_tee_sync; tstream->next = tee->children; tee->children = tstream; ret = i_stream_create(&tstream->istream, input, i_stream_get_fd(input)); i_stream_set_name(&tstream->istream.istream, i_stream_get_name(input)); /* we keep the reference in tee stream, no need for extra references */ i_stream_unref(&input); return ret; } bool tee_i_stream_child_is_waiting(struct istream *input) { struct tee_child_istream *tstream = (struct tee_child_istream *)input->real_stream; return tstream->last_read_waiting; } dovecot-2.2.33.2/src/lib/base32.c0000644000175000017500000002251013123174404013104 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base32.h" #include "buffer.h" static const char b32enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; static const char b32hexenc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; static const unsigned char b32dec[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 48-55 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, /* 72-79 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; static const unsigned char b32hexdec[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 48-55 */ 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* 64-71 */ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* 72-79 */ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0xff, /* 80-87 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; static void base32_encode_with_alphabet(const char *alph, bool pad, const void *src, size_t src_size, buffer_t *dest) { const unsigned char *src_c = src; unsigned char tmp[8], endb; size_t src_pos; /* [5 3][2 5 1][4 4][1 5 2][3 5] (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) */ /* encode main part */ for (src_pos = 0; src_pos + 4 < src_size; src_pos += 5) { tmp[0] = alph[src_c[src_pos] >> 3]; tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | ((src_c[src_pos+1] >> 6) & 0x03)]; tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | (src_c[src_pos+2] >> 4)]; tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | (src_c[src_pos+3] >> 7)]; tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3) | (src_c[src_pos+4] >> 5)]; tmp[7] = alph[src_c[src_pos+4] & 0x1f]; buffer_append(dest, tmp, 8); } /* encode last < 5 bytes if any */ if (src_pos < src_size) { tmp[0] = alph[src_c[src_pos] >> 3]; switch (src_size - src_pos) { case 1: tmp[1] = alph[((src_c[src_pos] & 0x07) << 2)]; endb = 2; break; case 2: tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | ((src_c[src_pos+1] >> 6) & 0x03)]; tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4)]; endb = 4; break; case 3: tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | ((src_c[src_pos+1] >> 6) & 0x03)]; tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | (src_c[src_pos+2] >> 4)]; tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1)]; endb = 5; break; case 4: tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | ((src_c[src_pos+1] >> 6) & 0x03)]; tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | (src_c[src_pos+2] >> 4)]; tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | (src_c[src_pos+3] >> 7)]; tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3)]; endb = 7; break; default: i_unreached(); } /* add padding if required */ if (pad) { memset(&tmp[endb], '=', sizeof(tmp)-endb); buffer_append(dest, tmp, 8); } else { buffer_append(dest, tmp, endb); } } } void base32_encode(bool pad, const void *src, size_t src_size, buffer_t *dest) { base32_encode_with_alphabet(b32enc, pad, src, src_size, dest); } void base32hex_encode(bool pad, const void *src, size_t src_size, buffer_t *dest) { base32_encode_with_alphabet(b32hexenc, pad, src, src_size, dest); } #define IS_EMPTY(c) \ ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') static int base32_decode_with_alphabet(const unsigned char *alph, const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { const unsigned char *src_c = src; size_t block_pos, src_pos; unsigned char output[5], ipos, opos; int ret = 1; /* (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) [5 3][2 5 1][4 4][1 5 2][3 5] */ ipos = opos = 0; block_pos = 0; for (src_pos = 0; src_pos < src_size; src_pos++) { unsigned char input = alph[src_c[src_pos]]; if (input == 0xff) { if (unlikely(!IS_EMPTY(src_c[src_pos]))) break; continue; } ipos++; switch (ipos) { case 1: output[0] = input << 3; opos = 0; break; case 2: output[0] |= input >> 2; output[1] = (input & 0x03) << 6; opos = 1; break; case 3: output[1] |= input << 1; opos = 1; break; case 4: output[1] |= input >> 4; output[2] = (input & 0x0f) << 4; opos = 2; break; case 5: output[2] |= input >> 1; output[3] = (input & 0x01) << 7; opos = 3; break; case 6: output[3] |= input << 2; opos = 3; break; case 7: output[3] |= input >> 3; output[4] = ((input & 0x07) << 5); opos = 4; break; case 8: output[4] |= input; buffer_append(dest, output, 5); ipos = 0; opos = 0; block_pos = src_pos; break; default: i_unreached(); } } if (ipos > 0) { for (; src_pos < src_size; src_pos++) { if (src_c[src_pos] != '=') { if (unlikely(!IS_EMPTY(src_c[src_pos]))) { ret = -1; break; } continue; } if (++ipos >= 8) { buffer_append(dest, output, opos); ipos = 0; ret = 0; src_pos++; break; } } } if (src_pos_r != NULL) { if (ipos == 0) { for (; src_pos < src_size; src_pos++) { if (!IS_EMPTY(src_c[src_pos])) break; } *src_pos_r = src_pos; } else { *src_pos_r = block_pos; } } return ret; } int base32_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { return base32_decode_with_alphabet (b32dec, src, src_size, src_pos_r, dest); } int base32hex_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { return base32_decode_with_alphabet (b32hexdec, src, src_size, src_pos_r, dest); } buffer_t *t_base32_decode_str(const char *str) { buffer_t *buf; size_t len = strlen(str); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE32_DECODED_SIZE(len)); (void)base32_decode(str, len, NULL, buf); return buf; } buffer_t *t_base32hex_decode_str(const char *str) { buffer_t *buf; size_t len = strlen(str); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE32_DECODED_SIZE(len)); (void)base32hex_decode(str, len, NULL, buf); return buf; } bool base32_is_valid_char(char c) { return b32dec[(uint8_t)c] != 0xff; } bool base32hex_is_valid_char(char c) { return b32hexdec[(uint8_t)c] != 0xff; } dovecot-2.2.33.2/src/lib/aqueue.c0000644000175000017500000000606213123174404013316 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "aqueue.h" struct aqueue *aqueue_init(struct array *array) { struct aqueue *aqueue; aqueue = i_new(struct aqueue, 1); aqueue->arr = array; aqueue->area_size = buffer_get_writable_size(aqueue->arr->buffer) / aqueue->arr->element_size; i_assert(aqueue->area_size > 0); return aqueue; } void aqueue_deinit(struct aqueue **_aqueue) { struct aqueue *aqueue = *_aqueue; *_aqueue = NULL; i_free(aqueue); } static void aqueue_grow(struct aqueue *aqueue) { unsigned int orig_area_size, count; i_assert(aqueue->full && aqueue->head == aqueue->tail); orig_area_size = aqueue->area_size; (void)array_append_space_i(aqueue->arr); aqueue->area_size = buffer_get_writable_size(aqueue->arr->buffer) / aqueue->arr->element_size; i_assert(orig_area_size < aqueue->area_size); count = I_MIN(aqueue->area_size - orig_area_size, aqueue->head); array_copy(aqueue->arr, orig_area_size, aqueue->arr, 0, count); if (count < aqueue->area_size - orig_area_size) aqueue->head = orig_area_size + count; else { array_copy(aqueue->arr, 0, aqueue->arr, count, aqueue->head - count); aqueue->head -= count; } i_assert(aqueue->head != aqueue->tail); aqueue->full = FALSE; } void aqueue_append(struct aqueue *aqueue, const void *data) { if (aqueue->full) { aqueue_grow(aqueue); i_assert(!aqueue->full); } array_idx_set_i(aqueue->arr, aqueue->head, data); aqueue->head = (aqueue->head + 1) % aqueue->area_size; aqueue->full = aqueue->head == aqueue->tail; } void aqueue_delete(struct aqueue *aqueue, unsigned int n) { unsigned int idx, count = aqueue_count(aqueue); i_assert(n < count); aqueue->full = FALSE; if (n == 0) { /* optimized deletion from tail */ aqueue->tail = (aqueue->tail + 1) % aqueue->area_size; return; } if (n == count-1) { /* optimized deletion from head */ aqueue->head = (aqueue->head + aqueue->area_size - 1) % aqueue->area_size; return; } idx = aqueue_idx(aqueue, n); if ((n < count/2 || idx > aqueue->head) && idx > aqueue->tail) { /* move tail forward. ..tail##idx##head.. or ##head..tail##idx## */ array_copy(aqueue->arr, aqueue->tail + 1, aqueue->arr, aqueue->tail, idx - aqueue->tail); aqueue->tail++; i_assert(aqueue->tail < aqueue->area_size); } else { /* move head backward. ..tail##idx##head.. or ##idx##head..tail## */ i_assert(idx < aqueue->head); array_copy(aqueue->arr, idx, aqueue->arr, idx + 1, aqueue->head - idx); aqueue->head = (aqueue->head + aqueue->area_size - 1) % aqueue->area_size; } i_assert(aqueue->head < aqueue->area_size && aqueue->head != aqueue->tail); } void aqueue_delete_tail(struct aqueue *aqueue) { aqueue_delete(aqueue, 0); } void aqueue_clear(struct aqueue *aqueue) { aqueue->head = aqueue->tail = 0; aqueue->full = FALSE; } unsigned int aqueue_count(const struct aqueue *aqueue) { unsigned int area_size = aqueue->area_size; return aqueue->full ? area_size : (area_size - aqueue->tail + aqueue->head) % area_size; } dovecot-2.2.33.2/src/lib/write-full.h0000644000175000017500000000060013123174404014120 00000000000000#ifndef WRITE_FULL_H #define WRITE_FULL_H /* Write data into file. Returns -1 if error occurred, or 0 if all was ok. If there's not enough space in device, -1 with ENOSPC is returned, and it's unspecified how much data was actually written. */ int write_full(int fd, const void *data, size_t size); int pwrite_full(int fd, const void *data, size_t size, off_t offset); #endif dovecot-2.2.33.2/src/lib/iostream-rawlog.c0000644000175000017500000001735613165463624015170 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "ioloop.h" #include "buffer.h" #include "str.h" #include "net.h" #include "write-full.h" #include "time-util.h" #include "istream.h" #include "ostream.h" #include "iostream-private.h" #include "iostream-rawlog-private.h" #include "istream-rawlog.h" #include "ostream-rawlog.h" #include "iostream-rawlog.h" #include #include #define RAWLOG_MAX_LINE_LEN 8192 static void rawlog_write_timestamp(struct rawlog_iostream *rstream, bool line_ends) { unsigned char data[MAX_INT_STRLEN + 6 + 1 + 3]; buffer_t buf; if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_TIMESTAMP) == 0) return; buffer_create_from_data(&buf, data, sizeof(data)); str_printfa(&buf, "%lu.%06u ", (unsigned long)ioloop_timeval.tv_sec, (unsigned int)ioloop_timeval.tv_usec); if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) { str_append_c(&buf, rstream->input ? 'I' : 'O'); str_append_c(&buf, line_ends ? ':' : '>'); str_append_c(&buf, ' '); } o_stream_nsend(rstream->rawlog_output, buf.data, buf.used); } void iostream_rawlog_init(struct rawlog_iostream *rstream, enum iostream_rawlog_flags flags, bool input) { rstream->flags = flags; rstream->input = input; if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) rstream->buffer = buffer_create_dynamic(default_pool, 1024); } static void iostream_rawlog_write_buffered(struct rawlog_iostream *rstream, const unsigned char *data, size_t size) { const unsigned char *p; size_t pos; bool line_ends; while (size > 0) { p = memchr(data, '\n', size); if (p != NULL) { line_ends = TRUE; pos = p-data + 1; } else if (rstream->buffer->used + size < RAWLOG_MAX_LINE_LEN) { buffer_append(rstream->buffer, data, size); break; } else { line_ends = FALSE; pos = size; } rawlog_write_timestamp(rstream, line_ends); if (rstream->buffer->used > 0) { o_stream_nsend(rstream->rawlog_output, rstream->buffer->data, rstream->buffer->used); buffer_set_used_size(rstream->buffer, 0); } o_stream_nsend(rstream->rawlog_output, data, pos); data += pos; size -= pos; } } static void iostream_rawlog_write_unbuffered(struct rawlog_iostream *rstream, const unsigned char *data, size_t size) { size_t i, start; if (!rstream->line_continued) rawlog_write_timestamp(rstream, TRUE); for (start = 0, i = 1; i < size; i++) { if (data[i-1] == '\n') { o_stream_nsend(rstream->rawlog_output, data + start, i - start); rawlog_write_timestamp(rstream, TRUE); start = i; } } if (start != size) { o_stream_nsend(rstream->rawlog_output, data + start, size - start); } rstream->line_continued = data[size-1] != '\n'; } void iostream_rawlog_write(struct rawlog_iostream *rstream, const unsigned char *data, size_t size) { if (size == 0 || rstream->rawlog_output == NULL) return; io_loop_time_refresh(); o_stream_cork(rstream->rawlog_output); if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) iostream_rawlog_write_buffered(rstream, data, size); else iostream_rawlog_write_unbuffered(rstream, data, size); o_stream_uncork(rstream->rawlog_output); if (o_stream_nfinish(rstream->rawlog_output) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(rstream->rawlog_output), o_stream_get_error(rstream->rawlog_output)); iostream_rawlog_close(rstream); } } void iostream_rawlog_close(struct rawlog_iostream *rstream) { if (rstream->rawlog_output != NULL) o_stream_unref(&rstream->rawlog_output); if (rstream->buffer != NULL) buffer_free(&rstream->buffer); } static void iostream_rawlog_create_fd(int fd, const char *path, struct istream **input, struct ostream **output) { struct istream *old_input; struct ostream *old_output; old_input = *input; old_output = *output; *input = i_stream_create_rawlog(old_input, path, fd, IOSTREAM_RAWLOG_FLAG_BUFFERED | IOSTREAM_RAWLOG_FLAG_TIMESTAMP); *output = o_stream_create_rawlog(old_output, path, fd, IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | IOSTREAM_RAWLOG_FLAG_BUFFERED | IOSTREAM_RAWLOG_FLAG_TIMESTAMP); i_stream_unref(&old_input); o_stream_unref(&old_output); } static int iostream_rawlog_try_create_tcp(const char *path, struct istream **input, struct ostream **output) { const char *host; struct ip_addr *ips; unsigned int ips_count; in_port_t port; int ret, fd; /* tcp:host:port */ if (strncmp(path, "tcp:", 4) != 0) return 0; path += 4; if (strchr(path, '/') != NULL) return 0; if (net_str2hostport(path, 0, &host, &port) < 0 || port == 0) return 0; ret = net_gethostbyname(host, &ips, &ips_count); if (ret != 0) { i_error("net_gethostbyname(%s) failed: %s", host, net_gethosterror(ret)); return -1; } fd = net_connect_ip_blocking(&ips[0], port, NULL); if (fd == -1) { i_error("connect(%s:%u) failed: %m", net_ip2addr(&ips[0]), port); return -1; } iostream_rawlog_create_fd(fd, path, input, output); return 1; } int iostream_rawlog_create(const char *dir, struct istream **input, struct ostream **output) { static unsigned int counter = 0; const char *timestamp, *prefix; struct stat st; int ret; if ((ret = iostream_rawlog_try_create_tcp(dir, input, output)) != 0) return ret < 0 ? -1 : 0; if (stat(dir, &st) < 0) { if (errno != ENOENT && errno != EACCES) i_error("rawlog: stat(%s) failed: %m", dir); return -1; } timestamp = t_strflocaltime("%Y%m%d-%H%M%S", ioloop_time); counter++; prefix = t_strdup_printf("%s/%s.%s.%u", dir, timestamp, my_pid, counter); return iostream_rawlog_create_prefix(prefix, input, output); } int iostream_rawlog_create_prefix(const char *prefix, struct istream **input, struct ostream **output) { const char *in_path, *out_path; struct istream *old_input; struct ostream *old_output; int in_fd, out_fd; in_path = t_strdup_printf("%s.in", prefix); in_fd = open(in_path, O_CREAT | O_APPEND | O_WRONLY, 0600); if (in_fd == -1) { i_error("creat(%s) failed: %m", in_path); return -1; } out_path = t_strdup_printf("%s.out", prefix); out_fd = open(out_path, O_CREAT | O_APPEND | O_WRONLY, 0600); if (out_fd == -1) { i_error("creat(%s) failed: %m", out_path); i_close_fd(&in_fd); i_unlink(in_path); return -1; } old_input = *input; old_output = *output; *input = i_stream_create_rawlog(old_input, in_path, in_fd, IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | IOSTREAM_RAWLOG_FLAG_TIMESTAMP); *output = o_stream_create_rawlog(old_output, out_path, out_fd, IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | IOSTREAM_RAWLOG_FLAG_TIMESTAMP); i_stream_unref(&old_input); o_stream_unref(&old_output); return 0; } int iostream_rawlog_create_path(const char *path, struct istream **input, struct ostream **output) { int ret, fd; if ((ret = iostream_rawlog_try_create_tcp(path, input, output)) != 0) return ret < 0 ? -1 : 0; fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); if (fd == -1) { i_error("creat(%s) failed: %m", path); return -1; } iostream_rawlog_create_fd(fd, path, input, output); return 0; } void iostream_rawlog_create_from_stream(struct ostream *rawlog_output, struct istream **input, struct ostream **output) { const enum iostream_rawlog_flags rawlog_flags = IOSTREAM_RAWLOG_FLAG_BUFFERED | IOSTREAM_RAWLOG_FLAG_TIMESTAMP; struct istream *old_input; struct ostream *old_output; if (input != NULL) { old_input = *input; *input = i_stream_create_rawlog_from_stream(old_input, rawlog_output, rawlog_flags); i_stream_unref(&old_input); } if (output != NULL) { old_output = *output; *output = o_stream_create_rawlog_from_stream(old_output, rawlog_output, rawlog_flags); o_stream_unref(&old_output); } if (input != NULL && output != NULL) o_stream_ref(rawlog_output); } dovecot-2.2.33.2/src/lib/unichar.c0000644000175000017500000002227613123174404013467 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "unichar.h" #include "unicodemap.c" #define HANGUL_FIRST 0xac00 #define HANGUL_LAST 0xd7a3 const unsigned char utf8_replacement_char[UTF8_REPLACEMENT_CHAR_LEN] = { 0xef, 0xbf, 0xbd }; /* 0xfffd */ static const uint8_t utf8_non1_bytes[256 - 192 - 2] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 }; const uint8_t *const uni_utf8_non1_bytes = utf8_non1_bytes; unsigned int uni_strlen(const unichar_t *str) { unsigned int len = 0; for (len = 0; str[len] != 0; len++) ; return len; } int uni_utf8_get_char(const char *input, unichar_t *chr_r) { return uni_utf8_get_char_n((const unsigned char *)input, (size_t)-1, chr_r); } int uni_utf8_get_char_n(const void *_input, size_t max_len, unichar_t *chr_r) { static unichar_t lowest_valid_chr_table[] = { 0, 0, 0x80, 0x800, 0x10000, 0x200000, 0x4000000 }; const unsigned char *input = _input; unichar_t chr, lowest_valid_chr; unsigned int i, len; int ret; i_assert(max_len > 0); if (*input < 0x80) { *chr_r = *input; return 1; } /* first byte has len highest bits set, followed by zero bit. the rest of the bits are used as the highest bits of the value. */ chr = *input; len = uni_utf8_char_bytes(*input); switch (len) { case 2: chr &= 0x1f; break; case 3: chr &= 0x0f; break; case 4: chr &= 0x07; break; case 5: chr &= 0x03; break; case 6: chr &= 0x01; break; default: /* only 7bit chars should have len==1 */ i_assert(len == 1); return -1; } if (len <= max_len) { lowest_valid_chr = lowest_valid_chr_table[len]; ret = len; } else { /* check first if the input is invalid before returning 0 */ lowest_valid_chr = 0; ret = 0; len = max_len; } /* the following bytes must all be 10xxxxxx */ for (i = 1; i < len; i++) { if ((input[i] & 0xc0) != 0x80) return input[i] == '\0' ? 0 : -1; chr <<= 6; chr |= input[i] & 0x3f; } if (chr < lowest_valid_chr) { /* overlong encoding */ return -1; } *chr_r = chr; return ret; } int uni_utf8_to_ucs4(const char *input, ARRAY_TYPE(unichars) *output) { unichar_t chr; while (*input != '\0') { int len = uni_utf8_get_char(input, &chr); if (len <= 0) { /* invalid input */ return -1; } input += len; array_append(output, &chr, 1); } return 0; } int uni_utf8_to_ucs4_n(const unsigned char *input, size_t size, ARRAY_TYPE(unichars) *output) { unichar_t chr; while (size > 0) { int len = uni_utf8_get_char_n(input, size, &chr); if (len <= 0) return -1; /* invalid input */ input += len; size -= len; array_append(output, &chr, 1); } return 0; } void uni_ucs4_to_utf8(const unichar_t *input, size_t len, buffer_t *output) { for (; len > 0 && *input != '\0'; input++, len--) uni_ucs4_to_utf8_c(*input, output); } void uni_ucs4_to_utf8_c(unichar_t chr, buffer_t *output) { unsigned char first; int bitpos; if (chr < 0x80) { buffer_append_c(output, chr); return; } i_assert(chr < 0x80000000); /* 1 << (5*6 + 1) */ if (chr < (1 << (6 + 5))) { /* 110xxxxx */ bitpos = 6; first = 0x80 | 0x40; } else if (chr < (1 << ((2*6) + 4))) { /* 1110xxxx */ bitpos = 2*6; first = 0x80 | 0x40 | 0x20; } else if (chr < (1 << ((3*6) + 3))) { /* 11110xxx */ bitpos = 3*6; first = 0x80 | 0x40 | 0x20 | 0x10; } else if (chr < (1 << ((4*6) + 2))) { /* 111110xx */ bitpos = 4*6; first = 0x80 | 0x40 | 0x20 | 0x10 | 0x08; } else { /* 1111110x */ bitpos = 5*6; first = 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x04; } buffer_append_c(output, first | (chr >> bitpos)); do { bitpos -= 6; buffer_append_c(output, 0x80 | ((chr >> bitpos) & 0x3f)); } while (bitpos > 0); } unsigned int uni_utf8_strlen(const char *input) { return uni_utf8_strlen_n(input, strlen(input)); } unsigned int uni_utf8_strlen_n(const void *input, size_t size) { size_t partial_pos; return uni_utf8_partial_strlen_n(input, size, &partial_pos); } unsigned int uni_utf8_partial_strlen_n(const void *_input, size_t size, size_t *partial_pos_r) { const unsigned char *input = _input; unsigned int count, len = 0; size_t i; for (i = 0; i < size; ) { count = uni_utf8_char_bytes(input[i]); if (i + count > size) break; i += count; len++; } *partial_pos_r = i; return len; } static bool uint16_find(const uint16_t *data, unsigned int count, uint16_t value, unsigned int *idx_r) { BINARY_NUMBER_SEARCH(data, count, value, idx_r); } static bool uint32_find(const uint32_t *data, unsigned int count, uint32_t value, unsigned int *idx_r) { BINARY_NUMBER_SEARCH(data, count, value, idx_r); } unichar_t uni_ucs4_to_titlecase(unichar_t chr) { unsigned int idx; if (chr <= 0xff) return titlecase8_map[chr]; else if (chr <= 0xffff) { if (!uint16_find(titlecase16_keys, N_ELEMENTS(titlecase16_keys), chr, &idx)) return chr; else return titlecase16_values[idx]; } else { if (!uint32_find(titlecase32_keys, N_ELEMENTS(titlecase32_keys), chr, &idx)) return chr; else return titlecase32_values[idx]; } } static bool uni_ucs4_decompose_uni(unichar_t *chr) { unsigned int idx; if (*chr <= 0xff) { if (uni8_decomp_map[*chr] == *chr) return FALSE; *chr = uni8_decomp_map[*chr]; } else if (*chr <= 0xffff) { if (*chr < uni16_decomp_keys[0]) return FALSE; if (!uint16_find(uni16_decomp_keys, N_ELEMENTS(uni16_decomp_keys), *chr, &idx)) return FALSE; *chr = uni16_decomp_values[idx]; } else { if (!uint32_find(uni32_decomp_keys, N_ELEMENTS(uni32_decomp_keys), *chr, &idx)) return FALSE; *chr = uni32_decomp_values[idx]; } return TRUE; } static void uni_ucs4_decompose_hangul_utf8(unichar_t chr, buffer_t *output) { #define SBase HANGUL_FIRST #define LBase 0x1100 #define VBase 0x1161 #define TBase 0x11A7 #define LCount 19 #define VCount 21 #define TCount 28 #define NCount (VCount * TCount) unsigned int SIndex = chr - SBase; unichar_t L = LBase + SIndex / NCount; unichar_t V = VBase + (SIndex % NCount) / TCount; unichar_t T = TBase + SIndex % TCount; uni_ucs4_to_utf8_c(L, output); uni_ucs4_to_utf8_c(V, output); if (T != TBase) uni_ucs4_to_utf8_c(T, output); } static bool uni_ucs4_decompose_multi_utf8(unichar_t chr, buffer_t *output) { const uint32_t *value; unsigned int idx; if (chr < multidecomp_keys[0] || chr > 0xffff) return FALSE; if (!uint32_find(multidecomp_keys, N_ELEMENTS(multidecomp_keys), chr, &idx)) return FALSE; value = &multidecomp_values[multidecomp_offsets[idx]]; for (; *value != 0; value++) uni_ucs4_to_utf8_c(*value, output); return TRUE; } static void output_add_replacement_char(buffer_t *output) { if (output->used >= UTF8_REPLACEMENT_CHAR_LEN && memcmp(CONST_PTR_OFFSET(output->data, output->used - UTF8_REPLACEMENT_CHAR_LEN), utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN) == 0) { /* don't add the replacement char multiple times */ return; } buffer_append(output, utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN); } int uni_utf8_to_decomposed_titlecase(const void *_input, size_t size, buffer_t *output) { const unsigned char *input = _input; unichar_t chr; int ret = 0; while (size > 0) { int bytes = uni_utf8_get_char_n(input, size, &chr); if (bytes <= 0) { /* invalid input. try the next byte. */ ret = -1; input++; size--; output_add_replacement_char(output); continue; } input += bytes; size -= bytes; chr = uni_ucs4_to_titlecase(chr); if (chr >= HANGUL_FIRST && chr <= HANGUL_LAST) uni_ucs4_decompose_hangul_utf8(chr, output); else if (uni_ucs4_decompose_uni(&chr) || !uni_ucs4_decompose_multi_utf8(chr, output)) uni_ucs4_to_utf8_c(chr, output); } return ret; } static inline unsigned int is_valid_utf8_seq(const unsigned char *input, unsigned int size) { unichar_t chr; int len = uni_utf8_get_char_n(input, size, &chr); return len <= 0 ? 0 : len; } static int uni_utf8_find_invalid_pos(const unsigned char *input, size_t size, size_t *pos_r) { size_t i, len; /* find the first invalid utf8 sequence */ for (i = 0; i < size;) { if (input[i] < 0x80) i++; else { len = is_valid_utf8_seq(input + i, size-i); if (unlikely(len == 0)) { *pos_r = i; return -1; } i += len; } } return 0; } bool uni_utf8_get_valid_data(const unsigned char *input, size_t size, buffer_t *buf) { size_t i, len; if (uni_utf8_find_invalid_pos(input, size, &i) == 0) return TRUE; /* broken utf-8 input - skip the broken characters */ buffer_append(buf, input, i++); output_add_replacement_char(buf); while (i < size) { if (input[i] < 0x80) { buffer_append_c(buf, input[i++]); continue; } len = is_valid_utf8_seq(input + i, size-i); if (len == 0) { i++; output_add_replacement_char(buf); continue; } buffer_append(buf, input + i, len); i += len; } return FALSE; } bool uni_utf8_str_is_valid(const char *str) { size_t i; return uni_utf8_find_invalid_pos((const unsigned char *)str, strlen(str), &i) == 0; } bool uni_utf8_data_is_valid(const unsigned char *data, size_t size) { size_t i; return uni_utf8_find_invalid_pos(data, size, &i) == 0; } dovecot-2.2.33.2/src/lib/istream-base64-decoder.c0000644000175000017500000000757113165463624016203 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "hex-binary.h" #include "istream-private.h" #include "istream-base64.h" struct base64_decoder_istream { struct istream_private istream; }; static int i_stream_read_parent(struct istream_private *stream) { size_t size; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size >= 4) return 1; /* we have less than one base64 block. see if there is more data available. */ ret = i_stream_read(stream->parent); if (ret <= 0) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); return 1; } static int i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; i_stream_try_alloc(stream, (size+3)/4*3, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 3) / 4 * 3 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 3) * 4; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); if (base64_decode(data, size, &pos, &buf) < 0) { io_stream_set_error(&stream->iostream, "Invalid base64 data: 0x%s", binary_to_hex(data+pos, I_MIN(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; } static ssize_t i_stream_base64_decoder_read(struct istream_private *stream) { struct base64_decoder_istream *bstream = (struct base64_decoder_istream *)stream; const unsigned char *data; size_t pre_count, post_count, size; int ret; do { ret = i_stream_read_parent(stream); if (ret <= 0) { if (ret < 0 && stream->istream.stream_errno == 0 && i_stream_get_data_size(stream->parent) > 0) { /* base64 input with a partial block */ data = i_stream_get_data(stream->parent, &size); io_stream_set_error(&stream->iostream, "base64 input ends with a partial block: 0x%s", binary_to_hex(data, size)); stream->istream.stream_errno = EINVAL; } return ret; } /* encode as many blocks as fits into destination buffer */ pre_count = stream->pos - stream->skip; while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ; post_count = stream->pos - stream->skip; } while (ret == 0 && pre_count == post_count); if (ret < 0 && pre_count == post_count) return ret; i_assert(post_count > pre_count); return post_count - pre_count; } static void i_stream_base64_decoder_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { if (v_offset < stream->istream.v_offset) { /* seeking backwards - go back to beginning and seek forward from there. */ stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; i_stream_seek(stream->parent, 0); } i_stream_default_seek_nonseekable(stream, v_offset, mark); } struct istream * i_stream_create_base64_decoder(struct istream *input) { struct base64_decoder_istream *bstream; bstream = i_new(struct base64_decoder_istream, 1); bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; bstream->istream.read = i_stream_base64_decoder_read; bstream->istream.seek = i_stream_base64_decoder_seek; bstream->istream.istream.readable_fd = FALSE; bstream->istream.istream.blocking = input->blocking; bstream->istream.istream.seekable = input->seekable; return i_stream_create(&bstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/test-ostream-failure-at.c0000644000175000017500000000360713123174404016511 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "ostream.h" #include "ostream-failure-at.h" #define TEST_DATA_LENGTH 128 #define TEST_ERRMSG "test-ostream-failure-at error triggered" void test_ostream_failure_at(void) { unsigned char test_data[TEST_DATA_LENGTH]; struct ostream *output, *buf_output; buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 256); unsigned int i; test_begin("ostream failure at"); for (i = 0; i < sizeof(test_data); i++) test_data[i] = i; for (i = 0; i < TEST_DATA_LENGTH; i++) { buf_output = o_stream_create_buffer(buf); output = o_stream_create_failure_at(buf_output, i, TEST_ERRMSG); if (i > 0) test_assert(o_stream_send(output, test_data, sizeof(test_data)) == (int)i); test_assert_idx(o_stream_send(output, test_data, sizeof(test_data)) == -1 && output->offset == i && output->stream_errno == EIO && strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0, i); o_stream_destroy(&output); o_stream_destroy(&buf_output); } /* shouldn't fail */ buf_output = o_stream_create_buffer(buf); output = o_stream_create_failure_at(buf_output, TEST_DATA_LENGTH, TEST_ERRMSG); test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); test_assert(o_stream_flush(output) > 0 && output->offset == TEST_DATA_LENGTH && output->stream_errno == 0); o_stream_destroy(&output); o_stream_destroy(&buf_output); /* fail at flush */ buf_output = o_stream_create_buffer(buf); output = o_stream_create_failure_at_flush(buf_output, TEST_ERRMSG); test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); test_assert(o_stream_flush(output) < 0 && output->stream_errno == EIO && strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0); o_stream_destroy(&output); o_stream_destroy(&buf_output); test_end(); } dovecot-2.2.33.2/src/lib/istream-file-private.h0000644000175000017500000000105413165463624016076 00000000000000#ifndef ISTREAM_FILE_PRIVATE_H #define ISTREAM_FILE_PRIVATE_H #include "istream-private.h" struct file_istream { struct istream_private istream; uoff_t skip_left; unsigned int file:1; unsigned int autoclose_fd:1; unsigned int seen_eof:1; }; struct istream * i_stream_create_file_common(struct file_istream *fstream, int fd, const char *path, size_t max_buffer_size, bool autoclose_fd); ssize_t i_stream_file_read(struct istream_private *stream); void i_stream_file_close(struct iostream_private *stream, bool close_parent); #endif dovecot-2.2.33.2/src/lib/file-cache.h0000644000175000017500000000312513123174404014013 00000000000000#ifndef FILE_CACHE_H #define FILE_CACHE_H /* Create a new file cache. It works very much like file-backed mmap()ed memory, but it works more nicely with remote filesystems (no SIGBUS). */ struct file_cache *file_cache_new(int fd); struct file_cache *file_cache_new_path(int fd, const char *path); /* Destroy the cache and set cache pointer to NULL. */ void file_cache_free(struct file_cache **cache); /* Change cached file descriptor. Invalidates the whole cache. */ void file_cache_set_fd(struct file_cache *cache, int fd); /* Change the memory allocated for the cache. This can be used to immediately set the maximum size so there's no need to grow the memory area with possibly slow copying. */ int file_cache_set_size(struct file_cache *cache, uoff_t size); /* Read data from file, returns how many bytes was actually read or -1 if error occurred. */ ssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size); /* Returns pointer to beginning of cached file. Only parts of the returned memory that are valid are the ones that have been file_cache_read(). Note that the pointer may become invalid after calling file_cache_read(). */ const void *file_cache_get_map(struct file_cache *cache, size_t *size_r); /* Update cached memory area. Mark fully written pages as cached. */ void file_cache_write(struct file_cache *cache, const void *data, size_t size, uoff_t offset); /* Invalidate cached memory area. It will be read again next time it's tried to be accessed. */ void file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size); #endif dovecot-2.2.33.2/src/lib/test-wildcard-match.c0000644000175000017500000000200513165463624015675 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "wildcard-match.h" static struct { const char *data; const char *mask; bool result; } tests[] = { { "foo", "*", TRUE }, { "foo", "*foo*", TRUE }, { "foo", "foo", TRUE }, { "foo", "f*o*o", TRUE }, { "foo", "f??", TRUE }, { "foo", "f?o", TRUE }, { "foo", "*??", TRUE }, { "foo", "???", TRUE }, { "foo", "f??*", TRUE }, { "foo", "???*", TRUE }, { "foo", "", FALSE }, { "foo", "f", FALSE }, { "foo", "fo", FALSE }, { "foo", "fooo", FALSE }, { "foo", "????", FALSE }, { "foo", "f*o*o*o", FALSE }, { "foo", "f???*", FALSE }, { "*foo", "foo", FALSE }, { "foo*", "foo", FALSE }, { "*foo*", "foo", FALSE }, { "", "*", TRUE }, { "", "", TRUE }, { "", "?", FALSE } }; void test_wildcard_match(void) { unsigned int i; test_begin("wildcard_match()"); for (i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(wildcard_match(tests[i].data, tests[i].mask) == tests[i].result, i); } test_end(); } dovecot-2.2.33.2/src/lib/iostream.h0000644000175000017500000000033313123174404013654 00000000000000#ifndef IOSTREAM_H #define IOSTREAM_H /* Returns human-readable reason for why iostream was disconnected. */ const char *io_stream_get_disconnect_reason(struct istream *input, struct ostream *output); #endif dovecot-2.2.33.2/src/lib/abspath.h0000644000175000017500000000151213165463624013466 00000000000000#ifndef ABSPATH_H #define ABSPATH_H /* Returns path as absolute path. If it's not already absolute path, it's assumed to be relative to current working directory. */ const char *t_abspath(const char *path); /* Like t_abspath(), but path is relative to given root. */ const char *t_abspath_to(const char *path, const char *root); /* Returns current directory, allocated from data stack. */ int t_get_current_dir(const char **dir_r); /* Returns symlink destination, allocated from data stack. */ int t_readlink(const char *path, const char **dest_r); /* Update binpath to be absolute: a) begins with '/' -> no change b) contains '/' -> assume relative to working directory c) set to first executable that's found from $PATH If no usable binary was found, return FALSE. */ bool t_binary_abspath(const char **binpath); #endif dovecot-2.2.33.2/src/lib/test-printf-format-fix.c0000644000175000017500000000552013123174404016360 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ /* Unit tests for printf-format-fix helper */ #include "test-lib.h" #include "printf-format-fix.h" #include struct format_fix_rewrites { const char *input; const char *output; size_t length; }; static void test_unchanged() { static const char *tests[] = { "Hello world", "Embedded %%, %u, %f, etc. are OK", "%%doesn't cause confusion in %%m and %%n", }; unsigned int i; test_begin("printf_format_fix(safe)"); for (i = 0; i < N_ELEMENTS(tests); ++i) { size_t len; T_BEGIN { test_assert_idx(printf_format_fix(tests[i]) == tests[i], i); test_assert_idx(printf_format_fix_get_len(tests[i], &len) == tests[i], i); test_assert_idx(len == strlen(tests[i]), i); } T_END; } test_end(); } static void test_ok_changes() { static const char *tests[] = { "OK to have a trailing %m", "%m can appear at the start too", "Even %m in the middle with a confusing %%m elsewhere is OK", }; unsigned int i; const char *needle; unsigned int needlen; int old_errno = errno; test_begin("printf_format_fix(rewrites)"); errno = EINVAL; needle = strerror(errno); i_assert(needle != NULL); needlen = strlen(needle); for (i = 0; i < N_ELEMENTS(tests); ++i) { size_t len; char const *chgd; char const *insert; unsigned int offs; T_BEGIN { chgd = printf_format_fix(tests[i]); test_assert_idx(chgd != tests[i], i); insert = strstr(chgd, needle); test_assert_idx(insert != NULL, i); offs = insert - chgd; test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i); test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i); test_assert_idx(strcmp(chgd+offs+needlen, tests[i]+offs+2) == 0, i); chgd = printf_format_fix_get_len(tests[i], &len); test_assert_idx(chgd != tests[i], i); test_assert_idx(len == strlen(chgd), i); insert = strstr(chgd, needle); test_assert_idx(insert != NULL, i); offs = insert - chgd; test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i); test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i); test_assert_idx(memcmp(chgd+offs+needlen, tests[i]+offs+2, len-needlen-offs) == 0, i); } T_END; } errno = old_errno; test_end(); } void test_printf_format_fix() { test_unchanged(); test_ok_changes(); } /* Want to test the panics too? go for it! */ enum fatal_test_state fatal_printf_format_fix(unsigned int stage) { static const char *fatals[] = { "no no no %n's", "no no no %-1234567890123n's with extra stuff", "%m allowed once, but not twice: %m", "%m must not obscure a later %n", "definitely can't have a tailing %", }; if(stage >= N_ELEMENTS(fatals)) { test_end(); return FATAL_TEST_FINISHED; } if(stage == 0) test_begin("fatal_printf_format_fix"); /* let's crash! */ (void)printf_format_fix(fatals[stage]); return FATAL_TEST_FAILURE; } dovecot-2.2.33.2/src/lib/macros.h0000644000175000017500000001730713165463624013341 00000000000000#ifndef MACROS_H #define MACROS_H /* several useful macros, mostly from glib.h */ #ifndef NULL # define NULL ((void *)0) #endif #ifndef FALSE # define FALSE (0) #endif #ifndef TRUE # define TRUE (!FALSE) #endif #define N_ELEMENTS(arr) \ (sizeof(arr) / sizeof((arr)[0])) #define MEM_ALIGN(size) \ (((size) + MEM_ALIGN_SIZE-1) & ~((size_t) MEM_ALIGN_SIZE-1)) #define PTR_OFFSET(ptr, offset) \ ((void *) (((unsigned char *) (ptr)) + (offset))) #define CONST_PTR_OFFSET(ptr, offset) \ ((const void *) (((const unsigned char *) (ptr)) + (offset))) /* Don't use simply MIN/MAX, as they're often defined elsewhere in include files that are included after this file generating tons of warnings. */ #define I_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define I_MAX(a, b) (((a) > (b)) ? (a) : (b)) /* make it easier to cast from/to pointers. assumes that sizeof(size_t) == sizeof(void *) and they're both the largest datatypes that are allowed to be used. so, long long isn't safe with these. */ #define POINTER_CAST(i) \ ((void *) ((char *) NULL + (i))) #define POINTER_CAST_TO(p, type) \ ((type) ((const char *) (p) - (const char *) NULL)) /* Define VA_COPY() to do the right thing for copying va_list variables. config.h may have already defined VA_COPY as va_copy or __va_copy. */ #ifndef VA_COPY # if defined (__GNUC__) && defined (__PPC__) && \ (defined (_CALL_SYSV) || defined (_WIN32)) # define VA_COPY(ap1, ap2) (*(ap1) = *(ap2)) # elif defined (VA_COPY_AS_ARRAY) # define VA_COPY(ap1, ap2) memmove ((ap1), (ap2), sizeof (va_list)) # else /* va_list is a pointer */ # define VA_COPY(ap1, ap2) ((ap1) = (ap2)) # endif /* va_list is a pointer */ #endif /* Provide convenience macros for handling structure * fields through their offsets. */ #define STRUCT_MEMBER_P(struct_p, struct_offset) \ ((void *) ((char *) (struct_p) + (long) (struct_offset))) #define CONST_STRUCT_MEMBER_P(struct_p, struct_offset) \ ((const void *) ((const char *) (struct_p) + (long) (struct_offset))) /* Provide simple macro statement wrappers (adapted from Perl): STMT_START { statements; } STMT_END; can be used as a single statement, as in if (x) STMT_START { ... } STMT_END; else ... For gcc we will wrap the statements within `({' and `})' braces. For SunOS they will be wrapped within `if (1)' and `else (void) 0', and otherwise within `do' and `while (0)'. */ #if !(defined (STMT_START) && defined (STMT_END)) # if defined (__GNUC__) && !defined (__cplusplus) && \ !defined (__STRICT_ANSI__) && !defined (PEDANTIC) # define STMT_START (void)( # define STMT_END ) # else # if (defined (sun) || defined (__sun__)) # define STMT_START if (1) # define STMT_END else (void)0 # else # define STMT_START do # define STMT_END while (0) # endif # endif #endif /* Provide macros to feature the GCC function attribute. */ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) # define ATTRS_DEFINED # define ATTR_FORMAT(format_idx, arg_idx) \ __attribute__((format (printf, format_idx, arg_idx))) # define ATTR_FORMAT_ARG(arg_idx) \ __attribute__((format_arg (arg_idx))) # define ATTR_SCANF(format_idx, arg_idx) \ __attribute__((format (scanf, format_idx, arg_idx))) # define ATTR_STRFTIME(format_idx) \ __attribute__((format (strftime, format_idx, 0))) # define ATTR_UNUSED __attribute__((unused)) # define ATTR_NORETURN __attribute__((noreturn)) # define ATTR_CONST __attribute__((const)) # define ATTR_PURE __attribute__((pure)) #else # define ATTR_FORMAT(format_idx, arg_idx) # define ATTR_FORMAT_ARG(arg_idx) # define ATTR_SCANF(format_idx, arg_idx) # define ATTR_STRFTIME(format_idx) # define ATTR_UNUSED # define ATTR_NORETURN # define ATTR_CONST # define ATTR_PURE #endif #ifdef HAVE_ATTR_NULL # define ATTR_NULL(...) __attribute__((null(__VA_ARGS__))) #else # define ATTR_NULL(...) #endif #ifdef HAVE_ATTR_NOWARN_UNUSED_RESULT # define ATTR_NOWARN_UNUSED_RESULT __attribute__((nowarn_unused_result)) #else # define ATTR_NOWARN_UNUSED_RESULT #endif #if __GNUC__ > 2 # define ATTR_MALLOC __attribute__((malloc)) #else # define ATTR_MALLOC #endif #if __GNUC__ > 3 /* GCC 4.0 and later */ # define ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) # define ATTR_SENTINEL __attribute__((sentinel)) #else # define ATTR_WARN_UNUSED_RESULT # define ATTR_SENTINEL #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) /* GCC 4.3 and later */ # define ATTR_HOT __attribute__((hot)) # define ATTR_COLD __attribute__((cold)) #else # define ATTR_HOT # define ATTR_COLD #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) /* GCC 4.9 and later */ # define ATTR_RETURNS_NONNULL __attribute__((returns_nonnull)) #else # define ATTR_RETURNS_NONNULL #endif /* Macros to provide type safety for callback functions' context parameters */ #ifdef HAVE_TYPE_CHECKS # define CALLBACK_TYPECHECK(callback, type) \ (COMPILE_ERROR_IF_TRUE(!__builtin_types_compatible_p( \ typeof(&callback), type)) ? 1 : 0) #else # define CALLBACK_TYPECHECK(callback, type) 0 #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)) && !defined(__cplusplus) # define COMPILE_ERROR_IF_TRUE(condition) \ (sizeof(char[1 - 2 * ((condition) ? 1 : 0)]) - 1) #else # define COMPILE_ERROR_IF_TRUE(condition) 0 #endif #ifdef HAVE_TYPE_CHECKS # define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) \ COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(_a), typeof(_b))) #define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) \ COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(_a1), typeof(_b)) && \ !__builtin_types_compatible_p(typeof(_a2), typeof(_b))) #else # define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) 0 # define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) 0 #endif #if __GNUC__ > 2 # define unlikely(expr) __builtin_expect(!!(expr), 0) # define likely(expr) __builtin_expect(!!(expr), 1) #else # define unlikely(expr) expr # define likely(expr) expr #endif #if defined(__clang__) && ((__clang_major__ > 4) || (__clang_major__ == 3 && __clang_minor__ >= 9)) # define ATTR_UNSIGNED_WRAPS __attribute__((no_sanitize("integer"))) #else # define ATTR_UNSIGNED_WRAPS #endif /* Provide macros for error handling. */ #ifdef DISABLE_ASSERTS # define i_assert(expr) #elif defined (__GNUC__) && !defined (__STRICT_ANSI__) #define i_assert(expr) STMT_START{ \ if (unlikely(!(expr))) \ i_panic("file %s: line %d (%s): assertion failed: (%s)", \ __FILE__, \ __LINE__, \ __FUNCTION__, \ #expr); }STMT_END #else /* !__GNUC__ */ #define i_assert(expr) STMT_START{ \ if (unlikely(!(expr))) \ i_panic("file %s: line %d: assertion failed: (%s)", \ __FILE__, \ __LINE__, \ #expr); }STMT_END #endif /* Close the fd and set it to -1. This assert-crashes if fd == 0. Normally this would happen only if an uninitialized fd is attempted to be closed, which is a bug. */ #define i_close_fd(fd) STMT_START { \ i_assert(*fd > 0); \ if (unlikely(close_keep_errno(fd) < 0)) \ i_error("close(%d[%s:%d]) failed: %m", \ *(fd), __FILE__, __LINE__); \ } STMT_END #define i_unreached() \ i_panic("file %s: line %d: unreached", __FILE__, __LINE__) /* Convenience macros to test the versions of dovecot. */ #if defined DOVECOT_VERSION_MAJOR && defined DOVECOT_VERSION_MINOR # define DOVECOT_PREREQ(maj, min) \ ((DOVECOT_VERSION_MAJOR << 16) + DOVECOT_VERSION_MINOR >= ((maj) << 16) + (min)) #else # define DOVECOT_PREREQ(maj, min) 0 #endif #ifdef __cplusplus # undef STATIC_ARRAY # define STATIC_ARRAY #endif /* Convenience wrappers for initializing a struct */ #define i_zero(p) memset(p, 0, sizeof(*(p))) #define i_zero_safe(p) safe_memset(p, 0, sizeof(*(p))) #endif dovecot-2.2.33.2/src/lib/istream-hash.h0000644000175000017500000000054313123174404014421 00000000000000#ifndef ISTREAM_HASH_H #define ISTREAM_HASH_H struct hash_method; /* hash_context must be allocated and initialized by caller. This istream will simply call method->loop() for all the data going through the istream. */ struct istream * i_stream_create_hash(struct istream *input, const struct hash_method *method, void *hash_context); #endif dovecot-2.2.33.2/src/lib/istream-base64-encoder.c0000644000175000017500000001247113123174404016175 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "istream-private.h" #include "istream-base64.h" struct base64_encoder_istream { struct istream_private istream; /* current encoded line length. */ size_t cur_line_len; unsigned int chars_per_line; bool crlf; }; static int i_stream_read_parent(struct istream_private *stream) { size_t size; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size >= 3) return 1; /* we have less than one base64 block. see if there is more data available. */ ret = i_stream_read(stream->parent); if (ret <= 0) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); return 1; } static int i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0 || (size < 3 && !stream->parent->eof)) return 0; if (bstream->cur_line_len == bstream->chars_per_line) { /* @UNSAFE: end of line, add newline */ if (!i_stream_try_alloc(stream, bstream->crlf ? 2 : 1, &avail)) return -2; if (bstream->crlf) stream->w_buffer[stream->pos++] = '\r'; stream->w_buffer[stream->pos++] = '\n'; bstream->cur_line_len = 0; } i_stream_try_alloc(stream, (size+2)/3*4, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 2) / 3 * 4 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 4) * 3; if (size == 0) return -2; } else if (!stream->parent->eof && size % 3 != 0) { /* encode 3 chars at a time, so base64_encode() doesn't add '=' characters in the middle of the stream */ size -= (size % 3); } i_assert(size != 0); if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) { size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3; i_assert(size != 0); } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); base64_encode(data, size, &buf); i_assert(buf.used > 0); bstream->cur_line_len += buf.used; i_assert(bstream->cur_line_len <= bstream->chars_per_line); stream->pos += buf.used; i_stream_skip(stream->parent, size); return 1; } static ssize_t i_stream_base64_encoder_read(struct istream_private *stream) { struct base64_encoder_istream *bstream = (struct base64_encoder_istream *)stream; size_t pre_count, post_count; int ret; do { ret = i_stream_read_parent(stream); if (ret == 0) return 0; if (ret < 0) { if (i_stream_get_data_size(stream->parent) == 0) return -1; /* add the final partial block */ } /* encode as many lines as fits into destination buffer */ pre_count = stream->pos - stream->skip; while ((ret = i_stream_base64_try_encode_line(bstream)) > 0) ; post_count = stream->pos - stream->skip; } while (ret == 0 && pre_count == post_count); if (ret < 0 && pre_count == post_count) return ret; i_assert(post_count > pre_count); return post_count - pre_count; } static void i_stream_base64_encoder_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { struct base64_encoder_istream *bstream = (struct base64_encoder_istream *)stream; if (v_offset < stream->istream.v_offset) { /* seeking backwards - go back to beginning and seek forward from there. */ stream->parent_expected_offset = stream->parent_start_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = 0; bstream->cur_line_len = 0; i_stream_seek(stream->parent, 0); } i_stream_default_seek_nonseekable(stream, v_offset, mark); } static int i_stream_base64_encoder_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct base64_encoder_istream *bstream = (struct base64_encoder_istream *)stream; const struct stat *st; off_t newlines, size; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; /* calculate size of encoded data */ size = (st->st_size / 3) * 4 + ((st->st_size % 3) == 0 ? 0 : 4); /* update size with added newlines */ newlines = (size / bstream->chars_per_line - 1) + ((size % bstream->chars_per_line) == 0 ? 0 : 1); size += newlines * (bstream->crlf ? 2 : 1); stream->statbuf.st_size = size; return 0; } struct istream * i_stream_create_base64_encoder(struct istream *input, unsigned int chars_per_line, bool crlf) { struct base64_encoder_istream *bstream; i_assert(chars_per_line % 4 == 0); bstream = i_new(struct base64_encoder_istream, 1); bstream->chars_per_line = chars_per_line; bstream->crlf = crlf; bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; bstream->istream.read = i_stream_base64_encoder_read; bstream->istream.seek = i_stream_base64_encoder_seek; bstream->istream.stat = i_stream_base64_encoder_stat; bstream->istream.istream.readable_fd = FALSE; bstream->istream.istream.blocking = input->blocking; bstream->istream.istream.seekable = input->seekable; return i_stream_create(&bstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/mempool-datastack.c0000644000175000017500000000704313165463624015451 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mempool.h" static const char *pool_data_stack_get_name(pool_t pool); static void pool_data_stack_ref(pool_t pool); static void pool_data_stack_unref(pool_t *pool); static void *pool_data_stack_malloc(pool_t pool, size_t size); static void pool_data_stack_free(pool_t pool, void *mem); static void *pool_data_stack_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size); static void pool_data_stack_clear(pool_t pool); static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool); static struct pool_vfuncs static_data_stack_pool_vfuncs = { pool_data_stack_get_name, pool_data_stack_ref, pool_data_stack_unref, pool_data_stack_malloc, pool_data_stack_free, pool_data_stack_realloc, pool_data_stack_clear, pool_data_stack_get_max_easy_alloc_size }; static const struct pool static_data_stack_pool = { .v = &static_data_stack_pool_vfuncs, .alloconly_pool = TRUE, .datastack_pool = TRUE }; struct datastack_pool { struct pool pool; int refcount; unsigned int data_stack_frame; }; pool_t pool_datastack_create(void) { struct datastack_pool *dpool; dpool = t_new(struct datastack_pool, 1); dpool->pool = static_data_stack_pool; dpool->refcount = 1; dpool->data_stack_frame = data_stack_frame; return &dpool->pool; } static const char *pool_data_stack_get_name(pool_t pool ATTR_UNUSED) { return "data stack"; } static void pool_data_stack_ref(pool_t pool) { struct datastack_pool *dpool = (struct datastack_pool *) pool; if (unlikely(dpool->data_stack_frame != data_stack_frame)) i_panic("pool_data_stack_ref(): stack frame changed"); dpool->refcount++; } static void pool_data_stack_unref(pool_t *pool) { struct datastack_pool *dpool = (struct datastack_pool *)*pool; if (unlikely(dpool->data_stack_frame != data_stack_frame)) i_panic("pool_data_stack_unref(): stack frame changed"); dpool->refcount--; i_assert(dpool->refcount >= 0); *pool = NULL; } static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size) { struct datastack_pool *dpool = (struct datastack_pool *) pool; if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); if (unlikely(dpool->data_stack_frame != data_stack_frame)) i_panic("pool_data_stack_malloc(): stack frame changed"); return t_malloc0(size); } static void pool_data_stack_free(pool_t pool, void *mem ATTR_UNUSED) { struct datastack_pool *dpool = (struct datastack_pool *) pool; if (unlikely(dpool->data_stack_frame != data_stack_frame)) i_panic("pool_data_stack_free(): stack frame changed"); } static void *pool_data_stack_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size) { struct datastack_pool *dpool = (struct datastack_pool *) pool; void *new_mem; /* @UNSAFE */ if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size); if (unlikely(dpool->data_stack_frame != data_stack_frame)) i_panic("pool_data_stack_realloc(): stack frame changed"); if (mem == NULL) return pool_data_stack_malloc(pool, new_size); if (old_size >= new_size) return mem; if (!t_try_realloc(mem, new_size)) { new_mem = t_malloc(new_size); memcpy(new_mem, mem, old_size); mem = new_mem; } memset((char *) mem + old_size, 0, new_size - old_size); return mem; } static void pool_data_stack_clear(pool_t pool ATTR_UNUSED) { } static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) { return t_get_bytes_available(); } dovecot-2.2.33.2/src/lib/malloc-overflow.h0000644000175000017500000000400713123174404015143 00000000000000#ifndef MALLOC_OVERFLOW_H #define MALLOC_OVERFLOW_H /* MALLOC_*() can be used to calculate memory allocation sizes. If there's an overflow, it'll cleanly panic instead of causing a potential buffer overflow. Note that *_malloc(size+1) doesn't need to use MALLOC_ADD(size, 1). It wraps to size==0 and the *_malloc() calls already panic if size==0. */ static inline size_t malloc_multiply_check(size_t a, size_t b, size_t sizeof_a, size_t sizeof_b, const char *fname, unsigned int linenum) { /* the first sizeof-checks are intended to optimize away this entire if-check for types that are small enough to never wrap size_t. */ if ((sizeof_a * 2 > sizeof(size_t) || sizeof_b * 2 > sizeof(size_t)) && b != 0 && (a > SIZE_MAX / b)) { i_panic("file %s: line %d: memory allocation overflow: " "%" PRIuSIZE_T" * %" PRIuSIZE_T, fname, linenum, a, b); } return a * b; } #ifndef STATIC_CHECKER # define MALLOC_MULTIPLY(a, b) \ malloc_multiply_check(a, b, sizeof(a), sizeof(b), __FILE__, __LINE__) #else /* avoid warning every time about sizeof(b) when b contains any arithmetic */ # define MALLOC_MULTIPLY(a, b) \ malloc_multiply_check(a, b, sizeof(a), sizeof(size_t), __FILE__, __LINE__) #endif static inline size_t malloc_add_check(size_t a, size_t b, size_t sizeof_a, size_t sizeof_b, const char *fname, unsigned int linenum) { /* the first sizeof-checks are intended to optimize away this entire if-check for types that are small enough to never wrap size_t. */ if ((sizeof_a >= sizeof(size_t) || sizeof_b >= sizeof(size_t)) && SIZE_MAX - a < b) { i_panic("file %s: line %d: memory allocation overflow: " "%" PRIuSIZE_T" + %" PRIuSIZE_T, fname, linenum, a, b); } return a + b; } #ifndef STATIC_CHECKER # define MALLOC_ADD(a, b) \ malloc_add_check(a, b, sizeof(a), sizeof(b), __FILE__, __LINE__) #else /* avoid warning every time about sizeof(b) when b contains any arithmetic */ # define MALLOC_ADD(a, b) \ malloc_add_check(a, b, sizeof(a), sizeof(size_t), __FILE__, __LINE__) #endif #endif dovecot-2.2.33.2/src/lib/hmac.h0000644000175000017500000000215113123174404012741 00000000000000#ifndef HMAC_H #define HMAC_H #include "hash-method.h" #include "sha1.h" #define HMAC_MAX_CONTEXT_SIZE 256 struct hmac_context_priv { char ctx[HMAC_MAX_CONTEXT_SIZE]; char ctxo[HMAC_MAX_CONTEXT_SIZE]; const struct hash_method *hash; }; struct hmac_context { union { struct hmac_context_priv priv; uint64_t padding_requirement; } u; }; void hmac_init(struct hmac_context *ctx, const unsigned char *key, size_t key_len, const struct hash_method *meth); void hmac_final(struct hmac_context *ctx, unsigned char *digest); static inline void hmac_update(struct hmac_context *_ctx, const void *data, size_t size) { struct hmac_context_priv *ctx = &_ctx->u.priv; ctx->hash->loop(ctx->ctx, data, size); } buffer_t *t_hmac_data(const struct hash_method *meth, const unsigned char *key, size_t key_len, const void *data, size_t data_len); buffer_t *t_hmac_buffer(const struct hash_method *meth, const unsigned char *key, size_t key_len, const buffer_t *data); buffer_t *t_hmac_str(const struct hash_method *meth, const unsigned char *key, size_t key_len, const char *data); #endif dovecot-2.2.33.2/src/lib/test-ostream-buffer.c0000644000175000017500000000566313165463624015750 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "str.h" #include "randgen.h" #include "istream.h" #include "ostream.h" #define MAX_BUFSIZE 256 static void test_ostream_buffer_random_once(void) { buffer_t *buffer; struct ostream *output; char buf[MAX_BUFSIZE*4], randbuf[MAX_BUFSIZE]; unsigned int i, offset, size; buffer = buffer_create_dynamic(default_pool, 8); memset(buf, 0, sizeof(buf)); output = o_stream_create_buffer(buffer); o_stream_cork(output); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf, randbuf, size); test_assert(o_stream_send(output, buf, size) > 0); for (i = 0; i < 10; i++) { offset = rand() % (MAX_BUFSIZE*3); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf + offset, randbuf, size); test_assert(o_stream_pwrite(output, randbuf, size, offset) == 0); if (rand() % 10 == 0) test_assert(o_stream_flush(output) > 0); } test_assert(o_stream_flush(output) > 0); o_stream_uncork(output); i_assert(buffer->used <= MAX_BUFSIZE*4); test_assert(memcmp(buf, buffer->data, buffer->used) == 0); o_stream_unref(&output); buffer_free(&buffer); } static void test_ostream_buffer_random(void) { unsigned int i; test_begin("ostream buffer pwrite random"); for (i = 0; i < 100; i++) T_BEGIN { test_ostream_buffer_random_once(); } T_END; test_end(); } static void test_ostream_buffer_size(void) { struct ostream *output; string_t *str = t_str_new(64); test_begin("ostream buffer size/available"); output = o_stream_create_buffer(str); test_assert(o_stream_get_buffer_used_size(output) == 0); test_assert(o_stream_get_buffer_avail_size(output) == (size_t)-1); /* test shrinking sink's max buffer size */ o_stream_set_max_buffer_size(output, 10); test_assert(o_stream_get_buffer_used_size(output) == 0); test_assert(o_stream_get_buffer_avail_size(output) == 10); /* partial send */ const char *partial_input = "01234567890123456789"; ssize_t ret = o_stream_send_str(output, partial_input); test_assert(ret == 10); test_assert(o_stream_get_buffer_used_size(output) == 10); test_assert(o_stream_get_buffer_avail_size(output) == 0); /* increase max buffer size so that it can hold the whole message */ o_stream_set_max_buffer_size(output, 100); test_assert(o_stream_get_buffer_used_size(output) == 10); test_assert(o_stream_get_buffer_avail_size(output) == 90); /* send the rest */ ret += o_stream_send_str(output, partial_input + ret); test_assert(ret == (ssize_t)strlen(partial_input)); test_assert(output->offset == str_len(str)); test_assert(o_stream_get_buffer_used_size(output) == 20); test_assert(o_stream_get_buffer_avail_size(output) == 80); /* check buffered data */ test_assert(strcmp(str_c(str), partial_input) == 0); o_stream_unref(&output); test_end(); } void test_ostream_buffer(void) { test_ostream_buffer_random(); test_ostream_buffer_size(); } dovecot-2.2.33.2/src/lib/istream-crlf.c0000644000175000017500000001131613165463624014432 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-crlf.h" struct crlf_istream { struct istream_private istream; unsigned int pending_cr:1; unsigned int last_cr:1; }; static int i_stream_crlf_read_common(struct crlf_istream *cstream) { struct istream_private *stream = &cstream->istream; size_t size, avail; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); } if (!i_stream_try_alloc(stream, size, &avail)) return -2; return 1; } static ssize_t i_stream_crlf_read_crlf(struct istream_private *stream) { struct crlf_istream *cstream = (struct crlf_istream *)stream; const unsigned char *data, *ptr, *src, *src_end; unsigned char *dest, *dest_end; size_t size, copy_len; ssize_t ret; ret = i_stream_crlf_read_common(cstream); if (ret <= 0) return ret; /* at least one byte was read */ data = i_stream_get_data(stream->parent, &size); dest = stream->w_buffer + stream->pos; dest_end = stream->w_buffer + stream->buffer_size; src = data; src_end = data + size; /* @UNSAFE: add missing CRs */ if (*src == '\n') { if (!cstream->last_cr && dest < dest_end) *dest++ = '\r'; if (dest < dest_end) { *dest++ = '\n'; src++; } } while (dest < dest_end) { ptr = memchr(src, '\n', src_end - src); if (ptr == NULL) ptr = src_end; /* copy data up to LF */ copy_len = ptr - src; if (dest + copy_len > dest_end) copy_len = dest_end - dest; if (copy_len > 0) { memcpy(dest, src, copy_len); dest += copy_len; src += copy_len; } i_assert(dest <= dest_end && src <= src_end); if (dest == dest_end || src == src_end) break; /* add the CR if necessary and copy the LF. (src >= data+1, because data[0]=='\n' was handled before this loop) */ if (src[-1] != '\r') *dest++ = '\r'; if (dest == dest_end) break; *dest++ = '\n'; src++; i_assert(src == ptr + 1); } i_assert(dest != stream->w_buffer); cstream->last_cr = dest[-1] == '\r'; i_stream_skip(stream->parent, src - data); ret = (dest - stream->w_buffer) - stream->pos; i_assert(ret > 0); stream->pos = dest - stream->w_buffer; return ret; } static ssize_t i_stream_crlf_read_lf(struct istream_private *stream) { struct crlf_istream *cstream = (struct crlf_istream *)stream; const unsigned char *data, *p; size_t i, dest, size, max; ssize_t ret; bool pending_cr; ret = i_stream_crlf_read_common(cstream); if (ret <= 0) return ret; data = i_stream_get_data(stream->parent, &size); /* @UNSAFE */ /* \r\n -> \n \r -> \r \r\r\n -> \r\n */ dest = stream->pos; pending_cr = cstream->pending_cr; for (i = 0; i < size && dest < stream->buffer_size; ) { if (data[i] == '\r') { if (pending_cr) { /* \r\r */ stream->w_buffer[dest++] = '\r'; } else { pending_cr = TRUE; } i++; } else if (data[i] == '\n') { /* [\r]\n */ pending_cr = FALSE; stream->w_buffer[dest++] = '\n'; i++; } else if (pending_cr) { /* \r */ pending_cr = FALSE; stream->w_buffer[dest++] = '\r'; } else { /* copy everything until the next \r */ max = I_MIN(size - i, stream->buffer_size - dest); p = memchr(data + i, '\r', max); if (p != NULL) max = p - (data+i); memcpy(stream->w_buffer + dest, data + i, max); dest += max; i += max; } } i_assert(i <= size); i_assert(dest <= stream->buffer_size); cstream->pending_cr = pending_cr; i_stream_skip(stream->parent, i); ret = dest - stream->pos; if (ret == 0) { i_assert(cstream->pending_cr && size == 1); return i_stream_crlf_read_lf(stream); } i_assert(ret > 0); stream->pos = dest; return ret; } static struct istream * i_stream_create_crlf_full(struct istream *input, bool crlf) { struct crlf_istream *cstream; cstream = i_new(struct crlf_istream, 1); cstream->istream.max_buffer_size = input->real_stream->max_buffer_size; cstream->istream.read = crlf ? i_stream_crlf_read_crlf : i_stream_crlf_read_lf; cstream->istream.istream.readable_fd = FALSE; cstream->istream.istream.blocking = input->blocking; cstream->istream.istream.seekable = FALSE; return i_stream_create(&cstream->istream, input, i_stream_get_fd(input)); } struct istream *i_stream_create_crlf(struct istream *input) { return i_stream_create_crlf_full(input, TRUE); } struct istream *i_stream_create_lf(struct istream *input) { return i_stream_create_crlf_full(input, FALSE); } dovecot-2.2.33.2/src/lib/ostream-rawlog.h0000644000175000017500000000055313165463624015013 00000000000000#ifndef OSTREAM_RAWLOG_H #define OSTREAM_RAWLOG_H struct ostream * o_stream_create_rawlog(struct ostream *output, const char *rawlog_path, int rawlog_fd, enum iostream_rawlog_flags flags); struct ostream * o_stream_create_rawlog_from_stream(struct ostream *output, struct ostream *rawlog_output, enum iostream_rawlog_flags flags); #endif dovecot-2.2.33.2/src/lib/array-decl.h0000644000175000017500000000140713123174404014057 00000000000000#ifndef ARRAY_DECL_H #define ARRAY_DECL_H #define ARRAY(array_type) union { struct array arr; array_type const *const *v; array_type **v_modifiable; } #define ARRAY_INIT { { NULL, 0 } } #define ARRAY_DEFINE_TYPE(name, array_type) \ union array ## __ ## name { struct array arr; array_type const *const *v; array_type **v_modifiable; } #define ARRAY_TYPE(name) \ union array ## __ ## name struct array { buffer_t *buffer; size_t element_size; }; ARRAY_DEFINE_TYPE(string, char *); ARRAY_DEFINE_TYPE(const_string, const char *); ARRAY_DEFINE_TYPE(uint8_t, uint8_t); ARRAY_DEFINE_TYPE(uint16_t, uint16_t); ARRAY_DEFINE_TYPE(uint32_t, uint32_t); ARRAY_DEFINE_TYPE(uint64_t, uint64_t); ARRAY_DEFINE_TYPE(uint, unsigned int); ARRAY_DEFINE_TYPE(void_array, void *); #endif dovecot-2.2.33.2/src/lib/strnum.c0000644000175000017500000003046013123174404013360 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "strnum.h" bool str_is_numeric(const char *str, char end_char) { if (*str == '\0' || *str == end_char) return FALSE; while (*str != '\0' && *str != end_char) { if (*str < '0' || *str > '9') return FALSE; str++; } return TRUE; } bool str_is_float(const char *str, char end_char) { bool dot_seen = FALSE; bool num_seen = FALSE; if (*str == '\0' || *str == end_char) return FALSE; while (*str != '\0' && *str != end_char) { if (*str == '.') { if (dot_seen || !num_seen) return FALSE; dot_seen = TRUE; num_seen = FALSE; str++; /* enforce that number follows dot */ continue; } if (*str < '0' || *str > '9') return FALSE; num_seen = TRUE; str++; } return num_seen; } /* * Unsigned decimal */ #define STR_PARSE_U__TEMPLATE(name, type) \ int name(const char *str, type *num_r, const char **endp_r) \ { \ uintmax_t l; \ if (str_parse_uintmax(str, &l, endp_r) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_PARSE_U__TEMPLATE(str_parse_uint, unsigned int) STR_PARSE_U__TEMPLATE(str_parse_ulong, unsigned long) STR_PARSE_U__TEMPLATE(str_parse_ullong, unsigned long long) STR_PARSE_U__TEMPLATE(str_parse_uint32, uint32_t) STR_PARSE_U__TEMPLATE(str_parse_uint64, uint64_t) #define STR_TO_U__TEMPLATE(name, type) \ int name(const char *str, type *num_r) \ { \ uintmax_t l; \ if (str_to_uintmax(str, &l) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_TO_U__TEMPLATE(str_to_uint, unsigned int) STR_TO_U__TEMPLATE(str_to_ulong, unsigned long) STR_TO_U__TEMPLATE(str_to_ullong, unsigned long long) STR_TO_U__TEMPLATE(str_to_uint32, uint32_t) STR_TO_U__TEMPLATE(str_to_uint64, uint64_t) int str_parse_uintmax(const char *str, uintmax_t *num_r, const char **endp_r) { uintmax_t n = 0; if (*str < '0' || *str > '9') return -1; for (; *str >= '0' && *str <= '9'; str++) { if (n >= ((uintmax_t)-1 / 10)) { if (n > (uintmax_t)-1 / 10) return -1; if ((uintmax_t)(*str - '0') > ((uintmax_t)-1 % 10)) return -1; } n = n * 10 + (*str - '0'); } if (endp_r != NULL) *endp_r = str; *num_r = n; return 0; } int str_to_uintmax(const char *str, uintmax_t *num_r) { const char *endp; uintmax_t n; int ret = str_parse_uintmax(str, &n, &endp); if ((ret != 0) || (*endp != '\0')) return -1; *num_r = n; return 0; } bool str_uint_equals(const char *str, uintmax_t num) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return FALSE; return l == num; } /* * Unsigned hexadecimal */ #define STR_PARSE_UHEX__TEMPLATE(name, type) \ int name(const char *str, type *num_r, const char **endp_r) \ { \ uintmax_t l; \ if (str_parse_uintmax_hex(str, &l, endp_r) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_PARSE_UHEX__TEMPLATE(str_parse_uint_hex, unsigned int) STR_PARSE_UHEX__TEMPLATE(str_parse_ulong_hex, unsigned long) STR_PARSE_UHEX__TEMPLATE(str_parse_ullong_hex, unsigned long long) STR_PARSE_UHEX__TEMPLATE(str_parse_uint32_hex, uint32_t) STR_PARSE_UHEX__TEMPLATE(str_parse_uint64_hex, uint64_t) #define STR_TO_UHEX__TEMPLATE(name, type) \ int name(const char *str, type *num_r) \ { \ uintmax_t l; \ if (str_to_uintmax_hex(str, &l) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_TO_UHEX__TEMPLATE(str_to_uint_hex, unsigned int) STR_TO_UHEX__TEMPLATE(str_to_ulong_hex, unsigned long) STR_TO_UHEX__TEMPLATE(str_to_ullong_hex, unsigned long long) STR_TO_UHEX__TEMPLATE(str_to_uint32_hex, uint32_t) STR_TO_UHEX__TEMPLATE(str_to_uint64_hex, uint64_t) static inline int _str_parse_hex(const char ch, unsigned int *hex_r) { switch (ch) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': *hex_r = (unsigned int)(ch - 'a' + 10); return 0; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': *hex_r = (unsigned int)(ch - 'A' + 10); return 0; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *hex_r = (unsigned int)(ch - '0'); return 0; default: break; } return -1; } int str_parse_uintmax_hex(const char *str, uintmax_t *num_r, const char **endp_r) { unsigned int hex; uintmax_t n = 0; if (_str_parse_hex(*str, &hex) < 0) return -1; do { if (n > (uintmax_t)-1 >> 4) return -1; n = (n << 4) + hex; str++; } while (_str_parse_hex(*str, &hex) >= 0); if (endp_r != NULL) *endp_r = str; *num_r = n; return 0; } int str_to_uintmax_hex(const char *str, uintmax_t *num_r) { const char *endp; uintmax_t n; int ret = str_parse_uintmax_hex(str, &n, &endp); if ((ret != 0) || (*endp != '\0')) return -1; *num_r = n; return 0; } /* * Unsigned octal */ #define STR_PARSE_UOCT__TEMPLATE(name, type) \ int name(const char *str, type *num_r, const char **endp_r) \ { \ uintmax_t l; \ if (str_parse_uintmax_oct(str, &l, endp_r) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_PARSE_UOCT__TEMPLATE(str_parse_uint_oct, unsigned int) STR_PARSE_UOCT__TEMPLATE(str_parse_ulong_oct, unsigned long) STR_PARSE_UOCT__TEMPLATE(str_parse_ullong_oct, unsigned long long) STR_PARSE_UOCT__TEMPLATE(str_parse_uint32_oct, uint32_t) STR_PARSE_UOCT__TEMPLATE(str_parse_uint64_oct, uint64_t) #define STR_TO_UOCT__TEMPLATE(name, type) \ int name(const char *str, type *num_r) \ { \ uintmax_t l; \ if (str_to_uintmax_oct(str, &l) < 0 || l > (type)-1) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_TO_UOCT__TEMPLATE(str_to_uint_oct, unsigned int) STR_TO_UOCT__TEMPLATE(str_to_ulong_oct, unsigned long) STR_TO_UOCT__TEMPLATE(str_to_ullong_oct, unsigned long long) STR_TO_UOCT__TEMPLATE(str_to_uint32_oct, uint32_t) STR_TO_UOCT__TEMPLATE(str_to_uint64_oct, uint64_t) int str_parse_uintmax_oct(const char *str, uintmax_t *num_r, const char **endp_r) { uintmax_t n = 0; if (*str < '0' || *str > '7') return -1; for (; *str >= '0' && *str <= '7'; str++) { if (n > (uintmax_t)-1 >> 3) return -1; n = (n << 3) + (*str - '0'); } if (endp_r != NULL) *endp_r = str; *num_r = n; return 0; } int str_to_uintmax_oct(const char *str, uintmax_t *num_r) { const char *endp; uintmax_t n; int ret = str_parse_uintmax_oct(str, &n, &endp); if ((ret != 0) || (*endp != '\0')) return -1; *num_r = n; return 0; } /* * Signed */ #define STR_PARSE_S__TEMPLATE(name, type, int_min, int_max) \ int name(const char *str, type *num_r, const char **endp_r) \ { \ intmax_t l; \ if (str_parse_intmax(str, &l, endp_r) < 0) \ return -1; \ if (l < int_min || l > int_max) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_PARSE_S__TEMPLATE(str_parse_int, int, INT_MIN, INT_MAX) STR_PARSE_S__TEMPLATE(str_parse_long, long, LONG_MIN, LONG_MAX) STR_PARSE_S__TEMPLATE(str_parse_llong, long long, LLONG_MIN, LLONG_MAX) STR_PARSE_S__TEMPLATE(str_parse_int32, int32_t, INT32_MIN, INT32_MAX) STR_PARSE_S__TEMPLATE(str_parse_int64, int64_t, INT64_MIN, INT64_MAX) #define STR_TO_S__TEMPLATE(name, type, int_min, int_max) \ int name(const char *str, type *num_r) \ { \ intmax_t l; \ if (str_to_intmax(str, &l) < 0) \ return -1; \ if (l < int_min || l > int_max) \ return -1; \ *num_r = (type)l; \ return 0; \ } STR_TO_S__TEMPLATE(str_to_int, int, INT_MIN, INT_MAX) STR_TO_S__TEMPLATE(str_to_long, long, LONG_MIN, LONG_MAX) STR_TO_S__TEMPLATE(str_to_llong, long long, LLONG_MIN, LLONG_MAX) STR_TO_S__TEMPLATE(str_to_int32, int32_t, INT32_MIN, INT32_MAX) STR_TO_S__TEMPLATE(str_to_int64, int64_t, INT64_MIN, INT64_MAX) int str_parse_intmax(const char *str, intmax_t *num_r, const char **endp_r) { bool neg = FALSE; uintmax_t l; if (*str == '-') { neg = TRUE; str++; } if (str_parse_uintmax(str, &l, endp_r) < 0) return -1; if (!neg) { if (l > INTMAX_MAX) return -1; *num_r = (intmax_t)l; } else { if (l > UINTMAX_MAX - (UINTMAX_MAX + INTMAX_MIN)) return -1; *num_r = (intmax_t)-l; } return 0; } int str_to_intmax(const char *str, intmax_t *num_r) { const char *endp; intmax_t n; int ret = str_parse_intmax(str, &n, &endp); if ((ret != 0) || (*endp != '\0')) return -1; *num_r = n; return 0; } /* * Special numeric types */ static int verify_xid(uintmax_t l, unsigned int result_size) { unsigned int result_bits; /* we assume that result is a signed type, but that it can never be negative */ result_bits = result_size*CHAR_BIT - 1; if ((l >> result_bits) != 0) return -1; return 0; } int str_to_uid(const char *str, uid_t *num_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (verify_xid(l, sizeof(*num_r)) < 0) return -1; *num_r = (uid_t)l; return 0; } int str_to_gid(const char *str, gid_t *num_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; /* OS X uses negative GIDs */ #ifndef __APPLE__ if (verify_xid(l, sizeof(*num_r)) < 0) return -1; #endif *num_r = (gid_t)l; return 0; } int str_to_pid(const char *str, pid_t *num_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (verify_xid(l, sizeof(*num_r)) < 0) return -1; *num_r = (pid_t)l; return 0; } int str_to_ino(const char *str, ino_t *num_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (verify_xid(l, sizeof(*num_r)) < 0) return -1; *num_r = (ino_t)l; return 0; } int str_to_uoff(const char *str, uoff_t *num_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (l > (uoff_t)-1) return -1; *num_r = (uoff_t)l; return 0; } int str_to_time(const char *str, time_t *num_r) { intmax_t l; if (str_to_intmax(str, &l) < 0) return -1; *num_r = (time_t)l; return 0; } STR_PARSE_U__TEMPLATE(str_parse_uoff, uoff_t) /* * Error handling */ const char *str_num_error(const char *str) { if (*str == '-') { if (!str_is_numeric(str + 1, '\0')) return "Not a valid number"; return "Number too small"; } else { if (!str_is_numeric(str, '\0')) return "Not a valid number"; return "Number too large"; } } dovecot-2.2.33.2/src/lib/test-hash-method.c0000644000175000017500000002201213123174404015200 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "mmap-util.h" #include "hash-method.h" #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) # define MAP_ANONYMOUS MAP_ANON #endif static unsigned char *buf; static unsigned int buf_size; static void test_hash_method_one(const struct hash_method *method) { unsigned char *ctx, *digest; unsigned int i; test_begin(t_strdup_printf("hash method %s", method->name)); ctx = i_malloc(method->context_size); digest = i_malloc(method->digest_size); method->init(ctx); /* make sure the code doesn't try to access data past boundaries */ for (i = 0; i < buf_size; i++) method->loop(ctx, buf + buf_size - i, i); method->result(ctx, digest); i_free(ctx); i_free(digest); test_end(); } static void test_hash_method_boundary(void) { unsigned int i; buf_size = mmap_get_page_size(); #ifdef MAP_ANONYMOUS buf = mmap(NULL, buf_size*2, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); mprotect(buf + buf_size, buf_size, PROT_NONE); #else buf = i_malloc(buf_size); #endif memset(buf, 0, buf_size); for (i = 0; hash_methods[i] != NULL; i++) test_hash_method_one(hash_methods[i]); } static void test_hash_methods_fips() { const char *last_method = NULL; struct { const char *method; const void *input; size_t ilen; size_t rounds; const void *output; size_t olen; } test_vectors[] = { { "md4", "", 0, 1, "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0", 128 / 8 }, { "md4", "abc", 3, 1, "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d", 128 / 8 }, { "md4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" "ghijklmnopqrstuvwxyz0123456789", 62, 1, "\x04\x3f\x85\x82\xf2\x41\xdb\x35" "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4", 128 / 8 }, { "md5", "", 0, 1, "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" "\xe9\x80\x09\x98\xec\xf8\x42\x7e", 128 / 8 }, { "md5", "abc", 3, 1, "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72", 128 / 8 }, { "md5", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" "ghijklmnopqrstuvwxyz0123456789", 62, 1, "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 128 / 8 }, { "sha1", "", 0, 1, "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d" "\x32\x55\xbf\xef\x95\x60\x18\x90" "\xaf\xd8\x07\x09", 160 / 8 }, { "sha1", "abc", 3, 1, "\xa9\x99\x3e\x36\x47\x06\x81\x6a" "\xba\x3e\x25\x71\x78\x50\xc2\x6c" "\x9c\xd0\xd8\x9d", 160 / 8 }, { "sha1", "abcdbcdecdefdefgefghfghighijhijk" "ijkljklmklmnlmnomnopnopq", 56, 1, "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e" "\xba\xae\x4a\xa1\xf9\x51\x29\xe5" "\xe5\x46\x70\xf1", 160 / 8 }, { "sha3-256", "", 0, 1, "\xa7\xff\xc6\xf8\xbf\x1e\xd7\x66" "\x51\xc1\x47\x56\xa0\x61\xd6\x62" "\xf5\x80\xff\x4d\xe4\x3b\x49\xfa" "\x82\xd8\x0a\x4b\x80\xf8\x43\x4a", 256 / 8 }, { "sha3-256", "\xb7\x71\xd5\xce\xf5\xd1\xa4\x1a" "\x93\xd1\x56\x43\xd7\x18\x1d\x2a" "\x2e\xf0\xa8\xe8\x4d\x91\x81\x2f" "\x20\xed\x21\xf1\x47\xbe\xf7\x32" "\xbf\x3a\x60\xef\x40\x67\xc3\x73" "\x4b\x85\xbc\x8c\xd4\x71\x78\x0f" "\x10\xdc\x9e\x82\x91\xb5\x83\x39" "\xa6\x77\xb9\x60\x21\x8f\x71\xe7" "\x93\xf2\x79\x7a\xea\x34\x94\x06" "\x51\x28\x29\x06\x5d\x37\xbb\x55" "\xea\x79\x6f\xa4\xf5\x6f\xd8\x89" "\x6b\x49\xb2\xcd\x19\xb4\x32\x15" "\xad\x96\x7c\x71\x2b\x24\xe5\x03" "\x2d\x06\x52\x32\xe0\x2c\x12\x74" "\x09\xd2\xed\x41\x46\xb9\xd7\x5d" "\x76\x3d\x52\xdb\x98\xd9\x49\xd3" "\xb0\xfe\xd6\xa8\x05\x2f\xbb", 1080 / 8, 1, "\xa1\x9e\xee\x92\xbb\x20\x97\xb6" "\x4e\x82\x3d\x59\x77\x98\xaa\x18" "\xbe\x9b\x7c\x73\x6b\x80\x59\xab" "\xfd\x67\x79\xac\x35\xac\x81\xb5", 256 / 8 }, { "sha3-256", "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3", 200, 1, "\x79\xf3\x8a\xde\xc5\xc2\x03\x07" "\xa9\x8e\xf7\x6e\x83\x24\xaf\xbf" "\xd4\x6c\xfd\x81\xb2\x2e\x39\x73" "\xc6\x5f\xa1\xbd\x9d\xe3\x17\x87", 256 / 8, }, { "sha3-256", "\xa3", 1, 200, "\x79\xf3\x8a\xde\xc5\xc2\x03\x07" "\xa9\x8e\xf7\x6e\x83\x24\xaf\xbf" "\xd4\x6c\xfd\x81\xb2\x2e\x39\x73" "\xc6\x5f\xa1\xbd\x9d\xe3\x17\x87", 256 / 8, }, { "sha3-512", "", 0, 1, "\xa6\x9f\x73\xcc\xa2\x3a\x9a\xc5" "\xc8\xb5\x67\xdc\x18\x5a\x75\x6e" "\x97\xc9\x82\x16\x4f\xe2\x58\x59" "\xe0\xd1\xdc\xc1\x47\x5c\x80\xa6" "\x15\xb2\x12\x3a\xf1\xf5\xf9\x4c" "\x11\xe3\xe9\x40\x2c\x3a\xc5\x58" "\xf5\x00\x19\x9d\x95\xb6\xd3\xe3" "\x01\x75\x85\x86\x28\x1d\xcd\x26", 512 / 8 }, { "sha3-512", "\xb7\x71\xd5\xce\xf5\xd1\xa4\x1a" "\x93\xd1\x56\x43\xd7\x18\x1d\x2a" "\x2e\xf0\xa8\xe8\x4d\x91\x81\x2f" "\x20\xed\x21\xf1\x47\xbe\xf7\x32" "\xbf\x3a\x60\xef\x40\x67\xc3\x73" "\x4b\x85\xbc\x8c\xd4\x71\x78\x0f" "\x10\xdc\x9e\x82\x91\xb5\x83\x39" "\xa6\x77\xb9\x60\x21\x8f\x71\xe7" "\x93\xf2\x79\x7a\xea\x34\x94\x06" "\x51\x28\x29\x06\x5d\x37\xbb\x55" "\xea\x79\x6f\xa4\xf5\x6f\xd8\x89" "\x6b\x49\xb2\xcd\x19\xb4\x32\x15" "\xad\x96\x7c\x71\x2b\x24\xe5\x03" "\x2d\x06\x52\x32\xe0\x2c\x12\x74" "\x09\xd2\xed\x41\x46\xb9\xd7\x5d" "\x76\x3d\x52\xdb\x98\xd9\x49\xd3" "\xb0\xfe\xd6\xa8\x05\x2f\xbb", 1080 / 8, 1, "\x75\x75\xa1\xfb\x4f\xc9\xa8\xf9" "\xc0\x46\x6b\xd5\xfc\xa4\x96\xd1" "\xcb\x78\x69\x67\x73\xa2\x12\xa5" "\xf6\x2d\x02\xd1\x4e\x32\x59\xd1" "\x92\xa8\x7e\xba\x44\x07\xdd\x83" "\x89\x35\x27\x33\x14\x07\xb6\xda" "\xda\xad\x92\x0d\xbc\x46\x48\x9b" "\x67\x74\x93\xce\x5f\x20\xb5\x95", 512 / 8 }, { "sha3-512", "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3", 200, 1, "\xe7\x6d\xfa\xd2\x20\x84\xa8\xb1" "\x46\x7f\xcf\x2f\xfa\x58\x36\x1b" "\xec\x76\x28\xed\xf5\xf3\xfd\xc0" "\xe4\x80\x5d\xc4\x8c\xae\xec\xa8" "\x1b\x7c\x13\xc3\x0a\xdf\x52\xa3" "\x65\x95\x84\x73\x9a\x2d\xf4\x6b" "\xe5\x89\xc5\x1c\xa1\xa4\xa8\x41" "\x6d\xf6\x54\x5a\x1c\xe8\xba\x00", 512 / 8, }, { "sha3-512", "\xa3", 1, 200, "\xe7\x6d\xfa\xd2\x20\x84\xa8\xb1" "\x46\x7f\xcf\x2f\xfa\x58\x36\x1b" "\xec\x76\x28\xed\xf5\xf3\xfd\xc0" "\xe4\x80\x5d\xc4\x8c\xae\xec\xa8" "\x1b\x7c\x13\xc3\x0a\xdf\x52\xa3" "\x65\x95\x84\x73\x9a\x2d\xf4\x6b" "\xe5\x89\xc5\x1c\xa1\xa4\xa8\x41" "\x6d\xf6\x54\x5a\x1c\xe8\xba\x00", 512 / 8, }, }; for(size_t i = 0; i < N_ELEMENTS(test_vectors); i++) { if (last_method == NULL || strcmp(last_method, test_vectors[i].method) != 0) { if (last_method != NULL) test_end(); last_method = test_vectors[i].method; test_begin(t_strdup_printf("hash method %s (test vectors)", last_method)); } const struct hash_method *method = hash_method_lookup(test_vectors[i].method); unsigned char context[method->context_size]; unsigned char result[method->digest_size]; test_assert_idx(method->digest_size == test_vectors[i].olen, i); method->init(context); for(size_t n = 0; n < test_vectors[i].rounds; n++) method->loop(context, test_vectors[i].input, test_vectors[i].ilen); method->result(context, result); test_assert_idx(memcmp(result, test_vectors[i].output, test_vectors[i].olen) == 0, i); } test_end(); } void test_hash_method(void) { test_hash_method_boundary(); test_hash_methods_fips(); } dovecot-2.2.33.2/src/lib/hex-binary.h0000644000175000017500000000101413123174404014074 00000000000000#ifndef HEX_BINARY_H #define HEX_BINARY_H /* Convert binary to hex digits allocating return value from data stack */ const char *binary_to_hex(const unsigned char *data, size_t size); const char *binary_to_hex_ucase(const unsigned char *data, size_t size); void binary_to_hex_append(string_t *dest, const unsigned char *data, size_t size); /* Convert hex to binary. data and dest may point to same value. Returns 0 if all ok, -1 if data is invalid. */ int hex_to_binary(const char *data, buffer_t *dest); #endif dovecot-2.2.33.2/src/lib/test-strescape.c0000644000175000017500000000541413165463624015012 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "strescape.h" struct strinput { const char *input; const char *output; }; void test_strescape(void) { static struct strinput unesc[] = { { "foo", "foo" }, { "\\\\\\\\\\\"\\\"\\\'\\\'", "\\\\\"\"\'\'" }, { "\\a\\n\\r\\", "anr" } }; static struct strinput tabesc[] = { { "foo", "foo" }, { "\001", "\0011" }, { "\t", "\001t" }, { "\r", "\001r" }, { "\n", "\001n" }, { "\001\001\t\t\r\r\n\n", "\0011\0011\001t\001t\001r\001r\001n\001n" } }; unsigned char buf[1 << CHAR_BIT]; const char *escaped, *tabstr, *unesc_str; string_t *str; unsigned int i; test_begin("str_escape"); for (i = 1; i < sizeof(buf); i++) buf[i-1] = i; buf[i-1] = '\0'; escaped = str_escape((char *)buf); test_assert(strlen(escaped) == (1 << CHAR_BIT) - 1 + 3); test_assert(escaped['\"'-1] == '\\'); /* 34 */ test_assert(escaped['\"'] == '\"'); test_assert(escaped['\''+1-1] == '\\'); /* 39 */ test_assert(escaped['\''+1] == '\''); test_assert(escaped['\\'+2-1] == '\\'); /* 92 */ test_assert(escaped['\\'+2] == '\\'); test_assert(strcmp(str_escape("\\\\\"\"\'\'"), "\\\\\\\\\\\"\\\"\\\'\\\'") == 0); test_end(); str = t_str_new(256); test_begin("str_unescape"); for (i = 0; i < N_ELEMENTS(unesc); i++) { test_assert(strcmp(str_unescape(t_strdup_noconst(unesc[i].input)), unesc[i].output) == 0); str_truncate(str, 0); str_append_unescaped(str, unesc[i].input, strlen(unesc[i].input)); test_assert(strcmp(str_c(str), unesc[i].output) == 0); } test_end(); test_begin("str_unescape_next"); escaped = "foo\"bar\\\"b\\\\az\"plop"; test_assert(str_unescape_next(&escaped, &unesc_str) == 0); test_assert(strcmp(unesc_str, "foo") == 0); test_assert(str_unescape_next(&escaped, &unesc_str) == 0); test_assert(strcmp(unesc_str, "bar\"b\\az") == 0); test_assert(str_unescape_next(&escaped, &unesc_str) == -1); escaped = "foo\\"; test_assert(str_unescape_next(&escaped, &unesc_str) == -1); test_end(); test_begin("str_tabescape"); for (i = 0; i < N_ELEMENTS(tabesc); i++) { test_assert(strcmp(t_str_tabunescape(tabesc[i].output), tabesc[i].input) == 0); test_assert(strcmp(str_tabunescape(t_strdup_noconst(tabesc[i].output)), tabesc[i].input) == 0); test_assert(strcmp(str_tabescape(tabesc[i].input), tabesc[i].output) == 0); str_truncate(str, 0); str_append_tabunescaped(str, tabesc[i].output, strlen(tabesc[i].output)); test_assert(strcmp(str_c(str), tabesc[i].input) == 0); } str_truncate(str, 0); tabstr = "\0012\001l\001"; str_append_tabunescaped(str, tabstr, strlen(tabstr)); test_assert(strcmp(str_c(str), "2l") == 0); test_assert(strcmp(str_c(str), str_tabunescape(t_strdup_noconst(tabstr))) == 0); test_end(); } dovecot-2.2.33.2/src/lib/aqueue.h0000644000175000017500000000205513123174404013321 00000000000000#ifndef AQUEUE_H #define AQUEUE_H /* Dynamically growing queue. Use the array directly to access the data, for example: count = queue_count(queue); for (i = 0; i < count; i++) { data = array[queue_idx(i)]; } */ struct aqueue { struct array *arr; unsigned int head, tail, area_size; bool full; }; struct aqueue *aqueue_init(struct array *array); void aqueue_deinit(struct aqueue **aqueue); /* Append item to head */ void aqueue_append(struct aqueue *aqueue, const void *data); /* Delete last item from tail */ void aqueue_delete_tail(struct aqueue *aqueue); /* Remove item from n'th position */ void aqueue_delete(struct aqueue *aqueue, unsigned int n); /* Clear the entire aqueue */ void aqueue_clear(struct aqueue *aqueue); /* Returns the number of items in aqueue. */ unsigned int aqueue_count(const struct aqueue *aqueue) ATTR_PURE; /* Returns array index of n'th element in aqueue. */ static inline unsigned int ATTR_PURE aqueue_idx(const struct aqueue *aqueue, unsigned int n) { return (aqueue->tail + n) % aqueue->area_size; } #endif dovecot-2.2.33.2/src/lib/istream-mmap.c0000644000175000017500000001462713165463624014446 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mmap-util.h" #include "istream-private.h" #include #include struct mmap_istream { struct istream_private istream; struct timeval fstat_cache_stamp; void *mmap_base; off_t mmap_offset; uoff_t v_size; unsigned int autoclose_fd:1; }; static size_t mmap_pagemask = 0; static void i_stream_mmap_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct mmap_istream *mstream = (struct mmap_istream *) stream; if (mstream->autoclose_fd && mstream->istream.fd != -1) { if (close(mstream->istream.fd) < 0) { i_error("mmap_istream.close(%s) failed: %m", i_stream_get_name(&mstream->istream.istream)); } } mstream->istream.fd = -1; } static void i_stream_munmap(struct mmap_istream *mstream) { struct istream_private *_stream = &mstream->istream; if (_stream->buffer != NULL) { if (munmap(mstream->mmap_base, _stream->buffer_size) < 0) { i_error("mmap_istream.munmap(%s) failed: %m", i_stream_get_name(&_stream->istream)); } mstream->mmap_base = NULL; _stream->buffer = NULL; _stream->buffer_size = 0; mstream->mmap_offset = 0; } } static void i_stream_mmap_destroy(struct iostream_private *stream) { struct mmap_istream *mstream = (struct mmap_istream *) stream; i_stream_munmap(mstream); } static size_t mstream_get_mmap_block_size(struct istream_private *stream) { return (stream->max_buffer_size + mmap_get_page_size() - 1) & ~ (mmap_get_page_size() - 1); } static ssize_t i_stream_mmap_read(struct istream_private *stream) { struct mmap_istream *mstream = (struct mmap_istream *) stream; size_t aligned_skip; uoff_t top; if (stream->pos < stream->buffer_size) { /* more bytes available without needing to mmap() */ stream->pos = stream->buffer_size; return stream->pos - stream->skip; } if (stream->istream.v_offset >= mstream->v_size) { stream->istream.eof = TRUE; return -1; } aligned_skip = stream->skip & ~mmap_pagemask; if (aligned_skip == 0 && mstream->mmap_base != NULL) { /* didn't skip enough bytes */ return -2; } stream->skip -= aligned_skip; mstream->mmap_offset += aligned_skip; if (mstream->mmap_base != NULL) { if (munmap(mstream->mmap_base, stream->buffer_size) < 0) { i_error("mmap_istream.munmap(%s) failed: %m", i_stream_get_name(&stream->istream)); } } top = mstream->v_size - mstream->mmap_offset; stream->buffer_size = I_MIN(top, mstream_get_mmap_block_size(stream)); i_assert((uoff_t)mstream->mmap_offset + stream->buffer_size <= mstream->v_size); if (stream->buffer_size == 0) { /* don't bother even trying mmap */ mstream->mmap_base = NULL; stream->buffer = NULL; } else { mstream->mmap_base = mmap(NULL, stream->buffer_size, PROT_READ, MAP_PRIVATE, stream->fd, mstream->mmap_offset); if (mstream->mmap_base == MAP_FAILED) { i_assert(errno != 0); stream->istream.stream_errno = errno; mstream->mmap_base = NULL; stream->buffer = NULL; stream->buffer_size = 0; stream->skip = stream->pos = 0; io_stream_set_error(&stream->iostream, "mmap() failed: %m"); i_error("mmap_istream.mmap(%s) failed: %m", i_stream_get_name(&stream->istream)); return -1; } stream->buffer = mstream->mmap_base; } if (stream->buffer_size > mmap_get_page_size()) { if (madvise(mstream->mmap_base, stream->buffer_size, MADV_SEQUENTIAL) < 0) { i_error("mmap_istream.madvise(%s): %m", i_stream_get_name(&stream->istream)); } } stream->pos = stream->buffer_size; i_assert(stream->pos - stream->skip > 0); return stream->pos - stream->skip; } static void i_stream_mmap_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct mmap_istream *mstream = (struct mmap_istream *) stream; if (stream->buffer_size != 0 && (uoff_t)mstream->mmap_offset <= v_offset && (uoff_t)mstream->mmap_offset + stream->buffer_size > v_offset) { /* already mmaped */ stream->skip = stream->pos = v_offset - mstream->mmap_offset; } else { /* force reading next time */ i_stream_munmap(mstream); stream->skip = stream->pos = v_offset; } stream->istream.v_offset = v_offset; } static void i_stream_mmap_sync(struct istream_private *stream) { struct mmap_istream *mstream = (struct mmap_istream *) stream; i_stream_munmap(mstream); stream->skip = stream->pos = stream->istream.v_offset; mstream->fstat_cache_stamp.tv_sec = 0; } static int fstat_cached(struct mmap_istream *mstream) { if (mstream->fstat_cache_stamp.tv_sec == ioloop_timeval.tv_sec && mstream->fstat_cache_stamp.tv_usec == ioloop_timeval.tv_usec) return 0; if (fstat(mstream->istream.fd, &mstream->istream.statbuf) < 0) { i_error("mmap_istream.fstat(%s) failed: %m", i_stream_get_name(&mstream->istream.istream)); mstream->istream.istream.stream_errno = errno; return -1; } mstream->fstat_cache_stamp = ioloop_timeval; return 0; } static int i_stream_mmap_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct mmap_istream *mstream = (struct mmap_istream *) stream; return fstat_cached(mstream); } struct istream *i_stream_create_mmap(int fd, size_t block_size, uoff_t start_offset, uoff_t v_size, bool autoclose_fd) { struct mmap_istream *mstream; struct istream *istream; struct stat st; if (mmap_pagemask == 0) mmap_pagemask = mmap_get_page_size()-1; mstream = i_new(struct mmap_istream, 1); if (v_size == 0) { if (fstat(fd, &st) < 0) { i_error("i_stream_create_mmap(): fstat() failed: %m"); mstream->istream.istream.eof = TRUE; mstream->istream.istream.stream_errno = errno; } else { v_size = st.st_size; if (start_offset > v_size) start_offset = v_size; v_size -= start_offset; } } mstream->autoclose_fd = autoclose_fd; mstream->v_size = v_size; mstream->istream.iostream.close = i_stream_mmap_close; mstream->istream.iostream.destroy = i_stream_mmap_destroy; mstream->istream.max_buffer_size = block_size; mstream->istream.read = i_stream_mmap_read; mstream->istream.seek = i_stream_mmap_seek; mstream->istream.sync = i_stream_mmap_sync; mstream->istream.stat = i_stream_mmap_stat; mstream->istream.istream.readable_fd = TRUE; mstream->istream.abs_start_offset = start_offset; istream = i_stream_create(&mstream->istream, NULL, fd); istream->mmaped = TRUE; istream->blocking = TRUE; istream->seekable = TRUE; return istream; } dovecot-2.2.33.2/src/lib/ostream-escaped.c0000644000175000017500000000670213123174404015106 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "ostream.h" #include "ostream-private.h" #include "ostream-escaped.h" struct escaped_ostream { struct ostream_private ostream; ostream_escaped_escape_formatter_t format; string_t *buf; bool flushed; }; static ssize_t o_stream_escaped_send_outbuf(struct escaped_ostream *estream) { ssize_t ret; if (estream->flushed) return 1; /* nothing to send */ ret = o_stream_send(estream->ostream.parent, str_data(estream->buf), str_len(estream->buf)); if (ret < 0) { o_stream_copy_error_from_parent(&estream->ostream); return -1; } if ((size_t)ret != str_len(estream->buf)) { /* move data */ str_delete(estream->buf, 0, ret); return 0; } str_truncate(estream->buf, 0); estream->flushed = TRUE; return 1; } static ssize_t o_stream_escaped_send_chunk(struct escaped_ostream *estream, const unsigned char *data, size_t len) { size_t i, max_buffer_size; ssize_t ret; max_buffer_size = I_MIN(o_stream_get_max_buffer_size(estream->ostream.parent), estream->ostream.max_buffer_size); if (max_buffer_size > IO_BLOCK_SIZE) { /* avoid using up too much memory in case of large buffers */ max_buffer_size = IO_BLOCK_SIZE; } for (i = 0; i < len; i++) { if (str_len(estream->buf) + 2 > max_buffer_size) { /* escaping takes at least two bytes */ ret = o_stream_escaped_send_outbuf(estream); if (ret < 0) { estream->ostream.ostream.offset += i; return ret; } if (ret == 0) break; } estream->format(estream->buf, data[i]); estream->flushed = FALSE; } estream->ostream.ostream.offset += i; return i; } static ssize_t o_stream_escaped_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct escaped_ostream *estream = (struct escaped_ostream *)stream; unsigned int iov_cur; ssize_t ret, bytes = 0; for (iov_cur = 0; iov_cur < iov_count; iov_cur++) { ret = o_stream_escaped_send_chunk(estream, iov[iov_cur].iov_base, iov[iov_cur].iov_len); if (ret < 0) return ret; bytes += ret; if ((size_t)ret != iov[iov_cur].iov_len) break; } if (o_stream_escaped_send_outbuf(estream) < 0) return -1; return bytes; } static int o_stream_escaped_flush(struct ostream_private *stream) { struct escaped_ostream *estream = (struct escaped_ostream *)stream; int ret; if ((ret = o_stream_escaped_send_outbuf(estream)) <= 0) return ret; if ((ret = o_stream_flush(stream->parent)) < 0) o_stream_copy_error_from_parent(stream); return ret; } static void o_stream_escaped_destroy(struct iostream_private *stream) { struct escaped_ostream *estream = (struct escaped_ostream *)stream; str_free(&estream->buf); o_stream_unref(&estream->ostream.parent); } void ostream_escaped_hex_format(string_t *dest, unsigned char chr) { str_printfa(dest, "%02x", chr); } struct ostream * o_stream_create_escaped(struct ostream *output, ostream_escaped_escape_formatter_t format) { struct escaped_ostream *estream; estream = i_new(struct escaped_ostream, 1); estream->ostream.sendv = o_stream_escaped_sendv; estream->ostream.flush = o_stream_escaped_flush; estream->ostream.max_buffer_size = o_stream_get_max_buffer_size(output); estream->ostream.iostream.destroy = o_stream_escaped_destroy; estream->buf = str_new(default_pool, 512); estream->format = format; estream->flushed = FALSE; return o_stream_create(&estream->ostream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib/test-timing.c0000644000175000017500000000524713165463624014314 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "timing.h" #include "sort.h" static void test_timing_verify(const struct timing *t, const int64_t *input, unsigned int input_size) { uint64_t min = INT_MAX, max = 0, sum = 0; uint64_t *copy; unsigned int i; i_assert(input_size > 0); copy = i_new(uint64_t, input_size); for (i = 0; i < input_size; i++) { uint64_t value = input[i]; if (min > value) min = value; if (max < value) max = value; sum += value; copy[i] = value; } i_qsort(copy, input_size, sizeof(*copy), uint64_cmp); test_assert_idx(timing_get_count(t) == input_size, input_size); test_assert_idx(timing_get_sum(t) == sum, input_size); test_assert_idx(timing_get_min(t) == min, input_size); test_assert_idx(timing_get_max(t) == max, input_size); test_assert_idx(timing_get_avg(t) == (sum + input_size/2)/input_size, input_size); /* these aren't always fully accurate: */ test_assert_idx(timing_get_median(t) >= copy[(input_size-1)/2] && timing_get_median(t) <= copy[input_size/2], input_size); /* when we have 20 elements, [19] is the max, not the 95th %ile, so subtract 1 */ test_assert_idx(timing_get_95th(t) == copy[input_size*95/100 - !(input_size%20)], input_size); i_free(copy); } void test_timing(void) { static int64_t test_input1[] = { 20, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1 }; static int64_t test_input2[] = { 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1 }; static int64_t test_input3[] = { 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 22, -1 }; static int64_t *test_inputs[] = { test_input1, test_input2, test_input3 }; struct timing *t; unsigned int i, j; for (i = 0; i < N_ELEMENTS(test_inputs); i++) { test_begin(t_strdup_printf("timings %u", i)); t = timing_init(); for (j = 0; test_inputs[i][j] >= 0; j++) { timing_add_usecs(t, test_inputs[i][j]); test_timing_verify(t, test_inputs[i], j+1); } timing_reset(t); test_assert(timing_get_count(t) == 0); test_assert(timing_get_max(t) == 0); timing_deinit(&t); test_end(); } test_begin("timings large"); t = timing_init(); for (i = 0; i < 10000; i++) timing_add_usecs(t, i); test_assert(timing_get_count(t) == i); test_assert(timing_get_sum(t) == (i-1)*i/2); test_assert(timing_get_min(t) == 0); test_assert(timing_get_max(t) == i-1); test_assert(timing_get_avg(t) == i/2); /* just test that these work: */ test_assert(timing_get_median(t) > 0 && timing_get_median(t) < i-1); test_assert(timing_get_95th(t) > 0 && timing_get_95th(t) < i-1); timing_deinit(&t); test_end(); } dovecot-2.2.33.2/src/lib/istream-timeout.c0000644000175000017500000001065313165463624015175 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "time-util.h" #include "istream-private.h" #include "istream-timeout.h" struct timeout_istream { struct istream_private istream; struct timeout *to; struct timeval last_read_timestamp; time_t created; unsigned int timeout_msecs; bool update_timestamp; }; static void i_stream_timeout_close(struct iostream_private *stream, bool close_parent) { struct timeout_istream *tstream = (struct timeout_istream *)stream; if (tstream->to != NULL) timeout_remove(&tstream->to); if (close_parent) i_stream_close(tstream->istream.parent); } static void i_stream_timeout_switch_ioloop(struct istream_private *stream) { struct timeout_istream *tstream = (struct timeout_istream *)stream; if (tstream->to != NULL) tstream->to = io_loop_move_timeout(&tstream->to); } static void i_stream_timeout(struct timeout_istream *tstream) { unsigned int over_msecs; int diff; if (tstream->update_timestamp) { /* we came here after a long-running code. timeouts are handled before IOs, so wait for i_stream_read() to be called again before assuming that we've timed out. */ return; } timeout_remove(&tstream->to); diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp); if (diff < (int)tstream->timeout_msecs) { /* we haven't reached the read timeout yet, update it */ if (diff < 0) diff = 0; tstream->to = timeout_add(tstream->timeout_msecs - diff, i_stream_timeout, tstream); return; } over_msecs = diff - tstream->timeout_msecs; io_stream_set_error(&tstream->istream.iostream, "Read timeout in %u.%03u s after %"PRIuUOFF_T" bytes%s", diff/1000, diff%1000, tstream->istream.istream.v_offset, over_msecs < 1000 ? "" : t_strdup_printf( " (requested timeout in %u ms)", tstream->timeout_msecs)); tstream->istream.istream.stream_errno = ETIMEDOUT; i_stream_set_input_pending(tstream->istream.parent, TRUE); } static void i_stream_timeout_set_pending(struct timeout_istream *tstream) { /* make sure we get called again on the next ioloop run. this updates the timeout to the timestamp where we actually would have wanted to start waiting for more data (so if there is long-running code outside the ioloop it's not counted) */ tstream->update_timestamp = TRUE; tstream->last_read_timestamp = ioloop_timeval; i_stream_set_input_pending(&tstream->istream.istream, TRUE); } static ssize_t i_stream_timeout_read(struct istream_private *stream) { struct timeout_istream *tstream = (struct timeout_istream *)stream; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); if (ret < 0) { /* failed */ if (errno == ECONNRESET || errno == EPIPE) { int diff = ioloop_time - tstream->created; io_stream_set_error(&tstream->istream.iostream, "%s (opened %d secs ago)", i_stream_get_error(stream->parent), diff); } } else if (tstream->to == NULL && tstream->timeout_msecs > 0) { /* first read. add the timeout here instead of in init in case the stream is created long before it's actually read from. */ tstream->to = timeout_add(tstream->timeout_msecs, i_stream_timeout, tstream); i_stream_timeout_set_pending(tstream); } else if (ret > 0 && tstream->to != NULL) { /* we read something, reset the timeout */ timeout_reset(tstream->to); i_stream_timeout_set_pending(tstream); } else if (tstream->update_timestamp) { tstream->update_timestamp = FALSE; tstream->last_read_timestamp = ioloop_timeval; } return ret; } struct istream * i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs) { struct timeout_istream *tstream; tstream = i_new(struct timeout_istream, 1); tstream->timeout_msecs = timeout_msecs; tstream->istream.max_buffer_size = input->real_stream->max_buffer_size; tstream->istream.stream_size_passthrough = TRUE; tstream->created = ioloop_time; tstream->istream.read = i_stream_timeout_read; tstream->istream.switch_ioloop = i_stream_timeout_switch_ioloop; tstream->istream.iostream.close = i_stream_timeout_close; tstream->istream.istream.readable_fd = input->readable_fd; tstream->istream.istream.blocking = input->blocking; tstream->istream.istream.seekable = input->seekable; return i_stream_create(&tstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/timing.c0000644000175000017500000000553313165463624013335 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "timing.h" #include "sort.h" /* In order to have a vaguely accurate 95th percentile, you need way more than 20 in your subsample. */ #define TIMING_SUBSAMPLING_BUFFER (20*24) /* 20*24 fits in a page */ struct timing { unsigned int count; bool sorted; uint64_t min; uint64_t samples[TIMING_SUBSAMPLING_BUFFER]; uint64_t max; uint64_t sum; }; struct timing *timing_init(void) { return i_new(struct timing, 1); } void timing_deinit(struct timing **_timing) { i_free_and_null(*_timing); } void timing_reset(struct timing *timing) { i_zero(timing); } void timing_add_usecs(struct timing *timing, uint64_t usecs) { if (timing->count < TIMING_SUBSAMPLING_BUFFER) { timing->samples[timing->count] = usecs; if (timing->count == 0) timing->min = timing->max = usecs; } else { unsigned int count = timing->count; unsigned int idx; if (count > RAND_MAX >> 6) idx = (rand()*((uint64_t)RAND_MAX+1) + rand()) % count; else idx = rand() % count; if (idx < TIMING_SUBSAMPLING_BUFFER) timing->samples[idx] = usecs; } timing->count++; timing->sum += usecs; if (timing->max < usecs) timing->max = usecs; if (timing->min > usecs) timing->min = usecs; timing->sorted = FALSE; } unsigned int timing_get_count(const struct timing *timing) { return timing->count; } uint64_t timing_get_sum(const struct timing *timing) { return timing->sum; } uint64_t timing_get_min(const struct timing *timing) { return timing->min; } uint64_t timing_get_max(const struct timing *timing) { return timing->max; } uint64_t timing_get_avg(const struct timing *timing) { if (timing->count == 0) return 0; return (timing->sum + timing->count/2) / timing->count; } static void timing_ensure_sorted(struct timing *timing) { if (timing->sorted) return; unsigned int count = (timing->count < TIMING_SUBSAMPLING_BUFFER) ? timing->count : TIMING_SUBSAMPLING_BUFFER; i_qsort(timing->samples, count, sizeof(*timing->samples), uint64_cmp); timing->sorted = TRUE; } uint64_t timing_get_median(const struct timing *timing) { if (timing->count == 0) return 0; /* cast-away const - reading requires sorting */ timing_ensure_sorted((struct timing *)timing); unsigned int count = (timing->count < TIMING_SUBSAMPLING_BUFFER) ? timing->count : TIMING_SUBSAMPLING_BUFFER; unsigned int idx1 = (count-1)/2, idx2 = count/2; return (timing->samples[idx1] + timing->samples[idx2]) / 2; } uint64_t timing_get_95th(const struct timing *timing) { if (timing->count == 0) return 0; /* cast-away const - reading requires sorting */ timing_ensure_sorted((struct timing *)timing); unsigned int count = (timing->count < TIMING_SUBSAMPLING_BUFFER) ? timing->count : TIMING_SUBSAMPLING_BUFFER; unsigned int idx = count - count/20 - 1; return timing->samples[idx]; } dovecot-2.2.33.2/src/lib/byteorder.h0000644000175000017500000001756613147010712014045 00000000000000/* * Copyright (c) 2016-2017 Josef 'Jeff' Sipek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef BYTEORDER_H #define BYTEORDER_H /* * These prototypes exist to catch bugs in the code generating macros below. */ /* return byte swapped input */ static inline uint64_t bswap_64(uint64_t in); static inline uint32_t bswap_32(uint32_t in); static inline uint16_t bswap_16(uint16_t in); static inline uint8_t bswap_8(uint8_t in); /* load an unaligned cpu native endian number from memory */ static inline uint64_t cpu64_to_cpu_unaligned(const void *in); static inline uint32_t cpu32_to_cpu_unaligned(const void *in); static inline uint16_t cpu16_to_cpu_unaligned(const void *in); static inline uint8_t cpu8_to_cpu_unaligned(const void *in); /* load an unaligned big endian number from memory */ static inline uint64_t be64_to_cpu_unaligned(const void *in); static inline uint32_t be32_to_cpu_unaligned(const void *in); static inline uint16_t be16_to_cpu_unaligned(const void *in); static inline uint8_t be8_to_cpu_unaligned(const void *in); /* load an unaligned little endian number from memory */ static inline uint64_t le64_to_cpu_unaligned(const void *in); static inline uint32_t le32_to_cpu_unaligned(const void *in); static inline uint16_t le16_to_cpu_unaligned(const void *in); static inline uint8_t le8_to_cpu_unaligned(const void *in); /* store into memory a cpu native endian number as a big endian number */ static inline void cpu64_to_be_unaligned(uint64_t in, void *out); static inline void cpu32_to_be_unaligned(uint32_t in, void *out); static inline void cpu16_to_be_unaligned(uint16_t in, void *out); static inline void cpu8_to_be_unaligned(uint8_t in, void *out); /* store into memory a cpu native endian number as a little endian number */ static inline void cpu64_to_le_unaligned(uint64_t in, void *out); static inline void cpu32_to_le_unaligned(uint32_t in, void *out); static inline void cpu16_to_le_unaligned(uint16_t in, void *out); static inline void cpu8_to_le_unaligned(uint8_t in, void *out); /* convert a big endian input into cpu native endian */ static inline uint64_t be64_to_cpu(uint64_t in); static inline uint32_t be32_to_cpu(uint32_t in); static inline uint16_t be16_to_cpu(uint16_t in); static inline uint8_t be8_to_cpu(uint8_t in); /* convert a cpu native endian input into big endian */ static inline uint64_t cpu64_to_be(uint64_t in); static inline uint32_t cpu32_to_be(uint32_t in); static inline uint16_t cpu16_to_be(uint16_t in); static inline uint8_t cpu8_to_be(uint8_t in); /* convert a little endian input into cpu native endian */ static inline uint64_t le64_to_cpu(uint64_t in); static inline uint32_t le32_to_cpu(uint32_t in); static inline uint16_t le16_to_cpu(uint16_t in); static inline uint8_t le8_to_cpu(uint8_t in); /* convert a cpu native endian input into little endian */ static inline uint64_t cpu64_to_le(uint64_t in); static inline uint32_t cpu32_to_le(uint32_t in); static inline uint16_t cpu16_to_le(uint16_t in); static inline uint8_t cpu8_to_le(uint8_t in); /* * byte swapping */ static inline uint64_t bswap_64(uint64_t in) { return ((in & 0xff00000000000000) >> 56) | ((in & 0x00ff000000000000) >> 40) | ((in & 0x0000ff0000000000) >> 24) | ((in & 0x000000ff00000000) >> 8) | ((in & 0x00000000ff000000) << 8) | ((in & 0x0000000000ff0000) << 24) | ((in & 0x000000000000ff00) << 40) | ((in & 0x00000000000000ff) << 56); } static inline uint32_t bswap_32(uint32_t in) { return ((in & 0xff000000) >> 24) | ((in & 0x00ff0000) >> 8) | ((in & 0x0000ff00) << 8) | ((in & 0x000000ff) << 24); } static inline uint16_t bswap_16(uint16_t in) { return ((in & 0xff00) >> 8) | ((in & 0x00ff) << 8); } static inline uint8_t bswap_8(uint8_t in) { return (in & 0xff); } /* * unaligned big-endian integer */ static inline uint64_t be64_to_cpu_unaligned(const void *in) { const uint8_t *p = (const uint8_t *) in; return (((uint64_t) p[0] << 56) | ((uint64_t) p[1] << 48) | ((uint64_t) p[2] << 40) | ((uint64_t) p[3] << 32) | ((uint64_t) p[4] << 24) | ((uint64_t) p[5] << 16) | ((uint64_t) p[6] << 8) | ((uint64_t) p[7])); } static inline void cpu64_to_be_unaligned(uint64_t in, void *out) { uint8_t *p = (uint8_t *) out; p[0] = (in >> 56) & 0xff; p[1] = (in >> 48) & 0xff; p[2] = (in >> 40) & 0xff; p[3] = (in >> 32) & 0xff; p[4] = (in >> 24) & 0xff; p[5] = (in >> 16) & 0xff; p[6] = (in >> 8) & 0xff; p[7] = in & 0xff; } static inline uint32_t be32_to_cpu_unaligned(const void *in) { const uint8_t *p = (const uint8_t *) in; return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16) | ((uint32_t) p[2] << 8) | ((uint32_t) p[3])); } static inline void cpu32_to_be_unaligned(uint32_t in, void *out) { uint8_t *p = (uint8_t *) out; p[0] = (in >> 24) & 0xff; p[1] = (in >> 16) & 0xff; p[2] = (in >> 8) & 0xff; p[3] = in & 0xff; } static inline uint16_t be16_to_cpu_unaligned(const void *in) { const uint8_t *p = (const uint8_t *) in; return (((uint16_t) p[0] << 8) | ((uint16_t) p[1])); } static inline void cpu16_to_be_unaligned(uint16_t in, void *out) { uint8_t *p = (uint8_t *) out; p[0] = (in >> 8) & 0xff; p[1] = in & 0xff; } static inline uint8_t be8_to_cpu_unaligned(const void *in) { return *((const uint8_t *) in); } static inline void cpu8_to_be_unaligned(uint8_t in, void *out) { uint8_t *p = (uint8_t *) out; *p = in; } /* * unaligned little-endian & cpu-endian integers */ #define __GEN(size, bswap) \ static inline uint##size##_t le##size##_to_cpu_unaligned(const void *in)\ { \ uint##size##_t x = be##size##_to_cpu_unaligned(in); \ /* we read a LE int as BE, so we always have to byte swap */ \ return bswap_##size(x); \ } \ static inline void cpu##size##_to_le_unaligned(uint##size##_t in, \ void *out) \ { \ /* we'll be writing in BE, so we always have to byte swap */ \ cpu##size##_to_be_unaligned(bswap_##size(in), out); \ } \ static inline uint##size##_t cpu##size##_to_cpu_unaligned(const void *in)\ { \ uint##size##_t x = be##size##_to_cpu_unaligned(in); \ return bswap; \ } #if WORDS_BIGENDIAN #define GEN(size) __GEN(size, x) #else #define GEN(size) __GEN(size, bswap_##size(x)) #endif GEN(64) GEN(32) GEN(16) GEN(8) #undef __GEN #undef GEN /* * byte ordering */ #define ___GEN(from, size, to, bswap) \ static inline uint##size##_t from##size##_to_##to(uint##size##_t x) \ { \ return bswap; \ } #if WORDS_BIGENDIAN #define __GEN(from, size, to, be, le) ___GEN(from, size, to, be) #else #define __GEN(from, size, to, be, le) ___GEN(from, size, to, le) #endif #define GEN(size) \ __GEN(be, size, cpu, x, bswap_##size(x)) \ __GEN(cpu, size, be, x, bswap_##size(x)) \ __GEN(le, size, cpu, bswap_##size(x), x) \ __GEN(cpu, size, le, bswap_##size(x), x) GEN(64) GEN(32) GEN(16) GEN(8) #undef ___GEN #undef __GEN #undef GEN #endif dovecot-2.2.33.2/src/lib/ostream-hash.h0000644000175000017500000000054413123174404014430 00000000000000#ifndef OSTREAM_HASH_H #define OSTREAM_HASH_H struct hash_method; /* hash_context must be allocated and initialized by caller. This ostream will simply call method->loop() for all the data going through the ostream. */ struct ostream * o_stream_create_hash(struct ostream *output, const struct hash_method *method, void *hash_context); #endif dovecot-2.2.33.2/src/lib/ioloop-iolist.c0000644000175000017500000000225513123174404014633 00000000000000/* * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "ioloop-private.h" #include "ioloop-iolist.h" bool ioloop_iolist_add(struct io_list *list, struct io_file *io) { int i, idx; if ((io->io.condition & IO_READ) != 0) idx = IOLOOP_IOLIST_INPUT; else if ((io->io.condition & IO_WRITE) != 0) idx = IOLOOP_IOLIST_OUTPUT; else if ((io->io.condition & IO_ERROR) != 0) idx = IOLOOP_IOLIST_ERROR; else { i_unreached(); } if (list->ios[idx] != NULL) { i_panic("io_add(0x%x) called twice fd=%d, callback=%p -> %p", io->io.condition, io->fd, list->ios[idx]->io.callback, io->io.callback); } i_assert(list->ios[idx] == NULL); list->ios[idx] = io; /* check if this was the first one */ for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { if (i != idx && list->ios[i] != NULL) return FALSE; } return TRUE; } bool ioloop_iolist_del(struct io_list *list, struct io_file *io) { bool last = TRUE; int i; for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { if (list->ios[i] != NULL) { if (list->ios[i] == io) list->ios[i] = NULL; else last = FALSE; } } return last; } dovecot-2.2.33.2/src/lib/test-aqueue.c0000644000175000017500000000351013123174404014266 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "array.h" #include "aqueue.h" static bool aqueue_is_ok(struct aqueue *aqueue, unsigned int deleted_n) { const unsigned int *p; unsigned int n, i, count; count = aqueue_count(aqueue); for (i = 0, n = 1; i < count; i++, n++) { p = array_idx_i(aqueue->arr, aqueue_idx(aqueue, i)); if (i == deleted_n) n++; if (*p != n) return FALSE; } return TRUE; } static const unsigned int aqueue_input[] = { 1, 2, 3, 4, 5, 6 }; static const char *test_aqueue2(unsigned int initial_size) { ARRAY(unsigned int) aqueue_array; unsigned int i, j, k; for (i = 0; i < N_ELEMENTS(aqueue_input); i++) { for (k = 0; k < N_ELEMENTS(aqueue_input); k++) { struct aqueue *aqueue; t_array_init(&aqueue_array, initial_size); aqueue = aqueue_init(&aqueue_array.arr); aqueue->head = aqueue->tail = initial_size - 1; for (j = 0; j < k; j++) { aqueue_append(aqueue, &aqueue_input[j]); if (aqueue_count(aqueue) != j + 1) { return t_strdup_printf("Wrong count after append %u vs %u)", aqueue_count(aqueue), j + 1); } if (!aqueue_is_ok(aqueue, UINT_MAX)) return "Invalid data after append"; } if (k != 0 && i < k) { aqueue_delete(aqueue, i); if (aqueue_count(aqueue) != k - 1) return "Wrong count after delete"; if (!aqueue_is_ok(aqueue, i)) return "Invalid data after delete"; } aqueue_clear(aqueue); if (aqueue_count(aqueue) != 0) return "aqueue_clear() broken"; aqueue_deinit(&aqueue); } } return NULL; } void test_aqueue(void) { unsigned int i; const char *reason = NULL; for (i = 1; i <= N_ELEMENTS(aqueue_input) + 1 && reason == NULL; i++) { T_BEGIN { reason = test_aqueue2(i); } T_END; } test_out_reason("aqueue", reason == NULL, reason); } dovecot-2.2.33.2/src/lib/test-crc32.c0000644000175000017500000000047413123174404013723 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "crc32.h" void test_crc32(void) { const char str[] = "foo\0bar"; test_begin("crc32"); test_assert(crc32_str(str) == 0x8c736521); test_assert(crc32_data(str, sizeof(str)) == 0x32c9723d); test_end(); } dovecot-2.2.33.2/src/lib/istream.c0000644000175000017500000005644513165463624013522 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "istream-private.h" static bool i_stream_is_buffer_invalid(const struct istream_private *stream); void i_stream_set_name(struct istream *stream, const char *name) { i_free(stream->real_stream->iostream.name); stream->real_stream->iostream.name = i_strdup(name); } const char *i_stream_get_name(struct istream *stream) { while (stream->real_stream->iostream.name == NULL) { stream = stream->real_stream->parent; if (stream == NULL) return ""; } return stream->real_stream->iostream.name; } static void i_stream_close_full(struct istream *stream, bool close_parents) { io_stream_close(&stream->real_stream->iostream, close_parents); stream->closed = TRUE; if (stream->stream_errno == 0) stream->stream_errno = EPIPE; } void i_stream_destroy(struct istream **stream) { i_stream_close_full(*stream, FALSE); i_stream_unref(stream); } void i_stream_ref(struct istream *stream) { io_stream_ref(&stream->real_stream->iostream); } void i_stream_unref(struct istream **stream) { struct istream_private *_stream = (*stream)->real_stream; if (_stream->iostream.refcount == 1) { if (_stream->line_str != NULL) str_free(&_stream->line_str); } io_stream_unref(&(*stream)->real_stream->iostream); *stream = NULL; } #undef i_stream_add_destroy_callback void i_stream_add_destroy_callback(struct istream *stream, istream_callback_t *callback, void *context) { io_stream_add_destroy_callback(&stream->real_stream->iostream, callback, context); } void i_stream_remove_destroy_callback(struct istream *stream, void (*callback)()) { io_stream_remove_destroy_callback(&stream->real_stream->iostream, callback); } int i_stream_get_fd(struct istream *stream) { struct istream_private *_stream = stream->real_stream; return _stream->fd; } const char *i_stream_get_error(struct istream *stream) { struct istream *s; /* we'll only return errors for streams that have stream_errno set or that have reached EOF. we might be returning unintended error otherwise. */ if (stream->stream_errno == 0) return stream->eof ? "EOF" : ""; for (s = stream; s != NULL; s = s->real_stream->parent) { if (s->stream_errno == 0) break; if (s->real_stream->iostream.error != NULL) return s->real_stream->iostream.error; } return strerror(stream->stream_errno); } const char *i_stream_get_disconnect_reason(struct istream *stream) { return io_stream_get_disconnect_reason(stream, NULL); } void i_stream_close(struct istream *stream) { i_stream_close_full(stream, TRUE); } void i_stream_set_init_buffer_size(struct istream *stream, size_t size) { stream->real_stream->init_buffer_size = size; } void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size) { io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size); } size_t i_stream_get_max_buffer_size(struct istream *stream) { size_t max_size = 0; do { if (max_size < stream->real_stream->max_buffer_size) max_size = stream->real_stream->max_buffer_size; stream = stream->real_stream->parent; } while (stream != NULL); return max_size; } void i_stream_set_return_partial_line(struct istream *stream, bool set) { stream->real_stream->return_nolf_line = set; } void i_stream_set_persistent_buffers(struct istream *stream, bool set) { do { stream->real_stream->nonpersistent_buffers = !set; stream = stream->real_stream->parent; } while (stream != NULL); } static void i_stream_update(struct istream_private *stream) { if (stream->parent == NULL) stream->access_counter++; else { stream->access_counter = stream->parent->real_stream->access_counter; stream->parent_expected_offset = stream->parent->v_offset; } } ssize_t i_stream_read(struct istream *stream) { struct istream_private *_stream = stream->real_stream; size_t old_size; ssize_t ret; if (unlikely(stream->closed || stream->stream_errno != 0)) { stream->eof = TRUE; errno = stream->stream_errno; return -1; } stream->eof = FALSE; if (_stream->parent != NULL) i_stream_seek(_stream->parent, _stream->parent_expected_offset); old_size = _stream->pos - _stream->skip; ret = _stream->read(_stream); i_assert(old_size <= _stream->pos - _stream->skip); switch (ret) { case -2: i_assert(_stream->skip != _stream->pos); break; case -1: if (stream->stream_errno != 0) { /* error handling should be easier if we now just assume the stream is now at EOF */ stream->eof = TRUE; errno = stream->stream_errno; } else { i_assert(stream->eof); i_assert(old_size == _stream->pos - _stream->skip); } break; case 0: i_assert(!stream->blocking); break; default: i_assert(ret > 0); i_assert(_stream->skip < _stream->pos); i_assert((size_t)ret+old_size == _stream->pos - _stream->skip); break; } if (stream->stream_errno != 0) { /* error handling should be easier if we now just assume the stream is now at EOF. Note that we could get here even if read() didn't return -1, although that's a little bit sloppy istream implementation. */ stream->eof = TRUE; } i_stream_update(_stream); /* verify that parents' access_counters are valid. the parent's i_stream_read() should guarantee this. */ i_assert(!i_stream_is_buffer_invalid(_stream)); return ret; } ssize_t i_stream_read_copy_from_parent(struct istream *istream) { struct istream_private *stream = istream->real_stream; size_t pos; ssize_t ret; stream->pos -= stream->skip; stream->skip = 0; stream->buffer = i_stream_get_data(stream->parent, &pos); if (pos > stream->pos) ret = 0; else do { if ((ret = i_stream_read(stream->parent)) == -2) { i_stream_update(stream); return -2; } stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; stream->buffer = i_stream_get_data(stream->parent, &pos); /* check again, in case the parent stream had been seeked backwards and the previous read() didn't get us far enough. */ } while (pos <= stream->pos && ret > 0); ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); i_stream_update(stream); return ret; } void i_stream_skip(struct istream *stream, uoff_t count) { struct istream_private *_stream = stream->real_stream; size_t data_size; data_size = _stream->pos - _stream->skip; if (count <= data_size) { /* within buffer */ stream->v_offset += count; _stream->skip += count; if (_stream->nonpersistent_buffers && _stream->skip == _stream->pos) { _stream->skip = _stream->pos = 0; _stream->buffer_size = 0; i_free_and_null(_stream->w_buffer); } return; } /* have to seek forward */ count -= data_size; _stream->skip = _stream->pos; stream->v_offset += data_size; if (unlikely(stream->closed)) return; _stream->seek(_stream, stream->v_offset + count, FALSE); } static bool i_stream_can_optimize_seek(struct istream_private *stream) { if (stream->parent == NULL) return TRUE; /* use the fast route only if the parent stream hasn't been changed */ if (stream->access_counter != stream->parent->real_stream->access_counter) return FALSE; return i_stream_can_optimize_seek(stream->parent->real_stream); } void i_stream_seek(struct istream *stream, uoff_t v_offset) { struct istream_private *_stream = stream->real_stream; if (v_offset >= stream->v_offset && i_stream_can_optimize_seek(_stream)) i_stream_skip(stream, v_offset - stream->v_offset); else { if (unlikely(stream->closed || stream->stream_errno != 0)) { stream->eof = TRUE; return; } stream->eof = FALSE; _stream->seek(_stream, v_offset, FALSE); } i_stream_update(_stream); } void i_stream_seek_mark(struct istream *stream, uoff_t v_offset) { struct istream_private *_stream = stream->real_stream; if (unlikely(stream->closed)) return; stream->eof = FALSE; _stream->seek(_stream, v_offset, TRUE); i_stream_update(_stream); } void i_stream_sync(struct istream *stream) { struct istream_private *_stream = stream->real_stream; if (unlikely(stream->closed)) return; if (_stream->sync != NULL) { _stream->sync(_stream); i_stream_update(_stream); } } int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r) { struct istream_private *_stream = stream->real_stream; if (unlikely(stream->closed)) return -1; if (_stream->stat(_stream, exact) < 0) { stream->eof = TRUE; return -1; } *st_r = &_stream->statbuf; return 0; } int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r) { struct istream_private *_stream = stream->real_stream; if (unlikely(stream->closed)) return -1; int ret; if ((ret = _stream->get_size(_stream, exact, size_r)) < 0) stream->eof = TRUE; return ret; } bool i_stream_have_bytes_left(struct istream *stream) { return i_stream_get_data_size(stream) > 0 || !stream->eof; } bool i_stream_is_eof(struct istream *stream) { if (i_stream_get_data_size(stream) == 0) (void)i_stream_read(stream); return !i_stream_have_bytes_left(stream); } uoff_t i_stream_get_absolute_offset(struct istream *stream) { return stream->real_stream->abs_start_offset + stream->v_offset; } static char *i_stream_next_line_finish(struct istream_private *stream, size_t i) { char *ret; size_t end; if (i > 0 && stream->buffer[i-1] == '\r') { end = i - 1; stream->line_crlf = TRUE; } else { end = i; stream->line_crlf = FALSE; } if (stream->buffer == stream->w_buffer) { /* modify the buffer directly */ stream->w_buffer[end] = '\0'; ret = (char *)stream->w_buffer + stream->skip; } else { /* use a temporary string to return it */ if (stream->line_str == NULL) stream->line_str = str_new(default_pool, 256); str_truncate(stream->line_str, 0); str_append_n(stream->line_str, stream->buffer + stream->skip, end - stream->skip); ret = str_c_modifiable(stream->line_str); } if (i < stream->pos) i++; stream->istream.v_offset += i - stream->skip; stream->skip = i; return ret; } static char *i_stream_last_line(struct istream_private *_stream) { if (_stream->istream.eof && _stream->skip != _stream->pos && _stream->return_nolf_line) { /* the last line is missing LF and we want to return it. */ return i_stream_next_line_finish(_stream, _stream->pos); } return NULL; } char *i_stream_next_line(struct istream *stream) { struct istream_private *_stream = stream->real_stream; const unsigned char *pos; if (_stream->skip >= _stream->pos) return NULL; pos = memchr(_stream->buffer + _stream->skip, '\n', _stream->pos - _stream->skip); if (pos != NULL) { return i_stream_next_line_finish(_stream, pos - _stream->buffer); } else { return i_stream_last_line(_stream); } } char *i_stream_read_next_line(struct istream *stream) { char *line; for (;;) { line = i_stream_next_line(stream); if (line != NULL) break; switch (i_stream_read(stream)) { case -2: io_stream_set_error(&stream->real_stream->iostream, "Line is too long (over %"PRIuSIZE_T " bytes at offset %"PRIuUOFF_T")", i_stream_get_data_size(stream), stream->v_offset); stream->stream_errno = errno = ENOBUFS; stream->eof = TRUE; return NULL; case -1: return i_stream_last_line(stream->real_stream); case 0: return NULL; } } return line; } bool i_stream_last_line_crlf(struct istream *stream) { return stream->real_stream->line_crlf; } static bool i_stream_is_buffer_invalid(const struct istream_private *stream) { if (stream->parent == NULL) { /* the buffer can't point to parent, because it doesn't exist */ return FALSE; } if (stream->w_buffer != NULL) { /* we can pretty safely assume that the stream is using its own private buffer, so it can never become invalid. */ return FALSE; } if (stream->access_counter != stream->parent->real_stream->access_counter) { /* parent has been modified behind this stream, we can't trust that our buffer is valid */ return TRUE; } return i_stream_is_buffer_invalid(stream->parent->real_stream); } const unsigned char * i_stream_get_data(struct istream *stream, size_t *size_r) { struct istream_private *_stream = stream->real_stream; if (_stream->skip >= _stream->pos) { *size_r = 0; return NULL; } if (i_stream_is_buffer_invalid(_stream)) { /* This stream may be using parent's buffer directly as _stream->buffer, but the parent stream has already been modified indirectly. This means that the buffer might no longer point to where we assume it points to. So we'll just return the stream as empty until it's read again. It's a bit ugly to suddenly drop data from the stream that was already read, but since this happens only with shared parent istreams the caller is hopefully aware enough that something like this might happen. The other solutions would be to a) try to automatically read the data back (but we can't handle errors..) or b) always copy data to stream's own buffer instead of pointing to parent's buffer (but this causes data copying that is nearly always unnecessary). */ *size_r = 0; /* if we had already read until EOF, mark the stream again as not being at the end of file. */ if (stream->stream_errno == 0) { _stream->skip = _stream->pos = 0; stream->eof = FALSE; } return NULL; } *size_r = _stream->pos - _stream->skip; return _stream->buffer + _stream->skip; } size_t i_stream_get_data_size(struct istream *stream) { size_t size; (void)i_stream_get_data(stream, &size); return size; } unsigned char *i_stream_get_modifiable_data(struct istream *stream, size_t *size_r) { struct istream_private *_stream = stream->real_stream; if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) { *size_r = 0; return NULL; } *size_r = _stream->pos - _stream->skip; return _stream->w_buffer + _stream->skip; } int i_stream_read_data(struct istream *stream, const unsigned char **data_r, size_t *size_r, size_t threshold) { ssize_t ret = 0; bool read_more = FALSE; do { *data_r = i_stream_get_data(stream, size_r); if (*size_r > threshold) return 1; /* we need more data */ ret = i_stream_read(stream); if (ret > 0) read_more = TRUE; } while (ret > 0); *data_r = i_stream_get_data(stream, size_r); if (ret == -2) return -2; if (ret == 0) { /* need to read more */ i_assert(!stream->blocking); return 0; } if (stream->eof) { if (read_more) { /* we read at least some new data */ return 0; } } else { i_assert(stream->stream_errno != 0); } return -1; } void i_stream_compress(struct istream_private *stream) { if (stream->skip != stream->pos) { memmove(stream->w_buffer, stream->w_buffer + stream->skip, stream->pos - stream->skip); } stream->pos -= stream->skip; stream->skip = 0; } void i_stream_grow_buffer(struct istream_private *stream, size_t bytes) { size_t old_size, max_size; old_size = stream->buffer_size; stream->buffer_size = stream->pos + bytes; if (stream->buffer_size <= stream->init_buffer_size) stream->buffer_size = stream->init_buffer_size; else stream->buffer_size = nearest_power(stream->buffer_size); max_size = i_stream_get_max_buffer_size(&stream->istream); i_assert(max_size > 0); if (stream->buffer_size > max_size) stream->buffer_size = max_size; if (stream->buffer_size <= old_size) stream->buffer_size = old_size; else { stream->w_buffer = i_realloc(stream->w_buffer, old_size, stream->buffer_size); stream->buffer = stream->w_buffer; } } bool i_stream_try_alloc(struct istream_private *stream, size_t wanted_size, size_t *size_r) { i_assert(wanted_size > 0); if (wanted_size > stream->buffer_size - stream->pos) { if (stream->skip > 0) { /* remove the unused bytes from beginning of buffer */ i_stream_compress(stream); } else if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) { /* buffer is full - grow it */ i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); } } *size_r = stream->buffer_size - stream->pos; if (stream->try_alloc_limit > 0 && *size_r > stream->try_alloc_limit) *size_r = stream->try_alloc_limit; return *size_r > 0; } void *i_stream_alloc(struct istream_private *stream, size_t size) { size_t old_size, avail_size; i_stream_try_alloc(stream, size, &avail_size); if (avail_size < size) { old_size = stream->buffer_size; stream->buffer_size = nearest_power(stream->pos + size); stream->w_buffer = i_realloc(stream->w_buffer, old_size, stream->buffer_size); stream->buffer = stream->w_buffer; i_stream_try_alloc(stream, size, &avail_size); i_assert(avail_size >= size); } return stream->w_buffer + stream->pos; } bool i_stream_add_data(struct istream *_stream, const unsigned char *data, size_t size) { struct istream_private *stream = _stream->real_stream; size_t size2; i_stream_try_alloc(stream, size, &size2); if (size > size2) return FALSE; memcpy(stream->w_buffer + stream->pos, data, size); stream->pos += size; return TRUE; } void i_stream_set_input_pending(struct istream *stream, bool pending) { if (!pending) return; while (stream->real_stream->parent != NULL) { i_assert(stream->real_stream->io == NULL); stream = stream->real_stream->parent; } if (stream->real_stream->io != NULL) io_set_pending(stream->real_stream->io); } void i_stream_switch_ioloop(struct istream *stream) { do { if (stream->real_stream->switch_ioloop != NULL) stream->real_stream->switch_ioloop(stream->real_stream); stream = stream->real_stream->parent; } while (stream != NULL); } void i_stream_set_io(struct istream *stream, struct io *io) { while (stream->real_stream->parent != NULL) { i_assert(stream->real_stream->io == NULL); stream = stream->real_stream->parent; } i_assert(stream->real_stream->io == NULL); stream->real_stream->io = io; } void i_stream_unset_io(struct istream *stream, struct io *io) { while (stream->real_stream->parent != NULL) { i_assert(stream->real_stream->io == NULL); stream = stream->real_stream->parent; } i_assert(stream->real_stream->io == io); stream->real_stream->io = NULL; } static void i_stream_default_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct istream_private *_stream = (struct istream_private *)stream; _stream->max_buffer_size = max_size; if (_stream->parent != NULL) i_stream_set_max_buffer_size(_stream->parent, max_size); } static void i_stream_default_close(struct iostream_private *stream, bool close_parent) { struct istream_private *_stream = (struct istream_private *)stream; if (close_parent && _stream->parent != NULL) i_stream_close(_stream->parent); } static void i_stream_default_destroy(struct iostream_private *stream) { struct istream_private *_stream = (struct istream_private *)stream; i_free(_stream->w_buffer); if (_stream->parent != NULL) i_stream_unref(&_stream->parent); } static void i_stream_default_seek_seekable(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; } void i_stream_default_seek_nonseekable(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { size_t available; if (stream->istream.v_offset > v_offset) i_panic("stream %s doesn't support seeking backwards", i_stream_get_name(&stream->istream)); while (stream->istream.v_offset < v_offset) { (void)i_stream_read(&stream->istream); available = stream->pos - stream->skip; if (available == 0) { if (stream->istream.stream_errno != 0) { /* read failed */ return; } io_stream_set_error(&stream->iostream, "Can't seek to offset %"PRIuUOFF_T ", because we have data only up to offset %" PRIuUOFF_T" (eof=%d)", v_offset, stream->istream.v_offset, stream->istream.eof); stream->istream.stream_errno = ESPIPE; return; } if (available <= v_offset - stream->istream.v_offset) i_stream_skip(&stream->istream, available); else { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); } } } static int i_stream_default_stat(struct istream_private *stream, bool exact) { const struct stat *st; if (stream->parent == NULL) return stream->istream.stream_errno == 0 ? 0 : -1; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; if (exact && !stream->stream_size_passthrough) { /* exact size is not known, even if parent returned something */ stream->statbuf.st_size = -1; } return 0; } static int i_stream_default_get_size(struct istream_private *stream, bool exact, uoff_t *size_r) { if (stream->stat(stream, exact) < 0) return -1; if (stream->statbuf.st_size == -1) return 0; *size_r = stream->statbuf.st_size; return 1; } void i_stream_init_parent(struct istream_private *_stream, struct istream *parent) { _stream->access_counter = parent->real_stream->access_counter; _stream->parent = parent; _stream->parent_start_offset = parent->v_offset; _stream->parent_expected_offset = parent->v_offset; _stream->abs_start_offset = parent->v_offset + parent->real_stream->abs_start_offset; /* if parent stream is an istream-error, copy the error */ _stream->istream.stream_errno = parent->stream_errno; _stream->istream.eof = parent->eof; i_stream_ref(parent); } struct istream * i_stream_create(struct istream_private *_stream, struct istream *parent, int fd) { _stream->fd = fd; if (parent != NULL) i_stream_init_parent(_stream, parent); _stream->istream.real_stream = _stream; if (_stream->iostream.close == NULL) _stream->iostream.close = i_stream_default_close; if (_stream->iostream.destroy == NULL) _stream->iostream.destroy = i_stream_default_destroy; if (_stream->seek == NULL) { _stream->seek = _stream->istream.seekable ? i_stream_default_seek_seekable : i_stream_default_seek_nonseekable; } if (_stream->stat == NULL) _stream->stat = i_stream_default_stat; if (_stream->get_size == NULL) _stream->get_size = i_stream_default_get_size; if (_stream->iostream.set_max_buffer_size == NULL) { _stream->iostream.set_max_buffer_size = i_stream_default_set_max_buffer_size; } if (_stream->init_buffer_size == 0) _stream->init_buffer_size = I_STREAM_MIN_SIZE; i_zero(&_stream->statbuf); _stream->statbuf.st_size = -1; _stream->statbuf.st_atime = _stream->statbuf.st_mtime = _stream->statbuf.st_ctime = ioloop_time; io_stream_init(&_stream->iostream); if (_stream->istream.stream_errno != 0) _stream->istream.eof = TRUE; return &_stream->istream; } struct istream *i_stream_create_error(int stream_errno) { struct istream_private *stream; stream = i_new(struct istream_private, 1); stream->istream.closed = TRUE; stream->istream.readable_fd = FALSE; stream->istream.blocking = TRUE; stream->istream.seekable = TRUE; stream->istream.eof = TRUE; stream->istream.stream_errno = stream_errno; i_stream_create(stream, NULL, -1); i_stream_set_name(&stream->istream, "(error)"); return &stream->istream; } struct istream * i_stream_create_error_str(int stream_errno, const char *fmt, ...) { struct istream *input; va_list args; va_start(args, fmt); input = i_stream_create_error(stream_errno); io_stream_set_verror(&input->real_stream->iostream, fmt, args); va_end(args); return input; } dovecot-2.2.33.2/src/lib/unichar.h0000644000175000017500000001041113123174404013460 00000000000000#ifndef UNICHAR_H #define UNICHAR_H /* Character used to replace invalid input. */ #define UNICODE_REPLACEMENT_CHAR 0xfffd #define UNICODE_REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBD" /* Characters >= base require surrogates */ #define UTF16_SURROGATE_BASE 0x10000 #define UTF16_SURROGATE_SHIFT 10 #define UTF16_SURROGATE_MASK 0x03ff #define UTF16_SURROGATE_HIGH_FIRST 0xd800 #define UTF16_SURROGATE_HIGH_LAST 0xdbff #define UTF16_SURROGATE_HIGH_MAX 0xdfff #define UTF16_SURROGATE_LOW_FIRST 0xdc00 #define UTF16_SURROGATE_LOW_LAST 0xdfff #define UTF16_SURROGATE_HIGH(chr) \ (UTF16_SURROGATE_HIGH_FIRST + \ (((chr) - UTF16_SURROGATE_BASE) >> UTF16_SURROGATE_SHIFT)) #define UTF16_SURROGATE_LOW(chr) \ (UTF16_SURROGATE_LOW_FIRST + \ (((chr) - UTF16_SURROGATE_BASE) & UTF16_SURROGATE_MASK)) /* Returns TRUE if given byte is ASCII character or the beginning of a multibyte UTF-8 sequence */ #define UTF8_IS_START_SEQ(b) \ (((b) & 0x80) == 0 || ((b) & 0xC0) == 0xC0) #define UTF8_REPLACEMENT_CHAR_LEN 3 typedef uint32_t unichar_t; ARRAY_DEFINE_TYPE(unichars, unichar_t); /* Normalize UTF8 input and append it to output buffer. Returns 0 if ok, -1 if input was invalid. Even if input was invalid, as much as possible should be added to output. */ typedef int normalizer_func_t(const void *input, size_t size, buffer_t *output); extern const unsigned char utf8_replacement_char[UTF8_REPLACEMENT_CHAR_LEN]; extern const uint8_t *const uni_utf8_non1_bytes; /* Returns number of characters in a NUL-terminated unicode string */ unsigned int uni_strlen(const unichar_t *str) ATTR_PURE; /* Translates UTF-8 input to UCS-4 output. Returns 0 if ok, -1 if input was invalid */ int uni_utf8_to_ucs4(const char *input, ARRAY_TYPE(unichars) *output); int uni_utf8_to_ucs4_n(const unsigned char *input, size_t size, ARRAY_TYPE(unichars) *output); /* Translates UCS-4 input to UTF-8 output. */ void uni_ucs4_to_utf8(const unichar_t *input, size_t len, buffer_t *output); void uni_ucs4_to_utf8_c(unichar_t chr, buffer_t *output); /* Returns char_bytes (>0) if *chr_r is set, 0 for incomplete trailing character, -1 for invalid input. */ int uni_utf8_get_char(const char *input, unichar_t *chr_r); int uni_utf8_get_char_n(const void *input, size_t max_len, unichar_t *chr_r); /* Returns number of characters in UTF-8 string. */ unsigned int uni_utf8_strlen(const char *input) ATTR_PURE; /* Returns number of characters in UTF-8 input of specified size. */ unsigned int uni_utf8_strlen_n(const void *input, size_t size) ATTR_PURE; /* Same as uni_utf8_strlen_n(), but if input ends with a partial UTF-8 character, don't include it in the return value and set partial_pos_r to where the character begins. Otherwise partial_pos_r is set to the end of the input. */ unsigned int uni_utf8_partial_strlen_n(const void *input, size_t size, size_t *partial_pos_r); /* Returns the number of bytes belonging to this UTF-8 character. The given parameter is the first byte of the UTF-8 sequence. Invalid input is returned with length 1. */ static inline unsigned int ATTR_CONST uni_utf8_char_bytes(char chr) { /* 0x00 .. 0x7f are ASCII. 0x80 .. 0xC1 are invalid. */ if ((uint8_t)chr < (192 + 2)) return 1; return uni_utf8_non1_bytes[(uint8_t)chr - (192 + 2)]; } /* Return given character in titlecase. */ unichar_t uni_ucs4_to_titlecase(unichar_t chr) ATTR_CONST; /* Convert UTF-8 input to titlecase and decompose the titlecase characters to output buffer. Returns 0 if ok, -1 if input was invalid. This generates output that's compatible with i;unicode-casemap comparator. Invalid input is replaced with unicode replacement character (0xfffd). */ int uni_utf8_to_decomposed_titlecase(const void *input, size_t size, buffer_t *output); /* If input contains only valid UTF-8 characters, return TRUE without updating buf. If input contains invalid UTF-8 characters, replace them with unicode replacement character (0xfffd), write the output to buf and return FALSE. */ bool uni_utf8_get_valid_data(const unsigned char *input, size_t size, buffer_t *buf); /* Returns TRUE if string is valid UTF-8 input. */ bool uni_utf8_str_is_valid(const char *str); /* Returns TRUE if data contains only valid UTF-8 input. */ bool uni_utf8_data_is_valid(const unsigned char *data, size_t size); #endif dovecot-2.2.33.2/src/lib/ioloop-private.h0000644000175000017500000000534613165463624015026 00000000000000#ifndef IOLOOP_PRIVATE_H #define IOLOOP_PRIVATE_H #include "priorityq.h" #include "ioloop.h" #include "array-decl.h" #ifndef IOLOOP_INITIAL_FD_COUNT # define IOLOOP_INITIAL_FD_COUNT 128 #endif struct ioloop { struct ioloop *prev; struct ioloop_context *cur_ctx; struct io_file *io_files; struct io_file *next_io_file; struct priorityq *timeouts; ARRAY(struct timeout *) timeouts_new; struct io_wait_timer *wait_timers; struct ioloop_handler_context *handler_context; struct ioloop_notify_handler_context *notify_handler_context; unsigned int max_fd_count; io_loop_time_moved_callback_t *time_moved_callback; time_t next_max_time; uint64_t ioloop_wait_usecs; struct timeval wait_started; unsigned int io_pending_count; unsigned int running:1; unsigned int iolooping:1; }; struct io { enum io_condition condition; const char *source_filename; unsigned int source_linenum; /* trigger I/O callback even if OS doesn't think there is input pending */ bool pending; io_callback_t *callback; void *context; struct ioloop *ioloop; struct ioloop_context *ctx; }; struct io_file { struct io io; /* use a doubly linked list so that io_remove() is quick */ struct io_file *prev, *next; int refcount; int fd; /* only for io_add_istream(), a bit kludgy to be here.. */ struct istream *istream; }; struct timeout { struct priorityq_item item; const char *source_filename; unsigned int source_linenum; unsigned int msecs; struct timeval next_run; timeout_callback_t *callback; void *context; struct ioloop *ioloop; struct ioloop_context *ctx; unsigned int one_shot:1; }; struct io_wait_timer { struct io_wait_timer *prev, *next; const char *source_filename; unsigned int source_linenum; struct ioloop *ioloop; uint64_t usecs; }; struct ioloop_context_callback { io_callback_t *activate; io_callback_t *deactivate; void *context; bool activated; }; struct ioloop_context { int refcount; struct ioloop *ioloop; ARRAY(struct ioloop_context_callback) callbacks; }; int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r); void io_loop_handle_timeouts(struct ioloop *ioloop); void io_loop_call_io(struct io *io); void io_loop_handler_run_internal(struct ioloop *ioloop); /* I/O handler calls */ void io_loop_handle_add(struct io_file *io); void io_loop_handle_remove(struct io_file *io, bool closed); void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count); void io_loop_handler_deinit(struct ioloop *ioloop); void io_loop_notify_remove(struct io *io); void io_loop_notify_handler_deinit(struct ioloop *ioloop); void io_loop_context_activate(struct ioloop_context *ctx); void io_loop_context_deactivate(struct ioloop_context *ctx); #endif dovecot-2.2.33.2/src/lib/read-full.c0000644000175000017500000000124713123174404013704 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "read-full.h" #include int read_full(int fd, void *data, size_t size) { ssize_t ret; while (size > 0) { ret = read(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX); if (ret <= 0) return ret; data = PTR_OFFSET(data, ret); size -= ret; } return 1; } int pread_full(int fd, void *data, size_t size, off_t offset) { ssize_t ret; while (size > 0) { ret = pread(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX, offset); if (ret <= 0) return ret; data = PTR_OFFSET(data, ret); size -= ret; offset += ret; } return 1; } dovecot-2.2.33.2/src/lib/primes.h0000644000175000017500000000037313123174404013334 00000000000000#ifndef PRIMES_H #define PRIMES_H /* Returns a prime close to specified number, or the number itself if it's a prime. Note that the returned value may be smaller than requested! */ unsigned int primes_closest(unsigned int num) ATTR_CONST; #endif dovecot-2.2.33.2/src/lib/uri-util.h0000644000175000017500000000504313165463624013621 00000000000000#ifndef URI_UTIL_H #define URI_UTIL_H #include "net.h" /* * Generic URI parsing. */ struct uri_authority { const char *enc_userinfo; const char *host_literal; struct ip_addr host_ip; in_port_t port; unsigned int have_host_ip:1; unsigned int have_port:1; }; struct uri_parser { pool_t pool; const char *error; const unsigned char *begin, *cur, *end; string_t *tmpbuf; bool allow_pct_nul:1; }; int uri_parse_pct_encoded(struct uri_parser *parser, unsigned char *ch_r); int uri_parse_unreserved(struct uri_parser *parser, string_t *part); bool uri_data_decode(struct uri_parser *parser, const char *data, const char *until, const char **decoded_r) ATTR_NULL(3); int uri_cut_scheme(const char **uri_p, const char **scheme_r) ATTR_NULL(2); int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r) ATTR_NULL(2); int uri_parse_authority(struct uri_parser *parser, struct uri_authority *auth) ATTR_NULL(2); int uri_parse_slashslash_authority(struct uri_parser *parser, struct uri_authority *auth) ATTR_NULL(2); int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r) ATTR_NULL(2); int uri_parse_path(struct uri_parser *parser, int *relative_r, const char *const **path_r) ATTR_NULL(2,3); int uri_parse_query(struct uri_parser *parser, const char **query_r) ATTR_NULL(2); int uri_parse_fragment(struct uri_parser *parser, const char **fragment_r) ATTR_NULL(2); void uri_parser_init(struct uri_parser *parser, pool_t pool, const char *data); string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size); /* * Generic URI construction */ void uri_append_scheme(string_t *out, const char *scheme); void uri_append_user_data(string_t *out, const char *esc, const char *data); void uri_append_userinfo(string_t *out, const char *userinfo); void uri_append_host_name(string_t *out, const char *name); void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip); void uri_append_port(string_t *out, in_port_t port); void uri_append_path_segment_data(string_t *out, const char *esc, const char *data); void uri_append_path_segment(string_t *out, const char *segment); void uri_append_path_data(string_t *out, const char *esc, const char *data); void uri_append_path(string_t *out, const char *path); void uri_append_query_data(string_t *out, const char *esc, const char *data); void uri_append_query(string_t *out, const char *query); void uri_append_fragment_data(string_t *out, const char *esc, const char *data); void uri_append_fragment(string_t *out, const char *fragment); #endif dovecot-2.2.33.2/src/lib/fsync-mode.h0000644000175000017500000000055413123174404014102 00000000000000#ifndef FSYNC_MODE_H #define FSYNC_MODE_H enum fsync_mode { /* fsync when it's necessary for data safety. */ FSYNC_MODE_OPTIMIZED = 0, /* never fsync (in case of a crash can lose data) */ FSYNC_MODE_NEVER, /* fsync after all writes. this is necessary with NFS to avoid write failures being delayed until file is close(). */ FSYNC_MODE_ALWAYS }; #endif dovecot-2.2.33.2/src/lib/istream-jsonstr.c0000644000175000017500000000766713165463624015224 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-dec.h" #include "unichar.h" #include "istream-private.h" #include "istream-jsonstr.h" #define MAX_UTF8_LEN 6 struct jsonstr_istream { struct istream_private istream; /* The end '"' was found */ unsigned int str_end:1; }; static int i_stream_jsonstr_read_parent(struct jsonstr_istream *jstream, unsigned int min_bytes) { struct istream_private *stream = &jstream->istream; size_t size, avail; ssize_t ret; size = i_stream_get_data_size(stream->parent); while (size < min_bytes) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } size = i_stream_get_data_size(stream->parent); } if (!i_stream_try_alloc(stream, size, &avail)) return -2; return 1; } static int i_stream_json_unescape(const unsigned char *src, unsigned char *dest, unsigned int *src_size_r, unsigned int *dest_size_r) { switch (*src) { case '"': case '\\': case '/': *dest = *src; break; case 'b': *dest = '\b'; break; case 'f': *dest = '\f'; break; case 'n': *dest = '\n'; break; case 'r': *dest = '\r'; break; case 't': *dest = '\t'; break; case 'u': { buffer_t buf; buffer_create_from_data(&buf, dest, MAX_UTF8_LEN); uni_ucs4_to_utf8_c(hex2dec(src+1, 4), &buf); *src_size_r = 5; *dest_size_r = buf.used; return 0; } default: return -1; } *src_size_r = 1; *dest_size_r = 1; return 0; } static ssize_t i_stream_jsonstr_read(struct istream_private *stream) { struct jsonstr_istream *jstream = (struct jsonstr_istream *)stream; const unsigned char *data; unsigned int srcskip, destskip, extra; size_t i, dest, size; ssize_t ret; if (jstream->str_end) { stream->istream.eof = TRUE; return -1; } ret = i_stream_jsonstr_read_parent(jstream, 1); if (ret <= 0) return ret; /* @UNSAFE */ dest = stream->pos; extra = 0; data = i_stream_get_data(stream->parent, &size); for (i = 0; i < size && dest < stream->buffer_size; ) { if (data[i] == '"') { jstream->str_end = TRUE; if (dest == stream->pos) { stream->istream.eof = TRUE; return -1; } break; } else if (data[i] == '\\') { if (i+1 == size) { /* not enough input for \x */ extra = 1; break; } if ((data[i+1] == 'u' && i+1+4 >= size)) { /* not enough input for \u0000 */ extra = 5; break; } if (data[i+1] == 'u' && stream->buffer_size - dest < MAX_UTF8_LEN) { /* UTF8 output is max. 6 chars */ if (dest == stream->pos) return -2; break; } i++; if (i_stream_json_unescape(data + i, stream->w_buffer + dest, &srcskip, &destskip) < 0) { /* invalid string */ io_stream_set_error(&stream->iostream, "Invalid JSON string"); stream->istream.stream_errno = EINVAL; return -1; } i += srcskip; i_assert(i <= size); dest += destskip; i_assert(dest <= stream->buffer_size); } else { stream->w_buffer[dest++] = data[i]; i++; } } i_stream_skip(stream->parent, i); ret = dest - stream->pos; if (ret == 0) { /* not enough input */ i_assert(extra > 0); ret = i_stream_jsonstr_read_parent(jstream, i+extra+1); if (ret <= 0) return ret; return i_stream_jsonstr_read(stream); } i_assert(ret > 0); stream->pos = dest; return ret; } struct istream *i_stream_create_jsonstr(struct istream *input) { struct jsonstr_istream *dstream; dstream = i_new(struct jsonstr_istream, 1); dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; dstream->istream.read = i_stream_jsonstr_read; dstream->istream.istream.readable_fd = FALSE; dstream->istream.istream.blocking = input->blocking; dstream->istream.istream.seekable = FALSE; return i_stream_create(&dstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/pkcs5.h0000644000175000017500000000220613123174404013057 00000000000000#ifndef PKCS5_H #define PKCS5_H 1 enum pkcs5_pbkdf_mode { PKCS5_PBKDF1, PKCS5_PBKDF2 }; /* mode - v1.0 or v2.0 hash - hash_method_lookup return value password - private password for generation password_len - length of password in octets salt - salt for generation salt_len - length of salt in octets iterations - number of iterations to hash (use at least 1000, a very large number => very very slow) dk_len - number of bytes to return from derived key result - buffer_t to hold the result, either use dynamic or make sure it fits dk_len non-zero return value indicates that either iterations was less than 1 or dk_len was too large Sample code: buffer_t *result = buffer_create_dynamic(pool_datastack_create(), 256); if (pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha256"), "password", 8, "salt", 4, 4096, 256, result) != 0) { // error } */ int pkcs5_pbkdf(enum pkcs5_pbkdf_mode mode, const struct hash_method *hash, const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, unsigned int iterations, uint32_t dk_len, buffer_t *result); #endif dovecot-2.2.33.2/src/lib/guid.c0000644000175000017500000001111113165463624012763 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "str.h" #include "sha1.h" #include "hash.h" #include "hex-binary.h" #include "hostpid.h" #include "guid.h" #include #include const char *guid_generate(void) { static struct timespec ts = { 0, 0 }; static unsigned int pid = 0; /* we'll use the current time in nanoseconds as the initial 64bit counter. */ if (ts.tv_sec == 0) { if (clock_gettime(CLOCK_REALTIME, &ts) < 0) i_fatal("clock_gettime() failed: %m"); pid = getpid(); } else if ((uint32_t)ts.tv_nsec < (uint32_t)-1) { ts.tv_nsec++; } else { ts.tv_sec++; ts.tv_nsec = 0; } return t_strdup_printf("%04x%04lx%04x%s", (unsigned int)ts.tv_nsec, (unsigned long)ts.tv_sec, pid, my_hostname); } void guid_128_host_hash_get(const char *host, unsigned char hash_r[STATIC_ARRAY GUID_128_HOST_HASH_SIZE]) { unsigned char full_hash[SHA1_RESULTLEN]; sha1_get_digest(host, strlen(host), full_hash); memcpy(hash_r, full_hash + sizeof(full_hash)-GUID_128_HOST_HASH_SIZE, GUID_128_HOST_HASH_SIZE); } void guid_128_generate(guid_128_t guid_r) { #if GUID_128_HOST_HASH_SIZE != 4 # error GUID_128_HOST_HASH_SIZE must be 4 #endif static struct timespec ts = { 0, 0 }; static uint8_t guid_static[8]; uint32_t pid; /* we'll use the current time in nanoseconds as the initial 64bit counter. */ if (ts.tv_sec == 0) { if (clock_gettime(CLOCK_REALTIME, &ts) < 0) i_fatal("clock_gettime() failed: %m"); pid = getpid(); guid_static[0] = (pid & 0x000000ff); guid_static[1] = (pid & 0x0000ff00) >> 8; guid_static[2] = (pid & 0x00ff0000) >> 16; guid_static[3] = (pid & 0xff000000) >> 24; guid_128_host_hash_get(my_hostdomain(), guid_static+4); } else if (ioloop_timeval.tv_sec > ts.tv_sec || (ioloop_timeval.tv_sec == ts.tv_sec && ioloop_timeval.tv_usec * 1000 > ts.tv_nsec)) { /* use ioloop's time since we have it. it doesn't provide any more uniqueness, but it allows finding out more reliably when a GUID was created. */ ts.tv_sec = ioloop_timeval.tv_sec; ts.tv_nsec = ioloop_timeval.tv_usec*1000; } else if (ts.tv_nsec < 999999999L) { ts.tv_nsec++; } else { ts.tv_sec++; ts.tv_nsec = 0; } guid_r[0] = (ts.tv_nsec & 0x000000ff); guid_r[1] = (ts.tv_nsec & 0x0000ff00) >> 8; guid_r[2] = (ts.tv_nsec & 0x00ff0000) >> 16; guid_r[3] = (ts.tv_nsec & 0xff000000) >> 24; guid_r[4] = (ts.tv_sec & 0x000000ff); guid_r[5] = (ts.tv_sec & 0x0000ff00) >> 8; guid_r[6] = (ts.tv_sec & 0x00ff0000) >> 16; guid_r[7] = (ts.tv_sec & 0xff000000) >> 24; memcpy(guid_r + 8, guid_static, 8); } bool guid_128_is_empty(const guid_128_t guid) { unsigned int i; for (i = 0; i < GUID_128_SIZE; i++) { if (guid[i] != 0) return FALSE; } return TRUE; } bool guid_128_equals(const guid_128_t guid1, const guid_128_t guid2) { return memcmp(guid1, guid2, GUID_128_SIZE) == 0; } int guid_128_from_string(const char *str, guid_128_t guid_r) { buffer_t buf; buffer_create_from_data(&buf, guid_r, GUID_128_SIZE); return strlen(str) == GUID_128_SIZE*2 && hex_to_binary(str, &buf) == 0 && buf.used == GUID_128_SIZE ? 0 : -1; } const char *guid_128_to_string(const guid_128_t guid) { return binary_to_hex(guid, GUID_128_SIZE); } unsigned int guid_128_hash(const guid_128_t guid) { return mem_hash(guid, GUID_128_SIZE); } int guid_128_cmp(const guid_128_t guid1, const guid_128_t guid2) { return memcmp(guid1, guid2, GUID_128_SIZE); } const char *guid_128_to_uuid_string(const guid_128_t guid, enum uuid_format format) { switch(format) { case FORMAT_COMPACT: return guid_128_to_string(guid); case FORMAT_RECORD: return t_strdup_printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid[0], guid[1], guid[2], guid[3], guid[4], guid[5], guid[6], guid[7], guid[8], guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]); case FORMAT_MICROSOFT: return t_strdup_printf("{%s}", guid_128_to_uuid_string(guid, FORMAT_RECORD)); } i_unreached(); } int guid_128_from_uuid_string(const char *str, guid_128_t guid_r) { size_t i,len,m=0; int ret; T_BEGIN { len = strlen(str); string_t *str2 = t_str_new(len); for(i=0; i < len; i++) { /* Microsoft format */ if (i==0 && str[i] == '{') { m=1; continue; } else if (i == len-1 && str[i] == '}') continue; /* 8-4-4-4-12 */ if (((i==8+m) || (i==13+m) || (i==18+m) || (i==23+m)) && str[i] == '-') continue; str_append_c(str2, str[i]); } ret = guid_128_from_string(str_c(str2), guid_r); } T_END; return ret; } dovecot-2.2.33.2/src/lib/connection.h0000644000175000017500000001157713165463624014217 00000000000000#ifndef CONNECTION_H #define CONNECTION_H #include "net.h" struct connection; enum connection_behavior { CONNECTION_BEHAVIOR_DESTROY = 0, CONNECTION_BEHAVIOR_ALLOW }; enum connection_disconnect_reason { /* not disconnected yet */ CONNECTION_DISCONNECT_NOT = 0, /* normal requested disconnection */ CONNECTION_DISCONNECT_DEINIT, /* input buffer full */ CONNECTION_DISCONNECT_BUFFER_FULL, /* connection got disconnected */ CONNECTION_DISCONNECT_CONN_CLOSED, /* connect() timed out */ CONNECTION_DISCONNECT_CONNECT_TIMEOUT, /* remote didn't send input */ CONNECTION_DISCONNECT_IDLE_TIMEOUT }; struct connection_vfuncs { void (*destroy)(struct connection *conn); /* For UNIX socket clients this gets called immediately (unless delayed_unix_client_connected_callback=TRUE) with success=TRUE, for IP connections it gets called later: If connect() fails, sets success=FALSE and errno. Streams aren't initialized in that situation either. destroy() is called after the callback. */ void (*client_connected)(struct connection *conn, bool success); /* implement one of the input*() methods. They return 1 = ok, continue. 0 = ok, but stop processing more lines, -1 = error, disconnect the client. */ void (*input)(struct connection *conn); int (*input_line)(struct connection *conn, const char *line); int (*input_args)(struct connection *conn, const char *const *args); }; struct connection_settings { const char *service_name_in; const char *service_name_out; unsigned int major_version, minor_version; unsigned int client_connect_timeout_msecs; unsigned int input_idle_timeout_secs; size_t input_max_size; size_t output_max_size; enum connection_behavior input_full_behavior; bool client; bool dont_send_version; /* By default when only input_args() is used, or when connection_input_line_default() is used, empty lines aren't allowed since it would result in additional args[0] == NULL check. Setting this to TRUE passes it through instead of logging an error. */ bool allow_empty_args_input; /* Don't call client_connected() immediately on connection_client_connect() with UNIX sockets. This is mainly to make the functionality identical with inet sockets, which may simplify the calling code. */ bool delayed_unix_client_connected_callback; /* If connect() to UNIX socket fails with EAGAIN, retry for this many milliseconds before giving up (0 = try once) */ unsigned int unix_client_connect_msecs; }; struct connection { struct connection *prev, *next; struct connection_list *list; char *name; int fd_in, fd_out; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; time_t last_input; struct timeval last_input_tv; struct timeval connect_started; struct timeval connect_finished; /* for IP client: */ struct ip_addr ip; in_port_t port; /* received minor version */ unsigned int minor_version; enum connection_disconnect_reason disconnect_reason; unsigned int version_received:1; unsigned int unix_socket:1; }; struct connection_list { struct connection *connections; unsigned int connections_count; struct connection_settings set; struct connection_vfuncs v; }; void connection_init_server(struct connection_list *list, struct connection *conn, const char *name, int fd_in, int fd_out); void connection_init_client_ip(struct connection_list *list, struct connection *conn, const struct ip_addr *ip, in_port_t port); void connection_init_client_unix(struct connection_list *list, struct connection *conn, const char *path); void connection_init_from_streams(struct connection_list *list, struct connection *conn, const char *name, struct istream *input, struct ostream *output); int connection_client_connect(struct connection *conn); void connection_disconnect(struct connection *conn); void connection_deinit(struct connection *conn); /* Returns -1 = disconnected, 0 = nothing new, 1 = something new. If input_full_behavior is ALLOW, may return also -2 = buffer full. */ int connection_input_read(struct connection *conn); /* Verify that VERSION input matches what we expect. */ int connection_verify_version(struct connection *conn, const char *const *args); /* Returns human-readable reason for why connection was disconnected. */ const char *connection_disconnect_reason(struct connection *conn); /* Returns human-readable reason for why connection timed out, e.g. "No input for 10.023 secs". */ const char *connection_input_timeout_reason(struct connection *conn); void connection_switch_ioloop(struct connection *conn); struct connection_list * connection_list_init(const struct connection_settings *set, const struct connection_vfuncs *vfuncs); void connection_list_deinit(struct connection_list **list); void connection_input_default(struct connection *conn); int connection_input_line_default(struct connection *conn, const char *line); #endif dovecot-2.2.33.2/src/lib/ostream-unix.c0000644000175000017500000000413413165463624014475 00000000000000/* Copyright (c) 2015-2016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fdpass.h" #include "ostream-file-private.h" #include "ostream-unix.h" struct unix_ostream { struct file_ostream fstream; int write_fd; }; static void o_stream_unix_close(struct iostream_private *stream, bool close_parent) { struct unix_ostream *ustream = (struct unix_ostream *)stream; if (ustream->write_fd != -1) i_close_fd(&ustream->write_fd); o_stream_file_close(stream, close_parent); } static ssize_t o_stream_unix_writev(struct file_ostream *fstream, const struct const_iovec *iov, unsigned int iov_count) { struct unix_ostream *ustream = (struct unix_ostream *)fstream; size_t sent; ssize_t ret; if (ustream->write_fd == -1) { /* no fd */ return o_stream_file_writev(fstream, iov, iov_count); } /* send first iovec along with fd */ if (iov_count == 0) return 0; i_assert(iov[0].iov_len > 0); ret = fd_send(fstream->fd, ustream->write_fd, iov[0].iov_base, iov[0].iov_len); if (ret < 0) return ret; /* update stream */ sent = ret; fstream->real_offset += sent; ustream->write_fd = -1; if (sent < iov[0].iov_len || iov_count == 1) { /* caller will call us again to write the rest */ return sent; } /* send remaining iovecs */ ret = o_stream_file_writev(fstream, &iov[1], iov_count-1); if (ret < 0) return (errno == EAGAIN || errno == EINTR ? (ssize_t)sent : ret); sent += ret; return sent; } struct ostream *o_stream_create_unix(int fd, size_t max_buffer_size) { struct unix_ostream *ustream; struct ostream *output; i_assert(fd != -1); ustream = i_new(struct unix_ostream, 1); ustream->write_fd = -1; output = o_stream_create_file_common(&ustream->fstream, fd, max_buffer_size, FALSE); output->real_stream->iostream.close = o_stream_unix_close; ustream->fstream.writev = o_stream_unix_writev; return output; } bool o_stream_unix_write_fd(struct ostream *output, int fd) { struct unix_ostream *ustream = (struct unix_ostream *)output->real_stream; if (ustream->write_fd >= 0) return FALSE; ustream->write_fd = fd; return TRUE; } dovecot-2.2.33.2/src/lib/restrict-access.c0000644000175000017500000003302413165463624015140 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #define _GNU_SOURCE /* setresgid() */ #include /* for AIX */ #include #include #include "lib.h" #include "str.h" #include "restrict-access.h" #include "env-util.h" #include "ipwd.h" #include #ifdef HAVE_PR_SET_DUMPABLE # include #endif static gid_t process_primary_gid = (gid_t)-1; static gid_t process_privileged_gid = (gid_t)-1; static bool process_using_priv_gid = FALSE; static char *chroot_dir = NULL; void restrict_access_init(struct restrict_access_settings *set) { i_zero(set); set->uid = (uid_t)-1; set->gid = (gid_t)-1; set->privileged_gid = (gid_t)-1; } static const char *get_uid_str(uid_t uid) { struct passwd pw; const char *ret; int old_errno = errno; if (i_getpwuid(uid, &pw) <= 0) ret = dec2str(uid); else ret = t_strdup_printf("%s(%s)", dec2str(uid), pw.pw_name); errno = old_errno; return ret; } static const char *get_gid_str(gid_t gid) { struct group group; const char *ret; int old_errno = errno; if (i_getgrgid(gid, &group) <= 0) ret = dec2str(gid); else ret = t_strdup_printf("%s(%s)", dec2str(gid), group.gr_name); errno = old_errno; return ret; } static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid, const char *gid_source) { string_t *str; if (privileged_gid == (gid_t)-1) { if (primary_gid == getgid() && primary_gid == getegid()) { /* everything is already set */ return; } if (setgid(primary_gid) == 0) return; str = t_str_new(128); str_printfa(str, "setgid(%s", get_gid_str(primary_gid)); if (gid_source != NULL) str_printfa(str, " from %s", gid_source); str_printfa(str, ") failed with euid=%s, gid=%s, egid=%s: %m " "(This binary should probably be called with " "process group set to %s instead of %s)", get_uid_str(geteuid()), get_gid_str(getgid()), get_gid_str(getegid()), get_gid_str(primary_gid), get_gid_str(getegid())); i_fatal("%s", str_c(str)); } if (getegid() != 0 && primary_gid == getgid() && primary_gid == getegid()) { /* privileged_gid is hopefully in saved ID. if not, there's nothing we can do about it. */ return; } #ifdef HAVE_SETRESGID if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) { i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m", get_gid_str(primary_gid), get_gid_str(primary_gid), get_gid_str(privileged_gid), get_uid_str(geteuid())); } #else if (geteuid() == 0) { /* real, effective, saved -> privileged_gid */ if (setgid(privileged_gid) < 0) { i_fatal("setgid(%s) failed: %m", get_gid_str(privileged_gid)); } } /* real, effective -> primary_gid saved -> keep */ if (setregid(primary_gid, primary_gid) != 0) { i_fatal("setregid(%s,%s) failed with euid=%s: %m", get_gid_str(primary_gid), get_gid_str(privileged_gid), get_uid_str(geteuid())); } #endif } gid_t *restrict_get_groups_list(unsigned int *gid_count_r) { gid_t *gid_list; int ret, gid_count; if ((gid_count = getgroups(0, NULL)) < 0) i_fatal("getgroups() failed: %m"); /* @UNSAFE */ gid_list = t_new(gid_t, gid_count+1); /* +1 in case gid_count=0 */ if ((ret = getgroups(gid_count, gid_list)) < 0) i_fatal("getgroups() failed: %m"); *gid_count_r = ret; return gid_list; } static void drop_restricted_groups(const struct restrict_access_settings *set, gid_t *gid_list, unsigned int *gid_count, bool *have_root_group) { /* @UNSAFE */ unsigned int i, used; for (i = 0, used = 0; i < *gid_count; i++) { if (gid_list[i] >= set->first_valid_gid && (set->last_valid_gid == 0 || gid_list[i] <= set->last_valid_gid)) { if (gid_list[i] == 0) *have_root_group = TRUE; gid_list[used++] = gid_list[i]; } } *gid_count = used; } static gid_t get_group_id(const char *name) { struct group group; gid_t gid; if (str_to_gid(name, &gid) == 0) return gid; switch (i_getgrnam(name, &group)) { case -1: i_fatal("getgrnam(%s) failed: %m", name); case 0: i_fatal("unknown group name in extra_groups: %s", name); default: return group.gr_gid; } } static void fix_groups_list(const struct restrict_access_settings *set, bool preserve_existing, bool *have_root_group) { gid_t gid, *gid_list, *gid_list2; const char *const *tmp, *empty = NULL; unsigned int i, gid_count; bool add_primary_gid; /* if we're using a privileged GID, we can temporarily drop our effective GID. we still want to be able to use its privileges, so add it to supplementary groups. */ add_primary_gid = process_privileged_gid != (gid_t)-1; tmp = set->extra_groups == NULL ? &empty : t_strsplit_spaces(set->extra_groups, ", "); if (preserve_existing) { gid_list = restrict_get_groups_list(&gid_count); drop_restricted_groups(set, gid_list, &gid_count, have_root_group); /* see if the list already contains the primary GID */ for (i = 0; i < gid_count; i++) { if (gid_list[i] == process_primary_gid) { add_primary_gid = FALSE; break; } } } else { gid_list = NULL; gid_count = 0; } if (gid_count == 0) { /* Some OSes don't like an empty groups list, so use the primary GID as the only one. */ gid_list = t_new(gid_t, 2); gid_list[0] = process_primary_gid; gid_count = 1; add_primary_gid = FALSE; } if (*tmp != NULL || add_primary_gid) { /* @UNSAFE: add extra groups and/or primary GID to gids list */ gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1); memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t)); for (; *tmp != NULL; tmp++) { gid = get_group_id(*tmp); if (gid != process_primary_gid) gid_list2[gid_count++] = gid; } if (add_primary_gid) gid_list2[gid_count++] = process_primary_gid; gid_list = gid_list2; } if (setgroups(gid_count, gid_list) < 0) { if (errno == EINVAL) { i_fatal("setgroups(%s) failed: Too many extra groups", set->extra_groups == NULL ? "" : set->extra_groups); } else { i_fatal("setgroups() failed: %m"); } } } static const char * get_setuid_error_str(const struct restrict_access_settings *set, uid_t target_uid) { string_t *str = t_str_new(128); str_printfa(str, "setuid(%s", get_uid_str(target_uid)); if (set->uid_source != NULL) str_printfa(str, " from %s", set->uid_source); str_printfa(str, ") failed with euid=%s: %m ", get_uid_str(geteuid())); if (errno == EAGAIN) { str_append(str, "(ulimit -u reached)"); } else { str_printfa(str, "(This binary should probably be called with " "process user set to %s instead of %s)", get_uid_str(target_uid), get_uid_str(geteuid())); } return str_c(str); } void restrict_access(const struct restrict_access_settings *set, const char *home, bool disallow_root) { bool is_root, have_root_group, preserve_groups = FALSE; bool allow_root_gid; uid_t target_uid = set->uid; is_root = geteuid() == 0; if (!is_root && set->drop_setuid_root && getuid() == 0) { /* recover current effective UID */ if (target_uid == (uid_t)-1) target_uid = geteuid(); else i_assert(target_uid > 0); /* try to elevate to root */ if (seteuid(0) < 0) i_fatal("seteuid(0) failed: %m"); is_root = TRUE; } /* set the primary/privileged group */ process_primary_gid = set->gid; process_privileged_gid = set->privileged_gid; if (process_privileged_gid == process_primary_gid) { /* a pointless configuration, ignore it */ process_privileged_gid = (gid_t)-1; } have_root_group = process_primary_gid == 0; if (process_primary_gid != (gid_t)-1 || process_privileged_gid != (gid_t)-1) { if (process_primary_gid == (gid_t)-1) process_primary_gid = getegid(); restrict_init_groups(process_primary_gid, process_privileged_gid, set->gid_source); } else { if (process_primary_gid == (gid_t)-1) process_primary_gid = getegid(); } /* set system user's groups */ if (set->system_groups_user != NULL && is_root) { if (initgroups(set->system_groups_user, process_primary_gid) < 0) { i_fatal("initgroups(%s, %s) failed: %m", set->system_groups_user, get_gid_str(process_primary_gid)); } preserve_groups = TRUE; } /* add extra groups. if we set system user's groups, drop the restricted groups at the same time. */ if (is_root) T_BEGIN { fix_groups_list(set, preserve_groups, &have_root_group); } T_END; /* chrooting */ if (set->chroot_dir != NULL) { /* kludge: localtime() must be called before chroot(), or the timezone isn't known */ time_t t = 0; (void)localtime(&t); if (chroot(set->chroot_dir) != 0) i_fatal("chroot(%s) failed: %m", set->chroot_dir); chroot_dir = i_strdup(set->chroot_dir); if (home != NULL) { if (chdir(home) < 0) { i_error("chdir(%s) failed: %m", home); home = NULL; } } if (home == NULL) { if (chdir("/") != 0) i_fatal("chdir(/) failed: %m"); } } /* uid last */ if (target_uid != (uid_t)-1) { if (setuid(target_uid) != 0) i_fatal("%s", get_setuid_error_str(set, target_uid)); } /* verify that we actually dropped the privileges */ if ((target_uid != (uid_t)-1 && target_uid != 0) || disallow_root) { if (setuid(0) == 0) { if (disallow_root && (target_uid == 0 || target_uid == (uid_t)-1)) i_fatal("This process must not be run as root"); i_fatal("We couldn't drop root privileges"); } } if (set->first_valid_gid != 0) allow_root_gid = FALSE; else if (process_primary_gid == 0 || process_privileged_gid == 0) allow_root_gid = TRUE; else allow_root_gid = FALSE; if (!allow_root_gid && target_uid != 0 && (target_uid != (uid_t)-1 || !is_root)) { if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) { if (process_primary_gid == 0) i_fatal("GID 0 isn't permitted"); i_fatal("We couldn't drop root group privileges " "(wanted=%s, gid=%s, egid=%s)", get_gid_str(process_primary_gid), get_gid_str(getgid()), get_gid_str(getegid())); } } } void restrict_access_set_env(const struct restrict_access_settings *set) { if (set->system_groups_user != NULL && *set->system_groups_user != '\0') { env_put(t_strconcat("RESTRICT_USER=", set->system_groups_user, NULL)); } if (set->chroot_dir != NULL && *set->chroot_dir != '\0') env_put(t_strconcat("RESTRICT_CHROOT=", set->chroot_dir, NULL)); if (set->uid != (uid_t)-1) { env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(set->uid))); } if (set->gid != (gid_t)-1) { env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(set->gid))); } if (set->privileged_gid != (gid_t)-1) { env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s", dec2str(set->privileged_gid))); } if (set->extra_groups != NULL && *set->extra_groups != '\0') { env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=", set->extra_groups, NULL)); } if (set->first_valid_gid != 0) { env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s", dec2str(set->first_valid_gid))); } if (set->last_valid_gid != 0) { env_put(t_strdup_printf("RESTRICT_GID_LAST=%s", dec2str(set->last_valid_gid))); } } static const char *null_if_empty(const char *str) { return str == NULL || *str == '\0' ? NULL : str; } void restrict_access_get_env(struct restrict_access_settings *set_r) { const char *value; restrict_access_init(set_r); if ((value = getenv("RESTRICT_SETUID")) != NULL) { if (str_to_uid(value, &set_r->uid) < 0) i_fatal("Invalid uid: %s", value); } if ((value = getenv("RESTRICT_SETGID")) != NULL) { if (str_to_gid(value, &set_r->gid) < 0) i_fatal("Invalid gid: %s", value); } if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL) { if (str_to_gid(value, &set_r->privileged_gid) < 0) i_fatal("Invalid privileged_gid: %s", value); } if ((value = getenv("RESTRICT_GID_FIRST")) != NULL) { if (str_to_gid(value, &set_r->first_valid_gid) < 0) i_fatal("Invalid first_valid_gid: %s", value); } if ((value = getenv("RESTRICT_GID_LAST")) != NULL) { if (str_to_gid(value, &set_r->last_valid_gid) < 0) i_fatal("Invalid last_value_gid: %s", value); } set_r->extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS")); set_r->system_groups_user = null_if_empty(getenv("RESTRICT_USER")); set_r->chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT")); } void restrict_access_by_env(const char *home, bool disallow_root) { struct restrict_access_settings set; restrict_access_get_env(&set); restrict_access(&set, home, disallow_root); /* clear the environment, so we don't fail if we get back here */ env_remove("RESTRICT_SETUID"); if (process_privileged_gid == (gid_t)-1) { /* if we're dropping privileges before executing and a privileged group is set, the groups must be fixed after exec */ env_remove("RESTRICT_SETGID"); env_remove("RESTRICT_SETGID_PRIV"); } env_remove("RESTRICT_GID_FIRST"); env_remove("RESTRICT_GID_LAST"); env_remove("RESTRICT_SETEXTRAGROUPS"); env_remove("RESTRICT_USER"); env_remove("RESTRICT_CHROOT"); } const char *restrict_access_get_current_chroot(void) { return chroot_dir; } void restrict_access_allow_coredumps(bool allow ATTR_UNUSED) { #ifdef HAVE_PR_SET_DUMPABLE (void)prctl(PR_SET_DUMPABLE, allow, 0, 0, 0); #endif } int restrict_access_use_priv_gid(void) { i_assert(!process_using_priv_gid); if (process_privileged_gid == (gid_t)-1) return 0; if (setegid(process_privileged_gid) < 0) { i_error("setegid(privileged) failed: %m"); return -1; } process_using_priv_gid = TRUE; return 0; } void restrict_access_drop_priv_gid(void) { if (!process_using_priv_gid) return; if (setegid(process_primary_gid) < 0) i_fatal("setegid(primary) failed: %m"); process_using_priv_gid = FALSE; } bool restrict_access_have_priv_gid(void) { return process_privileged_gid != (gid_t)-1; } dovecot-2.2.33.2/src/lib/ostream.c0000644000175000017500000004112613165463624013516 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "ostream-private.h" void o_stream_set_name(struct ostream *stream, const char *name) { i_free(stream->real_stream->iostream.name); stream->real_stream->iostream.name = i_strdup(name); } const char *o_stream_get_name(struct ostream *stream) { while (stream->real_stream->iostream.name == NULL) { stream = stream->real_stream->parent; if (stream == NULL) return ""; } return stream->real_stream->iostream.name; } int o_stream_get_fd(struct ostream *stream) { return stream->real_stream->fd; } const char *o_stream_get_error(struct ostream *stream) { struct ostream *s; /* we'll only return errors for streams that have stream_errno set. we might be returning unintended error otherwise. */ if (stream->stream_errno == 0) return ""; for (s = stream; s != NULL; s = s->real_stream->parent) { if (s->stream_errno == 0) break; if (s->real_stream->iostream.error != NULL) return s->real_stream->iostream.error; } return strerror(stream->stream_errno); } static void o_stream_close_full(struct ostream *stream, bool close_parents) { if (!stream->closed && !stream->real_stream->closing) { /* first mark the stream as being closed so the o_stream_copy_error_from_parent() won't recurse us back here. but don't immediately mark the stream closed, because we may still want to write something to it. */ stream->real_stream->closing = TRUE; io_stream_close(&stream->real_stream->iostream, close_parents); stream->closed = TRUE; } if (stream->stream_errno != 0) i_assert(stream->last_failed_errno != 0); else { stream->stream_errno = EPIPE; stream->last_failed_errno = EPIPE; } } void o_stream_destroy(struct ostream **stream) { o_stream_close_full(*stream, FALSE); o_stream_unref(stream); } void o_stream_ref(struct ostream *stream) { io_stream_ref(&stream->real_stream->iostream); } void o_stream_unref(struct ostream **_stream) { struct ostream *stream = *_stream; if (stream->real_stream->last_errors_not_checked && !stream->real_stream->error_handling_disabled && stream->real_stream->iostream.refcount == 1) { i_panic("output stream %s is missing error handling", o_stream_get_name(stream)); } io_stream_unref(&stream->real_stream->iostream); *_stream = NULL; } #undef o_stream_add_destroy_callback void o_stream_add_destroy_callback(struct ostream *stream, ostream_callback_t *callback, void *context) { io_stream_add_destroy_callback(&stream->real_stream->iostream, callback, context); } void o_stream_remove_destroy_callback(struct ostream *stream, void (*callback)()) { io_stream_remove_destroy_callback(&stream->real_stream->iostream, callback); } void o_stream_close(struct ostream *stream) { o_stream_close_full(stream, TRUE); } #undef o_stream_set_flush_callback void o_stream_set_flush_callback(struct ostream *stream, stream_flush_callback_t *callback, void *context) { struct ostream_private *_stream = stream->real_stream; _stream->set_flush_callback(_stream, callback, context); } void o_stream_unset_flush_callback(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; _stream->set_flush_callback(_stream, NULL, NULL); } void o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size) { io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size); } size_t o_stream_get_max_buffer_size(struct ostream *stream) { return stream->real_stream->max_buffer_size; } void o_stream_cork(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; if (unlikely(stream->closed || stream->stream_errno != 0)) return; _stream->cork(_stream, TRUE); if (stream->stream_errno != 0) errno = stream->last_failed_errno = stream->stream_errno; } void o_stream_uncork(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; if (unlikely(stream->closed || stream->stream_errno != 0)) return; _stream->cork(_stream, FALSE); if (stream->stream_errno != 0) errno = stream->last_failed_errno = stream->stream_errno; } bool o_stream_is_corked(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; return _stream->corked; } int o_stream_flush(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; int ret = 1; if (unlikely(stream->closed || stream->stream_errno != 0)) { errno = stream->stream_errno; return -1; } if (unlikely((ret = _stream->flush(_stream)) < 0)) { i_assert(stream->stream_errno != 0); stream->last_failed_errno = stream->stream_errno; errno = stream->stream_errno; } return ret; } void o_stream_set_flush_pending(struct ostream *stream, bool set) { struct ostream_private *_stream = stream->real_stream; if (unlikely(stream->closed || stream->stream_errno != 0)) return; _stream->flush_pending(_stream, set); } size_t o_stream_get_buffer_used_size(const struct ostream *stream) { const struct ostream_private *_stream = stream->real_stream; return _stream->get_used_size(_stream); } size_t o_stream_get_buffer_avail_size(const struct ostream *stream) { size_t used = o_stream_get_buffer_used_size(stream); return stream->real_stream->max_buffer_size <= used ? 0 : stream->real_stream->max_buffer_size - used; } int o_stream_seek(struct ostream *stream, uoff_t offset) { struct ostream_private *_stream = stream->real_stream; if (unlikely(stream->closed || stream->stream_errno != 0)) { errno = stream->stream_errno; return -1; } if (unlikely(_stream->seek(_stream, offset) < 0)) { i_assert(stream->stream_errno != 0); stream->last_failed_errno = stream->stream_errno; errno = stream->stream_errno; return -1; } return 1; } ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size) { struct const_iovec iov; i_zero(&iov); iov.iov_base = data; iov.iov_len = size; return o_stream_sendv(stream, &iov, 1); } ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count) { struct ostream_private *_stream = stream->real_stream; unsigned int i; size_t total_size; ssize_t ret; if (unlikely(stream->closed || stream->stream_errno != 0)) { errno = stream->stream_errno; return -1; } for (i = 0, total_size = 0; i < iov_count; i++) total_size += iov[i].iov_len; if (total_size == 0) return 0; ret = _stream->sendv(_stream, iov, iov_count); if (unlikely(ret != (ssize_t)total_size)) { if (ret < 0) { i_assert(stream->stream_errno != 0); stream->last_failed_errno = stream->stream_errno; errno = stream->stream_errno; } else { i_assert(!stream->blocking); stream->overflow = TRUE; } } return ret; } ssize_t o_stream_send_str(struct ostream *stream, const char *str) { return o_stream_send(stream, str, strlen(str)); } void o_stream_nsend(struct ostream *stream, const void *data, size_t size) { struct const_iovec iov; i_zero(&iov); iov.iov_base = data; iov.iov_len = size; o_stream_nsendv(stream, &iov, 1); } void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count) { if (unlikely(stream->closed || stream->stream_errno != 0)) return; (void)o_stream_sendv(stream, iov, iov_count); stream->real_stream->last_errors_not_checked = TRUE; } void o_stream_nsend_str(struct ostream *stream, const char *str) { o_stream_nsend(stream, str, strlen(str)); } void o_stream_nflush(struct ostream *stream) { if (unlikely(stream->closed || stream->stream_errno != 0)) return; (void)o_stream_flush(stream); stream->real_stream->last_errors_not_checked = TRUE; } int o_stream_nfinish(struct ostream *stream) { o_stream_nflush(stream); o_stream_ignore_last_errors(stream); errno = stream->last_failed_errno; return stream->last_failed_errno != 0 ? -1 : 0; } void o_stream_ignore_last_errors(struct ostream *stream) { while (stream != NULL) { stream->real_stream->last_errors_not_checked = FALSE; stream = stream->real_stream->parent; } } void o_stream_set_no_error_handling(struct ostream *stream, bool set) { stream->real_stream->error_handling_disabled = set; } off_t o_stream_send_istream(struct ostream *outstream, struct istream *instream) { struct ostream_private *_outstream = outstream->real_stream; off_t ret; if (unlikely(outstream->closed || instream->closed || outstream->stream_errno != 0)) { errno = outstream->stream_errno; return -1; } ret = _outstream->send_istream(_outstream, instream); if (unlikely(ret < 0)) { if (outstream->stream_errno != 0) { outstream->last_failed_errno = outstream->stream_errno; errno = outstream->stream_errno; } else { i_assert(instream->stream_errno != 0); } } return ret; } int o_stream_pwrite(struct ostream *stream, const void *data, size_t size, uoff_t offset) { int ret; if (unlikely(stream->closed || stream->stream_errno != 0)) { errno = stream->stream_errno; return -1; } ret = stream->real_stream->write_at(stream->real_stream, data, size, offset); if (unlikely(ret < 0)) { i_assert(stream->stream_errno != 0); stream->last_failed_errno = stream->stream_errno; errno = stream->stream_errno; } return ret; } off_t io_stream_copy(struct ostream *outstream, struct istream *instream) { uoff_t start_offset; struct const_iovec iov; const unsigned char *data; ssize_t ret; start_offset = instream->v_offset; do { (void)i_stream_read_data(instream, &data, &iov.iov_len, 0); if (iov.iov_len == 0) { /* all sent */ if (instream->stream_errno != 0) return -1; break; } iov.iov_base = data; ret = o_stream_sendv(outstream, &iov, 1); if (ret <= 0) { if (ret == 0) break; return -1; } i_stream_skip(instream, ret); } while ((size_t)ret == iov.iov_len); return (off_t)(instream->v_offset - start_offset); } void o_stream_switch_ioloop(struct ostream *stream) { struct ostream_private *_stream = stream->real_stream; _stream->switch_ioloop(_stream); } static void o_stream_default_close(struct iostream_private *stream, bool close_parent) { struct ostream_private *_stream = (struct ostream_private *)stream; (void)o_stream_flush(&_stream->ostream); if (close_parent && _stream->parent != NULL) o_stream_close(_stream->parent); } static void o_stream_default_destroy(struct iostream_private *stream) { struct ostream_private *_stream = (struct ostream_private *)stream; if (_stream->parent != NULL) o_stream_unref(&_stream->parent); } static void o_stream_default_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct ostream_private *_stream = (struct ostream_private *)stream; if (_stream->parent != NULL) o_stream_set_max_buffer_size(_stream->parent, max_size); _stream->max_buffer_size = max_size; } static void o_stream_default_cork(struct ostream_private *_stream, bool set) { _stream->corked = set; if (set) { if (_stream->parent != NULL) o_stream_cork(_stream->parent); } else { (void)o_stream_flush(&_stream->ostream); if (_stream->parent != NULL) o_stream_uncork(_stream->parent); } } void o_stream_copy_error_from_parent(struct ostream_private *_stream) { struct ostream *src = _stream->parent; struct ostream *dest = &_stream->ostream; dest->stream_errno = src->stream_errno; dest->last_failed_errno = src->last_failed_errno; dest->overflow = src->overflow; if (src->closed) o_stream_close(dest); } int o_stream_flush_parent_if_needed(struct ostream_private *_stream) { if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE) { /* we already have quite a lot of data in parent stream. unless we can flush it, don't add any more to it or we could keep wasting memory by just increasing the buffer size all the time. */ if (o_stream_flush(_stream->parent) < 0) { o_stream_copy_error_from_parent(_stream); return -1; } if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE) return 0; } return 1; } static int o_stream_default_flush(struct ostream_private *_stream) { int ret; if (_stream->parent == NULL) return 1; if ((ret = o_stream_flush(_stream->parent)) < 0) o_stream_copy_error_from_parent(_stream); return ret; } static void o_stream_default_set_flush_callback(struct ostream_private *_stream, stream_flush_callback_t *callback, void *context) { if (_stream->parent != NULL) o_stream_set_flush_callback(_stream->parent, callback, context); _stream->callback = callback; _stream->context = context; } static void o_stream_default_set_flush_pending(struct ostream_private *_stream, bool set) { if (_stream->parent != NULL) o_stream_set_flush_pending(_stream->parent, set); } static size_t o_stream_default_get_used_size(const struct ostream_private *_stream) { if (_stream->parent == NULL) return 0; else return o_stream_get_buffer_used_size(_stream->parent); } static int o_stream_default_seek(struct ostream_private *_stream, uoff_t offset ATTR_UNUSED) { _stream->ostream.stream_errno = ESPIPE; _stream->ostream.last_failed_errno = ESPIPE; return -1; } static ssize_t o_stream_default_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { ssize_t ret; if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { o_stream_copy_error_from_parent(stream); return -1; } stream->ostream.offset += ret; return ret; } static int o_stream_default_write_at(struct ostream_private *_stream, const void *data ATTR_UNUSED, size_t size ATTR_UNUSED, uoff_t offset ATTR_UNUSED) { _stream->ostream.stream_errno = ESPIPE; _stream->ostream.last_failed_errno = ESPIPE; return -1; } static off_t o_stream_default_send_istream(struct ostream_private *outstream, struct istream *instream) { return io_stream_copy(&outstream->ostream, instream); } static void o_stream_default_switch_ioloop(struct ostream_private *_stream) { if (_stream->parent != NULL) o_stream_switch_ioloop(_stream->parent); } struct ostream * o_stream_create(struct ostream_private *_stream, struct ostream *parent, int fd) { _stream->fd = fd; _stream->ostream.real_stream = _stream; if (parent != NULL) { _stream->ostream.blocking = parent->blocking; _stream->parent = parent; o_stream_ref(parent); _stream->callback = parent->real_stream->callback; _stream->context = parent->real_stream->context; _stream->max_buffer_size = parent->real_stream->max_buffer_size; _stream->error_handling_disabled = parent->real_stream->error_handling_disabled; } if (_stream->iostream.close == NULL) _stream->iostream.close = o_stream_default_close; if (_stream->iostream.destroy == NULL) _stream->iostream.destroy = o_stream_default_destroy; if (_stream->iostream.set_max_buffer_size == NULL) { _stream->iostream.set_max_buffer_size = o_stream_default_set_max_buffer_size; } if (_stream->cork == NULL) _stream->cork = o_stream_default_cork; if (_stream->flush == NULL) _stream->flush = o_stream_default_flush; if (_stream->set_flush_callback == NULL) { _stream->set_flush_callback = o_stream_default_set_flush_callback; } if (_stream->flush_pending == NULL) _stream->flush_pending = o_stream_default_set_flush_pending; if (_stream->get_used_size == NULL) _stream->get_used_size = o_stream_default_get_used_size; if (_stream->seek == NULL) _stream->seek = o_stream_default_seek; if (_stream->sendv == NULL) _stream->sendv = o_stream_default_sendv; if (_stream->write_at == NULL) _stream->write_at = o_stream_default_write_at; if (_stream->send_istream == NULL) _stream->send_istream = o_stream_default_send_istream; if (_stream->switch_ioloop == NULL) _stream->switch_ioloop = o_stream_default_switch_ioloop; io_stream_init(&_stream->iostream); return &_stream->ostream; } struct ostream *o_stream_create_error(int stream_errno) { struct ostream_private *stream; struct ostream *output; stream = i_new(struct ostream_private, 1); stream->ostream.blocking = TRUE; stream->ostream.closed = TRUE; stream->ostream.stream_errno = stream_errno; stream->ostream.last_failed_errno = stream_errno; output = o_stream_create(stream, NULL, -1); o_stream_set_no_error_handling(output, TRUE); o_stream_set_name(output, "(error)"); return output; } struct ostream * o_stream_create_error_str(int stream_errno, const char *fmt, ...) { struct ostream *output; va_list args; va_start(args, fmt); output = o_stream_create_error(stream_errno); io_stream_set_verror(&output->real_stream->iostream, fmt, args); va_end(args); return output; } struct ostream *o_stream_create_passthrough(struct ostream *output) { struct ostream_private *stream; stream = i_new(struct ostream_private, 1); return o_stream_create(stream, output, o_stream_get_fd(output)); } dovecot-2.2.33.2/src/lib/hex-dec.c0000644000175000017500000000147713123174404013353 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hex-dec.h" void dec2hex(unsigned char *hexstr, uintmax_t dec, unsigned int hexstr_size) { unsigned int i; for (i = 0; i < hexstr_size; i++) { unsigned int value = dec & 0x0f; if (value < 10) hexstr[hexstr_size-i-1] = value + '0'; else hexstr[hexstr_size-i-1] = value - 10 + 'A'; dec >>= 4; } } uintmax_t hex2dec(const unsigned char *data, unsigned int len) { unsigned int i; uintmax_t value = 0; for (i = 0; i < len; i++) { value = value*0x10; if (data[i] >= '0' && data[i] <= '9') value += data[i]-'0'; else if (data[i] >= 'A' && data[i] <= 'F') value += data[i]-'A' + 10; else if (data[i] >= 'a' && data[i] <= 'f') value += data[i]-'a' + 10; else return 0; } return value; } dovecot-2.2.33.2/src/lib/test-ioloop.c0000644000175000017500000001104613165463624014320 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "net.h" #include "time-util.h" #include "ioloop.h" #include "istream.h" #include struct test_ctx { bool got_left; bool got_right; bool got_to; }; static void timeout_callback(struct timeval *tv) { if (gettimeofday(tv, NULL) < 0) i_fatal("gettimeofday() failed: %m"); io_loop_stop(current_ioloop); } static void test_ioloop_fd_cb_left(struct test_ctx *ctx) { ctx->got_left = TRUE; if (ctx->got_left && ctx->got_right) io_loop_stop(current_ioloop); } static void test_ioloop_fd_cb_right(struct test_ctx *ctx) { ctx->got_right = TRUE; if (ctx->got_left && ctx->got_right) io_loop_stop(current_ioloop); } static void test_ioloop_fd_to(struct test_ctx *ctx) { ctx->got_to = TRUE; io_loop_stop(current_ioloop); } static void test_ioloop_fd(void) { test_begin("ioloop fd"); struct test_ctx test_ctx; int fds[2]; int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); test_assert(ret == 0); if (ret < 0) { i_error("socketpair() failed: %m"); test_end(); return; } i_zero(&test_ctx); struct ioloop *ioloop = io_loop_create(); struct io *io_left = io_add(fds[0], IO_READ, test_ioloop_fd_cb_left, &test_ctx); struct io *io_right = io_add(fds[1], IO_READ, test_ioloop_fd_cb_right, &test_ctx); struct timeout *to = timeout_add(2000, test_ioloop_fd_to, &test_ctx); if (write(fds[0], "ltr", 3) != 3 || write(fds[1], "rtl", 3) != 3) i_fatal("write() failed: %m"); io_loop_run(ioloop); timeout_remove(&to); io_remove(&io_left); io_remove(&io_right); test_assert(test_ctx.got_to == FALSE); test_assert(test_ctx.got_left == TRUE); test_assert(test_ctx.got_right == TRUE); io_loop_destroy(&ioloop); i_close_fd(&fds[0]); i_close_fd(&fds[1]); test_end(); } static void test_ioloop_timeout(void) { struct ioloop *ioloop, *ioloop2; struct timeout *to, *to2; struct timeval tv_start, tv_callback; test_begin("ioloop timeout"); ioloop = io_loop_create(); /* add a timeout by moving it from another ioloop */ ioloop2 = io_loop_create(); to2 = timeout_add(1000, timeout_callback, &tv_callback); io_loop_set_current(ioloop); to2 = io_loop_move_timeout(&to2); io_loop_set_current(ioloop2); io_loop_destroy(&ioloop2); sleep(1); /* add & remove immediately */ to = timeout_add(1000, timeout_callback, &tv_callback); timeout_remove(&to); /* add the timeout we're actually testing below */ to = timeout_add(1000, timeout_callback, &tv_callback); if (gettimeofday(&tv_start, NULL) < 0) i_fatal("gettimeofday() failed: %m"); io_loop_run(ioloop); test_assert(timeval_diff_msecs(&tv_callback, &tv_start) >= 500); timeout_remove(&to); timeout_remove(&to2); io_loop_destroy(&ioloop); test_end(); } static void io_callback(void *context ATTR_UNUSED) { } static void test_ioloop_find_fd_conditions(void) { struct { enum io_condition condition; int fd[2]; struct io *io; } tests[] = { { IO_ERROR, { -1, -1 }, NULL }, { IO_READ, { -1, -1 }, NULL }, { IO_WRITE, { -1, -1 }, NULL }, { IO_READ | IO_WRITE, { -1, -1 }, NULL }, { IO_READ, { -1, -1 }, NULL } /* read+write as separate ios */ }; struct ioloop *ioloop; struct io *io; unsigned int i; test_begin("ioloop find fd conditions"); ioloop = io_loop_create(); for (i = 0; i < N_ELEMENTS(tests); i++) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, tests[i].fd) < 0) i_fatal("socketpair() failed: %m"); tests[i].io = io_add(tests[i].fd[0], tests[i].condition, io_callback, (void *)NULL); } io = io_add(tests[i-1].fd[0], IO_WRITE, io_callback, (void *)NULL); tests[i-1].condition |= IO_WRITE; for (i = 0; i < N_ELEMENTS(tests); i++) test_assert_idx(io_loop_find_fd_conditions(ioloop, tests[i].fd[0]) == tests[i].condition, i); io_remove(&io); for (i = 0; i < N_ELEMENTS(tests); i++) { io_remove(&tests[i].io); i_close_fd(&tests[i].fd[0]); i_close_fd(&tests[i].fd[1]); } io_loop_destroy(&ioloop); test_end(); } static void io_callback_pending_io(void *context ATTR_UNUSED) { io_loop_stop(current_ioloop); } static void test_ioloop_pending_io(void) { test_begin("ioloop pending io"); struct istream *is = i_stream_create_from_data("data", 4); struct ioloop *ioloop = io_loop_create(); struct io *io = io_add_istream(is, io_callback_pending_io, NULL); io_loop_set_current(ioloop); io_set_pending(io); io_loop_run(ioloop); io_remove(&io); i_stream_unref(&is); io_loop_destroy(&ioloop); test_end(); } void test_ioloop(void) { test_ioloop_timeout(); test_ioloop_find_fd_conditions(); test_ioloop_pending_io(); test_ioloop_fd(); } dovecot-2.2.33.2/src/lib/bits.h0000644000175000017500000000717013165463624013013 00000000000000#ifndef BITS_H #define BITS_H #define UINT64_SUM_OVERFLOWS(a, b) \ (a > (uint64_t)-1 - b) #define BIT(n) (1u << (n)) size_t nearest_power(size_t num) ATTR_CONST; /* Returns TRUE if 2^x=num, i.e. if num has only a single bit set to 1. */ static inline bool ATTR_CONST bits_is_power_of_two(uint64_t num) { return num > 0 && (num & (num - 1)) == 0; } #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) static inline unsigned int ATTR_CONST bits_required32(uint32_t num) { return num == 0 ? 0 : 32 - __builtin_clz(num); } static inline unsigned int ATTR_CONST bits_required8(uint8_t num) { return bits_required32(num); } static inline unsigned int ATTR_CONST bits_required16(uint16_t num) { return bits_required32(num); } static inline unsigned int ATTR_CONST bits_required64(uint64_t num) { return num == 0 ? 0 : 64 - __builtin_clzll(num); } #else unsigned int bits_required8(uint8_t num) ATTR_CONST; static inline unsigned int bits_required16(uint16_t num) { return (num <= 0xff) ? bits_required8(num) : 8 + bits_required8(num >> 8); } static inline unsigned int bits_required32(uint32_t num) { return (num <= 0xffff) ? bits_required16(num) : 16 + bits_required16(num >> 16); } static inline unsigned int bits_required64(uint64_t num) { return (num <= 0xffffffff) ? bits_required32(num) : 32 + bits_required32(num >> 32); } #endif /* These functions look too big to be inline, but in almost all expected uses, 'fracbits' will be a compile-time constant, and most of the expressions will simplify greatly. */ /* Perform a piecewise-linear approximation to a log2, with fracbits "fractional" bits. Best explained with examples: With 2 fractional bits splitting each power of 2 into 4 bands: 00, 01, 10, 11 -> 00, 01, 10, 11 (small corner cases) 100, 101, 110, 111 -> 100, 101, 110, 111 ([4-8) split into 4 bands) 1000, 1001, 1010, 1011 -> 1000, 1000, 1001, 1001 ([8-15) split ... 1100, 1101, 1110, 1111 -> 1010, 1010, 1011, 1011 ... into 4 bands) [16..31) -> 11bb [32..63) -> 100bb [64..127) -> 101bb [128..255) -> 110bb e.g. 236 = 11101100 -> ((8-2)<<2 == 11000) + (111.....>> 5 == 111) - 100 == 11011 */ static inline unsigned int ATTR_CONST bits_fraclog(unsigned int val, unsigned int fracbits) { unsigned bits = bits_required32(val); if (bits <= fracbits + 1) return val; unsigned int bandnum = bits - fracbits; unsigned int bandstart = bandnum << fracbits; unsigned int fracoffsbad = val >> (bandnum - 1); /* has leading 1 still */ unsigned int bucket = bandstart + fracoffsbad - BIT(fracbits); return bucket; } static inline unsigned int ATTR_CONST bits_fraclog_bucket_start(unsigned int bucket, unsigned int fracbits) { unsigned int bandnum = bucket >> fracbits; if (bandnum <= 1) return bucket; if (fracbits == 0) return BIT(bucket - 1); unsigned int fracoffs = bucket & (BIT(fracbits)-1); unsigned int fracoffs1 = BIT(fracbits) + fracoffs; unsigned int bandstart = fracoffs1 << (bandnum - 1); return bandstart; } static inline unsigned int ATTR_CONST bits_fraclog_bucket_end(unsigned int bucket, unsigned int fracbits) { unsigned int bandnum = bucket >> fracbits; if (bandnum <= 1) return bucket; if (fracbits == 0) return BIT(bucket - 1) * 2 - 1; unsigned int fracoffs = bucket & (BIT(fracbits)-1); unsigned int nextfracoffs1 = 1 + BIT(fracbits) + fracoffs; unsigned int nextbandstart = nextfracoffs1 << (bandnum - 1); return nextbandstart - 1; } /* UNSAFE: multiple use of parameter (but expecting a constant in reality). But a macro as it's most likely to be used to declare an array size. */ #define BITS_FRACLOG_BUCKETS(bits) ((33u - (bits)) << (bits)) #endif dovecot-2.2.33.2/src/lib/safe-memset.h0000644000175000017500000000036013123174404014237 00000000000000#ifndef SAFE_MEMSET_H #define SAFE_MEMSET_H /* memset() guaranteed not to get optimized away by compiler. Should be used instead of memset() when clearing any sensitive data. */ void safe_memset(void *data, int c, size_t size); #endif dovecot-2.2.33.2/src/lib/time-util.h0000644000175000017500000000271513123174404013750 00000000000000#ifndef TIME_UTIL_H #define TIME_UTIL_H #include /* for struct timeval */ /* Returns -1 if tv1tv2, 0 if they're equal. */ int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2); /* Same as timeval_cmp, but tv->usecs must differ by at least usec_margin */ int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2, unsigned int usec_margin); /* Returns tv1-tv2 in milliseconds. */ int timeval_diff_msecs(const struct timeval *tv1, const struct timeval *tv2); /* Returns tv1-tv2 in microseconds. */ long long timeval_diff_usecs(const struct timeval *tv1, const struct timeval *tv2); static inline void timeval_add_msecs(struct timeval *tv, unsigned int msecs) { tv->tv_sec += msecs / 1000; tv->tv_usec += (msecs % 1000) * 1000; if (tv->tv_usec >= 1000000) { tv->tv_sec++; tv->tv_usec -= 1000000; } } static inline void timeval_sub_msecs(struct timeval *tv, unsigned int msecs) { tv->tv_sec -= msecs / 1000; tv->tv_usec -= (msecs % 1000) * 1000; if (tv->tv_usec < 0) { tv->tv_sec--; tv->tv_usec += 1000000; } } /* Convert t to local time and return timestamp on that day at 00:00:00. */ time_t time_to_local_day_start(time_t t); /* Wrappers to strftime() */ const char *t_strftime(const char *fmt, const struct tm *tm) ATTR_STRFTIME(1); const char *t_strflocaltime(const char *fmt, time_t t) ATTR_STRFTIME(1); const char *t_strfgmtime(const char *fmt, time_t t) ATTR_STRFTIME(1); #endif dovecot-2.2.33.2/src/lib/file-lock.c0000644000175000017500000003132413165463624013710 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "file-lock.h" #include "time-util.h" #include #include #ifdef HAVE_FLOCK # include #endif struct file_lock { int fd; char *path; struct timeval locked_time; int lock_type; enum file_lock_method lock_method; bool unlink_on_free; bool close_on_free; }; static struct timeval lock_wait_start; static uint64_t file_lock_wait_usecs = 0; static long long file_lock_slow_warning_usecs = -1; static void file_lock_log_warning_if_slow(struct file_lock *lock); bool file_lock_method_parse(const char *name, enum file_lock_method *method_r) { if (strcasecmp(name, "fcntl") == 0) *method_r = FILE_LOCK_METHOD_FCNTL; else if (strcasecmp(name, "flock") == 0) *method_r = FILE_LOCK_METHOD_FLOCK; else if (strcasecmp(name, "dotlock") == 0) *method_r = FILE_LOCK_METHOD_DOTLOCK; else return FALSE; return TRUE; } const char *file_lock_method_to_str(enum file_lock_method method) { switch (method) { case FILE_LOCK_METHOD_FCNTL: return "fcntl"; case FILE_LOCK_METHOD_FLOCK: return "flock"; case FILE_LOCK_METHOD_DOTLOCK: return "dotlock"; } i_unreached(); } int file_try_lock(int fd, const char *path, int lock_type, enum file_lock_method lock_method, struct file_lock **lock_r) { return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r); } int file_try_lock_error(int fd, const char *path, int lock_type, enum file_lock_method lock_method, struct file_lock **lock_r, const char **error_r) { return file_wait_lock_error(fd, path, lock_type, lock_method, 0, lock_r, error_r); } static const char * file_lock_find_fcntl(int lock_fd, int lock_type) { struct flock fl; i_zero(&fl); fl.l_type = lock_type; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; if (fcntl(lock_fd, F_GETLK, &fl) < 0 || fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0) return ""; return t_strdup_printf(" (%s lock held by pid %ld)", fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid); } static const char * file_lock_find_proc_locks(int lock_fd ATTR_UNUSED) { /* do anything except Linux support this? don't bother trying it for OSes we don't know about. */ #ifdef __linux__ static bool have_proc_locks = TRUE; struct stat st; char node_buf[MAX_INT_STRLEN*3 + 2 + 1]; struct istream *input; const char *line, *lock_type = ""; pid_t pid = 0; int fd; if (!have_proc_locks) return FALSE; if (fstat(lock_fd, &st) < 0) return ""; i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu", major(st.st_dev), minor(st.st_dev), (unsigned long long)st.st_ino); fd = open("/proc/locks", O_RDONLY); if (fd == -1) { have_proc_locks = FALSE; return ""; } input = i_stream_create_fd_autoclose(&fd, 512); while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN { const char *const *args = t_strsplit_spaces(line, " "); /* number: FLOCK/POSIX ADVISORY READ/WRITE pid major:minor:inode region-start region-end */ if (str_array_length(args) < 8) { ; /* don't continue from within a T_BEGIN {...} T_END */ } else if (strcmp(args[5], node_buf) == 0) { lock_type = strcmp(args[3], "READ") == 0 ? "READ" : "WRITE"; if (str_to_pid(args[4], &pid) < 0) pid = 0; } } T_END; i_stream_destroy(&input); if (pid == 0) { /* not found */ return ""; } if (pid == getpid()) return " (BUG: lock is held by our own process)"; return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid); #else return ""; #endif } const char *file_lock_find(int lock_fd, enum file_lock_method lock_method, int lock_type) { const char *ret; if (lock_method == FILE_LOCK_METHOD_FCNTL) { ret = file_lock_find_fcntl(lock_fd, lock_type); if (ret[0] != '\0') return ret; } return file_lock_find_proc_locks(lock_fd); } static bool err_is_lock_timeout(time_t started, unsigned int timeout_secs) { /* if EINTR took at least timeout_secs-1 number of seconds, assume it was the alarm. otherwise log EINTR failure. (We most likely don't want to retry EINTR since a signal means somebody wants us to stop blocking). */ return errno == EINTR && (unsigned long)(time(NULL) - started + 1) >= timeout_secs; } static int file_lock_do(int fd, const char *path, int lock_type, enum file_lock_method lock_method, unsigned int timeout_secs, const char **error_r) { const char *lock_type_str; time_t started = time(NULL); int ret; i_assert(fd != -1); if (timeout_secs != 0) { alarm(timeout_secs); file_lock_wait_start(); } lock_type_str = lock_type == F_UNLCK ? "unlock" : (lock_type == F_RDLCK ? "read-lock" : "write-lock"); switch (lock_method) { case FILE_LOCK_METHOD_FCNTL: { #ifndef HAVE_FCNTL *error_r = t_strdup_printf( "Can't lock file %s: fcntl() locks not supported", path); return -1; #else struct flock fl; fl.l_type = lock_type; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; ret = fcntl(fd, timeout_secs ? F_SETLKW : F_SETLK, &fl); if (timeout_secs != 0) { alarm(0); file_lock_wait_end(path); } if (ret == 0) break; if (timeout_secs == 0 && (errno == EACCES || errno == EAGAIN)) { /* locked by another process */ *error_r = t_strdup_printf( "fcntl(%s, %s, F_SETLK) locking failed: %m " "(File is already locked)", path, lock_type_str); return 0; } if (err_is_lock_timeout(started, timeout_secs)) { errno = EAGAIN; *error_r = t_strdup_printf( "fcntl(%s, %s, F_SETLKW) locking failed: " "Timed out after %u seconds%s", path, lock_type_str, timeout_secs, file_lock_find(fd, lock_method, lock_type)); return 0; } *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m", path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW"); if (errno == EDEADLK) i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type)); return -1; #endif } case FILE_LOCK_METHOD_FLOCK: { #ifndef HAVE_FLOCK *error_r = t_strdup_printf( "Can't lock file %s: flock() not supported", path); return -1; #else int operation = timeout_secs != 0 ? 0 : LOCK_NB; switch (lock_type) { case F_RDLCK: operation |= LOCK_SH; break; case F_WRLCK: operation |= LOCK_EX; break; case F_UNLCK: operation |= LOCK_UN; break; } ret = flock(fd, operation); if (timeout_secs != 0) { alarm(0); file_lock_wait_end(path); } if (ret == 0) break; if (timeout_secs == 0 && errno == EWOULDBLOCK) { /* locked by another process */ *error_r = t_strdup_printf( "flock(%s, %s) failed: %m " "(File is already locked)", path, lock_type_str); return 0; } if (err_is_lock_timeout(started, timeout_secs)) { errno = EAGAIN; *error_r = t_strdup_printf("flock(%s, %s) failed: " "Timed out after %u seconds%s", path, lock_type_str, timeout_secs, file_lock_find(fd, lock_method, lock_type)); return 0; } *error_r = t_strdup_printf("flock(%s, %s) failed: %m", path, lock_type_str); if (errno == EDEADLK) i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type)); return -1; #endif } case FILE_LOCK_METHOD_DOTLOCK: /* we shouldn't get here */ i_unreached(); } return 1; } int file_wait_lock(int fd, const char *path, int lock_type, enum file_lock_method lock_method, unsigned int timeout_secs, struct file_lock **lock_r) { const char *error; int ret; ret = file_wait_lock_error(fd, path, lock_type, lock_method, timeout_secs, lock_r, &error); if (ret < 0) i_error("%s", error); return ret; } int file_wait_lock_error(int fd, const char *path, int lock_type, enum file_lock_method lock_method, unsigned int timeout_secs, struct file_lock **lock_r, const char **error_r) { struct file_lock *lock; int ret; ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r); if (ret <= 0) return ret; lock = i_new(struct file_lock, 1); lock->fd = fd; lock->path = i_strdup(path); lock->lock_type = lock_type; lock->lock_method = lock_method; if (gettimeofday(&lock->locked_time, NULL) < 0) i_fatal("gettimeofday() failed: %m"); *lock_r = lock; return 1; } int file_lock_try_update(struct file_lock *lock, int lock_type) { const char *error; int ret; ret = file_lock_do(lock->fd, lock->path, lock_type, lock->lock_method, 0, &error); if (ret <= 0) return ret; file_lock_log_warning_if_slow(lock); lock->lock_type = lock_type; return 1; } void file_lock_set_unlink_on_free(struct file_lock *lock, bool set) { lock->unlink_on_free = set; } void file_lock_set_close_on_free(struct file_lock *lock, bool set) { lock->close_on_free = set; } static void file_unlock_real(struct file_lock *lock) { const char *error; if (file_lock_do(lock->fd, lock->path, F_UNLCK, lock->lock_method, 0, &error) == 0) { /* this shouldn't happen */ i_error("file_unlock(%s) failed: %m", lock->path); } } void file_unlock(struct file_lock **_lock) { struct file_lock *lock = *_lock; *_lock = NULL; /* unlocking is unnecessary when the file is unlinked. or alternatively the unlink() must be done before unlocking, because otherwise it could be deleting the new lock. */ i_assert(!lock->unlink_on_free); file_unlock_real(lock); file_lock_free(&lock); } static void file_try_unlink_locked(struct file_lock *lock) { struct file_lock *temp_lock = NULL; struct stat st1, st2; const char *error; int ret; file_unlock_real(lock); ret = file_try_lock_error(lock->fd, lock->path, F_WRLCK, lock->lock_method, &temp_lock, &error); if (ret < 0) { i_error("file_lock_free(): Unexpectedly failed to retry locking %s: %s", lock->path, error); } else if (ret == 0) { /* already locked by someone else */ } else if (fstat(lock->fd, &st1) < 0) { /* not expected to happen */ i_error("file_lock_free(): fstat(%s) failed: %m", lock->path); } else if (stat(lock->path, &st2) < 0) { if (errno != ENOENT) i_error("file_lock_free(): stat(%s) failed: %m", lock->path); } else if (st1.st_ino != st2.st_ino || !CMP_DEV_T(st1.st_dev, st2.st_dev)) { /* lock file was recreated already - don't delete it */ } else { /* nobody was waiting on the lock - unlink it */ i_unlink(lock->path); } if (temp_lock != NULL) file_lock_free(&temp_lock); } void file_lock_free(struct file_lock **_lock) { struct file_lock *lock = *_lock; *_lock = NULL; if (lock->unlink_on_free) file_try_unlink_locked(lock); if (lock->close_on_free) i_close_fd(&lock->fd); file_lock_log_warning_if_slow(lock); i_free(lock->path); i_free(lock); } const char *file_lock_get_path(struct file_lock *lock) { return lock->path; } void file_lock_set_path(struct file_lock *lock, const char *path) { if (path != lock->path) { i_free(lock->path); lock->path = i_strdup(path); } } void file_lock_wait_start(void) { i_assert(lock_wait_start.tv_sec == 0); if (gettimeofday(&lock_wait_start, NULL) < 0) i_fatal("gettimeofday() failed: %m"); } static void file_lock_wait_init_warning(void) { const char *value; i_assert(file_lock_slow_warning_usecs == -1); value = getenv("FILE_LOCK_SLOW_WARNING_MSECS"); if (value == NULL) file_lock_slow_warning_usecs = LLONG_MAX; else if (str_to_llong(value, &file_lock_slow_warning_usecs) == 0 && file_lock_slow_warning_usecs > 0) { file_lock_slow_warning_usecs *= 1000; } else { i_error("FILE_LOCK_SLOW_WARNING_MSECS: " "Invalid value '%s' - ignoring", value); file_lock_slow_warning_usecs = LLONG_MAX; } } static void file_lock_log_warning_if_slow(struct file_lock *lock) { if (file_lock_slow_warning_usecs < 0) file_lock_wait_init_warning(); if (file_lock_slow_warning_usecs == LLONG_MAX) { /* slowness checking is disabled */ return; } if (lock->lock_type != F_WRLCK) { /* some shared locks can legitimately be kept for a long time. don't warn about them. */ return; } struct timeval now; if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); int diff = timeval_diff_msecs(&now, &lock->locked_time); if (diff > file_lock_slow_warning_usecs/1000) { i_warning("Lock %s kept for %d.%03d secs", lock->path, diff / 1000, diff % 1000); } } void file_lock_wait_end(const char *lock_name) { struct timeval now; i_assert(lock_wait_start.tv_sec != 0); if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); long long diff = timeval_diff_usecs(&now, &lock_wait_start); if (diff > file_lock_slow_warning_usecs) { if (file_lock_slow_warning_usecs < 0) file_lock_wait_init_warning(); if (diff > file_lock_slow_warning_usecs) { int diff_msecs = (diff + 999) / 1000; i_warning("Locking %s took %d.%03d secs", lock_name, diff_msecs / 1000, diff_msecs % 1000); } } file_lock_wait_usecs += diff; lock_wait_start.tv_sec = 0; } uint64_t file_lock_wait_get_total_usecs(void) { return file_lock_wait_usecs; } dovecot-2.2.33.2/src/lib/istream-chain.c0000644000175000017500000002123713165463624014571 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "istream-private.h" #include "istream-chain.h" struct chain_istream; struct istream_chain_link { struct istream_chain_link *prev, *next; struct istream *stream; bool eof; }; struct istream_chain { struct istream_chain_link *head, *tail; struct chain_istream *stream; }; struct chain_istream { struct istream_private istream; /* how much of the previous link's stream still exists at the beginning of our buffer. skipping through this should point to the beginning of the current link's stream. */ size_t prev_stream_left; size_t prev_skip; bool have_explicit_max_buffer_size; struct istream_chain chain; }; static void ATTR_NULL(2) i_stream_chain_append_internal(struct istream_chain *chain, struct istream *stream) { struct istream_chain_link *link; if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL) return; link = i_new(struct istream_chain_link, 1); link->stream = stream; link->eof = stream == NULL; if (stream != NULL) i_stream_ref(stream); if (chain->head == NULL && stream != NULL) { struct chain_istream *cstream = (struct chain_istream *)chain->stream; if (cstream->have_explicit_max_buffer_size) { i_stream_set_max_buffer_size(stream, chain->stream->istream.max_buffer_size); } else { size_t max_size = i_stream_get_max_buffer_size(stream); if (cstream->istream.max_buffer_size < max_size) cstream->istream.max_buffer_size = max_size; } } DLLIST2_APPEND(&chain->head, &chain->tail, link); /* if io_add_istream() has been added to this chain stream, notify the callback that we have more data available. */ if (stream != NULL) i_stream_set_input_pending(stream, TRUE); } void i_stream_chain_append(struct istream_chain *chain, struct istream *stream) { i_stream_chain_append_internal(chain, stream); } void i_stream_chain_append_eof(struct istream_chain *chain) { i_stream_chain_append_internal(chain, NULL); } static void i_stream_chain_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct chain_istream *cstream = (struct chain_istream *)stream; struct istream_chain_link *link = cstream->chain.head; cstream->have_explicit_max_buffer_size = TRUE; cstream->istream.max_buffer_size = max_size; while (link != NULL) { if (link->stream != NULL) i_stream_set_max_buffer_size(link->stream, max_size); link = link->next; } } static void i_stream_chain_destroy(struct iostream_private *stream) { struct chain_istream *cstream = (struct chain_istream *)stream; struct istream_chain_link *link = cstream->chain.head; while (link != NULL) { struct istream_chain_link *next = link->next; if (link->stream != NULL) i_stream_unref(&link->stream); i_free(link); link = next; } i_free(cstream->istream.w_buffer); } static void i_stream_chain_read_next(struct chain_istream *cstream) { struct istream_chain_link *link = cstream->chain.head; struct istream *prev_input; const unsigned char *data; size_t data_size, cur_data_pos; i_assert(link != NULL && link->stream != NULL); i_assert(link->stream->eof); prev_input = link->stream; data = i_stream_get_data(prev_input, &data_size); DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link); i_free(link); /* a) we have more streams, b) we have EOF, c) we need to wait for more streams */ link = cstream->chain.head; if (link != NULL && link->stream != NULL) i_stream_seek(link->stream, 0); if (cstream->prev_stream_left > 0) { /* we've already buffered some of the prev_input. continue appending the rest to it. if it's already at EOF, there's nothing more to append. */ cur_data_pos = cstream->istream.pos - (cstream->istream.skip + cstream->prev_stream_left); i_assert(cur_data_pos <= data_size); data += cur_data_pos; data_size -= cur_data_pos; /* the stream has now become "previous", so its contents in buffer are now part of prev_stream_left. */ cstream->prev_stream_left += cur_data_pos; } else { cstream->istream.pos = 0; cstream->istream.skip = 0; cstream->prev_stream_left = 0; } if (data_size > 0) { memcpy(i_stream_alloc(&cstream->istream, data_size), data, data_size); cstream->istream.pos += data_size; cstream->prev_stream_left += data_size; } i_stream_skip(prev_input, i_stream_get_data_size(prev_input)); i_stream_unref(&prev_input); } static bool i_stream_chain_skip(struct chain_istream *cstream) { struct istream_private *stream = &cstream->istream; struct istream_chain_link *link = cstream->chain.head; size_t bytes_skipped; i_assert(stream->skip >= cstream->prev_skip); bytes_skipped = stream->skip - cstream->prev_skip; if (cstream->prev_stream_left == 0) { /* no need to worry about buffers, skip everything */ } else if (bytes_skipped < cstream->prev_stream_left) { /* we're still skipping inside buffer */ cstream->prev_stream_left -= bytes_skipped; bytes_skipped = 0; } else { /* done with the buffer */ bytes_skipped -= cstream->prev_stream_left; cstream->prev_stream_left = 0; } stream->pos -= bytes_skipped; stream->skip -= bytes_skipped; stream->buffer += bytes_skipped; cstream->prev_skip = stream->skip; if (link == NULL || link->eof) { i_assert(bytes_skipped == 0); return FALSE; } i_stream_skip(link->stream, bytes_skipped); return TRUE; } static ssize_t i_stream_chain_read(struct istream_private *stream) { struct chain_istream *cstream = (struct chain_istream *)stream; struct istream_chain_link *link = cstream->chain.head; const unsigned char *data; size_t data_size, cur_data_pos, new_pos; size_t new_bytes_count; ssize_t ret; if (link != NULL && link->eof) { stream->istream.eof = TRUE; return -1; } if (!i_stream_chain_skip(cstream)) return 0; i_assert(link != NULL); i_assert(stream->pos >= stream->skip + cstream->prev_stream_left); cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left); data = i_stream_get_data(link->stream, &data_size); if (data_size > cur_data_pos) ret = 0; else { /* need to read more */ i_assert(cur_data_pos == data_size); ret = i_stream_read(link->stream); if (ret == -2 || ret == 0) return ret; if (ret == -1) { if (link->stream->stream_errno != 0) { io_stream_set_error(&stream->iostream, "read(%s) failed: %s", i_stream_get_name(link->stream), i_stream_get_error(link->stream)); stream->istream.stream_errno = link->stream->stream_errno; return -1; } /* EOF of this stream, go to next stream */ i_stream_chain_read_next(cstream); cstream->prev_skip = stream->skip; return i_stream_chain_read(stream); } /* we read something */ data = i_stream_get_data(link->stream, &data_size); } if (cstream->prev_stream_left == 0) { /* we can point directly to the current stream's buffers */ stream->buffer = data; stream->pos -= stream->skip; stream->skip = 0; new_pos = data_size; } else if (data_size == cur_data_pos) { /* nothing new read */ i_assert(ret == 0 || ret == -1); stream->buffer = stream->w_buffer; new_pos = stream->pos; } else { /* we still have some of the previous stream left. merge the new data with it. */ i_assert(data_size > cur_data_pos); new_bytes_count = data_size - cur_data_pos; memcpy(i_stream_alloc(stream, new_bytes_count), data + cur_data_pos, new_bytes_count); stream->buffer = stream->w_buffer; new_pos = stream->pos + new_bytes_count; } ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = new_pos; cstream->prev_skip = stream->skip; return ret; } static void i_stream_chain_close(struct iostream_private *stream, bool close_parent) { struct chain_istream *cstream = (struct chain_istream *)stream; /* seek to the correct position in parent stream in case it didn't end with EOF */ (void)i_stream_chain_skip(cstream); if (close_parent) { struct istream_chain_link *link = cstream->chain.head; while (link != NULL) { i_stream_close(link->stream); link = link->next; } } } struct istream *i_stream_create_chain(struct istream_chain **chain_r) { struct chain_istream *cstream; cstream = i_new(struct chain_istream, 1); cstream->chain.stream = cstream; cstream->istream.iostream.close = i_stream_chain_close; cstream->istream.iostream.destroy = i_stream_chain_destroy; cstream->istream.iostream.set_max_buffer_size = i_stream_chain_set_max_buffer_size; cstream->istream.read = i_stream_chain_read; cstream->istream.istream.readable_fd = FALSE; cstream->istream.istream.blocking = FALSE; cstream->istream.istream.seekable = FALSE; *chain_r = &cstream->chain; return i_stream_create(&cstream->istream, NULL, -1); } dovecot-2.2.33.2/src/lib/hash.c0000644000175000017500000002661313165463624012773 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "hash.h" #include "primes.h" #include #define HASH_TABLE_MIN_SIZE 67 #undef hash_table_create #undef hash_table_create_direct #undef hash_table_destroy #undef hash_table_clear #undef hash_table_lookup #undef hash_table_lookup_full #undef hash_table_insert #undef hash_table_update #undef hash_table_try_remove #undef hash_table_count #undef hash_table_iterate_init #undef hash_table_iterate #undef hash_table_freeze #undef hash_table_thaw #undef hash_table_copy struct hash_node { struct hash_node *next; void *key; void *value; }; struct hash_table { pool_t node_pool; int frozen; unsigned int initial_size, nodes_count, removed_count; unsigned int size; struct hash_node *nodes; struct hash_node *free_nodes; hash_callback_t *hash_cb; hash_cmp_callback_t *key_compare_cb; }; struct hash_iterate_context { struct hash_table *table; struct hash_node *next; unsigned int pos; }; static bool hash_table_resize(struct hash_table *table, bool grow); void hash_table_create(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size, hash_callback_t *hash_cb, hash_cmp_callback_t *key_compare_cb) { struct hash_table *table; pool_ref(node_pool); table = i_new(struct hash_table, 1); table->node_pool = node_pool; table->initial_size = I_MAX(primes_closest(initial_size), HASH_TABLE_MIN_SIZE); table->hash_cb = hash_cb; table->key_compare_cb = key_compare_cb; table->size = table->initial_size; table->nodes = i_new(struct hash_node, table->size); *table_r = table; } static unsigned int direct_hash(const void *p) { /* NOTE: may truncate the value, but that doesn't matter. */ return POINTER_CAST_TO(p, unsigned int); } static int direct_cmp(const void *p1, const void *p2) { return p1 == p2 ? 0 : 1; } void hash_table_create_direct(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size) { hash_table_create(table_r, node_pool, initial_size, direct_hash, direct_cmp); } static void free_node(struct hash_table *table, struct hash_node *node) { if (!table->node_pool->alloconly_pool) p_free(table->node_pool, node); else { node->next = table->free_nodes; table->free_nodes = node; } } static void destroy_node_list(struct hash_table *table, struct hash_node *node) { struct hash_node *next; while (node != NULL) { next = node->next; p_free(table->node_pool, node); node = next; } } static void hash_table_destroy_nodes(struct hash_table *table) { unsigned int i; for (i = 0; i < table->size; i++) { if (table->nodes[i].next != NULL) destroy_node_list(table, table->nodes[i].next); } } void hash_table_destroy(struct hash_table **_table) { struct hash_table *table = *_table; *_table = NULL; i_assert(table->frozen == 0); if (!table->node_pool->alloconly_pool) { hash_table_destroy_nodes(table); destroy_node_list(table, table->free_nodes); } pool_unref(&table->node_pool); i_free(table->nodes); i_free(table); } void hash_table_clear(struct hash_table *table, bool free_nodes) { i_assert(table->frozen == 0); if (!table->node_pool->alloconly_pool) hash_table_destroy_nodes(table); if (free_nodes) { if (!table->node_pool->alloconly_pool) destroy_node_list(table, table->free_nodes); table->free_nodes = NULL; } memset(table->nodes, 0, sizeof(struct hash_node) * table->size); table->nodes_count = 0; table->removed_count = 0; } static struct hash_node * hash_table_lookup_node(const struct hash_table *table, const void *key, unsigned int hash) { struct hash_node *node; node = &table->nodes[hash % table->size]; do { if (node->key != NULL) { if (table->key_compare_cb(node->key, key) == 0) return node; } node = node->next; } while (node != NULL); return NULL; } void *hash_table_lookup(const struct hash_table *table, const void *key) { struct hash_node *node; node = hash_table_lookup_node(table, key, table->hash_cb(key)); return node != NULL ? node->value : NULL; } bool hash_table_lookup_full(const struct hash_table *table, const void *lookup_key, void **orig_key, void **value) { struct hash_node *node; node = hash_table_lookup_node(table, lookup_key, table->hash_cb(lookup_key)); if (node == NULL) return FALSE; *orig_key = node->key; *value = node->value; return TRUE; } static struct hash_node * ATTR_NOWARN_UNUSED_RESULT hash_table_insert_node(struct hash_table *table, void *key, void *value, bool check_existing) { struct hash_node *node, *prev; unsigned int hash; i_assert(key != NULL); hash = table->hash_cb(key); if (check_existing && table->removed_count > 0) { /* there may be holes, have to check everything */ node = hash_table_lookup_node(table, key, hash); if (node != NULL) { node->value = value; return node; } check_existing = FALSE; } /* a) primary node */ node = &table->nodes[hash % table->size]; if (node->key == NULL) { table->nodes_count++; node->key = key; node->value = value; return node; } if (check_existing) { if (table->key_compare_cb(node->key, key) == 0) { node->value = value; return node; } } /* b) collisions list */ prev = node; node = node->next; while (node != NULL) { if (node->key == NULL) break; if (check_existing) { if (table->key_compare_cb(node->key, key) == 0) { node->value = value; return node; } } prev = node; node = node->next; } if (node == NULL) { if (table->frozen == 0 && hash_table_resize(table, TRUE)) { /* resized table, try again */ return hash_table_insert_node(table, key, value, FALSE); } if (table->free_nodes == NULL) node = p_new(table->node_pool, struct hash_node, 1); else { node = table->free_nodes; table->free_nodes = node->next; node->next = NULL; } prev->next = node; } node->key = key; node->value = value; table->nodes_count++; return node; } void hash_table_insert(struct hash_table *table, void *key, void *value) { struct hash_node *node; node = hash_table_insert_node(table, key, value, TRUE); node->key = key; } void hash_table_update(struct hash_table *table, void *key, void *value) { hash_table_insert_node(table, key, value, TRUE); } static void hash_table_compress(struct hash_table *table, struct hash_node *root) { struct hash_node *node, *next; i_assert(table->frozen == 0); /* remove deleted nodes from the list */ for (node = root; node->next != NULL; ) { next = node->next; if (next->key == NULL) { node->next = next->next; free_node(table, next); } else { node = next; } } /* update root */ if (root->key == NULL && root->next != NULL) { next = root->next; *root = *next; free_node(table, next); } } static void hash_table_compress_removed(struct hash_table *table) { unsigned int i; for (i = 0; i < table->size; i++) hash_table_compress(table, &table->nodes[i]); table->removed_count = 0; } bool hash_table_try_remove(struct hash_table *table, const void *key) { struct hash_node *node; unsigned int hash; hash = table->hash_cb(key); node = hash_table_lookup_node(table, key, hash); if (unlikely(node == NULL)) return FALSE; node->key = NULL; table->nodes_count--; if (table->frozen != 0) table->removed_count++; else if (!hash_table_resize(table, FALSE)) hash_table_compress(table, &table->nodes[hash % table->size]); return TRUE; } unsigned int hash_table_count(const struct hash_table *table) { return table->nodes_count; } struct hash_iterate_context *hash_table_iterate_init(struct hash_table *table) { struct hash_iterate_context *ctx; hash_table_freeze(table); ctx = i_new(struct hash_iterate_context, 1); ctx->table = table; ctx->next = &table->nodes[0]; return ctx; } static struct hash_node * hash_table_iterate_next(struct hash_iterate_context *ctx, struct hash_node *node) { do { node = node->next; if (node == NULL) { if (++ctx->pos == ctx->table->size) { ctx->pos--; return NULL; } node = &ctx->table->nodes[ctx->pos]; } } while (node->key == NULL); return node; } bool hash_table_iterate(struct hash_iterate_context *ctx, void **key_r, void **value_r) { struct hash_node *node; node = ctx->next; if (node != NULL && node->key == NULL) node = hash_table_iterate_next(ctx, node); if (node == NULL) { *key_r = *value_r = NULL; return FALSE; } *key_r = node->key; *value_r = node->value; ctx->next = hash_table_iterate_next(ctx, node); return TRUE; } void hash_table_iterate_deinit(struct hash_iterate_context **_ctx) { struct hash_iterate_context *ctx = *_ctx; *_ctx = NULL; hash_table_thaw(ctx->table); i_free(ctx); } void hash_table_freeze(struct hash_table *table) { table->frozen++; } void hash_table_thaw(struct hash_table *table) { i_assert(table->frozen > 0); if (--table->frozen > 0) return; if (table->removed_count > 0) { if (!hash_table_resize(table, FALSE)) hash_table_compress_removed(table); } } static bool hash_table_resize(struct hash_table *table, bool grow) { struct hash_node *old_nodes, *node, *next; unsigned int next_size, old_size, i; float nodes_per_list; i_assert(table->frozen == 0); nodes_per_list = (float) table->nodes_count / (float) table->size; if (nodes_per_list > 0.3 && nodes_per_list < 2.0) return FALSE; next_size = I_MAX(primes_closest(table->nodes_count+1), table->initial_size); if (next_size == table->size) return FALSE; if (grow && table->size >= next_size) return FALSE; /* recreate primary table */ old_size = table->size; old_nodes = table->nodes; table->size = I_MAX(next_size, HASH_TABLE_MIN_SIZE); table->nodes = i_new(struct hash_node, table->size); table->nodes_count = 0; table->removed_count = 0; table->frozen++; /* move the data */ for (i = 0; i < old_size; i++) { node = &old_nodes[i]; if (node->key != NULL) { hash_table_insert_node(table, node->key, node->value, FALSE); } for (node = node->next; node != NULL; node = next) { next = node->next; if (node->key != NULL) { hash_table_insert_node(table, node->key, node->value, FALSE); } free_node(table, node); } } table->frozen--; i_free(old_nodes); return TRUE; } void hash_table_copy(struct hash_table *dest, struct hash_table *src) { struct hash_iterate_context *iter; void *key, *value; hash_table_freeze(dest); iter = hash_table_iterate_init(src); while (hash_table_iterate(iter, &key, &value)) hash_table_insert(dest, key, value); hash_table_iterate_deinit(&iter); hash_table_thaw(dest); } /* a char* hash function from ASU -- from glib */ unsigned int str_hash(const char *p) { const unsigned char *s = (const unsigned char *)p; unsigned int g, h = 0; while (*s != '\0') { h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h; } /* a char* hash function from ASU -- from glib */ unsigned int strcase_hash(const char *p) { const unsigned char *s = (const unsigned char *)p; unsigned int g, h = 0; while (*s != '\0') { h = (h << 4) + i_toupper(*s); if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h; } unsigned int mem_hash(const void *p, unsigned int size) { const unsigned char *s = p; unsigned int i, g, h = 0; for (i = 0; i < size; i++) { h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h; } dovecot-2.2.33.2/src/lib/sha2.h0000644000175000017500000000535413123174404012676 00000000000000/* * FIPS 180-2 SHA-224/256/384/512 implementation * Last update: 02/02/2007 * Issue date: 04/30/2005 * * Copyright (C) 2005, 2007 Olivier Gay * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef SHA2_H #define SHA2_H #include "hash-method.h" #include "sha-common.h" struct sha256_ctx { size_t tot_len; size_t len; unsigned char block[2 * SHA256_BLOCK_SIZE]; uint32_t h[8]; }; struct sha512_ctx { size_t tot_len; size_t len; unsigned char block[2 * SHA512_BLOCK_SIZE]; uint64_t h[8]; }; void sha256_init(struct sha256_ctx *ctx); void sha256_loop(struct sha256_ctx *ctx, const void *data, size_t len); void sha256_result(struct sha256_ctx *ctx, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); void sha256_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); void sha512_init(struct sha512_ctx *ctx); void sha512_loop(struct sha512_ctx *ctx, const void *data, size_t len); void sha512_result(struct sha512_ctx *ctx, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); void sha512_get_digest(const void *data, size_t size, unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); extern const struct hash_method hash_method_sha256; extern const struct hash_method hash_method_sha512; #endif /* !SHA2_H */ dovecot-2.2.33.2/src/lib/test-istream-failure-at.c0000644000175000017500000000320013123174404016470 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "istream.h" #include "istream-failure-at.h" #define TEST_DATA_LENGTH 128 #define TEST_ERRMSG "test-istream-failure-at error triggered" void test_istream_failure_at(void) { struct istream *input, *data_input; unsigned char test_data[TEST_DATA_LENGTH]; unsigned int i; ssize_t ret; test_begin("istream failure at"); for (i = 0; i < sizeof(test_data); i++) test_data[i] = i; data_input = i_stream_create_from_data(test_data, sizeof(test_data)); for (i = 0; i < TEST_DATA_LENGTH; i++) { i_stream_seek(data_input, 0); input = i_stream_create_failure_at(data_input, i, TEST_ERRMSG); while ((ret = i_stream_read(input)) > 0) i_stream_skip(input, ret); test_assert_idx(ret == -1 && input->v_offset == i && input->stream_errno == EIO && strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); i_stream_destroy(&input); } /* shouldn't fail */ i_stream_seek(data_input, 0); input = i_stream_create_failure_at(data_input, TEST_DATA_LENGTH, TEST_ERRMSG); while ((ret = i_stream_read(input)) > 0) i_stream_skip(input, ret); test_assert(ret == -1 && input->stream_errno == 0); i_stream_destroy(&input); /* fail at EOF */ i_stream_seek(data_input, 0); input = i_stream_create_failure_at_eof(data_input, TEST_ERRMSG); while ((ret = i_stream_read(input)) > 0) i_stream_skip(input, ret); test_assert_idx(ret == -1 && input->v_offset == TEST_DATA_LENGTH && input->stream_errno == EIO && strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); i_stream_destroy(&input); i_stream_destroy(&data_input); test_end(); } dovecot-2.2.33.2/src/lib/net.c0000644000175000017500000006443313165463624012640 00000000000000/* Copyright (c) 1999-2016 Dovecot authors, see the included COPYING file */ #define _GNU_SOURCE /* For Linux's struct ucred */ #include "lib.h" #include "fd-set-nonblock.h" #include "time-util.h" #include "net.h" #include #include #include #include #include #if defined(HAVE_UCRED_H) # include /* for getpeerucred() */ #elif defined(HAVE_SYS_UCRED_H) # include /* for FreeBSD struct xucred */ #endif union sockaddr_union { struct sockaddr sa; struct sockaddr_in sin; #ifdef HAVE_IPV6 struct sockaddr_in6 sin6; #endif }; union sockaddr_union_unix { struct sockaddr sa; struct sockaddr_un un; }; #ifdef HAVE_IPV6 # define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \ sizeof(so.sin6) : sizeof(so.sin)) #else # define SIZEOF_SOCKADDR(so) (sizeof(so.sin)) #endif #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_GETPEERUCRED) && defined(MSG_WAITALL) && defined(LOCAL_CREDS) # define NEEDS_LOCAL_CREDS 1 #endif /* If connect() fails with EADDRNOTAVAIL (or some others on FreeBSD), retry it this many times. This is needed on busy systems kernel may assign the same source port to two sockets at bind() stage, which is what we generally want to allow more than 64k outgoing connections to different destinations. However, at bind() stage the kernel doesn't know the destination yet. So it's possible that it assigns the same source port to two (or more) sockets that have the same destination IP+port as well. In this case connect() will fail with EADDRNOTAVAIL. We'll need to retry this and hope that the next attempt won't conflict. */ #define MAX_CONNECT_RETRIES 20 bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2) { return net_ip_cmp(ip1, ip2) == 0; } int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2) { if (ip1->family != ip2->family) return ip1->family - ip2->family; #ifdef HAVE_IPV6 if (ip1->family == AF_INET6) return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6)); #endif return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4)); } unsigned int net_ip_hash(const struct ip_addr *ip) { const unsigned char *p; unsigned int len, g, h = 0; #ifdef HAVE_IPV6 if (ip->family == AF_INET6) { p = ip->u.ip6.s6_addr; len = sizeof(ip->u.ip6); } else #endif { return ip->u.ip4.s_addr; } for (; len > 0; len--, p++) { h = (h << 4) + *p; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } } return h; } /* copy IP to sockaddr */ static inline void sin_set_ip(union sockaddr_union *so, const struct ip_addr *ip) { if (ip == NULL) { #ifdef HAVE_IPV6 so->sin6.sin6_family = AF_INET6; so->sin6.sin6_addr = in6addr_any; #else so->sin.sin_family = AF_INET; so->sin.sin_addr.s_addr = INADDR_ANY; #endif return; } so->sin.sin_family = ip->family; #ifdef HAVE_IPV6 if (ip->family == AF_INET6) memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6)); else #endif memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4)); } static inline void sin_get_ip(const union sockaddr_union *so, struct ip_addr *ip) { /* IP structs may be sent across processes. Clear the whole struct first to make sure it won't leak any data across processes. */ i_zero(ip); ip->family = so->sin.sin_family; #ifdef HAVE_IPV6 if (ip->family == AF_INET6) memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6)); else #endif if (ip->family == AF_INET) memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4)); else i_zero(&ip->u); } static inline void sin_set_port(union sockaddr_union *so, in_port_t port) { #ifdef HAVE_IPV6 if (so->sin.sin_family == AF_INET6) so->sin6.sin6_port = htons(port); else #endif so->sin.sin_port = htons(port); } static inline in_port_t sin_get_port(union sockaddr_union *so) { #ifdef HAVE_IPV6 if (so->sin.sin_family == AF_INET6) return ntohs(so->sin6.sin6_port); #endif if (so->sin.sin_family == AF_INET) return ntohs(so->sin.sin_port); return 0; } static int net_connect_ip_once(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip, int sock_type, bool blocking) { union sockaddr_union so; int fd, ret, opt = 1; if (my_ip != NULL && ip->family != my_ip->family) { i_warning("net_connect_ip(): ip->family != my_ip->family"); my_ip = NULL; } /* create the socket */ i_zero(&so); so.sin.sin_family = ip->family; fd = socket(ip->family, sock_type, 0); if (fd == -1) { i_error("socket() failed: %m"); return -1; } /* set socket options */ (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (sock_type == SOCK_STREAM) (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)); if (!blocking) net_set_nonblock(fd, TRUE); /* set our own address */ if (my_ip != NULL) { sin_set_ip(&so, my_ip); if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) { i_error("bind(%s) failed: %m", net_ip2addr(my_ip)); i_close_fd(&fd); return -1; } } /* connect */ sin_set_ip(&so, ip); sin_set_port(&so, port); ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so)); #ifndef WIN32 if (ret < 0 && errno != EINPROGRESS) #else if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) #endif { i_close_fd(&fd); return -1; } return fd; } static int net_connect_ip_full(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip, int sock_type, bool blocking) { int fd, try; for (try = 0;;) { fd = net_connect_ip_once(ip, port, my_ip, sock_type, blocking); if (fd != -1 || try++ >= MAX_CONNECT_RETRIES || (errno != EADDRNOTAVAIL #ifdef __FreeBSD__ /* busy */ && errno != EADDRINUSE /* pf may cause this if another connection used the same port recently */ && errno != EACCES #endif )) break; } return fd; } int net_connect_ip(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip) { return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, FALSE); } int net_connect_ip_blocking(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip) { return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, TRUE); } int net_connect_udp(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip) { return net_connect_ip_full(ip, port, my_ip, SOCK_DGRAM, FALSE); } int net_try_bind(const struct ip_addr *ip) { union sockaddr_union so; int fd; /* create the socket */ i_zero(&so); so.sin.sin_family = ip->family; fd = socket(ip->family, SOCK_STREAM, 0); if (fd == -1) { i_error("socket() failed: %m"); return -1; } sin_set_ip(&so, ip); if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) { i_close_fd(&fd); return -1; } i_close_fd(&fd); return 0; } int net_connect_unix(const char *path) { union sockaddr_union_unix sa; int fd, ret; i_zero(&sa); sa.un.sun_family = AF_UNIX; if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) { /* too long path */ #ifdef ENAMETOOLONG errno = ENAMETOOLONG; #else errno = EINVAL; #endif return -1; } /* create the socket */ fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { i_error("socket(%s) failed: %m", path); return -1; } net_set_nonblock(fd, TRUE); /* connect */ ret = connect(fd, &sa.sa, sizeof(sa)); if (ret < 0 && errno != EINPROGRESS) { i_close_fd(&fd); return -1; } #ifdef NEEDS_LOCAL_CREDS { int on = 1; if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) { i_error("setsockopt(LOCAL_CREDS) failed: %m"); return -1; } } #endif return fd; } int net_connect_unix_with_retries(const char *path, unsigned int msecs) { struct timeval start, now; int fd; if (gettimeofday(&start, NULL) < 0) i_panic("gettimeofday() failed: %m"); do { fd = net_connect_unix(path); if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED)) break; /* busy. wait for a while. */ usleep(((rand() % 10) + 1) * 10000); if (gettimeofday(&now, NULL) < 0) i_panic("gettimeofday() failed: %m"); } while (timeval_diff_msecs(&now, &start) < (int)msecs); return fd; } void net_disconnect(int fd) { /* FreeBSD's close() fails with ECONNRESET if socket still has unsent data in transmit buffer. We don't care. */ if (close(fd) < 0 && errno != ECONNRESET) i_error("net_disconnect() failed: %m"); } void net_set_nonblock(int fd, bool nonblock) { fd_set_nonblock(fd, nonblock); } int net_set_cork(int fd ATTR_UNUSED, bool cork ATTR_UNUSED) { #ifdef TCP_CORK int val = cork; return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(val)); #else errno = ENOPROTOOPT; return -1; #endif } int net_set_tcp_nodelay(int fd, bool nodelay) { int val = nodelay; return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); } int net_set_send_buffer_size(int fd, size_t size) { int opt; if (size > INT_MAX) { errno = EINVAL; return -1; } opt = (int)size; return setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); } int net_set_recv_buffer_size(int fd, size_t size) { int opt; if (size > INT_MAX) { errno = EINVAL; return -1; } opt = (int)size; return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); } void net_get_ip_any4(struct ip_addr *ip) { ip->family = AF_INET; ip->u.ip4.s_addr = INADDR_ANY; } void net_get_ip_any6(struct ip_addr *ip) { #ifdef HAVE_IPV6 ip->family = AF_INET6; ip->u.ip6 = in6addr_any; #else memset(ip, 0, sizeof(struct ip_addr)); #endif } int net_listen(const struct ip_addr *my_ip, in_port_t *port, int backlog) { enum net_listen_flags flags = 0; return net_listen_full(my_ip, port, &flags, backlog); } int net_listen_full(const struct ip_addr *my_ip, in_port_t *port, enum net_listen_flags *flags, int backlog) { union sockaddr_union so; int ret, fd, opt = 1; socklen_t len; i_zero(&so); sin_set_port(&so, *port); sin_set_ip(&so, my_ip); /* create the socket */ fd = socket(so.sin.sin_family, SOCK_STREAM, 0); #ifdef HAVE_IPV6 if (fd == -1 && my_ip == NULL && (errno == EINVAL || errno == EAFNOSUPPORT)) { /* IPv6 is not supported by OS */ so.sin.sin_family = AF_INET; so.sin.sin_addr.s_addr = INADDR_ANY; fd = socket(AF_INET, SOCK_STREAM, 0); } #endif if (fd == -1) { i_error("socket() failed: %m"); return -1; } /* set socket options */ (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)); if ((*flags & NET_LISTEN_FLAG_REUSEPORT) != 0) { #ifdef SO_REUSEPORT if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) #endif *flags &= ~NET_LISTEN_FLAG_REUSEPORT; } /* If using IPv6, bind only to IPv6 if possible. This avoids ambiguities with IPv4-mapped IPv6 addresses. */ #ifdef IPV6_V6ONLY if (so.sin.sin_family == AF_INET6) { opt = 1; (void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); } #endif /* specify the address/port we want to listen in */ ret = bind(fd, &so.sa, SIZEOF_SOCKADDR(so)); if (ret < 0) { if (errno != EADDRINUSE) { i_error("bind(%s, %u) failed: %m", my_ip == NULL ? "" : net_ip2addr(my_ip), *port); } } else { /* get the actual port we started listen */ len = SIZEOF_SOCKADDR(so); ret = getsockname(fd, &so.sa, &len); if (ret >= 0) { *port = sin_get_port(&so); /* start listening */ if (listen(fd, backlog) >= 0) return fd; if (errno != EADDRINUSE) i_error("listen() failed: %m"); } } /* error */ i_close_fd(&fd); return -1; } int net_listen_unix(const char *path, int backlog) { union { struct sockaddr sa; struct sockaddr_un un; } sa; int fd; i_zero(&sa); sa.un.sun_family = AF_UNIX; if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) { /* too long path */ errno = EOVERFLOW; return -1; } /* create the socket */ fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { i_error("socket() failed: %m"); return -1; } #ifdef NEEDS_LOCAL_CREDS { int on = 1; if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) { i_error("setsockopt(LOCAL_CREDS) failed: %m"); return -1; } } #endif /* bind */ if (bind(fd, &sa.sa, sizeof(sa)) < 0) { if (errno != EADDRINUSE) i_error("bind(%s) failed: %m", path); } else { /* start listening */ if (listen(fd, backlog) == 0) return fd; if (errno != EADDRINUSE) i_error("listen() failed: %m"); } i_close_fd(&fd); return -1; } int net_listen_unix_unlink_stale(const char *path, int backlog) { unsigned int i = 0; int fd; while ((fd = net_listen_unix(path, backlog)) == -1) { if (errno != EADDRINUSE || ++i == 2) return -1; /* see if it really exists */ fd = net_connect_unix(path); if (fd != -1 || errno != ECONNREFUSED) { if (fd != -1) i_close_fd(&fd); errno = EADDRINUSE; return -1; } /* delete and try again */ if (i_unlink_if_exists(path) < 0) { errno = EADDRINUSE; return -1; } } return fd; } int net_accept(int fd, struct ip_addr *addr_r, in_port_t *port_r) { union sockaddr_union so; int ret; socklen_t addrlen; i_assert(fd >= 0); addrlen = sizeof(so); ret = accept(fd, &so.sa, &addrlen); if (ret < 0) { if (errno == EAGAIN || errno == ECONNABORTED) return -1; else return -2; } if (so.sin.sin_family == AF_UNIX) { if (addr_r != NULL) i_zero(addr_r); if (port_r != NULL) *port_r = 0; } else { if (addr_r != NULL) sin_get_ip(&so, addr_r); if (port_r != NULL) *port_r = sin_get_port(&so); } return ret; } ssize_t net_receive(int fd, void *buf, size_t len) { ssize_t ret; i_assert(fd >= 0); i_assert(len <= SSIZE_T_MAX); ret = read(fd, buf, len); if (ret == 0) { /* disconnected */ errno = 0; return -2; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) return 0; if (errno == ECONNRESET || errno == ETIMEDOUT) { /* treat as disconnection */ return -2; } } return ret; } ssize_t net_transmit(int fd, const void *data, size_t len) { ssize_t ret; i_assert(fd >= 0); i_assert(len <= SSIZE_T_MAX); ret = send(fd, data, len, 0); if (ret == -1) { if (errno == EINTR || errno == EAGAIN) return 0; if (errno == EPIPE) return -2; } return ret; } int net_gethostbyname(const char *addr, struct ip_addr **ips, unsigned int *ips_count) { /* @UNSAFE */ #ifdef HAVE_IPV6 union sockaddr_union *so; struct addrinfo hints, *ai, *origai; struct ip_addr ip; int host_error; #else struct hostent *hp; #endif int count; *ips = NULL; *ips_count = 0; #ifdef HAVE_IPV6 /* support [ipv6] style addresses here so they work globally */ if (addr[0] == '[' && net_addr2ip(addr, &ip) == 0) { *ips_count = 1; *ips = t_new(struct ip_addr, 1); **ips = ip; return 0; } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; /* save error to host_error for later use */ host_error = getaddrinfo(addr, NULL, &hints, &ai); if (host_error != 0) return host_error; /* get number of IPs */ origai = ai; for (count = 0; ai != NULL; ai = ai->ai_next) count++; *ips_count = count; *ips = t_malloc(sizeof(struct ip_addr) * count); count = 0; for (ai = origai; ai != NULL; ai = ai->ai_next, count++) { so = (union sockaddr_union *) ai->ai_addr; sin_get_ip(so, &(*ips)[count]); } freeaddrinfo(origai); #else hp = gethostbyname(addr); if (hp == NULL) return h_errno; /* get number of IPs */ count = 0; while (hp->h_addr_list[count] != NULL) count++; *ips_count = count; *ips = t_malloc(sizeof(struct ip_addr) * count); while (count > 0) { count--; (*ips)[count].family = AF_INET; memcpy(&(*ips)[count].u.ip4, hp->h_addr_list[count], sizeof((*ips)[count].u.ip4)); } #endif return 0; } int net_gethostbyaddr(const struct ip_addr *ip, const char **name_r) { union sockaddr_union so; socklen_t addrlen = sizeof(so); char hbuf[NI_MAXHOST]; int ret; i_zero(&so); sin_set_ip(&so, ip); ret = getnameinfo(&so.sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD); if (ret != 0) return ret; *name_r = t_strdup(hbuf); return 0; } int net_getsockname(int fd, struct ip_addr *addr, in_port_t *port) { union sockaddr_union so; socklen_t addrlen; i_assert(fd >= 0); addrlen = sizeof(so); if (getsockname(fd, &so.sa, &addrlen) == -1) return -1; if (so.sin.sin_family == AF_UNIX) { if (addr != NULL) i_zero(addr); if (port != NULL) *port = 0; } else { if (addr != NULL) sin_get_ip(&so, addr); if (port != NULL) *port = sin_get_port(&so); } return 0; } int net_getpeername(int fd, struct ip_addr *addr, in_port_t *port) { union sockaddr_union so; socklen_t addrlen; i_assert(fd >= 0); addrlen = sizeof(so); if (getpeername(fd, &so.sa, &addrlen) == -1) return -1; if (so.sin.sin_family == AF_UNIX) { if (addr != NULL) i_zero(addr); if (port != NULL) *port = 0; } else { if (addr != NULL) sin_get_ip(&so, addr); if (port != NULL) *port = sin_get_port(&so); } return 0; } int net_getunixname(int fd, const char **name_r) { union sockaddr_union_unix so; socklen_t addrlen = sizeof(so); if (getsockname(fd, &so.sa, &addrlen) < 0) return -1; if (so.un.sun_family != AF_UNIX) { errno = ENOTSOCK; return -1; } *name_r = t_strdup(so.un.sun_path); return 0; } int net_getunixcred(int fd, struct net_unix_cred *cred_r) { #if defined(SO_PEERCRED) # if defined(HAVE_STRUCT_SOCKPEERCRED) /* OpenBSD (may also provide getpeereid, but we also want pid) */ struct sockpeercred ucred; # else /* Linux */ struct ucred ucred; # endif socklen_t len = sizeof(ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { i_error("getsockopt(SO_PEERCRED) failed: %m"); return -1; } cred_r->uid = ucred.uid; cred_r->gid = ucred.gid; cred_r->pid = ucred.pid; return 0; #elif defined(LOCAL_PEEREID) /* NetBSD (may also provide getpeereid, but we also want pid) */ struct unpcbid ucred; socklen_t len = sizeof(ucred); if (getsockopt(fd, 0, LOCAL_PEEREID, &ucred, &len) < 0) { i_error("getsockopt(LOCAL_PEEREID) failed: %m"); return -1; } cred_r->uid = ucred.unp_euid; cred_r->gid = ucred.unp_egid; cred_r->pid = ucred.unp_pid; return 0; #elif defined(HAVE_GETPEEREID) /* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */ if (getpeereid(fd, &cred_r->uid, &cred_r->gid) < 0) { i_error("getpeereid() failed: %m"); return -1; } cred_r->pid = (pid_t)-1; return 0; #elif defined(LOCAL_PEERCRED) /* Older FreeBSD */ struct xucred ucred; socklen_t len = sizeof(ucred); if (getsockopt(fd, 0, LOCAL_PEERCRED, &ucred, &len) < 0) { i_error("getsockopt(LOCAL_PEERCRED) failed: %m"); return -1; } if (ucred.cr_version != XUCRED_VERSION) { errno = EINVAL; return -1; } cred_r->uid = ucred.cr_uid; cred_r->gid = ucred.cr_gid; cred_r->pid = (pid_t)-1; return 0; #elif defined(HAVE_GETPEERUCRED) /* Solaris */ ucred_t *ucred = NULL; if (getpeerucred(fd, &ucred) < 0) { i_error("getpeerucred() failed: %m"); return -1; } cred_r->uid = ucred_geteuid(ucred); cred_r->gid = ucred_getrgid(ucred); cred_r->pid = ucred_getpid(ucred); ucred_free(ucred); if (cred_r->uid == (uid_t)-1 || cred_r->gid == (gid_t)-1) { errno = EINVAL; return -1; } return 0; #elif NEEDS_LOCAL_CREDS /* NetBSD < 5 */ int i, n, on; struct iovec iov; struct msghdr msg; struct { struct cmsghdr ch; char buf[110]; } cdata; struct sockcred *sc; iov.iov_base = (char *)&on; iov.iov_len = 1; sc = (struct sockcred *)cdata.buf; sc->sc_uid = sc->sc_euid = sc->sc_gid = sc->sc_egid = -1; memset(&cdata.ch, 0, sizeof cdata.ch); memset(&msg, 0, sizeof msg); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cdata; msg.msg_controllen = sizeof(cdata.ch) + sizeof(cdata.buf); for (i = 0; i < 10; i++) { n = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK); if (n >= 0 || errno != EAGAIN) break; usleep(100); } if (n < 0) { i_error("recvmsg() failed: %m"); return -1; } cred_r->uid = sc->sc_euid; cred_r->gid = sc->sc_egid; cred_r->pid = (pid_t)-1; return 0; #else errno = EINVAL; return -1; #endif } const char *net_ip2addr(const struct ip_addr *ip) { #ifdef HAVE_IPV6 char addr[MAX_IP_LEN+1]; addr[MAX_IP_LEN] = '\0'; if (inet_ntop(ip->family, &ip->u.ip6, addr, MAX_IP_LEN) == NULL) return ""; return t_strdup(addr); #else unsigned long ip4; if (ip->family != AF_INET) return ""; ip4 = ntohl(ip->u.ip4.s_addr); return t_strdup_printf("%lu.%lu.%lu.%lu", (ip4 & 0xff000000UL) >> 24, (ip4 & 0x00ff0000) >> 16, (ip4 & 0x0000ff00) >> 8, (ip4 & 0x000000ff)); #endif } int net_addr2ip(const char *addr, struct ip_addr *ip) { int ret; if (strchr(addr, ':') != NULL) { /* IPv6 */ #ifdef HAVE_IPV6 T_BEGIN { if (addr[0] == '[') { /* allow [ipv6 addr] */ size_t len = strlen(addr); if (addr[len-1] == ']') addr = t_strndup(addr+1, len-2); } ret = inet_pton(AF_INET6, addr, &ip->u.ip6); } T_END; if (ret == 0) return -1; #else ip->u.ip4.s_addr = 0; #endif ip->family = AF_INET6; } else { /* IPv4 */ if (inet_aton(addr, &ip->u.ip4) == 0) return -1; ip->family = AF_INET; } return 0; } int net_str2port(const char *str, in_port_t *port_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (l == 0 || l > (in_port_t)-1) return -1; *port_r = (in_port_t)l; return 0; } int net_str2port_zero(const char *str, in_port_t *port_r) { uintmax_t l; if (str_to_uintmax(str, &l) < 0) return -1; if (l > (in_port_t)-1) return -1; *port_r = (in_port_t)l; return 0; } int net_str2hostport(const char *str, in_port_t default_port, const char **host_r, in_port_t *port_r) { const char *p, *host; in_port_t port; if (str[0] == '[') { /* [IPv6] address, possibly followed by :port */ p = strchr(str, ']'); if (p == NULL) return -1; host = t_strdup_until(str+1, p++); } else { p = strchr(str, ':'); if (p == NULL || strchr(p+1, ':') != NULL) { /* host or IPv6 address */ *host_r = str; *port_r = default_port; return 0; } host = t_strdup_until(str, p); } if (p[0] == '\0') { *host_r = host; *port_r = default_port; return 0; } if (p[0] != ':') return -1; if (net_str2port(p+1, &port) < 0) return -1; *host_r = host; *port_r = port; return 0; } int net_ipport2str(const struct ip_addr *ip, in_port_t port, const char **str_r) { if (!IPADDR_IS_V4(ip) && !IPADDR_IS_V6(ip)) return -1; *str_r = t_strdup_printf("%s%s%s:%u", IPADDR_IS_V6(ip) ? "[" : "", net_ip2addr(ip), IPADDR_IS_V6(ip) ? "]" : "", port); return 0; } int net_ipv6_mapped_ipv4_convert(const struct ip_addr *src, struct ip_addr *dest) { #ifdef HAVE_IPV6 static uint8_t v4_prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; if (!IPADDR_IS_V6(src)) return -1; if (memcmp(src->u.ip6.s6_addr, v4_prefix, sizeof(v4_prefix)) != 0) return -1; dest->family = AF_INET; memcpy(&dest->u.ip6, &src->u.ip6.s6_addr[3*4], 4); return 0; #else return -1; #endif } int net_geterror(int fd) { int data; socklen_t len = sizeof(data); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &data, &len) == -1) { /* we're now really returning the getsockopt()'s error code instead of the socket's, but normally we should never get here anyway. */ return errno; } return data; } const char *net_gethosterror(int error) { #ifdef HAVE_IPV6 i_assert(error != 0); return gai_strerror(error); #else switch (error) { case HOST_NOT_FOUND: return "Host not found"; case NO_ADDRESS: return "No IP address found for name"; case NO_RECOVERY: return "A non-recoverable name server error occurred"; case TRY_AGAIN: return "A temporary error on an authoritative name server"; default: return t_strdup_printf("Unknown error %d", error); } #endif } int net_hosterror_notfound(int error) { #ifdef HAVE_IPV6 #ifdef EAI_NODATA /* NODATA is depricated */ return error != 1 && (error == EAI_NONAME || error == EAI_NODATA); #else return error != 1 && (error == EAI_NONAME); #endif #else return error == HOST_NOT_FOUND || error == NO_ADDRESS; #endif } const char *net_getservbyport(in_port_t port) { struct servent *entry; entry = getservbyport(htons(port), "tcp"); return entry == NULL ? NULL : entry->s_name; } bool is_ipv4_address(const char *addr) { while (*addr != '\0') { if (*addr != '.' && !i_isdigit(*addr)) return FALSE; addr++; } return TRUE; } bool is_ipv6_address(const char *addr) { bool have_prefix = FALSE; if (*addr == '[') { have_prefix = TRUE; addr++; } while (*addr != '\0') { if (*addr != ':' && !i_isxdigit(*addr)) { if (have_prefix && *addr == ']' && addr[1] == '\0') break; return FALSE; } addr++; } return TRUE; } int net_parse_range(const char *network, struct ip_addr *ip_r, unsigned int *bits_r) { const char *p; unsigned int bits, max_bits; p = strchr(network, '/'); if (p != NULL) network = t_strdup_until(network, p++); if (net_addr2ip(network, ip_r) < 0) return -1; max_bits = IPADDR_BITS(ip_r); if (p == NULL) { /* full IP address must match */ bits = max_bits; } else { /* get the network mask */ if (str_to_uint(p, &bits) < 0 || bits > max_bits) return -1; } *bits_r = bits; return 0; } bool net_is_in_network(const struct ip_addr *ip, const struct ip_addr *net_ip, unsigned int bits) { struct ip_addr tmp_ip; const uint32_t *ip1, *ip2; uint32_t mask, i1, i2; unsigned int pos, i; if (net_ipv6_mapped_ipv4_convert(ip, &tmp_ip) == 0) { /* IPv4 address mapped disguised as IPv6 address */ ip = &tmp_ip; } if (ip->family == 0 || net_ip->family == 0) { /* non-IPv4/IPv6 address (e.g. UNIX socket) never matches anything */ return FALSE; } if (IPADDR_IS_V4(ip) != IPADDR_IS_V4(net_ip)) { /* one is IPv6 and one is IPv4 */ return FALSE; } i_assert(IPADDR_IS_V6(ip) == IPADDR_IS_V6(net_ip)); if (IPADDR_IS_V4(ip)) { ip1 = &ip->u.ip4.s_addr; ip2 = &net_ip->u.ip4.s_addr; } else { #ifdef HAVE_IPV6 ip1 = (const void *)&ip->u.ip6; ip2 = (const void *)&net_ip->u.ip6; #else /* shouldn't get here */ return FALSE; #endif } /* check first the full 32bit ints */ for (pos = 0, i = 0; pos + 32 <= bits; pos += 32, i++) { if (ip1[i] != ip2[i]) return FALSE; } i1 = htonl(ip1[i]); i2 = htonl(ip2[i]); /* check the last full bytes */ for (mask = 0xff000000; pos + 8 <= bits; pos += 8, mask >>= 8) { if ((i1 & mask) != (i2 & mask)) return FALSE; } /* check the last bits, they're reversed in bytes */ bits -= pos; for (mask = 0x80000000 >> (pos % 32); bits > 0; bits--, mask >>= 1) { if ((i1 & mask) != (i2 & mask)) return FALSE; } return TRUE; } dovecot-2.2.33.2/src/lib/abspath.c0000644000175000017500000000362713165463624013472 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "abspath.h" #include const char *t_abspath(const char *path) { const char *dir; if (*path == '/') return path; if (t_get_current_dir(&dir) < 0) i_fatal("getcwd() failed: %m"); return t_strconcat(dir, "/", path, NULL); } const char *t_abspath_to(const char *path, const char *root) { if (*path == '/') return path; return t_strconcat(root, "/", path, NULL); } int t_get_current_dir(const char **dir_r) { /* @UNSAFE */ char *dir; size_t size = 128; dir = t_buffer_get(size); while (getcwd(dir, size) == NULL) { if (errno != ERANGE) return -1; size = nearest_power(size+1); dir = t_buffer_get(size); } t_buffer_alloc(strlen(dir) + 1); *dir_r = dir; return 0; } int t_readlink(const char *path, const char **dest_r) { /* @UNSAFE */ ssize_t ret; char *dest; size_t size = 128; dest = t_buffer_get(size); while ((ret = readlink(path, dest, size)) >= (ssize_t)size) { size = nearest_power(size+1); dest = t_buffer_get(size); } if (ret < 0) return -1; dest[ret] = '\0'; t_buffer_alloc(ret + 1); *dest_r = dest; return 0; } bool t_binary_abspath(const char **binpath) { const char *path_env, *const *paths; string_t *path; if (**binpath == '/') { /* already have absolute path */ return TRUE; } else if (strchr(*binpath, '/') != NULL) { /* relative to current directory */ *binpath = t_abspath(*binpath); return TRUE; } else if ((path_env = getenv("PATH")) != NULL) { /* we have to find our executable from path */ path = t_str_new(256); paths = t_strsplit(path_env, ":"); for (; *paths != NULL; paths++) { str_append(path, *paths); str_append_c(path, '/'); str_append(path, *binpath); if (access(str_c(path), X_OK) == 0) { *binpath = str_c(path); return TRUE; } str_truncate(path, 0); } } return FALSE; } dovecot-2.2.33.2/src/lib/imem.c0000644000175000017500000000252013165463624012766 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" pool_t default_pool = &static_system_pool; void *i_malloc(size_t size) { return p_malloc(default_pool, size); } void *i_realloc(void *mem, size_t old_size, size_t new_size) { return p_realloc(default_pool, mem, old_size, new_size); } char *i_strdup(const char *str) { return p_strdup(default_pool, str); } char *i_strdup_empty(const char *str) { return p_strdup_empty(default_pool, str); } char *i_strdup_until(const void *str, const void *end) { return p_strdup_until(default_pool, str, end); } char *i_strndup(const void *str, size_t max_chars) { return p_strndup(default_pool, str, max_chars); } char *i_strdup_printf(const char *format, ...) { va_list args; char *ret; va_start(args, format); ret = p_strdup_vprintf(default_pool, format, args); va_end(args); return ret; } char *i_strdup_vprintf(const char *format, va_list args) { return p_strdup_vprintf(default_pool, format, args); } char *i_strconcat(const char *str1, ...) { va_list args; char *ret; size_t len; va_start(args, str1); T_BEGIN { const char *temp = vstrconcat(str1, args, &len); if (temp == NULL) ret = NULL; else { t_buffer_alloc(len); ret = p_malloc(default_pool, len); memcpy(ret, temp, len); } } T_END; va_end(args); return ret; } dovecot-2.2.33.2/src/lib/test-strnum.c0000644000175000017500000002631713165463624014356 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #define INVALID(n) { #n, -1, 0 } #define VALID(n) { #n, 0, n } /* always pads with leading zeros to a size of 9 digits */ static int crappy_uintmax_to_str(char *into, uintmax_t val) { #define BIGBASE 1000000000ull #define STRINGIFY(s) #s #define STRINGIFY2(s) STRINGIFY(s) int len = 0; if(val >= BIGBASE) { len = crappy_uintmax_to_str(into, val/BIGBASE); } i_snprintf(into + len, 10, "%09llu", (unsigned long long)(val % BIGBASE)); return len + strlen(STRINGIFY2(BIGBASE))-4; #undef STRINGIFY2 #undef STRINGIFY #undef BIGBASE } static void test_str_to_uintmax(void) { unsigned int i=0; int randrange = rand()%15+1; /* when 1, will max out on 1s */ uintmax_t value = 0, valbase = rand() * 1000ull; int len, ret; char buff[50]; /* totally assumes < 159 bits */ test_begin("str_to_uintmax in range"); while (i < sizeof(uintmax_t)*CHAR_BIT) { uintmax_t value_back; const char *endp; value = (value << 1) + 1; if (value >= 64) value -= rand()%randrange; /* don't always test the same numbers */ len = crappy_uintmax_to_str(buff, value); ret = str_to_uintmax(buff, &value_back); test_assert_idx(ret == 0, i); test_assert_idx(value == value_back, i); /* test with trailing noise */ buff[len] = 'x'; /* don't even null-terminate, let's be evil */ value_back = 0x1234567890123456; ret = str_to_uintmax(buff, &value_back); test_assert_idx(ret < 0, i); test_assert_idx(value_back == 0x1234567890123456, i); ret = str_parse_uintmax(buff, &value_back, &endp); test_assert_idx(ret == 0, i); test_assert_idx(value_back == value, i); test_assert_idx(endp == &buff[len], i); i++; } test_end(); /* not knowing exactly how large a uintmax_t is, we have to construct the troublesome near-10/9*MAX strings manually by appending digits to a MAX/9 string which we can easily create. Do a wider range of 30 rather than the obvious 10, just in case - all are too large.*/ test_begin("str_to_uintmax overflow corner case"); value = UINTMAX_MAX/9-1; len = crappy_uintmax_to_str(buff, value); buff[len] = '0'; buff[len+1] = '\0'; for(i = 0; i <= 30; ++i) { int j = len + 1; while (buff[--j] == '9') buff[j] = '0'; buff[j]++; value = valbase + i; ret = str_to_uintmax(buff, &value); test_assert_idx(ret < 0 && value == valbase + i, i); } test_end(); } /* always pads with leading zeros to a size of 9 digits */ static int crappy_uintmax_to_str_hex(char *into, uintmax_t val) { #define BIGBASE 0x1000000000ull #define STRINGIFY(s) #s #define STRINGIFY2(s) STRINGIFY(s) int len = 0; if(val >= BIGBASE) { len = crappy_uintmax_to_str_hex(into, val/BIGBASE); } i_snprintf(into + len, 10, "%09llx", (unsigned long long)(val % BIGBASE)); return len + strlen(STRINGIFY2(BIGBASE))-6; #undef STRINGIFY2 #undef STRINGIFY #undef BIGBASE } static void test_str_to_uintmax_hex(void) { unsigned int i=0; int randrange = rand()%15+1; /* when 1, will max out on 1s */ uintmax_t value = 0, valbase = rand() * 1000ull; int len, ret; char buff[52]; /* totally assumes < 200 bits */ test_begin("str_to_uintmax_hex in range"); while (i < sizeof(uintmax_t)*CHAR_BIT) { uintmax_t value_back; const char *endp; value = (value << 1) + 1; if (value >= 64) value -= rand()%randrange; /* don't always test the same numbers */ len = crappy_uintmax_to_str_hex(buff, value); ret = str_to_uintmax_hex(buff, &value_back); test_assert_idx(ret == 0, i); test_assert_idx(value == value_back, i); /* test with trailing noise */ buff[len] = 'x'; /* don't even null-terminate, let's be evil */ value_back = 0x1234567890123456; ret = str_to_uintmax_hex(buff, &value_back); test_assert_idx(ret < 0, i); test_assert_idx(value_back == 0x1234567890123456, i); ret = str_parse_uintmax_hex(buff, &value_back, &endp); test_assert_idx(ret == 0, i); test_assert_idx(value_back == value, i); test_assert_idx(endp == &buff[len], i); i++; } test_end(); /* not knowing exactly how large a uintmax_t is, we have to construct the troublesome near-0x10/0x0F*MAX strings manually by appending digits to a MAX/0x0f string which we can easily create. Do a wider range of 0x30 rather than the obvious 0x10, just in case - all are too large.*/ test_begin("str_to_uintmax_hex overflow corner case"); value = (UINTMAX_MAX/0x0f)-1; len = crappy_uintmax_to_str_hex(buff, value); buff[len] = '0'; buff[len+1] = '\0'; for(i = 0; i <= 0x30; ++i) { int j = len + 1; while (buff[--j] == 'f') buff[j] = '0'; if (buff[j] == '9') buff[j] = 'a'; else buff[j]++; value = valbase + i; ret = str_to_uintmax_hex(buff, &value); test_assert_idx(ret < 0 && value == valbase + i, i); } test_end(); } /* always pads with leading zeros to a size of 9 digits */ static int crappy_uintmax_to_str_oct(char *into, uintmax_t val) { #define BIGBASE 01000000000ull #define STRINGIFY(s) #s #define STRINGIFY2(s) STRINGIFY(s) int len = 0; if(val >= BIGBASE) { len = crappy_uintmax_to_str_oct(into, val/BIGBASE); } i_snprintf(into + len, 10, "%09llo", (unsigned long long)(val % BIGBASE)); return len + strlen(STRINGIFY2(BIGBASE))-5; #undef STRINGIFY2 #undef STRINGIFY #undef BIGBASE } static void test_str_to_uintmax_oct(void) { unsigned int i=0; int randrange = rand()%15+1; /* when 1, will max out on 1s */ uintmax_t value = 0, valbase = rand() * 1000ull; int len, ret; char buff[69]; /* totally assumes < 200 bits */ test_begin("str_to_uintmax_oct in range"); while (i < sizeof(uintmax_t)*CHAR_BIT) { uintmax_t value_back; const char *endp; value = (value << 1) + 1; if (value >= 64) value -= rand()%randrange; /* don't always test the same numbers */ len = crappy_uintmax_to_str_oct(buff, value); ret = str_to_uintmax_oct(buff, &value_back); test_assert_idx(ret == 0, i); test_assert_idx(value == value_back, i); /* test with trailing noise */ buff[len] = 'x'; /* don't even null-terminate, let's be evil */ value_back = 0x1234567890123456; ret = str_to_uintmax_oct(buff, &value_back); test_assert_idx(ret < 0, i); test_assert_idx(value_back == 0x1234567890123456, i); ret = str_parse_uintmax_oct(buff, &value_back, &endp); test_assert_idx(ret == 0, i); test_assert_idx(value_back == value, i); test_assert_idx(endp == &buff[len], i); i++; } test_end(); /* not knowing exactly how large a uintmax_t is, we have to construct the troublesome near-010/007*MAX strings manually by appending digits to a MAX/007 string which we can easily create. Do a wider range of 030 rather than the obvious 010, just in case - all are too large.*/ test_begin("str_to_uintmax_oct overflow corner case"); value = (UINTMAX_MAX/007)-1; len = crappy_uintmax_to_str_oct(buff, value); buff[len] = '0'; buff[len+1] = '\0'; for(i = 0; i <= 030; ++i) { int j = len + 1; while (buff[--j] == '7') buff[j] = '0'; buff[j]++; value = valbase + i; ret = str_to_uintmax_oct(buff, &value); test_assert_idx(ret < 0 && value == valbase + i, i); } test_end(); } static void test_str_to_u64(void) { unsigned int i; const struct { const char *input; int ret; uint64_t val; } u64tests[] = { INVALID(-1), INVALID(foo), VALID(0), VALID(000000000000000000000000000000000000000000000000000000000000000), { "000000000000000000000000000000000000000000000000000001000000001", 0, 1000000001 }, { "18446744073709551615", 0, 18446744073709551615ULL }, INVALID(18446744073709551616), INVALID(20496382304121724010), /* 2^64*10/9 doesn't wrap */ INVALID(20496382304121724017), /* 2^64*10/9 wraps only after addition */ INVALID(20496382304121724020), /* 2^64*10/9 wraps on multiply*/ }; test_begin("str_to_uint64"); for (i = 0; i < N_ELEMENTS(u64tests); ++i) { uint64_t val = 0xBADBEEF15BADF00D; int ret = str_to_uint64(u64tests[i].input, &val); test_assert_idx(ret == u64tests[i].ret, i); if (ret == 0) test_assert_idx(val == u64tests[i].val, i); else test_assert_idx(val == 0xBADBEEF15BADF00D, i); if (ret == 0) T_BEGIN { const char *longer = t_strconcat(u64tests[i].input, "x", NULL); ret = str_to_uint64(longer, &val); test_assert_idx(ret < 0, i); } T_END; } test_end(); } static void test_str_to_u32(void) { unsigned int i; const struct { const char *input; int ret; uint32_t val; } u32tests[] = { VALID(0), INVALID(-0), VALID(4294967295), INVALID(4294967296), INVALID(4772185880), INVALID(4772185884), INVALID(4772185890), }; test_begin("str_to_uint32"); for (i = 0; i < N_ELEMENTS(u32tests); ++i) { uint32_t val = 0xDEADF00D; int ret = str_to_uint32(u32tests[i].input, &val); test_assert_idx(ret == u32tests[i].ret, i); if (ret == 0) test_assert_idx(val == u32tests[i].val, i); else test_assert_idx(val == 0xDEADF00D, i); } test_end(); } /* Assumes long long is 64 bit, 2's complement */ static void test_str_to_llong(void) { unsigned int i; const struct { const char *input; int ret; long long val; } i64tests[] = { VALID(0), VALID(-0), INVALID(--0), VALID(2147483648), VALID(-2147483649), VALID(9223372036854775807), { "-9223372036854775808", 0, -9223372036854775807-1 }, INVALID(9223372036854775808), INVALID(-9223372036854775809), }; test_begin("str_to_llong"); for (i = 0; i < N_ELEMENTS(i64tests); ++i) { long long val = 123456789; int ret = str_to_llong(i64tests[i].input, &val); test_assert_idx(ret == i64tests[i].ret, i); if (ret == 0) test_assert_idx(val == i64tests[i].val, i); else test_assert_idx(val == 123456789, i); } test_end(); } /* Assumes int is 32 bit, 2's complement */ static void test_str_to_i32(void) { unsigned int i; const struct { const char *input; int ret; int val; } i32tests[] = { VALID(0), VALID(-0), INVALID(--0), VALID(2147483647), VALID(-2147483648), INVALID(2147483648), INVALID(-2147483649), }; test_begin("str_to_int"); for (i = 0; i < N_ELEMENTS(i32tests); ++i) { int val = 123456789; int ret = str_to_int(i32tests[i].input, &val); test_assert_idx(ret == i32tests[i].ret, i); if (ret == 0) test_assert_idx(val == i32tests[i].val, i); else test_assert_idx(val == 123456789, i); } test_end(); } static void test_str_is_float(void) { test_begin("str_is_float accepts integer"); /* accepts integer */ test_assert(str_is_float("0",'\0')); test_assert(str_is_float("1234",'\0')); test_end(); test_begin("str_is_float accepts float"); test_assert(str_is_float("0.0",'\0')); test_assert(str_is_float("1234.0",'\0')); test_assert(str_is_float("0.1234",'\0')); test_assert(str_is_float("1234.1234",'\0')); test_assert(str_is_float("0.1234 ",' ')); test_assert(str_is_float("1234.1234",'.')); test_end(); test_begin("str_is_float refuses invalid values"); test_assert(!str_is_float(".",'\0')); test_assert(!str_is_float(".1234",'\0')); test_assert(!str_is_float("1234.",'\0')); test_assert(!str_is_float("i am not a float at all",'\0')); test_assert(!str_is_float("0x1234.0x1234",'\0')); test_assert(!str_is_float(".0",'\0')); test_assert(!str_is_float("0.",'\0')); test_end(); } void test_strnum(void) { /* If the above isn't true, then we do expect some failures possibly */ test_str_to_uintmax(); test_str_to_uintmax_hex(); test_str_to_uintmax_oct(); test_str_to_u64(); test_str_to_u32(); test_str_to_llong(); test_str_to_i32(); test_str_is_float(); } dovecot-2.2.33.2/src/lib/ostream-null.c0000644000175000017500000000143413123174404014451 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream-private.h" #include "ostream-null.h" static ssize_t o_stream_null_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { unsigned int i; size_t ret = 0; for (i = 0; i < iov_count; i++) ret += iov[i].iov_len; stream->ostream.offset += ret; return ret; } struct ostream *o_stream_create_null(void) { struct ostream_private *stream; struct ostream *output; stream = i_new(struct ostream_private, 1); stream->ostream.blocking = TRUE; stream->sendv = o_stream_null_sendv; output = o_stream_create(stream, NULL, -1); o_stream_set_no_error_handling(output, TRUE); o_stream_set_name(output, "(/dev/null)"); return output; } dovecot-2.2.33.2/src/lib/unlink-directory.c0000644000175000017500000001137313165463624015347 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* There's a bit tricky race condition with recursive deletion. Suppose this happens: lstat(dir, ..) -> OK, it's a directory // attacker deletes dir, replaces it with symlink to / opendir(dir) -> it actually opens / Most portable solution is to lstat() the dir, chdir() there, then check that "." points to same device/inode as we originally lstat()ed. This assumes that the device has usable inodes, most should except for some NFS implementations. Filesystems may also reassign a deleted inode to another file immediately after it's deleted. That in theory makes it possible to exploit this race to delete the new directory. However, the new inode is quite unlikely to be any important directory, and attacker is quite unlikely to find out which directory even got the inode. Maybe with some setuid program or daemon interaction something could come out of it though. Another less portable solution is to fchdir(open(dir, O_NOFOLLOW)). This should be completely safe. The actual file deletion also has to be done relative to current directory, to make sure that the whole directory structure isn't replaced with another one while we're deleting it. Going back to parent directory isn't too easy either - safest (and easiest) way again is to open() the directory and fchdir() back there. */ #define _GNU_SOURCE /* for O_NOFOLLOW with Linux */ #include "lib.h" #include "unlink-directory.h" #include #include #include #include static int unlink_directory_r(const char *dir, enum unlink_directory_flags flags) { DIR *dirp; struct dirent *d; struct stat st; int dir_fd, old_errno; #ifdef O_NOFOLLOW dir_fd = open(dir, O_RDONLY | O_NOFOLLOW); if (dir_fd == -1) return -1; #else struct stat st2; if (lstat(dir, &st) < 0) return -1; if (!S_ISDIR(st.st_mode)) { if ((st.st_mode & S_IFMT) != S_IFLNK) errno = ENOTDIR; else { /* be compatible with O_NOFOLLOW */ errno = ELOOP; } return -1; } dir_fd = open(dir, O_RDONLY); if (dir_fd == -1) return -1; if (fstat(dir_fd, &st2) < 0) { i_close_fd(&dir_fd); return -1; } if (st.st_ino != st2.st_ino || !CMP_DEV_T(st.st_dev, st2.st_dev)) { /* directory was just replaced with something else. */ i_close_fd(&dir_fd); errno = ENOTDIR; return -1; } #endif if (fchdir(dir_fd) < 0) { i_close_fd(&dir_fd); return -1; } dirp = opendir("."); if (dirp == NULL) { i_close_fd(&dir_fd); return -1; } errno = 0; while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] == '.') { if ((d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { /* skip . and .. */ continue; } if ((flags & UNLINK_DIRECTORY_FLAG_SKIP_DOTFILES) != 0) continue; } if (unlink(d->d_name) < 0 && errno != ENOENT) { old_errno = errno; if (lstat(d->d_name, &st) < 0) { if (errno != ENOENT) break; errno = 0; } else if (S_ISDIR(st.st_mode) && (flags & UNLINK_DIRECTORY_FLAG_FILES_ONLY) == 0) { if (unlink_directory_r(d->d_name, flags) < 0) { if (errno != ENOENT) break; errno = 0; } if (fchdir(dir_fd) < 0) break; if (rmdir(d->d_name) < 0) { if (errno != ENOENT) { if (errno == EEXIST) { /* standardize errno */ errno = ENOTEMPTY; } break; } errno = 0; } } else if (S_ISDIR(st.st_mode) && (flags & UNLINK_DIRECTORY_FLAG_FILES_ONLY) != 0) { /* skip directory */ } else if (old_errno == EBUSY && strncmp(d->d_name, ".nfs", 4) == 0) { /* can't delete NFS files that are still in use. let the caller decide if this error is worth logging about */ break; } else { /* so it wasn't a directory */ errno = old_errno; i_error("unlink(%s/%s) failed: %m", dir, d->d_name); break; } } } old_errno = errno; i_close_fd(&dir_fd); if (closedir(dirp) < 0) return -1; if (old_errno != 0) { errno = old_errno; return -1; } return 0; } int unlink_directory(const char *dir, enum unlink_directory_flags flags) { int fd, ret, old_errno; fd = open(".", O_RDONLY); if (fd == -1) return -1; ret = unlink_directory_r(dir, flags); if (ret < 0 && errno == ENOENT) ret = 0; old_errno = errno; if (fchdir(fd) < 0) { i_fatal("unlink_directory(%s): " "Can't fchdir() back to our original dir: %m", dir); } i_close_fd(&fd); if (ret < 0) { errno = old_errno; return -1; } if ((flags & UNLINK_DIRECTORY_FLAG_RMDIR) != 0) { if (rmdir(dir) < 0 && errno != ENOENT) { if (errno == EEXIST) { /* standardize errno */ errno = ENOTEMPTY; } return -1; } } return 0; } dovecot-2.2.33.2/src/lib/ostream-buffer.c0000644000175000017500000000431413147010712014745 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "ostream-private.h" struct buffer_ostream { struct ostream_private ostream; buffer_t *buf; bool seeked; }; static int o_stream_buffer_seek(struct ostream_private *stream, uoff_t offset) { struct buffer_ostream *bstream = (struct buffer_ostream *)stream; bstream->seeked = TRUE; stream->ostream.offset = offset; return 1; } static int o_stream_buffer_write_at(struct ostream_private *stream, const void *data, size_t size, uoff_t offset) { struct buffer_ostream *bstream = (struct buffer_ostream *)stream; buffer_write(bstream->buf, offset, data, size); return 0; } static ssize_t o_stream_buffer_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct buffer_ostream *bstream = (struct buffer_ostream *)stream; size_t left, n, offset; ssize_t ret = 0; unsigned int i; offset = bstream->seeked ? stream->ostream.offset : bstream->buf->used; for (i = 0; i < iov_count; i++) { left = bstream->ostream.max_buffer_size - stream->ostream.offset; n = I_MIN(left, iov[i].iov_len); buffer_write(bstream->buf, offset, iov[i].iov_base, n); stream->ostream.offset += n; offset += n; ret += n; if (n != iov[i].iov_len) break; } return ret; } static size_t o_stream_buffer_get_used_size(const struct ostream_private *stream) { struct buffer_ostream *bstream = (struct buffer_ostream *)stream; return bstream->buf->used; } struct ostream *o_stream_create_buffer(buffer_t *buf) { struct buffer_ostream *bstream; struct ostream *output; bstream = i_new(struct buffer_ostream, 1); /* we don't set buffer as blocking, because if max_buffer_size is changed it can get truncated. this is used in various places in unit tests. */ bstream->ostream.max_buffer_size = (size_t)-1; bstream->ostream.seek = o_stream_buffer_seek; bstream->ostream.sendv = o_stream_buffer_sendv; bstream->ostream.write_at = o_stream_buffer_write_at; bstream->ostream.get_used_size = o_stream_buffer_get_used_size; bstream->buf = buf; output = o_stream_create(&bstream->ostream, NULL, -1); o_stream_set_name(output, "(buffer)"); return output; } dovecot-2.2.33.2/src/lib/var-expand-if.c0000644000175000017500000001342513165463624014506 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "var-expand-private.h" #include "wildcard-match.h" #include enum var_expand_if_op { OP_UNKNOWN, OP_NUM_EQ, OP_NUM_LT, OP_NUM_LE, OP_NUM_GT, OP_NUM_GE, OP_NUM_NE, /* put all numeric comparisons before this line */ OP_STR_EQ, OP_STR_LT, OP_STR_LE, OP_STR_GT, OP_STR_GE, OP_STR_NE, OP_STR_LIKE, OP_STR_NOT_LIKE, OP_STR_REGEXP, OP_STR_NOT_REGEXP, /* keep this as last */ OP_COUNT }; static enum var_expand_if_op var_expand_if_str_to_comp(const char *op) { const char *ops[OP_COUNT] = { NULL, "==", "<", "<=", ">", ">=", "!=", "eq", "lt", "le", "gt", "ge", "ne", "*", "!*", "~", "!~", }; for(enum var_expand_if_op i = 1; i < OP_COUNT; i++) { i_assert(ops[i] != NULL); if (strcmp(op, ops[i]) == 0) return i; } return OP_UNKNOWN; } static int var_expand_if_comp(const char *lhs, const char *_op, const char *rhs, bool *result_r, const char **error_r) { bool neg = FALSE; enum var_expand_if_op op = var_expand_if_str_to_comp(_op); *result_r = FALSE; if (op == OP_UNKNOWN) { *error_r = t_strdup_printf("if: Unsupported comparator '%s'", _op); return -1; } if (op < OP_STR_EQ) { intmax_t a; intmax_t b; if (str_to_intmax(lhs, &a) < 0) { *error_r = t_strdup_printf("if: %s (lhs) is not a number", lhs); return -1; } if (str_to_intmax(rhs, &b) < 0) { *error_r = t_strdup_printf("if: %s (rhs) is not a number", rhs); return -1; } switch(op) { case OP_NUM_EQ: *result_r = a==b; return 0; case OP_NUM_LT: *result_r = ab; return 0; case OP_NUM_GE: *result_r = a>=b; return 0; case OP_NUM_NE: *result_r = a!=b; return 0; default: i_panic("Missing numeric comparator %u", op); } } switch(op) { case OP_STR_EQ: *result_r = strcmp(lhs,rhs)==0; return 0; case OP_STR_LT: *result_r = strcmp(lhs,rhs)<0; return 0; case OP_STR_LE: *result_r = strcmp(lhs,rhs)<=0; return 0; case OP_STR_GT: *result_r = strcmp(lhs,rhs)>0; return 0; case OP_STR_GE: *result_r = strcmp(lhs,rhs)>=0; return 0; case OP_STR_NE: *result_r = strcmp(lhs,rhs)!=0; return 0; case OP_STR_LIKE: *result_r = wildcard_match(lhs, rhs); return 0; case OP_STR_NOT_LIKE: *result_r = !wildcard_match(lhs, rhs); return 0; case OP_STR_NOT_REGEXP: neg = TRUE; /* fall through */ case OP_STR_REGEXP: { int ec; bool res; regex_t reg; if ((ec = regcomp(®, rhs, REG_EXTENDED)) != 0) { size_t siz; char *errbuf; siz = regerror(ec, ®, NULL, 0); errbuf = t_malloc(siz); (void)regerror(ec, ®, errbuf, siz); i_error("if: regex failed: %s", errbuf); return -1; } if ((ec = regexec(®, lhs, 0, 0, 0)) != 0) { i_assert(ec == REG_NOMATCH); res = FALSE; } else { res = TRUE; } regfree(®); /* this should be same as neg. if NOT_REGEXP, neg == TRUE and res should be FALSE if REGEXP, ned == FALSE, and res should be TRUE */ *result_r = res != neg; return 0; } default: i_panic("Missing generic comparator %u", op); } } int var_expand_if(struct var_expand_context *ctx, const char *key, const char *field, const char **result_r, const char **error_r) { /* in case the original input had :, we need to fix that by concatenating the key and field together. */ const char *input = t_strconcat(key, ":", field, NULL); const char *p = strchr(input, ';'); const char *par_end; string_t *parbuf; const char *const *parms; unsigned int depth = 0; bool result, escape = FALSE, maybe_var = FALSE; if (p == NULL) { *error_r = "if: missing parameter(s)"; return -1; } ARRAY_TYPE(const_string) params; t_array_init(¶ms, 6); parbuf = t_str_new(64); /* we need to skip any %{} parameters here, so we can split the string correctly from , without breaking any inner expansions */ for(par_end = p+1; *par_end != '\0'; par_end++) { if (*par_end == '\\') { escape = TRUE; continue; } else if (escape) { str_append_c(parbuf, *par_end); escape = FALSE; continue; } if (*par_end == '%') { maybe_var = TRUE; } else if (maybe_var && *par_end == '{') { depth++; maybe_var = FALSE; } else if (depth > 0 && *par_end == '}') { depth--; } else if (depth == 0 && *par_end == ';') { const char *par = str_c(parbuf); array_append(¶ms, &par, 1); parbuf = t_str_new(64); continue; /* if there is a unescaped : at top level it means that the key + arguments end here. it's probably a by-product of the t_strconcat at top of function, which is best handled here. */ } else if (depth == 0 && *par_end == ':') { break; } str_append_c(parbuf, *par_end); } if (str_len(parbuf) > 0) { const char *par = str_c(parbuf); array_append(¶ms, &par, 1); } if (array_count(¶ms) != 5) { if (array_count(¶ms) == 4) { const char *empty = ""; array_append(¶ms, &empty, 1); } else { *error_r = t_strdup_printf("if: requires four or five parameters, got %u", array_count(¶ms)); return -1; } } array_append_zero(¶ms); parms = array_idx(¶ms, 0); t_array_init(¶ms, 6); for(;*parms != NULL; parms++) { /* expand the parameters */ string_t *param = t_str_new(64); var_expand_with_funcs(param, *parms, ctx->table, ctx->func_table, ctx->context); const char *p = str_c(param); array_append(¶ms, &p, 1); } i_assert(array_count(¶ms) == 5); /* execute comparison */ const char *const *args = array_idx(¶ms, 0); if (var_expand_if_comp(args[0], args[1], args[2], &result, error_r)<0) return -1; *result_r = result ? args[3] : args[4]; return 1; } dovecot-2.2.33.2/src/lib/fd-close-on-exec.h0000644000175000017500000000037113123174404015063 00000000000000#ifndef FD_CLOSE_ON_EXEC_H #define FD_CLOSE_ON_EXEC_H /* Change close-on-exec flag of fd. */ void fd_close_on_exec(int fd, bool set); /* Verify that fds in given range don't exist. */ void fd_debug_verify_leaks(int first_fd, int last_fd); #endif dovecot-2.2.33.2/src/lib/hash-method.c0000644000175000017500000000306613165463624014246 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "md4.h" #include "md5.h" #include "sha1.h" #include "sha2.h" #include "sha3.h" #include "hash-method.h" const struct hash_method *hash_method_lookup(const char *name) { unsigned int i; for (i = 0; hash_methods[i] != NULL; i++) { if (strcmp(hash_methods[i]->name, name) == 0) return hash_methods[i]; } return NULL; } static void hash_method_init_size(void *context) { uint64_t *ctx = context; *ctx = 0; } static void hash_method_loop_size(void *context, const void *data ATTR_UNUSED, size_t size) { uint64_t *ctx = context; *ctx += size; } static void hash_method_result_size(void *context, unsigned char *result_r) { uint64_t *ctx = context; result_r[0] = (*ctx & 0xff00000000000000ULL) >> 56; result_r[1] = (*ctx & 0x00ff000000000000ULL) >> 48; result_r[2] = (*ctx & 0x0000ff0000000000ULL) >> 40; result_r[3] = (*ctx & 0x000000ff00000000ULL) >> 32; result_r[4] = (*ctx & 0x00000000ff000000ULL) >> 24; result_r[5] = (*ctx & 0x0000000000ff0000ULL) >> 16; result_r[6] = (*ctx & 0x000000000000ff00ULL) >> 8; result_r[7] = (*ctx & 0x00000000000000ffULL); } static const struct hash_method hash_method_size = { "size", sizeof(uint64_t), sizeof(uint64_t), hash_method_init_size, hash_method_loop_size, hash_method_result_size }; const struct hash_method *hash_methods[] = { &hash_method_md4, &hash_method_md5, &hash_method_sha1, &hash_method_sha256, &hash_method_sha512, &hash_method_sha3_256, &hash_method_sha3_512, &hash_method_size, NULL }; dovecot-2.2.33.2/src/lib/hash-method.h0000644000175000017500000000111413165463624014243 00000000000000#ifndef HASH_METHOD_H #define HASH_METHOD_H struct hash_method { const char *name; /* Number of bytes that must be allocated for context */ unsigned int context_size; /* Number of bytes that must be allocated for result()'s digest */ unsigned int digest_size; void (*init)(void *context); void (*loop)(void *context, const void *data, size_t size); void (*result)(void *context, unsigned char *digest_r); }; const struct hash_method *hash_method_lookup(const char *name); /* NULL-terminated list of all hash methods */ extern const struct hash_method *hash_methods[]; #endif dovecot-2.2.33.2/src/lib/strescape.h0000644000175000017500000000232413123174404014024 00000000000000#ifndef STRESCAPE_H #define STRESCAPE_H #define IS_ESCAPED_CHAR(c) ((c) == '"' || (c) == '\\' || (c) == '\'') /* escape all '\', '"' and "'" characters */ const char *str_escape(const char *str); /* remove all '\' characters, append to given string */ void str_append_unescaped(string_t *dest, const void *src, size_t src_size); /* remove all '\' characters */ char *str_unescape(char *str); /* Remove all '\' chars from str until '"' is reached and return the unescaped string. *str is updated to point to the character after the '"'. Returns 0 if ok, -1 if '"' wasn't found. */ int str_unescape_next(const char **str, const char **unescaped_r); /* For Dovecot's internal protocols: Escape \001, \t, \r and \n characters using \001. */ const char *str_tabescape(const char *str); void str_append_tabescaped(string_t *dest, const char *src); void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size); void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size); char *str_tabunescape(char *str); const char *t_str_tabunescape(const char *str); char **p_strsplit_tabescaped(pool_t pool, const char *str); const char *const *t_strsplit_tabescaped(const char *str); #endif dovecot-2.2.33.2/src/lib/hex-binary.c0000644000175000017500000000351713165463624014114 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" static void binary_to_hex_case(unsigned char *dest, const unsigned char *data, size_t size, bool ucase) { unsigned char *p; char base_char; size_t i; int value; /* @UNSAFE */ base_char = ucase ? 'A' : 'a'; p = dest; for (i = 0; i < size; i++) { value = data[i] >> 4; *p++ = value < 10 ? value + '0' : value - 10 + base_char; value = data[i] & 0x0f; *p++ = value < 10 ? value + '0' : value - 10 + base_char; } } const char *binary_to_hex(const unsigned char *data, size_t size) { unsigned char *dest = t_malloc(MALLOC_MULTIPLY(size, 2) + 1); binary_to_hex_case(dest, data, size, FALSE); dest[size*2] = '\0'; return (char *)dest; } const char *binary_to_hex_ucase(const unsigned char *data, size_t size) { unsigned char *dest = t_malloc(MALLOC_MULTIPLY(size, 2) + 1); binary_to_hex_case(dest, data, size, TRUE); dest[size*2] = '\0'; return (char *)dest; } void binary_to_hex_append(string_t *dest, const unsigned char *data, size_t size) { unsigned char *buf; buf = buffer_append_space_unsafe(dest, size * 2); binary_to_hex_case(buf, data, size, FALSE); } int hex_to_binary(const char *data, buffer_t *dest) { int value; while (*data != '\0') { if (*data >= '0' && *data <= '9') value = (*data - '0') << 4; else if (*data >= 'a' && *data <= 'f') value = (*data - 'a' + 10) << 4; else if (*data >= 'A' && *data <= 'F') value = (*data - 'A' + 10) << 4; else return -1; data++; if (*data >= '0' && *data <= '9') value |= *data - '0'; else if (*data >= 'a' && *data <= 'f') value |= *data - 'a' + 10; else if (*data >= 'A' && *data <= 'F') value |= *data - 'A' + 10; else return -1; buffer_append_c(dest, value); data++; } return 0; } dovecot-2.2.33.2/src/lib/test-bits.c0000644000175000017500000000612013165463624013755 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ /* Unit tests for bit twiddles library */ #include "test-lib.h" #include /* nearest_power(0) = error bits_requiredXX(0) = 0 nearest_power(1) = 1 = 1<<0 bits_requiredXX(1) = 1 nearest_power(2) = 2 = 1<<1 bits_requiredXX(2) = 2 nearest_power(3) = 4 = 1<<2 bits_requiredXX(3) = 2 nearest_power(4) = 4 = 1<<2 bits_requiredXX(4) = 3 nearest_power(5) = 8 = 1<<3 bits_requiredXX(5) = 3 nearest_power(7) = 8 = 1<<3 bits_requiredXX(7) = 3 nearest_power(8) = 8 = 1<<3 bits_requiredXX(8) = 4 */ /* nearest_power(num) == 1ULL << bits_required64(num-1) */ static void test_nearest_power(void) { unsigned int b; size_t num; test_begin("nearest_power()"); test_assert(nearest_power(1)==1); test_assert(nearest_power(2)==2); for (b = 2; b < CHAR_BIT*sizeof(size_t) - 1; ++b) { /* b=2 tests 3,4,5; b=3 tests 7,8,9; ... b=30 tests ~1G */ num = (size_t)1 << b; test_assert_idx(nearest_power(num-1) == num, b); test_assert_idx(nearest_power(num ) == num, b); test_assert_idx(nearest_power(num+1) == num<<1, b); } /* With 32-bit size_t, now: b=31 tests 2G-1, 2G, not 2G+1. */ num = (size_t)1 << b; test_assert_idx(nearest_power(num-1) == num, b); test_assert_idx(nearest_power(num ) == num, b); /* i_assert()s: test_assert_idx(nearest_power(num+1) == num<<1, b); */ test_end(); } static void test_bits_is_power_of_two(void) { test_begin("bits_is_power_of_two()"); for (unsigned int i = 0; i < 64; i++) test_assert_idx(bits_is_power_of_two(1ULL << i), i); for (unsigned int i = 2; i < 64; i++) { test_assert_idx(!bits_is_power_of_two((1ULL << i) - 1), i); test_assert_idx(!bits_is_power_of_two((1ULL << i) + 1), i); } test_assert(!bits_is_power_of_two(0)); test_assert(!bits_is_power_of_two(0xffffffffffffffffULL)); test_end(); } static void test_bits_requiredXX(void) { /* As ..64 depends on ..32 and tests it twice, * and ..32 depends on ..16 and tests it twice, * etc., we only test ..64 */ unsigned int b; test_begin("bits_requiredXX()"); test_assert(bits_required64(0) == 0); test_assert(bits_required64(1) == 1); test_assert(bits_required64(2) == 2); for (b = 2; b < 64; ++b) { /* b=2 tests 3,4,5; b=3 tests 7,8,9; ... */ uint64_t num = 1ULL << b; test_assert_idx(bits_required64(num-1) == b, b); test_assert_idx(bits_required64(num ) == b+1, b); test_assert_idx(bits_required64(num+1) == b+1, b); } test_end(); } static void test_sum_overflows(void) { #define MAX64 (uint64_t)-1 static const struct { uint64_t a, b; bool overflows; } tests[] = { { MAX64-1, 1, FALSE }, { MAX64, 1, TRUE }, { MAX64-1, 1, FALSE }, { MAX64-1, 2, TRUE }, { MAX64-1, MAX64-1, TRUE }, { MAX64-1, MAX64, TRUE }, { MAX64, MAX64, TRUE } }; unsigned int i; test_begin("UINT64_SUM_OVERFLOWS"); for (i = 0; i < N_ELEMENTS(tests); i++) test_assert(UINT64_SUM_OVERFLOWS(tests[i].a, tests[i].b) == tests[i].overflows); test_end(); } void test_bits() { test_nearest_power(); test_bits_is_power_of_two(); test_bits_requiredXX(); test_sum_overflows(); } dovecot-2.2.33.2/src/lib/str.h0000644000175000017500000000377213123174404012653 00000000000000#ifndef STR_H #define STR_H #include "buffer.h" string_t *str_new(pool_t pool, size_t initial_size); string_t *t_str_new(size_t initial_size); /* Allocate a constant string using the given str as the input data. str pointer is saved directly, so it must not be freed until the returned string is no longer used. len must contain strlen(str). */ string_t *str_new_const(pool_t pool, const char *str, size_t len); string_t *t_str_new_const(const char *str, size_t len); void str_free(string_t **str); char *str_free_without_data(string_t **str); const char *str_c(string_t *str); char *str_c_modifiable(string_t *str); bool str_equals(const string_t *str1, const string_t *str2) ATTR_PURE; static inline const unsigned char *str_data(const string_t *str) { return str->data; } static inline size_t str_len(const string_t *str) { return str->used; } /* Append string/character */ void str_append_n(string_t *str, const void *cstr, size_t max_len); static inline void str_append(string_t *str, const char *cstr) { buffer_append(str, cstr, strlen(cstr)); } static inline void str_append_data(string_t *str, const void *data, size_t len) { buffer_append(str, data, len); } static inline void str_append_c(string_t *str, unsigned char chr) { buffer_append_c(str, chr); } static inline void str_append_str(string_t *dest, const string_t *src) { buffer_append(dest, src->data, src->used); } /* Append printf()-like data */ void str_printfa(string_t *str, const char *fmt, ...) ATTR_FORMAT(2, 3); void str_vprintfa(string_t *str, const char *fmt, va_list args) ATTR_FORMAT(2, 0); static inline void str_insert(string_t *str, size_t pos, const char *cstr) { buffer_insert(str, pos, cstr, strlen(cstr)); } static inline void str_delete(string_t *str, size_t pos, size_t len) { buffer_delete(str, pos, len); } /* Truncate the string to specified length. If it's already smaller, do nothing. */ static inline void str_truncate(string_t *str, size_t len) { if (str_len(str) > len) buffer_set_used_size(str, len); } #endif dovecot-2.2.33.2/src/lib/test-str-table.c0000644000175000017500000000146413123174404014704 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str-table.h" void test_str_table(void) { struct str_table *table; const char *key1, *key2, *key1_copy, *key2_copy; test_begin("str_table"); table = str_table_init(); key1 = str_table_ref(table, "str1"); key2 = str_table_ref(table, "str2"); test_assert(key1 != key2); key1_copy = str_table_ref(table, "str1"); test_assert(key1_copy == key1); key2_copy = str_table_ref(table, "str2"); test_assert(key2_copy == key2); str_table_unref(table, &key1); test_assert(key1 == NULL); str_table_unref(table, &key1_copy); str_table_unref(table, &key2); str_table_unref(table, &key2_copy); test_assert(str_table_is_empty(table)); str_table_deinit(&table); test_assert(table == NULL); test_end(); } dovecot-2.2.33.2/src/lib/ioloop-epoll.c0000644000175000017500000001332013123174404014436 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "fd-close-on-exec.h" #include "ioloop-private.h" #include "ioloop-iolist.h" #ifdef IOLOOP_EPOLL #include #include struct ioloop_handler_context { int epfd; unsigned int deleted_count; ARRAY(struct io_list *) fd_index; ARRAY(struct epoll_event) events; }; void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) { struct ioloop_handler_context *ctx; ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); i_array_init(&ctx->events, initial_fd_count); i_array_init(&ctx->fd_index, initial_fd_count); ctx->epfd = epoll_create(initial_fd_count); if (ctx->epfd < 0) { if (errno != EMFILE) i_fatal("epoll_create(): %m"); else { i_fatal("epoll_create(): %m (you may need to increase " "/proc/sys/fs/epoll/max_user_instances)"); } } fd_close_on_exec(ctx->epfd, TRUE); } void io_loop_handler_deinit(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct io_list **list; unsigned int i, count; list = array_get_modifiable(&ctx->fd_index, &count); for (i = 0; i < count; i++) i_free(list[i]); if (close(ctx->epfd) < 0) i_error("close(epoll) failed: %m"); array_free(&ioloop->handler_context->fd_index); array_free(&ioloop->handler_context->events); i_free(ioloop->handler_context); } #define IO_EPOLL_ERROR (EPOLLERR | EPOLLHUP) #define IO_EPOLL_INPUT (EPOLLIN | EPOLLPRI | IO_EPOLL_ERROR) #define IO_EPOLL_OUTPUT (EPOLLOUT | IO_EPOLL_ERROR) static int epoll_event_mask(struct io_list *list) { int events = 0, i; struct io_file *io; for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { io = list->ios[i]; if (io == NULL) continue; if ((io->io.condition & IO_READ) != 0) events |= IO_EPOLL_INPUT; if ((io->io.condition & IO_WRITE) != 0) events |= IO_EPOLL_OUTPUT; if ((io->io.condition & IO_ERROR) != 0) events |= IO_EPOLL_ERROR; } return events; } void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct io_list **list; struct epoll_event event; int op; bool first; list = array_idx_modifiable(&ctx->fd_index, io->fd); if (*list == NULL) *list = i_new(struct io_list, 1); first = ioloop_iolist_add(*list, io); i_zero(&event); event.data.ptr = *list; event.events = epoll_event_mask(*list); op = first ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) { if (errno == EPERM && op == EPOLL_CTL_ADD) { i_panic("epoll_ctl(add, %d) failed: %m " "(fd doesn't support epoll%s)", io->fd, io->fd != STDIN_FILENO ? "" : " - instead of 'fd); } if (first) { /* allow epoll_wait() to return the maximum number of events by keeping space allocated for each file descriptor */ if (ctx->deleted_count > 0) ctx->deleted_count--; else array_append_zero(&ctx->events); } } void io_loop_handle_remove(struct io_file *io, bool closed) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct io_list **list; struct epoll_event event; int op; bool last; list = array_idx_modifiable(&ctx->fd_index, io->fd); last = ioloop_iolist_del(*list, io); if (!closed) { i_zero(&event); event.data.ptr = *list; event.events = epoll_event_mask(*list); op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) { const char *errstr = t_strdup_printf( "epoll_ctl(%s, %d) failed: %m", op == EPOLL_CTL_DEL ? "del" : "mod", io->fd); if (errno == EBADF) i_panic("%s", errstr); else i_error("%s", errstr); } } if (last) { /* since we're not freeing memory in any case, just increase deleted counter so next handle_add() can just decrease it insteading of appending to the events array */ ctx->deleted_count++; } i_free(io); } void io_loop_handler_run_internal(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct epoll_event *events; const struct epoll_event *event; struct io_list *list; struct io_file *io; struct timeval tv; unsigned int events_count; int msecs, ret, i, j; bool call; i_assert(ctx != NULL); /* get the time left for next timeout task */ msecs = io_loop_get_wait_time(ioloop, &tv); events = array_get_modifiable(&ctx->events, &events_count); if (ioloop->io_files != NULL && events_count > ctx->deleted_count) { ret = epoll_wait(ctx->epfd, events, events_count, msecs); if (ret < 0 && errno != EINTR) i_fatal("epoll_wait(): %m"); } else { /* no I/Os, but we should have some timeouts. just wait for them. */ if (msecs < 0) i_panic("BUG: No IOs or timeouts set. Not waiting for infinity."); usleep(msecs*1000); ret = 0; } /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); if (!ioloop->running) return; for (i = 0; i < ret; i++) { /* io_loop_handle_add() may cause events array reallocation, so we have use array_idx() */ event = array_idx(&ctx->events, i); list = event->data.ptr; for (j = 0; j < IOLOOP_IOLIST_IOS_PER_FD; j++) { io = list->ios[j]; if (io == NULL) continue; call = FALSE; if ((event->events & (EPOLLHUP | EPOLLERR)) != 0) call = TRUE; else if ((io->io.condition & IO_READ) != 0) call = (event->events & EPOLLIN) != 0; else if ((io->io.condition & IO_WRITE) != 0) call = (event->events & EPOLLOUT) != 0; else if ((io->io.condition & IO_ERROR) != 0) call = (event->events & IO_EPOLL_ERROR) != 0; if (call) io_loop_call_io(&io->io); } } } #endif /* IOLOOP_EPOLL */ dovecot-2.2.33.2/src/lib/test-istream-concat.c0000644000175000017500000001137213165463624015732 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "istream-private.h" #include "istream-concat.h" #include #include #define TEST_MAX_ISTREAM_COUNT 10 #define TEST_MAX_ISTREAM_SIZE 1024 #define TEST_MAX_BUFFER_SIZE 128 static void test_istream_concat_one(unsigned int buffer_size) { static const char *input_string = "xyz"; #define STREAM_COUNT 5 #define STREAM_BYTES 3 struct istream *streams[STREAM_COUNT+1]; struct istream *input; const unsigned char *data; size_t size; unsigned int i, j; for (i = 0; i < STREAM_COUNT; i++) { streams[i] = test_istream_create(input_string); test_istream_set_allow_eof(streams[i], TRUE); test_istream_set_size(streams[i], 0); } streams[i] = NULL; input = i_stream_create_concat(streams); for (i = 0; i/STREAM_BYTES < STREAM_COUNT; i++) { test_istream_set_size(streams[i/STREAM_BYTES], (i%STREAM_BYTES) + 1); test_assert(i_stream_read(input) == 1); if (i < buffer_size) { data = i_stream_get_data(input, &size); test_assert(size == i+1); } else { i_stream_skip(input, 1); data = i_stream_get_data(input, &size); test_assert(size == buffer_size); } for (j = 0; j < size; j++) { test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]); } } test_assert(i_stream_read(input) == -1); i_stream_skip(input, i_stream_get_data_size(input)); i_stream_unref(&input); for (i = 0; i < STREAM_COUNT; i++) { test_assert(i_stream_is_eof(streams[i])); i_stream_unref(&streams[i]); } } static bool test_istream_concat_random(void) { struct istream **streams, *concat, **limits = NULL; const unsigned char *data; unsigned char *w_data; size_t size = 0; unsigned int i, j, offset, stream_count, data_len, simult; stream_count = (rand() % TEST_MAX_ISTREAM_COUNT) + 2; streams = t_new(struct istream *, stream_count + 1); for (i = 0, offset = 0; i < stream_count; i++) { data_len = rand() % TEST_MAX_ISTREAM_SIZE + 1; w_data = t_malloc(data_len); for (j = 0; j < data_len; j++) w_data[j] = offset++; streams[i] = test_istream_create_data(w_data, data_len); test_istream_set_allow_eof(streams[i], TRUE); } streams[i] = NULL; i_assert(offset > 0); concat = i_stream_create_concat(streams); i_stream_set_max_buffer_size(concat, TEST_MAX_BUFFER_SIZE); simult = rand() % TEST_MAX_ISTREAM_COUNT; if (simult > 0) { limits = t_new(struct istream *, simult); for (i = 0; i < simult; i++) limits[i] = i_stream_create_limit(concat, (uoff_t)-1); } for (i = 0; i < 1000; i++) { struct istream *input = (simult == 0) ? concat : limits[rand() % simult]; if (rand() % 3 == 0) { i_stream_seek(input, rand() % offset); } else { ssize_t ret = i_stream_read(input); size = i_stream_get_data_size(input); if (ret == -2) { test_assert(size >= TEST_MAX_BUFFER_SIZE); } else if (input->v_offset + size != offset) { test_assert(ret > 0); test_assert(input->v_offset + ret <= offset); i_stream_skip(input, rand() % ret); data = i_stream_get_data(input, &size); for (j = 0; j < size; j++) { test_assert(data[j] == (input->v_offset + j) % 256); } } } if (test_has_failed()) break; } for (i = 0; i < stream_count; i++) i_stream_unref(&streams[i]); for (i = 0; i < simult; i++) i_stream_unref(&limits[i]); i_stream_unref(&concat); return !test_has_failed(); } static void test_istream_concat_seek_end(void) { test_begin("istream concat seek end"); struct istream *streams[] = { test_istream_create("s1"), test_istream_create("s2"), NULL }; struct istream *input = i_stream_create_concat(streams); i_stream_unref(&streams[0]); i_stream_unref(&streams[1]); i_stream_seek(input, 4); test_assert(i_stream_read(input) == -1); i_stream_unref(&input); test_end(); } static void test_istream_concat_early_end(void) { struct istream *input, *streams[2]; test_begin("istream concat early end"); streams[0] = test_istream_create("stream"); test_istream_set_size(streams[0], 3); test_istream_set_allow_eof(streams[0], FALSE); streams[1] = NULL; input = i_stream_create_concat(streams); test_assert(i_stream_read(input) == 3); test_istream_set_size(streams[0], 5); test_assert(i_stream_read(input) == 2); i_stream_skip(input, 5); i_stream_unref(&input); test_assert(streams[0]->v_offset == 5); i_stream_unref(&streams[0]); test_end(); } void test_istream_concat(void) { unsigned int i; test_begin("istream concat"); for (i = 1; i < STREAM_BYTES*STREAM_COUNT; i++) { test_istream_concat_one(i); } test_end(); test_begin("istream concat random"); for (i = 0; i < 100; i++) T_BEGIN { if(!test_istream_concat_random()) i = 101; /* don't break a T_BEGIN */ } T_END; test_end(); test_istream_concat_seek_end(); test_istream_concat_early_end(); } dovecot-2.2.33.2/src/lib/unicodemap.c0000644000175000017500000053503313144653671014175 00000000000000/* This file is automatically generated by unicodemap.pl from UnicodeData.txt NOTE: decompositions for characters having titlecase characters are not included, because we first translate everything to titlecase */ static const uint16_t titlecase8_map[256] = { 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f, 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017, 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f, 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027, 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f, 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f, 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087, 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f, 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097, 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f, 0x000a0, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7, 0x000a8, 0x000a9, 0x000aa, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af, 0x000b0, 0x000b1, 0x000b2, 0x000b3, 0x000b4, 0x0039c, 0x000b6, 0x000b7, 0x000b8, 0x000b9, 0x000ba, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d7, 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x000df, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000f7, 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x00178 }; static const uint16_t titlecase16_keys[] = { 0x00101, 0x00103, 0x00105, 0x00107, 0x00109, 0x0010b, 0x0010d, 0x0010f, 0x00111, 0x00113, 0x00115, 0x00117, 0x00119, 0x0011b, 0x0011d, 0x0011f, 0x00121, 0x00123, 0x00125, 0x00127, 0x00129, 0x0012b, 0x0012d, 0x0012f, 0x00131, 0x00133, 0x00135, 0x00137, 0x0013a, 0x0013c, 0x0013e, 0x00140, 0x00142, 0x00144, 0x00146, 0x00148, 0x0014b, 0x0014d, 0x0014f, 0x00151, 0x00153, 0x00155, 0x00157, 0x00159, 0x0015b, 0x0015d, 0x0015f, 0x00161, 0x00163, 0x00165, 0x00167, 0x00169, 0x0016b, 0x0016d, 0x0016f, 0x00171, 0x00173, 0x00175, 0x00177, 0x0017a, 0x0017c, 0x0017e, 0x0017f, 0x00180, 0x00183, 0x00185, 0x00188, 0x0018c, 0x00192, 0x00195, 0x00199, 0x0019a, 0x0019e, 0x001a1, 0x001a3, 0x001a5, 0x001a8, 0x001ad, 0x001b0, 0x001b4, 0x001b6, 0x001b9, 0x001bd, 0x001bf, 0x001c4, 0x001c6, 0x001c7, 0x001c9, 0x001ca, 0x001cc, 0x001ce, 0x001d0, 0x001d2, 0x001d4, 0x001d6, 0x001d8, 0x001da, 0x001dc, 0x001dd, 0x001df, 0x001e1, 0x001e3, 0x001e5, 0x001e7, 0x001e9, 0x001eb, 0x001ed, 0x001ef, 0x001f1, 0x001f3, 0x001f5, 0x001f9, 0x001fb, 0x001fd, 0x001ff, 0x00201, 0x00203, 0x00205, 0x00207, 0x00209, 0x0020b, 0x0020d, 0x0020f, 0x00211, 0x00213, 0x00215, 0x00217, 0x00219, 0x0021b, 0x0021d, 0x0021f, 0x00223, 0x00225, 0x00227, 0x00229, 0x0022b, 0x0022d, 0x0022f, 0x00231, 0x00233, 0x0023c, 0x0023f, 0x00240, 0x00242, 0x00247, 0x00249, 0x0024b, 0x0024d, 0x0024f, 0x00250, 0x00251, 0x00252, 0x00253, 0x00254, 0x00256, 0x00257, 0x00259, 0x0025b, 0x0025c, 0x00260, 0x00261, 0x00263, 0x00265, 0x00266, 0x00268, 0x00269, 0x0026a, 0x0026b, 0x0026c, 0x0026f, 0x00271, 0x00272, 0x00275, 0x0027d, 0x00280, 0x00283, 0x00287, 0x00288, 0x00289, 0x0028a, 0x0028b, 0x0028c, 0x00292, 0x0029d, 0x0029e, 0x00345, 0x00371, 0x00373, 0x00377, 0x0037b, 0x0037c, 0x0037d, 0x003ac, 0x003ad, 0x003ae, 0x003af, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x003ca, 0x003cb, 0x003cc, 0x003cd, 0x003ce, 0x003d0, 0x003d1, 0x003d5, 0x003d6, 0x003d7, 0x003d9, 0x003db, 0x003dd, 0x003df, 0x003e1, 0x003e3, 0x003e5, 0x003e7, 0x003e9, 0x003eb, 0x003ed, 0x003ef, 0x003f0, 0x003f1, 0x003f2, 0x003f3, 0x003f5, 0x003f8, 0x003fb, 0x00430, 0x00431, 0x00432, 0x00433, 0x00434, 0x00435, 0x00436, 0x00437, 0x00438, 0x00439, 0x0043a, 0x0043b, 0x0043c, 0x0043d, 0x0043e, 0x0043f, 0x00440, 0x00441, 0x00442, 0x00443, 0x00444, 0x00445, 0x00446, 0x00447, 0x00448, 0x00449, 0x0044a, 0x0044b, 0x0044c, 0x0044d, 0x0044e, 0x0044f, 0x00450, 0x00451, 0x00452, 0x00453, 0x00454, 0x00455, 0x00456, 0x00457, 0x00458, 0x00459, 0x0045a, 0x0045b, 0x0045c, 0x0045d, 0x0045e, 0x0045f, 0x00461, 0x00463, 0x00465, 0x00467, 0x00469, 0x0046b, 0x0046d, 0x0046f, 0x00471, 0x00473, 0x00475, 0x00477, 0x00479, 0x0047b, 0x0047d, 0x0047f, 0x00481, 0x0048b, 0x0048d, 0x0048f, 0x00491, 0x00493, 0x00495, 0x00497, 0x00499, 0x0049b, 0x0049d, 0x0049f, 0x004a1, 0x004a3, 0x004a5, 0x004a7, 0x004a9, 0x004ab, 0x004ad, 0x004af, 0x004b1, 0x004b3, 0x004b5, 0x004b7, 0x004b9, 0x004bb, 0x004bd, 0x004bf, 0x004c2, 0x004c4, 0x004c6, 0x004c8, 0x004ca, 0x004cc, 0x004ce, 0x004cf, 0x004d1, 0x004d3, 0x004d5, 0x004d7, 0x004d9, 0x004db, 0x004dd, 0x004df, 0x004e1, 0x004e3, 0x004e5, 0x004e7, 0x004e9, 0x004eb, 0x004ed, 0x004ef, 0x004f1, 0x004f3, 0x004f5, 0x004f7, 0x004f9, 0x004fb, 0x004fd, 0x004ff, 0x00501, 0x00503, 0x00505, 0x00507, 0x00509, 0x0050b, 0x0050d, 0x0050f, 0x00511, 0x00513, 0x00515, 0x00517, 0x00519, 0x0051b, 0x0051d, 0x0051f, 0x00521, 0x00523, 0x00525, 0x00527, 0x00529, 0x0052b, 0x0052d, 0x0052f, 0x00561, 0x00562, 0x00563, 0x00564, 0x00565, 0x00566, 0x00567, 0x00568, 0x00569, 0x0056a, 0x0056b, 0x0056c, 0x0056d, 0x0056e, 0x0056f, 0x00570, 0x00571, 0x00572, 0x00573, 0x00574, 0x00575, 0x00576, 0x00577, 0x00578, 0x00579, 0x0057a, 0x0057b, 0x0057c, 0x0057d, 0x0057e, 0x0057f, 0x00580, 0x00581, 0x00582, 0x00583, 0x00584, 0x00585, 0x00586, 0x013f8, 0x013f9, 0x013fa, 0x013fb, 0x013fc, 0x013fd, 0x01c80, 0x01c81, 0x01c82, 0x01c83, 0x01c84, 0x01c85, 0x01c86, 0x01c87, 0x01c88, 0x01d79, 0x01d7d, 0x01e01, 0x01e03, 0x01e05, 0x01e07, 0x01e09, 0x01e0b, 0x01e0d, 0x01e0f, 0x01e11, 0x01e13, 0x01e15, 0x01e17, 0x01e19, 0x01e1b, 0x01e1d, 0x01e1f, 0x01e21, 0x01e23, 0x01e25, 0x01e27, 0x01e29, 0x01e2b, 0x01e2d, 0x01e2f, 0x01e31, 0x01e33, 0x01e35, 0x01e37, 0x01e39, 0x01e3b, 0x01e3d, 0x01e3f, 0x01e41, 0x01e43, 0x01e45, 0x01e47, 0x01e49, 0x01e4b, 0x01e4d, 0x01e4f, 0x01e51, 0x01e53, 0x01e55, 0x01e57, 0x01e59, 0x01e5b, 0x01e5d, 0x01e5f, 0x01e61, 0x01e63, 0x01e65, 0x01e67, 0x01e69, 0x01e6b, 0x01e6d, 0x01e6f, 0x01e71, 0x01e73, 0x01e75, 0x01e77, 0x01e79, 0x01e7b, 0x01e7d, 0x01e7f, 0x01e81, 0x01e83, 0x01e85, 0x01e87, 0x01e89, 0x01e8b, 0x01e8d, 0x01e8f, 0x01e91, 0x01e93, 0x01e95, 0x01e9b, 0x01ea1, 0x01ea3, 0x01ea5, 0x01ea7, 0x01ea9, 0x01eab, 0x01ead, 0x01eaf, 0x01eb1, 0x01eb3, 0x01eb5, 0x01eb7, 0x01eb9, 0x01ebb, 0x01ebd, 0x01ebf, 0x01ec1, 0x01ec3, 0x01ec5, 0x01ec7, 0x01ec9, 0x01ecb, 0x01ecd, 0x01ecf, 0x01ed1, 0x01ed3, 0x01ed5, 0x01ed7, 0x01ed9, 0x01edb, 0x01edd, 0x01edf, 0x01ee1, 0x01ee3, 0x01ee5, 0x01ee7, 0x01ee9, 0x01eeb, 0x01eed, 0x01eef, 0x01ef1, 0x01ef3, 0x01ef5, 0x01ef7, 0x01ef9, 0x01efb, 0x01efd, 0x01eff, 0x01f00, 0x01f01, 0x01f02, 0x01f03, 0x01f04, 0x01f05, 0x01f06, 0x01f07, 0x01f10, 0x01f11, 0x01f12, 0x01f13, 0x01f14, 0x01f15, 0x01f20, 0x01f21, 0x01f22, 0x01f23, 0x01f24, 0x01f25, 0x01f26, 0x01f27, 0x01f30, 0x01f31, 0x01f32, 0x01f33, 0x01f34, 0x01f35, 0x01f36, 0x01f37, 0x01f40, 0x01f41, 0x01f42, 0x01f43, 0x01f44, 0x01f45, 0x01f51, 0x01f53, 0x01f55, 0x01f57, 0x01f60, 0x01f61, 0x01f62, 0x01f63, 0x01f64, 0x01f65, 0x01f66, 0x01f67, 0x01f70, 0x01f71, 0x01f72, 0x01f73, 0x01f74, 0x01f75, 0x01f76, 0x01f77, 0x01f78, 0x01f79, 0x01f7a, 0x01f7b, 0x01f7c, 0x01f7d, 0x01f80, 0x01f81, 0x01f82, 0x01f83, 0x01f84, 0x01f85, 0x01f86, 0x01f87, 0x01f90, 0x01f91, 0x01f92, 0x01f93, 0x01f94, 0x01f95, 0x01f96, 0x01f97, 0x01fa0, 0x01fa1, 0x01fa2, 0x01fa3, 0x01fa4, 0x01fa5, 0x01fa6, 0x01fa7, 0x01fb0, 0x01fb1, 0x01fb3, 0x01fbe, 0x01fc3, 0x01fd0, 0x01fd1, 0x01fe0, 0x01fe1, 0x01fe5, 0x01ff3, 0x0214e, 0x02170, 0x02171, 0x02172, 0x02173, 0x02174, 0x02175, 0x02176, 0x02177, 0x02178, 0x02179, 0x0217a, 0x0217b, 0x0217c, 0x0217d, 0x0217e, 0x0217f, 0x02184, 0x024d0, 0x024d1, 0x024d2, 0x024d3, 0x024d4, 0x024d5, 0x024d6, 0x024d7, 0x024d8, 0x024d9, 0x024da, 0x024db, 0x024dc, 0x024dd, 0x024de, 0x024df, 0x024e0, 0x024e1, 0x024e2, 0x024e3, 0x024e4, 0x024e5, 0x024e6, 0x024e7, 0x024e8, 0x024e9, 0x02c30, 0x02c31, 0x02c32, 0x02c33, 0x02c34, 0x02c35, 0x02c36, 0x02c37, 0x02c38, 0x02c39, 0x02c3a, 0x02c3b, 0x02c3c, 0x02c3d, 0x02c3e, 0x02c3f, 0x02c40, 0x02c41, 0x02c42, 0x02c43, 0x02c44, 0x02c45, 0x02c46, 0x02c47, 0x02c48, 0x02c49, 0x02c4a, 0x02c4b, 0x02c4c, 0x02c4d, 0x02c4e, 0x02c4f, 0x02c50, 0x02c51, 0x02c52, 0x02c53, 0x02c54, 0x02c55, 0x02c56, 0x02c57, 0x02c58, 0x02c59, 0x02c5a, 0x02c5b, 0x02c5c, 0x02c5d, 0x02c5e, 0x02c61, 0x02c65, 0x02c66, 0x02c68, 0x02c6a, 0x02c6c, 0x02c73, 0x02c76, 0x02c81, 0x02c83, 0x02c85, 0x02c87, 0x02c89, 0x02c8b, 0x02c8d, 0x02c8f, 0x02c91, 0x02c93, 0x02c95, 0x02c97, 0x02c99, 0x02c9b, 0x02c9d, 0x02c9f, 0x02ca1, 0x02ca3, 0x02ca5, 0x02ca7, 0x02ca9, 0x02cab, 0x02cad, 0x02caf, 0x02cb1, 0x02cb3, 0x02cb5, 0x02cb7, 0x02cb9, 0x02cbb, 0x02cbd, 0x02cbf, 0x02cc1, 0x02cc3, 0x02cc5, 0x02cc7, 0x02cc9, 0x02ccb, 0x02ccd, 0x02ccf, 0x02cd1, 0x02cd3, 0x02cd5, 0x02cd7, 0x02cd9, 0x02cdb, 0x02cdd, 0x02cdf, 0x02ce1, 0x02ce3, 0x02cec, 0x02cee, 0x02cf3, 0x02d00, 0x02d01, 0x02d02, 0x02d03, 0x02d04, 0x02d05, 0x02d06, 0x02d07, 0x02d08, 0x02d09, 0x02d0a, 0x02d0b, 0x02d0c, 0x02d0d, 0x02d0e, 0x02d0f, 0x02d10, 0x02d11, 0x02d12, 0x02d13, 0x02d14, 0x02d15, 0x02d16, 0x02d17, 0x02d18, 0x02d19, 0x02d1a, 0x02d1b, 0x02d1c, 0x02d1d, 0x02d1e, 0x02d1f, 0x02d20, 0x02d21, 0x02d22, 0x02d23, 0x02d24, 0x02d25, 0x02d27, 0x02d2d, 0x0a641, 0x0a643, 0x0a645, 0x0a647, 0x0a649, 0x0a64b, 0x0a64d, 0x0a64f, 0x0a651, 0x0a653, 0x0a655, 0x0a657, 0x0a659, 0x0a65b, 0x0a65d, 0x0a65f, 0x0a661, 0x0a663, 0x0a665, 0x0a667, 0x0a669, 0x0a66b, 0x0a66d, 0x0a681, 0x0a683, 0x0a685, 0x0a687, 0x0a689, 0x0a68b, 0x0a68d, 0x0a68f, 0x0a691, 0x0a693, 0x0a695, 0x0a697, 0x0a699, 0x0a69b, 0x0a723, 0x0a725, 0x0a727, 0x0a729, 0x0a72b, 0x0a72d, 0x0a72f, 0x0a733, 0x0a735, 0x0a737, 0x0a739, 0x0a73b, 0x0a73d, 0x0a73f, 0x0a741, 0x0a743, 0x0a745, 0x0a747, 0x0a749, 0x0a74b, 0x0a74d, 0x0a74f, 0x0a751, 0x0a753, 0x0a755, 0x0a757, 0x0a759, 0x0a75b, 0x0a75d, 0x0a75f, 0x0a761, 0x0a763, 0x0a765, 0x0a767, 0x0a769, 0x0a76b, 0x0a76d, 0x0a76f, 0x0a77a, 0x0a77c, 0x0a77f, 0x0a781, 0x0a783, 0x0a785, 0x0a787, 0x0a78c, 0x0a791, 0x0a793, 0x0a797, 0x0a799, 0x0a79b, 0x0a79d, 0x0a79f, 0x0a7a1, 0x0a7a3, 0x0a7a5, 0x0a7a7, 0x0a7a9, 0x0a7b5, 0x0a7b7, 0x0ab53, 0x0ab70, 0x0ab71, 0x0ab72, 0x0ab73, 0x0ab74, 0x0ab75, 0x0ab76, 0x0ab77, 0x0ab78, 0x0ab79, 0x0ab7a, 0x0ab7b, 0x0ab7c, 0x0ab7d, 0x0ab7e, 0x0ab7f, 0x0ab80, 0x0ab81, 0x0ab82, 0x0ab83, 0x0ab84, 0x0ab85, 0x0ab86, 0x0ab87, 0x0ab88, 0x0ab89, 0x0ab8a, 0x0ab8b, 0x0ab8c, 0x0ab8d, 0x0ab8e, 0x0ab8f, 0x0ab90, 0x0ab91, 0x0ab92, 0x0ab93, 0x0ab94, 0x0ab95, 0x0ab96, 0x0ab97, 0x0ab98, 0x0ab99, 0x0ab9a, 0x0ab9b, 0x0ab9c, 0x0ab9d, 0x0ab9e, 0x0ab9f, 0x0aba0, 0x0aba1, 0x0aba2, 0x0aba3, 0x0aba4, 0x0aba5, 0x0aba6, 0x0aba7, 0x0aba8, 0x0aba9, 0x0abaa, 0x0abab, 0x0abac, 0x0abad, 0x0abae, 0x0abaf, 0x0abb0, 0x0abb1, 0x0abb2, 0x0abb3, 0x0abb4, 0x0abb5, 0x0abb6, 0x0abb7, 0x0abb8, 0x0abb9, 0x0abba, 0x0abbb, 0x0abbc, 0x0abbd, 0x0abbe, 0x0abbf, 0x0ff41, 0x0ff42, 0x0ff43, 0x0ff44, 0x0ff45, 0x0ff46, 0x0ff47, 0x0ff48, 0x0ff49, 0x0ff4a, 0x0ff4b, 0x0ff4c, 0x0ff4d, 0x0ff4e, 0x0ff4f, 0x0ff50, 0x0ff51, 0x0ff52, 0x0ff53, 0x0ff54, 0x0ff55, 0x0ff56, 0x0ff57, 0x0ff58, 0x0ff59, 0x0ff5a }; static const uint16_t titlecase16_values[] = { 0x00100, 0x00102, 0x00104, 0x00106, 0x00108, 0x0010a, 0x0010c, 0x0010e, 0x00110, 0x00112, 0x00114, 0x00116, 0x00118, 0x0011a, 0x0011c, 0x0011e, 0x00120, 0x00122, 0x00124, 0x00126, 0x00128, 0x0012a, 0x0012c, 0x0012e, 0x00049, 0x00132, 0x00134, 0x00136, 0x00139, 0x0013b, 0x0013d, 0x0013f, 0x00141, 0x00143, 0x00145, 0x00147, 0x0014a, 0x0014c, 0x0014e, 0x00150, 0x00152, 0x00154, 0x00156, 0x00158, 0x0015a, 0x0015c, 0x0015e, 0x00160, 0x00162, 0x00164, 0x00166, 0x00168, 0x0016a, 0x0016c, 0x0016e, 0x00170, 0x00172, 0x00174, 0x00176, 0x00179, 0x0017b, 0x0017d, 0x00053, 0x00243, 0x00182, 0x00184, 0x00187, 0x0018b, 0x00191, 0x001f6, 0x00198, 0x0023d, 0x00220, 0x001a0, 0x001a2, 0x001a4, 0x001a7, 0x001ac, 0x001af, 0x001b3, 0x001b5, 0x001b8, 0x001bc, 0x001f7, 0x001c5, 0x001c5, 0x001c8, 0x001c8, 0x001cb, 0x001cb, 0x001cd, 0x001cf, 0x001d1, 0x001d3, 0x001d5, 0x001d7, 0x001d9, 0x001db, 0x0018e, 0x001de, 0x001e0, 0x001e2, 0x001e4, 0x001e6, 0x001e8, 0x001ea, 0x001ec, 0x001ee, 0x001f2, 0x001f2, 0x001f4, 0x001f8, 0x001fa, 0x001fc, 0x001fe, 0x00200, 0x00202, 0x00204, 0x00206, 0x00208, 0x0020a, 0x0020c, 0x0020e, 0x00210, 0x00212, 0x00214, 0x00216, 0x00218, 0x0021a, 0x0021c, 0x0021e, 0x00222, 0x00224, 0x00226, 0x00228, 0x0022a, 0x0022c, 0x0022e, 0x00230, 0x00232, 0x0023b, 0x02c7e, 0x02c7f, 0x00241, 0x00246, 0x00248, 0x0024a, 0x0024c, 0x0024e, 0x02c6f, 0x02c6d, 0x02c70, 0x00181, 0x00186, 0x00189, 0x0018a, 0x0018f, 0x00190, 0x0a7ab, 0x00193, 0x0a7ac, 0x00194, 0x0a78d, 0x0a7aa, 0x00197, 0x00196, 0x0a7ae, 0x02c62, 0x0a7ad, 0x0019c, 0x02c6e, 0x0019d, 0x0019f, 0x02c64, 0x001a6, 0x001a9, 0x0a7b1, 0x001ae, 0x00244, 0x001b1, 0x001b2, 0x00245, 0x001b7, 0x0a7b2, 0x0a7b0, 0x00399, 0x00370, 0x00372, 0x00376, 0x003fd, 0x003fe, 0x003ff, 0x00386, 0x00388, 0x00389, 0x0038a, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x003aa, 0x003ab, 0x0038c, 0x0038e, 0x0038f, 0x00392, 0x00398, 0x003a6, 0x003a0, 0x003cf, 0x003d8, 0x003da, 0x003dc, 0x003de, 0x003e0, 0x003e2, 0x003e4, 0x003e6, 0x003e8, 0x003ea, 0x003ec, 0x003ee, 0x0039a, 0x003a1, 0x003f9, 0x0037f, 0x00395, 0x003f7, 0x003fa, 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417, 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f, 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427, 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f, 0x00400, 0x00401, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407, 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f, 0x00460, 0x00462, 0x00464, 0x00466, 0x00468, 0x0046a, 0x0046c, 0x0046e, 0x00470, 0x00472, 0x00474, 0x00476, 0x00478, 0x0047a, 0x0047c, 0x0047e, 0x00480, 0x0048a, 0x0048c, 0x0048e, 0x00490, 0x00492, 0x00494, 0x00496, 0x00498, 0x0049a, 0x0049c, 0x0049e, 0x004a0, 0x004a2, 0x004a4, 0x004a6, 0x004a8, 0x004aa, 0x004ac, 0x004ae, 0x004b0, 0x004b2, 0x004b4, 0x004b6, 0x004b8, 0x004ba, 0x004bc, 0x004be, 0x004c1, 0x004c3, 0x004c5, 0x004c7, 0x004c9, 0x004cb, 0x004cd, 0x004c0, 0x004d0, 0x004d2, 0x004d4, 0x004d6, 0x004d8, 0x004da, 0x004dc, 0x004de, 0x004e0, 0x004e2, 0x004e4, 0x004e6, 0x004e8, 0x004ea, 0x004ec, 0x004ee, 0x004f0, 0x004f2, 0x004f4, 0x004f6, 0x004f8, 0x004fa, 0x004fc, 0x004fe, 0x00500, 0x00502, 0x00504, 0x00506, 0x00508, 0x0050a, 0x0050c, 0x0050e, 0x00510, 0x00512, 0x00514, 0x00516, 0x00518, 0x0051a, 0x0051c, 0x0051e, 0x00520, 0x00522, 0x00524, 0x00526, 0x00528, 0x0052a, 0x0052c, 0x0052e, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537, 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f, 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547, 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f, 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x013f0, 0x013f1, 0x013f2, 0x013f3, 0x013f4, 0x013f5, 0x00412, 0x00414, 0x0041e, 0x00421, 0x00422, 0x00422, 0x0042a, 0x00462, 0x0a64a, 0x0a77d, 0x02c63, 0x01e00, 0x01e02, 0x01e04, 0x01e06, 0x01e08, 0x01e0a, 0x01e0c, 0x01e0e, 0x01e10, 0x01e12, 0x01e14, 0x01e16, 0x01e18, 0x01e1a, 0x01e1c, 0x01e1e, 0x01e20, 0x01e22, 0x01e24, 0x01e26, 0x01e28, 0x01e2a, 0x01e2c, 0x01e2e, 0x01e30, 0x01e32, 0x01e34, 0x01e36, 0x01e38, 0x01e3a, 0x01e3c, 0x01e3e, 0x01e40, 0x01e42, 0x01e44, 0x01e46, 0x01e48, 0x01e4a, 0x01e4c, 0x01e4e, 0x01e50, 0x01e52, 0x01e54, 0x01e56, 0x01e58, 0x01e5a, 0x01e5c, 0x01e5e, 0x01e60, 0x01e62, 0x01e64, 0x01e66, 0x01e68, 0x01e6a, 0x01e6c, 0x01e6e, 0x01e70, 0x01e72, 0x01e74, 0x01e76, 0x01e78, 0x01e7a, 0x01e7c, 0x01e7e, 0x01e80, 0x01e82, 0x01e84, 0x01e86, 0x01e88, 0x01e8a, 0x01e8c, 0x01e8e, 0x01e90, 0x01e92, 0x01e94, 0x01e60, 0x01ea0, 0x01ea2, 0x01ea4, 0x01ea6, 0x01ea8, 0x01eaa, 0x01eac, 0x01eae, 0x01eb0, 0x01eb2, 0x01eb4, 0x01eb6, 0x01eb8, 0x01eba, 0x01ebc, 0x01ebe, 0x01ec0, 0x01ec2, 0x01ec4, 0x01ec6, 0x01ec8, 0x01eca, 0x01ecc, 0x01ece, 0x01ed0, 0x01ed2, 0x01ed4, 0x01ed6, 0x01ed8, 0x01eda, 0x01edc, 0x01ede, 0x01ee0, 0x01ee2, 0x01ee4, 0x01ee6, 0x01ee8, 0x01eea, 0x01eec, 0x01eee, 0x01ef0, 0x01ef2, 0x01ef4, 0x01ef6, 0x01ef8, 0x01efa, 0x01efc, 0x01efe, 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, 0x01f0d, 0x01f0e, 0x01f0f, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, 0x01f1d, 0x01f28, 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, 0x01f2f, 0x01f38, 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, 0x01f3f, 0x01f48, 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f59, 0x01f5b, 0x01f5d, 0x01f5f, 0x01f68, 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, 0x01f6f, 0x01fba, 0x01fbb, 0x01fc8, 0x01fc9, 0x01fca, 0x01fcb, 0x01fda, 0x01fdb, 0x01ff8, 0x01ff9, 0x01fea, 0x01feb, 0x01ffa, 0x01ffb, 0x01f88, 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f98, 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01fa8, 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fb8, 0x01fb9, 0x01fbc, 0x00399, 0x01fcc, 0x01fd8, 0x01fd9, 0x01fe8, 0x01fe9, 0x01fec, 0x01ffc, 0x02132, 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167, 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, 0x0216d, 0x0216e, 0x0216f, 0x02183, 0x024b6, 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf, 0x02c00, 0x02c01, 0x02c02, 0x02c03, 0x02c04, 0x02c05, 0x02c06, 0x02c07, 0x02c08, 0x02c09, 0x02c0a, 0x02c0b, 0x02c0c, 0x02c0d, 0x02c0e, 0x02c0f, 0x02c10, 0x02c11, 0x02c12, 0x02c13, 0x02c14, 0x02c15, 0x02c16, 0x02c17, 0x02c18, 0x02c19, 0x02c1a, 0x02c1b, 0x02c1c, 0x02c1d, 0x02c1e, 0x02c1f, 0x02c20, 0x02c21, 0x02c22, 0x02c23, 0x02c24, 0x02c25, 0x02c26, 0x02c27, 0x02c28, 0x02c29, 0x02c2a, 0x02c2b, 0x02c2c, 0x02c2d, 0x02c2e, 0x02c60, 0x0023a, 0x0023e, 0x02c67, 0x02c69, 0x02c6b, 0x02c72, 0x02c75, 0x02c80, 0x02c82, 0x02c84, 0x02c86, 0x02c88, 0x02c8a, 0x02c8c, 0x02c8e, 0x02c90, 0x02c92, 0x02c94, 0x02c96, 0x02c98, 0x02c9a, 0x02c9c, 0x02c9e, 0x02ca0, 0x02ca2, 0x02ca4, 0x02ca6, 0x02ca8, 0x02caa, 0x02cac, 0x02cae, 0x02cb0, 0x02cb2, 0x02cb4, 0x02cb6, 0x02cb8, 0x02cba, 0x02cbc, 0x02cbe, 0x02cc0, 0x02cc2, 0x02cc4, 0x02cc6, 0x02cc8, 0x02cca, 0x02ccc, 0x02cce, 0x02cd0, 0x02cd2, 0x02cd4, 0x02cd6, 0x02cd8, 0x02cda, 0x02cdc, 0x02cde, 0x02ce0, 0x02ce2, 0x02ceb, 0x02ced, 0x02cf2, 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7, 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af, 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7, 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf, 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010c7, 0x010cd, 0x0a640, 0x0a642, 0x0a644, 0x0a646, 0x0a648, 0x0a64a, 0x0a64c, 0x0a64e, 0x0a650, 0x0a652, 0x0a654, 0x0a656, 0x0a658, 0x0a65a, 0x0a65c, 0x0a65e, 0x0a660, 0x0a662, 0x0a664, 0x0a666, 0x0a668, 0x0a66a, 0x0a66c, 0x0a680, 0x0a682, 0x0a684, 0x0a686, 0x0a688, 0x0a68a, 0x0a68c, 0x0a68e, 0x0a690, 0x0a692, 0x0a694, 0x0a696, 0x0a698, 0x0a69a, 0x0a722, 0x0a724, 0x0a726, 0x0a728, 0x0a72a, 0x0a72c, 0x0a72e, 0x0a732, 0x0a734, 0x0a736, 0x0a738, 0x0a73a, 0x0a73c, 0x0a73e, 0x0a740, 0x0a742, 0x0a744, 0x0a746, 0x0a748, 0x0a74a, 0x0a74c, 0x0a74e, 0x0a750, 0x0a752, 0x0a754, 0x0a756, 0x0a758, 0x0a75a, 0x0a75c, 0x0a75e, 0x0a760, 0x0a762, 0x0a764, 0x0a766, 0x0a768, 0x0a76a, 0x0a76c, 0x0a76e, 0x0a779, 0x0a77b, 0x0a77e, 0x0a780, 0x0a782, 0x0a784, 0x0a786, 0x0a78b, 0x0a790, 0x0a792, 0x0a796, 0x0a798, 0x0a79a, 0x0a79c, 0x0a79e, 0x0a7a0, 0x0a7a2, 0x0a7a4, 0x0a7a6, 0x0a7a8, 0x0a7b4, 0x0a7b6, 0x0a7b3, 0x013a0, 0x013a1, 0x013a2, 0x013a3, 0x013a4, 0x013a5, 0x013a6, 0x013a7, 0x013a8, 0x013a9, 0x013aa, 0x013ab, 0x013ac, 0x013ad, 0x013ae, 0x013af, 0x013b0, 0x013b1, 0x013b2, 0x013b3, 0x013b4, 0x013b5, 0x013b6, 0x013b7, 0x013b8, 0x013b9, 0x013ba, 0x013bb, 0x013bc, 0x013bd, 0x013be, 0x013bf, 0x013c0, 0x013c1, 0x013c2, 0x013c3, 0x013c4, 0x013c5, 0x013c6, 0x013c7, 0x013c8, 0x013c9, 0x013ca, 0x013cb, 0x013cc, 0x013cd, 0x013ce, 0x013cf, 0x013d0, 0x013d1, 0x013d2, 0x013d3, 0x013d4, 0x013d5, 0x013d6, 0x013d7, 0x013d8, 0x013d9, 0x013da, 0x013db, 0x013dc, 0x013dd, 0x013de, 0x013df, 0x013e0, 0x013e1, 0x013e2, 0x013e3, 0x013e4, 0x013e5, 0x013e6, 0x013e7, 0x013e8, 0x013e9, 0x013ea, 0x013eb, 0x013ec, 0x013ed, 0x013ee, 0x013ef, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a }; static const uint32_t titlecase32_keys[] = { 0x10428, 0x10429, 0x1042a, 0x1042b, 0x1042c, 0x1042d, 0x1042e, 0x1042f, 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f, 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, 0x1044f, 0x104d8, 0x104d9, 0x104da, 0x104db, 0x104dc, 0x104dd, 0x104de, 0x104df, 0x104e0, 0x104e1, 0x104e2, 0x104e3, 0x104e4, 0x104e5, 0x104e6, 0x104e7, 0x104e8, 0x104e9, 0x104ea, 0x104eb, 0x104ec, 0x104ed, 0x104ee, 0x104ef, 0x104f0, 0x104f1, 0x104f2, 0x104f3, 0x104f4, 0x104f5, 0x104f6, 0x104f7, 0x104f8, 0x104f9, 0x104fa, 0x104fb, 0x10cc0, 0x10cc1, 0x10cc2, 0x10cc3, 0x10cc4, 0x10cc5, 0x10cc6, 0x10cc7, 0x10cc8, 0x10cc9, 0x10cca, 0x10ccb, 0x10ccc, 0x10ccd, 0x10cce, 0x10ccf, 0x10cd0, 0x10cd1, 0x10cd2, 0x10cd3, 0x10cd4, 0x10cd5, 0x10cd6, 0x10cd7, 0x10cd8, 0x10cd9, 0x10cda, 0x10cdb, 0x10cdc, 0x10cdd, 0x10cde, 0x10cdf, 0x10ce0, 0x10ce1, 0x10ce2, 0x10ce3, 0x10ce4, 0x10ce5, 0x10ce6, 0x10ce7, 0x10ce8, 0x10ce9, 0x10cea, 0x10ceb, 0x10cec, 0x10ced, 0x10cee, 0x10cef, 0x10cf0, 0x10cf1, 0x10cf2, 0x118c0, 0x118c1, 0x118c2, 0x118c3, 0x118c4, 0x118c5, 0x118c6, 0x118c7, 0x118c8, 0x118c9, 0x118ca, 0x118cb, 0x118cc, 0x118cd, 0x118ce, 0x118cf, 0x118d0, 0x118d1, 0x118d2, 0x118d3, 0x118d4, 0x118d5, 0x118d6, 0x118d7, 0x118d8, 0x118d9, 0x118da, 0x118db, 0x118dc, 0x118dd, 0x118de, 0x118df, 0x1e922, 0x1e923, 0x1e924, 0x1e925, 0x1e926, 0x1e927, 0x1e928, 0x1e929, 0x1e92a, 0x1e92b, 0x1e92c, 0x1e92d, 0x1e92e, 0x1e92f, 0x1e930, 0x1e931, 0x1e932, 0x1e933, 0x1e934, 0x1e935, 0x1e936, 0x1e937, 0x1e938, 0x1e939, 0x1e93a, 0x1e93b, 0x1e93c, 0x1e93d, 0x1e93e, 0x1e93f, 0x1e940, 0x1e941, 0x1e942, 0x1e943 }; static const uint32_t titlecase32_values[] = { 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921 }; static const uint16_t uni8_decomp_map[256] = { 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f, 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017, 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f, 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027, 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f, 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, 0x00060, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f, 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087, 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f, 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097, 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f, 0x00020, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7, 0x000a8, 0x000a9, 0x00061, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af, 0x000b0, 0x000b1, 0x00032, 0x00033, 0x000b4, 0x000b5, 0x000b6, 0x000b7, 0x000b8, 0x00031, 0x0006f, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d7, 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x000df, 0x000e0, 0x000e1, 0x000e2, 0x000e3, 0x000e4, 0x000e5, 0x000e6, 0x000e7, 0x000e8, 0x000e9, 0x000ea, 0x000eb, 0x000ec, 0x000ed, 0x000ee, 0x000ef, 0x000f0, 0x000f1, 0x000f2, 0x000f3, 0x000f4, 0x000f5, 0x000f6, 0x000f7, 0x000f8, 0x000f9, 0x000fa, 0x000fb, 0x000fc, 0x000fd, 0x000fe, 0x000ff }; static const uint16_t uni16_decomp_keys[] = { 0x002b0, 0x002b1, 0x002b2, 0x002b3, 0x002b4, 0x002b5, 0x002b6, 0x002b7, 0x002b8, 0x002e0, 0x002e1, 0x002e2, 0x002e3, 0x002e4, 0x00340, 0x00341, 0x00343, 0x00374, 0x0037e, 0x00387, 0x003d2, 0x003f4, 0x003f9, 0x00f0c, 0x010fc, 0x01d2c, 0x01d2d, 0x01d2e, 0x01d30, 0x01d31, 0x01d32, 0x01d33, 0x01d34, 0x01d35, 0x01d36, 0x01d37, 0x01d38, 0x01d39, 0x01d3a, 0x01d3c, 0x01d3d, 0x01d3e, 0x01d3f, 0x01d40, 0x01d41, 0x01d42, 0x01d43, 0x01d44, 0x01d45, 0x01d46, 0x01d47, 0x01d48, 0x01d49, 0x01d4a, 0x01d4b, 0x01d4c, 0x01d4d, 0x01d4f, 0x01d50, 0x01d51, 0x01d52, 0x01d53, 0x01d54, 0x01d55, 0x01d56, 0x01d57, 0x01d58, 0x01d59, 0x01d5a, 0x01d5b, 0x01d5c, 0x01d5d, 0x01d5e, 0x01d5f, 0x01d60, 0x01d61, 0x01d62, 0x01d63, 0x01d64, 0x01d65, 0x01d66, 0x01d67, 0x01d68, 0x01d69, 0x01d6a, 0x01d78, 0x01d9b, 0x01d9c, 0x01d9d, 0x01d9e, 0x01d9f, 0x01da0, 0x01da1, 0x01da2, 0x01da3, 0x01da4, 0x01da5, 0x01da6, 0x01da7, 0x01da8, 0x01da9, 0x01daa, 0x01dab, 0x01dac, 0x01dad, 0x01dae, 0x01daf, 0x01db0, 0x01db1, 0x01db2, 0x01db3, 0x01db4, 0x01db5, 0x01db6, 0x01db7, 0x01db8, 0x01db9, 0x01dba, 0x01dbb, 0x01dbc, 0x01dbd, 0x01dbe, 0x01dbf, 0x01fbb, 0x01fc9, 0x01fcb, 0x01fd3, 0x01fdb, 0x01fe3, 0x01feb, 0x01fee, 0x01fef, 0x01ff9, 0x01ffb, 0x01ffd, 0x02000, 0x02001, 0x02002, 0x02003, 0x02004, 0x02005, 0x02006, 0x02007, 0x02008, 0x02009, 0x0200a, 0x02011, 0x02024, 0x0202f, 0x0205f, 0x02070, 0x02071, 0x02074, 0x02075, 0x02076, 0x02077, 0x02078, 0x02079, 0x0207a, 0x0207b, 0x0207c, 0x0207d, 0x0207e, 0x0207f, 0x02080, 0x02081, 0x02082, 0x02083, 0x02084, 0x02085, 0x02086, 0x02087, 0x02088, 0x02089, 0x0208a, 0x0208b, 0x0208c, 0x0208d, 0x0208e, 0x02090, 0x02091, 0x02092, 0x02093, 0x02094, 0x02095, 0x02096, 0x02097, 0x02098, 0x02099, 0x0209a, 0x0209b, 0x0209c, 0x02102, 0x02107, 0x0210a, 0x0210b, 0x0210c, 0x0210d, 0x0210e, 0x0210f, 0x02110, 0x02111, 0x02112, 0x02113, 0x02115, 0x02119, 0x0211a, 0x0211b, 0x0211c, 0x0211d, 0x02124, 0x02126, 0x02128, 0x0212a, 0x0212b, 0x0212c, 0x0212d, 0x0212f, 0x02130, 0x02131, 0x02133, 0x02134, 0x02135, 0x02136, 0x02137, 0x02138, 0x02139, 0x0213c, 0x0213d, 0x0213e, 0x0213f, 0x02140, 0x02145, 0x02146, 0x02147, 0x02148, 0x02149, 0x02160, 0x02164, 0x02169, 0x0216c, 0x0216d, 0x0216e, 0x0216f, 0x02329, 0x0232a, 0x02460, 0x02461, 0x02462, 0x02463, 0x02464, 0x02465, 0x02466, 0x02467, 0x02468, 0x024b6, 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf, 0x024ea, 0x02c7c, 0x02c7d, 0x02d6f, 0x02e9f, 0x02ef3, 0x02f00, 0x02f01, 0x02f02, 0x02f03, 0x02f04, 0x02f05, 0x02f06, 0x02f07, 0x02f08, 0x02f09, 0x02f0a, 0x02f0b, 0x02f0c, 0x02f0d, 0x02f0e, 0x02f0f, 0x02f10, 0x02f11, 0x02f12, 0x02f13, 0x02f14, 0x02f15, 0x02f16, 0x02f17, 0x02f18, 0x02f19, 0x02f1a, 0x02f1b, 0x02f1c, 0x02f1d, 0x02f1e, 0x02f1f, 0x02f20, 0x02f21, 0x02f22, 0x02f23, 0x02f24, 0x02f25, 0x02f26, 0x02f27, 0x02f28, 0x02f29, 0x02f2a, 0x02f2b, 0x02f2c, 0x02f2d, 0x02f2e, 0x02f2f, 0x02f30, 0x02f31, 0x02f32, 0x02f33, 0x02f34, 0x02f35, 0x02f36, 0x02f37, 0x02f38, 0x02f39, 0x02f3a, 0x02f3b, 0x02f3c, 0x02f3d, 0x02f3e, 0x02f3f, 0x02f40, 0x02f41, 0x02f42, 0x02f43, 0x02f44, 0x02f45, 0x02f46, 0x02f47, 0x02f48, 0x02f49, 0x02f4a, 0x02f4b, 0x02f4c, 0x02f4d, 0x02f4e, 0x02f4f, 0x02f50, 0x02f51, 0x02f52, 0x02f53, 0x02f54, 0x02f55, 0x02f56, 0x02f57, 0x02f58, 0x02f59, 0x02f5a, 0x02f5b, 0x02f5c, 0x02f5d, 0x02f5e, 0x02f5f, 0x02f60, 0x02f61, 0x02f62, 0x02f63, 0x02f64, 0x02f65, 0x02f66, 0x02f67, 0x02f68, 0x02f69, 0x02f6a, 0x02f6b, 0x02f6c, 0x02f6d, 0x02f6e, 0x02f6f, 0x02f70, 0x02f71, 0x02f72, 0x02f73, 0x02f74, 0x02f75, 0x02f76, 0x02f77, 0x02f78, 0x02f79, 0x02f7a, 0x02f7b, 0x02f7c, 0x02f7d, 0x02f7e, 0x02f7f, 0x02f80, 0x02f81, 0x02f82, 0x02f83, 0x02f84, 0x02f85, 0x02f86, 0x02f87, 0x02f88, 0x02f89, 0x02f8a, 0x02f8b, 0x02f8c, 0x02f8d, 0x02f8e, 0x02f8f, 0x02f90, 0x02f91, 0x02f92, 0x02f93, 0x02f94, 0x02f95, 0x02f96, 0x02f97, 0x02f98, 0x02f99, 0x02f9a, 0x02f9b, 0x02f9c, 0x02f9d, 0x02f9e, 0x02f9f, 0x02fa0, 0x02fa1, 0x02fa2, 0x02fa3, 0x02fa4, 0x02fa5, 0x02fa6, 0x02fa7, 0x02fa8, 0x02fa9, 0x02faa, 0x02fab, 0x02fac, 0x02fad, 0x02fae, 0x02faf, 0x02fb0, 0x02fb1, 0x02fb2, 0x02fb3, 0x02fb4, 0x02fb5, 0x02fb6, 0x02fb7, 0x02fb8, 0x02fb9, 0x02fba, 0x02fbb, 0x02fbc, 0x02fbd, 0x02fbe, 0x02fbf, 0x02fc0, 0x02fc1, 0x02fc2, 0x02fc3, 0x02fc4, 0x02fc5, 0x02fc6, 0x02fc7, 0x02fc8, 0x02fc9, 0x02fca, 0x02fcb, 0x02fcc, 0x02fcd, 0x02fce, 0x02fcf, 0x02fd0, 0x02fd1, 0x02fd2, 0x02fd3, 0x02fd4, 0x02fd5, 0x03000, 0x03036, 0x03038, 0x03039, 0x0303a, 0x03131, 0x03132, 0x03133, 0x03134, 0x03135, 0x03136, 0x03137, 0x03138, 0x03139, 0x0313a, 0x0313b, 0x0313c, 0x0313d, 0x0313e, 0x0313f, 0x03140, 0x03141, 0x03142, 0x03143, 0x03144, 0x03145, 0x03146, 0x03147, 0x03148, 0x03149, 0x0314a, 0x0314b, 0x0314c, 0x0314d, 0x0314e, 0x0314f, 0x03150, 0x03151, 0x03152, 0x03153, 0x03154, 0x03155, 0x03156, 0x03157, 0x03158, 0x03159, 0x0315a, 0x0315b, 0x0315c, 0x0315d, 0x0315e, 0x0315f, 0x03160, 0x03161, 0x03162, 0x03163, 0x03164, 0x03165, 0x03166, 0x03167, 0x03168, 0x03169, 0x0316a, 0x0316b, 0x0316c, 0x0316d, 0x0316e, 0x0316f, 0x03170, 0x03171, 0x03172, 0x03173, 0x03174, 0x03175, 0x03176, 0x03177, 0x03178, 0x03179, 0x0317a, 0x0317b, 0x0317c, 0x0317d, 0x0317e, 0x0317f, 0x03180, 0x03181, 0x03182, 0x03183, 0x03184, 0x03185, 0x03186, 0x03187, 0x03188, 0x03189, 0x0318a, 0x0318b, 0x0318c, 0x0318d, 0x0318e, 0x03192, 0x03193, 0x03194, 0x03195, 0x03196, 0x03197, 0x03198, 0x03199, 0x0319a, 0x0319b, 0x0319c, 0x0319d, 0x0319e, 0x0319f, 0x03244, 0x03245, 0x03246, 0x03247, 0x03260, 0x03261, 0x03262, 0x03263, 0x03264, 0x03265, 0x03266, 0x03267, 0x03268, 0x03269, 0x0326a, 0x0326b, 0x0326c, 0x0326d, 0x03280, 0x03281, 0x03282, 0x03283, 0x03284, 0x03285, 0x03286, 0x03287, 0x03288, 0x03289, 0x0328a, 0x0328b, 0x0328c, 0x0328d, 0x0328e, 0x0328f, 0x03290, 0x03291, 0x03292, 0x03293, 0x03294, 0x03295, 0x03296, 0x03297, 0x03298, 0x03299, 0x0329a, 0x0329b, 0x0329c, 0x0329d, 0x0329e, 0x0329f, 0x032a0, 0x032a1, 0x032a2, 0x032a3, 0x032a4, 0x032a5, 0x032a6, 0x032a7, 0x032a8, 0x032a9, 0x032aa, 0x032ab, 0x032ac, 0x032ad, 0x032ae, 0x032af, 0x032b0, 0x032d0, 0x032d1, 0x032d2, 0x032d3, 0x032d4, 0x032d5, 0x032d6, 0x032d7, 0x032d8, 0x032d9, 0x032da, 0x032db, 0x032dc, 0x032dd, 0x032de, 0x032df, 0x032e0, 0x032e1, 0x032e2, 0x032e3, 0x032e4, 0x032e5, 0x032e6, 0x032e7, 0x032e8, 0x032e9, 0x032ea, 0x032eb, 0x032ec, 0x032ed, 0x032ee, 0x032ef, 0x032f0, 0x032f1, 0x032f2, 0x032f3, 0x032f4, 0x032f5, 0x032f6, 0x032f7, 0x032f8, 0x032f9, 0x032fa, 0x032fb, 0x032fc, 0x032fd, 0x032fe, 0x0a69c, 0x0a69d, 0x0a770, 0x0a7f8, 0x0a7f9, 0x0ab5c, 0x0ab5d, 0x0ab5e, 0x0ab5f, 0x0f900, 0x0f901, 0x0f902, 0x0f903, 0x0f904, 0x0f905, 0x0f906, 0x0f907, 0x0f908, 0x0f909, 0x0f90a, 0x0f90b, 0x0f90c, 0x0f90d, 0x0f90e, 0x0f90f, 0x0f910, 0x0f911, 0x0f912, 0x0f913, 0x0f914, 0x0f915, 0x0f916, 0x0f917, 0x0f918, 0x0f919, 0x0f91a, 0x0f91b, 0x0f91c, 0x0f91d, 0x0f91e, 0x0f91f, 0x0f920, 0x0f921, 0x0f922, 0x0f923, 0x0f924, 0x0f925, 0x0f926, 0x0f927, 0x0f928, 0x0f929, 0x0f92a, 0x0f92b, 0x0f92c, 0x0f92d, 0x0f92e, 0x0f92f, 0x0f930, 0x0f931, 0x0f932, 0x0f933, 0x0f934, 0x0f935, 0x0f936, 0x0f937, 0x0f938, 0x0f939, 0x0f93a, 0x0f93b, 0x0f93c, 0x0f93d, 0x0f93e, 0x0f93f, 0x0f940, 0x0f941, 0x0f942, 0x0f943, 0x0f944, 0x0f945, 0x0f946, 0x0f947, 0x0f948, 0x0f949, 0x0f94a, 0x0f94b, 0x0f94c, 0x0f94d, 0x0f94e, 0x0f94f, 0x0f950, 0x0f951, 0x0f952, 0x0f953, 0x0f954, 0x0f955, 0x0f956, 0x0f957, 0x0f958, 0x0f959, 0x0f95a, 0x0f95b, 0x0f95c, 0x0f95d, 0x0f95e, 0x0f95f, 0x0f960, 0x0f961, 0x0f962, 0x0f963, 0x0f964, 0x0f965, 0x0f966, 0x0f967, 0x0f968, 0x0f969, 0x0f96a, 0x0f96b, 0x0f96c, 0x0f96d, 0x0f96e, 0x0f96f, 0x0f970, 0x0f971, 0x0f972, 0x0f973, 0x0f974, 0x0f975, 0x0f976, 0x0f977, 0x0f978, 0x0f979, 0x0f97a, 0x0f97b, 0x0f97c, 0x0f97d, 0x0f97e, 0x0f97f, 0x0f980, 0x0f981, 0x0f982, 0x0f983, 0x0f984, 0x0f985, 0x0f986, 0x0f987, 0x0f988, 0x0f989, 0x0f98a, 0x0f98b, 0x0f98c, 0x0f98d, 0x0f98e, 0x0f98f, 0x0f990, 0x0f991, 0x0f992, 0x0f993, 0x0f994, 0x0f995, 0x0f996, 0x0f997, 0x0f998, 0x0f999, 0x0f99a, 0x0f99b, 0x0f99c, 0x0f99d, 0x0f99e, 0x0f99f, 0x0f9a0, 0x0f9a1, 0x0f9a2, 0x0f9a3, 0x0f9a4, 0x0f9a5, 0x0f9a6, 0x0f9a7, 0x0f9a8, 0x0f9a9, 0x0f9aa, 0x0f9ab, 0x0f9ac, 0x0f9ad, 0x0f9ae, 0x0f9af, 0x0f9b0, 0x0f9b1, 0x0f9b2, 0x0f9b3, 0x0f9b4, 0x0f9b5, 0x0f9b6, 0x0f9b7, 0x0f9b8, 0x0f9b9, 0x0f9ba, 0x0f9bb, 0x0f9bc, 0x0f9bd, 0x0f9be, 0x0f9bf, 0x0f9c0, 0x0f9c1, 0x0f9c2, 0x0f9c3, 0x0f9c4, 0x0f9c5, 0x0f9c6, 0x0f9c7, 0x0f9c8, 0x0f9c9, 0x0f9ca, 0x0f9cb, 0x0f9cc, 0x0f9cd, 0x0f9ce, 0x0f9cf, 0x0f9d0, 0x0f9d1, 0x0f9d2, 0x0f9d3, 0x0f9d4, 0x0f9d5, 0x0f9d6, 0x0f9d7, 0x0f9d8, 0x0f9d9, 0x0f9da, 0x0f9db, 0x0f9dc, 0x0f9dd, 0x0f9de, 0x0f9df, 0x0f9e0, 0x0f9e1, 0x0f9e2, 0x0f9e3, 0x0f9e4, 0x0f9e5, 0x0f9e6, 0x0f9e7, 0x0f9e8, 0x0f9e9, 0x0f9ea, 0x0f9eb, 0x0f9ec, 0x0f9ed, 0x0f9ee, 0x0f9ef, 0x0f9f0, 0x0f9f1, 0x0f9f2, 0x0f9f3, 0x0f9f4, 0x0f9f5, 0x0f9f6, 0x0f9f7, 0x0f9f8, 0x0f9f9, 0x0f9fa, 0x0f9fb, 0x0f9fc, 0x0f9fd, 0x0f9fe, 0x0f9ff, 0x0fa00, 0x0fa01, 0x0fa02, 0x0fa03, 0x0fa04, 0x0fa05, 0x0fa06, 0x0fa07, 0x0fa08, 0x0fa09, 0x0fa0a, 0x0fa0b, 0x0fa0c, 0x0fa0d, 0x0fa10, 0x0fa12, 0x0fa15, 0x0fa16, 0x0fa17, 0x0fa18, 0x0fa19, 0x0fa1a, 0x0fa1b, 0x0fa1c, 0x0fa1d, 0x0fa1e, 0x0fa20, 0x0fa22, 0x0fa25, 0x0fa26, 0x0fa2a, 0x0fa2b, 0x0fa2c, 0x0fa2d, 0x0fa2e, 0x0fa2f, 0x0fa30, 0x0fa31, 0x0fa32, 0x0fa33, 0x0fa34, 0x0fa35, 0x0fa36, 0x0fa37, 0x0fa38, 0x0fa39, 0x0fa3a, 0x0fa3b, 0x0fa3c, 0x0fa3d, 0x0fa3e, 0x0fa3f, 0x0fa40, 0x0fa41, 0x0fa42, 0x0fa43, 0x0fa44, 0x0fa45, 0x0fa46, 0x0fa47, 0x0fa48, 0x0fa49, 0x0fa4a, 0x0fa4b, 0x0fa4c, 0x0fa4d, 0x0fa4e, 0x0fa4f, 0x0fa50, 0x0fa51, 0x0fa52, 0x0fa53, 0x0fa54, 0x0fa55, 0x0fa56, 0x0fa57, 0x0fa58, 0x0fa59, 0x0fa5a, 0x0fa5b, 0x0fa5c, 0x0fa5d, 0x0fa5e, 0x0fa5f, 0x0fa60, 0x0fa61, 0x0fa62, 0x0fa63, 0x0fa64, 0x0fa65, 0x0fa66, 0x0fa67, 0x0fa68, 0x0fa69, 0x0fa6a, 0x0fa6b, 0x0fa6c, 0x0fa6d, 0x0fa70, 0x0fa71, 0x0fa72, 0x0fa73, 0x0fa74, 0x0fa75, 0x0fa76, 0x0fa77, 0x0fa78, 0x0fa79, 0x0fa7a, 0x0fa7b, 0x0fa7c, 0x0fa7d, 0x0fa7e, 0x0fa7f, 0x0fa80, 0x0fa81, 0x0fa82, 0x0fa83, 0x0fa84, 0x0fa85, 0x0fa86, 0x0fa87, 0x0fa88, 0x0fa89, 0x0fa8a, 0x0fa8b, 0x0fa8c, 0x0fa8d, 0x0fa8e, 0x0fa8f, 0x0fa90, 0x0fa91, 0x0fa92, 0x0fa93, 0x0fa94, 0x0fa95, 0x0fa96, 0x0fa97, 0x0fa98, 0x0fa99, 0x0fa9a, 0x0fa9b, 0x0fa9c, 0x0fa9d, 0x0fa9e, 0x0fa9f, 0x0faa0, 0x0faa1, 0x0faa2, 0x0faa3, 0x0faa4, 0x0faa5, 0x0faa6, 0x0faa7, 0x0faa8, 0x0faa9, 0x0faaa, 0x0faab, 0x0faac, 0x0faad, 0x0faae, 0x0faaf, 0x0fab0, 0x0fab1, 0x0fab2, 0x0fab3, 0x0fab4, 0x0fab5, 0x0fab6, 0x0fab7, 0x0fab8, 0x0fab9, 0x0faba, 0x0fabb, 0x0fabc, 0x0fabd, 0x0fabe, 0x0fabf, 0x0fac0, 0x0fac1, 0x0fac2, 0x0fac3, 0x0fac4, 0x0fac5, 0x0fac6, 0x0fac7, 0x0fac8, 0x0fac9, 0x0faca, 0x0facb, 0x0facc, 0x0facd, 0x0face, 0x0facf, 0x0fad0, 0x0fad1, 0x0fad2, 0x0fad3, 0x0fad4, 0x0fad5, 0x0fad6, 0x0fad7, 0x0fad8, 0x0fad9, 0x0fb20, 0x0fb21, 0x0fb22, 0x0fb23, 0x0fb24, 0x0fb25, 0x0fb26, 0x0fb27, 0x0fb28, 0x0fb29, 0x0fb50, 0x0fb51, 0x0fb52, 0x0fb53, 0x0fb54, 0x0fb55, 0x0fb56, 0x0fb57, 0x0fb58, 0x0fb59, 0x0fb5a, 0x0fb5b, 0x0fb5c, 0x0fb5d, 0x0fb5e, 0x0fb5f, 0x0fb60, 0x0fb61, 0x0fb62, 0x0fb63, 0x0fb64, 0x0fb65, 0x0fb66, 0x0fb67, 0x0fb68, 0x0fb69, 0x0fb6a, 0x0fb6b, 0x0fb6c, 0x0fb6d, 0x0fb6e, 0x0fb6f, 0x0fb70, 0x0fb71, 0x0fb72, 0x0fb73, 0x0fb74, 0x0fb75, 0x0fb76, 0x0fb77, 0x0fb78, 0x0fb79, 0x0fb7a, 0x0fb7b, 0x0fb7c, 0x0fb7d, 0x0fb7e, 0x0fb7f, 0x0fb80, 0x0fb81, 0x0fb82, 0x0fb83, 0x0fb84, 0x0fb85, 0x0fb86, 0x0fb87, 0x0fb88, 0x0fb89, 0x0fb8a, 0x0fb8b, 0x0fb8c, 0x0fb8d, 0x0fb8e, 0x0fb8f, 0x0fb90, 0x0fb91, 0x0fb92, 0x0fb93, 0x0fb94, 0x0fb95, 0x0fb96, 0x0fb97, 0x0fb98, 0x0fb99, 0x0fb9a, 0x0fb9b, 0x0fb9c, 0x0fb9d, 0x0fb9e, 0x0fb9f, 0x0fba0, 0x0fba1, 0x0fba2, 0x0fba3, 0x0fba4, 0x0fba5, 0x0fba6, 0x0fba7, 0x0fba8, 0x0fba9, 0x0fbaa, 0x0fbab, 0x0fbac, 0x0fbad, 0x0fbae, 0x0fbaf, 0x0fbb0, 0x0fbb1, 0x0fbd3, 0x0fbd4, 0x0fbd5, 0x0fbd6, 0x0fbd7, 0x0fbd8, 0x0fbd9, 0x0fbda, 0x0fbdb, 0x0fbdc, 0x0fbdd, 0x0fbde, 0x0fbdf, 0x0fbe0, 0x0fbe1, 0x0fbe2, 0x0fbe3, 0x0fbe4, 0x0fbe5, 0x0fbe6, 0x0fbe7, 0x0fbe8, 0x0fbe9, 0x0fbfc, 0x0fbfd, 0x0fbfe, 0x0fbff, 0x0fe10, 0x0fe11, 0x0fe12, 0x0fe13, 0x0fe14, 0x0fe15, 0x0fe16, 0x0fe17, 0x0fe18, 0x0fe19, 0x0fe30, 0x0fe31, 0x0fe32, 0x0fe33, 0x0fe34, 0x0fe35, 0x0fe36, 0x0fe37, 0x0fe38, 0x0fe39, 0x0fe3a, 0x0fe3b, 0x0fe3c, 0x0fe3d, 0x0fe3e, 0x0fe3f, 0x0fe40, 0x0fe41, 0x0fe42, 0x0fe43, 0x0fe44, 0x0fe47, 0x0fe48, 0x0fe49, 0x0fe4a, 0x0fe4b, 0x0fe4c, 0x0fe4d, 0x0fe4e, 0x0fe4f, 0x0fe50, 0x0fe51, 0x0fe52, 0x0fe54, 0x0fe55, 0x0fe56, 0x0fe57, 0x0fe58, 0x0fe59, 0x0fe5a, 0x0fe5b, 0x0fe5c, 0x0fe5d, 0x0fe5e, 0x0fe5f, 0x0fe60, 0x0fe61, 0x0fe62, 0x0fe63, 0x0fe64, 0x0fe65, 0x0fe66, 0x0fe68, 0x0fe69, 0x0fe6a, 0x0fe6b, 0x0fe80, 0x0fe81, 0x0fe82, 0x0fe83, 0x0fe84, 0x0fe85, 0x0fe86, 0x0fe87, 0x0fe88, 0x0fe89, 0x0fe8a, 0x0fe8b, 0x0fe8c, 0x0fe8d, 0x0fe8e, 0x0fe8f, 0x0fe90, 0x0fe91, 0x0fe92, 0x0fe93, 0x0fe94, 0x0fe95, 0x0fe96, 0x0fe97, 0x0fe98, 0x0fe99, 0x0fe9a, 0x0fe9b, 0x0fe9c, 0x0fe9d, 0x0fe9e, 0x0fe9f, 0x0fea0, 0x0fea1, 0x0fea2, 0x0fea3, 0x0fea4, 0x0fea5, 0x0fea6, 0x0fea7, 0x0fea8, 0x0fea9, 0x0feaa, 0x0feab, 0x0feac, 0x0fead, 0x0feae, 0x0feaf, 0x0feb0, 0x0feb1, 0x0feb2, 0x0feb3, 0x0feb4, 0x0feb5, 0x0feb6, 0x0feb7, 0x0feb8, 0x0feb9, 0x0feba, 0x0febb, 0x0febc, 0x0febd, 0x0febe, 0x0febf, 0x0fec0, 0x0fec1, 0x0fec2, 0x0fec3, 0x0fec4, 0x0fec5, 0x0fec6, 0x0fec7, 0x0fec8, 0x0fec9, 0x0feca, 0x0fecb, 0x0fecc, 0x0fecd, 0x0fece, 0x0fecf, 0x0fed0, 0x0fed1, 0x0fed2, 0x0fed3, 0x0fed4, 0x0fed5, 0x0fed6, 0x0fed7, 0x0fed8, 0x0fed9, 0x0feda, 0x0fedb, 0x0fedc, 0x0fedd, 0x0fede, 0x0fedf, 0x0fee0, 0x0fee1, 0x0fee2, 0x0fee3, 0x0fee4, 0x0fee5, 0x0fee6, 0x0fee7, 0x0fee8, 0x0fee9, 0x0feea, 0x0feeb, 0x0feec, 0x0feed, 0x0feee, 0x0feef, 0x0fef0, 0x0fef1, 0x0fef2, 0x0fef3, 0x0fef4, 0x0ff01, 0x0ff02, 0x0ff03, 0x0ff04, 0x0ff05, 0x0ff06, 0x0ff07, 0x0ff08, 0x0ff09, 0x0ff0a, 0x0ff0b, 0x0ff0c, 0x0ff0d, 0x0ff0e, 0x0ff0f, 0x0ff10, 0x0ff11, 0x0ff12, 0x0ff13, 0x0ff14, 0x0ff15, 0x0ff16, 0x0ff17, 0x0ff18, 0x0ff19, 0x0ff1a, 0x0ff1b, 0x0ff1c, 0x0ff1d, 0x0ff1e, 0x0ff1f, 0x0ff20, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a, 0x0ff3b, 0x0ff3c, 0x0ff3d, 0x0ff3e, 0x0ff3f, 0x0ff40, 0x0ff5b, 0x0ff5c, 0x0ff5d, 0x0ff5e, 0x0ff5f, 0x0ff60, 0x0ff61, 0x0ff62, 0x0ff63, 0x0ff64, 0x0ff65, 0x0ff66, 0x0ff67, 0x0ff68, 0x0ff69, 0x0ff6a, 0x0ff6b, 0x0ff6c, 0x0ff6d, 0x0ff6e, 0x0ff6f, 0x0ff70, 0x0ff71, 0x0ff72, 0x0ff73, 0x0ff74, 0x0ff75, 0x0ff76, 0x0ff77, 0x0ff78, 0x0ff79, 0x0ff7a, 0x0ff7b, 0x0ff7c, 0x0ff7d, 0x0ff7e, 0x0ff7f, 0x0ff80, 0x0ff81, 0x0ff82, 0x0ff83, 0x0ff84, 0x0ff85, 0x0ff86, 0x0ff87, 0x0ff88, 0x0ff89, 0x0ff8a, 0x0ff8b, 0x0ff8c, 0x0ff8d, 0x0ff8e, 0x0ff8f, 0x0ff90, 0x0ff91, 0x0ff92, 0x0ff93, 0x0ff94, 0x0ff95, 0x0ff96, 0x0ff97, 0x0ff98, 0x0ff99, 0x0ff9a, 0x0ff9b, 0x0ff9c, 0x0ff9d, 0x0ff9e, 0x0ff9f, 0x0ffa0, 0x0ffa1, 0x0ffa2, 0x0ffa3, 0x0ffa4, 0x0ffa5, 0x0ffa6, 0x0ffa7, 0x0ffa8, 0x0ffa9, 0x0ffaa, 0x0ffab, 0x0ffac, 0x0ffad, 0x0ffae, 0x0ffaf, 0x0ffb0, 0x0ffb1, 0x0ffb2, 0x0ffb3, 0x0ffb4, 0x0ffb5, 0x0ffb6, 0x0ffb7, 0x0ffb8, 0x0ffb9, 0x0ffba, 0x0ffbb, 0x0ffbc, 0x0ffbd, 0x0ffbe, 0x0ffc2, 0x0ffc3, 0x0ffc4, 0x0ffc5, 0x0ffc6, 0x0ffc7, 0x0ffca, 0x0ffcb, 0x0ffcc, 0x0ffcd, 0x0ffce, 0x0ffcf, 0x0ffd2, 0x0ffd3, 0x0ffd4, 0x0ffd5, 0x0ffd6, 0x0ffd7, 0x0ffda, 0x0ffdb, 0x0ffdc, 0x0ffe0, 0x0ffe1, 0x0ffe2, 0x0ffe3, 0x0ffe4, 0x0ffe5, 0x0ffe6, 0x0ffe8, 0x0ffe9, 0x0ffea, 0x0ffeb, 0x0ffec, 0x0ffed, 0x0ffee }; static const uint32_t uni16_decomp_values[] = { 0x00068, 0x00266, 0x0006a, 0x00072, 0x00279, 0x0027b, 0x00281, 0x00077, 0x00079, 0x00263, 0x0006c, 0x00073, 0x00078, 0x00295, 0x00300, 0x00301, 0x00313, 0x002b9, 0x0003b, 0x000b7, 0x003a5, 0x00398, 0x003a3, 0x00f0b, 0x010dc, 0x00041, 0x000c6, 0x00042, 0x00044, 0x00045, 0x0018e, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00222, 0x00050, 0x00052, 0x00054, 0x00055, 0x00057, 0x00061, 0x00250, 0x00251, 0x01d02, 0x00062, 0x00064, 0x00065, 0x00259, 0x0025b, 0x0025c, 0x00067, 0x0006b, 0x0006d, 0x0014b, 0x0006f, 0x00254, 0x01d16, 0x01d17, 0x00070, 0x00074, 0x00075, 0x01d1d, 0x0026f, 0x00076, 0x01d25, 0x003b2, 0x003b3, 0x003b4, 0x003c6, 0x003c7, 0x00069, 0x00072, 0x00075, 0x00076, 0x003b2, 0x003b3, 0x003c1, 0x003c6, 0x003c7, 0x0043d, 0x00252, 0x00063, 0x00255, 0x000f0, 0x0025c, 0x00066, 0x0025f, 0x00261, 0x00265, 0x00268, 0x00269, 0x0026a, 0x01d7b, 0x0029d, 0x0026d, 0x01d85, 0x0029f, 0x00271, 0x00270, 0x00272, 0x00273, 0x00274, 0x00275, 0x00278, 0x00282, 0x00283, 0x001ab, 0x00289, 0x0028a, 0x01d1c, 0x0028b, 0x0028c, 0x0007a, 0x00290, 0x00291, 0x00292, 0x003b8, 0x00386, 0x00388, 0x00389, 0x00390, 0x0038a, 0x003b0, 0x0038e, 0x00385, 0x00060, 0x0038c, 0x0038f, 0x000b4, 0x02002, 0x02003, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x02010, 0x0002e, 0x00020, 0x00020, 0x00030, 0x00069, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0002b, 0x02212, 0x0003d, 0x00028, 0x00029, 0x0006e, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0002b, 0x02212, 0x0003d, 0x00028, 0x00029, 0x00061, 0x00065, 0x0006f, 0x00078, 0x00259, 0x00068, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x00070, 0x00073, 0x00074, 0x00043, 0x00190, 0x00067, 0x00048, 0x00048, 0x00048, 0x00068, 0x00127, 0x00049, 0x00049, 0x0004c, 0x0006c, 0x0004e, 0x00050, 0x00051, 0x00052, 0x00052, 0x00052, 0x0005a, 0x003a9, 0x0005a, 0x0004b, 0x000c5, 0x00042, 0x00043, 0x00065, 0x00045, 0x00046, 0x0004d, 0x0006f, 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x00069, 0x003c0, 0x003b3, 0x00393, 0x003a0, 0x02211, 0x00044, 0x00064, 0x00065, 0x00069, 0x0006a, 0x00049, 0x00056, 0x00058, 0x0004c, 0x00043, 0x00044, 0x0004d, 0x03008, 0x03009, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00030, 0x0006a, 0x00056, 0x02d61, 0x06bcd, 0x09f9f, 0x04e00, 0x04e28, 0x04e36, 0x04e3f, 0x04e59, 0x04e85, 0x04e8c, 0x04ea0, 0x04eba, 0x0513f, 0x05165, 0x0516b, 0x05182, 0x05196, 0x051ab, 0x051e0, 0x051f5, 0x05200, 0x0529b, 0x052f9, 0x05315, 0x0531a, 0x05338, 0x05341, 0x0535c, 0x05369, 0x05382, 0x053b6, 0x053c8, 0x053e3, 0x056d7, 0x0571f, 0x058eb, 0x05902, 0x0590a, 0x05915, 0x05927, 0x05973, 0x05b50, 0x05b80, 0x05bf8, 0x05c0f, 0x05c22, 0x05c38, 0x05c6e, 0x05c71, 0x05ddb, 0x05de5, 0x05df1, 0x05dfe, 0x05e72, 0x05e7a, 0x05e7f, 0x05ef4, 0x05efe, 0x05f0b, 0x05f13, 0x05f50, 0x05f61, 0x05f73, 0x05fc3, 0x06208, 0x06236, 0x0624b, 0x0652f, 0x06534, 0x06587, 0x06597, 0x065a4, 0x065b9, 0x065e0, 0x065e5, 0x066f0, 0x06708, 0x06728, 0x06b20, 0x06b62, 0x06b79, 0x06bb3, 0x06bcb, 0x06bd4, 0x06bdb, 0x06c0f, 0x06c14, 0x06c34, 0x0706b, 0x0722a, 0x07236, 0x0723b, 0x0723f, 0x07247, 0x07259, 0x0725b, 0x072ac, 0x07384, 0x07389, 0x074dc, 0x074e6, 0x07518, 0x0751f, 0x07528, 0x07530, 0x0758b, 0x07592, 0x07676, 0x0767d, 0x076ae, 0x076bf, 0x076ee, 0x077db, 0x077e2, 0x077f3, 0x0793a, 0x079b8, 0x079be, 0x07a74, 0x07acb, 0x07af9, 0x07c73, 0x07cf8, 0x07f36, 0x07f51, 0x07f8a, 0x07fbd, 0x08001, 0x0800c, 0x08012, 0x08033, 0x0807f, 0x08089, 0x081e3, 0x081ea, 0x081f3, 0x081fc, 0x0820c, 0x0821b, 0x0821f, 0x0826e, 0x08272, 0x08278, 0x0864d, 0x0866b, 0x08840, 0x0884c, 0x08863, 0x0897e, 0x0898b, 0x089d2, 0x08a00, 0x08c37, 0x08c46, 0x08c55, 0x08c78, 0x08c9d, 0x08d64, 0x08d70, 0x08db3, 0x08eab, 0x08eca, 0x08f9b, 0x08fb0, 0x08fb5, 0x09091, 0x09149, 0x091c6, 0x091cc, 0x091d1, 0x09577, 0x09580, 0x0961c, 0x096b6, 0x096b9, 0x096e8, 0x09751, 0x0975e, 0x09762, 0x09769, 0x097cb, 0x097ed, 0x097f3, 0x09801, 0x098a8, 0x098db, 0x098df, 0x09996, 0x09999, 0x099ac, 0x09aa8, 0x09ad8, 0x09adf, 0x09b25, 0x09b2f, 0x09b32, 0x09b3c, 0x09b5a, 0x09ce5, 0x09e75, 0x09e7f, 0x09ea5, 0x09ebb, 0x09ec3, 0x09ecd, 0x09ed1, 0x09ef9, 0x09efd, 0x09f0e, 0x09f13, 0x09f20, 0x09f3b, 0x09f4a, 0x09f52, 0x09f8d, 0x09f9c, 0x09fa0, 0x00020, 0x03012, 0x05341, 0x05344, 0x05345, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, 0x01103, 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, 0x011b5, 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, 0x0110b, 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x01161, 0x01162, 0x01163, 0x01164, 0x01165, 0x01166, 0x01167, 0x01168, 0x01169, 0x0116a, 0x0116b, 0x0116c, 0x0116d, 0x0116e, 0x0116f, 0x01170, 0x01171, 0x01172, 0x01173, 0x01174, 0x01175, 0x01160, 0x01114, 0x01115, 0x011c7, 0x011c8, 0x011cc, 0x011ce, 0x011d3, 0x011d7, 0x011d9, 0x0111c, 0x011dd, 0x011df, 0x0111d, 0x0111e, 0x01120, 0x01122, 0x01123, 0x01127, 0x01129, 0x0112b, 0x0112c, 0x0112d, 0x0112e, 0x0112f, 0x01132, 0x01136, 0x01140, 0x01147, 0x0114c, 0x011f1, 0x011f2, 0x01157, 0x01158, 0x01159, 0x01184, 0x01185, 0x01188, 0x01191, 0x01192, 0x01194, 0x0119e, 0x011a1, 0x04e00, 0x04e8c, 0x04e09, 0x056db, 0x04e0a, 0x04e2d, 0x04e0b, 0x07532, 0x04e59, 0x04e19, 0x04e01, 0x05929, 0x05730, 0x04eba, 0x0554f, 0x05e7c, 0x06587, 0x07b8f, 0x01100, 0x01102, 0x01103, 0x01105, 0x01106, 0x01107, 0x01109, 0x0110b, 0x0110c, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x04e00, 0x04e8c, 0x04e09, 0x056db, 0x04e94, 0x0516d, 0x04e03, 0x0516b, 0x04e5d, 0x05341, 0x06708, 0x0706b, 0x06c34, 0x06728, 0x091d1, 0x0571f, 0x065e5, 0x0682a, 0x06709, 0x0793e, 0x0540d, 0x07279, 0x08ca1, 0x0795d, 0x052b4, 0x079d8, 0x07537, 0x05973, 0x09069, 0x0512a, 0x05370, 0x06ce8, 0x09805, 0x04f11, 0x05199, 0x06b63, 0x04e0a, 0x04e2d, 0x04e0b, 0x05de6, 0x053f3, 0x0533b, 0x05b97, 0x05b66, 0x076e3, 0x04f01, 0x08cc7, 0x05354, 0x0591c, 0x030a2, 0x030a4, 0x030a6, 0x030a8, 0x030aa, 0x030ab, 0x030ad, 0x030af, 0x030b1, 0x030b3, 0x030b5, 0x030b7, 0x030b9, 0x030bb, 0x030bd, 0x030bf, 0x030c1, 0x030c4, 0x030c6, 0x030c8, 0x030ca, 0x030cb, 0x030cc, 0x030cd, 0x030ce, 0x030cf, 0x030d2, 0x030d5, 0x030d8, 0x030db, 0x030de, 0x030df, 0x030e0, 0x030e1, 0x030e2, 0x030e4, 0x030e6, 0x030e8, 0x030e9, 0x030ea, 0x030eb, 0x030ec, 0x030ed, 0x030ef, 0x030f0, 0x030f1, 0x030f2, 0x0044a, 0x0044c, 0x0a76f, 0x00126, 0x00153, 0x0a727, 0x0ab37, 0x0026b, 0x0ab52, 0x08c48, 0x066f4, 0x08eca, 0x08cc8, 0x06ed1, 0x04e32, 0x053e5, 0x09f9c, 0x09f9c, 0x05951, 0x091d1, 0x05587, 0x05948, 0x061f6, 0x07669, 0x07f85, 0x0863f, 0x087ba, 0x088f8, 0x0908f, 0x06a02, 0x06d1b, 0x070d9, 0x073de, 0x0843d, 0x0916a, 0x099f1, 0x04e82, 0x05375, 0x06b04, 0x0721b, 0x0862d, 0x09e1e, 0x05d50, 0x06feb, 0x085cd, 0x08964, 0x062c9, 0x081d8, 0x0881f, 0x05eca, 0x06717, 0x06d6a, 0x072fc, 0x090ce, 0x04f86, 0x051b7, 0x052de, 0x064c4, 0x06ad3, 0x07210, 0x076e7, 0x08001, 0x08606, 0x0865c, 0x08def, 0x09732, 0x09b6f, 0x09dfa, 0x0788c, 0x0797f, 0x07da0, 0x083c9, 0x09304, 0x09e7f, 0x08ad6, 0x058df, 0x05f04, 0x07c60, 0x0807e, 0x07262, 0x078ca, 0x08cc2, 0x096f7, 0x058d8, 0x05c62, 0x06a13, 0x06dda, 0x06f0f, 0x07d2f, 0x07e37, 0x0964b, 0x052d2, 0x0808b, 0x051dc, 0x051cc, 0x07a1c, 0x07dbe, 0x083f1, 0x09675, 0x08b80, 0x062cf, 0x06a02, 0x08afe, 0x04e39, 0x05be7, 0x06012, 0x07387, 0x07570, 0x05317, 0x078fb, 0x04fbf, 0x05fa9, 0x04e0d, 0x06ccc, 0x06578, 0x07d22, 0x053c3, 0x0585e, 0x07701, 0x08449, 0x08aaa, 0x06bba, 0x08fb0, 0x06c88, 0x062fe, 0x082e5, 0x063a0, 0x07565, 0x04eae, 0x05169, 0x051c9, 0x06881, 0x07ce7, 0x0826f, 0x08ad2, 0x091cf, 0x052f5, 0x05442, 0x05973, 0x05eec, 0x065c5, 0x06ffe, 0x0792a, 0x095ad, 0x09a6a, 0x09e97, 0x09ece, 0x0529b, 0x066c6, 0x06b77, 0x08f62, 0x05e74, 0x06190, 0x06200, 0x0649a, 0x06f23, 0x07149, 0x07489, 0x079ca, 0x07df4, 0x0806f, 0x08f26, 0x084ee, 0x09023, 0x0934a, 0x05217, 0x052a3, 0x054bd, 0x070c8, 0x088c2, 0x08aaa, 0x05ec9, 0x05ff5, 0x0637b, 0x06bae, 0x07c3e, 0x07375, 0x04ee4, 0x056f9, 0x05be7, 0x05dba, 0x0601c, 0x073b2, 0x07469, 0x07f9a, 0x08046, 0x09234, 0x096f6, 0x09748, 0x09818, 0x04f8b, 0x079ae, 0x091b4, 0x096b8, 0x060e1, 0x04e86, 0x050da, 0x05bee, 0x05c3f, 0x06599, 0x06a02, 0x071ce, 0x07642, 0x084fc, 0x0907c, 0x09f8d, 0x06688, 0x0962e, 0x05289, 0x0677b, 0x067f3, 0x06d41, 0x06e9c, 0x07409, 0x07559, 0x0786b, 0x07d10, 0x0985e, 0x0516d, 0x0622e, 0x09678, 0x0502b, 0x05d19, 0x06dea, 0x08f2a, 0x05f8b, 0x06144, 0x06817, 0x07387, 0x09686, 0x05229, 0x0540f, 0x05c65, 0x06613, 0x0674e, 0x068a8, 0x06ce5, 0x07406, 0x075e2, 0x07f79, 0x088cf, 0x088e1, 0x091cc, 0x096e2, 0x0533f, 0x06eba, 0x0541d, 0x071d0, 0x07498, 0x085fa, 0x096a3, 0x09c57, 0x09e9f, 0x06797, 0x06dcb, 0x081e8, 0x07acb, 0x07b20, 0x07c92, 0x072c0, 0x07099, 0x08b58, 0x04ec0, 0x08336, 0x0523a, 0x05207, 0x05ea6, 0x062d3, 0x07cd6, 0x05b85, 0x06d1e, 0x066b4, 0x08f3b, 0x0884c, 0x0964d, 0x0898b, 0x05ed3, 0x05140, 0x055c0, 0x0585a, 0x06674, 0x051de, 0x0732a, 0x076ca, 0x0793c, 0x0795e, 0x07965, 0x0798f, 0x09756, 0x07cbe, 0x07fbd, 0x08612, 0x08af8, 0x09038, 0x090fd, 0x098ef, 0x098fc, 0x09928, 0x09db4, 0x090de, 0x096b7, 0x04fae, 0x050e7, 0x0514d, 0x052c9, 0x052e4, 0x05351, 0x0559d, 0x05606, 0x05668, 0x05840, 0x058a8, 0x05c64, 0x05c6e, 0x06094, 0x06168, 0x0618e, 0x061f2, 0x0654f, 0x065e2, 0x06691, 0x06885, 0x06d77, 0x06e1a, 0x06f22, 0x0716e, 0x0722b, 0x07422, 0x07891, 0x0793e, 0x07949, 0x07948, 0x07950, 0x07956, 0x0795d, 0x0798d, 0x0798e, 0x07a40, 0x07a81, 0x07bc0, 0x07df4, 0x07e09, 0x07e41, 0x07f72, 0x08005, 0x081ed, 0x08279, 0x08279, 0x08457, 0x08910, 0x08996, 0x08b01, 0x08b39, 0x08cd3, 0x08d08, 0x08fb6, 0x09038, 0x096e3, 0x097ff, 0x0983b, 0x06075, 0x242ee, 0x08218, 0x04e26, 0x051b5, 0x05168, 0x04f80, 0x05145, 0x05180, 0x052c7, 0x052fa, 0x0559d, 0x05555, 0x05599, 0x055e2, 0x0585a, 0x058b3, 0x05944, 0x05954, 0x05a62, 0x05b28, 0x05ed2, 0x05ed9, 0x05f69, 0x05fad, 0x060d8, 0x0614e, 0x06108, 0x0618e, 0x06160, 0x061f2, 0x06234, 0x063c4, 0x0641c, 0x06452, 0x06556, 0x06674, 0x06717, 0x0671b, 0x06756, 0x06b79, 0x06bba, 0x06d41, 0x06edb, 0x06ecb, 0x06f22, 0x0701e, 0x0716e, 0x077a7, 0x07235, 0x072af, 0x0732a, 0x07471, 0x07506, 0x0753b, 0x0761d, 0x0761f, 0x076ca, 0x076db, 0x076f4, 0x0774a, 0x07740, 0x078cc, 0x07ab1, 0x07bc0, 0x07c7b, 0x07d5b, 0x07df4, 0x07f3e, 0x08005, 0x08352, 0x083ef, 0x08779, 0x08941, 0x08986, 0x08996, 0x08abf, 0x08af8, 0x08acb, 0x08b01, 0x08afe, 0x08aed, 0x08b39, 0x08b8a, 0x08d08, 0x08f38, 0x09072, 0x09199, 0x09276, 0x0967c, 0x096e3, 0x09756, 0x097db, 0x097ff, 0x0980b, 0x0983b, 0x09b12, 0x09f9c, 0x2284a, 0x22844, 0x233d5, 0x03b9d, 0x04018, 0x04039, 0x25249, 0x25cd0, 0x27ed3, 0x09f43, 0x09f8e, 0x005e2, 0x005d0, 0x005d3, 0x005d4, 0x005db, 0x005dc, 0x005dd, 0x005e8, 0x005ea, 0x0002b, 0x00671, 0x00671, 0x0067b, 0x0067b, 0x0067b, 0x0067b, 0x0067e, 0x0067e, 0x0067e, 0x0067e, 0x00680, 0x00680, 0x00680, 0x00680, 0x0067a, 0x0067a, 0x0067a, 0x0067a, 0x0067f, 0x0067f, 0x0067f, 0x0067f, 0x00679, 0x00679, 0x00679, 0x00679, 0x006a4, 0x006a4, 0x006a4, 0x006a4, 0x006a6, 0x006a6, 0x006a6, 0x006a6, 0x00684, 0x00684, 0x00684, 0x00684, 0x00683, 0x00683, 0x00683, 0x00683, 0x00686, 0x00686, 0x00686, 0x00686, 0x00687, 0x00687, 0x00687, 0x00687, 0x0068d, 0x0068d, 0x0068c, 0x0068c, 0x0068e, 0x0068e, 0x00688, 0x00688, 0x00698, 0x00698, 0x00691, 0x00691, 0x006a9, 0x006a9, 0x006a9, 0x006a9, 0x006af, 0x006af, 0x006af, 0x006af, 0x006b3, 0x006b3, 0x006b3, 0x006b3, 0x006b1, 0x006b1, 0x006b1, 0x006b1, 0x006ba, 0x006ba, 0x006bb, 0x006bb, 0x006bb, 0x006bb, 0x006c0, 0x006c0, 0x006c1, 0x006c1, 0x006c1, 0x006c1, 0x006be, 0x006be, 0x006be, 0x006be, 0x006d2, 0x006d2, 0x006d3, 0x006d3, 0x006ad, 0x006ad, 0x006ad, 0x006ad, 0x006c7, 0x006c7, 0x006c6, 0x006c6, 0x006c8, 0x006c8, 0x00677, 0x006cb, 0x006cb, 0x006c5, 0x006c5, 0x006c9, 0x006c9, 0x006d0, 0x006d0, 0x006d0, 0x006d0, 0x00649, 0x00649, 0x006cc, 0x006cc, 0x006cc, 0x006cc, 0x0002c, 0x03001, 0x03002, 0x0003a, 0x0003b, 0x00021, 0x0003f, 0x03016, 0x03017, 0x02026, 0x02025, 0x02014, 0x02013, 0x0005f, 0x0005f, 0x00028, 0x00029, 0x0007b, 0x0007d, 0x03014, 0x03015, 0x03010, 0x03011, 0x0300a, 0x0300b, 0x03008, 0x03009, 0x0300c, 0x0300d, 0x0300e, 0x0300f, 0x0005b, 0x0005d, 0x0203e, 0x0203e, 0x0203e, 0x0203e, 0x0005f, 0x0005f, 0x0005f, 0x0002c, 0x03001, 0x0002e, 0x0003b, 0x0003a, 0x0003f, 0x00021, 0x02014, 0x00028, 0x00029, 0x0007b, 0x0007d, 0x03014, 0x03015, 0x00023, 0x00026, 0x0002a, 0x0002b, 0x0002d, 0x0003c, 0x0003e, 0x0003d, 0x0005c, 0x00024, 0x00025, 0x00040, 0x00621, 0x00622, 0x00622, 0x00623, 0x00623, 0x00624, 0x00624, 0x00625, 0x00625, 0x00626, 0x00626, 0x00626, 0x00626, 0x00627, 0x00627, 0x00628, 0x00628, 0x00628, 0x00628, 0x00629, 0x00629, 0x0062a, 0x0062a, 0x0062a, 0x0062a, 0x0062b, 0x0062b, 0x0062b, 0x0062b, 0x0062c, 0x0062c, 0x0062c, 0x0062c, 0x0062d, 0x0062d, 0x0062d, 0x0062d, 0x0062e, 0x0062e, 0x0062e, 0x0062e, 0x0062f, 0x0062f, 0x00630, 0x00630, 0x00631, 0x00631, 0x00632, 0x00632, 0x00633, 0x00633, 0x00633, 0x00633, 0x00634, 0x00634, 0x00634, 0x00634, 0x00635, 0x00635, 0x00635, 0x00635, 0x00636, 0x00636, 0x00636, 0x00636, 0x00637, 0x00637, 0x00637, 0x00637, 0x00638, 0x00638, 0x00638, 0x00638, 0x00639, 0x00639, 0x00639, 0x00639, 0x0063a, 0x0063a, 0x0063a, 0x0063a, 0x00641, 0x00641, 0x00641, 0x00641, 0x00642, 0x00642, 0x00642, 0x00642, 0x00643, 0x00643, 0x00643, 0x00643, 0x00644, 0x00644, 0x00644, 0x00644, 0x00645, 0x00645, 0x00645, 0x00645, 0x00646, 0x00646, 0x00646, 0x00646, 0x00647, 0x00647, 0x00647, 0x00647, 0x00648, 0x00648, 0x00649, 0x00649, 0x0064a, 0x0064a, 0x0064a, 0x0064a, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027, 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f, 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, 0x00060, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x02985, 0x02986, 0x03002, 0x0300c, 0x0300d, 0x03001, 0x030fb, 0x030f2, 0x030a1, 0x030a3, 0x030a5, 0x030a7, 0x030a9, 0x030e3, 0x030e5, 0x030e7, 0x030c3, 0x030fc, 0x030a2, 0x030a4, 0x030a6, 0x030a8, 0x030aa, 0x030ab, 0x030ad, 0x030af, 0x030b1, 0x030b3, 0x030b5, 0x030b7, 0x030b9, 0x030bb, 0x030bd, 0x030bf, 0x030c1, 0x030c4, 0x030c6, 0x030c8, 0x030ca, 0x030cb, 0x030cc, 0x030cd, 0x030ce, 0x030cf, 0x030d2, 0x030d5, 0x030d8, 0x030db, 0x030de, 0x030df, 0x030e0, 0x030e1, 0x030e2, 0x030e4, 0x030e6, 0x030e8, 0x030e9, 0x030ea, 0x030eb, 0x030ec, 0x030ed, 0x030ef, 0x030f3, 0x03099, 0x0309a, 0x03164, 0x03131, 0x03132, 0x03133, 0x03134, 0x03135, 0x03136, 0x03137, 0x03138, 0x03139, 0x0313a, 0x0313b, 0x0313c, 0x0313d, 0x0313e, 0x0313f, 0x03140, 0x03141, 0x03142, 0x03143, 0x03144, 0x03145, 0x03146, 0x03147, 0x03148, 0x03149, 0x0314a, 0x0314b, 0x0314c, 0x0314d, 0x0314e, 0x0314f, 0x03150, 0x03151, 0x03152, 0x03153, 0x03154, 0x03155, 0x03156, 0x03157, 0x03158, 0x03159, 0x0315a, 0x0315b, 0x0315c, 0x0315d, 0x0315e, 0x0315f, 0x03160, 0x03161, 0x03162, 0x03163, 0x000a2, 0x000a3, 0x000ac, 0x000af, 0x000a6, 0x000a5, 0x020a9, 0x02502, 0x02190, 0x02191, 0x02192, 0x02193, 0x025a0, 0x025cb }; static const uint32_t uni32_decomp_keys[] = { 0x1d400, 0x1d401, 0x1d402, 0x1d403, 0x1d404, 0x1d405, 0x1d406, 0x1d407, 0x1d408, 0x1d409, 0x1d40a, 0x1d40b, 0x1d40c, 0x1d40d, 0x1d40e, 0x1d40f, 0x1d410, 0x1d411, 0x1d412, 0x1d413, 0x1d414, 0x1d415, 0x1d416, 0x1d417, 0x1d418, 0x1d419, 0x1d41a, 0x1d41b, 0x1d41c, 0x1d41d, 0x1d41e, 0x1d41f, 0x1d420, 0x1d421, 0x1d422, 0x1d423, 0x1d424, 0x1d425, 0x1d426, 0x1d427, 0x1d428, 0x1d429, 0x1d42a, 0x1d42b, 0x1d42c, 0x1d42d, 0x1d42e, 0x1d42f, 0x1d430, 0x1d431, 0x1d432, 0x1d433, 0x1d434, 0x1d435, 0x1d436, 0x1d437, 0x1d438, 0x1d439, 0x1d43a, 0x1d43b, 0x1d43c, 0x1d43d, 0x1d43e, 0x1d43f, 0x1d440, 0x1d441, 0x1d442, 0x1d443, 0x1d444, 0x1d445, 0x1d446, 0x1d447, 0x1d448, 0x1d449, 0x1d44a, 0x1d44b, 0x1d44c, 0x1d44d, 0x1d44e, 0x1d44f, 0x1d450, 0x1d451, 0x1d452, 0x1d453, 0x1d454, 0x1d456, 0x1d457, 0x1d458, 0x1d459, 0x1d45a, 0x1d45b, 0x1d45c, 0x1d45d, 0x1d45e, 0x1d45f, 0x1d460, 0x1d461, 0x1d462, 0x1d463, 0x1d464, 0x1d465, 0x1d466, 0x1d467, 0x1d468, 0x1d469, 0x1d46a, 0x1d46b, 0x1d46c, 0x1d46d, 0x1d46e, 0x1d46f, 0x1d470, 0x1d471, 0x1d472, 0x1d473, 0x1d474, 0x1d475, 0x1d476, 0x1d477, 0x1d478, 0x1d479, 0x1d47a, 0x1d47b, 0x1d47c, 0x1d47d, 0x1d47e, 0x1d47f, 0x1d480, 0x1d481, 0x1d482, 0x1d483, 0x1d484, 0x1d485, 0x1d486, 0x1d487, 0x1d488, 0x1d489, 0x1d48a, 0x1d48b, 0x1d48c, 0x1d48d, 0x1d48e, 0x1d48f, 0x1d490, 0x1d491, 0x1d492, 0x1d493, 0x1d494, 0x1d495, 0x1d496, 0x1d497, 0x1d498, 0x1d499, 0x1d49a, 0x1d49b, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a2, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4aa, 0x1d4ab, 0x1d4ac, 0x1d4ae, 0x1d4af, 0x1d4b0, 0x1d4b1, 0x1d4b2, 0x1d4b3, 0x1d4b4, 0x1d4b5, 0x1d4b6, 0x1d4b7, 0x1d4b8, 0x1d4b9, 0x1d4bb, 0x1d4bd, 0x1d4be, 0x1d4bf, 0x1d4c0, 0x1d4c1, 0x1d4c2, 0x1d4c3, 0x1d4c5, 0x1d4c6, 0x1d4c7, 0x1d4c8, 0x1d4c9, 0x1d4ca, 0x1d4cb, 0x1d4cc, 0x1d4cd, 0x1d4ce, 0x1d4cf, 0x1d4d0, 0x1d4d1, 0x1d4d2, 0x1d4d3, 0x1d4d4, 0x1d4d5, 0x1d4d6, 0x1d4d7, 0x1d4d8, 0x1d4d9, 0x1d4da, 0x1d4db, 0x1d4dc, 0x1d4dd, 0x1d4de, 0x1d4df, 0x1d4e0, 0x1d4e1, 0x1d4e2, 0x1d4e3, 0x1d4e4, 0x1d4e5, 0x1d4e6, 0x1d4e7, 0x1d4e8, 0x1d4e9, 0x1d4ea, 0x1d4eb, 0x1d4ec, 0x1d4ed, 0x1d4ee, 0x1d4ef, 0x1d4f0, 0x1d4f1, 0x1d4f2, 0x1d4f3, 0x1d4f4, 0x1d4f5, 0x1d4f6, 0x1d4f7, 0x1d4f8, 0x1d4f9, 0x1d4fa, 0x1d4fb, 0x1d4fc, 0x1d4fd, 0x1d4fe, 0x1d4ff, 0x1d500, 0x1d501, 0x1d502, 0x1d503, 0x1d504, 0x1d505, 0x1d507, 0x1d508, 0x1d509, 0x1d50a, 0x1d50d, 0x1d50e, 0x1d50f, 0x1d510, 0x1d511, 0x1d512, 0x1d513, 0x1d514, 0x1d516, 0x1d517, 0x1d518, 0x1d519, 0x1d51a, 0x1d51b, 0x1d51c, 0x1d51e, 0x1d51f, 0x1d520, 0x1d521, 0x1d522, 0x1d523, 0x1d524, 0x1d525, 0x1d526, 0x1d527, 0x1d528, 0x1d529, 0x1d52a, 0x1d52b, 0x1d52c, 0x1d52d, 0x1d52e, 0x1d52f, 0x1d530, 0x1d531, 0x1d532, 0x1d533, 0x1d534, 0x1d535, 0x1d536, 0x1d537, 0x1d538, 0x1d539, 0x1d53b, 0x1d53c, 0x1d53d, 0x1d53e, 0x1d540, 0x1d541, 0x1d542, 0x1d543, 0x1d544, 0x1d546, 0x1d54a, 0x1d54b, 0x1d54c, 0x1d54d, 0x1d54e, 0x1d54f, 0x1d550, 0x1d552, 0x1d553, 0x1d554, 0x1d555, 0x1d556, 0x1d557, 0x1d558, 0x1d559, 0x1d55a, 0x1d55b, 0x1d55c, 0x1d55d, 0x1d55e, 0x1d55f, 0x1d560, 0x1d561, 0x1d562, 0x1d563, 0x1d564, 0x1d565, 0x1d566, 0x1d567, 0x1d568, 0x1d569, 0x1d56a, 0x1d56b, 0x1d56c, 0x1d56d, 0x1d56e, 0x1d56f, 0x1d570, 0x1d571, 0x1d572, 0x1d573, 0x1d574, 0x1d575, 0x1d576, 0x1d577, 0x1d578, 0x1d579, 0x1d57a, 0x1d57b, 0x1d57c, 0x1d57d, 0x1d57e, 0x1d57f, 0x1d580, 0x1d581, 0x1d582, 0x1d583, 0x1d584, 0x1d585, 0x1d586, 0x1d587, 0x1d588, 0x1d589, 0x1d58a, 0x1d58b, 0x1d58c, 0x1d58d, 0x1d58e, 0x1d58f, 0x1d590, 0x1d591, 0x1d592, 0x1d593, 0x1d594, 0x1d595, 0x1d596, 0x1d597, 0x1d598, 0x1d599, 0x1d59a, 0x1d59b, 0x1d59c, 0x1d59d, 0x1d59e, 0x1d59f, 0x1d5a0, 0x1d5a1, 0x1d5a2, 0x1d5a3, 0x1d5a4, 0x1d5a5, 0x1d5a6, 0x1d5a7, 0x1d5a8, 0x1d5a9, 0x1d5aa, 0x1d5ab, 0x1d5ac, 0x1d5ad, 0x1d5ae, 0x1d5af, 0x1d5b0, 0x1d5b1, 0x1d5b2, 0x1d5b3, 0x1d5b4, 0x1d5b5, 0x1d5b6, 0x1d5b7, 0x1d5b8, 0x1d5b9, 0x1d5ba, 0x1d5bb, 0x1d5bc, 0x1d5bd, 0x1d5be, 0x1d5bf, 0x1d5c0, 0x1d5c1, 0x1d5c2, 0x1d5c3, 0x1d5c4, 0x1d5c5, 0x1d5c6, 0x1d5c7, 0x1d5c8, 0x1d5c9, 0x1d5ca, 0x1d5cb, 0x1d5cc, 0x1d5cd, 0x1d5ce, 0x1d5cf, 0x1d5d0, 0x1d5d1, 0x1d5d2, 0x1d5d3, 0x1d5d4, 0x1d5d5, 0x1d5d6, 0x1d5d7, 0x1d5d8, 0x1d5d9, 0x1d5da, 0x1d5db, 0x1d5dc, 0x1d5dd, 0x1d5de, 0x1d5df, 0x1d5e0, 0x1d5e1, 0x1d5e2, 0x1d5e3, 0x1d5e4, 0x1d5e5, 0x1d5e6, 0x1d5e7, 0x1d5e8, 0x1d5e9, 0x1d5ea, 0x1d5eb, 0x1d5ec, 0x1d5ed, 0x1d5ee, 0x1d5ef, 0x1d5f0, 0x1d5f1, 0x1d5f2, 0x1d5f3, 0x1d5f4, 0x1d5f5, 0x1d5f6, 0x1d5f7, 0x1d5f8, 0x1d5f9, 0x1d5fa, 0x1d5fb, 0x1d5fc, 0x1d5fd, 0x1d5fe, 0x1d5ff, 0x1d600, 0x1d601, 0x1d602, 0x1d603, 0x1d604, 0x1d605, 0x1d606, 0x1d607, 0x1d608, 0x1d609, 0x1d60a, 0x1d60b, 0x1d60c, 0x1d60d, 0x1d60e, 0x1d60f, 0x1d610, 0x1d611, 0x1d612, 0x1d613, 0x1d614, 0x1d615, 0x1d616, 0x1d617, 0x1d618, 0x1d619, 0x1d61a, 0x1d61b, 0x1d61c, 0x1d61d, 0x1d61e, 0x1d61f, 0x1d620, 0x1d621, 0x1d622, 0x1d623, 0x1d624, 0x1d625, 0x1d626, 0x1d627, 0x1d628, 0x1d629, 0x1d62a, 0x1d62b, 0x1d62c, 0x1d62d, 0x1d62e, 0x1d62f, 0x1d630, 0x1d631, 0x1d632, 0x1d633, 0x1d634, 0x1d635, 0x1d636, 0x1d637, 0x1d638, 0x1d639, 0x1d63a, 0x1d63b, 0x1d63c, 0x1d63d, 0x1d63e, 0x1d63f, 0x1d640, 0x1d641, 0x1d642, 0x1d643, 0x1d644, 0x1d645, 0x1d646, 0x1d647, 0x1d648, 0x1d649, 0x1d64a, 0x1d64b, 0x1d64c, 0x1d64d, 0x1d64e, 0x1d64f, 0x1d650, 0x1d651, 0x1d652, 0x1d653, 0x1d654, 0x1d655, 0x1d656, 0x1d657, 0x1d658, 0x1d659, 0x1d65a, 0x1d65b, 0x1d65c, 0x1d65d, 0x1d65e, 0x1d65f, 0x1d660, 0x1d661, 0x1d662, 0x1d663, 0x1d664, 0x1d665, 0x1d666, 0x1d667, 0x1d668, 0x1d669, 0x1d66a, 0x1d66b, 0x1d66c, 0x1d66d, 0x1d66e, 0x1d66f, 0x1d670, 0x1d671, 0x1d672, 0x1d673, 0x1d674, 0x1d675, 0x1d676, 0x1d677, 0x1d678, 0x1d679, 0x1d67a, 0x1d67b, 0x1d67c, 0x1d67d, 0x1d67e, 0x1d67f, 0x1d680, 0x1d681, 0x1d682, 0x1d683, 0x1d684, 0x1d685, 0x1d686, 0x1d687, 0x1d688, 0x1d689, 0x1d68a, 0x1d68b, 0x1d68c, 0x1d68d, 0x1d68e, 0x1d68f, 0x1d690, 0x1d691, 0x1d692, 0x1d693, 0x1d694, 0x1d695, 0x1d696, 0x1d697, 0x1d698, 0x1d699, 0x1d69a, 0x1d69b, 0x1d69c, 0x1d69d, 0x1d69e, 0x1d69f, 0x1d6a0, 0x1d6a1, 0x1d6a2, 0x1d6a3, 0x1d6a4, 0x1d6a5, 0x1d6a8, 0x1d6a9, 0x1d6aa, 0x1d6ab, 0x1d6ac, 0x1d6ad, 0x1d6ae, 0x1d6af, 0x1d6b0, 0x1d6b1, 0x1d6b2, 0x1d6b3, 0x1d6b4, 0x1d6b5, 0x1d6b6, 0x1d6b7, 0x1d6b8, 0x1d6b9, 0x1d6ba, 0x1d6bb, 0x1d6bc, 0x1d6bd, 0x1d6be, 0x1d6bf, 0x1d6c0, 0x1d6c1, 0x1d6c2, 0x1d6c3, 0x1d6c4, 0x1d6c5, 0x1d6c6, 0x1d6c7, 0x1d6c8, 0x1d6c9, 0x1d6ca, 0x1d6cb, 0x1d6cc, 0x1d6cd, 0x1d6ce, 0x1d6cf, 0x1d6d0, 0x1d6d1, 0x1d6d2, 0x1d6d3, 0x1d6d4, 0x1d6d5, 0x1d6d6, 0x1d6d7, 0x1d6d8, 0x1d6d9, 0x1d6da, 0x1d6db, 0x1d6dc, 0x1d6dd, 0x1d6de, 0x1d6df, 0x1d6e0, 0x1d6e1, 0x1d6e2, 0x1d6e3, 0x1d6e4, 0x1d6e5, 0x1d6e6, 0x1d6e7, 0x1d6e8, 0x1d6e9, 0x1d6ea, 0x1d6eb, 0x1d6ec, 0x1d6ed, 0x1d6ee, 0x1d6ef, 0x1d6f0, 0x1d6f1, 0x1d6f2, 0x1d6f3, 0x1d6f4, 0x1d6f5, 0x1d6f6, 0x1d6f7, 0x1d6f8, 0x1d6f9, 0x1d6fa, 0x1d6fb, 0x1d6fc, 0x1d6fd, 0x1d6fe, 0x1d6ff, 0x1d700, 0x1d701, 0x1d702, 0x1d703, 0x1d704, 0x1d705, 0x1d706, 0x1d707, 0x1d708, 0x1d709, 0x1d70a, 0x1d70b, 0x1d70c, 0x1d70d, 0x1d70e, 0x1d70f, 0x1d710, 0x1d711, 0x1d712, 0x1d713, 0x1d714, 0x1d715, 0x1d716, 0x1d717, 0x1d718, 0x1d719, 0x1d71a, 0x1d71b, 0x1d71c, 0x1d71d, 0x1d71e, 0x1d71f, 0x1d720, 0x1d721, 0x1d722, 0x1d723, 0x1d724, 0x1d725, 0x1d726, 0x1d727, 0x1d728, 0x1d729, 0x1d72a, 0x1d72b, 0x1d72c, 0x1d72d, 0x1d72e, 0x1d72f, 0x1d730, 0x1d731, 0x1d732, 0x1d733, 0x1d734, 0x1d735, 0x1d736, 0x1d737, 0x1d738, 0x1d739, 0x1d73a, 0x1d73b, 0x1d73c, 0x1d73d, 0x1d73e, 0x1d73f, 0x1d740, 0x1d741, 0x1d742, 0x1d743, 0x1d744, 0x1d745, 0x1d746, 0x1d747, 0x1d748, 0x1d749, 0x1d74a, 0x1d74b, 0x1d74c, 0x1d74d, 0x1d74e, 0x1d74f, 0x1d750, 0x1d751, 0x1d752, 0x1d753, 0x1d754, 0x1d755, 0x1d756, 0x1d757, 0x1d758, 0x1d759, 0x1d75a, 0x1d75b, 0x1d75c, 0x1d75d, 0x1d75e, 0x1d75f, 0x1d760, 0x1d761, 0x1d762, 0x1d763, 0x1d764, 0x1d765, 0x1d766, 0x1d767, 0x1d768, 0x1d769, 0x1d76a, 0x1d76b, 0x1d76c, 0x1d76d, 0x1d76e, 0x1d76f, 0x1d770, 0x1d771, 0x1d772, 0x1d773, 0x1d774, 0x1d775, 0x1d776, 0x1d777, 0x1d778, 0x1d779, 0x1d77a, 0x1d77b, 0x1d77c, 0x1d77d, 0x1d77e, 0x1d77f, 0x1d780, 0x1d781, 0x1d782, 0x1d783, 0x1d784, 0x1d785, 0x1d786, 0x1d787, 0x1d788, 0x1d789, 0x1d78a, 0x1d78b, 0x1d78c, 0x1d78d, 0x1d78e, 0x1d78f, 0x1d790, 0x1d791, 0x1d792, 0x1d793, 0x1d794, 0x1d795, 0x1d796, 0x1d797, 0x1d798, 0x1d799, 0x1d79a, 0x1d79b, 0x1d79c, 0x1d79d, 0x1d79e, 0x1d79f, 0x1d7a0, 0x1d7a1, 0x1d7a2, 0x1d7a3, 0x1d7a4, 0x1d7a5, 0x1d7a6, 0x1d7a7, 0x1d7a8, 0x1d7a9, 0x1d7aa, 0x1d7ab, 0x1d7ac, 0x1d7ad, 0x1d7ae, 0x1d7af, 0x1d7b0, 0x1d7b1, 0x1d7b2, 0x1d7b3, 0x1d7b4, 0x1d7b5, 0x1d7b6, 0x1d7b7, 0x1d7b8, 0x1d7b9, 0x1d7ba, 0x1d7bb, 0x1d7bc, 0x1d7bd, 0x1d7be, 0x1d7bf, 0x1d7c0, 0x1d7c1, 0x1d7c2, 0x1d7c3, 0x1d7c4, 0x1d7c5, 0x1d7c6, 0x1d7c7, 0x1d7c8, 0x1d7c9, 0x1d7ca, 0x1d7cb, 0x1d7ce, 0x1d7cf, 0x1d7d0, 0x1d7d1, 0x1d7d2, 0x1d7d3, 0x1d7d4, 0x1d7d5, 0x1d7d6, 0x1d7d7, 0x1d7d8, 0x1d7d9, 0x1d7da, 0x1d7db, 0x1d7dc, 0x1d7dd, 0x1d7de, 0x1d7df, 0x1d7e0, 0x1d7e1, 0x1d7e2, 0x1d7e3, 0x1d7e4, 0x1d7e5, 0x1d7e6, 0x1d7e7, 0x1d7e8, 0x1d7e9, 0x1d7ea, 0x1d7eb, 0x1d7ec, 0x1d7ed, 0x1d7ee, 0x1d7ef, 0x1d7f0, 0x1d7f1, 0x1d7f2, 0x1d7f3, 0x1d7f4, 0x1d7f5, 0x1d7f6, 0x1d7f7, 0x1d7f8, 0x1d7f9, 0x1d7fa, 0x1d7fb, 0x1d7fc, 0x1d7fd, 0x1d7fe, 0x1d7ff, 0x1ee00, 0x1ee01, 0x1ee02, 0x1ee03, 0x1ee05, 0x1ee06, 0x1ee07, 0x1ee08, 0x1ee09, 0x1ee0a, 0x1ee0b, 0x1ee0c, 0x1ee0d, 0x1ee0e, 0x1ee0f, 0x1ee10, 0x1ee11, 0x1ee12, 0x1ee13, 0x1ee14, 0x1ee15, 0x1ee16, 0x1ee17, 0x1ee18, 0x1ee19, 0x1ee1a, 0x1ee1b, 0x1ee1c, 0x1ee1d, 0x1ee1e, 0x1ee1f, 0x1ee21, 0x1ee22, 0x1ee24, 0x1ee27, 0x1ee29, 0x1ee2a, 0x1ee2b, 0x1ee2c, 0x1ee2d, 0x1ee2e, 0x1ee2f, 0x1ee30, 0x1ee31, 0x1ee32, 0x1ee34, 0x1ee35, 0x1ee36, 0x1ee37, 0x1ee39, 0x1ee3b, 0x1ee42, 0x1ee47, 0x1ee49, 0x1ee4b, 0x1ee4d, 0x1ee4e, 0x1ee4f, 0x1ee51, 0x1ee52, 0x1ee54, 0x1ee57, 0x1ee59, 0x1ee5b, 0x1ee5d, 0x1ee5f, 0x1ee61, 0x1ee62, 0x1ee64, 0x1ee67, 0x1ee68, 0x1ee69, 0x1ee6a, 0x1ee6c, 0x1ee6d, 0x1ee6e, 0x1ee6f, 0x1ee70, 0x1ee71, 0x1ee72, 0x1ee74, 0x1ee75, 0x1ee76, 0x1ee77, 0x1ee79, 0x1ee7a, 0x1ee7b, 0x1ee7c, 0x1ee7e, 0x1ee80, 0x1ee81, 0x1ee82, 0x1ee83, 0x1ee84, 0x1ee85, 0x1ee86, 0x1ee87, 0x1ee88, 0x1ee89, 0x1ee8b, 0x1ee8c, 0x1ee8d, 0x1ee8e, 0x1ee8f, 0x1ee90, 0x1ee91, 0x1ee92, 0x1ee93, 0x1ee94, 0x1ee95, 0x1ee96, 0x1ee97, 0x1ee98, 0x1ee99, 0x1ee9a, 0x1ee9b, 0x1eea1, 0x1eea2, 0x1eea3, 0x1eea5, 0x1eea6, 0x1eea7, 0x1eea8, 0x1eea9, 0x1eeab, 0x1eeac, 0x1eead, 0x1eeae, 0x1eeaf, 0x1eeb0, 0x1eeb1, 0x1eeb2, 0x1eeb3, 0x1eeb4, 0x1eeb5, 0x1eeb6, 0x1eeb7, 0x1eeb8, 0x1eeb9, 0x1eeba, 0x1eebb, 0x1f12b, 0x1f12c, 0x1f130, 0x1f131, 0x1f132, 0x1f133, 0x1f134, 0x1f135, 0x1f136, 0x1f137, 0x1f138, 0x1f139, 0x1f13a, 0x1f13b, 0x1f13c, 0x1f13d, 0x1f13e, 0x1f13f, 0x1f140, 0x1f141, 0x1f142, 0x1f143, 0x1f144, 0x1f145, 0x1f146, 0x1f147, 0x1f148, 0x1f149, 0x1f202, 0x1f210, 0x1f211, 0x1f212, 0x1f213, 0x1f214, 0x1f215, 0x1f216, 0x1f217, 0x1f218, 0x1f219, 0x1f21a, 0x1f21b, 0x1f21c, 0x1f21d, 0x1f21e, 0x1f21f, 0x1f220, 0x1f221, 0x1f222, 0x1f223, 0x1f224, 0x1f225, 0x1f226, 0x1f227, 0x1f228, 0x1f229, 0x1f22a, 0x1f22b, 0x1f22c, 0x1f22d, 0x1f22e, 0x1f22f, 0x1f230, 0x1f231, 0x1f232, 0x1f233, 0x1f234, 0x1f235, 0x1f236, 0x1f237, 0x1f238, 0x1f239, 0x1f23a, 0x1f23b, 0x1f250, 0x1f251, 0x2f800, 0x2f801, 0x2f802, 0x2f803, 0x2f804, 0x2f805, 0x2f806, 0x2f807, 0x2f808, 0x2f809, 0x2f80a, 0x2f80b, 0x2f80c, 0x2f80d, 0x2f80e, 0x2f80f, 0x2f810, 0x2f811, 0x2f812, 0x2f813, 0x2f814, 0x2f815, 0x2f816, 0x2f817, 0x2f818, 0x2f819, 0x2f81a, 0x2f81b, 0x2f81c, 0x2f81d, 0x2f81e, 0x2f81f, 0x2f820, 0x2f821, 0x2f822, 0x2f823, 0x2f824, 0x2f825, 0x2f826, 0x2f827, 0x2f828, 0x2f829, 0x2f82a, 0x2f82b, 0x2f82c, 0x2f82d, 0x2f82e, 0x2f82f, 0x2f830, 0x2f831, 0x2f832, 0x2f833, 0x2f834, 0x2f835, 0x2f836, 0x2f837, 0x2f838, 0x2f839, 0x2f83a, 0x2f83b, 0x2f83c, 0x2f83d, 0x2f83e, 0x2f83f, 0x2f840, 0x2f841, 0x2f842, 0x2f843, 0x2f844, 0x2f845, 0x2f846, 0x2f847, 0x2f848, 0x2f849, 0x2f84a, 0x2f84b, 0x2f84c, 0x2f84d, 0x2f84e, 0x2f84f, 0x2f850, 0x2f851, 0x2f852, 0x2f853, 0x2f854, 0x2f855, 0x2f856, 0x2f857, 0x2f858, 0x2f859, 0x2f85a, 0x2f85b, 0x2f85c, 0x2f85d, 0x2f85e, 0x2f85f, 0x2f860, 0x2f861, 0x2f862, 0x2f863, 0x2f864, 0x2f865, 0x2f866, 0x2f867, 0x2f868, 0x2f869, 0x2f86a, 0x2f86b, 0x2f86c, 0x2f86d, 0x2f86e, 0x2f86f, 0x2f870, 0x2f871, 0x2f872, 0x2f873, 0x2f874, 0x2f875, 0x2f876, 0x2f877, 0x2f878, 0x2f879, 0x2f87a, 0x2f87b, 0x2f87c, 0x2f87d, 0x2f87e, 0x2f87f, 0x2f880, 0x2f881, 0x2f882, 0x2f883, 0x2f884, 0x2f885, 0x2f886, 0x2f887, 0x2f888, 0x2f889, 0x2f88a, 0x2f88b, 0x2f88c, 0x2f88d, 0x2f88e, 0x2f88f, 0x2f890, 0x2f891, 0x2f892, 0x2f893, 0x2f894, 0x2f895, 0x2f896, 0x2f897, 0x2f898, 0x2f899, 0x2f89a, 0x2f89b, 0x2f89c, 0x2f89d, 0x2f89e, 0x2f89f, 0x2f8a0, 0x2f8a1, 0x2f8a2, 0x2f8a3, 0x2f8a4, 0x2f8a5, 0x2f8a6, 0x2f8a7, 0x2f8a8, 0x2f8a9, 0x2f8aa, 0x2f8ab, 0x2f8ac, 0x2f8ad, 0x2f8ae, 0x2f8af, 0x2f8b0, 0x2f8b1, 0x2f8b2, 0x2f8b3, 0x2f8b4, 0x2f8b5, 0x2f8b6, 0x2f8b7, 0x2f8b8, 0x2f8b9, 0x2f8ba, 0x2f8bb, 0x2f8bc, 0x2f8bd, 0x2f8be, 0x2f8bf, 0x2f8c0, 0x2f8c1, 0x2f8c2, 0x2f8c3, 0x2f8c4, 0x2f8c5, 0x2f8c6, 0x2f8c7, 0x2f8c8, 0x2f8c9, 0x2f8ca, 0x2f8cb, 0x2f8cc, 0x2f8cd, 0x2f8ce, 0x2f8cf, 0x2f8d0, 0x2f8d1, 0x2f8d2, 0x2f8d3, 0x2f8d4, 0x2f8d5, 0x2f8d6, 0x2f8d7, 0x2f8d8, 0x2f8d9, 0x2f8da, 0x2f8db, 0x2f8dc, 0x2f8dd, 0x2f8de, 0x2f8df, 0x2f8e0, 0x2f8e1, 0x2f8e2, 0x2f8e3, 0x2f8e4, 0x2f8e5, 0x2f8e6, 0x2f8e7, 0x2f8e8, 0x2f8e9, 0x2f8ea, 0x2f8eb, 0x2f8ec, 0x2f8ed, 0x2f8ee, 0x2f8ef, 0x2f8f0, 0x2f8f1, 0x2f8f2, 0x2f8f3, 0x2f8f4, 0x2f8f5, 0x2f8f6, 0x2f8f7, 0x2f8f8, 0x2f8f9, 0x2f8fa, 0x2f8fb, 0x2f8fc, 0x2f8fd, 0x2f8fe, 0x2f8ff, 0x2f900, 0x2f901, 0x2f902, 0x2f903, 0x2f904, 0x2f905, 0x2f906, 0x2f907, 0x2f908, 0x2f909, 0x2f90a, 0x2f90b, 0x2f90c, 0x2f90d, 0x2f90e, 0x2f90f, 0x2f910, 0x2f911, 0x2f912, 0x2f913, 0x2f914, 0x2f915, 0x2f916, 0x2f917, 0x2f918, 0x2f919, 0x2f91a, 0x2f91b, 0x2f91c, 0x2f91d, 0x2f91e, 0x2f91f, 0x2f920, 0x2f921, 0x2f922, 0x2f923, 0x2f924, 0x2f925, 0x2f926, 0x2f927, 0x2f928, 0x2f929, 0x2f92a, 0x2f92b, 0x2f92c, 0x2f92d, 0x2f92e, 0x2f92f, 0x2f930, 0x2f931, 0x2f932, 0x2f933, 0x2f934, 0x2f935, 0x2f936, 0x2f937, 0x2f938, 0x2f939, 0x2f93a, 0x2f93b, 0x2f93c, 0x2f93d, 0x2f93e, 0x2f93f, 0x2f940, 0x2f941, 0x2f942, 0x2f943, 0x2f944, 0x2f945, 0x2f946, 0x2f947, 0x2f948, 0x2f949, 0x2f94a, 0x2f94b, 0x2f94c, 0x2f94d, 0x2f94e, 0x2f94f, 0x2f950, 0x2f951, 0x2f952, 0x2f953, 0x2f954, 0x2f955, 0x2f956, 0x2f957, 0x2f958, 0x2f959, 0x2f95a, 0x2f95b, 0x2f95c, 0x2f95d, 0x2f95e, 0x2f95f, 0x2f960, 0x2f961, 0x2f962, 0x2f963, 0x2f964, 0x2f965, 0x2f966, 0x2f967, 0x2f968, 0x2f969, 0x2f96a, 0x2f96b, 0x2f96c, 0x2f96d, 0x2f96e, 0x2f96f, 0x2f970, 0x2f971, 0x2f972, 0x2f973, 0x2f974, 0x2f975, 0x2f976, 0x2f977, 0x2f978, 0x2f979, 0x2f97a, 0x2f97b, 0x2f97c, 0x2f97d, 0x2f97e, 0x2f97f, 0x2f980, 0x2f981, 0x2f982, 0x2f983, 0x2f984, 0x2f985, 0x2f986, 0x2f987, 0x2f988, 0x2f989, 0x2f98a, 0x2f98b, 0x2f98c, 0x2f98d, 0x2f98e, 0x2f98f, 0x2f990, 0x2f991, 0x2f992, 0x2f993, 0x2f994, 0x2f995, 0x2f996, 0x2f997, 0x2f998, 0x2f999, 0x2f99a, 0x2f99b, 0x2f99c, 0x2f99d, 0x2f99e, 0x2f99f, 0x2f9a0, 0x2f9a1, 0x2f9a2, 0x2f9a3, 0x2f9a4, 0x2f9a5, 0x2f9a6, 0x2f9a7, 0x2f9a8, 0x2f9a9, 0x2f9aa, 0x2f9ab, 0x2f9ac, 0x2f9ad, 0x2f9ae, 0x2f9af, 0x2f9b0, 0x2f9b1, 0x2f9b2, 0x2f9b3, 0x2f9b4, 0x2f9b5, 0x2f9b6, 0x2f9b7, 0x2f9b8, 0x2f9b9, 0x2f9ba, 0x2f9bb, 0x2f9bc, 0x2f9bd, 0x2f9be, 0x2f9bf, 0x2f9c0, 0x2f9c1, 0x2f9c2, 0x2f9c3, 0x2f9c4, 0x2f9c5, 0x2f9c6, 0x2f9c7, 0x2f9c8, 0x2f9c9, 0x2f9ca, 0x2f9cb, 0x2f9cc, 0x2f9cd, 0x2f9ce, 0x2f9cf, 0x2f9d0, 0x2f9d1, 0x2f9d2, 0x2f9d3, 0x2f9d4, 0x2f9d5, 0x2f9d6, 0x2f9d7, 0x2f9d8, 0x2f9d9, 0x2f9da, 0x2f9db, 0x2f9dc, 0x2f9dd, 0x2f9de, 0x2f9df, 0x2f9e0, 0x2f9e1, 0x2f9e2, 0x2f9e3, 0x2f9e4, 0x2f9e5, 0x2f9e6, 0x2f9e7, 0x2f9e8, 0x2f9e9, 0x2f9ea, 0x2f9eb, 0x2f9ec, 0x2f9ed, 0x2f9ee, 0x2f9ef, 0x2f9f0, 0x2f9f1, 0x2f9f2, 0x2f9f3, 0x2f9f4, 0x2f9f5, 0x2f9f6, 0x2f9f7, 0x2f9f8, 0x2f9f9, 0x2f9fa, 0x2f9fb, 0x2f9fc, 0x2f9fd, 0x2f9fe, 0x2f9ff, 0x2fa00, 0x2fa01, 0x2fa02, 0x2fa03, 0x2fa04, 0x2fa05, 0x2fa06, 0x2fa07, 0x2fa08, 0x2fa09, 0x2fa0a, 0x2fa0b, 0x2fa0c, 0x2fa0d, 0x2fa0e, 0x2fa0f, 0x2fa10, 0x2fa11, 0x2fa12, 0x2fa13, 0x2fa14, 0x2fa15, 0x2fa16, 0x2fa17, 0x2fa18, 0x2fa19, 0x2fa1a, 0x2fa1b, 0x2fa1c, 0x2fa1d }; static const uint32_t uni32_decomp_values[] = { 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00043, 0x00044, 0x00047, 0x0004a, 0x0004b, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00066, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00044, 0x00045, 0x00046, 0x00047, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00044, 0x00045, 0x00046, 0x00047, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004f, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00131, 0x00237, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x003dc, 0x003dd, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00627, 0x00628, 0x0062c, 0x0062f, 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, 0x00643, 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00631, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, 0x0063a, 0x0066e, 0x006ba, 0x006a1, 0x0066f, 0x00628, 0x0062c, 0x00647, 0x0062d, 0x0064a, 0x00643, 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00636, 0x0063a, 0x0062c, 0x0062d, 0x0064a, 0x00644, 0x00646, 0x00633, 0x00639, 0x00635, 0x00642, 0x00634, 0x0062e, 0x00636, 0x0063a, 0x006ba, 0x0066f, 0x00628, 0x0062c, 0x00647, 0x0062d, 0x00637, 0x0064a, 0x00643, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00636, 0x00638, 0x0063a, 0x0066e, 0x006a1, 0x00627, 0x00628, 0x0062c, 0x0062f, 0x00647, 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00631, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, 0x0063a, 0x00628, 0x0062c, 0x0062f, 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00631, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, 0x0063a, 0x00043, 0x00052, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x030b5, 0x0624b, 0x05b57, 0x053cc, 0x030c7, 0x04e8c, 0x0591a, 0x089e3, 0x05929, 0x04ea4, 0x06620, 0x07121, 0x06599, 0x0524d, 0x05f8c, 0x0518d, 0x065b0, 0x0521d, 0x07d42, 0x0751f, 0x08ca9, 0x058f0, 0x05439, 0x06f14, 0x06295, 0x06355, 0x04e00, 0x04e09, 0x0904a, 0x05de6, 0x04e2d, 0x053f3, 0x06307, 0x08d70, 0x06253, 0x07981, 0x07a7a, 0x05408, 0x06e80, 0x06709, 0x06708, 0x07533, 0x05272, 0x055b6, 0x0914d, 0x05f97, 0x053ef, 0x04e3d, 0x04e38, 0x04e41, 0x20122, 0x04f60, 0x04fae, 0x04fbb, 0x05002, 0x0507a, 0x05099, 0x050e7, 0x050cf, 0x0349e, 0x2063a, 0x0514d, 0x05154, 0x05164, 0x05177, 0x2051c, 0x034b9, 0x05167, 0x0518d, 0x2054b, 0x05197, 0x051a4, 0x04ecc, 0x051ac, 0x051b5, 0x291df, 0x051f5, 0x05203, 0x034df, 0x0523b, 0x05246, 0x05272, 0x05277, 0x03515, 0x052c7, 0x052c9, 0x052e4, 0x052fa, 0x05305, 0x05306, 0x05317, 0x05349, 0x05351, 0x0535a, 0x05373, 0x0537d, 0x0537f, 0x0537f, 0x0537f, 0x20a2c, 0x07070, 0x053ca, 0x053df, 0x20b63, 0x053eb, 0x053f1, 0x05406, 0x0549e, 0x05438, 0x05448, 0x05468, 0x054a2, 0x054f6, 0x05510, 0x05553, 0x05563, 0x05584, 0x05584, 0x05599, 0x055ab, 0x055b3, 0x055c2, 0x05716, 0x05606, 0x05717, 0x05651, 0x05674, 0x05207, 0x058ee, 0x057ce, 0x057f4, 0x0580d, 0x0578b, 0x05832, 0x05831, 0x058ac, 0x214e4, 0x058f2, 0x058f7, 0x05906, 0x0591a, 0x05922, 0x05962, 0x216a8, 0x216ea, 0x059ec, 0x05a1b, 0x05a27, 0x059d8, 0x05a66, 0x036ee, 0x036fc, 0x05b08, 0x05b3e, 0x05b3e, 0x219c8, 0x05bc3, 0x05bd8, 0x05be7, 0x05bf3, 0x21b18, 0x05bff, 0x05c06, 0x05f53, 0x05c22, 0x03781, 0x05c60, 0x05c6e, 0x05cc0, 0x05c8d, 0x21de4, 0x05d43, 0x21de6, 0x05d6e, 0x05d6b, 0x05d7c, 0x05de1, 0x05de2, 0x0382f, 0x05dfd, 0x05e28, 0x05e3d, 0x05e69, 0x03862, 0x22183, 0x0387c, 0x05eb0, 0x05eb3, 0x05eb6, 0x05eca, 0x2a392, 0x05efe, 0x22331, 0x22331, 0x08201, 0x05f22, 0x05f22, 0x038c7, 0x232b8, 0x261da, 0x05f62, 0x05f6b, 0x038e3, 0x05f9a, 0x05fcd, 0x05fd7, 0x05ff9, 0x06081, 0x0393a, 0x0391c, 0x06094, 0x226d4, 0x060c7, 0x06148, 0x0614c, 0x0614e, 0x0614c, 0x0617a, 0x0618e, 0x061b2, 0x061a4, 0x061af, 0x061de, 0x061f2, 0x061f6, 0x06210, 0x0621b, 0x0625d, 0x062b1, 0x062d4, 0x06350, 0x22b0c, 0x0633d, 0x062fc, 0x06368, 0x06383, 0x063e4, 0x22bf1, 0x06422, 0x063c5, 0x063a9, 0x03a2e, 0x06469, 0x0647e, 0x0649d, 0x06477, 0x03a6c, 0x0654f, 0x0656c, 0x2300a, 0x065e3, 0x066f8, 0x06649, 0x03b19, 0x06691, 0x03b08, 0x03ae4, 0x05192, 0x05195, 0x06700, 0x0669c, 0x080ad, 0x043d9, 0x06717, 0x0671b, 0x06721, 0x0675e, 0x06753, 0x233c3, 0x03b49, 0x067fa, 0x06785, 0x06852, 0x06885, 0x2346d, 0x0688e, 0x0681f, 0x06914, 0x03b9d, 0x06942, 0x069a3, 0x069ea, 0x06aa8, 0x236a3, 0x06adb, 0x03c18, 0x06b21, 0x238a7, 0x06b54, 0x03c4e, 0x06b72, 0x06b9f, 0x06bba, 0x06bbb, 0x23a8d, 0x21d0b, 0x23afa, 0x06c4e, 0x23cbc, 0x06cbf, 0x06ccd, 0x06c67, 0x06d16, 0x06d3e, 0x06d77, 0x06d41, 0x06d69, 0x06d78, 0x06d85, 0x23d1e, 0x06d34, 0x06e2f, 0x06e6e, 0x03d33, 0x06ecb, 0x06ec7, 0x23ed1, 0x06df9, 0x06f6e, 0x23f5e, 0x23f8e, 0x06fc6, 0x07039, 0x0701e, 0x0701b, 0x03d96, 0x0704a, 0x0707d, 0x07077, 0x070ad, 0x20525, 0x07145, 0x24263, 0x0719c, 0x243ab, 0x07228, 0x07235, 0x07250, 0x24608, 0x07280, 0x07295, 0x24735, 0x24814, 0x0737a, 0x0738b, 0x03eac, 0x073a5, 0x03eb8, 0x03eb8, 0x07447, 0x0745c, 0x07471, 0x07485, 0x074ca, 0x03f1b, 0x07524, 0x24c36, 0x0753e, 0x24c92, 0x07570, 0x2219f, 0x07610, 0x24fa1, 0x24fb8, 0x25044, 0x03ffc, 0x04008, 0x076f4, 0x250f3, 0x250f2, 0x25119, 0x25133, 0x0771e, 0x0771f, 0x0771f, 0x0774a, 0x04039, 0x0778b, 0x04046, 0x04096, 0x2541d, 0x0784e, 0x0788c, 0x078cc, 0x040e3, 0x25626, 0x07956, 0x2569a, 0x256c5, 0x0798f, 0x079eb, 0x0412f, 0x07a40, 0x07a4a, 0x07a4f, 0x2597c, 0x25aa7, 0x25aa7, 0x07aee, 0x04202, 0x25bab, 0x07bc6, 0x07bc9, 0x04227, 0x25c80, 0x07cd2, 0x042a0, 0x07ce8, 0x07ce3, 0x07d00, 0x25f86, 0x07d63, 0x04301, 0x07dc7, 0x07e02, 0x07e45, 0x04334, 0x26228, 0x26247, 0x04359, 0x262d9, 0x07f7a, 0x2633e, 0x07f95, 0x07ffa, 0x08005, 0x264da, 0x26523, 0x08060, 0x265a8, 0x08070, 0x2335f, 0x043d5, 0x080b2, 0x08103, 0x0440b, 0x0813e, 0x05ab5, 0x267a7, 0x267b5, 0x23393, 0x2339c, 0x08201, 0x08204, 0x08f9e, 0x0446b, 0x08291, 0x0828b, 0x0829d, 0x052b3, 0x082b1, 0x082b3, 0x082bd, 0x082e6, 0x26b3c, 0x082e5, 0x0831d, 0x08363, 0x083ad, 0x08323, 0x083bd, 0x083e7, 0x08457, 0x08353, 0x083ca, 0x083cc, 0x083dc, 0x26c36, 0x26d6b, 0x26cd5, 0x0452b, 0x084f1, 0x084f3, 0x08516, 0x273ca, 0x08564, 0x26f2c, 0x0455d, 0x04561, 0x26fb1, 0x270d2, 0x0456b, 0x08650, 0x0865c, 0x08667, 0x08669, 0x086a9, 0x08688, 0x0870e, 0x086e2, 0x08779, 0x08728, 0x0876b, 0x08786, 0x045d7, 0x087e1, 0x08801, 0x045f9, 0x08860, 0x08863, 0x27667, 0x088d7, 0x088de, 0x04635, 0x088fa, 0x034bb, 0x278ae, 0x27966, 0x046be, 0x046c7, 0x08aa0, 0x08aed, 0x08b8a, 0x08c55, 0x27ca8, 0x08cab, 0x08cc1, 0x08d1b, 0x08d77, 0x27f2f, 0x20804, 0x08dcb, 0x08dbc, 0x08df0, 0x208de, 0x08ed4, 0x08f38, 0x285d2, 0x285ed, 0x09094, 0x090f1, 0x09111, 0x2872e, 0x0911b, 0x09238, 0x092d7, 0x092d8, 0x0927c, 0x093f9, 0x09415, 0x28bfa, 0x0958b, 0x04995, 0x095b7, 0x28d77, 0x049e6, 0x096c3, 0x05db2, 0x09723, 0x29145, 0x2921a, 0x04a6e, 0x04a76, 0x097e0, 0x2940a, 0x04ab2, 0x29496, 0x0980b, 0x0980b, 0x09829, 0x295b6, 0x098e2, 0x04b33, 0x09929, 0x099a7, 0x099c2, 0x099fe, 0x04bce, 0x29b30, 0x09b12, 0x09c40, 0x09cfd, 0x04cce, 0x04ced, 0x09d67, 0x2a0ce, 0x04cf8, 0x2a105, 0x2a20e, 0x2a291, 0x09ebb, 0x04d56, 0x09ef9, 0x09efe, 0x09f05, 0x09f0f, 0x09f16, 0x09f3b, 0x2a600 }; static const uint32_t multidecomp_keys[] = { 0x000a8, 0x000af, 0x000b4, 0x000b8, 0x000bc, 0x000bd, 0x000be, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x00100, 0x00102, 0x00104, 0x00106, 0x00108, 0x0010a, 0x0010c, 0x0010e, 0x00112, 0x00114, 0x00116, 0x00118, 0x0011a, 0x0011c, 0x0011e, 0x00120, 0x00122, 0x00124, 0x00128, 0x0012a, 0x0012c, 0x0012e, 0x00130, 0x00132, 0x00134, 0x00136, 0x00139, 0x0013b, 0x0013d, 0x0013f, 0x00143, 0x00145, 0x00147, 0x00149, 0x0014c, 0x0014e, 0x00150, 0x00154, 0x00156, 0x00158, 0x0015a, 0x0015c, 0x0015e, 0x00160, 0x00162, 0x00164, 0x00168, 0x0016a, 0x0016c, 0x0016e, 0x00170, 0x00172, 0x00174, 0x00176, 0x00178, 0x00179, 0x0017b, 0x0017d, 0x001a0, 0x001af, 0x001cd, 0x001cf, 0x001d1, 0x001d3, 0x001d5, 0x001d7, 0x001d9, 0x001db, 0x001de, 0x001e0, 0x001e2, 0x001e6, 0x001e8, 0x001ea, 0x001ec, 0x001ee, 0x001f0, 0x001f4, 0x001f8, 0x001fa, 0x001fc, 0x001fe, 0x00200, 0x00202, 0x00204, 0x00206, 0x00208, 0x0020a, 0x0020c, 0x0020e, 0x00210, 0x00212, 0x00214, 0x00216, 0x00218, 0x0021a, 0x0021e, 0x00226, 0x00228, 0x0022a, 0x0022c, 0x0022e, 0x00230, 0x00232, 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x00344, 0x0037a, 0x00384, 0x00385, 0x00386, 0x00388, 0x00389, 0x0038a, 0x0038c, 0x0038e, 0x0038f, 0x00390, 0x003aa, 0x003ab, 0x003b0, 0x003d3, 0x003d4, 0x00400, 0x00401, 0x00403, 0x00407, 0x0040c, 0x0040d, 0x0040e, 0x00419, 0x00476, 0x004c1, 0x004d0, 0x004d2, 0x004d6, 0x004da, 0x004dc, 0x004de, 0x004e2, 0x004e4, 0x004e6, 0x004ea, 0x004ec, 0x004ee, 0x004f0, 0x004f2, 0x004f4, 0x004f8, 0x00587, 0x00622, 0x00623, 0x00624, 0x00625, 0x00626, 0x00675, 0x00676, 0x00677, 0x00678, 0x006c0, 0x006c2, 0x006d3, 0x00929, 0x00931, 0x00934, 0x00958, 0x00959, 0x0095a, 0x0095b, 0x0095c, 0x0095d, 0x0095e, 0x0095f, 0x009cb, 0x009cc, 0x009dc, 0x009dd, 0x009df, 0x00a33, 0x00a36, 0x00a59, 0x00a5a, 0x00a5b, 0x00a5e, 0x00b48, 0x00b4b, 0x00b4c, 0x00b5c, 0x00b5d, 0x00b94, 0x00bca, 0x00bcb, 0x00bcc, 0x00c48, 0x00cc0, 0x00cc7, 0x00cc8, 0x00cca, 0x00ccb, 0x00d4a, 0x00d4b, 0x00d4c, 0x00dda, 0x00ddc, 0x00ddd, 0x00dde, 0x00e33, 0x00eb3, 0x00edc, 0x00edd, 0x00f43, 0x00f4d, 0x00f52, 0x00f57, 0x00f5c, 0x00f69, 0x00f73, 0x00f75, 0x00f76, 0x00f77, 0x00f78, 0x00f79, 0x00f81, 0x00f93, 0x00f9d, 0x00fa2, 0x00fa7, 0x00fac, 0x00fb9, 0x01026, 0x01b06, 0x01b08, 0x01b0a, 0x01b0c, 0x01b0e, 0x01b12, 0x01b3b, 0x01b3d, 0x01b40, 0x01b41, 0x01b43, 0x01e00, 0x01e02, 0x01e04, 0x01e06, 0x01e08, 0x01e0a, 0x01e0c, 0x01e0e, 0x01e10, 0x01e12, 0x01e14, 0x01e16, 0x01e18, 0x01e1a, 0x01e1c, 0x01e1e, 0x01e20, 0x01e22, 0x01e24, 0x01e26, 0x01e28, 0x01e2a, 0x01e2c, 0x01e2e, 0x01e30, 0x01e32, 0x01e34, 0x01e36, 0x01e38, 0x01e3a, 0x01e3c, 0x01e3e, 0x01e40, 0x01e42, 0x01e44, 0x01e46, 0x01e48, 0x01e4a, 0x01e4c, 0x01e4e, 0x01e50, 0x01e52, 0x01e54, 0x01e56, 0x01e58, 0x01e5a, 0x01e5c, 0x01e5e, 0x01e60, 0x01e62, 0x01e64, 0x01e66, 0x01e68, 0x01e6a, 0x01e6c, 0x01e6e, 0x01e70, 0x01e72, 0x01e74, 0x01e76, 0x01e78, 0x01e7a, 0x01e7c, 0x01e7e, 0x01e80, 0x01e82, 0x01e84, 0x01e86, 0x01e88, 0x01e8a, 0x01e8c, 0x01e8e, 0x01e90, 0x01e92, 0x01e94, 0x01e96, 0x01e97, 0x01e98, 0x01e99, 0x01e9a, 0x01ea0, 0x01ea2, 0x01ea4, 0x01ea6, 0x01ea8, 0x01eaa, 0x01eac, 0x01eae, 0x01eb0, 0x01eb2, 0x01eb4, 0x01eb6, 0x01eb8, 0x01eba, 0x01ebc, 0x01ebe, 0x01ec0, 0x01ec2, 0x01ec4, 0x01ec6, 0x01ec8, 0x01eca, 0x01ecc, 0x01ece, 0x01ed0, 0x01ed2, 0x01ed4, 0x01ed6, 0x01ed8, 0x01eda, 0x01edc, 0x01ede, 0x01ee0, 0x01ee2, 0x01ee4, 0x01ee6, 0x01ee8, 0x01eea, 0x01eec, 0x01eee, 0x01ef0, 0x01ef2, 0x01ef4, 0x01ef6, 0x01ef8, 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, 0x01f0d, 0x01f0e, 0x01f0f, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, 0x01f1d, 0x01f28, 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, 0x01f2f, 0x01f38, 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, 0x01f3f, 0x01f48, 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f50, 0x01f52, 0x01f54, 0x01f56, 0x01f59, 0x01f5b, 0x01f5d, 0x01f5f, 0x01f68, 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, 0x01f6f, 0x01f88, 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f98, 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01fa8, 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fb2, 0x01fb4, 0x01fb6, 0x01fb7, 0x01fb8, 0x01fb9, 0x01fba, 0x01fbc, 0x01fbd, 0x01fbf, 0x01fc0, 0x01fc1, 0x01fc2, 0x01fc4, 0x01fc6, 0x01fc7, 0x01fc8, 0x01fca, 0x01fcc, 0x01fcd, 0x01fce, 0x01fcf, 0x01fd2, 0x01fd6, 0x01fd7, 0x01fd8, 0x01fd9, 0x01fda, 0x01fdd, 0x01fde, 0x01fdf, 0x01fe2, 0x01fe4, 0x01fe6, 0x01fe7, 0x01fe8, 0x01fe9, 0x01fea, 0x01fec, 0x01fed, 0x01ff2, 0x01ff4, 0x01ff6, 0x01ff7, 0x01ff8, 0x01ffa, 0x01ffc, 0x01ffe, 0x02017, 0x02025, 0x02026, 0x02033, 0x02034, 0x02036, 0x02037, 0x0203c, 0x0203e, 0x02047, 0x02048, 0x02049, 0x02057, 0x020a8, 0x02100, 0x02101, 0x02103, 0x02105, 0x02106, 0x02109, 0x02116, 0x02120, 0x02121, 0x02122, 0x0213b, 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157, 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f, 0x02161, 0x02162, 0x02163, 0x02165, 0x02166, 0x02167, 0x02168, 0x0216a, 0x0216b, 0x02189, 0x0219a, 0x0219b, 0x021ae, 0x021cd, 0x021ce, 0x021cf, 0x02204, 0x02209, 0x0220c, 0x02224, 0x02226, 0x0222c, 0x0222d, 0x0222f, 0x02230, 0x02241, 0x02244, 0x02247, 0x02249, 0x02260, 0x02262, 0x0226d, 0x0226e, 0x0226f, 0x02270, 0x02271, 0x02274, 0x02275, 0x02278, 0x02279, 0x02280, 0x02281, 0x02284, 0x02285, 0x02288, 0x02289, 0x022ac, 0x022ad, 0x022ae, 0x022af, 0x022e0, 0x022e1, 0x022e2, 0x022e3, 0x022ea, 0x022eb, 0x022ec, 0x022ed, 0x02469, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e, 0x0246f, 0x02470, 0x02471, 0x02472, 0x02473, 0x02474, 0x02475, 0x02476, 0x02477, 0x02478, 0x02479, 0x0247a, 0x0247b, 0x0247c, 0x0247d, 0x0247e, 0x0247f, 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, 0x02487, 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, 0x0248f, 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, 0x02497, 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, 0x0249f, 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, 0x024a7, 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, 0x024af, 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x02a0c, 0x02a74, 0x02a75, 0x02a76, 0x02adc, 0x0304c, 0x0304e, 0x03050, 0x03052, 0x03054, 0x03056, 0x03058, 0x0305a, 0x0305c, 0x0305e, 0x03060, 0x03062, 0x03065, 0x03067, 0x03069, 0x03070, 0x03071, 0x03073, 0x03074, 0x03076, 0x03077, 0x03079, 0x0307a, 0x0307c, 0x0307d, 0x03094, 0x0309b, 0x0309c, 0x0309e, 0x0309f, 0x030ac, 0x030ae, 0x030b0, 0x030b2, 0x030b4, 0x030b6, 0x030b8, 0x030ba, 0x030bc, 0x030be, 0x030c0, 0x030c2, 0x030c5, 0x030c7, 0x030c9, 0x030d0, 0x030d1, 0x030d3, 0x030d4, 0x030d6, 0x030d7, 0x030d9, 0x030da, 0x030dc, 0x030dd, 0x030f4, 0x030f7, 0x030f8, 0x030f9, 0x030fa, 0x030fe, 0x030ff, 0x03200, 0x03201, 0x03202, 0x03203, 0x03204, 0x03205, 0x03206, 0x03207, 0x03208, 0x03209, 0x0320a, 0x0320b, 0x0320c, 0x0320d, 0x0320e, 0x0320f, 0x03210, 0x03211, 0x03212, 0x03213, 0x03214, 0x03215, 0x03216, 0x03217, 0x03218, 0x03219, 0x0321a, 0x0321b, 0x0321c, 0x0321d, 0x0321e, 0x03220, 0x03221, 0x03222, 0x03223, 0x03224, 0x03225, 0x03226, 0x03227, 0x03228, 0x03229, 0x0322a, 0x0322b, 0x0322c, 0x0322d, 0x0322e, 0x0322f, 0x03230, 0x03231, 0x03232, 0x03233, 0x03234, 0x03235, 0x03236, 0x03237, 0x03238, 0x03239, 0x0323a, 0x0323b, 0x0323c, 0x0323d, 0x0323e, 0x0323f, 0x03240, 0x03241, 0x03242, 0x03243, 0x03250, 0x03251, 0x03252, 0x03253, 0x03254, 0x03255, 0x03256, 0x03257, 0x03258, 0x03259, 0x0325a, 0x0325b, 0x0325c, 0x0325d, 0x0325e, 0x0325f, 0x0326e, 0x0326f, 0x03270, 0x03271, 0x03272, 0x03273, 0x03274, 0x03275, 0x03276, 0x03277, 0x03278, 0x03279, 0x0327a, 0x0327b, 0x0327c, 0x0327d, 0x0327e, 0x032b1, 0x032b2, 0x032b3, 0x032b4, 0x032b5, 0x032b6, 0x032b7, 0x032b8, 0x032b9, 0x032ba, 0x032bb, 0x032bc, 0x032bd, 0x032be, 0x032bf, 0x032c0, 0x032c1, 0x032c2, 0x032c3, 0x032c4, 0x032c5, 0x032c6, 0x032c7, 0x032c8, 0x032c9, 0x032ca, 0x032cb, 0x032cc, 0x032cd, 0x032ce, 0x032cf, 0x03300, 0x03301, 0x03302, 0x03303, 0x03304, 0x03305, 0x03306, 0x03307, 0x03308, 0x03309, 0x0330a, 0x0330b, 0x0330c, 0x0330d, 0x0330e, 0x0330f, 0x03310, 0x03311, 0x03312, 0x03313, 0x03314, 0x03315, 0x03316, 0x03317, 0x03318, 0x03319, 0x0331a, 0x0331b, 0x0331c, 0x0331d, 0x0331e, 0x0331f, 0x03320, 0x03321, 0x03322, 0x03323, 0x03324, 0x03325, 0x03326, 0x03327, 0x03328, 0x03329, 0x0332a, 0x0332b, 0x0332c, 0x0332d, 0x0332e, 0x0332f, 0x03330, 0x03331, 0x03332, 0x03333, 0x03334, 0x03335, 0x03336, 0x03337, 0x03338, 0x03339, 0x0333a, 0x0333b, 0x0333c, 0x0333d, 0x0333e, 0x0333f, 0x03340, 0x03341, 0x03342, 0x03343, 0x03344, 0x03345, 0x03346, 0x03347, 0x03348, 0x03349, 0x0334a, 0x0334b, 0x0334c, 0x0334d, 0x0334e, 0x0334f, 0x03350, 0x03351, 0x03352, 0x03353, 0x03354, 0x03355, 0x03356, 0x03357, 0x03358, 0x03359, 0x0335a, 0x0335b, 0x0335c, 0x0335d, 0x0335e, 0x0335f, 0x03360, 0x03361, 0x03362, 0x03363, 0x03364, 0x03365, 0x03366, 0x03367, 0x03368, 0x03369, 0x0336a, 0x0336b, 0x0336c, 0x0336d, 0x0336e, 0x0336f, 0x03370, 0x03371, 0x03372, 0x03373, 0x03374, 0x03375, 0x03376, 0x03377, 0x03378, 0x03379, 0x0337a, 0x0337b, 0x0337c, 0x0337d, 0x0337e, 0x0337f, 0x03380, 0x03381, 0x03382, 0x03383, 0x03384, 0x03385, 0x03386, 0x03387, 0x03388, 0x03389, 0x0338a, 0x0338b, 0x0338c, 0x0338d, 0x0338e, 0x0338f, 0x03390, 0x03391, 0x03392, 0x03393, 0x03394, 0x03395, 0x03396, 0x03397, 0x03398, 0x03399, 0x0339a, 0x0339b, 0x0339c, 0x0339d, 0x0339e, 0x0339f, 0x033a0, 0x033a1, 0x033a2, 0x033a3, 0x033a4, 0x033a5, 0x033a6, 0x033a7, 0x033a8, 0x033a9, 0x033aa, 0x033ab, 0x033ac, 0x033ad, 0x033ae, 0x033af, 0x033b0, 0x033b1, 0x033b2, 0x033b3, 0x033b4, 0x033b5, 0x033b6, 0x033b7, 0x033b8, 0x033b9, 0x033ba, 0x033bb, 0x033bc, 0x033bd, 0x033be, 0x033bf, 0x033c0, 0x033c1, 0x033c2, 0x033c3, 0x033c4, 0x033c5, 0x033c6, 0x033c7, 0x033c8, 0x033c9, 0x033ca, 0x033cb, 0x033cc, 0x033cd, 0x033ce, 0x033cf, 0x033d0, 0x033d1, 0x033d2, 0x033d3, 0x033d4, 0x033d5, 0x033d6, 0x033d7, 0x033d8, 0x033d9, 0x033da, 0x033db, 0x033dc, 0x033dd, 0x033de, 0x033df, 0x033e0, 0x033e1, 0x033e2, 0x033e3, 0x033e4, 0x033e5, 0x033e6, 0x033e7, 0x033e8, 0x033e9, 0x033ea, 0x033eb, 0x033ec, 0x033ed, 0x033ee, 0x033ef, 0x033f0, 0x033f1, 0x033f2, 0x033f3, 0x033f4, 0x033f5, 0x033f6, 0x033f7, 0x033f8, 0x033f9, 0x033fa, 0x033fb, 0x033fc, 0x033fd, 0x033fe, 0x033ff, 0x0fb00, 0x0fb01, 0x0fb02, 0x0fb03, 0x0fb04, 0x0fb05, 0x0fb06, 0x0fb13, 0x0fb14, 0x0fb15, 0x0fb16, 0x0fb17, 0x0fb1d, 0x0fb1f, 0x0fb2a, 0x0fb2b, 0x0fb2c, 0x0fb2d, 0x0fb2e, 0x0fb2f, 0x0fb30, 0x0fb31, 0x0fb32, 0x0fb33, 0x0fb34, 0x0fb35, 0x0fb36, 0x0fb38, 0x0fb39, 0x0fb3a, 0x0fb3b, 0x0fb3c, 0x0fb3e, 0x0fb40, 0x0fb41, 0x0fb43, 0x0fb44, 0x0fb46, 0x0fb47, 0x0fb48, 0x0fb49, 0x0fb4a, 0x0fb4b, 0x0fb4c, 0x0fb4d, 0x0fb4e, 0x0fb4f, 0x0fbea, 0x0fbeb, 0x0fbec, 0x0fbed, 0x0fbee, 0x0fbef, 0x0fbf0, 0x0fbf1, 0x0fbf2, 0x0fbf3, 0x0fbf4, 0x0fbf5, 0x0fbf6, 0x0fbf7, 0x0fbf8, 0x0fbf9, 0x0fbfa, 0x0fbfb, 0x0fc00, 0x0fc01, 0x0fc02, 0x0fc03, 0x0fc04, 0x0fc05, 0x0fc06, 0x0fc07, 0x0fc08, 0x0fc09, 0x0fc0a, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fc0f, 0x0fc10, 0x0fc11, 0x0fc12, 0x0fc13, 0x0fc14, 0x0fc15, 0x0fc16, 0x0fc17, 0x0fc18, 0x0fc19, 0x0fc1a, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fc1f, 0x0fc20, 0x0fc21, 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25, 0x0fc26, 0x0fc27, 0x0fc28, 0x0fc29, 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e, 0x0fc2f, 0x0fc30, 0x0fc31, 0x0fc32, 0x0fc33, 0x0fc34, 0x0fc35, 0x0fc36, 0x0fc37, 0x0fc38, 0x0fc39, 0x0fc3a, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc3f, 0x0fc40, 0x0fc41, 0x0fc42, 0x0fc43, 0x0fc44, 0x0fc45, 0x0fc46, 0x0fc47, 0x0fc48, 0x0fc49, 0x0fc4a, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fc4f, 0x0fc50, 0x0fc51, 0x0fc52, 0x0fc53, 0x0fc54, 0x0fc55, 0x0fc56, 0x0fc57, 0x0fc58, 0x0fc59, 0x0fc5a, 0x0fc5b, 0x0fc5c, 0x0fc5d, 0x0fc5e, 0x0fc5f, 0x0fc60, 0x0fc61, 0x0fc62, 0x0fc63, 0x0fc64, 0x0fc65, 0x0fc66, 0x0fc67, 0x0fc68, 0x0fc69, 0x0fc6a, 0x0fc6b, 0x0fc6c, 0x0fc6d, 0x0fc6e, 0x0fc6f, 0x0fc70, 0x0fc71, 0x0fc72, 0x0fc73, 0x0fc74, 0x0fc75, 0x0fc76, 0x0fc77, 0x0fc78, 0x0fc79, 0x0fc7a, 0x0fc7b, 0x0fc7c, 0x0fc7d, 0x0fc7e, 0x0fc7f, 0x0fc80, 0x0fc81, 0x0fc82, 0x0fc83, 0x0fc84, 0x0fc85, 0x0fc86, 0x0fc87, 0x0fc88, 0x0fc89, 0x0fc8a, 0x0fc8b, 0x0fc8c, 0x0fc8d, 0x0fc8e, 0x0fc8f, 0x0fc90, 0x0fc91, 0x0fc92, 0x0fc93, 0x0fc94, 0x0fc95, 0x0fc96, 0x0fc97, 0x0fc98, 0x0fc99, 0x0fc9a, 0x0fc9b, 0x0fc9c, 0x0fc9d, 0x0fc9e, 0x0fc9f, 0x0fca0, 0x0fca1, 0x0fca2, 0x0fca3, 0x0fca4, 0x0fca5, 0x0fca6, 0x0fca7, 0x0fca8, 0x0fca9, 0x0fcaa, 0x0fcab, 0x0fcac, 0x0fcad, 0x0fcae, 0x0fcaf, 0x0fcb0, 0x0fcb1, 0x0fcb2, 0x0fcb3, 0x0fcb4, 0x0fcb5, 0x0fcb6, 0x0fcb7, 0x0fcb8, 0x0fcb9, 0x0fcba, 0x0fcbb, 0x0fcbc, 0x0fcbd, 0x0fcbe, 0x0fcbf, 0x0fcc0, 0x0fcc1, 0x0fcc2, 0x0fcc3, 0x0fcc4, 0x0fcc5, 0x0fcc6, 0x0fcc7, 0x0fcc8, 0x0fcc9, 0x0fcca, 0x0fccb, 0x0fccc, 0x0fccd, 0x0fcce, 0x0fccf, 0x0fcd0, 0x0fcd1, 0x0fcd2, 0x0fcd3, 0x0fcd4, 0x0fcd5, 0x0fcd6, 0x0fcd7, 0x0fcd8, 0x0fcd9, 0x0fcda, 0x0fcdb, 0x0fcdc, 0x0fcdd, 0x0fcde, 0x0fcdf, 0x0fce0, 0x0fce1, 0x0fce2, 0x0fce3, 0x0fce4, 0x0fce5, 0x0fce6, 0x0fce7, 0x0fce8, 0x0fce9, 0x0fcea, 0x0fceb, 0x0fcec, 0x0fced, 0x0fcee, 0x0fcef, 0x0fcf0, 0x0fcf1, 0x0fcf2, 0x0fcf3, 0x0fcf4, 0x0fcf5, 0x0fcf6, 0x0fcf7, 0x0fcf8, 0x0fcf9, 0x0fcfa, 0x0fcfb, 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fcff, 0x0fd00, 0x0fd01, 0x0fd02, 0x0fd03, 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07, 0x0fd08, 0x0fd09, 0x0fd0a, 0x0fd0b, 0x0fd0c, 0x0fd0d, 0x0fd0e, 0x0fd0f, 0x0fd10, 0x0fd11, 0x0fd12, 0x0fd13, 0x0fd14, 0x0fd15, 0x0fd16, 0x0fd17, 0x0fd18, 0x0fd19, 0x0fd1a, 0x0fd1b, 0x0fd1c, 0x0fd1d, 0x0fd1e, 0x0fd1f, 0x0fd20, 0x0fd21, 0x0fd22, 0x0fd23, 0x0fd24, 0x0fd25, 0x0fd26, 0x0fd27, 0x0fd28, 0x0fd29, 0x0fd2a, 0x0fd2b, 0x0fd2c, 0x0fd2d, 0x0fd2e, 0x0fd2f, 0x0fd30, 0x0fd31, 0x0fd32, 0x0fd33, 0x0fd34, 0x0fd35, 0x0fd36, 0x0fd37, 0x0fd38, 0x0fd39, 0x0fd3a, 0x0fd3b, 0x0fd3c, 0x0fd3d, 0x0fd50, 0x0fd51, 0x0fd52, 0x0fd53, 0x0fd54, 0x0fd55, 0x0fd56, 0x0fd57, 0x0fd58, 0x0fd59, 0x0fd5a, 0x0fd5b, 0x0fd5c, 0x0fd5d, 0x0fd5e, 0x0fd5f, 0x0fd60, 0x0fd61, 0x0fd62, 0x0fd63, 0x0fd64, 0x0fd65, 0x0fd66, 0x0fd67, 0x0fd68, 0x0fd69, 0x0fd6a, 0x0fd6b, 0x0fd6c, 0x0fd6d, 0x0fd6e, 0x0fd6f, 0x0fd70, 0x0fd71, 0x0fd72, 0x0fd73, 0x0fd74, 0x0fd75, 0x0fd76, 0x0fd77, 0x0fd78, 0x0fd79, 0x0fd7a, 0x0fd7b, 0x0fd7c, 0x0fd7d, 0x0fd7e, 0x0fd7f, 0x0fd80, 0x0fd81, 0x0fd82, 0x0fd83, 0x0fd84, 0x0fd85, 0x0fd86, 0x0fd87, 0x0fd88, 0x0fd89, 0x0fd8a, 0x0fd8b, 0x0fd8c, 0x0fd8d, 0x0fd8e, 0x0fd8f, 0x0fd92, 0x0fd93, 0x0fd94, 0x0fd95, 0x0fd96, 0x0fd97, 0x0fd98, 0x0fd99, 0x0fd9a, 0x0fd9b, 0x0fd9c, 0x0fd9d, 0x0fd9e, 0x0fd9f, 0x0fda0, 0x0fda1, 0x0fda2, 0x0fda3, 0x0fda4, 0x0fda5, 0x0fda6, 0x0fda7, 0x0fda8, 0x0fda9, 0x0fdaa, 0x0fdab, 0x0fdac, 0x0fdad, 0x0fdae, 0x0fdaf, 0x0fdb0, 0x0fdb1, 0x0fdb2, 0x0fdb3, 0x0fdb4, 0x0fdb5, 0x0fdb6, 0x0fdb7, 0x0fdb8, 0x0fdb9, 0x0fdba, 0x0fdbb, 0x0fdbc, 0x0fdbd, 0x0fdbe, 0x0fdbf, 0x0fdc0, 0x0fdc1, 0x0fdc2, 0x0fdc3, 0x0fdc4, 0x0fdc5, 0x0fdc6, 0x0fdc7, 0x0fdf0, 0x0fdf1, 0x0fdf2, 0x0fdf3, 0x0fdf4, 0x0fdf5, 0x0fdf6, 0x0fdf7, 0x0fdf8, 0x0fdf9, 0x0fdfa, 0x0fdfb, 0x0fdfc, 0x0fe70, 0x0fe71, 0x0fe72, 0x0fe74, 0x0fe76, 0x0fe77, 0x0fe78, 0x0fe79, 0x0fe7a, 0x0fe7b, 0x0fe7c, 0x0fe7d, 0x0fe7e, 0x0fe7f, 0x0fef5, 0x0fef6, 0x0fef7, 0x0fef8, 0x0fef9, 0x0fefa, 0x0fefb, 0x0fefc, 0x1109a, 0x1109c, 0x110ab, 0x1112e, 0x1112f, 0x1134b, 0x1134c, 0x114bb, 0x114bc, 0x114be, 0x115ba, 0x115bb, 0x1d15e, 0x1d15f, 0x1d160, 0x1d161, 0x1d162, 0x1d163, 0x1d164, 0x1d1bb, 0x1d1bc, 0x1d1bd, 0x1d1be, 0x1d1bf, 0x1d1c0, 0x1f100, 0x1f101, 0x1f102, 0x1f103, 0x1f104, 0x1f105, 0x1f106, 0x1f107, 0x1f108, 0x1f109, 0x1f10a, 0x1f110, 0x1f111, 0x1f112, 0x1f113, 0x1f114, 0x1f115, 0x1f116, 0x1f117, 0x1f118, 0x1f119, 0x1f11a, 0x1f11b, 0x1f11c, 0x1f11d, 0x1f11e, 0x1f11f, 0x1f120, 0x1f121, 0x1f122, 0x1f123, 0x1f124, 0x1f125, 0x1f126, 0x1f127, 0x1f128, 0x1f129, 0x1f12a, 0x1f12d, 0x1f12e, 0x1f14a, 0x1f14b, 0x1f14c, 0x1f14d, 0x1f14e, 0x1f14f, 0x1f16a, 0x1f16b, 0x1f190, 0x1f200, 0x1f201, 0x1f240, 0x1f241, 0x1f242, 0x1f243, 0x1f244, 0x1f245, 0x1f246, 0x1f247, 0x1f248 }; static const uint16_t multidecomp_offsets[] = { 0x00000, 0x00003, 0x00006, 0x00009, 0x0000c, 0x00010, 0x00014, 0x00018, 0x0001b, 0x0001e, 0x00021, 0x00024, 0x00027, 0x0002a, 0x0002d, 0x00030, 0x00033, 0x00036, 0x00039, 0x0003c, 0x0003f, 0x00042, 0x00045, 0x00048, 0x0004b, 0x0004e, 0x00051, 0x00054, 0x00057, 0x0005a, 0x0005d, 0x00060, 0x00063, 0x00066, 0x00069, 0x0006c, 0x0006f, 0x00072, 0x00075, 0x00078, 0x0007b, 0x0007e, 0x00081, 0x00084, 0x00087, 0x0008a, 0x0008d, 0x00090, 0x00093, 0x00096, 0x00099, 0x0009c, 0x0009f, 0x000a2, 0x000a5, 0x000a8, 0x000ab, 0x000ae, 0x000b1, 0x000b4, 0x000b7, 0x000ba, 0x000bd, 0x000c0, 0x000c3, 0x000c6, 0x000c9, 0x000cc, 0x000cf, 0x000d2, 0x000d5, 0x000d8, 0x000db, 0x000de, 0x000e1, 0x000e4, 0x000e7, 0x000ea, 0x000ed, 0x000f0, 0x000f3, 0x000f6, 0x000f9, 0x000fc, 0x000ff, 0x00102, 0x00105, 0x00108, 0x0010b, 0x0010e, 0x00111, 0x00114, 0x00117, 0x0011a, 0x0011d, 0x00120, 0x00123, 0x00126, 0x00129, 0x0012c, 0x0012f, 0x00132, 0x00135, 0x00138, 0x0013b, 0x0013e, 0x00141, 0x00144, 0x00147, 0x0014a, 0x0014d, 0x00150, 0x00153, 0x00156, 0x00159, 0x0015c, 0x0015f, 0x00162, 0x00165, 0x00168, 0x0016b, 0x0016e, 0x00171, 0x00174, 0x00177, 0x0017a, 0x0017d, 0x00180, 0x00183, 0x00186, 0x00189, 0x0018c, 0x0018f, 0x00192, 0x00195, 0x00198, 0x0019b, 0x0019e, 0x001a1, 0x001a4, 0x001a7, 0x001aa, 0x001ad, 0x001b0, 0x001b3, 0x001b6, 0x001b9, 0x001bc, 0x001bf, 0x001c2, 0x001c5, 0x001c8, 0x001cb, 0x001ce, 0x001d1, 0x001d4, 0x001d7, 0x001da, 0x001dd, 0x001e0, 0x001e3, 0x001e6, 0x001e9, 0x001ec, 0x001ef, 0x001f2, 0x001f5, 0x001f8, 0x001fb, 0x001fe, 0x00201, 0x00204, 0x00207, 0x0020a, 0x0020d, 0x00210, 0x00213, 0x00216, 0x00219, 0x0021c, 0x0021f, 0x00222, 0x00225, 0x00228, 0x0022b, 0x0022e, 0x00231, 0x00234, 0x00237, 0x0023a, 0x0023d, 0x00240, 0x00243, 0x00246, 0x00249, 0x0024c, 0x0024f, 0x00252, 0x00255, 0x00258, 0x0025b, 0x0025e, 0x00261, 0x00264, 0x00267, 0x0026a, 0x0026d, 0x00270, 0x00273, 0x00276, 0x00279, 0x0027c, 0x0027f, 0x00282, 0x00285, 0x00288, 0x0028b, 0x0028e, 0x00291, 0x00294, 0x00297, 0x0029a, 0x0029d, 0x002a0, 0x002a3, 0x002a6, 0x002a9, 0x002ac, 0x002af, 0x002b2, 0x002b5, 0x002b8, 0x002bb, 0x002be, 0x002c1, 0x002c4, 0x002c7, 0x002ca, 0x002cd, 0x002d0, 0x002d3, 0x002d6, 0x002d9, 0x002dc, 0x002df, 0x002e2, 0x002e5, 0x002e8, 0x002eb, 0x002ee, 0x002f1, 0x002f4, 0x002f7, 0x002fa, 0x002fd, 0x00300, 0x00303, 0x00306, 0x00309, 0x0030c, 0x0030f, 0x00312, 0x00315, 0x00318, 0x0031b, 0x0031e, 0x00321, 0x00324, 0x00327, 0x0032a, 0x0032d, 0x00330, 0x00333, 0x00336, 0x00339, 0x0033c, 0x0033f, 0x00342, 0x00345, 0x00348, 0x0034b, 0x0034e, 0x00351, 0x00354, 0x00357, 0x0035a, 0x0035d, 0x00360, 0x00363, 0x00366, 0x00369, 0x0036c, 0x0036f, 0x00372, 0x00375, 0x00378, 0x0037b, 0x0037e, 0x00381, 0x00384, 0x00387, 0x0038a, 0x0038d, 0x00390, 0x00393, 0x00396, 0x00399, 0x0039c, 0x0039f, 0x003a2, 0x003a5, 0x003a8, 0x003ab, 0x003ae, 0x003b1, 0x003b4, 0x003b7, 0x003ba, 0x003bd, 0x003c0, 0x003c3, 0x003c6, 0x003c9, 0x003cc, 0x003cf, 0x003d2, 0x003d5, 0x003d8, 0x003db, 0x003de, 0x003e1, 0x003e4, 0x003e7, 0x003ea, 0x003ed, 0x003f0, 0x003f3, 0x003f6, 0x003f9, 0x003fc, 0x003ff, 0x00402, 0x00405, 0x00408, 0x0040b, 0x0040e, 0x00411, 0x00414, 0x00417, 0x0041a, 0x0041d, 0x00420, 0x00423, 0x00426, 0x00429, 0x0042c, 0x0042f, 0x00432, 0x00435, 0x00438, 0x0043b, 0x0043e, 0x00441, 0x00444, 0x00447, 0x0044a, 0x0044d, 0x00450, 0x00453, 0x00456, 0x00459, 0x0045c, 0x0045f, 0x00462, 0x00465, 0x00468, 0x0046b, 0x0046e, 0x00471, 0x00474, 0x00477, 0x0047a, 0x0047d, 0x00480, 0x00483, 0x00486, 0x00489, 0x0048c, 0x0048f, 0x00492, 0x00495, 0x00498, 0x0049b, 0x0049e, 0x004a1, 0x004a4, 0x004a7, 0x004aa, 0x004ad, 0x004b0, 0x004b3, 0x004b6, 0x004b9, 0x004bc, 0x004bf, 0x004c2, 0x004c5, 0x004c8, 0x004cb, 0x004ce, 0x004d1, 0x004d4, 0x004d7, 0x004da, 0x004dd, 0x004e0, 0x004e3, 0x004e6, 0x004e9, 0x004ec, 0x004ef, 0x004f2, 0x004f5, 0x004f8, 0x004fb, 0x004fe, 0x00501, 0x00504, 0x00507, 0x0050a, 0x0050d, 0x00510, 0x00513, 0x00516, 0x00519, 0x0051c, 0x0051f, 0x00522, 0x00525, 0x00528, 0x0052b, 0x0052e, 0x00531, 0x00534, 0x00537, 0x0053a, 0x0053d, 0x00540, 0x00543, 0x00546, 0x00549, 0x0054c, 0x0054f, 0x00552, 0x00555, 0x00558, 0x0055b, 0x0055e, 0x00561, 0x00564, 0x00567, 0x0056a, 0x0056d, 0x00570, 0x00573, 0x00576, 0x00579, 0x0057c, 0x0057f, 0x00582, 0x00585, 0x00588, 0x0058b, 0x0058e, 0x00591, 0x00594, 0x00597, 0x0059a, 0x0059d, 0x005a0, 0x005a3, 0x005a6, 0x005a9, 0x005ac, 0x005af, 0x005b2, 0x005b5, 0x005b8, 0x005bb, 0x005be, 0x005c1, 0x005c4, 0x005c7, 0x005ca, 0x005cd, 0x005d0, 0x005d3, 0x005d6, 0x005d9, 0x005dc, 0x005df, 0x005e2, 0x005e5, 0x005e8, 0x005eb, 0x005ee, 0x005f1, 0x005f4, 0x005f7, 0x005fa, 0x005fd, 0x00600, 0x00603, 0x00606, 0x00609, 0x0060c, 0x0060f, 0x00612, 0x00615, 0x00618, 0x0061b, 0x0061e, 0x00621, 0x00624, 0x00627, 0x0062a, 0x0062d, 0x00630, 0x00633, 0x00636, 0x0063a, 0x0063d, 0x00641, 0x00644, 0x00648, 0x0064b, 0x0064e, 0x00651, 0x00654, 0x00657, 0x0065c, 0x0065f, 0x00663, 0x00667, 0x0066a, 0x0066e, 0x00672, 0x00675, 0x00678, 0x0067b, 0x0067f, 0x00682, 0x00686, 0x0068a, 0x0068e, 0x00693, 0x00697, 0x0069b, 0x0069f, 0x006a3, 0x006a7, 0x006ab, 0x006af, 0x006b3, 0x006b7, 0x006bb, 0x006bf, 0x006c3, 0x006c6, 0x006c9, 0x006cd, 0x006d0, 0x006d3, 0x006d7, 0x006dc, 0x006df, 0x006e2, 0x006e6, 0x006ea, 0x006ed, 0x006f0, 0x006f3, 0x006f6, 0x006f9, 0x006fc, 0x006ff, 0x00702, 0x00705, 0x00708, 0x0070b, 0x0070e, 0x00712, 0x00715, 0x00719, 0x0071c, 0x0071f, 0x00722, 0x00725, 0x00728, 0x0072b, 0x0072e, 0x00731, 0x00734, 0x00737, 0x0073a, 0x0073d, 0x00740, 0x00743, 0x00746, 0x00749, 0x0074c, 0x0074f, 0x00752, 0x00755, 0x00758, 0x0075b, 0x0075e, 0x00761, 0x00764, 0x00767, 0x0076a, 0x0076d, 0x00770, 0x00773, 0x00776, 0x00779, 0x0077c, 0x0077f, 0x00782, 0x00785, 0x00788, 0x0078b, 0x0078e, 0x00791, 0x00794, 0x00797, 0x0079a, 0x0079d, 0x007a1, 0x007a5, 0x007a9, 0x007ad, 0x007b1, 0x007b5, 0x007b9, 0x007bd, 0x007c1, 0x007c6, 0x007cb, 0x007d0, 0x007d5, 0x007da, 0x007df, 0x007e4, 0x007e9, 0x007ee, 0x007f3, 0x007f8, 0x007fb, 0x007fe, 0x00801, 0x00804, 0x00807, 0x0080a, 0x0080d, 0x00810, 0x00813, 0x00817, 0x0081b, 0x0081f, 0x00823, 0x00827, 0x0082b, 0x0082f, 0x00833, 0x00837, 0x0083b, 0x0083f, 0x00843, 0x00847, 0x0084b, 0x0084f, 0x00853, 0x00857, 0x0085b, 0x0085f, 0x00863, 0x00867, 0x0086b, 0x0086f, 0x00873, 0x00877, 0x0087b, 0x0087f, 0x00883, 0x00887, 0x0088b, 0x0088f, 0x00893, 0x00897, 0x0089b, 0x0089f, 0x008a3, 0x008a7, 0x008ac, 0x008b0, 0x008b3, 0x008b7, 0x008ba, 0x008bd, 0x008c0, 0x008c3, 0x008c6, 0x008c9, 0x008cc, 0x008cf, 0x008d2, 0x008d5, 0x008d8, 0x008db, 0x008de, 0x008e1, 0x008e4, 0x008e7, 0x008ea, 0x008ed, 0x008f0, 0x008f3, 0x008f6, 0x008f9, 0x008fc, 0x008ff, 0x00902, 0x00905, 0x00908, 0x0090b, 0x0090e, 0x00911, 0x00914, 0x00917, 0x0091a, 0x0091d, 0x00920, 0x00923, 0x00926, 0x00929, 0x0092c, 0x0092f, 0x00932, 0x00935, 0x00938, 0x0093b, 0x0093e, 0x00941, 0x00944, 0x00947, 0x0094a, 0x0094d, 0x00950, 0x00953, 0x00956, 0x00959, 0x0095c, 0x0095f, 0x00962, 0x00965, 0x00968, 0x0096b, 0x0096e, 0x00971, 0x00974, 0x00978, 0x0097c, 0x00980, 0x00984, 0x00988, 0x0098c, 0x00990, 0x00994, 0x00998, 0x0099c, 0x009a0, 0x009a4, 0x009a8, 0x009ac, 0x009b1, 0x009b6, 0x009bb, 0x009c0, 0x009c5, 0x009ca, 0x009cf, 0x009d4, 0x009d9, 0x009de, 0x009e3, 0x009e8, 0x009ed, 0x009f2, 0x009f7, 0x009ff, 0x00a06, 0x00a0a, 0x00a0e, 0x00a12, 0x00a16, 0x00a1a, 0x00a1e, 0x00a22, 0x00a26, 0x00a2a, 0x00a2e, 0x00a32, 0x00a36, 0x00a3a, 0x00a3e, 0x00a42, 0x00a46, 0x00a4a, 0x00a4e, 0x00a52, 0x00a56, 0x00a5a, 0x00a5e, 0x00a62, 0x00a66, 0x00a6a, 0x00a6e, 0x00a72, 0x00a76, 0x00a7a, 0x00a7e, 0x00a82, 0x00a86, 0x00a8a, 0x00a8e, 0x00a92, 0x00a96, 0x00a9a, 0x00a9d, 0x00aa0, 0x00aa3, 0x00aa6, 0x00aa9, 0x00aac, 0x00aaf, 0x00ab2, 0x00ab5, 0x00ab8, 0x00abb, 0x00abe, 0x00ac1, 0x00ac4, 0x00ac7, 0x00aca, 0x00acd, 0x00ad0, 0x00ad3, 0x00ad6, 0x00ad9, 0x00adc, 0x00adf, 0x00ae2, 0x00ae5, 0x00ae8, 0x00aeb, 0x00aee, 0x00af1, 0x00af7, 0x00afc, 0x00aff, 0x00b02, 0x00b05, 0x00b08, 0x00b0b, 0x00b0e, 0x00b11, 0x00b14, 0x00b17, 0x00b1a, 0x00b1d, 0x00b20, 0x00b23, 0x00b26, 0x00b29, 0x00b2c, 0x00b2f, 0x00b32, 0x00b35, 0x00b38, 0x00b3b, 0x00b3e, 0x00b41, 0x00b44, 0x00b47, 0x00b4b, 0x00b4f, 0x00b53, 0x00b56, 0x00b5a, 0x00b5d, 0x00b61, 0x00b66, 0x00b6b, 0x00b70, 0x00b74, 0x00b79, 0x00b7d, 0x00b81, 0x00b87, 0x00b8c, 0x00b90, 0x00b94, 0x00b98, 0x00b9d, 0x00ba2, 0x00ba6, 0x00baa, 0x00bad, 0x00bb1, 0x00bb6, 0x00bbb, 0x00bbe, 0x00bc4, 0x00bcb, 0x00bd1, 0x00bd5, 0x00bdb, 0x00be1, 0x00be6, 0x00bea, 0x00bee, 0x00bf2, 0x00bf7, 0x00bfd, 0x00c02, 0x00c06, 0x00c0a, 0x00c0e, 0x00c11, 0x00c14, 0x00c17, 0x00c1a, 0x00c1e, 0x00c22, 0x00c28, 0x00c2c, 0x00c31, 0x00c37, 0x00c3b, 0x00c3e, 0x00c41, 0x00c47, 0x00c4c, 0x00c52, 0x00c56, 0x00c5c, 0x00c5f, 0x00c63, 0x00c67, 0x00c6b, 0x00c6f, 0x00c73, 0x00c78, 0x00c7c, 0x00c7f, 0x00c83, 0x00c87, 0x00c8b, 0x00c90, 0x00c94, 0x00c98, 0x00c9c, 0x00ca2, 0x00ca7, 0x00caa, 0x00cb0, 0x00cb3, 0x00cb8, 0x00cbd, 0x00cc1, 0x00cc5, 0x00cc9, 0x00cce, 0x00cd1, 0x00cd5, 0x00cda, 0x00cdd, 0x00ce3, 0x00ce7, 0x00cea, 0x00ced, 0x00cf0, 0x00cf3, 0x00cf6, 0x00cf9, 0x00cfc, 0x00cff, 0x00d02, 0x00d05, 0x00d09, 0x00d0d, 0x00d11, 0x00d15, 0x00d19, 0x00d1d, 0x00d21, 0x00d25, 0x00d29, 0x00d2d, 0x00d31, 0x00d35, 0x00d39, 0x00d3d, 0x00d41, 0x00d45, 0x00d48, 0x00d4b, 0x00d4f, 0x00d52, 0x00d55, 0x00d58, 0x00d5c, 0x00d60, 0x00d63, 0x00d66, 0x00d69, 0x00d6c, 0x00d6f, 0x00d74, 0x00d77, 0x00d7a, 0x00d7d, 0x00d80, 0x00d83, 0x00d86, 0x00d89, 0x00d8c, 0x00d90, 0x00d95, 0x00d98, 0x00d9b, 0x00d9e, 0x00da1, 0x00da4, 0x00da7, 0x00daa, 0x00dae, 0x00db2, 0x00db6, 0x00dba, 0x00dbd, 0x00dc0, 0x00dc3, 0x00dc6, 0x00dc9, 0x00dcc, 0x00dcf, 0x00dd2, 0x00dd5, 0x00dd8, 0x00ddc, 0x00de0, 0x00de3, 0x00de7, 0x00deb, 0x00def, 0x00df2, 0x00df6, 0x00dfa, 0x00dff, 0x00e02, 0x00e06, 0x00e0a, 0x00e0e, 0x00e12, 0x00e18, 0x00e1f, 0x00e22, 0x00e25, 0x00e28, 0x00e2b, 0x00e2e, 0x00e31, 0x00e34, 0x00e37, 0x00e3a, 0x00e3d, 0x00e40, 0x00e43, 0x00e46, 0x00e49, 0x00e4c, 0x00e4f, 0x00e52, 0x00e55, 0x00e5a, 0x00e5d, 0x00e60, 0x00e63, 0x00e68, 0x00e6c, 0x00e6f, 0x00e72, 0x00e75, 0x00e78, 0x00e7b, 0x00e7e, 0x00e81, 0x00e84, 0x00e87, 0x00e8a, 0x00e8e, 0x00e91, 0x00e94, 0x00e98, 0x00e9c, 0x00e9f, 0x00ea4, 0x00ea8, 0x00eab, 0x00eae, 0x00eb1, 0x00eb4, 0x00eb8, 0x00ebc, 0x00ebf, 0x00ec2, 0x00ec5, 0x00ec8, 0x00ecb, 0x00ece, 0x00ed1, 0x00ed4, 0x00ed7, 0x00edb, 0x00edf, 0x00ee3, 0x00ee7, 0x00eeb, 0x00eef, 0x00ef3, 0x00ef7, 0x00efb, 0x00eff, 0x00f03, 0x00f07, 0x00f0b, 0x00f0f, 0x00f13, 0x00f17, 0x00f1b, 0x00f1f, 0x00f23, 0x00f27, 0x00f2b, 0x00f2f, 0x00f33, 0x00f36, 0x00f39, 0x00f3c, 0x00f40, 0x00f44, 0x00f47, 0x00f4a, 0x00f4d, 0x00f50, 0x00f53, 0x00f56, 0x00f59, 0x00f5c, 0x00f5f, 0x00f62, 0x00f65, 0x00f68, 0x00f6b, 0x00f6e, 0x00f71, 0x00f74, 0x00f77, 0x00f7a, 0x00f7d, 0x00f80, 0x00f83, 0x00f86, 0x00f89, 0x00f8c, 0x00f8f, 0x00f92, 0x00f95, 0x00f98, 0x00f9b, 0x00f9e, 0x00fa1, 0x00fa4, 0x00fa7, 0x00faa, 0x00fad, 0x00fb0, 0x00fb3, 0x00fb6, 0x00fb9, 0x00fbc, 0x00fbf, 0x00fc2, 0x00fc5, 0x00fc8, 0x00fcb, 0x00fce, 0x00fd1, 0x00fd4, 0x00fd7, 0x00fda, 0x00fdd, 0x00fe0, 0x00fe3, 0x00fe6, 0x00fe9, 0x00fec, 0x00fef, 0x00ff2, 0x00ff5, 0x00ff8, 0x00ffb, 0x00ffe, 0x01001, 0x01004, 0x01007, 0x0100a, 0x0100d, 0x01010, 0x01013, 0x01016, 0x01019, 0x0101c, 0x0101f, 0x01022, 0x01025, 0x01028, 0x0102b, 0x0102e, 0x01031, 0x01034, 0x01037, 0x0103a, 0x0103d, 0x01040, 0x01043, 0x01046, 0x01049, 0x0104c, 0x0104f, 0x01052, 0x01055, 0x01058, 0x0105b, 0x0105e, 0x01061, 0x01064, 0x01067, 0x0106a, 0x0106d, 0x01070, 0x01073, 0x01076, 0x01079, 0x0107c, 0x0107f, 0x01082, 0x01085, 0x01088, 0x0108b, 0x0108e, 0x01091, 0x01094, 0x01097, 0x0109a, 0x0109d, 0x010a0, 0x010a3, 0x010a6, 0x010a9, 0x010ac, 0x010af, 0x010b2, 0x010b5, 0x010b8, 0x010bb, 0x010be, 0x010c1, 0x010c4, 0x010c7, 0x010ca, 0x010cd, 0x010d0, 0x010d3, 0x010d6, 0x010d9, 0x010dc, 0x010df, 0x010e2, 0x010e5, 0x010e8, 0x010eb, 0x010ee, 0x010f1, 0x010f4, 0x010f7, 0x010fa, 0x010fd, 0x01100, 0x01103, 0x01106, 0x01109, 0x0110c, 0x0110f, 0x01112, 0x01116, 0x0111a, 0x0111e, 0x01122, 0x01126, 0x0112a, 0x0112d, 0x01130, 0x01133, 0x01136, 0x01139, 0x0113c, 0x0113f, 0x01142, 0x01145, 0x01148, 0x0114b, 0x0114e, 0x01151, 0x01154, 0x01157, 0x0115a, 0x0115d, 0x01160, 0x01163, 0x01166, 0x01169, 0x0116c, 0x0116f, 0x01172, 0x01175, 0x01178, 0x0117b, 0x0117e, 0x01181, 0x01184, 0x01187, 0x0118a, 0x0118d, 0x01190, 0x01193, 0x01196, 0x01199, 0x0119c, 0x0119f, 0x011a2, 0x011a5, 0x011a8, 0x011ab, 0x011ae, 0x011b1, 0x011b4, 0x011b7, 0x011ba, 0x011bd, 0x011c0, 0x011c3, 0x011c6, 0x011c9, 0x011cc, 0x011cf, 0x011d2, 0x011d5, 0x011d8, 0x011db, 0x011de, 0x011e1, 0x011e4, 0x011e7, 0x011ea, 0x011ed, 0x011f0, 0x011f3, 0x011f6, 0x011f9, 0x011fc, 0x011ff, 0x01202, 0x01205, 0x01208, 0x0120b, 0x0120e, 0x01211, 0x01214, 0x01217, 0x0121a, 0x0121d, 0x01220, 0x01223, 0x01226, 0x01229, 0x0122c, 0x0122f, 0x01232, 0x01235, 0x01238, 0x0123b, 0x0123e, 0x01241, 0x01244, 0x01247, 0x0124a, 0x0124d, 0x01250, 0x01253, 0x01256, 0x01259, 0x0125c, 0x0125f, 0x01262, 0x01265, 0x01268, 0x0126b, 0x0126e, 0x01271, 0x01274, 0x01277, 0x0127a, 0x0127d, 0x01280, 0x01283, 0x01286, 0x01289, 0x0128c, 0x0128f, 0x01292, 0x01295, 0x01298, 0x0129b, 0x0129e, 0x012a1, 0x012a4, 0x012a7, 0x012aa, 0x012ad, 0x012b0, 0x012b3, 0x012b6, 0x012b9, 0x012bc, 0x012bf, 0x012c2, 0x012c5, 0x012c8, 0x012cb, 0x012ce, 0x012d1, 0x012d4, 0x012d8, 0x012dc, 0x012e0, 0x012e3, 0x012e6, 0x012e9, 0x012ec, 0x012ef, 0x012f2, 0x012f5, 0x012f8, 0x012fb, 0x012fe, 0x01301, 0x01304, 0x01307, 0x0130a, 0x0130d, 0x01310, 0x01313, 0x01316, 0x01319, 0x0131c, 0x0131f, 0x01322, 0x01325, 0x01328, 0x0132b, 0x0132e, 0x01331, 0x01334, 0x01337, 0x0133a, 0x0133d, 0x01340, 0x01343, 0x01346, 0x01349, 0x0134c, 0x0134f, 0x01352, 0x01355, 0x01358, 0x0135b, 0x0135e, 0x01361, 0x01364, 0x01367, 0x0136a, 0x0136d, 0x01370, 0x01373, 0x01376, 0x01379, 0x0137c, 0x0137f, 0x01382, 0x01385, 0x01388, 0x0138b, 0x0138e, 0x01391, 0x01394, 0x01397, 0x0139a, 0x0139d, 0x013a0, 0x013a3, 0x013a6, 0x013a9, 0x013ac, 0x013af, 0x013b2, 0x013b5, 0x013b8, 0x013bb, 0x013bf, 0x013c3, 0x013c7, 0x013cb, 0x013cf, 0x013d3, 0x013d7, 0x013db, 0x013df, 0x013e3, 0x013e7, 0x013eb, 0x013ef, 0x013f3, 0x013f7, 0x013fb, 0x013ff, 0x01403, 0x01407, 0x0140b, 0x0140f, 0x01413, 0x01417, 0x0141b, 0x0141f, 0x01423, 0x01427, 0x0142b, 0x0142f, 0x01433, 0x01437, 0x0143b, 0x0143f, 0x01443, 0x01447, 0x0144b, 0x0144f, 0x01453, 0x01457, 0x0145b, 0x0145f, 0x01463, 0x01467, 0x0146b, 0x0146f, 0x01473, 0x01477, 0x0147b, 0x0147f, 0x01483, 0x01487, 0x0148b, 0x0148f, 0x01493, 0x01497, 0x0149b, 0x0149f, 0x014a3, 0x014a7, 0x014ab, 0x014af, 0x014b3, 0x014b7, 0x014bb, 0x014bf, 0x014c3, 0x014c7, 0x014cb, 0x014cf, 0x014d3, 0x014d7, 0x014db, 0x014df, 0x014e3, 0x014e7, 0x014eb, 0x014ef, 0x014f3, 0x014f7, 0x014fb, 0x014ff, 0x01503, 0x01507, 0x0150b, 0x0150f, 0x01513, 0x01517, 0x0151b, 0x0151f, 0x01523, 0x01527, 0x0152b, 0x0152f, 0x01533, 0x01537, 0x0153b, 0x0153f, 0x01543, 0x01547, 0x0154b, 0x0154f, 0x01553, 0x01557, 0x0155b, 0x0155f, 0x01563, 0x01567, 0x0156b, 0x0156f, 0x01573, 0x01577, 0x0157b, 0x0157f, 0x01583, 0x01587, 0x0158b, 0x0158f, 0x01593, 0x01597, 0x0159b, 0x015a0, 0x015a5, 0x015aa, 0x015af, 0x015b4, 0x015b9, 0x015be, 0x015c2, 0x015d5, 0x015de, 0x015e3, 0x015e6, 0x015e9, 0x015ec, 0x015ef, 0x015f2, 0x015f5, 0x015f8, 0x015fb, 0x015fe, 0x01601, 0x01604, 0x01607, 0x0160a, 0x0160d, 0x01610, 0x01613, 0x01616, 0x01619, 0x0161c, 0x0161f, 0x01622, 0x01625, 0x01628, 0x0162b, 0x0162e, 0x01631, 0x01634, 0x01637, 0x0163a, 0x0163d, 0x01640, 0x01643, 0x01646, 0x01649, 0x0164c, 0x0164f, 0x01652, 0x01655, 0x01658, 0x0165b, 0x0165e, 0x01661, 0x01664, 0x01667, 0x0166a, 0x0166d, 0x01670, 0x01673, 0x01676, 0x01679, 0x0167c, 0x0167f, 0x01682, 0x01685, 0x01688, 0x0168b, 0x0168e, 0x01691, 0x01695, 0x01699, 0x0169d, 0x016a1, 0x016a5, 0x016a9, 0x016ad, 0x016b1, 0x016b5, 0x016b9, 0x016bd, 0x016c1, 0x016c5, 0x016c9, 0x016cd, 0x016d1, 0x016d5, 0x016d9, 0x016dd, 0x016e1, 0x016e5, 0x016e9, 0x016ed, 0x016f1, 0x016f5, 0x016f9, 0x016fd, 0x01700, 0x01703, 0x01706, 0x01709, 0x0170c, 0x0170f, 0x01713, 0x01716, 0x01719, 0x0171c, 0x0171f, 0x01722, 0x01725, 0x01729, 0x0172d, 0x01731, 0x01735, 0x01739, 0x0173d, 0x01741, 0x01745 }; static const uint32_t multidecomp_values[] = { 0x00020, 0x00308, 0x00000, 0x00020, 0x00304, 0x00000, 0x00020, 0x00301, 0x00000, 0x00020, 0x00327, 0x00000, 0x00031, 0x02044, 0x00034, 0x00000, 0x00031, 0x02044, 0x00032, 0x00000, 0x00033, 0x02044, 0x00034, 0x00000, 0x00041, 0x00300, 0x00000, 0x00041, 0x00301, 0x00000, 0x00041, 0x00302, 0x00000, 0x00041, 0x00303, 0x00000, 0x00041, 0x00308, 0x00000, 0x00041, 0x0030a, 0x00000, 0x00043, 0x00327, 0x00000, 0x00045, 0x00300, 0x00000, 0x00045, 0x00301, 0x00000, 0x00045, 0x00302, 0x00000, 0x00045, 0x00308, 0x00000, 0x00049, 0x00300, 0x00000, 0x00049, 0x00301, 0x00000, 0x00049, 0x00302, 0x00000, 0x00049, 0x00308, 0x00000, 0x0004e, 0x00303, 0x00000, 0x0004f, 0x00300, 0x00000, 0x0004f, 0x00301, 0x00000, 0x0004f, 0x00302, 0x00000, 0x0004f, 0x00303, 0x00000, 0x0004f, 0x00308, 0x00000, 0x00055, 0x00300, 0x00000, 0x00055, 0x00301, 0x00000, 0x00055, 0x00302, 0x00000, 0x00055, 0x00308, 0x00000, 0x00059, 0x00301, 0x00000, 0x00041, 0x00304, 0x00000, 0x00041, 0x00306, 0x00000, 0x00041, 0x00328, 0x00000, 0x00043, 0x00301, 0x00000, 0x00043, 0x00302, 0x00000, 0x00043, 0x00307, 0x00000, 0x00043, 0x0030c, 0x00000, 0x00044, 0x0030c, 0x00000, 0x00045, 0x00304, 0x00000, 0x00045, 0x00306, 0x00000, 0x00045, 0x00307, 0x00000, 0x00045, 0x00328, 0x00000, 0x00045, 0x0030c, 0x00000, 0x00047, 0x00302, 0x00000, 0x00047, 0x00306, 0x00000, 0x00047, 0x00307, 0x00000, 0x00047, 0x00327, 0x00000, 0x00048, 0x00302, 0x00000, 0x00049, 0x00303, 0x00000, 0x00049, 0x00304, 0x00000, 0x00049, 0x00306, 0x00000, 0x00049, 0x00328, 0x00000, 0x00049, 0x00307, 0x00000, 0x00049, 0x0004a, 0x00000, 0x0004a, 0x00302, 0x00000, 0x0004b, 0x00327, 0x00000, 0x0004c, 0x00301, 0x00000, 0x0004c, 0x00327, 0x00000, 0x0004c, 0x0030c, 0x00000, 0x0004c, 0x000b7, 0x00000, 0x0004e, 0x00301, 0x00000, 0x0004e, 0x00327, 0x00000, 0x0004e, 0x0030c, 0x00000, 0x002bc, 0x0006e, 0x00000, 0x0004f, 0x00304, 0x00000, 0x0004f, 0x00306, 0x00000, 0x0004f, 0x0030b, 0x00000, 0x00052, 0x00301, 0x00000, 0x00052, 0x00327, 0x00000, 0x00052, 0x0030c, 0x00000, 0x00053, 0x00301, 0x00000, 0x00053, 0x00302, 0x00000, 0x00053, 0x00327, 0x00000, 0x00053, 0x0030c, 0x00000, 0x00054, 0x00327, 0x00000, 0x00054, 0x0030c, 0x00000, 0x00055, 0x00303, 0x00000, 0x00055, 0x00304, 0x00000, 0x00055, 0x00306, 0x00000, 0x00055, 0x0030a, 0x00000, 0x00055, 0x0030b, 0x00000, 0x00055, 0x00328, 0x00000, 0x00057, 0x00302, 0x00000, 0x00059, 0x00302, 0x00000, 0x00059, 0x00308, 0x00000, 0x0005a, 0x00301, 0x00000, 0x0005a, 0x00307, 0x00000, 0x0005a, 0x0030c, 0x00000, 0x0004f, 0x0031b, 0x00000, 0x00055, 0x0031b, 0x00000, 0x00041, 0x0030c, 0x00000, 0x00049, 0x0030c, 0x00000, 0x0004f, 0x0030c, 0x00000, 0x00055, 0x0030c, 0x00000, 0x000dc, 0x00304, 0x00000, 0x000dc, 0x00301, 0x00000, 0x000dc, 0x0030c, 0x00000, 0x000dc, 0x00300, 0x00000, 0x000c4, 0x00304, 0x00000, 0x00226, 0x00304, 0x00000, 0x000c6, 0x00304, 0x00000, 0x00047, 0x0030c, 0x00000, 0x0004b, 0x0030c, 0x00000, 0x0004f, 0x00328, 0x00000, 0x001ea, 0x00304, 0x00000, 0x001b7, 0x0030c, 0x00000, 0x0006a, 0x0030c, 0x00000, 0x00047, 0x00301, 0x00000, 0x0004e, 0x00300, 0x00000, 0x000c5, 0x00301, 0x00000, 0x000c6, 0x00301, 0x00000, 0x000d8, 0x00301, 0x00000, 0x00041, 0x0030f, 0x00000, 0x00041, 0x00311, 0x00000, 0x00045, 0x0030f, 0x00000, 0x00045, 0x00311, 0x00000, 0x00049, 0x0030f, 0x00000, 0x00049, 0x00311, 0x00000, 0x0004f, 0x0030f, 0x00000, 0x0004f, 0x00311, 0x00000, 0x00052, 0x0030f, 0x00000, 0x00052, 0x00311, 0x00000, 0x00055, 0x0030f, 0x00000, 0x00055, 0x00311, 0x00000, 0x00053, 0x00326, 0x00000, 0x00054, 0x00326, 0x00000, 0x00048, 0x0030c, 0x00000, 0x00041, 0x00307, 0x00000, 0x00045, 0x00327, 0x00000, 0x000d6, 0x00304, 0x00000, 0x000d5, 0x00304, 0x00000, 0x0004f, 0x00307, 0x00000, 0x0022e, 0x00304, 0x00000, 0x00059, 0x00304, 0x00000, 0x00020, 0x00306, 0x00000, 0x00020, 0x00307, 0x00000, 0x00020, 0x0030a, 0x00000, 0x00020, 0x00328, 0x00000, 0x00020, 0x00303, 0x00000, 0x00020, 0x0030b, 0x00000, 0x00308, 0x00301, 0x00000, 0x00020, 0x00345, 0x00000, 0x00020, 0x00301, 0x00000, 0x000a8, 0x00301, 0x00000, 0x00391, 0x00301, 0x00000, 0x00395, 0x00301, 0x00000, 0x00397, 0x00301, 0x00000, 0x00399, 0x00301, 0x00000, 0x0039f, 0x00301, 0x00000, 0x003a5, 0x00301, 0x00000, 0x003a9, 0x00301, 0x00000, 0x003ca, 0x00301, 0x00000, 0x00399, 0x00308, 0x00000, 0x003a5, 0x00308, 0x00000, 0x003cb, 0x00301, 0x00000, 0x003d2, 0x00301, 0x00000, 0x003d2, 0x00308, 0x00000, 0x00415, 0x00300, 0x00000, 0x00415, 0x00308, 0x00000, 0x00413, 0x00301, 0x00000, 0x00406, 0x00308, 0x00000, 0x0041a, 0x00301, 0x00000, 0x00418, 0x00300, 0x00000, 0x00423, 0x00306, 0x00000, 0x00418, 0x00306, 0x00000, 0x00474, 0x0030f, 0x00000, 0x00416, 0x00306, 0x00000, 0x00410, 0x00306, 0x00000, 0x00410, 0x00308, 0x00000, 0x00415, 0x00306, 0x00000, 0x004d8, 0x00308, 0x00000, 0x00416, 0x00308, 0x00000, 0x00417, 0x00308, 0x00000, 0x00418, 0x00304, 0x00000, 0x00418, 0x00308, 0x00000, 0x0041e, 0x00308, 0x00000, 0x004e8, 0x00308, 0x00000, 0x0042d, 0x00308, 0x00000, 0x00423, 0x00304, 0x00000, 0x00423, 0x00308, 0x00000, 0x00423, 0x0030b, 0x00000, 0x00427, 0x00308, 0x00000, 0x0042b, 0x00308, 0x00000, 0x00565, 0x00582, 0x00000, 0x00627, 0x00653, 0x00000, 0x00627, 0x00654, 0x00000, 0x00648, 0x00654, 0x00000, 0x00627, 0x00655, 0x00000, 0x0064a, 0x00654, 0x00000, 0x00627, 0x00674, 0x00000, 0x00648, 0x00674, 0x00000, 0x006c7, 0x00674, 0x00000, 0x0064a, 0x00674, 0x00000, 0x006d5, 0x00654, 0x00000, 0x006c1, 0x00654, 0x00000, 0x006d2, 0x00654, 0x00000, 0x00928, 0x0093c, 0x00000, 0x00930, 0x0093c, 0x00000, 0x00933, 0x0093c, 0x00000, 0x00915, 0x0093c, 0x00000, 0x00916, 0x0093c, 0x00000, 0x00917, 0x0093c, 0x00000, 0x0091c, 0x0093c, 0x00000, 0x00921, 0x0093c, 0x00000, 0x00922, 0x0093c, 0x00000, 0x0092b, 0x0093c, 0x00000, 0x0092f, 0x0093c, 0x00000, 0x009c7, 0x009be, 0x00000, 0x009c7, 0x009d7, 0x00000, 0x009a1, 0x009bc, 0x00000, 0x009a2, 0x009bc, 0x00000, 0x009af, 0x009bc, 0x00000, 0x00a32, 0x00a3c, 0x00000, 0x00a38, 0x00a3c, 0x00000, 0x00a16, 0x00a3c, 0x00000, 0x00a17, 0x00a3c, 0x00000, 0x00a1c, 0x00a3c, 0x00000, 0x00a2b, 0x00a3c, 0x00000, 0x00b47, 0x00b56, 0x00000, 0x00b47, 0x00b3e, 0x00000, 0x00b47, 0x00b57, 0x00000, 0x00b21, 0x00b3c, 0x00000, 0x00b22, 0x00b3c, 0x00000, 0x00b92, 0x00bd7, 0x00000, 0x00bc6, 0x00bbe, 0x00000, 0x00bc7, 0x00bbe, 0x00000, 0x00bc6, 0x00bd7, 0x00000, 0x00c46, 0x00c56, 0x00000, 0x00cbf, 0x00cd5, 0x00000, 0x00cc6, 0x00cd5, 0x00000, 0x00cc6, 0x00cd6, 0x00000, 0x00cc6, 0x00cc2, 0x00000, 0x00cca, 0x00cd5, 0x00000, 0x00d46, 0x00d3e, 0x00000, 0x00d47, 0x00d3e, 0x00000, 0x00d46, 0x00d57, 0x00000, 0x00dd9, 0x00dca, 0x00000, 0x00dd9, 0x00dcf, 0x00000, 0x00ddc, 0x00dca, 0x00000, 0x00dd9, 0x00ddf, 0x00000, 0x00e4d, 0x00e32, 0x00000, 0x00ecd, 0x00eb2, 0x00000, 0x00eab, 0x00e99, 0x00000, 0x00eab, 0x00ea1, 0x00000, 0x00f42, 0x00fb7, 0x00000, 0x00f4c, 0x00fb7, 0x00000, 0x00f51, 0x00fb7, 0x00000, 0x00f56, 0x00fb7, 0x00000, 0x00f5b, 0x00fb7, 0x00000, 0x00f40, 0x00fb5, 0x00000, 0x00f71, 0x00f72, 0x00000, 0x00f71, 0x00f74, 0x00000, 0x00fb2, 0x00f80, 0x00000, 0x00fb2, 0x00f81, 0x00000, 0x00fb3, 0x00f80, 0x00000, 0x00fb3, 0x00f81, 0x00000, 0x00f71, 0x00f80, 0x00000, 0x00f92, 0x00fb7, 0x00000, 0x00f9c, 0x00fb7, 0x00000, 0x00fa1, 0x00fb7, 0x00000, 0x00fa6, 0x00fb7, 0x00000, 0x00fab, 0x00fb7, 0x00000, 0x00f90, 0x00fb5, 0x00000, 0x01025, 0x0102e, 0x00000, 0x01b05, 0x01b35, 0x00000, 0x01b07, 0x01b35, 0x00000, 0x01b09, 0x01b35, 0x00000, 0x01b0b, 0x01b35, 0x00000, 0x01b0d, 0x01b35, 0x00000, 0x01b11, 0x01b35, 0x00000, 0x01b3a, 0x01b35, 0x00000, 0x01b3c, 0x01b35, 0x00000, 0x01b3e, 0x01b35, 0x00000, 0x01b3f, 0x01b35, 0x00000, 0x01b42, 0x01b35, 0x00000, 0x00041, 0x00325, 0x00000, 0x00042, 0x00307, 0x00000, 0x00042, 0x00323, 0x00000, 0x00042, 0x00331, 0x00000, 0x000c7, 0x00301, 0x00000, 0x00044, 0x00307, 0x00000, 0x00044, 0x00323, 0x00000, 0x00044, 0x00331, 0x00000, 0x00044, 0x00327, 0x00000, 0x00044, 0x0032d, 0x00000, 0x00112, 0x00300, 0x00000, 0x00112, 0x00301, 0x00000, 0x00045, 0x0032d, 0x00000, 0x00045, 0x00330, 0x00000, 0x00228, 0x00306, 0x00000, 0x00046, 0x00307, 0x00000, 0x00047, 0x00304, 0x00000, 0x00048, 0x00307, 0x00000, 0x00048, 0x00323, 0x00000, 0x00048, 0x00308, 0x00000, 0x00048, 0x00327, 0x00000, 0x00048, 0x0032e, 0x00000, 0x00049, 0x00330, 0x00000, 0x000cf, 0x00301, 0x00000, 0x0004b, 0x00301, 0x00000, 0x0004b, 0x00323, 0x00000, 0x0004b, 0x00331, 0x00000, 0x0004c, 0x00323, 0x00000, 0x01e36, 0x00304, 0x00000, 0x0004c, 0x00331, 0x00000, 0x0004c, 0x0032d, 0x00000, 0x0004d, 0x00301, 0x00000, 0x0004d, 0x00307, 0x00000, 0x0004d, 0x00323, 0x00000, 0x0004e, 0x00307, 0x00000, 0x0004e, 0x00323, 0x00000, 0x0004e, 0x00331, 0x00000, 0x0004e, 0x0032d, 0x00000, 0x000d5, 0x00301, 0x00000, 0x000d5, 0x00308, 0x00000, 0x0014c, 0x00300, 0x00000, 0x0014c, 0x00301, 0x00000, 0x00050, 0x00301, 0x00000, 0x00050, 0x00307, 0x00000, 0x00052, 0x00307, 0x00000, 0x00052, 0x00323, 0x00000, 0x01e5a, 0x00304, 0x00000, 0x00052, 0x00331, 0x00000, 0x00053, 0x00307, 0x00000, 0x00053, 0x00323, 0x00000, 0x0015a, 0x00307, 0x00000, 0x00160, 0x00307, 0x00000, 0x01e62, 0x00307, 0x00000, 0x00054, 0x00307, 0x00000, 0x00054, 0x00323, 0x00000, 0x00054, 0x00331, 0x00000, 0x00054, 0x0032d, 0x00000, 0x00055, 0x00324, 0x00000, 0x00055, 0x00330, 0x00000, 0x00055, 0x0032d, 0x00000, 0x00168, 0x00301, 0x00000, 0x0016a, 0x00308, 0x00000, 0x00056, 0x00303, 0x00000, 0x00056, 0x00323, 0x00000, 0x00057, 0x00300, 0x00000, 0x00057, 0x00301, 0x00000, 0x00057, 0x00308, 0x00000, 0x00057, 0x00307, 0x00000, 0x00057, 0x00323, 0x00000, 0x00058, 0x00307, 0x00000, 0x00058, 0x00308, 0x00000, 0x00059, 0x00307, 0x00000, 0x0005a, 0x00302, 0x00000, 0x0005a, 0x00323, 0x00000, 0x0005a, 0x00331, 0x00000, 0x00068, 0x00331, 0x00000, 0x00074, 0x00308, 0x00000, 0x00077, 0x0030a, 0x00000, 0x00079, 0x0030a, 0x00000, 0x00061, 0x002be, 0x00000, 0x00041, 0x00323, 0x00000, 0x00041, 0x00309, 0x00000, 0x000c2, 0x00301, 0x00000, 0x000c2, 0x00300, 0x00000, 0x000c2, 0x00309, 0x00000, 0x000c2, 0x00303, 0x00000, 0x01ea0, 0x00302, 0x00000, 0x00102, 0x00301, 0x00000, 0x00102, 0x00300, 0x00000, 0x00102, 0x00309, 0x00000, 0x00102, 0x00303, 0x00000, 0x01ea0, 0x00306, 0x00000, 0x00045, 0x00323, 0x00000, 0x00045, 0x00309, 0x00000, 0x00045, 0x00303, 0x00000, 0x000ca, 0x00301, 0x00000, 0x000ca, 0x00300, 0x00000, 0x000ca, 0x00309, 0x00000, 0x000ca, 0x00303, 0x00000, 0x01eb8, 0x00302, 0x00000, 0x00049, 0x00309, 0x00000, 0x00049, 0x00323, 0x00000, 0x0004f, 0x00323, 0x00000, 0x0004f, 0x00309, 0x00000, 0x000d4, 0x00301, 0x00000, 0x000d4, 0x00300, 0x00000, 0x000d4, 0x00309, 0x00000, 0x000d4, 0x00303, 0x00000, 0x01ecc, 0x00302, 0x00000, 0x001a0, 0x00301, 0x00000, 0x001a0, 0x00300, 0x00000, 0x001a0, 0x00309, 0x00000, 0x001a0, 0x00303, 0x00000, 0x001a0, 0x00323, 0x00000, 0x00055, 0x00323, 0x00000, 0x00055, 0x00309, 0x00000, 0x001af, 0x00301, 0x00000, 0x001af, 0x00300, 0x00000, 0x001af, 0x00309, 0x00000, 0x001af, 0x00303, 0x00000, 0x001af, 0x00323, 0x00000, 0x00059, 0x00300, 0x00000, 0x00059, 0x00323, 0x00000, 0x00059, 0x00309, 0x00000, 0x00059, 0x00303, 0x00000, 0x00391, 0x00313, 0x00000, 0x00391, 0x00314, 0x00000, 0x01f08, 0x00300, 0x00000, 0x01f09, 0x00300, 0x00000, 0x01f08, 0x00301, 0x00000, 0x01f09, 0x00301, 0x00000, 0x01f08, 0x00342, 0x00000, 0x01f09, 0x00342, 0x00000, 0x00395, 0x00313, 0x00000, 0x00395, 0x00314, 0x00000, 0x01f18, 0x00300, 0x00000, 0x01f19, 0x00300, 0x00000, 0x01f18, 0x00301, 0x00000, 0x01f19, 0x00301, 0x00000, 0x00397, 0x00313, 0x00000, 0x00397, 0x00314, 0x00000, 0x01f28, 0x00300, 0x00000, 0x01f29, 0x00300, 0x00000, 0x01f28, 0x00301, 0x00000, 0x01f29, 0x00301, 0x00000, 0x01f28, 0x00342, 0x00000, 0x01f29, 0x00342, 0x00000, 0x00399, 0x00313, 0x00000, 0x00399, 0x00314, 0x00000, 0x01f38, 0x00300, 0x00000, 0x01f39, 0x00300, 0x00000, 0x01f38, 0x00301, 0x00000, 0x01f39, 0x00301, 0x00000, 0x01f38, 0x00342, 0x00000, 0x01f39, 0x00342, 0x00000, 0x0039f, 0x00313, 0x00000, 0x0039f, 0x00314, 0x00000, 0x01f48, 0x00300, 0x00000, 0x01f49, 0x00300, 0x00000, 0x01f48, 0x00301, 0x00000, 0x01f49, 0x00301, 0x00000, 0x003c5, 0x00313, 0x00000, 0x01f50, 0x00300, 0x00000, 0x01f50, 0x00301, 0x00000, 0x01f50, 0x00342, 0x00000, 0x003a5, 0x00314, 0x00000, 0x01f59, 0x00300, 0x00000, 0x01f59, 0x00301, 0x00000, 0x01f59, 0x00342, 0x00000, 0x003a9, 0x00313, 0x00000, 0x003a9, 0x00314, 0x00000, 0x01f68, 0x00300, 0x00000, 0x01f69, 0x00300, 0x00000, 0x01f68, 0x00301, 0x00000, 0x01f69, 0x00301, 0x00000, 0x01f68, 0x00342, 0x00000, 0x01f69, 0x00342, 0x00000, 0x01f08, 0x00345, 0x00000, 0x01f09, 0x00345, 0x00000, 0x01f0a, 0x00345, 0x00000, 0x01f0b, 0x00345, 0x00000, 0x01f0c, 0x00345, 0x00000, 0x01f0d, 0x00345, 0x00000, 0x01f0e, 0x00345, 0x00000, 0x01f0f, 0x00345, 0x00000, 0x01f28, 0x00345, 0x00000, 0x01f29, 0x00345, 0x00000, 0x01f2a, 0x00345, 0x00000, 0x01f2b, 0x00345, 0x00000, 0x01f2c, 0x00345, 0x00000, 0x01f2d, 0x00345, 0x00000, 0x01f2e, 0x00345, 0x00000, 0x01f2f, 0x00345, 0x00000, 0x01f68, 0x00345, 0x00000, 0x01f69, 0x00345, 0x00000, 0x01f6a, 0x00345, 0x00000, 0x01f6b, 0x00345, 0x00000, 0x01f6c, 0x00345, 0x00000, 0x01f6d, 0x00345, 0x00000, 0x01f6e, 0x00345, 0x00000, 0x01f6f, 0x00345, 0x00000, 0x01f70, 0x00345, 0x00000, 0x003ac, 0x00345, 0x00000, 0x003b1, 0x00342, 0x00000, 0x01fb6, 0x00345, 0x00000, 0x00391, 0x00306, 0x00000, 0x00391, 0x00304, 0x00000, 0x00391, 0x00300, 0x00000, 0x00391, 0x00345, 0x00000, 0x00020, 0x00313, 0x00000, 0x00020, 0x00313, 0x00000, 0x00020, 0x00342, 0x00000, 0x000a8, 0x00342, 0x00000, 0x01f74, 0x00345, 0x00000, 0x003ae, 0x00345, 0x00000, 0x003b7, 0x00342, 0x00000, 0x01fc6, 0x00345, 0x00000, 0x00395, 0x00300, 0x00000, 0x00397, 0x00300, 0x00000, 0x00397, 0x00345, 0x00000, 0x01fbf, 0x00300, 0x00000, 0x01fbf, 0x00301, 0x00000, 0x01fbf, 0x00342, 0x00000, 0x003ca, 0x00300, 0x00000, 0x003b9, 0x00342, 0x00000, 0x003ca, 0x00342, 0x00000, 0x00399, 0x00306, 0x00000, 0x00399, 0x00304, 0x00000, 0x00399, 0x00300, 0x00000, 0x01ffe, 0x00300, 0x00000, 0x01ffe, 0x00301, 0x00000, 0x01ffe, 0x00342, 0x00000, 0x003cb, 0x00300, 0x00000, 0x003c1, 0x00313, 0x00000, 0x003c5, 0x00342, 0x00000, 0x003cb, 0x00342, 0x00000, 0x003a5, 0x00306, 0x00000, 0x003a5, 0x00304, 0x00000, 0x003a5, 0x00300, 0x00000, 0x003a1, 0x00314, 0x00000, 0x000a8, 0x00300, 0x00000, 0x01f7c, 0x00345, 0x00000, 0x003ce, 0x00345, 0x00000, 0x003c9, 0x00342, 0x00000, 0x01ff6, 0x00345, 0x00000, 0x0039f, 0x00300, 0x00000, 0x003a9, 0x00300, 0x00000, 0x003a9, 0x00345, 0x00000, 0x00020, 0x00314, 0x00000, 0x00020, 0x00333, 0x00000, 0x0002e, 0x0002e, 0x00000, 0x0002e, 0x0002e, 0x0002e, 0x00000, 0x02032, 0x02032, 0x00000, 0x02032, 0x02032, 0x02032, 0x00000, 0x02035, 0x02035, 0x00000, 0x02035, 0x02035, 0x02035, 0x00000, 0x00021, 0x00021, 0x00000, 0x00020, 0x00305, 0x00000, 0x0003f, 0x0003f, 0x00000, 0x0003f, 0x00021, 0x00000, 0x00021, 0x0003f, 0x00000, 0x02032, 0x02032, 0x02032, 0x02032, 0x00000, 0x00052, 0x00073, 0x00000, 0x00061, 0x0002f, 0x00063, 0x00000, 0x00061, 0x0002f, 0x00073, 0x00000, 0x000b0, 0x00043, 0x00000, 0x00063, 0x0002f, 0x0006f, 0x00000, 0x00063, 0x0002f, 0x00075, 0x00000, 0x000b0, 0x00046, 0x00000, 0x0004e, 0x0006f, 0x00000, 0x00053, 0x0004d, 0x00000, 0x00054, 0x00045, 0x0004c, 0x00000, 0x00054, 0x0004d, 0x00000, 0x00046, 0x00041, 0x00058, 0x00000, 0x00031, 0x02044, 0x00037, 0x00000, 0x00031, 0x02044, 0x00039, 0x00000, 0x00031, 0x02044, 0x00031, 0x00030, 0x00000, 0x00031, 0x02044, 0x00033, 0x00000, 0x00032, 0x02044, 0x00033, 0x00000, 0x00031, 0x02044, 0x00035, 0x00000, 0x00032, 0x02044, 0x00035, 0x00000, 0x00033, 0x02044, 0x00035, 0x00000, 0x00034, 0x02044, 0x00035, 0x00000, 0x00031, 0x02044, 0x00036, 0x00000, 0x00035, 0x02044, 0x00036, 0x00000, 0x00031, 0x02044, 0x00038, 0x00000, 0x00033, 0x02044, 0x00038, 0x00000, 0x00035, 0x02044, 0x00038, 0x00000, 0x00037, 0x02044, 0x00038, 0x00000, 0x00031, 0x02044, 0x00000, 0x00049, 0x00049, 0x00000, 0x00049, 0x00049, 0x00049, 0x00000, 0x00049, 0x00056, 0x00000, 0x00056, 0x00049, 0x00000, 0x00056, 0x00049, 0x00049, 0x00000, 0x00056, 0x00049, 0x00049, 0x00049, 0x00000, 0x00049, 0x00058, 0x00000, 0x00058, 0x00049, 0x00000, 0x00058, 0x00049, 0x00049, 0x00000, 0x00030, 0x02044, 0x00033, 0x00000, 0x02190, 0x00338, 0x00000, 0x02192, 0x00338, 0x00000, 0x02194, 0x00338, 0x00000, 0x021d0, 0x00338, 0x00000, 0x021d4, 0x00338, 0x00000, 0x021d2, 0x00338, 0x00000, 0x02203, 0x00338, 0x00000, 0x02208, 0x00338, 0x00000, 0x0220b, 0x00338, 0x00000, 0x02223, 0x00338, 0x00000, 0x02225, 0x00338, 0x00000, 0x0222b, 0x0222b, 0x00000, 0x0222b, 0x0222b, 0x0222b, 0x00000, 0x0222e, 0x0222e, 0x00000, 0x0222e, 0x0222e, 0x0222e, 0x00000, 0x0223c, 0x00338, 0x00000, 0x02243, 0x00338, 0x00000, 0x02245, 0x00338, 0x00000, 0x02248, 0x00338, 0x00000, 0x0003d, 0x00338, 0x00000, 0x02261, 0x00338, 0x00000, 0x0224d, 0x00338, 0x00000, 0x0003c, 0x00338, 0x00000, 0x0003e, 0x00338, 0x00000, 0x02264, 0x00338, 0x00000, 0x02265, 0x00338, 0x00000, 0x02272, 0x00338, 0x00000, 0x02273, 0x00338, 0x00000, 0x02276, 0x00338, 0x00000, 0x02277, 0x00338, 0x00000, 0x0227a, 0x00338, 0x00000, 0x0227b, 0x00338, 0x00000, 0x02282, 0x00338, 0x00000, 0x02283, 0x00338, 0x00000, 0x02286, 0x00338, 0x00000, 0x02287, 0x00338, 0x00000, 0x022a2, 0x00338, 0x00000, 0x022a8, 0x00338, 0x00000, 0x022a9, 0x00338, 0x00000, 0x022ab, 0x00338, 0x00000, 0x0227c, 0x00338, 0x00000, 0x0227d, 0x00338, 0x00000, 0x02291, 0x00338, 0x00000, 0x02292, 0x00338, 0x00000, 0x022b2, 0x00338, 0x00000, 0x022b3, 0x00338, 0x00000, 0x022b4, 0x00338, 0x00000, 0x022b5, 0x00338, 0x00000, 0x00031, 0x00030, 0x00000, 0x00031, 0x00031, 0x00000, 0x00031, 0x00032, 0x00000, 0x00031, 0x00033, 0x00000, 0x00031, 0x00034, 0x00000, 0x00031, 0x00035, 0x00000, 0x00031, 0x00036, 0x00000, 0x00031, 0x00037, 0x00000, 0x00031, 0x00038, 0x00000, 0x00031, 0x00039, 0x00000, 0x00032, 0x00030, 0x00000, 0x00028, 0x00031, 0x00029, 0x00000, 0x00028, 0x00032, 0x00029, 0x00000, 0x00028, 0x00033, 0x00029, 0x00000, 0x00028, 0x00034, 0x00029, 0x00000, 0x00028, 0x00035, 0x00029, 0x00000, 0x00028, 0x00036, 0x00029, 0x00000, 0x00028, 0x00037, 0x00029, 0x00000, 0x00028, 0x00038, 0x00029, 0x00000, 0x00028, 0x00039, 0x00029, 0x00000, 0x00028, 0x00031, 0x00030, 0x00029, 0x00000, 0x00028, 0x00031, 0x00031, 0x00029, 0x00000, 0x00028, 0x00031, 0x00032, 0x00029, 0x00000, 0x00028, 0x00031, 0x00033, 0x00029, 0x00000, 0x00028, 0x00031, 0x00034, 0x00029, 0x00000, 0x00028, 0x00031, 0x00035, 0x00029, 0x00000, 0x00028, 0x00031, 0x00036, 0x00029, 0x00000, 0x00028, 0x00031, 0x00037, 0x00029, 0x00000, 0x00028, 0x00031, 0x00038, 0x00029, 0x00000, 0x00028, 0x00031, 0x00039, 0x00029, 0x00000, 0x00028, 0x00032, 0x00030, 0x00029, 0x00000, 0x00031, 0x0002e, 0x00000, 0x00032, 0x0002e, 0x00000, 0x00033, 0x0002e, 0x00000, 0x00034, 0x0002e, 0x00000, 0x00035, 0x0002e, 0x00000, 0x00036, 0x0002e, 0x00000, 0x00037, 0x0002e, 0x00000, 0x00038, 0x0002e, 0x00000, 0x00039, 0x0002e, 0x00000, 0x00031, 0x00030, 0x0002e, 0x00000, 0x00031, 0x00031, 0x0002e, 0x00000, 0x00031, 0x00032, 0x0002e, 0x00000, 0x00031, 0x00033, 0x0002e, 0x00000, 0x00031, 0x00034, 0x0002e, 0x00000, 0x00031, 0x00035, 0x0002e, 0x00000, 0x00031, 0x00036, 0x0002e, 0x00000, 0x00031, 0x00037, 0x0002e, 0x00000, 0x00031, 0x00038, 0x0002e, 0x00000, 0x00031, 0x00039, 0x0002e, 0x00000, 0x00032, 0x00030, 0x0002e, 0x00000, 0x00028, 0x00061, 0x00029, 0x00000, 0x00028, 0x00062, 0x00029, 0x00000, 0x00028, 0x00063, 0x00029, 0x00000, 0x00028, 0x00064, 0x00029, 0x00000, 0x00028, 0x00065, 0x00029, 0x00000, 0x00028, 0x00066, 0x00029, 0x00000, 0x00028, 0x00067, 0x00029, 0x00000, 0x00028, 0x00068, 0x00029, 0x00000, 0x00028, 0x00069, 0x00029, 0x00000, 0x00028, 0x0006a, 0x00029, 0x00000, 0x00028, 0x0006b, 0x00029, 0x00000, 0x00028, 0x0006c, 0x00029, 0x00000, 0x00028, 0x0006d, 0x00029, 0x00000, 0x00028, 0x0006e, 0x00029, 0x00000, 0x00028, 0x0006f, 0x00029, 0x00000, 0x00028, 0x00070, 0x00029, 0x00000, 0x00028, 0x00071, 0x00029, 0x00000, 0x00028, 0x00072, 0x00029, 0x00000, 0x00028, 0x00073, 0x00029, 0x00000, 0x00028, 0x00074, 0x00029, 0x00000, 0x00028, 0x00075, 0x00029, 0x00000, 0x00028, 0x00076, 0x00029, 0x00000, 0x00028, 0x00077, 0x00029, 0x00000, 0x00028, 0x00078, 0x00029, 0x00000, 0x00028, 0x00079, 0x00029, 0x00000, 0x00028, 0x0007a, 0x00029, 0x00000, 0x0222b, 0x0222b, 0x0222b, 0x0222b, 0x00000, 0x0003a, 0x0003a, 0x0003d, 0x00000, 0x0003d, 0x0003d, 0x00000, 0x0003d, 0x0003d, 0x0003d, 0x00000, 0x02add, 0x00338, 0x00000, 0x0304b, 0x03099, 0x00000, 0x0304d, 0x03099, 0x00000, 0x0304f, 0x03099, 0x00000, 0x03051, 0x03099, 0x00000, 0x03053, 0x03099, 0x00000, 0x03055, 0x03099, 0x00000, 0x03057, 0x03099, 0x00000, 0x03059, 0x03099, 0x00000, 0x0305b, 0x03099, 0x00000, 0x0305d, 0x03099, 0x00000, 0x0305f, 0x03099, 0x00000, 0x03061, 0x03099, 0x00000, 0x03064, 0x03099, 0x00000, 0x03066, 0x03099, 0x00000, 0x03068, 0x03099, 0x00000, 0x0306f, 0x03099, 0x00000, 0x0306f, 0x0309a, 0x00000, 0x03072, 0x03099, 0x00000, 0x03072, 0x0309a, 0x00000, 0x03075, 0x03099, 0x00000, 0x03075, 0x0309a, 0x00000, 0x03078, 0x03099, 0x00000, 0x03078, 0x0309a, 0x00000, 0x0307b, 0x03099, 0x00000, 0x0307b, 0x0309a, 0x00000, 0x03046, 0x03099, 0x00000, 0x00020, 0x03099, 0x00000, 0x00020, 0x0309a, 0x00000, 0x0309d, 0x03099, 0x00000, 0x03088, 0x0308a, 0x00000, 0x030ab, 0x03099, 0x00000, 0x030ad, 0x03099, 0x00000, 0x030af, 0x03099, 0x00000, 0x030b1, 0x03099, 0x00000, 0x030b3, 0x03099, 0x00000, 0x030b5, 0x03099, 0x00000, 0x030b7, 0x03099, 0x00000, 0x030b9, 0x03099, 0x00000, 0x030bb, 0x03099, 0x00000, 0x030bd, 0x03099, 0x00000, 0x030bf, 0x03099, 0x00000, 0x030c1, 0x03099, 0x00000, 0x030c4, 0x03099, 0x00000, 0x030c6, 0x03099, 0x00000, 0x030c8, 0x03099, 0x00000, 0x030cf, 0x03099, 0x00000, 0x030cf, 0x0309a, 0x00000, 0x030d2, 0x03099, 0x00000, 0x030d2, 0x0309a, 0x00000, 0x030d5, 0x03099, 0x00000, 0x030d5, 0x0309a, 0x00000, 0x030d8, 0x03099, 0x00000, 0x030d8, 0x0309a, 0x00000, 0x030db, 0x03099, 0x00000, 0x030db, 0x0309a, 0x00000, 0x030a6, 0x03099, 0x00000, 0x030ef, 0x03099, 0x00000, 0x030f0, 0x03099, 0x00000, 0x030f1, 0x03099, 0x00000, 0x030f2, 0x03099, 0x00000, 0x030fd, 0x03099, 0x00000, 0x030b3, 0x030c8, 0x00000, 0x00028, 0x01100, 0x00029, 0x00000, 0x00028, 0x01102, 0x00029, 0x00000, 0x00028, 0x01103, 0x00029, 0x00000, 0x00028, 0x01105, 0x00029, 0x00000, 0x00028, 0x01106, 0x00029, 0x00000, 0x00028, 0x01107, 0x00029, 0x00000, 0x00028, 0x01109, 0x00029, 0x00000, 0x00028, 0x0110b, 0x00029, 0x00000, 0x00028, 0x0110c, 0x00029, 0x00000, 0x00028, 0x0110e, 0x00029, 0x00000, 0x00028, 0x0110f, 0x00029, 0x00000, 0x00028, 0x01110, 0x00029, 0x00000, 0x00028, 0x01111, 0x00029, 0x00000, 0x00028, 0x01112, 0x00029, 0x00000, 0x00028, 0x01100, 0x01161, 0x00029, 0x00000, 0x00028, 0x01102, 0x01161, 0x00029, 0x00000, 0x00028, 0x01103, 0x01161, 0x00029, 0x00000, 0x00028, 0x01105, 0x01161, 0x00029, 0x00000, 0x00028, 0x01106, 0x01161, 0x00029, 0x00000, 0x00028, 0x01107, 0x01161, 0x00029, 0x00000, 0x00028, 0x01109, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110b, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110c, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110e, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110f, 0x01161, 0x00029, 0x00000, 0x00028, 0x01110, 0x01161, 0x00029, 0x00000, 0x00028, 0x01111, 0x01161, 0x00029, 0x00000, 0x00028, 0x01112, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110c, 0x0116e, 0x00029, 0x00000, 0x00028, 0x0110b, 0x01169, 0x0110c, 0x01165, 0x011ab, 0x00029, 0x00000, 0x00028, 0x0110b, 0x01169, 0x01112, 0x0116e, 0x00029, 0x00000, 0x00028, 0x04e00, 0x00029, 0x00000, 0x00028, 0x04e8c, 0x00029, 0x00000, 0x00028, 0x04e09, 0x00029, 0x00000, 0x00028, 0x056db, 0x00029, 0x00000, 0x00028, 0x04e94, 0x00029, 0x00000, 0x00028, 0x0516d, 0x00029, 0x00000, 0x00028, 0x04e03, 0x00029, 0x00000, 0x00028, 0x0516b, 0x00029, 0x00000, 0x00028, 0x04e5d, 0x00029, 0x00000, 0x00028, 0x05341, 0x00029, 0x00000, 0x00028, 0x06708, 0x00029, 0x00000, 0x00028, 0x0706b, 0x00029, 0x00000, 0x00028, 0x06c34, 0x00029, 0x00000, 0x00028, 0x06728, 0x00029, 0x00000, 0x00028, 0x091d1, 0x00029, 0x00000, 0x00028, 0x0571f, 0x00029, 0x00000, 0x00028, 0x065e5, 0x00029, 0x00000, 0x00028, 0x0682a, 0x00029, 0x00000, 0x00028, 0x06709, 0x00029, 0x00000, 0x00028, 0x0793e, 0x00029, 0x00000, 0x00028, 0x0540d, 0x00029, 0x00000, 0x00028, 0x07279, 0x00029, 0x00000, 0x00028, 0x08ca1, 0x00029, 0x00000, 0x00028, 0x0795d, 0x00029, 0x00000, 0x00028, 0x052b4, 0x00029, 0x00000, 0x00028, 0x04ee3, 0x00029, 0x00000, 0x00028, 0x0547c, 0x00029, 0x00000, 0x00028, 0x05b66, 0x00029, 0x00000, 0x00028, 0x076e3, 0x00029, 0x00000, 0x00028, 0x04f01, 0x00029, 0x00000, 0x00028, 0x08cc7, 0x00029, 0x00000, 0x00028, 0x05354, 0x00029, 0x00000, 0x00028, 0x0796d, 0x00029, 0x00000, 0x00028, 0x04f11, 0x00029, 0x00000, 0x00028, 0x081ea, 0x00029, 0x00000, 0x00028, 0x081f3, 0x00029, 0x00000, 0x00050, 0x00054, 0x00045, 0x00000, 0x00032, 0x00031, 0x00000, 0x00032, 0x00032, 0x00000, 0x00032, 0x00033, 0x00000, 0x00032, 0x00034, 0x00000, 0x00032, 0x00035, 0x00000, 0x00032, 0x00036, 0x00000, 0x00032, 0x00037, 0x00000, 0x00032, 0x00038, 0x00000, 0x00032, 0x00039, 0x00000, 0x00033, 0x00030, 0x00000, 0x00033, 0x00031, 0x00000, 0x00033, 0x00032, 0x00000, 0x00033, 0x00033, 0x00000, 0x00033, 0x00034, 0x00000, 0x00033, 0x00035, 0x00000, 0x01100, 0x01161, 0x00000, 0x01102, 0x01161, 0x00000, 0x01103, 0x01161, 0x00000, 0x01105, 0x01161, 0x00000, 0x01106, 0x01161, 0x00000, 0x01107, 0x01161, 0x00000, 0x01109, 0x01161, 0x00000, 0x0110b, 0x01161, 0x00000, 0x0110c, 0x01161, 0x00000, 0x0110e, 0x01161, 0x00000, 0x0110f, 0x01161, 0x00000, 0x01110, 0x01161, 0x00000, 0x01111, 0x01161, 0x00000, 0x01112, 0x01161, 0x00000, 0x0110e, 0x01161, 0x011b7, 0x01100, 0x01169, 0x00000, 0x0110c, 0x0116e, 0x0110b, 0x01174, 0x00000, 0x0110b, 0x0116e, 0x00000, 0x00033, 0x00036, 0x00000, 0x00033, 0x00037, 0x00000, 0x00033, 0x00038, 0x00000, 0x00033, 0x00039, 0x00000, 0x00034, 0x00030, 0x00000, 0x00034, 0x00031, 0x00000, 0x00034, 0x00032, 0x00000, 0x00034, 0x00033, 0x00000, 0x00034, 0x00034, 0x00000, 0x00034, 0x00035, 0x00000, 0x00034, 0x00036, 0x00000, 0x00034, 0x00037, 0x00000, 0x00034, 0x00038, 0x00000, 0x00034, 0x00039, 0x00000, 0x00035, 0x00030, 0x00000, 0x00031, 0x06708, 0x00000, 0x00032, 0x06708, 0x00000, 0x00033, 0x06708, 0x00000, 0x00034, 0x06708, 0x00000, 0x00035, 0x06708, 0x00000, 0x00036, 0x06708, 0x00000, 0x00037, 0x06708, 0x00000, 0x00038, 0x06708, 0x00000, 0x00039, 0x06708, 0x00000, 0x00031, 0x00030, 0x06708, 0x00000, 0x00031, 0x00031, 0x06708, 0x00000, 0x00031, 0x00032, 0x06708, 0x00000, 0x00048, 0x00067, 0x00000, 0x00065, 0x00072, 0x00067, 0x00000, 0x00065, 0x00056, 0x00000, 0x0004c, 0x00054, 0x00044, 0x00000, 0x030a2, 0x030d1, 0x030fc, 0x030c8, 0x00000, 0x030a2, 0x030eb, 0x030d5, 0x030a1, 0x00000, 0x030a2, 0x030f3, 0x030da, 0x030a2, 0x00000, 0x030a2, 0x030fc, 0x030eb, 0x00000, 0x030a4, 0x030cb, 0x030f3, 0x030b0, 0x00000, 0x030a4, 0x030f3, 0x030c1, 0x00000, 0x030a6, 0x030a9, 0x030f3, 0x00000, 0x030a8, 0x030b9, 0x030af, 0x030fc, 0x030c9, 0x00000, 0x030a8, 0x030fc, 0x030ab, 0x030fc, 0x00000, 0x030aa, 0x030f3, 0x030b9, 0x00000, 0x030aa, 0x030fc, 0x030e0, 0x00000, 0x030ab, 0x030a4, 0x030ea, 0x00000, 0x030ab, 0x030e9, 0x030c3, 0x030c8, 0x00000, 0x030ab, 0x030ed, 0x030ea, 0x030fc, 0x00000, 0x030ac, 0x030ed, 0x030f3, 0x00000, 0x030ac, 0x030f3, 0x030de, 0x00000, 0x030ae, 0x030ac, 0x00000, 0x030ae, 0x030cb, 0x030fc, 0x00000, 0x030ad, 0x030e5, 0x030ea, 0x030fc, 0x00000, 0x030ae, 0x030eb, 0x030c0, 0x030fc, 0x00000, 0x030ad, 0x030ed, 0x00000, 0x030ad, 0x030ed, 0x030b0, 0x030e9, 0x030e0, 0x00000, 0x030ad, 0x030ed, 0x030e1, 0x030fc, 0x030c8, 0x030eb, 0x00000, 0x030ad, 0x030ed, 0x030ef, 0x030c3, 0x030c8, 0x00000, 0x030b0, 0x030e9, 0x030e0, 0x00000, 0x030b0, 0x030e9, 0x030e0, 0x030c8, 0x030f3, 0x00000, 0x030af, 0x030eb, 0x030bc, 0x030a4, 0x030ed, 0x00000, 0x030af, 0x030ed, 0x030fc, 0x030cd, 0x00000, 0x030b1, 0x030fc, 0x030b9, 0x00000, 0x030b3, 0x030eb, 0x030ca, 0x00000, 0x030b3, 0x030fc, 0x030dd, 0x00000, 0x030b5, 0x030a4, 0x030af, 0x030eb, 0x00000, 0x030b5, 0x030f3, 0x030c1, 0x030fc, 0x030e0, 0x00000, 0x030b7, 0x030ea, 0x030f3, 0x030b0, 0x00000, 0x030bb, 0x030f3, 0x030c1, 0x00000, 0x030bb, 0x030f3, 0x030c8, 0x00000, 0x030c0, 0x030fc, 0x030b9, 0x00000, 0x030c7, 0x030b7, 0x00000, 0x030c9, 0x030eb, 0x00000, 0x030c8, 0x030f3, 0x00000, 0x030ca, 0x030ce, 0x00000, 0x030ce, 0x030c3, 0x030c8, 0x00000, 0x030cf, 0x030a4, 0x030c4, 0x00000, 0x030d1, 0x030fc, 0x030bb, 0x030f3, 0x030c8, 0x00000, 0x030d1, 0x030fc, 0x030c4, 0x00000, 0x030d0, 0x030fc, 0x030ec, 0x030eb, 0x00000, 0x030d4, 0x030a2, 0x030b9, 0x030c8, 0x030eb, 0x00000, 0x030d4, 0x030af, 0x030eb, 0x00000, 0x030d4, 0x030b3, 0x00000, 0x030d3, 0x030eb, 0x00000, 0x030d5, 0x030a1, 0x030e9, 0x030c3, 0x030c9, 0x00000, 0x030d5, 0x030a3, 0x030fc, 0x030c8, 0x00000, 0x030d6, 0x030c3, 0x030b7, 0x030a7, 0x030eb, 0x00000, 0x030d5, 0x030e9, 0x030f3, 0x00000, 0x030d8, 0x030af, 0x030bf, 0x030fc, 0x030eb, 0x00000, 0x030da, 0x030bd, 0x00000, 0x030da, 0x030cb, 0x030d2, 0x00000, 0x030d8, 0x030eb, 0x030c4, 0x00000, 0x030da, 0x030f3, 0x030b9, 0x00000, 0x030da, 0x030fc, 0x030b8, 0x00000, 0x030d9, 0x030fc, 0x030bf, 0x00000, 0x030dd, 0x030a4, 0x030f3, 0x030c8, 0x00000, 0x030dc, 0x030eb, 0x030c8, 0x00000, 0x030db, 0x030f3, 0x00000, 0x030dd, 0x030f3, 0x030c9, 0x00000, 0x030db, 0x030fc, 0x030eb, 0x00000, 0x030db, 0x030fc, 0x030f3, 0x00000, 0x030de, 0x030a4, 0x030af, 0x030ed, 0x00000, 0x030de, 0x030a4, 0x030eb, 0x00000, 0x030de, 0x030c3, 0x030cf, 0x00000, 0x030de, 0x030eb, 0x030af, 0x00000, 0x030de, 0x030f3, 0x030b7, 0x030e7, 0x030f3, 0x00000, 0x030df, 0x030af, 0x030ed, 0x030f3, 0x00000, 0x030df, 0x030ea, 0x00000, 0x030df, 0x030ea, 0x030d0, 0x030fc, 0x030eb, 0x00000, 0x030e1, 0x030ac, 0x00000, 0x030e1, 0x030ac, 0x030c8, 0x030f3, 0x00000, 0x030e1, 0x030fc, 0x030c8, 0x030eb, 0x00000, 0x030e4, 0x030fc, 0x030c9, 0x00000, 0x030e4, 0x030fc, 0x030eb, 0x00000, 0x030e6, 0x030a2, 0x030f3, 0x00000, 0x030ea, 0x030c3, 0x030c8, 0x030eb, 0x00000, 0x030ea, 0x030e9, 0x00000, 0x030eb, 0x030d4, 0x030fc, 0x00000, 0x030eb, 0x030fc, 0x030d6, 0x030eb, 0x00000, 0x030ec, 0x030e0, 0x00000, 0x030ec, 0x030f3, 0x030c8, 0x030b2, 0x030f3, 0x00000, 0x030ef, 0x030c3, 0x030c8, 0x00000, 0x00030, 0x070b9, 0x00000, 0x00031, 0x070b9, 0x00000, 0x00032, 0x070b9, 0x00000, 0x00033, 0x070b9, 0x00000, 0x00034, 0x070b9, 0x00000, 0x00035, 0x070b9, 0x00000, 0x00036, 0x070b9, 0x00000, 0x00037, 0x070b9, 0x00000, 0x00038, 0x070b9, 0x00000, 0x00039, 0x070b9, 0x00000, 0x00031, 0x00030, 0x070b9, 0x00000, 0x00031, 0x00031, 0x070b9, 0x00000, 0x00031, 0x00032, 0x070b9, 0x00000, 0x00031, 0x00033, 0x070b9, 0x00000, 0x00031, 0x00034, 0x070b9, 0x00000, 0x00031, 0x00035, 0x070b9, 0x00000, 0x00031, 0x00036, 0x070b9, 0x00000, 0x00031, 0x00037, 0x070b9, 0x00000, 0x00031, 0x00038, 0x070b9, 0x00000, 0x00031, 0x00039, 0x070b9, 0x00000, 0x00032, 0x00030, 0x070b9, 0x00000, 0x00032, 0x00031, 0x070b9, 0x00000, 0x00032, 0x00032, 0x070b9, 0x00000, 0x00032, 0x00033, 0x070b9, 0x00000, 0x00032, 0x00034, 0x070b9, 0x00000, 0x00068, 0x00050, 0x00061, 0x00000, 0x00064, 0x00061, 0x00000, 0x00041, 0x00055, 0x00000, 0x00062, 0x00061, 0x00072, 0x00000, 0x0006f, 0x00056, 0x00000, 0x00070, 0x00063, 0x00000, 0x00064, 0x0006d, 0x00000, 0x00064, 0x0006d, 0x000b2, 0x00000, 0x00064, 0x0006d, 0x000b3, 0x00000, 0x00049, 0x00055, 0x00000, 0x05e73, 0x06210, 0x00000, 0x0662d, 0x0548c, 0x00000, 0x05927, 0x06b63, 0x00000, 0x0660e, 0x06cbb, 0x00000, 0x0682a, 0x05f0f, 0x04f1a, 0x0793e, 0x00000, 0x00070, 0x00041, 0x00000, 0x0006e, 0x00041, 0x00000, 0x003bc, 0x00041, 0x00000, 0x0006d, 0x00041, 0x00000, 0x0006b, 0x00041, 0x00000, 0x0004b, 0x00042, 0x00000, 0x0004d, 0x00042, 0x00000, 0x00047, 0x00042, 0x00000, 0x00063, 0x00061, 0x0006c, 0x00000, 0x0006b, 0x00063, 0x00061, 0x0006c, 0x00000, 0x00070, 0x00046, 0x00000, 0x0006e, 0x00046, 0x00000, 0x003bc, 0x00046, 0x00000, 0x003bc, 0x00067, 0x00000, 0x0006d, 0x00067, 0x00000, 0x0006b, 0x00067, 0x00000, 0x00048, 0x0007a, 0x00000, 0x0006b, 0x00048, 0x0007a, 0x00000, 0x0004d, 0x00048, 0x0007a, 0x00000, 0x00047, 0x00048, 0x0007a, 0x00000, 0x00054, 0x00048, 0x0007a, 0x00000, 0x003bc, 0x02113, 0x00000, 0x0006d, 0x02113, 0x00000, 0x00064, 0x02113, 0x00000, 0x0006b, 0x02113, 0x00000, 0x00066, 0x0006d, 0x00000, 0x0006e, 0x0006d, 0x00000, 0x003bc, 0x0006d, 0x00000, 0x0006d, 0x0006d, 0x00000, 0x00063, 0x0006d, 0x00000, 0x0006b, 0x0006d, 0x00000, 0x0006d, 0x0006d, 0x000b2, 0x00000, 0x00063, 0x0006d, 0x000b2, 0x00000, 0x0006d, 0x000b2, 0x00000, 0x0006b, 0x0006d, 0x000b2, 0x00000, 0x0006d, 0x0006d, 0x000b3, 0x00000, 0x00063, 0x0006d, 0x000b3, 0x00000, 0x0006d, 0x000b3, 0x00000, 0x0006b, 0x0006d, 0x000b3, 0x00000, 0x0006d, 0x02215, 0x00073, 0x00000, 0x0006d, 0x02215, 0x00073, 0x000b2, 0x00000, 0x00050, 0x00061, 0x00000, 0x0006b, 0x00050, 0x00061, 0x00000, 0x0004d, 0x00050, 0x00061, 0x00000, 0x00047, 0x00050, 0x00061, 0x00000, 0x00072, 0x00061, 0x00064, 0x00000, 0x00072, 0x00061, 0x00064, 0x02215, 0x00073, 0x00000, 0x00072, 0x00061, 0x00064, 0x02215, 0x00073, 0x000b2, 0x00000, 0x00070, 0x00073, 0x00000, 0x0006e, 0x00073, 0x00000, 0x003bc, 0x00073, 0x00000, 0x0006d, 0x00073, 0x00000, 0x00070, 0x00056, 0x00000, 0x0006e, 0x00056, 0x00000, 0x003bc, 0x00056, 0x00000, 0x0006d, 0x00056, 0x00000, 0x0006b, 0x00056, 0x00000, 0x0004d, 0x00056, 0x00000, 0x00070, 0x00057, 0x00000, 0x0006e, 0x00057, 0x00000, 0x003bc, 0x00057, 0x00000, 0x0006d, 0x00057, 0x00000, 0x0006b, 0x00057, 0x00000, 0x0004d, 0x00057, 0x00000, 0x0006b, 0x003a9, 0x00000, 0x0004d, 0x003a9, 0x00000, 0x00061, 0x0002e, 0x0006d, 0x0002e, 0x00000, 0x00042, 0x00071, 0x00000, 0x00063, 0x00063, 0x00000, 0x00063, 0x00064, 0x00000, 0x00043, 0x02215, 0x0006b, 0x00067, 0x00000, 0x00043, 0x0006f, 0x0002e, 0x00000, 0x00064, 0x00042, 0x00000, 0x00047, 0x00079, 0x00000, 0x00068, 0x00061, 0x00000, 0x00048, 0x00050, 0x00000, 0x00069, 0x0006e, 0x00000, 0x0004b, 0x0004b, 0x00000, 0x0004b, 0x0004d, 0x00000, 0x0006b, 0x00074, 0x00000, 0x0006c, 0x0006d, 0x00000, 0x0006c, 0x0006e, 0x00000, 0x0006c, 0x0006f, 0x00067, 0x00000, 0x0006c, 0x00078, 0x00000, 0x0006d, 0x00062, 0x00000, 0x0006d, 0x00069, 0x0006c, 0x00000, 0x0006d, 0x0006f, 0x0006c, 0x00000, 0x00050, 0x00048, 0x00000, 0x00070, 0x0002e, 0x0006d, 0x0002e, 0x00000, 0x00050, 0x00050, 0x0004d, 0x00000, 0x00050, 0x00052, 0x00000, 0x00073, 0x00072, 0x00000, 0x00053, 0x00076, 0x00000, 0x00057, 0x00062, 0x00000, 0x00056, 0x02215, 0x0006d, 0x00000, 0x00041, 0x02215, 0x0006d, 0x00000, 0x00031, 0x065e5, 0x00000, 0x00032, 0x065e5, 0x00000, 0x00033, 0x065e5, 0x00000, 0x00034, 0x065e5, 0x00000, 0x00035, 0x065e5, 0x00000, 0x00036, 0x065e5, 0x00000, 0x00037, 0x065e5, 0x00000, 0x00038, 0x065e5, 0x00000, 0x00039, 0x065e5, 0x00000, 0x00031, 0x00030, 0x065e5, 0x00000, 0x00031, 0x00031, 0x065e5, 0x00000, 0x00031, 0x00032, 0x065e5, 0x00000, 0x00031, 0x00033, 0x065e5, 0x00000, 0x00031, 0x00034, 0x065e5, 0x00000, 0x00031, 0x00035, 0x065e5, 0x00000, 0x00031, 0x00036, 0x065e5, 0x00000, 0x00031, 0x00037, 0x065e5, 0x00000, 0x00031, 0x00038, 0x065e5, 0x00000, 0x00031, 0x00039, 0x065e5, 0x00000, 0x00032, 0x00030, 0x065e5, 0x00000, 0x00032, 0x00031, 0x065e5, 0x00000, 0x00032, 0x00032, 0x065e5, 0x00000, 0x00032, 0x00033, 0x065e5, 0x00000, 0x00032, 0x00034, 0x065e5, 0x00000, 0x00032, 0x00035, 0x065e5, 0x00000, 0x00032, 0x00036, 0x065e5, 0x00000, 0x00032, 0x00037, 0x065e5, 0x00000, 0x00032, 0x00038, 0x065e5, 0x00000, 0x00032, 0x00039, 0x065e5, 0x00000, 0x00033, 0x00030, 0x065e5, 0x00000, 0x00033, 0x00031, 0x065e5, 0x00000, 0x00067, 0x00061, 0x0006c, 0x00000, 0x00066, 0x00066, 0x00000, 0x00066, 0x00069, 0x00000, 0x00066, 0x0006c, 0x00000, 0x00066, 0x00066, 0x00069, 0x00000, 0x00066, 0x00066, 0x0006c, 0x00000, 0x0017f, 0x00074, 0x00000, 0x00073, 0x00074, 0x00000, 0x00574, 0x00576, 0x00000, 0x00574, 0x00565, 0x00000, 0x00574, 0x0056b, 0x00000, 0x0057e, 0x00576, 0x00000, 0x00574, 0x0056d, 0x00000, 0x005d9, 0x005b4, 0x00000, 0x005f2, 0x005b7, 0x00000, 0x005e9, 0x005c1, 0x00000, 0x005e9, 0x005c2, 0x00000, 0x0fb49, 0x005c1, 0x00000, 0x0fb49, 0x005c2, 0x00000, 0x005d0, 0x005b7, 0x00000, 0x005d0, 0x005b8, 0x00000, 0x005d0, 0x005bc, 0x00000, 0x005d1, 0x005bc, 0x00000, 0x005d2, 0x005bc, 0x00000, 0x005d3, 0x005bc, 0x00000, 0x005d4, 0x005bc, 0x00000, 0x005d5, 0x005bc, 0x00000, 0x005d6, 0x005bc, 0x00000, 0x005d8, 0x005bc, 0x00000, 0x005d9, 0x005bc, 0x00000, 0x005da, 0x005bc, 0x00000, 0x005db, 0x005bc, 0x00000, 0x005dc, 0x005bc, 0x00000, 0x005de, 0x005bc, 0x00000, 0x005e0, 0x005bc, 0x00000, 0x005e1, 0x005bc, 0x00000, 0x005e3, 0x005bc, 0x00000, 0x005e4, 0x005bc, 0x00000, 0x005e6, 0x005bc, 0x00000, 0x005e7, 0x005bc, 0x00000, 0x005e8, 0x005bc, 0x00000, 0x005e9, 0x005bc, 0x00000, 0x005ea, 0x005bc, 0x00000, 0x005d5, 0x005b9, 0x00000, 0x005d1, 0x005bf, 0x00000, 0x005db, 0x005bf, 0x00000, 0x005e4, 0x005bf, 0x00000, 0x005d0, 0x005dc, 0x00000, 0x00626, 0x00627, 0x00000, 0x00626, 0x00627, 0x00000, 0x00626, 0x006d5, 0x00000, 0x00626, 0x006d5, 0x00000, 0x00626, 0x00648, 0x00000, 0x00626, 0x00648, 0x00000, 0x00626, 0x006c7, 0x00000, 0x00626, 0x006c7, 0x00000, 0x00626, 0x006c6, 0x00000, 0x00626, 0x006c6, 0x00000, 0x00626, 0x006c8, 0x00000, 0x00626, 0x006c8, 0x00000, 0x00626, 0x006d0, 0x00000, 0x00626, 0x006d0, 0x00000, 0x00626, 0x006d0, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x0062c, 0x00000, 0x00626, 0x0062d, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x0064a, 0x00000, 0x00628, 0x0062c, 0x00000, 0x00628, 0x0062d, 0x00000, 0x00628, 0x0062e, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00649, 0x00000, 0x00628, 0x0064a, 0x00000, 0x0062a, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x00000, 0x0062a, 0x0062e, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00649, 0x00000, 0x0062a, 0x0064a, 0x00000, 0x0062b, 0x0062c, 0x00000, 0x0062b, 0x00645, 0x00000, 0x0062b, 0x00649, 0x00000, 0x0062b, 0x0064a, 0x00000, 0x0062c, 0x0062d, 0x00000, 0x0062c, 0x00645, 0x00000, 0x0062d, 0x0062c, 0x00000, 0x0062d, 0x00645, 0x00000, 0x0062e, 0x0062c, 0x00000, 0x0062e, 0x0062d, 0x00000, 0x0062e, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, 0x00633, 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00633, 0x00645, 0x00000, 0x00635, 0x0062d, 0x00000, 0x00635, 0x00645, 0x00000, 0x00636, 0x0062c, 0x00000, 0x00636, 0x0062d, 0x00000, 0x00636, 0x0062e, 0x00000, 0x00636, 0x00645, 0x00000, 0x00637, 0x0062d, 0x00000, 0x00637, 0x00645, 0x00000, 0x00638, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00000, 0x00639, 0x00645, 0x00000, 0x0063a, 0x0062c, 0x00000, 0x0063a, 0x00645, 0x00000, 0x00641, 0x0062c, 0x00000, 0x00641, 0x0062d, 0x00000, 0x00641, 0x0062e, 0x00000, 0x00641, 0x00645, 0x00000, 0x00641, 0x00649, 0x00000, 0x00641, 0x0064a, 0x00000, 0x00642, 0x0062d, 0x00000, 0x00642, 0x00645, 0x00000, 0x00642, 0x00649, 0x00000, 0x00642, 0x0064a, 0x00000, 0x00643, 0x00627, 0x00000, 0x00643, 0x0062c, 0x00000, 0x00643, 0x0062d, 0x00000, 0x00643, 0x0062e, 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00643, 0x00649, 0x00000, 0x00643, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x00000, 0x00644, 0x0062d, 0x00000, 0x00644, 0x0062e, 0x00000, 0x00644, 0x00645, 0x00000, 0x00644, 0x00649, 0x00000, 0x00644, 0x0064a, 0x00000, 0x00645, 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00000, 0x00645, 0x0062e, 0x00000, 0x00645, 0x00645, 0x00000, 0x00645, 0x00649, 0x00000, 0x00645, 0x0064a, 0x00000, 0x00646, 0x0062c, 0x00000, 0x00646, 0x0062d, 0x00000, 0x00646, 0x0062e, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00649, 0x00000, 0x00646, 0x0064a, 0x00000, 0x00647, 0x0062c, 0x00000, 0x00647, 0x00645, 0x00000, 0x00647, 0x00649, 0x00000, 0x00647, 0x0064a, 0x00000, 0x0064a, 0x0062c, 0x00000, 0x0064a, 0x0062d, 0x00000, 0x0064a, 0x0062e, 0x00000, 0x0064a, 0x00645, 0x00000, 0x0064a, 0x00649, 0x00000, 0x0064a, 0x0064a, 0x00000, 0x00630, 0x00670, 0x00000, 0x00631, 0x00670, 0x00000, 0x00649, 0x00670, 0x00000, 0x00020, 0x0064c, 0x00651, 0x00000, 0x00020, 0x0064d, 0x00651, 0x00000, 0x00020, 0x0064e, 0x00651, 0x00000, 0x00020, 0x0064f, 0x00651, 0x00000, 0x00020, 0x00650, 0x00651, 0x00000, 0x00020, 0x00651, 0x00670, 0x00000, 0x00626, 0x00631, 0x00000, 0x00626, 0x00632, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, 0x00646, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x0064a, 0x00000, 0x00628, 0x00631, 0x00000, 0x00628, 0x00632, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00646, 0x00000, 0x00628, 0x00649, 0x00000, 0x00628, 0x0064a, 0x00000, 0x0062a, 0x00631, 0x00000, 0x0062a, 0x00632, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00646, 0x00000, 0x0062a, 0x00649, 0x00000, 0x0062a, 0x0064a, 0x00000, 0x0062b, 0x00631, 0x00000, 0x0062b, 0x00632, 0x00000, 0x0062b, 0x00645, 0x00000, 0x0062b, 0x00646, 0x00000, 0x0062b, 0x00649, 0x00000, 0x0062b, 0x0064a, 0x00000, 0x00641, 0x00649, 0x00000, 0x00641, 0x0064a, 0x00000, 0x00642, 0x00649, 0x00000, 0x00642, 0x0064a, 0x00000, 0x00643, 0x00627, 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00643, 0x00649, 0x00000, 0x00643, 0x0064a, 0x00000, 0x00644, 0x00645, 0x00000, 0x00644, 0x00649, 0x00000, 0x00644, 0x0064a, 0x00000, 0x00645, 0x00627, 0x00000, 0x00645, 0x00645, 0x00000, 0x00646, 0x00631, 0x00000, 0x00646, 0x00632, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00646, 0x00000, 0x00646, 0x00649, 0x00000, 0x00646, 0x0064a, 0x00000, 0x00649, 0x00670, 0x00000, 0x0064a, 0x00631, 0x00000, 0x0064a, 0x00632, 0x00000, 0x0064a, 0x00645, 0x00000, 0x0064a, 0x00646, 0x00000, 0x0064a, 0x00649, 0x00000, 0x0064a, 0x0064a, 0x00000, 0x00626, 0x0062c, 0x00000, 0x00626, 0x0062d, 0x00000, 0x00626, 0x0062e, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, 0x00647, 0x00000, 0x00628, 0x0062c, 0x00000, 0x00628, 0x0062d, 0x00000, 0x00628, 0x0062e, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00647, 0x00000, 0x0062a, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x00000, 0x0062a, 0x0062e, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00647, 0x00000, 0x0062b, 0x00645, 0x00000, 0x0062c, 0x0062d, 0x00000, 0x0062c, 0x00645, 0x00000, 0x0062d, 0x0062c, 0x00000, 0x0062d, 0x00645, 0x00000, 0x0062e, 0x0062c, 0x00000, 0x0062e, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, 0x00633, 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00633, 0x00645, 0x00000, 0x00635, 0x0062d, 0x00000, 0x00635, 0x0062e, 0x00000, 0x00635, 0x00645, 0x00000, 0x00636, 0x0062c, 0x00000, 0x00636, 0x0062d, 0x00000, 0x00636, 0x0062e, 0x00000, 0x00636, 0x00645, 0x00000, 0x00637, 0x0062d, 0x00000, 0x00638, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00000, 0x00639, 0x00645, 0x00000, 0x0063a, 0x0062c, 0x00000, 0x0063a, 0x00645, 0x00000, 0x00641, 0x0062c, 0x00000, 0x00641, 0x0062d, 0x00000, 0x00641, 0x0062e, 0x00000, 0x00641, 0x00645, 0x00000, 0x00642, 0x0062d, 0x00000, 0x00642, 0x00645, 0x00000, 0x00643, 0x0062c, 0x00000, 0x00643, 0x0062d, 0x00000, 0x00643, 0x0062e, 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00644, 0x0062c, 0x00000, 0x00644, 0x0062d, 0x00000, 0x00644, 0x0062e, 0x00000, 0x00644, 0x00645, 0x00000, 0x00644, 0x00647, 0x00000, 0x00645, 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00000, 0x00645, 0x0062e, 0x00000, 0x00645, 0x00645, 0x00000, 0x00646, 0x0062c, 0x00000, 0x00646, 0x0062d, 0x00000, 0x00646, 0x0062e, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00647, 0x00000, 0x00647, 0x0062c, 0x00000, 0x00647, 0x00645, 0x00000, 0x00647, 0x00670, 0x00000, 0x0064a, 0x0062c, 0x00000, 0x0064a, 0x0062d, 0x00000, 0x0064a, 0x0062e, 0x00000, 0x0064a, 0x00645, 0x00000, 0x0064a, 0x00647, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, 0x00647, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00647, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00647, 0x00000, 0x0062b, 0x00645, 0x00000, 0x0062b, 0x00647, 0x00000, 0x00633, 0x00645, 0x00000, 0x00633, 0x00647, 0x00000, 0x00634, 0x00645, 0x00000, 0x00634, 0x00647, 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00644, 0x00645, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00647, 0x00000, 0x0064a, 0x00645, 0x00000, 0x0064a, 0x00647, 0x00000, 0x00640, 0x0064e, 0x00651, 0x00000, 0x00640, 0x0064f, 0x00651, 0x00000, 0x00640, 0x00650, 0x00651, 0x00000, 0x00637, 0x00649, 0x00000, 0x00637, 0x0064a, 0x00000, 0x00639, 0x00649, 0x00000, 0x00639, 0x0064a, 0x00000, 0x0063a, 0x00649, 0x00000, 0x0063a, 0x0064a, 0x00000, 0x00633, 0x00649, 0x00000, 0x00633, 0x0064a, 0x00000, 0x00634, 0x00649, 0x00000, 0x00634, 0x0064a, 0x00000, 0x0062d, 0x00649, 0x00000, 0x0062d, 0x0064a, 0x00000, 0x0062c, 0x00649, 0x00000, 0x0062c, 0x0064a, 0x00000, 0x0062e, 0x00649, 0x00000, 0x0062e, 0x0064a, 0x00000, 0x00635, 0x00649, 0x00000, 0x00635, 0x0064a, 0x00000, 0x00636, 0x00649, 0x00000, 0x00636, 0x0064a, 0x00000, 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00000, 0x00634, 0x00631, 0x00000, 0x00633, 0x00631, 0x00000, 0x00635, 0x00631, 0x00000, 0x00636, 0x00631, 0x00000, 0x00637, 0x00649, 0x00000, 0x00637, 0x0064a, 0x00000, 0x00639, 0x00649, 0x00000, 0x00639, 0x0064a, 0x00000, 0x0063a, 0x00649, 0x00000, 0x0063a, 0x0064a, 0x00000, 0x00633, 0x00649, 0x00000, 0x00633, 0x0064a, 0x00000, 0x00634, 0x00649, 0x00000, 0x00634, 0x0064a, 0x00000, 0x0062d, 0x00649, 0x00000, 0x0062d, 0x0064a, 0x00000, 0x0062c, 0x00649, 0x00000, 0x0062c, 0x0064a, 0x00000, 0x0062e, 0x00649, 0x00000, 0x0062e, 0x0064a, 0x00000, 0x00635, 0x00649, 0x00000, 0x00635, 0x0064a, 0x00000, 0x00636, 0x00649, 0x00000, 0x00636, 0x0064a, 0x00000, 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00000, 0x00634, 0x00631, 0x00000, 0x00633, 0x00631, 0x00000, 0x00635, 0x00631, 0x00000, 0x00636, 0x00631, 0x00000, 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00000, 0x00633, 0x00647, 0x00000, 0x00634, 0x00647, 0x00000, 0x00637, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, 0x00633, 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00637, 0x00645, 0x00000, 0x00638, 0x00645, 0x00000, 0x00627, 0x0064b, 0x00000, 0x00627, 0x0064b, 0x00000, 0x0062a, 0x0062c, 0x00645, 0x00000, 0x0062a, 0x0062d, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x00645, 0x00000, 0x0062a, 0x0062e, 0x00645, 0x00000, 0x0062a, 0x00645, 0x0062c, 0x00000, 0x0062a, 0x00645, 0x0062d, 0x00000, 0x0062a, 0x00645, 0x0062e, 0x00000, 0x0062c, 0x00645, 0x0062d, 0x00000, 0x0062c, 0x00645, 0x0062d, 0x00000, 0x0062d, 0x00645, 0x0064a, 0x00000, 0x0062d, 0x00645, 0x00649, 0x00000, 0x00633, 0x0062d, 0x0062c, 0x00000, 0x00633, 0x0062c, 0x0062d, 0x00000, 0x00633, 0x0062c, 0x00649, 0x00000, 0x00633, 0x00645, 0x0062d, 0x00000, 0x00633, 0x00645, 0x0062d, 0x00000, 0x00633, 0x00645, 0x0062c, 0x00000, 0x00633, 0x00645, 0x00645, 0x00000, 0x00633, 0x00645, 0x00645, 0x00000, 0x00635, 0x0062d, 0x0062d, 0x00000, 0x00635, 0x0062d, 0x0062d, 0x00000, 0x00635, 0x00645, 0x00645, 0x00000, 0x00634, 0x0062d, 0x00645, 0x00000, 0x00634, 0x0062d, 0x00645, 0x00000, 0x00634, 0x0062c, 0x0064a, 0x00000, 0x00634, 0x00645, 0x0062e, 0x00000, 0x00634, 0x00645, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00645, 0x00000, 0x00634, 0x00645, 0x00645, 0x00000, 0x00636, 0x0062d, 0x00649, 0x00000, 0x00636, 0x0062e, 0x00645, 0x00000, 0x00636, 0x0062e, 0x00645, 0x00000, 0x00637, 0x00645, 0x0062d, 0x00000, 0x00637, 0x00645, 0x0062d, 0x00000, 0x00637, 0x00645, 0x00645, 0x00000, 0x00637, 0x00645, 0x0064a, 0x00000, 0x00639, 0x0062c, 0x00645, 0x00000, 0x00639, 0x00645, 0x00645, 0x00000, 0x00639, 0x00645, 0x00645, 0x00000, 0x00639, 0x00645, 0x00649, 0x00000, 0x0063a, 0x00645, 0x00645, 0x00000, 0x0063a, 0x00645, 0x0064a, 0x00000, 0x0063a, 0x00645, 0x00649, 0x00000, 0x00641, 0x0062e, 0x00645, 0x00000, 0x00641, 0x0062e, 0x00645, 0x00000, 0x00642, 0x00645, 0x0062d, 0x00000, 0x00642, 0x00645, 0x00645, 0x00000, 0x00644, 0x0062d, 0x00645, 0x00000, 0x00644, 0x0062d, 0x0064a, 0x00000, 0x00644, 0x0062d, 0x00649, 0x00000, 0x00644, 0x0062c, 0x0062c, 0x00000, 0x00644, 0x0062c, 0x0062c, 0x00000, 0x00644, 0x0062e, 0x00645, 0x00000, 0x00644, 0x0062e, 0x00645, 0x00000, 0x00644, 0x00645, 0x0062d, 0x00000, 0x00644, 0x00645, 0x0062d, 0x00000, 0x00645, 0x0062d, 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00645, 0x00000, 0x00645, 0x0062d, 0x0064a, 0x00000, 0x00645, 0x0062c, 0x0062d, 0x00000, 0x00645, 0x0062c, 0x00645, 0x00000, 0x00645, 0x0062e, 0x0062c, 0x00000, 0x00645, 0x0062e, 0x00645, 0x00000, 0x00645, 0x0062c, 0x0062e, 0x00000, 0x00647, 0x00645, 0x0062c, 0x00000, 0x00647, 0x00645, 0x00645, 0x00000, 0x00646, 0x0062d, 0x00645, 0x00000, 0x00646, 0x0062d, 0x00649, 0x00000, 0x00646, 0x0062c, 0x00645, 0x00000, 0x00646, 0x0062c, 0x00645, 0x00000, 0x00646, 0x0062c, 0x00649, 0x00000, 0x00646, 0x00645, 0x0064a, 0x00000, 0x00646, 0x00645, 0x00649, 0x00000, 0x0064a, 0x00645, 0x00645, 0x00000, 0x0064a, 0x00645, 0x00645, 0x00000, 0x00628, 0x0062e, 0x0064a, 0x00000, 0x0062a, 0x0062c, 0x0064a, 0x00000, 0x0062a, 0x0062c, 0x00649, 0x00000, 0x0062a, 0x0062e, 0x0064a, 0x00000, 0x0062a, 0x0062e, 0x00649, 0x00000, 0x0062a, 0x00645, 0x0064a, 0x00000, 0x0062a, 0x00645, 0x00649, 0x00000, 0x0062c, 0x00645, 0x0064a, 0x00000, 0x0062c, 0x0062d, 0x00649, 0x00000, 0x0062c, 0x00645, 0x00649, 0x00000, 0x00633, 0x0062e, 0x00649, 0x00000, 0x00635, 0x0062d, 0x0064a, 0x00000, 0x00634, 0x0062d, 0x0064a, 0x00000, 0x00636, 0x0062d, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x0064a, 0x00000, 0x00644, 0x00645, 0x0064a, 0x00000, 0x0064a, 0x0062d, 0x0064a, 0x00000, 0x0064a, 0x0062c, 0x0064a, 0x00000, 0x0064a, 0x00645, 0x0064a, 0x00000, 0x00645, 0x00645, 0x0064a, 0x00000, 0x00642, 0x00645, 0x0064a, 0x00000, 0x00646, 0x0062d, 0x0064a, 0x00000, 0x00642, 0x00645, 0x0062d, 0x00000, 0x00644, 0x0062d, 0x00645, 0x00000, 0x00639, 0x00645, 0x0064a, 0x00000, 0x00643, 0x00645, 0x0064a, 0x00000, 0x00646, 0x0062c, 0x0062d, 0x00000, 0x00645, 0x0062e, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x00645, 0x00000, 0x00643, 0x00645, 0x00645, 0x00000, 0x00644, 0x0062c, 0x00645, 0x00000, 0x00646, 0x0062c, 0x0062d, 0x00000, 0x0062c, 0x0062d, 0x0064a, 0x00000, 0x0062d, 0x0062c, 0x0064a, 0x00000, 0x00645, 0x0062c, 0x0064a, 0x00000, 0x00641, 0x00645, 0x0064a, 0x00000, 0x00628, 0x0062d, 0x0064a, 0x00000, 0x00643, 0x00645, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00645, 0x00000, 0x00635, 0x00645, 0x00645, 0x00000, 0x00633, 0x0062e, 0x0064a, 0x00000, 0x00646, 0x0062c, 0x0064a, 0x00000, 0x00635, 0x00644, 0x006d2, 0x00000, 0x00642, 0x00644, 0x006d2, 0x00000, 0x00627, 0x00644, 0x00644, 0x00647, 0x00000, 0x00627, 0x00643, 0x00628, 0x00631, 0x00000, 0x00645, 0x0062d, 0x00645, 0x0062f, 0x00000, 0x00635, 0x00644, 0x00639, 0x00645, 0x00000, 0x00631, 0x00633, 0x00648, 0x00644, 0x00000, 0x00639, 0x00644, 0x0064a, 0x00647, 0x00000, 0x00648, 0x00633, 0x00644, 0x00645, 0x00000, 0x00635, 0x00644, 0x00649, 0x00000, 0x00635, 0x00644, 0x00649, 0x00020, 0x00627, 0x00644, 0x00644, 0x00647, 0x00020, 0x00639, 0x00644, 0x0064a, 0x00647, 0x00020, 0x00648, 0x00633, 0x00644, 0x00645, 0x00000, 0x0062c, 0x00644, 0x00020, 0x0062c, 0x00644, 0x00627, 0x00644, 0x00647, 0x00000, 0x00631, 0x006cc, 0x00627, 0x00644, 0x00000, 0x00020, 0x0064b, 0x00000, 0x00640, 0x0064b, 0x00000, 0x00020, 0x0064c, 0x00000, 0x00020, 0x0064d, 0x00000, 0x00020, 0x0064e, 0x00000, 0x00640, 0x0064e, 0x00000, 0x00020, 0x0064f, 0x00000, 0x00640, 0x0064f, 0x00000, 0x00020, 0x00650, 0x00000, 0x00640, 0x00650, 0x00000, 0x00020, 0x00651, 0x00000, 0x00640, 0x00651, 0x00000, 0x00020, 0x00652, 0x00000, 0x00640, 0x00652, 0x00000, 0x00644, 0x00622, 0x00000, 0x00644, 0x00622, 0x00000, 0x00644, 0x00623, 0x00000, 0x00644, 0x00623, 0x00000, 0x00644, 0x00625, 0x00000, 0x00644, 0x00625, 0x00000, 0x00644, 0x00627, 0x00000, 0x00644, 0x00627, 0x00000, 0x11099, 0x110ba, 0x00000, 0x1109b, 0x110ba, 0x00000, 0x110a5, 0x110ba, 0x00000, 0x11131, 0x11127, 0x00000, 0x11132, 0x11127, 0x00000, 0x11347, 0x1133e, 0x00000, 0x11347, 0x11357, 0x00000, 0x114b9, 0x114ba, 0x00000, 0x114b9, 0x114b0, 0x00000, 0x114b9, 0x114bd, 0x00000, 0x115b8, 0x115af, 0x00000, 0x115b9, 0x115af, 0x00000, 0x1d157, 0x1d165, 0x00000, 0x1d158, 0x1d165, 0x00000, 0x1d15f, 0x1d16e, 0x00000, 0x1d15f, 0x1d16f, 0x00000, 0x1d15f, 0x1d170, 0x00000, 0x1d15f, 0x1d171, 0x00000, 0x1d15f, 0x1d172, 0x00000, 0x1d1b9, 0x1d165, 0x00000, 0x1d1ba, 0x1d165, 0x00000, 0x1d1bb, 0x1d16e, 0x00000, 0x1d1bc, 0x1d16e, 0x00000, 0x1d1bb, 0x1d16f, 0x00000, 0x1d1bc, 0x1d16f, 0x00000, 0x00030, 0x0002e, 0x00000, 0x00030, 0x0002c, 0x00000, 0x00031, 0x0002c, 0x00000, 0x00032, 0x0002c, 0x00000, 0x00033, 0x0002c, 0x00000, 0x00034, 0x0002c, 0x00000, 0x00035, 0x0002c, 0x00000, 0x00036, 0x0002c, 0x00000, 0x00037, 0x0002c, 0x00000, 0x00038, 0x0002c, 0x00000, 0x00039, 0x0002c, 0x00000, 0x00028, 0x00041, 0x00029, 0x00000, 0x00028, 0x00042, 0x00029, 0x00000, 0x00028, 0x00043, 0x00029, 0x00000, 0x00028, 0x00044, 0x00029, 0x00000, 0x00028, 0x00045, 0x00029, 0x00000, 0x00028, 0x00046, 0x00029, 0x00000, 0x00028, 0x00047, 0x00029, 0x00000, 0x00028, 0x00048, 0x00029, 0x00000, 0x00028, 0x00049, 0x00029, 0x00000, 0x00028, 0x0004a, 0x00029, 0x00000, 0x00028, 0x0004b, 0x00029, 0x00000, 0x00028, 0x0004c, 0x00029, 0x00000, 0x00028, 0x0004d, 0x00029, 0x00000, 0x00028, 0x0004e, 0x00029, 0x00000, 0x00028, 0x0004f, 0x00029, 0x00000, 0x00028, 0x00050, 0x00029, 0x00000, 0x00028, 0x00051, 0x00029, 0x00000, 0x00028, 0x00052, 0x00029, 0x00000, 0x00028, 0x00053, 0x00029, 0x00000, 0x00028, 0x00054, 0x00029, 0x00000, 0x00028, 0x00055, 0x00029, 0x00000, 0x00028, 0x00056, 0x00029, 0x00000, 0x00028, 0x00057, 0x00029, 0x00000, 0x00028, 0x00058, 0x00029, 0x00000, 0x00028, 0x00059, 0x00029, 0x00000, 0x00028, 0x0005a, 0x00029, 0x00000, 0x03014, 0x00053, 0x03015, 0x00000, 0x00043, 0x00044, 0x00000, 0x00057, 0x0005a, 0x00000, 0x00048, 0x00056, 0x00000, 0x0004d, 0x00056, 0x00000, 0x00053, 0x00044, 0x00000, 0x00053, 0x00053, 0x00000, 0x00050, 0x00050, 0x00056, 0x00000, 0x00057, 0x00043, 0x00000, 0x0004d, 0x00043, 0x00000, 0x0004d, 0x00044, 0x00000, 0x00044, 0x0004a, 0x00000, 0x0307b, 0x0304b, 0x00000, 0x030b3, 0x030b3, 0x00000, 0x03014, 0x0672c, 0x03015, 0x00000, 0x03014, 0x04e09, 0x03015, 0x00000, 0x03014, 0x04e8c, 0x03015, 0x00000, 0x03014, 0x05b89, 0x03015, 0x00000, 0x03014, 0x070b9, 0x03015, 0x00000, 0x03014, 0x06253, 0x03015, 0x00000, 0x03014, 0x076d7, 0x03015, 0x00000, 0x03014, 0x052dd, 0x03015, 0x00000, 0x03014, 0x06557, 0x03015, 0x00000 }; dovecot-2.2.33.2/src/lib/istream-timeout.h0000644000175000017500000000043013123174404015157 00000000000000#ifndef ISTREAM_TIMEOUT_H #define ISTREAM_TIMEOUT_H /* Return ETIMEDOUT error if read() doesn't return anything for timeout_msecs. If timeout_msecs=0, there is no timeout. */ struct istream * i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs); #endif dovecot-2.2.33.2/src/lib/module-dir.h0000644000175000017500000000522613165463624014113 00000000000000#ifndef MODULE_DIR_H #define MODULE_DIR_H struct module_dir_load_settings { /* If abi_version is non-NULL and the module contains a version symbol, fail the load if they're different. In both strings ignore anything after the first '(' character, so the version can be e.g.: 2.2.ABIv1(2.2.15) */ const char *abi_version; /* Binary name used for checking if plugin is tried to be loaded for wrong binary. */ const char *binary_name; /* Setting name used in plugin dependency error message */ const char *setting_name; /* If non-NULL, load only modules where filter_callback returns TRUE */ bool (*filter_callback)(const char *name, void *context); void *filter_context; /* Require all plugins to have _init() function */ unsigned int require_init_funcs:1; /* Enable debug logging */ unsigned int debug:1; /* If dlopen() fails for some modules, silently skip it. */ unsigned int ignore_dlopen_errors:1; /* Don't fail if some specified modules weren't found */ unsigned int ignore_missing:1; }; struct module { char *path, *name; void *handle; void (*init)(struct module *module); void (*deinit)(void); unsigned int initialized:1; struct module *next; }; /* Load modules in given directory. module_names is a space separated list of module names to load. */ struct module *module_dir_load(const char *dir, const char *module_names, const struct module_dir_load_settings *set) ATTR_NULL(2); /* Load modules that aren't already loaded. */ struct module * module_dir_load_missing(struct module *old_modules, const char *dir, const char *module_names, const struct module_dir_load_settings *set) ATTR_NULL(1, 3); /* Load modules that aren't already loaded. */ int module_dir_try_load_missing(struct module **modules, const char *dir, const char *module_names, const struct module_dir_load_settings *set, const char **error_r) ATTR_NULL(1, 3); /* Call init() in all modules */ void module_dir_init(struct module *modules); /* Call deinit() in all modules and mark them NULL so module_dir_unload() won't do it again. */ void module_dir_deinit(struct module *modules); /* Unload all modules */ void module_dir_unload(struct module **modules); /* Find a module by name. */ struct module *module_dir_find(struct module *modules, const char *name); void *module_get_symbol(struct module *module, const char *symbol); void *module_get_symbol_quiet(struct module *module, const char *symbol); /* Returns module's base name from the filename. */ const char *module_file_get_name(const char *fname); /* Returns module's name without "_plugin" suffix. */ const char *module_get_plugin_name(struct module *module); #endif dovecot-2.2.33.2/src/lib/fd-set-nonblock.c0000644000175000017500000000071013165463624015023 00000000000000/* Copyright (c) 1999-2016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fd-set-nonblock.h" #include void fd_set_nonblock(int fd, bool nonblock) { int flags; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) i_fatal("fcntl(%d, F_GETFL) failed: %m", fd); if (nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) i_fatal("fcntl(%d, F_SETFL) failed: %m", fd); } dovecot-2.2.33.2/src/lib/ostream-unix.h0000644000175000017500000000053213123174404014465 00000000000000#ifndef OSTREAM_UNIX_H #define OSTREAM_UNIX_H struct ostream *o_stream_create_unix(int fd, size_t max_buffer_size); /* Write fd to UNIX socket along with the next outgoing data block. Returns TRUE if fd is accepted, and FALSE if a previous fd still needs to be sent. */ bool o_stream_unix_write_fd(struct ostream *output, int fd); #endif dovecot-2.2.33.2/src/lib/process-title.c0000644000175000017500000001003413165463624014633 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "env-util.h" #include "process-title.h" #include /* FreeBSD */ static char *process_name = NULL; #ifdef HAVE_SETPROCTITLE # undef PROCTITLE_HACK #endif #ifdef PROCTITLE_HACK #define PROCTITLE_CLEAR_CHAR 0xab static char *process_title; static size_t process_title_len, process_title_clean_pos; static void *argv_memblock, *environ_memblock; static void proctitle_hack_init(char *argv[], char *env[]) { char *last; unsigned int i; bool clear_env; i_assert(argv[0] != NULL); /* find the last argv or environment string. it should always be the last string in environ, but don't rely on it. this is what openssh does, so hopefully it's safe enough. */ last = argv[0] + strlen(argv[0]) + 1; for (i = 1; argv[i] != NULL; i++) { if (argv[i] == last) last = argv[i] + strlen(argv[i]) + 1; } if (env[0] == NULL) clear_env = FALSE; else { clear_env = last == env[0]; for (i = 0; env[i] != NULL; i++) { if (env[i] == last) last = env[i] + strlen(env[i]) + 1; } } process_title = argv[0]; process_title_len = last - argv[0]; /* if there are problems with this approach, try to make sure we notice it */ if (clear_env) { memset(env[0], PROCTITLE_CLEAR_CHAR, last - env[0]); process_title_clean_pos = env[0] - process_title; } else { process_title_clean_pos = 0; } } static char **argv_dup(char *old_argv[], void **memblock_r) { /* @UNSAFE */ void *memblock, *memblock_end; char **new_argv; unsigned int i, count; size_t len, memblock_len = 0; for (count = 0; old_argv[count] != NULL; count++) memblock_len += strlen(old_argv[count]) + 1; memblock_len += sizeof(char *) * (count + 1); memblock = malloc(memblock_len); if (memblock == NULL) i_fatal_status(FATAL_OUTOFMEM, "malloc() failed: %m"); *memblock_r = memblock; memblock_end = PTR_OFFSET(memblock, memblock_len); new_argv = memblock; memblock = PTR_OFFSET(memblock, sizeof(char *) * (count + 1)); for (i = 0; i < count; i++) { new_argv[i] = memblock; len = strlen(old_argv[i]) + 1; memcpy(memblock, old_argv[i], len); memblock = PTR_OFFSET(memblock, len); } i_assert(memblock == memblock_end); new_argv[i] = NULL; return new_argv; } static void proctitle_hack_set(const char *title) { size_t len = strlen(title); /* OS X wants two NULs */ if (len >= process_title_len-1) len = process_title_len - 2; memcpy(process_title, title, len); process_title[len++] = '\0'; process_title[len++] = '\0'; if (len < process_title_clean_pos) { memset(process_title + len, PROCTITLE_CLEAR_CHAR, process_title_clean_pos - len); process_title_clean_pos = len; } else if (process_title_clean_pos != 0) { process_title_clean_pos = len; } } #endif void process_title_init(char **argv[]) { #ifdef PROCTITLE_HACK char ***environ_p = env_get_environ_p(); char **orig_argv = *argv; char **orig_environ = *environ_p; *argv = argv_dup(orig_argv, &argv_memblock); *environ_p = argv_dup(orig_environ, &environ_memblock); proctitle_hack_init(orig_argv, orig_environ); #endif process_name = (*argv)[0]; } void process_title_set(const char *title ATTR_UNUSED) { i_assert(process_name != NULL); #ifdef HAVE_SETPROCTITLE if (title == NULL) setproctitle(NULL); else setproctitle("%s", title); #elif defined(PROCTITLE_HACK) T_BEGIN { proctitle_hack_set(t_strconcat(process_name, " ", title, NULL)); } T_END; #endif } void process_title_deinit(void) { #ifdef PROCTITLE_HACK char ***environ_p = env_get_environ_p(); free(argv_memblock); free(environ_memblock); /* Environment is no longer usable. Make sure we won't crash in case some library's deinit function still calls getenv(). This code was mainly added because of GNUTLS where we don't really care about the getenv() call. Alternatively we could remove the free() calls above, but that would annoy memory leak checking tools. Also we could attempt to restore the environ_p to its original state, but that's a bit complicated. */ *environ_p = NULL; #endif } dovecot-2.2.33.2/src/lib/hash2.c0000644000175000017500000001405113123174404013033 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "primes.h" #include "hash2.h" #define HASH_TABLE_MIN_SIZE 131 struct hash2_value { struct hash2_value *next; unsigned int key_hash; /* user_data[value_size] */ }; ARRAY_DEFINE_TYPE(hash2_value, struct hash2_value *); struct hash2_table { unsigned int count; unsigned int initial_size; unsigned int hash_table_size; unsigned int value_size; pool_t value_pool; struct hash2_value *deleted_values; ARRAY_TYPE(hash2_value) hash_table; hash2_key_callback_t *key_hash_cb; hash2_cmp_callback_t *key_compare_cb; void *context; }; static void hash2_alloc_table(struct hash2_table *hash, unsigned int size) { hash->hash_table_size = size; i_array_init(&hash->hash_table, hash->hash_table_size); (void)array_idx_modifiable(&hash->hash_table, hash->hash_table_size-1); } struct hash2_table * hash2_create(unsigned int initial_size, unsigned int value_size, hash2_key_callback_t *key_hash_cb, hash2_cmp_callback_t *key_compare_cb, void *context) { struct hash2_table *hash; hash = i_new(struct hash2_table, 1); hash->initial_size = I_MAX(initial_size, HASH_TABLE_MIN_SIZE); hash->value_size = value_size; hash->key_hash_cb = key_hash_cb; hash->key_compare_cb = key_compare_cb; hash->context = context; hash->value_pool = pool_alloconly_create("hash2 value pool", 16384); hash2_alloc_table(hash, hash->initial_size); return hash; } void hash2_destroy(struct hash2_table **_hash) { struct hash2_table *hash = *_hash; *_hash = NULL; array_free(&hash->hash_table); pool_unref(&hash->value_pool); i_free(hash); } void hash2_clear(struct hash2_table *hash) { array_free(&hash->hash_table); hash2_alloc_table(hash, hash->initial_size); p_clear(hash->value_pool); hash->count = 0; hash->deleted_values = NULL; } static void hash2_resize(struct hash2_table *hash, bool grow) { ARRAY_TYPE(hash2_value) old_hash_table; struct hash2_value *const *old_hash, *value, **valuep, *next; unsigned int next_size, old_count, i, idx; float nodes_per_list; nodes_per_list = (float)hash->count / (float)hash->hash_table_size; if (nodes_per_list > 0.3 && nodes_per_list < 2.0) return; next_size = I_MAX(primes_closest(hash->count + 1), hash->initial_size); if (hash->hash_table_size >= next_size && (grow || next_size == hash->hash_table_size)) return; old_hash_table = hash->hash_table; hash2_alloc_table(hash, next_size); old_count = array_count(&old_hash_table); for (i = 0; i < old_count; i++) { old_hash = array_idx(&old_hash_table, i); for (value = *old_hash; value != NULL; value = next) { next = value->next; idx = value->key_hash % hash->hash_table_size; valuep = array_idx_modifiable(&hash->hash_table, idx); value->next = *valuep; *valuep = value; } } array_free(&old_hash_table); } void *hash2_lookup(const struct hash2_table *hash, const void *key) { unsigned int key_hash = hash->key_hash_cb(key); struct hash2_value *const *valuep; struct hash2_value *value; void *user_value; valuep = array_idx(&hash->hash_table, key_hash % hash->hash_table_size); value = *valuep; while (value != NULL) { if (value->key_hash == key_hash) { user_value = value + 1; if (hash->key_compare_cb(key, user_value, hash->context)) return user_value; } value = value->next; } return NULL; } void *hash2_iterate(const struct hash2_table *hash, unsigned int key_hash, struct hash2_iter *iter) { struct hash2_value *const *valuep; if (iter->value == NULL) { iter->key_hash = key_hash; valuep = array_idx(&hash->hash_table, key_hash % hash->hash_table_size); iter->next_value = *valuep; } while (iter->next_value != NULL) { if (iter->next_value->key_hash == key_hash) { iter->value = iter->next_value; iter->next_value = iter->next_value->next; return iter->value + 1; } iter->next_value = iter->next_value->next; } return NULL; } void *hash2_insert(struct hash2_table *hash, const void *key) { return hash2_insert_hash(hash, hash->key_hash_cb(key)); } void *hash2_insert_hash(struct hash2_table *hash, unsigned int key_hash) { struct hash2_value *value, **valuep; hash2_resize(hash, TRUE); if (hash->deleted_values != NULL) { value = hash->deleted_values; hash->deleted_values = value->next; value->next = NULL; memset(value + 1, 0, hash->value_size); } else { value = p_malloc(hash->value_pool, sizeof(*value) + hash->value_size); } value->key_hash = key_hash; valuep = array_idx_modifiable(&hash->hash_table, key_hash % hash->hash_table_size); value->next = *valuep; *valuep = value; hash->count++; return value + 1; } static void hash2_remove_value_p(struct hash2_table *hash, struct hash2_value **valuep) { struct hash2_value *deleted_value; deleted_value = *valuep; *valuep = deleted_value->next; deleted_value->next = hash->deleted_values; hash->deleted_values = deleted_value; hash->count--; } void hash2_remove(struct hash2_table *hash, const void *key) { unsigned int key_hash = hash->key_hash_cb(key); struct hash2_value **valuep; valuep = array_idx_modifiable(&hash->hash_table, key_hash % hash->hash_table_size); while (*valuep != NULL) { if ((*valuep)->key_hash == key_hash && hash->key_compare_cb(key, (*valuep) + 1, hash->context)) { hash2_remove_value_p(hash, valuep); hash2_resize(hash, FALSE); return; } valuep = &(*valuep)->next; } i_panic("hash2_remove(): key not found"); } void hash2_remove_iter(struct hash2_table *hash, struct hash2_iter *iter) { struct hash2_value **valuep, *next; valuep = array_idx_modifiable(&hash->hash_table, iter->key_hash % hash->hash_table_size); while (*valuep != NULL) { if (*valuep == iter->value) { next = (*valuep)->next; /* don't allow resizing, otherwise iterating would break completely */ hash2_remove_value_p(hash, valuep); iter->next_value = next; return; } valuep = &(*valuep)->next; } i_panic("hash2_remove_value(): key/value not found"); } unsigned int hash2_count(const struct hash2_table *hash) { return hash->count; } dovecot-2.2.33.2/src/lib/nfs-workarounds.c0000644000175000017500000002272213165463624015207 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ /* These tests were done with various Linux 2.6 kernels, FreeBSD 6.2 and Solaris 8 and 10. Attribute cache is usually flushed with chown()ing or fchown()ing the file. The safest way would be to use uid=-1 gid=-1, but this doesn't work with Linux (it does with FreeBSD 6.2 and Solaris). So we'll first get the file's owner and use it. As long as we're not root the file's owner can't change accidentally. If would be possible to also use chmod()/fchmod(), but that's riskier since it could actually cause an unwanted change. Write cache can be flushed with fdatasync(). It's all we need, but other tested alternatives are: fcntl locking (Linux 2.6, Solaris), fchown() (Solaris) and dup()+close() (Linux 2.6, Solaris). Read cache flushing is more problematic. There's no universal way to do it. The working methods are: Linux 2.6: fcntl(), O_DIRECT Solaris: fchown(), fcntl(), dup()+close() FreeBSD 6.2: fchown() fchown() can be easily used for Solaris and FreeBSD, but Linux requires playing with locks. O_DIRECT requires CONFIG_NFS_DIRECTIO to be enabled, so we can't always use it. */ #include "lib.h" #include "abspath.h" #include "nfs-workarounds.h" #include #include #include #if defined (__linux__) || defined(__sun) # define READ_CACHE_FLUSH_FCNTL #endif #if defined(__FreeBSD__) || defined(__sun) # define ATTRCACHE_FLUSH_CHOWN_UID_1 #endif static void nfs_flush_file_handle_cache_parent_dir(const char *path); static int nfs_safe_do(const char *path, int (*callback)(const char *path, void *context), void *context) { unsigned int i; int ret; for (i = 1;; i++) { ret = callback(path, context); if (ret == 0 || errno != ESTALE || i == NFS_ESTALE_RETRY_COUNT) break; /* ESTALE: Some operating systems may fail with this if they can't internally revalidate the NFS file handle. Flush the file handle and try again */ nfs_flush_file_handle_cache(path); } return ret; } struct nfs_safe_open_context { int flags; int fd; }; static int nfs_safe_open_callback(const char *path, void *context) { struct nfs_safe_open_context *ctx = context; ctx->fd = open(path, ctx->flags); return ctx->fd == -1 ? -1 : 0; } int nfs_safe_open(const char *path, int flags) { struct nfs_safe_open_context ctx; i_assert((flags & O_CREAT) == 0); ctx.flags = flags; if (nfs_safe_do(path, nfs_safe_open_callback, &ctx) < 0) return -1; return ctx.fd; } static int nfs_safe_stat_callback(const char *path, void *context) { struct stat *buf = context; return stat(path, buf); } int nfs_safe_stat(const char *path, struct stat *buf) { return nfs_safe_do(path, nfs_safe_stat_callback, buf); } static int nfs_safe_lstat_callback(const char *path, void *context) { struct stat *buf = context; return lstat(path, buf); } int nfs_safe_lstat(const char *path, struct stat *buf) { return nfs_safe_do(path, nfs_safe_lstat_callback, buf); } int nfs_safe_link(const char *oldpath, const char *newpath, bool links1) { struct stat st; nlink_t orig_link_count = 1; if (!links1) { if (stat(oldpath, &st) < 0) return -1; orig_link_count = st.st_nlink; } if (link(oldpath, newpath) == 0) { #ifndef __FreeBSD__ return 0; #endif /* FreeBSD at least up to v6.2 converts EEXIST errors to success. */ } else if (errno != EEXIST) return -1; /* We don't know if it succeeded or failed. stat() to make sure. */ if (stat(oldpath, &st) < 0) return -1; if (st.st_nlink == orig_link_count) { errno = EEXIST; return -1; } return 0; } static void nfs_flush_chown_uid(const char *path) { uid_t uid; #ifdef ATTRCACHE_FLUSH_CHOWN_UID_1 uid = (uid_t)-1; #else struct stat st; if (stat(path, &st) == 0) uid = st.st_uid; else { if (errno == ESTALE) { /* ESTALE causes the OS to flush the attr cache */ return; } if (likely(errno == ENOENT)) { nfs_flush_file_handle_cache_parent_dir(path); return; } i_error("nfs_flush_chown_uid: stat(%s) failed: %m", path); return; } #endif if (chown(path, uid, (gid_t)-1) < 0) { if (errno == ESTALE || errno == EPERM || errno == ENOENT) { /* attr cache is flushed */ return; } if (likely(errno == ENOENT)) { nfs_flush_file_handle_cache_parent_dir(path); return; } i_error("nfs_flush_chown_uid: chown(%s) failed: %m", path); } } #ifdef __FreeBSD__ static bool nfs_flush_fchown_uid(const char *path, int fd) { uid_t uid; #ifndef ATTRCACHE_FLUSH_CHOWN_UID_1 struct stat st; if (fstat(fd, &st) < 0) { if (likely(errno == ESTALE)) return FALSE; i_error("nfs_flush_attr_cache_fchown: fstat(%s) failed: %m", path); return TRUE; } uid = st.st_uid; #else uid = (uid_t)-1; #endif if (fchown(fd, uid, (gid_t)-1) < 0) { if (errno == ESTALE) return FALSE; if (likely(errno == EACCES || errno == EPERM)) { /* attr cache is flushed */ return TRUE; } i_error("nfs_flush_attr_cache_fd_locked: fchown(%s) failed: %m", path); } return TRUE; } #endif #ifdef READ_CACHE_FLUSH_FCNTL static bool nfs_flush_fcntl(const char *path, int fd) { static bool locks_disabled = FALSE; struct flock fl; int ret; if (locks_disabled) return FALSE; /* If the file was already locked, we'll just get the same lock again. It should succeed just fine. If was was unlocked, we'll have to get a lock and then unlock it. Linux 2.6 flushes read cache only when read/write locking succeeded. */ fl.l_type = F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; alarm(60); ret = fcntl(fd, F_SETLKW, &fl); alarm(0); if (unlikely(ret < 0)) { if (errno == ENOLCK) { locks_disabled = TRUE; return FALSE; } i_error("nfs_flush_fcntl: fcntl(%s, F_RDLCK) failed: %m", path); return FALSE; } fl.l_type = F_UNLCK; (void)fcntl(fd, F_SETLKW, &fl); return TRUE; } #endif void nfs_flush_attr_cache_unlocked(const char *path) { int fd; /* Try to flush the attribute cache the nice way first. */ fd = open(path, O_RDONLY); if (fd != -1) i_close_fd(&fd); else if (errno == ESTALE) { /* this already flushed the cache */ } else { /* most likely ENOENT, which means a negative cache hit. flush the file handles for its parent directory. */ nfs_flush_file_handle_cache_parent_dir(path); } } void nfs_flush_attr_cache_maybe_locked(const char *path) { nfs_flush_chown_uid(path); } void nfs_flush_attr_cache_fd_locked(const char *path ATTR_UNUSED, int fd ATTR_UNUSED) { #ifdef __FreeBSD__ /* FreeBSD doesn't flush attribute cache with fcntl(), so we have to do it ourself. */ (void)nfs_flush_fchown_uid(path, fd); #else /* Linux and Solaris are fine. */ #endif } static bool nfs_flush_file_handle_cache_dir(const char *path, bool try_parent ATTR_UNUSED) { #ifdef __linux__ /* chown()ing parent is the safest way to handle this */ nfs_flush_chown_uid(path); #else /* rmdir() is the only choice with FreeBSD and Solaris */ if (unlikely(rmdir(path) == 0)) { if (mkdir(path, 0700) == 0) { i_warning("nfs_flush_file_handle_cache_dir: " "rmdir(%s) unexpectedly " "removed the dir. recreated.", path); } else { i_warning("nfs_flush_file_handle_cache_dir: " "rmdir(%s) unexpectedly " "removed the dir. mkdir() failed: %m", path); } } else if (errno == ESTALE || errno == ENOTDIR || errno == ENOTEMPTY || errno == EEXIST || errno == EACCES) { /* expected failures */ } else if (errno == ENOENT) { return FALSE; } else if (errno == EINVAL && try_parent) { /* Solaris gives this if we're trying to rmdir() the current directory. Work around this by temporarily changing the current directory to the parent directory. */ const char *cur_path, *p; int cur_dir_fd; bool ret; cur_dir_fd = open(".", O_RDONLY); if (cur_dir_fd == -1) { i_error("open(.) failed for: %m"); return TRUE; } if (t_get_current_dir(&cur_path) < 0) { i_error("nfs_flush_file_handle_cache_dir: " "getcwd() failed"); i_close_fd(&cur_dir_fd); return TRUE; } p = strrchr(cur_path, '/'); if (p == NULL) cur_path = "/"; else cur_path = t_strdup_until(cur_path, p); if (chdir(cur_path) < 0) { i_error("nfs_flush_file_handle_cache_dir: " "chdir() failed"); } ret = nfs_flush_file_handle_cache_dir(path, FALSE); if (fchdir(cur_dir_fd) < 0) i_error("fchdir() failed: %m"); i_close_fd(&cur_dir_fd); return ret; } else { i_error("nfs_flush_file_handle_cache_dir: " "rmdir(%s) failed: %m", path); } #endif return TRUE; } static void nfs_flush_file_handle_cache_parent_dir(const char *path) { const char *p; p = strrchr(path, '/'); T_BEGIN { if (p == NULL) (void)nfs_flush_file_handle_cache_dir(".", TRUE); else (void)nfs_flush_file_handle_cache_dir(t_strdup_until(path, p), TRUE); } T_END; } void nfs_flush_file_handle_cache(const char *path) { nfs_flush_file_handle_cache_parent_dir(path); } void nfs_flush_read_cache_locked(const char *path ATTR_UNUSED, int fd ATTR_UNUSED) { #ifdef READ_CACHE_FLUSH_FCNTL /* already flushed when fcntl() was called */ #else /* we can only hope that underlying filesystem uses micro/nanosecond resolution so that attribute cache flushing notices mtime changes */ nfs_flush_attr_cache_fd_locked(path, fd); #endif } void nfs_flush_read_cache_unlocked(const char *path, int fd) { #ifdef READ_CACHE_FLUSH_FCNTL if (!nfs_flush_fcntl(path, fd)) nfs_flush_attr_cache_fd_locked(path, fd); #else nfs_flush_read_cache_locked(path, fd); #endif } dovecot-2.2.33.2/src/lib/file-lock.h0000644000175000017500000000643313165463543013720 00000000000000#ifndef FILE_LOCK_H #define FILE_LOCK_H #include #include #define DEFAULT_LOCK_TIMEOUT 120 struct file_lock; enum file_lock_method { FILE_LOCK_METHOD_FCNTL, FILE_LOCK_METHOD_FLOCK, FILE_LOCK_METHOD_DOTLOCK }; /* Parse lock method from given string. Returns TRUE if ok, FALSE if name is unknown. */ bool file_lock_method_parse(const char *name, enum file_lock_method *method_r); /* Convert lock method to string. */ const char *file_lock_method_to_str(enum file_lock_method method); /* Lock the file. Returns 1 if successful, 0 if file is already locked, or -1 if error. lock_type is F_WRLCK or F_RDLCK. */ int file_try_lock(int fd, const char *path, int lock_type, enum file_lock_method lock_method, struct file_lock **lock_r); /* Like file_try_lock(), but return the error message as a string instead of logging it. Also when returning 0 an error message is returned. */ int file_try_lock_error(int fd, const char *path, int lock_type, enum file_lock_method lock_method, struct file_lock **lock_r, const char **error_r); /* Like lock_try_lock(), but return 0 only after having tried to lock for timeout_secs. */ int file_wait_lock(int fd, const char *path, int lock_type, enum file_lock_method lock_method, unsigned int timeout_secs, struct file_lock **lock_r); /* Like file_wait_lock(), but return the error message as a string instead of logging it. Also when returning 0 an error message is returned. */ int file_wait_lock_error(int fd, const char *path, int lock_type, enum file_lock_method lock_method, unsigned int timeout_secs, struct file_lock **lock_r, const char **error_r); /* Change the lock type. WARNING: This isn't an atomic operation! The result is the same as file_unlock() + file_try_lock(). */ int file_lock_try_update(struct file_lock *lock, int lock_type); /* When the lock is freed, unlink() the file automatically, unless other processes are already waiting on the lock. This can be useful for files that are only created to exist as lock files. */ void file_lock_set_unlink_on_free(struct file_lock *lock, bool set); /* When the lock is freed, close the fd automatically. This can be useful for files that are only created to exist as lock files. */ void file_lock_set_close_on_free(struct file_lock *lock, bool set); /* Unlock and free the lock. */ void file_unlock(struct file_lock **lock); /* Free the lock without unlocking it (because you're closing the fd anyway). */ void file_lock_free(struct file_lock **lock); /* Returns the path given as parameter to file_*lock*(). */ const char *file_lock_get_path(struct file_lock *lock); /* Update lock file's path (after it gets renamed by the caller). This is useful mainly together with file_lock_set_unlink_on_free(). */ void file_lock_set_path(struct file_lock *lock, const char *path); /* Returns human-readable string containing the process that has the file currently locked. Returns "" if unknown, otherwise " (string)". */ const char *file_lock_find(int lock_fd, enum file_lock_method lock_method, int lock_type); /* Track the duration of a lock wait. */ void file_lock_wait_start(void); void file_lock_wait_end(const char *lock_name); /* Return how many microseconds has been spent on lock waiting. */ uint64_t file_lock_wait_get_total_usecs(void); #endif dovecot-2.2.33.2/src/lib/bsearch-insert-pos.h0000644000175000017500000000425213123174404015545 00000000000000#ifndef BSEARCH_INSERT_POS_H #define BSEARCH_INSERT_POS_H /* Binary search template - getdata must be the name of a pure function or a function-like macro that takes the two obvious parameters. */ #define BINARY_NUMERIC_SEARCH(getdata, data, count, value, idx_r) \ unsigned int idx, left_idx, right_idx; \ \ i_assert((count) < INT_MAX); \ left_idx = 0; right_idx = (count); \ while (left_idx < right_idx) { \ idx = (left_idx + right_idx) / 2; \ \ if (getdata(data, idx) < (value)) \ left_idx = idx+1; \ else if (getdata(data, idx) > (value))\ right_idx = idx; \ else { \ *(idx_r) = idx; \ return TRUE; \ } \ } \ return FALSE #define BINARY_SEARCH_ARRAY_GET(array, index) ((array)[(index)]) #define BINARY_NUMBER_SEARCH(data, count, value, idx_r) \ BINARY_NUMERIC_SEARCH(BINARY_SEARCH_ARRAY_GET, data, count, value, idx_r); /* If key is found, returns TRUE and sets idx_r to the position where the key was found. If key isn't found, returns FALSE and sets idx_r to the position where the key should be inserted. */ bool ATTR_NOWARN_UNUSED_RESULT bsearch_insert_pos(const void *key, const void *base, unsigned int nmemb, size_t size, int (*cmp)(const void *, const void *), unsigned int *idx_r); #define bsearch_insert_pos(key, base, nmemb, size, cmp, idx_r) \ bsearch_insert_pos(key, base, nmemb, size + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ typeof(const typeof(*base) *))), \ (int (*)(const void *, const void *))cmp, idx_r) bool ATTR_NOWARN_UNUSED_RESULT array_bsearch_insert_pos_i(const struct array *array, const void *key, int (*cmp)(const void *, const void *), unsigned int *idx_r); #define array_bsearch_insert_pos(array, key, cmp, idx_r) \ array_bsearch_insert_pos_i(&(array)->arr + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ typeof(*(array)->v))), \ (const void *)key, (int (*)(const void *, const void *))cmp, idx_r) #endif dovecot-2.2.33.2/src/lib/hex-dec.h0000644000175000017500000000064313123174404013352 00000000000000#ifndef HEX_DEC_H #define HEX_DEC_H #define DEC2HEX(hexstr, str) \ dec2hex(hexstr, str, sizeof(hexstr)) /* Decimal -> hex string translation. The result isn't NUL-terminated. */ void dec2hex(unsigned char *hexstr, uintmax_t dec, unsigned int hexstr_size); /* Parses hex string and returns its decimal value, or 0 in case of errors */ uintmax_t hex2dec(const unsigned char *data, unsigned int len) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/test-istream-seekable.c0000644000175000017500000001264313165463624016240 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "istream-private.h" #include "istream-seekable.h" #include #include static int fd_callback(const char **path_r, void *context ATTR_UNUSED) { int fd; *path_r = "test-lib.tmp"; fd = open(*path_r, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_error("creat(%s) failed: %m", *path_r); else i_unlink(*path_r); return fd; } static void test_istream_seekable_one(unsigned int buffer_size) { static const char *input_string = "xyz"; #define STREAM_COUNT 5 #define STREAM_BYTES 3 struct istream *streams[STREAM_COUNT+1]; struct istream *input; const unsigned char *data; size_t size; unsigned int i, j; for (i = 0; i < STREAM_COUNT; i++) { streams[i] = test_istream_create(input_string); streams[i]->seekable = FALSE; test_istream_set_allow_eof(streams[i], TRUE); test_istream_set_size(streams[i], 0); } streams[i] = NULL; input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); for (i = 0; i/STREAM_BYTES < STREAM_COUNT; i++) { test_istream_set_size(streams[i/STREAM_BYTES], (i%STREAM_BYTES) + 1); if (i < buffer_size) { test_assert(i_stream_read(input) == 1); data = i_stream_get_data(input, &size); test_assert(size == i+1); } else { test_assert(i_stream_read(input) == -2); i_stream_skip(input, 1); test_assert(i_stream_read(input) == 1); data = i_stream_get_data(input, &size); test_assert(size == buffer_size); } for (j = 0; j < size; j++) { test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]); } } test_assert(i_stream_read(input) == -1); for (i = 0; i < STREAM_COUNT; i++) { test_assert(i_stream_is_eof(streams[i])); i_stream_unref(&streams[i]); } i_stream_unref(&input); } static void test_istream_seekable_random(void) { struct istream **streams, *input; const unsigned char *data; unsigned char *w_data; size_t size; unsigned int i, j, offset, stream_count, data_len, buffer_size; stream_count = (rand() % 10) + 2; streams = t_new(struct istream *, stream_count + 1); for (i = 0, offset = 0; i < stream_count; i++) { data_len = rand() % 100 + 1; w_data = t_malloc(data_len); for (j = 0; j < data_len; j++) w_data[j] = offset++; streams[i] = test_istream_create_data(w_data, data_len); streams[i]->seekable = FALSE; test_istream_set_allow_eof(streams[i], TRUE); } streams[i] = NULL; i_assert(offset > 0); buffer_size = (rand() % 100) + 1; size = 0; input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); /* first read it through */ while (i_stream_read(input) > 0) { size = i_stream_get_data_size(input); i_stream_skip(input, size); } i_stream_seek(input, 0); for (i = 0; i < 100; i++) { if (rand() % 3 == 0) { i_stream_seek(input, rand() % offset); } else { ssize_t ret = i_stream_read(input); if (input->v_offset + size == offset) test_assert(ret < 0); else if (ret == -2) { test_assert(size == buffer_size); } else { test_assert(ret > 0); test_assert(input->v_offset + ret <= offset); i_stream_skip(input, rand() % (ret+1)); data = i_stream_get_data(input, &size); for (j = 0; j < size; j++) { test_assert(data[j] == (input->v_offset + j) % 256); } } } size = i_stream_get_data_size(input); } for (i = 0; i < stream_count; i++) { test_assert(i_stream_is_eof(streams[i])); i_stream_unref(&streams[i]); } i_stream_unref(&input); } static void test_istream_seekable_eof(void) { static const char *in_str = "foo"; unsigned int in_str_len = strlen(in_str); struct istream *streams[2], *input; const unsigned char *data; size_t size; test_begin("istream seekable eof"); streams[0] = i_stream_create_from_data(in_str, in_str_len); streams[0]->seekable = FALSE; streams[1] = NULL; input = i_stream_create_seekable(streams, in_str_len, fd_callback, NULL); test_assert(i_stream_read(input) == (ssize_t)in_str_len); data = i_stream_get_data(input, &size); test_assert(size == in_str_len); test_assert(memcmp(data, in_str, in_str_len) == 0); test_assert(i_stream_read(input) == -1); data = i_stream_get_data(input, &size); test_assert(size == in_str_len); test_assert(memcmp(data, in_str, in_str_len) == 0); i_stream_seek(input, size); i_stream_unref(&input); test_assert(streams[0]->v_offset == in_str_len); test_assert(streams[0]->eof); i_stream_unref(&streams[0]); test_end(); } static void test_istream_seekable_early_end(void) { struct istream *input, *streams[2]; test_begin("istream seekable early end"); streams[0] = test_istream_create("stream"); test_istream_set_size(streams[0], 3); test_istream_set_allow_eof(streams[0], FALSE); streams[0]->seekable = FALSE; streams[1] = NULL; input = i_stream_create_seekable(streams, 1000, fd_callback, NULL); test_assert(i_stream_read(input) == 3); test_istream_set_size(streams[0], 5); test_assert(i_stream_read(input) == 2); i_stream_skip(input, 5); i_stream_unref(&input); test_assert(streams[0]->v_offset == 5); i_stream_unref(&streams[0]); test_end(); } void test_istream_seekable(void) { unsigned int i; test_begin("istream seekable"); for (i = 1; i <= STREAM_BYTES*STREAM_COUNT; i++) test_istream_seekable_one(i); test_end(); test_begin("istream seekable random"); for (i = 0; i < 100; i++) T_BEGIN { test_istream_seekable_random(); } T_END; test_end(); test_istream_seekable_eof(); test_istream_seekable_early_end(); } dovecot-2.2.33.2/src/lib/fd-set-nonblock.h0000644000175000017500000000024213123174404015015 00000000000000#ifndef FD_SET_NONBLOCK_H #define FD_SET_NONBLOCK_H /* Set file descriptor to blocking/nonblocking state */ void fd_set_nonblock(int fd, bool nonblock); #endif dovecot-2.2.33.2/src/lib/child-wait.h0000644000175000017500000000164013165463624014073 00000000000000#ifndef CHILD_WAIT_H #define CHILD_WAIT_H struct child_wait_status { struct child_wait *wait; pid_t pid; int status; }; typedef void child_wait_callback_t(const struct child_wait_status *status, void *context); struct child_wait * child_wait_new_with_pid(pid_t pid, child_wait_callback_t *callback, void *context) ATTR_NULL(3); #define child_wait_new_with_pid(pid, callback, context) \ child_wait_new_with_pid(pid + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct child_wait_status *status, typeof(context))), \ (child_wait_callback_t *)callback, context) #define child_wait_new(callback, context) \ child_wait_new_with_pid((pid_t)-1, callback, context) void child_wait_free(struct child_wait **wait); void child_wait_add_pid(struct child_wait *wait, pid_t pid); void child_wait_remove_pid(struct child_wait *wait, pid_t pid); void child_wait_init(void); void child_wait_deinit(void); #endif dovecot-2.2.33.2/src/lib/istream-sized.c0000644000175000017500000001461213165463624014624 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-sized.h" struct sized_istream { struct istream_private istream; istream_sized_callback_t *error_callback; void *error_context; uoff_t size; bool min_size_only; }; static void i_stream_sized_destroy(struct iostream_private *stream) { struct sized_istream *sstream = (struct sized_istream *)stream; uoff_t v_offset; v_offset = sstream->istream.parent_start_offset + sstream->istream.istream.v_offset; if (sstream->istream.parent->seekable || v_offset > sstream->istream.parent->v_offset) { /* get to same position in parent stream */ i_stream_seek(sstream->istream.parent, v_offset); } i_stream_unref(&sstream->istream.parent); } static const char * i_stream_create_sized_default_error_callback( const struct istream_sized_error_data *data, void *context ATTR_UNUSED) { if (data->v_offset + data->new_bytes < data->wanted_size) { return t_strdup_printf("Stream is smaller than expected " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", data->v_offset + data->new_bytes, data->wanted_size); } else { return t_strdup_printf("Stream is larger than expected " "(%"PRIuUOFF_T" > %"PRIuUOFF_T", eof=%d)", data->v_offset + data->new_bytes, data->wanted_size, data->eof); } } static ssize_t i_stream_sized_parent_read(struct istream_private *stream, size_t *pos_r) { ssize_t ret; do { if ((ret = i_stream_read(stream->parent)) == -2) break; stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; stream->buffer = i_stream_get_data(stream->parent, pos_r); } while (*pos_r <= stream->pos && ret > 0); return ret; } static ssize_t i_stream_sized_read(struct istream_private *stream) { struct sized_istream *sstream = (struct sized_istream *)stream; struct istream_sized_error_data data; const char *error; uoff_t left; ssize_t ret; size_t pos; i_stream_seek(stream->parent, sstream->istream.parent_start_offset + stream->istream.v_offset); stream->pos -= stream->skip; stream->skip = 0; stream->buffer = i_stream_get_data(stream->parent, &pos); if (pos > stream->pos) ret = 0; else { if ((ret = i_stream_sized_parent_read(stream, &pos)) == -2) return -2; } left = sstream->size - stream->istream.v_offset; if (pos == left && ret != -1) { /* we have exactly the wanted amount of data left, but we don't know yet if there is more data in parent. */ ret = i_stream_sized_parent_read(stream, &pos); } i_zero(&data); data.v_offset = stream->istream.v_offset; data.new_bytes = pos; data.wanted_size = sstream->size; data.eof = stream->istream.eof; if (pos == left) { /* we may or may not be finished, depending on whether parent is at EOF. */ } else if (pos > left) { /* parent has more data available than expected */ if (!sstream->min_size_only) { error = sstream->error_callback(&data, sstream->error_context); io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EINVAL; return -1; } pos = left; if (pos <= stream->pos) { stream->istream.eof = TRUE; ret = -1; } } else if (!stream->istream.eof) { /* still more to read */ } else if (stream->istream.stream_errno == ENOENT) { /* lost the file */ } else { /* EOF before we reached the wanted size */ error = sstream->error_callback(&data, sstream->error_context); io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EPIPE; } ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); return ret; } static int i_stream_sized_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct sized_istream *sstream = (struct sized_istream *)stream; const struct stat *st; /* parent stream may be base64-decoder. don't waste time decoding the entire stream, since we already know what the size is supposed to be. */ if (i_stream_stat(stream->parent, FALSE, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; stream->statbuf.st_size = sstream->size; return 0; } static struct sized_istream * i_stream_create_sized_common(struct istream *input, uoff_t size) { struct sized_istream *sstream; sstream = i_new(struct sized_istream, 1); sstream->size = size; sstream->istream.max_buffer_size = input->real_stream->max_buffer_size; sstream->istream.iostream.destroy = i_stream_sized_destroy; sstream->istream.read = i_stream_sized_read; sstream->istream.stat = i_stream_sized_stat; sstream->istream.istream.readable_fd = input->readable_fd; sstream->istream.istream.blocking = input->blocking; sstream->istream.istream.seekable = input->seekable; (void)i_stream_create(&sstream->istream, input, i_stream_get_fd(input)); return sstream; } struct istream *i_stream_create_sized(struct istream *input, uoff_t size) { struct sized_istream *sstream; sstream = i_stream_create_sized_common(input, size); sstream->error_callback = i_stream_create_sized_default_error_callback; sstream->error_context = sstream; return &sstream->istream.istream; } struct istream *i_stream_create_sized_range(struct istream *input, uoff_t offset, uoff_t size) { uoff_t orig_offset = input->v_offset; struct istream *ret; input->v_offset = offset; ret = i_stream_create_sized(input, size); input->v_offset = orig_offset; return ret; } struct istream *i_stream_create_min_sized(struct istream *input, uoff_t min_size) { struct istream *ret; ret= i_stream_create_sized(input, min_size); ((struct sized_istream *)ret->real_stream)->min_size_only = TRUE; return ret; } struct istream *i_stream_create_min_sized_range(struct istream *input, uoff_t offset, uoff_t min_size) { struct istream *ret; ret = i_stream_create_sized_range(input, offset, min_size); ((struct sized_istream *)ret->real_stream)->min_size_only = TRUE; return ret; } #undef i_stream_create_sized_with_callback struct istream * i_stream_create_sized_with_callback(struct istream *input, uoff_t size, istream_sized_callback_t *error_callback, void *context) { struct sized_istream *sstream; sstream = i_stream_create_sized_common(input, size); sstream->error_callback = error_callback; sstream->error_context = context; return &sstream->istream.istream; } dovecot-2.2.33.2/src/lib/test-log-throttle.c0000644000175000017500000000273013123174404015430 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "ioloop.h" #include "log-throttle.h" static unsigned int test_log_throttle_new_events_count; static void test_log_throttle_callback(unsigned int new_events_count, struct ioloop *ioloop) { test_log_throttle_new_events_count = new_events_count; io_loop_stop(ioloop); } void test_log_throttle(void) { const struct log_throttle_settings set = { .throttle_at_max_per_interval = 10, .unthrottle_at_max_per_interval = 5, .interval_msecs = 10, }; struct log_throttle *throttle; struct ioloop *ioloop; unsigned int i; test_begin("log throttle"); ioloop = io_loop_create(); throttle = log_throttle_init(&set, test_log_throttle_callback, ioloop); /* throttle once and drop out just below */ for (i = 0; i < 10; i++) test_assert_idx(log_throttle_accept(throttle), i); for (i = 0; i < 4; i++) test_assert_idx(!log_throttle_accept(throttle), i); io_loop_run(ioloop); test_assert(test_log_throttle_new_events_count == 4); /* throttle and continue just above */ for (i = 0; i < 10; i++) test_assert_idx(log_throttle_accept(throttle), i); for (i = 0; i < 5; i++) test_assert_idx(!log_throttle_accept(throttle), i); io_loop_run(ioloop); test_assert(test_log_throttle_new_events_count == 5); /* we should be still throttled */ test_assert(!log_throttle_accept(throttle)); log_throttle_deinit(&throttle); io_loop_destroy(&ioloop); test_end(); } dovecot-2.2.33.2/src/lib/randgen.c0000644000175000017500000000421513165463624013460 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "randgen.h" #ifdef DEV_URANDOM_PATH #include "fd-close-on-exec.h" #include #include static int init_refcount = 0; static int urandom_fd; void random_fill(void *buf, size_t size) { size_t pos; ssize_t ret; i_assert(init_refcount > 0); i_assert(size < SSIZE_T_MAX); for (pos = 0; pos < size; ) { ret = read(urandom_fd, (char *) buf + pos, size - pos); if (unlikely(ret <= 0)) { if (ret == 0) i_fatal("EOF when reading from "DEV_URANDOM_PATH); else if (errno != EINTR) i_fatal("read("DEV_URANDOM_PATH") failed: %m"); } else { pos += ret; } } } void random_init(void) { unsigned int seed; if (init_refcount++ > 0) return; urandom_fd = open(DEV_URANDOM_PATH, O_RDONLY); if (urandom_fd == -1) { if (errno == ENOENT) { i_fatal(DEV_URANDOM_PATH" doesn't exist, " "currently we require it"); } else { i_fatal("Can't open "DEV_URANDOM_PATH": %m"); } } random_fill(&seed, sizeof(seed)); rand_set_seed(seed); fd_close_on_exec(urandom_fd, TRUE); } void random_deinit(void) { if (--init_refcount > 0) return; i_close_fd(&urandom_fd); } #elif defined(HAVE_OPENSSL_RAND_H) #include #include static const char *ssl_last_error(void) { unsigned long err; char *buf; size_t err_size = 256; err = ERR_get_error(); if (err == 0) return strerror(errno); buf = t_malloc(err_size); buf[err_size-1] = '\0'; ERR_error_string_n(err, buf, err_size-1); return buf; } void random_fill(void *buf, size_t size) { if (RAND_bytes(buf, size) != 1) i_fatal("RAND_pseudo_bytes() failed: %s", ssl_last_error()); } void random_init(void) { unsigned int seed; if (RAND_status() == 0) { i_fatal("Random generator not initialized: " "Install egd on /var/run/egd-pool"); } random_fill(&seed, sizeof(seed)); rand_set_seed(seed); } void random_deinit(void) {} #else # error No random number generator, use eg. OpenSSL. #endif void random_fill_weak(void *buf, size_t size) { unsigned char *cbuf = buf; for (; size > 0; size--) *cbuf++ = (unsigned char)rand(); } dovecot-2.2.33.2/src/lib/istream-hash.c0000644000175000017500000000463513123174404014422 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash-method.h" #include "istream-private.h" #include "istream-hash.h" struct hash_istream { struct istream_private istream; const struct hash_method *method; void *hash_context; uoff_t high_offset; }; static ssize_t i_stream_hash_read(struct istream_private *stream) { struct hash_istream *hstream = (struct hash_istream *)stream; const unsigned char *data; size_t size; uoff_t skip; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); if (ret > 0 && hstream->hash_context != NULL) { data = i_stream_get_data(&stream->istream, &size); i_assert((size_t)ret <= size); i_assert(stream->istream.v_offset <= hstream->high_offset); skip = hstream->high_offset - stream->istream.v_offset; if (skip < (size_t)size) { hstream->high_offset += (size-skip); hstream->method->loop(hstream->hash_context, data+skip, size-skip); } } else if (ret < 0) { /* we finished hashing it. don't access it anymore, because the memory pointed by the hash may be freed before the istream itself */ hstream->hash_context = NULL; } return ret; } static void i_stream_hash_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct hash_istream *hstream = (struct hash_istream *)stream; if (hstream->hash_context != NULL) { io_stream_set_error(&stream->iostream, "Seeking not supported before hashing is finished"); stream->istream.stream_errno = ESPIPE; } stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; } struct istream * i_stream_create_hash(struct istream *input, const struct hash_method *method, void *hash_context) { struct hash_istream *hstream; hstream = i_new(struct hash_istream, 1); hstream->istream.max_buffer_size = input->real_stream->max_buffer_size; hstream->istream.stream_size_passthrough = TRUE; hstream->istream.read = i_stream_hash_read; hstream->istream.seek = i_stream_hash_seek; hstream->istream.istream.readable_fd = input->readable_fd; hstream->istream.istream.blocking = input->blocking; hstream->istream.istream.seekable = input->seekable; hstream->method = method; hstream->hash_context = hash_context; return i_stream_create(&hstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/file-copy.h0000644000175000017500000000067113123174404013725 00000000000000#ifndef FILE_COPY_H #define FILE_COPY_H /* Copy file atomically. First try hardlinking, then fallback to creating a temporary file (destpath.tmp) and rename()ing it over srcpath. If the destination file already exists, it may or may not be overwritten, so that shouldn't be relied on. Returns -1 = error, 0 = source file not found, 1 = ok */ int file_copy(const char *srcpath, const char *destpath, bool try_hardlink); #endif dovecot-2.2.33.2/src/lib/restrict-process-size.h0000644000175000017500000000153213123174404016316 00000000000000#ifndef RESTRICT_PROCESS_SIZE_H #define RESTRICT_PROCESS_SIZE_H #include #ifdef HAVE_SYS_RESOURCE_H # include #endif /* Restrict max. process size. */ void restrict_process_size(rlim_t bytes); /* Restrict max. number of processes. */ void restrict_process_count(rlim_t count); /* Set fd limit to count. */ void restrict_fd_limit(rlim_t count); /* Get the core dump size limit. Returns 0 if ok, -1 if lookup failed. */ int restrict_get_core_limit(rlim_t *limit_r); /* Get the process VSZ size limit. Returns 0 if ok, -1 if lookup failed. */ int restrict_get_process_size(rlim_t *limit_r); /* Get the process count limit. Returns 0 if ok, -1 if lookup failed. */ int restrict_get_process_limit(rlim_t *limit_r); /* Get the fd limit. Returns 0 if ok, -1 if lookup failed. */ int restrict_get_fd_limit(rlim_t *limit_r); #endif dovecot-2.2.33.2/src/lib/eacces-error.h0000644000175000017500000000107313123174404014405 00000000000000#ifndef EACCES_ERROR_H #define EACCES_ERROR_H /* Return a user-friendly error message for EACCES failures. */ const char *eacces_error_get(const char *func, const char *path); const char *eacces_error_get_creating(const char *func, const char *path); /* Return a user-friendly error message for fchown() or chown() EPERM failures when only the group is being changed. gid_origin specifies why exactly this group is being used. */ const char *eperm_error_get_chgrp(const char *func, const char *path, gid_t gid, const char *gid_origin) ATTR_NULL(4); #endif dovecot-2.2.33.2/src/lib/str-find.c0000644000175000017500000000757613123174404013572 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "str-find.h" struct str_find_context { pool_t pool; unsigned char *key; unsigned int key_len; unsigned int *matches; unsigned int match_count; size_t match_end_pos; int badtab[UCHAR_MAX+1]; int goodtab[FLEXIBLE_ARRAY_MEMBER]; }; static void init_badtab(struct str_find_context *ctx) { unsigned int i, len_1 = ctx->key_len - 1; for (i = 0; i <= UCHAR_MAX; i++) ctx->badtab[i] = ctx->key_len; for (i = 0; i < len_1; i++) ctx->badtab[ctx->key[i]] = len_1 - i; } static void init_suffixes(struct str_find_context *ctx, unsigned int *suffixes) { unsigned int len_1 = ctx->key_len - 1; int f = 0, g, i; suffixes[len_1] = ctx->key_len; g = len_1; for (i = (int)ctx->key_len - 2; i >= 0; i--) { if (i > g && (int)suffixes[i + len_1 - f] < i - g) suffixes[i] = suffixes[i + len_1 - f]; else { if (i < g) g = i; f = i; while (g >= 0 && ctx->key[g] == ctx->key[g + len_1 - f]) g--; suffixes[i] = f - g; } } } static void init_goodtab(struct str_find_context *ctx) { unsigned int *suffixes; int j, i, len_1 = ctx->key_len - 1; suffixes = t_buffer_get(sizeof(*suffixes) * ctx->key_len); init_suffixes(ctx, suffixes); for (i = 0; i < (int)ctx->key_len; i++) ctx->goodtab[i] = ctx->key_len; j = 0; for (i = len_1; i >= -1; i--) { if (i < 0 || suffixes[i] == (unsigned int)i + 1) { for (; j < len_1 - i; j++) { if (ctx->goodtab[j] == (int)ctx->key_len) ctx->goodtab[j] = len_1 - i; } } } for (i = 0; i <= (int)ctx->key_len - 2; i++) ctx->goodtab[len_1 - suffixes[i]] = len_1 - i; } struct str_find_context *str_find_init(pool_t pool, const char *key) { struct str_find_context *ctx; size_t key_len = strlen(key); i_assert(key_len > 0); i_assert(key_len < INT_MAX); ctx = p_malloc(pool, MALLOC_ADD(sizeof(struct str_find_context), MALLOC_MULTIPLY(sizeof(ctx->goodtab[0]), key_len))); ctx->pool = pool; ctx->matches = p_new(pool, unsigned int, key_len); ctx->key_len = key_len; ctx->key = p_malloc(pool, key_len); memcpy(ctx->key, key, key_len); init_goodtab(ctx); init_badtab(ctx); return ctx; } void str_find_deinit(struct str_find_context **_ctx) { struct str_find_context *ctx = *_ctx; *_ctx = NULL; p_free(ctx->pool, ctx->matches); p_free(ctx->pool, ctx->key); p_free(ctx->pool, ctx); } bool str_find_more(struct str_find_context *ctx, const unsigned char *data, size_t size) { unsigned int key_len = ctx->key_len; unsigned int i, j, a, b; int bad_value; for (i = j = 0; i < ctx->match_count; i++) { a = ctx->matches[i]; if (ctx->matches[i] + size >= key_len) { /* we can fully determine this match now */ for (; a < key_len; a++) { if (ctx->key[a] != data[a - ctx->matches[i]]) break; } if (a == key_len) { ctx->match_end_pos = key_len - ctx->matches[i]; return TRUE; } } else { for (b = 0; b < size; b++) { if (ctx->key[a+b] != data[b]) break; } if (b == size) ctx->matches[j++] = a + size; } } if (j > 0) { i_assert(j + size < key_len); ctx->match_count = j; j = 0; } else { /* Boyer-Moore searching */ j = 0; while (j + key_len <= size) { i = key_len - 1; while (ctx->key[i] == data[i + j]) { if (i == 0) { ctx->match_end_pos = j + key_len; return TRUE; } i--; } bad_value = (int)(ctx->badtab[data[i + j]] + i + 1) - (int)key_len; j += I_MAX(ctx->goodtab[i], bad_value); } i_assert(j <= size); ctx->match_count = 0; } for (; j < size; j++) { for (i = j; i < size; i++) { if (ctx->key[i-j] != data[i]) break; } if (i == size) ctx->matches[ctx->match_count++] = size - j; } return FALSE; } size_t str_find_get_match_end_pos(struct str_find_context *ctx) { return ctx->match_end_pos; } void str_find_reset(struct str_find_context *ctx) { ctx->match_count = 0; } dovecot-2.2.33.2/src/lib/crc32.c0000644000175000017500000000752413123174404012751 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "crc32.h" static uint32_t crc32tab[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; uint32_t crc32_data(const void *data, size_t size) { return crc32_data_more(0, data, size); } uint32_t crc32_data_more(uint32_t crc, const void *data, size_t size) { const uint8_t *p = data, *end = p + size; crc ^= 0xffffffff; for (; p != end; p++) crc = (crc >> 8) ^ crc32tab[((crc ^ *p) & 0xff)]; crc ^= 0xffffffff; return crc; } uint32_t crc32_str(const char *str) { return crc32_str_more(0, str); } uint32_t crc32_str_more(uint32_t crc, const char *str) { const uint8_t *p = (const uint8_t *)str; crc ^= 0xffffffff; for (; *p != '\0'; p++) crc = (crc >> 8) ^ crc32tab[((crc ^ *p) & 0xff)]; crc ^= 0xffffffff; return crc; } dovecot-2.2.33.2/src/lib/test-time-util.c0000644000175000017500000001201213165463624014722 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "time-util.h" #include static void test_timeval_cmp(void) { static struct timeval input[] = { { 0, 0 }, { 0, 0 }, { INT_MAX, 999999 }, { INT_MAX, 999999 }, { 0, 0 }, { 0, 1 }, { 0, 0 }, { 1, 0 }, { 0, 999999 }, { 1, 0 }, { 1, 0 }, { 1, 1 }, { -INT_MAX, 0 }, { INT_MAX, 0 } }; static int output[] = { 0, 0, -1, -1, -1, -1, -1 }; unsigned int i; test_begin("timeval_cmp()"); for (i = 0; i < N_ELEMENTS(input); i += 2) { test_assert(timeval_cmp(&input[i], &input[i+1]) == output[i/2]); test_assert(timeval_cmp(&input[i+1], &input[i]) == -output[i/2]); } test_end(); } static void test_timeval_diff(void) { static struct timeval input[] = { { 1, 0 }, { 0, 999999 }, { 1, 0 }, { 0, 999001 }, { 1, 1 }, { 0, 999001 }, { 2, 1 }, { 1, 0 }, { INT_MAX, 0 }, { INT_MAX-1, 1 } }; static int output[] = { 1, 999, 1000, 1000001, 999999 }; unsigned int i; long long udiff; int mdiff; test_begin("timeval_diff_*()"); for (i = 0; i < N_ELEMENTS(input); i += 2) { udiff = timeval_diff_usecs(&input[i], &input[i+1]); mdiff = timeval_diff_msecs(&input[i], &input[i+1]); test_assert(udiff == output[i/2]); test_assert(mdiff == udiff/1000); udiff = timeval_diff_usecs(&input[i+1], &input[i]); mdiff = timeval_diff_msecs(&input[i+1], &input[i]); test_assert(udiff == -output[i/2]); test_assert(mdiff == udiff/1000); } test_end(); } static void test_time_to_local_day_start(void) { /* Try this around days when DST changes in some of the more popular timezones. If that works, everything else probably works too. */ const struct tm tests[] = { /* Europe winter -> summer */ { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26 }, { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, /* Europe summer -> winter */ { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29 }, { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, /* USA winter -> summer */ { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12 }, { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, /* USA summer -> winter */ { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5 }, { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, /* (some of) Australia summer -> winter */ { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2 }, { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, /* (some of) Australia winter -> summer */ { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1 }, { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1, .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, }; const struct tm *tm; struct tm tm_copy; time_t t; test_begin("time_to_local_day_start()"); for (unsigned i = 0; i < N_ELEMENTS(tests); i++) { tm_copy = tests[i]; tm_copy.tm_isdst = -1; t = mktime(&tm_copy); test_assert_idx(t != (time_t)-1, i); t = time_to_local_day_start(t); tm = localtime(&t); test_assert_idx(tm->tm_year == tests[i].tm_year && tm->tm_mon == tests[i].tm_mon && tm->tm_mday == tests[i].tm_mday, i); test_assert_idx(tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0, i); } test_end(); } static void test_timestamp(const char *ts, int idx) { /* %G:%H:%M:%S */ const char **t = t_strsplit(ts, ":"); unsigned len = str_array_length(t); test_assert_idx(len == 4, idx); /* %G - ISO 8601 year */ test_assert_idx(strlen(t[0]) == 4, idx); unsigned v; test_assert_idx(str_to_uint(t[0], &v) == 0, idx); test_assert_idx(1000 <= v, idx); test_assert_idx(v <= 3000, idx); /* %H - hour from 00 to 23 */ test_assert_idx(strlen(t[1]) == 2, idx); test_assert_idx(str_to_uint(t[1], &v) == 0, idx); test_assert_idx(v <= 23, idx); /* %M - minute from 00 to 59 */ test_assert_idx(strlen(t[2]) == 2, idx); test_assert_idx(str_to_uint(t[2], &v) == 0, idx); test_assert_idx(v <= 59, idx); /* %S - second from 00 to 60 */ test_assert_idx(strlen(t[3]) == 2, idx); test_assert_idx(str_to_uint(t[3], &v) == 0, idx); test_assert_idx(v <= 60, idx); } #define TS_FMT "%G:%H:%M:%S" static void test_strftime_now(void) { test_begin("t_strftime and variants now"); time_t now = time(NULL); test_timestamp(t_strftime(TS_FMT, gmtime(&now)), 0); test_timestamp(t_strfgmtime(TS_FMT, now), 1); test_timestamp(t_strflocaltime(TS_FMT, now), 2); test_end(); } #define RFC2822_FMT "%a, %d %b %Y %T" static void test_strftime_fixed(void) { test_begin("t_strftime and variants fixed timestamp"); time_t ts = 1481222536; const char *exp = "Thu, 08 Dec 2016 18:42:16"; test_assert(strcmp(t_strftime(RFC2822_FMT, gmtime(&ts)), exp) == 0); test_assert(strcmp(t_strfgmtime(RFC2822_FMT, ts), exp) == 0); test_end(); } void test_time_util(void) { test_timeval_cmp(); test_timeval_diff(); test_time_to_local_day_start(); test_strftime_now(); test_strftime_fixed(); } dovecot-2.2.33.2/src/lib/wildcard-match.c0000644000175000017500000000552713165463624014734 00000000000000/* * This code would not have been possible without the prior work and * suggestions of various sourced. Special thanks to Robey for * all his time/help tracking down bugs and his ever-helpful advice. * * 04/09: Fixed the "*\*" against "*a" bug (caused an endless loop) * * Chris Fuller (aka Fred1@IRC & Fwitz@IRC) * crf@cfox.bchs.uh.edu * * I hereby release this code into the public domain * */ #include "lib.h" #include "wildcard-match.h" #include #define WILDS '*' /* matches 0 or more characters (including spaces) */ #define WILDQ '?' /* matches ecactly one character */ #define NOMATCH 0 #define MATCH (match+sofar) static int wildcard_match_int(const char *data, const char *mask, int icase) { const char *ma = mask, *na = data, *lsm = NULL, *lsn = NULL; int match = 1; int sofar = 0; if (na[0] == '\0') { /* empty string can match only "*" wildcard(s) */ while (ma[0] == '*') ma++; return ma[0] == '\0' ? MATCH : NOMATCH; } /* find the end of each string */ while (*(mask++)); mask-=2; while (*(data++)); data-=2; while (data >= na) { /* If the mask runs out of chars before the string, fall back on * a wildcard or fail. */ if (mask < ma) { if (lsm) { data = --lsn; mask = lsm; if (data < na) lsm = NULL; sofar = 0; } else return NOMATCH; } switch (*mask) { case WILDS: /* Matches anything */ do mask--; /* Zap redundant wilds */ while ((mask >= ma) && (*mask == WILDS)); lsm = mask; lsn = data; match += sofar; sofar = 0; /* Update fallback pos */ if (mask < ma) return MATCH; continue; /* Next char, please */ case WILDQ: mask--; data--; continue; /* '?' always matches */ } if (icase ? (i_toupper(*mask) == i_toupper(*data)) : (*mask == *data)) { /* If matching char */ mask--; data--; sofar++; /* Tally the match */ continue; /* Next char, please */ } if (lsm) { /* To to fallback on '*' */ data = --lsn; mask = lsm; if (data < na) lsm = NULL; /* Rewind to saved pos */ sofar = 0; continue; /* Next char, please */ } return NOMATCH; /* No fallback=No match */ } while ((mask >= ma) && (*mask == WILDS)) mask--; /* Zap leftover %s & *s */ return (mask >= ma) ? NOMATCH : MATCH; /* Start of both = match */ } bool wildcard_match(const char *data, const char *mask) { return wildcard_match_int(data, mask, FALSE) != 0; } bool wildcard_match_icase(const char *data, const char *mask) { return wildcard_match_int(data, mask, TRUE) != 0; } dovecot-2.2.33.2/src/lib/str-sanitize.c0000644000175000017500000000375513165463624014506 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "unichar.h" #include "str.h" #include "str-sanitize.h" static size_t str_sanitize_skip_start(const char *src, size_t max_bytes) { unichar_t chr; size_t i; for (i = 0; i < max_bytes && src[i] != '\0'; ) { int len = uni_utf8_get_char_n(src+i, max_bytes-i, &chr); if (len <= 0) break; if ((unsigned char)src[i] < 32) break; i += len; } i_assert(i <= max_bytes); return i; } static void str_sanitize_truncate_char(string_t *dest, unsigned int initial_pos) { const unsigned char *data = str_data(dest); size_t len = str_len(dest); if (len == initial_pos) return; if ((data[len-1] & 0x80) == 0) { str_truncate(dest, len-1); return; } /* truncate UTF-8 sequence. */ while (len > 0 && (data[len-1] & 0xc0) == 0x80) len--; if (len > 0 && (data[len-1] & 0xc0) == 0xc0) len--; if (len >= initial_pos) str_truncate(dest, len); } void str_sanitize_append(string_t *dest, const char *src, size_t max_bytes) { size_t initial_pos = str_len(dest); unichar_t chr; size_t i; for (i = 0; i < max_bytes && src[i] != '\0'; ) { int len = uni_utf8_get_char_n(src+i, max_bytes-i, &chr); if (len == 0) break; /* input ended too early */ if (len < 0) { /* invalid UTF-8 */ str_append_c(dest, '?'); i++; continue; } if ((unsigned char)src[i] < 32) str_append_c(dest, '?'); else str_append_n(dest, src+i, len); i += len; } if (src[i] != '\0') { if (max_bytes < 3) str_truncate(dest, initial_pos); else { while (str_len(dest) - initial_pos > max_bytes-3) str_sanitize_truncate_char(dest, initial_pos); } str_append(dest, "..."); } } const char *str_sanitize(const char *src, size_t max_bytes) { string_t *str; size_t i; if (src == NULL) return NULL; i = str_sanitize_skip_start(src, max_bytes); if (src[i] == '\0') return src; str = t_str_new(I_MIN(max_bytes, 256)); str_sanitize_append(str, src, max_bytes); return str_c(str); } dovecot-2.2.33.2/src/lib/file-set-size.h0000644000175000017500000000074213123174404014515 00000000000000#ifndef FILE_SET_SIZE_H #define FILE_SET_SIZE_H /* Shrink/grow file. If file is grown, the new data is guaranteed to be zeros. The file offset may be anywhere after this call. Returns -1 if failed, 0 if successful. */ int file_set_size(int fd, off_t size); /* Preallocate file to given size, without actually changing the size reported by stat(). Returns 1 if ok, 0 if not supported by this filesystem, -1 if error. */ int file_preallocate(int fd, off_t size); #endif dovecot-2.2.33.2/src/lib/failures.c0000644000175000017500000004247413165463624013665 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "hostpid.h" #include "net.h" #include "lib-signals.h" #include "backtrace-string.h" #include "printf-format-fix.h" #include "write-full.h" #include "fd-close-on-exec.h" #include #include #include #include const char *failure_log_type_prefixes[LOG_TYPE_COUNT] = { "Debug: ", "Info: ", "Warning: ", "Error: ", "Fatal: ", "Panic: " }; const char *failure_log_type_names[LOG_TYPE_COUNT] = { "debug", "info", "warning", "error", "fatal", "panic" }; /* Initialize working defaults */ static failure_callback_t *fatal_handler ATTR_NORETURN = default_fatal_handler; static failure_callback_t *error_handler = default_error_handler; static failure_callback_t *info_handler = default_error_handler; static failure_callback_t *debug_handler = default_error_handler; static void (*failure_exit_callback)(int *) = NULL; static struct failure_context failure_ctx_debug = { .type = LOG_TYPE_DEBUG }; static struct failure_context failure_ctx_info = { .type = LOG_TYPE_INFO }; static struct failure_context failure_ctx_warning = { .type = LOG_TYPE_WARNING }; static struct failure_context failure_ctx_error = { .type = LOG_TYPE_ERROR }; static int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO, log_debug_fd = STDERR_FILENO; static char *log_prefix = NULL; static char *log_stamp_format = NULL, *log_stamp_format_suffix = NULL; static bool failure_ignore_errors = FALSE, log_prefix_sent = FALSE; static bool coredump_on_error = FALSE; static void ATTR_FORMAT(2, 0) i_internal_error_handler(const struct failure_context *ctx, const char *format, va_list args); /* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */ static const char * get_log_stamp_format(const char *format_arg, unsigned int timestamp_usecs) ATTR_FORMAT_ARG(1); static const char *get_log_stamp_format(const char *format_arg ATTR_UNUSED, unsigned int timestamp_usecs) { if (log_stamp_format_suffix == NULL) return log_stamp_format; return t_strdup_printf("%s%06u%s", log_stamp_format, timestamp_usecs, log_stamp_format_suffix); } void failure_exit(int status) { if (failure_exit_callback != NULL) failure_exit_callback(&status); exit(status); } static void log_prefix_add(const struct failure_context *ctx, string_t *str) { const struct tm *tm = ctx->timestamp; char buf[256]; struct timeval now; if (log_stamp_format != NULL) { if (tm == NULL) { if (gettimeofday(&now, NULL) < 0) i_panic("gettimeofday() failed: %m"); tm = localtime(&now.tv_sec); } else { now.tv_usec = ctx->timestamp_usecs; } if (strftime(buf, sizeof(buf), get_log_stamp_format("unused", now.tv_usec), tm) > 0) str_append(str, buf); } if (log_prefix != NULL) str_append(str, log_prefix); } static void log_fd_flush_stop(struct ioloop *ioloop) { io_loop_stop(ioloop); } static int log_fd_write(int fd, const unsigned char *data, size_t len) { struct ioloop *ioloop; struct io *io; ssize_t ret; unsigned int prev_signal_term_counter = signal_term_counter; unsigned int terminal_eintr_count = 0; while ((ret = write(fd, data, len)) != (ssize_t)len) { if (ret > 0) { /* some was written, continue.. */ data += ret; len -= ret; continue; } if (ret == 0) { /* out of disk space? */ errno = ENOSPC; return -1; } switch (errno) { case EAGAIN: /* wait until we can write more. this can happen at least when writing to terminal, even if fd is blocking. */ ioloop = io_loop_create(); io = io_add(fd, IO_WRITE, log_fd_flush_stop, ioloop); io_loop_run(ioloop); io_remove(&io); io_loop_destroy(&ioloop); break; case EINTR: if (prev_signal_term_counter == signal_term_counter) { /* non-terminal signal. ignore. */ } else if (terminal_eintr_count++ == 0) { /* we'd rather not die in the middle of writing to log. try again once more */ } else { /* received two terminal signals. someone wants us dead. */ return -1; } break; default: return -1; } prev_signal_term_counter = signal_term_counter; } return 0; } static int ATTR_FORMAT(3, 0) default_handler(const struct failure_context *ctx, int fd, const char *format, va_list args) { static int recursed = 0; int ret; if (recursed >= 2) { /* we're being called from some signal handler or we ran out of memory */ return -1; } recursed++; T_BEGIN { string_t *str = t_str_new(256); log_prefix_add(ctx, str); str_append(str, failure_log_type_prefixes[ctx->type]); /* make sure there's no %n in there and fix %m */ str_vprintfa(str, printf_format_fix(format), args); str_append_c(str, '\n'); ret = log_fd_write(fd, str_data(str), str_len(str)); } T_END; if (ret < 0 && failure_ignore_errors) ret = 0; recursed--; return ret; } static void ATTR_NORETURN default_fatal_finish(enum log_type type, int status) { const char *backtrace; if (type == LOG_TYPE_PANIC || status == FATAL_OUTOFMEM) { if (backtrace_get(&backtrace) == 0) i_error("Raw backtrace: %s", backtrace); } if (type == LOG_TYPE_PANIC || getenv("CORE_ERROR") != NULL || (status == FATAL_OUTOFMEM && getenv("CORE_OUTOFMEM") != NULL)) abort(); else failure_exit(status); } void default_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) { int status = ctx->exit_status; if (default_handler(ctx, log_fd, format, args) < 0 && status == FATAL_DEFAULT) status = FATAL_LOGWRITE; default_fatal_finish(ctx->type, status); } void default_error_handler(const struct failure_context *ctx, const char *format, va_list args) { int fd; switch (ctx->type) { case LOG_TYPE_DEBUG: fd = log_debug_fd; break; case LOG_TYPE_INFO: fd = log_info_fd; break; default: fd = log_fd; } if (default_handler(ctx, fd, format, args) < 0) { if (fd == log_fd) failure_exit(FATAL_LOGWRITE); /* we failed to log to info/debug log, try to log the write error to error log - maybe that'll work. */ i_fatal_status(FATAL_LOGWRITE, "write() failed to %s log: %m", fd == log_info_fd ? "info" : "debug"); } if (ctx->type == LOG_TYPE_ERROR && coredump_on_error) abort(); } void i_log_type(const struct failure_context *ctx, const char *format, ...) { va_list args; va_start(args, format); switch (ctx->type) { case LOG_TYPE_DEBUG: debug_handler(ctx, format, args); break; case LOG_TYPE_INFO: info_handler(ctx, format, args); break; default: error_handler(ctx, format, args); } va_end(args); } void i_panic(const char *format, ...) { struct failure_context ctx; va_list args; i_zero(&ctx); ctx.type = LOG_TYPE_PANIC; va_start(args, format); fatal_handler(&ctx, format, args); va_end(args); } void i_fatal(const char *format, ...) { struct failure_context ctx; va_list args; i_zero(&ctx); ctx.type = LOG_TYPE_FATAL; ctx.exit_status = FATAL_DEFAULT; va_start(args, format); fatal_handler(&ctx, format, args); va_end(args); } void i_fatal_status(int status, const char *format, ...) { struct failure_context ctx; va_list args; i_zero(&ctx); ctx.type = LOG_TYPE_FATAL; ctx.exit_status = status; va_start(args, format); fatal_handler(&ctx, format, args); va_end(args); } void i_error(const char *format, ...) { int old_errno = errno; va_list args; va_start(args, format); error_handler(&failure_ctx_error, format, args); va_end(args); errno = old_errno; } void i_warning(const char *format, ...) { int old_errno = errno; va_list args; va_start(args, format); error_handler(&failure_ctx_warning, format, args); va_end(args); errno = old_errno; } void i_info(const char *format, ...) { int old_errno = errno; va_list args; va_start(args, format); info_handler(&failure_ctx_info, format, args); va_end(args); errno = old_errno; } void i_debug(const char *format, ...) { int old_errno = errno; va_list args; va_start(args, format); debug_handler(&failure_ctx_debug, format, args); va_end(args); errno = old_errno; } void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN) { fatal_handler = callback; } void i_set_error_handler(failure_callback_t *callback) { coredump_on_error = getenv("CORE_ERROR") != NULL; error_handler = callback; } void i_set_info_handler(failure_callback_t *callback) { info_handler = callback; } void i_set_debug_handler(failure_callback_t *callback) { debug_handler = callback; } void i_get_failure_handlers(failure_callback_t **fatal_callback_r, failure_callback_t **error_callback_r, failure_callback_t **info_callback_r, failure_callback_t **debug_callback_r) { *fatal_callback_r = fatal_handler; *error_callback_r = error_handler; *info_callback_r = info_handler; *debug_callback_r = debug_handler; } static int ATTR_FORMAT(3, 0) syslog_handler(int level, enum log_type type, const char *format, va_list args) { static int recursed = 0; if (recursed >= 2) return -1; recursed++; /* syslogs don't generatelly bother to log the level in any way, so make sure errors are shown clearly */ T_BEGIN { syslog(level, "%s%s%s", log_prefix == NULL ? "" : log_prefix, type != LOG_TYPE_INFO ? failure_log_type_prefixes[type] : "", t_strdup_vprintf(format, args)); } T_END; recursed--; return 0; } void i_syslog_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) { int status = ctx->exit_status; if (syslog_handler(LOG_CRIT, ctx->type, format, args) < 0 && status == FATAL_DEFAULT) status = FATAL_LOGERROR; default_fatal_finish(ctx->type, status); } void i_syslog_error_handler(const struct failure_context *ctx, const char *format, va_list args) { int level = LOG_ERR; switch (ctx->type) { case LOG_TYPE_DEBUG: level = LOG_DEBUG; break; case LOG_TYPE_INFO: level = LOG_INFO; break; case LOG_TYPE_WARNING: level = LOG_WARNING; break; case LOG_TYPE_ERROR: level = LOG_ERR; break; case LOG_TYPE_FATAL: case LOG_TYPE_PANIC: level = LOG_CRIT; break; case LOG_TYPE_COUNT: case LOG_TYPE_OPTION: i_unreached(); } if (syslog_handler(level, ctx->type, format, args) < 0) failure_exit(FATAL_LOGERROR); } void i_set_failure_syslog(const char *ident, int options, int facility) { openlog(ident, options, facility); i_set_fatal_handler(i_syslog_fatal_handler); i_set_error_handler(i_syslog_error_handler); i_set_info_handler(i_syslog_error_handler); i_set_debug_handler(i_syslog_error_handler); } static void open_log_file(int *fd, const char *path) { const char *str; if (*fd != STDERR_FILENO) { if (close(*fd) < 0) { str = t_strdup_printf("close(%d) failed: %m\n", *fd); (void)write_full(STDERR_FILENO, str, strlen(str)); } } if (path == NULL || strcmp(path, "/dev/stderr") == 0) *fd = STDERR_FILENO; else { *fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); if (*fd == -1) { *fd = STDERR_FILENO; str = t_strdup_printf("Can't open log file %s: %m\n", path); (void)write_full(STDERR_FILENO, str, strlen(str)); if (fd == &log_fd) failure_exit(FATAL_LOGOPEN); else i_fatal_status(FATAL_LOGOPEN, "%s", str); } fd_close_on_exec(*fd, TRUE); } } void i_set_failure_file(const char *path, const char *prefix) { i_set_failure_prefix("%s", prefix); if (log_info_fd != STDERR_FILENO && log_info_fd != log_fd) { if (close(log_info_fd) < 0) i_error("close(%d) failed: %m", log_info_fd); } if (log_debug_fd != STDERR_FILENO && log_debug_fd != log_info_fd && log_debug_fd != log_fd) { if (close(log_debug_fd) < 0) i_error("close(%d) failed: %m", log_debug_fd); } open_log_file(&log_fd, path); /* if info/debug logs are elsewhere, i_set_info/debug_file() overrides these later. */ log_info_fd = log_fd; log_debug_fd = log_fd; i_set_fatal_handler(default_fatal_handler); i_set_error_handler(default_error_handler); i_set_info_handler(default_error_handler); i_set_debug_handler(default_error_handler); } static void i_failure_send_option(const char *key, const char *value) { const char *str; if (error_handler != i_internal_error_handler) return; str = t_strdup_printf("\001%c%s %s=%s\n", LOG_TYPE_OPTION+1, my_pid, key, value); (void)write_full(STDERR_FILENO, str, strlen(str)); } void i_set_failure_prefix(const char *prefix_fmt, ...) { va_list args; va_start(args, prefix_fmt); i_free(log_prefix); log_prefix = i_strdup_vprintf(prefix_fmt, args); va_end(args); log_prefix_sent = FALSE; } void i_unset_failure_prefix(void) { i_free(log_prefix); log_prefix = i_strdup(""); log_prefix_sent = FALSE; } const char *i_get_failure_prefix(void) { return log_prefix != NULL ? log_prefix : ""; } static int internal_send_split(string_t *full_str, size_t prefix_len) { string_t *str; size_t max_text_len, pos = prefix_len; str = t_str_new(PIPE_BUF); str_append_n(str, str_c(full_str), prefix_len); max_text_len = PIPE_BUF - prefix_len - 1; while (pos < str_len(full_str)) { str_truncate(str, prefix_len); str_append_n(str, str_c(full_str) + pos, max_text_len); str_append_c(str, '\n'); if (log_fd_write(STDERR_FILENO, str_data(str), str_len(str)) < 0) return -1; pos += max_text_len; } return 0; } static int ATTR_FORMAT(2, 0) internal_handler(const struct failure_context *ctx, const char *format, va_list args) { static int recursed = 0; int ret; if (recursed >= 2) { /* we're being called from some signal handler or we ran out of memory */ return -1; } recursed++; T_BEGIN { string_t *str; size_t prefix_len; if (!log_prefix_sent && log_prefix != NULL) { log_prefix_sent = TRUE; i_failure_send_option("prefix", log_prefix); } str = t_str_new(128); str_printfa(str, "\001%c%s ", ctx->type + 1, my_pid); prefix_len = str_len(str); str_vprintfa(str, format, args); if (str_len(str)+1 <= PIPE_BUF) { str_append_c(str, '\n'); ret = log_fd_write(STDERR_FILENO, str_data(str), str_len(str)); } else { ret = internal_send_split(str, prefix_len); } } T_END; if (ret < 0 && failure_ignore_errors) ret = 0; recursed--; return ret; } static bool line_is_ok(const char *line) { if (*line != 1) return FALSE; if (line[1] == '\0') { i_warning("Broken log line follows (type=NUL)"); return FALSE; } if (line[1]-1 > LOG_TYPE_OPTION) { i_warning("Broken log line follows (type=%d)", line[1]-1); return FALSE; } return TRUE; } void i_failure_parse_line(const char *line, struct failure_line *failure) { i_zero(failure); if (!line_is_ok(line)) { failure->log_type = LOG_TYPE_ERROR; failure->text = line; return; } failure->log_type = line[1] - 1; line += 2; failure->text = line; while (*line >= '0' && *line <= '9') { failure->pid = failure->pid*10 + (*line - '0'); line++; } if (*line != ' ') { /* some old protocol? */ failure->pid = 0; return; } failure->text = line + 1; } static void ATTR_NORETURN ATTR_FORMAT(2, 0) i_internal_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) { int status = ctx->exit_status; if (internal_handler(ctx, format, args) < 0 && status == FATAL_DEFAULT) status = FATAL_LOGERROR; default_fatal_finish(ctx->type, status); } static void i_internal_error_handler(const struct failure_context *ctx, const char *format, va_list args) { if (internal_handler(ctx, format, args) < 0) failure_exit(FATAL_LOGERROR); } void i_set_failure_internal(void) { i_set_fatal_handler(i_internal_fatal_handler); i_set_error_handler(i_internal_error_handler); i_set_info_handler(i_internal_error_handler); i_set_debug_handler(i_internal_error_handler); } void i_set_failure_ignore_errors(bool ignore) { failure_ignore_errors = ignore; } void i_set_info_file(const char *path) { if (log_info_fd == log_fd) log_info_fd = STDERR_FILENO; open_log_file(&log_info_fd, path); info_handler = default_error_handler; /* write debug-level messages to the info_log_path, until i_set_debug_file() was called */ log_debug_fd = log_info_fd; i_set_debug_handler(default_error_handler); } void i_set_debug_file(const char *path) { if (log_debug_fd == log_fd || log_debug_fd == log_info_fd) log_debug_fd = STDERR_FILENO; open_log_file(&log_debug_fd, path); debug_handler = default_error_handler; } void i_set_failure_timestamp_format(const char *fmt) { const char *p; i_free(log_stamp_format); i_free_and_null(log_stamp_format_suffix); p = strstr(fmt, "%{usecs}"); if (p == NULL) log_stamp_format = i_strdup(fmt); else { log_stamp_format = i_strdup_until(fmt, p); log_stamp_format_suffix = i_strdup(p + 8); } } void i_set_failure_send_ip(const struct ip_addr *ip) { i_failure_send_option("ip", net_ip2addr(ip)); } void i_set_failure_send_prefix(const char *prefix) { i_failure_send_option("prefix", prefix); } void i_set_failure_exit_callback(void (*callback)(int *status)) { failure_exit_callback = callback; } void failures_deinit(void) { if (log_debug_fd == log_info_fd || log_debug_fd == log_fd) log_debug_fd = STDERR_FILENO; if (log_info_fd == log_fd) log_info_fd = STDERR_FILENO; if (log_fd != STDERR_FILENO) { i_close_fd(&log_fd); log_fd = STDERR_FILENO; } if (log_info_fd != STDERR_FILENO) { i_close_fd(&log_info_fd); log_info_fd = STDERR_FILENO; } if (log_debug_fd != STDERR_FILENO) { i_close_fd(&log_debug_fd); log_debug_fd = STDERR_FILENO; } i_free_and_null(log_prefix); i_free_and_null(log_stamp_format); i_free_and_null(log_stamp_format_suffix); } dovecot-2.2.33.2/src/lib/var-expand.c0000644000175000017500000004111113165463624014103 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "md5.h" #include "hash.h" #include "hex-binary.h" #include "base64.h" #include "hostpid.h" #include "hmac.h" #include "pkcs5.h" #include "hash-method.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "var-expand-private.h" #include #include #define TABLE_LAST(t) \ ((t)->key == '\0' && (t)->long_key == NULL) struct var_expand_modifier { char key; const char *(*func)(const char *, struct var_expand_context *); }; static ARRAY(struct var_expand_extension_func_table) var_expand_extensions; static const char * m_str_lcase(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { return t_str_lcase(str); } static const char * m_str_ucase(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { return t_str_ucase(str); } static const char * m_str_escape(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { return str_escape(str); } static const char * m_str_hex(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { unsigned long long l; if (str_to_ullong(str, &l) < 0) l = 0; return t_strdup_printf("%llx", l); } static const char * m_str_reverse(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { size_t len = strlen(str); char *p, *rev; rev = t_malloc(len + 1); rev[len] = '\0'; for (p = rev + len - 1; *str != '\0'; str++) *p-- = *str; return rev; } static const char *m_str_hash(const char *str, struct var_expand_context *ctx) { unsigned int value = str_hash(str); string_t *hash = t_str_new(20); if (ctx->width != 0) { value %= ctx->width; ctx->width = 0; } str_printfa(hash, "%x", value); while ((int)str_len(hash) < ctx->offset) str_insert(hash, 0, "0"); ctx->offset = 0; return str_c(hash); } static const char * m_str_newhash(const char *str, struct var_expand_context *ctx) { string_t *hash = t_str_new(20); unsigned char result[MD5_RESULTLEN]; unsigned int i; uint64_t value = 0; md5_get_digest(str, strlen(str), result); for (i = 0; i < sizeof(value); i++) { value <<= 8; value |= result[i]; } if (ctx->width != 0) { value %= ctx->width; ctx->width = 0; } str_printfa(hash, "%x", (unsigned int)value); while ((int)str_len(hash) < ctx->offset) str_insert(hash, 0, "0"); ctx->offset = 0; return str_c(hash); } static const char * m_str_md5(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { unsigned char digest[16]; md5_get_digest(str, strlen(str), digest); return binary_to_hex(digest, sizeof(digest)); } static const char * m_str_ldap_dn(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { string_t *ret = t_str_new(256); while (*str) { if (*str == '.') str_append(ret, ",dc="); else str_append_c(ret, *str); str++; } return str_free_without_data(&ret); } static const char * m_str_trim(const char *str, struct var_expand_context *ctx ATTR_UNUSED) { size_t len; len = strlen(str); while (len > 0 && i_isspace(str[len-1])) len--; return t_strndup(str, len); } #define MAX_MODIFIER_COUNT 10 static const struct var_expand_modifier modifiers[] = { { 'L', m_str_lcase }, { 'U', m_str_ucase }, { 'E', m_str_escape }, { 'X', m_str_hex }, { 'R', m_str_reverse }, { 'H', m_str_hash }, { 'N', m_str_newhash }, { 'M', m_str_md5 }, { 'D', m_str_ldap_dn }, { 'T', m_str_trim }, { '\0', NULL } }; static const char * var_expand_short(const struct var_expand_table *table, char key) { const struct var_expand_table *t; if (table != NULL) { for (t = table; !TABLE_LAST(t); t++) { if (t->key == key) return t->value != NULL ? t->value : ""; } } /* not found */ if (key == '%') return "%"; return NULL; } static int var_expand_hash(struct var_expand_context *ctx, const char *key, const char *field, const char **result_r, const char **error_r) { enum { FORMAT_HEX, FORMAT_HEX_UC, FORMAT_BASE64 } format = FORMAT_HEX; const char *p = strchr(key, ';'); const char *const *args = NULL; const char *algo = key; const char *value; int ret; if (p != NULL) { algo = t_strcut(key, ';'); args = t_strsplit(p+1, ","); } const struct hash_method *method; if (strcmp(algo, "pkcs5") == 0) { method = hash_method_lookup("sha256"); } else if ((method = hash_method_lookup(algo)) == NULL) { return 0; } string_t *field_value = t_str_new(64); string_t *salt = t_str_new(64); string_t *tmp = t_str_new(method->digest_size); if ((ret = var_expand_long(ctx, field, strlen(field), &value, error_r)) < 1) { return ret; } str_append(field_value, value); /* default values */ unsigned int rounds = 1; unsigned int truncbits = 0; if (strcmp(algo, "pkcs5") == 0) { rounds = 2048; str_append(salt, field); } while(args != NULL && *args != NULL) { const char *k = t_strcut(*args, '='); const char *value = strchr(*args, '='); if (value == NULL) { args++; continue; } else { value++; } if (strcmp(k, "rounds") == 0) { if (str_to_uint(value, &rounds)<0) { *error_r = t_strdup_printf( "Cannot parse hash arguments:" "'%s' is not number for rounds", value); return -1; } if (rounds < 1) { *error_r = t_strdup_printf( "Cannot parse hash arguments:" "rounds must be at least 1"); return -1; } } else if (strcmp(k, "truncate") == 0) { if (str_to_uint(value, &truncbits)<0) { *error_r = t_strdup_printf( "Cannot parse hash arguments:" "'%s' is not number for truncbits", value); return -1; } truncbits = I_MIN(truncbits, method->digest_size*8); } else if (strcmp(k, "salt") == 0) { str_truncate(salt, 0); var_expand_with_funcs(salt, value, ctx->table, ctx->func_table, ctx->context); break; } else if (strcmp(k, "format") == 0) { if (strcmp(value, "hex") == 0) { format = FORMAT_HEX; } else if (strcmp(value, "hexuc") == 0){ format = FORMAT_HEX_UC; } else if (strcmp(value, "base64") == 0) { format = FORMAT_BASE64; } else { *error_r = t_strdup_printf( "Cannot parse hash arguments:" "'%s' is not supported format", value); return -1; } } args++; } str_truncate(tmp, 0); if (strcmp(algo, "pkcs5") == 0) { if (pkcs5_pbkdf(PKCS5_PBKDF2, method, field_value->data, field_value->used, salt->data, salt->used, rounds, HMAC_MAX_CONTEXT_SIZE, tmp) != 0) { *error_r = "Cannot hash: PKCS5_PBKDF2 failed"; return -1; } } else { void *context = t_malloc(method->context_size); str_append_str(tmp, field_value); for(;rounds>0;rounds--) { method->init(context); if (salt->used > 0) method->loop(context, salt->data, salt->used); method->loop(context, tmp->data, tmp->used); unsigned char *digest = buffer_get_modifiable_data(tmp, NULL); method->result(context, digest); if (tmp->used != method->digest_size) buffer_set_used_size(tmp, method->digest_size); } } if (truncbits > 0) buffer_truncate_rshift_bits(tmp, truncbits); switch(format) { case FORMAT_HEX: *result_r = binary_to_hex(tmp->data, tmp->used); return 1; case FORMAT_HEX_UC: *result_r = binary_to_hex(tmp->data, tmp->used); return 1; case FORMAT_BASE64: { string_t *dest = t_str_new(64); base64_encode(tmp->data, tmp->used, dest); *result_r = str_c(dest); return 1; } } i_unreached(); } static int var_expand_func(const struct var_expand_func_table *func_table, const char *key, const char *data, void *context, const char **var_r, const char **error_r) { const char *value; if (strcmp(key, "env") == 0) { value = getenv(data); *var_r = value != NULL ? value : ""; return 1; } if (func_table != NULL) { for (; func_table->key != NULL; func_table++) { if (strcmp(func_table->key, key) == 0) { value = func_table->func(data, context); *var_r = value != NULL ? value : ""; return 1; } } } *error_r = t_strdup_printf("Unknown variable '%%%s'", key); *var_r = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key); return 0; } static int var_expand_try_extension(struct var_expand_context *ctx, const char *key, const char *data, const char **var_r, const char **error_r) { int ret; const char *sep = strchr(key, ';'); if (sep == NULL) sep = key + strlen(key); /* try with extensions */ const struct var_expand_extension_func_table *f; array_foreach(&var_expand_extensions, f) { /* ensure we won't match abbreviations */ size_t len = sep-key; if (strncasecmp(key, f->key, len) == 0 && f->key[len] == '\0') return f->func(ctx, key, data, var_r, error_r); } if ((ret = var_expand_func(ctx->func_table, key, data, ctx->context, var_r, error_r)) == 0) { *error_r = t_strdup_printf("Unknown variable '%%%s'", key); } return ret; } int var_expand_long(struct var_expand_context *ctx, const void *key_start, size_t key_len, const char **var_r, const char **error_r) { const struct var_expand_table *t; const char *key, *value = NULL; int ret = 1; if (ctx->table != NULL) { for (t = ctx->table; !TABLE_LAST(t); t++) { if (t->long_key != NULL && strncmp(t->long_key, key_start, key_len) == 0 && t->long_key[key_len] == '\0') { *var_r = t->value != NULL ? t->value : ""; return 1; } } } key = t_strndup(key_start, key_len); /* built-in variables: */ switch (key_len) { case 3: if (strcmp(key, "pid") == 0) value = my_pid; else if (strcmp(key, "uid") == 0) value = dec2str(geteuid()); else if (strcmp(key, "gid") == 0) value = dec2str(getegid()); break; case 8: if (strcmp(key, "hostname") == 0) value = my_hostname; break; } if (value == NULL) { const char *data = strchr(key, ':'); if (data != NULL) key = t_strdup_until(key, data++); else data = ""; ret = var_expand_try_extension(ctx, key, data, &value, error_r); if (ret <= 0 && value == NULL) { value = ""; } } if (value == NULL) value = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key); *var_r = value; return ret; } void var_expand_with_funcs(string_t *dest, const char *str, const struct var_expand_table *table, const struct var_expand_func_table *func_table, void *context) { const struct var_expand_modifier *m; const char *var; struct var_expand_context ctx; const char *(*modifier[MAX_MODIFIER_COUNT]) (const char *, struct var_expand_context *); const char *end; unsigned int i, modifier_count; size_t len; i_zero(&ctx); ctx.table = table; ctx.func_table = func_table; ctx.context = context; for (; *str != '\0'; str++) { if (*str != '%') str_append_c(dest, *str); else { int sign = 1; str++; /* reset per-field modifiers */ ctx.offset = 0; ctx.width = 0; ctx.zero_padding = FALSE; /* [.][] */ if (*str == '-') { sign = -1; str++; } if (*str == '0') { ctx.zero_padding = TRUE; str++; } while (*str >= '0' && *str <= '9') { ctx.width = ctx.width*10 + (*str - '0'); str++; } if (*str == '.') { ctx.offset = sign * ctx.width; sign = 1; ctx.width = 0; str++; /* if offset was prefixed with zero (or it was plain zero), just ignore that. zero padding is done with the width. */ ctx.zero_padding = FALSE; if (*str == '0') { ctx.zero_padding = TRUE; str++; } if (*str == '-') { sign = -1; str++; } while (*str >= '0' && *str <= '9') { ctx.width = ctx.width*10 + (*str - '0'); str++; } ctx.width = sign * ctx.width; } modifier_count = 0; while (modifier_count < MAX_MODIFIER_COUNT) { modifier[modifier_count] = NULL; for (m = modifiers; m->key != '\0'; m++) { if (m->key == *str) { /* @UNSAFE */ modifier[modifier_count] = m->func; str++; break; } } if (modifier[modifier_count] == NULL) break; modifier_count++; } if (*str == '\0') break; var = NULL; if (*str == '{' && (end = strchr(str, '}')) != NULL) { /* %{long_key} */ const char *error; unsigned int ctr = 1; end = str; while(*++end != '\0' && ctr > 0) { if (*end == '{') ctr++; if (*end == '}') ctr--; } if (ctr == 0) /* it needs to come back a bit */ end--; /* if there is no } it will consume rest of the string */ len = end - (str + 1); if (var_expand_long(&ctx, str+1, len, &var, &error) < 0) i_error("var_expand_long(%s) failed: %s", str+1, error); i_assert(var != NULL); str = end; } else { var = var_expand_short(table, *str); } if (var != NULL) { for (i = 0; i < modifier_count; i++) var = modifier[i](var, &ctx); if (ctx.offset < 0) { /* if offset is < 0 then we want to start at the end */ size_t len = strlen(var); if (len > (size_t)-ctx.offset) var += len + ctx.offset; } else { while (*var != '\0' && ctx.offset > 0) { ctx.offset--; var++; } } if (ctx.width == 0) str_append(dest, var); else if (!ctx.zero_padding) { if (ctx.width < 0) ctx.width = strlen(var) - (-ctx.width); str_append_n(dest, var, ctx.width); } else { /* %05d -like padding. no truncation. */ ssize_t len = strlen(var); while (len < ctx.width) { str_append_c(dest, '0'); ctx.width--; } str_append(dest, var); } } } } } void var_expand(string_t *dest, const char *str, const struct var_expand_table *table) { var_expand_with_funcs(dest, str, table, NULL, NULL); } static bool var_get_key_range_full(const char *str, unsigned int *idx_r, unsigned int *size_r) { const struct var_expand_modifier *m; unsigned int i = 0; /* [.][] */ while ((str[i] >= '0' && str[i] <= '9') || str[i] == '-') i++; if (str[i] == '.') { i++; while ((str[i] >= '0' && str[i] <= '9') || str[i] == '-') i++; } do { for (m = modifiers; m->key != '\0'; m++) { if (m->key == str[i]) { i++; break; } } } while (m->key != '\0'); if (str[i] != '{') { /* short key */ *idx_r = i; *size_r = str[i] == '\0' ? 0 : 1; return FALSE; } else { /* long key */ *idx_r = ++i; for (; str[i] != '\0'; i++) { if (str[i] == '}') break; } *size_r = i - *idx_r; return TRUE; } } char var_get_key(const char *str) { unsigned int idx, size; if (var_get_key_range_full(str, &idx, &size)) return '{'; return str[idx]; } void var_get_key_range(const char *str, unsigned int *idx_r, unsigned int *size_r) { (void)var_get_key_range_full(str, idx_r, size_r); } static bool var_has_long_key(const char **str, const char *long_key) { const char *start, *end; start = strchr(*str, '{'); i_assert(start != NULL); end = strchr(++start, '}'); if (end == NULL) return FALSE; if (strncmp(start, long_key, end-start) == 0 && long_key[end-start] == '\0') return TRUE; *str = end; return FALSE; } bool var_has_key(const char *str, char key, const char *long_key) { char c; for (; *str != '\0'; str++) { if (*str == '%' && str[1] != '\0') { str++; c = var_get_key(str); if (c == key && key != '\0') return TRUE; if (c == '{' && long_key != NULL) { if (var_has_long_key(&str, long_key)) return TRUE; } } } return FALSE; } void var_expand_extensions_deinit(void) { array_free(&var_expand_extensions); } void var_expand_extensions_init(void) { i_array_init(&var_expand_extensions, 32); /* put all hash methods there */ for(const struct hash_method **meth = hash_methods; *meth != NULL; meth++) { struct var_expand_extension_func_table *func = array_append_space(&var_expand_extensions); func->key = (*meth)->name; func->func = var_expand_hash; } /* pkcs5 */ struct var_expand_extension_func_table *func = array_append_space(&var_expand_extensions); func->key = "pkcs5"; func->func = var_expand_hash; /* if */ func = array_append_space(&var_expand_extensions); func->key = "if"; func->func = var_expand_if; } void var_expand_register_func_array(const struct var_expand_extension_func_table *funcs) { for(const struct var_expand_extension_func_table *ptr = funcs; ptr->key != NULL; ptr++) { i_assert(*ptr->key != '\0'); array_insert(&var_expand_extensions, 0, ptr, 1); } } void var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs) { for(const struct var_expand_extension_func_table *ptr = funcs; ptr->key != NULL; ptr++) { i_assert(ptr->func != NULL); for(unsigned int i = 0; i < array_count(&var_expand_extensions); i++) { const struct var_expand_extension_func_table *func = array_idx(&var_expand_extensions, i); if (strcasecmp(func->key, ptr->key) == 0) { array_delete(&var_expand_extensions, i, 1); } } } } dovecot-2.2.33.2/src/lib/test-istream-base64-encoder.c0000644000175000017500000000635313165463624017167 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "istream-private.h" #include "istream-base64.h" static struct test { const char *input; unsigned int chars_per_line; bool crlf; const char *output; } tests[] = { { "hello world", 80, FALSE, "aGVsbG8gd29ybGQ=" }, { "hello world", 4, FALSE, "aGVs\nbG8g\nd29y\nbGQ=" }, { "hello world", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=" }, { "hello worlds", 80, FALSE, "aGVsbG8gd29ybGRz" }, { "hello worlds", 4, FALSE, "aGVs\nbG8g\nd29y\nbGRz" }, { "hello worlds", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGRz" }, { "hell world", 80, FALSE, "aGVsbCB3b3JsZA==" }, { "hell world", 4, FALSE, "aGVs\nbCB3\nb3Js\nZA==" }, { "hell world", 4, TRUE, "aGVs\r\nbCB3\r\nb3Js\r\nZA==" }, { "hello to the world!!", 80, FALSE, "aGVsbG8gdG8gdGhlIHdvcmxkISE=" }, { "hello to the world!!", 8, FALSE, "aGVsbG8g\ndG8gdGhl\nIHdvcmxk\nISE=" }, { "hello to the world!!", 8, TRUE, "aGVsbG8g\r\ndG8gdGhl\r\nIHdvcmxk\r\nISE=" }, }; static const char *hello = "hello world"; static void encode_test(const char *text, unsigned int chars_per_line, bool crlf, const char *output) { unsigned int i, text_len = strlen(text); struct istream *input, *input_data; const unsigned char *data; uoff_t stream_size; size_t size; ssize_t ret; input_data = test_istream_create_data(text, text_len); test_istream_set_allow_eof(input_data, FALSE); input = i_stream_create_base64_encoder(input_data, chars_per_line, crlf); for (i = 1; i <= text_len; i++) { test_istream_set_size(input_data, i); while ((ret = i_stream_read(input)) > 0) ; test_assert(ret == 0); } test_istream_set_allow_eof(input_data, TRUE); while ((ret = i_stream_read(input)) > 0) ; test_assert(ret == -1); data = i_stream_get_data(input, &size); test_assert(size == strlen(output) && memcmp(data, output, size) == 0); ret = i_stream_get_size(input, TRUE, &stream_size); test_assert(ret > 0); test_assert(size == stream_size); i_stream_unref(&input); i_stream_unref(&input_data); } static void test_istream_base64_encoder_seek(const char *textin, const char *textout) { unsigned int offset, len = strlen(textout); struct istream *input, *input_data; const unsigned char *data; size_t size; ssize_t ret; input_data = i_stream_create_from_data(textin, strlen(textin)); input = i_stream_create_base64_encoder(input_data, 4, TRUE); while (i_stream_read(input) > 0) ; i_stream_skip(input, i_stream_get_data_size(input)); for (offset = 0; offset < len; offset++) { i_stream_seek(input, offset); while ((ret = i_stream_read(input)) > 0) ; test_assert(ret == -1); data = i_stream_get_data(input, &size); test_assert(size == len-offset); test_assert(memcmp(data, textout+offset, size) == 0); i_stream_skip(input, size); } i_stream_unref(&input); i_stream_unref(&input_data); } void test_istream_base64_encoder(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(tests); i++) { test_begin(t_strdup_printf("istream base64 decoder %u", i+1)); encode_test(tests[i].input, tests[i].chars_per_line, tests[i].crlf, tests[i].output); test_end(); } test_begin("istream base64 encoder seek"); test_istream_base64_encoder_seek(hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ="); test_end(); } dovecot-2.2.33.2/src/lib/test-strfuncs.c0000644000175000017500000001744613165463624014700 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "array.h" static void test_p_strarray_dup(void) { const char *input[][3] = { { NULL }, { "a", NULL }, { "foobar", NULL }, { "a", "foo", NULL } }; const char **ret; unsigned int i, j; test_begin("p_strarray_dup"); for (i = 0; i < N_ELEMENTS(input); i++) { ret = p_strarray_dup(default_pool, input[i]); for (j = 0; input[i][j] != NULL; j++) { test_assert(strcmp(input[i][j], ret[j]) == 0); test_assert(input[i][j] != ret[j]); } test_assert(ret[j] == NULL); i_free(ret); } test_end(); } static void test_t_strsplit(void) { const char *const *args; test_begin("t_strsplit"); /* empty string -> empty array. was this perhaps a mistake for the API to do this originally?.. can't really change now anyway. */ args = t_strsplit("", "\n"); test_assert(args[0] == NULL); /* two empty strings */ args = t_strsplit("\n", "\n"); test_assert(args[0][0] == '\0'); test_assert(args[1][0] == '\0'); test_assert(args[2] == NULL); test_end(); } static void strsplit_verify(const char *str) { T_BEGIN { const char **s1, **s2; unsigned int i; s1 = t_strsplit_tab(str); s2 = t_strsplit(str, "\t"); for (i = 0; s1[i] != NULL; i++) test_assert(null_strcmp(s1[i], s2[i]) == 0); test_assert(s2[i] == NULL); } T_END; } static void test_t_strsplit_tab(void) { char buf[4096]; unsigned int i, j, max; test_begin("t_strsplit_tab"); strsplit_verify(""); strsplit_verify("\t"); strsplit_verify("\t\t"); strsplit_verify("foo"); strsplit_verify("foo\tbar"); strsplit_verify("foo\tbar\tbaz"); strsplit_verify("foo\t\tbaz"); buf[sizeof(buf)-1] = '\0'; for (i = 0; i < sizeof(buf)-1; i++) buf[i] = '\t'; strsplit_verify(buf); for (j = 0; j < 256; j++) { memset(buf, '\t', j); buf[j+1] = '\0'; strsplit_verify(buf); } for (j = 0; j < 100; j++) { max = (rand() % sizeof(buf)) + 1; buf[--max] = '\0'; for (i = 0; i < max; i++) { if (rand() % 10 == 0) buf[i] = '\t'; else buf[i] = 'x'; } strsplit_verify(buf); } test_end(); } static void test_t_strsplit_spaces(void) { const char *const *args; test_begin("t_strsplit_spaces"); /* empty strings */ args = t_strsplit_spaces("", "\n"); test_assert(args[0] == NULL); args = t_strsplit_spaces("\n", "\n"); test_assert(args[0] == NULL); args = t_strsplit_spaces("\n\n", "\n"); test_assert(args[0] == NULL); /* multiple separators */ args = t_strsplit_spaces(" , , ,str1 , ,,, , str2 , ", " ,"); test_assert(strcmp(args[0], "str1") == 0); test_assert(strcmp(args[1], "str2") == 0); test_assert(args[2] == NULL); test_end(); } static void test_t_str_replace(void) { test_begin("t_str_replace"); test_assert(strcmp(t_str_replace("foo", 'a', 'b'), "foo") == 0); test_assert(strcmp(t_str_replace("fooa", 'a', 'b'), "foob") == 0); test_assert(strcmp(t_str_replace("afooa", 'a', 'b'), "bfoob") == 0); test_assert(strcmp(t_str_replace("", 'a', 'b'), "") == 0); test_assert(strcmp(t_str_replace("a", 'a', 'b'), "b") == 0); test_assert(strcmp(t_str_replace("aaa", 'a', 'b'), "bbb") == 0); test_assert(strcmp(t_str_replace("bbb", 'a', 'b'), "bbb") == 0); test_assert(strcmp(t_str_replace("aba", 'a', 'b'), "bbb") == 0); test_end(); } #if 0 static void test_t_str_trim(void) { test_begin("t_str_trim"); test_assert(strcmp(t_str_trim("foo", ""), "foo") == 0); test_assert(strcmp(t_str_trim("foo", " "), "foo") == 0); test_assert(strcmp(t_str_trim("foo ", " "), "foo") == 0); test_assert(strcmp(t_str_trim(" foo", " "), "foo") == 0); test_assert(strcmp(t_str_trim(" foo ", " "), "foo") == 0); test_assert(strcmp(t_str_trim("\tfoo ", "\t "), "foo") == 0); test_assert(strcmp(t_str_trim(" \tfoo\t ", "\t "), "foo") == 0); test_assert(strcmp(t_str_trim("\r \tfoo\t \r", "\t \r"), "foo") == 0); test_assert(strcmp(t_str_trim("\r \tfoo foo\t \r", "\t \r"), "foo foo") == 0); test_assert(strcmp(t_str_trim("\tfoo\tfoo\t", "\t \r"), "foo\tfoo") == 0); test_end(); } #endif static void test_t_str_ltrim(void) { test_begin("t_str_ltrim"); test_assert(strcmp(t_str_ltrim("foo", ""), "foo") == 0); test_assert(strcmp(t_str_ltrim("foo", " "), "foo") == 0); test_assert(strcmp(t_str_ltrim("foo ", " "), "foo ") == 0); test_assert(strcmp(t_str_ltrim(" foo", " "), "foo") == 0); test_assert(strcmp(t_str_ltrim(" foo ", " "), "foo ") == 0); test_assert(strcmp(t_str_ltrim("\tfoo ", "\t "), "foo ") == 0); test_assert(strcmp(t_str_ltrim(" \tfoo\t ", "\t "), "foo\t ") == 0); test_assert(strcmp(t_str_ltrim("\r \tfoo\t \r", "\t \r"), "foo\t \r") == 0); test_assert(strcmp(t_str_ltrim("\r \tfoo foo\t \r", "\t \r"), "foo foo\t \r") == 0); test_assert(strcmp(t_str_ltrim("\tfoo\tfoo\t", "\t \r"), "foo\tfoo\t") == 0); test_end(); } static void test_t_str_rtrim(void) { test_begin("t_str_rtrim"); test_assert(strcmp(t_str_rtrim("foo", ""), "foo") == 0); test_assert(strcmp(t_str_rtrim("foo", " "), "foo") == 0); test_assert(strcmp(t_str_rtrim("foo ", " "), "foo") == 0); test_assert(strcmp(t_str_rtrim(" foo", " "), " foo") == 0); test_assert(strcmp(t_str_rtrim(" foo ", " "), " foo") == 0); test_assert(strcmp(t_str_rtrim("\tfoo ", "\t "), "\tfoo") == 0); test_assert(strcmp(t_str_rtrim(" \tfoo\t ", "\t "), " \tfoo") == 0); test_assert(strcmp(t_str_rtrim("\r \tfoo\t \r", "\t \r"), "\r \tfoo") == 0); test_assert(strcmp(t_str_rtrim("\r \tfoo foo\t \r", "\t \r"), "\r \tfoo foo") == 0); test_assert(strcmp(t_str_rtrim("\tfoo\tfoo\t", "\t \r"), "\tfoo\tfoo") == 0); test_end(); } static const char *const test_strarray_input[] = { "", "hello", "world", "", "yay", "", NULL }; static struct { const char *separator; const char *output; } test_strarray_outputs[] = { { "", "helloworldyay" }, /* FIXME: v2.3 - test_output should have separator in the beginning */ { " ", "hello world yay " }, { "!-?", "hello!-?world!-?!-?yay!-?" } }; static void test_t_strarray_join(void) { const char *null = NULL; unsigned int i; test_begin("t_strarray_join()"); /* empty array -> empty string */ test_assert(strcmp(t_strarray_join(&null, " "), "") == 0); for (i = 0; i < N_ELEMENTS(test_strarray_outputs); i++) { test_assert_idx(strcmp(t_strarray_join(test_strarray_input, test_strarray_outputs[i].separator), test_strarray_outputs[i].output) == 0, i); } test_end(); } static void test_p_array_const_string_join(void) { ARRAY_TYPE(const_string) arr; unsigned int i; char *res; test_begin("p_array_const_string_join()"); i_array_init(&arr, 2); /* empty array -> empty string */ test_assert(strcmp(t_array_const_string_join(&arr, " "), "") == 0); array_append(&arr, test_strarray_input, str_array_length(test_strarray_input)); for (i = 0; i < N_ELEMENTS(test_strarray_outputs); i++) { res = p_array_const_string_join(default_pool, &arr, test_strarray_outputs[i].separator); test_assert_idx(strcmp(res, test_strarray_outputs[i].output) == 0, i); i_free(res); } array_free(&arr); test_end(); } static void test_mem_equals_timing_safe(void) { const struct { const char *a, *b; } tests[] = { { "", "" }, { "a", "a" }, { "b", "a" }, { "ab", "ab" }, { "ab", "ba" }, { "ab", "bc" }, }; test_begin("mem_equals_timing_safe()"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { size_t len = strlen(tests[i].a); i_assert(len == strlen(tests[i].b)); test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) == mem_equals_timing_safe(tests[i].a, tests[i].b, len)); test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) == mem_equals_timing_safe(tests[i].b, tests[i].a, len)); } test_end(); } void test_strfuncs(void) { test_p_strarray_dup(); test_t_strsplit(); test_t_strsplit_tab(); test_t_strsplit_spaces(); test_t_str_replace(); /*test_t_str_trim();*/ test_t_str_ltrim(); test_t_str_rtrim(); test_t_strarray_join(); test_p_array_const_string_join(); test_mem_equals_timing_safe(); } dovecot-2.2.33.2/src/lib/var-expand.h0000644000175000017500000000266213165463624014120 00000000000000#ifndef VAR_EXPAND_H #define VAR_EXPAND_H struct var_expand_table { char key; const char *value; const char *long_key; }; struct var_expand_func_table { const char *key; /* %{key:data}, or data is "" with %{key}, */ const char *(*func)(const char *data, void *context); }; /* Expand % variables in src and append the string in dest. table must end with key = 0. */ void var_expand(string_t *dest, const char *str, const struct var_expand_table *table); /* Like var_expand(), but support also callback functions for variable expansion. */ void var_expand_with_funcs(string_t *dest, const char *str, const struct var_expand_table *table, const struct var_expand_func_table *func_table, void *func_context) ATTR_NULL(3, 4, 5); /* Returns the actual key character for given string, ie. skip any modifiers that are before it. The string should be the data after the '%' character. For %{long_variable}, '{' is returned. */ char var_get_key(const char *str) ATTR_PURE; /* Similar to var_get_key(), but works for long keys as well. For single char keys size=1, while for e.g. %{key} size=3 and idx points to 'k'. */ void var_get_key_range(const char *str, unsigned int *idx_r, unsigned int *size_r); /* Returns TRUE if key variable is used in the string. If key is '\0', it's ignored. If long_key is NULL, it's ignored. */ bool var_has_key(const char *str, char key, const char *long_key) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/ioloop-notify-none.c0000644000175000017500000000127613123174404015577 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop-private.h" #ifdef IOLOOP_NOTIFY_NONE #undef io_add_notify enum io_notify_result io_add_notify(const char *path ATTR_UNUSED, const char *source_filename ATTR_UNUSED, unsigned int source_linenum ATTR_UNUSED, io_callback_t *callback ATTR_UNUSED, void *context ATTR_UNUSED, struct io **io_r) { *io_r = NULL; return IO_NOTIFY_NOSUPPORT; } void io_loop_notify_remove(struct io *io ATTR_UNUSED) { } void io_loop_notify_handler_deinit(struct ioloop *ioloop ATTR_UNUSED) { } int io_loop_extract_notify_fd(struct ioloop *ioloop ATTR_UNUSED) { return -1; } #endif dovecot-2.2.33.2/src/lib/test-ostream-multiplex.c0000644000175000017500000000712013167162046016504 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "randgen.h" #include "ioloop.h" #include "fd-set-nonblock.h" #include "str.h" #include "istream.h" #include "ostream-private.h" #include "ostream-multiplex.h" #include "ostream.h" #include #include "hex-binary.h" static void test_ostream_multiplex_simple(void) { test_begin("ostream multiplex (simple)"); const unsigned char expected[] = { '\x00','\x00','\x00','\x00','\x05','\x68','\x65', '\x6c','\x6c','\x6f','\x01','\x00','\x00','\x00', '\x05','\x77','\x6f','\x72','\x6c','\x64' }; buffer_t *result = t_str_new(64); struct ostream *os = test_ostream_create(result); struct ostream *os2 = o_stream_create_multiplex(os, (size_t)-1); struct ostream *os3 = o_stream_multiplex_add_channel(os2, 1); test_assert(o_stream_send_str(os2, "hello") == 5); test_assert(o_stream_send_str(os3, "world") == 5); o_stream_unref(&os3); o_stream_unref(&os2); o_stream_unref(&os); test_assert(sizeof(expected) == result->used); test_assert(memcmp(result->data, expected, I_MIN(sizeof(expected), result->used)) == 0); test_end(); } static unsigned int channel_counter[2] = {0, 0}; static struct ostream *chan0, *chan1; static const char *msgs[] = { "", "a", "bb", "ccc", "dddd", "eeeee", "ffffff" }; static void test_ostream_multiplex_stream_read(struct istream *is) { uint8_t cid; const unsigned char *data; size_t siz,dlen=0,pos=0; if (i_stream_read_more(is, &data, &siz)>0) { /* parse stream */ for(;pos 0) { if (dlen < N_ELEMENTS(msgs)) { test_assert_idx(memcmp(&data[pos], msgs[dlen], dlen)==0, channel_counter[data[0] % 2]); } channel_counter[data[0] % 2]++; pos += dlen; dlen = 0; } else if (dlen == 0) { cid = data[pos] % 2; test_assert_idx(data[pos] < 2, channel_counter[cid]); pos++; dlen = be32_to_cpu_unaligned(&data[pos]); pos += 4; test_assert(dlen > 0 && dlen < N_ELEMENTS(msgs)); } } i_stream_skip(is, siz); } if (channel_counter[0] > 100 && channel_counter[1] > 100) io_loop_stop(current_ioloop); } static void test_ostream_multiplex_stream_write(struct ostream *channel ATTR_UNUSED) { size_t rounds = 1 + rand() % 10; for(size_t i = 0; i < rounds; i++) { if ((rand() % 2) != 0) o_stream_nsend_str(chan1, msgs[rand() % N_ELEMENTS(msgs)]); else o_stream_nsend_str(chan0, msgs[rand() % N_ELEMENTS(msgs)]); } } static void test_ostream_multiplex_stream(void) { test_begin("ostream multiplex (stream)"); struct ioloop *ioloop = io_loop_create(); io_loop_set_current(ioloop); int fds[2]; test_assert(pipe(fds) == 0); fd_set_nonblock(fds[0], TRUE); fd_set_nonblock(fds[1], TRUE); struct ostream *os = o_stream_create_fd(fds[1], (size_t)-1, FALSE); struct istream *is = i_stream_create_fd(fds[0], (size_t)-1, FALSE); chan0 = o_stream_create_multiplex(os, (size_t)-1); chan1 = o_stream_multiplex_add_channel(chan0, 1); struct io *io0 = io_add_istream(is, test_ostream_multiplex_stream_read, is); struct io *io1 = io_add(fds[1], IO_WRITE, test_ostream_multiplex_stream_write, os); io_loop_run(current_ioloop); io_remove(&io0); io_remove(&io1); test_assert(o_stream_nfinish(chan1) == 0); o_stream_unref(&chan1); test_assert(o_stream_nfinish(chan0) == 0); o_stream_unref(&chan0); i_stream_unref(&is); o_stream_unref(&os); io_loop_destroy(&ioloop); i_close_fd(&fds[0]); i_close_fd(&fds[1]); test_end(); } void test_ostream_multiplex(void) { random_init(); test_ostream_multiplex_simple(); test_ostream_multiplex_stream(); random_deinit(); } dovecot-2.2.33.2/src/lib/file-copy.c0000644000175000017500000000462713165463624013740 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "ostream.h" #include "file-copy.h" #include #include #include #include static int file_copy_to_tmp(const char *srcpath, const char *tmppath, bool try_hardlink) { struct istream *input; struct ostream *output; struct stat st; mode_t old_umask; int fd_in, fd_out; off_t ret; if (try_hardlink) { /* see if hardlinking works */ if (link(srcpath, tmppath) == 0) return 1; if (errno == EEXIST) { if (i_unlink_if_exists(tmppath) < 0) return -1; if (link(srcpath, tmppath) == 0) return 1; } if (errno == ENOENT) return 0; if (!ECANTLINK(errno)) { i_error("link(%s, %s) failed: %m", srcpath, tmppath); return -1; } /* fallback to manual copying */ } fd_in = open(srcpath, O_RDONLY); if (fd_in == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", srcpath); return -1; } if (fstat(fd_in, &st) < 0) { i_error("fstat(%s) failed: %m", srcpath); i_close_fd(&fd_in); return -1; } old_umask = umask(0); fd_out = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); umask(old_umask); if (fd_out == -1) { i_error("open(%s, O_CREAT) failed: %m", tmppath); i_close_fd(&fd_in); return -1; } /* try to change the group, don't really care if it fails */ if (fchown(fd_out, (uid_t)-1, st.st_gid) < 0 && errno != EPERM) i_error("fchown(%s) failed: %m", tmppath); input = i_stream_create_fd(fd_in, IO_BLOCK_SIZE, FALSE); output = o_stream_create_fd_file(fd_out, 0, FALSE); while ((ret = o_stream_send_istream(output, input)) > 0) ; if (ret < 0) i_error("write(%s) failed: %m", tmppath); i_stream_destroy(&input); o_stream_destroy(&output); if (close(fd_in) < 0) { i_error("close(%s) failed: %m", srcpath); ret = -1; } if (close(fd_out) < 0) { i_error("close(%s) failed: %m", tmppath); ret = -1; } return ret < 0 ? -1 : 1; } int file_copy(const char *srcpath, const char *destpath, bool try_hardlink) { int ret; T_BEGIN { const char *tmppath; tmppath = t_strconcat(destpath, ".tmp", NULL); ret = file_copy_to_tmp(srcpath, tmppath, try_hardlink); if (ret > 0) { if (rename(tmppath, destpath) < 0) { i_error("rename(%s, %s) failed: %m", tmppath, destpath); ret = -1; } } if (ret < 0) i_unlink(tmppath); } T_END; return ret; } dovecot-2.2.33.2/src/lib/test-hash-format.c0000644000175000017500000000272413165463624015233 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "hash-format.h" struct hash_format_test { const char *input; const char *output; }; void test_hash_format(void) { static const char *fail_input[] = { "%", "%A{sha1}", "%{sha1", "%{sha1:8", "%{sha1:8a}", "%{sha1:0}", "%{sha1:168}", NULL }; static struct hash_format_test tests[] = { { "%{sha1}", "8843d7f92416211de9ebb963ff4ce28125932878" }, { "*%{sha1}*", "*8843d7f92416211de9ebb963ff4ce28125932878*" }, { "*%{sha1:8}*", "*88*" }, { "%{sha1:152}", "8843d7f92416211de9ebb963ff4ce281259328" }, { "%X{size}", "6" }, { "%{sha256:80}", "c3ab8ff13720e8ad9047" }, { "%{sha512:80}", "0a50261ebd1a390fed2b" }, { "%{md4}", "547aefd231dcbaac398625718336f143" }, { "%{md5}", "3858f62230ac3c915f300c664312c63f" }, { "%{sha256:80}-%X{size}", "c3ab8ff13720e8ad9047-6" } }; struct hash_format *format; string_t *str = t_str_new(128); const char *error; unsigned int i; test_begin("hash_format"); for (i = 0; fail_input[i] != NULL; i++) test_assert(hash_format_init(fail_input[i], &format, &error) < 0); for (i = 0; i < N_ELEMENTS(tests); i++) { test_assert(hash_format_init(tests[i].input, &format, &error) == 0); hash_format_loop(format, "foo", 3); hash_format_loop(format, "bar", 3); str_truncate(str, 0); hash_format_deinit(&format, str); test_assert(strcmp(str_c(str), tests[i].output) == 0); } test_end(); } dovecot-2.2.33.2/src/lib/test-mempool.c0000644000175000017500000001263313123174404014457 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #if SIZEOF_VOID_P == 8 typedef char uint32max_array_t[4294967295]; #else typedef char uint32max_array_t[65535]; #endif extern struct pool test_pool; static void test_mempool_overflow(void) { test_begin("mempool overflow"); #if SIZEOF_VOID_P == 8 uint32max_array_t *m1 = p_new(&test_pool, uint32max_array_t, 4294967297ULL); test_assert(m1 == POINTER_CAST(18446744073709551615ULL)); char *m2 = p_new(&test_pool, char, 18446744073709551615ULL); test_assert(m2 == POINTER_CAST(18446744073709551615ULL)); uint32_t *m3 = p_new(&test_pool, uint32_t, 4611686018427387903ULL); test_assert(m3 == POINTER_CAST(18446744073709551612ULL)); /* grow */ test_assert(p_realloc_type(&test_pool, m1, uint32max_array_t, 4294967296ULL, 4294967297ULL) == POINTER_CAST(18446744073709551615ULL)); test_assert(p_realloc_type(&test_pool, m2, char, 18446744073709551614ULL, 18446744073709551615ULL) == POINTER_CAST(18446744073709551615ULL)); test_assert(p_realloc_type(&test_pool, m3, uint32_t, 4611686018427387902ULL, 4611686018427387903ULL) == POINTER_CAST(18446744073709551612ULL)); /* shrink */ test_assert(p_realloc_type(&test_pool, m1, uint32max_array_t, 4294967297ULL, 4294967296ULL) == POINTER_CAST(18446744069414584320ULL)); test_assert(p_realloc_type(&test_pool, m2, char, 18446744073709551615ULL, 18446744073709551614ULL) == POINTER_CAST(18446744073709551614ULL)); test_assert(p_realloc_type(&test_pool, m3, uint32_t, 4611686018427387903ULL, 4611686018427387902ULL) == POINTER_CAST(18446744073709551608ULL)); #elif SIZEOF_VOID_P == 4 uint32max_array_t *m1 = p_new(&test_pool, uint32max_array_t, 65537); test_assert(m1 == POINTER_CAST(4294967295U)); char *m2 = p_new(&test_pool, char, 4294967295U); test_assert(m2 == POINTER_CAST(4294967295U)); uint32_t *m3 = p_new(&test_pool, uint32_t, 1073741823U); test_assert(m3 == POINTER_CAST(4294967292U)); /* grow */ test_assert(p_realloc_type(&test_pool, m1, uint32max_array_t, 65536, 65537) == POINTER_CAST(4294967295U)); test_assert(p_realloc_type(&test_pool, m2, char, 4294967294U, 4294967295U) == POINTER_CAST(4294967295U)); test_assert(p_realloc_type(&test_pool, m3, uint32_t, 1073741822U, 1073741823U) == POINTER_CAST(4294967292U)); /* shrink */ test_assert(p_realloc_type(&test_pool, m1, uint32max_array_t, 65537, 65536) == POINTER_CAST(4294901760U)); test_assert(p_realloc_type(&test_pool, m2, char, 4294967295U, 4294967294U) == POINTER_CAST(4294967294U)); test_assert(p_realloc_type(&test_pool, m3, uint32_t, 1073741823U, 1073741822U) == POINTER_CAST(4294967288U)); #else # error unsupported pointer size #endif test_end(); } enum fatal_test_state fatal_mempool(unsigned int stage) { static uint32max_array_t *m1; static uint32_t *m2; #if SIZEOF_VOID_P == 8 switch(stage) { case 0: test_begin("fatal mempool overflow"); m1 = p_new(&test_pool, uint32max_array_t, 4294967298ULL); return FATAL_TEST_FAILURE; case 1: m2 = p_new(&test_pool, uint32_t, 4611686018427387904ULL); return FATAL_TEST_FAILURE; case 2: /* grow */ m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, 4294967297ULL, 4294967298ULL); return FATAL_TEST_FAILURE; case 3: m2 = p_realloc_type(&test_pool, m2, uint32_t, 4611686018427387903ULL, 4611686018427387904ULL); return FATAL_TEST_FAILURE; case 4: /* shrink */ m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, 4294967298ULL, 4294967297ULL); return FATAL_TEST_FAILURE; case 5: m2 = p_realloc_type(&test_pool, m2, uint32_t, 4611686018427387904ULL, 4611686018427387903ULL); return FATAL_TEST_FAILURE; } #elif SIZEOF_VOID_P == 4 switch(stage) { case 0: test_begin("fatal mempool overflow"); m1 = p_new(&test_pool, uint32max_array_t, 65538); return FATAL_TEST_FAILURE; case 1: m2 = p_new(&test_pool, uint32_t, 1073741824U); return FATAL_TEST_FAILURE; case 2: /* grow */ m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, 65537, 65538); return FATAL_TEST_FAILURE; case 3: m2 = p_realloc_type(&test_pool, m2, uint32_t, 1073741823U, 1073741824U); return FATAL_TEST_FAILURE; case 4: /* shrink */ m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, 65538, 65537); return FATAL_TEST_FAILURE; case 5: m2 = p_realloc_type(&test_pool, m2, uint32_t, 1073741824U, 1073741823U); return FATAL_TEST_FAILURE; } #else # error unsupported pointer size #endif test_end(); return FATAL_TEST_FINISHED; } static const char *pool_test_get_name(pool_t pool ATTR_UNUSED) { return "test"; } static void pool_test_ref(pool_t pool ATTR_UNUSED) { } static void pool_test_unref(pool_t *pool) { *pool = NULL; } static void *pool_test_malloc(pool_t pool ATTR_UNUSED, size_t size) { return POINTER_CAST(size); } static void pool_test_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED) { } static void *pool_test_realloc(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED, size_t old_size ATTR_UNUSED, size_t new_size) { return POINTER_CAST(new_size); } static void pool_test_clear(pool_t pool ATTR_UNUSED) { } static size_t pool_test_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) { return 12345; } static const struct pool_vfuncs test_pool_vfuncs = { pool_test_get_name, pool_test_ref, pool_test_unref, pool_test_malloc, pool_test_free, pool_test_realloc, pool_test_clear, pool_test_get_max_easy_alloc_size }; struct pool test_pool = { .v = &test_pool_vfuncs, .alloconly_pool = TRUE, .datastack_pool = FALSE, }; void test_mempool(void) { test_mempool_overflow(); } dovecot-2.2.33.2/src/lib/test-pkcs5.c0000644000175000017500000000374213165463624014050 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "buffer.h" #include "hash-method.h" #include "pkcs5.h" struct test_vector { const char *prf; unsigned char *p; size_t pLen; unsigned char *s; size_t sLen; unsigned int i; unsigned char *dk; size_t dkLen; }; #define TEST_BUF(x) (unsigned char*)x, sizeof(x)-1 /* RFC 6070 test vectors */ static const struct test_vector test_vectors_v2[] = { { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 1, TEST_BUF("\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6") }, { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 2, TEST_BUF("\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57") }, { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 4096, TEST_BUF("\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1") }, /* enable the next test only when you need it, it takes quite long time */ /* { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 16777216, TEST_BUF("\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84") }, */ { "sha1", TEST_BUF("passwordPASSWORDpassword"), TEST_BUF("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, TEST_BUF("\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38") }, { "sha1", TEST_BUF("pass\0word"), TEST_BUF("sa\0lt"), 4096, TEST_BUF("\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3") } }; void test_pkcs5_pbkdf2(void) { buffer_t *res = buffer_create_dynamic(default_pool, 25); test_begin("pkcs5_pbkdf2"); for(size_t i = 0; i < N_ELEMENTS(test_vectors_v2); i++) { buffer_reset(res); const struct test_vector *vec = &(test_vectors_v2[i]); pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup(vec->prf), vec->p, vec->pLen, vec->s, vec->sLen, vec->i, vec->dkLen, res); test_assert_idx(memcmp(res->data, vec->dk, vec->dkLen) == 0, i); } buffer_free(&res); test_end(); } dovecot-2.2.33.2/src/lib/env-util.h0000644000175000017500000000202213123174404013571 00000000000000#ifndef ENV_UTIL_H #define ENV_UTIL_H /* Add new environment variable. Wrapper to putenv(). Note that calls to this function allocates memory which isn't free'd until env_clean() is called. */ void env_put(const char *env); /* Remove a single environment. */ void env_remove(const char *name); /* Clear all environment variables. */ void env_clean(void); /* Clear all environment variables except what's listed in preserve_envs[] */ void env_clean_except(const char *const preserve_envs[]); /* Save a copy of the current environment. */ struct env_backup *env_backup_save(void); /* Clear the current environment and restore the backup. */ void env_backup_restore(struct env_backup *env); /* Free the memory used by environment backup. */ void env_backup_free(struct env_backup **env); /* Returns the value of "&environ". This is more portable than using it directly. */ char ***env_get_environ_p(void); /* Free all memory used by env_put() function. Environment must not be accessed afterwards. */ void env_deinit(void); #endif dovecot-2.2.33.2/src/lib/utc-offset.c0000644000175000017500000000151213123174404014103 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "utc-offset.h" #include int utc_offset(struct tm *tm, time_t t ATTR_UNUSED) { #ifdef HAVE_TM_GMTOFF return (int) (tm->tm_gmtoff/60); #else struct tm ltm, gtm; int offset; /* gmtime() overwrites tm, so we need to copy it elsewhere */ ltm = *tm; tm = gmtime(&t); gtm = *tm; /* max offset of 24 hours */ if ((ltm.tm_yday < gtm.tm_yday && ltm.tm_year == gtm.tm_year) || ltm.tm_year < gtm.tm_year) offset = -24 * 60; else if ((ltm.tm_yday > gtm.tm_yday && ltm.tm_year == gtm.tm_year) || ltm.tm_year > gtm.tm_year) offset = 24 * 60; else offset = 0; offset += (ltm.tm_hour - gtm.tm_hour) * 60; offset += (ltm.tm_min - gtm.tm_min); /* restore overwritten tm */ *tm = ltm; return offset; #endif } dovecot-2.2.33.2/src/lib/ostream-null.h0000644000175000017500000000023613123174404014455 00000000000000#ifndef OSTREAM_NULL_H #define OSTREAM_NULL_H /* Create an output stream that ignores all the writes. */ struct ostream *o_stream_create_null(void); #endif dovecot-2.2.33.2/src/lib/array.c0000644000175000017500000001031713165463624013160 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" void *array_idx_modifiable_i(struct array *array, unsigned int idx) { return buffer_get_space_unsafe(array->buffer, idx * array->element_size, array->element_size); } void array_idx_set_i(struct array *array, unsigned int idx, const void *data) { buffer_write(array->buffer, idx * array->element_size, data, array->element_size); } void array_idx_clear_i(struct array *array, unsigned int idx) { buffer_write_zero(array->buffer, idx * array->element_size, array->element_size); } void *array_insert_space_i(struct array *array, unsigned int idx) { void *data; size_t pos; pos = idx * array->element_size; buffer_copy(array->buffer, pos + array->element_size, array->buffer, pos, (size_t)-1); data = buffer_get_space_unsafe(array->buffer, pos, array->element_size); memset(data, 0, array->element_size); return data; } bool array_cmp_i(const struct array *array1, const struct array *array2) { if (!array_is_created_i(array1) || array1->buffer->used == 0) return !array_is_created_i(array2) || array2->buffer->used == 0; if (!array_is_created_i(array2)) return FALSE; return buffer_cmp(array1->buffer, array2->buffer); } bool array_equal_fn_i(const struct array *array1, const struct array *array2, int (*cmp)(const void *, const void*)) { unsigned int count1, count2, i; size_t size; if (!array_is_created_i(array1) || array1->buffer->used == 0) return !array_is_created_i(array2) || array2->buffer->used == 0; if (!array_is_created_i(array2)) return FALSE; count1 = array_count_i(array1); count2 = array_count_i(array2); if (count1 != count2) return FALSE; size = array1->element_size; i_assert(size == array2->element_size); for (i = 0; i < count1; i++) { if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size), CONST_PTR_OFFSET(array2->buffer->data, i * size)) != 0) return FALSE; } return TRUE; } bool array_equal_fn_ctx_i(const struct array *array1, const struct array *array2, int (*cmp)(const void *, const void *, const void *), const void *context) { unsigned int count1, count2, i; size_t size; if (!array_is_created_i(array1) || array1->buffer->used == 0) return !array_is_created_i(array2) || array2->buffer->used == 0; if (!array_is_created_i(array2)) return FALSE; count1 = array_count_i(array1); count2 = array_count_i(array2); if (count1 != count2) return FALSE; size = array1->element_size; i_assert(size == array2->element_size); for (i = 0; i < count1; i++) { if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size), CONST_PTR_OFFSET(array2->buffer->data, i * size), context) != 0) return FALSE; } return TRUE; } void array_reverse_i(struct array *array) { const size_t element_size = array->element_size; unsigned int i, count = array_count_i(array); size_t size; void *data, *tmp; data = buffer_get_modifiable_data(array->buffer, &size); tmp = t_buffer_get(array->element_size); for (i = 0; i+1 < count; i++, count--) { memcpy(tmp, PTR_OFFSET(data, i * element_size), element_size); memcpy(PTR_OFFSET(data, i * element_size), PTR_OFFSET(data, (count-1) * element_size), element_size); memcpy(PTR_OFFSET(data, (count-1) * element_size), tmp, element_size); } } void array_sort_i(struct array *array, int (*cmp)(const void *, const void *)) { unsigned int count; count = array_count_i(array); if (count == 0) return; qsort(buffer_get_modifiable_data(array->buffer, NULL), count, array->element_size, cmp); } void *array_bsearch_i(struct array *array, const void *key, int (*cmp)(const void *, const void *)) { unsigned int count; count = array_count_i(array); return bsearch(key, array->buffer->data, count, array->element_size, cmp); } const void *array_lsearch_i(const struct array *array, const void *key, int (*cmp)(const void *, const void *)) { const void * const data = buffer_get_data(array->buffer, NULL); const size_t s = array->element_size; unsigned int idx; for (idx = 0; idx < array_count_i(array); idx++) { if (cmp(key, CONST_PTR_OFFSET(data, idx * s)) == 0) { return PTR_OFFSET(data, idx * s); } } return NULL; } dovecot-2.2.33.2/src/lib/istream-unix.c0000644000175000017500000000514413165463624014471 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fdpass.h" #include "istream-file-private.h" #include "istream-unix.h" struct unix_istream { struct file_istream fstream; bool next_read_fd; int read_fd; }; static void i_stream_unix_close(struct iostream_private *stream, bool close_parent) { struct unix_istream *ustream = (struct unix_istream *)stream; if (ustream->read_fd != -1) i_close_fd(&ustream->read_fd); i_stream_file_close(stream, close_parent); } static ssize_t i_stream_unix_read(struct istream_private *stream) { struct unix_istream *ustream = (struct unix_istream *)stream; size_t size; ssize_t ret; if (!ustream->next_read_fd) return i_stream_file_read(stream); i_assert(ustream->read_fd == -1); i_assert(ustream->fstream.skip_left == 0); /* not supported here.. */ if (!i_stream_try_alloc(stream, 1, &size)) return -2; do { ret = fd_read(stream->fd, stream->w_buffer + stream->pos, size, &ustream->read_fd); } while (unlikely(ret < 0 && errno == EINTR && stream->istream.blocking)); if (ustream->read_fd != -1) ustream->next_read_fd = FALSE; if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; ustream->fstream.seen_eof = TRUE; return -1; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) { i_assert(!stream->istream.blocking); return 0; } else { i_assert(errno != 0); /* if we get EBADF for a valid fd, it means something's really wrong and we'd better just crash. */ i_assert(errno != EBADF); stream->istream.stream_errno = errno; return -1; } } stream->pos += ret; return ret; } struct istream *i_stream_create_unix(int fd, size_t max_buffer_size) { struct unix_istream *ustream; struct istream *input; i_assert(fd != -1); ustream = i_new(struct unix_istream, 1); ustream->read_fd = -1; input = i_stream_create_file_common(&ustream->fstream, fd, NULL, max_buffer_size, FALSE); input->real_stream->iostream.close = i_stream_unix_close; input->real_stream->read = i_stream_unix_read; return input; } void i_stream_unix_set_read_fd(struct istream *input) { struct unix_istream *ustream = (struct unix_istream *)input->real_stream; ustream->next_read_fd = TRUE; } void i_stream_unix_unset_read_fd(struct istream *input) { struct unix_istream *ustream = (struct unix_istream *)input->real_stream; ustream->next_read_fd = FALSE; } int i_stream_unix_get_read_fd(struct istream *input) { struct unix_istream *ustream = (struct unix_istream *)input->real_stream; int fd; fd = ustream->read_fd; ustream->read_fd = -1; return fd; } dovecot-2.2.33.2/src/lib/sha1.h0000644000175000017500000000577613165463624012720 00000000000000/* $FreeBSD: src/sys/crypto/sha1.h,v 1.8 2002/03/20 05:13:50 alfred Exp $ */ /* $KAME: sha1.h,v 1.5 2000/03/27 04:36:23 sumikawa Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) * based on: http://csrc.nist.gov/fips/fip180-1.txt * implemented by Jun-ichiro itojun Itoh */ #ifndef SHA1_H #define SHA1_H #include "hash-method.h" /* libmysqlclient really should try to keep its internal stuff internal so they won't conflict with the actual programs that are trying to use it. This particular instance has been fixed in 4.1.18 and 5.0.19, but there are others. */ #define sha1_result sha1_result_libmysqlclient_craps_all_over struct sha1_ctxt { union { uint8_t b8[20]; uint32_t b32[5]; } h; union { uint8_t b8[8]; uint64_t b64[1]; } c; union { uint8_t b8[64]; uint32_t b32[16]; } m; uint8_t count; }; extern void sha1_init(struct sha1_ctxt *); extern void sha1_pad(struct sha1_ctxt *); extern void sha1_loop(struct sha1_ctxt *, const void *, size_t); extern void sha1_result(struct sha1_ctxt *, void *); /* compatibilty with other SHA1 source codes */ typedef struct sha1_ctxt SHA1_CTX; #define SHA1Init(x) sha1_init((x)) #define SHA1Update(x, y, z) sha1_loop((x), (y), (z)) #define SHA1Final(x, y) sha1_result((y), (x)) #define SHA1_RESULTLEN (160/8) extern void sha1_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY SHA1_RESULTLEN]); extern const struct hash_method hash_method_sha1; #endif dovecot-2.2.33.2/src/lib/test-utc-mktime.c0000644000175000017500000000332113165463624015073 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "utc-mktime.h" struct test_utc_mktime { int year, month, day, hour, min, sec; time_t out; }; void test_utc_mktime(void) { static const struct test_utc_mktime tests[] = { #ifdef TIME_T_SIGNED { 1969, 12, 31, 23, 59, 59, -1 }, { 1901, 12, 13, 20, 45, 53, -2147483647 }, #endif #if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) { 2106, 2, 7, 6, 28, 15, 4294967295 }, { 2038, 1, 19, 3, 14, 8, 2147483648 }, #endif { 2007, 11, 7, 1, 7, 20, 1194397640 }, { 1970, 1, 1, 0, 0, 0, 0 }, { 2038, 1, 19, 3, 14, 7, 2147483647 }, { INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1 }, { 2106, 2, 7, 6, 28, 15, 4294967295 }, #if TIME_T_MAX_BITS > 32 { 2106, 2, 7, 6, 28, 16, 4294967296 }, #endif /* June leap second */ { 2015, 6, 30, 23, 59, 59, 1435708799 }, { 2015, 6, 30, 23, 59, 60, 1435708799 }, { 2015, 7, 1, 0, 0, 0, 1435708800 }, /* Invalid leap second */ { 2017, 1, 24, 16, 40, 60, 1485276059 }, /* Dec leap second */ { 2016, 12, 31, 23, 59, 59, 1483228799 }, { 2016, 12, 31, 23, 59, 60, 1483228799 }, { 2017, 1, 1, 0, 0, 0, 1483228800 }, }; struct tm tm; unsigned int i; time_t t; bool success; for (i = 0; i < N_ELEMENTS(tests); i++) { const struct test_utc_mktime *test = &tests[i]; i_zero(&tm); tm.tm_year = test->year - 1900; tm.tm_mon = test->month - 1; tm.tm_mday = test->day; tm.tm_hour = test->hour; tm.tm_min = test->min; tm.tm_sec = test->sec; t = utc_mktime(&tm); success = t == test->out; test_out_reason(t_strdup_printf("utc_mktime(%d)", i), success, success ? NULL : t_strdup_printf("%ld != %ld", (long)t, (long)test->out)); } } dovecot-2.2.33.2/src/lib/mempool-system.c0000644000175000017500000000703313165463624015035 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "safe-memset.h" #include "mempool.h" #ifndef HAVE_MALLOC_USABLE_SIZE /* no extra includes needed */ #elif defined (HAVE_MALLOC_NP_H) # include /* FreeBSD */ #elif defined (HAVE_MALLOC_H) # include /* Linux */ #endif #ifdef HAVE_GC_GC_H # include #elif defined (HAVE_GC_H) # include #endif #define CLEAR_CHR 0xde static const char *pool_system_get_name(pool_t pool); static void pool_system_ref(pool_t pool); static void pool_system_unref(pool_t *pool); static void *pool_system_malloc(pool_t pool, size_t size); static void pool_system_free(pool_t pool, void *mem); static void *pool_system_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size); static void pool_system_clear(pool_t pool); static size_t pool_system_get_max_easy_alloc_size(pool_t pool); static struct pool_vfuncs static_system_pool_vfuncs = { pool_system_get_name, pool_system_ref, pool_system_unref, pool_system_malloc, pool_system_free, pool_system_realloc, pool_system_clear, pool_system_get_max_easy_alloc_size }; struct pool static_system_pool = { .v = &static_system_pool_vfuncs, .alloconly_pool = FALSE, .datastack_pool = FALSE }; pool_t system_pool = &static_system_pool; static const char *pool_system_get_name(pool_t pool ATTR_UNUSED) { return "system"; } static void pool_system_ref(pool_t pool ATTR_UNUSED) { } static void pool_system_unref(pool_t *pool ATTR_UNUSED) { } static void *pool_system_malloc(pool_t pool ATTR_UNUSED, size_t size) { void *mem; #ifdef DEBUG int old_errno = errno; #endif if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); #ifndef USE_GC mem = calloc(size, 1); #else mem = GC_malloc(size); #endif if (unlikely(mem == NULL)) { i_fatal_status(FATAL_OUTOFMEM, "pool_system_malloc(%"PRIuSIZE_T "): Out of memory", size); } #ifdef DEBUG /* we rely on errno not changing. it shouldn't. */ i_assert(errno == old_errno); #endif return mem; } static void pool_system_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED) { #ifdef DEBUG int old_errno = errno; #endif #if !defined(USE_GC) && defined(HAVE_MALLOC_USABLE_SIZE) && defined(DEBUG) safe_memset(mem, CLEAR_CHR, malloc_usable_size(mem)); #endif #ifndef USE_GC free(mem); #endif #ifdef DEBUG /* we rely on errno not changing. it shouldn't. */ i_assert(errno == old_errno); #endif } static void *pool_system_realloc(pool_t pool ATTR_UNUSED, void *mem, size_t old_size, size_t new_size) { if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size); if (mem == NULL) { i_assert(old_size == 0); return pool_system_malloc(pool, new_size); } #if !defined(USE_GC) && defined(HAVE_MALLOC_USABLE_SIZE) i_assert(old_size == (size_t)-1 || mem == NULL || old_size <= malloc_usable_size(mem)); #endif #ifndef USE_GC mem = realloc(mem, new_size); #else mem = GC_realloc(mem, new_size); #endif if (unlikely(mem == NULL)) { i_fatal_status(FATAL_OUTOFMEM, "pool_system_realloc(%"PRIuSIZE_T "): Out of memory", new_size); } if (old_size < new_size) { /* clear new data */ memset((char *) mem + old_size, 0, new_size - old_size); } return mem; } static void ATTR_NORETURN pool_system_clear(pool_t pool ATTR_UNUSED) { i_panic("pool_system_clear() must not be called"); } static size_t pool_system_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) { return 0; } dovecot-2.2.33.2/src/lib/hook-build.c0000644000175000017500000000607413123174404014071 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "hook-build.h" struct hook_stack { struct hook_stack *prev, *next; /* Pointer to vfuncs struct. This assumes that a struct containing function pointers equals to an array of function pointers. Not ANSI-C, but should work in all OSes supported by Dovecot. Much easier anyway than doing this work manually.. */ void (**vfuncs)(); /* nonzero in the areas where vfuncs has been changed */ void (**mask)(); }; struct hook_build_context { pool_t pool; /* size of the vfuncs struct */ size_t size; /* number of function pointers in the struct */ unsigned int count; struct hook_stack *head, *tail; }; static void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)()) { struct hook_stack *stack; stack = p_new(ctx->pool, struct hook_stack, 1); stack->vfuncs = vfuncs; stack->mask = p_malloc(ctx->pool, ctx->size); DLLIST2_APPEND(&ctx->head, &ctx->tail, stack); } struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size) { struct hook_build_context *ctx; pool_t pool; i_assert((size % sizeof(void (*)())) == 0); pool = pool_alloconly_create("hook build context", 2048); ctx = p_new(pool, struct hook_build_context, 1); ctx->pool = pool; ctx->size = size; ctx->count = size / sizeof(void (*)()); hook_build_append(ctx, vfuncs); return ctx; } static void hook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack, void (**vlast)()) { unsigned int i; for (i = 0; i < ctx->count; i++) { if (stack->vfuncs[i] != vlast[i]) { i_assert(stack->vfuncs[i] != NULL); stack->mask[i] = stack->vfuncs[i]; } } } static void hook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack) { unsigned int i; i_assert(stack->next != NULL); for (i = 0; i < ctx->count; i++) { if (stack->mask[i] == NULL) { stack->vfuncs[i] = stack->next->vfuncs[i]; stack->mask[i] = stack->next->mask[i]; } } } void hook_build_update(struct hook_build_context *ctx, void *_vlast) { void (**vlast)() = _vlast; struct hook_stack *stack; if (ctx->tail->vfuncs == vlast) { /* no vfuncs overridden */ return; } /* ctx->vfuncs_stack->vfuncs points to the root vfuncs, ctx->vfuncs_stack->next->vfuncs points to the first super function that is being called, and so on. the previous plugin added its vfuncs to the stack tail. vlast contains the previous plugin's super vfuncs, which is where the next plugin should put its own vfuncs. first we'll need to figure out what vfuncs the previous plugin changed and update the mask */ hook_update_mask(ctx, ctx->tail, vlast); /* now go up in the stack as long as the mask isn't set, and update the vfuncs */ for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev) hook_copy_stack(ctx, stack); /* add vlast to stack */ hook_build_append(ctx, vlast); } void hook_build_deinit(struct hook_build_context **_ctx) { struct hook_build_context *ctx = *_ctx; *_ctx = NULL; pool_unref(&ctx->pool); } dovecot-2.2.33.2/src/lib/test-byteorder.c0000644000175000017500000001352113147010712015000 00000000000000/* * Copyright (c) 2016-2017 Josef 'Jeff' Sipek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "test-lib.h" #include "byteorder.h" struct bswap_run { uint64_t in; uint8_t out8; uint16_t out16; uint32_t out32; uint64_t out64; }; static const struct bswap_run runs[] = { { .in = 0, .out8 = 0, .out16 = 0, .out32 = 0, .out64 = 0, }, { .in = 0xffffffffffffffff, .out8 = 0xff, .out16 = 0xffff, .out32 = 0xffffffff, .out64 = 0xffffffffffffffff, }, { .in = 0x123456789abcdef0, .out8 = 0xf0, .out16 = 0xf0de, .out32 = 0xf0debc9a, .out64 = 0xf0debc9a78563412, }, { .in = 0x8080808080808080, .out8 = 0x80, .out16 = 0x8080, .out32 = 0x80808080, .out64 = 0x8080808080808080, }, }; #define CHECK(iter, size, in, exp) \ do { \ uint##size##_t got = bswap_##size(in); \ \ test_begin(t_strdup_printf("byteorder - bswap " \ "(size:%-2u iter:%u)", \ size, iter)); \ test_assert(got == exp); \ test_end(); \ } while (0) static void __test(int iter, const struct bswap_run *run) { CHECK(iter, 8, run->in & 0xff, run->out8); CHECK(iter, 16, run->in & 0xffff, run->out16); CHECK(iter, 32, run->in & 0xffffffff, run->out32); CHECK(iter, 64, run->in, run->out64); } static void test_bswap(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(runs) ; i++) __test(i, &runs[i]); } struct unaligned_run { uint8_t in[8]; /* outputs */ uint8_t be8; uint16_t be16; uint32_t be32; uint64_t be64; uint8_t le8; uint16_t le16; uint32_t le32; uint64_t le64; #if WORDS_BIGENDIAN #define cpu8 be8 #define cpu16 be16 #define cpu32 be32 #define cpu64 be64 #else #define cpu8 le8 #define cpu16 le16 #define cpu32 le32 #define cpu64 le64 #endif }; static const struct unaligned_run uruns[] = { { .in = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, .be8 = 0, .be16 = 0, .be32 = 0, .be64 = 0, .le8 = 0, .le16 = 0, .le32 = 0, .le64 = 0, }, { .in = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, .be8 = 0xff, .be16 = 0xffff, .be32 = 0xffffffff, .be64 = 0xffffffffffffffff, .le8 = 0xff, .le16 = 0xffff, .le32 = 0xffffffff, .le64 = 0xffffffffffffffff, }, { .in = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, }, .be8 = 0x12, .be16 = 0x1234, .be32 = 0x12345678, .be64 = 0x123456789abcdef0, .le8 = 0x12, .le16 = 0x3412, .le32 = 0x78563412, .le64 = 0xf0debc9a78563412, }, { .in = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }, .be8 = 0x80, .be16 = 0x8080, .be32 = 0x80808080, .be64 = 0x8080808080808080, .le8 = 0x80, .le16 = 0x8080, .le32 = 0x80808080, .le64 = 0x8080808080808080, }, }; #define __CHECK_READ(iter, size, pfx, in, fxn, exp) \ do { \ uint##size##_t got = fxn(in); \ \ test_begin(t_strdup_printf("byteorder - unaligned read "\ "(%-3s size:%-2u iter:%u)", \ pfx, size, iter)); \ test_assert(got == exp); \ test_end(); \ } while (0) #define CHECK_READ(iter, size, in, be_exp, le_exp, cpu_exp) \ do { \ __CHECK_READ(iter, size, "BE", in, \ be##size##_to_cpu_unaligned, be_exp); \ __CHECK_READ(iter, size, "LE", in, \ le##size##_to_cpu_unaligned, le_exp); \ __CHECK_READ(iter, size, "CPU", in, \ cpu##size##_to_cpu_unaligned, cpu_exp); \ } while (0) static void __test_read(int iter, const struct unaligned_run *run) { CHECK_READ(iter, 8, run->in, run->be8, run->le8, run->cpu8); CHECK_READ(iter, 16, run->in, run->be16, run->le16, run->cpu16); CHECK_READ(iter, 32, run->in, run->be32, run->le32, run->cpu32); CHECK_READ(iter, 64, run->in, run->be64, run->le64, run->cpu64); } #define __CHECK_WRITE(iter, size, pfx, in, fxn, exp) \ do { \ uint8_t got[size / 8]; \ \ fxn(in, got); \ \ test_begin(t_strdup_printf("byteorder - unaligned write "\ "(%-3s size:%-2u iter:%u)", \ pfx, size, iter)); \ test_assert(memcmp(got, exp, sizeof(got)) == 0); \ test_end(); \ } while (0) #define CHECK_WRITE(iter, size, out, be_in, le_in) \ do { \ __CHECK_WRITE(iter, size, "BE", be_in, \ cpu##size##_to_be_unaligned, out); \ __CHECK_WRITE(iter, size, "LE", le_in, \ cpu##size##_to_le_unaligned, out); \ } while (0) static void __test_write(int iter, const struct unaligned_run *run) { CHECK_WRITE(iter, 8, run->in, run->be8, run->le8); CHECK_WRITE(iter, 16, run->in, run->be16, run->le16); CHECK_WRITE(iter, 32, run->in, run->be32, run->le32); CHECK_WRITE(iter, 64, run->in, run->be64, run->le64); } static void test_unaligned(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(uruns) ; i++) __test_read(i, &uruns[i]); for (i = 0; i < N_ELEMENTS(uruns) ; i++) __test_write(i, &uruns[i]); } void test_byteorder(void) { test_bswap(); test_unaligned(); } dovecot-2.2.33.2/src/lib/read-full.h0000644000175000017500000000043513123174404013707 00000000000000#ifndef READ_FULL_H #define READ_FULL_H /* Read data from file. Returns -1 if error occurred, or 0 if EOF came before everything was read, or 1 if all was ok. */ int read_full(int fd, void *data, size_t size); int pread_full(int fd, void *data, size_t size, off_t offset); #endif dovecot-2.2.33.2/src/lib/eacces-error.c0000644000175000017500000001646013165463624014421 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "abspath.h" #include "ipwd.h" #include "restrict-access.h" #include "eacces-error.h" #include #include static bool is_in_group(gid_t gid) { const gid_t *gids; unsigned int i, count; if (getegid() == gid) return TRUE; gids = restrict_get_groups_list(&count); for (i = 0; i < count; i++) { if (gids[i] == gid) return TRUE; } return FALSE; } static void write_eacces_error(string_t *errmsg, const char *path, int mode) { char c; switch (mode) { case R_OK: c = 'r'; break; case W_OK: c = 'w'; break; case X_OK: c = 'x'; break; default: i_unreached(); } str_printfa(errmsg, " missing +%c perm: %s", c, path); } static int test_manual_access(const char *path, int access_mode, bool write_eacces, string_t *errmsg) { const struct group *group; bool user_not_in_group = FALSE; struct stat st; int mode; if (stat(path, &st) < 0) { str_printfa(errmsg, " stat(%s) failed: %m", path); return -1; } switch (access_mode) { case R_OK: mode = 04; break; case W_OK: mode = 02; break; case X_OK: mode = 01; break; default: i_unreached(); } if (st.st_uid == geteuid()) st.st_mode = (st.st_mode & 0700) >> 6; else if (is_in_group(st.st_gid)) st.st_mode = (st.st_mode & 0070) >> 3; else { if ((((st.st_mode & 0070) >> 3) & mode) != 0) user_not_in_group = TRUE; st.st_mode = (st.st_mode & 0007); } if ((st.st_mode & mode) != 0) return 0; if (write_eacces) write_eacces_error(errmsg, path, access_mode); if (user_not_in_group) { /* group would have had enough permissions, but we don't belong to the group */ str_printfa(errmsg, ", we're not in group %s", dec2str(st.st_gid)); group = getgrgid(st.st_gid); if (group != NULL) str_printfa(errmsg, "(%s)", group->gr_name); } errno = EACCES; return -1; } static int test_access(const char *path, int access_mode, string_t *errmsg) { struct stat st; if (getuid() == geteuid()) { if (access(path, access_mode) == 0) return 0; if (errno == EACCES) { write_eacces_error(errmsg, path, access_mode); if (test_manual_access(path, access_mode, FALSE, errmsg) == 0) { str_append(errmsg, ", UNIX perms appear ok " "(ACL/MAC wrong?)"); } errno = EACCES; } else { str_printfa(errmsg, ", access(%s, %d) failed: %m", path, access_mode); } return -1; } /* access() uses real uid, not effective uid. we'll have to do these checks manually. */ switch (access_mode) { case X_OK: if (stat(t_strconcat(path, "/test", NULL), &st) == 0) return 0; if (errno == ENOENT || errno == ENOTDIR) return 0; if (errno == EACCES) write_eacces_error(errmsg, path, access_mode); else str_printfa(errmsg, ", stat(%s/test) failed: %m", path); return -1; case R_OK: case W_OK: break; default: i_unreached(); } return test_manual_access(path, access_mode, TRUE, errmsg); } static const char * eacces_error_get_full(const char *func, const char *path, bool creating) { const char *prev_path, *dir = NULL, *p; const char *pw_name = NULL, *gr_name = NULL; struct passwd pw; struct group group; string_t *errmsg; struct stat st; int orig_errno, ret, missing_mode = 0; orig_errno = errno; errmsg = t_str_new(256); str_printfa(errmsg, "%s(%s)", func, path); if (*path != '/') { if (t_get_current_dir(&dir) == 0) { str_printfa(errmsg, " in directory %s", dir); path = t_strconcat(dir, "/", path, NULL); } } str_printfa(errmsg, " failed: Permission denied (euid=%s", dec2str(geteuid())); switch (i_getpwuid(geteuid(), &pw)) { case -1: str_append(errmsg, "()"); break; case 0: str_append(errmsg, "()"); break; default: pw_name = t_strdup(pw.pw_name); str_printfa(errmsg, "(%s)", pw_name); break; } str_printfa(errmsg, " egid=%s", dec2str(getegid())); switch (i_getgrgid(getegid(), &group)) { case -1: str_append(errmsg, "()"); break; case 0: str_append(errmsg, "()"); break; default: gr_name = t_strdup(group.gr_name); str_printfa(errmsg, "(%s)", gr_name); break; } prev_path = path; ret = -1; while (strcmp(prev_path, "/") != 0) { if ((p = strrchr(prev_path, '/')) == NULL) break; dir = t_strdup_until(prev_path, p); ret = stat(dir, &st); if (ret == 0) break; if (errno == EACCES && strcmp(dir, "/") != 0) { /* see if we have access to parent directory */ } else if (errno == ENOENT && creating && strcmp(dir, "/") != 0) { /* probably mkdir_parents() failed here, find the first parent directory we couldn't create */ } else { /* some other error, can't handle it */ str_printfa(errmsg, " stat(%s) failed: %m", dir); break; } prev_path = dir; } if (ret == 0) { /* dir is the first parent directory we can stat() */ if (test_access(dir, X_OK, errmsg) < 0) { if (errno == EACCES) missing_mode = 1; } else if (creating && test_access(dir, W_OK, errmsg) < 0) { if (errno == EACCES) missing_mode = 2; } else if (prev_path == path && test_access(path, R_OK, errmsg) < 0) { } else if (!creating && test_access(path, W_OK, errmsg) < 0) { /* this produces a wrong error if the operation didn't actually need write permissions, but we don't know it here.. */ if (errno == EACCES) missing_mode = 4; } else { str_append(errmsg, " UNIX perms appear ok " "(ACL/MAC wrong?)"); } } if (ret < 0) ; else if (st.st_uid != geteuid()) { if (pw_name != NULL && i_getpwuid(st.st_uid, &pw) > 0 && strcmp(pw.pw_name, pw_name) == 0) { str_printfa(errmsg, ", conflicting dir uid=%s(%s)", dec2str(st.st_uid), pw_name); } else { str_printfa(errmsg, ", dir owned by %s:%s mode=0%o", dec2str(st.st_uid), dec2str(st.st_gid), (unsigned int)(st.st_mode & 0777)); } } else if (missing_mode != 0 && (((st.st_mode & 0700) >> 6) & missing_mode) == 0) { str_append(errmsg, ", dir owner missing perms"); } if (ret == 0 && gr_name != NULL && st.st_gid != getegid()) { if (i_getgrgid(st.st_gid, &group) > 0 && strcmp(group.gr_name, gr_name) == 0) { str_printfa(errmsg, ", conflicting dir gid=%s(%s)", dec2str(st.st_gid), gr_name); } } str_append_c(errmsg, ')'); errno = orig_errno; return str_c(errmsg); } const char *eacces_error_get(const char *func, const char *path) { return eacces_error_get_full(func, path, FALSE); } const char *eacces_error_get_creating(const char *func, const char *path) { return eacces_error_get_full(func, path, TRUE); } const char *eperm_error_get_chgrp(const char *func, const char *path, gid_t gid, const char *gid_origin) { string_t *errmsg; const struct group *group; int orig_errno = errno; errmsg = t_str_new(256); str_printfa(errmsg, "%s(%s, group=%s", func, path, dec2str(gid)); group = getgrgid(gid); if (group != NULL) str_printfa(errmsg, "(%s)", group->gr_name); str_printfa(errmsg, ") failed: Operation not permitted (egid=%s", dec2str(getegid())); group = getgrgid(getegid()); if (group != NULL) str_printfa(errmsg, "(%s)", group->gr_name); if (gid_origin != NULL) str_printfa(errmsg, ", group based on %s", gid_origin); str_append(errmsg, " - see http://wiki2.dovecot.org/Errors/ChgrpNoPerm)"); errno = orig_errno; return str_c(errmsg); } dovecot-2.2.33.2/src/lib/base64.c0000644000175000017500000001110513165463624013122 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "buffer.h" static const char b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const unsigned char b64dec[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* 40-47 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; void base64_encode(const void *src, size_t src_size, buffer_t *dest) { const unsigned char *src_c = src; unsigned char tmp[4]; size_t src_pos; for (src_pos = 0; src_pos < src_size; ) { tmp[0] = b64enc[src_c[src_pos] >> 2]; switch (src_size - src_pos) { case 1: tmp[1] = b64enc[(src_c[src_pos] & 0x03) << 4]; tmp[2] = '='; tmp[3] = '='; src_pos++; break; case 2: tmp[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | (src_c[src_pos+1] >> 4)]; tmp[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2)]; tmp[3] = '='; src_pos += 2; break; default: tmp[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | (src_c[src_pos+1] >> 4)]; tmp[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) | ((src_c[src_pos+2] & 0xc0) >> 6)]; tmp[3] = b64enc[src_c[src_pos+2] & 0x3f]; src_pos += 3; break; } buffer_append(dest, tmp, 4); } } #define IS_EMPTY(c) \ ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') int base64_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { const unsigned char *src_c = src; size_t src_pos; unsigned char input[4], output[3]; int ret = 1; for (src_pos = 0; src_pos+3 < src_size; ) { input[0] = b64dec[src_c[src_pos]]; if (input[0] == 0xff) { if (unlikely(!IS_EMPTY(src_c[src_pos]))) { ret = -1; break; } src_pos++; continue; } input[1] = b64dec[src_c[src_pos+1]]; if (unlikely(input[1] == 0xff)) { ret = -1; break; } output[0] = (input[0] << 2) | (input[1] >> 4); input[2] = b64dec[src_c[src_pos+2]]; if (input[2] == 0xff) { if (unlikely(src_c[src_pos+2] != '=' || src_c[src_pos+3] != '=')) { ret = -1; break; } buffer_append(dest, output, 1); ret = 0; src_pos += 4; break; } output[1] = (input[1] << 4) | (input[2] >> 2); input[3] = b64dec[src_c[src_pos+3]]; if (input[3] == 0xff) { if (unlikely(src_c[src_pos+3] != '=')) { ret = -1; break; } buffer_append(dest, output, 2); ret = 0; src_pos += 4; break; } output[2] = ((input[2] << 6) & 0xc0) | input[3]; buffer_append(dest, output, 3); src_pos += 4; } for (; src_pos < src_size; src_pos++) { if (!IS_EMPTY(src_c[src_pos])) break; } if (src_pos_r != NULL) *src_pos_r = src_pos; return ret; } buffer_t *t_base64_decode_str(const char *str) { buffer_t *buf; size_t len = strlen(str); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(len)); (void)base64_decode(str, len, NULL, buf); return buf; } bool base64_is_valid_char(char c) { return b64dec[(uint8_t)c] != 0xff; } dovecot-2.2.33.2/src/lib/istream-seekable.c0000644000175000017500000003261713165463624015266 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "read-full.h" #include "write-full.h" #include "safe-mkstemp.h" #include "istream-private.h" #include "istream-concat.h" #include "istream-seekable.h" #include #define BUF_INITIAL_SIZE (1024*32) struct seekable_istream { struct istream_private istream; char *temp_path; uoff_t write_peak; uoff_t size; int (*fd_callback)(const char **path_r, void *context); void *context; buffer_t *membuf; struct istream **input, *cur_input; struct istream *fd_input; unsigned int cur_idx; int fd; bool free_context; }; static void i_stream_seekable_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct seekable_istream *sstream = (struct seekable_istream *)stream; sstream->fd = -1; if (sstream->fd_input != NULL) i_stream_close(sstream->fd_input); } static void unref_streams(struct seekable_istream *sstream) { unsigned int i; for (i = 0; sstream->input[i] != NULL; i++) i_stream_unref(&sstream->input[i]); } static void i_stream_seekable_destroy(struct iostream_private *stream) { struct seekable_istream *sstream = (struct seekable_istream *)stream; if (sstream->membuf != NULL) buffer_free(&sstream->membuf); if (sstream->fd_input != NULL) i_stream_unref(&sstream->fd_input); unref_streams(sstream); if (sstream->free_context) i_free(sstream->context); i_free(sstream->temp_path); i_free(sstream->input); } static void i_stream_seekable_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct seekable_istream *sstream = (struct seekable_istream *)stream; unsigned int i; sstream->istream.max_buffer_size = max_size; if (sstream->fd_input != NULL) i_stream_set_max_buffer_size(sstream->fd_input, max_size); for (i = 0; sstream->input[i] != NULL; i++) i_stream_set_max_buffer_size(sstream->input[i], max_size); } static int copy_to_temp_file(struct seekable_istream *sstream) { struct istream_private *stream = &sstream->istream; const char *path; const unsigned char *buffer; size_t size; int fd; fd = sstream->fd_callback(&path, sstream->context); if (fd == -1) return -1; /* copy our currently read buffer to it */ if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) { if (!ENOSPACE(errno)) i_error("istream-seekable: write_full(%s) failed: %m", path); i_close_fd(&fd); return -1; } sstream->temp_path = i_strdup(path); sstream->write_peak = sstream->membuf->used; sstream->fd = fd; sstream->fd_input = i_stream_create_fd_autoclose(&fd, I_MAX(stream->pos, sstream->istream.max_buffer_size)); i_stream_set_name(sstream->fd_input, t_strdup_printf( "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream))); /* read back the data we just had in our buffer */ i_stream_seek(sstream->fd_input, stream->istream.v_offset); for (;;) { buffer = i_stream_get_data(sstream->fd_input, &size); if (size >= stream->pos) break; ssize_t ret; if ((ret = i_stream_read(sstream->fd_input)) <= 0) { i_assert(ret != 0); i_assert(ret != -2); i_error("istream-seekable: Couldn't read back " "in-memory input %s: %s", i_stream_get_name(&stream->istream), i_stream_get_error(sstream->fd_input)); i_stream_destroy(&sstream->fd_input); return -1; } } /* Set the max buffer size only after we've already read everything into memory. For example with istream-data it's possible that more data exists in buffer than max_buffer_size. */ i_stream_set_max_buffer_size(sstream->fd_input, sstream->istream.max_buffer_size); stream->buffer = buffer; stream->pos = size; buffer_free(&sstream->membuf); return 0; } static ssize_t read_more(struct seekable_istream *sstream) { size_t size; ssize_t ret; if (sstream->cur_input == NULL) { sstream->istream.istream.eof = TRUE; return -1; } while ((ret = i_stream_read(sstream->cur_input)) == -1) { if (sstream->cur_input->stream_errno != 0) { io_stream_set_error(&sstream->istream.iostream, "read(%s) failed: %s", i_stream_get_name(sstream->cur_input), i_stream_get_error(sstream->cur_input)); sstream->istream.istream.eof = TRUE; sstream->istream.istream.stream_errno = sstream->cur_input->stream_errno; return -1; } /* go to next stream */ sstream->cur_input = sstream->input[sstream->cur_idx++]; if (sstream->cur_input == NULL) { /* last one, EOF */ sstream->size = sstream->istream.istream.v_offset; sstream->istream.istream.eof = TRUE; unref_streams(sstream); return -1; } /* see if stream has pending data */ size = i_stream_get_data_size(sstream->cur_input); if (size != 0) return size; } return ret; } static bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r) { struct istream_private *stream = &sstream->istream; const unsigned char *data; size_t size, pos, offset; i_assert(stream->skip == 0); if (stream->istream.v_offset + stream->pos >= sstream->membuf->used) { /* need to read more */ if (sstream->membuf->used >= stream->max_buffer_size) return FALSE; size = sstream->cur_input == NULL ? 0 : i_stream_get_data_size(sstream->cur_input); if (size == 0) { /* read more to buffer */ *ret_r = read_more(sstream); if (*ret_r == 0 || *ret_r == -1) return TRUE; } /* we should have more now. */ data = i_stream_get_data(sstream->cur_input, &size); i_assert(size > 0); buffer_append(sstream->membuf, data, size); i_stream_skip(sstream->cur_input, size); } offset = stream->istream.v_offset; stream->buffer = CONST_PTR_OFFSET(sstream->membuf->data, offset); pos = sstream->membuf->used - offset; *ret_r = pos - stream->pos; i_assert(*ret_r > 0); stream->pos = pos; return TRUE; } static int i_stream_seekable_write_failed(struct seekable_istream *sstream) { struct istream_private *stream = &sstream->istream; void *data; i_assert(sstream->membuf == NULL); sstream->membuf = buffer_create_dynamic(default_pool, sstream->write_peak); data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak); if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) { i_error("istream-seekable: read(%s) failed: %m", sstream->temp_path); buffer_free(&sstream->membuf); return -1; } i_stream_destroy(&sstream->fd_input); i_close_fd(&sstream->fd); stream->max_buffer_size = (size_t)-1; i_free_and_null(sstream->temp_path); return 0; } static ssize_t i_stream_seekable_read(struct istream_private *stream) { struct seekable_istream *sstream = (struct seekable_istream *)stream; const unsigned char *data; size_t size, pos; ssize_t ret; stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip); stream->pos -= stream->skip; stream->skip = 0; if (sstream->membuf != NULL) { if (read_from_buffer(sstream, &ret)) return ret; /* copy everything to temp file and use it as the stream */ if (copy_to_temp_file(sstream) < 0) { stream->max_buffer_size = (size_t)-1; if (!read_from_buffer(sstream, &ret)) i_unreached(); return ret; } i_assert(sstream->membuf == NULL); } i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak); if (stream->istream.v_offset + stream->pos == sstream->write_peak) { /* need to read more */ ret = read_more(sstream); if (ret == -1 || ret == 0) return ret; /* save to our file */ data = i_stream_get_data(sstream->cur_input, &size); ret = write(sstream->fd, data, size); if (ret <= 0) { if (ret < 0 && !ENOSPACE(errno)) { i_error("istream-seekable: write_full(%s) failed: %m", sstream->temp_path); } if (i_stream_seekable_write_failed(sstream) < 0) return -1; if (!read_from_buffer(sstream, &ret)) i_unreached(); return ret; } i_stream_sync(sstream->fd_input); i_stream_skip(sstream->cur_input, ret); sstream->write_peak += ret; } i_stream_seek(sstream->fd_input, stream->istream.v_offset); ret = i_stream_read(sstream->fd_input); if (ret <= 0) { stream->istream.eof = sstream->fd_input->eof; stream->istream.stream_errno = sstream->fd_input->stream_errno; } else { ret = -2; } stream->buffer = i_stream_get_data(sstream->fd_input, &pos); stream->pos -= stream->skip; stream->skip = 0; ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret; stream->pos = pos; return ret; } static int i_stream_seekable_stat(struct istream_private *stream, bool exact) { struct seekable_istream *sstream = (struct seekable_istream *)stream; const struct stat *st; uoff_t old_offset; ssize_t ret; if (sstream->size != (uoff_t)-1) { /* we've already reached EOF and know the size */ stream->statbuf.st_size = sstream->size; return 0; } /* we want to know the full size of the file, so read until we're finished */ old_offset = stream->istream.v_offset; do { i_stream_skip(&stream->istream, stream->pos - stream->skip); } while ((ret = i_stream_seekable_read(stream)) > 0); if (ret == 0) { i_panic("i_stream_stat() used for non-blocking " "seekable stream %s offset %"PRIuUOFF_T, i_stream_get_name(sstream->cur_input), sstream->cur_input->v_offset); } i_stream_skip(&stream->istream, stream->pos - stream->skip); i_stream_seek(&stream->istream, old_offset); unref_streams(sstream); if (stream->istream.stream_errno != 0) return -1; if (sstream->fd_input != NULL) { /* using a file backed buffer, we can use real fstat() */ if (i_stream_stat(sstream->fd_input, exact, &st) < 0) return -1; stream->statbuf = *st; } else { /* buffer is completely in memory */ i_assert(sstream->membuf != NULL); stream->statbuf.st_size = sstream->membuf->used; } return 0; } static void i_stream_seekable_seek(struct istream_private *stream, uoff_t v_offset, bool mark) { if (v_offset <= stream->istream.v_offset) { /* seeking backwards */ stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; } else { /* we can't skip over data we haven't yet read and written to our buffer/temp file */ i_stream_default_seek_nonseekable(stream, v_offset, mark); } } struct istream * i_streams_merge(struct istream *input[], size_t max_buffer_size, int (*fd_callback)(const char **path_r, void *context), void *context) ATTR_NULL(4) { struct seekable_istream *sstream; const unsigned char *data; unsigned int count; size_t size; bool blocking = TRUE; i_assert(max_buffer_size > 0); /* if any of the streams isn't blocking, set ourself also nonblocking */ for (count = 0; input[count] != NULL; count++) { if (!input[count]->blocking) blocking = FALSE; i_stream_ref(input[count]); } i_assert(count != 0); sstream = i_new(struct seekable_istream, 1); sstream->fd_callback = fd_callback; sstream->context = context; sstream->membuf = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE); sstream->istream.max_buffer_size = max_buffer_size; sstream->fd = -1; sstream->size = (uoff_t)-1; sstream->input = i_new(struct istream *, count + 1); memcpy(sstream->input, input, sizeof(*input) * count); sstream->cur_input = sstream->input[0]; /* initialize our buffer from first stream's pending data */ data = i_stream_get_data(sstream->cur_input, &size); buffer_append(sstream->membuf, data, size); i_stream_skip(sstream->cur_input, size); sstream->istream.iostream.close = i_stream_seekable_close; sstream->istream.iostream.destroy = i_stream_seekable_destroy; sstream->istream.iostream.set_max_buffer_size = i_stream_seekable_set_max_buffer_size; sstream->istream.read = i_stream_seekable_read; sstream->istream.stat = i_stream_seekable_stat; sstream->istream.seek = i_stream_seekable_seek; sstream->istream.istream.readable_fd = FALSE; sstream->istream.istream.blocking = blocking; sstream->istream.istream.seekable = TRUE; return i_stream_create(&sstream->istream, NULL, -1); } static bool inputs_are_seekable(struct istream *input[]) { unsigned int count; for (count = 0; input[count] != NULL; count++) { if (!input[count]->seekable) return FALSE; } return TRUE; } struct istream * i_stream_create_seekable(struct istream *input[], size_t max_buffer_size, int (*fd_callback)(const char **path_r, void *context), void *context) { i_assert(max_buffer_size > 0); /* If all input streams are seekable, use concat istream instead */ if (inputs_are_seekable(input)) return i_stream_create_concat(input); return i_streams_merge(input, max_buffer_size, fd_callback, context); } static int seekable_fd_callback(const char **path_r, void *context) { char *temp_path_prefix = context; string_t *path; int fd; path = t_str_new(128); str_append(path, temp_path_prefix); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("istream-seekable: safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } struct istream * i_stream_create_seekable_path(struct istream *input[], size_t max_buffer_size, const char *temp_path_prefix) { struct seekable_istream *sstream; struct istream *stream; i_assert(temp_path_prefix != NULL); i_assert(max_buffer_size > 0); if (inputs_are_seekable(input)) return i_stream_create_concat(input); stream = i_stream_create_seekable(input, max_buffer_size, seekable_fd_callback, i_strdup(temp_path_prefix)); sstream = (struct seekable_istream *)stream->real_stream; sstream->free_context = TRUE; return stream; } dovecot-2.2.33.2/src/lib/numpack.h0000644000175000017500000000055313123174404013473 00000000000000#ifndef NUMPACK_H #define NUMPACK_H /* Numbers are stored by 7 bits at a time. The highest bit specifies if the number continues to next byte. */ void numpack_encode(buffer_t *buf, uint64_t num); int numpack_decode(const uint8_t **p, const uint8_t *end, uint64_t *num_r); int numpack_decode32(const uint8_t **p, const uint8_t *end, uint32_t *num_r); #endif dovecot-2.2.33.2/src/lib/istream-limit.c0000644000175000017500000000737213165463624014631 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" struct limit_istream { struct istream_private istream; uoff_t v_size; }; static void i_stream_limit_destroy(struct iostream_private *stream) { struct limit_istream *lstream = (struct limit_istream *) stream; uoff_t v_offset; v_offset = lstream->istream.parent_start_offset + lstream->istream.istream.v_offset; if (lstream->istream.parent->seekable || v_offset > lstream->istream.parent->v_offset) { /* get to same position in parent stream */ i_stream_seek(lstream->istream.parent, v_offset); } i_stream_unref(&lstream->istream.parent); } static ssize_t i_stream_limit_read(struct istream_private *stream) { struct limit_istream *lstream = (struct limit_istream *) stream; uoff_t left; ssize_t ret; size_t pos; i_stream_seek(stream->parent, lstream->istream.parent_start_offset + stream->istream.v_offset); if (stream->istream.v_offset + (stream->pos - stream->skip) >= lstream->v_size) { stream->istream.eof = TRUE; return -1; } stream->pos -= stream->skip; stream->skip = 0; stream->buffer = i_stream_get_data(stream->parent, &pos); if (pos > stream->pos) ret = 0; else do { if ((ret = i_stream_read(stream->parent)) == -2) return -2; stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; stream->buffer = i_stream_get_data(stream->parent, &pos); } while (pos <= stream->pos && ret > 0); if (lstream->v_size != (uoff_t)-1) { left = lstream->v_size - stream->istream.v_offset; if (pos >= left) { pos = left; stream->istream.eof = TRUE; } } ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); return ret; } static int i_stream_limit_stat(struct istream_private *stream, bool exact) { struct limit_istream *lstream = (struct limit_istream *) stream; const struct stat *st; if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; if (lstream->v_size != (uoff_t)-1) stream->statbuf.st_size = lstream->v_size; return 0; } static int i_stream_limit_get_size(struct istream_private *stream, bool exact, uoff_t *size_r) { struct limit_istream *lstream = (struct limit_istream *) stream; const struct stat *st; if (lstream->v_size != (uoff_t)-1) { *size_r = lstream->v_size; return 1; } if (i_stream_stat(&stream->istream, exact, &st) < 0) return -1; if (st->st_size == -1) return 0; *size_r = st->st_size; return 1; } struct istream *i_stream_create_limit(struct istream *input, uoff_t v_size) { struct limit_istream *lstream; lstream = i_new(struct limit_istream, 1); lstream->v_size = v_size; lstream->istream.max_buffer_size = input->real_stream->max_buffer_size; lstream->istream.iostream.destroy = i_stream_limit_destroy; lstream->istream.read = i_stream_limit_read; lstream->istream.stat = i_stream_limit_stat; lstream->istream.get_size = i_stream_limit_get_size; lstream->istream.istream.readable_fd = input->readable_fd; lstream->istream.istream.blocking = input->blocking; lstream->istream.istream.seekable = input->seekable; return i_stream_create(&lstream->istream, input, i_stream_get_fd(input)); } struct istream *i_stream_create_range(struct istream *input, uoff_t v_offset, uoff_t v_size) { uoff_t orig_offset = input->v_offset; struct istream *ret; input->v_offset = v_offset; ret = i_stream_create_limit(input, v_size); input->v_offset = orig_offset; return ret; } dovecot-2.2.33.2/src/lib/failures.h0000644000175000017500000001142013123174404013642 00000000000000#ifndef FAILURES_H #define FAILURES_H struct ip_addr; /* Default exit status codes that we could use. */ enum fatal_exit_status { FATAL_LOGOPEN = 80, /* Can't open log file */ FATAL_LOGWRITE = 81, /* Can't write to log file */ FATAL_LOGERROR = 82, /* Internal logging error */ FATAL_OUTOFMEM = 83, /* Out of memory */ FATAL_EXEC = 84, /* exec() failed */ FATAL_DEFAULT = 89 }; enum log_type { LOG_TYPE_DEBUG, LOG_TYPE_INFO, LOG_TYPE_WARNING, LOG_TYPE_ERROR, LOG_TYPE_FATAL, LOG_TYPE_PANIC, LOG_TYPE_COUNT, LOG_TYPE_OPTION }; struct failure_line { pid_t pid; enum log_type log_type; const char *text; }; struct failure_context { enum log_type type; int exit_status; /* for LOG_TYPE_FATAL */ const struct tm *timestamp; /* NULL = use time() + localtime() */ unsigned int timestamp_usecs; }; #define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S " typedef void failure_callback_t(const struct failure_context *ctx, const char *format, va_list args); extern const char *failure_log_type_prefixes[]; extern const char *failure_log_type_names[]; void i_log_type(const struct failure_context *ctx, const char *format, ...) ATTR_FORMAT(2, 3); void i_panic(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD; void i_fatal(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD; void i_error(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_COLD; void i_warning(const char *format, ...) ATTR_FORMAT(1, 2); void i_info(const char *format, ...) ATTR_FORMAT(1, 2); void i_debug(const char *format, ...) ATTR_FORMAT(1, 2); void i_fatal_status(int status, const char *format, ...) ATTR_FORMAT(2, 3) ATTR_NORETURN ATTR_COLD; /* Change failure handlers. */ #ifndef __cplusplus void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN); #else /* Older g++ doesn't like attributes in parameters */ void i_set_fatal_handler(failure_callback_t *callback); #endif void i_set_error_handler(failure_callback_t *callback); void i_set_info_handler(failure_callback_t *callback); void i_set_debug_handler(failure_callback_t *callback); void i_get_failure_handlers(failure_callback_t **fatal_callback_r, failure_callback_t **error_callback_r, failure_callback_t **info_callback_r, failure_callback_t **debug_callback_r); /* Send failures to file. */ void default_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) ATTR_NORETURN ATTR_FORMAT(2, 0); void default_error_handler(const struct failure_context *ctx, const char *format, va_list args) ATTR_FORMAT(2, 0); /* Send failures to syslog() */ void i_syslog_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) ATTR_NORETURN ATTR_FORMAT(2, 0); void i_syslog_error_handler(const struct failure_context *ctx, const char *format, va_list args) ATTR_FORMAT(2, 0); /* Open syslog and set failure/info/debug handlers to use it. */ void i_set_failure_syslog(const char *ident, int options, int facility); /* Send failures to specified log file instead of stderr. */ void i_set_failure_file(const char *path, const char *prefix); /* Send errors to stderr using internal error protocol. */ void i_set_failure_internal(void); /* If writing to log fails, ignore it instead of existing with FATAL_LOGWRITE or FATAL_LOGERROR. */ void i_set_failure_ignore_errors(bool ignore); /* Send informational messages to specified log file. i_set_failure_*() functions modify the info file too, so call this function after them. */ void i_set_info_file(const char *path); /* Send debug-level message to the given log file. The i_set_info_file() function modifies also the debug log file, so call this function after it. */ void i_set_debug_file(const char *path); /* Set the failure prefix. */ void i_set_failure_prefix(const char *prefix_fmt, ...) ATTR_FORMAT(1, 2); /* Set prefix to "". */ void i_unset_failure_prefix(void); /* Returns the current failure prefix (never NULL). */ const char *i_get_failure_prefix(void); /* Prefix failures with a timestamp. fmt is in strftime() format. */ void i_set_failure_timestamp_format(const char *fmt); /* When logging with internal error protocol, update the process's current IP address / log prefix by sending it to log process. This is mainly used to improve the error message if the process crashes. */ void i_set_failure_send_ip(const struct ip_addr *ip); void i_set_failure_send_prefix(const char *prefix); /* Call the callback before exit()ing. The callback may update the status. */ void i_set_failure_exit_callback(void (*callback)(int *status)); /* Call the exit callback and exit() */ void failure_exit(int status) ATTR_NORETURN ATTR_COLD; /* Parse a line logged using internal failure handler */ void i_failure_parse_line(const char *line, struct failure_line *failure); void failures_deinit(void); #endif dovecot-2.2.33.2/src/lib/test-var-expand.c0000644000175000017500000002461113165463624015066 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "env-util.h" #include "hostpid.h" #include "var-expand.h" #include "var-expand-private.h" struct var_expand_test { const char *in; const char *out; }; struct var_get_key_range_test { const char *in; unsigned int idx, size; }; static void test_var_expand_ranges(void) { static struct var_expand_test tests[] = { { "%v", "value1234" }, { "%3v", "val" }, { "%3.2v", "ue" }, { "%3.-2v", "ue12" }, { "%-3.2v", "23" }, { "%0.-1v", "value123" }, { "%-4.-1v", "123" } }; static struct var_expand_table table[] = { { 'v', "value1234", NULL }, { '\0', NULL, NULL } }; string_t *str = t_str_new(128); unsigned int i; test_begin("var_expand - ranges"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); var_expand(str, tests[i].in, table); test_assert(strcmp(tests[i].out, str_c(str)) == 0); } test_end(); } static void test_var_expand_builtin(void) { static struct var_expand_test tests[] = { { "%{hostname}", NULL }, { "%{pid}", NULL }, { "a%{env:FOO}b", "abaRb" }, { "%50Hv", "1f" }, { "%50Hw", "2e" }, { "%50Nv", "25" }, { "%50Nw", "e" }, { "%{nonexistent}", "UNSUPPORTED_VARIABLE_nonexistent" }, { "%{nonexistent:default}", "UNSUPPORTED_VARIABLE_nonexistent" }, }; static struct var_expand_table table[] = { { 'v', "value", NULL }, { 'w', "value2", NULL }, { '\0', NULL, NULL } }; string_t *str = t_str_new(128); unsigned int i; tests[0].out = my_hostname; tests[1].out = my_pid; env_put("FOO=baR"); test_begin("var_expand - builtin"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); var_expand(str, tests[i].in, table); test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); } test_end(); } static void test_var_get_key_range(void) { static struct var_get_key_range_test tests[] = { { "", 0, 0 }, { "{", 1, 0 }, { "k", 0, 1 }, { "{key}", 1, 3 }, { "5.5Rk", 4, 1 }, { "5.5R{key}", 5, 3 }, { "{key", 1, 3 } }; unsigned int i, idx, size; test_begin("var_get_key_range"); for (i = 0; i < N_ELEMENTS(tests); i++) { var_get_key_range(tests[i].in, &idx, &size); test_assert_idx(tests[i].idx == idx, i); test_assert_idx(tests[i].size == size, i); if (tests[i].size == 1) test_assert_idx(tests[i].in[idx] == var_get_key(tests[i].in), i); } test_end(); } static const char *test_var_expand_func1(const char *data, void *context) { test_assert(*(int *)context == 0xabcdef); return t_strdup_printf("<%s>", data); } static const char *test_var_expand_func2(const char *data ATTR_UNUSED, void *context ATTR_UNUSED) { return ""; } static const char *test_var_expand_func3(const char *data ATTR_UNUSED, void *context ATTR_UNUSED) { return NULL; } static void test_var_expand_with_funcs(void) { static struct var_expand_test tests[] = { { "%{func1}", "<>" }, { "%{func1:foo}", "" }, { "%{func2}", "" }, { "%{func3}", "" } }; static struct var_expand_table table[] = { { '\0', NULL, NULL } }; static const struct var_expand_func_table func_table[] = { { "func1", test_var_expand_func1 }, { "func2", test_var_expand_func2 }, { "func3", test_var_expand_func3 }, { NULL, NULL } }; string_t *str = t_str_new(128); unsigned int i; int ctx = 0xabcdef; test_begin("var_expand_with_funcs"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); var_expand_with_funcs(str, tests[i].in, table, func_table, &ctx); test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); } test_end(); } static void test_var_get_key(void) { static struct { const char *str; char key; } tests[] = { { "x", 'x' }, { "2.5Mx", 'x' }, { "200MDx", 'x' }, { "200MD{foo}", '{' }, { "{foo}", '{' }, { "", '\0' }, }; test_begin("var_get_key"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) test_assert_idx(var_get_key(tests[i].str) == tests[i].key, i); test_end(); } static void test_var_has_key(void) { static struct { const char *str; char key; const char *long_key; bool result; } tests[] = { { "%x%y", 'x', NULL, TRUE }, { "%x%y", 'y', NULL, TRUE }, { "%x%y", 'z', NULL, FALSE }, { "%{foo}", 'f', NULL, FALSE }, { "%{foo}", 'o', NULL, FALSE }, { "%{foo}", '\0', "foo", TRUE }, { "%{foo}", 'o', "foo", TRUE }, { "%2.5Mx%y", 'x', NULL, TRUE }, { "%2.5M{foo}", '\0', "foo", TRUE }, }; test_begin("var_has_key"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) test_assert_idx(var_has_key(tests[i].str, tests[i].key, tests[i].long_key) == tests[i].result, i); test_end(); } static const char *test_var_expand_hashing_func1(const char *data, void *context ATTR_UNUSED) { return data; } static int test_var_expand_bad_func(struct var_expand_context *ctx ATTR_UNUSED, const char *key, const char *field ATTR_UNUSED, const char **result_r ATTR_UNUSED, const char **error_r) { if (strcmp(key, "notfound") == 0) return 0; *error_r = "Bad parameters"; return -1; } static const struct var_expand_extension_func_table test_extension_funcs[] = { { "notfound", test_var_expand_bad_func }, { "badparam", test_var_expand_bad_func }, { NULL, NULL } }; static void test_var_expand_extensions(void) { test_begin("var_expand_extensions"); var_expand_register_func_array(test_extension_funcs); static struct var_expand_table table[] = { {'\0', "example", "value" }, {'\0', "other-example", "other-value" }, {'\0', NULL, NULL} }; static struct { const char *in; const char *out; } tests[] = { { "md5: %M{value} %{md5:value}", "md5: 1a79a4d60de6718e8e5b326e338ae533 1a79a4d60de6718e8e5b326e338ae533" }, { "sha1: %{sha1:value}", "sha1: c3499c2729730a7f807efb8676a92dcb6f8a3f8f" }, { "sha1: %{sha1:func1:example}", "sha1: c3499c2729730a7f807efb8676a92dcb6f8a3f8f" }, { "truncate: %{sha1;truncate=12:value}", "truncate: 0c34" }, { "truncate: %{sha1;truncate=16:value}", "truncate: c349" }, { "rounds,salt: %{sha1;rounds=1000,salt=seawater:value}", "rounds,salt: b515c85884f6b82dc7588279f3643a73e55d2289" }, { "rounds,salt,expand: %{sha1;rounds=1000,salt=%{other-value}:value} %{other-value}", "rounds,salt,expand: 49a598ee110af615e175f2e4511cc5d7ccff96ab other-example" }, { "format: %4.8{sha1:value}", "format: 9c272973" }, { "base64: %{sha1;format=base64:value}", "base64: w0mcJylzCn+AfvuGdqkty2+KP48=" }, }; static const struct var_expand_func_table func_table[] = { { "func1", test_var_expand_hashing_func1 }, { NULL, NULL } }; string_t *str = t_str_new(128); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); var_expand_with_funcs(str, tests[i].in, table, func_table, NULL); test_assert_idx(strcmp(str_c(str), tests[i].out) == 0, i); } var_expand_with_funcs(str, "notfound: %{notfound:field}", table, func_table, NULL); /* expect error */ test_expect_errors(1); var_expand_with_funcs(str, "notfound: %{badparam:field}", table, func_table, NULL); var_expand_unregister_func_array(test_extension_funcs); test_end(); } static void test_var_expand_if(void) { static const struct var_expand_table table[] = { { 'a', "alpha", "alpha" }, { 'b', "beta", "beta" }, { 'o', "1", "one" }, { 't', "2", "two" }, { '\0', ";:", "evil1" }, { '\0', ";test;", "evil2" }, { '\0', NULL, NULL } }; string_t *dest = t_str_new(64); test_begin("var_expand_if"); static const struct var_expand_test tests[] = { /* basic numeric operand test */ { "%{if;1;==;1;yes;no}", "yes"}, { "%{if;1;==;2;yes;no}", "no"}, { "%{if;1;<;1;yes;no}", "no"}, { "%{if;1;<;2;yes;no}", "yes"}, { "%{if;1;<=;1;yes;no}", "yes"}, { "%{if;1;<=;2;yes;no}", "yes"}, { "%{if;1;>;1;yes;no}", "no"}, { "%{if;1;>;2;yes;no}", "no"}, { "%{if;1;>=;1;yes;no}", "yes"}, { "%{if;1;>=;2;yes;no}", "no"}, { "%{if;1;!=;1;yes;no}", "no"}, { "%{if;1;!=;2;yes;no}", "yes"}, /* basic string operand test */ { "%{if;a;eq;a;yes;no}", "yes"}, { "%{if;a;eq;b;yes;no}", "no"}, { "%{if;a;lt;a;yes;no}", "no"}, { "%{if;a;lt;b;yes;no}", "yes"}, { "%{if;a;le;a;yes;no}", "yes"}, { "%{if;a;le;b;yes;no}", "yes"}, { "%{if;a;gt;a;yes;no}", "no"}, { "%{if;a;gt;b;yes;no}", "no"}, { "%{if;a;ge;a;yes;no}", "yes"}, { "%{if;a;ge;b;yes;no}", "no"}, { "%{if;a;ne;a;yes;no}", "no"}, { "%{if;a;ne;b;yes;no}", "yes"}, { "%{if;a;*;a;yes;no}", "yes"}, { "%{if;a;*;b;yes;no}", "no"}, { "%{if;a;*;*a*;yes;no}", "yes"}, { "%{if;a;*;*b*;yes;no}", "no"}, { "%{if;a;*;*;yes;no}", "yes"}, { "%{if;a;!*;a;yes;no}", "no"}, { "%{if;a;!*;b;yes;no}", "yes"}, { "%{if;a;!*;*a*;yes;no}", "no"}, { "%{if;a;!*;*b*;yes;no}", "yes"}, { "%{if;a;!*;*;yes;no}", "no"}, { "%{if;a;~;a;yes;no}", "yes"}, { "%{if;a;~;b;yes;no}", "no"}, { "%{if;a;~;.*a.*;yes;no}", "yes"}, { "%{if;a;~;.*b.*;yes;no}", "no"}, { "%{if;a;~;.*;yes;no}", "yes"}, { "%{if;a;!~;a;yes;no}", "no"}, { "%{if;a;!~;b;yes;no}", "yes"}, { "%{if;a;!~;.*a.*;yes;no}", "no"}, { "%{if;a;!~;.*b.*;yes;no}", "yes"}, { "%{if;a;!~;.*;yes;no}", "no"}, { "%{if;this is test;~;^test;yes;no}", "no"}, { "%{if;this is test;~;.*test;yes;no}", "yes"}, /* variable expansion */ { "%{if;%a;eq;%a;yes;no}", "yes"}, { "%{if;%a;eq;%b;yes;no}", "no"}, { "%{if;%{alpha};eq;%{alpha};yes;no}", "yes"}, { "%{if;%{alpha};eq;%{beta};yes;no}", "no"}, { "%{if;%o;eq;%o;yes;no}", "yes"}, { "%{if;%o;eq;%t;yes;no}", "no"}, { "%{if;%{one};eq;%{one};yes;no}", "yes"}, { "%{if;%{one};eq;%{two};yes;no}", "no"}, { "%{if;%{one};eq;%{one};%{one};%{two}}", "1"}, { "%{if;%{one};gt;%{two};%{one};%{two}}", "2"}, { "%{if;%{evil1};eq;\\;\\:;%{evil2};no}", ";test;"}, /* inner if */ { "%{if;%{if;%{one};eq;1;1;0};eq;%{if;%{two};eq;2;2;3};yes;no}", "no"}, /* no false */ { "%{if;1;==;1;yes}", "yes"}, { "%{if;1;==;2;yes}", ""}, /* invalid input */ { "%{if;}", ""}, { "%{if;1;}", ""}, { "%{if;1;==;}", ""}, { "%{if;1;==;2;}", ""}, { "%{if;1;fu;2;yes;no}", ""}, /* missing variables */ { "%{if;%{missing1};==;%{missing2};yes;no}", ""}, }; test_expect_errors(6); for(size_t i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(dest, 0); var_expand(dest, tests[i].in, table); test_assert_idx(strcmp(tests[i].out, str_c(dest)) == 0, i); } test_expect_no_more_errors(); test_end(); } void test_var_expand(void) { test_var_expand_ranges(); test_var_expand_builtin(); test_var_get_key_range(); test_var_expand_with_funcs(); test_var_get_key(); test_var_has_key(); test_var_expand_extensions(); test_var_expand_if(); } dovecot-2.2.33.2/src/lib/test-guid.c0000644000175000017500000001765613147010712013746 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "guid.h" #include "ioloop.h" /* * We want earlier timestamps to compare as < with later timestamps, but * guid_128_cmp() doesn't do that because the timestamps in the guid are * stored in little-endian byte order. */ static int reverse_guid_128_cmp(const guid_128_t a, const guid_128_t b) { int i; for (i = GUID_128_SIZE - 1; i >= 0; i--) if (a[i] != b[i]) return (int)a[i] - (int)b[i]; return 0; } static bool guid_128_has_sane_nsecs(const guid_128_t g) { unsigned long nsecs; nsecs = le32_to_cpu_unaligned(g); return nsecs < 1000000000UL; } static inline void set_fake_time(time_t sec, long usec) { ioloop_timeval.tv_sec = sec; ioloop_timeval.tv_usec = usec; } /* * We muck with the ioloop_timeval in various ways and make sure that the * guids that get generated make sense. To make sure that the guid * generation code takes up our faked timestamp, we use a far-away time (Jan * 1 2038) as the base time. We don't want to go beyond 32-bit signed * time_t for the base time to avoid issues on systems with 32-bit signed * time_t. * * While guids really only need to be unique, here we actually enforce that * they are increasing (as defined by reverse_guid_128_cmp()). If guids are * always increasing, they will always be unique. */ static void test_ioloop_guid_128_generate(void) { const time_t basetime = 2145909600; /* Jan 1 2038 */ struct timeval saved_ioloop_timeval; guid_128_t guids[2]; int i; /* save the ioloop_timeval before we start messing with it */ saved_ioloop_timeval = ioloop_timeval; /* * Generating multiple guids within a microsecond should keep * incrementing them. */ test_begin("guid_128_generate() increasing guid within a usec"); set_fake_time(basetime, 0); guid_128_generate(guids[1]); for (i = 0; i < 10; i++) { const int this = i % 2; const int prev = 1 - this; guid_128_generate(guids[this]); test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); test_assert(guid_128_has_sane_nsecs(guids[this])); } test_end(); /* * If the current time changes by +1 usec, so should the guids. */ test_begin("guid_128_generate() increasing guid with usec fast-forward"); for (i = 0; i < 10; i++) { const int this = i % 2; const int prev = 1 - this; set_fake_time(basetime, 1 + i); guid_128_generate(guids[this]); test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); test_assert(guid_128_has_sane_nsecs(guids[this])); } test_end(); /* * If the current time changes by +1 sec, so should the guids. */ test_begin("guid_128_generate() increasing guid with sec fast-forward"); for (i = 0; i < 10; i++) { const int this = i % 2; const int prev = 1 - this; set_fake_time(basetime + 1 + i, 0); guid_128_generate(guids[this]); test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); test_assert(guid_128_has_sane_nsecs(guids[this])); } test_end(); /* * Requesting enough guids should increment the seconds but always * produce valid nsecs. * * (Set a time that leaves us 1000 guids before seconds overflow and * then ask for 2500 guids.) */ test_begin("guid_128_generate() proper guid nsec overflow"); set_fake_time(basetime + 11, 999999L); for (i = 0; i < 2500; i++) { const int this = i % 2; const int prev = 1 - this; guid_128_generate(guids[this]); test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); test_assert(guid_128_has_sane_nsecs(guids[this])); } test_end(); /* * When ahead by 1500 guids (see previous test), +1 usec shouldn't * have any effect. */ test_begin("guid_128_generate() no effect with increasing time when ahead"); set_fake_time(basetime + 12, 0); guid_128_generate(guids[0]); test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0); test_assert(guid_128_has_sane_nsecs(guids[0])); test_end(); /* not a test - just set a more convenient time */ set_fake_time(basetime + 15, 500); guid_128_generate(guids[1]); test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0); test_assert(guid_128_has_sane_nsecs(guids[1])); /* * Time going backwards by 1 usec should have no effect on guids. */ test_begin("guid_128_generate() usec time-travel still increasing"); set_fake_time(basetime + 15, 499); guid_128_generate(guids[0]); test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0); test_assert(guid_128_has_sane_nsecs(guids[0])); test_end(); /* * Time going backwards by 1 sec should have no effect on guids. */ test_begin("guid_128_generate() sec time-travel still increasing"); set_fake_time(basetime + 14, 499); guid_128_generate(guids[1]); test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0); test_assert(guid_128_has_sane_nsecs(guids[1])); test_end(); /* restore the previously saved value just in case */ ioloop_timeval = saved_ioloop_timeval; } void test_guid(void) { static const guid_128_t test_guid = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xAB, 0xCD, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00 }; guid_128_t guid1, guid2, guid3; const char *str; char guidbuf[GUID_128_SIZE*2 + 2]; unsigned int i; test_begin("guid_128_generate()"); guid_128_generate(guid1); guid_128_generate(guid2); test_assert(!guid_128_equals(guid1, guid2)); test_assert(guid_128_cmp(guid1, guid2) != 0); test_end(); test_begin("guid_128_is_empty()"); test_assert(!guid_128_is_empty(guid1)); test_assert(!guid_128_is_empty(guid2)); guid_128_generate(guid3); guid_128_empty(guid3); test_assert(guid_128_is_empty(guid3)); test_end(); test_begin("guid_128_copy()"); guid_128_copy(guid3, guid1); test_assert(guid_128_equals(guid3, guid1)); test_assert(!guid_128_equals(guid3, guid2)); guid_128_copy(guid3, guid2); test_assert(!guid_128_equals(guid3, guid1)); test_assert(guid_128_equals(guid3, guid2)); test_end(); test_begin("guid_128_to_string()"); str = guid_128_to_string(guid1); test_assert(guid_128_from_string(str, guid3) == 0); test_assert(guid_128_equals(guid3, guid1)); test_end(); test_begin("guid_128_from_string()"); /* empty */ memset(guidbuf, '0', GUID_128_SIZE*2); guidbuf[GUID_128_SIZE*2] = '\0'; guidbuf[GUID_128_SIZE*2+1] = '\0'; test_assert(guid_128_from_string(guidbuf, guid3) == 0); test_assert(guid_128_is_empty(guid3)); /* too large */ guidbuf[GUID_128_SIZE*2] = '0'; test_assert(guid_128_from_string(guidbuf, guid3) < 0); /* too small */ guidbuf[GUID_128_SIZE*2-1] = '\0'; test_assert(guid_128_from_string(guidbuf, guid3) < 0); /* reset to normal */ guidbuf[GUID_128_SIZE*2-1] = '0'; guidbuf[GUID_128_SIZE*2] = '\0'; test_assert(guid_128_from_string(guidbuf, guid3) == 0); /* upper + lowercase hex chars */ i_assert(GUID_128_SIZE*2 > 16 + 6); for (i = 0; i < 10; i++) guidbuf[i] = '0' + i; for (i = 0; i < 6; i++) guidbuf[10 + i] = 'a' + i; for (i = 0; i < 6; i++) guidbuf[16 + i] = 'A' + i; test_assert(guid_128_from_string(guidbuf, guid3) == 0); test_assert(guid_128_equals(guid3, test_guid)); /* non-hex chars */ guidbuf[0] = 'g'; test_assert(guid_128_from_string(guidbuf, guid3) < 0); guidbuf[0] = ' '; test_assert(guid_128_from_string(guidbuf, guid3) < 0); test_assert(guid_128_from_uuid_string("fee0ceac-0327-11e7-ad39-52540078f374", guid3) == 0); test_assert(guid_128_from_uuid_string("fee0ceac032711e7ad3952540078f374", guid2) == 0); test_assert(guid_128_cmp(guid3, guid2) == 0); test_assert(guid_128_from_uuid_string("{fee0ceac-0327-11e7-ad39-52540078f374}", guid2) == 0); test_assert(guid_128_cmp(guid3, guid2) == 0); test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_RECORD), "fee0ceac-0327-11e7-ad39-52540078f374")==0); test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_COMPACT), "fee0ceac032711e7ad3952540078f374")==0); test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_MICROSOFT), "{fee0ceac-0327-11e7-ad39-52540078f374}")==0); /* failure test */ test_assert(guid_128_from_uuid_string("fe-e0ceac-0327-11e7-ad39-52540078f374", guid3) < 0); test_end(); test_ioloop_guid_128_generate(); } dovecot-2.2.33.2/src/lib/askpass.h0000644000175000017500000000022613123174404013477 00000000000000#ifndef ASKPASS_H #define ASKPASS_H void askpass(const char *prompt, char *buf, size_t buf_size); const char *t_askpass(const char *prompt); #endif dovecot-2.2.33.2/src/lib/test-failures.c0000644000175000017500000000372213123174404014620 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ /* Unit tests for failure helpers */ #include "test-lib.h" #include "failures.h" static int handlers_set_me; static void test_failures_handler(const struct failure_context *ctx, const char *format ATTR_UNUSED, va_list args ATTR_UNUSED) { handlers_set_me = ctx->type; } static void test_get_set_handlers(void) { failure_callback_t *handlers[4]; test_begin("get_handlers"); i_get_failure_handlers(handlers, handlers+1, handlers+2, handlers+3); test_end(); test_begin("set_handlers"); i_set_debug_handler(&test_failures_handler); i_debug("If you see this debug, something's gone wrong"); test_assert(handlers_set_me == LOG_TYPE_DEBUG); i_set_debug_handler(handlers[3]); i_set_info_handler(&test_failures_handler); i_info("If you see this info, something's gone wrong"); test_assert(handlers_set_me == LOG_TYPE_INFO); i_set_info_handler(handlers[2]); i_set_error_handler(&test_failures_handler); i_warning("If you see this warning, something's gone wrong"); test_assert(handlers_set_me == LOG_TYPE_WARNING); i_error("If you see this error, something's gone wrong"); test_assert(handlers_set_me == LOG_TYPE_ERROR); i_set_error_handler(handlers[1]); //i_set_fatal_handler(&test_failures_handler); //i_fatal("If you see this fatal, something's gone wrong"); //test_assert(handlers_set_me == LOG_TYPE_FATAL); //i_set_fatal_handler(handlers[0]); test_end(); } static void test_expected(void) { test_begin("expected messages"); test_expect_errors(1); i_warning("deliberate warning - not suppressed"); test_expect_no_more_errors(); test_end(); } static void test_expected_str(void) { test_begin("expected strings in messages"); test_expect_error_string("be unhappy"); i_error("deliberate error - suppressed - be unhappy if you see this"); test_expect_no_more_errors(); test_end(); } void test_failures(void) { test_get_set_handlers(); test_expected(); test_expected_str(); } dovecot-2.2.33.2/src/lib/module-dir.c0000644000175000017500000003754713165463624014121 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "module-dir.h" #ifdef HAVE_MODULES #include #include #include #include #ifndef RTLD_GLOBAL # define RTLD_GLOBAL 0 #endif #ifndef RTLD_NOW # define RTLD_NOW 0 #endif static const char *module_name_drop_suffix(const char *name); void *module_get_symbol_quiet(struct module *module, const char *symbol) { /* clear out old errors */ (void)dlerror(); return dlsym(module->handle, symbol); } void *module_get_symbol(struct module *module, const char *symbol) { const char *error; void *ret; ret = module_get_symbol_quiet(module, symbol); if (ret == NULL) { error = dlerror(); if (error != NULL) { i_error("module %s: dlsym(%s) failed: %s", module->path, symbol, error); ret = NULL; } } return ret; } static void *get_symbol(struct module *module, const char *symbol, bool quiet) { if (quiet) return module_get_symbol_quiet(module, symbol); return module_get_symbol(module, symbol); } static void module_free(struct module *module) { if (module->deinit != NULL && module->initialized) module->deinit(); /* dlclose()ing removes all symbols from valgrind's visibility. if GDB environment is set, don't actually unload the module (the GDB environment is used elsewhere too) */ if (getenv("GDB") == NULL) { if (dlclose(module->handle) != 0) i_error("dlclose(%s) failed: %m", module->path); } i_free(module->path); i_free(module->name); i_free(module); } static bool module_check_wrong_binary_dependency(const struct module_dir_load_settings *set, struct module *module, const char **error_r) { const char *symbol_name, *binary_dep, *const *names; string_t *errstr; if (set->binary_name == NULL) return TRUE; symbol_name = t_strconcat(module->name, "_binary_dependency", NULL); binary_dep = dlsym(module->handle, symbol_name); if (binary_dep == NULL) return TRUE; names = t_strsplit(binary_dep, " "); if (str_array_find(names, set->binary_name)) return TRUE; errstr = t_str_new(128); str_printfa(errstr, "Can't load plugin %s: " "Plugin is intended to be used only by ", module->name); if (names[1] == NULL) str_printfa(errstr, "%s binary", binary_dep); else str_printfa(errstr, "binaries: %s", binary_dep); str_printfa(errstr, " (we're %s)", set->binary_name); *error_r = str_c(errstr); return FALSE; } static bool module_check_missing_plugin_dependencies(const struct module_dir_load_settings *set, struct module *module, struct module *all_modules, const char **error_r) { const char **deps; struct module *m; string_t *errmsg; size_t len; deps = dlsym(module->handle, t_strconcat(module->name, "_dependencies", NULL)); if (deps == NULL) return TRUE; for (; *deps != NULL; deps++) { len = strlen(*deps); for (m = all_modules; m != NULL; m = m->next) { if (strncmp(m->name, *deps, len) == 0 && (m->name[len] == '\0' || strcmp(m->name+len, "_plugin") == 0)) break; } if (m == NULL) { errmsg = t_str_new(128); str_printfa(errmsg, "Plugin %s must be loaded also", *deps); if (set->setting_name != NULL) { str_printfa(errmsg, " (you must set: %s=$%s %s)", set->setting_name, set->setting_name, *deps); } *error_r = str_c(errmsg); return FALSE; } } return TRUE; } static void *quiet_dlopen(const char *path, int flags) { #ifndef __OpenBSD__ return dlopen(path, flags); #else void *handle; int fd; /* OpenBSD likes to print all "undefined symbol" errors to stderr. Hide them by sending them to /dev/null. */ fd = dup(STDERR_FILENO); if (fd == -1) i_fatal("dup() failed: %m"); if (dup2(dev_null_fd, STDERR_FILENO) < 0) i_fatal("dup2() failed: %m"); handle = dlopen(path, flags); if (dup2(fd, STDERR_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_error("close() failed: %m"); return handle; #endif } static bool versions_equal(const char *str1, const char *str2) { while (*str1 == *str2) { if (*str1 == '\0' || *str1 == '(') return TRUE; str1++; str2++; } return FALSE; } static int module_load(const char *path, const char *name, const struct module_dir_load_settings *set, struct module *all_modules, struct module **module_r, const char **error_r) { void *handle; struct module *module; const char *const *module_version; *module_r = NULL; *error_r = NULL; if (set->ignore_dlopen_errors) { handle = quiet_dlopen(path, RTLD_GLOBAL | RTLD_NOW); if (handle == NULL) { if (set->debug) { i_debug("Skipping module %s, " "because dlopen() failed: %s " "(this is usually intentional, " "so just ignore this message)", name, dlerror()); } return 0; } } else { handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); if (handle == NULL) { *error_r = t_strdup_printf("dlopen() failed: %s", dlerror()); #ifdef RTLD_LAZY /* try to give a better error message by lazily loading the plugin and checking its dependencies */ handle = dlopen(path, RTLD_LAZY); if (handle == NULL) return -1; #else return -1; #endif } } module = i_new(struct module, 1); module->path = i_strdup(path); module->name = i_strdup(name); module->handle = handle; module_version = set->abi_version == NULL ? NULL : get_symbol(module, t_strconcat(name, "_version", NULL), TRUE); if (module_version != NULL && !versions_equal(*module_version, set->abi_version)) { *error_r = t_strdup_printf( "Module is for different ABI version %s (we have %s)", *module_version, set->abi_version); module_free(module); return -1; } /* get our init func */ module->init = (void (*)(struct module *)) get_symbol(module, t_strconcat(name, "_init", NULL), !set->require_init_funcs); module->deinit = (void (*)(void)) get_symbol(module, t_strconcat(name, "_deinit", NULL), !set->require_init_funcs); if ((module->init == NULL || module->deinit == NULL) && set->require_init_funcs) { *error_r = t_strdup_printf( "Module doesn't have %s function", module->init == NULL ? "init" : "deinit"); } else if (!module_check_wrong_binary_dependency(set, module, error_r)) { /* failed */ } else if (!module_check_missing_plugin_dependencies(set, module, all_modules, error_r)) { /* failed */ } if (*error_r != NULL) { module->deinit = NULL; module_free(module); return -1; } if (set->debug) i_debug("Module loaded: %s", path); *module_r = module; return 1; } static int module_name_cmp(const char *const *n1, const char *const *n2) { const char *s1 = *n1, *s2 = *n2; if (strncmp(s1, "lib", 3) == 0) s1 += 3; if (strncmp(s2, "lib", 3) == 0) s2 += 3; return strcmp(s1, s2); } static bool module_want_load(const struct module_dir_load_settings *set, const char **names, const char *name) { if (set->filter_callback != NULL) { if (!set->filter_callback(name, set->filter_context)) return FALSE; } if (names == NULL) return TRUE; for (; *names != NULL; names++) { if (strcmp(*names, name) == 0) { *names = ""; return TRUE; } } return FALSE; } static void check_duplicates(ARRAY_TYPE(const_string) *names, const char *name, const char *dir) { const char *const *names_p, *base_name, *tmp; unsigned int i, count; base_name = module_file_get_name(name); names_p = array_get(names, &count); for (i = 0; i < count; i++) { tmp = module_file_get_name(names_p[i]); if (strcmp(tmp, base_name) == 0) i_fatal("Multiple files for module %s: %s/%s, %s/%s", base_name, dir, name, dir, names_p[i]); } } struct module *module_dir_find(struct module *modules, const char *name) { struct module *module; size_t len = strlen(name); for (module = modules; module != NULL; module = module->next) { if (strncmp(module->name, name, len) == 0) { if (module->name[len] == '\0' || strcmp(module->name + len, "_plugin") == 0) return module; } } return NULL; } static bool module_is_loaded(struct module *modules, const char *name) { return module_dir_find(modules, name) != NULL; } static void module_names_fix(const char **module_names) { unsigned int i, j; if (module_names[0] == NULL) return; /* allow giving the module names also in non-base form. convert them in here. */ for (i = 0; module_names[i] != NULL; i++) module_names[i] = module_file_get_name(module_names[i]); /* @UNSAFE: drop duplicates */ i_qsort(module_names, i, sizeof(*module_names), i_strcmp_p); for (i = j = 1; module_names[i] != NULL; i++) { if (strcmp(module_names[i-1], module_names[i]) != 0) module_names[j++] = module_names[i]; } module_names[j] = NULL; } static bool module_dir_is_all_loaded(struct module *old_modules, const char **module_names) { unsigned int i; for (i = 0; module_names[i] != NULL; i++) { if (!module_is_loaded(old_modules, module_names[i])) return FALSE; } return TRUE; } static int module_dir_load_real(struct module **_modules, const char *dir, const char **module_names, const struct module_dir_load_settings *set, char **error_r) { DIR *dirp; struct dirent *d; const char *name, *p, *error, *const *names_p; struct module *modules, *module, **module_pos, *old_modules = *_modules; unsigned int i, count; ARRAY_TYPE(const_string) names; pool_t pool; int ret; *error_r = NULL; if (module_names != NULL) { if (module_dir_is_all_loaded(old_modules, module_names)) return 0; } if (set->debug) i_debug("Loading modules from directory: %s", dir); dirp = opendir(dir); if (dirp == NULL) { *error_r = i_strdup_printf("opendir(%s) failed: %m", dir); if (module_names != NULL) { /* we were given a list of modules to load. we can't fail. */ return -1; } return errno == ENOENT ? 0 : -1; } pool = pool_alloconly_create("module loader", 4096); p_array_init(&names, pool, 32); modules = NULL; for (errno = 0; (d = readdir(dirp)) != NULL; errno = 0) { name = d->d_name; if (name[0] == '.') continue; p = strstr(name, MODULE_SUFFIX); if (p == NULL || strlen(p) != 3) continue; T_BEGIN { check_duplicates(&names, name, dir); } T_END; name = p_strdup(pool, d->d_name); array_append(&names, &name, 1); } if (errno != 0) *error_r = i_strdup_printf("readdir(%s) failed: %m", dir); if (closedir(dirp) < 0 && *error_r == NULL) *error_r = i_strdup_printf("closedir(%s) failed: %m", dir); if (*error_r != NULL) { pool_unref(&pool); return -1; } array_sort(&names, module_name_cmp); names_p = array_get(&names, &count); modules = old_modules; module_pos = &modules; while (*module_pos != NULL) module_pos = &(*module_pos)->next; for (i = 0; i < count; i++) T_BEGIN { const char *path, *stripped_name, *suffixless_name; name = names_p[i]; stripped_name = module_file_get_name(name); suffixless_name = module_name_drop_suffix(stripped_name); if (!module_want_load(set, module_names, suffixless_name) || module_is_loaded(old_modules, suffixless_name)) module = NULL; else { path = t_strconcat(dir, "/", name, NULL); ret = module_load(path, stripped_name, set, modules, &module, &error); if (ret >= 0) ; else if (module_names != NULL) { *error_r = i_strdup_printf("Couldn't load required plugin %s: %s", path, error); i = count; } else { i_error("Couldn't load plugin %s: %s", path, error); } } if (module != NULL) { *module_pos = module; module_pos = &module->next; } } T_END; pool_unref(&pool); if (module_names != NULL && *error_r == NULL && !set->ignore_missing) { /* make sure all modules were found */ for (; *module_names != NULL; module_names++) { if (**module_names != '\0') { *error_r = i_strdup_printf("Plugin '%s' not found from directory %s", *module_names, dir); break; } } } *_modules = modules; return *error_r != NULL ? -1 : 0; } int module_dir_try_load_missing(struct module **modules, const char *dir, const char *module_names, const struct module_dir_load_settings *set, const char **error_r) { char *error = NULL; int ret; T_BEGIN { const char **arr = NULL; if (module_names != NULL) { arr = t_strsplit_spaces(module_names, ", "); module_names_fix(arr); } ret = module_dir_load_real(modules, dir, arr, set, &error); } T_END; *error_r = t_strdup(error); i_free(error); return ret; } struct module * module_dir_load_missing(struct module *old_modules, const char *dir, const char *module_names, const struct module_dir_load_settings *set) { struct module *new_modules = old_modules; const char *error; if (module_dir_try_load_missing(&new_modules, dir, module_names, set, &error) < 0) { if (module_names != NULL) i_fatal("%s", error); else i_error("%s", error); } return new_modules; } void module_dir_init(struct module *modules) { struct module *module; for (module = modules; module != NULL; module = module->next) { if (!module->initialized) { module->initialized = TRUE; if (module->init != NULL) T_BEGIN { module->init(module); } T_END; } } } void module_dir_deinit(struct module *modules) { struct module *module, **rev; unsigned int i, count = 0; for (module = modules; module != NULL; module = module->next) { if (module->deinit != NULL && module->initialized) count++; } if (count == 0) return; /* @UNSAFE: deinitialize in reverse order */ T_BEGIN { rev = t_new(struct module *, count); for (i = 0, module = modules; i < count; ) { if (module->deinit != NULL && module->initialized) { rev[count-i-1] = module; i++; } module = module->next; } for (i = 0; i < count; i++) { module = rev[i]; module->deinit(); module->initialized = FALSE; } } T_END; } void module_dir_unload(struct module **modules) { struct module *module, *next; /* Call all modules' deinit() first, so that they may still call each others' functions. */ module_dir_deinit(*modules); for (module = *modules; module != NULL; module = next) { next = module->next; module_free(module); } *modules = NULL; } #else #ifndef MODULE_SUFFIX # define MODULE_SUFFIX ".so" /* just to avoid build failure */ #endif struct module * module_dir_load_missing(struct module *old_modules ATTR_UNUSED, const char *dir ATTR_UNUSED, const char *module_names, const struct module_dir_load_settings *set ATTR_UNUSED) { #define NO_SUPPORT_ERRSTR "Dynamically loadable module support not built in" if (module_names == NULL) i_error(NO_SUPPORT_ERRSTR); else { i_fatal(NO_SUPPORT_ERRSTR", can't load plugins: %s", module_names); } return NULL; } void module_dir_init(struct module *modules ATTR_UNUSED) { } void module_dir_deinit(struct module *modules ATTR_UNUSED) { } void module_dir_unload(struct module **modules ATTR_UNUSED) { } struct module *module_dir_find(struct module *modules ATTR_UNUSED, const char *name ATTR_UNUSED) { return NULL; } void *module_get_symbol(struct module *module ATTR_UNUSED, const char *symbol ATTR_UNUSED) { return NULL; } void *module_get_symbol_quiet(struct module *module ATTR_UNUSED, const char *symbol ATTR_UNUSED) { return NULL; } #endif struct module *module_dir_load(const char *dir, const char *module_names, const struct module_dir_load_settings *set) { return module_dir_load_missing(NULL, dir, module_names, set); } const char *module_file_get_name(const char *fname) { const char *p; /* [lib][nn_]name(.so) */ if (strncmp(fname, "lib", 3) == 0) fname += 3; for (p = fname; *p != '\0'; p++) { if (*p < '0' || *p > '9') break; } if (*p == '_') fname = p + 1; p = strstr(fname, MODULE_SUFFIX); if (p == NULL) return fname; return t_strdup_until(fname, p); } static const char *module_name_drop_suffix(const char *name) { size_t len; len = strlen(name); if (len > 7 && strcmp(name + len - 7, "_plugin") == 0) name = t_strndup(name, len - 7); return name; } const char *module_get_plugin_name(struct module *module) { return module_name_drop_suffix(module->name); } dovecot-2.2.33.2/src/lib/lib-signals.c0000644000175000017500000002233513165463624014251 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "fd-close-on-exec.h" #include "fd-set-nonblock.h" #include "write-full.h" #include "lib-signals.h" #include #include #include #define MAX_SIGNAL_VALUE 63 #define SIGNAL_IS_TERMINAL(signo) \ ((signo) == SIGINT || (signo) == SIGQUIT || (signo) == SIGTERM) #if !defined(SA_SIGINFO) && !defined(SI_NOINFO) /* without SA_SIGINFO we don't know what the real code is. we need SI_NOINFO to make sure lib_signal_code_to_str() returns "". */ # define SI_NOINFO -1 #endif struct signal_handler { signal_handler_t *handler; void *context; enum libsig_flags flags; struct signal_handler *next; }; volatile unsigned int signal_term_counter = 0; /* Remember that these are accessed inside signal handler which may be called even while we're initializing/deinitializing. Try hard to keep everything in consistent state. */ static struct signal_handler *signal_handlers[MAX_SIGNAL_VALUE+1] = { NULL, }; static int sig_pipe_fd[2] = { -1, -1 }; static bool signals_initialized = FALSE; static struct io *io_sig = NULL; static siginfo_t pending_signals[MAX_SIGNAL_VALUE+1]; static bool have_pending_signals = FALSE; const char *lib_signal_code_to_str(int signo, int sicode) { /* common */ switch (sicode) { #ifdef SI_NOINFO case SI_NOINFO: return ""; #endif case SI_USER: return "kill"; #ifdef SI_KERNEL case SI_KERNEL: return "kernel"; #endif case SI_TIMER: return "timer"; } /* If SEGV_MAPERR is supported, the rest of them must be too. FreeBSD 6 at least doesn't support these. */ #ifdef SEGV_MAPERR switch (signo) { case SIGSEGV: switch (sicode) { case SEGV_MAPERR: return "address not mapped"; case SEGV_ACCERR: return "invalid permissions"; } break; case SIGBUS: switch (sicode) { case BUS_ADRALN: return "invalid address alignment"; #ifdef BUS_ADRERR /* for OSX 10.3 */ case BUS_ADRERR: return "nonexistent physical address"; #endif #ifdef BUS_OBJERR /* for OSX 10.3 */ case BUS_OBJERR: return "object-specific hardware error"; #endif } } #endif return t_strdup_printf("unknown %d", sicode); } #ifdef SA_SIGINFO static void sig_handler(int signo, siginfo_t *si, void *context ATTR_UNUSED) #else static void sig_handler(int signo) #endif { struct signal_handler *h; int saved_errno; char c = 0; #if defined(SI_NOINFO) || !defined(SA_SIGINFO) #ifndef SA_SIGINFO siginfo_t *si = NULL; #endif siginfo_t tmp_si; if (si == NULL) { /* Solaris can leave this to NULL */ i_zero(&tmp_si); tmp_si.si_signo = signo; tmp_si.si_code = SI_NOINFO; si = &tmp_si; } #endif if (signo < 0 || signo > MAX_SIGNAL_VALUE) return; if (SIGNAL_IS_TERMINAL(signo)) signal_term_counter++; /* remember that we're inside a signal handler which might have been called at any time. don't do anything that's unsafe. we might also get interrupted by another signal while inside this handler. */ saved_errno = errno; for (h = signal_handlers[signo]; h != NULL; h = h->next) { if ((h->flags & LIBSIG_FLAG_DELAYED) == 0) h->handler(si, h->context); else if (pending_signals[signo].si_signo == 0) { pending_signals[signo] = *si; if (!have_pending_signals) { if (write(sig_pipe_fd[1], &c, 1) != 1) lib_signals_syscall_error("signal: write(sigpipe) failed: "); have_pending_signals = TRUE; } } } errno = saved_errno; } #ifdef SA_SIGINFO static void sig_ignore(int signo ATTR_UNUSED, siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) #else static void sig_ignore(int signo ATTR_UNUSED) #endif { /* if we used SIG_IGN instead of this function, the system call might be restarted */ } static void ATTR_NULL(1) signal_read(void *context ATTR_UNUSED) { siginfo_t signals[MAX_SIGNAL_VALUE+1]; sigset_t fullset, oldset; struct signal_handler *h; char buf[64]; int signo; ssize_t ret; if (sigfillset(&fullset) < 0) i_fatal("sigfillset() failed: %m"); if (sigprocmask(SIG_BLOCK, &fullset, &oldset) < 0) i_fatal("sigprocmask() failed: %m"); /* typically we should read only a single byte, but if a signal is sent while signal handler is running we might get more. */ ret = read(sig_pipe_fd[0], buf, sizeof(buf)); if (ret > 0) { memcpy(signals, pending_signals, sizeof(signals)); memset(pending_signals, 0, sizeof(pending_signals)); have_pending_signals = FALSE; } else if (ret < 0) { if (errno != EAGAIN) i_fatal("read(sigpipe) failed: %m"); } else { i_fatal("read(sigpipe) failed: EOF"); } if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) i_fatal("sigprocmask() failed: %m"); if (ret < 0) return; /* call the delayed handlers after signals are copied and unblocked */ for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) { if (signals[signo].si_signo == 0) continue; for (h = signal_handlers[signo]; h != NULL; h = h->next) { if ((h->flags & LIBSIG_FLAG_DELAYED) != 0) h->handler(&signals[signo], h->context); } } } static void lib_signals_set(int signo, enum libsig_flags flags) { struct sigaction act; if (sigemptyset(&act.sa_mask) < 0) i_fatal("sigemptyset(): %m"); #ifdef SA_SIGINFO act.sa_flags = SA_SIGINFO; act.sa_sigaction = sig_handler; #else act.sa_flags = 0; act.sa_handler = sig_handler; #endif if ((flags & LIBSIG_FLAG_RESTART) != 0) act.sa_flags |= SA_RESTART; if (sigaction(signo, &act, NULL) < 0) i_fatal("sigaction(%d): %m", signo); } void lib_signals_set_handler(int signo, enum libsig_flags flags, signal_handler_t *handler, void *context) { struct signal_handler *h; i_assert(handler != NULL); if (signo < 0 || signo > MAX_SIGNAL_VALUE) { i_panic("Trying to set signal %d handler, but max is %d", signo, MAX_SIGNAL_VALUE); } if (signal_handlers[signo] == NULL && signals_initialized) lib_signals_set(signo, flags); if ((flags & LIBSIG_FLAG_DELAYED) != 0 && sig_pipe_fd[0] == -1) { /* first delayed handler */ if (pipe(sig_pipe_fd) < 0) i_fatal("pipe() failed: %m"); fd_set_nonblock(sig_pipe_fd[0], TRUE); fd_set_nonblock(sig_pipe_fd[1], TRUE); fd_close_on_exec(sig_pipe_fd[0], TRUE); fd_close_on_exec(sig_pipe_fd[1], TRUE); if (signals_initialized) { io_sig = io_add(sig_pipe_fd[0], IO_READ, signal_read, (void *)NULL); } } h = i_new(struct signal_handler, 1); h->handler = handler; h->context = context; h->flags = flags; /* atomically set to signal_handlers[] list */ h->next = signal_handlers[signo]; signal_handlers[signo] = h; } void lib_signals_ignore(int signo, bool restart_syscalls) { struct sigaction act; if (signo < 0 || signo > MAX_SIGNAL_VALUE) { i_panic("Trying to ignore signal %d, but max is %d", signo, MAX_SIGNAL_VALUE); } i_assert(signal_handlers[signo] == NULL); if (sigemptyset(&act.sa_mask) < 0) i_fatal("sigemptyset(): %m"); if (restart_syscalls) { act.sa_flags = SA_RESTART; act.sa_handler = SIG_IGN; } else { #ifdef SA_SIGINFO act.sa_flags = SA_SIGINFO; act.sa_sigaction = sig_ignore; #else act.sa_flags = 0; act.sa_handler = sig_ignore; #endif } if (sigaction(signo, &act, NULL) < 0) i_fatal("sigaction(%d): %m", signo); } void lib_signals_unset_handler(int signo, signal_handler_t *handler, void *context) { struct signal_handler *h, **p; for (p = &signal_handlers[signo]; *p != NULL; p = &(*p)->next) { if ((*p)->handler == handler && (*p)->context == context) { h = *p; *p = h->next; i_free(h); return; } } i_panic("lib_signals_unset_handler(%d, %p, %p): handler not found", signo, (void *)handler, context); } void lib_signals_reset_ioloop(void) { if (io_sig != NULL) { io_remove(&io_sig); io_sig = io_add(sig_pipe_fd[0], IO_READ, signal_read, (void *)NULL); } } void lib_signals_syscall_error(const char *prefix) { /* @UNSAFE: We're in a signal handler. It's very limited what is allowed in here. Especially strerror() isn't at least officially allowed. */ char errno_buf[MAX_INT_STRLEN], *errno_str; errno_str = dec2str_buf(errno_buf, errno); size_t prefix_len = strlen(prefix); size_t errno_str_len = strlen(errno_str); char buf[prefix_len + errno_str_len + 1]; memcpy(buf, prefix, prefix_len); memcpy(buf + prefix_len, errno_str, errno_str_len); buf[prefix_len + errno_str_len] = '\n'; if (write_full(STDERR_FILENO, buf, prefix_len + errno_str_len + 1) < 0) { /* can't really do anything */ } } void lib_signals_init(void) { int i; signals_initialized = TRUE; /* add signals that were already registered */ for (i = 0; i < MAX_SIGNAL_VALUE; i++) { if (signal_handlers[i] != NULL) lib_signals_set(i, signal_handlers[i]->flags); } if (sig_pipe_fd[0] != -1) { io_sig = io_add(sig_pipe_fd[0], IO_READ, signal_read, (void *)NULL); } } void lib_signals_deinit(void) { struct signal_handler *handlers, *h; int i; for (i = 0; i < MAX_SIGNAL_VALUE; i++) { if (signal_handlers[i] != NULL) { /* atomically remove from signal_handlers[] list */ handlers = signal_handlers[i]; signal_handlers[i] = NULL; while (handlers != NULL) { h = handlers; handlers = h->next; i_free(h); } } } if (io_sig != NULL) io_remove(&io_sig); if (sig_pipe_fd[0] != -1) { if (close(sig_pipe_fd[0]) < 0) i_error("close(sigpipe) failed: %m"); if (close(sig_pipe_fd[1]) < 0) i_error("close(sigpipe) failed: %m"); } } dovecot-2.2.33.2/src/lib/hostpid.c0000644000175000017500000000300313123174404013473 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include #include #define HOSTNAME_DISALLOWED_CHARS "/\r\n\t" const char *my_hostname = NULL; const char *my_pid = NULL; static char *my_hostname_dup = NULL; static char *my_domain = NULL; void hostpid_init(void) { static char pid[MAX_INT_STRLEN]; char hostname[256]; const char *value; /* allow calling hostpid_init() multiple times to reset hostname */ i_free_and_null(my_hostname_dup); i_free_and_null(my_domain); value = getenv(MY_HOSTNAME_ENV); if (value == NULL) { if (gethostname(hostname, sizeof(hostname)-1) < 0) i_fatal("gethostname() failed: %m"); hostname[sizeof(hostname)-1] = '\0'; value = hostname; } if (value[0] == '\0' || strcspn(value, HOSTNAME_DISALLOWED_CHARS) != strlen(value)) i_fatal("Invalid system hostname: '%s'", value); my_hostname_dup = i_strdup(value); my_hostname = my_hostname_dup; i_snprintf(pid, sizeof(pid), "%lld", (long long)getpid()); my_pid = pid; } void hostpid_deinit(void) { i_free(my_hostname_dup); i_free(my_domain); } const char *my_hostdomain(void) { struct hostent *hent; const char *name; if (my_domain == NULL) { name = getenv(MY_HOSTDOMAIN_ENV); if (name == NULL) { hent = gethostbyname(my_hostname); name = hent != NULL ? hent->h_name : NULL; if (name == NULL) { /* failed, use just the hostname */ name = my_hostname; } } my_domain = i_strdup(name); } return my_domain; } dovecot-2.2.33.2/src/lib/write-full.c0000644000175000017500000000200313123174404014112 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "write-full.h" #include int write_full(int fd, const void *data, size_t size) { ssize_t ret; while (size > 0) { ret = write(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX); if (unlikely(ret < 0)) return -1; if (unlikely(ret == 0)) { /* nothing was written, only reason for this should be out of disk space */ errno = ENOSPC; return -1; } data = CONST_PTR_OFFSET(data, ret); size -= ret; } return 0; } int pwrite_full(int fd, const void *data, size_t size, off_t offset) { ssize_t ret; while (size > 0) { ret = pwrite(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX, offset); if (unlikely(ret < 0)) return -1; if (unlikely(ret == 0)) { /* nothing was written, only reason for this should be out of disk space */ errno = ENOSPC; return -1; } data = CONST_PTR_OFFSET(data, ret); size -= ret; offset += ret; } return 0; } dovecot-2.2.33.2/src/lib/llist.h0000644000175000017500000000437513123174404013172 00000000000000#ifndef LLIST_H #define LLIST_H /* Doubly linked list */ #define DLLIST_PREPEND_FULL(list, item, prev, next) STMT_START { \ (item)->prev = NULL; \ (item)->next = *(list); \ if (*(list) != NULL) (*(list))->prev = (item); \ *(list) = (item); \ } STMT_END #define DLLIST_PREPEND(list, item) \ DLLIST_PREPEND_FULL(list, item, prev, next) #define DLLIST_REMOVE_FULL(list, item, prev, next) STMT_START { \ if ((item)->prev != NULL) \ (item)->prev->next = (item)->next; \ else if ((*list) == item) \ *(list) = (item)->next; \ if ((item)->next != NULL) { \ (item)->next->prev = (item)->prev; \ (item)->next = NULL; \ } \ (item)->prev = NULL; \ } STMT_END #define DLLIST_REMOVE(list, item) \ DLLIST_REMOVE_FULL(list, item, prev, next) /* Doubly linked list with head and tail */ #define DLLIST2_PREPEND_FULL(head, tail, item, prev, next) STMT_START { \ (item)->prev = NULL; \ (item)->next = *(head); \ if (*(head) != NULL) (*(head))->prev = (item); else (*tail) = (item); \ *(head) = (item); \ } STMT_END #define DLLIST2_PREPEND(head, tail, item) \ DLLIST2_PREPEND_FULL(head, tail, item, prev, next) #define DLLIST2_APPEND_FULL(head, tail, item, prev, next) STMT_START { \ (item)->prev = *(tail); \ (item)->next = NULL; \ if (*(tail) != NULL) (*(tail))->next = (item); else (*head) = (item); \ *(tail) = (item); \ } STMT_END #define DLLIST2_APPEND(head, tail, item) \ DLLIST2_APPEND_FULL(head, tail, item, prev, next) #define DLLIST2_INSERT_AFTER_FULL(head, tail, after, item, prev, next) \ STMT_START { \ (item)->prev = (after); \ (item)->next = (after)->next; \ (after)->next = (item); \ if (*(tail) == (after)) \ *(tail) = (item); \ } STMT_END #define DLLIST2_INSERT_AFTER(head, tail, after, item) \ DLLIST2_INSERT_AFTER_FULL(head, tail, after, item, prev, next) #define DLLIST2_REMOVE_FULL(head, tail, item, prev, next) STMT_START { \ if ((item)->prev != NULL) \ (item)->prev->next = (item)->next; \ else if (*(head) == item) \ *(head) = (item)->next; \ if ((item)->next != NULL) { \ (item)->next->prev = (item)->prev; \ (item)->next = NULL; \ } else if ((*tail) == item) \ *(tail) = (item)->prev; \ (item)->prev = NULL; \ } STMT_END #define DLLIST2_REMOVE(head, tail, item) \ DLLIST2_REMOVE_FULL(head, tail, item, prev, next) #endif dovecot-2.2.33.2/src/lib/strfuncs.h0000644000175000017500000001454613165463624013726 00000000000000#ifndef STRFUNC_H #define STRFUNC_H #define MAX_INT_STRLEN ((sizeof(uintmax_t) * CHAR_BIT + 2) / 3 + 1) extern const unsigned char uchar_nul; /* (const unsigned char *)"" */ /* Returns -1 if dest wasn't large enough, 0 if not. */ int i_snprintf(char *dest, size_t max_chars, const char *format, ...) ATTR_FORMAT(3, 4); char *p_strdup(pool_t pool, const char *str) ATTR_MALLOC; void *p_memdup(pool_t pool, const void *data, size_t size) ATTR_MALLOC; /* return NULL if str = "" */ char *p_strdup_empty(pool_t pool, const char *str) ATTR_MALLOC; /* *end isn't included */ char *p_strdup_until(pool_t pool, const void *start, const void *end) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *p_strndup(pool_t pool, const void *str, size_t max_chars) ATTR_MALLOC; char *p_strdup_printf(pool_t pool, const char *format, ...) ATTR_FORMAT(2, 3) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *p_strdup_vprintf(pool_t pool, const char *format, va_list args) ATTR_FORMAT(2, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *p_strconcat(pool_t pool, const char *str1, ...) ATTR_SENTINEL ATTR_MALLOC; /* same with temporary memory allocations: */ const char *t_strdup(const char *str) ATTR_MALLOC; char *t_strdup_noconst(const char *str) ATTR_MALLOC; /* return NULL if str = "" */ const char *t_strdup_empty(const char *str) ATTR_MALLOC; /* *end isn't included */ const char *t_strdup_until(const void *start, const void *end) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char *t_strndup(const void *str, size_t max_chars) ATTR_MALLOC; const char *t_strdup_printf(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char *t_strdup_vprintf(const char *format, va_list args) ATTR_FORMAT(1, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char *t_strconcat(const char *str1, ...) ATTR_SENTINEL ATTR_MALLOC; /* Like t_strdup(), but stop at cutchar. */ const char *t_strcut(const char *str, char cutchar); /* Replace all from->to chars in the string. */ const char *t_str_replace(const char *str, char from, char to); /* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */ int i_strocpy(char *dest, const char *src, size_t dstsize); char *str_ucase(char *str); char *str_lcase(char *str); const char *t_str_lcase(const char *str); const char *t_str_ucase(const char *str); /* Trim matching chars from either side of the string */ const char *str_ltrim(const char *str, const char *chars); const char *t_str_ltrim(const char *str, const char *chars); const char *t_str_rtrim(const char *str, const char *chars); /*const char *t_str_trim(const char *str, const char *chars);*/ int null_strcmp(const char *s1, const char *s2) ATTR_PURE; int null_strcasecmp(const char *s1, const char *s2) ATTR_PURE; int bsearch_strcmp(const char *key, const char *const *member) ATTR_PURE; int bsearch_strcasecmp(const char *key, const char *const *member) ATTR_PURE; int i_memcasecmp(const void *p1, const void *p2, size_t size) ATTR_PURE; int i_strcmp_p(const char *const *p1, const char *const *p2) ATTR_PURE; int i_strcasecmp_p(const char *const *p1, const char *const *p2) ATTR_PURE; /* Returns TRUE if the two memory areas are equal. This function is safe against timing attacks, so it compares all the bytes every time. */ bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size); static inline char *i_strchr_to_next(const char *str, char chr) { char *tmp = (char *)strchr(str, chr); return tmp == NULL ? NULL : tmp+1; } /* separators is an array of separator characters, not a separator string. an empty data string results in an array containing only NULL. */ char **p_strsplit(pool_t pool, const char *data, const char *separators) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char **t_strsplit(const char *data, const char *separators) ATTR_MALLOC ATTR_RETURNS_NONNULL; /* like p_strsplit(), but treats multiple adjacent separators as a single separator. separators at the beginning or at the end of the string are also ignored, so it's not possible for the result to have any empty strings. */ char **p_strsplit_spaces(pool_t pool, const char *data, const char *separators) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char **t_strsplit_spaces(const char *data, const char *separators) ATTR_MALLOC ATTR_RETURNS_NONNULL; void p_strsplit_free(pool_t pool, char **arr); /* Optimized version of t_strsplit(data, "\t") */ const char **t_strsplit_tab(const char *data) ATTR_MALLOC ATTR_RETURNS_NONNULL; const char *dec2str(uintmax_t number); /* Use the given buffer to write out the number. Returns pointer to the written number in the buffer. Note that this isn't the same as the beginning of the buffer. */ char *dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN], uintmax_t number); /* Return length of NULL-terminated list string array */ unsigned int str_array_length(const char *const *arr) ATTR_PURE; /* Return all strings from array joined into one string. */ const char *t_strarray_join(const char *const *arr, const char *separator) ATTR_MALLOC ATTR_RETURNS_NONNULL; /* Removes a value from NULL-terminated string array. Returns TRUE if found. */ bool str_array_remove(const char **arr, const char *value); /* Returns TRUE if value exists in NULL-terminated string array. */ bool str_array_find(const char *const *arr, const char *value); /* Like str_array_find(), but use strcasecmp(). */ bool str_array_icase_find(const char *const *arr, const char *value); /* Duplicate array of strings. The memory can be freed by freeing the return value. */ const char **p_strarray_dup(pool_t pool, const char *const *arr) ATTR_MALLOC ATTR_RETURNS_NONNULL; /* Join ARRAY_TYPE(const_string) to a string, similar to t_strarray_join() */ char *p_array_const_string_join(pool_t pool, const ARRAY_TYPE(const_string) *arr, const char *separator); #define t_array_const_string_join(arr, separator) \ ((const char *)p_array_const_string_join(unsafe_data_stack_pool, arr, separator)) /* FIXME: v2.3 - sort and search APIs belong into their own header, not here */ #include "sort.h" #define i_bsearch(key, base, nmemb, size, cmp) \ bsearch(key, base, nmemb, size + \ CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ typeof(const typeof(*base) *))), \ (int (*)(const void *, const void *))cmp) /* INTERNAL */ char *t_noalloc_strdup_vprintf(const char *format, va_list args, unsigned int *size_r) ATTR_FORMAT(1, 0) ATTR_RETURNS_NONNULL; char *vstrconcat(const char *str1, va_list args, size_t *ret_len) ATTR_MALLOC; #endif dovecot-2.2.33.2/src/lib/askpass.c0000644000175000017500000000261013165463624013504 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "askpass.h" #include #include #include #include static void askpass_str(const char *prompt, buffer_t *pass) { struct termios old_tio, tio; bool tty, restore_tio = FALSE; char ch; int fd; tty = isatty(STDIN_FILENO); if (tty) { fputs(prompt, stderr); fflush(stderr); fd = open("/dev/tty", O_RDONLY); if (fd < 0) i_fatal("open(/dev/tty) failed: %m"); /* turn off echo */ if (tcgetattr(fd, &old_tio) == 0) { restore_tio = TRUE; tio = old_tio; tio.c_lflag &= ~(ECHO | ECHONL); (void)tcsetattr(fd, TCSAFLUSH, &tio); } } else { /* read it from stdin without showing a prompt */ fd = STDIN_FILENO; } /* read the password */ while (read(fd, &ch, 1) > 0) { if (ch == '\n' || ch == '\r') break; buffer_append_c(pass, ch); } if (tty) { if (restore_tio) (void)tcsetattr(fd, TCSAFLUSH, &old_tio); fputs("\n", stderr); fflush(stderr); i_close_fd(&fd); } } void askpass(const char *prompt, char *buf, size_t buf_size) { buffer_t str; buffer_create_from_data(&str, buf, buf_size); askpass_str(prompt, &str); buffer_append_c(&str, '\0'); } const char *t_askpass(const char *prompt) { string_t *str = t_str_new(32); askpass_str(prompt, str); return str_c(str); } dovecot-2.2.33.2/src/lib/test-seq-range-array.c0000644000175000017500000002027513165463624016021 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "array.h" #include "seq-range-array.h" static void boundaries_permute(uint32_t *input, unsigned int i, unsigned int count) { ARRAY_TYPE(seq_range) range; const struct seq_range *seqs; unsigned int seqs_count; uint32_t tmp; unsigned int j; if (i+1 < count) { for (j = i; j < count; j++) { tmp = input[i]; input[i] = input[j]; input[j] = tmp; boundaries_permute(input, i+1, count); tmp = input[i]; input[i] = input[j]; input[j] = tmp; } return; } t_array_init(&range, 4); for (i = 0; i < count; i++) seq_range_array_add(&range, input[i]); seqs = array_get(&range, &seqs_count); test_assert(seqs_count == 2); test_assert(seqs[0].seq1 == 0); test_assert(seqs[0].seq2 == 1); test_assert(seqs[1].seq1 == (uint32_t)-2); test_assert(seqs[1].seq2 == (uint32_t)-1); } static void test_seq_range_array_add_boundaries(void) { static uint32_t input[] = { 0, 1, (uint32_t)-2, (uint32_t)-1 }; boundaries_permute(input, 0, N_ELEMENTS(input)); } static void test_seq_range_array_add_merge(void) { ARRAY_TYPE(seq_range) range; test_begin("seq_range_array_add() merging"); t_array_init(&range, 8); seq_range_array_add(&range, 4); seq_range_array_add(&range, 1); seq_range_array_add(&range, 2); test_assert(array_count(&range) == 2); seq_range_array_add_range(&range, 1, (uint32_t)-1); test_assert(array_count(&range) == 1); seq_range_array_add_range(&range, 1, (uint32_t)-1); test_assert(array_count(&range) == 1); test_end(); } static void test_seq_range_array_remove_nth(void) { ARRAY_TYPE(seq_range) range; const struct seq_range *r; test_begin("seq_range_array_remove_nth()"); t_array_init(&range, 8); seq_range_array_add_range(&range, 1, 5); seq_range_array_add(&range, 7); seq_range_array_add_range(&range, 10,20); test_assert(array_count(&range) == 3); seq_range_array_remove_nth(&range, 0, 2); r = array_idx(&range, 0); test_assert(r->seq1 == 3 && r->seq2 == 5); seq_range_array_remove_nth(&range, 1, 4); r = array_idx(&range, 0); test_assert(r->seq1 == 3 && r->seq2 == 3); r = array_idx(&range, 1); test_assert(r->seq1 == 11 && r->seq2 == 20); seq_range_array_remove_nth(&range, 5, (uint32_t)-1); r = array_idx(&range, 1); test_assert(r->seq1 == 11 && r->seq2 == 14); test_assert(array_count(&range) == 2); test_end(); } static void test_seq_range_array_random(void) { #define SEQ_RANGE_TEST_BUFSIZE 100 #define SEQ_RANGE_TEST_COUNT 20000 unsigned char shadowbuf[SEQ_RANGE_TEST_BUFSIZE]; ARRAY_TYPE(seq_range) range; const struct seq_range *seqs; uint32_t seq1, seq2; unsigned int i, j, ret, ret2, count; int test = -1; ret = ret2 = 0; i_array_init(&range, 1); memset(shadowbuf, 0, sizeof(shadowbuf)); for (i = 0; i < SEQ_RANGE_TEST_COUNT; i++) { seq1 = rand() % SEQ_RANGE_TEST_BUFSIZE; seq2 = seq1 + rand() % (SEQ_RANGE_TEST_BUFSIZE - seq1); test = rand() % 4; switch (test) { case 0: ret = seq_range_array_add(&range, seq1) ? 0 : 1; /* FALSE == added */ ret2 = shadowbuf[seq1] == 0 ? 1 : 0; shadowbuf[seq1] = 1; break; case 1: ret = seq_range_array_add_range_count(&range, seq1, seq2); for (ret2 = 0; seq1 <= seq2; seq1++) { if (shadowbuf[seq1] == 0) { ret2++; shadowbuf[seq1] = 1; } } break; case 2: ret = seq_range_array_remove(&range, seq1) ? 1 : 0; ret2 = shadowbuf[seq1] != 0 ? 1 : 0; shadowbuf[seq1] = 0; break; case 3: ret = seq_range_array_remove_range(&range, seq1, seq2); for (ret2 = 0; seq1 <= seq2; seq1++) { if (shadowbuf[seq1] != 0) { ret2++; shadowbuf[seq1] = 0; } } break; } if (ret != ret2) break; seqs = array_get(&range, &count); for (j = 0, seq1 = 0; j < count; j++) { if (j > 0 && seqs[j-1].seq2+1 >= seqs[j].seq1) goto fail; for (; seq1 < seqs[j].seq1; seq1++) { if (shadowbuf[seq1] != 0) goto fail; } for (; seq1 <= seqs[j].seq2; seq1++) { if (shadowbuf[seq1] == 0) goto fail; } } i_assert(seq1 <= SEQ_RANGE_TEST_BUFSIZE); for (; seq1 < SEQ_RANGE_TEST_BUFSIZE; seq1++) { if (shadowbuf[seq1] != 0) goto fail; } } fail: if (i == SEQ_RANGE_TEST_COUNT) test_out("seq_range_array random", TRUE); else { test_out_reason("seq_range_array random", FALSE, t_strdup_printf("round %u test %d failed", i, test)); } array_free(&range); } static void test_seq_range_array_invert_minmax(uint32_t min, uint32_t max) { ARRAY_TYPE(seq_range) range = ARRAY_INIT; struct seq_range_iter iter; unsigned int n, inverse_mask, mask_inside, mask_size = max-min+1; uint32_t seq; i_assert(mask_size <= sizeof(unsigned int)*8); t_array_init(&range, 16); for (unsigned int mask = 0; mask < mask_size; mask++) { array_clear(&range); for (unsigned int i = 0; i < mask_size; i++) { if ((mask & (1 << i)) != 0) seq_range_array_add(&range, min+i); } seq_range_array_invert(&range, min, max); inverse_mask = 0; seq_range_array_iter_init(&iter, &range); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { test_assert(seq >= min && seq <= max); inverse_mask |= 1 << (seq-min); } mask_inside = ((1 << mask_size)-1); test_assert_idx((inverse_mask & ~mask_inside) == 0, mask); test_assert_idx(inverse_mask == (mask ^ mask_inside), mask); } } static void test_seq_range_array_invert(void) { test_begin("seq_range_array_invert()"); /* first numbers */ for (unsigned int min = 0; min <= 7; min++) { for (unsigned int max = min; max <= 7; max++) T_BEGIN { test_seq_range_array_invert_minmax(min, max); } T_END; } /* last numbers */ for (uint64_t min = 0xffffffff-7; min <= 0xffffffff; min++) { for (uint64_t max = min; max <= 0xffffffff; max++) T_BEGIN { test_seq_range_array_invert_minmax(min, max); } T_END; } test_end(); } static void test_seq_range_array_invert_edges(void) { const struct { int64_t a_seq1, a_seq2, b_seq1, b_seq2; int64_t resa_seq1, resa_seq2, resb_seq1, resb_seq2; } tests[] = { { -1, -1, -1, -1, 0, 0xffffffff, -1, -1 }, { 0, 0xffffffff, -1, -1, -1, -1, -1, -1 }, { 0, 0xfffffffe, -1, -1, 0xffffffff, 0xffffffff, -1, -1 }, { 1, 0xfffffffe, -1, -1, 0, 0, 0xffffffff, 0xffffffff }, { 1, 0xffffffff, -1, -1, 0, 0, -1, -1 }, { 0, 0, 0xffffffff, 0xffffffff, 1, 0xfffffffe, -1, -1 }, { 0xffffffff, 0xffffffff, -1, -1, 0, 0xfffffffe, -1, -1 }, }; ARRAY_TYPE(seq_range) range = ARRAY_INIT; const struct seq_range *result; unsigned int count; test_begin("seq_range_array_invert() edges"); for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN { t_array_init(&range, 10); if (tests[i].a_seq1 != -1) seq_range_array_add_range(&range, tests[i].a_seq1, tests[i].a_seq2); if (tests[i].b_seq1 != -1) seq_range_array_add_range(&range, tests[i].b_seq1, tests[i].b_seq2); seq_range_array_invert(&range, 0, 0xffffffff); result = array_get(&range, &count); if (tests[i].resa_seq1 == -1) test_assert_idx(count == 0, i); else { test_assert(result[0].seq1 == tests[i].resa_seq1); test_assert(result[0].seq2 == tests[i].resa_seq2); if (tests[i].resb_seq1 == -1) test_assert_idx(count == 1, i); else { test_assert(result[1].seq1 == tests[i].resb_seq1); test_assert(result[1].seq2 == tests[i].resb_seq2); } } } T_END; test_end(); } static void test_seq_range_create(ARRAY_TYPE(seq_range) *array, uint8_t byte) { unsigned int i; array_clear(array); for (i = 0; i < 8; i++) { if ((byte & (1 << i)) != 0) seq_range_array_add(array, i + 1); } } static void test_seq_range_array_have_common(void) { ARRAY_TYPE(seq_range) arr1, arr2; unsigned int i, j; bool ret1, ret2, success = TRUE; t_array_init(&arr1, 8); t_array_init(&arr2, 8); for (i = 0; i < 256; i++) { test_seq_range_create(&arr1, i); for (j = 0; j < 256; j++) { test_seq_range_create(&arr2, j); ret1 = seq_range_array_have_common(&arr1, &arr2); ret2 = (i & j) != 0; if (ret1 != ret2) success = FALSE; } } test_out("seq_range_array_have_common()", success); } void test_seq_range_array(void) { test_seq_range_array_add_boundaries(); test_seq_range_array_add_merge(); test_seq_range_array_remove_nth(); test_seq_range_array_invert(); test_seq_range_array_invert_edges(); test_seq_range_array_have_common(); test_seq_range_array_random(); } dovecot-2.2.33.2/src/lib/test-primes.c0000644000175000017500000000077413123174404014311 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "primes.h" void test_primes(void) { unsigned int i, j, num; bool success; success = primes_closest(0) > 0; for (num = 1; num < 1024; num++) { if (primes_closest(num) < num) success = FALSE; } for (i = 10; i < 32; i++) { num = (1U << i) - 100; for (j = 0; j < 200; j++, num++) { if (primes_closest(num) < num) success = FALSE; } } test_out("primes_closest()", success); } dovecot-2.2.33.2/src/lib/iostream-temp.c0000644000175000017500000002560713165463624014640 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "safe-mkstemp.h" #include "write-full.h" #include "istream-private.h" #include "ostream-private.h" #include "iostream-temp.h" #include #define IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT (1024*128) struct temp_ostream { struct ostream_private ostream; char *temp_path_prefix; enum iostream_temp_flags flags; size_t max_mem_size; struct istream *dupstream; uoff_t dupstream_offset, dupstream_start_offset; char *name; buffer_t *buf; int fd; bool fd_tried; uoff_t fd_size; }; static void o_stream_temp_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct temp_ostream *tstream = (struct temp_ostream *)stream; if (tstream->fd != -1) i_close_fd(&tstream->fd); if (tstream->buf != NULL) buffer_free(&tstream->buf); i_free(tstream->temp_path_prefix); i_free(tstream->name); } static int o_stream_temp_move_to_fd(struct temp_ostream *tstream) { string_t *path; if (tstream->fd_tried) return -1; tstream->fd_tried = TRUE; path = t_str_new(128); str_append(path, tstream->temp_path_prefix); tstream->fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1); if (tstream->fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } if (i_unlink(str_c(path)) < 0) { i_close_fd(&tstream->fd); return -1; } if (write_full(tstream->fd, tstream->buf->data, tstream->buf->used) < 0) { i_error("write(%s) failed: %m", str_c(path)); i_close_fd(&tstream->fd); return -1; } /* make the fd available also to o_stream_get_fd(), e.g. for unit tests */ tstream->ostream.fd = tstream->fd; tstream->fd_size = tstream->buf->used; buffer_free(&tstream->buf); return 0; } int o_stream_temp_move_to_memory(struct ostream *output) { struct temp_ostream *tstream = (struct temp_ostream *)output->real_stream; unsigned char buf[IO_BLOCK_SIZE]; uoff_t offset = 0; ssize_t ret = 0; i_assert(tstream->buf == NULL); tstream->buf = buffer_create_dynamic(default_pool, 8192); while (offset < tstream->ostream.ostream.offset && (ret = pread(tstream->fd, buf, sizeof(buf), offset)) > 0) { if ((size_t)ret > tstream->ostream.ostream.offset - offset) ret = tstream->ostream.ostream.offset - offset; buffer_append(tstream->buf, buf, ret); offset += ret; } if (ret < 0) { /* not really expecting this to happen */ i_error("iostream-temp %s: read(%s*) failed: %m", o_stream_get_name(&tstream->ostream.ostream), tstream->temp_path_prefix); tstream->ostream.ostream.stream_errno = EIO; return -1; } i_close_fd(&tstream->fd); tstream->ostream.fd = -1; return 0; } static ssize_t o_stream_temp_fd_sendv(struct temp_ostream *tstream, const struct const_iovec *iov, unsigned int iov_count) { size_t bytes = 0; unsigned int i; for (i = 0; i < iov_count; i++) { if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) { i_error("iostream-temp %s: write(%s*) failed: %m - moving to memory", o_stream_get_name(&tstream->ostream.ostream), tstream->temp_path_prefix); if (o_stream_temp_move_to_memory(&tstream->ostream.ostream) < 0) return -1; for (; i < iov_count; i++) { buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len); bytes += iov[i].iov_len; tstream->ostream.ostream.offset += iov[i].iov_len; } i_assert(tstream->fd_tried); return bytes; } bytes += iov[i].iov_len; tstream->ostream.ostream.offset += iov[i].iov_len; } tstream->fd_size += bytes; return bytes; } static ssize_t o_stream_temp_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct temp_ostream *tstream = (struct temp_ostream *)stream; ssize_t ret = 0; unsigned int i; tstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP; if (tstream->fd != -1) return o_stream_temp_fd_sendv(tstream, iov, iov_count); for (i = 0; i < iov_count; i++) { if (tstream->buf->used + iov[i].iov_len > tstream->max_mem_size) { if (o_stream_temp_move_to_fd(tstream) == 0) { i_assert(tstream->fd != -1); return o_stream_temp_fd_sendv(tstream, iov+i, iov_count-i); } /* failed to move to temp fd, just keep it in memory */ } buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len); ret += iov[i].iov_len; stream->ostream.offset += iov[i].iov_len; } return ret; } static int o_stream_temp_dup_cancel(struct temp_ostream *tstream) { struct istream *input; uoff_t size = tstream->dupstream_offset - tstream->dupstream_start_offset; off_t ret; i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset); input = i_stream_create_limit(tstream->dupstream, size); do { ret = io_stream_copy(&tstream->ostream.ostream, input); } while (input->v_offset < tstream->dupstream_offset && ret > 0); if (ret < 0 && tstream->ostream.ostream.stream_errno == 0) { i_assert(input->stream_errno != 0); tstream->ostream.ostream.stream_errno = input->stream_errno; } i_stream_destroy(&input); i_stream_unref(&tstream->dupstream); return ret < 0 ? -1 : 0; } static off_t o_stream_temp_dup_istream(struct temp_ostream *outstream, struct istream *instream) { uoff_t in_size; off_t ret; if (!instream->readable_fd || i_stream_get_fd(instream) == -1) return 0; if (i_stream_get_size(instream, TRUE, &in_size) <= 0) { if (outstream->dupstream != NULL) return o_stream_temp_dup_cancel(outstream); return 0; } i_assert(instream->v_offset <= in_size); if (outstream->dupstream == NULL) { outstream->dupstream = instream; outstream->dupstream_start_offset = instream->v_offset; i_stream_ref(outstream->dupstream); } else { if (outstream->dupstream != instream || outstream->dupstream_offset != instream->v_offset || outstream->dupstream_offset > in_size) return o_stream_temp_dup_cancel(outstream); } ret = in_size - instream->v_offset; i_stream_seek(instream, in_size); outstream->dupstream_offset = instream->v_offset; outstream->ostream.ostream.offset = outstream->dupstream_offset - outstream->dupstream_start_offset; return ret; } static off_t o_stream_temp_send_istream(struct ostream_private *_outstream, struct istream *instream) { struct temp_ostream *outstream = (struct temp_ostream *)_outstream; uoff_t orig_offset; off_t ret; if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) { orig_offset = outstream->dupstream_offset; if ((ret = o_stream_temp_dup_istream(outstream, instream)) > 0) { i_assert(outstream->dupstream_offset >= orig_offset); return outstream->dupstream_offset - orig_offset; } if (ret < 0) return -1; outstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP; } return io_stream_copy(&outstream->ostream.ostream, instream); } static int o_stream_temp_write_at(struct ostream_private *stream, const void *data, size_t size, uoff_t offset) { struct temp_ostream *tstream = (struct temp_ostream *)stream; if (tstream->fd == -1) { i_assert(stream->ostream.offset == tstream->buf->used); buffer_write(tstream->buf, offset, data, size); stream->ostream.offset = tstream->buf->used; } else { if (pwrite_full(tstream->fd, data, size, offset) < 0) { stream->ostream.stream_errno = errno; i_close_fd(&tstream->fd); return -1; } if (tstream->fd_size < offset + size) tstream->fd_size = offset + size; } return 0; } static int o_stream_temp_seek(struct ostream_private *_stream, uoff_t offset) { _stream->ostream.offset = offset; return 0; } struct ostream *iostream_temp_create(const char *temp_path_prefix, enum iostream_temp_flags flags) { return iostream_temp_create_named(temp_path_prefix, flags, ""); } struct ostream *iostream_temp_create_named(const char *temp_path_prefix, enum iostream_temp_flags flags, const char *name) { return iostream_temp_create_sized(temp_path_prefix, flags, name, IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT); } struct ostream *iostream_temp_create_sized(const char *temp_path_prefix, enum iostream_temp_flags flags, const char *name, size_t max_mem_size) { struct temp_ostream *tstream; struct ostream *output; tstream = i_new(struct temp_ostream, 1); tstream->ostream.sendv = o_stream_temp_sendv; tstream->ostream.send_istream = o_stream_temp_send_istream; tstream->ostream.write_at = o_stream_temp_write_at; tstream->ostream.seek = o_stream_temp_seek; tstream->ostream.iostream.close = o_stream_temp_close; tstream->temp_path_prefix = i_strdup(temp_path_prefix); tstream->flags = flags; tstream->max_mem_size = max_mem_size; tstream->buf = buffer_create_dynamic(default_pool, 8192); tstream->fd = -1; output = o_stream_create(&tstream->ostream, NULL, -1); tstream->name = i_strdup(name); if (name[0] == '\0') { o_stream_set_name(output, t_strdup_printf( "(temp iostream in %s)", temp_path_prefix)); } else { o_stream_set_name(output, t_strdup_printf( "(temp iostream in %s for %s)", temp_path_prefix, name)); } return output; } static void iostream_temp_buf_destroyed(buffer_t *buf) { buffer_free(&buf); } struct istream *iostream_temp_finish(struct ostream **output, size_t max_buffer_size) { struct temp_ostream *tstream = (struct temp_ostream *)(*output)->real_stream; struct istream *input, *input2; uoff_t abs_offset, size; const char *for_path; int fd; if (tstream->name[0] == '\0') for_path = ""; else for_path = t_strdup_printf(" for %s", tstream->name); if (tstream->dupstream != NULL && !tstream->dupstream->closed) { abs_offset = tstream->dupstream->real_stream->abs_start_offset + tstream->dupstream_start_offset; size = tstream->dupstream_offset - tstream->dupstream_start_offset; fd = dup(i_stream_get_fd(tstream->dupstream)); if (fd == -1) input = i_stream_create_error_str(errno, "dup() failed: %m"); else { input2 = i_stream_create_fd_autoclose(&fd, max_buffer_size); i_stream_seek(input2, abs_offset); input = i_stream_create_limit(input2, size); i_stream_unref(&input2); } i_stream_set_name(input, t_strdup_printf( "(Temp file in %s%s, from %s)", tstream->temp_path_prefix, for_path, i_stream_get_name(tstream->dupstream))); i_stream_unref(&tstream->dupstream); } else if (tstream->dupstream != NULL) { /* return the original failed stream. */ input = tstream->dupstream; } else if (tstream->fd != -1) { int fd = tstream->fd; input = i_stream_create_fd_autoclose(&tstream->fd, max_buffer_size); i_stream_set_name(input, t_strdup_printf( "(Temp file fd %d in %s%s, %"PRIuUOFF_T" bytes)", fd, tstream->temp_path_prefix, for_path, tstream->fd_size)); } else { input = i_stream_create_from_data(tstream->buf->data, tstream->buf->used); i_stream_set_name(input, t_strdup_printf( "(Temp buffer in %s%s, %"PRIuSIZE_T" bytes)", tstream->temp_path_prefix, for_path, tstream->buf->used)); i_stream_add_destroy_callback(input, iostream_temp_buf_destroyed, tstream->buf); tstream->buf = NULL; } o_stream_destroy(output); return input; } dovecot-2.2.33.2/src/lib/mmap-util.c0000644000175000017500000000233613123174404013736 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mmap-util.h" #include void *mmap_file(int fd, size_t *length, int prot) { struct stat st; if (fstat(fd, &st) < 0) return MAP_FAILED; #if OFF_T_MAX > SSIZE_T_MAX if (st.st_size > SSIZE_T_MAX) { /* too large file to map into memory */ errno = EFBIG; return MAP_FAILED; } #endif *length = (size_t)st.st_size; if (*length == 0) return NULL; i_assert(*length > 0 && *length < SSIZE_T_MAX); return mmap(NULL, *length, prot, MAP_SHARED, fd, 0); } void *mmap_ro_file(int fd, size_t *length) { return mmap_file(fd, length, PROT_READ); } void *mmap_rw_file(int fd, size_t *length) { return mmap_file(fd, length, PROT_READ | PROT_WRITE); } #undef madvise int my_madvise(void *start ATTR_UNUSED, size_t length ATTR_UNUSED, int advice ATTR_UNUSED) { #ifdef HAVE_MADVISE /* Ignore ENOSYS errors, which happen if the kernel hasn't implemented the syscall even if libc has. */ if (madvise(start, length, advice) < 0 && errno != ENOSYS) return -1; #endif return 0; } size_t mmap_get_page_size(void) { static size_t size = 0; if (size != 0) return size; size = getpagesize(); return size; } dovecot-2.2.33.2/src/lib/iso8601-date.h0000644000175000017500000000165413123174404014064 00000000000000#ifndef ISO8601_DATE_H #define ISO8601_DATE_H /* Parses ISO8601 (RFC3339) date-time string. timezone_offset is filled with the timezone's difference to UTC in minutes. Returned time_t timestamp is compensated for time zone. */ bool iso8601_date_parse(const unsigned char *data, size_t size, time_t *timestamp_r, int *timezone_offset_r); /* Equal to iso8601_date_parse, but writes uncompensated timestamp to tm_r. */ bool iso8601_date_parse_tm(const unsigned char *data, size_t size, struct tm *tm_r, int *timezone_offset_r); /* Create ISO8601 date-time string from given time struct in specified timezone. A zone offset of zero will not to 'Z', but '+00:00'. If zone_offset == INT_MAX, the time zone will be 'Z'. */ const char *iso8601_date_create_tm(struct tm *tm, int zone_offset); /* Create ISO8601 date-time string from given time in local timezone. */ const char *iso8601_date_create(time_t timestamp); #endif dovecot-2.2.33.2/src/lib/test-hex-binary.c0000644000175000017500000000314413123174404015052 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" #include "str.h" #include "hex-binary.h" static void test_binary_to_hex(void) { static unsigned char input[] = { 0xff, 0x00, 0x01, 0xb3 }; static char *output_lcase = "ff0001b3"; static char *output_ucase = "FF0001B3"; string_t *str; test_begin("binary to hex"); test_assert(strcmp(binary_to_hex(input, sizeof(input)), output_lcase) == 0); test_end(); test_begin("binary to hex ucase"); test_assert(strcmp(binary_to_hex_ucase(input, sizeof(input)), output_ucase) == 0); test_end(); test_begin("binary to hex ucase"); str = t_str_new(32); str_append_c(str, '<'); binary_to_hex_append(str, input, sizeof(input)); str_append_c(str, '>'); test_assert(strcmp(str_c(str), t_strconcat("<", output_lcase, ">", NULL)) == 0); test_end(); } static void test_hex_to_binary(void) { static const char *ok_input = "0001fEFf"; static unsigned char ok_output[] = { 0x00, 0x01, 0xfe, 0xff }; static const char *error_input[] = { "00 01", "0x01", "0g" }; buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 10); unsigned int i; test_begin("hex to binary"); test_assert(hex_to_binary("", buf) == 0); test_assert(buf->used == 0); test_assert(hex_to_binary(ok_input, buf) == 0); test_assert(buf->used == N_ELEMENTS(ok_output)); test_assert(memcmp(buf->data, ok_output, buf->used) == 0); for (i = 0; i < N_ELEMENTS(error_input); i++) test_assert(hex_to_binary(error_input[i], buf) == -1); test_end(); } void test_hex_binary(void) { test_binary_to_hex(); test_hex_to_binary(); } dovecot-2.2.33.2/src/lib/compat.h0000644000175000017500000002032213165463624013327 00000000000000#ifndef COMPAT_H #define COMPAT_H #if defined (HAVE_INTTYPES_H) && (defined(__osf__) || defined(_HPUX_SOURCE)) # include #endif /* well, this is obviously wrong since it assumes it's 64bit, but older GCCs don't define it and we really want it. */ #ifndef LLONG_MAX # define LLONG_MAX 9223372036854775807LL #endif #if ((__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) && \ defined(HAVE_TYPEOF)) && !defined(__cplusplus) # define HAVE_TYPE_CHECKS #endif /* We really want NULL to be a pointer, since we have various type-checks that may result in compiler warnings/errors if it's not. Do this only when type checking is used - it's not otherwise needed and causes compiling problems with e.g. Sun C compiler. */ #ifdef HAVE_TYPE_CHECKS # undef NULL # define NULL ((void *)0) #endif #ifndef __cplusplus #ifdef HAVE__BOOL typedef _Bool bool; #else typedef int bool; #endif #endif #if defined (HAVE_UOFF_T) /* native support */ #elif defined (UOFF_T_INT) typedef unsigned int uoff_t; #elif defined (UOFF_T_LONG) typedef unsigned long uoff_t; #elif defined (UOFF_T_LONG_LONG) typedef unsigned long long uoff_t; #else # error uoff_t size not set #endif #ifndef HAVE_UINTMAX_T # if SIZEOF_LONG_LONG > 0 typedef unsigned long long uintmax_t; # else typedef unsigned long uintmax_t; # endif #endif #ifndef HAVE_UINT_FAST32_T # if SIZEOF_INT >= 4 typedef unsigned int uint_fast32_t; # else typedef unsigned long uint_fast32_t; # endif #endif #ifndef HAVE_SOCKLEN_T typedef int socklen_t; #endif #ifdef HAVE_SYS_SYSMACROS_H # include # ifdef HAVE_SYS_MKDEV_H # include /* UnixWare */ # endif # define CMP_DEV_T(a, b) (major(a) == major(b) && minor(a) == minor(b)) #elif !defined (DEV_T_STRUCT) # define CMP_DEV_T(a, b) ((a) == (b)) #else # error I do not know how to compare dev_t #endif #ifdef HAVE_STAT_XTIM # define HAVE_ST_NSECS # define ST_ATIME_NSEC(st) ((unsigned long)(st).st_atim.tv_nsec) # define ST_MTIME_NSEC(st) ((unsigned long)(st).st_mtim.tv_nsec) # define ST_CTIME_NSEC(st) ((unsigned long)(st).st_ctim.tv_nsec) #elif defined (HAVE_STAT_XTIMESPEC) # define HAVE_ST_NSECS # define ST_ATIME_NSEC(st) ((unsigned long)(st).st_atimespec.tv_nsec) # define ST_MTIME_NSEC(st) ((unsigned long)(st).st_mtimespec.tv_nsec) # define ST_CTIME_NSEC(st) ((unsigned long)(st).st_ctimespec.tv_nsec) #else # define ST_ATIME_NSEC(st) 0UL # define ST_MTIME_NSEC(st) 0UL # define ST_CTIME_NSEC(st) 0UL #endif #ifdef HAVE_ST_NSECS /* TRUE if a nanosecond timestamp from struct stat matches another nanosecond. If nanoseconds aren't supported in struct stat, returns always TRUE (useful with NFS if some hosts support nanoseconds and others don't). */ # define ST_NTIMES_EQUAL(ns1, ns2) ((ns1) == (ns2)) #else # define ST_NTIMES_EQUAL(ns1, ns2) TRUE #endif #define CMP_ST_MTIME(st1, st2) \ ((st1)->st_mtime == (st2)->st_mtime && \ ST_NTIMES_EQUAL(ST_MTIME_NSEC(*(st1)), ST_MTIME_NSEC(*(st2)))) #define CMP_ST_CTIME(st1, st2) \ ((st1)->st_ctime == (st2)->st_ctime && \ ST_NTIMES_EQUAL(ST_CTIME_NSEC(*(st1)), ST_CTIME_NSEC(*(st2)))) /* strcasecmp(), strncasecmp() */ #ifndef HAVE_STRCASECMP # ifdef HAVE_STRICMP # define strcasecmp stricmp # define strncasecmp strnicmp # else # define strcasecmp i_my_strcasecmp # define strncasecmp i_my_strncasecmp int i_my_strcasecmp(const char *s1, const char *s2); int i_my_strncasecmp(const char *s1, const char *s2, size_t max_chars); # endif #endif #ifndef HAVE_INET_ATON # include # include # include # define inet_aton i_my_inet_aton int i_my_inet_aton(const char *cp, struct in_addr *inp); #endif #ifndef HAVE_VSYSLOG # define vsyslog i_my_vsyslog void i_my_vsyslog(int priority, const char *format, va_list args); #endif #ifndef HAVE_GETPAGESIZE # define getpagesize i_my_getpagesize int i_my_getpagesize(void); #endif #ifndef HAVE_FDATASYNC # define fdatasync fsync #endif struct const_iovec { const void *iov_base; size_t iov_len; }; #ifndef HAVE_STRUCT_IOVEC struct iovec { void *iov_base; size_t iov_len; }; #endif /* IOV_MAX should be in limits.h nowadays. Linux still (2005) requires defining _XOPEN_SOURCE to get that value. UIO_MAXIOV works with it though, so use it instead. 16 is the lowest acceptable value for all OSes. */ #ifndef IOV_MAX # include # ifdef UIO_MAXIOV # define IOV_MAX UIO_MAXIOV # else # define IOV_MAX 16 # endif #endif #ifndef HAVE_WRITEV # define writev i_my_writev struct iovec; ssize_t i_my_writev(int fd, const struct iovec *iov, int iov_len); #endif #if !defined(HAVE_PREAD) || defined(PREAD_WRAPPERS) || defined(PREAD_BROKEN) # ifndef IN_COMPAT_C # define pread i_my_pread # define pwrite i_my_pwrite # endif ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset); ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset); #endif #ifndef HAVE_SETEUID # define seteuid i_my_seteuid int i_my_seteuid(uid_t euid); #endif #ifndef HAVE_SETEGID # define setegid i_my_setegid int i_my_setegid(gid_t egid); #endif #ifndef HAVE_LIBGEN_H # define basename i_my_basename char *i_my_basename(char *path); #endif #ifdef HAVE_OLD_VSNPRINTF # include # define vsnprintf i_my_vsnprintf int i_my_vsnprintf(char *str, size_t size, const char *format, va_list ap); #endif #ifndef HAVE_CLOCK_GETTIME # include # undef CLOCK_REALTIME # define CLOCK_REALTIME 1 # define clock_gettime i_my_clock_gettime int i_my_clock_gettime(int clk_id, struct timespec *tp); #endif /* ctype.h isn't safe with signed chars, use our own instead if really needed */ #define i_toupper(x) ((char) toupper((int) (unsigned char) (x))) #define i_tolower(x) ((char) tolower((int) (unsigned char) (x))) #define i_isalnum(x) isalnum((int) (unsigned char) (x)) #define i_isalpha(x) isalpha((int) (unsigned char) (x)) #define i_isascii(x) isascii((int) (unsigned char) (x)) #define i_isblank(x) isblank((int) (unsigned char) (x)) #define i_iscntrl(x) iscntrl((int) (unsigned char) (x)) #define i_isdigit(x) isdigit((int) (unsigned char) (x)) #define i_isgraph(x) isgraph((int) (unsigned char) (x)) #define i_islower(x) islower((int) (unsigned char) (x)) #define i_isprint(x) isprint((int) (unsigned char) (x)) #define i_ispunct(x) ispunct((int) (unsigned char) (x)) #define i_isspace(x) isspace((int) (unsigned char) (x)) #define i_isupper(x) isupper((int) (unsigned char) (x)) #define i_isxdigit(x) isxdigit((int) (unsigned char) (x)) #ifndef EOVERFLOW # define EOVERFLOW ERANGE #endif #ifdef EDQUOT # define ENOSPACE(errno) ((errno) == ENOSPC || (errno) == EDQUOT) # define ENOQUOTA(errno) ((errno) == EDQUOT) #else /* probably all modern OSes have EDQUOT, but just in case one doesn't assume that ENOSPC is the same as "over quota". */ # define ENOSPACE(errno) ((errno) == ENOSPC) # define ENOQUOTA(errno) ((errno) == ENOSPC) #endif /* EPERM is returned sometimes if device doesn't support such modification */ #ifdef EROFS # define ENOACCESS(errno) \ ((errno) == EACCES || (errno) == EROFS || (errno) == EPERM) #else # define ENOACCESS(errno) ((errno) == EACCES || (errno) == EPERM) #endif #define ENOTFOUND(errno) \ ((errno) == ENOENT || (errno) == ENOTDIR || \ (errno) == ELOOP || (errno) == ENAMETOOLONG) #define ECANTLINK(errno) \ ((errno) == EXDEV || (errno) == EMLINK || (errno) == EPERM) /* Returns TRUE if unlink() failed because it attempted to delete a directory */ #define UNLINK_EISDIR(errno) \ ((errno) == EPERM || /* POSIX */ \ (errno) == EISDIR) /* Linux */ /* EBUSY is given by some NFS implementations */ #define EDESTDIREXISTS(errno) \ ((errno) == EEXIST || (errno) == ENOTEMPTY || (errno) == EBUSY) /* fstat() returns ENOENT instead of ESTALE with some Linux versions */ #define ESTALE_FSTAT(errno) \ ((errno) == ESTALE || (errno) == ENOENT) #if !defined(_POSIX_SYNCHRONIZED_IO) && \ defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060) /* OS X Snow Leopard has fdatasync(), but no prototype for it. */ int fdatasync(int); #endif /* Try to keep IO operations at least this size */ #ifndef IO_BLOCK_SIZE # define IO_BLOCK_SIZE 8192 #endif #if !defined(PIPE_BUF) && defined(_POSIX_PIPE_BUF) # define PIPE_BUF (8 * _POSIX_PIPE_BUF) /* for HURD */ #endif #endif dovecot-2.2.33.2/src/lib/time-util.c0000644000175000017500000000425213123174404013741 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "time-util.h" #include #define STRFTIME_MAX_BUFSIZE (1024*64) int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2) { if (tv1->tv_sec < tv2->tv_sec) return -1; if (tv1->tv_sec > tv2->tv_sec) return 1; if (tv1->tv_usec < tv2->tv_usec) return -1; if (tv1->tv_usec > tv2->tv_usec) return 1; return 0; } int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2, unsigned int usec_margin) { if (tv1->tv_sec < tv2->tv_sec) return -1; if (tv1->tv_sec > tv2->tv_sec) return 1; if ((tv2->tv_usec - tv1->tv_usec) > (int)usec_margin) return -1; if ((tv1->tv_usec - tv2->tv_usec) > (int)usec_margin) return 1; return 0; } int timeval_diff_msecs(const struct timeval *tv1, const struct timeval *tv2) { return timeval_diff_usecs(tv1, tv2) / 1000; } long long timeval_diff_usecs(const struct timeval *tv1, const struct timeval *tv2) { time_t secs; int usecs; secs = tv1->tv_sec - tv2->tv_sec; usecs = tv1->tv_usec - tv2->tv_usec; if (usecs < 0) { secs--; usecs += 1000000; } return ((long long)secs * 1000000LL) + usecs; } time_t time_to_local_day_start(time_t t) { const struct tm *day_tm; struct tm tm; time_t new_start_time; day_tm = localtime(&t); i_zero(&tm); tm.tm_year = day_tm->tm_year; tm.tm_mon = day_tm->tm_mon; tm.tm_mday = day_tm->tm_mday; tm.tm_isdst = -1; new_start_time = mktime(&tm); i_assert(new_start_time != (time_t)-1); return new_start_time; } static const char *strftime_real(const char *fmt, const struct tm *tm) { size_t bufsize = strlen(fmt) + 32; char *buf = t_buffer_get(bufsize); size_t ret; while ((ret = strftime(buf, bufsize, fmt, tm)) == 0) { bufsize *= 2; i_assert(bufsize <= STRFTIME_MAX_BUFSIZE); buf = t_buffer_get(bufsize); } t_buffer_alloc(ret + 1); return buf; } const char *t_strftime(const char *fmt, const struct tm *tm) { return strftime_real(fmt, tm); } const char *t_strflocaltime(const char *fmt, time_t t) { return strftime_real(fmt, localtime(&t)); } const char *t_strfgmtime(const char *fmt, time_t t) { return strftime_real(fmt, gmtime(&t)); } dovecot-2.2.33.2/src/lib/istream-tee.h0000644000175000017500000000132313123174404014250 00000000000000#ifndef ISTREAM_TEE_H #define ISTREAM_TEE_H /* Tee can be used to create multiple child input streams which can access a single non-blocking input stream in a way that data isn't removed from memory until all child streams have consumed the input. If the stream's buffer gets full because some child isn't consuming the data, other streams get returned 0 by i_stream_read(). */ struct tee_istream *tee_i_stream_create(struct istream *input); /* Returns TRUE if last read() operation returned 0, because it was waiting for another tee stream to read more of its data. */ bool tee_i_stream_child_is_waiting(struct istream *input); struct istream *tee_i_stream_create_child(struct tee_istream *tee); #endif dovecot-2.2.33.2/src/lib/ioloop-notify-fd.c0000644000175000017500000000212713123174404015225 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop-private.h" #include "ioloop-notify-fd.h" #if defined(IOLOOP_NOTIFY_INOTIFY) struct io *io_notify_fd_add(struct ioloop_notify_fd_context *ctx, int fd, io_callback_t *callback, void *context) { struct io_notify *io; io = i_new(struct io_notify, 1); io->io.condition = IO_NOTIFY; io->io.callback = callback; io->io.context = context; io->io.ioloop = current_ioloop; io->fd = fd; if (ctx->notifies != NULL) { ctx->notifies->prev = io; io->next = ctx->notifies; } ctx->notifies = io; return &io->io; } void io_notify_fd_free(struct ioloop_notify_fd_context *ctx, struct io_notify *io) { if (io->prev != NULL) io->prev->next = io->next; else ctx->notifies = io->next; if (io->next != NULL) io->next->prev = io->prev; i_free(io); } struct io_notify * io_notify_fd_find(struct ioloop_notify_fd_context *ctx, int fd) { struct io_notify *io; for (io = ctx->notifies; io != NULL; io = io->next) { if (io->fd == fd) return io; } return NULL; } #endif dovecot-2.2.33.2/src/lib/ioloop-kqueue.c0000644000175000017500000001074313123174404014630 00000000000000/* * BSD kqueue() based ioloop handler. * * Copyright (c) 2005 Vaclav Haisman */ #include "lib.h" #ifdef IOLOOP_KQUEUE #include "array.h" #include "fd-close-on-exec.h" #include "ioloop-private.h" #include #include #include #include #include /* kevent.udata's type just has to be different in NetBSD than in FreeBSD and OpenBSD.. */ #ifdef __NetBSD__ # define MY_EV_SET(a, b, c, d, e, f, g) \ EV_SET(a, b, c, d, e, f, (intptr_t)g) #else # define MY_EV_SET(a, b, c, d, e, f, g) \ EV_SET(a, b, c, d, e, f, g) #endif struct ioloop_handler_context { int kq; unsigned int deleted_count; ARRAY(struct kevent) events; }; void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) { struct ioloop_handler_context *ctx; ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); ctx->kq = kqueue(); if (ctx->kq < 0) i_fatal("kqueue() in io_loop_handler_init() failed: %m"); fd_close_on_exec(ctx->kq, TRUE); i_array_init(&ctx->events, initial_fd_count); } void io_loop_handler_deinit(struct ioloop *ioloop) { if (close(ioloop->handler_context->kq) < 0) i_error("close(kqueue) in io_loop_handler_deinit() failed: %m"); array_free(&ioloop->handler_context->events); i_free(ioloop->handler_context); } void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct kevent ev; if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) { MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_ADD, 0, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_panic("kevent(EV_ADD, READ, %d) failed: %m", io->fd); } if ((io->io.condition & IO_WRITE) != 0) { MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_ADD, 0, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_panic("kevent(EV_ADD, WRITE, %d) failed: %m", io->fd); } /* allow kevent() to return the maximum number of events by keeping space allocated for each handle */ if (ctx->deleted_count > 0) ctx->deleted_count--; else array_append_zero(&ctx->events); } void io_loop_handle_remove(struct io_file *io, bool closed) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct kevent ev; i_assert(io->io.condition != 0); if ((io->io.condition & (IO_READ | IO_ERROR)) != 0 && !closed) { MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); } if ((io->io.condition & IO_WRITE) != 0 && !closed) { MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); } io->io.condition = 0; /* since we're not freeing memory in any case, just increase deleted counter so next handle_add() can just decrease it insteading of appending to the events array */ ctx->deleted_count++; i_assert(io->refcount > 0); if (--io->refcount == 0) i_free(io); } void io_loop_handler_run_internal(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct kevent *events; const struct kevent *event; struct timeval tv; struct timespec ts; struct io_file *io; unsigned int events_count; int ret, i, msecs; /* get the time left for next timeout task */ msecs = io_loop_get_wait_time(ioloop, &tv); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; /* wait for events */ events = array_get_modifiable(&ctx->events, &events_count); if (events_count > 0) { ret = kevent (ctx->kq, NULL, 0, events, events_count, &ts); if (ret < 0 && errno != EINTR) { i_panic("kevent(events=%u, ts=%ld.%u) failed: %m", events_count, (long)ts.tv_sec, (unsigned int)ts.tv_nsec); } } else { if (msecs < 0) i_panic("BUG: No IOs or timeouts set. Not waiting for infinity."); usleep(msecs * 1000); ret = 0; } /* reference all IOs */ for (i = 0; i < ret; i++) { io = (void *)events[i].udata; i_assert(io->refcount > 0); io->refcount++; } /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); for (i = 0; i < ret; i++) { /* io_loop_handle_add() may cause events array reallocation, so we have use array_idx() */ event = array_idx(&ctx->events, i); io = (void *)event->udata; /* callback is NULL if io_remove() was already called */ if (io->io.callback != NULL) io_loop_call_io(&io->io); i_assert(io->refcount > 0); if (--io->refcount == 0) i_free(io); } } #endif dovecot-2.2.33.2/src/lib/str-find.h0000644000175000017500000000140413123174404013557 00000000000000#ifndef STR_FIND_H #define STR_FIND_H struct str_find_context; struct str_find_context *str_find_init(pool_t pool, const char *key); void str_find_deinit(struct str_find_context **ctx); /* Returns TRUE if key is found. It's possible to send the data in arbitrary blocks and have the key still match. */ bool str_find_more(struct str_find_context *ctx, const unsigned char *data, size_t size); /* After str_find_more() has returned TRUE, this function returns the end position in the previous data block where the key had matched. */ size_t str_find_get_match_end_pos(struct str_find_context *ctx); /* Reset input data. The next str_find_more() call won't try to match the key to earlier data. */ void str_find_reset(struct str_find_context *ctx); #endif dovecot-2.2.33.2/src/lib/strnum.h0000644000175000017500000001476013123174404013372 00000000000000#ifndef STRNUM_H #define STRNUM_H /* str_to_*() functions return 0 if string is nothing more than a valid number in valid range. Otherwise -1 is returned and num_r is left untouched str_parse_*() helpers do not require the number to be the entire string and pass back the pointer just past a valid parsed integer in endp_r if it is non-NULL. What is written to endp_r in error cases is undefined. */ /* * Unsigned decimal */ int str_to_uint(const char *str, unsigned int *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint(const char *str, unsigned int *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ulong(const char *str, unsigned long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ulong(const char *str, unsigned long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ullong(const char *str, unsigned long long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ullong(const char *str, unsigned long long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint32(const char *str, uint32_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint32(const char *str, uint32_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint64(const char *str, uint64_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint64(const char *str, uint64_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uintmax(const char *str, uintmax_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uintmax(const char *str, uintmax_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); /* Returns TRUE if str is a valid unsigned number that equals to num. */ bool str_uint_equals(const char *str, uintmax_t num); /* * Unsigned hexadecimal */ int str_to_uint_hex(const char *str, unsigned int *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint_hex(const char *str, unsigned int *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ulong_hex(const char *str, unsigned long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ulong_hex(const char *str, unsigned long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ullong_hex(const char *str, unsigned long long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ullong_hex(const char *str, unsigned long long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint32_hex(const char *str, uint32_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint32_hex(const char *str, uint32_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint64_hex(const char *str, uint64_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint64_hex(const char *str, uint64_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uintmax_hex(const char *str, uintmax_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uintmax_hex(const char *str, uintmax_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); /* * Unsigned octal */ int str_to_uint_oct(const char *str, unsigned int *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint_oct(const char *str, unsigned int *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ulong_oct(const char *str, unsigned long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ulong_oct(const char *str, unsigned long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_ullong_oct(const char *str, unsigned long long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_ullong_oct(const char *str, unsigned long long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint32_oct(const char *str, uint32_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint32_oct(const char *str, uint32_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uint64_oct(const char *str, uint64_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uint64_oct(const char *str, uint64_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_uintmax_oct(const char *str, uintmax_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uintmax_oct(const char *str, uintmax_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); /* * Signed */ int str_to_int(const char *str, int *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_int(const char *str, int *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_long(const char *str, long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_long(const char *str, long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_llong(const char *str, long long *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_llong(const char *str, long long *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_int32(const char *str, int32_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_int32(const char *str, int32_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_int64(const char *str, int64_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_int64(const char *str, int64_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_intmax(const char *str, intmax_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_intmax(const char *str, intmax_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); /* * Special numeric types */ int str_to_uid(const char *str, uid_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_to_gid(const char *str, gid_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_to_pid(const char *str, pid_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_to_ino(const char *str, ino_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_to_uoff(const char *str, uoff_t *num_r) ATTR_WARN_UNUSED_RESULT; int str_parse_uoff(const char *str, uoff_t *num_r, const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); int str_to_time(const char *str, time_t *num_r) ATTR_WARN_UNUSED_RESULT; /* * Utility */ /* Return TRUE if all characters in string are numbers. Stop when `end_char' is found from string. */ bool str_is_numeric(const char *str, char end_char) ATTR_PURE; /* Return TRUE when string has one or more numbers, followed with zero or one dot, followed with at least one number. */ bool str_is_float(const char *str, char end_char) ATTR_PURE; /* Returns human readable string about what is wrong with the string. This function assumes that str_to_*() had already returned -1 for the string. */ const char *str_num_error(const char *str); #endif dovecot-2.2.33.2/src/lib/lib.h0000644000175000017500000000607413165463624012622 00000000000000#ifndef LIB_H #define LIB_H /* default lib includes */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* default system includes - keep these at minimum.. */ #include /* Solaris defines NULL wrong unless this is used */ #include #include /* strcmp() etc. */ #ifdef HAVE_STRINGS_H # include /* strcasecmp() etc. */ #endif #include /* va_list is used everywhere */ #include /* INT_MAX, etc. */ #include /* error checking is good */ #include /* many other includes want this */ #ifdef HAVE_STDINT_H # include /* C99 int types, we mostly need uintmax_t */ #endif #include "compat.h" #include "macros.h" #include "failures.h" #include "malloc-overflow.h" #include "data-stack.h" #include "mempool.h" #include "imem.h" #include "byteorder.h" #include "rand.h" typedef struct buffer buffer_t; typedef struct buffer string_t; struct istream; struct ostream; typedef void lib_atexit_callback_t(void); #include "array-decl.h" /* ARRAY*()s may exist in any header */ #include "bits.h" #include "hash-decl.h" /* HASH_TABLE*()s may exist in any header */ #include "strfuncs.h" #include "strnum.h" #define LIB_ATEXIT_PRIORITY_HIGH -10 #define LIB_ATEXIT_PRIORITY_DEFAULT 0 #define LIB_ATEXIT_PRIORITY_LOW 10 /* /dev/null opened as O_WRONLY. Opened at lib_init(), so it can be accessed also inside chroots. */ extern int dev_null_fd; int close_keep_errno(int *fd); /* Close fd_in and fd_out, unless they're already -1. They can point to the same fd, in which case they're closed only once. If they point to stdin or stdout, they're replaced with /dev/null. */ void fd_close_maybe_stdio(int *fd_in, int *fd_out); /* Call unlink(). If it fails, log an error including the source filename and line number. */ int i_unlink(const char *path, const char *source_fname, unsigned int source_linenum); #define i_unlink(path) i_unlink(path, __FILE__, __LINE__) /* Same as i_unlink(), but don't log an error if errno=ENOENT. Returns 1 on unlink() success, 0 if errno=ENOENT, -1 on other errors. */ int i_unlink_if_exists(const char *path, const char *source_fname, unsigned int source_linenum); #define i_unlink_if_exists(path) i_unlink_if_exists(path, __FILE__, __LINE__) /* Reset getopt() so it can be used for the next args. */ void i_getopt_reset(void); /* Call the given callback at the beginning of lib_deinit(). The main difference to atexit() is that liblib's memory allocation and logging functions are still available. Also if lib_atexit() is called multiple times to the same callback, it's added only once. */ void lib_atexit(lib_atexit_callback_t *callback); /* Specify the order in which the callback is called. Lowest numbered priorities are called first. lib_atexit() is called with priority=0. */ void lib_atexit_priority(lib_atexit_callback_t *callback, int priority); /* Manually run the atexit callbacks. lib_deinit() also does this if not explicitly called. */ void lib_atexit_run(void); void lib_init(void); bool lib_is_initialized(void); void lib_deinit(void); #endif dovecot-2.2.33.2/src/lib/test-ostream-file.c0000644000175000017500000000315313165463624015406 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "safe-mkstemp.h" #include "randgen.h" #include "ostream.h" #include #define MAX_BUFSIZE 256 static void test_ostream_file_random(void) { struct ostream *output; string_t *path = t_str_new(128); char buf[MAX_BUFSIZE*4], buf2[MAX_BUFSIZE*4], randbuf[MAX_BUFSIZE]; unsigned int i, offset, size; ssize_t ret; int fd; memset(buf, 0, sizeof(buf)); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) i_fatal("safe_mkstemp(%s) failed: %m", str_c(path)); i_unlink(str_c(path)); output = o_stream_create_fd(fd, MAX_BUFSIZE, FALSE); o_stream_cork(output); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf, randbuf, size); test_assert(o_stream_send(output, buf, size) > 0); for (i = 0; i < 10; i++) { offset = rand() % (MAX_BUFSIZE*3); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf + offset, randbuf, size); test_assert(o_stream_pwrite(output, randbuf, size, offset) == 0); if (rand() % 10 == 0) test_assert(o_stream_flush(output) > 0); } test_assert(o_stream_flush(output) > 0); o_stream_uncork(output); ret = pread(fd, buf2, sizeof(buf2), 0); if (ret < 0) i_fatal("pread() failed: %m"); else { i_assert(ret > 0); test_assert(memcmp(buf, buf2, ret) == 0); } o_stream_unref(&output); i_close_fd(&fd); } void test_ostream_file(void) { unsigned int i; test_begin("ostream pwrite random"); for (i = 0; i < 100; i++) T_BEGIN { test_ostream_file_random(); } T_END; test_end(); } dovecot-2.2.33.2/src/lib/nfs-workarounds.h0000644000175000017500000000352713123174404015203 00000000000000#ifndef NFS_WORKAROUNDS_H #define NFS_WORKAROUNDS_H /* Note that some systems (Solaris) may use a macro to redefine struct stat */ #include /* When syscall fails with ESTALE error, how many times to try reopening the file and retrying the operation. */ #define NFS_ESTALE_RETRY_COUNT 10 /* Same as open(), but try to handle ESTALE errors. */ int nfs_safe_open(const char *path, int flags); /* Same as stat(), but try to handle ESTALE errors. Doesn't flush attribute cache. */ int nfs_safe_stat(const char *path, struct stat *buf); int nfs_safe_lstat(const char *path, struct stat *buf); /* Same as link(), but handle problems with link() by verifying the file's link count changes. If links1=TRUE, assume the original file's link count is 1, otherwise stat() first to find it out. */ int nfs_safe_link(const char *oldpath, const char *newpath, bool links1); /* Flush attribute cache for given path. The file must not be fcntl locked or the locks may get dropped. */ void nfs_flush_attr_cache_unlocked(const char *path); /* Flush attribute cache for given path. The file may be fcntl locked. */ void nfs_flush_attr_cache_maybe_locked(const char *path); /* Flush attribute cache for a fcntl locked file descriptor. If locking flushes the attribute cache with the running OS, this function does nothing. The given path is used only for logging. */ void nfs_flush_attr_cache_fd_locked(const char *path, int fd); /* Flush file handle cache for given file. */ void nfs_flush_file_handle_cache(const char *path); /* Flush read cache for fd that was just fcntl locked. If the OS flushes read cache when fcntl locking file, this function does nothing. */ void nfs_flush_read_cache_locked(const char *path, int fd); /* Flush read cache for fd that doesn't have fcntl locks. */ void nfs_flush_read_cache_unlocked(const char *path, int fd); #endif dovecot-2.2.33.2/src/lib/strfuncs.c0000644000175000017500000003507313165463624013717 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "printf-format-fix.h" #include "strfuncs.h" #include "array.h" #include #include #include #define STRCONCAT_BUFSIZE 512 const unsigned char uchar_nul = '\0'; volatile int timing_safety_unoptimization; int i_snprintf(char *dest, size_t max_chars, const char *format, ...) { va_list args; int ret; i_assert(max_chars < INT_MAX); va_start(args, format); ret = vsnprintf(dest, max_chars, printf_format_fix_unsafe(format), args); va_end(args); i_assert(ret >= 0); return (unsigned int)ret < max_chars ? 0 : -1; } char *p_strdup(pool_t pool, const char *str) { void *mem; size_t len; if (str == NULL) return NULL; len = strlen(str) + 1; mem = p_malloc(pool, len); memcpy(mem, str, len); return mem; } void *p_memdup(pool_t pool, const void *data, size_t size) { void *mem; mem = p_malloc(pool, size); memcpy(mem, data, size); return mem; } char *p_strdup_empty(pool_t pool, const char *str) { if (str == NULL || *str == '\0') return NULL; return p_strdup(pool, str); } char *p_strdup_until(pool_t pool, const void *start, const void *end) { size_t size; char *mem; i_assert((const char *) start <= (const char *) end); size = (size_t) ((const char *) end - (const char *) start); mem = p_malloc(pool, size + 1); memcpy(mem, start, size); return mem; } char *p_strndup(pool_t pool, const void *str, size_t max_chars) { char *mem; size_t len; i_assert(max_chars != (size_t)-1); if (str == NULL) return NULL; len = 0; while (len < max_chars && ((const char *) str)[len] != '\0') len++; mem = p_malloc(pool, len+1); memcpy(mem, str, len); mem[len] = '\0'; return mem; } char *p_strdup_printf(pool_t pool, const char *format, ...) { va_list args; char *ret; va_start(args, format); ret = p_strdup_vprintf(pool, format, args); va_end(args); return ret; } char *t_noalloc_strdup_vprintf(const char *format, va_list args, unsigned int *size_r) { #define SNPRINTF_INITIAL_EXTRA_SIZE 256 va_list args2; char *tmp; size_t init_size; int ret; #ifdef DEBUG int old_errno = errno; #endif VA_COPY(args2, args); /* the format string is modified only if %m exists in it. it happens only in error conditions, so don't try to t_push() here since it'll just slow down the normal code path. */ format = printf_format_fix_get_len(format, &init_size); init_size += SNPRINTF_INITIAL_EXTRA_SIZE; tmp = t_buffer_get(init_size); ret = vsnprintf(tmp, init_size, format, args); i_assert(ret >= 0); *size_r = ret + 1; if ((unsigned int)ret >= init_size) { /* didn't fit with the first guess. now we know the size, so try again. */ tmp = t_buffer_get(*size_r); ret = vsnprintf(tmp, *size_r, format, args2); i_assert((unsigned int)ret == *size_r-1); } #ifdef DEBUG /* we rely on errno not changing. it shouldn't. */ i_assert(errno == old_errno); #endif va_end(args2); return tmp; } char *p_strdup_vprintf(pool_t pool, const char *format, va_list args) { char *tmp, *buf; unsigned int size; tmp = t_noalloc_strdup_vprintf(format, args, &size); if (pool->datastack_pool) { t_buffer_alloc(size); return tmp; } else { buf = p_malloc(pool, size); memcpy(buf, tmp, size - 1); return buf; } } char *vstrconcat(const char *str1, va_list args, size_t *ret_len) { const char *str; char *temp; size_t bufsize, i, len; if (str1 == NULL) return NULL; str = str1; bufsize = STRCONCAT_BUFSIZE; temp = t_buffer_get(bufsize); i = 0; do { len = strlen(str); if (i + len >= bufsize) { /* need more memory */ bufsize = nearest_power(i + len + 1); temp = t_buffer_reget(temp, bufsize); } memcpy(temp + i, str, len); i += len; /* next string */ str = va_arg(args, const char *); } while (str != NULL); i_assert(i < bufsize); temp[i++] = '\0'; *ret_len = i; return temp; } char *p_strconcat(pool_t pool, const char *str1, ...) { va_list args; char *temp, *ret; size_t len; va_start(args, str1); if (pool->datastack_pool) { ret = vstrconcat(str1, args, &len); if (ret != NULL) t_buffer_alloc(len); } else { temp = vstrconcat(str1, args, &len); ret = p_malloc(pool, len); memcpy(ret, temp, len); } va_end(args); return ret; } const char *t_strdup(const char *str) { return p_strdup(unsafe_data_stack_pool, str); } char *t_strdup_noconst(const char *str) { return p_strdup(unsafe_data_stack_pool, str); } const char *t_strdup_empty(const char *str) { return p_strdup_empty(unsafe_data_stack_pool, str); } const char *t_strdup_until(const void *start, const void *end) { return p_strdup_until(unsafe_data_stack_pool, start, end); } const char *t_strndup(const void *str, size_t max_chars) { return p_strndup(unsafe_data_stack_pool, str, max_chars); } const char *t_strdup_printf(const char *format, ...) { va_list args; const char *ret; va_start(args, format); ret = p_strdup_vprintf(unsafe_data_stack_pool, format, args); va_end(args); return ret; } const char *t_strdup_vprintf(const char *format, va_list args) { return p_strdup_vprintf(unsafe_data_stack_pool, format, args); } const char *t_strconcat(const char *str1, ...) { va_list args; const char *ret; size_t len; va_start(args, str1); ret = vstrconcat(str1, args, &len); if (ret != NULL) t_buffer_alloc(len); va_end(args); return ret; } const char *t_strcut(const char *str, char cutchar) { const char *p; for (p = str; *p != '\0'; p++) { if (*p == cutchar) return t_strdup_until(str, p); } return str; } const char *t_str_replace(const char *str, char from, char to) { char *out; size_t i, len; if (strchr(str, from) == NULL) return str; len = strlen(str); out = t_malloc(len + 1); for (i = 0; i < len; i++) { if (str[i] == from) out[i] = to; else out[i] = str[i]; } out[i] = '\0'; return out; } int i_strocpy(char *dest, const char *src, size_t dstsize) { if (dstsize == 0) return -1; while (*src != '\0' && dstsize > 1) { *dest++ = *src++; dstsize--; } *dest = '\0'; return *src == '\0' ? 0 : -1; } char *str_ucase(char *str) { char *p; for (p = str; *p != '\0'; p++) *p = i_toupper(*p); return str; } char *str_lcase(char *str) { char *p; for (p = str; *p != '\0'; p++) *p = i_tolower(*p); return str; } const char *t_str_lcase(const char *str) { i_assert(str != NULL); return str_lcase(t_strdup_noconst(str)); } const char *t_str_ucase(const char *str) { i_assert(str != NULL); return str_ucase(t_strdup_noconst(str)); } #if 0 /* FIXME: wait for v2.3 due to a collision with pigeonhole */ const char *t_str_trim(const char *str, const char *chars) { const char *p, *pend, *begin; pend = str + strlen(str); if (pend == str) return ""; p = str; while (p < pend && strchr(chars, *p) != NULL) p++; begin = p; p = pend - 1; while (p > begin && strchr(chars, *p) != NULL) p--; if (p <= begin) return ""; return t_strdup_until(begin, p+1); } #endif const char *str_ltrim(const char *str, const char *chars) { const char *p; if (*str == '\0') return ""; p = str; while (*p != '\0' && strchr(chars, *p) != NULL) p++; return p; } const char *t_str_ltrim(const char *str, const char *chars) { return t_strdup(str_ltrim(str, chars)); } const char *t_str_rtrim(const char *str, const char *chars) { const char *p, *pend; pend = str + strlen(str); if (pend == str) return ""; p = pend - 1; while (p > str && strchr(chars, *p) != NULL) p--; if (p <= str) return ""; return t_strdup_until(str, p+1); } int null_strcmp(const char *s1, const char *s2) { if (s1 == NULL) return s2 == NULL ? 0 : -1; if (s2 == NULL) return 1; return strcmp(s1, s2); } int null_strcasecmp(const char *s1, const char *s2) { if (s1 == NULL) return s2 == NULL ? 0 : -1; if (s2 == NULL) return 1; return strcasecmp(s1, s2); } int i_memcasecmp(const void *p1, const void *p2, size_t size) { const unsigned char *s1 = p1; const unsigned char *s2 = p2; int ret; while (size > 0) { ret = i_toupper(*s1) - i_toupper(*s2); if (ret != 0) return ret; s1++; s2++; size--; } return 0; } int bsearch_strcmp(const char *key, const char *const *member) { return strcmp(key, *member); } int i_strcmp_p(const char *const *p1, const char *const *p2) { return strcmp(*p1, *p2); } int bsearch_strcasecmp(const char *key, const char *const *member) { return strcasecmp(key, *member); } int i_strcasecmp_p(const char *const *p1, const char *const *p2) { return strcasecmp(*p1, *p2); } bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size) { const unsigned char *s1 = p1, *s2 = p2; size_t i; int ret = 0; for (i = 0; i < size; i++) ret |= s1[i] ^ s2[i]; /* make sure the compiler optimizer doesn't try to break out of the above loop early. */ timing_safety_unoptimization = ret; return ret == 0; } static char ** split_str(pool_t pool, const char *data, const char *separators, int spaces) { char **array; char *str; unsigned int count, alloc_count, new_alloc_count; i_assert(*separators != '\0'); if (spaces) { /* skip leading separators */ while (*data != '\0' && strchr(separators, *data) != NULL) data++; } if (*data == '\0') return p_new(pool, char *, 1); str = p_strdup(pool, data); alloc_count = 32; array = p_new(pool, char *, alloc_count); array[0] = str; count = 1; while (*str != '\0') { if (strchr(separators, *str) != NULL) { /* separator found */ if (count+1 >= alloc_count) { new_alloc_count = nearest_power(alloc_count+1); array = p_realloc(pool, array, sizeof(char *) * alloc_count, sizeof(char *) * new_alloc_count); alloc_count = new_alloc_count; } *str = '\0'; if (spaces) { while (str[1] != '\0' && strchr(separators, str[1]) != NULL) str++; /* ignore trailing separators */ if (str[1] == '\0') break; } array[count++] = str+1; } str++; } i_assert(count < alloc_count); array[count] = NULL; return array; } const char **t_strsplit(const char *data, const char *separators) { return (const char **)split_str(unsafe_data_stack_pool, data, separators, FALSE); } const char **t_strsplit_spaces(const char *data, const char *separators) { return (const char **)split_str(unsafe_data_stack_pool, data, separators, TRUE); } char **p_strsplit(pool_t pool, const char *data, const char *separators) { return split_str(pool, data, separators, FALSE); } char **p_strsplit_spaces(pool_t pool, const char *data, const char *separators) { return split_str(pool, data, separators, TRUE); } const char **t_strsplit_tab(const char *data) { const char **array; char *dest; unsigned int i, array_idx, array_size, dest_size; if (*data == '\0') return t_new(const char *, 1); array_size = 1; dest_size = 128; dest = t_buffer_get(dest_size+1); for (i = 0; data[i] != '\0'; i++) { if (i >= dest_size) { dest_size = nearest_power(dest_size+1); dest = t_buffer_reget(dest, dest_size+1); } if (data[i] != '\t') dest[i] = data[i]; else { dest[i] = '\0'; array_size++; } } i_assert(i <= dest_size); dest[i] = '\0'; t_buffer_alloc(i+1); dest_size = i; array = t_new(const char *, array_size + 1); array[0] = dest; array_idx = 1; for (i = 0; i < dest_size; i++) { if (dest[i] == '\0') array[array_idx++] = dest+i+1; } i_assert(array_idx == array_size); array[array_idx] = NULL; return array; } void p_strsplit_free(pool_t pool, char **arr) { p_free(pool, arr[0]); p_free(pool, arr); } unsigned int str_array_length(const char *const *arr) { unsigned int count; if (arr == NULL) return 0; for (count = 0; *arr != NULL; arr++) count++; return count; } static char * p_strarray_join_n(pool_t pool, const char *const *arr, unsigned int arr_len, const char *separator) { size_t alloc_len, sep_len, len, pos, needed_space; unsigned int i; char *str; sep_len = strlen(separator); alloc_len = 64; str = t_buffer_get(alloc_len); pos = 0; for (i = 0; i < arr_len; i++) { len = strlen(arr[i]); needed_space = pos + len + sep_len + 1; if (needed_space > alloc_len) { alloc_len = nearest_power(needed_space); str = t_buffer_reget(str, alloc_len); } if (pos != 0) { memcpy(str + pos, separator, sep_len); pos += sep_len; } memcpy(str + pos, arr[i], len); pos += len; } str[pos] = '\0'; if (!pool->datastack_pool) return p_memdup(pool, str, pos + 1); t_buffer_alloc(pos + 1); return str; } const char *t_strarray_join(const char *const *arr, const char *separator) { return p_strarray_join_n(unsafe_data_stack_pool, arr, str_array_length(arr), separator); } bool str_array_remove(const char **arr, const char *value) { const char **dest; for (; *arr != NULL; arr++) { if (strcmp(*arr, value) == 0) { /* found it. now move the rest. */ for (dest = arr, arr++; *arr != NULL; arr++, dest++) *dest = *arr; *dest = NULL; return TRUE; } } return FALSE; } bool str_array_find(const char *const *arr, const char *value) { for (; *arr != NULL; arr++) { if (strcmp(*arr, value) == 0) return TRUE; } return FALSE; } bool str_array_icase_find(const char *const *arr, const char *value) { for (; *arr != NULL; arr++) { if (strcasecmp(*arr, value) == 0) return TRUE; } return FALSE; } const char **p_strarray_dup(pool_t pool, const char *const *arr) { unsigned int i; const char **ret; char *p; size_t len, size = sizeof(const char *); /* @UNSAFE: integer overflow checks are missing */ for (i = 0; arr[i] != NULL; i++) size += sizeof(const char *) + strlen(arr[i]) + 1; ret = p_malloc(pool, size); p = PTR_OFFSET(ret, sizeof(const char *) * (i + 1)); for (i = 0; arr[i] != NULL; i++) { len = strlen(arr[i]) + 1; memcpy(p, arr[i], len); ret[i] = p; p += len; } i_assert(PTR_OFFSET(ret, size) == (void *)p); return ret; } const char *dec2str(uintmax_t number) { return dec2str_buf(t_malloc(MAX_INT_STRLEN), number); } char *dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN], uintmax_t number) { int pos; pos = MAX_INT_STRLEN; buffer[--pos] = '\0'; do { buffer[--pos] = (number % 10) + '0'; number /= 10; } while (number != 0 && pos >= 0); i_assert(pos >= 0); return buffer + pos; } char *p_array_const_string_join(pool_t pool, const ARRAY_TYPE(const_string) *arr, const char *separator) { if (array_count(arr) == 0) return ""; return p_strarray_join_n(pool, array_idx(arr, 0), array_count(arr), separator); } dovecot-2.2.33.2/src/lib/ioloop.c0000644000175000017500000006502113165463624013345 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "backtrace-string.h" #include "llist.h" #include "time-util.h" #include "istream-private.h" #include "ioloop-private.h" #include #define timer_is_larger(tvp, uvp) \ ((tvp)->tv_sec > (uvp)->tv_sec || \ ((tvp)->tv_sec == (uvp)->tv_sec && \ (tvp)->tv_usec > (uvp)->tv_usec)) time_t ioloop_time = 0; struct timeval ioloop_timeval; struct ioloop *current_ioloop = NULL; uint64_t ioloop_global_wait_usecs = 0; static ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT; static void io_loop_initialize_handler(struct ioloop *ioloop) { unsigned int initial_fd_count; initial_fd_count = ioloop->max_fd_count > 0 && ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ? ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT; io_loop_handler_init(ioloop, initial_fd_count); } static struct io_file * io_add_file(int fd, enum io_condition condition, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context) { struct io_file *io; i_assert(callback != NULL); i_assert((condition & IO_NOTIFY) == 0); io = i_new(struct io_file, 1); io->io.condition = condition; io->io.callback = callback; io->io.context = context; io->io.ioloop = current_ioloop; io->io.source_filename = source_filename; io->io.source_linenum = source_linenum; io->refcount = 1; io->fd = fd; if (io->io.ioloop->cur_ctx != NULL) { io->io.ctx = io->io.ioloop->cur_ctx; io_loop_context_ref(io->io.ctx); } if (io->io.ioloop->handler_context == NULL) io_loop_initialize_handler(io->io.ioloop); if (fd != -1) io_loop_handle_add(io); else { /* we're adding an istream whose only way to get notified is to call i_stream_set_input_pending() */ } if (io->io.ioloop->io_files != NULL) { io->io.ioloop->io_files->prev = io; io->next = io->io.ioloop->io_files; } io->io.ioloop->io_files = io; return io; } #undef io_add struct io *io_add(int fd, enum io_condition condition, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context) { struct io_file *io; i_assert(fd >= 0); io = io_add_file(fd, condition, source_filename, source_linenum, callback, context); return &io->io; } #undef io_add_istream struct io *io_add_istream(struct istream *input, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context) { struct io_file *io; io = io_add_file(i_stream_get_fd(input), IO_READ, source_filename, source_linenum, callback, context); io->istream = input; i_stream_ref(io->istream); i_stream_set_io(io->istream, &io->io); return &io->io; } static void io_file_unlink(struct io_file *io) { if (io->prev != NULL) io->prev->next = io->next; else io->io.ioloop->io_files = io->next; if (io->next != NULL) io->next->prev = io->prev; /* if we got here from an I/O handler callback, make sure we don't try to handle this one next. */ if (io->io.ioloop->next_io_file == io) io->io.ioloop->next_io_file = io->next; } static void io_remove_full(struct io **_io, bool closed) { struct io *io = *_io; i_assert(io->callback != NULL); *_io = NULL; /* make sure the callback doesn't get called anymore. kqueue code relies on this. */ io->callback = NULL; if (io->pending) { i_assert(io->ioloop->io_pending_count > 0); io->ioloop->io_pending_count--; } if (io->ctx != NULL) io_loop_context_unref(&io->ctx); if ((io->condition & IO_NOTIFY) != 0) io_loop_notify_remove(io); else { struct io_file *io_file = (struct io_file *)io; struct istream *istream = io_file->istream; if (istream != NULL) { /* remove io before it's freed */ i_stream_unset_io(istream, io); } io_file_unlink(io_file); if (io_file->fd != -1) io_loop_handle_remove(io_file, closed); else i_free(io); /* remove io from the ioloop before unreferencing the istream, because a destroyed istream may automatically close the fd. */ if (istream != NULL) i_stream_unref(&istream); } } void io_remove(struct io **io) { io_remove_full(io, FALSE); } void io_remove_closed(struct io **io) { i_assert(((*io)->condition & IO_NOTIFY) == 0); io_remove_full(io, TRUE); } void io_set_pending(struct io *io) { i_assert((io->condition & IO_NOTIFY) == 0); if (!io->pending) { io->pending = TRUE; io->ioloop->io_pending_count++; } } static void timeout_update_next(struct timeout *timeout, struct timeval *tv_now) { if (tv_now == NULL) { if (gettimeofday(&timeout->next_run, NULL) < 0) i_fatal("gettimeofday(): %m"); } else { timeout->next_run.tv_sec = tv_now->tv_sec; timeout->next_run.tv_usec = tv_now->tv_usec; } /* we don't want microsecond accuracy or this function will be called all the time - millisecond is more than enough */ timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000; timeout->next_run.tv_sec += timeout->msecs/1000; timeout->next_run.tv_usec += (timeout->msecs%1000)*1000; if (timeout->next_run.tv_usec > 1000000) { timeout->next_run.tv_sec++; timeout->next_run.tv_usec -= 1000000; } } static struct timeout * timeout_add_common(const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) { struct timeout *timeout; timeout = i_new(struct timeout, 1); timeout->item.idx = UINT_MAX; timeout->source_filename = source_filename; timeout->source_linenum = source_linenum; timeout->ioloop = current_ioloop; timeout->callback = callback; timeout->context = context; if (timeout->ioloop->cur_ctx != NULL) { timeout->ctx = timeout->ioloop->cur_ctx; io_loop_context_ref(timeout->ctx); } return timeout; } #undef timeout_add struct timeout *timeout_add(unsigned int msecs, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) { struct timeout *timeout; timeout = timeout_add_common(source_filename, source_linenum, callback, context); timeout->msecs = msecs; if (msecs > 0) { /* start this timeout in the next run cycle */ array_append(&timeout->ioloop->timeouts_new, &timeout, 1); } else { /* trigger zero timeouts as soon as possible */ timeout_update_next(timeout, timeout->ioloop->running ? NULL : &ioloop_timeval); priorityq_add(timeout->ioloop->timeouts, &timeout->item); } return timeout; } #undef timeout_add_short struct timeout * timeout_add_short(unsigned int msecs, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) { return timeout_add(msecs, source_filename, source_linenum, callback, context); } #undef timeout_add_absolute struct timeout * timeout_add_absolute(const struct timeval *time, const char *source_filename, unsigned int source_linenum, timeout_callback_t *callback, void *context) { struct timeout *timeout; timeout = timeout_add_common(source_filename, source_linenum, callback, context); timeout->one_shot = TRUE; timeout->next_run = *time; priorityq_add(timeout->ioloop->timeouts, &timeout->item); return timeout; } static struct timeout * timeout_copy(const struct timeout *old_to) { struct timeout *new_to; new_to = timeout_add_common (old_to->source_filename, old_to->source_linenum, old_to->callback, old_to->context); new_to->one_shot = old_to->one_shot; new_to->msecs = old_to->msecs; new_to->next_run = old_to->next_run; if (old_to->item.idx != UINT_MAX) priorityq_add(new_to->ioloop->timeouts, &new_to->item); else if (!new_to->one_shot) { i_assert(new_to->msecs > 0); array_append(&new_to->ioloop->timeouts_new, &new_to, 1); } return new_to; } static void timeout_free(struct timeout *timeout) { if (timeout->ctx != NULL) io_loop_context_unref(&timeout->ctx); i_free(timeout); } void timeout_remove(struct timeout **_timeout) { struct timeout *timeout = *_timeout; struct ioloop *ioloop = timeout->ioloop; *_timeout = NULL; if (timeout->item.idx != UINT_MAX) priorityq_remove(timeout->ioloop->timeouts, &timeout->item); else if (!timeout->one_shot && timeout->msecs > 0) { struct timeout *const *to_idx; array_foreach(&ioloop->timeouts_new, to_idx) { if (*to_idx == timeout) { array_delete(&ioloop->timeouts_new, array_foreach_idx(&ioloop->timeouts_new, to_idx), 1); break; } } } timeout_free(timeout); } static void ATTR_NULL(2) timeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now) { if (timeout->item.idx == UINT_MAX) return; timeout_update_next(timeout, tv_now); if (timeout->msecs <= 1) { /* if we came here from io_loop_handle_timeouts(), next_run must be larger than tv_now or we could go to infinite loop. +1000 to get 1 ms further, another +1000 to account for timeout_update_next()'s truncation. */ timeout->next_run.tv_usec += 2000; if (timeout->next_run.tv_usec >= 1000000) { timeout->next_run.tv_sec++; timeout->next_run.tv_usec -= 1000000; } } i_assert(tv_now == NULL || timeout->next_run.tv_sec > tv_now->tv_sec || (timeout->next_run.tv_sec == tv_now->tv_sec && timeout->next_run.tv_usec > tv_now->tv_usec)); priorityq_remove(timeout->ioloop->timeouts, &timeout->item); priorityq_add(timeout->ioloop->timeouts, &timeout->item); } void timeout_reset(struct timeout *timeout) { i_assert(!timeout->one_shot); timeout_reset_timeval(timeout, NULL); } static int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r, struct timeval *tv_now) { int ret; if (tv_now->tv_sec == 0) { if (gettimeofday(tv_now, NULL) < 0) i_fatal("gettimeofday(): %m"); } tv_r->tv_sec = tv_now->tv_sec; tv_r->tv_usec = tv_now->tv_usec; i_assert(tv_r->tv_sec > 0); i_assert(timeout->next_run.tv_sec > 0); tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec; tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec; if (tv_r->tv_usec < 0) { tv_r->tv_sec--; tv_r->tv_usec += 1000000; } if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) { tv_r->tv_sec = 0; tv_r->tv_usec = 0; return 0; } if (tv_r->tv_sec > INT_MAX/1000-1) tv_r->tv_sec = INT_MAX/1000-1; /* round wait times up to next millisecond */ ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000; i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0); return ret; } int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r) { struct timeval tv_now; struct priorityq_item *item; struct timeout *timeout; int msecs; item = priorityq_peek(ioloop->timeouts); timeout = (struct timeout *)item; /* we need to see if there are pending IO waiting, if there is, we set msecs = 0 to ensure they are processed without delay */ if (timeout == NULL && ioloop->io_pending_count == 0) { /* no timeouts. use INT_MAX msecs for timeval and return -1 for poll/epoll infinity. */ tv_r->tv_sec = INT_MAX / 1000; tv_r->tv_usec = 0; ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1; return -1; } if (ioloop->io_pending_count > 0) { if (gettimeofday(&tv_now, NULL) < 0) i_fatal("gettimeofday(): %m"); msecs = 0; tv_r->tv_sec = 0; tv_r->tv_usec = 0; } else { tv_now.tv_sec = 0; msecs = timeout_get_wait_time(timeout, tv_r, &tv_now); } ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1; /* update ioloop_timeval - this is meant for io_loop_handle_timeouts()'s ioloop_wait_usecs calculation. normally after this we go to the ioloop and after that we update ioloop_timeval immediately again. */ ioloop_timeval = tv_now; ioloop_time = tv_now.tv_sec; return msecs; } static int timeout_cmp(const void *p1, const void *p2) { const struct timeout *to1 = p1, *to2 = p2; return timeval_cmp(&to1->next_run, &to2->next_run); } static void io_loop_default_time_moved(time_t old_time, time_t new_time) { if (old_time > new_time) { i_warning("Time moved backwards by %ld seconds.", (long)(old_time - new_time)); } } static void io_loop_timeouts_start_new(struct ioloop *ioloop) { struct timeout *const *to_idx; if (array_count(&ioloop->timeouts_new) == 0) return; io_loop_time_refresh(); array_foreach(&ioloop->timeouts_new, to_idx) { struct timeout *timeout= *to_idx; i_assert(timeout->next_run.tv_sec == 0 && timeout->next_run.tv_usec == 0); i_assert(!timeout->one_shot); i_assert(timeout->msecs > 0); timeout_update_next(timeout, &ioloop_timeval); priorityq_add(ioloop->timeouts, &timeout->item); } array_clear(&ioloop->timeouts_new); } static void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs) { struct priorityq_item *const *items; unsigned int i, count; count = priorityq_count(ioloop->timeouts); items = priorityq_items(ioloop->timeouts); for (i = 0; i < count; i++) { struct timeout *to = (struct timeout *)items[i]; to->next_run.tv_sec += diff_secs; } } static void io_loops_timeouts_update(long diff_secs) { struct ioloop *ioloop; for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev) io_loop_timeouts_update(ioloop, diff_secs); } static void ioloop_add_wait_time(struct ioloop *ioloop) { struct io_wait_timer *timer; long long diff = timeval_diff_usecs(&ioloop_timeval, &ioloop->wait_started); ioloop->ioloop_wait_usecs += diff; ioloop_global_wait_usecs += diff; for (timer = ioloop->wait_timers; timer != NULL; timer = timer->next) timer->usecs += diff; } static void io_loop_handle_timeouts_real(struct ioloop *ioloop) { struct priorityq_item *item; struct timeval tv, tv_call; unsigned int t_id; if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); /* Don't bother comparing usecs. */ if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) { /* time moved backwards */ io_loops_timeouts_update(-(long)(ioloop_time - ioloop_timeval.tv_sec)); ioloop->time_moved_callback(ioloop_time, ioloop_timeval.tv_sec); /* the callback may have slept, so check the time again. */ if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); } else { if (unlikely(ioloop_timeval.tv_sec > ioloop->next_max_time)) { io_loops_timeouts_update(ioloop_timeval.tv_sec - ioloop->next_max_time); /* time moved forwards */ ioloop->time_moved_callback(ioloop->next_max_time, ioloop_timeval.tv_sec); } ioloop_add_wait_time(ioloop); } ioloop_time = ioloop_timeval.tv_sec; tv_call = ioloop_timeval; while ((item = priorityq_peek(ioloop->timeouts)) != NULL) { struct timeout *timeout = (struct timeout *)item; /* use tv_call to make sure we don't get to infinite loop in case callbacks update ioloop_timeval. */ if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0) break; if (timeout->one_shot) { /* remove timeout from queue */ priorityq_remove(timeout->ioloop->timeouts, &timeout->item); } else { /* update timeout's next_run and reposition it in the queue */ timeout_reset_timeval(timeout, &tv_call); } if (timeout->ctx != NULL) io_loop_context_activate(timeout->ctx); t_id = t_push_named("ioloop timeout handler %p", (void *)timeout->callback); timeout->callback(timeout->context); if (t_pop() != t_id) { i_panic("Leaked a t_pop() call in timeout handler %p", (void *)timeout->callback); } if (ioloop->cur_ctx != NULL) io_loop_context_deactivate(ioloop->cur_ctx); } } void io_loop_handle_timeouts(struct ioloop *ioloop) { T_BEGIN { io_loop_handle_timeouts_real(ioloop); } T_END; } void io_loop_call_io(struct io *io) { struct ioloop *ioloop = io->ioloop; unsigned int t_id; if (io->pending) { i_assert(ioloop->io_pending_count > 0); ioloop->io_pending_count--; io->pending = FALSE; } if (io->ctx != NULL) io_loop_context_activate(io->ctx); t_id = t_push_named("ioloop handler %p", (void *)io->callback); io->callback(io->context); if (t_pop() != t_id) { i_panic("Leaked a t_pop() call in I/O handler %p", (void *)io->callback); } if (ioloop->cur_ctx != NULL) io_loop_context_deactivate(ioloop->cur_ctx); } void io_loop_run(struct ioloop *ioloop) { if (ioloop->handler_context == NULL) io_loop_initialize_handler(ioloop); if (ioloop->cur_ctx != NULL) io_loop_context_unref(&ioloop->cur_ctx); /* recursive io_loop_run() isn't allowed for the same ioloop. it can break backends. */ i_assert(!ioloop->iolooping); ioloop->iolooping = TRUE; ioloop->running = TRUE; while (ioloop->running) io_loop_handler_run(ioloop); ioloop->iolooping = FALSE; } static void io_loop_call_pending(struct ioloop *ioloop) { struct io_file *io; while (ioloop->io_pending_count > 0) { io = ioloop->io_files; do { ioloop->next_io_file = io->next; if (io->io.pending) io_loop_call_io(&io->io); if (ioloop->io_pending_count == 0) break; io = ioloop->next_io_file; } while (io != NULL); } } void io_loop_handler_run(struct ioloop *ioloop) { io_loop_timeouts_start_new(ioloop); ioloop->wait_started = ioloop_timeval; io_loop_handler_run_internal(ioloop); io_loop_call_pending(ioloop); } void io_loop_stop(struct ioloop *ioloop) { ioloop->running = FALSE; } void io_loop_set_running(struct ioloop *ioloop) { ioloop->running = TRUE; } void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds) { ioloop->max_fd_count = max_fds; } bool io_loop_is_running(struct ioloop *ioloop) { return ioloop->running; } void io_loop_time_refresh(void) { if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); ioloop_time = ioloop_timeval.tv_sec; } struct ioloop *io_loop_create(void) { struct ioloop *ioloop; /* initialize time */ if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); ioloop_time = ioloop_timeval.tv_sec; ioloop = i_new(struct ioloop, 1); ioloop->timeouts = priorityq_init(timeout_cmp, 32); i_array_init(&ioloop->timeouts_new, 8); ioloop->time_moved_callback = current_ioloop != NULL ? current_ioloop->time_moved_callback : io_loop_default_time_moved; ioloop->prev = current_ioloop; io_loop_set_current(ioloop); return ioloop; } void io_loop_destroy(struct ioloop **_ioloop) { struct ioloop *ioloop = *_ioloop; struct timeout *const *to_idx; struct priorityq_item *item; bool leaks = FALSE; *_ioloop = NULL; /* ->prev won't work unless loops are destroyed in create order */ i_assert(ioloop == current_ioloop); io_loop_set_current(current_ioloop->prev); if (ioloop->notify_handler_context != NULL) io_loop_notify_handler_deinit(ioloop); while (ioloop->io_files != NULL) { struct io_file *io = ioloop->io_files; struct io *_io = &io->io; i_warning("I/O leak: %p (%s:%u, fd %d)", (void *)io->io.callback, io->io.source_filename, io->io.source_linenum, io->fd); io_remove(&_io); leaks = TRUE; } i_assert(ioloop->io_pending_count == 0); array_foreach(&ioloop->timeouts_new, to_idx) { struct timeout *to = *to_idx; i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback, to->source_filename, to->source_linenum); timeout_free(to); leaks = TRUE; } array_free(&ioloop->timeouts_new); while ((item = priorityq_pop(ioloop->timeouts)) != NULL) { struct timeout *to = (struct timeout *)item; i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback, to->source_filename, to->source_linenum); timeout_free(to); leaks = TRUE; } priorityq_deinit(&ioloop->timeouts); while (ioloop->wait_timers != NULL) { struct io_wait_timer *timer = ioloop->wait_timers; i_warning("IO wait timer leak: %s:%u", timer->source_filename, timer->source_linenum); io_wait_timer_remove(&timer); leaks = TRUE; } if (leaks) { const char *backtrace; if (backtrace_get(&backtrace) == 0) i_warning("Raw backtrace for leaks: %s", backtrace); } if (ioloop->handler_context != NULL) io_loop_handler_deinit(ioloop); if (ioloop->cur_ctx != NULL) io_loop_context_deactivate(ioloop->cur_ctx); i_free(ioloop); } void io_loop_set_time_moved_callback(struct ioloop *ioloop, io_loop_time_moved_callback_t *callback) { ioloop->time_moved_callback = callback; } static void io_switch_callbacks_free(void) { array_free(&io_switch_callbacks); } void io_loop_set_current(struct ioloop *ioloop) { io_switch_callback_t *const *callbackp; struct ioloop *prev_ioloop = current_ioloop; if (ioloop == current_ioloop) return; current_ioloop = ioloop; if (array_is_created(&io_switch_callbacks)) { array_foreach(&io_switch_callbacks, callbackp) (*callbackp)(prev_ioloop); } } void io_loop_add_switch_callback(io_switch_callback_t *callback) { if (!array_is_created(&io_switch_callbacks)) { i_array_init(&io_switch_callbacks, 4); lib_atexit(io_switch_callbacks_free); } array_append(&io_switch_callbacks, &callback, 1); } void io_loop_remove_switch_callback(io_switch_callback_t *callback) { io_switch_callback_t *const *callbackp; unsigned int idx; array_foreach(&io_switch_callbacks, callbackp) { if (*callbackp == callback) { idx = array_foreach_idx(&io_switch_callbacks, callbackp); array_delete(&io_switch_callbacks, idx, 1); return; } } i_unreached(); } struct ioloop_context *io_loop_context_new(struct ioloop *ioloop) { struct ioloop_context *ctx; ctx = i_new(struct ioloop_context, 1); ctx->refcount = 2; ctx->ioloop = ioloop; i_array_init(&ctx->callbacks, 4); if (ioloop->cur_ctx != NULL) io_loop_context_unref(&ioloop->cur_ctx); ioloop->cur_ctx = ctx; return ctx; } void io_loop_context_ref(struct ioloop_context *ctx) { i_assert(ctx->refcount > 0); ctx->refcount++; } void io_loop_context_unref(struct ioloop_context **_ctx) { struct ioloop_context *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->refcount > 0); if (--ctx->refcount > 0) return; /* cur_ctx itself keeps a reference */ i_assert(ctx->ioloop->cur_ctx != ctx); array_free(&ctx->callbacks); i_free(ctx); } #undef io_loop_context_add_callbacks void io_loop_context_add_callbacks(struct ioloop_context *ctx, io_callback_t *activate, io_callback_t *deactivate, void *context) { struct ioloop_context_callback cb; i_zero(&cb); cb.activate = activate; cb.deactivate = deactivate; cb.context = context; array_append(&ctx->callbacks, &cb, 1); } #undef io_loop_context_remove_callbacks void io_loop_context_remove_callbacks(struct ioloop_context *ctx, io_callback_t *activate, io_callback_t *deactivate, void *context) { struct ioloop_context_callback *cb; array_foreach_modifiable(&ctx->callbacks, cb) { if (cb->context == context && cb->activate == activate && cb->deactivate == deactivate) { /* simply mark it as deleted, since we could get here from activate/deactivate loop */ cb->activate = NULL; cb->deactivate = NULL; cb->context = NULL; return; } } i_panic("io_loop_context_remove_callbacks() context not found"); } static void io_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx) { const struct ioloop_context_callback *cbs; unsigned int i, count; cbs = array_get(&ctx->callbacks, &count); for (i = 0; i < count; ) { if (cbs[i].activate != NULL) i++; else { array_delete(&ctx->callbacks, i, 1); cbs = array_get(&ctx->callbacks, &count); } } } void io_loop_context_activate(struct ioloop_context *ctx) { struct ioloop_context_callback *cb; i_assert(ctx->ioloop->cur_ctx == NULL); ctx->ioloop->cur_ctx = ctx; io_loop_context_ref(ctx); array_foreach_modifiable(&ctx->callbacks, cb) { i_assert(!cb->activated); if (cb->activate != NULL) cb->activate(cb->context); cb->activated = TRUE; } } void io_loop_context_deactivate(struct ioloop_context *ctx) { struct ioloop_context_callback *cb; i_assert(ctx->ioloop->cur_ctx != NULL); array_foreach_modifiable(&ctx->callbacks, cb) { if (!cb->activated) { /* we just added this callback. don't deactivate it before it gets first activated. */ } else { if (cb->deactivate != NULL) cb->deactivate(cb->context); cb->activated = FALSE; } } ctx->ioloop->cur_ctx = NULL; io_loop_context_remove_deleted_callbacks(ctx); io_loop_context_unref(&ctx); } struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop) { return ioloop->cur_ctx; } struct io *io_loop_move_io(struct io **_io) { struct io *old_io = *_io; struct io_file *old_io_file, *new_io_file; i_assert((old_io->condition & IO_NOTIFY) == 0); if (old_io->ioloop == current_ioloop) return old_io; old_io_file = (struct io_file *)old_io; new_io_file = io_add_file(old_io_file->fd, old_io->condition, old_io->source_filename, old_io->source_linenum, old_io->callback, old_io->context); if (old_io_file->istream != NULL) { /* reference before io_remove() */ new_io_file->istream = old_io_file->istream; i_stream_ref(new_io_file->istream); } if (old_io->pending) io_set_pending(&new_io_file->io); io_remove(_io); if (new_io_file->istream != NULL) { /* update istream io after it was removed with io_remove() */ i_stream_set_io(new_io_file->istream, &new_io_file->io); } return &new_io_file->io; } struct timeout *io_loop_move_timeout(struct timeout **_timeout) { struct timeout *new_to, *old_to = *_timeout; if (old_to->ioloop == current_ioloop) return old_to; new_to = timeout_copy(old_to); timeout_remove(_timeout); return new_to; } bool io_loop_have_ios(struct ioloop *ioloop) { return ioloop->io_files != NULL; } bool io_loop_have_immediate_timeouts(struct ioloop *ioloop) { struct timeval tv; return io_loop_get_wait_time(ioloop, &tv) == 0; } uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop) { return ioloop->ioloop_wait_usecs; } enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd) { enum io_condition conditions = 0; struct io_file *io; i_assert(fd >= 0); for (io = ioloop->io_files; io != NULL; io = io->next) { if (io->fd == fd) conditions |= io->io.condition; } return conditions; } #undef io_wait_timer_add struct io_wait_timer * io_wait_timer_add(const char *source_filename, unsigned int source_linenum) { struct io_wait_timer *timer; timer = i_new(struct io_wait_timer, 1); timer->ioloop = current_ioloop; timer->source_filename = source_filename; timer->source_linenum = source_linenum; DLLIST_PREPEND(¤t_ioloop->wait_timers, timer); return timer; } struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **_timer) { struct io_wait_timer *timer = *_timer; *_timer = NULL; DLLIST_REMOVE(&timer->ioloop->wait_timers, timer); DLLIST_PREPEND(¤t_ioloop->wait_timers, timer); timer->ioloop = current_ioloop; return timer; } void io_wait_timer_remove(struct io_wait_timer **_timer) { struct io_wait_timer *timer = *_timer; *_timer = NULL; DLLIST_REMOVE(&timer->ioloop->wait_timers, timer); i_free(timer); } uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer) { return timer->usecs; } dovecot-2.2.33.2/src/lib/mmap-util.h0000644000175000017500000000202613123174404013737 00000000000000#ifndef MMAP_UTIL_H #define MMAP_UTIL_H #include #ifdef HAVE_LINUX_MREMAP # define __USE_GNU /* for MREMAP_MAYMOVE */ #endif #include #undef __USE_GNU #if !defined (MREMAP_MAYMOVE) && !defined (HAVE_LINUX_MREMAP) # define MREMAP_MAYMOVE 1 #endif #define madvise my_madvise int my_madvise(void *start, size_t length, int advice); #ifndef HAVE_MADVISE # ifndef MADV_NORMAL # define MADV_NORMAL 0 # define MADV_RANDOM 0 # define MADV_SEQUENTIAL 0 # define MADV_WILLNEED 0 # define MADV_DONTNEED 0 # endif #endif void *mmap_file(int fd, size_t *length, int prot); void *mmap_ro_file(int fd, size_t *length); void *mmap_rw_file(int fd, size_t *length); /* for allocating anonymous mmap()s, with portable mremap(). these must not be mixed with any standard mmap calls. */ void *mmap_anon(size_t length); void *mremap_anon(void *old_address, size_t old_size, size_t new_size, unsigned long flags); int munmap_anon(void *start, size_t length); size_t mmap_get_page_size(void) ATTR_CONST; #endif dovecot-2.2.33.2/src/lib/mountpoint.h0000644000175000017500000000133613123174404014251 00000000000000#ifndef MOUNTPOINT_H #define MOUNTPOINT_H struct mountpoint { char *device_path; char *mount_path; char *type; dev_t dev; unsigned int block_size; /* may not be set for iteration */ }; /* Returns 1 = found, 0 = not found (from mount tabs, or the path itself), -1 = error */ int mountpoint_get(const char *path, pool_t pool, struct mountpoint *point_r); /* Iterate through mountpoints */ struct mountpoint_iter *mountpoint_iter_init(void); /* Returns the next mountpoint or NULL if there are no more. */ const struct mountpoint *mountpoint_iter_next(struct mountpoint_iter *iter); /* Returns 0 if mountpoints were iterated successfully, -1 if it failed. */ int mountpoint_iter_deinit(struct mountpoint_iter **iter); #endif dovecot-2.2.33.2/src/lib/test-iostream-temp.c0000644000175000017500000000254213165463624015606 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "ostream.h" #include "iostream-temp.h" static void test_iostream_temp_create_sized_memory(void) { struct ostream *output; test_begin("iostream_temp_create_sized() memory"); output = iostream_temp_create_sized(".intentional-nonexistent-error/", 0, "test", 4); test_assert(o_stream_send(output, "123", 3) == 3); test_assert(o_stream_send(output, "4", 1) == 1); test_assert(o_stream_get_fd(output) == -1); /* now we'll try to switch to writing to a file, but it'll fail */ test_expect_errors(1); test_assert(o_stream_send(output, "5", 1) == 1); test_expect_no_more_errors(); test_assert(o_stream_get_fd(output) == -1); o_stream_destroy(&output); test_end(); } static void test_iostream_temp_create_sized_disk(void) { struct ostream *output; test_begin("iostream_temp_create_sized() disk"); output = iostream_temp_create_sized(".", 0, "test", 4); test_assert(o_stream_send(output, "123", 3) == 3); test_assert(o_stream_send(output, "4", 1) == 1); test_assert(o_stream_get_fd(output) == -1); test_assert(o_stream_send(output, "5", 1) == 1); test_assert(o_stream_get_fd(output) != -1); o_stream_destroy(&output); test_end(); } void test_iostream_temp(void) { test_iostream_temp_create_sized_memory(); test_iostream_temp_create_sized_disk(); } dovecot-2.2.33.2/src/lib/istream.h0000644000175000017500000002565713165463624013530 00000000000000#ifndef ISTREAM_H #define ISTREAM_H /* Note that some systems (Solaris) may use a macro to redefine struct stat */ #include struct istream { uoff_t v_offset; /* Commonly used errors: ENOENT - File/object doesn't exist. EPIPE - Stream ended unexpectedly (or i_stream_close() was called). ESPIPE - i_stream_seek() was used on a stream that can't be seeked. ENOBUFS - i_stream_read_next_line() was used for a too long line. EIO - Internal error. Retrying may work, but it may also be because of a misconfiguration. EINVAL - Stream is corrupted. */ int stream_errno; unsigned int mmaped:1; /* be careful when copying data */ unsigned int blocking:1; /* read() shouldn't return 0 */ unsigned int closed:1; unsigned int readable_fd:1; /* fd can be read directly if necessary (for sendfile()) */ unsigned int seekable:1; /* we can seek() backwards */ unsigned int eof:1; /* read() has reached to end of file (but may still be data available in buffer) */ struct istream_private *real_stream; }; typedef void istream_callback_t(void *context); struct istream *i_stream_create_fd(int fd, size_t max_buffer_size, bool autoclose_fd); /* The fd is set to -1 immediately to avoid accidentally closing it twice. */ struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); /* Open the given path only when something is actually tried to be read from the stream. */ struct istream *i_stream_create_file(const char *path, size_t max_buffer_size); struct istream *i_stream_create_mmap(int fd, size_t block_size, uoff_t start_offset, uoff_t v_size, bool autoclose_fd); /* Create an input stream using the provided data block. That data block must remain allocated during the full lifetime of the stream. */ struct istream *i_stream_create_from_data(const void *data, size_t size); #define i_stream_create_from_buffer(buf) \ i_stream_create_from_data((buf)->data, (buf)->used) #define i_stream_create_from_string(str) \ i_stream_create_from_data(str_data(str), str_len(str)) /* Create an input stream using a copy of the provided data block. The provided data block may be freed at any time. The copy is freed when the stream is destroyed. */ struct istream * i_stream_create_copy_from_data(const void *data, size_t size); #define i_stream_create_copy_from_buffer(buf) \ i_stream_create_copy_from_data((buf)->data, (buf)->used) #define i_stream_create_copy_from_string(str) \ i_stream_create_copy_from_data(str_data(str), str_len(str)) struct istream *i_stream_create_limit(struct istream *input, uoff_t v_size); struct istream *i_stream_create_range(struct istream *input, uoff_t v_offset, uoff_t v_size); struct istream *i_stream_create_error(int stream_errno); struct istream * i_stream_create_error_str(int stream_errno, const char *fmt, ...) ATTR_FORMAT(2, 3); /* Set name (e.g. path) for input stream. */ void i_stream_set_name(struct istream *stream, const char *name); /* Get input stream's name. If stream itself doesn't have a name, it looks up further into stream's parents until one of them has a name. Returns "" if stream has no name. */ const char *i_stream_get_name(struct istream *stream); /* Close this stream (but not its parents) and unreference it. */ void i_stream_destroy(struct istream **stream); /* Reference counting. References start from 1, so calling i_stream_unref() destroys the stream if i_stream_ref() is never used. */ void i_stream_ref(struct istream *stream); /* Unreferences the stream and sets stream pointer to NULL. */ void i_stream_unref(struct istream **stream); /* Call the given callback function when stream is destroyed. */ void i_stream_add_destroy_callback(struct istream *stream, istream_callback_t *callback, void *context) ATTR_NULL(3); #define i_stream_add_destroy_callback(stream, callback, context) \ i_stream_add_destroy_callback(stream + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (istream_callback_t *)callback, context) /* Remove the destroy callback. */ void i_stream_remove_destroy_callback(struct istream *stream, void (*callback)()); /* Return file descriptor for stream, or -1 if none is available. */ int i_stream_get_fd(struct istream *stream); /* Returns error string for the last error. It also returns "EOF" in case there is no error, but eof is set. Otherwise it returns "". */ const char *i_stream_get_error(struct istream *stream); /* Returns human-readable reason for why istream was disconnected. This can be called to log the error when i_stream_read() returns -1. If there's an error the output is identical to i_stream_get_error(). */ const char *i_stream_get_disconnect_reason(struct istream *stream); /* Mark the stream and all of its parent streams closed. Any reads after this will return -1. The data already read can still be used. */ void i_stream_close(struct istream *stream); /* Sync the stream with the underlying backend, ie. if a file has been modified, flush any cached data. */ void i_stream_sync(struct istream *stream); /* Change the initial size for stream's input buffer. This basically just grows the read buffer size from the default. This function has no effect unless it's called before reading anything. */ void i_stream_set_init_buffer_size(struct istream *stream, size_t size); /* Change the maximum size for stream's input buffer to grow. Useful only for buffered streams (currently only file). This changes also all the parent streams' max buffer size. */ void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size); /* Returns the current max. buffer size for the stream. This function also goesthrough all of the parent streams and returns the highest seen max buffer size. This is needed because some streams (e.g. istream-chain) change their max buffer size dynamically. */ size_t i_stream_get_max_buffer_size(struct istream *stream); /* Enable/disable i_stream[_read]_next_line() returning the last line if it doesn't end with LF. */ void i_stream_set_return_partial_line(struct istream *stream, bool set); /* Change whether buffers are allocated persistently (default=TRUE). When not, the memory usage is minimized by freeing the stream's buffers whenever they become empty. */ void i_stream_set_persistent_buffers(struct istream *stream, bool set); /* Returns number of bytes read if read was ok, -1 if EOF or error, -2 if the input buffer is full. */ ssize_t i_stream_read(struct istream *stream); /* Skip forward a number of bytes. Never fails, the next read tells if it was successful. */ void i_stream_skip(struct istream *stream, uoff_t count); /* Seek to specified position from beginning of file. Never fails, the next read tells if it was successful. This works only for files, others will set stream_errno=ESPIPE. */ void i_stream_seek(struct istream *stream, uoff_t v_offset); /* Like i_stream_seek(), but also giving a hint that after reading some data we could be seeking back to this mark or somewhere after it. If input stream's implementation is slow in seeking backwards, it can use this hint to cache some of the data in memory. */ void i_stream_seek_mark(struct istream *stream, uoff_t v_offset); /* Returns 0 if ok, -1 if error. As the underlying stream may not be a file, only some of the fields might be set, others would be zero. st_size is always set, and if it's not known, it's -1. If exact=FALSE, the stream may not return exactly correct values, but the returned values can be compared to see if anything had changed (eg. in compressed stream st_size could be compressed size) */ int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r); /* Similar to i_stream_stat() call. Returns 1 if size was successfully set, 0 if size is unknown, -1 if error. */ int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r); /* Returns TRUE if there are any bytes left to be read or in buffer. */ bool i_stream_have_bytes_left(struct istream *stream); /* Returns TRUE if there are no bytes buffered and read() returns EOF. */ bool i_stream_is_eof(struct istream *stream); /* Returns the absolute offset of the stream. This is the stream's current v_offset + the parent's absolute offset when the stream was created. */ uoff_t i_stream_get_absolute_offset(struct istream *stream); /* Gets the next line from stream and returns it, or NULL if more data is needed to make a full line. i_stream_set_return_partial_line() specifies if the last line should be returned if it doesn't end with LF. */ char *i_stream_next_line(struct istream *stream); /* Like i_stream_next_line(), but reads for more data if needed. Returns NULL if more data is needed or error occurred. If the input buffer gets full, stream_errno is set to ENOBUFS. */ char *i_stream_read_next_line(struct istream *stream); /* Returns TRUE if the last line read with i_stream_next_line() ended with CRLF (instead of LF). */ bool i_stream_last_line_crlf(struct istream *stream); /* Returns pointer to beginning of read data, or NULL if there's no data buffered. */ const unsigned char *i_stream_get_data(struct istream *stream, size_t *size_r); size_t i_stream_get_data_size(struct istream *stream); /* Like i_stream_get_data(), but returns non-const data. This only works with buffered streams (currently only file), others return NULL. */ unsigned char *i_stream_get_modifiable_data(struct istream *stream, size_t *size_r); /* Like i_stream_get_data(), but read more when needed. Returns 1 if more than threshold bytes are available, 0 if as much or less, -1 if error or EOF with no bytes read that weren't already in buffer, or -2 if stream's input buffer is full. */ int i_stream_read_data(struct istream *stream, const unsigned char **data_r, size_t *size_r, size_t threshold); /* Like i_stream_get_data(), but read more when needed. Returns 1 if at least the wanted number of bytes are available, 0 if less, -1 if error or EOF with no bytes read that weren't already in buffer, or -2 if stream's input buffer is full. */ static inline int i_stream_read_bytes(struct istream *stream, const unsigned char **data_r, size_t *size_r, size_t wanted) { i_assert(wanted > 0); return i_stream_read_data(stream, data_r, size_r, wanted - 1); } /* Short-hand for just requesting more data (i.e. even one byte) */ static inline int i_stream_read_more(struct istream *stream, const unsigned char **data_r, size_t *size_r) { return i_stream_read_bytes(stream, data_r, size_r, 1); } /* Append external data to input stream. Returns TRUE if successful, FALSE if there is not enough space in the stream. */ bool i_stream_add_data(struct istream *stream, const unsigned char *data, size_t size); void i_stream_set_input_pending(struct istream *stream, bool pending); /* If there are any I/O loop items associated with the stream, move all of them to current_ioloop. */ void i_stream_switch_ioloop(struct istream *stream); #endif dovecot-2.2.33.2/src/lib/hash-format.c0000644000175000017500000001317513123174404014245 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "hex-binary.h" #include "str.h" #include "hash-method.h" #include "hash-format.h" enum hash_encoding { HASH_ENCODING_HEX, HASH_ENCODING_HEX_SHORT, HASH_ENCODING_BASE64 }; struct hash_format_list { struct hash_format_list *next; const struct hash_method *method; void *context; unsigned int bits; enum hash_encoding encoding; }; struct hash_format { pool_t pool; const char *str; struct hash_format_list *list, **pos; unsigned char *digest; }; static int hash_format_parse(const char *str, unsigned int *idxp, const struct hash_method **method_r, unsigned int *bits_r, const char **error_r) { const char *name, *end, *bitsp; unsigned int bits, i = *idxp; /* we should have "hash_name}" or "hash_name:bits}" */ end = strchr(str+i, '}'); if (end == NULL) { *error_r = "Missing '}'"; return -1; } *idxp = end - str; name = t_strdup_until(str+i, end); bitsp = strchr(name, ':'); if (bitsp != NULL) name = t_strdup_until(name, bitsp++); *method_r = hash_method_lookup(name); if (*method_r == NULL) { *error_r = t_strconcat("Unknown hash method: ", name, NULL); return -1; } bits = (*method_r)->digest_size * 8; if (bitsp != NULL) { if (str_to_uint(bitsp, &bits) < 0 || bits == 0 || bits > (*method_r)->digest_size*8) { *error_r = t_strconcat("Invalid :bits number: ", bitsp, NULL); return -1; } if ((bits % 8) != 0) { *error_r = t_strconcat( "Currently :bits must be divisible by 8: ", bitsp, NULL); return -1; } } *bits_r = bits; return 0; } static int hash_format_string_analyze(struct hash_format *format, const char *str, const char **error_r) { struct hash_format_list *list; unsigned int i; for (i = 0; str[i] != '\0'; i++) { if (str[i] != '%') continue; i++; list = p_new(format->pool, struct hash_format_list, 1); list->encoding = HASH_ENCODING_HEX; *format->pos = list; format->pos = &list->next; if (str[i] == 'B') { list->encoding = HASH_ENCODING_BASE64; i++; } else if (str[i] == 'X') { list->encoding = HASH_ENCODING_HEX_SHORT; i++; } if (str[i++] != '{') { *error_r = "No '{' after '%'"; return -1; } if (hash_format_parse(str, &i, &list->method, &list->bits, error_r) < 0) return -1; list->context = p_malloc(format->pool, list->method->context_size); list->method->init(list->context); } return 0; } int hash_format_init(const char *format_string, struct hash_format **format_r, const char **error_r) { struct hash_format *format; pool_t pool; int ret; pool = pool_alloconly_create("hash format", 1024); format = p_new(pool, struct hash_format, 1); format->pool = pool; format->str = p_strdup(pool, format_string); format->pos = &format->list; T_BEGIN { ret = hash_format_string_analyze(format, format_string, error_r); if (ret < 0) *error_r = p_strdup(format->pool, *error_r); } T_END; if (ret < 0) { *error_r = t_strdup(*error_r); pool_unref(&pool); return -1; } *format_r = format; return 0; } void hash_format_loop(struct hash_format *format, const void *data, size_t size) { struct hash_format_list *list; for (list = format->list; list != NULL; list = list->next) list->method->loop(list->context, data, size); } void hash_format_reset(struct hash_format *format) { struct hash_format_list *list; for (list = format->list; list != NULL; list = list->next) { memset(list->context, 0, list->method->context_size); list->method->init(list->context); } } static void hash_format_digest(string_t *dest, const struct hash_format_list *list, const unsigned char *digest) { unsigned int i, orig_len, size = list->bits / 8; i_assert(list->bits % 8 == 0); switch (list->encoding) { case HASH_ENCODING_HEX: binary_to_hex_append(dest, digest, size); break; case HASH_ENCODING_HEX_SHORT: orig_len = str_len(dest); binary_to_hex_append(dest, digest, size); /* drop leading zeros, except if it's the only one */ for (i = orig_len; i < str_len(dest); i++) { if (str_data(dest)[i] != '0') break; } if (i == str_len(dest)) i--; str_delete(dest, orig_len, i-orig_len); break; case HASH_ENCODING_BASE64: orig_len = str_len(dest); base64_encode(digest, size, dest); /* drop trailing '=' chars */ while (str_len(dest) > orig_len && str_data(dest)[str_len(dest)-1] == '=') str_truncate(dest, str_len(dest)-1); break; } } void hash_format_write(struct hash_format *format, string_t *dest) { struct hash_format_list *list; const char *p; unsigned int i, max_digest_size = 0; for (list = format->list; list != NULL; list = list->next) { if (max_digest_size < list->method->digest_size) max_digest_size = list->method->digest_size; } if (format->digest == NULL) format->digest = p_malloc(format->pool, max_digest_size); list = format->list; for (i = 0; format->str[i] != '\0'; i++) { if (format->str[i] != '%') { str_append_c(dest, format->str[i]); continue; } /* we already verified that the string is ok */ i_assert(list != NULL); list->method->result(list->context, format->digest); hash_format_digest(dest, list, format->digest); list = list->next; p = strchr(format->str+i, '}'); i_assert(p != NULL); i = p - format->str; } } void hash_format_deinit(struct hash_format **_format, string_t *dest) { struct hash_format *format = *_format; *_format = NULL; hash_format_write(format, dest); pool_unref(&format->pool); } void hash_format_deinit_free(struct hash_format **_format) { struct hash_format *format = *_format; *_format = NULL; pool_unref(&format->pool); } dovecot-2.2.33.2/src/lib/timing.h0000644000175000017500000000200413123174404013315 00000000000000#ifndef TIMING_H #define TIMING_H struct timing *timing_init(void); void timing_deinit(struct timing **timing); /* Reset all events. */ void timing_reset(struct timing *timing); /* Add a new event that took the specified number of usecs. */ void timing_add_usecs(struct timing *timing, uint64_t usecs); /* Returns number of events added. */ unsigned int timing_get_count(const struct timing *timing); /* Returns the sum of all usecs added. */ uint64_t timing_get_sum(const struct timing *timing); /* Returns events' minimum. */ uint64_t timing_get_min(const struct timing *timing); /* Returns events' maximum. */ uint64_t timing_get_max(const struct timing *timing); /* Returns events' average. */ uint64_t timing_get_avg(const struct timing *timing); /* Returns events' approximate (through random subsampling) median. */ uint64_t timing_get_median(const struct timing *timing); /* Returns events' approximate (through random subsampling) 95th percentile. */ uint64_t timing_get_95th(const struct timing *timing); #endif dovecot-2.2.33.2/src/lib/ostream-multiplex.c0000644000175000017500000001437113167162046015535 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "ostream-private.h" #include "ostream-multiplex.h" /* all multiplex packets are [1 byte cid][4 byte length][data] */ struct multiplex_ostream; struct multiplex_ochannel { struct ostream_private ostream; struct multiplex_ostream *mstream; uint8_t cid; buffer_t *buf; time_t last_sent; bool closed:1; }; struct multiplex_ostream { struct ostream *parent; /* channel 0 is main channel */ uint8_t cur_channel; unsigned int remain; buffer_t *wbuf; size_t bufsize; ARRAY(struct multiplex_ochannel *) channels; bool destroyed:1; }; static struct multiplex_ochannel * get_channel(struct multiplex_ostream *mstream, uint8_t cid) { struct multiplex_ochannel **channelp; i_assert(mstream != NULL); array_foreach_modifiable(&mstream->channels, channelp) { if (*channelp != NULL && (*channelp)->cid == cid) return *channelp; } return NULL; } static void propagate_error(struct multiplex_ostream *mstream, int stream_errno) { struct multiplex_ochannel **channelp; array_foreach_modifiable(&mstream->channels, channelp) if (*channelp != NULL) (*channelp)->ostream.ostream.stream_errno = stream_errno; } static struct multiplex_ochannel *get_next_channel(struct multiplex_ostream *mstream) { time_t oldest = ioloop_time; struct multiplex_ochannel *channel = NULL; struct multiplex_ochannel **channelp; array_foreach_modifiable(&mstream->channels, channelp) if (*channelp != NULL && (*channelp)->last_sent <= oldest && (*channelp)->buf->used > 0) channel = *channelp; return channel; } static ssize_t o_stream_multiplex_sendv(struct multiplex_ostream *mstream) { struct multiplex_ochannel *channel; ssize_t ret = 0; if (mstream->bufsize <= mstream->wbuf->used + 5) return -2; while((channel = get_next_channel(mstream)) != NULL) { size_t tmp = mstream->bufsize - mstream->wbuf->used - 5; /* ensure it fits into 32 bit int */ size_t amt = I_MIN(UINT_MAX, I_MIN(tmp, channel->buf->used)); if (tmp == 0) break; uint32_t len = cpu32_to_be(amt); buffer_append(mstream->wbuf, &channel->cid, 1); buffer_append(mstream->wbuf, &len, 4); buffer_append(mstream->wbuf, channel->buf->data, amt); buffer_delete(channel->buf, 0, amt); channel->last_sent = ioloop_time; } if (mstream->wbuf->used > 0) { ret = o_stream_send(mstream->parent, mstream->wbuf->data, mstream->wbuf->used); if (ret < 0) { propagate_error(mstream, mstream->parent->stream_errno); return ret; } buffer_delete(mstream->wbuf, 0, ret); } return ret; } static ssize_t o_stream_multiplex_ochannel_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct multiplex_ochannel *channel = (struct multiplex_ochannel*)stream; ssize_t ret; size_t total = 0; if (channel->mstream->bufsize <= channel->buf->used) return -2; for(unsigned int i=0; i < iov_count; i++) { /* copy data to buffer */ size_t tmp = channel->mstream->bufsize - channel->buf->used; if (tmp == 0) break; buffer_append(channel->buf, iov[i].iov_base, I_MIN(tmp, iov[i].iov_len)); total += I_MIN(tmp, iov[i].iov_len); } stream->ostream.offset += total; if ((ret = o_stream_multiplex_sendv(channel->mstream)) < 0) return ret; return total; } static void o_stream_multiplex_ochannel_close(struct iostream_private *stream, bool close_parent) { struct multiplex_ochannel *const *channelp; struct multiplex_ochannel *channel = (struct multiplex_ochannel*)stream; (void)o_stream_flush(&channel->ostream.ostream); channel->closed = TRUE; if (close_parent) { array_foreach(&channel->mstream->channels, channelp) if (*channelp !=NULL && !(*channelp)->closed) return; o_stream_close(channel->mstream->parent); } } static void o_stream_multiplex_try_destroy(struct multiplex_ostream *mstream) { struct multiplex_ochannel **channelp; /* can't do anything until they are all closed */ array_foreach_modifiable(&mstream->channels, channelp) if (*channelp != NULL) return; o_stream_unref(&mstream->parent); array_free(&mstream->channels); buffer_free(&mstream->wbuf); i_free(mstream); } static void o_stream_multiplex_ochannel_destroy(struct iostream_private *stream) { struct multiplex_ochannel **channelp; struct multiplex_ochannel *channel = (struct multiplex_ochannel*)stream; o_stream_multiplex_ochannel_close(stream, TRUE); o_stream_unref(&channel->ostream.parent); if (channel->buf != NULL) buffer_free(&channel->buf); /* delete the channel */ array_foreach_modifiable(&channel->mstream->channels, channelp) { if (*channelp != NULL && (*channelp)->cid == channel->cid) { *channelp = NULL; break; } } o_stream_multiplex_try_destroy(channel->mstream); } static struct ostream * o_stream_add_channel_real(struct multiplex_ostream *mstream, uint8_t cid) { struct multiplex_ochannel *channel = i_new(struct multiplex_ochannel, 1); channel->cid = cid; channel->buf = buffer_create_dynamic(default_pool, 256); channel->mstream = mstream; channel->ostream.sendv = o_stream_multiplex_ochannel_sendv; channel->ostream.iostream.close = o_stream_multiplex_ochannel_close; channel->ostream.iostream.destroy = o_stream_multiplex_ochannel_destroy; channel->ostream.fd = o_stream_get_fd(mstream->parent); array_append(&channel->mstream->channels, &channel, 1); return o_stream_create(&channel->ostream, mstream->parent, mstream->bufsize); } struct ostream *o_stream_multiplex_add_channel(struct ostream *stream, uint8_t cid) { struct multiplex_ochannel *chan = (struct multiplex_ochannel *)stream->real_stream; i_assert(get_channel(chan->mstream, cid) == NULL); return o_stream_add_channel_real(chan->mstream, cid); } struct ostream *o_stream_create_multiplex(struct ostream *parent, size_t bufsize) { struct multiplex_ostream *mstream; mstream = i_new(struct multiplex_ostream, 1); mstream->parent = parent; mstream->bufsize = bufsize; mstream->wbuf = buffer_create_dynamic(default_pool, 256); i_array_init(&mstream->channels, 8); o_stream_ref(parent); return o_stream_add_channel_real(mstream, 0); } uint8_t o_stream_multiplex_get_channel_id(struct ostream *stream) { struct multiplex_ochannel *channel = (struct multiplex_ochannel *)stream->real_stream; return channel->cid; } dovecot-2.2.33.2/src/lib/safe-memset.c0000644000175000017500000000054013123174404014232 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "safe-memset.h" void safe_memset(void *data, int c, size_t size) { volatile unsigned int volatile_zero_idx = 0; volatile unsigned char *p = data; if (size == 0) return; do { memset(data, c, size); } while (p[volatile_zero_idx] != c); } dovecot-2.2.33.2/src/lib/iostream-rawlog-private.h0000644000175000017500000000115013123174404016613 00000000000000#ifndef IOSTREAM_RAWLOG_PRIVATE_H #define IOSTREAM_RAWLOG_PRIVATE_H #include "iostream-rawlog.h" #define IOSTREAM_RAWLOG_MAX_PREFIX_LEN 3 struct rawlog_iostream { struct iostream_private *iostream; enum iostream_rawlog_flags flags; struct ostream *rawlog_output; buffer_t *buffer; bool input; bool line_continued; }; void iostream_rawlog_init(struct rawlog_iostream *rstream, enum iostream_rawlog_flags flags, bool input); void iostream_rawlog_write(struct rawlog_iostream *rstream, const unsigned char *data, size_t size); void iostream_rawlog_close(struct rawlog_iostream *rstream); #endif dovecot-2.2.33.2/src/lib/mempool-alloconly.c0000644000175000017500000002516213165463624015510 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "safe-memset.h" #include "mempool.h" #ifdef HAVE_GC_GC_H # include #elif defined (HAVE_GC_H) # include #endif #define MAX_ALLOC_SIZE SSIZE_T_MAX struct alloconly_pool { struct pool pool; int refcount; struct pool_block *block; #ifdef DEBUG const char *name; size_t base_size; bool disable_warning; #endif bool clean_frees; }; struct pool_block { struct pool_block *prev; size_t size; size_t left; size_t last_alloc_size; /* unsigned char data[]; */ }; #define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block))) #define POOL_BLOCK_DATA(block) \ ((unsigned char *) (block) + SIZEOF_POOLBLOCK) #define DEFAULT_BASE_SIZE MEM_ALIGN(sizeof(struct alloconly_pool)) #ifdef DEBUG # define CLEAR_CHR 0xde # define SENTRY_COUNT 8 #else # define SENTRY_COUNT 0 # define CLEAR_CHR 0 #endif static const char *pool_alloconly_get_name(pool_t pool); static void pool_alloconly_ref(pool_t pool); static void pool_alloconly_unref(pool_t *pool); static void *pool_alloconly_malloc(pool_t pool, size_t size); static void pool_alloconly_free(pool_t pool, void *mem); static void *pool_alloconly_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size); static void pool_alloconly_clear(pool_t pool); static size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool); static void block_alloc(struct alloconly_pool *pool, size_t size); static const struct pool_vfuncs static_alloconly_pool_vfuncs = { pool_alloconly_get_name, pool_alloconly_ref, pool_alloconly_unref, pool_alloconly_malloc, pool_alloconly_free, pool_alloconly_realloc, pool_alloconly_clear, pool_alloconly_get_max_easy_alloc_size }; static const struct pool static_alloconly_pool = { .v = &static_alloconly_pool_vfuncs, .alloconly_pool = TRUE, .datastack_pool = FALSE }; #ifdef DEBUG static void check_sentries(struct pool_block *block) { const unsigned char *data = POOL_BLOCK_DATA(block); size_t i, max_pos, alloc_size, used_size; used_size = block->size - block->left; for (i = 0; i < used_size; ) { alloc_size = *(size_t *)(data + i); if (alloc_size == 0 || used_size - i < alloc_size) i_panic("mempool-alloconly: saved alloc size broken"); i += MEM_ALIGN(sizeof(alloc_size)); max_pos = i + MEM_ALIGN(alloc_size + SENTRY_COUNT); i += alloc_size; for (; i < max_pos; i++) { if (data[i] != CLEAR_CHR) i_panic("mempool-alloconly: buffer overflow"); } } if (i != used_size) i_panic("mempool-alloconly: used_size wrong"); /* The unused data must be NULs */ for (; i < block->size; i++) { if (data[i] != '\0') i_unreached(); } if (block->prev != NULL) check_sentries(block->prev); } #endif pool_t pool_alloconly_create(const char *name ATTR_UNUSED, size_t size) { struct alloconly_pool apool, *new_apool; size_t min_alloc = SIZEOF_POOLBLOCK + MEM_ALIGN(sizeof(struct alloconly_pool) + SENTRY_COUNT); #ifdef DEBUG min_alloc += MEM_ALIGN(strlen(name) + 1 + SENTRY_COUNT) + sizeof(size_t)*2; #endif /* create a fake alloconly_pool so we can call block_alloc() */ i_zero(&apool); apool.pool = static_alloconly_pool; apool.refcount = 1; if (size < min_alloc) size = nearest_power(size + min_alloc); block_alloc(&apool, size); /* now allocate the actual alloconly_pool from the created block */ new_apool = p_new(&apool.pool, struct alloconly_pool, 1); *new_apool = apool; #ifdef DEBUG if (strncmp(name, MEMPOOL_GROWING, strlen(MEMPOOL_GROWING)) == 0 || getenv("DEBUG_SILENT") != NULL) { name += strlen(MEMPOOL_GROWING); new_apool->disable_warning = TRUE; } new_apool->name = p_strdup(&new_apool->pool, name); /* set base_size so p_clear() doesn't trash alloconly_pool structure. */ new_apool->base_size = new_apool->block->size - new_apool->block->left; new_apool->block->last_alloc_size = 0; #endif /* the first pool allocations must be from the first block */ i_assert(new_apool->block->prev == NULL); return &new_apool->pool; } pool_t pool_alloconly_create_clean(const char *name, size_t size) { struct alloconly_pool *apool; pool_t pool; pool = pool_alloconly_create(name, size); apool = (struct alloconly_pool *)pool; apool->clean_frees = TRUE; return pool; } static void pool_alloconly_destroy(struct alloconly_pool *apool) { void *block; /* destroy all but the last block */ pool_alloconly_clear(&apool->pool); /* destroy the last block */ block = apool->block; #ifdef DEBUG safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + apool->block->size); #else if (apool->clean_frees) { safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + apool->block->size); } #endif #ifndef USE_GC free(block); #endif } static const char *pool_alloconly_get_name(pool_t pool ATTR_UNUSED) { #ifdef DEBUG struct alloconly_pool *apool = (struct alloconly_pool *)pool; return apool->name; #else return "alloconly"; #endif } static void pool_alloconly_ref(pool_t pool) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; apool->refcount++; } static void pool_alloconly_unref(pool_t *pool) { struct alloconly_pool *apool = (struct alloconly_pool *)*pool; if (--apool->refcount > 0) return; /* erase the pointer before freeing anything, as the pointer may exist inside the pool's memory area */ *pool = NULL; pool_alloconly_destroy(apool); } static void block_alloc(struct alloconly_pool *apool, size_t size) { struct pool_block *block; i_assert(size > SIZEOF_POOLBLOCK); if (apool->block != NULL) { /* each block is at least twice the size of the previous one */ if (size <= apool->block->size) size += apool->block->size; size = nearest_power(size); #ifdef DEBUG if (!apool->disable_warning) { /* i_warning() overwrites unallocated data in data stack, so make sure everything is allocated before calling it. */ t_buffer_alloc_last_full(); i_warning("Growing pool '%s' with: %"PRIuSIZE_T, apool->name, size); } #endif } #ifndef USE_GC block = calloc(size, 1); #else block = GC_malloc(size); #endif if (unlikely(block == NULL)) { i_fatal_status(FATAL_OUTOFMEM, "block_alloc(%"PRIuSIZE_T "): Out of memory", size); } block->prev = apool->block; apool->block = block; block->size = size - SIZEOF_POOLBLOCK; block->left = block->size; } static void *pool_alloconly_malloc(pool_t pool, size_t size) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; void *mem; size_t alloc_size; if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); #ifndef DEBUG alloc_size = MEM_ALIGN(size); #else alloc_size = MEM_ALIGN(sizeof(size)) + MEM_ALIGN(size + SENTRY_COUNT); #endif if (apool->block->left < alloc_size) { /* we need a new block */ block_alloc(apool, alloc_size + SIZEOF_POOLBLOCK); } mem = POOL_BLOCK_DATA(apool->block) + (apool->block->size - apool->block->left); apool->block->left -= alloc_size; apool->block->last_alloc_size = alloc_size; #ifdef DEBUG memcpy(mem, &size, sizeof(size)); mem = PTR_OFFSET(mem, MEM_ALIGN(sizeof(size))); /* write CLEAR_CHRs to sentry */ memset(PTR_OFFSET(mem, size), CLEAR_CHR, MEM_ALIGN(size + SENTRY_COUNT) - size); #endif return mem; } static void pool_alloconly_free(pool_t pool, void *mem) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; /* we can free only the last allocation */ if (POOL_BLOCK_DATA(apool->block) + (apool->block->size - apool->block->left - apool->block->last_alloc_size) == mem) { memset(mem, 0, apool->block->last_alloc_size); apool->block->left += apool->block->last_alloc_size; apool->block->last_alloc_size = 0; } } static bool pool_try_grow(struct alloconly_pool *apool, void *mem, size_t size) { /* see if we want to grow the memory we allocated last */ if (POOL_BLOCK_DATA(apool->block) + (apool->block->size - apool->block->left - apool->block->last_alloc_size) == mem) { /* yeah, see if we can grow */ if (apool->block->left >= size-apool->block->last_alloc_size) { /* just shrink the available size */ apool->block->left -= size - apool->block->last_alloc_size; apool->block->last_alloc_size = size; return TRUE; } } return FALSE; } static void *pool_alloconly_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; unsigned char *new_mem; if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size); if (mem == NULL) return pool_alloconly_malloc(pool, new_size); if (new_size <= old_size) return mem; new_size = MEM_ALIGN(new_size); /* see if we can directly grow it */ if (!pool_try_grow(apool, mem, new_size)) { /* slow way - allocate + copy */ new_mem = pool_alloconly_malloc(pool, new_size); memcpy(new_mem, mem, old_size); mem = new_mem; } return mem; } static void pool_alloconly_clear(pool_t pool) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; struct pool_block *block; size_t base_size, avail_size; #ifdef DEBUG check_sentries(apool->block); #endif /* destroy all blocks but the oldest, which contains the struct alloconly_pool allocation. */ while (apool->block->prev != NULL) { block = apool->block; apool->block = block->prev; #ifdef DEBUG safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + block->size); #else if (apool->clean_frees) { safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + block->size); } #endif #ifndef USE_GC free(block); #endif } /* clear the first block */ #ifdef DEBUG base_size = apool->base_size; #else base_size = DEFAULT_BASE_SIZE; #endif avail_size = apool->block->size - base_size; memset(PTR_OFFSET(POOL_BLOCK_DATA(apool->block), base_size), 0, avail_size - apool->block->left); apool->block->left = avail_size; apool->block->last_alloc_size = 0; } static size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; return apool->block->left; } size_t pool_alloconly_get_total_used_size(pool_t pool) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; struct pool_block *block; size_t size = 0; i_assert(pool->v == &static_alloconly_pool_vfuncs); for (block = apool->block; block != NULL; block = block->prev) size += block->size - block->left; return size; } size_t pool_alloconly_get_total_alloc_size(pool_t pool) { struct alloconly_pool *apool = (struct alloconly_pool *)pool; struct pool_block *block; size_t size = 0; i_assert(pool->v == &static_alloconly_pool_vfuncs); for (block = apool->block; block != NULL; block = block->prev) size += block->size + SIZEOF_POOLBLOCK; return size; } dovecot-2.2.33.2/src/lib/test-str-find.c0000644000175000017500000000375213123174404014537 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str-find.h" static const char *str_find_text = "xababcd"; static bool test_str_find_substring(const char *key, int expected_pos) { const unsigned char *text = (const unsigned char *)str_find_text; const unsigned int text_len = strlen(str_find_text); struct str_find_context *ctx; unsigned int i, j, pos, max, offset; bool ret; ctx = str_find_init(pool_datastack_create(), key); /* divide text into every possible block combination and test that it matches */ i_assert(text_len > 0); max = 1U << (text_len-1); for (i = 0; i < max; i++) { str_find_reset(ctx); pos = 0; offset = 0; ret = FALSE; for (j = 0; j < text_len; j++) { if ((i & (1 << j)) != 0) { if (str_find_more(ctx, text+pos, j-pos+1)) { ret = TRUE; break; } offset += j-pos + 1; pos = j + 1; } } if (pos != text_len && !ret) { if (str_find_more(ctx, text+pos, j-pos)) ret = TRUE; } if (expected_pos < 0) { if (ret) return FALSE; } else { if (!ret) return FALSE; pos = str_find_get_match_end_pos(ctx) + offset - strlen(key); if ((int)pos != expected_pos) return FALSE; } } return TRUE; } struct str_find_input { const char *str; int pos; }; void test_str_find(void) { static const char *fail_input[] = { "xabc", "xabd", "abd" }; unsigned int idx, len; const char *key, *p; unsigned int i; bool success = TRUE; for (idx = 0; idx < strlen(str_find_text); idx++) { for (len = strlen(str_find_text)-idx; len > 0; len--) { /* we'll get a search key for all substrings of text */ T_BEGIN { key = t_strndup(str_find_text + idx, len); p = strstr(str_find_text, key); success = test_str_find_substring(key, p - str_find_text); } T_END; if (!success) break; } } for (i = 0; i < N_ELEMENTS(fail_input) && success; i++) success = test_str_find_substring(fail_input[i], -1); test_out("str_find()", success); } dovecot-2.2.33.2/src/lib/safe-mkstemp.h0000644000175000017500000000115713123174404014432 00000000000000#ifndef SAFE_MKSTEMP_H #define SAFE_MKSTEMP_H /* Create a new file with a given prefix. The string is updated to contain the created filename. uid and gid can be (uid_t)-1 and (gid_t)-1 to use the defaults. */ int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid); int safe_mkstemp_group(string_t *prefix, mode_t mode, gid_t gid, const char *gid_origin); /* Append host and PID to the prefix. */ int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid); int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode, gid_t gid, const char *gid_origin); #endif dovecot-2.2.33.2/src/lib/istream-multiplex.h0000644000175000017500000000045213165463543015533 00000000000000#ifndef ISTREAM_MULTIPLEX #define ISTREAM_MULTIPLEX 1 struct istream *i_stream_create_multiplex(struct istream *parent, size_t bufsize); struct istream *i_stream_multiplex_add_channel(struct istream *stream, uint8_t cid); uint8_t i_stream_multiplex_get_channel_id(struct istream *stream); #endif dovecot-2.2.33.2/src/lib/hash-format.h0000644000175000017500000000166713123174404014255 00000000000000#ifndef HASH_FORMAT_H #define HASH_FORMAT_H struct hash_format; /* Initialize formatting hash. Format can contain text with %{sha1} style variables. Each hash hash can be also truncated by specifying the number of bits to truncate to, such as %{sha1:80}. */ int hash_format_init(const char *format_string, struct hash_format **format_r, const char **error_r); /* Add more data to hash. */ void hash_format_loop(struct hash_format *format, const void *data, size_t size); /* Finish the hash and write it into given string. */ void hash_format_write(struct hash_format *format, string_t *dest); /* Reset hash to initial state. */ void hash_format_reset(struct hash_format *format); /* Write the hash into given string and free used memory. */ void hash_format_deinit(struct hash_format **format, string_t *dest); /* Free used memory without writing to string. */ void hash_format_deinit_free(struct hash_format **format); #endif dovecot-2.2.33.2/src/lib/md4.h0000644000175000017500000000152113123174404012515 00000000000000/* * This is an OpenSSL-compatible implementation of the RSA Data Security, * Inc. MD4 Message-Digest Algorithm. * * Written by Solar Designer in 2001, and placed in * the public domain. See md4.c for more information. */ #ifndef MD4_H #define MD4_H #include "hash-method.h" #define MD4_RESULTLEN (128/8) struct md4_context { uint_fast32_t lo, hi; uint_fast32_t a, b, c, d; unsigned char buffer[64]; uint_fast32_t block[MD4_RESULTLEN]; }; void md4_init(struct md4_context *ctx); void md4_update(struct md4_context *ctx, const void *data, size_t size); void md4_final(struct md4_context *ctx, unsigned char result[STATIC_ARRAY MD4_RESULTLEN]); void md4_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY MD4_RESULTLEN]); extern const struct hash_method hash_method_md4; #endif dovecot-2.2.33.2/src/lib/istream-rawlog.c0000644000175000017500000000677613165463624015015 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream.h" #include "iostream-rawlog-private.h" #include "istream-private.h" #include "istream-rawlog.h" struct rawlog_istream { struct istream_private istream; struct rawlog_iostream riostream; }; static void i_stream_rawlog_close(struct iostream_private *stream, bool close_parent) { struct rawlog_istream *rstream = (struct rawlog_istream *)stream; iostream_rawlog_close(&rstream->riostream); if (close_parent) i_stream_close(rstream->istream.parent); } static void i_stream_rawlog_destroy(struct iostream_private *stream) { struct rawlog_istream *rstream = (struct rawlog_istream *)stream; uoff_t v_offset; v_offset = rstream->istream.parent_start_offset + rstream->istream.istream.v_offset; if (rstream->istream.parent->seekable || v_offset > rstream->istream.parent->v_offset) { /* get to same position in parent stream */ i_stream_seek(rstream->istream.parent, v_offset); } i_stream_unref(&rstream->istream.parent); } static ssize_t i_stream_rawlog_read(struct istream_private *stream) { struct rawlog_istream *rstream = (struct rawlog_istream *)stream; ssize_t ret; size_t pos; i_stream_seek(stream->parent, rstream->istream.parent_start_offset + stream->istream.v_offset); stream->pos -= stream->skip; stream->skip = 0; stream->buffer = i_stream_get_data(stream->parent, &pos); if (pos > stream->pos) ret = 0; else do { if ((ret = i_stream_read(stream->parent)) == -2) return -2; stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; stream->buffer = i_stream_get_data(stream->parent, &pos); } while (pos <= stream->pos && ret > 0); if (pos <= stream->pos) ret = ret == 0 ? 0 : -1; else { ret = (ssize_t)(pos - stream->pos); iostream_rawlog_write(&rstream->riostream, stream->buffer + stream->pos, ret); } stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); return ret; } struct istream * i_stream_create_rawlog(struct istream *input, const char *rawlog_path, int rawlog_fd, enum iostream_rawlog_flags flags) { struct ostream *rawlog_output; bool autoclose_fd = (flags & IOSTREAM_RAWLOG_FLAG_AUTOCLOSE) != 0; i_assert(rawlog_path != NULL); i_assert(rawlog_fd != -1); rawlog_output = o_stream_create_fd(rawlog_fd, 0, autoclose_fd); o_stream_set_name(rawlog_output, t_strdup_printf("rawlog(%s)", rawlog_path)); return i_stream_create_rawlog_from_stream(input, rawlog_output, flags); } struct istream * i_stream_create_rawlog_from_stream(struct istream *input, struct ostream *rawlog_output, enum iostream_rawlog_flags flags) { struct rawlog_istream *rstream; rstream = i_new(struct rawlog_istream, 1); rstream->istream.max_buffer_size = input->real_stream->max_buffer_size; rstream->istream.stream_size_passthrough = TRUE; rstream->riostream.rawlog_output = rawlog_output; iostream_rawlog_init(&rstream->riostream, flags, TRUE); rstream->istream.read = i_stream_rawlog_read; rstream->istream.iostream.close = i_stream_rawlog_close; rstream->istream.iostream.destroy = i_stream_rawlog_destroy; rstream->istream.istream.readable_fd = input->readable_fd; rstream->istream.istream.blocking = input->blocking; rstream->istream.istream.seekable = input->seekable; return i_stream_create(&rstream->istream, input, i_stream_get_fd(input)); } dovecot-2.2.33.2/src/lib/ostream.h0000644000175000017500000001750313165463624013525 00000000000000#ifndef OSTREAM_H #define OSTREAM_H #include "ioloop.h" struct ostream { uoff_t offset; /* errno for the last operation send/seek operation. cleared before each call. */ int stream_errno; /* errno of the last failed send/seek. never cleared. */ int last_failed_errno; /* overflow is set when some of the data given to send() functions was neither sent nor buffered. It's never unset inside ostream code. */ unsigned int overflow:1; /* o_stream_send() writes all the data or returns failure */ unsigned int blocking:1; unsigned int closed:1; struct ostream_private *real_stream; }; /* Returns 1 if all data is sent (not necessarily flushed), 0 if not. Pretty much the only real reason to return 0 is if you wish to send more data to client which isn't buffered, eg. o_stream_send_istream(). */ typedef int stream_flush_callback_t(void *context); typedef void ostream_callback_t(void *context); /* Create new output stream from given file descriptor. If max_buffer_size is 0, an "optimal" buffer size is used (max 128kB). */ struct ostream * o_stream_create_fd(int fd, size_t max_buffer_size, bool autoclose_fd); /* The fd is set to -1 immediately to avoid accidentally closing it twice. */ struct ostream *o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); /* Create an output stream from a regular file which begins at given offset. If offset==(uoff_t)-1, the current offset isn't known. */ struct ostream * o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd); struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset); /* Create an output stream to a buffer. */ struct ostream *o_stream_create_buffer(buffer_t *buf); /* Create an output streams that always fails the writes. */ struct ostream *o_stream_create_error(int stream_errno); struct ostream * o_stream_create_error_str(int stream_errno, const char *fmt, ...) ATTR_FORMAT(2, 3); /* Create an output stream that simply passes through data. This is mainly useful as a wrapper when combined with destroy callbacks. */ struct ostream *o_stream_create_passthrough(struct ostream *output); /* Set name (e.g. path) for output stream. */ void o_stream_set_name(struct ostream *stream, const char *name); /* Get output stream's name. Returns "" if stream has no name. */ const char *o_stream_get_name(struct ostream *stream); /* Return file descriptor for stream, or -1 if none is available. */ int o_stream_get_fd(struct ostream *stream); /* Returns error string for the previous error (stream_errno, not last_failed_errno). */ const char *o_stream_get_error(struct ostream *stream); /* Close this stream (but not its parents) and unreference it. */ void o_stream_destroy(struct ostream **stream); /* Reference counting. References start from 1, so calling o_stream_unref() destroys the stream if o_stream_ref() is never used. */ void o_stream_ref(struct ostream *stream); /* Unreferences the stream and sets stream pointer to NULL. */ void o_stream_unref(struct ostream **stream); /* Call the given callback function when stream is destroyed. */ void o_stream_add_destroy_callback(struct ostream *stream, ostream_callback_t *callback, void *context) ATTR_NULL(3); #define o_stream_add_destroy_callback(stream, callback, context) \ o_stream_add_destroy_callback(stream + \ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ (ostream_callback_t *)callback, context) /* Remove the destroy callback. */ void o_stream_remove_destroy_callback(struct ostream *stream, void (*callback)()); /* Mark the stream and all of its parent streams closed. Nothing will be sent after this call. */ void o_stream_close(struct ostream *stream); /* Set IO_WRITE callback. Default will just try to flush the output and finishes when the buffer is empty. */ void o_stream_set_flush_callback(struct ostream *stream, stream_flush_callback_t *callback, void *context) ATTR_NULL(3); #define o_stream_set_flush_callback(stream, callback, context) \ o_stream_set_flush_callback(stream + \ CALLBACK_TYPECHECK(callback, int (*)(typeof(context))), \ (stream_flush_callback_t *)callback, context) void o_stream_unset_flush_callback(struct ostream *stream); /* Change the maximum size for stream's output buffer to grow. */ void o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size); /* Returns the current max. buffer size. */ size_t o_stream_get_max_buffer_size(struct ostream *stream); /* Delays sending as far as possible, writing only full buffers. Also sets TCP_CORK on if supported. */ void o_stream_cork(struct ostream *stream); void o_stream_uncork(struct ostream *stream); bool o_stream_is_corked(struct ostream *stream); /* Try to flush the output stream. Returns 1 if all sent, 0 if not, -1 if error. */ int o_stream_flush(struct ostream *stream); /* Set "flush pending" state of stream. If set, the flush callback is called when more data is allowed to be sent, even if the buffer itself is empty. */ void o_stream_set_flush_pending(struct ostream *stream, bool set); /* Returns number of bytes currently in buffer. */ size_t o_stream_get_buffer_used_size(const struct ostream *stream) ATTR_PURE; /* Returns number of bytes we can still write without failing. */ size_t o_stream_get_buffer_avail_size(const struct ostream *stream) ATTR_PURE; /* Seek to specified position from beginning of file. This works only for files. Returns 1 if successful, -1 if error. */ int o_stream_seek(struct ostream *stream, uoff_t offset); /* Returns number of bytes sent, -1 = error */ ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size); ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count); ssize_t o_stream_send_str(struct ostream *stream, const char *str); /* Send with delayed error handling. o_stream_has_errors() or o_stream_ignore_last_errors() must be called after these functions before the stream is destroyed. */ void o_stream_nsend(struct ostream *stream, const void *data, size_t size); void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count); void o_stream_nsend_str(struct ostream *stream, const char *str); void o_stream_nflush(struct ostream *stream); /* Flushes the stream and returns -1 if stream->last_failed_errno is non-zero. Marks the stream's error handling as completed. errno is also set to last_failed_errno. */ int o_stream_nfinish(struct ostream *stream); /* Marks the stream's error handling as completed to avoid i_panic() on destroy. */ void o_stream_ignore_last_errors(struct ostream *stream); /* If error handling is disabled, the i_panic() on destroy is never called. This function can be called immediately after the stream is created. When creating wrapper streams, they copy this behavior from the parent stream. */ void o_stream_set_no_error_handling(struct ostream *stream, bool set); /* Send data from input stream. Returns number of bytes sent, or -1 if error in either outstream or instream. Note that this function may block if either instream or outstream is blocking. Also note that this function may not add anything to the output buffer, so if you want the flush callback to be called when more data can be written, you'll need to call o_stream_set_flush_pending() manually. It's also possible to use this function to copy data within same file descriptor. If the file must be grown, you have to do it manually before calling this function. */ off_t o_stream_send_istream(struct ostream *outstream, struct istream *instream); /* Write data to specified offset. Returns 0 if successful, -1 if error. */ int o_stream_pwrite(struct ostream *stream, const void *data, size_t size, uoff_t offset); /* If there are any I/O loop items associated with the stream, move all of them to current_ioloop. */ void o_stream_switch_ioloop(struct ostream *stream); #endif dovecot-2.2.33.2/src/lib/env-util.c0000644000175000017500000000617613165463624013615 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "env-util.h" #ifdef __APPLE__ # include #endif struct env_backup { pool_t pool; const char **strings; }; static pool_t env_pool = NULL; void env_put(const char *env) { if (env_pool == NULL) { env_pool = pool_alloconly_create(MEMPOOL_GROWING"Environment", 2048); } if (putenv(p_strdup(env_pool, env)) != 0) i_fatal("putenv(%s) failed: %m", env); } void env_remove(const char *name) { #ifdef HAVE_UNSETENV #ifdef UNSETENV_RET_INT if (unsetenv(name) < 0) i_fatal("unsetenv(%s) failed: %m", name); #else unsetenv(name); #endif #else extern char **environ; size_t len; char **envp; len = strlen(name); for (envp = environ; *envp != NULL; envp++) { if (strncmp(name, *envp, len) == 0 && (*envp)[len] == '=') { do { envp[0] = envp[1]; } while (*++envp != NULL); break; } } #endif } void env_clean(void) { #ifdef HAVE_CLEARENV if (clearenv() < 0) i_fatal("clearenv() failed"); #else char ***environ_p = env_get_environ_p(); /* Try to clear the environment. a) environ = NULL crashes on OS X. b) *environ = NULL doesn't work on FreeBSD 7.0. c) environ = emptyenv doesn't work on Haiku OS d) environ = calloc() should work everywhere */ *environ_p = calloc(1, sizeof(**environ_p)); #endif if (env_pool != NULL) p_clear(env_pool); } static void env_clean_except_real(const char *const preserve_envs[]) { ARRAY_TYPE(const_string) copy; const char *value, *const *envp; unsigned int i; t_array_init(©, 16); for (i = 0; preserve_envs[i] != NULL; i++) { const char *key = preserve_envs[i]; value = getenv(key); if (value != NULL) { value = t_strconcat(key, "=", value, NULL); array_append(©, &value, 1); } } /* Note that if the original environment was set with env_put(), the environment strings will be invalid after env_clean(). That's why we t_strconcat() them above. */ env_clean(); array_foreach(©, envp) env_put(*envp); } void env_clean_except(const char *const preserve_envs[]) { T_BEGIN { env_clean_except_real(preserve_envs); } T_END; } struct env_backup *env_backup_save(void) { char **environ = *env_get_environ_p(); struct env_backup *env; unsigned int i, count; pool_t pool; i_assert(environ != NULL); for (count = 0; environ[count] != NULL; count++) ; pool = pool_alloconly_create("saved environment", 4096); env = p_new(pool, struct env_backup, 1); env->pool = pool; env->strings = p_new(pool, const char *, count + 1); for (i = 0; i < count; i++) env->strings[i] = p_strdup(pool, environ[i]); return env; } void env_backup_restore(struct env_backup *env) { unsigned int i; env_clean(); for (i = 0; env->strings[i] != NULL; i++) env_put(env->strings[i]); } void env_backup_free(struct env_backup **_env) { struct env_backup *env = *_env; *_env = NULL; pool_unref(&env->pool); } char ***env_get_environ_p(void) { #ifdef __APPLE__ return _NSGetEnviron(); #else extern char **environ; return &environ; #endif } void env_deinit(void) { if (env_pool != NULL) pool_unref(&env_pool); } dovecot-2.2.33.2/src/lib/test-istream-unix.c0000644000175000017500000001036613165463624015450 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "net.h" #include "fd-set-nonblock.h" #include "fdpass.h" #include "istream.h" #include "istream-unix.h" #include #include #include static int send_fd, send_fd2; static void write_one(int fd) { if (write(fd, "1", 1) < 0) i_fatal("write() failed: %m"); } static void read_one(int fd) { char buf; if (read(fd, &buf, 1) < 0) i_fatal("read() failed: m"); } static void test_server_read_nofd(struct istream *input, unsigned int idx) { const unsigned char *data; size_t size; test_assert_idx(i_stream_read_data(input, &data, &size, 0) == 1, idx); i_stream_skip(input, 1); test_assert_idx(i_stream_unix_get_read_fd(input) == -1, idx); } static void test_server_read_fd(struct istream *input, int wanted_fd, unsigned int idx) { struct stat st1, st2; const unsigned char *data; size_t size; int recv_fd; test_assert_idx(i_stream_read_data(input, &data, &size, 0) == 1, idx); i_stream_skip(input, 1); test_assert_idx((recv_fd = i_stream_unix_get_read_fd(input)) != -1, idx); if (recv_fd != -1) { if (fstat(recv_fd, &st1) < 0 || fstat(wanted_fd, &st2) < 0) i_fatal("fstat() failed: %m"); test_assert_idx(st1.st_ino == st2.st_ino, idx); i_close_fd(&recv_fd); } } static void test_istream_unix_server(int fd) { struct istream *input; const unsigned char *data; size_t size; input = i_stream_create_unix(fd, 1024); /* 1) simple read */ test_server_read_nofd(input, 1); write_one(fd); /* 2) fd was sent but we won't get it */ test_server_read_nofd(input, 2); /* we still shouldn't have the fd */ fd_set_nonblock(fd, TRUE); i_stream_unix_set_read_fd(input); test_assert(i_stream_read_data(input, &data, &size, 0) == 0); test_assert(i_stream_unix_get_read_fd(input) == -1); fd_set_nonblock(fd, FALSE); write_one(fd); /* 3) the previous fd should be lost now */ test_server_read_nofd(input, 3); write_one(fd); /* 4) we should get the fd now */ test_server_read_fd(input, send_fd2, 4); write_one(fd); /* 5) the previous fd shouldn't be returned anymore */ i_stream_unix_set_read_fd(input); test_server_read_nofd(input, 5); write_one(fd); /* 6) with i_stream_unix_unset_read_fd() we shouldn't get fd anymore */ i_stream_unix_unset_read_fd(input); test_server_read_nofd(input, 6); write_one(fd); /* 7-8) two fds were sent, but we'll get only the first one */ i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd, 7); test_server_read_nofd(input, 8); write_one(fd); /* 9-10) two fds were sent, and we'll get them both */ i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd, 9); i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd2, 10); write_one(fd); i_stream_destroy(&input); i_close_fd(&fd); } static void test_istream_unix_client(int fd) { /* 1) */ write_one(fd); read_one(fd); /* 2) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 3) */ write_one(fd); read_one(fd); /* 4) */ if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 5) */ write_one(fd); read_one(fd); /* 6) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 7-8) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 9-10) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); i_close_fd(&fd); } void test_istream_unix(void) { int fd[2]; test_begin("istream unix"); if ((send_fd = open("/dev/null", O_RDONLY)) == -1) i_fatal("open(/dev/null) failed: %m"); if ((send_fd2 = open("/dev/zero", O_RDONLY)) == -1) i_fatal("open(/dev/zero) failed: %m"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) i_fatal("socketpair() failed: %m"); switch (fork()) { case -1: i_fatal("fork() failed: %m"); case 0: i_close_fd(&fd[0]); test_istream_unix_client(fd[1]); test_exit(0); default: i_close_fd(&fd[1]); test_istream_unix_server(fd[0]); break; } i_close_fd(&send_fd); i_close_fd(&send_fd2); test_end(); } dovecot-2.2.33.2/src/lib/mempool-unsafe-datastack.c0000644000175000017500000000504013165463624016723 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mempool.h" static const char *pool_unsafe_data_stack_get_name(pool_t pool); static void pool_unsafe_data_stack_ref(pool_t pool); static void pool_unsafe_data_stack_unref(pool_t *pool); static void *pool_unsafe_data_stack_malloc(pool_t pool, size_t size); static void pool_unsafe_data_stack_free(pool_t pool, void *mem); static void *pool_unsafe_data_stack_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size); static void pool_unsafe_data_stack_clear(pool_t pool); static size_t pool_unsafe_data_stack_get_max_easy_alloc_size(pool_t pool); static struct pool_vfuncs static_unsafe_data_stack_pool_vfuncs = { pool_unsafe_data_stack_get_name, pool_unsafe_data_stack_ref, pool_unsafe_data_stack_unref, pool_unsafe_data_stack_malloc, pool_unsafe_data_stack_free, pool_unsafe_data_stack_realloc, pool_unsafe_data_stack_clear, pool_unsafe_data_stack_get_max_easy_alloc_size }; static struct pool static_unsafe_data_stack_pool = { .v = &static_unsafe_data_stack_pool_vfuncs, .alloconly_pool = TRUE, .datastack_pool = TRUE }; pool_t unsafe_data_stack_pool = &static_unsafe_data_stack_pool; static const char *pool_unsafe_data_stack_get_name(pool_t pool ATTR_UNUSED) { return "unsafe data stack"; } static void pool_unsafe_data_stack_ref(pool_t pool ATTR_UNUSED) { } static void pool_unsafe_data_stack_unref(pool_t *pool ATTR_UNUSED) { } static void *pool_unsafe_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size) { if (unlikely(size == 0 || size > SSIZE_T_MAX)) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size); return t_malloc0(size); } static void pool_unsafe_data_stack_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED) { } static void *pool_unsafe_data_stack_realloc(pool_t pool ATTR_UNUSED, void *mem, size_t old_size, size_t new_size) { void *new_mem; /* @UNSAFE */ if (new_size == 0 || new_size > SSIZE_T_MAX) i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size); if (mem == NULL) return pool_unsafe_data_stack_malloc(pool, new_size); if (old_size >= new_size) return mem; if (!t_try_realloc(mem, new_size)) { new_mem = t_malloc(new_size); memcpy(new_mem, mem, old_size); mem = new_mem; } memset((char *) mem + old_size, 0, new_size - old_size); return mem; } static void pool_unsafe_data_stack_clear(pool_t pool ATTR_UNUSED) { } static size_t pool_unsafe_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) { return t_get_bytes_available(); } dovecot-2.2.33.2/src/lib/unlink-old-files.h0000644000175000017500000000052113123174404015204 00000000000000#ifndef UNLINK_OLD_FILES_H #define UNLINK_OLD_FILES_H /* Unlink all files from directory beginning with given prefix and having ctime older than min_time. Makes sure that the directory's atime is updated. Returns -1 if there were some errors. */ int unlink_old_files(const char *dir, const char *prefix, time_t min_time); #endif dovecot-2.2.33.2/src/lib/UnicodeData.txt0000644000175000017500000633565312716462145014640 000000000000000000;;Cc;0;BN;;;;;N;NULL;;;; 0001;;Cc;0;BN;;;;;N;START OF HEADING;;;; 0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; 0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; 0004;;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; 0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; 0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; 0007;;Cc;0;BN;;;;;N;BELL;;;; 0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; 0009;;Cc;0;S;;;;;N;CHARACTER TABULATION;;;; 000A;;Cc;0;B;;;;;N;LINE FEED (LF);;;; 000B;;Cc;0;S;;;;;N;LINE TABULATION;;;; 000C;;Cc;0;WS;;;;;N;FORM FEED (FF);;;; 000D;;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;; 000E;;Cc;0;BN;;;;;N;SHIFT OUT;;;; 000F;;Cc;0;BN;;;;;N;SHIFT IN;;;; 0010;;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;; 0011;;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;; 0012;;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;; 0013;;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;; 0014;;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;; 0015;;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;; 0016;;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;; 0017;;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;; 0018;;Cc;0;BN;;;;;N;CANCEL;;;; 0019;;Cc;0;BN;;;;;N;END OF MEDIUM;;;; 001A;;Cc;0;BN;;;;;N;SUBSTITUTE;;;; 001B;;Cc;0;BN;;;;;N;ESCAPE;;;; 001C;;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;; 001D;;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;; 001E;;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;; 001F;;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;; 0020;SPACE;Zs;0;WS;;;;;N;;;;; 0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; 0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;; 0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;; 0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; 0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;; 0026;AMPERSAND;Po;0;ON;;;;;N;;;;; 0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;; 0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;; 0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;; 002A;ASTERISK;Po;0;ON;;;;;N;;;;; 002B;PLUS SIGN;Sm;0;ES;;;;;N;;;;; 002C;COMMA;Po;0;CS;;;;;N;;;;; 002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;; 002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;; 002F;SOLIDUS;Po;0;CS;;;;;N;SLASH;;;; 0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;; 0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;; 0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;; 0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;; 0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;; 0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;; 0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;; 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; 0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;; 0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;; 003A;COLON;Po;0;CS;;;;;N;;;;; 003B;SEMICOLON;Po;0;ON;;;;;N;;;;; 003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;; 003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;; 003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;; 003F;QUESTION MARK;Po;0;ON;;;;;N;;;;; 0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;; 0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061; 0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062; 0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063; 0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064; 0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065; 0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066; 0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067; 0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068; 0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069; 004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A; 004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B; 004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C; 004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D; 004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E; 004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F; 0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070; 0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071; 0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072; 0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073; 0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074; 0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075; 0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076; 0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077; 0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078; 0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079; 005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A; 005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;; 005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;; 005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;; 005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;; 005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;; 0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;; 0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041 0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042 0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043 0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044 0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045 0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046 0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047 0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048 0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049 006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A 006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B 006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C 006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D 006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E 006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F 0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050 0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051 0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052 0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053 0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054 0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055 0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056 0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057 0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058 0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A 007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;; 007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;; 007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;; 007E;TILDE;Sm;0;ON;;;;;N;;;;; 007F;;Cc;0;BN;;;;;N;DELETE;;;; 0080;;Cc;0;BN;;;;;N;;;;; 0081;;Cc;0;BN;;;;;N;;;;; 0082;;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;; 0083;;Cc;0;BN;;;;;N;NO BREAK HERE;;;; 0084;;Cc;0;BN;;;;;N;;;;; 0085;;Cc;0;B;;;;;N;NEXT LINE (NEL);;;; 0086;;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;; 0087;;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;; 0088;;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;; 0089;;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;; 008A;;Cc;0;BN;;;;;N;LINE TABULATION SET;;;; 008B;;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;; 008C;;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;; 008D;;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;; 008E;;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;; 008F;;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;; 0090;;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;; 0091;;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;; 0092;;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;; 0093;;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;; 0094;;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;; 0095;;Cc;0;BN;;;;;N;MESSAGE WAITING;;;; 0096;;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;; 0097;;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;; 0098;;Cc;0;BN;;;;;N;START OF STRING;;;; 0099;;Cc;0;BN;;;;;N;;;;; 009A;;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;; 009B;;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;; 009C;;Cc;0;BN;;;;;N;STRING TERMINATOR;;;; 009D;;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;; 009E;;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;; 009F;;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;; 00A0;NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;NON-BREAKING SPACE;;;; 00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; 00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;; 00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;; 00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; 00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;; 00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;; 00A7;SECTION SIGN;Po;0;ON;;;;;N;;;;; 00A8;DIAERESIS;Sk;0;ON; 0020 0308;;;;N;SPACING DIAERESIS;;;; 00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;; 00AA;FEMININE ORDINAL INDICATOR;Lo;0;L; 0061;;;;N;;;;; 00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;;;; 00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;; 00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;; 00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;; 00AF;MACRON;Sk;0;ON; 0020 0304;;;;N;SPACING MACRON;;;; 00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;; 00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;; 00B2;SUPERSCRIPT TWO;No;0;EN; 0032;;2;2;N;SUPERSCRIPT DIGIT TWO;;;; 00B3;SUPERSCRIPT THREE;No;0;EN; 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;; 00B4;ACUTE ACCENT;Sk;0;ON; 0020 0301;;;;N;SPACING ACUTE;;;; 00B5;MICRO SIGN;Ll;0;L; 03BC;;;;N;;;039C;;039C 00B6;PILCROW SIGN;Po;0;ON;;;;;N;PARAGRAPH SIGN;;;; 00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;; 00B8;CEDILLA;Sk;0;ON; 0020 0327;;;;N;SPACING CEDILLA;;;; 00B9;SUPERSCRIPT ONE;No;0;EN; 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;; 00BA;MASCULINE ORDINAL INDICATOR;Lo;0;L; 006F;;;;N;;;;; 00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;;;; 00BC;VULGAR FRACTION ONE QUARTER;No;0;ON; 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;; 00BD;VULGAR FRACTION ONE HALF;No;0;ON; 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;; 00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON; 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;; 00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;; 00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0; 00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1; 00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2; 00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3; 00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4; 00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5; 00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;;;00E6; 00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7; 00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8; 00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9; 00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA; 00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB; 00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC; 00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED; 00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE; 00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF; 00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;00F0; 00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1; 00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2; 00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3; 00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4; 00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5; 00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6; 00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;; 00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8; 00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9; 00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA; 00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB; 00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC; 00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD; 00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;;;00FE; 00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;;;; 00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0 00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1 00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2 00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3 00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4 00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5 00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;;00C6;;00C6 00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7 00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8 00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9 00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA 00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB 00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC 00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD 00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE 00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF 00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;;00D0;;00D0 00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1 00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2 00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3 00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4 00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5 00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6 00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;; 00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8 00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9 00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA 00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB 00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC 00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD 00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;;00DE;;00DE 00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178 0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101; 0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100 0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103; 0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102 0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105; 0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104 0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107; 0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106 0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109; 0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108 010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B; 010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A 010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D; 010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C 010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F; 010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E 0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111; 0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110 0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113; 0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112 0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115; 0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114 0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117; 0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116 0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119; 0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118 011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B; 011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A 011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D; 011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C 011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F; 011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E 0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121; 0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120 0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123; 0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122 0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125; 0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124 0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127; 0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126 0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129; 0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128 012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B; 012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A 012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D; 012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C 012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F; 012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E 0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069; 0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049 0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L; 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133; 0133;LATIN SMALL LIGATURE IJ;Ll;0;L; 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132 0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135; 0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134 0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137; 0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136 0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;;;; 0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A; 013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139 013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C; 013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B 013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E; 013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D 013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L; 004C 00B7;;;;N;;;;0140; 0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L; 006C 00B7;;;;N;;;013F;;013F 0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142; 0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141 0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144; 0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143 0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146; 0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145 0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148; 0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147 0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L; 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;; 014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;014B; 014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;;014A;;014A 014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D; 014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C 014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F; 014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E 0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151; 0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150 0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153; 0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152 0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155; 0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154 0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157; 0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156 0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159; 0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158 015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B; 015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A 015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D; 015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C 015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;;;015F; 015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;;015E;;015E 0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161; 0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160 0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;;;0163; 0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;;0162;;0162 0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165; 0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164 0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167; 0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166 0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169; 0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168 016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B; 016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A 016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D; 016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C 016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F; 016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E 0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171; 0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170 0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173; 0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172 0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175; 0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174 0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177; 0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176 0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF; 0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A; 017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179 017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C; 017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B 017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E; 017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D 017F;LATIN SMALL LETTER LONG S;Ll;0;L; 0073;;;;N;;;0053;;0053 0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;0243;;0243 0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253; 0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183; 0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182 0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185; 0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184 0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254; 0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188; 0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187 0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;;;0256; 018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257; 018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C; 018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B 018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;; 018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD; 018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259; 0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B; 0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192; 0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191 0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260; 0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263; 0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;;01F6;;01F6 0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269; 0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268; 0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199; 0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198 019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;023D;;023D 019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;; 019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F; 019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272; 019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220 019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;;;0275; 01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1; 01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0 01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;;;01A3; 01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;;01A2;;01A2 01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5; 01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4 01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;;;0280; 01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8; 01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7 01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283; 01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;; 01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;; 01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD; 01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC 01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288; 01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0; 01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF 01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A; 01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B; 01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4; 01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3 01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6; 01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5 01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292; 01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9; 01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8 01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;; 01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;; 01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD; 01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC 01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;; 01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7 01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;; 01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;; 01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;; 01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;; 01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L; 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5 01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L; 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;01C5 01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L; 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5 01C7;LATIN CAPITAL LETTER LJ;Lu;0;L; 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8 01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L; 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;01C8 01C9;LATIN SMALL LETTER LJ;Ll;0;L; 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8 01CA;LATIN CAPITAL LETTER NJ;Lu;0;L; 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB 01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L; 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;01CB 01CC;LATIN SMALL LETTER NJ;Ll;0;L; 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB 01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE; 01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD 01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0; 01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF 01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2; 01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1 01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4; 01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3 01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6; 01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5 01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8; 01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7 01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA; 01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9 01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC; 01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB 01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E 01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF; 01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE 01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1; 01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0 01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;;;01E3; 01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;;01E2;;01E2 01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5; 01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4 01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7; 01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6 01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9; 01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8 01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB; 01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA 01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED; 01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC 01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF; 01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE 01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;; 01F1;LATIN CAPITAL LETTER DZ;Lu;0;L; 0044 005A;;;;N;;;;01F3;01F2 01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L; 0044 007A;;;;N;;;01F1;01F3;01F2 01F3;LATIN SMALL LETTER DZ;Ll;0;L; 0064 007A;;;;N;;;01F1;;01F2 01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5; 01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4 01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195; 01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF; 01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9; 01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8 01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB; 01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA 01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;;;01FD; 01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;;01FC;;01FC 01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF; 01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE 0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201; 0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200 0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203; 0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202 0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205; 0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204 0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207; 0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206 0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209; 0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208 020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B; 020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A 020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D; 020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C 020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F; 020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E 0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211; 0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210 0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213; 0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212 0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215; 0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214 0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217; 0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216 0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;;;0219; 0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;;0218;;0218 021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;;;021B; 021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;;021A;;021A 021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D; 021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C 021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F; 021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E 0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E; 0221;LATIN SMALL LETTER D WITH CURL;Ll;0;L;;;;;N;;;;; 0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223; 0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222 0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225; 0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224 0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227; 0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226 0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229; 0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228 022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B; 022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A 022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D; 022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C 022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F; 022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E 0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231; 0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230 0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233; 0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232 0234;LATIN SMALL LETTER L WITH CURL;Ll;0;L;;;;;N;;;;; 0235;LATIN SMALL LETTER N WITH CURL;Ll;0;L;;;;;N;;;;; 0236;LATIN SMALL LETTER T WITH CURL;Ll;0;L;;;;;N;;;;; 0237;LATIN SMALL LETTER DOTLESS J;Ll;0;L;;;;;N;;;;; 0238;LATIN SMALL LETTER DB DIGRAPH;Ll;0;L;;;;;N;;;;; 0239;LATIN SMALL LETTER QP DIGRAPH;Ll;0;L;;;;;N;;;;; 023A;LATIN CAPITAL LETTER A WITH STROKE;Lu;0;L;;;;;N;;;;2C65; 023B;LATIN CAPITAL LETTER C WITH STROKE;Lu;0;L;;;;;N;;;;023C; 023C;LATIN SMALL LETTER C WITH STROKE;Ll;0;L;;;;;N;;;023B;;023B 023D;LATIN CAPITAL LETTER L WITH BAR;Lu;0;L;;;;;N;;;;019A; 023E;LATIN CAPITAL LETTER T WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;2C66; 023F;LATIN SMALL LETTER S WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7E;;2C7E 0240;LATIN SMALL LETTER Z WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7F;;2C7F 0241;LATIN CAPITAL LETTER GLOTTAL STOP;Lu;0;L;;;;;N;;;;0242; 0242;LATIN SMALL LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;0241;;0241 0243;LATIN CAPITAL LETTER B WITH STROKE;Lu;0;L;;;;;N;;;;0180; 0244;LATIN CAPITAL LETTER U BAR;Lu;0;L;;;;;N;;;;0289; 0245;LATIN CAPITAL LETTER TURNED V;Lu;0;L;;;;;N;;;;028C; 0246;LATIN CAPITAL LETTER E WITH STROKE;Lu;0;L;;;;;N;;;;0247; 0247;LATIN SMALL LETTER E WITH STROKE;Ll;0;L;;;;;N;;;0246;;0246 0248;LATIN CAPITAL LETTER J WITH STROKE;Lu;0;L;;;;;N;;;;0249; 0249;LATIN SMALL LETTER J WITH STROKE;Ll;0;L;;;;;N;;;0248;;0248 024A;LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL;Lu;0;L;;;;;N;;;;024B; 024B;LATIN SMALL LETTER Q WITH HOOK TAIL;Ll;0;L;;;;;N;;;024A;;024A 024C;LATIN CAPITAL LETTER R WITH STROKE;Lu;0;L;;;;;N;;;;024D; 024D;LATIN SMALL LETTER R WITH STROKE;Ll;0;L;;;;;N;;;024C;;024C 024E;LATIN CAPITAL LETTER Y WITH STROKE;Lu;0;L;;;;;N;;;;024F; 024F;LATIN SMALL LETTER Y WITH STROKE;Ll;0;L;;;;;N;;;024E;;024E 0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;2C6F;;2C6F 0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;2C6D;;2C6D 0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;2C70;;2C70 0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181 0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186 0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;; 0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189 0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A 0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;; 0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F 025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;; 025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190 025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;A7AB;;A7AB 025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;; 025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;; 025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;; 0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193 0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;A7AC;;A7AC 0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;; 0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194 0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;; 0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;A78D;;A78D 0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;A7AA;;A7AA 0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;; 0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197 0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196 026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;A7AE;;A7AE 026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;2C62;;2C62 026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;A7AD;;A7AD 026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;; 026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;; 026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C 0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;; 0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;2C6E;;2C6E 0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D 0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;; 0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;; 0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F 0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;; 0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;; 0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;; 0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;; 027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;; 027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;; 027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;; 027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;2C64;;2C64 027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;; 027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;; 0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6 0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;; 0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;; 0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9 0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;; 0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;; 0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;; 0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;A7B1;;A7B1 0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE 0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;0244;;0244 028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1 028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2 028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;0245;;0245 028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;; 028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;; 028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;; 0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;; 0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;; 0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7 0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;; 0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; 0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; 0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;; 0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;; 0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;; 0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;; 029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;; 029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;; 029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;; 029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;A7B2;;A7B2 029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;A7B0;;A7B0 029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;; 02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;; 02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;; 02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;; 02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;; 02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;; 02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;; 02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;; 02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;; 02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;; 02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;; 02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;; 02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;; 02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; 02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; 02AE;LATIN SMALL LETTER TURNED H WITH FISHHOOK;Ll;0;L;;;;;N;;;;; 02AF;LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL;Ll;0;L;;;;;N;;;;; 02B0;MODIFIER LETTER SMALL H;Lm;0;L; 0068;;;;N;;;;; 02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L; 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;; 02B2;MODIFIER LETTER SMALL J;Lm;0;L; 006A;;;;N;;;;; 02B3;MODIFIER LETTER SMALL R;Lm;0;L; 0072;;;;N;;;;; 02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L; 0279;;;;N;;;;; 02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L; 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;; 02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L; 0281;;;;N;;;;; 02B7;MODIFIER LETTER SMALL W;Lm;0;L; 0077;;;;N;;;;; 02B8;MODIFIER LETTER SMALL Y;Lm;0;L; 0079;;;;N;;;;; 02B9;MODIFIER LETTER PRIME;Lm;0;ON;;;;;N;;;;; 02BA;MODIFIER LETTER DOUBLE PRIME;Lm;0;ON;;;;;N;;;;; 02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;; 02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;; 02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;; 02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;; 02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; 02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;; 02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;; 02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;; 02C7;CARON;Lm;0;ON;;;;;N;MODIFIER LETTER HACEK;;;; 02C8;MODIFIER LETTER VERTICAL LINE;Lm;0;ON;;;;;N;;;;; 02C9;MODIFIER LETTER MACRON;Lm;0;ON;;;;;N;;;;; 02CA;MODIFIER LETTER ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER ACUTE;;;; 02CB;MODIFIER LETTER GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER GRAVE;;;; 02CC;MODIFIER LETTER LOW VERTICAL LINE;Lm;0;ON;;;;;N;;;;; 02CD;MODIFIER LETTER LOW MACRON;Lm;0;ON;;;;;N;;;;; 02CE;MODIFIER LETTER LOW GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;; 02CF;MODIFIER LETTER LOW ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;; 02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; 02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; 02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;; 02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;; 02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;; 02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;; 02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;; 02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;; 02D8;BREVE;Sk;0;ON; 0020 0306;;;;N;SPACING BREVE;;;; 02D9;DOT ABOVE;Sk;0;ON; 0020 0307;;;;N;SPACING DOT ABOVE;;;; 02DA;RING ABOVE;Sk;0;ON; 0020 030A;;;;N;SPACING RING ABOVE;;;; 02DB;OGONEK;Sk;0;ON; 0020 0328;;;;N;SPACING OGONEK;;;; 02DC;SMALL TILDE;Sk;0;ON; 0020 0303;;;;N;SPACING TILDE;;;; 02DD;DOUBLE ACUTE ACCENT;Sk;0;ON; 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;; 02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;; 02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;; 02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L; 0263;;;;N;;;;; 02E1;MODIFIER LETTER SMALL L;Lm;0;L; 006C;;;;N;;;;; 02E2;MODIFIER LETTER SMALL S;Lm;0;L; 0073;;;;N;;;;; 02E3;MODIFIER LETTER SMALL X;Lm;0;L; 0078;;;;N;;;;; 02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L; 0295;;;;N;;;;; 02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; 02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; 02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;; 02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;; 02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;; 02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; 02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; 02EC;MODIFIER LETTER VOICING;Lm;0;ON;;;;;N;;;;; 02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;; 02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;; 02EF;MODIFIER LETTER LOW DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02F0;MODIFIER LETTER LOW UP ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02F1;MODIFIER LETTER LOW LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02F2;MODIFIER LETTER LOW RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;; 02F3;MODIFIER LETTER LOW RING;Sk;0;ON;;;;;N;;;;; 02F4;MODIFIER LETTER MIDDLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;; 02F5;MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;; 02F6;MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT;Sk;0;ON;;;;;N;;;;; 02F7;MODIFIER LETTER LOW TILDE;Sk;0;ON;;;;;N;;;;; 02F8;MODIFIER LETTER RAISED COLON;Sk;0;ON;;;;;N;;;;; 02F9;MODIFIER LETTER BEGIN HIGH TONE;Sk;0;ON;;;;;N;;;;; 02FA;MODIFIER LETTER END HIGH TONE;Sk;0;ON;;;;;N;;;;; 02FB;MODIFIER LETTER BEGIN LOW TONE;Sk;0;ON;;;;;N;;;;; 02FC;MODIFIER LETTER END LOW TONE;Sk;0;ON;;;;;N;;;;; 02FD;MODIFIER LETTER SHELF;Sk;0;ON;;;;;N;;;;; 02FE;MODIFIER LETTER OPEN SHELF;Sk;0;ON;;;;;N;;;;; 02FF;MODIFIER LETTER LOW LEFT ARROW;Sk;0;ON;;;;;N;;;;; 0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;;;; 0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;;;; 0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;; 0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;; 0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;; 0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;; 0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;;;; 0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;; 0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;;;; 0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;; 030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;; 030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;; 030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;; 030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;; 030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;; 030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;; 0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;; 0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;; 0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;; 0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;;;; 0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;;;; 0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;; 0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;; 0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;; 0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;; 0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;; 031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;; 031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;; 031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;; 031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;; 031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;; 031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;; 0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;; 0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;; 0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;; 0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;; 0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;; 0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;; 0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;; 0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;; 0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;; 0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;; 032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;; 032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;; 032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;; 032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;; 032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;; 032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;; 0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;; 0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;; 0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;; 0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;; 0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;; 0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;; 0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;; 0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;; 0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;; 0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;; 033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;; 033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;; 033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;; 033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;; 033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;; 033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;; 0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;;;; 0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;;;; 0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;; 0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;; 0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;; 0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399 0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; 0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;; 0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;; 0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;; 034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;; 034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;; 034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;; 034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; 034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;; 034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;; 0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; 0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;; 0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;; 0353;COMBINING X BELOW;Mn;220;NSM;;;;;N;;;;; 0354;COMBINING LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 0355;COMBINING RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 0356;COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;; 0358;COMBINING DOT ABOVE RIGHT;Mn;232;NSM;;;;;N;;;;; 0359;COMBINING ASTERISK BELOW;Mn;220;NSM;;;;;N;;;;; 035A;COMBINING DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;; 035B;COMBINING ZIGZAG ABOVE;Mn;230;NSM;;;;;N;;;;; 035C;COMBINING DOUBLE BREVE BELOW;Mn;233;NSM;;;;;N;;;;; 035D;COMBINING DOUBLE BREVE;Mn;234;NSM;;;;;N;;;;; 035E;COMBINING DOUBLE MACRON;Mn;234;NSM;;;;;N;;;;; 035F;COMBINING DOUBLE MACRON BELOW;Mn;233;NSM;;;;;N;;;;; 0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;; 0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;; 0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;; 0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;; 0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;; 0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;; 0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;; 0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;; 0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;; 0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;; 036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;; 036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;; 036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;; 036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;; 036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;; 036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;; 0370;GREEK CAPITAL LETTER HETA;Lu;0;L;;;;;N;;;;0371; 0371;GREEK SMALL LETTER HETA;Ll;0;L;;;;;N;;;0370;;0370 0372;GREEK CAPITAL LETTER ARCHAIC SAMPI;Lu;0;L;;;;;N;;;;0373; 0373;GREEK SMALL LETTER ARCHAIC SAMPI;Ll;0;L;;;;;N;;;0372;;0372 0374;GREEK NUMERAL SIGN;Lm;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;;;; 0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;;;; 0376;GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA;Lu;0;L;;;;;N;;;;0377; 0377;GREEK SMALL LETTER PAMPHYLIAN DIGAMMA;Ll;0;L;;;;;N;;;0376;;0376 037A;GREEK YPOGEGRAMMENI;Lm;0;L; 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;; 037B;GREEK SMALL REVERSED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FD;;03FD 037C;GREEK SMALL DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FE;;03FE 037D;GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FF;;03FF 037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;;;; 037F;GREEK CAPITAL LETTER YOT;Lu;0;L;;;;;N;;;;03F3; 0384;GREEK TONOS;Sk;0;ON; 0020 0301;;;;N;GREEK SPACING TONOS;;;; 0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;; 0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC; 0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;; 0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD; 0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE; 038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF; 038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC; 038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD; 038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE; 0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;; 0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1; 0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2; 0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3; 0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4; 0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5; 0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6; 0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7; 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; 0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9; 039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA; 039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB; 039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC; 039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD; 039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE; 039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF; 03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0; 03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1; 03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3; 03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4; 03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5; 03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6; 03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7; 03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8; 03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9; 03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA; 03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB; 03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386 03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388 03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389 03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A 03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;; 03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391 03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392 03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393 03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394 03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395 03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396 03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399 03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A 03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B 03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C 03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D 03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E 03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F 03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1 03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4 03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5 03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6 03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7 03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8 03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9 03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA 03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB 03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C 03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E 03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F 03CF;GREEK CAPITAL KAI SYMBOL;Lu;0;L;;;;;N;;;;03D7; 03D0;GREEK BETA SYMBOL;Ll;0;L; 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392 03D1;GREEK THETA SYMBOL;Ll;0;L; 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L; 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;; 03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;; 03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;; 03D5;GREEK PHI SYMBOL;Ll;0;L; 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6 03D6;GREEK PI SYMBOL;Ll;0;L; 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0 03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;03CF;;03CF 03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;;;03D9; 03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;;03D8;;03D8 03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB; 03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA 03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD; 03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC 03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF; 03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE 03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1; 03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0 03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3; 03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2 03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5; 03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4 03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7; 03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6 03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9; 03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8 03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB; 03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA 03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED; 03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC 03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF; 03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE 03F0;GREEK KAPPA SYMBOL;Ll;0;L; 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A 03F1;GREEK RHO SYMBOL;Ll;0;L; 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1 03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L; 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03F9;;03F9 03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;037F;;037F 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L; 0398;;;;N;;;;03B8; 03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L; 03B5;;;;N;;;0395;;0395 03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;; 03F7;GREEK CAPITAL LETTER SHO;Lu;0;L;;;;;N;;;;03F8; 03F8;GREEK SMALL LETTER SHO;Ll;0;L;;;;;N;;;03F7;;03F7 03F9;GREEK CAPITAL LUNATE SIGMA SYMBOL;Lu;0;L; 03A3;;;;N;;;;03F2; 03FA;GREEK CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;03FB; 03FB;GREEK SMALL LETTER SAN;Ll;0;L;;;;;N;;;03FA;;03FA 03FC;GREEK RHO WITH STROKE SYMBOL;Ll;0;L;;;;;N;;;;; 03FD;GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037B; 03FE;GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037C; 03FF;GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037D; 0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450; 0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451; 0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;;;0452; 0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453; 0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454; 0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455; 0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456; 0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;;;0457; 0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458; 0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459; 040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A; 040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;;;045B; 040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C; 040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D; 040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;;;045E; 040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F; 0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430; 0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431; 0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432; 0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433; 0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434; 0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435; 0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436; 0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437; 0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438; 0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439; 041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A; 041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B; 041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C; 041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D; 041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E; 041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F; 0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440; 0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441; 0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442; 0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443; 0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444; 0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445; 0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446; 0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447; 0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448; 0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449; 042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A; 042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B; 042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C; 042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D; 042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E; 042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F; 0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410 0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411 0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412 0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413 0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414 0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415 0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416 0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417 0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418 0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419 043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A 043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B 043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C 043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D 043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E 043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F 0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420 0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421 0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422 0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423 0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424 0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425 0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426 0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427 0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428 0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429 044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A 044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B 044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C 044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D 044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E 044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F 0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400 0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401 0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;;0402;;0402 0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403 0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404 0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405 0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406 0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;;0407;;0407 0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408 0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409 045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A 045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;;040B;;040B 045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C 045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D 045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;;040E;;040E 045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F 0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461; 0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460 0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463; 0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462 0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465; 0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464 0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467; 0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466 0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469; 0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468 046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B; 046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A 046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D; 046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C 046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F; 046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E 0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471; 0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470 0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473; 0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472 0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475; 0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474 0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477; 0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476 0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479; 0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478 047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B; 047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A 047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D; 047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C 047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F; 047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E 0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481; 0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480 0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;; 0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;; 0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;; 0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;; 0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;; 0487;COMBINING CYRILLIC POKRYTIE;Mn;230;NSM;;;;;N;;;;; 0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;; 0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; 048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B; 048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A 048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D; 048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C 048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F; 048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E 0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491; 0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490 0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493; 0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492 0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495; 0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494 0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497; 0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496 0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499; 0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498 049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B; 049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A 049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D; 049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C 049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F; 049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E 04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1; 04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0 04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3; 04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2 04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5; 04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4 04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;;;04A7; 04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;;04A6;;04A6 04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9; 04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8 04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB; 04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA 04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD; 04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC 04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF; 04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE 04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1; 04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0 04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3; 04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2 04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;;;04B5; 04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;;04B4;;04B4 04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7; 04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6 04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9; 04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8 04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB; 04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA 04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD; 04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC 04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF; 04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE 04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;04CF; 04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2; 04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1 04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4; 04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3 04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6; 04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5 04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8; 04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7 04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA; 04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9 04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC; 04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB 04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE; 04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD 04CF;CYRILLIC SMALL LETTER PALOCHKA;Ll;0;L;;;;;N;;;04C0;;04C0 04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1; 04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0 04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3; 04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2 04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5; 04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4 04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7; 04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6 04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9; 04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8 04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB; 04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA 04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD; 04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC 04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF; 04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE 04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1; 04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0 04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3; 04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2 04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5; 04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4 04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7; 04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6 04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9; 04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8 04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB; 04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA 04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED; 04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC 04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF; 04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE 04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1; 04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0 04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3; 04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2 04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5; 04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4 04F6;CYRILLIC CAPITAL LETTER GHE WITH DESCENDER;Lu;0;L;;;;;N;;;;04F7; 04F7;CYRILLIC SMALL LETTER GHE WITH DESCENDER;Ll;0;L;;;;;N;;;04F6;;04F6 04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9; 04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8 04FA;CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK;Lu;0;L;;;;;N;;;;04FB; 04FB;CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK;Ll;0;L;;;;;N;;;04FA;;04FA 04FC;CYRILLIC CAPITAL LETTER HA WITH HOOK;Lu;0;L;;;;;N;;;;04FD; 04FD;CYRILLIC SMALL LETTER HA WITH HOOK;Ll;0;L;;;;;N;;;04FC;;04FC 04FE;CYRILLIC CAPITAL LETTER HA WITH STROKE;Lu;0;L;;;;;N;;;;04FF; 04FF;CYRILLIC SMALL LETTER HA WITH STROKE;Ll;0;L;;;;;N;;;04FE;;04FE 0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501; 0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500 0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503; 0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502 0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505; 0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504 0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507; 0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506 0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509; 0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508 050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B; 050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A 050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D; 050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C 050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F; 050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E 0510;CYRILLIC CAPITAL LETTER REVERSED ZE;Lu;0;L;;;;;N;;;;0511; 0511;CYRILLIC SMALL LETTER REVERSED ZE;Ll;0;L;;;;;N;;;0510;;0510 0512;CYRILLIC CAPITAL LETTER EL WITH HOOK;Lu;0;L;;;;;N;;;;0513; 0513;CYRILLIC SMALL LETTER EL WITH HOOK;Ll;0;L;;;;;N;;;0512;;0512 0514;CYRILLIC CAPITAL LETTER LHA;Lu;0;L;;;;;N;;;;0515; 0515;CYRILLIC SMALL LETTER LHA;Ll;0;L;;;;;N;;;0514;;0514 0516;CYRILLIC CAPITAL LETTER RHA;Lu;0;L;;;;;N;;;;0517; 0517;CYRILLIC SMALL LETTER RHA;Ll;0;L;;;;;N;;;0516;;0516 0518;CYRILLIC CAPITAL LETTER YAE;Lu;0;L;;;;;N;;;;0519; 0519;CYRILLIC SMALL LETTER YAE;Ll;0;L;;;;;N;;;0518;;0518 051A;CYRILLIC CAPITAL LETTER QA;Lu;0;L;;;;;N;;;;051B; 051B;CYRILLIC SMALL LETTER QA;Ll;0;L;;;;;N;;;051A;;051A 051C;CYRILLIC CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;051D; 051D;CYRILLIC SMALL LETTER WE;Ll;0;L;;;;;N;;;051C;;051C 051E;CYRILLIC CAPITAL LETTER ALEUT KA;Lu;0;L;;;;;N;;;;051F; 051F;CYRILLIC SMALL LETTER ALEUT KA;Ll;0;L;;;;;N;;;051E;;051E 0520;CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0521; 0521;CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0520;;0520 0522;CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0523; 0523;CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0522;;0522 0524;CYRILLIC CAPITAL LETTER PE WITH DESCENDER;Lu;0;L;;;;;N;;;;0525; 0525;CYRILLIC SMALL LETTER PE WITH DESCENDER;Ll;0;L;;;;;N;;;0524;;0524 0526;CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER;Lu;0;L;;;;;N;;;;0527; 0527;CYRILLIC SMALL LETTER SHHA WITH DESCENDER;Ll;0;L;;;;;N;;;0526;;0526 0528;CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK;Lu;0;L;;;;;N;;;;0529; 0529;CYRILLIC SMALL LETTER EN WITH LEFT HOOK;Ll;0;L;;;;;N;;;0528;;0528 052A;CYRILLIC CAPITAL LETTER DZZHE;Lu;0;L;;;;;N;;;;052B; 052B;CYRILLIC SMALL LETTER DZZHE;Ll;0;L;;;;;N;;;052A;;052A 052C;CYRILLIC CAPITAL LETTER DCHE;Lu;0;L;;;;;N;;;;052D; 052D;CYRILLIC SMALL LETTER DCHE;Ll;0;L;;;;;N;;;052C;;052C 052E;CYRILLIC CAPITAL LETTER EL WITH DESCENDER;Lu;0;L;;;;;N;;;;052F; 052F;CYRILLIC SMALL LETTER EL WITH DESCENDER;Ll;0;L;;;;;N;;;052E;;052E 0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561; 0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562; 0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563; 0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564; 0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565; 0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566; 0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567; 0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568; 0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569; 053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A; 053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B; 053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C; 053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D; 053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E; 053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F; 0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570; 0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571; 0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572; 0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573; 0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574; 0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575; 0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576; 0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577; 0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578; 0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579; 054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A; 054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B; 054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C; 054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D; 054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E; 054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F; 0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580; 0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581; 0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582; 0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583; 0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584; 0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585; 0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586; 0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; 055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;; 055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;; 055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;; 055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;; 055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;; 055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;; 0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531 0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532 0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533 0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534 0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535 0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536 0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537 0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538 0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539 056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A 056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B 056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C 056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D 056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E 056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F 0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540 0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541 0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542 0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543 0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544 0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545 0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546 0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547 0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548 0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549 057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A 057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B 057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C 057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D 057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E 057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F 0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550 0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551 0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552 0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553 0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554 0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555 0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556 0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L; 0565 0582;;;;N;;;;; 0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;; 058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;; 058D;RIGHT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;; 058E;LEFT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;; 058F;ARMENIAN DRAM SIGN;Sc;0;ET;;;;;N;;;;; 0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;; 0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;; 0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;; 0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;; 0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;; 0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;;;; 0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;; 0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;;;; 0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;; 059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;; 059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;; 059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;; 059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;; 059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;; 059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;; 05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;; 05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;; 05A2;HEBREW ACCENT ATNAH HAFUKH;Mn;220;NSM;;;;;N;;;;; 05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;; 05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;; 05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;;;; 05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;; 05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;; 05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;;;; 05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;; 05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;;;; 05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;; 05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;; 05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;; 05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;; 05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;; 05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;; 05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;; 05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;; 05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;; 05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;; 05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;; 05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;; 05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;; 05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;; 05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;; 05BA;HEBREW POINT HOLAM HASER FOR VAV;Mn;19;NSM;;;;;N;;;;; 05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;; 05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;;;; 05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;;;; 05BE;HEBREW PUNCTUATION MAQAF;Pd;0;R;;;;;N;;;;; 05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;; 05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;;;; 05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;; 05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;; 05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;;;; 05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;; 05C5;HEBREW MARK LOWER DOT;Mn;220;NSM;;;;;N;;;;; 05C6;HEBREW PUNCTUATION NUN HAFUKHA;Po;0;R;;;;;N;;;;; 05C7;HEBREW POINT QAMATS QATAN;Mn;18;NSM;;;;;N;;;;; 05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;; 05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;; 05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;; 05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;; 05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;; 05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;; 05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;; 05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;; 05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;; 05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;; 05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;; 05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;; 05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; 05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;; 05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; 05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;; 05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;; 05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;; 05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;; 05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;; 05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;; 05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;; 05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;; 05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;; 05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;; 05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;; 05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;; 05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;; 05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;; 05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;; 0600;ARABIC NUMBER SIGN;Cf;0;AN;;;;;N;;;;; 0601;ARABIC SIGN SANAH;Cf;0;AN;;;;;N;;;;; 0602;ARABIC FOOTNOTE MARKER;Cf;0;AN;;;;;N;;;;; 0603;ARABIC SIGN SAFHA;Cf;0;AN;;;;;N;;;;; 0604;ARABIC SIGN SAMVAT;Cf;0;AN;;;;;N;;;;; 0605;ARABIC NUMBER MARK ABOVE;Cf;0;AN;;;;;N;;;;; 0606;ARABIC-INDIC CUBE ROOT;Sm;0;ON;;;;;N;;;;; 0607;ARABIC-INDIC FOURTH ROOT;Sm;0;ON;;;;;N;;;;; 0608;ARABIC RAY;Sm;0;AL;;;;;N;;;;; 0609;ARABIC-INDIC PER MILLE SIGN;Po;0;ET;;;;;N;;;;; 060A;ARABIC-INDIC PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;; 060B;AFGHANI SIGN;Sc;0;AL;;;;;N;;;;; 060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;; 060D;ARABIC DATE SEPARATOR;Po;0;AL;;;;;N;;;;; 060E;ARABIC POETIC VERSE SIGN;So;0;ON;;;;;N;;;;; 060F;ARABIC SIGN MISRA;So;0;ON;;;;;N;;;;; 0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;; 0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;; 0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;; 0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;; 0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;; 0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;; 0616;ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH;Mn;230;NSM;;;;;N;;;;; 0617;ARABIC SMALL HIGH ZAIN;Mn;230;NSM;;;;;N;;;;; 0618;ARABIC SMALL FATHA;Mn;30;NSM;;;;;N;;;;; 0619;ARABIC SMALL DAMMA;Mn;31;NSM;;;;;N;;;;; 061A;ARABIC SMALL KASRA;Mn;32;NSM;;;;;N;;;;; 061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;; 061C;ARABIC LETTER MARK;Cf;0;AL;;;;;N;;;;; 061E;ARABIC TRIPLE DOT PUNCTUATION MARK;Po;0;AL;;;;;N;;;;; 061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;; 0620;ARABIC LETTER KASHMIRI YEH;Lo;0;AL;;;;;N;;;;; 0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;; 0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;; 0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;; 0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;; 0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;; 0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;; 0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;; 0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;; 0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;; 062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;; 062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;; 062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;; 062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;; 062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;; 062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;; 0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;; 0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;; 0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; 0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;; 0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;; 0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;; 0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;; 0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;; 0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;; 0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;; 063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;; 063B;ARABIC LETTER KEHEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 063C;ARABIC LETTER KEHEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 063D;ARABIC LETTER FARSI YEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; 063E;ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 063F;ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;; 0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;; 0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;; 0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;; 0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;; 0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;; 0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;; 0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;; 0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;; 0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;; 064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;; 064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;; 064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;; 064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;; 064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;; 064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;; 0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;; 0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;; 0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;; 0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;; 0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;; 0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;; 0656;ARABIC SUBSCRIPT ALEF;Mn;220;NSM;;;;;N;;;;; 0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;; 0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;; 0659;ARABIC ZWARAKAY;Mn;230;NSM;;;;;N;;;;; 065A;ARABIC VOWEL SIGN SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;; 065B;ARABIC VOWEL SIGN INVERTED SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;; 065C;ARABIC VOWEL SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;; 065D;ARABIC REVERSED DAMMA;Mn;230;NSM;;;;;N;;;;; 065E;ARABIC FATHA WITH TWO DOTS;Mn;230;NSM;;;;;N;;;;; 065F;ARABIC WAVY HAMZA BELOW;Mn;220;NSM;;;;;N;;;;; 0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;; 0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;; 0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;; 0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;; 0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;; 0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;; 0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;; 0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;; 0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;; 0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;; 066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;; 066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;; 066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;; 066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;; 066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;; 066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;; 0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;; 0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;; 0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;; 0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;; 0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;; 0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL; 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;; 0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL; 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;; 0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL; 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;; 0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL; 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;; 0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;; 067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;; 067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;; 067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;; 067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;; 067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;; 067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;; 0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;; 0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;; 0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;; 0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;; 0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;; 0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;; 0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;; 0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;; 0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;; 0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;; 068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;; 068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;; 068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;; 068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;; 068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;; 0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;; 0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;; 0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;; 0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;; 0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;; 0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;; 0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;; 0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;; 0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;; 069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; 069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; 069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;; 06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;; 06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;; 06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;; 06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;; 06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;; 06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;; 06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;; 06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;; 06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;; 06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;; 06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;; 06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;;;; 06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;; 06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;; 06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; 06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;; 06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;; 06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;; 06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;; 06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;; 06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;; 06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;; 06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;; 06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;; 06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;; 06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;; 06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;; 06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;; 06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;; 06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;; 06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;; 06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;; 06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;; 06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;; 06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;; 06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;;;; 06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;; 06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;; 06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;; 06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;; 06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;; 06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; 06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; 06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;; 06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;; 06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;; 06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;; 06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;; 06DD;ARABIC END OF AYAH;Cf;0;AN;;;;;N;;;;; 06DE;ARABIC START OF RUB EL HIZB;So;0;ON;;;;;N;;;;; 06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;; 06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;; 06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;; 06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;; 06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;; 06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;; 06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;; 06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;; 06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;; 06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;; 06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;; 06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;; 06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;; 06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;; 06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;; 06EE;ARABIC LETTER DAL WITH INVERTED V;Lo;0;AL;;;;;N;;;;; 06EF;ARABIC LETTER REH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; 06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;; 06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;; 06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;; 06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;; 06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;; 06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;; 06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;; 06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;; 06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;; 06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;; 06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;; 06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;; 06FF;ARABIC LETTER HEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; 0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;; 0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;; 0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;; 0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;; 0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;; 0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;; 0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; 0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; 0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; 0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; 070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;; 070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;; 070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;; 070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;; 070F;SYRIAC ABBREVIATION MARK;Cf;0;AL;;;;;N;;;;; 0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;; 0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;; 0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;; 0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;; 0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;; 0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;; 0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;; 0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;; 0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;; 0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; 071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;; 071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;; 071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;; 071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;; 071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;; 071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;; 0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;; 0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;; 0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;; 0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;; 0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;; 0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;; 0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;; 0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;; 0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;; 0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;; 072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;; 072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;; 072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;; 072D;SYRIAC LETTER PERSIAN BHETH;Lo;0;AL;;;;;N;;;;; 072E;SYRIAC LETTER PERSIAN GHAMAL;Lo;0;AL;;;;;N;;;;; 072F;SYRIAC LETTER PERSIAN DHALATH;Lo;0;AL;;;;;N;;;;; 0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;; 0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;; 0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;; 0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;; 0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;; 0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;; 0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;; 0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;; 0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;; 0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;; 073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;; 073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;; 073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;; 073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;; 073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;; 073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;; 0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;; 0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;; 0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;; 0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; 0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;; 0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; 0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;; 0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;; 0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;; 0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;; 074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;; 074D;SYRIAC LETTER SOGDIAN ZHAIN;Lo;0;AL;;;;;N;;;;; 074E;SYRIAC LETTER SOGDIAN KHAPH;Lo;0;AL;;;;;N;;;;; 074F;SYRIAC LETTER SOGDIAN FE;Lo;0;AL;;;;;N;;;;; 0750;ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW;Lo;0;AL;;;;;N;;;;; 0751;ARABIC LETTER BEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0752;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; 0753;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW AND TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0754;ARABIC LETTER BEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; 0755;ARABIC LETTER BEH WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;; 0756;ARABIC LETTER BEH WITH SMALL V;Lo;0;AL;;;;;N;;;;; 0757;ARABIC LETTER HAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0758;ARABIC LETTER HAH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; 0759;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;; 075A;ARABIC LETTER DAL WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;; 075B;ARABIC LETTER REH WITH STROKE;Lo;0;AL;;;;;N;;;;; 075C;ARABIC LETTER SEEN WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 075D;ARABIC LETTER AIN WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 075E;ARABIC LETTER AIN WITH THREE DOTS POINTING DOWNWARDS ABOVE;Lo;0;AL;;;;;N;;;;; 075F;ARABIC LETTER AIN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; 0760;ARABIC LETTER FEH WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; 0761;ARABIC LETTER FEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; 0762;ARABIC LETTER KEHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 0763;ARABIC LETTER KEHEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0764;ARABIC LETTER KEHEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; 0765;ARABIC LETTER MEEM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; 0766;ARABIC LETTER MEEM WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 0767;ARABIC LETTER NOON WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; 0768;ARABIC LETTER NOON WITH SMALL TAH;Lo;0;AL;;;;;N;;;;; 0769;ARABIC LETTER NOON WITH SMALL V;Lo;0;AL;;;;;N;;;;; 076A;ARABIC LETTER LAM WITH BAR;Lo;0;AL;;;;;N;;;;; 076B;ARABIC LETTER REH WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; 076C;ARABIC LETTER REH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; 076D;ARABIC LETTER SEEN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; 076E;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH BELOW;Lo;0;AL;;;;;N;;;;; 076F;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; 0770;ARABIC LETTER SEEN WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; 0771;ARABIC LETTER REH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; 0772;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH ABOVE;Lo;0;AL;;;;;N;;;;; 0773;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; 0774;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; 0775;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; 0776;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; 0777;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;; 0778;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; 0779;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; 077A;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; 077B;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; 077C;ARABIC LETTER HAH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;; 077D;ARABIC LETTER SEEN WITH EXTENDED ARABIC-INDIC DIGIT FOUR ABOVE;Lo;0;AL;;;;;N;;;;; 077E;ARABIC LETTER SEEN WITH INVERTED V;Lo;0;AL;;;;;N;;;;; 077F;ARABIC LETTER KAF WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;; 0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;; 0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;; 0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;; 0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;; 0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;; 0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;; 0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;; 0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;; 0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;; 078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;; 078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;; 078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;; 078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;; 078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;; 078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;; 0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;; 0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;; 0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;; 0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;; 0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;; 0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;; 0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;; 0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;; 0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;; 0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;; 079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;; 079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;; 079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;; 079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;; 079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;; 079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;; 07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;; 07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;; 07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;; 07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;; 07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;; 07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;; 07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;; 07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;; 07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;; 07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;; 07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;; 07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;; 07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;; 07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;; 07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;; 07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;; 07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;; 07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;; 07C0;NKO DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;; 07C1;NKO DIGIT ONE;Nd;0;R;;1;1;1;N;;;;; 07C2;NKO DIGIT TWO;Nd;0;R;;2;2;2;N;;;;; 07C3;NKO DIGIT THREE;Nd;0;R;;3;3;3;N;;;;; 07C4;NKO DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;; 07C5;NKO DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;; 07C6;NKO DIGIT SIX;Nd;0;R;;6;6;6;N;;;;; 07C7;NKO DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;; 07C8;NKO DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;; 07C9;NKO DIGIT NINE;Nd;0;R;;9;9;9;N;;;;; 07CA;NKO LETTER A;Lo;0;R;;;;;N;;;;; 07CB;NKO LETTER EE;Lo;0;R;;;;;N;;;;; 07CC;NKO LETTER I;Lo;0;R;;;;;N;;;;; 07CD;NKO LETTER E;Lo;0;R;;;;;N;;;;; 07CE;NKO LETTER U;Lo;0;R;;;;;N;;;;; 07CF;NKO LETTER OO;Lo;0;R;;;;;N;;;;; 07D0;NKO LETTER O;Lo;0;R;;;;;N;;;;; 07D1;NKO LETTER DAGBASINNA;Lo;0;R;;;;;N;;;;; 07D2;NKO LETTER N;Lo;0;R;;;;;N;;;;; 07D3;NKO LETTER BA;Lo;0;R;;;;;N;;;;; 07D4;NKO LETTER PA;Lo;0;R;;;;;N;;;;; 07D5;NKO LETTER TA;Lo;0;R;;;;;N;;;;; 07D6;NKO LETTER JA;Lo;0;R;;;;;N;;;;; 07D7;NKO LETTER CHA;Lo;0;R;;;;;N;;;;; 07D8;NKO LETTER DA;Lo;0;R;;;;;N;;;;; 07D9;NKO LETTER RA;Lo;0;R;;;;;N;;;;; 07DA;NKO LETTER RRA;Lo;0;R;;;;;N;;;;; 07DB;NKO LETTER SA;Lo;0;R;;;;;N;;;;; 07DC;NKO LETTER GBA;Lo;0;R;;;;;N;;;;; 07DD;NKO LETTER FA;Lo;0;R;;;;;N;;;;; 07DE;NKO LETTER KA;Lo;0;R;;;;;N;;;;; 07DF;NKO LETTER LA;Lo;0;R;;;;;N;;;;; 07E0;NKO LETTER NA WOLOSO;Lo;0;R;;;;;N;;;;; 07E1;NKO LETTER MA;Lo;0;R;;;;;N;;;;; 07E2;NKO LETTER NYA;Lo;0;R;;;;;N;;;;; 07E3;NKO LETTER NA;Lo;0;R;;;;;N;;;;; 07E4;NKO LETTER HA;Lo;0;R;;;;;N;;;;; 07E5;NKO LETTER WA;Lo;0;R;;;;;N;;;;; 07E6;NKO LETTER YA;Lo;0;R;;;;;N;;;;; 07E7;NKO LETTER NYA WOLOSO;Lo;0;R;;;;;N;;;;; 07E8;NKO LETTER JONA JA;Lo;0;R;;;;;N;;;;; 07E9;NKO LETTER JONA CHA;Lo;0;R;;;;;N;;;;; 07EA;NKO LETTER JONA RA;Lo;0;R;;;;;N;;;;; 07EB;NKO COMBINING SHORT HIGH TONE;Mn;230;NSM;;;;;N;;;;; 07EC;NKO COMBINING SHORT LOW TONE;Mn;230;NSM;;;;;N;;;;; 07ED;NKO COMBINING SHORT RISING TONE;Mn;230;NSM;;;;;N;;;;; 07EE;NKO COMBINING LONG DESCENDING TONE;Mn;230;NSM;;;;;N;;;;; 07EF;NKO COMBINING LONG HIGH TONE;Mn;230;NSM;;;;;N;;;;; 07F0;NKO COMBINING LONG LOW TONE;Mn;230;NSM;;;;;N;;;;; 07F1;NKO COMBINING LONG RISING TONE;Mn;230;NSM;;;;;N;;;;; 07F2;NKO COMBINING NASALIZATION MARK;Mn;220;NSM;;;;;N;;;;; 07F3;NKO COMBINING DOUBLE DOT ABOVE;Mn;230;NSM;;;;;N;;;;; 07F4;NKO HIGH TONE APOSTROPHE;Lm;0;R;;;;;N;;;;; 07F5;NKO LOW TONE APOSTROPHE;Lm;0;R;;;;;N;;;;; 07F6;NKO SYMBOL OO DENNEN;So;0;ON;;;;;N;;;;; 07F7;NKO SYMBOL GBAKURUNEN;Po;0;ON;;;;;N;;;;; 07F8;NKO COMMA;Po;0;ON;;;;;N;;;;; 07F9;NKO EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; 07FA;NKO LAJANYALAN;Lm;0;R;;;;;N;;;;; 0800;SAMARITAN LETTER ALAF;Lo;0;R;;;;;N;;;;; 0801;SAMARITAN LETTER BIT;Lo;0;R;;;;;N;;;;; 0802;SAMARITAN LETTER GAMAN;Lo;0;R;;;;;N;;;;; 0803;SAMARITAN LETTER DALAT;Lo;0;R;;;;;N;;;;; 0804;SAMARITAN LETTER IY;Lo;0;R;;;;;N;;;;; 0805;SAMARITAN LETTER BAA;Lo;0;R;;;;;N;;;;; 0806;SAMARITAN LETTER ZEN;Lo;0;R;;;;;N;;;;; 0807;SAMARITAN LETTER IT;Lo;0;R;;;;;N;;;;; 0808;SAMARITAN LETTER TIT;Lo;0;R;;;;;N;;;;; 0809;SAMARITAN LETTER YUT;Lo;0;R;;;;;N;;;;; 080A;SAMARITAN LETTER KAAF;Lo;0;R;;;;;N;;;;; 080B;SAMARITAN LETTER LABAT;Lo;0;R;;;;;N;;;;; 080C;SAMARITAN LETTER MIM;Lo;0;R;;;;;N;;;;; 080D;SAMARITAN LETTER NUN;Lo;0;R;;;;;N;;;;; 080E;SAMARITAN LETTER SINGAAT;Lo;0;R;;;;;N;;;;; 080F;SAMARITAN LETTER IN;Lo;0;R;;;;;N;;;;; 0810;SAMARITAN LETTER FI;Lo;0;R;;;;;N;;;;; 0811;SAMARITAN LETTER TSAADIY;Lo;0;R;;;;;N;;;;; 0812;SAMARITAN LETTER QUF;Lo;0;R;;;;;N;;;;; 0813;SAMARITAN LETTER RISH;Lo;0;R;;;;;N;;;;; 0814;SAMARITAN LETTER SHAN;Lo;0;R;;;;;N;;;;; 0815;SAMARITAN LETTER TAAF;Lo;0;R;;;;;N;;;;; 0816;SAMARITAN MARK IN;Mn;230;NSM;;;;;N;;;;; 0817;SAMARITAN MARK IN-ALAF;Mn;230;NSM;;;;;N;;;;; 0818;SAMARITAN MARK OCCLUSION;Mn;230;NSM;;;;;N;;;;; 0819;SAMARITAN MARK DAGESH;Mn;230;NSM;;;;;N;;;;; 081A;SAMARITAN MODIFIER LETTER EPENTHETIC YUT;Lm;0;R;;;;;N;;;;; 081B;SAMARITAN MARK EPENTHETIC YUT;Mn;230;NSM;;;;;N;;;;; 081C;SAMARITAN VOWEL SIGN LONG E;Mn;230;NSM;;;;;N;;;;; 081D;SAMARITAN VOWEL SIGN E;Mn;230;NSM;;;;;N;;;;; 081E;SAMARITAN VOWEL SIGN OVERLONG AA;Mn;230;NSM;;;;;N;;;;; 081F;SAMARITAN VOWEL SIGN LONG AA;Mn;230;NSM;;;;;N;;;;; 0820;SAMARITAN VOWEL SIGN AA;Mn;230;NSM;;;;;N;;;;; 0821;SAMARITAN VOWEL SIGN OVERLONG A;Mn;230;NSM;;;;;N;;;;; 0822;SAMARITAN VOWEL SIGN LONG A;Mn;230;NSM;;;;;N;;;;; 0823;SAMARITAN VOWEL SIGN A;Mn;230;NSM;;;;;N;;;;; 0824;SAMARITAN MODIFIER LETTER SHORT A;Lm;0;R;;;;;N;;;;; 0825;SAMARITAN VOWEL SIGN SHORT A;Mn;230;NSM;;;;;N;;;;; 0826;SAMARITAN VOWEL SIGN LONG U;Mn;230;NSM;;;;;N;;;;; 0827;SAMARITAN VOWEL SIGN U;Mn;230;NSM;;;;;N;;;;; 0828;SAMARITAN MODIFIER LETTER I;Lm;0;R;;;;;N;;;;; 0829;SAMARITAN VOWEL SIGN LONG I;Mn;230;NSM;;;;;N;;;;; 082A;SAMARITAN VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;; 082B;SAMARITAN VOWEL SIGN O;Mn;230;NSM;;;;;N;;;;; 082C;SAMARITAN VOWEL SIGN SUKUN;Mn;230;NSM;;;;;N;;;;; 082D;SAMARITAN MARK NEQUDAA;Mn;230;NSM;;;;;N;;;;; 0830;SAMARITAN PUNCTUATION NEQUDAA;Po;0;R;;;;;N;;;;; 0831;SAMARITAN PUNCTUATION AFSAAQ;Po;0;R;;;;;N;;;;; 0832;SAMARITAN PUNCTUATION ANGED;Po;0;R;;;;;N;;;;; 0833;SAMARITAN PUNCTUATION BAU;Po;0;R;;;;;N;;;;; 0834;SAMARITAN PUNCTUATION ATMAAU;Po;0;R;;;;;N;;;;; 0835;SAMARITAN PUNCTUATION SHIYYAALAA;Po;0;R;;;;;N;;;;; 0836;SAMARITAN ABBREVIATION MARK;Po;0;R;;;;;N;;;;; 0837;SAMARITAN PUNCTUATION MELODIC QITSA;Po;0;R;;;;;N;;;;; 0838;SAMARITAN PUNCTUATION ZIQAA;Po;0;R;;;;;N;;;;; 0839;SAMARITAN PUNCTUATION QITSA;Po;0;R;;;;;N;;;;; 083A;SAMARITAN PUNCTUATION ZAEF;Po;0;R;;;;;N;;;;; 083B;SAMARITAN PUNCTUATION TURU;Po;0;R;;;;;N;;;;; 083C;SAMARITAN PUNCTUATION ARKAANU;Po;0;R;;;;;N;;;;; 083D;SAMARITAN PUNCTUATION SOF MASHFAAT;Po;0;R;;;;;N;;;;; 083E;SAMARITAN PUNCTUATION ANNAAU;Po;0;R;;;;;N;;;;; 0840;MANDAIC LETTER HALQA;Lo;0;R;;;;;N;;;;; 0841;MANDAIC LETTER AB;Lo;0;R;;;;;N;;;;; 0842;MANDAIC LETTER AG;Lo;0;R;;;;;N;;;;; 0843;MANDAIC LETTER AD;Lo;0;R;;;;;N;;;;; 0844;MANDAIC LETTER AH;Lo;0;R;;;;;N;;;;; 0845;MANDAIC LETTER USHENNA;Lo;0;R;;;;;N;;;;; 0846;MANDAIC LETTER AZ;Lo;0;R;;;;;N;;;;; 0847;MANDAIC LETTER IT;Lo;0;R;;;;;N;;;;; 0848;MANDAIC LETTER ATT;Lo;0;R;;;;;N;;;;; 0849;MANDAIC LETTER AKSA;Lo;0;R;;;;;N;;;;; 084A;MANDAIC LETTER AK;Lo;0;R;;;;;N;;;;; 084B;MANDAIC LETTER AL;Lo;0;R;;;;;N;;;;; 084C;MANDAIC LETTER AM;Lo;0;R;;;;;N;;;;; 084D;MANDAIC LETTER AN;Lo;0;R;;;;;N;;;;; 084E;MANDAIC LETTER AS;Lo;0;R;;;;;N;;;;; 084F;MANDAIC LETTER IN;Lo;0;R;;;;;N;;;;; 0850;MANDAIC LETTER AP;Lo;0;R;;;;;N;;;;; 0851;MANDAIC LETTER ASZ;Lo;0;R;;;;;N;;;;; 0852;MANDAIC LETTER AQ;Lo;0;R;;;;;N;;;;; 0853;MANDAIC LETTER AR;Lo;0;R;;;;;N;;;;; 0854;MANDAIC LETTER ASH;Lo;0;R;;;;;N;;;;; 0855;MANDAIC LETTER AT;Lo;0;R;;;;;N;;;;; 0856;MANDAIC LETTER DUSHENNA;Lo;0;R;;;;;N;;;;; 0857;MANDAIC LETTER KAD;Lo;0;R;;;;;N;;;;; 0858;MANDAIC LETTER AIN;Lo;0;R;;;;;N;;;;; 0859;MANDAIC AFFRICATION MARK;Mn;220;NSM;;;;;N;;;;; 085A;MANDAIC VOCALIZATION MARK;Mn;220;NSM;;;;;N;;;;; 085B;MANDAIC GEMINATION MARK;Mn;220;NSM;;;;;N;;;;; 085E;MANDAIC PUNCTUATION;Po;0;R;;;;;N;;;;; 08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;; 08A1;ARABIC LETTER BEH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; 08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 08A3;ARABIC LETTER TAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 08A4;ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 08A5;ARABIC LETTER QAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 08A6;ARABIC LETTER LAM WITH DOUBLE BAR;Lo;0;AL;;;;;N;;;;; 08A7;ARABIC LETTER MEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; 08A8;ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; 08A9;ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; 08AA;ARABIC LETTER REH WITH LOOP;Lo;0;AL;;;;;N;;;;; 08AB;ARABIC LETTER WAW WITH DOT WITHIN;Lo;0;AL;;;;;N;;;;; 08AC;ARABIC LETTER ROHINGYA YEH;Lo;0;AL;;;;;N;;;;; 08AD;ARABIC LETTER LOW ALEF;Lo;0;AL;;;;;N;;;;; 08AE;ARABIC LETTER DAL WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 08AF;ARABIC LETTER SAD WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 08B0;ARABIC LETTER GAF WITH INVERTED STROKE;Lo;0;AL;;;;;N;;;;; 08B1;ARABIC LETTER STRAIGHT WAW;Lo;0;AL;;;;;N;;;;; 08B2;ARABIC LETTER ZAIN WITH INVERTED V ABOVE;Lo;0;AL;;;;;N;;;;; 08B3;ARABIC LETTER AIN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 08B4;ARABIC LETTER KAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; 08B6;ARABIC LETTER BEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;; 08B7;ARABIC LETTER PEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;; 08B8;ARABIC LETTER TEH WITH SMALL TEH ABOVE;Lo;0;AL;;;;;N;;;;; 08B9;ARABIC LETTER REH WITH SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;; 08BA;ARABIC LETTER YEH WITH TWO DOTS BELOW AND SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;; 08BB;ARABIC LETTER AFRICAN FEH;Lo;0;AL;;;;;N;;;;; 08BC;ARABIC LETTER AFRICAN QAF;Lo;0;AL;;;;;N;;;;; 08BD;ARABIC LETTER AFRICAN NOON;Lo;0;AL;;;;;N;;;;; 08D4;ARABIC SMALL HIGH WORD AR-RUB;Mn;230;NSM;;;;;N;;;;; 08D5;ARABIC SMALL HIGH SAD;Mn;230;NSM;;;;;N;;;;; 08D6;ARABIC SMALL HIGH AIN;Mn;230;NSM;;;;;N;;;;; 08D7;ARABIC SMALL HIGH QAF;Mn;230;NSM;;;;;N;;;;; 08D8;ARABIC SMALL HIGH NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;; 08D9;ARABIC SMALL LOW NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;; 08DA;ARABIC SMALL HIGH WORD ATH-THALATHA;Mn;230;NSM;;;;;N;;;;; 08DB;ARABIC SMALL HIGH WORD AS-SAJDA;Mn;230;NSM;;;;;N;;;;; 08DC;ARABIC SMALL HIGH WORD AN-NISF;Mn;230;NSM;;;;;N;;;;; 08DD;ARABIC SMALL HIGH WORD SAKTA;Mn;230;NSM;;;;;N;;;;; 08DE;ARABIC SMALL HIGH WORD QIF;Mn;230;NSM;;;;;N;;;;; 08DF;ARABIC SMALL HIGH WORD WAQFA;Mn;230;NSM;;;;;N;;;;; 08E0;ARABIC SMALL HIGH FOOTNOTE MARKER;Mn;230;NSM;;;;;N;;;;; 08E1;ARABIC SMALL HIGH SIGN SAFHA;Mn;230;NSM;;;;;N;;;;; 08E2;ARABIC DISPUTED END OF AYAH;Cf;0;AN;;;;;N;;;;; 08E3;ARABIC TURNED DAMMA BELOW;Mn;220;NSM;;;;;N;;;;; 08E4;ARABIC CURLY FATHA;Mn;230;NSM;;;;;N;;;;; 08E5;ARABIC CURLY DAMMA;Mn;230;NSM;;;;;N;;;;; 08E6;ARABIC CURLY KASRA;Mn;220;NSM;;;;;N;;;;; 08E7;ARABIC CURLY FATHATAN;Mn;230;NSM;;;;;N;;;;; 08E8;ARABIC CURLY DAMMATAN;Mn;230;NSM;;;;;N;;;;; 08E9;ARABIC CURLY KASRATAN;Mn;220;NSM;;;;;N;;;;; 08EA;ARABIC TONE ONE DOT ABOVE;Mn;230;NSM;;;;;N;;;;; 08EB;ARABIC TONE TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; 08EC;ARABIC TONE LOOP ABOVE;Mn;230;NSM;;;;;N;;;;; 08ED;ARABIC TONE ONE DOT BELOW;Mn;220;NSM;;;;;N;;;;; 08EE;ARABIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;; 08EF;ARABIC TONE LOOP BELOW;Mn;220;NSM;;;;;N;;;;; 08F0;ARABIC OPEN FATHATAN;Mn;27;NSM;;;;;N;;;;; 08F1;ARABIC OPEN DAMMATAN;Mn;28;NSM;;;;;N;;;;; 08F2;ARABIC OPEN KASRATAN;Mn;29;NSM;;;;;N;;;;; 08F3;ARABIC SMALL HIGH WAW;Mn;230;NSM;;;;;N;;;;; 08F4;ARABIC FATHA WITH RING;Mn;230;NSM;;;;;N;;;;; 08F5;ARABIC FATHA WITH DOT ABOVE;Mn;230;NSM;;;;;N;;;;; 08F6;ARABIC KASRA WITH DOT BELOW;Mn;220;NSM;;;;;N;;;;; 08F7;ARABIC LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; 08F8;ARABIC RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; 08F9;ARABIC LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 08FA;ARABIC RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 08FB;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; 08FC;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; 08FD;ARABIC RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; 08FE;ARABIC DAMMA WITH DOT;Mn;230;NSM;;;;;N;;;;; 08FF;ARABIC MARK SIDEWAYS NOON GHUNNA;Mn;230;NSM;;;;;N;;;;; 0900;DEVANAGARI SIGN INVERTED CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0904;DEVANAGARI LETTER SHORT A;Lo;0;L;;;;;N;;;;; 0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;; 0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;; 0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;; 0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;; 0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;; 090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;; 090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;; 090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;; 090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;; 0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;; 0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;; 0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;; 0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;; 0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;; 0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;; 0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;; 0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;; 0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;; 0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;; 091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;; 091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;; 091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;; 091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;; 091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;; 091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;; 0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;; 0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;; 0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;; 0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;; 0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;; 0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;; 0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;; 0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;; 0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;; 0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;; 092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;; 092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;; 092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;; 092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;; 092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;; 092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;; 0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;; 0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;; 0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;; 0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;; 0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;; 0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;; 0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;; 0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;; 0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;; 0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;; 093A;DEVANAGARI VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; 093B;DEVANAGARI VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;; 093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; 0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; 0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; 094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; 094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 094E;DEVANAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;; 094F;DEVANAGARI VOWEL SIGN AW;Mc;0;L;;;;;N;;;;; 0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;; 0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;; 0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;; 0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;; 0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;; 0955;DEVANAGARI VOWEL SIGN CANDRA LONG E;Mn;0;NSM;;;;;N;;;;; 0956;DEVANAGARI VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; 0957;DEVANAGARI VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; 0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;; 0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;; 095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;; 095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;; 095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;; 095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;; 095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;; 095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;; 0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;; 0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 0971;DEVANAGARI SIGN HIGH SPACING DOT;Lm;0;L;;;;;N;;;;; 0972;DEVANAGARI LETTER CANDRA A;Lo;0;L;;;;;N;;;;; 0973;DEVANAGARI LETTER OE;Lo;0;L;;;;;N;;;;; 0974;DEVANAGARI LETTER OOE;Lo;0;L;;;;;N;;;;; 0975;DEVANAGARI LETTER AW;Lo;0;L;;;;;N;;;;; 0976;DEVANAGARI LETTER UE;Lo;0;L;;;;;N;;;;; 0977;DEVANAGARI LETTER UUE;Lo;0;L;;;;;N;;;;; 0978;DEVANAGARI LETTER MARWARI DDA;Lo;0;L;;;;;N;;;;; 0979;DEVANAGARI LETTER ZHA;Lo;0;L;;;;;N;;;;; 097A;DEVANAGARI LETTER HEAVY YA;Lo;0;L;;;;;N;;;;; 097B;DEVANAGARI LETTER GGA;Lo;0;L;;;;;N;;;;; 097C;DEVANAGARI LETTER JJA;Lo;0;L;;;;;N;;;;; 097D;DEVANAGARI LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; 097E;DEVANAGARI LETTER DDDA;Lo;0;L;;;;;N;;;;; 097F;DEVANAGARI LETTER BBA;Lo;0;L;;;;;N;;;;; 0980;BENGALI ANJI;Lo;0;L;;;;;N;;;;; 0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;; 0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;; 0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;; 0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;; 0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;; 098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;; 098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;; 0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;; 0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;; 0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;; 0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;; 0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;; 0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;; 0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;; 0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;; 099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;; 099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;; 099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;; 099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;; 099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;; 099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;; 09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;; 09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;; 09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;; 09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;; 09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;; 09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;; 09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;; 09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;; 09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;; 09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;; 09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;; 09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;; 09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;; 09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;; 09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;; 09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;; 09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;; 09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;; 09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;; 09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;; 09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;; 09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 09BD;BENGALI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;; 09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;; 09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 09CE;BENGALI LETTER KHANDA TA;Lo;0;L;;;;;N;;;;; 09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;; 09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;; 09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;; 09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;; 09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;;;; 09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;;;; 09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;; 09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; 09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1/16;N;;;;; 09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;1/8;N;;;;; 09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3/16;N;;;;; 09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;1/4;N;;;;; 09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;3/4;N;;;;; 09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;; 09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;; 09FB;BENGALI GANDA MARK;Sc;0;ET;;;;;N;;;;; 0A01;GURMUKHI SIGN ADAK BINDI;Mn;0;NSM;;;;;N;;;;; 0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;; 0A03;GURMUKHI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;; 0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;; 0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;; 0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;; 0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;; 0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;; 0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;; 0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;; 0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;; 0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;; 0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;; 0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;; 0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;; 0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;; 0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;; 0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;; 0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;; 0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;; 0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;; 0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;; 0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;; 0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;; 0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;; 0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;; 0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;; 0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;; 0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;; 0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;; 0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;; 0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;; 0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;; 0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;; 0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;; 0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;; 0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;; 0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;; 0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;; 0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;; 0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;; 0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;; 0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;; 0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;; 0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;; 0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; 0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; 0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0A51;GURMUKHI SIGN UDAAT;Mn;0;NSM;;;;;N;;;;; 0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;; 0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;; 0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;; 0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;; 0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;; 0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;; 0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;; 0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;; 0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;; 0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;; 0A75;GURMUKHI SIGN YAKASH;Mn;0;NSM;;;;;N;;;;; 0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;; 0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;; 0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;; 0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;; 0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;; 0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;; 0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 0A8C;GUJARATI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;; 0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;; 0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;; 0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;; 0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;; 0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;; 0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;; 0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;; 0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;; 0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;; 0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;; 0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;; 0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;; 0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;; 0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;; 0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;; 0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;; 0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;; 0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;; 0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;; 0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;; 0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;; 0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;; 0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;; 0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;; 0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;; 0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;; 0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;; 0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;; 0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;; 0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;; 0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;; 0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;; 0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;; 0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;; 0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;; 0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;; 0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;; 0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;; 0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;; 0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; 0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; 0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;; 0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0AE1;GUJARATI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0AE2;GUJARATI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0AE3;GUJARATI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0AF0;GUJARATI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; 0AF9;GUJARATI LETTER ZHA;Lo;0;L;;;;;N;;;;; 0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;; 0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;; 0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;; 0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;; 0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;; 0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;; 0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;; 0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;; 0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;; 0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;; 0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;; 0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;; 0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;; 0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;; 0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;; 0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;; 0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;; 0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;; 0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;; 0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;; 0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;; 0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;; 0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;; 0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;; 0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;; 0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;; 0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;; 0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;; 0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;; 0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;; 0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;; 0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;; 0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;; 0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;; 0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;; 0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;; 0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;; 0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;; 0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;; 0B35;ORIYA LETTER VA;Lo;0;L;;;;;N;;;;; 0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;; 0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;; 0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;; 0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;; 0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 0B44;ORIYA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;; 0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;; 0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;; 0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;; 0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;; 0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;; 0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;; 0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;; 0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0B62;ORIYA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0B63;ORIYA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;; 0B71;ORIYA LETTER WA;Lo;0;L;;;;;N;;;;; 0B72;ORIYA FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; 0B73;ORIYA FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; 0B74;ORIYA FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; 0B75;ORIYA FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; 0B76;ORIYA FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; 0B77;ORIYA FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; 0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;; 0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;; 0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;; 0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;; 0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;; 0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;; 0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;; 0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;; 0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;; 0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;; 0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;; 0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;; 0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;; 0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;; 0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;; 0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;; 0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;; 0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;; 0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;; 0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;; 0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;; 0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;; 0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;; 0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;; 0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;; 0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;; 0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;; 0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;; 0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;; 0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;; 0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;; 0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;; 0BB6;TAMIL LETTER SHA;Lo;0;L;;;;;N;;;;; 0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;; 0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;; 0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;; 0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; 0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;; 0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;; 0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;; 0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0BD0;TAMIL OM;Lo;0;L;;;;;N;;;;; 0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;; 0BE6;TAMIL DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;; 0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; 0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; 0BF3;TAMIL DAY SIGN;So;0;ON;;;;;N;;;;; 0BF4;TAMIL MONTH SIGN;So;0;ON;;;;;N;;;;; 0BF5;TAMIL YEAR SIGN;So;0;ON;;;;;N;;;;; 0BF6;TAMIL DEBIT SIGN;So;0;ON;;;;;N;;;;; 0BF7;TAMIL CREDIT SIGN;So;0;ON;;;;;N;;;;; 0BF8;TAMIL AS ABOVE SIGN;So;0;ON;;;;;N;;;;; 0BF9;TAMIL RUPEE SIGN;Sc;0;ET;;;;;N;;;;; 0BFA;TAMIL NUMBER SIGN;So;0;ON;;;;;N;;;;; 0C00;TELUGU SIGN COMBINING CANDRABINDU ABOVE;Mn;0;NSM;;;;;N;;;;; 0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;; 0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;; 0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;; 0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;; 0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;; 0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;; 0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;; 0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;; 0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;; 0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;; 0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;; 0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;; 0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;; 0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;; 0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;; 0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;; 0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;; 0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;; 0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;; 0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;; 0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;; 0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;; 0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;; 0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;; 0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;; 0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;; 0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;; 0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;; 0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;; 0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;; 0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;; 0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;; 0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;; 0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;; 0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;; 0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;; 0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;; 0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;; 0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;; 0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;; 0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;; 0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;; 0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;; 0C34;TELUGU LETTER LLLA;Lo;0;L;;;;;N;;;;; 0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;; 0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;; 0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;; 0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;; 0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;; 0C3D;TELUGU SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; 0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; 0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; 0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; 0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;; 0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; 0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;; 0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;; 0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;; 0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;; 0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;; 0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0C62;TELUGU VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0C63;TELUGU VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;; 0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;; 0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;; 0C7B;TELUGU FRACTION DIGIT THREE FOR ODD POWERS OF FOUR;No;0;ON;;;;3;N;;;;; 0C7C;TELUGU FRACTION DIGIT ONE FOR EVEN POWERS OF FOUR;No;0;ON;;;;1;N;;;;; 0C7D;TELUGU FRACTION DIGIT TWO FOR EVEN POWERS OF FOUR;No;0;ON;;;;2;N;;;;; 0C7E;TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR;No;0;ON;;;;3;N;;;;; 0C7F;TELUGU SIGN TUUMU;So;0;L;;;;;N;;;;; 0C80;KANNADA SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;; 0C81;KANNADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;; 0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;; 0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;; 0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;; 0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;; 0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;; 0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;; 0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;; 0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;; 0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;; 0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;; 0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;; 0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;; 0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;; 0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;; 0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;; 0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;; 0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;; 0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;; 0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;; 0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;; 0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;; 0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;; 0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;; 0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;; 0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;; 0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;; 0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;; 0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;; 0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;; 0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;; 0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;; 0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;; 0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;; 0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;; 0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;; 0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;; 0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;; 0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;; 0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;; 0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;; 0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;; 0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;; 0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;; 0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;; 0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;; 0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;; 0CBC;KANNADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 0CBD;KANNADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0CBF;KANNADA VOWEL SIGN I;Mn;0;L;;;;;N;;;;; 0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;; 0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; 0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; 0CC6;KANNADA VOWEL SIGN E;Mn;0;L;;;;;N;;;;; 0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;; 0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;; 0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;; 0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;; 0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;; 0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;; 0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;; 0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0CE2;KANNADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0CE3;KANNADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0CF1;KANNADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; 0CF2;KANNADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; 0D01;MALAYALAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;; 0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;; 0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;; 0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;; 0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;; 0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;; 0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;; 0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;; 0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;; 0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;; 0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;; 0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;; 0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;; 0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;; 0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;; 0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;; 0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;; 0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;; 0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;; 0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;; 0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;; 0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;; 0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;; 0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;; 0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;; 0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;; 0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;; 0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;; 0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;; 0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;; 0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;; 0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;; 0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;; 0D29;MALAYALAM LETTER NNNA;Lo;0;L;;;;;N;;;;; 0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;; 0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;; 0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;; 0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;; 0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;; 0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;; 0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;; 0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;; 0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;; 0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;; 0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;; 0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;; 0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;; 0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;; 0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;; 0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;; 0D3A;MALAYALAM LETTER TTTA;Lo;0;L;;;;;N;;;;; 0D3D;MALAYALAM SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 0D44;MALAYALAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; 0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;; 0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;; 0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;; 0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0D4E;MALAYALAM LETTER DOT REPH;Lo;0;L;;;;;N;;;;; 0D4F;MALAYALAM SIGN PARA;So;0;L;;;;;N;;;;; 0D54;MALAYALAM LETTER CHILLU M;Lo;0;L;;;;;N;;;;; 0D55;MALAYALAM LETTER CHILLU Y;Lo;0;L;;;;;N;;;;; 0D56;MALAYALAM LETTER CHILLU LLL;Lo;0;L;;;;;N;;;;; 0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;; 0D58;MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;; 0D59;MALAYALAM FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;; 0D5A;MALAYALAM FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;; 0D5B;MALAYALAM FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;; 0D5C;MALAYALAM FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;; 0D5D;MALAYALAM FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;; 0D5E;MALAYALAM FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;; 0D5F;MALAYALAM LETTER ARCHAIC II;Lo;0;L;;;;;N;;;;; 0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 0D62;MALAYALAM VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 0D63;MALAYALAM VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0D70;MALAYALAM NUMBER TEN;No;0;L;;;;10;N;;;;; 0D71;MALAYALAM NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; 0D72;MALAYALAM NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; 0D73;MALAYALAM FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; 0D74;MALAYALAM FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; 0D75;MALAYALAM FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; 0D76;MALAYALAM FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; 0D77;MALAYALAM FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; 0D78;MALAYALAM FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; 0D79;MALAYALAM DATE MARK;So;0;L;;;;;N;;;;; 0D7A;MALAYALAM LETTER CHILLU NN;Lo;0;L;;;;;N;;;;; 0D7B;MALAYALAM LETTER CHILLU N;Lo;0;L;;;;;N;;;;; 0D7C;MALAYALAM LETTER CHILLU RR;Lo;0;L;;;;;N;;;;; 0D7D;MALAYALAM LETTER CHILLU L;Lo;0;L;;;;;N;;;;; 0D7E;MALAYALAM LETTER CHILLU LL;Lo;0;L;;;;;N;;;;; 0D7F;MALAYALAM LETTER CHILLU K;Lo;0;L;;;;;N;;;;; 0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;; 0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;; 0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;; 0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;; 0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;; 0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;; 0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;; 0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;; 0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;; 0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;; 0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;; 0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;; 0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;; 0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;; 0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;; 0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;; 0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;; 0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;; 0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;; 0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;; 0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; 0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; 0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; 0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; 0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; 0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;; 0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; 0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; 0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; 0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; 0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; 0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;; 0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;; 0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; 0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; 0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; 0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; 0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;; 0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;; 0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; 0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; 0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; 0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; 0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;; 0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;; 0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; 0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; 0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; 0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; 0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;; 0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;; 0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;; 0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;; 0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;; 0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;; 0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;; 0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;; 0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;; 0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;; 0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;; 0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;; 0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;; 0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;; 0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;; 0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;; 0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;; 0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;; 0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;; 0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;; 0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;; 0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;; 0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;; 0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;; 0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;; 0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;; 0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;; 0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;; 0DE6;SINHALA LITH DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0DE7;SINHALA LITH DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0DE8;SINHALA LITH DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0DE9;SINHALA LITH DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0DEA;SINHALA LITH DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0DEB;SINHALA LITH DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0DEC;SINHALA LITH DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0DED;SINHALA LITH DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0DEE;SINHALA LITH DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0DEF;SINHALA LITH DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;; 0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;; 0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;; 0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;; 0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;; 0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;; 0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;; 0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;; 0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;; 0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;; 0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;; 0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;; 0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;; 0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;; 0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;; 0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;; 0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;; 0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;; 0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;; 0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;; 0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;; 0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;; 0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;; 0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;; 0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;; 0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;; 0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;; 0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;; 0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;; 0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;; 0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;; 0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;; 0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;; 0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;; 0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;; 0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;; 0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;; 0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;; 0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;; 0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;; 0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;; 0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;; 0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;; 0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;; 0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;; 0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;; 0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;; 0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;; 0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;; 0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;;;; 0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;; 0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;; 0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;; 0E33;THAI CHARACTER SARA AM;Lo;0;L; 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;; 0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;; 0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;; 0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;; 0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;;;; 0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;; 0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;; 0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;; 0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;; 0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;; 0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;; 0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;; 0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;;;; 0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;;;; 0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;;;; 0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;;;; 0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;;;; 0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;; 0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;; 0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;; 0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;; 0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;; 0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;;;; 0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;; 0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;; 0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;; 0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;; 0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;; 0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;; 0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;; 0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;; 0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;; 0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;; 0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;; 0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;; 0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;; 0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;; 0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;; 0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;; 0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;; 0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;; 0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;; 0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;; 0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;; 0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;; 0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;; 0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;; 0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;; 0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;; 0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;; 0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;; 0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;; 0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;; 0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;; 0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;; 0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;; 0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;; 0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;; 0EB3;LAO VOWEL SIGN AM;Lo;0;L; 0ECD 0EB2;;;;N;;;;; 0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; 0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; 0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;; 0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;; 0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;; 0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;; 0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;; 0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;; 0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;; 0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;; 0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;; 0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;; 0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;; 0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;; 0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;; 0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;; 0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;; 0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;; 0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;; 0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0EDC;LAO HO NO;Lo;0;L; 0EAB 0E99;;;;N;;;;; 0EDD;LAO HO MO;Lo;0;L; 0EAB 0EA1;;;;N;;;;; 0EDE;LAO LETTER KHMU GO;Lo;0;L;;;;;N;;;;; 0EDF;LAO LETTER KHMU NYO;Lo;0;L;;;;;N;;;;; 0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;; 0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;;;; 0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;;;; 0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;;;; 0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;;;; 0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;; 0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;;;; 0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;;;; 0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;;;; 0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;;;; 0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;;;; 0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;;;; 0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L; 0F0B;;;;N;;;;; 0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;;;; 0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;;;; 0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;;;; 0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;;;; 0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;;;; 0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;;;; 0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;;;; 0F14;TIBETAN MARK GTER TSHEG;Po;0;L;;;;;N;TIBETAN COMMA;;;; 0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;;;; 0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;;;; 0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;;;; 0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;;;; 0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;;;; 0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;;;; 0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;;;; 0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;;;; 0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;;;; 0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;;;; 0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;;;; 0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;; 0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;; 0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;; 0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;; 0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;; 0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;; 0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;; 0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;; 0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;; 0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;; 0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;;;; 0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;;;; 0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;;;; 0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;;;; 0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;;;; 0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;;;; 0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;Y;;;;; 0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;Y;;;;; 0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;Y;TIBETAN LEFT BRACE;;;; 0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;Y;TIBETAN RIGHT BRACE;;;; 0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;;;; 0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;;;; 0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;; 0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;; 0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;; 0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;; 0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;; 0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;; 0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;; 0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;; 0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;; 0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;; 0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;; 0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;; 0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;; 0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;; 0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;; 0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;; 0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;; 0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;; 0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;; 0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;; 0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;; 0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;; 0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;; 0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;; 0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;; 0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;; 0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;; 0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;; 0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;; 0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;; 0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;; 0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;; 0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;; 0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;;;; 0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;; 0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;; 0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;; 0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;; 0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;; 0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;; 0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;; 0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;;;; 0F6B;TIBETAN LETTER KKA;Lo;0;L;;;;;N;;;;; 0F6C;TIBETAN LETTER RRA;Lo;0;L;;;;;N;;;;; 0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;; 0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;; 0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;; 0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;; 0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;; 0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;; 0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM; 0FB2 0F81;;;;N;;;;; 0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;; 0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM; 0FB3 0F81;;;;N;;;;; 0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;; 0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;; 0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;; 0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;; 0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;;;; 0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;;;; 0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;; 0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;; 0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;;;; 0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;;;; 0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;; 0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;; 0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;;;; 0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;;;; 0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;;;; 0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;;;; 0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;;;; 0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;;;; 0F8C;TIBETAN SIGN INVERTED MCHU CAN;Lo;0;L;;;;;N;;;;; 0F8D;TIBETAN SUBJOINED SIGN LCE TSA CAN;Mn;0;NSM;;;;;N;;;;; 0F8E;TIBETAN SUBJOINED SIGN MCHU CAN;Mn;0;NSM;;;;;N;;;;; 0F8F;TIBETAN SUBJOINED SIGN INVERTED MCHU CAN;Mn;0;NSM;;;;;N;;;;; 0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;; 0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;; 0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;; 0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;; 0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;; 0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;; 0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;; 0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;; 0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;; 0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;; 0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;; 0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;; 0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;; 0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;; 0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;; 0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;; 0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;; 0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;; 0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;; 0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;; 0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;; 0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;; 0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;; 0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;; 0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;; 0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;; 0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;; 0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;; 0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;; 0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;; 0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;; 0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;; 0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;;;; 0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;; 0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;; 0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;; 0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;; 0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;; 0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;; 0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;; 0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;; 0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;;;; 0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;;;; 0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;;;; 0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;;;; 0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;;;; 0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;; 0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;; 0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;;;; 0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;;;; 0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;;;; 0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;;;; 0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;;;; 0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;;;; 0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;;;; 0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;;;; 0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;;;; 0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;;;; 0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;;;; 0FCE;TIBETAN SIGN RDEL NAG RDEL DKAR;So;0;L;;;;;N;;;;; 0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;;;; 0FD0;TIBETAN MARK BSKA- SHOG GI MGO RGYAN;Po;0;L;;;;;N;;;;; 0FD1;TIBETAN MARK MNYAM YIG GI MGO RGYAN;Po;0;L;;;;;N;;;;; 0FD2;TIBETAN MARK NYIS TSHEG;Po;0;L;;;;;N;;;;; 0FD3;TIBETAN MARK INITIAL BRDA RNYING YIG MGO MDUN MA;Po;0;L;;;;;N;;;;; 0FD4;TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;; 0FD5;RIGHT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;; 0FD6;LEFT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;; 0FD7;RIGHT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;; 0FD8;LEFT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;; 0FD9;TIBETAN MARK LEADING MCHAN RTAGS;Po;0;L;;;;;N;;;;; 0FDA;TIBETAN MARK TRAILING MCHAN RTAGS;Po;0;L;;;;;N;;;;; 1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;; 1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;; 1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;; 1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;; 1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;; 1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;; 1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;; 1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;; 1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;; 1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;; 100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;; 100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;; 100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;; 100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;; 100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;; 100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;; 1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;; 1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;; 1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;; 1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;; 1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;; 1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;; 1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;; 1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;; 1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;; 1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;; 101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;; 101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;; 101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;; 101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;; 101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;; 101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;; 1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;; 1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;; 1022;MYANMAR LETTER SHAN A;Lo;0;L;;;;;N;;;;; 1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;; 1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;; 1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;; 1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;; 1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;; 1028;MYANMAR LETTER MON E;Lo;0;L;;;;;N;;;;; 1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;; 102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;; 102B;MYANMAR VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;; 102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 1033;MYANMAR VOWEL SIGN MON II;Mn;0;NSM;;;;;N;;;;; 1034;MYANMAR VOWEL SIGN MON O;Mn;0;NSM;;;;;N;;;;; 1035;MYANMAR VOWEL SIGN E ABOVE;Mn;0;NSM;;;;;N;;;;; 1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;; 1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;; 1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 103A;MYANMAR SIGN ASAT;Mn;9;NSM;;;;;N;;;;; 103B;MYANMAR CONSONANT SIGN MEDIAL YA;Mc;0;L;;;;;N;;;;; 103C;MYANMAR CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;; 103D;MYANMAR CONSONANT SIGN MEDIAL WA;Mn;0;NSM;;;;;N;;;;; 103E;MYANMAR CONSONANT SIGN MEDIAL HA;Mn;0;NSM;;;;;N;;;;; 103F;MYANMAR LETTER GREAT SA;Lo;0;L;;;;;N;;;;; 1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;; 104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;; 104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;; 104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;; 104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;; 104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;; 1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;; 1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;; 1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; 1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; 1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 105A;MYANMAR LETTER MON NGA;Lo;0;L;;;;;N;;;;; 105B;MYANMAR LETTER MON JHA;Lo;0;L;;;;;N;;;;; 105C;MYANMAR LETTER MON BBA;Lo;0;L;;;;;N;;;;; 105D;MYANMAR LETTER MON BBE;Lo;0;L;;;;;N;;;;; 105E;MYANMAR CONSONANT SIGN MON MEDIAL NA;Mn;0;NSM;;;;;N;;;;; 105F;MYANMAR CONSONANT SIGN MON MEDIAL MA;Mn;0;NSM;;;;;N;;;;; 1060;MYANMAR CONSONANT SIGN MON MEDIAL LA;Mn;0;NSM;;;;;N;;;;; 1061;MYANMAR LETTER SGAW KAREN SHA;Lo;0;L;;;;;N;;;;; 1062;MYANMAR VOWEL SIGN SGAW KAREN EU;Mc;0;L;;;;;N;;;;; 1063;MYANMAR TONE MARK SGAW KAREN HATHI;Mc;0;L;;;;;N;;;;; 1064;MYANMAR TONE MARK SGAW KAREN KE PHO;Mc;0;L;;;;;N;;;;; 1065;MYANMAR LETTER WESTERN PWO KAREN THA;Lo;0;L;;;;;N;;;;; 1066;MYANMAR LETTER WESTERN PWO KAREN PWA;Lo;0;L;;;;;N;;;;; 1067;MYANMAR VOWEL SIGN WESTERN PWO KAREN EU;Mc;0;L;;;;;N;;;;; 1068;MYANMAR VOWEL SIGN WESTERN PWO KAREN UE;Mc;0;L;;;;;N;;;;; 1069;MYANMAR SIGN WESTERN PWO KAREN TONE-1;Mc;0;L;;;;;N;;;;; 106A;MYANMAR SIGN WESTERN PWO KAREN TONE-2;Mc;0;L;;;;;N;;;;; 106B;MYANMAR SIGN WESTERN PWO KAREN TONE-3;Mc;0;L;;;;;N;;;;; 106C;MYANMAR SIGN WESTERN PWO KAREN TONE-4;Mc;0;L;;;;;N;;;;; 106D;MYANMAR SIGN WESTERN PWO KAREN TONE-5;Mc;0;L;;;;;N;;;;; 106E;MYANMAR LETTER EASTERN PWO KAREN NNA;Lo;0;L;;;;;N;;;;; 106F;MYANMAR LETTER EASTERN PWO KAREN YWA;Lo;0;L;;;;;N;;;;; 1070;MYANMAR LETTER EASTERN PWO KAREN GHWA;Lo;0;L;;;;;N;;;;; 1071;MYANMAR VOWEL SIGN GEBA KAREN I;Mn;0;NSM;;;;;N;;;;; 1072;MYANMAR VOWEL SIGN KAYAH OE;Mn;0;NSM;;;;;N;;;;; 1073;MYANMAR VOWEL SIGN KAYAH U;Mn;0;NSM;;;;;N;;;;; 1074;MYANMAR VOWEL SIGN KAYAH EE;Mn;0;NSM;;;;;N;;;;; 1075;MYANMAR LETTER SHAN KA;Lo;0;L;;;;;N;;;;; 1076;MYANMAR LETTER SHAN KHA;Lo;0;L;;;;;N;;;;; 1077;MYANMAR LETTER SHAN GA;Lo;0;L;;;;;N;;;;; 1078;MYANMAR LETTER SHAN CA;Lo;0;L;;;;;N;;;;; 1079;MYANMAR LETTER SHAN ZA;Lo;0;L;;;;;N;;;;; 107A;MYANMAR LETTER SHAN NYA;Lo;0;L;;;;;N;;;;; 107B;MYANMAR LETTER SHAN DA;Lo;0;L;;;;;N;;;;; 107C;MYANMAR LETTER SHAN NA;Lo;0;L;;;;;N;;;;; 107D;MYANMAR LETTER SHAN PHA;Lo;0;L;;;;;N;;;;; 107E;MYANMAR LETTER SHAN FA;Lo;0;L;;;;;N;;;;; 107F;MYANMAR LETTER SHAN BA;Lo;0;L;;;;;N;;;;; 1080;MYANMAR LETTER SHAN THA;Lo;0;L;;;;;N;;;;; 1081;MYANMAR LETTER SHAN HA;Lo;0;L;;;;;N;;;;; 1082;MYANMAR CONSONANT SIGN SHAN MEDIAL WA;Mn;0;NSM;;;;;N;;;;; 1083;MYANMAR VOWEL SIGN SHAN AA;Mc;0;L;;;;;N;;;;; 1084;MYANMAR VOWEL SIGN SHAN E;Mc;0;L;;;;;N;;;;; 1085;MYANMAR VOWEL SIGN SHAN E ABOVE;Mn;0;NSM;;;;;N;;;;; 1086;MYANMAR VOWEL SIGN SHAN FINAL Y;Mn;0;NSM;;;;;N;;;;; 1087;MYANMAR SIGN SHAN TONE-2;Mc;0;L;;;;;N;;;;; 1088;MYANMAR SIGN SHAN TONE-3;Mc;0;L;;;;;N;;;;; 1089;MYANMAR SIGN SHAN TONE-5;Mc;0;L;;;;;N;;;;; 108A;MYANMAR SIGN SHAN TONE-6;Mc;0;L;;;;;N;;;;; 108B;MYANMAR SIGN SHAN COUNCIL TONE-2;Mc;0;L;;;;;N;;;;; 108C;MYANMAR SIGN SHAN COUNCIL TONE-3;Mc;0;L;;;;;N;;;;; 108D;MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE;Mn;220;NSM;;;;;N;;;;; 108E;MYANMAR LETTER RUMAI PALAUNG FA;Lo;0;L;;;;;N;;;;; 108F;MYANMAR SIGN RUMAI PALAUNG TONE-5;Mc;0;L;;;;;N;;;;; 1090;MYANMAR SHAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1091;MYANMAR SHAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1092;MYANMAR SHAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1093;MYANMAR SHAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1094;MYANMAR SHAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1095;MYANMAR SHAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1096;MYANMAR SHAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1097;MYANMAR SHAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1098;MYANMAR SHAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1099;MYANMAR SHAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 109A;MYANMAR SIGN KHAMTI TONE-1;Mc;0;L;;;;;N;;;;; 109B;MYANMAR SIGN KHAMTI TONE-3;Mc;0;L;;;;;N;;;;; 109C;MYANMAR VOWEL SIGN AITON A;Mc;0;L;;;;;N;;;;; 109D;MYANMAR VOWEL SIGN AITON AI;Mn;0;NSM;;;;;N;;;;; 109E;MYANMAR SYMBOL SHAN ONE;So;0;L;;;;;N;;;;; 109F;MYANMAR SYMBOL SHAN EXCLAMATION;So;0;L;;;;;N;;;;; 10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;;;2D00; 10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;;;2D01; 10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;;;2D02; 10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;;;2D03; 10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;2D04; 10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;;;2D05; 10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;;;2D06; 10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;;;2D07; 10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;;;2D08; 10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;;;2D09; 10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;;;2D0A; 10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;;;2D0B; 10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;;;2D0C; 10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;;;2D0D; 10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;;;2D0E; 10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;;;2D0F; 10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;;;2D10; 10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;2D11; 10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;;;2D12; 10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;;;2D13; 10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;;;2D14; 10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;;;2D15; 10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;;;2D16; 10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;;;2D17; 10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;;;2D18; 10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;;;2D19; 10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;;;2D1A; 10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;;;2D1B; 10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;;;2D1C; 10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;;;2D1D; 10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;;;2D1E; 10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;;;2D1F; 10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;;;2D20; 10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;;;2D21; 10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;;;2D22; 10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;2D23; 10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;2D24; 10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;2D25; 10C7;GEORGIAN CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;2D27; 10CD;GEORGIAN CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;2D2D; 10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;; 10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;; 10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;; 10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;; 10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;; 10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;; 10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;; 10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;; 10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;; 10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;; 10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;; 10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;; 10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;; 10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;; 10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;; 10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;; 10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;; 10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;; 10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;; 10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;; 10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;; 10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;; 10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;; 10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;; 10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;; 10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;; 10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;; 10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;; 10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;; 10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;; 10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;; 10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;; 10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;; 10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;; 10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;; 10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;; 10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;; 10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;; 10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;; 10F7;GEORGIAN LETTER YN;Lo;0;L;;;;;N;;;;; 10F8;GEORGIAN LETTER ELIFI;Lo;0;L;;;;;N;;;;; 10F9;GEORGIAN LETTER TURNED GAN;Lo;0;L;;;;;N;;;;; 10FA;GEORGIAN LETTER AIN;Lo;0;L;;;;;N;;;;; 10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; 10FC;MODIFIER LETTER GEORGIAN NAR;Lm;0;L; 10DC;;;;N;;;;; 10FD;GEORGIAN LETTER AEN;Lo;0;L;;;;;N;;;;; 10FE;GEORGIAN LETTER HARD SIGN;Lo;0;L;;;;;N;;;;; 10FF;GEORGIAN LETTER LABIAL SIGN;Lo;0;L;;;;;N;;;;; 1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;; 1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;; 1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;;;; 1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;;;; 1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;; 1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;;;; 1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;;;; 1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;;;; 1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;; 1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;;;; 110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;; 110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;; 110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;;;; 110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;; 110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;;;; 110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;;;; 1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;;;; 1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;;;; 1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;;;; 1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; 1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;; 1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; 1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;; 1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; 1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; 1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; 111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;; 111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;; 111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; 111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; 111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; 111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;; 1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; 1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;; 1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; 1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; 1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;; 1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;; 1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;; 1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;; 1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;; 1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;; 112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; 112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; 112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;; 112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; 112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;; 112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; 1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; 1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;; 1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; 1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; 1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;; 1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;; 1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;; 1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;; 1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;; 1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; 113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;; 113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;; 113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;; 113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; 113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;; 113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; 1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;; 1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; 1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;; 1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;; 1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;; 1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;; 1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; 1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; 1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;; 1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;; 114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;; 114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;; 114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; 114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;; 114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;; 114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; 1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;; 1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; 1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;; 1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;; 1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;; 1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;; 1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; 1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; 1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;; 1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; 115A;HANGUL CHOSEONG KIYEOK-TIKEUT;Lo;0;L;;;;;N;;;;; 115B;HANGUL CHOSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;; 115C;HANGUL CHOSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;; 115D;HANGUL CHOSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;; 115E;HANGUL CHOSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;; 115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;; 1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;; 1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;; 1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;; 1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;; 1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;; 1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;; 1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;; 1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;; 1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;; 1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;; 116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;; 116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;; 116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;; 116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;; 116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;; 116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;; 1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;; 1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;; 1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;; 1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;; 1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;; 1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;; 1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;; 1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;; 1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;; 1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;; 117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;; 117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;; 117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;; 117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;; 117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;; 117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;; 1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;; 1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;; 1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;; 1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;; 1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;; 1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;; 1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;; 1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;; 1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;; 1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;; 118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;; 118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;; 118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;; 118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;; 118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;; 118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;; 1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;; 1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;; 1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;; 1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;; 1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;; 1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;; 1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;; 1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;; 1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;; 1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;; 119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;; 119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;; 119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;; 119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;; 119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;; 119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;; 11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;; 11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;; 11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;; 11A3;HANGUL JUNGSEONG A-EU;Lo;0;L;;;;;N;;;;; 11A4;HANGUL JUNGSEONG YA-U;Lo;0;L;;;;;N;;;;; 11A5;HANGUL JUNGSEONG YEO-YA;Lo;0;L;;;;;N;;;;; 11A6;HANGUL JUNGSEONG O-YA;Lo;0;L;;;;;N;;;;; 11A7;HANGUL JUNGSEONG O-YAE;Lo;0;L;;;;;N;;;;; 11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;;;; 11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;; 11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;;;; 11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;;;; 11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;; 11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;; 11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;;;; 11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;;;; 11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;; 11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;; 11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;; 11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;; 11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;;;; 11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;; 11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;; 11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;;;; 11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;;;; 11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;; 11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;;;; 11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;; 11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;;;; 11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;;;; 11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;;;; 11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;;;; 11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;;;; 11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;;;; 11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;;;; 11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;; 11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; 11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; 11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; 11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;; 11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;; 11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;; 11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; 11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;; 11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;; 11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; 11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;; 11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;; 11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; 11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; 11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;; 11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;; 11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; 11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; 11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;; 11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;; 11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; 11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;; 11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; 11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;; 11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; 11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;; 11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;; 11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;; 11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;; 11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;; 11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; 11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;; 11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; 11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; 11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; 11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; 11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; 11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; 11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; 11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;; 11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; 11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; 11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; 11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;; 11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; 11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;; 11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; 11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; 11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; 11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;; 11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;; 11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;; 11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;; 11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; 11FA;HANGUL JONGSEONG KIYEOK-NIEUN;Lo;0;L;;;;;N;;;;; 11FB;HANGUL JONGSEONG KIYEOK-PIEUP;Lo;0;L;;;;;N;;;;; 11FC;HANGUL JONGSEONG KIYEOK-CHIEUCH;Lo;0;L;;;;;N;;;;; 11FD;HANGUL JONGSEONG KIYEOK-KHIEUKH;Lo;0;L;;;;;N;;;;; 11FE;HANGUL JONGSEONG KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;; 11FF;HANGUL JONGSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;; 1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;; 1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;; 1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;; 1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;; 1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;; 1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;; 1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;; 1207;ETHIOPIC SYLLABLE HOA;Lo;0;L;;;;;N;;;;; 1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;; 1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;; 120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;; 120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;; 120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;; 120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;; 120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;; 120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;; 1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;; 1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;; 1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;; 1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;; 1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;; 1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;; 1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;; 1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;; 1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;; 1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;; 121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;; 121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;; 121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;; 121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;; 121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;; 121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;; 1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;; 1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;; 1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;; 1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;; 1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;; 1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;; 1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;; 1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;; 1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;; 1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;; 122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;; 122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;; 122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;; 122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;; 122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;; 122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;; 1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;; 1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;; 1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;; 1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;; 1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;; 1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;; 1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;; 1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;; 1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;; 1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;; 123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;; 123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;; 123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;; 123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;; 123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;; 123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;; 1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;; 1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;; 1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;; 1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;; 1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;; 1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;; 1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;; 1247;ETHIOPIC SYLLABLE QOA;Lo;0;L;;;;;N;;;;; 1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;; 124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;; 124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;; 124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;; 124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;; 1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;; 1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;; 1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;; 1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;; 1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;; 1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;; 1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;; 1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;; 125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;; 125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;; 125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;; 125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;; 1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;; 1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;; 1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;; 1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;; 1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;; 1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;; 1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;; 1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;; 1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;; 1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;; 126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;; 126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;; 126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;; 126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;; 126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;; 126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;; 1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;; 1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;; 1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;; 1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;; 1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;; 1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;; 1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;; 1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;; 1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;; 1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;; 127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;; 127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;; 127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;; 127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;; 127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;; 127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;; 1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;; 1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;; 1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;; 1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;; 1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;; 1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;; 1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;; 1287;ETHIOPIC SYLLABLE XOA;Lo;0;L;;;;;N;;;;; 1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;; 128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;; 128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;; 128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;; 128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;; 1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;; 1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;; 1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;; 1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;; 1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;; 1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;; 1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;; 1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;; 1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;; 1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;; 129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;; 129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;; 129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;; 129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;; 129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;; 129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;; 12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;; 12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;; 12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;; 12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;; 12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;; 12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;; 12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;; 12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;; 12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;; 12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;; 12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;; 12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;; 12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;; 12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;; 12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;; 12AF;ETHIOPIC SYLLABLE KOA;Lo;0;L;;;;;N;;;;; 12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;; 12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;; 12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;; 12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;; 12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;; 12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;; 12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;; 12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;; 12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;; 12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;; 12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;; 12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;; 12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;; 12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;; 12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;; 12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;; 12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;; 12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;; 12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;; 12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;; 12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;; 12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;; 12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;; 12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;; 12CF;ETHIOPIC SYLLABLE WOA;Lo;0;L;;;;;N;;;;; 12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;; 12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;; 12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;; 12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;; 12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;; 12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;; 12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;; 12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;; 12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;; 12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;; 12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;; 12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;; 12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;; 12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;; 12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;; 12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; 12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; 12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;; 12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;; 12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;; 12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; 12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; 12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;; 12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;; 12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;; 12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;; 12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;; 12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;; 12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;; 12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;; 12EF;ETHIOPIC SYLLABLE YOA;Lo;0;L;;;;;N;;;;; 12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;; 12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;; 12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;; 12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;; 12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;; 12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;; 12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;; 12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;; 12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;; 12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;; 12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;; 12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;; 12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;; 12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;; 12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;; 12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;; 1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;; 1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;; 1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;; 1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;; 1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;; 1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;; 1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;; 1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;; 1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;; 1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;; 130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;; 130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;; 130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;; 130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;; 130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;; 130F;ETHIOPIC SYLLABLE GOA;Lo;0;L;;;;;N;;;;; 1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;; 1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;; 1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;; 1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;; 1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;; 1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;; 1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;; 131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;; 131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;; 131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;; 131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;; 131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;; 131F;ETHIOPIC SYLLABLE GGWAA;Lo;0;L;;;;;N;;;;; 1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;; 1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;; 1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;; 1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;; 1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;; 1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;; 1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;; 1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;; 1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;; 1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;; 132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;; 132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;; 132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;; 132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;; 132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;; 132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;; 1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;; 1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;; 1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;; 1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;; 1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;; 1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;; 1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;; 1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;; 1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;; 1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;; 133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;; 133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;; 133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;; 133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;; 133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;; 133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;; 1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;; 1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;; 1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;; 1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;; 1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;; 1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;; 1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;; 1347;ETHIOPIC SYLLABLE TZOA;Lo;0;L;;;;;N;;;;; 1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;; 1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;; 134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;; 134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;; 134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;; 134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;; 134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;; 134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;; 1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;; 1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;; 1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;; 1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;; 1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;; 1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;; 1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;; 1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;; 1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;; 1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;; 135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;; 135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; 135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; 135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;; 1360;ETHIOPIC SECTION MARK;Po;0;L;;;;;N;;;;; 1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;; 1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;; 1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;; 1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;; 1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;; 1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;; 1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;; 1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; 1369;ETHIOPIC DIGIT ONE;No;0;L;;;1;1;N;;;;; 136A;ETHIOPIC DIGIT TWO;No;0;L;;;2;2;N;;;;; 136B;ETHIOPIC DIGIT THREE;No;0;L;;;3;3;N;;;;; 136C;ETHIOPIC DIGIT FOUR;No;0;L;;;4;4;N;;;;; 136D;ETHIOPIC DIGIT FIVE;No;0;L;;;5;5;N;;;;; 136E;ETHIOPIC DIGIT SIX;No;0;L;;;6;6;N;;;;; 136F;ETHIOPIC DIGIT SEVEN;No;0;L;;;7;7;N;;;;; 1370;ETHIOPIC DIGIT EIGHT;No;0;L;;;8;8;N;;;;; 1371;ETHIOPIC DIGIT NINE;No;0;L;;;9;9;N;;;;; 1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;; 1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;; 1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;; 1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;; 1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;; 1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;; 1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;; 1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;; 137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;; 137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;; 137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;; 1380;ETHIOPIC SYLLABLE SEBATBEIT MWA;Lo;0;L;;;;;N;;;;; 1381;ETHIOPIC SYLLABLE MWI;Lo;0;L;;;;;N;;;;; 1382;ETHIOPIC SYLLABLE MWEE;Lo;0;L;;;;;N;;;;; 1383;ETHIOPIC SYLLABLE MWE;Lo;0;L;;;;;N;;;;; 1384;ETHIOPIC SYLLABLE SEBATBEIT BWA;Lo;0;L;;;;;N;;;;; 1385;ETHIOPIC SYLLABLE BWI;Lo;0;L;;;;;N;;;;; 1386;ETHIOPIC SYLLABLE BWEE;Lo;0;L;;;;;N;;;;; 1387;ETHIOPIC SYLLABLE BWE;Lo;0;L;;;;;N;;;;; 1388;ETHIOPIC SYLLABLE SEBATBEIT FWA;Lo;0;L;;;;;N;;;;; 1389;ETHIOPIC SYLLABLE FWI;Lo;0;L;;;;;N;;;;; 138A;ETHIOPIC SYLLABLE FWEE;Lo;0;L;;;;;N;;;;; 138B;ETHIOPIC SYLLABLE FWE;Lo;0;L;;;;;N;;;;; 138C;ETHIOPIC SYLLABLE SEBATBEIT PWA;Lo;0;L;;;;;N;;;;; 138D;ETHIOPIC SYLLABLE PWI;Lo;0;L;;;;;N;;;;; 138E;ETHIOPIC SYLLABLE PWEE;Lo;0;L;;;;;N;;;;; 138F;ETHIOPIC SYLLABLE PWE;Lo;0;L;;;;;N;;;;; 1390;ETHIOPIC TONAL MARK YIZET;So;0;ON;;;;;N;;;;; 1391;ETHIOPIC TONAL MARK DERET;So;0;ON;;;;;N;;;;; 1392;ETHIOPIC TONAL MARK RIKRIK;So;0;ON;;;;;N;;;;; 1393;ETHIOPIC TONAL MARK SHORT RIKRIK;So;0;ON;;;;;N;;;;; 1394;ETHIOPIC TONAL MARK DIFAT;So;0;ON;;;;;N;;;;; 1395;ETHIOPIC TONAL MARK KENAT;So;0;ON;;;;;N;;;;; 1396;ETHIOPIC TONAL MARK CHIRET;So;0;ON;;;;;N;;;;; 1397;ETHIOPIC TONAL MARK HIDET;So;0;ON;;;;;N;;;;; 1398;ETHIOPIC TONAL MARK DERET-HIDET;So;0;ON;;;;;N;;;;; 1399;ETHIOPIC TONAL MARK KURT;So;0;ON;;;;;N;;;;; 13A0;CHEROKEE LETTER A;Lu;0;L;;;;;N;;;;AB70; 13A1;CHEROKEE LETTER E;Lu;0;L;;;;;N;;;;AB71; 13A2;CHEROKEE LETTER I;Lu;0;L;;;;;N;;;;AB72; 13A3;CHEROKEE LETTER O;Lu;0;L;;;;;N;;;;AB73; 13A4;CHEROKEE LETTER U;Lu;0;L;;;;;N;;;;AB74; 13A5;CHEROKEE LETTER V;Lu;0;L;;;;;N;;;;AB75; 13A6;CHEROKEE LETTER GA;Lu;0;L;;;;;N;;;;AB76; 13A7;CHEROKEE LETTER KA;Lu;0;L;;;;;N;;;;AB77; 13A8;CHEROKEE LETTER GE;Lu;0;L;;;;;N;;;;AB78; 13A9;CHEROKEE LETTER GI;Lu;0;L;;;;;N;;;;AB79; 13AA;CHEROKEE LETTER GO;Lu;0;L;;;;;N;;;;AB7A; 13AB;CHEROKEE LETTER GU;Lu;0;L;;;;;N;;;;AB7B; 13AC;CHEROKEE LETTER GV;Lu;0;L;;;;;N;;;;AB7C; 13AD;CHEROKEE LETTER HA;Lu;0;L;;;;;N;;;;AB7D; 13AE;CHEROKEE LETTER HE;Lu;0;L;;;;;N;;;;AB7E; 13AF;CHEROKEE LETTER HI;Lu;0;L;;;;;N;;;;AB7F; 13B0;CHEROKEE LETTER HO;Lu;0;L;;;;;N;;;;AB80; 13B1;CHEROKEE LETTER HU;Lu;0;L;;;;;N;;;;AB81; 13B2;CHEROKEE LETTER HV;Lu;0;L;;;;;N;;;;AB82; 13B3;CHEROKEE LETTER LA;Lu;0;L;;;;;N;;;;AB83; 13B4;CHEROKEE LETTER LE;Lu;0;L;;;;;N;;;;AB84; 13B5;CHEROKEE LETTER LI;Lu;0;L;;;;;N;;;;AB85; 13B6;CHEROKEE LETTER LO;Lu;0;L;;;;;N;;;;AB86; 13B7;CHEROKEE LETTER LU;Lu;0;L;;;;;N;;;;AB87; 13B8;CHEROKEE LETTER LV;Lu;0;L;;;;;N;;;;AB88; 13B9;CHEROKEE LETTER MA;Lu;0;L;;;;;N;;;;AB89; 13BA;CHEROKEE LETTER ME;Lu;0;L;;;;;N;;;;AB8A; 13BB;CHEROKEE LETTER MI;Lu;0;L;;;;;N;;;;AB8B; 13BC;CHEROKEE LETTER MO;Lu;0;L;;;;;N;;;;AB8C; 13BD;CHEROKEE LETTER MU;Lu;0;L;;;;;N;;;;AB8D; 13BE;CHEROKEE LETTER NA;Lu;0;L;;;;;N;;;;AB8E; 13BF;CHEROKEE LETTER HNA;Lu;0;L;;;;;N;;;;AB8F; 13C0;CHEROKEE LETTER NAH;Lu;0;L;;;;;N;;;;AB90; 13C1;CHEROKEE LETTER NE;Lu;0;L;;;;;N;;;;AB91; 13C2;CHEROKEE LETTER NI;Lu;0;L;;;;;N;;;;AB92; 13C3;CHEROKEE LETTER NO;Lu;0;L;;;;;N;;;;AB93; 13C4;CHEROKEE LETTER NU;Lu;0;L;;;;;N;;;;AB94; 13C5;CHEROKEE LETTER NV;Lu;0;L;;;;;N;;;;AB95; 13C6;CHEROKEE LETTER QUA;Lu;0;L;;;;;N;;;;AB96; 13C7;CHEROKEE LETTER QUE;Lu;0;L;;;;;N;;;;AB97; 13C8;CHEROKEE LETTER QUI;Lu;0;L;;;;;N;;;;AB98; 13C9;CHEROKEE LETTER QUO;Lu;0;L;;;;;N;;;;AB99; 13CA;CHEROKEE LETTER QUU;Lu;0;L;;;;;N;;;;AB9A; 13CB;CHEROKEE LETTER QUV;Lu;0;L;;;;;N;;;;AB9B; 13CC;CHEROKEE LETTER SA;Lu;0;L;;;;;N;;;;AB9C; 13CD;CHEROKEE LETTER S;Lu;0;L;;;;;N;;;;AB9D; 13CE;CHEROKEE LETTER SE;Lu;0;L;;;;;N;;;;AB9E; 13CF;CHEROKEE LETTER SI;Lu;0;L;;;;;N;;;;AB9F; 13D0;CHEROKEE LETTER SO;Lu;0;L;;;;;N;;;;ABA0; 13D1;CHEROKEE LETTER SU;Lu;0;L;;;;;N;;;;ABA1; 13D2;CHEROKEE LETTER SV;Lu;0;L;;;;;N;;;;ABA2; 13D3;CHEROKEE LETTER DA;Lu;0;L;;;;;N;;;;ABA3; 13D4;CHEROKEE LETTER TA;Lu;0;L;;;;;N;;;;ABA4; 13D5;CHEROKEE LETTER DE;Lu;0;L;;;;;N;;;;ABA5; 13D6;CHEROKEE LETTER TE;Lu;0;L;;;;;N;;;;ABA6; 13D7;CHEROKEE LETTER DI;Lu;0;L;;;;;N;;;;ABA7; 13D8;CHEROKEE LETTER TI;Lu;0;L;;;;;N;;;;ABA8; 13D9;CHEROKEE LETTER DO;Lu;0;L;;;;;N;;;;ABA9; 13DA;CHEROKEE LETTER DU;Lu;0;L;;;;;N;;;;ABAA; 13DB;CHEROKEE LETTER DV;Lu;0;L;;;;;N;;;;ABAB; 13DC;CHEROKEE LETTER DLA;Lu;0;L;;;;;N;;;;ABAC; 13DD;CHEROKEE LETTER TLA;Lu;0;L;;;;;N;;;;ABAD; 13DE;CHEROKEE LETTER TLE;Lu;0;L;;;;;N;;;;ABAE; 13DF;CHEROKEE LETTER TLI;Lu;0;L;;;;;N;;;;ABAF; 13E0;CHEROKEE LETTER TLO;Lu;0;L;;;;;N;;;;ABB0; 13E1;CHEROKEE LETTER TLU;Lu;0;L;;;;;N;;;;ABB1; 13E2;CHEROKEE LETTER TLV;Lu;0;L;;;;;N;;;;ABB2; 13E3;CHEROKEE LETTER TSA;Lu;0;L;;;;;N;;;;ABB3; 13E4;CHEROKEE LETTER TSE;Lu;0;L;;;;;N;;;;ABB4; 13E5;CHEROKEE LETTER TSI;Lu;0;L;;;;;N;;;;ABB5; 13E6;CHEROKEE LETTER TSO;Lu;0;L;;;;;N;;;;ABB6; 13E7;CHEROKEE LETTER TSU;Lu;0;L;;;;;N;;;;ABB7; 13E8;CHEROKEE LETTER TSV;Lu;0;L;;;;;N;;;;ABB8; 13E9;CHEROKEE LETTER WA;Lu;0;L;;;;;N;;;;ABB9; 13EA;CHEROKEE LETTER WE;Lu;0;L;;;;;N;;;;ABBA; 13EB;CHEROKEE LETTER WI;Lu;0;L;;;;;N;;;;ABBB; 13EC;CHEROKEE LETTER WO;Lu;0;L;;;;;N;;;;ABBC; 13ED;CHEROKEE LETTER WU;Lu;0;L;;;;;N;;;;ABBD; 13EE;CHEROKEE LETTER WV;Lu;0;L;;;;;N;;;;ABBE; 13EF;CHEROKEE LETTER YA;Lu;0;L;;;;;N;;;;ABBF; 13F0;CHEROKEE LETTER YE;Lu;0;L;;;;;N;;;;13F8; 13F1;CHEROKEE LETTER YI;Lu;0;L;;;;;N;;;;13F9; 13F2;CHEROKEE LETTER YO;Lu;0;L;;;;;N;;;;13FA; 13F3;CHEROKEE LETTER YU;Lu;0;L;;;;;N;;;;13FB; 13F4;CHEROKEE LETTER YV;Lu;0;L;;;;;N;;;;13FC; 13F5;CHEROKEE LETTER MV;Lu;0;L;;;;;N;;;;13FD; 13F8;CHEROKEE SMALL LETTER YE;Ll;0;L;;;;;N;;;13F0;;13F0 13F9;CHEROKEE SMALL LETTER YI;Ll;0;L;;;;;N;;;13F1;;13F1 13FA;CHEROKEE SMALL LETTER YO;Ll;0;L;;;;;N;;;13F2;;13F2 13FB;CHEROKEE SMALL LETTER YU;Ll;0;L;;;;;N;;;13F3;;13F3 13FC;CHEROKEE SMALL LETTER YV;Ll;0;L;;;;;N;;;13F4;;13F4 13FD;CHEROKEE SMALL LETTER MV;Ll;0;L;;;;;N;;;13F5;;13F5 1400;CANADIAN SYLLABICS HYPHEN;Pd;0;ON;;;;;N;;;;; 1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;; 1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;; 1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;; 1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;; 1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;; 1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;; 1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;; 1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;; 1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;; 140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;; 140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;; 140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;; 140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;; 140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;; 140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;; 1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;; 1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;; 1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;; 1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;; 1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;; 1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;; 1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;; 1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;; 1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;; 1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;; 141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;; 141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;; 141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;; 141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;; 141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;; 141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;; 1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;; 1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;; 1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;; 1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;; 1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;; 1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;; 1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;; 1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;; 1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;; 1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;; 142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;; 142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;; 142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;; 142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;; 142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;; 142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;; 1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;; 1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;; 1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;; 1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;; 1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;; 1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;; 1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;; 1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;; 1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;; 1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;; 143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;; 143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;; 143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;; 143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;; 143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;; 143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;; 1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;; 1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;; 1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;; 1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;; 1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;; 1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;; 1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;; 1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;; 1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;; 1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;; 144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;; 144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;; 144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;; 144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;; 144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;; 144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;; 1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;; 1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;; 1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;; 1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;; 1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;; 1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;; 1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;; 1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;; 1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;; 1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;; 145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;; 145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;; 145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;; 145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;; 145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;; 145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;; 1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;; 1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;; 1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;; 1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;; 1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;; 1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;; 1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;; 1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;; 1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;; 1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;; 146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;; 146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;; 146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;; 146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;; 146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;; 146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;; 1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;; 1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;; 1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;; 1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;; 1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;; 1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;; 1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;; 1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;; 1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;; 1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;; 147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;; 147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;; 147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;; 147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;; 147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;; 147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;; 1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;; 1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;; 1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;; 1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;; 1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;; 1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;; 1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;; 1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;; 1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;; 1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;; 148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;; 148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;; 148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;; 148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;; 148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;; 148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;; 1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;; 1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;; 1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;; 1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;; 1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;; 1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;; 1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;; 1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;; 1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;; 1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;; 149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;; 149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;; 149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;; 149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;; 149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;; 149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;; 14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;; 14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;; 14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;; 14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;; 14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;; 14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;; 14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;; 14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;; 14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;; 14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;; 14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;; 14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;; 14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;; 14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;; 14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;; 14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;; 14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;; 14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;; 14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;; 14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;; 14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;; 14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;; 14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;; 14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;; 14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;; 14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;; 14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;; 14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;; 14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;; 14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;; 14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;; 14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;; 14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;; 14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;; 14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;; 14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;; 14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;; 14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;; 14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;; 14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;; 14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;; 14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;; 14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;; 14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;; 14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;; 14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;; 14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;; 14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;; 14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;; 14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;; 14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;; 14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;; 14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;; 14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;; 14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;; 14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;; 14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;; 14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;; 14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;; 14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;; 14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;; 14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;; 14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;; 14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;; 14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;; 14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;; 14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;; 14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;; 14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;; 14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;; 14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;; 14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;; 14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;; 14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;; 14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;; 14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;; 14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;; 14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;; 14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;; 14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;; 14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;; 14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;; 14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;; 14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;; 14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;; 14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;; 14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;; 14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;; 14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;; 14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;; 14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;; 14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;; 14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;; 14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;; 14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;; 14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;; 1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;; 1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;; 1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;; 1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;; 1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;; 1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;; 1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;; 1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;; 1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;; 1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;; 150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;; 150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;; 150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;; 150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;; 150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;; 150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;; 1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;; 1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;; 1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;; 1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;; 1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;; 1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;; 1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;; 1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;; 1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;; 1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;; 151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;; 151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;; 151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;; 151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;; 151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;; 151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;; 1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;; 1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;; 1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;; 1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;; 1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;; 1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;; 1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;; 1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;; 1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;; 1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;; 152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;; 152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;; 152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;; 152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;; 152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;; 152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;; 1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;; 1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;; 1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;; 1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;; 1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;; 1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;; 1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;; 1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;; 1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;; 1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;; 153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;; 153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;; 153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;; 153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;; 153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;; 153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;; 1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;; 1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;; 1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;; 1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;; 1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;; 1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;; 1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;; 1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;; 1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;; 1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;; 154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;; 154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;; 154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;; 154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;; 154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;; 154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;; 1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;; 1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;; 1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;; 1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;; 1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;; 1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;; 1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;; 1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;; 1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;; 1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;; 155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;; 155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;; 155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;; 155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;; 155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;; 155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;; 1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;; 1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;; 1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;; 1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;; 1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;; 1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;; 1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;; 1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;; 1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;; 1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;; 156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;; 156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;; 156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;; 156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;; 156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;; 156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;; 1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;; 1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;; 1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;; 1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;; 1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;; 1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;; 1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;; 1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;; 1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;; 1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;; 157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;; 157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;; 157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;; 157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;; 157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;; 157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;; 1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;; 1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;; 1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;; 1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;; 1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;; 1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;; 1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;; 1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;; 1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;; 1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;; 158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;; 158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;; 158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;; 158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;; 158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;; 158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;; 1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;; 1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;; 1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;; 1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;; 1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;; 1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;; 1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;; 1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;; 1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;; 1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;; 159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;; 159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;; 159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;; 159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;; 159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;; 159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;; 15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;; 15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;; 15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;; 15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;; 15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;; 15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;; 15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;; 15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;; 15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;; 15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;; 15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;; 15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;; 15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;; 15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;; 15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;; 15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;; 15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;; 15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;; 15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;; 15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;; 15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;; 15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;; 15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;; 15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;; 15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;; 15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;; 15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;; 15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;; 15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;; 15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;; 15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;; 15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;; 15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;; 15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;; 15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;; 15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;; 15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;; 15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;; 15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;; 15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;; 15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;; 15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;; 15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;; 15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;; 15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;; 15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;; 15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;; 15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;; 15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;; 15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;; 15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;; 15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;; 15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;; 15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;; 15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;; 15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;; 15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;; 15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;; 15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;; 15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;; 15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;; 15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;; 15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;; 15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;; 15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;; 15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;; 15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;; 15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;; 15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;; 15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;; 15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;; 15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;; 15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;; 15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;; 15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;; 15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;; 15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;; 15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;; 15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;; 15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;; 15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;; 15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;; 15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;; 15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;; 15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;; 15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;; 15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;; 15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;; 15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;; 15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;; 15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;; 15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;; 15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;; 15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;; 15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;; 15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;; 1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;; 1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;; 1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;; 1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;; 1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;; 1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;; 1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;; 1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;; 1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;; 1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;; 160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;; 160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;; 160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;; 160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;; 160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;; 160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;; 1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;; 1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;; 1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;; 1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;; 1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;; 1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;; 1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;; 1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;; 1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;; 1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;; 161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;; 161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;; 161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;; 161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;; 161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;; 161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;; 1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;; 1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;; 1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;; 1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;; 1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;; 1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;; 1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;; 1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;; 1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;; 1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;; 162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;; 162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;; 162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;; 162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;; 162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;; 162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;; 1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;; 1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;; 1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;; 1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;; 1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;; 1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;; 1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;; 1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;; 1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;; 1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;; 163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;; 163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;; 163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;; 163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;; 163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;; 163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;; 1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;; 1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;; 1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;; 1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;; 1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;; 1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;; 1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;; 1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;; 1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;; 1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;; 164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;; 164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;; 164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;; 164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;; 164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;; 164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;; 1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;; 1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;; 1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;; 1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;; 1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;; 1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;; 1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;; 1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;; 1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;; 1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;; 165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;; 165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;; 165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;; 165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;; 165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;; 165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;; 1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;; 1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;; 1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;; 1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;; 1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;; 1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;; 1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;; 1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;; 1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;; 1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;; 166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;; 166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;; 166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;; 166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;; 166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;; 166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;; 1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;; 1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;; 1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;; 1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;; 1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;; 1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;; 1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;; 1677;CANADIAN SYLLABICS WOODS-CREE THWEE;Lo;0;L;;;;;N;;;;; 1678;CANADIAN SYLLABICS WOODS-CREE THWI;Lo;0;L;;;;;N;;;;; 1679;CANADIAN SYLLABICS WOODS-CREE THWII;Lo;0;L;;;;;N;;;;; 167A;CANADIAN SYLLABICS WOODS-CREE THWO;Lo;0;L;;;;;N;;;;; 167B;CANADIAN SYLLABICS WOODS-CREE THWOO;Lo;0;L;;;;;N;;;;; 167C;CANADIAN SYLLABICS WOODS-CREE THWA;Lo;0;L;;;;;N;;;;; 167D;CANADIAN SYLLABICS WOODS-CREE THWAA;Lo;0;L;;;;;N;;;;; 167E;CANADIAN SYLLABICS WOODS-CREE FINAL TH;Lo;0;L;;;;;N;;;;; 167F;CANADIAN SYLLABICS BLACKFOOT W;Lo;0;L;;;;;N;;;;; 1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;; 1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;; 1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;; 1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;; 1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;; 1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;; 1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;; 1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;; 1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;; 1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;; 168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;; 168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;; 168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;; 168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;; 168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;; 168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;; 1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;; 1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;; 1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;; 1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;; 1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;; 1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;; 1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;; 1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;; 1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;; 1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;; 169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;; 169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;Y;;;;; 169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;Y;;;;; 16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;; 16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;; 16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;; 16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;; 16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;; 16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;; 16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;; 16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;; 16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;; 16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;; 16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;; 16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;; 16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;; 16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;; 16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;; 16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;; 16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;; 16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;; 16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;; 16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;; 16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;; 16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;; 16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;; 16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;; 16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;; 16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;; 16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;; 16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;; 16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;; 16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;; 16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;; 16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;; 16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;; 16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;; 16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;; 16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;; 16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;; 16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;; 16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;; 16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;; 16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;; 16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;; 16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;; 16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;; 16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;; 16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;; 16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;; 16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;; 16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;; 16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;; 16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;; 16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;; 16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;; 16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;; 16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;; 16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;; 16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;; 16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;; 16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;; 16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;; 16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;; 16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;; 16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;; 16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;; 16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;; 16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;; 16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;; 16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;; 16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;; 16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;; 16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;; 16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;; 16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;; 16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;; 16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;; 16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; 16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;; 16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;; 16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;;;; 16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;;;; 16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;;;; 16F1;RUNIC LETTER K;Lo;0;L;;;;;N;;;;; 16F2;RUNIC LETTER SH;Lo;0;L;;;;;N;;;;; 16F3;RUNIC LETTER OO;Lo;0;L;;;;;N;;;;; 16F4;RUNIC LETTER FRANKS CASKET OS;Lo;0;L;;;;;N;;;;; 16F5;RUNIC LETTER FRANKS CASKET IS;Lo;0;L;;;;;N;;;;; 16F6;RUNIC LETTER FRANKS CASKET EH;Lo;0;L;;;;;N;;;;; 16F7;RUNIC LETTER FRANKS CASKET AC;Lo;0;L;;;;;N;;;;; 16F8;RUNIC LETTER FRANKS CASKET AESC;Lo;0;L;;;;;N;;;;; 1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;; 1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;; 1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;; 1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;; 1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;; 1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;; 1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;; 1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;; 1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;; 1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;; 170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;; 170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;; 170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;; 170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;; 170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;; 1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;; 1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;; 1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;; 1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;; 1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;; 1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;; 1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;; 1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;; 1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;; 1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;; 1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;; 1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;; 172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;; 172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;; 172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;; 172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;; 172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;; 172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;; 1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;; 1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;; 1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;; 1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; 1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;; 1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;; 1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;; 1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;; 1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;; 1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;; 1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;; 1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;; 1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;; 1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;; 1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;; 174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;; 174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;; 174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;; 174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;; 174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;; 174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;; 1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;; 1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;; 1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;; 1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;; 1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;; 1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;; 1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;; 1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;; 1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;; 1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;; 1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;; 1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;; 176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;; 176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;; 176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;; 176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;; 176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;; 1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;; 1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;; 1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;; 1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;; 1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;; 1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;; 1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;; 1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;; 1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;; 1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;; 1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;; 178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;; 178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;; 178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;; 178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;; 178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;; 178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;; 1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;; 1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;; 1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;; 1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;; 1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;; 1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;; 1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;; 1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;; 1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;; 1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;; 179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;; 179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;; 179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;; 179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;; 179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;; 179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;; 17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;; 17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;; 17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;; 17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;; 17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;; 17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;; 17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;; 17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;; 17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;; 17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;; 17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;; 17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;; 17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;; 17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;; 17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;; 17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;; 17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;; 17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;; 17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;; 17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;; 17B4;KHMER VOWEL INHERENT AQ;Mn;0;NSM;;;;;N;;;;; 17B5;KHMER VOWEL INHERENT AA;Mn;0;NSM;;;;;N;;;;; 17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; 17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; 17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;; 17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;; 17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;; 17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; 17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; 17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; 17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;; 17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;; 17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;; 17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;; 17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;; 17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;; 17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;; 17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;; 17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;; 17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;; 17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;; 17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;; 17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;; 17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;; 17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;; 17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;; 17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;; 17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;; 17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;; 17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;; 17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;; 17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;; 17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;; 17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;; 17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 17F0;KHMER SYMBOL LEK ATTAK SON;No;0;ON;;;;0;N;;;;; 17F1;KHMER SYMBOL LEK ATTAK MUOY;No;0;ON;;;;1;N;;;;; 17F2;KHMER SYMBOL LEK ATTAK PII;No;0;ON;;;;2;N;;;;; 17F3;KHMER SYMBOL LEK ATTAK BEI;No;0;ON;;;;3;N;;;;; 17F4;KHMER SYMBOL LEK ATTAK BUON;No;0;ON;;;;4;N;;;;; 17F5;KHMER SYMBOL LEK ATTAK PRAM;No;0;ON;;;;5;N;;;;; 17F6;KHMER SYMBOL LEK ATTAK PRAM-MUOY;No;0;ON;;;;6;N;;;;; 17F7;KHMER SYMBOL LEK ATTAK PRAM-PII;No;0;ON;;;;7;N;;;;; 17F8;KHMER SYMBOL LEK ATTAK PRAM-BEI;No;0;ON;;;;8;N;;;;; 17F9;KHMER SYMBOL LEK ATTAK PRAM-BUON;No;0;ON;;;;9;N;;;;; 1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;; 1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;; 1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;; 1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;; 1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;; 1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;; 1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;; 1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;; 1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;; 1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;; 180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;; 180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;; 180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;; 180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;; 180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;; 1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;; 1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;; 1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;; 1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;; 1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;; 1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;; 1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;; 1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;; 1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;; 1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;; 182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;; 182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;; 182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;; 182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;; 182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;; 182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;; 1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;; 1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;; 1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;; 1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;; 1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;; 1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;; 1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;; 1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;; 1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;; 1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;; 183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;; 183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;; 183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;; 183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;; 183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;; 183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;; 1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;; 1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;; 1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;; 1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;; 1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;; 1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;; 1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;; 1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;; 1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;; 1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;; 184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;; 184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;; 184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;; 184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;; 184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;; 184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;; 1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;; 1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;; 1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;; 1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;; 1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;; 1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;; 1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;; 1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;; 1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;; 1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;; 185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;; 185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;; 185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;; 185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;; 185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;; 185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;; 1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;; 1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;; 1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;; 1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;; 1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;; 1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;; 1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;; 1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;; 1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;; 1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;; 186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;; 186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;; 186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;; 186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;; 186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;; 186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;; 1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;; 1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;; 1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;; 1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;; 1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;; 1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;; 1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;; 1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;; 1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;; 1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;; 1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;; 1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;; 1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;; 1885;MONGOLIAN LETTER ALI GALI BALUDA;Mn;0;NSM;;;;;N;;;;; 1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Mn;0;NSM;;;;;N;;;;; 1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;; 1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;; 1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;; 188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;; 188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;; 188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;; 188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;; 188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;; 188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;; 1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;; 1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;; 1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;; 1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;; 1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;; 1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;; 1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;; 1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;; 1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;; 1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;; 189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;; 189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;; 189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;; 189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;; 189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;; 189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;; 18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;; 18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;; 18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;; 18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;; 18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;; 18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;; 18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;; 18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;; 18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;; 18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;; 18AA;MONGOLIAN LETTER MANCHU ALI GALI LHA;Lo;0;L;;;;;N;;;;; 18B0;CANADIAN SYLLABICS OY;Lo;0;L;;;;;N;;;;; 18B1;CANADIAN SYLLABICS AY;Lo;0;L;;;;;N;;;;; 18B2;CANADIAN SYLLABICS AAY;Lo;0;L;;;;;N;;;;; 18B3;CANADIAN SYLLABICS WAY;Lo;0;L;;;;;N;;;;; 18B4;CANADIAN SYLLABICS POY;Lo;0;L;;;;;N;;;;; 18B5;CANADIAN SYLLABICS PAY;Lo;0;L;;;;;N;;;;; 18B6;CANADIAN SYLLABICS PWOY;Lo;0;L;;;;;N;;;;; 18B7;CANADIAN SYLLABICS TAY;Lo;0;L;;;;;N;;;;; 18B8;CANADIAN SYLLABICS KAY;Lo;0;L;;;;;N;;;;; 18B9;CANADIAN SYLLABICS KWAY;Lo;0;L;;;;;N;;;;; 18BA;CANADIAN SYLLABICS MAY;Lo;0;L;;;;;N;;;;; 18BB;CANADIAN SYLLABICS NOY;Lo;0;L;;;;;N;;;;; 18BC;CANADIAN SYLLABICS NAY;Lo;0;L;;;;;N;;;;; 18BD;CANADIAN SYLLABICS LAY;Lo;0;L;;;;;N;;;;; 18BE;CANADIAN SYLLABICS SOY;Lo;0;L;;;;;N;;;;; 18BF;CANADIAN SYLLABICS SAY;Lo;0;L;;;;;N;;;;; 18C0;CANADIAN SYLLABICS SHOY;Lo;0;L;;;;;N;;;;; 18C1;CANADIAN SYLLABICS SHAY;Lo;0;L;;;;;N;;;;; 18C2;CANADIAN SYLLABICS SHWOY;Lo;0;L;;;;;N;;;;; 18C3;CANADIAN SYLLABICS YOY;Lo;0;L;;;;;N;;;;; 18C4;CANADIAN SYLLABICS YAY;Lo;0;L;;;;;N;;;;; 18C5;CANADIAN SYLLABICS RAY;Lo;0;L;;;;;N;;;;; 18C6;CANADIAN SYLLABICS NWI;Lo;0;L;;;;;N;;;;; 18C7;CANADIAN SYLLABICS OJIBWAY NWI;Lo;0;L;;;;;N;;;;; 18C8;CANADIAN SYLLABICS NWII;Lo;0;L;;;;;N;;;;; 18C9;CANADIAN SYLLABICS OJIBWAY NWII;Lo;0;L;;;;;N;;;;; 18CA;CANADIAN SYLLABICS NWO;Lo;0;L;;;;;N;;;;; 18CB;CANADIAN SYLLABICS OJIBWAY NWO;Lo;0;L;;;;;N;;;;; 18CC;CANADIAN SYLLABICS NWOO;Lo;0;L;;;;;N;;;;; 18CD;CANADIAN SYLLABICS OJIBWAY NWOO;Lo;0;L;;;;;N;;;;; 18CE;CANADIAN SYLLABICS RWEE;Lo;0;L;;;;;N;;;;; 18CF;CANADIAN SYLLABICS RWI;Lo;0;L;;;;;N;;;;; 18D0;CANADIAN SYLLABICS RWII;Lo;0;L;;;;;N;;;;; 18D1;CANADIAN SYLLABICS RWO;Lo;0;L;;;;;N;;;;; 18D2;CANADIAN SYLLABICS RWOO;Lo;0;L;;;;;N;;;;; 18D3;CANADIAN SYLLABICS RWA;Lo;0;L;;;;;N;;;;; 18D4;CANADIAN SYLLABICS OJIBWAY P;Lo;0;L;;;;;N;;;;; 18D5;CANADIAN SYLLABICS OJIBWAY T;Lo;0;L;;;;;N;;;;; 18D6;CANADIAN SYLLABICS OJIBWAY K;Lo;0;L;;;;;N;;;;; 18D7;CANADIAN SYLLABICS OJIBWAY C;Lo;0;L;;;;;N;;;;; 18D8;CANADIAN SYLLABICS OJIBWAY M;Lo;0;L;;;;;N;;;;; 18D9;CANADIAN SYLLABICS OJIBWAY N;Lo;0;L;;;;;N;;;;; 18DA;CANADIAN SYLLABICS OJIBWAY S;Lo;0;L;;;;;N;;;;; 18DB;CANADIAN SYLLABICS OJIBWAY SH;Lo;0;L;;;;;N;;;;; 18DC;CANADIAN SYLLABICS EASTERN W;Lo;0;L;;;;;N;;;;; 18DD;CANADIAN SYLLABICS WESTERN W;Lo;0;L;;;;;N;;;;; 18DE;CANADIAN SYLLABICS FINAL SMALL RING;Lo;0;L;;;;;N;;;;; 18DF;CANADIAN SYLLABICS FINAL RAISED DOT;Lo;0;L;;;;;N;;;;; 18E0;CANADIAN SYLLABICS R-CREE RWE;Lo;0;L;;;;;N;;;;; 18E1;CANADIAN SYLLABICS WEST-CREE LOO;Lo;0;L;;;;;N;;;;; 18E2;CANADIAN SYLLABICS WEST-CREE LAA;Lo;0;L;;;;;N;;;;; 18E3;CANADIAN SYLLABICS THWE;Lo;0;L;;;;;N;;;;; 18E4;CANADIAN SYLLABICS THWA;Lo;0;L;;;;;N;;;;; 18E5;CANADIAN SYLLABICS TTHWE;Lo;0;L;;;;;N;;;;; 18E6;CANADIAN SYLLABICS TTHOO;Lo;0;L;;;;;N;;;;; 18E7;CANADIAN SYLLABICS TTHAA;Lo;0;L;;;;;N;;;;; 18E8;CANADIAN SYLLABICS TLHWE;Lo;0;L;;;;;N;;;;; 18E9;CANADIAN SYLLABICS TLHOO;Lo;0;L;;;;;N;;;;; 18EA;CANADIAN SYLLABICS SAYISI SHWE;Lo;0;L;;;;;N;;;;; 18EB;CANADIAN SYLLABICS SAYISI SHOO;Lo;0;L;;;;;N;;;;; 18EC;CANADIAN SYLLABICS SAYISI HOO;Lo;0;L;;;;;N;;;;; 18ED;CANADIAN SYLLABICS CARRIER GWU;Lo;0;L;;;;;N;;;;; 18EE;CANADIAN SYLLABICS CARRIER DENE GEE;Lo;0;L;;;;;N;;;;; 18EF;CANADIAN SYLLABICS CARRIER GAA;Lo;0;L;;;;;N;;;;; 18F0;CANADIAN SYLLABICS CARRIER GWA;Lo;0;L;;;;;N;;;;; 18F1;CANADIAN SYLLABICS SAYISI JUU;Lo;0;L;;;;;N;;;;; 18F2;CANADIAN SYLLABICS CARRIER JWA;Lo;0;L;;;;;N;;;;; 18F3;CANADIAN SYLLABICS BEAVER DENE L;Lo;0;L;;;;;N;;;;; 18F4;CANADIAN SYLLABICS BEAVER DENE R;Lo;0;L;;;;;N;;;;; 18F5;CANADIAN SYLLABICS CARRIER DENTAL S;Lo;0;L;;;;;N;;;;; 1900;LIMBU VOWEL-CARRIER LETTER;Lo;0;L;;;;;N;;;;; 1901;LIMBU LETTER KA;Lo;0;L;;;;;N;;;;; 1902;LIMBU LETTER KHA;Lo;0;L;;;;;N;;;;; 1903;LIMBU LETTER GA;Lo;0;L;;;;;N;;;;; 1904;LIMBU LETTER GHA;Lo;0;L;;;;;N;;;;; 1905;LIMBU LETTER NGA;Lo;0;L;;;;;N;;;;; 1906;LIMBU LETTER CA;Lo;0;L;;;;;N;;;;; 1907;LIMBU LETTER CHA;Lo;0;L;;;;;N;;;;; 1908;LIMBU LETTER JA;Lo;0;L;;;;;N;;;;; 1909;LIMBU LETTER JHA;Lo;0;L;;;;;N;;;;; 190A;LIMBU LETTER YAN;Lo;0;L;;;;;N;;;;; 190B;LIMBU LETTER TA;Lo;0;L;;;;;N;;;;; 190C;LIMBU LETTER THA;Lo;0;L;;;;;N;;;;; 190D;LIMBU LETTER DA;Lo;0;L;;;;;N;;;;; 190E;LIMBU LETTER DHA;Lo;0;L;;;;;N;;;;; 190F;LIMBU LETTER NA;Lo;0;L;;;;;N;;;;; 1910;LIMBU LETTER PA;Lo;0;L;;;;;N;;;;; 1911;LIMBU LETTER PHA;Lo;0;L;;;;;N;;;;; 1912;LIMBU LETTER BA;Lo;0;L;;;;;N;;;;; 1913;LIMBU LETTER BHA;Lo;0;L;;;;;N;;;;; 1914;LIMBU LETTER MA;Lo;0;L;;;;;N;;;;; 1915;LIMBU LETTER YA;Lo;0;L;;;;;N;;;;; 1916;LIMBU LETTER RA;Lo;0;L;;;;;N;;;;; 1917;LIMBU LETTER LA;Lo;0;L;;;;;N;;;;; 1918;LIMBU LETTER WA;Lo;0;L;;;;;N;;;;; 1919;LIMBU LETTER SHA;Lo;0;L;;;;;N;;;;; 191A;LIMBU LETTER SSA;Lo;0;L;;;;;N;;;;; 191B;LIMBU LETTER SA;Lo;0;L;;;;;N;;;;; 191C;LIMBU LETTER HA;Lo;0;L;;;;;N;;;;; 191D;LIMBU LETTER GYAN;Lo;0;L;;;;;N;;;;; 191E;LIMBU LETTER TRA;Lo;0;L;;;;;N;;;;; 1920;LIMBU VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;; 1921;LIMBU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1922;LIMBU VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1923;LIMBU VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; 1924;LIMBU VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 1925;LIMBU VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; 1926;LIMBU VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 1927;LIMBU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 1928;LIMBU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 1929;LIMBU SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; 192A;LIMBU SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;; 192B;LIMBU SUBJOINED LETTER WA;Mc;0;L;;;;;N;;;;; 1930;LIMBU SMALL LETTER KA;Mc;0;L;;;;;N;;;;; 1931;LIMBU SMALL LETTER NGA;Mc;0;L;;;;;N;;;;; 1932;LIMBU SMALL LETTER ANUSVARA;Mn;0;NSM;;;;;N;;;;; 1933;LIMBU SMALL LETTER TA;Mc;0;L;;;;;N;;;;; 1934;LIMBU SMALL LETTER NA;Mc;0;L;;;;;N;;;;; 1935;LIMBU SMALL LETTER PA;Mc;0;L;;;;;N;;;;; 1936;LIMBU SMALL LETTER MA;Mc;0;L;;;;;N;;;;; 1937;LIMBU SMALL LETTER RA;Mc;0;L;;;;;N;;;;; 1938;LIMBU SMALL LETTER LA;Mc;0;L;;;;;N;;;;; 1939;LIMBU SIGN MUKPHRENG;Mn;222;NSM;;;;;N;;;;; 193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;; 193B;LIMBU SIGN SA-I;Mn;220;NSM;;;;;N;;;;; 1940;LIMBU SIGN LOO;So;0;ON;;;;;N;;;;; 1944;LIMBU EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; 1945;LIMBU QUESTION MARK;Po;0;ON;;;;;N;;;;; 1946;LIMBU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1947;LIMBU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1948;LIMBU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1949;LIMBU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 194A;LIMBU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 194B;LIMBU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 194C;LIMBU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 194D;LIMBU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 194E;LIMBU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 194F;LIMBU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1950;TAI LE LETTER KA;Lo;0;L;;;;;N;;;;; 1951;TAI LE LETTER XA;Lo;0;L;;;;;N;;;;; 1952;TAI LE LETTER NGA;Lo;0;L;;;;;N;;;;; 1953;TAI LE LETTER TSA;Lo;0;L;;;;;N;;;;; 1954;TAI LE LETTER SA;Lo;0;L;;;;;N;;;;; 1955;TAI LE LETTER YA;Lo;0;L;;;;;N;;;;; 1956;TAI LE LETTER TA;Lo;0;L;;;;;N;;;;; 1957;TAI LE LETTER THA;Lo;0;L;;;;;N;;;;; 1958;TAI LE LETTER LA;Lo;0;L;;;;;N;;;;; 1959;TAI LE LETTER PA;Lo;0;L;;;;;N;;;;; 195A;TAI LE LETTER PHA;Lo;0;L;;;;;N;;;;; 195B;TAI LE LETTER MA;Lo;0;L;;;;;N;;;;; 195C;TAI LE LETTER FA;Lo;0;L;;;;;N;;;;; 195D;TAI LE LETTER VA;Lo;0;L;;;;;N;;;;; 195E;TAI LE LETTER HA;Lo;0;L;;;;;N;;;;; 195F;TAI LE LETTER QA;Lo;0;L;;;;;N;;;;; 1960;TAI LE LETTER KHA;Lo;0;L;;;;;N;;;;; 1961;TAI LE LETTER TSHA;Lo;0;L;;;;;N;;;;; 1962;TAI LE LETTER NA;Lo;0;L;;;;;N;;;;; 1963;TAI LE LETTER A;Lo;0;L;;;;;N;;;;; 1964;TAI LE LETTER I;Lo;0;L;;;;;N;;;;; 1965;TAI LE LETTER EE;Lo;0;L;;;;;N;;;;; 1966;TAI LE LETTER EH;Lo;0;L;;;;;N;;;;; 1967;TAI LE LETTER U;Lo;0;L;;;;;N;;;;; 1968;TAI LE LETTER OO;Lo;0;L;;;;;N;;;;; 1969;TAI LE LETTER O;Lo;0;L;;;;;N;;;;; 196A;TAI LE LETTER UE;Lo;0;L;;;;;N;;;;; 196B;TAI LE LETTER E;Lo;0;L;;;;;N;;;;; 196C;TAI LE LETTER AUE;Lo;0;L;;;;;N;;;;; 196D;TAI LE LETTER AI;Lo;0;L;;;;;N;;;;; 1970;TAI LE LETTER TONE-2;Lo;0;L;;;;;N;;;;; 1971;TAI LE LETTER TONE-3;Lo;0;L;;;;;N;;;;; 1972;TAI LE LETTER TONE-4;Lo;0;L;;;;;N;;;;; 1973;TAI LE LETTER TONE-5;Lo;0;L;;;;;N;;;;; 1974;TAI LE LETTER TONE-6;Lo;0;L;;;;;N;;;;; 1980;NEW TAI LUE LETTER HIGH QA;Lo;0;L;;;;;N;;;;; 1981;NEW TAI LUE LETTER LOW QA;Lo;0;L;;;;;N;;;;; 1982;NEW TAI LUE LETTER HIGH KA;Lo;0;L;;;;;N;;;;; 1983;NEW TAI LUE LETTER HIGH XA;Lo;0;L;;;;;N;;;;; 1984;NEW TAI LUE LETTER HIGH NGA;Lo;0;L;;;;;N;;;;; 1985;NEW TAI LUE LETTER LOW KA;Lo;0;L;;;;;N;;;;; 1986;NEW TAI LUE LETTER LOW XA;Lo;0;L;;;;;N;;;;; 1987;NEW TAI LUE LETTER LOW NGA;Lo;0;L;;;;;N;;;;; 1988;NEW TAI LUE LETTER HIGH TSA;Lo;0;L;;;;;N;;;;; 1989;NEW TAI LUE LETTER HIGH SA;Lo;0;L;;;;;N;;;;; 198A;NEW TAI LUE LETTER HIGH YA;Lo;0;L;;;;;N;;;;; 198B;NEW TAI LUE LETTER LOW TSA;Lo;0;L;;;;;N;;;;; 198C;NEW TAI LUE LETTER LOW SA;Lo;0;L;;;;;N;;;;; 198D;NEW TAI LUE LETTER LOW YA;Lo;0;L;;;;;N;;;;; 198E;NEW TAI LUE LETTER HIGH TA;Lo;0;L;;;;;N;;;;; 198F;NEW TAI LUE LETTER HIGH THA;Lo;0;L;;;;;N;;;;; 1990;NEW TAI LUE LETTER HIGH NA;Lo;0;L;;;;;N;;;;; 1991;NEW TAI LUE LETTER LOW TA;Lo;0;L;;;;;N;;;;; 1992;NEW TAI LUE LETTER LOW THA;Lo;0;L;;;;;N;;;;; 1993;NEW TAI LUE LETTER LOW NA;Lo;0;L;;;;;N;;;;; 1994;NEW TAI LUE LETTER HIGH PA;Lo;0;L;;;;;N;;;;; 1995;NEW TAI LUE LETTER HIGH PHA;Lo;0;L;;;;;N;;;;; 1996;NEW TAI LUE LETTER HIGH MA;Lo;0;L;;;;;N;;;;; 1997;NEW TAI LUE LETTER LOW PA;Lo;0;L;;;;;N;;;;; 1998;NEW TAI LUE LETTER LOW PHA;Lo;0;L;;;;;N;;;;; 1999;NEW TAI LUE LETTER LOW MA;Lo;0;L;;;;;N;;;;; 199A;NEW TAI LUE LETTER HIGH FA;Lo;0;L;;;;;N;;;;; 199B;NEW TAI LUE LETTER HIGH VA;Lo;0;L;;;;;N;;;;; 199C;NEW TAI LUE LETTER HIGH LA;Lo;0;L;;;;;N;;;;; 199D;NEW TAI LUE LETTER LOW FA;Lo;0;L;;;;;N;;;;; 199E;NEW TAI LUE LETTER LOW VA;Lo;0;L;;;;;N;;;;; 199F;NEW TAI LUE LETTER LOW LA;Lo;0;L;;;;;N;;;;; 19A0;NEW TAI LUE LETTER HIGH HA;Lo;0;L;;;;;N;;;;; 19A1;NEW TAI LUE LETTER HIGH DA;Lo;0;L;;;;;N;;;;; 19A2;NEW TAI LUE LETTER HIGH BA;Lo;0;L;;;;;N;;;;; 19A3;NEW TAI LUE LETTER LOW HA;Lo;0;L;;;;;N;;;;; 19A4;NEW TAI LUE LETTER LOW DA;Lo;0;L;;;;;N;;;;; 19A5;NEW TAI LUE LETTER LOW BA;Lo;0;L;;;;;N;;;;; 19A6;NEW TAI LUE LETTER HIGH KVA;Lo;0;L;;;;;N;;;;; 19A7;NEW TAI LUE LETTER HIGH XVA;Lo;0;L;;;;;N;;;;; 19A8;NEW TAI LUE LETTER LOW KVA;Lo;0;L;;;;;N;;;;; 19A9;NEW TAI LUE LETTER LOW XVA;Lo;0;L;;;;;N;;;;; 19AA;NEW TAI LUE LETTER HIGH SUA;Lo;0;L;;;;;N;;;;; 19AB;NEW TAI LUE LETTER LOW SUA;Lo;0;L;;;;;N;;;;; 19B0;NEW TAI LUE VOWEL SIGN VOWEL SHORTENER;Lo;0;L;;;;;N;;;;; 19B1;NEW TAI LUE VOWEL SIGN AA;Lo;0;L;;;;;N;;;;; 19B2;NEW TAI LUE VOWEL SIGN II;Lo;0;L;;;;;N;;;;; 19B3;NEW TAI LUE VOWEL SIGN U;Lo;0;L;;;;;N;;;;; 19B4;NEW TAI LUE VOWEL SIGN UU;Lo;0;L;;;;;N;;;;; 19B5;NEW TAI LUE VOWEL SIGN E;Lo;0;L;;;;;N;;;;; 19B6;NEW TAI LUE VOWEL SIGN AE;Lo;0;L;;;;;N;;;;; 19B7;NEW TAI LUE VOWEL SIGN O;Lo;0;L;;;;;N;;;;; 19B8;NEW TAI LUE VOWEL SIGN OA;Lo;0;L;;;;;N;;;;; 19B9;NEW TAI LUE VOWEL SIGN UE;Lo;0;L;;;;;N;;;;; 19BA;NEW TAI LUE VOWEL SIGN AY;Lo;0;L;;;;;N;;;;; 19BB;NEW TAI LUE VOWEL SIGN AAY;Lo;0;L;;;;;N;;;;; 19BC;NEW TAI LUE VOWEL SIGN UY;Lo;0;L;;;;;N;;;;; 19BD;NEW TAI LUE VOWEL SIGN OY;Lo;0;L;;;;;N;;;;; 19BE;NEW TAI LUE VOWEL SIGN OAY;Lo;0;L;;;;;N;;;;; 19BF;NEW TAI LUE VOWEL SIGN UEY;Lo;0;L;;;;;N;;;;; 19C0;NEW TAI LUE VOWEL SIGN IY;Lo;0;L;;;;;N;;;;; 19C1;NEW TAI LUE LETTER FINAL V;Lo;0;L;;;;;N;;;;; 19C2;NEW TAI LUE LETTER FINAL NG;Lo;0;L;;;;;N;;;;; 19C3;NEW TAI LUE LETTER FINAL N;Lo;0;L;;;;;N;;;;; 19C4;NEW TAI LUE LETTER FINAL M;Lo;0;L;;;;;N;;;;; 19C5;NEW TAI LUE LETTER FINAL K;Lo;0;L;;;;;N;;;;; 19C6;NEW TAI LUE LETTER FINAL D;Lo;0;L;;;;;N;;;;; 19C7;NEW TAI LUE LETTER FINAL B;Lo;0;L;;;;;N;;;;; 19C8;NEW TAI LUE TONE MARK-1;Lo;0;L;;;;;N;;;;; 19C9;NEW TAI LUE TONE MARK-2;Lo;0;L;;;;;N;;;;; 19D0;NEW TAI LUE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 19D1;NEW TAI LUE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 19D2;NEW TAI LUE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 19D3;NEW TAI LUE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 19D4;NEW TAI LUE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 19D5;NEW TAI LUE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 19D6;NEW TAI LUE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 19D7;NEW TAI LUE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 19D8;NEW TAI LUE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 19D9;NEW TAI LUE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 19DA;NEW TAI LUE THAM DIGIT ONE;No;0;L;;;1;1;N;;;;; 19DE;NEW TAI LUE SIGN LAE;So;0;ON;;;;;N;;;;; 19DF;NEW TAI LUE SIGN LAEV;So;0;ON;;;;;N;;;;; 19E0;KHMER SYMBOL PATHAMASAT;So;0;ON;;;;;N;;;;; 19E1;KHMER SYMBOL MUOY KOET;So;0;ON;;;;;N;;;;; 19E2;KHMER SYMBOL PII KOET;So;0;ON;;;;;N;;;;; 19E3;KHMER SYMBOL BEI KOET;So;0;ON;;;;;N;;;;; 19E4;KHMER SYMBOL BUON KOET;So;0;ON;;;;;N;;;;; 19E5;KHMER SYMBOL PRAM KOET;So;0;ON;;;;;N;;;;; 19E6;KHMER SYMBOL PRAM-MUOY KOET;So;0;ON;;;;;N;;;;; 19E7;KHMER SYMBOL PRAM-PII KOET;So;0;ON;;;;;N;;;;; 19E8;KHMER SYMBOL PRAM-BEI KOET;So;0;ON;;;;;N;;;;; 19E9;KHMER SYMBOL PRAM-BUON KOET;So;0;ON;;;;;N;;;;; 19EA;KHMER SYMBOL DAP KOET;So;0;ON;;;;;N;;;;; 19EB;KHMER SYMBOL DAP-MUOY KOET;So;0;ON;;;;;N;;;;; 19EC;KHMER SYMBOL DAP-PII KOET;So;0;ON;;;;;N;;;;; 19ED;KHMER SYMBOL DAP-BEI KOET;So;0;ON;;;;;N;;;;; 19EE;KHMER SYMBOL DAP-BUON KOET;So;0;ON;;;;;N;;;;; 19EF;KHMER SYMBOL DAP-PRAM KOET;So;0;ON;;;;;N;;;;; 19F0;KHMER SYMBOL TUTEYASAT;So;0;ON;;;;;N;;;;; 19F1;KHMER SYMBOL MUOY ROC;So;0;ON;;;;;N;;;;; 19F2;KHMER SYMBOL PII ROC;So;0;ON;;;;;N;;;;; 19F3;KHMER SYMBOL BEI ROC;So;0;ON;;;;;N;;;;; 19F4;KHMER SYMBOL BUON ROC;So;0;ON;;;;;N;;;;; 19F5;KHMER SYMBOL PRAM ROC;So;0;ON;;;;;N;;;;; 19F6;KHMER SYMBOL PRAM-MUOY ROC;So;0;ON;;;;;N;;;;; 19F7;KHMER SYMBOL PRAM-PII ROC;So;0;ON;;;;;N;;;;; 19F8;KHMER SYMBOL PRAM-BEI ROC;So;0;ON;;;;;N;;;;; 19F9;KHMER SYMBOL PRAM-BUON ROC;So;0;ON;;;;;N;;;;; 19FA;KHMER SYMBOL DAP ROC;So;0;ON;;;;;N;;;;; 19FB;KHMER SYMBOL DAP-MUOY ROC;So;0;ON;;;;;N;;;;; 19FC;KHMER SYMBOL DAP-PII ROC;So;0;ON;;;;;N;;;;; 19FD;KHMER SYMBOL DAP-BEI ROC;So;0;ON;;;;;N;;;;; 19FE;KHMER SYMBOL DAP-BUON ROC;So;0;ON;;;;;N;;;;; 19FF;KHMER SYMBOL DAP-PRAM ROC;So;0;ON;;;;;N;;;;; 1A00;BUGINESE LETTER KA;Lo;0;L;;;;;N;;;;; 1A01;BUGINESE LETTER GA;Lo;0;L;;;;;N;;;;; 1A02;BUGINESE LETTER NGA;Lo;0;L;;;;;N;;;;; 1A03;BUGINESE LETTER NGKA;Lo;0;L;;;;;N;;;;; 1A04;BUGINESE LETTER PA;Lo;0;L;;;;;N;;;;; 1A05;BUGINESE LETTER BA;Lo;0;L;;;;;N;;;;; 1A06;BUGINESE LETTER MA;Lo;0;L;;;;;N;;;;; 1A07;BUGINESE LETTER MPA;Lo;0;L;;;;;N;;;;; 1A08;BUGINESE LETTER TA;Lo;0;L;;;;;N;;;;; 1A09;BUGINESE LETTER DA;Lo;0;L;;;;;N;;;;; 1A0A;BUGINESE LETTER NA;Lo;0;L;;;;;N;;;;; 1A0B;BUGINESE LETTER NRA;Lo;0;L;;;;;N;;;;; 1A0C;BUGINESE LETTER CA;Lo;0;L;;;;;N;;;;; 1A0D;BUGINESE LETTER JA;Lo;0;L;;;;;N;;;;; 1A0E;BUGINESE LETTER NYA;Lo;0;L;;;;;N;;;;; 1A0F;BUGINESE LETTER NYCA;Lo;0;L;;;;;N;;;;; 1A10;BUGINESE LETTER YA;Lo;0;L;;;;;N;;;;; 1A11;BUGINESE LETTER RA;Lo;0;L;;;;;N;;;;; 1A12;BUGINESE LETTER LA;Lo;0;L;;;;;N;;;;; 1A13;BUGINESE LETTER VA;Lo;0;L;;;;;N;;;;; 1A14;BUGINESE LETTER SA;Lo;0;L;;;;;N;;;;; 1A15;BUGINESE LETTER A;Lo;0;L;;;;;N;;;;; 1A16;BUGINESE LETTER HA;Lo;0;L;;;;;N;;;;; 1A17;BUGINESE VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;; 1A18;BUGINESE VOWEL SIGN U;Mn;220;NSM;;;;;N;;;;; 1A19;BUGINESE VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 1A1A;BUGINESE VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 1A1B;BUGINESE VOWEL SIGN AE;Mn;0;NSM;;;;;N;;;;; 1A1E;BUGINESE PALLAWA;Po;0;L;;;;;N;;;;; 1A1F;BUGINESE END OF SECTION;Po;0;L;;;;;N;;;;; 1A20;TAI THAM LETTER HIGH KA;Lo;0;L;;;;;N;;;;; 1A21;TAI THAM LETTER HIGH KHA;Lo;0;L;;;;;N;;;;; 1A22;TAI THAM LETTER HIGH KXA;Lo;0;L;;;;;N;;;;; 1A23;TAI THAM LETTER LOW KA;Lo;0;L;;;;;N;;;;; 1A24;TAI THAM LETTER LOW KXA;Lo;0;L;;;;;N;;;;; 1A25;TAI THAM LETTER LOW KHA;Lo;0;L;;;;;N;;;;; 1A26;TAI THAM LETTER NGA;Lo;0;L;;;;;N;;;;; 1A27;TAI THAM LETTER HIGH CA;Lo;0;L;;;;;N;;;;; 1A28;TAI THAM LETTER HIGH CHA;Lo;0;L;;;;;N;;;;; 1A29;TAI THAM LETTER LOW CA;Lo;0;L;;;;;N;;;;; 1A2A;TAI THAM LETTER LOW SA;Lo;0;L;;;;;N;;;;; 1A2B;TAI THAM LETTER LOW CHA;Lo;0;L;;;;;N;;;;; 1A2C;TAI THAM LETTER NYA;Lo;0;L;;;;;N;;;;; 1A2D;TAI THAM LETTER RATA;Lo;0;L;;;;;N;;;;; 1A2E;TAI THAM LETTER HIGH RATHA;Lo;0;L;;;;;N;;;;; 1A2F;TAI THAM LETTER DA;Lo;0;L;;;;;N;;;;; 1A30;TAI THAM LETTER LOW RATHA;Lo;0;L;;;;;N;;;;; 1A31;TAI THAM LETTER RANA;Lo;0;L;;;;;N;;;;; 1A32;TAI THAM LETTER HIGH TA;Lo;0;L;;;;;N;;;;; 1A33;TAI THAM LETTER HIGH THA;Lo;0;L;;;;;N;;;;; 1A34;TAI THAM LETTER LOW TA;Lo;0;L;;;;;N;;;;; 1A35;TAI THAM LETTER LOW THA;Lo;0;L;;;;;N;;;;; 1A36;TAI THAM LETTER NA;Lo;0;L;;;;;N;;;;; 1A37;TAI THAM LETTER BA;Lo;0;L;;;;;N;;;;; 1A38;TAI THAM LETTER HIGH PA;Lo;0;L;;;;;N;;;;; 1A39;TAI THAM LETTER HIGH PHA;Lo;0;L;;;;;N;;;;; 1A3A;TAI THAM LETTER HIGH FA;Lo;0;L;;;;;N;;;;; 1A3B;TAI THAM LETTER LOW PA;Lo;0;L;;;;;N;;;;; 1A3C;TAI THAM LETTER LOW FA;Lo;0;L;;;;;N;;;;; 1A3D;TAI THAM LETTER LOW PHA;Lo;0;L;;;;;N;;;;; 1A3E;TAI THAM LETTER MA;Lo;0;L;;;;;N;;;;; 1A3F;TAI THAM LETTER LOW YA;Lo;0;L;;;;;N;;;;; 1A40;TAI THAM LETTER HIGH YA;Lo;0;L;;;;;N;;;;; 1A41;TAI THAM LETTER RA;Lo;0;L;;;;;N;;;;; 1A42;TAI THAM LETTER RUE;Lo;0;L;;;;;N;;;;; 1A43;TAI THAM LETTER LA;Lo;0;L;;;;;N;;;;; 1A44;TAI THAM LETTER LUE;Lo;0;L;;;;;N;;;;; 1A45;TAI THAM LETTER WA;Lo;0;L;;;;;N;;;;; 1A46;TAI THAM LETTER HIGH SHA;Lo;0;L;;;;;N;;;;; 1A47;TAI THAM LETTER HIGH SSA;Lo;0;L;;;;;N;;;;; 1A48;TAI THAM LETTER HIGH SA;Lo;0;L;;;;;N;;;;; 1A49;TAI THAM LETTER HIGH HA;Lo;0;L;;;;;N;;;;; 1A4A;TAI THAM LETTER LLA;Lo;0;L;;;;;N;;;;; 1A4B;TAI THAM LETTER A;Lo;0;L;;;;;N;;;;; 1A4C;TAI THAM LETTER LOW HA;Lo;0;L;;;;;N;;;;; 1A4D;TAI THAM LETTER I;Lo;0;L;;;;;N;;;;; 1A4E;TAI THAM LETTER II;Lo;0;L;;;;;N;;;;; 1A4F;TAI THAM LETTER U;Lo;0;L;;;;;N;;;;; 1A50;TAI THAM LETTER UU;Lo;0;L;;;;;N;;;;; 1A51;TAI THAM LETTER EE;Lo;0;L;;;;;N;;;;; 1A52;TAI THAM LETTER OO;Lo;0;L;;;;;N;;;;; 1A53;TAI THAM LETTER LAE;Lo;0;L;;;;;N;;;;; 1A54;TAI THAM LETTER GREAT SA;Lo;0;L;;;;;N;;;;; 1A55;TAI THAM CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;; 1A56;TAI THAM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;; 1A57;TAI THAM CONSONANT SIGN LA TANG LAI;Mc;0;L;;;;;N;;;;; 1A58;TAI THAM SIGN MAI KANG LAI;Mn;0;NSM;;;;;N;;;;; 1A59;TAI THAM CONSONANT SIGN FINAL NGA;Mn;0;NSM;;;;;N;;;;; 1A5A;TAI THAM CONSONANT SIGN LOW PA;Mn;0;NSM;;;;;N;;;;; 1A5B;TAI THAM CONSONANT SIGN HIGH RATHA OR LOW PA;Mn;0;NSM;;;;;N;;;;; 1A5C;TAI THAM CONSONANT SIGN MA;Mn;0;NSM;;;;;N;;;;; 1A5D;TAI THAM CONSONANT SIGN BA;Mn;0;NSM;;;;;N;;;;; 1A5E;TAI THAM CONSONANT SIGN SA;Mn;0;NSM;;;;;N;;;;; 1A60;TAI THAM SIGN SAKOT;Mn;9;NSM;;;;;N;;;;; 1A61;TAI THAM VOWEL SIGN A;Mc;0;L;;;;;N;;;;; 1A62;TAI THAM VOWEL SIGN MAI SAT;Mn;0;NSM;;;;;N;;;;; 1A63;TAI THAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 1A64;TAI THAM VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;; 1A65;TAI THAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1A66;TAI THAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 1A67;TAI THAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; 1A68;TAI THAM VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; 1A69;TAI THAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1A6A;TAI THAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 1A6B;TAI THAM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 1A6C;TAI THAM VOWEL SIGN OA BELOW;Mn;0;NSM;;;;;N;;;;; 1A6D;TAI THAM VOWEL SIGN OY;Mc;0;L;;;;;N;;;;; 1A6E;TAI THAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 1A6F;TAI THAM VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; 1A70;TAI THAM VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; 1A71;TAI THAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 1A72;TAI THAM VOWEL SIGN THAM AI;Mc;0;L;;;;;N;;;;; 1A73;TAI THAM VOWEL SIGN OA ABOVE;Mn;0;NSM;;;;;N;;;;; 1A74;TAI THAM SIGN MAI KANG;Mn;0;NSM;;;;;N;;;;; 1A75;TAI THAM SIGN TONE-1;Mn;230;NSM;;;;;N;;;;; 1A76;TAI THAM SIGN TONE-2;Mn;230;NSM;;;;;N;;;;; 1A77;TAI THAM SIGN KHUEN TONE-3;Mn;230;NSM;;;;;N;;;;; 1A78;TAI THAM SIGN KHUEN TONE-4;Mn;230;NSM;;;;;N;;;;; 1A79;TAI THAM SIGN KHUEN TONE-5;Mn;230;NSM;;;;;N;;;;; 1A7A;TAI THAM SIGN RA HAAM;Mn;230;NSM;;;;;N;;;;; 1A7B;TAI THAM SIGN MAI SAM;Mn;230;NSM;;;;;N;;;;; 1A7C;TAI THAM SIGN KHUEN-LUE KARAN;Mn;230;NSM;;;;;N;;;;; 1A7F;TAI THAM COMBINING CRYPTOGRAMMIC DOT;Mn;220;NSM;;;;;N;;;;; 1A80;TAI THAM HORA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1A81;TAI THAM HORA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1A82;TAI THAM HORA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1A83;TAI THAM HORA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1A84;TAI THAM HORA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1A85;TAI THAM HORA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1A86;TAI THAM HORA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1A87;TAI THAM HORA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1A88;TAI THAM HORA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1A89;TAI THAM HORA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1A90;TAI THAM THAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1A91;TAI THAM THAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1A92;TAI THAM THAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1A93;TAI THAM THAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1A94;TAI THAM THAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1A95;TAI THAM THAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1A96;TAI THAM THAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1A97;TAI THAM THAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1A98;TAI THAM THAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1A99;TAI THAM THAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1AA0;TAI THAM SIGN WIANG;Po;0;L;;;;;N;;;;; 1AA1;TAI THAM SIGN WIANGWAAK;Po;0;L;;;;;N;;;;; 1AA2;TAI THAM SIGN SAWAN;Po;0;L;;;;;N;;;;; 1AA3;TAI THAM SIGN KEOW;Po;0;L;;;;;N;;;;; 1AA4;TAI THAM SIGN HOY;Po;0;L;;;;;N;;;;; 1AA5;TAI THAM SIGN DOKMAI;Po;0;L;;;;;N;;;;; 1AA6;TAI THAM SIGN REVERSED ROTATED RANA;Po;0;L;;;;;N;;;;; 1AA7;TAI THAM SIGN MAI YAMOK;Lm;0;L;;;;;N;;;;; 1AA8;TAI THAM SIGN KAAN;Po;0;L;;;;;N;;;;; 1AA9;TAI THAM SIGN KAANKUU;Po;0;L;;;;;N;;;;; 1AAA;TAI THAM SIGN SATKAAN;Po;0;L;;;;;N;;;;; 1AAB;TAI THAM SIGN SATKAANKUU;Po;0;L;;;;;N;;;;; 1AAC;TAI THAM SIGN HANG;Po;0;L;;;;;N;;;;; 1AAD;TAI THAM SIGN CAANG;Po;0;L;;;;;N;;;;; 1AB0;COMBINING DOUBLED CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;;;;; 1AB1;COMBINING DIAERESIS-RING;Mn;230;NSM;;;;;N;;;;; 1AB2;COMBINING INFINITY;Mn;230;NSM;;;;;N;;;;; 1AB3;COMBINING DOWNWARDS ARROW;Mn;230;NSM;;;;;N;;;;; 1AB4;COMBINING TRIPLE DOT;Mn;230;NSM;;;;;N;;;;; 1AB5;COMBINING X-X BELOW;Mn;220;NSM;;;;;N;;;;; 1AB6;COMBINING WIGGLY LINE BELOW;Mn;220;NSM;;;;;N;;;;; 1AB7;COMBINING OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;; 1AB8;COMBINING DOUBLE OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;; 1AB9;COMBINING LIGHT CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;; 1ABA;COMBINING STRONG CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;; 1ABB;COMBINING PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;; 1ABC;COMBINING DOUBLE PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;; 1ABD;COMBINING PARENTHESES BELOW;Mn;220;NSM;;;;;N;;;;; 1ABE;COMBINING PARENTHESES OVERLAY;Me;0;NSM;;;;;N;;;;; 1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;; 1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;; 1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;; 1B03;BALINESE SIGN SURANG;Mn;0;NSM;;;;;N;;;;; 1B04;BALINESE SIGN BISAH;Mc;0;L;;;;;N;;;;; 1B05;BALINESE LETTER AKARA;Lo;0;L;;;;;N;;;;; 1B06;BALINESE LETTER AKARA TEDUNG;Lo;0;L;1B05 1B35;;;;N;;;;; 1B07;BALINESE LETTER IKARA;Lo;0;L;;;;;N;;;;; 1B08;BALINESE LETTER IKARA TEDUNG;Lo;0;L;1B07 1B35;;;;N;;;;; 1B09;BALINESE LETTER UKARA;Lo;0;L;;;;;N;;;;; 1B0A;BALINESE LETTER UKARA TEDUNG;Lo;0;L;1B09 1B35;;;;N;;;;; 1B0B;BALINESE LETTER RA REPA;Lo;0;L;;;;;N;;;;; 1B0C;BALINESE LETTER RA REPA TEDUNG;Lo;0;L;1B0B 1B35;;;;N;;;;; 1B0D;BALINESE LETTER LA LENGA;Lo;0;L;;;;;N;;;;; 1B0E;BALINESE LETTER LA LENGA TEDUNG;Lo;0;L;1B0D 1B35;;;;N;;;;; 1B0F;BALINESE LETTER EKARA;Lo;0;L;;;;;N;;;;; 1B10;BALINESE LETTER AIKARA;Lo;0;L;;;;;N;;;;; 1B11;BALINESE LETTER OKARA;Lo;0;L;;;;;N;;;;; 1B12;BALINESE LETTER OKARA TEDUNG;Lo;0;L;1B11 1B35;;;;N;;;;; 1B13;BALINESE LETTER KA;Lo;0;L;;;;;N;;;;; 1B14;BALINESE LETTER KA MAHAPRANA;Lo;0;L;;;;;N;;;;; 1B15;BALINESE LETTER GA;Lo;0;L;;;;;N;;;;; 1B16;BALINESE LETTER GA GORA;Lo;0;L;;;;;N;;;;; 1B17;BALINESE LETTER NGA;Lo;0;L;;;;;N;;;;; 1B18;BALINESE LETTER CA;Lo;0;L;;;;;N;;;;; 1B19;BALINESE LETTER CA LACA;Lo;0;L;;;;;N;;;;; 1B1A;BALINESE LETTER JA;Lo;0;L;;;;;N;;;;; 1B1B;BALINESE LETTER JA JERA;Lo;0;L;;;;;N;;;;; 1B1C;BALINESE LETTER NYA;Lo;0;L;;;;;N;;;;; 1B1D;BALINESE LETTER TA LATIK;Lo;0;L;;;;;N;;;;; 1B1E;BALINESE LETTER TA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;; 1B1F;BALINESE LETTER DA MURDA ALPAPRANA;Lo;0;L;;;;;N;;;;; 1B20;BALINESE LETTER DA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;; 1B21;BALINESE LETTER NA RAMBAT;Lo;0;L;;;;;N;;;;; 1B22;BALINESE LETTER TA;Lo;0;L;;;;;N;;;;; 1B23;BALINESE LETTER TA TAWA;Lo;0;L;;;;;N;;;;; 1B24;BALINESE LETTER DA;Lo;0;L;;;;;N;;;;; 1B25;BALINESE LETTER DA MADU;Lo;0;L;;;;;N;;;;; 1B26;BALINESE LETTER NA;Lo;0;L;;;;;N;;;;; 1B27;BALINESE LETTER PA;Lo;0;L;;;;;N;;;;; 1B28;BALINESE LETTER PA KAPAL;Lo;0;L;;;;;N;;;;; 1B29;BALINESE LETTER BA;Lo;0;L;;;;;N;;;;; 1B2A;BALINESE LETTER BA KEMBANG;Lo;0;L;;;;;N;;;;; 1B2B;BALINESE LETTER MA;Lo;0;L;;;;;N;;;;; 1B2C;BALINESE LETTER YA;Lo;0;L;;;;;N;;;;; 1B2D;BALINESE LETTER RA;Lo;0;L;;;;;N;;;;; 1B2E;BALINESE LETTER LA;Lo;0;L;;;;;N;;;;; 1B2F;BALINESE LETTER WA;Lo;0;L;;;;;N;;;;; 1B30;BALINESE LETTER SA SAGA;Lo;0;L;;;;;N;;;;; 1B31;BALINESE LETTER SA SAPA;Lo;0;L;;;;;N;;;;; 1B32;BALINESE LETTER SA;Lo;0;L;;;;;N;;;;; 1B33;BALINESE LETTER HA;Lo;0;L;;;;;N;;;;; 1B34;BALINESE SIGN REREKAN;Mn;7;NSM;;;;;N;;;;; 1B35;BALINESE VOWEL SIGN TEDUNG;Mc;0;L;;;;;N;;;;; 1B36;BALINESE VOWEL SIGN ULU;Mn;0;NSM;;;;;N;;;;; 1B37;BALINESE VOWEL SIGN ULU SARI;Mn;0;NSM;;;;;N;;;;; 1B38;BALINESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;; 1B39;BALINESE VOWEL SIGN SUKU ILUT;Mn;0;NSM;;;;;N;;;;; 1B3A;BALINESE VOWEL SIGN RA REPA;Mn;0;NSM;;;;;N;;;;; 1B3B;BALINESE VOWEL SIGN RA REPA TEDUNG;Mc;0;L;1B3A 1B35;;;;N;;;;; 1B3C;BALINESE VOWEL SIGN LA LENGA;Mn;0;NSM;;;;;N;;;;; 1B3D;BALINESE VOWEL SIGN LA LENGA TEDUNG;Mc;0;L;1B3C 1B35;;;;N;;;;; 1B3E;BALINESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;; 1B3F;BALINESE VOWEL SIGN TALING REPA;Mc;0;L;;;;;N;;;;; 1B40;BALINESE VOWEL SIGN TALING TEDUNG;Mc;0;L;1B3E 1B35;;;;N;;;;; 1B41;BALINESE VOWEL SIGN TALING REPA TEDUNG;Mc;0;L;1B3F 1B35;;;;N;;;;; 1B42;BALINESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;; 1B43;BALINESE VOWEL SIGN PEPET TEDUNG;Mc;0;L;1B42 1B35;;;;N;;;;; 1B44;BALINESE ADEG ADEG;Mc;9;L;;;;;N;;;;; 1B45;BALINESE LETTER KAF SASAK;Lo;0;L;;;;;N;;;;; 1B46;BALINESE LETTER KHOT SASAK;Lo;0;L;;;;;N;;;;; 1B47;BALINESE LETTER TZIR SASAK;Lo;0;L;;;;;N;;;;; 1B48;BALINESE LETTER EF SASAK;Lo;0;L;;;;;N;;;;; 1B49;BALINESE LETTER VE SASAK;Lo;0;L;;;;;N;;;;; 1B4A;BALINESE LETTER ZAL SASAK;Lo;0;L;;;;;N;;;;; 1B4B;BALINESE LETTER ASYURA SASAK;Lo;0;L;;;;;N;;;;; 1B50;BALINESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1B51;BALINESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1B52;BALINESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1B53;BALINESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1B54;BALINESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1B55;BALINESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1B56;BALINESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1B57;BALINESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1B58;BALINESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1B59;BALINESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1B5A;BALINESE PANTI;Po;0;L;;;;;N;;;;; 1B5B;BALINESE PAMADA;Po;0;L;;;;;N;;;;; 1B5C;BALINESE WINDU;Po;0;L;;;;;N;;;;; 1B5D;BALINESE CARIK PAMUNGKAH;Po;0;L;;;;;N;;;;; 1B5E;BALINESE CARIK SIKI;Po;0;L;;;;;N;;;;; 1B5F;BALINESE CARIK PAREREN;Po;0;L;;;;;N;;;;; 1B60;BALINESE PAMENENG;Po;0;L;;;;;N;;;;; 1B61;BALINESE MUSICAL SYMBOL DONG;So;0;L;;;;;N;;;;; 1B62;BALINESE MUSICAL SYMBOL DENG;So;0;L;;;;;N;;;;; 1B63;BALINESE MUSICAL SYMBOL DUNG;So;0;L;;;;;N;;;;; 1B64;BALINESE MUSICAL SYMBOL DANG;So;0;L;;;;;N;;;;; 1B65;BALINESE MUSICAL SYMBOL DANG SURANG;So;0;L;;;;;N;;;;; 1B66;BALINESE MUSICAL SYMBOL DING;So;0;L;;;;;N;;;;; 1B67;BALINESE MUSICAL SYMBOL DAENG;So;0;L;;;;;N;;;;; 1B68;BALINESE MUSICAL SYMBOL DEUNG;So;0;L;;;;;N;;;;; 1B69;BALINESE MUSICAL SYMBOL DAING;So;0;L;;;;;N;;;;; 1B6A;BALINESE MUSICAL SYMBOL DANG GEDE;So;0;L;;;;;N;;;;; 1B6B;BALINESE MUSICAL SYMBOL COMBINING TEGEH;Mn;230;NSM;;;;;N;;;;; 1B6C;BALINESE MUSICAL SYMBOL COMBINING ENDEP;Mn;220;NSM;;;;;N;;;;; 1B6D;BALINESE MUSICAL SYMBOL COMBINING KEMPUL;Mn;230;NSM;;;;;N;;;;; 1B6E;BALINESE MUSICAL SYMBOL COMBINING KEMPLI;Mn;230;NSM;;;;;N;;;;; 1B6F;BALINESE MUSICAL SYMBOL COMBINING JEGOGAN;Mn;230;NSM;;;;;N;;;;; 1B70;BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;; 1B71;BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;; 1B72;BALINESE MUSICAL SYMBOL COMBINING BENDE;Mn;230;NSM;;;;;N;;;;; 1B73;BALINESE MUSICAL SYMBOL COMBINING GONG;Mn;230;NSM;;;;;N;;;;; 1B74;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG;So;0;L;;;;;N;;;;; 1B75;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DAG;So;0;L;;;;;N;;;;; 1B76;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TUK;So;0;L;;;;;N;;;;; 1B77;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TAK;So;0;L;;;;;N;;;;; 1B78;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PANG;So;0;L;;;;;N;;;;; 1B79;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PUNG;So;0;L;;;;;N;;;;; 1B7A;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLAK;So;0;L;;;;;N;;;;; 1B7B;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLUK;So;0;L;;;;;N;;;;; 1B7C;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING;So;0;L;;;;;N;;;;; 1B80;SUNDANESE SIGN PANYECEK;Mn;0;NSM;;;;;N;;;;; 1B81;SUNDANESE SIGN PANGLAYAR;Mn;0;NSM;;;;;N;;;;; 1B82;SUNDANESE SIGN PANGWISAD;Mc;0;L;;;;;N;;;;; 1B83;SUNDANESE LETTER A;Lo;0;L;;;;;N;;;;; 1B84;SUNDANESE LETTER I;Lo;0;L;;;;;N;;;;; 1B85;SUNDANESE LETTER U;Lo;0;L;;;;;N;;;;; 1B86;SUNDANESE LETTER AE;Lo;0;L;;;;;N;;;;; 1B87;SUNDANESE LETTER O;Lo;0;L;;;;;N;;;;; 1B88;SUNDANESE LETTER E;Lo;0;L;;;;;N;;;;; 1B89;SUNDANESE LETTER EU;Lo;0;L;;;;;N;;;;; 1B8A;SUNDANESE LETTER KA;Lo;0;L;;;;;N;;;;; 1B8B;SUNDANESE LETTER QA;Lo;0;L;;;;;N;;;;; 1B8C;SUNDANESE LETTER GA;Lo;0;L;;;;;N;;;;; 1B8D;SUNDANESE LETTER NGA;Lo;0;L;;;;;N;;;;; 1B8E;SUNDANESE LETTER CA;Lo;0;L;;;;;N;;;;; 1B8F;SUNDANESE LETTER JA;Lo;0;L;;;;;N;;;;; 1B90;SUNDANESE LETTER ZA;Lo;0;L;;;;;N;;;;; 1B91;SUNDANESE LETTER NYA;Lo;0;L;;;;;N;;;;; 1B92;SUNDANESE LETTER TA;Lo;0;L;;;;;N;;;;; 1B93;SUNDANESE LETTER DA;Lo;0;L;;;;;N;;;;; 1B94;SUNDANESE LETTER NA;Lo;0;L;;;;;N;;;;; 1B95;SUNDANESE LETTER PA;Lo;0;L;;;;;N;;;;; 1B96;SUNDANESE LETTER FA;Lo;0;L;;;;;N;;;;; 1B97;SUNDANESE LETTER VA;Lo;0;L;;;;;N;;;;; 1B98;SUNDANESE LETTER BA;Lo;0;L;;;;;N;;;;; 1B99;SUNDANESE LETTER MA;Lo;0;L;;;;;N;;;;; 1B9A;SUNDANESE LETTER YA;Lo;0;L;;;;;N;;;;; 1B9B;SUNDANESE LETTER RA;Lo;0;L;;;;;N;;;;; 1B9C;SUNDANESE LETTER LA;Lo;0;L;;;;;N;;;;; 1B9D;SUNDANESE LETTER WA;Lo;0;L;;;;;N;;;;; 1B9E;SUNDANESE LETTER SA;Lo;0;L;;;;;N;;;;; 1B9F;SUNDANESE LETTER XA;Lo;0;L;;;;;N;;;;; 1BA0;SUNDANESE LETTER HA;Lo;0;L;;;;;N;;;;; 1BA1;SUNDANESE CONSONANT SIGN PAMINGKAL;Mc;0;L;;;;;N;;;;; 1BA2;SUNDANESE CONSONANT SIGN PANYAKRA;Mn;0;NSM;;;;;N;;;;; 1BA3;SUNDANESE CONSONANT SIGN PANYIKU;Mn;0;NSM;;;;;N;;;;; 1BA4;SUNDANESE VOWEL SIGN PANGHULU;Mn;0;NSM;;;;;N;;;;; 1BA5;SUNDANESE VOWEL SIGN PANYUKU;Mn;0;NSM;;;;;N;;;;; 1BA6;SUNDANESE VOWEL SIGN PANAELAENG;Mc;0;L;;;;;N;;;;; 1BA7;SUNDANESE VOWEL SIGN PANOLONG;Mc;0;L;;;;;N;;;;; 1BA8;SUNDANESE VOWEL SIGN PAMEPET;Mn;0;NSM;;;;;N;;;;; 1BA9;SUNDANESE VOWEL SIGN PANEULEUNG;Mn;0;NSM;;;;;N;;;;; 1BAA;SUNDANESE SIGN PAMAAEH;Mc;9;L;;;;;N;;;;; 1BAB;SUNDANESE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 1BAC;SUNDANESE CONSONANT SIGN PASANGAN MA;Mn;0;NSM;;;;;N;;;;; 1BAD;SUNDANESE CONSONANT SIGN PASANGAN WA;Mn;0;NSM;;;;;N;;;;; 1BAE;SUNDANESE LETTER KHA;Lo;0;L;;;;;N;;;;; 1BAF;SUNDANESE LETTER SYA;Lo;0;L;;;;;N;;;;; 1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1BB1;SUNDANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1BB2;SUNDANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1BB3;SUNDANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1BB4;SUNDANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1BB5;SUNDANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1BB6;SUNDANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1BBA;SUNDANESE AVAGRAHA;Lo;0;L;;;;;N;;;;; 1BBB;SUNDANESE LETTER REU;Lo;0;L;;;;;N;;;;; 1BBC;SUNDANESE LETTER LEU;Lo;0;L;;;;;N;;;;; 1BBD;SUNDANESE LETTER BHA;Lo;0;L;;;;;N;;;;; 1BBE;SUNDANESE LETTER FINAL K;Lo;0;L;;;;;N;;;;; 1BBF;SUNDANESE LETTER FINAL M;Lo;0;L;;;;;N;;;;; 1BC0;BATAK LETTER A;Lo;0;L;;;;;N;;;;; 1BC1;BATAK LETTER SIMALUNGUN A;Lo;0;L;;;;;N;;;;; 1BC2;BATAK LETTER HA;Lo;0;L;;;;;N;;;;; 1BC3;BATAK LETTER SIMALUNGUN HA;Lo;0;L;;;;;N;;;;; 1BC4;BATAK LETTER MANDAILING HA;Lo;0;L;;;;;N;;;;; 1BC5;BATAK LETTER BA;Lo;0;L;;;;;N;;;;; 1BC6;BATAK LETTER KARO BA;Lo;0;L;;;;;N;;;;; 1BC7;BATAK LETTER PA;Lo;0;L;;;;;N;;;;; 1BC8;BATAK LETTER SIMALUNGUN PA;Lo;0;L;;;;;N;;;;; 1BC9;BATAK LETTER NA;Lo;0;L;;;;;N;;;;; 1BCA;BATAK LETTER MANDAILING NA;Lo;0;L;;;;;N;;;;; 1BCB;BATAK LETTER WA;Lo;0;L;;;;;N;;;;; 1BCC;BATAK LETTER SIMALUNGUN WA;Lo;0;L;;;;;N;;;;; 1BCD;BATAK LETTER PAKPAK WA;Lo;0;L;;;;;N;;;;; 1BCE;BATAK LETTER GA;Lo;0;L;;;;;N;;;;; 1BCF;BATAK LETTER SIMALUNGUN GA;Lo;0;L;;;;;N;;;;; 1BD0;BATAK LETTER JA;Lo;0;L;;;;;N;;;;; 1BD1;BATAK LETTER DA;Lo;0;L;;;;;N;;;;; 1BD2;BATAK LETTER RA;Lo;0;L;;;;;N;;;;; 1BD3;BATAK LETTER SIMALUNGUN RA;Lo;0;L;;;;;N;;;;; 1BD4;BATAK LETTER MA;Lo;0;L;;;;;N;;;;; 1BD5;BATAK LETTER SIMALUNGUN MA;Lo;0;L;;;;;N;;;;; 1BD6;BATAK LETTER SOUTHERN TA;Lo;0;L;;;;;N;;;;; 1BD7;BATAK LETTER NORTHERN TA;Lo;0;L;;;;;N;;;;; 1BD8;BATAK LETTER SA;Lo;0;L;;;;;N;;;;; 1BD9;BATAK LETTER SIMALUNGUN SA;Lo;0;L;;;;;N;;;;; 1BDA;BATAK LETTER MANDAILING SA;Lo;0;L;;;;;N;;;;; 1BDB;BATAK LETTER YA;Lo;0;L;;;;;N;;;;; 1BDC;BATAK LETTER SIMALUNGUN YA;Lo;0;L;;;;;N;;;;; 1BDD;BATAK LETTER NGA;Lo;0;L;;;;;N;;;;; 1BDE;BATAK LETTER LA;Lo;0;L;;;;;N;;;;; 1BDF;BATAK LETTER SIMALUNGUN LA;Lo;0;L;;;;;N;;;;; 1BE0;BATAK LETTER NYA;Lo;0;L;;;;;N;;;;; 1BE1;BATAK LETTER CA;Lo;0;L;;;;;N;;;;; 1BE2;BATAK LETTER NDA;Lo;0;L;;;;;N;;;;; 1BE3;BATAK LETTER MBA;Lo;0;L;;;;;N;;;;; 1BE4;BATAK LETTER I;Lo;0;L;;;;;N;;;;; 1BE5;BATAK LETTER U;Lo;0;L;;;;;N;;;;; 1BE6;BATAK SIGN TOMPI;Mn;7;NSM;;;;;N;;;;; 1BE7;BATAK VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 1BE8;BATAK VOWEL SIGN PAKPAK E;Mn;0;NSM;;;;;N;;;;; 1BE9;BATAK VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; 1BEA;BATAK VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 1BEB;BATAK VOWEL SIGN KARO I;Mc;0;L;;;;;N;;;;; 1BEC;BATAK VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 1BED;BATAK VOWEL SIGN KARO O;Mn;0;NSM;;;;;N;;;;; 1BEE;BATAK VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 1BEF;BATAK VOWEL SIGN U FOR SIMALUNGUN SA;Mn;0;NSM;;;;;N;;;;; 1BF0;BATAK CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;; 1BF1;BATAK CONSONANT SIGN H;Mn;0;NSM;;;;;N;;;;; 1BF2;BATAK PANGOLAT;Mc;9;L;;;;;N;;;;; 1BF3;BATAK PANONGONAN;Mc;9;L;;;;;N;;;;; 1BFC;BATAK SYMBOL BINDU NA METEK;Po;0;L;;;;;N;;;;; 1BFD;BATAK SYMBOL BINDU PINARBORAS;Po;0;L;;;;;N;;;;; 1BFE;BATAK SYMBOL BINDU JUDUL;Po;0;L;;;;;N;;;;; 1BFF;BATAK SYMBOL BINDU PANGOLAT;Po;0;L;;;;;N;;;;; 1C00;LEPCHA LETTER KA;Lo;0;L;;;;;N;;;;; 1C01;LEPCHA LETTER KLA;Lo;0;L;;;;;N;;;;; 1C02;LEPCHA LETTER KHA;Lo;0;L;;;;;N;;;;; 1C03;LEPCHA LETTER GA;Lo;0;L;;;;;N;;;;; 1C04;LEPCHA LETTER GLA;Lo;0;L;;;;;N;;;;; 1C05;LEPCHA LETTER NGA;Lo;0;L;;;;;N;;;;; 1C06;LEPCHA LETTER CA;Lo;0;L;;;;;N;;;;; 1C07;LEPCHA LETTER CHA;Lo;0;L;;;;;N;;;;; 1C08;LEPCHA LETTER JA;Lo;0;L;;;;;N;;;;; 1C09;LEPCHA LETTER NYA;Lo;0;L;;;;;N;;;;; 1C0A;LEPCHA LETTER TA;Lo;0;L;;;;;N;;;;; 1C0B;LEPCHA LETTER THA;Lo;0;L;;;;;N;;;;; 1C0C;LEPCHA LETTER DA;Lo;0;L;;;;;N;;;;; 1C0D;LEPCHA LETTER NA;Lo;0;L;;;;;N;;;;; 1C0E;LEPCHA LETTER PA;Lo;0;L;;;;;N;;;;; 1C0F;LEPCHA LETTER PLA;Lo;0;L;;;;;N;;;;; 1C10;LEPCHA LETTER PHA;Lo;0;L;;;;;N;;;;; 1C11;LEPCHA LETTER FA;Lo;0;L;;;;;N;;;;; 1C12;LEPCHA LETTER FLA;Lo;0;L;;;;;N;;;;; 1C13;LEPCHA LETTER BA;Lo;0;L;;;;;N;;;;; 1C14;LEPCHA LETTER BLA;Lo;0;L;;;;;N;;;;; 1C15;LEPCHA LETTER MA;Lo;0;L;;;;;N;;;;; 1C16;LEPCHA LETTER MLA;Lo;0;L;;;;;N;;;;; 1C17;LEPCHA LETTER TSA;Lo;0;L;;;;;N;;;;; 1C18;LEPCHA LETTER TSHA;Lo;0;L;;;;;N;;;;; 1C19;LEPCHA LETTER DZA;Lo;0;L;;;;;N;;;;; 1C1A;LEPCHA LETTER YA;Lo;0;L;;;;;N;;;;; 1C1B;LEPCHA LETTER RA;Lo;0;L;;;;;N;;;;; 1C1C;LEPCHA LETTER LA;Lo;0;L;;;;;N;;;;; 1C1D;LEPCHA LETTER HA;Lo;0;L;;;;;N;;;;; 1C1E;LEPCHA LETTER HLA;Lo;0;L;;;;;N;;;;; 1C1F;LEPCHA LETTER VA;Lo;0;L;;;;;N;;;;; 1C20;LEPCHA LETTER SA;Lo;0;L;;;;;N;;;;; 1C21;LEPCHA LETTER SHA;Lo;0;L;;;;;N;;;;; 1C22;LEPCHA LETTER WA;Lo;0;L;;;;;N;;;;; 1C23;LEPCHA LETTER A;Lo;0;L;;;;;N;;;;; 1C24;LEPCHA SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; 1C25;LEPCHA SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;; 1C26;LEPCHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 1C27;LEPCHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 1C28;LEPCHA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 1C29;LEPCHA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; 1C2A;LEPCHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 1C2B;LEPCHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 1C2C;LEPCHA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 1C2D;LEPCHA CONSONANT SIGN K;Mn;0;NSM;;;;;N;;;;; 1C2E;LEPCHA CONSONANT SIGN M;Mn;0;NSM;;;;;N;;;;; 1C2F;LEPCHA CONSONANT SIGN L;Mn;0;NSM;;;;;N;;;;; 1C30;LEPCHA CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;; 1C31;LEPCHA CONSONANT SIGN P;Mn;0;NSM;;;;;N;;;;; 1C32;LEPCHA CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;; 1C33;LEPCHA CONSONANT SIGN T;Mn;0;NSM;;;;;N;;;;; 1C34;LEPCHA CONSONANT SIGN NYIN-DO;Mc;0;L;;;;;N;;;;; 1C35;LEPCHA CONSONANT SIGN KANG;Mc;0;L;;;;;N;;;;; 1C36;LEPCHA SIGN RAN;Mn;0;NSM;;;;;N;;;;; 1C37;LEPCHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 1C3B;LEPCHA PUNCTUATION TA-ROL;Po;0;L;;;;;N;;;;; 1C3C;LEPCHA PUNCTUATION NYET THYOOM TA-ROL;Po;0;L;;;;;N;;;;; 1C3D;LEPCHA PUNCTUATION CER-WA;Po;0;L;;;;;N;;;;; 1C3E;LEPCHA PUNCTUATION TSHOOK CER-WA;Po;0;L;;;;;N;;;;; 1C3F;LEPCHA PUNCTUATION TSHOOK;Po;0;L;;;;;N;;;;; 1C40;LEPCHA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1C41;LEPCHA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1C42;LEPCHA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1C43;LEPCHA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1C44;LEPCHA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1C45;LEPCHA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1C46;LEPCHA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1C47;LEPCHA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1C48;LEPCHA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1C49;LEPCHA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1C4D;LEPCHA LETTER TTA;Lo;0;L;;;;;N;;;;; 1C4E;LEPCHA LETTER TTHA;Lo;0;L;;;;;N;;;;; 1C4F;LEPCHA LETTER DDA;Lo;0;L;;;;;N;;;;; 1C50;OL CHIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 1C51;OL CHIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 1C52;OL CHIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 1C53;OL CHIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1C54;OL CHIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1C55;OL CHIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1C56;OL CHIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1C57;OL CHIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1C58;OL CHIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1C59;OL CHIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1C5A;OL CHIKI LETTER LA;Lo;0;L;;;;;N;;;;; 1C5B;OL CHIKI LETTER AT;Lo;0;L;;;;;N;;;;; 1C5C;OL CHIKI LETTER AG;Lo;0;L;;;;;N;;;;; 1C5D;OL CHIKI LETTER ANG;Lo;0;L;;;;;N;;;;; 1C5E;OL CHIKI LETTER AL;Lo;0;L;;;;;N;;;;; 1C5F;OL CHIKI LETTER LAA;Lo;0;L;;;;;N;;;;; 1C60;OL CHIKI LETTER AAK;Lo;0;L;;;;;N;;;;; 1C61;OL CHIKI LETTER AAJ;Lo;0;L;;;;;N;;;;; 1C62;OL CHIKI LETTER AAM;Lo;0;L;;;;;N;;;;; 1C63;OL CHIKI LETTER AAW;Lo;0;L;;;;;N;;;;; 1C64;OL CHIKI LETTER LI;Lo;0;L;;;;;N;;;;; 1C65;OL CHIKI LETTER IS;Lo;0;L;;;;;N;;;;; 1C66;OL CHIKI LETTER IH;Lo;0;L;;;;;N;;;;; 1C67;OL CHIKI LETTER INY;Lo;0;L;;;;;N;;;;; 1C68;OL CHIKI LETTER IR;Lo;0;L;;;;;N;;;;; 1C69;OL CHIKI LETTER LU;Lo;0;L;;;;;N;;;;; 1C6A;OL CHIKI LETTER UC;Lo;0;L;;;;;N;;;;; 1C6B;OL CHIKI LETTER UD;Lo;0;L;;;;;N;;;;; 1C6C;OL CHIKI LETTER UNN;Lo;0;L;;;;;N;;;;; 1C6D;OL CHIKI LETTER UY;Lo;0;L;;;;;N;;;;; 1C6E;OL CHIKI LETTER LE;Lo;0;L;;;;;N;;;;; 1C6F;OL CHIKI LETTER EP;Lo;0;L;;;;;N;;;;; 1C70;OL CHIKI LETTER EDD;Lo;0;L;;;;;N;;;;; 1C71;OL CHIKI LETTER EN;Lo;0;L;;;;;N;;;;; 1C72;OL CHIKI LETTER ERR;Lo;0;L;;;;;N;;;;; 1C73;OL CHIKI LETTER LO;Lo;0;L;;;;;N;;;;; 1C74;OL CHIKI LETTER OTT;Lo;0;L;;;;;N;;;;; 1C75;OL CHIKI LETTER OB;Lo;0;L;;;;;N;;;;; 1C76;OL CHIKI LETTER OV;Lo;0;L;;;;;N;;;;; 1C77;OL CHIKI LETTER OH;Lo;0;L;;;;;N;;;;; 1C78;OL CHIKI MU TTUDDAG;Lm;0;L;;;;;N;;;;; 1C79;OL CHIKI GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;; 1C7A;OL CHIKI MU-GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;; 1C7B;OL CHIKI RELAA;Lm;0;L;;;;;N;;;;; 1C7C;OL CHIKI PHAARKAA;Lm;0;L;;;;;N;;;;; 1C7D;OL CHIKI AHAD;Lm;0;L;;;;;N;;;;; 1C7E;OL CHIKI PUNCTUATION MUCAAD;Po;0;L;;;;;N;;;;; 1C7F;OL CHIKI PUNCTUATION DOUBLE MUCAAD;Po;0;L;;;;;N;;;;; 1C80;CYRILLIC SMALL LETTER ROUNDED VE;Ll;0;L;;;;;N;;;0412;;0412 1C81;CYRILLIC SMALL LETTER LONG-LEGGED DE;Ll;0;L;;;;;N;;;0414;;0414 1C82;CYRILLIC SMALL LETTER NARROW O;Ll;0;L;;;;;N;;;041E;;041E 1C83;CYRILLIC SMALL LETTER WIDE ES;Ll;0;L;;;;;N;;;0421;;0421 1C84;CYRILLIC SMALL LETTER TALL TE;Ll;0;L;;;;;N;;;0422;;0422 1C85;CYRILLIC SMALL LETTER THREE-LEGGED TE;Ll;0;L;;;;;N;;;0422;;0422 1C86;CYRILLIC SMALL LETTER TALL HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A 1C87;CYRILLIC SMALL LETTER TALL YAT;Ll;0;L;;;;;N;;;0462;;0462 1C88;CYRILLIC SMALL LETTER UNBLENDED UK;Ll;0;L;;;;;N;;;A64A;;A64A 1CC0;SUNDANESE PUNCTUATION BINDU SURYA;Po;0;L;;;;;N;;;;; 1CC1;SUNDANESE PUNCTUATION BINDU PANGLONG;Po;0;L;;;;;N;;;;; 1CC2;SUNDANESE PUNCTUATION BINDU PURNAMA;Po;0;L;;;;;N;;;;; 1CC3;SUNDANESE PUNCTUATION BINDU CAKRA;Po;0;L;;;;;N;;;;; 1CC4;SUNDANESE PUNCTUATION BINDU LEU SATANGA;Po;0;L;;;;;N;;;;; 1CC5;SUNDANESE PUNCTUATION BINDU KA SATANGA;Po;0;L;;;;;N;;;;; 1CC6;SUNDANESE PUNCTUATION BINDU DA SATANGA;Po;0;L;;;;;N;;;;; 1CC7;SUNDANESE PUNCTUATION BINDU BA SATANGA;Po;0;L;;;;;N;;;;; 1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;; 1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;; 1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;; 1CD3;VEDIC SIGN NIHSHVASA;Po;0;L;;;;;N;;;;; 1CD4;VEDIC SIGN YAJURVEDIC MIDLINE SVARITA;Mn;1;NSM;;;;;N;;;;; 1CD5;VEDIC TONE YAJURVEDIC AGGRAVATED INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; 1CD6;VEDIC TONE YAJURVEDIC INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; 1CD7;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; 1CD8;VEDIC TONE CANDRA BELOW;Mn;220;NSM;;;;;N;;;;; 1CD9;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA SCHROEDER;Mn;220;NSM;;;;;N;;;;; 1CDA;VEDIC TONE DOUBLE SVARITA;Mn;230;NSM;;;;;N;;;;; 1CDB;VEDIC TONE TRIPLE SVARITA;Mn;230;NSM;;;;;N;;;;; 1CDC;VEDIC TONE KATHAKA ANUDATTA;Mn;220;NSM;;;;;N;;;;; 1CDD;VEDIC TONE DOT BELOW;Mn;220;NSM;;;;;N;;;;; 1CDE;VEDIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;; 1CDF;VEDIC TONE THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;; 1CE0;VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA;Mn;230;NSM;;;;;N;;;;; 1CE1;VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA;Mc;0;L;;;;;N;;;;; 1CE2;VEDIC SIGN VISARGA SVARITA;Mn;1;NSM;;;;;N;;;;; 1CE3;VEDIC SIGN VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;; 1CE4;VEDIC SIGN REVERSED VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;; 1CE5;VEDIC SIGN VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;; 1CE6;VEDIC SIGN REVERSED VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;; 1CE7;VEDIC SIGN VISARGA UDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;; 1CE8;VEDIC SIGN VISARGA ANUDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;; 1CE9;VEDIC SIGN ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;; 1CEA;VEDIC SIGN ANUSVARA BAHIRGOMUKHA;Lo;0;L;;;;;N;;;;; 1CEB;VEDIC SIGN ANUSVARA VAMAGOMUKHA;Lo;0;L;;;;;N;;;;; 1CEC;VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL;Lo;0;L;;;;;N;;;;; 1CED;VEDIC SIGN TIRYAK;Mn;220;NSM;;;;;N;;;;; 1CEE;VEDIC SIGN HEXIFORM LONG ANUSVARA;Lo;0;L;;;;;N;;;;; 1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;; 1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;; 1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;; 1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;; 1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;; 1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;; 1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; 1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; 1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;; 1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;; 1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;; 1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;; 1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;; 1D03;LATIN LETTER SMALL CAPITAL BARRED B;Ll;0;L;;;;;N;;;;; 1D04;LATIN LETTER SMALL CAPITAL C;Ll;0;L;;;;;N;;;;; 1D05;LATIN LETTER SMALL CAPITAL D;Ll;0;L;;;;;N;;;;; 1D06;LATIN LETTER SMALL CAPITAL ETH;Ll;0;L;;;;;N;;;;; 1D07;LATIN LETTER SMALL CAPITAL E;Ll;0;L;;;;;N;;;;; 1D08;LATIN SMALL LETTER TURNED OPEN E;Ll;0;L;;;;;N;;;;; 1D09;LATIN SMALL LETTER TURNED I;Ll;0;L;;;;;N;;;;; 1D0A;LATIN LETTER SMALL CAPITAL J;Ll;0;L;;;;;N;;;;; 1D0B;LATIN LETTER SMALL CAPITAL K;Ll;0;L;;;;;N;;;;; 1D0C;LATIN LETTER SMALL CAPITAL L WITH STROKE;Ll;0;L;;;;;N;;;;; 1D0D;LATIN LETTER SMALL CAPITAL M;Ll;0;L;;;;;N;;;;; 1D0E;LATIN LETTER SMALL CAPITAL REVERSED N;Ll;0;L;;;;;N;;;;; 1D0F;LATIN LETTER SMALL CAPITAL O;Ll;0;L;;;;;N;;;;; 1D10;LATIN LETTER SMALL CAPITAL OPEN O;Ll;0;L;;;;;N;;;;; 1D11;LATIN SMALL LETTER SIDEWAYS O;Ll;0;L;;;;;N;;;;; 1D12;LATIN SMALL LETTER SIDEWAYS OPEN O;Ll;0;L;;;;;N;;;;; 1D13;LATIN SMALL LETTER SIDEWAYS O WITH STROKE;Ll;0;L;;;;;N;;;;; 1D14;LATIN SMALL LETTER TURNED OE;Ll;0;L;;;;;N;;;;; 1D15;LATIN LETTER SMALL CAPITAL OU;Ll;0;L;;;;;N;;;;; 1D16;LATIN SMALL LETTER TOP HALF O;Ll;0;L;;;;;N;;;;; 1D17;LATIN SMALL LETTER BOTTOM HALF O;Ll;0;L;;;;;N;;;;; 1D18;LATIN LETTER SMALL CAPITAL P;Ll;0;L;;;;;N;;;;; 1D19;LATIN LETTER SMALL CAPITAL REVERSED R;Ll;0;L;;;;;N;;;;; 1D1A;LATIN LETTER SMALL CAPITAL TURNED R;Ll;0;L;;;;;N;;;;; 1D1B;LATIN LETTER SMALL CAPITAL T;Ll;0;L;;;;;N;;;;; 1D1C;LATIN LETTER SMALL CAPITAL U;Ll;0;L;;;;;N;;;;; 1D1D;LATIN SMALL LETTER SIDEWAYS U;Ll;0;L;;;;;N;;;;; 1D1E;LATIN SMALL LETTER SIDEWAYS DIAERESIZED U;Ll;0;L;;;;;N;;;;; 1D1F;LATIN SMALL LETTER SIDEWAYS TURNED M;Ll;0;L;;;;;N;;;;; 1D20;LATIN LETTER SMALL CAPITAL V;Ll;0;L;;;;;N;;;;; 1D21;LATIN LETTER SMALL CAPITAL W;Ll;0;L;;;;;N;;;;; 1D22;LATIN LETTER SMALL CAPITAL Z;Ll;0;L;;;;;N;;;;; 1D23;LATIN LETTER SMALL CAPITAL EZH;Ll;0;L;;;;;N;;;;; 1D24;LATIN LETTER VOICED LARYNGEAL SPIRANT;Ll;0;L;;;;;N;;;;; 1D25;LATIN LETTER AIN;Ll;0;L;;;;;N;;;;; 1D26;GREEK LETTER SMALL CAPITAL GAMMA;Ll;0;L;;;;;N;;;;; 1D27;GREEK LETTER SMALL CAPITAL LAMDA;Ll;0;L;;;;;N;;;;; 1D28;GREEK LETTER SMALL CAPITAL PI;Ll;0;L;;;;;N;;;;; 1D29;GREEK LETTER SMALL CAPITAL RHO;Ll;0;L;;;;;N;;;;; 1D2A;GREEK LETTER SMALL CAPITAL PSI;Ll;0;L;;;;;N;;;;; 1D2B;CYRILLIC LETTER SMALL CAPITAL EL;Ll;0;L;;;;;N;;;;; 1D2C;MODIFIER LETTER CAPITAL A;Lm;0;L; 0041;;;;N;;;;; 1D2D;MODIFIER LETTER CAPITAL AE;Lm;0;L; 00C6;;;;N;;;;; 1D2E;MODIFIER LETTER CAPITAL B;Lm;0;L; 0042;;;;N;;;;; 1D2F;MODIFIER LETTER CAPITAL BARRED B;Lm;0;L;;;;;N;;;;; 1D30;MODIFIER LETTER CAPITAL D;Lm;0;L; 0044;;;;N;;;;; 1D31;MODIFIER LETTER CAPITAL E;Lm;0;L; 0045;;;;N;;;;; 1D32;MODIFIER LETTER CAPITAL REVERSED E;Lm;0;L; 018E;;;;N;;;;; 1D33;MODIFIER LETTER CAPITAL G;Lm;0;L; 0047;;;;N;;;;; 1D34;MODIFIER LETTER CAPITAL H;Lm;0;L; 0048;;;;N;;;;; 1D35;MODIFIER LETTER CAPITAL I;Lm;0;L; 0049;;;;N;;;;; 1D36;MODIFIER LETTER CAPITAL J;Lm;0;L; 004A;;;;N;;;;; 1D37;MODIFIER LETTER CAPITAL K;Lm;0;L; 004B;;;;N;;;;; 1D38;MODIFIER LETTER CAPITAL L;Lm;0;L; 004C;;;;N;;;;; 1D39;MODIFIER LETTER CAPITAL M;Lm;0;L; 004D;;;;N;;;;; 1D3A;MODIFIER LETTER CAPITAL N;Lm;0;L; 004E;;;;N;;;;; 1D3B;MODIFIER LETTER CAPITAL REVERSED N;Lm;0;L;;;;;N;;;;; 1D3C;MODIFIER LETTER CAPITAL O;Lm;0;L; 004F;;;;N;;;;; 1D3D;MODIFIER LETTER CAPITAL OU;Lm;0;L; 0222;;;;N;;;;; 1D3E;MODIFIER LETTER CAPITAL P;Lm;0;L; 0050;;;;N;;;;; 1D3F;MODIFIER LETTER CAPITAL R;Lm;0;L; 0052;;;;N;;;;; 1D40;MODIFIER LETTER CAPITAL T;Lm;0;L; 0054;;;;N;;;;; 1D41;MODIFIER LETTER CAPITAL U;Lm;0;L; 0055;;;;N;;;;; 1D42;MODIFIER LETTER CAPITAL W;Lm;0;L; 0057;;;;N;;;;; 1D43;MODIFIER LETTER SMALL A;Lm;0;L; 0061;;;;N;;;;; 1D44;MODIFIER LETTER SMALL TURNED A;Lm;0;L; 0250;;;;N;;;;; 1D45;MODIFIER LETTER SMALL ALPHA;Lm;0;L; 0251;;;;N;;;;; 1D46;MODIFIER LETTER SMALL TURNED AE;Lm;0;L; 1D02;;;;N;;;;; 1D47;MODIFIER LETTER SMALL B;Lm;0;L; 0062;;;;N;;;;; 1D48;MODIFIER LETTER SMALL D;Lm;0;L; 0064;;;;N;;;;; 1D49;MODIFIER LETTER SMALL E;Lm;0;L; 0065;;;;N;;;;; 1D4A;MODIFIER LETTER SMALL SCHWA;Lm;0;L; 0259;;;;N;;;;; 1D4B;MODIFIER LETTER SMALL OPEN E;Lm;0;L; 025B;;;;N;;;;; 1D4C;MODIFIER LETTER SMALL TURNED OPEN E;Lm;0;L; 025C;;;;N;;;;; 1D4D;MODIFIER LETTER SMALL G;Lm;0;L; 0067;;;;N;;;;; 1D4E;MODIFIER LETTER SMALL TURNED I;Lm;0;L;;;;;N;;;;; 1D4F;MODIFIER LETTER SMALL K;Lm;0;L; 006B;;;;N;;;;; 1D50;MODIFIER LETTER SMALL M;Lm;0;L; 006D;;;;N;;;;; 1D51;MODIFIER LETTER SMALL ENG;Lm;0;L; 014B;;;;N;;;;; 1D52;MODIFIER LETTER SMALL O;Lm;0;L; 006F;;;;N;;;;; 1D53;MODIFIER LETTER SMALL OPEN O;Lm;0;L; 0254;;;;N;;;;; 1D54;MODIFIER LETTER SMALL TOP HALF O;Lm;0;L; 1D16;;;;N;;;;; 1D55;MODIFIER LETTER SMALL BOTTOM HALF O;Lm;0;L; 1D17;;;;N;;;;; 1D56;MODIFIER LETTER SMALL P;Lm;0;L; 0070;;;;N;;;;; 1D57;MODIFIER LETTER SMALL T;Lm;0;L; 0074;;;;N;;;;; 1D58;MODIFIER LETTER SMALL U;Lm;0;L; 0075;;;;N;;;;; 1D59;MODIFIER LETTER SMALL SIDEWAYS U;Lm;0;L; 1D1D;;;;N;;;;; 1D5A;MODIFIER LETTER SMALL TURNED M;Lm;0;L; 026F;;;;N;;;;; 1D5B;MODIFIER LETTER SMALL V;Lm;0;L; 0076;;;;N;;;;; 1D5C;MODIFIER LETTER SMALL AIN;Lm;0;L; 1D25;;;;N;;;;; 1D5D;MODIFIER LETTER SMALL BETA;Lm;0;L; 03B2;;;;N;;;;; 1D5E;MODIFIER LETTER SMALL GREEK GAMMA;Lm;0;L; 03B3;;;;N;;;;; 1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L; 03B4;;;;N;;;;; 1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L; 03C6;;;;N;;;;; 1D61;MODIFIER LETTER SMALL CHI;Lm;0;L; 03C7;;;;N;;;;; 1D62;LATIN SUBSCRIPT SMALL LETTER I;Lm;0;L; 0069;;;;N;;;;; 1D63;LATIN SUBSCRIPT SMALL LETTER R;Lm;0;L; 0072;;;;N;;;;; 1D64;LATIN SUBSCRIPT SMALL LETTER U;Lm;0;L; 0075;;;;N;;;;; 1D65;LATIN SUBSCRIPT SMALL LETTER V;Lm;0;L; 0076;;;;N;;;;; 1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Lm;0;L; 03B2;;;;N;;;;; 1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Lm;0;L; 03B3;;;;N;;;;; 1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Lm;0;L; 03C1;;;;N;;;;; 1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Lm;0;L; 03C6;;;;N;;;;; 1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Lm;0;L; 03C7;;;;N;;;;; 1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;; 1D6C;LATIN SMALL LETTER B WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D6D;LATIN SMALL LETTER D WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D6E;LATIN SMALL LETTER F WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D6F;LATIN SMALL LETTER M WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D70;LATIN SMALL LETTER N WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D71;LATIN SMALL LETTER P WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D72;LATIN SMALL LETTER R WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D73;LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D74;LATIN SMALL LETTER S WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D75;LATIN SMALL LETTER T WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D76;LATIN SMALL LETTER Z WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D77;LATIN SMALL LETTER TURNED G;Ll;0;L;;;;;N;;;;; 1D78;MODIFIER LETTER CYRILLIC EN;Lm;0;L; 043D;;;;N;;;;; 1D79;LATIN SMALL LETTER INSULAR G;Ll;0;L;;;;;N;;;A77D;;A77D 1D7A;LATIN SMALL LETTER TH WITH STRIKETHROUGH;Ll;0;L;;;;;N;;;;; 1D7B;LATIN SMALL CAPITAL LETTER I WITH STROKE;Ll;0;L;;;;;N;;;;; 1D7C;LATIN SMALL LETTER IOTA WITH STROKE;Ll;0;L;;;;;N;;;;; 1D7D;LATIN SMALL LETTER P WITH STROKE;Ll;0;L;;;;;N;;;2C63;;2C63 1D7E;LATIN SMALL CAPITAL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;;; 1D7F;LATIN SMALL LETTER UPSILON WITH STROKE;Ll;0;L;;;;;N;;;;; 1D80;LATIN SMALL LETTER B WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D81;LATIN SMALL LETTER D WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D82;LATIN SMALL LETTER F WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D83;LATIN SMALL LETTER G WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D84;LATIN SMALL LETTER K WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D85;LATIN SMALL LETTER L WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D86;LATIN SMALL LETTER M WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D87;LATIN SMALL LETTER N WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D88;LATIN SMALL LETTER P WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D89;LATIN SMALL LETTER R WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8A;LATIN SMALL LETTER S WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; 1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;; 1D92;LATIN SMALL LETTER E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D93;LATIN SMALL LETTER OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D94;LATIN SMALL LETTER REVERSED OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D95;LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D96;LATIN SMALL LETTER I WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D97;LATIN SMALL LETTER OPEN O WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D98;LATIN SMALL LETTER ESH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D99;LATIN SMALL LETTER U WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D9A;LATIN SMALL LETTER EZH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; 1D9B;MODIFIER LETTER SMALL TURNED ALPHA;Lm;0;L; 0252;;;;N;;;;; 1D9C;MODIFIER LETTER SMALL C;Lm;0;L; 0063;;;;N;;;;; 1D9D;MODIFIER LETTER SMALL C WITH CURL;Lm;0;L; 0255;;;;N;;;;; 1D9E;MODIFIER LETTER SMALL ETH;Lm;0;L; 00F0;;;;N;;;;; 1D9F;MODIFIER LETTER SMALL REVERSED OPEN E;Lm;0;L; 025C;;;;N;;;;; 1DA0;MODIFIER LETTER SMALL F;Lm;0;L; 0066;;;;N;;;;; 1DA1;MODIFIER LETTER SMALL DOTLESS J WITH STROKE;Lm;0;L; 025F;;;;N;;;;; 1DA2;MODIFIER LETTER SMALL SCRIPT G;Lm;0;L; 0261;;;;N;;;;; 1DA3;MODIFIER LETTER SMALL TURNED H;Lm;0;L; 0265;;;;N;;;;; 1DA4;MODIFIER LETTER SMALL I WITH STROKE;Lm;0;L; 0268;;;;N;;;;; 1DA5;MODIFIER LETTER SMALL IOTA;Lm;0;L; 0269;;;;N;;;;; 1DA6;MODIFIER LETTER SMALL CAPITAL I;Lm;0;L; 026A;;;;N;;;;; 1DA7;MODIFIER LETTER SMALL CAPITAL I WITH STROKE;Lm;0;L; 1D7B;;;;N;;;;; 1DA8;MODIFIER LETTER SMALL J WITH CROSSED-TAIL;Lm;0;L; 029D;;;;N;;;;; 1DA9;MODIFIER LETTER SMALL L WITH RETROFLEX HOOK;Lm;0;L; 026D;;;;N;;;;; 1DAA;MODIFIER LETTER SMALL L WITH PALATAL HOOK;Lm;0;L; 1D85;;;;N;;;;; 1DAB;MODIFIER LETTER SMALL CAPITAL L;Lm;0;L; 029F;;;;N;;;;; 1DAC;MODIFIER LETTER SMALL M WITH HOOK;Lm;0;L; 0271;;;;N;;;;; 1DAD;MODIFIER LETTER SMALL TURNED M WITH LONG LEG;Lm;0;L; 0270;;;;N;;;;; 1DAE;MODIFIER LETTER SMALL N WITH LEFT HOOK;Lm;0;L; 0272;;;;N;;;;; 1DAF;MODIFIER LETTER SMALL N WITH RETROFLEX HOOK;Lm;0;L; 0273;;;;N;;;;; 1DB0;MODIFIER LETTER SMALL CAPITAL N;Lm;0;L; 0274;;;;N;;;;; 1DB1;MODIFIER LETTER SMALL BARRED O;Lm;0;L; 0275;;;;N;;;;; 1DB2;MODIFIER LETTER SMALL PHI;Lm;0;L; 0278;;;;N;;;;; 1DB3;MODIFIER LETTER SMALL S WITH HOOK;Lm;0;L; 0282;;;;N;;;;; 1DB4;MODIFIER LETTER SMALL ESH;Lm;0;L; 0283;;;;N;;;;; 1DB5;MODIFIER LETTER SMALL T WITH PALATAL HOOK;Lm;0;L; 01AB;;;;N;;;;; 1DB6;MODIFIER LETTER SMALL U BAR;Lm;0;L; 0289;;;;N;;;;; 1DB7;MODIFIER LETTER SMALL UPSILON;Lm;0;L; 028A;;;;N;;;;; 1DB8;MODIFIER LETTER SMALL CAPITAL U;Lm;0;L; 1D1C;;;;N;;;;; 1DB9;MODIFIER LETTER SMALL V WITH HOOK;Lm;0;L; 028B;;;;N;;;;; 1DBA;MODIFIER LETTER SMALL TURNED V;Lm;0;L; 028C;;;;N;;;;; 1DBB;MODIFIER LETTER SMALL Z;Lm;0;L; 007A;;;;N;;;;; 1DBC;MODIFIER LETTER SMALL Z WITH RETROFLEX HOOK;Lm;0;L; 0290;;;;N;;;;; 1DBD;MODIFIER LETTER SMALL Z WITH CURL;Lm;0;L; 0291;;;;N;;;;; 1DBE;MODIFIER LETTER SMALL EZH;Lm;0;L; 0292;;;;N;;;;; 1DBF;MODIFIER LETTER SMALL THETA;Lm;0;L; 03B8;;;;N;;;;; 1DC0;COMBINING DOTTED GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;; 1DC1;COMBINING DOTTED ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;; 1DC2;COMBINING SNAKE BELOW;Mn;220;NSM;;;;;N;;;;; 1DC3;COMBINING SUSPENSION MARK;Mn;230;NSM;;;;;N;;;;; 1DC4;COMBINING MACRON-ACUTE;Mn;230;NSM;;;;;N;;;;; 1DC5;COMBINING GRAVE-MACRON;Mn;230;NSM;;;;;N;;;;; 1DC6;COMBINING MACRON-GRAVE;Mn;230;NSM;;;;;N;;;;; 1DC7;COMBINING ACUTE-MACRON;Mn;230;NSM;;;;;N;;;;; 1DC8;COMBINING GRAVE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; 1DC9;COMBINING ACUTE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;; 1DCA;COMBINING LATIN SMALL LETTER R BELOW;Mn;220;NSM;;;;;N;;;;; 1DCB;COMBINING BREVE-MACRON;Mn;230;NSM;;;;;N;;;;; 1DCC;COMBINING MACRON-BREVE;Mn;230;NSM;;;;;N;;;;; 1DCD;COMBINING DOUBLE CIRCUMFLEX ABOVE;Mn;234;NSM;;;;;N;;;;; 1DCE;COMBINING OGONEK ABOVE;Mn;214;NSM;;;;;N;;;;; 1DCF;COMBINING ZIGZAG BELOW;Mn;220;NSM;;;;;N;;;;; 1DD0;COMBINING IS BELOW;Mn;202;NSM;;;;;N;;;;; 1DD1;COMBINING UR ABOVE;Mn;230;NSM;;;;;N;;;;; 1DD2;COMBINING US ABOVE;Mn;230;NSM;;;;;N;;;;; 1DD3;COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE;Mn;230;NSM;;;;;N;;;;; 1DD4;COMBINING LATIN SMALL LETTER AE;Mn;230;NSM;;;;;N;;;;; 1DD5;COMBINING LATIN SMALL LETTER AO;Mn;230;NSM;;;;;N;;;;; 1DD6;COMBINING LATIN SMALL LETTER AV;Mn;230;NSM;;;;;N;;;;; 1DD7;COMBINING LATIN SMALL LETTER C CEDILLA;Mn;230;NSM;;;;;N;;;;; 1DD8;COMBINING LATIN SMALL LETTER INSULAR D;Mn;230;NSM;;;;;N;;;;; 1DD9;COMBINING LATIN SMALL LETTER ETH;Mn;230;NSM;;;;;N;;;;; 1DDA;COMBINING LATIN SMALL LETTER G;Mn;230;NSM;;;;;N;;;;; 1DDB;COMBINING LATIN LETTER SMALL CAPITAL G;Mn;230;NSM;;;;;N;;;;; 1DDC;COMBINING LATIN SMALL LETTER K;Mn;230;NSM;;;;;N;;;;; 1DDD;COMBINING LATIN SMALL LETTER L;Mn;230;NSM;;;;;N;;;;; 1DDE;COMBINING LATIN LETTER SMALL CAPITAL L;Mn;230;NSM;;;;;N;;;;; 1DDF;COMBINING LATIN LETTER SMALL CAPITAL M;Mn;230;NSM;;;;;N;;;;; 1DE0;COMBINING LATIN SMALL LETTER N;Mn;230;NSM;;;;;N;;;;; 1DE1;COMBINING LATIN LETTER SMALL CAPITAL N;Mn;230;NSM;;;;;N;;;;; 1DE2;COMBINING LATIN LETTER SMALL CAPITAL R;Mn;230;NSM;;;;;N;;;;; 1DE3;COMBINING LATIN SMALL LETTER R ROTUNDA;Mn;230;NSM;;;;;N;;;;; 1DE4;COMBINING LATIN SMALL LETTER S;Mn;230;NSM;;;;;N;;;;; 1DE5;COMBINING LATIN SMALL LETTER LONG S;Mn;230;NSM;;;;;N;;;;; 1DE6;COMBINING LATIN SMALL LETTER Z;Mn;230;NSM;;;;;N;;;;; 1DE7;COMBINING LATIN SMALL LETTER ALPHA;Mn;230;NSM;;;;;N;;;;; 1DE8;COMBINING LATIN SMALL LETTER B;Mn;230;NSM;;;;;N;;;;; 1DE9;COMBINING LATIN SMALL LETTER BETA;Mn;230;NSM;;;;;N;;;;; 1DEA;COMBINING LATIN SMALL LETTER SCHWA;Mn;230;NSM;;;;;N;;;;; 1DEB;COMBINING LATIN SMALL LETTER F;Mn;230;NSM;;;;;N;;;;; 1DEC;COMBINING LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Mn;230;NSM;;;;;N;;;;; 1DED;COMBINING LATIN SMALL LETTER O WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;; 1DEE;COMBINING LATIN SMALL LETTER P;Mn;230;NSM;;;;;N;;;;; 1DEF;COMBINING LATIN SMALL LETTER ESH;Mn;230;NSM;;;;;N;;;;; 1DF0;COMBINING LATIN SMALL LETTER U WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;; 1DF1;COMBINING LATIN SMALL LETTER W;Mn;230;NSM;;;;;N;;;;; 1DF2;COMBINING LATIN SMALL LETTER A WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; 1DF3;COMBINING LATIN SMALL LETTER O WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; 1DF4;COMBINING LATIN SMALL LETTER U WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; 1DF5;COMBINING UP TACK ABOVE;Mn;230;NSM;;;;;N;;;;; 1DFB;COMBINING DELETION MARK;Mn;230;NSM;;;;;N;;;;; 1DFC;COMBINING DOUBLE INVERTED BREVE BELOW;Mn;233;NSM;;;;;N;;;;; 1DFD;COMBINING ALMOST EQUAL TO BELOW;Mn;220;NSM;;;;;N;;;;; 1DFE;COMBINING LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; 1DFF;COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; 1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01; 1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00 1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03; 1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02 1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05; 1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04 1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07; 1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06 1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09; 1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08 1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B; 1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A 1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D; 1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C 1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F; 1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E 1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11; 1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10 1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13; 1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12 1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15; 1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14 1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17; 1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16 1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19; 1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18 1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B; 1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A 1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D; 1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C 1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F; 1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E 1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21; 1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20 1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23; 1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22 1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25; 1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24 1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27; 1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26 1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29; 1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28 1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B; 1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A 1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D; 1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C 1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F; 1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E 1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31; 1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30 1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33; 1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32 1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35; 1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34 1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37; 1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36 1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39; 1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38 1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B; 1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A 1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D; 1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C 1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F; 1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E 1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41; 1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40 1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43; 1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42 1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45; 1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44 1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47; 1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46 1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49; 1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48 1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B; 1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A 1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D; 1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C 1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F; 1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E 1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51; 1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50 1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53; 1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52 1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55; 1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54 1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57; 1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56 1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59; 1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58 1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B; 1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A 1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D; 1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C 1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F; 1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E 1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61; 1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60 1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63; 1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62 1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65; 1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64 1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67; 1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66 1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69; 1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68 1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B; 1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A 1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D; 1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C 1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F; 1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E 1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71; 1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70 1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73; 1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72 1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75; 1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74 1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77; 1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76 1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79; 1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78 1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B; 1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A 1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D; 1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C 1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F; 1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E 1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81; 1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80 1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83; 1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82 1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85; 1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84 1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87; 1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86 1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89; 1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88 1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B; 1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A 1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D; 1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C 1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F; 1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E 1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91; 1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90 1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93; 1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92 1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95; 1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94 1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;; 1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;; 1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;; 1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;; 1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L; 0061 02BE;;;;N;;;;; 1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60 1E9C;LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;;; 1E9D;LATIN SMALL LETTER LONG S WITH HIGH STROKE;Ll;0;L;;;;;N;;;;; 1E9E;LATIN CAPITAL LETTER SHARP S;Lu;0;L;;;;;N;;;;00DF; 1E9F;LATIN SMALL LETTER DELTA;Ll;0;L;;;;;N;;;;; 1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1; 1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0 1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3; 1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2 1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5; 1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4 1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7; 1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6 1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9; 1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8 1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB; 1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA 1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD; 1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC 1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF; 1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE 1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1; 1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0 1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3; 1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2 1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5; 1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4 1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7; 1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6 1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9; 1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8 1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB; 1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA 1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD; 1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC 1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF; 1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE 1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1; 1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0 1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3; 1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2 1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5; 1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4 1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7; 1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6 1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9; 1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8 1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB; 1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA 1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD; 1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC 1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF; 1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE 1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1; 1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0 1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3; 1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2 1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5; 1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4 1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7; 1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6 1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9; 1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8 1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB; 1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA 1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD; 1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC 1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF; 1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE 1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1; 1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0 1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3; 1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2 1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5; 1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4 1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7; 1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6 1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9; 1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8 1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB; 1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA 1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED; 1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC 1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF; 1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE 1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1; 1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0 1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3; 1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2 1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5; 1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4 1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7; 1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6 1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9; 1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8 1EFA;LATIN CAPITAL LETTER MIDDLE-WELSH LL;Lu;0;L;;;;;N;;;;1EFB; 1EFB;LATIN SMALL LETTER MIDDLE-WELSH LL;Ll;0;L;;;;;N;;;1EFA;;1EFA 1EFC;LATIN CAPITAL LETTER MIDDLE-WELSH V;Lu;0;L;;;;;N;;;;1EFD; 1EFD;LATIN SMALL LETTER MIDDLE-WELSH V;Ll;0;L;;;;;N;;;1EFC;;1EFC 1EFE;LATIN CAPITAL LETTER Y WITH LOOP;Lu;0;L;;;;;N;;;;1EFF; 1EFF;LATIN SMALL LETTER Y WITH LOOP;Ll;0;L;;;;;N;;;1EFE;;1EFE 1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08 1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09 1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A 1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B 1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C 1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D 1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E 1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F 1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00; 1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01; 1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02; 1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03; 1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04; 1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05; 1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06; 1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07; 1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18 1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19 1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A 1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B 1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C 1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D 1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10; 1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11; 1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12; 1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13; 1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14; 1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15; 1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28 1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29 1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A 1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B 1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C 1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D 1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E 1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F 1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20; 1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21; 1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22; 1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23; 1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24; 1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25; 1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26; 1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27; 1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38 1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39 1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A 1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B 1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C 1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D 1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E 1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F 1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30; 1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31; 1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32; 1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33; 1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34; 1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35; 1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36; 1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37; 1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48 1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49 1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A 1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B 1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C 1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D 1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40; 1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41; 1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42; 1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43; 1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44; 1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45; 1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;; 1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59 1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;; 1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B 1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;; 1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D 1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;; 1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F 1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51; 1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53; 1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55; 1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57; 1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68 1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69 1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A 1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B 1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C 1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D 1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E 1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F 1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60; 1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61; 1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62; 1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63; 1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64; 1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65; 1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66; 1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67; 1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA 1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB 1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8 1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9 1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA 1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB 1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA 1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB 1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8 1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9 1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA 1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB 1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA 1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB 1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88 1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89 1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A 1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B 1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C 1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D 1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E 1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F 1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80; 1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81; 1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82; 1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83; 1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84; 1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85; 1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86; 1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87; 1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98 1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99 1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A 1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B 1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C 1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D 1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E 1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F 1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90; 1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91; 1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92; 1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93; 1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94; 1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95; 1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96; 1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97; 1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8 1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9 1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA 1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB 1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC 1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD 1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE 1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF 1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0; 1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1; 1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2; 1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3; 1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4; 1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5; 1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6; 1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7; 1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8 1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9 1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;; 1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC 1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;; 1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;; 1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;; 1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0; 1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1; 1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70; 1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71; 1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3; 1FBD;GREEK KORONIS;Sk;0;ON; 0020 0313;;;;N;;;;; 1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399 1FBF;GREEK PSILI;Sk;0;ON; 0020 0313;;;;N;;;;; 1FC0;GREEK PERISPOMENI;Sk;0;ON; 0020 0342;;;;N;;;;; 1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;; 1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;; 1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC 1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;; 1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;; 1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;; 1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72; 1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73; 1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74; 1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75; 1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3; 1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;; 1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;; 1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;; 1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8 1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9 1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;; 1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;; 1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;; 1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;; 1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0; 1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1; 1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76; 1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77; 1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;; 1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;; 1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;; 1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8 1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9 1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;; 1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;; 1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;; 1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC 1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;; 1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;; 1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0; 1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1; 1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A; 1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B; 1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5; 1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;; 1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;; 1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;; 1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;; 1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC 1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;; 1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;; 1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;; 1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78; 1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79; 1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C; 1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D; 1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3; 1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;; 1FFE;GREEK DASIA;Sk;0;ON; 0020 0314;;;;N;;;;; 2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;; 2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;; 2002;EN SPACE;Zs;0;WS; 0020;;;;N;;;;; 2003;EM SPACE;Zs;0;WS; 0020;;;;N;;;;; 2004;THREE-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; 2005;FOUR-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; 2006;SIX-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; 2007;FIGURE SPACE;Zs;0;WS; 0020;;;;N;;;;; 2008;PUNCTUATION SPACE;Zs;0;WS; 0020;;;;N;;;;; 2009;THIN SPACE;Zs;0;WS; 0020;;;;N;;;;; 200A;HAIR SPACE;Zs;0;WS; 0020;;;;N;;;;; 200B;ZERO WIDTH SPACE;Cf;0;BN;;;;;N;;;;; 200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;; 200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;; 200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;; 200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;; 2010;HYPHEN;Pd;0;ON;;;;;N;;;;; 2011;NON-BREAKING HYPHEN;Pd;0;ON; 2010;;;;N;;;;; 2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;; 2013;EN DASH;Pd;0;ON;;;;;N;;;;; 2014;EM DASH;Pd;0;ON;;;;;N;;;;; 2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;; 2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;; 2017;DOUBLE LOW LINE;Po;0;ON; 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;; 2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;; 2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;; 201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;; 201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;; 201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;; 201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;; 201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;; 201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;; 2020;DAGGER;Po;0;ON;;;;;N;;;;; 2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;; 2022;BULLET;Po;0;ON;;;;;N;;;;; 2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;; 2024;ONE DOT LEADER;Po;0;ON; 002E;;;;N;;;;; 2025;TWO DOT LEADER;Po;0;ON; 002E 002E;;;;N;;;;; 2026;HORIZONTAL ELLIPSIS;Po;0;ON; 002E 002E 002E;;;;N;;;;; 2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;; 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; 202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;; 202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;; 202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;; 202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;; 202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;; 202F;NARROW NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;;;;; 2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;; 2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;; 2032;PRIME;Po;0;ET;;;;;N;;;;; 2033;DOUBLE PRIME;Po;0;ET; 2032 2032;;;;N;;;;; 2034;TRIPLE PRIME;Po;0;ET; 2032 2032 2032;;;;N;;;;; 2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;; 2036;REVERSED DOUBLE PRIME;Po;0;ON; 2035 2035;;;;N;;;;; 2037;REVERSED TRIPLE PRIME;Po;0;ON; 2035 2035 2035;;;;N;;;;; 2038;CARET;Po;0;ON;;;;;N;;;;; 2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;; 203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;; 203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;; 203C;DOUBLE EXCLAMATION MARK;Po;0;ON; 0021 0021;;;;N;;;;; 203D;INTERROBANG;Po;0;ON;;;;;N;;;;; 203E;OVERLINE;Po;0;ON; 0020 0305;;;;N;SPACING OVERSCORE;;;; 203F;UNDERTIE;Pc;0;ON;;;;;N;;;;; 2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;; 2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;; 2042;ASTERISM;Po;0;ON;;;;;N;;;;; 2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;; 2044;FRACTION SLASH;Sm;0;CS;;;;;N;;;;; 2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;; 2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;; 2047;DOUBLE QUESTION MARK;Po;0;ON; 003F 003F;;;;N;;;;; 2048;QUESTION EXCLAMATION MARK;Po;0;ON; 003F 0021;;;;N;;;;; 2049;EXCLAMATION QUESTION MARK;Po;0;ON; 0021 003F;;;;N;;;;; 204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;; 204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;; 204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;; 204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;; 204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;; 204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;; 2050;CLOSE UP;Po;0;ON;;;;;N;;;;; 2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;; 2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;; 2053;SWUNG DASH;Po;0;ON;;;;;N;;;;; 2054;INVERTED UNDERTIE;Pc;0;ON;;;;;N;;;;; 2055;FLOWER PUNCTUATION MARK;Po;0;ON;;;;;N;;;;; 2056;THREE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 2057;QUADRUPLE PRIME;Po;0;ON; 2032 2032 2032 2032;;;;N;;;;; 2058;FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 2059;FIVE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 205A;TWO DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 205B;FOUR DOT MARK;Po;0;ON;;;;;N;;;;; 205C;DOTTED CROSS;Po;0;ON;;;;;N;;;;; 205D;TRICOLON;Po;0;ON;;;;;N;;;;; 205E;VERTICAL FOUR DOTS;Po;0;ON;;;;;N;;;;; 205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS; 0020;;;;N;;;;; 2060;WORD JOINER;Cf;0;BN;;;;;N;;;;; 2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;; 2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;; 2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;; 2064;INVISIBLE PLUS;Cf;0;BN;;;;;N;;;;; 2066;LEFT-TO-RIGHT ISOLATE;Cf;0;LRI;;;;;N;;;;; 2067;RIGHT-TO-LEFT ISOLATE;Cf;0;RLI;;;;;N;;;;; 2068;FIRST STRONG ISOLATE;Cf;0;FSI;;;;;N;;;;; 2069;POP DIRECTIONAL ISOLATE;Cf;0;PDI;;;;;N;;;;; 206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; 206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; 206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; 206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; 206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; 206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; 2070;SUPERSCRIPT ZERO;No;0;EN; 0030;;0;0;N;SUPERSCRIPT DIGIT ZERO;;;; 2071;SUPERSCRIPT LATIN SMALL LETTER I;Lm;0;L; 0069;;;;N;;;;; 2074;SUPERSCRIPT FOUR;No;0;EN; 0034;;4;4;N;SUPERSCRIPT DIGIT FOUR;;;; 2075;SUPERSCRIPT FIVE;No;0;EN; 0035;;5;5;N;SUPERSCRIPT DIGIT FIVE;;;; 2076;SUPERSCRIPT SIX;No;0;EN; 0036;;6;6;N;SUPERSCRIPT DIGIT SIX;;;; 2077;SUPERSCRIPT SEVEN;No;0;EN; 0037;;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;; 2078;SUPERSCRIPT EIGHT;No;0;EN; 0038;;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;; 2079;SUPERSCRIPT NINE;No;0;EN; 0039;;9;9;N;SUPERSCRIPT DIGIT NINE;;;; 207A;SUPERSCRIPT PLUS SIGN;Sm;0;ES; 002B;;;;N;;;;; 207B;SUPERSCRIPT MINUS;Sm;0;ES; 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;; 207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON; 003D;;;;N;;;;; 207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON; 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;; 207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON; 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;; 207F;SUPERSCRIPT LATIN SMALL LETTER N;Lm;0;L; 006E;;;;N;;;;; 2080;SUBSCRIPT ZERO;No;0;EN; 0030;;0;0;N;SUBSCRIPT DIGIT ZERO;;;; 2081;SUBSCRIPT ONE;No;0;EN; 0031;;1;1;N;SUBSCRIPT DIGIT ONE;;;; 2082;SUBSCRIPT TWO;No;0;EN; 0032;;2;2;N;SUBSCRIPT DIGIT TWO;;;; 2083;SUBSCRIPT THREE;No;0;EN; 0033;;3;3;N;SUBSCRIPT DIGIT THREE;;;; 2084;SUBSCRIPT FOUR;No;0;EN; 0034;;4;4;N;SUBSCRIPT DIGIT FOUR;;;; 2085;SUBSCRIPT FIVE;No;0;EN; 0035;;5;5;N;SUBSCRIPT DIGIT FIVE;;;; 2086;SUBSCRIPT SIX;No;0;EN; 0036;;6;6;N;SUBSCRIPT DIGIT SIX;;;; 2087;SUBSCRIPT SEVEN;No;0;EN; 0037;;7;7;N;SUBSCRIPT DIGIT SEVEN;;;; 2088;SUBSCRIPT EIGHT;No;0;EN; 0038;;8;8;N;SUBSCRIPT DIGIT EIGHT;;;; 2089;SUBSCRIPT NINE;No;0;EN; 0039;;9;9;N;SUBSCRIPT DIGIT NINE;;;; 208A;SUBSCRIPT PLUS SIGN;Sm;0;ES; 002B;;;;N;;;;; 208B;SUBSCRIPT MINUS;Sm;0;ES; 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;; 208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON; 003D;;;;N;;;;; 208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON; 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;; 208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON; 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;; 2090;LATIN SUBSCRIPT SMALL LETTER A;Lm;0;L; 0061;;;;N;;;;; 2091;LATIN SUBSCRIPT SMALL LETTER E;Lm;0;L; 0065;;;;N;;;;; 2092;LATIN SUBSCRIPT SMALL LETTER O;Lm;0;L; 006F;;;;N;;;;; 2093;LATIN SUBSCRIPT SMALL LETTER X;Lm;0;L; 0078;;;;N;;;;; 2094;LATIN SUBSCRIPT SMALL LETTER SCHWA;Lm;0;L; 0259;;;;N;;;;; 2095;LATIN SUBSCRIPT SMALL LETTER H;Lm;0;L; 0068;;;;N;;;;; 2096;LATIN SUBSCRIPT SMALL LETTER K;Lm;0;L; 006B;;;;N;;;;; 2097;LATIN SUBSCRIPT SMALL LETTER L;Lm;0;L; 006C;;;;N;;;;; 2098;LATIN SUBSCRIPT SMALL LETTER M;Lm;0;L; 006D;;;;N;;;;; 2099;LATIN SUBSCRIPT SMALL LETTER N;Lm;0;L; 006E;;;;N;;;;; 209A;LATIN SUBSCRIPT SMALL LETTER P;Lm;0;L; 0070;;;;N;;;;; 209B;LATIN SUBSCRIPT SMALL LETTER S;Lm;0;L; 0073;;;;N;;;;; 209C;LATIN SUBSCRIPT SMALL LETTER T;Lm;0;L; 0074;;;;N;;;;; 20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; 20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;; 20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;; 20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;; 20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;; 20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;; 20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;; 20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;; 20A8;RUPEE SIGN;Sc;0;ET; 0052 0073;;;;N;;;;; 20A9;WON SIGN;Sc;0;ET;;;;;N;;;;; 20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;; 20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;; 20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;; 20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;; 20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;; 20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;; 20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;; 20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;; 20B2;GUARANI SIGN;Sc;0;ET;;;;;N;;;;; 20B3;AUSTRAL SIGN;Sc;0;ET;;;;;N;;;;; 20B4;HRYVNIA SIGN;Sc;0;ET;;;;;N;;;;; 20B5;CEDI SIGN;Sc;0;ET;;;;;N;;;;; 20B6;LIVRE TOURNOIS SIGN;Sc;0;ET;;;;;N;;;;; 20B7;SPESMILO SIGN;Sc;0;ET;;;;;N;;;;; 20B8;TENGE SIGN;Sc;0;ET;;;;;N;;;;; 20B9;INDIAN RUPEE SIGN;Sc;0;ET;;;;;N;;;;; 20BA;TURKISH LIRA SIGN;Sc;0;ET;;;;;N;;;;; 20BB;NORDIC MARK SIGN;Sc;0;ET;;;;;N;;;;; 20BC;MANAT SIGN;Sc;0;ET;;;;;N;;;;; 20BD;RUBLE SIGN;Sc;0;ET;;;;;N;;;;; 20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;; 20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;; 20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;; 20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;; 20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;; 20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;; 20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;; 20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;; 20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;; 20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;; 20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;; 20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;; 20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;; 20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;; 20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;; 20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;; 20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;; 20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;; 20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;; 20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;; 20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;; 20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;; 20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;; 20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;; 20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;; 20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;; 20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; 20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;; 20EB;COMBINING LONG DOUBLE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;; 20EC;COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;; 20ED;COMBINING LEFTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;; 20EE;COMBINING LEFT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; 20EF;COMBINING RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; 20F0;COMBINING ASTERISK ABOVE;Mn;230;NSM;;;;;N;;;;; 2100;ACCOUNT OF;So;0;ON; 0061 002F 0063;;;;N;;;;; 2101;ADDRESSED TO THE SUBJECT;So;0;ON; 0061 002F 0073;;;;N;;;;; 2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L; 0043;;;;N;DOUBLE-STRUCK C;;;; 2103;DEGREE CELSIUS;So;0;ON; 00B0 0043;;;;N;DEGREES CENTIGRADE;;;; 2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;; 2105;CARE OF;So;0;ON; 0063 002F 006F;;;;N;;;;; 2106;CADA UNA;So;0;ON; 0063 002F 0075;;;;N;;;;; 2107;EULER CONSTANT;Lu;0;L; 0190;;;;N;EULERS;;;; 2108;SCRUPLE;So;0;ON;;;;;N;;;;; 2109;DEGREE FAHRENHEIT;So;0;ON; 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;; 210A;SCRIPT SMALL G;Ll;0;L; 0067;;;;N;;;;; 210B;SCRIPT CAPITAL H;Lu;0;L; 0048;;;;N;SCRIPT H;;;; 210C;BLACK-LETTER CAPITAL H;Lu;0;L; 0048;;;;N;BLACK-LETTER H;;;; 210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L; 0048;;;;N;DOUBLE-STRUCK H;;;; 210E;PLANCK CONSTANT;Ll;0;L; 0068;;;;N;;;;; 210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L; 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;; 2110;SCRIPT CAPITAL I;Lu;0;L; 0049;;;;N;SCRIPT I;;;; 2111;BLACK-LETTER CAPITAL I;Lu;0;L; 0049;;;;N;BLACK-LETTER I;;;; 2112;SCRIPT CAPITAL L;Lu;0;L; 004C;;;;N;SCRIPT L;;;; 2113;SCRIPT SMALL L;Ll;0;L; 006C;;;;N;;;;; 2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;; 2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L; 004E;;;;N;DOUBLE-STRUCK N;;;; 2116;NUMERO SIGN;So;0;ON; 004E 006F;;;;N;NUMERO;;;; 2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;; 2118;SCRIPT CAPITAL P;Sm;0;ON;;;;;N;SCRIPT P;;;; 2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L; 0050;;;;N;DOUBLE-STRUCK P;;;; 211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L; 0051;;;;N;DOUBLE-STRUCK Q;;;; 211B;SCRIPT CAPITAL R;Lu;0;L; 0052;;;;N;SCRIPT R;;;; 211C;BLACK-LETTER CAPITAL R;Lu;0;L; 0052;;;;N;BLACK-LETTER R;;;; 211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L; 0052;;;;N;DOUBLE-STRUCK R;;;; 211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;; 211F;RESPONSE;So;0;ON;;;;;N;;;;; 2120;SERVICE MARK;So;0;ON; 0053 004D;;;;N;;;;; 2121;TELEPHONE SIGN;So;0;ON; 0054 0045 004C;;;;N;T E L SYMBOL;;;; 2122;TRADE MARK SIGN;So;0;ON; 0054 004D;;;;N;TRADEMARK;;;; 2123;VERSICLE;So;0;ON;;;;;N;;;;; 2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L; 005A;;;;N;DOUBLE-STRUCK Z;;;; 2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;; 2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9; 2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;; 2128;BLACK-LETTER CAPITAL Z;Lu;0;L; 005A;;;;N;BLACK-LETTER Z;;;; 2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;; 212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B; 212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5; 212C;SCRIPT CAPITAL B;Lu;0;L; 0042;;;;N;SCRIPT B;;;; 212D;BLACK-LETTER CAPITAL C;Lu;0;L; 0043;;;;N;BLACK-LETTER C;;;; 212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;; 212F;SCRIPT SMALL E;Ll;0;L; 0065;;;;N;;;;; 2130;SCRIPT CAPITAL E;Lu;0;L; 0045;;;;N;SCRIPT E;;;; 2131;SCRIPT CAPITAL F;Lu;0;L; 0046;;;;N;SCRIPT F;;;; 2132;TURNED CAPITAL F;Lu;0;L;;;;;N;TURNED F;;;214E; 2133;SCRIPT CAPITAL M;Lu;0;L; 004D;;;;N;SCRIPT M;;;; 2134;SCRIPT SMALL O;Ll;0;L; 006F;;;;N;;;;; 2135;ALEF SYMBOL;Lo;0;L; 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;; 2136;BET SYMBOL;Lo;0;L; 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;; 2137;GIMEL SYMBOL;Lo;0;L; 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;; 2138;DALET SYMBOL;Lo;0;L; 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;; 2139;INFORMATION SOURCE;Ll;0;L; 0069;;;;N;;;;; 213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;; 213B;FACSIMILE SIGN;So;0;ON; 0046 0041 0058;;;;N;;;;; 213C;DOUBLE-STRUCK SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON; 2211;;;;Y;;;;; 2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;; 2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; 2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; 2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;; 2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L; 0064;;;;N;;;;; 2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L; 0065;;;;N;;;;; 2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L; 0069;;;;N;;;;; 2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L; 006A;;;;N;;;;; 214A;PROPERTY LINE;So;0;ON;;;;;N;;;;; 214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;; 214C;PER SIGN;So;0;ON;;;;;N;;;;; 214D;AKTIESELSKAB;So;0;ON;;;;;N;;;;; 214E;TURNED SMALL F;Ll;0;L;;;;;N;;;2132;;2132 214F;SYMBOL FOR SAMARITAN SOURCE;So;0;L;;;;;N;;;;; 2150;VULGAR FRACTION ONE SEVENTH;No;0;ON; 0031 2044 0037;;;1/7;N;;;;; 2151;VULGAR FRACTION ONE NINTH;No;0;ON; 0031 2044 0039;;;1/9;N;;;;; 2152;VULGAR FRACTION ONE TENTH;No;0;ON; 0031 2044 0031 0030;;;1/10;N;;;;; 2153;VULGAR FRACTION ONE THIRD;No;0;ON; 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;; 2154;VULGAR FRACTION TWO THIRDS;No;0;ON; 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;; 2155;VULGAR FRACTION ONE FIFTH;No;0;ON; 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;; 2156;VULGAR FRACTION TWO FIFTHS;No;0;ON; 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;; 2157;VULGAR FRACTION THREE FIFTHS;No;0;ON; 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;; 2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON; 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;; 2159;VULGAR FRACTION ONE SIXTH;No;0;ON; 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;; 215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON; 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;; 215B;VULGAR FRACTION ONE EIGHTH;No;0;ON; 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;; 215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON; 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;; 215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON; 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;; 215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON; 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;; 215F;FRACTION NUMERATOR ONE;No;0;ON; 0031 2044;;;1;N;;;;; 2160;ROMAN NUMERAL ONE;Nl;0;L; 0049;;;1;N;;;;2170; 2161;ROMAN NUMERAL TWO;Nl;0;L; 0049 0049;;;2;N;;;;2171; 2162;ROMAN NUMERAL THREE;Nl;0;L; 0049 0049 0049;;;3;N;;;;2172; 2163;ROMAN NUMERAL FOUR;Nl;0;L; 0049 0056;;;4;N;;;;2173; 2164;ROMAN NUMERAL FIVE;Nl;0;L; 0056;;;5;N;;;;2174; 2165;ROMAN NUMERAL SIX;Nl;0;L; 0056 0049;;;6;N;;;;2175; 2166;ROMAN NUMERAL SEVEN;Nl;0;L; 0056 0049 0049;;;7;N;;;;2176; 2167;ROMAN NUMERAL EIGHT;Nl;0;L; 0056 0049 0049 0049;;;8;N;;;;2177; 2168;ROMAN NUMERAL NINE;Nl;0;L; 0049 0058;;;9;N;;;;2178; 2169;ROMAN NUMERAL TEN;Nl;0;L; 0058;;;10;N;;;;2179; 216A;ROMAN NUMERAL ELEVEN;Nl;0;L; 0058 0049;;;11;N;;;;217A; 216B;ROMAN NUMERAL TWELVE;Nl;0;L; 0058 0049 0049;;;12;N;;;;217B; 216C;ROMAN NUMERAL FIFTY;Nl;0;L; 004C;;;50;N;;;;217C; 216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L; 0043;;;100;N;;;;217D; 216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L; 0044;;;500;N;;;;217E; 216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L; 004D;;;1000;N;;;;217F; 2170;SMALL ROMAN NUMERAL ONE;Nl;0;L; 0069;;;1;N;;;2160;;2160 2171;SMALL ROMAN NUMERAL TWO;Nl;0;L; 0069 0069;;;2;N;;;2161;;2161 2172;SMALL ROMAN NUMERAL THREE;Nl;0;L; 0069 0069 0069;;;3;N;;;2162;;2162 2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L; 0069 0076;;;4;N;;;2163;;2163 2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L; 0076;;;5;N;;;2164;;2164 2175;SMALL ROMAN NUMERAL SIX;Nl;0;L; 0076 0069;;;6;N;;;2165;;2165 2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L; 0076 0069 0069;;;7;N;;;2166;;2166 2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L; 0076 0069 0069 0069;;;8;N;;;2167;;2167 2178;SMALL ROMAN NUMERAL NINE;Nl;0;L; 0069 0078;;;9;N;;;2168;;2168 2179;SMALL ROMAN NUMERAL TEN;Nl;0;L; 0078;;;10;N;;;2169;;2169 217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L; 0078 0069;;;11;N;;;216A;;216A 217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L; 0078 0069 0069;;;12;N;;;216B;;216B 217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L; 006C;;;50;N;;;216C;;216C 217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L; 0063;;;100;N;;;216D;;216D 217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L; 0064;;;500;N;;;216E;;216E 217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L; 006D;;;1000;N;;;216F;;216F 2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;; 2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;; 2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;; 2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Lu;0;L;;;;;N;;;;2184; 2184;LATIN SMALL LETTER REVERSED C;Ll;0;L;;;;;N;;;2183;;2183 2185;ROMAN NUMERAL SIX LATE FORM;Nl;0;L;;;;6;N;;;;; 2186;ROMAN NUMERAL FIFTY EARLY FORM;Nl;0;L;;;;50;N;;;;; 2187;ROMAN NUMERAL FIFTY THOUSAND;Nl;0;L;;;;50000;N;;;;; 2188;ROMAN NUMERAL ONE HUNDRED THOUSAND;Nl;0;L;;;;100000;N;;;;; 2189;VULGAR FRACTION ZERO THIRDS;No;0;ON; 0030 2044 0033;;;0;N;;;;; 218A;TURNED DIGIT TWO;So;0;ON;;;;;N;;;;; 218B;TURNED DIGIT THREE;So;0;ON;;;;;N;;;;; 2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;; 2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;; 2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;; 2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;; 2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; 2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;; 2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;; 2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;; 2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;; 2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;; 219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;; 219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;; 219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;; 219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;; 219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;; 219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;; 21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;; 21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;; 21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;; 21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;; 21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;; 21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;; 21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;; 21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;; 21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;; 21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;; 21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;; 21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;; 21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;; 21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;; 21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;; 21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;; 21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;; 21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;; 21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;; 21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;; 21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;; 21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;; 21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; 21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; 21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;; 21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;; 21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; 21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; 21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;; 21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;; 21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;; 21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;; 21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;; 21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;; 21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;; 21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;; 21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;; 21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;; 21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;; 21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;; 21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;; 21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;; 21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;; 21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;; 21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;; 21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;; 21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;; 21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;; 21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;; 21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;; 21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;; 21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;; 21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; 21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;; 21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;; 21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;; 21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;; 21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;; 21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;; 21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;; 21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;; 21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;; 21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;; 21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;; 21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;; 21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;; 21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;; 21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;; 21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;; 21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;; 21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;; 21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;; 21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;; 21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;; 21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;; 21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; 21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;; 21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; 21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;; 21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; 21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;; 21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;; 21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;; 21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;; 21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; 21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;; 21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;; 21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; 21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; 21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; 2200;FOR ALL;Sm;0;ON;;;;;N;;;;; 2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;; 2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;; 2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;; 2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;; 2205;EMPTY SET;Sm;0;ON;;;;;N;;;;; 2206;INCREMENT;Sm;0;ON;;;;;N;;;;; 2207;NABLA;Sm;0;ON;;;;;N;;;;; 2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;; 2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;; 220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;; 220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; 220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;; 220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; 220E;END OF PROOF;Sm;0;ON;;;;;N;;;;; 220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;; 2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;; 2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;; 2212;MINUS SIGN;Sm;0;ES;;;;;N;;;;; 2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;; 2214;DOT PLUS;Sm;0;ON;;;;;N;;;;; 2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; 2216;SET MINUS;Sm;0;ON;;;;;Y;;;;; 2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; 2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;; 2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;; 221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;; 221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;; 221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;; 221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;; 221E;INFINITY;Sm;0;ON;;;;;N;;;;; 221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;; 2220;ANGLE;Sm;0;ON;;;;;Y;;;;; 2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;; 2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;; 2223;DIVIDES;Sm;0;ON;;;;;N;;;;; 2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;; 2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;; 2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;; 2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;; 2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;; 2229;INTERSECTION;Sm;0;ON;;;;;N;;;;; 222A;UNION;Sm;0;ON;;;;;N;;;;; 222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;; 222C;DOUBLE INTEGRAL;Sm;0;ON; 222B 222B;;;;Y;;;;; 222D;TRIPLE INTEGRAL;Sm;0;ON; 222B 222B 222B;;;;Y;;;;; 222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; 222F;SURFACE INTEGRAL;Sm;0;ON; 222E 222E;;;;Y;;;;; 2230;VOLUME INTEGRAL;Sm;0;ON; 222E 222E 222E;;;;Y;;;;; 2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2234;THEREFORE;Sm;0;ON;;;;;N;;;;; 2235;BECAUSE;Sm;0;ON;;;;;N;;;;; 2236;RATIO;Sm;0;ON;;;;;N;;;;; 2237;PROPORTION;Sm;0;ON;;;;;N;;;;; 2238;DOT MINUS;Sm;0;ON;;;;;N;;;;; 2239;EXCESS;Sm;0;ON;;;;;Y;;;;; 223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;; 223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;; 223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; 223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;;;; 223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;; 223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;; 2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;; 2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;; 2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;; 2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;; 2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;; 2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;; 224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;; 224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;; 224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; 224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; 224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;; 2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;; 2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;; 2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;; 2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;; 2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;; 2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;; 2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;; 2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;; 2259;ESTIMATES;Sm;0;ON;;;;;N;;;;; 225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;; 225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;; 225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;; 225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;; 225E;MEASURED BY;Sm;0;ON;;;;;N;;;;; 225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;; 2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;; 2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;; 2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; 2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;; 2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;; 2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;; 2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;; 2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;; 2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;; 226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;; 226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;; 226C;BETWEEN;Sm;0;ON;;;;;N;;;;; 226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;; 226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;; 226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;; 2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;; 2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;; 2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;; 2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;; 2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;; 2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;; 2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;; 2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;; 2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;; 2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;; 227A;PRECEDES;Sm;0;ON;;;;;Y;;;;; 227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;; 227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; 227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; 2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;; 2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;; 2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;; 2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;; 2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;; 2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;; 2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;; 2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;; 228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;; 228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;; 228C;MULTISET;Sm;0;ON;;;;;Y;;;;; 228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;; 228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;; 228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;; 2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; 2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;; 2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;; 2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; 2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;; 2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;; 2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; 2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; 229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;; 229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; 229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;; 229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;; 229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;; 229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;; 22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;; 22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; 22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;; 22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;; 22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;; 22A5;UP TACK;Sm;0;ON;;;;;N;;;;; 22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;; 22A7;MODELS;Sm;0;ON;;;;;Y;;;;; 22A8;TRUE;Sm;0;ON;;;;;Y;;;;; 22A9;FORCES;Sm;0;ON;;;;;Y;;;;; 22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;; 22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;; 22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;; 22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;; 22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;; 22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;; 22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;; 22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;; 22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; 22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;; 22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;; 22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;; 22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;; 22BB;XOR;Sm;0;ON;;;;;N;;;;; 22BC;NAND;Sm;0;ON;;;;;N;;;;; 22BD;NOR;Sm;0;ON;;;;;N;;;;; 22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;; 22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; 22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;; 22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;; 22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;; 22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;; 22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;; 22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;; 22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;; 22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;; 22C8;BOWTIE;Sm;0;ON;;;;;N;;;;; 22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; 22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; 22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; 22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; 22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;; 22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;; 22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;; 22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;; 22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;; 22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;; 22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;; 22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;; 22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;; 22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;; 22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;; 22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;; 22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;; 22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;; 22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;; 22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;; 22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;; 22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;; 22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;; 22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;; 22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;; 22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;; 22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;; 22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;; 22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;; 22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; 22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; 22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;; 22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;; 22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;; 22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;; 22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; 22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; 22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; 22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; 22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; 22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;; 22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;; 2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;; 2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;; 2302;HOUSE;So;0;ON;;;;;N;;;;; 2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;; 2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;; 2305;PROJECTIVE;So;0;ON;;;;;N;;;;; 2306;PERSPECTIVE;So;0;ON;;;;;N;;;;; 2307;WAVY LINE;So;0;ON;;;;;N;;;;; 2308;LEFT CEILING;Ps;0;ON;;;;;Y;;;;; 2309;RIGHT CEILING;Pe;0;ON;;;;;Y;;;;; 230A;LEFT FLOOR;Ps;0;ON;;;;;Y;;;;; 230B;RIGHT FLOOR;Pe;0;ON;;;;;Y;;;;; 230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;; 230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;; 230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;; 230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;; 2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;; 2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;; 2312;ARC;So;0;ON;;;;;N;;;;; 2313;SEGMENT;So;0;ON;;;;;N;;;;; 2314;SECTOR;So;0;ON;;;;;N;;;;; 2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;; 2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;; 2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;; 2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;; 2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;; 231A;WATCH;So;0;ON;;;;;N;;;;; 231B;HOURGLASS;So;0;ON;;;;;N;;;;; 231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;; 231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;; 231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;; 231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;; 2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2322;FROWN;So;0;ON;;;;;N;;;;; 2323;SMILE;So;0;ON;;;;;N;;;;; 2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;; 2325;OPTION KEY;So;0;ON;;;;;N;;;;; 2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;; 2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;; 2328;KEYBOARD;So;0;ON;;;;;N;;;;; 2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;; 232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;; 232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;; 232C;BENZENE RING;So;0;ON;;;;;N;;;;; 232D;CYLINDRICITY;So;0;ON;;;;;N;;;;; 232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;; 232F;SYMMETRY;So;0;ON;;;;;N;;;;; 2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;; 2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;; 2332;CONICAL TAPER;So;0;ON;;;;;N;;;;; 2333;SLOPE;So;0;ON;;;;;N;;;;; 2334;COUNTERBORE;So;0;ON;;;;;N;;;;; 2335;COUNTERSINK;So;0;ON;;;;;N;;;;; 2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;; 2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;; 2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;; 2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;; 233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;; 233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;; 233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;; 233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;; 233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;; 233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;; 2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;; 2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;; 2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;; 2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;; 2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;; 2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;; 2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;; 2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;; 2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;; 2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;; 234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;;;; 234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;; 234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;; 234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;; 234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;;;; 234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;; 2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;; 2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;;;; 2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;; 2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;; 2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;; 2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;;;; 2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;; 2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;; 2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;; 2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;; 235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;; 235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;; 235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;; 235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;; 235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;; 235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;; 2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;; 2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;;;; 2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;; 2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;; 2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;; 2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;; 2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;; 2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;; 2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;; 2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;; 236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;; 236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;; 236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;; 236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;; 236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;; 236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;; 2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;; 2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;; 2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;; 2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;; 2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;; 2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;; 2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;; 2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;; 2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;; 2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;; 237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;; 237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;; 237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;; 237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;; 237E;BELL SYMBOL;So;0;ON;;;;;N;;;;; 237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;; 2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;; 2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; 2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; 2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;; 2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;; 2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;; 2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;; 2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;; 2388;HELM SYMBOL;So;0;ON;;;;;N;;;;; 2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;;;; 238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;;;; 238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;;;; 238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;; 238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;; 238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;; 238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;; 2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;; 2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; 2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; 2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;; 2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;; 2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;; 2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;; 2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;; 2398;NEXT PAGE;So;0;ON;;;;;N;;;;; 2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;; 239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;; 239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; 239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; 239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; 239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; 239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; 23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; 23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; 23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; 23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; 23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; 23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; 23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; 23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; 23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; 23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; 23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; 23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; 23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; 23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; 23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;; 23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;; 23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; 23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; 23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;; 23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;; 23B4;TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;; 23B5;BOTTOM SQUARE BRACKET;So;0;ON;;;;;N;;;;; 23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;; 23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;; 23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; 23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; 23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;; 23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;; 23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;; 23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;; 23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;; 23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;; 23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;; 23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; 23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; 23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; 23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; 23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; 23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;; 23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; 23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; 23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;; 23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;; 23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;; 23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;; 23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;; 23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;; 23CF;EJECT SYMBOL;So;0;ON;;;;;N;;;;; 23D0;VERTICAL LINE EXTENSION;So;0;ON;;;;;N;;;;; 23D1;METRICAL BREVE;So;0;ON;;;;;N;;;;; 23D2;METRICAL LONG OVER SHORT;So;0;ON;;;;;N;;;;; 23D3;METRICAL SHORT OVER LONG;So;0;ON;;;;;N;;;;; 23D4;METRICAL LONG OVER TWO SHORTS;So;0;ON;;;;;N;;;;; 23D5;METRICAL TWO SHORTS OVER LONG;So;0;ON;;;;;N;;;;; 23D6;METRICAL TWO SHORTS JOINED;So;0;ON;;;;;N;;;;; 23D7;METRICAL TRISEME;So;0;ON;;;;;N;;;;; 23D8;METRICAL TETRASEME;So;0;ON;;;;;N;;;;; 23D9;METRICAL PENTASEME;So;0;ON;;;;;N;;;;; 23DA;EARTH GROUND;So;0;ON;;;;;N;;;;; 23DB;FUSE;So;0;ON;;;;;N;;;;; 23DC;TOP PARENTHESIS;Sm;0;ON;;;;;N;;;;; 23DD;BOTTOM PARENTHESIS;Sm;0;ON;;;;;N;;;;; 23DE;TOP CURLY BRACKET;Sm;0;ON;;;;;N;;;;; 23DF;BOTTOM CURLY BRACKET;Sm;0;ON;;;;;N;;;;; 23E0;TOP TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;; 23E1;BOTTOM TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;; 23E2;WHITE TRAPEZIUM;So;0;ON;;;;;N;;;;; 23E3;BENZENE RING WITH CIRCLE;So;0;ON;;;;;N;;;;; 23E4;STRAIGHTNESS;So;0;ON;;;;;N;;;;; 23E5;FLATNESS;So;0;ON;;;;;N;;;;; 23E6;AC CURRENT;So;0;ON;;;;;N;;;;; 23E7;ELECTRICAL INTERSECTION;So;0;ON;;;;;N;;;;; 23E8;DECIMAL EXPONENT SYMBOL;So;0;ON;;;;;N;;;;; 23E9;BLACK RIGHT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; 23EA;BLACK LEFT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; 23EB;BLACK UP-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; 23EC;BLACK DOWN-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; 23ED;BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; 23EE;BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; 23EF;BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;; 23F0;ALARM CLOCK;So;0;ON;;;;;N;;;;; 23F1;STOPWATCH;So;0;ON;;;;;N;;;;; 23F2;TIMER CLOCK;So;0;ON;;;;;N;;;;; 23F3;HOURGLASS WITH FLOWING SAND;So;0;ON;;;;;N;;;;; 23F4;BLACK MEDIUM LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; 23F5;BLACK MEDIUM RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; 23F6;BLACK MEDIUM UP-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; 23F7;BLACK MEDIUM DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; 23F8;DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;; 23F9;BLACK SQUARE FOR STOP;So;0;ON;;;;;N;;;;; 23FA;BLACK CIRCLE FOR RECORD;So;0;ON;;;;;N;;;;; 23FB;POWER SYMBOL;So;0;ON;;;;;N;;;;; 23FC;POWER ON-OFF SYMBOL;So;0;ON;;;;;N;;;;; 23FD;POWER ON SYMBOL;So;0;ON;;;;;N;;;;; 23FE;POWER SLEEP SYMBOL;So;0;ON;;;;;N;;;;; 2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;; 2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;; 2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;; 2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;; 2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;; 2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;; 2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;; 2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;; 2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;; 2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;; 240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;; 240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;; 240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;; 240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;; 240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;; 240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;; 2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;; 2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;; 2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;; 2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;; 2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;; 2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;; 2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;; 2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;; 2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;; 2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;; 241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;; 241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;; 241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;; 241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;; 241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;; 241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;; 2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;; 2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;; 2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;; 2423;OPEN BOX;So;0;ON;;;;;N;;;;; 2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;; 2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;; 2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;; 2440;OCR HOOK;So;0;ON;;;;;N;;;;; 2441;OCR CHAIR;So;0;ON;;;;;N;;;;; 2442;OCR FORK;So;0;ON;;;;;N;;;;; 2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;; 2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;; 2445;OCR BOW TIE;So;0;ON;;;;;N;;;;; 2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;; 2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;; 2448;OCR DASH;So;0;ON;;;;;N;;;;; 2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;; 244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;; 2460;CIRCLED DIGIT ONE;No;0;ON; 0031;;1;1;N;;;;; 2461;CIRCLED DIGIT TWO;No;0;ON; 0032;;2;2;N;;;;; 2462;CIRCLED DIGIT THREE;No;0;ON; 0033;;3;3;N;;;;; 2463;CIRCLED DIGIT FOUR;No;0;ON; 0034;;4;4;N;;;;; 2464;CIRCLED DIGIT FIVE;No;0;ON; 0035;;5;5;N;;;;; 2465;CIRCLED DIGIT SIX;No;0;ON; 0036;;6;6;N;;;;; 2466;CIRCLED DIGIT SEVEN;No;0;ON; 0037;;7;7;N;;;;; 2467;CIRCLED DIGIT EIGHT;No;0;ON; 0038;;8;8;N;;;;; 2468;CIRCLED DIGIT NINE;No;0;ON; 0039;;9;9;N;;;;; 2469;CIRCLED NUMBER TEN;No;0;ON; 0031 0030;;;10;N;;;;; 246A;CIRCLED NUMBER ELEVEN;No;0;ON; 0031 0031;;;11;N;;;;; 246B;CIRCLED NUMBER TWELVE;No;0;ON; 0031 0032;;;12;N;;;;; 246C;CIRCLED NUMBER THIRTEEN;No;0;ON; 0031 0033;;;13;N;;;;; 246D;CIRCLED NUMBER FOURTEEN;No;0;ON; 0031 0034;;;14;N;;;;; 246E;CIRCLED NUMBER FIFTEEN;No;0;ON; 0031 0035;;;15;N;;;;; 246F;CIRCLED NUMBER SIXTEEN;No;0;ON; 0031 0036;;;16;N;;;;; 2470;CIRCLED NUMBER SEVENTEEN;No;0;ON; 0031 0037;;;17;N;;;;; 2471;CIRCLED NUMBER EIGHTEEN;No;0;ON; 0031 0038;;;18;N;;;;; 2472;CIRCLED NUMBER NINETEEN;No;0;ON; 0031 0039;;;19;N;;;;; 2473;CIRCLED NUMBER TWENTY;No;0;ON; 0032 0030;;;20;N;;;;; 2474;PARENTHESIZED DIGIT ONE;No;0;ON; 0028 0031 0029;;1;1;N;;;;; 2475;PARENTHESIZED DIGIT TWO;No;0;ON; 0028 0032 0029;;2;2;N;;;;; 2476;PARENTHESIZED DIGIT THREE;No;0;ON; 0028 0033 0029;;3;3;N;;;;; 2477;PARENTHESIZED DIGIT FOUR;No;0;ON; 0028 0034 0029;;4;4;N;;;;; 2478;PARENTHESIZED DIGIT FIVE;No;0;ON; 0028 0035 0029;;5;5;N;;;;; 2479;PARENTHESIZED DIGIT SIX;No;0;ON; 0028 0036 0029;;6;6;N;;;;; 247A;PARENTHESIZED DIGIT SEVEN;No;0;ON; 0028 0037 0029;;7;7;N;;;;; 247B;PARENTHESIZED DIGIT EIGHT;No;0;ON; 0028 0038 0029;;8;8;N;;;;; 247C;PARENTHESIZED DIGIT NINE;No;0;ON; 0028 0039 0029;;9;9;N;;;;; 247D;PARENTHESIZED NUMBER TEN;No;0;ON; 0028 0031 0030 0029;;;10;N;;;;; 247E;PARENTHESIZED NUMBER ELEVEN;No;0;ON; 0028 0031 0031 0029;;;11;N;;;;; 247F;PARENTHESIZED NUMBER TWELVE;No;0;ON; 0028 0031 0032 0029;;;12;N;;;;; 2480;PARENTHESIZED NUMBER THIRTEEN;No;0;ON; 0028 0031 0033 0029;;;13;N;;;;; 2481;PARENTHESIZED NUMBER FOURTEEN;No;0;ON; 0028 0031 0034 0029;;;14;N;;;;; 2482;PARENTHESIZED NUMBER FIFTEEN;No;0;ON; 0028 0031 0035 0029;;;15;N;;;;; 2483;PARENTHESIZED NUMBER SIXTEEN;No;0;ON; 0028 0031 0036 0029;;;16;N;;;;; 2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;ON; 0028 0031 0037 0029;;;17;N;;;;; 2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;ON; 0028 0031 0038 0029;;;18;N;;;;; 2486;PARENTHESIZED NUMBER NINETEEN;No;0;ON; 0028 0031 0039 0029;;;19;N;;;;; 2487;PARENTHESIZED NUMBER TWENTY;No;0;ON; 0028 0032 0030 0029;;;20;N;;;;; 2488;DIGIT ONE FULL STOP;No;0;EN; 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;; 2489;DIGIT TWO FULL STOP;No;0;EN; 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;; 248A;DIGIT THREE FULL STOP;No;0;EN; 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;; 248B;DIGIT FOUR FULL STOP;No;0;EN; 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;; 248C;DIGIT FIVE FULL STOP;No;0;EN; 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;; 248D;DIGIT SIX FULL STOP;No;0;EN; 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;; 248E;DIGIT SEVEN FULL STOP;No;0;EN; 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;; 248F;DIGIT EIGHT FULL STOP;No;0;EN; 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;; 2490;DIGIT NINE FULL STOP;No;0;EN; 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;; 2491;NUMBER TEN FULL STOP;No;0;EN; 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;; 2492;NUMBER ELEVEN FULL STOP;No;0;EN; 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;; 2493;NUMBER TWELVE FULL STOP;No;0;EN; 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;; 2494;NUMBER THIRTEEN FULL STOP;No;0;EN; 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;; 2495;NUMBER FOURTEEN FULL STOP;No;0;EN; 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;; 2496;NUMBER FIFTEEN FULL STOP;No;0;EN; 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;; 2497;NUMBER SIXTEEN FULL STOP;No;0;EN; 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;; 2498;NUMBER SEVENTEEN FULL STOP;No;0;EN; 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;; 2499;NUMBER EIGHTEEN FULL STOP;No;0;EN; 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;; 249A;NUMBER NINETEEN FULL STOP;No;0;EN; 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;; 249B;NUMBER TWENTY FULL STOP;No;0;EN; 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;; 249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L; 0028 0061 0029;;;;N;;;;; 249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L; 0028 0062 0029;;;;N;;;;; 249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L; 0028 0063 0029;;;;N;;;;; 249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L; 0028 0064 0029;;;;N;;;;; 24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L; 0028 0065 0029;;;;N;;;;; 24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L; 0028 0066 0029;;;;N;;;;; 24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L; 0028 0067 0029;;;;N;;;;; 24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L; 0028 0068 0029;;;;N;;;;; 24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L; 0028 0069 0029;;;;N;;;;; 24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L; 0028 006A 0029;;;;N;;;;; 24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L; 0028 006B 0029;;;;N;;;;; 24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L; 0028 006C 0029;;;;N;;;;; 24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L; 0028 006D 0029;;;;N;;;;; 24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L; 0028 006E 0029;;;;N;;;;; 24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L; 0028 006F 0029;;;;N;;;;; 24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L; 0028 0070 0029;;;;N;;;;; 24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L; 0028 0071 0029;;;;N;;;;; 24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L; 0028 0072 0029;;;;N;;;;; 24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L; 0028 0073 0029;;;;N;;;;; 24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L; 0028 0074 0029;;;;N;;;;; 24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L; 0028 0075 0029;;;;N;;;;; 24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L; 0028 0076 0029;;;;N;;;;; 24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L; 0028 0077 0029;;;;N;;;;; 24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L; 0028 0078 0029;;;;N;;;;; 24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L; 0028 0079 0029;;;;N;;;;; 24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L; 0028 007A 0029;;;;N;;;;; 24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L; 0041;;;;N;;;;24D0; 24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L; 0042;;;;N;;;;24D1; 24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L; 0043;;;;N;;;;24D2; 24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L; 0044;;;;N;;;;24D3; 24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L; 0045;;;;N;;;;24D4; 24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L; 0046;;;;N;;;;24D5; 24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L; 0047;;;;N;;;;24D6; 24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L; 0048;;;;N;;;;24D7; 24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L; 0049;;;;N;;;;24D8; 24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L; 004A;;;;N;;;;24D9; 24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L; 004B;;;;N;;;;24DA; 24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L; 004C;;;;N;;;;24DB; 24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L; 004D;;;;N;;;;24DC; 24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L; 004E;;;;N;;;;24DD; 24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L; 004F;;;;N;;;;24DE; 24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L; 0050;;;;N;;;;24DF; 24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L; 0051;;;;N;;;;24E0; 24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L; 0052;;;;N;;;;24E1; 24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L; 0053;;;;N;;;;24E2; 24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L; 0054;;;;N;;;;24E3; 24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L; 0055;;;;N;;;;24E4; 24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L; 0056;;;;N;;;;24E5; 24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L; 0057;;;;N;;;;24E6; 24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L; 0058;;;;N;;;;24E7; 24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L; 0059;;;;N;;;;24E8; 24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L; 005A;;;;N;;;;24E9; 24D0;CIRCLED LATIN SMALL LETTER A;So;0;L; 0061;;;;N;;;24B6;;24B6 24D1;CIRCLED LATIN SMALL LETTER B;So;0;L; 0062;;;;N;;;24B7;;24B7 24D2;CIRCLED LATIN SMALL LETTER C;So;0;L; 0063;;;;N;;;24B8;;24B8 24D3;CIRCLED LATIN SMALL LETTER D;So;0;L; 0064;;;;N;;;24B9;;24B9 24D4;CIRCLED LATIN SMALL LETTER E;So;0;L; 0065;;;;N;;;24BA;;24BA 24D5;CIRCLED LATIN SMALL LETTER F;So;0;L; 0066;;;;N;;;24BB;;24BB 24D6;CIRCLED LATIN SMALL LETTER G;So;0;L; 0067;;;;N;;;24BC;;24BC 24D7;CIRCLED LATIN SMALL LETTER H;So;0;L; 0068;;;;N;;;24BD;;24BD 24D8;CIRCLED LATIN SMALL LETTER I;So;0;L; 0069;;;;N;;;24BE;;24BE 24D9;CIRCLED LATIN SMALL LETTER J;So;0;L; 006A;;;;N;;;24BF;;24BF 24DA;CIRCLED LATIN SMALL LETTER K;So;0;L; 006B;;;;N;;;24C0;;24C0 24DB;CIRCLED LATIN SMALL LETTER L;So;0;L; 006C;;;;N;;;24C1;;24C1 24DC;CIRCLED LATIN SMALL LETTER M;So;0;L; 006D;;;;N;;;24C2;;24C2 24DD;CIRCLED LATIN SMALL LETTER N;So;0;L; 006E;;;;N;;;24C3;;24C3 24DE;CIRCLED LATIN SMALL LETTER O;So;0;L; 006F;;;;N;;;24C4;;24C4 24DF;CIRCLED LATIN SMALL LETTER P;So;0;L; 0070;;;;N;;;24C5;;24C5 24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L; 0071;;;;N;;;24C6;;24C6 24E1;CIRCLED LATIN SMALL LETTER R;So;0;L; 0072;;;;N;;;24C7;;24C7 24E2;CIRCLED LATIN SMALL LETTER S;So;0;L; 0073;;;;N;;;24C8;;24C8 24E3;CIRCLED LATIN SMALL LETTER T;So;0;L; 0074;;;;N;;;24C9;;24C9 24E4;CIRCLED LATIN SMALL LETTER U;So;0;L; 0075;;;;N;;;24CA;;24CA 24E5;CIRCLED LATIN SMALL LETTER V;So;0;L; 0076;;;;N;;;24CB;;24CB 24E6;CIRCLED LATIN SMALL LETTER W;So;0;L; 0077;;;;N;;;24CC;;24CC 24E7;CIRCLED LATIN SMALL LETTER X;So;0;L; 0078;;;;N;;;24CD;;24CD 24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L; 0079;;;;N;;;24CE;;24CE 24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L; 007A;;;;N;;;24CF;;24CF 24EA;CIRCLED DIGIT ZERO;No;0;ON; 0030;;0;0;N;;;;; 24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;; 24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;; 24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;; 24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;; 24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;; 24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;; 24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;; 24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;; 24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;; 24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;; 24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;; 24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;; 24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;; 24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;; 24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;; 24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;; 24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;; 24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;; 24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;; 24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;; 24FF;NEGATIVE CIRCLED DIGIT ZERO;No;0;ON;;;0;0;N;;;;; 2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;; 2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;; 2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;; 2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;; 2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;; 2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;; 2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;; 2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;; 2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;; 2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;; 250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;; 250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;; 250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;; 250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;; 250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;; 250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;; 2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;; 2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;; 2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;; 2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;; 2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;; 2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;; 2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;; 2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;; 2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;; 2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;; 251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;; 251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;; 251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;; 251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;; 251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;; 251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;; 2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;; 2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;; 2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;; 2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;; 2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;; 2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;; 2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;; 2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;; 2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;; 2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;; 252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;; 252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;; 252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;; 252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;; 252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;; 252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;; 2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;; 2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;; 2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;; 2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;; 2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;; 2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;; 2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;; 2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;; 2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;; 2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;; 253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;; 253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;; 253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;; 253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;; 253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;; 253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;; 2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;; 2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;; 2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;; 2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;; 2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;; 2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;; 2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;; 2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;; 2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;; 2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;; 254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;; 254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;; 254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;; 254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;; 254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;; 254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;; 2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;; 2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;; 2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;; 2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;; 2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;; 2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;; 2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;; 2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;; 2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;; 2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;; 255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;; 255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;; 255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;; 255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;; 255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;; 255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;; 2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;; 2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;; 2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;; 2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;; 2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;; 2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;; 2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;; 2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;; 2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;; 2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;; 256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;; 256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;; 256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;; 256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;; 256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;; 256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;; 2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;; 2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;; 2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;; 2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;; 2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;; 2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;; 2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;; 2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;; 2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;; 2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;; 257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;; 257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;; 257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;; 257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;; 257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;; 257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;; 2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;; 2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; 2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; 2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;; 2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;; 2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 2588;FULL BLOCK;So;0;ON;;;;;N;;;;; 2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;; 258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;; 258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; 258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; 258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; 2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;; 2591;LIGHT SHADE;So;0;ON;;;;;N;;;;; 2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;; 2593;DARK SHADE;So;0;ON;;;;;N;;;;; 2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; 2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; 2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;; 2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;; 2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;; 2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; 259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; 259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; 259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; 259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;; 259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; 259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; 25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;; 25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;; 25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;; 25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; 25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;; 25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; 25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; 25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;; 25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;; 25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; 25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; 25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;; 25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;; 25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;; 25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; 25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; 25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;; 25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;; 25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;; 25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;; 25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;; 25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;; 25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;; 25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;; 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;; 25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;; 25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;; 25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;; 25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;; 25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;; 25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;; 25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;; 25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;; 25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;; 25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;; 25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;; 25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;; 25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;; 25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;; 25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;; 25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;; 25C9;FISHEYE;So;0;ON;;;;;N;;;;; 25CA;LOZENGE;So;0;ON;;;;;N;;;;; 25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;; 25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;; 25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; 25CE;BULLSEYE;So;0;ON;;;;;N;;;;; 25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;; 25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; 25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; 25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;; 25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;; 25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;; 25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;; 25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; 25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; 25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;; 25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; 25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; 25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; 25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; 25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; 25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; 25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; 25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;; 25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;; 25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; 25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; 25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 25E6;WHITE BULLET;So;0;ON;;;;;N;;;;; 25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; 25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; 25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; 25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; 25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;; 25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;; 25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;; 25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;; 25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;; 25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; 25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; 25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; 25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; 25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; 25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; 25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; 25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; 25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; 25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; 25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; 25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; 25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; 25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; 25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; 25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; 2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;; 2601;CLOUD;So;0;ON;;;;;N;;;;; 2602;UMBRELLA;So;0;ON;;;;;N;;;;; 2603;SNOWMAN;So;0;ON;;;;;N;;;;; 2604;COMET;So;0;ON;;;;;N;;;;; 2605;BLACK STAR;So;0;ON;;;;;N;;;;; 2606;WHITE STAR;So;0;ON;;;;;N;;;;; 2607;LIGHTNING;So;0;ON;;;;;N;;;;; 2608;THUNDERSTORM;So;0;ON;;;;;N;;;;; 2609;SUN;So;0;ON;;;;;N;;;;; 260A;ASCENDING NODE;So;0;ON;;;;;N;;;;; 260B;DESCENDING NODE;So;0;ON;;;;;N;;;;; 260C;CONJUNCTION;So;0;ON;;;;;N;;;;; 260D;OPPOSITION;So;0;ON;;;;;N;;;;; 260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;; 260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;; 2610;BALLOT BOX;So;0;ON;;;;;N;;;;; 2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;; 2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;; 2613;SALTIRE;So;0;ON;;;;;N;;;;; 2614;UMBRELLA WITH RAIN DROPS;So;0;ON;;;;;N;;;;; 2615;HOT BEVERAGE;So;0;ON;;;;;N;;;;; 2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;; 2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;; 2618;SHAMROCK;So;0;ON;;;;;N;;;;; 2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; 261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; 261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; 261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; 261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;; 261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; 261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; 2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;; 2621;CAUTION SIGN;So;0;ON;;;;;N;;;;; 2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;; 2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;; 2624;CADUCEUS;So;0;ON;;;;;N;;;;; 2625;ANKH;So;0;ON;;;;;N;;;;; 2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;; 2627;CHI RHO;So;0;ON;;;;;N;;;;; 2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;; 2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;; 262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;; 262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;; 262C;ADI SHAKTI;So;0;ON;;;;;N;;;;; 262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;; 262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;; 262F;YIN YANG;So;0;ON;;;;;N;;;;; 2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;; 2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;; 2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;; 2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;; 2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;; 2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;; 2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;; 2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;; 2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;; 2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;; 263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;; 263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;; 263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;; 263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;; 263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;; 263F;MERCURY;So;0;ON;;;;;N;;;;; 2640;FEMALE SIGN;So;0;ON;;;;;N;;;;; 2641;EARTH;So;0;ON;;;;;N;;;;; 2642;MALE SIGN;So;0;ON;;;;;N;;;;; 2643;JUPITER;So;0;ON;;;;;N;;;;; 2644;SATURN;So;0;ON;;;;;N;;;;; 2645;URANUS;So;0;ON;;;;;N;;;;; 2646;NEPTUNE;So;0;ON;;;;;N;;;;; 2647;PLUTO;So;0;ON;;;;;N;;;;; 2648;ARIES;So;0;ON;;;;;N;;;;; 2649;TAURUS;So;0;ON;;;;;N;;;;; 264A;GEMINI;So;0;ON;;;;;N;;;;; 264B;CANCER;So;0;ON;;;;;N;;;;; 264C;LEO;So;0;ON;;;;;N;;;;; 264D;VIRGO;So;0;ON;;;;;N;;;;; 264E;LIBRA;So;0;ON;;;;;N;;;;; 264F;SCORPIUS;So;0;ON;;;;;N;;;;; 2650;SAGITTARIUS;So;0;ON;;;;;N;;;;; 2651;CAPRICORN;So;0;ON;;;;;N;;;;; 2652;AQUARIUS;So;0;ON;;;;;N;;;;; 2653;PISCES;So;0;ON;;;;;N;;;;; 2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;; 2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;; 2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;; 2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;; 2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;; 2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;; 265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;; 265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;; 265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;; 265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;; 265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;; 265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;; 2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;; 2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;; 2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;; 2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;; 2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;; 2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;; 2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;; 2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;; 2668;HOT SPRINGS;So;0;ON;;;;;N;;;;; 2669;QUARTER NOTE;So;0;ON;;;;;N;;;;; 266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;; 266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;; 266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;; 266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;; 266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;; 266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;; 2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;; 2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;; 2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; 2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;;;; 2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;;;; 2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;;;; 2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;;;; 2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;;;; 2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;;;; 2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;;;; 267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;; 267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; 267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; 267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; 267E;PERMANENT PAPER SIGN;So;0;ON;;;;;N;;;;; 267F;WHEELCHAIR SYMBOL;So;0;ON;;;;;N;;;;; 2680;DIE FACE-1;So;0;ON;;;;;N;;;;; 2681;DIE FACE-2;So;0;ON;;;;;N;;;;; 2682;DIE FACE-3;So;0;ON;;;;;N;;;;; 2683;DIE FACE-4;So;0;ON;;;;;N;;;;; 2684;DIE FACE-5;So;0;ON;;;;;N;;;;; 2685;DIE FACE-6;So;0;ON;;;;;N;;;;; 2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;; 2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;; 2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;; 2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;; 268A;MONOGRAM FOR YANG;So;0;ON;;;;;N;;;;; 268B;MONOGRAM FOR YIN;So;0;ON;;;;;N;;;;; 268C;DIGRAM FOR GREATER YANG;So;0;ON;;;;;N;;;;; 268D;DIGRAM FOR LESSER YIN;So;0;ON;;;;;N;;;;; 268E;DIGRAM FOR LESSER YANG;So;0;ON;;;;;N;;;;; 268F;DIGRAM FOR GREATER YIN;So;0;ON;;;;;N;;;;; 2690;WHITE FLAG;So;0;ON;;;;;N;;;;; 2691;BLACK FLAG;So;0;ON;;;;;N;;;;; 2692;HAMMER AND PICK;So;0;ON;;;;;N;;;;; 2693;ANCHOR;So;0;ON;;;;;N;;;;; 2694;CROSSED SWORDS;So;0;ON;;;;;N;;;;; 2695;STAFF OF AESCULAPIUS;So;0;ON;;;;;N;;;;; 2696;SCALES;So;0;ON;;;;;N;;;;; 2697;ALEMBIC;So;0;ON;;;;;N;;;;; 2698;FLOWER;So;0;ON;;;;;N;;;;; 2699;GEAR;So;0;ON;;;;;N;;;;; 269A;STAFF OF HERMES;So;0;ON;;;;;N;;;;; 269B;ATOM SYMBOL;So;0;ON;;;;;N;;;;; 269C;FLEUR-DE-LIS;So;0;ON;;;;;N;;;;; 269D;OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;; 269E;THREE LINES CONVERGING RIGHT;So;0;ON;;;;;N;;;;; 269F;THREE LINES CONVERGING LEFT;So;0;ON;;;;;N;;;;; 26A0;WARNING SIGN;So;0;ON;;;;;N;;;;; 26A1;HIGH VOLTAGE SIGN;So;0;ON;;;;;N;;;;; 26A2;DOUBLED FEMALE SIGN;So;0;ON;;;;;N;;;;; 26A3;DOUBLED MALE SIGN;So;0;ON;;;;;N;;;;; 26A4;INTERLOCKED FEMALE AND MALE SIGN;So;0;ON;;;;;N;;;;; 26A5;MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;; 26A6;MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; 26A7;MALE WITH STROKE AND MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;; 26A8;VERTICAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; 26A9;HORIZONTAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; 26AA;MEDIUM WHITE CIRCLE;So;0;ON;;;;;N;;;;; 26AB;MEDIUM BLACK CIRCLE;So;0;ON;;;;;N;;;;; 26AC;MEDIUM SMALL WHITE CIRCLE;So;0;L;;;;;N;;;;; 26AD;MARRIAGE SYMBOL;So;0;ON;;;;;N;;;;; 26AE;DIVORCE SYMBOL;So;0;ON;;;;;N;;;;; 26AF;UNMARRIED PARTNERSHIP SYMBOL;So;0;ON;;;;;N;;;;; 26B0;COFFIN;So;0;ON;;;;;N;;;;; 26B1;FUNERAL URN;So;0;ON;;;;;N;;;;; 26B2;NEUTER;So;0;ON;;;;;N;;;;; 26B3;CERES;So;0;ON;;;;;N;;;;; 26B4;PALLAS;So;0;ON;;;;;N;;;;; 26B5;JUNO;So;0;ON;;;;;N;;;;; 26B6;VESTA;So;0;ON;;;;;N;;;;; 26B7;CHIRON;So;0;ON;;;;;N;;;;; 26B8;BLACK MOON LILITH;So;0;ON;;;;;N;;;;; 26B9;SEXTILE;So;0;ON;;;;;N;;;;; 26BA;SEMISEXTILE;So;0;ON;;;;;N;;;;; 26BB;QUINCUNX;So;0;ON;;;;;N;;;;; 26BC;SESQUIQUADRATE;So;0;ON;;;;;N;;;;; 26BD;SOCCER BALL;So;0;ON;;;;;N;;;;; 26BE;BASEBALL;So;0;ON;;;;;N;;;;; 26BF;SQUARED KEY;So;0;ON;;;;;N;;;;; 26C0;WHITE DRAUGHTS MAN;So;0;ON;;;;;N;;;;; 26C1;WHITE DRAUGHTS KING;So;0;ON;;;;;N;;;;; 26C2;BLACK DRAUGHTS MAN;So;0;ON;;;;;N;;;;; 26C3;BLACK DRAUGHTS KING;So;0;ON;;;;;N;;;;; 26C4;SNOWMAN WITHOUT SNOW;So;0;ON;;;;;N;;;;; 26C5;SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;; 26C6;RAIN;So;0;ON;;;;;N;;;;; 26C7;BLACK SNOWMAN;So;0;ON;;;;;N;;;;; 26C8;THUNDER CLOUD AND RAIN;So;0;ON;;;;;N;;;;; 26C9;TURNED WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;; 26CA;TURNED BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;; 26CB;WHITE DIAMOND IN SQUARE;So;0;ON;;;;;N;;;;; 26CC;CROSSING LANES;So;0;ON;;;;;N;;;;; 26CD;DISABLED CAR;So;0;ON;;;;;N;;;;; 26CE;OPHIUCHUS;So;0;ON;;;;;N;;;;; 26CF;PICK;So;0;ON;;;;;N;;;;; 26D0;CAR SLIDING;So;0;ON;;;;;N;;;;; 26D1;HELMET WITH WHITE CROSS;So;0;ON;;;;;N;;;;; 26D2;CIRCLED CROSSING LANES;So;0;ON;;;;;N;;;;; 26D3;CHAINS;So;0;ON;;;;;N;;;;; 26D4;NO ENTRY;So;0;ON;;;;;N;;;;; 26D5;ALTERNATE ONE-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; 26D6;BLACK TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; 26D7;WHITE TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; 26D8;BLACK LEFT LANE MERGE;So;0;ON;;;;;N;;;;; 26D9;WHITE LEFT LANE MERGE;So;0;ON;;;;;N;;;;; 26DA;DRIVE SLOW SIGN;So;0;ON;;;;;N;;;;; 26DB;HEAVY WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; 26DC;LEFT CLOSED ENTRY;So;0;ON;;;;;N;;;;; 26DD;SQUARED SALTIRE;So;0;ON;;;;;N;;;;; 26DE;FALLING DIAGONAL IN WHITE CIRCLE IN BLACK SQUARE;So;0;ON;;;;;N;;;;; 26DF;BLACK TRUCK;So;0;ON;;;;;N;;;;; 26E0;RESTRICTED LEFT ENTRY-1;So;0;ON;;;;;N;;;;; 26E1;RESTRICTED LEFT ENTRY-2;So;0;ON;;;;;N;;;;; 26E2;ASTRONOMICAL SYMBOL FOR URANUS;So;0;ON;;;;;N;;;;; 26E3;HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE;So;0;ON;;;;;N;;;;; 26E4;PENTAGRAM;So;0;ON;;;;;N;;;;; 26E5;RIGHT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;; 26E6;LEFT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;; 26E7;INVERTED PENTAGRAM;So;0;ON;;;;;N;;;;; 26E8;BLACK CROSS ON SHIELD;So;0;ON;;;;;N;;;;; 26E9;SHINTO SHRINE;So;0;ON;;;;;N;;;;; 26EA;CHURCH;So;0;ON;;;;;N;;;;; 26EB;CASTLE;So;0;ON;;;;;N;;;;; 26EC;HISTORIC SITE;So;0;ON;;;;;N;;;;; 26ED;GEAR WITHOUT HUB;So;0;ON;;;;;N;;;;; 26EE;GEAR WITH HANDLES;So;0;ON;;;;;N;;;;; 26EF;MAP SYMBOL FOR LIGHTHOUSE;So;0;ON;;;;;N;;;;; 26F0;MOUNTAIN;So;0;ON;;;;;N;;;;; 26F1;UMBRELLA ON GROUND;So;0;ON;;;;;N;;;;; 26F2;FOUNTAIN;So;0;ON;;;;;N;;;;; 26F3;FLAG IN HOLE;So;0;ON;;;;;N;;;;; 26F4;FERRY;So;0;ON;;;;;N;;;;; 26F5;SAILBOAT;So;0;ON;;;;;N;;;;; 26F6;SQUARE FOUR CORNERS;So;0;ON;;;;;N;;;;; 26F7;SKIER;So;0;ON;;;;;N;;;;; 26F8;ICE SKATE;So;0;ON;;;;;N;;;;; 26F9;PERSON WITH BALL;So;0;ON;;;;;N;;;;; 26FA;TENT;So;0;ON;;;;;N;;;;; 26FB;JAPANESE BANK SYMBOL;So;0;ON;;;;;N;;;;; 26FC;HEADSTONE GRAVEYARD SYMBOL;So;0;ON;;;;;N;;;;; 26FD;FUEL PUMP;So;0;ON;;;;;N;;;;; 26FE;CUP ON BLACK SQUARE;So;0;ON;;;;;N;;;;; 26FF;WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE;So;0;ON;;;;;N;;;;; 2700;BLACK SAFETY SCISSORS;So;0;ON;;;;;N;;;;; 2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;; 2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;; 2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;; 2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;; 2705;WHITE HEAVY CHECK MARK;So;0;ON;;;;;N;;;;; 2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;; 2707;TAPE DRIVE;So;0;ON;;;;;N;;;;; 2708;AIRPLANE;So;0;ON;;;;;N;;;;; 2709;ENVELOPE;So;0;ON;;;;;N;;;;; 270A;RAISED FIST;So;0;ON;;;;;N;;;;; 270B;RAISED HAND;So;0;ON;;;;;N;;;;; 270C;VICTORY HAND;So;0;ON;;;;;N;;;;; 270D;WRITING HAND;So;0;ON;;;;;N;;;;; 270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;; 270F;PENCIL;So;0;ON;;;;;N;;;;; 2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;; 2711;WHITE NIB;So;0;ON;;;;;N;;;;; 2712;BLACK NIB;So;0;ON;;;;;N;;;;; 2713;CHECK MARK;So;0;ON;;;;;N;;;;; 2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;; 2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;; 2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;; 2717;BALLOT X;So;0;ON;;;;;N;;;;; 2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;; 2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;; 271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; 271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;; 271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;; 271D;LATIN CROSS;So;0;ON;;;;;N;;;;; 271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;; 271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;; 2720;MALTESE CROSS;So;0;ON;;;;;N;;;;; 2721;STAR OF DAVID;So;0;ON;;;;;N;;;;; 2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;; 2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;; 2728;SPARKLES;So;0;ON;;;;;N;;;;; 2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;; 272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;; 272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;; 272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;; 272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; 272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; 272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;; 2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;; 2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;; 2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;; 2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; 2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; 2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;; 273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;; 273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;; 273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;; 2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;; 2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;; 2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;; 2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;; 2744;SNOWFLAKE;So;0;ON;;;;;N;;;;; 2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;; 2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;; 2747;SPARKLE;So;0;ON;;;;;N;;;;; 2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;; 2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; 274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; 274C;CROSS MARK;So;0;ON;;;;;N;;;;; 274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; 274E;NEGATIVE SQUARED CROSS MARK;So;0;ON;;;;;N;;;;; 274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; 2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; 2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; 2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; 2753;BLACK QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2754;WHITE QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2755;WHITE EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;; 2757;HEAVY EXCLAMATION MARK SYMBOL;So;0;ON;;;;;N;;;;; 2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;; 2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;; 275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;; 275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 275F;HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2760;HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;; 2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;; 2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;; 2766;FLORAL HEART;So;0;ON;;;;;N;;;;; 2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; 2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; 2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; 276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; 276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; 276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; 276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; 276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;; 276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;; 2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; 2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; 2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; 2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; 2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; 2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; 2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;; 2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;; 2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;; 2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;; 277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;; 277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;; 277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;; 277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;; 277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;; 277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;; 2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;; 2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;; 2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;; 2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;; 2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;; 2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;; 2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;; 2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;; 2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;; 2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;; 278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;; 278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;; 278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;; 278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;; 278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;; 278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;; 2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;; 2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;; 2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;; 2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;; 2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;; 2795;HEAVY PLUS SIGN;So;0;ON;;;;;N;;;;; 2796;HEAVY MINUS SIGN;So;0;ON;;;;;N;;;;; 2797;HEAVY DIVISION SIGN;So;0;ON;;;;;N;;;;; 2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;; 2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;; 279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;; 279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;; 279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;; 279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;; 279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;; 279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;; 27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;; 27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;; 27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;; 27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;; 27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;; 27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;; 27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;; 27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;; 27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;; 27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;; 27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;; 27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;; 27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;; 27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; 27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; 27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; 27B0;CURLY LOOP;So;0;ON;;;;;N;;;;; 27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; 27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;; 27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;; 27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;; 27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;; 27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;; 27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;; 27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;; 27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;; 27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;; 27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;; 27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;; 27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;; 27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;; 27BF;DOUBLE CURLY LOOP;So;0;ON;;;;;N;;;;; 27C0;THREE DIMENSIONAL ANGLE;Sm;0;ON;;;;;Y;;;;; 27C1;WHITE TRIANGLE CONTAINING SMALL WHITE TRIANGLE;Sm;0;ON;;;;;N;;;;; 27C2;PERPENDICULAR;Sm;0;ON;;;;;N;;;;; 27C3;OPEN SUBSET;Sm;0;ON;;;;;Y;;;;; 27C4;OPEN SUPERSET;Sm;0;ON;;;;;Y;;;;; 27C5;LEFT S-SHAPED BAG DELIMITER;Ps;0;ON;;;;;Y;;;;; 27C6;RIGHT S-SHAPED BAG DELIMITER;Pe;0;ON;;;;;Y;;;;; 27C7;OR WITH DOT INSIDE;Sm;0;ON;;;;;N;;;;; 27C8;REVERSE SOLIDUS PRECEDING SUBSET;Sm;0;ON;;;;;Y;;;;; 27C9;SUPERSET PRECEDING SOLIDUS;Sm;0;ON;;;;;Y;;;;; 27CA;VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; 27CB;MATHEMATICAL RISING DIAGONAL;Sm;0;ON;;;;;Y;;;;; 27CC;LONG DIVISION;Sm;0;ON;;;;;Y;;;;; 27CD;MATHEMATICAL FALLING DIAGONAL;Sm;0;ON;;;;;Y;;;;; 27CE;SQUARED LOGICAL AND;Sm;0;ON;;;;;N;;;;; 27CF;SQUARED LOGICAL OR;Sm;0;ON;;;;;N;;;;; 27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;; 27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;; 27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;; 27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; 27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; 27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; 27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; 27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;; 27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;; 27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;; 27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;; 27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;; 27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;; 27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;; 27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;; 27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; 27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;; 27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;; 27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; 27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; 27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; 27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; 27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;; 27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;; 27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; 27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; 27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; 27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; 27EC;MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;; 27ED;MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;; 27EE;MATHEMATICAL LEFT FLATTENED PARENTHESIS;Ps;0;ON;;;;;Y;;;;; 27EF;MATHEMATICAL RIGHT FLATTENED PARENTHESIS;Pe;0;ON;;;;;Y;;;;; 27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; 27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; 27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; 27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; 27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; 27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; 27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; 27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; 27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; 27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;; 2800;BRAILLE PATTERN BLANK;So;0;L;;;;;N;;;;; 2801;BRAILLE PATTERN DOTS-1;So;0;L;;;;;N;;;;; 2802;BRAILLE PATTERN DOTS-2;So;0;L;;;;;N;;;;; 2803;BRAILLE PATTERN DOTS-12;So;0;L;;;;;N;;;;; 2804;BRAILLE PATTERN DOTS-3;So;0;L;;;;;N;;;;; 2805;BRAILLE PATTERN DOTS-13;So;0;L;;;;;N;;;;; 2806;BRAILLE PATTERN DOTS-23;So;0;L;;;;;N;;;;; 2807;BRAILLE PATTERN DOTS-123;So;0;L;;;;;N;;;;; 2808;BRAILLE PATTERN DOTS-4;So;0;L;;;;;N;;;;; 2809;BRAILLE PATTERN DOTS-14;So;0;L;;;;;N;;;;; 280A;BRAILLE PATTERN DOTS-24;So;0;L;;;;;N;;;;; 280B;BRAILLE PATTERN DOTS-124;So;0;L;;;;;N;;;;; 280C;BRAILLE PATTERN DOTS-34;So;0;L;;;;;N;;;;; 280D;BRAILLE PATTERN DOTS-134;So;0;L;;;;;N;;;;; 280E;BRAILLE PATTERN DOTS-234;So;0;L;;;;;N;;;;; 280F;BRAILLE PATTERN DOTS-1234;So;0;L;;;;;N;;;;; 2810;BRAILLE PATTERN DOTS-5;So;0;L;;;;;N;;;;; 2811;BRAILLE PATTERN DOTS-15;So;0;L;;;;;N;;;;; 2812;BRAILLE PATTERN DOTS-25;So;0;L;;;;;N;;;;; 2813;BRAILLE PATTERN DOTS-125;So;0;L;;;;;N;;;;; 2814;BRAILLE PATTERN DOTS-35;So;0;L;;;;;N;;;;; 2815;BRAILLE PATTERN DOTS-135;So;0;L;;;;;N;;;;; 2816;BRAILLE PATTERN DOTS-235;So;0;L;;;;;N;;;;; 2817;BRAILLE PATTERN DOTS-1235;So;0;L;;;;;N;;;;; 2818;BRAILLE PATTERN DOTS-45;So;0;L;;;;;N;;;;; 2819;BRAILLE PATTERN DOTS-145;So;0;L;;;;;N;;;;; 281A;BRAILLE PATTERN DOTS-245;So;0;L;;;;;N;;;;; 281B;BRAILLE PATTERN DOTS-1245;So;0;L;;;;;N;;;;; 281C;BRAILLE PATTERN DOTS-345;So;0;L;;;;;N;;;;; 281D;BRAILLE PATTERN DOTS-1345;So;0;L;;;;;N;;;;; 281E;BRAILLE PATTERN DOTS-2345;So;0;L;;;;;N;;;;; 281F;BRAILLE PATTERN DOTS-12345;So;0;L;;;;;N;;;;; 2820;BRAILLE PATTERN DOTS-6;So;0;L;;;;;N;;;;; 2821;BRAILLE PATTERN DOTS-16;So;0;L;;;;;N;;;;; 2822;BRAILLE PATTERN DOTS-26;So;0;L;;;;;N;;;;; 2823;BRAILLE PATTERN DOTS-126;So;0;L;;;;;N;;;;; 2824;BRAILLE PATTERN DOTS-36;So;0;L;;;;;N;;;;; 2825;BRAILLE PATTERN DOTS-136;So;0;L;;;;;N;;;;; 2826;BRAILLE PATTERN DOTS-236;So;0;L;;;;;N;;;;; 2827;BRAILLE PATTERN DOTS-1236;So;0;L;;;;;N;;;;; 2828;BRAILLE PATTERN DOTS-46;So;0;L;;;;;N;;;;; 2829;BRAILLE PATTERN DOTS-146;So;0;L;;;;;N;;;;; 282A;BRAILLE PATTERN DOTS-246;So;0;L;;;;;N;;;;; 282B;BRAILLE PATTERN DOTS-1246;So;0;L;;;;;N;;;;; 282C;BRAILLE PATTERN DOTS-346;So;0;L;;;;;N;;;;; 282D;BRAILLE PATTERN DOTS-1346;So;0;L;;;;;N;;;;; 282E;BRAILLE PATTERN DOTS-2346;So;0;L;;;;;N;;;;; 282F;BRAILLE PATTERN DOTS-12346;So;0;L;;;;;N;;;;; 2830;BRAILLE PATTERN DOTS-56;So;0;L;;;;;N;;;;; 2831;BRAILLE PATTERN DOTS-156;So;0;L;;;;;N;;;;; 2832;BRAILLE PATTERN DOTS-256;So;0;L;;;;;N;;;;; 2833;BRAILLE PATTERN DOTS-1256;So;0;L;;;;;N;;;;; 2834;BRAILLE PATTERN DOTS-356;So;0;L;;;;;N;;;;; 2835;BRAILLE PATTERN DOTS-1356;So;0;L;;;;;N;;;;; 2836;BRAILLE PATTERN DOTS-2356;So;0;L;;;;;N;;;;; 2837;BRAILLE PATTERN DOTS-12356;So;0;L;;;;;N;;;;; 2838;BRAILLE PATTERN DOTS-456;So;0;L;;;;;N;;;;; 2839;BRAILLE PATTERN DOTS-1456;So;0;L;;;;;N;;;;; 283A;BRAILLE PATTERN DOTS-2456;So;0;L;;;;;N;;;;; 283B;BRAILLE PATTERN DOTS-12456;So;0;L;;;;;N;;;;; 283C;BRAILLE PATTERN DOTS-3456;So;0;L;;;;;N;;;;; 283D;BRAILLE PATTERN DOTS-13456;So;0;L;;;;;N;;;;; 283E;BRAILLE PATTERN DOTS-23456;So;0;L;;;;;N;;;;; 283F;BRAILLE PATTERN DOTS-123456;So;0;L;;;;;N;;;;; 2840;BRAILLE PATTERN DOTS-7;So;0;L;;;;;N;;;;; 2841;BRAILLE PATTERN DOTS-17;So;0;L;;;;;N;;;;; 2842;BRAILLE PATTERN DOTS-27;So;0;L;;;;;N;;;;; 2843;BRAILLE PATTERN DOTS-127;So;0;L;;;;;N;;;;; 2844;BRAILLE PATTERN DOTS-37;So;0;L;;;;;N;;;;; 2845;BRAILLE PATTERN DOTS-137;So;0;L;;;;;N;;;;; 2846;BRAILLE PATTERN DOTS-237;So;0;L;;;;;N;;;;; 2847;BRAILLE PATTERN DOTS-1237;So;0;L;;;;;N;;;;; 2848;BRAILLE PATTERN DOTS-47;So;0;L;;;;;N;;;;; 2849;BRAILLE PATTERN DOTS-147;So;0;L;;;;;N;;;;; 284A;BRAILLE PATTERN DOTS-247;So;0;L;;;;;N;;;;; 284B;BRAILLE PATTERN DOTS-1247;So;0;L;;;;;N;;;;; 284C;BRAILLE PATTERN DOTS-347;So;0;L;;;;;N;;;;; 284D;BRAILLE PATTERN DOTS-1347;So;0;L;;;;;N;;;;; 284E;BRAILLE PATTERN DOTS-2347;So;0;L;;;;;N;;;;; 284F;BRAILLE PATTERN DOTS-12347;So;0;L;;;;;N;;;;; 2850;BRAILLE PATTERN DOTS-57;So;0;L;;;;;N;;;;; 2851;BRAILLE PATTERN DOTS-157;So;0;L;;;;;N;;;;; 2852;BRAILLE PATTERN DOTS-257;So;0;L;;;;;N;;;;; 2853;BRAILLE PATTERN DOTS-1257;So;0;L;;;;;N;;;;; 2854;BRAILLE PATTERN DOTS-357;So;0;L;;;;;N;;;;; 2855;BRAILLE PATTERN DOTS-1357;So;0;L;;;;;N;;;;; 2856;BRAILLE PATTERN DOTS-2357;So;0;L;;;;;N;;;;; 2857;BRAILLE PATTERN DOTS-12357;So;0;L;;;;;N;;;;; 2858;BRAILLE PATTERN DOTS-457;So;0;L;;;;;N;;;;; 2859;BRAILLE PATTERN DOTS-1457;So;0;L;;;;;N;;;;; 285A;BRAILLE PATTERN DOTS-2457;So;0;L;;;;;N;;;;; 285B;BRAILLE PATTERN DOTS-12457;So;0;L;;;;;N;;;;; 285C;BRAILLE PATTERN DOTS-3457;So;0;L;;;;;N;;;;; 285D;BRAILLE PATTERN DOTS-13457;So;0;L;;;;;N;;;;; 285E;BRAILLE PATTERN DOTS-23457;So;0;L;;;;;N;;;;; 285F;BRAILLE PATTERN DOTS-123457;So;0;L;;;;;N;;;;; 2860;BRAILLE PATTERN DOTS-67;So;0;L;;;;;N;;;;; 2861;BRAILLE PATTERN DOTS-167;So;0;L;;;;;N;;;;; 2862;BRAILLE PATTERN DOTS-267;So;0;L;;;;;N;;;;; 2863;BRAILLE PATTERN DOTS-1267;So;0;L;;;;;N;;;;; 2864;BRAILLE PATTERN DOTS-367;So;0;L;;;;;N;;;;; 2865;BRAILLE PATTERN DOTS-1367;So;0;L;;;;;N;;;;; 2866;BRAILLE PATTERN DOTS-2367;So;0;L;;;;;N;;;;; 2867;BRAILLE PATTERN DOTS-12367;So;0;L;;;;;N;;;;; 2868;BRAILLE PATTERN DOTS-467;So;0;L;;;;;N;;;;; 2869;BRAILLE PATTERN DOTS-1467;So;0;L;;;;;N;;;;; 286A;BRAILLE PATTERN DOTS-2467;So;0;L;;;;;N;;;;; 286B;BRAILLE PATTERN DOTS-12467;So;0;L;;;;;N;;;;; 286C;BRAILLE PATTERN DOTS-3467;So;0;L;;;;;N;;;;; 286D;BRAILLE PATTERN DOTS-13467;So;0;L;;;;;N;;;;; 286E;BRAILLE PATTERN DOTS-23467;So;0;L;;;;;N;;;;; 286F;BRAILLE PATTERN DOTS-123467;So;0;L;;;;;N;;;;; 2870;BRAILLE PATTERN DOTS-567;So;0;L;;;;;N;;;;; 2871;BRAILLE PATTERN DOTS-1567;So;0;L;;;;;N;;;;; 2872;BRAILLE PATTERN DOTS-2567;So;0;L;;;;;N;;;;; 2873;BRAILLE PATTERN DOTS-12567;So;0;L;;;;;N;;;;; 2874;BRAILLE PATTERN DOTS-3567;So;0;L;;;;;N;;;;; 2875;BRAILLE PATTERN DOTS-13567;So;0;L;;;;;N;;;;; 2876;BRAILLE PATTERN DOTS-23567;So;0;L;;;;;N;;;;; 2877;BRAILLE PATTERN DOTS-123567;So;0;L;;;;;N;;;;; 2878;BRAILLE PATTERN DOTS-4567;So;0;L;;;;;N;;;;; 2879;BRAILLE PATTERN DOTS-14567;So;0;L;;;;;N;;;;; 287A;BRAILLE PATTERN DOTS-24567;So;0;L;;;;;N;;;;; 287B;BRAILLE PATTERN DOTS-124567;So;0;L;;;;;N;;;;; 287C;BRAILLE PATTERN DOTS-34567;So;0;L;;;;;N;;;;; 287D;BRAILLE PATTERN DOTS-134567;So;0;L;;;;;N;;;;; 287E;BRAILLE PATTERN DOTS-234567;So;0;L;;;;;N;;;;; 287F;BRAILLE PATTERN DOTS-1234567;So;0;L;;;;;N;;;;; 2880;BRAILLE PATTERN DOTS-8;So;0;L;;;;;N;;;;; 2881;BRAILLE PATTERN DOTS-18;So;0;L;;;;;N;;;;; 2882;BRAILLE PATTERN DOTS-28;So;0;L;;;;;N;;;;; 2883;BRAILLE PATTERN DOTS-128;So;0;L;;;;;N;;;;; 2884;BRAILLE PATTERN DOTS-38;So;0;L;;;;;N;;;;; 2885;BRAILLE PATTERN DOTS-138;So;0;L;;;;;N;;;;; 2886;BRAILLE PATTERN DOTS-238;So;0;L;;;;;N;;;;; 2887;BRAILLE PATTERN DOTS-1238;So;0;L;;;;;N;;;;; 2888;BRAILLE PATTERN DOTS-48;So;0;L;;;;;N;;;;; 2889;BRAILLE PATTERN DOTS-148;So;0;L;;;;;N;;;;; 288A;BRAILLE PATTERN DOTS-248;So;0;L;;;;;N;;;;; 288B;BRAILLE PATTERN DOTS-1248;So;0;L;;;;;N;;;;; 288C;BRAILLE PATTERN DOTS-348;So;0;L;;;;;N;;;;; 288D;BRAILLE PATTERN DOTS-1348;So;0;L;;;;;N;;;;; 288E;BRAILLE PATTERN DOTS-2348;So;0;L;;;;;N;;;;; 288F;BRAILLE PATTERN DOTS-12348;So;0;L;;;;;N;;;;; 2890;BRAILLE PATTERN DOTS-58;So;0;L;;;;;N;;;;; 2891;BRAILLE PATTERN DOTS-158;So;0;L;;;;;N;;;;; 2892;BRAILLE PATTERN DOTS-258;So;0;L;;;;;N;;;;; 2893;BRAILLE PATTERN DOTS-1258;So;0;L;;;;;N;;;;; 2894;BRAILLE PATTERN DOTS-358;So;0;L;;;;;N;;;;; 2895;BRAILLE PATTERN DOTS-1358;So;0;L;;;;;N;;;;; 2896;BRAILLE PATTERN DOTS-2358;So;0;L;;;;;N;;;;; 2897;BRAILLE PATTERN DOTS-12358;So;0;L;;;;;N;;;;; 2898;BRAILLE PATTERN DOTS-458;So;0;L;;;;;N;;;;; 2899;BRAILLE PATTERN DOTS-1458;So;0;L;;;;;N;;;;; 289A;BRAILLE PATTERN DOTS-2458;So;0;L;;;;;N;;;;; 289B;BRAILLE PATTERN DOTS-12458;So;0;L;;;;;N;;;;; 289C;BRAILLE PATTERN DOTS-3458;So;0;L;;;;;N;;;;; 289D;BRAILLE PATTERN DOTS-13458;So;0;L;;;;;N;;;;; 289E;BRAILLE PATTERN DOTS-23458;So;0;L;;;;;N;;;;; 289F;BRAILLE PATTERN DOTS-123458;So;0;L;;;;;N;;;;; 28A0;BRAILLE PATTERN DOTS-68;So;0;L;;;;;N;;;;; 28A1;BRAILLE PATTERN DOTS-168;So;0;L;;;;;N;;;;; 28A2;BRAILLE PATTERN DOTS-268;So;0;L;;;;;N;;;;; 28A3;BRAILLE PATTERN DOTS-1268;So;0;L;;;;;N;;;;; 28A4;BRAILLE PATTERN DOTS-368;So;0;L;;;;;N;;;;; 28A5;BRAILLE PATTERN DOTS-1368;So;0;L;;;;;N;;;;; 28A6;BRAILLE PATTERN DOTS-2368;So;0;L;;;;;N;;;;; 28A7;BRAILLE PATTERN DOTS-12368;So;0;L;;;;;N;;;;; 28A8;BRAILLE PATTERN DOTS-468;So;0;L;;;;;N;;;;; 28A9;BRAILLE PATTERN DOTS-1468;So;0;L;;;;;N;;;;; 28AA;BRAILLE PATTERN DOTS-2468;So;0;L;;;;;N;;;;; 28AB;BRAILLE PATTERN DOTS-12468;So;0;L;;;;;N;;;;; 28AC;BRAILLE PATTERN DOTS-3468;So;0;L;;;;;N;;;;; 28AD;BRAILLE PATTERN DOTS-13468;So;0;L;;;;;N;;;;; 28AE;BRAILLE PATTERN DOTS-23468;So;0;L;;;;;N;;;;; 28AF;BRAILLE PATTERN DOTS-123468;So;0;L;;;;;N;;;;; 28B0;BRAILLE PATTERN DOTS-568;So;0;L;;;;;N;;;;; 28B1;BRAILLE PATTERN DOTS-1568;So;0;L;;;;;N;;;;; 28B2;BRAILLE PATTERN DOTS-2568;So;0;L;;;;;N;;;;; 28B3;BRAILLE PATTERN DOTS-12568;So;0;L;;;;;N;;;;; 28B4;BRAILLE PATTERN DOTS-3568;So;0;L;;;;;N;;;;; 28B5;BRAILLE PATTERN DOTS-13568;So;0;L;;;;;N;;;;; 28B6;BRAILLE PATTERN DOTS-23568;So;0;L;;;;;N;;;;; 28B7;BRAILLE PATTERN DOTS-123568;So;0;L;;;;;N;;;;; 28B8;BRAILLE PATTERN DOTS-4568;So;0;L;;;;;N;;;;; 28B9;BRAILLE PATTERN DOTS-14568;So;0;L;;;;;N;;;;; 28BA;BRAILLE PATTERN DOTS-24568;So;0;L;;;;;N;;;;; 28BB;BRAILLE PATTERN DOTS-124568;So;0;L;;;;;N;;;;; 28BC;BRAILLE PATTERN DOTS-34568;So;0;L;;;;;N;;;;; 28BD;BRAILLE PATTERN DOTS-134568;So;0;L;;;;;N;;;;; 28BE;BRAILLE PATTERN DOTS-234568;So;0;L;;;;;N;;;;; 28BF;BRAILLE PATTERN DOTS-1234568;So;0;L;;;;;N;;;;; 28C0;BRAILLE PATTERN DOTS-78;So;0;L;;;;;N;;;;; 28C1;BRAILLE PATTERN DOTS-178;So;0;L;;;;;N;;;;; 28C2;BRAILLE PATTERN DOTS-278;So;0;L;;;;;N;;;;; 28C3;BRAILLE PATTERN DOTS-1278;So;0;L;;;;;N;;;;; 28C4;BRAILLE PATTERN DOTS-378;So;0;L;;;;;N;;;;; 28C5;BRAILLE PATTERN DOTS-1378;So;0;L;;;;;N;;;;; 28C6;BRAILLE PATTERN DOTS-2378;So;0;L;;;;;N;;;;; 28C7;BRAILLE PATTERN DOTS-12378;So;0;L;;;;;N;;;;; 28C8;BRAILLE PATTERN DOTS-478;So;0;L;;;;;N;;;;; 28C9;BRAILLE PATTERN DOTS-1478;So;0;L;;;;;N;;;;; 28CA;BRAILLE PATTERN DOTS-2478;So;0;L;;;;;N;;;;; 28CB;BRAILLE PATTERN DOTS-12478;So;0;L;;;;;N;;;;; 28CC;BRAILLE PATTERN DOTS-3478;So;0;L;;;;;N;;;;; 28CD;BRAILLE PATTERN DOTS-13478;So;0;L;;;;;N;;;;; 28CE;BRAILLE PATTERN DOTS-23478;So;0;L;;;;;N;;;;; 28CF;BRAILLE PATTERN DOTS-123478;So;0;L;;;;;N;;;;; 28D0;BRAILLE PATTERN DOTS-578;So;0;L;;;;;N;;;;; 28D1;BRAILLE PATTERN DOTS-1578;So;0;L;;;;;N;;;;; 28D2;BRAILLE PATTERN DOTS-2578;So;0;L;;;;;N;;;;; 28D3;BRAILLE PATTERN DOTS-12578;So;0;L;;;;;N;;;;; 28D4;BRAILLE PATTERN DOTS-3578;So;0;L;;;;;N;;;;; 28D5;BRAILLE PATTERN DOTS-13578;So;0;L;;;;;N;;;;; 28D6;BRAILLE PATTERN DOTS-23578;So;0;L;;;;;N;;;;; 28D7;BRAILLE PATTERN DOTS-123578;So;0;L;;;;;N;;;;; 28D8;BRAILLE PATTERN DOTS-4578;So;0;L;;;;;N;;;;; 28D9;BRAILLE PATTERN DOTS-14578;So;0;L;;;;;N;;;;; 28DA;BRAILLE PATTERN DOTS-24578;So;0;L;;;;;N;;;;; 28DB;BRAILLE PATTERN DOTS-124578;So;0;L;;;;;N;;;;; 28DC;BRAILLE PATTERN DOTS-34578;So;0;L;;;;;N;;;;; 28DD;BRAILLE PATTERN DOTS-134578;So;0;L;;;;;N;;;;; 28DE;BRAILLE PATTERN DOTS-234578;So;0;L;;;;;N;;;;; 28DF;BRAILLE PATTERN DOTS-1234578;So;0;L;;;;;N;;;;; 28E0;BRAILLE PATTERN DOTS-678;So;0;L;;;;;N;;;;; 28E1;BRAILLE PATTERN DOTS-1678;So;0;L;;;;;N;;;;; 28E2;BRAILLE PATTERN DOTS-2678;So;0;L;;;;;N;;;;; 28E3;BRAILLE PATTERN DOTS-12678;So;0;L;;;;;N;;;;; 28E4;BRAILLE PATTERN DOTS-3678;So;0;L;;;;;N;;;;; 28E5;BRAILLE PATTERN DOTS-13678;So;0;L;;;;;N;;;;; 28E6;BRAILLE PATTERN DOTS-23678;So;0;L;;;;;N;;;;; 28E7;BRAILLE PATTERN DOTS-123678;So;0;L;;;;;N;;;;; 28E8;BRAILLE PATTERN DOTS-4678;So;0;L;;;;;N;;;;; 28E9;BRAILLE PATTERN DOTS-14678;So;0;L;;;;;N;;;;; 28EA;BRAILLE PATTERN DOTS-24678;So;0;L;;;;;N;;;;; 28EB;BRAILLE PATTERN DOTS-124678;So;0;L;;;;;N;;;;; 28EC;BRAILLE PATTERN DOTS-34678;So;0;L;;;;;N;;;;; 28ED;BRAILLE PATTERN DOTS-134678;So;0;L;;;;;N;;;;; 28EE;BRAILLE PATTERN DOTS-234678;So;0;L;;;;;N;;;;; 28EF;BRAILLE PATTERN DOTS-1234678;So;0;L;;;;;N;;;;; 28F0;BRAILLE PATTERN DOTS-5678;So;0;L;;;;;N;;;;; 28F1;BRAILLE PATTERN DOTS-15678;So;0;L;;;;;N;;;;; 28F2;BRAILLE PATTERN DOTS-25678;So;0;L;;;;;N;;;;; 28F3;BRAILLE PATTERN DOTS-125678;So;0;L;;;;;N;;;;; 28F4;BRAILLE PATTERN DOTS-35678;So;0;L;;;;;N;;;;; 28F5;BRAILLE PATTERN DOTS-135678;So;0;L;;;;;N;;;;; 28F6;BRAILLE PATTERN DOTS-235678;So;0;L;;;;;N;;;;; 28F7;BRAILLE PATTERN DOTS-1235678;So;0;L;;;;;N;;;;; 28F8;BRAILLE PATTERN DOTS-45678;So;0;L;;;;;N;;;;; 28F9;BRAILLE PATTERN DOTS-145678;So;0;L;;;;;N;;;;; 28FA;BRAILLE PATTERN DOTS-245678;So;0;L;;;;;N;;;;; 28FB;BRAILLE PATTERN DOTS-1245678;So;0;L;;;;;N;;;;; 28FC;BRAILLE PATTERN DOTS-345678;So;0;L;;;;;N;;;;; 28FD;BRAILLE PATTERN DOTS-1345678;So;0;L;;;;;N;;;;; 28FE;BRAILLE PATTERN DOTS-2345678;So;0;L;;;;;N;;;;; 28FF;BRAILLE PATTERN DOTS-12345678;So;0;L;;;;;N;;;;; 2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; 2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; 290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; 290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; 290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;; 2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; 2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; 2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;; 2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; 291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; 291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; 291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; 291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; 291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; 291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; 2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; 2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; 2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; 2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; 2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; 2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; 2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; 292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; 292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;; 292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;; 292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; 2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; 2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;; 2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;; 2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;; 2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;; 2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;; 2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;; 293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;; 293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; 2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; 2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; 2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; 2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; 2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; 2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; 2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; 294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; 294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; 294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; 294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; 294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; 294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; 2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; 2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; 2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; 2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; 2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; 2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; 2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; 2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; 2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; 2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; 295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; 295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; 295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; 295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; 295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; 295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; 2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; 2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; 2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; 2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; 2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; 2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; 2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; 2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; 2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; 2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; 296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; 296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; 296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; 296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; 296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; 296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; 2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;; 2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; 2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; 2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; 2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;; 2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;; 297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;; 297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;; 297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;; 297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;; 2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;; 2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;; 2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;; 2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;; 2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;; 2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;; 2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;; 2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;; 2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;; 2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;; 298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;; 298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;; 298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;; 298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;; 298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;; 298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;; 2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;; 2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;; 2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;; 2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; 2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; 2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; 2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; 2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;; 2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;; 2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;; 299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;; 299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; 299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;; 299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;; 299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;; 299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;; 29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; 29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; 29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;; 29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;; 29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; 29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; 29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; 29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;; 29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;; 29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;; 29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;; 29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;; 29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;; 29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;; 29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;; 29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;; 29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;; 29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;; 29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; 29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; 29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; 29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;; 29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;; 29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;; 29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; 29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;; 29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;; 29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;; 29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; 29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;; 29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;; 29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;; 29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;; 29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; 29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; 29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; 29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; 29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;; 29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; 29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;; 29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;; 29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; 29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; 29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;; 29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;; 29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;; 29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;; 29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; 29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;; 29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;; 29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;; 29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; 29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; 29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; 29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; 29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;; 29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;; 29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;; 29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;; 29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;; 29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;; 29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;; 29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; 29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; 29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; 29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;; 29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;; 29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; 29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; 29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;; 29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; 29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; 29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;; 29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;; 29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;; 29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; 29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;; 29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;; 29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;; 29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; 29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; 29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;; 29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; 29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;; 29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;; 29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; 29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; 29FE;TINY;Sm;0;ON;;;;;N;;;;; 29FF;MINY;Sm;0;ON;;;;;N;;;;; 2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; 2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;; 2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; 2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;; 2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;; 2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;; 2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;; 2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;; 2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;; 2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; 2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;; 2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON; 222B 222B 222B 222B;;;;Y;;;;; 2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;; 2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;; 2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;; 2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;; 2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;; 2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; 2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; 2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;; 2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;; 2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;; 2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;; 2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;; 2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;; 2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;; 2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; 2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; 2A1D;JOIN;Sm;0;ON;;;;;N;;;;; 2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;; 2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;; 2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;; 2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;; 2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; 2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;; 2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; 2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; 2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;; 2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;; 2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;; 2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;; 2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; 2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;; 2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; 2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; 2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; 2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;; 2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; 2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; 2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;; 2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; 2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; 2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; 2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;; 2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;; 2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; 2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; 2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; 2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; 2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; 2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; 2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;; 2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;; 2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;; 2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;; 2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; 2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; 2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;; 2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;; 2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; 2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;; 2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; 2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;; 2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;; 2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;; 2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;; 2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;; 2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;; 2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;; 2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; 2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; 2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; 2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;; 2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;; 2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;; 2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;; 2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;; 2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;; 2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;; 2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; 2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; 2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; 2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; 2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; 2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; 2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; 2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; 2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; 2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; 2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; 2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; 2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; 2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; 2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; 2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;; 2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;; 2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;; 2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;; 2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;; 2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; 2A74;DOUBLE COLON EQUAL;Sm;0;ON; 003A 003A 003D;;;;Y;;;;; 2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON; 003D 003D;;;;N;;;;; 2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON; 003D 003D 003D;;;;N;;;;; 2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;; 2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;; 2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; 2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; 2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; 2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; 2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; 2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; 2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;; 2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;; 2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; 2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; 2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; 2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; 2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; 2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; 2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; 2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; 2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; 2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; 2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; 2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; 2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; 2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;; 2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;; 2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; 2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; 2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; 2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; 2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;; 2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;; 2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;; 2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;; 2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;; 2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;; 2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;; 2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; 2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; 2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; 2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; 2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; 2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; 2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; 2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; 2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; 2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; 2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;; 2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;; 2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; 2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; 2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; 2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; 2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;; 2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;; 2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;; 2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;; 2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;; 2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;;;; 2ADD;NONFORKING;Sm;0;ON;;;;;N;;;;; 2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;; 2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; 2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;; 2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;; 2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; 2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;; 2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;; 2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; 2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; 2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;; 2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;; 2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; 2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; 2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;; 2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; 2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; 2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; 2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; 2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; 2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;; 2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; 2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;; 2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; 2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; 2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; 2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;; 2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;; 2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; 2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; 2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; 2B00;NORTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;; 2B01;NORTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;; 2B02;SOUTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;; 2B03;SOUTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;; 2B04;LEFT RIGHT WHITE ARROW;So;0;ON;;;;;N;;;;; 2B05;LEFTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; 2B06;UPWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; 2B07;DOWNWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; 2B08;NORTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;; 2B09;NORTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;; 2B0A;SOUTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;; 2B0B;SOUTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;; 2B0C;LEFT RIGHT BLACK ARROW;So;0;ON;;;;;N;;;;; 2B0D;UP DOWN BLACK ARROW;So;0;ON;;;;;N;;;;; 2B0E;RIGHTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;; 2B0F;RIGHTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;; 2B10;LEFTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;; 2B11;LEFTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;; 2B12;SQUARE WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;; 2B13;SQUARE WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;; 2B14;SQUARE WITH UPPER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; 2B15;SQUARE WITH LOWER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; 2B16;DIAMOND WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; 2B17;DIAMOND WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; 2B18;DIAMOND WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;; 2B19;DIAMOND WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;; 2B1A;DOTTED SQUARE;So;0;ON;;;;;N;;;;; 2B1B;BLACK LARGE SQUARE;So;0;ON;;;;;N;;;;; 2B1C;WHITE LARGE SQUARE;So;0;ON;;;;;N;;;;; 2B1D;BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; 2B1E;WHITE VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; 2B1F;BLACK PENTAGON;So;0;ON;;;;;N;;;;; 2B20;WHITE PENTAGON;So;0;ON;;;;;N;;;;; 2B21;WHITE HEXAGON;So;0;ON;;;;;N;;;;; 2B22;BLACK HEXAGON;So;0;ON;;;;;N;;;;; 2B23;HORIZONTAL BLACK HEXAGON;So;0;ON;;;;;N;;;;; 2B24;BLACK LARGE CIRCLE;So;0;ON;;;;;N;;;;; 2B25;BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; 2B26;WHITE MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; 2B27;BLACK MEDIUM LOZENGE;So;0;ON;;;;;N;;;;; 2B28;WHITE MEDIUM LOZENGE;So;0;ON;;;;;N;;;;; 2B29;BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;; 2B2A;BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;; 2B2B;WHITE SMALL LOZENGE;So;0;ON;;;;;N;;;;; 2B2C;BLACK HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;; 2B2D;WHITE HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;; 2B2E;BLACK VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;; 2B2F;WHITE VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;; 2B30;LEFT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; 2B31;THREE LEFTWARDS ARROWS;Sm;0;ON;;;;;N;;;;; 2B32;LEFT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; 2B33;LONG LEFTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;; 2B34;LEFTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B35;LEFTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B36;LEFTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; 2B37;LEFTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; 2B38;LEFTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;; 2B39;LEFTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B3A;LEFTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B3B;LEFTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;; 2B3C;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B3D;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; 2B3E;LEFTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; 2B3F;WAVE ARROW POINTING DIRECTLY LEFT;Sm;0;ON;;;;;N;;;;; 2B40;EQUALS SIGN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2B41;REVERSE TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2B42;LEFTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; 2B43;RIGHTWARDS ARROW THROUGH GREATER-THAN;Sm;0;ON;;;;;N;;;;; 2B44;RIGHTWARDS ARROW THROUGH SUPERSET;Sm;0;ON;;;;;N;;;;; 2B45;LEFTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;; 2B46;RIGHTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;; 2B47;REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2B48;RIGHTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; 2B49;TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; 2B4A;LEFTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; 2B4B;LEFTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; 2B4C;RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; 2B4D;DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW;So;0;ON;;;;;N;;;;; 2B4E;SHORT SLANTED NORTH ARROW;So;0;ON;;;;;N;;;;; 2B4F;SHORT BACKSLANTED SOUTH ARROW;So;0;ON;;;;;N;;;;; 2B50;WHITE MEDIUM STAR;So;0;ON;;;;;N;;;;; 2B51;BLACK SMALL STAR;So;0;ON;;;;;N;;;;; 2B52;WHITE SMALL STAR;So;0;ON;;;;;N;;;;; 2B53;BLACK RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;; 2B54;WHITE RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;; 2B55;HEAVY LARGE CIRCLE;So;0;ON;;;;;N;;;;; 2B56;HEAVY OVAL WITH OVAL INSIDE;So;0;ON;;;;;N;;;;; 2B57;HEAVY CIRCLE WITH CIRCLE INSIDE;So;0;ON;;;;;N;;;;; 2B58;HEAVY CIRCLE;So;0;ON;;;;;N;;;;; 2B59;HEAVY CIRCLED SALTIRE;So;0;ON;;;;;N;;;;; 2B5A;SLANTED NORTH ARROW WITH HOOKED HEAD;So;0;ON;;;;;N;;;;; 2B5B;BACKSLANTED SOUTH ARROW WITH HOOKED TAIL;So;0;ON;;;;;N;;;;; 2B5C;SLANTED NORTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;; 2B5D;BACKSLANTED SOUTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;; 2B5E;BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;; 2B5F;SHORT BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;; 2B60;LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B61;UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B62;RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B63;DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B64;LEFT RIGHT TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B65;UP DOWN TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B66;NORTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B67;NORTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B68;SOUTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B69;SOUTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B6A;LEFTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; 2B6B;UPWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; 2B6C;RIGHTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; 2B6D;DOWNWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; 2B6E;CLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; 2B6F;ANTICLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; 2B70;LEFTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B71;UPWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B72;RIGHTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B73;DOWNWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B76;NORTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B77;NORTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B78;SOUTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B79;SOUTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; 2B7A;LEFTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; 2B7B;UPWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; 2B7C;RIGHTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; 2B7D;DOWNWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; 2B7E;HORIZONTAL TAB KEY;So;0;ON;;;;;N;;;;; 2B7F;VERTICAL TAB KEY;So;0;ON;;;;;N;;;;; 2B80;LEFTWARDS TRIANGLE-HEADED ARROW OVER RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B81;UPWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B82;RIGHTWARDS TRIANGLE-HEADED ARROW OVER LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B83;DOWNWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; 2B84;LEFTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; 2B85;UPWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; 2B86;RIGHTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; 2B87;DOWNWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; 2B88;LEFTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; 2B89;UPWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; 2B8A;RIGHTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; 2B8B;DOWNWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; 2B8C;ANTICLOCKWISE TRIANGLE-HEADED RIGHT U-SHAPED ARROW;So;0;ON;;;;;N;;;;; 2B8D;ANTICLOCKWISE TRIANGLE-HEADED BOTTOM U-SHAPED ARROW;So;0;ON;;;;;N;;;;; 2B8E;ANTICLOCKWISE TRIANGLE-HEADED LEFT U-SHAPED ARROW;So;0;ON;;;;;N;;;;; 2B8F;ANTICLOCKWISE TRIANGLE-HEADED TOP U-SHAPED ARROW;So;0;ON;;;;;N;;;;; 2B90;RETURN LEFT;So;0;ON;;;;;N;;;;; 2B91;RETURN RIGHT;So;0;ON;;;;;N;;;;; 2B92;NEWLINE LEFT;So;0;ON;;;;;N;;;;; 2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;; 2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;; 2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; 2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9A;THREE-D TOP-LIGHTED RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9B;THREE-D LEFT-LIGHTED DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9C;BLACK LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9D;BLACK UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9E;BLACK RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B9F;BLACK DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2BA0;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;; 2BA1;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;; 2BA2;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;; 2BA3;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;; 2BA4;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;; 2BA5;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;; 2BA6;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;; 2BA7;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;; 2BA8;BLACK CURVED DOWNWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;; 2BA9;BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAA;BLACK CURVED UPWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAB;BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAC;BLACK CURVED LEFTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAD;BLACK CURVED RIGHTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAE;BLACK CURVED LEFTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; 2BAF;BLACK CURVED RIGHTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; 2BB0;RIBBON ARROW DOWN LEFT;So;0;ON;;;;;N;;;;; 2BB1;RIBBON ARROW DOWN RIGHT;So;0;ON;;;;;N;;;;; 2BB2;RIBBON ARROW UP LEFT;So;0;ON;;;;;N;;;;; 2BB3;RIBBON ARROW UP RIGHT;So;0;ON;;;;;N;;;;; 2BB4;RIBBON ARROW LEFT UP;So;0;ON;;;;;N;;;;; 2BB5;RIBBON ARROW RIGHT UP;So;0;ON;;;;;N;;;;; 2BB6;RIBBON ARROW LEFT DOWN;So;0;ON;;;;;N;;;;; 2BB7;RIBBON ARROW RIGHT DOWN;So;0;ON;;;;;N;;;;; 2BB8;UPWARDS WHITE ARROW FROM BAR WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;; 2BB9;UP ARROWHEAD IN A RECTANGLE BOX;So;0;ON;;;;;N;;;;; 2BBD;BALLOT BOX WITH LIGHT X;So;0;ON;;;;;N;;;;; 2BBE;CIRCLED X;So;0;ON;;;;;N;;;;; 2BBF;CIRCLED BOLD X;So;0;ON;;;;;N;;;;; 2BC0;BLACK SQUARE CENTRED;So;0;ON;;;;;N;;;;; 2BC1;BLACK DIAMOND CENTRED;So;0;ON;;;;;N;;;;; 2BC2;TURNED BLACK PENTAGON;So;0;ON;;;;;N;;;;; 2BC3;HORIZONTAL BLACK OCTAGON;So;0;ON;;;;;N;;;;; 2BC4;BLACK OCTAGON;So;0;ON;;;;;N;;;;; 2BC5;BLACK MEDIUM UP-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; 2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; 2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; 2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; 2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; 2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; 2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;; 2BCD;ROTATED LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;; 2BCE;WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;; 2BCF;ROTATED WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;; 2BD0;SQUARE POSITION INDICATOR;So;0;ON;;;;;N;;;;; 2BD1;UNCERTAINTY SIGN;So;0;ON;;;;;N;;;;; 2BEC;LEFTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; 2BED;UPWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; 2BEE;RIGHTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; 2BEF;DOWNWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; 2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30; 2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31; 2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32; 2C03;GLAGOLITIC CAPITAL LETTER GLAGOLI;Lu;0;L;;;;;N;;;;2C33; 2C04;GLAGOLITIC CAPITAL LETTER DOBRO;Lu;0;L;;;;;N;;;;2C34; 2C05;GLAGOLITIC CAPITAL LETTER YESTU;Lu;0;L;;;;;N;;;;2C35; 2C06;GLAGOLITIC CAPITAL LETTER ZHIVETE;Lu;0;L;;;;;N;;;;2C36; 2C07;GLAGOLITIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;2C37; 2C08;GLAGOLITIC CAPITAL LETTER ZEMLJA;Lu;0;L;;;;;N;;;;2C38; 2C09;GLAGOLITIC CAPITAL LETTER IZHE;Lu;0;L;;;;;N;;;;2C39; 2C0A;GLAGOLITIC CAPITAL LETTER INITIAL IZHE;Lu;0;L;;;;;N;;;;2C3A; 2C0B;GLAGOLITIC CAPITAL LETTER I;Lu;0;L;;;;;N;;;;2C3B; 2C0C;GLAGOLITIC CAPITAL LETTER DJERVI;Lu;0;L;;;;;N;;;;2C3C; 2C0D;GLAGOLITIC CAPITAL LETTER KAKO;Lu;0;L;;;;;N;;;;2C3D; 2C0E;GLAGOLITIC CAPITAL LETTER LJUDIJE;Lu;0;L;;;;;N;;;;2C3E; 2C0F;GLAGOLITIC CAPITAL LETTER MYSLITE;Lu;0;L;;;;;N;;;;2C3F; 2C10;GLAGOLITIC CAPITAL LETTER NASHI;Lu;0;L;;;;;N;;;;2C40; 2C11;GLAGOLITIC CAPITAL LETTER ONU;Lu;0;L;;;;;N;;;;2C41; 2C12;GLAGOLITIC CAPITAL LETTER POKOJI;Lu;0;L;;;;;N;;;;2C42; 2C13;GLAGOLITIC CAPITAL LETTER RITSI;Lu;0;L;;;;;N;;;;2C43; 2C14;GLAGOLITIC CAPITAL LETTER SLOVO;Lu;0;L;;;;;N;;;;2C44; 2C15;GLAGOLITIC CAPITAL LETTER TVRIDO;Lu;0;L;;;;;N;;;;2C45; 2C16;GLAGOLITIC CAPITAL LETTER UKU;Lu;0;L;;;;;N;;;;2C46; 2C17;GLAGOLITIC CAPITAL LETTER FRITU;Lu;0;L;;;;;N;;;;2C47; 2C18;GLAGOLITIC CAPITAL LETTER HERU;Lu;0;L;;;;;N;;;;2C48; 2C19;GLAGOLITIC CAPITAL LETTER OTU;Lu;0;L;;;;;N;;;;2C49; 2C1A;GLAGOLITIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;2C4A; 2C1B;GLAGOLITIC CAPITAL LETTER SHTA;Lu;0;L;;;;;N;;;;2C4B; 2C1C;GLAGOLITIC CAPITAL LETTER TSI;Lu;0;L;;;;;N;;;;2C4C; 2C1D;GLAGOLITIC CAPITAL LETTER CHRIVI;Lu;0;L;;;;;N;;;;2C4D; 2C1E;GLAGOLITIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;2C4E; 2C1F;GLAGOLITIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;;;;2C4F; 2C20;GLAGOLITIC CAPITAL LETTER YERI;Lu;0;L;;;;;N;;;;2C50; 2C21;GLAGOLITIC CAPITAL LETTER YATI;Lu;0;L;;;;;N;;;;2C51; 2C22;GLAGOLITIC CAPITAL LETTER SPIDERY HA;Lu;0;L;;;;;N;;;;2C52; 2C23;GLAGOLITIC CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;2C53; 2C24;GLAGOLITIC CAPITAL LETTER SMALL YUS;Lu;0;L;;;;;N;;;;2C54; 2C25;GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL;Lu;0;L;;;;;N;;;;2C55; 2C26;GLAGOLITIC CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;2C56; 2C27;GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS;Lu;0;L;;;;;N;;;;2C57; 2C28;GLAGOLITIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;2C58; 2C29;GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS;Lu;0;L;;;;;N;;;;2C59; 2C2A;GLAGOLITIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;2C5A; 2C2B;GLAGOLITIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;2C5B; 2C2C;GLAGOLITIC CAPITAL LETTER SHTAPIC;Lu;0;L;;;;;N;;;;2C5C; 2C2D;GLAGOLITIC CAPITAL LETTER TROKUTASTI A;Lu;0;L;;;;;N;;;;2C5D; 2C2E;GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE;Lu;0;L;;;;;N;;;;2C5E; 2C30;GLAGOLITIC SMALL LETTER AZU;Ll;0;L;;;;;N;;;2C00;;2C00 2C31;GLAGOLITIC SMALL LETTER BUKY;Ll;0;L;;;;;N;;;2C01;;2C01 2C32;GLAGOLITIC SMALL LETTER VEDE;Ll;0;L;;;;;N;;;2C02;;2C02 2C33;GLAGOLITIC SMALL LETTER GLAGOLI;Ll;0;L;;;;;N;;;2C03;;2C03 2C34;GLAGOLITIC SMALL LETTER DOBRO;Ll;0;L;;;;;N;;;2C04;;2C04 2C35;GLAGOLITIC SMALL LETTER YESTU;Ll;0;L;;;;;N;;;2C05;;2C05 2C36;GLAGOLITIC SMALL LETTER ZHIVETE;Ll;0;L;;;;;N;;;2C06;;2C06 2C37;GLAGOLITIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;2C07;;2C07 2C38;GLAGOLITIC SMALL LETTER ZEMLJA;Ll;0;L;;;;;N;;;2C08;;2C08 2C39;GLAGOLITIC SMALL LETTER IZHE;Ll;0;L;;;;;N;;;2C09;;2C09 2C3A;GLAGOLITIC SMALL LETTER INITIAL IZHE;Ll;0;L;;;;;N;;;2C0A;;2C0A 2C3B;GLAGOLITIC SMALL LETTER I;Ll;0;L;;;;;N;;;2C0B;;2C0B 2C3C;GLAGOLITIC SMALL LETTER DJERVI;Ll;0;L;;;;;N;;;2C0C;;2C0C 2C3D;GLAGOLITIC SMALL LETTER KAKO;Ll;0;L;;;;;N;;;2C0D;;2C0D 2C3E;GLAGOLITIC SMALL LETTER LJUDIJE;Ll;0;L;;;;;N;;;2C0E;;2C0E 2C3F;GLAGOLITIC SMALL LETTER MYSLITE;Ll;0;L;;;;;N;;;2C0F;;2C0F 2C40;GLAGOLITIC SMALL LETTER NASHI;Ll;0;L;;;;;N;;;2C10;;2C10 2C41;GLAGOLITIC SMALL LETTER ONU;Ll;0;L;;;;;N;;;2C11;;2C11 2C42;GLAGOLITIC SMALL LETTER POKOJI;Ll;0;L;;;;;N;;;2C12;;2C12 2C43;GLAGOLITIC SMALL LETTER RITSI;Ll;0;L;;;;;N;;;2C13;;2C13 2C44;GLAGOLITIC SMALL LETTER SLOVO;Ll;0;L;;;;;N;;;2C14;;2C14 2C45;GLAGOLITIC SMALL LETTER TVRIDO;Ll;0;L;;;;;N;;;2C15;;2C15 2C46;GLAGOLITIC SMALL LETTER UKU;Ll;0;L;;;;;N;;;2C16;;2C16 2C47;GLAGOLITIC SMALL LETTER FRITU;Ll;0;L;;;;;N;;;2C17;;2C17 2C48;GLAGOLITIC SMALL LETTER HERU;Ll;0;L;;;;;N;;;2C18;;2C18 2C49;GLAGOLITIC SMALL LETTER OTU;Ll;0;L;;;;;N;;;2C19;;2C19 2C4A;GLAGOLITIC SMALL LETTER PE;Ll;0;L;;;;;N;;;2C1A;;2C1A 2C4B;GLAGOLITIC SMALL LETTER SHTA;Ll;0;L;;;;;N;;;2C1B;;2C1B 2C4C;GLAGOLITIC SMALL LETTER TSI;Ll;0;L;;;;;N;;;2C1C;;2C1C 2C4D;GLAGOLITIC SMALL LETTER CHRIVI;Ll;0;L;;;;;N;;;2C1D;;2C1D 2C4E;GLAGOLITIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;2C1E;;2C1E 2C4F;GLAGOLITIC SMALL LETTER YERU;Ll;0;L;;;;;N;;;2C1F;;2C1F 2C50;GLAGOLITIC SMALL LETTER YERI;Ll;0;L;;;;;N;;;2C20;;2C20 2C51;GLAGOLITIC SMALL LETTER YATI;Ll;0;L;;;;;N;;;2C21;;2C21 2C52;GLAGOLITIC SMALL LETTER SPIDERY HA;Ll;0;L;;;;;N;;;2C22;;2C22 2C53;GLAGOLITIC SMALL LETTER YU;Ll;0;L;;;;;N;;;2C23;;2C23 2C54;GLAGOLITIC SMALL LETTER SMALL YUS;Ll;0;L;;;;;N;;;2C24;;2C24 2C55;GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL;Ll;0;L;;;;;N;;;2C25;;2C25 2C56;GLAGOLITIC SMALL LETTER YO;Ll;0;L;;;;;N;;;2C26;;2C26 2C57;GLAGOLITIC SMALL LETTER IOTATED SMALL YUS;Ll;0;L;;;;;N;;;2C27;;2C27 2C58;GLAGOLITIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;2C28;;2C28 2C59;GLAGOLITIC SMALL LETTER IOTATED BIG YUS;Ll;0;L;;;;;N;;;2C29;;2C29 2C5A;GLAGOLITIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;2C2A;;2C2A 2C5B;GLAGOLITIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;2C2B;;2C2B 2C5C;GLAGOLITIC SMALL LETTER SHTAPIC;Ll;0;L;;;;;N;;;2C2C;;2C2C 2C5D;GLAGOLITIC SMALL LETTER TROKUTASTI A;Ll;0;L;;;;;N;;;2C2D;;2C2D 2C5E;GLAGOLITIC SMALL LETTER LATINATE MYSLITE;Ll;0;L;;;;;N;;;2C2E;;2C2E 2C60;LATIN CAPITAL LETTER L WITH DOUBLE BAR;Lu;0;L;;;;;N;;;;2C61; 2C61;LATIN SMALL LETTER L WITH DOUBLE BAR;Ll;0;L;;;;;N;;;2C60;;2C60 2C62;LATIN CAPITAL LETTER L WITH MIDDLE TILDE;Lu;0;L;;;;;N;;;;026B; 2C63;LATIN CAPITAL LETTER P WITH STROKE;Lu;0;L;;;;;N;;;;1D7D; 2C64;LATIN CAPITAL LETTER R WITH TAIL;Lu;0;L;;;;;N;;;;027D; 2C65;LATIN SMALL LETTER A WITH STROKE;Ll;0;L;;;;;N;;;023A;;023A 2C66;LATIN SMALL LETTER T WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;023E;;023E 2C67;LATIN CAPITAL LETTER H WITH DESCENDER;Lu;0;L;;;;;N;;;;2C68; 2C68;LATIN SMALL LETTER H WITH DESCENDER;Ll;0;L;;;;;N;;;2C67;;2C67 2C69;LATIN CAPITAL LETTER K WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6A; 2C6A;LATIN SMALL LETTER K WITH DESCENDER;Ll;0;L;;;;;N;;;2C69;;2C69 2C6B;LATIN CAPITAL LETTER Z WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6C; 2C6C;LATIN SMALL LETTER Z WITH DESCENDER;Ll;0;L;;;;;N;;;2C6B;;2C6B 2C6D;LATIN CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;0251; 2C6E;LATIN CAPITAL LETTER M WITH HOOK;Lu;0;L;;;;;N;;;;0271; 2C6F;LATIN CAPITAL LETTER TURNED A;Lu;0;L;;;;;N;;;;0250; 2C70;LATIN CAPITAL LETTER TURNED ALPHA;Lu;0;L;;;;;N;;;;0252; 2C71;LATIN SMALL LETTER V WITH RIGHT HOOK;Ll;0;L;;;;;N;;;;; 2C72;LATIN CAPITAL LETTER W WITH HOOK;Lu;0;L;;;;;N;;;;2C73; 2C73;LATIN SMALL LETTER W WITH HOOK;Ll;0;L;;;;;N;;;2C72;;2C72 2C74;LATIN SMALL LETTER V WITH CURL;Ll;0;L;;;;;N;;;;; 2C75;LATIN CAPITAL LETTER HALF H;Lu;0;L;;;;;N;;;;2C76; 2C76;LATIN SMALL LETTER HALF H;Ll;0;L;;;;;N;;;2C75;;2C75 2C77;LATIN SMALL LETTER TAILLESS PHI;Ll;0;L;;;;;N;;;;; 2C78;LATIN SMALL LETTER E WITH NOTCH;Ll;0;L;;;;;N;;;;; 2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;; 2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;; 2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;; 2C7C;LATIN SUBSCRIPT SMALL LETTER J;Lm;0;L; 006A;;;;N;;;;; 2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L; 0056;;;;N;;;;; 2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F; 2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240; 2C80;COPTIC CAPITAL LETTER ALFA;Lu;0;L;;;;;N;;;;2C81; 2C81;COPTIC SMALL LETTER ALFA;Ll;0;L;;;;;N;;;2C80;;2C80 2C82;COPTIC CAPITAL LETTER VIDA;Lu;0;L;;;;;N;;;;2C83; 2C83;COPTIC SMALL LETTER VIDA;Ll;0;L;;;;;N;;;2C82;;2C82 2C84;COPTIC CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;2C85; 2C85;COPTIC SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;2C84;;2C84 2C86;COPTIC CAPITAL LETTER DALDA;Lu;0;L;;;;;N;;;;2C87; 2C87;COPTIC SMALL LETTER DALDA;Ll;0;L;;;;;N;;;2C86;;2C86 2C88;COPTIC CAPITAL LETTER EIE;Lu;0;L;;;;;N;;;;2C89; 2C89;COPTIC SMALL LETTER EIE;Ll;0;L;;;;;N;;;2C88;;2C88 2C8A;COPTIC CAPITAL LETTER SOU;Lu;0;L;;;;;N;;;;2C8B; 2C8B;COPTIC SMALL LETTER SOU;Ll;0;L;;;;;N;;;2C8A;;2C8A 2C8C;COPTIC CAPITAL LETTER ZATA;Lu;0;L;;;;;N;;;;2C8D; 2C8D;COPTIC SMALL LETTER ZATA;Ll;0;L;;;;;N;;;2C8C;;2C8C 2C8E;COPTIC CAPITAL LETTER HATE;Lu;0;L;;;;;N;;;;2C8F; 2C8F;COPTIC SMALL LETTER HATE;Ll;0;L;;;;;N;;;2C8E;;2C8E 2C90;COPTIC CAPITAL LETTER THETHE;Lu;0;L;;;;;N;;;;2C91; 2C91;COPTIC SMALL LETTER THETHE;Ll;0;L;;;;;N;;;2C90;;2C90 2C92;COPTIC CAPITAL LETTER IAUDA;Lu;0;L;;;;;N;;;;2C93; 2C93;COPTIC SMALL LETTER IAUDA;Ll;0;L;;;;;N;;;2C92;;2C92 2C94;COPTIC CAPITAL LETTER KAPA;Lu;0;L;;;;;N;;;;2C95; 2C95;COPTIC SMALL LETTER KAPA;Ll;0;L;;;;;N;;;2C94;;2C94 2C96;COPTIC CAPITAL LETTER LAULA;Lu;0;L;;;;;N;;;;2C97; 2C97;COPTIC SMALL LETTER LAULA;Ll;0;L;;;;;N;;;2C96;;2C96 2C98;COPTIC CAPITAL LETTER MI;Lu;0;L;;;;;N;;;;2C99; 2C99;COPTIC SMALL LETTER MI;Ll;0;L;;;;;N;;;2C98;;2C98 2C9A;COPTIC CAPITAL LETTER NI;Lu;0;L;;;;;N;;;;2C9B; 2C9B;COPTIC SMALL LETTER NI;Ll;0;L;;;;;N;;;2C9A;;2C9A 2C9C;COPTIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;2C9D; 2C9D;COPTIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;2C9C;;2C9C 2C9E;COPTIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;2C9F; 2C9F;COPTIC SMALL LETTER O;Ll;0;L;;;;;N;;;2C9E;;2C9E 2CA0;COPTIC CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;2CA1; 2CA1;COPTIC SMALL LETTER PI;Ll;0;L;;;;;N;;;2CA0;;2CA0 2CA2;COPTIC CAPITAL LETTER RO;Lu;0;L;;;;;N;;;;2CA3; 2CA3;COPTIC SMALL LETTER RO;Ll;0;L;;;;;N;;;2CA2;;2CA2 2CA4;COPTIC CAPITAL LETTER SIMA;Lu;0;L;;;;;N;;;;2CA5; 2CA5;COPTIC SMALL LETTER SIMA;Ll;0;L;;;;;N;;;2CA4;;2CA4 2CA6;COPTIC CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;2CA7; 2CA7;COPTIC SMALL LETTER TAU;Ll;0;L;;;;;N;;;2CA6;;2CA6 2CA8;COPTIC CAPITAL LETTER UA;Lu;0;L;;;;;N;;;;2CA9; 2CA9;COPTIC SMALL LETTER UA;Ll;0;L;;;;;N;;;2CA8;;2CA8 2CAA;COPTIC CAPITAL LETTER FI;Lu;0;L;;;;;N;;;;2CAB; 2CAB;COPTIC SMALL LETTER FI;Ll;0;L;;;;;N;;;2CAA;;2CAA 2CAC;COPTIC CAPITAL LETTER KHI;Lu;0;L;;;;;N;;;;2CAD; 2CAD;COPTIC SMALL LETTER KHI;Ll;0;L;;;;;N;;;2CAC;;2CAC 2CAE;COPTIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;2CAF; 2CAF;COPTIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;2CAE;;2CAE 2CB0;COPTIC CAPITAL LETTER OOU;Lu;0;L;;;;;N;;;;2CB1; 2CB1;COPTIC SMALL LETTER OOU;Ll;0;L;;;;;N;;;2CB0;;2CB0 2CB2;COPTIC CAPITAL LETTER DIALECT-P ALEF;Lu;0;L;;;;;N;;;;2CB3; 2CB3;COPTIC SMALL LETTER DIALECT-P ALEF;Ll;0;L;;;;;N;;;2CB2;;2CB2 2CB4;COPTIC CAPITAL LETTER OLD COPTIC AIN;Lu;0;L;;;;;N;;;;2CB5; 2CB5;COPTIC SMALL LETTER OLD COPTIC AIN;Ll;0;L;;;;;N;;;2CB4;;2CB4 2CB6;COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE;Lu;0;L;;;;;N;;;;2CB7; 2CB7;COPTIC SMALL LETTER CRYPTOGRAMMIC EIE;Ll;0;L;;;;;N;;;2CB6;;2CB6 2CB8;COPTIC CAPITAL LETTER DIALECT-P KAPA;Lu;0;L;;;;;N;;;;2CB9; 2CB9;COPTIC SMALL LETTER DIALECT-P KAPA;Ll;0;L;;;;;N;;;2CB8;;2CB8 2CBA;COPTIC CAPITAL LETTER DIALECT-P NI;Lu;0;L;;;;;N;;;;2CBB; 2CBB;COPTIC SMALL LETTER DIALECT-P NI;Ll;0;L;;;;;N;;;2CBA;;2CBA 2CBC;COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI;Lu;0;L;;;;;N;;;;2CBD; 2CBD;COPTIC SMALL LETTER CRYPTOGRAMMIC NI;Ll;0;L;;;;;N;;;2CBC;;2CBC 2CBE;COPTIC CAPITAL LETTER OLD COPTIC OOU;Lu;0;L;;;;;N;;;;2CBF; 2CBF;COPTIC SMALL LETTER OLD COPTIC OOU;Ll;0;L;;;;;N;;;2CBE;;2CBE 2CC0;COPTIC CAPITAL LETTER SAMPI;Lu;0;L;;;;;N;;;;2CC1; 2CC1;COPTIC SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;2CC0;;2CC0 2CC2;COPTIC CAPITAL LETTER CROSSED SHEI;Lu;0;L;;;;;N;;;;2CC3; 2CC3;COPTIC SMALL LETTER CROSSED SHEI;Ll;0;L;;;;;N;;;2CC2;;2CC2 2CC4;COPTIC CAPITAL LETTER OLD COPTIC SHEI;Lu;0;L;;;;;N;;;;2CC5; 2CC5;COPTIC SMALL LETTER OLD COPTIC SHEI;Ll;0;L;;;;;N;;;2CC4;;2CC4 2CC6;COPTIC CAPITAL LETTER OLD COPTIC ESH;Lu;0;L;;;;;N;;;;2CC7; 2CC7;COPTIC SMALL LETTER OLD COPTIC ESH;Ll;0;L;;;;;N;;;2CC6;;2CC6 2CC8;COPTIC CAPITAL LETTER AKHMIMIC KHEI;Lu;0;L;;;;;N;;;;2CC9; 2CC9;COPTIC SMALL LETTER AKHMIMIC KHEI;Ll;0;L;;;;;N;;;2CC8;;2CC8 2CCA;COPTIC CAPITAL LETTER DIALECT-P HORI;Lu;0;L;;;;;N;;;;2CCB; 2CCB;COPTIC SMALL LETTER DIALECT-P HORI;Ll;0;L;;;;;N;;;2CCA;;2CCA 2CCC;COPTIC CAPITAL LETTER OLD COPTIC HORI;Lu;0;L;;;;;N;;;;2CCD; 2CCD;COPTIC SMALL LETTER OLD COPTIC HORI;Ll;0;L;;;;;N;;;2CCC;;2CCC 2CCE;COPTIC CAPITAL LETTER OLD COPTIC HA;Lu;0;L;;;;;N;;;;2CCF; 2CCF;COPTIC SMALL LETTER OLD COPTIC HA;Ll;0;L;;;;;N;;;2CCE;;2CCE 2CD0;COPTIC CAPITAL LETTER L-SHAPED HA;Lu;0;L;;;;;N;;;;2CD1; 2CD1;COPTIC SMALL LETTER L-SHAPED HA;Ll;0;L;;;;;N;;;2CD0;;2CD0 2CD2;COPTIC CAPITAL LETTER OLD COPTIC HEI;Lu;0;L;;;;;N;;;;2CD3; 2CD3;COPTIC SMALL LETTER OLD COPTIC HEI;Ll;0;L;;;;;N;;;2CD2;;2CD2 2CD4;COPTIC CAPITAL LETTER OLD COPTIC HAT;Lu;0;L;;;;;N;;;;2CD5; 2CD5;COPTIC SMALL LETTER OLD COPTIC HAT;Ll;0;L;;;;;N;;;2CD4;;2CD4 2CD6;COPTIC CAPITAL LETTER OLD COPTIC GANGIA;Lu;0;L;;;;;N;;;;2CD7; 2CD7;COPTIC SMALL LETTER OLD COPTIC GANGIA;Ll;0;L;;;;;N;;;2CD6;;2CD6 2CD8;COPTIC CAPITAL LETTER OLD COPTIC DJA;Lu;0;L;;;;;N;;;;2CD9; 2CD9;COPTIC SMALL LETTER OLD COPTIC DJA;Ll;0;L;;;;;N;;;2CD8;;2CD8 2CDA;COPTIC CAPITAL LETTER OLD COPTIC SHIMA;Lu;0;L;;;;;N;;;;2CDB; 2CDB;COPTIC SMALL LETTER OLD COPTIC SHIMA;Ll;0;L;;;;;N;;;2CDA;;2CDA 2CDC;COPTIC CAPITAL LETTER OLD NUBIAN SHIMA;Lu;0;L;;;;;N;;;;2CDD; 2CDD;COPTIC SMALL LETTER OLD NUBIAN SHIMA;Ll;0;L;;;;;N;;;2CDC;;2CDC 2CDE;COPTIC CAPITAL LETTER OLD NUBIAN NGI;Lu;0;L;;;;;N;;;;2CDF; 2CDF;COPTIC SMALL LETTER OLD NUBIAN NGI;Ll;0;L;;;;;N;;;2CDE;;2CDE 2CE0;COPTIC CAPITAL LETTER OLD NUBIAN NYI;Lu;0;L;;;;;N;;;;2CE1; 2CE1;COPTIC SMALL LETTER OLD NUBIAN NYI;Ll;0;L;;;;;N;;;2CE0;;2CE0 2CE2;COPTIC CAPITAL LETTER OLD NUBIAN WAU;Lu;0;L;;;;;N;;;;2CE3; 2CE3;COPTIC SMALL LETTER OLD NUBIAN WAU;Ll;0;L;;;;;N;;;2CE2;;2CE2 2CE4;COPTIC SYMBOL KAI;Ll;0;L;;;;;N;;;;; 2CE5;COPTIC SYMBOL MI RO;So;0;ON;;;;;N;;;;; 2CE6;COPTIC SYMBOL PI RO;So;0;ON;;;;;N;;;;; 2CE7;COPTIC SYMBOL STAUROS;So;0;ON;;;;;N;;;;; 2CE8;COPTIC SYMBOL TAU RO;So;0;ON;;;;;N;;;;; 2CE9;COPTIC SYMBOL KHI RO;So;0;ON;;;;;N;;;;; 2CEA;COPTIC SYMBOL SHIMA SIMA;So;0;ON;;;;;N;;;;; 2CEB;COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI;Lu;0;L;;;;;N;;;;2CEC; 2CEC;COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI;Ll;0;L;;;;;N;;;2CEB;;2CEB 2CED;COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA;Lu;0;L;;;;;N;;;;2CEE; 2CEE;COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA;Ll;0;L;;;;;N;;;2CED;;2CED 2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;; 2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;; 2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;; 2CF2;COPTIC CAPITAL LETTER BOHAIRIC KHEI;Lu;0;L;;;;;N;;;;2CF3; 2CF3;COPTIC SMALL LETTER BOHAIRIC KHEI;Ll;0;L;;;;;N;;;2CF2;;2CF2 2CF9;COPTIC OLD NUBIAN FULL STOP;Po;0;ON;;;;;N;;;;; 2CFA;COPTIC OLD NUBIAN DIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; 2CFB;COPTIC OLD NUBIAN INDIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; 2CFC;COPTIC OLD NUBIAN VERSE DIVIDER;Po;0;ON;;;;;N;;;;; 2CFD;COPTIC FRACTION ONE HALF;No;0;ON;;;;1/2;N;;;;; 2CFE;COPTIC FULL STOP;Po;0;ON;;;;;N;;;;; 2CFF;COPTIC MORPHOLOGICAL DIVIDER;Po;0;ON;;;;;N;;;;; 2D00;GEORGIAN SMALL LETTER AN;Ll;0;L;;;;;N;;;10A0;;10A0 2D01;GEORGIAN SMALL LETTER BAN;Ll;0;L;;;;;N;;;10A1;;10A1 2D02;GEORGIAN SMALL LETTER GAN;Ll;0;L;;;;;N;;;10A2;;10A2 2D03;GEORGIAN SMALL LETTER DON;Ll;0;L;;;;;N;;;10A3;;10A3 2D04;GEORGIAN SMALL LETTER EN;Ll;0;L;;;;;N;;;10A4;;10A4 2D05;GEORGIAN SMALL LETTER VIN;Ll;0;L;;;;;N;;;10A5;;10A5 2D06;GEORGIAN SMALL LETTER ZEN;Ll;0;L;;;;;N;;;10A6;;10A6 2D07;GEORGIAN SMALL LETTER TAN;Ll;0;L;;;;;N;;;10A7;;10A7 2D08;GEORGIAN SMALL LETTER IN;Ll;0;L;;;;;N;;;10A8;;10A8 2D09;GEORGIAN SMALL LETTER KAN;Ll;0;L;;;;;N;;;10A9;;10A9 2D0A;GEORGIAN SMALL LETTER LAS;Ll;0;L;;;;;N;;;10AA;;10AA 2D0B;GEORGIAN SMALL LETTER MAN;Ll;0;L;;;;;N;;;10AB;;10AB 2D0C;GEORGIAN SMALL LETTER NAR;Ll;0;L;;;;;N;;;10AC;;10AC 2D0D;GEORGIAN SMALL LETTER ON;Ll;0;L;;;;;N;;;10AD;;10AD 2D0E;GEORGIAN SMALL LETTER PAR;Ll;0;L;;;;;N;;;10AE;;10AE 2D0F;GEORGIAN SMALL LETTER ZHAR;Ll;0;L;;;;;N;;;10AF;;10AF 2D10;GEORGIAN SMALL LETTER RAE;Ll;0;L;;;;;N;;;10B0;;10B0 2D11;GEORGIAN SMALL LETTER SAN;Ll;0;L;;;;;N;;;10B1;;10B1 2D12;GEORGIAN SMALL LETTER TAR;Ll;0;L;;;;;N;;;10B2;;10B2 2D13;GEORGIAN SMALL LETTER UN;Ll;0;L;;;;;N;;;10B3;;10B3 2D14;GEORGIAN SMALL LETTER PHAR;Ll;0;L;;;;;N;;;10B4;;10B4 2D15;GEORGIAN SMALL LETTER KHAR;Ll;0;L;;;;;N;;;10B5;;10B5 2D16;GEORGIAN SMALL LETTER GHAN;Ll;0;L;;;;;N;;;10B6;;10B6 2D17;GEORGIAN SMALL LETTER QAR;Ll;0;L;;;;;N;;;10B7;;10B7 2D18;GEORGIAN SMALL LETTER SHIN;Ll;0;L;;;;;N;;;10B8;;10B8 2D19;GEORGIAN SMALL LETTER CHIN;Ll;0;L;;;;;N;;;10B9;;10B9 2D1A;GEORGIAN SMALL LETTER CAN;Ll;0;L;;;;;N;;;10BA;;10BA 2D1B;GEORGIAN SMALL LETTER JIL;Ll;0;L;;;;;N;;;10BB;;10BB 2D1C;GEORGIAN SMALL LETTER CIL;Ll;0;L;;;;;N;;;10BC;;10BC 2D1D;GEORGIAN SMALL LETTER CHAR;Ll;0;L;;;;;N;;;10BD;;10BD 2D1E;GEORGIAN SMALL LETTER XAN;Ll;0;L;;;;;N;;;10BE;;10BE 2D1F;GEORGIAN SMALL LETTER JHAN;Ll;0;L;;;;;N;;;10BF;;10BF 2D20;GEORGIAN SMALL LETTER HAE;Ll;0;L;;;;;N;;;10C0;;10C0 2D21;GEORGIAN SMALL LETTER HE;Ll;0;L;;;;;N;;;10C1;;10C1 2D22;GEORGIAN SMALL LETTER HIE;Ll;0;L;;;;;N;;;10C2;;10C2 2D23;GEORGIAN SMALL LETTER WE;Ll;0;L;;;;;N;;;10C3;;10C3 2D24;GEORGIAN SMALL LETTER HAR;Ll;0;L;;;;;N;;;10C4;;10C4 2D25;GEORGIAN SMALL LETTER HOE;Ll;0;L;;;;;N;;;10C5;;10C5 2D27;GEORGIAN SMALL LETTER YN;Ll;0;L;;;;;N;;;10C7;;10C7 2D2D;GEORGIAN SMALL LETTER AEN;Ll;0;L;;;;;N;;;10CD;;10CD 2D30;TIFINAGH LETTER YA;Lo;0;L;;;;;N;;;;; 2D31;TIFINAGH LETTER YAB;Lo;0;L;;;;;N;;;;; 2D32;TIFINAGH LETTER YABH;Lo;0;L;;;;;N;;;;; 2D33;TIFINAGH LETTER YAG;Lo;0;L;;;;;N;;;;; 2D34;TIFINAGH LETTER YAGHH;Lo;0;L;;;;;N;;;;; 2D35;TIFINAGH LETTER BERBER ACADEMY YAJ;Lo;0;L;;;;;N;;;;; 2D36;TIFINAGH LETTER YAJ;Lo;0;L;;;;;N;;;;; 2D37;TIFINAGH LETTER YAD;Lo;0;L;;;;;N;;;;; 2D38;TIFINAGH LETTER YADH;Lo;0;L;;;;;N;;;;; 2D39;TIFINAGH LETTER YADD;Lo;0;L;;;;;N;;;;; 2D3A;TIFINAGH LETTER YADDH;Lo;0;L;;;;;N;;;;; 2D3B;TIFINAGH LETTER YEY;Lo;0;L;;;;;N;;;;; 2D3C;TIFINAGH LETTER YAF;Lo;0;L;;;;;N;;;;; 2D3D;TIFINAGH LETTER YAK;Lo;0;L;;;;;N;;;;; 2D3E;TIFINAGH LETTER TUAREG YAK;Lo;0;L;;;;;N;;;;; 2D3F;TIFINAGH LETTER YAKHH;Lo;0;L;;;;;N;;;;; 2D40;TIFINAGH LETTER YAH;Lo;0;L;;;;;N;;;;; 2D41;TIFINAGH LETTER BERBER ACADEMY YAH;Lo;0;L;;;;;N;;;;; 2D42;TIFINAGH LETTER TUAREG YAH;Lo;0;L;;;;;N;;;;; 2D43;TIFINAGH LETTER YAHH;Lo;0;L;;;;;N;;;;; 2D44;TIFINAGH LETTER YAA;Lo;0;L;;;;;N;;;;; 2D45;TIFINAGH LETTER YAKH;Lo;0;L;;;;;N;;;;; 2D46;TIFINAGH LETTER TUAREG YAKH;Lo;0;L;;;;;N;;;;; 2D47;TIFINAGH LETTER YAQ;Lo;0;L;;;;;N;;;;; 2D48;TIFINAGH LETTER TUAREG YAQ;Lo;0;L;;;;;N;;;;; 2D49;TIFINAGH LETTER YI;Lo;0;L;;;;;N;;;;; 2D4A;TIFINAGH LETTER YAZH;Lo;0;L;;;;;N;;;;; 2D4B;TIFINAGH LETTER AHAGGAR YAZH;Lo;0;L;;;;;N;;;;; 2D4C;TIFINAGH LETTER TUAREG YAZH;Lo;0;L;;;;;N;;;;; 2D4D;TIFINAGH LETTER YAL;Lo;0;L;;;;;N;;;;; 2D4E;TIFINAGH LETTER YAM;Lo;0;L;;;;;N;;;;; 2D4F;TIFINAGH LETTER YAN;Lo;0;L;;;;;N;;;;; 2D50;TIFINAGH LETTER TUAREG YAGN;Lo;0;L;;;;;N;;;;; 2D51;TIFINAGH LETTER TUAREG YANG;Lo;0;L;;;;;N;;;;; 2D52;TIFINAGH LETTER YAP;Lo;0;L;;;;;N;;;;; 2D53;TIFINAGH LETTER YU;Lo;0;L;;;;;N;;;;; 2D54;TIFINAGH LETTER YAR;Lo;0;L;;;;;N;;;;; 2D55;TIFINAGH LETTER YARR;Lo;0;L;;;;;N;;;;; 2D56;TIFINAGH LETTER YAGH;Lo;0;L;;;;;N;;;;; 2D57;TIFINAGH LETTER TUAREG YAGH;Lo;0;L;;;;;N;;;;; 2D58;TIFINAGH LETTER AYER YAGH;Lo;0;L;;;;;N;;;;; 2D59;TIFINAGH LETTER YAS;Lo;0;L;;;;;N;;;;; 2D5A;TIFINAGH LETTER YASS;Lo;0;L;;;;;N;;;;; 2D5B;TIFINAGH LETTER YASH;Lo;0;L;;;;;N;;;;; 2D5C;TIFINAGH LETTER YAT;Lo;0;L;;;;;N;;;;; 2D5D;TIFINAGH LETTER YATH;Lo;0;L;;;;;N;;;;; 2D5E;TIFINAGH LETTER YACH;Lo;0;L;;;;;N;;;;; 2D5F;TIFINAGH LETTER YATT;Lo;0;L;;;;;N;;;;; 2D60;TIFINAGH LETTER YAV;Lo;0;L;;;;;N;;;;; 2D61;TIFINAGH LETTER YAW;Lo;0;L;;;;;N;;;;; 2D62;TIFINAGH LETTER YAY;Lo;0;L;;;;;N;;;;; 2D63;TIFINAGH LETTER YAZ;Lo;0;L;;;;;N;;;;; 2D64;TIFINAGH LETTER TAWELLEMET YAZ;Lo;0;L;;;;;N;;;;; 2D65;TIFINAGH LETTER YAZZ;Lo;0;L;;;;;N;;;;; 2D66;TIFINAGH LETTER YE;Lo;0;L;;;;;N;;;;; 2D67;TIFINAGH LETTER YO;Lo;0;L;;;;;N;;;;; 2D6F;TIFINAGH MODIFIER LETTER LABIALIZATION MARK;Lm;0;L; 2D61;;;;N;;;;; 2D70;TIFINAGH SEPARATOR MARK;Po;0;L;;;;;N;;;;; 2D7F;TIFINAGH CONSONANT JOINER;Mn;9;NSM;;;;;N;;;;; 2D80;ETHIOPIC SYLLABLE LOA;Lo;0;L;;;;;N;;;;; 2D81;ETHIOPIC SYLLABLE MOA;Lo;0;L;;;;;N;;;;; 2D82;ETHIOPIC SYLLABLE ROA;Lo;0;L;;;;;N;;;;; 2D83;ETHIOPIC SYLLABLE SOA;Lo;0;L;;;;;N;;;;; 2D84;ETHIOPIC SYLLABLE SHOA;Lo;0;L;;;;;N;;;;; 2D85;ETHIOPIC SYLLABLE BOA;Lo;0;L;;;;;N;;;;; 2D86;ETHIOPIC SYLLABLE TOA;Lo;0;L;;;;;N;;;;; 2D87;ETHIOPIC SYLLABLE COA;Lo;0;L;;;;;N;;;;; 2D88;ETHIOPIC SYLLABLE NOA;Lo;0;L;;;;;N;;;;; 2D89;ETHIOPIC SYLLABLE NYOA;Lo;0;L;;;;;N;;;;; 2D8A;ETHIOPIC SYLLABLE GLOTTAL OA;Lo;0;L;;;;;N;;;;; 2D8B;ETHIOPIC SYLLABLE ZOA;Lo;0;L;;;;;N;;;;; 2D8C;ETHIOPIC SYLLABLE DOA;Lo;0;L;;;;;N;;;;; 2D8D;ETHIOPIC SYLLABLE DDOA;Lo;0;L;;;;;N;;;;; 2D8E;ETHIOPIC SYLLABLE JOA;Lo;0;L;;;;;N;;;;; 2D8F;ETHIOPIC SYLLABLE THOA;Lo;0;L;;;;;N;;;;; 2D90;ETHIOPIC SYLLABLE CHOA;Lo;0;L;;;;;N;;;;; 2D91;ETHIOPIC SYLLABLE PHOA;Lo;0;L;;;;;N;;;;; 2D92;ETHIOPIC SYLLABLE POA;Lo;0;L;;;;;N;;;;; 2D93;ETHIOPIC SYLLABLE GGWA;Lo;0;L;;;;;N;;;;; 2D94;ETHIOPIC SYLLABLE GGWI;Lo;0;L;;;;;N;;;;; 2D95;ETHIOPIC SYLLABLE GGWEE;Lo;0;L;;;;;N;;;;; 2D96;ETHIOPIC SYLLABLE GGWE;Lo;0;L;;;;;N;;;;; 2DA0;ETHIOPIC SYLLABLE SSA;Lo;0;L;;;;;N;;;;; 2DA1;ETHIOPIC SYLLABLE SSU;Lo;0;L;;;;;N;;;;; 2DA2;ETHIOPIC SYLLABLE SSI;Lo;0;L;;;;;N;;;;; 2DA3;ETHIOPIC SYLLABLE SSAA;Lo;0;L;;;;;N;;;;; 2DA4;ETHIOPIC SYLLABLE SSEE;Lo;0;L;;;;;N;;;;; 2DA5;ETHIOPIC SYLLABLE SSE;Lo;0;L;;;;;N;;;;; 2DA6;ETHIOPIC SYLLABLE SSO;Lo;0;L;;;;;N;;;;; 2DA8;ETHIOPIC SYLLABLE CCA;Lo;0;L;;;;;N;;;;; 2DA9;ETHIOPIC SYLLABLE CCU;Lo;0;L;;;;;N;;;;; 2DAA;ETHIOPIC SYLLABLE CCI;Lo;0;L;;;;;N;;;;; 2DAB;ETHIOPIC SYLLABLE CCAA;Lo;0;L;;;;;N;;;;; 2DAC;ETHIOPIC SYLLABLE CCEE;Lo;0;L;;;;;N;;;;; 2DAD;ETHIOPIC SYLLABLE CCE;Lo;0;L;;;;;N;;;;; 2DAE;ETHIOPIC SYLLABLE CCO;Lo;0;L;;;;;N;;;;; 2DB0;ETHIOPIC SYLLABLE ZZA;Lo;0;L;;;;;N;;;;; 2DB1;ETHIOPIC SYLLABLE ZZU;Lo;0;L;;;;;N;;;;; 2DB2;ETHIOPIC SYLLABLE ZZI;Lo;0;L;;;;;N;;;;; 2DB3;ETHIOPIC SYLLABLE ZZAA;Lo;0;L;;;;;N;;;;; 2DB4;ETHIOPIC SYLLABLE ZZEE;Lo;0;L;;;;;N;;;;; 2DB5;ETHIOPIC SYLLABLE ZZE;Lo;0;L;;;;;N;;;;; 2DB6;ETHIOPIC SYLLABLE ZZO;Lo;0;L;;;;;N;;;;; 2DB8;ETHIOPIC SYLLABLE CCHA;Lo;0;L;;;;;N;;;;; 2DB9;ETHIOPIC SYLLABLE CCHU;Lo;0;L;;;;;N;;;;; 2DBA;ETHIOPIC SYLLABLE CCHI;Lo;0;L;;;;;N;;;;; 2DBB;ETHIOPIC SYLLABLE CCHAA;Lo;0;L;;;;;N;;;;; 2DBC;ETHIOPIC SYLLABLE CCHEE;Lo;0;L;;;;;N;;;;; 2DBD;ETHIOPIC SYLLABLE CCHE;Lo;0;L;;;;;N;;;;; 2DBE;ETHIOPIC SYLLABLE CCHO;Lo;0;L;;;;;N;;;;; 2DC0;ETHIOPIC SYLLABLE QYA;Lo;0;L;;;;;N;;;;; 2DC1;ETHIOPIC SYLLABLE QYU;Lo;0;L;;;;;N;;;;; 2DC2;ETHIOPIC SYLLABLE QYI;Lo;0;L;;;;;N;;;;; 2DC3;ETHIOPIC SYLLABLE QYAA;Lo;0;L;;;;;N;;;;; 2DC4;ETHIOPIC SYLLABLE QYEE;Lo;0;L;;;;;N;;;;; 2DC5;ETHIOPIC SYLLABLE QYE;Lo;0;L;;;;;N;;;;; 2DC6;ETHIOPIC SYLLABLE QYO;Lo;0;L;;;;;N;;;;; 2DC8;ETHIOPIC SYLLABLE KYA;Lo;0;L;;;;;N;;;;; 2DC9;ETHIOPIC SYLLABLE KYU;Lo;0;L;;;;;N;;;;; 2DCA;ETHIOPIC SYLLABLE KYI;Lo;0;L;;;;;N;;;;; 2DCB;ETHIOPIC SYLLABLE KYAA;Lo;0;L;;;;;N;;;;; 2DCC;ETHIOPIC SYLLABLE KYEE;Lo;0;L;;;;;N;;;;; 2DCD;ETHIOPIC SYLLABLE KYE;Lo;0;L;;;;;N;;;;; 2DCE;ETHIOPIC SYLLABLE KYO;Lo;0;L;;;;;N;;;;; 2DD0;ETHIOPIC SYLLABLE XYA;Lo;0;L;;;;;N;;;;; 2DD1;ETHIOPIC SYLLABLE XYU;Lo;0;L;;;;;N;;;;; 2DD2;ETHIOPIC SYLLABLE XYI;Lo;0;L;;;;;N;;;;; 2DD3;ETHIOPIC SYLLABLE XYAA;Lo;0;L;;;;;N;;;;; 2DD4;ETHIOPIC SYLLABLE XYEE;Lo;0;L;;;;;N;;;;; 2DD5;ETHIOPIC SYLLABLE XYE;Lo;0;L;;;;;N;;;;; 2DD6;ETHIOPIC SYLLABLE XYO;Lo;0;L;;;;;N;;;;; 2DD8;ETHIOPIC SYLLABLE GYA;Lo;0;L;;;;;N;;;;; 2DD9;ETHIOPIC SYLLABLE GYU;Lo;0;L;;;;;N;;;;; 2DDA;ETHIOPIC SYLLABLE GYI;Lo;0;L;;;;;N;;;;; 2DDB;ETHIOPIC SYLLABLE GYAA;Lo;0;L;;;;;N;;;;; 2DDC;ETHIOPIC SYLLABLE GYEE;Lo;0;L;;;;;N;;;;; 2DDD;ETHIOPIC SYLLABLE GYE;Lo;0;L;;;;;N;;;;; 2DDE;ETHIOPIC SYLLABLE GYO;Lo;0;L;;;;;N;;;;; 2DE0;COMBINING CYRILLIC LETTER BE;Mn;230;NSM;;;;;N;;;;; 2DE1;COMBINING CYRILLIC LETTER VE;Mn;230;NSM;;;;;N;;;;; 2DE2;COMBINING CYRILLIC LETTER GHE;Mn;230;NSM;;;;;N;;;;; 2DE3;COMBINING CYRILLIC LETTER DE;Mn;230;NSM;;;;;N;;;;; 2DE4;COMBINING CYRILLIC LETTER ZHE;Mn;230;NSM;;;;;N;;;;; 2DE5;COMBINING CYRILLIC LETTER ZE;Mn;230;NSM;;;;;N;;;;; 2DE6;COMBINING CYRILLIC LETTER KA;Mn;230;NSM;;;;;N;;;;; 2DE7;COMBINING CYRILLIC LETTER EL;Mn;230;NSM;;;;;N;;;;; 2DE8;COMBINING CYRILLIC LETTER EM;Mn;230;NSM;;;;;N;;;;; 2DE9;COMBINING CYRILLIC LETTER EN;Mn;230;NSM;;;;;N;;;;; 2DEA;COMBINING CYRILLIC LETTER O;Mn;230;NSM;;;;;N;;;;; 2DEB;COMBINING CYRILLIC LETTER PE;Mn;230;NSM;;;;;N;;;;; 2DEC;COMBINING CYRILLIC LETTER ER;Mn;230;NSM;;;;;N;;;;; 2DED;COMBINING CYRILLIC LETTER ES;Mn;230;NSM;;;;;N;;;;; 2DEE;COMBINING CYRILLIC LETTER TE;Mn;230;NSM;;;;;N;;;;; 2DEF;COMBINING CYRILLIC LETTER HA;Mn;230;NSM;;;;;N;;;;; 2DF0;COMBINING CYRILLIC LETTER TSE;Mn;230;NSM;;;;;N;;;;; 2DF1;COMBINING CYRILLIC LETTER CHE;Mn;230;NSM;;;;;N;;;;; 2DF2;COMBINING CYRILLIC LETTER SHA;Mn;230;NSM;;;;;N;;;;; 2DF3;COMBINING CYRILLIC LETTER SHCHA;Mn;230;NSM;;;;;N;;;;; 2DF4;COMBINING CYRILLIC LETTER FITA;Mn;230;NSM;;;;;N;;;;; 2DF5;COMBINING CYRILLIC LETTER ES-TE;Mn;230;NSM;;;;;N;;;;; 2DF6;COMBINING CYRILLIC LETTER A;Mn;230;NSM;;;;;N;;;;; 2DF7;COMBINING CYRILLIC LETTER IE;Mn;230;NSM;;;;;N;;;;; 2DF8;COMBINING CYRILLIC LETTER DJERV;Mn;230;NSM;;;;;N;;;;; 2DF9;COMBINING CYRILLIC LETTER MONOGRAPH UK;Mn;230;NSM;;;;;N;;;;; 2DFA;COMBINING CYRILLIC LETTER YAT;Mn;230;NSM;;;;;N;;;;; 2DFB;COMBINING CYRILLIC LETTER YU;Mn;230;NSM;;;;;N;;;;; 2DFC;COMBINING CYRILLIC LETTER IOTIFIED A;Mn;230;NSM;;;;;N;;;;; 2DFD;COMBINING CYRILLIC LETTER LITTLE YUS;Mn;230;NSM;;;;;N;;;;; 2DFE;COMBINING CYRILLIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;; 2DFF;COMBINING CYRILLIC LETTER IOTIFIED BIG YUS;Mn;230;NSM;;;;;N;;;;; 2E00;RIGHT ANGLE SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;; 2E01;RIGHT ANGLE DOTTED SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;; 2E02;LEFT SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;; 2E03;RIGHT SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;; 2E04;LEFT DOTTED SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;; 2E05;RIGHT DOTTED SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;; 2E06;RAISED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;; 2E07;RAISED DOTTED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;; 2E08;DOTTED TRANSPOSITION MARKER;Po;0;ON;;;;;N;;;;; 2E09;LEFT TRANSPOSITION BRACKET;Pi;0;ON;;;;;Y;;;;; 2E0A;RIGHT TRANSPOSITION BRACKET;Pf;0;ON;;;;;Y;;;;; 2E0B;RAISED SQUARE;Po;0;ON;;;;;N;;;;; 2E0C;LEFT RAISED OMISSION BRACKET;Pi;0;ON;;;;;Y;;;;; 2E0D;RIGHT RAISED OMISSION BRACKET;Pf;0;ON;;;;;Y;;;;; 2E0E;EDITORIAL CORONIS;Po;0;ON;;;;;N;;;;; 2E0F;PARAGRAPHOS;Po;0;ON;;;;;N;;;;; 2E10;FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;; 2E11;REVERSED FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;; 2E12;HYPODIASTOLE;Po;0;ON;;;;;N;;;;; 2E13;DOTTED OBELOS;Po;0;ON;;;;;N;;;;; 2E14;DOWNWARDS ANCORA;Po;0;ON;;;;;N;;;;; 2E15;UPWARDS ANCORA;Po;0;ON;;;;;N;;;;; 2E16;DOTTED RIGHT-POINTING ANGLE;Po;0;ON;;;;;N;;;;; 2E17;DOUBLE OBLIQUE HYPHEN;Pd;0;ON;;;;;N;;;;; 2E18;INVERTED INTERROBANG;Po;0;ON;;;;;N;;;;; 2E19;PALM BRANCH;Po;0;ON;;;;;N;;;;; 2E1A;HYPHEN WITH DIAERESIS;Pd;0;ON;;;;;N;;;;; 2E1B;TILDE WITH RING ABOVE;Po;0;ON;;;;;N;;;;; 2E1C;LEFT LOW PARAPHRASE BRACKET;Pi;0;ON;;;;;Y;;;;; 2E1D;RIGHT LOW PARAPHRASE BRACKET;Pf;0;ON;;;;;Y;;;;; 2E1E;TILDE WITH DOT ABOVE;Po;0;ON;;;;;N;;;;; 2E1F;TILDE WITH DOT BELOW;Po;0;ON;;;;;N;;;;; 2E20;LEFT VERTICAL BAR WITH QUILL;Pi;0;ON;;;;;Y;;;;; 2E21;RIGHT VERTICAL BAR WITH QUILL;Pf;0;ON;;;;;Y;;;;; 2E22;TOP LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;; 2E23;TOP RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;; 2E24;BOTTOM LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;; 2E25;BOTTOM RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;; 2E26;LEFT SIDEWAYS U BRACKET;Ps;0;ON;;;;;Y;;;;; 2E27;RIGHT SIDEWAYS U BRACKET;Pe;0;ON;;;;;Y;;;;; 2E28;LEFT DOUBLE PARENTHESIS;Ps;0;ON;;;;;Y;;;;; 2E29;RIGHT DOUBLE PARENTHESIS;Pe;0;ON;;;;;Y;;;;; 2E2A;TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 2E2B;ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;; 2E2C;SQUARED FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 2E2D;FIVE DOT MARK;Po;0;ON;;;;;N;;;;; 2E2E;REVERSED QUESTION MARK;Po;0;ON;;;;;N;;;;; 2E2F;VERTICAL TILDE;Lm;0;ON;;;;;N;;;;; 2E30;RING POINT;Po;0;ON;;;;;N;;;;; 2E31;WORD SEPARATOR MIDDLE DOT;Po;0;ON;;;;;N;;;;; 2E32;TURNED COMMA;Po;0;ON;;;;;N;;;;; 2E33;RAISED DOT;Po;0;ON;;;;;N;;;;; 2E34;RAISED COMMA;Po;0;ON;;;;;N;;;;; 2E35;TURNED SEMICOLON;Po;0;ON;;;;;N;;;;; 2E36;DAGGER WITH LEFT GUARD;Po;0;ON;;;;;N;;;;; 2E37;DAGGER WITH RIGHT GUARD;Po;0;ON;;;;;N;;;;; 2E38;TURNED DAGGER;Po;0;ON;;;;;N;;;;; 2E39;TOP HALF SECTION SIGN;Po;0;ON;;;;;N;;;;; 2E3A;TWO-EM DASH;Pd;0;ON;;;;;N;;;;; 2E3B;THREE-EM DASH;Pd;0;ON;;;;;N;;;;; 2E3C;STENOGRAPHIC FULL STOP;Po;0;ON;;;;;N;;;;; 2E3D;VERTICAL SIX DOTS;Po;0;ON;;;;;N;;;;; 2E3E;WIGGLY VERTICAL LINE;Po;0;ON;;;;;N;;;;; 2E3F;CAPITULUM;Po;0;ON;;;;;N;;;;; 2E40;DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;; 2E41;REVERSED COMMA;Po;0;ON;;;;;N;;;;; 2E42;DOUBLE LOW-REVERSED-9 QUOTATION MARK;Ps;0;ON;;;;;N;;;;; 2E43;DASH WITH LEFT UPTURN;Po;0;ON;;;;;N;;;;; 2E44;DOUBLE SUSPENSION MARK;Po;0;ON;;;;;N;;;;; 2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;; 2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;; 2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;; 2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;; 2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;; 2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;; 2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;; 2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;; 2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;; 2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;; 2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;; 2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;; 2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;; 2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;; 2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;; 2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;; 2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;; 2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;; 2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;; 2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;; 2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;; 2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;; 2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;; 2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;; 2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;; 2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;; 2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;; 2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;; 2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;; 2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;; 2E9F;CJK RADICAL MOTHER;So;0;ON; 6BCD;;;;N;;;;; 2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;; 2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;; 2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;; 2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;; 2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;; 2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;; 2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;; 2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;; 2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;; 2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;; 2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;; 2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;; 2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;; 2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;; 2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;; 2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;; 2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;; 2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;; 2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;; 2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;; 2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;; 2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;; 2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;; 2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;; 2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;; 2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;; 2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;; 2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;; 2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;; 2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;; 2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;; 2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;; 2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;; 2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;; 2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;; 2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;; 2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;; 2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;; 2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;; 2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;; 2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;; 2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;; 2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;; 2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;; 2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;; 2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;; 2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;; 2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;; 2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;; 2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;; 2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;; 2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;; 2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;; 2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;; 2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;; 2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;; 2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;; 2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;; 2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;; 2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;; 2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;; 2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;; 2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;; 2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;; 2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;; 2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;; 2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;; 2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;; 2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;; 2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;; 2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;; 2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;; 2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;; 2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;; 2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;; 2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; 2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; 2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; 2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; 2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; 2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; 2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;; 2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;; 2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON; 9F9F;;;;N;;;;; 2F00;KANGXI RADICAL ONE;So;0;ON; 4E00;;;;N;;;;; 2F01;KANGXI RADICAL LINE;So;0;ON; 4E28;;;;N;;;;; 2F02;KANGXI RADICAL DOT;So;0;ON; 4E36;;;;N;;;;; 2F03;KANGXI RADICAL SLASH;So;0;ON; 4E3F;;;;N;;;;; 2F04;KANGXI RADICAL SECOND;So;0;ON; 4E59;;;;N;;;;; 2F05;KANGXI RADICAL HOOK;So;0;ON; 4E85;;;;N;;;;; 2F06;KANGXI RADICAL TWO;So;0;ON; 4E8C;;;;N;;;;; 2F07;KANGXI RADICAL LID;So;0;ON; 4EA0;;;;N;;;;; 2F08;KANGXI RADICAL MAN;So;0;ON; 4EBA;;;;N;;;;; 2F09;KANGXI RADICAL LEGS;So;0;ON; 513F;;;;N;;;;; 2F0A;KANGXI RADICAL ENTER;So;0;ON; 5165;;;;N;;;;; 2F0B;KANGXI RADICAL EIGHT;So;0;ON; 516B;;;;N;;;;; 2F0C;KANGXI RADICAL DOWN BOX;So;0;ON; 5182;;;;N;;;;; 2F0D;KANGXI RADICAL COVER;So;0;ON; 5196;;;;N;;;;; 2F0E;KANGXI RADICAL ICE;So;0;ON; 51AB;;;;N;;;;; 2F0F;KANGXI RADICAL TABLE;So;0;ON; 51E0;;;;N;;;;; 2F10;KANGXI RADICAL OPEN BOX;So;0;ON; 51F5;;;;N;;;;; 2F11;KANGXI RADICAL KNIFE;So;0;ON; 5200;;;;N;;;;; 2F12;KANGXI RADICAL POWER;So;0;ON; 529B;;;;N;;;;; 2F13;KANGXI RADICAL WRAP;So;0;ON; 52F9;;;;N;;;;; 2F14;KANGXI RADICAL SPOON;So;0;ON; 5315;;;;N;;;;; 2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON; 531A;;;;N;;;;; 2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON; 5338;;;;N;;;;; 2F17;KANGXI RADICAL TEN;So;0;ON; 5341;;;;N;;;;; 2F18;KANGXI RADICAL DIVINATION;So;0;ON; 535C;;;;N;;;;; 2F19;KANGXI RADICAL SEAL;So;0;ON; 5369;;;;N;;;;; 2F1A;KANGXI RADICAL CLIFF;So;0;ON; 5382;;;;N;;;;; 2F1B;KANGXI RADICAL PRIVATE;So;0;ON; 53B6;;;;N;;;;; 2F1C;KANGXI RADICAL AGAIN;So;0;ON; 53C8;;;;N;;;;; 2F1D;KANGXI RADICAL MOUTH;So;0;ON; 53E3;;;;N;;;;; 2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON; 56D7;;;;N;;;;; 2F1F;KANGXI RADICAL EARTH;So;0;ON; 571F;;;;N;;;;; 2F20;KANGXI RADICAL SCHOLAR;So;0;ON; 58EB;;;;N;;;;; 2F21;KANGXI RADICAL GO;So;0;ON; 5902;;;;N;;;;; 2F22;KANGXI RADICAL GO SLOWLY;So;0;ON; 590A;;;;N;;;;; 2F23;KANGXI RADICAL EVENING;So;0;ON; 5915;;;;N;;;;; 2F24;KANGXI RADICAL BIG;So;0;ON; 5927;;;;N;;;;; 2F25;KANGXI RADICAL WOMAN;So;0;ON; 5973;;;;N;;;;; 2F26;KANGXI RADICAL CHILD;So;0;ON; 5B50;;;;N;;;;; 2F27;KANGXI RADICAL ROOF;So;0;ON; 5B80;;;;N;;;;; 2F28;KANGXI RADICAL INCH;So;0;ON; 5BF8;;;;N;;;;; 2F29;KANGXI RADICAL SMALL;So;0;ON; 5C0F;;;;N;;;;; 2F2A;KANGXI RADICAL LAME;So;0;ON; 5C22;;;;N;;;;; 2F2B;KANGXI RADICAL CORPSE;So;0;ON; 5C38;;;;N;;;;; 2F2C;KANGXI RADICAL SPROUT;So;0;ON; 5C6E;;;;N;;;;; 2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON; 5C71;;;;N;;;;; 2F2E;KANGXI RADICAL RIVER;So;0;ON; 5DDB;;;;N;;;;; 2F2F;KANGXI RADICAL WORK;So;0;ON; 5DE5;;;;N;;;;; 2F30;KANGXI RADICAL ONESELF;So;0;ON; 5DF1;;;;N;;;;; 2F31;KANGXI RADICAL TURBAN;So;0;ON; 5DFE;;;;N;;;;; 2F32;KANGXI RADICAL DRY;So;0;ON; 5E72;;;;N;;;;; 2F33;KANGXI RADICAL SHORT THREAD;So;0;ON; 5E7A;;;;N;;;;; 2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON; 5E7F;;;;N;;;;; 2F35;KANGXI RADICAL LONG STRIDE;So;0;ON; 5EF4;;;;N;;;;; 2F36;KANGXI RADICAL TWO HANDS;So;0;ON; 5EFE;;;;N;;;;; 2F37;KANGXI RADICAL SHOOT;So;0;ON; 5F0B;;;;N;;;;; 2F38;KANGXI RADICAL BOW;So;0;ON; 5F13;;;;N;;;;; 2F39;KANGXI RADICAL SNOUT;So;0;ON; 5F50;;;;N;;;;; 2F3A;KANGXI RADICAL BRISTLE;So;0;ON; 5F61;;;;N;;;;; 2F3B;KANGXI RADICAL STEP;So;0;ON; 5F73;;;;N;;;;; 2F3C;KANGXI RADICAL HEART;So;0;ON; 5FC3;;;;N;;;;; 2F3D;KANGXI RADICAL HALBERD;So;0;ON; 6208;;;;N;;;;; 2F3E;KANGXI RADICAL DOOR;So;0;ON; 6236;;;;N;;;;; 2F3F;KANGXI RADICAL HAND;So;0;ON; 624B;;;;N;;;;; 2F40;KANGXI RADICAL BRANCH;So;0;ON; 652F;;;;N;;;;; 2F41;KANGXI RADICAL RAP;So;0;ON; 6534;;;;N;;;;; 2F42;KANGXI RADICAL SCRIPT;So;0;ON; 6587;;;;N;;;;; 2F43;KANGXI RADICAL DIPPER;So;0;ON; 6597;;;;N;;;;; 2F44;KANGXI RADICAL AXE;So;0;ON; 65A4;;;;N;;;;; 2F45;KANGXI RADICAL SQUARE;So;0;ON; 65B9;;;;N;;;;; 2F46;KANGXI RADICAL NOT;So;0;ON; 65E0;;;;N;;;;; 2F47;KANGXI RADICAL SUN;So;0;ON; 65E5;;;;N;;;;; 2F48;KANGXI RADICAL SAY;So;0;ON; 66F0;;;;N;;;;; 2F49;KANGXI RADICAL MOON;So;0;ON; 6708;;;;N;;;;; 2F4A;KANGXI RADICAL TREE;So;0;ON; 6728;;;;N;;;;; 2F4B;KANGXI RADICAL LACK;So;0;ON; 6B20;;;;N;;;;; 2F4C;KANGXI RADICAL STOP;So;0;ON; 6B62;;;;N;;;;; 2F4D;KANGXI RADICAL DEATH;So;0;ON; 6B79;;;;N;;;;; 2F4E;KANGXI RADICAL WEAPON;So;0;ON; 6BB3;;;;N;;;;; 2F4F;KANGXI RADICAL DO NOT;So;0;ON; 6BCB;;;;N;;;;; 2F50;KANGXI RADICAL COMPARE;So;0;ON; 6BD4;;;;N;;;;; 2F51;KANGXI RADICAL FUR;So;0;ON; 6BDB;;;;N;;;;; 2F52;KANGXI RADICAL CLAN;So;0;ON; 6C0F;;;;N;;;;; 2F53;KANGXI RADICAL STEAM;So;0;ON; 6C14;;;;N;;;;; 2F54;KANGXI RADICAL WATER;So;0;ON; 6C34;;;;N;;;;; 2F55;KANGXI RADICAL FIRE;So;0;ON; 706B;;;;N;;;;; 2F56;KANGXI RADICAL CLAW;So;0;ON; 722A;;;;N;;;;; 2F57;KANGXI RADICAL FATHER;So;0;ON; 7236;;;;N;;;;; 2F58;KANGXI RADICAL DOUBLE X;So;0;ON; 723B;;;;N;;;;; 2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON; 723F;;;;N;;;;; 2F5A;KANGXI RADICAL SLICE;So;0;ON; 7247;;;;N;;;;; 2F5B;KANGXI RADICAL FANG;So;0;ON; 7259;;;;N;;;;; 2F5C;KANGXI RADICAL COW;So;0;ON; 725B;;;;N;;;;; 2F5D;KANGXI RADICAL DOG;So;0;ON; 72AC;;;;N;;;;; 2F5E;KANGXI RADICAL PROFOUND;So;0;ON; 7384;;;;N;;;;; 2F5F;KANGXI RADICAL JADE;So;0;ON; 7389;;;;N;;;;; 2F60;KANGXI RADICAL MELON;So;0;ON; 74DC;;;;N;;;;; 2F61;KANGXI RADICAL TILE;So;0;ON; 74E6;;;;N;;;;; 2F62;KANGXI RADICAL SWEET;So;0;ON; 7518;;;;N;;;;; 2F63;KANGXI RADICAL LIFE;So;0;ON; 751F;;;;N;;;;; 2F64;KANGXI RADICAL USE;So;0;ON; 7528;;;;N;;;;; 2F65;KANGXI RADICAL FIELD;So;0;ON; 7530;;;;N;;;;; 2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON; 758B;;;;N;;;;; 2F67;KANGXI RADICAL SICKNESS;So;0;ON; 7592;;;;N;;;;; 2F68;KANGXI RADICAL DOTTED TENT;So;0;ON; 7676;;;;N;;;;; 2F69;KANGXI RADICAL WHITE;So;0;ON; 767D;;;;N;;;;; 2F6A;KANGXI RADICAL SKIN;So;0;ON; 76AE;;;;N;;;;; 2F6B;KANGXI RADICAL DISH;So;0;ON; 76BF;;;;N;;;;; 2F6C;KANGXI RADICAL EYE;So;0;ON; 76EE;;;;N;;;;; 2F6D;KANGXI RADICAL SPEAR;So;0;ON; 77DB;;;;N;;;;; 2F6E;KANGXI RADICAL ARROW;So;0;ON; 77E2;;;;N;;;;; 2F6F;KANGXI RADICAL STONE;So;0;ON; 77F3;;;;N;;;;; 2F70;KANGXI RADICAL SPIRIT;So;0;ON; 793A;;;;N;;;;; 2F71;KANGXI RADICAL TRACK;So;0;ON; 79B8;;;;N;;;;; 2F72;KANGXI RADICAL GRAIN;So;0;ON; 79BE;;;;N;;;;; 2F73;KANGXI RADICAL CAVE;So;0;ON; 7A74;;;;N;;;;; 2F74;KANGXI RADICAL STAND;So;0;ON; 7ACB;;;;N;;;;; 2F75;KANGXI RADICAL BAMBOO;So;0;ON; 7AF9;;;;N;;;;; 2F76;KANGXI RADICAL RICE;So;0;ON; 7C73;;;;N;;;;; 2F77;KANGXI RADICAL SILK;So;0;ON; 7CF8;;;;N;;;;; 2F78;KANGXI RADICAL JAR;So;0;ON; 7F36;;;;N;;;;; 2F79;KANGXI RADICAL NET;So;0;ON; 7F51;;;;N;;;;; 2F7A;KANGXI RADICAL SHEEP;So;0;ON; 7F8A;;;;N;;;;; 2F7B;KANGXI RADICAL FEATHER;So;0;ON; 7FBD;;;;N;;;;; 2F7C;KANGXI RADICAL OLD;So;0;ON; 8001;;;;N;;;;; 2F7D;KANGXI RADICAL AND;So;0;ON; 800C;;;;N;;;;; 2F7E;KANGXI RADICAL PLOW;So;0;ON; 8012;;;;N;;;;; 2F7F;KANGXI RADICAL EAR;So;0;ON; 8033;;;;N;;;;; 2F80;KANGXI RADICAL BRUSH;So;0;ON; 807F;;;;N;;;;; 2F81;KANGXI RADICAL MEAT;So;0;ON; 8089;;;;N;;;;; 2F82;KANGXI RADICAL MINISTER;So;0;ON; 81E3;;;;N;;;;; 2F83;KANGXI RADICAL SELF;So;0;ON; 81EA;;;;N;;;;; 2F84;KANGXI RADICAL ARRIVE;So;0;ON; 81F3;;;;N;;;;; 2F85;KANGXI RADICAL MORTAR;So;0;ON; 81FC;;;;N;;;;; 2F86;KANGXI RADICAL TONGUE;So;0;ON; 820C;;;;N;;;;; 2F87;KANGXI RADICAL OPPOSE;So;0;ON; 821B;;;;N;;;;; 2F88;KANGXI RADICAL BOAT;So;0;ON; 821F;;;;N;;;;; 2F89;KANGXI RADICAL STOPPING;So;0;ON; 826E;;;;N;;;;; 2F8A;KANGXI RADICAL COLOR;So;0;ON; 8272;;;;N;;;;; 2F8B;KANGXI RADICAL GRASS;So;0;ON; 8278;;;;N;;;;; 2F8C;KANGXI RADICAL TIGER;So;0;ON; 864D;;;;N;;;;; 2F8D;KANGXI RADICAL INSECT;So;0;ON; 866B;;;;N;;;;; 2F8E;KANGXI RADICAL BLOOD;So;0;ON; 8840;;;;N;;;;; 2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON; 884C;;;;N;;;;; 2F90;KANGXI RADICAL CLOTHES;So;0;ON; 8863;;;;N;;;;; 2F91;KANGXI RADICAL WEST;So;0;ON; 897E;;;;N;;;;; 2F92;KANGXI RADICAL SEE;So;0;ON; 898B;;;;N;;;;; 2F93;KANGXI RADICAL HORN;So;0;ON; 89D2;;;;N;;;;; 2F94;KANGXI RADICAL SPEECH;So;0;ON; 8A00;;;;N;;;;; 2F95;KANGXI RADICAL VALLEY;So;0;ON; 8C37;;;;N;;;;; 2F96;KANGXI RADICAL BEAN;So;0;ON; 8C46;;;;N;;;;; 2F97;KANGXI RADICAL PIG;So;0;ON; 8C55;;;;N;;;;; 2F98;KANGXI RADICAL BADGER;So;0;ON; 8C78;;;;N;;;;; 2F99;KANGXI RADICAL SHELL;So;0;ON; 8C9D;;;;N;;;;; 2F9A;KANGXI RADICAL RED;So;0;ON; 8D64;;;;N;;;;; 2F9B;KANGXI RADICAL RUN;So;0;ON; 8D70;;;;N;;;;; 2F9C;KANGXI RADICAL FOOT;So;0;ON; 8DB3;;;;N;;;;; 2F9D;KANGXI RADICAL BODY;So;0;ON; 8EAB;;;;N;;;;; 2F9E;KANGXI RADICAL CART;So;0;ON; 8ECA;;;;N;;;;; 2F9F;KANGXI RADICAL BITTER;So;0;ON; 8F9B;;;;N;;;;; 2FA0;KANGXI RADICAL MORNING;So;0;ON; 8FB0;;;;N;;;;; 2FA1;KANGXI RADICAL WALK;So;0;ON; 8FB5;;;;N;;;;; 2FA2;KANGXI RADICAL CITY;So;0;ON; 9091;;;;N;;;;; 2FA3;KANGXI RADICAL WINE;So;0;ON; 9149;;;;N;;;;; 2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON; 91C6;;;;N;;;;; 2FA5;KANGXI RADICAL VILLAGE;So;0;ON; 91CC;;;;N;;;;; 2FA6;KANGXI RADICAL GOLD;So;0;ON; 91D1;;;;N;;;;; 2FA7;KANGXI RADICAL LONG;So;0;ON; 9577;;;;N;;;;; 2FA8;KANGXI RADICAL GATE;So;0;ON; 9580;;;;N;;;;; 2FA9;KANGXI RADICAL MOUND;So;0;ON; 961C;;;;N;;;;; 2FAA;KANGXI RADICAL SLAVE;So;0;ON; 96B6;;;;N;;;;; 2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON; 96B9;;;;N;;;;; 2FAC;KANGXI RADICAL RAIN;So;0;ON; 96E8;;;;N;;;;; 2FAD;KANGXI RADICAL BLUE;So;0;ON; 9751;;;;N;;;;; 2FAE;KANGXI RADICAL WRONG;So;0;ON; 975E;;;;N;;;;; 2FAF;KANGXI RADICAL FACE;So;0;ON; 9762;;;;N;;;;; 2FB0;KANGXI RADICAL LEATHER;So;0;ON; 9769;;;;N;;;;; 2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON; 97CB;;;;N;;;;; 2FB2;KANGXI RADICAL LEEK;So;0;ON; 97ED;;;;N;;;;; 2FB3;KANGXI RADICAL SOUND;So;0;ON; 97F3;;;;N;;;;; 2FB4;KANGXI RADICAL LEAF;So;0;ON; 9801;;;;N;;;;; 2FB5;KANGXI RADICAL WIND;So;0;ON; 98A8;;;;N;;;;; 2FB6;KANGXI RADICAL FLY;So;0;ON; 98DB;;;;N;;;;; 2FB7;KANGXI RADICAL EAT;So;0;ON; 98DF;;;;N;;;;; 2FB8;KANGXI RADICAL HEAD;So;0;ON; 9996;;;;N;;;;; 2FB9;KANGXI RADICAL FRAGRANT;So;0;ON; 9999;;;;N;;;;; 2FBA;KANGXI RADICAL HORSE;So;0;ON; 99AC;;;;N;;;;; 2FBB;KANGXI RADICAL BONE;So;0;ON; 9AA8;;;;N;;;;; 2FBC;KANGXI RADICAL TALL;So;0;ON; 9AD8;;;;N;;;;; 2FBD;KANGXI RADICAL HAIR;So;0;ON; 9ADF;;;;N;;;;; 2FBE;KANGXI RADICAL FIGHT;So;0;ON; 9B25;;;;N;;;;; 2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON; 9B2F;;;;N;;;;; 2FC0;KANGXI RADICAL CAULDRON;So;0;ON; 9B32;;;;N;;;;; 2FC1;KANGXI RADICAL GHOST;So;0;ON; 9B3C;;;;N;;;;; 2FC2;KANGXI RADICAL FISH;So;0;ON; 9B5A;;;;N;;;;; 2FC3;KANGXI RADICAL BIRD;So;0;ON; 9CE5;;;;N;;;;; 2FC4;KANGXI RADICAL SALT;So;0;ON; 9E75;;;;N;;;;; 2FC5;KANGXI RADICAL DEER;So;0;ON; 9E7F;;;;N;;;;; 2FC6;KANGXI RADICAL WHEAT;So;0;ON; 9EA5;;;;N;;;;; 2FC7;KANGXI RADICAL HEMP;So;0;ON; 9EBB;;;;N;;;;; 2FC8;KANGXI RADICAL YELLOW;So;0;ON; 9EC3;;;;N;;;;; 2FC9;KANGXI RADICAL MILLET;So;0;ON; 9ECD;;;;N;;;;; 2FCA;KANGXI RADICAL BLACK;So;0;ON; 9ED1;;;;N;;;;; 2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON; 9EF9;;;;N;;;;; 2FCC;KANGXI RADICAL FROG;So;0;ON; 9EFD;;;;N;;;;; 2FCD;KANGXI RADICAL TRIPOD;So;0;ON; 9F0E;;;;N;;;;; 2FCE;KANGXI RADICAL DRUM;So;0;ON; 9F13;;;;N;;;;; 2FCF;KANGXI RADICAL RAT;So;0;ON; 9F20;;;;N;;;;; 2FD0;KANGXI RADICAL NOSE;So;0;ON; 9F3B;;;;N;;;;; 2FD1;KANGXI RADICAL EVEN;So;0;ON; 9F4A;;;;N;;;;; 2FD2;KANGXI RADICAL TOOTH;So;0;ON; 9F52;;;;N;;;;; 2FD3;KANGXI RADICAL DRAGON;So;0;ON; 9F8D;;;;N;;;;; 2FD4;KANGXI RADICAL TURTLE;So;0;ON; 9F9C;;;;N;;;;; 2FD5;KANGXI RADICAL FLUTE;So;0;ON; 9FA0;;;;N;;;;; 2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;; 2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;; 2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;; 2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;; 2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;; 2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;; 2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;; 2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;; 2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;; 2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;; 2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;; 2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;; 3000;IDEOGRAPHIC SPACE;Zs;0;WS; 0020;;;;N;;;;; 3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;; 3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;; 3003;DITTO MARK;Po;0;ON;;;;;N;;;;; 3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;; 3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; 3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;; 3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;; 3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;; 3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;; 300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;; 300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;; 300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;; 300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;; 300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;; 300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;; 3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;; 3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;; 3012;POSTAL MARK;So;0;ON;;;;;N;;;;; 3013;GETA MARK;So;0;ON;;;;;N;;;;; 3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;; 3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;; 3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;; 3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;; 3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;; 3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;; 301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;; 301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;; 301C;WAVE DASH;Pd;0;ON;;;;;N;;;;; 301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;; 301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; 301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; 3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;; 3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;; 3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;; 3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;; 3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;; 3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;; 3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;; 3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;; 3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;; 3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;; 302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;; 302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;; 302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;; 302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;; 302E;HANGUL SINGLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; 302F;HANGUL DOUBLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; 3030;WAVY DASH;Pd;0;ON;;;;;N;;;;; 3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;; 3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;; 3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;; 3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;; 3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;; 3036;CIRCLED POSTAL MARK;So;0;ON; 3012;;;;N;;;;; 3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;; 3038;HANGZHOU NUMERAL TEN;Nl;0;L; 5341;;;10;N;;;;; 3039;HANGZHOU NUMERAL TWENTY;Nl;0;L; 5344;;;20;N;;;;; 303A;HANGZHOU NUMERAL THIRTY;Nl;0;L; 5345;;;30;N;;;;; 303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; 303C;MASU MARK;Lo;0;L;;;;;N;;;;; 303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;; 303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;; 303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;; 3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; 3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;; 3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; 3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;; 3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; 3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;; 3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; 3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;; 3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; 304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;; 304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;; 304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;; 304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;; 304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;; 304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;; 3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;; 3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;; 3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;; 3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;; 3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;; 3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;; 3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;; 3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;; 3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;; 3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;; 305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;; 305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;; 305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;; 305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;; 305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;; 305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;; 3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;; 3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;; 3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;; 3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; 3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;; 3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;; 3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;; 3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;; 3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;; 3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;; 306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;; 306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;; 306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;; 306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;; 306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;; 306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;; 3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;; 3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;; 3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;; 3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;; 3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;; 3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;; 3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;; 3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;; 3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;; 3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;; 307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;; 307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;; 307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;; 307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;; 307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;; 307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;; 3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;; 3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;; 3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;; 3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; 3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;; 3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; 3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;; 3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; 3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;; 3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;; 308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;; 308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;; 308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;; 308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;; 308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; 308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;; 3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;; 3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;; 3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;; 3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;; 3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;; 3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; 3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; 3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;; 309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;; 309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON; 0020 3099;;;;N;;;;; 309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON; 0020 309A;;;;N;;;;; 309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;; 309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;; 309F;HIRAGANA DIGRAPH YORI;Lo;0;L; 3088 308A;;;;N;;;;; 30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;; 30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; 30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;; 30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; 30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;; 30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; 30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;; 30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; 30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;; 30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; 30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;; 30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;; 30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;; 30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;; 30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;; 30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;; 30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;; 30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;; 30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;; 30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;; 30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;; 30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;; 30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;; 30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;; 30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;; 30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;; 30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;; 30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;; 30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;; 30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;; 30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;; 30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;; 30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;; 30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;; 30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;; 30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; 30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;; 30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;; 30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;; 30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;; 30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;; 30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;; 30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;; 30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;; 30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;; 30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;; 30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;; 30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;; 30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;; 30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;; 30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;; 30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;; 30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;; 30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;; 30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;; 30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;; 30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;; 30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;; 30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;; 30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;; 30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;; 30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;; 30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;; 30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;; 30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;; 30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;; 30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;; 30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; 30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;; 30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; 30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;; 30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; 30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;; 30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;; 30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;; 30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;; 30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;; 30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;; 30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; 30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;; 30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;; 30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;; 30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;; 30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;; 30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;; 30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; 30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; 30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;; 30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;; 30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;; 30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;; 30FB;KATAKANA MIDDLE DOT;Po;0;ON;;;;;N;;;;; 30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;; 30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;; 30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;; 30FF;KATAKANA DIGRAPH KOTO;Lo;0;L; 30B3 30C8;;;;N;;;;; 3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;; 3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;; 3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;; 3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;; 3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;; 310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;; 310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;; 310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;; 310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;; 310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;; 310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;; 3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;; 3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;; 3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;; 3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;; 3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;; 3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;; 3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;; 3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;; 3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;; 3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;; 311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;; 311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;; 311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;; 311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;; 311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;; 311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;; 3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;; 3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;; 3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;; 3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;; 3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;; 3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;; 3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;; 3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;; 3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;; 3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;; 312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;; 312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;; 312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;; 312D;BOPOMOFO LETTER IH;Lo;0;L;;;;;N;;;;; 3131;HANGUL LETTER KIYEOK;Lo;0;L; 1100;;;;N;HANGUL LETTER GIYEOG;;;; 3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L; 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;; 3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;; 3134;HANGUL LETTER NIEUN;Lo;0;L; 1102;;;;N;;;;; 3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L; 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;; 3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L; 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;; 3137;HANGUL LETTER TIKEUT;Lo;0;L; 1103;;;;N;HANGUL LETTER DIGEUD;;;; 3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L; 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;; 3139;HANGUL LETTER RIEUL;Lo;0;L; 1105;;;;N;HANGUL LETTER LIEUL;;;; 313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L; 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;; 313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L; 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;; 313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L; 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;; 313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L; 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;; 313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L; 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;; 313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L; 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;; 3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L; 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;; 3141;HANGUL LETTER MIEUM;Lo;0;L; 1106;;;;N;;;;; 3142;HANGUL LETTER PIEUP;Lo;0;L; 1107;;;;N;HANGUL LETTER BIEUB;;;; 3143;HANGUL LETTER SSANGPIEUP;Lo;0;L; 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;; 3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L; 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;; 3145;HANGUL LETTER SIOS;Lo;0;L; 1109;;;;N;;;;; 3146;HANGUL LETTER SSANGSIOS;Lo;0;L; 110A;;;;N;HANGUL LETTER SSANG SIOS;;;; 3147;HANGUL LETTER IEUNG;Lo;0;L; 110B;;;;N;;;;; 3148;HANGUL LETTER CIEUC;Lo;0;L; 110C;;;;N;HANGUL LETTER JIEUJ;;;; 3149;HANGUL LETTER SSANGCIEUC;Lo;0;L; 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;; 314A;HANGUL LETTER CHIEUCH;Lo;0;L; 110E;;;;N;HANGUL LETTER CIEUC;;;; 314B;HANGUL LETTER KHIEUKH;Lo;0;L; 110F;;;;N;HANGUL LETTER KIYEOK;;;; 314C;HANGUL LETTER THIEUTH;Lo;0;L; 1110;;;;N;HANGUL LETTER TIEUT;;;; 314D;HANGUL LETTER PHIEUPH;Lo;0;L; 1111;;;;N;HANGUL LETTER PIEUP;;;; 314E;HANGUL LETTER HIEUH;Lo;0;L; 1112;;;;N;;;;; 314F;HANGUL LETTER A;Lo;0;L; 1161;;;;N;;;;; 3150;HANGUL LETTER AE;Lo;0;L; 1162;;;;N;;;;; 3151;HANGUL LETTER YA;Lo;0;L; 1163;;;;N;;;;; 3152;HANGUL LETTER YAE;Lo;0;L; 1164;;;;N;;;;; 3153;HANGUL LETTER EO;Lo;0;L; 1165;;;;N;;;;; 3154;HANGUL LETTER E;Lo;0;L; 1166;;;;N;;;;; 3155;HANGUL LETTER YEO;Lo;0;L; 1167;;;;N;;;;; 3156;HANGUL LETTER YE;Lo;0;L; 1168;;;;N;;;;; 3157;HANGUL LETTER O;Lo;0;L; 1169;;;;N;;;;; 3158;HANGUL LETTER WA;Lo;0;L; 116A;;;;N;;;;; 3159;HANGUL LETTER WAE;Lo;0;L; 116B;;;;N;;;;; 315A;HANGUL LETTER OE;Lo;0;L; 116C;;;;N;;;;; 315B;HANGUL LETTER YO;Lo;0;L; 116D;;;;N;;;;; 315C;HANGUL LETTER U;Lo;0;L; 116E;;;;N;;;;; 315D;HANGUL LETTER WEO;Lo;0;L; 116F;;;;N;;;;; 315E;HANGUL LETTER WE;Lo;0;L; 1170;;;;N;;;;; 315F;HANGUL LETTER WI;Lo;0;L; 1171;;;;N;;;;; 3160;HANGUL LETTER YU;Lo;0;L; 1172;;;;N;;;;; 3161;HANGUL LETTER EU;Lo;0;L; 1173;;;;N;;;;; 3162;HANGUL LETTER YI;Lo;0;L; 1174;;;;N;;;;; 3163;HANGUL LETTER I;Lo;0;L; 1175;;;;N;;;;; 3164;HANGUL FILLER;Lo;0;L; 1160;;;;N;HANGUL CAE OM;;;; 3165;HANGUL LETTER SSANGNIEUN;Lo;0;L; 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;; 3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L; 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;; 3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L; 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;; 3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L; 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;; 3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L; 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;; 316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L; 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;; 316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L; 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;; 316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L; 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;; 316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L; 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;; 316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L; 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;; 316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L; 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;; 3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L; 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;; 3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L; 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;; 3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L; 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;; 3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L; 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;; 3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L; 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;; 3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L; 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;; 3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L; 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;; 3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L; 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;; 3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L; 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;; 3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L; 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;; 317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L; 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;; 317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L; 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;; 317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L; 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;; 317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L; 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;; 317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L; 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;; 317F;HANGUL LETTER PANSIOS;Lo;0;L; 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;; 3180;HANGUL LETTER SSANGIEUNG;Lo;0;L; 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;; 3181;HANGUL LETTER YESIEUNG;Lo;0;L; 114C;;;;N;HANGUL LETTER NGIEUNG;;;; 3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L; 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;; 3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L; 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;; 3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L; 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;; 3185;HANGUL LETTER SSANGHIEUH;Lo;0;L; 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;; 3186;HANGUL LETTER YEORINHIEUH;Lo;0;L; 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;; 3187;HANGUL LETTER YO-YA;Lo;0;L; 1184;;;;N;HANGUL LETTER YOYA;;;; 3188;HANGUL LETTER YO-YAE;Lo;0;L; 1185;;;;N;HANGUL LETTER YOYAE;;;; 3189;HANGUL LETTER YO-I;Lo;0;L; 1188;;;;N;HANGUL LETTER YOI;;;; 318A;HANGUL LETTER YU-YEO;Lo;0;L; 1191;;;;N;HANGUL LETTER YUYEO;;;; 318B;HANGUL LETTER YU-YE;Lo;0;L; 1192;;;;N;HANGUL LETTER YUYE;;;; 318C;HANGUL LETTER YU-I;Lo;0;L; 1194;;;;N;HANGUL LETTER YUI;;;; 318D;HANGUL LETTER ARAEA;Lo;0;L; 119E;;;;N;HANGUL LETTER ALAE A;;;; 318E;HANGUL LETTER ARAEAE;Lo;0;L; 11A1;;;;N;HANGUL LETTER ALAE AE;;;; 3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;;;; 3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;;;; 3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L; 4E00;;;1;N;KAERITEN ITI;;;; 3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L; 4E8C;;;2;N;KAERITEN NI;;;; 3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L; 4E09;;;3;N;KAERITEN SAN;;;; 3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L; 56DB;;;4;N;KAERITEN SI;;;; 3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L; 4E0A;;;;N;KAERITEN ZYOU;;;; 3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L; 4E2D;;;;N;KAERITEN TYUU;;;; 3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L; 4E0B;;;;N;KAERITEN GE;;;; 3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L; 7532;;;;N;KAERITEN KOU;;;; 319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L; 4E59;;;;N;KAERITEN OTU;;;; 319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L; 4E19;;;;N;KAERITEN HEI;;;; 319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L; 4E01;;;;N;KAERITEN TEI;;;; 319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L; 5929;;;;N;KAERITEN TEN;;;; 319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L; 5730;;;;N;KAERITEN TI;;;; 319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L; 4EBA;;;;N;KAERITEN ZIN;;;; 31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;; 31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;; 31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;; 31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;; 31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;; 31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;; 31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;; 31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;; 31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;; 31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;; 31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;; 31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;; 31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;; 31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;; 31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;; 31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;; 31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;; 31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;; 31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;; 31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;; 31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;; 31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;; 31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;; 31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;; 31B8;BOPOMOFO LETTER GH;Lo;0;L;;;;;N;;;;; 31B9;BOPOMOFO LETTER LH;Lo;0;L;;;;;N;;;;; 31BA;BOPOMOFO LETTER ZY;Lo;0;L;;;;;N;;;;; 31C0;CJK STROKE T;So;0;ON;;;;;N;;;;; 31C1;CJK STROKE WG;So;0;ON;;;;;N;;;;; 31C2;CJK STROKE XG;So;0;ON;;;;;N;;;;; 31C3;CJK STROKE BXG;So;0;ON;;;;;N;;;;; 31C4;CJK STROKE SW;So;0;ON;;;;;N;;;;; 31C5;CJK STROKE HZZ;So;0;ON;;;;;N;;;;; 31C6;CJK STROKE HZG;So;0;ON;;;;;N;;;;; 31C7;CJK STROKE HP;So;0;ON;;;;;N;;;;; 31C8;CJK STROKE HZWG;So;0;ON;;;;;N;;;;; 31C9;CJK STROKE SZWG;So;0;ON;;;;;N;;;;; 31CA;CJK STROKE HZT;So;0;ON;;;;;N;;;;; 31CB;CJK STROKE HZZP;So;0;ON;;;;;N;;;;; 31CC;CJK STROKE HPWG;So;0;ON;;;;;N;;;;; 31CD;CJK STROKE HZW;So;0;ON;;;;;N;;;;; 31CE;CJK STROKE HZZZ;So;0;ON;;;;;N;;;;; 31CF;CJK STROKE N;So;0;ON;;;;;N;;;;; 31D0;CJK STROKE H;So;0;ON;;;;;N;;;;; 31D1;CJK STROKE S;So;0;ON;;;;;N;;;;; 31D2;CJK STROKE P;So;0;ON;;;;;N;;;;; 31D3;CJK STROKE SP;So;0;ON;;;;;N;;;;; 31D4;CJK STROKE D;So;0;ON;;;;;N;;;;; 31D5;CJK STROKE HZ;So;0;ON;;;;;N;;;;; 31D6;CJK STROKE HG;So;0;ON;;;;;N;;;;; 31D7;CJK STROKE SZ;So;0;ON;;;;;N;;;;; 31D8;CJK STROKE SWZ;So;0;ON;;;;;N;;;;; 31D9;CJK STROKE ST;So;0;ON;;;;;N;;;;; 31DA;CJK STROKE SG;So;0;ON;;;;;N;;;;; 31DB;CJK STROKE PD;So;0;ON;;;;;N;;;;; 31DC;CJK STROKE PZ;So;0;ON;;;;;N;;;;; 31DD;CJK STROKE TN;So;0;ON;;;;;N;;;;; 31DE;CJK STROKE SZZ;So;0;ON;;;;;N;;;;; 31DF;CJK STROKE SWG;So;0;ON;;;;;N;;;;; 31E0;CJK STROKE HXWG;So;0;ON;;;;;N;;;;; 31E1;CJK STROKE HZZZG;So;0;ON;;;;;N;;;;; 31E2;CJK STROKE PG;So;0;ON;;;;;N;;;;; 31E3;CJK STROKE Q;So;0;ON;;;;;N;;;;; 31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;; 31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;; 31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;; 31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;; 31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;; 31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;; 31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;; 31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;; 31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;; 31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;; 31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;; 31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;; 31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;; 31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;; 31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;; 31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;; 3200;PARENTHESIZED HANGUL KIYEOK;So;0;L; 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;; 3201;PARENTHESIZED HANGUL NIEUN;So;0;L; 0028 1102 0029;;;;N;;;;; 3202;PARENTHESIZED HANGUL TIKEUT;So;0;L; 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;; 3203;PARENTHESIZED HANGUL RIEUL;So;0;L; 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;; 3204;PARENTHESIZED HANGUL MIEUM;So;0;L; 0028 1106 0029;;;;N;;;;; 3205;PARENTHESIZED HANGUL PIEUP;So;0;L; 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;; 3206;PARENTHESIZED HANGUL SIOS;So;0;L; 0028 1109 0029;;;;N;;;;; 3207;PARENTHESIZED HANGUL IEUNG;So;0;L; 0028 110B 0029;;;;N;;;;; 3208;PARENTHESIZED HANGUL CIEUC;So;0;L; 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;; 3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L; 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;; 320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L; 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;; 320B;PARENTHESIZED HANGUL THIEUTH;So;0;L; 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;; 320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L; 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;; 320D;PARENTHESIZED HANGUL HIEUH;So;0;L; 0028 1112 0029;;;;N;;;;; 320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L; 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;; 320F;PARENTHESIZED HANGUL NIEUN A;So;0;L; 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;; 3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L; 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;; 3211;PARENTHESIZED HANGUL RIEUL A;So;0;L; 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;; 3212;PARENTHESIZED HANGUL MIEUM A;So;0;L; 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;; 3213;PARENTHESIZED HANGUL PIEUP A;So;0;L; 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;; 3214;PARENTHESIZED HANGUL SIOS A;So;0;L; 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;; 3215;PARENTHESIZED HANGUL IEUNG A;So;0;L; 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;; 3216;PARENTHESIZED HANGUL CIEUC A;So;0;L; 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;; 3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L; 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;; 3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L; 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;; 3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L; 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;; 321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L; 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;; 321B;PARENTHESIZED HANGUL HIEUH A;So;0;L; 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;; 321C;PARENTHESIZED HANGUL CIEUC U;So;0;L; 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;; 321D;PARENTHESIZED KOREAN CHARACTER OJEON;So;0;ON; 0028 110B 1169 110C 1165 11AB 0029;;;;N;;;;; 321E;PARENTHESIZED KOREAN CHARACTER O HU;So;0;ON; 0028 110B 1169 1112 116E 0029;;;;N;;;;; 3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L; 0028 4E00 0029;;;1;N;;;;; 3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L; 0028 4E8C 0029;;;2;N;;;;; 3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L; 0028 4E09 0029;;;3;N;;;;; 3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L; 0028 56DB 0029;;;4;N;;;;; 3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L; 0028 4E94 0029;;;5;N;;;;; 3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L; 0028 516D 0029;;;6;N;;;;; 3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L; 0028 4E03 0029;;;7;N;;;;; 3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L; 0028 516B 0029;;;8;N;;;;; 3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L; 0028 4E5D 0029;;;9;N;;;;; 3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L; 0028 5341 0029;;;10;N;;;;; 322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L; 0028 6708 0029;;;;N;;;;; 322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L; 0028 706B 0029;;;;N;;;;; 322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L; 0028 6C34 0029;;;;N;;;;; 322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L; 0028 6728 0029;;;;N;;;;; 322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L; 0028 91D1 0029;;;;N;;;;; 322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L; 0028 571F 0029;;;;N;;;;; 3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L; 0028 65E5 0029;;;;N;;;;; 3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L; 0028 682A 0029;;;;N;;;;; 3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L; 0028 6709 0029;;;;N;;;;; 3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L; 0028 793E 0029;;;;N;;;;; 3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L; 0028 540D 0029;;;;N;;;;; 3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L; 0028 7279 0029;;;;N;;;;; 3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L; 0028 8CA1 0029;;;;N;;;;; 3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L; 0028 795D 0029;;;;N;;;;; 3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L; 0028 52B4 0029;;;;N;;;;; 3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L; 0028 4EE3 0029;;;;N;;;;; 323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L; 0028 547C 0029;;;;N;;;;; 323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L; 0028 5B66 0029;;;;N;;;;; 323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L; 0028 76E3 0029;;;;N;;;;; 323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L; 0028 4F01 0029;;;;N;;;;; 323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L; 0028 8CC7 0029;;;;N;;;;; 323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L; 0028 5354 0029;;;;N;;;;; 3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L; 0028 796D 0029;;;;N;;;;; 3241;PARENTHESIZED IDEOGRAPH REST;So;0;L; 0028 4F11 0029;;;;N;;;;; 3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L; 0028 81EA 0029;;;;N;;;;; 3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L; 0028 81F3 0029;;;;N;;;;; 3244;CIRCLED IDEOGRAPH QUESTION;So;0;L; 554F;;;;N;;;;; 3245;CIRCLED IDEOGRAPH KINDERGARTEN;So;0;L; 5E7C;;;;N;;;;; 3246;CIRCLED IDEOGRAPH SCHOOL;So;0;L; 6587;;;;N;;;;; 3247;CIRCLED IDEOGRAPH KOTO;So;0;L; 7B8F;;;;N;;;;; 3248;CIRCLED NUMBER TEN ON BLACK SQUARE;No;0;L;;;;10;N;;;;; 3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;No;0;L;;;;20;N;;;;; 324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;No;0;L;;;;30;N;;;;; 324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;No;0;L;;;;40;N;;;;; 324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;No;0;L;;;;50;N;;;;; 324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;No;0;L;;;;60;N;;;;; 324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;No;0;L;;;;70;N;;;;; 324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;No;0;L;;;;80;N;;;;; 3250;PARTNERSHIP SIGN;So;0;ON; 0050 0054 0045;;;;N;;;;; 3251;CIRCLED NUMBER TWENTY ONE;No;0;ON; 0032 0031;;;21;N;;;;; 3252;CIRCLED NUMBER TWENTY TWO;No;0;ON; 0032 0032;;;22;N;;;;; 3253;CIRCLED NUMBER TWENTY THREE;No;0;ON; 0032 0033;;;23;N;;;;; 3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON; 0032 0034;;;24;N;;;;; 3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON; 0032 0035;;;25;N;;;;; 3256;CIRCLED NUMBER TWENTY SIX;No;0;ON; 0032 0036;;;26;N;;;;; 3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON; 0032 0037;;;27;N;;;;; 3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON; 0032 0038;;;28;N;;;;; 3259;CIRCLED NUMBER TWENTY NINE;No;0;ON; 0032 0039;;;29;N;;;;; 325A;CIRCLED NUMBER THIRTY;No;0;ON; 0033 0030;;;30;N;;;;; 325B;CIRCLED NUMBER THIRTY ONE;No;0;ON; 0033 0031;;;31;N;;;;; 325C;CIRCLED NUMBER THIRTY TWO;No;0;ON; 0033 0032;;;32;N;;;;; 325D;CIRCLED NUMBER THIRTY THREE;No;0;ON; 0033 0033;;;33;N;;;;; 325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON; 0033 0034;;;34;N;;;;; 325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON; 0033 0035;;;35;N;;;;; 3260;CIRCLED HANGUL KIYEOK;So;0;L; 1100;;;;N;CIRCLED HANGUL GIYEOG;;;; 3261;CIRCLED HANGUL NIEUN;So;0;L; 1102;;;;N;;;;; 3262;CIRCLED HANGUL TIKEUT;So;0;L; 1103;;;;N;CIRCLED HANGUL DIGEUD;;;; 3263;CIRCLED HANGUL RIEUL;So;0;L; 1105;;;;N;CIRCLED HANGUL LIEUL;;;; 3264;CIRCLED HANGUL MIEUM;So;0;L; 1106;;;;N;;;;; 3265;CIRCLED HANGUL PIEUP;So;0;L; 1107;;;;N;CIRCLED HANGUL BIEUB;;;; 3266;CIRCLED HANGUL SIOS;So;0;L; 1109;;;;N;;;;; 3267;CIRCLED HANGUL IEUNG;So;0;L; 110B;;;;N;;;;; 3268;CIRCLED HANGUL CIEUC;So;0;L; 110C;;;;N;CIRCLED HANGUL JIEUJ;;;; 3269;CIRCLED HANGUL CHIEUCH;So;0;L; 110E;;;;N;CIRCLED HANGUL CIEUC;;;; 326A;CIRCLED HANGUL KHIEUKH;So;0;L; 110F;;;;N;CIRCLED HANGUL KIYEOK;;;; 326B;CIRCLED HANGUL THIEUTH;So;0;L; 1110;;;;N;CIRCLED HANGUL TIEUT;;;; 326C;CIRCLED HANGUL PHIEUPH;So;0;L; 1111;;;;N;CIRCLED HANGUL PIEUP;;;; 326D;CIRCLED HANGUL HIEUH;So;0;L; 1112;;;;N;;;;; 326E;CIRCLED HANGUL KIYEOK A;So;0;L; 1100 1161;;;;N;CIRCLED HANGUL GA;;;; 326F;CIRCLED HANGUL NIEUN A;So;0;L; 1102 1161;;;;N;CIRCLED HANGUL NA;;;; 3270;CIRCLED HANGUL TIKEUT A;So;0;L; 1103 1161;;;;N;CIRCLED HANGUL DA;;;; 3271;CIRCLED HANGUL RIEUL A;So;0;L; 1105 1161;;;;N;CIRCLED HANGUL LA;;;; 3272;CIRCLED HANGUL MIEUM A;So;0;L; 1106 1161;;;;N;CIRCLED HANGUL MA;;;; 3273;CIRCLED HANGUL PIEUP A;So;0;L; 1107 1161;;;;N;CIRCLED HANGUL BA;;;; 3274;CIRCLED HANGUL SIOS A;So;0;L; 1109 1161;;;;N;CIRCLED HANGUL SA;;;; 3275;CIRCLED HANGUL IEUNG A;So;0;L; 110B 1161;;;;N;CIRCLED HANGUL A;;;; 3276;CIRCLED HANGUL CIEUC A;So;0;L; 110C 1161;;;;N;CIRCLED HANGUL JA;;;; 3277;CIRCLED HANGUL CHIEUCH A;So;0;L; 110E 1161;;;;N;CIRCLED HANGUL CA;;;; 3278;CIRCLED HANGUL KHIEUKH A;So;0;L; 110F 1161;;;;N;CIRCLED HANGUL KA;;;; 3279;CIRCLED HANGUL THIEUTH A;So;0;L; 1110 1161;;;;N;CIRCLED HANGUL TA;;;; 327A;CIRCLED HANGUL PHIEUPH A;So;0;L; 1111 1161;;;;N;CIRCLED HANGUL PA;;;; 327B;CIRCLED HANGUL HIEUH A;So;0;L; 1112 1161;;;;N;CIRCLED HANGUL HA;;;; 327C;CIRCLED KOREAN CHARACTER CHAMKO;So;0;ON; 110E 1161 11B7 1100 1169;;;;N;;;;; 327D;CIRCLED KOREAN CHARACTER JUEUI;So;0;ON; 110C 116E 110B 1174;;;;N;;;;; 327E;CIRCLED HANGUL IEUNG U;So;0;ON; 110B 116E;;;;N;;;;; 327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;; 3280;CIRCLED IDEOGRAPH ONE;No;0;L; 4E00;;;1;N;;;;; 3281;CIRCLED IDEOGRAPH TWO;No;0;L; 4E8C;;;2;N;;;;; 3282;CIRCLED IDEOGRAPH THREE;No;0;L; 4E09;;;3;N;;;;; 3283;CIRCLED IDEOGRAPH FOUR;No;0;L; 56DB;;;4;N;;;;; 3284;CIRCLED IDEOGRAPH FIVE;No;0;L; 4E94;;;5;N;;;;; 3285;CIRCLED IDEOGRAPH SIX;No;0;L; 516D;;;6;N;;;;; 3286;CIRCLED IDEOGRAPH SEVEN;No;0;L; 4E03;;;7;N;;;;; 3287;CIRCLED IDEOGRAPH EIGHT;No;0;L; 516B;;;8;N;;;;; 3288;CIRCLED IDEOGRAPH NINE;No;0;L; 4E5D;;;9;N;;;;; 3289;CIRCLED IDEOGRAPH TEN;No;0;L; 5341;;;10;N;;;;; 328A;CIRCLED IDEOGRAPH MOON;So;0;L; 6708;;;;N;;;;; 328B;CIRCLED IDEOGRAPH FIRE;So;0;L; 706B;;;;N;;;;; 328C;CIRCLED IDEOGRAPH WATER;So;0;L; 6C34;;;;N;;;;; 328D;CIRCLED IDEOGRAPH WOOD;So;0;L; 6728;;;;N;;;;; 328E;CIRCLED IDEOGRAPH METAL;So;0;L; 91D1;;;;N;;;;; 328F;CIRCLED IDEOGRAPH EARTH;So;0;L; 571F;;;;N;;;;; 3290;CIRCLED IDEOGRAPH SUN;So;0;L; 65E5;;;;N;;;;; 3291;CIRCLED IDEOGRAPH STOCK;So;0;L; 682A;;;;N;;;;; 3292;CIRCLED IDEOGRAPH HAVE;So;0;L; 6709;;;;N;;;;; 3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L; 793E;;;;N;;;;; 3294;CIRCLED IDEOGRAPH NAME;So;0;L; 540D;;;;N;;;;; 3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L; 7279;;;;N;;;;; 3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L; 8CA1;;;;N;;;;; 3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L; 795D;;;;N;;;;; 3298;CIRCLED IDEOGRAPH LABOR;So;0;L; 52B4;;;;N;;;;; 3299;CIRCLED IDEOGRAPH SECRET;So;0;L; 79D8;;;;N;;;;; 329A;CIRCLED IDEOGRAPH MALE;So;0;L; 7537;;;;N;;;;; 329B;CIRCLED IDEOGRAPH FEMALE;So;0;L; 5973;;;;N;;;;; 329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L; 9069;;;;N;;;;; 329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L; 512A;;;;N;;;;; 329E;CIRCLED IDEOGRAPH PRINT;So;0;L; 5370;;;;N;;;;; 329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L; 6CE8;;;;N;;;;; 32A0;CIRCLED IDEOGRAPH ITEM;So;0;L; 9805;;;;N;;;;; 32A1;CIRCLED IDEOGRAPH REST;So;0;L; 4F11;;;;N;;;;; 32A2;CIRCLED IDEOGRAPH COPY;So;0;L; 5199;;;;N;;;;; 32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L; 6B63;;;;N;;;;; 32A4;CIRCLED IDEOGRAPH HIGH;So;0;L; 4E0A;;;;N;;;;; 32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L; 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;; 32A6;CIRCLED IDEOGRAPH LOW;So;0;L; 4E0B;;;;N;;;;; 32A7;CIRCLED IDEOGRAPH LEFT;So;0;L; 5DE6;;;;N;;;;; 32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L; 53F3;;;;N;;;;; 32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L; 533B;;;;N;;;;; 32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L; 5B97;;;;N;;;;; 32AB;CIRCLED IDEOGRAPH STUDY;So;0;L; 5B66;;;;N;;;;; 32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L; 76E3;;;;N;;;;; 32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L; 4F01;;;;N;;;;; 32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L; 8CC7;;;;N;;;;; 32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L; 5354;;;;N;;;;; 32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L; 591C;;;;N;;;;; 32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON; 0033 0036;;;36;N;;;;; 32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON; 0033 0037;;;37;N;;;;; 32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON; 0033 0038;;;38;N;;;;; 32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON; 0033 0039;;;39;N;;;;; 32B5;CIRCLED NUMBER FORTY;No;0;ON; 0034 0030;;;40;N;;;;; 32B6;CIRCLED NUMBER FORTY ONE;No;0;ON; 0034 0031;;;41;N;;;;; 32B7;CIRCLED NUMBER FORTY TWO;No;0;ON; 0034 0032;;;42;N;;;;; 32B8;CIRCLED NUMBER FORTY THREE;No;0;ON; 0034 0033;;;43;N;;;;; 32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON; 0034 0034;;;44;N;;;;; 32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON; 0034 0035;;;45;N;;;;; 32BB;CIRCLED NUMBER FORTY SIX;No;0;ON; 0034 0036;;;46;N;;;;; 32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON; 0034 0037;;;47;N;;;;; 32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON; 0034 0038;;;48;N;;;;; 32BE;CIRCLED NUMBER FORTY NINE;No;0;ON; 0034 0039;;;49;N;;;;; 32BF;CIRCLED NUMBER FIFTY;No;0;ON; 0035 0030;;;50;N;;;;; 32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L; 0031 6708;;;;N;;;;; 32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L; 0032 6708;;;;N;;;;; 32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L; 0033 6708;;;;N;;;;; 32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L; 0034 6708;;;;N;;;;; 32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L; 0035 6708;;;;N;;;;; 32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L; 0036 6708;;;;N;;;;; 32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L; 0037 6708;;;;N;;;;; 32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L; 0038 6708;;;;N;;;;; 32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L; 0039 6708;;;;N;;;;; 32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L; 0031 0030 6708;;;;N;;;;; 32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L; 0031 0031 6708;;;;N;;;;; 32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L; 0031 0032 6708;;;;N;;;;; 32CC;SQUARE HG;So;0;ON; 0048 0067;;;;N;;;;; 32CD;SQUARE ERG;So;0;ON; 0065 0072 0067;;;;N;;;;; 32CE;SQUARE EV;So;0;ON; 0065 0056;;;;N;;;;; 32CF;LIMITED LIABILITY SIGN;So;0;ON; 004C 0054 0044;;;;N;;;;; 32D0;CIRCLED KATAKANA A;So;0;L; 30A2;;;;N;;;;; 32D1;CIRCLED KATAKANA I;So;0;L; 30A4;;;;N;;;;; 32D2;CIRCLED KATAKANA U;So;0;L; 30A6;;;;N;;;;; 32D3;CIRCLED KATAKANA E;So;0;L; 30A8;;;;N;;;;; 32D4;CIRCLED KATAKANA O;So;0;L; 30AA;;;;N;;;;; 32D5;CIRCLED KATAKANA KA;So;0;L; 30AB;;;;N;;;;; 32D6;CIRCLED KATAKANA KI;So;0;L; 30AD;;;;N;;;;; 32D7;CIRCLED KATAKANA KU;So;0;L; 30AF;;;;N;;;;; 32D8;CIRCLED KATAKANA KE;So;0;L; 30B1;;;;N;;;;; 32D9;CIRCLED KATAKANA KO;So;0;L; 30B3;;;;N;;;;; 32DA;CIRCLED KATAKANA SA;So;0;L; 30B5;;;;N;;;;; 32DB;CIRCLED KATAKANA SI;So;0;L; 30B7;;;;N;;;;; 32DC;CIRCLED KATAKANA SU;So;0;L; 30B9;;;;N;;;;; 32DD;CIRCLED KATAKANA SE;So;0;L; 30BB;;;;N;;;;; 32DE;CIRCLED KATAKANA SO;So;0;L; 30BD;;;;N;;;;; 32DF;CIRCLED KATAKANA TA;So;0;L; 30BF;;;;N;;;;; 32E0;CIRCLED KATAKANA TI;So;0;L; 30C1;;;;N;;;;; 32E1;CIRCLED KATAKANA TU;So;0;L; 30C4;;;;N;;;;; 32E2;CIRCLED KATAKANA TE;So;0;L; 30C6;;;;N;;;;; 32E3;CIRCLED KATAKANA TO;So;0;L; 30C8;;;;N;;;;; 32E4;CIRCLED KATAKANA NA;So;0;L; 30CA;;;;N;;;;; 32E5;CIRCLED KATAKANA NI;So;0;L; 30CB;;;;N;;;;; 32E6;CIRCLED KATAKANA NU;So;0;L; 30CC;;;;N;;;;; 32E7;CIRCLED KATAKANA NE;So;0;L; 30CD;;;;N;;;;; 32E8;CIRCLED KATAKANA NO;So;0;L; 30CE;;;;N;;;;; 32E9;CIRCLED KATAKANA HA;So;0;L; 30CF;;;;N;;;;; 32EA;CIRCLED KATAKANA HI;So;0;L; 30D2;;;;N;;;;; 32EB;CIRCLED KATAKANA HU;So;0;L; 30D5;;;;N;;;;; 32EC;CIRCLED KATAKANA HE;So;0;L; 30D8;;;;N;;;;; 32ED;CIRCLED KATAKANA HO;So;0;L; 30DB;;;;N;;;;; 32EE;CIRCLED KATAKANA MA;So;0;L; 30DE;;;;N;;;;; 32EF;CIRCLED KATAKANA MI;So;0;L; 30DF;;;;N;;;;; 32F0;CIRCLED KATAKANA MU;So;0;L; 30E0;;;;N;;;;; 32F1;CIRCLED KATAKANA ME;So;0;L; 30E1;;;;N;;;;; 32F2;CIRCLED KATAKANA MO;So;0;L; 30E2;;;;N;;;;; 32F3;CIRCLED KATAKANA YA;So;0;L; 30E4;;;;N;;;;; 32F4;CIRCLED KATAKANA YU;So;0;L; 30E6;;;;N;;;;; 32F5;CIRCLED KATAKANA YO;So;0;L; 30E8;;;;N;;;;; 32F6;CIRCLED KATAKANA RA;So;0;L; 30E9;;;;N;;;;; 32F7;CIRCLED KATAKANA RI;So;0;L; 30EA;;;;N;;;;; 32F8;CIRCLED KATAKANA RU;So;0;L; 30EB;;;;N;;;;; 32F9;CIRCLED KATAKANA RE;So;0;L; 30EC;;;;N;;;;; 32FA;CIRCLED KATAKANA RO;So;0;L; 30ED;;;;N;;;;; 32FB;CIRCLED KATAKANA WA;So;0;L; 30EF;;;;N;;;;; 32FC;CIRCLED KATAKANA WI;So;0;L; 30F0;;;;N;;;;; 32FD;CIRCLED KATAKANA WE;So;0;L; 30F1;;;;N;;;;; 32FE;CIRCLED KATAKANA WO;So;0;L; 30F2;;;;N;;;;; 3300;SQUARE APAATO;So;0;L; 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;; 3301;SQUARE ARUHUA;So;0;L; 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;; 3302;SQUARE ANPEA;So;0;L; 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;; 3303;SQUARE AARU;So;0;L; 30A2 30FC 30EB;;;;N;SQUARED AARU;;;; 3304;SQUARE ININGU;So;0;L; 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;; 3305;SQUARE INTI;So;0;L; 30A4 30F3 30C1;;;;N;SQUARED INTI;;;; 3306;SQUARE UON;So;0;L; 30A6 30A9 30F3;;;;N;SQUARED UON;;;; 3307;SQUARE ESUKUUDO;So;0;L; 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;; 3308;SQUARE EEKAA;So;0;L; 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;; 3309;SQUARE ONSU;So;0;L; 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;; 330A;SQUARE OOMU;So;0;L; 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;; 330B;SQUARE KAIRI;So;0;L; 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;; 330C;SQUARE KARATTO;So;0;L; 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;; 330D;SQUARE KARORII;So;0;L; 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;; 330E;SQUARE GARON;So;0;L; 30AC 30ED 30F3;;;;N;SQUARED GARON;;;; 330F;SQUARE GANMA;So;0;L; 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;; 3310;SQUARE GIGA;So;0;L; 30AE 30AC;;;;N;SQUARED GIGA;;;; 3311;SQUARE GINII;So;0;L; 30AE 30CB 30FC;;;;N;SQUARED GINII;;;; 3312;SQUARE KYURII;So;0;L; 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;; 3313;SQUARE GIRUDAA;So;0;L; 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;; 3314;SQUARE KIRO;So;0;L; 30AD 30ED;;;;N;SQUARED KIRO;;;; 3315;SQUARE KIROGURAMU;So;0;L; 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;; 3316;SQUARE KIROMEETORU;So;0;L; 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;; 3317;SQUARE KIROWATTO;So;0;L; 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;; 3318;SQUARE GURAMU;So;0;L; 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;; 3319;SQUARE GURAMUTON;So;0;L; 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;; 331A;SQUARE KURUZEIRO;So;0;L; 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;; 331B;SQUARE KUROONE;So;0;L; 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;; 331C;SQUARE KEESU;So;0;L; 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;; 331D;SQUARE KORUNA;So;0;L; 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;; 331E;SQUARE KOOPO;So;0;L; 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;; 331F;SQUARE SAIKURU;So;0;L; 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;; 3320;SQUARE SANTIIMU;So;0;L; 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;; 3321;SQUARE SIRINGU;So;0;L; 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;; 3322;SQUARE SENTI;So;0;L; 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;; 3323;SQUARE SENTO;So;0;L; 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;; 3324;SQUARE DAASU;So;0;L; 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;; 3325;SQUARE DESI;So;0;L; 30C7 30B7;;;;N;SQUARED DESI;;;; 3326;SQUARE DORU;So;0;L; 30C9 30EB;;;;N;SQUARED DORU;;;; 3327;SQUARE TON;So;0;L; 30C8 30F3;;;;N;SQUARED TON;;;; 3328;SQUARE NANO;So;0;L; 30CA 30CE;;;;N;SQUARED NANO;;;; 3329;SQUARE NOTTO;So;0;L; 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;; 332A;SQUARE HAITU;So;0;L; 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;; 332B;SQUARE PAASENTO;So;0;L; 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;; 332C;SQUARE PAATU;So;0;L; 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;; 332D;SQUARE BAARERU;So;0;L; 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;; 332E;SQUARE PIASUTORU;So;0;L; 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;; 332F;SQUARE PIKURU;So;0;L; 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;; 3330;SQUARE PIKO;So;0;L; 30D4 30B3;;;;N;SQUARED PIKO;;;; 3331;SQUARE BIRU;So;0;L; 30D3 30EB;;;;N;SQUARED BIRU;;;; 3332;SQUARE HUARADDO;So;0;L; 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;; 3333;SQUARE HUIITO;So;0;L; 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;; 3334;SQUARE BUSSYERU;So;0;L; 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;; 3335;SQUARE HURAN;So;0;L; 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;; 3336;SQUARE HEKUTAARU;So;0;L; 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;; 3337;SQUARE PESO;So;0;L; 30DA 30BD;;;;N;SQUARED PESO;;;; 3338;SQUARE PENIHI;So;0;L; 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;; 3339;SQUARE HERUTU;So;0;L; 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;; 333A;SQUARE PENSU;So;0;L; 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;; 333B;SQUARE PEEZI;So;0;L; 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;; 333C;SQUARE BEETA;So;0;L; 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;; 333D;SQUARE POINTO;So;0;L; 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;; 333E;SQUARE BORUTO;So;0;L; 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;; 333F;SQUARE HON;So;0;L; 30DB 30F3;;;;N;SQUARED HON;;;; 3340;SQUARE PONDO;So;0;L; 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;; 3341;SQUARE HOORU;So;0;L; 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;; 3342;SQUARE HOON;So;0;L; 30DB 30FC 30F3;;;;N;SQUARED HOON;;;; 3343;SQUARE MAIKURO;So;0;L; 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;; 3344;SQUARE MAIRU;So;0;L; 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;; 3345;SQUARE MAHHA;So;0;L; 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;; 3346;SQUARE MARUKU;So;0;L; 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;; 3347;SQUARE MANSYON;So;0;L; 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;; 3348;SQUARE MIKURON;So;0;L; 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;; 3349;SQUARE MIRI;So;0;L; 30DF 30EA;;;;N;SQUARED MIRI;;;; 334A;SQUARE MIRIBAARU;So;0;L; 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;; 334B;SQUARE MEGA;So;0;L; 30E1 30AC;;;;N;SQUARED MEGA;;;; 334C;SQUARE MEGATON;So;0;L; 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;; 334D;SQUARE MEETORU;So;0;L; 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;; 334E;SQUARE YAADO;So;0;L; 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;; 334F;SQUARE YAARU;So;0;L; 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;; 3350;SQUARE YUAN;So;0;L; 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;; 3351;SQUARE RITTORU;So;0;L; 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;; 3352;SQUARE RIRA;So;0;L; 30EA 30E9;;;;N;SQUARED RIRA;;;; 3353;SQUARE RUPII;So;0;L; 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;; 3354;SQUARE RUUBURU;So;0;L; 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;; 3355;SQUARE REMU;So;0;L; 30EC 30E0;;;;N;SQUARED REMU;;;; 3356;SQUARE RENTOGEN;So;0;L; 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;; 3357;SQUARE WATTO;So;0;L; 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;; 3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L; 0030 70B9;;;;N;;;;; 3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L; 0031 70B9;;;;N;;;;; 335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L; 0032 70B9;;;;N;;;;; 335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L; 0033 70B9;;;;N;;;;; 335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L; 0034 70B9;;;;N;;;;; 335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L; 0035 70B9;;;;N;;;;; 335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L; 0036 70B9;;;;N;;;;; 335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L; 0037 70B9;;;;N;;;;; 3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L; 0038 70B9;;;;N;;;;; 3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L; 0039 70B9;;;;N;;;;; 3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L; 0031 0030 70B9;;;;N;;;;; 3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L; 0031 0031 70B9;;;;N;;;;; 3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L; 0031 0032 70B9;;;;N;;;;; 3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L; 0031 0033 70B9;;;;N;;;;; 3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L; 0031 0034 70B9;;;;N;;;;; 3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L; 0031 0035 70B9;;;;N;;;;; 3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L; 0031 0036 70B9;;;;N;;;;; 3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L; 0031 0037 70B9;;;;N;;;;; 336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L; 0031 0038 70B9;;;;N;;;;; 336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L; 0031 0039 70B9;;;;N;;;;; 336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L; 0032 0030 70B9;;;;N;;;;; 336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L; 0032 0031 70B9;;;;N;;;;; 336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L; 0032 0032 70B9;;;;N;;;;; 336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L; 0032 0033 70B9;;;;N;;;;; 3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L; 0032 0034 70B9;;;;N;;;;; 3371;SQUARE HPA;So;0;L; 0068 0050 0061;;;;N;;;;; 3372;SQUARE DA;So;0;L; 0064 0061;;;;N;;;;; 3373;SQUARE AU;So;0;L; 0041 0055;;;;N;;;;; 3374;SQUARE BAR;So;0;L; 0062 0061 0072;;;;N;;;;; 3375;SQUARE OV;So;0;L; 006F 0056;;;;N;;;;; 3376;SQUARE PC;So;0;L; 0070 0063;;;;N;;;;; 3377;SQUARE DM;So;0;ON; 0064 006D;;;;N;;;;; 3378;SQUARE DM SQUARED;So;0;ON; 0064 006D 00B2;;;;N;;;;; 3379;SQUARE DM CUBED;So;0;ON; 0064 006D 00B3;;;;N;;;;; 337A;SQUARE IU;So;0;ON; 0049 0055;;;;N;;;;; 337B;SQUARE ERA NAME HEISEI;So;0;L; 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;; 337C;SQUARE ERA NAME SYOUWA;So;0;L; 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;; 337D;SQUARE ERA NAME TAISYOU;So;0;L; 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;; 337E;SQUARE ERA NAME MEIZI;So;0;L; 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;; 337F;SQUARE CORPORATION;So;0;L; 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;; 3380;SQUARE PA AMPS;So;0;L; 0070 0041;;;;N;SQUARED PA AMPS;;;; 3381;SQUARE NA;So;0;L; 006E 0041;;;;N;SQUARED NA;;;; 3382;SQUARE MU A;So;0;L; 03BC 0041;;;;N;SQUARED MU A;;;; 3383;SQUARE MA;So;0;L; 006D 0041;;;;N;SQUARED MA;;;; 3384;SQUARE KA;So;0;L; 006B 0041;;;;N;SQUARED KA;;;; 3385;SQUARE KB;So;0;L; 004B 0042;;;;N;SQUARED KB;;;; 3386;SQUARE MB;So;0;L; 004D 0042;;;;N;SQUARED MB;;;; 3387;SQUARE GB;So;0;L; 0047 0042;;;;N;SQUARED GB;;;; 3388;SQUARE CAL;So;0;L; 0063 0061 006C;;;;N;SQUARED CAL;;;; 3389;SQUARE KCAL;So;0;L; 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;; 338A;SQUARE PF;So;0;L; 0070 0046;;;;N;SQUARED PF;;;; 338B;SQUARE NF;So;0;L; 006E 0046;;;;N;SQUARED NF;;;; 338C;SQUARE MU F;So;0;L; 03BC 0046;;;;N;SQUARED MU F;;;; 338D;SQUARE MU G;So;0;L; 03BC 0067;;;;N;SQUARED MU G;;;; 338E;SQUARE MG;So;0;L; 006D 0067;;;;N;SQUARED MG;;;; 338F;SQUARE KG;So;0;L; 006B 0067;;;;N;SQUARED KG;;;; 3390;SQUARE HZ;So;0;L; 0048 007A;;;;N;SQUARED HZ;;;; 3391;SQUARE KHZ;So;0;L; 006B 0048 007A;;;;N;SQUARED KHZ;;;; 3392;SQUARE MHZ;So;0;L; 004D 0048 007A;;;;N;SQUARED MHZ;;;; 3393;SQUARE GHZ;So;0;L; 0047 0048 007A;;;;N;SQUARED GHZ;;;; 3394;SQUARE THZ;So;0;L; 0054 0048 007A;;;;N;SQUARED THZ;;;; 3395;SQUARE MU L;So;0;L; 03BC 2113;;;;N;SQUARED MU L;;;; 3396;SQUARE ML;So;0;L; 006D 2113;;;;N;SQUARED ML;;;; 3397;SQUARE DL;So;0;L; 0064 2113;;;;N;SQUARED DL;;;; 3398;SQUARE KL;So;0;L; 006B 2113;;;;N;SQUARED KL;;;; 3399;SQUARE FM;So;0;L; 0066 006D;;;;N;SQUARED FM;;;; 339A;SQUARE NM;So;0;L; 006E 006D;;;;N;SQUARED NM;;;; 339B;SQUARE MU M;So;0;L; 03BC 006D;;;;N;SQUARED MU M;;;; 339C;SQUARE MM;So;0;L; 006D 006D;;;;N;SQUARED MM;;;; 339D;SQUARE CM;So;0;L; 0063 006D;;;;N;SQUARED CM;;;; 339E;SQUARE KM;So;0;L; 006B 006D;;;;N;SQUARED KM;;;; 339F;SQUARE MM SQUARED;So;0;L; 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;; 33A0;SQUARE CM SQUARED;So;0;L; 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;; 33A1;SQUARE M SQUARED;So;0;L; 006D 00B2;;;;N;SQUARED M SQUARED;;;; 33A2;SQUARE KM SQUARED;So;0;L; 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;; 33A3;SQUARE MM CUBED;So;0;L; 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;; 33A4;SQUARE CM CUBED;So;0;L; 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;; 33A5;SQUARE M CUBED;So;0;L; 006D 00B3;;;;N;SQUARED M CUBED;;;; 33A6;SQUARE KM CUBED;So;0;L; 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;; 33A7;SQUARE M OVER S;So;0;L; 006D 2215 0073;;;;N;SQUARED M OVER S;;;; 33A8;SQUARE M OVER S SQUARED;So;0;L; 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;; 33A9;SQUARE PA;So;0;L; 0050 0061;;;;N;SQUARED PA;;;; 33AA;SQUARE KPA;So;0;L; 006B 0050 0061;;;;N;SQUARED KPA;;;; 33AB;SQUARE MPA;So;0;L; 004D 0050 0061;;;;N;SQUARED MPA;;;; 33AC;SQUARE GPA;So;0;L; 0047 0050 0061;;;;N;SQUARED GPA;;;; 33AD;SQUARE RAD;So;0;L; 0072 0061 0064;;;;N;SQUARED RAD;;;; 33AE;SQUARE RAD OVER S;So;0;L; 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;; 33AF;SQUARE RAD OVER S SQUARED;So;0;L; 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;; 33B0;SQUARE PS;So;0;L; 0070 0073;;;;N;SQUARED PS;;;; 33B1;SQUARE NS;So;0;L; 006E 0073;;;;N;SQUARED NS;;;; 33B2;SQUARE MU S;So;0;L; 03BC 0073;;;;N;SQUARED MU S;;;; 33B3;SQUARE MS;So;0;L; 006D 0073;;;;N;SQUARED MS;;;; 33B4;SQUARE PV;So;0;L; 0070 0056;;;;N;SQUARED PV;;;; 33B5;SQUARE NV;So;0;L; 006E 0056;;;;N;SQUARED NV;;;; 33B6;SQUARE MU V;So;0;L; 03BC 0056;;;;N;SQUARED MU V;;;; 33B7;SQUARE MV;So;0;L; 006D 0056;;;;N;SQUARED MV;;;; 33B8;SQUARE KV;So;0;L; 006B 0056;;;;N;SQUARED KV;;;; 33B9;SQUARE MV MEGA;So;0;L; 004D 0056;;;;N;SQUARED MV MEGA;;;; 33BA;SQUARE PW;So;0;L; 0070 0057;;;;N;SQUARED PW;;;; 33BB;SQUARE NW;So;0;L; 006E 0057;;;;N;SQUARED NW;;;; 33BC;SQUARE MU W;So;0;L; 03BC 0057;;;;N;SQUARED MU W;;;; 33BD;SQUARE MW;So;0;L; 006D 0057;;;;N;SQUARED MW;;;; 33BE;SQUARE KW;So;0;L; 006B 0057;;;;N;SQUARED KW;;;; 33BF;SQUARE MW MEGA;So;0;L; 004D 0057;;;;N;SQUARED MW MEGA;;;; 33C0;SQUARE K OHM;So;0;L; 006B 03A9;;;;N;SQUARED K OHM;;;; 33C1;SQUARE M OHM;So;0;L; 004D 03A9;;;;N;SQUARED M OHM;;;; 33C2;SQUARE AM;So;0;L; 0061 002E 006D 002E;;;;N;SQUARED AM;;;; 33C3;SQUARE BQ;So;0;L; 0042 0071;;;;N;SQUARED BQ;;;; 33C4;SQUARE CC;So;0;L; 0063 0063;;;;N;SQUARED CC;;;; 33C5;SQUARE CD;So;0;L; 0063 0064;;;;N;SQUARED CD;;;; 33C6;SQUARE C OVER KG;So;0;L; 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;; 33C7;SQUARE CO;So;0;L; 0043 006F 002E;;;;N;SQUARED CO;;;; 33C8;SQUARE DB;So;0;L; 0064 0042;;;;N;SQUARED DB;;;; 33C9;SQUARE GY;So;0;L; 0047 0079;;;;N;SQUARED GY;;;; 33CA;SQUARE HA;So;0;L; 0068 0061;;;;N;SQUARED HA;;;; 33CB;SQUARE HP;So;0;L; 0048 0050;;;;N;SQUARED HP;;;; 33CC;SQUARE IN;So;0;L; 0069 006E;;;;N;SQUARED IN;;;; 33CD;SQUARE KK;So;0;L; 004B 004B;;;;N;SQUARED KK;;;; 33CE;SQUARE KM CAPITAL;So;0;L; 004B 004D;;;;N;SQUARED KM CAPITAL;;;; 33CF;SQUARE KT;So;0;L; 006B 0074;;;;N;SQUARED KT;;;; 33D0;SQUARE LM;So;0;L; 006C 006D;;;;N;SQUARED LM;;;; 33D1;SQUARE LN;So;0;L; 006C 006E;;;;N;SQUARED LN;;;; 33D2;SQUARE LOG;So;0;L; 006C 006F 0067;;;;N;SQUARED LOG;;;; 33D3;SQUARE LX;So;0;L; 006C 0078;;;;N;SQUARED LX;;;; 33D4;SQUARE MB SMALL;So;0;L; 006D 0062;;;;N;SQUARED MB SMALL;;;; 33D5;SQUARE MIL;So;0;L; 006D 0069 006C;;;;N;SQUARED MIL;;;; 33D6;SQUARE MOL;So;0;L; 006D 006F 006C;;;;N;SQUARED MOL;;;; 33D7;SQUARE PH;So;0;L; 0050 0048;;;;N;SQUARED PH;;;; 33D8;SQUARE PM;So;0;L; 0070 002E 006D 002E;;;;N;SQUARED PM;;;; 33D9;SQUARE PPM;So;0;L; 0050 0050 004D;;;;N;SQUARED PPM;;;; 33DA;SQUARE PR;So;0;L; 0050 0052;;;;N;SQUARED PR;;;; 33DB;SQUARE SR;So;0;L; 0073 0072;;;;N;SQUARED SR;;;; 33DC;SQUARE SV;So;0;L; 0053 0076;;;;N;SQUARED SV;;;; 33DD;SQUARE WB;So;0;L; 0057 0062;;;;N;SQUARED WB;;;; 33DE;SQUARE V OVER M;So;0;ON; 0056 2215 006D;;;;N;;;;; 33DF;SQUARE A OVER M;So;0;ON; 0041 2215 006D;;;;N;;;;; 33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L; 0031 65E5;;;;N;;;;; 33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L; 0032 65E5;;;;N;;;;; 33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L; 0033 65E5;;;;N;;;;; 33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L; 0034 65E5;;;;N;;;;; 33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L; 0035 65E5;;;;N;;;;; 33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L; 0036 65E5;;;;N;;;;; 33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L; 0037 65E5;;;;N;;;;; 33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L; 0038 65E5;;;;N;;;;; 33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L; 0039 65E5;;;;N;;;;; 33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L; 0031 0030 65E5;;;;N;;;;; 33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L; 0031 0031 65E5;;;;N;;;;; 33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L; 0031 0032 65E5;;;;N;;;;; 33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L; 0031 0033 65E5;;;;N;;;;; 33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L; 0031 0034 65E5;;;;N;;;;; 33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L; 0031 0035 65E5;;;;N;;;;; 33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L; 0031 0036 65E5;;;;N;;;;; 33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L; 0031 0037 65E5;;;;N;;;;; 33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L; 0031 0038 65E5;;;;N;;;;; 33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L; 0031 0039 65E5;;;;N;;;;; 33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L; 0032 0030 65E5;;;;N;;;;; 33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L; 0032 0031 65E5;;;;N;;;;; 33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L; 0032 0032 65E5;;;;N;;;;; 33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L; 0032 0033 65E5;;;;N;;;;; 33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L; 0032 0034 65E5;;;;N;;;;; 33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L; 0032 0035 65E5;;;;N;;;;; 33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L; 0032 0036 65E5;;;;N;;;;; 33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L; 0032 0037 65E5;;;;N;;;;; 33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L; 0032 0038 65E5;;;;N;;;;; 33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L; 0032 0039 65E5;;;;N;;;;; 33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L; 0033 0030 65E5;;;;N;;;;; 33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L; 0033 0031 65E5;;;;N;;;;; 33FF;SQUARE GAL;So;0;ON; 0067 0061 006C;;;;N;;;;; 3400;;Lo;0;L;;;;;N;;;;; 4DB5;;Lo;0;L;;;;;N;;;;; 4DC0;HEXAGRAM FOR THE CREATIVE HEAVEN;So;0;ON;;;;;N;;;;; 4DC1;HEXAGRAM FOR THE RECEPTIVE EARTH;So;0;ON;;;;;N;;;;; 4DC2;HEXAGRAM FOR DIFFICULTY AT THE BEGINNING;So;0;ON;;;;;N;;;;; 4DC3;HEXAGRAM FOR YOUTHFUL FOLLY;So;0;ON;;;;;N;;;;; 4DC4;HEXAGRAM FOR WAITING;So;0;ON;;;;;N;;;;; 4DC5;HEXAGRAM FOR CONFLICT;So;0;ON;;;;;N;;;;; 4DC6;HEXAGRAM FOR THE ARMY;So;0;ON;;;;;N;;;;; 4DC7;HEXAGRAM FOR HOLDING TOGETHER;So;0;ON;;;;;N;;;;; 4DC8;HEXAGRAM FOR SMALL TAMING;So;0;ON;;;;;N;;;;; 4DC9;HEXAGRAM FOR TREADING;So;0;ON;;;;;N;;;;; 4DCA;HEXAGRAM FOR PEACE;So;0;ON;;;;;N;;;;; 4DCB;HEXAGRAM FOR STANDSTILL;So;0;ON;;;;;N;;;;; 4DCC;HEXAGRAM FOR FELLOWSHIP;So;0;ON;;;;;N;;;;; 4DCD;HEXAGRAM FOR GREAT POSSESSION;So;0;ON;;;;;N;;;;; 4DCE;HEXAGRAM FOR MODESTY;So;0;ON;;;;;N;;;;; 4DCF;HEXAGRAM FOR ENTHUSIASM;So;0;ON;;;;;N;;;;; 4DD0;HEXAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;; 4DD1;HEXAGRAM FOR WORK ON THE DECAYED;So;0;ON;;;;;N;;;;; 4DD2;HEXAGRAM FOR APPROACH;So;0;ON;;;;;N;;;;; 4DD3;HEXAGRAM FOR CONTEMPLATION;So;0;ON;;;;;N;;;;; 4DD4;HEXAGRAM FOR BITING THROUGH;So;0;ON;;;;;N;;;;; 4DD5;HEXAGRAM FOR GRACE;So;0;ON;;;;;N;;;;; 4DD6;HEXAGRAM FOR SPLITTING APART;So;0;ON;;;;;N;;;;; 4DD7;HEXAGRAM FOR RETURN;So;0;ON;;;;;N;;;;; 4DD8;HEXAGRAM FOR INNOCENCE;So;0;ON;;;;;N;;;;; 4DD9;HEXAGRAM FOR GREAT TAMING;So;0;ON;;;;;N;;;;; 4DDA;HEXAGRAM FOR MOUTH CORNERS;So;0;ON;;;;;N;;;;; 4DDB;HEXAGRAM FOR GREAT PREPONDERANCE;So;0;ON;;;;;N;;;;; 4DDC;HEXAGRAM FOR THE ABYSMAL WATER;So;0;ON;;;;;N;;;;; 4DDD;HEXAGRAM FOR THE CLINGING FIRE;So;0;ON;;;;;N;;;;; 4DDE;HEXAGRAM FOR INFLUENCE;So;0;ON;;;;;N;;;;; 4DDF;HEXAGRAM FOR DURATION;So;0;ON;;;;;N;;;;; 4DE0;HEXAGRAM FOR RETREAT;So;0;ON;;;;;N;;;;; 4DE1;HEXAGRAM FOR GREAT POWER;So;0;ON;;;;;N;;;;; 4DE2;HEXAGRAM FOR PROGRESS;So;0;ON;;;;;N;;;;; 4DE3;HEXAGRAM FOR DARKENING OF THE LIGHT;So;0;ON;;;;;N;;;;; 4DE4;HEXAGRAM FOR THE FAMILY;So;0;ON;;;;;N;;;;; 4DE5;HEXAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;; 4DE6;HEXAGRAM FOR OBSTRUCTION;So;0;ON;;;;;N;;;;; 4DE7;HEXAGRAM FOR DELIVERANCE;So;0;ON;;;;;N;;;;; 4DE8;HEXAGRAM FOR DECREASE;So;0;ON;;;;;N;;;;; 4DE9;HEXAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;; 4DEA;HEXAGRAM FOR BREAKTHROUGH;So;0;ON;;;;;N;;;;; 4DEB;HEXAGRAM FOR COMING TO MEET;So;0;ON;;;;;N;;;;; 4DEC;HEXAGRAM FOR GATHERING TOGETHER;So;0;ON;;;;;N;;;;; 4DED;HEXAGRAM FOR PUSHING UPWARD;So;0;ON;;;;;N;;;;; 4DEE;HEXAGRAM FOR OPPRESSION;So;0;ON;;;;;N;;;;; 4DEF;HEXAGRAM FOR THE WELL;So;0;ON;;;;;N;;;;; 4DF0;HEXAGRAM FOR REVOLUTION;So;0;ON;;;;;N;;;;; 4DF1;HEXAGRAM FOR THE CAULDRON;So;0;ON;;;;;N;;;;; 4DF2;HEXAGRAM FOR THE AROUSING THUNDER;So;0;ON;;;;;N;;;;; 4DF3;HEXAGRAM FOR THE KEEPING STILL MOUNTAIN;So;0;ON;;;;;N;;;;; 4DF4;HEXAGRAM FOR DEVELOPMENT;So;0;ON;;;;;N;;;;; 4DF5;HEXAGRAM FOR THE MARRYING MAIDEN;So;0;ON;;;;;N;;;;; 4DF6;HEXAGRAM FOR ABUNDANCE;So;0;ON;;;;;N;;;;; 4DF7;HEXAGRAM FOR THE WANDERER;So;0;ON;;;;;N;;;;; 4DF8;HEXAGRAM FOR THE GENTLE WIND;So;0;ON;;;;;N;;;;; 4DF9;HEXAGRAM FOR THE JOYOUS LAKE;So;0;ON;;;;;N;;;;; 4DFA;HEXAGRAM FOR DISPERSION;So;0;ON;;;;;N;;;;; 4DFB;HEXAGRAM FOR LIMITATION;So;0;ON;;;;;N;;;;; 4DFC;HEXAGRAM FOR INNER TRUTH;So;0;ON;;;;;N;;;;; 4DFD;HEXAGRAM FOR SMALL PREPONDERANCE;So;0;ON;;;;;N;;;;; 4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;; 4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;; 4E00;;Lo;0;L;;;;;N;;;;; 9FD5;;Lo;0;L;;;;;N;;;;; A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;; A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;; A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;; A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;; A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;; A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;; A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;; A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;; A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;; A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;; A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;; A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;; A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;; A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;; A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;; A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;; A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;; A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;; A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;; A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;; A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;; A015;YI SYLLABLE WU;Lm;0;L;;;;;N;;;;; A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;; A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;; A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;; A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;; A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;; A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;; A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;; A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;; A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;; A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;; A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;; A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;; A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;; A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;; A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;; A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;; A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;; A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;; A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;; A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;; A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;; A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;; A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;; A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;; A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;; A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;; A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;; A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;; A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;; A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;; A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;; A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;; A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;; A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;; A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;; A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;; A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;; A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;; A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;; A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;; A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;; A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;; A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;; A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;; A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;; A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;; A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;; A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;; A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;; A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;; A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;; A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;; A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;; A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;; A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;; A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;; A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;; A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;; A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;; A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;; A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;; A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;; A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;; A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;; A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;; A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;; A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;; A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;; A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;; A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;; A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;; A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;; A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;; A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;; A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;; A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;; A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;; A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;; A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;; A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;; A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;; A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;; A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;; A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;; A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;; A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;; A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;; A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;; A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;; A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;; A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;; A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;; A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;; A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;; A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;; A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;; A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;; A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;; A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;; A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;; A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;; A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;; A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;; A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;; A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;; A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;; A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;; A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;; A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;; A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;; A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;; A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;; A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;; A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;; A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;; A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;; A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;; A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;; A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;; A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;; A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;; A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;; A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;; A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;; A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;; A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;; A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;; A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;; A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;; A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;; A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;; A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;; A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;; A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;; A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;; A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;; A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;; A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;; A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;; A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;; A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;; A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;; A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;; A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;; A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;; A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;; A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;; A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;; A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;; A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;; A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;; A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;; A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;; A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;; A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;; A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;; A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;; A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;; A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;; A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;; A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;; A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;; A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;; A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;; A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;; A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;; A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;; A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;; A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;; A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;; A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;; A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;; A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;; A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;; A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;; A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;; A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;; A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;; A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;; A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;; A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;; A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;; A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;; A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;; A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;; A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;; A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;; A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;; A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;; A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;; A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;; A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;; A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;; A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;; A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;; A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;; A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;; A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;; A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;; A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;; A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;; A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;; A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;; A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;; A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;; A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;; A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;; A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;; A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;; A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;; A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;; A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;; A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;; A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;; A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;; A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;; A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;; A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;; A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;; A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;; A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;; A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;; A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;; A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;; A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;; A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;; A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;; A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;; A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;; A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;; A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;; A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;; A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;; A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;; A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;; A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;; A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;; A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;; A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;; A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;; A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;; A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;; A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;; A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;; A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;; A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;; A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;; A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;; A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;; A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;; A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;; A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;; A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;; A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;; A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;; A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;; A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;; A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;; A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;; A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;; A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;; A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;; A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;; A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;; A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;; A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;; A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;; A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;; A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;; A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;; A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;; A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;; A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;; A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;; A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;; A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;; A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;; A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;; A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;; A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;; A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;; A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;; A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;; A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;; A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;; A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;; A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;; A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;; A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;; A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;; A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;; A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;; A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;; A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;; A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;; A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;; A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;; A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;; A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;; A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;; A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;; A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;; A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;; A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;; A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;; A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;; A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;; A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;; A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;; A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;; A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;; A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;; A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;; A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;; A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;; A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;; A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;; A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;; A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;; A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;; A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;; A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;; A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;; A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;; A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;; A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;; A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;; A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;; A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;; A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;; A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;; A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;; A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;; A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;; A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;; A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;; A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;; A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;; A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;; A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;; A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;; A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;; A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;; A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;; A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;; A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;; A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;; A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;; A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;; A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;; A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;; A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;; A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;; A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;; A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;; A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;; A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;; A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;; A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;; A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;; A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;; A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;; A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;; A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;; A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;; A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;; A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;; A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;; A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;; A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;; A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;; A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;; A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;; A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;; A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;; A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;; A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;; A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;; A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;; A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;; A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;; A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;; A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;; A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;; A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;; A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;; A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;; A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;; A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;; A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;; A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;; A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;; A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;; A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;; A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;; A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;; A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;; A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;; A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;; A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;; A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;; A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;; A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;; A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;; A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;; A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;; A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;; A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;; A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;; A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;; A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;; A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;; A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;; A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;; A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;; A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;; A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;; A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;; A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;; A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;; A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;; A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;; A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;; A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;; A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;; A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;; A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;; A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;; A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;; A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;; A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;; A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;; A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;; A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;; A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;; A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;; A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;; A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;; A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;; A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;; A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;; A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;; A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;; A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;; A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;; A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;; A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;; A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;; A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;; A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;; A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;; A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;; A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;; A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;; A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;; A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;; A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;; A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;; A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;; A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;; A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;; A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;; A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;; A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;; A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;; A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;; A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;; A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;; A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;; A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;; A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;; A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;; A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;; A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;; A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;; A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;; A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;; A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;; A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;; A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;; A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;; A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;; A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;; A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;; A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;; A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;; A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;; A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;; A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;; A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;; A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;; A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;; A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;; A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;; A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;; A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;; A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;; A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;; A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;; A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;; A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;; A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;; A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;; A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;; A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;; A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;; A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;; A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;; A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;; A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;; A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;; A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;; A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;; A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;; A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;; A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;; A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;; A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;; A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;; A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;; A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;; A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;; A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;; A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;; A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;; A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;; A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;; A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;; A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;; A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;; A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;; A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;; A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;; A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;; A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;; A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;; A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;; A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;; A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;; A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;; A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;; A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;; A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;; A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;; A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;; A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;; A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;; A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;; A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;; A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;; A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;; A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;; A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;; A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;; A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;; A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;; A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;; A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;; A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;; A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;; A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;; A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;; A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;; A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;; A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;; A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;; A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;; A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;; A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;; A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;; A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;; A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;; A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;; A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;; A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;; A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;; A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;; A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;; A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;; A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;; A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;; A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;; A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;; A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;; A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;; A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;; A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;; A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;; A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;; A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;; A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;; A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;; A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;; A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;; A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;; A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;; A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;; A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;; A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;; A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;; A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;; A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;; A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;; A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;; A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;; A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;; A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;; A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;; A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;; A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;; A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;; A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;; A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;; A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;; A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;; A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;; A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;; A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;; A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;; A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;; A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;; A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;; A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;; A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;; A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;; A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;; A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;; A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;; A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;; A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;; A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;; A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;; A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;; A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;; A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;; A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;; A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;; A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;; A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;; A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;; A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;; A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;; A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;; A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;; A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;; A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;; A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;; A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;; A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;; A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;; A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;; A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;; A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;; A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;; A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;; A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;; A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;; A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;; A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;; A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;; A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;; A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;; A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;; A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;; A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;; A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;; A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;; A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;; A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;; A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;; A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;; A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;; A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;; A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;; A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;; A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;; A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;; A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;; A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;; A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;; A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;; A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;; A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;; A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;; A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;; A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;; A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;; A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;; A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;; A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;; A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;; A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;; A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;; A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;; A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;; A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;; A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;; A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;; A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;; A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;; A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;; A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;; A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;; A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;; A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;; A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;; A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;; A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;; A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;; A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;; A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;; A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;; A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;; A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;; A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;; A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;; A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;; A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;; A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;; A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;; A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;; A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;; A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;; A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;; A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;; A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;; A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;; A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;; A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;; A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;; A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;; A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;; A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;; A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;; A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;; A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;; A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;; A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;; A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;; A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;; A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;; A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;; A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;; A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;; A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;; A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;; A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;; A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;; A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;; A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;; A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;; A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;; A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;; A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;; A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;; A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;; A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;; A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;; A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;; A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;; A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;; A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;; A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;; A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;; A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;; A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;; A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;; A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;; A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;; A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;; A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;; A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;; A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;; A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;; A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;; A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;; A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;; A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;; A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;; A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;; A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;; A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;; A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;; A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;; A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;; A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;; A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;; A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;; A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;; A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;; A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;; A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;; A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;; A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;; A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;; A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;; A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;; A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;; A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;; A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;; A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;; A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;; A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;; A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;; A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;; A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;; A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;; A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;; A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;; A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;; A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;; A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;; A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;; A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;; A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;; A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;; A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;; A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;; A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;; A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;; A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;; A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;; A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;; A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;; A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;; A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;; A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;; A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;; A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;; A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;; A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;; A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;; A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;; A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;; A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;; A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;; A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;; A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;; A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;; A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;; A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;; A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;; A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;; A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;; A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;; A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;; A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;; A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;; A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;; A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;; A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;; A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;; A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;; A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;; A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;; A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;; A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;; A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;; A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;; A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;; A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;; A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;; A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;; A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;; A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;; A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;; A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;; A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;; A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;; A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;; A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;; A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;; A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;; A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;; A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;; A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;; A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;; A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;; A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;; A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;; A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;; A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;; A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;; A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;; A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;; A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;; A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;; A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;; A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;; A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;; A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;; A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;; A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;; A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;; A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;; A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;; A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;; A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;; A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;; A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;; A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;; A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;; A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;; A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;; A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;; A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;; A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;; A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;; A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;; A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;; A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;; A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;; A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;; A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;; A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;; A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;; A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;; A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;; A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;; A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;; A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;; A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;; A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;; A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;; A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;; A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;; A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;; A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;; A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;; A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;; A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;; A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;; A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;; A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;; A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;; A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;; A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;; A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;; A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;; A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;; A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;; A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;; A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;; A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;; A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;; A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;; A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;; A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;; A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;; A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;; A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;; A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;; A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;; A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;; A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;; A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;; A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;; A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;; A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;; A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;; A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;; A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;; A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;; A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;; A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;; A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;; A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;; A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;; A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;; A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;; A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;; A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;; A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;; A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;; A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;; A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;; A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;; A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;; A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;; A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;; A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;; A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;; A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;; A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;; A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;; A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;; A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;; A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;; A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;; A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;; A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;; A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;; A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;; A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;; A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;; A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;; A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;; A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;; A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;; A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;; A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;; A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;; A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;; A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;; A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;; A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;; A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;; A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;; A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;; A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;; A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;; A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;; A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;; A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;; A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;; A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;; A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;; A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;; A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;; A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;; A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;; A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;; A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;; A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;; A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;; A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;; A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;; A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;; A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;; A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;; A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;; A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;; A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;; A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;; A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;; A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;; A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;; A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;; A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;; A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;; A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;; A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;; A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;; A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;; A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;; A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;; A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;; A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;; A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;; A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;; A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;; A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;; A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;; A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;; A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;; A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;; A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;; A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;; A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;; A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;; A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;; A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;; A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;; A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;; A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;; A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;; A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;; A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;; A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;; A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;; A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;; A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;; A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;; A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;; A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;; A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;; A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;; A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;; A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;; A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;; A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;; A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;; A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;; A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;; A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;; A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;; A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;; A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;; A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;; A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;; A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;; A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;; A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;; A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;; A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;; A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;; A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;; A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;; A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;; A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;; A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;; A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;; A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;; A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;; A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;; A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;; A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;; A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;; A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;; A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;; A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;; A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;; A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;; A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;; A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;; A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;; A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;; A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;; A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;; A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;; A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;; A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;; A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;; A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;; A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;; A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;; A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;; A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;; A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;; A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;; A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;; A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;; A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;; A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;; A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;; A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;; A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;; A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;; A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;; A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;; A491;YI RADICAL LI;So;0;ON;;;;;N;;;;; A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;; A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;; A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;; A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;; A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;; A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;; A498;YI RADICAL MI;So;0;ON;;;;;N;;;;; A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;; A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;; A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;; A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;; A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;; A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;; A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;; A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;; A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;; A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;; A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;; A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;; A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;; A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;; A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;; A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;; A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;; A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;; A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;; A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;; A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;; A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;; A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;; A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;; A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;; A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;; A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;; A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;; A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;; A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;; A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;; A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;; A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;; A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;; A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;; A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;; A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;; A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;; A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;; A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;; A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;; A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;; A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;; A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;; A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;; A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;; A4D0;LISU LETTER BA;Lo;0;L;;;;;N;;;;; A4D1;LISU LETTER PA;Lo;0;L;;;;;N;;;;; A4D2;LISU LETTER PHA;Lo;0;L;;;;;N;;;;; A4D3;LISU LETTER DA;Lo;0;L;;;;;N;;;;; A4D4;LISU LETTER TA;Lo;0;L;;;;;N;;;;; A4D5;LISU LETTER THA;Lo;0;L;;;;;N;;;;; A4D6;LISU LETTER GA;Lo;0;L;;;;;N;;;;; A4D7;LISU LETTER KA;Lo;0;L;;;;;N;;;;; A4D8;LISU LETTER KHA;Lo;0;L;;;;;N;;;;; A4D9;LISU LETTER JA;Lo;0;L;;;;;N;;;;; A4DA;LISU LETTER CA;Lo;0;L;;;;;N;;;;; A4DB;LISU LETTER CHA;Lo;0;L;;;;;N;;;;; A4DC;LISU LETTER DZA;Lo;0;L;;;;;N;;;;; A4DD;LISU LETTER TSA;Lo;0;L;;;;;N;;;;; A4DE;LISU LETTER TSHA;Lo;0;L;;;;;N;;;;; A4DF;LISU LETTER MA;Lo;0;L;;;;;N;;;;; A4E0;LISU LETTER NA;Lo;0;L;;;;;N;;;;; A4E1;LISU LETTER LA;Lo;0;L;;;;;N;;;;; A4E2;LISU LETTER SA;Lo;0;L;;;;;N;;;;; A4E3;LISU LETTER ZHA;Lo;0;L;;;;;N;;;;; A4E4;LISU LETTER ZA;Lo;0;L;;;;;N;;;;; A4E5;LISU LETTER NGA;Lo;0;L;;;;;N;;;;; A4E6;LISU LETTER HA;Lo;0;L;;;;;N;;;;; A4E7;LISU LETTER XA;Lo;0;L;;;;;N;;;;; A4E8;LISU LETTER HHA;Lo;0;L;;;;;N;;;;; A4E9;LISU LETTER FA;Lo;0;L;;;;;N;;;;; A4EA;LISU LETTER WA;Lo;0;L;;;;;N;;;;; A4EB;LISU LETTER SHA;Lo;0;L;;;;;N;;;;; A4EC;LISU LETTER YA;Lo;0;L;;;;;N;;;;; A4ED;LISU LETTER GHA;Lo;0;L;;;;;N;;;;; A4EE;LISU LETTER A;Lo;0;L;;;;;N;;;;; A4EF;LISU LETTER AE;Lo;0;L;;;;;N;;;;; A4F0;LISU LETTER E;Lo;0;L;;;;;N;;;;; A4F1;LISU LETTER EU;Lo;0;L;;;;;N;;;;; A4F2;LISU LETTER I;Lo;0;L;;;;;N;;;;; A4F3;LISU LETTER O;Lo;0;L;;;;;N;;;;; A4F4;LISU LETTER U;Lo;0;L;;;;;N;;;;; A4F5;LISU LETTER UE;Lo;0;L;;;;;N;;;;; A4F6;LISU LETTER UH;Lo;0;L;;;;;N;;;;; A4F7;LISU LETTER OE;Lo;0;L;;;;;N;;;;; A4F8;LISU LETTER TONE MYA TI;Lm;0;L;;;;;N;;;;; A4F9;LISU LETTER TONE NA PO;Lm;0;L;;;;;N;;;;; A4FA;LISU LETTER TONE MYA CYA;Lm;0;L;;;;;N;;;;; A4FB;LISU LETTER TONE MYA BO;Lm;0;L;;;;;N;;;;; A4FC;LISU LETTER TONE MYA NA;Lm;0;L;;;;;N;;;;; A4FD;LISU LETTER TONE MYA JEU;Lm;0;L;;;;;N;;;;; A4FE;LISU PUNCTUATION COMMA;Po;0;L;;;;;N;;;;; A4FF;LISU PUNCTUATION FULL STOP;Po;0;L;;;;;N;;;;; A500;VAI SYLLABLE EE;Lo;0;L;;;;;N;;;;; A501;VAI SYLLABLE EEN;Lo;0;L;;;;;N;;;;; A502;VAI SYLLABLE HEE;Lo;0;L;;;;;N;;;;; A503;VAI SYLLABLE WEE;Lo;0;L;;;;;N;;;;; A504;VAI SYLLABLE WEEN;Lo;0;L;;;;;N;;;;; A505;VAI SYLLABLE PEE;Lo;0;L;;;;;N;;;;; A506;VAI SYLLABLE BHEE;Lo;0;L;;;;;N;;;;; A507;VAI SYLLABLE BEE;Lo;0;L;;;;;N;;;;; A508;VAI SYLLABLE MBEE;Lo;0;L;;;;;N;;;;; A509;VAI SYLLABLE KPEE;Lo;0;L;;;;;N;;;;; A50A;VAI SYLLABLE MGBEE;Lo;0;L;;;;;N;;;;; A50B;VAI SYLLABLE GBEE;Lo;0;L;;;;;N;;;;; A50C;VAI SYLLABLE FEE;Lo;0;L;;;;;N;;;;; A50D;VAI SYLLABLE VEE;Lo;0;L;;;;;N;;;;; A50E;VAI SYLLABLE TEE;Lo;0;L;;;;;N;;;;; A50F;VAI SYLLABLE THEE;Lo;0;L;;;;;N;;;;; A510;VAI SYLLABLE DHEE;Lo;0;L;;;;;N;;;;; A511;VAI SYLLABLE DHHEE;Lo;0;L;;;;;N;;;;; A512;VAI SYLLABLE LEE;Lo;0;L;;;;;N;;;;; A513;VAI SYLLABLE REE;Lo;0;L;;;;;N;;;;; A514;VAI SYLLABLE DEE;Lo;0;L;;;;;N;;;;; A515;VAI SYLLABLE NDEE;Lo;0;L;;;;;N;;;;; A516;VAI SYLLABLE SEE;Lo;0;L;;;;;N;;;;; A517;VAI SYLLABLE SHEE;Lo;0;L;;;;;N;;;;; A518;VAI SYLLABLE ZEE;Lo;0;L;;;;;N;;;;; A519;VAI SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;; A51A;VAI SYLLABLE CEE;Lo;0;L;;;;;N;;;;; A51B;VAI SYLLABLE JEE;Lo;0;L;;;;;N;;;;; A51C;VAI SYLLABLE NJEE;Lo;0;L;;;;;N;;;;; A51D;VAI SYLLABLE YEE;Lo;0;L;;;;;N;;;;; A51E;VAI SYLLABLE KEE;Lo;0;L;;;;;N;;;;; A51F;VAI SYLLABLE NGGEE;Lo;0;L;;;;;N;;;;; A520;VAI SYLLABLE GEE;Lo;0;L;;;;;N;;;;; A521;VAI SYLLABLE MEE;Lo;0;L;;;;;N;;;;; A522;VAI SYLLABLE NEE;Lo;0;L;;;;;N;;;;; A523;VAI SYLLABLE NYEE;Lo;0;L;;;;;N;;;;; A524;VAI SYLLABLE I;Lo;0;L;;;;;N;;;;; A525;VAI SYLLABLE IN;Lo;0;L;;;;;N;;;;; A526;VAI SYLLABLE HI;Lo;0;L;;;;;N;;;;; A527;VAI SYLLABLE HIN;Lo;0;L;;;;;N;;;;; A528;VAI SYLLABLE WI;Lo;0;L;;;;;N;;;;; A529;VAI SYLLABLE WIN;Lo;0;L;;;;;N;;;;; A52A;VAI SYLLABLE PI;Lo;0;L;;;;;N;;;;; A52B;VAI SYLLABLE BHI;Lo;0;L;;;;;N;;;;; A52C;VAI SYLLABLE BI;Lo;0;L;;;;;N;;;;; A52D;VAI SYLLABLE MBI;Lo;0;L;;;;;N;;;;; A52E;VAI SYLLABLE KPI;Lo;0;L;;;;;N;;;;; A52F;VAI SYLLABLE MGBI;Lo;0;L;;;;;N;;;;; A530;VAI SYLLABLE GBI;Lo;0;L;;;;;N;;;;; A531;VAI SYLLABLE FI;Lo;0;L;;;;;N;;;;; A532;VAI SYLLABLE VI;Lo;0;L;;;;;N;;;;; A533;VAI SYLLABLE TI;Lo;0;L;;;;;N;;;;; A534;VAI SYLLABLE THI;Lo;0;L;;;;;N;;;;; A535;VAI SYLLABLE DHI;Lo;0;L;;;;;N;;;;; A536;VAI SYLLABLE DHHI;Lo;0;L;;;;;N;;;;; A537;VAI SYLLABLE LI;Lo;0;L;;;;;N;;;;; A538;VAI SYLLABLE RI;Lo;0;L;;;;;N;;;;; A539;VAI SYLLABLE DI;Lo;0;L;;;;;N;;;;; A53A;VAI SYLLABLE NDI;Lo;0;L;;;;;N;;;;; A53B;VAI SYLLABLE SI;Lo;0;L;;;;;N;;;;; A53C;VAI SYLLABLE SHI;Lo;0;L;;;;;N;;;;; A53D;VAI SYLLABLE ZI;Lo;0;L;;;;;N;;;;; A53E;VAI SYLLABLE ZHI;Lo;0;L;;;;;N;;;;; A53F;VAI SYLLABLE CI;Lo;0;L;;;;;N;;;;; A540;VAI SYLLABLE JI;Lo;0;L;;;;;N;;;;; A541;VAI SYLLABLE NJI;Lo;0;L;;;;;N;;;;; A542;VAI SYLLABLE YI;Lo;0;L;;;;;N;;;;; A543;VAI SYLLABLE KI;Lo;0;L;;;;;N;;;;; A544;VAI SYLLABLE NGGI;Lo;0;L;;;;;N;;;;; A545;VAI SYLLABLE GI;Lo;0;L;;;;;N;;;;; A546;VAI SYLLABLE MI;Lo;0;L;;;;;N;;;;; A547;VAI SYLLABLE NI;Lo;0;L;;;;;N;;;;; A548;VAI SYLLABLE NYI;Lo;0;L;;;;;N;;;;; A549;VAI SYLLABLE A;Lo;0;L;;;;;N;;;;; A54A;VAI SYLLABLE AN;Lo;0;L;;;;;N;;;;; A54B;VAI SYLLABLE NGAN;Lo;0;L;;;;;N;;;;; A54C;VAI SYLLABLE HA;Lo;0;L;;;;;N;;;;; A54D;VAI SYLLABLE HAN;Lo;0;L;;;;;N;;;;; A54E;VAI SYLLABLE WA;Lo;0;L;;;;;N;;;;; A54F;VAI SYLLABLE WAN;Lo;0;L;;;;;N;;;;; A550;VAI SYLLABLE PA;Lo;0;L;;;;;N;;;;; A551;VAI SYLLABLE BHA;Lo;0;L;;;;;N;;;;; A552;VAI SYLLABLE BA;Lo;0;L;;;;;N;;;;; A553;VAI SYLLABLE MBA;Lo;0;L;;;;;N;;;;; A554;VAI SYLLABLE KPA;Lo;0;L;;;;;N;;;;; A555;VAI SYLLABLE KPAN;Lo;0;L;;;;;N;;;;; A556;VAI SYLLABLE MGBA;Lo;0;L;;;;;N;;;;; A557;VAI SYLLABLE GBA;Lo;0;L;;;;;N;;;;; A558;VAI SYLLABLE FA;Lo;0;L;;;;;N;;;;; A559;VAI SYLLABLE VA;Lo;0;L;;;;;N;;;;; A55A;VAI SYLLABLE TA;Lo;0;L;;;;;N;;;;; A55B;VAI SYLLABLE THA;Lo;0;L;;;;;N;;;;; A55C;VAI SYLLABLE DHA;Lo;0;L;;;;;N;;;;; A55D;VAI SYLLABLE DHHA;Lo;0;L;;;;;N;;;;; A55E;VAI SYLLABLE LA;Lo;0;L;;;;;N;;;;; A55F;VAI SYLLABLE RA;Lo;0;L;;;;;N;;;;; A560;VAI SYLLABLE DA;Lo;0;L;;;;;N;;;;; A561;VAI SYLLABLE NDA;Lo;0;L;;;;;N;;;;; A562;VAI SYLLABLE SA;Lo;0;L;;;;;N;;;;; A563;VAI SYLLABLE SHA;Lo;0;L;;;;;N;;;;; A564;VAI SYLLABLE ZA;Lo;0;L;;;;;N;;;;; A565;VAI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; A566;VAI SYLLABLE CA;Lo;0;L;;;;;N;;;;; A567;VAI SYLLABLE JA;Lo;0;L;;;;;N;;;;; A568;VAI SYLLABLE NJA;Lo;0;L;;;;;N;;;;; A569;VAI SYLLABLE YA;Lo;0;L;;;;;N;;;;; A56A;VAI SYLLABLE KA;Lo;0;L;;;;;N;;;;; A56B;VAI SYLLABLE KAN;Lo;0;L;;;;;N;;;;; A56C;VAI SYLLABLE NGGA;Lo;0;L;;;;;N;;;;; A56D;VAI SYLLABLE GA;Lo;0;L;;;;;N;;;;; A56E;VAI SYLLABLE MA;Lo;0;L;;;;;N;;;;; A56F;VAI SYLLABLE NA;Lo;0;L;;;;;N;;;;; A570;VAI SYLLABLE NYA;Lo;0;L;;;;;N;;;;; A571;VAI SYLLABLE OO;Lo;0;L;;;;;N;;;;; A572;VAI SYLLABLE OON;Lo;0;L;;;;;N;;;;; A573;VAI SYLLABLE HOO;Lo;0;L;;;;;N;;;;; A574;VAI SYLLABLE WOO;Lo;0;L;;;;;N;;;;; A575;VAI SYLLABLE WOON;Lo;0;L;;;;;N;;;;; A576;VAI SYLLABLE POO;Lo;0;L;;;;;N;;;;; A577;VAI SYLLABLE BHOO;Lo;0;L;;;;;N;;;;; A578;VAI SYLLABLE BOO;Lo;0;L;;;;;N;;;;; A579;VAI SYLLABLE MBOO;Lo;0;L;;;;;N;;;;; A57A;VAI SYLLABLE KPOO;Lo;0;L;;;;;N;;;;; A57B;VAI SYLLABLE MGBOO;Lo;0;L;;;;;N;;;;; A57C;VAI SYLLABLE GBOO;Lo;0;L;;;;;N;;;;; A57D;VAI SYLLABLE FOO;Lo;0;L;;;;;N;;;;; A57E;VAI SYLLABLE VOO;Lo;0;L;;;;;N;;;;; A57F;VAI SYLLABLE TOO;Lo;0;L;;;;;N;;;;; A580;VAI SYLLABLE THOO;Lo;0;L;;;;;N;;;;; A581;VAI SYLLABLE DHOO;Lo;0;L;;;;;N;;;;; A582;VAI SYLLABLE DHHOO;Lo;0;L;;;;;N;;;;; A583;VAI SYLLABLE LOO;Lo;0;L;;;;;N;;;;; A584;VAI SYLLABLE ROO;Lo;0;L;;;;;N;;;;; A585;VAI SYLLABLE DOO;Lo;0;L;;;;;N;;;;; A586;VAI SYLLABLE NDOO;Lo;0;L;;;;;N;;;;; A587;VAI SYLLABLE SOO;Lo;0;L;;;;;N;;;;; A588;VAI SYLLABLE SHOO;Lo;0;L;;;;;N;;;;; A589;VAI SYLLABLE ZOO;Lo;0;L;;;;;N;;;;; A58A;VAI SYLLABLE ZHOO;Lo;0;L;;;;;N;;;;; A58B;VAI SYLLABLE COO;Lo;0;L;;;;;N;;;;; A58C;VAI SYLLABLE JOO;Lo;0;L;;;;;N;;;;; A58D;VAI SYLLABLE NJOO;Lo;0;L;;;;;N;;;;; A58E;VAI SYLLABLE YOO;Lo;0;L;;;;;N;;;;; A58F;VAI SYLLABLE KOO;Lo;0;L;;;;;N;;;;; A590;VAI SYLLABLE NGGOO;Lo;0;L;;;;;N;;;;; A591;VAI SYLLABLE GOO;Lo;0;L;;;;;N;;;;; A592;VAI SYLLABLE MOO;Lo;0;L;;;;;N;;;;; A593;VAI SYLLABLE NOO;Lo;0;L;;;;;N;;;;; A594;VAI SYLLABLE NYOO;Lo;0;L;;;;;N;;;;; A595;VAI SYLLABLE U;Lo;0;L;;;;;N;;;;; A596;VAI SYLLABLE UN;Lo;0;L;;;;;N;;;;; A597;VAI SYLLABLE HU;Lo;0;L;;;;;N;;;;; A598;VAI SYLLABLE HUN;Lo;0;L;;;;;N;;;;; A599;VAI SYLLABLE WU;Lo;0;L;;;;;N;;;;; A59A;VAI SYLLABLE WUN;Lo;0;L;;;;;N;;;;; A59B;VAI SYLLABLE PU;Lo;0;L;;;;;N;;;;; A59C;VAI SYLLABLE BHU;Lo;0;L;;;;;N;;;;; A59D;VAI SYLLABLE BU;Lo;0;L;;;;;N;;;;; A59E;VAI SYLLABLE MBU;Lo;0;L;;;;;N;;;;; A59F;VAI SYLLABLE KPU;Lo;0;L;;;;;N;;;;; A5A0;VAI SYLLABLE MGBU;Lo;0;L;;;;;N;;;;; A5A1;VAI SYLLABLE GBU;Lo;0;L;;;;;N;;;;; A5A2;VAI SYLLABLE FU;Lo;0;L;;;;;N;;;;; A5A3;VAI SYLLABLE VU;Lo;0;L;;;;;N;;;;; A5A4;VAI SYLLABLE TU;Lo;0;L;;;;;N;;;;; A5A5;VAI SYLLABLE THU;Lo;0;L;;;;;N;;;;; A5A6;VAI SYLLABLE DHU;Lo;0;L;;;;;N;;;;; A5A7;VAI SYLLABLE DHHU;Lo;0;L;;;;;N;;;;; A5A8;VAI SYLLABLE LU;Lo;0;L;;;;;N;;;;; A5A9;VAI SYLLABLE RU;Lo;0;L;;;;;N;;;;; A5AA;VAI SYLLABLE DU;Lo;0;L;;;;;N;;;;; A5AB;VAI SYLLABLE NDU;Lo;0;L;;;;;N;;;;; A5AC;VAI SYLLABLE SU;Lo;0;L;;;;;N;;;;; A5AD;VAI SYLLABLE SHU;Lo;0;L;;;;;N;;;;; A5AE;VAI SYLLABLE ZU;Lo;0;L;;;;;N;;;;; A5AF;VAI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; A5B0;VAI SYLLABLE CU;Lo;0;L;;;;;N;;;;; A5B1;VAI SYLLABLE JU;Lo;0;L;;;;;N;;;;; A5B2;VAI SYLLABLE NJU;Lo;0;L;;;;;N;;;;; A5B3;VAI SYLLABLE YU;Lo;0;L;;;;;N;;;;; A5B4;VAI SYLLABLE KU;Lo;0;L;;;;;N;;;;; A5B5;VAI SYLLABLE NGGU;Lo;0;L;;;;;N;;;;; A5B6;VAI SYLLABLE GU;Lo;0;L;;;;;N;;;;; A5B7;VAI SYLLABLE MU;Lo;0;L;;;;;N;;;;; A5B8;VAI SYLLABLE NU;Lo;0;L;;;;;N;;;;; A5B9;VAI SYLLABLE NYU;Lo;0;L;;;;;N;;;;; A5BA;VAI SYLLABLE O;Lo;0;L;;;;;N;;;;; A5BB;VAI SYLLABLE ON;Lo;0;L;;;;;N;;;;; A5BC;VAI SYLLABLE NGON;Lo;0;L;;;;;N;;;;; A5BD;VAI SYLLABLE HO;Lo;0;L;;;;;N;;;;; A5BE;VAI SYLLABLE HON;Lo;0;L;;;;;N;;;;; A5BF;VAI SYLLABLE WO;Lo;0;L;;;;;N;;;;; A5C0;VAI SYLLABLE WON;Lo;0;L;;;;;N;;;;; A5C1;VAI SYLLABLE PO;Lo;0;L;;;;;N;;;;; A5C2;VAI SYLLABLE BHO;Lo;0;L;;;;;N;;;;; A5C3;VAI SYLLABLE BO;Lo;0;L;;;;;N;;;;; A5C4;VAI SYLLABLE MBO;Lo;0;L;;;;;N;;;;; A5C5;VAI SYLLABLE KPO;Lo;0;L;;;;;N;;;;; A5C6;VAI SYLLABLE MGBO;Lo;0;L;;;;;N;;;;; A5C7;VAI SYLLABLE GBO;Lo;0;L;;;;;N;;;;; A5C8;VAI SYLLABLE GBON;Lo;0;L;;;;;N;;;;; A5C9;VAI SYLLABLE FO;Lo;0;L;;;;;N;;;;; A5CA;VAI SYLLABLE VO;Lo;0;L;;;;;N;;;;; A5CB;VAI SYLLABLE TO;Lo;0;L;;;;;N;;;;; A5CC;VAI SYLLABLE THO;Lo;0;L;;;;;N;;;;; A5CD;VAI SYLLABLE DHO;Lo;0;L;;;;;N;;;;; A5CE;VAI SYLLABLE DHHO;Lo;0;L;;;;;N;;;;; A5CF;VAI SYLLABLE LO;Lo;0;L;;;;;N;;;;; A5D0;VAI SYLLABLE RO;Lo;0;L;;;;;N;;;;; A5D1;VAI SYLLABLE DO;Lo;0;L;;;;;N;;;;; A5D2;VAI SYLLABLE NDO;Lo;0;L;;;;;N;;;;; A5D3;VAI SYLLABLE SO;Lo;0;L;;;;;N;;;;; A5D4;VAI SYLLABLE SHO;Lo;0;L;;;;;N;;;;; A5D5;VAI SYLLABLE ZO;Lo;0;L;;;;;N;;;;; A5D6;VAI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; A5D7;VAI SYLLABLE CO;Lo;0;L;;;;;N;;;;; A5D8;VAI SYLLABLE JO;Lo;0;L;;;;;N;;;;; A5D9;VAI SYLLABLE NJO;Lo;0;L;;;;;N;;;;; A5DA;VAI SYLLABLE YO;Lo;0;L;;;;;N;;;;; A5DB;VAI SYLLABLE KO;Lo;0;L;;;;;N;;;;; A5DC;VAI SYLLABLE NGGO;Lo;0;L;;;;;N;;;;; A5DD;VAI SYLLABLE GO;Lo;0;L;;;;;N;;;;; A5DE;VAI SYLLABLE MO;Lo;0;L;;;;;N;;;;; A5DF;VAI SYLLABLE NO;Lo;0;L;;;;;N;;;;; A5E0;VAI SYLLABLE NYO;Lo;0;L;;;;;N;;;;; A5E1;VAI SYLLABLE E;Lo;0;L;;;;;N;;;;; A5E2;VAI SYLLABLE EN;Lo;0;L;;;;;N;;;;; A5E3;VAI SYLLABLE NGEN;Lo;0;L;;;;;N;;;;; A5E4;VAI SYLLABLE HE;Lo;0;L;;;;;N;;;;; A5E5;VAI SYLLABLE HEN;Lo;0;L;;;;;N;;;;; A5E6;VAI SYLLABLE WE;Lo;0;L;;;;;N;;;;; A5E7;VAI SYLLABLE WEN;Lo;0;L;;;;;N;;;;; A5E8;VAI SYLLABLE PE;Lo;0;L;;;;;N;;;;; A5E9;VAI SYLLABLE BHE;Lo;0;L;;;;;N;;;;; A5EA;VAI SYLLABLE BE;Lo;0;L;;;;;N;;;;; A5EB;VAI SYLLABLE MBE;Lo;0;L;;;;;N;;;;; A5EC;VAI SYLLABLE KPE;Lo;0;L;;;;;N;;;;; A5ED;VAI SYLLABLE KPEN;Lo;0;L;;;;;N;;;;; A5EE;VAI SYLLABLE MGBE;Lo;0;L;;;;;N;;;;; A5EF;VAI SYLLABLE GBE;Lo;0;L;;;;;N;;;;; A5F0;VAI SYLLABLE GBEN;Lo;0;L;;;;;N;;;;; A5F1;VAI SYLLABLE FE;Lo;0;L;;;;;N;;;;; A5F2;VAI SYLLABLE VE;Lo;0;L;;;;;N;;;;; A5F3;VAI SYLLABLE TE;Lo;0;L;;;;;N;;;;; A5F4;VAI SYLLABLE THE;Lo;0;L;;;;;N;;;;; A5F5;VAI SYLLABLE DHE;Lo;0;L;;;;;N;;;;; A5F6;VAI SYLLABLE DHHE;Lo;0;L;;;;;N;;;;; A5F7;VAI SYLLABLE LE;Lo;0;L;;;;;N;;;;; A5F8;VAI SYLLABLE RE;Lo;0;L;;;;;N;;;;; A5F9;VAI SYLLABLE DE;Lo;0;L;;;;;N;;;;; A5FA;VAI SYLLABLE NDE;Lo;0;L;;;;;N;;;;; A5FB;VAI SYLLABLE SE;Lo;0;L;;;;;N;;;;; A5FC;VAI SYLLABLE SHE;Lo;0;L;;;;;N;;;;; A5FD;VAI SYLLABLE ZE;Lo;0;L;;;;;N;;;;; A5FE;VAI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; A5FF;VAI SYLLABLE CE;Lo;0;L;;;;;N;;;;; A600;VAI SYLLABLE JE;Lo;0;L;;;;;N;;;;; A601;VAI SYLLABLE NJE;Lo;0;L;;;;;N;;;;; A602;VAI SYLLABLE YE;Lo;0;L;;;;;N;;;;; A603;VAI SYLLABLE KE;Lo;0;L;;;;;N;;;;; A604;VAI SYLLABLE NGGE;Lo;0;L;;;;;N;;;;; A605;VAI SYLLABLE NGGEN;Lo;0;L;;;;;N;;;;; A606;VAI SYLLABLE GE;Lo;0;L;;;;;N;;;;; A607;VAI SYLLABLE GEN;Lo;0;L;;;;;N;;;;; A608;VAI SYLLABLE ME;Lo;0;L;;;;;N;;;;; A609;VAI SYLLABLE NE;Lo;0;L;;;;;N;;;;; A60A;VAI SYLLABLE NYE;Lo;0;L;;;;;N;;;;; A60B;VAI SYLLABLE NG;Lo;0;L;;;;;N;;;;; A60C;VAI SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;; A60D;VAI COMMA;Po;0;ON;;;;;N;;;;; A60E;VAI FULL STOP;Po;0;ON;;;;;N;;;;; A60F;VAI QUESTION MARK;Po;0;ON;;;;;N;;;;; A610;VAI SYLLABLE NDOLE FA;Lo;0;L;;;;;N;;;;; A611;VAI SYLLABLE NDOLE KA;Lo;0;L;;;;;N;;;;; A612;VAI SYLLABLE NDOLE SOO;Lo;0;L;;;;;N;;;;; A613;VAI SYMBOL FEENG;Lo;0;L;;;;;N;;;;; A614;VAI SYMBOL KEENG;Lo;0;L;;;;;N;;;;; A615;VAI SYMBOL TING;Lo;0;L;;;;;N;;;;; A616;VAI SYMBOL NII;Lo;0;L;;;;;N;;;;; A617;VAI SYMBOL BANG;Lo;0;L;;;;;N;;;;; A618;VAI SYMBOL FAA;Lo;0;L;;;;;N;;;;; A619;VAI SYMBOL TAA;Lo;0;L;;;;;N;;;;; A61A;VAI SYMBOL DANG;Lo;0;L;;;;;N;;;;; A61B;VAI SYMBOL DOONG;Lo;0;L;;;;;N;;;;; A61C;VAI SYMBOL KUNG;Lo;0;L;;;;;N;;;;; A61D;VAI SYMBOL TONG;Lo;0;L;;;;;N;;;;; A61E;VAI SYMBOL DO-O;Lo;0;L;;;;;N;;;;; A61F;VAI SYMBOL JONG;Lo;0;L;;;;;N;;;;; A620;VAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; A621;VAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; A622;VAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; A623;VAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; A624;VAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; A625;VAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; A626;VAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; A627;VAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; A628;VAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; A629;VAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; A62A;VAI SYLLABLE NDOLE MA;Lo;0;L;;;;;N;;;;; A62B;VAI SYLLABLE NDOLE DO;Lo;0;L;;;;;N;;;;; A640;CYRILLIC CAPITAL LETTER ZEMLYA;Lu;0;L;;;;;N;;;;A641; A641;CYRILLIC SMALL LETTER ZEMLYA;Ll;0;L;;;;;N;;;A640;;A640 A642;CYRILLIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;A643; A643;CYRILLIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;A642;;A642 A644;CYRILLIC CAPITAL LETTER REVERSED DZE;Lu;0;L;;;;;N;;;;A645; A645;CYRILLIC SMALL LETTER REVERSED DZE;Ll;0;L;;;;;N;;;A644;;A644 A646;CYRILLIC CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;A647; A647;CYRILLIC SMALL LETTER IOTA;Ll;0;L;;;;;N;;;A646;;A646 A648;CYRILLIC CAPITAL LETTER DJERV;Lu;0;L;;;;;N;;;;A649; A649;CYRILLIC SMALL LETTER DJERV;Ll;0;L;;;;;N;;;A648;;A648 A64A;CYRILLIC CAPITAL LETTER MONOGRAPH UK;Lu;0;L;;;;;N;;;;A64B; A64B;CYRILLIC SMALL LETTER MONOGRAPH UK;Ll;0;L;;;;;N;;;A64A;;A64A A64C;CYRILLIC CAPITAL LETTER BROAD OMEGA;Lu;0;L;;;;;N;;;;A64D; A64D;CYRILLIC SMALL LETTER BROAD OMEGA;Ll;0;L;;;;;N;;;A64C;;A64C A64E;CYRILLIC CAPITAL LETTER NEUTRAL YER;Lu;0;L;;;;;N;;;;A64F; A64F;CYRILLIC SMALL LETTER NEUTRAL YER;Ll;0;L;;;;;N;;;A64E;;A64E A650;CYRILLIC CAPITAL LETTER YERU WITH BACK YER;Lu;0;L;;;;;N;;;;A651; A651;CYRILLIC SMALL LETTER YERU WITH BACK YER;Ll;0;L;;;;;N;;;A650;;A650 A652;CYRILLIC CAPITAL LETTER IOTIFIED YAT;Lu;0;L;;;;;N;;;;A653; A653;CYRILLIC SMALL LETTER IOTIFIED YAT;Ll;0;L;;;;;N;;;A652;;A652 A654;CYRILLIC CAPITAL LETTER REVERSED YU;Lu;0;L;;;;;N;;;;A655; A655;CYRILLIC SMALL LETTER REVERSED YU;Ll;0;L;;;;;N;;;A654;;A654 A656;CYRILLIC CAPITAL LETTER IOTIFIED A;Lu;0;L;;;;;N;;;;A657; A657;CYRILLIC SMALL LETTER IOTIFIED A;Ll;0;L;;;;;N;;;A656;;A656 A658;CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A659; A659;CYRILLIC SMALL LETTER CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A658;;A658 A65A;CYRILLIC CAPITAL LETTER BLENDED YUS;Lu;0;L;;;;;N;;;;A65B; A65B;CYRILLIC SMALL LETTER BLENDED YUS;Ll;0;L;;;;;N;;;A65A;;A65A A65C;CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A65D; A65D;CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A65C;;A65C A65E;CYRILLIC CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;A65F; A65F;CYRILLIC SMALL LETTER YN;Ll;0;L;;;;;N;;;A65E;;A65E A660;CYRILLIC CAPITAL LETTER REVERSED TSE;Lu;0;L;;;;;N;;;;A661; A661;CYRILLIC SMALL LETTER REVERSED TSE;Ll;0;L;;;;;N;;;A660;;A660 A662;CYRILLIC CAPITAL LETTER SOFT DE;Lu;0;L;;;;;N;;;;A663; A663;CYRILLIC SMALL LETTER SOFT DE;Ll;0;L;;;;;N;;;A662;;A662 A664;CYRILLIC CAPITAL LETTER SOFT EL;Lu;0;L;;;;;N;;;;A665; A665;CYRILLIC SMALL LETTER SOFT EL;Ll;0;L;;;;;N;;;A664;;A664 A666;CYRILLIC CAPITAL LETTER SOFT EM;Lu;0;L;;;;;N;;;;A667; A667;CYRILLIC SMALL LETTER SOFT EM;Ll;0;L;;;;;N;;;A666;;A666 A668;CYRILLIC CAPITAL LETTER MONOCULAR O;Lu;0;L;;;;;N;;;;A669; A669;CYRILLIC SMALL LETTER MONOCULAR O;Ll;0;L;;;;;N;;;A668;;A668 A66A;CYRILLIC CAPITAL LETTER BINOCULAR O;Lu;0;L;;;;;N;;;;A66B; A66B;CYRILLIC SMALL LETTER BINOCULAR O;Ll;0;L;;;;;N;;;A66A;;A66A A66C;CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O;Lu;0;L;;;;;N;;;;A66D; A66D;CYRILLIC SMALL LETTER DOUBLE MONOCULAR O;Ll;0;L;;;;;N;;;A66C;;A66C A66E;CYRILLIC LETTER MULTIOCULAR O;Lo;0;L;;;;;N;;;;; A66F;COMBINING CYRILLIC VZMET;Mn;230;NSM;;;;;N;;;;; A670;COMBINING CYRILLIC TEN MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A671;COMBINING CYRILLIC HUNDRED MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A672;COMBINING CYRILLIC THOUSAND MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A673;SLAVONIC ASTERISK;Po;0;ON;;;;;N;;;;; A674;COMBINING CYRILLIC LETTER UKRAINIAN IE;Mn;230;NSM;;;;;N;;;;; A675;COMBINING CYRILLIC LETTER I;Mn;230;NSM;;;;;N;;;;; A676;COMBINING CYRILLIC LETTER YI;Mn;230;NSM;;;;;N;;;;; A677;COMBINING CYRILLIC LETTER U;Mn;230;NSM;;;;;N;;;;; A678;COMBINING CYRILLIC LETTER HARD SIGN;Mn;230;NSM;;;;;N;;;;; A679;COMBINING CYRILLIC LETTER YERU;Mn;230;NSM;;;;;N;;;;; A67A;COMBINING CYRILLIC LETTER SOFT SIGN;Mn;230;NSM;;;;;N;;;;; A67B;COMBINING CYRILLIC LETTER OMEGA;Mn;230;NSM;;;;;N;;;;; A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;; A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;; A67E;CYRILLIC KAVYKA;Po;0;ON;;;;;N;;;;; A67F;CYRILLIC PAYEROK;Lm;0;ON;;;;;N;;;;; A680;CYRILLIC CAPITAL LETTER DWE;Lu;0;L;;;;;N;;;;A681; A681;CYRILLIC SMALL LETTER DWE;Ll;0;L;;;;;N;;;A680;;A680 A682;CYRILLIC CAPITAL LETTER DZWE;Lu;0;L;;;;;N;;;;A683; A683;CYRILLIC SMALL LETTER DZWE;Ll;0;L;;;;;N;;;A682;;A682 A684;CYRILLIC CAPITAL LETTER ZHWE;Lu;0;L;;;;;N;;;;A685; A685;CYRILLIC SMALL LETTER ZHWE;Ll;0;L;;;;;N;;;A684;;A684 A686;CYRILLIC CAPITAL LETTER CCHE;Lu;0;L;;;;;N;;;;A687; A687;CYRILLIC SMALL LETTER CCHE;Ll;0;L;;;;;N;;;A686;;A686 A688;CYRILLIC CAPITAL LETTER DZZE;Lu;0;L;;;;;N;;;;A689; A689;CYRILLIC SMALL LETTER DZZE;Ll;0;L;;;;;N;;;A688;;A688 A68A;CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;A68B; A68B;CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;A68A;;A68A A68C;CYRILLIC CAPITAL LETTER TWE;Lu;0;L;;;;;N;;;;A68D; A68D;CYRILLIC SMALL LETTER TWE;Ll;0;L;;;;;N;;;A68C;;A68C A68E;CYRILLIC CAPITAL LETTER TSWE;Lu;0;L;;;;;N;;;;A68F; A68F;CYRILLIC SMALL LETTER TSWE;Ll;0;L;;;;;N;;;A68E;;A68E A690;CYRILLIC CAPITAL LETTER TSSE;Lu;0;L;;;;;N;;;;A691; A691;CYRILLIC SMALL LETTER TSSE;Ll;0;L;;;;;N;;;A690;;A690 A692;CYRILLIC CAPITAL LETTER TCHE;Lu;0;L;;;;;N;;;;A693; A693;CYRILLIC SMALL LETTER TCHE;Ll;0;L;;;;;N;;;A692;;A692 A694;CYRILLIC CAPITAL LETTER HWE;Lu;0;L;;;;;N;;;;A695; A695;CYRILLIC SMALL LETTER HWE;Ll;0;L;;;;;N;;;A694;;A694 A696;CYRILLIC CAPITAL LETTER SHWE;Lu;0;L;;;;;N;;;;A697; A697;CYRILLIC SMALL LETTER SHWE;Ll;0;L;;;;;N;;;A696;;A696 A698;CYRILLIC CAPITAL LETTER DOUBLE O;Lu;0;L;;;;;N;;;;A699; A699;CYRILLIC SMALL LETTER DOUBLE O;Ll;0;L;;;;;N;;;A698;;A698 A69A;CYRILLIC CAPITAL LETTER CROSSED O;Lu;0;L;;;;;N;;;;A69B; A69B;CYRILLIC SMALL LETTER CROSSED O;Ll;0;L;;;;;N;;;A69A;;A69A A69C;MODIFIER LETTER CYRILLIC HARD SIGN;Lm;0;L; 044A;;;;N;;;;; A69D;MODIFIER LETTER CYRILLIC SOFT SIGN;Lm;0;L; 044C;;;;N;;;;; A69E;COMBINING CYRILLIC LETTER EF;Mn;230;NSM;;;;;N;;;;; A69F;COMBINING CYRILLIC LETTER IOTIFIED E;Mn;230;NSM;;;;;N;;;;; A6A0;BAMUM LETTER A;Lo;0;L;;;;;N;;;;; A6A1;BAMUM LETTER KA;Lo;0;L;;;;;N;;;;; A6A2;BAMUM LETTER U;Lo;0;L;;;;;N;;;;; A6A3;BAMUM LETTER KU;Lo;0;L;;;;;N;;;;; A6A4;BAMUM LETTER EE;Lo;0;L;;;;;N;;;;; A6A5;BAMUM LETTER REE;Lo;0;L;;;;;N;;;;; A6A6;BAMUM LETTER TAE;Lo;0;L;;;;;N;;;;; A6A7;BAMUM LETTER O;Lo;0;L;;;;;N;;;;; A6A8;BAMUM LETTER NYI;Lo;0;L;;;;;N;;;;; A6A9;BAMUM LETTER I;Lo;0;L;;;;;N;;;;; A6AA;BAMUM LETTER LA;Lo;0;L;;;;;N;;;;; A6AB;BAMUM LETTER PA;Lo;0;L;;;;;N;;;;; A6AC;BAMUM LETTER RII;Lo;0;L;;;;;N;;;;; A6AD;BAMUM LETTER RIEE;Lo;0;L;;;;;N;;;;; A6AE;BAMUM LETTER LEEEE;Lo;0;L;;;;;N;;;;; A6AF;BAMUM LETTER MEEEE;Lo;0;L;;;;;N;;;;; A6B0;BAMUM LETTER TAA;Lo;0;L;;;;;N;;;;; A6B1;BAMUM LETTER NDAA;Lo;0;L;;;;;N;;;;; A6B2;BAMUM LETTER NJAEM;Lo;0;L;;;;;N;;;;; A6B3;BAMUM LETTER M;Lo;0;L;;;;;N;;;;; A6B4;BAMUM LETTER SUU;Lo;0;L;;;;;N;;;;; A6B5;BAMUM LETTER MU;Lo;0;L;;;;;N;;;;; A6B6;BAMUM LETTER SHII;Lo;0;L;;;;;N;;;;; A6B7;BAMUM LETTER SI;Lo;0;L;;;;;N;;;;; A6B8;BAMUM LETTER SHEUX;Lo;0;L;;;;;N;;;;; A6B9;BAMUM LETTER SEUX;Lo;0;L;;;;;N;;;;; A6BA;BAMUM LETTER KYEE;Lo;0;L;;;;;N;;;;; A6BB;BAMUM LETTER KET;Lo;0;L;;;;;N;;;;; A6BC;BAMUM LETTER NUAE;Lo;0;L;;;;;N;;;;; A6BD;BAMUM LETTER NU;Lo;0;L;;;;;N;;;;; A6BE;BAMUM LETTER NJUAE;Lo;0;L;;;;;N;;;;; A6BF;BAMUM LETTER YOQ;Lo;0;L;;;;;N;;;;; A6C0;BAMUM LETTER SHU;Lo;0;L;;;;;N;;;;; A6C1;BAMUM LETTER YUQ;Lo;0;L;;;;;N;;;;; A6C2;BAMUM LETTER YA;Lo;0;L;;;;;N;;;;; A6C3;BAMUM LETTER NSHA;Lo;0;L;;;;;N;;;;; A6C4;BAMUM LETTER KEUX;Lo;0;L;;;;;N;;;;; A6C5;BAMUM LETTER PEUX;Lo;0;L;;;;;N;;;;; A6C6;BAMUM LETTER NJEE;Lo;0;L;;;;;N;;;;; A6C7;BAMUM LETTER NTEE;Lo;0;L;;;;;N;;;;; A6C8;BAMUM LETTER PUE;Lo;0;L;;;;;N;;;;; A6C9;BAMUM LETTER WUE;Lo;0;L;;;;;N;;;;; A6CA;BAMUM LETTER PEE;Lo;0;L;;;;;N;;;;; A6CB;BAMUM LETTER FEE;Lo;0;L;;;;;N;;;;; A6CC;BAMUM LETTER RU;Lo;0;L;;;;;N;;;;; A6CD;BAMUM LETTER LU;Lo;0;L;;;;;N;;;;; A6CE;BAMUM LETTER MI;Lo;0;L;;;;;N;;;;; A6CF;BAMUM LETTER NI;Lo;0;L;;;;;N;;;;; A6D0;BAMUM LETTER REUX;Lo;0;L;;;;;N;;;;; A6D1;BAMUM LETTER RAE;Lo;0;L;;;;;N;;;;; A6D2;BAMUM LETTER KEN;Lo;0;L;;;;;N;;;;; A6D3;BAMUM LETTER NGKWAEN;Lo;0;L;;;;;N;;;;; A6D4;BAMUM LETTER NGGA;Lo;0;L;;;;;N;;;;; A6D5;BAMUM LETTER NGA;Lo;0;L;;;;;N;;;;; A6D6;BAMUM LETTER SHO;Lo;0;L;;;;;N;;;;; A6D7;BAMUM LETTER PUAE;Lo;0;L;;;;;N;;;;; A6D8;BAMUM LETTER FU;Lo;0;L;;;;;N;;;;; A6D9;BAMUM LETTER FOM;Lo;0;L;;;;;N;;;;; A6DA;BAMUM LETTER WA;Lo;0;L;;;;;N;;;;; A6DB;BAMUM LETTER NA;Lo;0;L;;;;;N;;;;; A6DC;BAMUM LETTER LI;Lo;0;L;;;;;N;;;;; A6DD;BAMUM LETTER PI;Lo;0;L;;;;;N;;;;; A6DE;BAMUM LETTER LOQ;Lo;0;L;;;;;N;;;;; A6DF;BAMUM LETTER KO;Lo;0;L;;;;;N;;;;; A6E0;BAMUM LETTER MBEN;Lo;0;L;;;;;N;;;;; A6E1;BAMUM LETTER REN;Lo;0;L;;;;;N;;;;; A6E2;BAMUM LETTER MEN;Lo;0;L;;;;;N;;;;; A6E3;BAMUM LETTER MA;Lo;0;L;;;;;N;;;;; A6E4;BAMUM LETTER TI;Lo;0;L;;;;;N;;;;; A6E5;BAMUM LETTER KI;Lo;0;L;;;;;N;;;;; A6E6;BAMUM LETTER MO;Nl;0;L;;;;1;N;;;;; A6E7;BAMUM LETTER MBAA;Nl;0;L;;;;2;N;;;;; A6E8;BAMUM LETTER TET;Nl;0;L;;;;3;N;;;;; A6E9;BAMUM LETTER KPA;Nl;0;L;;;;4;N;;;;; A6EA;BAMUM LETTER TEN;Nl;0;L;;;;5;N;;;;; A6EB;BAMUM LETTER NTUU;Nl;0;L;;;;6;N;;;;; A6EC;BAMUM LETTER SAMBA;Nl;0;L;;;;7;N;;;;; A6ED;BAMUM LETTER FAAMAE;Nl;0;L;;;;8;N;;;;; A6EE;BAMUM LETTER KOVUU;Nl;0;L;;;;9;N;;;;; A6EF;BAMUM LETTER KOGHOM;Nl;0;L;;;;0;N;;;;; A6F0;BAMUM COMBINING MARK KOQNDON;Mn;230;NSM;;;;;N;;;;; A6F1;BAMUM COMBINING MARK TUKWENTIS;Mn;230;NSM;;;;;N;;;;; A6F2;BAMUM NJAEMLI;Po;0;L;;;;;N;;;;; A6F3;BAMUM FULL STOP;Po;0;L;;;;;N;;;;; A6F4;BAMUM COLON;Po;0;L;;;;;N;;;;; A6F5;BAMUM COMMA;Po;0;L;;;;;N;;;;; A6F6;BAMUM SEMICOLON;Po;0;L;;;;;N;;;;; A6F7;BAMUM QUESTION MARK;Po;0;L;;;;;N;;;;; A700;MODIFIER LETTER CHINESE TONE YIN PING;Sk;0;ON;;;;;N;;;;; A701;MODIFIER LETTER CHINESE TONE YANG PING;Sk;0;ON;;;;;N;;;;; A702;MODIFIER LETTER CHINESE TONE YIN SHANG;Sk;0;ON;;;;;N;;;;; A703;MODIFIER LETTER CHINESE TONE YANG SHANG;Sk;0;ON;;;;;N;;;;; A704;MODIFIER LETTER CHINESE TONE YIN QU;Sk;0;ON;;;;;N;;;;; A705;MODIFIER LETTER CHINESE TONE YANG QU;Sk;0;ON;;;;;N;;;;; A706;MODIFIER LETTER CHINESE TONE YIN RU;Sk;0;ON;;;;;N;;;;; A707;MODIFIER LETTER CHINESE TONE YANG RU;Sk;0;ON;;;;;N;;;;; A708;MODIFIER LETTER EXTRA-HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; A709;MODIFIER LETTER HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; A70A;MODIFIER LETTER MID DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; A70B;MODIFIER LETTER LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; A70C;MODIFIER LETTER EXTRA-LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; A70D;MODIFIER LETTER EXTRA-HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A70E;MODIFIER LETTER HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A70F;MODIFIER LETTER MID DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A710;MODIFIER LETTER LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A711;MODIFIER LETTER EXTRA-LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A712;MODIFIER LETTER EXTRA-HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A713;MODIFIER LETTER HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A714;MODIFIER LETTER MID LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A715;MODIFIER LETTER LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A716;MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; A717;MODIFIER LETTER DOT VERTICAL BAR;Lm;0;ON;;;;;N;;;;; A718;MODIFIER LETTER DOT SLASH;Lm;0;ON;;;;;N;;;;; A719;MODIFIER LETTER DOT HORIZONTAL BAR;Lm;0;ON;;;;;N;;;;; A71A;MODIFIER LETTER LOWER RIGHT CORNER ANGLE;Lm;0;ON;;;;;N;;;;; A71B;MODIFIER LETTER RAISED UP ARROW;Lm;0;ON;;;;;N;;;;; A71C;MODIFIER LETTER RAISED DOWN ARROW;Lm;0;ON;;;;;N;;;;; A71D;MODIFIER LETTER RAISED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; A71E;MODIFIER LETTER RAISED INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; A71F;MODIFIER LETTER LOW INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; A720;MODIFIER LETTER STRESS AND HIGH TONE;Sk;0;ON;;;;;N;;;;; A721;MODIFIER LETTER STRESS AND LOW TONE;Sk;0;ON;;;;;N;;;;; A722;LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF;Lu;0;L;;;;;N;;;;A723; A723;LATIN SMALL LETTER EGYPTOLOGICAL ALEF;Ll;0;L;;;;;N;;;A722;;A722 A724;LATIN CAPITAL LETTER EGYPTOLOGICAL AIN;Lu;0;L;;;;;N;;;;A725; A725;LATIN SMALL LETTER EGYPTOLOGICAL AIN;Ll;0;L;;;;;N;;;A724;;A724 A726;LATIN CAPITAL LETTER HENG;Lu;0;L;;;;;N;;;;A727; A727;LATIN SMALL LETTER HENG;Ll;0;L;;;;;N;;;A726;;A726 A728;LATIN CAPITAL LETTER TZ;Lu;0;L;;;;;N;;;;A729; A729;LATIN SMALL LETTER TZ;Ll;0;L;;;;;N;;;A728;;A728 A72A;LATIN CAPITAL LETTER TRESILLO;Lu;0;L;;;;;N;;;;A72B; A72B;LATIN SMALL LETTER TRESILLO;Ll;0;L;;;;;N;;;A72A;;A72A A72C;LATIN CAPITAL LETTER CUATRILLO;Lu;0;L;;;;;N;;;;A72D; A72D;LATIN SMALL LETTER CUATRILLO;Ll;0;L;;;;;N;;;A72C;;A72C A72E;LATIN CAPITAL LETTER CUATRILLO WITH COMMA;Lu;0;L;;;;;N;;;;A72F; A72F;LATIN SMALL LETTER CUATRILLO WITH COMMA;Ll;0;L;;;;;N;;;A72E;;A72E A730;LATIN LETTER SMALL CAPITAL F;Ll;0;L;;;;;N;;;;; A731;LATIN LETTER SMALL CAPITAL S;Ll;0;L;;;;;N;;;;; A732;LATIN CAPITAL LETTER AA;Lu;0;L;;;;;N;;;;A733; A733;LATIN SMALL LETTER AA;Ll;0;L;;;;;N;;;A732;;A732 A734;LATIN CAPITAL LETTER AO;Lu;0;L;;;;;N;;;;A735; A735;LATIN SMALL LETTER AO;Ll;0;L;;;;;N;;;A734;;A734 A736;LATIN CAPITAL LETTER AU;Lu;0;L;;;;;N;;;;A737; A737;LATIN SMALL LETTER AU;Ll;0;L;;;;;N;;;A736;;A736 A738;LATIN CAPITAL LETTER AV;Lu;0;L;;;;;N;;;;A739; A739;LATIN SMALL LETTER AV;Ll;0;L;;;;;N;;;A738;;A738 A73A;LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR;Lu;0;L;;;;;N;;;;A73B; A73B;LATIN SMALL LETTER AV WITH HORIZONTAL BAR;Ll;0;L;;;;;N;;;A73A;;A73A A73C;LATIN CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;A73D; A73D;LATIN SMALL LETTER AY;Ll;0;L;;;;;N;;;A73C;;A73C A73E;LATIN CAPITAL LETTER REVERSED C WITH DOT;Lu;0;L;;;;;N;;;;A73F; A73F;LATIN SMALL LETTER REVERSED C WITH DOT;Ll;0;L;;;;;N;;;A73E;;A73E A740;LATIN CAPITAL LETTER K WITH STROKE;Lu;0;L;;;;;N;;;;A741; A741;LATIN SMALL LETTER K WITH STROKE;Ll;0;L;;;;;N;;;A740;;A740 A742;LATIN CAPITAL LETTER K WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A743; A743;LATIN SMALL LETTER K WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A742;;A742 A744;LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A745; A745;LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE;Ll;0;L;;;;;N;;;A744;;A744 A746;LATIN CAPITAL LETTER BROKEN L;Lu;0;L;;;;;N;;;;A747; A747;LATIN SMALL LETTER BROKEN L;Ll;0;L;;;;;N;;;A746;;A746 A748;LATIN CAPITAL LETTER L WITH HIGH STROKE;Lu;0;L;;;;;N;;;;A749; A749;LATIN SMALL LETTER L WITH HIGH STROKE;Ll;0;L;;;;;N;;;A748;;A748 A74A;LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY;Lu;0;L;;;;;N;;;;A74B; A74B;LATIN SMALL LETTER O WITH LONG STROKE OVERLAY;Ll;0;L;;;;;N;;;A74A;;A74A A74C;LATIN CAPITAL LETTER O WITH LOOP;Lu;0;L;;;;;N;;;;A74D; A74D;LATIN SMALL LETTER O WITH LOOP;Ll;0;L;;;;;N;;;A74C;;A74C A74E;LATIN CAPITAL LETTER OO;Lu;0;L;;;;;N;;;;A74F; A74F;LATIN SMALL LETTER OO;Ll;0;L;;;;;N;;;A74E;;A74E A750;LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A751; A751;LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A750;;A750 A752;LATIN CAPITAL LETTER P WITH FLOURISH;Lu;0;L;;;;;N;;;;A753; A753;LATIN SMALL LETTER P WITH FLOURISH;Ll;0;L;;;;;N;;;A752;;A752 A754;LATIN CAPITAL LETTER P WITH SQUIRREL TAIL;Lu;0;L;;;;;N;;;;A755; A755;LATIN SMALL LETTER P WITH SQUIRREL TAIL;Ll;0;L;;;;;N;;;A754;;A754 A756;LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A757; A757;LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A756;;A756 A758;LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A759; A759;LATIN SMALL LETTER Q WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A758;;A758 A75A;LATIN CAPITAL LETTER R ROTUNDA;Lu;0;L;;;;;N;;;;A75B; A75B;LATIN SMALL LETTER R ROTUNDA;Ll;0;L;;;;;N;;;A75A;;A75A A75C;LATIN CAPITAL LETTER RUM ROTUNDA;Lu;0;L;;;;;N;;;;A75D; A75D;LATIN SMALL LETTER RUM ROTUNDA;Ll;0;L;;;;;N;;;A75C;;A75C A75E;LATIN CAPITAL LETTER V WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A75F; A75F;LATIN SMALL LETTER V WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A75E;;A75E A760;LATIN CAPITAL LETTER VY;Lu;0;L;;;;;N;;;;A761; A761;LATIN SMALL LETTER VY;Ll;0;L;;;;;N;;;A760;;A760 A762;LATIN CAPITAL LETTER VISIGOTHIC Z;Lu;0;L;;;;;N;;;;A763; A763;LATIN SMALL LETTER VISIGOTHIC Z;Ll;0;L;;;;;N;;;A762;;A762 A764;LATIN CAPITAL LETTER THORN WITH STROKE;Lu;0;L;;;;;N;;;;A765; A765;LATIN SMALL LETTER THORN WITH STROKE;Ll;0;L;;;;;N;;;A764;;A764 A766;LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A767; A767;LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A766;;A766 A768;LATIN CAPITAL LETTER VEND;Lu;0;L;;;;;N;;;;A769; A769;LATIN SMALL LETTER VEND;Ll;0;L;;;;;N;;;A768;;A768 A76A;LATIN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;A76B; A76B;LATIN SMALL LETTER ET;Ll;0;L;;;;;N;;;A76A;;A76A A76C;LATIN CAPITAL LETTER IS;Lu;0;L;;;;;N;;;;A76D; A76D;LATIN SMALL LETTER IS;Ll;0;L;;;;;N;;;A76C;;A76C A76E;LATIN CAPITAL LETTER CON;Lu;0;L;;;;;N;;;;A76F; A76F;LATIN SMALL LETTER CON;Ll;0;L;;;;;N;;;A76E;;A76E A770;MODIFIER LETTER US;Lm;0;L; A76F;;;;N;;;;; A771;LATIN SMALL LETTER DUM;Ll;0;L;;;;;N;;;;; A772;LATIN SMALL LETTER LUM;Ll;0;L;;;;;N;;;;; A773;LATIN SMALL LETTER MUM;Ll;0;L;;;;;N;;;;; A774;LATIN SMALL LETTER NUM;Ll;0;L;;;;;N;;;;; A775;LATIN SMALL LETTER RUM;Ll;0;L;;;;;N;;;;; A776;LATIN LETTER SMALL CAPITAL RUM;Ll;0;L;;;;;N;;;;; A777;LATIN SMALL LETTER TUM;Ll;0;L;;;;;N;;;;; A778;LATIN SMALL LETTER UM;Ll;0;L;;;;;N;;;;; A779;LATIN CAPITAL LETTER INSULAR D;Lu;0;L;;;;;N;;;;A77A; A77A;LATIN SMALL LETTER INSULAR D;Ll;0;L;;;;;N;;;A779;;A779 A77B;LATIN CAPITAL LETTER INSULAR F;Lu;0;L;;;;;N;;;;A77C; A77C;LATIN SMALL LETTER INSULAR F;Ll;0;L;;;;;N;;;A77B;;A77B A77D;LATIN CAPITAL LETTER INSULAR G;Lu;0;L;;;;;N;;;;1D79; A77E;LATIN CAPITAL LETTER TURNED INSULAR G;Lu;0;L;;;;;N;;;;A77F; A77F;LATIN SMALL LETTER TURNED INSULAR G;Ll;0;L;;;;;N;;;A77E;;A77E A780;LATIN CAPITAL LETTER TURNED L;Lu;0;L;;;;;N;;;;A781; A781;LATIN SMALL LETTER TURNED L;Ll;0;L;;;;;N;;;A780;;A780 A782;LATIN CAPITAL LETTER INSULAR R;Lu;0;L;;;;;N;;;;A783; A783;LATIN SMALL LETTER INSULAR R;Ll;0;L;;;;;N;;;A782;;A782 A784;LATIN CAPITAL LETTER INSULAR S;Lu;0;L;;;;;N;;;;A785; A785;LATIN SMALL LETTER INSULAR S;Ll;0;L;;;;;N;;;A784;;A784 A786;LATIN CAPITAL LETTER INSULAR T;Lu;0;L;;;;;N;;;;A787; A787;LATIN SMALL LETTER INSULAR T;Ll;0;L;;;;;N;;;A786;;A786 A788;MODIFIER LETTER LOW CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;;;;; A789;MODIFIER LETTER COLON;Sk;0;L;;;;;N;;;;; A78A;MODIFIER LETTER SHORT EQUALS SIGN;Sk;0;L;;;;;N;;;;; A78B;LATIN CAPITAL LETTER SALTILLO;Lu;0;L;;;;;N;;;;A78C; A78C;LATIN SMALL LETTER SALTILLO;Ll;0;L;;;;;N;;;A78B;;A78B A78D;LATIN CAPITAL LETTER TURNED H;Lu;0;L;;;;;N;;;;0265; A78E;LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT;Ll;0;L;;;;;N;;;;; A78F;LATIN LETTER SINOLOGICAL DOT;Lo;0;L;;;;;N;;;;; A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791; A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790 A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793; A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792 A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797; A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796 A798;LATIN CAPITAL LETTER F WITH STROKE;Lu;0;L;;;;;N;;;;A799; A799;LATIN SMALL LETTER F WITH STROKE;Ll;0;L;;;;;N;;;A798;;A798 A79A;LATIN CAPITAL LETTER VOLAPUK AE;Lu;0;L;;;;;N;;;;A79B; A79B;LATIN SMALL LETTER VOLAPUK AE;Ll;0;L;;;;;N;;;A79A;;A79A A79C;LATIN CAPITAL LETTER VOLAPUK OE;Lu;0;L;;;;;N;;;;A79D; A79D;LATIN SMALL LETTER VOLAPUK OE;Ll;0;L;;;;;N;;;A79C;;A79C A79E;LATIN CAPITAL LETTER VOLAPUK UE;Lu;0;L;;;;;N;;;;A79F; A79F;LATIN SMALL LETTER VOLAPUK UE;Ll;0;L;;;;;N;;;A79E;;A79E A7A0;LATIN CAPITAL LETTER G WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A1; A7A1;LATIN SMALL LETTER G WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A0;;A7A0 A7A2;LATIN CAPITAL LETTER K WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A3; A7A3;LATIN SMALL LETTER K WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A2;;A7A2 A7A4;LATIN CAPITAL LETTER N WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A5; A7A5;LATIN SMALL LETTER N WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A4;;A7A4 A7A6;LATIN CAPITAL LETTER R WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A7; A7A7;LATIN SMALL LETTER R WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A6;;A7A6 A7A8;LATIN CAPITAL LETTER S WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A9; A7A9;LATIN SMALL LETTER S WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A8;;A7A8 A7AA;LATIN CAPITAL LETTER H WITH HOOK;Lu;0;L;;;;;N;;;;0266; A7AB;LATIN CAPITAL LETTER REVERSED OPEN E;Lu;0;L;;;;;N;;;;025C; A7AC;LATIN CAPITAL LETTER SCRIPT G;Lu;0;L;;;;;N;;;;0261; A7AD;LATIN CAPITAL LETTER L WITH BELT;Lu;0;L;;;;;N;;;;026C; A7AE;LATIN CAPITAL LETTER SMALL CAPITAL I;Lu;0;L;;;;;N;;;;026A; A7B0;LATIN CAPITAL LETTER TURNED K;Lu;0;L;;;;;N;;;;029E; A7B1;LATIN CAPITAL LETTER TURNED T;Lu;0;L;;;;;N;;;;0287; A7B2;LATIN CAPITAL LETTER J WITH CROSSED-TAIL;Lu;0;L;;;;;N;;;;029D; A7B3;LATIN CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;AB53; A7B4;LATIN CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;A7B5; A7B5;LATIN SMALL LETTER BETA;Ll;0;L;;;;;N;;;A7B4;;A7B4 A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7; A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6 A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;; A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L; 0126;;;;N;;;;; A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L; 0153;;;;N;;;;; A7FA;LATIN LETTER SMALL CAPITAL TURNED M;Ll;0;L;;;;;N;;;;; A7FB;LATIN EPIGRAPHIC LETTER REVERSED F;Lo;0;L;;;;;N;;;;; A7FC;LATIN EPIGRAPHIC LETTER REVERSED P;Lo;0;L;;;;;N;;;;; A7FD;LATIN EPIGRAPHIC LETTER INVERTED M;Lo;0;L;;;;;N;;;;; A7FE;LATIN EPIGRAPHIC LETTER I LONGA;Lo;0;L;;;;;N;;;;; A7FF;LATIN EPIGRAPHIC LETTER ARCHAIC M;Lo;0;L;;;;;N;;;;; A800;SYLOTI NAGRI LETTER A;Lo;0;L;;;;;N;;;;; A801;SYLOTI NAGRI LETTER I;Lo;0;L;;;;;N;;;;; A802;SYLOTI NAGRI SIGN DVISVARA;Mn;0;NSM;;;;;N;;;;; A803;SYLOTI NAGRI LETTER U;Lo;0;L;;;;;N;;;;; A804;SYLOTI NAGRI LETTER E;Lo;0;L;;;;;N;;;;; A805;SYLOTI NAGRI LETTER O;Lo;0;L;;;;;N;;;;; A806;SYLOTI NAGRI SIGN HASANTA;Mn;9;NSM;;;;;N;;;;; A807;SYLOTI NAGRI LETTER KO;Lo;0;L;;;;;N;;;;; A808;SYLOTI NAGRI LETTER KHO;Lo;0;L;;;;;N;;;;; A809;SYLOTI NAGRI LETTER GO;Lo;0;L;;;;;N;;;;; A80A;SYLOTI NAGRI LETTER GHO;Lo;0;L;;;;;N;;;;; A80B;SYLOTI NAGRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; A80C;SYLOTI NAGRI LETTER CO;Lo;0;L;;;;;N;;;;; A80D;SYLOTI NAGRI LETTER CHO;Lo;0;L;;;;;N;;;;; A80E;SYLOTI NAGRI LETTER JO;Lo;0;L;;;;;N;;;;; A80F;SYLOTI NAGRI LETTER JHO;Lo;0;L;;;;;N;;;;; A810;SYLOTI NAGRI LETTER TTO;Lo;0;L;;;;;N;;;;; A811;SYLOTI NAGRI LETTER TTHO;Lo;0;L;;;;;N;;;;; A812;SYLOTI NAGRI LETTER DDO;Lo;0;L;;;;;N;;;;; A813;SYLOTI NAGRI LETTER DDHO;Lo;0;L;;;;;N;;;;; A814;SYLOTI NAGRI LETTER TO;Lo;0;L;;;;;N;;;;; A815;SYLOTI NAGRI LETTER THO;Lo;0;L;;;;;N;;;;; A816;SYLOTI NAGRI LETTER DO;Lo;0;L;;;;;N;;;;; A817;SYLOTI NAGRI LETTER DHO;Lo;0;L;;;;;N;;;;; A818;SYLOTI NAGRI LETTER NO;Lo;0;L;;;;;N;;;;; A819;SYLOTI NAGRI LETTER PO;Lo;0;L;;;;;N;;;;; A81A;SYLOTI NAGRI LETTER PHO;Lo;0;L;;;;;N;;;;; A81B;SYLOTI NAGRI LETTER BO;Lo;0;L;;;;;N;;;;; A81C;SYLOTI NAGRI LETTER BHO;Lo;0;L;;;;;N;;;;; A81D;SYLOTI NAGRI LETTER MO;Lo;0;L;;;;;N;;;;; A81E;SYLOTI NAGRI LETTER RO;Lo;0;L;;;;;N;;;;; A81F;SYLOTI NAGRI LETTER LO;Lo;0;L;;;;;N;;;;; A820;SYLOTI NAGRI LETTER RRO;Lo;0;L;;;;;N;;;;; A821;SYLOTI NAGRI LETTER SO;Lo;0;L;;;;;N;;;;; A822;SYLOTI NAGRI LETTER HO;Lo;0;L;;;;;N;;;;; A823;SYLOTI NAGRI VOWEL SIGN A;Mc;0;L;;;;;N;;;;; A824;SYLOTI NAGRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; A825;SYLOTI NAGRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; A826;SYLOTI NAGRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; A827;SYLOTI NAGRI VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; A828;SYLOTI NAGRI POETRY MARK-1;So;0;ON;;;;;N;;;;; A829;SYLOTI NAGRI POETRY MARK-2;So;0;ON;;;;;N;;;;; A82A;SYLOTI NAGRI POETRY MARK-3;So;0;ON;;;;;N;;;;; A82B;SYLOTI NAGRI POETRY MARK-4;So;0;ON;;;;;N;;;;; A830;NORTH INDIC FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; A831;NORTH INDIC FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; A832;NORTH INDIC FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; A833;NORTH INDIC FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; A834;NORTH INDIC FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; A835;NORTH INDIC FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; A836;NORTH INDIC QUARTER MARK;So;0;L;;;;;N;;;;; A837;NORTH INDIC PLACEHOLDER MARK;So;0;L;;;;;N;;;;; A838;NORTH INDIC RUPEE MARK;Sc;0;ET;;;;;N;;;;; A839;NORTH INDIC QUANTITY MARK;So;0;ET;;;;;N;;;;; A840;PHAGS-PA LETTER KA;Lo;0;L;;;;;N;;;;; A841;PHAGS-PA LETTER KHA;Lo;0;L;;;;;N;;;;; A842;PHAGS-PA LETTER GA;Lo;0;L;;;;;N;;;;; A843;PHAGS-PA LETTER NGA;Lo;0;L;;;;;N;;;;; A844;PHAGS-PA LETTER CA;Lo;0;L;;;;;N;;;;; A845;PHAGS-PA LETTER CHA;Lo;0;L;;;;;N;;;;; A846;PHAGS-PA LETTER JA;Lo;0;L;;;;;N;;;;; A847;PHAGS-PA LETTER NYA;Lo;0;L;;;;;N;;;;; A848;PHAGS-PA LETTER TA;Lo;0;L;;;;;N;;;;; A849;PHAGS-PA LETTER THA;Lo;0;L;;;;;N;;;;; A84A;PHAGS-PA LETTER DA;Lo;0;L;;;;;N;;;;; A84B;PHAGS-PA LETTER NA;Lo;0;L;;;;;N;;;;; A84C;PHAGS-PA LETTER PA;Lo;0;L;;;;;N;;;;; A84D;PHAGS-PA LETTER PHA;Lo;0;L;;;;;N;;;;; A84E;PHAGS-PA LETTER BA;Lo;0;L;;;;;N;;;;; A84F;PHAGS-PA LETTER MA;Lo;0;L;;;;;N;;;;; A850;PHAGS-PA LETTER TSA;Lo;0;L;;;;;N;;;;; A851;PHAGS-PA LETTER TSHA;Lo;0;L;;;;;N;;;;; A852;PHAGS-PA LETTER DZA;Lo;0;L;;;;;N;;;;; A853;PHAGS-PA LETTER WA;Lo;0;L;;;;;N;;;;; A854;PHAGS-PA LETTER ZHA;Lo;0;L;;;;;N;;;;; A855;PHAGS-PA LETTER ZA;Lo;0;L;;;;;N;;;;; A856;PHAGS-PA LETTER SMALL A;Lo;0;L;;;;;N;;;;; A857;PHAGS-PA LETTER YA;Lo;0;L;;;;;N;;;;; A858;PHAGS-PA LETTER RA;Lo;0;L;;;;;N;;;;; A859;PHAGS-PA LETTER LA;Lo;0;L;;;;;N;;;;; A85A;PHAGS-PA LETTER SHA;Lo;0;L;;;;;N;;;;; A85B;PHAGS-PA LETTER SA;Lo;0;L;;;;;N;;;;; A85C;PHAGS-PA LETTER HA;Lo;0;L;;;;;N;;;;; A85D;PHAGS-PA LETTER A;Lo;0;L;;;;;N;;;;; A85E;PHAGS-PA LETTER I;Lo;0;L;;;;;N;;;;; A85F;PHAGS-PA LETTER U;Lo;0;L;;;;;N;;;;; A860;PHAGS-PA LETTER E;Lo;0;L;;;;;N;;;;; A861;PHAGS-PA LETTER O;Lo;0;L;;;;;N;;;;; A862;PHAGS-PA LETTER QA;Lo;0;L;;;;;N;;;;; A863;PHAGS-PA LETTER XA;Lo;0;L;;;;;N;;;;; A864;PHAGS-PA LETTER FA;Lo;0;L;;;;;N;;;;; A865;PHAGS-PA LETTER GGA;Lo;0;L;;;;;N;;;;; A866;PHAGS-PA LETTER EE;Lo;0;L;;;;;N;;;;; A867;PHAGS-PA SUBJOINED LETTER WA;Lo;0;L;;;;;N;;;;; A868;PHAGS-PA SUBJOINED LETTER YA;Lo;0;L;;;;;N;;;;; A869;PHAGS-PA LETTER TTA;Lo;0;L;;;;;N;;;;; A86A;PHAGS-PA LETTER TTHA;Lo;0;L;;;;;N;;;;; A86B;PHAGS-PA LETTER DDA;Lo;0;L;;;;;N;;;;; A86C;PHAGS-PA LETTER NNA;Lo;0;L;;;;;N;;;;; A86D;PHAGS-PA LETTER ALTERNATE YA;Lo;0;L;;;;;N;;;;; A86E;PHAGS-PA LETTER VOICELESS SHA;Lo;0;L;;;;;N;;;;; A86F;PHAGS-PA LETTER VOICED HA;Lo;0;L;;;;;N;;;;; A870;PHAGS-PA LETTER ASPIRATED FA;Lo;0;L;;;;;N;;;;; A871;PHAGS-PA SUBJOINED LETTER RA;Lo;0;L;;;;;N;;;;; A872;PHAGS-PA SUPERFIXED LETTER RA;Lo;0;L;;;;;N;;;;; A873;PHAGS-PA LETTER CANDRABINDU;Lo;0;L;;;;;N;;;;; A874;PHAGS-PA SINGLE HEAD MARK;Po;0;ON;;;;;N;;;;; A875;PHAGS-PA DOUBLE HEAD MARK;Po;0;ON;;;;;N;;;;; A876;PHAGS-PA MARK SHAD;Po;0;ON;;;;;N;;;;; A877;PHAGS-PA MARK DOUBLE SHAD;Po;0;ON;;;;;N;;;;; A880;SAURASHTRA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; A881;SAURASHTRA SIGN VISARGA;Mc;0;L;;;;;N;;;;; A882;SAURASHTRA LETTER A;Lo;0;L;;;;;N;;;;; A883;SAURASHTRA LETTER AA;Lo;0;L;;;;;N;;;;; A884;SAURASHTRA LETTER I;Lo;0;L;;;;;N;;;;; A885;SAURASHTRA LETTER II;Lo;0;L;;;;;N;;;;; A886;SAURASHTRA LETTER U;Lo;0;L;;;;;N;;;;; A887;SAURASHTRA LETTER UU;Lo;0;L;;;;;N;;;;; A888;SAURASHTRA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; A889;SAURASHTRA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; A88A;SAURASHTRA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; A88B;SAURASHTRA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; A88C;SAURASHTRA LETTER E;Lo;0;L;;;;;N;;;;; A88D;SAURASHTRA LETTER EE;Lo;0;L;;;;;N;;;;; A88E;SAURASHTRA LETTER AI;Lo;0;L;;;;;N;;;;; A88F;SAURASHTRA LETTER O;Lo;0;L;;;;;N;;;;; A890;SAURASHTRA LETTER OO;Lo;0;L;;;;;N;;;;; A891;SAURASHTRA LETTER AU;Lo;0;L;;;;;N;;;;; A892;SAURASHTRA LETTER KA;Lo;0;L;;;;;N;;;;; A893;SAURASHTRA LETTER KHA;Lo;0;L;;;;;N;;;;; A894;SAURASHTRA LETTER GA;Lo;0;L;;;;;N;;;;; A895;SAURASHTRA LETTER GHA;Lo;0;L;;;;;N;;;;; A896;SAURASHTRA LETTER NGA;Lo;0;L;;;;;N;;;;; A897;SAURASHTRA LETTER CA;Lo;0;L;;;;;N;;;;; A898;SAURASHTRA LETTER CHA;Lo;0;L;;;;;N;;;;; A899;SAURASHTRA LETTER JA;Lo;0;L;;;;;N;;;;; A89A;SAURASHTRA LETTER JHA;Lo;0;L;;;;;N;;;;; A89B;SAURASHTRA LETTER NYA;Lo;0;L;;;;;N;;;;; A89C;SAURASHTRA LETTER TTA;Lo;0;L;;;;;N;;;;; A89D;SAURASHTRA LETTER TTHA;Lo;0;L;;;;;N;;;;; A89E;SAURASHTRA LETTER DDA;Lo;0;L;;;;;N;;;;; A89F;SAURASHTRA LETTER DDHA;Lo;0;L;;;;;N;;;;; A8A0;SAURASHTRA LETTER NNA;Lo;0;L;;;;;N;;;;; A8A1;SAURASHTRA LETTER TA;Lo;0;L;;;;;N;;;;; A8A2;SAURASHTRA LETTER THA;Lo;0;L;;;;;N;;;;; A8A3;SAURASHTRA LETTER DA;Lo;0;L;;;;;N;;;;; A8A4;SAURASHTRA LETTER DHA;Lo;0;L;;;;;N;;;;; A8A5;SAURASHTRA LETTER NA;Lo;0;L;;;;;N;;;;; A8A6;SAURASHTRA LETTER PA;Lo;0;L;;;;;N;;;;; A8A7;SAURASHTRA LETTER PHA;Lo;0;L;;;;;N;;;;; A8A8;SAURASHTRA LETTER BA;Lo;0;L;;;;;N;;;;; A8A9;SAURASHTRA LETTER BHA;Lo;0;L;;;;;N;;;;; A8AA;SAURASHTRA LETTER MA;Lo;0;L;;;;;N;;;;; A8AB;SAURASHTRA LETTER YA;Lo;0;L;;;;;N;;;;; A8AC;SAURASHTRA LETTER RA;Lo;0;L;;;;;N;;;;; A8AD;SAURASHTRA LETTER LA;Lo;0;L;;;;;N;;;;; A8AE;SAURASHTRA LETTER VA;Lo;0;L;;;;;N;;;;; A8AF;SAURASHTRA LETTER SHA;Lo;0;L;;;;;N;;;;; A8B0;SAURASHTRA LETTER SSA;Lo;0;L;;;;;N;;;;; A8B1;SAURASHTRA LETTER SA;Lo;0;L;;;;;N;;;;; A8B2;SAURASHTRA LETTER HA;Lo;0;L;;;;;N;;;;; A8B3;SAURASHTRA LETTER LLA;Lo;0;L;;;;;N;;;;; A8B4;SAURASHTRA CONSONANT SIGN HAARU;Mc;0;L;;;;;N;;;;; A8B5;SAURASHTRA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; A8B6;SAURASHTRA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; A8B7;SAURASHTRA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; A8B8;SAURASHTRA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; A8B9;SAURASHTRA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; A8BA;SAURASHTRA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; A8BB;SAURASHTRA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; A8BC;SAURASHTRA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;; A8BD;SAURASHTRA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;; A8BE;SAURASHTRA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; A8BF;SAURASHTRA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; A8C0;SAURASHTRA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; A8C1;SAURASHTRA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; A8C2;SAURASHTRA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; A8C3;SAURASHTRA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; A8C4;SAURASHTRA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; A8C5;SAURASHTRA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; A8CE;SAURASHTRA DANDA;Po;0;L;;;;;N;;;;; A8CF;SAURASHTRA DOUBLE DANDA;Po;0;L;;;;;N;;;;; A8D0;SAURASHTRA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; A8D1;SAURASHTRA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; A8D2;SAURASHTRA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; A8D3;SAURASHTRA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; A8D4;SAURASHTRA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; A8D5;SAURASHTRA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; A8D6;SAURASHTRA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; A8D7;SAURASHTRA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; A8D8;SAURASHTRA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; A8D9;SAURASHTRA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; A8E0;COMBINING DEVANAGARI DIGIT ZERO;Mn;230;NSM;;;;;N;;;;; A8E1;COMBINING DEVANAGARI DIGIT ONE;Mn;230;NSM;;;;;N;;;;; A8E2;COMBINING DEVANAGARI DIGIT TWO;Mn;230;NSM;;;;;N;;;;; A8E3;COMBINING DEVANAGARI DIGIT THREE;Mn;230;NSM;;;;;N;;;;; A8E4;COMBINING DEVANAGARI DIGIT FOUR;Mn;230;NSM;;;;;N;;;;; A8E5;COMBINING DEVANAGARI DIGIT FIVE;Mn;230;NSM;;;;;N;;;;; A8E6;COMBINING DEVANAGARI DIGIT SIX;Mn;230;NSM;;;;;N;;;;; A8E7;COMBINING DEVANAGARI DIGIT SEVEN;Mn;230;NSM;;;;;N;;;;; A8E8;COMBINING DEVANAGARI DIGIT EIGHT;Mn;230;NSM;;;;;N;;;;; A8E9;COMBINING DEVANAGARI DIGIT NINE;Mn;230;NSM;;;;;N;;;;; A8EA;COMBINING DEVANAGARI LETTER A;Mn;230;NSM;;;;;N;;;;; A8EB;COMBINING DEVANAGARI LETTER U;Mn;230;NSM;;;;;N;;;;; A8EC;COMBINING DEVANAGARI LETTER KA;Mn;230;NSM;;;;;N;;;;; A8ED;COMBINING DEVANAGARI LETTER NA;Mn;230;NSM;;;;;N;;;;; A8EE;COMBINING DEVANAGARI LETTER PA;Mn;230;NSM;;;;;N;;;;; A8EF;COMBINING DEVANAGARI LETTER RA;Mn;230;NSM;;;;;N;;;;; A8F0;COMBINING DEVANAGARI LETTER VI;Mn;230;NSM;;;;;N;;;;; A8F1;COMBINING DEVANAGARI SIGN AVAGRAHA;Mn;230;NSM;;;;;N;;;;; A8F2;DEVANAGARI SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;; A8F3;DEVANAGARI SIGN CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;; A8F4;DEVANAGARI SIGN DOUBLE CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;; A8F5;DEVANAGARI SIGN CANDRABINDU TWO;Lo;0;L;;;;;N;;;;; A8F6;DEVANAGARI SIGN CANDRABINDU THREE;Lo;0;L;;;;;N;;;;; A8F7;DEVANAGARI SIGN CANDRABINDU AVAGRAHA;Lo;0;L;;;;;N;;;;; A8F8;DEVANAGARI SIGN PUSHPIKA;Po;0;L;;;;;N;;;;; A8F9;DEVANAGARI GAP FILLER;Po;0;L;;;;;N;;;;; A8FA;DEVANAGARI CARET;Po;0;L;;;;;N;;;;; A8FB;DEVANAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;; A8FC;DEVANAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;; A8FD;DEVANAGARI JAIN OM;Lo;0;L;;;;;N;;;;; A900;KAYAH LI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; A901;KAYAH LI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; A902;KAYAH LI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; A903;KAYAH LI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; A904;KAYAH LI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; A905;KAYAH LI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; A906;KAYAH LI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; A907;KAYAH LI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; A908;KAYAH LI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; A909;KAYAH LI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; A90A;KAYAH LI LETTER KA;Lo;0;L;;;;;N;;;;; A90B;KAYAH LI LETTER KHA;Lo;0;L;;;;;N;;;;; A90C;KAYAH LI LETTER GA;Lo;0;L;;;;;N;;;;; A90D;KAYAH LI LETTER NGA;Lo;0;L;;;;;N;;;;; A90E;KAYAH LI LETTER SA;Lo;0;L;;;;;N;;;;; A90F;KAYAH LI LETTER SHA;Lo;0;L;;;;;N;;;;; A910;KAYAH LI LETTER ZA;Lo;0;L;;;;;N;;;;; A911;KAYAH LI LETTER NYA;Lo;0;L;;;;;N;;;;; A912;KAYAH LI LETTER TA;Lo;0;L;;;;;N;;;;; A913;KAYAH LI LETTER HTA;Lo;0;L;;;;;N;;;;; A914;KAYAH LI LETTER NA;Lo;0;L;;;;;N;;;;; A915;KAYAH LI LETTER PA;Lo;0;L;;;;;N;;;;; A916;KAYAH LI LETTER PHA;Lo;0;L;;;;;N;;;;; A917;KAYAH LI LETTER MA;Lo;0;L;;;;;N;;;;; A918;KAYAH LI LETTER DA;Lo;0;L;;;;;N;;;;; A919;KAYAH LI LETTER BA;Lo;0;L;;;;;N;;;;; A91A;KAYAH LI LETTER RA;Lo;0;L;;;;;N;;;;; A91B;KAYAH LI LETTER YA;Lo;0;L;;;;;N;;;;; A91C;KAYAH LI LETTER LA;Lo;0;L;;;;;N;;;;; A91D;KAYAH LI LETTER WA;Lo;0;L;;;;;N;;;;; A91E;KAYAH LI LETTER THA;Lo;0;L;;;;;N;;;;; A91F;KAYAH LI LETTER HA;Lo;0;L;;;;;N;;;;; A920;KAYAH LI LETTER VA;Lo;0;L;;;;;N;;;;; A921;KAYAH LI LETTER CA;Lo;0;L;;;;;N;;;;; A922;KAYAH LI LETTER A;Lo;0;L;;;;;N;;;;; A923;KAYAH LI LETTER OE;Lo;0;L;;;;;N;;;;; A924;KAYAH LI LETTER I;Lo;0;L;;;;;N;;;;; A925;KAYAH LI LETTER OO;Lo;0;L;;;;;N;;;;; A926;KAYAH LI VOWEL UE;Mn;0;NSM;;;;;N;;;;; A927;KAYAH LI VOWEL E;Mn;0;NSM;;;;;N;;;;; A928;KAYAH LI VOWEL U;Mn;0;NSM;;;;;N;;;;; A929;KAYAH LI VOWEL EE;Mn;0;NSM;;;;;N;;;;; A92A;KAYAH LI VOWEL O;Mn;0;NSM;;;;;N;;;;; A92B;KAYAH LI TONE PLOPHU;Mn;220;NSM;;;;;N;;;;; A92C;KAYAH LI TONE CALYA;Mn;220;NSM;;;;;N;;;;; A92D;KAYAH LI TONE CALYA PLOPHU;Mn;220;NSM;;;;;N;;;;; A92E;KAYAH LI SIGN CWI;Po;0;L;;;;;N;;;;; A92F;KAYAH LI SIGN SHYA;Po;0;L;;;;;N;;;;; A930;REJANG LETTER KA;Lo;0;L;;;;;N;;;;; A931;REJANG LETTER GA;Lo;0;L;;;;;N;;;;; A932;REJANG LETTER NGA;Lo;0;L;;;;;N;;;;; A933;REJANG LETTER TA;Lo;0;L;;;;;N;;;;; A934;REJANG LETTER DA;Lo;0;L;;;;;N;;;;; A935;REJANG LETTER NA;Lo;0;L;;;;;N;;;;; A936;REJANG LETTER PA;Lo;0;L;;;;;N;;;;; A937;REJANG LETTER BA;Lo;0;L;;;;;N;;;;; A938;REJANG LETTER MA;Lo;0;L;;;;;N;;;;; A939;REJANG LETTER CA;Lo;0;L;;;;;N;;;;; A93A;REJANG LETTER JA;Lo;0;L;;;;;N;;;;; A93B;REJANG LETTER NYA;Lo;0;L;;;;;N;;;;; A93C;REJANG LETTER SA;Lo;0;L;;;;;N;;;;; A93D;REJANG LETTER RA;Lo;0;L;;;;;N;;;;; A93E;REJANG LETTER LA;Lo;0;L;;;;;N;;;;; A93F;REJANG LETTER YA;Lo;0;L;;;;;N;;;;; A940;REJANG LETTER WA;Lo;0;L;;;;;N;;;;; A941;REJANG LETTER HA;Lo;0;L;;;;;N;;;;; A942;REJANG LETTER MBA;Lo;0;L;;;;;N;;;;; A943;REJANG LETTER NGGA;Lo;0;L;;;;;N;;;;; A944;REJANG LETTER NDA;Lo;0;L;;;;;N;;;;; A945;REJANG LETTER NYJA;Lo;0;L;;;;;N;;;;; A946;REJANG LETTER A;Lo;0;L;;;;;N;;;;; A947;REJANG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; A948;REJANG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; A949;REJANG VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; A94A;REJANG VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; A94B;REJANG VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; A94C;REJANG VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; A94D;REJANG VOWEL SIGN EU;Mn;0;NSM;;;;;N;;;;; A94E;REJANG VOWEL SIGN EA;Mn;0;NSM;;;;;N;;;;; A94F;REJANG CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;; A950;REJANG CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;; A951;REJANG CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;; A952;REJANG CONSONANT SIGN H;Mc;0;L;;;;;N;;;;; A953;REJANG VIRAMA;Mc;9;L;;;;;N;;;;; A95F;REJANG SECTION MARK;Po;0;L;;;;;N;;;;; A960;HANGUL CHOSEONG TIKEUT-MIEUM;Lo;0;L;;;;;N;;;;; A961;HANGUL CHOSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; A962;HANGUL CHOSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;; A963;HANGUL CHOSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;; A964;HANGUL CHOSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;; A965;HANGUL CHOSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; A966;HANGUL CHOSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;; A967;HANGUL CHOSEONG RIEUL-SSANGTIKEUT;Lo;0;L;;;;;N;;;;; A968;HANGUL CHOSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;; A969;HANGUL CHOSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;; A96A;HANGUL CHOSEONG RIEUL-SSANGPIEUP;Lo;0;L;;;;;N;;;;; A96B;HANGUL CHOSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; A96C;HANGUL CHOSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;; A96D;HANGUL CHOSEONG RIEUL-CIEUC;Lo;0;L;;;;;N;;;;; A96E;HANGUL CHOSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; A96F;HANGUL CHOSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; A970;HANGUL CHOSEONG MIEUM-TIKEUT;Lo;0;L;;;;;N;;;;; A971;HANGUL CHOSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;; A972;HANGUL CHOSEONG PIEUP-SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; A973;HANGUL CHOSEONG PIEUP-KHIEUKH;Lo;0;L;;;;;N;;;;; A974;HANGUL CHOSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; A975;HANGUL CHOSEONG SSANGSIOS-PIEUP;Lo;0;L;;;;;N;;;;; A976;HANGUL CHOSEONG IEUNG-RIEUL;Lo;0;L;;;;;N;;;;; A977;HANGUL CHOSEONG IEUNG-HIEUH;Lo;0;L;;;;;N;;;;; A978;HANGUL CHOSEONG SSANGCIEUC-HIEUH;Lo;0;L;;;;;N;;;;; A979;HANGUL CHOSEONG SSANGTHIEUTH;Lo;0;L;;;;;N;;;;; A97A;HANGUL CHOSEONG PHIEUPH-HIEUH;Lo;0;L;;;;;N;;;;; A97B;HANGUL CHOSEONG HIEUH-SIOS;Lo;0;L;;;;;N;;;;; A97C;HANGUL CHOSEONG SSANGYEORINHIEUH;Lo;0;L;;;;;N;;;;; A980;JAVANESE SIGN PANYANGGA;Mn;0;NSM;;;;;N;;;;; A981;JAVANESE SIGN CECAK;Mn;0;NSM;;;;;N;;;;; A982;JAVANESE SIGN LAYAR;Mn;0;NSM;;;;;N;;;;; A983;JAVANESE SIGN WIGNYAN;Mc;0;L;;;;;N;;;;; A984;JAVANESE LETTER A;Lo;0;L;;;;;N;;;;; A985;JAVANESE LETTER I KAWI;Lo;0;L;;;;;N;;;;; A986;JAVANESE LETTER I;Lo;0;L;;;;;N;;;;; A987;JAVANESE LETTER II;Lo;0;L;;;;;N;;;;; A988;JAVANESE LETTER U;Lo;0;L;;;;;N;;;;; A989;JAVANESE LETTER PA CEREK;Lo;0;L;;;;;N;;;;; A98A;JAVANESE LETTER NGA LELET;Lo;0;L;;;;;N;;;;; A98B;JAVANESE LETTER NGA LELET RASWADI;Lo;0;L;;;;;N;;;;; A98C;JAVANESE LETTER E;Lo;0;L;;;;;N;;;;; A98D;JAVANESE LETTER AI;Lo;0;L;;;;;N;;;;; A98E;JAVANESE LETTER O;Lo;0;L;;;;;N;;;;; A98F;JAVANESE LETTER KA;Lo;0;L;;;;;N;;;;; A990;JAVANESE LETTER KA SASAK;Lo;0;L;;;;;N;;;;; A991;JAVANESE LETTER KA MURDA;Lo;0;L;;;;;N;;;;; A992;JAVANESE LETTER GA;Lo;0;L;;;;;N;;;;; A993;JAVANESE LETTER GA MURDA;Lo;0;L;;;;;N;;;;; A994;JAVANESE LETTER NGA;Lo;0;L;;;;;N;;;;; A995;JAVANESE LETTER CA;Lo;0;L;;;;;N;;;;; A996;JAVANESE LETTER CA MURDA;Lo;0;L;;;;;N;;;;; A997;JAVANESE LETTER JA;Lo;0;L;;;;;N;;;;; A998;JAVANESE LETTER NYA MURDA;Lo;0;L;;;;;N;;;;; A999;JAVANESE LETTER JA MAHAPRANA;Lo;0;L;;;;;N;;;;; A99A;JAVANESE LETTER NYA;Lo;0;L;;;;;N;;;;; A99B;JAVANESE LETTER TTA;Lo;0;L;;;;;N;;;;; A99C;JAVANESE LETTER TTA MAHAPRANA;Lo;0;L;;;;;N;;;;; A99D;JAVANESE LETTER DDA;Lo;0;L;;;;;N;;;;; A99E;JAVANESE LETTER DDA MAHAPRANA;Lo;0;L;;;;;N;;;;; A99F;JAVANESE LETTER NA MURDA;Lo;0;L;;;;;N;;;;; A9A0;JAVANESE LETTER TA;Lo;0;L;;;;;N;;;;; A9A1;JAVANESE LETTER TA MURDA;Lo;0;L;;;;;N;;;;; A9A2;JAVANESE LETTER DA;Lo;0;L;;;;;N;;;;; A9A3;JAVANESE LETTER DA MAHAPRANA;Lo;0;L;;;;;N;;;;; A9A4;JAVANESE LETTER NA;Lo;0;L;;;;;N;;;;; A9A5;JAVANESE LETTER PA;Lo;0;L;;;;;N;;;;; A9A6;JAVANESE LETTER PA MURDA;Lo;0;L;;;;;N;;;;; A9A7;JAVANESE LETTER BA;Lo;0;L;;;;;N;;;;; A9A8;JAVANESE LETTER BA MURDA;Lo;0;L;;;;;N;;;;; A9A9;JAVANESE LETTER MA;Lo;0;L;;;;;N;;;;; A9AA;JAVANESE LETTER YA;Lo;0;L;;;;;N;;;;; A9AB;JAVANESE LETTER RA;Lo;0;L;;;;;N;;;;; A9AC;JAVANESE LETTER RA AGUNG;Lo;0;L;;;;;N;;;;; A9AD;JAVANESE LETTER LA;Lo;0;L;;;;;N;;;;; A9AE;JAVANESE LETTER WA;Lo;0;L;;;;;N;;;;; A9AF;JAVANESE LETTER SA MURDA;Lo;0;L;;;;;N;;;;; A9B0;JAVANESE LETTER SA MAHAPRANA;Lo;0;L;;;;;N;;;;; A9B1;JAVANESE LETTER SA;Lo;0;L;;;;;N;;;;; A9B2;JAVANESE LETTER HA;Lo;0;L;;;;;N;;;;; A9B3;JAVANESE SIGN CECAK TELU;Mn;7;NSM;;;;;N;;;;; A9B4;JAVANESE VOWEL SIGN TARUNG;Mc;0;L;;;;;N;;;;; A9B5;JAVANESE VOWEL SIGN TOLONG;Mc;0;L;;;;;N;;;;; A9B6;JAVANESE VOWEL SIGN WULU;Mn;0;NSM;;;;;N;;;;; A9B7;JAVANESE VOWEL SIGN WULU MELIK;Mn;0;NSM;;;;;N;;;;; A9B8;JAVANESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;; A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;; A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;; A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;; A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;; A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;; A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;; A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;; A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;; A9C1;JAVANESE LEFT RERENGGAN;Po;0;L;;;;;N;;;;; A9C2;JAVANESE RIGHT RERENGGAN;Po;0;L;;;;;N;;;;; A9C3;JAVANESE PADA ANDAP;Po;0;L;;;;;N;;;;; A9C4;JAVANESE PADA MADYA;Po;0;L;;;;;N;;;;; A9C5;JAVANESE PADA LUHUR;Po;0;L;;;;;N;;;;; A9C6;JAVANESE PADA WINDU;Po;0;L;;;;;N;;;;; A9C7;JAVANESE PADA PANGKAT;Po;0;L;;;;;N;;;;; A9C8;JAVANESE PADA LINGSA;Po;0;L;;;;;N;;;;; A9C9;JAVANESE PADA LUNGSI;Po;0;L;;;;;N;;;;; A9CA;JAVANESE PADA ADEG;Po;0;L;;;;;N;;;;; A9CB;JAVANESE PADA ADEG ADEG;Po;0;L;;;;;N;;;;; A9CC;JAVANESE PADA PISELEH;Po;0;L;;;;;N;;;;; A9CD;JAVANESE TURNED PADA PISELEH;Po;0;L;;;;;N;;;;; A9CF;JAVANESE PANGRANGKEP;Lm;0;L;;;;;N;;;;; A9D0;JAVANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; A9D1;JAVANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; A9D2;JAVANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; A9D3;JAVANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; A9D4;JAVANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; A9D5;JAVANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; A9D6;JAVANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; A9D7;JAVANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; A9D8;JAVANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; A9D9;JAVANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; A9DE;JAVANESE PADA TIRTA TUMETES;Po;0;L;;;;;N;;;;; A9DF;JAVANESE PADA ISEN-ISEN;Po;0;L;;;;;N;;;;; A9E0;MYANMAR LETTER SHAN GHA;Lo;0;L;;;;;N;;;;; A9E1;MYANMAR LETTER SHAN CHA;Lo;0;L;;;;;N;;;;; A9E2;MYANMAR LETTER SHAN JHA;Lo;0;L;;;;;N;;;;; A9E3;MYANMAR LETTER SHAN NNA;Lo;0;L;;;;;N;;;;; A9E4;MYANMAR LETTER SHAN BHA;Lo;0;L;;;;;N;;;;; A9E5;MYANMAR SIGN SHAN SAW;Mn;0;NSM;;;;;N;;;;; A9E6;MYANMAR MODIFIER LETTER SHAN REDUPLICATION;Lm;0;L;;;;;N;;;;; A9E7;MYANMAR LETTER TAI LAING NYA;Lo;0;L;;;;;N;;;;; A9E8;MYANMAR LETTER TAI LAING FA;Lo;0;L;;;;;N;;;;; A9E9;MYANMAR LETTER TAI LAING GA;Lo;0;L;;;;;N;;;;; A9EA;MYANMAR LETTER TAI LAING GHA;Lo;0;L;;;;;N;;;;; A9EB;MYANMAR LETTER TAI LAING JA;Lo;0;L;;;;;N;;;;; A9EC;MYANMAR LETTER TAI LAING JHA;Lo;0;L;;;;;N;;;;; A9ED;MYANMAR LETTER TAI LAING DDA;Lo;0;L;;;;;N;;;;; A9EE;MYANMAR LETTER TAI LAING DDHA;Lo;0;L;;;;;N;;;;; A9EF;MYANMAR LETTER TAI LAING NNA;Lo;0;L;;;;;N;;;;; A9F0;MYANMAR TAI LAING DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; A9F1;MYANMAR TAI LAING DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; A9F2;MYANMAR TAI LAING DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; A9F3;MYANMAR TAI LAING DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; A9F4;MYANMAR TAI LAING DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; A9F5;MYANMAR TAI LAING DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; A9F6;MYANMAR TAI LAING DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; A9F7;MYANMAR TAI LAING DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; A9F8;MYANMAR TAI LAING DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; A9F9;MYANMAR TAI LAING DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; A9FA;MYANMAR LETTER TAI LAING LLA;Lo;0;L;;;;;N;;;;; A9FB;MYANMAR LETTER TAI LAING DA;Lo;0;L;;;;;N;;;;; A9FC;MYANMAR LETTER TAI LAING DHA;Lo;0;L;;;;;N;;;;; A9FD;MYANMAR LETTER TAI LAING BA;Lo;0;L;;;;;N;;;;; A9FE;MYANMAR LETTER TAI LAING BHA;Lo;0;L;;;;;N;;;;; AA00;CHAM LETTER A;Lo;0;L;;;;;N;;;;; AA01;CHAM LETTER I;Lo;0;L;;;;;N;;;;; AA02;CHAM LETTER U;Lo;0;L;;;;;N;;;;; AA03;CHAM LETTER E;Lo;0;L;;;;;N;;;;; AA04;CHAM LETTER AI;Lo;0;L;;;;;N;;;;; AA05;CHAM LETTER O;Lo;0;L;;;;;N;;;;; AA06;CHAM LETTER KA;Lo;0;L;;;;;N;;;;; AA07;CHAM LETTER KHA;Lo;0;L;;;;;N;;;;; AA08;CHAM LETTER GA;Lo;0;L;;;;;N;;;;; AA09;CHAM LETTER GHA;Lo;0;L;;;;;N;;;;; AA0A;CHAM LETTER NGUE;Lo;0;L;;;;;N;;;;; AA0B;CHAM LETTER NGA;Lo;0;L;;;;;N;;;;; AA0C;CHAM LETTER CHA;Lo;0;L;;;;;N;;;;; AA0D;CHAM LETTER CHHA;Lo;0;L;;;;;N;;;;; AA0E;CHAM LETTER JA;Lo;0;L;;;;;N;;;;; AA0F;CHAM LETTER JHA;Lo;0;L;;;;;N;;;;; AA10;CHAM LETTER NHUE;Lo;0;L;;;;;N;;;;; AA11;CHAM LETTER NHA;Lo;0;L;;;;;N;;;;; AA12;CHAM LETTER NHJA;Lo;0;L;;;;;N;;;;; AA13;CHAM LETTER TA;Lo;0;L;;;;;N;;;;; AA14;CHAM LETTER THA;Lo;0;L;;;;;N;;;;; AA15;CHAM LETTER DA;Lo;0;L;;;;;N;;;;; AA16;CHAM LETTER DHA;Lo;0;L;;;;;N;;;;; AA17;CHAM LETTER NUE;Lo;0;L;;;;;N;;;;; AA18;CHAM LETTER NA;Lo;0;L;;;;;N;;;;; AA19;CHAM LETTER DDA;Lo;0;L;;;;;N;;;;; AA1A;CHAM LETTER PA;Lo;0;L;;;;;N;;;;; AA1B;CHAM LETTER PPA;Lo;0;L;;;;;N;;;;; AA1C;CHAM LETTER PHA;Lo;0;L;;;;;N;;;;; AA1D;CHAM LETTER BA;Lo;0;L;;;;;N;;;;; AA1E;CHAM LETTER BHA;Lo;0;L;;;;;N;;;;; AA1F;CHAM LETTER MUE;Lo;0;L;;;;;N;;;;; AA20;CHAM LETTER MA;Lo;0;L;;;;;N;;;;; AA21;CHAM LETTER BBA;Lo;0;L;;;;;N;;;;; AA22;CHAM LETTER YA;Lo;0;L;;;;;N;;;;; AA23;CHAM LETTER RA;Lo;0;L;;;;;N;;;;; AA24;CHAM LETTER LA;Lo;0;L;;;;;N;;;;; AA25;CHAM LETTER VA;Lo;0;L;;;;;N;;;;; AA26;CHAM LETTER SSA;Lo;0;L;;;;;N;;;;; AA27;CHAM LETTER SA;Lo;0;L;;;;;N;;;;; AA28;CHAM LETTER HA;Lo;0;L;;;;;N;;;;; AA29;CHAM VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; AA2A;CHAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; AA2B;CHAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; AA2C;CHAM VOWEL SIGN EI;Mn;0;NSM;;;;;N;;;;; AA2D;CHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; AA2E;CHAM VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; AA2F;CHAM VOWEL SIGN O;Mc;0;L;;;;;N;;;;; AA30;CHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; AA31;CHAM VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; AA32;CHAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; AA33;CHAM CONSONANT SIGN YA;Mc;0;L;;;;;N;;;;; AA34;CHAM CONSONANT SIGN RA;Mc;0;L;;;;;N;;;;; AA35;CHAM CONSONANT SIGN LA;Mn;0;NSM;;;;;N;;;;; AA36;CHAM CONSONANT SIGN WA;Mn;0;NSM;;;;;N;;;;; AA40;CHAM LETTER FINAL K;Lo;0;L;;;;;N;;;;; AA41;CHAM LETTER FINAL G;Lo;0;L;;;;;N;;;;; AA42;CHAM LETTER FINAL NG;Lo;0;L;;;;;N;;;;; AA43;CHAM CONSONANT SIGN FINAL NG;Mn;0;NSM;;;;;N;;;;; AA44;CHAM LETTER FINAL CH;Lo;0;L;;;;;N;;;;; AA45;CHAM LETTER FINAL T;Lo;0;L;;;;;N;;;;; AA46;CHAM LETTER FINAL N;Lo;0;L;;;;;N;;;;; AA47;CHAM LETTER FINAL P;Lo;0;L;;;;;N;;;;; AA48;CHAM LETTER FINAL Y;Lo;0;L;;;;;N;;;;; AA49;CHAM LETTER FINAL R;Lo;0;L;;;;;N;;;;; AA4A;CHAM LETTER FINAL L;Lo;0;L;;;;;N;;;;; AA4B;CHAM LETTER FINAL SS;Lo;0;L;;;;;N;;;;; AA4C;CHAM CONSONANT SIGN FINAL M;Mn;0;NSM;;;;;N;;;;; AA4D;CHAM CONSONANT SIGN FINAL H;Mc;0;L;;;;;N;;;;; AA50;CHAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; AA51;CHAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; AA52;CHAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; AA53;CHAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; AA54;CHAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; AA55;CHAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; AA56;CHAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; AA57;CHAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; AA58;CHAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; AA59;CHAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; AA5C;CHAM PUNCTUATION SPIRAL;Po;0;L;;;;;N;;;;; AA5D;CHAM PUNCTUATION DANDA;Po;0;L;;;;;N;;;;; AA5E;CHAM PUNCTUATION DOUBLE DANDA;Po;0;L;;;;;N;;;;; AA5F;CHAM PUNCTUATION TRIPLE DANDA;Po;0;L;;;;;N;;;;; AA60;MYANMAR LETTER KHAMTI GA;Lo;0;L;;;;;N;;;;; AA61;MYANMAR LETTER KHAMTI CA;Lo;0;L;;;;;N;;;;; AA62;MYANMAR LETTER KHAMTI CHA;Lo;0;L;;;;;N;;;;; AA63;MYANMAR LETTER KHAMTI JA;Lo;0;L;;;;;N;;;;; AA64;MYANMAR LETTER KHAMTI JHA;Lo;0;L;;;;;N;;;;; AA65;MYANMAR LETTER KHAMTI NYA;Lo;0;L;;;;;N;;;;; AA66;MYANMAR LETTER KHAMTI TTA;Lo;0;L;;;;;N;;;;; AA67;MYANMAR LETTER KHAMTI TTHA;Lo;0;L;;;;;N;;;;; AA68;MYANMAR LETTER KHAMTI DDA;Lo;0;L;;;;;N;;;;; AA69;MYANMAR LETTER KHAMTI DDHA;Lo;0;L;;;;;N;;;;; AA6A;MYANMAR LETTER KHAMTI DHA;Lo;0;L;;;;;N;;;;; AA6B;MYANMAR LETTER KHAMTI NA;Lo;0;L;;;;;N;;;;; AA6C;MYANMAR LETTER KHAMTI SA;Lo;0;L;;;;;N;;;;; AA6D;MYANMAR LETTER KHAMTI HA;Lo;0;L;;;;;N;;;;; AA6E;MYANMAR LETTER KHAMTI HHA;Lo;0;L;;;;;N;;;;; AA6F;MYANMAR LETTER KHAMTI FA;Lo;0;L;;;;;N;;;;; AA70;MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION;Lm;0;L;;;;;N;;;;; AA71;MYANMAR LETTER KHAMTI XA;Lo;0;L;;;;;N;;;;; AA72;MYANMAR LETTER KHAMTI ZA;Lo;0;L;;;;;N;;;;; AA73;MYANMAR LETTER KHAMTI RA;Lo;0;L;;;;;N;;;;; AA74;MYANMAR LOGOGRAM KHAMTI OAY;Lo;0;L;;;;;N;;;;; AA75;MYANMAR LOGOGRAM KHAMTI QN;Lo;0;L;;;;;N;;;;; AA76;MYANMAR LOGOGRAM KHAMTI HM;Lo;0;L;;;;;N;;;;; AA77;MYANMAR SYMBOL AITON EXCLAMATION;So;0;L;;;;;N;;;;; AA78;MYANMAR SYMBOL AITON ONE;So;0;L;;;;;N;;;;; AA79;MYANMAR SYMBOL AITON TWO;So;0;L;;;;;N;;;;; AA7A;MYANMAR LETTER AITON RA;Lo;0;L;;;;;N;;;;; AA7B;MYANMAR SIGN PAO KAREN TONE;Mc;0;L;;;;;N;;;;; AA7C;MYANMAR SIGN TAI LAING TONE-2;Mn;0;NSM;;;;;N;;;;; AA7D;MYANMAR SIGN TAI LAING TONE-5;Mc;0;L;;;;;N;;;;; AA7E;MYANMAR LETTER SHWE PALAUNG CHA;Lo;0;L;;;;;N;;;;; AA7F;MYANMAR LETTER SHWE PALAUNG SHA;Lo;0;L;;;;;N;;;;; AA80;TAI VIET LETTER LOW KO;Lo;0;L;;;;;N;;;;; AA81;TAI VIET LETTER HIGH KO;Lo;0;L;;;;;N;;;;; AA82;TAI VIET LETTER LOW KHO;Lo;0;L;;;;;N;;;;; AA83;TAI VIET LETTER HIGH KHO;Lo;0;L;;;;;N;;;;; AA84;TAI VIET LETTER LOW KHHO;Lo;0;L;;;;;N;;;;; AA85;TAI VIET LETTER HIGH KHHO;Lo;0;L;;;;;N;;;;; AA86;TAI VIET LETTER LOW GO;Lo;0;L;;;;;N;;;;; AA87;TAI VIET LETTER HIGH GO;Lo;0;L;;;;;N;;;;; AA88;TAI VIET LETTER LOW NGO;Lo;0;L;;;;;N;;;;; AA89;TAI VIET LETTER HIGH NGO;Lo;0;L;;;;;N;;;;; AA8A;TAI VIET LETTER LOW CO;Lo;0;L;;;;;N;;;;; AA8B;TAI VIET LETTER HIGH CO;Lo;0;L;;;;;N;;;;; AA8C;TAI VIET LETTER LOW CHO;Lo;0;L;;;;;N;;;;; AA8D;TAI VIET LETTER HIGH CHO;Lo;0;L;;;;;N;;;;; AA8E;TAI VIET LETTER LOW SO;Lo;0;L;;;;;N;;;;; AA8F;TAI VIET LETTER HIGH SO;Lo;0;L;;;;;N;;;;; AA90;TAI VIET LETTER LOW NYO;Lo;0;L;;;;;N;;;;; AA91;TAI VIET LETTER HIGH NYO;Lo;0;L;;;;;N;;;;; AA92;TAI VIET LETTER LOW DO;Lo;0;L;;;;;N;;;;; AA93;TAI VIET LETTER HIGH DO;Lo;0;L;;;;;N;;;;; AA94;TAI VIET LETTER LOW TO;Lo;0;L;;;;;N;;;;; AA95;TAI VIET LETTER HIGH TO;Lo;0;L;;;;;N;;;;; AA96;TAI VIET LETTER LOW THO;Lo;0;L;;;;;N;;;;; AA97;TAI VIET LETTER HIGH THO;Lo;0;L;;;;;N;;;;; AA98;TAI VIET LETTER LOW NO;Lo;0;L;;;;;N;;;;; AA99;TAI VIET LETTER HIGH NO;Lo;0;L;;;;;N;;;;; AA9A;TAI VIET LETTER LOW BO;Lo;0;L;;;;;N;;;;; AA9B;TAI VIET LETTER HIGH BO;Lo;0;L;;;;;N;;;;; AA9C;TAI VIET LETTER LOW PO;Lo;0;L;;;;;N;;;;; AA9D;TAI VIET LETTER HIGH PO;Lo;0;L;;;;;N;;;;; AA9E;TAI VIET LETTER LOW PHO;Lo;0;L;;;;;N;;;;; AA9F;TAI VIET LETTER HIGH PHO;Lo;0;L;;;;;N;;;;; AAA0;TAI VIET LETTER LOW FO;Lo;0;L;;;;;N;;;;; AAA1;TAI VIET LETTER HIGH FO;Lo;0;L;;;;;N;;;;; AAA2;TAI VIET LETTER LOW MO;Lo;0;L;;;;;N;;;;; AAA3;TAI VIET LETTER HIGH MO;Lo;0;L;;;;;N;;;;; AAA4;TAI VIET LETTER LOW YO;Lo;0;L;;;;;N;;;;; AAA5;TAI VIET LETTER HIGH YO;Lo;0;L;;;;;N;;;;; AAA6;TAI VIET LETTER LOW RO;Lo;0;L;;;;;N;;;;; AAA7;TAI VIET LETTER HIGH RO;Lo;0;L;;;;;N;;;;; AAA8;TAI VIET LETTER LOW LO;Lo;0;L;;;;;N;;;;; AAA9;TAI VIET LETTER HIGH LO;Lo;0;L;;;;;N;;;;; AAAA;TAI VIET LETTER LOW VO;Lo;0;L;;;;;N;;;;; AAAB;TAI VIET LETTER HIGH VO;Lo;0;L;;;;;N;;;;; AAAC;TAI VIET LETTER LOW HO;Lo;0;L;;;;;N;;;;; AAAD;TAI VIET LETTER HIGH HO;Lo;0;L;;;;;N;;;;; AAAE;TAI VIET LETTER LOW O;Lo;0;L;;;;;N;;;;; AAAF;TAI VIET LETTER HIGH O;Lo;0;L;;;;;N;;;;; AAB0;TAI VIET MAI KANG;Mn;230;NSM;;;;;N;;;;; AAB1;TAI VIET VOWEL AA;Lo;0;L;;;;;N;;;;; AAB2;TAI VIET VOWEL I;Mn;230;NSM;;;;;N;;;;; AAB3;TAI VIET VOWEL UE;Mn;230;NSM;;;;;N;;;;; AAB4;TAI VIET VOWEL U;Mn;220;NSM;;;;;N;;;;; AAB5;TAI VIET VOWEL E;Lo;0;L;;;;;N;;;;; AAB6;TAI VIET VOWEL O;Lo;0;L;;;;;N;;;;; AAB7;TAI VIET MAI KHIT;Mn;230;NSM;;;;;N;;;;; AAB8;TAI VIET VOWEL IA;Mn;230;NSM;;;;;N;;;;; AAB9;TAI VIET VOWEL UEA;Lo;0;L;;;;;N;;;;; AABA;TAI VIET VOWEL UA;Lo;0;L;;;;;N;;;;; AABB;TAI VIET VOWEL AUE;Lo;0;L;;;;;N;;;;; AABC;TAI VIET VOWEL AY;Lo;0;L;;;;;N;;;;; AABD;TAI VIET VOWEL AN;Lo;0;L;;;;;N;;;;; AABE;TAI VIET VOWEL AM;Mn;230;NSM;;;;;N;;;;; AABF;TAI VIET TONE MAI EK;Mn;230;NSM;;;;;N;;;;; AAC0;TAI VIET TONE MAI NUENG;Lo;0;L;;;;;N;;;;; AAC1;TAI VIET TONE MAI THO;Mn;230;NSM;;;;;N;;;;; AAC2;TAI VIET TONE MAI SONG;Lo;0;L;;;;;N;;;;; AADB;TAI VIET SYMBOL KON;Lo;0;L;;;;;N;;;;; AADC;TAI VIET SYMBOL NUENG;Lo;0;L;;;;;N;;;;; AADD;TAI VIET SYMBOL SAM;Lm;0;L;;;;;N;;;;; AADE;TAI VIET SYMBOL HO HOI;Po;0;L;;;;;N;;;;; AADF;TAI VIET SYMBOL KOI KOI;Po;0;L;;;;;N;;;;; AAE0;MEETEI MAYEK LETTER E;Lo;0;L;;;;;N;;;;; AAE1;MEETEI MAYEK LETTER O;Lo;0;L;;;;;N;;;;; AAE2;MEETEI MAYEK LETTER CHA;Lo;0;L;;;;;N;;;;; AAE3;MEETEI MAYEK LETTER NYA;Lo;0;L;;;;;N;;;;; AAE4;MEETEI MAYEK LETTER TTA;Lo;0;L;;;;;N;;;;; AAE5;MEETEI MAYEK LETTER TTHA;Lo;0;L;;;;;N;;;;; AAE6;MEETEI MAYEK LETTER DDA;Lo;0;L;;;;;N;;;;; AAE7;MEETEI MAYEK LETTER DDHA;Lo;0;L;;;;;N;;;;; AAE8;MEETEI MAYEK LETTER NNA;Lo;0;L;;;;;N;;;;; AAE9;MEETEI MAYEK LETTER SHA;Lo;0;L;;;;;N;;;;; AAEA;MEETEI MAYEK LETTER SSA;Lo;0;L;;;;;N;;;;; AAEB;MEETEI MAYEK VOWEL SIGN II;Mc;0;L;;;;;N;;;;; AAEC;MEETEI MAYEK VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; AAED;MEETEI MAYEK VOWEL SIGN AAI;Mn;0;NSM;;;;;N;;;;; AAEE;MEETEI MAYEK VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; AAEF;MEETEI MAYEK VOWEL SIGN AAU;Mc;0;L;;;;;N;;;;; AAF0;MEETEI MAYEK CHEIKHAN;Po;0;L;;;;;N;;;;; AAF1;MEETEI MAYEK AHANG KHUDAM;Po;0;L;;;;;N;;;;; AAF2;MEETEI MAYEK ANJI;Lo;0;L;;;;;N;;;;; AAF3;MEETEI MAYEK SYLLABLE REPETITION MARK;Lm;0;L;;;;;N;;;;; AAF4;MEETEI MAYEK WORD REPETITION MARK;Lm;0;L;;;;;N;;;;; AAF5;MEETEI MAYEK VOWEL SIGN VISARGA;Mc;0;L;;;;;N;;;;; AAF6;MEETEI MAYEK VIRAMA;Mn;9;NSM;;;;;N;;;;; AB01;ETHIOPIC SYLLABLE TTHU;Lo;0;L;;;;;N;;;;; AB02;ETHIOPIC SYLLABLE TTHI;Lo;0;L;;;;;N;;;;; AB03;ETHIOPIC SYLLABLE TTHAA;Lo;0;L;;;;;N;;;;; AB04;ETHIOPIC SYLLABLE TTHEE;Lo;0;L;;;;;N;;;;; AB05;ETHIOPIC SYLLABLE TTHE;Lo;0;L;;;;;N;;;;; AB06;ETHIOPIC SYLLABLE TTHO;Lo;0;L;;;;;N;;;;; AB09;ETHIOPIC SYLLABLE DDHU;Lo;0;L;;;;;N;;;;; AB0A;ETHIOPIC SYLLABLE DDHI;Lo;0;L;;;;;N;;;;; AB0B;ETHIOPIC SYLLABLE DDHAA;Lo;0;L;;;;;N;;;;; AB0C;ETHIOPIC SYLLABLE DDHEE;Lo;0;L;;;;;N;;;;; AB0D;ETHIOPIC SYLLABLE DDHE;Lo;0;L;;;;;N;;;;; AB0E;ETHIOPIC SYLLABLE DDHO;Lo;0;L;;;;;N;;;;; AB11;ETHIOPIC SYLLABLE DZU;Lo;0;L;;;;;N;;;;; AB12;ETHIOPIC SYLLABLE DZI;Lo;0;L;;;;;N;;;;; AB13;ETHIOPIC SYLLABLE DZAA;Lo;0;L;;;;;N;;;;; AB14;ETHIOPIC SYLLABLE DZEE;Lo;0;L;;;;;N;;;;; AB15;ETHIOPIC SYLLABLE DZE;Lo;0;L;;;;;N;;;;; AB16;ETHIOPIC SYLLABLE DZO;Lo;0;L;;;;;N;;;;; AB20;ETHIOPIC SYLLABLE CCHHA;Lo;0;L;;;;;N;;;;; AB21;ETHIOPIC SYLLABLE CCHHU;Lo;0;L;;;;;N;;;;; AB22;ETHIOPIC SYLLABLE CCHHI;Lo;0;L;;;;;N;;;;; AB23;ETHIOPIC SYLLABLE CCHHAA;Lo;0;L;;;;;N;;;;; AB24;ETHIOPIC SYLLABLE CCHHEE;Lo;0;L;;;;;N;;;;; AB25;ETHIOPIC SYLLABLE CCHHE;Lo;0;L;;;;;N;;;;; AB26;ETHIOPIC SYLLABLE CCHHO;Lo;0;L;;;;;N;;;;; AB28;ETHIOPIC SYLLABLE BBA;Lo;0;L;;;;;N;;;;; AB29;ETHIOPIC SYLLABLE BBU;Lo;0;L;;;;;N;;;;; AB2A;ETHIOPIC SYLLABLE BBI;Lo;0;L;;;;;N;;;;; AB2B;ETHIOPIC SYLLABLE BBAA;Lo;0;L;;;;;N;;;;; AB2C;ETHIOPIC SYLLABLE BBEE;Lo;0;L;;;;;N;;;;; AB2D;ETHIOPIC SYLLABLE BBE;Lo;0;L;;;;;N;;;;; AB2E;ETHIOPIC SYLLABLE BBO;Lo;0;L;;;;;N;;;;; AB30;LATIN SMALL LETTER BARRED ALPHA;Ll;0;L;;;;;N;;;;; AB31;LATIN SMALL LETTER A REVERSED-SCHWA;Ll;0;L;;;;;N;;;;; AB32;LATIN SMALL LETTER BLACKLETTER E;Ll;0;L;;;;;N;;;;; AB33;LATIN SMALL LETTER BARRED E;Ll;0;L;;;;;N;;;;; AB34;LATIN SMALL LETTER E WITH FLOURISH;Ll;0;L;;;;;N;;;;; AB35;LATIN SMALL LETTER LENIS F;Ll;0;L;;;;;N;;;;; AB36;LATIN SMALL LETTER SCRIPT G WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB37;LATIN SMALL LETTER L WITH INVERTED LAZY S;Ll;0;L;;;;;N;;;;; AB38;LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Ll;0;L;;;;;N;;;;; AB39;LATIN SMALL LETTER L WITH MIDDLE RING;Ll;0;L;;;;;N;;;;; AB3A;LATIN SMALL LETTER M WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB3B;LATIN SMALL LETTER N WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB3C;LATIN SMALL LETTER ENG WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB3D;LATIN SMALL LETTER BLACKLETTER O;Ll;0;L;;;;;N;;;;; AB3E;LATIN SMALL LETTER BLACKLETTER O WITH STROKE;Ll;0;L;;;;;N;;;;; AB3F;LATIN SMALL LETTER OPEN O WITH STROKE;Ll;0;L;;;;;N;;;;; AB40;LATIN SMALL LETTER INVERTED OE;Ll;0;L;;;;;N;;;;; AB41;LATIN SMALL LETTER TURNED OE WITH STROKE;Ll;0;L;;;;;N;;;;; AB42;LATIN SMALL LETTER TURNED OE WITH HORIZONTAL STROKE;Ll;0;L;;;;;N;;;;; AB43;LATIN SMALL LETTER TURNED O OPEN-O;Ll;0;L;;;;;N;;;;; AB44;LATIN SMALL LETTER TURNED O OPEN-O WITH STROKE;Ll;0;L;;;;;N;;;;; AB45;LATIN SMALL LETTER STIRRUP R;Ll;0;L;;;;;N;;;;; AB46;LATIN LETTER SMALL CAPITAL R WITH RIGHT LEG;Ll;0;L;;;;;N;;;;; AB47;LATIN SMALL LETTER R WITHOUT HANDLE;Ll;0;L;;;;;N;;;;; AB48;LATIN SMALL LETTER DOUBLE R;Ll;0;L;;;;;N;;;;; AB49;LATIN SMALL LETTER R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB4A;LATIN SMALL LETTER DOUBLE R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; AB4B;LATIN SMALL LETTER SCRIPT R;Ll;0;L;;;;;N;;;;; AB4C;LATIN SMALL LETTER SCRIPT R WITH RING;Ll;0;L;;;;;N;;;;; AB4D;LATIN SMALL LETTER BASELINE ESH;Ll;0;L;;;;;N;;;;; AB4E;LATIN SMALL LETTER U WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; AB4F;LATIN SMALL LETTER U BAR WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; AB50;LATIN SMALL LETTER UI;Ll;0;L;;;;;N;;;;; AB51;LATIN SMALL LETTER TURNED UI;Ll;0;L;;;;;N;;;;; AB52;LATIN SMALL LETTER U WITH LEFT HOOK;Ll;0;L;;;;;N;;;;; AB53;LATIN SMALL LETTER CHI;Ll;0;L;;;;;N;;;A7B3;;A7B3 AB54;LATIN SMALL LETTER CHI WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;; AB55;LATIN SMALL LETTER CHI WITH LOW LEFT SERIF;Ll;0;L;;;;;N;;;;; AB56;LATIN SMALL LETTER X WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;; AB57;LATIN SMALL LETTER X WITH LONG LEFT LEG;Ll;0;L;;;;;N;;;;; AB58;LATIN SMALL LETTER X WITH LONG LEFT LEG AND LOW RIGHT RING;Ll;0;L;;;;;N;;;;; AB59;LATIN SMALL LETTER X WITH LONG LEFT LEG WITH SERIF;Ll;0;L;;;;;N;;;;; AB5A;LATIN SMALL LETTER Y WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; AB5B;MODIFIER BREVE WITH INVERTED BREVE;Sk;0;L;;;;;N;;;;; AB5C;MODIFIER LETTER SMALL HENG;Lm;0;L; A727;;;;N;;;;; AB5D;MODIFIER LETTER SMALL L WITH INVERTED LAZY S;Lm;0;L; AB37;;;;N;;;;; AB5E;MODIFIER LETTER SMALL L WITH MIDDLE TILDE;Lm;0;L; 026B;;;;N;;;;; AB5F;MODIFIER LETTER SMALL U WITH LEFT HOOK;Lm;0;L; AB52;;;;N;;;;; AB60;LATIN SMALL LETTER SAKHA YAT;Ll;0;L;;;;;N;;;;; AB61;LATIN SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;;; AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;; AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;; AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;; AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;; AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0 AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1 AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2 AB73;CHEROKEE SMALL LETTER O;Ll;0;L;;;;;N;;;13A3;;13A3 AB74;CHEROKEE SMALL LETTER U;Ll;0;L;;;;;N;;;13A4;;13A4 AB75;CHEROKEE SMALL LETTER V;Ll;0;L;;;;;N;;;13A5;;13A5 AB76;CHEROKEE SMALL LETTER GA;Ll;0;L;;;;;N;;;13A6;;13A6 AB77;CHEROKEE SMALL LETTER KA;Ll;0;L;;;;;N;;;13A7;;13A7 AB78;CHEROKEE SMALL LETTER GE;Ll;0;L;;;;;N;;;13A8;;13A8 AB79;CHEROKEE SMALL LETTER GI;Ll;0;L;;;;;N;;;13A9;;13A9 AB7A;CHEROKEE SMALL LETTER GO;Ll;0;L;;;;;N;;;13AA;;13AA AB7B;CHEROKEE SMALL LETTER GU;Ll;0;L;;;;;N;;;13AB;;13AB AB7C;CHEROKEE SMALL LETTER GV;Ll;0;L;;;;;N;;;13AC;;13AC AB7D;CHEROKEE SMALL LETTER HA;Ll;0;L;;;;;N;;;13AD;;13AD AB7E;CHEROKEE SMALL LETTER HE;Ll;0;L;;;;;N;;;13AE;;13AE AB7F;CHEROKEE SMALL LETTER HI;Ll;0;L;;;;;N;;;13AF;;13AF AB80;CHEROKEE SMALL LETTER HO;Ll;0;L;;;;;N;;;13B0;;13B0 AB81;CHEROKEE SMALL LETTER HU;Ll;0;L;;;;;N;;;13B1;;13B1 AB82;CHEROKEE SMALL LETTER HV;Ll;0;L;;;;;N;;;13B2;;13B2 AB83;CHEROKEE SMALL LETTER LA;Ll;0;L;;;;;N;;;13B3;;13B3 AB84;CHEROKEE SMALL LETTER LE;Ll;0;L;;;;;N;;;13B4;;13B4 AB85;CHEROKEE SMALL LETTER LI;Ll;0;L;;;;;N;;;13B5;;13B5 AB86;CHEROKEE SMALL LETTER LO;Ll;0;L;;;;;N;;;13B6;;13B6 AB87;CHEROKEE SMALL LETTER LU;Ll;0;L;;;;;N;;;13B7;;13B7 AB88;CHEROKEE SMALL LETTER LV;Ll;0;L;;;;;N;;;13B8;;13B8 AB89;CHEROKEE SMALL LETTER MA;Ll;0;L;;;;;N;;;13B9;;13B9 AB8A;CHEROKEE SMALL LETTER ME;Ll;0;L;;;;;N;;;13BA;;13BA AB8B;CHEROKEE SMALL LETTER MI;Ll;0;L;;;;;N;;;13BB;;13BB AB8C;CHEROKEE SMALL LETTER MO;Ll;0;L;;;;;N;;;13BC;;13BC AB8D;CHEROKEE SMALL LETTER MU;Ll;0;L;;;;;N;;;13BD;;13BD AB8E;CHEROKEE SMALL LETTER NA;Ll;0;L;;;;;N;;;13BE;;13BE AB8F;CHEROKEE SMALL LETTER HNA;Ll;0;L;;;;;N;;;13BF;;13BF AB90;CHEROKEE SMALL LETTER NAH;Ll;0;L;;;;;N;;;13C0;;13C0 AB91;CHEROKEE SMALL LETTER NE;Ll;0;L;;;;;N;;;13C1;;13C1 AB92;CHEROKEE SMALL LETTER NI;Ll;0;L;;;;;N;;;13C2;;13C2 AB93;CHEROKEE SMALL LETTER NO;Ll;0;L;;;;;N;;;13C3;;13C3 AB94;CHEROKEE SMALL LETTER NU;Ll;0;L;;;;;N;;;13C4;;13C4 AB95;CHEROKEE SMALL LETTER NV;Ll;0;L;;;;;N;;;13C5;;13C5 AB96;CHEROKEE SMALL LETTER QUA;Ll;0;L;;;;;N;;;13C6;;13C6 AB97;CHEROKEE SMALL LETTER QUE;Ll;0;L;;;;;N;;;13C7;;13C7 AB98;CHEROKEE SMALL LETTER QUI;Ll;0;L;;;;;N;;;13C8;;13C8 AB99;CHEROKEE SMALL LETTER QUO;Ll;0;L;;;;;N;;;13C9;;13C9 AB9A;CHEROKEE SMALL LETTER QUU;Ll;0;L;;;;;N;;;13CA;;13CA AB9B;CHEROKEE SMALL LETTER QUV;Ll;0;L;;;;;N;;;13CB;;13CB AB9C;CHEROKEE SMALL LETTER SA;Ll;0;L;;;;;N;;;13CC;;13CC AB9D;CHEROKEE SMALL LETTER S;Ll;0;L;;;;;N;;;13CD;;13CD AB9E;CHEROKEE SMALL LETTER SE;Ll;0;L;;;;;N;;;13CE;;13CE AB9F;CHEROKEE SMALL LETTER SI;Ll;0;L;;;;;N;;;13CF;;13CF ABA0;CHEROKEE SMALL LETTER SO;Ll;0;L;;;;;N;;;13D0;;13D0 ABA1;CHEROKEE SMALL LETTER SU;Ll;0;L;;;;;N;;;13D1;;13D1 ABA2;CHEROKEE SMALL LETTER SV;Ll;0;L;;;;;N;;;13D2;;13D2 ABA3;CHEROKEE SMALL LETTER DA;Ll;0;L;;;;;N;;;13D3;;13D3 ABA4;CHEROKEE SMALL LETTER TA;Ll;0;L;;;;;N;;;13D4;;13D4 ABA5;CHEROKEE SMALL LETTER DE;Ll;0;L;;;;;N;;;13D5;;13D5 ABA6;CHEROKEE SMALL LETTER TE;Ll;0;L;;;;;N;;;13D6;;13D6 ABA7;CHEROKEE SMALL LETTER DI;Ll;0;L;;;;;N;;;13D7;;13D7 ABA8;CHEROKEE SMALL LETTER TI;Ll;0;L;;;;;N;;;13D8;;13D8 ABA9;CHEROKEE SMALL LETTER DO;Ll;0;L;;;;;N;;;13D9;;13D9 ABAA;CHEROKEE SMALL LETTER DU;Ll;0;L;;;;;N;;;13DA;;13DA ABAB;CHEROKEE SMALL LETTER DV;Ll;0;L;;;;;N;;;13DB;;13DB ABAC;CHEROKEE SMALL LETTER DLA;Ll;0;L;;;;;N;;;13DC;;13DC ABAD;CHEROKEE SMALL LETTER TLA;Ll;0;L;;;;;N;;;13DD;;13DD ABAE;CHEROKEE SMALL LETTER TLE;Ll;0;L;;;;;N;;;13DE;;13DE ABAF;CHEROKEE SMALL LETTER TLI;Ll;0;L;;;;;N;;;13DF;;13DF ABB0;CHEROKEE SMALL LETTER TLO;Ll;0;L;;;;;N;;;13E0;;13E0 ABB1;CHEROKEE SMALL LETTER TLU;Ll;0;L;;;;;N;;;13E1;;13E1 ABB2;CHEROKEE SMALL LETTER TLV;Ll;0;L;;;;;N;;;13E2;;13E2 ABB3;CHEROKEE SMALL LETTER TSA;Ll;0;L;;;;;N;;;13E3;;13E3 ABB4;CHEROKEE SMALL LETTER TSE;Ll;0;L;;;;;N;;;13E4;;13E4 ABB5;CHEROKEE SMALL LETTER TSI;Ll;0;L;;;;;N;;;13E5;;13E5 ABB6;CHEROKEE SMALL LETTER TSO;Ll;0;L;;;;;N;;;13E6;;13E6 ABB7;CHEROKEE SMALL LETTER TSU;Ll;0;L;;;;;N;;;13E7;;13E7 ABB8;CHEROKEE SMALL LETTER TSV;Ll;0;L;;;;;N;;;13E8;;13E8 ABB9;CHEROKEE SMALL LETTER WA;Ll;0;L;;;;;N;;;13E9;;13E9 ABBA;CHEROKEE SMALL LETTER WE;Ll;0;L;;;;;N;;;13EA;;13EA ABBB;CHEROKEE SMALL LETTER WI;Ll;0;L;;;;;N;;;13EB;;13EB ABBC;CHEROKEE SMALL LETTER WO;Ll;0;L;;;;;N;;;13EC;;13EC ABBD;CHEROKEE SMALL LETTER WU;Ll;0;L;;;;;N;;;13ED;;13ED ABBE;CHEROKEE SMALL LETTER WV;Ll;0;L;;;;;N;;;13EE;;13EE ABBF;CHEROKEE SMALL LETTER YA;Ll;0;L;;;;;N;;;13EF;;13EF ABC0;MEETEI MAYEK LETTER KOK;Lo;0;L;;;;;N;;;;; ABC1;MEETEI MAYEK LETTER SAM;Lo;0;L;;;;;N;;;;; ABC2;MEETEI MAYEK LETTER LAI;Lo;0;L;;;;;N;;;;; ABC3;MEETEI MAYEK LETTER MIT;Lo;0;L;;;;;N;;;;; ABC4;MEETEI MAYEK LETTER PA;Lo;0;L;;;;;N;;;;; ABC5;MEETEI MAYEK LETTER NA;Lo;0;L;;;;;N;;;;; ABC6;MEETEI MAYEK LETTER CHIL;Lo;0;L;;;;;N;;;;; ABC7;MEETEI MAYEK LETTER TIL;Lo;0;L;;;;;N;;;;; ABC8;MEETEI MAYEK LETTER KHOU;Lo;0;L;;;;;N;;;;; ABC9;MEETEI MAYEK LETTER NGOU;Lo;0;L;;;;;N;;;;; ABCA;MEETEI MAYEK LETTER THOU;Lo;0;L;;;;;N;;;;; ABCB;MEETEI MAYEK LETTER WAI;Lo;0;L;;;;;N;;;;; ABCC;MEETEI MAYEK LETTER YANG;Lo;0;L;;;;;N;;;;; ABCD;MEETEI MAYEK LETTER HUK;Lo;0;L;;;;;N;;;;; ABCE;MEETEI MAYEK LETTER UN;Lo;0;L;;;;;N;;;;; ABCF;MEETEI MAYEK LETTER I;Lo;0;L;;;;;N;;;;; ABD0;MEETEI MAYEK LETTER PHAM;Lo;0;L;;;;;N;;;;; ABD1;MEETEI MAYEK LETTER ATIYA;Lo;0;L;;;;;N;;;;; ABD2;MEETEI MAYEK LETTER GOK;Lo;0;L;;;;;N;;;;; ABD3;MEETEI MAYEK LETTER JHAM;Lo;0;L;;;;;N;;;;; ABD4;MEETEI MAYEK LETTER RAI;Lo;0;L;;;;;N;;;;; ABD5;MEETEI MAYEK LETTER BA;Lo;0;L;;;;;N;;;;; ABD6;MEETEI MAYEK LETTER JIL;Lo;0;L;;;;;N;;;;; ABD7;MEETEI MAYEK LETTER DIL;Lo;0;L;;;;;N;;;;; ABD8;MEETEI MAYEK LETTER GHOU;Lo;0;L;;;;;N;;;;; ABD9;MEETEI MAYEK LETTER DHOU;Lo;0;L;;;;;N;;;;; ABDA;MEETEI MAYEK LETTER BHAM;Lo;0;L;;;;;N;;;;; ABDB;MEETEI MAYEK LETTER KOK LONSUM;Lo;0;L;;;;;N;;;;; ABDC;MEETEI MAYEK LETTER LAI LONSUM;Lo;0;L;;;;;N;;;;; ABDD;MEETEI MAYEK LETTER MIT LONSUM;Lo;0;L;;;;;N;;;;; ABDE;MEETEI MAYEK LETTER PA LONSUM;Lo;0;L;;;;;N;;;;; ABDF;MEETEI MAYEK LETTER NA LONSUM;Lo;0;L;;;;;N;;;;; ABE0;MEETEI MAYEK LETTER TIL LONSUM;Lo;0;L;;;;;N;;;;; ABE1;MEETEI MAYEK LETTER NGOU LONSUM;Lo;0;L;;;;;N;;;;; ABE2;MEETEI MAYEK LETTER I LONSUM;Lo;0;L;;;;;N;;;;; ABE3;MEETEI MAYEK VOWEL SIGN ONAP;Mc;0;L;;;;;N;;;;; ABE4;MEETEI MAYEK VOWEL SIGN INAP;Mc;0;L;;;;;N;;;;; ABE5;MEETEI MAYEK VOWEL SIGN ANAP;Mn;0;NSM;;;;;N;;;;; ABE6;MEETEI MAYEK VOWEL SIGN YENAP;Mc;0;L;;;;;N;;;;; ABE7;MEETEI MAYEK VOWEL SIGN SOUNAP;Mc;0;L;;;;;N;;;;; ABE8;MEETEI MAYEK VOWEL SIGN UNAP;Mn;0;NSM;;;;;N;;;;; ABE9;MEETEI MAYEK VOWEL SIGN CHEINAP;Mc;0;L;;;;;N;;;;; ABEA;MEETEI MAYEK VOWEL SIGN NUNG;Mc;0;L;;;;;N;;;;; ABEB;MEETEI MAYEK CHEIKHEI;Po;0;L;;;;;N;;;;; ABEC;MEETEI MAYEK LUM IYEK;Mc;0;L;;;;;N;;;;; ABED;MEETEI MAYEK APUN IYEK;Mn;9;NSM;;;;;N;;;;; ABF0;MEETEI MAYEK DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; ABF1;MEETEI MAYEK DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; ABF2;MEETEI MAYEK DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; ABF3;MEETEI MAYEK DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; ABF4;MEETEI MAYEK DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; ABF5;MEETEI MAYEK DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; ABF6;MEETEI MAYEK DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; ABF7;MEETEI MAYEK DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; ABF8;MEETEI MAYEK DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; ABF9;MEETEI MAYEK DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; AC00;;Lo;0;L;;;;;N;;;;; D7A3;;Lo;0;L;;;;;N;;;;; D7B0;HANGUL JUNGSEONG O-YEO;Lo;0;L;;;;;N;;;;; D7B1;HANGUL JUNGSEONG O-O-I;Lo;0;L;;;;;N;;;;; D7B2;HANGUL JUNGSEONG YO-A;Lo;0;L;;;;;N;;;;; D7B3;HANGUL JUNGSEONG YO-AE;Lo;0;L;;;;;N;;;;; D7B4;HANGUL JUNGSEONG YO-EO;Lo;0;L;;;;;N;;;;; D7B5;HANGUL JUNGSEONG U-YEO;Lo;0;L;;;;;N;;;;; D7B6;HANGUL JUNGSEONG U-I-I;Lo;0;L;;;;;N;;;;; D7B7;HANGUL JUNGSEONG YU-AE;Lo;0;L;;;;;N;;;;; D7B8;HANGUL JUNGSEONG YU-O;Lo;0;L;;;;;N;;;;; D7B9;HANGUL JUNGSEONG EU-A;Lo;0;L;;;;;N;;;;; D7BA;HANGUL JUNGSEONG EU-EO;Lo;0;L;;;;;N;;;;; D7BB;HANGUL JUNGSEONG EU-E;Lo;0;L;;;;;N;;;;; D7BC;HANGUL JUNGSEONG EU-O;Lo;0;L;;;;;N;;;;; D7BD;HANGUL JUNGSEONG I-YA-O;Lo;0;L;;;;;N;;;;; D7BE;HANGUL JUNGSEONG I-YAE;Lo;0;L;;;;;N;;;;; D7BF;HANGUL JUNGSEONG I-YEO;Lo;0;L;;;;;N;;;;; D7C0;HANGUL JUNGSEONG I-YE;Lo;0;L;;;;;N;;;;; D7C1;HANGUL JUNGSEONG I-O-I;Lo;0;L;;;;;N;;;;; D7C2;HANGUL JUNGSEONG I-YO;Lo;0;L;;;;;N;;;;; D7C3;HANGUL JUNGSEONG I-YU;Lo;0;L;;;;;N;;;;; D7C4;HANGUL JUNGSEONG I-I;Lo;0;L;;;;;N;;;;; D7C5;HANGUL JUNGSEONG ARAEA-A;Lo;0;L;;;;;N;;;;; D7C6;HANGUL JUNGSEONG ARAEA-E;Lo;0;L;;;;;N;;;;; D7CB;HANGUL JONGSEONG NIEUN-RIEUL;Lo;0;L;;;;;N;;;;; D7CC;HANGUL JONGSEONG NIEUN-CHIEUCH;Lo;0;L;;;;;N;;;;; D7CD;HANGUL JONGSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;; D7CE;HANGUL JONGSEONG SSANGTIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; D7CF;HANGUL JONGSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; D7D0;HANGUL JONGSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;; D7D1;HANGUL JONGSEONG TIKEUT-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; D7D2;HANGUL JONGSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;; D7D3;HANGUL JONGSEONG TIKEUT-CHIEUCH;Lo;0;L;;;;;N;;;;; D7D4;HANGUL JONGSEONG TIKEUT-THIEUTH;Lo;0;L;;;;;N;;;;; D7D5;HANGUL JONGSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; D7D6;HANGUL JONGSEONG RIEUL-KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;; D7D7;HANGUL JONGSEONG SSANGRIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; D7D8;HANGUL JONGSEONG RIEUL-MIEUM-HIEUH;Lo;0;L;;;;;N;;;;; D7D9;HANGUL JONGSEONG RIEUL-PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; D7DA;HANGUL JONGSEONG RIEUL-PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; D7DB;HANGUL JONGSEONG RIEUL-YESIEUNG;Lo;0;L;;;;;N;;;;; D7DC;HANGUL JONGSEONG RIEUL-YEORINHIEUH-HIEUH;Lo;0;L;;;;;N;;;;; D7DD;HANGUL JONGSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;; D7DE;HANGUL JONGSEONG MIEUM-NIEUN;Lo;0;L;;;;;N;;;;; D7DF;HANGUL JONGSEONG MIEUM-SSANGNIEUN;Lo;0;L;;;;;N;;;;; D7E0;HANGUL JONGSEONG SSANGMIEUM;Lo;0;L;;;;;N;;;;; D7E1;HANGUL JONGSEONG MIEUM-PIEUP-SIOS;Lo;0;L;;;;;N;;;;; D7E2;HANGUL JONGSEONG MIEUM-CIEUC;Lo;0;L;;;;;N;;;;; D7E3;HANGUL JONGSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; D7E4;HANGUL JONGSEONG PIEUP-RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;; D7E5;HANGUL JONGSEONG PIEUP-MIEUM;Lo;0;L;;;;;N;;;;; D7E6;HANGUL JONGSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;; D7E7;HANGUL JONGSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; D7E8;HANGUL JONGSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;; D7E9;HANGUL JONGSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;; D7EA;HANGUL JONGSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;; D7EB;HANGUL JONGSEONG SIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; D7EC;HANGUL JONGSEONG SSANGSIOS-KIYEOK;Lo;0;L;;;;;N;;;;; D7ED;HANGUL JONGSEONG SSANGSIOS-TIKEUT;Lo;0;L;;;;;N;;;;; D7EE;HANGUL JONGSEONG SIOS-PANSIOS;Lo;0;L;;;;;N;;;;; D7EF;HANGUL JONGSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;; D7F0;HANGUL JONGSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;; D7F1;HANGUL JONGSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; D7F2;HANGUL JONGSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;; D7F3;HANGUL JONGSEONG PANSIOS-PIEUP;Lo;0;L;;;;;N;;;;; D7F4;HANGUL JONGSEONG PANSIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; D7F5;HANGUL JONGSEONG YESIEUNG-MIEUM;Lo;0;L;;;;;N;;;;; D7F6;HANGUL JONGSEONG YESIEUNG-HIEUH;Lo;0;L;;;;;N;;;;; D7F7;HANGUL JONGSEONG CIEUC-PIEUP;Lo;0;L;;;;;N;;;;; D7F8;HANGUL JONGSEONG CIEUC-SSANGPIEUP;Lo;0;L;;;;;N;;;;; D7F9;HANGUL JONGSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;; D7FA;HANGUL JONGSEONG PHIEUPH-SIOS;Lo;0;L;;;;;N;;;;; D7FB;HANGUL JONGSEONG PHIEUPH-THIEUTH;Lo;0;L;;;;;N;;;;; D800;;Cs;0;L;;;;;N;;;;; DB7F;;Cs;0;L;;;;;N;;;;; DB80;;Cs;0;L;;;;;N;;;;; DBFF;;Cs;0;L;;;;;N;;;;; DC00;;Cs;0;L;;;;;N;;;;; DFFF;;Cs;0;L;;;;;N;;;;; E000;;Co;0;L;;;;;N;;;;; F8FF;;Co;0;L;;;;;N;;;;; F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;; F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;; F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;; F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;; F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;; F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;; F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;; F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;; F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;; F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;; F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;; F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;; F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;; F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;; F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;; F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;; F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;; F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;; F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;; F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;; F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;; F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;; F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;; F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;; F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;; F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;; F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;; F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;; F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;; F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;; F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;; F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;; F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;; F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;; F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;; F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;; F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;; F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;; F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;; F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;; F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;; F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;; F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;; F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;; F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;; F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;; F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;; F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;; F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;; F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;; F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;; F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;; F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;; F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;; F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;; F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;; F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;; F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;; F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;; F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;; F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;; F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;; F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;; F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;; F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;; F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;; F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;; F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;; F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;; F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;; F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;; F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;; F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;; F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;; F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;; F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;; F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;; F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;; F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;; F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;; F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;; F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;; F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;; F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;; F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;; F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;; F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;; F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;; F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;; F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;; F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;; F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;; F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;; F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;; F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;; F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;; F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;; F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;; F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;; F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;; F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;; F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;; F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;; F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;; F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;; F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;; F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;; F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;3;N;;;;; F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;; F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;; F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;; F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;; F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;; F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;; F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;; F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;10;N;;;;; F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;; F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;; F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;; F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;; F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;2;N;;;;; F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;; F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;; F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;; F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;; F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;; F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;; F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;; F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;; F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;; F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;; F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;; F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;; F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;; F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;; F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;; F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;; F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;; F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;; F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;; F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;; F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;; F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;; F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;; F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;; F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;; F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;; F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;; F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;; F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;; F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;; F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;; F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;; F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;; F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;; F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;; F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;; F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;; F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;; F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;; F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;; F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;; F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;; F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;; F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;; F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;; F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;; F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;; F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;; F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;; F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;; F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;; F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;; F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;; F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;; F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;; F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;; F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;; F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;0;N;;;;; F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;; F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;; F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;; F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;; F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;; F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;; F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;; F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;; F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;; F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;; F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;; F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;; F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;; F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;; F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;; F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;; F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;; F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;; F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;; F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;; F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;; F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;; F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;; F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;; F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;; F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;; F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;; F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;; F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;; F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;; F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;6;N;;;;; F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;; F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;6;N;;;;; F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;; F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;; F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;; F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;; F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;; F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;; F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;; F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;; F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;; F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;; F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;; F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;; F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;; F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;; F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;; F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;; F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;; F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;; F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;; F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;; F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;; F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;; F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;; F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;; F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;; F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;; F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;; F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;; F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;; F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;; F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;; F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;; F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;; F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;; F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;; F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;; F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;; F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;; F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;; F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;; F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;; F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;10;N;;;;; F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;; F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;; FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;; FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;; FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;; FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;; FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;; FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;; FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;; FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;; FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;; FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;; FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;; FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;; FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;; FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;; FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;; FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;; FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;; FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;; FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;; FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;; FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;; FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;; FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;; FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;; FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;; FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;; FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;; FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;; FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;; FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;; FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;; FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;;;; FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;; FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;; FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;; FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;;;; FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;; FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;; FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;; FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;; FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;; FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;; FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;; FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;; FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;; FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;; FA2E;CJK COMPATIBILITY IDEOGRAPH-FA2E;Lo;0;L;90DE;;;;N;;;;; FA2F;CJK COMPATIBILITY IDEOGRAPH-FA2F;Lo;0;L;96B7;;;;N;;;;; FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;; FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;; FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;; FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;; FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;; FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;; FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;; FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;; FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;; FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;; FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;; FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;; FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;; FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;; FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;; FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;; FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;; FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;; FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;; FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;; FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;; FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;; FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;; FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;; FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;; FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;; FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;; FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;; FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;; FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;; FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;; FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;; FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;; FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;; FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;; FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;; FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;; FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;; FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;; FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;; FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;; FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;; FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;; FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;; FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;; FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;; FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;; FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;; FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;; FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;; FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;; FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;; FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;; FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;; FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;; FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;; FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;; FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;; FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;; FA6B;CJK COMPATIBILITY IDEOGRAPH-FA6B;Lo;0;L;6075;;;;N;;;;; FA6C;CJK COMPATIBILITY IDEOGRAPH-FA6C;Lo;0;L;242EE;;;;N;;;;; FA6D;CJK COMPATIBILITY IDEOGRAPH-FA6D;Lo;0;L;8218;;;;N;;;;; FA70;CJK COMPATIBILITY IDEOGRAPH-FA70;Lo;0;L;4E26;;;;N;;;;; FA71;CJK COMPATIBILITY IDEOGRAPH-FA71;Lo;0;L;51B5;;;;N;;;;; FA72;CJK COMPATIBILITY IDEOGRAPH-FA72;Lo;0;L;5168;;;;N;;;;; FA73;CJK COMPATIBILITY IDEOGRAPH-FA73;Lo;0;L;4F80;;;;N;;;;; FA74;CJK COMPATIBILITY IDEOGRAPH-FA74;Lo;0;L;5145;;;;N;;;;; FA75;CJK COMPATIBILITY IDEOGRAPH-FA75;Lo;0;L;5180;;;;N;;;;; FA76;CJK COMPATIBILITY IDEOGRAPH-FA76;Lo;0;L;52C7;;;;N;;;;; FA77;CJK COMPATIBILITY IDEOGRAPH-FA77;Lo;0;L;52FA;;;;N;;;;; FA78;CJK COMPATIBILITY IDEOGRAPH-FA78;Lo;0;L;559D;;;;N;;;;; FA79;CJK COMPATIBILITY IDEOGRAPH-FA79;Lo;0;L;5555;;;;N;;;;; FA7A;CJK COMPATIBILITY IDEOGRAPH-FA7A;Lo;0;L;5599;;;;N;;;;; FA7B;CJK COMPATIBILITY IDEOGRAPH-FA7B;Lo;0;L;55E2;;;;N;;;;; FA7C;CJK COMPATIBILITY IDEOGRAPH-FA7C;Lo;0;L;585A;;;;N;;;;; FA7D;CJK COMPATIBILITY IDEOGRAPH-FA7D;Lo;0;L;58B3;;;;N;;;;; FA7E;CJK COMPATIBILITY IDEOGRAPH-FA7E;Lo;0;L;5944;;;;N;;;;; FA7F;CJK COMPATIBILITY IDEOGRAPH-FA7F;Lo;0;L;5954;;;;N;;;;; FA80;CJK COMPATIBILITY IDEOGRAPH-FA80;Lo;0;L;5A62;;;;N;;;;; FA81;CJK COMPATIBILITY IDEOGRAPH-FA81;Lo;0;L;5B28;;;;N;;;;; FA82;CJK COMPATIBILITY IDEOGRAPH-FA82;Lo;0;L;5ED2;;;;N;;;;; FA83;CJK COMPATIBILITY IDEOGRAPH-FA83;Lo;0;L;5ED9;;;;N;;;;; FA84;CJK COMPATIBILITY IDEOGRAPH-FA84;Lo;0;L;5F69;;;;N;;;;; FA85;CJK COMPATIBILITY IDEOGRAPH-FA85;Lo;0;L;5FAD;;;;N;;;;; FA86;CJK COMPATIBILITY IDEOGRAPH-FA86;Lo;0;L;60D8;;;;N;;;;; FA87;CJK COMPATIBILITY IDEOGRAPH-FA87;Lo;0;L;614E;;;;N;;;;; FA88;CJK COMPATIBILITY IDEOGRAPH-FA88;Lo;0;L;6108;;;;N;;;;; FA89;CJK COMPATIBILITY IDEOGRAPH-FA89;Lo;0;L;618E;;;;N;;;;; FA8A;CJK COMPATIBILITY IDEOGRAPH-FA8A;Lo;0;L;6160;;;;N;;;;; FA8B;CJK COMPATIBILITY IDEOGRAPH-FA8B;Lo;0;L;61F2;;;;N;;;;; FA8C;CJK COMPATIBILITY IDEOGRAPH-FA8C;Lo;0;L;6234;;;;N;;;;; FA8D;CJK COMPATIBILITY IDEOGRAPH-FA8D;Lo;0;L;63C4;;;;N;;;;; FA8E;CJK COMPATIBILITY IDEOGRAPH-FA8E;Lo;0;L;641C;;;;N;;;;; FA8F;CJK COMPATIBILITY IDEOGRAPH-FA8F;Lo;0;L;6452;;;;N;;;;; FA90;CJK COMPATIBILITY IDEOGRAPH-FA90;Lo;0;L;6556;;;;N;;;;; FA91;CJK COMPATIBILITY IDEOGRAPH-FA91;Lo;0;L;6674;;;;N;;;;; FA92;CJK COMPATIBILITY IDEOGRAPH-FA92;Lo;0;L;6717;;;;N;;;;; FA93;CJK COMPATIBILITY IDEOGRAPH-FA93;Lo;0;L;671B;;;;N;;;;; FA94;CJK COMPATIBILITY IDEOGRAPH-FA94;Lo;0;L;6756;;;;N;;;;; FA95;CJK COMPATIBILITY IDEOGRAPH-FA95;Lo;0;L;6B79;;;;N;;;;; FA96;CJK COMPATIBILITY IDEOGRAPH-FA96;Lo;0;L;6BBA;;;;N;;;;; FA97;CJK COMPATIBILITY IDEOGRAPH-FA97;Lo;0;L;6D41;;;;N;;;;; FA98;CJK COMPATIBILITY IDEOGRAPH-FA98;Lo;0;L;6EDB;;;;N;;;;; FA99;CJK COMPATIBILITY IDEOGRAPH-FA99;Lo;0;L;6ECB;;;;N;;;;; FA9A;CJK COMPATIBILITY IDEOGRAPH-FA9A;Lo;0;L;6F22;;;;N;;;;; FA9B;CJK COMPATIBILITY IDEOGRAPH-FA9B;Lo;0;L;701E;;;;N;;;;; FA9C;CJK COMPATIBILITY IDEOGRAPH-FA9C;Lo;0;L;716E;;;;N;;;;; FA9D;CJK COMPATIBILITY IDEOGRAPH-FA9D;Lo;0;L;77A7;;;;N;;;;; FA9E;CJK COMPATIBILITY IDEOGRAPH-FA9E;Lo;0;L;7235;;;;N;;;;; FA9F;CJK COMPATIBILITY IDEOGRAPH-FA9F;Lo;0;L;72AF;;;;N;;;;; FAA0;CJK COMPATIBILITY IDEOGRAPH-FAA0;Lo;0;L;732A;;;;N;;;;; FAA1;CJK COMPATIBILITY IDEOGRAPH-FAA1;Lo;0;L;7471;;;;N;;;;; FAA2;CJK COMPATIBILITY IDEOGRAPH-FAA2;Lo;0;L;7506;;;;N;;;;; FAA3;CJK COMPATIBILITY IDEOGRAPH-FAA3;Lo;0;L;753B;;;;N;;;;; FAA4;CJK COMPATIBILITY IDEOGRAPH-FAA4;Lo;0;L;761D;;;;N;;;;; FAA5;CJK COMPATIBILITY IDEOGRAPH-FAA5;Lo;0;L;761F;;;;N;;;;; FAA6;CJK COMPATIBILITY IDEOGRAPH-FAA6;Lo;0;L;76CA;;;;N;;;;; FAA7;CJK COMPATIBILITY IDEOGRAPH-FAA7;Lo;0;L;76DB;;;;N;;;;; FAA8;CJK COMPATIBILITY IDEOGRAPH-FAA8;Lo;0;L;76F4;;;;N;;;;; FAA9;CJK COMPATIBILITY IDEOGRAPH-FAA9;Lo;0;L;774A;;;;N;;;;; FAAA;CJK COMPATIBILITY IDEOGRAPH-FAAA;Lo;0;L;7740;;;;N;;;;; FAAB;CJK COMPATIBILITY IDEOGRAPH-FAAB;Lo;0;L;78CC;;;;N;;;;; FAAC;CJK COMPATIBILITY IDEOGRAPH-FAAC;Lo;0;L;7AB1;;;;N;;;;; FAAD;CJK COMPATIBILITY IDEOGRAPH-FAAD;Lo;0;L;7BC0;;;;N;;;;; FAAE;CJK COMPATIBILITY IDEOGRAPH-FAAE;Lo;0;L;7C7B;;;;N;;;;; FAAF;CJK COMPATIBILITY IDEOGRAPH-FAAF;Lo;0;L;7D5B;;;;N;;;;; FAB0;CJK COMPATIBILITY IDEOGRAPH-FAB0;Lo;0;L;7DF4;;;;N;;;;; FAB1;CJK COMPATIBILITY IDEOGRAPH-FAB1;Lo;0;L;7F3E;;;;N;;;;; FAB2;CJK COMPATIBILITY IDEOGRAPH-FAB2;Lo;0;L;8005;;;;N;;;;; FAB3;CJK COMPATIBILITY IDEOGRAPH-FAB3;Lo;0;L;8352;;;;N;;;;; FAB4;CJK COMPATIBILITY IDEOGRAPH-FAB4;Lo;0;L;83EF;;;;N;;;;; FAB5;CJK COMPATIBILITY IDEOGRAPH-FAB5;Lo;0;L;8779;;;;N;;;;; FAB6;CJK COMPATIBILITY IDEOGRAPH-FAB6;Lo;0;L;8941;;;;N;;;;; FAB7;CJK COMPATIBILITY IDEOGRAPH-FAB7;Lo;0;L;8986;;;;N;;;;; FAB8;CJK COMPATIBILITY IDEOGRAPH-FAB8;Lo;0;L;8996;;;;N;;;;; FAB9;CJK COMPATIBILITY IDEOGRAPH-FAB9;Lo;0;L;8ABF;;;;N;;;;; FABA;CJK COMPATIBILITY IDEOGRAPH-FABA;Lo;0;L;8AF8;;;;N;;;;; FABB;CJK COMPATIBILITY IDEOGRAPH-FABB;Lo;0;L;8ACB;;;;N;;;;; FABC;CJK COMPATIBILITY IDEOGRAPH-FABC;Lo;0;L;8B01;;;;N;;;;; FABD;CJK COMPATIBILITY IDEOGRAPH-FABD;Lo;0;L;8AFE;;;;N;;;;; FABE;CJK COMPATIBILITY IDEOGRAPH-FABE;Lo;0;L;8AED;;;;N;;;;; FABF;CJK COMPATIBILITY IDEOGRAPH-FABF;Lo;0;L;8B39;;;;N;;;;; FAC0;CJK COMPATIBILITY IDEOGRAPH-FAC0;Lo;0;L;8B8A;;;;N;;;;; FAC1;CJK COMPATIBILITY IDEOGRAPH-FAC1;Lo;0;L;8D08;;;;N;;;;; FAC2;CJK COMPATIBILITY IDEOGRAPH-FAC2;Lo;0;L;8F38;;;;N;;;;; FAC3;CJK COMPATIBILITY IDEOGRAPH-FAC3;Lo;0;L;9072;;;;N;;;;; FAC4;CJK COMPATIBILITY IDEOGRAPH-FAC4;Lo;0;L;9199;;;;N;;;;; FAC5;CJK COMPATIBILITY IDEOGRAPH-FAC5;Lo;0;L;9276;;;;N;;;;; FAC6;CJK COMPATIBILITY IDEOGRAPH-FAC6;Lo;0;L;967C;;;;N;;;;; FAC7;CJK COMPATIBILITY IDEOGRAPH-FAC7;Lo;0;L;96E3;;;;N;;;;; FAC8;CJK COMPATIBILITY IDEOGRAPH-FAC8;Lo;0;L;9756;;;;N;;;;; FAC9;CJK COMPATIBILITY IDEOGRAPH-FAC9;Lo;0;L;97DB;;;;N;;;;; FACA;CJK COMPATIBILITY IDEOGRAPH-FACA;Lo;0;L;97FF;;;;N;;;;; FACB;CJK COMPATIBILITY IDEOGRAPH-FACB;Lo;0;L;980B;;;;N;;;;; FACC;CJK COMPATIBILITY IDEOGRAPH-FACC;Lo;0;L;983B;;;;N;;;;; FACD;CJK COMPATIBILITY IDEOGRAPH-FACD;Lo;0;L;9B12;;;;N;;;;; FACE;CJK COMPATIBILITY IDEOGRAPH-FACE;Lo;0;L;9F9C;;;;N;;;;; FACF;CJK COMPATIBILITY IDEOGRAPH-FACF;Lo;0;L;2284A;;;;N;;;;; FAD0;CJK COMPATIBILITY IDEOGRAPH-FAD0;Lo;0;L;22844;;;;N;;;;; FAD1;CJK COMPATIBILITY IDEOGRAPH-FAD1;Lo;0;L;233D5;;;;N;;;;; FAD2;CJK COMPATIBILITY IDEOGRAPH-FAD2;Lo;0;L;3B9D;;;;N;;;;; FAD3;CJK COMPATIBILITY IDEOGRAPH-FAD3;Lo;0;L;4018;;;;N;;;;; FAD4;CJK COMPATIBILITY IDEOGRAPH-FAD4;Lo;0;L;4039;;;;N;;;;; FAD5;CJK COMPATIBILITY IDEOGRAPH-FAD5;Lo;0;L;25249;;;;N;;;;; FAD6;CJK COMPATIBILITY IDEOGRAPH-FAD6;Lo;0;L;25CD0;;;;N;;;;; FAD7;CJK COMPATIBILITY IDEOGRAPH-FAD7;Lo;0;L;27ED3;;;;N;;;;; FAD8;CJK COMPATIBILITY IDEOGRAPH-FAD8;Lo;0;L;9F43;;;;N;;;;; FAD9;CJK COMPATIBILITY IDEOGRAPH-FAD9;Lo;0;L;9F8E;;;;N;;;;; FB00;LATIN SMALL LIGATURE FF;Ll;0;L; 0066 0066;;;;N;;;;; FB01;LATIN SMALL LIGATURE FI;Ll;0;L; 0066 0069;;;;N;;;;; FB02;LATIN SMALL LIGATURE FL;Ll;0;L; 0066 006C;;;;N;;;;; FB03;LATIN SMALL LIGATURE FFI;Ll;0;L; 0066 0066 0069;;;;N;;;;; FB04;LATIN SMALL LIGATURE FFL;Ll;0;L; 0066 0066 006C;;;;N;;;;; FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L; 017F 0074;;;;N;;;;; FB06;LATIN SMALL LIGATURE ST;Ll;0;L; 0073 0074;;;;N;;;;; FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L; 0574 0576;;;;N;;;;; FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L; 0574 0565;;;;N;;;;; FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L; 0574 056B;;;;N;;;;; FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L; 057E 0576;;;;N;;;;; FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L; 0574 056D;;;;N;;;;; FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;; FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;; FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;; FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R; 05E2;;;;N;;;;; FB21;HEBREW LETTER WIDE ALEF;Lo;0;R; 05D0;;;;N;;;;; FB22;HEBREW LETTER WIDE DALET;Lo;0;R; 05D3;;;;N;;;;; FB23;HEBREW LETTER WIDE HE;Lo;0;R; 05D4;;;;N;;;;; FB24;HEBREW LETTER WIDE KAF;Lo;0;R; 05DB;;;;N;;;;; FB25;HEBREW LETTER WIDE LAMED;Lo;0;R; 05DC;;;;N;;;;; FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R; 05DD;;;;N;;;;; FB27;HEBREW LETTER WIDE RESH;Lo;0;R; 05E8;;;;N;;;;; FB28;HEBREW LETTER WIDE TAV;Lo;0;R; 05EA;;;;N;;;;; FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ES; 002B;;;;N;;;;; FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;; FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;; FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;; FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;; FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;; FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;; FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;; FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;; FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;; FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;; FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;; FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;; FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;; FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;; FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;; FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;; FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;; FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;; FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;; FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;; FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;; FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;; FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;; FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;; FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;; FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;; FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;; FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;; FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;; FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;; FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;; FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;; FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R; 05D0 05DC;;;;N;;;;; FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL; 0671;;;;N;;;;; FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL; 0671;;;;N;;;;; FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL; 067B;;;;N;;;;; FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL; 067B;;;;N;;;;; FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL; 067B;;;;N;;;;; FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL; 067B;;;;N;;;;; FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL; 067E;;;;N;;;;; FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL; 067E;;;;N;;;;; FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL; 067E;;;;N;;;;; FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL; 067E;;;;N;;;;; FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL; 0680;;;;N;;;;; FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL; 0680;;;;N;;;;; FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL; 0680;;;;N;;;;; FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL; 0680;;;;N;;;;; FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL; 067A;;;;N;;;;; FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL; 067A;;;;N;;;;; FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL; 067A;;;;N;;;;; FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL; 067A;;;;N;;;;; FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL; 067F;;;;N;;;;; FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL; 067F;;;;N;;;;; FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL; 067F;;;;N;;;;; FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL; 067F;;;;N;;;;; FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL; 0679;;;;N;;;;; FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL; 0679;;;;N;;;;; FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL; 0679;;;;N;;;;; FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL; 0679;;;;N;;;;; FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL; 06A4;;;;N;;;;; FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL; 06A4;;;;N;;;;; FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL; 06A4;;;;N;;;;; FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL; 06A4;;;;N;;;;; FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL; 06A6;;;;N;;;;; FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL; 06A6;;;;N;;;;; FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL; 06A6;;;;N;;;;; FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL; 06A6;;;;N;;;;; FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL; 0684;;;;N;;;;; FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL; 0684;;;;N;;;;; FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL; 0684;;;;N;;;;; FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL; 0684;;;;N;;;;; FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL; 0683;;;;N;;;;; FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL; 0683;;;;N;;;;; FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL; 0683;;;;N;;;;; FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL; 0683;;;;N;;;;; FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL; 0686;;;;N;;;;; FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL; 0686;;;;N;;;;; FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL; 0686;;;;N;;;;; FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL; 0686;;;;N;;;;; FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL; 0687;;;;N;;;;; FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL; 0687;;;;N;;;;; FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL; 0687;;;;N;;;;; FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL; 0687;;;;N;;;;; FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL; 068D;;;;N;;;;; FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL; 068D;;;;N;;;;; FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL; 068C;;;;N;;;;; FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL; 068C;;;;N;;;;; FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL; 068E;;;;N;;;;; FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL; 068E;;;;N;;;;; FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL; 0688;;;;N;;;;; FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL; 0688;;;;N;;;;; FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL; 0698;;;;N;;;;; FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL; 0698;;;;N;;;;; FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL; 0691;;;;N;;;;; FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL; 0691;;;;N;;;;; FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL; 06A9;;;;N;;;;; FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL; 06A9;;;;N;;;;; FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL; 06A9;;;;N;;;;; FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL; 06A9;;;;N;;;;; FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL; 06AF;;;;N;;;;; FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL; 06AF;;;;N;;;;; FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL; 06AF;;;;N;;;;; FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL; 06AF;;;;N;;;;; FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL; 06B3;;;;N;;;;; FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL; 06B3;;;;N;;;;; FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL; 06B3;;;;N;;;;; FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL; 06B3;;;;N;;;;; FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL; 06B1;;;;N;;;;; FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL; 06B1;;;;N;;;;; FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL; 06B1;;;;N;;;;; FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL; 06B1;;;;N;;;;; FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL; 06BA;;;;N;;;;; FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL; 06BA;;;;N;;;;; FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL; 06BB;;;;N;;;;; FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL; 06BB;;;;N;;;;; FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL; 06BB;;;;N;;;;; FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL; 06BB;;;;N;;;;; FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL; 06C0;;;;N;;;;; FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL; 06C0;;;;N;;;;; FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL; 06C1;;;;N;;;;; FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL; 06C1;;;;N;;;;; FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL; 06C1;;;;N;;;;; FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL; 06C1;;;;N;;;;; FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL; 06BE;;;;N;;;;; FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL; 06BE;;;;N;;;;; FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL; 06BE;;;;N;;;;; FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL; 06BE;;;;N;;;;; FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL; 06D2;;;;N;;;;; FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL; 06D2;;;;N;;;;; FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 06D3;;;;N;;;;; FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 06D3;;;;N;;;;; FBB2;ARABIC SYMBOL DOT ABOVE;Sk;0;AL;;;;;N;;;;; FBB3;ARABIC SYMBOL DOT BELOW;Sk;0;AL;;;;;N;;;;; FBB4;ARABIC SYMBOL TWO DOTS ABOVE;Sk;0;AL;;;;;N;;;;; FBB5;ARABIC SYMBOL TWO DOTS BELOW;Sk;0;AL;;;;;N;;;;; FBB6;ARABIC SYMBOL THREE DOTS ABOVE;Sk;0;AL;;;;;N;;;;; FBB7;ARABIC SYMBOL THREE DOTS BELOW;Sk;0;AL;;;;;N;;;;; FBB8;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS ABOVE;Sk;0;AL;;;;;N;;;;; FBB9;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS BELOW;Sk;0;AL;;;;;N;;;;; FBBA;ARABIC SYMBOL FOUR DOTS ABOVE;Sk;0;AL;;;;;N;;;;; FBBB;ARABIC SYMBOL FOUR DOTS BELOW;Sk;0;AL;;;;;N;;;;; FBBC;ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW;Sk;0;AL;;;;;N;;;;; FBBD;ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE;Sk;0;AL;;;;;N;;;;; FBBE;ARABIC SYMBOL TWO DOTS VERTICALLY BELOW;Sk;0;AL;;;;;N;;;;; FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;; FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;; FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;; FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL; 06C7;;;;N;;;;; FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL; 06C7;;;;N;;;;; FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL; 06C6;;;;N;;;;; FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL; 06C6;;;;N;;;;; FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL; 06C8;;;;N;;;;; FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL; 06C8;;;;N;;;;; FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0677;;;;N;;;;; FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL; 06CB;;;;N;;;;; FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL; 06CB;;;;N;;;;; FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL; 06C5;;;;N;;;;; FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL; 06C5;;;;N;;;;; FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL; 06C9;;;;N;;;;; FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL; 06C9;;;;N;;;;; FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL; 06D0;;;;N;;;;; FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL; 06D0;;;;N;;;;; FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL; 06D0;;;;N;;;;; FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL; 06D0;;;;N;;;;; FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL; 0649;;;;N;;;;; FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL; 0649;;;;N;;;;; FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL; 0626 0627;;;;N;;;;; FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL; 0626 0627;;;;N;;;;; FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL; 0626 06D5;;;;N;;;;; FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL; 0626 06D5;;;;N;;;;; FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL; 0626 0648;;;;N;;;;; FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL; 0626 0648;;;;N;;;;; FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL; 0626 06C7;;;;N;;;;; FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL; 0626 06C7;;;;N;;;;; FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL; 0626 06C6;;;;N;;;;; FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL; 0626 06C6;;;;N;;;;; FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL; 0626 06C8;;;;N;;;;; FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL; 0626 06C8;;;;N;;;;; FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL; 0626 06D0;;;;N;;;;; FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL; 0626 06D0;;;;N;;;;; FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL; 0626 06D0;;;;N;;;;; FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0626 0649;;;;N;;;;; FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0626 0649;;;;N;;;;; FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL; 0626 0649;;;;N;;;;; FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL; 06CC;;;;N;;;;; FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL; 06CC;;;;N;;;;; FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL; 06CC;;;;N;;;;; FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL; 06CC;;;;N;;;;; FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL; 0626 062C;;;;N;;;;; FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL; 0626 062D;;;;N;;;;; FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL; 0626 0645;;;;N;;;;; FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0626 0649;;;;N;;;;; FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL; 0626 064A;;;;N;;;;; FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL; 0628 062C;;;;N;;;;; FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL; 0628 062D;;;;N;;;;; FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL; 0628 062E;;;;N;;;;; FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL; 0628 0645;;;;N;;;;; FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0628 0649;;;;N;;;;; FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL; 0628 064A;;;;N;;;;; FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL; 062A 062C;;;;N;;;;; FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL; 062A 062D;;;;N;;;;; FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL; 062A 062E;;;;N;;;;; FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL; 062A 0645;;;;N;;;;; FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 062A 0649;;;;N;;;;; FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL; 062A 064A;;;;N;;;;; FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL; 062B 062C;;;;N;;;;; FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL; 062B 0645;;;;N;;;;; FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 062B 0649;;;;N;;;;; FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL; 062B 064A;;;;N;;;;; FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL; 062C 062D;;;;N;;;;; FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL; 062C 0645;;;;N;;;;; FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL; 062D 062C;;;;N;;;;; FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL; 062D 0645;;;;N;;;;; FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL; 062E 062C;;;;N;;;;; FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL; 062E 062D;;;;N;;;;; FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL; 062E 0645;;;;N;;;;; FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL; 0633 062C;;;;N;;;;; FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL; 0633 062D;;;;N;;;;; FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL; 0633 062E;;;;N;;;;; FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL; 0633 0645;;;;N;;;;; FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL; 0635 062D;;;;N;;;;; FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL; 0635 0645;;;;N;;;;; FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL; 0636 062C;;;;N;;;;; FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL; 0636 062D;;;;N;;;;; FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL; 0636 062E;;;;N;;;;; FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL; 0636 0645;;;;N;;;;; FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL; 0637 062D;;;;N;;;;; FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL; 0637 0645;;;;N;;;;; FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL; 0638 0645;;;;N;;;;; FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL; 0639 062C;;;;N;;;;; FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL; 0639 0645;;;;N;;;;; FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL; 063A 062C;;;;N;;;;; FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL; 063A 0645;;;;N;;;;; FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL; 0641 062C;;;;N;;;;; FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL; 0641 062D;;;;N;;;;; FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL; 0641 062E;;;;N;;;;; FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL; 0641 0645;;;;N;;;;; FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0641 0649;;;;N;;;;; FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL; 0641 064A;;;;N;;;;; FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL; 0642 062D;;;;N;;;;; FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL; 0642 0645;;;;N;;;;; FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0642 0649;;;;N;;;;; FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL; 0642 064A;;;;N;;;;; FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL; 0643 0627;;;;N;;;;; FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL; 0643 062C;;;;N;;;;; FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL; 0643 062D;;;;N;;;;; FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL; 0643 062E;;;;N;;;;; FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL; 0643 0644;;;;N;;;;; FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL; 0643 0645;;;;N;;;;; FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0643 0649;;;;N;;;;; FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL; 0643 064A;;;;N;;;;; FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL; 0644 062C;;;;N;;;;; FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL; 0644 062D;;;;N;;;;; FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL; 0644 062E;;;;N;;;;; FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL; 0644 0645;;;;N;;;;; FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0644 0649;;;;N;;;;; FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL; 0644 064A;;;;N;;;;; FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL; 0645 062C;;;;N;;;;; FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL; 0645 062D;;;;N;;;;; FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL; 0645 062E;;;;N;;;;; FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL; 0645 0645;;;;N;;;;; FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0645 0649;;;;N;;;;; FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL; 0645 064A;;;;N;;;;; FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL; 0646 062C;;;;N;;;;; FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL; 0646 062D;;;;N;;;;; FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL; 0646 062E;;;;N;;;;; FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL; 0646 0645;;;;N;;;;; FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0646 0649;;;;N;;;;; FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL; 0646 064A;;;;N;;;;; FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL; 0647 062C;;;;N;;;;; FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL; 0647 0645;;;;N;;;;; FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0647 0649;;;;N;;;;; FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL; 0647 064A;;;;N;;;;; FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL; 064A 062C;;;;N;;;;; FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL; 064A 062D;;;;N;;;;; FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL; 064A 062E;;;;N;;;;; FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL; 064A 0645;;;;N;;;;; FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 064A 0649;;;;N;;;;; FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL; 064A 064A;;;;N;;;;; FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL; 0630 0670;;;;N;;;;; FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL; 0631 0670;;;;N;;;;; FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL; 0649 0670;;;;N;;;;; FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL; 0020 064C 0651;;;;N;;;;; FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL; 0020 064D 0651;;;;N;;;;; FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL; 0020 064E 0651;;;;N;;;;; FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL; 0020 064F 0651;;;;N;;;;; FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL; 0020 0650 0651;;;;N;;;;; FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL; 0020 0651 0670;;;;N;;;;; FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL; 0626 0631;;;;N;;;;; FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL; 0626 0632;;;;N;;;;; FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL; 0626 0645;;;;N;;;;; FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL; 0626 0646;;;;N;;;;; FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0626 0649;;;;N;;;;; FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL; 0626 064A;;;;N;;;;; FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL; 0628 0631;;;;N;;;;; FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL; 0628 0632;;;;N;;;;; FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL; 0628 0645;;;;N;;;;; FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL; 0628 0646;;;;N;;;;; FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0628 0649;;;;N;;;;; FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL; 0628 064A;;;;N;;;;; FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL; 062A 0631;;;;N;;;;; FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL; 062A 0632;;;;N;;;;; FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL; 062A 0645;;;;N;;;;; FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL; 062A 0646;;;;N;;;;; FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062A 0649;;;;N;;;;; FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL; 062A 064A;;;;N;;;;; FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL; 062B 0631;;;;N;;;;; FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL; 062B 0632;;;;N;;;;; FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL; 062B 0645;;;;N;;;;; FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL; 062B 0646;;;;N;;;;; FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062B 0649;;;;N;;;;; FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL; 062B 064A;;;;N;;;;; FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0641 0649;;;;N;;;;; FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL; 0641 064A;;;;N;;;;; FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0642 0649;;;;N;;;;; FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL; 0642 064A;;;;N;;;;; FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL; 0643 0627;;;;N;;;;; FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL; 0643 0644;;;;N;;;;; FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL; 0643 0645;;;;N;;;;; FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0643 0649;;;;N;;;;; FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL; 0643 064A;;;;N;;;;; FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL; 0644 0645;;;;N;;;;; FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0644 0649;;;;N;;;;; FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL; 0644 064A;;;;N;;;;; FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL; 0645 0627;;;;N;;;;; FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0645 0645;;;;N;;;;; FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL; 0646 0631;;;;N;;;;; FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL; 0646 0632;;;;N;;;;; FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL; 0646 0645;;;;N;;;;; FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL; 0646 0646;;;;N;;;;; FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0646 0649;;;;N;;;;; FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL; 0646 064A;;;;N;;;;; FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL; 0649 0670;;;;N;;;;; FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL; 064A 0631;;;;N;;;;; FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL; 064A 0632;;;;N;;;;; FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL; 064A 0645;;;;N;;;;; FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL; 064A 0646;;;;N;;;;; FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 064A 0649;;;;N;;;;; FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL; 064A 064A;;;;N;;;;; FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL; 0626 062C;;;;N;;;;; FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL; 0626 062D;;;;N;;;;; FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL; 0626 062E;;;;N;;;;; FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL; 0626 0645;;;;N;;;;; FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL; 0626 0647;;;;N;;;;; FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL; 0628 062C;;;;N;;;;; FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL; 0628 062D;;;;N;;;;; FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL; 0628 062E;;;;N;;;;; FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL; 0628 0645;;;;N;;;;; FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL; 0628 0647;;;;N;;;;; FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL; 062A 062C;;;;N;;;;; FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL; 062A 062D;;;;N;;;;; FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL; 062A 062E;;;;N;;;;; FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL; 062A 0645;;;;N;;;;; FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL; 062A 0647;;;;N;;;;; FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL; 062B 0645;;;;N;;;;; FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL; 062C 062D;;;;N;;;;; FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 062C 0645;;;;N;;;;; FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL; 062D 062C;;;;N;;;;; FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL; 062D 0645;;;;N;;;;; FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL; 062E 062C;;;;N;;;;; FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 062E 0645;;;;N;;;;; FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL; 0633 062C;;;;N;;;;; FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL; 0633 062D;;;;N;;;;; FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL; 0633 062E;;;;N;;;;; FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL; 0633 0645;;;;N;;;;; FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL; 0635 062D;;;;N;;;;; FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL; 0635 062E;;;;N;;;;; FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL; 0635 0645;;;;N;;;;; FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL; 0636 062C;;;;N;;;;; FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL; 0636 062D;;;;N;;;;; FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL; 0636 062E;;;;N;;;;; FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL; 0636 0645;;;;N;;;;; FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL; 0637 062D;;;;N;;;;; FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL; 0638 0645;;;;N;;;;; FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL; 0639 062C;;;;N;;;;; FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL; 0639 0645;;;;N;;;;; FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL; 063A 062C;;;;N;;;;; FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL; 063A 0645;;;;N;;;;; FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL; 0641 062C;;;;N;;;;; FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL; 0641 062D;;;;N;;;;; FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL; 0641 062E;;;;N;;;;; FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL; 0641 0645;;;;N;;;;; FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL; 0642 062D;;;;N;;;;; FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL; 0642 0645;;;;N;;;;; FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL; 0643 062C;;;;N;;;;; FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL; 0643 062D;;;;N;;;;; FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL; 0643 062E;;;;N;;;;; FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL; 0643 0644;;;;N;;;;; FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL; 0643 0645;;;;N;;;;; FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL; 0644 062C;;;;N;;;;; FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL; 0644 062D;;;;N;;;;; FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL; 0644 062E;;;;N;;;;; FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL; 0644 0645;;;;N;;;;; FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL; 0644 0647;;;;N;;;;; FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0645 062C;;;;N;;;;; FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL; 0645 062D;;;;N;;;;; FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL; 0645 062E;;;;N;;;;; FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0645 0645;;;;N;;;;; FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL; 0646 062C;;;;N;;;;; FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL; 0646 062D;;;;N;;;;; FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL; 0646 062E;;;;N;;;;; FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL; 0646 0645;;;;N;;;;; FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL; 0646 0647;;;;N;;;;; FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL; 0647 062C;;;;N;;;;; FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL; 0647 0645;;;;N;;;;; FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL; 0647 0670;;;;N;;;;; FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL; 064A 062C;;;;N;;;;; FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL; 064A 062D;;;;N;;;;; FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL; 064A 062E;;;;N;;;;; FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL; 064A 0645;;;;N;;;;; FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL; 064A 0647;;;;N;;;;; FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL; 0626 0645;;;;N;;;;; FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL; 0626 0647;;;;N;;;;; FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL; 0628 0645;;;;N;;;;; FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL; 0628 0647;;;;N;;;;; FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL; 062A 0645;;;;N;;;;; FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL; 062A 0647;;;;N;;;;; FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL; 062B 0645;;;;N;;;;; FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL; 062B 0647;;;;N;;;;; FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL; 0633 0645;;;;N;;;;; FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL; 0633 0647;;;;N;;;;; FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL; 0634 0645;;;;N;;;;; FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL; 0634 0647;;;;N;;;;; FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL; 0643 0644;;;;N;;;;; FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL; 0643 0645;;;;N;;;;; FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL; 0644 0645;;;;N;;;;; FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL; 0646 0645;;;;N;;;;; FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL; 0646 0647;;;;N;;;;; FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL; 064A 0645;;;;N;;;;; FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL; 064A 0647;;;;N;;;;; FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL; 0640 064E 0651;;;;N;;;;; FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL; 0640 064F 0651;;;;N;;;;; FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL; 0640 0650 0651;;;;N;;;;; FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0637 0649;;;;N;;;;; FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL; 0637 064A;;;;N;;;;; FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0639 0649;;;;N;;;;; FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL; 0639 064A;;;;N;;;;; FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 063A 0649;;;;N;;;;; FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL; 063A 064A;;;;N;;;;; FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0633 0649;;;;N;;;;; FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL; 0633 064A;;;;N;;;;; FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0634 0649;;;;N;;;;; FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL; 0634 064A;;;;N;;;;; FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 062D 0649;;;;N;;;;; FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL; 062D 064A;;;;N;;;;; FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 062C 0649;;;;N;;;;; FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL; 062C 064A;;;;N;;;;; FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 062E 0649;;;;N;;;;; FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL; 062E 064A;;;;N;;;;; FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0635 0649;;;;N;;;;; FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL; 0635 064A;;;;N;;;;; FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0636 0649;;;;N;;;;; FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL; 0636 064A;;;;N;;;;; FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL; 0634 062C;;;;N;;;;; FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL; 0634 062D;;;;N;;;;; FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL; 0634 062E;;;;N;;;;; FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL; 0634 0645;;;;N;;;;; FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL; 0634 0631;;;;N;;;;; FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL; 0633 0631;;;;N;;;;; FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL; 0635 0631;;;;N;;;;; FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL; 0636 0631;;;;N;;;;; FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0637 0649;;;;N;;;;; FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL; 0637 064A;;;;N;;;;; FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0639 0649;;;;N;;;;; FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL; 0639 064A;;;;N;;;;; FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 063A 0649;;;;N;;;;; FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL; 063A 064A;;;;N;;;;; FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0633 0649;;;;N;;;;; FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL; 0633 064A;;;;N;;;;; FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0634 0649;;;;N;;;;; FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL; 0634 064A;;;;N;;;;; FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062D 0649;;;;N;;;;; FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL; 062D 064A;;;;N;;;;; FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062C 0649;;;;N;;;;; FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL; 062C 064A;;;;N;;;;; FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062E 0649;;;;N;;;;; FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL; 062E 064A;;;;N;;;;; FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0635 0649;;;;N;;;;; FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL; 0635 064A;;;;N;;;;; FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0636 0649;;;;N;;;;; FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL; 0636 064A;;;;N;;;;; FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL; 0634 062C;;;;N;;;;; FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL; 0634 062D;;;;N;;;;; FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL; 0634 062E;;;;N;;;;; FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL; 0634 0645;;;;N;;;;; FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL; 0634 0631;;;;N;;;;; FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL; 0633 0631;;;;N;;;;; FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL; 0635 0631;;;;N;;;;; FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL; 0636 0631;;;;N;;;;; FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL; 0634 062C;;;;N;;;;; FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL; 0634 062D;;;;N;;;;; FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL; 0634 062E;;;;N;;;;; FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL; 0634 0645;;;;N;;;;; FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL; 0633 0647;;;;N;;;;; FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL; 0634 0647;;;;N;;;;; FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL; 0637 0645;;;;N;;;;; FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL; 0633 062C;;;;N;;;;; FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL; 0633 062D;;;;N;;;;; FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL; 0633 062E;;;;N;;;;; FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL; 0634 062C;;;;N;;;;; FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL; 0634 062D;;;;N;;;;; FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL; 0634 062E;;;;N;;;;; FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL; 0637 0645;;;;N;;;;; FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL; 0638 0645;;;;N;;;;; FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL; 0627 064B;;;;N;;;;; FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL; 0627 064B;;;;N;;;;; FD3E;ORNATE LEFT PARENTHESIS;Pe;0;ON;;;;;N;;;;; FD3F;ORNATE RIGHT PARENTHESIS;Ps;0;ON;;;;;N;;;;; FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 062A 062C 0645;;;;N;;;;; FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL; 062A 062D 062C;;;;N;;;;; FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL; 062A 062D 062C;;;;N;;;;; FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL; 062A 062D 0645;;;;N;;;;; FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 062A 062E 0645;;;;N;;;;; FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 062A 0645 062C;;;;N;;;;; FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 062A 0645 062D;;;;N;;;;; FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL; 062A 0645 062E;;;;N;;;;; FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL; 062C 0645 062D;;;;N;;;;; FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 062C 0645 062D;;;;N;;;;; FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 062D 0645 064A;;;;N;;;;; FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062D 0645 0649;;;;N;;;;; FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL; 0633 062D 062C;;;;N;;;;; FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL; 0633 062C 062D;;;;N;;;;; FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0633 062C 0649;;;;N;;;;; FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL; 0633 0645 062D;;;;N;;;;; FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 0633 0645 062D;;;;N;;;;; FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0633 0645 062C;;;;N;;;;; FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0633 0645 0645;;;;N;;;;; FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0633 0645 0645;;;;N;;;;; FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL; 0635 062D 062D;;;;N;;;;; FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL; 0635 062D 062D;;;;N;;;;; FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0635 0645 0645;;;;N;;;;; FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL; 0634 062D 0645;;;;N;;;;; FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL; 0634 062D 0645;;;;N;;;;; FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0634 062C 064A;;;;N;;;;; FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL; 0634 0645 062E;;;;N;;;;; FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL; 0634 0645 062E;;;;N;;;;; FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0634 0645 0645;;;;N;;;;; FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0634 0645 0645;;;;N;;;;; FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0636 062D 0649;;;;N;;;;; FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL; 0636 062E 0645;;;;N;;;;; FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0636 062E 0645;;;;N;;;;; FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL; 0637 0645 062D;;;;N;;;;; FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 0637 0645 062D;;;;N;;;;; FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0637 0645 0645;;;;N;;;;; FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0637 0645 064A;;;;N;;;;; FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL; 0639 062C 0645;;;;N;;;;; FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0639 0645 0645;;;;N;;;;; FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0639 0645 0645;;;;N;;;;; FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0639 0645 0649;;;;N;;;;; FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 063A 0645 0645;;;;N;;;;; FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 063A 0645 064A;;;;N;;;;; FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 063A 0645 0649;;;;N;;;;; FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL; 0641 062E 0645;;;;N;;;;; FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0641 062E 0645;;;;N;;;;; FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL; 0642 0645 062D;;;;N;;;;; FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0642 0645 0645;;;;N;;;;; FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL; 0644 062D 0645;;;;N;;;;; FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0644 062D 064A;;;;N;;;;; FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0644 062D 0649;;;;N;;;;; FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0644 062C 062C;;;;N;;;;; FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL; 0644 062C 062C;;;;N;;;;; FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL; 0644 062E 0645;;;;N;;;;; FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0644 062E 0645;;;;N;;;;; FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL; 0644 0645 062D;;;;N;;;;; FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 0644 0645 062D;;;;N;;;;; FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL; 0645 062D 062C;;;;N;;;;; FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062D 0645;;;;N;;;;; FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0645 062D 064A;;;;N;;;;; FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL; 0645 062C 062D;;;;N;;;;; FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062C 0645;;;;N;;;;; FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL; 0645 062E 062C;;;;N;;;;; FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062E 0645;;;;N;;;;; FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL; 0645 062C 062E;;;;N;;;;; FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0647 0645 062C;;;;N;;;;; FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0647 0645 0645;;;;N;;;;; FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL; 0646 062D 0645;;;;N;;;;; FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0646 062D 0649;;;;N;;;;; FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL; 0646 062C 0645;;;;N;;;;; FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0646 062C 0645;;;;N;;;;; FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0646 062C 0649;;;;N;;;;; FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0646 0645 064A;;;;N;;;;; FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0646 0645 0649;;;;N;;;;; FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 064A 0645 0645;;;;N;;;;; FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 064A 0645 0645;;;;N;;;;; FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 0628 062E 064A;;;;N;;;;; FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 062A 062C 064A;;;;N;;;;; FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062A 062C 0649;;;;N;;;;; FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 062A 062E 064A;;;;N;;;;; FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062A 062E 0649;;;;N;;;;; FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 062A 0645 064A;;;;N;;;;; FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062A 0645 0649;;;;N;;;;; FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 062C 0645 064A;;;;N;;;;; FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062C 062D 0649;;;;N;;;;; FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 062C 0645 0649;;;;N;;;;; FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL; 0633 062E 0649;;;;N;;;;; FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0635 062D 064A;;;;N;;;;; FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0634 062D 064A;;;;N;;;;; FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0636 062D 064A;;;;N;;;;; FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0644 062C 064A;;;;N;;;;; FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0644 0645 064A;;;;N;;;;; FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 064A 062D 064A;;;;N;;;;; FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 064A 062C 064A;;;;N;;;;; FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 064A 0645 064A;;;;N;;;;; FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0645 0645 064A;;;;N;;;;; FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0642 0645 064A;;;;N;;;;; FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0646 062D 064A;;;;N;;;;; FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL; 0642 0645 062D;;;;N;;;;; FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL; 0644 062D 0645;;;;N;;;;; FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0639 0645 064A;;;;N;;;;; FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0643 0645 064A;;;;N;;;;; FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL; 0646 062C 062D;;;;N;;;;; FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 0645 062E 064A;;;;N;;;;; FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0644 062C 0645;;;;N;;;;; FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL; 0643 0645 0645;;;;N;;;;; FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL; 0644 062C 0645;;;;N;;;;; FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL; 0646 062C 062D;;;;N;;;;; FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 062C 062D 064A;;;;N;;;;; FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 062D 062C 064A;;;;N;;;;; FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0645 062C 064A;;;;N;;;;; FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL; 0641 0645 064A;;;;N;;;;; FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL; 0628 062D 064A;;;;N;;;;; FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0643 0645 0645;;;;N;;;;; FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0639 062C 0645;;;;N;;;;; FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0635 0645 0645;;;;N;;;;; FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 0633 062E 064A;;;;N;;;;; FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0646 062C 064A;;;;N;;;;; FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0635 0644 06D2;;;;N;;;;; FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0642 0644 06D2;;;;N;;;;; FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL; 0627 0644 0644 0647;;;;N;;;;; FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL; 0627 0643 0628 0631;;;;N;;;;; FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL; 0645 062D 0645 062F;;;;N;;;;; FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL; 0635 0644 0639 0645;;;;N;;;;; FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL; 0631 0633 0648 0644;;;;N;;;;; FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL; 0639 0644 064A 0647;;;;N;;;;; FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL; 0648 0633 0644 0645;;;;N;;;;; FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL; 0635 0644 0649;;;;N;;;;; FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL; 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;; FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL; 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;; FDFC;RIAL SIGN;Sc;0;AL; 0631 06CC 0627 0644;;;;N;;;;; FDFD;ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM;So;0;ON;;;;;N;;;;; FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;; FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;; FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;; FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;; FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;; FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;; FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;; FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;; FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;; FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;; FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;; FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;; FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;; FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;; FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;; FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;; FE10;PRESENTATION FORM FOR VERTICAL COMMA;Po;0;ON; 002C;;;;N;;;;; FE11;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA;Po;0;ON; 3001;;;;N;;;;; FE12;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP;Po;0;ON; 3002;;;;N;;;;; FE13;PRESENTATION FORM FOR VERTICAL COLON;Po;0;ON; 003A;;;;N;;;;; FE14;PRESENTATION FORM FOR VERTICAL SEMICOLON;Po;0;ON; 003B;;;;N;;;;; FE15;PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK;Po;0;ON; 0021;;;;N;;;;; FE16;PRESENTATION FORM FOR VERTICAL QUESTION MARK;Po;0;ON; 003F;;;;N;;;;; FE17;PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET;Ps;0;ON; 3016;;;;N;;;;; FE18;PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET;Pe;0;ON; 3017;;;;N;;;;; FE19;PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS;Po;0;ON; 2026;;;;N;;;;; FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;; FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;; FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; FE24;COMBINING MACRON LEFT HALF;Mn;230;NSM;;;;;N;;;;; FE25;COMBINING MACRON RIGHT HALF;Mn;230;NSM;;;;;N;;;;; FE26;COMBINING CONJOINING MACRON;Mn;230;NSM;;;;;N;;;;; FE27;COMBINING LIGATURE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE28;COMBINING LIGATURE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE29;COMBINING TILDE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE2A;COMBINING TILDE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE2B;COMBINING MACRON LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE2C;COMBINING MACRON RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; FE2D;COMBINING CONJOINING MACRON BELOW;Mn;220;NSM;;;;;N;;;;; FE2E;COMBINING CYRILLIC TITLO LEFT HALF;Mn;230;NSM;;;;;N;;;;; FE2F;COMBINING CYRILLIC TITLO RIGHT HALF;Mn;230;NSM;;;;;N;;;;; FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON; 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;; FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON; 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;; FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON; 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;; FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON; 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;; FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON; 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;; FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON; 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;; FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON; 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;; FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON; 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;; FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON; 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;; FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON; 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;; FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON; 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;; FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON; 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;; FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON; 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;; FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON; 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;; FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON; 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;; FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON; 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;; FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON; 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;; FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON; 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;; FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON; 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;; FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON; 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;; FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON; 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;; FE45;SESAME DOT;Po;0;ON;;;;;N;;;;; FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;; FE47;PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET;Ps;0;ON; 005B;;;;N;;;;; FE48;PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET;Pe;0;ON; 005D;;;;N;;;;; FE49;DASHED OVERLINE;Po;0;ON; 203E;;;;N;SPACING DASHED OVERSCORE;;;; FE4A;CENTRELINE OVERLINE;Po;0;ON; 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;; FE4B;WAVY OVERLINE;Po;0;ON; 203E;;;;N;SPACING WAVY OVERSCORE;;;; FE4C;DOUBLE WAVY OVERLINE;Po;0;ON; 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;; FE4D;DASHED LOW LINE;Pc;0;ON; 005F;;;;N;SPACING DASHED UNDERSCORE;;;; FE4E;CENTRELINE LOW LINE;Pc;0;ON; 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;; FE4F;WAVY LOW LINE;Pc;0;ON; 005F;;;;N;SPACING WAVY UNDERSCORE;;;; FE50;SMALL COMMA;Po;0;CS; 002C;;;;N;;;;; FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON; 3001;;;;N;;;;; FE52;SMALL FULL STOP;Po;0;CS; 002E;;;;N;SMALL PERIOD;;;; FE54;SMALL SEMICOLON;Po;0;ON; 003B;;;;N;;;;; FE55;SMALL COLON;Po;0;CS; 003A;;;;N;;;;; FE56;SMALL QUESTION MARK;Po;0;ON; 003F;;;;N;;;;; FE57;SMALL EXCLAMATION MARK;Po;0;ON; 0021;;;;N;;;;; FE58;SMALL EM DASH;Pd;0;ON; 2014;;;;N;;;;; FE59;SMALL LEFT PARENTHESIS;Ps;0;ON; 0028;;;;Y;SMALL OPENING PARENTHESIS;;;; FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON; 0029;;;;Y;SMALL CLOSING PARENTHESIS;;;; FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON; 007B;;;;Y;SMALL OPENING CURLY BRACKET;;;; FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON; 007D;;;;Y;SMALL CLOSING CURLY BRACKET;;;; FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON; 3014;;;;Y;SMALL OPENING TORTOISE SHELL BRACKET;;;; FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON; 3015;;;;Y;SMALL CLOSING TORTOISE SHELL BRACKET;;;; FE5F;SMALL NUMBER SIGN;Po;0;ET; 0023;;;;N;;;;; FE60;SMALL AMPERSAND;Po;0;ON; 0026;;;;N;;;;; FE61;SMALL ASTERISK;Po;0;ON; 002A;;;;N;;;;; FE62;SMALL PLUS SIGN;Sm;0;ES; 002B;;;;N;;;;; FE63;SMALL HYPHEN-MINUS;Pd;0;ES; 002D;;;;N;;;;; FE64;SMALL LESS-THAN SIGN;Sm;0;ON; 003C;;;;Y;;;;; FE65;SMALL GREATER-THAN SIGN;Sm;0;ON; 003E;;;;Y;;;;; FE66;SMALL EQUALS SIGN;Sm;0;ON; 003D;;;;N;;;;; FE68;SMALL REVERSE SOLIDUS;Po;0;ON; 005C;;;;N;SMALL BACKSLASH;;;; FE69;SMALL DOLLAR SIGN;Sc;0;ET; 0024;;;;N;;;;; FE6A;SMALL PERCENT SIGN;Po;0;ET; 0025;;;;N;;;;; FE6B;SMALL COMMERCIAL AT;Po;0;ON; 0040;;;;N;;;;; FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL; 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;; FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL; 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;; FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL; 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;; FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;; FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL; 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;; FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL; 0020 064E;;;;N;ARABIC SPACING FATHAH;;;; FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL; 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;; FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL; 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;; FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL; 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;; FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL; 0020 0650;;;;N;ARABIC SPACING KASRAH;;;; FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL; 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;; FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL; 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;; FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL; 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;; FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL; 0020 0652;;;;N;ARABIC SPACING SUKUN;;;; FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL; 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;; FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL; 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;; FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL; 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;; FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL; 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;; FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;; FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;; FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;; FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;; FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL; 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;; FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL; 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;; FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;; FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;; FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL; 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;; FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL; 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;; FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL; 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;; FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL; 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;; FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;; FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;; FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;; FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;; FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL; 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;; FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL; 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;; FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;; FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;; FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;; FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;; FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;; FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;; FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;; FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;; FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;; FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;; FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;; FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;; FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;; FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;; FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;; FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;; FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;; FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;; FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;; FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;; FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL; 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;; FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL; 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;; FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL; 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;; FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL; 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;; FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL; 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;; FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL; 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;; FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL; 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;; FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL; 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;; FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;; FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;; FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;; FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;; FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;; FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;; FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;; FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;; FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;; FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;; FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;; FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;; FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;; FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;; FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;; FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;; FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL; 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;; FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL; 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;; FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL; 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;; FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL; 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;; FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL; 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;; FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL; 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;; FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL; 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;; FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL; 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;; FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;; FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;; FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;; FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;; FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;; FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;; FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;; FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;; FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;; FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;; FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;; FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;; FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;; FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;; FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;; FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;; FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;; FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;; FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;; FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;; FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;; FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;; FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;; FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;; FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;; FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;; FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;; FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;; FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;; FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;; FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;; FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;; FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;; FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;; FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;; FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;; FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL; 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;; FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL; 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;; FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;; FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL; 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;; FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;; FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;; FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;; FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;; FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL; 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;; FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL; 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;; FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL; 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL; 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL; 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;; FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL; 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;; FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON; 0021;;;;N;;;;; FF02;FULLWIDTH QUOTATION MARK;Po;0;ON; 0022;;;;N;;;;; FF03;FULLWIDTH NUMBER SIGN;Po;0;ET; 0023;;;;N;;;;; FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET; 0024;;;;N;;;;; FF05;FULLWIDTH PERCENT SIGN;Po;0;ET; 0025;;;;N;;;;; FF06;FULLWIDTH AMPERSAND;Po;0;ON; 0026;;;;N;;;;; FF07;FULLWIDTH APOSTROPHE;Po;0;ON; 0027;;;;N;;;;; FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON; 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;; FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON; 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;; FF0A;FULLWIDTH ASTERISK;Po;0;ON; 002A;;;;N;;;;; FF0B;FULLWIDTH PLUS SIGN;Sm;0;ES; 002B;;;;N;;;;; FF0C;FULLWIDTH COMMA;Po;0;CS; 002C;;;;N;;;;; FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ES; 002D;;;;N;;;;; FF0E;FULLWIDTH FULL STOP;Po;0;CS; 002E;;;;N;FULLWIDTH PERIOD;;;; FF0F;FULLWIDTH SOLIDUS;Po;0;CS; 002F;;;;N;FULLWIDTH SLASH;;;; FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; FF11;FULLWIDTH DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; FF12;FULLWIDTH DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; FF13;FULLWIDTH DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; FF16;FULLWIDTH DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; FF19;FULLWIDTH DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; FF1A;FULLWIDTH COLON;Po;0;CS; 003A;;;;N;;;;; FF1B;FULLWIDTH SEMICOLON;Po;0;ON; 003B;;;;N;;;;; FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON; 003C;;;;Y;;;;; FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON; 003D;;;;N;;;;; FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON; 003E;;;;Y;;;;; FF1F;FULLWIDTH QUESTION MARK;Po;0;ON; 003F;;;;N;;;;; FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON; 0040;;;;N;;;;; FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L; 0041;;;;N;;;;FF41; FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L; 0042;;;;N;;;;FF42; FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L; 0043;;;;N;;;;FF43; FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L; 0044;;;;N;;;;FF44; FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L; 0045;;;;N;;;;FF45; FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L; 0046;;;;N;;;;FF46; FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L; 0047;;;;N;;;;FF47; FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L; 0048;;;;N;;;;FF48; FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L; 0049;;;;N;;;;FF49; FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L; 004A;;;;N;;;;FF4A; FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L; 004B;;;;N;;;;FF4B; FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L; 004C;;;;N;;;;FF4C; FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L; 004D;;;;N;;;;FF4D; FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L; 004E;;;;N;;;;FF4E; FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L; 004F;;;;N;;;;FF4F; FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L; 0050;;;;N;;;;FF50; FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L; 0051;;;;N;;;;FF51; FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L; 0052;;;;N;;;;FF52; FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L; 0053;;;;N;;;;FF53; FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L; 0054;;;;N;;;;FF54; FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L; 0055;;;;N;;;;FF55; FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L; 0056;;;;N;;;;FF56; FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L; 0057;;;;N;;;;FF57; FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L; 0058;;;;N;;;;FF58; FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L; 0059;;;;N;;;;FF59; FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L; 005A;;;;N;;;;FF5A; FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON; 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;; FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON; 005C;;;;N;FULLWIDTH BACKSLASH;;;; FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON; 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;; FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON; 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;; FF3F;FULLWIDTH LOW LINE;Pc;0;ON; 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;; FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON; 0060;;;;N;FULLWIDTH SPACING GRAVE;;;; FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L; 0061;;;;N;;;FF21;;FF21 FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L; 0062;;;;N;;;FF22;;FF22 FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L; 0063;;;;N;;;FF23;;FF23 FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L; 0064;;;;N;;;FF24;;FF24 FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L; 0065;;;;N;;;FF25;;FF25 FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L; 0066;;;;N;;;FF26;;FF26 FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L; 0067;;;;N;;;FF27;;FF27 FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L; 0068;;;;N;;;FF28;;FF28 FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L; 0069;;;;N;;;FF29;;FF29 FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L; 006A;;;;N;;;FF2A;;FF2A FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L; 006B;;;;N;;;FF2B;;FF2B FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L; 006C;;;;N;;;FF2C;;FF2C FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L; 006D;;;;N;;;FF2D;;FF2D FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L; 006E;;;;N;;;FF2E;;FF2E FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L; 006F;;;;N;;;FF2F;;FF2F FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L; 0070;;;;N;;;FF30;;FF30 FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L; 0071;;;;N;;;FF31;;FF31 FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L; 0072;;;;N;;;FF32;;FF32 FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L; 0073;;;;N;;;FF33;;FF33 FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L; 0074;;;;N;;;FF34;;FF34 FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L; 0075;;;;N;;;FF35;;FF35 FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L; 0076;;;;N;;;FF36;;FF36 FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L; 0077;;;;N;;;FF37;;FF37 FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L; 0078;;;;N;;;FF38;;FF38 FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L; 0079;;;;N;;;FF39;;FF39 FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L; 007A;;;;N;;;FF3A;;FF3A FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON; 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;; FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON; 007C;;;;N;FULLWIDTH VERTICAL BAR;;;; FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON; 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;; FF5E;FULLWIDTH TILDE;Sm;0;ON; 007E;;;;N;FULLWIDTH SPACING TILDE;;;; FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON; 2985;;;;Y;;;;; FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON; 2986;;;;Y;;;;; FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON; 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;; FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON; 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;; FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON; 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;; FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON; 3001;;;;N;;;;; FF65;HALFWIDTH KATAKANA MIDDLE DOT;Po;0;ON; 30FB;;;;N;;;;; FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L; 30F2;;;;N;;;;; FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L; 30A1;;;;N;;;;; FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L; 30A3;;;;N;;;;; FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L; 30A5;;;;N;;;;; FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L; 30A7;;;;N;;;;; FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L; 30A9;;;;N;;;;; FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L; 30E3;;;;N;;;;; FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L; 30E5;;;;N;;;;; FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L; 30E7;;;;N;;;;; FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L; 30C3;;;;N;;;;; FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L; 30FC;;;;N;;;;; FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L; 30A2;;;;N;;;;; FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L; 30A4;;;;N;;;;; FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L; 30A6;;;;N;;;;; FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L; 30A8;;;;N;;;;; FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L; 30AA;;;;N;;;;; FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L; 30AB;;;;N;;;;; FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L; 30AD;;;;N;;;;; FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L; 30AF;;;;N;;;;; FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L; 30B1;;;;N;;;;; FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L; 30B3;;;;N;;;;; FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L; 30B5;;;;N;;;;; FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L; 30B7;;;;N;;;;; FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L; 30B9;;;;N;;;;; FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L; 30BB;;;;N;;;;; FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L; 30BD;;;;N;;;;; FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L; 30BF;;;;N;;;;; FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L; 30C1;;;;N;;;;; FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L; 30C4;;;;N;;;;; FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L; 30C6;;;;N;;;;; FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L; 30C8;;;;N;;;;; FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L; 30CA;;;;N;;;;; FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L; 30CB;;;;N;;;;; FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L; 30CC;;;;N;;;;; FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L; 30CD;;;;N;;;;; FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L; 30CE;;;;N;;;;; FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L; 30CF;;;;N;;;;; FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L; 30D2;;;;N;;;;; FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L; 30D5;;;;N;;;;; FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L; 30D8;;;;N;;;;; FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L; 30DB;;;;N;;;;; FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L; 30DE;;;;N;;;;; FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L; 30DF;;;;N;;;;; FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L; 30E0;;;;N;;;;; FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L; 30E1;;;;N;;;;; FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L; 30E2;;;;N;;;;; FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L; 30E4;;;;N;;;;; FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L; 30E6;;;;N;;;;; FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L; 30E8;;;;N;;;;; FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L; 30E9;;;;N;;;;; FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L; 30EA;;;;N;;;;; FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L; 30EB;;;;N;;;;; FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L; 30EC;;;;N;;;;; FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L; 30ED;;;;N;;;;; FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L; 30EF;;;;N;;;;; FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L; 30F3;;;;N;;;;; FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L; 3099;;;;N;;;;; FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L; 309A;;;;N;;;;; FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L; 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;; FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L; 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;; FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L; 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;; FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;; FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L; 3134;;;;N;;;;; FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L; 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;; FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L; 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;; FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L; 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;; FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L; 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;; FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L; 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;; FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L; 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;; FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L; 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;; FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L; 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;; FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L; 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;; FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L; 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;; FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L; 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;; FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L; 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;; FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L; 3141;;;;N;;;;; FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L; 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;; FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L; 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;; FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L; 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;; FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L; 3145;;;;N;;;;; FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L; 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;; FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L; 3147;;;;N;;;;; FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L; 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;; FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L; 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;; FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L; 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;; FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L; 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;; FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L; 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;; FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L; 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;; FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L; 314E;;;;N;;;;; FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L; 314F;;;;N;;;;; FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L; 3150;;;;N;;;;; FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L; 3151;;;;N;;;;; FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L; 3152;;;;N;;;;; FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L; 3153;;;;N;;;;; FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L; 3154;;;;N;;;;; FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L; 3155;;;;N;;;;; FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L; 3156;;;;N;;;;; FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L; 3157;;;;N;;;;; FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L; 3158;;;;N;;;;; FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L; 3159;;;;N;;;;; FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L; 315A;;;;N;;;;; FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L; 315B;;;;N;;;;; FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L; 315C;;;;N;;;;; FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L; 315D;;;;N;;;;; FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L; 315E;;;;N;;;;; FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L; 315F;;;;N;;;;; FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L; 3160;;;;N;;;;; FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L; 3161;;;;N;;;;; FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L; 3162;;;;N;;;;; FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L; 3163;;;;N;;;;; FFE0;FULLWIDTH CENT SIGN;Sc;0;ET; 00A2;;;;N;;;;; FFE1;FULLWIDTH POUND SIGN;Sc;0;ET; 00A3;;;;N;;;;; FFE2;FULLWIDTH NOT SIGN;Sm;0;ON; 00AC;;;;N;;;;; FFE3;FULLWIDTH MACRON;Sk;0;ON; 00AF;;;;N;FULLWIDTH SPACING MACRON;;;; FFE4;FULLWIDTH BROKEN BAR;So;0;ON; 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;; FFE5;FULLWIDTH YEN SIGN;Sc;0;ET; 00A5;;;;N;;;;; FFE6;FULLWIDTH WON SIGN;Sc;0;ET; 20A9;;;;N;;;;; FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON; 2502;;;;N;;;;; FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON; 2190;;;;N;;;;; FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON; 2191;;;;N;;;;; FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON; 2192;;;;N;;;;; FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON; 2193;;;;N;;;;; FFED;HALFWIDTH BLACK SQUARE;So;0;ON; 25A0;;;;N;;;;; FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON; 25CB;;;;N;;;;; FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;ON;;;;;N;;;;; FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;ON;;;;;N;;;;; FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;ON;;;;;N;;;;; FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10000;LINEAR B SYLLABLE B008 A;Lo;0;L;;;;;N;;;;; 10001;LINEAR B SYLLABLE B038 E;Lo;0;L;;;;;N;;;;; 10002;LINEAR B SYLLABLE B028 I;Lo;0;L;;;;;N;;;;; 10003;LINEAR B SYLLABLE B061 O;Lo;0;L;;;;;N;;;;; 10004;LINEAR B SYLLABLE B010 U;Lo;0;L;;;;;N;;;;; 10005;LINEAR B SYLLABLE B001 DA;Lo;0;L;;;;;N;;;;; 10006;LINEAR B SYLLABLE B045 DE;Lo;0;L;;;;;N;;;;; 10007;LINEAR B SYLLABLE B007 DI;Lo;0;L;;;;;N;;;;; 10008;LINEAR B SYLLABLE B014 DO;Lo;0;L;;;;;N;;;;; 10009;LINEAR B SYLLABLE B051 DU;Lo;0;L;;;;;N;;;;; 1000A;LINEAR B SYLLABLE B057 JA;Lo;0;L;;;;;N;;;;; 1000B;LINEAR B SYLLABLE B046 JE;Lo;0;L;;;;;N;;;;; 1000D;LINEAR B SYLLABLE B036 JO;Lo;0;L;;;;;N;;;;; 1000E;LINEAR B SYLLABLE B065 JU;Lo;0;L;;;;;N;;;;; 1000F;LINEAR B SYLLABLE B077 KA;Lo;0;L;;;;;N;;;;; 10010;LINEAR B SYLLABLE B044 KE;Lo;0;L;;;;;N;;;;; 10011;LINEAR B SYLLABLE B067 KI;Lo;0;L;;;;;N;;;;; 10012;LINEAR B SYLLABLE B070 KO;Lo;0;L;;;;;N;;;;; 10013;LINEAR B SYLLABLE B081 KU;Lo;0;L;;;;;N;;;;; 10014;LINEAR B SYLLABLE B080 MA;Lo;0;L;;;;;N;;;;; 10015;LINEAR B SYLLABLE B013 ME;Lo;0;L;;;;;N;;;;; 10016;LINEAR B SYLLABLE B073 MI;Lo;0;L;;;;;N;;;;; 10017;LINEAR B SYLLABLE B015 MO;Lo;0;L;;;;;N;;;;; 10018;LINEAR B SYLLABLE B023 MU;Lo;0;L;;;;;N;;;;; 10019;LINEAR B SYLLABLE B006 NA;Lo;0;L;;;;;N;;;;; 1001A;LINEAR B SYLLABLE B024 NE;Lo;0;L;;;;;N;;;;; 1001B;LINEAR B SYLLABLE B030 NI;Lo;0;L;;;;;N;;;;; 1001C;LINEAR B SYLLABLE B052 NO;Lo;0;L;;;;;N;;;;; 1001D;LINEAR B SYLLABLE B055 NU;Lo;0;L;;;;;N;;;;; 1001E;LINEAR B SYLLABLE B003 PA;Lo;0;L;;;;;N;;;;; 1001F;LINEAR B SYLLABLE B072 PE;Lo;0;L;;;;;N;;;;; 10020;LINEAR B SYLLABLE B039 PI;Lo;0;L;;;;;N;;;;; 10021;LINEAR B SYLLABLE B011 PO;Lo;0;L;;;;;N;;;;; 10022;LINEAR B SYLLABLE B050 PU;Lo;0;L;;;;;N;;;;; 10023;LINEAR B SYLLABLE B016 QA;Lo;0;L;;;;;N;;;;; 10024;LINEAR B SYLLABLE B078 QE;Lo;0;L;;;;;N;;;;; 10025;LINEAR B SYLLABLE B021 QI;Lo;0;L;;;;;N;;;;; 10026;LINEAR B SYLLABLE B032 QO;Lo;0;L;;;;;N;;;;; 10028;LINEAR B SYLLABLE B060 RA;Lo;0;L;;;;;N;;;;; 10029;LINEAR B SYLLABLE B027 RE;Lo;0;L;;;;;N;;;;; 1002A;LINEAR B SYLLABLE B053 RI;Lo;0;L;;;;;N;;;;; 1002B;LINEAR B SYLLABLE B002 RO;Lo;0;L;;;;;N;;;;; 1002C;LINEAR B SYLLABLE B026 RU;Lo;0;L;;;;;N;;;;; 1002D;LINEAR B SYLLABLE B031 SA;Lo;0;L;;;;;N;;;;; 1002E;LINEAR B SYLLABLE B009 SE;Lo;0;L;;;;;N;;;;; 1002F;LINEAR B SYLLABLE B041 SI;Lo;0;L;;;;;N;;;;; 10030;LINEAR B SYLLABLE B012 SO;Lo;0;L;;;;;N;;;;; 10031;LINEAR B SYLLABLE B058 SU;Lo;0;L;;;;;N;;;;; 10032;LINEAR B SYLLABLE B059 TA;Lo;0;L;;;;;N;;;;; 10033;LINEAR B SYLLABLE B004 TE;Lo;0;L;;;;;N;;;;; 10034;LINEAR B SYLLABLE B037 TI;Lo;0;L;;;;;N;;;;; 10035;LINEAR B SYLLABLE B005 TO;Lo;0;L;;;;;N;;;;; 10036;LINEAR B SYLLABLE B069 TU;Lo;0;L;;;;;N;;;;; 10037;LINEAR B SYLLABLE B054 WA;Lo;0;L;;;;;N;;;;; 10038;LINEAR B SYLLABLE B075 WE;Lo;0;L;;;;;N;;;;; 10039;LINEAR B SYLLABLE B040 WI;Lo;0;L;;;;;N;;;;; 1003A;LINEAR B SYLLABLE B042 WO;Lo;0;L;;;;;N;;;;; 1003C;LINEAR B SYLLABLE B017 ZA;Lo;0;L;;;;;N;;;;; 1003D;LINEAR B SYLLABLE B074 ZE;Lo;0;L;;;;;N;;;;; 1003F;LINEAR B SYLLABLE B020 ZO;Lo;0;L;;;;;N;;;;; 10040;LINEAR B SYLLABLE B025 A2;Lo;0;L;;;;;N;;;;; 10041;LINEAR B SYLLABLE B043 A3;Lo;0;L;;;;;N;;;;; 10042;LINEAR B SYLLABLE B085 AU;Lo;0;L;;;;;N;;;;; 10043;LINEAR B SYLLABLE B071 DWE;Lo;0;L;;;;;N;;;;; 10044;LINEAR B SYLLABLE B090 DWO;Lo;0;L;;;;;N;;;;; 10045;LINEAR B SYLLABLE B048 NWA;Lo;0;L;;;;;N;;;;; 10046;LINEAR B SYLLABLE B029 PU2;Lo;0;L;;;;;N;;;;; 10047;LINEAR B SYLLABLE B062 PTE;Lo;0;L;;;;;N;;;;; 10048;LINEAR B SYLLABLE B076 RA2;Lo;0;L;;;;;N;;;;; 10049;LINEAR B SYLLABLE B033 RA3;Lo;0;L;;;;;N;;;;; 1004A;LINEAR B SYLLABLE B068 RO2;Lo;0;L;;;;;N;;;;; 1004B;LINEAR B SYLLABLE B066 TA2;Lo;0;L;;;;;N;;;;; 1004C;LINEAR B SYLLABLE B087 TWE;Lo;0;L;;;;;N;;;;; 1004D;LINEAR B SYLLABLE B091 TWO;Lo;0;L;;;;;N;;;;; 10050;LINEAR B SYMBOL B018;Lo;0;L;;;;;N;;;;; 10051;LINEAR B SYMBOL B019;Lo;0;L;;;;;N;;;;; 10052;LINEAR B SYMBOL B022;Lo;0;L;;;;;N;;;;; 10053;LINEAR B SYMBOL B034;Lo;0;L;;;;;N;;;;; 10054;LINEAR B SYMBOL B047;Lo;0;L;;;;;N;;;;; 10055;LINEAR B SYMBOL B049;Lo;0;L;;;;;N;;;;; 10056;LINEAR B SYMBOL B056;Lo;0;L;;;;;N;;;;; 10057;LINEAR B SYMBOL B063;Lo;0;L;;;;;N;;;;; 10058;LINEAR B SYMBOL B064;Lo;0;L;;;;;N;;;;; 10059;LINEAR B SYMBOL B079;Lo;0;L;;;;;N;;;;; 1005A;LINEAR B SYMBOL B082;Lo;0;L;;;;;N;;;;; 1005B;LINEAR B SYMBOL B083;Lo;0;L;;;;;N;;;;; 1005C;LINEAR B SYMBOL B086;Lo;0;L;;;;;N;;;;; 1005D;LINEAR B SYMBOL B089;Lo;0;L;;;;;N;;;;; 10080;LINEAR B IDEOGRAM B100 MAN;Lo;0;L;;;;;N;;;;; 10081;LINEAR B IDEOGRAM B102 WOMAN;Lo;0;L;;;;;N;;;;; 10082;LINEAR B IDEOGRAM B104 DEER;Lo;0;L;;;;;N;;;;; 10083;LINEAR B IDEOGRAM B105 EQUID;Lo;0;L;;;;;N;;;;; 10084;LINEAR B IDEOGRAM B105F MARE;Lo;0;L;;;;;N;;;;; 10085;LINEAR B IDEOGRAM B105M STALLION;Lo;0;L;;;;;N;;;;; 10086;LINEAR B IDEOGRAM B106F EWE;Lo;0;L;;;;;N;;;;; 10087;LINEAR B IDEOGRAM B106M RAM;Lo;0;L;;;;;N;;;;; 10088;LINEAR B IDEOGRAM B107F SHE-GOAT;Lo;0;L;;;;;N;;;;; 10089;LINEAR B IDEOGRAM B107M HE-GOAT;Lo;0;L;;;;;N;;;;; 1008A;LINEAR B IDEOGRAM B108F SOW;Lo;0;L;;;;;N;;;;; 1008B;LINEAR B IDEOGRAM B108M BOAR;Lo;0;L;;;;;N;;;;; 1008C;LINEAR B IDEOGRAM B109F COW;Lo;0;L;;;;;N;;;;; 1008D;LINEAR B IDEOGRAM B109M BULL;Lo;0;L;;;;;N;;;;; 1008E;LINEAR B IDEOGRAM B120 WHEAT;Lo;0;L;;;;;N;;;;; 1008F;LINEAR B IDEOGRAM B121 BARLEY;Lo;0;L;;;;;N;;;;; 10090;LINEAR B IDEOGRAM B122 OLIVE;Lo;0;L;;;;;N;;;;; 10091;LINEAR B IDEOGRAM B123 SPICE;Lo;0;L;;;;;N;;;;; 10092;LINEAR B IDEOGRAM B125 CYPERUS;Lo;0;L;;;;;N;;;;; 10093;LINEAR B MONOGRAM B127 KAPO;Lo;0;L;;;;;N;;;;; 10094;LINEAR B MONOGRAM B128 KANAKO;Lo;0;L;;;;;N;;;;; 10095;LINEAR B IDEOGRAM B130 OIL;Lo;0;L;;;;;N;;;;; 10096;LINEAR B IDEOGRAM B131 WINE;Lo;0;L;;;;;N;;;;; 10097;LINEAR B IDEOGRAM B132;Lo;0;L;;;;;N;;;;; 10098;LINEAR B MONOGRAM B133 AREPA;Lo;0;L;;;;;N;;;;; 10099;LINEAR B MONOGRAM B135 MERI;Lo;0;L;;;;;N;;;;; 1009A;LINEAR B IDEOGRAM B140 BRONZE;Lo;0;L;;;;;N;;;;; 1009B;LINEAR B IDEOGRAM B141 GOLD;Lo;0;L;;;;;N;;;;; 1009C;LINEAR B IDEOGRAM B142;Lo;0;L;;;;;N;;;;; 1009D;LINEAR B IDEOGRAM B145 WOOL;Lo;0;L;;;;;N;;;;; 1009E;LINEAR B IDEOGRAM B146;Lo;0;L;;;;;N;;;;; 1009F;LINEAR B IDEOGRAM B150;Lo;0;L;;;;;N;;;;; 100A0;LINEAR B IDEOGRAM B151 HORN;Lo;0;L;;;;;N;;;;; 100A1;LINEAR B IDEOGRAM B152;Lo;0;L;;;;;N;;;;; 100A2;LINEAR B IDEOGRAM B153;Lo;0;L;;;;;N;;;;; 100A3;LINEAR B IDEOGRAM B154;Lo;0;L;;;;;N;;;;; 100A4;LINEAR B MONOGRAM B156 TURO2;Lo;0;L;;;;;N;;;;; 100A5;LINEAR B IDEOGRAM B157;Lo;0;L;;;;;N;;;;; 100A6;LINEAR B IDEOGRAM B158;Lo;0;L;;;;;N;;;;; 100A7;LINEAR B IDEOGRAM B159 CLOTH;Lo;0;L;;;;;N;;;;; 100A8;LINEAR B IDEOGRAM B160;Lo;0;L;;;;;N;;;;; 100A9;LINEAR B IDEOGRAM B161;Lo;0;L;;;;;N;;;;; 100AA;LINEAR B IDEOGRAM B162 GARMENT;Lo;0;L;;;;;N;;;;; 100AB;LINEAR B IDEOGRAM B163 ARMOUR;Lo;0;L;;;;;N;;;;; 100AC;LINEAR B IDEOGRAM B164;Lo;0;L;;;;;N;;;;; 100AD;LINEAR B IDEOGRAM B165;Lo;0;L;;;;;N;;;;; 100AE;LINEAR B IDEOGRAM B166;Lo;0;L;;;;;N;;;;; 100AF;LINEAR B IDEOGRAM B167;Lo;0;L;;;;;N;;;;; 100B0;LINEAR B IDEOGRAM B168;Lo;0;L;;;;;N;;;;; 100B1;LINEAR B IDEOGRAM B169;Lo;0;L;;;;;N;;;;; 100B2;LINEAR B IDEOGRAM B170;Lo;0;L;;;;;N;;;;; 100B3;LINEAR B IDEOGRAM B171;Lo;0;L;;;;;N;;;;; 100B4;LINEAR B IDEOGRAM B172;Lo;0;L;;;;;N;;;;; 100B5;LINEAR B IDEOGRAM B173 MONTH;Lo;0;L;;;;;N;;;;; 100B6;LINEAR B IDEOGRAM B174;Lo;0;L;;;;;N;;;;; 100B7;LINEAR B IDEOGRAM B176 TREE;Lo;0;L;;;;;N;;;;; 100B8;LINEAR B IDEOGRAM B177;Lo;0;L;;;;;N;;;;; 100B9;LINEAR B IDEOGRAM B178;Lo;0;L;;;;;N;;;;; 100BA;LINEAR B IDEOGRAM B179;Lo;0;L;;;;;N;;;;; 100BB;LINEAR B IDEOGRAM B180;Lo;0;L;;;;;N;;;;; 100BC;LINEAR B IDEOGRAM B181;Lo;0;L;;;;;N;;;;; 100BD;LINEAR B IDEOGRAM B182;Lo;0;L;;;;;N;;;;; 100BE;LINEAR B IDEOGRAM B183;Lo;0;L;;;;;N;;;;; 100BF;LINEAR B IDEOGRAM B184;Lo;0;L;;;;;N;;;;; 100C0;LINEAR B IDEOGRAM B185;Lo;0;L;;;;;N;;;;; 100C1;LINEAR B IDEOGRAM B189;Lo;0;L;;;;;N;;;;; 100C2;LINEAR B IDEOGRAM B190;Lo;0;L;;;;;N;;;;; 100C3;LINEAR B IDEOGRAM B191 HELMET;Lo;0;L;;;;;N;;;;; 100C4;LINEAR B IDEOGRAM B220 FOOTSTOOL;Lo;0;L;;;;;N;;;;; 100C5;LINEAR B IDEOGRAM B225 BATHTUB;Lo;0;L;;;;;N;;;;; 100C6;LINEAR B IDEOGRAM B230 SPEAR;Lo;0;L;;;;;N;;;;; 100C7;LINEAR B IDEOGRAM B231 ARROW;Lo;0;L;;;;;N;;;;; 100C8;LINEAR B IDEOGRAM B232;Lo;0;L;;;;;N;;;;; 100C9;LINEAR B IDEOGRAM B233 SWORD;Lo;0;L;;;;;N;;;;; 100CA;LINEAR B IDEOGRAM B234;Lo;0;L;;;;;N;;;;; 100CB;LINEAR B IDEOGRAM B236;Lo;0;L;;;;;N;;;;; 100CC;LINEAR B IDEOGRAM B240 WHEELED CHARIOT;Lo;0;L;;;;;N;;;;; 100CD;LINEAR B IDEOGRAM B241 CHARIOT;Lo;0;L;;;;;N;;;;; 100CE;LINEAR B IDEOGRAM B242 CHARIOT FRAME;Lo;0;L;;;;;N;;;;; 100CF;LINEAR B IDEOGRAM B243 WHEEL;Lo;0;L;;;;;N;;;;; 100D0;LINEAR B IDEOGRAM B245;Lo;0;L;;;;;N;;;;; 100D1;LINEAR B IDEOGRAM B246;Lo;0;L;;;;;N;;;;; 100D2;LINEAR B MONOGRAM B247 DIPTE;Lo;0;L;;;;;N;;;;; 100D3;LINEAR B IDEOGRAM B248;Lo;0;L;;;;;N;;;;; 100D4;LINEAR B IDEOGRAM B249;Lo;0;L;;;;;N;;;;; 100D5;LINEAR B IDEOGRAM B251;Lo;0;L;;;;;N;;;;; 100D6;LINEAR B IDEOGRAM B252;Lo;0;L;;;;;N;;;;; 100D7;LINEAR B IDEOGRAM B253;Lo;0;L;;;;;N;;;;; 100D8;LINEAR B IDEOGRAM B254 DART;Lo;0;L;;;;;N;;;;; 100D9;LINEAR B IDEOGRAM B255;Lo;0;L;;;;;N;;;;; 100DA;LINEAR B IDEOGRAM B256;Lo;0;L;;;;;N;;;;; 100DB;LINEAR B IDEOGRAM B257;Lo;0;L;;;;;N;;;;; 100DC;LINEAR B IDEOGRAM B258;Lo;0;L;;;;;N;;;;; 100DD;LINEAR B IDEOGRAM B259;Lo;0;L;;;;;N;;;;; 100DE;LINEAR B IDEOGRAM VESSEL B155;Lo;0;L;;;;;N;;;;; 100DF;LINEAR B IDEOGRAM VESSEL B200;Lo;0;L;;;;;N;;;;; 100E0;LINEAR B IDEOGRAM VESSEL B201;Lo;0;L;;;;;N;;;;; 100E1;LINEAR B IDEOGRAM VESSEL B202;Lo;0;L;;;;;N;;;;; 100E2;LINEAR B IDEOGRAM VESSEL B203;Lo;0;L;;;;;N;;;;; 100E3;LINEAR B IDEOGRAM VESSEL B204;Lo;0;L;;;;;N;;;;; 100E4;LINEAR B IDEOGRAM VESSEL B205;Lo;0;L;;;;;N;;;;; 100E5;LINEAR B IDEOGRAM VESSEL B206;Lo;0;L;;;;;N;;;;; 100E6;LINEAR B IDEOGRAM VESSEL B207;Lo;0;L;;;;;N;;;;; 100E7;LINEAR B IDEOGRAM VESSEL B208;Lo;0;L;;;;;N;;;;; 100E8;LINEAR B IDEOGRAM VESSEL B209;Lo;0;L;;;;;N;;;;; 100E9;LINEAR B IDEOGRAM VESSEL B210;Lo;0;L;;;;;N;;;;; 100EA;LINEAR B IDEOGRAM VESSEL B211;Lo;0;L;;;;;N;;;;; 100EB;LINEAR B IDEOGRAM VESSEL B212;Lo;0;L;;;;;N;;;;; 100EC;LINEAR B IDEOGRAM VESSEL B213;Lo;0;L;;;;;N;;;;; 100ED;LINEAR B IDEOGRAM VESSEL B214;Lo;0;L;;;;;N;;;;; 100EE;LINEAR B IDEOGRAM VESSEL B215;Lo;0;L;;;;;N;;;;; 100EF;LINEAR B IDEOGRAM VESSEL B216;Lo;0;L;;;;;N;;;;; 100F0;LINEAR B IDEOGRAM VESSEL B217;Lo;0;L;;;;;N;;;;; 100F1;LINEAR B IDEOGRAM VESSEL B218;Lo;0;L;;;;;N;;;;; 100F2;LINEAR B IDEOGRAM VESSEL B219;Lo;0;L;;;;;N;;;;; 100F3;LINEAR B IDEOGRAM VESSEL B221;Lo;0;L;;;;;N;;;;; 100F4;LINEAR B IDEOGRAM VESSEL B222;Lo;0;L;;;;;N;;;;; 100F5;LINEAR B IDEOGRAM VESSEL B226;Lo;0;L;;;;;N;;;;; 100F6;LINEAR B IDEOGRAM VESSEL B227;Lo;0;L;;;;;N;;;;; 100F7;LINEAR B IDEOGRAM VESSEL B228;Lo;0;L;;;;;N;;;;; 100F8;LINEAR B IDEOGRAM VESSEL B229;Lo;0;L;;;;;N;;;;; 100F9;LINEAR B IDEOGRAM VESSEL B250;Lo;0;L;;;;;N;;;;; 100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;; 10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;; 10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;; 10102;AEGEAN CHECK MARK;Po;0;L;;;;;N;;;;; 10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;; 10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;; 10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;; 1010A;AEGEAN NUMBER FOUR;No;0;L;;;;4;N;;;;; 1010B;AEGEAN NUMBER FIVE;No;0;L;;;;5;N;;;;; 1010C;AEGEAN NUMBER SIX;No;0;L;;;;6;N;;;;; 1010D;AEGEAN NUMBER SEVEN;No;0;L;;;;7;N;;;;; 1010E;AEGEAN NUMBER EIGHT;No;0;L;;;;8;N;;;;; 1010F;AEGEAN NUMBER NINE;No;0;L;;;;9;N;;;;; 10110;AEGEAN NUMBER TEN;No;0;L;;;;10;N;;;;; 10111;AEGEAN NUMBER TWENTY;No;0;L;;;;20;N;;;;; 10112;AEGEAN NUMBER THIRTY;No;0;L;;;;30;N;;;;; 10113;AEGEAN NUMBER FORTY;No;0;L;;;;40;N;;;;; 10114;AEGEAN NUMBER FIFTY;No;0;L;;;;50;N;;;;; 10115;AEGEAN NUMBER SIXTY;No;0;L;;;;60;N;;;;; 10116;AEGEAN NUMBER SEVENTY;No;0;L;;;;70;N;;;;; 10117;AEGEAN NUMBER EIGHTY;No;0;L;;;;80;N;;;;; 10118;AEGEAN NUMBER NINETY;No;0;L;;;;90;N;;;;; 10119;AEGEAN NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; 1011A;AEGEAN NUMBER TWO HUNDRED;No;0;L;;;;200;N;;;;; 1011B;AEGEAN NUMBER THREE HUNDRED;No;0;L;;;;300;N;;;;; 1011C;AEGEAN NUMBER FOUR HUNDRED;No;0;L;;;;400;N;;;;; 1011D;AEGEAN NUMBER FIVE HUNDRED;No;0;L;;;;500;N;;;;; 1011E;AEGEAN NUMBER SIX HUNDRED;No;0;L;;;;600;N;;;;; 1011F;AEGEAN NUMBER SEVEN HUNDRED;No;0;L;;;;700;N;;;;; 10120;AEGEAN NUMBER EIGHT HUNDRED;No;0;L;;;;800;N;;;;; 10121;AEGEAN NUMBER NINE HUNDRED;No;0;L;;;;900;N;;;;; 10122;AEGEAN NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; 10123;AEGEAN NUMBER TWO THOUSAND;No;0;L;;;;2000;N;;;;; 10124;AEGEAN NUMBER THREE THOUSAND;No;0;L;;;;3000;N;;;;; 10125;AEGEAN NUMBER FOUR THOUSAND;No;0;L;;;;4000;N;;;;; 10126;AEGEAN NUMBER FIVE THOUSAND;No;0;L;;;;5000;N;;;;; 10127;AEGEAN NUMBER SIX THOUSAND;No;0;L;;;;6000;N;;;;; 10128;AEGEAN NUMBER SEVEN THOUSAND;No;0;L;;;;7000;N;;;;; 10129;AEGEAN NUMBER EIGHT THOUSAND;No;0;L;;;;8000;N;;;;; 1012A;AEGEAN NUMBER NINE THOUSAND;No;0;L;;;;9000;N;;;;; 1012B;AEGEAN NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;; 1012C;AEGEAN NUMBER TWENTY THOUSAND;No;0;L;;;;20000;N;;;;; 1012D;AEGEAN NUMBER THIRTY THOUSAND;No;0;L;;;;30000;N;;;;; 1012E;AEGEAN NUMBER FORTY THOUSAND;No;0;L;;;;40000;N;;;;; 1012F;AEGEAN NUMBER FIFTY THOUSAND;No;0;L;;;;50000;N;;;;; 10130;AEGEAN NUMBER SIXTY THOUSAND;No;0;L;;;;60000;N;;;;; 10131;AEGEAN NUMBER SEVENTY THOUSAND;No;0;L;;;;70000;N;;;;; 10132;AEGEAN NUMBER EIGHTY THOUSAND;No;0;L;;;;80000;N;;;;; 10133;AEGEAN NUMBER NINETY THOUSAND;No;0;L;;;;90000;N;;;;; 10137;AEGEAN WEIGHT BASE UNIT;So;0;L;;;;;N;;;;; 10138;AEGEAN WEIGHT FIRST SUBUNIT;So;0;L;;;;;N;;;;; 10139;AEGEAN WEIGHT SECOND SUBUNIT;So;0;L;;;;;N;;;;; 1013A;AEGEAN WEIGHT THIRD SUBUNIT;So;0;L;;;;;N;;;;; 1013B;AEGEAN WEIGHT FOURTH SUBUNIT;So;0;L;;;;;N;;;;; 1013C;AEGEAN DRY MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;; 1013D;AEGEAN LIQUID MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;; 1013E;AEGEAN MEASURE SECOND SUBUNIT;So;0;L;;;;;N;;;;; 1013F;AEGEAN MEASURE THIRD SUBUNIT;So;0;L;;;;;N;;;;; 10140;GREEK ACROPHONIC ATTIC ONE QUARTER;Nl;0;ON;;;;1/4;N;;;;; 10141;GREEK ACROPHONIC ATTIC ONE HALF;Nl;0;ON;;;;1/2;N;;;;; 10142;GREEK ACROPHONIC ATTIC ONE DRACHMA;Nl;0;ON;;;;1;N;;;;; 10143;GREEK ACROPHONIC ATTIC FIVE;Nl;0;ON;;;;5;N;;;;; 10144;GREEK ACROPHONIC ATTIC FIFTY;Nl;0;ON;;;;50;N;;;;; 10145;GREEK ACROPHONIC ATTIC FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 10146;GREEK ACROPHONIC ATTIC FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;; 10147;GREEK ACROPHONIC ATTIC FIFTY THOUSAND;Nl;0;ON;;;;50000;N;;;;; 10148;GREEK ACROPHONIC ATTIC FIVE TALENTS;Nl;0;ON;;;;5;N;;;;; 10149;GREEK ACROPHONIC ATTIC TEN TALENTS;Nl;0;ON;;;;10;N;;;;; 1014A;GREEK ACROPHONIC ATTIC FIFTY TALENTS;Nl;0;ON;;;;50;N;;;;; 1014B;GREEK ACROPHONIC ATTIC ONE HUNDRED TALENTS;Nl;0;ON;;;;100;N;;;;; 1014C;GREEK ACROPHONIC ATTIC FIVE HUNDRED TALENTS;Nl;0;ON;;;;500;N;;;;; 1014D;GREEK ACROPHONIC ATTIC ONE THOUSAND TALENTS;Nl;0;ON;;;;1000;N;;;;; 1014E;GREEK ACROPHONIC ATTIC FIVE THOUSAND TALENTS;Nl;0;ON;;;;5000;N;;;;; 1014F;GREEK ACROPHONIC ATTIC FIVE STATERS;Nl;0;ON;;;;5;N;;;;; 10150;GREEK ACROPHONIC ATTIC TEN STATERS;Nl;0;ON;;;;10;N;;;;; 10151;GREEK ACROPHONIC ATTIC FIFTY STATERS;Nl;0;ON;;;;50;N;;;;; 10152;GREEK ACROPHONIC ATTIC ONE HUNDRED STATERS;Nl;0;ON;;;;100;N;;;;; 10153;GREEK ACROPHONIC ATTIC FIVE HUNDRED STATERS;Nl;0;ON;;;;500;N;;;;; 10154;GREEK ACROPHONIC ATTIC ONE THOUSAND STATERS;Nl;0;ON;;;;1000;N;;;;; 10155;GREEK ACROPHONIC ATTIC TEN THOUSAND STATERS;Nl;0;ON;;;;10000;N;;;;; 10156;GREEK ACROPHONIC ATTIC FIFTY THOUSAND STATERS;Nl;0;ON;;;;50000;N;;;;; 10157;GREEK ACROPHONIC ATTIC TEN MNAS;Nl;0;ON;;;;10;N;;;;; 10158;GREEK ACROPHONIC HERAEUM ONE PLETHRON;Nl;0;ON;;;;1;N;;;;; 10159;GREEK ACROPHONIC THESPIAN ONE;Nl;0;ON;;;;1;N;;;;; 1015A;GREEK ACROPHONIC HERMIONIAN ONE;Nl;0;ON;;;;1;N;;;;; 1015B;GREEK ACROPHONIC EPIDAUREAN TWO;Nl;0;ON;;;;2;N;;;;; 1015C;GREEK ACROPHONIC THESPIAN TWO;Nl;0;ON;;;;2;N;;;;; 1015D;GREEK ACROPHONIC CYRENAIC TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;; 1015E;GREEK ACROPHONIC EPIDAUREAN TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;; 1015F;GREEK ACROPHONIC TROEZENIAN FIVE;Nl;0;ON;;;;5;N;;;;; 10160;GREEK ACROPHONIC TROEZENIAN TEN;Nl;0;ON;;;;10;N;;;;; 10161;GREEK ACROPHONIC TROEZENIAN TEN ALTERNATE FORM;Nl;0;ON;;;;10;N;;;;; 10162;GREEK ACROPHONIC HERMIONIAN TEN;Nl;0;ON;;;;10;N;;;;; 10163;GREEK ACROPHONIC MESSENIAN TEN;Nl;0;ON;;;;10;N;;;;; 10164;GREEK ACROPHONIC THESPIAN TEN;Nl;0;ON;;;;10;N;;;;; 10165;GREEK ACROPHONIC THESPIAN THIRTY;Nl;0;ON;;;;30;N;;;;; 10166;GREEK ACROPHONIC TROEZENIAN FIFTY;Nl;0;ON;;;;50;N;;;;; 10167;GREEK ACROPHONIC TROEZENIAN FIFTY ALTERNATE FORM;Nl;0;ON;;;;50;N;;;;; 10168;GREEK ACROPHONIC HERMIONIAN FIFTY;Nl;0;ON;;;;50;N;;;;; 10169;GREEK ACROPHONIC THESPIAN FIFTY;Nl;0;ON;;;;50;N;;;;; 1016A;GREEK ACROPHONIC THESPIAN ONE HUNDRED;Nl;0;ON;;;;100;N;;;;; 1016B;GREEK ACROPHONIC THESPIAN THREE HUNDRED;Nl;0;ON;;;;300;N;;;;; 1016C;GREEK ACROPHONIC EPIDAUREAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 1016D;GREEK ACROPHONIC TROEZENIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 1016E;GREEK ACROPHONIC THESPIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 1016F;GREEK ACROPHONIC CARYSTIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 10170;GREEK ACROPHONIC NAXIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; 10171;GREEK ACROPHONIC THESPIAN ONE THOUSAND;Nl;0;ON;;;;1000;N;;;;; 10172;GREEK ACROPHONIC THESPIAN FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;; 10173;GREEK ACROPHONIC DELPHIC FIVE MNAS;Nl;0;ON;;;;5;N;;;;; 10174;GREEK ACROPHONIC STRATIAN FIFTY MNAS;Nl;0;ON;;;;50;N;;;;; 10175;GREEK ONE HALF SIGN;No;0;ON;;;;1/2;N;;;;; 10176;GREEK ONE HALF SIGN ALTERNATE FORM;No;0;ON;;;;1/2;N;;;;; 10177;GREEK TWO THIRDS SIGN;No;0;ON;;;;2/3;N;;;;; 10178;GREEK THREE QUARTERS SIGN;No;0;ON;;;;3/4;N;;;;; 10179;GREEK YEAR SIGN;So;0;ON;;;;;N;;;;; 1017A;GREEK TALENT SIGN;So;0;ON;;;;;N;;;;; 1017B;GREEK DRACHMA SIGN;So;0;ON;;;;;N;;;;; 1017C;GREEK OBOL SIGN;So;0;ON;;;;;N;;;;; 1017D;GREEK TWO OBOLS SIGN;So;0;ON;;;;;N;;;;; 1017E;GREEK THREE OBOLS SIGN;So;0;ON;;;;;N;;;;; 1017F;GREEK FOUR OBOLS SIGN;So;0;ON;;;;;N;;;;; 10180;GREEK FIVE OBOLS SIGN;So;0;ON;;;;;N;;;;; 10181;GREEK METRETES SIGN;So;0;ON;;;;;N;;;;; 10182;GREEK KYATHOS BASE SIGN;So;0;ON;;;;;N;;;;; 10183;GREEK LITRA SIGN;So;0;ON;;;;;N;;;;; 10184;GREEK OUNKIA SIGN;So;0;ON;;;;;N;;;;; 10185;GREEK XESTES SIGN;So;0;ON;;;;;N;;;;; 10186;GREEK ARTABE SIGN;So;0;ON;;;;;N;;;;; 10187;GREEK AROURA SIGN;So;0;ON;;;;;N;;;;; 10188;GREEK GRAMMA SIGN;So;0;ON;;;;;N;;;;; 10189;GREEK TRYBLION BASE SIGN;So;0;ON;;;;;N;;;;; 1018A;GREEK ZERO SIGN;No;0;ON;;;;0;N;;;;; 1018B;GREEK ONE QUARTER SIGN;No;0;ON;;;;1/4;N;;;;; 1018C;GREEK SINUSOID SIGN;So;0;ON;;;;;N;;;;; 1018D;GREEK INDICTION SIGN;So;0;L;;;;;N;;;;; 1018E;NOMISMA SIGN;So;0;L;;;;;N;;;;; 10190;ROMAN SEXTANS SIGN;So;0;ON;;;;;N;;;;; 10191;ROMAN UNCIA SIGN;So;0;ON;;;;;N;;;;; 10192;ROMAN SEMUNCIA SIGN;So;0;ON;;;;;N;;;;; 10193;ROMAN SEXTULA SIGN;So;0;ON;;;;;N;;;;; 10194;ROMAN DIMIDIA SEXTULA SIGN;So;0;ON;;;;;N;;;;; 10195;ROMAN SILIQUA SIGN;So;0;ON;;;;;N;;;;; 10196;ROMAN DENARIUS SIGN;So;0;ON;;;;;N;;;;; 10197;ROMAN QUINARIUS SIGN;So;0;ON;;;;;N;;;;; 10198;ROMAN SESTERTIUS SIGN;So;0;ON;;;;;N;;;;; 10199;ROMAN DUPONDIUS SIGN;So;0;ON;;;;;N;;;;; 1019A;ROMAN AS SIGN;So;0;ON;;;;;N;;;;; 1019B;ROMAN CENTURIAL SIGN;So;0;ON;;;;;N;;;;; 101A0;GREEK SYMBOL TAU RHO;So;0;ON;;;;;N;;;;; 101D0;PHAISTOS DISC SIGN PEDESTRIAN;So;0;L;;;;;N;;;;; 101D1;PHAISTOS DISC SIGN PLUMED HEAD;So;0;L;;;;;N;;;;; 101D2;PHAISTOS DISC SIGN TATTOOED HEAD;So;0;L;;;;;N;;;;; 101D3;PHAISTOS DISC SIGN CAPTIVE;So;0;L;;;;;N;;;;; 101D4;PHAISTOS DISC SIGN CHILD;So;0;L;;;;;N;;;;; 101D5;PHAISTOS DISC SIGN WOMAN;So;0;L;;;;;N;;;;; 101D6;PHAISTOS DISC SIGN HELMET;So;0;L;;;;;N;;;;; 101D7;PHAISTOS DISC SIGN GAUNTLET;So;0;L;;;;;N;;;;; 101D8;PHAISTOS DISC SIGN TIARA;So;0;L;;;;;N;;;;; 101D9;PHAISTOS DISC SIGN ARROW;So;0;L;;;;;N;;;;; 101DA;PHAISTOS DISC SIGN BOW;So;0;L;;;;;N;;;;; 101DB;PHAISTOS DISC SIGN SHIELD;So;0;L;;;;;N;;;;; 101DC;PHAISTOS DISC SIGN CLUB;So;0;L;;;;;N;;;;; 101DD;PHAISTOS DISC SIGN MANACLES;So;0;L;;;;;N;;;;; 101DE;PHAISTOS DISC SIGN MATTOCK;So;0;L;;;;;N;;;;; 101DF;PHAISTOS DISC SIGN SAW;So;0;L;;;;;N;;;;; 101E0;PHAISTOS DISC SIGN LID;So;0;L;;;;;N;;;;; 101E1;PHAISTOS DISC SIGN BOOMERANG;So;0;L;;;;;N;;;;; 101E2;PHAISTOS DISC SIGN CARPENTRY PLANE;So;0;L;;;;;N;;;;; 101E3;PHAISTOS DISC SIGN DOLIUM;So;0;L;;;;;N;;;;; 101E4;PHAISTOS DISC SIGN COMB;So;0;L;;;;;N;;;;; 101E5;PHAISTOS DISC SIGN SLING;So;0;L;;;;;N;;;;; 101E6;PHAISTOS DISC SIGN COLUMN;So;0;L;;;;;N;;;;; 101E7;PHAISTOS DISC SIGN BEEHIVE;So;0;L;;;;;N;;;;; 101E8;PHAISTOS DISC SIGN SHIP;So;0;L;;;;;N;;;;; 101E9;PHAISTOS DISC SIGN HORN;So;0;L;;;;;N;;;;; 101EA;PHAISTOS DISC SIGN HIDE;So;0;L;;;;;N;;;;; 101EB;PHAISTOS DISC SIGN BULLS LEG;So;0;L;;;;;N;;;;; 101EC;PHAISTOS DISC SIGN CAT;So;0;L;;;;;N;;;;; 101ED;PHAISTOS DISC SIGN RAM;So;0;L;;;;;N;;;;; 101EE;PHAISTOS DISC SIGN EAGLE;So;0;L;;;;;N;;;;; 101EF;PHAISTOS DISC SIGN DOVE;So;0;L;;;;;N;;;;; 101F0;PHAISTOS DISC SIGN TUNNY;So;0;L;;;;;N;;;;; 101F1;PHAISTOS DISC SIGN BEE;So;0;L;;;;;N;;;;; 101F2;PHAISTOS DISC SIGN PLANE TREE;So;0;L;;;;;N;;;;; 101F3;PHAISTOS DISC SIGN VINE;So;0;L;;;;;N;;;;; 101F4;PHAISTOS DISC SIGN PAPYRUS;So;0;L;;;;;N;;;;; 101F5;PHAISTOS DISC SIGN ROSETTE;So;0;L;;;;;N;;;;; 101F6;PHAISTOS DISC SIGN LILY;So;0;L;;;;;N;;;;; 101F7;PHAISTOS DISC SIGN OX BACK;So;0;L;;;;;N;;;;; 101F8;PHAISTOS DISC SIGN FLUTE;So;0;L;;;;;N;;;;; 101F9;PHAISTOS DISC SIGN GRATER;So;0;L;;;;;N;;;;; 101FA;PHAISTOS DISC SIGN STRAINER;So;0;L;;;;;N;;;;; 101FB;PHAISTOS DISC SIGN SMALL AXE;So;0;L;;;;;N;;;;; 101FC;PHAISTOS DISC SIGN WAVY BAND;So;0;L;;;;;N;;;;; 101FD;PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE;Mn;220;NSM;;;;;N;;;;; 10280;LYCIAN LETTER A;Lo;0;L;;;;;N;;;;; 10281;LYCIAN LETTER E;Lo;0;L;;;;;N;;;;; 10282;LYCIAN LETTER B;Lo;0;L;;;;;N;;;;; 10283;LYCIAN LETTER BH;Lo;0;L;;;;;N;;;;; 10284;LYCIAN LETTER G;Lo;0;L;;;;;N;;;;; 10285;LYCIAN LETTER D;Lo;0;L;;;;;N;;;;; 10286;LYCIAN LETTER I;Lo;0;L;;;;;N;;;;; 10287;LYCIAN LETTER W;Lo;0;L;;;;;N;;;;; 10288;LYCIAN LETTER Z;Lo;0;L;;;;;N;;;;; 10289;LYCIAN LETTER TH;Lo;0;L;;;;;N;;;;; 1028A;LYCIAN LETTER J;Lo;0;L;;;;;N;;;;; 1028B;LYCIAN LETTER K;Lo;0;L;;;;;N;;;;; 1028C;LYCIAN LETTER Q;Lo;0;L;;;;;N;;;;; 1028D;LYCIAN LETTER L;Lo;0;L;;;;;N;;;;; 1028E;LYCIAN LETTER M;Lo;0;L;;;;;N;;;;; 1028F;LYCIAN LETTER N;Lo;0;L;;;;;N;;;;; 10290;LYCIAN LETTER MM;Lo;0;L;;;;;N;;;;; 10291;LYCIAN LETTER NN;Lo;0;L;;;;;N;;;;; 10292;LYCIAN LETTER U;Lo;0;L;;;;;N;;;;; 10293;LYCIAN LETTER P;Lo;0;L;;;;;N;;;;; 10294;LYCIAN LETTER KK;Lo;0;L;;;;;N;;;;; 10295;LYCIAN LETTER R;Lo;0;L;;;;;N;;;;; 10296;LYCIAN LETTER S;Lo;0;L;;;;;N;;;;; 10297;LYCIAN LETTER T;Lo;0;L;;;;;N;;;;; 10298;LYCIAN LETTER TT;Lo;0;L;;;;;N;;;;; 10299;LYCIAN LETTER AN;Lo;0;L;;;;;N;;;;; 1029A;LYCIAN LETTER EN;Lo;0;L;;;;;N;;;;; 1029B;LYCIAN LETTER H;Lo;0;L;;;;;N;;;;; 1029C;LYCIAN LETTER X;Lo;0;L;;;;;N;;;;; 102A0;CARIAN LETTER A;Lo;0;L;;;;;N;;;;; 102A1;CARIAN LETTER P2;Lo;0;L;;;;;N;;;;; 102A2;CARIAN LETTER D;Lo;0;L;;;;;N;;;;; 102A3;CARIAN LETTER L;Lo;0;L;;;;;N;;;;; 102A4;CARIAN LETTER UUU;Lo;0;L;;;;;N;;;;; 102A5;CARIAN LETTER R;Lo;0;L;;;;;N;;;;; 102A6;CARIAN LETTER LD;Lo;0;L;;;;;N;;;;; 102A7;CARIAN LETTER A2;Lo;0;L;;;;;N;;;;; 102A8;CARIAN LETTER Q;Lo;0;L;;;;;N;;;;; 102A9;CARIAN LETTER B;Lo;0;L;;;;;N;;;;; 102AA;CARIAN LETTER M;Lo;0;L;;;;;N;;;;; 102AB;CARIAN LETTER O;Lo;0;L;;;;;N;;;;; 102AC;CARIAN LETTER D2;Lo;0;L;;;;;N;;;;; 102AD;CARIAN LETTER T;Lo;0;L;;;;;N;;;;; 102AE;CARIAN LETTER SH;Lo;0;L;;;;;N;;;;; 102AF;CARIAN LETTER SH2;Lo;0;L;;;;;N;;;;; 102B0;CARIAN LETTER S;Lo;0;L;;;;;N;;;;; 102B1;CARIAN LETTER C-18;Lo;0;L;;;;;N;;;;; 102B2;CARIAN LETTER U;Lo;0;L;;;;;N;;;;; 102B3;CARIAN LETTER NN;Lo;0;L;;;;;N;;;;; 102B4;CARIAN LETTER X;Lo;0;L;;;;;N;;;;; 102B5;CARIAN LETTER N;Lo;0;L;;;;;N;;;;; 102B6;CARIAN LETTER TT2;Lo;0;L;;;;;N;;;;; 102B7;CARIAN LETTER P;Lo;0;L;;;;;N;;;;; 102B8;CARIAN LETTER SS;Lo;0;L;;;;;N;;;;; 102B9;CARIAN LETTER I;Lo;0;L;;;;;N;;;;; 102BA;CARIAN LETTER E;Lo;0;L;;;;;N;;;;; 102BB;CARIAN LETTER UUUU;Lo;0;L;;;;;N;;;;; 102BC;CARIAN LETTER K;Lo;0;L;;;;;N;;;;; 102BD;CARIAN LETTER K2;Lo;0;L;;;;;N;;;;; 102BE;CARIAN LETTER ND;Lo;0;L;;;;;N;;;;; 102BF;CARIAN LETTER UU;Lo;0;L;;;;;N;;;;; 102C0;CARIAN LETTER G;Lo;0;L;;;;;N;;;;; 102C1;CARIAN LETTER G2;Lo;0;L;;;;;N;;;;; 102C2;CARIAN LETTER ST;Lo;0;L;;;;;N;;;;; 102C3;CARIAN LETTER ST2;Lo;0;L;;;;;N;;;;; 102C4;CARIAN LETTER NG;Lo;0;L;;;;;N;;;;; 102C5;CARIAN LETTER II;Lo;0;L;;;;;N;;;;; 102C6;CARIAN LETTER C-39;Lo;0;L;;;;;N;;;;; 102C7;CARIAN LETTER TT;Lo;0;L;;;;;N;;;;; 102C8;CARIAN LETTER UUU2;Lo;0;L;;;;;N;;;;; 102C9;CARIAN LETTER RR;Lo;0;L;;;;;N;;;;; 102CA;CARIAN LETTER MB;Lo;0;L;;;;;N;;;;; 102CB;CARIAN LETTER MB2;Lo;0;L;;;;;N;;;;; 102CC;CARIAN LETTER MB3;Lo;0;L;;;;;N;;;;; 102CD;CARIAN LETTER MB4;Lo;0;L;;;;;N;;;;; 102CE;CARIAN LETTER LD2;Lo;0;L;;;;;N;;;;; 102CF;CARIAN LETTER E2;Lo;0;L;;;;;N;;;;; 102D0;CARIAN LETTER UUU3;Lo;0;L;;;;;N;;;;; 102E0;COPTIC EPACT THOUSANDS MARK;Mn;220;NSM;;;;;N;;;;; 102E1;COPTIC EPACT DIGIT ONE;No;0;EN;;;;1;N;;;;; 102E2;COPTIC EPACT DIGIT TWO;No;0;EN;;;;2;N;;;;; 102E3;COPTIC EPACT DIGIT THREE;No;0;EN;;;;3;N;;;;; 102E4;COPTIC EPACT DIGIT FOUR;No;0;EN;;;;4;N;;;;; 102E5;COPTIC EPACT DIGIT FIVE;No;0;EN;;;;5;N;;;;; 102E6;COPTIC EPACT DIGIT SIX;No;0;EN;;;;6;N;;;;; 102E7;COPTIC EPACT DIGIT SEVEN;No;0;EN;;;;7;N;;;;; 102E8;COPTIC EPACT DIGIT EIGHT;No;0;EN;;;;8;N;;;;; 102E9;COPTIC EPACT DIGIT NINE;No;0;EN;;;;9;N;;;;; 102EA;COPTIC EPACT NUMBER TEN;No;0;EN;;;;10;N;;;;; 102EB;COPTIC EPACT NUMBER TWENTY;No;0;EN;;;;20;N;;;;; 102EC;COPTIC EPACT NUMBER THIRTY;No;0;EN;;;;30;N;;;;; 102ED;COPTIC EPACT NUMBER FORTY;No;0;EN;;;;40;N;;;;; 102EE;COPTIC EPACT NUMBER FIFTY;No;0;EN;;;;50;N;;;;; 102EF;COPTIC EPACT NUMBER SIXTY;No;0;EN;;;;60;N;;;;; 102F0;COPTIC EPACT NUMBER SEVENTY;No;0;EN;;;;70;N;;;;; 102F1;COPTIC EPACT NUMBER EIGHTY;No;0;EN;;;;80;N;;;;; 102F2;COPTIC EPACT NUMBER NINETY;No;0;EN;;;;90;N;;;;; 102F3;COPTIC EPACT NUMBER ONE HUNDRED;No;0;EN;;;;100;N;;;;; 102F4;COPTIC EPACT NUMBER TWO HUNDRED;No;0;EN;;;;200;N;;;;; 102F5;COPTIC EPACT NUMBER THREE HUNDRED;No;0;EN;;;;300;N;;;;; 102F6;COPTIC EPACT NUMBER FOUR HUNDRED;No;0;EN;;;;400;N;;;;; 102F7;COPTIC EPACT NUMBER FIVE HUNDRED;No;0;EN;;;;500;N;;;;; 102F8;COPTIC EPACT NUMBER SIX HUNDRED;No;0;EN;;;;600;N;;;;; 102F9;COPTIC EPACT NUMBER SEVEN HUNDRED;No;0;EN;;;;700;N;;;;; 102FA;COPTIC EPACT NUMBER EIGHT HUNDRED;No;0;EN;;;;800;N;;;;; 102FB;COPTIC EPACT NUMBER NINE HUNDRED;No;0;EN;;;;900;N;;;;; 10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;; 10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;; 10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;; 10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;; 10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;; 10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;; 10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;; 10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;; 10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;; 10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;; 1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;; 1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;; 1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;; 1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;; 1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;; 1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;;;; 10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;; 10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;; 10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;; 10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;; 10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;; 10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;; 10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;; 10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;;;; 10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;; 10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;; 1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;; 1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;;;; 1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;;;; 1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;;;; 1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;;;; 1031F;OLD ITALIC LETTER ESS;Lo;0;L;;;;;N;;;;; 10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;; 10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;; 10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;; 10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;; 10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;; 10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;; 10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;; 10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;; 10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;; 10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;; 10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;; 10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;; 10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;; 10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;; 1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;; 1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;; 1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;; 1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;; 1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;; 1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;; 10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;; 10341;GOTHIC LETTER NINETY;Nl;0;L;;;;90;N;;;;; 10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;; 10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;; 10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;; 10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;; 10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;; 10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;; 10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;; 10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;; 1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;900;N;;;;; 10350;OLD PERMIC LETTER AN;Lo;0;L;;;;;N;;;;; 10351;OLD PERMIC LETTER BUR;Lo;0;L;;;;;N;;;;; 10352;OLD PERMIC LETTER GAI;Lo;0;L;;;;;N;;;;; 10353;OLD PERMIC LETTER DOI;Lo;0;L;;;;;N;;;;; 10354;OLD PERMIC LETTER E;Lo;0;L;;;;;N;;;;; 10355;OLD PERMIC LETTER ZHOI;Lo;0;L;;;;;N;;;;; 10356;OLD PERMIC LETTER DZHOI;Lo;0;L;;;;;N;;;;; 10357;OLD PERMIC LETTER ZATA;Lo;0;L;;;;;N;;;;; 10358;OLD PERMIC LETTER DZITA;Lo;0;L;;;;;N;;;;; 10359;OLD PERMIC LETTER I;Lo;0;L;;;;;N;;;;; 1035A;OLD PERMIC LETTER KOKE;Lo;0;L;;;;;N;;;;; 1035B;OLD PERMIC LETTER LEI;Lo;0;L;;;;;N;;;;; 1035C;OLD PERMIC LETTER MENOE;Lo;0;L;;;;;N;;;;; 1035D;OLD PERMIC LETTER NENOE;Lo;0;L;;;;;N;;;;; 1035E;OLD PERMIC LETTER VOOI;Lo;0;L;;;;;N;;;;; 1035F;OLD PERMIC LETTER PEEI;Lo;0;L;;;;;N;;;;; 10360;OLD PERMIC LETTER REI;Lo;0;L;;;;;N;;;;; 10361;OLD PERMIC LETTER SII;Lo;0;L;;;;;N;;;;; 10362;OLD PERMIC LETTER TAI;Lo;0;L;;;;;N;;;;; 10363;OLD PERMIC LETTER U;Lo;0;L;;;;;N;;;;; 10364;OLD PERMIC LETTER CHERY;Lo;0;L;;;;;N;;;;; 10365;OLD PERMIC LETTER SHOOI;Lo;0;L;;;;;N;;;;; 10366;OLD PERMIC LETTER SHCHOOI;Lo;0;L;;;;;N;;;;; 10367;OLD PERMIC LETTER YRY;Lo;0;L;;;;;N;;;;; 10368;OLD PERMIC LETTER YERU;Lo;0;L;;;;;N;;;;; 10369;OLD PERMIC LETTER O;Lo;0;L;;;;;N;;;;; 1036A;OLD PERMIC LETTER OO;Lo;0;L;;;;;N;;;;; 1036B;OLD PERMIC LETTER EF;Lo;0;L;;;;;N;;;;; 1036C;OLD PERMIC LETTER HA;Lo;0;L;;;;;N;;;;; 1036D;OLD PERMIC LETTER TSIU;Lo;0;L;;;;;N;;;;; 1036E;OLD PERMIC LETTER VER;Lo;0;L;;;;;N;;;;; 1036F;OLD PERMIC LETTER YER;Lo;0;L;;;;;N;;;;; 10370;OLD PERMIC LETTER YERI;Lo;0;L;;;;;N;;;;; 10371;OLD PERMIC LETTER YAT;Lo;0;L;;;;;N;;;;; 10372;OLD PERMIC LETTER IE;Lo;0;L;;;;;N;;;;; 10373;OLD PERMIC LETTER YU;Lo;0;L;;;;;N;;;;; 10374;OLD PERMIC LETTER YA;Lo;0;L;;;;;N;;;;; 10375;OLD PERMIC LETTER IA;Lo;0;L;;;;;N;;;;; 10376;COMBINING OLD PERMIC LETTER AN;Mn;230;NSM;;;;;N;;;;; 10377;COMBINING OLD PERMIC LETTER DOI;Mn;230;NSM;;;;;N;;;;; 10378;COMBINING OLD PERMIC LETTER ZATA;Mn;230;NSM;;;;;N;;;;; 10379;COMBINING OLD PERMIC LETTER NENOE;Mn;230;NSM;;;;;N;;;;; 1037A;COMBINING OLD PERMIC LETTER SII;Mn;230;NSM;;;;;N;;;;; 10380;UGARITIC LETTER ALPA;Lo;0;L;;;;;N;;;;; 10381;UGARITIC LETTER BETA;Lo;0;L;;;;;N;;;;; 10382;UGARITIC LETTER GAMLA;Lo;0;L;;;;;N;;;;; 10383;UGARITIC LETTER KHA;Lo;0;L;;;;;N;;;;; 10384;UGARITIC LETTER DELTA;Lo;0;L;;;;;N;;;;; 10385;UGARITIC LETTER HO;Lo;0;L;;;;;N;;;;; 10386;UGARITIC LETTER WO;Lo;0;L;;;;;N;;;;; 10387;UGARITIC LETTER ZETA;Lo;0;L;;;;;N;;;;; 10388;UGARITIC LETTER HOTA;Lo;0;L;;;;;N;;;;; 10389;UGARITIC LETTER TET;Lo;0;L;;;;;N;;;;; 1038A;UGARITIC LETTER YOD;Lo;0;L;;;;;N;;;;; 1038B;UGARITIC LETTER KAF;Lo;0;L;;;;;N;;;;; 1038C;UGARITIC LETTER SHIN;Lo;0;L;;;;;N;;;;; 1038D;UGARITIC LETTER LAMDA;Lo;0;L;;;;;N;;;;; 1038E;UGARITIC LETTER MEM;Lo;0;L;;;;;N;;;;; 1038F;UGARITIC LETTER DHAL;Lo;0;L;;;;;N;;;;; 10390;UGARITIC LETTER NUN;Lo;0;L;;;;;N;;;;; 10391;UGARITIC LETTER ZU;Lo;0;L;;;;;N;;;;; 10392;UGARITIC LETTER SAMKA;Lo;0;L;;;;;N;;;;; 10393;UGARITIC LETTER AIN;Lo;0;L;;;;;N;;;;; 10394;UGARITIC LETTER PU;Lo;0;L;;;;;N;;;;; 10395;UGARITIC LETTER SADE;Lo;0;L;;;;;N;;;;; 10396;UGARITIC LETTER QOPA;Lo;0;L;;;;;N;;;;; 10397;UGARITIC LETTER RASHA;Lo;0;L;;;;;N;;;;; 10398;UGARITIC LETTER THANNA;Lo;0;L;;;;;N;;;;; 10399;UGARITIC LETTER GHAIN;Lo;0;L;;;;;N;;;;; 1039A;UGARITIC LETTER TO;Lo;0;L;;;;;N;;;;; 1039B;UGARITIC LETTER I;Lo;0;L;;;;;N;;;;; 1039C;UGARITIC LETTER U;Lo;0;L;;;;;N;;;;; 1039D;UGARITIC LETTER SSU;Lo;0;L;;;;;N;;;;; 1039F;UGARITIC WORD DIVIDER;Po;0;L;;;;;N;;;;; 103A0;OLD PERSIAN SIGN A;Lo;0;L;;;;;N;;;;; 103A1;OLD PERSIAN SIGN I;Lo;0;L;;;;;N;;;;; 103A2;OLD PERSIAN SIGN U;Lo;0;L;;;;;N;;;;; 103A3;OLD PERSIAN SIGN KA;Lo;0;L;;;;;N;;;;; 103A4;OLD PERSIAN SIGN KU;Lo;0;L;;;;;N;;;;; 103A5;OLD PERSIAN SIGN GA;Lo;0;L;;;;;N;;;;; 103A6;OLD PERSIAN SIGN GU;Lo;0;L;;;;;N;;;;; 103A7;OLD PERSIAN SIGN XA;Lo;0;L;;;;;N;;;;; 103A8;OLD PERSIAN SIGN CA;Lo;0;L;;;;;N;;;;; 103A9;OLD PERSIAN SIGN JA;Lo;0;L;;;;;N;;;;; 103AA;OLD PERSIAN SIGN JI;Lo;0;L;;;;;N;;;;; 103AB;OLD PERSIAN SIGN TA;Lo;0;L;;;;;N;;;;; 103AC;OLD PERSIAN SIGN TU;Lo;0;L;;;;;N;;;;; 103AD;OLD PERSIAN SIGN DA;Lo;0;L;;;;;N;;;;; 103AE;OLD PERSIAN SIGN DI;Lo;0;L;;;;;N;;;;; 103AF;OLD PERSIAN SIGN DU;Lo;0;L;;;;;N;;;;; 103B0;OLD PERSIAN SIGN THA;Lo;0;L;;;;;N;;;;; 103B1;OLD PERSIAN SIGN PA;Lo;0;L;;;;;N;;;;; 103B2;OLD PERSIAN SIGN BA;Lo;0;L;;;;;N;;;;; 103B3;OLD PERSIAN SIGN FA;Lo;0;L;;;;;N;;;;; 103B4;OLD PERSIAN SIGN NA;Lo;0;L;;;;;N;;;;; 103B5;OLD PERSIAN SIGN NU;Lo;0;L;;;;;N;;;;; 103B6;OLD PERSIAN SIGN MA;Lo;0;L;;;;;N;;;;; 103B7;OLD PERSIAN SIGN MI;Lo;0;L;;;;;N;;;;; 103B8;OLD PERSIAN SIGN MU;Lo;0;L;;;;;N;;;;; 103B9;OLD PERSIAN SIGN YA;Lo;0;L;;;;;N;;;;; 103BA;OLD PERSIAN SIGN VA;Lo;0;L;;;;;N;;;;; 103BB;OLD PERSIAN SIGN VI;Lo;0;L;;;;;N;;;;; 103BC;OLD PERSIAN SIGN RA;Lo;0;L;;;;;N;;;;; 103BD;OLD PERSIAN SIGN RU;Lo;0;L;;;;;N;;;;; 103BE;OLD PERSIAN SIGN LA;Lo;0;L;;;;;N;;;;; 103BF;OLD PERSIAN SIGN SA;Lo;0;L;;;;;N;;;;; 103C0;OLD PERSIAN SIGN ZA;Lo;0;L;;;;;N;;;;; 103C1;OLD PERSIAN SIGN SHA;Lo;0;L;;;;;N;;;;; 103C2;OLD PERSIAN SIGN SSA;Lo;0;L;;;;;N;;;;; 103C3;OLD PERSIAN SIGN HA;Lo;0;L;;;;;N;;;;; 103C8;OLD PERSIAN SIGN AURAMAZDAA;Lo;0;L;;;;;N;;;;; 103C9;OLD PERSIAN SIGN AURAMAZDAA-2;Lo;0;L;;;;;N;;;;; 103CA;OLD PERSIAN SIGN AURAMAZDAAHA;Lo;0;L;;;;;N;;;;; 103CB;OLD PERSIAN SIGN XSHAAYATHIYA;Lo;0;L;;;;;N;;;;; 103CC;OLD PERSIAN SIGN DAHYAAUSH;Lo;0;L;;;;;N;;;;; 103CD;OLD PERSIAN SIGN DAHYAAUSH-2;Lo;0;L;;;;;N;;;;; 103CE;OLD PERSIAN SIGN BAGA;Lo;0;L;;;;;N;;;;; 103CF;OLD PERSIAN SIGN BUUMISH;Lo;0;L;;;;;N;;;;; 103D0;OLD PERSIAN WORD DIVIDER;Po;0;L;;;;;N;;;;; 103D1;OLD PERSIAN NUMBER ONE;Nl;0;L;;;;1;N;;;;; 103D2;OLD PERSIAN NUMBER TWO;Nl;0;L;;;;2;N;;;;; 103D3;OLD PERSIAN NUMBER TEN;Nl;0;L;;;;10;N;;;;; 103D4;OLD PERSIAN NUMBER TWENTY;Nl;0;L;;;;20;N;;;;; 103D5;OLD PERSIAN NUMBER HUNDRED;Nl;0;L;;;;100;N;;;;; 10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428; 10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429; 10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A; 10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B; 10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C; 10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D; 10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E; 10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F; 10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430; 10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431; 1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432; 1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433; 1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434; 1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435; 1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436; 1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437; 10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438; 10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439; 10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A; 10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B; 10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C; 10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D; 10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E; 10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F; 10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440; 10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441; 1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442; 1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443; 1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444; 1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445; 1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446; 1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447; 10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448; 10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449; 10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A; 10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B; 10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C; 10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D; 10426;DESERET CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;1044E; 10427;DESERET CAPITAL LETTER EW;Lu;0;L;;;;;N;;;;1044F; 10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400 10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401 1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402 1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403 1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404 1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405 1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406 1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407 10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408 10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409 10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A 10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B 10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C 10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D 10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E 10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F 10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410 10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411 1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412 1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413 1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414 1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415 1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416 1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417 10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418 10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419 10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A 10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B 10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C 10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D 10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E 10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F 10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420 10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421 1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422 1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423 1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424 1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425 1044E;DESERET SMALL LETTER OI;Ll;0;L;;;;;N;;;10426;;10426 1044F;DESERET SMALL LETTER EW;Ll;0;L;;;;;N;;;10427;;10427 10450;SHAVIAN LETTER PEEP;Lo;0;L;;;;;N;;;;; 10451;SHAVIAN LETTER TOT;Lo;0;L;;;;;N;;;;; 10452;SHAVIAN LETTER KICK;Lo;0;L;;;;;N;;;;; 10453;SHAVIAN LETTER FEE;Lo;0;L;;;;;N;;;;; 10454;SHAVIAN LETTER THIGH;Lo;0;L;;;;;N;;;;; 10455;SHAVIAN LETTER SO;Lo;0;L;;;;;N;;;;; 10456;SHAVIAN LETTER SURE;Lo;0;L;;;;;N;;;;; 10457;SHAVIAN LETTER CHURCH;Lo;0;L;;;;;N;;;;; 10458;SHAVIAN LETTER YEA;Lo;0;L;;;;;N;;;;; 10459;SHAVIAN LETTER HUNG;Lo;0;L;;;;;N;;;;; 1045A;SHAVIAN LETTER BIB;Lo;0;L;;;;;N;;;;; 1045B;SHAVIAN LETTER DEAD;Lo;0;L;;;;;N;;;;; 1045C;SHAVIAN LETTER GAG;Lo;0;L;;;;;N;;;;; 1045D;SHAVIAN LETTER VOW;Lo;0;L;;;;;N;;;;; 1045E;SHAVIAN LETTER THEY;Lo;0;L;;;;;N;;;;; 1045F;SHAVIAN LETTER ZOO;Lo;0;L;;;;;N;;;;; 10460;SHAVIAN LETTER MEASURE;Lo;0;L;;;;;N;;;;; 10461;SHAVIAN LETTER JUDGE;Lo;0;L;;;;;N;;;;; 10462;SHAVIAN LETTER WOE;Lo;0;L;;;;;N;;;;; 10463;SHAVIAN LETTER HA-HA;Lo;0;L;;;;;N;;;;; 10464;SHAVIAN LETTER LOLL;Lo;0;L;;;;;N;;;;; 10465;SHAVIAN LETTER MIME;Lo;0;L;;;;;N;;;;; 10466;SHAVIAN LETTER IF;Lo;0;L;;;;;N;;;;; 10467;SHAVIAN LETTER EGG;Lo;0;L;;;;;N;;;;; 10468;SHAVIAN LETTER ASH;Lo;0;L;;;;;N;;;;; 10469;SHAVIAN LETTER ADO;Lo;0;L;;;;;N;;;;; 1046A;SHAVIAN LETTER ON;Lo;0;L;;;;;N;;;;; 1046B;SHAVIAN LETTER WOOL;Lo;0;L;;;;;N;;;;; 1046C;SHAVIAN LETTER OUT;Lo;0;L;;;;;N;;;;; 1046D;SHAVIAN LETTER AH;Lo;0;L;;;;;N;;;;; 1046E;SHAVIAN LETTER ROAR;Lo;0;L;;;;;N;;;;; 1046F;SHAVIAN LETTER NUN;Lo;0;L;;;;;N;;;;; 10470;SHAVIAN LETTER EAT;Lo;0;L;;;;;N;;;;; 10471;SHAVIAN LETTER AGE;Lo;0;L;;;;;N;;;;; 10472;SHAVIAN LETTER ICE;Lo;0;L;;;;;N;;;;; 10473;SHAVIAN LETTER UP;Lo;0;L;;;;;N;;;;; 10474;SHAVIAN LETTER OAK;Lo;0;L;;;;;N;;;;; 10475;SHAVIAN LETTER OOZE;Lo;0;L;;;;;N;;;;; 10476;SHAVIAN LETTER OIL;Lo;0;L;;;;;N;;;;; 10477;SHAVIAN LETTER AWE;Lo;0;L;;;;;N;;;;; 10478;SHAVIAN LETTER ARE;Lo;0;L;;;;;N;;;;; 10479;SHAVIAN LETTER OR;Lo;0;L;;;;;N;;;;; 1047A;SHAVIAN LETTER AIR;Lo;0;L;;;;;N;;;;; 1047B;SHAVIAN LETTER ERR;Lo;0;L;;;;;N;;;;; 1047C;SHAVIAN LETTER ARRAY;Lo;0;L;;;;;N;;;;; 1047D;SHAVIAN LETTER EAR;Lo;0;L;;;;;N;;;;; 1047E;SHAVIAN LETTER IAN;Lo;0;L;;;;;N;;;;; 1047F;SHAVIAN LETTER YEW;Lo;0;L;;;;;N;;;;; 10480;OSMANYA LETTER ALEF;Lo;0;L;;;;;N;;;;; 10481;OSMANYA LETTER BA;Lo;0;L;;;;;N;;;;; 10482;OSMANYA LETTER TA;Lo;0;L;;;;;N;;;;; 10483;OSMANYA LETTER JA;Lo;0;L;;;;;N;;;;; 10484;OSMANYA LETTER XA;Lo;0;L;;;;;N;;;;; 10485;OSMANYA LETTER KHA;Lo;0;L;;;;;N;;;;; 10486;OSMANYA LETTER DEEL;Lo;0;L;;;;;N;;;;; 10487;OSMANYA LETTER RA;Lo;0;L;;;;;N;;;;; 10488;OSMANYA LETTER SA;Lo;0;L;;;;;N;;;;; 10489;OSMANYA LETTER SHIIN;Lo;0;L;;;;;N;;;;; 1048A;OSMANYA LETTER DHA;Lo;0;L;;;;;N;;;;; 1048B;OSMANYA LETTER CAYN;Lo;0;L;;;;;N;;;;; 1048C;OSMANYA LETTER GA;Lo;0;L;;;;;N;;;;; 1048D;OSMANYA LETTER FA;Lo;0;L;;;;;N;;;;; 1048E;OSMANYA LETTER QAAF;Lo;0;L;;;;;N;;;;; 1048F;OSMANYA LETTER KAAF;Lo;0;L;;;;;N;;;;; 10490;OSMANYA LETTER LAAN;Lo;0;L;;;;;N;;;;; 10491;OSMANYA LETTER MIIN;Lo;0;L;;;;;N;;;;; 10492;OSMANYA LETTER NUUN;Lo;0;L;;;;;N;;;;; 10493;OSMANYA LETTER WAW;Lo;0;L;;;;;N;;;;; 10494;OSMANYA LETTER HA;Lo;0;L;;;;;N;;;;; 10495;OSMANYA LETTER YA;Lo;0;L;;;;;N;;;;; 10496;OSMANYA LETTER A;Lo;0;L;;;;;N;;;;; 10497;OSMANYA LETTER E;Lo;0;L;;;;;N;;;;; 10498;OSMANYA LETTER I;Lo;0;L;;;;;N;;;;; 10499;OSMANYA LETTER O;Lo;0;L;;;;;N;;;;; 1049A;OSMANYA LETTER U;Lo;0;L;;;;;N;;;;; 1049B;OSMANYA LETTER AA;Lo;0;L;;;;;N;;;;; 1049C;OSMANYA LETTER EE;Lo;0;L;;;;;N;;;;; 1049D;OSMANYA LETTER OO;Lo;0;L;;;;;N;;;;; 104A0;OSMANYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 104A1;OSMANYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 104A2;OSMANYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 104A3;OSMANYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 104A4;OSMANYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 104A5;OSMANYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 104A6;OSMANYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 104A7;OSMANYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 104A8;OSMANYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 104A9;OSMANYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 104B0;OSAGE CAPITAL LETTER A;Lu;0;L;;;;;N;;;;104D8; 104B1;OSAGE CAPITAL LETTER AI;Lu;0;L;;;;;N;;;;104D9; 104B2;OSAGE CAPITAL LETTER AIN;Lu;0;L;;;;;N;;;;104DA; 104B3;OSAGE CAPITAL LETTER AH;Lu;0;L;;;;;N;;;;104DB; 104B4;OSAGE CAPITAL LETTER BRA;Lu;0;L;;;;;N;;;;104DC; 104B5;OSAGE CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;104DD; 104B6;OSAGE CAPITAL LETTER EHCHA;Lu;0;L;;;;;N;;;;104DE; 104B7;OSAGE CAPITAL LETTER E;Lu;0;L;;;;;N;;;;104DF; 104B8;OSAGE CAPITAL LETTER EIN;Lu;0;L;;;;;N;;;;104E0; 104B9;OSAGE CAPITAL LETTER HA;Lu;0;L;;;;;N;;;;104E1; 104BA;OSAGE CAPITAL LETTER HYA;Lu;0;L;;;;;N;;;;104E2; 104BB;OSAGE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;104E3; 104BC;OSAGE CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;104E4; 104BD;OSAGE CAPITAL LETTER EHKA;Lu;0;L;;;;;N;;;;104E5; 104BE;OSAGE CAPITAL LETTER KYA;Lu;0;L;;;;;N;;;;104E6; 104BF;OSAGE CAPITAL LETTER LA;Lu;0;L;;;;;N;;;;104E7; 104C0;OSAGE CAPITAL LETTER MA;Lu;0;L;;;;;N;;;;104E8; 104C1;OSAGE CAPITAL LETTER NA;Lu;0;L;;;;;N;;;;104E9; 104C2;OSAGE CAPITAL LETTER O;Lu;0;L;;;;;N;;;;104EA; 104C3;OSAGE CAPITAL LETTER OIN;Lu;0;L;;;;;N;;;;104EB; 104C4;OSAGE CAPITAL LETTER PA;Lu;0;L;;;;;N;;;;104EC; 104C5;OSAGE CAPITAL LETTER EHPA;Lu;0;L;;;;;N;;;;104ED; 104C6;OSAGE CAPITAL LETTER SA;Lu;0;L;;;;;N;;;;104EE; 104C7;OSAGE CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;104EF; 104C8;OSAGE CAPITAL LETTER TA;Lu;0;L;;;;;N;;;;104F0; 104C9;OSAGE CAPITAL LETTER EHTA;Lu;0;L;;;;;N;;;;104F1; 104CA;OSAGE CAPITAL LETTER TSA;Lu;0;L;;;;;N;;;;104F2; 104CB;OSAGE CAPITAL LETTER EHTSA;Lu;0;L;;;;;N;;;;104F3; 104CC;OSAGE CAPITAL LETTER TSHA;Lu;0;L;;;;;N;;;;104F4; 104CD;OSAGE CAPITAL LETTER DHA;Lu;0;L;;;;;N;;;;104F5; 104CE;OSAGE CAPITAL LETTER U;Lu;0;L;;;;;N;;;;104F6; 104CF;OSAGE CAPITAL LETTER WA;Lu;0;L;;;;;N;;;;104F7; 104D0;OSAGE CAPITAL LETTER KHA;Lu;0;L;;;;;N;;;;104F8; 104D1;OSAGE CAPITAL LETTER GHA;Lu;0;L;;;;;N;;;;104F9; 104D2;OSAGE CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;104FA; 104D3;OSAGE CAPITAL LETTER ZHA;Lu;0;L;;;;;N;;;;104FB; 104D8;OSAGE SMALL LETTER A;Ll;0;L;;;;;N;;;104B0;;104B0 104D9;OSAGE SMALL LETTER AI;Ll;0;L;;;;;N;;;104B1;;104B1 104DA;OSAGE SMALL LETTER AIN;Ll;0;L;;;;;N;;;104B2;;104B2 104DB;OSAGE SMALL LETTER AH;Ll;0;L;;;;;N;;;104B3;;104B3 104DC;OSAGE SMALL LETTER BRA;Ll;0;L;;;;;N;;;104B4;;104B4 104DD;OSAGE SMALL LETTER CHA;Ll;0;L;;;;;N;;;104B5;;104B5 104DE;OSAGE SMALL LETTER EHCHA;Ll;0;L;;;;;N;;;104B6;;104B6 104DF;OSAGE SMALL LETTER E;Ll;0;L;;;;;N;;;104B7;;104B7 104E0;OSAGE SMALL LETTER EIN;Ll;0;L;;;;;N;;;104B8;;104B8 104E1;OSAGE SMALL LETTER HA;Ll;0;L;;;;;N;;;104B9;;104B9 104E2;OSAGE SMALL LETTER HYA;Ll;0;L;;;;;N;;;104BA;;104BA 104E3;OSAGE SMALL LETTER I;Ll;0;L;;;;;N;;;104BB;;104BB 104E4;OSAGE SMALL LETTER KA;Ll;0;L;;;;;N;;;104BC;;104BC 104E5;OSAGE SMALL LETTER EHKA;Ll;0;L;;;;;N;;;104BD;;104BD 104E6;OSAGE SMALL LETTER KYA;Ll;0;L;;;;;N;;;104BE;;104BE 104E7;OSAGE SMALL LETTER LA;Ll;0;L;;;;;N;;;104BF;;104BF 104E8;OSAGE SMALL LETTER MA;Ll;0;L;;;;;N;;;104C0;;104C0 104E9;OSAGE SMALL LETTER NA;Ll;0;L;;;;;N;;;104C1;;104C1 104EA;OSAGE SMALL LETTER O;Ll;0;L;;;;;N;;;104C2;;104C2 104EB;OSAGE SMALL LETTER OIN;Ll;0;L;;;;;N;;;104C3;;104C3 104EC;OSAGE SMALL LETTER PA;Ll;0;L;;;;;N;;;104C4;;104C4 104ED;OSAGE SMALL LETTER EHPA;Ll;0;L;;;;;N;;;104C5;;104C5 104EE;OSAGE SMALL LETTER SA;Ll;0;L;;;;;N;;;104C6;;104C6 104EF;OSAGE SMALL LETTER SHA;Ll;0;L;;;;;N;;;104C7;;104C7 104F0;OSAGE SMALL LETTER TA;Ll;0;L;;;;;N;;;104C8;;104C8 104F1;OSAGE SMALL LETTER EHTA;Ll;0;L;;;;;N;;;104C9;;104C9 104F2;OSAGE SMALL LETTER TSA;Ll;0;L;;;;;N;;;104CA;;104CA 104F3;OSAGE SMALL LETTER EHTSA;Ll;0;L;;;;;N;;;104CB;;104CB 104F4;OSAGE SMALL LETTER TSHA;Ll;0;L;;;;;N;;;104CC;;104CC 104F5;OSAGE SMALL LETTER DHA;Ll;0;L;;;;;N;;;104CD;;104CD 104F6;OSAGE SMALL LETTER U;Ll;0;L;;;;;N;;;104CE;;104CE 104F7;OSAGE SMALL LETTER WA;Ll;0;L;;;;;N;;;104CF;;104CF 104F8;OSAGE SMALL LETTER KHA;Ll;0;L;;;;;N;;;104D0;;104D0 104F9;OSAGE SMALL LETTER GHA;Ll;0;L;;;;;N;;;104D1;;104D1 104FA;OSAGE SMALL LETTER ZA;Ll;0;L;;;;;N;;;104D2;;104D2 104FB;OSAGE SMALL LETTER ZHA;Ll;0;L;;;;;N;;;104D3;;104D3 10500;ELBASAN LETTER A;Lo;0;L;;;;;N;;;;; 10501;ELBASAN LETTER BE;Lo;0;L;;;;;N;;;;; 10502;ELBASAN LETTER CE;Lo;0;L;;;;;N;;;;; 10503;ELBASAN LETTER CHE;Lo;0;L;;;;;N;;;;; 10504;ELBASAN LETTER DE;Lo;0;L;;;;;N;;;;; 10505;ELBASAN LETTER NDE;Lo;0;L;;;;;N;;;;; 10506;ELBASAN LETTER DHE;Lo;0;L;;;;;N;;;;; 10507;ELBASAN LETTER EI;Lo;0;L;;;;;N;;;;; 10508;ELBASAN LETTER E;Lo;0;L;;;;;N;;;;; 10509;ELBASAN LETTER FE;Lo;0;L;;;;;N;;;;; 1050A;ELBASAN LETTER GE;Lo;0;L;;;;;N;;;;; 1050B;ELBASAN LETTER GJE;Lo;0;L;;;;;N;;;;; 1050C;ELBASAN LETTER HE;Lo;0;L;;;;;N;;;;; 1050D;ELBASAN LETTER I;Lo;0;L;;;;;N;;;;; 1050E;ELBASAN LETTER JE;Lo;0;L;;;;;N;;;;; 1050F;ELBASAN LETTER KE;Lo;0;L;;;;;N;;;;; 10510;ELBASAN LETTER LE;Lo;0;L;;;;;N;;;;; 10511;ELBASAN LETTER LLE;Lo;0;L;;;;;N;;;;; 10512;ELBASAN LETTER ME;Lo;0;L;;;;;N;;;;; 10513;ELBASAN LETTER NE;Lo;0;L;;;;;N;;;;; 10514;ELBASAN LETTER NA;Lo;0;L;;;;;N;;;;; 10515;ELBASAN LETTER NJE;Lo;0;L;;;;;N;;;;; 10516;ELBASAN LETTER O;Lo;0;L;;;;;N;;;;; 10517;ELBASAN LETTER PE;Lo;0;L;;;;;N;;;;; 10518;ELBASAN LETTER QE;Lo;0;L;;;;;N;;;;; 10519;ELBASAN LETTER RE;Lo;0;L;;;;;N;;;;; 1051A;ELBASAN LETTER RRE;Lo;0;L;;;;;N;;;;; 1051B;ELBASAN LETTER SE;Lo;0;L;;;;;N;;;;; 1051C;ELBASAN LETTER SHE;Lo;0;L;;;;;N;;;;; 1051D;ELBASAN LETTER TE;Lo;0;L;;;;;N;;;;; 1051E;ELBASAN LETTER THE;Lo;0;L;;;;;N;;;;; 1051F;ELBASAN LETTER U;Lo;0;L;;;;;N;;;;; 10520;ELBASAN LETTER VE;Lo;0;L;;;;;N;;;;; 10521;ELBASAN LETTER XE;Lo;0;L;;;;;N;;;;; 10522;ELBASAN LETTER Y;Lo;0;L;;;;;N;;;;; 10523;ELBASAN LETTER ZE;Lo;0;L;;;;;N;;;;; 10524;ELBASAN LETTER ZHE;Lo;0;L;;;;;N;;;;; 10525;ELBASAN LETTER GHE;Lo;0;L;;;;;N;;;;; 10526;ELBASAN LETTER GHAMMA;Lo;0;L;;;;;N;;;;; 10527;ELBASAN LETTER KHE;Lo;0;L;;;;;N;;;;; 10530;CAUCASIAN ALBANIAN LETTER ALT;Lo;0;L;;;;;N;;;;; 10531;CAUCASIAN ALBANIAN LETTER BET;Lo;0;L;;;;;N;;;;; 10532;CAUCASIAN ALBANIAN LETTER GIM;Lo;0;L;;;;;N;;;;; 10533;CAUCASIAN ALBANIAN LETTER DAT;Lo;0;L;;;;;N;;;;; 10534;CAUCASIAN ALBANIAN LETTER EB;Lo;0;L;;;;;N;;;;; 10535;CAUCASIAN ALBANIAN LETTER ZARL;Lo;0;L;;;;;N;;;;; 10536;CAUCASIAN ALBANIAN LETTER EYN;Lo;0;L;;;;;N;;;;; 10537;CAUCASIAN ALBANIAN LETTER ZHIL;Lo;0;L;;;;;N;;;;; 10538;CAUCASIAN ALBANIAN LETTER TAS;Lo;0;L;;;;;N;;;;; 10539;CAUCASIAN ALBANIAN LETTER CHA;Lo;0;L;;;;;N;;;;; 1053A;CAUCASIAN ALBANIAN LETTER YOWD;Lo;0;L;;;;;N;;;;; 1053B;CAUCASIAN ALBANIAN LETTER ZHA;Lo;0;L;;;;;N;;;;; 1053C;CAUCASIAN ALBANIAN LETTER IRB;Lo;0;L;;;;;N;;;;; 1053D;CAUCASIAN ALBANIAN LETTER SHA;Lo;0;L;;;;;N;;;;; 1053E;CAUCASIAN ALBANIAN LETTER LAN;Lo;0;L;;;;;N;;;;; 1053F;CAUCASIAN ALBANIAN LETTER INYA;Lo;0;L;;;;;N;;;;; 10540;CAUCASIAN ALBANIAN LETTER XEYN;Lo;0;L;;;;;N;;;;; 10541;CAUCASIAN ALBANIAN LETTER DYAN;Lo;0;L;;;;;N;;;;; 10542;CAUCASIAN ALBANIAN LETTER CAR;Lo;0;L;;;;;N;;;;; 10543;CAUCASIAN ALBANIAN LETTER JHOX;Lo;0;L;;;;;N;;;;; 10544;CAUCASIAN ALBANIAN LETTER KAR;Lo;0;L;;;;;N;;;;; 10545;CAUCASIAN ALBANIAN LETTER LYIT;Lo;0;L;;;;;N;;;;; 10546;CAUCASIAN ALBANIAN LETTER HEYT;Lo;0;L;;;;;N;;;;; 10547;CAUCASIAN ALBANIAN LETTER QAY;Lo;0;L;;;;;N;;;;; 10548;CAUCASIAN ALBANIAN LETTER AOR;Lo;0;L;;;;;N;;;;; 10549;CAUCASIAN ALBANIAN LETTER CHOY;Lo;0;L;;;;;N;;;;; 1054A;CAUCASIAN ALBANIAN LETTER CHI;Lo;0;L;;;;;N;;;;; 1054B;CAUCASIAN ALBANIAN LETTER CYAY;Lo;0;L;;;;;N;;;;; 1054C;CAUCASIAN ALBANIAN LETTER MAQ;Lo;0;L;;;;;N;;;;; 1054D;CAUCASIAN ALBANIAN LETTER QAR;Lo;0;L;;;;;N;;;;; 1054E;CAUCASIAN ALBANIAN LETTER NOWC;Lo;0;L;;;;;N;;;;; 1054F;CAUCASIAN ALBANIAN LETTER DZYAY;Lo;0;L;;;;;N;;;;; 10550;CAUCASIAN ALBANIAN LETTER SHAK;Lo;0;L;;;;;N;;;;; 10551;CAUCASIAN ALBANIAN LETTER JAYN;Lo;0;L;;;;;N;;;;; 10552;CAUCASIAN ALBANIAN LETTER ON;Lo;0;L;;;;;N;;;;; 10553;CAUCASIAN ALBANIAN LETTER TYAY;Lo;0;L;;;;;N;;;;; 10554;CAUCASIAN ALBANIAN LETTER FAM;Lo;0;L;;;;;N;;;;; 10555;CAUCASIAN ALBANIAN LETTER DZAY;Lo;0;L;;;;;N;;;;; 10556;CAUCASIAN ALBANIAN LETTER CHAT;Lo;0;L;;;;;N;;;;; 10557;CAUCASIAN ALBANIAN LETTER PEN;Lo;0;L;;;;;N;;;;; 10558;CAUCASIAN ALBANIAN LETTER GHEYS;Lo;0;L;;;;;N;;;;; 10559;CAUCASIAN ALBANIAN LETTER RAT;Lo;0;L;;;;;N;;;;; 1055A;CAUCASIAN ALBANIAN LETTER SEYK;Lo;0;L;;;;;N;;;;; 1055B;CAUCASIAN ALBANIAN LETTER VEYZ;Lo;0;L;;;;;N;;;;; 1055C;CAUCASIAN ALBANIAN LETTER TIWR;Lo;0;L;;;;;N;;;;; 1055D;CAUCASIAN ALBANIAN LETTER SHOY;Lo;0;L;;;;;N;;;;; 1055E;CAUCASIAN ALBANIAN LETTER IWN;Lo;0;L;;;;;N;;;;; 1055F;CAUCASIAN ALBANIAN LETTER CYAW;Lo;0;L;;;;;N;;;;; 10560;CAUCASIAN ALBANIAN LETTER CAYN;Lo;0;L;;;;;N;;;;; 10561;CAUCASIAN ALBANIAN LETTER YAYD;Lo;0;L;;;;;N;;;;; 10562;CAUCASIAN ALBANIAN LETTER PIWR;Lo;0;L;;;;;N;;;;; 10563;CAUCASIAN ALBANIAN LETTER KIW;Lo;0;L;;;;;N;;;;; 1056F;CAUCASIAN ALBANIAN CITATION MARK;Po;0;L;;;;;N;;;;; 10600;LINEAR A SIGN AB001;Lo;0;L;;;;;N;;;;; 10601;LINEAR A SIGN AB002;Lo;0;L;;;;;N;;;;; 10602;LINEAR A SIGN AB003;Lo;0;L;;;;;N;;;;; 10603;LINEAR A SIGN AB004;Lo;0;L;;;;;N;;;;; 10604;LINEAR A SIGN AB005;Lo;0;L;;;;;N;;;;; 10605;LINEAR A SIGN AB006;Lo;0;L;;;;;N;;;;; 10606;LINEAR A SIGN AB007;Lo;0;L;;;;;N;;;;; 10607;LINEAR A SIGN AB008;Lo;0;L;;;;;N;;;;; 10608;LINEAR A SIGN AB009;Lo;0;L;;;;;N;;;;; 10609;LINEAR A SIGN AB010;Lo;0;L;;;;;N;;;;; 1060A;LINEAR A SIGN AB011;Lo;0;L;;;;;N;;;;; 1060B;LINEAR A SIGN AB013;Lo;0;L;;;;;N;;;;; 1060C;LINEAR A SIGN AB016;Lo;0;L;;;;;N;;;;; 1060D;LINEAR A SIGN AB017;Lo;0;L;;;;;N;;;;; 1060E;LINEAR A SIGN AB020;Lo;0;L;;;;;N;;;;; 1060F;LINEAR A SIGN AB021;Lo;0;L;;;;;N;;;;; 10610;LINEAR A SIGN AB021F;Lo;0;L;;;;;N;;;;; 10611;LINEAR A SIGN AB021M;Lo;0;L;;;;;N;;;;; 10612;LINEAR A SIGN AB022;Lo;0;L;;;;;N;;;;; 10613;LINEAR A SIGN AB022F;Lo;0;L;;;;;N;;;;; 10614;LINEAR A SIGN AB022M;Lo;0;L;;;;;N;;;;; 10615;LINEAR A SIGN AB023;Lo;0;L;;;;;N;;;;; 10616;LINEAR A SIGN AB023M;Lo;0;L;;;;;N;;;;; 10617;LINEAR A SIGN AB024;Lo;0;L;;;;;N;;;;; 10618;LINEAR A SIGN AB026;Lo;0;L;;;;;N;;;;; 10619;LINEAR A SIGN AB027;Lo;0;L;;;;;N;;;;; 1061A;LINEAR A SIGN AB028;Lo;0;L;;;;;N;;;;; 1061B;LINEAR A SIGN A028B;Lo;0;L;;;;;N;;;;; 1061C;LINEAR A SIGN AB029;Lo;0;L;;;;;N;;;;; 1061D;LINEAR A SIGN AB030;Lo;0;L;;;;;N;;;;; 1061E;LINEAR A SIGN AB031;Lo;0;L;;;;;N;;;;; 1061F;LINEAR A SIGN AB034;Lo;0;L;;;;;N;;;;; 10620;LINEAR A SIGN AB037;Lo;0;L;;;;;N;;;;; 10621;LINEAR A SIGN AB038;Lo;0;L;;;;;N;;;;; 10622;LINEAR A SIGN AB039;Lo;0;L;;;;;N;;;;; 10623;LINEAR A SIGN AB040;Lo;0;L;;;;;N;;;;; 10624;LINEAR A SIGN AB041;Lo;0;L;;;;;N;;;;; 10625;LINEAR A SIGN AB044;Lo;0;L;;;;;N;;;;; 10626;LINEAR A SIGN AB045;Lo;0;L;;;;;N;;;;; 10627;LINEAR A SIGN AB046;Lo;0;L;;;;;N;;;;; 10628;LINEAR A SIGN AB047;Lo;0;L;;;;;N;;;;; 10629;LINEAR A SIGN AB048;Lo;0;L;;;;;N;;;;; 1062A;LINEAR A SIGN AB049;Lo;0;L;;;;;N;;;;; 1062B;LINEAR A SIGN AB050;Lo;0;L;;;;;N;;;;; 1062C;LINEAR A SIGN AB051;Lo;0;L;;;;;N;;;;; 1062D;LINEAR A SIGN AB053;Lo;0;L;;;;;N;;;;; 1062E;LINEAR A SIGN AB054;Lo;0;L;;;;;N;;;;; 1062F;LINEAR A SIGN AB055;Lo;0;L;;;;;N;;;;; 10630;LINEAR A SIGN AB056;Lo;0;L;;;;;N;;;;; 10631;LINEAR A SIGN AB057;Lo;0;L;;;;;N;;;;; 10632;LINEAR A SIGN AB058;Lo;0;L;;;;;N;;;;; 10633;LINEAR A SIGN AB059;Lo;0;L;;;;;N;;;;; 10634;LINEAR A SIGN AB060;Lo;0;L;;;;;N;;;;; 10635;LINEAR A SIGN AB061;Lo;0;L;;;;;N;;;;; 10636;LINEAR A SIGN AB065;Lo;0;L;;;;;N;;;;; 10637;LINEAR A SIGN AB066;Lo;0;L;;;;;N;;;;; 10638;LINEAR A SIGN AB067;Lo;0;L;;;;;N;;;;; 10639;LINEAR A SIGN AB069;Lo;0;L;;;;;N;;;;; 1063A;LINEAR A SIGN AB070;Lo;0;L;;;;;N;;;;; 1063B;LINEAR A SIGN AB073;Lo;0;L;;;;;N;;;;; 1063C;LINEAR A SIGN AB074;Lo;0;L;;;;;N;;;;; 1063D;LINEAR A SIGN AB076;Lo;0;L;;;;;N;;;;; 1063E;LINEAR A SIGN AB077;Lo;0;L;;;;;N;;;;; 1063F;LINEAR A SIGN AB078;Lo;0;L;;;;;N;;;;; 10640;LINEAR A SIGN AB079;Lo;0;L;;;;;N;;;;; 10641;LINEAR A SIGN AB080;Lo;0;L;;;;;N;;;;; 10642;LINEAR A SIGN AB081;Lo;0;L;;;;;N;;;;; 10643;LINEAR A SIGN AB082;Lo;0;L;;;;;N;;;;; 10644;LINEAR A SIGN AB085;Lo;0;L;;;;;N;;;;; 10645;LINEAR A SIGN AB086;Lo;0;L;;;;;N;;;;; 10646;LINEAR A SIGN AB087;Lo;0;L;;;;;N;;;;; 10647;LINEAR A SIGN A100-102;Lo;0;L;;;;;N;;;;; 10648;LINEAR A SIGN AB118;Lo;0;L;;;;;N;;;;; 10649;LINEAR A SIGN AB120;Lo;0;L;;;;;N;;;;; 1064A;LINEAR A SIGN A120B;Lo;0;L;;;;;N;;;;; 1064B;LINEAR A SIGN AB122;Lo;0;L;;;;;N;;;;; 1064C;LINEAR A SIGN AB123;Lo;0;L;;;;;N;;;;; 1064D;LINEAR A SIGN AB131A;Lo;0;L;;;;;N;;;;; 1064E;LINEAR A SIGN AB131B;Lo;0;L;;;;;N;;;;; 1064F;LINEAR A SIGN A131C;Lo;0;L;;;;;N;;;;; 10650;LINEAR A SIGN AB164;Lo;0;L;;;;;N;;;;; 10651;LINEAR A SIGN AB171;Lo;0;L;;;;;N;;;;; 10652;LINEAR A SIGN AB180;Lo;0;L;;;;;N;;;;; 10653;LINEAR A SIGN AB188;Lo;0;L;;;;;N;;;;; 10654;LINEAR A SIGN AB191;Lo;0;L;;;;;N;;;;; 10655;LINEAR A SIGN A301;Lo;0;L;;;;;N;;;;; 10656;LINEAR A SIGN A302;Lo;0;L;;;;;N;;;;; 10657;LINEAR A SIGN A303;Lo;0;L;;;;;N;;;;; 10658;LINEAR A SIGN A304;Lo;0;L;;;;;N;;;;; 10659;LINEAR A SIGN A305;Lo;0;L;;;;;N;;;;; 1065A;LINEAR A SIGN A306;Lo;0;L;;;;;N;;;;; 1065B;LINEAR A SIGN A307;Lo;0;L;;;;;N;;;;; 1065C;LINEAR A SIGN A308;Lo;0;L;;;;;N;;;;; 1065D;LINEAR A SIGN A309A;Lo;0;L;;;;;N;;;;; 1065E;LINEAR A SIGN A309B;Lo;0;L;;;;;N;;;;; 1065F;LINEAR A SIGN A309C;Lo;0;L;;;;;N;;;;; 10660;LINEAR A SIGN A310;Lo;0;L;;;;;N;;;;; 10661;LINEAR A SIGN A311;Lo;0;L;;;;;N;;;;; 10662;LINEAR A SIGN A312;Lo;0;L;;;;;N;;;;; 10663;LINEAR A SIGN A313A;Lo;0;L;;;;;N;;;;; 10664;LINEAR A SIGN A313B;Lo;0;L;;;;;N;;;;; 10665;LINEAR A SIGN A313C;Lo;0;L;;;;;N;;;;; 10666;LINEAR A SIGN A314;Lo;0;L;;;;;N;;;;; 10667;LINEAR A SIGN A315;Lo;0;L;;;;;N;;;;; 10668;LINEAR A SIGN A316;Lo;0;L;;;;;N;;;;; 10669;LINEAR A SIGN A317;Lo;0;L;;;;;N;;;;; 1066A;LINEAR A SIGN A318;Lo;0;L;;;;;N;;;;; 1066B;LINEAR A SIGN A319;Lo;0;L;;;;;N;;;;; 1066C;LINEAR A SIGN A320;Lo;0;L;;;;;N;;;;; 1066D;LINEAR A SIGN A321;Lo;0;L;;;;;N;;;;; 1066E;LINEAR A SIGN A322;Lo;0;L;;;;;N;;;;; 1066F;LINEAR A SIGN A323;Lo;0;L;;;;;N;;;;; 10670;LINEAR A SIGN A324;Lo;0;L;;;;;N;;;;; 10671;LINEAR A SIGN A325;Lo;0;L;;;;;N;;;;; 10672;LINEAR A SIGN A326;Lo;0;L;;;;;N;;;;; 10673;LINEAR A SIGN A327;Lo;0;L;;;;;N;;;;; 10674;LINEAR A SIGN A328;Lo;0;L;;;;;N;;;;; 10675;LINEAR A SIGN A329;Lo;0;L;;;;;N;;;;; 10676;LINEAR A SIGN A330;Lo;0;L;;;;;N;;;;; 10677;LINEAR A SIGN A331;Lo;0;L;;;;;N;;;;; 10678;LINEAR A SIGN A332;Lo;0;L;;;;;N;;;;; 10679;LINEAR A SIGN A333;Lo;0;L;;;;;N;;;;; 1067A;LINEAR A SIGN A334;Lo;0;L;;;;;N;;;;; 1067B;LINEAR A SIGN A335;Lo;0;L;;;;;N;;;;; 1067C;LINEAR A SIGN A336;Lo;0;L;;;;;N;;;;; 1067D;LINEAR A SIGN A337;Lo;0;L;;;;;N;;;;; 1067E;LINEAR A SIGN A338;Lo;0;L;;;;;N;;;;; 1067F;LINEAR A SIGN A339;Lo;0;L;;;;;N;;;;; 10680;LINEAR A SIGN A340;Lo;0;L;;;;;N;;;;; 10681;LINEAR A SIGN A341;Lo;0;L;;;;;N;;;;; 10682;LINEAR A SIGN A342;Lo;0;L;;;;;N;;;;; 10683;LINEAR A SIGN A343;Lo;0;L;;;;;N;;;;; 10684;LINEAR A SIGN A344;Lo;0;L;;;;;N;;;;; 10685;LINEAR A SIGN A345;Lo;0;L;;;;;N;;;;; 10686;LINEAR A SIGN A346;Lo;0;L;;;;;N;;;;; 10687;LINEAR A SIGN A347;Lo;0;L;;;;;N;;;;; 10688;LINEAR A SIGN A348;Lo;0;L;;;;;N;;;;; 10689;LINEAR A SIGN A349;Lo;0;L;;;;;N;;;;; 1068A;LINEAR A SIGN A350;Lo;0;L;;;;;N;;;;; 1068B;LINEAR A SIGN A351;Lo;0;L;;;;;N;;;;; 1068C;LINEAR A SIGN A352;Lo;0;L;;;;;N;;;;; 1068D;LINEAR A SIGN A353;Lo;0;L;;;;;N;;;;; 1068E;LINEAR A SIGN A354;Lo;0;L;;;;;N;;;;; 1068F;LINEAR A SIGN A355;Lo;0;L;;;;;N;;;;; 10690;LINEAR A SIGN A356;Lo;0;L;;;;;N;;;;; 10691;LINEAR A SIGN A357;Lo;0;L;;;;;N;;;;; 10692;LINEAR A SIGN A358;Lo;0;L;;;;;N;;;;; 10693;LINEAR A SIGN A359;Lo;0;L;;;;;N;;;;; 10694;LINEAR A SIGN A360;Lo;0;L;;;;;N;;;;; 10695;LINEAR A SIGN A361;Lo;0;L;;;;;N;;;;; 10696;LINEAR A SIGN A362;Lo;0;L;;;;;N;;;;; 10697;LINEAR A SIGN A363;Lo;0;L;;;;;N;;;;; 10698;LINEAR A SIGN A364;Lo;0;L;;;;;N;;;;; 10699;LINEAR A SIGN A365;Lo;0;L;;;;;N;;;;; 1069A;LINEAR A SIGN A366;Lo;0;L;;;;;N;;;;; 1069B;LINEAR A SIGN A367;Lo;0;L;;;;;N;;;;; 1069C;LINEAR A SIGN A368;Lo;0;L;;;;;N;;;;; 1069D;LINEAR A SIGN A369;Lo;0;L;;;;;N;;;;; 1069E;LINEAR A SIGN A370;Lo;0;L;;;;;N;;;;; 1069F;LINEAR A SIGN A371;Lo;0;L;;;;;N;;;;; 106A0;LINEAR A SIGN A400-VAS;Lo;0;L;;;;;N;;;;; 106A1;LINEAR A SIGN A401-VAS;Lo;0;L;;;;;N;;;;; 106A2;LINEAR A SIGN A402-VAS;Lo;0;L;;;;;N;;;;; 106A3;LINEAR A SIGN A403-VAS;Lo;0;L;;;;;N;;;;; 106A4;LINEAR A SIGN A404-VAS;Lo;0;L;;;;;N;;;;; 106A5;LINEAR A SIGN A405-VAS;Lo;0;L;;;;;N;;;;; 106A6;LINEAR A SIGN A406-VAS;Lo;0;L;;;;;N;;;;; 106A7;LINEAR A SIGN A407-VAS;Lo;0;L;;;;;N;;;;; 106A8;LINEAR A SIGN A408-VAS;Lo;0;L;;;;;N;;;;; 106A9;LINEAR A SIGN A409-VAS;Lo;0;L;;;;;N;;;;; 106AA;LINEAR A SIGN A410-VAS;Lo;0;L;;;;;N;;;;; 106AB;LINEAR A SIGN A411-VAS;Lo;0;L;;;;;N;;;;; 106AC;LINEAR A SIGN A412-VAS;Lo;0;L;;;;;N;;;;; 106AD;LINEAR A SIGN A413-VAS;Lo;0;L;;;;;N;;;;; 106AE;LINEAR A SIGN A414-VAS;Lo;0;L;;;;;N;;;;; 106AF;LINEAR A SIGN A415-VAS;Lo;0;L;;;;;N;;;;; 106B0;LINEAR A SIGN A416-VAS;Lo;0;L;;;;;N;;;;; 106B1;LINEAR A SIGN A417-VAS;Lo;0;L;;;;;N;;;;; 106B2;LINEAR A SIGN A418-VAS;Lo;0;L;;;;;N;;;;; 106B3;LINEAR A SIGN A501;Lo;0;L;;;;;N;;;;; 106B4;LINEAR A SIGN A502;Lo;0;L;;;;;N;;;;; 106B5;LINEAR A SIGN A503;Lo;0;L;;;;;N;;;;; 106B6;LINEAR A SIGN A504;Lo;0;L;;;;;N;;;;; 106B7;LINEAR A SIGN A505;Lo;0;L;;;;;N;;;;; 106B8;LINEAR A SIGN A506;Lo;0;L;;;;;N;;;;; 106B9;LINEAR A SIGN A508;Lo;0;L;;;;;N;;;;; 106BA;LINEAR A SIGN A509;Lo;0;L;;;;;N;;;;; 106BB;LINEAR A SIGN A510;Lo;0;L;;;;;N;;;;; 106BC;LINEAR A SIGN A511;Lo;0;L;;;;;N;;;;; 106BD;LINEAR A SIGN A512;Lo;0;L;;;;;N;;;;; 106BE;LINEAR A SIGN A513;Lo;0;L;;;;;N;;;;; 106BF;LINEAR A SIGN A515;Lo;0;L;;;;;N;;;;; 106C0;LINEAR A SIGN A516;Lo;0;L;;;;;N;;;;; 106C1;LINEAR A SIGN A520;Lo;0;L;;;;;N;;;;; 106C2;LINEAR A SIGN A521;Lo;0;L;;;;;N;;;;; 106C3;LINEAR A SIGN A523;Lo;0;L;;;;;N;;;;; 106C4;LINEAR A SIGN A524;Lo;0;L;;;;;N;;;;; 106C5;LINEAR A SIGN A525;Lo;0;L;;;;;N;;;;; 106C6;LINEAR A SIGN A526;Lo;0;L;;;;;N;;;;; 106C7;LINEAR A SIGN A527;Lo;0;L;;;;;N;;;;; 106C8;LINEAR A SIGN A528;Lo;0;L;;;;;N;;;;; 106C9;LINEAR A SIGN A529;Lo;0;L;;;;;N;;;;; 106CA;LINEAR A SIGN A530;Lo;0;L;;;;;N;;;;; 106CB;LINEAR A SIGN A531;Lo;0;L;;;;;N;;;;; 106CC;LINEAR A SIGN A532;Lo;0;L;;;;;N;;;;; 106CD;LINEAR A SIGN A534;Lo;0;L;;;;;N;;;;; 106CE;LINEAR A SIGN A535;Lo;0;L;;;;;N;;;;; 106CF;LINEAR A SIGN A536;Lo;0;L;;;;;N;;;;; 106D0;LINEAR A SIGN A537;Lo;0;L;;;;;N;;;;; 106D1;LINEAR A SIGN A538;Lo;0;L;;;;;N;;;;; 106D2;LINEAR A SIGN A539;Lo;0;L;;;;;N;;;;; 106D3;LINEAR A SIGN A540;Lo;0;L;;;;;N;;;;; 106D4;LINEAR A SIGN A541;Lo;0;L;;;;;N;;;;; 106D5;LINEAR A SIGN A542;Lo;0;L;;;;;N;;;;; 106D6;LINEAR A SIGN A545;Lo;0;L;;;;;N;;;;; 106D7;LINEAR A SIGN A547;Lo;0;L;;;;;N;;;;; 106D8;LINEAR A SIGN A548;Lo;0;L;;;;;N;;;;; 106D9;LINEAR A SIGN A549;Lo;0;L;;;;;N;;;;; 106DA;LINEAR A SIGN A550;Lo;0;L;;;;;N;;;;; 106DB;LINEAR A SIGN A551;Lo;0;L;;;;;N;;;;; 106DC;LINEAR A SIGN A552;Lo;0;L;;;;;N;;;;; 106DD;LINEAR A SIGN A553;Lo;0;L;;;;;N;;;;; 106DE;LINEAR A SIGN A554;Lo;0;L;;;;;N;;;;; 106DF;LINEAR A SIGN A555;Lo;0;L;;;;;N;;;;; 106E0;LINEAR A SIGN A556;Lo;0;L;;;;;N;;;;; 106E1;LINEAR A SIGN A557;Lo;0;L;;;;;N;;;;; 106E2;LINEAR A SIGN A559;Lo;0;L;;;;;N;;;;; 106E3;LINEAR A SIGN A563;Lo;0;L;;;;;N;;;;; 106E4;LINEAR A SIGN A564;Lo;0;L;;;;;N;;;;; 106E5;LINEAR A SIGN A565;Lo;0;L;;;;;N;;;;; 106E6;LINEAR A SIGN A566;Lo;0;L;;;;;N;;;;; 106E7;LINEAR A SIGN A568;Lo;0;L;;;;;N;;;;; 106E8;LINEAR A SIGN A569;Lo;0;L;;;;;N;;;;; 106E9;LINEAR A SIGN A570;Lo;0;L;;;;;N;;;;; 106EA;LINEAR A SIGN A571;Lo;0;L;;;;;N;;;;; 106EB;LINEAR A SIGN A572;Lo;0;L;;;;;N;;;;; 106EC;LINEAR A SIGN A573;Lo;0;L;;;;;N;;;;; 106ED;LINEAR A SIGN A574;Lo;0;L;;;;;N;;;;; 106EE;LINEAR A SIGN A575;Lo;0;L;;;;;N;;;;; 106EF;LINEAR A SIGN A576;Lo;0;L;;;;;N;;;;; 106F0;LINEAR A SIGN A577;Lo;0;L;;;;;N;;;;; 106F1;LINEAR A SIGN A578;Lo;0;L;;;;;N;;;;; 106F2;LINEAR A SIGN A579;Lo;0;L;;;;;N;;;;; 106F3;LINEAR A SIGN A580;Lo;0;L;;;;;N;;;;; 106F4;LINEAR A SIGN A581;Lo;0;L;;;;;N;;;;; 106F5;LINEAR A SIGN A582;Lo;0;L;;;;;N;;;;; 106F6;LINEAR A SIGN A583;Lo;0;L;;;;;N;;;;; 106F7;LINEAR A SIGN A584;Lo;0;L;;;;;N;;;;; 106F8;LINEAR A SIGN A585;Lo;0;L;;;;;N;;;;; 106F9;LINEAR A SIGN A586;Lo;0;L;;;;;N;;;;; 106FA;LINEAR A SIGN A587;Lo;0;L;;;;;N;;;;; 106FB;LINEAR A SIGN A588;Lo;0;L;;;;;N;;;;; 106FC;LINEAR A SIGN A589;Lo;0;L;;;;;N;;;;; 106FD;LINEAR A SIGN A591;Lo;0;L;;;;;N;;;;; 106FE;LINEAR A SIGN A592;Lo;0;L;;;;;N;;;;; 106FF;LINEAR A SIGN A594;Lo;0;L;;;;;N;;;;; 10700;LINEAR A SIGN A595;Lo;0;L;;;;;N;;;;; 10701;LINEAR A SIGN A596;Lo;0;L;;;;;N;;;;; 10702;LINEAR A SIGN A598;Lo;0;L;;;;;N;;;;; 10703;LINEAR A SIGN A600;Lo;0;L;;;;;N;;;;; 10704;LINEAR A SIGN A601;Lo;0;L;;;;;N;;;;; 10705;LINEAR A SIGN A602;Lo;0;L;;;;;N;;;;; 10706;LINEAR A SIGN A603;Lo;0;L;;;;;N;;;;; 10707;LINEAR A SIGN A604;Lo;0;L;;;;;N;;;;; 10708;LINEAR A SIGN A606;Lo;0;L;;;;;N;;;;; 10709;LINEAR A SIGN A608;Lo;0;L;;;;;N;;;;; 1070A;LINEAR A SIGN A609;Lo;0;L;;;;;N;;;;; 1070B;LINEAR A SIGN A610;Lo;0;L;;;;;N;;;;; 1070C;LINEAR A SIGN A611;Lo;0;L;;;;;N;;;;; 1070D;LINEAR A SIGN A612;Lo;0;L;;;;;N;;;;; 1070E;LINEAR A SIGN A613;Lo;0;L;;;;;N;;;;; 1070F;LINEAR A SIGN A614;Lo;0;L;;;;;N;;;;; 10710;LINEAR A SIGN A615;Lo;0;L;;;;;N;;;;; 10711;LINEAR A SIGN A616;Lo;0;L;;;;;N;;;;; 10712;LINEAR A SIGN A617;Lo;0;L;;;;;N;;;;; 10713;LINEAR A SIGN A618;Lo;0;L;;;;;N;;;;; 10714;LINEAR A SIGN A619;Lo;0;L;;;;;N;;;;; 10715;LINEAR A SIGN A620;Lo;0;L;;;;;N;;;;; 10716;LINEAR A SIGN A621;Lo;0;L;;;;;N;;;;; 10717;LINEAR A SIGN A622;Lo;0;L;;;;;N;;;;; 10718;LINEAR A SIGN A623;Lo;0;L;;;;;N;;;;; 10719;LINEAR A SIGN A624;Lo;0;L;;;;;N;;;;; 1071A;LINEAR A SIGN A626;Lo;0;L;;;;;N;;;;; 1071B;LINEAR A SIGN A627;Lo;0;L;;;;;N;;;;; 1071C;LINEAR A SIGN A628;Lo;0;L;;;;;N;;;;; 1071D;LINEAR A SIGN A629;Lo;0;L;;;;;N;;;;; 1071E;LINEAR A SIGN A634;Lo;0;L;;;;;N;;;;; 1071F;LINEAR A SIGN A637;Lo;0;L;;;;;N;;;;; 10720;LINEAR A SIGN A638;Lo;0;L;;;;;N;;;;; 10721;LINEAR A SIGN A640;Lo;0;L;;;;;N;;;;; 10722;LINEAR A SIGN A642;Lo;0;L;;;;;N;;;;; 10723;LINEAR A SIGN A643;Lo;0;L;;;;;N;;;;; 10724;LINEAR A SIGN A644;Lo;0;L;;;;;N;;;;; 10725;LINEAR A SIGN A645;Lo;0;L;;;;;N;;;;; 10726;LINEAR A SIGN A646;Lo;0;L;;;;;N;;;;; 10727;LINEAR A SIGN A648;Lo;0;L;;;;;N;;;;; 10728;LINEAR A SIGN A649;Lo;0;L;;;;;N;;;;; 10729;LINEAR A SIGN A651;Lo;0;L;;;;;N;;;;; 1072A;LINEAR A SIGN A652;Lo;0;L;;;;;N;;;;; 1072B;LINEAR A SIGN A653;Lo;0;L;;;;;N;;;;; 1072C;LINEAR A SIGN A654;Lo;0;L;;;;;N;;;;; 1072D;LINEAR A SIGN A655;Lo;0;L;;;;;N;;;;; 1072E;LINEAR A SIGN A656;Lo;0;L;;;;;N;;;;; 1072F;LINEAR A SIGN A657;Lo;0;L;;;;;N;;;;; 10730;LINEAR A SIGN A658;Lo;0;L;;;;;N;;;;; 10731;LINEAR A SIGN A659;Lo;0;L;;;;;N;;;;; 10732;LINEAR A SIGN A660;Lo;0;L;;;;;N;;;;; 10733;LINEAR A SIGN A661;Lo;0;L;;;;;N;;;;; 10734;LINEAR A SIGN A662;Lo;0;L;;;;;N;;;;; 10735;LINEAR A SIGN A663;Lo;0;L;;;;;N;;;;; 10736;LINEAR A SIGN A664;Lo;0;L;;;;;N;;;;; 10740;LINEAR A SIGN A701 A;Lo;0;L;;;;;N;;;;; 10741;LINEAR A SIGN A702 B;Lo;0;L;;;;;N;;;;; 10742;LINEAR A SIGN A703 D;Lo;0;L;;;;;N;;;;; 10743;LINEAR A SIGN A704 E;Lo;0;L;;;;;N;;;;; 10744;LINEAR A SIGN A705 F;Lo;0;L;;;;;N;;;;; 10745;LINEAR A SIGN A706 H;Lo;0;L;;;;;N;;;;; 10746;LINEAR A SIGN A707 J;Lo;0;L;;;;;N;;;;; 10747;LINEAR A SIGN A708 K;Lo;0;L;;;;;N;;;;; 10748;LINEAR A SIGN A709 L;Lo;0;L;;;;;N;;;;; 10749;LINEAR A SIGN A709-2 L2;Lo;0;L;;;;;N;;;;; 1074A;LINEAR A SIGN A709-3 L3;Lo;0;L;;;;;N;;;;; 1074B;LINEAR A SIGN A709-4 L4;Lo;0;L;;;;;N;;;;; 1074C;LINEAR A SIGN A709-6 L6;Lo;0;L;;;;;N;;;;; 1074D;LINEAR A SIGN A710 W;Lo;0;L;;;;;N;;;;; 1074E;LINEAR A SIGN A711 X;Lo;0;L;;;;;N;;;;; 1074F;LINEAR A SIGN A712 Y;Lo;0;L;;;;;N;;;;; 10750;LINEAR A SIGN A713 OMEGA;Lo;0;L;;;;;N;;;;; 10751;LINEAR A SIGN A714 ABB;Lo;0;L;;;;;N;;;;; 10752;LINEAR A SIGN A715 BB;Lo;0;L;;;;;N;;;;; 10753;LINEAR A SIGN A717 DD;Lo;0;L;;;;;N;;;;; 10754;LINEAR A SIGN A726 EYYY;Lo;0;L;;;;;N;;;;; 10755;LINEAR A SIGN A732 JE;Lo;0;L;;;;;N;;;;; 10760;LINEAR A SIGN A800;Lo;0;L;;;;;N;;;;; 10761;LINEAR A SIGN A801;Lo;0;L;;;;;N;;;;; 10762;LINEAR A SIGN A802;Lo;0;L;;;;;N;;;;; 10763;LINEAR A SIGN A803;Lo;0;L;;;;;N;;;;; 10764;LINEAR A SIGN A804;Lo;0;L;;;;;N;;;;; 10765;LINEAR A SIGN A805;Lo;0;L;;;;;N;;;;; 10766;LINEAR A SIGN A806;Lo;0;L;;;;;N;;;;; 10767;LINEAR A SIGN A807;Lo;0;L;;;;;N;;;;; 10800;CYPRIOT SYLLABLE A;Lo;0;R;;;;;N;;;;; 10801;CYPRIOT SYLLABLE E;Lo;0;R;;;;;N;;;;; 10802;CYPRIOT SYLLABLE I;Lo;0;R;;;;;N;;;;; 10803;CYPRIOT SYLLABLE O;Lo;0;R;;;;;N;;;;; 10804;CYPRIOT SYLLABLE U;Lo;0;R;;;;;N;;;;; 10805;CYPRIOT SYLLABLE JA;Lo;0;R;;;;;N;;;;; 10808;CYPRIOT SYLLABLE JO;Lo;0;R;;;;;N;;;;; 1080A;CYPRIOT SYLLABLE KA;Lo;0;R;;;;;N;;;;; 1080B;CYPRIOT SYLLABLE KE;Lo;0;R;;;;;N;;;;; 1080C;CYPRIOT SYLLABLE KI;Lo;0;R;;;;;N;;;;; 1080D;CYPRIOT SYLLABLE KO;Lo;0;R;;;;;N;;;;; 1080E;CYPRIOT SYLLABLE KU;Lo;0;R;;;;;N;;;;; 1080F;CYPRIOT SYLLABLE LA;Lo;0;R;;;;;N;;;;; 10810;CYPRIOT SYLLABLE LE;Lo;0;R;;;;;N;;;;; 10811;CYPRIOT SYLLABLE LI;Lo;0;R;;;;;N;;;;; 10812;CYPRIOT SYLLABLE LO;Lo;0;R;;;;;N;;;;; 10813;CYPRIOT SYLLABLE LU;Lo;0;R;;;;;N;;;;; 10814;CYPRIOT SYLLABLE MA;Lo;0;R;;;;;N;;;;; 10815;CYPRIOT SYLLABLE ME;Lo;0;R;;;;;N;;;;; 10816;CYPRIOT SYLLABLE MI;Lo;0;R;;;;;N;;;;; 10817;CYPRIOT SYLLABLE MO;Lo;0;R;;;;;N;;;;; 10818;CYPRIOT SYLLABLE MU;Lo;0;R;;;;;N;;;;; 10819;CYPRIOT SYLLABLE NA;Lo;0;R;;;;;N;;;;; 1081A;CYPRIOT SYLLABLE NE;Lo;0;R;;;;;N;;;;; 1081B;CYPRIOT SYLLABLE NI;Lo;0;R;;;;;N;;;;; 1081C;CYPRIOT SYLLABLE NO;Lo;0;R;;;;;N;;;;; 1081D;CYPRIOT SYLLABLE NU;Lo;0;R;;;;;N;;;;; 1081E;CYPRIOT SYLLABLE PA;Lo;0;R;;;;;N;;;;; 1081F;CYPRIOT SYLLABLE PE;Lo;0;R;;;;;N;;;;; 10820;CYPRIOT SYLLABLE PI;Lo;0;R;;;;;N;;;;; 10821;CYPRIOT SYLLABLE PO;Lo;0;R;;;;;N;;;;; 10822;CYPRIOT SYLLABLE PU;Lo;0;R;;;;;N;;;;; 10823;CYPRIOT SYLLABLE RA;Lo;0;R;;;;;N;;;;; 10824;CYPRIOT SYLLABLE RE;Lo;0;R;;;;;N;;;;; 10825;CYPRIOT SYLLABLE RI;Lo;0;R;;;;;N;;;;; 10826;CYPRIOT SYLLABLE RO;Lo;0;R;;;;;N;;;;; 10827;CYPRIOT SYLLABLE RU;Lo;0;R;;;;;N;;;;; 10828;CYPRIOT SYLLABLE SA;Lo;0;R;;;;;N;;;;; 10829;CYPRIOT SYLLABLE SE;Lo;0;R;;;;;N;;;;; 1082A;CYPRIOT SYLLABLE SI;Lo;0;R;;;;;N;;;;; 1082B;CYPRIOT SYLLABLE SO;Lo;0;R;;;;;N;;;;; 1082C;CYPRIOT SYLLABLE SU;Lo;0;R;;;;;N;;;;; 1082D;CYPRIOT SYLLABLE TA;Lo;0;R;;;;;N;;;;; 1082E;CYPRIOT SYLLABLE TE;Lo;0;R;;;;;N;;;;; 1082F;CYPRIOT SYLLABLE TI;Lo;0;R;;;;;N;;;;; 10830;CYPRIOT SYLLABLE TO;Lo;0;R;;;;;N;;;;; 10831;CYPRIOT SYLLABLE TU;Lo;0;R;;;;;N;;;;; 10832;CYPRIOT SYLLABLE WA;Lo;0;R;;;;;N;;;;; 10833;CYPRIOT SYLLABLE WE;Lo;0;R;;;;;N;;;;; 10834;CYPRIOT SYLLABLE WI;Lo;0;R;;;;;N;;;;; 10835;CYPRIOT SYLLABLE WO;Lo;0;R;;;;;N;;;;; 10837;CYPRIOT SYLLABLE XA;Lo;0;R;;;;;N;;;;; 10838;CYPRIOT SYLLABLE XE;Lo;0;R;;;;;N;;;;; 1083C;CYPRIOT SYLLABLE ZA;Lo;0;R;;;;;N;;;;; 1083F;CYPRIOT SYLLABLE ZO;Lo;0;R;;;;;N;;;;; 10840;IMPERIAL ARAMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10841;IMPERIAL ARAMAIC LETTER BETH;Lo;0;R;;;;;N;;;;; 10842;IMPERIAL ARAMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10843;IMPERIAL ARAMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;; 10844;IMPERIAL ARAMAIC LETTER HE;Lo;0;R;;;;;N;;;;; 10845;IMPERIAL ARAMAIC LETTER WAW;Lo;0;R;;;;;N;;;;; 10846;IMPERIAL ARAMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10847;IMPERIAL ARAMAIC LETTER HETH;Lo;0;R;;;;;N;;;;; 10848;IMPERIAL ARAMAIC LETTER TETH;Lo;0;R;;;;;N;;;;; 10849;IMPERIAL ARAMAIC LETTER YODH;Lo;0;R;;;;;N;;;;; 1084A;IMPERIAL ARAMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;; 1084B;IMPERIAL ARAMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 1084C;IMPERIAL ARAMAIC LETTER MEM;Lo;0;R;;;;;N;;;;; 1084D;IMPERIAL ARAMAIC LETTER NUN;Lo;0;R;;;;;N;;;;; 1084E;IMPERIAL ARAMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 1084F;IMPERIAL ARAMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;; 10850;IMPERIAL ARAMAIC LETTER PE;Lo;0;R;;;;;N;;;;; 10851;IMPERIAL ARAMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;; 10852;IMPERIAL ARAMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;; 10853;IMPERIAL ARAMAIC LETTER RESH;Lo;0;R;;;;;N;;;;; 10854;IMPERIAL ARAMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;; 10855;IMPERIAL ARAMAIC LETTER TAW;Lo;0;R;;;;;N;;;;; 10857;IMPERIAL ARAMAIC SECTION SIGN;Po;0;R;;;;;N;;;;; 10858;IMPERIAL ARAMAIC NUMBER ONE;No;0;R;;;;1;N;;;;; 10859;IMPERIAL ARAMAIC NUMBER TWO;No;0;R;;;;2;N;;;;; 1085A;IMPERIAL ARAMAIC NUMBER THREE;No;0;R;;;;3;N;;;;; 1085B;IMPERIAL ARAMAIC NUMBER TEN;No;0;R;;;;10;N;;;;; 1085C;IMPERIAL ARAMAIC NUMBER TWENTY;No;0;R;;;;20;N;;;;; 1085D;IMPERIAL ARAMAIC NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 1085E;IMPERIAL ARAMAIC NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 1085F;IMPERIAL ARAMAIC NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;; 10860;PALMYRENE LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10861;PALMYRENE LETTER BETH;Lo;0;R;;;;;N;;;;; 10862;PALMYRENE LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10863;PALMYRENE LETTER DALETH;Lo;0;R;;;;;N;;;;; 10864;PALMYRENE LETTER HE;Lo;0;R;;;;;N;;;;; 10865;PALMYRENE LETTER WAW;Lo;0;R;;;;;N;;;;; 10866;PALMYRENE LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10867;PALMYRENE LETTER HETH;Lo;0;R;;;;;N;;;;; 10868;PALMYRENE LETTER TETH;Lo;0;R;;;;;N;;;;; 10869;PALMYRENE LETTER YODH;Lo;0;R;;;;;N;;;;; 1086A;PALMYRENE LETTER KAPH;Lo;0;R;;;;;N;;;;; 1086B;PALMYRENE LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 1086C;PALMYRENE LETTER MEM;Lo;0;R;;;;;N;;;;; 1086D;PALMYRENE LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; 1086E;PALMYRENE LETTER NUN;Lo;0;R;;;;;N;;;;; 1086F;PALMYRENE LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10870;PALMYRENE LETTER AYIN;Lo;0;R;;;;;N;;;;; 10871;PALMYRENE LETTER PE;Lo;0;R;;;;;N;;;;; 10872;PALMYRENE LETTER SADHE;Lo;0;R;;;;;N;;;;; 10873;PALMYRENE LETTER QOPH;Lo;0;R;;;;;N;;;;; 10874;PALMYRENE LETTER RESH;Lo;0;R;;;;;N;;;;; 10875;PALMYRENE LETTER SHIN;Lo;0;R;;;;;N;;;;; 10876;PALMYRENE LETTER TAW;Lo;0;R;;;;;N;;;;; 10877;PALMYRENE LEFT-POINTING FLEURON;So;0;R;;;;;N;;;;; 10878;PALMYRENE RIGHT-POINTING FLEURON;So;0;R;;;;;N;;;;; 10879;PALMYRENE NUMBER ONE;No;0;R;;;;1;N;;;;; 1087A;PALMYRENE NUMBER TWO;No;0;R;;;;2;N;;;;; 1087B;PALMYRENE NUMBER THREE;No;0;R;;;;3;N;;;;; 1087C;PALMYRENE NUMBER FOUR;No;0;R;;;;4;N;;;;; 1087D;PALMYRENE NUMBER FIVE;No;0;R;;;;5;N;;;;; 1087E;PALMYRENE NUMBER TEN;No;0;R;;;;10;N;;;;; 1087F;PALMYRENE NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10880;NABATAEAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;; 10881;NABATAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10882;NABATAEAN LETTER FINAL BETH;Lo;0;R;;;;;N;;;;; 10883;NABATAEAN LETTER BETH;Lo;0;R;;;;;N;;;;; 10884;NABATAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10885;NABATAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;; 10886;NABATAEAN LETTER FINAL HE;Lo;0;R;;;;;N;;;;; 10887;NABATAEAN LETTER HE;Lo;0;R;;;;;N;;;;; 10888;NABATAEAN LETTER WAW;Lo;0;R;;;;;N;;;;; 10889;NABATAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 1088A;NABATAEAN LETTER HETH;Lo;0;R;;;;;N;;;;; 1088B;NABATAEAN LETTER TETH;Lo;0;R;;;;;N;;;;; 1088C;NABATAEAN LETTER FINAL YODH;Lo;0;R;;;;;N;;;;; 1088D;NABATAEAN LETTER YODH;Lo;0;R;;;;;N;;;;; 1088E;NABATAEAN LETTER FINAL KAPH;Lo;0;R;;;;;N;;;;; 1088F;NABATAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;; 10890;NABATAEAN LETTER FINAL LAMEDH;Lo;0;R;;;;;N;;;;; 10891;NABATAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10892;NABATAEAN LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; 10893;NABATAEAN LETTER MEM;Lo;0;R;;;;;N;;;;; 10894;NABATAEAN LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; 10895;NABATAEAN LETTER NUN;Lo;0;R;;;;;N;;;;; 10896;NABATAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10897;NABATAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;; 10898;NABATAEAN LETTER PE;Lo;0;R;;;;;N;;;;; 10899;NABATAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;; 1089A;NABATAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;; 1089B;NABATAEAN LETTER RESH;Lo;0;R;;;;;N;;;;; 1089C;NABATAEAN LETTER FINAL SHIN;Lo;0;R;;;;;N;;;;; 1089D;NABATAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 1089E;NABATAEAN LETTER TAW;Lo;0;R;;;;;N;;;;; 108A7;NABATAEAN NUMBER ONE;No;0;R;;;;1;N;;;;; 108A8;NABATAEAN NUMBER TWO;No;0;R;;;;2;N;;;;; 108A9;NABATAEAN NUMBER THREE;No;0;R;;;;3;N;;;;; 108AA;NABATAEAN NUMBER FOUR;No;0;R;;;;4;N;;;;; 108AB;NABATAEAN CRUCIFORM NUMBER FOUR;No;0;R;;;;4;N;;;;; 108AC;NABATAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;; 108AD;NABATAEAN NUMBER TEN;No;0;R;;;;10;N;;;;; 108AE;NABATAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 108AF;NABATAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 108E0;HATRAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; 108E1;HATRAN LETTER BETH;Lo;0;R;;;;;N;;;;; 108E2;HATRAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; 108E3;HATRAN LETTER DALETH-RESH;Lo;0;R;;;;;N;;;;; 108E4;HATRAN LETTER HE;Lo;0;R;;;;;N;;;;; 108E5;HATRAN LETTER WAW;Lo;0;R;;;;;N;;;;; 108E6;HATRAN LETTER ZAYN;Lo;0;R;;;;;N;;;;; 108E7;HATRAN LETTER HETH;Lo;0;R;;;;;N;;;;; 108E8;HATRAN LETTER TETH;Lo;0;R;;;;;N;;;;; 108E9;HATRAN LETTER YODH;Lo;0;R;;;;;N;;;;; 108EA;HATRAN LETTER KAPH;Lo;0;R;;;;;N;;;;; 108EB;HATRAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 108EC;HATRAN LETTER MEM;Lo;0;R;;;;;N;;;;; 108ED;HATRAN LETTER NUN;Lo;0;R;;;;;N;;;;; 108EE;HATRAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 108EF;HATRAN LETTER AYN;Lo;0;R;;;;;N;;;;; 108F0;HATRAN LETTER PE;Lo;0;R;;;;;N;;;;; 108F1;HATRAN LETTER SADHE;Lo;0;R;;;;;N;;;;; 108F2;HATRAN LETTER QOPH;Lo;0;R;;;;;N;;;;; 108F4;HATRAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 108F5;HATRAN LETTER TAW;Lo;0;R;;;;;N;;;;; 108FB;HATRAN NUMBER ONE;No;0;R;;;;1;N;;;;; 108FC;HATRAN NUMBER FIVE;No;0;R;;;;5;N;;;;; 108FD;HATRAN NUMBER TEN;No;0;R;;;;10;N;;;;; 108FE;HATRAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 108FF;HATRAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10900;PHOENICIAN LETTER ALF;Lo;0;R;;;;;N;;;;; 10901;PHOENICIAN LETTER BET;Lo;0;R;;;;;N;;;;; 10902;PHOENICIAN LETTER GAML;Lo;0;R;;;;;N;;;;; 10903;PHOENICIAN LETTER DELT;Lo;0;R;;;;;N;;;;; 10904;PHOENICIAN LETTER HE;Lo;0;R;;;;;N;;;;; 10905;PHOENICIAN LETTER WAU;Lo;0;R;;;;;N;;;;; 10906;PHOENICIAN LETTER ZAI;Lo;0;R;;;;;N;;;;; 10907;PHOENICIAN LETTER HET;Lo;0;R;;;;;N;;;;; 10908;PHOENICIAN LETTER TET;Lo;0;R;;;;;N;;;;; 10909;PHOENICIAN LETTER YOD;Lo;0;R;;;;;N;;;;; 1090A;PHOENICIAN LETTER KAF;Lo;0;R;;;;;N;;;;; 1090B;PHOENICIAN LETTER LAMD;Lo;0;R;;;;;N;;;;; 1090C;PHOENICIAN LETTER MEM;Lo;0;R;;;;;N;;;;; 1090D;PHOENICIAN LETTER NUN;Lo;0;R;;;;;N;;;;; 1090E;PHOENICIAN LETTER SEMK;Lo;0;R;;;;;N;;;;; 1090F;PHOENICIAN LETTER AIN;Lo;0;R;;;;;N;;;;; 10910;PHOENICIAN LETTER PE;Lo;0;R;;;;;N;;;;; 10911;PHOENICIAN LETTER SADE;Lo;0;R;;;;;N;;;;; 10912;PHOENICIAN LETTER QOF;Lo;0;R;;;;;N;;;;; 10913;PHOENICIAN LETTER ROSH;Lo;0;R;;;;;N;;;;; 10914;PHOENICIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 10915;PHOENICIAN LETTER TAU;Lo;0;R;;;;;N;;;;; 10916;PHOENICIAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10917;PHOENICIAN NUMBER TEN;No;0;R;;;;10;N;;;;; 10918;PHOENICIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10919;PHOENICIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 1091A;PHOENICIAN NUMBER TWO;No;0;R;;;;2;N;;;;; 1091B;PHOENICIAN NUMBER THREE;No;0;R;;;;3;N;;;;; 1091F;PHOENICIAN WORD SEPARATOR;Po;0;ON;;;;;N;;;;; 10920;LYDIAN LETTER A;Lo;0;R;;;;;N;;;;; 10921;LYDIAN LETTER B;Lo;0;R;;;;;N;;;;; 10922;LYDIAN LETTER G;Lo;0;R;;;;;N;;;;; 10923;LYDIAN LETTER D;Lo;0;R;;;;;N;;;;; 10924;LYDIAN LETTER E;Lo;0;R;;;;;N;;;;; 10925;LYDIAN LETTER V;Lo;0;R;;;;;N;;;;; 10926;LYDIAN LETTER I;Lo;0;R;;;;;N;;;;; 10927;LYDIAN LETTER Y;Lo;0;R;;;;;N;;;;; 10928;LYDIAN LETTER K;Lo;0;R;;;;;N;;;;; 10929;LYDIAN LETTER L;Lo;0;R;;;;;N;;;;; 1092A;LYDIAN LETTER M;Lo;0;R;;;;;N;;;;; 1092B;LYDIAN LETTER N;Lo;0;R;;;;;N;;;;; 1092C;LYDIAN LETTER O;Lo;0;R;;;;;N;;;;; 1092D;LYDIAN LETTER R;Lo;0;R;;;;;N;;;;; 1092E;LYDIAN LETTER SS;Lo;0;R;;;;;N;;;;; 1092F;LYDIAN LETTER T;Lo;0;R;;;;;N;;;;; 10930;LYDIAN LETTER U;Lo;0;R;;;;;N;;;;; 10931;LYDIAN LETTER F;Lo;0;R;;;;;N;;;;; 10932;LYDIAN LETTER Q;Lo;0;R;;;;;N;;;;; 10933;LYDIAN LETTER S;Lo;0;R;;;;;N;;;;; 10934;LYDIAN LETTER TT;Lo;0;R;;;;;N;;;;; 10935;LYDIAN LETTER AN;Lo;0;R;;;;;N;;;;; 10936;LYDIAN LETTER EN;Lo;0;R;;;;;N;;;;; 10937;LYDIAN LETTER LY;Lo;0;R;;;;;N;;;;; 10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;; 10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;; 1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;; 10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;; 10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;; 10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;; 10983;MEROITIC HIEROGLYPHIC LETTER O;Lo;0;R;;;;;N;;;;; 10984;MEROITIC HIEROGLYPHIC LETTER YA;Lo;0;R;;;;;N;;;;; 10985;MEROITIC HIEROGLYPHIC LETTER WA;Lo;0;R;;;;;N;;;;; 10986;MEROITIC HIEROGLYPHIC LETTER BA;Lo;0;R;;;;;N;;;;; 10987;MEROITIC HIEROGLYPHIC LETTER BA-2;Lo;0;R;;;;;N;;;;; 10988;MEROITIC HIEROGLYPHIC LETTER PA;Lo;0;R;;;;;N;;;;; 10989;MEROITIC HIEROGLYPHIC LETTER MA;Lo;0;R;;;;;N;;;;; 1098A;MEROITIC HIEROGLYPHIC LETTER NA;Lo;0;R;;;;;N;;;;; 1098B;MEROITIC HIEROGLYPHIC LETTER NA-2;Lo;0;R;;;;;N;;;;; 1098C;MEROITIC HIEROGLYPHIC LETTER NE;Lo;0;R;;;;;N;;;;; 1098D;MEROITIC HIEROGLYPHIC LETTER NE-2;Lo;0;R;;;;;N;;;;; 1098E;MEROITIC HIEROGLYPHIC LETTER RA;Lo;0;R;;;;;N;;;;; 1098F;MEROITIC HIEROGLYPHIC LETTER RA-2;Lo;0;R;;;;;N;;;;; 10990;MEROITIC HIEROGLYPHIC LETTER LA;Lo;0;R;;;;;N;;;;; 10991;MEROITIC HIEROGLYPHIC LETTER KHA;Lo;0;R;;;;;N;;;;; 10992;MEROITIC HIEROGLYPHIC LETTER HHA;Lo;0;R;;;;;N;;;;; 10993;MEROITIC HIEROGLYPHIC LETTER SA;Lo;0;R;;;;;N;;;;; 10994;MEROITIC HIEROGLYPHIC LETTER SA-2;Lo;0;R;;;;;N;;;;; 10995;MEROITIC HIEROGLYPHIC LETTER SE;Lo;0;R;;;;;N;;;;; 10996;MEROITIC HIEROGLYPHIC LETTER KA;Lo;0;R;;;;;N;;;;; 10997;MEROITIC HIEROGLYPHIC LETTER QA;Lo;0;R;;;;;N;;;;; 10998;MEROITIC HIEROGLYPHIC LETTER TA;Lo;0;R;;;;;N;;;;; 10999;MEROITIC HIEROGLYPHIC LETTER TA-2;Lo;0;R;;;;;N;;;;; 1099A;MEROITIC HIEROGLYPHIC LETTER TE;Lo;0;R;;;;;N;;;;; 1099B;MEROITIC HIEROGLYPHIC LETTER TE-2;Lo;0;R;;;;;N;;;;; 1099C;MEROITIC HIEROGLYPHIC LETTER TO;Lo;0;R;;;;;N;;;;; 1099D;MEROITIC HIEROGLYPHIC LETTER DA;Lo;0;R;;;;;N;;;;; 1099E;MEROITIC HIEROGLYPHIC SYMBOL VIDJ;Lo;0;R;;;;;N;;;;; 1099F;MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2;Lo;0;R;;;;;N;;;;; 109A0;MEROITIC CURSIVE LETTER A;Lo;0;R;;;;;N;;;;; 109A1;MEROITIC CURSIVE LETTER E;Lo;0;R;;;;;N;;;;; 109A2;MEROITIC CURSIVE LETTER I;Lo;0;R;;;;;N;;;;; 109A3;MEROITIC CURSIVE LETTER O;Lo;0;R;;;;;N;;;;; 109A4;MEROITIC CURSIVE LETTER YA;Lo;0;R;;;;;N;;;;; 109A5;MEROITIC CURSIVE LETTER WA;Lo;0;R;;;;;N;;;;; 109A6;MEROITIC CURSIVE LETTER BA;Lo;0;R;;;;;N;;;;; 109A7;MEROITIC CURSIVE LETTER PA;Lo;0;R;;;;;N;;;;; 109A8;MEROITIC CURSIVE LETTER MA;Lo;0;R;;;;;N;;;;; 109A9;MEROITIC CURSIVE LETTER NA;Lo;0;R;;;;;N;;;;; 109AA;MEROITIC CURSIVE LETTER NE;Lo;0;R;;;;;N;;;;; 109AB;MEROITIC CURSIVE LETTER RA;Lo;0;R;;;;;N;;;;; 109AC;MEROITIC CURSIVE LETTER LA;Lo;0;R;;;;;N;;;;; 109AD;MEROITIC CURSIVE LETTER KHA;Lo;0;R;;;;;N;;;;; 109AE;MEROITIC CURSIVE LETTER HHA;Lo;0;R;;;;;N;;;;; 109AF;MEROITIC CURSIVE LETTER SA;Lo;0;R;;;;;N;;;;; 109B0;MEROITIC CURSIVE LETTER ARCHAIC SA;Lo;0;R;;;;;N;;;;; 109B1;MEROITIC CURSIVE LETTER SE;Lo;0;R;;;;;N;;;;; 109B2;MEROITIC CURSIVE LETTER KA;Lo;0;R;;;;;N;;;;; 109B3;MEROITIC CURSIVE LETTER QA;Lo;0;R;;;;;N;;;;; 109B4;MEROITIC CURSIVE LETTER TA;Lo;0;R;;;;;N;;;;; 109B5;MEROITIC CURSIVE LETTER TE;Lo;0;R;;;;;N;;;;; 109B6;MEROITIC CURSIVE LETTER TO;Lo;0;R;;;;;N;;;;; 109B7;MEROITIC CURSIVE LETTER DA;Lo;0;R;;;;;N;;;;; 109BC;MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS;No;0;R;;;;11/12;N;;;;; 109BD;MEROITIC CURSIVE FRACTION ONE HALF;No;0;R;;;;1/2;N;;;;; 109BE;MEROITIC CURSIVE LOGOGRAM RMT;Lo;0;R;;;;;N;;;;; 109BF;MEROITIC CURSIVE LOGOGRAM IMN;Lo;0;R;;;;;N;;;;; 109C0;MEROITIC CURSIVE NUMBER ONE;No;0;R;;;;1;N;;;;; 109C1;MEROITIC CURSIVE NUMBER TWO;No;0;R;;;;2;N;;;;; 109C2;MEROITIC CURSIVE NUMBER THREE;No;0;R;;;;3;N;;;;; 109C3;MEROITIC CURSIVE NUMBER FOUR;No;0;R;;;;4;N;;;;; 109C4;MEROITIC CURSIVE NUMBER FIVE;No;0;R;;;;5;N;;;;; 109C5;MEROITIC CURSIVE NUMBER SIX;No;0;R;;;;6;N;;;;; 109C6;MEROITIC CURSIVE NUMBER SEVEN;No;0;R;;;;7;N;;;;; 109C7;MEROITIC CURSIVE NUMBER EIGHT;No;0;R;;;;8;N;;;;; 109C8;MEROITIC CURSIVE NUMBER NINE;No;0;R;;;;9;N;;;;; 109C9;MEROITIC CURSIVE NUMBER TEN;No;0;R;;;;10;N;;;;; 109CA;MEROITIC CURSIVE NUMBER TWENTY;No;0;R;;;;20;N;;;;; 109CB;MEROITIC CURSIVE NUMBER THIRTY;No;0;R;;;;30;N;;;;; 109CC;MEROITIC CURSIVE NUMBER FORTY;No;0;R;;;;40;N;;;;; 109CD;MEROITIC CURSIVE NUMBER FIFTY;No;0;R;;;;50;N;;;;; 109CE;MEROITIC CURSIVE NUMBER SIXTY;No;0;R;;;;60;N;;;;; 109CF;MEROITIC CURSIVE NUMBER SEVENTY;No;0;R;;;;70;N;;;;; 109D2;MEROITIC CURSIVE NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 109D3;MEROITIC CURSIVE NUMBER TWO HUNDRED;No;0;R;;;;200;N;;;;; 109D4;MEROITIC CURSIVE NUMBER THREE HUNDRED;No;0;R;;;;300;N;;;;; 109D5;MEROITIC CURSIVE NUMBER FOUR HUNDRED;No;0;R;;;;400;N;;;;; 109D6;MEROITIC CURSIVE NUMBER FIVE HUNDRED;No;0;R;;;;500;N;;;;; 109D7;MEROITIC CURSIVE NUMBER SIX HUNDRED;No;0;R;;;;600;N;;;;; 109D8;MEROITIC CURSIVE NUMBER SEVEN HUNDRED;No;0;R;;;;700;N;;;;; 109D9;MEROITIC CURSIVE NUMBER EIGHT HUNDRED;No;0;R;;;;800;N;;;;; 109DA;MEROITIC CURSIVE NUMBER NINE HUNDRED;No;0;R;;;;900;N;;;;; 109DB;MEROITIC CURSIVE NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 109DC;MEROITIC CURSIVE NUMBER TWO THOUSAND;No;0;R;;;;2000;N;;;;; 109DD;MEROITIC CURSIVE NUMBER THREE THOUSAND;No;0;R;;;;3000;N;;;;; 109DE;MEROITIC CURSIVE NUMBER FOUR THOUSAND;No;0;R;;;;4000;N;;;;; 109DF;MEROITIC CURSIVE NUMBER FIVE THOUSAND;No;0;R;;;;5000;N;;;;; 109E0;MEROITIC CURSIVE NUMBER SIX THOUSAND;No;0;R;;;;6000;N;;;;; 109E1;MEROITIC CURSIVE NUMBER SEVEN THOUSAND;No;0;R;;;;7000;N;;;;; 109E2;MEROITIC CURSIVE NUMBER EIGHT THOUSAND;No;0;R;;;;8000;N;;;;; 109E3;MEROITIC CURSIVE NUMBER NINE THOUSAND;No;0;R;;;;9000;N;;;;; 109E4;MEROITIC CURSIVE NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;; 109E5;MEROITIC CURSIVE NUMBER TWENTY THOUSAND;No;0;R;;;;20000;N;;;;; 109E6;MEROITIC CURSIVE NUMBER THIRTY THOUSAND;No;0;R;;;;30000;N;;;;; 109E7;MEROITIC CURSIVE NUMBER FORTY THOUSAND;No;0;R;;;;40000;N;;;;; 109E8;MEROITIC CURSIVE NUMBER FIFTY THOUSAND;No;0;R;;;;50000;N;;;;; 109E9;MEROITIC CURSIVE NUMBER SIXTY THOUSAND;No;0;R;;;;60000;N;;;;; 109EA;MEROITIC CURSIVE NUMBER SEVENTY THOUSAND;No;0;R;;;;70000;N;;;;; 109EB;MEROITIC CURSIVE NUMBER EIGHTY THOUSAND;No;0;R;;;;80000;N;;;;; 109EC;MEROITIC CURSIVE NUMBER NINETY THOUSAND;No;0;R;;;;90000;N;;;;; 109ED;MEROITIC CURSIVE NUMBER ONE HUNDRED THOUSAND;No;0;R;;;;100000;N;;;;; 109EE;MEROITIC CURSIVE NUMBER TWO HUNDRED THOUSAND;No;0;R;;;;200000;N;;;;; 109EF;MEROITIC CURSIVE NUMBER THREE HUNDRED THOUSAND;No;0;R;;;;300000;N;;;;; 109F0;MEROITIC CURSIVE NUMBER FOUR HUNDRED THOUSAND;No;0;R;;;;400000;N;;;;; 109F1;MEROITIC CURSIVE NUMBER FIVE HUNDRED THOUSAND;No;0;R;;;;500000;N;;;;; 109F2;MEROITIC CURSIVE NUMBER SIX HUNDRED THOUSAND;No;0;R;;;;600000;N;;;;; 109F3;MEROITIC CURSIVE NUMBER SEVEN HUNDRED THOUSAND;No;0;R;;;;700000;N;;;;; 109F4;MEROITIC CURSIVE NUMBER EIGHT HUNDRED THOUSAND;No;0;R;;;;800000;N;;;;; 109F5;MEROITIC CURSIVE NUMBER NINE HUNDRED THOUSAND;No;0;R;;;;900000;N;;;;; 109F6;MEROITIC CURSIVE FRACTION ONE TWELFTH;No;0;R;;;;1/12;N;;;;; 109F7;MEROITIC CURSIVE FRACTION TWO TWELFTHS;No;0;R;;;;2/12;N;;;;; 109F8;MEROITIC CURSIVE FRACTION THREE TWELFTHS;No;0;R;;;;3/12;N;;;;; 109F9;MEROITIC CURSIVE FRACTION FOUR TWELFTHS;No;0;R;;;;4/12;N;;;;; 109FA;MEROITIC CURSIVE FRACTION FIVE TWELFTHS;No;0;R;;;;5/12;N;;;;; 109FB;MEROITIC CURSIVE FRACTION SIX TWELFTHS;No;0;R;;;;6/12;N;;;;; 109FC;MEROITIC CURSIVE FRACTION SEVEN TWELFTHS;No;0;R;;;;7/12;N;;;;; 109FD;MEROITIC CURSIVE FRACTION EIGHT TWELFTHS;No;0;R;;;;8/12;N;;;;; 109FE;MEROITIC CURSIVE FRACTION NINE TWELFTHS;No;0;R;;;;9/12;N;;;;; 109FF;MEROITIC CURSIVE FRACTION TEN TWELFTHS;No;0;R;;;;10/12;N;;;;; 10A00;KHAROSHTHI LETTER A;Lo;0;R;;;;;N;;;;; 10A01;KHAROSHTHI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 10A02;KHAROSHTHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 10A03;KHAROSHTHI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 10A05;KHAROSHTHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 10A06;KHAROSHTHI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 10A0C;KHAROSHTHI VOWEL LENGTH MARK;Mn;0;NSM;;;;;N;;;;; 10A0D;KHAROSHTHI SIGN DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;; 10A0E;KHAROSHTHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 10A0F;KHAROSHTHI SIGN VISARGA;Mn;230;NSM;;;;;N;;;;; 10A10;KHAROSHTHI LETTER KA;Lo;0;R;;;;;N;;;;; 10A11;KHAROSHTHI LETTER KHA;Lo;0;R;;;;;N;;;;; 10A12;KHAROSHTHI LETTER GA;Lo;0;R;;;;;N;;;;; 10A13;KHAROSHTHI LETTER GHA;Lo;0;R;;;;;N;;;;; 10A15;KHAROSHTHI LETTER CA;Lo;0;R;;;;;N;;;;; 10A16;KHAROSHTHI LETTER CHA;Lo;0;R;;;;;N;;;;; 10A17;KHAROSHTHI LETTER JA;Lo;0;R;;;;;N;;;;; 10A19;KHAROSHTHI LETTER NYA;Lo;0;R;;;;;N;;;;; 10A1A;KHAROSHTHI LETTER TTA;Lo;0;R;;;;;N;;;;; 10A1B;KHAROSHTHI LETTER TTHA;Lo;0;R;;;;;N;;;;; 10A1C;KHAROSHTHI LETTER DDA;Lo;0;R;;;;;N;;;;; 10A1D;KHAROSHTHI LETTER DDHA;Lo;0;R;;;;;N;;;;; 10A1E;KHAROSHTHI LETTER NNA;Lo;0;R;;;;;N;;;;; 10A1F;KHAROSHTHI LETTER TA;Lo;0;R;;;;;N;;;;; 10A20;KHAROSHTHI LETTER THA;Lo;0;R;;;;;N;;;;; 10A21;KHAROSHTHI LETTER DA;Lo;0;R;;;;;N;;;;; 10A22;KHAROSHTHI LETTER DHA;Lo;0;R;;;;;N;;;;; 10A23;KHAROSHTHI LETTER NA;Lo;0;R;;;;;N;;;;; 10A24;KHAROSHTHI LETTER PA;Lo;0;R;;;;;N;;;;; 10A25;KHAROSHTHI LETTER PHA;Lo;0;R;;;;;N;;;;; 10A26;KHAROSHTHI LETTER BA;Lo;0;R;;;;;N;;;;; 10A27;KHAROSHTHI LETTER BHA;Lo;0;R;;;;;N;;;;; 10A28;KHAROSHTHI LETTER MA;Lo;0;R;;;;;N;;;;; 10A29;KHAROSHTHI LETTER YA;Lo;0;R;;;;;N;;;;; 10A2A;KHAROSHTHI LETTER RA;Lo;0;R;;;;;N;;;;; 10A2B;KHAROSHTHI LETTER LA;Lo;0;R;;;;;N;;;;; 10A2C;KHAROSHTHI LETTER VA;Lo;0;R;;;;;N;;;;; 10A2D;KHAROSHTHI LETTER SHA;Lo;0;R;;;;;N;;;;; 10A2E;KHAROSHTHI LETTER SSA;Lo;0;R;;;;;N;;;;; 10A2F;KHAROSHTHI LETTER SA;Lo;0;R;;;;;N;;;;; 10A30;KHAROSHTHI LETTER ZA;Lo;0;R;;;;;N;;;;; 10A31;KHAROSHTHI LETTER HA;Lo;0;R;;;;;N;;;;; 10A32;KHAROSHTHI LETTER KKA;Lo;0;R;;;;;N;;;;; 10A33;KHAROSHTHI LETTER TTTHA;Lo;0;R;;;;;N;;;;; 10A38;KHAROSHTHI SIGN BAR ABOVE;Mn;230;NSM;;;;;N;;;;; 10A39;KHAROSHTHI SIGN CAUDA;Mn;1;NSM;;;;;N;;;;; 10A3A;KHAROSHTHI SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;; 10A3F;KHAROSHTHI VIRAMA;Mn;9;NSM;;;;;N;;;;; 10A40;KHAROSHTHI DIGIT ONE;No;0;R;;;1;1;N;;;;; 10A41;KHAROSHTHI DIGIT TWO;No;0;R;;;2;2;N;;;;; 10A42;KHAROSHTHI DIGIT THREE;No;0;R;;;3;3;N;;;;; 10A43;KHAROSHTHI DIGIT FOUR;No;0;R;;;4;4;N;;;;; 10A44;KHAROSHTHI NUMBER TEN;No;0;R;;;;10;N;;;;; 10A45;KHAROSHTHI NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10A46;KHAROSHTHI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10A47;KHAROSHTHI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 10A50;KHAROSHTHI PUNCTUATION DOT;Po;0;R;;;;;N;;;;; 10A51;KHAROSHTHI PUNCTUATION SMALL CIRCLE;Po;0;R;;;;;N;;;;; 10A52;KHAROSHTHI PUNCTUATION CIRCLE;Po;0;R;;;;;N;;;;; 10A53;KHAROSHTHI PUNCTUATION CRESCENT BAR;Po;0;R;;;;;N;;;;; 10A54;KHAROSHTHI PUNCTUATION MANGALAM;Po;0;R;;;;;N;;;;; 10A55;KHAROSHTHI PUNCTUATION LOTUS;Po;0;R;;;;;N;;;;; 10A56;KHAROSHTHI PUNCTUATION DANDA;Po;0;R;;;;;N;;;;; 10A57;KHAROSHTHI PUNCTUATION DOUBLE DANDA;Po;0;R;;;;;N;;;;; 10A58;KHAROSHTHI PUNCTUATION LINES;Po;0;R;;;;;N;;;;; 10A60;OLD SOUTH ARABIAN LETTER HE;Lo;0;R;;;;;N;;;;; 10A61;OLD SOUTH ARABIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10A62;OLD SOUTH ARABIAN LETTER HETH;Lo;0;R;;;;;N;;;;; 10A63;OLD SOUTH ARABIAN LETTER MEM;Lo;0;R;;;;;N;;;;; 10A64;OLD SOUTH ARABIAN LETTER QOPH;Lo;0;R;;;;;N;;;;; 10A65;OLD SOUTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;; 10A66;OLD SOUTH ARABIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 10A67;OLD SOUTH ARABIAN LETTER RESH;Lo;0;R;;;;;N;;;;; 10A68;OLD SOUTH ARABIAN LETTER BETH;Lo;0;R;;;;;N;;;;; 10A69;OLD SOUTH ARABIAN LETTER TAW;Lo;0;R;;;;;N;;;;; 10A6A;OLD SOUTH ARABIAN LETTER SAT;Lo;0;R;;;;;N;;;;; 10A6B;OLD SOUTH ARABIAN LETTER KAPH;Lo;0;R;;;;;N;;;;; 10A6C;OLD SOUTH ARABIAN LETTER NUN;Lo;0;R;;;;;N;;;;; 10A6D;OLD SOUTH ARABIAN LETTER KHETH;Lo;0;R;;;;;N;;;;; 10A6E;OLD SOUTH ARABIAN LETTER SADHE;Lo;0;R;;;;;N;;;;; 10A6F;OLD SOUTH ARABIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10A70;OLD SOUTH ARABIAN LETTER FE;Lo;0;R;;;;;N;;;;; 10A71;OLD SOUTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;; 10A72;OLD SOUTH ARABIAN LETTER AYN;Lo;0;R;;;;;N;;;;; 10A73;OLD SOUTH ARABIAN LETTER DHADHE;Lo;0;R;;;;;N;;;;; 10A74;OLD SOUTH ARABIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10A75;OLD SOUTH ARABIAN LETTER DALETH;Lo;0;R;;;;;N;;;;; 10A76;OLD SOUTH ARABIAN LETTER GHAYN;Lo;0;R;;;;;N;;;;; 10A77;OLD SOUTH ARABIAN LETTER TETH;Lo;0;R;;;;;N;;;;; 10A78;OLD SOUTH ARABIAN LETTER ZAYN;Lo;0;R;;;;;N;;;;; 10A79;OLD SOUTH ARABIAN LETTER DHALETH;Lo;0;R;;;;;N;;;;; 10A7A;OLD SOUTH ARABIAN LETTER YODH;Lo;0;R;;;;;N;;;;; 10A7B;OLD SOUTH ARABIAN LETTER THAW;Lo;0;R;;;;;N;;;;; 10A7C;OLD SOUTH ARABIAN LETTER THETH;Lo;0;R;;;;;N;;;;; 10A7D;OLD SOUTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10A7E;OLD SOUTH ARABIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;; 10A7F;OLD SOUTH ARABIAN NUMERIC INDICATOR;Po;0;R;;;;;N;;;;; 10A80;OLD NORTH ARABIAN LETTER HEH;Lo;0;R;;;;;N;;;;; 10A81;OLD NORTH ARABIAN LETTER LAM;Lo;0;R;;;;;N;;;;; 10A82;OLD NORTH ARABIAN LETTER HAH;Lo;0;R;;;;;N;;;;; 10A83;OLD NORTH ARABIAN LETTER MEEM;Lo;0;R;;;;;N;;;;; 10A84;OLD NORTH ARABIAN LETTER QAF;Lo;0;R;;;;;N;;;;; 10A85;OLD NORTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;; 10A86;OLD NORTH ARABIAN LETTER ES-2;Lo;0;R;;;;;N;;;;; 10A87;OLD NORTH ARABIAN LETTER REH;Lo;0;R;;;;;N;;;;; 10A88;OLD NORTH ARABIAN LETTER BEH;Lo;0;R;;;;;N;;;;; 10A89;OLD NORTH ARABIAN LETTER TEH;Lo;0;R;;;;;N;;;;; 10A8A;OLD NORTH ARABIAN LETTER ES-1;Lo;0;R;;;;;N;;;;; 10A8B;OLD NORTH ARABIAN LETTER KAF;Lo;0;R;;;;;N;;;;; 10A8C;OLD NORTH ARABIAN LETTER NOON;Lo;0;R;;;;;N;;;;; 10A8D;OLD NORTH ARABIAN LETTER KHAH;Lo;0;R;;;;;N;;;;; 10A8E;OLD NORTH ARABIAN LETTER SAD;Lo;0;R;;;;;N;;;;; 10A8F;OLD NORTH ARABIAN LETTER ES-3;Lo;0;R;;;;;N;;;;; 10A90;OLD NORTH ARABIAN LETTER FEH;Lo;0;R;;;;;N;;;;; 10A91;OLD NORTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;; 10A92;OLD NORTH ARABIAN LETTER AIN;Lo;0;R;;;;;N;;;;; 10A93;OLD NORTH ARABIAN LETTER DAD;Lo;0;R;;;;;N;;;;; 10A94;OLD NORTH ARABIAN LETTER GEEM;Lo;0;R;;;;;N;;;;; 10A95;OLD NORTH ARABIAN LETTER DAL;Lo;0;R;;;;;N;;;;; 10A96;OLD NORTH ARABIAN LETTER GHAIN;Lo;0;R;;;;;N;;;;; 10A97;OLD NORTH ARABIAN LETTER TAH;Lo;0;R;;;;;N;;;;; 10A98;OLD NORTH ARABIAN LETTER ZAIN;Lo;0;R;;;;;N;;;;; 10A99;OLD NORTH ARABIAN LETTER THAL;Lo;0;R;;;;;N;;;;; 10A9A;OLD NORTH ARABIAN LETTER YEH;Lo;0;R;;;;;N;;;;; 10A9B;OLD NORTH ARABIAN LETTER THEH;Lo;0;R;;;;;N;;;;; 10A9C;OLD NORTH ARABIAN LETTER ZAH;Lo;0;R;;;;;N;;;;; 10A9D;OLD NORTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10A9E;OLD NORTH ARABIAN NUMBER TEN;No;0;R;;;;10;N;;;;; 10A9F;OLD NORTH ARABIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10AC0;MANICHAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10AC1;MANICHAEAN LETTER BETH;Lo;0;R;;;;;N;;;;; 10AC2;MANICHAEAN LETTER BHETH;Lo;0;R;;;;;N;;;;; 10AC3;MANICHAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10AC4;MANICHAEAN LETTER GHIMEL;Lo;0;R;;;;;N;;;;; 10AC5;MANICHAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;; 10AC6;MANICHAEAN LETTER HE;Lo;0;R;;;;;N;;;;; 10AC7;MANICHAEAN LETTER WAW;Lo;0;R;;;;;N;;;;; 10AC8;MANICHAEAN SIGN UD;So;0;R;;;;;N;;;;; 10AC9;MANICHAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10ACA;MANICHAEAN LETTER ZHAYIN;Lo;0;R;;;;;N;;;;; 10ACB;MANICHAEAN LETTER JAYIN;Lo;0;R;;;;;N;;;;; 10ACC;MANICHAEAN LETTER JHAYIN;Lo;0;R;;;;;N;;;;; 10ACD;MANICHAEAN LETTER HETH;Lo;0;R;;;;;N;;;;; 10ACE;MANICHAEAN LETTER TETH;Lo;0;R;;;;;N;;;;; 10ACF;MANICHAEAN LETTER YODH;Lo;0;R;;;;;N;;;;; 10AD0;MANICHAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;; 10AD1;MANICHAEAN LETTER XAPH;Lo;0;R;;;;;N;;;;; 10AD2;MANICHAEAN LETTER KHAPH;Lo;0;R;;;;;N;;;;; 10AD3;MANICHAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10AD4;MANICHAEAN LETTER DHAMEDH;Lo;0;R;;;;;N;;;;; 10AD5;MANICHAEAN LETTER THAMEDH;Lo;0;R;;;;;N;;;;; 10AD6;MANICHAEAN LETTER MEM;Lo;0;R;;;;;N;;;;; 10AD7;MANICHAEAN LETTER NUN;Lo;0;R;;;;;N;;;;; 10AD8;MANICHAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10AD9;MANICHAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;; 10ADA;MANICHAEAN LETTER AAYIN;Lo;0;R;;;;;N;;;;; 10ADB;MANICHAEAN LETTER PE;Lo;0;R;;;;;N;;;;; 10ADC;MANICHAEAN LETTER FE;Lo;0;R;;;;;N;;;;; 10ADD;MANICHAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;; 10ADE;MANICHAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;; 10ADF;MANICHAEAN LETTER XOPH;Lo;0;R;;;;;N;;;;; 10AE0;MANICHAEAN LETTER QHOPH;Lo;0;R;;;;;N;;;;; 10AE1;MANICHAEAN LETTER RESH;Lo;0;R;;;;;N;;;;; 10AE2;MANICHAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 10AE3;MANICHAEAN LETTER SSHIN;Lo;0;R;;;;;N;;;;; 10AE4;MANICHAEAN LETTER TAW;Lo;0;R;;;;;N;;;;; 10AE5;MANICHAEAN ABBREVIATION MARK ABOVE;Mn;230;NSM;;;;;N;;;;; 10AE6;MANICHAEAN ABBREVIATION MARK BELOW;Mn;220;NSM;;;;;N;;;;; 10AEB;MANICHAEAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10AEC;MANICHAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;; 10AED;MANICHAEAN NUMBER TEN;No;0;R;;;;10;N;;;;; 10AEE;MANICHAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10AEF;MANICHAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10AF0;MANICHAEAN PUNCTUATION STAR;Po;0;R;;;;;N;;;;; 10AF1;MANICHAEAN PUNCTUATION FLEURON;Po;0;R;;;;;N;;;;; 10AF2;MANICHAEAN PUNCTUATION DOUBLE DOT WITHIN DOT;Po;0;R;;;;;N;;;;; 10AF3;MANICHAEAN PUNCTUATION DOT WITHIN DOT;Po;0;R;;;;;N;;;;; 10AF4;MANICHAEAN PUNCTUATION DOT;Po;0;R;;;;;N;;;;; 10AF5;MANICHAEAN PUNCTUATION TWO DOTS;Po;0;R;;;;;N;;;;; 10AF6;MANICHAEAN PUNCTUATION LINE FILLER;Po;0;R;;;;;N;;;;; 10B00;AVESTAN LETTER A;Lo;0;R;;;;;N;;;;; 10B01;AVESTAN LETTER AA;Lo;0;R;;;;;N;;;;; 10B02;AVESTAN LETTER AO;Lo;0;R;;;;;N;;;;; 10B03;AVESTAN LETTER AAO;Lo;0;R;;;;;N;;;;; 10B04;AVESTAN LETTER AN;Lo;0;R;;;;;N;;;;; 10B05;AVESTAN LETTER AAN;Lo;0;R;;;;;N;;;;; 10B06;AVESTAN LETTER AE;Lo;0;R;;;;;N;;;;; 10B07;AVESTAN LETTER AEE;Lo;0;R;;;;;N;;;;; 10B08;AVESTAN LETTER E;Lo;0;R;;;;;N;;;;; 10B09;AVESTAN LETTER EE;Lo;0;R;;;;;N;;;;; 10B0A;AVESTAN LETTER O;Lo;0;R;;;;;N;;;;; 10B0B;AVESTAN LETTER OO;Lo;0;R;;;;;N;;;;; 10B0C;AVESTAN LETTER I;Lo;0;R;;;;;N;;;;; 10B0D;AVESTAN LETTER II;Lo;0;R;;;;;N;;;;; 10B0E;AVESTAN LETTER U;Lo;0;R;;;;;N;;;;; 10B0F;AVESTAN LETTER UU;Lo;0;R;;;;;N;;;;; 10B10;AVESTAN LETTER KE;Lo;0;R;;;;;N;;;;; 10B11;AVESTAN LETTER XE;Lo;0;R;;;;;N;;;;; 10B12;AVESTAN LETTER XYE;Lo;0;R;;;;;N;;;;; 10B13;AVESTAN LETTER XVE;Lo;0;R;;;;;N;;;;; 10B14;AVESTAN LETTER GE;Lo;0;R;;;;;N;;;;; 10B15;AVESTAN LETTER GGE;Lo;0;R;;;;;N;;;;; 10B16;AVESTAN LETTER GHE;Lo;0;R;;;;;N;;;;; 10B17;AVESTAN LETTER CE;Lo;0;R;;;;;N;;;;; 10B18;AVESTAN LETTER JE;Lo;0;R;;;;;N;;;;; 10B19;AVESTAN LETTER TE;Lo;0;R;;;;;N;;;;; 10B1A;AVESTAN LETTER THE;Lo;0;R;;;;;N;;;;; 10B1B;AVESTAN LETTER DE;Lo;0;R;;;;;N;;;;; 10B1C;AVESTAN LETTER DHE;Lo;0;R;;;;;N;;;;; 10B1D;AVESTAN LETTER TTE;Lo;0;R;;;;;N;;;;; 10B1E;AVESTAN LETTER PE;Lo;0;R;;;;;N;;;;; 10B1F;AVESTAN LETTER FE;Lo;0;R;;;;;N;;;;; 10B20;AVESTAN LETTER BE;Lo;0;R;;;;;N;;;;; 10B21;AVESTAN LETTER BHE;Lo;0;R;;;;;N;;;;; 10B22;AVESTAN LETTER NGE;Lo;0;R;;;;;N;;;;; 10B23;AVESTAN LETTER NGYE;Lo;0;R;;;;;N;;;;; 10B24;AVESTAN LETTER NGVE;Lo;0;R;;;;;N;;;;; 10B25;AVESTAN LETTER NE;Lo;0;R;;;;;N;;;;; 10B26;AVESTAN LETTER NYE;Lo;0;R;;;;;N;;;;; 10B27;AVESTAN LETTER NNE;Lo;0;R;;;;;N;;;;; 10B28;AVESTAN LETTER ME;Lo;0;R;;;;;N;;;;; 10B29;AVESTAN LETTER HME;Lo;0;R;;;;;N;;;;; 10B2A;AVESTAN LETTER YYE;Lo;0;R;;;;;N;;;;; 10B2B;AVESTAN LETTER YE;Lo;0;R;;;;;N;;;;; 10B2C;AVESTAN LETTER VE;Lo;0;R;;;;;N;;;;; 10B2D;AVESTAN LETTER RE;Lo;0;R;;;;;N;;;;; 10B2E;AVESTAN LETTER LE;Lo;0;R;;;;;N;;;;; 10B2F;AVESTAN LETTER SE;Lo;0;R;;;;;N;;;;; 10B30;AVESTAN LETTER ZE;Lo;0;R;;;;;N;;;;; 10B31;AVESTAN LETTER SHE;Lo;0;R;;;;;N;;;;; 10B32;AVESTAN LETTER ZHE;Lo;0;R;;;;;N;;;;; 10B33;AVESTAN LETTER SHYE;Lo;0;R;;;;;N;;;;; 10B34;AVESTAN LETTER SSHE;Lo;0;R;;;;;N;;;;; 10B35;AVESTAN LETTER HE;Lo;0;R;;;;;N;;;;; 10B39;AVESTAN ABBREVIATION MARK;Po;0;ON;;;;;N;;;;; 10B3A;TINY TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B3B;SMALL TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B3C;LARGE TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B3D;LARGE ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B3E;LARGE TWO RINGS OVER ONE RING PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B3F;LARGE ONE RING OVER TWO RINGS PUNCTUATION;Po;0;ON;;;;;N;;;;; 10B40;INSCRIPTIONAL PARTHIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10B41;INSCRIPTIONAL PARTHIAN LETTER BETH;Lo;0;R;;;;;N;;;;; 10B42;INSCRIPTIONAL PARTHIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10B43;INSCRIPTIONAL PARTHIAN LETTER DALETH;Lo;0;R;;;;;N;;;;; 10B44;INSCRIPTIONAL PARTHIAN LETTER HE;Lo;0;R;;;;;N;;;;; 10B45;INSCRIPTIONAL PARTHIAN LETTER WAW;Lo;0;R;;;;;N;;;;; 10B46;INSCRIPTIONAL PARTHIAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10B47;INSCRIPTIONAL PARTHIAN LETTER HETH;Lo;0;R;;;;;N;;;;; 10B48;INSCRIPTIONAL PARTHIAN LETTER TETH;Lo;0;R;;;;;N;;;;; 10B49;INSCRIPTIONAL PARTHIAN LETTER YODH;Lo;0;R;;;;;N;;;;; 10B4A;INSCRIPTIONAL PARTHIAN LETTER KAPH;Lo;0;R;;;;;N;;;;; 10B4B;INSCRIPTIONAL PARTHIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10B4C;INSCRIPTIONAL PARTHIAN LETTER MEM;Lo;0;R;;;;;N;;;;; 10B4D;INSCRIPTIONAL PARTHIAN LETTER NUN;Lo;0;R;;;;;N;;;;; 10B4E;INSCRIPTIONAL PARTHIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10B4F;INSCRIPTIONAL PARTHIAN LETTER AYIN;Lo;0;R;;;;;N;;;;; 10B50;INSCRIPTIONAL PARTHIAN LETTER PE;Lo;0;R;;;;;N;;;;; 10B51;INSCRIPTIONAL PARTHIAN LETTER SADHE;Lo;0;R;;;;;N;;;;; 10B52;INSCRIPTIONAL PARTHIAN LETTER QOPH;Lo;0;R;;;;;N;;;;; 10B53;INSCRIPTIONAL PARTHIAN LETTER RESH;Lo;0;R;;;;;N;;;;; 10B54;INSCRIPTIONAL PARTHIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; 10B55;INSCRIPTIONAL PARTHIAN LETTER TAW;Lo;0;R;;;;;N;;;;; 10B58;INSCRIPTIONAL PARTHIAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10B59;INSCRIPTIONAL PARTHIAN NUMBER TWO;No;0;R;;;;2;N;;;;; 10B5A;INSCRIPTIONAL PARTHIAN NUMBER THREE;No;0;R;;;;3;N;;;;; 10B5B;INSCRIPTIONAL PARTHIAN NUMBER FOUR;No;0;R;;;;4;N;;;;; 10B5C;INSCRIPTIONAL PARTHIAN NUMBER TEN;No;0;R;;;;10;N;;;;; 10B5D;INSCRIPTIONAL PARTHIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10B5E;INSCRIPTIONAL PARTHIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10B5F;INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 10B60;INSCRIPTIONAL PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10B61;INSCRIPTIONAL PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;; 10B62;INSCRIPTIONAL PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10B63;INSCRIPTIONAL PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;; 10B64;INSCRIPTIONAL PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;; 10B65;INSCRIPTIONAL PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;; 10B66;INSCRIPTIONAL PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10B67;INSCRIPTIONAL PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;; 10B68;INSCRIPTIONAL PAHLAVI LETTER TETH;Lo;0;R;;;;;N;;;;; 10B69;INSCRIPTIONAL PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;; 10B6A;INSCRIPTIONAL PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;; 10B6B;INSCRIPTIONAL PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10B6C;INSCRIPTIONAL PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;; 10B6D;INSCRIPTIONAL PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;; 10B6E;INSCRIPTIONAL PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10B6F;INSCRIPTIONAL PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;; 10B70;INSCRIPTIONAL PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;; 10B71;INSCRIPTIONAL PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;; 10B72;INSCRIPTIONAL PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;; 10B78;INSCRIPTIONAL PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;; 10B79;INSCRIPTIONAL PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;; 10B7A;INSCRIPTIONAL PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;; 10B7B;INSCRIPTIONAL PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;; 10B7C;INSCRIPTIONAL PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;; 10B7D;INSCRIPTIONAL PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10B7E;INSCRIPTIONAL PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10B7F;INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 10B80;PSALTER PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;; 10B81;PSALTER PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;; 10B82;PSALTER PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;; 10B83;PSALTER PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;; 10B84;PSALTER PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;; 10B85;PSALTER PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;; 10B86;PSALTER PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;; 10B87;PSALTER PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;; 10B88;PSALTER PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;; 10B89;PSALTER PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;; 10B8A;PSALTER PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;; 10B8B;PSALTER PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;; 10B8C;PSALTER PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;; 10B8D;PSALTER PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;; 10B8E;PSALTER PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;; 10B8F;PSALTER PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;; 10B90;PSALTER PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;; 10B91;PSALTER PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;; 10B99;PSALTER PAHLAVI SECTION MARK;Po;0;R;;;;;N;;;;; 10B9A;PSALTER PAHLAVI TURNED SECTION MARK;Po;0;R;;;;;N;;;;; 10B9B;PSALTER PAHLAVI FOUR DOTS WITH CROSS;Po;0;R;;;;;N;;;;; 10B9C;PSALTER PAHLAVI FOUR DOTS WITH DOT;Po;0;R;;;;;N;;;;; 10BA9;PSALTER PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;; 10BAA;PSALTER PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;; 10BAB;PSALTER PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;; 10BAC;PSALTER PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;; 10BAD;PSALTER PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;; 10BAE;PSALTER PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;; 10BAF;PSALTER PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10C00;OLD TURKIC LETTER ORKHON A;Lo;0;R;;;;;N;;;;; 10C01;OLD TURKIC LETTER YENISEI A;Lo;0;R;;;;;N;;;;; 10C02;OLD TURKIC LETTER YENISEI AE;Lo;0;R;;;;;N;;;;; 10C03;OLD TURKIC LETTER ORKHON I;Lo;0;R;;;;;N;;;;; 10C04;OLD TURKIC LETTER YENISEI I;Lo;0;R;;;;;N;;;;; 10C05;OLD TURKIC LETTER YENISEI E;Lo;0;R;;;;;N;;;;; 10C06;OLD TURKIC LETTER ORKHON O;Lo;0;R;;;;;N;;;;; 10C07;OLD TURKIC LETTER ORKHON OE;Lo;0;R;;;;;N;;;;; 10C08;OLD TURKIC LETTER YENISEI OE;Lo;0;R;;;;;N;;;;; 10C09;OLD TURKIC LETTER ORKHON AB;Lo;0;R;;;;;N;;;;; 10C0A;OLD TURKIC LETTER YENISEI AB;Lo;0;R;;;;;N;;;;; 10C0B;OLD TURKIC LETTER ORKHON AEB;Lo;0;R;;;;;N;;;;; 10C0C;OLD TURKIC LETTER YENISEI AEB;Lo;0;R;;;;;N;;;;; 10C0D;OLD TURKIC LETTER ORKHON AG;Lo;0;R;;;;;N;;;;; 10C0E;OLD TURKIC LETTER YENISEI AG;Lo;0;R;;;;;N;;;;; 10C0F;OLD TURKIC LETTER ORKHON AEG;Lo;0;R;;;;;N;;;;; 10C10;OLD TURKIC LETTER YENISEI AEG;Lo;0;R;;;;;N;;;;; 10C11;OLD TURKIC LETTER ORKHON AD;Lo;0;R;;;;;N;;;;; 10C12;OLD TURKIC LETTER YENISEI AD;Lo;0;R;;;;;N;;;;; 10C13;OLD TURKIC LETTER ORKHON AED;Lo;0;R;;;;;N;;;;; 10C14;OLD TURKIC LETTER ORKHON EZ;Lo;0;R;;;;;N;;;;; 10C15;OLD TURKIC LETTER YENISEI EZ;Lo;0;R;;;;;N;;;;; 10C16;OLD TURKIC LETTER ORKHON AY;Lo;0;R;;;;;N;;;;; 10C17;OLD TURKIC LETTER YENISEI AY;Lo;0;R;;;;;N;;;;; 10C18;OLD TURKIC LETTER ORKHON AEY;Lo;0;R;;;;;N;;;;; 10C19;OLD TURKIC LETTER YENISEI AEY;Lo;0;R;;;;;N;;;;; 10C1A;OLD TURKIC LETTER ORKHON AEK;Lo;0;R;;;;;N;;;;; 10C1B;OLD TURKIC LETTER YENISEI AEK;Lo;0;R;;;;;N;;;;; 10C1C;OLD TURKIC LETTER ORKHON OEK;Lo;0;R;;;;;N;;;;; 10C1D;OLD TURKIC LETTER YENISEI OEK;Lo;0;R;;;;;N;;;;; 10C1E;OLD TURKIC LETTER ORKHON AL;Lo;0;R;;;;;N;;;;; 10C1F;OLD TURKIC LETTER YENISEI AL;Lo;0;R;;;;;N;;;;; 10C20;OLD TURKIC LETTER ORKHON AEL;Lo;0;R;;;;;N;;;;; 10C21;OLD TURKIC LETTER ORKHON ELT;Lo;0;R;;;;;N;;;;; 10C22;OLD TURKIC LETTER ORKHON EM;Lo;0;R;;;;;N;;;;; 10C23;OLD TURKIC LETTER ORKHON AN;Lo;0;R;;;;;N;;;;; 10C24;OLD TURKIC LETTER ORKHON AEN;Lo;0;R;;;;;N;;;;; 10C25;OLD TURKIC LETTER YENISEI AEN;Lo;0;R;;;;;N;;;;; 10C26;OLD TURKIC LETTER ORKHON ENT;Lo;0;R;;;;;N;;;;; 10C27;OLD TURKIC LETTER YENISEI ENT;Lo;0;R;;;;;N;;;;; 10C28;OLD TURKIC LETTER ORKHON ENC;Lo;0;R;;;;;N;;;;; 10C29;OLD TURKIC LETTER YENISEI ENC;Lo;0;R;;;;;N;;;;; 10C2A;OLD TURKIC LETTER ORKHON ENY;Lo;0;R;;;;;N;;;;; 10C2B;OLD TURKIC LETTER YENISEI ENY;Lo;0;R;;;;;N;;;;; 10C2C;OLD TURKIC LETTER YENISEI ANG;Lo;0;R;;;;;N;;;;; 10C2D;OLD TURKIC LETTER ORKHON ENG;Lo;0;R;;;;;N;;;;; 10C2E;OLD TURKIC LETTER YENISEI AENG;Lo;0;R;;;;;N;;;;; 10C2F;OLD TURKIC LETTER ORKHON EP;Lo;0;R;;;;;N;;;;; 10C30;OLD TURKIC LETTER ORKHON OP;Lo;0;R;;;;;N;;;;; 10C31;OLD TURKIC LETTER ORKHON IC;Lo;0;R;;;;;N;;;;; 10C32;OLD TURKIC LETTER ORKHON EC;Lo;0;R;;;;;N;;;;; 10C33;OLD TURKIC LETTER YENISEI EC;Lo;0;R;;;;;N;;;;; 10C34;OLD TURKIC LETTER ORKHON AQ;Lo;0;R;;;;;N;;;;; 10C35;OLD TURKIC LETTER YENISEI AQ;Lo;0;R;;;;;N;;;;; 10C36;OLD TURKIC LETTER ORKHON IQ;Lo;0;R;;;;;N;;;;; 10C37;OLD TURKIC LETTER YENISEI IQ;Lo;0;R;;;;;N;;;;; 10C38;OLD TURKIC LETTER ORKHON OQ;Lo;0;R;;;;;N;;;;; 10C39;OLD TURKIC LETTER YENISEI OQ;Lo;0;R;;;;;N;;;;; 10C3A;OLD TURKIC LETTER ORKHON AR;Lo;0;R;;;;;N;;;;; 10C3B;OLD TURKIC LETTER YENISEI AR;Lo;0;R;;;;;N;;;;; 10C3C;OLD TURKIC LETTER ORKHON AER;Lo;0;R;;;;;N;;;;; 10C3D;OLD TURKIC LETTER ORKHON AS;Lo;0;R;;;;;N;;;;; 10C3E;OLD TURKIC LETTER ORKHON AES;Lo;0;R;;;;;N;;;;; 10C3F;OLD TURKIC LETTER ORKHON ASH;Lo;0;R;;;;;N;;;;; 10C40;OLD TURKIC LETTER YENISEI ASH;Lo;0;R;;;;;N;;;;; 10C41;OLD TURKIC LETTER ORKHON ESH;Lo;0;R;;;;;N;;;;; 10C42;OLD TURKIC LETTER YENISEI ESH;Lo;0;R;;;;;N;;;;; 10C43;OLD TURKIC LETTER ORKHON AT;Lo;0;R;;;;;N;;;;; 10C44;OLD TURKIC LETTER YENISEI AT;Lo;0;R;;;;;N;;;;; 10C45;OLD TURKIC LETTER ORKHON AET;Lo;0;R;;;;;N;;;;; 10C46;OLD TURKIC LETTER YENISEI AET;Lo;0;R;;;;;N;;;;; 10C47;OLD TURKIC LETTER ORKHON OT;Lo;0;R;;;;;N;;;;; 10C48;OLD TURKIC LETTER ORKHON BASH;Lo;0;R;;;;;N;;;;; 10C80;OLD HUNGARIAN CAPITAL LETTER A;Lu;0;R;;;;;N;;;;10CC0; 10C81;OLD HUNGARIAN CAPITAL LETTER AA;Lu;0;R;;;;;N;;;;10CC1; 10C82;OLD HUNGARIAN CAPITAL LETTER EB;Lu;0;R;;;;;N;;;;10CC2; 10C83;OLD HUNGARIAN CAPITAL LETTER AMB;Lu;0;R;;;;;N;;;;10CC3; 10C84;OLD HUNGARIAN CAPITAL LETTER EC;Lu;0;R;;;;;N;;;;10CC4; 10C85;OLD HUNGARIAN CAPITAL LETTER ENC;Lu;0;R;;;;;N;;;;10CC5; 10C86;OLD HUNGARIAN CAPITAL LETTER ECS;Lu;0;R;;;;;N;;;;10CC6; 10C87;OLD HUNGARIAN CAPITAL LETTER ED;Lu;0;R;;;;;N;;;;10CC7; 10C88;OLD HUNGARIAN CAPITAL LETTER AND;Lu;0;R;;;;;N;;;;10CC8; 10C89;OLD HUNGARIAN CAPITAL LETTER E;Lu;0;R;;;;;N;;;;10CC9; 10C8A;OLD HUNGARIAN CAPITAL LETTER CLOSE E;Lu;0;R;;;;;N;;;;10CCA; 10C8B;OLD HUNGARIAN CAPITAL LETTER EE;Lu;0;R;;;;;N;;;;10CCB; 10C8C;OLD HUNGARIAN CAPITAL LETTER EF;Lu;0;R;;;;;N;;;;10CCC; 10C8D;OLD HUNGARIAN CAPITAL LETTER EG;Lu;0;R;;;;;N;;;;10CCD; 10C8E;OLD HUNGARIAN CAPITAL LETTER EGY;Lu;0;R;;;;;N;;;;10CCE; 10C8F;OLD HUNGARIAN CAPITAL LETTER EH;Lu;0;R;;;;;N;;;;10CCF; 10C90;OLD HUNGARIAN CAPITAL LETTER I;Lu;0;R;;;;;N;;;;10CD0; 10C91;OLD HUNGARIAN CAPITAL LETTER II;Lu;0;R;;;;;N;;;;10CD1; 10C92;OLD HUNGARIAN CAPITAL LETTER EJ;Lu;0;R;;;;;N;;;;10CD2; 10C93;OLD HUNGARIAN CAPITAL LETTER EK;Lu;0;R;;;;;N;;;;10CD3; 10C94;OLD HUNGARIAN CAPITAL LETTER AK;Lu;0;R;;;;;N;;;;10CD4; 10C95;OLD HUNGARIAN CAPITAL LETTER UNK;Lu;0;R;;;;;N;;;;10CD5; 10C96;OLD HUNGARIAN CAPITAL LETTER EL;Lu;0;R;;;;;N;;;;10CD6; 10C97;OLD HUNGARIAN CAPITAL LETTER ELY;Lu;0;R;;;;;N;;;;10CD7; 10C98;OLD HUNGARIAN CAPITAL LETTER EM;Lu;0;R;;;;;N;;;;10CD8; 10C99;OLD HUNGARIAN CAPITAL LETTER EN;Lu;0;R;;;;;N;;;;10CD9; 10C9A;OLD HUNGARIAN CAPITAL LETTER ENY;Lu;0;R;;;;;N;;;;10CDA; 10C9B;OLD HUNGARIAN CAPITAL LETTER O;Lu;0;R;;;;;N;;;;10CDB; 10C9C;OLD HUNGARIAN CAPITAL LETTER OO;Lu;0;R;;;;;N;;;;10CDC; 10C9D;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE;Lu;0;R;;;;;N;;;;10CDD; 10C9E;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE;Lu;0;R;;;;;N;;;;10CDE; 10C9F;OLD HUNGARIAN CAPITAL LETTER OEE;Lu;0;R;;;;;N;;;;10CDF; 10CA0;OLD HUNGARIAN CAPITAL LETTER EP;Lu;0;R;;;;;N;;;;10CE0; 10CA1;OLD HUNGARIAN CAPITAL LETTER EMP;Lu;0;R;;;;;N;;;;10CE1; 10CA2;OLD HUNGARIAN CAPITAL LETTER ER;Lu;0;R;;;;;N;;;;10CE2; 10CA3;OLD HUNGARIAN CAPITAL LETTER SHORT ER;Lu;0;R;;;;;N;;;;10CE3; 10CA4;OLD HUNGARIAN CAPITAL LETTER ES;Lu;0;R;;;;;N;;;;10CE4; 10CA5;OLD HUNGARIAN CAPITAL LETTER ESZ;Lu;0;R;;;;;N;;;;10CE5; 10CA6;OLD HUNGARIAN CAPITAL LETTER ET;Lu;0;R;;;;;N;;;;10CE6; 10CA7;OLD HUNGARIAN CAPITAL LETTER ENT;Lu;0;R;;;;;N;;;;10CE7; 10CA8;OLD HUNGARIAN CAPITAL LETTER ETY;Lu;0;R;;;;;N;;;;10CE8; 10CA9;OLD HUNGARIAN CAPITAL LETTER ECH;Lu;0;R;;;;;N;;;;10CE9; 10CAA;OLD HUNGARIAN CAPITAL LETTER U;Lu;0;R;;;;;N;;;;10CEA; 10CAB;OLD HUNGARIAN CAPITAL LETTER UU;Lu;0;R;;;;;N;;;;10CEB; 10CAC;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE;Lu;0;R;;;;;N;;;;10CEC; 10CAD;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE;Lu;0;R;;;;;N;;;;10CED; 10CAE;OLD HUNGARIAN CAPITAL LETTER EV;Lu;0;R;;;;;N;;;;10CEE; 10CAF;OLD HUNGARIAN CAPITAL LETTER EZ;Lu;0;R;;;;;N;;;;10CEF; 10CB0;OLD HUNGARIAN CAPITAL LETTER EZS;Lu;0;R;;;;;N;;;;10CF0; 10CB1;OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN;Lu;0;R;;;;;N;;;;10CF1; 10CB2;OLD HUNGARIAN CAPITAL LETTER US;Lu;0;R;;;;;N;;;;10CF2; 10CC0;OLD HUNGARIAN SMALL LETTER A;Ll;0;R;;;;;N;;;10C80;;10C80 10CC1;OLD HUNGARIAN SMALL LETTER AA;Ll;0;R;;;;;N;;;10C81;;10C81 10CC2;OLD HUNGARIAN SMALL LETTER EB;Ll;0;R;;;;;N;;;10C82;;10C82 10CC3;OLD HUNGARIAN SMALL LETTER AMB;Ll;0;R;;;;;N;;;10C83;;10C83 10CC4;OLD HUNGARIAN SMALL LETTER EC;Ll;0;R;;;;;N;;;10C84;;10C84 10CC5;OLD HUNGARIAN SMALL LETTER ENC;Ll;0;R;;;;;N;;;10C85;;10C85 10CC6;OLD HUNGARIAN SMALL LETTER ECS;Ll;0;R;;;;;N;;;10C86;;10C86 10CC7;OLD HUNGARIAN SMALL LETTER ED;Ll;0;R;;;;;N;;;10C87;;10C87 10CC8;OLD HUNGARIAN SMALL LETTER AND;Ll;0;R;;;;;N;;;10C88;;10C88 10CC9;OLD HUNGARIAN SMALL LETTER E;Ll;0;R;;;;;N;;;10C89;;10C89 10CCA;OLD HUNGARIAN SMALL LETTER CLOSE E;Ll;0;R;;;;;N;;;10C8A;;10C8A 10CCB;OLD HUNGARIAN SMALL LETTER EE;Ll;0;R;;;;;N;;;10C8B;;10C8B 10CCC;OLD HUNGARIAN SMALL LETTER EF;Ll;0;R;;;;;N;;;10C8C;;10C8C 10CCD;OLD HUNGARIAN SMALL LETTER EG;Ll;0;R;;;;;N;;;10C8D;;10C8D 10CCE;OLD HUNGARIAN SMALL LETTER EGY;Ll;0;R;;;;;N;;;10C8E;;10C8E 10CCF;OLD HUNGARIAN SMALL LETTER EH;Ll;0;R;;;;;N;;;10C8F;;10C8F 10CD0;OLD HUNGARIAN SMALL LETTER I;Ll;0;R;;;;;N;;;10C90;;10C90 10CD1;OLD HUNGARIAN SMALL LETTER II;Ll;0;R;;;;;N;;;10C91;;10C91 10CD2;OLD HUNGARIAN SMALL LETTER EJ;Ll;0;R;;;;;N;;;10C92;;10C92 10CD3;OLD HUNGARIAN SMALL LETTER EK;Ll;0;R;;;;;N;;;10C93;;10C93 10CD4;OLD HUNGARIAN SMALL LETTER AK;Ll;0;R;;;;;N;;;10C94;;10C94 10CD5;OLD HUNGARIAN SMALL LETTER UNK;Ll;0;R;;;;;N;;;10C95;;10C95 10CD6;OLD HUNGARIAN SMALL LETTER EL;Ll;0;R;;;;;N;;;10C96;;10C96 10CD7;OLD HUNGARIAN SMALL LETTER ELY;Ll;0;R;;;;;N;;;10C97;;10C97 10CD8;OLD HUNGARIAN SMALL LETTER EM;Ll;0;R;;;;;N;;;10C98;;10C98 10CD9;OLD HUNGARIAN SMALL LETTER EN;Ll;0;R;;;;;N;;;10C99;;10C99 10CDA;OLD HUNGARIAN SMALL LETTER ENY;Ll;0;R;;;;;N;;;10C9A;;10C9A 10CDB;OLD HUNGARIAN SMALL LETTER O;Ll;0;R;;;;;N;;;10C9B;;10C9B 10CDC;OLD HUNGARIAN SMALL LETTER OO;Ll;0;R;;;;;N;;;10C9C;;10C9C 10CDD;OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE;Ll;0;R;;;;;N;;;10C9D;;10C9D 10CDE;OLD HUNGARIAN SMALL LETTER RUDIMENTA OE;Ll;0;R;;;;;N;;;10C9E;;10C9E 10CDF;OLD HUNGARIAN SMALL LETTER OEE;Ll;0;R;;;;;N;;;10C9F;;10C9F 10CE0;OLD HUNGARIAN SMALL LETTER EP;Ll;0;R;;;;;N;;;10CA0;;10CA0 10CE1;OLD HUNGARIAN SMALL LETTER EMP;Ll;0;R;;;;;N;;;10CA1;;10CA1 10CE2;OLD HUNGARIAN SMALL LETTER ER;Ll;0;R;;;;;N;;;10CA2;;10CA2 10CE3;OLD HUNGARIAN SMALL LETTER SHORT ER;Ll;0;R;;;;;N;;;10CA3;;10CA3 10CE4;OLD HUNGARIAN SMALL LETTER ES;Ll;0;R;;;;;N;;;10CA4;;10CA4 10CE5;OLD HUNGARIAN SMALL LETTER ESZ;Ll;0;R;;;;;N;;;10CA5;;10CA5 10CE6;OLD HUNGARIAN SMALL LETTER ET;Ll;0;R;;;;;N;;;10CA6;;10CA6 10CE7;OLD HUNGARIAN SMALL LETTER ENT;Ll;0;R;;;;;N;;;10CA7;;10CA7 10CE8;OLD HUNGARIAN SMALL LETTER ETY;Ll;0;R;;;;;N;;;10CA8;;10CA8 10CE9;OLD HUNGARIAN SMALL LETTER ECH;Ll;0;R;;;;;N;;;10CA9;;10CA9 10CEA;OLD HUNGARIAN SMALL LETTER U;Ll;0;R;;;;;N;;;10CAA;;10CAA 10CEB;OLD HUNGARIAN SMALL LETTER UU;Ll;0;R;;;;;N;;;10CAB;;10CAB 10CEC;OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE;Ll;0;R;;;;;N;;;10CAC;;10CAC 10CED;OLD HUNGARIAN SMALL LETTER RUDIMENTA UE;Ll;0;R;;;;;N;;;10CAD;;10CAD 10CEE;OLD HUNGARIAN SMALL LETTER EV;Ll;0;R;;;;;N;;;10CAE;;10CAE 10CEF;OLD HUNGARIAN SMALL LETTER EZ;Ll;0;R;;;;;N;;;10CAF;;10CAF 10CF0;OLD HUNGARIAN SMALL LETTER EZS;Ll;0;R;;;;;N;;;10CB0;;10CB0 10CF1;OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN;Ll;0;R;;;;;N;;;10CB1;;10CB1 10CF2;OLD HUNGARIAN SMALL LETTER US;Ll;0;R;;;;;N;;;10CB2;;10CB2 10CFA;OLD HUNGARIAN NUMBER ONE;No;0;R;;;;1;N;;;;; 10CFB;OLD HUNGARIAN NUMBER FIVE;No;0;R;;;;5;N;;;;; 10CFC;OLD HUNGARIAN NUMBER TEN;No;0;R;;;;10;N;;;;; 10CFD;OLD HUNGARIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;; 10CFE;OLD HUNGARIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; 10CFF;OLD HUNGARIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; 10E60;RUMI DIGIT ONE;No;0;AN;;;1;1;N;;;;; 10E61;RUMI DIGIT TWO;No;0;AN;;;2;2;N;;;;; 10E62;RUMI DIGIT THREE;No;0;AN;;;3;3;N;;;;; 10E63;RUMI DIGIT FOUR;No;0;AN;;;4;4;N;;;;; 10E64;RUMI DIGIT FIVE;No;0;AN;;;5;5;N;;;;; 10E65;RUMI DIGIT SIX;No;0;AN;;;6;6;N;;;;; 10E66;RUMI DIGIT SEVEN;No;0;AN;;;7;7;N;;;;; 10E67;RUMI DIGIT EIGHT;No;0;AN;;;8;8;N;;;;; 10E68;RUMI DIGIT NINE;No;0;AN;;;9;9;N;;;;; 10E69;RUMI NUMBER TEN;No;0;AN;;;;10;N;;;;; 10E6A;RUMI NUMBER TWENTY;No;0;AN;;;;20;N;;;;; 10E6B;RUMI NUMBER THIRTY;No;0;AN;;;;30;N;;;;; 10E6C;RUMI NUMBER FORTY;No;0;AN;;;;40;N;;;;; 10E6D;RUMI NUMBER FIFTY;No;0;AN;;;;50;N;;;;; 10E6E;RUMI NUMBER SIXTY;No;0;AN;;;;60;N;;;;; 10E6F;RUMI NUMBER SEVENTY;No;0;AN;;;;70;N;;;;; 10E70;RUMI NUMBER EIGHTY;No;0;AN;;;;80;N;;;;; 10E71;RUMI NUMBER NINETY;No;0;AN;;;;90;N;;;;; 10E72;RUMI NUMBER ONE HUNDRED;No;0;AN;;;;100;N;;;;; 10E73;RUMI NUMBER TWO HUNDRED;No;0;AN;;;;200;N;;;;; 10E74;RUMI NUMBER THREE HUNDRED;No;0;AN;;;;300;N;;;;; 10E75;RUMI NUMBER FOUR HUNDRED;No;0;AN;;;;400;N;;;;; 10E76;RUMI NUMBER FIVE HUNDRED;No;0;AN;;;;500;N;;;;; 10E77;RUMI NUMBER SIX HUNDRED;No;0;AN;;;;600;N;;;;; 10E78;RUMI NUMBER SEVEN HUNDRED;No;0;AN;;;;700;N;;;;; 10E79;RUMI NUMBER EIGHT HUNDRED;No;0;AN;;;;800;N;;;;; 10E7A;RUMI NUMBER NINE HUNDRED;No;0;AN;;;;900;N;;;;; 10E7B;RUMI FRACTION ONE HALF;No;0;AN;;;;1/2;N;;;;; 10E7C;RUMI FRACTION ONE QUARTER;No;0;AN;;;;1/4;N;;;;; 10E7D;RUMI FRACTION ONE THIRD;No;0;AN;;;;1/3;N;;;;; 10E7E;RUMI FRACTION TWO THIRDS;No;0;AN;;;;2/3;N;;;;; 11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;; 11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11003;BRAHMI SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; 11004;BRAHMI SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; 11005;BRAHMI LETTER A;Lo;0;L;;;;;N;;;;; 11006;BRAHMI LETTER AA;Lo;0;L;;;;;N;;;;; 11007;BRAHMI LETTER I;Lo;0;L;;;;;N;;;;; 11008;BRAHMI LETTER II;Lo;0;L;;;;;N;;;;; 11009;BRAHMI LETTER U;Lo;0;L;;;;;N;;;;; 1100A;BRAHMI LETTER UU;Lo;0;L;;;;;N;;;;; 1100B;BRAHMI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 1100C;BRAHMI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 1100D;BRAHMI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 1100E;BRAHMI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1100F;BRAHMI LETTER E;Lo;0;L;;;;;N;;;;; 11010;BRAHMI LETTER AI;Lo;0;L;;;;;N;;;;; 11011;BRAHMI LETTER O;Lo;0;L;;;;;N;;;;; 11012;BRAHMI LETTER AU;Lo;0;L;;;;;N;;;;; 11013;BRAHMI LETTER KA;Lo;0;L;;;;;N;;;;; 11014;BRAHMI LETTER KHA;Lo;0;L;;;;;N;;;;; 11015;BRAHMI LETTER GA;Lo;0;L;;;;;N;;;;; 11016;BRAHMI LETTER GHA;Lo;0;L;;;;;N;;;;; 11017;BRAHMI LETTER NGA;Lo;0;L;;;;;N;;;;; 11018;BRAHMI LETTER CA;Lo;0;L;;;;;N;;;;; 11019;BRAHMI LETTER CHA;Lo;0;L;;;;;N;;;;; 1101A;BRAHMI LETTER JA;Lo;0;L;;;;;N;;;;; 1101B;BRAHMI LETTER JHA;Lo;0;L;;;;;N;;;;; 1101C;BRAHMI LETTER NYA;Lo;0;L;;;;;N;;;;; 1101D;BRAHMI LETTER TTA;Lo;0;L;;;;;N;;;;; 1101E;BRAHMI LETTER TTHA;Lo;0;L;;;;;N;;;;; 1101F;BRAHMI LETTER DDA;Lo;0;L;;;;;N;;;;; 11020;BRAHMI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11021;BRAHMI LETTER NNA;Lo;0;L;;;;;N;;;;; 11022;BRAHMI LETTER TA;Lo;0;L;;;;;N;;;;; 11023;BRAHMI LETTER THA;Lo;0;L;;;;;N;;;;; 11024;BRAHMI LETTER DA;Lo;0;L;;;;;N;;;;; 11025;BRAHMI LETTER DHA;Lo;0;L;;;;;N;;;;; 11026;BRAHMI LETTER NA;Lo;0;L;;;;;N;;;;; 11027;BRAHMI LETTER PA;Lo;0;L;;;;;N;;;;; 11028;BRAHMI LETTER PHA;Lo;0;L;;;;;N;;;;; 11029;BRAHMI LETTER BA;Lo;0;L;;;;;N;;;;; 1102A;BRAHMI LETTER BHA;Lo;0;L;;;;;N;;;;; 1102B;BRAHMI LETTER MA;Lo;0;L;;;;;N;;;;; 1102C;BRAHMI LETTER YA;Lo;0;L;;;;;N;;;;; 1102D;BRAHMI LETTER RA;Lo;0;L;;;;;N;;;;; 1102E;BRAHMI LETTER LA;Lo;0;L;;;;;N;;;;; 1102F;BRAHMI LETTER VA;Lo;0;L;;;;;N;;;;; 11030;BRAHMI LETTER SHA;Lo;0;L;;;;;N;;;;; 11031;BRAHMI LETTER SSA;Lo;0;L;;;;;N;;;;; 11032;BRAHMI LETTER SA;Lo;0;L;;;;;N;;;;; 11033;BRAHMI LETTER HA;Lo;0;L;;;;;N;;;;; 11034;BRAHMI LETTER LLA;Lo;0;L;;;;;N;;;;; 11035;BRAHMI LETTER OLD TAMIL LLLA;Lo;0;L;;;;;N;;;;; 11036;BRAHMI LETTER OLD TAMIL RRA;Lo;0;L;;;;;N;;;;; 11037;BRAHMI LETTER OLD TAMIL NNNA;Lo;0;L;;;;;N;;;;; 11038;BRAHMI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; 11039;BRAHMI VOWEL SIGN BHATTIPROLU AA;Mn;0;NSM;;;;;N;;;;; 1103A;BRAHMI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 1103B;BRAHMI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 1103C;BRAHMI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1103D;BRAHMI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 1103E;BRAHMI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 1103F;BRAHMI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 11040;BRAHMI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 11041;BRAHMI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 11042;BRAHMI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 11043;BRAHMI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 11044;BRAHMI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 11045;BRAHMI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 11046;BRAHMI VIRAMA;Mn;9;NSM;;;;;N;;;;; 11047;BRAHMI DANDA;Po;0;L;;;;;N;;;;; 11048;BRAHMI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 11049;BRAHMI PUNCTUATION DOT;Po;0;L;;;;;N;;;;; 1104A;BRAHMI PUNCTUATION DOUBLE DOT;Po;0;L;;;;;N;;;;; 1104B;BRAHMI PUNCTUATION LINE;Po;0;L;;;;;N;;;;; 1104C;BRAHMI PUNCTUATION CRESCENT BAR;Po;0;L;;;;;N;;;;; 1104D;BRAHMI PUNCTUATION LOTUS;Po;0;L;;;;;N;;;;; 11052;BRAHMI NUMBER ONE;No;0;ON;;;1;1;N;;;;; 11053;BRAHMI NUMBER TWO;No;0;ON;;;2;2;N;;;;; 11054;BRAHMI NUMBER THREE;No;0;ON;;;3;3;N;;;;; 11055;BRAHMI NUMBER FOUR;No;0;ON;;;4;4;N;;;;; 11056;BRAHMI NUMBER FIVE;No;0;ON;;;5;5;N;;;;; 11057;BRAHMI NUMBER SIX;No;0;ON;;;6;6;N;;;;; 11058;BRAHMI NUMBER SEVEN;No;0;ON;;;7;7;N;;;;; 11059;BRAHMI NUMBER EIGHT;No;0;ON;;;8;8;N;;;;; 1105A;BRAHMI NUMBER NINE;No;0;ON;;;9;9;N;;;;; 1105B;BRAHMI NUMBER TEN;No;0;ON;;;;10;N;;;;; 1105C;BRAHMI NUMBER TWENTY;No;0;ON;;;;20;N;;;;; 1105D;BRAHMI NUMBER THIRTY;No;0;ON;;;;30;N;;;;; 1105E;BRAHMI NUMBER FORTY;No;0;ON;;;;40;N;;;;; 1105F;BRAHMI NUMBER FIFTY;No;0;ON;;;;50;N;;;;; 11060;BRAHMI NUMBER SIXTY;No;0;ON;;;;60;N;;;;; 11061;BRAHMI NUMBER SEVENTY;No;0;ON;;;;70;N;;;;; 11062;BRAHMI NUMBER EIGHTY;No;0;ON;;;;80;N;;;;; 11063;BRAHMI NUMBER NINETY;No;0;ON;;;;90;N;;;;; 11064;BRAHMI NUMBER ONE HUNDRED;No;0;ON;;;;100;N;;;;; 11065;BRAHMI NUMBER ONE THOUSAND;No;0;ON;;;;1000;N;;;;; 11066;BRAHMI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11067;BRAHMI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11068;BRAHMI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11069;BRAHMI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1106A;BRAHMI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1106B;BRAHMI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1106C;BRAHMI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1106D;BRAHMI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1106E;BRAHMI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1106F;BRAHMI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1107F;BRAHMI NUMBER JOINER;Mn;9;NSM;;;;;N;;;;; 11080;KAITHI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 11081;KAITHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11082;KAITHI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11083;KAITHI LETTER A;Lo;0;L;;;;;N;;;;; 11084;KAITHI LETTER AA;Lo;0;L;;;;;N;;;;; 11085;KAITHI LETTER I;Lo;0;L;;;;;N;;;;; 11086;KAITHI LETTER II;Lo;0;L;;;;;N;;;;; 11087;KAITHI LETTER U;Lo;0;L;;;;;N;;;;; 11088;KAITHI LETTER UU;Lo;0;L;;;;;N;;;;; 11089;KAITHI LETTER E;Lo;0;L;;;;;N;;;;; 1108A;KAITHI LETTER AI;Lo;0;L;;;;;N;;;;; 1108B;KAITHI LETTER O;Lo;0;L;;;;;N;;;;; 1108C;KAITHI LETTER AU;Lo;0;L;;;;;N;;;;; 1108D;KAITHI LETTER KA;Lo;0;L;;;;;N;;;;; 1108E;KAITHI LETTER KHA;Lo;0;L;;;;;N;;;;; 1108F;KAITHI LETTER GA;Lo;0;L;;;;;N;;;;; 11090;KAITHI LETTER GHA;Lo;0;L;;;;;N;;;;; 11091;KAITHI LETTER NGA;Lo;0;L;;;;;N;;;;; 11092;KAITHI LETTER CA;Lo;0;L;;;;;N;;;;; 11093;KAITHI LETTER CHA;Lo;0;L;;;;;N;;;;; 11094;KAITHI LETTER JA;Lo;0;L;;;;;N;;;;; 11095;KAITHI LETTER JHA;Lo;0;L;;;;;N;;;;; 11096;KAITHI LETTER NYA;Lo;0;L;;;;;N;;;;; 11097;KAITHI LETTER TTA;Lo;0;L;;;;;N;;;;; 11098;KAITHI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11099;KAITHI LETTER DDA;Lo;0;L;;;;;N;;;;; 1109A;KAITHI LETTER DDDHA;Lo;0;L;11099 110BA;;;;N;;;;; 1109B;KAITHI LETTER DDHA;Lo;0;L;;;;;N;;;;; 1109C;KAITHI LETTER RHA;Lo;0;L;1109B 110BA;;;;N;;;;; 1109D;KAITHI LETTER NNA;Lo;0;L;;;;;N;;;;; 1109E;KAITHI LETTER TA;Lo;0;L;;;;;N;;;;; 1109F;KAITHI LETTER THA;Lo;0;L;;;;;N;;;;; 110A0;KAITHI LETTER DA;Lo;0;L;;;;;N;;;;; 110A1;KAITHI LETTER DHA;Lo;0;L;;;;;N;;;;; 110A2;KAITHI LETTER NA;Lo;0;L;;;;;N;;;;; 110A3;KAITHI LETTER PA;Lo;0;L;;;;;N;;;;; 110A4;KAITHI LETTER PHA;Lo;0;L;;;;;N;;;;; 110A5;KAITHI LETTER BA;Lo;0;L;;;;;N;;;;; 110A6;KAITHI LETTER BHA;Lo;0;L;;;;;N;;;;; 110A7;KAITHI LETTER MA;Lo;0;L;;;;;N;;;;; 110A8;KAITHI LETTER YA;Lo;0;L;;;;;N;;;;; 110A9;KAITHI LETTER RA;Lo;0;L;;;;;N;;;;; 110AA;KAITHI LETTER LA;Lo;0;L;;;;;N;;;;; 110AB;KAITHI LETTER VA;Lo;0;L;110A5 110BA;;;;N;;;;; 110AC;KAITHI LETTER SHA;Lo;0;L;;;;;N;;;;; 110AD;KAITHI LETTER SSA;Lo;0;L;;;;;N;;;;; 110AE;KAITHI LETTER SA;Lo;0;L;;;;;N;;;;; 110AF;KAITHI LETTER HA;Lo;0;L;;;;;N;;;;; 110B0;KAITHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 110B1;KAITHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 110B2;KAITHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 110B3;KAITHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 110B4;KAITHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 110B5;KAITHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 110B6;KAITHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 110B7;KAITHI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 110B8;KAITHI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 110B9;KAITHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 110BA;KAITHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 110BB;KAITHI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 110BC;KAITHI ENUMERATION SIGN;Po;0;L;;;;;N;;;;; 110BD;KAITHI NUMBER SIGN;Cf;0;L;;;;;N;;;;; 110BE;KAITHI SECTION MARK;Po;0;L;;;;;N;;;;; 110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;; 110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;; 110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;; 110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;; 110D2;SORA SOMPENG LETTER BAH;Lo;0;L;;;;;N;;;;; 110D3;SORA SOMPENG LETTER CAH;Lo;0;L;;;;;N;;;;; 110D4;SORA SOMPENG LETTER DAH;Lo;0;L;;;;;N;;;;; 110D5;SORA SOMPENG LETTER GAH;Lo;0;L;;;;;N;;;;; 110D6;SORA SOMPENG LETTER MAH;Lo;0;L;;;;;N;;;;; 110D7;SORA SOMPENG LETTER NGAH;Lo;0;L;;;;;N;;;;; 110D8;SORA SOMPENG LETTER LAH;Lo;0;L;;;;;N;;;;; 110D9;SORA SOMPENG LETTER NAH;Lo;0;L;;;;;N;;;;; 110DA;SORA SOMPENG LETTER VAH;Lo;0;L;;;;;N;;;;; 110DB;SORA SOMPENG LETTER PAH;Lo;0;L;;;;;N;;;;; 110DC;SORA SOMPENG LETTER YAH;Lo;0;L;;;;;N;;;;; 110DD;SORA SOMPENG LETTER RAH;Lo;0;L;;;;;N;;;;; 110DE;SORA SOMPENG LETTER HAH;Lo;0;L;;;;;N;;;;; 110DF;SORA SOMPENG LETTER KAH;Lo;0;L;;;;;N;;;;; 110E0;SORA SOMPENG LETTER JAH;Lo;0;L;;;;;N;;;;; 110E1;SORA SOMPENG LETTER NYAH;Lo;0;L;;;;;N;;;;; 110E2;SORA SOMPENG LETTER AH;Lo;0;L;;;;;N;;;;; 110E3;SORA SOMPENG LETTER EEH;Lo;0;L;;;;;N;;;;; 110E4;SORA SOMPENG LETTER IH;Lo;0;L;;;;;N;;;;; 110E5;SORA SOMPENG LETTER UH;Lo;0;L;;;;;N;;;;; 110E6;SORA SOMPENG LETTER OH;Lo;0;L;;;;;N;;;;; 110E7;SORA SOMPENG LETTER EH;Lo;0;L;;;;;N;;;;; 110E8;SORA SOMPENG LETTER MAE;Lo;0;L;;;;;N;;;;; 110F0;SORA SOMPENG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 110F1;SORA SOMPENG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 110F2;SORA SOMPENG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 110F3;SORA SOMPENG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 110F4;SORA SOMPENG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 110F5;SORA SOMPENG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 110F6;SORA SOMPENG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 110F7;SORA SOMPENG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 110F8;SORA SOMPENG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 110F9;SORA SOMPENG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11100;CHAKMA SIGN CANDRABINDU;Mn;230;NSM;;;;;N;;;;; 11101;CHAKMA SIGN ANUSVARA;Mn;230;NSM;;;;;N;;;;; 11102;CHAKMA SIGN VISARGA;Mn;230;NSM;;;;;N;;;;; 11103;CHAKMA LETTER AA;Lo;0;L;;;;;N;;;;; 11104;CHAKMA LETTER I;Lo;0;L;;;;;N;;;;; 11105;CHAKMA LETTER U;Lo;0;L;;;;;N;;;;; 11106;CHAKMA LETTER E;Lo;0;L;;;;;N;;;;; 11107;CHAKMA LETTER KAA;Lo;0;L;;;;;N;;;;; 11108;CHAKMA LETTER KHAA;Lo;0;L;;;;;N;;;;; 11109;CHAKMA LETTER GAA;Lo;0;L;;;;;N;;;;; 1110A;CHAKMA LETTER GHAA;Lo;0;L;;;;;N;;;;; 1110B;CHAKMA LETTER NGAA;Lo;0;L;;;;;N;;;;; 1110C;CHAKMA LETTER CAA;Lo;0;L;;;;;N;;;;; 1110D;CHAKMA LETTER CHAA;Lo;0;L;;;;;N;;;;; 1110E;CHAKMA LETTER JAA;Lo;0;L;;;;;N;;;;; 1110F;CHAKMA LETTER JHAA;Lo;0;L;;;;;N;;;;; 11110;CHAKMA LETTER NYAA;Lo;0;L;;;;;N;;;;; 11111;CHAKMA LETTER TTAA;Lo;0;L;;;;;N;;;;; 11112;CHAKMA LETTER TTHAA;Lo;0;L;;;;;N;;;;; 11113;CHAKMA LETTER DDAA;Lo;0;L;;;;;N;;;;; 11114;CHAKMA LETTER DDHAA;Lo;0;L;;;;;N;;;;; 11115;CHAKMA LETTER NNAA;Lo;0;L;;;;;N;;;;; 11116;CHAKMA LETTER TAA;Lo;0;L;;;;;N;;;;; 11117;CHAKMA LETTER THAA;Lo;0;L;;;;;N;;;;; 11118;CHAKMA LETTER DAA;Lo;0;L;;;;;N;;;;; 11119;CHAKMA LETTER DHAA;Lo;0;L;;;;;N;;;;; 1111A;CHAKMA LETTER NAA;Lo;0;L;;;;;N;;;;; 1111B;CHAKMA LETTER PAA;Lo;0;L;;;;;N;;;;; 1111C;CHAKMA LETTER PHAA;Lo;0;L;;;;;N;;;;; 1111D;CHAKMA LETTER BAA;Lo;0;L;;;;;N;;;;; 1111E;CHAKMA LETTER BHAA;Lo;0;L;;;;;N;;;;; 1111F;CHAKMA LETTER MAA;Lo;0;L;;;;;N;;;;; 11120;CHAKMA LETTER YYAA;Lo;0;L;;;;;N;;;;; 11121;CHAKMA LETTER YAA;Lo;0;L;;;;;N;;;;; 11122;CHAKMA LETTER RAA;Lo;0;L;;;;;N;;;;; 11123;CHAKMA LETTER LAA;Lo;0;L;;;;;N;;;;; 11124;CHAKMA LETTER WAA;Lo;0;L;;;;;N;;;;; 11125;CHAKMA LETTER SAA;Lo;0;L;;;;;N;;;;; 11126;CHAKMA LETTER HAA;Lo;0;L;;;;;N;;;;; 11127;CHAKMA VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;; 11128;CHAKMA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 11129;CHAKMA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 1112A;CHAKMA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 1112B;CHAKMA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 1112C;CHAKMA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 1112D;CHAKMA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 1112E;CHAKMA VOWEL SIGN O;Mn;0;NSM;11131 11127;;;;N;;;;; 1112F;CHAKMA VOWEL SIGN AU;Mn;0;NSM;11132 11127;;;;N;;;;; 11130;CHAKMA VOWEL SIGN OI;Mn;0;NSM;;;;;N;;;;; 11131;CHAKMA O MARK;Mn;0;NSM;;;;;N;;;;; 11132;CHAKMA AU MARK;Mn;0;NSM;;;;;N;;;;; 11133;CHAKMA VIRAMA;Mn;9;NSM;;;;;N;;;;; 11134;CHAKMA MAAYYAA;Mn;9;NSM;;;;;N;;;;; 11136;CHAKMA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11137;CHAKMA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11138;CHAKMA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11139;CHAKMA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 1113A;CHAKMA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 1113B;CHAKMA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 1113C;CHAKMA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 1113D;CHAKMA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1113E;CHAKMA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1113F;CHAKMA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11140;CHAKMA SECTION MARK;Po;0;L;;;;;N;;;;; 11141;CHAKMA DANDA;Po;0;L;;;;;N;;;;; 11142;CHAKMA DOUBLE DANDA;Po;0;L;;;;;N;;;;; 11143;CHAKMA QUESTION MARK;Po;0;L;;;;;N;;;;; 11150;MAHAJANI LETTER A;Lo;0;L;;;;;N;;;;; 11151;MAHAJANI LETTER I;Lo;0;L;;;;;N;;;;; 11152;MAHAJANI LETTER U;Lo;0;L;;;;;N;;;;; 11153;MAHAJANI LETTER E;Lo;0;L;;;;;N;;;;; 11154;MAHAJANI LETTER O;Lo;0;L;;;;;N;;;;; 11155;MAHAJANI LETTER KA;Lo;0;L;;;;;N;;;;; 11156;MAHAJANI LETTER KHA;Lo;0;L;;;;;N;;;;; 11157;MAHAJANI LETTER GA;Lo;0;L;;;;;N;;;;; 11158;MAHAJANI LETTER GHA;Lo;0;L;;;;;N;;;;; 11159;MAHAJANI LETTER CA;Lo;0;L;;;;;N;;;;; 1115A;MAHAJANI LETTER CHA;Lo;0;L;;;;;N;;;;; 1115B;MAHAJANI LETTER JA;Lo;0;L;;;;;N;;;;; 1115C;MAHAJANI LETTER JHA;Lo;0;L;;;;;N;;;;; 1115D;MAHAJANI LETTER NYA;Lo;0;L;;;;;N;;;;; 1115E;MAHAJANI LETTER TTA;Lo;0;L;;;;;N;;;;; 1115F;MAHAJANI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11160;MAHAJANI LETTER DDA;Lo;0;L;;;;;N;;;;; 11161;MAHAJANI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11162;MAHAJANI LETTER NNA;Lo;0;L;;;;;N;;;;; 11163;MAHAJANI LETTER TA;Lo;0;L;;;;;N;;;;; 11164;MAHAJANI LETTER THA;Lo;0;L;;;;;N;;;;; 11165;MAHAJANI LETTER DA;Lo;0;L;;;;;N;;;;; 11166;MAHAJANI LETTER DHA;Lo;0;L;;;;;N;;;;; 11167;MAHAJANI LETTER NA;Lo;0;L;;;;;N;;;;; 11168;MAHAJANI LETTER PA;Lo;0;L;;;;;N;;;;; 11169;MAHAJANI LETTER PHA;Lo;0;L;;;;;N;;;;; 1116A;MAHAJANI LETTER BA;Lo;0;L;;;;;N;;;;; 1116B;MAHAJANI LETTER BHA;Lo;0;L;;;;;N;;;;; 1116C;MAHAJANI LETTER MA;Lo;0;L;;;;;N;;;;; 1116D;MAHAJANI LETTER RA;Lo;0;L;;;;;N;;;;; 1116E;MAHAJANI LETTER LA;Lo;0;L;;;;;N;;;;; 1116F;MAHAJANI LETTER VA;Lo;0;L;;;;;N;;;;; 11170;MAHAJANI LETTER SA;Lo;0;L;;;;;N;;;;; 11171;MAHAJANI LETTER HA;Lo;0;L;;;;;N;;;;; 11172;MAHAJANI LETTER RRA;Lo;0;L;;;;;N;;;;; 11173;MAHAJANI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 11174;MAHAJANI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 11175;MAHAJANI SECTION MARK;Po;0;L;;;;;N;;;;; 11176;MAHAJANI LIGATURE SHRI;Lo;0;L;;;;;N;;;;; 11180;SHARADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 11181;SHARADA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11182;SHARADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11183;SHARADA LETTER A;Lo;0;L;;;;;N;;;;; 11184;SHARADA LETTER AA;Lo;0;L;;;;;N;;;;; 11185;SHARADA LETTER I;Lo;0;L;;;;;N;;;;; 11186;SHARADA LETTER II;Lo;0;L;;;;;N;;;;; 11187;SHARADA LETTER U;Lo;0;L;;;;;N;;;;; 11188;SHARADA LETTER UU;Lo;0;L;;;;;N;;;;; 11189;SHARADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 1118A;SHARADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 1118B;SHARADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 1118C;SHARADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1118D;SHARADA LETTER E;Lo;0;L;;;;;N;;;;; 1118E;SHARADA LETTER AI;Lo;0;L;;;;;N;;;;; 1118F;SHARADA LETTER O;Lo;0;L;;;;;N;;;;; 11190;SHARADA LETTER AU;Lo;0;L;;;;;N;;;;; 11191;SHARADA LETTER KA;Lo;0;L;;;;;N;;;;; 11192;SHARADA LETTER KHA;Lo;0;L;;;;;N;;;;; 11193;SHARADA LETTER GA;Lo;0;L;;;;;N;;;;; 11194;SHARADA LETTER GHA;Lo;0;L;;;;;N;;;;; 11195;SHARADA LETTER NGA;Lo;0;L;;;;;N;;;;; 11196;SHARADA LETTER CA;Lo;0;L;;;;;N;;;;; 11197;SHARADA LETTER CHA;Lo;0;L;;;;;N;;;;; 11198;SHARADA LETTER JA;Lo;0;L;;;;;N;;;;; 11199;SHARADA LETTER JHA;Lo;0;L;;;;;N;;;;; 1119A;SHARADA LETTER NYA;Lo;0;L;;;;;N;;;;; 1119B;SHARADA LETTER TTA;Lo;0;L;;;;;N;;;;; 1119C;SHARADA LETTER TTHA;Lo;0;L;;;;;N;;;;; 1119D;SHARADA LETTER DDA;Lo;0;L;;;;;N;;;;; 1119E;SHARADA LETTER DDHA;Lo;0;L;;;;;N;;;;; 1119F;SHARADA LETTER NNA;Lo;0;L;;;;;N;;;;; 111A0;SHARADA LETTER TA;Lo;0;L;;;;;N;;;;; 111A1;SHARADA LETTER THA;Lo;0;L;;;;;N;;;;; 111A2;SHARADA LETTER DA;Lo;0;L;;;;;N;;;;; 111A3;SHARADA LETTER DHA;Lo;0;L;;;;;N;;;;; 111A4;SHARADA LETTER NA;Lo;0;L;;;;;N;;;;; 111A5;SHARADA LETTER PA;Lo;0;L;;;;;N;;;;; 111A6;SHARADA LETTER PHA;Lo;0;L;;;;;N;;;;; 111A7;SHARADA LETTER BA;Lo;0;L;;;;;N;;;;; 111A8;SHARADA LETTER BHA;Lo;0;L;;;;;N;;;;; 111A9;SHARADA LETTER MA;Lo;0;L;;;;;N;;;;; 111AA;SHARADA LETTER YA;Lo;0;L;;;;;N;;;;; 111AB;SHARADA LETTER RA;Lo;0;L;;;;;N;;;;; 111AC;SHARADA LETTER LA;Lo;0;L;;;;;N;;;;; 111AD;SHARADA LETTER LLA;Lo;0;L;;;;;N;;;;; 111AE;SHARADA LETTER VA;Lo;0;L;;;;;N;;;;; 111AF;SHARADA LETTER SHA;Lo;0;L;;;;;N;;;;; 111B0;SHARADA LETTER SSA;Lo;0;L;;;;;N;;;;; 111B1;SHARADA LETTER SA;Lo;0;L;;;;;N;;;;; 111B2;SHARADA LETTER HA;Lo;0;L;;;;;N;;;;; 111B3;SHARADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 111B4;SHARADA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 111B5;SHARADA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 111B6;SHARADA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 111B7;SHARADA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 111B8;SHARADA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 111B9;SHARADA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 111BA;SHARADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 111BB;SHARADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 111BC;SHARADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 111BD;SHARADA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 111BE;SHARADA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 111BF;SHARADA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 111C0;SHARADA SIGN VIRAMA;Mc;9;L;;;;;N;;;;; 111C1;SHARADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 111C2;SHARADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; 111C3;SHARADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; 111C4;SHARADA OM;Lo;0;L;;;;;N;;;;; 111C5;SHARADA DANDA;Po;0;L;;;;;N;;;;; 111C6;SHARADA DOUBLE DANDA;Po;0;L;;;;;N;;;;; 111C7;SHARADA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 111C8;SHARADA SEPARATOR;Po;0;L;;;;;N;;;;; 111C9;SHARADA SANDHI MARK;Po;0;L;;;;;N;;;;; 111CA;SHARADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 111CB;SHARADA VOWEL MODIFIER MARK;Mn;0;NSM;;;;;N;;;;; 111CC;SHARADA EXTRA SHORT VOWEL MARK;Mn;0;NSM;;;;;N;;;;; 111CD;SHARADA SUTRA MARK;Po;0;L;;;;;N;;;;; 111D0;SHARADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 111D1;SHARADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 111D2;SHARADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 111D3;SHARADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 111D4;SHARADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 111D5;SHARADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 111D6;SHARADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 111D7;SHARADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 111D8;SHARADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 111D9;SHARADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 111DA;SHARADA EKAM;Lo;0;L;;;;;N;;;;; 111DB;SHARADA SIGN SIDDHAM;Po;0;L;;;;;N;;;;; 111DC;SHARADA HEADSTROKE;Lo;0;L;;;;;N;;;;; 111DD;SHARADA CONTINUATION SIGN;Po;0;L;;;;;N;;;;; 111DE;SHARADA SECTION MARK-1;Po;0;L;;;;;N;;;;; 111DF;SHARADA SECTION MARK-2;Po;0;L;;;;;N;;;;; 111E1;SINHALA ARCHAIC DIGIT ONE;No;0;L;;;;1;N;;;;; 111E2;SINHALA ARCHAIC DIGIT TWO;No;0;L;;;;2;N;;;;; 111E3;SINHALA ARCHAIC DIGIT THREE;No;0;L;;;;3;N;;;;; 111E4;SINHALA ARCHAIC DIGIT FOUR;No;0;L;;;;4;N;;;;; 111E5;SINHALA ARCHAIC DIGIT FIVE;No;0;L;;;;5;N;;;;; 111E6;SINHALA ARCHAIC DIGIT SIX;No;0;L;;;;6;N;;;;; 111E7;SINHALA ARCHAIC DIGIT SEVEN;No;0;L;;;;7;N;;;;; 111E8;SINHALA ARCHAIC DIGIT EIGHT;No;0;L;;;;8;N;;;;; 111E9;SINHALA ARCHAIC DIGIT NINE;No;0;L;;;;9;N;;;;; 111EA;SINHALA ARCHAIC NUMBER TEN;No;0;L;;;;10;N;;;;; 111EB;SINHALA ARCHAIC NUMBER TWENTY;No;0;L;;;;20;N;;;;; 111EC;SINHALA ARCHAIC NUMBER THIRTY;No;0;L;;;;30;N;;;;; 111ED;SINHALA ARCHAIC NUMBER FORTY;No;0;L;;;;40;N;;;;; 111EE;SINHALA ARCHAIC NUMBER FIFTY;No;0;L;;;;50;N;;;;; 111EF;SINHALA ARCHAIC NUMBER SIXTY;No;0;L;;;;60;N;;;;; 111F0;SINHALA ARCHAIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;; 111F1;SINHALA ARCHAIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;; 111F2;SINHALA ARCHAIC NUMBER NINETY;No;0;L;;;;90;N;;;;; 111F3;SINHALA ARCHAIC NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; 111F4;SINHALA ARCHAIC NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; 11200;KHOJKI LETTER A;Lo;0;L;;;;;N;;;;; 11201;KHOJKI LETTER AA;Lo;0;L;;;;;N;;;;; 11202;KHOJKI LETTER I;Lo;0;L;;;;;N;;;;; 11203;KHOJKI LETTER U;Lo;0;L;;;;;N;;;;; 11204;KHOJKI LETTER E;Lo;0;L;;;;;N;;;;; 11205;KHOJKI LETTER AI;Lo;0;L;;;;;N;;;;; 11206;KHOJKI LETTER O;Lo;0;L;;;;;N;;;;; 11207;KHOJKI LETTER AU;Lo;0;L;;;;;N;;;;; 11208;KHOJKI LETTER KA;Lo;0;L;;;;;N;;;;; 11209;KHOJKI LETTER KHA;Lo;0;L;;;;;N;;;;; 1120A;KHOJKI LETTER GA;Lo;0;L;;;;;N;;;;; 1120B;KHOJKI LETTER GGA;Lo;0;L;;;;;N;;;;; 1120C;KHOJKI LETTER GHA;Lo;0;L;;;;;N;;;;; 1120D;KHOJKI LETTER NGA;Lo;0;L;;;;;N;;;;; 1120E;KHOJKI LETTER CA;Lo;0;L;;;;;N;;;;; 1120F;KHOJKI LETTER CHA;Lo;0;L;;;;;N;;;;; 11210;KHOJKI LETTER JA;Lo;0;L;;;;;N;;;;; 11211;KHOJKI LETTER JJA;Lo;0;L;;;;;N;;;;; 11213;KHOJKI LETTER NYA;Lo;0;L;;;;;N;;;;; 11214;KHOJKI LETTER TTA;Lo;0;L;;;;;N;;;;; 11215;KHOJKI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11216;KHOJKI LETTER DDA;Lo;0;L;;;;;N;;;;; 11217;KHOJKI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11218;KHOJKI LETTER NNA;Lo;0;L;;;;;N;;;;; 11219;KHOJKI LETTER TA;Lo;0;L;;;;;N;;;;; 1121A;KHOJKI LETTER THA;Lo;0;L;;;;;N;;;;; 1121B;KHOJKI LETTER DA;Lo;0;L;;;;;N;;;;; 1121C;KHOJKI LETTER DDDA;Lo;0;L;;;;;N;;;;; 1121D;KHOJKI LETTER DHA;Lo;0;L;;;;;N;;;;; 1121E;KHOJKI LETTER NA;Lo;0;L;;;;;N;;;;; 1121F;KHOJKI LETTER PA;Lo;0;L;;;;;N;;;;; 11220;KHOJKI LETTER PHA;Lo;0;L;;;;;N;;;;; 11221;KHOJKI LETTER BA;Lo;0;L;;;;;N;;;;; 11222;KHOJKI LETTER BBA;Lo;0;L;;;;;N;;;;; 11223;KHOJKI LETTER BHA;Lo;0;L;;;;;N;;;;; 11224;KHOJKI LETTER MA;Lo;0;L;;;;;N;;;;; 11225;KHOJKI LETTER YA;Lo;0;L;;;;;N;;;;; 11226;KHOJKI LETTER RA;Lo;0;L;;;;;N;;;;; 11227;KHOJKI LETTER LA;Lo;0;L;;;;;N;;;;; 11228;KHOJKI LETTER VA;Lo;0;L;;;;;N;;;;; 11229;KHOJKI LETTER SA;Lo;0;L;;;;;N;;;;; 1122A;KHOJKI LETTER HA;Lo;0;L;;;;;N;;;;; 1122B;KHOJKI LETTER LLA;Lo;0;L;;;;;N;;;;; 1122C;KHOJKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 1122D;KHOJKI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 1122E;KHOJKI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 1122F;KHOJKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11230;KHOJKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 11231;KHOJKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 11232;KHOJKI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 11233;KHOJKI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 11234;KHOJKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11235;KHOJKI SIGN VIRAMA;Mc;9;L;;;;;N;;;;; 11236;KHOJKI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 11237;KHOJKI SIGN SHADDA;Mn;0;NSM;;;;;N;;;;; 11238;KHOJKI DANDA;Po;0;L;;;;;N;;;;; 11239;KHOJKI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 1123A;KHOJKI WORD SEPARATOR;Po;0;L;;;;;N;;;;; 1123B;KHOJKI SECTION MARK;Po;0;L;;;;;N;;;;; 1123C;KHOJKI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;; 1123D;KHOJKI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 1123E;KHOJKI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;; 11280;MULTANI LETTER A;Lo;0;L;;;;;N;;;;; 11281;MULTANI LETTER I;Lo;0;L;;;;;N;;;;; 11282;MULTANI LETTER U;Lo;0;L;;;;;N;;;;; 11283;MULTANI LETTER E;Lo;0;L;;;;;N;;;;; 11284;MULTANI LETTER KA;Lo;0;L;;;;;N;;;;; 11285;MULTANI LETTER KHA;Lo;0;L;;;;;N;;;;; 11286;MULTANI LETTER GA;Lo;0;L;;;;;N;;;;; 11288;MULTANI LETTER GHA;Lo;0;L;;;;;N;;;;; 1128A;MULTANI LETTER CA;Lo;0;L;;;;;N;;;;; 1128B;MULTANI LETTER CHA;Lo;0;L;;;;;N;;;;; 1128C;MULTANI LETTER JA;Lo;0;L;;;;;N;;;;; 1128D;MULTANI LETTER JJA;Lo;0;L;;;;;N;;;;; 1128F;MULTANI LETTER NYA;Lo;0;L;;;;;N;;;;; 11290;MULTANI LETTER TTA;Lo;0;L;;;;;N;;;;; 11291;MULTANI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11292;MULTANI LETTER DDA;Lo;0;L;;;;;N;;;;; 11293;MULTANI LETTER DDDA;Lo;0;L;;;;;N;;;;; 11294;MULTANI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11295;MULTANI LETTER NNA;Lo;0;L;;;;;N;;;;; 11296;MULTANI LETTER TA;Lo;0;L;;;;;N;;;;; 11297;MULTANI LETTER THA;Lo;0;L;;;;;N;;;;; 11298;MULTANI LETTER DA;Lo;0;L;;;;;N;;;;; 11299;MULTANI LETTER DHA;Lo;0;L;;;;;N;;;;; 1129A;MULTANI LETTER NA;Lo;0;L;;;;;N;;;;; 1129B;MULTANI LETTER PA;Lo;0;L;;;;;N;;;;; 1129C;MULTANI LETTER PHA;Lo;0;L;;;;;N;;;;; 1129D;MULTANI LETTER BA;Lo;0;L;;;;;N;;;;; 1129F;MULTANI LETTER BHA;Lo;0;L;;;;;N;;;;; 112A0;MULTANI LETTER MA;Lo;0;L;;;;;N;;;;; 112A1;MULTANI LETTER YA;Lo;0;L;;;;;N;;;;; 112A2;MULTANI LETTER RA;Lo;0;L;;;;;N;;;;; 112A3;MULTANI LETTER LA;Lo;0;L;;;;;N;;;;; 112A4;MULTANI LETTER VA;Lo;0;L;;;;;N;;;;; 112A5;MULTANI LETTER SA;Lo;0;L;;;;;N;;;;; 112A6;MULTANI LETTER HA;Lo;0;L;;;;;N;;;;; 112A7;MULTANI LETTER RRA;Lo;0;L;;;;;N;;;;; 112A8;MULTANI LETTER RHA;Lo;0;L;;;;;N;;;;; 112A9;MULTANI SECTION MARK;Po;0;L;;;;;N;;;;; 112B0;KHUDAWADI LETTER A;Lo;0;L;;;;;N;;;;; 112B1;KHUDAWADI LETTER AA;Lo;0;L;;;;;N;;;;; 112B2;KHUDAWADI LETTER I;Lo;0;L;;;;;N;;;;; 112B3;KHUDAWADI LETTER II;Lo;0;L;;;;;N;;;;; 112B4;KHUDAWADI LETTER U;Lo;0;L;;;;;N;;;;; 112B5;KHUDAWADI LETTER UU;Lo;0;L;;;;;N;;;;; 112B6;KHUDAWADI LETTER E;Lo;0;L;;;;;N;;;;; 112B7;KHUDAWADI LETTER AI;Lo;0;L;;;;;N;;;;; 112B8;KHUDAWADI LETTER O;Lo;0;L;;;;;N;;;;; 112B9;KHUDAWADI LETTER AU;Lo;0;L;;;;;N;;;;; 112BA;KHUDAWADI LETTER KA;Lo;0;L;;;;;N;;;;; 112BB;KHUDAWADI LETTER KHA;Lo;0;L;;;;;N;;;;; 112BC;KHUDAWADI LETTER GA;Lo;0;L;;;;;N;;;;; 112BD;KHUDAWADI LETTER GGA;Lo;0;L;;;;;N;;;;; 112BE;KHUDAWADI LETTER GHA;Lo;0;L;;;;;N;;;;; 112BF;KHUDAWADI LETTER NGA;Lo;0;L;;;;;N;;;;; 112C0;KHUDAWADI LETTER CA;Lo;0;L;;;;;N;;;;; 112C1;KHUDAWADI LETTER CHA;Lo;0;L;;;;;N;;;;; 112C2;KHUDAWADI LETTER JA;Lo;0;L;;;;;N;;;;; 112C3;KHUDAWADI LETTER JJA;Lo;0;L;;;;;N;;;;; 112C4;KHUDAWADI LETTER JHA;Lo;0;L;;;;;N;;;;; 112C5;KHUDAWADI LETTER NYA;Lo;0;L;;;;;N;;;;; 112C6;KHUDAWADI LETTER TTA;Lo;0;L;;;;;N;;;;; 112C7;KHUDAWADI LETTER TTHA;Lo;0;L;;;;;N;;;;; 112C8;KHUDAWADI LETTER DDA;Lo;0;L;;;;;N;;;;; 112C9;KHUDAWADI LETTER DDDA;Lo;0;L;;;;;N;;;;; 112CA;KHUDAWADI LETTER RRA;Lo;0;L;;;;;N;;;;; 112CB;KHUDAWADI LETTER DDHA;Lo;0;L;;;;;N;;;;; 112CC;KHUDAWADI LETTER NNA;Lo;0;L;;;;;N;;;;; 112CD;KHUDAWADI LETTER TA;Lo;0;L;;;;;N;;;;; 112CE;KHUDAWADI LETTER THA;Lo;0;L;;;;;N;;;;; 112CF;KHUDAWADI LETTER DA;Lo;0;L;;;;;N;;;;; 112D0;KHUDAWADI LETTER DHA;Lo;0;L;;;;;N;;;;; 112D1;KHUDAWADI LETTER NA;Lo;0;L;;;;;N;;;;; 112D2;KHUDAWADI LETTER PA;Lo;0;L;;;;;N;;;;; 112D3;KHUDAWADI LETTER PHA;Lo;0;L;;;;;N;;;;; 112D4;KHUDAWADI LETTER BA;Lo;0;L;;;;;N;;;;; 112D5;KHUDAWADI LETTER BBA;Lo;0;L;;;;;N;;;;; 112D6;KHUDAWADI LETTER BHA;Lo;0;L;;;;;N;;;;; 112D7;KHUDAWADI LETTER MA;Lo;0;L;;;;;N;;;;; 112D8;KHUDAWADI LETTER YA;Lo;0;L;;;;;N;;;;; 112D9;KHUDAWADI LETTER RA;Lo;0;L;;;;;N;;;;; 112DA;KHUDAWADI LETTER LA;Lo;0;L;;;;;N;;;;; 112DB;KHUDAWADI LETTER VA;Lo;0;L;;;;;N;;;;; 112DC;KHUDAWADI LETTER SHA;Lo;0;L;;;;;N;;;;; 112DD;KHUDAWADI LETTER SA;Lo;0;L;;;;;N;;;;; 112DE;KHUDAWADI LETTER HA;Lo;0;L;;;;;N;;;;; 112DF;KHUDAWADI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 112E0;KHUDAWADI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 112E1;KHUDAWADI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 112E2;KHUDAWADI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 112E3;KHUDAWADI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 112E4;KHUDAWADI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 112E5;KHUDAWADI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 112E6;KHUDAWADI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 112E7;KHUDAWADI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 112E8;KHUDAWADI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 112E9;KHUDAWADI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 112EA;KHUDAWADI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 112F0;KHUDAWADI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 112F1;KHUDAWADI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 112F2;KHUDAWADI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 112F3;KHUDAWADI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 112F4;KHUDAWADI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 112F5;KHUDAWADI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 112F6;KHUDAWADI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 112F7;KHUDAWADI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 112F8;KHUDAWADI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 112F9;KHUDAWADI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11300;GRANTHA SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;; 11301;GRANTHA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 11302;GRANTHA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; 11303;GRANTHA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11305;GRANTHA LETTER A;Lo;0;L;;;;;N;;;;; 11306;GRANTHA LETTER AA;Lo;0;L;;;;;N;;;;; 11307;GRANTHA LETTER I;Lo;0;L;;;;;N;;;;; 11308;GRANTHA LETTER II;Lo;0;L;;;;;N;;;;; 11309;GRANTHA LETTER U;Lo;0;L;;;;;N;;;;; 1130A;GRANTHA LETTER UU;Lo;0;L;;;;;N;;;;; 1130B;GRANTHA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 1130C;GRANTHA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 1130F;GRANTHA LETTER EE;Lo;0;L;;;;;N;;;;; 11310;GRANTHA LETTER AI;Lo;0;L;;;;;N;;;;; 11313;GRANTHA LETTER OO;Lo;0;L;;;;;N;;;;; 11314;GRANTHA LETTER AU;Lo;0;L;;;;;N;;;;; 11315;GRANTHA LETTER KA;Lo;0;L;;;;;N;;;;; 11316;GRANTHA LETTER KHA;Lo;0;L;;;;;N;;;;; 11317;GRANTHA LETTER GA;Lo;0;L;;;;;N;;;;; 11318;GRANTHA LETTER GHA;Lo;0;L;;;;;N;;;;; 11319;GRANTHA LETTER NGA;Lo;0;L;;;;;N;;;;; 1131A;GRANTHA LETTER CA;Lo;0;L;;;;;N;;;;; 1131B;GRANTHA LETTER CHA;Lo;0;L;;;;;N;;;;; 1131C;GRANTHA LETTER JA;Lo;0;L;;;;;N;;;;; 1131D;GRANTHA LETTER JHA;Lo;0;L;;;;;N;;;;; 1131E;GRANTHA LETTER NYA;Lo;0;L;;;;;N;;;;; 1131F;GRANTHA LETTER TTA;Lo;0;L;;;;;N;;;;; 11320;GRANTHA LETTER TTHA;Lo;0;L;;;;;N;;;;; 11321;GRANTHA LETTER DDA;Lo;0;L;;;;;N;;;;; 11322;GRANTHA LETTER DDHA;Lo;0;L;;;;;N;;;;; 11323;GRANTHA LETTER NNA;Lo;0;L;;;;;N;;;;; 11324;GRANTHA LETTER TA;Lo;0;L;;;;;N;;;;; 11325;GRANTHA LETTER THA;Lo;0;L;;;;;N;;;;; 11326;GRANTHA LETTER DA;Lo;0;L;;;;;N;;;;; 11327;GRANTHA LETTER DHA;Lo;0;L;;;;;N;;;;; 11328;GRANTHA LETTER NA;Lo;0;L;;;;;N;;;;; 1132A;GRANTHA LETTER PA;Lo;0;L;;;;;N;;;;; 1132B;GRANTHA LETTER PHA;Lo;0;L;;;;;N;;;;; 1132C;GRANTHA LETTER BA;Lo;0;L;;;;;N;;;;; 1132D;GRANTHA LETTER BHA;Lo;0;L;;;;;N;;;;; 1132E;GRANTHA LETTER MA;Lo;0;L;;;;;N;;;;; 1132F;GRANTHA LETTER YA;Lo;0;L;;;;;N;;;;; 11330;GRANTHA LETTER RA;Lo;0;L;;;;;N;;;;; 11332;GRANTHA LETTER LA;Lo;0;L;;;;;N;;;;; 11333;GRANTHA LETTER LLA;Lo;0;L;;;;;N;;;;; 11335;GRANTHA LETTER VA;Lo;0;L;;;;;N;;;;; 11336;GRANTHA LETTER SHA;Lo;0;L;;;;;N;;;;; 11337;GRANTHA LETTER SSA;Lo;0;L;;;;;N;;;;; 11338;GRANTHA LETTER SA;Lo;0;L;;;;;N;;;;; 11339;GRANTHA LETTER HA;Lo;0;L;;;;;N;;;;; 1133C;GRANTHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 1133D;GRANTHA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 1133E;GRANTHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 1133F;GRANTHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 11340;GRANTHA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 11341;GRANTHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 11342;GRANTHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 11343;GRANTHA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; 11344;GRANTHA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; 11347;GRANTHA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; 11348;GRANTHA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 1134B;GRANTHA VOWEL SIGN OO;Mc;0;L;11347 1133E;;;;N;;;;; 1134C;GRANTHA VOWEL SIGN AU;Mc;0;L;11347 11357;;;;N;;;;; 1134D;GRANTHA SIGN VIRAMA;Mc;9;L;;;;;N;;;;; 11350;GRANTHA OM;Lo;0;L;;;;;N;;;;; 11357;GRANTHA AU LENGTH MARK;Mc;0;L;;;;;N;;;;; 1135D;GRANTHA SIGN PLUTA;Lo;0;L;;;;;N;;;;; 1135E;GRANTHA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;; 1135F;GRANTHA LETTER VEDIC DOUBLE ANUSVARA;Lo;0;L;;;;;N;;;;; 11360;GRANTHA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11361;GRANTHA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 11362;GRANTHA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;; 11363;GRANTHA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;; 11366;COMBINING GRANTHA DIGIT ZERO;Mn;230;NSM;;;;;N;;;;; 11367;COMBINING GRANTHA DIGIT ONE;Mn;230;NSM;;;;;N;;;;; 11368;COMBINING GRANTHA DIGIT TWO;Mn;230;NSM;;;;;N;;;;; 11369;COMBINING GRANTHA DIGIT THREE;Mn;230;NSM;;;;;N;;;;; 1136A;COMBINING GRANTHA DIGIT FOUR;Mn;230;NSM;;;;;N;;;;; 1136B;COMBINING GRANTHA DIGIT FIVE;Mn;230;NSM;;;;;N;;;;; 1136C;COMBINING GRANTHA DIGIT SIX;Mn;230;NSM;;;;;N;;;;; 11370;COMBINING GRANTHA LETTER A;Mn;230;NSM;;;;;N;;;;; 11371;COMBINING GRANTHA LETTER KA;Mn;230;NSM;;;;;N;;;;; 11372;COMBINING GRANTHA LETTER NA;Mn;230;NSM;;;;;N;;;;; 11373;COMBINING GRANTHA LETTER VI;Mn;230;NSM;;;;;N;;;;; 11374;COMBINING GRANTHA LETTER PA;Mn;230;NSM;;;;;N;;;;; 11400;NEWA LETTER A;Lo;0;L;;;;;N;;;;; 11401;NEWA LETTER AA;Lo;0;L;;;;;N;;;;; 11402;NEWA LETTER I;Lo;0;L;;;;;N;;;;; 11403;NEWA LETTER II;Lo;0;L;;;;;N;;;;; 11404;NEWA LETTER U;Lo;0;L;;;;;N;;;;; 11405;NEWA LETTER UU;Lo;0;L;;;;;N;;;;; 11406;NEWA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 11407;NEWA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11408;NEWA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 11409;NEWA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1140A;NEWA LETTER E;Lo;0;L;;;;;N;;;;; 1140B;NEWA LETTER AI;Lo;0;L;;;;;N;;;;; 1140C;NEWA LETTER O;Lo;0;L;;;;;N;;;;; 1140D;NEWA LETTER AU;Lo;0;L;;;;;N;;;;; 1140E;NEWA LETTER KA;Lo;0;L;;;;;N;;;;; 1140F;NEWA LETTER KHA;Lo;0;L;;;;;N;;;;; 11410;NEWA LETTER GA;Lo;0;L;;;;;N;;;;; 11411;NEWA LETTER GHA;Lo;0;L;;;;;N;;;;; 11412;NEWA LETTER NGA;Lo;0;L;;;;;N;;;;; 11413;NEWA LETTER NGHA;Lo;0;L;;;;;N;;;;; 11414;NEWA LETTER CA;Lo;0;L;;;;;N;;;;; 11415;NEWA LETTER CHA;Lo;0;L;;;;;N;;;;; 11416;NEWA LETTER JA;Lo;0;L;;;;;N;;;;; 11417;NEWA LETTER JHA;Lo;0;L;;;;;N;;;;; 11418;NEWA LETTER NYA;Lo;0;L;;;;;N;;;;; 11419;NEWA LETTER NYHA;Lo;0;L;;;;;N;;;;; 1141A;NEWA LETTER TTA;Lo;0;L;;;;;N;;;;; 1141B;NEWA LETTER TTHA;Lo;0;L;;;;;N;;;;; 1141C;NEWA LETTER DDA;Lo;0;L;;;;;N;;;;; 1141D;NEWA LETTER DDHA;Lo;0;L;;;;;N;;;;; 1141E;NEWA LETTER NNA;Lo;0;L;;;;;N;;;;; 1141F;NEWA LETTER TA;Lo;0;L;;;;;N;;;;; 11420;NEWA LETTER THA;Lo;0;L;;;;;N;;;;; 11421;NEWA LETTER DA;Lo;0;L;;;;;N;;;;; 11422;NEWA LETTER DHA;Lo;0;L;;;;;N;;;;; 11423;NEWA LETTER NA;Lo;0;L;;;;;N;;;;; 11424;NEWA LETTER NHA;Lo;0;L;;;;;N;;;;; 11425;NEWA LETTER PA;Lo;0;L;;;;;N;;;;; 11426;NEWA LETTER PHA;Lo;0;L;;;;;N;;;;; 11427;NEWA LETTER BA;Lo;0;L;;;;;N;;;;; 11428;NEWA LETTER BHA;Lo;0;L;;;;;N;;;;; 11429;NEWA LETTER MA;Lo;0;L;;;;;N;;;;; 1142A;NEWA LETTER MHA;Lo;0;L;;;;;N;;;;; 1142B;NEWA LETTER YA;Lo;0;L;;;;;N;;;;; 1142C;NEWA LETTER RA;Lo;0;L;;;;;N;;;;; 1142D;NEWA LETTER RHA;Lo;0;L;;;;;N;;;;; 1142E;NEWA LETTER LA;Lo;0;L;;;;;N;;;;; 1142F;NEWA LETTER LHA;Lo;0;L;;;;;N;;;;; 11430;NEWA LETTER WA;Lo;0;L;;;;;N;;;;; 11431;NEWA LETTER SHA;Lo;0;L;;;;;N;;;;; 11432;NEWA LETTER SSA;Lo;0;L;;;;;N;;;;; 11433;NEWA LETTER SA;Lo;0;L;;;;;N;;;;; 11434;NEWA LETTER HA;Lo;0;L;;;;;N;;;;; 11435;NEWA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 11436;NEWA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 11437;NEWA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 11438;NEWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11439;NEWA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 1143A;NEWA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 1143B;NEWA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 1143C;NEWA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 1143D;NEWA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 1143E;NEWA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 1143F;NEWA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 11440;NEWA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 11441;NEWA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 11442;NEWA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 11443;NEWA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 11444;NEWA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11445;NEWA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11446;NEWA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 11447;NEWA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 11448;NEWA SIGN FINAL ANUSVARA;Lo;0;L;;;;;N;;;;; 11449;NEWA OM;Lo;0;L;;;;;N;;;;; 1144A;NEWA SIDDHI;Lo;0;L;;;;;N;;;;; 1144B;NEWA DANDA;Po;0;L;;;;;N;;;;; 1144C;NEWA DOUBLE DANDA;Po;0;L;;;;;N;;;;; 1144D;NEWA COMMA;Po;0;L;;;;;N;;;;; 1144E;NEWA GAP FILLER;Po;0;L;;;;;N;;;;; 1144F;NEWA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 11450;NEWA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11451;NEWA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11452;NEWA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11453;NEWA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 11454;NEWA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 11455;NEWA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 11456;NEWA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 11457;NEWA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11458;NEWA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11459;NEWA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;; 1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;; 11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;; 11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;; 11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;; 11483;TIRHUTA LETTER I;Lo;0;L;;;;;N;;;;; 11484;TIRHUTA LETTER II;Lo;0;L;;;;;N;;;;; 11485;TIRHUTA LETTER U;Lo;0;L;;;;;N;;;;; 11486;TIRHUTA LETTER UU;Lo;0;L;;;;;N;;;;; 11487;TIRHUTA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 11488;TIRHUTA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11489;TIRHUTA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 1148A;TIRHUTA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1148B;TIRHUTA LETTER E;Lo;0;L;;;;;N;;;;; 1148C;TIRHUTA LETTER AI;Lo;0;L;;;;;N;;;;; 1148D;TIRHUTA LETTER O;Lo;0;L;;;;;N;;;;; 1148E;TIRHUTA LETTER AU;Lo;0;L;;;;;N;;;;; 1148F;TIRHUTA LETTER KA;Lo;0;L;;;;;N;;;;; 11490;TIRHUTA LETTER KHA;Lo;0;L;;;;;N;;;;; 11491;TIRHUTA LETTER GA;Lo;0;L;;;;;N;;;;; 11492;TIRHUTA LETTER GHA;Lo;0;L;;;;;N;;;;; 11493;TIRHUTA LETTER NGA;Lo;0;L;;;;;N;;;;; 11494;TIRHUTA LETTER CA;Lo;0;L;;;;;N;;;;; 11495;TIRHUTA LETTER CHA;Lo;0;L;;;;;N;;;;; 11496;TIRHUTA LETTER JA;Lo;0;L;;;;;N;;;;; 11497;TIRHUTA LETTER JHA;Lo;0;L;;;;;N;;;;; 11498;TIRHUTA LETTER NYA;Lo;0;L;;;;;N;;;;; 11499;TIRHUTA LETTER TTA;Lo;0;L;;;;;N;;;;; 1149A;TIRHUTA LETTER TTHA;Lo;0;L;;;;;N;;;;; 1149B;TIRHUTA LETTER DDA;Lo;0;L;;;;;N;;;;; 1149C;TIRHUTA LETTER DDHA;Lo;0;L;;;;;N;;;;; 1149D;TIRHUTA LETTER NNA;Lo;0;L;;;;;N;;;;; 1149E;TIRHUTA LETTER TA;Lo;0;L;;;;;N;;;;; 1149F;TIRHUTA LETTER THA;Lo;0;L;;;;;N;;;;; 114A0;TIRHUTA LETTER DA;Lo;0;L;;;;;N;;;;; 114A1;TIRHUTA LETTER DHA;Lo;0;L;;;;;N;;;;; 114A2;TIRHUTA LETTER NA;Lo;0;L;;;;;N;;;;; 114A3;TIRHUTA LETTER PA;Lo;0;L;;;;;N;;;;; 114A4;TIRHUTA LETTER PHA;Lo;0;L;;;;;N;;;;; 114A5;TIRHUTA LETTER BA;Lo;0;L;;;;;N;;;;; 114A6;TIRHUTA LETTER BHA;Lo;0;L;;;;;N;;;;; 114A7;TIRHUTA LETTER MA;Lo;0;L;;;;;N;;;;; 114A8;TIRHUTA LETTER YA;Lo;0;L;;;;;N;;;;; 114A9;TIRHUTA LETTER RA;Lo;0;L;;;;;N;;;;; 114AA;TIRHUTA LETTER LA;Lo;0;L;;;;;N;;;;; 114AB;TIRHUTA LETTER VA;Lo;0;L;;;;;N;;;;; 114AC;TIRHUTA LETTER SHA;Lo;0;L;;;;;N;;;;; 114AD;TIRHUTA LETTER SSA;Lo;0;L;;;;;N;;;;; 114AE;TIRHUTA LETTER SA;Lo;0;L;;;;;N;;;;; 114AF;TIRHUTA LETTER HA;Lo;0;L;;;;;N;;;;; 114B0;TIRHUTA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 114B1;TIRHUTA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 114B2;TIRHUTA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 114B3;TIRHUTA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 114B4;TIRHUTA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 114B5;TIRHUTA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 114B6;TIRHUTA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 114B7;TIRHUTA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 114B8;TIRHUTA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 114B9;TIRHUTA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 114BA;TIRHUTA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; 114BB;TIRHUTA VOWEL SIGN AI;Mc;0;L;114B9 114BA;;;;N;;;;; 114BC;TIRHUTA VOWEL SIGN O;Mc;0;L;114B9 114B0;;;;N;;;;; 114BD;TIRHUTA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; 114BE;TIRHUTA VOWEL SIGN AU;Mc;0;L;114B9 114BD;;;;N;;;;; 114BF;TIRHUTA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 114C0;TIRHUTA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 114C1;TIRHUTA SIGN VISARGA;Mc;0;L;;;;;N;;;;; 114C2;TIRHUTA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 114C3;TIRHUTA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 114C4;TIRHUTA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 114C5;TIRHUTA GVANG;Lo;0;L;;;;;N;;;;; 114C6;TIRHUTA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 114C7;TIRHUTA OM;Lo;0;L;;;;;N;;;;; 114D0;TIRHUTA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 114D1;TIRHUTA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 114D2;TIRHUTA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 114D3;TIRHUTA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 114D4;TIRHUTA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 114D5;TIRHUTA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 114D6;TIRHUTA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 114D7;TIRHUTA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 114D8;TIRHUTA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 114D9;TIRHUTA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11580;SIDDHAM LETTER A;Lo;0;L;;;;;N;;;;; 11581;SIDDHAM LETTER AA;Lo;0;L;;;;;N;;;;; 11582;SIDDHAM LETTER I;Lo;0;L;;;;;N;;;;; 11583;SIDDHAM LETTER II;Lo;0;L;;;;;N;;;;; 11584;SIDDHAM LETTER U;Lo;0;L;;;;;N;;;;; 11585;SIDDHAM LETTER UU;Lo;0;L;;;;;N;;;;; 11586;SIDDHAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 11587;SIDDHAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11588;SIDDHAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 11589;SIDDHAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1158A;SIDDHAM LETTER E;Lo;0;L;;;;;N;;;;; 1158B;SIDDHAM LETTER AI;Lo;0;L;;;;;N;;;;; 1158C;SIDDHAM LETTER O;Lo;0;L;;;;;N;;;;; 1158D;SIDDHAM LETTER AU;Lo;0;L;;;;;N;;;;; 1158E;SIDDHAM LETTER KA;Lo;0;L;;;;;N;;;;; 1158F;SIDDHAM LETTER KHA;Lo;0;L;;;;;N;;;;; 11590;SIDDHAM LETTER GA;Lo;0;L;;;;;N;;;;; 11591;SIDDHAM LETTER GHA;Lo;0;L;;;;;N;;;;; 11592;SIDDHAM LETTER NGA;Lo;0;L;;;;;N;;;;; 11593;SIDDHAM LETTER CA;Lo;0;L;;;;;N;;;;; 11594;SIDDHAM LETTER CHA;Lo;0;L;;;;;N;;;;; 11595;SIDDHAM LETTER JA;Lo;0;L;;;;;N;;;;; 11596;SIDDHAM LETTER JHA;Lo;0;L;;;;;N;;;;; 11597;SIDDHAM LETTER NYA;Lo;0;L;;;;;N;;;;; 11598;SIDDHAM LETTER TTA;Lo;0;L;;;;;N;;;;; 11599;SIDDHAM LETTER TTHA;Lo;0;L;;;;;N;;;;; 1159A;SIDDHAM LETTER DDA;Lo;0;L;;;;;N;;;;; 1159B;SIDDHAM LETTER DDHA;Lo;0;L;;;;;N;;;;; 1159C;SIDDHAM LETTER NNA;Lo;0;L;;;;;N;;;;; 1159D;SIDDHAM LETTER TA;Lo;0;L;;;;;N;;;;; 1159E;SIDDHAM LETTER THA;Lo;0;L;;;;;N;;;;; 1159F;SIDDHAM LETTER DA;Lo;0;L;;;;;N;;;;; 115A0;SIDDHAM LETTER DHA;Lo;0;L;;;;;N;;;;; 115A1;SIDDHAM LETTER NA;Lo;0;L;;;;;N;;;;; 115A2;SIDDHAM LETTER PA;Lo;0;L;;;;;N;;;;; 115A3;SIDDHAM LETTER PHA;Lo;0;L;;;;;N;;;;; 115A4;SIDDHAM LETTER BA;Lo;0;L;;;;;N;;;;; 115A5;SIDDHAM LETTER BHA;Lo;0;L;;;;;N;;;;; 115A6;SIDDHAM LETTER MA;Lo;0;L;;;;;N;;;;; 115A7;SIDDHAM LETTER YA;Lo;0;L;;;;;N;;;;; 115A8;SIDDHAM LETTER RA;Lo;0;L;;;;;N;;;;; 115A9;SIDDHAM LETTER LA;Lo;0;L;;;;;N;;;;; 115AA;SIDDHAM LETTER VA;Lo;0;L;;;;;N;;;;; 115AB;SIDDHAM LETTER SHA;Lo;0;L;;;;;N;;;;; 115AC;SIDDHAM LETTER SSA;Lo;0;L;;;;;N;;;;; 115AD;SIDDHAM LETTER SA;Lo;0;L;;;;;N;;;;; 115AE;SIDDHAM LETTER HA;Lo;0;L;;;;;N;;;;; 115AF;SIDDHAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 115B0;SIDDHAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 115B1;SIDDHAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 115B2;SIDDHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 115B3;SIDDHAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 115B4;SIDDHAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 115B5;SIDDHAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 115B8;SIDDHAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 115B9;SIDDHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 115BA;SIDDHAM VOWEL SIGN O;Mc;0;L;115B8 115AF;;;;N;;;;; 115BB;SIDDHAM VOWEL SIGN AU;Mc;0;L;115B9 115AF;;;;N;;;;; 115BC;SIDDHAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 115BD;SIDDHAM SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 115BE;SIDDHAM SIGN VISARGA;Mc;0;L;;;;;N;;;;; 115BF;SIDDHAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 115C0;SIDDHAM SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 115C1;SIDDHAM SIGN SIDDHAM;Po;0;L;;;;;N;;;;; 115C2;SIDDHAM DANDA;Po;0;L;;;;;N;;;;; 115C3;SIDDHAM DOUBLE DANDA;Po;0;L;;;;;N;;;;; 115C4;SIDDHAM SEPARATOR DOT;Po;0;L;;;;;N;;;;; 115C5;SIDDHAM SEPARATOR BAR;Po;0;L;;;;;N;;;;; 115C6;SIDDHAM REPETITION MARK-1;Po;0;L;;;;;N;;;;; 115C7;SIDDHAM REPETITION MARK-2;Po;0;L;;;;;N;;;;; 115C8;SIDDHAM REPETITION MARK-3;Po;0;L;;;;;N;;;;; 115C9;SIDDHAM END OF TEXT MARK;Po;0;L;;;;;N;;;;; 115CA;SIDDHAM SECTION MARK WITH TRIDENT AND U-SHAPED ORNAMENTS;Po;0;L;;;;;N;;;;; 115CB;SIDDHAM SECTION MARK WITH TRIDENT AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;; 115CC;SIDDHAM SECTION MARK WITH RAYS AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;; 115CD;SIDDHAM SECTION MARK WITH RAYS AND DOTTED DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;; 115CE;SIDDHAM SECTION MARK WITH RAYS AND DOTTED TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;; 115CF;SIDDHAM SECTION MARK DOUBLE RING;Po;0;L;;;;;N;;;;; 115D0;SIDDHAM SECTION MARK DOUBLE RING WITH RAYS;Po;0;L;;;;;N;;;;; 115D1;SIDDHAM SECTION MARK WITH DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;; 115D2;SIDDHAM SECTION MARK WITH TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;; 115D3;SIDDHAM SECTION MARK WITH QUADRUPLE CRESCENTS;Po;0;L;;;;;N;;;;; 115D4;SIDDHAM SECTION MARK WITH SEPTUPLE CRESCENTS;Po;0;L;;;;;N;;;;; 115D5;SIDDHAM SECTION MARK WITH CIRCLES AND RAYS;Po;0;L;;;;;N;;;;; 115D6;SIDDHAM SECTION MARK WITH CIRCLES AND TWO ENCLOSURES;Po;0;L;;;;;N;;;;; 115D7;SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES;Po;0;L;;;;;N;;;;; 115D8;SIDDHAM LETTER THREE-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;; 115D9;SIDDHAM LETTER TWO-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;; 115DA;SIDDHAM LETTER TWO-CIRCLE ALTERNATE II;Lo;0;L;;;;;N;;;;; 115DB;SIDDHAM LETTER ALTERNATE U;Lo;0;L;;;;;N;;;;; 115DC;SIDDHAM VOWEL SIGN ALTERNATE U;Mn;0;NSM;;;;;N;;;;; 115DD;SIDDHAM VOWEL SIGN ALTERNATE UU;Mn;0;NSM;;;;;N;;;;; 11600;MODI LETTER A;Lo;0;L;;;;;N;;;;; 11601;MODI LETTER AA;Lo;0;L;;;;;N;;;;; 11602;MODI LETTER I;Lo;0;L;;;;;N;;;;; 11603;MODI LETTER II;Lo;0;L;;;;;N;;;;; 11604;MODI LETTER U;Lo;0;L;;;;;N;;;;; 11605;MODI LETTER UU;Lo;0;L;;;;;N;;;;; 11606;MODI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 11607;MODI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11608;MODI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 11609;MODI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; 1160A;MODI LETTER E;Lo;0;L;;;;;N;;;;; 1160B;MODI LETTER AI;Lo;0;L;;;;;N;;;;; 1160C;MODI LETTER O;Lo;0;L;;;;;N;;;;; 1160D;MODI LETTER AU;Lo;0;L;;;;;N;;;;; 1160E;MODI LETTER KA;Lo;0;L;;;;;N;;;;; 1160F;MODI LETTER KHA;Lo;0;L;;;;;N;;;;; 11610;MODI LETTER GA;Lo;0;L;;;;;N;;;;; 11611;MODI LETTER GHA;Lo;0;L;;;;;N;;;;; 11612;MODI LETTER NGA;Lo;0;L;;;;;N;;;;; 11613;MODI LETTER CA;Lo;0;L;;;;;N;;;;; 11614;MODI LETTER CHA;Lo;0;L;;;;;N;;;;; 11615;MODI LETTER JA;Lo;0;L;;;;;N;;;;; 11616;MODI LETTER JHA;Lo;0;L;;;;;N;;;;; 11617;MODI LETTER NYA;Lo;0;L;;;;;N;;;;; 11618;MODI LETTER TTA;Lo;0;L;;;;;N;;;;; 11619;MODI LETTER TTHA;Lo;0;L;;;;;N;;;;; 1161A;MODI LETTER DDA;Lo;0;L;;;;;N;;;;; 1161B;MODI LETTER DDHA;Lo;0;L;;;;;N;;;;; 1161C;MODI LETTER NNA;Lo;0;L;;;;;N;;;;; 1161D;MODI LETTER TA;Lo;0;L;;;;;N;;;;; 1161E;MODI LETTER THA;Lo;0;L;;;;;N;;;;; 1161F;MODI LETTER DA;Lo;0;L;;;;;N;;;;; 11620;MODI LETTER DHA;Lo;0;L;;;;;N;;;;; 11621;MODI LETTER NA;Lo;0;L;;;;;N;;;;; 11622;MODI LETTER PA;Lo;0;L;;;;;N;;;;; 11623;MODI LETTER PHA;Lo;0;L;;;;;N;;;;; 11624;MODI LETTER BA;Lo;0;L;;;;;N;;;;; 11625;MODI LETTER BHA;Lo;0;L;;;;;N;;;;; 11626;MODI LETTER MA;Lo;0;L;;;;;N;;;;; 11627;MODI LETTER YA;Lo;0;L;;;;;N;;;;; 11628;MODI LETTER RA;Lo;0;L;;;;;N;;;;; 11629;MODI LETTER LA;Lo;0;L;;;;;N;;;;; 1162A;MODI LETTER VA;Lo;0;L;;;;;N;;;;; 1162B;MODI LETTER SHA;Lo;0;L;;;;;N;;;;; 1162C;MODI LETTER SSA;Lo;0;L;;;;;N;;;;; 1162D;MODI LETTER SA;Lo;0;L;;;;;N;;;;; 1162E;MODI LETTER HA;Lo;0;L;;;;;N;;;;; 1162F;MODI LETTER LLA;Lo;0;L;;;;;N;;;;; 11630;MODI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 11631;MODI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 11632;MODI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 11633;MODI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11634;MODI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 11635;MODI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 11636;MODI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 11637;MODI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 11638;MODI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; 11639;MODI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 1163A;MODI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 1163B;MODI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 1163C;MODI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 1163D;MODI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 1163E;MODI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 1163F;MODI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 11640;MODI SIGN ARDHACANDRA;Mn;0;NSM;;;;;N;;;;; 11641;MODI DANDA;Po;0;L;;;;;N;;;;; 11642;MODI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 11643;MODI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 11644;MODI SIGN HUVA;Lo;0;L;;;;;N;;;;; 11650;MODI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11651;MODI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11652;MODI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11653;MODI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 11654;MODI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 11655;MODI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 11656;MODI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 11657;MODI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11658;MODI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11659;MODI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11660;MONGOLIAN BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; 11661;MONGOLIAN ROTATED BIRGA;Po;0;ON;;;;;N;;;;; 11662;MONGOLIAN DOUBLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; 11663;MONGOLIAN TRIPLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; 11664;MONGOLIAN BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; 11665;MONGOLIAN ROTATED BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; 11666;MONGOLIAN ROTATED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; 11667;MONGOLIAN INVERTED BIRGA;Po;0;ON;;;;;N;;;;; 11668;MONGOLIAN INVERTED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; 11669;MONGOLIAN SWIRL BIRGA;Po;0;ON;;;;;N;;;;; 1166A;MONGOLIAN SWIRL BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; 1166B;MONGOLIAN SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; 1166C;MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; 11680;TAKRI LETTER A;Lo;0;L;;;;;N;;;;; 11681;TAKRI LETTER AA;Lo;0;L;;;;;N;;;;; 11682;TAKRI LETTER I;Lo;0;L;;;;;N;;;;; 11683;TAKRI LETTER II;Lo;0;L;;;;;N;;;;; 11684;TAKRI LETTER U;Lo;0;L;;;;;N;;;;; 11685;TAKRI LETTER UU;Lo;0;L;;;;;N;;;;; 11686;TAKRI LETTER E;Lo;0;L;;;;;N;;;;; 11687;TAKRI LETTER AI;Lo;0;L;;;;;N;;;;; 11688;TAKRI LETTER O;Lo;0;L;;;;;N;;;;; 11689;TAKRI LETTER AU;Lo;0;L;;;;;N;;;;; 1168A;TAKRI LETTER KA;Lo;0;L;;;;;N;;;;; 1168B;TAKRI LETTER KHA;Lo;0;L;;;;;N;;;;; 1168C;TAKRI LETTER GA;Lo;0;L;;;;;N;;;;; 1168D;TAKRI LETTER GHA;Lo;0;L;;;;;N;;;;; 1168E;TAKRI LETTER NGA;Lo;0;L;;;;;N;;;;; 1168F;TAKRI LETTER CA;Lo;0;L;;;;;N;;;;; 11690;TAKRI LETTER CHA;Lo;0;L;;;;;N;;;;; 11691;TAKRI LETTER JA;Lo;0;L;;;;;N;;;;; 11692;TAKRI LETTER JHA;Lo;0;L;;;;;N;;;;; 11693;TAKRI LETTER NYA;Lo;0;L;;;;;N;;;;; 11694;TAKRI LETTER TTA;Lo;0;L;;;;;N;;;;; 11695;TAKRI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11696;TAKRI LETTER DDA;Lo;0;L;;;;;N;;;;; 11697;TAKRI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11698;TAKRI LETTER NNA;Lo;0;L;;;;;N;;;;; 11699;TAKRI LETTER TA;Lo;0;L;;;;;N;;;;; 1169A;TAKRI LETTER THA;Lo;0;L;;;;;N;;;;; 1169B;TAKRI LETTER DA;Lo;0;L;;;;;N;;;;; 1169C;TAKRI LETTER DHA;Lo;0;L;;;;;N;;;;; 1169D;TAKRI LETTER NA;Lo;0;L;;;;;N;;;;; 1169E;TAKRI LETTER PA;Lo;0;L;;;;;N;;;;; 1169F;TAKRI LETTER PHA;Lo;0;L;;;;;N;;;;; 116A0;TAKRI LETTER BA;Lo;0;L;;;;;N;;;;; 116A1;TAKRI LETTER BHA;Lo;0;L;;;;;N;;;;; 116A2;TAKRI LETTER MA;Lo;0;L;;;;;N;;;;; 116A3;TAKRI LETTER YA;Lo;0;L;;;;;N;;;;; 116A4;TAKRI LETTER RA;Lo;0;L;;;;;N;;;;; 116A5;TAKRI LETTER LA;Lo;0;L;;;;;N;;;;; 116A6;TAKRI LETTER VA;Lo;0;L;;;;;N;;;;; 116A7;TAKRI LETTER SHA;Lo;0;L;;;;;N;;;;; 116A8;TAKRI LETTER SA;Lo;0;L;;;;;N;;;;; 116A9;TAKRI LETTER HA;Lo;0;L;;;;;N;;;;; 116AA;TAKRI LETTER RRA;Lo;0;L;;;;;N;;;;; 116AB;TAKRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 116AC;TAKRI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 116AD;TAKRI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; 116AE;TAKRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 116AF;TAKRI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 116B0;TAKRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 116B1;TAKRI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 116B2;TAKRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 116B3;TAKRI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 116B4;TAKRI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;; 116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; 116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 116C3;TAKRI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 116C4;TAKRI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 116C5;TAKRI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 116C6;TAKRI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 116C7;TAKRI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 116C8;TAKRI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 116C9;TAKRI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11700;AHOM LETTER KA;Lo;0;L;;;;;N;;;;; 11701;AHOM LETTER KHA;Lo;0;L;;;;;N;;;;; 11702;AHOM LETTER NGA;Lo;0;L;;;;;N;;;;; 11703;AHOM LETTER NA;Lo;0;L;;;;;N;;;;; 11704;AHOM LETTER TA;Lo;0;L;;;;;N;;;;; 11705;AHOM LETTER ALTERNATE TA;Lo;0;L;;;;;N;;;;; 11706;AHOM LETTER PA;Lo;0;L;;;;;N;;;;; 11707;AHOM LETTER PHA;Lo;0;L;;;;;N;;;;; 11708;AHOM LETTER BA;Lo;0;L;;;;;N;;;;; 11709;AHOM LETTER MA;Lo;0;L;;;;;N;;;;; 1170A;AHOM LETTER JA;Lo;0;L;;;;;N;;;;; 1170B;AHOM LETTER CHA;Lo;0;L;;;;;N;;;;; 1170C;AHOM LETTER THA;Lo;0;L;;;;;N;;;;; 1170D;AHOM LETTER RA;Lo;0;L;;;;;N;;;;; 1170E;AHOM LETTER LA;Lo;0;L;;;;;N;;;;; 1170F;AHOM LETTER SA;Lo;0;L;;;;;N;;;;; 11710;AHOM LETTER NYA;Lo;0;L;;;;;N;;;;; 11711;AHOM LETTER HA;Lo;0;L;;;;;N;;;;; 11712;AHOM LETTER A;Lo;0;L;;;;;N;;;;; 11713;AHOM LETTER DA;Lo;0;L;;;;;N;;;;; 11714;AHOM LETTER DHA;Lo;0;L;;;;;N;;;;; 11715;AHOM LETTER GA;Lo;0;L;;;;;N;;;;; 11716;AHOM LETTER ALTERNATE GA;Lo;0;L;;;;;N;;;;; 11717;AHOM LETTER GHA;Lo;0;L;;;;;N;;;;; 11718;AHOM LETTER BHA;Lo;0;L;;;;;N;;;;; 11719;AHOM LETTER JHA;Lo;0;L;;;;;N;;;;; 1171D;AHOM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;; 1171E;AHOM CONSONANT SIGN MEDIAL RA;Mn;0;NSM;;;;;N;;;;; 1171F;AHOM CONSONANT SIGN MEDIAL LIGATING RA;Mn;0;NSM;;;;;N;;;;; 11720;AHOM VOWEL SIGN A;Mc;0;L;;;;;N;;;;; 11721;AHOM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 11722;AHOM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 11723;AHOM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 11724;AHOM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11725;AHOM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 11726;AHOM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 11727;AHOM VOWEL SIGN AW;Mn;0;NSM;;;;;N;;;;; 11728;AHOM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 11729;AHOM VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 1172A;AHOM VOWEL SIGN AM;Mn;0;NSM;;;;;N;;;;; 1172B;AHOM SIGN KILLER;Mn;9;NSM;;;;;N;;;;; 11730;AHOM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11731;AHOM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11732;AHOM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11733;AHOM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 11734;AHOM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 11735;AHOM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 11736;AHOM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 11737;AHOM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11738;AHOM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11739;AHOM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1173A;AHOM NUMBER TEN;No;0;L;;;;10;N;;;;; 1173B;AHOM NUMBER TWENTY;No;0;L;;;;20;N;;;;; 1173C;AHOM SIGN SMALL SECTION;Po;0;L;;;;;N;;;;; 1173D;AHOM SIGN SECTION;Po;0;L;;;;;N;;;;; 1173E;AHOM SIGN RULAI;Po;0;L;;;;;N;;;;; 1173F;AHOM SYMBOL VI;So;0;L;;;;;N;;;;; 118A0;WARANG CITI CAPITAL LETTER NGAA;Lu;0;L;;;;;N;;;;118C0; 118A1;WARANG CITI CAPITAL LETTER A;Lu;0;L;;;;;N;;;;118C1; 118A2;WARANG CITI CAPITAL LETTER WI;Lu;0;L;;;;;N;;;;118C2; 118A3;WARANG CITI CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;118C3; 118A4;WARANG CITI CAPITAL LETTER YA;Lu;0;L;;;;;N;;;;118C4; 118A5;WARANG CITI CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;118C5; 118A6;WARANG CITI CAPITAL LETTER II;Lu;0;L;;;;;N;;;;118C6; 118A7;WARANG CITI CAPITAL LETTER UU;Lu;0;L;;;;;N;;;;118C7; 118A8;WARANG CITI CAPITAL LETTER E;Lu;0;L;;;;;N;;;;118C8; 118A9;WARANG CITI CAPITAL LETTER O;Lu;0;L;;;;;N;;;;118C9; 118AA;WARANG CITI CAPITAL LETTER ANG;Lu;0;L;;;;;N;;;;118CA; 118AB;WARANG CITI CAPITAL LETTER GA;Lu;0;L;;;;;N;;;;118CB; 118AC;WARANG CITI CAPITAL LETTER KO;Lu;0;L;;;;;N;;;;118CC; 118AD;WARANG CITI CAPITAL LETTER ENY;Lu;0;L;;;;;N;;;;118CD; 118AE;WARANG CITI CAPITAL LETTER YUJ;Lu;0;L;;;;;N;;;;118CE; 118AF;WARANG CITI CAPITAL LETTER UC;Lu;0;L;;;;;N;;;;118CF; 118B0;WARANG CITI CAPITAL LETTER ENN;Lu;0;L;;;;;N;;;;118D0; 118B1;WARANG CITI CAPITAL LETTER ODD;Lu;0;L;;;;;N;;;;118D1; 118B2;WARANG CITI CAPITAL LETTER TTE;Lu;0;L;;;;;N;;;;118D2; 118B3;WARANG CITI CAPITAL LETTER NUNG;Lu;0;L;;;;;N;;;;118D3; 118B4;WARANG CITI CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;118D4; 118B5;WARANG CITI CAPITAL LETTER AT;Lu;0;L;;;;;N;;;;118D5; 118B6;WARANG CITI CAPITAL LETTER AM;Lu;0;L;;;;;N;;;;118D6; 118B7;WARANG CITI CAPITAL LETTER BU;Lu;0;L;;;;;N;;;;118D7; 118B8;WARANG CITI CAPITAL LETTER PU;Lu;0;L;;;;;N;;;;118D8; 118B9;WARANG CITI CAPITAL LETTER HIYO;Lu;0;L;;;;;N;;;;118D9; 118BA;WARANG CITI CAPITAL LETTER HOLO;Lu;0;L;;;;;N;;;;118DA; 118BB;WARANG CITI CAPITAL LETTER HORR;Lu;0;L;;;;;N;;;;118DB; 118BC;WARANG CITI CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;118DC; 118BD;WARANG CITI CAPITAL LETTER SSUU;Lu;0;L;;;;;N;;;;118DD; 118BE;WARANG CITI CAPITAL LETTER SII;Lu;0;L;;;;;N;;;;118DE; 118BF;WARANG CITI CAPITAL LETTER VIYO;Lu;0;L;;;;;N;;;;118DF; 118C0;WARANG CITI SMALL LETTER NGAA;Ll;0;L;;;;;N;;;118A0;;118A0 118C1;WARANG CITI SMALL LETTER A;Ll;0;L;;;;;N;;;118A1;;118A1 118C2;WARANG CITI SMALL LETTER WI;Ll;0;L;;;;;N;;;118A2;;118A2 118C3;WARANG CITI SMALL LETTER YU;Ll;0;L;;;;;N;;;118A3;;118A3 118C4;WARANG CITI SMALL LETTER YA;Ll;0;L;;;;;N;;;118A4;;118A4 118C5;WARANG CITI SMALL LETTER YO;Ll;0;L;;;;;N;;;118A5;;118A5 118C6;WARANG CITI SMALL LETTER II;Ll;0;L;;;;;N;;;118A6;;118A6 118C7;WARANG CITI SMALL LETTER UU;Ll;0;L;;;;;N;;;118A7;;118A7 118C8;WARANG CITI SMALL LETTER E;Ll;0;L;;;;;N;;;118A8;;118A8 118C9;WARANG CITI SMALL LETTER O;Ll;0;L;;;;;N;;;118A9;;118A9 118CA;WARANG CITI SMALL LETTER ANG;Ll;0;L;;;;;N;;;118AA;;118AA 118CB;WARANG CITI SMALL LETTER GA;Ll;0;L;;;;;N;;;118AB;;118AB 118CC;WARANG CITI SMALL LETTER KO;Ll;0;L;;;;;N;;;118AC;;118AC 118CD;WARANG CITI SMALL LETTER ENY;Ll;0;L;;;;;N;;;118AD;;118AD 118CE;WARANG CITI SMALL LETTER YUJ;Ll;0;L;;;;;N;;;118AE;;118AE 118CF;WARANG CITI SMALL LETTER UC;Ll;0;L;;;;;N;;;118AF;;118AF 118D0;WARANG CITI SMALL LETTER ENN;Ll;0;L;;;;;N;;;118B0;;118B0 118D1;WARANG CITI SMALL LETTER ODD;Ll;0;L;;;;;N;;;118B1;;118B1 118D2;WARANG CITI SMALL LETTER TTE;Ll;0;L;;;;;N;;;118B2;;118B2 118D3;WARANG CITI SMALL LETTER NUNG;Ll;0;L;;;;;N;;;118B3;;118B3 118D4;WARANG CITI SMALL LETTER DA;Ll;0;L;;;;;N;;;118B4;;118B4 118D5;WARANG CITI SMALL LETTER AT;Ll;0;L;;;;;N;;;118B5;;118B5 118D6;WARANG CITI SMALL LETTER AM;Ll;0;L;;;;;N;;;118B6;;118B6 118D7;WARANG CITI SMALL LETTER BU;Ll;0;L;;;;;N;;;118B7;;118B7 118D8;WARANG CITI SMALL LETTER PU;Ll;0;L;;;;;N;;;118B8;;118B8 118D9;WARANG CITI SMALL LETTER HIYO;Ll;0;L;;;;;N;;;118B9;;118B9 118DA;WARANG CITI SMALL LETTER HOLO;Ll;0;L;;;;;N;;;118BA;;118BA 118DB;WARANG CITI SMALL LETTER HORR;Ll;0;L;;;;;N;;;118BB;;118BB 118DC;WARANG CITI SMALL LETTER HAR;Ll;0;L;;;;;N;;;118BC;;118BC 118DD;WARANG CITI SMALL LETTER SSUU;Ll;0;L;;;;;N;;;118BD;;118BD 118DE;WARANG CITI SMALL LETTER SII;Ll;0;L;;;;;N;;;118BE;;118BE 118DF;WARANG CITI SMALL LETTER VIYO;Ll;0;L;;;;;N;;;118BF;;118BF 118E0;WARANG CITI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 118E1;WARANG CITI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 118E2;WARANG CITI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 118E3;WARANG CITI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 118E4;WARANG CITI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 118E5;WARANG CITI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 118E6;WARANG CITI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 118E7;WARANG CITI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 118E8;WARANG CITI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 118E9;WARANG CITI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 118EA;WARANG CITI NUMBER TEN;No;0;L;;;;10;N;;;;; 118EB;WARANG CITI NUMBER TWENTY;No;0;L;;;;20;N;;;;; 118EC;WARANG CITI NUMBER THIRTY;No;0;L;;;;30;N;;;;; 118ED;WARANG CITI NUMBER FORTY;No;0;L;;;;40;N;;;;; 118EE;WARANG CITI NUMBER FIFTY;No;0;L;;;;50;N;;;;; 118EF;WARANG CITI NUMBER SIXTY;No;0;L;;;;60;N;;;;; 118F0;WARANG CITI NUMBER SEVENTY;No;0;L;;;;70;N;;;;; 118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;; 118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;; 118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;; 11AC0;PAU CIN HAU LETTER PA;Lo;0;L;;;;;N;;;;; 11AC1;PAU CIN HAU LETTER KA;Lo;0;L;;;;;N;;;;; 11AC2;PAU CIN HAU LETTER LA;Lo;0;L;;;;;N;;;;; 11AC3;PAU CIN HAU LETTER MA;Lo;0;L;;;;;N;;;;; 11AC4;PAU CIN HAU LETTER DA;Lo;0;L;;;;;N;;;;; 11AC5;PAU CIN HAU LETTER ZA;Lo;0;L;;;;;N;;;;; 11AC6;PAU CIN HAU LETTER VA;Lo;0;L;;;;;N;;;;; 11AC7;PAU CIN HAU LETTER NGA;Lo;0;L;;;;;N;;;;; 11AC8;PAU CIN HAU LETTER HA;Lo;0;L;;;;;N;;;;; 11AC9;PAU CIN HAU LETTER GA;Lo;0;L;;;;;N;;;;; 11ACA;PAU CIN HAU LETTER KHA;Lo;0;L;;;;;N;;;;; 11ACB;PAU CIN HAU LETTER SA;Lo;0;L;;;;;N;;;;; 11ACC;PAU CIN HAU LETTER BA;Lo;0;L;;;;;N;;;;; 11ACD;PAU CIN HAU LETTER CA;Lo;0;L;;;;;N;;;;; 11ACE;PAU CIN HAU LETTER TA;Lo;0;L;;;;;N;;;;; 11ACF;PAU CIN HAU LETTER THA;Lo;0;L;;;;;N;;;;; 11AD0;PAU CIN HAU LETTER NA;Lo;0;L;;;;;N;;;;; 11AD1;PAU CIN HAU LETTER PHA;Lo;0;L;;;;;N;;;;; 11AD2;PAU CIN HAU LETTER RA;Lo;0;L;;;;;N;;;;; 11AD3;PAU CIN HAU LETTER FA;Lo;0;L;;;;;N;;;;; 11AD4;PAU CIN HAU LETTER CHA;Lo;0;L;;;;;N;;;;; 11AD5;PAU CIN HAU LETTER A;Lo;0;L;;;;;N;;;;; 11AD6;PAU CIN HAU LETTER E;Lo;0;L;;;;;N;;;;; 11AD7;PAU CIN HAU LETTER I;Lo;0;L;;;;;N;;;;; 11AD8;PAU CIN HAU LETTER O;Lo;0;L;;;;;N;;;;; 11AD9;PAU CIN HAU LETTER U;Lo;0;L;;;;;N;;;;; 11ADA;PAU CIN HAU LETTER UA;Lo;0;L;;;;;N;;;;; 11ADB;PAU CIN HAU LETTER IA;Lo;0;L;;;;;N;;;;; 11ADC;PAU CIN HAU LETTER FINAL P;Lo;0;L;;;;;N;;;;; 11ADD;PAU CIN HAU LETTER FINAL K;Lo;0;L;;;;;N;;;;; 11ADE;PAU CIN HAU LETTER FINAL T;Lo;0;L;;;;;N;;;;; 11ADF;PAU CIN HAU LETTER FINAL M;Lo;0;L;;;;;N;;;;; 11AE0;PAU CIN HAU LETTER FINAL N;Lo;0;L;;;;;N;;;;; 11AE1;PAU CIN HAU LETTER FINAL L;Lo;0;L;;;;;N;;;;; 11AE2;PAU CIN HAU LETTER FINAL W;Lo;0;L;;;;;N;;;;; 11AE3;PAU CIN HAU LETTER FINAL NG;Lo;0;L;;;;;N;;;;; 11AE4;PAU CIN HAU LETTER FINAL Y;Lo;0;L;;;;;N;;;;; 11AE5;PAU CIN HAU RISING TONE LONG;Lo;0;L;;;;;N;;;;; 11AE6;PAU CIN HAU RISING TONE;Lo;0;L;;;;;N;;;;; 11AE7;PAU CIN HAU SANDHI GLOTTAL STOP;Lo;0;L;;;;;N;;;;; 11AE8;PAU CIN HAU RISING TONE LONG FINAL;Lo;0;L;;;;;N;;;;; 11AE9;PAU CIN HAU RISING TONE FINAL;Lo;0;L;;;;;N;;;;; 11AEA;PAU CIN HAU SANDHI GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;; 11AEB;PAU CIN HAU SANDHI TONE LONG;Lo;0;L;;;;;N;;;;; 11AEC;PAU CIN HAU SANDHI TONE;Lo;0;L;;;;;N;;;;; 11AED;PAU CIN HAU SANDHI TONE LONG FINAL;Lo;0;L;;;;;N;;;;; 11AEE;PAU CIN HAU SANDHI TONE FINAL;Lo;0;L;;;;;N;;;;; 11AEF;PAU CIN HAU MID-LEVEL TONE;Lo;0;L;;;;;N;;;;; 11AF0;PAU CIN HAU GLOTTAL STOP VARIANT;Lo;0;L;;;;;N;;;;; 11AF1;PAU CIN HAU MID-LEVEL TONE LONG FINAL;Lo;0;L;;;;;N;;;;; 11AF2;PAU CIN HAU MID-LEVEL TONE FINAL;Lo;0;L;;;;;N;;;;; 11AF3;PAU CIN HAU LOW-FALLING TONE LONG;Lo;0;L;;;;;N;;;;; 11AF4;PAU CIN HAU LOW-FALLING TONE;Lo;0;L;;;;;N;;;;; 11AF5;PAU CIN HAU GLOTTAL STOP;Lo;0;L;;;;;N;;;;; 11AF6;PAU CIN HAU LOW-FALLING TONE LONG FINAL;Lo;0;L;;;;;N;;;;; 11AF7;PAU CIN HAU LOW-FALLING TONE FINAL;Lo;0;L;;;;;N;;;;; 11AF8;PAU CIN HAU GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;; 11C00;BHAIKSUKI LETTER A;Lo;0;L;;;;;N;;;;; 11C01;BHAIKSUKI LETTER AA;Lo;0;L;;;;;N;;;;; 11C02;BHAIKSUKI LETTER I;Lo;0;L;;;;;N;;;;; 11C03;BHAIKSUKI LETTER II;Lo;0;L;;;;;N;;;;; 11C04;BHAIKSUKI LETTER U;Lo;0;L;;;;;N;;;;; 11C05;BHAIKSUKI LETTER UU;Lo;0;L;;;;;N;;;;; 11C06;BHAIKSUKI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; 11C07;BHAIKSUKI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 11C08;BHAIKSUKI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; 11C0A;BHAIKSUKI LETTER E;Lo;0;L;;;;;N;;;;; 11C0B;BHAIKSUKI LETTER AI;Lo;0;L;;;;;N;;;;; 11C0C;BHAIKSUKI LETTER O;Lo;0;L;;;;;N;;;;; 11C0D;BHAIKSUKI LETTER AU;Lo;0;L;;;;;N;;;;; 11C0E;BHAIKSUKI LETTER KA;Lo;0;L;;;;;N;;;;; 11C0F;BHAIKSUKI LETTER KHA;Lo;0;L;;;;;N;;;;; 11C10;BHAIKSUKI LETTER GA;Lo;0;L;;;;;N;;;;; 11C11;BHAIKSUKI LETTER GHA;Lo;0;L;;;;;N;;;;; 11C12;BHAIKSUKI LETTER NGA;Lo;0;L;;;;;N;;;;; 11C13;BHAIKSUKI LETTER CA;Lo;0;L;;;;;N;;;;; 11C14;BHAIKSUKI LETTER CHA;Lo;0;L;;;;;N;;;;; 11C15;BHAIKSUKI LETTER JA;Lo;0;L;;;;;N;;;;; 11C16;BHAIKSUKI LETTER JHA;Lo;0;L;;;;;N;;;;; 11C17;BHAIKSUKI LETTER NYA;Lo;0;L;;;;;N;;;;; 11C18;BHAIKSUKI LETTER TTA;Lo;0;L;;;;;N;;;;; 11C19;BHAIKSUKI LETTER TTHA;Lo;0;L;;;;;N;;;;; 11C1A;BHAIKSUKI LETTER DDA;Lo;0;L;;;;;N;;;;; 11C1B;BHAIKSUKI LETTER DDHA;Lo;0;L;;;;;N;;;;; 11C1C;BHAIKSUKI LETTER NNA;Lo;0;L;;;;;N;;;;; 11C1D;BHAIKSUKI LETTER TA;Lo;0;L;;;;;N;;;;; 11C1E;BHAIKSUKI LETTER THA;Lo;0;L;;;;;N;;;;; 11C1F;BHAIKSUKI LETTER DA;Lo;0;L;;;;;N;;;;; 11C20;BHAIKSUKI LETTER DHA;Lo;0;L;;;;;N;;;;; 11C21;BHAIKSUKI LETTER NA;Lo;0;L;;;;;N;;;;; 11C22;BHAIKSUKI LETTER PA;Lo;0;L;;;;;N;;;;; 11C23;BHAIKSUKI LETTER PHA;Lo;0;L;;;;;N;;;;; 11C24;BHAIKSUKI LETTER BA;Lo;0;L;;;;;N;;;;; 11C25;BHAIKSUKI LETTER BHA;Lo;0;L;;;;;N;;;;; 11C26;BHAIKSUKI LETTER MA;Lo;0;L;;;;;N;;;;; 11C27;BHAIKSUKI LETTER YA;Lo;0;L;;;;;N;;;;; 11C28;BHAIKSUKI LETTER RA;Lo;0;L;;;;;N;;;;; 11C29;BHAIKSUKI LETTER LA;Lo;0;L;;;;;N;;;;; 11C2A;BHAIKSUKI LETTER VA;Lo;0;L;;;;;N;;;;; 11C2B;BHAIKSUKI LETTER SHA;Lo;0;L;;;;;N;;;;; 11C2C;BHAIKSUKI LETTER SSA;Lo;0;L;;;;;N;;;;; 11C2D;BHAIKSUKI LETTER SA;Lo;0;L;;;;;N;;;;; 11C2E;BHAIKSUKI LETTER HA;Lo;0;L;;;;;N;;;;; 11C2F;BHAIKSUKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 11C30;BHAIKSUKI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 11C31;BHAIKSUKI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; 11C32;BHAIKSUKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11C33;BHAIKSUKI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; 11C34;BHAIKSUKI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; 11C35;BHAIKSUKI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; 11C36;BHAIKSUKI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; 11C38;BHAIKSUKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 11C39;BHAIKSUKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; 11C3A;BHAIKSUKI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; 11C3B;BHAIKSUKI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; 11C3C;BHAIKSUKI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 11C3D;BHAIKSUKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11C3E;BHAIKSUKI SIGN VISARGA;Mc;0;L;;;;;N;;;;; 11C3F;BHAIKSUKI SIGN VIRAMA;Mn;9;L;;;;;N;;;;; 11C40;BHAIKSUKI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; 11C41;BHAIKSUKI DANDA;Po;0;L;;;;;N;;;;; 11C42;BHAIKSUKI DOUBLE DANDA;Po;0;L;;;;;N;;;;; 11C43;BHAIKSUKI WORD SEPARATOR;Po;0;L;;;;;N;;;;; 11C44;BHAIKSUKI GAP FILLER-1;Po;0;L;;;;;N;;;;; 11C45;BHAIKSUKI GAP FILLER-2;Po;0;L;;;;;N;;;;; 11C50;BHAIKSUKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 11C51;BHAIKSUKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 11C52;BHAIKSUKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 11C53;BHAIKSUKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 11C54;BHAIKSUKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 11C55;BHAIKSUKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 11C56;BHAIKSUKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 11C57;BHAIKSUKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11C58;BHAIKSUKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11C59;BHAIKSUKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11C5A;BHAIKSUKI NUMBER ONE;No;0;L;;;;1;N;;;;; 11C5B;BHAIKSUKI NUMBER TWO;No;0;L;;;;2;N;;;;; 11C5C;BHAIKSUKI NUMBER THREE;No;0;L;;;;3;N;;;;; 11C5D;BHAIKSUKI NUMBER FOUR;No;0;L;;;;4;N;;;;; 11C5E;BHAIKSUKI NUMBER FIVE;No;0;L;;;;5;N;;;;; 11C5F;BHAIKSUKI NUMBER SIX;No;0;L;;;;6;N;;;;; 11C60;BHAIKSUKI NUMBER SEVEN;No;0;L;;;;7;N;;;;; 11C61;BHAIKSUKI NUMBER EIGHT;No;0;L;;;;8;N;;;;; 11C62;BHAIKSUKI NUMBER NINE;No;0;L;;;;9;N;;;;; 11C63;BHAIKSUKI NUMBER TEN;No;0;L;;;;10;N;;;;; 11C64;BHAIKSUKI NUMBER TWENTY;No;0;L;;;;20;N;;;;; 11C65;BHAIKSUKI NUMBER THIRTY;No;0;L;;;;30;N;;;;; 11C66;BHAIKSUKI NUMBER FORTY;No;0;L;;;;40;N;;;;; 11C67;BHAIKSUKI NUMBER FIFTY;No;0;L;;;;50;N;;;;; 11C68;BHAIKSUKI NUMBER SIXTY;No;0;L;;;;60;N;;;;; 11C69;BHAIKSUKI NUMBER SEVENTY;No;0;L;;;;70;N;;;;; 11C6A;BHAIKSUKI NUMBER EIGHTY;No;0;L;;;;80;N;;;;; 11C6B;BHAIKSUKI NUMBER NINETY;No;0;L;;;;90;N;;;;; 11C6C;BHAIKSUKI HUNDREDS UNIT MARK;No;0;L;;;;100;N;;;;; 11C70;MARCHEN HEAD MARK;Po;0;L;;;;;N;;;;; 11C71;MARCHEN MARK SHAD;Po;0;L;;;;;N;;;;; 11C72;MARCHEN LETTER KA;Lo;0;L;;;;;N;;;;; 11C73;MARCHEN LETTER KHA;Lo;0;L;;;;;N;;;;; 11C74;MARCHEN LETTER GA;Lo;0;L;;;;;N;;;;; 11C75;MARCHEN LETTER NGA;Lo;0;L;;;;;N;;;;; 11C76;MARCHEN LETTER CA;Lo;0;L;;;;;N;;;;; 11C77;MARCHEN LETTER CHA;Lo;0;L;;;;;N;;;;; 11C78;MARCHEN LETTER JA;Lo;0;L;;;;;N;;;;; 11C79;MARCHEN LETTER NYA;Lo;0;L;;;;;N;;;;; 11C7A;MARCHEN LETTER TA;Lo;0;L;;;;;N;;;;; 11C7B;MARCHEN LETTER THA;Lo;0;L;;;;;N;;;;; 11C7C;MARCHEN LETTER DA;Lo;0;L;;;;;N;;;;; 11C7D;MARCHEN LETTER NA;Lo;0;L;;;;;N;;;;; 11C7E;MARCHEN LETTER PA;Lo;0;L;;;;;N;;;;; 11C7F;MARCHEN LETTER PHA;Lo;0;L;;;;;N;;;;; 11C80;MARCHEN LETTER BA;Lo;0;L;;;;;N;;;;; 11C81;MARCHEN LETTER MA;Lo;0;L;;;;;N;;;;; 11C82;MARCHEN LETTER TSA;Lo;0;L;;;;;N;;;;; 11C83;MARCHEN LETTER TSHA;Lo;0;L;;;;;N;;;;; 11C84;MARCHEN LETTER DZA;Lo;0;L;;;;;N;;;;; 11C85;MARCHEN LETTER WA;Lo;0;L;;;;;N;;;;; 11C86;MARCHEN LETTER ZHA;Lo;0;L;;;;;N;;;;; 11C87;MARCHEN LETTER ZA;Lo;0;L;;;;;N;;;;; 11C88;MARCHEN LETTER -A;Lo;0;L;;;;;N;;;;; 11C89;MARCHEN LETTER YA;Lo;0;L;;;;;N;;;;; 11C8A;MARCHEN LETTER RA;Lo;0;L;;;;;N;;;;; 11C8B;MARCHEN LETTER LA;Lo;0;L;;;;;N;;;;; 11C8C;MARCHEN LETTER SHA;Lo;0;L;;;;;N;;;;; 11C8D;MARCHEN LETTER SA;Lo;0;L;;;;;N;;;;; 11C8E;MARCHEN LETTER HA;Lo;0;L;;;;;N;;;;; 11C8F;MARCHEN LETTER A;Lo;0;L;;;;;N;;;;; 11C92;MARCHEN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;; 11C93;MARCHEN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;; 11C94;MARCHEN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;; 11C95;MARCHEN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;; 11C96;MARCHEN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;; 11C97;MARCHEN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;; 11C98;MARCHEN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;; 11C99;MARCHEN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;; 11C9A;MARCHEN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;; 11C9B;MARCHEN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;; 11C9C;MARCHEN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;; 11C9D;MARCHEN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;; 11C9E;MARCHEN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;; 11C9F;MARCHEN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;; 11CA0;MARCHEN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;; 11CA1;MARCHEN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;; 11CA2;MARCHEN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;; 11CA3;MARCHEN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;; 11CA4;MARCHEN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;; 11CA5;MARCHEN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;; 11CA6;MARCHEN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;; 11CA7;MARCHEN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;; 11CA9;MARCHEN SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; 11CAA;MARCHEN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;; 11CAB;MARCHEN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;; 11CAC;MARCHEN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;; 11CAD;MARCHEN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;; 11CAE;MARCHEN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;; 11CAF;MARCHEN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;; 11CB0;MARCHEN VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; 11CB1;MARCHEN VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 11CB2;MARCHEN VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; 11CB3;MARCHEN VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; 11CB4;MARCHEN VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 11CB5;MARCHEN SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; 11CB6;MARCHEN SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;; 12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;; 12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;; 12003;CUNEIFORM SIGN A TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 12004;CUNEIFORM SIGN A TIMES HA;Lo;0;L;;;;;N;;;;; 12005;CUNEIFORM SIGN A TIMES IGI;Lo;0;L;;;;;N;;;;; 12006;CUNEIFORM SIGN A TIMES LAGAR GUNU;Lo;0;L;;;;;N;;;;; 12007;CUNEIFORM SIGN A TIMES MUSH;Lo;0;L;;;;;N;;;;; 12008;CUNEIFORM SIGN A TIMES SAG;Lo;0;L;;;;;N;;;;; 12009;CUNEIFORM SIGN A2;Lo;0;L;;;;;N;;;;; 1200A;CUNEIFORM SIGN AB;Lo;0;L;;;;;N;;;;; 1200B;CUNEIFORM SIGN AB TIMES ASH2;Lo;0;L;;;;;N;;;;; 1200C;CUNEIFORM SIGN AB TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;; 1200D;CUNEIFORM SIGN AB TIMES GAL;Lo;0;L;;;;;N;;;;; 1200E;CUNEIFORM SIGN AB TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1200F;CUNEIFORM SIGN AB TIMES HA;Lo;0;L;;;;;N;;;;; 12010;CUNEIFORM SIGN AB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12011;CUNEIFORM SIGN AB TIMES IMIN;Lo;0;L;;;;;N;;;;; 12012;CUNEIFORM SIGN AB TIMES LAGAB;Lo;0;L;;;;;N;;;;; 12013;CUNEIFORM SIGN AB TIMES SHESH;Lo;0;L;;;;;N;;;;; 12014;CUNEIFORM SIGN AB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; 12015;CUNEIFORM SIGN AB GUNU;Lo;0;L;;;;;N;;;;; 12016;CUNEIFORM SIGN AB2;Lo;0;L;;;;;N;;;;; 12017;CUNEIFORM SIGN AB2 TIMES BALAG;Lo;0;L;;;;;N;;;;; 12018;CUNEIFORM SIGN AB2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 12019;CUNEIFORM SIGN AB2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; 1201A;CUNEIFORM SIGN AB2 TIMES SHA3;Lo;0;L;;;;;N;;;;; 1201B;CUNEIFORM SIGN AB2 TIMES TAK4;Lo;0;L;;;;;N;;;;; 1201C;CUNEIFORM SIGN AD;Lo;0;L;;;;;N;;;;; 1201D;CUNEIFORM SIGN AK;Lo;0;L;;;;;N;;;;; 1201E;CUNEIFORM SIGN AK TIMES ERIN2;Lo;0;L;;;;;N;;;;; 1201F;CUNEIFORM SIGN AK TIMES SHITA PLUS GISH;Lo;0;L;;;;;N;;;;; 12020;CUNEIFORM SIGN AL;Lo;0;L;;;;;N;;;;; 12021;CUNEIFORM SIGN AL TIMES AL;Lo;0;L;;;;;N;;;;; 12022;CUNEIFORM SIGN AL TIMES DIM2;Lo;0;L;;;;;N;;;;; 12023;CUNEIFORM SIGN AL TIMES GISH;Lo;0;L;;;;;N;;;;; 12024;CUNEIFORM SIGN AL TIMES HA;Lo;0;L;;;;;N;;;;; 12025;CUNEIFORM SIGN AL TIMES KAD3;Lo;0;L;;;;;N;;;;; 12026;CUNEIFORM SIGN AL TIMES KI;Lo;0;L;;;;;N;;;;; 12027;CUNEIFORM SIGN AL TIMES SHE;Lo;0;L;;;;;N;;;;; 12028;CUNEIFORM SIGN AL TIMES USH;Lo;0;L;;;;;N;;;;; 12029;CUNEIFORM SIGN ALAN;Lo;0;L;;;;;N;;;;; 1202A;CUNEIFORM SIGN ALEPH;Lo;0;L;;;;;N;;;;; 1202B;CUNEIFORM SIGN AMAR;Lo;0;L;;;;;N;;;;; 1202C;CUNEIFORM SIGN AMAR TIMES SHE;Lo;0;L;;;;;N;;;;; 1202D;CUNEIFORM SIGN AN;Lo;0;L;;;;;N;;;;; 1202E;CUNEIFORM SIGN AN OVER AN;Lo;0;L;;;;;N;;;;; 1202F;CUNEIFORM SIGN AN THREE TIMES;Lo;0;L;;;;;N;;;;; 12030;CUNEIFORM SIGN AN PLUS NAGA OPPOSING AN PLUS NAGA;Lo;0;L;;;;;N;;;;; 12031;CUNEIFORM SIGN AN PLUS NAGA SQUARED;Lo;0;L;;;;;N;;;;; 12032;CUNEIFORM SIGN ANSHE;Lo;0;L;;;;;N;;;;; 12033;CUNEIFORM SIGN APIN;Lo;0;L;;;;;N;;;;; 12034;CUNEIFORM SIGN ARAD;Lo;0;L;;;;;N;;;;; 12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;; 12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;; 12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;; 12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;; 12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; 1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;; 1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;; 1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; 1203D;CUNEIFORM SIGN ASH OVER ASH OVER ASH CROSSING ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; 1203E;CUNEIFORM SIGN ASH2;Lo;0;L;;;;;N;;;;; 1203F;CUNEIFORM SIGN ASHGAB;Lo;0;L;;;;;N;;;;; 12040;CUNEIFORM SIGN BA;Lo;0;L;;;;;N;;;;; 12041;CUNEIFORM SIGN BAD;Lo;0;L;;;;;N;;;;; 12042;CUNEIFORM SIGN BAG3;Lo;0;L;;;;;N;;;;; 12043;CUNEIFORM SIGN BAHAR2;Lo;0;L;;;;;N;;;;; 12044;CUNEIFORM SIGN BAL;Lo;0;L;;;;;N;;;;; 12045;CUNEIFORM SIGN BAL OVER BAL;Lo;0;L;;;;;N;;;;; 12046;CUNEIFORM SIGN BALAG;Lo;0;L;;;;;N;;;;; 12047;CUNEIFORM SIGN BAR;Lo;0;L;;;;;N;;;;; 12048;CUNEIFORM SIGN BARA2;Lo;0;L;;;;;N;;;;; 12049;CUNEIFORM SIGN BI;Lo;0;L;;;;;N;;;;; 1204A;CUNEIFORM SIGN BI TIMES A;Lo;0;L;;;;;N;;;;; 1204B;CUNEIFORM SIGN BI TIMES GAR;Lo;0;L;;;;;N;;;;; 1204C;CUNEIFORM SIGN BI TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 1204D;CUNEIFORM SIGN BU;Lo;0;L;;;;;N;;;;; 1204E;CUNEIFORM SIGN BU OVER BU AB;Lo;0;L;;;;;N;;;;; 1204F;CUNEIFORM SIGN BU OVER BU UN;Lo;0;L;;;;;N;;;;; 12050;CUNEIFORM SIGN BU CROSSING BU;Lo;0;L;;;;;N;;;;; 12051;CUNEIFORM SIGN BULUG;Lo;0;L;;;;;N;;;;; 12052;CUNEIFORM SIGN BULUG OVER BULUG;Lo;0;L;;;;;N;;;;; 12053;CUNEIFORM SIGN BUR;Lo;0;L;;;;;N;;;;; 12054;CUNEIFORM SIGN BUR2;Lo;0;L;;;;;N;;;;; 12055;CUNEIFORM SIGN DA;Lo;0;L;;;;;N;;;;; 12056;CUNEIFORM SIGN DAG;Lo;0;L;;;;;N;;;;; 12057;CUNEIFORM SIGN DAG KISIM5 TIMES A PLUS MASH;Lo;0;L;;;;;N;;;;; 12058;CUNEIFORM SIGN DAG KISIM5 TIMES AMAR;Lo;0;L;;;;;N;;;;; 12059;CUNEIFORM SIGN DAG KISIM5 TIMES BALAG;Lo;0;L;;;;;N;;;;; 1205A;CUNEIFORM SIGN DAG KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;; 1205B;CUNEIFORM SIGN DAG KISIM5 TIMES GA;Lo;0;L;;;;;N;;;;; 1205C;CUNEIFORM SIGN DAG KISIM5 TIMES GA PLUS MASH;Lo;0;L;;;;;N;;;;; 1205D;CUNEIFORM SIGN DAG KISIM5 TIMES GI;Lo;0;L;;;;;N;;;;; 1205E;CUNEIFORM SIGN DAG KISIM5 TIMES GIR2;Lo;0;L;;;;;N;;;;; 1205F;CUNEIFORM SIGN DAG KISIM5 TIMES GUD;Lo;0;L;;;;;N;;;;; 12060;CUNEIFORM SIGN DAG KISIM5 TIMES HA;Lo;0;L;;;;;N;;;;; 12061;CUNEIFORM SIGN DAG KISIM5 TIMES IR;Lo;0;L;;;;;N;;;;; 12062;CUNEIFORM SIGN DAG KISIM5 TIMES IR PLUS LU;Lo;0;L;;;;;N;;;;; 12063;CUNEIFORM SIGN DAG KISIM5 TIMES KAK;Lo;0;L;;;;;N;;;;; 12064;CUNEIFORM SIGN DAG KISIM5 TIMES LA;Lo;0;L;;;;;N;;;;; 12065;CUNEIFORM SIGN DAG KISIM5 TIMES LU;Lo;0;L;;;;;N;;;;; 12066;CUNEIFORM SIGN DAG KISIM5 TIMES LU PLUS MASH2;Lo;0;L;;;;;N;;;;; 12067;CUNEIFORM SIGN DAG KISIM5 TIMES LUM;Lo;0;L;;;;;N;;;;; 12068;CUNEIFORM SIGN DAG KISIM5 TIMES NE;Lo;0;L;;;;;N;;;;; 12069;CUNEIFORM SIGN DAG KISIM5 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;; 1206A;CUNEIFORM SIGN DAG KISIM5 TIMES SI;Lo;0;L;;;;;N;;;;; 1206B;CUNEIFORM SIGN DAG KISIM5 TIMES TAK4;Lo;0;L;;;;;N;;;;; 1206C;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS GIR2;Lo;0;L;;;;;N;;;;; 1206D;CUNEIFORM SIGN DAG KISIM5 TIMES USH;Lo;0;L;;;;;N;;;;; 1206E;CUNEIFORM SIGN DAM;Lo;0;L;;;;;N;;;;; 1206F;CUNEIFORM SIGN DAR;Lo;0;L;;;;;N;;;;; 12070;CUNEIFORM SIGN DARA3;Lo;0;L;;;;;N;;;;; 12071;CUNEIFORM SIGN DARA4;Lo;0;L;;;;;N;;;;; 12072;CUNEIFORM SIGN DI;Lo;0;L;;;;;N;;;;; 12073;CUNEIFORM SIGN DIB;Lo;0;L;;;;;N;;;;; 12074;CUNEIFORM SIGN DIM;Lo;0;L;;;;;N;;;;; 12075;CUNEIFORM SIGN DIM TIMES SHE;Lo;0;L;;;;;N;;;;; 12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;; 12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;; 12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;; 12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;; 1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;; 1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;; 1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;; 1207D;CUNEIFORM SIGN DU SHESHIG;Lo;0;L;;;;;N;;;;; 1207E;CUNEIFORM SIGN DUB;Lo;0;L;;;;;N;;;;; 1207F;CUNEIFORM SIGN DUB TIMES ESH2;Lo;0;L;;;;;N;;;;; 12080;CUNEIFORM SIGN DUB2;Lo;0;L;;;;;N;;;;; 12081;CUNEIFORM SIGN DUG;Lo;0;L;;;;;N;;;;; 12082;CUNEIFORM SIGN DUGUD;Lo;0;L;;;;;N;;;;; 12083;CUNEIFORM SIGN DUH;Lo;0;L;;;;;N;;;;; 12084;CUNEIFORM SIGN DUN;Lo;0;L;;;;;N;;;;; 12085;CUNEIFORM SIGN DUN3;Lo;0;L;;;;;N;;;;; 12086;CUNEIFORM SIGN DUN3 GUNU;Lo;0;L;;;;;N;;;;; 12087;CUNEIFORM SIGN DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; 12088;CUNEIFORM SIGN DUN4;Lo;0;L;;;;;N;;;;; 12089;CUNEIFORM SIGN DUR2;Lo;0;L;;;;;N;;;;; 1208A;CUNEIFORM SIGN E;Lo;0;L;;;;;N;;;;; 1208B;CUNEIFORM SIGN E TIMES PAP;Lo;0;L;;;;;N;;;;; 1208C;CUNEIFORM SIGN E OVER E NUN OVER NUN;Lo;0;L;;;;;N;;;;; 1208D;CUNEIFORM SIGN E2;Lo;0;L;;;;;N;;;;; 1208E;CUNEIFORM SIGN E2 TIMES A PLUS HA PLUS DA;Lo;0;L;;;;;N;;;;; 1208F;CUNEIFORM SIGN E2 TIMES GAR;Lo;0;L;;;;;N;;;;; 12090;CUNEIFORM SIGN E2 TIMES MI;Lo;0;L;;;;;N;;;;; 12091;CUNEIFORM SIGN E2 TIMES SAL;Lo;0;L;;;;;N;;;;; 12092;CUNEIFORM SIGN E2 TIMES SHE;Lo;0;L;;;;;N;;;;; 12093;CUNEIFORM SIGN E2 TIMES U;Lo;0;L;;;;;N;;;;; 12094;CUNEIFORM SIGN EDIN;Lo;0;L;;;;;N;;;;; 12095;CUNEIFORM SIGN EGIR;Lo;0;L;;;;;N;;;;; 12096;CUNEIFORM SIGN EL;Lo;0;L;;;;;N;;;;; 12097;CUNEIFORM SIGN EN;Lo;0;L;;;;;N;;;;; 12098;CUNEIFORM SIGN EN TIMES GAN2;Lo;0;L;;;;;N;;;;; 12099;CUNEIFORM SIGN EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1209A;CUNEIFORM SIGN EN TIMES ME;Lo;0;L;;;;;N;;;;; 1209B;CUNEIFORM SIGN EN CROSSING EN;Lo;0;L;;;;;N;;;;; 1209C;CUNEIFORM SIGN EN OPPOSING EN;Lo;0;L;;;;;N;;;;; 1209D;CUNEIFORM SIGN EN SQUARED;Lo;0;L;;;;;N;;;;; 1209E;CUNEIFORM SIGN EREN;Lo;0;L;;;;;N;;;;; 1209F;CUNEIFORM SIGN ERIN2;Lo;0;L;;;;;N;;;;; 120A0;CUNEIFORM SIGN ESH2;Lo;0;L;;;;;N;;;;; 120A1;CUNEIFORM SIGN EZEN;Lo;0;L;;;;;N;;;;; 120A2;CUNEIFORM SIGN EZEN TIMES A;Lo;0;L;;;;;N;;;;; 120A3;CUNEIFORM SIGN EZEN TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;; 120A4;CUNEIFORM SIGN EZEN TIMES A PLUS LAL TIMES LAL;Lo;0;L;;;;;N;;;;; 120A5;CUNEIFORM SIGN EZEN TIMES AN;Lo;0;L;;;;;N;;;;; 120A6;CUNEIFORM SIGN EZEN TIMES BAD;Lo;0;L;;;;;N;;;;; 120A7;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;; 120A8;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; 120A9;CUNEIFORM SIGN EZEN TIMES HA;Lo;0;L;;;;;N;;;;; 120AA;CUNEIFORM SIGN EZEN TIMES HA GUNU;Lo;0;L;;;;;N;;;;; 120AB;CUNEIFORM SIGN EZEN TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 120AC;CUNEIFORM SIGN EZEN TIMES KASKAL;Lo;0;L;;;;;N;;;;; 120AD;CUNEIFORM SIGN EZEN TIMES KASKAL SQUARED;Lo;0;L;;;;;N;;;;; 120AE;CUNEIFORM SIGN EZEN TIMES KU3;Lo;0;L;;;;;N;;;;; 120AF;CUNEIFORM SIGN EZEN TIMES LA;Lo;0;L;;;;;N;;;;; 120B0;CUNEIFORM SIGN EZEN TIMES LAL TIMES LAL;Lo;0;L;;;;;N;;;;; 120B1;CUNEIFORM SIGN EZEN TIMES LI;Lo;0;L;;;;;N;;;;; 120B2;CUNEIFORM SIGN EZEN TIMES LU;Lo;0;L;;;;;N;;;;; 120B3;CUNEIFORM SIGN EZEN TIMES U2;Lo;0;L;;;;;N;;;;; 120B4;CUNEIFORM SIGN EZEN TIMES UD;Lo;0;L;;;;;N;;;;; 120B5;CUNEIFORM SIGN GA;Lo;0;L;;;;;N;;;;; 120B6;CUNEIFORM SIGN GA GUNU;Lo;0;L;;;;;N;;;;; 120B7;CUNEIFORM SIGN GA2;Lo;0;L;;;;;N;;;;; 120B8;CUNEIFORM SIGN GA2 TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;; 120B9;CUNEIFORM SIGN GA2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;; 120BA;CUNEIFORM SIGN GA2 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;; 120BB;CUNEIFORM SIGN GA2 TIMES AB2 TENU PLUS TAB;Lo;0;L;;;;;N;;;;; 120BC;CUNEIFORM SIGN GA2 TIMES AN;Lo;0;L;;;;;N;;;;; 120BD;CUNEIFORM SIGN GA2 TIMES ASH;Lo;0;L;;;;;N;;;;; 120BE;CUNEIFORM SIGN GA2 TIMES ASH2 PLUS GAL;Lo;0;L;;;;;N;;;;; 120BF;CUNEIFORM SIGN GA2 TIMES BAD;Lo;0;L;;;;;N;;;;; 120C0;CUNEIFORM SIGN GA2 TIMES BAR PLUS RA;Lo;0;L;;;;;N;;;;; 120C1;CUNEIFORM SIGN GA2 TIMES BUR;Lo;0;L;;;;;N;;;;; 120C2;CUNEIFORM SIGN GA2 TIMES BUR PLUS RA;Lo;0;L;;;;;N;;;;; 120C3;CUNEIFORM SIGN GA2 TIMES DA;Lo;0;L;;;;;N;;;;; 120C4;CUNEIFORM SIGN GA2 TIMES DI;Lo;0;L;;;;;N;;;;; 120C5;CUNEIFORM SIGN GA2 TIMES DIM TIMES SHE;Lo;0;L;;;;;N;;;;; 120C6;CUNEIFORM SIGN GA2 TIMES DUB;Lo;0;L;;;;;N;;;;; 120C7;CUNEIFORM SIGN GA2 TIMES EL;Lo;0;L;;;;;N;;;;; 120C8;CUNEIFORM SIGN GA2 TIMES EL PLUS LA;Lo;0;L;;;;;N;;;;; 120C9;CUNEIFORM SIGN GA2 TIMES EN;Lo;0;L;;;;;N;;;;; 120CA;CUNEIFORM SIGN GA2 TIMES EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 120CB;CUNEIFORM SIGN GA2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 120CC;CUNEIFORM SIGN GA2 TIMES GAR;Lo;0;L;;;;;N;;;;; 120CD;CUNEIFORM SIGN GA2 TIMES GI;Lo;0;L;;;;;N;;;;; 120CE;CUNEIFORM SIGN GA2 TIMES GI4;Lo;0;L;;;;;N;;;;; 120CF;CUNEIFORM SIGN GA2 TIMES GI4 PLUS A;Lo;0;L;;;;;N;;;;; 120D0;CUNEIFORM SIGN GA2 TIMES GIR2 PLUS SU;Lo;0;L;;;;;N;;;;; 120D1;CUNEIFORM SIGN GA2 TIMES HA PLUS LU PLUS ESH2;Lo;0;L;;;;;N;;;;; 120D2;CUNEIFORM SIGN GA2 TIMES HAL;Lo;0;L;;;;;N;;;;; 120D3;CUNEIFORM SIGN GA2 TIMES HAL PLUS LA;Lo;0;L;;;;;N;;;;; 120D4;CUNEIFORM SIGN GA2 TIMES HI PLUS LI;Lo;0;L;;;;;N;;;;; 120D5;CUNEIFORM SIGN GA2 TIMES HUB2;Lo;0;L;;;;;N;;;;; 120D6;CUNEIFORM SIGN GA2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 120D7;CUNEIFORM SIGN GA2 TIMES ISH PLUS HU PLUS ASH;Lo;0;L;;;;;N;;;;; 120D8;CUNEIFORM SIGN GA2 TIMES KAK;Lo;0;L;;;;;N;;;;; 120D9;CUNEIFORM SIGN GA2 TIMES KASKAL;Lo;0;L;;;;;N;;;;; 120DA;CUNEIFORM SIGN GA2 TIMES KID;Lo;0;L;;;;;N;;;;; 120DB;CUNEIFORM SIGN GA2 TIMES KID PLUS LAL;Lo;0;L;;;;;N;;;;; 120DC;CUNEIFORM SIGN GA2 TIMES KU3 PLUS AN;Lo;0;L;;;;;N;;;;; 120DD;CUNEIFORM SIGN GA2 TIMES LA;Lo;0;L;;;;;N;;;;; 120DE;CUNEIFORM SIGN GA2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; 120DF;CUNEIFORM SIGN GA2 TIMES MI;Lo;0;L;;;;;N;;;;; 120E0;CUNEIFORM SIGN GA2 TIMES NUN;Lo;0;L;;;;;N;;;;; 120E1;CUNEIFORM SIGN GA2 TIMES NUN OVER NUN;Lo;0;L;;;;;N;;;;; 120E2;CUNEIFORM SIGN GA2 TIMES PA;Lo;0;L;;;;;N;;;;; 120E3;CUNEIFORM SIGN GA2 TIMES SAL;Lo;0;L;;;;;N;;;;; 120E4;CUNEIFORM SIGN GA2 TIMES SAR;Lo;0;L;;;;;N;;;;; 120E5;CUNEIFORM SIGN GA2 TIMES SHE;Lo;0;L;;;;;N;;;;; 120E6;CUNEIFORM SIGN GA2 TIMES SHE PLUS TUR;Lo;0;L;;;;;N;;;;; 120E7;CUNEIFORM SIGN GA2 TIMES SHID;Lo;0;L;;;;;N;;;;; 120E8;CUNEIFORM SIGN GA2 TIMES SUM;Lo;0;L;;;;;N;;;;; 120E9;CUNEIFORM SIGN GA2 TIMES TAK4;Lo;0;L;;;;;N;;;;; 120EA;CUNEIFORM SIGN GA2 TIMES U;Lo;0;L;;;;;N;;;;; 120EB;CUNEIFORM SIGN GA2 TIMES UD;Lo;0;L;;;;;N;;;;; 120EC;CUNEIFORM SIGN GA2 TIMES UD PLUS DU;Lo;0;L;;;;;N;;;;; 120ED;CUNEIFORM SIGN GA2 OVER GA2;Lo;0;L;;;;;N;;;;; 120EE;CUNEIFORM SIGN GABA;Lo;0;L;;;;;N;;;;; 120EF;CUNEIFORM SIGN GABA CROSSING GABA;Lo;0;L;;;;;N;;;;; 120F0;CUNEIFORM SIGN GAD;Lo;0;L;;;;;N;;;;; 120F1;CUNEIFORM SIGN GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; 120F2;CUNEIFORM SIGN GAL;Lo;0;L;;;;;N;;;;; 120F3;CUNEIFORM SIGN GAL GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; 120F4;CUNEIFORM SIGN GALAM;Lo;0;L;;;;;N;;;;; 120F5;CUNEIFORM SIGN GAM;Lo;0;L;;;;;N;;;;; 120F6;CUNEIFORM SIGN GAN;Lo;0;L;;;;;N;;;;; 120F7;CUNEIFORM SIGN GAN2;Lo;0;L;;;;;N;;;;; 120F8;CUNEIFORM SIGN GAN2 TENU;Lo;0;L;;;;;N;;;;; 120F9;CUNEIFORM SIGN GAN2 OVER GAN2;Lo;0;L;;;;;N;;;;; 120FA;CUNEIFORM SIGN GAN2 CROSSING GAN2;Lo;0;L;;;;;N;;;;; 120FB;CUNEIFORM SIGN GAR;Lo;0;L;;;;;N;;;;; 120FC;CUNEIFORM SIGN GAR3;Lo;0;L;;;;;N;;;;; 120FD;CUNEIFORM SIGN GASHAN;Lo;0;L;;;;;N;;;;; 120FE;CUNEIFORM SIGN GESHTIN;Lo;0;L;;;;;N;;;;; 120FF;CUNEIFORM SIGN GESHTIN TIMES KUR;Lo;0;L;;;;;N;;;;; 12100;CUNEIFORM SIGN GI;Lo;0;L;;;;;N;;;;; 12101;CUNEIFORM SIGN GI TIMES E;Lo;0;L;;;;;N;;;;; 12102;CUNEIFORM SIGN GI TIMES U;Lo;0;L;;;;;N;;;;; 12103;CUNEIFORM SIGN GI CROSSING GI;Lo;0;L;;;;;N;;;;; 12104;CUNEIFORM SIGN GI4;Lo;0;L;;;;;N;;;;; 12105;CUNEIFORM SIGN GI4 OVER GI4;Lo;0;L;;;;;N;;;;; 12106;CUNEIFORM SIGN GI4 CROSSING GI4;Lo;0;L;;;;;N;;;;; 12107;CUNEIFORM SIGN GIDIM;Lo;0;L;;;;;N;;;;; 12108;CUNEIFORM SIGN GIR2;Lo;0;L;;;;;N;;;;; 12109;CUNEIFORM SIGN GIR2 GUNU;Lo;0;L;;;;;N;;;;; 1210A;CUNEIFORM SIGN GIR3;Lo;0;L;;;;;N;;;;; 1210B;CUNEIFORM SIGN GIR3 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;; 1210C;CUNEIFORM SIGN GIR3 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1210D;CUNEIFORM SIGN GIR3 TIMES IGI;Lo;0;L;;;;;N;;;;; 1210E;CUNEIFORM SIGN GIR3 TIMES LU PLUS IGI;Lo;0;L;;;;;N;;;;; 1210F;CUNEIFORM SIGN GIR3 TIMES PA;Lo;0;L;;;;;N;;;;; 12110;CUNEIFORM SIGN GISAL;Lo;0;L;;;;;N;;;;; 12111;CUNEIFORM SIGN GISH;Lo;0;L;;;;;N;;;;; 12112;CUNEIFORM SIGN GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; 12113;CUNEIFORM SIGN GISH TIMES BAD;Lo;0;L;;;;;N;;;;; 12114;CUNEIFORM SIGN GISH TIMES TAK4;Lo;0;L;;;;;N;;;;; 12115;CUNEIFORM SIGN GISH TENU;Lo;0;L;;;;;N;;;;; 12116;CUNEIFORM SIGN GU;Lo;0;L;;;;;N;;;;; 12117;CUNEIFORM SIGN GU CROSSING GU;Lo;0;L;;;;;N;;;;; 12118;CUNEIFORM SIGN GU2;Lo;0;L;;;;;N;;;;; 12119;CUNEIFORM SIGN GU2 TIMES KAK;Lo;0;L;;;;;N;;;;; 1211A;CUNEIFORM SIGN GU2 TIMES KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 1211B;CUNEIFORM SIGN GU2 TIMES NUN;Lo;0;L;;;;;N;;;;; 1211C;CUNEIFORM SIGN GU2 TIMES SAL PLUS TUG2;Lo;0;L;;;;;N;;;;; 1211D;CUNEIFORM SIGN GU2 GUNU;Lo;0;L;;;;;N;;;;; 1211E;CUNEIFORM SIGN GUD;Lo;0;L;;;;;N;;;;; 1211F;CUNEIFORM SIGN GUD TIMES A PLUS KUR;Lo;0;L;;;;;N;;;;; 12120;CUNEIFORM SIGN GUD TIMES KUR;Lo;0;L;;;;;N;;;;; 12121;CUNEIFORM SIGN GUD OVER GUD LUGAL;Lo;0;L;;;;;N;;;;; 12122;CUNEIFORM SIGN GUL;Lo;0;L;;;;;N;;;;; 12123;CUNEIFORM SIGN GUM;Lo;0;L;;;;;N;;;;; 12124;CUNEIFORM SIGN GUM TIMES SHE;Lo;0;L;;;;;N;;;;; 12125;CUNEIFORM SIGN GUR;Lo;0;L;;;;;N;;;;; 12126;CUNEIFORM SIGN GUR7;Lo;0;L;;;;;N;;;;; 12127;CUNEIFORM SIGN GURUN;Lo;0;L;;;;;N;;;;; 12128;CUNEIFORM SIGN GURUSH;Lo;0;L;;;;;N;;;;; 12129;CUNEIFORM SIGN HA;Lo;0;L;;;;;N;;;;; 1212A;CUNEIFORM SIGN HA TENU;Lo;0;L;;;;;N;;;;; 1212B;CUNEIFORM SIGN HA GUNU;Lo;0;L;;;;;N;;;;; 1212C;CUNEIFORM SIGN HAL;Lo;0;L;;;;;N;;;;; 1212D;CUNEIFORM SIGN HI;Lo;0;L;;;;;N;;;;; 1212E;CUNEIFORM SIGN HI TIMES ASH;Lo;0;L;;;;;N;;;;; 1212F;CUNEIFORM SIGN HI TIMES ASH2;Lo;0;L;;;;;N;;;;; 12130;CUNEIFORM SIGN HI TIMES BAD;Lo;0;L;;;;;N;;;;; 12131;CUNEIFORM SIGN HI TIMES DISH;Lo;0;L;;;;;N;;;;; 12132;CUNEIFORM SIGN HI TIMES GAD;Lo;0;L;;;;;N;;;;; 12133;CUNEIFORM SIGN HI TIMES KIN;Lo;0;L;;;;;N;;;;; 12134;CUNEIFORM SIGN HI TIMES NUN;Lo;0;L;;;;;N;;;;; 12135;CUNEIFORM SIGN HI TIMES SHE;Lo;0;L;;;;;N;;;;; 12136;CUNEIFORM SIGN HI TIMES U;Lo;0;L;;;;;N;;;;; 12137;CUNEIFORM SIGN HU;Lo;0;L;;;;;N;;;;; 12138;CUNEIFORM SIGN HUB2;Lo;0;L;;;;;N;;;;; 12139;CUNEIFORM SIGN HUB2 TIMES AN;Lo;0;L;;;;;N;;;;; 1213A;CUNEIFORM SIGN HUB2 TIMES HAL;Lo;0;L;;;;;N;;;;; 1213B;CUNEIFORM SIGN HUB2 TIMES KASKAL;Lo;0;L;;;;;N;;;;; 1213C;CUNEIFORM SIGN HUB2 TIMES LISH;Lo;0;L;;;;;N;;;;; 1213D;CUNEIFORM SIGN HUB2 TIMES UD;Lo;0;L;;;;;N;;;;; 1213E;CUNEIFORM SIGN HUL2;Lo;0;L;;;;;N;;;;; 1213F;CUNEIFORM SIGN I;Lo;0;L;;;;;N;;;;; 12140;CUNEIFORM SIGN I A;Lo;0;L;;;;;N;;;;; 12141;CUNEIFORM SIGN IB;Lo;0;L;;;;;N;;;;; 12142;CUNEIFORM SIGN IDIM;Lo;0;L;;;;;N;;;;; 12143;CUNEIFORM SIGN IDIM OVER IDIM BUR;Lo;0;L;;;;;N;;;;; 12144;CUNEIFORM SIGN IDIM OVER IDIM SQUARED;Lo;0;L;;;;;N;;;;; 12145;CUNEIFORM SIGN IG;Lo;0;L;;;;;N;;;;; 12146;CUNEIFORM SIGN IGI;Lo;0;L;;;;;N;;;;; 12147;CUNEIFORM SIGN IGI DIB;Lo;0;L;;;;;N;;;;; 12148;CUNEIFORM SIGN IGI RI;Lo;0;L;;;;;N;;;;; 12149;CUNEIFORM SIGN IGI OVER IGI SHIR OVER SHIR UD OVER UD;Lo;0;L;;;;;N;;;;; 1214A;CUNEIFORM SIGN IGI GUNU;Lo;0;L;;;;;N;;;;; 1214B;CUNEIFORM SIGN IL;Lo;0;L;;;;;N;;;;; 1214C;CUNEIFORM SIGN IL TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1214D;CUNEIFORM SIGN IL2;Lo;0;L;;;;;N;;;;; 1214E;CUNEIFORM SIGN IM;Lo;0;L;;;;;N;;;;; 1214F;CUNEIFORM SIGN IM TIMES TAK4;Lo;0;L;;;;;N;;;;; 12150;CUNEIFORM SIGN IM CROSSING IM;Lo;0;L;;;;;N;;;;; 12151;CUNEIFORM SIGN IM OPPOSING IM;Lo;0;L;;;;;N;;;;; 12152;CUNEIFORM SIGN IM SQUARED;Lo;0;L;;;;;N;;;;; 12153;CUNEIFORM SIGN IMIN;Lo;0;L;;;;;N;;;;; 12154;CUNEIFORM SIGN IN;Lo;0;L;;;;;N;;;;; 12155;CUNEIFORM SIGN IR;Lo;0;L;;;;;N;;;;; 12156;CUNEIFORM SIGN ISH;Lo;0;L;;;;;N;;;;; 12157;CUNEIFORM SIGN KA;Lo;0;L;;;;;N;;;;; 12158;CUNEIFORM SIGN KA TIMES A;Lo;0;L;;;;;N;;;;; 12159;CUNEIFORM SIGN KA TIMES AD;Lo;0;L;;;;;N;;;;; 1215A;CUNEIFORM SIGN KA TIMES AD PLUS KU3;Lo;0;L;;;;;N;;;;; 1215B;CUNEIFORM SIGN KA TIMES ASH2;Lo;0;L;;;;;N;;;;; 1215C;CUNEIFORM SIGN KA TIMES BAD;Lo;0;L;;;;;N;;;;; 1215D;CUNEIFORM SIGN KA TIMES BALAG;Lo;0;L;;;;;N;;;;; 1215E;CUNEIFORM SIGN KA TIMES BAR;Lo;0;L;;;;;N;;;;; 1215F;CUNEIFORM SIGN KA TIMES BI;Lo;0;L;;;;;N;;;;; 12160;CUNEIFORM SIGN KA TIMES ERIN2;Lo;0;L;;;;;N;;;;; 12161;CUNEIFORM SIGN KA TIMES ESH2;Lo;0;L;;;;;N;;;;; 12162;CUNEIFORM SIGN KA TIMES GA;Lo;0;L;;;;;N;;;;; 12163;CUNEIFORM SIGN KA TIMES GAL;Lo;0;L;;;;;N;;;;; 12164;CUNEIFORM SIGN KA TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 12165;CUNEIFORM SIGN KA TIMES GAR;Lo;0;L;;;;;N;;;;; 12166;CUNEIFORM SIGN KA TIMES GAR PLUS SHA3 PLUS A;Lo;0;L;;;;;N;;;;; 12167;CUNEIFORM SIGN KA TIMES GI;Lo;0;L;;;;;N;;;;; 12168;CUNEIFORM SIGN KA TIMES GIR2;Lo;0;L;;;;;N;;;;; 12169;CUNEIFORM SIGN KA TIMES GISH PLUS SAR;Lo;0;L;;;;;N;;;;; 1216A;CUNEIFORM SIGN KA TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; 1216B;CUNEIFORM SIGN KA TIMES GU;Lo;0;L;;;;;N;;;;; 1216C;CUNEIFORM SIGN KA TIMES GUR7;Lo;0;L;;;;;N;;;;; 1216D;CUNEIFORM SIGN KA TIMES IGI;Lo;0;L;;;;;N;;;;; 1216E;CUNEIFORM SIGN KA TIMES IM;Lo;0;L;;;;;N;;;;; 1216F;CUNEIFORM SIGN KA TIMES KAK;Lo;0;L;;;;;N;;;;; 12170;CUNEIFORM SIGN KA TIMES KI;Lo;0;L;;;;;N;;;;; 12171;CUNEIFORM SIGN KA TIMES KID;Lo;0;L;;;;;N;;;;; 12172;CUNEIFORM SIGN KA TIMES LI;Lo;0;L;;;;;N;;;;; 12173;CUNEIFORM SIGN KA TIMES LU;Lo;0;L;;;;;N;;;;; 12174;CUNEIFORM SIGN KA TIMES ME;Lo;0;L;;;;;N;;;;; 12175;CUNEIFORM SIGN KA TIMES ME PLUS DU;Lo;0;L;;;;;N;;;;; 12176;CUNEIFORM SIGN KA TIMES ME PLUS GI;Lo;0;L;;;;;N;;;;; 12177;CUNEIFORM SIGN KA TIMES ME PLUS TE;Lo;0;L;;;;;N;;;;; 12178;CUNEIFORM SIGN KA TIMES MI;Lo;0;L;;;;;N;;;;; 12179;CUNEIFORM SIGN KA TIMES MI PLUS NUNUZ;Lo;0;L;;;;;N;;;;; 1217A;CUNEIFORM SIGN KA TIMES NE;Lo;0;L;;;;;N;;;;; 1217B;CUNEIFORM SIGN KA TIMES NUN;Lo;0;L;;;;;N;;;;; 1217C;CUNEIFORM SIGN KA TIMES PI;Lo;0;L;;;;;N;;;;; 1217D;CUNEIFORM SIGN KA TIMES RU;Lo;0;L;;;;;N;;;;; 1217E;CUNEIFORM SIGN KA TIMES SA;Lo;0;L;;;;;N;;;;; 1217F;CUNEIFORM SIGN KA TIMES SAR;Lo;0;L;;;;;N;;;;; 12180;CUNEIFORM SIGN KA TIMES SHA;Lo;0;L;;;;;N;;;;; 12181;CUNEIFORM SIGN KA TIMES SHE;Lo;0;L;;;;;N;;;;; 12182;CUNEIFORM SIGN KA TIMES SHID;Lo;0;L;;;;;N;;;;; 12183;CUNEIFORM SIGN KA TIMES SHU;Lo;0;L;;;;;N;;;;; 12184;CUNEIFORM SIGN KA TIMES SIG;Lo;0;L;;;;;N;;;;; 12185;CUNEIFORM SIGN KA TIMES SUHUR;Lo;0;L;;;;;N;;;;; 12186;CUNEIFORM SIGN KA TIMES TAR;Lo;0;L;;;;;N;;;;; 12187;CUNEIFORM SIGN KA TIMES U;Lo;0;L;;;;;N;;;;; 12188;CUNEIFORM SIGN KA TIMES U2;Lo;0;L;;;;;N;;;;; 12189;CUNEIFORM SIGN KA TIMES UD;Lo;0;L;;;;;N;;;;; 1218A;CUNEIFORM SIGN KA TIMES UMUM TIMES PA;Lo;0;L;;;;;N;;;;; 1218B;CUNEIFORM SIGN KA TIMES USH;Lo;0;L;;;;;N;;;;; 1218C;CUNEIFORM SIGN KA TIMES ZI;Lo;0;L;;;;;N;;;;; 1218D;CUNEIFORM SIGN KA2;Lo;0;L;;;;;N;;;;; 1218E;CUNEIFORM SIGN KA2 CROSSING KA2;Lo;0;L;;;;;N;;;;; 1218F;CUNEIFORM SIGN KAB;Lo;0;L;;;;;N;;;;; 12190;CUNEIFORM SIGN KAD2;Lo;0;L;;;;;N;;;;; 12191;CUNEIFORM SIGN KAD3;Lo;0;L;;;;;N;;;;; 12192;CUNEIFORM SIGN KAD4;Lo;0;L;;;;;N;;;;; 12193;CUNEIFORM SIGN KAD5;Lo;0;L;;;;;N;;;;; 12194;CUNEIFORM SIGN KAD5 OVER KAD5;Lo;0;L;;;;;N;;;;; 12195;CUNEIFORM SIGN KAK;Lo;0;L;;;;;N;;;;; 12196;CUNEIFORM SIGN KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12197;CUNEIFORM SIGN KAL;Lo;0;L;;;;;N;;;;; 12198;CUNEIFORM SIGN KAL TIMES BAD;Lo;0;L;;;;;N;;;;; 12199;CUNEIFORM SIGN KAL CROSSING KAL;Lo;0;L;;;;;N;;;;; 1219A;CUNEIFORM SIGN KAM2;Lo;0;L;;;;;N;;;;; 1219B;CUNEIFORM SIGN KAM4;Lo;0;L;;;;;N;;;;; 1219C;CUNEIFORM SIGN KASKAL;Lo;0;L;;;;;N;;;;; 1219D;CUNEIFORM SIGN KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;; 1219E;CUNEIFORM SIGN KASKAL OVER KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;; 1219F;CUNEIFORM SIGN KESH2;Lo;0;L;;;;;N;;;;; 121A0;CUNEIFORM SIGN KI;Lo;0;L;;;;;N;;;;; 121A1;CUNEIFORM SIGN KI TIMES BAD;Lo;0;L;;;;;N;;;;; 121A2;CUNEIFORM SIGN KI TIMES U;Lo;0;L;;;;;N;;;;; 121A3;CUNEIFORM SIGN KI TIMES UD;Lo;0;L;;;;;N;;;;; 121A4;CUNEIFORM SIGN KID;Lo;0;L;;;;;N;;;;; 121A5;CUNEIFORM SIGN KIN;Lo;0;L;;;;;N;;;;; 121A6;CUNEIFORM SIGN KISAL;Lo;0;L;;;;;N;;;;; 121A7;CUNEIFORM SIGN KISH;Lo;0;L;;;;;N;;;;; 121A8;CUNEIFORM SIGN KISIM5;Lo;0;L;;;;;N;;;;; 121A9;CUNEIFORM SIGN KISIM5 OVER KISIM5;Lo;0;L;;;;;N;;;;; 121AA;CUNEIFORM SIGN KU;Lo;0;L;;;;;N;;;;; 121AB;CUNEIFORM SIGN KU OVER HI TIMES ASH2 KU OVER HI TIMES ASH2;Lo;0;L;;;;;N;;;;; 121AC;CUNEIFORM SIGN KU3;Lo;0;L;;;;;N;;;;; 121AD;CUNEIFORM SIGN KU4;Lo;0;L;;;;;N;;;;; 121AE;CUNEIFORM SIGN KU4 VARIANT FORM;Lo;0;L;;;;;N;;;;; 121AF;CUNEIFORM SIGN KU7;Lo;0;L;;;;;N;;;;; 121B0;CUNEIFORM SIGN KUL;Lo;0;L;;;;;N;;;;; 121B1;CUNEIFORM SIGN KUL GUNU;Lo;0;L;;;;;N;;;;; 121B2;CUNEIFORM SIGN KUN;Lo;0;L;;;;;N;;;;; 121B3;CUNEIFORM SIGN KUR;Lo;0;L;;;;;N;;;;; 121B4;CUNEIFORM SIGN KUR OPPOSING KUR;Lo;0;L;;;;;N;;;;; 121B5;CUNEIFORM SIGN KUSHU2;Lo;0;L;;;;;N;;;;; 121B6;CUNEIFORM SIGN KWU318;Lo;0;L;;;;;N;;;;; 121B7;CUNEIFORM SIGN LA;Lo;0;L;;;;;N;;;;; 121B8;CUNEIFORM SIGN LAGAB;Lo;0;L;;;;;N;;;;; 121B9;CUNEIFORM SIGN LAGAB TIMES A;Lo;0;L;;;;;N;;;;; 121BA;CUNEIFORM SIGN LAGAB TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;; 121BB;CUNEIFORM SIGN LAGAB TIMES A PLUS GAR;Lo;0;L;;;;;N;;;;; 121BC;CUNEIFORM SIGN LAGAB TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;; 121BD;CUNEIFORM SIGN LAGAB TIMES AL;Lo;0;L;;;;;N;;;;; 121BE;CUNEIFORM SIGN LAGAB TIMES AN;Lo;0;L;;;;;N;;;;; 121BF;CUNEIFORM SIGN LAGAB TIMES ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; 121C0;CUNEIFORM SIGN LAGAB TIMES BAD;Lo;0;L;;;;;N;;;;; 121C1;CUNEIFORM SIGN LAGAB TIMES BI;Lo;0;L;;;;;N;;;;; 121C2;CUNEIFORM SIGN LAGAB TIMES DAR;Lo;0;L;;;;;N;;;;; 121C3;CUNEIFORM SIGN LAGAB TIMES EN;Lo;0;L;;;;;N;;;;; 121C4;CUNEIFORM SIGN LAGAB TIMES GA;Lo;0;L;;;;;N;;;;; 121C5;CUNEIFORM SIGN LAGAB TIMES GAR;Lo;0;L;;;;;N;;;;; 121C6;CUNEIFORM SIGN LAGAB TIMES GUD;Lo;0;L;;;;;N;;;;; 121C7;CUNEIFORM SIGN LAGAB TIMES GUD PLUS GUD;Lo;0;L;;;;;N;;;;; 121C8;CUNEIFORM SIGN LAGAB TIMES HA;Lo;0;L;;;;;N;;;;; 121C9;CUNEIFORM SIGN LAGAB TIMES HAL;Lo;0;L;;;;;N;;;;; 121CA;CUNEIFORM SIGN LAGAB TIMES HI TIMES NUN;Lo;0;L;;;;;N;;;;; 121CB;CUNEIFORM SIGN LAGAB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 121CC;CUNEIFORM SIGN LAGAB TIMES IM;Lo;0;L;;;;;N;;;;; 121CD;CUNEIFORM SIGN LAGAB TIMES IM PLUS HA;Lo;0;L;;;;;N;;;;; 121CE;CUNEIFORM SIGN LAGAB TIMES IM PLUS LU;Lo;0;L;;;;;N;;;;; 121CF;CUNEIFORM SIGN LAGAB TIMES KI;Lo;0;L;;;;;N;;;;; 121D0;CUNEIFORM SIGN LAGAB TIMES KIN;Lo;0;L;;;;;N;;;;; 121D1;CUNEIFORM SIGN LAGAB TIMES KU3;Lo;0;L;;;;;N;;;;; 121D2;CUNEIFORM SIGN LAGAB TIMES KUL;Lo;0;L;;;;;N;;;;; 121D3;CUNEIFORM SIGN LAGAB TIMES KUL PLUS HI PLUS A;Lo;0;L;;;;;N;;;;; 121D4;CUNEIFORM SIGN LAGAB TIMES LAGAB;Lo;0;L;;;;;N;;;;; 121D5;CUNEIFORM SIGN LAGAB TIMES LISH;Lo;0;L;;;;;N;;;;; 121D6;CUNEIFORM SIGN LAGAB TIMES LU;Lo;0;L;;;;;N;;;;; 121D7;CUNEIFORM SIGN LAGAB TIMES LUL;Lo;0;L;;;;;N;;;;; 121D8;CUNEIFORM SIGN LAGAB TIMES ME;Lo;0;L;;;;;N;;;;; 121D9;CUNEIFORM SIGN LAGAB TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; 121DA;CUNEIFORM SIGN LAGAB TIMES MUSH;Lo;0;L;;;;;N;;;;; 121DB;CUNEIFORM SIGN LAGAB TIMES NE;Lo;0;L;;;;;N;;;;; 121DC;CUNEIFORM SIGN LAGAB TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;; 121DD;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH PLUS ERIN2;Lo;0;L;;;;;N;;;;; 121DE;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH TENU;Lo;0;L;;;;;N;;;;; 121DF;CUNEIFORM SIGN LAGAB TIMES SHU2;Lo;0;L;;;;;N;;;;; 121E0;CUNEIFORM SIGN LAGAB TIMES SHU2 PLUS SHU2;Lo;0;L;;;;;N;;;;; 121E1;CUNEIFORM SIGN LAGAB TIMES SUM;Lo;0;L;;;;;N;;;;; 121E2;CUNEIFORM SIGN LAGAB TIMES TAG;Lo;0;L;;;;;N;;;;; 121E3;CUNEIFORM SIGN LAGAB TIMES TAK4;Lo;0;L;;;;;N;;;;; 121E4;CUNEIFORM SIGN LAGAB TIMES TE PLUS A PLUS SU PLUS NA;Lo;0;L;;;;;N;;;;; 121E5;CUNEIFORM SIGN LAGAB TIMES U;Lo;0;L;;;;;N;;;;; 121E6;CUNEIFORM SIGN LAGAB TIMES U PLUS A;Lo;0;L;;;;;N;;;;; 121E7;CUNEIFORM SIGN LAGAB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; 121E8;CUNEIFORM SIGN LAGAB TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; 121E9;CUNEIFORM SIGN LAGAB TIMES UD;Lo;0;L;;;;;N;;;;; 121EA;CUNEIFORM SIGN LAGAB TIMES USH;Lo;0;L;;;;;N;;;;; 121EB;CUNEIFORM SIGN LAGAB SQUARED;Lo;0;L;;;;;N;;;;; 121EC;CUNEIFORM SIGN LAGAR;Lo;0;L;;;;;N;;;;; 121ED;CUNEIFORM SIGN LAGAR TIMES SHE;Lo;0;L;;;;;N;;;;; 121EE;CUNEIFORM SIGN LAGAR TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;; 121EF;CUNEIFORM SIGN LAGAR GUNU;Lo;0;L;;;;;N;;;;; 121F0;CUNEIFORM SIGN LAGAR GUNU OVER LAGAR GUNU SHE;Lo;0;L;;;;;N;;;;; 121F1;CUNEIFORM SIGN LAHSHU;Lo;0;L;;;;;N;;;;; 121F2;CUNEIFORM SIGN LAL;Lo;0;L;;;;;N;;;;; 121F3;CUNEIFORM SIGN LAL TIMES LAL;Lo;0;L;;;;;N;;;;; 121F4;CUNEIFORM SIGN LAM;Lo;0;L;;;;;N;;;;; 121F5;CUNEIFORM SIGN LAM TIMES KUR;Lo;0;L;;;;;N;;;;; 121F6;CUNEIFORM SIGN LAM TIMES KUR PLUS RU;Lo;0;L;;;;;N;;;;; 121F7;CUNEIFORM SIGN LI;Lo;0;L;;;;;N;;;;; 121F8;CUNEIFORM SIGN LIL;Lo;0;L;;;;;N;;;;; 121F9;CUNEIFORM SIGN LIMMU2;Lo;0;L;;;;;N;;;;; 121FA;CUNEIFORM SIGN LISH;Lo;0;L;;;;;N;;;;; 121FB;CUNEIFORM SIGN LU;Lo;0;L;;;;;N;;;;; 121FC;CUNEIFORM SIGN LU TIMES BAD;Lo;0;L;;;;;N;;;;; 121FD;CUNEIFORM SIGN LU2;Lo;0;L;;;;;N;;;;; 121FE;CUNEIFORM SIGN LU2 TIMES AL;Lo;0;L;;;;;N;;;;; 121FF;CUNEIFORM SIGN LU2 TIMES BAD;Lo;0;L;;;;;N;;;;; 12200;CUNEIFORM SIGN LU2 TIMES ESH2;Lo;0;L;;;;;N;;;;; 12201;CUNEIFORM SIGN LU2 TIMES ESH2 TENU;Lo;0;L;;;;;N;;;;; 12202;CUNEIFORM SIGN LU2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 12203;CUNEIFORM SIGN LU2 TIMES HI TIMES BAD;Lo;0;L;;;;;N;;;;; 12204;CUNEIFORM SIGN LU2 TIMES IM;Lo;0;L;;;;;N;;;;; 12205;CUNEIFORM SIGN LU2 TIMES KAD2;Lo;0;L;;;;;N;;;;; 12206;CUNEIFORM SIGN LU2 TIMES KAD3;Lo;0;L;;;;;N;;;;; 12207;CUNEIFORM SIGN LU2 TIMES KAD3 PLUS ASH;Lo;0;L;;;;;N;;;;; 12208;CUNEIFORM SIGN LU2 TIMES KI;Lo;0;L;;;;;N;;;;; 12209;CUNEIFORM SIGN LU2 TIMES LA PLUS ASH;Lo;0;L;;;;;N;;;;; 1220A;CUNEIFORM SIGN LU2 TIMES LAGAB;Lo;0;L;;;;;N;;;;; 1220B;CUNEIFORM SIGN LU2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; 1220C;CUNEIFORM SIGN LU2 TIMES NE;Lo;0;L;;;;;N;;;;; 1220D;CUNEIFORM SIGN LU2 TIMES NU;Lo;0;L;;;;;N;;;;; 1220E;CUNEIFORM SIGN LU2 TIMES SI PLUS ASH;Lo;0;L;;;;;N;;;;; 1220F;CUNEIFORM SIGN LU2 TIMES SIK2 PLUS BU;Lo;0;L;;;;;N;;;;; 12210;CUNEIFORM SIGN LU2 TIMES TUG2;Lo;0;L;;;;;N;;;;; 12211;CUNEIFORM SIGN LU2 TENU;Lo;0;L;;;;;N;;;;; 12212;CUNEIFORM SIGN LU2 CROSSING LU2;Lo;0;L;;;;;N;;;;; 12213;CUNEIFORM SIGN LU2 OPPOSING LU2;Lo;0;L;;;;;N;;;;; 12214;CUNEIFORM SIGN LU2 SQUARED;Lo;0;L;;;;;N;;;;; 12215;CUNEIFORM SIGN LU2 SHESHIG;Lo;0;L;;;;;N;;;;; 12216;CUNEIFORM SIGN LU3;Lo;0;L;;;;;N;;;;; 12217;CUNEIFORM SIGN LUGAL;Lo;0;L;;;;;N;;;;; 12218;CUNEIFORM SIGN LUGAL OVER LUGAL;Lo;0;L;;;;;N;;;;; 12219;CUNEIFORM SIGN LUGAL OPPOSING LUGAL;Lo;0;L;;;;;N;;;;; 1221A;CUNEIFORM SIGN LUGAL SHESHIG;Lo;0;L;;;;;N;;;;; 1221B;CUNEIFORM SIGN LUH;Lo;0;L;;;;;N;;;;; 1221C;CUNEIFORM SIGN LUL;Lo;0;L;;;;;N;;;;; 1221D;CUNEIFORM SIGN LUM;Lo;0;L;;;;;N;;;;; 1221E;CUNEIFORM SIGN LUM OVER LUM;Lo;0;L;;;;;N;;;;; 1221F;CUNEIFORM SIGN LUM OVER LUM GAR OVER GAR;Lo;0;L;;;;;N;;;;; 12220;CUNEIFORM SIGN MA;Lo;0;L;;;;;N;;;;; 12221;CUNEIFORM SIGN MA TIMES TAK4;Lo;0;L;;;;;N;;;;; 12222;CUNEIFORM SIGN MA GUNU;Lo;0;L;;;;;N;;;;; 12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;; 12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;; 12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;; 12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;; 12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;; 12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;; 12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;; 1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;; 1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;; 1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;; 1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;; 1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;; 1222F;CUNEIFORM SIGN MUG GUNU;Lo;0;L;;;;;N;;;;; 12230;CUNEIFORM SIGN MUNSUB;Lo;0;L;;;;;N;;;;; 12231;CUNEIFORM SIGN MURGU2;Lo;0;L;;;;;N;;;;; 12232;CUNEIFORM SIGN MUSH;Lo;0;L;;;;;N;;;;; 12233;CUNEIFORM SIGN MUSH TIMES A;Lo;0;L;;;;;N;;;;; 12234;CUNEIFORM SIGN MUSH TIMES KUR;Lo;0;L;;;;;N;;;;; 12235;CUNEIFORM SIGN MUSH TIMES ZA;Lo;0;L;;;;;N;;;;; 12236;CUNEIFORM SIGN MUSH OVER MUSH;Lo;0;L;;;;;N;;;;; 12237;CUNEIFORM SIGN MUSH OVER MUSH TIMES A PLUS NA;Lo;0;L;;;;;N;;;;; 12238;CUNEIFORM SIGN MUSH CROSSING MUSH;Lo;0;L;;;;;N;;;;; 12239;CUNEIFORM SIGN MUSH3;Lo;0;L;;;;;N;;;;; 1223A;CUNEIFORM SIGN MUSH3 TIMES A;Lo;0;L;;;;;N;;;;; 1223B;CUNEIFORM SIGN MUSH3 TIMES A PLUS DI;Lo;0;L;;;;;N;;;;; 1223C;CUNEIFORM SIGN MUSH3 TIMES DI;Lo;0;L;;;;;N;;;;; 1223D;CUNEIFORM SIGN MUSH3 GUNU;Lo;0;L;;;;;N;;;;; 1223E;CUNEIFORM SIGN NA;Lo;0;L;;;;;N;;;;; 1223F;CUNEIFORM SIGN NA2;Lo;0;L;;;;;N;;;;; 12240;CUNEIFORM SIGN NAGA;Lo;0;L;;;;;N;;;;; 12241;CUNEIFORM SIGN NAGA INVERTED;Lo;0;L;;;;;N;;;;; 12242;CUNEIFORM SIGN NAGA TIMES SHU TENU;Lo;0;L;;;;;N;;;;; 12243;CUNEIFORM SIGN NAGA OPPOSING NAGA;Lo;0;L;;;;;N;;;;; 12244;CUNEIFORM SIGN NAGAR;Lo;0;L;;;;;N;;;;; 12245;CUNEIFORM SIGN NAM NUTILLU;Lo;0;L;;;;;N;;;;; 12246;CUNEIFORM SIGN NAM;Lo;0;L;;;;;N;;;;; 12247;CUNEIFORM SIGN NAM2;Lo;0;L;;;;;N;;;;; 12248;CUNEIFORM SIGN NE;Lo;0;L;;;;;N;;;;; 12249;CUNEIFORM SIGN NE TIMES A;Lo;0;L;;;;;N;;;;; 1224A;CUNEIFORM SIGN NE TIMES UD;Lo;0;L;;;;;N;;;;; 1224B;CUNEIFORM SIGN NE SHESHIG;Lo;0;L;;;;;N;;;;; 1224C;CUNEIFORM SIGN NI;Lo;0;L;;;;;N;;;;; 1224D;CUNEIFORM SIGN NI TIMES E;Lo;0;L;;;;;N;;;;; 1224E;CUNEIFORM SIGN NI2;Lo;0;L;;;;;N;;;;; 1224F;CUNEIFORM SIGN NIM;Lo;0;L;;;;;N;;;;; 12250;CUNEIFORM SIGN NIM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 12251;CUNEIFORM SIGN NIM TIMES GAR PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;; 12252;CUNEIFORM SIGN NINDA2;Lo;0;L;;;;;N;;;;; 12253;CUNEIFORM SIGN NINDA2 TIMES AN;Lo;0;L;;;;;N;;;;; 12254;CUNEIFORM SIGN NINDA2 TIMES ASH;Lo;0;L;;;;;N;;;;; 12255;CUNEIFORM SIGN NINDA2 TIMES ASH PLUS ASH;Lo;0;L;;;;;N;;;;; 12256;CUNEIFORM SIGN NINDA2 TIMES GUD;Lo;0;L;;;;;N;;;;; 12257;CUNEIFORM SIGN NINDA2 TIMES ME PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;; 12258;CUNEIFORM SIGN NINDA2 TIMES NE;Lo;0;L;;;;;N;;;;; 12259;CUNEIFORM SIGN NINDA2 TIMES NUN;Lo;0;L;;;;;N;;;;; 1225A;CUNEIFORM SIGN NINDA2 TIMES SHE;Lo;0;L;;;;;N;;;;; 1225B;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS A AN;Lo;0;L;;;;;N;;;;; 1225C;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH;Lo;0;L;;;;;N;;;;; 1225D;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH PLUS ASH;Lo;0;L;;;;;N;;;;; 1225E;CUNEIFORM SIGN NINDA2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; 1225F;CUNEIFORM SIGN NINDA2 TIMES USH;Lo;0;L;;;;;N;;;;; 12260;CUNEIFORM SIGN NISAG;Lo;0;L;;;;;N;;;;; 12261;CUNEIFORM SIGN NU;Lo;0;L;;;;;N;;;;; 12262;CUNEIFORM SIGN NU11;Lo;0;L;;;;;N;;;;; 12263;CUNEIFORM SIGN NUN;Lo;0;L;;;;;N;;;;; 12264;CUNEIFORM SIGN NUN LAGAR TIMES GAR;Lo;0;L;;;;;N;;;;; 12265;CUNEIFORM SIGN NUN LAGAR TIMES MASH;Lo;0;L;;;;;N;;;;; 12266;CUNEIFORM SIGN NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;; 12267;CUNEIFORM SIGN NUN LAGAR TIMES SAL OVER NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;; 12268;CUNEIFORM SIGN NUN LAGAR TIMES USH;Lo;0;L;;;;;N;;;;; 12269;CUNEIFORM SIGN NUN TENU;Lo;0;L;;;;;N;;;;; 1226A;CUNEIFORM SIGN NUN OVER NUN;Lo;0;L;;;;;N;;;;; 1226B;CUNEIFORM SIGN NUN CROSSING NUN;Lo;0;L;;;;;N;;;;; 1226C;CUNEIFORM SIGN NUN CROSSING NUN LAGAR OVER LAGAR;Lo;0;L;;;;;N;;;;; 1226D;CUNEIFORM SIGN NUNUZ;Lo;0;L;;;;;N;;;;; 1226E;CUNEIFORM SIGN NUNUZ AB2 TIMES ASHGAB;Lo;0;L;;;;;N;;;;; 1226F;CUNEIFORM SIGN NUNUZ AB2 TIMES BI;Lo;0;L;;;;;N;;;;; 12270;CUNEIFORM SIGN NUNUZ AB2 TIMES DUG;Lo;0;L;;;;;N;;;;; 12271;CUNEIFORM SIGN NUNUZ AB2 TIMES GUD;Lo;0;L;;;;;N;;;;; 12272;CUNEIFORM SIGN NUNUZ AB2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12273;CUNEIFORM SIGN NUNUZ AB2 TIMES KAD3;Lo;0;L;;;;;N;;;;; 12274;CUNEIFORM SIGN NUNUZ AB2 TIMES LA;Lo;0;L;;;;;N;;;;; 12275;CUNEIFORM SIGN NUNUZ AB2 TIMES NE;Lo;0;L;;;;;N;;;;; 12276;CUNEIFORM SIGN NUNUZ AB2 TIMES SILA3;Lo;0;L;;;;;N;;;;; 12277;CUNEIFORM SIGN NUNUZ AB2 TIMES U2;Lo;0;L;;;;;N;;;;; 12278;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;; 12279;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI U;Lo;0;L;;;;;N;;;;; 1227A;CUNEIFORM SIGN PA;Lo;0;L;;;;;N;;;;; 1227B;CUNEIFORM SIGN PAD;Lo;0;L;;;;;N;;;;; 1227C;CUNEIFORM SIGN PAN;Lo;0;L;;;;;N;;;;; 1227D;CUNEIFORM SIGN PAP;Lo;0;L;;;;;N;;;;; 1227E;CUNEIFORM SIGN PESH2;Lo;0;L;;;;;N;;;;; 1227F;CUNEIFORM SIGN PI;Lo;0;L;;;;;N;;;;; 12280;CUNEIFORM SIGN PI TIMES A;Lo;0;L;;;;;N;;;;; 12281;CUNEIFORM SIGN PI TIMES AB;Lo;0;L;;;;;N;;;;; 12282;CUNEIFORM SIGN PI TIMES BI;Lo;0;L;;;;;N;;;;; 12283;CUNEIFORM SIGN PI TIMES BU;Lo;0;L;;;;;N;;;;; 12284;CUNEIFORM SIGN PI TIMES E;Lo;0;L;;;;;N;;;;; 12285;CUNEIFORM SIGN PI TIMES I;Lo;0;L;;;;;N;;;;; 12286;CUNEIFORM SIGN PI TIMES IB;Lo;0;L;;;;;N;;;;; 12287;CUNEIFORM SIGN PI TIMES U;Lo;0;L;;;;;N;;;;; 12288;CUNEIFORM SIGN PI TIMES U2;Lo;0;L;;;;;N;;;;; 12289;CUNEIFORM SIGN PI CROSSING PI;Lo;0;L;;;;;N;;;;; 1228A;CUNEIFORM SIGN PIRIG;Lo;0;L;;;;;N;;;;; 1228B;CUNEIFORM SIGN PIRIG TIMES KAL;Lo;0;L;;;;;N;;;;; 1228C;CUNEIFORM SIGN PIRIG TIMES UD;Lo;0;L;;;;;N;;;;; 1228D;CUNEIFORM SIGN PIRIG TIMES ZA;Lo;0;L;;;;;N;;;;; 1228E;CUNEIFORM SIGN PIRIG OPPOSING PIRIG;Lo;0;L;;;;;N;;;;; 1228F;CUNEIFORM SIGN RA;Lo;0;L;;;;;N;;;;; 12290;CUNEIFORM SIGN RAB;Lo;0;L;;;;;N;;;;; 12291;CUNEIFORM SIGN RI;Lo;0;L;;;;;N;;;;; 12292;CUNEIFORM SIGN RU;Lo;0;L;;;;;N;;;;; 12293;CUNEIFORM SIGN SA;Lo;0;L;;;;;N;;;;; 12294;CUNEIFORM SIGN SAG NUTILLU;Lo;0;L;;;;;N;;;;; 12295;CUNEIFORM SIGN SAG;Lo;0;L;;;;;N;;;;; 12296;CUNEIFORM SIGN SAG TIMES A;Lo;0;L;;;;;N;;;;; 12297;CUNEIFORM SIGN SAG TIMES DU;Lo;0;L;;;;;N;;;;; 12298;CUNEIFORM SIGN SAG TIMES DUB;Lo;0;L;;;;;N;;;;; 12299;CUNEIFORM SIGN SAG TIMES HA;Lo;0;L;;;;;N;;;;; 1229A;CUNEIFORM SIGN SAG TIMES KAK;Lo;0;L;;;;;N;;;;; 1229B;CUNEIFORM SIGN SAG TIMES KUR;Lo;0;L;;;;;N;;;;; 1229C;CUNEIFORM SIGN SAG TIMES LUM;Lo;0;L;;;;;N;;;;; 1229D;CUNEIFORM SIGN SAG TIMES MI;Lo;0;L;;;;;N;;;;; 1229E;CUNEIFORM SIGN SAG TIMES NUN;Lo;0;L;;;;;N;;;;; 1229F;CUNEIFORM SIGN SAG TIMES SAL;Lo;0;L;;;;;N;;;;; 122A0;CUNEIFORM SIGN SAG TIMES SHID;Lo;0;L;;;;;N;;;;; 122A1;CUNEIFORM SIGN SAG TIMES TAB;Lo;0;L;;;;;N;;;;; 122A2;CUNEIFORM SIGN SAG TIMES U2;Lo;0;L;;;;;N;;;;; 122A3;CUNEIFORM SIGN SAG TIMES UB;Lo;0;L;;;;;N;;;;; 122A4;CUNEIFORM SIGN SAG TIMES UM;Lo;0;L;;;;;N;;;;; 122A5;CUNEIFORM SIGN SAG TIMES UR;Lo;0;L;;;;;N;;;;; 122A6;CUNEIFORM SIGN SAG TIMES USH;Lo;0;L;;;;;N;;;;; 122A7;CUNEIFORM SIGN SAG OVER SAG;Lo;0;L;;;;;N;;;;; 122A8;CUNEIFORM SIGN SAG GUNU;Lo;0;L;;;;;N;;;;; 122A9;CUNEIFORM SIGN SAL;Lo;0;L;;;;;N;;;;; 122AA;CUNEIFORM SIGN SAL LAGAB TIMES ASH2;Lo;0;L;;;;;N;;;;; 122AB;CUNEIFORM SIGN SANGA2;Lo;0;L;;;;;N;;;;; 122AC;CUNEIFORM SIGN SAR;Lo;0;L;;;;;N;;;;; 122AD;CUNEIFORM SIGN SHA;Lo;0;L;;;;;N;;;;; 122AE;CUNEIFORM SIGN SHA3;Lo;0;L;;;;;N;;;;; 122AF;CUNEIFORM SIGN SHA3 TIMES A;Lo;0;L;;;;;N;;;;; 122B0;CUNEIFORM SIGN SHA3 TIMES BAD;Lo;0;L;;;;;N;;;;; 122B1;CUNEIFORM SIGN SHA3 TIMES GISH;Lo;0;L;;;;;N;;;;; 122B2;CUNEIFORM SIGN SHA3 TIMES NE;Lo;0;L;;;;;N;;;;; 122B3;CUNEIFORM SIGN SHA3 TIMES SHU2;Lo;0;L;;;;;N;;;;; 122B4;CUNEIFORM SIGN SHA3 TIMES TUR;Lo;0;L;;;;;N;;;;; 122B5;CUNEIFORM SIGN SHA3 TIMES U;Lo;0;L;;;;;N;;;;; 122B6;CUNEIFORM SIGN SHA3 TIMES U PLUS A;Lo;0;L;;;;;N;;;;; 122B7;CUNEIFORM SIGN SHA6;Lo;0;L;;;;;N;;;;; 122B8;CUNEIFORM SIGN SHAB6;Lo;0;L;;;;;N;;;;; 122B9;CUNEIFORM SIGN SHAR2;Lo;0;L;;;;;N;;;;; 122BA;CUNEIFORM SIGN SHE;Lo;0;L;;;;;N;;;;; 122BB;CUNEIFORM SIGN SHE HU;Lo;0;L;;;;;N;;;;; 122BC;CUNEIFORM SIGN SHE OVER SHE GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; 122BD;CUNEIFORM SIGN SHE OVER SHE TAB OVER TAB GAR OVER GAR;Lo;0;L;;;;;N;;;;; 122BE;CUNEIFORM SIGN SHEG9;Lo;0;L;;;;;N;;;;; 122BF;CUNEIFORM SIGN SHEN;Lo;0;L;;;;;N;;;;; 122C0;CUNEIFORM SIGN SHESH;Lo;0;L;;;;;N;;;;; 122C1;CUNEIFORM SIGN SHESH2;Lo;0;L;;;;;N;;;;; 122C2;CUNEIFORM SIGN SHESHLAM;Lo;0;L;;;;;N;;;;; 122C3;CUNEIFORM SIGN SHID;Lo;0;L;;;;;N;;;;; 122C4;CUNEIFORM SIGN SHID TIMES A;Lo;0;L;;;;;N;;;;; 122C5;CUNEIFORM SIGN SHID TIMES IM;Lo;0;L;;;;;N;;;;; 122C6;CUNEIFORM SIGN SHIM;Lo;0;L;;;;;N;;;;; 122C7;CUNEIFORM SIGN SHIM TIMES A;Lo;0;L;;;;;N;;;;; 122C8;CUNEIFORM SIGN SHIM TIMES BAL;Lo;0;L;;;;;N;;;;; 122C9;CUNEIFORM SIGN SHIM TIMES BULUG;Lo;0;L;;;;;N;;;;; 122CA;CUNEIFORM SIGN SHIM TIMES DIN;Lo;0;L;;;;;N;;;;; 122CB;CUNEIFORM SIGN SHIM TIMES GAR;Lo;0;L;;;;;N;;;;; 122CC;CUNEIFORM SIGN SHIM TIMES IGI;Lo;0;L;;;;;N;;;;; 122CD;CUNEIFORM SIGN SHIM TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 122CE;CUNEIFORM SIGN SHIM TIMES KUSHU2;Lo;0;L;;;;;N;;;;; 122CF;CUNEIFORM SIGN SHIM TIMES LUL;Lo;0;L;;;;;N;;;;; 122D0;CUNEIFORM SIGN SHIM TIMES MUG;Lo;0;L;;;;;N;;;;; 122D1;CUNEIFORM SIGN SHIM TIMES SAL;Lo;0;L;;;;;N;;;;; 122D2;CUNEIFORM SIGN SHINIG;Lo;0;L;;;;;N;;;;; 122D3;CUNEIFORM SIGN SHIR;Lo;0;L;;;;;N;;;;; 122D4;CUNEIFORM SIGN SHIR TENU;Lo;0;L;;;;;N;;;;; 122D5;CUNEIFORM SIGN SHIR OVER SHIR BUR OVER BUR;Lo;0;L;;;;;N;;;;; 122D6;CUNEIFORM SIGN SHITA;Lo;0;L;;;;;N;;;;; 122D7;CUNEIFORM SIGN SHU;Lo;0;L;;;;;N;;;;; 122D8;CUNEIFORM SIGN SHU OVER INVERTED SHU;Lo;0;L;;;;;N;;;;; 122D9;CUNEIFORM SIGN SHU2;Lo;0;L;;;;;N;;;;; 122DA;CUNEIFORM SIGN SHUBUR;Lo;0;L;;;;;N;;;;; 122DB;CUNEIFORM SIGN SI;Lo;0;L;;;;;N;;;;; 122DC;CUNEIFORM SIGN SI GUNU;Lo;0;L;;;;;N;;;;; 122DD;CUNEIFORM SIGN SIG;Lo;0;L;;;;;N;;;;; 122DE;CUNEIFORM SIGN SIG4;Lo;0;L;;;;;N;;;;; 122DF;CUNEIFORM SIGN SIG4 OVER SIG4 SHU2;Lo;0;L;;;;;N;;;;; 122E0;CUNEIFORM SIGN SIK2;Lo;0;L;;;;;N;;;;; 122E1;CUNEIFORM SIGN SILA3;Lo;0;L;;;;;N;;;;; 122E2;CUNEIFORM SIGN SU;Lo;0;L;;;;;N;;;;; 122E3;CUNEIFORM SIGN SU OVER SU;Lo;0;L;;;;;N;;;;; 122E4;CUNEIFORM SIGN SUD;Lo;0;L;;;;;N;;;;; 122E5;CUNEIFORM SIGN SUD2;Lo;0;L;;;;;N;;;;; 122E6;CUNEIFORM SIGN SUHUR;Lo;0;L;;;;;N;;;;; 122E7;CUNEIFORM SIGN SUM;Lo;0;L;;;;;N;;;;; 122E8;CUNEIFORM SIGN SUMASH;Lo;0;L;;;;;N;;;;; 122E9;CUNEIFORM SIGN SUR;Lo;0;L;;;;;N;;;;; 122EA;CUNEIFORM SIGN SUR9;Lo;0;L;;;;;N;;;;; 122EB;CUNEIFORM SIGN TA;Lo;0;L;;;;;N;;;;; 122EC;CUNEIFORM SIGN TA ASTERISK;Lo;0;L;;;;;N;;;;; 122ED;CUNEIFORM SIGN TA TIMES HI;Lo;0;L;;;;;N;;;;; 122EE;CUNEIFORM SIGN TA TIMES MI;Lo;0;L;;;;;N;;;;; 122EF;CUNEIFORM SIGN TA GUNU;Lo;0;L;;;;;N;;;;; 122F0;CUNEIFORM SIGN TAB;Lo;0;L;;;;;N;;;;; 122F1;CUNEIFORM SIGN TAB OVER TAB NI OVER NI DISH OVER DISH;Lo;0;L;;;;;N;;;;; 122F2;CUNEIFORM SIGN TAB SQUARED;Lo;0;L;;;;;N;;;;; 122F3;CUNEIFORM SIGN TAG;Lo;0;L;;;;;N;;;;; 122F4;CUNEIFORM SIGN TAG TIMES BI;Lo;0;L;;;;;N;;;;; 122F5;CUNEIFORM SIGN TAG TIMES GUD;Lo;0;L;;;;;N;;;;; 122F6;CUNEIFORM SIGN TAG TIMES SHE;Lo;0;L;;;;;N;;;;; 122F7;CUNEIFORM SIGN TAG TIMES SHU;Lo;0;L;;;;;N;;;;; 122F8;CUNEIFORM SIGN TAG TIMES TUG2;Lo;0;L;;;;;N;;;;; 122F9;CUNEIFORM SIGN TAG TIMES UD;Lo;0;L;;;;;N;;;;; 122FA;CUNEIFORM SIGN TAK4;Lo;0;L;;;;;N;;;;; 122FB;CUNEIFORM SIGN TAR;Lo;0;L;;;;;N;;;;; 122FC;CUNEIFORM SIGN TE;Lo;0;L;;;;;N;;;;; 122FD;CUNEIFORM SIGN TE GUNU;Lo;0;L;;;;;N;;;;; 122FE;CUNEIFORM SIGN TI;Lo;0;L;;;;;N;;;;; 122FF;CUNEIFORM SIGN TI TENU;Lo;0;L;;;;;N;;;;; 12300;CUNEIFORM SIGN TIL;Lo;0;L;;;;;N;;;;; 12301;CUNEIFORM SIGN TIR;Lo;0;L;;;;;N;;;;; 12302;CUNEIFORM SIGN TIR TIMES TAK4;Lo;0;L;;;;;N;;;;; 12303;CUNEIFORM SIGN TIR OVER TIR;Lo;0;L;;;;;N;;;;; 12304;CUNEIFORM SIGN TIR OVER TIR GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; 12305;CUNEIFORM SIGN TU;Lo;0;L;;;;;N;;;;; 12306;CUNEIFORM SIGN TUG2;Lo;0;L;;;;;N;;;;; 12307;CUNEIFORM SIGN TUK;Lo;0;L;;;;;N;;;;; 12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;; 12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;; 1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;; 1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;; 1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;; 1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;; 1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;; 1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;; 12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;; 12311;CUNEIFORM SIGN U2;Lo;0;L;;;;;N;;;;; 12312;CUNEIFORM SIGN UB;Lo;0;L;;;;;N;;;;; 12313;CUNEIFORM SIGN UD;Lo;0;L;;;;;N;;;;; 12314;CUNEIFORM SIGN UD KUSHU2;Lo;0;L;;;;;N;;;;; 12315;CUNEIFORM SIGN UD TIMES BAD;Lo;0;L;;;;;N;;;;; 12316;CUNEIFORM SIGN UD TIMES MI;Lo;0;L;;;;;N;;;;; 12317;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; 12318;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U GUNU;Lo;0;L;;;;;N;;;;; 12319;CUNEIFORM SIGN UD GUNU;Lo;0;L;;;;;N;;;;; 1231A;CUNEIFORM SIGN UD SHESHIG;Lo;0;L;;;;;N;;;;; 1231B;CUNEIFORM SIGN UD SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;; 1231C;CUNEIFORM SIGN UDUG;Lo;0;L;;;;;N;;;;; 1231D;CUNEIFORM SIGN UM;Lo;0;L;;;;;N;;;;; 1231E;CUNEIFORM SIGN UM TIMES LAGAB;Lo;0;L;;;;;N;;;;; 1231F;CUNEIFORM SIGN UM TIMES ME PLUS DA;Lo;0;L;;;;;N;;;;; 12320;CUNEIFORM SIGN UM TIMES SHA3;Lo;0;L;;;;;N;;;;; 12321;CUNEIFORM SIGN UM TIMES U;Lo;0;L;;;;;N;;;;; 12322;CUNEIFORM SIGN UMBIN;Lo;0;L;;;;;N;;;;; 12323;CUNEIFORM SIGN UMUM;Lo;0;L;;;;;N;;;;; 12324;CUNEIFORM SIGN UMUM TIMES KASKAL;Lo;0;L;;;;;N;;;;; 12325;CUNEIFORM SIGN UMUM TIMES PA;Lo;0;L;;;;;N;;;;; 12326;CUNEIFORM SIGN UN;Lo;0;L;;;;;N;;;;; 12327;CUNEIFORM SIGN UN GUNU;Lo;0;L;;;;;N;;;;; 12328;CUNEIFORM SIGN UR;Lo;0;L;;;;;N;;;;; 12329;CUNEIFORM SIGN UR CROSSING UR;Lo;0;L;;;;;N;;;;; 1232A;CUNEIFORM SIGN UR SHESHIG;Lo;0;L;;;;;N;;;;; 1232B;CUNEIFORM SIGN UR2;Lo;0;L;;;;;N;;;;; 1232C;CUNEIFORM SIGN UR2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;; 1232D;CUNEIFORM SIGN UR2 TIMES A PLUS NA;Lo;0;L;;;;;N;;;;; 1232E;CUNEIFORM SIGN UR2 TIMES AL;Lo;0;L;;;;;N;;;;; 1232F;CUNEIFORM SIGN UR2 TIMES HA;Lo;0;L;;;;;N;;;;; 12330;CUNEIFORM SIGN UR2 TIMES NUN;Lo;0;L;;;;;N;;;;; 12331;CUNEIFORM SIGN UR2 TIMES U2;Lo;0;L;;;;;N;;;;; 12332;CUNEIFORM SIGN UR2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; 12333;CUNEIFORM SIGN UR2 TIMES U2 PLUS BI;Lo;0;L;;;;;N;;;;; 12334;CUNEIFORM SIGN UR4;Lo;0;L;;;;;N;;;;; 12335;CUNEIFORM SIGN URI;Lo;0;L;;;;;N;;;;; 12336;CUNEIFORM SIGN URI3;Lo;0;L;;;;;N;;;;; 12337;CUNEIFORM SIGN URU;Lo;0;L;;;;;N;;;;; 12338;CUNEIFORM SIGN URU TIMES A;Lo;0;L;;;;;N;;;;; 12339;CUNEIFORM SIGN URU TIMES ASHGAB;Lo;0;L;;;;;N;;;;; 1233A;CUNEIFORM SIGN URU TIMES BAR;Lo;0;L;;;;;N;;;;; 1233B;CUNEIFORM SIGN URU TIMES DUN;Lo;0;L;;;;;N;;;;; 1233C;CUNEIFORM SIGN URU TIMES GA;Lo;0;L;;;;;N;;;;; 1233D;CUNEIFORM SIGN URU TIMES GAL;Lo;0;L;;;;;N;;;;; 1233E;CUNEIFORM SIGN URU TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1233F;CUNEIFORM SIGN URU TIMES GAR;Lo;0;L;;;;;N;;;;; 12340;CUNEIFORM SIGN URU TIMES GU;Lo;0;L;;;;;N;;;;; 12341;CUNEIFORM SIGN URU TIMES HA;Lo;0;L;;;;;N;;;;; 12342;CUNEIFORM SIGN URU TIMES IGI;Lo;0;L;;;;;N;;;;; 12343;CUNEIFORM SIGN URU TIMES IM;Lo;0;L;;;;;N;;;;; 12344;CUNEIFORM SIGN URU TIMES ISH;Lo;0;L;;;;;N;;;;; 12345;CUNEIFORM SIGN URU TIMES KI;Lo;0;L;;;;;N;;;;; 12346;CUNEIFORM SIGN URU TIMES LUM;Lo;0;L;;;;;N;;;;; 12347;CUNEIFORM SIGN URU TIMES MIN;Lo;0;L;;;;;N;;;;; 12348;CUNEIFORM SIGN URU TIMES PA;Lo;0;L;;;;;N;;;;; 12349;CUNEIFORM SIGN URU TIMES SHE;Lo;0;L;;;;;N;;;;; 1234A;CUNEIFORM SIGN URU TIMES SIG4;Lo;0;L;;;;;N;;;;; 1234B;CUNEIFORM SIGN URU TIMES TU;Lo;0;L;;;;;N;;;;; 1234C;CUNEIFORM SIGN URU TIMES U PLUS GUD;Lo;0;L;;;;;N;;;;; 1234D;CUNEIFORM SIGN URU TIMES UD;Lo;0;L;;;;;N;;;;; 1234E;CUNEIFORM SIGN URU TIMES URUDA;Lo;0;L;;;;;N;;;;; 1234F;CUNEIFORM SIGN URUDA;Lo;0;L;;;;;N;;;;; 12350;CUNEIFORM SIGN URUDA TIMES U;Lo;0;L;;;;;N;;;;; 12351;CUNEIFORM SIGN USH;Lo;0;L;;;;;N;;;;; 12352;CUNEIFORM SIGN USH TIMES A;Lo;0;L;;;;;N;;;;; 12353;CUNEIFORM SIGN USH TIMES KU;Lo;0;L;;;;;N;;;;; 12354;CUNEIFORM SIGN USH TIMES KUR;Lo;0;L;;;;;N;;;;; 12355;CUNEIFORM SIGN USH TIMES TAK4;Lo;0;L;;;;;N;;;;; 12356;CUNEIFORM SIGN USHX;Lo;0;L;;;;;N;;;;; 12357;CUNEIFORM SIGN USH2;Lo;0;L;;;;;N;;;;; 12358;CUNEIFORM SIGN USHUMX;Lo;0;L;;;;;N;;;;; 12359;CUNEIFORM SIGN UTUKI;Lo;0;L;;;;;N;;;;; 1235A;CUNEIFORM SIGN UZ3;Lo;0;L;;;;;N;;;;; 1235B;CUNEIFORM SIGN UZ3 TIMES KASKAL;Lo;0;L;;;;;N;;;;; 1235C;CUNEIFORM SIGN UZU;Lo;0;L;;;;;N;;;;; 1235D;CUNEIFORM SIGN ZA;Lo;0;L;;;;;N;;;;; 1235E;CUNEIFORM SIGN ZA TENU;Lo;0;L;;;;;N;;;;; 1235F;CUNEIFORM SIGN ZA SQUARED TIMES KUR;Lo;0;L;;;;;N;;;;; 12360;CUNEIFORM SIGN ZAG;Lo;0;L;;;;;N;;;;; 12361;CUNEIFORM SIGN ZAMX;Lo;0;L;;;;;N;;;;; 12362;CUNEIFORM SIGN ZE2;Lo;0;L;;;;;N;;;;; 12363;CUNEIFORM SIGN ZI;Lo;0;L;;;;;N;;;;; 12364;CUNEIFORM SIGN ZI OVER ZI;Lo;0;L;;;;;N;;;;; 12365;CUNEIFORM SIGN ZI3;Lo;0;L;;;;;N;;;;; 12366;CUNEIFORM SIGN ZIB;Lo;0;L;;;;;N;;;;; 12367;CUNEIFORM SIGN ZIB KABA TENU;Lo;0;L;;;;;N;;;;; 12368;CUNEIFORM SIGN ZIG;Lo;0;L;;;;;N;;;;; 12369;CUNEIFORM SIGN ZIZ2;Lo;0;L;;;;;N;;;;; 1236A;CUNEIFORM SIGN ZU;Lo;0;L;;;;;N;;;;; 1236B;CUNEIFORM SIGN ZU5;Lo;0;L;;;;;N;;;;; 1236C;CUNEIFORM SIGN ZU5 TIMES A;Lo;0;L;;;;;N;;;;; 1236D;CUNEIFORM SIGN ZUBUR;Lo;0;L;;;;;N;;;;; 1236E;CUNEIFORM SIGN ZUM;Lo;0;L;;;;;N;;;;; 1236F;CUNEIFORM SIGN KAP ELAMITE;Lo;0;L;;;;;N;;;;; 12370;CUNEIFORM SIGN AB TIMES NUN;Lo;0;L;;;;;N;;;;; 12371;CUNEIFORM SIGN AB2 TIMES A;Lo;0;L;;;;;N;;;;; 12372;CUNEIFORM SIGN AMAR TIMES KUG;Lo;0;L;;;;;N;;;;; 12373;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS MASH;Lo;0;L;;;;;N;;;;; 12374;CUNEIFORM SIGN DAG3;Lo;0;L;;;;;N;;;;; 12375;CUNEIFORM SIGN DISH PLUS SHU;Lo;0;L;;;;;N;;;;; 12376;CUNEIFORM SIGN DUB TIMES SHE;Lo;0;L;;;;;N;;;;; 12377;CUNEIFORM SIGN EZEN TIMES GUD;Lo;0;L;;;;;N;;;;; 12378;CUNEIFORM SIGN EZEN TIMES SHE;Lo;0;L;;;;;N;;;;; 12379;CUNEIFORM SIGN GA2 TIMES AN PLUS KAK PLUS A;Lo;0;L;;;;;N;;;;; 1237A;CUNEIFORM SIGN GA2 TIMES ASH2;Lo;0;L;;;;;N;;;;; 1237B;CUNEIFORM SIGN GE22;Lo;0;L;;;;;N;;;;; 1237C;CUNEIFORM SIGN GIG;Lo;0;L;;;;;N;;;;; 1237D;CUNEIFORM SIGN HUSH;Lo;0;L;;;;;N;;;;; 1237E;CUNEIFORM SIGN KA TIMES ANSHE;Lo;0;L;;;;;N;;;;; 1237F;CUNEIFORM SIGN KA TIMES ASH3;Lo;0;L;;;;;N;;;;; 12380;CUNEIFORM SIGN KA TIMES GISH;Lo;0;L;;;;;N;;;;; 12381;CUNEIFORM SIGN KA TIMES GUD;Lo;0;L;;;;;N;;;;; 12382;CUNEIFORM SIGN KA TIMES HI TIMES ASH2;Lo;0;L;;;;;N;;;;; 12383;CUNEIFORM SIGN KA TIMES LUM;Lo;0;L;;;;;N;;;;; 12384;CUNEIFORM SIGN KA TIMES PA;Lo;0;L;;;;;N;;;;; 12385;CUNEIFORM SIGN KA TIMES SHUL;Lo;0;L;;;;;N;;;;; 12386;CUNEIFORM SIGN KA TIMES TU;Lo;0;L;;;;;N;;;;; 12387;CUNEIFORM SIGN KA TIMES UR2;Lo;0;L;;;;;N;;;;; 12388;CUNEIFORM SIGN LAGAB TIMES GI;Lo;0;L;;;;;N;;;;; 12389;CUNEIFORM SIGN LU2 SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;; 1238A;CUNEIFORM SIGN LU2 TIMES ESH2 PLUS LAL;Lo;0;L;;;;;N;;;;; 1238B;CUNEIFORM SIGN LU2 TIMES SHU;Lo;0;L;;;;;N;;;;; 1238C;CUNEIFORM SIGN MESH;Lo;0;L;;;;;N;;;;; 1238D;CUNEIFORM SIGN MUSH3 TIMES ZA;Lo;0;L;;;;;N;;;;; 1238E;CUNEIFORM SIGN NA4;Lo;0;L;;;;;N;;;;; 1238F;CUNEIFORM SIGN NIN;Lo;0;L;;;;;N;;;;; 12390;CUNEIFORM SIGN NIN9;Lo;0;L;;;;;N;;;;; 12391;CUNEIFORM SIGN NINDA2 TIMES BAL;Lo;0;L;;;;;N;;;;; 12392;CUNEIFORM SIGN NINDA2 TIMES GI;Lo;0;L;;;;;N;;;;; 12393;CUNEIFORM SIGN NU11 ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;; 12394;CUNEIFORM SIGN PESH2 ASTERISK;Lo;0;L;;;;;N;;;;; 12395;CUNEIFORM SIGN PIR2;Lo;0;L;;;;;N;;;;; 12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;; 12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;; 12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;; 12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;; 12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;; 12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;; 12403;CUNEIFORM NUMERIC SIGN FIVE ASH;Nl;0;L;;;;5;N;;;;; 12404;CUNEIFORM NUMERIC SIGN SIX ASH;Nl;0;L;;;;6;N;;;;; 12405;CUNEIFORM NUMERIC SIGN SEVEN ASH;Nl;0;L;;;;7;N;;;;; 12406;CUNEIFORM NUMERIC SIGN EIGHT ASH;Nl;0;L;;;;8;N;;;;; 12407;CUNEIFORM NUMERIC SIGN NINE ASH;Nl;0;L;;;;9;N;;;;; 12408;CUNEIFORM NUMERIC SIGN THREE DISH;Nl;0;L;;;;3;N;;;;; 12409;CUNEIFORM NUMERIC SIGN FOUR DISH;Nl;0;L;;;;4;N;;;;; 1240A;CUNEIFORM NUMERIC SIGN FIVE DISH;Nl;0;L;;;;5;N;;;;; 1240B;CUNEIFORM NUMERIC SIGN SIX DISH;Nl;0;L;;;;6;N;;;;; 1240C;CUNEIFORM NUMERIC SIGN SEVEN DISH;Nl;0;L;;;;7;N;;;;; 1240D;CUNEIFORM NUMERIC SIGN EIGHT DISH;Nl;0;L;;;;8;N;;;;; 1240E;CUNEIFORM NUMERIC SIGN NINE DISH;Nl;0;L;;;;9;N;;;;; 1240F;CUNEIFORM NUMERIC SIGN FOUR U;Nl;0;L;;;;4;N;;;;; 12410;CUNEIFORM NUMERIC SIGN FIVE U;Nl;0;L;;;;5;N;;;;; 12411;CUNEIFORM NUMERIC SIGN SIX U;Nl;0;L;;;;6;N;;;;; 12412;CUNEIFORM NUMERIC SIGN SEVEN U;Nl;0;L;;;;7;N;;;;; 12413;CUNEIFORM NUMERIC SIGN EIGHT U;Nl;0;L;;;;8;N;;;;; 12414;CUNEIFORM NUMERIC SIGN NINE U;Nl;0;L;;;;9;N;;;;; 12415;CUNEIFORM NUMERIC SIGN ONE GESH2;Nl;0;L;;;;1;N;;;;; 12416;CUNEIFORM NUMERIC SIGN TWO GESH2;Nl;0;L;;;;2;N;;;;; 12417;CUNEIFORM NUMERIC SIGN THREE GESH2;Nl;0;L;;;;3;N;;;;; 12418;CUNEIFORM NUMERIC SIGN FOUR GESH2;Nl;0;L;;;;4;N;;;;; 12419;CUNEIFORM NUMERIC SIGN FIVE GESH2;Nl;0;L;;;;5;N;;;;; 1241A;CUNEIFORM NUMERIC SIGN SIX GESH2;Nl;0;L;;;;6;N;;;;; 1241B;CUNEIFORM NUMERIC SIGN SEVEN GESH2;Nl;0;L;;;;7;N;;;;; 1241C;CUNEIFORM NUMERIC SIGN EIGHT GESH2;Nl;0;L;;;;8;N;;;;; 1241D;CUNEIFORM NUMERIC SIGN NINE GESH2;Nl;0;L;;;;9;N;;;;; 1241E;CUNEIFORM NUMERIC SIGN ONE GESHU;Nl;0;L;;;;1;N;;;;; 1241F;CUNEIFORM NUMERIC SIGN TWO GESHU;Nl;0;L;;;;2;N;;;;; 12420;CUNEIFORM NUMERIC SIGN THREE GESHU;Nl;0;L;;;;3;N;;;;; 12421;CUNEIFORM NUMERIC SIGN FOUR GESHU;Nl;0;L;;;;4;N;;;;; 12422;CUNEIFORM NUMERIC SIGN FIVE GESHU;Nl;0;L;;;;5;N;;;;; 12423;CUNEIFORM NUMERIC SIGN TWO SHAR2;Nl;0;L;;;;2;N;;;;; 12424;CUNEIFORM NUMERIC SIGN THREE SHAR2;Nl;0;L;;;;3;N;;;;; 12425;CUNEIFORM NUMERIC SIGN THREE SHAR2 VARIANT FORM;Nl;0;L;;;;3;N;;;;; 12426;CUNEIFORM NUMERIC SIGN FOUR SHAR2;Nl;0;L;;;;4;N;;;;; 12427;CUNEIFORM NUMERIC SIGN FIVE SHAR2;Nl;0;L;;;;5;N;;;;; 12428;CUNEIFORM NUMERIC SIGN SIX SHAR2;Nl;0;L;;;;6;N;;;;; 12429;CUNEIFORM NUMERIC SIGN SEVEN SHAR2;Nl;0;L;;;;7;N;;;;; 1242A;CUNEIFORM NUMERIC SIGN EIGHT SHAR2;Nl;0;L;;;;8;N;;;;; 1242B;CUNEIFORM NUMERIC SIGN NINE SHAR2;Nl;0;L;;;;9;N;;;;; 1242C;CUNEIFORM NUMERIC SIGN ONE SHARU;Nl;0;L;;;;1;N;;;;; 1242D;CUNEIFORM NUMERIC SIGN TWO SHARU;Nl;0;L;;;;2;N;;;;; 1242E;CUNEIFORM NUMERIC SIGN THREE SHARU;Nl;0;L;;;;3;N;;;;; 1242F;CUNEIFORM NUMERIC SIGN THREE SHARU VARIANT FORM;Nl;0;L;;;;3;N;;;;; 12430;CUNEIFORM NUMERIC SIGN FOUR SHARU;Nl;0;L;;;;4;N;;;;; 12431;CUNEIFORM NUMERIC SIGN FIVE SHARU;Nl;0;L;;;;5;N;;;;; 12432;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS DISH;Nl;0;L;;;;216000;N;;;;; 12433;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS MIN;Nl;0;L;;;;432000;N;;;;; 12434;CUNEIFORM NUMERIC SIGN ONE BURU;Nl;0;L;;;;1;N;;;;; 12435;CUNEIFORM NUMERIC SIGN TWO BURU;Nl;0;L;;;;2;N;;;;; 12436;CUNEIFORM NUMERIC SIGN THREE BURU;Nl;0;L;;;;3;N;;;;; 12437;CUNEIFORM NUMERIC SIGN THREE BURU VARIANT FORM;Nl;0;L;;;;3;N;;;;; 12438;CUNEIFORM NUMERIC SIGN FOUR BURU;Nl;0;L;;;;4;N;;;;; 12439;CUNEIFORM NUMERIC SIGN FIVE BURU;Nl;0;L;;;;5;N;;;;; 1243A;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH16;Nl;0;L;;;;3;N;;;;; 1243B;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH21;Nl;0;L;;;;3;N;;;;; 1243C;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU;Nl;0;L;;;;4;N;;;;; 1243D;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU4;Nl;0;L;;;;4;N;;;;; 1243E;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU A;Nl;0;L;;;;4;N;;;;; 1243F;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU B;Nl;0;L;;;;4;N;;;;; 12440;CUNEIFORM NUMERIC SIGN SIX VARIANT FORM ASH9;Nl;0;L;;;;6;N;;;;; 12441;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN3;Nl;0;L;;;;7;N;;;;; 12442;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN A;Nl;0;L;;;;7;N;;;;; 12443;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN B;Nl;0;L;;;;7;N;;;;; 12444;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU;Nl;0;L;;;;8;N;;;;; 12445;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU3;Nl;0;L;;;;8;N;;;;; 12446;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU;Nl;0;L;;;;9;N;;;;; 12447;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU3;Nl;0;L;;;;9;N;;;;; 12448;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU4;Nl;0;L;;;;9;N;;;;; 12449;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU A;Nl;0;L;;;;9;N;;;;; 1244A;CUNEIFORM NUMERIC SIGN TWO ASH TENU;Nl;0;L;;;;2;N;;;;; 1244B;CUNEIFORM NUMERIC SIGN THREE ASH TENU;Nl;0;L;;;;3;N;;;;; 1244C;CUNEIFORM NUMERIC SIGN FOUR ASH TENU;Nl;0;L;;;;4;N;;;;; 1244D;CUNEIFORM NUMERIC SIGN FIVE ASH TENU;Nl;0;L;;;;5;N;;;;; 1244E;CUNEIFORM NUMERIC SIGN SIX ASH TENU;Nl;0;L;;;;6;N;;;;; 1244F;CUNEIFORM NUMERIC SIGN ONE BAN2;Nl;0;L;;;;1;N;;;;; 12450;CUNEIFORM NUMERIC SIGN TWO BAN2;Nl;0;L;;;;2;N;;;;; 12451;CUNEIFORM NUMERIC SIGN THREE BAN2;Nl;0;L;;;;3;N;;;;; 12452;CUNEIFORM NUMERIC SIGN FOUR BAN2;Nl;0;L;;;;4;N;;;;; 12453;CUNEIFORM NUMERIC SIGN FOUR BAN2 VARIANT FORM;Nl;0;L;;;;4;N;;;;; 12454;CUNEIFORM NUMERIC SIGN FIVE BAN2;Nl;0;L;;;;5;N;;;;; 12455;CUNEIFORM NUMERIC SIGN FIVE BAN2 VARIANT FORM;Nl;0;L;;;;5;N;;;;; 12456;CUNEIFORM NUMERIC SIGN NIGIDAMIN;Nl;0;L;;;;2;N;;;;; 12457;CUNEIFORM NUMERIC SIGN NIGIDAESH;Nl;0;L;;;;3;N;;;;; 12458;CUNEIFORM NUMERIC SIGN ONE ESHE3;Nl;0;L;;;;1;N;;;;; 12459;CUNEIFORM NUMERIC SIGN TWO ESHE3;Nl;0;L;;;;2;N;;;;; 1245A;CUNEIFORM NUMERIC SIGN ONE THIRD DISH;Nl;0;L;;;;1/3;N;;;;; 1245B;CUNEIFORM NUMERIC SIGN TWO THIRDS DISH;Nl;0;L;;;;2/3;N;;;;; 1245C;CUNEIFORM NUMERIC SIGN FIVE SIXTHS DISH;Nl;0;L;;;;5/6;N;;;;; 1245D;CUNEIFORM NUMERIC SIGN ONE THIRD VARIANT FORM A;Nl;0;L;;;;1/3;N;;;;; 1245E;CUNEIFORM NUMERIC SIGN TWO THIRDS VARIANT FORM A;Nl;0;L;;;;2/3;N;;;;; 1245F;CUNEIFORM NUMERIC SIGN ONE EIGHTH ASH;Nl;0;L;;;;1/8;N;;;;; 12460;CUNEIFORM NUMERIC SIGN ONE QUARTER ASH;Nl;0;L;;;;1/4;N;;;;; 12461;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE SIXTH;Nl;0;L;;;;1/6;N;;;;; 12462;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER;Nl;0;L;;;;1/4;N;;;;; 12463;CUNEIFORM NUMERIC SIGN ONE QUARTER GUR;Nl;0;L;;;;1/4;N;;;;; 12464;CUNEIFORM NUMERIC SIGN ONE HALF GUR;Nl;0;L;;;;1/2;N;;;;; 12465;CUNEIFORM NUMERIC SIGN ELAMITE ONE THIRD;Nl;0;L;;;;1/3;N;;;;; 12466;CUNEIFORM NUMERIC SIGN ELAMITE TWO THIRDS;Nl;0;L;;;;2/3;N;;;;; 12467;CUNEIFORM NUMERIC SIGN ELAMITE FORTY;Nl;0;L;;;;40;N;;;;; 12468;CUNEIFORM NUMERIC SIGN ELAMITE FIFTY;Nl;0;L;;;;50;N;;;;; 12469;CUNEIFORM NUMERIC SIGN FOUR U VARIANT FORM;Nl;0;L;;;;4;N;;;;; 1246A;CUNEIFORM NUMERIC SIGN FIVE U VARIANT FORM;Nl;0;L;;;;5;N;;;;; 1246B;CUNEIFORM NUMERIC SIGN SIX U VARIANT FORM;Nl;0;L;;;;6;N;;;;; 1246C;CUNEIFORM NUMERIC SIGN SEVEN U VARIANT FORM;Nl;0;L;;;;7;N;;;;; 1246D;CUNEIFORM NUMERIC SIGN EIGHT U VARIANT FORM;Nl;0;L;;;;8;N;;;;; 1246E;CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM;Nl;0;L;;;;9;N;;;;; 12470;CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER;Po;0;L;;;;;N;;;;; 12471;CUNEIFORM PUNCTUATION SIGN VERTICAL COLON;Po;0;L;;;;;N;;;;; 12472;CUNEIFORM PUNCTUATION SIGN DIAGONAL COLON;Po;0;L;;;;;N;;;;; 12473;CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON;Po;0;L;;;;;N;;;;; 12474;CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON;Po;0;L;;;;;N;;;;; 12480;CUNEIFORM SIGN AB TIMES NUN TENU;Lo;0;L;;;;;N;;;;; 12481;CUNEIFORM SIGN AB TIMES SHU2;Lo;0;L;;;;;N;;;;; 12482;CUNEIFORM SIGN AD TIMES ESH2;Lo;0;L;;;;;N;;;;; 12483;CUNEIFORM SIGN BAD TIMES DISH TENU;Lo;0;L;;;;;N;;;;; 12484;CUNEIFORM SIGN BAHAR2 TIMES AB2;Lo;0;L;;;;;N;;;;; 12485;CUNEIFORM SIGN BAHAR2 TIMES NI;Lo;0;L;;;;;N;;;;; 12486;CUNEIFORM SIGN BAHAR2 TIMES ZA;Lo;0;L;;;;;N;;;;; 12487;CUNEIFORM SIGN BU OVER BU TIMES NA2;Lo;0;L;;;;;N;;;;; 12488;CUNEIFORM SIGN DA TIMES TAK4;Lo;0;L;;;;;N;;;;; 12489;CUNEIFORM SIGN DAG TIMES KUR;Lo;0;L;;;;;N;;;;; 1248A;CUNEIFORM SIGN DIM TIMES IGI;Lo;0;L;;;;;N;;;;; 1248B;CUNEIFORM SIGN DIM TIMES U U U;Lo;0;L;;;;;N;;;;; 1248C;CUNEIFORM SIGN DIM2 TIMES UD;Lo;0;L;;;;;N;;;;; 1248D;CUNEIFORM SIGN DUG TIMES ANSHE;Lo;0;L;;;;;N;;;;; 1248E;CUNEIFORM SIGN DUG TIMES ASH;Lo;0;L;;;;;N;;;;; 1248F;CUNEIFORM SIGN DUG TIMES ASH AT LEFT;Lo;0;L;;;;;N;;;;; 12490;CUNEIFORM SIGN DUG TIMES DIN;Lo;0;L;;;;;N;;;;; 12491;CUNEIFORM SIGN DUG TIMES DUN;Lo;0;L;;;;;N;;;;; 12492;CUNEIFORM SIGN DUG TIMES ERIN2;Lo;0;L;;;;;N;;;;; 12493;CUNEIFORM SIGN DUG TIMES GA;Lo;0;L;;;;;N;;;;; 12494;CUNEIFORM SIGN DUG TIMES GI;Lo;0;L;;;;;N;;;;; 12495;CUNEIFORM SIGN DUG TIMES GIR2 GUNU;Lo;0;L;;;;;N;;;;; 12496;CUNEIFORM SIGN DUG TIMES GISH;Lo;0;L;;;;;N;;;;; 12497;CUNEIFORM SIGN DUG TIMES HA;Lo;0;L;;;;;N;;;;; 12498;CUNEIFORM SIGN DUG TIMES HI;Lo;0;L;;;;;N;;;;; 12499;CUNEIFORM SIGN DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 1249A;CUNEIFORM SIGN DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;; 1249B;CUNEIFORM SIGN DUG TIMES KUR;Lo;0;L;;;;;N;;;;; 1249C;CUNEIFORM SIGN DUG TIMES KUSHU2;Lo;0;L;;;;;N;;;;; 1249D;CUNEIFORM SIGN DUG TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;; 1249E;CUNEIFORM SIGN DUG TIMES LAK-020;Lo;0;L;;;;;N;;;;; 1249F;CUNEIFORM SIGN DUG TIMES LAM;Lo;0;L;;;;;N;;;;; 124A0;CUNEIFORM SIGN DUG TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;; 124A1;CUNEIFORM SIGN DUG TIMES LUH PLUS GISH;Lo;0;L;;;;;N;;;;; 124A2;CUNEIFORM SIGN DUG TIMES MASH;Lo;0;L;;;;;N;;;;; 124A3;CUNEIFORM SIGN DUG TIMES MES;Lo;0;L;;;;;N;;;;; 124A4;CUNEIFORM SIGN DUG TIMES MI;Lo;0;L;;;;;N;;;;; 124A5;CUNEIFORM SIGN DUG TIMES NI;Lo;0;L;;;;;N;;;;; 124A6;CUNEIFORM SIGN DUG TIMES PI;Lo;0;L;;;;;N;;;;; 124A7;CUNEIFORM SIGN DUG TIMES SHE;Lo;0;L;;;;;N;;;;; 124A8;CUNEIFORM SIGN DUG TIMES SI GUNU;Lo;0;L;;;;;N;;;;; 124A9;CUNEIFORM SIGN E2 TIMES KUR;Lo;0;L;;;;;N;;;;; 124AA;CUNEIFORM SIGN E2 TIMES PAP;Lo;0;L;;;;;N;;;;; 124AB;CUNEIFORM SIGN ERIN2 X;Lo;0;L;;;;;N;;;;; 124AC;CUNEIFORM SIGN ESH2 CROSSING ESH2;Lo;0;L;;;;;N;;;;; 124AD;CUNEIFORM SIGN EZEN SHESHIG TIMES ASH;Lo;0;L;;;;;N;;;;; 124AE;CUNEIFORM SIGN EZEN SHESHIG TIMES HI;Lo;0;L;;;;;N;;;;; 124AF;CUNEIFORM SIGN EZEN SHESHIG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 124B0;CUNEIFORM SIGN EZEN SHESHIG TIMES LA;Lo;0;L;;;;;N;;;;; 124B1;CUNEIFORM SIGN EZEN SHESHIG TIMES LAL;Lo;0;L;;;;;N;;;;; 124B2;CUNEIFORM SIGN EZEN SHESHIG TIMES ME;Lo;0;L;;;;;N;;;;; 124B3;CUNEIFORM SIGN EZEN SHESHIG TIMES MES;Lo;0;L;;;;;N;;;;; 124B4;CUNEIFORM SIGN EZEN SHESHIG TIMES SU;Lo;0;L;;;;;N;;;;; 124B5;CUNEIFORM SIGN EZEN TIMES SU;Lo;0;L;;;;;N;;;;; 124B6;CUNEIFORM SIGN GA2 TIMES BAHAR2;Lo;0;L;;;;;N;;;;; 124B7;CUNEIFORM SIGN GA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;; 124B8;CUNEIFORM SIGN GA2 TIMES DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 124B9;CUNEIFORM SIGN GA2 TIMES DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;; 124BA;CUNEIFORM SIGN GA2 TIMES EREN;Lo;0;L;;;;;N;;;;; 124BB;CUNEIFORM SIGN GA2 TIMES GA;Lo;0;L;;;;;N;;;;; 124BC;CUNEIFORM SIGN GA2 TIMES GAR PLUS DI;Lo;0;L;;;;;N;;;;; 124BD;CUNEIFORM SIGN GA2 TIMES GAR PLUS NE;Lo;0;L;;;;;N;;;;; 124BE;CUNEIFORM SIGN GA2 TIMES HA PLUS A;Lo;0;L;;;;;N;;;;; 124BF;CUNEIFORM SIGN GA2 TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;; 124C0;CUNEIFORM SIGN GA2 TIMES LAM;Lo;0;L;;;;;N;;;;; 124C1;CUNEIFORM SIGN GA2 TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;; 124C2;CUNEIFORM SIGN GA2 TIMES LUH;Lo;0;L;;;;;N;;;;; 124C3;CUNEIFORM SIGN GA2 TIMES MUSH;Lo;0;L;;;;;N;;;;; 124C4;CUNEIFORM SIGN GA2 TIMES NE;Lo;0;L;;;;;N;;;;; 124C5;CUNEIFORM SIGN GA2 TIMES NE PLUS E2;Lo;0;L;;;;;N;;;;; 124C6;CUNEIFORM SIGN GA2 TIMES NE PLUS GI;Lo;0;L;;;;;N;;;;; 124C7;CUNEIFORM SIGN GA2 TIMES SHIM;Lo;0;L;;;;;N;;;;; 124C8;CUNEIFORM SIGN GA2 TIMES ZIZ2;Lo;0;L;;;;;N;;;;; 124C9;CUNEIFORM SIGN GABA ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;; 124CA;CUNEIFORM SIGN GESHTIN TIMES U;Lo;0;L;;;;;N;;;;; 124CB;CUNEIFORM SIGN GISH TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; 124CC;CUNEIFORM SIGN GU2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 124CD;CUNEIFORM SIGN GUD PLUS GISH TIMES TAK4;Lo;0;L;;;;;N;;;;; 124CE;CUNEIFORM SIGN HA TENU GUNU;Lo;0;L;;;;;N;;;;; 124CF;CUNEIFORM SIGN HI TIMES ASH OVER HI TIMES ASH;Lo;0;L;;;;;N;;;;; 124D0;CUNEIFORM SIGN KA TIMES BU;Lo;0;L;;;;;N;;;;; 124D1;CUNEIFORM SIGN KA TIMES KA;Lo;0;L;;;;;N;;;;; 124D2;CUNEIFORM SIGN KA TIMES U U U;Lo;0;L;;;;;N;;;;; 124D3;CUNEIFORM SIGN KA TIMES UR;Lo;0;L;;;;;N;;;;; 124D4;CUNEIFORM SIGN LAGAB TIMES ZU OVER ZU;Lo;0;L;;;;;N;;;;; 124D5;CUNEIFORM SIGN LAK-003;Lo;0;L;;;;;N;;;;; 124D6;CUNEIFORM SIGN LAK-021;Lo;0;L;;;;;N;;;;; 124D7;CUNEIFORM SIGN LAK-025;Lo;0;L;;;;;N;;;;; 124D8;CUNEIFORM SIGN LAK-030;Lo;0;L;;;;;N;;;;; 124D9;CUNEIFORM SIGN LAK-050;Lo;0;L;;;;;N;;;;; 124DA;CUNEIFORM SIGN LAK-051;Lo;0;L;;;;;N;;;;; 124DB;CUNEIFORM SIGN LAK-062;Lo;0;L;;;;;N;;;;; 124DC;CUNEIFORM SIGN LAK-079 OVER LAK-079 GUNU;Lo;0;L;;;;;N;;;;; 124DD;CUNEIFORM SIGN LAK-080;Lo;0;L;;;;;N;;;;; 124DE;CUNEIFORM SIGN LAK-081 OVER LAK-081;Lo;0;L;;;;;N;;;;; 124DF;CUNEIFORM SIGN LAK-092;Lo;0;L;;;;;N;;;;; 124E0;CUNEIFORM SIGN LAK-130;Lo;0;L;;;;;N;;;;; 124E1;CUNEIFORM SIGN LAK-142;Lo;0;L;;;;;N;;;;; 124E2;CUNEIFORM SIGN LAK-210;Lo;0;L;;;;;N;;;;; 124E3;CUNEIFORM SIGN LAK-219;Lo;0;L;;;;;N;;;;; 124E4;CUNEIFORM SIGN LAK-220;Lo;0;L;;;;;N;;;;; 124E5;CUNEIFORM SIGN LAK-225;Lo;0;L;;;;;N;;;;; 124E6;CUNEIFORM SIGN LAK-228;Lo;0;L;;;;;N;;;;; 124E7;CUNEIFORM SIGN LAK-238;Lo;0;L;;;;;N;;;;; 124E8;CUNEIFORM SIGN LAK-265;Lo;0;L;;;;;N;;;;; 124E9;CUNEIFORM SIGN LAK-266;Lo;0;L;;;;;N;;;;; 124EA;CUNEIFORM SIGN LAK-343;Lo;0;L;;;;;N;;;;; 124EB;CUNEIFORM SIGN LAK-347;Lo;0;L;;;;;N;;;;; 124EC;CUNEIFORM SIGN LAK-348;Lo;0;L;;;;;N;;;;; 124ED;CUNEIFORM SIGN LAK-383;Lo;0;L;;;;;N;;;;; 124EE;CUNEIFORM SIGN LAK-384;Lo;0;L;;;;;N;;;;; 124EF;CUNEIFORM SIGN LAK-390;Lo;0;L;;;;;N;;;;; 124F0;CUNEIFORM SIGN LAK-441;Lo;0;L;;;;;N;;;;; 124F1;CUNEIFORM SIGN LAK-449;Lo;0;L;;;;;N;;;;; 124F2;CUNEIFORM SIGN LAK-449 TIMES GU;Lo;0;L;;;;;N;;;;; 124F3;CUNEIFORM SIGN LAK-449 TIMES IGI;Lo;0;L;;;;;N;;;;; 124F4;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS LU3;Lo;0;L;;;;;N;;;;; 124F5;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; 124F6;CUNEIFORM SIGN LAK-449 TIMES U2 PLUS BA;Lo;0;L;;;;;N;;;;; 124F7;CUNEIFORM SIGN LAK-450;Lo;0;L;;;;;N;;;;; 124F8;CUNEIFORM SIGN LAK-457;Lo;0;L;;;;;N;;;;; 124F9;CUNEIFORM SIGN LAK-470;Lo;0;L;;;;;N;;;;; 124FA;CUNEIFORM SIGN LAK-483;Lo;0;L;;;;;N;;;;; 124FB;CUNEIFORM SIGN LAK-490;Lo;0;L;;;;;N;;;;; 124FC;CUNEIFORM SIGN LAK-492;Lo;0;L;;;;;N;;;;; 124FD;CUNEIFORM SIGN LAK-493;Lo;0;L;;;;;N;;;;; 124FE;CUNEIFORM SIGN LAK-495;Lo;0;L;;;;;N;;;;; 124FF;CUNEIFORM SIGN LAK-550;Lo;0;L;;;;;N;;;;; 12500;CUNEIFORM SIGN LAK-608;Lo;0;L;;;;;N;;;;; 12501;CUNEIFORM SIGN LAK-617;Lo;0;L;;;;;N;;;;; 12502;CUNEIFORM SIGN LAK-617 TIMES ASH;Lo;0;L;;;;;N;;;;; 12503;CUNEIFORM SIGN LAK-617 TIMES BAD;Lo;0;L;;;;;N;;;;; 12504;CUNEIFORM SIGN LAK-617 TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; 12505;CUNEIFORM SIGN LAK-617 TIMES KU3;Lo;0;L;;;;;N;;;;; 12506;CUNEIFORM SIGN LAK-617 TIMES LA;Lo;0;L;;;;;N;;;;; 12507;CUNEIFORM SIGN LAK-617 TIMES TAR;Lo;0;L;;;;;N;;;;; 12508;CUNEIFORM SIGN LAK-617 TIMES TE;Lo;0;L;;;;;N;;;;; 12509;CUNEIFORM SIGN LAK-617 TIMES U2;Lo;0;L;;;;;N;;;;; 1250A;CUNEIFORM SIGN LAK-617 TIMES UD;Lo;0;L;;;;;N;;;;; 1250B;CUNEIFORM SIGN LAK-617 TIMES URUDA;Lo;0;L;;;;;N;;;;; 1250C;CUNEIFORM SIGN LAK-636;Lo;0;L;;;;;N;;;;; 1250D;CUNEIFORM SIGN LAK-648;Lo;0;L;;;;;N;;;;; 1250E;CUNEIFORM SIGN LAK-648 TIMES DUB;Lo;0;L;;;;;N;;;;; 1250F;CUNEIFORM SIGN LAK-648 TIMES GA;Lo;0;L;;;;;N;;;;; 12510;CUNEIFORM SIGN LAK-648 TIMES IGI;Lo;0;L;;;;;N;;;;; 12511;CUNEIFORM SIGN LAK-648 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12512;CUNEIFORM SIGN LAK-648 TIMES NI;Lo;0;L;;;;;N;;;;; 12513;CUNEIFORM SIGN LAK-648 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; 12514;CUNEIFORM SIGN LAK-648 TIMES SHESH PLUS KI;Lo;0;L;;;;;N;;;;; 12515;CUNEIFORM SIGN LAK-648 TIMES UD;Lo;0;L;;;;;N;;;;; 12516;CUNEIFORM SIGN LAK-648 TIMES URUDA;Lo;0;L;;;;;N;;;;; 12517;CUNEIFORM SIGN LAK-724;Lo;0;L;;;;;N;;;;; 12518;CUNEIFORM SIGN LAK-749;Lo;0;L;;;;;N;;;;; 12519;CUNEIFORM SIGN LU2 GUNU TIMES ASH;Lo;0;L;;;;;N;;;;; 1251A;CUNEIFORM SIGN LU2 TIMES DISH;Lo;0;L;;;;;N;;;;; 1251B;CUNEIFORM SIGN LU2 TIMES HAL;Lo;0;L;;;;;N;;;;; 1251C;CUNEIFORM SIGN LU2 TIMES PAP;Lo;0;L;;;;;N;;;;; 1251D;CUNEIFORM SIGN LU2 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; 1251E;CUNEIFORM SIGN LU2 TIMES TAK4;Lo;0;L;;;;;N;;;;; 1251F;CUNEIFORM SIGN MI PLUS ZA7;Lo;0;L;;;;;N;;;;; 12520;CUNEIFORM SIGN MUSH OVER MUSH TIMES GA;Lo;0;L;;;;;N;;;;; 12521;CUNEIFORM SIGN MUSH OVER MUSH TIMES KAK;Lo;0;L;;;;;N;;;;; 12522;CUNEIFORM SIGN NINDA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;; 12523;CUNEIFORM SIGN NINDA2 TIMES GISH;Lo;0;L;;;;;N;;;;; 12524;CUNEIFORM SIGN NINDA2 TIMES GUL;Lo;0;L;;;;;N;;;;; 12525;CUNEIFORM SIGN NINDA2 TIMES HI;Lo;0;L;;;;;N;;;;; 12526;CUNEIFORM SIGN NINDA2 TIMES KESH2;Lo;0;L;;;;;N;;;;; 12527;CUNEIFORM SIGN NINDA2 TIMES LAK-050;Lo;0;L;;;;;N;;;;; 12528;CUNEIFORM SIGN NINDA2 TIMES MASH;Lo;0;L;;;;;N;;;;; 12529;CUNEIFORM SIGN NINDA2 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;; 1252A;CUNEIFORM SIGN NINDA2 TIMES U;Lo;0;L;;;;;N;;;;; 1252B;CUNEIFORM SIGN NINDA2 TIMES U PLUS U;Lo;0;L;;;;;N;;;;; 1252C;CUNEIFORM SIGN NINDA2 TIMES URUDA;Lo;0;L;;;;;N;;;;; 1252D;CUNEIFORM SIGN SAG GUNU TIMES HA;Lo;0;L;;;;;N;;;;; 1252E;CUNEIFORM SIGN SAG TIMES EN;Lo;0;L;;;;;N;;;;; 1252F;CUNEIFORM SIGN SAG TIMES SHE AT LEFT;Lo;0;L;;;;;N;;;;; 12530;CUNEIFORM SIGN SAG TIMES TAK4;Lo;0;L;;;;;N;;;;; 12531;CUNEIFORM SIGN SHA6 TENU;Lo;0;L;;;;;N;;;;; 12532;CUNEIFORM SIGN SHE OVER SHE;Lo;0;L;;;;;N;;;;; 12533;CUNEIFORM SIGN SHE PLUS HUB2;Lo;0;L;;;;;N;;;;; 12534;CUNEIFORM SIGN SHE PLUS NAM2;Lo;0;L;;;;;N;;;;; 12535;CUNEIFORM SIGN SHE PLUS SAR;Lo;0;L;;;;;N;;;;; 12536;CUNEIFORM SIGN SHU2 PLUS DUG TIMES NI;Lo;0;L;;;;;N;;;;; 12537;CUNEIFORM SIGN SHU2 PLUS E2 TIMES AN;Lo;0;L;;;;;N;;;;; 12538;CUNEIFORM SIGN SI TIMES TAK4;Lo;0;L;;;;;N;;;;; 12539;CUNEIFORM SIGN TAK4 PLUS SAG;Lo;0;L;;;;;N;;;;; 1253A;CUNEIFORM SIGN TUM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; 1253B;CUNEIFORM SIGN TUM TIMES THREE DISH;Lo;0;L;;;;;N;;;;; 1253C;CUNEIFORM SIGN UR2 INVERTED;Lo;0;L;;;;;N;;;;; 1253D;CUNEIFORM SIGN UR2 TIMES UD;Lo;0;L;;;;;N;;;;; 1253E;CUNEIFORM SIGN URU TIMES DARA3;Lo;0;L;;;;;N;;;;; 1253F;CUNEIFORM SIGN URU TIMES LAK-668;Lo;0;L;;;;;N;;;;; 12540;CUNEIFORM SIGN URU TIMES LU3;Lo;0;L;;;;;N;;;;; 12541;CUNEIFORM SIGN ZA7;Lo;0;L;;;;;N;;;;; 12542;CUNEIFORM SIGN ZU OVER ZU PLUS SAR;Lo;0;L;;;;;N;;;;; 12543;CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU;Lo;0;L;;;;;N;;;;; 13000;EGYPTIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;; 13001;EGYPTIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;; 13002;EGYPTIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;; 13003;EGYPTIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;; 13004;EGYPTIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;; 13005;EGYPTIAN HIEROGLYPH A005A;Lo;0;L;;;;;N;;;;; 13006;EGYPTIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;; 13007;EGYPTIAN HIEROGLYPH A006A;Lo;0;L;;;;;N;;;;; 13008;EGYPTIAN HIEROGLYPH A006B;Lo;0;L;;;;;N;;;;; 13009;EGYPTIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;; 1300A;EGYPTIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;; 1300B;EGYPTIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;; 1300C;EGYPTIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;; 1300D;EGYPTIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;; 1300E;EGYPTIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;; 1300F;EGYPTIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;; 13010;EGYPTIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;; 13011;EGYPTIAN HIEROGLYPH A014A;Lo;0;L;;;;;N;;;;; 13012;EGYPTIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;; 13013;EGYPTIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;; 13014;EGYPTIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;; 13015;EGYPTIAN HIEROGLYPH A017A;Lo;0;L;;;;;N;;;;; 13016;EGYPTIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;; 13017;EGYPTIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;; 13018;EGYPTIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;; 13019;EGYPTIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;; 1301A;EGYPTIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;; 1301B;EGYPTIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;; 1301C;EGYPTIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;; 1301D;EGYPTIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;; 1301E;EGYPTIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;; 1301F;EGYPTIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;; 13020;EGYPTIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;; 13021;EGYPTIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;; 13022;EGYPTIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;; 13023;EGYPTIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;; 13024;EGYPTIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;; 13025;EGYPTIAN HIEROGLYPH A032A;Lo;0;L;;;;;N;;;;; 13026;EGYPTIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;; 13027;EGYPTIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;; 13028;EGYPTIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;; 13029;EGYPTIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;; 1302A;EGYPTIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;; 1302B;EGYPTIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;; 1302C;EGYPTIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;; 1302D;EGYPTIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;; 1302E;EGYPTIAN HIEROGLYPH A040A;Lo;0;L;;;;;N;;;;; 1302F;EGYPTIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;; 13030;EGYPTIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;; 13031;EGYPTIAN HIEROGLYPH A042A;Lo;0;L;;;;;N;;;;; 13032;EGYPTIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;; 13033;EGYPTIAN HIEROGLYPH A043A;Lo;0;L;;;;;N;;;;; 13034;EGYPTIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;; 13035;EGYPTIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;; 13036;EGYPTIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;; 13037;EGYPTIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;; 13038;EGYPTIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;; 13039;EGYPTIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;; 1303A;EGYPTIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;; 1303B;EGYPTIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;; 1303C;EGYPTIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;; 1303D;EGYPTIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;; 1303E;EGYPTIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;; 1303F;EGYPTIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;; 13040;EGYPTIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;; 13041;EGYPTIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;; 13042;EGYPTIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;; 13043;EGYPTIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;; 13044;EGYPTIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;; 13045;EGYPTIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;; 13046;EGYPTIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;; 13047;EGYPTIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;; 13048;EGYPTIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;; 13049;EGYPTIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;; 1304A;EGYPTIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;; 1304B;EGYPTIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;; 1304C;EGYPTIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;; 1304D;EGYPTIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;; 1304E;EGYPTIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;; 1304F;EGYPTIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;; 13050;EGYPTIAN HIEROGLYPH B001;Lo;0;L;;;;;N;;;;; 13051;EGYPTIAN HIEROGLYPH B002;Lo;0;L;;;;;N;;;;; 13052;EGYPTIAN HIEROGLYPH B003;Lo;0;L;;;;;N;;;;; 13053;EGYPTIAN HIEROGLYPH B004;Lo;0;L;;;;;N;;;;; 13054;EGYPTIAN HIEROGLYPH B005;Lo;0;L;;;;;N;;;;; 13055;EGYPTIAN HIEROGLYPH B005A;Lo;0;L;;;;;N;;;;; 13056;EGYPTIAN HIEROGLYPH B006;Lo;0;L;;;;;N;;;;; 13057;EGYPTIAN HIEROGLYPH B007;Lo;0;L;;;;;N;;;;; 13058;EGYPTIAN HIEROGLYPH B008;Lo;0;L;;;;;N;;;;; 13059;EGYPTIAN HIEROGLYPH B009;Lo;0;L;;;;;N;;;;; 1305A;EGYPTIAN HIEROGLYPH C001;Lo;0;L;;;;;N;;;;; 1305B;EGYPTIAN HIEROGLYPH C002;Lo;0;L;;;;;N;;;;; 1305C;EGYPTIAN HIEROGLYPH C002A;Lo;0;L;;;;;N;;;;; 1305D;EGYPTIAN HIEROGLYPH C002B;Lo;0;L;;;;;N;;;;; 1305E;EGYPTIAN HIEROGLYPH C002C;Lo;0;L;;;;;N;;;;; 1305F;EGYPTIAN HIEROGLYPH C003;Lo;0;L;;;;;N;;;;; 13060;EGYPTIAN HIEROGLYPH C004;Lo;0;L;;;;;N;;;;; 13061;EGYPTIAN HIEROGLYPH C005;Lo;0;L;;;;;N;;;;; 13062;EGYPTIAN HIEROGLYPH C006;Lo;0;L;;;;;N;;;;; 13063;EGYPTIAN HIEROGLYPH C007;Lo;0;L;;;;;N;;;;; 13064;EGYPTIAN HIEROGLYPH C008;Lo;0;L;;;;;N;;;;; 13065;EGYPTIAN HIEROGLYPH C009;Lo;0;L;;;;;N;;;;; 13066;EGYPTIAN HIEROGLYPH C010;Lo;0;L;;;;;N;;;;; 13067;EGYPTIAN HIEROGLYPH C010A;Lo;0;L;;;;;N;;;;; 13068;EGYPTIAN HIEROGLYPH C011;Lo;0;L;;;;;N;;;;; 13069;EGYPTIAN HIEROGLYPH C012;Lo;0;L;;;;;N;;;;; 1306A;EGYPTIAN HIEROGLYPH C013;Lo;0;L;;;;;N;;;;; 1306B;EGYPTIAN HIEROGLYPH C014;Lo;0;L;;;;;N;;;;; 1306C;EGYPTIAN HIEROGLYPH C015;Lo;0;L;;;;;N;;;;; 1306D;EGYPTIAN HIEROGLYPH C016;Lo;0;L;;;;;N;;;;; 1306E;EGYPTIAN HIEROGLYPH C017;Lo;0;L;;;;;N;;;;; 1306F;EGYPTIAN HIEROGLYPH C018;Lo;0;L;;;;;N;;;;; 13070;EGYPTIAN HIEROGLYPH C019;Lo;0;L;;;;;N;;;;; 13071;EGYPTIAN HIEROGLYPH C020;Lo;0;L;;;;;N;;;;; 13072;EGYPTIAN HIEROGLYPH C021;Lo;0;L;;;;;N;;;;; 13073;EGYPTIAN HIEROGLYPH C022;Lo;0;L;;;;;N;;;;; 13074;EGYPTIAN HIEROGLYPH C023;Lo;0;L;;;;;N;;;;; 13075;EGYPTIAN HIEROGLYPH C024;Lo;0;L;;;;;N;;;;; 13076;EGYPTIAN HIEROGLYPH D001;Lo;0;L;;;;;N;;;;; 13077;EGYPTIAN HIEROGLYPH D002;Lo;0;L;;;;;N;;;;; 13078;EGYPTIAN HIEROGLYPH D003;Lo;0;L;;;;;N;;;;; 13079;EGYPTIAN HIEROGLYPH D004;Lo;0;L;;;;;N;;;;; 1307A;EGYPTIAN HIEROGLYPH D005;Lo;0;L;;;;;N;;;;; 1307B;EGYPTIAN HIEROGLYPH D006;Lo;0;L;;;;;N;;;;; 1307C;EGYPTIAN HIEROGLYPH D007;Lo;0;L;;;;;N;;;;; 1307D;EGYPTIAN HIEROGLYPH D008;Lo;0;L;;;;;N;;;;; 1307E;EGYPTIAN HIEROGLYPH D008A;Lo;0;L;;;;;N;;;;; 1307F;EGYPTIAN HIEROGLYPH D009;Lo;0;L;;;;;N;;;;; 13080;EGYPTIAN HIEROGLYPH D010;Lo;0;L;;;;;N;;;;; 13081;EGYPTIAN HIEROGLYPH D011;Lo;0;L;;;;;N;;;;; 13082;EGYPTIAN HIEROGLYPH D012;Lo;0;L;;;;;N;;;;; 13083;EGYPTIAN HIEROGLYPH D013;Lo;0;L;;;;;N;;;;; 13084;EGYPTIAN HIEROGLYPH D014;Lo;0;L;;;;;N;;;;; 13085;EGYPTIAN HIEROGLYPH D015;Lo;0;L;;;;;N;;;;; 13086;EGYPTIAN HIEROGLYPH D016;Lo;0;L;;;;;N;;;;; 13087;EGYPTIAN HIEROGLYPH D017;Lo;0;L;;;;;N;;;;; 13088;EGYPTIAN HIEROGLYPH D018;Lo;0;L;;;;;N;;;;; 13089;EGYPTIAN HIEROGLYPH D019;Lo;0;L;;;;;N;;;;; 1308A;EGYPTIAN HIEROGLYPH D020;Lo;0;L;;;;;N;;;;; 1308B;EGYPTIAN HIEROGLYPH D021;Lo;0;L;;;;;N;;;;; 1308C;EGYPTIAN HIEROGLYPH D022;Lo;0;L;;;;;N;;;;; 1308D;EGYPTIAN HIEROGLYPH D023;Lo;0;L;;;;;N;;;;; 1308E;EGYPTIAN HIEROGLYPH D024;Lo;0;L;;;;;N;;;;; 1308F;EGYPTIAN HIEROGLYPH D025;Lo;0;L;;;;;N;;;;; 13090;EGYPTIAN HIEROGLYPH D026;Lo;0;L;;;;;N;;;;; 13091;EGYPTIAN HIEROGLYPH D027;Lo;0;L;;;;;N;;;;; 13092;EGYPTIAN HIEROGLYPH D027A;Lo;0;L;;;;;N;;;;; 13093;EGYPTIAN HIEROGLYPH D028;Lo;0;L;;;;;N;;;;; 13094;EGYPTIAN HIEROGLYPH D029;Lo;0;L;;;;;N;;;;; 13095;EGYPTIAN HIEROGLYPH D030;Lo;0;L;;;;;N;;;;; 13096;EGYPTIAN HIEROGLYPH D031;Lo;0;L;;;;;N;;;;; 13097;EGYPTIAN HIEROGLYPH D031A;Lo;0;L;;;;;N;;;;; 13098;EGYPTIAN HIEROGLYPH D032;Lo;0;L;;;;;N;;;;; 13099;EGYPTIAN HIEROGLYPH D033;Lo;0;L;;;;;N;;;;; 1309A;EGYPTIAN HIEROGLYPH D034;Lo;0;L;;;;;N;;;;; 1309B;EGYPTIAN HIEROGLYPH D034A;Lo;0;L;;;;;N;;;;; 1309C;EGYPTIAN HIEROGLYPH D035;Lo;0;L;;;;;N;;;;; 1309D;EGYPTIAN HIEROGLYPH D036;Lo;0;L;;;;;N;;;;; 1309E;EGYPTIAN HIEROGLYPH D037;Lo;0;L;;;;;N;;;;; 1309F;EGYPTIAN HIEROGLYPH D038;Lo;0;L;;;;;N;;;;; 130A0;EGYPTIAN HIEROGLYPH D039;Lo;0;L;;;;;N;;;;; 130A1;EGYPTIAN HIEROGLYPH D040;Lo;0;L;;;;;N;;;;; 130A2;EGYPTIAN HIEROGLYPH D041;Lo;0;L;;;;;N;;;;; 130A3;EGYPTIAN HIEROGLYPH D042;Lo;0;L;;;;;N;;;;; 130A4;EGYPTIAN HIEROGLYPH D043;Lo;0;L;;;;;N;;;;; 130A5;EGYPTIAN HIEROGLYPH D044;Lo;0;L;;;;;N;;;;; 130A6;EGYPTIAN HIEROGLYPH D045;Lo;0;L;;;;;N;;;;; 130A7;EGYPTIAN HIEROGLYPH D046;Lo;0;L;;;;;N;;;;; 130A8;EGYPTIAN HIEROGLYPH D046A;Lo;0;L;;;;;N;;;;; 130A9;EGYPTIAN HIEROGLYPH D047;Lo;0;L;;;;;N;;;;; 130AA;EGYPTIAN HIEROGLYPH D048;Lo;0;L;;;;;N;;;;; 130AB;EGYPTIAN HIEROGLYPH D048A;Lo;0;L;;;;;N;;;;; 130AC;EGYPTIAN HIEROGLYPH D049;Lo;0;L;;;;;N;;;;; 130AD;EGYPTIAN HIEROGLYPH D050;Lo;0;L;;;;;N;;;;; 130AE;EGYPTIAN HIEROGLYPH D050A;Lo;0;L;;;;;N;;;;; 130AF;EGYPTIAN HIEROGLYPH D050B;Lo;0;L;;;;;N;;;;; 130B0;EGYPTIAN HIEROGLYPH D050C;Lo;0;L;;;;;N;;;;; 130B1;EGYPTIAN HIEROGLYPH D050D;Lo;0;L;;;;;N;;;;; 130B2;EGYPTIAN HIEROGLYPH D050E;Lo;0;L;;;;;N;;;;; 130B3;EGYPTIAN HIEROGLYPH D050F;Lo;0;L;;;;;N;;;;; 130B4;EGYPTIAN HIEROGLYPH D050G;Lo;0;L;;;;;N;;;;; 130B5;EGYPTIAN HIEROGLYPH D050H;Lo;0;L;;;;;N;;;;; 130B6;EGYPTIAN HIEROGLYPH D050I;Lo;0;L;;;;;N;;;;; 130B7;EGYPTIAN HIEROGLYPH D051;Lo;0;L;;;;;N;;;;; 130B8;EGYPTIAN HIEROGLYPH D052;Lo;0;L;;;;;N;;;;; 130B9;EGYPTIAN HIEROGLYPH D052A;Lo;0;L;;;;;N;;;;; 130BA;EGYPTIAN HIEROGLYPH D053;Lo;0;L;;;;;N;;;;; 130BB;EGYPTIAN HIEROGLYPH D054;Lo;0;L;;;;;N;;;;; 130BC;EGYPTIAN HIEROGLYPH D054A;Lo;0;L;;;;;N;;;;; 130BD;EGYPTIAN HIEROGLYPH D055;Lo;0;L;;;;;N;;;;; 130BE;EGYPTIAN HIEROGLYPH D056;Lo;0;L;;;;;N;;;;; 130BF;EGYPTIAN HIEROGLYPH D057;Lo;0;L;;;;;N;;;;; 130C0;EGYPTIAN HIEROGLYPH D058;Lo;0;L;;;;;N;;;;; 130C1;EGYPTIAN HIEROGLYPH D059;Lo;0;L;;;;;N;;;;; 130C2;EGYPTIAN HIEROGLYPH D060;Lo;0;L;;;;;N;;;;; 130C3;EGYPTIAN HIEROGLYPH D061;Lo;0;L;;;;;N;;;;; 130C4;EGYPTIAN HIEROGLYPH D062;Lo;0;L;;;;;N;;;;; 130C5;EGYPTIAN HIEROGLYPH D063;Lo;0;L;;;;;N;;;;; 130C6;EGYPTIAN HIEROGLYPH D064;Lo;0;L;;;;;N;;;;; 130C7;EGYPTIAN HIEROGLYPH D065;Lo;0;L;;;;;N;;;;; 130C8;EGYPTIAN HIEROGLYPH D066;Lo;0;L;;;;;N;;;;; 130C9;EGYPTIAN HIEROGLYPH D067;Lo;0;L;;;;;N;;;;; 130CA;EGYPTIAN HIEROGLYPH D067A;Lo;0;L;;;;;N;;;;; 130CB;EGYPTIAN HIEROGLYPH D067B;Lo;0;L;;;;;N;;;;; 130CC;EGYPTIAN HIEROGLYPH D067C;Lo;0;L;;;;;N;;;;; 130CD;EGYPTIAN HIEROGLYPH D067D;Lo;0;L;;;;;N;;;;; 130CE;EGYPTIAN HIEROGLYPH D067E;Lo;0;L;;;;;N;;;;; 130CF;EGYPTIAN HIEROGLYPH D067F;Lo;0;L;;;;;N;;;;; 130D0;EGYPTIAN HIEROGLYPH D067G;Lo;0;L;;;;;N;;;;; 130D1;EGYPTIAN HIEROGLYPH D067H;Lo;0;L;;;;;N;;;;; 130D2;EGYPTIAN HIEROGLYPH E001;Lo;0;L;;;;;N;;;;; 130D3;EGYPTIAN HIEROGLYPH E002;Lo;0;L;;;;;N;;;;; 130D4;EGYPTIAN HIEROGLYPH E003;Lo;0;L;;;;;N;;;;; 130D5;EGYPTIAN HIEROGLYPH E004;Lo;0;L;;;;;N;;;;; 130D6;EGYPTIAN HIEROGLYPH E005;Lo;0;L;;;;;N;;;;; 130D7;EGYPTIAN HIEROGLYPH E006;Lo;0;L;;;;;N;;;;; 130D8;EGYPTIAN HIEROGLYPH E007;Lo;0;L;;;;;N;;;;; 130D9;EGYPTIAN HIEROGLYPH E008;Lo;0;L;;;;;N;;;;; 130DA;EGYPTIAN HIEROGLYPH E008A;Lo;0;L;;;;;N;;;;; 130DB;EGYPTIAN HIEROGLYPH E009;Lo;0;L;;;;;N;;;;; 130DC;EGYPTIAN HIEROGLYPH E009A;Lo;0;L;;;;;N;;;;; 130DD;EGYPTIAN HIEROGLYPH E010;Lo;0;L;;;;;N;;;;; 130DE;EGYPTIAN HIEROGLYPH E011;Lo;0;L;;;;;N;;;;; 130DF;EGYPTIAN HIEROGLYPH E012;Lo;0;L;;;;;N;;;;; 130E0;EGYPTIAN HIEROGLYPH E013;Lo;0;L;;;;;N;;;;; 130E1;EGYPTIAN HIEROGLYPH E014;Lo;0;L;;;;;N;;;;; 130E2;EGYPTIAN HIEROGLYPH E015;Lo;0;L;;;;;N;;;;; 130E3;EGYPTIAN HIEROGLYPH E016;Lo;0;L;;;;;N;;;;; 130E4;EGYPTIAN HIEROGLYPH E016A;Lo;0;L;;;;;N;;;;; 130E5;EGYPTIAN HIEROGLYPH E017;Lo;0;L;;;;;N;;;;; 130E6;EGYPTIAN HIEROGLYPH E017A;Lo;0;L;;;;;N;;;;; 130E7;EGYPTIAN HIEROGLYPH E018;Lo;0;L;;;;;N;;;;; 130E8;EGYPTIAN HIEROGLYPH E019;Lo;0;L;;;;;N;;;;; 130E9;EGYPTIAN HIEROGLYPH E020;Lo;0;L;;;;;N;;;;; 130EA;EGYPTIAN HIEROGLYPH E020A;Lo;0;L;;;;;N;;;;; 130EB;EGYPTIAN HIEROGLYPH E021;Lo;0;L;;;;;N;;;;; 130EC;EGYPTIAN HIEROGLYPH E022;Lo;0;L;;;;;N;;;;; 130ED;EGYPTIAN HIEROGLYPH E023;Lo;0;L;;;;;N;;;;; 130EE;EGYPTIAN HIEROGLYPH E024;Lo;0;L;;;;;N;;;;; 130EF;EGYPTIAN HIEROGLYPH E025;Lo;0;L;;;;;N;;;;; 130F0;EGYPTIAN HIEROGLYPH E026;Lo;0;L;;;;;N;;;;; 130F1;EGYPTIAN HIEROGLYPH E027;Lo;0;L;;;;;N;;;;; 130F2;EGYPTIAN HIEROGLYPH E028;Lo;0;L;;;;;N;;;;; 130F3;EGYPTIAN HIEROGLYPH E028A;Lo;0;L;;;;;N;;;;; 130F4;EGYPTIAN HIEROGLYPH E029;Lo;0;L;;;;;N;;;;; 130F5;EGYPTIAN HIEROGLYPH E030;Lo;0;L;;;;;N;;;;; 130F6;EGYPTIAN HIEROGLYPH E031;Lo;0;L;;;;;N;;;;; 130F7;EGYPTIAN HIEROGLYPH E032;Lo;0;L;;;;;N;;;;; 130F8;EGYPTIAN HIEROGLYPH E033;Lo;0;L;;;;;N;;;;; 130F9;EGYPTIAN HIEROGLYPH E034;Lo;0;L;;;;;N;;;;; 130FA;EGYPTIAN HIEROGLYPH E034A;Lo;0;L;;;;;N;;;;; 130FB;EGYPTIAN HIEROGLYPH E036;Lo;0;L;;;;;N;;;;; 130FC;EGYPTIAN HIEROGLYPH E037;Lo;0;L;;;;;N;;;;; 130FD;EGYPTIAN HIEROGLYPH E038;Lo;0;L;;;;;N;;;;; 130FE;EGYPTIAN HIEROGLYPH F001;Lo;0;L;;;;;N;;;;; 130FF;EGYPTIAN HIEROGLYPH F001A;Lo;0;L;;;;;N;;;;; 13100;EGYPTIAN HIEROGLYPH F002;Lo;0;L;;;;;N;;;;; 13101;EGYPTIAN HIEROGLYPH F003;Lo;0;L;;;;;N;;;;; 13102;EGYPTIAN HIEROGLYPH F004;Lo;0;L;;;;;N;;;;; 13103;EGYPTIAN HIEROGLYPH F005;Lo;0;L;;;;;N;;;;; 13104;EGYPTIAN HIEROGLYPH F006;Lo;0;L;;;;;N;;;;; 13105;EGYPTIAN HIEROGLYPH F007;Lo;0;L;;;;;N;;;;; 13106;EGYPTIAN HIEROGLYPH F008;Lo;0;L;;;;;N;;;;; 13107;EGYPTIAN HIEROGLYPH F009;Lo;0;L;;;;;N;;;;; 13108;EGYPTIAN HIEROGLYPH F010;Lo;0;L;;;;;N;;;;; 13109;EGYPTIAN HIEROGLYPH F011;Lo;0;L;;;;;N;;;;; 1310A;EGYPTIAN HIEROGLYPH F012;Lo;0;L;;;;;N;;;;; 1310B;EGYPTIAN HIEROGLYPH F013;Lo;0;L;;;;;N;;;;; 1310C;EGYPTIAN HIEROGLYPH F013A;Lo;0;L;;;;;N;;;;; 1310D;EGYPTIAN HIEROGLYPH F014;Lo;0;L;;;;;N;;;;; 1310E;EGYPTIAN HIEROGLYPH F015;Lo;0;L;;;;;N;;;;; 1310F;EGYPTIAN HIEROGLYPH F016;Lo;0;L;;;;;N;;;;; 13110;EGYPTIAN HIEROGLYPH F017;Lo;0;L;;;;;N;;;;; 13111;EGYPTIAN HIEROGLYPH F018;Lo;0;L;;;;;N;;;;; 13112;EGYPTIAN HIEROGLYPH F019;Lo;0;L;;;;;N;;;;; 13113;EGYPTIAN HIEROGLYPH F020;Lo;0;L;;;;;N;;;;; 13114;EGYPTIAN HIEROGLYPH F021;Lo;0;L;;;;;N;;;;; 13115;EGYPTIAN HIEROGLYPH F021A;Lo;0;L;;;;;N;;;;; 13116;EGYPTIAN HIEROGLYPH F022;Lo;0;L;;;;;N;;;;; 13117;EGYPTIAN HIEROGLYPH F023;Lo;0;L;;;;;N;;;;; 13118;EGYPTIAN HIEROGLYPH F024;Lo;0;L;;;;;N;;;;; 13119;EGYPTIAN HIEROGLYPH F025;Lo;0;L;;;;;N;;;;; 1311A;EGYPTIAN HIEROGLYPH F026;Lo;0;L;;;;;N;;;;; 1311B;EGYPTIAN HIEROGLYPH F027;Lo;0;L;;;;;N;;;;; 1311C;EGYPTIAN HIEROGLYPH F028;Lo;0;L;;;;;N;;;;; 1311D;EGYPTIAN HIEROGLYPH F029;Lo;0;L;;;;;N;;;;; 1311E;EGYPTIAN HIEROGLYPH F030;Lo;0;L;;;;;N;;;;; 1311F;EGYPTIAN HIEROGLYPH F031;Lo;0;L;;;;;N;;;;; 13120;EGYPTIAN HIEROGLYPH F031A;Lo;0;L;;;;;N;;;;; 13121;EGYPTIAN HIEROGLYPH F032;Lo;0;L;;;;;N;;;;; 13122;EGYPTIAN HIEROGLYPH F033;Lo;0;L;;;;;N;;;;; 13123;EGYPTIAN HIEROGLYPH F034;Lo;0;L;;;;;N;;;;; 13124;EGYPTIAN HIEROGLYPH F035;Lo;0;L;;;;;N;;;;; 13125;EGYPTIAN HIEROGLYPH F036;Lo;0;L;;;;;N;;;;; 13126;EGYPTIAN HIEROGLYPH F037;Lo;0;L;;;;;N;;;;; 13127;EGYPTIAN HIEROGLYPH F037A;Lo;0;L;;;;;N;;;;; 13128;EGYPTIAN HIEROGLYPH F038;Lo;0;L;;;;;N;;;;; 13129;EGYPTIAN HIEROGLYPH F038A;Lo;0;L;;;;;N;;;;; 1312A;EGYPTIAN HIEROGLYPH F039;Lo;0;L;;;;;N;;;;; 1312B;EGYPTIAN HIEROGLYPH F040;Lo;0;L;;;;;N;;;;; 1312C;EGYPTIAN HIEROGLYPH F041;Lo;0;L;;;;;N;;;;; 1312D;EGYPTIAN HIEROGLYPH F042;Lo;0;L;;;;;N;;;;; 1312E;EGYPTIAN HIEROGLYPH F043;Lo;0;L;;;;;N;;;;; 1312F;EGYPTIAN HIEROGLYPH F044;Lo;0;L;;;;;N;;;;; 13130;EGYPTIAN HIEROGLYPH F045;Lo;0;L;;;;;N;;;;; 13131;EGYPTIAN HIEROGLYPH F045A;Lo;0;L;;;;;N;;;;; 13132;EGYPTIAN HIEROGLYPH F046;Lo;0;L;;;;;N;;;;; 13133;EGYPTIAN HIEROGLYPH F046A;Lo;0;L;;;;;N;;;;; 13134;EGYPTIAN HIEROGLYPH F047;Lo;0;L;;;;;N;;;;; 13135;EGYPTIAN HIEROGLYPH F047A;Lo;0;L;;;;;N;;;;; 13136;EGYPTIAN HIEROGLYPH F048;Lo;0;L;;;;;N;;;;; 13137;EGYPTIAN HIEROGLYPH F049;Lo;0;L;;;;;N;;;;; 13138;EGYPTIAN HIEROGLYPH F050;Lo;0;L;;;;;N;;;;; 13139;EGYPTIAN HIEROGLYPH F051;Lo;0;L;;;;;N;;;;; 1313A;EGYPTIAN HIEROGLYPH F051A;Lo;0;L;;;;;N;;;;; 1313B;EGYPTIAN HIEROGLYPH F051B;Lo;0;L;;;;;N;;;;; 1313C;EGYPTIAN HIEROGLYPH F051C;Lo;0;L;;;;;N;;;;; 1313D;EGYPTIAN HIEROGLYPH F052;Lo;0;L;;;;;N;;;;; 1313E;EGYPTIAN HIEROGLYPH F053;Lo;0;L;;;;;N;;;;; 1313F;EGYPTIAN HIEROGLYPH G001;Lo;0;L;;;;;N;;;;; 13140;EGYPTIAN HIEROGLYPH G002;Lo;0;L;;;;;N;;;;; 13141;EGYPTIAN HIEROGLYPH G003;Lo;0;L;;;;;N;;;;; 13142;EGYPTIAN HIEROGLYPH G004;Lo;0;L;;;;;N;;;;; 13143;EGYPTIAN HIEROGLYPH G005;Lo;0;L;;;;;N;;;;; 13144;EGYPTIAN HIEROGLYPH G006;Lo;0;L;;;;;N;;;;; 13145;EGYPTIAN HIEROGLYPH G006A;Lo;0;L;;;;;N;;;;; 13146;EGYPTIAN HIEROGLYPH G007;Lo;0;L;;;;;N;;;;; 13147;EGYPTIAN HIEROGLYPH G007A;Lo;0;L;;;;;N;;;;; 13148;EGYPTIAN HIEROGLYPH G007B;Lo;0;L;;;;;N;;;;; 13149;EGYPTIAN HIEROGLYPH G008;Lo;0;L;;;;;N;;;;; 1314A;EGYPTIAN HIEROGLYPH G009;Lo;0;L;;;;;N;;;;; 1314B;EGYPTIAN HIEROGLYPH G010;Lo;0;L;;;;;N;;;;; 1314C;EGYPTIAN HIEROGLYPH G011;Lo;0;L;;;;;N;;;;; 1314D;EGYPTIAN HIEROGLYPH G011A;Lo;0;L;;;;;N;;;;; 1314E;EGYPTIAN HIEROGLYPH G012;Lo;0;L;;;;;N;;;;; 1314F;EGYPTIAN HIEROGLYPH G013;Lo;0;L;;;;;N;;;;; 13150;EGYPTIAN HIEROGLYPH G014;Lo;0;L;;;;;N;;;;; 13151;EGYPTIAN HIEROGLYPH G015;Lo;0;L;;;;;N;;;;; 13152;EGYPTIAN HIEROGLYPH G016;Lo;0;L;;;;;N;;;;; 13153;EGYPTIAN HIEROGLYPH G017;Lo;0;L;;;;;N;;;;; 13154;EGYPTIAN HIEROGLYPH G018;Lo;0;L;;;;;N;;;;; 13155;EGYPTIAN HIEROGLYPH G019;Lo;0;L;;;;;N;;;;; 13156;EGYPTIAN HIEROGLYPH G020;Lo;0;L;;;;;N;;;;; 13157;EGYPTIAN HIEROGLYPH G020A;Lo;0;L;;;;;N;;;;; 13158;EGYPTIAN HIEROGLYPH G021;Lo;0;L;;;;;N;;;;; 13159;EGYPTIAN HIEROGLYPH G022;Lo;0;L;;;;;N;;;;; 1315A;EGYPTIAN HIEROGLYPH G023;Lo;0;L;;;;;N;;;;; 1315B;EGYPTIAN HIEROGLYPH G024;Lo;0;L;;;;;N;;;;; 1315C;EGYPTIAN HIEROGLYPH G025;Lo;0;L;;;;;N;;;;; 1315D;EGYPTIAN HIEROGLYPH G026;Lo;0;L;;;;;N;;;;; 1315E;EGYPTIAN HIEROGLYPH G026A;Lo;0;L;;;;;N;;;;; 1315F;EGYPTIAN HIEROGLYPH G027;Lo;0;L;;;;;N;;;;; 13160;EGYPTIAN HIEROGLYPH G028;Lo;0;L;;;;;N;;;;; 13161;EGYPTIAN HIEROGLYPH G029;Lo;0;L;;;;;N;;;;; 13162;EGYPTIAN HIEROGLYPH G030;Lo;0;L;;;;;N;;;;; 13163;EGYPTIAN HIEROGLYPH G031;Lo;0;L;;;;;N;;;;; 13164;EGYPTIAN HIEROGLYPH G032;Lo;0;L;;;;;N;;;;; 13165;EGYPTIAN HIEROGLYPH G033;Lo;0;L;;;;;N;;;;; 13166;EGYPTIAN HIEROGLYPH G034;Lo;0;L;;;;;N;;;;; 13167;EGYPTIAN HIEROGLYPH G035;Lo;0;L;;;;;N;;;;; 13168;EGYPTIAN HIEROGLYPH G036;Lo;0;L;;;;;N;;;;; 13169;EGYPTIAN HIEROGLYPH G036A;Lo;0;L;;;;;N;;;;; 1316A;EGYPTIAN HIEROGLYPH G037;Lo;0;L;;;;;N;;;;; 1316B;EGYPTIAN HIEROGLYPH G037A;Lo;0;L;;;;;N;;;;; 1316C;EGYPTIAN HIEROGLYPH G038;Lo;0;L;;;;;N;;;;; 1316D;EGYPTIAN HIEROGLYPH G039;Lo;0;L;;;;;N;;;;; 1316E;EGYPTIAN HIEROGLYPH G040;Lo;0;L;;;;;N;;;;; 1316F;EGYPTIAN HIEROGLYPH G041;Lo;0;L;;;;;N;;;;; 13170;EGYPTIAN HIEROGLYPH G042;Lo;0;L;;;;;N;;;;; 13171;EGYPTIAN HIEROGLYPH G043;Lo;0;L;;;;;N;;;;; 13172;EGYPTIAN HIEROGLYPH G043A;Lo;0;L;;;;;N;;;;; 13173;EGYPTIAN HIEROGLYPH G044;Lo;0;L;;;;;N;;;;; 13174;EGYPTIAN HIEROGLYPH G045;Lo;0;L;;;;;N;;;;; 13175;EGYPTIAN HIEROGLYPH G045A;Lo;0;L;;;;;N;;;;; 13176;EGYPTIAN HIEROGLYPH G046;Lo;0;L;;;;;N;;;;; 13177;EGYPTIAN HIEROGLYPH G047;Lo;0;L;;;;;N;;;;; 13178;EGYPTIAN HIEROGLYPH G048;Lo;0;L;;;;;N;;;;; 13179;EGYPTIAN HIEROGLYPH G049;Lo;0;L;;;;;N;;;;; 1317A;EGYPTIAN HIEROGLYPH G050;Lo;0;L;;;;;N;;;;; 1317B;EGYPTIAN HIEROGLYPH G051;Lo;0;L;;;;;N;;;;; 1317C;EGYPTIAN HIEROGLYPH G052;Lo;0;L;;;;;N;;;;; 1317D;EGYPTIAN HIEROGLYPH G053;Lo;0;L;;;;;N;;;;; 1317E;EGYPTIAN HIEROGLYPH G054;Lo;0;L;;;;;N;;;;; 1317F;EGYPTIAN HIEROGLYPH H001;Lo;0;L;;;;;N;;;;; 13180;EGYPTIAN HIEROGLYPH H002;Lo;0;L;;;;;N;;;;; 13181;EGYPTIAN HIEROGLYPH H003;Lo;0;L;;;;;N;;;;; 13182;EGYPTIAN HIEROGLYPH H004;Lo;0;L;;;;;N;;;;; 13183;EGYPTIAN HIEROGLYPH H005;Lo;0;L;;;;;N;;;;; 13184;EGYPTIAN HIEROGLYPH H006;Lo;0;L;;;;;N;;;;; 13185;EGYPTIAN HIEROGLYPH H006A;Lo;0;L;;;;;N;;;;; 13186;EGYPTIAN HIEROGLYPH H007;Lo;0;L;;;;;N;;;;; 13187;EGYPTIAN HIEROGLYPH H008;Lo;0;L;;;;;N;;;;; 13188;EGYPTIAN HIEROGLYPH I001;Lo;0;L;;;;;N;;;;; 13189;EGYPTIAN HIEROGLYPH I002;Lo;0;L;;;;;N;;;;; 1318A;EGYPTIAN HIEROGLYPH I003;Lo;0;L;;;;;N;;;;; 1318B;EGYPTIAN HIEROGLYPH I004;Lo;0;L;;;;;N;;;;; 1318C;EGYPTIAN HIEROGLYPH I005;Lo;0;L;;;;;N;;;;; 1318D;EGYPTIAN HIEROGLYPH I005A;Lo;0;L;;;;;N;;;;; 1318E;EGYPTIAN HIEROGLYPH I006;Lo;0;L;;;;;N;;;;; 1318F;EGYPTIAN HIEROGLYPH I007;Lo;0;L;;;;;N;;;;; 13190;EGYPTIAN HIEROGLYPH I008;Lo;0;L;;;;;N;;;;; 13191;EGYPTIAN HIEROGLYPH I009;Lo;0;L;;;;;N;;;;; 13192;EGYPTIAN HIEROGLYPH I009A;Lo;0;L;;;;;N;;;;; 13193;EGYPTIAN HIEROGLYPH I010;Lo;0;L;;;;;N;;;;; 13194;EGYPTIAN HIEROGLYPH I010A;Lo;0;L;;;;;N;;;;; 13195;EGYPTIAN HIEROGLYPH I011;Lo;0;L;;;;;N;;;;; 13196;EGYPTIAN HIEROGLYPH I011A;Lo;0;L;;;;;N;;;;; 13197;EGYPTIAN HIEROGLYPH I012;Lo;0;L;;;;;N;;;;; 13198;EGYPTIAN HIEROGLYPH I013;Lo;0;L;;;;;N;;;;; 13199;EGYPTIAN HIEROGLYPH I014;Lo;0;L;;;;;N;;;;; 1319A;EGYPTIAN HIEROGLYPH I015;Lo;0;L;;;;;N;;;;; 1319B;EGYPTIAN HIEROGLYPH K001;Lo;0;L;;;;;N;;;;; 1319C;EGYPTIAN HIEROGLYPH K002;Lo;0;L;;;;;N;;;;; 1319D;EGYPTIAN HIEROGLYPH K003;Lo;0;L;;;;;N;;;;; 1319E;EGYPTIAN HIEROGLYPH K004;Lo;0;L;;;;;N;;;;; 1319F;EGYPTIAN HIEROGLYPH K005;Lo;0;L;;;;;N;;;;; 131A0;EGYPTIAN HIEROGLYPH K006;Lo;0;L;;;;;N;;;;; 131A1;EGYPTIAN HIEROGLYPH K007;Lo;0;L;;;;;N;;;;; 131A2;EGYPTIAN HIEROGLYPH K008;Lo;0;L;;;;;N;;;;; 131A3;EGYPTIAN HIEROGLYPH L001;Lo;0;L;;;;;N;;;;; 131A4;EGYPTIAN HIEROGLYPH L002;Lo;0;L;;;;;N;;;;; 131A5;EGYPTIAN HIEROGLYPH L002A;Lo;0;L;;;;;N;;;;; 131A6;EGYPTIAN HIEROGLYPH L003;Lo;0;L;;;;;N;;;;; 131A7;EGYPTIAN HIEROGLYPH L004;Lo;0;L;;;;;N;;;;; 131A8;EGYPTIAN HIEROGLYPH L005;Lo;0;L;;;;;N;;;;; 131A9;EGYPTIAN HIEROGLYPH L006;Lo;0;L;;;;;N;;;;; 131AA;EGYPTIAN HIEROGLYPH L006A;Lo;0;L;;;;;N;;;;; 131AB;EGYPTIAN HIEROGLYPH L007;Lo;0;L;;;;;N;;;;; 131AC;EGYPTIAN HIEROGLYPH L008;Lo;0;L;;;;;N;;;;; 131AD;EGYPTIAN HIEROGLYPH M001;Lo;0;L;;;;;N;;;;; 131AE;EGYPTIAN HIEROGLYPH M001A;Lo;0;L;;;;;N;;;;; 131AF;EGYPTIAN HIEROGLYPH M001B;Lo;0;L;;;;;N;;;;; 131B0;EGYPTIAN HIEROGLYPH M002;Lo;0;L;;;;;N;;;;; 131B1;EGYPTIAN HIEROGLYPH M003;Lo;0;L;;;;;N;;;;; 131B2;EGYPTIAN HIEROGLYPH M003A;Lo;0;L;;;;;N;;;;; 131B3;EGYPTIAN HIEROGLYPH M004;Lo;0;L;;;;;N;;;;; 131B4;EGYPTIAN HIEROGLYPH M005;Lo;0;L;;;;;N;;;;; 131B5;EGYPTIAN HIEROGLYPH M006;Lo;0;L;;;;;N;;;;; 131B6;EGYPTIAN HIEROGLYPH M007;Lo;0;L;;;;;N;;;;; 131B7;EGYPTIAN HIEROGLYPH M008;Lo;0;L;;;;;N;;;;; 131B8;EGYPTIAN HIEROGLYPH M009;Lo;0;L;;;;;N;;;;; 131B9;EGYPTIAN HIEROGLYPH M010;Lo;0;L;;;;;N;;;;; 131BA;EGYPTIAN HIEROGLYPH M010A;Lo;0;L;;;;;N;;;;; 131BB;EGYPTIAN HIEROGLYPH M011;Lo;0;L;;;;;N;;;;; 131BC;EGYPTIAN HIEROGLYPH M012;Lo;0;L;;;;;N;;;;; 131BD;EGYPTIAN HIEROGLYPH M012A;Lo;0;L;;;;;N;;;;; 131BE;EGYPTIAN HIEROGLYPH M012B;Lo;0;L;;;;;N;;;;; 131BF;EGYPTIAN HIEROGLYPH M012C;Lo;0;L;;;;;N;;;;; 131C0;EGYPTIAN HIEROGLYPH M012D;Lo;0;L;;;;;N;;;;; 131C1;EGYPTIAN HIEROGLYPH M012E;Lo;0;L;;;;;N;;;;; 131C2;EGYPTIAN HIEROGLYPH M012F;Lo;0;L;;;;;N;;;;; 131C3;EGYPTIAN HIEROGLYPH M012G;Lo;0;L;;;;;N;;;;; 131C4;EGYPTIAN HIEROGLYPH M012H;Lo;0;L;;;;;N;;;;; 131C5;EGYPTIAN HIEROGLYPH M013;Lo;0;L;;;;;N;;;;; 131C6;EGYPTIAN HIEROGLYPH M014;Lo;0;L;;;;;N;;;;; 131C7;EGYPTIAN HIEROGLYPH M015;Lo;0;L;;;;;N;;;;; 131C8;EGYPTIAN HIEROGLYPH M015A;Lo;0;L;;;;;N;;;;; 131C9;EGYPTIAN HIEROGLYPH M016;Lo;0;L;;;;;N;;;;; 131CA;EGYPTIAN HIEROGLYPH M016A;Lo;0;L;;;;;N;;;;; 131CB;EGYPTIAN HIEROGLYPH M017;Lo;0;L;;;;;N;;;;; 131CC;EGYPTIAN HIEROGLYPH M017A;Lo;0;L;;;;;N;;;;; 131CD;EGYPTIAN HIEROGLYPH M018;Lo;0;L;;;;;N;;;;; 131CE;EGYPTIAN HIEROGLYPH M019;Lo;0;L;;;;;N;;;;; 131CF;EGYPTIAN HIEROGLYPH M020;Lo;0;L;;;;;N;;;;; 131D0;EGYPTIAN HIEROGLYPH M021;Lo;0;L;;;;;N;;;;; 131D1;EGYPTIAN HIEROGLYPH M022;Lo;0;L;;;;;N;;;;; 131D2;EGYPTIAN HIEROGLYPH M022A;Lo;0;L;;;;;N;;;;; 131D3;EGYPTIAN HIEROGLYPH M023;Lo;0;L;;;;;N;;;;; 131D4;EGYPTIAN HIEROGLYPH M024;Lo;0;L;;;;;N;;;;; 131D5;EGYPTIAN HIEROGLYPH M024A;Lo;0;L;;;;;N;;;;; 131D6;EGYPTIAN HIEROGLYPH M025;Lo;0;L;;;;;N;;;;; 131D7;EGYPTIAN HIEROGLYPH M026;Lo;0;L;;;;;N;;;;; 131D8;EGYPTIAN HIEROGLYPH M027;Lo;0;L;;;;;N;;;;; 131D9;EGYPTIAN HIEROGLYPH M028;Lo;0;L;;;;;N;;;;; 131DA;EGYPTIAN HIEROGLYPH M028A;Lo;0;L;;;;;N;;;;; 131DB;EGYPTIAN HIEROGLYPH M029;Lo;0;L;;;;;N;;;;; 131DC;EGYPTIAN HIEROGLYPH M030;Lo;0;L;;;;;N;;;;; 131DD;EGYPTIAN HIEROGLYPH M031;Lo;0;L;;;;;N;;;;; 131DE;EGYPTIAN HIEROGLYPH M031A;Lo;0;L;;;;;N;;;;; 131DF;EGYPTIAN HIEROGLYPH M032;Lo;0;L;;;;;N;;;;; 131E0;EGYPTIAN HIEROGLYPH M033;Lo;0;L;;;;;N;;;;; 131E1;EGYPTIAN HIEROGLYPH M033A;Lo;0;L;;;;;N;;;;; 131E2;EGYPTIAN HIEROGLYPH M033B;Lo;0;L;;;;;N;;;;; 131E3;EGYPTIAN HIEROGLYPH M034;Lo;0;L;;;;;N;;;;; 131E4;EGYPTIAN HIEROGLYPH M035;Lo;0;L;;;;;N;;;;; 131E5;EGYPTIAN HIEROGLYPH M036;Lo;0;L;;;;;N;;;;; 131E6;EGYPTIAN HIEROGLYPH M037;Lo;0;L;;;;;N;;;;; 131E7;EGYPTIAN HIEROGLYPH M038;Lo;0;L;;;;;N;;;;; 131E8;EGYPTIAN HIEROGLYPH M039;Lo;0;L;;;;;N;;;;; 131E9;EGYPTIAN HIEROGLYPH M040;Lo;0;L;;;;;N;;;;; 131EA;EGYPTIAN HIEROGLYPH M040A;Lo;0;L;;;;;N;;;;; 131EB;EGYPTIAN HIEROGLYPH M041;Lo;0;L;;;;;N;;;;; 131EC;EGYPTIAN HIEROGLYPH M042;Lo;0;L;;;;;N;;;;; 131ED;EGYPTIAN HIEROGLYPH M043;Lo;0;L;;;;;N;;;;; 131EE;EGYPTIAN HIEROGLYPH M044;Lo;0;L;;;;;N;;;;; 131EF;EGYPTIAN HIEROGLYPH N001;Lo;0;L;;;;;N;;;;; 131F0;EGYPTIAN HIEROGLYPH N002;Lo;0;L;;;;;N;;;;; 131F1;EGYPTIAN HIEROGLYPH N003;Lo;0;L;;;;;N;;;;; 131F2;EGYPTIAN HIEROGLYPH N004;Lo;0;L;;;;;N;;;;; 131F3;EGYPTIAN HIEROGLYPH N005;Lo;0;L;;;;;N;;;;; 131F4;EGYPTIAN HIEROGLYPH N006;Lo;0;L;;;;;N;;;;; 131F5;EGYPTIAN HIEROGLYPH N007;Lo;0;L;;;;;N;;;;; 131F6;EGYPTIAN HIEROGLYPH N008;Lo;0;L;;;;;N;;;;; 131F7;EGYPTIAN HIEROGLYPH N009;Lo;0;L;;;;;N;;;;; 131F8;EGYPTIAN HIEROGLYPH N010;Lo;0;L;;;;;N;;;;; 131F9;EGYPTIAN HIEROGLYPH N011;Lo;0;L;;;;;N;;;;; 131FA;EGYPTIAN HIEROGLYPH N012;Lo;0;L;;;;;N;;;;; 131FB;EGYPTIAN HIEROGLYPH N013;Lo;0;L;;;;;N;;;;; 131FC;EGYPTIAN HIEROGLYPH N014;Lo;0;L;;;;;N;;;;; 131FD;EGYPTIAN HIEROGLYPH N015;Lo;0;L;;;;;N;;;;; 131FE;EGYPTIAN HIEROGLYPH N016;Lo;0;L;;;;;N;;;;; 131FF;EGYPTIAN HIEROGLYPH N017;Lo;0;L;;;;;N;;;;; 13200;EGYPTIAN HIEROGLYPH N018;Lo;0;L;;;;;N;;;;; 13201;EGYPTIAN HIEROGLYPH N018A;Lo;0;L;;;;;N;;;;; 13202;EGYPTIAN HIEROGLYPH N018B;Lo;0;L;;;;;N;;;;; 13203;EGYPTIAN HIEROGLYPH N019;Lo;0;L;;;;;N;;;;; 13204;EGYPTIAN HIEROGLYPH N020;Lo;0;L;;;;;N;;;;; 13205;EGYPTIAN HIEROGLYPH N021;Lo;0;L;;;;;N;;;;; 13206;EGYPTIAN HIEROGLYPH N022;Lo;0;L;;;;;N;;;;; 13207;EGYPTIAN HIEROGLYPH N023;Lo;0;L;;;;;N;;;;; 13208;EGYPTIAN HIEROGLYPH N024;Lo;0;L;;;;;N;;;;; 13209;EGYPTIAN HIEROGLYPH N025;Lo;0;L;;;;;N;;;;; 1320A;EGYPTIAN HIEROGLYPH N025A;Lo;0;L;;;;;N;;;;; 1320B;EGYPTIAN HIEROGLYPH N026;Lo;0;L;;;;;N;;;;; 1320C;EGYPTIAN HIEROGLYPH N027;Lo;0;L;;;;;N;;;;; 1320D;EGYPTIAN HIEROGLYPH N028;Lo;0;L;;;;;N;;;;; 1320E;EGYPTIAN HIEROGLYPH N029;Lo;0;L;;;;;N;;;;; 1320F;EGYPTIAN HIEROGLYPH N030;Lo;0;L;;;;;N;;;;; 13210;EGYPTIAN HIEROGLYPH N031;Lo;0;L;;;;;N;;;;; 13211;EGYPTIAN HIEROGLYPH N032;Lo;0;L;;;;;N;;;;; 13212;EGYPTIAN HIEROGLYPH N033;Lo;0;L;;;;;N;;;;; 13213;EGYPTIAN HIEROGLYPH N033A;Lo;0;L;;;;;N;;;;; 13214;EGYPTIAN HIEROGLYPH N034;Lo;0;L;;;;;N;;;;; 13215;EGYPTIAN HIEROGLYPH N034A;Lo;0;L;;;;;N;;;;; 13216;EGYPTIAN HIEROGLYPH N035;Lo;0;L;;;;;N;;;;; 13217;EGYPTIAN HIEROGLYPH N035A;Lo;0;L;;;;;N;;;;; 13218;EGYPTIAN HIEROGLYPH N036;Lo;0;L;;;;;N;;;;; 13219;EGYPTIAN HIEROGLYPH N037;Lo;0;L;;;;;N;;;;; 1321A;EGYPTIAN HIEROGLYPH N037A;Lo;0;L;;;;;N;;;;; 1321B;EGYPTIAN HIEROGLYPH N038;Lo;0;L;;;;;N;;;;; 1321C;EGYPTIAN HIEROGLYPH N039;Lo;0;L;;;;;N;;;;; 1321D;EGYPTIAN HIEROGLYPH N040;Lo;0;L;;;;;N;;;;; 1321E;EGYPTIAN HIEROGLYPH N041;Lo;0;L;;;;;N;;;;; 1321F;EGYPTIAN HIEROGLYPH N042;Lo;0;L;;;;;N;;;;; 13220;EGYPTIAN HIEROGLYPH NL001;Lo;0;L;;;;;N;;;;; 13221;EGYPTIAN HIEROGLYPH NL002;Lo;0;L;;;;;N;;;;; 13222;EGYPTIAN HIEROGLYPH NL003;Lo;0;L;;;;;N;;;;; 13223;EGYPTIAN HIEROGLYPH NL004;Lo;0;L;;;;;N;;;;; 13224;EGYPTIAN HIEROGLYPH NL005;Lo;0;L;;;;;N;;;;; 13225;EGYPTIAN HIEROGLYPH NL005A;Lo;0;L;;;;;N;;;;; 13226;EGYPTIAN HIEROGLYPH NL006;Lo;0;L;;;;;N;;;;; 13227;EGYPTIAN HIEROGLYPH NL007;Lo;0;L;;;;;N;;;;; 13228;EGYPTIAN HIEROGLYPH NL008;Lo;0;L;;;;;N;;;;; 13229;EGYPTIAN HIEROGLYPH NL009;Lo;0;L;;;;;N;;;;; 1322A;EGYPTIAN HIEROGLYPH NL010;Lo;0;L;;;;;N;;;;; 1322B;EGYPTIAN HIEROGLYPH NL011;Lo;0;L;;;;;N;;;;; 1322C;EGYPTIAN HIEROGLYPH NL012;Lo;0;L;;;;;N;;;;; 1322D;EGYPTIAN HIEROGLYPH NL013;Lo;0;L;;;;;N;;;;; 1322E;EGYPTIAN HIEROGLYPH NL014;Lo;0;L;;;;;N;;;;; 1322F;EGYPTIAN HIEROGLYPH NL015;Lo;0;L;;;;;N;;;;; 13230;EGYPTIAN HIEROGLYPH NL016;Lo;0;L;;;;;N;;;;; 13231;EGYPTIAN HIEROGLYPH NL017;Lo;0;L;;;;;N;;;;; 13232;EGYPTIAN HIEROGLYPH NL017A;Lo;0;L;;;;;N;;;;; 13233;EGYPTIAN HIEROGLYPH NL018;Lo;0;L;;;;;N;;;;; 13234;EGYPTIAN HIEROGLYPH NL019;Lo;0;L;;;;;N;;;;; 13235;EGYPTIAN HIEROGLYPH NL020;Lo;0;L;;;;;N;;;;; 13236;EGYPTIAN HIEROGLYPH NU001;Lo;0;L;;;;;N;;;;; 13237;EGYPTIAN HIEROGLYPH NU002;Lo;0;L;;;;;N;;;;; 13238;EGYPTIAN HIEROGLYPH NU003;Lo;0;L;;;;;N;;;;; 13239;EGYPTIAN HIEROGLYPH NU004;Lo;0;L;;;;;N;;;;; 1323A;EGYPTIAN HIEROGLYPH NU005;Lo;0;L;;;;;N;;;;; 1323B;EGYPTIAN HIEROGLYPH NU006;Lo;0;L;;;;;N;;;;; 1323C;EGYPTIAN HIEROGLYPH NU007;Lo;0;L;;;;;N;;;;; 1323D;EGYPTIAN HIEROGLYPH NU008;Lo;0;L;;;;;N;;;;; 1323E;EGYPTIAN HIEROGLYPH NU009;Lo;0;L;;;;;N;;;;; 1323F;EGYPTIAN HIEROGLYPH NU010;Lo;0;L;;;;;N;;;;; 13240;EGYPTIAN HIEROGLYPH NU010A;Lo;0;L;;;;;N;;;;; 13241;EGYPTIAN HIEROGLYPH NU011;Lo;0;L;;;;;N;;;;; 13242;EGYPTIAN HIEROGLYPH NU011A;Lo;0;L;;;;;N;;;;; 13243;EGYPTIAN HIEROGLYPH NU012;Lo;0;L;;;;;N;;;;; 13244;EGYPTIAN HIEROGLYPH NU013;Lo;0;L;;;;;N;;;;; 13245;EGYPTIAN HIEROGLYPH NU014;Lo;0;L;;;;;N;;;;; 13246;EGYPTIAN HIEROGLYPH NU015;Lo;0;L;;;;;N;;;;; 13247;EGYPTIAN HIEROGLYPH NU016;Lo;0;L;;;;;N;;;;; 13248;EGYPTIAN HIEROGLYPH NU017;Lo;0;L;;;;;N;;;;; 13249;EGYPTIAN HIEROGLYPH NU018;Lo;0;L;;;;;N;;;;; 1324A;EGYPTIAN HIEROGLYPH NU018A;Lo;0;L;;;;;N;;;;; 1324B;EGYPTIAN HIEROGLYPH NU019;Lo;0;L;;;;;N;;;;; 1324C;EGYPTIAN HIEROGLYPH NU020;Lo;0;L;;;;;N;;;;; 1324D;EGYPTIAN HIEROGLYPH NU021;Lo;0;L;;;;;N;;;;; 1324E;EGYPTIAN HIEROGLYPH NU022;Lo;0;L;;;;;N;;;;; 1324F;EGYPTIAN HIEROGLYPH NU022A;Lo;0;L;;;;;N;;;;; 13250;EGYPTIAN HIEROGLYPH O001;Lo;0;L;;;;;N;;;;; 13251;EGYPTIAN HIEROGLYPH O001A;Lo;0;L;;;;;N;;;;; 13252;EGYPTIAN HIEROGLYPH O002;Lo;0;L;;;;;N;;;;; 13253;EGYPTIAN HIEROGLYPH O003;Lo;0;L;;;;;N;;;;; 13254;EGYPTIAN HIEROGLYPH O004;Lo;0;L;;;;;N;;;;; 13255;EGYPTIAN HIEROGLYPH O005;Lo;0;L;;;;;N;;;;; 13256;EGYPTIAN HIEROGLYPH O005A;Lo;0;L;;;;;N;;;;; 13257;EGYPTIAN HIEROGLYPH O006;Lo;0;L;;;;;N;;;;; 13258;EGYPTIAN HIEROGLYPH O006A;Lo;0;L;;;;;N;;;;; 13259;EGYPTIAN HIEROGLYPH O006B;Lo;0;L;;;;;N;;;;; 1325A;EGYPTIAN HIEROGLYPH O006C;Lo;0;L;;;;;N;;;;; 1325B;EGYPTIAN HIEROGLYPH O006D;Lo;0;L;;;;;N;;;;; 1325C;EGYPTIAN HIEROGLYPH O006E;Lo;0;L;;;;;N;;;;; 1325D;EGYPTIAN HIEROGLYPH O006F;Lo;0;L;;;;;N;;;;; 1325E;EGYPTIAN HIEROGLYPH O007;Lo;0;L;;;;;N;;;;; 1325F;EGYPTIAN HIEROGLYPH O008;Lo;0;L;;;;;N;;;;; 13260;EGYPTIAN HIEROGLYPH O009;Lo;0;L;;;;;N;;;;; 13261;EGYPTIAN HIEROGLYPH O010;Lo;0;L;;;;;N;;;;; 13262;EGYPTIAN HIEROGLYPH O010A;Lo;0;L;;;;;N;;;;; 13263;EGYPTIAN HIEROGLYPH O010B;Lo;0;L;;;;;N;;;;; 13264;EGYPTIAN HIEROGLYPH O010C;Lo;0;L;;;;;N;;;;; 13265;EGYPTIAN HIEROGLYPH O011;Lo;0;L;;;;;N;;;;; 13266;EGYPTIAN HIEROGLYPH O012;Lo;0;L;;;;;N;;;;; 13267;EGYPTIAN HIEROGLYPH O013;Lo;0;L;;;;;N;;;;; 13268;EGYPTIAN HIEROGLYPH O014;Lo;0;L;;;;;N;;;;; 13269;EGYPTIAN HIEROGLYPH O015;Lo;0;L;;;;;N;;;;; 1326A;EGYPTIAN HIEROGLYPH O016;Lo;0;L;;;;;N;;;;; 1326B;EGYPTIAN HIEROGLYPH O017;Lo;0;L;;;;;N;;;;; 1326C;EGYPTIAN HIEROGLYPH O018;Lo;0;L;;;;;N;;;;; 1326D;EGYPTIAN HIEROGLYPH O019;Lo;0;L;;;;;N;;;;; 1326E;EGYPTIAN HIEROGLYPH O019A;Lo;0;L;;;;;N;;;;; 1326F;EGYPTIAN HIEROGLYPH O020;Lo;0;L;;;;;N;;;;; 13270;EGYPTIAN HIEROGLYPH O020A;Lo;0;L;;;;;N;;;;; 13271;EGYPTIAN HIEROGLYPH O021;Lo;0;L;;;;;N;;;;; 13272;EGYPTIAN HIEROGLYPH O022;Lo;0;L;;;;;N;;;;; 13273;EGYPTIAN HIEROGLYPH O023;Lo;0;L;;;;;N;;;;; 13274;EGYPTIAN HIEROGLYPH O024;Lo;0;L;;;;;N;;;;; 13275;EGYPTIAN HIEROGLYPH O024A;Lo;0;L;;;;;N;;;;; 13276;EGYPTIAN HIEROGLYPH O025;Lo;0;L;;;;;N;;;;; 13277;EGYPTIAN HIEROGLYPH O025A;Lo;0;L;;;;;N;;;;; 13278;EGYPTIAN HIEROGLYPH O026;Lo;0;L;;;;;N;;;;; 13279;EGYPTIAN HIEROGLYPH O027;Lo;0;L;;;;;N;;;;; 1327A;EGYPTIAN HIEROGLYPH O028;Lo;0;L;;;;;N;;;;; 1327B;EGYPTIAN HIEROGLYPH O029;Lo;0;L;;;;;N;;;;; 1327C;EGYPTIAN HIEROGLYPH O029A;Lo;0;L;;;;;N;;;;; 1327D;EGYPTIAN HIEROGLYPH O030;Lo;0;L;;;;;N;;;;; 1327E;EGYPTIAN HIEROGLYPH O030A;Lo;0;L;;;;;N;;;;; 1327F;EGYPTIAN HIEROGLYPH O031;Lo;0;L;;;;;N;;;;; 13280;EGYPTIAN HIEROGLYPH O032;Lo;0;L;;;;;N;;;;; 13281;EGYPTIAN HIEROGLYPH O033;Lo;0;L;;;;;N;;;;; 13282;EGYPTIAN HIEROGLYPH O033A;Lo;0;L;;;;;N;;;;; 13283;EGYPTIAN HIEROGLYPH O034;Lo;0;L;;;;;N;;;;; 13284;EGYPTIAN HIEROGLYPH O035;Lo;0;L;;;;;N;;;;; 13285;EGYPTIAN HIEROGLYPH O036;Lo;0;L;;;;;N;;;;; 13286;EGYPTIAN HIEROGLYPH O036A;Lo;0;L;;;;;N;;;;; 13287;EGYPTIAN HIEROGLYPH O036B;Lo;0;L;;;;;N;;;;; 13288;EGYPTIAN HIEROGLYPH O036C;Lo;0;L;;;;;N;;;;; 13289;EGYPTIAN HIEROGLYPH O036D;Lo;0;L;;;;;N;;;;; 1328A;EGYPTIAN HIEROGLYPH O037;Lo;0;L;;;;;N;;;;; 1328B;EGYPTIAN HIEROGLYPH O038;Lo;0;L;;;;;N;;;;; 1328C;EGYPTIAN HIEROGLYPH O039;Lo;0;L;;;;;N;;;;; 1328D;EGYPTIAN HIEROGLYPH O040;Lo;0;L;;;;;N;;;;; 1328E;EGYPTIAN HIEROGLYPH O041;Lo;0;L;;;;;N;;;;; 1328F;EGYPTIAN HIEROGLYPH O042;Lo;0;L;;;;;N;;;;; 13290;EGYPTIAN HIEROGLYPH O043;Lo;0;L;;;;;N;;;;; 13291;EGYPTIAN HIEROGLYPH O044;Lo;0;L;;;;;N;;;;; 13292;EGYPTIAN HIEROGLYPH O045;Lo;0;L;;;;;N;;;;; 13293;EGYPTIAN HIEROGLYPH O046;Lo;0;L;;;;;N;;;;; 13294;EGYPTIAN HIEROGLYPH O047;Lo;0;L;;;;;N;;;;; 13295;EGYPTIAN HIEROGLYPH O048;Lo;0;L;;;;;N;;;;; 13296;EGYPTIAN HIEROGLYPH O049;Lo;0;L;;;;;N;;;;; 13297;EGYPTIAN HIEROGLYPH O050;Lo;0;L;;;;;N;;;;; 13298;EGYPTIAN HIEROGLYPH O050A;Lo;0;L;;;;;N;;;;; 13299;EGYPTIAN HIEROGLYPH O050B;Lo;0;L;;;;;N;;;;; 1329A;EGYPTIAN HIEROGLYPH O051;Lo;0;L;;;;;N;;;;; 1329B;EGYPTIAN HIEROGLYPH P001;Lo;0;L;;;;;N;;;;; 1329C;EGYPTIAN HIEROGLYPH P001A;Lo;0;L;;;;;N;;;;; 1329D;EGYPTIAN HIEROGLYPH P002;Lo;0;L;;;;;N;;;;; 1329E;EGYPTIAN HIEROGLYPH P003;Lo;0;L;;;;;N;;;;; 1329F;EGYPTIAN HIEROGLYPH P003A;Lo;0;L;;;;;N;;;;; 132A0;EGYPTIAN HIEROGLYPH P004;Lo;0;L;;;;;N;;;;; 132A1;EGYPTIAN HIEROGLYPH P005;Lo;0;L;;;;;N;;;;; 132A2;EGYPTIAN HIEROGLYPH P006;Lo;0;L;;;;;N;;;;; 132A3;EGYPTIAN HIEROGLYPH P007;Lo;0;L;;;;;N;;;;; 132A4;EGYPTIAN HIEROGLYPH P008;Lo;0;L;;;;;N;;;;; 132A5;EGYPTIAN HIEROGLYPH P009;Lo;0;L;;;;;N;;;;; 132A6;EGYPTIAN HIEROGLYPH P010;Lo;0;L;;;;;N;;;;; 132A7;EGYPTIAN HIEROGLYPH P011;Lo;0;L;;;;;N;;;;; 132A8;EGYPTIAN HIEROGLYPH Q001;Lo;0;L;;;;;N;;;;; 132A9;EGYPTIAN HIEROGLYPH Q002;Lo;0;L;;;;;N;;;;; 132AA;EGYPTIAN HIEROGLYPH Q003;Lo;0;L;;;;;N;;;;; 132AB;EGYPTIAN HIEROGLYPH Q004;Lo;0;L;;;;;N;;;;; 132AC;EGYPTIAN HIEROGLYPH Q005;Lo;0;L;;;;;N;;;;; 132AD;EGYPTIAN HIEROGLYPH Q006;Lo;0;L;;;;;N;;;;; 132AE;EGYPTIAN HIEROGLYPH Q007;Lo;0;L;;;;;N;;;;; 132AF;EGYPTIAN HIEROGLYPH R001;Lo;0;L;;;;;N;;;;; 132B0;EGYPTIAN HIEROGLYPH R002;Lo;0;L;;;;;N;;;;; 132B1;EGYPTIAN HIEROGLYPH R002A;Lo;0;L;;;;;N;;;;; 132B2;EGYPTIAN HIEROGLYPH R003;Lo;0;L;;;;;N;;;;; 132B3;EGYPTIAN HIEROGLYPH R003A;Lo;0;L;;;;;N;;;;; 132B4;EGYPTIAN HIEROGLYPH R003B;Lo;0;L;;;;;N;;;;; 132B5;EGYPTIAN HIEROGLYPH R004;Lo;0;L;;;;;N;;;;; 132B6;EGYPTIAN HIEROGLYPH R005;Lo;0;L;;;;;N;;;;; 132B7;EGYPTIAN HIEROGLYPH R006;Lo;0;L;;;;;N;;;;; 132B8;EGYPTIAN HIEROGLYPH R007;Lo;0;L;;;;;N;;;;; 132B9;EGYPTIAN HIEROGLYPH R008;Lo;0;L;;;;;N;;;;; 132BA;EGYPTIAN HIEROGLYPH R009;Lo;0;L;;;;;N;;;;; 132BB;EGYPTIAN HIEROGLYPH R010;Lo;0;L;;;;;N;;;;; 132BC;EGYPTIAN HIEROGLYPH R010A;Lo;0;L;;;;;N;;;;; 132BD;EGYPTIAN HIEROGLYPH R011;Lo;0;L;;;;;N;;;;; 132BE;EGYPTIAN HIEROGLYPH R012;Lo;0;L;;;;;N;;;;; 132BF;EGYPTIAN HIEROGLYPH R013;Lo;0;L;;;;;N;;;;; 132C0;EGYPTIAN HIEROGLYPH R014;Lo;0;L;;;;;N;;;;; 132C1;EGYPTIAN HIEROGLYPH R015;Lo;0;L;;;;;N;;;;; 132C2;EGYPTIAN HIEROGLYPH R016;Lo;0;L;;;;;N;;;;; 132C3;EGYPTIAN HIEROGLYPH R016A;Lo;0;L;;;;;N;;;;; 132C4;EGYPTIAN HIEROGLYPH R017;Lo;0;L;;;;;N;;;;; 132C5;EGYPTIAN HIEROGLYPH R018;Lo;0;L;;;;;N;;;;; 132C6;EGYPTIAN HIEROGLYPH R019;Lo;0;L;;;;;N;;;;; 132C7;EGYPTIAN HIEROGLYPH R020;Lo;0;L;;;;;N;;;;; 132C8;EGYPTIAN HIEROGLYPH R021;Lo;0;L;;;;;N;;;;; 132C9;EGYPTIAN HIEROGLYPH R022;Lo;0;L;;;;;N;;;;; 132CA;EGYPTIAN HIEROGLYPH R023;Lo;0;L;;;;;N;;;;; 132CB;EGYPTIAN HIEROGLYPH R024;Lo;0;L;;;;;N;;;;; 132CC;EGYPTIAN HIEROGLYPH R025;Lo;0;L;;;;;N;;;;; 132CD;EGYPTIAN HIEROGLYPH R026;Lo;0;L;;;;;N;;;;; 132CE;EGYPTIAN HIEROGLYPH R027;Lo;0;L;;;;;N;;;;; 132CF;EGYPTIAN HIEROGLYPH R028;Lo;0;L;;;;;N;;;;; 132D0;EGYPTIAN HIEROGLYPH R029;Lo;0;L;;;;;N;;;;; 132D1;EGYPTIAN HIEROGLYPH S001;Lo;0;L;;;;;N;;;;; 132D2;EGYPTIAN HIEROGLYPH S002;Lo;0;L;;;;;N;;;;; 132D3;EGYPTIAN HIEROGLYPH S002A;Lo;0;L;;;;;N;;;;; 132D4;EGYPTIAN HIEROGLYPH S003;Lo;0;L;;;;;N;;;;; 132D5;EGYPTIAN HIEROGLYPH S004;Lo;0;L;;;;;N;;;;; 132D6;EGYPTIAN HIEROGLYPH S005;Lo;0;L;;;;;N;;;;; 132D7;EGYPTIAN HIEROGLYPH S006;Lo;0;L;;;;;N;;;;; 132D8;EGYPTIAN HIEROGLYPH S006A;Lo;0;L;;;;;N;;;;; 132D9;EGYPTIAN HIEROGLYPH S007;Lo;0;L;;;;;N;;;;; 132DA;EGYPTIAN HIEROGLYPH S008;Lo;0;L;;;;;N;;;;; 132DB;EGYPTIAN HIEROGLYPH S009;Lo;0;L;;;;;N;;;;; 132DC;EGYPTIAN HIEROGLYPH S010;Lo;0;L;;;;;N;;;;; 132DD;EGYPTIAN HIEROGLYPH S011;Lo;0;L;;;;;N;;;;; 132DE;EGYPTIAN HIEROGLYPH S012;Lo;0;L;;;;;N;;;;; 132DF;EGYPTIAN HIEROGLYPH S013;Lo;0;L;;;;;N;;;;; 132E0;EGYPTIAN HIEROGLYPH S014;Lo;0;L;;;;;N;;;;; 132E1;EGYPTIAN HIEROGLYPH S014A;Lo;0;L;;;;;N;;;;; 132E2;EGYPTIAN HIEROGLYPH S014B;Lo;0;L;;;;;N;;;;; 132E3;EGYPTIAN HIEROGLYPH S015;Lo;0;L;;;;;N;;;;; 132E4;EGYPTIAN HIEROGLYPH S016;Lo;0;L;;;;;N;;;;; 132E5;EGYPTIAN HIEROGLYPH S017;Lo;0;L;;;;;N;;;;; 132E6;EGYPTIAN HIEROGLYPH S017A;Lo;0;L;;;;;N;;;;; 132E7;EGYPTIAN HIEROGLYPH S018;Lo;0;L;;;;;N;;;;; 132E8;EGYPTIAN HIEROGLYPH S019;Lo;0;L;;;;;N;;;;; 132E9;EGYPTIAN HIEROGLYPH S020;Lo;0;L;;;;;N;;;;; 132EA;EGYPTIAN HIEROGLYPH S021;Lo;0;L;;;;;N;;;;; 132EB;EGYPTIAN HIEROGLYPH S022;Lo;0;L;;;;;N;;;;; 132EC;EGYPTIAN HIEROGLYPH S023;Lo;0;L;;;;;N;;;;; 132ED;EGYPTIAN HIEROGLYPH S024;Lo;0;L;;;;;N;;;;; 132EE;EGYPTIAN HIEROGLYPH S025;Lo;0;L;;;;;N;;;;; 132EF;EGYPTIAN HIEROGLYPH S026;Lo;0;L;;;;;N;;;;; 132F0;EGYPTIAN HIEROGLYPH S026A;Lo;0;L;;;;;N;;;;; 132F1;EGYPTIAN HIEROGLYPH S026B;Lo;0;L;;;;;N;;;;; 132F2;EGYPTIAN HIEROGLYPH S027;Lo;0;L;;;;;N;;;;; 132F3;EGYPTIAN HIEROGLYPH S028;Lo;0;L;;;;;N;;;;; 132F4;EGYPTIAN HIEROGLYPH S029;Lo;0;L;;;;;N;;;;; 132F5;EGYPTIAN HIEROGLYPH S030;Lo;0;L;;;;;N;;;;; 132F6;EGYPTIAN HIEROGLYPH S031;Lo;0;L;;;;;N;;;;; 132F7;EGYPTIAN HIEROGLYPH S032;Lo;0;L;;;;;N;;;;; 132F8;EGYPTIAN HIEROGLYPH S033;Lo;0;L;;;;;N;;;;; 132F9;EGYPTIAN HIEROGLYPH S034;Lo;0;L;;;;;N;;;;; 132FA;EGYPTIAN HIEROGLYPH S035;Lo;0;L;;;;;N;;;;; 132FB;EGYPTIAN HIEROGLYPH S035A;Lo;0;L;;;;;N;;;;; 132FC;EGYPTIAN HIEROGLYPH S036;Lo;0;L;;;;;N;;;;; 132FD;EGYPTIAN HIEROGLYPH S037;Lo;0;L;;;;;N;;;;; 132FE;EGYPTIAN HIEROGLYPH S038;Lo;0;L;;;;;N;;;;; 132FF;EGYPTIAN HIEROGLYPH S039;Lo;0;L;;;;;N;;;;; 13300;EGYPTIAN HIEROGLYPH S040;Lo;0;L;;;;;N;;;;; 13301;EGYPTIAN HIEROGLYPH S041;Lo;0;L;;;;;N;;;;; 13302;EGYPTIAN HIEROGLYPH S042;Lo;0;L;;;;;N;;;;; 13303;EGYPTIAN HIEROGLYPH S043;Lo;0;L;;;;;N;;;;; 13304;EGYPTIAN HIEROGLYPH S044;Lo;0;L;;;;;N;;;;; 13305;EGYPTIAN HIEROGLYPH S045;Lo;0;L;;;;;N;;;;; 13306;EGYPTIAN HIEROGLYPH S046;Lo;0;L;;;;;N;;;;; 13307;EGYPTIAN HIEROGLYPH T001;Lo;0;L;;;;;N;;;;; 13308;EGYPTIAN HIEROGLYPH T002;Lo;0;L;;;;;N;;;;; 13309;EGYPTIAN HIEROGLYPH T003;Lo;0;L;;;;;N;;;;; 1330A;EGYPTIAN HIEROGLYPH T003A;Lo;0;L;;;;;N;;;;; 1330B;EGYPTIAN HIEROGLYPH T004;Lo;0;L;;;;;N;;;;; 1330C;EGYPTIAN HIEROGLYPH T005;Lo;0;L;;;;;N;;;;; 1330D;EGYPTIAN HIEROGLYPH T006;Lo;0;L;;;;;N;;;;; 1330E;EGYPTIAN HIEROGLYPH T007;Lo;0;L;;;;;N;;;;; 1330F;EGYPTIAN HIEROGLYPH T007A;Lo;0;L;;;;;N;;;;; 13310;EGYPTIAN HIEROGLYPH T008;Lo;0;L;;;;;N;;;;; 13311;EGYPTIAN HIEROGLYPH T008A;Lo;0;L;;;;;N;;;;; 13312;EGYPTIAN HIEROGLYPH T009;Lo;0;L;;;;;N;;;;; 13313;EGYPTIAN HIEROGLYPH T009A;Lo;0;L;;;;;N;;;;; 13314;EGYPTIAN HIEROGLYPH T010;Lo;0;L;;;;;N;;;;; 13315;EGYPTIAN HIEROGLYPH T011;Lo;0;L;;;;;N;;;;; 13316;EGYPTIAN HIEROGLYPH T011A;Lo;0;L;;;;;N;;;;; 13317;EGYPTIAN HIEROGLYPH T012;Lo;0;L;;;;;N;;;;; 13318;EGYPTIAN HIEROGLYPH T013;Lo;0;L;;;;;N;;;;; 13319;EGYPTIAN HIEROGLYPH T014;Lo;0;L;;;;;N;;;;; 1331A;EGYPTIAN HIEROGLYPH T015;Lo;0;L;;;;;N;;;;; 1331B;EGYPTIAN HIEROGLYPH T016;Lo;0;L;;;;;N;;;;; 1331C;EGYPTIAN HIEROGLYPH T016A;Lo;0;L;;;;;N;;;;; 1331D;EGYPTIAN HIEROGLYPH T017;Lo;0;L;;;;;N;;;;; 1331E;EGYPTIAN HIEROGLYPH T018;Lo;0;L;;;;;N;;;;; 1331F;EGYPTIAN HIEROGLYPH T019;Lo;0;L;;;;;N;;;;; 13320;EGYPTIAN HIEROGLYPH T020;Lo;0;L;;;;;N;;;;; 13321;EGYPTIAN HIEROGLYPH T021;Lo;0;L;;;;;N;;;;; 13322;EGYPTIAN HIEROGLYPH T022;Lo;0;L;;;;;N;;;;; 13323;EGYPTIAN HIEROGLYPH T023;Lo;0;L;;;;;N;;;;; 13324;EGYPTIAN HIEROGLYPH T024;Lo;0;L;;;;;N;;;;; 13325;EGYPTIAN HIEROGLYPH T025;Lo;0;L;;;;;N;;;;; 13326;EGYPTIAN HIEROGLYPH T026;Lo;0;L;;;;;N;;;;; 13327;EGYPTIAN HIEROGLYPH T027;Lo;0;L;;;;;N;;;;; 13328;EGYPTIAN HIEROGLYPH T028;Lo;0;L;;;;;N;;;;; 13329;EGYPTIAN HIEROGLYPH T029;Lo;0;L;;;;;N;;;;; 1332A;EGYPTIAN HIEROGLYPH T030;Lo;0;L;;;;;N;;;;; 1332B;EGYPTIAN HIEROGLYPH T031;Lo;0;L;;;;;N;;;;; 1332C;EGYPTIAN HIEROGLYPH T032;Lo;0;L;;;;;N;;;;; 1332D;EGYPTIAN HIEROGLYPH T032A;Lo;0;L;;;;;N;;;;; 1332E;EGYPTIAN HIEROGLYPH T033;Lo;0;L;;;;;N;;;;; 1332F;EGYPTIAN HIEROGLYPH T033A;Lo;0;L;;;;;N;;;;; 13330;EGYPTIAN HIEROGLYPH T034;Lo;0;L;;;;;N;;;;; 13331;EGYPTIAN HIEROGLYPH T035;Lo;0;L;;;;;N;;;;; 13332;EGYPTIAN HIEROGLYPH T036;Lo;0;L;;;;;N;;;;; 13333;EGYPTIAN HIEROGLYPH U001;Lo;0;L;;;;;N;;;;; 13334;EGYPTIAN HIEROGLYPH U002;Lo;0;L;;;;;N;;;;; 13335;EGYPTIAN HIEROGLYPH U003;Lo;0;L;;;;;N;;;;; 13336;EGYPTIAN HIEROGLYPH U004;Lo;0;L;;;;;N;;;;; 13337;EGYPTIAN HIEROGLYPH U005;Lo;0;L;;;;;N;;;;; 13338;EGYPTIAN HIEROGLYPH U006;Lo;0;L;;;;;N;;;;; 13339;EGYPTIAN HIEROGLYPH U006A;Lo;0;L;;;;;N;;;;; 1333A;EGYPTIAN HIEROGLYPH U006B;Lo;0;L;;;;;N;;;;; 1333B;EGYPTIAN HIEROGLYPH U007;Lo;0;L;;;;;N;;;;; 1333C;EGYPTIAN HIEROGLYPH U008;Lo;0;L;;;;;N;;;;; 1333D;EGYPTIAN HIEROGLYPH U009;Lo;0;L;;;;;N;;;;; 1333E;EGYPTIAN HIEROGLYPH U010;Lo;0;L;;;;;N;;;;; 1333F;EGYPTIAN HIEROGLYPH U011;Lo;0;L;;;;;N;;;;; 13340;EGYPTIAN HIEROGLYPH U012;Lo;0;L;;;;;N;;;;; 13341;EGYPTIAN HIEROGLYPH U013;Lo;0;L;;;;;N;;;;; 13342;EGYPTIAN HIEROGLYPH U014;Lo;0;L;;;;;N;;;;; 13343;EGYPTIAN HIEROGLYPH U015;Lo;0;L;;;;;N;;;;; 13344;EGYPTIAN HIEROGLYPH U016;Lo;0;L;;;;;N;;;;; 13345;EGYPTIAN HIEROGLYPH U017;Lo;0;L;;;;;N;;;;; 13346;EGYPTIAN HIEROGLYPH U018;Lo;0;L;;;;;N;;;;; 13347;EGYPTIAN HIEROGLYPH U019;Lo;0;L;;;;;N;;;;; 13348;EGYPTIAN HIEROGLYPH U020;Lo;0;L;;;;;N;;;;; 13349;EGYPTIAN HIEROGLYPH U021;Lo;0;L;;;;;N;;;;; 1334A;EGYPTIAN HIEROGLYPH U022;Lo;0;L;;;;;N;;;;; 1334B;EGYPTIAN HIEROGLYPH U023;Lo;0;L;;;;;N;;;;; 1334C;EGYPTIAN HIEROGLYPH U023A;Lo;0;L;;;;;N;;;;; 1334D;EGYPTIAN HIEROGLYPH U024;Lo;0;L;;;;;N;;;;; 1334E;EGYPTIAN HIEROGLYPH U025;Lo;0;L;;;;;N;;;;; 1334F;EGYPTIAN HIEROGLYPH U026;Lo;0;L;;;;;N;;;;; 13350;EGYPTIAN HIEROGLYPH U027;Lo;0;L;;;;;N;;;;; 13351;EGYPTIAN HIEROGLYPH U028;Lo;0;L;;;;;N;;;;; 13352;EGYPTIAN HIEROGLYPH U029;Lo;0;L;;;;;N;;;;; 13353;EGYPTIAN HIEROGLYPH U029A;Lo;0;L;;;;;N;;;;; 13354;EGYPTIAN HIEROGLYPH U030;Lo;0;L;;;;;N;;;;; 13355;EGYPTIAN HIEROGLYPH U031;Lo;0;L;;;;;N;;;;; 13356;EGYPTIAN HIEROGLYPH U032;Lo;0;L;;;;;N;;;;; 13357;EGYPTIAN HIEROGLYPH U032A;Lo;0;L;;;;;N;;;;; 13358;EGYPTIAN HIEROGLYPH U033;Lo;0;L;;;;;N;;;;; 13359;EGYPTIAN HIEROGLYPH U034;Lo;0;L;;;;;N;;;;; 1335A;EGYPTIAN HIEROGLYPH U035;Lo;0;L;;;;;N;;;;; 1335B;EGYPTIAN HIEROGLYPH U036;Lo;0;L;;;;;N;;;;; 1335C;EGYPTIAN HIEROGLYPH U037;Lo;0;L;;;;;N;;;;; 1335D;EGYPTIAN HIEROGLYPH U038;Lo;0;L;;;;;N;;;;; 1335E;EGYPTIAN HIEROGLYPH U039;Lo;0;L;;;;;N;;;;; 1335F;EGYPTIAN HIEROGLYPH U040;Lo;0;L;;;;;N;;;;; 13360;EGYPTIAN HIEROGLYPH U041;Lo;0;L;;;;;N;;;;; 13361;EGYPTIAN HIEROGLYPH U042;Lo;0;L;;;;;N;;;;; 13362;EGYPTIAN HIEROGLYPH V001;Lo;0;L;;;;;N;;;;; 13363;EGYPTIAN HIEROGLYPH V001A;Lo;0;L;;;;;N;;;;; 13364;EGYPTIAN HIEROGLYPH V001B;Lo;0;L;;;;;N;;;;; 13365;EGYPTIAN HIEROGLYPH V001C;Lo;0;L;;;;;N;;;;; 13366;EGYPTIAN HIEROGLYPH V001D;Lo;0;L;;;;;N;;;;; 13367;EGYPTIAN HIEROGLYPH V001E;Lo;0;L;;;;;N;;;;; 13368;EGYPTIAN HIEROGLYPH V001F;Lo;0;L;;;;;N;;;;; 13369;EGYPTIAN HIEROGLYPH V001G;Lo;0;L;;;;;N;;;;; 1336A;EGYPTIAN HIEROGLYPH V001H;Lo;0;L;;;;;N;;;;; 1336B;EGYPTIAN HIEROGLYPH V001I;Lo;0;L;;;;;N;;;;; 1336C;EGYPTIAN HIEROGLYPH V002;Lo;0;L;;;;;N;;;;; 1336D;EGYPTIAN HIEROGLYPH V002A;Lo;0;L;;;;;N;;;;; 1336E;EGYPTIAN HIEROGLYPH V003;Lo;0;L;;;;;N;;;;; 1336F;EGYPTIAN HIEROGLYPH V004;Lo;0;L;;;;;N;;;;; 13370;EGYPTIAN HIEROGLYPH V005;Lo;0;L;;;;;N;;;;; 13371;EGYPTIAN HIEROGLYPH V006;Lo;0;L;;;;;N;;;;; 13372;EGYPTIAN HIEROGLYPH V007;Lo;0;L;;;;;N;;;;; 13373;EGYPTIAN HIEROGLYPH V007A;Lo;0;L;;;;;N;;;;; 13374;EGYPTIAN HIEROGLYPH V007B;Lo;0;L;;;;;N;;;;; 13375;EGYPTIAN HIEROGLYPH V008;Lo;0;L;;;;;N;;;;; 13376;EGYPTIAN HIEROGLYPH V009;Lo;0;L;;;;;N;;;;; 13377;EGYPTIAN HIEROGLYPH V010;Lo;0;L;;;;;N;;;;; 13378;EGYPTIAN HIEROGLYPH V011;Lo;0;L;;;;;N;;;;; 13379;EGYPTIAN HIEROGLYPH V011A;Lo;0;L;;;;;N;;;;; 1337A;EGYPTIAN HIEROGLYPH V011B;Lo;0;L;;;;;N;;;;; 1337B;EGYPTIAN HIEROGLYPH V011C;Lo;0;L;;;;;N;;;;; 1337C;EGYPTIAN HIEROGLYPH V012;Lo;0;L;;;;;N;;;;; 1337D;EGYPTIAN HIEROGLYPH V012A;Lo;0;L;;;;;N;;;;; 1337E;EGYPTIAN HIEROGLYPH V012B;Lo;0;L;;;;;N;;;;; 1337F;EGYPTIAN HIEROGLYPH V013;Lo;0;L;;;;;N;;;;; 13380;EGYPTIAN HIEROGLYPH V014;Lo;0;L;;;;;N;;;;; 13381;EGYPTIAN HIEROGLYPH V015;Lo;0;L;;;;;N;;;;; 13382;EGYPTIAN HIEROGLYPH V016;Lo;0;L;;;;;N;;;;; 13383;EGYPTIAN HIEROGLYPH V017;Lo;0;L;;;;;N;;;;; 13384;EGYPTIAN HIEROGLYPH V018;Lo;0;L;;;;;N;;;;; 13385;EGYPTIAN HIEROGLYPH V019;Lo;0;L;;;;;N;;;;; 13386;EGYPTIAN HIEROGLYPH V020;Lo;0;L;;;;;N;;;;; 13387;EGYPTIAN HIEROGLYPH V020A;Lo;0;L;;;;;N;;;;; 13388;EGYPTIAN HIEROGLYPH V020B;Lo;0;L;;;;;N;;;;; 13389;EGYPTIAN HIEROGLYPH V020C;Lo;0;L;;;;;N;;;;; 1338A;EGYPTIAN HIEROGLYPH V020D;Lo;0;L;;;;;N;;;;; 1338B;EGYPTIAN HIEROGLYPH V020E;Lo;0;L;;;;;N;;;;; 1338C;EGYPTIAN HIEROGLYPH V020F;Lo;0;L;;;;;N;;;;; 1338D;EGYPTIAN HIEROGLYPH V020G;Lo;0;L;;;;;N;;;;; 1338E;EGYPTIAN HIEROGLYPH V020H;Lo;0;L;;;;;N;;;;; 1338F;EGYPTIAN HIEROGLYPH V020I;Lo;0;L;;;;;N;;;;; 13390;EGYPTIAN HIEROGLYPH V020J;Lo;0;L;;;;;N;;;;; 13391;EGYPTIAN HIEROGLYPH V020K;Lo;0;L;;;;;N;;;;; 13392;EGYPTIAN HIEROGLYPH V020L;Lo;0;L;;;;;N;;;;; 13393;EGYPTIAN HIEROGLYPH V021;Lo;0;L;;;;;N;;;;; 13394;EGYPTIAN HIEROGLYPH V022;Lo;0;L;;;;;N;;;;; 13395;EGYPTIAN HIEROGLYPH V023;Lo;0;L;;;;;N;;;;; 13396;EGYPTIAN HIEROGLYPH V023A;Lo;0;L;;;;;N;;;;; 13397;EGYPTIAN HIEROGLYPH V024;Lo;0;L;;;;;N;;;;; 13398;EGYPTIAN HIEROGLYPH V025;Lo;0;L;;;;;N;;;;; 13399;EGYPTIAN HIEROGLYPH V026;Lo;0;L;;;;;N;;;;; 1339A;EGYPTIAN HIEROGLYPH V027;Lo;0;L;;;;;N;;;;; 1339B;EGYPTIAN HIEROGLYPH V028;Lo;0;L;;;;;N;;;;; 1339C;EGYPTIAN HIEROGLYPH V028A;Lo;0;L;;;;;N;;;;; 1339D;EGYPTIAN HIEROGLYPH V029;Lo;0;L;;;;;N;;;;; 1339E;EGYPTIAN HIEROGLYPH V029A;Lo;0;L;;;;;N;;;;; 1339F;EGYPTIAN HIEROGLYPH V030;Lo;0;L;;;;;N;;;;; 133A0;EGYPTIAN HIEROGLYPH V030A;Lo;0;L;;;;;N;;;;; 133A1;EGYPTIAN HIEROGLYPH V031;Lo;0;L;;;;;N;;;;; 133A2;EGYPTIAN HIEROGLYPH V031A;Lo;0;L;;;;;N;;;;; 133A3;EGYPTIAN HIEROGLYPH V032;Lo;0;L;;;;;N;;;;; 133A4;EGYPTIAN HIEROGLYPH V033;Lo;0;L;;;;;N;;;;; 133A5;EGYPTIAN HIEROGLYPH V033A;Lo;0;L;;;;;N;;;;; 133A6;EGYPTIAN HIEROGLYPH V034;Lo;0;L;;;;;N;;;;; 133A7;EGYPTIAN HIEROGLYPH V035;Lo;0;L;;;;;N;;;;; 133A8;EGYPTIAN HIEROGLYPH V036;Lo;0;L;;;;;N;;;;; 133A9;EGYPTIAN HIEROGLYPH V037;Lo;0;L;;;;;N;;;;; 133AA;EGYPTIAN HIEROGLYPH V037A;Lo;0;L;;;;;N;;;;; 133AB;EGYPTIAN HIEROGLYPH V038;Lo;0;L;;;;;N;;;;; 133AC;EGYPTIAN HIEROGLYPH V039;Lo;0;L;;;;;N;;;;; 133AD;EGYPTIAN HIEROGLYPH V040;Lo;0;L;;;;;N;;;;; 133AE;EGYPTIAN HIEROGLYPH V040A;Lo;0;L;;;;;N;;;;; 133AF;EGYPTIAN HIEROGLYPH W001;Lo;0;L;;;;;N;;;;; 133B0;EGYPTIAN HIEROGLYPH W002;Lo;0;L;;;;;N;;;;; 133B1;EGYPTIAN HIEROGLYPH W003;Lo;0;L;;;;;N;;;;; 133B2;EGYPTIAN HIEROGLYPH W003A;Lo;0;L;;;;;N;;;;; 133B3;EGYPTIAN HIEROGLYPH W004;Lo;0;L;;;;;N;;;;; 133B4;EGYPTIAN HIEROGLYPH W005;Lo;0;L;;;;;N;;;;; 133B5;EGYPTIAN HIEROGLYPH W006;Lo;0;L;;;;;N;;;;; 133B6;EGYPTIAN HIEROGLYPH W007;Lo;0;L;;;;;N;;;;; 133B7;EGYPTIAN HIEROGLYPH W008;Lo;0;L;;;;;N;;;;; 133B8;EGYPTIAN HIEROGLYPH W009;Lo;0;L;;;;;N;;;;; 133B9;EGYPTIAN HIEROGLYPH W009A;Lo;0;L;;;;;N;;;;; 133BA;EGYPTIAN HIEROGLYPH W010;Lo;0;L;;;;;N;;;;; 133BB;EGYPTIAN HIEROGLYPH W010A;Lo;0;L;;;;;N;;;;; 133BC;EGYPTIAN HIEROGLYPH W011;Lo;0;L;;;;;N;;;;; 133BD;EGYPTIAN HIEROGLYPH W012;Lo;0;L;;;;;N;;;;; 133BE;EGYPTIAN HIEROGLYPH W013;Lo;0;L;;;;;N;;;;; 133BF;EGYPTIAN HIEROGLYPH W014;Lo;0;L;;;;;N;;;;; 133C0;EGYPTIAN HIEROGLYPH W014A;Lo;0;L;;;;;N;;;;; 133C1;EGYPTIAN HIEROGLYPH W015;Lo;0;L;;;;;N;;;;; 133C2;EGYPTIAN HIEROGLYPH W016;Lo;0;L;;;;;N;;;;; 133C3;EGYPTIAN HIEROGLYPH W017;Lo;0;L;;;;;N;;;;; 133C4;EGYPTIAN HIEROGLYPH W017A;Lo;0;L;;;;;N;;;;; 133C5;EGYPTIAN HIEROGLYPH W018;Lo;0;L;;;;;N;;;;; 133C6;EGYPTIAN HIEROGLYPH W018A;Lo;0;L;;;;;N;;;;; 133C7;EGYPTIAN HIEROGLYPH W019;Lo;0;L;;;;;N;;;;; 133C8;EGYPTIAN HIEROGLYPH W020;Lo;0;L;;;;;N;;;;; 133C9;EGYPTIAN HIEROGLYPH W021;Lo;0;L;;;;;N;;;;; 133CA;EGYPTIAN HIEROGLYPH W022;Lo;0;L;;;;;N;;;;; 133CB;EGYPTIAN HIEROGLYPH W023;Lo;0;L;;;;;N;;;;; 133CC;EGYPTIAN HIEROGLYPH W024;Lo;0;L;;;;;N;;;;; 133CD;EGYPTIAN HIEROGLYPH W024A;Lo;0;L;;;;;N;;;;; 133CE;EGYPTIAN HIEROGLYPH W025;Lo;0;L;;;;;N;;;;; 133CF;EGYPTIAN HIEROGLYPH X001;Lo;0;L;;;;;N;;;;; 133D0;EGYPTIAN HIEROGLYPH X002;Lo;0;L;;;;;N;;;;; 133D1;EGYPTIAN HIEROGLYPH X003;Lo;0;L;;;;;N;;;;; 133D2;EGYPTIAN HIEROGLYPH X004;Lo;0;L;;;;;N;;;;; 133D3;EGYPTIAN HIEROGLYPH X004A;Lo;0;L;;;;;N;;;;; 133D4;EGYPTIAN HIEROGLYPH X004B;Lo;0;L;;;;;N;;;;; 133D5;EGYPTIAN HIEROGLYPH X005;Lo;0;L;;;;;N;;;;; 133D6;EGYPTIAN HIEROGLYPH X006;Lo;0;L;;;;;N;;;;; 133D7;EGYPTIAN HIEROGLYPH X006A;Lo;0;L;;;;;N;;;;; 133D8;EGYPTIAN HIEROGLYPH X007;Lo;0;L;;;;;N;;;;; 133D9;EGYPTIAN HIEROGLYPH X008;Lo;0;L;;;;;N;;;;; 133DA;EGYPTIAN HIEROGLYPH X008A;Lo;0;L;;;;;N;;;;; 133DB;EGYPTIAN HIEROGLYPH Y001;Lo;0;L;;;;;N;;;;; 133DC;EGYPTIAN HIEROGLYPH Y001A;Lo;0;L;;;;;N;;;;; 133DD;EGYPTIAN HIEROGLYPH Y002;Lo;0;L;;;;;N;;;;; 133DE;EGYPTIAN HIEROGLYPH Y003;Lo;0;L;;;;;N;;;;; 133DF;EGYPTIAN HIEROGLYPH Y004;Lo;0;L;;;;;N;;;;; 133E0;EGYPTIAN HIEROGLYPH Y005;Lo;0;L;;;;;N;;;;; 133E1;EGYPTIAN HIEROGLYPH Y006;Lo;0;L;;;;;N;;;;; 133E2;EGYPTIAN HIEROGLYPH Y007;Lo;0;L;;;;;N;;;;; 133E3;EGYPTIAN HIEROGLYPH Y008;Lo;0;L;;;;;N;;;;; 133E4;EGYPTIAN HIEROGLYPH Z001;Lo;0;L;;;;;N;;;;; 133E5;EGYPTIAN HIEROGLYPH Z002;Lo;0;L;;;;;N;;;;; 133E6;EGYPTIAN HIEROGLYPH Z002A;Lo;0;L;;;;;N;;;;; 133E7;EGYPTIAN HIEROGLYPH Z002B;Lo;0;L;;;;;N;;;;; 133E8;EGYPTIAN HIEROGLYPH Z002C;Lo;0;L;;;;;N;;;;; 133E9;EGYPTIAN HIEROGLYPH Z002D;Lo;0;L;;;;;N;;;;; 133EA;EGYPTIAN HIEROGLYPH Z003;Lo;0;L;;;;;N;;;;; 133EB;EGYPTIAN HIEROGLYPH Z003A;Lo;0;L;;;;;N;;;;; 133EC;EGYPTIAN HIEROGLYPH Z003B;Lo;0;L;;;;;N;;;;; 133ED;EGYPTIAN HIEROGLYPH Z004;Lo;0;L;;;;;N;;;;; 133EE;EGYPTIAN HIEROGLYPH Z004A;Lo;0;L;;;;;N;;;;; 133EF;EGYPTIAN HIEROGLYPH Z005;Lo;0;L;;;;;N;;;;; 133F0;EGYPTIAN HIEROGLYPH Z005A;Lo;0;L;;;;;N;;;;; 133F1;EGYPTIAN HIEROGLYPH Z006;Lo;0;L;;;;;N;;;;; 133F2;EGYPTIAN HIEROGLYPH Z007;Lo;0;L;;;;;N;;;;; 133F3;EGYPTIAN HIEROGLYPH Z008;Lo;0;L;;;;;N;;;;; 133F4;EGYPTIAN HIEROGLYPH Z009;Lo;0;L;;;;;N;;;;; 133F5;EGYPTIAN HIEROGLYPH Z010;Lo;0;L;;;;;N;;;;; 133F6;EGYPTIAN HIEROGLYPH Z011;Lo;0;L;;;;;N;;;;; 133F7;EGYPTIAN HIEROGLYPH Z012;Lo;0;L;;;;;N;;;;; 133F8;EGYPTIAN HIEROGLYPH Z013;Lo;0;L;;;;;N;;;;; 133F9;EGYPTIAN HIEROGLYPH Z014;Lo;0;L;;;;;N;;;;; 133FA;EGYPTIAN HIEROGLYPH Z015;Lo;0;L;;;;;N;;;;; 133FB;EGYPTIAN HIEROGLYPH Z015A;Lo;0;L;;;;;N;;;;; 133FC;EGYPTIAN HIEROGLYPH Z015B;Lo;0;L;;;;;N;;;;; 133FD;EGYPTIAN HIEROGLYPH Z015C;Lo;0;L;;;;;N;;;;; 133FE;EGYPTIAN HIEROGLYPH Z015D;Lo;0;L;;;;;N;;;;; 133FF;EGYPTIAN HIEROGLYPH Z015E;Lo;0;L;;;;;N;;;;; 13400;EGYPTIAN HIEROGLYPH Z015F;Lo;0;L;;;;;N;;;;; 13401;EGYPTIAN HIEROGLYPH Z015G;Lo;0;L;;;;;N;;;;; 13402;EGYPTIAN HIEROGLYPH Z015H;Lo;0;L;;;;;N;;;;; 13403;EGYPTIAN HIEROGLYPH Z015I;Lo;0;L;;;;;N;;;;; 13404;EGYPTIAN HIEROGLYPH Z016;Lo;0;L;;;;;N;;;;; 13405;EGYPTIAN HIEROGLYPH Z016A;Lo;0;L;;;;;N;;;;; 13406;EGYPTIAN HIEROGLYPH Z016B;Lo;0;L;;;;;N;;;;; 13407;EGYPTIAN HIEROGLYPH Z016C;Lo;0;L;;;;;N;;;;; 13408;EGYPTIAN HIEROGLYPH Z016D;Lo;0;L;;;;;N;;;;; 13409;EGYPTIAN HIEROGLYPH Z016E;Lo;0;L;;;;;N;;;;; 1340A;EGYPTIAN HIEROGLYPH Z016F;Lo;0;L;;;;;N;;;;; 1340B;EGYPTIAN HIEROGLYPH Z016G;Lo;0;L;;;;;N;;;;; 1340C;EGYPTIAN HIEROGLYPH Z016H;Lo;0;L;;;;;N;;;;; 1340D;EGYPTIAN HIEROGLYPH AA001;Lo;0;L;;;;;N;;;;; 1340E;EGYPTIAN HIEROGLYPH AA002;Lo;0;L;;;;;N;;;;; 1340F;EGYPTIAN HIEROGLYPH AA003;Lo;0;L;;;;;N;;;;; 13410;EGYPTIAN HIEROGLYPH AA004;Lo;0;L;;;;;N;;;;; 13411;EGYPTIAN HIEROGLYPH AA005;Lo;0;L;;;;;N;;;;; 13412;EGYPTIAN HIEROGLYPH AA006;Lo;0;L;;;;;N;;;;; 13413;EGYPTIAN HIEROGLYPH AA007;Lo;0;L;;;;;N;;;;; 13414;EGYPTIAN HIEROGLYPH AA007A;Lo;0;L;;;;;N;;;;; 13415;EGYPTIAN HIEROGLYPH AA007B;Lo;0;L;;;;;N;;;;; 13416;EGYPTIAN HIEROGLYPH AA008;Lo;0;L;;;;;N;;;;; 13417;EGYPTIAN HIEROGLYPH AA009;Lo;0;L;;;;;N;;;;; 13418;EGYPTIAN HIEROGLYPH AA010;Lo;0;L;;;;;N;;;;; 13419;EGYPTIAN HIEROGLYPH AA011;Lo;0;L;;;;;N;;;;; 1341A;EGYPTIAN HIEROGLYPH AA012;Lo;0;L;;;;;N;;;;; 1341B;EGYPTIAN HIEROGLYPH AA013;Lo;0;L;;;;;N;;;;; 1341C;EGYPTIAN HIEROGLYPH AA014;Lo;0;L;;;;;N;;;;; 1341D;EGYPTIAN HIEROGLYPH AA015;Lo;0;L;;;;;N;;;;; 1341E;EGYPTIAN HIEROGLYPH AA016;Lo;0;L;;;;;N;;;;; 1341F;EGYPTIAN HIEROGLYPH AA017;Lo;0;L;;;;;N;;;;; 13420;EGYPTIAN HIEROGLYPH AA018;Lo;0;L;;;;;N;;;;; 13421;EGYPTIAN HIEROGLYPH AA019;Lo;0;L;;;;;N;;;;; 13422;EGYPTIAN HIEROGLYPH AA020;Lo;0;L;;;;;N;;;;; 13423;EGYPTIAN HIEROGLYPH AA021;Lo;0;L;;;;;N;;;;; 13424;EGYPTIAN HIEROGLYPH AA022;Lo;0;L;;;;;N;;;;; 13425;EGYPTIAN HIEROGLYPH AA023;Lo;0;L;;;;;N;;;;; 13426;EGYPTIAN HIEROGLYPH AA024;Lo;0;L;;;;;N;;;;; 13427;EGYPTIAN HIEROGLYPH AA025;Lo;0;L;;;;;N;;;;; 13428;EGYPTIAN HIEROGLYPH AA026;Lo;0;L;;;;;N;;;;; 13429;EGYPTIAN HIEROGLYPH AA027;Lo;0;L;;;;;N;;;;; 1342A;EGYPTIAN HIEROGLYPH AA028;Lo;0;L;;;;;N;;;;; 1342B;EGYPTIAN HIEROGLYPH AA029;Lo;0;L;;;;;N;;;;; 1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;; 1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;; 1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;; 14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;; 14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;; 14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;; 14403;ANATOLIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;; 14404;ANATOLIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;; 14405;ANATOLIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;; 14406;ANATOLIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;; 14407;ANATOLIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;; 14408;ANATOLIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;; 14409;ANATOLIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;; 1440A;ANATOLIAN HIEROGLYPH A010A;Lo;0;L;;;;;N;;;;; 1440B;ANATOLIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;; 1440C;ANATOLIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;; 1440D;ANATOLIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;; 1440E;ANATOLIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;; 1440F;ANATOLIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;; 14410;ANATOLIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;; 14411;ANATOLIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;; 14412;ANATOLIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;; 14413;ANATOLIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;; 14414;ANATOLIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;; 14415;ANATOLIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;; 14416;ANATOLIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;; 14417;ANATOLIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;; 14418;ANATOLIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;; 14419;ANATOLIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;; 1441A;ANATOLIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;; 1441B;ANATOLIAN HIEROGLYPH A026A;Lo;0;L;;;;;N;;;;; 1441C;ANATOLIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;; 1441D;ANATOLIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;; 1441E;ANATOLIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;; 1441F;ANATOLIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;; 14420;ANATOLIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;; 14421;ANATOLIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;; 14422;ANATOLIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;; 14423;ANATOLIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;; 14424;ANATOLIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;; 14425;ANATOLIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;; 14426;ANATOLIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;; 14427;ANATOLIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;; 14428;ANATOLIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;; 14429;ANATOLIAN HIEROGLYPH A039A;Lo;0;L;;;;;N;;;;; 1442A;ANATOLIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;; 1442B;ANATOLIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;; 1442C;ANATOLIAN HIEROGLYPH A041A;Lo;0;L;;;;;N;;;;; 1442D;ANATOLIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;; 1442E;ANATOLIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;; 1442F;ANATOLIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;; 14430;ANATOLIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;; 14431;ANATOLIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;; 14432;ANATOLIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;; 14433;ANATOLIAN HIEROGLYPH A046A;Lo;0;L;;;;;N;;;;; 14434;ANATOLIAN HIEROGLYPH A046B;Lo;0;L;;;;;N;;;;; 14435;ANATOLIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;; 14436;ANATOLIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;; 14437;ANATOLIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;; 14438;ANATOLIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;; 14439;ANATOLIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;; 1443A;ANATOLIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;; 1443B;ANATOLIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;; 1443C;ANATOLIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;; 1443D;ANATOLIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;; 1443E;ANATOLIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;; 1443F;ANATOLIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;; 14440;ANATOLIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;; 14441;ANATOLIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;; 14442;ANATOLIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;; 14443;ANATOLIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;; 14444;ANATOLIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;; 14445;ANATOLIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;; 14446;ANATOLIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;; 14447;ANATOLIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;; 14448;ANATOLIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;; 14449;ANATOLIAN HIEROGLYPH A066A;Lo;0;L;;;;;N;;;;; 1444A;ANATOLIAN HIEROGLYPH A066B;Lo;0;L;;;;;N;;;;; 1444B;ANATOLIAN HIEROGLYPH A066C;Lo;0;L;;;;;N;;;;; 1444C;ANATOLIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;; 1444D;ANATOLIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;; 1444E;ANATOLIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;; 1444F;ANATOLIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;; 14450;ANATOLIAN HIEROGLYPH A071;Lo;0;L;;;;;N;;;;; 14451;ANATOLIAN HIEROGLYPH A072;Lo;0;L;;;;;N;;;;; 14452;ANATOLIAN HIEROGLYPH A073;Lo;0;L;;;;;N;;;;; 14453;ANATOLIAN HIEROGLYPH A074;Lo;0;L;;;;;N;;;;; 14454;ANATOLIAN HIEROGLYPH A075;Lo;0;L;;;;;N;;;;; 14455;ANATOLIAN HIEROGLYPH A076;Lo;0;L;;;;;N;;;;; 14456;ANATOLIAN HIEROGLYPH A077;Lo;0;L;;;;;N;;;;; 14457;ANATOLIAN HIEROGLYPH A078;Lo;0;L;;;;;N;;;;; 14458;ANATOLIAN HIEROGLYPH A079;Lo;0;L;;;;;N;;;;; 14459;ANATOLIAN HIEROGLYPH A080;Lo;0;L;;;;;N;;;;; 1445A;ANATOLIAN HIEROGLYPH A081;Lo;0;L;;;;;N;;;;; 1445B;ANATOLIAN HIEROGLYPH A082;Lo;0;L;;;;;N;;;;; 1445C;ANATOLIAN HIEROGLYPH A083;Lo;0;L;;;;;N;;;;; 1445D;ANATOLIAN HIEROGLYPH A084;Lo;0;L;;;;;N;;;;; 1445E;ANATOLIAN HIEROGLYPH A085;Lo;0;L;;;;;N;;;;; 1445F;ANATOLIAN HIEROGLYPH A086;Lo;0;L;;;;;N;;;;; 14460;ANATOLIAN HIEROGLYPH A087;Lo;0;L;;;;;N;;;;; 14461;ANATOLIAN HIEROGLYPH A088;Lo;0;L;;;;;N;;;;; 14462;ANATOLIAN HIEROGLYPH A089;Lo;0;L;;;;;N;;;;; 14463;ANATOLIAN HIEROGLYPH A090;Lo;0;L;;;;;N;;;;; 14464;ANATOLIAN HIEROGLYPH A091;Lo;0;L;;;;;N;;;;; 14465;ANATOLIAN HIEROGLYPH A092;Lo;0;L;;;;;N;;;;; 14466;ANATOLIAN HIEROGLYPH A093;Lo;0;L;;;;;N;;;;; 14467;ANATOLIAN HIEROGLYPH A094;Lo;0;L;;;;;N;;;;; 14468;ANATOLIAN HIEROGLYPH A095;Lo;0;L;;;;;N;;;;; 14469;ANATOLIAN HIEROGLYPH A096;Lo;0;L;;;;;N;;;;; 1446A;ANATOLIAN HIEROGLYPH A097;Lo;0;L;;;;;N;;;;; 1446B;ANATOLIAN HIEROGLYPH A097A;Lo;0;L;;;;;N;;;;; 1446C;ANATOLIAN HIEROGLYPH A098;Lo;0;L;;;;;N;;;;; 1446D;ANATOLIAN HIEROGLYPH A098A;Lo;0;L;;;;;N;;;;; 1446E;ANATOLIAN HIEROGLYPH A099;Lo;0;L;;;;;N;;;;; 1446F;ANATOLIAN HIEROGLYPH A100;Lo;0;L;;;;;N;;;;; 14470;ANATOLIAN HIEROGLYPH A100A;Lo;0;L;;;;;N;;;;; 14471;ANATOLIAN HIEROGLYPH A101;Lo;0;L;;;;;N;;;;; 14472;ANATOLIAN HIEROGLYPH A101A;Lo;0;L;;;;;N;;;;; 14473;ANATOLIAN HIEROGLYPH A102;Lo;0;L;;;;;N;;;;; 14474;ANATOLIAN HIEROGLYPH A102A;Lo;0;L;;;;;N;;;;; 14475;ANATOLIAN HIEROGLYPH A103;Lo;0;L;;;;;N;;;;; 14476;ANATOLIAN HIEROGLYPH A104;Lo;0;L;;;;;N;;;;; 14477;ANATOLIAN HIEROGLYPH A104A;Lo;0;L;;;;;N;;;;; 14478;ANATOLIAN HIEROGLYPH A104B;Lo;0;L;;;;;N;;;;; 14479;ANATOLIAN HIEROGLYPH A104C;Lo;0;L;;;;;N;;;;; 1447A;ANATOLIAN HIEROGLYPH A105;Lo;0;L;;;;;N;;;;; 1447B;ANATOLIAN HIEROGLYPH A105A;Lo;0;L;;;;;N;;;;; 1447C;ANATOLIAN HIEROGLYPH A105B;Lo;0;L;;;;;N;;;;; 1447D;ANATOLIAN HIEROGLYPH A106;Lo;0;L;;;;;N;;;;; 1447E;ANATOLIAN HIEROGLYPH A107;Lo;0;L;;;;;N;;;;; 1447F;ANATOLIAN HIEROGLYPH A107A;Lo;0;L;;;;;N;;;;; 14480;ANATOLIAN HIEROGLYPH A107B;Lo;0;L;;;;;N;;;;; 14481;ANATOLIAN HIEROGLYPH A107C;Lo;0;L;;;;;N;;;;; 14482;ANATOLIAN HIEROGLYPH A108;Lo;0;L;;;;;N;;;;; 14483;ANATOLIAN HIEROGLYPH A109;Lo;0;L;;;;;N;;;;; 14484;ANATOLIAN HIEROGLYPH A110;Lo;0;L;;;;;N;;;;; 14485;ANATOLIAN HIEROGLYPH A110A;Lo;0;L;;;;;N;;;;; 14486;ANATOLIAN HIEROGLYPH A110B;Lo;0;L;;;;;N;;;;; 14487;ANATOLIAN HIEROGLYPH A111;Lo;0;L;;;;;N;;;;; 14488;ANATOLIAN HIEROGLYPH A112;Lo;0;L;;;;;N;;;;; 14489;ANATOLIAN HIEROGLYPH A113;Lo;0;L;;;;;N;;;;; 1448A;ANATOLIAN HIEROGLYPH A114;Lo;0;L;;;;;N;;;;; 1448B;ANATOLIAN HIEROGLYPH A115;Lo;0;L;;;;;N;;;;; 1448C;ANATOLIAN HIEROGLYPH A115A;Lo;0;L;;;;;N;;;;; 1448D;ANATOLIAN HIEROGLYPH A116;Lo;0;L;;;;;N;;;;; 1448E;ANATOLIAN HIEROGLYPH A117;Lo;0;L;;;;;N;;;;; 1448F;ANATOLIAN HIEROGLYPH A118;Lo;0;L;;;;;N;;;;; 14490;ANATOLIAN HIEROGLYPH A119;Lo;0;L;;;;;N;;;;; 14491;ANATOLIAN HIEROGLYPH A120;Lo;0;L;;;;;N;;;;; 14492;ANATOLIAN HIEROGLYPH A121;Lo;0;L;;;;;N;;;;; 14493;ANATOLIAN HIEROGLYPH A122;Lo;0;L;;;;;N;;;;; 14494;ANATOLIAN HIEROGLYPH A123;Lo;0;L;;;;;N;;;;; 14495;ANATOLIAN HIEROGLYPH A124;Lo;0;L;;;;;N;;;;; 14496;ANATOLIAN HIEROGLYPH A125;Lo;0;L;;;;;N;;;;; 14497;ANATOLIAN HIEROGLYPH A125A;Lo;0;L;;;;;N;;;;; 14498;ANATOLIAN HIEROGLYPH A126;Lo;0;L;;;;;N;;;;; 14499;ANATOLIAN HIEROGLYPH A127;Lo;0;L;;;;;N;;;;; 1449A;ANATOLIAN HIEROGLYPH A128;Lo;0;L;;;;;N;;;;; 1449B;ANATOLIAN HIEROGLYPH A129;Lo;0;L;;;;;N;;;;; 1449C;ANATOLIAN HIEROGLYPH A130;Lo;0;L;;;;;N;;;;; 1449D;ANATOLIAN HIEROGLYPH A131;Lo;0;L;;;;;N;;;;; 1449E;ANATOLIAN HIEROGLYPH A132;Lo;0;L;;;;;N;;;;; 1449F;ANATOLIAN HIEROGLYPH A133;Lo;0;L;;;;;N;;;;; 144A0;ANATOLIAN HIEROGLYPH A134;Lo;0;L;;;;;N;;;;; 144A1;ANATOLIAN HIEROGLYPH A135;Lo;0;L;;;;;N;;;;; 144A2;ANATOLIAN HIEROGLYPH A135A;Lo;0;L;;;;;N;;;;; 144A3;ANATOLIAN HIEROGLYPH A136;Lo;0;L;;;;;N;;;;; 144A4;ANATOLIAN HIEROGLYPH A137;Lo;0;L;;;;;N;;;;; 144A5;ANATOLIAN HIEROGLYPH A138;Lo;0;L;;;;;N;;;;; 144A6;ANATOLIAN HIEROGLYPH A139;Lo;0;L;;;;;N;;;;; 144A7;ANATOLIAN HIEROGLYPH A140;Lo;0;L;;;;;N;;;;; 144A8;ANATOLIAN HIEROGLYPH A141;Lo;0;L;;;;;N;;;;; 144A9;ANATOLIAN HIEROGLYPH A142;Lo;0;L;;;;;N;;;;; 144AA;ANATOLIAN HIEROGLYPH A143;Lo;0;L;;;;;N;;;;; 144AB;ANATOLIAN HIEROGLYPH A144;Lo;0;L;;;;;N;;;;; 144AC;ANATOLIAN HIEROGLYPH A145;Lo;0;L;;;;;N;;;;; 144AD;ANATOLIAN HIEROGLYPH A146;Lo;0;L;;;;;N;;;;; 144AE;ANATOLIAN HIEROGLYPH A147;Lo;0;L;;;;;N;;;;; 144AF;ANATOLIAN HIEROGLYPH A148;Lo;0;L;;;;;N;;;;; 144B0;ANATOLIAN HIEROGLYPH A149;Lo;0;L;;;;;N;;;;; 144B1;ANATOLIAN HIEROGLYPH A150;Lo;0;L;;;;;N;;;;; 144B2;ANATOLIAN HIEROGLYPH A151;Lo;0;L;;;;;N;;;;; 144B3;ANATOLIAN HIEROGLYPH A152;Lo;0;L;;;;;N;;;;; 144B4;ANATOLIAN HIEROGLYPH A153;Lo;0;L;;;;;N;;;;; 144B5;ANATOLIAN HIEROGLYPH A154;Lo;0;L;;;;;N;;;;; 144B6;ANATOLIAN HIEROGLYPH A155;Lo;0;L;;;;;N;;;;; 144B7;ANATOLIAN HIEROGLYPH A156;Lo;0;L;;;;;N;;;;; 144B8;ANATOLIAN HIEROGLYPH A157;Lo;0;L;;;;;N;;;;; 144B9;ANATOLIAN HIEROGLYPH A158;Lo;0;L;;;;;N;;;;; 144BA;ANATOLIAN HIEROGLYPH A159;Lo;0;L;;;;;N;;;;; 144BB;ANATOLIAN HIEROGLYPH A160;Lo;0;L;;;;;N;;;;; 144BC;ANATOLIAN HIEROGLYPH A161;Lo;0;L;;;;;N;;;;; 144BD;ANATOLIAN HIEROGLYPH A162;Lo;0;L;;;;;N;;;;; 144BE;ANATOLIAN HIEROGLYPH A163;Lo;0;L;;;;;N;;;;; 144BF;ANATOLIAN HIEROGLYPH A164;Lo;0;L;;;;;N;;;;; 144C0;ANATOLIAN HIEROGLYPH A165;Lo;0;L;;;;;N;;;;; 144C1;ANATOLIAN HIEROGLYPH A166;Lo;0;L;;;;;N;;;;; 144C2;ANATOLIAN HIEROGLYPH A167;Lo;0;L;;;;;N;;;;; 144C3;ANATOLIAN HIEROGLYPH A168;Lo;0;L;;;;;N;;;;; 144C4;ANATOLIAN HIEROGLYPH A169;Lo;0;L;;;;;N;;;;; 144C5;ANATOLIAN HIEROGLYPH A170;Lo;0;L;;;;;N;;;;; 144C6;ANATOLIAN HIEROGLYPH A171;Lo;0;L;;;;;N;;;;; 144C7;ANATOLIAN HIEROGLYPH A172;Lo;0;L;;;;;N;;;;; 144C8;ANATOLIAN HIEROGLYPH A173;Lo;0;L;;;;;N;;;;; 144C9;ANATOLIAN HIEROGLYPH A174;Lo;0;L;;;;;N;;;;; 144CA;ANATOLIAN HIEROGLYPH A175;Lo;0;L;;;;;N;;;;; 144CB;ANATOLIAN HIEROGLYPH A176;Lo;0;L;;;;;N;;;;; 144CC;ANATOLIAN HIEROGLYPH A177;Lo;0;L;;;;;N;;;;; 144CD;ANATOLIAN HIEROGLYPH A178;Lo;0;L;;;;;N;;;;; 144CE;ANATOLIAN HIEROGLYPH A179;Lo;0;L;;;;;N;;;;; 144CF;ANATOLIAN HIEROGLYPH A180;Lo;0;L;;;;;N;;;;; 144D0;ANATOLIAN HIEROGLYPH A181;Lo;0;L;;;;;N;;;;; 144D1;ANATOLIAN HIEROGLYPH A182;Lo;0;L;;;;;N;;;;; 144D2;ANATOLIAN HIEROGLYPH A183;Lo;0;L;;;;;N;;;;; 144D3;ANATOLIAN HIEROGLYPH A184;Lo;0;L;;;;;N;;;;; 144D4;ANATOLIAN HIEROGLYPH A185;Lo;0;L;;;;;N;;;;; 144D5;ANATOLIAN HIEROGLYPH A186;Lo;0;L;;;;;N;;;;; 144D6;ANATOLIAN HIEROGLYPH A187;Lo;0;L;;;;;N;;;;; 144D7;ANATOLIAN HIEROGLYPH A188;Lo;0;L;;;;;N;;;;; 144D8;ANATOLIAN HIEROGLYPH A189;Lo;0;L;;;;;N;;;;; 144D9;ANATOLIAN HIEROGLYPH A190;Lo;0;L;;;;;N;;;;; 144DA;ANATOLIAN HIEROGLYPH A191;Lo;0;L;;;;;N;;;;; 144DB;ANATOLIAN HIEROGLYPH A192;Lo;0;L;;;;;N;;;;; 144DC;ANATOLIAN HIEROGLYPH A193;Lo;0;L;;;;;N;;;;; 144DD;ANATOLIAN HIEROGLYPH A194;Lo;0;L;;;;;N;;;;; 144DE;ANATOLIAN HIEROGLYPH A195;Lo;0;L;;;;;N;;;;; 144DF;ANATOLIAN HIEROGLYPH A196;Lo;0;L;;;;;N;;;;; 144E0;ANATOLIAN HIEROGLYPH A197;Lo;0;L;;;;;N;;;;; 144E1;ANATOLIAN HIEROGLYPH A198;Lo;0;L;;;;;N;;;;; 144E2;ANATOLIAN HIEROGLYPH A199;Lo;0;L;;;;;N;;;;; 144E3;ANATOLIAN HIEROGLYPH A200;Lo;0;L;;;;;N;;;;; 144E4;ANATOLIAN HIEROGLYPH A201;Lo;0;L;;;;;N;;;;; 144E5;ANATOLIAN HIEROGLYPH A202;Lo;0;L;;;;;N;;;;; 144E6;ANATOLIAN HIEROGLYPH A202A;Lo;0;L;;;;;N;;;;; 144E7;ANATOLIAN HIEROGLYPH A202B;Lo;0;L;;;;;N;;;;; 144E8;ANATOLIAN HIEROGLYPH A203;Lo;0;L;;;;;N;;;;; 144E9;ANATOLIAN HIEROGLYPH A204;Lo;0;L;;;;;N;;;;; 144EA;ANATOLIAN HIEROGLYPH A205;Lo;0;L;;;;;N;;;;; 144EB;ANATOLIAN HIEROGLYPH A206;Lo;0;L;;;;;N;;;;; 144EC;ANATOLIAN HIEROGLYPH A207;Lo;0;L;;;;;N;;;;; 144ED;ANATOLIAN HIEROGLYPH A207A;Lo;0;L;;;;;N;;;;; 144EE;ANATOLIAN HIEROGLYPH A208;Lo;0;L;;;;;N;;;;; 144EF;ANATOLIAN HIEROGLYPH A209;Lo;0;L;;;;;N;;;;; 144F0;ANATOLIAN HIEROGLYPH A209A;Lo;0;L;;;;;N;;;;; 144F1;ANATOLIAN HIEROGLYPH A210;Lo;0;L;;;;;N;;;;; 144F2;ANATOLIAN HIEROGLYPH A211;Lo;0;L;;;;;N;;;;; 144F3;ANATOLIAN HIEROGLYPH A212;Lo;0;L;;;;;N;;;;; 144F4;ANATOLIAN HIEROGLYPH A213;Lo;0;L;;;;;N;;;;; 144F5;ANATOLIAN HIEROGLYPH A214;Lo;0;L;;;;;N;;;;; 144F6;ANATOLIAN HIEROGLYPH A215;Lo;0;L;;;;;N;;;;; 144F7;ANATOLIAN HIEROGLYPH A215A;Lo;0;L;;;;;N;;;;; 144F8;ANATOLIAN HIEROGLYPH A216;Lo;0;L;;;;;N;;;;; 144F9;ANATOLIAN HIEROGLYPH A216A;Lo;0;L;;;;;N;;;;; 144FA;ANATOLIAN HIEROGLYPH A217;Lo;0;L;;;;;N;;;;; 144FB;ANATOLIAN HIEROGLYPH A218;Lo;0;L;;;;;N;;;;; 144FC;ANATOLIAN HIEROGLYPH A219;Lo;0;L;;;;;N;;;;; 144FD;ANATOLIAN HIEROGLYPH A220;Lo;0;L;;;;;N;;;;; 144FE;ANATOLIAN HIEROGLYPH A221;Lo;0;L;;;;;N;;;;; 144FF;ANATOLIAN HIEROGLYPH A222;Lo;0;L;;;;;N;;;;; 14500;ANATOLIAN HIEROGLYPH A223;Lo;0;L;;;;;N;;;;; 14501;ANATOLIAN HIEROGLYPH A224;Lo;0;L;;;;;N;;;;; 14502;ANATOLIAN HIEROGLYPH A225;Lo;0;L;;;;;N;;;;; 14503;ANATOLIAN HIEROGLYPH A226;Lo;0;L;;;;;N;;;;; 14504;ANATOLIAN HIEROGLYPH A227;Lo;0;L;;;;;N;;;;; 14505;ANATOLIAN HIEROGLYPH A227A;Lo;0;L;;;;;N;;;;; 14506;ANATOLIAN HIEROGLYPH A228;Lo;0;L;;;;;N;;;;; 14507;ANATOLIAN HIEROGLYPH A229;Lo;0;L;;;;;N;;;;; 14508;ANATOLIAN HIEROGLYPH A230;Lo;0;L;;;;;N;;;;; 14509;ANATOLIAN HIEROGLYPH A231;Lo;0;L;;;;;N;;;;; 1450A;ANATOLIAN HIEROGLYPH A232;Lo;0;L;;;;;N;;;;; 1450B;ANATOLIAN HIEROGLYPH A233;Lo;0;L;;;;;N;;;;; 1450C;ANATOLIAN HIEROGLYPH A234;Lo;0;L;;;;;N;;;;; 1450D;ANATOLIAN HIEROGLYPH A235;Lo;0;L;;;;;N;;;;; 1450E;ANATOLIAN HIEROGLYPH A236;Lo;0;L;;;;;N;;;;; 1450F;ANATOLIAN HIEROGLYPH A237;Lo;0;L;;;;;N;;;;; 14510;ANATOLIAN HIEROGLYPH A238;Lo;0;L;;;;;N;;;;; 14511;ANATOLIAN HIEROGLYPH A239;Lo;0;L;;;;;N;;;;; 14512;ANATOLIAN HIEROGLYPH A240;Lo;0;L;;;;;N;;;;; 14513;ANATOLIAN HIEROGLYPH A241;Lo;0;L;;;;;N;;;;; 14514;ANATOLIAN HIEROGLYPH A242;Lo;0;L;;;;;N;;;;; 14515;ANATOLIAN HIEROGLYPH A243;Lo;0;L;;;;;N;;;;; 14516;ANATOLIAN HIEROGLYPH A244;Lo;0;L;;;;;N;;;;; 14517;ANATOLIAN HIEROGLYPH A245;Lo;0;L;;;;;N;;;;; 14518;ANATOLIAN HIEROGLYPH A246;Lo;0;L;;;;;N;;;;; 14519;ANATOLIAN HIEROGLYPH A247;Lo;0;L;;;;;N;;;;; 1451A;ANATOLIAN HIEROGLYPH A248;Lo;0;L;;;;;N;;;;; 1451B;ANATOLIAN HIEROGLYPH A249;Lo;0;L;;;;;N;;;;; 1451C;ANATOLIAN HIEROGLYPH A250;Lo;0;L;;;;;N;;;;; 1451D;ANATOLIAN HIEROGLYPH A251;Lo;0;L;;;;;N;;;;; 1451E;ANATOLIAN HIEROGLYPH A252;Lo;0;L;;;;;N;;;;; 1451F;ANATOLIAN HIEROGLYPH A253;Lo;0;L;;;;;N;;;;; 14520;ANATOLIAN HIEROGLYPH A254;Lo;0;L;;;;;N;;;;; 14521;ANATOLIAN HIEROGLYPH A255;Lo;0;L;;;;;N;;;;; 14522;ANATOLIAN HIEROGLYPH A256;Lo;0;L;;;;;N;;;;; 14523;ANATOLIAN HIEROGLYPH A257;Lo;0;L;;;;;N;;;;; 14524;ANATOLIAN HIEROGLYPH A258;Lo;0;L;;;;;N;;;;; 14525;ANATOLIAN HIEROGLYPH A259;Lo;0;L;;;;;N;;;;; 14526;ANATOLIAN HIEROGLYPH A260;Lo;0;L;;;;;N;;;;; 14527;ANATOLIAN HIEROGLYPH A261;Lo;0;L;;;;;N;;;;; 14528;ANATOLIAN HIEROGLYPH A262;Lo;0;L;;;;;N;;;;; 14529;ANATOLIAN HIEROGLYPH A263;Lo;0;L;;;;;N;;;;; 1452A;ANATOLIAN HIEROGLYPH A264;Lo;0;L;;;;;N;;;;; 1452B;ANATOLIAN HIEROGLYPH A265;Lo;0;L;;;;;N;;;;; 1452C;ANATOLIAN HIEROGLYPH A266;Lo;0;L;;;;;N;;;;; 1452D;ANATOLIAN HIEROGLYPH A267;Lo;0;L;;;;;N;;;;; 1452E;ANATOLIAN HIEROGLYPH A267A;Lo;0;L;;;;;N;;;;; 1452F;ANATOLIAN HIEROGLYPH A268;Lo;0;L;;;;;N;;;;; 14530;ANATOLIAN HIEROGLYPH A269;Lo;0;L;;;;;N;;;;; 14531;ANATOLIAN HIEROGLYPH A270;Lo;0;L;;;;;N;;;;; 14532;ANATOLIAN HIEROGLYPH A271;Lo;0;L;;;;;N;;;;; 14533;ANATOLIAN HIEROGLYPH A272;Lo;0;L;;;;;N;;;;; 14534;ANATOLIAN HIEROGLYPH A273;Lo;0;L;;;;;N;;;;; 14535;ANATOLIAN HIEROGLYPH A274;Lo;0;L;;;;;N;;;;; 14536;ANATOLIAN HIEROGLYPH A275;Lo;0;L;;;;;N;;;;; 14537;ANATOLIAN HIEROGLYPH A276;Lo;0;L;;;;;N;;;;; 14538;ANATOLIAN HIEROGLYPH A277;Lo;0;L;;;;;N;;;;; 14539;ANATOLIAN HIEROGLYPH A278;Lo;0;L;;;;;N;;;;; 1453A;ANATOLIAN HIEROGLYPH A279;Lo;0;L;;;;;N;;;;; 1453B;ANATOLIAN HIEROGLYPH A280;Lo;0;L;;;;;N;;;;; 1453C;ANATOLIAN HIEROGLYPH A281;Lo;0;L;;;;;N;;;;; 1453D;ANATOLIAN HIEROGLYPH A282;Lo;0;L;;;;;N;;;;; 1453E;ANATOLIAN HIEROGLYPH A283;Lo;0;L;;;;;N;;;;; 1453F;ANATOLIAN HIEROGLYPH A284;Lo;0;L;;;;;N;;;;; 14540;ANATOLIAN HIEROGLYPH A285;Lo;0;L;;;;;N;;;;; 14541;ANATOLIAN HIEROGLYPH A286;Lo;0;L;;;;;N;;;;; 14542;ANATOLIAN HIEROGLYPH A287;Lo;0;L;;;;;N;;;;; 14543;ANATOLIAN HIEROGLYPH A288;Lo;0;L;;;;;N;;;;; 14544;ANATOLIAN HIEROGLYPH A289;Lo;0;L;;;;;N;;;;; 14545;ANATOLIAN HIEROGLYPH A289A;Lo;0;L;;;;;N;;;;; 14546;ANATOLIAN HIEROGLYPH A290;Lo;0;L;;;;;N;;;;; 14547;ANATOLIAN HIEROGLYPH A291;Lo;0;L;;;;;N;;;;; 14548;ANATOLIAN HIEROGLYPH A292;Lo;0;L;;;;;N;;;;; 14549;ANATOLIAN HIEROGLYPH A293;Lo;0;L;;;;;N;;;;; 1454A;ANATOLIAN HIEROGLYPH A294;Lo;0;L;;;;;N;;;;; 1454B;ANATOLIAN HIEROGLYPH A294A;Lo;0;L;;;;;N;;;;; 1454C;ANATOLIAN HIEROGLYPH A295;Lo;0;L;;;;;N;;;;; 1454D;ANATOLIAN HIEROGLYPH A296;Lo;0;L;;;;;N;;;;; 1454E;ANATOLIAN HIEROGLYPH A297;Lo;0;L;;;;;N;;;;; 1454F;ANATOLIAN HIEROGLYPH A298;Lo;0;L;;;;;N;;;;; 14550;ANATOLIAN HIEROGLYPH A299;Lo;0;L;;;;;N;;;;; 14551;ANATOLIAN HIEROGLYPH A299A;Lo;0;L;;;;;N;;;;; 14552;ANATOLIAN HIEROGLYPH A300;Lo;0;L;;;;;N;;;;; 14553;ANATOLIAN HIEROGLYPH A301;Lo;0;L;;;;;N;;;;; 14554;ANATOLIAN HIEROGLYPH A302;Lo;0;L;;;;;N;;;;; 14555;ANATOLIAN HIEROGLYPH A303;Lo;0;L;;;;;N;;;;; 14556;ANATOLIAN HIEROGLYPH A304;Lo;0;L;;;;;N;;;;; 14557;ANATOLIAN HIEROGLYPH A305;Lo;0;L;;;;;N;;;;; 14558;ANATOLIAN HIEROGLYPH A306;Lo;0;L;;;;;N;;;;; 14559;ANATOLIAN HIEROGLYPH A307;Lo;0;L;;;;;N;;;;; 1455A;ANATOLIAN HIEROGLYPH A308;Lo;0;L;;;;;N;;;;; 1455B;ANATOLIAN HIEROGLYPH A309;Lo;0;L;;;;;N;;;;; 1455C;ANATOLIAN HIEROGLYPH A309A;Lo;0;L;;;;;N;;;;; 1455D;ANATOLIAN HIEROGLYPH A310;Lo;0;L;;;;;N;;;;; 1455E;ANATOLIAN HIEROGLYPH A311;Lo;0;L;;;;;N;;;;; 1455F;ANATOLIAN HIEROGLYPH A312;Lo;0;L;;;;;N;;;;; 14560;ANATOLIAN HIEROGLYPH A313;Lo;0;L;;;;;N;;;;; 14561;ANATOLIAN HIEROGLYPH A314;Lo;0;L;;;;;N;;;;; 14562;ANATOLIAN HIEROGLYPH A315;Lo;0;L;;;;;N;;;;; 14563;ANATOLIAN HIEROGLYPH A316;Lo;0;L;;;;;N;;;;; 14564;ANATOLIAN HIEROGLYPH A317;Lo;0;L;;;;;N;;;;; 14565;ANATOLIAN HIEROGLYPH A318;Lo;0;L;;;;;N;;;;; 14566;ANATOLIAN HIEROGLYPH A319;Lo;0;L;;;;;N;;;;; 14567;ANATOLIAN HIEROGLYPH A320;Lo;0;L;;;;;N;;;;; 14568;ANATOLIAN HIEROGLYPH A321;Lo;0;L;;;;;N;;;;; 14569;ANATOLIAN HIEROGLYPH A322;Lo;0;L;;;;;N;;;;; 1456A;ANATOLIAN HIEROGLYPH A323;Lo;0;L;;;;;N;;;;; 1456B;ANATOLIAN HIEROGLYPH A324;Lo;0;L;;;;;N;;;;; 1456C;ANATOLIAN HIEROGLYPH A325;Lo;0;L;;;;;N;;;;; 1456D;ANATOLIAN HIEROGLYPH A326;Lo;0;L;;;;;N;;;;; 1456E;ANATOLIAN HIEROGLYPH A327;Lo;0;L;;;;;N;;;;; 1456F;ANATOLIAN HIEROGLYPH A328;Lo;0;L;;;;;N;;;;; 14570;ANATOLIAN HIEROGLYPH A329;Lo;0;L;;;;;N;;;;; 14571;ANATOLIAN HIEROGLYPH A329A;Lo;0;L;;;;;N;;;;; 14572;ANATOLIAN HIEROGLYPH A330;Lo;0;L;;;;;N;;;;; 14573;ANATOLIAN HIEROGLYPH A331;Lo;0;L;;;;;N;;;;; 14574;ANATOLIAN HIEROGLYPH A332A;Lo;0;L;;;;;N;;;;; 14575;ANATOLIAN HIEROGLYPH A332B;Lo;0;L;;;;;N;;;;; 14576;ANATOLIAN HIEROGLYPH A332C;Lo;0;L;;;;;N;;;;; 14577;ANATOLIAN HIEROGLYPH A333;Lo;0;L;;;;;N;;;;; 14578;ANATOLIAN HIEROGLYPH A334;Lo;0;L;;;;;N;;;;; 14579;ANATOLIAN HIEROGLYPH A335;Lo;0;L;;;;;N;;;;; 1457A;ANATOLIAN HIEROGLYPH A336;Lo;0;L;;;;;N;;;;; 1457B;ANATOLIAN HIEROGLYPH A336A;Lo;0;L;;;;;N;;;;; 1457C;ANATOLIAN HIEROGLYPH A336B;Lo;0;L;;;;;N;;;;; 1457D;ANATOLIAN HIEROGLYPH A336C;Lo;0;L;;;;;N;;;;; 1457E;ANATOLIAN HIEROGLYPH A337;Lo;0;L;;;;;N;;;;; 1457F;ANATOLIAN HIEROGLYPH A338;Lo;0;L;;;;;N;;;;; 14580;ANATOLIAN HIEROGLYPH A339;Lo;0;L;;;;;N;;;;; 14581;ANATOLIAN HIEROGLYPH A340;Lo;0;L;;;;;N;;;;; 14582;ANATOLIAN HIEROGLYPH A341;Lo;0;L;;;;;N;;;;; 14583;ANATOLIAN HIEROGLYPH A342;Lo;0;L;;;;;N;;;;; 14584;ANATOLIAN HIEROGLYPH A343;Lo;0;L;;;;;N;;;;; 14585;ANATOLIAN HIEROGLYPH A344;Lo;0;L;;;;;N;;;;; 14586;ANATOLIAN HIEROGLYPH A345;Lo;0;L;;;;;N;;;;; 14587;ANATOLIAN HIEROGLYPH A346;Lo;0;L;;;;;N;;;;; 14588;ANATOLIAN HIEROGLYPH A347;Lo;0;L;;;;;N;;;;; 14589;ANATOLIAN HIEROGLYPH A348;Lo;0;L;;;;;N;;;;; 1458A;ANATOLIAN HIEROGLYPH A349;Lo;0;L;;;;;N;;;;; 1458B;ANATOLIAN HIEROGLYPH A350;Lo;0;L;;;;;N;;;;; 1458C;ANATOLIAN HIEROGLYPH A351;Lo;0;L;;;;;N;;;;; 1458D;ANATOLIAN HIEROGLYPH A352;Lo;0;L;;;;;N;;;;; 1458E;ANATOLIAN HIEROGLYPH A353;Lo;0;L;;;;;N;;;;; 1458F;ANATOLIAN HIEROGLYPH A354;Lo;0;L;;;;;N;;;;; 14590;ANATOLIAN HIEROGLYPH A355;Lo;0;L;;;;;N;;;;; 14591;ANATOLIAN HIEROGLYPH A356;Lo;0;L;;;;;N;;;;; 14592;ANATOLIAN HIEROGLYPH A357;Lo;0;L;;;;;N;;;;; 14593;ANATOLIAN HIEROGLYPH A358;Lo;0;L;;;;;N;;;;; 14594;ANATOLIAN HIEROGLYPH A359;Lo;0;L;;;;;N;;;;; 14595;ANATOLIAN HIEROGLYPH A359A;Lo;0;L;;;;;N;;;;; 14596;ANATOLIAN HIEROGLYPH A360;Lo;0;L;;;;;N;;;;; 14597;ANATOLIAN HIEROGLYPH A361;Lo;0;L;;;;;N;;;;; 14598;ANATOLIAN HIEROGLYPH A362;Lo;0;L;;;;;N;;;;; 14599;ANATOLIAN HIEROGLYPH A363;Lo;0;L;;;;;N;;;;; 1459A;ANATOLIAN HIEROGLYPH A364;Lo;0;L;;;;;N;;;;; 1459B;ANATOLIAN HIEROGLYPH A364A;Lo;0;L;;;;;N;;;;; 1459C;ANATOLIAN HIEROGLYPH A365;Lo;0;L;;;;;N;;;;; 1459D;ANATOLIAN HIEROGLYPH A366;Lo;0;L;;;;;N;;;;; 1459E;ANATOLIAN HIEROGLYPH A367;Lo;0;L;;;;;N;;;;; 1459F;ANATOLIAN HIEROGLYPH A368;Lo;0;L;;;;;N;;;;; 145A0;ANATOLIAN HIEROGLYPH A368A;Lo;0;L;;;;;N;;;;; 145A1;ANATOLIAN HIEROGLYPH A369;Lo;0;L;;;;;N;;;;; 145A2;ANATOLIAN HIEROGLYPH A370;Lo;0;L;;;;;N;;;;; 145A3;ANATOLIAN HIEROGLYPH A371;Lo;0;L;;;;;N;;;;; 145A4;ANATOLIAN HIEROGLYPH A371A;Lo;0;L;;;;;N;;;;; 145A5;ANATOLIAN HIEROGLYPH A372;Lo;0;L;;;;;N;;;;; 145A6;ANATOLIAN HIEROGLYPH A373;Lo;0;L;;;;;N;;;;; 145A7;ANATOLIAN HIEROGLYPH A374;Lo;0;L;;;;;N;;;;; 145A8;ANATOLIAN HIEROGLYPH A375;Lo;0;L;;;;;N;;;;; 145A9;ANATOLIAN HIEROGLYPH A376;Lo;0;L;;;;;N;;;;; 145AA;ANATOLIAN HIEROGLYPH A377;Lo;0;L;;;;;N;;;;; 145AB;ANATOLIAN HIEROGLYPH A378;Lo;0;L;;;;;N;;;;; 145AC;ANATOLIAN HIEROGLYPH A379;Lo;0;L;;;;;N;;;;; 145AD;ANATOLIAN HIEROGLYPH A380;Lo;0;L;;;;;N;;;;; 145AE;ANATOLIAN HIEROGLYPH A381;Lo;0;L;;;;;N;;;;; 145AF;ANATOLIAN HIEROGLYPH A381A;Lo;0;L;;;;;N;;;;; 145B0;ANATOLIAN HIEROGLYPH A382;Lo;0;L;;;;;N;;;;; 145B1;ANATOLIAN HIEROGLYPH A383 RA OR RI;Lo;0;L;;;;;N;;;;; 145B2;ANATOLIAN HIEROGLYPH A383A;Lo;0;L;;;;;N;;;;; 145B3;ANATOLIAN HIEROGLYPH A384;Lo;0;L;;;;;N;;;;; 145B4;ANATOLIAN HIEROGLYPH A385;Lo;0;L;;;;;N;;;;; 145B5;ANATOLIAN HIEROGLYPH A386;Lo;0;L;;;;;N;;;;; 145B6;ANATOLIAN HIEROGLYPH A386A;Lo;0;L;;;;;N;;;;; 145B7;ANATOLIAN HIEROGLYPH A387;Lo;0;L;;;;;N;;;;; 145B8;ANATOLIAN HIEROGLYPH A388;Lo;0;L;;;;;N;;;;; 145B9;ANATOLIAN HIEROGLYPH A389;Lo;0;L;;;;;N;;;;; 145BA;ANATOLIAN HIEROGLYPH A390;Lo;0;L;;;;;N;;;;; 145BB;ANATOLIAN HIEROGLYPH A391;Lo;0;L;;;;;N;;;;; 145BC;ANATOLIAN HIEROGLYPH A392;Lo;0;L;;;;;N;;;;; 145BD;ANATOLIAN HIEROGLYPH A393 EIGHT;Lo;0;L;;;;;N;;;;; 145BE;ANATOLIAN HIEROGLYPH A394;Lo;0;L;;;;;N;;;;; 145BF;ANATOLIAN HIEROGLYPH A395;Lo;0;L;;;;;N;;;;; 145C0;ANATOLIAN HIEROGLYPH A396;Lo;0;L;;;;;N;;;;; 145C1;ANATOLIAN HIEROGLYPH A397;Lo;0;L;;;;;N;;;;; 145C2;ANATOLIAN HIEROGLYPH A398;Lo;0;L;;;;;N;;;;; 145C3;ANATOLIAN HIEROGLYPH A399;Lo;0;L;;;;;N;;;;; 145C4;ANATOLIAN HIEROGLYPH A400;Lo;0;L;;;;;N;;;;; 145C5;ANATOLIAN HIEROGLYPH A401;Lo;0;L;;;;;N;;;;; 145C6;ANATOLIAN HIEROGLYPH A402;Lo;0;L;;;;;N;;;;; 145C7;ANATOLIAN HIEROGLYPH A403;Lo;0;L;;;;;N;;;;; 145C8;ANATOLIAN HIEROGLYPH A404;Lo;0;L;;;;;N;;;;; 145C9;ANATOLIAN HIEROGLYPH A405;Lo;0;L;;;;;N;;;;; 145CA;ANATOLIAN HIEROGLYPH A406;Lo;0;L;;;;;N;;;;; 145CB;ANATOLIAN HIEROGLYPH A407;Lo;0;L;;;;;N;;;;; 145CC;ANATOLIAN HIEROGLYPH A408;Lo;0;L;;;;;N;;;;; 145CD;ANATOLIAN HIEROGLYPH A409;Lo;0;L;;;;;N;;;;; 145CE;ANATOLIAN HIEROGLYPH A410 BEGIN LOGOGRAM MARK;Lo;0;L;;;;;N;;;;; 145CF;ANATOLIAN HIEROGLYPH A410A END LOGOGRAM MARK;Lo;0;L;;;;;N;;;;; 145D0;ANATOLIAN HIEROGLYPH A411;Lo;0;L;;;;;N;;;;; 145D1;ANATOLIAN HIEROGLYPH A412;Lo;0;L;;;;;N;;;;; 145D2;ANATOLIAN HIEROGLYPH A413;Lo;0;L;;;;;N;;;;; 145D3;ANATOLIAN HIEROGLYPH A414;Lo;0;L;;;;;N;;;;; 145D4;ANATOLIAN HIEROGLYPH A415;Lo;0;L;;;;;N;;;;; 145D5;ANATOLIAN HIEROGLYPH A416;Lo;0;L;;;;;N;;;;; 145D6;ANATOLIAN HIEROGLYPH A417;Lo;0;L;;;;;N;;;;; 145D7;ANATOLIAN HIEROGLYPH A418;Lo;0;L;;;;;N;;;;; 145D8;ANATOLIAN HIEROGLYPH A419;Lo;0;L;;;;;N;;;;; 145D9;ANATOLIAN HIEROGLYPH A420;Lo;0;L;;;;;N;;;;; 145DA;ANATOLIAN HIEROGLYPH A421;Lo;0;L;;;;;N;;;;; 145DB;ANATOLIAN HIEROGLYPH A422;Lo;0;L;;;;;N;;;;; 145DC;ANATOLIAN HIEROGLYPH A423;Lo;0;L;;;;;N;;;;; 145DD;ANATOLIAN HIEROGLYPH A424;Lo;0;L;;;;;N;;;;; 145DE;ANATOLIAN HIEROGLYPH A425;Lo;0;L;;;;;N;;;;; 145DF;ANATOLIAN HIEROGLYPH A426;Lo;0;L;;;;;N;;;;; 145E0;ANATOLIAN HIEROGLYPH A427;Lo;0;L;;;;;N;;;;; 145E1;ANATOLIAN HIEROGLYPH A428;Lo;0;L;;;;;N;;;;; 145E2;ANATOLIAN HIEROGLYPH A429;Lo;0;L;;;;;N;;;;; 145E3;ANATOLIAN HIEROGLYPH A430;Lo;0;L;;;;;N;;;;; 145E4;ANATOLIAN HIEROGLYPH A431;Lo;0;L;;;;;N;;;;; 145E5;ANATOLIAN HIEROGLYPH A432;Lo;0;L;;;;;N;;;;; 145E6;ANATOLIAN HIEROGLYPH A433;Lo;0;L;;;;;N;;;;; 145E7;ANATOLIAN HIEROGLYPH A434;Lo;0;L;;;;;N;;;;; 145E8;ANATOLIAN HIEROGLYPH A435;Lo;0;L;;;;;N;;;;; 145E9;ANATOLIAN HIEROGLYPH A436;Lo;0;L;;;;;N;;;;; 145EA;ANATOLIAN HIEROGLYPH A437;Lo;0;L;;;;;N;;;;; 145EB;ANATOLIAN HIEROGLYPH A438;Lo;0;L;;;;;N;;;;; 145EC;ANATOLIAN HIEROGLYPH A439;Lo;0;L;;;;;N;;;;; 145ED;ANATOLIAN HIEROGLYPH A440;Lo;0;L;;;;;N;;;;; 145EE;ANATOLIAN HIEROGLYPH A441;Lo;0;L;;;;;N;;;;; 145EF;ANATOLIAN HIEROGLYPH A442;Lo;0;L;;;;;N;;;;; 145F0;ANATOLIAN HIEROGLYPH A443;Lo;0;L;;;;;N;;;;; 145F1;ANATOLIAN HIEROGLYPH A444;Lo;0;L;;;;;N;;;;; 145F2;ANATOLIAN HIEROGLYPH A445;Lo;0;L;;;;;N;;;;; 145F3;ANATOLIAN HIEROGLYPH A446;Lo;0;L;;;;;N;;;;; 145F4;ANATOLIAN HIEROGLYPH A447;Lo;0;L;;;;;N;;;;; 145F5;ANATOLIAN HIEROGLYPH A448;Lo;0;L;;;;;N;;;;; 145F6;ANATOLIAN HIEROGLYPH A449;Lo;0;L;;;;;N;;;;; 145F7;ANATOLIAN HIEROGLYPH A450;Lo;0;L;;;;;N;;;;; 145F8;ANATOLIAN HIEROGLYPH A450A;Lo;0;L;;;;;N;;;;; 145F9;ANATOLIAN HIEROGLYPH A451;Lo;0;L;;;;;N;;;;; 145FA;ANATOLIAN HIEROGLYPH A452;Lo;0;L;;;;;N;;;;; 145FB;ANATOLIAN HIEROGLYPH A453;Lo;0;L;;;;;N;;;;; 145FC;ANATOLIAN HIEROGLYPH A454;Lo;0;L;;;;;N;;;;; 145FD;ANATOLIAN HIEROGLYPH A455;Lo;0;L;;;;;N;;;;; 145FE;ANATOLIAN HIEROGLYPH A456;Lo;0;L;;;;;N;;;;; 145FF;ANATOLIAN HIEROGLYPH A457;Lo;0;L;;;;;N;;;;; 14600;ANATOLIAN HIEROGLYPH A457A;Lo;0;L;;;;;N;;;;; 14601;ANATOLIAN HIEROGLYPH A458;Lo;0;L;;;;;N;;;;; 14602;ANATOLIAN HIEROGLYPH A459;Lo;0;L;;;;;N;;;;; 14603;ANATOLIAN HIEROGLYPH A460;Lo;0;L;;;;;N;;;;; 14604;ANATOLIAN HIEROGLYPH A461;Lo;0;L;;;;;N;;;;; 14605;ANATOLIAN HIEROGLYPH A462;Lo;0;L;;;;;N;;;;; 14606;ANATOLIAN HIEROGLYPH A463;Lo;0;L;;;;;N;;;;; 14607;ANATOLIAN HIEROGLYPH A464;Lo;0;L;;;;;N;;;;; 14608;ANATOLIAN HIEROGLYPH A465;Lo;0;L;;;;;N;;;;; 14609;ANATOLIAN HIEROGLYPH A466;Lo;0;L;;;;;N;;;;; 1460A;ANATOLIAN HIEROGLYPH A467;Lo;0;L;;;;;N;;;;; 1460B;ANATOLIAN HIEROGLYPH A468;Lo;0;L;;;;;N;;;;; 1460C;ANATOLIAN HIEROGLYPH A469;Lo;0;L;;;;;N;;;;; 1460D;ANATOLIAN HIEROGLYPH A470;Lo;0;L;;;;;N;;;;; 1460E;ANATOLIAN HIEROGLYPH A471;Lo;0;L;;;;;N;;;;; 1460F;ANATOLIAN HIEROGLYPH A472;Lo;0;L;;;;;N;;;;; 14610;ANATOLIAN HIEROGLYPH A473;Lo;0;L;;;;;N;;;;; 14611;ANATOLIAN HIEROGLYPH A474;Lo;0;L;;;;;N;;;;; 14612;ANATOLIAN HIEROGLYPH A475;Lo;0;L;;;;;N;;;;; 14613;ANATOLIAN HIEROGLYPH A476;Lo;0;L;;;;;N;;;;; 14614;ANATOLIAN HIEROGLYPH A477;Lo;0;L;;;;;N;;;;; 14615;ANATOLIAN HIEROGLYPH A478;Lo;0;L;;;;;N;;;;; 14616;ANATOLIAN HIEROGLYPH A479;Lo;0;L;;;;;N;;;;; 14617;ANATOLIAN HIEROGLYPH A480;Lo;0;L;;;;;N;;;;; 14618;ANATOLIAN HIEROGLYPH A481;Lo;0;L;;;;;N;;;;; 14619;ANATOLIAN HIEROGLYPH A482;Lo;0;L;;;;;N;;;;; 1461A;ANATOLIAN HIEROGLYPH A483;Lo;0;L;;;;;N;;;;; 1461B;ANATOLIAN HIEROGLYPH A484;Lo;0;L;;;;;N;;;;; 1461C;ANATOLIAN HIEROGLYPH A485;Lo;0;L;;;;;N;;;;; 1461D;ANATOLIAN HIEROGLYPH A486;Lo;0;L;;;;;N;;;;; 1461E;ANATOLIAN HIEROGLYPH A487;Lo;0;L;;;;;N;;;;; 1461F;ANATOLIAN HIEROGLYPH A488;Lo;0;L;;;;;N;;;;; 14620;ANATOLIAN HIEROGLYPH A489;Lo;0;L;;;;;N;;;;; 14621;ANATOLIAN HIEROGLYPH A490;Lo;0;L;;;;;N;;;;; 14622;ANATOLIAN HIEROGLYPH A491;Lo;0;L;;;;;N;;;;; 14623;ANATOLIAN HIEROGLYPH A492;Lo;0;L;;;;;N;;;;; 14624;ANATOLIAN HIEROGLYPH A493;Lo;0;L;;;;;N;;;;; 14625;ANATOLIAN HIEROGLYPH A494;Lo;0;L;;;;;N;;;;; 14626;ANATOLIAN HIEROGLYPH A495;Lo;0;L;;;;;N;;;;; 14627;ANATOLIAN HIEROGLYPH A496;Lo;0;L;;;;;N;;;;; 14628;ANATOLIAN HIEROGLYPH A497;Lo;0;L;;;;;N;;;;; 14629;ANATOLIAN HIEROGLYPH A501;Lo;0;L;;;;;N;;;;; 1462A;ANATOLIAN HIEROGLYPH A502;Lo;0;L;;;;;N;;;;; 1462B;ANATOLIAN HIEROGLYPH A503;Lo;0;L;;;;;N;;;;; 1462C;ANATOLIAN HIEROGLYPH A504;Lo;0;L;;;;;N;;;;; 1462D;ANATOLIAN HIEROGLYPH A505;Lo;0;L;;;;;N;;;;; 1462E;ANATOLIAN HIEROGLYPH A506;Lo;0;L;;;;;N;;;;; 1462F;ANATOLIAN HIEROGLYPH A507;Lo;0;L;;;;;N;;;;; 14630;ANATOLIAN HIEROGLYPH A508;Lo;0;L;;;;;N;;;;; 14631;ANATOLIAN HIEROGLYPH A509;Lo;0;L;;;;;N;;;;; 14632;ANATOLIAN HIEROGLYPH A510;Lo;0;L;;;;;N;;;;; 14633;ANATOLIAN HIEROGLYPH A511;Lo;0;L;;;;;N;;;;; 14634;ANATOLIAN HIEROGLYPH A512;Lo;0;L;;;;;N;;;;; 14635;ANATOLIAN HIEROGLYPH A513;Lo;0;L;;;;;N;;;;; 14636;ANATOLIAN HIEROGLYPH A514;Lo;0;L;;;;;N;;;;; 14637;ANATOLIAN HIEROGLYPH A515;Lo;0;L;;;;;N;;;;; 14638;ANATOLIAN HIEROGLYPH A516;Lo;0;L;;;;;N;;;;; 14639;ANATOLIAN HIEROGLYPH A517;Lo;0;L;;;;;N;;;;; 1463A;ANATOLIAN HIEROGLYPH A518;Lo;0;L;;;;;N;;;;; 1463B;ANATOLIAN HIEROGLYPH A519;Lo;0;L;;;;;N;;;;; 1463C;ANATOLIAN HIEROGLYPH A520;Lo;0;L;;;;;N;;;;; 1463D;ANATOLIAN HIEROGLYPH A521;Lo;0;L;;;;;N;;;;; 1463E;ANATOLIAN HIEROGLYPH A522;Lo;0;L;;;;;N;;;;; 1463F;ANATOLIAN HIEROGLYPH A523;Lo;0;L;;;;;N;;;;; 14640;ANATOLIAN HIEROGLYPH A524;Lo;0;L;;;;;N;;;;; 14641;ANATOLIAN HIEROGLYPH A525;Lo;0;L;;;;;N;;;;; 14642;ANATOLIAN HIEROGLYPH A526;Lo;0;L;;;;;N;;;;; 14643;ANATOLIAN HIEROGLYPH A527;Lo;0;L;;;;;N;;;;; 14644;ANATOLIAN HIEROGLYPH A528;Lo;0;L;;;;;N;;;;; 14645;ANATOLIAN HIEROGLYPH A529;Lo;0;L;;;;;N;;;;; 14646;ANATOLIAN HIEROGLYPH A530;Lo;0;L;;;;;N;;;;; 16800;BAMUM LETTER PHASE-A NGKUE MFON;Lo;0;L;;;;;N;;;;; 16801;BAMUM LETTER PHASE-A GBIEE FON;Lo;0;L;;;;;N;;;;; 16802;BAMUM LETTER PHASE-A PON MFON PIPAEMGBIEE;Lo;0;L;;;;;N;;;;; 16803;BAMUM LETTER PHASE-A PON MFON PIPAEMBA;Lo;0;L;;;;;N;;;;; 16804;BAMUM LETTER PHASE-A NAA MFON;Lo;0;L;;;;;N;;;;; 16805;BAMUM LETTER PHASE-A SHUENSHUET;Lo;0;L;;;;;N;;;;; 16806;BAMUM LETTER PHASE-A TITA MFON;Lo;0;L;;;;;N;;;;; 16807;BAMUM LETTER PHASE-A NZA MFON;Lo;0;L;;;;;N;;;;; 16808;BAMUM LETTER PHASE-A SHINDA PA NJI;Lo;0;L;;;;;N;;;;; 16809;BAMUM LETTER PHASE-A PON PA NJI PIPAEMGBIEE;Lo;0;L;;;;;N;;;;; 1680A;BAMUM LETTER PHASE-A PON PA NJI PIPAEMBA;Lo;0;L;;;;;N;;;;; 1680B;BAMUM LETTER PHASE-A MAEMBGBIEE;Lo;0;L;;;;;N;;;;; 1680C;BAMUM LETTER PHASE-A TU MAEMBA;Lo;0;L;;;;;N;;;;; 1680D;BAMUM LETTER PHASE-A NGANGU;Lo;0;L;;;;;N;;;;; 1680E;BAMUM LETTER PHASE-A MAEMVEUX;Lo;0;L;;;;;N;;;;; 1680F;BAMUM LETTER PHASE-A MANSUAE;Lo;0;L;;;;;N;;;;; 16810;BAMUM LETTER PHASE-A MVEUAENGAM;Lo;0;L;;;;;N;;;;; 16811;BAMUM LETTER PHASE-A SEUNYAM;Lo;0;L;;;;;N;;;;; 16812;BAMUM LETTER PHASE-A NTOQPEN;Lo;0;L;;;;;N;;;;; 16813;BAMUM LETTER PHASE-A KEUKEUTNDA;Lo;0;L;;;;;N;;;;; 16814;BAMUM LETTER PHASE-A NKINDI;Lo;0;L;;;;;N;;;;; 16815;BAMUM LETTER PHASE-A SUU;Lo;0;L;;;;;N;;;;; 16816;BAMUM LETTER PHASE-A NGKUENZEUM;Lo;0;L;;;;;N;;;;; 16817;BAMUM LETTER PHASE-A LAPAQ;Lo;0;L;;;;;N;;;;; 16818;BAMUM LETTER PHASE-A LET KUT;Lo;0;L;;;;;N;;;;; 16819;BAMUM LETTER PHASE-A NTAP MFAA;Lo;0;L;;;;;N;;;;; 1681A;BAMUM LETTER PHASE-A MAEKEUP;Lo;0;L;;;;;N;;;;; 1681B;BAMUM LETTER PHASE-A PASHAE;Lo;0;L;;;;;N;;;;; 1681C;BAMUM LETTER PHASE-A GHEUAERAE;Lo;0;L;;;;;N;;;;; 1681D;BAMUM LETTER PHASE-A PAMSHAE;Lo;0;L;;;;;N;;;;; 1681E;BAMUM LETTER PHASE-A MON NGGEUAET;Lo;0;L;;;;;N;;;;; 1681F;BAMUM LETTER PHASE-A NZUN MEUT;Lo;0;L;;;;;N;;;;; 16820;BAMUM LETTER PHASE-A U YUQ NAE;Lo;0;L;;;;;N;;;;; 16821;BAMUM LETTER PHASE-A GHEUAEGHEUAE;Lo;0;L;;;;;N;;;;; 16822;BAMUM LETTER PHASE-A NTAP NTAA;Lo;0;L;;;;;N;;;;; 16823;BAMUM LETTER PHASE-A SISA;Lo;0;L;;;;;N;;;;; 16824;BAMUM LETTER PHASE-A MGBASA;Lo;0;L;;;;;N;;;;; 16825;BAMUM LETTER PHASE-A MEUNJOMNDEUQ;Lo;0;L;;;;;N;;;;; 16826;BAMUM LETTER PHASE-A MOOMPUQ;Lo;0;L;;;;;N;;;;; 16827;BAMUM LETTER PHASE-A KAFA;Lo;0;L;;;;;N;;;;; 16828;BAMUM LETTER PHASE-A PA LEERAEWA;Lo;0;L;;;;;N;;;;; 16829;BAMUM LETTER PHASE-A NDA LEERAEWA;Lo;0;L;;;;;N;;;;; 1682A;BAMUM LETTER PHASE-A PET;Lo;0;L;;;;;N;;;;; 1682B;BAMUM LETTER PHASE-A MAEMKPEN;Lo;0;L;;;;;N;;;;; 1682C;BAMUM LETTER PHASE-A NIKA;Lo;0;L;;;;;N;;;;; 1682D;BAMUM LETTER PHASE-A PUP;Lo;0;L;;;;;N;;;;; 1682E;BAMUM LETTER PHASE-A TUAEP;Lo;0;L;;;;;N;;;;; 1682F;BAMUM LETTER PHASE-A LUAEP;Lo;0;L;;;;;N;;;;; 16830;BAMUM LETTER PHASE-A SONJAM;Lo;0;L;;;;;N;;;;; 16831;BAMUM LETTER PHASE-A TEUTEUWEN;Lo;0;L;;;;;N;;;;; 16832;BAMUM LETTER PHASE-A MAENYI;Lo;0;L;;;;;N;;;;; 16833;BAMUM LETTER PHASE-A KET;Lo;0;L;;;;;N;;;;; 16834;BAMUM LETTER PHASE-A NDAANGGEUAET;Lo;0;L;;;;;N;;;;; 16835;BAMUM LETTER PHASE-A KUOQ;Lo;0;L;;;;;N;;;;; 16836;BAMUM LETTER PHASE-A MOOMEUT;Lo;0;L;;;;;N;;;;; 16837;BAMUM LETTER PHASE-A SHUM;Lo;0;L;;;;;N;;;;; 16838;BAMUM LETTER PHASE-A LOMMAE;Lo;0;L;;;;;N;;;;; 16839;BAMUM LETTER PHASE-A FIRI;Lo;0;L;;;;;N;;;;; 1683A;BAMUM LETTER PHASE-A ROM;Lo;0;L;;;;;N;;;;; 1683B;BAMUM LETTER PHASE-A KPOQ;Lo;0;L;;;;;N;;;;; 1683C;BAMUM LETTER PHASE-A SOQ;Lo;0;L;;;;;N;;;;; 1683D;BAMUM LETTER PHASE-A MAP PIEET;Lo;0;L;;;;;N;;;;; 1683E;BAMUM LETTER PHASE-A SHIRAE;Lo;0;L;;;;;N;;;;; 1683F;BAMUM LETTER PHASE-A NTAP;Lo;0;L;;;;;N;;;;; 16840;BAMUM LETTER PHASE-A SHOQ NSHUT YUM;Lo;0;L;;;;;N;;;;; 16841;BAMUM LETTER PHASE-A NYIT MONGKEUAEQ;Lo;0;L;;;;;N;;;;; 16842;BAMUM LETTER PHASE-A PAARAE;Lo;0;L;;;;;N;;;;; 16843;BAMUM LETTER PHASE-A NKAARAE;Lo;0;L;;;;;N;;;;; 16844;BAMUM LETTER PHASE-A UNKNOWN;Lo;0;L;;;;;N;;;;; 16845;BAMUM LETTER PHASE-A NGGEN;Lo;0;L;;;;;N;;;;; 16846;BAMUM LETTER PHASE-A MAESI;Lo;0;L;;;;;N;;;;; 16847;BAMUM LETTER PHASE-A NJAM;Lo;0;L;;;;;N;;;;; 16848;BAMUM LETTER PHASE-A MBANYI;Lo;0;L;;;;;N;;;;; 16849;BAMUM LETTER PHASE-A NYET;Lo;0;L;;;;;N;;;;; 1684A;BAMUM LETTER PHASE-A TEUAEN;Lo;0;L;;;;;N;;;;; 1684B;BAMUM LETTER PHASE-A SOT;Lo;0;L;;;;;N;;;;; 1684C;BAMUM LETTER PHASE-A PAAM;Lo;0;L;;;;;N;;;;; 1684D;BAMUM LETTER PHASE-A NSHIEE;Lo;0;L;;;;;N;;;;; 1684E;BAMUM LETTER PHASE-A MAEM;Lo;0;L;;;;;N;;;;; 1684F;BAMUM LETTER PHASE-A NYI;Lo;0;L;;;;;N;;;;; 16850;BAMUM LETTER PHASE-A KAQ;Lo;0;L;;;;;N;;;;; 16851;BAMUM LETTER PHASE-A NSHA;Lo;0;L;;;;;N;;;;; 16852;BAMUM LETTER PHASE-A VEE;Lo;0;L;;;;;N;;;;; 16853;BAMUM LETTER PHASE-A LU;Lo;0;L;;;;;N;;;;; 16854;BAMUM LETTER PHASE-A NEN;Lo;0;L;;;;;N;;;;; 16855;BAMUM LETTER PHASE-A NAQ;Lo;0;L;;;;;N;;;;; 16856;BAMUM LETTER PHASE-A MBAQ;Lo;0;L;;;;;N;;;;; 16857;BAMUM LETTER PHASE-B NSHUET;Lo;0;L;;;;;N;;;;; 16858;BAMUM LETTER PHASE-B TU MAEMGBIEE;Lo;0;L;;;;;N;;;;; 16859;BAMUM LETTER PHASE-B SIEE;Lo;0;L;;;;;N;;;;; 1685A;BAMUM LETTER PHASE-B SET TU;Lo;0;L;;;;;N;;;;; 1685B;BAMUM LETTER PHASE-B LOM NTEUM;Lo;0;L;;;;;N;;;;; 1685C;BAMUM LETTER PHASE-B MBA MAELEE;Lo;0;L;;;;;N;;;;; 1685D;BAMUM LETTER PHASE-B KIEEM;Lo;0;L;;;;;N;;;;; 1685E;BAMUM LETTER PHASE-B YEURAE;Lo;0;L;;;;;N;;;;; 1685F;BAMUM LETTER PHASE-B MBAARAE;Lo;0;L;;;;;N;;;;; 16860;BAMUM LETTER PHASE-B KAM;Lo;0;L;;;;;N;;;;; 16861;BAMUM LETTER PHASE-B PEESHI;Lo;0;L;;;;;N;;;;; 16862;BAMUM LETTER PHASE-B YAFU LEERAEWA;Lo;0;L;;;;;N;;;;; 16863;BAMUM LETTER PHASE-B LAM NSHUT NYAM;Lo;0;L;;;;;N;;;;; 16864;BAMUM LETTER PHASE-B NTIEE SHEUOQ;Lo;0;L;;;;;N;;;;; 16865;BAMUM LETTER PHASE-B NDU NJAA;Lo;0;L;;;;;N;;;;; 16866;BAMUM LETTER PHASE-B GHEUGHEUAEM;Lo;0;L;;;;;N;;;;; 16867;BAMUM LETTER PHASE-B PIT;Lo;0;L;;;;;N;;;;; 16868;BAMUM LETTER PHASE-B TU NSIEE;Lo;0;L;;;;;N;;;;; 16869;BAMUM LETTER PHASE-B SHET NJAQ;Lo;0;L;;;;;N;;;;; 1686A;BAMUM LETTER PHASE-B SHEUAEQTU;Lo;0;L;;;;;N;;;;; 1686B;BAMUM LETTER PHASE-B MFON TEUAEQ;Lo;0;L;;;;;N;;;;; 1686C;BAMUM LETTER PHASE-B MBIT MBAAKET;Lo;0;L;;;;;N;;;;; 1686D;BAMUM LETTER PHASE-B NYI NTEUM;Lo;0;L;;;;;N;;;;; 1686E;BAMUM LETTER PHASE-B KEUPUQ;Lo;0;L;;;;;N;;;;; 1686F;BAMUM LETTER PHASE-B GHEUGHEN;Lo;0;L;;;;;N;;;;; 16870;BAMUM LETTER PHASE-B KEUYEUX;Lo;0;L;;;;;N;;;;; 16871;BAMUM LETTER PHASE-B LAANAE;Lo;0;L;;;;;N;;;;; 16872;BAMUM LETTER PHASE-B PARUM;Lo;0;L;;;;;N;;;;; 16873;BAMUM LETTER PHASE-B VEUM;Lo;0;L;;;;;N;;;;; 16874;BAMUM LETTER PHASE-B NGKINDI MVOP;Lo;0;L;;;;;N;;;;; 16875;BAMUM LETTER PHASE-B NGGEU MBU;Lo;0;L;;;;;N;;;;; 16876;BAMUM LETTER PHASE-B WUAET;Lo;0;L;;;;;N;;;;; 16877;BAMUM LETTER PHASE-B SAKEUAE;Lo;0;L;;;;;N;;;;; 16878;BAMUM LETTER PHASE-B TAAM;Lo;0;L;;;;;N;;;;; 16879;BAMUM LETTER PHASE-B MEUQ;Lo;0;L;;;;;N;;;;; 1687A;BAMUM LETTER PHASE-B NGGUOQ;Lo;0;L;;;;;N;;;;; 1687B;BAMUM LETTER PHASE-B NGGUOQ LARGE;Lo;0;L;;;;;N;;;;; 1687C;BAMUM LETTER PHASE-B MFIYAQ;Lo;0;L;;;;;N;;;;; 1687D;BAMUM LETTER PHASE-B SUE;Lo;0;L;;;;;N;;;;; 1687E;BAMUM LETTER PHASE-B MBEURI;Lo;0;L;;;;;N;;;;; 1687F;BAMUM LETTER PHASE-B MONTIEEN;Lo;0;L;;;;;N;;;;; 16880;BAMUM LETTER PHASE-B NYAEMAE;Lo;0;L;;;;;N;;;;; 16881;BAMUM LETTER PHASE-B PUNGAAM;Lo;0;L;;;;;N;;;;; 16882;BAMUM LETTER PHASE-B MEUT NGGEET;Lo;0;L;;;;;N;;;;; 16883;BAMUM LETTER PHASE-B FEUX;Lo;0;L;;;;;N;;;;; 16884;BAMUM LETTER PHASE-B MBUOQ;Lo;0;L;;;;;N;;;;; 16885;BAMUM LETTER PHASE-B FEE;Lo;0;L;;;;;N;;;;; 16886;BAMUM LETTER PHASE-B KEUAEM;Lo;0;L;;;;;N;;;;; 16887;BAMUM LETTER PHASE-B MA NJEUAENA;Lo;0;L;;;;;N;;;;; 16888;BAMUM LETTER PHASE-B MA NJUQA;Lo;0;L;;;;;N;;;;; 16889;BAMUM LETTER PHASE-B LET;Lo;0;L;;;;;N;;;;; 1688A;BAMUM LETTER PHASE-B NGGAAM;Lo;0;L;;;;;N;;;;; 1688B;BAMUM LETTER PHASE-B NSEN;Lo;0;L;;;;;N;;;;; 1688C;BAMUM LETTER PHASE-B MA;Lo;0;L;;;;;N;;;;; 1688D;BAMUM LETTER PHASE-B KIQ;Lo;0;L;;;;;N;;;;; 1688E;BAMUM LETTER PHASE-B NGOM;Lo;0;L;;;;;N;;;;; 1688F;BAMUM LETTER PHASE-C NGKUE MAEMBA;Lo;0;L;;;;;N;;;;; 16890;BAMUM LETTER PHASE-C NZA;Lo;0;L;;;;;N;;;;; 16891;BAMUM LETTER PHASE-C YUM;Lo;0;L;;;;;N;;;;; 16892;BAMUM LETTER PHASE-C WANGKUOQ;Lo;0;L;;;;;N;;;;; 16893;BAMUM LETTER PHASE-C NGGEN;Lo;0;L;;;;;N;;;;; 16894;BAMUM LETTER PHASE-C NDEUAEREE;Lo;0;L;;;;;N;;;;; 16895;BAMUM LETTER PHASE-C NGKAQ;Lo;0;L;;;;;N;;;;; 16896;BAMUM LETTER PHASE-C GHARAE;Lo;0;L;;;;;N;;;;; 16897;BAMUM LETTER PHASE-C MBEEKEET;Lo;0;L;;;;;N;;;;; 16898;BAMUM LETTER PHASE-C GBAYI;Lo;0;L;;;;;N;;;;; 16899;BAMUM LETTER PHASE-C NYIR MKPARAQ MEUN;Lo;0;L;;;;;N;;;;; 1689A;BAMUM LETTER PHASE-C NTU MBIT;Lo;0;L;;;;;N;;;;; 1689B;BAMUM LETTER PHASE-C MBEUM;Lo;0;L;;;;;N;;;;; 1689C;BAMUM LETTER PHASE-C PIRIEEN;Lo;0;L;;;;;N;;;;; 1689D;BAMUM LETTER PHASE-C NDOMBU;Lo;0;L;;;;;N;;;;; 1689E;BAMUM LETTER PHASE-C MBAA CABBAGE-TREE;Lo;0;L;;;;;N;;;;; 1689F;BAMUM LETTER PHASE-C KEUSHEUAEP;Lo;0;L;;;;;N;;;;; 168A0;BAMUM LETTER PHASE-C GHAP;Lo;0;L;;;;;N;;;;; 168A1;BAMUM LETTER PHASE-C KEUKAQ;Lo;0;L;;;;;N;;;;; 168A2;BAMUM LETTER PHASE-C YU MUOMAE;Lo;0;L;;;;;N;;;;; 168A3;BAMUM LETTER PHASE-C NZEUM;Lo;0;L;;;;;N;;;;; 168A4;BAMUM LETTER PHASE-C MBUE;Lo;0;L;;;;;N;;;;; 168A5;BAMUM LETTER PHASE-C NSEUAEN;Lo;0;L;;;;;N;;;;; 168A6;BAMUM LETTER PHASE-C MBIT;Lo;0;L;;;;;N;;;;; 168A7;BAMUM LETTER PHASE-C YEUQ;Lo;0;L;;;;;N;;;;; 168A8;BAMUM LETTER PHASE-C KPARAQ;Lo;0;L;;;;;N;;;;; 168A9;BAMUM LETTER PHASE-C KAA;Lo;0;L;;;;;N;;;;; 168AA;BAMUM LETTER PHASE-C SEUX;Lo;0;L;;;;;N;;;;; 168AB;BAMUM LETTER PHASE-C NDIDA;Lo;0;L;;;;;N;;;;; 168AC;BAMUM LETTER PHASE-C TAASHAE;Lo;0;L;;;;;N;;;;; 168AD;BAMUM LETTER PHASE-C NJUEQ;Lo;0;L;;;;;N;;;;; 168AE;BAMUM LETTER PHASE-C TITA YUE;Lo;0;L;;;;;N;;;;; 168AF;BAMUM LETTER PHASE-C SUAET;Lo;0;L;;;;;N;;;;; 168B0;BAMUM LETTER PHASE-C NGGUAEN NYAM;Lo;0;L;;;;;N;;;;; 168B1;BAMUM LETTER PHASE-C VEUX;Lo;0;L;;;;;N;;;;; 168B2;BAMUM LETTER PHASE-C NANSANAQ;Lo;0;L;;;;;N;;;;; 168B3;BAMUM LETTER PHASE-C MA KEUAERI;Lo;0;L;;;;;N;;;;; 168B4;BAMUM LETTER PHASE-C NTAA;Lo;0;L;;;;;N;;;;; 168B5;BAMUM LETTER PHASE-C NGGUON;Lo;0;L;;;;;N;;;;; 168B6;BAMUM LETTER PHASE-C LAP;Lo;0;L;;;;;N;;;;; 168B7;BAMUM LETTER PHASE-C MBIRIEEN;Lo;0;L;;;;;N;;;;; 168B8;BAMUM LETTER PHASE-C MGBASAQ;Lo;0;L;;;;;N;;;;; 168B9;BAMUM LETTER PHASE-C NTEUNGBA;Lo;0;L;;;;;N;;;;; 168BA;BAMUM LETTER PHASE-C TEUTEUX;Lo;0;L;;;;;N;;;;; 168BB;BAMUM LETTER PHASE-C NGGUM;Lo;0;L;;;;;N;;;;; 168BC;BAMUM LETTER PHASE-C FUE;Lo;0;L;;;;;N;;;;; 168BD;BAMUM LETTER PHASE-C NDEUT;Lo;0;L;;;;;N;;;;; 168BE;BAMUM LETTER PHASE-C NSA;Lo;0;L;;;;;N;;;;; 168BF;BAMUM LETTER PHASE-C NSHAQ;Lo;0;L;;;;;N;;;;; 168C0;BAMUM LETTER PHASE-C BUNG;Lo;0;L;;;;;N;;;;; 168C1;BAMUM LETTER PHASE-C VEUAEPEN;Lo;0;L;;;;;N;;;;; 168C2;BAMUM LETTER PHASE-C MBERAE;Lo;0;L;;;;;N;;;;; 168C3;BAMUM LETTER PHASE-C RU;Lo;0;L;;;;;N;;;;; 168C4;BAMUM LETTER PHASE-C NJAEM;Lo;0;L;;;;;N;;;;; 168C5;BAMUM LETTER PHASE-C LAM;Lo;0;L;;;;;N;;;;; 168C6;BAMUM LETTER PHASE-C TITUAEP;Lo;0;L;;;;;N;;;;; 168C7;BAMUM LETTER PHASE-C NSUOT NGOM;Lo;0;L;;;;;N;;;;; 168C8;BAMUM LETTER PHASE-C NJEEEE;Lo;0;L;;;;;N;;;;; 168C9;BAMUM LETTER PHASE-C KET;Lo;0;L;;;;;N;;;;; 168CA;BAMUM LETTER PHASE-C NGGU;Lo;0;L;;;;;N;;;;; 168CB;BAMUM LETTER PHASE-C MAESI;Lo;0;L;;;;;N;;;;; 168CC;BAMUM LETTER PHASE-C MBUAEM;Lo;0;L;;;;;N;;;;; 168CD;BAMUM LETTER PHASE-C LU;Lo;0;L;;;;;N;;;;; 168CE;BAMUM LETTER PHASE-C KUT;Lo;0;L;;;;;N;;;;; 168CF;BAMUM LETTER PHASE-C NJAM;Lo;0;L;;;;;N;;;;; 168D0;BAMUM LETTER PHASE-C NGOM;Lo;0;L;;;;;N;;;;; 168D1;BAMUM LETTER PHASE-C WUP;Lo;0;L;;;;;N;;;;; 168D2;BAMUM LETTER PHASE-C NGGUEET;Lo;0;L;;;;;N;;;;; 168D3;BAMUM LETTER PHASE-C NSOM;Lo;0;L;;;;;N;;;;; 168D4;BAMUM LETTER PHASE-C NTEN;Lo;0;L;;;;;N;;;;; 168D5;BAMUM LETTER PHASE-C KUOP NKAARAE;Lo;0;L;;;;;N;;;;; 168D6;BAMUM LETTER PHASE-C NSUN;Lo;0;L;;;;;N;;;;; 168D7;BAMUM LETTER PHASE-C NDAM;Lo;0;L;;;;;N;;;;; 168D8;BAMUM LETTER PHASE-C MA NSIEE;Lo;0;L;;;;;N;;;;; 168D9;BAMUM LETTER PHASE-C YAA;Lo;0;L;;;;;N;;;;; 168DA;BAMUM LETTER PHASE-C NDAP;Lo;0;L;;;;;N;;;;; 168DB;BAMUM LETTER PHASE-C SHUEQ;Lo;0;L;;;;;N;;;;; 168DC;BAMUM LETTER PHASE-C SETFON;Lo;0;L;;;;;N;;;;; 168DD;BAMUM LETTER PHASE-C MBI;Lo;0;L;;;;;N;;;;; 168DE;BAMUM LETTER PHASE-C MAEMBA;Lo;0;L;;;;;N;;;;; 168DF;BAMUM LETTER PHASE-C MBANYI;Lo;0;L;;;;;N;;;;; 168E0;BAMUM LETTER PHASE-C KEUSEUX;Lo;0;L;;;;;N;;;;; 168E1;BAMUM LETTER PHASE-C MBEUX;Lo;0;L;;;;;N;;;;; 168E2;BAMUM LETTER PHASE-C KEUM;Lo;0;L;;;;;N;;;;; 168E3;BAMUM LETTER PHASE-C MBAA PICKET;Lo;0;L;;;;;N;;;;; 168E4;BAMUM LETTER PHASE-C YUWOQ;Lo;0;L;;;;;N;;;;; 168E5;BAMUM LETTER PHASE-C NJEUX;Lo;0;L;;;;;N;;;;; 168E6;BAMUM LETTER PHASE-C MIEE;Lo;0;L;;;;;N;;;;; 168E7;BAMUM LETTER PHASE-C MUAE;Lo;0;L;;;;;N;;;;; 168E8;BAMUM LETTER PHASE-C SHIQ;Lo;0;L;;;;;N;;;;; 168E9;BAMUM LETTER PHASE-C KEN LAW;Lo;0;L;;;;;N;;;;; 168EA;BAMUM LETTER PHASE-C KEN FATIGUE;Lo;0;L;;;;;N;;;;; 168EB;BAMUM LETTER PHASE-C NGAQ;Lo;0;L;;;;;N;;;;; 168EC;BAMUM LETTER PHASE-C NAQ;Lo;0;L;;;;;N;;;;; 168ED;BAMUM LETTER PHASE-C LIQ;Lo;0;L;;;;;N;;;;; 168EE;BAMUM LETTER PHASE-C PIN;Lo;0;L;;;;;N;;;;; 168EF;BAMUM LETTER PHASE-C PEN;Lo;0;L;;;;;N;;;;; 168F0;BAMUM LETTER PHASE-C TET;Lo;0;L;;;;;N;;;;; 168F1;BAMUM LETTER PHASE-D MBUO;Lo;0;L;;;;;N;;;;; 168F2;BAMUM LETTER PHASE-D WAP;Lo;0;L;;;;;N;;;;; 168F3;BAMUM LETTER PHASE-D NJI;Lo;0;L;;;;;N;;;;; 168F4;BAMUM LETTER PHASE-D MFON;Lo;0;L;;;;;N;;;;; 168F5;BAMUM LETTER PHASE-D NJIEE;Lo;0;L;;;;;N;;;;; 168F6;BAMUM LETTER PHASE-D LIEE;Lo;0;L;;;;;N;;;;; 168F7;BAMUM LETTER PHASE-D NJEUT;Lo;0;L;;;;;N;;;;; 168F8;BAMUM LETTER PHASE-D NSHEE;Lo;0;L;;;;;N;;;;; 168F9;BAMUM LETTER PHASE-D NGGAAMAE;Lo;0;L;;;;;N;;;;; 168FA;BAMUM LETTER PHASE-D NYAM;Lo;0;L;;;;;N;;;;; 168FB;BAMUM LETTER PHASE-D WUAEN;Lo;0;L;;;;;N;;;;; 168FC;BAMUM LETTER PHASE-D NGKUN;Lo;0;L;;;;;N;;;;; 168FD;BAMUM LETTER PHASE-D SHEE;Lo;0;L;;;;;N;;;;; 168FE;BAMUM LETTER PHASE-D NGKAP;Lo;0;L;;;;;N;;;;; 168FF;BAMUM LETTER PHASE-D KEUAETMEUN;Lo;0;L;;;;;N;;;;; 16900;BAMUM LETTER PHASE-D TEUT;Lo;0;L;;;;;N;;;;; 16901;BAMUM LETTER PHASE-D SHEUAE;Lo;0;L;;;;;N;;;;; 16902;BAMUM LETTER PHASE-D NJAP;Lo;0;L;;;;;N;;;;; 16903;BAMUM LETTER PHASE-D SUE;Lo;0;L;;;;;N;;;;; 16904;BAMUM LETTER PHASE-D KET;Lo;0;L;;;;;N;;;;; 16905;BAMUM LETTER PHASE-D YAEMMAE;Lo;0;L;;;;;N;;;;; 16906;BAMUM LETTER PHASE-D KUOM;Lo;0;L;;;;;N;;;;; 16907;BAMUM LETTER PHASE-D SAP;Lo;0;L;;;;;N;;;;; 16908;BAMUM LETTER PHASE-D MFEUT;Lo;0;L;;;;;N;;;;; 16909;BAMUM LETTER PHASE-D NDEUX;Lo;0;L;;;;;N;;;;; 1690A;BAMUM LETTER PHASE-D MALEERI;Lo;0;L;;;;;N;;;;; 1690B;BAMUM LETTER PHASE-D MEUT;Lo;0;L;;;;;N;;;;; 1690C;BAMUM LETTER PHASE-D SEUAEQ;Lo;0;L;;;;;N;;;;; 1690D;BAMUM LETTER PHASE-D YEN;Lo;0;L;;;;;N;;;;; 1690E;BAMUM LETTER PHASE-D NJEUAEM;Lo;0;L;;;;;N;;;;; 1690F;BAMUM LETTER PHASE-D KEUOT MBUAE;Lo;0;L;;;;;N;;;;; 16910;BAMUM LETTER PHASE-D NGKEURI;Lo;0;L;;;;;N;;;;; 16911;BAMUM LETTER PHASE-D TU;Lo;0;L;;;;;N;;;;; 16912;BAMUM LETTER PHASE-D GHAA;Lo;0;L;;;;;N;;;;; 16913;BAMUM LETTER PHASE-D NGKYEE;Lo;0;L;;;;;N;;;;; 16914;BAMUM LETTER PHASE-D FEUFEUAET;Lo;0;L;;;;;N;;;;; 16915;BAMUM LETTER PHASE-D NDEE;Lo;0;L;;;;;N;;;;; 16916;BAMUM LETTER PHASE-D MGBOFUM;Lo;0;L;;;;;N;;;;; 16917;BAMUM LETTER PHASE-D LEUAEP;Lo;0;L;;;;;N;;;;; 16918;BAMUM LETTER PHASE-D NDON;Lo;0;L;;;;;N;;;;; 16919;BAMUM LETTER PHASE-D MONI;Lo;0;L;;;;;N;;;;; 1691A;BAMUM LETTER PHASE-D MGBEUN;Lo;0;L;;;;;N;;;;; 1691B;BAMUM LETTER PHASE-D PUUT;Lo;0;L;;;;;N;;;;; 1691C;BAMUM LETTER PHASE-D MGBIEE;Lo;0;L;;;;;N;;;;; 1691D;BAMUM LETTER PHASE-D MFO;Lo;0;L;;;;;N;;;;; 1691E;BAMUM LETTER PHASE-D LUM;Lo;0;L;;;;;N;;;;; 1691F;BAMUM LETTER PHASE-D NSIEEP;Lo;0;L;;;;;N;;;;; 16920;BAMUM LETTER PHASE-D MBAA;Lo;0;L;;;;;N;;;;; 16921;BAMUM LETTER PHASE-D KWAET;Lo;0;L;;;;;N;;;;; 16922;BAMUM LETTER PHASE-D NYET;Lo;0;L;;;;;N;;;;; 16923;BAMUM LETTER PHASE-D TEUAEN;Lo;0;L;;;;;N;;;;; 16924;BAMUM LETTER PHASE-D SOT;Lo;0;L;;;;;N;;;;; 16925;BAMUM LETTER PHASE-D YUWOQ;Lo;0;L;;;;;N;;;;; 16926;BAMUM LETTER PHASE-D KEUM;Lo;0;L;;;;;N;;;;; 16927;BAMUM LETTER PHASE-D RAEM;Lo;0;L;;;;;N;;;;; 16928;BAMUM LETTER PHASE-D TEEEE;Lo;0;L;;;;;N;;;;; 16929;BAMUM LETTER PHASE-D NGKEUAEQ;Lo;0;L;;;;;N;;;;; 1692A;BAMUM LETTER PHASE-D MFEUAE;Lo;0;L;;;;;N;;;;; 1692B;BAMUM LETTER PHASE-D NSIEET;Lo;0;L;;;;;N;;;;; 1692C;BAMUM LETTER PHASE-D KEUP;Lo;0;L;;;;;N;;;;; 1692D;BAMUM LETTER PHASE-D PIP;Lo;0;L;;;;;N;;;;; 1692E;BAMUM LETTER PHASE-D PEUTAE;Lo;0;L;;;;;N;;;;; 1692F;BAMUM LETTER PHASE-D NYUE;Lo;0;L;;;;;N;;;;; 16930;BAMUM LETTER PHASE-D LET;Lo;0;L;;;;;N;;;;; 16931;BAMUM LETTER PHASE-D NGGAAM;Lo;0;L;;;;;N;;;;; 16932;BAMUM LETTER PHASE-D MFIEE;Lo;0;L;;;;;N;;;;; 16933;BAMUM LETTER PHASE-D NGGWAEN;Lo;0;L;;;;;N;;;;; 16934;BAMUM LETTER PHASE-D YUOM;Lo;0;L;;;;;N;;;;; 16935;BAMUM LETTER PHASE-D PAP;Lo;0;L;;;;;N;;;;; 16936;BAMUM LETTER PHASE-D YUOP;Lo;0;L;;;;;N;;;;; 16937;BAMUM LETTER PHASE-D NDAM;Lo;0;L;;;;;N;;;;; 16938;BAMUM LETTER PHASE-D NTEUM;Lo;0;L;;;;;N;;;;; 16939;BAMUM LETTER PHASE-D SUAE;Lo;0;L;;;;;N;;;;; 1693A;BAMUM LETTER PHASE-D KUN;Lo;0;L;;;;;N;;;;; 1693B;BAMUM LETTER PHASE-D NGGEUX;Lo;0;L;;;;;N;;;;; 1693C;BAMUM LETTER PHASE-D NGKIEE;Lo;0;L;;;;;N;;;;; 1693D;BAMUM LETTER PHASE-D TUOT;Lo;0;L;;;;;N;;;;; 1693E;BAMUM LETTER PHASE-D MEUN;Lo;0;L;;;;;N;;;;; 1693F;BAMUM LETTER PHASE-D KUQ;Lo;0;L;;;;;N;;;;; 16940;BAMUM LETTER PHASE-D NSUM;Lo;0;L;;;;;N;;;;; 16941;BAMUM LETTER PHASE-D TEUN;Lo;0;L;;;;;N;;;;; 16942;BAMUM LETTER PHASE-D MAENJET;Lo;0;L;;;;;N;;;;; 16943;BAMUM LETTER PHASE-D NGGAP;Lo;0;L;;;;;N;;;;; 16944;BAMUM LETTER PHASE-D LEUM;Lo;0;L;;;;;N;;;;; 16945;BAMUM LETTER PHASE-D NGGUOM;Lo;0;L;;;;;N;;;;; 16946;BAMUM LETTER PHASE-D NSHUT;Lo;0;L;;;;;N;;;;; 16947;BAMUM LETTER PHASE-D NJUEQ;Lo;0;L;;;;;N;;;;; 16948;BAMUM LETTER PHASE-D GHEUAE;Lo;0;L;;;;;N;;;;; 16949;BAMUM LETTER PHASE-D KU;Lo;0;L;;;;;N;;;;; 1694A;BAMUM LETTER PHASE-D REN OLD;Lo;0;L;;;;;N;;;;; 1694B;BAMUM LETTER PHASE-D TAE;Lo;0;L;;;;;N;;;;; 1694C;BAMUM LETTER PHASE-D TOQ;Lo;0;L;;;;;N;;;;; 1694D;BAMUM LETTER PHASE-D NYI;Lo;0;L;;;;;N;;;;; 1694E;BAMUM LETTER PHASE-D RII;Lo;0;L;;;;;N;;;;; 1694F;BAMUM LETTER PHASE-D LEEEE;Lo;0;L;;;;;N;;;;; 16950;BAMUM LETTER PHASE-D MEEEE;Lo;0;L;;;;;N;;;;; 16951;BAMUM LETTER PHASE-D M;Lo;0;L;;;;;N;;;;; 16952;BAMUM LETTER PHASE-D SUU;Lo;0;L;;;;;N;;;;; 16953;BAMUM LETTER PHASE-D MU;Lo;0;L;;;;;N;;;;; 16954;BAMUM LETTER PHASE-D SHII;Lo;0;L;;;;;N;;;;; 16955;BAMUM LETTER PHASE-D SHEUX;Lo;0;L;;;;;N;;;;; 16956;BAMUM LETTER PHASE-D KYEE;Lo;0;L;;;;;N;;;;; 16957;BAMUM LETTER PHASE-D NU;Lo;0;L;;;;;N;;;;; 16958;BAMUM LETTER PHASE-D SHU;Lo;0;L;;;;;N;;;;; 16959;BAMUM LETTER PHASE-D NTEE;Lo;0;L;;;;;N;;;;; 1695A;BAMUM LETTER PHASE-D PEE;Lo;0;L;;;;;N;;;;; 1695B;BAMUM LETTER PHASE-D NI;Lo;0;L;;;;;N;;;;; 1695C;BAMUM LETTER PHASE-D SHOQ;Lo;0;L;;;;;N;;;;; 1695D;BAMUM LETTER PHASE-D PUQ;Lo;0;L;;;;;N;;;;; 1695E;BAMUM LETTER PHASE-D MVOP;Lo;0;L;;;;;N;;;;; 1695F;BAMUM LETTER PHASE-D LOQ;Lo;0;L;;;;;N;;;;; 16960;BAMUM LETTER PHASE-D REN MUCH;Lo;0;L;;;;;N;;;;; 16961;BAMUM LETTER PHASE-D TI;Lo;0;L;;;;;N;;;;; 16962;BAMUM LETTER PHASE-D NTUU;Lo;0;L;;;;;N;;;;; 16963;BAMUM LETTER PHASE-D MBAA SEVEN;Lo;0;L;;;;;N;;;;; 16964;BAMUM LETTER PHASE-D SAQ;Lo;0;L;;;;;N;;;;; 16965;BAMUM LETTER PHASE-D FAA;Lo;0;L;;;;;N;;;;; 16966;BAMUM LETTER PHASE-E NDAP;Lo;0;L;;;;;N;;;;; 16967;BAMUM LETTER PHASE-E TOON;Lo;0;L;;;;;N;;;;; 16968;BAMUM LETTER PHASE-E MBEUM;Lo;0;L;;;;;N;;;;; 16969;BAMUM LETTER PHASE-E LAP;Lo;0;L;;;;;N;;;;; 1696A;BAMUM LETTER PHASE-E VOM;Lo;0;L;;;;;N;;;;; 1696B;BAMUM LETTER PHASE-E LOON;Lo;0;L;;;;;N;;;;; 1696C;BAMUM LETTER PHASE-E PAA;Lo;0;L;;;;;N;;;;; 1696D;BAMUM LETTER PHASE-E SOM;Lo;0;L;;;;;N;;;;; 1696E;BAMUM LETTER PHASE-E RAQ;Lo;0;L;;;;;N;;;;; 1696F;BAMUM LETTER PHASE-E NSHUOP;Lo;0;L;;;;;N;;;;; 16970;BAMUM LETTER PHASE-E NDUN;Lo;0;L;;;;;N;;;;; 16971;BAMUM LETTER PHASE-E PUAE;Lo;0;L;;;;;N;;;;; 16972;BAMUM LETTER PHASE-E TAM;Lo;0;L;;;;;N;;;;; 16973;BAMUM LETTER PHASE-E NGKA;Lo;0;L;;;;;N;;;;; 16974;BAMUM LETTER PHASE-E KPEUX;Lo;0;L;;;;;N;;;;; 16975;BAMUM LETTER PHASE-E WUO;Lo;0;L;;;;;N;;;;; 16976;BAMUM LETTER PHASE-E SEE;Lo;0;L;;;;;N;;;;; 16977;BAMUM LETTER PHASE-E NGGEUAET;Lo;0;L;;;;;N;;;;; 16978;BAMUM LETTER PHASE-E PAAM;Lo;0;L;;;;;N;;;;; 16979;BAMUM LETTER PHASE-E TOO;Lo;0;L;;;;;N;;;;; 1697A;BAMUM LETTER PHASE-E KUOP;Lo;0;L;;;;;N;;;;; 1697B;BAMUM LETTER PHASE-E LOM;Lo;0;L;;;;;N;;;;; 1697C;BAMUM LETTER PHASE-E NSHIEE;Lo;0;L;;;;;N;;;;; 1697D;BAMUM LETTER PHASE-E NGOP;Lo;0;L;;;;;N;;;;; 1697E;BAMUM LETTER PHASE-E MAEM;Lo;0;L;;;;;N;;;;; 1697F;BAMUM LETTER PHASE-E NGKEUX;Lo;0;L;;;;;N;;;;; 16980;BAMUM LETTER PHASE-E NGOQ;Lo;0;L;;;;;N;;;;; 16981;BAMUM LETTER PHASE-E NSHUE;Lo;0;L;;;;;N;;;;; 16982;BAMUM LETTER PHASE-E RIMGBA;Lo;0;L;;;;;N;;;;; 16983;BAMUM LETTER PHASE-E NJEUX;Lo;0;L;;;;;N;;;;; 16984;BAMUM LETTER PHASE-E PEEM;Lo;0;L;;;;;N;;;;; 16985;BAMUM LETTER PHASE-E SAA;Lo;0;L;;;;;N;;;;; 16986;BAMUM LETTER PHASE-E NGGURAE;Lo;0;L;;;;;N;;;;; 16987;BAMUM LETTER PHASE-E MGBA;Lo;0;L;;;;;N;;;;; 16988;BAMUM LETTER PHASE-E GHEUX;Lo;0;L;;;;;N;;;;; 16989;BAMUM LETTER PHASE-E NGKEUAEM;Lo;0;L;;;;;N;;;;; 1698A;BAMUM LETTER PHASE-E NJAEMLI;Lo;0;L;;;;;N;;;;; 1698B;BAMUM LETTER PHASE-E MAP;Lo;0;L;;;;;N;;;;; 1698C;BAMUM LETTER PHASE-E LOOT;Lo;0;L;;;;;N;;;;; 1698D;BAMUM LETTER PHASE-E NGGEEEE;Lo;0;L;;;;;N;;;;; 1698E;BAMUM LETTER PHASE-E NDIQ;Lo;0;L;;;;;N;;;;; 1698F;BAMUM LETTER PHASE-E TAEN NTEUM;Lo;0;L;;;;;N;;;;; 16990;BAMUM LETTER PHASE-E SET;Lo;0;L;;;;;N;;;;; 16991;BAMUM LETTER PHASE-E PUM;Lo;0;L;;;;;N;;;;; 16992;BAMUM LETTER PHASE-E NDAA SOFTNESS;Lo;0;L;;;;;N;;;;; 16993;BAMUM LETTER PHASE-E NGGUAESHAE NYAM;Lo;0;L;;;;;N;;;;; 16994;BAMUM LETTER PHASE-E YIEE;Lo;0;L;;;;;N;;;;; 16995;BAMUM LETTER PHASE-E GHEUN;Lo;0;L;;;;;N;;;;; 16996;BAMUM LETTER PHASE-E TUAE;Lo;0;L;;;;;N;;;;; 16997;BAMUM LETTER PHASE-E YEUAE;Lo;0;L;;;;;N;;;;; 16998;BAMUM LETTER PHASE-E PO;Lo;0;L;;;;;N;;;;; 16999;BAMUM LETTER PHASE-E TUMAE;Lo;0;L;;;;;N;;;;; 1699A;BAMUM LETTER PHASE-E KEUAE;Lo;0;L;;;;;N;;;;; 1699B;BAMUM LETTER PHASE-E SUAEN;Lo;0;L;;;;;N;;;;; 1699C;BAMUM LETTER PHASE-E TEUAEQ;Lo;0;L;;;;;N;;;;; 1699D;BAMUM LETTER PHASE-E VEUAE;Lo;0;L;;;;;N;;;;; 1699E;BAMUM LETTER PHASE-E WEUX;Lo;0;L;;;;;N;;;;; 1699F;BAMUM LETTER PHASE-E LAAM;Lo;0;L;;;;;N;;;;; 169A0;BAMUM LETTER PHASE-E PU;Lo;0;L;;;;;N;;;;; 169A1;BAMUM LETTER PHASE-E TAAQ;Lo;0;L;;;;;N;;;;; 169A2;BAMUM LETTER PHASE-E GHAAMAE;Lo;0;L;;;;;N;;;;; 169A3;BAMUM LETTER PHASE-E NGEUREUT;Lo;0;L;;;;;N;;;;; 169A4;BAMUM LETTER PHASE-E SHEUAEQ;Lo;0;L;;;;;N;;;;; 169A5;BAMUM LETTER PHASE-E MGBEN;Lo;0;L;;;;;N;;;;; 169A6;BAMUM LETTER PHASE-E MBEE;Lo;0;L;;;;;N;;;;; 169A7;BAMUM LETTER PHASE-E NZAQ;Lo;0;L;;;;;N;;;;; 169A8;BAMUM LETTER PHASE-E NKOM;Lo;0;L;;;;;N;;;;; 169A9;BAMUM LETTER PHASE-E GBET;Lo;0;L;;;;;N;;;;; 169AA;BAMUM LETTER PHASE-E TUM;Lo;0;L;;;;;N;;;;; 169AB;BAMUM LETTER PHASE-E KUET;Lo;0;L;;;;;N;;;;; 169AC;BAMUM LETTER PHASE-E YAP;Lo;0;L;;;;;N;;;;; 169AD;BAMUM LETTER PHASE-E NYI CLEAVER;Lo;0;L;;;;;N;;;;; 169AE;BAMUM LETTER PHASE-E YIT;Lo;0;L;;;;;N;;;;; 169AF;BAMUM LETTER PHASE-E MFEUQ;Lo;0;L;;;;;N;;;;; 169B0;BAMUM LETTER PHASE-E NDIAQ;Lo;0;L;;;;;N;;;;; 169B1;BAMUM LETTER PHASE-E PIEEQ;Lo;0;L;;;;;N;;;;; 169B2;BAMUM LETTER PHASE-E YUEQ;Lo;0;L;;;;;N;;;;; 169B3;BAMUM LETTER PHASE-E LEUAEM;Lo;0;L;;;;;N;;;;; 169B4;BAMUM LETTER PHASE-E FUE;Lo;0;L;;;;;N;;;;; 169B5;BAMUM LETTER PHASE-E GBEUX;Lo;0;L;;;;;N;;;;; 169B6;BAMUM LETTER PHASE-E NGKUP;Lo;0;L;;;;;N;;;;; 169B7;BAMUM LETTER PHASE-E KET;Lo;0;L;;;;;N;;;;; 169B8;BAMUM LETTER PHASE-E MAE;Lo;0;L;;;;;N;;;;; 169B9;BAMUM LETTER PHASE-E NGKAAMI;Lo;0;L;;;;;N;;;;; 169BA;BAMUM LETTER PHASE-E GHET;Lo;0;L;;;;;N;;;;; 169BB;BAMUM LETTER PHASE-E FA;Lo;0;L;;;;;N;;;;; 169BC;BAMUM LETTER PHASE-E NTUM;Lo;0;L;;;;;N;;;;; 169BD;BAMUM LETTER PHASE-E PEUT;Lo;0;L;;;;;N;;;;; 169BE;BAMUM LETTER PHASE-E YEUM;Lo;0;L;;;;;N;;;;; 169BF;BAMUM LETTER PHASE-E NGGEUAE;Lo;0;L;;;;;N;;;;; 169C0;BAMUM LETTER PHASE-E NYI BETWEEN;Lo;0;L;;;;;N;;;;; 169C1;BAMUM LETTER PHASE-E NZUQ;Lo;0;L;;;;;N;;;;; 169C2;BAMUM LETTER PHASE-E POON;Lo;0;L;;;;;N;;;;; 169C3;BAMUM LETTER PHASE-E MIEE;Lo;0;L;;;;;N;;;;; 169C4;BAMUM LETTER PHASE-E FUET;Lo;0;L;;;;;N;;;;; 169C5;BAMUM LETTER PHASE-E NAE;Lo;0;L;;;;;N;;;;; 169C6;BAMUM LETTER PHASE-E MUAE;Lo;0;L;;;;;N;;;;; 169C7;BAMUM LETTER PHASE-E GHEUAE;Lo;0;L;;;;;N;;;;; 169C8;BAMUM LETTER PHASE-E FU I;Lo;0;L;;;;;N;;;;; 169C9;BAMUM LETTER PHASE-E MVI;Lo;0;L;;;;;N;;;;; 169CA;BAMUM LETTER PHASE-E PUAQ;Lo;0;L;;;;;N;;;;; 169CB;BAMUM LETTER PHASE-E NGKUM;Lo;0;L;;;;;N;;;;; 169CC;BAMUM LETTER PHASE-E KUT;Lo;0;L;;;;;N;;;;; 169CD;BAMUM LETTER PHASE-E PIET;Lo;0;L;;;;;N;;;;; 169CE;BAMUM LETTER PHASE-E NTAP;Lo;0;L;;;;;N;;;;; 169CF;BAMUM LETTER PHASE-E YEUAET;Lo;0;L;;;;;N;;;;; 169D0;BAMUM LETTER PHASE-E NGGUP;Lo;0;L;;;;;N;;;;; 169D1;BAMUM LETTER PHASE-E PA PEOPLE;Lo;0;L;;;;;N;;;;; 169D2;BAMUM LETTER PHASE-E FU CALL;Lo;0;L;;;;;N;;;;; 169D3;BAMUM LETTER PHASE-E FOM;Lo;0;L;;;;;N;;;;; 169D4;BAMUM LETTER PHASE-E NJEE;Lo;0;L;;;;;N;;;;; 169D5;BAMUM LETTER PHASE-E A;Lo;0;L;;;;;N;;;;; 169D6;BAMUM LETTER PHASE-E TOQ;Lo;0;L;;;;;N;;;;; 169D7;BAMUM LETTER PHASE-E O;Lo;0;L;;;;;N;;;;; 169D8;BAMUM LETTER PHASE-E I;Lo;0;L;;;;;N;;;;; 169D9;BAMUM LETTER PHASE-E LAQ;Lo;0;L;;;;;N;;;;; 169DA;BAMUM LETTER PHASE-E PA PLURAL;Lo;0;L;;;;;N;;;;; 169DB;BAMUM LETTER PHASE-E TAA;Lo;0;L;;;;;N;;;;; 169DC;BAMUM LETTER PHASE-E TAQ;Lo;0;L;;;;;N;;;;; 169DD;BAMUM LETTER PHASE-E NDAA MY HOUSE;Lo;0;L;;;;;N;;;;; 169DE;BAMUM LETTER PHASE-E SHIQ;Lo;0;L;;;;;N;;;;; 169DF;BAMUM LETTER PHASE-E YEUX;Lo;0;L;;;;;N;;;;; 169E0;BAMUM LETTER PHASE-E NGUAE;Lo;0;L;;;;;N;;;;; 169E1;BAMUM LETTER PHASE-E YUAEN;Lo;0;L;;;;;N;;;;; 169E2;BAMUM LETTER PHASE-E YOQ SWIMMING;Lo;0;L;;;;;N;;;;; 169E3;BAMUM LETTER PHASE-E YOQ COVER;Lo;0;L;;;;;N;;;;; 169E4;BAMUM LETTER PHASE-E YUQ;Lo;0;L;;;;;N;;;;; 169E5;BAMUM LETTER PHASE-E YUN;Lo;0;L;;;;;N;;;;; 169E6;BAMUM LETTER PHASE-E KEUX;Lo;0;L;;;;;N;;;;; 169E7;BAMUM LETTER PHASE-E PEUX;Lo;0;L;;;;;N;;;;; 169E8;BAMUM LETTER PHASE-E NJEE EPOCH;Lo;0;L;;;;;N;;;;; 169E9;BAMUM LETTER PHASE-E PUE;Lo;0;L;;;;;N;;;;; 169EA;BAMUM LETTER PHASE-E WUE;Lo;0;L;;;;;N;;;;; 169EB;BAMUM LETTER PHASE-E FEE;Lo;0;L;;;;;N;;;;; 169EC;BAMUM LETTER PHASE-E VEE;Lo;0;L;;;;;N;;;;; 169ED;BAMUM LETTER PHASE-E LU;Lo;0;L;;;;;N;;;;; 169EE;BAMUM LETTER PHASE-E MI;Lo;0;L;;;;;N;;;;; 169EF;BAMUM LETTER PHASE-E REUX;Lo;0;L;;;;;N;;;;; 169F0;BAMUM LETTER PHASE-E RAE;Lo;0;L;;;;;N;;;;; 169F1;BAMUM LETTER PHASE-E NGUAET;Lo;0;L;;;;;N;;;;; 169F2;BAMUM LETTER PHASE-E NGA;Lo;0;L;;;;;N;;;;; 169F3;BAMUM LETTER PHASE-E SHO;Lo;0;L;;;;;N;;;;; 169F4;BAMUM LETTER PHASE-E SHOQ;Lo;0;L;;;;;N;;;;; 169F5;BAMUM LETTER PHASE-E FU REMEDY;Lo;0;L;;;;;N;;;;; 169F6;BAMUM LETTER PHASE-E NA;Lo;0;L;;;;;N;;;;; 169F7;BAMUM LETTER PHASE-E PI;Lo;0;L;;;;;N;;;;; 169F8;BAMUM LETTER PHASE-E LOQ;Lo;0;L;;;;;N;;;;; 169F9;BAMUM LETTER PHASE-E KO;Lo;0;L;;;;;N;;;;; 169FA;BAMUM LETTER PHASE-E MEN;Lo;0;L;;;;;N;;;;; 169FB;BAMUM LETTER PHASE-E MA;Lo;0;L;;;;;N;;;;; 169FC;BAMUM LETTER PHASE-E MAQ;Lo;0;L;;;;;N;;;;; 169FD;BAMUM LETTER PHASE-E TEU;Lo;0;L;;;;;N;;;;; 169FE;BAMUM LETTER PHASE-E KI;Lo;0;L;;;;;N;;;;; 169FF;BAMUM LETTER PHASE-E MON;Lo;0;L;;;;;N;;;;; 16A00;BAMUM LETTER PHASE-E TEN;Lo;0;L;;;;;N;;;;; 16A01;BAMUM LETTER PHASE-E FAQ;Lo;0;L;;;;;N;;;;; 16A02;BAMUM LETTER PHASE-E GHOM;Lo;0;L;;;;;N;;;;; 16A03;BAMUM LETTER PHASE-F KA;Lo;0;L;;;;;N;;;;; 16A04;BAMUM LETTER PHASE-F U;Lo;0;L;;;;;N;;;;; 16A05;BAMUM LETTER PHASE-F KU;Lo;0;L;;;;;N;;;;; 16A06;BAMUM LETTER PHASE-F EE;Lo;0;L;;;;;N;;;;; 16A07;BAMUM LETTER PHASE-F REE;Lo;0;L;;;;;N;;;;; 16A08;BAMUM LETTER PHASE-F TAE;Lo;0;L;;;;;N;;;;; 16A09;BAMUM LETTER PHASE-F NYI;Lo;0;L;;;;;N;;;;; 16A0A;BAMUM LETTER PHASE-F LA;Lo;0;L;;;;;N;;;;; 16A0B;BAMUM LETTER PHASE-F RII;Lo;0;L;;;;;N;;;;; 16A0C;BAMUM LETTER PHASE-F RIEE;Lo;0;L;;;;;N;;;;; 16A0D;BAMUM LETTER PHASE-F MEEEE;Lo;0;L;;;;;N;;;;; 16A0E;BAMUM LETTER PHASE-F TAA;Lo;0;L;;;;;N;;;;; 16A0F;BAMUM LETTER PHASE-F NDAA;Lo;0;L;;;;;N;;;;; 16A10;BAMUM LETTER PHASE-F NJAEM;Lo;0;L;;;;;N;;;;; 16A11;BAMUM LETTER PHASE-F M;Lo;0;L;;;;;N;;;;; 16A12;BAMUM LETTER PHASE-F SUU;Lo;0;L;;;;;N;;;;; 16A13;BAMUM LETTER PHASE-F SHII;Lo;0;L;;;;;N;;;;; 16A14;BAMUM LETTER PHASE-F SI;Lo;0;L;;;;;N;;;;; 16A15;BAMUM LETTER PHASE-F SEUX;Lo;0;L;;;;;N;;;;; 16A16;BAMUM LETTER PHASE-F KYEE;Lo;0;L;;;;;N;;;;; 16A17;BAMUM LETTER PHASE-F KET;Lo;0;L;;;;;N;;;;; 16A18;BAMUM LETTER PHASE-F NUAE;Lo;0;L;;;;;N;;;;; 16A19;BAMUM LETTER PHASE-F NU;Lo;0;L;;;;;N;;;;; 16A1A;BAMUM LETTER PHASE-F NJUAE;Lo;0;L;;;;;N;;;;; 16A1B;BAMUM LETTER PHASE-F YOQ;Lo;0;L;;;;;N;;;;; 16A1C;BAMUM LETTER PHASE-F SHU;Lo;0;L;;;;;N;;;;; 16A1D;BAMUM LETTER PHASE-F YA;Lo;0;L;;;;;N;;;;; 16A1E;BAMUM LETTER PHASE-F NSHA;Lo;0;L;;;;;N;;;;; 16A1F;BAMUM LETTER PHASE-F PEUX;Lo;0;L;;;;;N;;;;; 16A20;BAMUM LETTER PHASE-F NTEE;Lo;0;L;;;;;N;;;;; 16A21;BAMUM LETTER PHASE-F WUE;Lo;0;L;;;;;N;;;;; 16A22;BAMUM LETTER PHASE-F PEE;Lo;0;L;;;;;N;;;;; 16A23;BAMUM LETTER PHASE-F RU;Lo;0;L;;;;;N;;;;; 16A24;BAMUM LETTER PHASE-F NI;Lo;0;L;;;;;N;;;;; 16A25;BAMUM LETTER PHASE-F REUX;Lo;0;L;;;;;N;;;;; 16A26;BAMUM LETTER PHASE-F KEN;Lo;0;L;;;;;N;;;;; 16A27;BAMUM LETTER PHASE-F NGKWAEN;Lo;0;L;;;;;N;;;;; 16A28;BAMUM LETTER PHASE-F NGGA;Lo;0;L;;;;;N;;;;; 16A29;BAMUM LETTER PHASE-F SHO;Lo;0;L;;;;;N;;;;; 16A2A;BAMUM LETTER PHASE-F PUAE;Lo;0;L;;;;;N;;;;; 16A2B;BAMUM LETTER PHASE-F FOM;Lo;0;L;;;;;N;;;;; 16A2C;BAMUM LETTER PHASE-F WA;Lo;0;L;;;;;N;;;;; 16A2D;BAMUM LETTER PHASE-F LI;Lo;0;L;;;;;N;;;;; 16A2E;BAMUM LETTER PHASE-F LOQ;Lo;0;L;;;;;N;;;;; 16A2F;BAMUM LETTER PHASE-F KO;Lo;0;L;;;;;N;;;;; 16A30;BAMUM LETTER PHASE-F MBEN;Lo;0;L;;;;;N;;;;; 16A31;BAMUM LETTER PHASE-F REN;Lo;0;L;;;;;N;;;;; 16A32;BAMUM LETTER PHASE-F MA;Lo;0;L;;;;;N;;;;; 16A33;BAMUM LETTER PHASE-F MO;Lo;0;L;;;;;N;;;;; 16A34;BAMUM LETTER PHASE-F MBAA;Lo;0;L;;;;;N;;;;; 16A35;BAMUM LETTER PHASE-F TET;Lo;0;L;;;;;N;;;;; 16A36;BAMUM LETTER PHASE-F KPA;Lo;0;L;;;;;N;;;;; 16A37;BAMUM LETTER PHASE-F SAMBA;Lo;0;L;;;;;N;;;;; 16A38;BAMUM LETTER PHASE-F VUEQ;Lo;0;L;;;;;N;;;;; 16A40;MRO LETTER TA;Lo;0;L;;;;;N;;;;; 16A41;MRO LETTER NGI;Lo;0;L;;;;;N;;;;; 16A42;MRO LETTER YO;Lo;0;L;;;;;N;;;;; 16A43;MRO LETTER MIM;Lo;0;L;;;;;N;;;;; 16A44;MRO LETTER BA;Lo;0;L;;;;;N;;;;; 16A45;MRO LETTER DA;Lo;0;L;;;;;N;;;;; 16A46;MRO LETTER A;Lo;0;L;;;;;N;;;;; 16A47;MRO LETTER PHI;Lo;0;L;;;;;N;;;;; 16A48;MRO LETTER KHAI;Lo;0;L;;;;;N;;;;; 16A49;MRO LETTER HAO;Lo;0;L;;;;;N;;;;; 16A4A;MRO LETTER DAI;Lo;0;L;;;;;N;;;;; 16A4B;MRO LETTER CHU;Lo;0;L;;;;;N;;;;; 16A4C;MRO LETTER KEAAE;Lo;0;L;;;;;N;;;;; 16A4D;MRO LETTER OL;Lo;0;L;;;;;N;;;;; 16A4E;MRO LETTER MAEM;Lo;0;L;;;;;N;;;;; 16A4F;MRO LETTER NIN;Lo;0;L;;;;;N;;;;; 16A50;MRO LETTER PA;Lo;0;L;;;;;N;;;;; 16A51;MRO LETTER OO;Lo;0;L;;;;;N;;;;; 16A52;MRO LETTER O;Lo;0;L;;;;;N;;;;; 16A53;MRO LETTER RO;Lo;0;L;;;;;N;;;;; 16A54;MRO LETTER SHI;Lo;0;L;;;;;N;;;;; 16A55;MRO LETTER THEA;Lo;0;L;;;;;N;;;;; 16A56;MRO LETTER EA;Lo;0;L;;;;;N;;;;; 16A57;MRO LETTER WA;Lo;0;L;;;;;N;;;;; 16A58;MRO LETTER E;Lo;0;L;;;;;N;;;;; 16A59;MRO LETTER KO;Lo;0;L;;;;;N;;;;; 16A5A;MRO LETTER LAN;Lo;0;L;;;;;N;;;;; 16A5B;MRO LETTER LA;Lo;0;L;;;;;N;;;;; 16A5C;MRO LETTER HAI;Lo;0;L;;;;;N;;;;; 16A5D;MRO LETTER RI;Lo;0;L;;;;;N;;;;; 16A5E;MRO LETTER TEK;Lo;0;L;;;;;N;;;;; 16A60;MRO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 16A61;MRO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 16A62;MRO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 16A63;MRO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 16A64;MRO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 16A65;MRO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 16A66;MRO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 16A67;MRO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 16A68;MRO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 16A69;MRO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 16A6E;MRO DANDA;Po;0;L;;;;;N;;;;; 16A6F;MRO DOUBLE DANDA;Po;0;L;;;;;N;;;;; 16AD0;BASSA VAH LETTER ENNI;Lo;0;L;;;;;N;;;;; 16AD1;BASSA VAH LETTER KA;Lo;0;L;;;;;N;;;;; 16AD2;BASSA VAH LETTER SE;Lo;0;L;;;;;N;;;;; 16AD3;BASSA VAH LETTER FA;Lo;0;L;;;;;N;;;;; 16AD4;BASSA VAH LETTER MBE;Lo;0;L;;;;;N;;;;; 16AD5;BASSA VAH LETTER YIE;Lo;0;L;;;;;N;;;;; 16AD6;BASSA VAH LETTER GAH;Lo;0;L;;;;;N;;;;; 16AD7;BASSA VAH LETTER DHII;Lo;0;L;;;;;N;;;;; 16AD8;BASSA VAH LETTER KPAH;Lo;0;L;;;;;N;;;;; 16AD9;BASSA VAH LETTER JO;Lo;0;L;;;;;N;;;;; 16ADA;BASSA VAH LETTER HWAH;Lo;0;L;;;;;N;;;;; 16ADB;BASSA VAH LETTER WA;Lo;0;L;;;;;N;;;;; 16ADC;BASSA VAH LETTER ZO;Lo;0;L;;;;;N;;;;; 16ADD;BASSA VAH LETTER GBU;Lo;0;L;;;;;N;;;;; 16ADE;BASSA VAH LETTER DO;Lo;0;L;;;;;N;;;;; 16ADF;BASSA VAH LETTER CE;Lo;0;L;;;;;N;;;;; 16AE0;BASSA VAH LETTER UWU;Lo;0;L;;;;;N;;;;; 16AE1;BASSA VAH LETTER TO;Lo;0;L;;;;;N;;;;; 16AE2;BASSA VAH LETTER BA;Lo;0;L;;;;;N;;;;; 16AE3;BASSA VAH LETTER VU;Lo;0;L;;;;;N;;;;; 16AE4;BASSA VAH LETTER YEIN;Lo;0;L;;;;;N;;;;; 16AE5;BASSA VAH LETTER PA;Lo;0;L;;;;;N;;;;; 16AE6;BASSA VAH LETTER WADDA;Lo;0;L;;;;;N;;;;; 16AE7;BASSA VAH LETTER A;Lo;0;L;;;;;N;;;;; 16AE8;BASSA VAH LETTER O;Lo;0;L;;;;;N;;;;; 16AE9;BASSA VAH LETTER OO;Lo;0;L;;;;;N;;;;; 16AEA;BASSA VAH LETTER U;Lo;0;L;;;;;N;;;;; 16AEB;BASSA VAH LETTER EE;Lo;0;L;;;;;N;;;;; 16AEC;BASSA VAH LETTER E;Lo;0;L;;;;;N;;;;; 16AED;BASSA VAH LETTER I;Lo;0;L;;;;;N;;;;; 16AF0;BASSA VAH COMBINING HIGH TONE;Mn;1;NSM;;;;;N;;;;; 16AF1;BASSA VAH COMBINING LOW TONE;Mn;1;NSM;;;;;N;;;;; 16AF2;BASSA VAH COMBINING MID TONE;Mn;1;NSM;;;;;N;;;;; 16AF3;BASSA VAH COMBINING LOW-MID TONE;Mn;1;NSM;;;;;N;;;;; 16AF4;BASSA VAH COMBINING HIGH-LOW TONE;Mn;1;NSM;;;;;N;;;;; 16AF5;BASSA VAH FULL STOP;Po;0;L;;;;;N;;;;; 16B00;PAHAWH HMONG VOWEL KEEB;Lo;0;L;;;;;N;;;;; 16B01;PAHAWH HMONG VOWEL KEEV;Lo;0;L;;;;;N;;;;; 16B02;PAHAWH HMONG VOWEL KIB;Lo;0;L;;;;;N;;;;; 16B03;PAHAWH HMONG VOWEL KIV;Lo;0;L;;;;;N;;;;; 16B04;PAHAWH HMONG VOWEL KAUB;Lo;0;L;;;;;N;;;;; 16B05;PAHAWH HMONG VOWEL KAUV;Lo;0;L;;;;;N;;;;; 16B06;PAHAWH HMONG VOWEL KUB;Lo;0;L;;;;;N;;;;; 16B07;PAHAWH HMONG VOWEL KUV;Lo;0;L;;;;;N;;;;; 16B08;PAHAWH HMONG VOWEL KEB;Lo;0;L;;;;;N;;;;; 16B09;PAHAWH HMONG VOWEL KEV;Lo;0;L;;;;;N;;;;; 16B0A;PAHAWH HMONG VOWEL KAIB;Lo;0;L;;;;;N;;;;; 16B0B;PAHAWH HMONG VOWEL KAIV;Lo;0;L;;;;;N;;;;; 16B0C;PAHAWH HMONG VOWEL KOOB;Lo;0;L;;;;;N;;;;; 16B0D;PAHAWH HMONG VOWEL KOOV;Lo;0;L;;;;;N;;;;; 16B0E;PAHAWH HMONG VOWEL KAWB;Lo;0;L;;;;;N;;;;; 16B0F;PAHAWH HMONG VOWEL KAWV;Lo;0;L;;;;;N;;;;; 16B10;PAHAWH HMONG VOWEL KUAB;Lo;0;L;;;;;N;;;;; 16B11;PAHAWH HMONG VOWEL KUAV;Lo;0;L;;;;;N;;;;; 16B12;PAHAWH HMONG VOWEL KOB;Lo;0;L;;;;;N;;;;; 16B13;PAHAWH HMONG VOWEL KOV;Lo;0;L;;;;;N;;;;; 16B14;PAHAWH HMONG VOWEL KIAB;Lo;0;L;;;;;N;;;;; 16B15;PAHAWH HMONG VOWEL KIAV;Lo;0;L;;;;;N;;;;; 16B16;PAHAWH HMONG VOWEL KAB;Lo;0;L;;;;;N;;;;; 16B17;PAHAWH HMONG VOWEL KAV;Lo;0;L;;;;;N;;;;; 16B18;PAHAWH HMONG VOWEL KWB;Lo;0;L;;;;;N;;;;; 16B19;PAHAWH HMONG VOWEL KWV;Lo;0;L;;;;;N;;;;; 16B1A;PAHAWH HMONG VOWEL KAAB;Lo;0;L;;;;;N;;;;; 16B1B;PAHAWH HMONG VOWEL KAAV;Lo;0;L;;;;;N;;;;; 16B1C;PAHAWH HMONG CONSONANT VAU;Lo;0;L;;;;;N;;;;; 16B1D;PAHAWH HMONG CONSONANT NTSAU;Lo;0;L;;;;;N;;;;; 16B1E;PAHAWH HMONG CONSONANT LAU;Lo;0;L;;;;;N;;;;; 16B1F;PAHAWH HMONG CONSONANT HAU;Lo;0;L;;;;;N;;;;; 16B20;PAHAWH HMONG CONSONANT NLAU;Lo;0;L;;;;;N;;;;; 16B21;PAHAWH HMONG CONSONANT RAU;Lo;0;L;;;;;N;;;;; 16B22;PAHAWH HMONG CONSONANT NKAU;Lo;0;L;;;;;N;;;;; 16B23;PAHAWH HMONG CONSONANT QHAU;Lo;0;L;;;;;N;;;;; 16B24;PAHAWH HMONG CONSONANT YAU;Lo;0;L;;;;;N;;;;; 16B25;PAHAWH HMONG CONSONANT HLAU;Lo;0;L;;;;;N;;;;; 16B26;PAHAWH HMONG CONSONANT MAU;Lo;0;L;;;;;N;;;;; 16B27;PAHAWH HMONG CONSONANT CHAU;Lo;0;L;;;;;N;;;;; 16B28;PAHAWH HMONG CONSONANT NCHAU;Lo;0;L;;;;;N;;;;; 16B29;PAHAWH HMONG CONSONANT HNAU;Lo;0;L;;;;;N;;;;; 16B2A;PAHAWH HMONG CONSONANT PLHAU;Lo;0;L;;;;;N;;;;; 16B2B;PAHAWH HMONG CONSONANT NTHAU;Lo;0;L;;;;;N;;;;; 16B2C;PAHAWH HMONG CONSONANT NAU;Lo;0;L;;;;;N;;;;; 16B2D;PAHAWH HMONG CONSONANT AU;Lo;0;L;;;;;N;;;;; 16B2E;PAHAWH HMONG CONSONANT XAU;Lo;0;L;;;;;N;;;;; 16B2F;PAHAWH HMONG CONSONANT CAU;Lo;0;L;;;;;N;;;;; 16B30;PAHAWH HMONG MARK CIM TUB;Mn;230;NSM;;;;;N;;;;; 16B31;PAHAWH HMONG MARK CIM SO;Mn;230;NSM;;;;;N;;;;; 16B32;PAHAWH HMONG MARK CIM KES;Mn;230;NSM;;;;;N;;;;; 16B33;PAHAWH HMONG MARK CIM KHAV;Mn;230;NSM;;;;;N;;;;; 16B34;PAHAWH HMONG MARK CIM SUAM;Mn;230;NSM;;;;;N;;;;; 16B35;PAHAWH HMONG MARK CIM HOM;Mn;230;NSM;;;;;N;;;;; 16B36;PAHAWH HMONG MARK CIM TAUM;Mn;230;NSM;;;;;N;;;;; 16B37;PAHAWH HMONG SIGN VOS THOM;Po;0;L;;;;;N;;;;; 16B38;PAHAWH HMONG SIGN VOS TSHAB CEEB;Po;0;L;;;;;N;;;;; 16B39;PAHAWH HMONG SIGN CIM CHEEM;Po;0;L;;;;;N;;;;; 16B3A;PAHAWH HMONG SIGN VOS THIAB;Po;0;L;;;;;N;;;;; 16B3B;PAHAWH HMONG SIGN VOS FEEM;Po;0;L;;;;;N;;;;; 16B3C;PAHAWH HMONG SIGN XYEEM NTXIV;So;0;L;;;;;N;;;;; 16B3D;PAHAWH HMONG SIGN XYEEM RHO;So;0;L;;;;;N;;;;; 16B3E;PAHAWH HMONG SIGN XYEEM TOV;So;0;L;;;;;N;;;;; 16B3F;PAHAWH HMONG SIGN XYEEM FAIB;So;0;L;;;;;N;;;;; 16B40;PAHAWH HMONG SIGN VOS SEEV;Lm;0;L;;;;;N;;;;; 16B41;PAHAWH HMONG SIGN MEEJ SUAB;Lm;0;L;;;;;N;;;;; 16B42;PAHAWH HMONG SIGN VOS NRUA;Lm;0;L;;;;;N;;;;; 16B43;PAHAWH HMONG SIGN IB YAM;Lm;0;L;;;;;N;;;;; 16B44;PAHAWH HMONG SIGN XAUS;Po;0;L;;;;;N;;;;; 16B45;PAHAWH HMONG SIGN CIM TSOV ROG;So;0;L;;;;;N;;;;; 16B50;PAHAWH HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; 16B51;PAHAWH HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; 16B52;PAHAWH HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; 16B53;PAHAWH HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; 16B54;PAHAWH HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; 16B55;PAHAWH HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; 16B56;PAHAWH HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; 16B57;PAHAWH HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 16B58;PAHAWH HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 16B59;PAHAWH HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 16B5B;PAHAWH HMONG NUMBER TENS;No;0;L;;;;10;N;;;;; 16B5C;PAHAWH HMONG NUMBER HUNDREDS;No;0;L;;;;100;N;;;;; 16B5D;PAHAWH HMONG NUMBER TEN THOUSANDS;No;0;L;;;;10000;N;;;;; 16B5E;PAHAWH HMONG NUMBER MILLIONS;No;0;L;;;;1000000;N;;;;; 16B5F;PAHAWH HMONG NUMBER HUNDRED MILLIONS;No;0;L;;;;100000000;N;;;;; 16B60;PAHAWH HMONG NUMBER TEN BILLIONS;No;0;L;;;;10000000000;N;;;;; 16B61;PAHAWH HMONG NUMBER TRILLIONS;No;0;L;;;;1000000000000;N;;;;; 16B63;PAHAWH HMONG SIGN VOS LUB;Lo;0;L;;;;;N;;;;; 16B64;PAHAWH HMONG SIGN XYOO;Lo;0;L;;;;;N;;;;; 16B65;PAHAWH HMONG SIGN HLI;Lo;0;L;;;;;N;;;;; 16B66;PAHAWH HMONG SIGN THIRD-STAGE HLI;Lo;0;L;;;;;N;;;;; 16B67;PAHAWH HMONG SIGN ZWJ THAJ;Lo;0;L;;;;;N;;;;; 16B68;PAHAWH HMONG SIGN HNUB;Lo;0;L;;;;;N;;;;; 16B69;PAHAWH HMONG SIGN NQIG;Lo;0;L;;;;;N;;;;; 16B6A;PAHAWH HMONG SIGN XIAB;Lo;0;L;;;;;N;;;;; 16B6B;PAHAWH HMONG SIGN NTUJ;Lo;0;L;;;;;N;;;;; 16B6C;PAHAWH HMONG SIGN AV;Lo;0;L;;;;;N;;;;; 16B6D;PAHAWH HMONG SIGN TXHEEJ CEEV;Lo;0;L;;;;;N;;;;; 16B6E;PAHAWH HMONG SIGN MEEJ TSEEB;Lo;0;L;;;;;N;;;;; 16B6F;PAHAWH HMONG SIGN TAU;Lo;0;L;;;;;N;;;;; 16B70;PAHAWH HMONG SIGN LOS;Lo;0;L;;;;;N;;;;; 16B71;PAHAWH HMONG SIGN MUS;Lo;0;L;;;;;N;;;;; 16B72;PAHAWH HMONG SIGN CIM HAIS LUS NTOG NTOG;Lo;0;L;;;;;N;;;;; 16B73;PAHAWH HMONG SIGN CIM CUAM TSHOOJ;Lo;0;L;;;;;N;;;;; 16B74;PAHAWH HMONG SIGN CIM TXWV;Lo;0;L;;;;;N;;;;; 16B75;PAHAWH HMONG SIGN CIM TXWV CHWV;Lo;0;L;;;;;N;;;;; 16B76;PAHAWH HMONG SIGN CIM PUB DAWB;Lo;0;L;;;;;N;;;;; 16B77;PAHAWH HMONG SIGN CIM NRES TOS;Lo;0;L;;;;;N;;;;; 16B7D;PAHAWH HMONG CLAN SIGN TSHEEJ;Lo;0;L;;;;;N;;;;; 16B7E;PAHAWH HMONG CLAN SIGN YEEG;Lo;0;L;;;;;N;;;;; 16B7F;PAHAWH HMONG CLAN SIGN LIS;Lo;0;L;;;;;N;;;;; 16B80;PAHAWH HMONG CLAN SIGN LAUJ;Lo;0;L;;;;;N;;;;; 16B81;PAHAWH HMONG CLAN SIGN XYOOJ;Lo;0;L;;;;;N;;;;; 16B82;PAHAWH HMONG CLAN SIGN KOO;Lo;0;L;;;;;N;;;;; 16B83;PAHAWH HMONG CLAN SIGN HAWJ;Lo;0;L;;;;;N;;;;; 16B84;PAHAWH HMONG CLAN SIGN MUAS;Lo;0;L;;;;;N;;;;; 16B85;PAHAWH HMONG CLAN SIGN THOJ;Lo;0;L;;;;;N;;;;; 16B86;PAHAWH HMONG CLAN SIGN TSAB;Lo;0;L;;;;;N;;;;; 16B87;PAHAWH HMONG CLAN SIGN PHAB;Lo;0;L;;;;;N;;;;; 16B88;PAHAWH HMONG CLAN SIGN KHAB;Lo;0;L;;;;;N;;;;; 16B89;PAHAWH HMONG CLAN SIGN HAM;Lo;0;L;;;;;N;;;;; 16B8A;PAHAWH HMONG CLAN SIGN VAJ;Lo;0;L;;;;;N;;;;; 16B8B;PAHAWH HMONG CLAN SIGN FAJ;Lo;0;L;;;;;N;;;;; 16B8C;PAHAWH HMONG CLAN SIGN YAJ;Lo;0;L;;;;;N;;;;; 16B8D;PAHAWH HMONG CLAN SIGN TSWB;Lo;0;L;;;;;N;;;;; 16B8E;PAHAWH HMONG CLAN SIGN KWM;Lo;0;L;;;;;N;;;;; 16B8F;PAHAWH HMONG CLAN SIGN VWJ;Lo;0;L;;;;;N;;;;; 16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;; 16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;; 16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;; 16F03;MIAO LETTER PLA;Lo;0;L;;;;;N;;;;; 16F04;MIAO LETTER MA;Lo;0;L;;;;;N;;;;; 16F05;MIAO LETTER MHA;Lo;0;L;;;;;N;;;;; 16F06;MIAO LETTER ARCHAIC MA;Lo;0;L;;;;;N;;;;; 16F07;MIAO LETTER FA;Lo;0;L;;;;;N;;;;; 16F08;MIAO LETTER VA;Lo;0;L;;;;;N;;;;; 16F09;MIAO LETTER VFA;Lo;0;L;;;;;N;;;;; 16F0A;MIAO LETTER TA;Lo;0;L;;;;;N;;;;; 16F0B;MIAO LETTER DA;Lo;0;L;;;;;N;;;;; 16F0C;MIAO LETTER YI TTA;Lo;0;L;;;;;N;;;;; 16F0D;MIAO LETTER YI TA;Lo;0;L;;;;;N;;;;; 16F0E;MIAO LETTER TTA;Lo;0;L;;;;;N;;;;; 16F0F;MIAO LETTER DDA;Lo;0;L;;;;;N;;;;; 16F10;MIAO LETTER NA;Lo;0;L;;;;;N;;;;; 16F11;MIAO LETTER NHA;Lo;0;L;;;;;N;;;;; 16F12;MIAO LETTER YI NNA;Lo;0;L;;;;;N;;;;; 16F13;MIAO LETTER ARCHAIC NA;Lo;0;L;;;;;N;;;;; 16F14;MIAO LETTER NNA;Lo;0;L;;;;;N;;;;; 16F15;MIAO LETTER NNHA;Lo;0;L;;;;;N;;;;; 16F16;MIAO LETTER LA;Lo;0;L;;;;;N;;;;; 16F17;MIAO LETTER LYA;Lo;0;L;;;;;N;;;;; 16F18;MIAO LETTER LHA;Lo;0;L;;;;;N;;;;; 16F19;MIAO LETTER LHYA;Lo;0;L;;;;;N;;;;; 16F1A;MIAO LETTER TLHA;Lo;0;L;;;;;N;;;;; 16F1B;MIAO LETTER DLHA;Lo;0;L;;;;;N;;;;; 16F1C;MIAO LETTER TLHYA;Lo;0;L;;;;;N;;;;; 16F1D;MIAO LETTER DLHYA;Lo;0;L;;;;;N;;;;; 16F1E;MIAO LETTER KA;Lo;0;L;;;;;N;;;;; 16F1F;MIAO LETTER GA;Lo;0;L;;;;;N;;;;; 16F20;MIAO LETTER YI KA;Lo;0;L;;;;;N;;;;; 16F21;MIAO LETTER QA;Lo;0;L;;;;;N;;;;; 16F22;MIAO LETTER QGA;Lo;0;L;;;;;N;;;;; 16F23;MIAO LETTER NGA;Lo;0;L;;;;;N;;;;; 16F24;MIAO LETTER NGHA;Lo;0;L;;;;;N;;;;; 16F25;MIAO LETTER ARCHAIC NGA;Lo;0;L;;;;;N;;;;; 16F26;MIAO LETTER HA;Lo;0;L;;;;;N;;;;; 16F27;MIAO LETTER XA;Lo;0;L;;;;;N;;;;; 16F28;MIAO LETTER GHA;Lo;0;L;;;;;N;;;;; 16F29;MIAO LETTER GHHA;Lo;0;L;;;;;N;;;;; 16F2A;MIAO LETTER TSSA;Lo;0;L;;;;;N;;;;; 16F2B;MIAO LETTER DZZA;Lo;0;L;;;;;N;;;;; 16F2C;MIAO LETTER NYA;Lo;0;L;;;;;N;;;;; 16F2D;MIAO LETTER NYHA;Lo;0;L;;;;;N;;;;; 16F2E;MIAO LETTER TSHA;Lo;0;L;;;;;N;;;;; 16F2F;MIAO LETTER DZHA;Lo;0;L;;;;;N;;;;; 16F30;MIAO LETTER YI TSHA;Lo;0;L;;;;;N;;;;; 16F31;MIAO LETTER YI DZHA;Lo;0;L;;;;;N;;;;; 16F32;MIAO LETTER REFORMED TSHA;Lo;0;L;;;;;N;;;;; 16F33;MIAO LETTER SHA;Lo;0;L;;;;;N;;;;; 16F34;MIAO LETTER SSA;Lo;0;L;;;;;N;;;;; 16F35;MIAO LETTER ZHA;Lo;0;L;;;;;N;;;;; 16F36;MIAO LETTER ZSHA;Lo;0;L;;;;;N;;;;; 16F37;MIAO LETTER TSA;Lo;0;L;;;;;N;;;;; 16F38;MIAO LETTER DZA;Lo;0;L;;;;;N;;;;; 16F39;MIAO LETTER YI TSA;Lo;0;L;;;;;N;;;;; 16F3A;MIAO LETTER SA;Lo;0;L;;;;;N;;;;; 16F3B;MIAO LETTER ZA;Lo;0;L;;;;;N;;;;; 16F3C;MIAO LETTER ZSA;Lo;0;L;;;;;N;;;;; 16F3D;MIAO LETTER ZZA;Lo;0;L;;;;;N;;;;; 16F3E;MIAO LETTER ZZSA;Lo;0;L;;;;;N;;;;; 16F3F;MIAO LETTER ARCHAIC ZZA;Lo;0;L;;;;;N;;;;; 16F40;MIAO LETTER ZZYA;Lo;0;L;;;;;N;;;;; 16F41;MIAO LETTER ZZSYA;Lo;0;L;;;;;N;;;;; 16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;; 16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;; 16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;; 16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;; 16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;; 16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;; 16F53;MIAO SIGN REFORMED ASPIRATION;Mc;0;L;;;;;N;;;;; 16F54;MIAO VOWEL SIGN A;Mc;0;L;;;;;N;;;;; 16F55;MIAO VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 16F56;MIAO VOWEL SIGN AHH;Mc;0;L;;;;;N;;;;; 16F57;MIAO VOWEL SIGN AN;Mc;0;L;;;;;N;;;;; 16F58;MIAO VOWEL SIGN ANG;Mc;0;L;;;;;N;;;;; 16F59;MIAO VOWEL SIGN O;Mc;0;L;;;;;N;;;;; 16F5A;MIAO VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; 16F5B;MIAO VOWEL SIGN WO;Mc;0;L;;;;;N;;;;; 16F5C;MIAO VOWEL SIGN W;Mc;0;L;;;;;N;;;;; 16F5D;MIAO VOWEL SIGN E;Mc;0;L;;;;;N;;;;; 16F5E;MIAO VOWEL SIGN EN;Mc;0;L;;;;;N;;;;; 16F5F;MIAO VOWEL SIGN ENG;Mc;0;L;;;;;N;;;;; 16F60;MIAO VOWEL SIGN OEY;Mc;0;L;;;;;N;;;;; 16F61;MIAO VOWEL SIGN I;Mc;0;L;;;;;N;;;;; 16F62;MIAO VOWEL SIGN IA;Mc;0;L;;;;;N;;;;; 16F63;MIAO VOWEL SIGN IAN;Mc;0;L;;;;;N;;;;; 16F64;MIAO VOWEL SIGN IANG;Mc;0;L;;;;;N;;;;; 16F65;MIAO VOWEL SIGN IO;Mc;0;L;;;;;N;;;;; 16F66;MIAO VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; 16F67;MIAO VOWEL SIGN II;Mc;0;L;;;;;N;;;;; 16F68;MIAO VOWEL SIGN IU;Mc;0;L;;;;;N;;;;; 16F69;MIAO VOWEL SIGN ING;Mc;0;L;;;;;N;;;;; 16F6A;MIAO VOWEL SIGN U;Mc;0;L;;;;;N;;;;; 16F6B;MIAO VOWEL SIGN UA;Mc;0;L;;;;;N;;;;; 16F6C;MIAO VOWEL SIGN UAN;Mc;0;L;;;;;N;;;;; 16F6D;MIAO VOWEL SIGN UANG;Mc;0;L;;;;;N;;;;; 16F6E;MIAO VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; 16F6F;MIAO VOWEL SIGN UEI;Mc;0;L;;;;;N;;;;; 16F70;MIAO VOWEL SIGN UNG;Mc;0;L;;;;;N;;;;; 16F71;MIAO VOWEL SIGN Y;Mc;0;L;;;;;N;;;;; 16F72;MIAO VOWEL SIGN YI;Mc;0;L;;;;;N;;;;; 16F73;MIAO VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; 16F74;MIAO VOWEL SIGN AEE;Mc;0;L;;;;;N;;;;; 16F75;MIAO VOWEL SIGN ERR;Mc;0;L;;;;;N;;;;; 16F76;MIAO VOWEL SIGN ROUNDED ERR;Mc;0;L;;;;;N;;;;; 16F77;MIAO VOWEL SIGN ER;Mc;0;L;;;;;N;;;;; 16F78;MIAO VOWEL SIGN ROUNDED ER;Mc;0;L;;;;;N;;;;; 16F79;MIAO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; 16F7A;MIAO VOWEL SIGN EI;Mc;0;L;;;;;N;;;;; 16F7B;MIAO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; 16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;; 16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;; 16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;; 16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;; 16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;; 16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;; 16F92;MIAO TONE BELOW;Mn;0;NSM;;;;;N;;;;; 16F93;MIAO LETTER TONE-2;Lm;0;L;;;;;N;;;;; 16F94;MIAO LETTER TONE-3;Lm;0;L;;;;;N;;;;; 16F95;MIAO LETTER TONE-4;Lm;0;L;;;;;N;;;;; 16F96;MIAO LETTER TONE-5;Lm;0;L;;;;;N;;;;; 16F97;MIAO LETTER TONE-6;Lm;0;L;;;;;N;;;;; 16F98;MIAO LETTER TONE-7;Lm;0;L;;;;;N;;;;; 16F99;MIAO LETTER TONE-8;Lm;0;L;;;;;N;;;;; 16F9A;MIAO LETTER REFORMED TONE-1;Lm;0;L;;;;;N;;;;; 16F9B;MIAO LETTER REFORMED TONE-2;Lm;0;L;;;;;N;;;;; 16F9C;MIAO LETTER REFORMED TONE-4;Lm;0;L;;;;;N;;;;; 16F9D;MIAO LETTER REFORMED TONE-5;Lm;0;L;;;;;N;;;;; 16F9E;MIAO LETTER REFORMED TONE-6;Lm;0;L;;;;;N;;;;; 16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;; 16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;; 17000;;Lo;0;L;;;;;N;;;;; 187EC;;Lo;0;L;;;;;N;;;;; 18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;; 18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;; 18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;; 18803;TANGUT COMPONENT-004;Lo;0;L;;;;;N;;;;; 18804;TANGUT COMPONENT-005;Lo;0;L;;;;;N;;;;; 18805;TANGUT COMPONENT-006;Lo;0;L;;;;;N;;;;; 18806;TANGUT COMPONENT-007;Lo;0;L;;;;;N;;;;; 18807;TANGUT COMPONENT-008;Lo;0;L;;;;;N;;;;; 18808;TANGUT COMPONENT-009;Lo;0;L;;;;;N;;;;; 18809;TANGUT COMPONENT-010;Lo;0;L;;;;;N;;;;; 1880A;TANGUT COMPONENT-011;Lo;0;L;;;;;N;;;;; 1880B;TANGUT COMPONENT-012;Lo;0;L;;;;;N;;;;; 1880C;TANGUT COMPONENT-013;Lo;0;L;;;;;N;;;;; 1880D;TANGUT COMPONENT-014;Lo;0;L;;;;;N;;;;; 1880E;TANGUT COMPONENT-015;Lo;0;L;;;;;N;;;;; 1880F;TANGUT COMPONENT-016;Lo;0;L;;;;;N;;;;; 18810;TANGUT COMPONENT-017;Lo;0;L;;;;;N;;;;; 18811;TANGUT COMPONENT-018;Lo;0;L;;;;;N;;;;; 18812;TANGUT COMPONENT-019;Lo;0;L;;;;;N;;;;; 18813;TANGUT COMPONENT-020;Lo;0;L;;;;;N;;;;; 18814;TANGUT COMPONENT-021;Lo;0;L;;;;;N;;;;; 18815;TANGUT COMPONENT-022;Lo;0;L;;;;;N;;;;; 18816;TANGUT COMPONENT-023;Lo;0;L;;;;;N;;;;; 18817;TANGUT COMPONENT-024;Lo;0;L;;;;;N;;;;; 18818;TANGUT COMPONENT-025;Lo;0;L;;;;;N;;;;; 18819;TANGUT COMPONENT-026;Lo;0;L;;;;;N;;;;; 1881A;TANGUT COMPONENT-027;Lo;0;L;;;;;N;;;;; 1881B;TANGUT COMPONENT-028;Lo;0;L;;;;;N;;;;; 1881C;TANGUT COMPONENT-029;Lo;0;L;;;;;N;;;;; 1881D;TANGUT COMPONENT-030;Lo;0;L;;;;;N;;;;; 1881E;TANGUT COMPONENT-031;Lo;0;L;;;;;N;;;;; 1881F;TANGUT COMPONENT-032;Lo;0;L;;;;;N;;;;; 18820;TANGUT COMPONENT-033;Lo;0;L;;;;;N;;;;; 18821;TANGUT COMPONENT-034;Lo;0;L;;;;;N;;;;; 18822;TANGUT COMPONENT-035;Lo;0;L;;;;;N;;;;; 18823;TANGUT COMPONENT-036;Lo;0;L;;;;;N;;;;; 18824;TANGUT COMPONENT-037;Lo;0;L;;;;;N;;;;; 18825;TANGUT COMPONENT-038;Lo;0;L;;;;;N;;;;; 18826;TANGUT COMPONENT-039;Lo;0;L;;;;;N;;;;; 18827;TANGUT COMPONENT-040;Lo;0;L;;;;;N;;;;; 18828;TANGUT COMPONENT-041;Lo;0;L;;;;;N;;;;; 18829;TANGUT COMPONENT-042;Lo;0;L;;;;;N;;;;; 1882A;TANGUT COMPONENT-043;Lo;0;L;;;;;N;;;;; 1882B;TANGUT COMPONENT-044;Lo;0;L;;;;;N;;;;; 1882C;TANGUT COMPONENT-045;Lo;0;L;;;;;N;;;;; 1882D;TANGUT COMPONENT-046;Lo;0;L;;;;;N;;;;; 1882E;TANGUT COMPONENT-047;Lo;0;L;;;;;N;;;;; 1882F;TANGUT COMPONENT-048;Lo;0;L;;;;;N;;;;; 18830;TANGUT COMPONENT-049;Lo;0;L;;;;;N;;;;; 18831;TANGUT COMPONENT-050;Lo;0;L;;;;;N;;;;; 18832;TANGUT COMPONENT-051;Lo;0;L;;;;;N;;;;; 18833;TANGUT COMPONENT-052;Lo;0;L;;;;;N;;;;; 18834;TANGUT COMPONENT-053;Lo;0;L;;;;;N;;;;; 18835;TANGUT COMPONENT-054;Lo;0;L;;;;;N;;;;; 18836;TANGUT COMPONENT-055;Lo;0;L;;;;;N;;;;; 18837;TANGUT COMPONENT-056;Lo;0;L;;;;;N;;;;; 18838;TANGUT COMPONENT-057;Lo;0;L;;;;;N;;;;; 18839;TANGUT COMPONENT-058;Lo;0;L;;;;;N;;;;; 1883A;TANGUT COMPONENT-059;Lo;0;L;;;;;N;;;;; 1883B;TANGUT COMPONENT-060;Lo;0;L;;;;;N;;;;; 1883C;TANGUT COMPONENT-061;Lo;0;L;;;;;N;;;;; 1883D;TANGUT COMPONENT-062;Lo;0;L;;;;;N;;;;; 1883E;TANGUT COMPONENT-063;Lo;0;L;;;;;N;;;;; 1883F;TANGUT COMPONENT-064;Lo;0;L;;;;;N;;;;; 18840;TANGUT COMPONENT-065;Lo;0;L;;;;;N;;;;; 18841;TANGUT COMPONENT-066;Lo;0;L;;;;;N;;;;; 18842;TANGUT COMPONENT-067;Lo;0;L;;;;;N;;;;; 18843;TANGUT COMPONENT-068;Lo;0;L;;;;;N;;;;; 18844;TANGUT COMPONENT-069;Lo;0;L;;;;;N;;;;; 18845;TANGUT COMPONENT-070;Lo;0;L;;;;;N;;;;; 18846;TANGUT COMPONENT-071;Lo;0;L;;;;;N;;;;; 18847;TANGUT COMPONENT-072;Lo;0;L;;;;;N;;;;; 18848;TANGUT COMPONENT-073;Lo;0;L;;;;;N;;;;; 18849;TANGUT COMPONENT-074;Lo;0;L;;;;;N;;;;; 1884A;TANGUT COMPONENT-075;Lo;0;L;;;;;N;;;;; 1884B;TANGUT COMPONENT-076;Lo;0;L;;;;;N;;;;; 1884C;TANGUT COMPONENT-077;Lo;0;L;;;;;N;;;;; 1884D;TANGUT COMPONENT-078;Lo;0;L;;;;;N;;;;; 1884E;TANGUT COMPONENT-079;Lo;0;L;;;;;N;;;;; 1884F;TANGUT COMPONENT-080;Lo;0;L;;;;;N;;;;; 18850;TANGUT COMPONENT-081;Lo;0;L;;;;;N;;;;; 18851;TANGUT COMPONENT-082;Lo;0;L;;;;;N;;;;; 18852;TANGUT COMPONENT-083;Lo;0;L;;;;;N;;;;; 18853;TANGUT COMPONENT-084;Lo;0;L;;;;;N;;;;; 18854;TANGUT COMPONENT-085;Lo;0;L;;;;;N;;;;; 18855;TANGUT COMPONENT-086;Lo;0;L;;;;;N;;;;; 18856;TANGUT COMPONENT-087;Lo;0;L;;;;;N;;;;; 18857;TANGUT COMPONENT-088;Lo;0;L;;;;;N;;;;; 18858;TANGUT COMPONENT-089;Lo;0;L;;;;;N;;;;; 18859;TANGUT COMPONENT-090;Lo;0;L;;;;;N;;;;; 1885A;TANGUT COMPONENT-091;Lo;0;L;;;;;N;;;;; 1885B;TANGUT COMPONENT-092;Lo;0;L;;;;;N;;;;; 1885C;TANGUT COMPONENT-093;Lo;0;L;;;;;N;;;;; 1885D;TANGUT COMPONENT-094;Lo;0;L;;;;;N;;;;; 1885E;TANGUT COMPONENT-095;Lo;0;L;;;;;N;;;;; 1885F;TANGUT COMPONENT-096;Lo;0;L;;;;;N;;;;; 18860;TANGUT COMPONENT-097;Lo;0;L;;;;;N;;;;; 18861;TANGUT COMPONENT-098;Lo;0;L;;;;;N;;;;; 18862;TANGUT COMPONENT-099;Lo;0;L;;;;;N;;;;; 18863;TANGUT COMPONENT-100;Lo;0;L;;;;;N;;;;; 18864;TANGUT COMPONENT-101;Lo;0;L;;;;;N;;;;; 18865;TANGUT COMPONENT-102;Lo;0;L;;;;;N;;;;; 18866;TANGUT COMPONENT-103;Lo;0;L;;;;;N;;;;; 18867;TANGUT COMPONENT-104;Lo;0;L;;;;;N;;;;; 18868;TANGUT COMPONENT-105;Lo;0;L;;;;;N;;;;; 18869;TANGUT COMPONENT-106;Lo;0;L;;;;;N;;;;; 1886A;TANGUT COMPONENT-107;Lo;0;L;;;;;N;;;;; 1886B;TANGUT COMPONENT-108;Lo;0;L;;;;;N;;;;; 1886C;TANGUT COMPONENT-109;Lo;0;L;;;;;N;;;;; 1886D;TANGUT COMPONENT-110;Lo;0;L;;;;;N;;;;; 1886E;TANGUT COMPONENT-111;Lo;0;L;;;;;N;;;;; 1886F;TANGUT COMPONENT-112;Lo;0;L;;;;;N;;;;; 18870;TANGUT COMPONENT-113;Lo;0;L;;;;;N;;;;; 18871;TANGUT COMPONENT-114;Lo;0;L;;;;;N;;;;; 18872;TANGUT COMPONENT-115;Lo;0;L;;;;;N;;;;; 18873;TANGUT COMPONENT-116;Lo;0;L;;;;;N;;;;; 18874;TANGUT COMPONENT-117;Lo;0;L;;;;;N;;;;; 18875;TANGUT COMPONENT-118;Lo;0;L;;;;;N;;;;; 18876;TANGUT COMPONENT-119;Lo;0;L;;;;;N;;;;; 18877;TANGUT COMPONENT-120;Lo;0;L;;;;;N;;;;; 18878;TANGUT COMPONENT-121;Lo;0;L;;;;;N;;;;; 18879;TANGUT COMPONENT-122;Lo;0;L;;;;;N;;;;; 1887A;TANGUT COMPONENT-123;Lo;0;L;;;;;N;;;;; 1887B;TANGUT COMPONENT-124;Lo;0;L;;;;;N;;;;; 1887C;TANGUT COMPONENT-125;Lo;0;L;;;;;N;;;;; 1887D;TANGUT COMPONENT-126;Lo;0;L;;;;;N;;;;; 1887E;TANGUT COMPONENT-127;Lo;0;L;;;;;N;;;;; 1887F;TANGUT COMPONENT-128;Lo;0;L;;;;;N;;;;; 18880;TANGUT COMPONENT-129;Lo;0;L;;;;;N;;;;; 18881;TANGUT COMPONENT-130;Lo;0;L;;;;;N;;;;; 18882;TANGUT COMPONENT-131;Lo;0;L;;;;;N;;;;; 18883;TANGUT COMPONENT-132;Lo;0;L;;;;;N;;;;; 18884;TANGUT COMPONENT-133;Lo;0;L;;;;;N;;;;; 18885;TANGUT COMPONENT-134;Lo;0;L;;;;;N;;;;; 18886;TANGUT COMPONENT-135;Lo;0;L;;;;;N;;;;; 18887;TANGUT COMPONENT-136;Lo;0;L;;;;;N;;;;; 18888;TANGUT COMPONENT-137;Lo;0;L;;;;;N;;;;; 18889;TANGUT COMPONENT-138;Lo;0;L;;;;;N;;;;; 1888A;TANGUT COMPONENT-139;Lo;0;L;;;;;N;;;;; 1888B;TANGUT COMPONENT-140;Lo;0;L;;;;;N;;;;; 1888C;TANGUT COMPONENT-141;Lo;0;L;;;;;N;;;;; 1888D;TANGUT COMPONENT-142;Lo;0;L;;;;;N;;;;; 1888E;TANGUT COMPONENT-143;Lo;0;L;;;;;N;;;;; 1888F;TANGUT COMPONENT-144;Lo;0;L;;;;;N;;;;; 18890;TANGUT COMPONENT-145;Lo;0;L;;;;;N;;;;; 18891;TANGUT COMPONENT-146;Lo;0;L;;;;;N;;;;; 18892;TANGUT COMPONENT-147;Lo;0;L;;;;;N;;;;; 18893;TANGUT COMPONENT-148;Lo;0;L;;;;;N;;;;; 18894;TANGUT COMPONENT-149;Lo;0;L;;;;;N;;;;; 18895;TANGUT COMPONENT-150;Lo;0;L;;;;;N;;;;; 18896;TANGUT COMPONENT-151;Lo;0;L;;;;;N;;;;; 18897;TANGUT COMPONENT-152;Lo;0;L;;;;;N;;;;; 18898;TANGUT COMPONENT-153;Lo;0;L;;;;;N;;;;; 18899;TANGUT COMPONENT-154;Lo;0;L;;;;;N;;;;; 1889A;TANGUT COMPONENT-155;Lo;0;L;;;;;N;;;;; 1889B;TANGUT COMPONENT-156;Lo;0;L;;;;;N;;;;; 1889C;TANGUT COMPONENT-157;Lo;0;L;;;;;N;;;;; 1889D;TANGUT COMPONENT-158;Lo;0;L;;;;;N;;;;; 1889E;TANGUT COMPONENT-159;Lo;0;L;;;;;N;;;;; 1889F;TANGUT COMPONENT-160;Lo;0;L;;;;;N;;;;; 188A0;TANGUT COMPONENT-161;Lo;0;L;;;;;N;;;;; 188A1;TANGUT COMPONENT-162;Lo;0;L;;;;;N;;;;; 188A2;TANGUT COMPONENT-163;Lo;0;L;;;;;N;;;;; 188A3;TANGUT COMPONENT-164;Lo;0;L;;;;;N;;;;; 188A4;TANGUT COMPONENT-165;Lo;0;L;;;;;N;;;;; 188A5;TANGUT COMPONENT-166;Lo;0;L;;;;;N;;;;; 188A6;TANGUT COMPONENT-167;Lo;0;L;;;;;N;;;;; 188A7;TANGUT COMPONENT-168;Lo;0;L;;;;;N;;;;; 188A8;TANGUT COMPONENT-169;Lo;0;L;;;;;N;;;;; 188A9;TANGUT COMPONENT-170;Lo;0;L;;;;;N;;;;; 188AA;TANGUT COMPONENT-171;Lo;0;L;;;;;N;;;;; 188AB;TANGUT COMPONENT-172;Lo;0;L;;;;;N;;;;; 188AC;TANGUT COMPONENT-173;Lo;0;L;;;;;N;;;;; 188AD;TANGUT COMPONENT-174;Lo;0;L;;;;;N;;;;; 188AE;TANGUT COMPONENT-175;Lo;0;L;;;;;N;;;;; 188AF;TANGUT COMPONENT-176;Lo;0;L;;;;;N;;;;; 188B0;TANGUT COMPONENT-177;Lo;0;L;;;;;N;;;;; 188B1;TANGUT COMPONENT-178;Lo;0;L;;;;;N;;;;; 188B2;TANGUT COMPONENT-179;Lo;0;L;;;;;N;;;;; 188B3;TANGUT COMPONENT-180;Lo;0;L;;;;;N;;;;; 188B4;TANGUT COMPONENT-181;Lo;0;L;;;;;N;;;;; 188B5;TANGUT COMPONENT-182;Lo;0;L;;;;;N;;;;; 188B6;TANGUT COMPONENT-183;Lo;0;L;;;;;N;;;;; 188B7;TANGUT COMPONENT-184;Lo;0;L;;;;;N;;;;; 188B8;TANGUT COMPONENT-185;Lo;0;L;;;;;N;;;;; 188B9;TANGUT COMPONENT-186;Lo;0;L;;;;;N;;;;; 188BA;TANGUT COMPONENT-187;Lo;0;L;;;;;N;;;;; 188BB;TANGUT COMPONENT-188;Lo;0;L;;;;;N;;;;; 188BC;TANGUT COMPONENT-189;Lo;0;L;;;;;N;;;;; 188BD;TANGUT COMPONENT-190;Lo;0;L;;;;;N;;;;; 188BE;TANGUT COMPONENT-191;Lo;0;L;;;;;N;;;;; 188BF;TANGUT COMPONENT-192;Lo;0;L;;;;;N;;;;; 188C0;TANGUT COMPONENT-193;Lo;0;L;;;;;N;;;;; 188C1;TANGUT COMPONENT-194;Lo;0;L;;;;;N;;;;; 188C2;TANGUT COMPONENT-195;Lo;0;L;;;;;N;;;;; 188C3;TANGUT COMPONENT-196;Lo;0;L;;;;;N;;;;; 188C4;TANGUT COMPONENT-197;Lo;0;L;;;;;N;;;;; 188C5;TANGUT COMPONENT-198;Lo;0;L;;;;;N;;;;; 188C6;TANGUT COMPONENT-199;Lo;0;L;;;;;N;;;;; 188C7;TANGUT COMPONENT-200;Lo;0;L;;;;;N;;;;; 188C8;TANGUT COMPONENT-201;Lo;0;L;;;;;N;;;;; 188C9;TANGUT COMPONENT-202;Lo;0;L;;;;;N;;;;; 188CA;TANGUT COMPONENT-203;Lo;0;L;;;;;N;;;;; 188CB;TANGUT COMPONENT-204;Lo;0;L;;;;;N;;;;; 188CC;TANGUT COMPONENT-205;Lo;0;L;;;;;N;;;;; 188CD;TANGUT COMPONENT-206;Lo;0;L;;;;;N;;;;; 188CE;TANGUT COMPONENT-207;Lo;0;L;;;;;N;;;;; 188CF;TANGUT COMPONENT-208;Lo;0;L;;;;;N;;;;; 188D0;TANGUT COMPONENT-209;Lo;0;L;;;;;N;;;;; 188D1;TANGUT COMPONENT-210;Lo;0;L;;;;;N;;;;; 188D2;TANGUT COMPONENT-211;Lo;0;L;;;;;N;;;;; 188D3;TANGUT COMPONENT-212;Lo;0;L;;;;;N;;;;; 188D4;TANGUT COMPONENT-213;Lo;0;L;;;;;N;;;;; 188D5;TANGUT COMPONENT-214;Lo;0;L;;;;;N;;;;; 188D6;TANGUT COMPONENT-215;Lo;0;L;;;;;N;;;;; 188D7;TANGUT COMPONENT-216;Lo;0;L;;;;;N;;;;; 188D8;TANGUT COMPONENT-217;Lo;0;L;;;;;N;;;;; 188D9;TANGUT COMPONENT-218;Lo;0;L;;;;;N;;;;; 188DA;TANGUT COMPONENT-219;Lo;0;L;;;;;N;;;;; 188DB;TANGUT COMPONENT-220;Lo;0;L;;;;;N;;;;; 188DC;TANGUT COMPONENT-221;Lo;0;L;;;;;N;;;;; 188DD;TANGUT COMPONENT-222;Lo;0;L;;;;;N;;;;; 188DE;TANGUT COMPONENT-223;Lo;0;L;;;;;N;;;;; 188DF;TANGUT COMPONENT-224;Lo;0;L;;;;;N;;;;; 188E0;TANGUT COMPONENT-225;Lo;0;L;;;;;N;;;;; 188E1;TANGUT COMPONENT-226;Lo;0;L;;;;;N;;;;; 188E2;TANGUT COMPONENT-227;Lo;0;L;;;;;N;;;;; 188E3;TANGUT COMPONENT-228;Lo;0;L;;;;;N;;;;; 188E4;TANGUT COMPONENT-229;Lo;0;L;;;;;N;;;;; 188E5;TANGUT COMPONENT-230;Lo;0;L;;;;;N;;;;; 188E6;TANGUT COMPONENT-231;Lo;0;L;;;;;N;;;;; 188E7;TANGUT COMPONENT-232;Lo;0;L;;;;;N;;;;; 188E8;TANGUT COMPONENT-233;Lo;0;L;;;;;N;;;;; 188E9;TANGUT COMPONENT-234;Lo;0;L;;;;;N;;;;; 188EA;TANGUT COMPONENT-235;Lo;0;L;;;;;N;;;;; 188EB;TANGUT COMPONENT-236;Lo;0;L;;;;;N;;;;; 188EC;TANGUT COMPONENT-237;Lo;0;L;;;;;N;;;;; 188ED;TANGUT COMPONENT-238;Lo;0;L;;;;;N;;;;; 188EE;TANGUT COMPONENT-239;Lo;0;L;;;;;N;;;;; 188EF;TANGUT COMPONENT-240;Lo;0;L;;;;;N;;;;; 188F0;TANGUT COMPONENT-241;Lo;0;L;;;;;N;;;;; 188F1;TANGUT COMPONENT-242;Lo;0;L;;;;;N;;;;; 188F2;TANGUT COMPONENT-243;Lo;0;L;;;;;N;;;;; 188F3;TANGUT COMPONENT-244;Lo;0;L;;;;;N;;;;; 188F4;TANGUT COMPONENT-245;Lo;0;L;;;;;N;;;;; 188F5;TANGUT COMPONENT-246;Lo;0;L;;;;;N;;;;; 188F6;TANGUT COMPONENT-247;Lo;0;L;;;;;N;;;;; 188F7;TANGUT COMPONENT-248;Lo;0;L;;;;;N;;;;; 188F8;TANGUT COMPONENT-249;Lo;0;L;;;;;N;;;;; 188F9;TANGUT COMPONENT-250;Lo;0;L;;;;;N;;;;; 188FA;TANGUT COMPONENT-251;Lo;0;L;;;;;N;;;;; 188FB;TANGUT COMPONENT-252;Lo;0;L;;;;;N;;;;; 188FC;TANGUT COMPONENT-253;Lo;0;L;;;;;N;;;;; 188FD;TANGUT COMPONENT-254;Lo;0;L;;;;;N;;;;; 188FE;TANGUT COMPONENT-255;Lo;0;L;;;;;N;;;;; 188FF;TANGUT COMPONENT-256;Lo;0;L;;;;;N;;;;; 18900;TANGUT COMPONENT-257;Lo;0;L;;;;;N;;;;; 18901;TANGUT COMPONENT-258;Lo;0;L;;;;;N;;;;; 18902;TANGUT COMPONENT-259;Lo;0;L;;;;;N;;;;; 18903;TANGUT COMPONENT-260;Lo;0;L;;;;;N;;;;; 18904;TANGUT COMPONENT-261;Lo;0;L;;;;;N;;;;; 18905;TANGUT COMPONENT-262;Lo;0;L;;;;;N;;;;; 18906;TANGUT COMPONENT-263;Lo;0;L;;;;;N;;;;; 18907;TANGUT COMPONENT-264;Lo;0;L;;;;;N;;;;; 18908;TANGUT COMPONENT-265;Lo;0;L;;;;;N;;;;; 18909;TANGUT COMPONENT-266;Lo;0;L;;;;;N;;;;; 1890A;TANGUT COMPONENT-267;Lo;0;L;;;;;N;;;;; 1890B;TANGUT COMPONENT-268;Lo;0;L;;;;;N;;;;; 1890C;TANGUT COMPONENT-269;Lo;0;L;;;;;N;;;;; 1890D;TANGUT COMPONENT-270;Lo;0;L;;;;;N;;;;; 1890E;TANGUT COMPONENT-271;Lo;0;L;;;;;N;;;;; 1890F;TANGUT COMPONENT-272;Lo;0;L;;;;;N;;;;; 18910;TANGUT COMPONENT-273;Lo;0;L;;;;;N;;;;; 18911;TANGUT COMPONENT-274;Lo;0;L;;;;;N;;;;; 18912;TANGUT COMPONENT-275;Lo;0;L;;;;;N;;;;; 18913;TANGUT COMPONENT-276;Lo;0;L;;;;;N;;;;; 18914;TANGUT COMPONENT-277;Lo;0;L;;;;;N;;;;; 18915;TANGUT COMPONENT-278;Lo;0;L;;;;;N;;;;; 18916;TANGUT COMPONENT-279;Lo;0;L;;;;;N;;;;; 18917;TANGUT COMPONENT-280;Lo;0;L;;;;;N;;;;; 18918;TANGUT COMPONENT-281;Lo;0;L;;;;;N;;;;; 18919;TANGUT COMPONENT-282;Lo;0;L;;;;;N;;;;; 1891A;TANGUT COMPONENT-283;Lo;0;L;;;;;N;;;;; 1891B;TANGUT COMPONENT-284;Lo;0;L;;;;;N;;;;; 1891C;TANGUT COMPONENT-285;Lo;0;L;;;;;N;;;;; 1891D;TANGUT COMPONENT-286;Lo;0;L;;;;;N;;;;; 1891E;TANGUT COMPONENT-287;Lo;0;L;;;;;N;;;;; 1891F;TANGUT COMPONENT-288;Lo;0;L;;;;;N;;;;; 18920;TANGUT COMPONENT-289;Lo;0;L;;;;;N;;;;; 18921;TANGUT COMPONENT-290;Lo;0;L;;;;;N;;;;; 18922;TANGUT COMPONENT-291;Lo;0;L;;;;;N;;;;; 18923;TANGUT COMPONENT-292;Lo;0;L;;;;;N;;;;; 18924;TANGUT COMPONENT-293;Lo;0;L;;;;;N;;;;; 18925;TANGUT COMPONENT-294;Lo;0;L;;;;;N;;;;; 18926;TANGUT COMPONENT-295;Lo;0;L;;;;;N;;;;; 18927;TANGUT COMPONENT-296;Lo;0;L;;;;;N;;;;; 18928;TANGUT COMPONENT-297;Lo;0;L;;;;;N;;;;; 18929;TANGUT COMPONENT-298;Lo;0;L;;;;;N;;;;; 1892A;TANGUT COMPONENT-299;Lo;0;L;;;;;N;;;;; 1892B;TANGUT COMPONENT-300;Lo;0;L;;;;;N;;;;; 1892C;TANGUT COMPONENT-301;Lo;0;L;;;;;N;;;;; 1892D;TANGUT COMPONENT-302;Lo;0;L;;;;;N;;;;; 1892E;TANGUT COMPONENT-303;Lo;0;L;;;;;N;;;;; 1892F;TANGUT COMPONENT-304;Lo;0;L;;;;;N;;;;; 18930;TANGUT COMPONENT-305;Lo;0;L;;;;;N;;;;; 18931;TANGUT COMPONENT-306;Lo;0;L;;;;;N;;;;; 18932;TANGUT COMPONENT-307;Lo;0;L;;;;;N;;;;; 18933;TANGUT COMPONENT-308;Lo;0;L;;;;;N;;;;; 18934;TANGUT COMPONENT-309;Lo;0;L;;;;;N;;;;; 18935;TANGUT COMPONENT-310;Lo;0;L;;;;;N;;;;; 18936;TANGUT COMPONENT-311;Lo;0;L;;;;;N;;;;; 18937;TANGUT COMPONENT-312;Lo;0;L;;;;;N;;;;; 18938;TANGUT COMPONENT-313;Lo;0;L;;;;;N;;;;; 18939;TANGUT COMPONENT-314;Lo;0;L;;;;;N;;;;; 1893A;TANGUT COMPONENT-315;Lo;0;L;;;;;N;;;;; 1893B;TANGUT COMPONENT-316;Lo;0;L;;;;;N;;;;; 1893C;TANGUT COMPONENT-317;Lo;0;L;;;;;N;;;;; 1893D;TANGUT COMPONENT-318;Lo;0;L;;;;;N;;;;; 1893E;TANGUT COMPONENT-319;Lo;0;L;;;;;N;;;;; 1893F;TANGUT COMPONENT-320;Lo;0;L;;;;;N;;;;; 18940;TANGUT COMPONENT-321;Lo;0;L;;;;;N;;;;; 18941;TANGUT COMPONENT-322;Lo;0;L;;;;;N;;;;; 18942;TANGUT COMPONENT-323;Lo;0;L;;;;;N;;;;; 18943;TANGUT COMPONENT-324;Lo;0;L;;;;;N;;;;; 18944;TANGUT COMPONENT-325;Lo;0;L;;;;;N;;;;; 18945;TANGUT COMPONENT-326;Lo;0;L;;;;;N;;;;; 18946;TANGUT COMPONENT-327;Lo;0;L;;;;;N;;;;; 18947;TANGUT COMPONENT-328;Lo;0;L;;;;;N;;;;; 18948;TANGUT COMPONENT-329;Lo;0;L;;;;;N;;;;; 18949;TANGUT COMPONENT-330;Lo;0;L;;;;;N;;;;; 1894A;TANGUT COMPONENT-331;Lo;0;L;;;;;N;;;;; 1894B;TANGUT COMPONENT-332;Lo;0;L;;;;;N;;;;; 1894C;TANGUT COMPONENT-333;Lo;0;L;;;;;N;;;;; 1894D;TANGUT COMPONENT-334;Lo;0;L;;;;;N;;;;; 1894E;TANGUT COMPONENT-335;Lo;0;L;;;;;N;;;;; 1894F;TANGUT COMPONENT-336;Lo;0;L;;;;;N;;;;; 18950;TANGUT COMPONENT-337;Lo;0;L;;;;;N;;;;; 18951;TANGUT COMPONENT-338;Lo;0;L;;;;;N;;;;; 18952;TANGUT COMPONENT-339;Lo;0;L;;;;;N;;;;; 18953;TANGUT COMPONENT-340;Lo;0;L;;;;;N;;;;; 18954;TANGUT COMPONENT-341;Lo;0;L;;;;;N;;;;; 18955;TANGUT COMPONENT-342;Lo;0;L;;;;;N;;;;; 18956;TANGUT COMPONENT-343;Lo;0;L;;;;;N;;;;; 18957;TANGUT COMPONENT-344;Lo;0;L;;;;;N;;;;; 18958;TANGUT COMPONENT-345;Lo;0;L;;;;;N;;;;; 18959;TANGUT COMPONENT-346;Lo;0;L;;;;;N;;;;; 1895A;TANGUT COMPONENT-347;Lo;0;L;;;;;N;;;;; 1895B;TANGUT COMPONENT-348;Lo;0;L;;;;;N;;;;; 1895C;TANGUT COMPONENT-349;Lo;0;L;;;;;N;;;;; 1895D;TANGUT COMPONENT-350;Lo;0;L;;;;;N;;;;; 1895E;TANGUT COMPONENT-351;Lo;0;L;;;;;N;;;;; 1895F;TANGUT COMPONENT-352;Lo;0;L;;;;;N;;;;; 18960;TANGUT COMPONENT-353;Lo;0;L;;;;;N;;;;; 18961;TANGUT COMPONENT-354;Lo;0;L;;;;;N;;;;; 18962;TANGUT COMPONENT-355;Lo;0;L;;;;;N;;;;; 18963;TANGUT COMPONENT-356;Lo;0;L;;;;;N;;;;; 18964;TANGUT COMPONENT-357;Lo;0;L;;;;;N;;;;; 18965;TANGUT COMPONENT-358;Lo;0;L;;;;;N;;;;; 18966;TANGUT COMPONENT-359;Lo;0;L;;;;;N;;;;; 18967;TANGUT COMPONENT-360;Lo;0;L;;;;;N;;;;; 18968;TANGUT COMPONENT-361;Lo;0;L;;;;;N;;;;; 18969;TANGUT COMPONENT-362;Lo;0;L;;;;;N;;;;; 1896A;TANGUT COMPONENT-363;Lo;0;L;;;;;N;;;;; 1896B;TANGUT COMPONENT-364;Lo;0;L;;;;;N;;;;; 1896C;TANGUT COMPONENT-365;Lo;0;L;;;;;N;;;;; 1896D;TANGUT COMPONENT-366;Lo;0;L;;;;;N;;;;; 1896E;TANGUT COMPONENT-367;Lo;0;L;;;;;N;;;;; 1896F;TANGUT COMPONENT-368;Lo;0;L;;;;;N;;;;; 18970;TANGUT COMPONENT-369;Lo;0;L;;;;;N;;;;; 18971;TANGUT COMPONENT-370;Lo;0;L;;;;;N;;;;; 18972;TANGUT COMPONENT-371;Lo;0;L;;;;;N;;;;; 18973;TANGUT COMPONENT-372;Lo;0;L;;;;;N;;;;; 18974;TANGUT COMPONENT-373;Lo;0;L;;;;;N;;;;; 18975;TANGUT COMPONENT-374;Lo;0;L;;;;;N;;;;; 18976;TANGUT COMPONENT-375;Lo;0;L;;;;;N;;;;; 18977;TANGUT COMPONENT-376;Lo;0;L;;;;;N;;;;; 18978;TANGUT COMPONENT-377;Lo;0;L;;;;;N;;;;; 18979;TANGUT COMPONENT-378;Lo;0;L;;;;;N;;;;; 1897A;TANGUT COMPONENT-379;Lo;0;L;;;;;N;;;;; 1897B;TANGUT COMPONENT-380;Lo;0;L;;;;;N;;;;; 1897C;TANGUT COMPONENT-381;Lo;0;L;;;;;N;;;;; 1897D;TANGUT COMPONENT-382;Lo;0;L;;;;;N;;;;; 1897E;TANGUT COMPONENT-383;Lo;0;L;;;;;N;;;;; 1897F;TANGUT COMPONENT-384;Lo;0;L;;;;;N;;;;; 18980;TANGUT COMPONENT-385;Lo;0;L;;;;;N;;;;; 18981;TANGUT COMPONENT-386;Lo;0;L;;;;;N;;;;; 18982;TANGUT COMPONENT-387;Lo;0;L;;;;;N;;;;; 18983;TANGUT COMPONENT-388;Lo;0;L;;;;;N;;;;; 18984;TANGUT COMPONENT-389;Lo;0;L;;;;;N;;;;; 18985;TANGUT COMPONENT-390;Lo;0;L;;;;;N;;;;; 18986;TANGUT COMPONENT-391;Lo;0;L;;;;;N;;;;; 18987;TANGUT COMPONENT-392;Lo;0;L;;;;;N;;;;; 18988;TANGUT COMPONENT-393;Lo;0;L;;;;;N;;;;; 18989;TANGUT COMPONENT-394;Lo;0;L;;;;;N;;;;; 1898A;TANGUT COMPONENT-395;Lo;0;L;;;;;N;;;;; 1898B;TANGUT COMPONENT-396;Lo;0;L;;;;;N;;;;; 1898C;TANGUT COMPONENT-397;Lo;0;L;;;;;N;;;;; 1898D;TANGUT COMPONENT-398;Lo;0;L;;;;;N;;;;; 1898E;TANGUT COMPONENT-399;Lo;0;L;;;;;N;;;;; 1898F;TANGUT COMPONENT-400;Lo;0;L;;;;;N;;;;; 18990;TANGUT COMPONENT-401;Lo;0;L;;;;;N;;;;; 18991;TANGUT COMPONENT-402;Lo;0;L;;;;;N;;;;; 18992;TANGUT COMPONENT-403;Lo;0;L;;;;;N;;;;; 18993;TANGUT COMPONENT-404;Lo;0;L;;;;;N;;;;; 18994;TANGUT COMPONENT-405;Lo;0;L;;;;;N;;;;; 18995;TANGUT COMPONENT-406;Lo;0;L;;;;;N;;;;; 18996;TANGUT COMPONENT-407;Lo;0;L;;;;;N;;;;; 18997;TANGUT COMPONENT-408;Lo;0;L;;;;;N;;;;; 18998;TANGUT COMPONENT-409;Lo;0;L;;;;;N;;;;; 18999;TANGUT COMPONENT-410;Lo;0;L;;;;;N;;;;; 1899A;TANGUT COMPONENT-411;Lo;0;L;;;;;N;;;;; 1899B;TANGUT COMPONENT-412;Lo;0;L;;;;;N;;;;; 1899C;TANGUT COMPONENT-413;Lo;0;L;;;;;N;;;;; 1899D;TANGUT COMPONENT-414;Lo;0;L;;;;;N;;;;; 1899E;TANGUT COMPONENT-415;Lo;0;L;;;;;N;;;;; 1899F;TANGUT COMPONENT-416;Lo;0;L;;;;;N;;;;; 189A0;TANGUT COMPONENT-417;Lo;0;L;;;;;N;;;;; 189A1;TANGUT COMPONENT-418;Lo;0;L;;;;;N;;;;; 189A2;TANGUT COMPONENT-419;Lo;0;L;;;;;N;;;;; 189A3;TANGUT COMPONENT-420;Lo;0;L;;;;;N;;;;; 189A4;TANGUT COMPONENT-421;Lo;0;L;;;;;N;;;;; 189A5;TANGUT COMPONENT-422;Lo;0;L;;;;;N;;;;; 189A6;TANGUT COMPONENT-423;Lo;0;L;;;;;N;;;;; 189A7;TANGUT COMPONENT-424;Lo;0;L;;;;;N;;;;; 189A8;TANGUT COMPONENT-425;Lo;0;L;;;;;N;;;;; 189A9;TANGUT COMPONENT-426;Lo;0;L;;;;;N;;;;; 189AA;TANGUT COMPONENT-427;Lo;0;L;;;;;N;;;;; 189AB;TANGUT COMPONENT-428;Lo;0;L;;;;;N;;;;; 189AC;TANGUT COMPONENT-429;Lo;0;L;;;;;N;;;;; 189AD;TANGUT COMPONENT-430;Lo;0;L;;;;;N;;;;; 189AE;TANGUT COMPONENT-431;Lo;0;L;;;;;N;;;;; 189AF;TANGUT COMPONENT-432;Lo;0;L;;;;;N;;;;; 189B0;TANGUT COMPONENT-433;Lo;0;L;;;;;N;;;;; 189B1;TANGUT COMPONENT-434;Lo;0;L;;;;;N;;;;; 189B2;TANGUT COMPONENT-435;Lo;0;L;;;;;N;;;;; 189B3;TANGUT COMPONENT-436;Lo;0;L;;;;;N;;;;; 189B4;TANGUT COMPONENT-437;Lo;0;L;;;;;N;;;;; 189B5;TANGUT COMPONENT-438;Lo;0;L;;;;;N;;;;; 189B6;TANGUT COMPONENT-439;Lo;0;L;;;;;N;;;;; 189B7;TANGUT COMPONENT-440;Lo;0;L;;;;;N;;;;; 189B8;TANGUT COMPONENT-441;Lo;0;L;;;;;N;;;;; 189B9;TANGUT COMPONENT-442;Lo;0;L;;;;;N;;;;; 189BA;TANGUT COMPONENT-443;Lo;0;L;;;;;N;;;;; 189BB;TANGUT COMPONENT-444;Lo;0;L;;;;;N;;;;; 189BC;TANGUT COMPONENT-445;Lo;0;L;;;;;N;;;;; 189BD;TANGUT COMPONENT-446;Lo;0;L;;;;;N;;;;; 189BE;TANGUT COMPONENT-447;Lo;0;L;;;;;N;;;;; 189BF;TANGUT COMPONENT-448;Lo;0;L;;;;;N;;;;; 189C0;TANGUT COMPONENT-449;Lo;0;L;;;;;N;;;;; 189C1;TANGUT COMPONENT-450;Lo;0;L;;;;;N;;;;; 189C2;TANGUT COMPONENT-451;Lo;0;L;;;;;N;;;;; 189C3;TANGUT COMPONENT-452;Lo;0;L;;;;;N;;;;; 189C4;TANGUT COMPONENT-453;Lo;0;L;;;;;N;;;;; 189C5;TANGUT COMPONENT-454;Lo;0;L;;;;;N;;;;; 189C6;TANGUT COMPONENT-455;Lo;0;L;;;;;N;;;;; 189C7;TANGUT COMPONENT-456;Lo;0;L;;;;;N;;;;; 189C8;TANGUT COMPONENT-457;Lo;0;L;;;;;N;;;;; 189C9;TANGUT COMPONENT-458;Lo;0;L;;;;;N;;;;; 189CA;TANGUT COMPONENT-459;Lo;0;L;;;;;N;;;;; 189CB;TANGUT COMPONENT-460;Lo;0;L;;;;;N;;;;; 189CC;TANGUT COMPONENT-461;Lo;0;L;;;;;N;;;;; 189CD;TANGUT COMPONENT-462;Lo;0;L;;;;;N;;;;; 189CE;TANGUT COMPONENT-463;Lo;0;L;;;;;N;;;;; 189CF;TANGUT COMPONENT-464;Lo;0;L;;;;;N;;;;; 189D0;TANGUT COMPONENT-465;Lo;0;L;;;;;N;;;;; 189D1;TANGUT COMPONENT-466;Lo;0;L;;;;;N;;;;; 189D2;TANGUT COMPONENT-467;Lo;0;L;;;;;N;;;;; 189D3;TANGUT COMPONENT-468;Lo;0;L;;;;;N;;;;; 189D4;TANGUT COMPONENT-469;Lo;0;L;;;;;N;;;;; 189D5;TANGUT COMPONENT-470;Lo;0;L;;;;;N;;;;; 189D6;TANGUT COMPONENT-471;Lo;0;L;;;;;N;;;;; 189D7;TANGUT COMPONENT-472;Lo;0;L;;;;;N;;;;; 189D8;TANGUT COMPONENT-473;Lo;0;L;;;;;N;;;;; 189D9;TANGUT COMPONENT-474;Lo;0;L;;;;;N;;;;; 189DA;TANGUT COMPONENT-475;Lo;0;L;;;;;N;;;;; 189DB;TANGUT COMPONENT-476;Lo;0;L;;;;;N;;;;; 189DC;TANGUT COMPONENT-477;Lo;0;L;;;;;N;;;;; 189DD;TANGUT COMPONENT-478;Lo;0;L;;;;;N;;;;; 189DE;TANGUT COMPONENT-479;Lo;0;L;;;;;N;;;;; 189DF;TANGUT COMPONENT-480;Lo;0;L;;;;;N;;;;; 189E0;TANGUT COMPONENT-481;Lo;0;L;;;;;N;;;;; 189E1;TANGUT COMPONENT-482;Lo;0;L;;;;;N;;;;; 189E2;TANGUT COMPONENT-483;Lo;0;L;;;;;N;;;;; 189E3;TANGUT COMPONENT-484;Lo;0;L;;;;;N;;;;; 189E4;TANGUT COMPONENT-485;Lo;0;L;;;;;N;;;;; 189E5;TANGUT COMPONENT-486;Lo;0;L;;;;;N;;;;; 189E6;TANGUT COMPONENT-487;Lo;0;L;;;;;N;;;;; 189E7;TANGUT COMPONENT-488;Lo;0;L;;;;;N;;;;; 189E8;TANGUT COMPONENT-489;Lo;0;L;;;;;N;;;;; 189E9;TANGUT COMPONENT-490;Lo;0;L;;;;;N;;;;; 189EA;TANGUT COMPONENT-491;Lo;0;L;;;;;N;;;;; 189EB;TANGUT COMPONENT-492;Lo;0;L;;;;;N;;;;; 189EC;TANGUT COMPONENT-493;Lo;0;L;;;;;N;;;;; 189ED;TANGUT COMPONENT-494;Lo;0;L;;;;;N;;;;; 189EE;TANGUT COMPONENT-495;Lo;0;L;;;;;N;;;;; 189EF;TANGUT COMPONENT-496;Lo;0;L;;;;;N;;;;; 189F0;TANGUT COMPONENT-497;Lo;0;L;;;;;N;;;;; 189F1;TANGUT COMPONENT-498;Lo;0;L;;;;;N;;;;; 189F2;TANGUT COMPONENT-499;Lo;0;L;;;;;N;;;;; 189F3;TANGUT COMPONENT-500;Lo;0;L;;;;;N;;;;; 189F4;TANGUT COMPONENT-501;Lo;0;L;;;;;N;;;;; 189F5;TANGUT COMPONENT-502;Lo;0;L;;;;;N;;;;; 189F6;TANGUT COMPONENT-503;Lo;0;L;;;;;N;;;;; 189F7;TANGUT COMPONENT-504;Lo;0;L;;;;;N;;;;; 189F8;TANGUT COMPONENT-505;Lo;0;L;;;;;N;;;;; 189F9;TANGUT COMPONENT-506;Lo;0;L;;;;;N;;;;; 189FA;TANGUT COMPONENT-507;Lo;0;L;;;;;N;;;;; 189FB;TANGUT COMPONENT-508;Lo;0;L;;;;;N;;;;; 189FC;TANGUT COMPONENT-509;Lo;0;L;;;;;N;;;;; 189FD;TANGUT COMPONENT-510;Lo;0;L;;;;;N;;;;; 189FE;TANGUT COMPONENT-511;Lo;0;L;;;;;N;;;;; 189FF;TANGUT COMPONENT-512;Lo;0;L;;;;;N;;;;; 18A00;TANGUT COMPONENT-513;Lo;0;L;;;;;N;;;;; 18A01;TANGUT COMPONENT-514;Lo;0;L;;;;;N;;;;; 18A02;TANGUT COMPONENT-515;Lo;0;L;;;;;N;;;;; 18A03;TANGUT COMPONENT-516;Lo;0;L;;;;;N;;;;; 18A04;TANGUT COMPONENT-517;Lo;0;L;;;;;N;;;;; 18A05;TANGUT COMPONENT-518;Lo;0;L;;;;;N;;;;; 18A06;TANGUT COMPONENT-519;Lo;0;L;;;;;N;;;;; 18A07;TANGUT COMPONENT-520;Lo;0;L;;;;;N;;;;; 18A08;TANGUT COMPONENT-521;Lo;0;L;;;;;N;;;;; 18A09;TANGUT COMPONENT-522;Lo;0;L;;;;;N;;;;; 18A0A;TANGUT COMPONENT-523;Lo;0;L;;;;;N;;;;; 18A0B;TANGUT COMPONENT-524;Lo;0;L;;;;;N;;;;; 18A0C;TANGUT COMPONENT-525;Lo;0;L;;;;;N;;;;; 18A0D;TANGUT COMPONENT-526;Lo;0;L;;;;;N;;;;; 18A0E;TANGUT COMPONENT-527;Lo;0;L;;;;;N;;;;; 18A0F;TANGUT COMPONENT-528;Lo;0;L;;;;;N;;;;; 18A10;TANGUT COMPONENT-529;Lo;0;L;;;;;N;;;;; 18A11;TANGUT COMPONENT-530;Lo;0;L;;;;;N;;;;; 18A12;TANGUT COMPONENT-531;Lo;0;L;;;;;N;;;;; 18A13;TANGUT COMPONENT-532;Lo;0;L;;;;;N;;;;; 18A14;TANGUT COMPONENT-533;Lo;0;L;;;;;N;;;;; 18A15;TANGUT COMPONENT-534;Lo;0;L;;;;;N;;;;; 18A16;TANGUT COMPONENT-535;Lo;0;L;;;;;N;;;;; 18A17;TANGUT COMPONENT-536;Lo;0;L;;;;;N;;;;; 18A18;TANGUT COMPONENT-537;Lo;0;L;;;;;N;;;;; 18A19;TANGUT COMPONENT-538;Lo;0;L;;;;;N;;;;; 18A1A;TANGUT COMPONENT-539;Lo;0;L;;;;;N;;;;; 18A1B;TANGUT COMPONENT-540;Lo;0;L;;;;;N;;;;; 18A1C;TANGUT COMPONENT-541;Lo;0;L;;;;;N;;;;; 18A1D;TANGUT COMPONENT-542;Lo;0;L;;;;;N;;;;; 18A1E;TANGUT COMPONENT-543;Lo;0;L;;;;;N;;;;; 18A1F;TANGUT COMPONENT-544;Lo;0;L;;;;;N;;;;; 18A20;TANGUT COMPONENT-545;Lo;0;L;;;;;N;;;;; 18A21;TANGUT COMPONENT-546;Lo;0;L;;;;;N;;;;; 18A22;TANGUT COMPONENT-547;Lo;0;L;;;;;N;;;;; 18A23;TANGUT COMPONENT-548;Lo;0;L;;;;;N;;;;; 18A24;TANGUT COMPONENT-549;Lo;0;L;;;;;N;;;;; 18A25;TANGUT COMPONENT-550;Lo;0;L;;;;;N;;;;; 18A26;TANGUT COMPONENT-551;Lo;0;L;;;;;N;;;;; 18A27;TANGUT COMPONENT-552;Lo;0;L;;;;;N;;;;; 18A28;TANGUT COMPONENT-553;Lo;0;L;;;;;N;;;;; 18A29;TANGUT COMPONENT-554;Lo;0;L;;;;;N;;;;; 18A2A;TANGUT COMPONENT-555;Lo;0;L;;;;;N;;;;; 18A2B;TANGUT COMPONENT-556;Lo;0;L;;;;;N;;;;; 18A2C;TANGUT COMPONENT-557;Lo;0;L;;;;;N;;;;; 18A2D;TANGUT COMPONENT-558;Lo;0;L;;;;;N;;;;; 18A2E;TANGUT COMPONENT-559;Lo;0;L;;;;;N;;;;; 18A2F;TANGUT COMPONENT-560;Lo;0;L;;;;;N;;;;; 18A30;TANGUT COMPONENT-561;Lo;0;L;;;;;N;;;;; 18A31;TANGUT COMPONENT-562;Lo;0;L;;;;;N;;;;; 18A32;TANGUT COMPONENT-563;Lo;0;L;;;;;N;;;;; 18A33;TANGUT COMPONENT-564;Lo;0;L;;;;;N;;;;; 18A34;TANGUT COMPONENT-565;Lo;0;L;;;;;N;;;;; 18A35;TANGUT COMPONENT-566;Lo;0;L;;;;;N;;;;; 18A36;TANGUT COMPONENT-567;Lo;0;L;;;;;N;;;;; 18A37;TANGUT COMPONENT-568;Lo;0;L;;;;;N;;;;; 18A38;TANGUT COMPONENT-569;Lo;0;L;;;;;N;;;;; 18A39;TANGUT COMPONENT-570;Lo;0;L;;;;;N;;;;; 18A3A;TANGUT COMPONENT-571;Lo;0;L;;;;;N;;;;; 18A3B;TANGUT COMPONENT-572;Lo;0;L;;;;;N;;;;; 18A3C;TANGUT COMPONENT-573;Lo;0;L;;;;;N;;;;; 18A3D;TANGUT COMPONENT-574;Lo;0;L;;;;;N;;;;; 18A3E;TANGUT COMPONENT-575;Lo;0;L;;;;;N;;;;; 18A3F;TANGUT COMPONENT-576;Lo;0;L;;;;;N;;;;; 18A40;TANGUT COMPONENT-577;Lo;0;L;;;;;N;;;;; 18A41;TANGUT COMPONENT-578;Lo;0;L;;;;;N;;;;; 18A42;TANGUT COMPONENT-579;Lo;0;L;;;;;N;;;;; 18A43;TANGUT COMPONENT-580;Lo;0;L;;;;;N;;;;; 18A44;TANGUT COMPONENT-581;Lo;0;L;;;;;N;;;;; 18A45;TANGUT COMPONENT-582;Lo;0;L;;;;;N;;;;; 18A46;TANGUT COMPONENT-583;Lo;0;L;;;;;N;;;;; 18A47;TANGUT COMPONENT-584;Lo;0;L;;;;;N;;;;; 18A48;TANGUT COMPONENT-585;Lo;0;L;;;;;N;;;;; 18A49;TANGUT COMPONENT-586;Lo;0;L;;;;;N;;;;; 18A4A;TANGUT COMPONENT-587;Lo;0;L;;;;;N;;;;; 18A4B;TANGUT COMPONENT-588;Lo;0;L;;;;;N;;;;; 18A4C;TANGUT COMPONENT-589;Lo;0;L;;;;;N;;;;; 18A4D;TANGUT COMPONENT-590;Lo;0;L;;;;;N;;;;; 18A4E;TANGUT COMPONENT-591;Lo;0;L;;;;;N;;;;; 18A4F;TANGUT COMPONENT-592;Lo;0;L;;;;;N;;;;; 18A50;TANGUT COMPONENT-593;Lo;0;L;;;;;N;;;;; 18A51;TANGUT COMPONENT-594;Lo;0;L;;;;;N;;;;; 18A52;TANGUT COMPONENT-595;Lo;0;L;;;;;N;;;;; 18A53;TANGUT COMPONENT-596;Lo;0;L;;;;;N;;;;; 18A54;TANGUT COMPONENT-597;Lo;0;L;;;;;N;;;;; 18A55;TANGUT COMPONENT-598;Lo;0;L;;;;;N;;;;; 18A56;TANGUT COMPONENT-599;Lo;0;L;;;;;N;;;;; 18A57;TANGUT COMPONENT-600;Lo;0;L;;;;;N;;;;; 18A58;TANGUT COMPONENT-601;Lo;0;L;;;;;N;;;;; 18A59;TANGUT COMPONENT-602;Lo;0;L;;;;;N;;;;; 18A5A;TANGUT COMPONENT-603;Lo;0;L;;;;;N;;;;; 18A5B;TANGUT COMPONENT-604;Lo;0;L;;;;;N;;;;; 18A5C;TANGUT COMPONENT-605;Lo;0;L;;;;;N;;;;; 18A5D;TANGUT COMPONENT-606;Lo;0;L;;;;;N;;;;; 18A5E;TANGUT COMPONENT-607;Lo;0;L;;;;;N;;;;; 18A5F;TANGUT COMPONENT-608;Lo;0;L;;;;;N;;;;; 18A60;TANGUT COMPONENT-609;Lo;0;L;;;;;N;;;;; 18A61;TANGUT COMPONENT-610;Lo;0;L;;;;;N;;;;; 18A62;TANGUT COMPONENT-611;Lo;0;L;;;;;N;;;;; 18A63;TANGUT COMPONENT-612;Lo;0;L;;;;;N;;;;; 18A64;TANGUT COMPONENT-613;Lo;0;L;;;;;N;;;;; 18A65;TANGUT COMPONENT-614;Lo;0;L;;;;;N;;;;; 18A66;TANGUT COMPONENT-615;Lo;0;L;;;;;N;;;;; 18A67;TANGUT COMPONENT-616;Lo;0;L;;;;;N;;;;; 18A68;TANGUT COMPONENT-617;Lo;0;L;;;;;N;;;;; 18A69;TANGUT COMPONENT-618;Lo;0;L;;;;;N;;;;; 18A6A;TANGUT COMPONENT-619;Lo;0;L;;;;;N;;;;; 18A6B;TANGUT COMPONENT-620;Lo;0;L;;;;;N;;;;; 18A6C;TANGUT COMPONENT-621;Lo;0;L;;;;;N;;;;; 18A6D;TANGUT COMPONENT-622;Lo;0;L;;;;;N;;;;; 18A6E;TANGUT COMPONENT-623;Lo;0;L;;;;;N;;;;; 18A6F;TANGUT COMPONENT-624;Lo;0;L;;;;;N;;;;; 18A70;TANGUT COMPONENT-625;Lo;0;L;;;;;N;;;;; 18A71;TANGUT COMPONENT-626;Lo;0;L;;;;;N;;;;; 18A72;TANGUT COMPONENT-627;Lo;0;L;;;;;N;;;;; 18A73;TANGUT COMPONENT-628;Lo;0;L;;;;;N;;;;; 18A74;TANGUT COMPONENT-629;Lo;0;L;;;;;N;;;;; 18A75;TANGUT COMPONENT-630;Lo;0;L;;;;;N;;;;; 18A76;TANGUT COMPONENT-631;Lo;0;L;;;;;N;;;;; 18A77;TANGUT COMPONENT-632;Lo;0;L;;;;;N;;;;; 18A78;TANGUT COMPONENT-633;Lo;0;L;;;;;N;;;;; 18A79;TANGUT COMPONENT-634;Lo;0;L;;;;;N;;;;; 18A7A;TANGUT COMPONENT-635;Lo;0;L;;;;;N;;;;; 18A7B;TANGUT COMPONENT-636;Lo;0;L;;;;;N;;;;; 18A7C;TANGUT COMPONENT-637;Lo;0;L;;;;;N;;;;; 18A7D;TANGUT COMPONENT-638;Lo;0;L;;;;;N;;;;; 18A7E;TANGUT COMPONENT-639;Lo;0;L;;;;;N;;;;; 18A7F;TANGUT COMPONENT-640;Lo;0;L;;;;;N;;;;; 18A80;TANGUT COMPONENT-641;Lo;0;L;;;;;N;;;;; 18A81;TANGUT COMPONENT-642;Lo;0;L;;;;;N;;;;; 18A82;TANGUT COMPONENT-643;Lo;0;L;;;;;N;;;;; 18A83;TANGUT COMPONENT-644;Lo;0;L;;;;;N;;;;; 18A84;TANGUT COMPONENT-645;Lo;0;L;;;;;N;;;;; 18A85;TANGUT COMPONENT-646;Lo;0;L;;;;;N;;;;; 18A86;TANGUT COMPONENT-647;Lo;0;L;;;;;N;;;;; 18A87;TANGUT COMPONENT-648;Lo;0;L;;;;;N;;;;; 18A88;TANGUT COMPONENT-649;Lo;0;L;;;;;N;;;;; 18A89;TANGUT COMPONENT-650;Lo;0;L;;;;;N;;;;; 18A8A;TANGUT COMPONENT-651;Lo;0;L;;;;;N;;;;; 18A8B;TANGUT COMPONENT-652;Lo;0;L;;;;;N;;;;; 18A8C;TANGUT COMPONENT-653;Lo;0;L;;;;;N;;;;; 18A8D;TANGUT COMPONENT-654;Lo;0;L;;;;;N;;;;; 18A8E;TANGUT COMPONENT-655;Lo;0;L;;;;;N;;;;; 18A8F;TANGUT COMPONENT-656;Lo;0;L;;;;;N;;;;; 18A90;TANGUT COMPONENT-657;Lo;0;L;;;;;N;;;;; 18A91;TANGUT COMPONENT-658;Lo;0;L;;;;;N;;;;; 18A92;TANGUT COMPONENT-659;Lo;0;L;;;;;N;;;;; 18A93;TANGUT COMPONENT-660;Lo;0;L;;;;;N;;;;; 18A94;TANGUT COMPONENT-661;Lo;0;L;;;;;N;;;;; 18A95;TANGUT COMPONENT-662;Lo;0;L;;;;;N;;;;; 18A96;TANGUT COMPONENT-663;Lo;0;L;;;;;N;;;;; 18A97;TANGUT COMPONENT-664;Lo;0;L;;;;;N;;;;; 18A98;TANGUT COMPONENT-665;Lo;0;L;;;;;N;;;;; 18A99;TANGUT COMPONENT-666;Lo;0;L;;;;;N;;;;; 18A9A;TANGUT COMPONENT-667;Lo;0;L;;;;;N;;;;; 18A9B;TANGUT COMPONENT-668;Lo;0;L;;;;;N;;;;; 18A9C;TANGUT COMPONENT-669;Lo;0;L;;;;;N;;;;; 18A9D;TANGUT COMPONENT-670;Lo;0;L;;;;;N;;;;; 18A9E;TANGUT COMPONENT-671;Lo;0;L;;;;;N;;;;; 18A9F;TANGUT COMPONENT-672;Lo;0;L;;;;;N;;;;; 18AA0;TANGUT COMPONENT-673;Lo;0;L;;;;;N;;;;; 18AA1;TANGUT COMPONENT-674;Lo;0;L;;;;;N;;;;; 18AA2;TANGUT COMPONENT-675;Lo;0;L;;;;;N;;;;; 18AA3;TANGUT COMPONENT-676;Lo;0;L;;;;;N;;;;; 18AA4;TANGUT COMPONENT-677;Lo;0;L;;;;;N;;;;; 18AA5;TANGUT COMPONENT-678;Lo;0;L;;;;;N;;;;; 18AA6;TANGUT COMPONENT-679;Lo;0;L;;;;;N;;;;; 18AA7;TANGUT COMPONENT-680;Lo;0;L;;;;;N;;;;; 18AA8;TANGUT COMPONENT-681;Lo;0;L;;;;;N;;;;; 18AA9;TANGUT COMPONENT-682;Lo;0;L;;;;;N;;;;; 18AAA;TANGUT COMPONENT-683;Lo;0;L;;;;;N;;;;; 18AAB;TANGUT COMPONENT-684;Lo;0;L;;;;;N;;;;; 18AAC;TANGUT COMPONENT-685;Lo;0;L;;;;;N;;;;; 18AAD;TANGUT COMPONENT-686;Lo;0;L;;;;;N;;;;; 18AAE;TANGUT COMPONENT-687;Lo;0;L;;;;;N;;;;; 18AAF;TANGUT COMPONENT-688;Lo;0;L;;;;;N;;;;; 18AB0;TANGUT COMPONENT-689;Lo;0;L;;;;;N;;;;; 18AB1;TANGUT COMPONENT-690;Lo;0;L;;;;;N;;;;; 18AB2;TANGUT COMPONENT-691;Lo;0;L;;;;;N;;;;; 18AB3;TANGUT COMPONENT-692;Lo;0;L;;;;;N;;;;; 18AB4;TANGUT COMPONENT-693;Lo;0;L;;;;;N;;;;; 18AB5;TANGUT COMPONENT-694;Lo;0;L;;;;;N;;;;; 18AB6;TANGUT COMPONENT-695;Lo;0;L;;;;;N;;;;; 18AB7;TANGUT COMPONENT-696;Lo;0;L;;;;;N;;;;; 18AB8;TANGUT COMPONENT-697;Lo;0;L;;;;;N;;;;; 18AB9;TANGUT COMPONENT-698;Lo;0;L;;;;;N;;;;; 18ABA;TANGUT COMPONENT-699;Lo;0;L;;;;;N;;;;; 18ABB;TANGUT COMPONENT-700;Lo;0;L;;;;;N;;;;; 18ABC;TANGUT COMPONENT-701;Lo;0;L;;;;;N;;;;; 18ABD;TANGUT COMPONENT-702;Lo;0;L;;;;;N;;;;; 18ABE;TANGUT COMPONENT-703;Lo;0;L;;;;;N;;;;; 18ABF;TANGUT COMPONENT-704;Lo;0;L;;;;;N;;;;; 18AC0;TANGUT COMPONENT-705;Lo;0;L;;;;;N;;;;; 18AC1;TANGUT COMPONENT-706;Lo;0;L;;;;;N;;;;; 18AC2;TANGUT COMPONENT-707;Lo;0;L;;;;;N;;;;; 18AC3;TANGUT COMPONENT-708;Lo;0;L;;;;;N;;;;; 18AC4;TANGUT COMPONENT-709;Lo;0;L;;;;;N;;;;; 18AC5;TANGUT COMPONENT-710;Lo;0;L;;;;;N;;;;; 18AC6;TANGUT COMPONENT-711;Lo;0;L;;;;;N;;;;; 18AC7;TANGUT COMPONENT-712;Lo;0;L;;;;;N;;;;; 18AC8;TANGUT COMPONENT-713;Lo;0;L;;;;;N;;;;; 18AC9;TANGUT COMPONENT-714;Lo;0;L;;;;;N;;;;; 18ACA;TANGUT COMPONENT-715;Lo;0;L;;;;;N;;;;; 18ACB;TANGUT COMPONENT-716;Lo;0;L;;;;;N;;;;; 18ACC;TANGUT COMPONENT-717;Lo;0;L;;;;;N;;;;; 18ACD;TANGUT COMPONENT-718;Lo;0;L;;;;;N;;;;; 18ACE;TANGUT COMPONENT-719;Lo;0;L;;;;;N;;;;; 18ACF;TANGUT COMPONENT-720;Lo;0;L;;;;;N;;;;; 18AD0;TANGUT COMPONENT-721;Lo;0;L;;;;;N;;;;; 18AD1;TANGUT COMPONENT-722;Lo;0;L;;;;;N;;;;; 18AD2;TANGUT COMPONENT-723;Lo;0;L;;;;;N;;;;; 18AD3;TANGUT COMPONENT-724;Lo;0;L;;;;;N;;;;; 18AD4;TANGUT COMPONENT-725;Lo;0;L;;;;;N;;;;; 18AD5;TANGUT COMPONENT-726;Lo;0;L;;;;;N;;;;; 18AD6;TANGUT COMPONENT-727;Lo;0;L;;;;;N;;;;; 18AD7;TANGUT COMPONENT-728;Lo;0;L;;;;;N;;;;; 18AD8;TANGUT COMPONENT-729;Lo;0;L;;;;;N;;;;; 18AD9;TANGUT COMPONENT-730;Lo;0;L;;;;;N;;;;; 18ADA;TANGUT COMPONENT-731;Lo;0;L;;;;;N;;;;; 18ADB;TANGUT COMPONENT-732;Lo;0;L;;;;;N;;;;; 18ADC;TANGUT COMPONENT-733;Lo;0;L;;;;;N;;;;; 18ADD;TANGUT COMPONENT-734;Lo;0;L;;;;;N;;;;; 18ADE;TANGUT COMPONENT-735;Lo;0;L;;;;;N;;;;; 18ADF;TANGUT COMPONENT-736;Lo;0;L;;;;;N;;;;; 18AE0;TANGUT COMPONENT-737;Lo;0;L;;;;;N;;;;; 18AE1;TANGUT COMPONENT-738;Lo;0;L;;;;;N;;;;; 18AE2;TANGUT COMPONENT-739;Lo;0;L;;;;;N;;;;; 18AE3;TANGUT COMPONENT-740;Lo;0;L;;;;;N;;;;; 18AE4;TANGUT COMPONENT-741;Lo;0;L;;;;;N;;;;; 18AE5;TANGUT COMPONENT-742;Lo;0;L;;;;;N;;;;; 18AE6;TANGUT COMPONENT-743;Lo;0;L;;;;;N;;;;; 18AE7;TANGUT COMPONENT-744;Lo;0;L;;;;;N;;;;; 18AE8;TANGUT COMPONENT-745;Lo;0;L;;;;;N;;;;; 18AE9;TANGUT COMPONENT-746;Lo;0;L;;;;;N;;;;; 18AEA;TANGUT COMPONENT-747;Lo;0;L;;;;;N;;;;; 18AEB;TANGUT COMPONENT-748;Lo;0;L;;;;;N;;;;; 18AEC;TANGUT COMPONENT-749;Lo;0;L;;;;;N;;;;; 18AED;TANGUT COMPONENT-750;Lo;0;L;;;;;N;;;;; 18AEE;TANGUT COMPONENT-751;Lo;0;L;;;;;N;;;;; 18AEF;TANGUT COMPONENT-752;Lo;0;L;;;;;N;;;;; 18AF0;TANGUT COMPONENT-753;Lo;0;L;;;;;N;;;;; 18AF1;TANGUT COMPONENT-754;Lo;0;L;;;;;N;;;;; 18AF2;TANGUT COMPONENT-755;Lo;0;L;;;;;N;;;;; 1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;; 1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;; 1BC00;DUPLOYAN LETTER H;Lo;0;L;;;;;N;;;;; 1BC01;DUPLOYAN LETTER X;Lo;0;L;;;;;N;;;;; 1BC02;DUPLOYAN LETTER P;Lo;0;L;;;;;N;;;;; 1BC03;DUPLOYAN LETTER T;Lo;0;L;;;;;N;;;;; 1BC04;DUPLOYAN LETTER F;Lo;0;L;;;;;N;;;;; 1BC05;DUPLOYAN LETTER K;Lo;0;L;;;;;N;;;;; 1BC06;DUPLOYAN LETTER L;Lo;0;L;;;;;N;;;;; 1BC07;DUPLOYAN LETTER B;Lo;0;L;;;;;N;;;;; 1BC08;DUPLOYAN LETTER D;Lo;0;L;;;;;N;;;;; 1BC09;DUPLOYAN LETTER V;Lo;0;L;;;;;N;;;;; 1BC0A;DUPLOYAN LETTER G;Lo;0;L;;;;;N;;;;; 1BC0B;DUPLOYAN LETTER R;Lo;0;L;;;;;N;;;;; 1BC0C;DUPLOYAN LETTER P N;Lo;0;L;;;;;N;;;;; 1BC0D;DUPLOYAN LETTER D S;Lo;0;L;;;;;N;;;;; 1BC0E;DUPLOYAN LETTER F N;Lo;0;L;;;;;N;;;;; 1BC0F;DUPLOYAN LETTER K M;Lo;0;L;;;;;N;;;;; 1BC10;DUPLOYAN LETTER R S;Lo;0;L;;;;;N;;;;; 1BC11;DUPLOYAN LETTER TH;Lo;0;L;;;;;N;;;;; 1BC12;DUPLOYAN LETTER SLOAN DH;Lo;0;L;;;;;N;;;;; 1BC13;DUPLOYAN LETTER DH;Lo;0;L;;;;;N;;;;; 1BC14;DUPLOYAN LETTER KK;Lo;0;L;;;;;N;;;;; 1BC15;DUPLOYAN LETTER SLOAN J;Lo;0;L;;;;;N;;;;; 1BC16;DUPLOYAN LETTER HL;Lo;0;L;;;;;N;;;;; 1BC17;DUPLOYAN LETTER LH;Lo;0;L;;;;;N;;;;; 1BC18;DUPLOYAN LETTER RH;Lo;0;L;;;;;N;;;;; 1BC19;DUPLOYAN LETTER M;Lo;0;L;;;;;N;;;;; 1BC1A;DUPLOYAN LETTER N;Lo;0;L;;;;;N;;;;; 1BC1B;DUPLOYAN LETTER J;Lo;0;L;;;;;N;;;;; 1BC1C;DUPLOYAN LETTER S;Lo;0;L;;;;;N;;;;; 1BC1D;DUPLOYAN LETTER M N;Lo;0;L;;;;;N;;;;; 1BC1E;DUPLOYAN LETTER N M;Lo;0;L;;;;;N;;;;; 1BC1F;DUPLOYAN LETTER J M;Lo;0;L;;;;;N;;;;; 1BC20;DUPLOYAN LETTER S J;Lo;0;L;;;;;N;;;;; 1BC21;DUPLOYAN LETTER M WITH DOT;Lo;0;L;;;;;N;;;;; 1BC22;DUPLOYAN LETTER N WITH DOT;Lo;0;L;;;;;N;;;;; 1BC23;DUPLOYAN LETTER J WITH DOT;Lo;0;L;;;;;N;;;;; 1BC24;DUPLOYAN LETTER J WITH DOTS INSIDE AND ABOVE;Lo;0;L;;;;;N;;;;; 1BC25;DUPLOYAN LETTER S WITH DOT;Lo;0;L;;;;;N;;;;; 1BC26;DUPLOYAN LETTER S WITH DOT BELOW;Lo;0;L;;;;;N;;;;; 1BC27;DUPLOYAN LETTER M S;Lo;0;L;;;;;N;;;;; 1BC28;DUPLOYAN LETTER N S;Lo;0;L;;;;;N;;;;; 1BC29;DUPLOYAN LETTER J S;Lo;0;L;;;;;N;;;;; 1BC2A;DUPLOYAN LETTER S S;Lo;0;L;;;;;N;;;;; 1BC2B;DUPLOYAN LETTER M N S;Lo;0;L;;;;;N;;;;; 1BC2C;DUPLOYAN LETTER N M S;Lo;0;L;;;;;N;;;;; 1BC2D;DUPLOYAN LETTER J M S;Lo;0;L;;;;;N;;;;; 1BC2E;DUPLOYAN LETTER S J S;Lo;0;L;;;;;N;;;;; 1BC2F;DUPLOYAN LETTER J S WITH DOT;Lo;0;L;;;;;N;;;;; 1BC30;DUPLOYAN LETTER J N;Lo;0;L;;;;;N;;;;; 1BC31;DUPLOYAN LETTER J N S;Lo;0;L;;;;;N;;;;; 1BC32;DUPLOYAN LETTER S T;Lo;0;L;;;;;N;;;;; 1BC33;DUPLOYAN LETTER S T R;Lo;0;L;;;;;N;;;;; 1BC34;DUPLOYAN LETTER S P;Lo;0;L;;;;;N;;;;; 1BC35;DUPLOYAN LETTER S P R;Lo;0;L;;;;;N;;;;; 1BC36;DUPLOYAN LETTER T S;Lo;0;L;;;;;N;;;;; 1BC37;DUPLOYAN LETTER T R S;Lo;0;L;;;;;N;;;;; 1BC38;DUPLOYAN LETTER W;Lo;0;L;;;;;N;;;;; 1BC39;DUPLOYAN LETTER WH;Lo;0;L;;;;;N;;;;; 1BC3A;DUPLOYAN LETTER W R;Lo;0;L;;;;;N;;;;; 1BC3B;DUPLOYAN LETTER S N;Lo;0;L;;;;;N;;;;; 1BC3C;DUPLOYAN LETTER S M;Lo;0;L;;;;;N;;;;; 1BC3D;DUPLOYAN LETTER K R S;Lo;0;L;;;;;N;;;;; 1BC3E;DUPLOYAN LETTER G R S;Lo;0;L;;;;;N;;;;; 1BC3F;DUPLOYAN LETTER S K;Lo;0;L;;;;;N;;;;; 1BC40;DUPLOYAN LETTER S K R;Lo;0;L;;;;;N;;;;; 1BC41;DUPLOYAN LETTER A;Lo;0;L;;;;;N;;;;; 1BC42;DUPLOYAN LETTER SLOAN OW;Lo;0;L;;;;;N;;;;; 1BC43;DUPLOYAN LETTER OA;Lo;0;L;;;;;N;;;;; 1BC44;DUPLOYAN LETTER O;Lo;0;L;;;;;N;;;;; 1BC45;DUPLOYAN LETTER AOU;Lo;0;L;;;;;N;;;;; 1BC46;DUPLOYAN LETTER I;Lo;0;L;;;;;N;;;;; 1BC47;DUPLOYAN LETTER E;Lo;0;L;;;;;N;;;;; 1BC48;DUPLOYAN LETTER IE;Lo;0;L;;;;;N;;;;; 1BC49;DUPLOYAN LETTER SHORT I;Lo;0;L;;;;;N;;;;; 1BC4A;DUPLOYAN LETTER UI;Lo;0;L;;;;;N;;;;; 1BC4B;DUPLOYAN LETTER EE;Lo;0;L;;;;;N;;;;; 1BC4C;DUPLOYAN LETTER SLOAN EH;Lo;0;L;;;;;N;;;;; 1BC4D;DUPLOYAN LETTER ROMANIAN I;Lo;0;L;;;;;N;;;;; 1BC4E;DUPLOYAN LETTER SLOAN EE;Lo;0;L;;;;;N;;;;; 1BC4F;DUPLOYAN LETTER LONG I;Lo;0;L;;;;;N;;;;; 1BC50;DUPLOYAN LETTER YE;Lo;0;L;;;;;N;;;;; 1BC51;DUPLOYAN LETTER U;Lo;0;L;;;;;N;;;;; 1BC52;DUPLOYAN LETTER EU;Lo;0;L;;;;;N;;;;; 1BC53;DUPLOYAN LETTER XW;Lo;0;L;;;;;N;;;;; 1BC54;DUPLOYAN LETTER U N;Lo;0;L;;;;;N;;;;; 1BC55;DUPLOYAN LETTER LONG U;Lo;0;L;;;;;N;;;;; 1BC56;DUPLOYAN LETTER ROMANIAN U;Lo;0;L;;;;;N;;;;; 1BC57;DUPLOYAN LETTER UH;Lo;0;L;;;;;N;;;;; 1BC58;DUPLOYAN LETTER SLOAN U;Lo;0;L;;;;;N;;;;; 1BC59;DUPLOYAN LETTER OOH;Lo;0;L;;;;;N;;;;; 1BC5A;DUPLOYAN LETTER OW;Lo;0;L;;;;;N;;;;; 1BC5B;DUPLOYAN LETTER OU;Lo;0;L;;;;;N;;;;; 1BC5C;DUPLOYAN LETTER WA;Lo;0;L;;;;;N;;;;; 1BC5D;DUPLOYAN LETTER WO;Lo;0;L;;;;;N;;;;; 1BC5E;DUPLOYAN LETTER WI;Lo;0;L;;;;;N;;;;; 1BC5F;DUPLOYAN LETTER WEI;Lo;0;L;;;;;N;;;;; 1BC60;DUPLOYAN LETTER WOW;Lo;0;L;;;;;N;;;;; 1BC61;DUPLOYAN LETTER NASAL U;Lo;0;L;;;;;N;;;;; 1BC62;DUPLOYAN LETTER NASAL O;Lo;0;L;;;;;N;;;;; 1BC63;DUPLOYAN LETTER NASAL I;Lo;0;L;;;;;N;;;;; 1BC64;DUPLOYAN LETTER NASAL A;Lo;0;L;;;;;N;;;;; 1BC65;DUPLOYAN LETTER PERNIN AN;Lo;0;L;;;;;N;;;;; 1BC66;DUPLOYAN LETTER PERNIN AM;Lo;0;L;;;;;N;;;;; 1BC67;DUPLOYAN LETTER SLOAN EN;Lo;0;L;;;;;N;;;;; 1BC68;DUPLOYAN LETTER SLOAN AN;Lo;0;L;;;;;N;;;;; 1BC69;DUPLOYAN LETTER SLOAN ON;Lo;0;L;;;;;N;;;;; 1BC6A;DUPLOYAN LETTER VOCALIC M;Lo;0;L;;;;;N;;;;; 1BC70;DUPLOYAN AFFIX LEFT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; 1BC71;DUPLOYAN AFFIX MID HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; 1BC72;DUPLOYAN AFFIX RIGHT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; 1BC73;DUPLOYAN AFFIX LOW VERTICAL SECANT;Lo;0;L;;;;;N;;;;; 1BC74;DUPLOYAN AFFIX MID VERTICAL SECANT;Lo;0;L;;;;;N;;;;; 1BC75;DUPLOYAN AFFIX HIGH VERTICAL SECANT;Lo;0;L;;;;;N;;;;; 1BC76;DUPLOYAN AFFIX ATTACHED SECANT;Lo;0;L;;;;;N;;;;; 1BC77;DUPLOYAN AFFIX ATTACHED LEFT-TO-RIGHT SECANT;Lo;0;L;;;;;N;;;;; 1BC78;DUPLOYAN AFFIX ATTACHED TANGENT;Lo;0;L;;;;;N;;;;; 1BC79;DUPLOYAN AFFIX ATTACHED TAIL;Lo;0;L;;;;;N;;;;; 1BC7A;DUPLOYAN AFFIX ATTACHED E HOOK;Lo;0;L;;;;;N;;;;; 1BC7B;DUPLOYAN AFFIX ATTACHED I HOOK;Lo;0;L;;;;;N;;;;; 1BC7C;DUPLOYAN AFFIX ATTACHED TANGENT HOOK;Lo;0;L;;;;;N;;;;; 1BC80;DUPLOYAN AFFIX HIGH ACUTE;Lo;0;L;;;;;N;;;;; 1BC81;DUPLOYAN AFFIX HIGH TIGHT ACUTE;Lo;0;L;;;;;N;;;;; 1BC82;DUPLOYAN AFFIX HIGH GRAVE;Lo;0;L;;;;;N;;;;; 1BC83;DUPLOYAN AFFIX HIGH LONG GRAVE;Lo;0;L;;;;;N;;;;; 1BC84;DUPLOYAN AFFIX HIGH DOT;Lo;0;L;;;;;N;;;;; 1BC85;DUPLOYAN AFFIX HIGH CIRCLE;Lo;0;L;;;;;N;;;;; 1BC86;DUPLOYAN AFFIX HIGH LINE;Lo;0;L;;;;;N;;;;; 1BC87;DUPLOYAN AFFIX HIGH WAVE;Lo;0;L;;;;;N;;;;; 1BC88;DUPLOYAN AFFIX HIGH VERTICAL;Lo;0;L;;;;;N;;;;; 1BC90;DUPLOYAN AFFIX LOW ACUTE;Lo;0;L;;;;;N;;;;; 1BC91;DUPLOYAN AFFIX LOW TIGHT ACUTE;Lo;0;L;;;;;N;;;;; 1BC92;DUPLOYAN AFFIX LOW GRAVE;Lo;0;L;;;;;N;;;;; 1BC93;DUPLOYAN AFFIX LOW LONG GRAVE;Lo;0;L;;;;;N;;;;; 1BC94;DUPLOYAN AFFIX LOW DOT;Lo;0;L;;;;;N;;;;; 1BC95;DUPLOYAN AFFIX LOW CIRCLE;Lo;0;L;;;;;N;;;;; 1BC96;DUPLOYAN AFFIX LOW LINE;Lo;0;L;;;;;N;;;;; 1BC97;DUPLOYAN AFFIX LOW WAVE;Lo;0;L;;;;;N;;;;; 1BC98;DUPLOYAN AFFIX LOW VERTICAL;Lo;0;L;;;;;N;;;;; 1BC99;DUPLOYAN AFFIX LOW ARROW;Lo;0;L;;;;;N;;;;; 1BC9C;DUPLOYAN SIGN O WITH CROSS;So;0;L;;;;;N;;;;; 1BC9D;DUPLOYAN THICK LETTER SELECTOR;Mn;0;NSM;;;;;N;;;;; 1BC9E;DUPLOYAN DOUBLE MARK;Mn;1;NSM;;;;;N;;;;; 1BC9F;DUPLOYAN PUNCTUATION CHINOOK FULL STOP;Po;0;L;;;;;N;;;;; 1BCA0;SHORTHAND FORMAT LETTER OVERLAP;Cf;0;BN;;;;;N;;;;; 1BCA1;SHORTHAND FORMAT CONTINUING OVERLAP;Cf;0;BN;;;;;N;;;;; 1BCA2;SHORTHAND FORMAT DOWN STEP;Cf;0;BN;;;;;N;;;;; 1BCA3;SHORTHAND FORMAT UP STEP;Cf;0;BN;;;;;N;;;;; 1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;; 1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;; 1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;; 1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;; 1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;; 1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;; 1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;; 1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;; 1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;; 1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;; 1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;; 1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;; 1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;; 1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;; 1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;; 1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;; 1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;; 1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;; 1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;; 1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;; 1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;; 1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;; 1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;; 1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;; 1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;; 1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;; 1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;; 1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;; 1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;; 1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;; 1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;; 1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;; 1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;; 1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;; 1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;; 1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;; 1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;; 1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;; 1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;; 1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;; 1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;; 1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;; 1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;; 1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;; 1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;; 1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;; 1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;; 1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;; 1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;; 1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;; 1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;; 1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;; 1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;; 1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;; 1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;; 1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;; 1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;; 1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;; 1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;; 1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;; 1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;; 1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;; 1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;; 1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;; 1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;; 1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;; 1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;; 1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;; 1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;; 1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;; 1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;; 1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;; 1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;; 1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;; 1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;; 1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;; 1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;; 1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;; 1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;; 1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;; 1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;; 1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;; 1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;; 1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;; 1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;; 1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;; 1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;; 1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;; 1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;; 1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;; 1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;; 1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;; 1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;; 1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;; 1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;; 1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;; 1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;; 1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;; 1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;; 1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;; 1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;; 1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;; 1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;; 1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;; 1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;; 1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;; 1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;; 1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;; 1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;; 1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;; 1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;; 1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;; 1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;; 1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;; 1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;; 1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;; 1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;; 1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;; 1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;; 1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;; 1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;; 1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;; 1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;; 1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;; 1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;; 1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;; 1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;; 1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;; 1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;; 1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;; 1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;; 1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;; 1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;; 1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;; 1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;; 1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;; 1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;; 1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;; 1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;; 1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;; 1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;; 1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;; 1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;; 1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;; 1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;; 1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; 1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;; 1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;; 1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;; 1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; 1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;; 1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;; 1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;; 1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;; 1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;; 1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;; 1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;; 1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;; 1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;; 1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;; 1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;; 1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;; 1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;; 1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;; 1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;; 1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;; 1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;; 1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;; 1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;; 1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;; 1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;; 1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;; 1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; 1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; 1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;; 1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;; 1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;; 1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;; 1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;; 1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;; 1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;; 1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;; 1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;; 1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;; 1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;; 1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;; 1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;; 1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;; 1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;; 1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;; 1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;; 1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;; 1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;; 1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;; 1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;; 1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;; 1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;; 1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;; 1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;; 1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;; 1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;; 1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;; 1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;; 1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;; 1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;; 1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;; 1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;; 1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;; 1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; 1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; 1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; 1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; 1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; 1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; 1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; 1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; 1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;; 1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;; 1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;; 1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;; 1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;; 1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;; 1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;; 1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;; 1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;; 1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;; 1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;; 1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;; 1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;; 1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;; 1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;; 1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;; 1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;; 1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;; 1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;; 1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;; 1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;; 1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;; 1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;; 1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;; 1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;; 1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;; 1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;; 1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;; 1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;; 1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;; 1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;; 1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;; 1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;; 1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;; 1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;; 1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;; 1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;; 1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;; 1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;; 1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;; 1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;; 1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;; 1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;; 1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;; 1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;; 1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;; 1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;; 1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;; 1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;; 1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;; 1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;; 1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;; 1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;; 1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;; 1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;; 1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;; 1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;; 1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;; 1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;; 1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;; 1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;; 1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; 1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; 1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;; 1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;; 1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; 1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; 1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;; 1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;; 1D129;MUSICAL SYMBOL MULTIPLE MEASURE REST;So;0;L;;;;;N;;;;; 1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;; 1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;; 1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;; 1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;; 1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;; 1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;; 1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;; 1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;; 1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;; 1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;; 1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;; 1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;; 1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;; 1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;; 1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;; 1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;; 1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;; 1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;; 1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;; 1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;; 1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;; 1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;; 1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;; 1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;; 1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;; 1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;; 1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;; 1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;; 1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;; 1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;; 1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;; 1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;; 1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;; 1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;; 1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;; 1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;; 1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; 1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; 1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;; 1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;; 1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;; 1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;; 1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; 1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; 1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;; 1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;; 1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;; 1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;; 1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;; 1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;; 1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;; 1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;; 1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;; 1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;; 1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;; 1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;; 1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;; 1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;; 1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;; 1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;; 1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;; 1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;; 1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;; 1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;; 1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;; 1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;; 1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;; 1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;; 1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;; 1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;; 1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;; 1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;; 1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;; 1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;; 1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;; 1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;; 1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;; 1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;; 1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;; 1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;; 1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;; 1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;; 1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;; 1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;; 1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;; 1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;; 1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;; 1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;; 1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;; 1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;; 1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;; 1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;; 1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;; 1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;; 1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;; 1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;; 1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;; 1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;; 1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;; 1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;; 1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;; 1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;; 1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;; 1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;; 1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;; 1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;; 1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;; 1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;; 1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;; 1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;; 1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;; 1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;; 1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;; 1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;; 1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;; 1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;; 1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;; 1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;; 1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;; 1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;; 1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;; 1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;; 1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;; 1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;; 1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;; 1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;; 1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;; 1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;; 1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;; 1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;; 1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;; 1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;; 1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;; 1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;; 1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;; 1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;; 1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;; 1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;; 1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;; 1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;; 1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;; 1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;; 1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;; 1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;; 1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;; 1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;; 1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;; 1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;; 1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;; 1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;; 1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;; 1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;; 1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;; 1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;; 1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;; 1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;; 1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;; 1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; 1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; 1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; 1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; 1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; 1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; 1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;; 1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;; 1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;; 1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;; 1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;; 1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;; 1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;; 1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;; 1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;; 1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;; 1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;; 1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;; 1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;; 1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;; 1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;; 1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;; 1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;; 1D1DE;MUSICAL SYMBOL KIEVAN C CLEF;So;0;L;;;;;N;;;;; 1D1DF;MUSICAL SYMBOL KIEVAN END OF PIECE;So;0;L;;;;;N;;;;; 1D1E0;MUSICAL SYMBOL KIEVAN FINAL NOTE;So;0;L;;;;;N;;;;; 1D1E1;MUSICAL SYMBOL KIEVAN RECITATIVE MARK;So;0;L;;;;;N;;;;; 1D1E2;MUSICAL SYMBOL KIEVAN WHOLE NOTE;So;0;L;;;;;N;;;;; 1D1E3;MUSICAL SYMBOL KIEVAN HALF NOTE;So;0;L;;;;;N;;;;; 1D1E4;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM DOWN;So;0;L;;;;;N;;;;; 1D1E5;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM UP;So;0;L;;;;;N;;;;; 1D1E6;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM DOWN;So;0;L;;;;;N;;;;; 1D1E7;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM UP;So;0;L;;;;;N;;;;; 1D1E8;MUSICAL SYMBOL KIEVAN FLAT SIGN;So;0;L;;;;;N;;;;; 1D200;GREEK VOCAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;; 1D201;GREEK VOCAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;; 1D202;GREEK VOCAL NOTATION SYMBOL-3;So;0;ON;;;;;N;;;;; 1D203;GREEK VOCAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;; 1D204;GREEK VOCAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;; 1D205;GREEK VOCAL NOTATION SYMBOL-6;So;0;ON;;;;;N;;;;; 1D206;GREEK VOCAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;; 1D207;GREEK VOCAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;; 1D208;GREEK VOCAL NOTATION SYMBOL-9;So;0;ON;;;;;N;;;;; 1D209;GREEK VOCAL NOTATION SYMBOL-10;So;0;ON;;;;;N;;;;; 1D20A;GREEK VOCAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;; 1D20B;GREEK VOCAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;; 1D20C;GREEK VOCAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;; 1D20D;GREEK VOCAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;; 1D20E;GREEK VOCAL NOTATION SYMBOL-15;So;0;ON;;;;;N;;;;; 1D20F;GREEK VOCAL NOTATION SYMBOL-16;So;0;ON;;;;;N;;;;; 1D210;GREEK VOCAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;; 1D211;GREEK VOCAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;; 1D212;GREEK VOCAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;; 1D213;GREEK VOCAL NOTATION SYMBOL-20;So;0;ON;;;;;N;;;;; 1D214;GREEK VOCAL NOTATION SYMBOL-21;So;0;ON;;;;;N;;;;; 1D215;GREEK VOCAL NOTATION SYMBOL-22;So;0;ON;;;;;N;;;;; 1D216;GREEK VOCAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;; 1D217;GREEK VOCAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;; 1D218;GREEK VOCAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;; 1D219;GREEK VOCAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;; 1D21A;GREEK VOCAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;; 1D21B;GREEK VOCAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;; 1D21C;GREEK VOCAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;; 1D21D;GREEK INSTRUMENTAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;; 1D21E;GREEK INSTRUMENTAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;; 1D21F;GREEK INSTRUMENTAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;; 1D220;GREEK INSTRUMENTAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;; 1D221;GREEK INSTRUMENTAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;; 1D222;GREEK INSTRUMENTAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;; 1D223;GREEK INSTRUMENTAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;; 1D224;GREEK INSTRUMENTAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;; 1D225;GREEK INSTRUMENTAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;; 1D226;GREEK INSTRUMENTAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;; 1D227;GREEK INSTRUMENTAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;; 1D228;GREEK INSTRUMENTAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;; 1D229;GREEK INSTRUMENTAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;; 1D22A;GREEK INSTRUMENTAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;; 1D22B;GREEK INSTRUMENTAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;; 1D22C;GREEK INSTRUMENTAL NOTATION SYMBOL-25;So;0;ON;;;;;N;;;;; 1D22D;GREEK INSTRUMENTAL NOTATION SYMBOL-26;So;0;ON;;;;;N;;;;; 1D22E;GREEK INSTRUMENTAL NOTATION SYMBOL-27;So;0;ON;;;;;N;;;;; 1D22F;GREEK INSTRUMENTAL NOTATION SYMBOL-29;So;0;ON;;;;;N;;;;; 1D230;GREEK INSTRUMENTAL NOTATION SYMBOL-30;So;0;ON;;;;;N;;;;; 1D231;GREEK INSTRUMENTAL NOTATION SYMBOL-32;So;0;ON;;;;;N;;;;; 1D232;GREEK INSTRUMENTAL NOTATION SYMBOL-36;So;0;ON;;;;;N;;;;; 1D233;GREEK INSTRUMENTAL NOTATION SYMBOL-37;So;0;ON;;;;;N;;;;; 1D234;GREEK INSTRUMENTAL NOTATION SYMBOL-38;So;0;ON;;;;;N;;;;; 1D235;GREEK INSTRUMENTAL NOTATION SYMBOL-39;So;0;ON;;;;;N;;;;; 1D236;GREEK INSTRUMENTAL NOTATION SYMBOL-40;So;0;ON;;;;;N;;;;; 1D237;GREEK INSTRUMENTAL NOTATION SYMBOL-42;So;0;ON;;;;;N;;;;; 1D238;GREEK INSTRUMENTAL NOTATION SYMBOL-43;So;0;ON;;;;;N;;;;; 1D239;GREEK INSTRUMENTAL NOTATION SYMBOL-45;So;0;ON;;;;;N;;;;; 1D23A;GREEK INSTRUMENTAL NOTATION SYMBOL-47;So;0;ON;;;;;N;;;;; 1D23B;GREEK INSTRUMENTAL NOTATION SYMBOL-48;So;0;ON;;;;;N;;;;; 1D23C;GREEK INSTRUMENTAL NOTATION SYMBOL-49;So;0;ON;;;;;N;;;;; 1D23D;GREEK INSTRUMENTAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;; 1D23E;GREEK INSTRUMENTAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;; 1D23F;GREEK INSTRUMENTAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;; 1D240;GREEK INSTRUMENTAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;; 1D241;GREEK INSTRUMENTAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;; 1D242;COMBINING GREEK MUSICAL TRISEME;Mn;230;NSM;;;;;N;;;;; 1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;; 1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;; 1D245;GREEK MUSICAL LEIMMA;So;0;ON;;;;;N;;;;; 1D300;MONOGRAM FOR EARTH;So;0;ON;;;;;N;;;;; 1D301;DIGRAM FOR HEAVENLY EARTH;So;0;ON;;;;;N;;;;; 1D302;DIGRAM FOR HUMAN EARTH;So;0;ON;;;;;N;;;;; 1D303;DIGRAM FOR EARTHLY HEAVEN;So;0;ON;;;;;N;;;;; 1D304;DIGRAM FOR EARTHLY HUMAN;So;0;ON;;;;;N;;;;; 1D305;DIGRAM FOR EARTH;So;0;ON;;;;;N;;;;; 1D306;TETRAGRAM FOR CENTRE;So;0;ON;;;;;N;;;;; 1D307;TETRAGRAM FOR FULL CIRCLE;So;0;ON;;;;;N;;;;; 1D308;TETRAGRAM FOR MIRED;So;0;ON;;;;;N;;;;; 1D309;TETRAGRAM FOR BARRIER;So;0;ON;;;;;N;;;;; 1D30A;TETRAGRAM FOR KEEPING SMALL;So;0;ON;;;;;N;;;;; 1D30B;TETRAGRAM FOR CONTRARIETY;So;0;ON;;;;;N;;;;; 1D30C;TETRAGRAM FOR ASCENT;So;0;ON;;;;;N;;;;; 1D30D;TETRAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;; 1D30E;TETRAGRAM FOR BRANCHING OUT;So;0;ON;;;;;N;;;;; 1D30F;TETRAGRAM FOR DEFECTIVENESS OR DISTORTION;So;0;ON;;;;;N;;;;; 1D310;TETRAGRAM FOR DIVERGENCE;So;0;ON;;;;;N;;;;; 1D311;TETRAGRAM FOR YOUTHFULNESS;So;0;ON;;;;;N;;;;; 1D312;TETRAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;; 1D313;TETRAGRAM FOR PENETRATION;So;0;ON;;;;;N;;;;; 1D314;TETRAGRAM FOR REACH;So;0;ON;;;;;N;;;;; 1D315;TETRAGRAM FOR CONTACT;So;0;ON;;;;;N;;;;; 1D316;TETRAGRAM FOR HOLDING BACK;So;0;ON;;;;;N;;;;; 1D317;TETRAGRAM FOR WAITING;So;0;ON;;;;;N;;;;; 1D318;TETRAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;; 1D319;TETRAGRAM FOR ADVANCE;So;0;ON;;;;;N;;;;; 1D31A;TETRAGRAM FOR RELEASE;So;0;ON;;;;;N;;;;; 1D31B;TETRAGRAM FOR RESISTANCE;So;0;ON;;;;;N;;;;; 1D31C;TETRAGRAM FOR EASE;So;0;ON;;;;;N;;;;; 1D31D;TETRAGRAM FOR JOY;So;0;ON;;;;;N;;;;; 1D31E;TETRAGRAM FOR CONTENTION;So;0;ON;;;;;N;;;;; 1D31F;TETRAGRAM FOR ENDEAVOUR;So;0;ON;;;;;N;;;;; 1D320;TETRAGRAM FOR DUTIES;So;0;ON;;;;;N;;;;; 1D321;TETRAGRAM FOR CHANGE;So;0;ON;;;;;N;;;;; 1D322;TETRAGRAM FOR DECISIVENESS;So;0;ON;;;;;N;;;;; 1D323;TETRAGRAM FOR BOLD RESOLUTION;So;0;ON;;;;;N;;;;; 1D324;TETRAGRAM FOR PACKING;So;0;ON;;;;;N;;;;; 1D325;TETRAGRAM FOR LEGION;So;0;ON;;;;;N;;;;; 1D326;TETRAGRAM FOR CLOSENESS;So;0;ON;;;;;N;;;;; 1D327;TETRAGRAM FOR KINSHIP;So;0;ON;;;;;N;;;;; 1D328;TETRAGRAM FOR GATHERING;So;0;ON;;;;;N;;;;; 1D329;TETRAGRAM FOR STRENGTH;So;0;ON;;;;;N;;;;; 1D32A;TETRAGRAM FOR PURITY;So;0;ON;;;;;N;;;;; 1D32B;TETRAGRAM FOR FULLNESS;So;0;ON;;;;;N;;;;; 1D32C;TETRAGRAM FOR RESIDENCE;So;0;ON;;;;;N;;;;; 1D32D;TETRAGRAM FOR LAW OR MODEL;So;0;ON;;;;;N;;;;; 1D32E;TETRAGRAM FOR RESPONSE;So;0;ON;;;;;N;;;;; 1D32F;TETRAGRAM FOR GOING TO MEET;So;0;ON;;;;;N;;;;; 1D330;TETRAGRAM FOR ENCOUNTERS;So;0;ON;;;;;N;;;;; 1D331;TETRAGRAM FOR STOVE;So;0;ON;;;;;N;;;;; 1D332;TETRAGRAM FOR GREATNESS;So;0;ON;;;;;N;;;;; 1D333;TETRAGRAM FOR ENLARGEMENT;So;0;ON;;;;;N;;;;; 1D334;TETRAGRAM FOR PATTERN;So;0;ON;;;;;N;;;;; 1D335;TETRAGRAM FOR RITUAL;So;0;ON;;;;;N;;;;; 1D336;TETRAGRAM FOR FLIGHT;So;0;ON;;;;;N;;;;; 1D337;TETRAGRAM FOR VASTNESS OR WASTING;So;0;ON;;;;;N;;;;; 1D338;TETRAGRAM FOR CONSTANCY;So;0;ON;;;;;N;;;;; 1D339;TETRAGRAM FOR MEASURE;So;0;ON;;;;;N;;;;; 1D33A;TETRAGRAM FOR ETERNITY;So;0;ON;;;;;N;;;;; 1D33B;TETRAGRAM FOR UNITY;So;0;ON;;;;;N;;;;; 1D33C;TETRAGRAM FOR DIMINISHMENT;So;0;ON;;;;;N;;;;; 1D33D;TETRAGRAM FOR CLOSED MOUTH;So;0;ON;;;;;N;;;;; 1D33E;TETRAGRAM FOR GUARDEDNESS;So;0;ON;;;;;N;;;;; 1D33F;TETRAGRAM FOR GATHERING IN;So;0;ON;;;;;N;;;;; 1D340;TETRAGRAM FOR MASSING;So;0;ON;;;;;N;;;;; 1D341;TETRAGRAM FOR ACCUMULATION;So;0;ON;;;;;N;;;;; 1D342;TETRAGRAM FOR EMBELLISHMENT;So;0;ON;;;;;N;;;;; 1D343;TETRAGRAM FOR DOUBT;So;0;ON;;;;;N;;;;; 1D344;TETRAGRAM FOR WATCH;So;0;ON;;;;;N;;;;; 1D345;TETRAGRAM FOR SINKING;So;0;ON;;;;;N;;;;; 1D346;TETRAGRAM FOR INNER;So;0;ON;;;;;N;;;;; 1D347;TETRAGRAM FOR DEPARTURE;So;0;ON;;;;;N;;;;; 1D348;TETRAGRAM FOR DARKENING;So;0;ON;;;;;N;;;;; 1D349;TETRAGRAM FOR DIMMING;So;0;ON;;;;;N;;;;; 1D34A;TETRAGRAM FOR EXHAUSTION;So;0;ON;;;;;N;;;;; 1D34B;TETRAGRAM FOR SEVERANCE;So;0;ON;;;;;N;;;;; 1D34C;TETRAGRAM FOR STOPPAGE;So;0;ON;;;;;N;;;;; 1D34D;TETRAGRAM FOR HARDNESS;So;0;ON;;;;;N;;;;; 1D34E;TETRAGRAM FOR COMPLETION;So;0;ON;;;;;N;;;;; 1D34F;TETRAGRAM FOR CLOSURE;So;0;ON;;;;;N;;;;; 1D350;TETRAGRAM FOR FAILURE;So;0;ON;;;;;N;;;;; 1D351;TETRAGRAM FOR AGGRAVATION;So;0;ON;;;;;N;;;;; 1D352;TETRAGRAM FOR COMPLIANCE;So;0;ON;;;;;N;;;;; 1D353;TETRAGRAM FOR ON THE VERGE;So;0;ON;;;;;N;;;;; 1D354;TETRAGRAM FOR DIFFICULTIES;So;0;ON;;;;;N;;;;; 1D355;TETRAGRAM FOR LABOURING;So;0;ON;;;;;N;;;;; 1D356;TETRAGRAM FOR FOSTERING;So;0;ON;;;;;N;;;;; 1D360;COUNTING ROD UNIT DIGIT ONE;No;0;L;;;;1;N;;;;; 1D361;COUNTING ROD UNIT DIGIT TWO;No;0;L;;;;2;N;;;;; 1D362;COUNTING ROD UNIT DIGIT THREE;No;0;L;;;;3;N;;;;; 1D363;COUNTING ROD UNIT DIGIT FOUR;No;0;L;;;;4;N;;;;; 1D364;COUNTING ROD UNIT DIGIT FIVE;No;0;L;;;;5;N;;;;; 1D365;COUNTING ROD UNIT DIGIT SIX;No;0;L;;;;6;N;;;;; 1D366;COUNTING ROD UNIT DIGIT SEVEN;No;0;L;;;;7;N;;;;; 1D367;COUNTING ROD UNIT DIGIT EIGHT;No;0;L;;;;8;N;;;;; 1D368;COUNTING ROD UNIT DIGIT NINE;No;0;L;;;;9;N;;;;; 1D369;COUNTING ROD TENS DIGIT ONE;No;0;L;;;;10;N;;;;; 1D36A;COUNTING ROD TENS DIGIT TWO;No;0;L;;;;20;N;;;;; 1D36B;COUNTING ROD TENS DIGIT THREE;No;0;L;;;;30;N;;;;; 1D36C;COUNTING ROD TENS DIGIT FOUR;No;0;L;;;;40;N;;;;; 1D36D;COUNTING ROD TENS DIGIT FIVE;No;0;L;;;;50;N;;;;; 1D36E;COUNTING ROD TENS DIGIT SIX;No;0;L;;;;60;N;;;;; 1D36F;COUNTING ROD TENS DIGIT SEVEN;No;0;L;;;;70;N;;;;; 1D370;COUNTING ROD TENS DIGIT EIGHT;No;0;L;;;;80;N;;;;; 1D371;COUNTING ROD TENS DIGIT NINE;No;0;L;;;;90;N;;;;; 1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D4C1;MATHEMATICAL SCRIPT SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L; 0041;;;;N;;;;; 1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L; 0042;;;;N;;;;; 1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L; 0043;;;;N;;;;; 1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L; 0044;;;;N;;;;; 1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L; 0045;;;;N;;;;; 1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L; 0046;;;;N;;;;; 1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L; 0047;;;;N;;;;; 1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L; 0048;;;;N;;;;; 1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L; 0049;;;;N;;;;; 1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L; 004A;;;;N;;;;; 1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L; 004B;;;;N;;;;; 1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L; 004C;;;;N;;;;; 1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L; 004D;;;;N;;;;; 1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L; 004E;;;;N;;;;; 1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L; 004F;;;;N;;;;; 1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L; 0050;;;;N;;;;; 1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L; 0051;;;;N;;;;; 1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L; 0052;;;;N;;;;; 1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L; 0053;;;;N;;;;; 1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L; 0054;;;;N;;;;; 1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L; 0055;;;;N;;;;; 1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L; 0056;;;;N;;;;; 1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L; 0057;;;;N;;;;; 1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L; 0058;;;;N;;;;; 1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L; 0059;;;;N;;;;; 1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L; 005A;;;;N;;;;; 1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L; 0061;;;;N;;;;; 1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L; 0062;;;;N;;;;; 1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L; 0063;;;;N;;;;; 1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L; 0064;;;;N;;;;; 1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L; 0065;;;;N;;;;; 1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L; 0066;;;;N;;;;; 1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L; 0067;;;;N;;;;; 1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L; 0068;;;;N;;;;; 1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L; 0069;;;;N;;;;; 1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L; 006A;;;;N;;;;; 1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L; 006B;;;;N;;;;; 1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L; 006C;;;;N;;;;; 1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L; 006D;;;;N;;;;; 1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L; 006E;;;;N;;;;; 1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L; 006F;;;;N;;;;; 1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L; 0070;;;;N;;;;; 1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L; 0071;;;;N;;;;; 1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L; 0072;;;;N;;;;; 1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L; 0073;;;;N;;;;; 1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L; 0074;;;;N;;;;; 1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L; 0075;;;;N;;;;; 1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L; 0076;;;;N;;;;; 1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L; 0077;;;;N;;;;; 1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L; 0078;;;;N;;;;; 1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L; 0079;;;;N;;;;; 1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L; 007A;;;;N;;;;; 1D6A4;MATHEMATICAL ITALIC SMALL DOTLESS I;Ll;0;L; 0131;;;;N;;;;; 1D6A5;MATHEMATICAL ITALIC SMALL DOTLESS J;Ll;0;L; 0237;;;;N;;;;; 1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L; 0391;;;;N;;;;; 1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L; 0392;;;;N;;;;; 1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L; 0394;;;;N;;;;; 1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L; 0395;;;;N;;;;; 1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L; 0396;;;;N;;;;; 1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L; 0397;;;;N;;;;; 1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L; 0398;;;;N;;;;; 1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L; 0399;;;;N;;;;; 1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L; 039A;;;;N;;;;; 1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L; 039B;;;;N;;;;; 1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L; 039C;;;;N;;;;; 1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L; 039D;;;;N;;;;; 1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L; 039E;;;;N;;;;; 1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L; 039F;;;;N;;;;; 1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L; 03A1;;;;N;;;;; 1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L; 03F4;;;;N;;;;; 1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L; 03A3;;;;N;;;;; 1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L; 03A4;;;;N;;;;; 1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L; 03A5;;;;N;;;;; 1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L; 03A6;;;;N;;;;; 1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L; 03A7;;;;N;;;;; 1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L; 03A8;;;;N;;;;; 1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L; 03A9;;;;N;;;;; 1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L; 2207;;;;N;;;;; 1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L; 03B1;;;;N;;;;; 1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L; 03B2;;;;N;;;;; 1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L; 03B4;;;;N;;;;; 1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L; 03B5;;;;N;;;;; 1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L; 03B6;;;;N;;;;; 1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L; 03B7;;;;N;;;;; 1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L; 03B8;;;;N;;;;; 1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L; 03B9;;;;N;;;;; 1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L; 03BA;;;;N;;;;; 1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L; 03BB;;;;N;;;;; 1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L; 03BC;;;;N;;;;; 1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L; 03BD;;;;N;;;;; 1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L; 03BE;;;;N;;;;; 1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L; 03BF;;;;N;;;;; 1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L; 03C1;;;;N;;;;; 1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L; 03C2;;;;N;;;;; 1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L; 03C3;;;;N;;;;; 1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L; 03C4;;;;N;;;;; 1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L; 03C5;;;;N;;;;; 1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L; 03C6;;;;N;;;;; 1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L; 03C7;;;;N;;;;; 1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L; 03C8;;;;N;;;;; 1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L; 03C9;;;;N;;;;; 1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;ON; 2202;;;;Y;;;;; 1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L; 03F5;;;;N;;;;; 1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L; 03D1;;;;N;;;;; 1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L; 03F0;;;;N;;;;; 1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L; 03D5;;;;N;;;;; 1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L; 03F1;;;;N;;;;; 1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L; 03D6;;;;N;;;;; 1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L; 0391;;;;N;;;;; 1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L; 0392;;;;N;;;;; 1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L; 0394;;;;N;;;;; 1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L; 0395;;;;N;;;;; 1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L; 0396;;;;N;;;;; 1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L; 0397;;;;N;;;;; 1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L; 0398;;;;N;;;;; 1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L; 0399;;;;N;;;;; 1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L; 039A;;;;N;;;;; 1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L; 039B;;;;N;;;;; 1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L; 039C;;;;N;;;;; 1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L; 039D;;;;N;;;;; 1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L; 039E;;;;N;;;;; 1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L; 039F;;;;N;;;;; 1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L; 03A1;;;;N;;;;; 1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L; 03F4;;;;N;;;;; 1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L; 03A3;;;;N;;;;; 1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L; 03A4;;;;N;;;;; 1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L; 03A5;;;;N;;;;; 1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L; 03A6;;;;N;;;;; 1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L; 03A7;;;;N;;;;; 1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L; 03A8;;;;N;;;;; 1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L; 03A9;;;;N;;;;; 1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L; 2207;;;;N;;;;; 1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L; 03B1;;;;N;;;;; 1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L; 03B2;;;;N;;;;; 1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L; 03B4;;;;N;;;;; 1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L; 03B5;;;;N;;;;; 1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L; 03B6;;;;N;;;;; 1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L; 03B7;;;;N;;;;; 1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L; 03B8;;;;N;;;;; 1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L; 03B9;;;;N;;;;; 1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L; 03BA;;;;N;;;;; 1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L; 03BB;;;;N;;;;; 1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L; 03BC;;;;N;;;;; 1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L; 03BD;;;;N;;;;; 1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L; 03BE;;;;N;;;;; 1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L; 03BF;;;;N;;;;; 1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L; 03C1;;;;N;;;;; 1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L; 03C2;;;;N;;;;; 1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L; 03C3;;;;N;;;;; 1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L; 03C4;;;;N;;;;; 1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L; 03C5;;;;N;;;;; 1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L; 03C6;;;;N;;;;; 1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L; 03C7;;;;N;;;;; 1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L; 03C8;;;;N;;;;; 1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L; 03C9;;;;N;;;;; 1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON; 2202;;;;Y;;;;; 1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L; 03F5;;;;N;;;;; 1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L; 03D1;;;;N;;;;; 1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L; 03F0;;;;N;;;;; 1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L; 03D5;;;;N;;;;; 1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L; 03F1;;;;N;;;;; 1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L; 03D6;;;;N;;;;; 1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L; 0391;;;;N;;;;; 1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L; 0392;;;;N;;;;; 1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L; 0394;;;;N;;;;; 1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L; 0395;;;;N;;;;; 1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L; 0396;;;;N;;;;; 1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L; 0397;;;;N;;;;; 1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L; 0398;;;;N;;;;; 1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L; 0399;;;;N;;;;; 1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L; 039A;;;;N;;;;; 1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L; 039B;;;;N;;;;; 1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L; 039C;;;;N;;;;; 1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L; 039D;;;;N;;;;; 1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L; 039E;;;;N;;;;; 1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L; 039F;;;;N;;;;; 1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L; 03A1;;;;N;;;;; 1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L; 03F4;;;;N;;;;; 1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L; 03A3;;;;N;;;;; 1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L; 03A4;;;;N;;;;; 1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L; 03A5;;;;N;;;;; 1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L; 03A6;;;;N;;;;; 1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L; 03A7;;;;N;;;;; 1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L; 03A8;;;;N;;;;; 1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L; 03A9;;;;N;;;;; 1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L; 2207;;;;N;;;;; 1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L; 03B1;;;;N;;;;; 1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L; 03B2;;;;N;;;;; 1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L; 03B4;;;;N;;;;; 1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L; 03B5;;;;N;;;;; 1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L; 03B6;;;;N;;;;; 1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L; 03B7;;;;N;;;;; 1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L; 03B8;;;;N;;;;; 1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L; 03B9;;;;N;;;;; 1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L; 03BA;;;;N;;;;; 1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L; 03BB;;;;N;;;;; 1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L; 03BC;;;;N;;;;; 1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L; 03BD;;;;N;;;;; 1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L; 03BE;;;;N;;;;; 1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L; 03BF;;;;N;;;;; 1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L; 03C1;;;;N;;;;; 1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L; 03C2;;;;N;;;;; 1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L; 03C3;;;;N;;;;; 1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L; 03C4;;;;N;;;;; 1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L; 03C5;;;;N;;;;; 1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L; 03C6;;;;N;;;;; 1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L; 03C7;;;;N;;;;; 1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L; 03C8;;;;N;;;;; 1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L; 03C9;;;;N;;;;; 1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON; 2202;;;;Y;;;;; 1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L; 03F5;;;;N;;;;; 1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L; 03D1;;;;N;;;;; 1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L; 03F0;;;;N;;;;; 1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L; 03D5;;;;N;;;;; 1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L; 03F1;;;;N;;;;; 1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L; 03D6;;;;N;;;;; 1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L; 0391;;;;N;;;;; 1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L; 0392;;;;N;;;;; 1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L; 0394;;;;N;;;;; 1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L; 0395;;;;N;;;;; 1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L; 0396;;;;N;;;;; 1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L; 0397;;;;N;;;;; 1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L; 0398;;;;N;;;;; 1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L; 0399;;;;N;;;;; 1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L; 039A;;;;N;;;;; 1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L; 039B;;;;N;;;;; 1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L; 039C;;;;N;;;;; 1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L; 039D;;;;N;;;;; 1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L; 039E;;;;N;;;;; 1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L; 039F;;;;N;;;;; 1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L; 03A1;;;;N;;;;; 1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L; 03F4;;;;N;;;;; 1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L; 03A3;;;;N;;;;; 1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L; 03A4;;;;N;;;;; 1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L; 03A5;;;;N;;;;; 1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L; 03A6;;;;N;;;;; 1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L; 03A7;;;;N;;;;; 1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L; 03A8;;;;N;;;;; 1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L; 03A9;;;;N;;;;; 1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L; 2207;;;;N;;;;; 1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L; 03B1;;;;N;;;;; 1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L; 03B2;;;;N;;;;; 1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L; 03B4;;;;N;;;;; 1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L; 03B5;;;;N;;;;; 1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L; 03B6;;;;N;;;;; 1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L; 03B7;;;;N;;;;; 1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L; 03B8;;;;N;;;;; 1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L; 03B9;;;;N;;;;; 1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L; 03BA;;;;N;;;;; 1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L; 03BB;;;;N;;;;; 1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L; 03BC;;;;N;;;;; 1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L; 03BD;;;;N;;;;; 1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L; 03BE;;;;N;;;;; 1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L; 03BF;;;;N;;;;; 1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L; 03C1;;;;N;;;;; 1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L; 03C2;;;;N;;;;; 1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L; 03C3;;;;N;;;;; 1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L; 03C4;;;;N;;;;; 1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L; 03C5;;;;N;;;;; 1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L; 03C6;;;;N;;;;; 1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L; 03C7;;;;N;;;;; 1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L; 03C8;;;;N;;;;; 1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L; 03C9;;;;N;;;;; 1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;ON; 2202;;;;Y;;;;; 1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L; 03F5;;;;N;;;;; 1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L; 03D1;;;;N;;;;; 1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L; 03F0;;;;N;;;;; 1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L; 03D5;;;;N;;;;; 1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L; 03F1;;;;N;;;;; 1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L; 03D6;;;;N;;;;; 1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L; 0391;;;;N;;;;; 1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L; 0392;;;;N;;;;; 1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L; 0393;;;;N;;;;; 1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L; 0394;;;;N;;;;; 1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L; 0395;;;;N;;;;; 1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L; 0396;;;;N;;;;; 1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L; 0397;;;;N;;;;; 1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L; 0398;;;;N;;;;; 1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L; 0399;;;;N;;;;; 1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L; 039A;;;;N;;;;; 1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L; 039B;;;;N;;;;; 1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L; 039C;;;;N;;;;; 1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L; 039D;;;;N;;;;; 1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L; 039E;;;;N;;;;; 1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L; 039F;;;;N;;;;; 1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L; 03A0;;;;N;;;;; 1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L; 03A1;;;;N;;;;; 1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L; 03F4;;;;N;;;;; 1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L; 03A3;;;;N;;;;; 1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L; 03A4;;;;N;;;;; 1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L; 03A5;;;;N;;;;; 1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L; 03A6;;;;N;;;;; 1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L; 03A7;;;;N;;;;; 1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L; 03A8;;;;N;;;;; 1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L; 03A9;;;;N;;;;; 1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L; 2207;;;;N;;;;; 1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L; 03B1;;;;N;;;;; 1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L; 03B2;;;;N;;;;; 1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L; 03B3;;;;N;;;;; 1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L; 03B4;;;;N;;;;; 1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L; 03B5;;;;N;;;;; 1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L; 03B6;;;;N;;;;; 1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L; 03B7;;;;N;;;;; 1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L; 03B8;;;;N;;;;; 1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L; 03B9;;;;N;;;;; 1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L; 03BA;;;;N;;;;; 1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L; 03BB;;;;N;;;;; 1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L; 03BC;;;;N;;;;; 1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L; 03BD;;;;N;;;;; 1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L; 03BE;;;;N;;;;; 1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L; 03BF;;;;N;;;;; 1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L; 03C0;;;;N;;;;; 1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L; 03C1;;;;N;;;;; 1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L; 03C2;;;;N;;;;; 1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L; 03C3;;;;N;;;;; 1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L; 03C4;;;;N;;;;; 1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L; 03C5;;;;N;;;;; 1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L; 03C6;;;;N;;;;; 1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L; 03C7;;;;N;;;;; 1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L; 03C8;;;;N;;;;; 1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L; 03C9;;;;N;;;;; 1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON; 2202;;;;Y;;;;; 1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L; 03F5;;;;N;;;;; 1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L; 03D1;;;;N;;;;; 1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L; 03F0;;;;N;;;;; 1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L; 03D5;;;;N;;;;; 1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L; 03F1;;;;N;;;;; 1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L; 03D6;;;;N;;;;; 1D7CA;MATHEMATICAL BOLD CAPITAL DIGAMMA;Lu;0;L; 03DC;;;;N;;;;; 1D7CB;MATHEMATICAL BOLD SMALL DIGAMMA;Ll;0;L; 03DD;;;;N;;;;; 1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; 1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; 1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; 1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; 1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; 1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; 1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; 1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; 1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; 1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; 1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; 1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; 1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; 1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; 1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; 1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; 1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; 1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; 1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; 1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; 1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; 1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; 1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; 1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; 1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; 1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; 1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; 1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; 1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; 1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; 1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; 1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; 1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN; 0030;0;0;0;N;;;;; 1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN; 0031;1;1;1;N;;;;; 1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN; 0032;2;2;2;N;;;;; 1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN; 0033;3;3;3;N;;;;; 1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN; 0034;4;4;4;N;;;;; 1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN; 0035;5;5;5;N;;;;; 1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN; 0036;6;6;6;N;;;;; 1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; 1D800;SIGNWRITING HAND-FIST INDEX;So;0;L;;;;;N;;;;; 1D801;SIGNWRITING HAND-CIRCLE INDEX;So;0;L;;;;;N;;;;; 1D802;SIGNWRITING HAND-CUP INDEX;So;0;L;;;;;N;;;;; 1D803;SIGNWRITING HAND-OVAL INDEX;So;0;L;;;;;N;;;;; 1D804;SIGNWRITING HAND-HINGE INDEX;So;0;L;;;;;N;;;;; 1D805;SIGNWRITING HAND-ANGLE INDEX;So;0;L;;;;;N;;;;; 1D806;SIGNWRITING HAND-FIST INDEX BENT;So;0;L;;;;;N;;;;; 1D807;SIGNWRITING HAND-CIRCLE INDEX BENT;So;0;L;;;;;N;;;;; 1D808;SIGNWRITING HAND-FIST THUMB UNDER INDEX BENT;So;0;L;;;;;N;;;;; 1D809;SIGNWRITING HAND-FIST INDEX RAISED KNUCKLE;So;0;L;;;;;N;;;;; 1D80A;SIGNWRITING HAND-FIST INDEX CUPPED;So;0;L;;;;;N;;;;; 1D80B;SIGNWRITING HAND-FIST INDEX HINGED;So;0;L;;;;;N;;;;; 1D80C;SIGNWRITING HAND-FIST INDEX HINGED LOW;So;0;L;;;;;N;;;;; 1D80D;SIGNWRITING HAND-CIRCLE INDEX HINGE;So;0;L;;;;;N;;;;; 1D80E;SIGNWRITING HAND-FIST INDEX MIDDLE;So;0;L;;;;;N;;;;; 1D80F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE;So;0;L;;;;;N;;;;; 1D810;SIGNWRITING HAND-FIST INDEX MIDDLE BENT;So;0;L;;;;;N;;;;; 1D811;SIGNWRITING HAND-FIST INDEX MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;; 1D812;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED;So;0;L;;;;;N;;;;; 1D813;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED;So;0;L;;;;;N;;;;; 1D814;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP;So;0;L;;;;;N;;;;; 1D815;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED;So;0;L;;;;;N;;;;; 1D816;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED INDEX BENT;So;0;L;;;;;N;;;;; 1D817;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED MIDDLE BENT;So;0;L;;;;;N;;;;; 1D818;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED;So;0;L;;;;;N;;;;; 1D819;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED;So;0;L;;;;;N;;;;; 1D81A;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;; 1D81B;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;; 1D81C;SIGNWRITING HAND-FIST MIDDLE BENT OVER INDEX;So;0;L;;;;;N;;;;; 1D81D;SIGNWRITING HAND-FIST INDEX BENT OVER MIDDLE;So;0;L;;;;;N;;;;; 1D81E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;; 1D81F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;; 1D820;SIGNWRITING HAND-FIST INDEX MIDDLE STRAIGHT THUMB BENT;So;0;L;;;;;N;;;;; 1D821;SIGNWRITING HAND-FIST INDEX MIDDLE BENT THUMB STRAIGHT;So;0;L;;;;;N;;;;; 1D822;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB BENT;So;0;L;;;;;N;;;;; 1D823;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED SPREAD THUMB SIDE;So;0;L;;;;;N;;;;; 1D824;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB SIDE;So;0;L;;;;;N;;;;; 1D825;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB CONJOINED;So;0;L;;;;;N;;;;; 1D826;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;; 1D827;SIGNWRITING HAND-FIST INDEX MIDDLE UP SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;; 1D828;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CUPPED;So;0;L;;;;;N;;;;; 1D829;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CIRCLED;So;0;L;;;;;N;;;;; 1D82A;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HOOKED;So;0;L;;;;;N;;;;; 1D82B;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HINGED;So;0;L;;;;;N;;;;; 1D82C;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE STRAIGHT;So;0;L;;;;;N;;;;; 1D82D;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE;So;0;L;;;;;N;;;;; 1D82E;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;; 1D82F;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE BENT;So;0;L;;;;;N;;;;; 1D830;SIGNWRITING HAND-FIST MIDDLE THUMB HOOKED INDEX UP;So;0;L;;;;;N;;;;; 1D831;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE UP;So;0;L;;;;;N;;;;; 1D832;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED THUMB SIDE;So;0;L;;;;;N;;;;; 1D833;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED THUMB SIDE;So;0;L;;;;;N;;;;; 1D834;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB FORWARD;So;0;L;;;;;N;;;;; 1D835;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED THUMB FORWARD;So;0;L;;;;;N;;;;; 1D836;SIGNWRITING HAND-FIST MIDDLE THUMB CUPPED INDEX UP;So;0;L;;;;;N;;;;; 1D837;SIGNWRITING HAND-FIST INDEX THUMB CUPPED MIDDLE UP;So;0;L;;;;;N;;;;; 1D838;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX UP;So;0;L;;;;;N;;;;; 1D839;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX HINGED;So;0;L;;;;;N;;;;; 1D83A;SIGNWRITING HAND-FIST INDEX THUMB ANGLED OUT MIDDLE UP;So;0;L;;;;;N;;;;; 1D83B;SIGNWRITING HAND-FIST INDEX THUMB ANGLED IN MIDDLE UP;So;0;L;;;;;N;;;;; 1D83C;SIGNWRITING HAND-FIST INDEX THUMB CIRCLED MIDDLE UP;So;0;L;;;;;N;;;;; 1D83D;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CONJOINED HINGED;So;0;L;;;;;N;;;;; 1D83E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED OUT;So;0;L;;;;;N;;;;; 1D83F;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED;So;0;L;;;;;N;;;;; 1D840;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX UP;So;0;L;;;;;N;;;;; 1D841;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX CROSSED;So;0;L;;;;;N;;;;; 1D842;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED INDEX UP;So;0;L;;;;;N;;;;; 1D843;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE HINGED;So;0;L;;;;;N;;;;; 1D844;SIGNWRITING HAND-FLAT FOUR FINGERS;So;0;L;;;;;N;;;;; 1D845;SIGNWRITING HAND-FLAT FOUR FINGERS BENT;So;0;L;;;;;N;;;;; 1D846;SIGNWRITING HAND-FLAT FOUR FINGERS HINGED;So;0;L;;;;;N;;;;; 1D847;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; 1D848;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED SPLIT;So;0;L;;;;;N;;;;; 1D849;SIGNWRITING HAND-CLAW FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; 1D84A;SIGNWRITING HAND-FIST FOUR FINGERS CONJOINED BENT;So;0;L;;;;;N;;;;; 1D84B;SIGNWRITING HAND-HINGE FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; 1D84C;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; 1D84D;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; 1D84E;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;; 1D84F;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;; 1D850;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;; 1D851;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;; 1D852;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;; 1D853;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; 1D854;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;; 1D855;SIGNWRITING HAND-HINGE FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;; 1D856;SIGNWRITING HAND-OVAL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; 1D857;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED;So;0;L;;;;;N;;;;; 1D858;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED THUMB SIDE;So;0;L;;;;;N;;;;; 1D859;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED NO THUMB;So;0;L;;;;;N;;;;; 1D85A;SIGNWRITING HAND-FLAT;So;0;L;;;;;N;;;;; 1D85B;SIGNWRITING HAND-FLAT BETWEEN PALM FACINGS;So;0;L;;;;;N;;;;; 1D85C;SIGNWRITING HAND-FLAT HEEL;So;0;L;;;;;N;;;;; 1D85D;SIGNWRITING HAND-FLAT THUMB SIDE;So;0;L;;;;;N;;;;; 1D85E;SIGNWRITING HAND-FLAT HEEL THUMB SIDE;So;0;L;;;;;N;;;;; 1D85F;SIGNWRITING HAND-FLAT THUMB BENT;So;0;L;;;;;N;;;;; 1D860;SIGNWRITING HAND-FLAT THUMB FORWARD;So;0;L;;;;;N;;;;; 1D861;SIGNWRITING HAND-FLAT SPLIT INDEX THUMB SIDE;So;0;L;;;;;N;;;;; 1D862;SIGNWRITING HAND-FLAT SPLIT CENTRE;So;0;L;;;;;N;;;;; 1D863;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE;So;0;L;;;;;N;;;;; 1D864;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE BENT;So;0;L;;;;;N;;;;; 1D865;SIGNWRITING HAND-FLAT SPLIT LITTLE;So;0;L;;;;;N;;;;; 1D866;SIGNWRITING HAND-CLAW;So;0;L;;;;;N;;;;; 1D867;SIGNWRITING HAND-CLAW THUMB SIDE;So;0;L;;;;;N;;;;; 1D868;SIGNWRITING HAND-CLAW NO THUMB;So;0;L;;;;;N;;;;; 1D869;SIGNWRITING HAND-CLAW THUMB FORWARD;So;0;L;;;;;N;;;;; 1D86A;SIGNWRITING HAND-HOOK CURLICUE;So;0;L;;;;;N;;;;; 1D86B;SIGNWRITING HAND-HOOK;So;0;L;;;;;N;;;;; 1D86C;SIGNWRITING HAND-CUP OPEN;So;0;L;;;;;N;;;;; 1D86D;SIGNWRITING HAND-CUP;So;0;L;;;;;N;;;;; 1D86E;SIGNWRITING HAND-CUP OPEN THUMB SIDE;So;0;L;;;;;N;;;;; 1D86F;SIGNWRITING HAND-CUP THUMB SIDE;So;0;L;;;;;N;;;;; 1D870;SIGNWRITING HAND-CUP OPEN NO THUMB;So;0;L;;;;;N;;;;; 1D871;SIGNWRITING HAND-CUP NO THUMB;So;0;L;;;;;N;;;;; 1D872;SIGNWRITING HAND-CUP OPEN THUMB FORWARD;So;0;L;;;;;N;;;;; 1D873;SIGNWRITING HAND-CUP THUMB FORWARD;So;0;L;;;;;N;;;;; 1D874;SIGNWRITING HAND-CURLICUE OPEN;So;0;L;;;;;N;;;;; 1D875;SIGNWRITING HAND-CURLICUE;So;0;L;;;;;N;;;;; 1D876;SIGNWRITING HAND-CIRCLE;So;0;L;;;;;N;;;;; 1D877;SIGNWRITING HAND-OVAL;So;0;L;;;;;N;;;;; 1D878;SIGNWRITING HAND-OVAL THUMB SIDE;So;0;L;;;;;N;;;;; 1D879;SIGNWRITING HAND-OVAL NO THUMB;So;0;L;;;;;N;;;;; 1D87A;SIGNWRITING HAND-OVAL THUMB FORWARD;So;0;L;;;;;N;;;;; 1D87B;SIGNWRITING HAND-HINGE OPEN;So;0;L;;;;;N;;;;; 1D87C;SIGNWRITING HAND-HINGE OPEN THUMB FORWARD;So;0;L;;;;;N;;;;; 1D87D;SIGNWRITING HAND-HINGE;So;0;L;;;;;N;;;;; 1D87E;SIGNWRITING HAND-HINGE SMALL;So;0;L;;;;;N;;;;; 1D87F;SIGNWRITING HAND-HINGE OPEN THUMB SIDE;So;0;L;;;;;N;;;;; 1D880;SIGNWRITING HAND-HINGE THUMB SIDE;So;0;L;;;;;N;;;;; 1D881;SIGNWRITING HAND-HINGE OPEN NO THUMB;So;0;L;;;;;N;;;;; 1D882;SIGNWRITING HAND-HINGE NO THUMB;So;0;L;;;;;N;;;;; 1D883;SIGNWRITING HAND-HINGE THUMB SIDE TOUCHING INDEX;So;0;L;;;;;N;;;;; 1D884;SIGNWRITING HAND-HINGE THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;; 1D885;SIGNWRITING HAND-ANGLE;So;0;L;;;;;N;;;;; 1D886;SIGNWRITING HAND-FIST INDEX MIDDLE RING;So;0;L;;;;;N;;;;; 1D887;SIGNWRITING HAND-CIRCLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; 1D888;SIGNWRITING HAND-HINGE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; 1D889;SIGNWRITING HAND-ANGLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; 1D88A;SIGNWRITING HAND-HINGE LITTLE;So;0;L;;;;;N;;;;; 1D88B;SIGNWRITING HAND-FIST INDEX MIDDLE RING BENT;So;0;L;;;;;N;;;;; 1D88C;SIGNWRITING HAND-FIST INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;; 1D88D;SIGNWRITING HAND-HINGE INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;; 1D88E;SIGNWRITING HAND-FIST LITTLE DOWN;So;0;L;;;;;N;;;;; 1D88F;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE STRAIGHT;So;0;L;;;;;N;;;;; 1D890;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE CURVED;So;0;L;;;;;N;;;;; 1D891;SIGNWRITING HAND-FIST LITTLE DOWN OTHERS CIRCLED;So;0;L;;;;;N;;;;; 1D892;SIGNWRITING HAND-FIST LITTLE UP;So;0;L;;;;;N;;;;; 1D893;SIGNWRITING HAND-FIST THUMB UNDER LITTLE UP;So;0;L;;;;;N;;;;; 1D894;SIGNWRITING HAND-CIRCLE LITTLE UP;So;0;L;;;;;N;;;;; 1D895;SIGNWRITING HAND-OVAL LITTLE UP;So;0;L;;;;;N;;;;; 1D896;SIGNWRITING HAND-ANGLE LITTLE UP;So;0;L;;;;;N;;;;; 1D897;SIGNWRITING HAND-FIST LITTLE RAISED KNUCKLE;So;0;L;;;;;N;;;;; 1D898;SIGNWRITING HAND-FIST LITTLE BENT;So;0;L;;;;;N;;;;; 1D899;SIGNWRITING HAND-FIST LITTLE TOUCHES THUMB;So;0;L;;;;;N;;;;; 1D89A;SIGNWRITING HAND-FIST LITTLE THUMB;So;0;L;;;;;N;;;;; 1D89B;SIGNWRITING HAND-HINGE LITTLE THUMB;So;0;L;;;;;N;;;;; 1D89C;SIGNWRITING HAND-FIST LITTLE INDEX THUMB;So;0;L;;;;;N;;;;; 1D89D;SIGNWRITING HAND-HINGE LITTLE INDEX THUMB;So;0;L;;;;;N;;;;; 1D89E;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB OUT;So;0;L;;;;;N;;;;; 1D89F;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB;So;0;L;;;;;N;;;;; 1D8A0;SIGNWRITING HAND-FIST LITTLE INDEX;So;0;L;;;;;N;;;;; 1D8A1;SIGNWRITING HAND-CIRCLE LITTLE INDEX;So;0;L;;;;;N;;;;; 1D8A2;SIGNWRITING HAND-HINGE LITTLE INDEX;So;0;L;;;;;N;;;;; 1D8A3;SIGNWRITING HAND-ANGLE LITTLE INDEX;So;0;L;;;;;N;;;;; 1D8A4;SIGNWRITING HAND-FIST INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; 1D8A5;SIGNWRITING HAND-CIRCLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; 1D8A6;SIGNWRITING HAND-HINGE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; 1D8A7;SIGNWRITING HAND-HINGE RING;So;0;L;;;;;N;;;;; 1D8A8;SIGNWRITING HAND-ANGLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; 1D8A9;SIGNWRITING HAND-FIST INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;; 1D8AA;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;; 1D8AB;SIGNWRITING HAND-FIST RING DOWN;So;0;L;;;;;N;;;;; 1D8AC;SIGNWRITING HAND-HINGE RING DOWN INDEX THUMB HOOK MIDDLE;So;0;L;;;;;N;;;;; 1D8AD;SIGNWRITING HAND-ANGLE RING DOWN MIDDLE THUMB INDEX CROSS;So;0;L;;;;;N;;;;; 1D8AE;SIGNWRITING HAND-FIST RING UP;So;0;L;;;;;N;;;;; 1D8AF;SIGNWRITING HAND-FIST RING RAISED KNUCKLE;So;0;L;;;;;N;;;;; 1D8B0;SIGNWRITING HAND-FIST RING LITTLE;So;0;L;;;;;N;;;;; 1D8B1;SIGNWRITING HAND-CIRCLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8B2;SIGNWRITING HAND-OVAL RING LITTLE;So;0;L;;;;;N;;;;; 1D8B3;SIGNWRITING HAND-ANGLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8B4;SIGNWRITING HAND-FIST RING MIDDLE;So;0;L;;;;;N;;;;; 1D8B5;SIGNWRITING HAND-FIST RING MIDDLE CONJOINED;So;0;L;;;;;N;;;;; 1D8B6;SIGNWRITING HAND-FIST RING MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;; 1D8B7;SIGNWRITING HAND-FIST RING INDEX;So;0;L;;;;;N;;;;; 1D8B8;SIGNWRITING HAND-FIST RING THUMB;So;0;L;;;;;N;;;;; 1D8B9;SIGNWRITING HAND-HOOK RING THUMB;So;0;L;;;;;N;;;;; 1D8BA;SIGNWRITING HAND-FIST INDEX RING LITTLE;So;0;L;;;;;N;;;;; 1D8BB;SIGNWRITING HAND-CIRCLE INDEX RING LITTLE;So;0;L;;;;;N;;;;; 1D8BC;SIGNWRITING HAND-CURLICUE INDEX RING LITTLE ON;So;0;L;;;;;N;;;;; 1D8BD;SIGNWRITING HAND-HOOK INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;; 1D8BE;SIGNWRITING HAND-HOOK INDEX RING LITTLE IN;So;0;L;;;;;N;;;;; 1D8BF;SIGNWRITING HAND-HOOK INDEX RING LITTLE UNDER;So;0;L;;;;;N;;;;; 1D8C0;SIGNWRITING HAND-CUP INDEX RING LITTLE;So;0;L;;;;;N;;;;; 1D8C1;SIGNWRITING HAND-HINGE INDEX RING LITTLE;So;0;L;;;;;N;;;;; 1D8C2;SIGNWRITING HAND-ANGLE INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;; 1D8C3;SIGNWRITING HAND-ANGLE INDEX RING LITTLE;So;0;L;;;;;N;;;;; 1D8C4;SIGNWRITING HAND-FIST MIDDLE DOWN;So;0;L;;;;;N;;;;; 1D8C5;SIGNWRITING HAND-HINGE MIDDLE;So;0;L;;;;;N;;;;; 1D8C6;SIGNWRITING HAND-FIST MIDDLE UP;So;0;L;;;;;N;;;;; 1D8C7;SIGNWRITING HAND-CIRCLE MIDDLE UP;So;0;L;;;;;N;;;;; 1D8C8;SIGNWRITING HAND-FIST MIDDLE RAISED KNUCKLE;So;0;L;;;;;N;;;;; 1D8C9;SIGNWRITING HAND-FIST MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;; 1D8CA;SIGNWRITING HAND-HOOK MIDDLE THUMB;So;0;L;;;;;N;;;;; 1D8CB;SIGNWRITING HAND-FIST MIDDLE THUMB LITTLE;So;0;L;;;;;N;;;;; 1D8CC;SIGNWRITING HAND-FIST MIDDLE LITTLE;So;0;L;;;;;N;;;;; 1D8CD;SIGNWRITING HAND-FIST MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8CE;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8CF;SIGNWRITING HAND-CURLICUE MIDDLE RING LITTLE ON;So;0;L;;;;;N;;;;; 1D8D0;SIGNWRITING HAND-CUP MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8D1;SIGNWRITING HAND-HINGE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8D2;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE OUT;So;0;L;;;;;N;;;;; 1D8D3;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE IN;So;0;L;;;;;N;;;;; 1D8D4;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; 1D8D5;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE BENT;So;0;L;;;;;N;;;;; 1D8D6;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;; 1D8D7;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED SIDE;So;0;L;;;;;N;;;;; 1D8D8;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED OUT;So;0;L;;;;;N;;;;; 1D8D9;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED IN;So;0;L;;;;;N;;;;; 1D8DA;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;; 1D8DB;SIGNWRITING HAND-HINGE INDEX HINGED;So;0;L;;;;;N;;;;; 1D8DC;SIGNWRITING HAND-FIST INDEX THUMB SIDE;So;0;L;;;;;N;;;;; 1D8DD;SIGNWRITING HAND-HINGE INDEX THUMB SIDE;So;0;L;;;;;N;;;;; 1D8DE;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB DIAGONAL;So;0;L;;;;;N;;;;; 1D8DF;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB CONJOINED;So;0;L;;;;;N;;;;; 1D8E0;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB BENT;So;0;L;;;;;N;;;;; 1D8E1;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX BENT;So;0;L;;;;;N;;;;; 1D8E2;SIGNWRITING HAND-FIST INDEX THUMB SIDE BOTH BENT;So;0;L;;;;;N;;;;; 1D8E3;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX HINGE;So;0;L;;;;;N;;;;; 1D8E4;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX STRAIGHT;So;0;L;;;;;N;;;;; 1D8E5;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX BENT;So;0;L;;;;;N;;;;; 1D8E6;SIGNWRITING HAND-FIST INDEX THUMB HOOK;So;0;L;;;;;N;;;;; 1D8E7;SIGNWRITING HAND-FIST INDEX THUMB CURLICUE;So;0;L;;;;;N;;;;; 1D8E8;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;; 1D8E9;SIGNWRITING HAND-CLAW INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;; 1D8EA;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB UNDER;So;0;L;;;;;N;;;;; 1D8EB;SIGNWRITING HAND-FIST INDEX THUMB CIRCLE;So;0;L;;;;;N;;;;; 1D8EC;SIGNWRITING HAND-CUP INDEX THUMB;So;0;L;;;;;N;;;;; 1D8ED;SIGNWRITING HAND-CUP INDEX THUMB OPEN;So;0;L;;;;;N;;;;; 1D8EE;SIGNWRITING HAND-HINGE INDEX THUMB OPEN;So;0;L;;;;;N;;;;; 1D8EF;SIGNWRITING HAND-HINGE INDEX THUMB LARGE;So;0;L;;;;;N;;;;; 1D8F0;SIGNWRITING HAND-HINGE INDEX THUMB;So;0;L;;;;;N;;;;; 1D8F1;SIGNWRITING HAND-HINGE INDEX THUMB SMALL;So;0;L;;;;;N;;;;; 1D8F2;SIGNWRITING HAND-ANGLE INDEX THUMB OUT;So;0;L;;;;;N;;;;; 1D8F3;SIGNWRITING HAND-ANGLE INDEX THUMB IN;So;0;L;;;;;N;;;;; 1D8F4;SIGNWRITING HAND-ANGLE INDEX THUMB;So;0;L;;;;;N;;;;; 1D8F5;SIGNWRITING HAND-FIST THUMB;So;0;L;;;;;N;;;;; 1D8F6;SIGNWRITING HAND-FIST THUMB HEEL;So;0;L;;;;;N;;;;; 1D8F7;SIGNWRITING HAND-FIST THUMB SIDE DIAGONAL;So;0;L;;;;;N;;;;; 1D8F8;SIGNWRITING HAND-FIST THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;; 1D8F9;SIGNWRITING HAND-FIST THUMB SIDE BENT;So;0;L;;;;;N;;;;; 1D8FA;SIGNWRITING HAND-FIST THUMB FORWARD;So;0;L;;;;;N;;;;; 1D8FB;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE;So;0;L;;;;;N;;;;; 1D8FC;SIGNWRITING HAND-FIST THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;; 1D8FD;SIGNWRITING HAND-FIST THUMB BETWEEN RING LITTLE;So;0;L;;;;;N;;;;; 1D8FE;SIGNWRITING HAND-FIST THUMB UNDER TWO FINGERS;So;0;L;;;;;N;;;;; 1D8FF;SIGNWRITING HAND-FIST THUMB OVER TWO FINGERS;So;0;L;;;;;N;;;;; 1D900;SIGNWRITING HAND-FIST THUMB UNDER THREE FINGERS;So;0;L;;;;;N;;;;; 1D901;SIGNWRITING HAND-FIST THUMB UNDER FOUR FINGERS;So;0;L;;;;;N;;;;; 1D902;SIGNWRITING HAND-FIST THUMB OVER FOUR RAISED KNUCKLES;So;0;L;;;;;N;;;;; 1D903;SIGNWRITING HAND-FIST;So;0;L;;;;;N;;;;; 1D904;SIGNWRITING HAND-FIST HEEL;So;0;L;;;;;N;;;;; 1D905;SIGNWRITING TOUCH SINGLE;So;0;L;;;;;N;;;;; 1D906;SIGNWRITING TOUCH MULTIPLE;So;0;L;;;;;N;;;;; 1D907;SIGNWRITING TOUCH BETWEEN;So;0;L;;;;;N;;;;; 1D908;SIGNWRITING GRASP SINGLE;So;0;L;;;;;N;;;;; 1D909;SIGNWRITING GRASP MULTIPLE;So;0;L;;;;;N;;;;; 1D90A;SIGNWRITING GRASP BETWEEN;So;0;L;;;;;N;;;;; 1D90B;SIGNWRITING STRIKE SINGLE;So;0;L;;;;;N;;;;; 1D90C;SIGNWRITING STRIKE MULTIPLE;So;0;L;;;;;N;;;;; 1D90D;SIGNWRITING STRIKE BETWEEN;So;0;L;;;;;N;;;;; 1D90E;SIGNWRITING BRUSH SINGLE;So;0;L;;;;;N;;;;; 1D90F;SIGNWRITING BRUSH MULTIPLE;So;0;L;;;;;N;;;;; 1D910;SIGNWRITING BRUSH BETWEEN;So;0;L;;;;;N;;;;; 1D911;SIGNWRITING RUB SINGLE;So;0;L;;;;;N;;;;; 1D912;SIGNWRITING RUB MULTIPLE;So;0;L;;;;;N;;;;; 1D913;SIGNWRITING RUB BETWEEN;So;0;L;;;;;N;;;;; 1D914;SIGNWRITING SURFACE SYMBOLS;So;0;L;;;;;N;;;;; 1D915;SIGNWRITING SURFACE BETWEEN;So;0;L;;;;;N;;;;; 1D916;SIGNWRITING SQUEEZE LARGE SINGLE;So;0;L;;;;;N;;;;; 1D917;SIGNWRITING SQUEEZE SMALL SINGLE;So;0;L;;;;;N;;;;; 1D918;SIGNWRITING SQUEEZE LARGE MULTIPLE;So;0;L;;;;;N;;;;; 1D919;SIGNWRITING SQUEEZE SMALL MULTIPLE;So;0;L;;;;;N;;;;; 1D91A;SIGNWRITING SQUEEZE SEQUENTIAL;So;0;L;;;;;N;;;;; 1D91B;SIGNWRITING FLICK LARGE SINGLE;So;0;L;;;;;N;;;;; 1D91C;SIGNWRITING FLICK SMALL SINGLE;So;0;L;;;;;N;;;;; 1D91D;SIGNWRITING FLICK LARGE MULTIPLE;So;0;L;;;;;N;;;;; 1D91E;SIGNWRITING FLICK SMALL MULTIPLE;So;0;L;;;;;N;;;;; 1D91F;SIGNWRITING FLICK SEQUENTIAL;So;0;L;;;;;N;;;;; 1D920;SIGNWRITING SQUEEZE FLICK ALTERNATING;So;0;L;;;;;N;;;;; 1D921;SIGNWRITING MOVEMENT-HINGE UP DOWN LARGE;So;0;L;;;;;N;;;;; 1D922;SIGNWRITING MOVEMENT-HINGE UP DOWN SMALL;So;0;L;;;;;N;;;;; 1D923;SIGNWRITING MOVEMENT-HINGE UP SEQUENTIAL;So;0;L;;;;;N;;;;; 1D924;SIGNWRITING MOVEMENT-HINGE DOWN SEQUENTIAL;So;0;L;;;;;N;;;;; 1D925;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING LARGE;So;0;L;;;;;N;;;;; 1D926;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING SMALL;So;0;L;;;;;N;;;;; 1D927;SIGNWRITING MOVEMENT-HINGE SIDE TO SIDE SCISSORS;So;0;L;;;;;N;;;;; 1D928;SIGNWRITING MOVEMENT-WALLPLANE FINGER CONTACT;So;0;L;;;;;N;;;;; 1D929;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CONTACT;So;0;L;;;;;N;;;;; 1D92A;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;; 1D92B;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;; 1D92C;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;; 1D92D;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;; 1D92E;SIGNWRITING MOVEMENT-WALLPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D92F;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;; 1D930;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D931;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;; 1D932;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; 1D933;SIGNWRITING MOVEMENT-WALLPLANE CROSS;So;0;L;;;;;N;;;;; 1D934;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;; 1D935;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D936;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING;So;0;L;;;;;N;;;;; 1D937;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; 1D938;SIGNWRITING MOVEMENT-WALLPLANE BEND SMALL;So;0;L;;;;;N;;;;; 1D939;SIGNWRITING MOVEMENT-WALLPLANE BEND MEDIUM;So;0;L;;;;;N;;;;; 1D93A;SIGNWRITING MOVEMENT-WALLPLANE BEND LARGE;So;0;L;;;;;N;;;;; 1D93B;SIGNWRITING MOVEMENT-WALLPLANE CORNER SMALL;So;0;L;;;;;N;;;;; 1D93C;SIGNWRITING MOVEMENT-WALLPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;; 1D93D;SIGNWRITING MOVEMENT-WALLPLANE CORNER LARGE;So;0;L;;;;;N;;;;; 1D93E;SIGNWRITING MOVEMENT-WALLPLANE CORNER ROTATION;So;0;L;;;;;N;;;;; 1D93F;SIGNWRITING MOVEMENT-WALLPLANE CHECK SMALL;So;0;L;;;;;N;;;;; 1D940;SIGNWRITING MOVEMENT-WALLPLANE CHECK MEDIUM;So;0;L;;;;;N;;;;; 1D941;SIGNWRITING MOVEMENT-WALLPLANE CHECK LARGE;So;0;L;;;;;N;;;;; 1D942;SIGNWRITING MOVEMENT-WALLPLANE BOX SMALL;So;0;L;;;;;N;;;;; 1D943;SIGNWRITING MOVEMENT-WALLPLANE BOX MEDIUM;So;0;L;;;;;N;;;;; 1D944;SIGNWRITING MOVEMENT-WALLPLANE BOX LARGE;So;0;L;;;;;N;;;;; 1D945;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;; 1D946;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;; 1D947;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;; 1D948;SIGNWRITING MOVEMENT-WALLPLANE PEAKS SMALL;So;0;L;;;;;N;;;;; 1D949;SIGNWRITING MOVEMENT-WALLPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;; 1D94A;SIGNWRITING MOVEMENT-WALLPLANE PEAKS LARGE;So;0;L;;;;;N;;;;; 1D94B;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; 1D94C;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D94D;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;; 1D94E;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; 1D94F;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D950;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; 1D951;SIGNWRITING TRAVEL-WALLPLANE SHAKING;So;0;L;;;;;N;;;;; 1D952;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL SINGLE;So;0;L;;;;;N;;;;; 1D953;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL DOUBLE;So;0;L;;;;;N;;;;; 1D954;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL TRIPLE;So;0;L;;;;;N;;;;; 1D955;SIGNWRITING MOVEMENT-DIAGONAL AWAY SMALL;So;0;L;;;;;N;;;;; 1D956;SIGNWRITING MOVEMENT-DIAGONAL AWAY MEDIUM;So;0;L;;;;;N;;;;; 1D957;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGE;So;0;L;;;;;N;;;;; 1D958;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGEST;So;0;L;;;;;N;;;;; 1D959;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS SMALL;So;0;L;;;;;N;;;;; 1D95A;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS MEDIUM;So;0;L;;;;;N;;;;; 1D95B;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGE;So;0;L;;;;;N;;;;; 1D95C;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGEST;So;0;L;;;;;N;;;;; 1D95D;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY SMALL;So;0;L;;;;;N;;;;; 1D95E;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY MEDIUM;So;0;L;;;;;N;;;;; 1D95F;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGE;So;0;L;;;;;N;;;;; 1D960;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGEST;So;0;L;;;;;N;;;;; 1D961;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS SMALL;So;0;L;;;;;N;;;;; 1D962;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS MEDIUM;So;0;L;;;;;N;;;;; 1D963;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGE;So;0;L;;;;;N;;;;; 1D964;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGEST;So;0;L;;;;;N;;;;; 1D965;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;; 1D966;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;; 1D967;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;; 1D968;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;; 1D969;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D96A;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;; 1D96B;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D96C;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;; 1D96D;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; 1D96E;SIGNWRITING MOVEMENT-FLOORPLANE CROSS;So;0;L;;;;;N;;;;; 1D96F;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;; 1D970;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;; 1D971;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING MOVEMENT;So;0;L;;;;;N;;;;; 1D972;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; 1D973;SIGNWRITING MOVEMENT-FLOORPLANE BEND;So;0;L;;;;;N;;;;; 1D974;SIGNWRITING MOVEMENT-FLOORPLANE CORNER SMALL;So;0;L;;;;;N;;;;; 1D975;SIGNWRITING MOVEMENT-FLOORPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;; 1D976;SIGNWRITING MOVEMENT-FLOORPLANE CORNER LARGE;So;0;L;;;;;N;;;;; 1D977;SIGNWRITING MOVEMENT-FLOORPLANE CHECK;So;0;L;;;;;N;;;;; 1D978;SIGNWRITING MOVEMENT-FLOORPLANE BOX SMALL;So;0;L;;;;;N;;;;; 1D979;SIGNWRITING MOVEMENT-FLOORPLANE BOX MEDIUM;So;0;L;;;;;N;;;;; 1D97A;SIGNWRITING MOVEMENT-FLOORPLANE BOX LARGE;So;0;L;;;;;N;;;;; 1D97B;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;; 1D97C;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;; 1D97D;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;; 1D97E;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS SMALL;So;0;L;;;;;N;;;;; 1D97F;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;; 1D980;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS LARGE;So;0;L;;;;;N;;;;; 1D981;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; 1D982;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D983;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; 1D984;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; 1D985;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D986;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;; 1D987;SIGNWRITING TRAVEL-FLOORPLANE SHAKING;So;0;L;;;;;N;;;;; 1D988;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER SMALL;So;0;L;;;;;N;;;;; 1D989;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER MEDIUM;So;0;L;;;;;N;;;;; 1D98A;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGE;So;0;L;;;;;N;;;;; 1D98B;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGEST;So;0;L;;;;;N;;;;; 1D98C;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE SMALL;So;0;L;;;;;N;;;;; 1D98D;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE MEDIUM;So;0;L;;;;;N;;;;; 1D98E;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGE;So;0;L;;;;;N;;;;; 1D98F;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGEST;So;0;L;;;;;N;;;;; 1D990;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE SMALL;So;0;L;;;;;N;;;;; 1D991;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE MEDIUM;So;0;L;;;;;N;;;;; 1D992;SIGNWRITING MOVEMENT-WALLPLANE HUMP SMALL;So;0;L;;;;;N;;;;; 1D993;SIGNWRITING MOVEMENT-WALLPLANE HUMP MEDIUM;So;0;L;;;;;N;;;;; 1D994;SIGNWRITING MOVEMENT-WALLPLANE HUMP LARGE;So;0;L;;;;;N;;;;; 1D995;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL;So;0;L;;;;;N;;;;; 1D996;SIGNWRITING MOVEMENT-WALLPLANE LOOP MEDIUM;So;0;L;;;;;N;;;;; 1D997;SIGNWRITING MOVEMENT-WALLPLANE LOOP LARGE;So;0;L;;;;;N;;;;; 1D998;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D999;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE SMALL;So;0;L;;;;;N;;;;; 1D99A;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE MEDIUM;So;0;L;;;;;N;;;;; 1D99B;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE LARGE;So;0;L;;;;;N;;;;; 1D99C;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE SMALL;So;0;L;;;;;N;;;;; 1D99D;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE MEDIUM;So;0;L;;;;;N;;;;; 1D99E;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE LARGE;So;0;L;;;;;N;;;;; 1D99F;SIGNWRITING MOVEMENT-WALLPLANE CURVE THEN STRAIGHT;So;0;L;;;;;N;;;;; 1D9A0;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS SMALL;So;0;L;;;;;N;;;;; 1D9A1;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS MEDIUM;So;0;L;;;;;N;;;;; 1D9A2;SIGNWRITING ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; 1D9A3;SIGNWRITING ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D9A4;SIGNWRITING ROTATION-WALLPLANE ALTERNATE;So;0;L;;;;;N;;;;; 1D9A5;SIGNWRITING MOVEMENT-WALLPLANE SHAKING;So;0;L;;;;;N;;;;; 1D9A6;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9A7;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9A8;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9A9;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9AA;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9AB;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9AC;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING FRONT WALL;So;0;L;;;;;N;;;;; 1D9AD;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING CHEST;So;0;L;;;;;N;;;;; 1D9AE;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING CHEST;So;0;L;;;;;N;;;;; 1D9AF;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING CHEST;So;0;L;;;;;N;;;;; 1D9B0;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING CHEST;So;0;L;;;;;N;;;;; 1D9B1;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING CHEST;So;0;L;;;;;N;;;;; 1D9B2;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING CHEST;So;0;L;;;;;N;;;;; 1D9B3;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING CHEST;So;0;L;;;;;N;;;;; 1D9B4;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH SMALL;So;0;L;;;;;N;;;;; 1D9B5;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH MEDIUM;So;0;L;;;;;N;;;;; 1D9B6;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH LARGE;So;0;L;;;;;N;;;;; 1D9B7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;; 1D9B8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;; 1D9B9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9BA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;; 1D9BB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL TRIPLE;So;0;L;;;;;N;;;;; 1D9BC;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE TRIPLE;So;0;L;;;;;N;;;;; 1D9BD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL SINGLE;So;0;L;;;;;N;;;;; 1D9BE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE SINGLE;So;0;L;;;;;N;;;;; 1D9BF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9C0;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;; 1D9C1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;; 1D9C2;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;; 1D9C3;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING CEILING;So;0;L;;;;;N;;;;; 1D9C4;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING CEILING;So;0;L;;;;;N;;;;; 1D9C5;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING CEILING;So;0;L;;;;;N;;;;; 1D9C6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;; 1D9C7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;; 1D9C8;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9C9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;; 1D9CA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE SMALL TRIPLE;So;0;L;;;;;N;;;;; 1D9CB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE LARGE TRIPLE;So;0;L;;;;;N;;;;; 1D9CC;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL SINGLE;So;0;L;;;;;N;;;;; 1D9CD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE SINGLE;So;0;L;;;;;N;;;;; 1D9CE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9CF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;; 1D9D0;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;; 1D9D1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;; 1D9D2;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING FLOOR;So;0;L;;;;;N;;;;; 1D9D3;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING FLOOR;So;0;L;;;;;N;;;;; 1D9D4;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING FLOOR;So;0;L;;;;;N;;;;; 1D9D5;SIGNWRITING MOVEMENT-FLOORPLANE CURVE SMALL;So;0;L;;;;;N;;;;; 1D9D6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE MEDIUM;So;0;L;;;;;N;;;;; 1D9D7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGE;So;0;L;;;;;N;;;;; 1D9D8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGEST;So;0;L;;;;;N;;;;; 1D9D9;SIGNWRITING MOVEMENT-FLOORPLANE CURVE COMBINED;So;0;L;;;;;N;;;;; 1D9DA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP SMALL;So;0;L;;;;;N;;;;; 1D9DB;SIGNWRITING MOVEMENT-FLOORPLANE LOOP SMALL;So;0;L;;;;;N;;;;; 1D9DC;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SNAKE;So;0;L;;;;;N;;;;; 1D9DD;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SMALL;So;0;L;;;;;N;;;;; 1D9DE;SIGNWRITING MOVEMENT-FLOORPLANE WAVE LARGE;So;0;L;;;;;N;;;;; 1D9DF;SIGNWRITING ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; 1D9E0;SIGNWRITING ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; 1D9E1;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; 1D9E2;SIGNWRITING MOVEMENT-FLOORPLANE SHAKING PARALLEL;So;0;L;;;;;N;;;;; 1D9E3;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL SINGLE;So;0;L;;;;;N;;;;; 1D9E4;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM SINGLE;So;0;L;;;;;N;;;;; 1D9E5;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9E6;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM DOUBLE;So;0;L;;;;;N;;;;; 1D9E7;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL SINGLE;So;0;L;;;;;N;;;;; 1D9E8;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM SINGLE;So;0;L;;;;;N;;;;; 1D9E9;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE SINGLE;So;0;L;;;;;N;;;;; 1D9EA;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL DOUBLE;So;0;L;;;;;N;;;;; 1D9EB;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM DOUBLE;So;0;L;;;;;N;;;;; 1D9EC;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE DOUBLE;So;0;L;;;;;N;;;;; 1D9ED;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT SINGLE;So;0;L;;;;;N;;;;; 1D9EE;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT DOUBLE;So;0;L;;;;;N;;;;; 1D9EF;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL SINGLE;So;0;L;;;;;N;;;;; 1D9F0;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL DOUBLE;So;0;L;;;;;N;;;;; 1D9F1;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES SINGLE;So;0;L;;;;;N;;;;; 1D9F2;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES DOUBLE;So;0;L;;;;;N;;;;; 1D9F3;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL SINGLE;So;0;L;;;;;N;;;;; 1D9F4;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL DOUBLE;So;0;L;;;;;N;;;;; 1D9F5;SIGNWRITING DYNAMIC ARROWHEAD SMALL;So;0;L;;;;;N;;;;; 1D9F6;SIGNWRITING DYNAMIC ARROWHEAD LARGE;So;0;L;;;;;N;;;;; 1D9F7;SIGNWRITING DYNAMIC FAST;So;0;L;;;;;N;;;;; 1D9F8;SIGNWRITING DYNAMIC SLOW;So;0;L;;;;;N;;;;; 1D9F9;SIGNWRITING DYNAMIC TENSE;So;0;L;;;;;N;;;;; 1D9FA;SIGNWRITING DYNAMIC RELAXED;So;0;L;;;;;N;;;;; 1D9FB;SIGNWRITING DYNAMIC SIMULTANEOUS;So;0;L;;;;;N;;;;; 1D9FC;SIGNWRITING DYNAMIC SIMULTANEOUS ALTERNATING;So;0;L;;;;;N;;;;; 1D9FD;SIGNWRITING DYNAMIC EVERY OTHER TIME;So;0;L;;;;;N;;;;; 1D9FE;SIGNWRITING DYNAMIC GRADUAL;So;0;L;;;;;N;;;;; 1D9FF;SIGNWRITING HEAD;So;0;L;;;;;N;;;;; 1DA00;SIGNWRITING HEAD RIM;Mn;0;NSM;;;;;N;;;;; 1DA01;SIGNWRITING HEAD MOVEMENT-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; 1DA02;SIGNWRITING HEAD MOVEMENT-WALLPLANE TILT;Mn;0;NSM;;;;;N;;;;; 1DA03;SIGNWRITING HEAD MOVEMENT-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; 1DA04;SIGNWRITING HEAD MOVEMENT-WALLPLANE CURVE;Mn;0;NSM;;;;;N;;;;; 1DA05;SIGNWRITING HEAD MOVEMENT-FLOORPLANE CURVE;Mn;0;NSM;;;;;N;;;;; 1DA06;SIGNWRITING HEAD MOVEMENT CIRCLE;Mn;0;NSM;;;;;N;;;;; 1DA07;SIGNWRITING FACE DIRECTION POSITION NOSE FORWARD TILTING;Mn;0;NSM;;;;;N;;;;; 1DA08;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN;Mn;0;NSM;;;;;N;;;;; 1DA09;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN TILTING;Mn;0;NSM;;;;;N;;;;; 1DA0A;SIGNWRITING EYEBROWS STRAIGHT UP;Mn;0;NSM;;;;;N;;;;; 1DA0B;SIGNWRITING EYEBROWS STRAIGHT NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA0C;SIGNWRITING EYEBROWS STRAIGHT DOWN;Mn;0;NSM;;;;;N;;;;; 1DA0D;SIGNWRITING DREAMY EYEBROWS NEUTRAL DOWN;Mn;0;NSM;;;;;N;;;;; 1DA0E;SIGNWRITING DREAMY EYEBROWS DOWN NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA0F;SIGNWRITING DREAMY EYEBROWS UP NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA10;SIGNWRITING DREAMY EYEBROWS NEUTRAL UP;Mn;0;NSM;;;;;N;;;;; 1DA11;SIGNWRITING FOREHEAD NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA12;SIGNWRITING FOREHEAD CONTACT;Mn;0;NSM;;;;;N;;;;; 1DA13;SIGNWRITING FOREHEAD WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA14;SIGNWRITING EYES OPEN;Mn;0;NSM;;;;;N;;;;; 1DA15;SIGNWRITING EYES SQUEEZED;Mn;0;NSM;;;;;N;;;;; 1DA16;SIGNWRITING EYES CLOSED;Mn;0;NSM;;;;;N;;;;; 1DA17;SIGNWRITING EYE BLINK SINGLE;Mn;0;NSM;;;;;N;;;;; 1DA18;SIGNWRITING EYE BLINK MULTIPLE;Mn;0;NSM;;;;;N;;;;; 1DA19;SIGNWRITING EYES HALF OPEN;Mn;0;NSM;;;;;N;;;;; 1DA1A;SIGNWRITING EYES WIDE OPEN;Mn;0;NSM;;;;;N;;;;; 1DA1B;SIGNWRITING EYES HALF CLOSED;Mn;0;NSM;;;;;N;;;;; 1DA1C;SIGNWRITING EYES WIDENING MOVEMENT;Mn;0;NSM;;;;;N;;;;; 1DA1D;SIGNWRITING EYE WINK;Mn;0;NSM;;;;;N;;;;; 1DA1E;SIGNWRITING EYELASHES UP;Mn;0;NSM;;;;;N;;;;; 1DA1F;SIGNWRITING EYELASHES DOWN;Mn;0;NSM;;;;;N;;;;; 1DA20;SIGNWRITING EYELASHES FLUTTERING;Mn;0;NSM;;;;;N;;;;; 1DA21;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; 1DA22;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;; 1DA23;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;; 1DA24;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; 1DA25;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;; 1DA26;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;; 1DA27;SIGNWRITING EYEGAZE-WALLPLANE CURVED;Mn;0;NSM;;;;;N;;;;; 1DA28;SIGNWRITING EYEGAZE-FLOORPLANE CURVED;Mn;0;NSM;;;;;N;;;;; 1DA29;SIGNWRITING EYEGAZE-WALLPLANE CIRCLING;Mn;0;NSM;;;;;N;;;;; 1DA2A;SIGNWRITING CHEEKS PUFFED;Mn;0;NSM;;;;;N;;;;; 1DA2B;SIGNWRITING CHEEKS NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA2C;SIGNWRITING CHEEKS SUCKED;Mn;0;NSM;;;;;N;;;;; 1DA2D;SIGNWRITING TENSE CHEEKS HIGH;Mn;0;NSM;;;;;N;;;;; 1DA2E;SIGNWRITING TENSE CHEEKS MIDDLE;Mn;0;NSM;;;;;N;;;;; 1DA2F;SIGNWRITING TENSE CHEEKS LOW;Mn;0;NSM;;;;;N;;;;; 1DA30;SIGNWRITING EARS;Mn;0;NSM;;;;;N;;;;; 1DA31;SIGNWRITING NOSE NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA32;SIGNWRITING NOSE CONTACT;Mn;0;NSM;;;;;N;;;;; 1DA33;SIGNWRITING NOSE WRINKLES;Mn;0;NSM;;;;;N;;;;; 1DA34;SIGNWRITING NOSE WIGGLES;Mn;0;NSM;;;;;N;;;;; 1DA35;SIGNWRITING AIR BLOWING OUT;Mn;0;NSM;;;;;N;;;;; 1DA36;SIGNWRITING AIR SUCKING IN;Mn;0;NSM;;;;;N;;;;; 1DA37;SIGNWRITING AIR BLOW SMALL ROTATIONS;So;0;L;;;;;N;;;;; 1DA38;SIGNWRITING AIR SUCK SMALL ROTATIONS;So;0;L;;;;;N;;;;; 1DA39;SIGNWRITING BREATH INHALE;So;0;L;;;;;N;;;;; 1DA3A;SIGNWRITING BREATH EXHALE;So;0;L;;;;;N;;;;; 1DA3B;SIGNWRITING MOUTH CLOSED NEUTRAL;Mn;0;NSM;;;;;N;;;;; 1DA3C;SIGNWRITING MOUTH CLOSED FORWARD;Mn;0;NSM;;;;;N;;;;; 1DA3D;SIGNWRITING MOUTH CLOSED CONTACT;Mn;0;NSM;;;;;N;;;;; 1DA3E;SIGNWRITING MOUTH SMILE;Mn;0;NSM;;;;;N;;;;; 1DA3F;SIGNWRITING MOUTH SMILE WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA40;SIGNWRITING MOUTH SMILE OPEN;Mn;0;NSM;;;;;N;;;;; 1DA41;SIGNWRITING MOUTH FROWN;Mn;0;NSM;;;;;N;;;;; 1DA42;SIGNWRITING MOUTH FROWN WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA43;SIGNWRITING MOUTH FROWN OPEN;Mn;0;NSM;;;;;N;;;;; 1DA44;SIGNWRITING MOUTH OPEN CIRCLE;Mn;0;NSM;;;;;N;;;;; 1DA45;SIGNWRITING MOUTH OPEN FORWARD;Mn;0;NSM;;;;;N;;;;; 1DA46;SIGNWRITING MOUTH OPEN WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA47;SIGNWRITING MOUTH OPEN OVAL;Mn;0;NSM;;;;;N;;;;; 1DA48;SIGNWRITING MOUTH OPEN OVAL WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA49;SIGNWRITING MOUTH OPEN OVAL YAWN;Mn;0;NSM;;;;;N;;;;; 1DA4A;SIGNWRITING MOUTH OPEN RECTANGLE;Mn;0;NSM;;;;;N;;;;; 1DA4B;SIGNWRITING MOUTH OPEN RECTANGLE WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA4C;SIGNWRITING MOUTH OPEN RECTANGLE YAWN;Mn;0;NSM;;;;;N;;;;; 1DA4D;SIGNWRITING MOUTH KISS;Mn;0;NSM;;;;;N;;;;; 1DA4E;SIGNWRITING MOUTH KISS FORWARD;Mn;0;NSM;;;;;N;;;;; 1DA4F;SIGNWRITING MOUTH KISS WRINKLED;Mn;0;NSM;;;;;N;;;;; 1DA50;SIGNWRITING MOUTH TENSE;Mn;0;NSM;;;;;N;;;;; 1DA51;SIGNWRITING MOUTH TENSE FORWARD;Mn;0;NSM;;;;;N;;;;; 1DA52;SIGNWRITING MOUTH TENSE SUCKED;Mn;0;NSM;;;;;N;;;;; 1DA53;SIGNWRITING LIPS PRESSED TOGETHER;Mn;0;NSM;;;;;N;;;;; 1DA54;SIGNWRITING LIP LOWER OVER UPPER;Mn;0;NSM;;;;;N;;;;; 1DA55;SIGNWRITING LIP UPPER OVER LOWER;Mn;0;NSM;;;;;N;;;;; 1DA56;SIGNWRITING MOUTH CORNERS;Mn;0;NSM;;;;;N;;;;; 1DA57;SIGNWRITING MOUTH WRINKLES SINGLE;Mn;0;NSM;;;;;N;;;;; 1DA58;SIGNWRITING MOUTH WRINKLES DOUBLE;Mn;0;NSM;;;;;N;;;;; 1DA59;SIGNWRITING TONGUE STICKING OUT FAR;Mn;0;NSM;;;;;N;;;;; 1DA5A;SIGNWRITING TONGUE LICKING LIPS;Mn;0;NSM;;;;;N;;;;; 1DA5B;SIGNWRITING TONGUE TIP BETWEEN LIPS;Mn;0;NSM;;;;;N;;;;; 1DA5C;SIGNWRITING TONGUE TIP TOUCHING INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;; 1DA5D;SIGNWRITING TONGUE INSIDE MOUTH RELAXED;Mn;0;NSM;;;;;N;;;;; 1DA5E;SIGNWRITING TONGUE MOVES AGAINST CHEEK;Mn;0;NSM;;;;;N;;;;; 1DA5F;SIGNWRITING TONGUE CENTRE STICKING OUT;Mn;0;NSM;;;;;N;;;;; 1DA60;SIGNWRITING TONGUE CENTRE INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;; 1DA61;SIGNWRITING TEETH;Mn;0;NSM;;;;;N;;;;; 1DA62;SIGNWRITING TEETH MOVEMENT;Mn;0;NSM;;;;;N;;;;; 1DA63;SIGNWRITING TEETH ON TONGUE;Mn;0;NSM;;;;;N;;;;; 1DA64;SIGNWRITING TEETH ON TONGUE MOVEMENT;Mn;0;NSM;;;;;N;;;;; 1DA65;SIGNWRITING TEETH ON LIPS;Mn;0;NSM;;;;;N;;;;; 1DA66;SIGNWRITING TEETH ON LIPS MOVEMENT;Mn;0;NSM;;;;;N;;;;; 1DA67;SIGNWRITING TEETH BITE LIPS;Mn;0;NSM;;;;;N;;;;; 1DA68;SIGNWRITING MOVEMENT-WALLPLANE JAW;Mn;0;NSM;;;;;N;;;;; 1DA69;SIGNWRITING MOVEMENT-FLOORPLANE JAW;Mn;0;NSM;;;;;N;;;;; 1DA6A;SIGNWRITING NECK;Mn;0;NSM;;;;;N;;;;; 1DA6B;SIGNWRITING HAIR;Mn;0;NSM;;;;;N;;;;; 1DA6C;SIGNWRITING EXCITEMENT;Mn;0;NSM;;;;;N;;;;; 1DA6D;SIGNWRITING SHOULDER HIP SPINE;So;0;L;;;;;N;;;;; 1DA6E;SIGNWRITING SHOULDER HIP POSITIONS;So;0;L;;;;;N;;;;; 1DA6F;SIGNWRITING WALLPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;; 1DA70;SIGNWRITING FLOORPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;; 1DA71;SIGNWRITING SHOULDER TILTING FROM WAIST;So;0;L;;;;;N;;;;; 1DA72;SIGNWRITING TORSO-WALLPLANE STRAIGHT STRETCH;So;0;L;;;;;N;;;;; 1DA73;SIGNWRITING TORSO-WALLPLANE CURVED BEND;So;0;L;;;;;N;;;;; 1DA74;SIGNWRITING TORSO-FLOORPLANE TWISTING;So;0;L;;;;;N;;;;; 1DA75;SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS;Mn;0;NSM;;;;;N;;;;; 1DA76;SIGNWRITING LIMB COMBINATION;So;0;L;;;;;N;;;;; 1DA77;SIGNWRITING LIMB LENGTH-1;So;0;L;;;;;N;;;;; 1DA78;SIGNWRITING LIMB LENGTH-2;So;0;L;;;;;N;;;;; 1DA79;SIGNWRITING LIMB LENGTH-3;So;0;L;;;;;N;;;;; 1DA7A;SIGNWRITING LIMB LENGTH-4;So;0;L;;;;;N;;;;; 1DA7B;SIGNWRITING LIMB LENGTH-5;So;0;L;;;;;N;;;;; 1DA7C;SIGNWRITING LIMB LENGTH-6;So;0;L;;;;;N;;;;; 1DA7D;SIGNWRITING LIMB LENGTH-7;So;0;L;;;;;N;;;;; 1DA7E;SIGNWRITING FINGER;So;0;L;;;;;N;;;;; 1DA7F;SIGNWRITING LOCATION-WALLPLANE SPACE;So;0;L;;;;;N;;;;; 1DA80;SIGNWRITING LOCATION-FLOORPLANE SPACE;So;0;L;;;;;N;;;;; 1DA81;SIGNWRITING LOCATION HEIGHT;So;0;L;;;;;N;;;;; 1DA82;SIGNWRITING LOCATION WIDTH;So;0;L;;;;;N;;;;; 1DA83;SIGNWRITING LOCATION DEPTH;So;0;L;;;;;N;;;;; 1DA84;SIGNWRITING LOCATION HEAD NECK;Mn;0;NSM;;;;;N;;;;; 1DA85;SIGNWRITING LOCATION TORSO;So;0;L;;;;;N;;;;; 1DA86;SIGNWRITING LOCATION LIMBS DIGITS;So;0;L;;;;;N;;;;; 1DA87;SIGNWRITING COMMA;Po;0;L;;;;;N;;;;; 1DA88;SIGNWRITING FULL STOP;Po;0;L;;;;;N;;;;; 1DA89;SIGNWRITING SEMICOLON;Po;0;L;;;;;N;;;;; 1DA8A;SIGNWRITING COLON;Po;0;L;;;;;N;;;;; 1DA8B;SIGNWRITING PARENTHESIS;Po;0;L;;;;;N;;;;; 1DA9B;SIGNWRITING FILL MODIFIER-2;Mn;0;NSM;;;;;N;;;;; 1DA9C;SIGNWRITING FILL MODIFIER-3;Mn;0;NSM;;;;;N;;;;; 1DA9D;SIGNWRITING FILL MODIFIER-4;Mn;0;NSM;;;;;N;;;;; 1DA9E;SIGNWRITING FILL MODIFIER-5;Mn;0;NSM;;;;;N;;;;; 1DA9F;SIGNWRITING FILL MODIFIER-6;Mn;0;NSM;;;;;N;;;;; 1DAA1;SIGNWRITING ROTATION MODIFIER-2;Mn;0;NSM;;;;;N;;;;; 1DAA2;SIGNWRITING ROTATION MODIFIER-3;Mn;0;NSM;;;;;N;;;;; 1DAA3;SIGNWRITING ROTATION MODIFIER-4;Mn;0;NSM;;;;;N;;;;; 1DAA4;SIGNWRITING ROTATION MODIFIER-5;Mn;0;NSM;;;;;N;;;;; 1DAA5;SIGNWRITING ROTATION MODIFIER-6;Mn;0;NSM;;;;;N;;;;; 1DAA6;SIGNWRITING ROTATION MODIFIER-7;Mn;0;NSM;;;;;N;;;;; 1DAA7;SIGNWRITING ROTATION MODIFIER-8;Mn;0;NSM;;;;;N;;;;; 1DAA8;SIGNWRITING ROTATION MODIFIER-9;Mn;0;NSM;;;;;N;;;;; 1DAA9;SIGNWRITING ROTATION MODIFIER-10;Mn;0;NSM;;;;;N;;;;; 1DAAA;SIGNWRITING ROTATION MODIFIER-11;Mn;0;NSM;;;;;N;;;;; 1DAAB;SIGNWRITING ROTATION MODIFIER-12;Mn;0;NSM;;;;;N;;;;; 1DAAC;SIGNWRITING ROTATION MODIFIER-13;Mn;0;NSM;;;;;N;;;;; 1DAAD;SIGNWRITING ROTATION MODIFIER-14;Mn;0;NSM;;;;;N;;;;; 1DAAE;SIGNWRITING ROTATION MODIFIER-15;Mn;0;NSM;;;;;N;;;;; 1DAAF;SIGNWRITING ROTATION MODIFIER-16;Mn;0;NSM;;;;;N;;;;; 1E000;COMBINING GLAGOLITIC LETTER AZU;Mn;230;NSM;;;;;N;;;;; 1E001;COMBINING GLAGOLITIC LETTER BUKY;Mn;230;NSM;;;;;N;;;;; 1E002;COMBINING GLAGOLITIC LETTER VEDE;Mn;230;NSM;;;;;N;;;;; 1E003;COMBINING GLAGOLITIC LETTER GLAGOLI;Mn;230;NSM;;;;;N;;;;; 1E004;COMBINING GLAGOLITIC LETTER DOBRO;Mn;230;NSM;;;;;N;;;;; 1E005;COMBINING GLAGOLITIC LETTER YESTU;Mn;230;NSM;;;;;N;;;;; 1E006;COMBINING GLAGOLITIC LETTER ZHIVETE;Mn;230;NSM;;;;;N;;;;; 1E008;COMBINING GLAGOLITIC LETTER ZEMLJA;Mn;230;NSM;;;;;N;;;;; 1E009;COMBINING GLAGOLITIC LETTER IZHE;Mn;230;NSM;;;;;N;;;;; 1E00A;COMBINING GLAGOLITIC LETTER INITIAL IZHE;Mn;230;NSM;;;;;N;;;;; 1E00B;COMBINING GLAGOLITIC LETTER I;Mn;230;NSM;;;;;N;;;;; 1E00C;COMBINING GLAGOLITIC LETTER DJERVI;Mn;230;NSM;;;;;N;;;;; 1E00D;COMBINING GLAGOLITIC LETTER KAKO;Mn;230;NSM;;;;;N;;;;; 1E00E;COMBINING GLAGOLITIC LETTER LJUDIJE;Mn;230;NSM;;;;;N;;;;; 1E00F;COMBINING GLAGOLITIC LETTER MYSLITE;Mn;230;NSM;;;;;N;;;;; 1E010;COMBINING GLAGOLITIC LETTER NASHI;Mn;230;NSM;;;;;N;;;;; 1E011;COMBINING GLAGOLITIC LETTER ONU;Mn;230;NSM;;;;;N;;;;; 1E012;COMBINING GLAGOLITIC LETTER POKOJI;Mn;230;NSM;;;;;N;;;;; 1E013;COMBINING GLAGOLITIC LETTER RITSI;Mn;230;NSM;;;;;N;;;;; 1E014;COMBINING GLAGOLITIC LETTER SLOVO;Mn;230;NSM;;;;;N;;;;; 1E015;COMBINING GLAGOLITIC LETTER TVRIDO;Mn;230;NSM;;;;;N;;;;; 1E016;COMBINING GLAGOLITIC LETTER UKU;Mn;230;NSM;;;;;N;;;;; 1E017;COMBINING GLAGOLITIC LETTER FRITU;Mn;230;NSM;;;;;N;;;;; 1E018;COMBINING GLAGOLITIC LETTER HERU;Mn;230;NSM;;;;;N;;;;; 1E01B;COMBINING GLAGOLITIC LETTER SHTA;Mn;230;NSM;;;;;N;;;;; 1E01C;COMBINING GLAGOLITIC LETTER TSI;Mn;230;NSM;;;;;N;;;;; 1E01D;COMBINING GLAGOLITIC LETTER CHRIVI;Mn;230;NSM;;;;;N;;;;; 1E01E;COMBINING GLAGOLITIC LETTER SHA;Mn;230;NSM;;;;;N;;;;; 1E01F;COMBINING GLAGOLITIC LETTER YERU;Mn;230;NSM;;;;;N;;;;; 1E020;COMBINING GLAGOLITIC LETTER YERI;Mn;230;NSM;;;;;N;;;;; 1E021;COMBINING GLAGOLITIC LETTER YATI;Mn;230;NSM;;;;;N;;;;; 1E023;COMBINING GLAGOLITIC LETTER YU;Mn;230;NSM;;;;;N;;;;; 1E024;COMBINING GLAGOLITIC LETTER SMALL YUS;Mn;230;NSM;;;;;N;;;;; 1E026;COMBINING GLAGOLITIC LETTER YO;Mn;230;NSM;;;;;N;;;;; 1E027;COMBINING GLAGOLITIC LETTER IOTATED SMALL YUS;Mn;230;NSM;;;;;N;;;;; 1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;; 1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;; 1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;; 1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;; 1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;; 1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;; 1E803;MENDE KIKAKUI SYLLABLE M065 KEE;Lo;0;R;;;;;N;;;;; 1E804;MENDE KIKAKUI SYLLABLE M095 KE;Lo;0;R;;;;;N;;;;; 1E805;MENDE KIKAKUI SYLLABLE M076 KOO;Lo;0;R;;;;;N;;;;; 1E806;MENDE KIKAKUI SYLLABLE M048 KO;Lo;0;R;;;;;N;;;;; 1E807;MENDE KIKAKUI SYLLABLE M179 KUA;Lo;0;R;;;;;N;;;;; 1E808;MENDE KIKAKUI SYLLABLE M004 WI;Lo;0;R;;;;;N;;;;; 1E809;MENDE KIKAKUI SYLLABLE M005 WA;Lo;0;R;;;;;N;;;;; 1E80A;MENDE KIKAKUI SYLLABLE M006 WU;Lo;0;R;;;;;N;;;;; 1E80B;MENDE KIKAKUI SYLLABLE M126 WEE;Lo;0;R;;;;;N;;;;; 1E80C;MENDE KIKAKUI SYLLABLE M118 WE;Lo;0;R;;;;;N;;;;; 1E80D;MENDE KIKAKUI SYLLABLE M114 WOO;Lo;0;R;;;;;N;;;;; 1E80E;MENDE KIKAKUI SYLLABLE M045 WO;Lo;0;R;;;;;N;;;;; 1E80F;MENDE KIKAKUI SYLLABLE M194 WUI;Lo;0;R;;;;;N;;;;; 1E810;MENDE KIKAKUI SYLLABLE M143 WEI;Lo;0;R;;;;;N;;;;; 1E811;MENDE KIKAKUI SYLLABLE M061 WVI;Lo;0;R;;;;;N;;;;; 1E812;MENDE KIKAKUI SYLLABLE M049 WVA;Lo;0;R;;;;;N;;;;; 1E813;MENDE KIKAKUI SYLLABLE M139 WVE;Lo;0;R;;;;;N;;;;; 1E814;MENDE KIKAKUI SYLLABLE M007 MIN;Lo;0;R;;;;;N;;;;; 1E815;MENDE KIKAKUI SYLLABLE M008 MAN;Lo;0;R;;;;;N;;;;; 1E816;MENDE KIKAKUI SYLLABLE M009 MUN;Lo;0;R;;;;;N;;;;; 1E817;MENDE KIKAKUI SYLLABLE M059 MEN;Lo;0;R;;;;;N;;;;; 1E818;MENDE KIKAKUI SYLLABLE M094 MON;Lo;0;R;;;;;N;;;;; 1E819;MENDE KIKAKUI SYLLABLE M154 MUAN;Lo;0;R;;;;;N;;;;; 1E81A;MENDE KIKAKUI SYLLABLE M189 MUEN;Lo;0;R;;;;;N;;;;; 1E81B;MENDE KIKAKUI SYLLABLE M010 BI;Lo;0;R;;;;;N;;;;; 1E81C;MENDE KIKAKUI SYLLABLE M011 BA;Lo;0;R;;;;;N;;;;; 1E81D;MENDE KIKAKUI SYLLABLE M012 BU;Lo;0;R;;;;;N;;;;; 1E81E;MENDE KIKAKUI SYLLABLE M150 BEE;Lo;0;R;;;;;N;;;;; 1E81F;MENDE KIKAKUI SYLLABLE M097 BE;Lo;0;R;;;;;N;;;;; 1E820;MENDE KIKAKUI SYLLABLE M103 BOO;Lo;0;R;;;;;N;;;;; 1E821;MENDE KIKAKUI SYLLABLE M138 BO;Lo;0;R;;;;;N;;;;; 1E822;MENDE KIKAKUI SYLLABLE M013 I;Lo;0;R;;;;;N;;;;; 1E823;MENDE KIKAKUI SYLLABLE M014 A;Lo;0;R;;;;;N;;;;; 1E824;MENDE KIKAKUI SYLLABLE M015 U;Lo;0;R;;;;;N;;;;; 1E825;MENDE KIKAKUI SYLLABLE M163 EE;Lo;0;R;;;;;N;;;;; 1E826;MENDE KIKAKUI SYLLABLE M100 E;Lo;0;R;;;;;N;;;;; 1E827;MENDE KIKAKUI SYLLABLE M165 OO;Lo;0;R;;;;;N;;;;; 1E828;MENDE KIKAKUI SYLLABLE M147 O;Lo;0;R;;;;;N;;;;; 1E829;MENDE KIKAKUI SYLLABLE M137 EI;Lo;0;R;;;;;N;;;;; 1E82A;MENDE KIKAKUI SYLLABLE M131 IN;Lo;0;R;;;;;N;;;;; 1E82B;MENDE KIKAKUI SYLLABLE M135 IN;Lo;0;R;;;;;N;;;;; 1E82C;MENDE KIKAKUI SYLLABLE M195 AN;Lo;0;R;;;;;N;;;;; 1E82D;MENDE KIKAKUI SYLLABLE M178 EN;Lo;0;R;;;;;N;;;;; 1E82E;MENDE KIKAKUI SYLLABLE M019 SI;Lo;0;R;;;;;N;;;;; 1E82F;MENDE KIKAKUI SYLLABLE M020 SA;Lo;0;R;;;;;N;;;;; 1E830;MENDE KIKAKUI SYLLABLE M021 SU;Lo;0;R;;;;;N;;;;; 1E831;MENDE KIKAKUI SYLLABLE M162 SEE;Lo;0;R;;;;;N;;;;; 1E832;MENDE KIKAKUI SYLLABLE M116 SE;Lo;0;R;;;;;N;;;;; 1E833;MENDE KIKAKUI SYLLABLE M136 SOO;Lo;0;R;;;;;N;;;;; 1E834;MENDE KIKAKUI SYLLABLE M079 SO;Lo;0;R;;;;;N;;;;; 1E835;MENDE KIKAKUI SYLLABLE M196 SIA;Lo;0;R;;;;;N;;;;; 1E836;MENDE KIKAKUI SYLLABLE M025 LI;Lo;0;R;;;;;N;;;;; 1E837;MENDE KIKAKUI SYLLABLE M026 LA;Lo;0;R;;;;;N;;;;; 1E838;MENDE KIKAKUI SYLLABLE M027 LU;Lo;0;R;;;;;N;;;;; 1E839;MENDE KIKAKUI SYLLABLE M084 LEE;Lo;0;R;;;;;N;;;;; 1E83A;MENDE KIKAKUI SYLLABLE M073 LE;Lo;0;R;;;;;N;;;;; 1E83B;MENDE KIKAKUI SYLLABLE M054 LOO;Lo;0;R;;;;;N;;;;; 1E83C;MENDE KIKAKUI SYLLABLE M153 LO;Lo;0;R;;;;;N;;;;; 1E83D;MENDE KIKAKUI SYLLABLE M110 LONG LE;Lo;0;R;;;;;N;;;;; 1E83E;MENDE KIKAKUI SYLLABLE M016 DI;Lo;0;R;;;;;N;;;;; 1E83F;MENDE KIKAKUI SYLLABLE M017 DA;Lo;0;R;;;;;N;;;;; 1E840;MENDE KIKAKUI SYLLABLE M018 DU;Lo;0;R;;;;;N;;;;; 1E841;MENDE KIKAKUI SYLLABLE M089 DEE;Lo;0;R;;;;;N;;;;; 1E842;MENDE KIKAKUI SYLLABLE M180 DOO;Lo;0;R;;;;;N;;;;; 1E843;MENDE KIKAKUI SYLLABLE M181 DO;Lo;0;R;;;;;N;;;;; 1E844;MENDE KIKAKUI SYLLABLE M022 TI;Lo;0;R;;;;;N;;;;; 1E845;MENDE KIKAKUI SYLLABLE M023 TA;Lo;0;R;;;;;N;;;;; 1E846;MENDE KIKAKUI SYLLABLE M024 TU;Lo;0;R;;;;;N;;;;; 1E847;MENDE KIKAKUI SYLLABLE M091 TEE;Lo;0;R;;;;;N;;;;; 1E848;MENDE KIKAKUI SYLLABLE M055 TE;Lo;0;R;;;;;N;;;;; 1E849;MENDE KIKAKUI SYLLABLE M104 TOO;Lo;0;R;;;;;N;;;;; 1E84A;MENDE KIKAKUI SYLLABLE M069 TO;Lo;0;R;;;;;N;;;;; 1E84B;MENDE KIKAKUI SYLLABLE M028 JI;Lo;0;R;;;;;N;;;;; 1E84C;MENDE KIKAKUI SYLLABLE M029 JA;Lo;0;R;;;;;N;;;;; 1E84D;MENDE KIKAKUI SYLLABLE M030 JU;Lo;0;R;;;;;N;;;;; 1E84E;MENDE KIKAKUI SYLLABLE M157 JEE;Lo;0;R;;;;;N;;;;; 1E84F;MENDE KIKAKUI SYLLABLE M113 JE;Lo;0;R;;;;;N;;;;; 1E850;MENDE KIKAKUI SYLLABLE M160 JOO;Lo;0;R;;;;;N;;;;; 1E851;MENDE KIKAKUI SYLLABLE M063 JO;Lo;0;R;;;;;N;;;;; 1E852;MENDE KIKAKUI SYLLABLE M175 LONG JO;Lo;0;R;;;;;N;;;;; 1E853;MENDE KIKAKUI SYLLABLE M031 YI;Lo;0;R;;;;;N;;;;; 1E854;MENDE KIKAKUI SYLLABLE M032 YA;Lo;0;R;;;;;N;;;;; 1E855;MENDE KIKAKUI SYLLABLE M033 YU;Lo;0;R;;;;;N;;;;; 1E856;MENDE KIKAKUI SYLLABLE M109 YEE;Lo;0;R;;;;;N;;;;; 1E857;MENDE KIKAKUI SYLLABLE M080 YE;Lo;0;R;;;;;N;;;;; 1E858;MENDE KIKAKUI SYLLABLE M141 YOO;Lo;0;R;;;;;N;;;;; 1E859;MENDE KIKAKUI SYLLABLE M121 YO;Lo;0;R;;;;;N;;;;; 1E85A;MENDE KIKAKUI SYLLABLE M034 FI;Lo;0;R;;;;;N;;;;; 1E85B;MENDE KIKAKUI SYLLABLE M035 FA;Lo;0;R;;;;;N;;;;; 1E85C;MENDE KIKAKUI SYLLABLE M036 FU;Lo;0;R;;;;;N;;;;; 1E85D;MENDE KIKAKUI SYLLABLE M078 FEE;Lo;0;R;;;;;N;;;;; 1E85E;MENDE KIKAKUI SYLLABLE M075 FE;Lo;0;R;;;;;N;;;;; 1E85F;MENDE KIKAKUI SYLLABLE M133 FOO;Lo;0;R;;;;;N;;;;; 1E860;MENDE KIKAKUI SYLLABLE M088 FO;Lo;0;R;;;;;N;;;;; 1E861;MENDE KIKAKUI SYLLABLE M197 FUA;Lo;0;R;;;;;N;;;;; 1E862;MENDE KIKAKUI SYLLABLE M101 FAN;Lo;0;R;;;;;N;;;;; 1E863;MENDE KIKAKUI SYLLABLE M037 NIN;Lo;0;R;;;;;N;;;;; 1E864;MENDE KIKAKUI SYLLABLE M038 NAN;Lo;0;R;;;;;N;;;;; 1E865;MENDE KIKAKUI SYLLABLE M039 NUN;Lo;0;R;;;;;N;;;;; 1E866;MENDE KIKAKUI SYLLABLE M117 NEN;Lo;0;R;;;;;N;;;;; 1E867;MENDE KIKAKUI SYLLABLE M169 NON;Lo;0;R;;;;;N;;;;; 1E868;MENDE KIKAKUI SYLLABLE M176 HI;Lo;0;R;;;;;N;;;;; 1E869;MENDE KIKAKUI SYLLABLE M041 HA;Lo;0;R;;;;;N;;;;; 1E86A;MENDE KIKAKUI SYLLABLE M186 HU;Lo;0;R;;;;;N;;;;; 1E86B;MENDE KIKAKUI SYLLABLE M040 HEE;Lo;0;R;;;;;N;;;;; 1E86C;MENDE KIKAKUI SYLLABLE M096 HE;Lo;0;R;;;;;N;;;;; 1E86D;MENDE KIKAKUI SYLLABLE M042 HOO;Lo;0;R;;;;;N;;;;; 1E86E;MENDE KIKAKUI SYLLABLE M140 HO;Lo;0;R;;;;;N;;;;; 1E86F;MENDE KIKAKUI SYLLABLE M083 HEEI;Lo;0;R;;;;;N;;;;; 1E870;MENDE KIKAKUI SYLLABLE M128 HOOU;Lo;0;R;;;;;N;;;;; 1E871;MENDE KIKAKUI SYLLABLE M053 HIN;Lo;0;R;;;;;N;;;;; 1E872;MENDE KIKAKUI SYLLABLE M130 HAN;Lo;0;R;;;;;N;;;;; 1E873;MENDE KIKAKUI SYLLABLE M087 HUN;Lo;0;R;;;;;N;;;;; 1E874;MENDE KIKAKUI SYLLABLE M052 HEN;Lo;0;R;;;;;N;;;;; 1E875;MENDE KIKAKUI SYLLABLE M193 HON;Lo;0;R;;;;;N;;;;; 1E876;MENDE KIKAKUI SYLLABLE M046 HUAN;Lo;0;R;;;;;N;;;;; 1E877;MENDE KIKAKUI SYLLABLE M090 NGGI;Lo;0;R;;;;;N;;;;; 1E878;MENDE KIKAKUI SYLLABLE M043 NGGA;Lo;0;R;;;;;N;;;;; 1E879;MENDE KIKAKUI SYLLABLE M082 NGGU;Lo;0;R;;;;;N;;;;; 1E87A;MENDE KIKAKUI SYLLABLE M115 NGGEE;Lo;0;R;;;;;N;;;;; 1E87B;MENDE KIKAKUI SYLLABLE M146 NGGE;Lo;0;R;;;;;N;;;;; 1E87C;MENDE KIKAKUI SYLLABLE M156 NGGOO;Lo;0;R;;;;;N;;;;; 1E87D;MENDE KIKAKUI SYLLABLE M120 NGGO;Lo;0;R;;;;;N;;;;; 1E87E;MENDE KIKAKUI SYLLABLE M159 NGGAA;Lo;0;R;;;;;N;;;;; 1E87F;MENDE KIKAKUI SYLLABLE M127 NGGUA;Lo;0;R;;;;;N;;;;; 1E880;MENDE KIKAKUI SYLLABLE M086 LONG NGGE;Lo;0;R;;;;;N;;;;; 1E881;MENDE KIKAKUI SYLLABLE M106 LONG NGGOO;Lo;0;R;;;;;N;;;;; 1E882;MENDE KIKAKUI SYLLABLE M183 LONG NGGO;Lo;0;R;;;;;N;;;;; 1E883;MENDE KIKAKUI SYLLABLE M155 GI;Lo;0;R;;;;;N;;;;; 1E884;MENDE KIKAKUI SYLLABLE M111 GA;Lo;0;R;;;;;N;;;;; 1E885;MENDE KIKAKUI SYLLABLE M168 GU;Lo;0;R;;;;;N;;;;; 1E886;MENDE KIKAKUI SYLLABLE M190 GEE;Lo;0;R;;;;;N;;;;; 1E887;MENDE KIKAKUI SYLLABLE M166 GUEI;Lo;0;R;;;;;N;;;;; 1E888;MENDE KIKAKUI SYLLABLE M167 GUAN;Lo;0;R;;;;;N;;;;; 1E889;MENDE KIKAKUI SYLLABLE M184 NGEN;Lo;0;R;;;;;N;;;;; 1E88A;MENDE KIKAKUI SYLLABLE M057 NGON;Lo;0;R;;;;;N;;;;; 1E88B;MENDE KIKAKUI SYLLABLE M177 NGUAN;Lo;0;R;;;;;N;;;;; 1E88C;MENDE KIKAKUI SYLLABLE M068 PI;Lo;0;R;;;;;N;;;;; 1E88D;MENDE KIKAKUI SYLLABLE M099 PA;Lo;0;R;;;;;N;;;;; 1E88E;MENDE KIKAKUI SYLLABLE M050 PU;Lo;0;R;;;;;N;;;;; 1E88F;MENDE KIKAKUI SYLLABLE M081 PEE;Lo;0;R;;;;;N;;;;; 1E890;MENDE KIKAKUI SYLLABLE M051 PE;Lo;0;R;;;;;N;;;;; 1E891;MENDE KIKAKUI SYLLABLE M102 POO;Lo;0;R;;;;;N;;;;; 1E892;MENDE KIKAKUI SYLLABLE M066 PO;Lo;0;R;;;;;N;;;;; 1E893;MENDE KIKAKUI SYLLABLE M145 MBI;Lo;0;R;;;;;N;;;;; 1E894;MENDE KIKAKUI SYLLABLE M062 MBA;Lo;0;R;;;;;N;;;;; 1E895;MENDE KIKAKUI SYLLABLE M122 MBU;Lo;0;R;;;;;N;;;;; 1E896;MENDE KIKAKUI SYLLABLE M047 MBEE;Lo;0;R;;;;;N;;;;; 1E897;MENDE KIKAKUI SYLLABLE M188 MBEE;Lo;0;R;;;;;N;;;;; 1E898;MENDE KIKAKUI SYLLABLE M072 MBE;Lo;0;R;;;;;N;;;;; 1E899;MENDE KIKAKUI SYLLABLE M172 MBOO;Lo;0;R;;;;;N;;;;; 1E89A;MENDE KIKAKUI SYLLABLE M174 MBO;Lo;0;R;;;;;N;;;;; 1E89B;MENDE KIKAKUI SYLLABLE M187 MBUU;Lo;0;R;;;;;N;;;;; 1E89C;MENDE KIKAKUI SYLLABLE M161 LONG MBE;Lo;0;R;;;;;N;;;;; 1E89D;MENDE KIKAKUI SYLLABLE M105 LONG MBOO;Lo;0;R;;;;;N;;;;; 1E89E;MENDE KIKAKUI SYLLABLE M142 LONG MBO;Lo;0;R;;;;;N;;;;; 1E89F;MENDE KIKAKUI SYLLABLE M132 KPI;Lo;0;R;;;;;N;;;;; 1E8A0;MENDE KIKAKUI SYLLABLE M092 KPA;Lo;0;R;;;;;N;;;;; 1E8A1;MENDE KIKAKUI SYLLABLE M074 KPU;Lo;0;R;;;;;N;;;;; 1E8A2;MENDE KIKAKUI SYLLABLE M044 KPEE;Lo;0;R;;;;;N;;;;; 1E8A3;MENDE KIKAKUI SYLLABLE M108 KPE;Lo;0;R;;;;;N;;;;; 1E8A4;MENDE KIKAKUI SYLLABLE M112 KPOO;Lo;0;R;;;;;N;;;;; 1E8A5;MENDE KIKAKUI SYLLABLE M158 KPO;Lo;0;R;;;;;N;;;;; 1E8A6;MENDE KIKAKUI SYLLABLE M124 GBI;Lo;0;R;;;;;N;;;;; 1E8A7;MENDE KIKAKUI SYLLABLE M056 GBA;Lo;0;R;;;;;N;;;;; 1E8A8;MENDE KIKAKUI SYLLABLE M148 GBU;Lo;0;R;;;;;N;;;;; 1E8A9;MENDE KIKAKUI SYLLABLE M093 GBEE;Lo;0;R;;;;;N;;;;; 1E8AA;MENDE KIKAKUI SYLLABLE M107 GBE;Lo;0;R;;;;;N;;;;; 1E8AB;MENDE KIKAKUI SYLLABLE M071 GBOO;Lo;0;R;;;;;N;;;;; 1E8AC;MENDE KIKAKUI SYLLABLE M070 GBO;Lo;0;R;;;;;N;;;;; 1E8AD;MENDE KIKAKUI SYLLABLE M171 RA;Lo;0;R;;;;;N;;;;; 1E8AE;MENDE KIKAKUI SYLLABLE M123 NDI;Lo;0;R;;;;;N;;;;; 1E8AF;MENDE KIKAKUI SYLLABLE M129 NDA;Lo;0;R;;;;;N;;;;; 1E8B0;MENDE KIKAKUI SYLLABLE M125 NDU;Lo;0;R;;;;;N;;;;; 1E8B1;MENDE KIKAKUI SYLLABLE M191 NDEE;Lo;0;R;;;;;N;;;;; 1E8B2;MENDE KIKAKUI SYLLABLE M119 NDE;Lo;0;R;;;;;N;;;;; 1E8B3;MENDE KIKAKUI SYLLABLE M067 NDOO;Lo;0;R;;;;;N;;;;; 1E8B4;MENDE KIKAKUI SYLLABLE M064 NDO;Lo;0;R;;;;;N;;;;; 1E8B5;MENDE KIKAKUI SYLLABLE M152 NJA;Lo;0;R;;;;;N;;;;; 1E8B6;MENDE KIKAKUI SYLLABLE M192 NJU;Lo;0;R;;;;;N;;;;; 1E8B7;MENDE KIKAKUI SYLLABLE M149 NJEE;Lo;0;R;;;;;N;;;;; 1E8B8;MENDE KIKAKUI SYLLABLE M134 NJOO;Lo;0;R;;;;;N;;;;; 1E8B9;MENDE KIKAKUI SYLLABLE M182 VI;Lo;0;R;;;;;N;;;;; 1E8BA;MENDE KIKAKUI SYLLABLE M185 VA;Lo;0;R;;;;;N;;;;; 1E8BB;MENDE KIKAKUI SYLLABLE M151 VU;Lo;0;R;;;;;N;;;;; 1E8BC;MENDE KIKAKUI SYLLABLE M173 VEE;Lo;0;R;;;;;N;;;;; 1E8BD;MENDE KIKAKUI SYLLABLE M085 VE;Lo;0;R;;;;;N;;;;; 1E8BE;MENDE KIKAKUI SYLLABLE M144 VOO;Lo;0;R;;;;;N;;;;; 1E8BF;MENDE KIKAKUI SYLLABLE M077 VO;Lo;0;R;;;;;N;;;;; 1E8C0;MENDE KIKAKUI SYLLABLE M164 NYIN;Lo;0;R;;;;;N;;;;; 1E8C1;MENDE KIKAKUI SYLLABLE M058 NYAN;Lo;0;R;;;;;N;;;;; 1E8C2;MENDE KIKAKUI SYLLABLE M170 NYUN;Lo;0;R;;;;;N;;;;; 1E8C3;MENDE KIKAKUI SYLLABLE M098 NYEN;Lo;0;R;;;;;N;;;;; 1E8C4;MENDE KIKAKUI SYLLABLE M060 NYON;Lo;0;R;;;;;N;;;;; 1E8C7;MENDE KIKAKUI DIGIT ONE;No;0;R;;;;1;N;;;;; 1E8C8;MENDE KIKAKUI DIGIT TWO;No;0;R;;;;2;N;;;;; 1E8C9;MENDE KIKAKUI DIGIT THREE;No;0;R;;;;3;N;;;;; 1E8CA;MENDE KIKAKUI DIGIT FOUR;No;0;R;;;;4;N;;;;; 1E8CB;MENDE KIKAKUI DIGIT FIVE;No;0;R;;;;5;N;;;;; 1E8CC;MENDE KIKAKUI DIGIT SIX;No;0;R;;;;6;N;;;;; 1E8CD;MENDE KIKAKUI DIGIT SEVEN;No;0;R;;;;7;N;;;;; 1E8CE;MENDE KIKAKUI DIGIT EIGHT;No;0;R;;;;8;N;;;;; 1E8CF;MENDE KIKAKUI DIGIT NINE;No;0;R;;;;9;N;;;;; 1E8D0;MENDE KIKAKUI COMBINING NUMBER TEENS;Mn;220;NSM;;;;;N;;;;; 1E8D1;MENDE KIKAKUI COMBINING NUMBER TENS;Mn;220;NSM;;;;;N;;;;; 1E8D2;MENDE KIKAKUI COMBINING NUMBER HUNDREDS;Mn;220;NSM;;;;;N;;;;; 1E8D3;MENDE KIKAKUI COMBINING NUMBER THOUSANDS;Mn;220;NSM;;;;;N;;;;; 1E8D4;MENDE KIKAKUI COMBINING NUMBER TEN THOUSANDS;Mn;220;NSM;;;;;N;;;;; 1E8D5;MENDE KIKAKUI COMBINING NUMBER HUNDRED THOUSANDS;Mn;220;NSM;;;;;N;;;;; 1E8D6;MENDE KIKAKUI COMBINING NUMBER MILLIONS;Mn;220;NSM;;;;;N;;;;; 1E900;ADLAM CAPITAL LETTER ALIF;Lu;0;R;;;;;N;;;;1E922; 1E901;ADLAM CAPITAL LETTER DAALI;Lu;0;R;;;;;N;;;;1E923; 1E902;ADLAM CAPITAL LETTER LAAM;Lu;0;R;;;;;N;;;;1E924; 1E903;ADLAM CAPITAL LETTER MIIM;Lu;0;R;;;;;N;;;;1E925; 1E904;ADLAM CAPITAL LETTER BA;Lu;0;R;;;;;N;;;;1E926; 1E905;ADLAM CAPITAL LETTER SINNYIIYHE;Lu;0;R;;;;;N;;;;1E927; 1E906;ADLAM CAPITAL LETTER PE;Lu;0;R;;;;;N;;;;1E928; 1E907;ADLAM CAPITAL LETTER BHE;Lu;0;R;;;;;N;;;;1E929; 1E908;ADLAM CAPITAL LETTER RA;Lu;0;R;;;;;N;;;;1E92A; 1E909;ADLAM CAPITAL LETTER E;Lu;0;R;;;;;N;;;;1E92B; 1E90A;ADLAM CAPITAL LETTER FA;Lu;0;R;;;;;N;;;;1E92C; 1E90B;ADLAM CAPITAL LETTER I;Lu;0;R;;;;;N;;;;1E92D; 1E90C;ADLAM CAPITAL LETTER O;Lu;0;R;;;;;N;;;;1E92E; 1E90D;ADLAM CAPITAL LETTER DHA;Lu;0;R;;;;;N;;;;1E92F; 1E90E;ADLAM CAPITAL LETTER YHE;Lu;0;R;;;;;N;;;;1E930; 1E90F;ADLAM CAPITAL LETTER WAW;Lu;0;R;;;;;N;;;;1E931; 1E910;ADLAM CAPITAL LETTER NUN;Lu;0;R;;;;;N;;;;1E932; 1E911;ADLAM CAPITAL LETTER KAF;Lu;0;R;;;;;N;;;;1E933; 1E912;ADLAM CAPITAL LETTER YA;Lu;0;R;;;;;N;;;;1E934; 1E913;ADLAM CAPITAL LETTER U;Lu;0;R;;;;;N;;;;1E935; 1E914;ADLAM CAPITAL LETTER JIIM;Lu;0;R;;;;;N;;;;1E936; 1E915;ADLAM CAPITAL LETTER CHI;Lu;0;R;;;;;N;;;;1E937; 1E916;ADLAM CAPITAL LETTER HA;Lu;0;R;;;;;N;;;;1E938; 1E917;ADLAM CAPITAL LETTER QAAF;Lu;0;R;;;;;N;;;;1E939; 1E918;ADLAM CAPITAL LETTER GA;Lu;0;R;;;;;N;;;;1E93A; 1E919;ADLAM CAPITAL LETTER NYA;Lu;0;R;;;;;N;;;;1E93B; 1E91A;ADLAM CAPITAL LETTER TU;Lu;0;R;;;;;N;;;;1E93C; 1E91B;ADLAM CAPITAL LETTER NHA;Lu;0;R;;;;;N;;;;1E93D; 1E91C;ADLAM CAPITAL LETTER VA;Lu;0;R;;;;;N;;;;1E93E; 1E91D;ADLAM CAPITAL LETTER KHA;Lu;0;R;;;;;N;;;;1E93F; 1E91E;ADLAM CAPITAL LETTER GBE;Lu;0;R;;;;;N;;;;1E940; 1E91F;ADLAM CAPITAL LETTER ZAL;Lu;0;R;;;;;N;;;;1E941; 1E920;ADLAM CAPITAL LETTER KPO;Lu;0;R;;;;;N;;;;1E942; 1E921;ADLAM CAPITAL LETTER SHA;Lu;0;R;;;;;N;;;;1E943; 1E922;ADLAM SMALL LETTER ALIF;Ll;0;R;;;;;N;;;1E900;;1E900 1E923;ADLAM SMALL LETTER DAALI;Ll;0;R;;;;;N;;;1E901;;1E901 1E924;ADLAM SMALL LETTER LAAM;Ll;0;R;;;;;N;;;1E902;;1E902 1E925;ADLAM SMALL LETTER MIIM;Ll;0;R;;;;;N;;;1E903;;1E903 1E926;ADLAM SMALL LETTER BA;Ll;0;R;;;;;N;;;1E904;;1E904 1E927;ADLAM SMALL LETTER SINNYIIYHE;Ll;0;R;;;;;N;;;1E905;;1E905 1E928;ADLAM SMALL LETTER PE;Ll;0;R;;;;;N;;;1E906;;1E906 1E929;ADLAM SMALL LETTER BHE;Ll;0;R;;;;;N;;;1E907;;1E907 1E92A;ADLAM SMALL LETTER RA;Ll;0;R;;;;;N;;;1E908;;1E908 1E92B;ADLAM SMALL LETTER E;Ll;0;R;;;;;N;;;1E909;;1E909 1E92C;ADLAM SMALL LETTER FA;Ll;0;R;;;;;N;;;1E90A;;1E90A 1E92D;ADLAM SMALL LETTER I;Ll;0;R;;;;;N;;;1E90B;;1E90B 1E92E;ADLAM SMALL LETTER O;Ll;0;R;;;;;N;;;1E90C;;1E90C 1E92F;ADLAM SMALL LETTER DHA;Ll;0;R;;;;;N;;;1E90D;;1E90D 1E930;ADLAM SMALL LETTER YHE;Ll;0;R;;;;;N;;;1E90E;;1E90E 1E931;ADLAM SMALL LETTER WAW;Ll;0;R;;;;;N;;;1E90F;;1E90F 1E932;ADLAM SMALL LETTER NUN;Ll;0;R;;;;;N;;;1E910;;1E910 1E933;ADLAM SMALL LETTER KAF;Ll;0;R;;;;;N;;;1E911;;1E911 1E934;ADLAM SMALL LETTER YA;Ll;0;R;;;;;N;;;1E912;;1E912 1E935;ADLAM SMALL LETTER U;Ll;0;R;;;;;N;;;1E913;;1E913 1E936;ADLAM SMALL LETTER JIIM;Ll;0;R;;;;;N;;;1E914;;1E914 1E937;ADLAM SMALL LETTER CHI;Ll;0;R;;;;;N;;;1E915;;1E915 1E938;ADLAM SMALL LETTER HA;Ll;0;R;;;;;N;;;1E916;;1E916 1E939;ADLAM SMALL LETTER QAAF;Ll;0;R;;;;;N;;;1E917;;1E917 1E93A;ADLAM SMALL LETTER GA;Ll;0;R;;;;;N;;;1E918;;1E918 1E93B;ADLAM SMALL LETTER NYA;Ll;0;R;;;;;N;;;1E919;;1E919 1E93C;ADLAM SMALL LETTER TU;Ll;0;R;;;;;N;;;1E91A;;1E91A 1E93D;ADLAM SMALL LETTER NHA;Ll;0;R;;;;;N;;;1E91B;;1E91B 1E93E;ADLAM SMALL LETTER VA;Ll;0;R;;;;;N;;;1E91C;;1E91C 1E93F;ADLAM SMALL LETTER KHA;Ll;0;R;;;;;N;;;1E91D;;1E91D 1E940;ADLAM SMALL LETTER GBE;Ll;0;R;;;;;N;;;1E91E;;1E91E 1E941;ADLAM SMALL LETTER ZAL;Ll;0;R;;;;;N;;;1E91F;;1E91F 1E942;ADLAM SMALL LETTER KPO;Ll;0;R;;;;;N;;;1E920;;1E920 1E943;ADLAM SMALL LETTER SHA;Ll;0;R;;;;;N;;;1E921;;1E921 1E944;ADLAM ALIF LENGTHENER;Mn;230;NSM;;;;;N;;;;; 1E945;ADLAM VOWEL LENGTHENER;Mn;230;NSM;;;;;N;;;;; 1E946;ADLAM GEMINATION MARK;Mn;230;NSM;;;;;N;;;;; 1E947;ADLAM HAMZA;Mn;230;NSM;;;;;N;;;;; 1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;; 1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;; 1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;; 1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;; 1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;; 1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;; 1E953;ADLAM DIGIT THREE;Nd;0;R;;3;3;3;N;;;;; 1E954;ADLAM DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;; 1E955;ADLAM DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;; 1E956;ADLAM DIGIT SIX;Nd;0;R;;6;6;6;N;;;;; 1E957;ADLAM DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;; 1E958;ADLAM DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;; 1E959;ADLAM DIGIT NINE;Nd;0;R;;9;9;9;N;;;;; 1E95E;ADLAM INITIAL EXCLAMATION MARK;Po;0;R;;;;;N;;;;; 1E95F;ADLAM INITIAL QUESTION MARK;Po;0;R;;;;;N;;;;; 1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL; 0627;;;;N;;;;; 1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL; 0628;;;;N;;;;; 1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EE03;ARABIC MATHEMATICAL DAL;Lo;0;AL; 062F;;;;N;;;;; 1EE05;ARABIC MATHEMATICAL WAW;Lo;0;AL; 0648;;;;N;;;;; 1EE06;ARABIC MATHEMATICAL ZAIN;Lo;0;AL; 0632;;;;N;;;;; 1EE07;ARABIC MATHEMATICAL HAH;Lo;0;AL; 062D;;;;N;;;;; 1EE08;ARABIC MATHEMATICAL TAH;Lo;0;AL; 0637;;;;N;;;;; 1EE09;ARABIC MATHEMATICAL YEH;Lo;0;AL; 064A;;;;N;;;;; 1EE0A;ARABIC MATHEMATICAL KAF;Lo;0;AL; 0643;;;;N;;;;; 1EE0B;ARABIC MATHEMATICAL LAM;Lo;0;AL; 0644;;;;N;;;;; 1EE0C;ARABIC MATHEMATICAL MEEM;Lo;0;AL; 0645;;;;N;;;;; 1EE0D;ARABIC MATHEMATICAL NOON;Lo;0;AL; 0646;;;;N;;;;; 1EE0E;ARABIC MATHEMATICAL SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EE0F;ARABIC MATHEMATICAL AIN;Lo;0;AL; 0639;;;;N;;;;; 1EE10;ARABIC MATHEMATICAL FEH;Lo;0;AL; 0641;;;;N;;;;; 1EE11;ARABIC MATHEMATICAL SAD;Lo;0;AL; 0635;;;;N;;;;; 1EE12;ARABIC MATHEMATICAL QAF;Lo;0;AL; 0642;;;;N;;;;; 1EE13;ARABIC MATHEMATICAL REH;Lo;0;AL; 0631;;;;N;;;;; 1EE14;ARABIC MATHEMATICAL SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EE15;ARABIC MATHEMATICAL TEH;Lo;0;AL; 062A;;;;N;;;;; 1EE16;ARABIC MATHEMATICAL THEH;Lo;0;AL; 062B;;;;N;;;;; 1EE17;ARABIC MATHEMATICAL KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EE18;ARABIC MATHEMATICAL THAL;Lo;0;AL; 0630;;;;N;;;;; 1EE19;ARABIC MATHEMATICAL DAD;Lo;0;AL; 0636;;;;N;;;;; 1EE1A;ARABIC MATHEMATICAL ZAH;Lo;0;AL; 0638;;;;N;;;;; 1EE1B;ARABIC MATHEMATICAL GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EE1C;ARABIC MATHEMATICAL DOTLESS BEH;Lo;0;AL; 066E;;;;N;;;;; 1EE1D;ARABIC MATHEMATICAL DOTLESS NOON;Lo;0;AL; 06BA;;;;N;;;;; 1EE1E;ARABIC MATHEMATICAL DOTLESS FEH;Lo;0;AL; 06A1;;;;N;;;;; 1EE1F;ARABIC MATHEMATICAL DOTLESS QAF;Lo;0;AL; 066F;;;;N;;;;; 1EE21;ARABIC MATHEMATICAL INITIAL BEH;Lo;0;AL; 0628;;;;N;;;;; 1EE22;ARABIC MATHEMATICAL INITIAL JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EE24;ARABIC MATHEMATICAL INITIAL HEH;Lo;0;AL; 0647;;;;N;;;;; 1EE27;ARABIC MATHEMATICAL INITIAL HAH;Lo;0;AL; 062D;;;;N;;;;; 1EE29;ARABIC MATHEMATICAL INITIAL YEH;Lo;0;AL; 064A;;;;N;;;;; 1EE2A;ARABIC MATHEMATICAL INITIAL KAF;Lo;0;AL; 0643;;;;N;;;;; 1EE2B;ARABIC MATHEMATICAL INITIAL LAM;Lo;0;AL; 0644;;;;N;;;;; 1EE2C;ARABIC MATHEMATICAL INITIAL MEEM;Lo;0;AL; 0645;;;;N;;;;; 1EE2D;ARABIC MATHEMATICAL INITIAL NOON;Lo;0;AL; 0646;;;;N;;;;; 1EE2E;ARABIC MATHEMATICAL INITIAL SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EE2F;ARABIC MATHEMATICAL INITIAL AIN;Lo;0;AL; 0639;;;;N;;;;; 1EE30;ARABIC MATHEMATICAL INITIAL FEH;Lo;0;AL; 0641;;;;N;;;;; 1EE31;ARABIC MATHEMATICAL INITIAL SAD;Lo;0;AL; 0635;;;;N;;;;; 1EE32;ARABIC MATHEMATICAL INITIAL QAF;Lo;0;AL; 0642;;;;N;;;;; 1EE34;ARABIC MATHEMATICAL INITIAL SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EE35;ARABIC MATHEMATICAL INITIAL TEH;Lo;0;AL; 062A;;;;N;;;;; 1EE36;ARABIC MATHEMATICAL INITIAL THEH;Lo;0;AL; 062B;;;;N;;;;; 1EE37;ARABIC MATHEMATICAL INITIAL KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EE39;ARABIC MATHEMATICAL INITIAL DAD;Lo;0;AL; 0636;;;;N;;;;; 1EE3B;ARABIC MATHEMATICAL INITIAL GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EE42;ARABIC MATHEMATICAL TAILED JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EE47;ARABIC MATHEMATICAL TAILED HAH;Lo;0;AL; 062D;;;;N;;;;; 1EE49;ARABIC MATHEMATICAL TAILED YEH;Lo;0;AL; 064A;;;;N;;;;; 1EE4B;ARABIC MATHEMATICAL TAILED LAM;Lo;0;AL; 0644;;;;N;;;;; 1EE4D;ARABIC MATHEMATICAL TAILED NOON;Lo;0;AL; 0646;;;;N;;;;; 1EE4E;ARABIC MATHEMATICAL TAILED SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EE4F;ARABIC MATHEMATICAL TAILED AIN;Lo;0;AL; 0639;;;;N;;;;; 1EE51;ARABIC MATHEMATICAL TAILED SAD;Lo;0;AL; 0635;;;;N;;;;; 1EE52;ARABIC MATHEMATICAL TAILED QAF;Lo;0;AL; 0642;;;;N;;;;; 1EE54;ARABIC MATHEMATICAL TAILED SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EE57;ARABIC MATHEMATICAL TAILED KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EE59;ARABIC MATHEMATICAL TAILED DAD;Lo;0;AL; 0636;;;;N;;;;; 1EE5B;ARABIC MATHEMATICAL TAILED GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EE5D;ARABIC MATHEMATICAL TAILED DOTLESS NOON;Lo;0;AL; 06BA;;;;N;;;;; 1EE5F;ARABIC MATHEMATICAL TAILED DOTLESS QAF;Lo;0;AL; 066F;;;;N;;;;; 1EE61;ARABIC MATHEMATICAL STRETCHED BEH;Lo;0;AL; 0628;;;;N;;;;; 1EE62;ARABIC MATHEMATICAL STRETCHED JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EE64;ARABIC MATHEMATICAL STRETCHED HEH;Lo;0;AL; 0647;;;;N;;;;; 1EE67;ARABIC MATHEMATICAL STRETCHED HAH;Lo;0;AL; 062D;;;;N;;;;; 1EE68;ARABIC MATHEMATICAL STRETCHED TAH;Lo;0;AL; 0637;;;;N;;;;; 1EE69;ARABIC MATHEMATICAL STRETCHED YEH;Lo;0;AL; 064A;;;;N;;;;; 1EE6A;ARABIC MATHEMATICAL STRETCHED KAF;Lo;0;AL; 0643;;;;N;;;;; 1EE6C;ARABIC MATHEMATICAL STRETCHED MEEM;Lo;0;AL; 0645;;;;N;;;;; 1EE6D;ARABIC MATHEMATICAL STRETCHED NOON;Lo;0;AL; 0646;;;;N;;;;; 1EE6E;ARABIC MATHEMATICAL STRETCHED SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EE6F;ARABIC MATHEMATICAL STRETCHED AIN;Lo;0;AL; 0639;;;;N;;;;; 1EE70;ARABIC MATHEMATICAL STRETCHED FEH;Lo;0;AL; 0641;;;;N;;;;; 1EE71;ARABIC MATHEMATICAL STRETCHED SAD;Lo;0;AL; 0635;;;;N;;;;; 1EE72;ARABIC MATHEMATICAL STRETCHED QAF;Lo;0;AL; 0642;;;;N;;;;; 1EE74;ARABIC MATHEMATICAL STRETCHED SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EE75;ARABIC MATHEMATICAL STRETCHED TEH;Lo;0;AL; 062A;;;;N;;;;; 1EE76;ARABIC MATHEMATICAL STRETCHED THEH;Lo;0;AL; 062B;;;;N;;;;; 1EE77;ARABIC MATHEMATICAL STRETCHED KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EE79;ARABIC MATHEMATICAL STRETCHED DAD;Lo;0;AL; 0636;;;;N;;;;; 1EE7A;ARABIC MATHEMATICAL STRETCHED ZAH;Lo;0;AL; 0638;;;;N;;;;; 1EE7B;ARABIC MATHEMATICAL STRETCHED GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EE7C;ARABIC MATHEMATICAL STRETCHED DOTLESS BEH;Lo;0;AL; 066E;;;;N;;;;; 1EE7E;ARABIC MATHEMATICAL STRETCHED DOTLESS FEH;Lo;0;AL; 06A1;;;;N;;;;; 1EE80;ARABIC MATHEMATICAL LOOPED ALEF;Lo;0;AL; 0627;;;;N;;;;; 1EE81;ARABIC MATHEMATICAL LOOPED BEH;Lo;0;AL; 0628;;;;N;;;;; 1EE82;ARABIC MATHEMATICAL LOOPED JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EE83;ARABIC MATHEMATICAL LOOPED DAL;Lo;0;AL; 062F;;;;N;;;;; 1EE84;ARABIC MATHEMATICAL LOOPED HEH;Lo;0;AL; 0647;;;;N;;;;; 1EE85;ARABIC MATHEMATICAL LOOPED WAW;Lo;0;AL; 0648;;;;N;;;;; 1EE86;ARABIC MATHEMATICAL LOOPED ZAIN;Lo;0;AL; 0632;;;;N;;;;; 1EE87;ARABIC MATHEMATICAL LOOPED HAH;Lo;0;AL; 062D;;;;N;;;;; 1EE88;ARABIC MATHEMATICAL LOOPED TAH;Lo;0;AL; 0637;;;;N;;;;; 1EE89;ARABIC MATHEMATICAL LOOPED YEH;Lo;0;AL; 064A;;;;N;;;;; 1EE8B;ARABIC MATHEMATICAL LOOPED LAM;Lo;0;AL; 0644;;;;N;;;;; 1EE8C;ARABIC MATHEMATICAL LOOPED MEEM;Lo;0;AL; 0645;;;;N;;;;; 1EE8D;ARABIC MATHEMATICAL LOOPED NOON;Lo;0;AL; 0646;;;;N;;;;; 1EE8E;ARABIC MATHEMATICAL LOOPED SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EE8F;ARABIC MATHEMATICAL LOOPED AIN;Lo;0;AL; 0639;;;;N;;;;; 1EE90;ARABIC MATHEMATICAL LOOPED FEH;Lo;0;AL; 0641;;;;N;;;;; 1EE91;ARABIC MATHEMATICAL LOOPED SAD;Lo;0;AL; 0635;;;;N;;;;; 1EE92;ARABIC MATHEMATICAL LOOPED QAF;Lo;0;AL; 0642;;;;N;;;;; 1EE93;ARABIC MATHEMATICAL LOOPED REH;Lo;0;AL; 0631;;;;N;;;;; 1EE94;ARABIC MATHEMATICAL LOOPED SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EE95;ARABIC MATHEMATICAL LOOPED TEH;Lo;0;AL; 062A;;;;N;;;;; 1EE96;ARABIC MATHEMATICAL LOOPED THEH;Lo;0;AL; 062B;;;;N;;;;; 1EE97;ARABIC MATHEMATICAL LOOPED KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EE98;ARABIC MATHEMATICAL LOOPED THAL;Lo;0;AL; 0630;;;;N;;;;; 1EE99;ARABIC MATHEMATICAL LOOPED DAD;Lo;0;AL; 0636;;;;N;;;;; 1EE9A;ARABIC MATHEMATICAL LOOPED ZAH;Lo;0;AL; 0638;;;;N;;;;; 1EE9B;ARABIC MATHEMATICAL LOOPED GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EEA1;ARABIC MATHEMATICAL DOUBLE-STRUCK BEH;Lo;0;AL; 0628;;;;N;;;;; 1EEA2;ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM;Lo;0;AL; 062C;;;;N;;;;; 1EEA3;ARABIC MATHEMATICAL DOUBLE-STRUCK DAL;Lo;0;AL; 062F;;;;N;;;;; 1EEA5;ARABIC MATHEMATICAL DOUBLE-STRUCK WAW;Lo;0;AL; 0648;;;;N;;;;; 1EEA6;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN;Lo;0;AL; 0632;;;;N;;;;; 1EEA7;ARABIC MATHEMATICAL DOUBLE-STRUCK HAH;Lo;0;AL; 062D;;;;N;;;;; 1EEA8;ARABIC MATHEMATICAL DOUBLE-STRUCK TAH;Lo;0;AL; 0637;;;;N;;;;; 1EEA9;ARABIC MATHEMATICAL DOUBLE-STRUCK YEH;Lo;0;AL; 064A;;;;N;;;;; 1EEAB;ARABIC MATHEMATICAL DOUBLE-STRUCK LAM;Lo;0;AL; 0644;;;;N;;;;; 1EEAC;ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM;Lo;0;AL; 0645;;;;N;;;;; 1EEAD;ARABIC MATHEMATICAL DOUBLE-STRUCK NOON;Lo;0;AL; 0646;;;;N;;;;; 1EEAE;ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN;Lo;0;AL; 0633;;;;N;;;;; 1EEAF;ARABIC MATHEMATICAL DOUBLE-STRUCK AIN;Lo;0;AL; 0639;;;;N;;;;; 1EEB0;ARABIC MATHEMATICAL DOUBLE-STRUCK FEH;Lo;0;AL; 0641;;;;N;;;;; 1EEB1;ARABIC MATHEMATICAL DOUBLE-STRUCK SAD;Lo;0;AL; 0635;;;;N;;;;; 1EEB2;ARABIC MATHEMATICAL DOUBLE-STRUCK QAF;Lo;0;AL; 0642;;;;N;;;;; 1EEB3;ARABIC MATHEMATICAL DOUBLE-STRUCK REH;Lo;0;AL; 0631;;;;N;;;;; 1EEB4;ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN;Lo;0;AL; 0634;;;;N;;;;; 1EEB5;ARABIC MATHEMATICAL DOUBLE-STRUCK TEH;Lo;0;AL; 062A;;;;N;;;;; 1EEB6;ARABIC MATHEMATICAL DOUBLE-STRUCK THEH;Lo;0;AL; 062B;;;;N;;;;; 1EEB7;ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH;Lo;0;AL; 062E;;;;N;;;;; 1EEB8;ARABIC MATHEMATICAL DOUBLE-STRUCK THAL;Lo;0;AL; 0630;;;;N;;;;; 1EEB9;ARABIC MATHEMATICAL DOUBLE-STRUCK DAD;Lo;0;AL; 0636;;;;N;;;;; 1EEBA;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH;Lo;0;AL; 0638;;;;N;;;;; 1EEBB;ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN;Lo;0;AL; 063A;;;;N;;;;; 1EEF0;ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL;Sm;0;ON;;;;;N;;;;; 1EEF1;ARABIC MATHEMATICAL OPERATOR HAH WITH DAL;Sm;0;ON;;;;;N;;;;; 1F000;MAHJONG TILE EAST WIND;So;0;ON;;;;;N;;;;; 1F001;MAHJONG TILE SOUTH WIND;So;0;ON;;;;;N;;;;; 1F002;MAHJONG TILE WEST WIND;So;0;ON;;;;;N;;;;; 1F003;MAHJONG TILE NORTH WIND;So;0;ON;;;;;N;;;;; 1F004;MAHJONG TILE RED DRAGON;So;0;ON;;;;;N;;;;; 1F005;MAHJONG TILE GREEN DRAGON;So;0;ON;;;;;N;;;;; 1F006;MAHJONG TILE WHITE DRAGON;So;0;ON;;;;;N;;;;; 1F007;MAHJONG TILE ONE OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F008;MAHJONG TILE TWO OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F009;MAHJONG TILE THREE OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00A;MAHJONG TILE FOUR OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00B;MAHJONG TILE FIVE OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00C;MAHJONG TILE SIX OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00D;MAHJONG TILE SEVEN OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00E;MAHJONG TILE EIGHT OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F00F;MAHJONG TILE NINE OF CHARACTERS;So;0;ON;;;;;N;;;;; 1F010;MAHJONG TILE ONE OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F011;MAHJONG TILE TWO OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F012;MAHJONG TILE THREE OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F013;MAHJONG TILE FOUR OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F014;MAHJONG TILE FIVE OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F015;MAHJONG TILE SIX OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F016;MAHJONG TILE SEVEN OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F017;MAHJONG TILE EIGHT OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F018;MAHJONG TILE NINE OF BAMBOOS;So;0;ON;;;;;N;;;;; 1F019;MAHJONG TILE ONE OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01A;MAHJONG TILE TWO OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01B;MAHJONG TILE THREE OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01C;MAHJONG TILE FOUR OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01D;MAHJONG TILE FIVE OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01E;MAHJONG TILE SIX OF CIRCLES;So;0;ON;;;;;N;;;;; 1F01F;MAHJONG TILE SEVEN OF CIRCLES;So;0;ON;;;;;N;;;;; 1F020;MAHJONG TILE EIGHT OF CIRCLES;So;0;ON;;;;;N;;;;; 1F021;MAHJONG TILE NINE OF CIRCLES;So;0;ON;;;;;N;;;;; 1F022;MAHJONG TILE PLUM;So;0;ON;;;;;N;;;;; 1F023;MAHJONG TILE ORCHID;So;0;ON;;;;;N;;;;; 1F024;MAHJONG TILE BAMBOO;So;0;ON;;;;;N;;;;; 1F025;MAHJONG TILE CHRYSANTHEMUM;So;0;ON;;;;;N;;;;; 1F026;MAHJONG TILE SPRING;So;0;ON;;;;;N;;;;; 1F027;MAHJONG TILE SUMMER;So;0;ON;;;;;N;;;;; 1F028;MAHJONG TILE AUTUMN;So;0;ON;;;;;N;;;;; 1F029;MAHJONG TILE WINTER;So;0;ON;;;;;N;;;;; 1F02A;MAHJONG TILE JOKER;So;0;ON;;;;;N;;;;; 1F02B;MAHJONG TILE BACK;So;0;ON;;;;;N;;;;; 1F030;DOMINO TILE HORIZONTAL BACK;So;0;ON;;;;;N;;;;; 1F031;DOMINO TILE HORIZONTAL-00-00;So;0;ON;;;;;N;;;;; 1F032;DOMINO TILE HORIZONTAL-00-01;So;0;ON;;;;;N;;;;; 1F033;DOMINO TILE HORIZONTAL-00-02;So;0;ON;;;;;N;;;;; 1F034;DOMINO TILE HORIZONTAL-00-03;So;0;ON;;;;;N;;;;; 1F035;DOMINO TILE HORIZONTAL-00-04;So;0;ON;;;;;N;;;;; 1F036;DOMINO TILE HORIZONTAL-00-05;So;0;ON;;;;;N;;;;; 1F037;DOMINO TILE HORIZONTAL-00-06;So;0;ON;;;;;N;;;;; 1F038;DOMINO TILE HORIZONTAL-01-00;So;0;ON;;;;;N;;;;; 1F039;DOMINO TILE HORIZONTAL-01-01;So;0;ON;;;;;N;;;;; 1F03A;DOMINO TILE HORIZONTAL-01-02;So;0;ON;;;;;N;;;;; 1F03B;DOMINO TILE HORIZONTAL-01-03;So;0;ON;;;;;N;;;;; 1F03C;DOMINO TILE HORIZONTAL-01-04;So;0;ON;;;;;N;;;;; 1F03D;DOMINO TILE HORIZONTAL-01-05;So;0;ON;;;;;N;;;;; 1F03E;DOMINO TILE HORIZONTAL-01-06;So;0;ON;;;;;N;;;;; 1F03F;DOMINO TILE HORIZONTAL-02-00;So;0;ON;;;;;N;;;;; 1F040;DOMINO TILE HORIZONTAL-02-01;So;0;ON;;;;;N;;;;; 1F041;DOMINO TILE HORIZONTAL-02-02;So;0;ON;;;;;N;;;;; 1F042;DOMINO TILE HORIZONTAL-02-03;So;0;ON;;;;;N;;;;; 1F043;DOMINO TILE HORIZONTAL-02-04;So;0;ON;;;;;N;;;;; 1F044;DOMINO TILE HORIZONTAL-02-05;So;0;ON;;;;;N;;;;; 1F045;DOMINO TILE HORIZONTAL-02-06;So;0;ON;;;;;N;;;;; 1F046;DOMINO TILE HORIZONTAL-03-00;So;0;ON;;;;;N;;;;; 1F047;DOMINO TILE HORIZONTAL-03-01;So;0;ON;;;;;N;;;;; 1F048;DOMINO TILE HORIZONTAL-03-02;So;0;ON;;;;;N;;;;; 1F049;DOMINO TILE HORIZONTAL-03-03;So;0;ON;;;;;N;;;;; 1F04A;DOMINO TILE HORIZONTAL-03-04;So;0;ON;;;;;N;;;;; 1F04B;DOMINO TILE HORIZONTAL-03-05;So;0;ON;;;;;N;;;;; 1F04C;DOMINO TILE HORIZONTAL-03-06;So;0;ON;;;;;N;;;;; 1F04D;DOMINO TILE HORIZONTAL-04-00;So;0;ON;;;;;N;;;;; 1F04E;DOMINO TILE HORIZONTAL-04-01;So;0;ON;;;;;N;;;;; 1F04F;DOMINO TILE HORIZONTAL-04-02;So;0;ON;;;;;N;;;;; 1F050;DOMINO TILE HORIZONTAL-04-03;So;0;ON;;;;;N;;;;; 1F051;DOMINO TILE HORIZONTAL-04-04;So;0;ON;;;;;N;;;;; 1F052;DOMINO TILE HORIZONTAL-04-05;So;0;ON;;;;;N;;;;; 1F053;DOMINO TILE HORIZONTAL-04-06;So;0;ON;;;;;N;;;;; 1F054;DOMINO TILE HORIZONTAL-05-00;So;0;ON;;;;;N;;;;; 1F055;DOMINO TILE HORIZONTAL-05-01;So;0;ON;;;;;N;;;;; 1F056;DOMINO TILE HORIZONTAL-05-02;So;0;ON;;;;;N;;;;; 1F057;DOMINO TILE HORIZONTAL-05-03;So;0;ON;;;;;N;;;;; 1F058;DOMINO TILE HORIZONTAL-05-04;So;0;ON;;;;;N;;;;; 1F059;DOMINO TILE HORIZONTAL-05-05;So;0;ON;;;;;N;;;;; 1F05A;DOMINO TILE HORIZONTAL-05-06;So;0;ON;;;;;N;;;;; 1F05B;DOMINO TILE HORIZONTAL-06-00;So;0;ON;;;;;N;;;;; 1F05C;DOMINO TILE HORIZONTAL-06-01;So;0;ON;;;;;N;;;;; 1F05D;DOMINO TILE HORIZONTAL-06-02;So;0;ON;;;;;N;;;;; 1F05E;DOMINO TILE HORIZONTAL-06-03;So;0;ON;;;;;N;;;;; 1F05F;DOMINO TILE HORIZONTAL-06-04;So;0;ON;;;;;N;;;;; 1F060;DOMINO TILE HORIZONTAL-06-05;So;0;ON;;;;;N;;;;; 1F061;DOMINO TILE HORIZONTAL-06-06;So;0;ON;;;;;N;;;;; 1F062;DOMINO TILE VERTICAL BACK;So;0;ON;;;;;N;;;;; 1F063;DOMINO TILE VERTICAL-00-00;So;0;ON;;;;;N;;;;; 1F064;DOMINO TILE VERTICAL-00-01;So;0;ON;;;;;N;;;;; 1F065;DOMINO TILE VERTICAL-00-02;So;0;ON;;;;;N;;;;; 1F066;DOMINO TILE VERTICAL-00-03;So;0;ON;;;;;N;;;;; 1F067;DOMINO TILE VERTICAL-00-04;So;0;ON;;;;;N;;;;; 1F068;DOMINO TILE VERTICAL-00-05;So;0;ON;;;;;N;;;;; 1F069;DOMINO TILE VERTICAL-00-06;So;0;ON;;;;;N;;;;; 1F06A;DOMINO TILE VERTICAL-01-00;So;0;ON;;;;;N;;;;; 1F06B;DOMINO TILE VERTICAL-01-01;So;0;ON;;;;;N;;;;; 1F06C;DOMINO TILE VERTICAL-01-02;So;0;ON;;;;;N;;;;; 1F06D;DOMINO TILE VERTICAL-01-03;So;0;ON;;;;;N;;;;; 1F06E;DOMINO TILE VERTICAL-01-04;So;0;ON;;;;;N;;;;; 1F06F;DOMINO TILE VERTICAL-01-05;So;0;ON;;;;;N;;;;; 1F070;DOMINO TILE VERTICAL-01-06;So;0;ON;;;;;N;;;;; 1F071;DOMINO TILE VERTICAL-02-00;So;0;ON;;;;;N;;;;; 1F072;DOMINO TILE VERTICAL-02-01;So;0;ON;;;;;N;;;;; 1F073;DOMINO TILE VERTICAL-02-02;So;0;ON;;;;;N;;;;; 1F074;DOMINO TILE VERTICAL-02-03;So;0;ON;;;;;N;;;;; 1F075;DOMINO TILE VERTICAL-02-04;So;0;ON;;;;;N;;;;; 1F076;DOMINO TILE VERTICAL-02-05;So;0;ON;;;;;N;;;;; 1F077;DOMINO TILE VERTICAL-02-06;So;0;ON;;;;;N;;;;; 1F078;DOMINO TILE VERTICAL-03-00;So;0;ON;;;;;N;;;;; 1F079;DOMINO TILE VERTICAL-03-01;So;0;ON;;;;;N;;;;; 1F07A;DOMINO TILE VERTICAL-03-02;So;0;ON;;;;;N;;;;; 1F07B;DOMINO TILE VERTICAL-03-03;So;0;ON;;;;;N;;;;; 1F07C;DOMINO TILE VERTICAL-03-04;So;0;ON;;;;;N;;;;; 1F07D;DOMINO TILE VERTICAL-03-05;So;0;ON;;;;;N;;;;; 1F07E;DOMINO TILE VERTICAL-03-06;So;0;ON;;;;;N;;;;; 1F07F;DOMINO TILE VERTICAL-04-00;So;0;ON;;;;;N;;;;; 1F080;DOMINO TILE VERTICAL-04-01;So;0;ON;;;;;N;;;;; 1F081;DOMINO TILE VERTICAL-04-02;So;0;ON;;;;;N;;;;; 1F082;DOMINO TILE VERTICAL-04-03;So;0;ON;;;;;N;;;;; 1F083;DOMINO TILE VERTICAL-04-04;So;0;ON;;;;;N;;;;; 1F084;DOMINO TILE VERTICAL-04-05;So;0;ON;;;;;N;;;;; 1F085;DOMINO TILE VERTICAL-04-06;So;0;ON;;;;;N;;;;; 1F086;DOMINO TILE VERTICAL-05-00;So;0;ON;;;;;N;;;;; 1F087;DOMINO TILE VERTICAL-05-01;So;0;ON;;;;;N;;;;; 1F088;DOMINO TILE VERTICAL-05-02;So;0;ON;;;;;N;;;;; 1F089;DOMINO TILE VERTICAL-05-03;So;0;ON;;;;;N;;;;; 1F08A;DOMINO TILE VERTICAL-05-04;So;0;ON;;;;;N;;;;; 1F08B;DOMINO TILE VERTICAL-05-05;So;0;ON;;;;;N;;;;; 1F08C;DOMINO TILE VERTICAL-05-06;So;0;ON;;;;;N;;;;; 1F08D;DOMINO TILE VERTICAL-06-00;So;0;ON;;;;;N;;;;; 1F08E;DOMINO TILE VERTICAL-06-01;So;0;ON;;;;;N;;;;; 1F08F;DOMINO TILE VERTICAL-06-02;So;0;ON;;;;;N;;;;; 1F090;DOMINO TILE VERTICAL-06-03;So;0;ON;;;;;N;;;;; 1F091;DOMINO TILE VERTICAL-06-04;So;0;ON;;;;;N;;;;; 1F092;DOMINO TILE VERTICAL-06-05;So;0;ON;;;;;N;;;;; 1F093;DOMINO TILE VERTICAL-06-06;So;0;ON;;;;;N;;;;; 1F0A0;PLAYING CARD BACK;So;0;ON;;;;;N;;;;; 1F0A1;PLAYING CARD ACE OF SPADES;So;0;ON;;;;;N;;;;; 1F0A2;PLAYING CARD TWO OF SPADES;So;0;ON;;;;;N;;;;; 1F0A3;PLAYING CARD THREE OF SPADES;So;0;ON;;;;;N;;;;; 1F0A4;PLAYING CARD FOUR OF SPADES;So;0;ON;;;;;N;;;;; 1F0A5;PLAYING CARD FIVE OF SPADES;So;0;ON;;;;;N;;;;; 1F0A6;PLAYING CARD SIX OF SPADES;So;0;ON;;;;;N;;;;; 1F0A7;PLAYING CARD SEVEN OF SPADES;So;0;ON;;;;;N;;;;; 1F0A8;PLAYING CARD EIGHT OF SPADES;So;0;ON;;;;;N;;;;; 1F0A9;PLAYING CARD NINE OF SPADES;So;0;ON;;;;;N;;;;; 1F0AA;PLAYING CARD TEN OF SPADES;So;0;ON;;;;;N;;;;; 1F0AB;PLAYING CARD JACK OF SPADES;So;0;ON;;;;;N;;;;; 1F0AC;PLAYING CARD KNIGHT OF SPADES;So;0;ON;;;;;N;;;;; 1F0AD;PLAYING CARD QUEEN OF SPADES;So;0;ON;;;;;N;;;;; 1F0AE;PLAYING CARD KING OF SPADES;So;0;ON;;;;;N;;;;; 1F0B1;PLAYING CARD ACE OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B2;PLAYING CARD TWO OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B3;PLAYING CARD THREE OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B4;PLAYING CARD FOUR OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B5;PLAYING CARD FIVE OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B6;PLAYING CARD SIX OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B7;PLAYING CARD SEVEN OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B8;PLAYING CARD EIGHT OF HEARTS;So;0;ON;;;;;N;;;;; 1F0B9;PLAYING CARD NINE OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BA;PLAYING CARD TEN OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BB;PLAYING CARD JACK OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BC;PLAYING CARD KNIGHT OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BD;PLAYING CARD QUEEN OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BE;PLAYING CARD KING OF HEARTS;So;0;ON;;;;;N;;;;; 1F0BF;PLAYING CARD RED JOKER;So;0;ON;;;;;N;;;;; 1F0C1;PLAYING CARD ACE OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C2;PLAYING CARD TWO OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C3;PLAYING CARD THREE OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C4;PLAYING CARD FOUR OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C5;PLAYING CARD FIVE OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C6;PLAYING CARD SIX OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C7;PLAYING CARD SEVEN OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C8;PLAYING CARD EIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0C9;PLAYING CARD NINE OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CA;PLAYING CARD TEN OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CB;PLAYING CARD JACK OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CC;PLAYING CARD KNIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CD;PLAYING CARD QUEEN OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CE;PLAYING CARD KING OF DIAMONDS;So;0;ON;;;;;N;;;;; 1F0CF;PLAYING CARD BLACK JOKER;So;0;ON;;;;;N;;;;; 1F0D1;PLAYING CARD ACE OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D2;PLAYING CARD TWO OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D3;PLAYING CARD THREE OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D4;PLAYING CARD FOUR OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D5;PLAYING CARD FIVE OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D6;PLAYING CARD SIX OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D7;PLAYING CARD SEVEN OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D8;PLAYING CARD EIGHT OF CLUBS;So;0;ON;;;;;N;;;;; 1F0D9;PLAYING CARD NINE OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DA;PLAYING CARD TEN OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DB;PLAYING CARD JACK OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DC;PLAYING CARD KNIGHT OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DD;PLAYING CARD QUEEN OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DE;PLAYING CARD KING OF CLUBS;So;0;ON;;;;;N;;;;; 1F0DF;PLAYING CARD WHITE JOKER;So;0;ON;;;;;N;;;;; 1F0E0;PLAYING CARD FOOL;So;0;ON;;;;;N;;;;; 1F0E1;PLAYING CARD TRUMP-1;So;0;ON;;;;;N;;;;; 1F0E2;PLAYING CARD TRUMP-2;So;0;ON;;;;;N;;;;; 1F0E3;PLAYING CARD TRUMP-3;So;0;ON;;;;;N;;;;; 1F0E4;PLAYING CARD TRUMP-4;So;0;ON;;;;;N;;;;; 1F0E5;PLAYING CARD TRUMP-5;So;0;ON;;;;;N;;;;; 1F0E6;PLAYING CARD TRUMP-6;So;0;ON;;;;;N;;;;; 1F0E7;PLAYING CARD TRUMP-7;So;0;ON;;;;;N;;;;; 1F0E8;PLAYING CARD TRUMP-8;So;0;ON;;;;;N;;;;; 1F0E9;PLAYING CARD TRUMP-9;So;0;ON;;;;;N;;;;; 1F0EA;PLAYING CARD TRUMP-10;So;0;ON;;;;;N;;;;; 1F0EB;PLAYING CARD TRUMP-11;So;0;ON;;;;;N;;;;; 1F0EC;PLAYING CARD TRUMP-12;So;0;ON;;;;;N;;;;; 1F0ED;PLAYING CARD TRUMP-13;So;0;ON;;;;;N;;;;; 1F0EE;PLAYING CARD TRUMP-14;So;0;ON;;;;;N;;;;; 1F0EF;PLAYING CARD TRUMP-15;So;0;ON;;;;;N;;;;; 1F0F0;PLAYING CARD TRUMP-16;So;0;ON;;;;;N;;;;; 1F0F1;PLAYING CARD TRUMP-17;So;0;ON;;;;;N;;;;; 1F0F2;PLAYING CARD TRUMP-18;So;0;ON;;;;;N;;;;; 1F0F3;PLAYING CARD TRUMP-19;So;0;ON;;;;;N;;;;; 1F0F4;PLAYING CARD TRUMP-20;So;0;ON;;;;;N;;;;; 1F0F5;PLAYING CARD TRUMP-21;So;0;ON;;;;;N;;;;; 1F100;DIGIT ZERO FULL STOP;No;0;EN; 0030 002E;;0;0;N;;;;; 1F101;DIGIT ZERO COMMA;No;0;EN; 0030 002C;;0;0;N;;;;; 1F102;DIGIT ONE COMMA;No;0;EN; 0031 002C;;1;1;N;;;;; 1F103;DIGIT TWO COMMA;No;0;EN; 0032 002C;;2;2;N;;;;; 1F104;DIGIT THREE COMMA;No;0;EN; 0033 002C;;3;3;N;;;;; 1F105;DIGIT FOUR COMMA;No;0;EN; 0034 002C;;4;4;N;;;;; 1F106;DIGIT FIVE COMMA;No;0;EN; 0035 002C;;5;5;N;;;;; 1F107;DIGIT SIX COMMA;No;0;EN; 0036 002C;;6;6;N;;;;; 1F108;DIGIT SEVEN COMMA;No;0;EN; 0037 002C;;7;7;N;;;;; 1F109;DIGIT EIGHT COMMA;No;0;EN; 0038 002C;;8;8;N;;;;; 1F10A;DIGIT NINE COMMA;No;0;EN; 0039 002C;;9;9;N;;;;; 1F10B;DINGBAT CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;; 1F10C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;; 1F110;PARENTHESIZED LATIN CAPITAL LETTER A;So;0;L; 0028 0041 0029;;;;N;;;;; 1F111;PARENTHESIZED LATIN CAPITAL LETTER B;So;0;L; 0028 0042 0029;;;;N;;;;; 1F112;PARENTHESIZED LATIN CAPITAL LETTER C;So;0;L; 0028 0043 0029;;;;N;;;;; 1F113;PARENTHESIZED LATIN CAPITAL LETTER D;So;0;L; 0028 0044 0029;;;;N;;;;; 1F114;PARENTHESIZED LATIN CAPITAL LETTER E;So;0;L; 0028 0045 0029;;;;N;;;;; 1F115;PARENTHESIZED LATIN CAPITAL LETTER F;So;0;L; 0028 0046 0029;;;;N;;;;; 1F116;PARENTHESIZED LATIN CAPITAL LETTER G;So;0;L; 0028 0047 0029;;;;N;;;;; 1F117;PARENTHESIZED LATIN CAPITAL LETTER H;So;0;L; 0028 0048 0029;;;;N;;;;; 1F118;PARENTHESIZED LATIN CAPITAL LETTER I;So;0;L; 0028 0049 0029;;;;N;;;;; 1F119;PARENTHESIZED LATIN CAPITAL LETTER J;So;0;L; 0028 004A 0029;;;;N;;;;; 1F11A;PARENTHESIZED LATIN CAPITAL LETTER K;So;0;L; 0028 004B 0029;;;;N;;;;; 1F11B;PARENTHESIZED LATIN CAPITAL LETTER L;So;0;L; 0028 004C 0029;;;;N;;;;; 1F11C;PARENTHESIZED LATIN CAPITAL LETTER M;So;0;L; 0028 004D 0029;;;;N;;;;; 1F11D;PARENTHESIZED LATIN CAPITAL LETTER N;So;0;L; 0028 004E 0029;;;;N;;;;; 1F11E;PARENTHESIZED LATIN CAPITAL LETTER O;So;0;L; 0028 004F 0029;;;;N;;;;; 1F11F;PARENTHESIZED LATIN CAPITAL LETTER P;So;0;L; 0028 0050 0029;;;;N;;;;; 1F120;PARENTHESIZED LATIN CAPITAL LETTER Q;So;0;L; 0028 0051 0029;;;;N;;;;; 1F121;PARENTHESIZED LATIN CAPITAL LETTER R;So;0;L; 0028 0052 0029;;;;N;;;;; 1F122;PARENTHESIZED LATIN CAPITAL LETTER S;So;0;L; 0028 0053 0029;;;;N;;;;; 1F123;PARENTHESIZED LATIN CAPITAL LETTER T;So;0;L; 0028 0054 0029;;;;N;;;;; 1F124;PARENTHESIZED LATIN CAPITAL LETTER U;So;0;L; 0028 0055 0029;;;;N;;;;; 1F125;PARENTHESIZED LATIN CAPITAL LETTER V;So;0;L; 0028 0056 0029;;;;N;;;;; 1F126;PARENTHESIZED LATIN CAPITAL LETTER W;So;0;L; 0028 0057 0029;;;;N;;;;; 1F127;PARENTHESIZED LATIN CAPITAL LETTER X;So;0;L; 0028 0058 0029;;;;N;;;;; 1F128;PARENTHESIZED LATIN CAPITAL LETTER Y;So;0;L; 0028 0059 0029;;;;N;;;;; 1F129;PARENTHESIZED LATIN CAPITAL LETTER Z;So;0;L; 0028 005A 0029;;;;N;;;;; 1F12A;TORTOISE SHELL BRACKETED LATIN CAPITAL LETTER S;So;0;L; 3014 0053 3015;;;;N;;;;; 1F12B;CIRCLED ITALIC LATIN CAPITAL LETTER C;So;0;L; 0043;;;;N;;;;; 1F12C;CIRCLED ITALIC LATIN CAPITAL LETTER R;So;0;L; 0052;;;;N;;;;; 1F12D;CIRCLED CD;So;0;L; 0043 0044;;;;N;;;;; 1F12E;CIRCLED WZ;So;0;L; 0057 005A;;;;N;;;;; 1F130;SQUARED LATIN CAPITAL LETTER A;So;0;L; 0041;;;;N;;;;; 1F131;SQUARED LATIN CAPITAL LETTER B;So;0;L; 0042;;;;N;;;;; 1F132;SQUARED LATIN CAPITAL LETTER C;So;0;L; 0043;;;;N;;;;; 1F133;SQUARED LATIN CAPITAL LETTER D;So;0;L; 0044;;;;N;;;;; 1F134;SQUARED LATIN CAPITAL LETTER E;So;0;L; 0045;;;;N;;;;; 1F135;SQUARED LATIN CAPITAL LETTER F;So;0;L; 0046;;;;N;;;;; 1F136;SQUARED LATIN CAPITAL LETTER G;So;0;L; 0047;;;;N;;;;; 1F137;SQUARED LATIN CAPITAL LETTER H;So;0;L; 0048;;;;N;;;;; 1F138;SQUARED LATIN CAPITAL LETTER I;So;0;L; 0049;;;;N;;;;; 1F139;SQUARED LATIN CAPITAL LETTER J;So;0;L; 004A;;;;N;;;;; 1F13A;SQUARED LATIN CAPITAL LETTER K;So;0;L; 004B;;;;N;;;;; 1F13B;SQUARED LATIN CAPITAL LETTER L;So;0;L; 004C;;;;N;;;;; 1F13C;SQUARED LATIN CAPITAL LETTER M;So;0;L; 004D;;;;N;;;;; 1F13D;SQUARED LATIN CAPITAL LETTER N;So;0;L; 004E;;;;N;;;;; 1F13E;SQUARED LATIN CAPITAL LETTER O;So;0;L; 004F;;;;N;;;;; 1F13F;SQUARED LATIN CAPITAL LETTER P;So;0;L; 0050;;;;N;;;;; 1F140;SQUARED LATIN CAPITAL LETTER Q;So;0;L; 0051;;;;N;;;;; 1F141;SQUARED LATIN CAPITAL LETTER R;So;0;L; 0052;;;;N;;;;; 1F142;SQUARED LATIN CAPITAL LETTER S;So;0;L; 0053;;;;N;;;;; 1F143;SQUARED LATIN CAPITAL LETTER T;So;0;L; 0054;;;;N;;;;; 1F144;SQUARED LATIN CAPITAL LETTER U;So;0;L; 0055;;;;N;;;;; 1F145;SQUARED LATIN CAPITAL LETTER V;So;0;L; 0056;;;;N;;;;; 1F146;SQUARED LATIN CAPITAL LETTER W;So;0;L; 0057;;;;N;;;;; 1F147;SQUARED LATIN CAPITAL LETTER X;So;0;L; 0058;;;;N;;;;; 1F148;SQUARED LATIN CAPITAL LETTER Y;So;0;L; 0059;;;;N;;;;; 1F149;SQUARED LATIN CAPITAL LETTER Z;So;0;L; 005A;;;;N;;;;; 1F14A;SQUARED HV;So;0;L; 0048 0056;;;;N;;;;; 1F14B;SQUARED MV;So;0;L; 004D 0056;;;;N;;;;; 1F14C;SQUARED SD;So;0;L; 0053 0044;;;;N;;;;; 1F14D;SQUARED SS;So;0;L; 0053 0053;;;;N;;;;; 1F14E;SQUARED PPV;So;0;L; 0050 0050 0056;;;;N;;;;; 1F14F;SQUARED WC;So;0;L; 0057 0043;;;;N;;;;; 1F150;NEGATIVE CIRCLED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;; 1F151;NEGATIVE CIRCLED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;; 1F152;NEGATIVE CIRCLED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;; 1F153;NEGATIVE CIRCLED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;; 1F154;NEGATIVE CIRCLED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;; 1F155;NEGATIVE CIRCLED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;; 1F156;NEGATIVE CIRCLED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;; 1F157;NEGATIVE CIRCLED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;; 1F158;NEGATIVE CIRCLED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;; 1F159;NEGATIVE CIRCLED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;; 1F15A;NEGATIVE CIRCLED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;; 1F15B;NEGATIVE CIRCLED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;; 1F15C;NEGATIVE CIRCLED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;; 1F15D;NEGATIVE CIRCLED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;; 1F15E;NEGATIVE CIRCLED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;; 1F15F;NEGATIVE CIRCLED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; 1F160;NEGATIVE CIRCLED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;; 1F161;NEGATIVE CIRCLED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;; 1F162;NEGATIVE CIRCLED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;; 1F163;NEGATIVE CIRCLED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;; 1F164;NEGATIVE CIRCLED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;; 1F165;NEGATIVE CIRCLED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;; 1F166;NEGATIVE CIRCLED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;; 1F167;NEGATIVE CIRCLED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;; 1F168;NEGATIVE CIRCLED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;; 1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;; 1F16A;RAISED MC SIGN;So;0;ON; 004D 0043;;;;N;;;;; 1F16B;RAISED MD SIGN;So;0;ON; 004D 0044;;;;N;;;;; 1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;; 1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;; 1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;; 1F173;NEGATIVE SQUARED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;; 1F174;NEGATIVE SQUARED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;; 1F175;NEGATIVE SQUARED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;; 1F176;NEGATIVE SQUARED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;; 1F177;NEGATIVE SQUARED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;; 1F178;NEGATIVE SQUARED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;; 1F179;NEGATIVE SQUARED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;; 1F17A;NEGATIVE SQUARED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;; 1F17B;NEGATIVE SQUARED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;; 1F17C;NEGATIVE SQUARED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;; 1F17D;NEGATIVE SQUARED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;; 1F17E;NEGATIVE SQUARED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;; 1F17F;NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; 1F180;NEGATIVE SQUARED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;; 1F181;NEGATIVE SQUARED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;; 1F182;NEGATIVE SQUARED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;; 1F183;NEGATIVE SQUARED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;; 1F184;NEGATIVE SQUARED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;; 1F185;NEGATIVE SQUARED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;; 1F186;NEGATIVE SQUARED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;; 1F187;NEGATIVE SQUARED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;; 1F188;NEGATIVE SQUARED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;; 1F189;NEGATIVE SQUARED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;; 1F18A;CROSSED NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; 1F18B;NEGATIVE SQUARED IC;So;0;L;;;;;N;;;;; 1F18C;NEGATIVE SQUARED PA;So;0;L;;;;;N;;;;; 1F18D;NEGATIVE SQUARED SA;So;0;L;;;;;N;;;;; 1F18E;NEGATIVE SQUARED AB;So;0;L;;;;;N;;;;; 1F18F;NEGATIVE SQUARED WC;So;0;L;;;;;N;;;;; 1F190;SQUARE DJ;So;0;L; 0044 004A;;;;N;;;;; 1F191;SQUARED CL;So;0;L;;;;;N;;;;; 1F192;SQUARED COOL;So;0;L;;;;;N;;;;; 1F193;SQUARED FREE;So;0;L;;;;;N;;;;; 1F194;SQUARED ID;So;0;L;;;;;N;;;;; 1F195;SQUARED NEW;So;0;L;;;;;N;;;;; 1F196;SQUARED NG;So;0;L;;;;;N;;;;; 1F197;SQUARED OK;So;0;L;;;;;N;;;;; 1F198;SQUARED SOS;So;0;L;;;;;N;;;;; 1F199;SQUARED UP WITH EXCLAMATION MARK;So;0;L;;;;;N;;;;; 1F19A;SQUARED VS;So;0;L;;;;;N;;;;; 1F19B;SQUARED THREE D;So;0;L;;;;;N;;;;; 1F19C;SQUARED SECOND SCREEN;So;0;L;;;;;N;;;;; 1F19D;SQUARED TWO K;So;0;L;;;;;N;;;;; 1F19E;SQUARED FOUR K;So;0;L;;;;;N;;;;; 1F19F;SQUARED EIGHT K;So;0;L;;;;;N;;;;; 1F1A0;SQUARED FIVE POINT ONE;So;0;L;;;;;N;;;;; 1F1A1;SQUARED SEVEN POINT ONE;So;0;L;;;;;N;;;;; 1F1A2;SQUARED TWENTY-TWO POINT TWO;So;0;L;;;;;N;;;;; 1F1A3;SQUARED SIXTY P;So;0;L;;;;;N;;;;; 1F1A4;SQUARED ONE HUNDRED TWENTY P;So;0;L;;;;;N;;;;; 1F1A5;SQUARED LATIN SMALL LETTER D;So;0;L;;;;;N;;;;; 1F1A6;SQUARED HC;So;0;L;;;;;N;;;;; 1F1A7;SQUARED HDR;So;0;L;;;;;N;;;;; 1F1A8;SQUARED HI-RES;So;0;L;;;;;N;;;;; 1F1A9;SQUARED LOSSLESS;So;0;L;;;;;N;;;;; 1F1AA;SQUARED SHV;So;0;L;;;;;N;;;;; 1F1AB;SQUARED UHD;So;0;L;;;;;N;;;;; 1F1AC;SQUARED VOD;So;0;L;;;;;N;;;;; 1F1E6;REGIONAL INDICATOR SYMBOL LETTER A;So;0;L;;;;;N;;;;; 1F1E7;REGIONAL INDICATOR SYMBOL LETTER B;So;0;L;;;;;N;;;;; 1F1E8;REGIONAL INDICATOR SYMBOL LETTER C;So;0;L;;;;;N;;;;; 1F1E9;REGIONAL INDICATOR SYMBOL LETTER D;So;0;L;;;;;N;;;;; 1F1EA;REGIONAL INDICATOR SYMBOL LETTER E;So;0;L;;;;;N;;;;; 1F1EB;REGIONAL INDICATOR SYMBOL LETTER F;So;0;L;;;;;N;;;;; 1F1EC;REGIONAL INDICATOR SYMBOL LETTER G;So;0;L;;;;;N;;;;; 1F1ED;REGIONAL INDICATOR SYMBOL LETTER H;So;0;L;;;;;N;;;;; 1F1EE;REGIONAL INDICATOR SYMBOL LETTER I;So;0;L;;;;;N;;;;; 1F1EF;REGIONAL INDICATOR SYMBOL LETTER J;So;0;L;;;;;N;;;;; 1F1F0;REGIONAL INDICATOR SYMBOL LETTER K;So;0;L;;;;;N;;;;; 1F1F1;REGIONAL INDICATOR SYMBOL LETTER L;So;0;L;;;;;N;;;;; 1F1F2;REGIONAL INDICATOR SYMBOL LETTER M;So;0;L;;;;;N;;;;; 1F1F3;REGIONAL INDICATOR SYMBOL LETTER N;So;0;L;;;;;N;;;;; 1F1F4;REGIONAL INDICATOR SYMBOL LETTER O;So;0;L;;;;;N;;;;; 1F1F5;REGIONAL INDICATOR SYMBOL LETTER P;So;0;L;;;;;N;;;;; 1F1F6;REGIONAL INDICATOR SYMBOL LETTER Q;So;0;L;;;;;N;;;;; 1F1F7;REGIONAL INDICATOR SYMBOL LETTER R;So;0;L;;;;;N;;;;; 1F1F8;REGIONAL INDICATOR SYMBOL LETTER S;So;0;L;;;;;N;;;;; 1F1F9;REGIONAL INDICATOR SYMBOL LETTER T;So;0;L;;;;;N;;;;; 1F1FA;REGIONAL INDICATOR SYMBOL LETTER U;So;0;L;;;;;N;;;;; 1F1FB;REGIONAL INDICATOR SYMBOL LETTER V;So;0;L;;;;;N;;;;; 1F1FC;REGIONAL INDICATOR SYMBOL LETTER W;So;0;L;;;;;N;;;;; 1F1FD;REGIONAL INDICATOR SYMBOL LETTER X;So;0;L;;;;;N;;;;; 1F1FE;REGIONAL INDICATOR SYMBOL LETTER Y;So;0;L;;;;;N;;;;; 1F1FF;REGIONAL INDICATOR SYMBOL LETTER Z;So;0;L;;;;;N;;;;; 1F200;SQUARE HIRAGANA HOKA;So;0;L; 307B 304B;;;;N;;;;; 1F201;SQUARED KATAKANA KOKO;So;0;L; 30B3 30B3;;;;N;;;;; 1F202;SQUARED KATAKANA SA;So;0;L; 30B5;;;;N;;;;; 1F210;SQUARED CJK UNIFIED IDEOGRAPH-624B;So;0;L; 624B;;;;N;;;;; 1F211;SQUARED CJK UNIFIED IDEOGRAPH-5B57;So;0;L; 5B57;;;;N;;;;; 1F212;SQUARED CJK UNIFIED IDEOGRAPH-53CC;So;0;L; 53CC;;;;N;;;;; 1F213;SQUARED KATAKANA DE;So;0;L; 30C7;;;;N;;;;; 1F214;SQUARED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L; 4E8C;;;;N;;;;; 1F215;SQUARED CJK UNIFIED IDEOGRAPH-591A;So;0;L; 591A;;;;N;;;;; 1F216;SQUARED CJK UNIFIED IDEOGRAPH-89E3;So;0;L; 89E3;;;;N;;;;; 1F217;SQUARED CJK UNIFIED IDEOGRAPH-5929;So;0;L; 5929;;;;N;;;;; 1F218;SQUARED CJK UNIFIED IDEOGRAPH-4EA4;So;0;L; 4EA4;;;;N;;;;; 1F219;SQUARED CJK UNIFIED IDEOGRAPH-6620;So;0;L; 6620;;;;N;;;;; 1F21A;SQUARED CJK UNIFIED IDEOGRAPH-7121;So;0;L; 7121;;;;N;;;;; 1F21B;SQUARED CJK UNIFIED IDEOGRAPH-6599;So;0;L; 6599;;;;N;;;;; 1F21C;SQUARED CJK UNIFIED IDEOGRAPH-524D;So;0;L; 524D;;;;N;;;;; 1F21D;SQUARED CJK UNIFIED IDEOGRAPH-5F8C;So;0;L; 5F8C;;;;N;;;;; 1F21E;SQUARED CJK UNIFIED IDEOGRAPH-518D;So;0;L; 518D;;;;N;;;;; 1F21F;SQUARED CJK UNIFIED IDEOGRAPH-65B0;So;0;L; 65B0;;;;N;;;;; 1F220;SQUARED CJK UNIFIED IDEOGRAPH-521D;So;0;L; 521D;;;;N;;;;; 1F221;SQUARED CJK UNIFIED IDEOGRAPH-7D42;So;0;L; 7D42;;;;N;;;;; 1F222;SQUARED CJK UNIFIED IDEOGRAPH-751F;So;0;L; 751F;;;;N;;;;; 1F223;SQUARED CJK UNIFIED IDEOGRAPH-8CA9;So;0;L; 8CA9;;;;N;;;;; 1F224;SQUARED CJK UNIFIED IDEOGRAPH-58F0;So;0;L; 58F0;;;;N;;;;; 1F225;SQUARED CJK UNIFIED IDEOGRAPH-5439;So;0;L; 5439;;;;N;;;;; 1F226;SQUARED CJK UNIFIED IDEOGRAPH-6F14;So;0;L; 6F14;;;;N;;;;; 1F227;SQUARED CJK UNIFIED IDEOGRAPH-6295;So;0;L; 6295;;;;N;;;;; 1F228;SQUARED CJK UNIFIED IDEOGRAPH-6355;So;0;L; 6355;;;;N;;;;; 1F229;SQUARED CJK UNIFIED IDEOGRAPH-4E00;So;0;L; 4E00;;;;N;;;;; 1F22A;SQUARED CJK UNIFIED IDEOGRAPH-4E09;So;0;L; 4E09;;;;N;;;;; 1F22B;SQUARED CJK UNIFIED IDEOGRAPH-904A;So;0;L; 904A;;;;N;;;;; 1F22C;SQUARED CJK UNIFIED IDEOGRAPH-5DE6;So;0;L; 5DE6;;;;N;;;;; 1F22D;SQUARED CJK UNIFIED IDEOGRAPH-4E2D;So;0;L; 4E2D;;;;N;;;;; 1F22E;SQUARED CJK UNIFIED IDEOGRAPH-53F3;So;0;L; 53F3;;;;N;;;;; 1F22F;SQUARED CJK UNIFIED IDEOGRAPH-6307;So;0;L; 6307;;;;N;;;;; 1F230;SQUARED CJK UNIFIED IDEOGRAPH-8D70;So;0;L; 8D70;;;;N;;;;; 1F231;SQUARED CJK UNIFIED IDEOGRAPH-6253;So;0;L; 6253;;;;N;;;;; 1F232;SQUARED CJK UNIFIED IDEOGRAPH-7981;So;0;L; 7981;;;;N;;;;; 1F233;SQUARED CJK UNIFIED IDEOGRAPH-7A7A;So;0;L; 7A7A;;;;N;;;;; 1F234;SQUARED CJK UNIFIED IDEOGRAPH-5408;So;0;L; 5408;;;;N;;;;; 1F235;SQUARED CJK UNIFIED IDEOGRAPH-6E80;So;0;L; 6E80;;;;N;;;;; 1F236;SQUARED CJK UNIFIED IDEOGRAPH-6709;So;0;L; 6709;;;;N;;;;; 1F237;SQUARED CJK UNIFIED IDEOGRAPH-6708;So;0;L; 6708;;;;N;;;;; 1F238;SQUARED CJK UNIFIED IDEOGRAPH-7533;So;0;L; 7533;;;;N;;;;; 1F239;SQUARED CJK UNIFIED IDEOGRAPH-5272;So;0;L; 5272;;;;N;;;;; 1F23A;SQUARED CJK UNIFIED IDEOGRAPH-55B6;So;0;L; 55B6;;;;N;;;;; 1F23B;SQUARED CJK UNIFIED IDEOGRAPH-914D;So;0;L; 914D;;;;N;;;;; 1F240;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C;So;0;L; 3014 672C 3015;;;;N;;;;; 1F241;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E09;So;0;L; 3014 4E09 3015;;;;N;;;;; 1F242;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L; 3014 4E8C 3015;;;;N;;;;; 1F243;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-5B89;So;0;L; 3014 5B89 3015;;;;N;;;;; 1F244;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-70B9;So;0;L; 3014 70B9 3015;;;;N;;;;; 1F245;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6253;So;0;L; 3014 6253 3015;;;;N;;;;; 1F246;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-76D7;So;0;L; 3014 76D7 3015;;;;N;;;;; 1F247;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-52DD;So;0;L; 3014 52DD 3015;;;;N;;;;; 1F248;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557;So;0;L; 3014 6557 3015;;;;N;;;;; 1F250;CIRCLED IDEOGRAPH ADVANTAGE;So;0;L; 5F97;;;;N;;;;; 1F251;CIRCLED IDEOGRAPH ACCEPT;So;0;L; 53EF;;;;N;;;;; 1F300;CYCLONE;So;0;ON;;;;;N;;;;; 1F301;FOGGY;So;0;ON;;;;;N;;;;; 1F302;CLOSED UMBRELLA;So;0;ON;;;;;N;;;;; 1F303;NIGHT WITH STARS;So;0;ON;;;;;N;;;;; 1F304;SUNRISE OVER MOUNTAINS;So;0;ON;;;;;N;;;;; 1F305;SUNRISE;So;0;ON;;;;;N;;;;; 1F306;CITYSCAPE AT DUSK;So;0;ON;;;;;N;;;;; 1F307;SUNSET OVER BUILDINGS;So;0;ON;;;;;N;;;;; 1F308;RAINBOW;So;0;ON;;;;;N;;;;; 1F309;BRIDGE AT NIGHT;So;0;ON;;;;;N;;;;; 1F30A;WATER WAVE;So;0;ON;;;;;N;;;;; 1F30B;VOLCANO;So;0;ON;;;;;N;;;;; 1F30C;MILKY WAY;So;0;ON;;;;;N;;;;; 1F30D;EARTH GLOBE EUROPE-AFRICA;So;0;ON;;;;;N;;;;; 1F30E;EARTH GLOBE AMERICAS;So;0;ON;;;;;N;;;;; 1F30F;EARTH GLOBE ASIA-AUSTRALIA;So;0;ON;;;;;N;;;;; 1F310;GLOBE WITH MERIDIANS;So;0;ON;;;;;N;;;;; 1F311;NEW MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F312;WAXING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F313;FIRST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F314;WAXING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F315;FULL MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F316;WANING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F317;LAST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F318;WANING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;; 1F319;CRESCENT MOON;So;0;ON;;;;;N;;;;; 1F31A;NEW MOON WITH FACE;So;0;ON;;;;;N;;;;; 1F31B;FIRST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;; 1F31C;LAST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;; 1F31D;FULL MOON WITH FACE;So;0;ON;;;;;N;;;;; 1F31E;SUN WITH FACE;So;0;ON;;;;;N;;;;; 1F31F;GLOWING STAR;So;0;ON;;;;;N;;;;; 1F320;SHOOTING STAR;So;0;ON;;;;;N;;;;; 1F321;THERMOMETER;So;0;ON;;;;;N;;;;; 1F322;BLACK DROPLET;So;0;ON;;;;;N;;;;; 1F323;WHITE SUN;So;0;ON;;;;;N;;;;; 1F324;WHITE SUN WITH SMALL CLOUD;So;0;ON;;;;;N;;;;; 1F325;WHITE SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;; 1F326;WHITE SUN BEHIND CLOUD WITH RAIN;So;0;ON;;;;;N;;;;; 1F327;CLOUD WITH RAIN;So;0;ON;;;;;N;;;;; 1F328;CLOUD WITH SNOW;So;0;ON;;;;;N;;;;; 1F329;CLOUD WITH LIGHTNING;So;0;ON;;;;;N;;;;; 1F32A;CLOUD WITH TORNADO;So;0;ON;;;;;N;;;;; 1F32B;FOG;So;0;ON;;;;;N;;;;; 1F32C;WIND BLOWING FACE;So;0;ON;;;;;N;;;;; 1F32D;HOT DOG;So;0;ON;;;;;N;;;;; 1F32E;TACO;So;0;ON;;;;;N;;;;; 1F32F;BURRITO;So;0;ON;;;;;N;;;;; 1F330;CHESTNUT;So;0;ON;;;;;N;;;;; 1F331;SEEDLING;So;0;ON;;;;;N;;;;; 1F332;EVERGREEN TREE;So;0;ON;;;;;N;;;;; 1F333;DECIDUOUS TREE;So;0;ON;;;;;N;;;;; 1F334;PALM TREE;So;0;ON;;;;;N;;;;; 1F335;CACTUS;So;0;ON;;;;;N;;;;; 1F336;HOT PEPPER;So;0;ON;;;;;N;;;;; 1F337;TULIP;So;0;ON;;;;;N;;;;; 1F338;CHERRY BLOSSOM;So;0;ON;;;;;N;;;;; 1F339;ROSE;So;0;ON;;;;;N;;;;; 1F33A;HIBISCUS;So;0;ON;;;;;N;;;;; 1F33B;SUNFLOWER;So;0;ON;;;;;N;;;;; 1F33C;BLOSSOM;So;0;ON;;;;;N;;;;; 1F33D;EAR OF MAIZE;So;0;ON;;;;;N;;;;; 1F33E;EAR OF RICE;So;0;ON;;;;;N;;;;; 1F33F;HERB;So;0;ON;;;;;N;;;;; 1F340;FOUR LEAF CLOVER;So;0;ON;;;;;N;;;;; 1F341;MAPLE LEAF;So;0;ON;;;;;N;;;;; 1F342;FALLEN LEAF;So;0;ON;;;;;N;;;;; 1F343;LEAF FLUTTERING IN WIND;So;0;ON;;;;;N;;;;; 1F344;MUSHROOM;So;0;ON;;;;;N;;;;; 1F345;TOMATO;So;0;ON;;;;;N;;;;; 1F346;AUBERGINE;So;0;ON;;;;;N;;;;; 1F347;GRAPES;So;0;ON;;;;;N;;;;; 1F348;MELON;So;0;ON;;;;;N;;;;; 1F349;WATERMELON;So;0;ON;;;;;N;;;;; 1F34A;TANGERINE;So;0;ON;;;;;N;;;;; 1F34B;LEMON;So;0;ON;;;;;N;;;;; 1F34C;BANANA;So;0;ON;;;;;N;;;;; 1F34D;PINEAPPLE;So;0;ON;;;;;N;;;;; 1F34E;RED APPLE;So;0;ON;;;;;N;;;;; 1F34F;GREEN APPLE;So;0;ON;;;;;N;;;;; 1F350;PEAR;So;0;ON;;;;;N;;;;; 1F351;PEACH;So;0;ON;;;;;N;;;;; 1F352;CHERRIES;So;0;ON;;;;;N;;;;; 1F353;STRAWBERRY;So;0;ON;;;;;N;;;;; 1F354;HAMBURGER;So;0;ON;;;;;N;;;;; 1F355;SLICE OF PIZZA;So;0;ON;;;;;N;;;;; 1F356;MEAT ON BONE;So;0;ON;;;;;N;;;;; 1F357;POULTRY LEG;So;0;ON;;;;;N;;;;; 1F358;RICE CRACKER;So;0;ON;;;;;N;;;;; 1F359;RICE BALL;So;0;ON;;;;;N;;;;; 1F35A;COOKED RICE;So;0;ON;;;;;N;;;;; 1F35B;CURRY AND RICE;So;0;ON;;;;;N;;;;; 1F35C;STEAMING BOWL;So;0;ON;;;;;N;;;;; 1F35D;SPAGHETTI;So;0;ON;;;;;N;;;;; 1F35E;BREAD;So;0;ON;;;;;N;;;;; 1F35F;FRENCH FRIES;So;0;ON;;;;;N;;;;; 1F360;ROASTED SWEET POTATO;So;0;ON;;;;;N;;;;; 1F361;DANGO;So;0;ON;;;;;N;;;;; 1F362;ODEN;So;0;ON;;;;;N;;;;; 1F363;SUSHI;So;0;ON;;;;;N;;;;; 1F364;FRIED SHRIMP;So;0;ON;;;;;N;;;;; 1F365;FISH CAKE WITH SWIRL DESIGN;So;0;ON;;;;;N;;;;; 1F366;SOFT ICE CREAM;So;0;ON;;;;;N;;;;; 1F367;SHAVED ICE;So;0;ON;;;;;N;;;;; 1F368;ICE CREAM;So;0;ON;;;;;N;;;;; 1F369;DOUGHNUT;So;0;ON;;;;;N;;;;; 1F36A;COOKIE;So;0;ON;;;;;N;;;;; 1F36B;CHOCOLATE BAR;So;0;ON;;;;;N;;;;; 1F36C;CANDY;So;0;ON;;;;;N;;;;; 1F36D;LOLLIPOP;So;0;ON;;;;;N;;;;; 1F36E;CUSTARD;So;0;ON;;;;;N;;;;; 1F36F;HONEY POT;So;0;ON;;;;;N;;;;; 1F370;SHORTCAKE;So;0;ON;;;;;N;;;;; 1F371;BENTO BOX;So;0;ON;;;;;N;;;;; 1F372;POT OF FOOD;So;0;ON;;;;;N;;;;; 1F373;COOKING;So;0;ON;;;;;N;;;;; 1F374;FORK AND KNIFE;So;0;ON;;;;;N;;;;; 1F375;TEACUP WITHOUT HANDLE;So;0;ON;;;;;N;;;;; 1F376;SAKE BOTTLE AND CUP;So;0;ON;;;;;N;;;;; 1F377;WINE GLASS;So;0;ON;;;;;N;;;;; 1F378;COCKTAIL GLASS;So;0;ON;;;;;N;;;;; 1F379;TROPICAL DRINK;So;0;ON;;;;;N;;;;; 1F37A;BEER MUG;So;0;ON;;;;;N;;;;; 1F37B;CLINKING BEER MUGS;So;0;ON;;;;;N;;;;; 1F37C;BABY BOTTLE;So;0;ON;;;;;N;;;;; 1F37D;FORK AND KNIFE WITH PLATE;So;0;ON;;;;;N;;;;; 1F37E;BOTTLE WITH POPPING CORK;So;0;ON;;;;;N;;;;; 1F37F;POPCORN;So;0;ON;;;;;N;;;;; 1F380;RIBBON;So;0;ON;;;;;N;;;;; 1F381;WRAPPED PRESENT;So;0;ON;;;;;N;;;;; 1F382;BIRTHDAY CAKE;So;0;ON;;;;;N;;;;; 1F383;JACK-O-LANTERN;So;0;ON;;;;;N;;;;; 1F384;CHRISTMAS TREE;So;0;ON;;;;;N;;;;; 1F385;FATHER CHRISTMAS;So;0;ON;;;;;N;;;;; 1F386;FIREWORKS;So;0;ON;;;;;N;;;;; 1F387;FIREWORK SPARKLER;So;0;ON;;;;;N;;;;; 1F388;BALLOON;So;0;ON;;;;;N;;;;; 1F389;PARTY POPPER;So;0;ON;;;;;N;;;;; 1F38A;CONFETTI BALL;So;0;ON;;;;;N;;;;; 1F38B;TANABATA TREE;So;0;ON;;;;;N;;;;; 1F38C;CROSSED FLAGS;So;0;ON;;;;;N;;;;; 1F38D;PINE DECORATION;So;0;ON;;;;;N;;;;; 1F38E;JAPANESE DOLLS;So;0;ON;;;;;N;;;;; 1F38F;CARP STREAMER;So;0;ON;;;;;N;;;;; 1F390;WIND CHIME;So;0;ON;;;;;N;;;;; 1F391;MOON VIEWING CEREMONY;So;0;ON;;;;;N;;;;; 1F392;SCHOOL SATCHEL;So;0;ON;;;;;N;;;;; 1F393;GRADUATION CAP;So;0;ON;;;;;N;;;;; 1F394;HEART WITH TIP ON THE LEFT;So;0;ON;;;;;N;;;;; 1F395;BOUQUET OF FLOWERS;So;0;ON;;;;;N;;;;; 1F396;MILITARY MEDAL;So;0;ON;;;;;N;;;;; 1F397;REMINDER RIBBON;So;0;ON;;;;;N;;;;; 1F398;MUSICAL KEYBOARD WITH JACKS;So;0;ON;;;;;N;;;;; 1F399;STUDIO MICROPHONE;So;0;ON;;;;;N;;;;; 1F39A;LEVEL SLIDER;So;0;ON;;;;;N;;;;; 1F39B;CONTROL KNOBS;So;0;ON;;;;;N;;;;; 1F39C;BEAMED ASCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;; 1F39D;BEAMED DESCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;; 1F39E;FILM FRAMES;So;0;ON;;;;;N;;;;; 1F39F;ADMISSION TICKETS;So;0;ON;;;;;N;;;;; 1F3A0;CAROUSEL HORSE;So;0;ON;;;;;N;;;;; 1F3A1;FERRIS WHEEL;So;0;ON;;;;;N;;;;; 1F3A2;ROLLER COASTER;So;0;ON;;;;;N;;;;; 1F3A3;FISHING POLE AND FISH;So;0;ON;;;;;N;;;;; 1F3A4;MICROPHONE;So;0;ON;;;;;N;;;;; 1F3A5;MOVIE CAMERA;So;0;ON;;;;;N;;;;; 1F3A6;CINEMA;So;0;ON;;;;;N;;;;; 1F3A7;HEADPHONE;So;0;ON;;;;;N;;;;; 1F3A8;ARTIST PALETTE;So;0;ON;;;;;N;;;;; 1F3A9;TOP HAT;So;0;ON;;;;;N;;;;; 1F3AA;CIRCUS TENT;So;0;ON;;;;;N;;;;; 1F3AB;TICKET;So;0;ON;;;;;N;;;;; 1F3AC;CLAPPER BOARD;So;0;ON;;;;;N;;;;; 1F3AD;PERFORMING ARTS;So;0;ON;;;;;N;;;;; 1F3AE;VIDEO GAME;So;0;ON;;;;;N;;;;; 1F3AF;DIRECT HIT;So;0;ON;;;;;N;;;;; 1F3B0;SLOT MACHINE;So;0;ON;;;;;N;;;;; 1F3B1;BILLIARDS;So;0;ON;;;;;N;;;;; 1F3B2;GAME DIE;So;0;ON;;;;;N;;;;; 1F3B3;BOWLING;So;0;ON;;;;;N;;;;; 1F3B4;FLOWER PLAYING CARDS;So;0;ON;;;;;N;;;;; 1F3B5;MUSICAL NOTE;So;0;ON;;;;;N;;;;; 1F3B6;MULTIPLE MUSICAL NOTES;So;0;ON;;;;;N;;;;; 1F3B7;SAXOPHONE;So;0;ON;;;;;N;;;;; 1F3B8;GUITAR;So;0;ON;;;;;N;;;;; 1F3B9;MUSICAL KEYBOARD;So;0;ON;;;;;N;;;;; 1F3BA;TRUMPET;So;0;ON;;;;;N;;;;; 1F3BB;VIOLIN;So;0;ON;;;;;N;;;;; 1F3BC;MUSICAL SCORE;So;0;ON;;;;;N;;;;; 1F3BD;RUNNING SHIRT WITH SASH;So;0;ON;;;;;N;;;;; 1F3BE;TENNIS RACQUET AND BALL;So;0;ON;;;;;N;;;;; 1F3BF;SKI AND SKI BOOT;So;0;ON;;;;;N;;;;; 1F3C0;BASKETBALL AND HOOP;So;0;ON;;;;;N;;;;; 1F3C1;CHEQUERED FLAG;So;0;ON;;;;;N;;;;; 1F3C2;SNOWBOARDER;So;0;ON;;;;;N;;;;; 1F3C3;RUNNER;So;0;ON;;;;;N;;;;; 1F3C4;SURFER;So;0;ON;;;;;N;;;;; 1F3C5;SPORTS MEDAL;So;0;ON;;;;;N;;;;; 1F3C6;TROPHY;So;0;ON;;;;;N;;;;; 1F3C7;HORSE RACING;So;0;ON;;;;;N;;;;; 1F3C8;AMERICAN FOOTBALL;So;0;ON;;;;;N;;;;; 1F3C9;RUGBY FOOTBALL;So;0;ON;;;;;N;;;;; 1F3CA;SWIMMER;So;0;ON;;;;;N;;;;; 1F3CB;WEIGHT LIFTER;So;0;ON;;;;;N;;;;; 1F3CC;GOLFER;So;0;ON;;;;;N;;;;; 1F3CD;RACING MOTORCYCLE;So;0;ON;;;;;N;;;;; 1F3CE;RACING CAR;So;0;ON;;;;;N;;;;; 1F3CF;CRICKET BAT AND BALL;So;0;ON;;;;;N;;;;; 1F3D0;VOLLEYBALL;So;0;ON;;;;;N;;;;; 1F3D1;FIELD HOCKEY STICK AND BALL;So;0;ON;;;;;N;;;;; 1F3D2;ICE HOCKEY STICK AND PUCK;So;0;ON;;;;;N;;;;; 1F3D3;TABLE TENNIS PADDLE AND BALL;So;0;ON;;;;;N;;;;; 1F3D4;SNOW CAPPED MOUNTAIN;So;0;ON;;;;;N;;;;; 1F3D5;CAMPING;So;0;ON;;;;;N;;;;; 1F3D6;BEACH WITH UMBRELLA;So;0;ON;;;;;N;;;;; 1F3D7;BUILDING CONSTRUCTION;So;0;ON;;;;;N;;;;; 1F3D8;HOUSE BUILDINGS;So;0;ON;;;;;N;;;;; 1F3D9;CITYSCAPE;So;0;ON;;;;;N;;;;; 1F3DA;DERELICT HOUSE BUILDING;So;0;ON;;;;;N;;;;; 1F3DB;CLASSICAL BUILDING;So;0;ON;;;;;N;;;;; 1F3DC;DESERT;So;0;ON;;;;;N;;;;; 1F3DD;DESERT ISLAND;So;0;ON;;;;;N;;;;; 1F3DE;NATIONAL PARK;So;0;ON;;;;;N;;;;; 1F3DF;STADIUM;So;0;ON;;;;;N;;;;; 1F3E0;HOUSE BUILDING;So;0;ON;;;;;N;;;;; 1F3E1;HOUSE WITH GARDEN;So;0;ON;;;;;N;;;;; 1F3E2;OFFICE BUILDING;So;0;ON;;;;;N;;;;; 1F3E3;JAPANESE POST OFFICE;So;0;ON;;;;;N;;;;; 1F3E4;EUROPEAN POST OFFICE;So;0;ON;;;;;N;;;;; 1F3E5;HOSPITAL;So;0;ON;;;;;N;;;;; 1F3E6;BANK;So;0;ON;;;;;N;;;;; 1F3E7;AUTOMATED TELLER MACHINE;So;0;ON;;;;;N;;;;; 1F3E8;HOTEL;So;0;ON;;;;;N;;;;; 1F3E9;LOVE HOTEL;So;0;ON;;;;;N;;;;; 1F3EA;CONVENIENCE STORE;So;0;ON;;;;;N;;;;; 1F3EB;SCHOOL;So;0;ON;;;;;N;;;;; 1F3EC;DEPARTMENT STORE;So;0;ON;;;;;N;;;;; 1F3ED;FACTORY;So;0;ON;;;;;N;;;;; 1F3EE;IZAKAYA LANTERN;So;0;ON;;;;;N;;;;; 1F3EF;JAPANESE CASTLE;So;0;ON;;;;;N;;;;; 1F3F0;EUROPEAN CASTLE;So;0;ON;;;;;N;;;;; 1F3F1;WHITE PENNANT;So;0;ON;;;;;N;;;;; 1F3F2;BLACK PENNANT;So;0;ON;;;;;N;;;;; 1F3F3;WAVING WHITE FLAG;So;0;ON;;;;;N;;;;; 1F3F4;WAVING BLACK FLAG;So;0;ON;;;;;N;;;;; 1F3F5;ROSETTE;So;0;ON;;;;;N;;;;; 1F3F6;BLACK ROSETTE;So;0;ON;;;;;N;;;;; 1F3F7;LABEL;So;0;ON;;;;;N;;;;; 1F3F8;BADMINTON RACQUET AND SHUTTLECOCK;So;0;ON;;;;;N;;;;; 1F3F9;BOW AND ARROW;So;0;ON;;;;;N;;;;; 1F3FA;AMPHORA;So;0;ON;;;;;N;;;;; 1F3FB;EMOJI MODIFIER FITZPATRICK TYPE-1-2;Sk;0;ON;;;;;N;;;;; 1F3FC;EMOJI MODIFIER FITZPATRICK TYPE-3;Sk;0;ON;;;;;N;;;;; 1F3FD;EMOJI MODIFIER FITZPATRICK TYPE-4;Sk;0;ON;;;;;N;;;;; 1F3FE;EMOJI MODIFIER FITZPATRICK TYPE-5;Sk;0;ON;;;;;N;;;;; 1F3FF;EMOJI MODIFIER FITZPATRICK TYPE-6;Sk;0;ON;;;;;N;;;;; 1F400;RAT;So;0;ON;;;;;N;;;;; 1F401;MOUSE;So;0;ON;;;;;N;;;;; 1F402;OX;So;0;ON;;;;;N;;;;; 1F403;WATER BUFFALO;So;0;ON;;;;;N;;;;; 1F404;COW;So;0;ON;;;;;N;;;;; 1F405;TIGER;So;0;ON;;;;;N;;;;; 1F406;LEOPARD;So;0;ON;;;;;N;;;;; 1F407;RABBIT;So;0;ON;;;;;N;;;;; 1F408;CAT;So;0;ON;;;;;N;;;;; 1F409;DRAGON;So;0;ON;;;;;N;;;;; 1F40A;CROCODILE;So;0;ON;;;;;N;;;;; 1F40B;WHALE;So;0;ON;;;;;N;;;;; 1F40C;SNAIL;So;0;ON;;;;;N;;;;; 1F40D;SNAKE;So;0;ON;;;;;N;;;;; 1F40E;HORSE;So;0;ON;;;;;N;;;;; 1F40F;RAM;So;0;ON;;;;;N;;;;; 1F410;GOAT;So;0;ON;;;;;N;;;;; 1F411;SHEEP;So;0;ON;;;;;N;;;;; 1F412;MONKEY;So;0;ON;;;;;N;;;;; 1F413;ROOSTER;So;0;ON;;;;;N;;;;; 1F414;CHICKEN;So;0;ON;;;;;N;;;;; 1F415;DOG;So;0;ON;;;;;N;;;;; 1F416;PIG;So;0;ON;;;;;N;;;;; 1F417;BOAR;So;0;ON;;;;;N;;;;; 1F418;ELEPHANT;So;0;ON;;;;;N;;;;; 1F419;OCTOPUS;So;0;ON;;;;;N;;;;; 1F41A;SPIRAL SHELL;So;0;ON;;;;;N;;;;; 1F41B;BUG;So;0;ON;;;;;N;;;;; 1F41C;ANT;So;0;ON;;;;;N;;;;; 1F41D;HONEYBEE;So;0;ON;;;;;N;;;;; 1F41E;LADY BEETLE;So;0;ON;;;;;N;;;;; 1F41F;FISH;So;0;ON;;;;;N;;;;; 1F420;TROPICAL FISH;So;0;ON;;;;;N;;;;; 1F421;BLOWFISH;So;0;ON;;;;;N;;;;; 1F422;TURTLE;So;0;ON;;;;;N;;;;; 1F423;HATCHING CHICK;So;0;ON;;;;;N;;;;; 1F424;BABY CHICK;So;0;ON;;;;;N;;;;; 1F425;FRONT-FACING BABY CHICK;So;0;ON;;;;;N;;;;; 1F426;BIRD;So;0;ON;;;;;N;;;;; 1F427;PENGUIN;So;0;ON;;;;;N;;;;; 1F428;KOALA;So;0;ON;;;;;N;;;;; 1F429;POODLE;So;0;ON;;;;;N;;;;; 1F42A;DROMEDARY CAMEL;So;0;ON;;;;;N;;;;; 1F42B;BACTRIAN CAMEL;So;0;ON;;;;;N;;;;; 1F42C;DOLPHIN;So;0;ON;;;;;N;;;;; 1F42D;MOUSE FACE;So;0;ON;;;;;N;;;;; 1F42E;COW FACE;So;0;ON;;;;;N;;;;; 1F42F;TIGER FACE;So;0;ON;;;;;N;;;;; 1F430;RABBIT FACE;So;0;ON;;;;;N;;;;; 1F431;CAT FACE;So;0;ON;;;;;N;;;;; 1F432;DRAGON FACE;So;0;ON;;;;;N;;;;; 1F433;SPOUTING WHALE;So;0;ON;;;;;N;;;;; 1F434;HORSE FACE;So;0;ON;;;;;N;;;;; 1F435;MONKEY FACE;So;0;ON;;;;;N;;;;; 1F436;DOG FACE;So;0;ON;;;;;N;;;;; 1F437;PIG FACE;So;0;ON;;;;;N;;;;; 1F438;FROG FACE;So;0;ON;;;;;N;;;;; 1F439;HAMSTER FACE;So;0;ON;;;;;N;;;;; 1F43A;WOLF FACE;So;0;ON;;;;;N;;;;; 1F43B;BEAR FACE;So;0;ON;;;;;N;;;;; 1F43C;PANDA FACE;So;0;ON;;;;;N;;;;; 1F43D;PIG NOSE;So;0;ON;;;;;N;;;;; 1F43E;PAW PRINTS;So;0;ON;;;;;N;;;;; 1F43F;CHIPMUNK;So;0;ON;;;;;N;;;;; 1F440;EYES;So;0;ON;;;;;N;;;;; 1F441;EYE;So;0;ON;;;;;N;;;;; 1F442;EAR;So;0;ON;;;;;N;;;;; 1F443;NOSE;So;0;ON;;;;;N;;;;; 1F444;MOUTH;So;0;ON;;;;;N;;;;; 1F445;TONGUE;So;0;ON;;;;;N;;;;; 1F446;WHITE UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F447;WHITE DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F448;WHITE LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F449;WHITE RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F44A;FISTED HAND SIGN;So;0;ON;;;;;N;;;;; 1F44B;WAVING HAND SIGN;So;0;ON;;;;;N;;;;; 1F44C;OK HAND SIGN;So;0;ON;;;;;N;;;;; 1F44D;THUMBS UP SIGN;So;0;ON;;;;;N;;;;; 1F44E;THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;; 1F44F;CLAPPING HANDS SIGN;So;0;ON;;;;;N;;;;; 1F450;OPEN HANDS SIGN;So;0;ON;;;;;N;;;;; 1F451;CROWN;So;0;ON;;;;;N;;;;; 1F452;WOMANS HAT;So;0;ON;;;;;N;;;;; 1F453;EYEGLASSES;So;0;ON;;;;;N;;;;; 1F454;NECKTIE;So;0;ON;;;;;N;;;;; 1F455;T-SHIRT;So;0;ON;;;;;N;;;;; 1F456;JEANS;So;0;ON;;;;;N;;;;; 1F457;DRESS;So;0;ON;;;;;N;;;;; 1F458;KIMONO;So;0;ON;;;;;N;;;;; 1F459;BIKINI;So;0;ON;;;;;N;;;;; 1F45A;WOMANS CLOTHES;So;0;ON;;;;;N;;;;; 1F45B;PURSE;So;0;ON;;;;;N;;;;; 1F45C;HANDBAG;So;0;ON;;;;;N;;;;; 1F45D;POUCH;So;0;ON;;;;;N;;;;; 1F45E;MANS SHOE;So;0;ON;;;;;N;;;;; 1F45F;ATHLETIC SHOE;So;0;ON;;;;;N;;;;; 1F460;HIGH-HEELED SHOE;So;0;ON;;;;;N;;;;; 1F461;WOMANS SANDAL;So;0;ON;;;;;N;;;;; 1F462;WOMANS BOOTS;So;0;ON;;;;;N;;;;; 1F463;FOOTPRINTS;So;0;ON;;;;;N;;;;; 1F464;BUST IN SILHOUETTE;So;0;ON;;;;;N;;;;; 1F465;BUSTS IN SILHOUETTE;So;0;ON;;;;;N;;;;; 1F466;BOY;So;0;ON;;;;;N;;;;; 1F467;GIRL;So;0;ON;;;;;N;;;;; 1F468;MAN;So;0;ON;;;;;N;;;;; 1F469;WOMAN;So;0;ON;;;;;N;;;;; 1F46A;FAMILY;So;0;ON;;;;;N;;;;; 1F46B;MAN AND WOMAN HOLDING HANDS;So;0;ON;;;;;N;;;;; 1F46C;TWO MEN HOLDING HANDS;So;0;ON;;;;;N;;;;; 1F46D;TWO WOMEN HOLDING HANDS;So;0;ON;;;;;N;;;;; 1F46E;POLICE OFFICER;So;0;ON;;;;;N;;;;; 1F46F;WOMAN WITH BUNNY EARS;So;0;ON;;;;;N;;;;; 1F470;BRIDE WITH VEIL;So;0;ON;;;;;N;;;;; 1F471;PERSON WITH BLOND HAIR;So;0;ON;;;;;N;;;;; 1F472;MAN WITH GUA PI MAO;So;0;ON;;;;;N;;;;; 1F473;MAN WITH TURBAN;So;0;ON;;;;;N;;;;; 1F474;OLDER MAN;So;0;ON;;;;;N;;;;; 1F475;OLDER WOMAN;So;0;ON;;;;;N;;;;; 1F476;BABY;So;0;ON;;;;;N;;;;; 1F477;CONSTRUCTION WORKER;So;0;ON;;;;;N;;;;; 1F478;PRINCESS;So;0;ON;;;;;N;;;;; 1F479;JAPANESE OGRE;So;0;ON;;;;;N;;;;; 1F47A;JAPANESE GOBLIN;So;0;ON;;;;;N;;;;; 1F47B;GHOST;So;0;ON;;;;;N;;;;; 1F47C;BABY ANGEL;So;0;ON;;;;;N;;;;; 1F47D;EXTRATERRESTRIAL ALIEN;So;0;ON;;;;;N;;;;; 1F47E;ALIEN MONSTER;So;0;ON;;;;;N;;;;; 1F47F;IMP;So;0;ON;;;;;N;;;;; 1F480;SKULL;So;0;ON;;;;;N;;;;; 1F481;INFORMATION DESK PERSON;So;0;ON;;;;;N;;;;; 1F482;GUARDSMAN;So;0;ON;;;;;N;;;;; 1F483;DANCER;So;0;ON;;;;;N;;;;; 1F484;LIPSTICK;So;0;ON;;;;;N;;;;; 1F485;NAIL POLISH;So;0;ON;;;;;N;;;;; 1F486;FACE MASSAGE;So;0;ON;;;;;N;;;;; 1F487;HAIRCUT;So;0;ON;;;;;N;;;;; 1F488;BARBER POLE;So;0;ON;;;;;N;;;;; 1F489;SYRINGE;So;0;ON;;;;;N;;;;; 1F48A;PILL;So;0;ON;;;;;N;;;;; 1F48B;KISS MARK;So;0;ON;;;;;N;;;;; 1F48C;LOVE LETTER;So;0;ON;;;;;N;;;;; 1F48D;RING;So;0;ON;;;;;N;;;;; 1F48E;GEM STONE;So;0;ON;;;;;N;;;;; 1F48F;KISS;So;0;ON;;;;;N;;;;; 1F490;BOUQUET;So;0;ON;;;;;N;;;;; 1F491;COUPLE WITH HEART;So;0;ON;;;;;N;;;;; 1F492;WEDDING;So;0;ON;;;;;N;;;;; 1F493;BEATING HEART;So;0;ON;;;;;N;;;;; 1F494;BROKEN HEART;So;0;ON;;;;;N;;;;; 1F495;TWO HEARTS;So;0;ON;;;;;N;;;;; 1F496;SPARKLING HEART;So;0;ON;;;;;N;;;;; 1F497;GROWING HEART;So;0;ON;;;;;N;;;;; 1F498;HEART WITH ARROW;So;0;ON;;;;;N;;;;; 1F499;BLUE HEART;So;0;ON;;;;;N;;;;; 1F49A;GREEN HEART;So;0;ON;;;;;N;;;;; 1F49B;YELLOW HEART;So;0;ON;;;;;N;;;;; 1F49C;PURPLE HEART;So;0;ON;;;;;N;;;;; 1F49D;HEART WITH RIBBON;So;0;ON;;;;;N;;;;; 1F49E;REVOLVING HEARTS;So;0;ON;;;;;N;;;;; 1F49F;HEART DECORATION;So;0;ON;;;;;N;;;;; 1F4A0;DIAMOND SHAPE WITH A DOT INSIDE;So;0;ON;;;;;N;;;;; 1F4A1;ELECTRIC LIGHT BULB;So;0;ON;;;;;N;;;;; 1F4A2;ANGER SYMBOL;So;0;ON;;;;;N;;;;; 1F4A3;BOMB;So;0;ON;;;;;N;;;;; 1F4A4;SLEEPING SYMBOL;So;0;ON;;;;;N;;;;; 1F4A5;COLLISION SYMBOL;So;0;ON;;;;;N;;;;; 1F4A6;SPLASHING SWEAT SYMBOL;So;0;ON;;;;;N;;;;; 1F4A7;DROPLET;So;0;ON;;;;;N;;;;; 1F4A8;DASH SYMBOL;So;0;ON;;;;;N;;;;; 1F4A9;PILE OF POO;So;0;ON;;;;;N;;;;; 1F4AA;FLEXED BICEPS;So;0;ON;;;;;N;;;;; 1F4AB;DIZZY SYMBOL;So;0;ON;;;;;N;;;;; 1F4AC;SPEECH BALLOON;So;0;ON;;;;;N;;;;; 1F4AD;THOUGHT BALLOON;So;0;ON;;;;;N;;;;; 1F4AE;WHITE FLOWER;So;0;ON;;;;;N;;;;; 1F4AF;HUNDRED POINTS SYMBOL;So;0;ON;;;;;N;;;;; 1F4B0;MONEY BAG;So;0;ON;;;;;N;;;;; 1F4B1;CURRENCY EXCHANGE;So;0;ON;;;;;N;;;;; 1F4B2;HEAVY DOLLAR SIGN;So;0;ON;;;;;N;;;;; 1F4B3;CREDIT CARD;So;0;ON;;;;;N;;;;; 1F4B4;BANKNOTE WITH YEN SIGN;So;0;ON;;;;;N;;;;; 1F4B5;BANKNOTE WITH DOLLAR SIGN;So;0;ON;;;;;N;;;;; 1F4B6;BANKNOTE WITH EURO SIGN;So;0;ON;;;;;N;;;;; 1F4B7;BANKNOTE WITH POUND SIGN;So;0;ON;;;;;N;;;;; 1F4B8;MONEY WITH WINGS;So;0;ON;;;;;N;;;;; 1F4B9;CHART WITH UPWARDS TREND AND YEN SIGN;So;0;ON;;;;;N;;;;; 1F4BA;SEAT;So;0;ON;;;;;N;;;;; 1F4BB;PERSONAL COMPUTER;So;0;ON;;;;;N;;;;; 1F4BC;BRIEFCASE;So;0;ON;;;;;N;;;;; 1F4BD;MINIDISC;So;0;ON;;;;;N;;;;; 1F4BE;FLOPPY DISK;So;0;ON;;;;;N;;;;; 1F4BF;OPTICAL DISC;So;0;ON;;;;;N;;;;; 1F4C0;DVD;So;0;ON;;;;;N;;;;; 1F4C1;FILE FOLDER;So;0;ON;;;;;N;;;;; 1F4C2;OPEN FILE FOLDER;So;0;ON;;;;;N;;;;; 1F4C3;PAGE WITH CURL;So;0;ON;;;;;N;;;;; 1F4C4;PAGE FACING UP;So;0;ON;;;;;N;;;;; 1F4C5;CALENDAR;So;0;ON;;;;;N;;;;; 1F4C6;TEAR-OFF CALENDAR;So;0;ON;;;;;N;;;;; 1F4C7;CARD INDEX;So;0;ON;;;;;N;;;;; 1F4C8;CHART WITH UPWARDS TREND;So;0;ON;;;;;N;;;;; 1F4C9;CHART WITH DOWNWARDS TREND;So;0;ON;;;;;N;;;;; 1F4CA;BAR CHART;So;0;ON;;;;;N;;;;; 1F4CB;CLIPBOARD;So;0;ON;;;;;N;;;;; 1F4CC;PUSHPIN;So;0;ON;;;;;N;;;;; 1F4CD;ROUND PUSHPIN;So;0;ON;;;;;N;;;;; 1F4CE;PAPERCLIP;So;0;ON;;;;;N;;;;; 1F4CF;STRAIGHT RULER;So;0;ON;;;;;N;;;;; 1F4D0;TRIANGULAR RULER;So;0;ON;;;;;N;;;;; 1F4D1;BOOKMARK TABS;So;0;ON;;;;;N;;;;; 1F4D2;LEDGER;So;0;ON;;;;;N;;;;; 1F4D3;NOTEBOOK;So;0;ON;;;;;N;;;;; 1F4D4;NOTEBOOK WITH DECORATIVE COVER;So;0;ON;;;;;N;;;;; 1F4D5;CLOSED BOOK;So;0;ON;;;;;N;;;;; 1F4D6;OPEN BOOK;So;0;ON;;;;;N;;;;; 1F4D7;GREEN BOOK;So;0;ON;;;;;N;;;;; 1F4D8;BLUE BOOK;So;0;ON;;;;;N;;;;; 1F4D9;ORANGE BOOK;So;0;ON;;;;;N;;;;; 1F4DA;BOOKS;So;0;ON;;;;;N;;;;; 1F4DB;NAME BADGE;So;0;ON;;;;;N;;;;; 1F4DC;SCROLL;So;0;ON;;;;;N;;;;; 1F4DD;MEMO;So;0;ON;;;;;N;;;;; 1F4DE;TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; 1F4DF;PAGER;So;0;ON;;;;;N;;;;; 1F4E0;FAX MACHINE;So;0;ON;;;;;N;;;;; 1F4E1;SATELLITE ANTENNA;So;0;ON;;;;;N;;;;; 1F4E2;PUBLIC ADDRESS LOUDSPEAKER;So;0;ON;;;;;N;;;;; 1F4E3;CHEERING MEGAPHONE;So;0;ON;;;;;N;;;;; 1F4E4;OUTBOX TRAY;So;0;ON;;;;;N;;;;; 1F4E5;INBOX TRAY;So;0;ON;;;;;N;;;;; 1F4E6;PACKAGE;So;0;ON;;;;;N;;;;; 1F4E7;E-MAIL SYMBOL;So;0;ON;;;;;N;;;;; 1F4E8;INCOMING ENVELOPE;So;0;ON;;;;;N;;;;; 1F4E9;ENVELOPE WITH DOWNWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F4EA;CLOSED MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;; 1F4EB;CLOSED MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;; 1F4EC;OPEN MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;; 1F4ED;OPEN MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;; 1F4EE;POSTBOX;So;0;ON;;;;;N;;;;; 1F4EF;POSTAL HORN;So;0;ON;;;;;N;;;;; 1F4F0;NEWSPAPER;So;0;ON;;;;;N;;;;; 1F4F1;MOBILE PHONE;So;0;ON;;;;;N;;;;; 1F4F2;MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT;So;0;ON;;;;;N;;;;; 1F4F3;VIBRATION MODE;So;0;ON;;;;;N;;;;; 1F4F4;MOBILE PHONE OFF;So;0;ON;;;;;N;;;;; 1F4F5;NO MOBILE PHONES;So;0;ON;;;;;N;;;;; 1F4F6;ANTENNA WITH BARS;So;0;ON;;;;;N;;;;; 1F4F7;CAMERA;So;0;ON;;;;;N;;;;; 1F4F8;CAMERA WITH FLASH;So;0;ON;;;;;N;;;;; 1F4F9;VIDEO CAMERA;So;0;ON;;;;;N;;;;; 1F4FA;TELEVISION;So;0;ON;;;;;N;;;;; 1F4FB;RADIO;So;0;ON;;;;;N;;;;; 1F4FC;VIDEOCASSETTE;So;0;ON;;;;;N;;;;; 1F4FD;FILM PROJECTOR;So;0;ON;;;;;N;;;;; 1F4FE;PORTABLE STEREO;So;0;ON;;;;;N;;;;; 1F4FF;PRAYER BEADS;So;0;ON;;;;;N;;;;; 1F500;TWISTED RIGHTWARDS ARROWS;So;0;ON;;;;;N;;;;; 1F501;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; 1F502;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY;So;0;ON;;;;;N;;;;; 1F503;CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; 1F504;ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; 1F505;LOW BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;; 1F506;HIGH BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;; 1F507;SPEAKER WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;; 1F508;SPEAKER;So;0;ON;;;;;N;;;;; 1F509;SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;; 1F50A;SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;; 1F50B;BATTERY;So;0;ON;;;;;N;;;;; 1F50C;ELECTRIC PLUG;So;0;ON;;;;;N;;;;; 1F50D;LEFT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;; 1F50E;RIGHT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;; 1F50F;LOCK WITH INK PEN;So;0;ON;;;;;N;;;;; 1F510;CLOSED LOCK WITH KEY;So;0;ON;;;;;N;;;;; 1F511;KEY;So;0;ON;;;;;N;;;;; 1F512;LOCK;So;0;ON;;;;;N;;;;; 1F513;OPEN LOCK;So;0;ON;;;;;N;;;;; 1F514;BELL;So;0;ON;;;;;N;;;;; 1F515;BELL WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;; 1F516;BOOKMARK;So;0;ON;;;;;N;;;;; 1F517;LINK SYMBOL;So;0;ON;;;;;N;;;;; 1F518;RADIO BUTTON;So;0;ON;;;;;N;;;;; 1F519;BACK WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F51A;END WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F51B;ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F51C;SOON WITH RIGHTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F51D;TOP WITH UPWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; 1F51E;NO ONE UNDER EIGHTEEN SYMBOL;So;0;ON;;;;;N;;;;; 1F51F;KEYCAP TEN;So;0;ON;;;;;N;;;;; 1F520;INPUT SYMBOL FOR LATIN CAPITAL LETTERS;So;0;ON;;;;;N;;;;; 1F521;INPUT SYMBOL FOR LATIN SMALL LETTERS;So;0;ON;;;;;N;;;;; 1F522;INPUT SYMBOL FOR NUMBERS;So;0;ON;;;;;N;;;;; 1F523;INPUT SYMBOL FOR SYMBOLS;So;0;ON;;;;;N;;;;; 1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;ON;;;;;N;;;;; 1F525;FIRE;So;0;ON;;;;;N;;;;; 1F526;ELECTRIC TORCH;So;0;ON;;;;;N;;;;; 1F527;WRENCH;So;0;ON;;;;;N;;;;; 1F528;HAMMER;So;0;ON;;;;;N;;;;; 1F529;NUT AND BOLT;So;0;ON;;;;;N;;;;; 1F52A;HOCHO;So;0;ON;;;;;N;;;;; 1F52B;PISTOL;So;0;ON;;;;;N;;;;; 1F52C;MICROSCOPE;So;0;ON;;;;;N;;;;; 1F52D;TELESCOPE;So;0;ON;;;;;N;;;;; 1F52E;CRYSTAL BALL;So;0;ON;;;;;N;;;;; 1F52F;SIX POINTED STAR WITH MIDDLE DOT;So;0;ON;;;;;N;;;;; 1F530;JAPANESE SYMBOL FOR BEGINNER;So;0;ON;;;;;N;;;;; 1F531;TRIDENT EMBLEM;So;0;ON;;;;;N;;;;; 1F532;BLACK SQUARE BUTTON;So;0;ON;;;;;N;;;;; 1F533;WHITE SQUARE BUTTON;So;0;ON;;;;;N;;;;; 1F534;LARGE RED CIRCLE;So;0;ON;;;;;N;;;;; 1F535;LARGE BLUE CIRCLE;So;0;ON;;;;;N;;;;; 1F536;LARGE ORANGE DIAMOND;So;0;ON;;;;;N;;;;; 1F537;LARGE BLUE DIAMOND;So;0;ON;;;;;N;;;;; 1F538;SMALL ORANGE DIAMOND;So;0;ON;;;;;N;;;;; 1F539;SMALL BLUE DIAMOND;So;0;ON;;;;;N;;;;; 1F53A;UP-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53B;DOWN-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53C;UP-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53D;DOWN-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53E;LOWER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F53F;UPPER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F540;CIRCLED CROSS POMMEE;So;0;ON;;;;;N;;;;; 1F541;CROSS POMMEE WITH HALF-CIRCLE BELOW;So;0;ON;;;;;N;;;;; 1F542;CROSS POMMEE;So;0;ON;;;;;N;;;;; 1F543;NOTCHED LEFT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;; 1F544;NOTCHED RIGHT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;; 1F545;SYMBOL FOR MARKS CHAPTER;So;0;ON;;;;;N;;;;; 1F546;WHITE LATIN CROSS;So;0;ON;;;;;N;;;;; 1F547;HEAVY LATIN CROSS;So;0;ON;;;;;N;;;;; 1F548;CELTIC CROSS;So;0;ON;;;;;N;;;;; 1F549;OM SYMBOL;So;0;ON;;;;;N;;;;; 1F54A;DOVE OF PEACE;So;0;ON;;;;;N;;;;; 1F54B;KAABA;So;0;ON;;;;;N;;;;; 1F54C;MOSQUE;So;0;ON;;;;;N;;;;; 1F54D;SYNAGOGUE;So;0;ON;;;;;N;;;;; 1F54E;MENORAH WITH NINE BRANCHES;So;0;ON;;;;;N;;;;; 1F54F;BOWL OF HYGIEIA;So;0;ON;;;;;N;;;;; 1F550;CLOCK FACE ONE OCLOCK;So;0;ON;;;;;N;;;;; 1F551;CLOCK FACE TWO OCLOCK;So;0;ON;;;;;N;;;;; 1F552;CLOCK FACE THREE OCLOCK;So;0;ON;;;;;N;;;;; 1F553;CLOCK FACE FOUR OCLOCK;So;0;ON;;;;;N;;;;; 1F554;CLOCK FACE FIVE OCLOCK;So;0;ON;;;;;N;;;;; 1F555;CLOCK FACE SIX OCLOCK;So;0;ON;;;;;N;;;;; 1F556;CLOCK FACE SEVEN OCLOCK;So;0;ON;;;;;N;;;;; 1F557;CLOCK FACE EIGHT OCLOCK;So;0;ON;;;;;N;;;;; 1F558;CLOCK FACE NINE OCLOCK;So;0;ON;;;;;N;;;;; 1F559;CLOCK FACE TEN OCLOCK;So;0;ON;;;;;N;;;;; 1F55A;CLOCK FACE ELEVEN OCLOCK;So;0;ON;;;;;N;;;;; 1F55B;CLOCK FACE TWELVE OCLOCK;So;0;ON;;;;;N;;;;; 1F55C;CLOCK FACE ONE-THIRTY;So;0;ON;;;;;N;;;;; 1F55D;CLOCK FACE TWO-THIRTY;So;0;ON;;;;;N;;;;; 1F55E;CLOCK FACE THREE-THIRTY;So;0;ON;;;;;N;;;;; 1F55F;CLOCK FACE FOUR-THIRTY;So;0;ON;;;;;N;;;;; 1F560;CLOCK FACE FIVE-THIRTY;So;0;ON;;;;;N;;;;; 1F561;CLOCK FACE SIX-THIRTY;So;0;ON;;;;;N;;;;; 1F562;CLOCK FACE SEVEN-THIRTY;So;0;ON;;;;;N;;;;; 1F563;CLOCK FACE EIGHT-THIRTY;So;0;ON;;;;;N;;;;; 1F564;CLOCK FACE NINE-THIRTY;So;0;ON;;;;;N;;;;; 1F565;CLOCK FACE TEN-THIRTY;So;0;ON;;;;;N;;;;; 1F566;CLOCK FACE ELEVEN-THIRTY;So;0;ON;;;;;N;;;;; 1F567;CLOCK FACE TWELVE-THIRTY;So;0;ON;;;;;N;;;;; 1F568;RIGHT SPEAKER;So;0;ON;;;;;N;;;;; 1F569;RIGHT SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;; 1F56A;RIGHT SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;; 1F56B;BULLHORN;So;0;ON;;;;;N;;;;; 1F56C;BULLHORN WITH SOUND WAVES;So;0;ON;;;;;N;;;;; 1F56D;RINGING BELL;So;0;ON;;;;;N;;;;; 1F56E;BOOK;So;0;ON;;;;;N;;;;; 1F56F;CANDLE;So;0;ON;;;;;N;;;;; 1F570;MANTELPIECE CLOCK;So;0;ON;;;;;N;;;;; 1F571;BLACK SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;; 1F572;NO PIRACY;So;0;ON;;;;;N;;;;; 1F573;HOLE;So;0;ON;;;;;N;;;;; 1F574;MAN IN BUSINESS SUIT LEVITATING;So;0;ON;;;;;N;;;;; 1F575;SLEUTH OR SPY;So;0;ON;;;;;N;;;;; 1F576;DARK SUNGLASSES;So;0;ON;;;;;N;;;;; 1F577;SPIDER;So;0;ON;;;;;N;;;;; 1F578;SPIDER WEB;So;0;ON;;;;;N;;;;; 1F579;JOYSTICK;So;0;ON;;;;;N;;;;; 1F57A;MAN DANCING;So;0;ON;;;;;N;;;;; 1F57B;LEFT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; 1F57C;TELEPHONE RECEIVER WITH PAGE;So;0;ON;;;;;N;;;;; 1F57D;RIGHT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; 1F57E;WHITE TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;; 1F57F;BLACK TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;; 1F580;TELEPHONE ON TOP OF MODEM;So;0;ON;;;;;N;;;;; 1F581;CLAMSHELL MOBILE PHONE;So;0;ON;;;;;N;;;;; 1F582;BACK OF ENVELOPE;So;0;ON;;;;;N;;;;; 1F583;STAMPED ENVELOPE;So;0;ON;;;;;N;;;;; 1F584;ENVELOPE WITH LIGHTNING;So;0;ON;;;;;N;;;;; 1F585;FLYING ENVELOPE;So;0;ON;;;;;N;;;;; 1F586;PEN OVER STAMPED ENVELOPE;So;0;ON;;;;;N;;;;; 1F587;LINKED PAPERCLIPS;So;0;ON;;;;;N;;;;; 1F588;BLACK PUSHPIN;So;0;ON;;;;;N;;;;; 1F589;LOWER LEFT PENCIL;So;0;ON;;;;;N;;;;; 1F58A;LOWER LEFT BALLPOINT PEN;So;0;ON;;;;;N;;;;; 1F58B;LOWER LEFT FOUNTAIN PEN;So;0;ON;;;;;N;;;;; 1F58C;LOWER LEFT PAINTBRUSH;So;0;ON;;;;;N;;;;; 1F58D;LOWER LEFT CRAYON;So;0;ON;;;;;N;;;;; 1F58E;LEFT WRITING HAND;So;0;ON;;;;;N;;;;; 1F58F;TURNED OK HAND SIGN;So;0;ON;;;;;N;;;;; 1F590;RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;; 1F591;REVERSED RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;; 1F592;REVERSED THUMBS UP SIGN;So;0;ON;;;;;N;;;;; 1F593;REVERSED THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;; 1F594;REVERSED VICTORY HAND;So;0;ON;;;;;N;;;;; 1F595;REVERSED HAND WITH MIDDLE FINGER EXTENDED;So;0;ON;;;;;N;;;;; 1F596;RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS;So;0;ON;;;;;N;;;;; 1F597;WHITE DOWN POINTING LEFT HAND INDEX;So;0;ON;;;;;N;;;;; 1F598;SIDEWAYS WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; 1F599;SIDEWAYS WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; 1F59A;SIDEWAYS BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; 1F59B;SIDEWAYS BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; 1F59C;BLACK LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F59D;BLACK RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F59E;SIDEWAYS WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;; 1F59F;SIDEWAYS WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; 1F5A0;SIDEWAYS BLACK UP POINTING INDEX;So;0;ON;;;;;N;;;;; 1F5A1;SIDEWAYS BLACK DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; 1F5A2;BLACK UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F5A3;BLACK DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; 1F5A4;BLACK HEART;So;0;ON;;;;;N;;;;; 1F5A5;DESKTOP COMPUTER;So;0;ON;;;;;N;;;;; 1F5A6;KEYBOARD AND MOUSE;So;0;ON;;;;;N;;;;; 1F5A7;THREE NETWORKED COMPUTERS;So;0;ON;;;;;N;;;;; 1F5A8;PRINTER;So;0;ON;;;;;N;;;;; 1F5A9;POCKET CALCULATOR;So;0;ON;;;;;N;;;;; 1F5AA;BLACK HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; 1F5AB;WHITE HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; 1F5AC;SOFT SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; 1F5AD;TAPE CARTRIDGE;So;0;ON;;;;;N;;;;; 1F5AE;WIRED KEYBOARD;So;0;ON;;;;;N;;;;; 1F5AF;ONE BUTTON MOUSE;So;0;ON;;;;;N;;;;; 1F5B0;TWO BUTTON MOUSE;So;0;ON;;;;;N;;;;; 1F5B1;THREE BUTTON MOUSE;So;0;ON;;;;;N;;;;; 1F5B2;TRACKBALL;So;0;ON;;;;;N;;;;; 1F5B3;OLD PERSONAL COMPUTER;So;0;ON;;;;;N;;;;; 1F5B4;HARD DISK;So;0;ON;;;;;N;;;;; 1F5B5;SCREEN;So;0;ON;;;;;N;;;;; 1F5B6;PRINTER ICON;So;0;ON;;;;;N;;;;; 1F5B7;FAX ICON;So;0;ON;;;;;N;;;;; 1F5B8;OPTICAL DISC ICON;So;0;ON;;;;;N;;;;; 1F5B9;DOCUMENT WITH TEXT;So;0;ON;;;;;N;;;;; 1F5BA;DOCUMENT WITH TEXT AND PICTURE;So;0;ON;;;;;N;;;;; 1F5BB;DOCUMENT WITH PICTURE;So;0;ON;;;;;N;;;;; 1F5BC;FRAME WITH PICTURE;So;0;ON;;;;;N;;;;; 1F5BD;FRAME WITH TILES;So;0;ON;;;;;N;;;;; 1F5BE;FRAME WITH AN X;So;0;ON;;;;;N;;;;; 1F5BF;BLACK FOLDER;So;0;ON;;;;;N;;;;; 1F5C0;FOLDER;So;0;ON;;;;;N;;;;; 1F5C1;OPEN FOLDER;So;0;ON;;;;;N;;;;; 1F5C2;CARD INDEX DIVIDERS;So;0;ON;;;;;N;;;;; 1F5C3;CARD FILE BOX;So;0;ON;;;;;N;;;;; 1F5C4;FILE CABINET;So;0;ON;;;;;N;;;;; 1F5C5;EMPTY NOTE;So;0;ON;;;;;N;;;;; 1F5C6;EMPTY NOTE PAGE;So;0;ON;;;;;N;;;;; 1F5C7;EMPTY NOTE PAD;So;0;ON;;;;;N;;;;; 1F5C8;NOTE;So;0;ON;;;;;N;;;;; 1F5C9;NOTE PAGE;So;0;ON;;;;;N;;;;; 1F5CA;NOTE PAD;So;0;ON;;;;;N;;;;; 1F5CB;EMPTY DOCUMENT;So;0;ON;;;;;N;;;;; 1F5CC;EMPTY PAGE;So;0;ON;;;;;N;;;;; 1F5CD;EMPTY PAGES;So;0;ON;;;;;N;;;;; 1F5CE;DOCUMENT;So;0;ON;;;;;N;;;;; 1F5CF;PAGE;So;0;ON;;;;;N;;;;; 1F5D0;PAGES;So;0;ON;;;;;N;;;;; 1F5D1;WASTEBASKET;So;0;ON;;;;;N;;;;; 1F5D2;SPIRAL NOTE PAD;So;0;ON;;;;;N;;;;; 1F5D3;SPIRAL CALENDAR PAD;So;0;ON;;;;;N;;;;; 1F5D4;DESKTOP WINDOW;So;0;ON;;;;;N;;;;; 1F5D5;MINIMIZE;So;0;ON;;;;;N;;;;; 1F5D6;MAXIMIZE;So;0;ON;;;;;N;;;;; 1F5D7;OVERLAP;So;0;ON;;;;;N;;;;; 1F5D8;CLOCKWISE RIGHT AND LEFT SEMICIRCLE ARROWS;So;0;ON;;;;;N;;;;; 1F5D9;CANCELLATION X;So;0;ON;;;;;N;;;;; 1F5DA;INCREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;; 1F5DB;DECREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;; 1F5DC;COMPRESSION;So;0;ON;;;;;N;;;;; 1F5DD;OLD KEY;So;0;ON;;;;;N;;;;; 1F5DE;ROLLED-UP NEWSPAPER;So;0;ON;;;;;N;;;;; 1F5DF;PAGE WITH CIRCLED TEXT;So;0;ON;;;;;N;;;;; 1F5E0;STOCK CHART;So;0;ON;;;;;N;;;;; 1F5E1;DAGGER KNIFE;So;0;ON;;;;;N;;;;; 1F5E2;LIPS;So;0;ON;;;;;N;;;;; 1F5E3;SPEAKING HEAD IN SILHOUETTE;So;0;ON;;;;;N;;;;; 1F5E4;THREE RAYS ABOVE;So;0;ON;;;;;N;;;;; 1F5E5;THREE RAYS BELOW;So;0;ON;;;;;N;;;;; 1F5E6;THREE RAYS LEFT;So;0;ON;;;;;N;;;;; 1F5E7;THREE RAYS RIGHT;So;0;ON;;;;;N;;;;; 1F5E8;LEFT SPEECH BUBBLE;So;0;ON;;;;;N;;;;; 1F5E9;RIGHT SPEECH BUBBLE;So;0;ON;;;;;N;;;;; 1F5EA;TWO SPEECH BUBBLES;So;0;ON;;;;;N;;;;; 1F5EB;THREE SPEECH BUBBLES;So;0;ON;;;;;N;;;;; 1F5EC;LEFT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;; 1F5ED;RIGHT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;; 1F5EE;LEFT ANGER BUBBLE;So;0;ON;;;;;N;;;;; 1F5EF;RIGHT ANGER BUBBLE;So;0;ON;;;;;N;;;;; 1F5F0;MOOD BUBBLE;So;0;ON;;;;;N;;;;; 1F5F1;LIGHTNING MOOD BUBBLE;So;0;ON;;;;;N;;;;; 1F5F2;LIGHTNING MOOD;So;0;ON;;;;;N;;;;; 1F5F3;BALLOT BOX WITH BALLOT;So;0;ON;;;;;N;;;;; 1F5F4;BALLOT SCRIPT X;So;0;ON;;;;;N;;;;; 1F5F5;BALLOT BOX WITH SCRIPT X;So;0;ON;;;;;N;;;;; 1F5F6;BALLOT BOLD SCRIPT X;So;0;ON;;;;;N;;;;; 1F5F7;BALLOT BOX WITH BOLD SCRIPT X;So;0;ON;;;;;N;;;;; 1F5F8;LIGHT CHECK MARK;So;0;ON;;;;;N;;;;; 1F5F9;BALLOT BOX WITH BOLD CHECK;So;0;ON;;;;;N;;;;; 1F5FA;WORLD MAP;So;0;ON;;;;;N;;;;; 1F5FB;MOUNT FUJI;So;0;ON;;;;;N;;;;; 1F5FC;TOKYO TOWER;So;0;ON;;;;;N;;;;; 1F5FD;STATUE OF LIBERTY;So;0;ON;;;;;N;;;;; 1F5FE;SILHOUETTE OF JAPAN;So;0;ON;;;;;N;;;;; 1F5FF;MOYAI;So;0;ON;;;;;N;;;;; 1F600;GRINNING FACE;So;0;ON;;;;;N;;;;; 1F601;GRINNING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F602;FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;; 1F603;SMILING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; 1F604;SMILING FACE WITH OPEN MOUTH AND SMILING EYES;So;0;ON;;;;;N;;;;; 1F605;SMILING FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;; 1F606;SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;; 1F607;SMILING FACE WITH HALO;So;0;ON;;;;;N;;;;; 1F608;SMILING FACE WITH HORNS;So;0;ON;;;;;N;;;;; 1F609;WINKING FACE;So;0;ON;;;;;N;;;;; 1F60A;SMILING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F60B;FACE SAVOURING DELICIOUS FOOD;So;0;ON;;;;;N;;;;; 1F60C;RELIEVED FACE;So;0;ON;;;;;N;;;;; 1F60D;SMILING FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;; 1F60E;SMILING FACE WITH SUNGLASSES;So;0;ON;;;;;N;;;;; 1F60F;SMIRKING FACE;So;0;ON;;;;;N;;;;; 1F610;NEUTRAL FACE;So;0;ON;;;;;N;;;;; 1F611;EXPRESSIONLESS FACE;So;0;ON;;;;;N;;;;; 1F612;UNAMUSED FACE;So;0;ON;;;;;N;;;;; 1F613;FACE WITH COLD SWEAT;So;0;ON;;;;;N;;;;; 1F614;PENSIVE FACE;So;0;ON;;;;;N;;;;; 1F615;CONFUSED FACE;So;0;ON;;;;;N;;;;; 1F616;CONFOUNDED FACE;So;0;ON;;;;;N;;;;; 1F617;KISSING FACE;So;0;ON;;;;;N;;;;; 1F618;FACE THROWING A KISS;So;0;ON;;;;;N;;;;; 1F619;KISSING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F61A;KISSING FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;; 1F61B;FACE WITH STUCK-OUT TONGUE;So;0;ON;;;;;N;;;;; 1F61C;FACE WITH STUCK-OUT TONGUE AND WINKING EYE;So;0;ON;;;;;N;;;;; 1F61D;FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;; 1F61E;DISAPPOINTED FACE;So;0;ON;;;;;N;;;;; 1F61F;WORRIED FACE;So;0;ON;;;;;N;;;;; 1F620;ANGRY FACE;So;0;ON;;;;;N;;;;; 1F621;POUTING FACE;So;0;ON;;;;;N;;;;; 1F622;CRYING FACE;So;0;ON;;;;;N;;;;; 1F623;PERSEVERING FACE;So;0;ON;;;;;N;;;;; 1F624;FACE WITH LOOK OF TRIUMPH;So;0;ON;;;;;N;;;;; 1F625;DISAPPOINTED BUT RELIEVED FACE;So;0;ON;;;;;N;;;;; 1F626;FROWNING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; 1F627;ANGUISHED FACE;So;0;ON;;;;;N;;;;; 1F628;FEARFUL FACE;So;0;ON;;;;;N;;;;; 1F629;WEARY FACE;So;0;ON;;;;;N;;;;; 1F62A;SLEEPY FACE;So;0;ON;;;;;N;;;;; 1F62B;TIRED FACE;So;0;ON;;;;;N;;;;; 1F62C;GRIMACING FACE;So;0;ON;;;;;N;;;;; 1F62D;LOUDLY CRYING FACE;So;0;ON;;;;;N;;;;; 1F62E;FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; 1F62F;HUSHED FACE;So;0;ON;;;;;N;;;;; 1F630;FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;; 1F631;FACE SCREAMING IN FEAR;So;0;ON;;;;;N;;;;; 1F632;ASTONISHED FACE;So;0;ON;;;;;N;;;;; 1F633;FLUSHED FACE;So;0;ON;;;;;N;;;;; 1F634;SLEEPING FACE;So;0;ON;;;;;N;;;;; 1F635;DIZZY FACE;So;0;ON;;;;;N;;;;; 1F636;FACE WITHOUT MOUTH;So;0;ON;;;;;N;;;;; 1F637;FACE WITH MEDICAL MASK;So;0;ON;;;;;N;;;;; 1F638;GRINNING CAT FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F639;CAT FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;; 1F63A;SMILING CAT FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; 1F63B;SMILING CAT FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;; 1F63C;CAT FACE WITH WRY SMILE;So;0;ON;;;;;N;;;;; 1F63D;KISSING CAT FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;; 1F63E;POUTING CAT FACE;So;0;ON;;;;;N;;;;; 1F63F;CRYING CAT FACE;So;0;ON;;;;;N;;;;; 1F640;WEARY CAT FACE;So;0;ON;;;;;N;;;;; 1F641;SLIGHTLY FROWNING FACE;So;0;ON;;;;;N;;;;; 1F642;SLIGHTLY SMILING FACE;So;0;ON;;;;;N;;;;; 1F643;UPSIDE-DOWN FACE;So;0;ON;;;;;N;;;;; 1F644;FACE WITH ROLLING EYES;So;0;ON;;;;;N;;;;; 1F645;FACE WITH NO GOOD GESTURE;So;0;ON;;;;;N;;;;; 1F646;FACE WITH OK GESTURE;So;0;ON;;;;;N;;;;; 1F647;PERSON BOWING DEEPLY;So;0;ON;;;;;N;;;;; 1F648;SEE-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; 1F649;HEAR-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; 1F64A;SPEAK-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; 1F64B;HAPPY PERSON RAISING ONE HAND;So;0;ON;;;;;N;;;;; 1F64C;PERSON RAISING BOTH HANDS IN CELEBRATION;So;0;ON;;;;;N;;;;; 1F64D;PERSON FROWNING;So;0;ON;;;;;N;;;;; 1F64E;PERSON WITH POUTING FACE;So;0;ON;;;;;N;;;;; 1F64F;PERSON WITH FOLDED HANDS;So;0;ON;;;;;N;;;;; 1F650;NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F651;SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F652;NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F653;SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F654;TURNED NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F655;TURNED SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F656;TURNED NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F657;TURNED SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; 1F658;NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F659;SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65A;NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65B;SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65C;HEAVY NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65D;HEAVY SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65E;HEAVY NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F65F;HEAVY SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; 1F660;NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; 1F661;SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; 1F662;NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; 1F663;SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; 1F664;HEAVY NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; 1F665;HEAVY SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; 1F666;HEAVY NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; 1F667;HEAVY SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; 1F668;HOLLOW QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;; 1F669;HOLLOW QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;; 1F66A;SOLID QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;; 1F66B;SOLID QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;; 1F66C;LEFTWARDS ROCKET;So;0;ON;;;;;N;;;;; 1F66D;UPWARDS ROCKET;So;0;ON;;;;;N;;;;; 1F66E;RIGHTWARDS ROCKET;So;0;ON;;;;;N;;;;; 1F66F;DOWNWARDS ROCKET;So;0;ON;;;;;N;;;;; 1F670;SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;; 1F671;HEAVY SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;; 1F672;LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;; 1F673;HEAVY LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;; 1F674;HEAVY AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;; 1F675;SWASH AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;; 1F676;SANS-SERIF HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 1F677;SANS-SERIF HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 1F678;SANS-SERIF HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; 1F679;HEAVY INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; 1F67A;SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; 1F67B;HEAVY SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; 1F67C;VERY HEAVY SOLIDUS;So;0;ON;;;;;N;;;;; 1F67D;VERY HEAVY REVERSE SOLIDUS;So;0;ON;;;;;N;;;;; 1F67E;CHECKER BOARD;So;0;ON;;;;;N;;;;; 1F67F;REVERSE CHECKER BOARD;So;0;ON;;;;;N;;;;; 1F680;ROCKET;So;0;ON;;;;;N;;;;; 1F681;HELICOPTER;So;0;ON;;;;;N;;;;; 1F682;STEAM LOCOMOTIVE;So;0;ON;;;;;N;;;;; 1F683;RAILWAY CAR;So;0;ON;;;;;N;;;;; 1F684;HIGH-SPEED TRAIN;So;0;ON;;;;;N;;;;; 1F685;HIGH-SPEED TRAIN WITH BULLET NOSE;So;0;ON;;;;;N;;;;; 1F686;TRAIN;So;0;ON;;;;;N;;;;; 1F687;METRO;So;0;ON;;;;;N;;;;; 1F688;LIGHT RAIL;So;0;ON;;;;;N;;;;; 1F689;STATION;So;0;ON;;;;;N;;;;; 1F68A;TRAM;So;0;ON;;;;;N;;;;; 1F68B;TRAM CAR;So;0;ON;;;;;N;;;;; 1F68C;BUS;So;0;ON;;;;;N;;;;; 1F68D;ONCOMING BUS;So;0;ON;;;;;N;;;;; 1F68E;TROLLEYBUS;So;0;ON;;;;;N;;;;; 1F68F;BUS STOP;So;0;ON;;;;;N;;;;; 1F690;MINIBUS;So;0;ON;;;;;N;;;;; 1F691;AMBULANCE;So;0;ON;;;;;N;;;;; 1F692;FIRE ENGINE;So;0;ON;;;;;N;;;;; 1F693;POLICE CAR;So;0;ON;;;;;N;;;;; 1F694;ONCOMING POLICE CAR;So;0;ON;;;;;N;;;;; 1F695;TAXI;So;0;ON;;;;;N;;;;; 1F696;ONCOMING TAXI;So;0;ON;;;;;N;;;;; 1F697;AUTOMOBILE;So;0;ON;;;;;N;;;;; 1F698;ONCOMING AUTOMOBILE;So;0;ON;;;;;N;;;;; 1F699;RECREATIONAL VEHICLE;So;0;ON;;;;;N;;;;; 1F69A;DELIVERY TRUCK;So;0;ON;;;;;N;;;;; 1F69B;ARTICULATED LORRY;So;0;ON;;;;;N;;;;; 1F69C;TRACTOR;So;0;ON;;;;;N;;;;; 1F69D;MONORAIL;So;0;ON;;;;;N;;;;; 1F69E;MOUNTAIN RAILWAY;So;0;ON;;;;;N;;;;; 1F69F;SUSPENSION RAILWAY;So;0;ON;;;;;N;;;;; 1F6A0;MOUNTAIN CABLEWAY;So;0;ON;;;;;N;;;;; 1F6A1;AERIAL TRAMWAY;So;0;ON;;;;;N;;;;; 1F6A2;SHIP;So;0;ON;;;;;N;;;;; 1F6A3;ROWBOAT;So;0;ON;;;;;N;;;;; 1F6A4;SPEEDBOAT;So;0;ON;;;;;N;;;;; 1F6A5;HORIZONTAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;; 1F6A6;VERTICAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;; 1F6A7;CONSTRUCTION SIGN;So;0;ON;;;;;N;;;;; 1F6A8;POLICE CARS REVOLVING LIGHT;So;0;ON;;;;;N;;;;; 1F6A9;TRIANGULAR FLAG ON POST;So;0;ON;;;;;N;;;;; 1F6AA;DOOR;So;0;ON;;;;;N;;;;; 1F6AB;NO ENTRY SIGN;So;0;ON;;;;;N;;;;; 1F6AC;SMOKING SYMBOL;So;0;ON;;;;;N;;;;; 1F6AD;NO SMOKING SYMBOL;So;0;ON;;;;;N;;;;; 1F6AE;PUT LITTER IN ITS PLACE SYMBOL;So;0;ON;;;;;N;;;;; 1F6AF;DO NOT LITTER SYMBOL;So;0;ON;;;;;N;;;;; 1F6B0;POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;; 1F6B1;NON-POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;; 1F6B2;BICYCLE;So;0;ON;;;;;N;;;;; 1F6B3;NO BICYCLES;So;0;ON;;;;;N;;;;; 1F6B4;BICYCLIST;So;0;ON;;;;;N;;;;; 1F6B5;MOUNTAIN BICYCLIST;So;0;ON;;;;;N;;;;; 1F6B6;PEDESTRIAN;So;0;ON;;;;;N;;;;; 1F6B7;NO PEDESTRIANS;So;0;ON;;;;;N;;;;; 1F6B8;CHILDREN CROSSING;So;0;ON;;;;;N;;;;; 1F6B9;MENS SYMBOL;So;0;ON;;;;;N;;;;; 1F6BA;WOMENS SYMBOL;So;0;ON;;;;;N;;;;; 1F6BB;RESTROOM;So;0;ON;;;;;N;;;;; 1F6BC;BABY SYMBOL;So;0;ON;;;;;N;;;;; 1F6BD;TOILET;So;0;ON;;;;;N;;;;; 1F6BE;WATER CLOSET;So;0;ON;;;;;N;;;;; 1F6BF;SHOWER;So;0;ON;;;;;N;;;;; 1F6C0;BATH;So;0;ON;;;;;N;;;;; 1F6C1;BATHTUB;So;0;ON;;;;;N;;;;; 1F6C2;PASSPORT CONTROL;So;0;ON;;;;;N;;;;; 1F6C3;CUSTOMS;So;0;ON;;;;;N;;;;; 1F6C4;BAGGAGE CLAIM;So;0;ON;;;;;N;;;;; 1F6C5;LEFT LUGGAGE;So;0;ON;;;;;N;;;;; 1F6C6;TRIANGLE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;; 1F6C7;PROHIBITED SIGN;So;0;ON;;;;;N;;;;; 1F6C8;CIRCLED INFORMATION SOURCE;So;0;ON;;;;;N;;;;; 1F6C9;BOYS SYMBOL;So;0;ON;;;;;N;;;;; 1F6CA;GIRLS SYMBOL;So;0;ON;;;;;N;;;;; 1F6CB;COUCH AND LAMP;So;0;ON;;;;;N;;;;; 1F6CC;SLEEPING ACCOMMODATION;So;0;ON;;;;;N;;;;; 1F6CD;SHOPPING BAGS;So;0;ON;;;;;N;;;;; 1F6CE;BELLHOP BELL;So;0;ON;;;;;N;;;;; 1F6CF;BED;So;0;ON;;;;;N;;;;; 1F6D0;PLACE OF WORSHIP;So;0;ON;;;;;N;;;;; 1F6D1;OCTAGONAL SIGN;So;0;ON;;;;;N;;;;; 1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;; 1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;; 1F6E1;SHIELD;So;0;ON;;;;;N;;;;; 1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;; 1F6E3;MOTORWAY;So;0;ON;;;;;N;;;;; 1F6E4;RAILWAY TRACK;So;0;ON;;;;;N;;;;; 1F6E5;MOTOR BOAT;So;0;ON;;;;;N;;;;; 1F6E6;UP-POINTING MILITARY AIRPLANE;So;0;ON;;;;;N;;;;; 1F6E7;UP-POINTING AIRPLANE;So;0;ON;;;;;N;;;;; 1F6E8;UP-POINTING SMALL AIRPLANE;So;0;ON;;;;;N;;;;; 1F6E9;SMALL AIRPLANE;So;0;ON;;;;;N;;;;; 1F6EA;NORTHEAST-POINTING AIRPLANE;So;0;ON;;;;;N;;;;; 1F6EB;AIRPLANE DEPARTURE;So;0;ON;;;;;N;;;;; 1F6EC;AIRPLANE ARRIVING;So;0;ON;;;;;N;;;;; 1F6F0;SATELLITE;So;0;ON;;;;;N;;;;; 1F6F1;ONCOMING FIRE ENGINE;So;0;ON;;;;;N;;;;; 1F6F2;DIESEL LOCOMOTIVE;So;0;ON;;;;;N;;;;; 1F6F3;PASSENGER SHIP;So;0;ON;;;;;N;;;;; 1F6F4;SCOOTER;So;0;ON;;;;;N;;;;; 1F6F5;MOTOR SCOOTER;So;0;ON;;;;;N;;;;; 1F6F6;CANOE;So;0;ON;;;;;N;;;;; 1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;; 1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;; 1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;; 1F703;ALCHEMICAL SYMBOL FOR EARTH;So;0;ON;;;;;N;;;;; 1F704;ALCHEMICAL SYMBOL FOR WATER;So;0;ON;;;;;N;;;;; 1F705;ALCHEMICAL SYMBOL FOR AQUAFORTIS;So;0;ON;;;;;N;;;;; 1F706;ALCHEMICAL SYMBOL FOR AQUA REGIA;So;0;ON;;;;;N;;;;; 1F707;ALCHEMICAL SYMBOL FOR AQUA REGIA-2;So;0;ON;;;;;N;;;;; 1F708;ALCHEMICAL SYMBOL FOR AQUA VITAE;So;0;ON;;;;;N;;;;; 1F709;ALCHEMICAL SYMBOL FOR AQUA VITAE-2;So;0;ON;;;;;N;;;;; 1F70A;ALCHEMICAL SYMBOL FOR VINEGAR;So;0;ON;;;;;N;;;;; 1F70B;ALCHEMICAL SYMBOL FOR VINEGAR-2;So;0;ON;;;;;N;;;;; 1F70C;ALCHEMICAL SYMBOL FOR VINEGAR-3;So;0;ON;;;;;N;;;;; 1F70D;ALCHEMICAL SYMBOL FOR SULFUR;So;0;ON;;;;;N;;;;; 1F70E;ALCHEMICAL SYMBOL FOR PHILOSOPHERS SULFUR;So;0;ON;;;;;N;;;;; 1F70F;ALCHEMICAL SYMBOL FOR BLACK SULFUR;So;0;ON;;;;;N;;;;; 1F710;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE;So;0;ON;;;;;N;;;;; 1F711;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-2;So;0;ON;;;;;N;;;;; 1F712;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-3;So;0;ON;;;;;N;;;;; 1F713;ALCHEMICAL SYMBOL FOR CINNABAR;So;0;ON;;;;;N;;;;; 1F714;ALCHEMICAL SYMBOL FOR SALT;So;0;ON;;;;;N;;;;; 1F715;ALCHEMICAL SYMBOL FOR NITRE;So;0;ON;;;;;N;;;;; 1F716;ALCHEMICAL SYMBOL FOR VITRIOL;So;0;ON;;;;;N;;;;; 1F717;ALCHEMICAL SYMBOL FOR VITRIOL-2;So;0;ON;;;;;N;;;;; 1F718;ALCHEMICAL SYMBOL FOR ROCK SALT;So;0;ON;;;;;N;;;;; 1F719;ALCHEMICAL SYMBOL FOR ROCK SALT-2;So;0;ON;;;;;N;;;;; 1F71A;ALCHEMICAL SYMBOL FOR GOLD;So;0;ON;;;;;N;;;;; 1F71B;ALCHEMICAL SYMBOL FOR SILVER;So;0;ON;;;;;N;;;;; 1F71C;ALCHEMICAL SYMBOL FOR IRON ORE;So;0;ON;;;;;N;;;;; 1F71D;ALCHEMICAL SYMBOL FOR IRON ORE-2;So;0;ON;;;;;N;;;;; 1F71E;ALCHEMICAL SYMBOL FOR CROCUS OF IRON;So;0;ON;;;;;N;;;;; 1F71F;ALCHEMICAL SYMBOL FOR REGULUS OF IRON;So;0;ON;;;;;N;;;;; 1F720;ALCHEMICAL SYMBOL FOR COPPER ORE;So;0;ON;;;;;N;;;;; 1F721;ALCHEMICAL SYMBOL FOR IRON-COPPER ORE;So;0;ON;;;;;N;;;;; 1F722;ALCHEMICAL SYMBOL FOR SUBLIMATE OF COPPER;So;0;ON;;;;;N;;;;; 1F723;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER;So;0;ON;;;;;N;;;;; 1F724;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER-2;So;0;ON;;;;;N;;;;; 1F725;ALCHEMICAL SYMBOL FOR COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;; 1F726;ALCHEMICAL SYMBOL FOR SALT OF COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;; 1F727;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF COPPER;So;0;ON;;;;;N;;;;; 1F728;ALCHEMICAL SYMBOL FOR VERDIGRIS;So;0;ON;;;;;N;;;;; 1F729;ALCHEMICAL SYMBOL FOR TIN ORE;So;0;ON;;;;;N;;;;; 1F72A;ALCHEMICAL SYMBOL FOR LEAD ORE;So;0;ON;;;;;N;;;;; 1F72B;ALCHEMICAL SYMBOL FOR ANTIMONY ORE;So;0;ON;;;;;N;;;;; 1F72C;ALCHEMICAL SYMBOL FOR SUBLIMATE OF ANTIMONY;So;0;ON;;;;;N;;;;; 1F72D;ALCHEMICAL SYMBOL FOR SALT OF ANTIMONY;So;0;ON;;;;;N;;;;; 1F72E;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF ANTIMONY;So;0;ON;;;;;N;;;;; 1F72F;ALCHEMICAL SYMBOL FOR VINEGAR OF ANTIMONY;So;0;ON;;;;;N;;;;; 1F730;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY;So;0;ON;;;;;N;;;;; 1F731;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY-2;So;0;ON;;;;;N;;;;; 1F732;ALCHEMICAL SYMBOL FOR REGULUS;So;0;ON;;;;;N;;;;; 1F733;ALCHEMICAL SYMBOL FOR REGULUS-2;So;0;ON;;;;;N;;;;; 1F734;ALCHEMICAL SYMBOL FOR REGULUS-3;So;0;ON;;;;;N;;;;; 1F735;ALCHEMICAL SYMBOL FOR REGULUS-4;So;0;ON;;;;;N;;;;; 1F736;ALCHEMICAL SYMBOL FOR ALKALI;So;0;ON;;;;;N;;;;; 1F737;ALCHEMICAL SYMBOL FOR ALKALI-2;So;0;ON;;;;;N;;;;; 1F738;ALCHEMICAL SYMBOL FOR MARCASITE;So;0;ON;;;;;N;;;;; 1F739;ALCHEMICAL SYMBOL FOR SAL-AMMONIAC;So;0;ON;;;;;N;;;;; 1F73A;ALCHEMICAL SYMBOL FOR ARSENIC;So;0;ON;;;;;N;;;;; 1F73B;ALCHEMICAL SYMBOL FOR REALGAR;So;0;ON;;;;;N;;;;; 1F73C;ALCHEMICAL SYMBOL FOR REALGAR-2;So;0;ON;;;;;N;;;;; 1F73D;ALCHEMICAL SYMBOL FOR AURIPIGMENT;So;0;ON;;;;;N;;;;; 1F73E;ALCHEMICAL SYMBOL FOR BISMUTH ORE;So;0;ON;;;;;N;;;;; 1F73F;ALCHEMICAL SYMBOL FOR TARTAR;So;0;ON;;;;;N;;;;; 1F740;ALCHEMICAL SYMBOL FOR TARTAR-2;So;0;ON;;;;;N;;;;; 1F741;ALCHEMICAL SYMBOL FOR QUICK LIME;So;0;ON;;;;;N;;;;; 1F742;ALCHEMICAL SYMBOL FOR BORAX;So;0;ON;;;;;N;;;;; 1F743;ALCHEMICAL SYMBOL FOR BORAX-2;So;0;ON;;;;;N;;;;; 1F744;ALCHEMICAL SYMBOL FOR BORAX-3;So;0;ON;;;;;N;;;;; 1F745;ALCHEMICAL SYMBOL FOR ALUM;So;0;ON;;;;;N;;;;; 1F746;ALCHEMICAL SYMBOL FOR OIL;So;0;ON;;;;;N;;;;; 1F747;ALCHEMICAL SYMBOL FOR SPIRIT;So;0;ON;;;;;N;;;;; 1F748;ALCHEMICAL SYMBOL FOR TINCTURE;So;0;ON;;;;;N;;;;; 1F749;ALCHEMICAL SYMBOL FOR GUM;So;0;ON;;;;;N;;;;; 1F74A;ALCHEMICAL SYMBOL FOR WAX;So;0;ON;;;;;N;;;;; 1F74B;ALCHEMICAL SYMBOL FOR POWDER;So;0;ON;;;;;N;;;;; 1F74C;ALCHEMICAL SYMBOL FOR CALX;So;0;ON;;;;;N;;;;; 1F74D;ALCHEMICAL SYMBOL FOR TUTTY;So;0;ON;;;;;N;;;;; 1F74E;ALCHEMICAL SYMBOL FOR CAPUT MORTUUM;So;0;ON;;;;;N;;;;; 1F74F;ALCHEMICAL SYMBOL FOR SCEPTER OF JOVE;So;0;ON;;;;;N;;;;; 1F750;ALCHEMICAL SYMBOL FOR CADUCEUS;So;0;ON;;;;;N;;;;; 1F751;ALCHEMICAL SYMBOL FOR TRIDENT;So;0;ON;;;;;N;;;;; 1F752;ALCHEMICAL SYMBOL FOR STARRED TRIDENT;So;0;ON;;;;;N;;;;; 1F753;ALCHEMICAL SYMBOL FOR LODESTONE;So;0;ON;;;;;N;;;;; 1F754;ALCHEMICAL SYMBOL FOR SOAP;So;0;ON;;;;;N;;;;; 1F755;ALCHEMICAL SYMBOL FOR URINE;So;0;ON;;;;;N;;;;; 1F756;ALCHEMICAL SYMBOL FOR HORSE DUNG;So;0;ON;;;;;N;;;;; 1F757;ALCHEMICAL SYMBOL FOR ASHES;So;0;ON;;;;;N;;;;; 1F758;ALCHEMICAL SYMBOL FOR POT ASHES;So;0;ON;;;;;N;;;;; 1F759;ALCHEMICAL SYMBOL FOR BRICK;So;0;ON;;;;;N;;;;; 1F75A;ALCHEMICAL SYMBOL FOR POWDERED BRICK;So;0;ON;;;;;N;;;;; 1F75B;ALCHEMICAL SYMBOL FOR AMALGAM;So;0;ON;;;;;N;;;;; 1F75C;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM;So;0;ON;;;;;N;;;;; 1F75D;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM-2;So;0;ON;;;;;N;;;;; 1F75E;ALCHEMICAL SYMBOL FOR SUBLIMATION;So;0;ON;;;;;N;;;;; 1F75F;ALCHEMICAL SYMBOL FOR PRECIPITATE;So;0;ON;;;;;N;;;;; 1F760;ALCHEMICAL SYMBOL FOR DISTILL;So;0;ON;;;;;N;;;;; 1F761;ALCHEMICAL SYMBOL FOR DISSOLVE;So;0;ON;;;;;N;;;;; 1F762;ALCHEMICAL SYMBOL FOR DISSOLVE-2;So;0;ON;;;;;N;;;;; 1F763;ALCHEMICAL SYMBOL FOR PURIFY;So;0;ON;;;;;N;;;;; 1F764;ALCHEMICAL SYMBOL FOR PUTREFACTION;So;0;ON;;;;;N;;;;; 1F765;ALCHEMICAL SYMBOL FOR CRUCIBLE;So;0;ON;;;;;N;;;;; 1F766;ALCHEMICAL SYMBOL FOR CRUCIBLE-2;So;0;ON;;;;;N;;;;; 1F767;ALCHEMICAL SYMBOL FOR CRUCIBLE-3;So;0;ON;;;;;N;;;;; 1F768;ALCHEMICAL SYMBOL FOR CRUCIBLE-4;So;0;ON;;;;;N;;;;; 1F769;ALCHEMICAL SYMBOL FOR CRUCIBLE-5;So;0;ON;;;;;N;;;;; 1F76A;ALCHEMICAL SYMBOL FOR ALEMBIC;So;0;ON;;;;;N;;;;; 1F76B;ALCHEMICAL SYMBOL FOR BATH OF MARY;So;0;ON;;;;;N;;;;; 1F76C;ALCHEMICAL SYMBOL FOR BATH OF VAPOURS;So;0;ON;;;;;N;;;;; 1F76D;ALCHEMICAL SYMBOL FOR RETORT;So;0;ON;;;;;N;;;;; 1F76E;ALCHEMICAL SYMBOL FOR HOUR;So;0;ON;;;;;N;;;;; 1F76F;ALCHEMICAL SYMBOL FOR NIGHT;So;0;ON;;;;;N;;;;; 1F770;ALCHEMICAL SYMBOL FOR DAY-NIGHT;So;0;ON;;;;;N;;;;; 1F771;ALCHEMICAL SYMBOL FOR MONTH;So;0;ON;;;;;N;;;;; 1F772;ALCHEMICAL SYMBOL FOR HALF DRAM;So;0;ON;;;;;N;;;;; 1F773;ALCHEMICAL SYMBOL FOR HALF OUNCE;So;0;ON;;;;;N;;;;; 1F780;BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 1F781;BLACK UP-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 1F782;BLACK RIGHT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 1F783;BLACK DOWN-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; 1F784;BLACK SLIGHTLY SMALL CIRCLE;So;0;ON;;;;;N;;;;; 1F785;MEDIUM BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F786;BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F787;HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F788;VERY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F789;EXTREMELY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; 1F78A;WHITE CIRCLE CONTAINING BLACK SMALL CIRCLE;So;0;ON;;;;;N;;;;; 1F78B;ROUND TARGET;So;0;ON;;;;;N;;;;; 1F78C;BLACK TINY SQUARE;So;0;ON;;;;;N;;;;; 1F78D;BLACK SLIGHTLY SMALL SQUARE;So;0;ON;;;;;N;;;;; 1F78E;LIGHT WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F78F;MEDIUM WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F790;BOLD WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F791;HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F792;VERY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F793;EXTREMELY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; 1F794;WHITE SQUARE CONTAINING BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; 1F795;WHITE SQUARE CONTAINING BLACK MEDIUM SQUARE;So;0;ON;;;;;N;;;;; 1F796;SQUARE TARGET;So;0;ON;;;;;N;;;;; 1F797;BLACK TINY DIAMOND;So;0;ON;;;;;N;;;;; 1F798;BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;; 1F799;BLACK MEDIUM SMALL DIAMOND;So;0;ON;;;;;N;;;;; 1F79A;WHITE DIAMOND CONTAINING BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;; 1F79B;WHITE DIAMOND CONTAINING BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; 1F79C;DIAMOND TARGET;So;0;ON;;;;;N;;;;; 1F79D;BLACK TINY LOZENGE;So;0;ON;;;;;N;;;;; 1F79E;BLACK VERY SMALL LOZENGE;So;0;ON;;;;;N;;;;; 1F79F;BLACK MEDIUM SMALL LOZENGE;So;0;ON;;;;;N;;;;; 1F7A0;WHITE LOZENGE CONTAINING BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;; 1F7A1;THIN GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A2;LIGHT GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A3;MEDIUM GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A4;BOLD GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A5;VERY BOLD GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A6;VERY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A7;EXTREMELY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; 1F7A8;THIN SALTIRE;So;0;ON;;;;;N;;;;; 1F7A9;LIGHT SALTIRE;So;0;ON;;;;;N;;;;; 1F7AA;MEDIUM SALTIRE;So;0;ON;;;;;N;;;;; 1F7AB;BOLD SALTIRE;So;0;ON;;;;;N;;;;; 1F7AC;HEAVY SALTIRE;So;0;ON;;;;;N;;;;; 1F7AD;VERY HEAVY SALTIRE;So;0;ON;;;;;N;;;;; 1F7AE;EXTREMELY HEAVY SALTIRE;So;0;ON;;;;;N;;;;; 1F7AF;LIGHT FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B0;MEDIUM FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B1;BOLD FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B2;HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B3;VERY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B4;EXTREMELY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B5;LIGHT SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B6;MEDIUM SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B7;BOLD SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B8;HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7B9;VERY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BA;EXTREMELY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BB;LIGHT EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BC;MEDIUM EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BD;BOLD EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BE;HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7BF;VERY HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; 1F7C0;LIGHT THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C1;MEDIUM THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C2;THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C3;MEDIUM THREE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F7C4;LIGHT FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C5;MEDIUM FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C6;FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7C7;MEDIUM FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F7C8;REVERSE LIGHT FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F7C9;LIGHT FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7CA;HEAVY FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7CB;MEDIUM SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7CC;HEAVY SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7CD;SIX POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F7CE;MEDIUM EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7CF;HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7D0;VERY HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7D1;HEAVY EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F7D2;LIGHT TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7D3;HEAVY TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; 1F7D4;HEAVY TWELVE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; 1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F803;DOWNWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F804;LEFTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F805;UPWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F806;RIGHTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F807;DOWNWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F808;LEFTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F809;UPWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F80A;RIGHTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F80B;DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F810;LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F811;UPWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F812;RIGHTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F813;DOWNWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F814;LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F815;UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F816;RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F817;DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F818;HEAVY LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F819;HEAVY UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81A;HEAVY RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81B;HEAVY DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81C;HEAVY LEFTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81D;HEAVY UPWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81E;HEAVY RIGHTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F81F;HEAVY DOWNWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 1F820;LEFTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; 1F821;UPWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; 1F822;RIGHTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; 1F823;DOWNWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; 1F824;LEFTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; 1F825;UPWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; 1F826;RIGHTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; 1F827;DOWNWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; 1F828;LEFTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; 1F829;UPWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; 1F82A;RIGHTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; 1F82B;DOWNWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; 1F82C;LEFTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F82D;UPWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F82E;RIGHTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F82F;DOWNWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F830;LEFTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F831;UPWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F832;RIGHTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F833;DOWNWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; 1F834;LEFTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; 1F835;UPWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; 1F836;RIGHTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; 1F837;DOWNWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; 1F838;LEFTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; 1F839;UPWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; 1F83A;RIGHTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; 1F83B;DOWNWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; 1F83C;LEFTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F83D;UPWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F83E;RIGHTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F83F;DOWNWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F840;LEFTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F841;UPWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F842;RIGHTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F843;DOWNWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; 1F844;LEFTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; 1F845;UPWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; 1F846;RIGHTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; 1F847;DOWNWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; 1F850;LEFTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F851;UPWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F852;RIGHTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F853;DOWNWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F854;NORTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F855;NORTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F856;SOUTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F857;SOUTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F858;LEFT RIGHT SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F859;UP DOWN SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; 1F860;WIDE-HEADED LEFTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F861;WIDE-HEADED UPWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F862;WIDE-HEADED RIGHTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F863;WIDE-HEADED DOWNWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F864;WIDE-HEADED NORTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F865;WIDE-HEADED NORTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F866;WIDE-HEADED SOUTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F867;WIDE-HEADED SOUTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; 1F868;WIDE-HEADED LEFTWARDS BARB ARROW;So;0;ON;;;;;N;;;;; 1F869;WIDE-HEADED UPWARDS BARB ARROW;So;0;ON;;;;;N;;;;; 1F86A;WIDE-HEADED RIGHTWARDS BARB ARROW;So;0;ON;;;;;N;;;;; 1F86B;WIDE-HEADED DOWNWARDS BARB ARROW;So;0;ON;;;;;N;;;;; 1F86C;WIDE-HEADED NORTH WEST BARB ARROW;So;0;ON;;;;;N;;;;; 1F86D;WIDE-HEADED NORTH EAST BARB ARROW;So;0;ON;;;;;N;;;;; 1F86E;WIDE-HEADED SOUTH EAST BARB ARROW;So;0;ON;;;;;N;;;;; 1F86F;WIDE-HEADED SOUTH WEST BARB ARROW;So;0;ON;;;;;N;;;;; 1F870;WIDE-HEADED LEFTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F871;WIDE-HEADED UPWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F872;WIDE-HEADED RIGHTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F873;WIDE-HEADED DOWNWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F874;WIDE-HEADED NORTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F875;WIDE-HEADED NORTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F876;WIDE-HEADED SOUTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F877;WIDE-HEADED SOUTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; 1F878;WIDE-HEADED LEFTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F879;WIDE-HEADED UPWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87A;WIDE-HEADED RIGHTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87B;WIDE-HEADED DOWNWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87C;WIDE-HEADED NORTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87D;WIDE-HEADED NORTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87E;WIDE-HEADED SOUTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F87F;WIDE-HEADED SOUTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F880;WIDE-HEADED LEFTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F881;WIDE-HEADED UPWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F882;WIDE-HEADED RIGHTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F883;WIDE-HEADED DOWNWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F884;WIDE-HEADED NORTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F885;WIDE-HEADED NORTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F886;WIDE-HEADED SOUTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F887;WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; 1F890;LEFTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F891;UPWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F892;RIGHTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F893;DOWNWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F894;LEFTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F895;UPWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F896;RIGHTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F897;DOWNWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; 1F898;LEFTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; 1F899;UPWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; 1F89A;RIGHTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; 1F89B;DOWNWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; 1F89C;HEAVY ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;; 1F89D;HEAVY ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;; 1F89E;HEAVY ARROW SHAFT WIDTH ONE HALF;So;0;ON;;;;;N;;;;; 1F89F;HEAVY ARROW SHAFT WIDTH ONE THIRD;So;0;ON;;;;;N;;;;; 1F8A0;LEFTWARDS BOTTOM-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A1;RIGHTWARDS BOTTOM SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A2;LEFTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A3;RIGHTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A4;LEFTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A5;RIGHTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A6;LEFTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A7;RIGHTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A8;LEFTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8A9;RIGHTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8AA;LEFTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8AB;RIGHTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; 1F8AC;WHITE ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;; 1F8AD;WHITE ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;; 1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;; 1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;; 1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;; 1F913;NERD FACE;So;0;ON;;;;;N;;;;; 1F914;THINKING FACE;So;0;ON;;;;;N;;;;; 1F915;FACE WITH HEAD-BANDAGE;So;0;ON;;;;;N;;;;; 1F916;ROBOT FACE;So;0;ON;;;;;N;;;;; 1F917;HUGGING FACE;So;0;ON;;;;;N;;;;; 1F918;SIGN OF THE HORNS;So;0;ON;;;;;N;;;;; 1F919;CALL ME HAND;So;0;ON;;;;;N;;;;; 1F91A;RAISED BACK OF HAND;So;0;ON;;;;;N;;;;; 1F91B;LEFT-FACING FIST;So;0;ON;;;;;N;;;;; 1F91C;RIGHT-FACING FIST;So;0;ON;;;;;N;;;;; 1F91D;HANDSHAKE;So;0;ON;;;;;N;;;;; 1F91E;HAND WITH INDEX AND MIDDLE FINGERS CROSSED;So;0;ON;;;;;N;;;;; 1F920;FACE WITH COWBOY HAT;So;0;ON;;;;;N;;;;; 1F921;CLOWN FACE;So;0;ON;;;;;N;;;;; 1F922;NAUSEATED FACE;So;0;ON;;;;;N;;;;; 1F923;ROLLING ON THE FLOOR LAUGHING;So;0;ON;;;;;N;;;;; 1F924;DROOLING FACE;So;0;ON;;;;;N;;;;; 1F925;LYING FACE;So;0;ON;;;;;N;;;;; 1F926;FACE PALM;So;0;ON;;;;;N;;;;; 1F927;SNEEZING FACE;So;0;ON;;;;;N;;;;; 1F930;PREGNANT WOMAN;So;0;ON;;;;;N;;;;; 1F933;SELFIE;So;0;ON;;;;;N;;;;; 1F934;PRINCE;So;0;ON;;;;;N;;;;; 1F935;MAN IN TUXEDO;So;0;ON;;;;;N;;;;; 1F936;MOTHER CHRISTMAS;So;0;ON;;;;;N;;;;; 1F937;SHRUG;So;0;ON;;;;;N;;;;; 1F938;PERSON DOING CARTWHEEL;So;0;ON;;;;;N;;;;; 1F939;JUGGLING;So;0;ON;;;;;N;;;;; 1F93A;FENCER;So;0;ON;;;;;N;;;;; 1F93B;MODERN PENTATHLON;So;0;ON;;;;;N;;;;; 1F93C;WRESTLERS;So;0;ON;;;;;N;;;;; 1F93D;WATER POLO;So;0;ON;;;;;N;;;;; 1F93E;HANDBALL;So;0;ON;;;;;N;;;;; 1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;; 1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;; 1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;; 1F943;TUMBLER GLASS;So;0;ON;;;;;N;;;;; 1F944;SPOON;So;0;ON;;;;;N;;;;; 1F945;GOAL NET;So;0;ON;;;;;N;;;;; 1F946;RIFLE;So;0;ON;;;;;N;;;;; 1F947;FIRST PLACE MEDAL;So;0;ON;;;;;N;;;;; 1F948;SECOND PLACE MEDAL;So;0;ON;;;;;N;;;;; 1F949;THIRD PLACE MEDAL;So;0;ON;;;;;N;;;;; 1F94A;BOXING GLOVE;So;0;ON;;;;;N;;;;; 1F94B;MARTIAL ARTS UNIFORM;So;0;ON;;;;;N;;;;; 1F950;CROISSANT;So;0;ON;;;;;N;;;;; 1F951;AVOCADO;So;0;ON;;;;;N;;;;; 1F952;CUCUMBER;So;0;ON;;;;;N;;;;; 1F953;BACON;So;0;ON;;;;;N;;;;; 1F954;POTATO;So;0;ON;;;;;N;;;;; 1F955;CARROT;So;0;ON;;;;;N;;;;; 1F956;BAGUETTE BREAD;So;0;ON;;;;;N;;;;; 1F957;GREEN SALAD;So;0;ON;;;;;N;;;;; 1F958;SHALLOW PAN OF FOOD;So;0;ON;;;;;N;;;;; 1F959;STUFFED FLATBREAD;So;0;ON;;;;;N;;;;; 1F95A;EGG;So;0;ON;;;;;N;;;;; 1F95B;GLASS OF MILK;So;0;ON;;;;;N;;;;; 1F95C;PEANUTS;So;0;ON;;;;;N;;;;; 1F95D;KIWIFRUIT;So;0;ON;;;;;N;;;;; 1F95E;PANCAKES;So;0;ON;;;;;N;;;;; 1F980;CRAB;So;0;ON;;;;;N;;;;; 1F981;LION FACE;So;0;ON;;;;;N;;;;; 1F982;SCORPION;So;0;ON;;;;;N;;;;; 1F983;TURKEY;So;0;ON;;;;;N;;;;; 1F984;UNICORN FACE;So;0;ON;;;;;N;;;;; 1F985;EAGLE;So;0;ON;;;;;N;;;;; 1F986;DUCK;So;0;ON;;;;;N;;;;; 1F987;BAT;So;0;ON;;;;;N;;;;; 1F988;SHARK;So;0;ON;;;;;N;;;;; 1F989;OWL;So;0;ON;;;;;N;;;;; 1F98A;FOX FACE;So;0;ON;;;;;N;;;;; 1F98B;BUTTERFLY;So;0;ON;;;;;N;;;;; 1F98C;DEER;So;0;ON;;;;;N;;;;; 1F98D;GORILLA;So;0;ON;;;;;N;;;;; 1F98E;LIZARD;So;0;ON;;;;;N;;;;; 1F98F;RHINOCEROS;So;0;ON;;;;;N;;;;; 1F990;SHRIMP;So;0;ON;;;;;N;;;;; 1F991;SQUID;So;0;ON;;;;;N;;;;; 1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;; 20000;;Lo;0;L;;;;;N;;;;; 2A6D6;;Lo;0;L;;;;;N;;;;; 2A700;;Lo;0;L;;;;;N;;;;; 2B734;;Lo;0;L;;;;;N;;;;; 2B740;;Lo;0;L;;;;;N;;;;; 2B81D;;Lo;0;L;;;;;N;;;;; 2B820;;Lo;0;L;;;;;N;;;;; 2CEA1;;Lo;0;L;;;;;N;;;;; 2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;; 2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;; 2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;; 2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;; 2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;; 2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;; 2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;; 2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;; 2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;; 2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;; 2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;; 2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;; 2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;; 2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;; 2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;; 2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;; 2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;; 2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;; 2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;; 2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;; 2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;; 2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;; 2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;; 2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;; 2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;; 2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;; 2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;; 2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;; 2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;; 2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;; 2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;; 2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;; 2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;; 2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;; 2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;; 2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;; 2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;; 2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;; 2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;; 2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;; 2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;; 2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;; 2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;; 2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;; 2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;; 2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;; 2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;; 2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;; 2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;; 2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;; 2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;; 2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;; 2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;; 2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;; 2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;; 2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;; 2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;; 2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;; 2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;; 2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;; 2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;; 2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;; 2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;; 2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;; 2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;; 2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;; 2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;; 2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;; 2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;; 2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;; 2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;; 2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;; 2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;; 2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;; 2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;; 2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;; 2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;; 2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;; 2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;; 2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;; 2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;; 2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;; 2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;; 2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;; 2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;; 2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;; 2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;; 2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;; 2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;; 2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;; 2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;; 2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;; 2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;; 2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;; 2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;; 2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;; 2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;; 2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;; 2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;; 2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;; 2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;; 2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;; 2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;; 2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;; 2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;36FC;;;;N;;;;; 2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;; 2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;; 2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;; 2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;; 2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;; 2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;; 2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;; 2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;; 2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;; 2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;; 2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;; 2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F53;;;;N;;;;; 2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;; 2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;; 2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;; 2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;; 2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;; 2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;; 2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;; 2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;; 2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;; 2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;; 2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;; 2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;; 2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;; 2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;; 2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;; 2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;; 2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;; 2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;; 2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;; 2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;; 2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;; 2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;; 2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;; 2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;; 2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;; 2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;; 2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;; 2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;9;N;;;;; 2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;; 2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;; 2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;; 2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;; 2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;; 2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;; 2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;; 2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;; 2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;; 2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;; 2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;; 2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;; 2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;; 2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;; 2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;; 2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;; 2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;; 2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;; 2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;; 2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;; 2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;; 2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;; 2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;; 2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;; 2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;; 2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;; 2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;; 2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;; 2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;; 2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;; 2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;; 2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;; 2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;; 2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;; 2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;; 2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;; 2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;; 2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;; 2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;; 2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;; 2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;; 2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;; 2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;; 2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;; 2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;; 2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;; 2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;; 2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;; 2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;; 2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;; 2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;; 2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;; 2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;; 2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;; 2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;; 2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;; 2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;; 2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;; 2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;; 2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;; 2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;; 2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;; 2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;; 2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;; 2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;; 2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;; 2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;; 2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;; 2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;; 2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;; 2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;; 2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;; 2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;; 2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;; 2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;; 2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;; 2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;; 2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;; 2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;; 2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;; 2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;; 2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;; 2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;; 2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;; 2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;; 2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;; 2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;; 2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;; 2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;; 2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;; 2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;; 2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;; 2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;; 2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;; 2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;; 2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;; 2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;; 2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;; 2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;; 2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;; 2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;; 2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;; 2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;; 2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;; 2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;; 2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;; 2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;; 2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;; 2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;; 2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;; 2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;; 2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;; 2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;; 2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;; 2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;; 2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;; 2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;; 2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;; 2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;; 2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;; 2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;; 2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;; 2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;; 2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;; 2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;; 2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;; 2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;; 2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;; 2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;; 2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;; 2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;; 2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;; 2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;; 2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;; 2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;; 2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;; 2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;; 2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;; 2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;; 2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;; 2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;; 2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;; 2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;243AB;;;;N;;;;; 2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;; 2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;; 2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;; 2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;; 2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;; 2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;; 2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;; 2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;; 2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;; 2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;; 2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;; 2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;; 2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;; 2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;; 2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;; 2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;; 2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;; 2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;; 2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;; 2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;; 2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;; 2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;; 2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;; 2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;; 2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;; 2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;; 2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;; 2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;; 2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;; 2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;; 2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;; 2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;; 2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;; 2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;; 2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;; 2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;; 2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;; 2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;; 2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;; 2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;; 2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;; 2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;; 2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;; 2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;; 2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;; 2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;; 2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;; 2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;; 2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;; 2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;; 2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;; 2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;; 2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;; 2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;; 2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;; 2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;; 2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;; 2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;; 2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;; 2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;; 2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;; 2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;; 2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;; 2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AEE;;;;N;;;;; 2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;; 2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;; 2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;; 2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;; 2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;; 2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;; 2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;; 2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;; 2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;; 2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;; 2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;; 2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;; 2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;; 2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;; 2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;; 2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;; 2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;; 2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;; 2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;; 2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;; 2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;; 2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;; 2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;; 2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;; 2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;; 2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;; 2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;; 2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;; 2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;; 2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;; 2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;; 2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;; 2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;; 2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;; 2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;; 2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;; 2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;; 2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;; 2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;; 2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;; 2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;; 2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;; 2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;; 2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;; 2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;; 2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;; 2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;; 2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;; 2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;; 2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;; 2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;; 2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;; 2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;; 2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;; 2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;; 2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;; 2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;; 2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;; 2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;; 2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;; 2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;; 2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;; 2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;; 2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;; 2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;; 2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;; 2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;; 2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;; 2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;; 2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;; 2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;; 2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;; 2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;; 2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;; 2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;; 2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;; 2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;; 2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;; 2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;; 2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;; 2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;; 2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;; 2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;; 2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;; 2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;; 2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;; 2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;; 2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;; 2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;; 2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;; 2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;; 2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;; 2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;; 2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;; 2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;; 2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;45D7;;;;N;;;;; 2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;; 2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;; 2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;; 2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;; 2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;; 2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;; 2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;; 2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;; 2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;; 2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;; 2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;; 2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;; 2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;; 2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;; 2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;; 2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;; 2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;; 2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;; 2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;; 2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;; 2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;; 2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;; 2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;; 2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;; 2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;; 2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;; 2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;; 2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;; 2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;; 2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;; 2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;; 2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;; 2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;; 2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;; 2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;; 2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;; 2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;; 2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;; 2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;; 2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;; 2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;; 2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;; 2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;; 2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;; 2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;; 2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;; 2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;; 2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;; 2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;; 2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;; 2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;; 2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;; 2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;; 2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;; 2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;; 2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;; 2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;; 2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;; 2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;; 2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;; 2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;; 2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;; 2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;; 2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;; 2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;; 2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;; 2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;; 2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;; 2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;; 2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;; 2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;; 2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;; 2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;; 2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;; 2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;; 2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;; 2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;; 2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;; 2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;; 2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;; 2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;; 2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;; 2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;; 2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;; 2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;; 2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;; 2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;; 2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;; 2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;; 2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;; 2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;; 2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;; 2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;; 2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;; E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;; E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;; E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;; E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;; E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;; E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;; E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;; E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;; E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;; E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;; E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;; E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;; E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;; E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;; E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;; E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;; E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;; E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;; E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;; E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;; E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;; E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;; E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;; E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;; E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;; E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;; E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;; E003A;TAG COLON;Cf;0;BN;;;;;N;;;;; E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;; E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;; E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;; E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;; E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;; E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;; E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;; E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;; E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;; E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;; E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;; E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;; E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;; E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;; E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;; E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;; E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;; E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;; E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;; E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;; E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;; E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;; E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;; E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;; E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;; E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;; E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;; E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;; E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;; E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;; E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;; E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;; E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;; E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;; E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;; E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;; E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;; E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;; E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;; E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;; E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;; E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;; E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;; E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;; E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;; E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;; E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;; E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;; E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;; E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;; E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;; E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;; E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;; E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;; E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;; E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;; E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;; E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;; E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;; E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;; E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;; E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;; E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;; E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;; E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;; E0100;VARIATION SELECTOR-17;Mn;0;NSM;;;;;N;;;;; E0101;VARIATION SELECTOR-18;Mn;0;NSM;;;;;N;;;;; E0102;VARIATION SELECTOR-19;Mn;0;NSM;;;;;N;;;;; E0103;VARIATION SELECTOR-20;Mn;0;NSM;;;;;N;;;;; E0104;VARIATION SELECTOR-21;Mn;0;NSM;;;;;N;;;;; E0105;VARIATION SELECTOR-22;Mn;0;NSM;;;;;N;;;;; E0106;VARIATION SELECTOR-23;Mn;0;NSM;;;;;N;;;;; E0107;VARIATION SELECTOR-24;Mn;0;NSM;;;;;N;;;;; E0108;VARIATION SELECTOR-25;Mn;0;NSM;;;;;N;;;;; E0109;VARIATION SELECTOR-26;Mn;0;NSM;;;;;N;;;;; E010A;VARIATION SELECTOR-27;Mn;0;NSM;;;;;N;;;;; E010B;VARIATION SELECTOR-28;Mn;0;NSM;;;;;N;;;;; E010C;VARIATION SELECTOR-29;Mn;0;NSM;;;;;N;;;;; E010D;VARIATION SELECTOR-30;Mn;0;NSM;;;;;N;;;;; E010E;VARIATION SELECTOR-31;Mn;0;NSM;;;;;N;;;;; E010F;VARIATION SELECTOR-32;Mn;0;NSM;;;;;N;;;;; E0110;VARIATION SELECTOR-33;Mn;0;NSM;;;;;N;;;;; E0111;VARIATION SELECTOR-34;Mn;0;NSM;;;;;N;;;;; E0112;VARIATION SELECTOR-35;Mn;0;NSM;;;;;N;;;;; E0113;VARIATION SELECTOR-36;Mn;0;NSM;;;;;N;;;;; E0114;VARIATION SELECTOR-37;Mn;0;NSM;;;;;N;;;;; E0115;VARIATION SELECTOR-38;Mn;0;NSM;;;;;N;;;;; E0116;VARIATION SELECTOR-39;Mn;0;NSM;;;;;N;;;;; E0117;VARIATION SELECTOR-40;Mn;0;NSM;;;;;N;;;;; E0118;VARIATION SELECTOR-41;Mn;0;NSM;;;;;N;;;;; E0119;VARIATION SELECTOR-42;Mn;0;NSM;;;;;N;;;;; E011A;VARIATION SELECTOR-43;Mn;0;NSM;;;;;N;;;;; E011B;VARIATION SELECTOR-44;Mn;0;NSM;;;;;N;;;;; E011C;VARIATION SELECTOR-45;Mn;0;NSM;;;;;N;;;;; E011D;VARIATION SELECTOR-46;Mn;0;NSM;;;;;N;;;;; E011E;VARIATION SELECTOR-47;Mn;0;NSM;;;;;N;;;;; E011F;VARIATION SELECTOR-48;Mn;0;NSM;;;;;N;;;;; E0120;VARIATION SELECTOR-49;Mn;0;NSM;;;;;N;;;;; E0121;VARIATION SELECTOR-50;Mn;0;NSM;;;;;N;;;;; E0122;VARIATION SELECTOR-51;Mn;0;NSM;;;;;N;;;;; E0123;VARIATION SELECTOR-52;Mn;0;NSM;;;;;N;;;;; E0124;VARIATION SELECTOR-53;Mn;0;NSM;;;;;N;;;;; E0125;VARIATION SELECTOR-54;Mn;0;NSM;;;;;N;;;;; E0126;VARIATION SELECTOR-55;Mn;0;NSM;;;;;N;;;;; E0127;VARIATION SELECTOR-56;Mn;0;NSM;;;;;N;;;;; E0128;VARIATION SELECTOR-57;Mn;0;NSM;;;;;N;;;;; E0129;VARIATION SELECTOR-58;Mn;0;NSM;;;;;N;;;;; E012A;VARIATION SELECTOR-59;Mn;0;NSM;;;;;N;;;;; E012B;VARIATION SELECTOR-60;Mn;0;NSM;;;;;N;;;;; E012C;VARIATION SELECTOR-61;Mn;0;NSM;;;;;N;;;;; E012D;VARIATION SELECTOR-62;Mn;0;NSM;;;;;N;;;;; E012E;VARIATION SELECTOR-63;Mn;0;NSM;;;;;N;;;;; E012F;VARIATION SELECTOR-64;Mn;0;NSM;;;;;N;;;;; E0130;VARIATION SELECTOR-65;Mn;0;NSM;;;;;N;;;;; E0131;VARIATION SELECTOR-66;Mn;0;NSM;;;;;N;;;;; E0132;VARIATION SELECTOR-67;Mn;0;NSM;;;;;N;;;;; E0133;VARIATION SELECTOR-68;Mn;0;NSM;;;;;N;;;;; E0134;VARIATION SELECTOR-69;Mn;0;NSM;;;;;N;;;;; E0135;VARIATION SELECTOR-70;Mn;0;NSM;;;;;N;;;;; E0136;VARIATION SELECTOR-71;Mn;0;NSM;;;;;N;;;;; E0137;VARIATION SELECTOR-72;Mn;0;NSM;;;;;N;;;;; E0138;VARIATION SELECTOR-73;Mn;0;NSM;;;;;N;;;;; E0139;VARIATION SELECTOR-74;Mn;0;NSM;;;;;N;;;;; E013A;VARIATION SELECTOR-75;Mn;0;NSM;;;;;N;;;;; E013B;VARIATION SELECTOR-76;Mn;0;NSM;;;;;N;;;;; E013C;VARIATION SELECTOR-77;Mn;0;NSM;;;;;N;;;;; E013D;VARIATION SELECTOR-78;Mn;0;NSM;;;;;N;;;;; E013E;VARIATION SELECTOR-79;Mn;0;NSM;;;;;N;;;;; E013F;VARIATION SELECTOR-80;Mn;0;NSM;;;;;N;;;;; E0140;VARIATION SELECTOR-81;Mn;0;NSM;;;;;N;;;;; E0141;VARIATION SELECTOR-82;Mn;0;NSM;;;;;N;;;;; E0142;VARIATION SELECTOR-83;Mn;0;NSM;;;;;N;;;;; E0143;VARIATION SELECTOR-84;Mn;0;NSM;;;;;N;;;;; E0144;VARIATION SELECTOR-85;Mn;0;NSM;;;;;N;;;;; E0145;VARIATION SELECTOR-86;Mn;0;NSM;;;;;N;;;;; E0146;VARIATION SELECTOR-87;Mn;0;NSM;;;;;N;;;;; E0147;VARIATION SELECTOR-88;Mn;0;NSM;;;;;N;;;;; E0148;VARIATION SELECTOR-89;Mn;0;NSM;;;;;N;;;;; E0149;VARIATION SELECTOR-90;Mn;0;NSM;;;;;N;;;;; E014A;VARIATION SELECTOR-91;Mn;0;NSM;;;;;N;;;;; E014B;VARIATION SELECTOR-92;Mn;0;NSM;;;;;N;;;;; E014C;VARIATION SELECTOR-93;Mn;0;NSM;;;;;N;;;;; E014D;VARIATION SELECTOR-94;Mn;0;NSM;;;;;N;;;;; E014E;VARIATION SELECTOR-95;Mn;0;NSM;;;;;N;;;;; E014F;VARIATION SELECTOR-96;Mn;0;NSM;;;;;N;;;;; E0150;VARIATION SELECTOR-97;Mn;0;NSM;;;;;N;;;;; E0151;VARIATION SELECTOR-98;Mn;0;NSM;;;;;N;;;;; E0152;VARIATION SELECTOR-99;Mn;0;NSM;;;;;N;;;;; E0153;VARIATION SELECTOR-100;Mn;0;NSM;;;;;N;;;;; E0154;VARIATION SELECTOR-101;Mn;0;NSM;;;;;N;;;;; E0155;VARIATION SELECTOR-102;Mn;0;NSM;;;;;N;;;;; E0156;VARIATION SELECTOR-103;Mn;0;NSM;;;;;N;;;;; E0157;VARIATION SELECTOR-104;Mn;0;NSM;;;;;N;;;;; E0158;VARIATION SELECTOR-105;Mn;0;NSM;;;;;N;;;;; E0159;VARIATION SELECTOR-106;Mn;0;NSM;;;;;N;;;;; E015A;VARIATION SELECTOR-107;Mn;0;NSM;;;;;N;;;;; E015B;VARIATION SELECTOR-108;Mn;0;NSM;;;;;N;;;;; E015C;VARIATION SELECTOR-109;Mn;0;NSM;;;;;N;;;;; E015D;VARIATION SELECTOR-110;Mn;0;NSM;;;;;N;;;;; E015E;VARIATION SELECTOR-111;Mn;0;NSM;;;;;N;;;;; E015F;VARIATION SELECTOR-112;Mn;0;NSM;;;;;N;;;;; E0160;VARIATION SELECTOR-113;Mn;0;NSM;;;;;N;;;;; E0161;VARIATION SELECTOR-114;Mn;0;NSM;;;;;N;;;;; E0162;VARIATION SELECTOR-115;Mn;0;NSM;;;;;N;;;;; E0163;VARIATION SELECTOR-116;Mn;0;NSM;;;;;N;;;;; E0164;VARIATION SELECTOR-117;Mn;0;NSM;;;;;N;;;;; E0165;VARIATION SELECTOR-118;Mn;0;NSM;;;;;N;;;;; E0166;VARIATION SELECTOR-119;Mn;0;NSM;;;;;N;;;;; E0167;VARIATION SELECTOR-120;Mn;0;NSM;;;;;N;;;;; E0168;VARIATION SELECTOR-121;Mn;0;NSM;;;;;N;;;;; E0169;VARIATION SELECTOR-122;Mn;0;NSM;;;;;N;;;;; E016A;VARIATION SELECTOR-123;Mn;0;NSM;;;;;N;;;;; E016B;VARIATION SELECTOR-124;Mn;0;NSM;;;;;N;;;;; E016C;VARIATION SELECTOR-125;Mn;0;NSM;;;;;N;;;;; E016D;VARIATION SELECTOR-126;Mn;0;NSM;;;;;N;;;;; E016E;VARIATION SELECTOR-127;Mn;0;NSM;;;;;N;;;;; E016F;VARIATION SELECTOR-128;Mn;0;NSM;;;;;N;;;;; E0170;VARIATION SELECTOR-129;Mn;0;NSM;;;;;N;;;;; E0171;VARIATION SELECTOR-130;Mn;0;NSM;;;;;N;;;;; E0172;VARIATION SELECTOR-131;Mn;0;NSM;;;;;N;;;;; E0173;VARIATION SELECTOR-132;Mn;0;NSM;;;;;N;;;;; E0174;VARIATION SELECTOR-133;Mn;0;NSM;;;;;N;;;;; E0175;VARIATION SELECTOR-134;Mn;0;NSM;;;;;N;;;;; E0176;VARIATION SELECTOR-135;Mn;0;NSM;;;;;N;;;;; E0177;VARIATION SELECTOR-136;Mn;0;NSM;;;;;N;;;;; E0178;VARIATION SELECTOR-137;Mn;0;NSM;;;;;N;;;;; E0179;VARIATION SELECTOR-138;Mn;0;NSM;;;;;N;;;;; E017A;VARIATION SELECTOR-139;Mn;0;NSM;;;;;N;;;;; E017B;VARIATION SELECTOR-140;Mn;0;NSM;;;;;N;;;;; E017C;VARIATION SELECTOR-141;Mn;0;NSM;;;;;N;;;;; E017D;VARIATION SELECTOR-142;Mn;0;NSM;;;;;N;;;;; E017E;VARIATION SELECTOR-143;Mn;0;NSM;;;;;N;;;;; E017F;VARIATION SELECTOR-144;Mn;0;NSM;;;;;N;;;;; E0180;VARIATION SELECTOR-145;Mn;0;NSM;;;;;N;;;;; E0181;VARIATION SELECTOR-146;Mn;0;NSM;;;;;N;;;;; E0182;VARIATION SELECTOR-147;Mn;0;NSM;;;;;N;;;;; E0183;VARIATION SELECTOR-148;Mn;0;NSM;;;;;N;;;;; E0184;VARIATION SELECTOR-149;Mn;0;NSM;;;;;N;;;;; E0185;VARIATION SELECTOR-150;Mn;0;NSM;;;;;N;;;;; E0186;VARIATION SELECTOR-151;Mn;0;NSM;;;;;N;;;;; E0187;VARIATION SELECTOR-152;Mn;0;NSM;;;;;N;;;;; E0188;VARIATION SELECTOR-153;Mn;0;NSM;;;;;N;;;;; E0189;VARIATION SELECTOR-154;Mn;0;NSM;;;;;N;;;;; E018A;VARIATION SELECTOR-155;Mn;0;NSM;;;;;N;;;;; E018B;VARIATION SELECTOR-156;Mn;0;NSM;;;;;N;;;;; E018C;VARIATION SELECTOR-157;Mn;0;NSM;;;;;N;;;;; E018D;VARIATION SELECTOR-158;Mn;0;NSM;;;;;N;;;;; E018E;VARIATION SELECTOR-159;Mn;0;NSM;;;;;N;;;;; E018F;VARIATION SELECTOR-160;Mn;0;NSM;;;;;N;;;;; E0190;VARIATION SELECTOR-161;Mn;0;NSM;;;;;N;;;;; E0191;VARIATION SELECTOR-162;Mn;0;NSM;;;;;N;;;;; E0192;VARIATION SELECTOR-163;Mn;0;NSM;;;;;N;;;;; E0193;VARIATION SELECTOR-164;Mn;0;NSM;;;;;N;;;;; E0194;VARIATION SELECTOR-165;Mn;0;NSM;;;;;N;;;;; E0195;VARIATION SELECTOR-166;Mn;0;NSM;;;;;N;;;;; E0196;VARIATION SELECTOR-167;Mn;0;NSM;;;;;N;;;;; E0197;VARIATION SELECTOR-168;Mn;0;NSM;;;;;N;;;;; E0198;VARIATION SELECTOR-169;Mn;0;NSM;;;;;N;;;;; E0199;VARIATION SELECTOR-170;Mn;0;NSM;;;;;N;;;;; E019A;VARIATION SELECTOR-171;Mn;0;NSM;;;;;N;;;;; E019B;VARIATION SELECTOR-172;Mn;0;NSM;;;;;N;;;;; E019C;VARIATION SELECTOR-173;Mn;0;NSM;;;;;N;;;;; E019D;VARIATION SELECTOR-174;Mn;0;NSM;;;;;N;;;;; E019E;VARIATION SELECTOR-175;Mn;0;NSM;;;;;N;;;;; E019F;VARIATION SELECTOR-176;Mn;0;NSM;;;;;N;;;;; E01A0;VARIATION SELECTOR-177;Mn;0;NSM;;;;;N;;;;; E01A1;VARIATION SELECTOR-178;Mn;0;NSM;;;;;N;;;;; E01A2;VARIATION SELECTOR-179;Mn;0;NSM;;;;;N;;;;; E01A3;VARIATION SELECTOR-180;Mn;0;NSM;;;;;N;;;;; E01A4;VARIATION SELECTOR-181;Mn;0;NSM;;;;;N;;;;; E01A5;VARIATION SELECTOR-182;Mn;0;NSM;;;;;N;;;;; E01A6;VARIATION SELECTOR-183;Mn;0;NSM;;;;;N;;;;; E01A7;VARIATION SELECTOR-184;Mn;0;NSM;;;;;N;;;;; E01A8;VARIATION SELECTOR-185;Mn;0;NSM;;;;;N;;;;; E01A9;VARIATION SELECTOR-186;Mn;0;NSM;;;;;N;;;;; E01AA;VARIATION SELECTOR-187;Mn;0;NSM;;;;;N;;;;; E01AB;VARIATION SELECTOR-188;Mn;0;NSM;;;;;N;;;;; E01AC;VARIATION SELECTOR-189;Mn;0;NSM;;;;;N;;;;; E01AD;VARIATION SELECTOR-190;Mn;0;NSM;;;;;N;;;;; E01AE;VARIATION SELECTOR-191;Mn;0;NSM;;;;;N;;;;; E01AF;VARIATION SELECTOR-192;Mn;0;NSM;;;;;N;;;;; E01B0;VARIATION SELECTOR-193;Mn;0;NSM;;;;;N;;;;; E01B1;VARIATION SELECTOR-194;Mn;0;NSM;;;;;N;;;;; E01B2;VARIATION SELECTOR-195;Mn;0;NSM;;;;;N;;;;; E01B3;VARIATION SELECTOR-196;Mn;0;NSM;;;;;N;;;;; E01B4;VARIATION SELECTOR-197;Mn;0;NSM;;;;;N;;;;; E01B5;VARIATION SELECTOR-198;Mn;0;NSM;;;;;N;;;;; E01B6;VARIATION SELECTOR-199;Mn;0;NSM;;;;;N;;;;; E01B7;VARIATION SELECTOR-200;Mn;0;NSM;;;;;N;;;;; E01B8;VARIATION SELECTOR-201;Mn;0;NSM;;;;;N;;;;; E01B9;VARIATION SELECTOR-202;Mn;0;NSM;;;;;N;;;;; E01BA;VARIATION SELECTOR-203;Mn;0;NSM;;;;;N;;;;; E01BB;VARIATION SELECTOR-204;Mn;0;NSM;;;;;N;;;;; E01BC;VARIATION SELECTOR-205;Mn;0;NSM;;;;;N;;;;; E01BD;VARIATION SELECTOR-206;Mn;0;NSM;;;;;N;;;;; E01BE;VARIATION SELECTOR-207;Mn;0;NSM;;;;;N;;;;; E01BF;VARIATION SELECTOR-208;Mn;0;NSM;;;;;N;;;;; E01C0;VARIATION SELECTOR-209;Mn;0;NSM;;;;;N;;;;; E01C1;VARIATION SELECTOR-210;Mn;0;NSM;;;;;N;;;;; E01C2;VARIATION SELECTOR-211;Mn;0;NSM;;;;;N;;;;; E01C3;VARIATION SELECTOR-212;Mn;0;NSM;;;;;N;;;;; E01C4;VARIATION SELECTOR-213;Mn;0;NSM;;;;;N;;;;; E01C5;VARIATION SELECTOR-214;Mn;0;NSM;;;;;N;;;;; E01C6;VARIATION SELECTOR-215;Mn;0;NSM;;;;;N;;;;; E01C7;VARIATION SELECTOR-216;Mn;0;NSM;;;;;N;;;;; E01C8;VARIATION SELECTOR-217;Mn;0;NSM;;;;;N;;;;; E01C9;VARIATION SELECTOR-218;Mn;0;NSM;;;;;N;;;;; E01CA;VARIATION SELECTOR-219;Mn;0;NSM;;;;;N;;;;; E01CB;VARIATION SELECTOR-220;Mn;0;NSM;;;;;N;;;;; E01CC;VARIATION SELECTOR-221;Mn;0;NSM;;;;;N;;;;; E01CD;VARIATION SELECTOR-222;Mn;0;NSM;;;;;N;;;;; E01CE;VARIATION SELECTOR-223;Mn;0;NSM;;;;;N;;;;; E01CF;VARIATION SELECTOR-224;Mn;0;NSM;;;;;N;;;;; E01D0;VARIATION SELECTOR-225;Mn;0;NSM;;;;;N;;;;; E01D1;VARIATION SELECTOR-226;Mn;0;NSM;;;;;N;;;;; E01D2;VARIATION SELECTOR-227;Mn;0;NSM;;;;;N;;;;; E01D3;VARIATION SELECTOR-228;Mn;0;NSM;;;;;N;;;;; E01D4;VARIATION SELECTOR-229;Mn;0;NSM;;;;;N;;;;; E01D5;VARIATION SELECTOR-230;Mn;0;NSM;;;;;N;;;;; E01D6;VARIATION SELECTOR-231;Mn;0;NSM;;;;;N;;;;; E01D7;VARIATION SELECTOR-232;Mn;0;NSM;;;;;N;;;;; E01D8;VARIATION SELECTOR-233;Mn;0;NSM;;;;;N;;;;; E01D9;VARIATION SELECTOR-234;Mn;0;NSM;;;;;N;;;;; E01DA;VARIATION SELECTOR-235;Mn;0;NSM;;;;;N;;;;; E01DB;VARIATION SELECTOR-236;Mn;0;NSM;;;;;N;;;;; E01DC;VARIATION SELECTOR-237;Mn;0;NSM;;;;;N;;;;; E01DD;VARIATION SELECTOR-238;Mn;0;NSM;;;;;N;;;;; E01DE;VARIATION SELECTOR-239;Mn;0;NSM;;;;;N;;;;; E01DF;VARIATION SELECTOR-240;Mn;0;NSM;;;;;N;;;;; E01E0;VARIATION SELECTOR-241;Mn;0;NSM;;;;;N;;;;; E01E1;VARIATION SELECTOR-242;Mn;0;NSM;;;;;N;;;;; E01E2;VARIATION SELECTOR-243;Mn;0;NSM;;;;;N;;;;; E01E3;VARIATION SELECTOR-244;Mn;0;NSM;;;;;N;;;;; E01E4;VARIATION SELECTOR-245;Mn;0;NSM;;;;;N;;;;; E01E5;VARIATION SELECTOR-246;Mn;0;NSM;;;;;N;;;;; E01E6;VARIATION SELECTOR-247;Mn;0;NSM;;;;;N;;;;; E01E7;VARIATION SELECTOR-248;Mn;0;NSM;;;;;N;;;;; E01E8;VARIATION SELECTOR-249;Mn;0;NSM;;;;;N;;;;; E01E9;VARIATION SELECTOR-250;Mn;0;NSM;;;;;N;;;;; E01EA;VARIATION SELECTOR-251;Mn;0;NSM;;;;;N;;;;; E01EB;VARIATION SELECTOR-252;Mn;0;NSM;;;;;N;;;;; E01EC;VARIATION SELECTOR-253;Mn;0;NSM;;;;;N;;;;; E01ED;VARIATION SELECTOR-254;Mn;0;NSM;;;;;N;;;;; E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;; E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;; F0000;;Co;0;L;;;;;N;;;;; FFFFD;;Co;0;L;;;;;N;;;;; 100000;;Co;0;L;;;;;N;;;;; 10FFFD;;Co;0;L;;;;;N;;;;; dovecot-2.2.33.2/src/lib/restrict-access.h0000644000175000017500000000516013123174404015132 00000000000000#ifndef RESTRICT_ACCESS_H #define RESTRICT_ACCESS_H struct restrict_access_settings { /* UID to use, or (uid_t)-1 if you don't want to change it */ uid_t uid; /* Effective GID to use, or (gid_t)-1 if you don't want to change it */ gid_t gid; /* If not (gid_t)-1, the privileged GID can be temporarily enabled/disabled. */ gid_t privileged_gid; /* Add access to these space or comma -separated extra groups */ const char *extra_groups; /* Add access to groups this system user belongs to */ const char *system_groups_user; /* All specified GIDs must be in this range. If extra_groups or system group user contains other GIDs, they're silently dropped. */ gid_t first_valid_gid, last_valid_gid; /* Human readable "source" of UID and GID values. If non-NULL, displayed on error messages about failing to change uid/gid. */ const char *uid_source, *gid_source; /* Chroot directory */ const char *chroot_dir; /* Set TRUE to attempt to drop any root privileges FIXME: Reverse logic on v2.3 */ bool drop_setuid_root; }; /* Initialize settings with values that don't change anything. */ void restrict_access_init(struct restrict_access_settings *set); /* Restrict access as specified by the settings. If home is not NULL, it's chdir()ed after chrooting, otherwise it chdirs to / (the chroot). */ void restrict_access(const struct restrict_access_settings *set, const char *home, bool disallow_root) ATTR_NULL(2); /* Set environment variables so they can be read with restrict_access_by_env(). */ void restrict_access_set_env(const struct restrict_access_settings *set); /* Read restrict_access_set_env() environments back into struct. */ void restrict_access_get_env(struct restrict_access_settings *set_r); /* Read restrictions from environment and call restrict_access(). If disallow_roots is TRUE, we'll kill ourself if we didn't have the environment settings. */ void restrict_access_by_env(const char *home, bool disallow_root) ATTR_NULL(1); /* Return the chrooted directory if restrict_access*() chrooted, otherwise NULL. */ const char *restrict_access_get_current_chroot(void); /* Try to set up the process in a way that core dumps are still allowed after calling restrict_access_by_env(). */ void restrict_access_allow_coredumps(bool allow); /* If privileged_gid was set, these functions can be used to temporarily gain access to the group. */ int restrict_access_use_priv_gid(void); void restrict_access_drop_priv_gid(void); /* Returns TRUE if privileged GID exists for this process. */ bool restrict_access_have_priv_gid(void); gid_t *restrict_get_groups_list(unsigned int *gid_count_r); #endif dovecot-2.2.33.2/src/lib/test-imem.c0000644000175000017500000000312313123174404013730 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" struct test_struct { uint32_t num[10]; }; static void test_imem_alloc(void) { struct test_struct ab, bc, cd, de; test_begin("imem allocs"); memset(ab.num, 0xab, sizeof(ab.num)); memset(bc.num, 0xbc, sizeof(bc.num)); memset(cd.num, 0xcd, sizeof(cd.num)); memset(de.num, 0xde, sizeof(de.num)); /* regular alloc */ struct test_struct *s1 = i_new(struct test_struct, 2); struct test_struct *s2 = i_malloc(sizeof(struct test_struct) * 2); s1[0] = ab; s2[0] = ab; s1[1] = bc; s2[1] = bc; test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 2) == 0); /* realloc */ s1 = i_realloc_type(s1, struct test_struct, 2, 4); s2 = i_realloc(s2, sizeof(struct test_struct) * 2, sizeof(struct test_struct) * 4); s1[2] = cd; s2[2] = cd; s1[3] = de; s2[3] = de; test_assert(memcmp(&s1[0], &ab, sizeof(ab)) == 0); test_assert(memcmp(&s1[1], &bc, sizeof(bc)) == 0); test_assert(memcmp(&s1[2], &cd, sizeof(cd)) == 0); test_assert(memcmp(&s1[3], &de, sizeof(de)) == 0); test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 4) == 0); /* freeing realloced memory */ i_free(s1); i_free(s2); test_assert(s1 == NULL); test_assert(s2 == NULL); /* allcating new memory with realloc */ s1 = i_realloc_type(NULL, struct test_struct, 0, 2); s2 = i_realloc(NULL, 0, sizeof(struct test_struct) * 2); s1[0] = ab; s2[0] = ab; s1[1] = bc; s2[1] = bc; test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 2) == 0); i_free(s1); i_free(s2); test_end(); } void test_imem(void) { test_imem_alloc(); } dovecot-2.2.33.2/src/lib/str-sanitize.h0000644000175000017500000000104613123174404014467 00000000000000#ifndef STR_SANITIZE_H #define STR_SANITIZE_H /* All control characters in src will be appended as '?'. If src is longer than max_bytes, it's truncated with "..." appended to the end. Note that src is treated as UTF-8 input, but max_bytes is in bytes instead of UTF-8 characters. */ void str_sanitize_append(string_t *dest, const char *src, size_t max_bytes); /* Return src sanitized. If there are no changes, src pointer is returned. If src is NULL, returns NULL. */ const char *str_sanitize(const char *src, size_t max_bytes); #endif dovecot-2.2.33.2/src/lib/istream-failure-at.h0000644000175000017500000000044013123174404015523 00000000000000#ifndef ISTREAM_FAILURE_AT_H #define ISTREAM_FAILURE_AT_H struct istream * i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, const char *error_string); struct istream * i_stream_create_failure_at_eof(struct istream *input, const char *error_string); #endif dovecot-2.2.33.2/src/lib/child-wait.c0000644000175000017500000000525413165463624014073 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "hash.h" #include "child-wait.h" #include struct child_wait { unsigned int pid_count; child_wait_callback_t *callback; void *context; }; static int child_wait_refcount = 0; /* pid_t => wait */ static HASH_TABLE(void *, struct child_wait *) child_pids; #undef child_wait_new_with_pid struct child_wait * child_wait_new_with_pid(pid_t pid, child_wait_callback_t *callback, void *context) { struct child_wait *wait; wait = i_new(struct child_wait, 1); wait->callback = callback; wait->context = context; if (pid != (pid_t)-1) child_wait_add_pid(wait, pid); return wait; } void child_wait_free(struct child_wait **_wait) { struct child_wait *wait = *_wait; struct hash_iterate_context *iter; void *key; struct child_wait *value; *_wait = NULL; if (wait->pid_count > 0) { /* this should be rare, so iterating hash is fast enough */ iter = hash_table_iterate_init(child_pids); while (hash_table_iterate(iter, child_pids, &key, &value)) { if (value == wait) { hash_table_remove(child_pids, key); if (--wait->pid_count == 0) break; } } hash_table_iterate_deinit(&iter); } i_free(wait); } void child_wait_add_pid(struct child_wait *wait, pid_t pid) { wait->pid_count++; hash_table_insert(child_pids, POINTER_CAST(pid), wait); } void child_wait_remove_pid(struct child_wait *wait, pid_t pid) { wait->pid_count--; hash_table_remove(child_pids, POINTER_CAST(pid)); } static void sigchld_handler(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { struct child_wait_status status; while ((status.pid = waitpid(-1, &status.status, WNOHANG)) > 0) { status.wait = hash_table_lookup(child_pids, POINTER_CAST(status.pid)); if (status.wait != NULL) { child_wait_remove_pid(status.wait, status.pid); status.wait->callback(&status, status.wait->context); } } if (status.pid == -1 && errno != EINTR && errno != ECHILD) i_error("waitpid() failed: %m"); } void child_wait_init(void) { if (child_wait_refcount++ > 0) return; hash_table_create_direct(&child_pids, default_pool, 0); lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE, sigchld_handler, NULL); } void child_wait_deinit(void) { struct hash_iterate_context *iter; void *key; struct child_wait *value; i_assert(child_wait_refcount > 0); if (--child_wait_refcount > 0) return; lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL); iter = hash_table_iterate_init(child_pids); while (hash_table_iterate(iter, child_pids, &key, &value)) i_free(value); hash_table_iterate_deinit(&iter); hash_table_destroy(&child_pids); } dovecot-2.2.33.2/src/lib/ioloop-poll.c0000644000175000017500000001353313123174404014277 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "ioloop-private.h" #ifdef IOLOOP_POLL #include #include struct ioloop_handler_context { unsigned int fds_count, fds_pos; struct pollfd *fds; unsigned int idx_count; int *fd_index; }; void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) { struct ioloop_handler_context *ctx; ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); ctx->fds_count = initial_fd_count; ctx->fds = i_new(struct pollfd, ctx->fds_count); ctx->idx_count = initial_fd_count; ctx->fd_index = i_new(int, ctx->idx_count); memset(ctx->fd_index, 0xff, sizeof(int) * ctx->idx_count); } void io_loop_handler_deinit(struct ioloop *ioloop) { i_free(ioloop->handler_context->fds); i_free(ioloop->handler_context->fd_index); i_free(ioloop->handler_context); } #define IO_POLL_ERROR (POLLERR | POLLHUP | POLLNVAL) #define IO_POLL_INPUT (POLLIN | POLLPRI | IO_POLL_ERROR) #define IO_POLL_OUTPUT (POLLOUT | IO_POLL_ERROR) void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; unsigned int old_count; int index, old_events, fd = io->fd; if ((unsigned int)fd >= ctx->idx_count) { /* grow the fd -> index array */ old_count = ctx->idx_count; ctx->idx_count = nearest_power((unsigned int) fd+1); ctx->fd_index = i_realloc_type(ctx->fd_index, int, old_count, ctx->idx_count); memset(ctx->fd_index + old_count, 0xff, sizeof(int) * (ctx->idx_count-old_count)); } if (ctx->fds_pos >= ctx->fds_count) { /* grow the fd array */ old_count = ctx->fds_count; ctx->fds_count = nearest_power(ctx->fds_count+1); ctx->fds = i_realloc_type(ctx->fds, struct pollfd, old_count, ctx->fds_count); } if (ctx->fd_index[fd] != -1) { /* update existing pollfd */ index = ctx->fd_index[fd]; } else { /* add new pollfd */ index = ctx->fds_pos++; ctx->fd_index[fd] = index; ctx->fds[index].fd = fd; ctx->fds[index].events = 0; ctx->fds[index].revents = 0; } old_events = ctx->fds[index].events; if ((condition & IO_READ) != 0) ctx->fds[index].events |= IO_POLL_INPUT; if ((condition & IO_WRITE) != 0) ctx->fds[index].events |= IO_POLL_OUTPUT; if ((condition & IO_ERROR) != 0) ctx->fds[index].events |= IO_POLL_ERROR; i_assert(ctx->fds[index].events != old_events); } void io_loop_handle_remove(struct io_file *io, bool closed ATTR_UNUSED) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; int index, fd = io->fd; index = ctx->fd_index[fd]; i_assert(index >= 0 && (unsigned int) index < ctx->fds_count); #ifdef DEBUG if (!closed) { /* io_remove() is required to be called before fd is closed. This is required by epoll/kqueue, but since poll is more commonly used while developing, this check here should catch the error early enough not to cause problems for kqueue users. */ if (fcntl(io->fd, F_GETFD, 0) < 0) { if (errno == EBADF) i_panic("io_remove(%d) called too late", io->fd); else i_error("fcntl(%d, F_GETFD) failed: %m", io->fd); } } #endif i_free(io); if ((condition & IO_READ) != 0) { ctx->fds[index].events &= ~(POLLIN|POLLPRI); ctx->fds[index].revents &= ~(POLLIN|POLLPRI); } if ((condition & IO_WRITE) != 0) { ctx->fds[index].events &= ~POLLOUT; ctx->fds[index].revents &= ~POLLOUT; } if ((ctx->fds[index].events & (POLLIN|POLLOUT)) == 0) { /* remove the whole pollfd */ ctx->fd_index[ctx->fds[index].fd] = -1; if (--ctx->fds_pos == (unsigned int) index) return; /* removing last one */ /* move the last pollfd over the removed one */ ctx->fds[index] = ctx->fds[ctx->fds_pos]; ctx->fd_index[ctx->fds[index].fd] = index; } } void io_loop_handler_run_internal(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct pollfd *pollfd; struct timeval tv; struct io_file *io; int msecs, ret; bool call; /* get the time left for next timeout task */ msecs = io_loop_get_wait_time(ioloop, &tv); #ifdef _AIX if (msecs > 1000) { /* AIX seems to check IO_POLL_ERRORs only at the beginning of the poll() call, not during it. keep timeouts short enough so that we'll notice them pretty quickly. */ msecs = 1000; } #endif ret = poll(ctx->fds, ctx->fds_pos, msecs); if (ret < 0 && errno != EINTR) i_fatal("poll(): %m"); /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); if (ret <= 0 || !ioloop->running) { /* no I/O events */ return; } io = ioloop->io_files; for (; io != NULL && ret > 0; io = ioloop->next_io_file) { ioloop->next_io_file = io->next; pollfd = &ctx->fds[ctx->fd_index[io->fd]]; if (pollfd->revents != 0) { if (pollfd->revents & POLLNVAL) { i_error("invalid I/O fd %d, callback %p", io->fd, (void *) io->io.callback); pollfd->events = 0; pollfd->revents = 0; call = TRUE; } else if ((io->io.condition & (IO_READ|IO_WRITE)) == (IO_READ|IO_WRITE)) { call = TRUE; pollfd->revents = 0; } else if ((io->io.condition & IO_READ) != 0) { call = (pollfd->revents & IO_POLL_INPUT) != 0; pollfd->revents &= ~IO_POLL_INPUT; } else if ((io->io.condition & IO_WRITE) != 0) { call = (pollfd->revents & IO_POLL_OUTPUT) != 0; pollfd->revents &= ~IO_POLL_OUTPUT; } else if ((io->io.condition & IO_ERROR) != 0) { call = (pollfd->revents & IO_POLL_ERROR) != 0; pollfd->revents &= ~IO_POLL_ERROR; } else { call = FALSE; } if (pollfd->revents == 0) ret--; if (call) io_loop_call_io(&io->io); } } } #endif dovecot-2.2.33.2/src/lib/md5.c0000644000175000017500000002027213165463624012530 00000000000000/* * This is an OpenSSL-compatible implementation of the RSA Data Security, * Inc. MD5 Message-Digest Algorithm. * * Written by Solar Designer in 2001, and placed in * the public domain. There's absolutely no warranty. * * This differs from Colin Plumb's older public domain implementation in * that no 32-bit integer data type is required, there's no compile-time * endianness configuration, and the function prototypes match OpenSSL's. * The primary goals are portability and ease of use. * * This implementation is meant to be fast, but not as fast as possible. * Some known optimizations are not included to reduce source code size * and avoid compile-time configuration. */ #include "lib.h" #include "safe-memset.h" #include "md5.h" /* * The basic MD5 functions. * * F is optimized compared to its RFC 1321 definition just like in Colin * Plumb's implementation. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); /* * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * * The check for little-endian architectures which tolerate unaligned * memory accesses is just an optimization. Nothing will break if it * doesn't work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #define SET(n) \ (*(const uint32_t *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ (uint_fast32_t)ptr[(n) * 4] | \ ((uint_fast32_t)ptr[(n) * 4 + 1] << 8) | \ ((uint_fast32_t)ptr[(n) * 4 + 2] << 16) | \ ((uint_fast32_t)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There're no alignment requirements. */ static const void * ATTR_NOWARN_UNUSED_RESULT ATTR_UNSIGNED_WRAPS body(struct md5_context *ctx, const void *data, size_t size) { const unsigned char *ptr; uint_fast32_t a, b, c, d; uint_fast32_t saved_a, saved_b, saved_c, saved_d; ptr = data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; /* Round 1 */ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) STEP(F, c, d, a, b, SET(2), 0x242070db, 17) STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) /* Round 2 */ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) STEP(G, d, a, b, c, GET(10), 0x02441453, 9) STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) /* Round 3 */ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) /* Round 4 */ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return ptr; } void md5_init(struct md5_context *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->lo = 0; ctx->hi = 0; memset(ctx->block, 0, sizeof(ctx->block)); } void ATTR_UNSIGNED_WRAPS md5_update(struct md5_context *ctx, const void *data, size_t size) { /* @UNSAFE */ uint_fast32_t saved_lo; unsigned long used, free; saved_lo = ctx->lo; if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) ctx->hi++; ctx->hi += size >> 29; used = saved_lo & 0x3f; if (used) { free = 64 - used; if (size < free) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, free); data = (const unsigned char *) data + free; size -= free; body(ctx, ctx->buffer, 64); } if (size >= 64) { data = body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } void ATTR_UNSIGNED_WRAPS md5_final(struct md5_context *ctx, unsigned char result[STATIC_ARRAY MD5_RESULTLEN]) { /* @UNSAFE */ unsigned long used, free; used = ctx->lo & 0x3f; ctx->buffer[used++] = 0x80; free = 64 - used; if (free < 8) { memset(&ctx->buffer[used], 0, free); body(ctx, ctx->buffer, 64); used = 0; free = 64; } memset(&ctx->buffer[used], 0, free - 8); ctx->lo <<= 3; ctx->buffer[56] = ctx->lo; ctx->buffer[57] = ctx->lo >> 8; ctx->buffer[58] = ctx->lo >> 16; ctx->buffer[59] = ctx->lo >> 24; ctx->buffer[60] = ctx->hi; ctx->buffer[61] = ctx->hi >> 8; ctx->buffer[62] = ctx->hi >> 16; ctx->buffer[63] = ctx->hi >> 24; body(ctx, ctx->buffer, 64); result[0] = ctx->a; result[1] = ctx->a >> 8; result[2] = ctx->a >> 16; result[3] = ctx->a >> 24; result[4] = ctx->b; result[5] = ctx->b >> 8; result[6] = ctx->b >> 16; result[7] = ctx->b >> 24; result[8] = ctx->c; result[9] = ctx->c >> 8; result[10] = ctx->c >> 16; result[11] = ctx->c >> 24; result[12] = ctx->d; result[13] = ctx->d >> 8; result[14] = ctx->d >> 16; result[15] = ctx->d >> 24; i_zero_safe(ctx); } void md5_get_digest(const void *data, size_t size, unsigned char result[STATIC_ARRAY MD5_RESULTLEN]) { struct md5_context ctx; md5_init(&ctx); md5_update(&ctx, data, size); md5_final(&ctx, result); } static void hash_method_init_md5(void *context) { md5_init(context); } static void hash_method_loop_md5(void *context, const void *data, size_t size) { md5_update(context, data, size); } static void hash_method_result_md5(void *context, unsigned char *result_r) { md5_final(context, result_r); } const struct hash_method hash_method_md5 = { "md5", sizeof(struct md5_context), MD5_RESULTLEN, hash_method_init_md5, hash_method_loop_md5, hash_method_result_md5 }; dovecot-2.2.33.2/src/lib/istream-rawlog.h0000644000175000017500000000055113165463624015003 00000000000000#ifndef ISTREAM_RAWLOG_H #define ISTREAM_RAWLOG_H struct istream * i_stream_create_rawlog(struct istream *input, const char *rawlog_path, int rawlog_fd, enum iostream_rawlog_flags flags); struct istream * i_stream_create_rawlog_from_stream(struct istream *input, struct ostream *rawlog_output, enum iostream_rawlog_flags flags); #endif dovecot-2.2.33.2/src/lib/data-stack.h0000644000175000017500000001152513165463624014065 00000000000000#ifndef DATA_STACK_H #define DATA_STACK_H /* Data stack makes it very easy to implement functions returning dynamic data without having to worry much about memory management like freeing the result or having large enough buffers for the result. t_ prefix was chosen to describe functions allocating memory from data stack. "t" meaning temporary. Advantages over control stack and alloca(): - Functions can return a value allocated from data stack - We can portably specify how much data we want to allocate at runtime Advantages over malloc(): - FAST, most of the time allocating memory means only updating a couple of pointers and integers. Freeing the memory all at once also is a fast operation. - No need to free() each allocation resulting in prettier code - No memory leaks - No memory fragmentation Disadvantages: - Allocating memory inside loops can accidentally allocate a lot of memory if the loops are long and you forgot to place t_push() and t_pop() there. - t_malloc()ed data could be accidentally stored into permanent location and accessed after it's already been freed. const'ing the return values helps for most uses though (see the t_malloc() description). - Debugging invalid memory usage may be difficult using existing tools, although compiling with DEBUG enabled helps finding simple buffer overflows. */ extern unsigned int data_stack_frame; /* All t_..() allocations between t_push*() and t_pop() are freed after t_pop() is called. Returns the current stack frame number, which can be used to detect missing t_pop() calls: x = t_push(__func__); .. if (t_pop() != x) abort(); In DEBUG mode, t_push_named() makes a temporary allocation for the name, but is safe to call in a loop as it performs the allocation within its own frame. However, you should always prefer to use T_BEGIN { ... } T_END below. */ unsigned int t_push(const char *marker) ATTR_HOT; unsigned int t_push_named(const char *format, ...) ATTR_HOT ATTR_FORMAT(1, 2); unsigned int t_pop(void) ATTR_HOT; /* Simplifies the if (t_pop() != x) check by comparing it internally and panicking if it doesn't match. */ void t_pop_check(unsigned int *id) ATTR_HOT; /* Usage: T_BEGIN { code } T_END */ #ifndef DEBUG #define T_BEGIN \ STMT_START { unsigned int _data_stack_cur_id = t_push(NULL); #elif defined (__GNUC__) && !defined (__STRICT_ANSI__) #define T_BEGIN \ STMT_START { unsigned int _data_stack_cur_id = t_push(__FUNCTION__); #else #define T_CAT2(a,b) (a ":" #b) #define T_BEGIN \ STMT_START { unsigned int _data_stack_cur_id = t_push(T_CAT2(__FILE__,__LINE__)); #endif #define T_END \ t_pop_check(&_data_stack_cur_id); } STMT_END /* WARNING: Be careful when using these functions, it's too easy to accidentally save the returned value somewhere permanently. You probably should never use these functions directly, rather create functions that return 'const xxx*' types and use t_malloc() internally in them. This is a lot safer, since usually compiler warns if you try to place them in xxx*. See strfuncs.c for examples. t_malloc() calls never fail. If there's not enough memory left, i_panic() will be called. */ void *t_malloc(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; void *t_malloc0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; /* Try growing allocated memory. Returns TRUE if successful. Works only for last allocated memory in current stack frame. */ bool t_try_realloc(void *mem, size_t size); /* Returns the number of bytes available in data stack without allocating more memory. */ size_t t_get_bytes_available(void) ATTR_PURE; #define t_new(type, count) \ ((type *) t_malloc0(MALLOC_MULTIPLY((unsigned int)sizeof(type), (count))) + \ COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) /* Returns pointer to a temporary buffer you can use. The buffer will be invalid as soon as next t_malloc() is called! If you wish to grow the buffer, you must give the full wanted size in the size parameter. If return value doesn't point to the same value as last time, you need to memcpy() data from the old buffer to the new one (or do some other trickery). See t_buffer_reget(). */ void *t_buffer_get(size_t size) ATTR_RETURNS_NONNULL; /* Grow the buffer, memcpy()ing the memory to new location if needed. */ void *t_buffer_reget(void *buffer, size_t size) ATTR_RETURNS_NONNULL; /* Make the last t_buffer_get()ed buffer permanent. Note that size MUST be less or equal than the size you gave with last t_buffer_get() or the result will be undefined. */ void t_buffer_alloc(size_t size); /* Allocate the last t_buffer_get()ed data entirely. */ void t_buffer_alloc_last_full(void); /* If enabled, all the used memory is cleared after t_pop(). */ void data_stack_set_clean_after_pop(bool enable); void data_stack_init(void); void data_stack_deinit(void); #endif dovecot-2.2.33.2/src/lib/net.h0000644000175000017500000001652113165463624012640 00000000000000#ifndef NET_H #define NET_H #ifndef WIN32 # include # include # include # include #endif #ifdef HAVE_SOCKS_H #include #endif #ifndef AF_INET6 # ifdef PF_INET6 # define AF_INET6 PF_INET6 # else # define AF_INET6 10 # endif #endif struct ip_addr { unsigned short family; union { #ifdef HAVE_IPV6 struct in6_addr ip6; #endif struct in_addr ip4; } u; }; ARRAY_DEFINE_TYPE(ip_addr, struct ip_addr); struct net_unix_cred { uid_t uid; gid_t gid; pid_t pid; }; /* maxmimum string length of IP address */ #ifdef HAVE_IPV6 # define MAX_IP_LEN INET6_ADDRSTRLEN #else # define MAX_IP_LEN 20 #endif #ifndef HAVE_IPV6 # undef EAI_NONAME # define EAI_NONAME NO_ADDRESS # undef EAI_FAIL # define EAI_FAIL NO_RECOVERY #endif #define IPADDR_IS_V4(ip) ((ip)->family == AF_INET) #define IPADDR_IS_V6(ip) ((ip)->family == AF_INET6) #define IPADDR_BITS(ip) (IPADDR_IS_V4(ip) ? 32 : 128) enum net_listen_flags { /* Try to use SO_REUSEPORT if available. If it's not, this flag is cleared on return. */ NET_LISTEN_FLAG_REUSEPORT = 0x01 }; /* Returns TRUE if IPs are the same */ bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2); /* Returns 0 if IPs are the same, -1 or 1 otherwise. */ int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2); unsigned int net_ip_hash(const struct ip_addr *ip); /* Connect to TCP socket with ip address. The socket and connect() is non-blocking. */ int net_connect_ip(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip) ATTR_NULL(3); /* Like net_connect_ip(), but do a blocking connect(). */ int net_connect_ip_blocking(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip) ATTR_NULL(3); /* Like net_connect_ip(), but open a UDP socket. */ int net_connect_udp(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip); /* Returns 0 if we can bind() as given IP, -1 if not. */ int net_try_bind(const struct ip_addr *ip); /* Connect to named UNIX socket */ int net_connect_unix(const char *path); /* Try to connect to UNIX socket for give number of seconds when connect() returns EAGAIN or ECONNREFUSED. */ int net_connect_unix_with_retries(const char *path, unsigned int msecs); /* Disconnect socket */ void net_disconnect(int fd); /* Set socket blocking/nonblocking */ void net_set_nonblock(int fd, bool nonblock); /* Set TCP_CORK if supported, ie. don't send out partial frames. Returns 0 if ok, -1 if failed. */ int net_set_cork(int fd, bool cork) ATTR_NOWARN_UNUSED_RESULT; /* Set TCP_NODELAY, which disables the Nagle algorithm. */ int net_set_tcp_nodelay(int fd, bool nodelay); /* Set socket kernel buffer sizes */ int net_set_send_buffer_size(int fd, size_t size); int net_set_recv_buffer_size(int fd, size_t size); /* Set IP to contain INADDR_ANY for IPv4 or IPv6. The IPv6 any address may include IPv4 depending on the system (Linux yes, BSD no). */ void net_get_ip_any4(struct ip_addr *ip); void net_get_ip_any6(struct ip_addr *ip); /* Listen for connections on a socket */ int net_listen(const struct ip_addr *my_ip, in_port_t *port, int backlog); int net_listen_full(const struct ip_addr *my_ip, in_port_t *port, enum net_listen_flags *flags, int backlog); /* Listen for connections on an UNIX socket */ int net_listen_unix(const char *path, int backlog); /* Like net_listen_unix(), but if socket already exists, try to connect to it. If it fails with ECONNREFUSED, unlink the socket and try creating it again. */ int net_listen_unix_unlink_stale(const char *path, int backlog); /* Accept a connection on a socket. Returns -1 if the connection got closed, -2 for other failures. For UNIX sockets addr_r->family=port=0. */ int net_accept(int fd, struct ip_addr *addr_r, in_port_t *port_r) ATTR_NULL(2, 3); /* Read data from socket, return number of bytes read, -1 = error, -2 = disconnected */ ssize_t net_receive(int fd, void *buf, size_t len); /* Transmit data, return number of bytes sent, -1 = error, -2 = disconnected */ ssize_t net_transmit(int fd, const void *data, size_t len); /* Get IP addresses for host. ips contains ips_count of IPs, they don't need to be free'd. Returns 0 = ok, others = error code for net_gethosterror() */ int net_gethostbyname(const char *addr, struct ip_addr **ips, unsigned int *ips_count); /* Return host for the IP address. Returns 0 = ok, others = error code for net_gethosterror(). */ int net_gethostbyaddr(const struct ip_addr *ip, const char **name_r); /* get error of net_gethostname() */ const char *net_gethosterror(int error) ATTR_CONST; /* return TRUE if host lookup failed because it didn't exist (ie. not some error with name server) */ int net_hosterror_notfound(int error) ATTR_CONST; /* Get socket local address/port. For UNIX sockets addr->family=port=0. */ int net_getsockname(int fd, struct ip_addr *addr, in_port_t *port) ATTR_NULL(2, 3); /* Get socket remote address/port. For UNIX sockets addr->family=port=0. */ int net_getpeername(int fd, struct ip_addr *addr, in_port_t *port) ATTR_NULL(2, 3); /* Get UNIX socket name. */ int net_getunixname(int fd, const char **name_r); /* Get UNIX socket peer process's credentials. The pid may be (pid_t)-1 if unavailable. */ int net_getunixcred(int fd, struct net_unix_cred *cred_r); /* Returns ip_addr as string, or "" if ip isn't valid IPv4 or IPv6 address. */ const char *net_ip2addr(const struct ip_addr *ip); /* char* -> struct ip_addr translation. */ int net_addr2ip(const char *addr, struct ip_addr *ip); /* char* -> in_port_t translation */ int net_str2port(const char *str, in_port_t *port_r); /* char* -> in_port_t translation (allows port zero) */ int net_str2port_zero(const char *str, in_port_t *port_r); /* Parse "host", "host:port", "IPv4", "IPv4:port", "IPv6", "[IPv6]" or "[IPv6]:port" to its host and port components. [IPv6] address is returned without []. If no port is given, return default_port. The :port in the parsed string isn't allowed to be zero, but default_port=0 is passed through. */ int net_str2hostport(const char *str, in_port_t default_port, const char **host_r, in_port_t *port_r); /* Converts ip and port to ipv4:port or [ipv6]:port. Returns -1 if ip is not valid IPv4 or IPv6 address. */ int net_ipport2str(const struct ip_addr *ip, in_port_t port, const char **str_r); /* Convert IPv6 mapped IPv4 address to an actual IPv4 address. Returns 0 if successful, -1 if the source address isn't IPv6 mapped IPv4 address. */ int net_ipv6_mapped_ipv4_convert(const struct ip_addr *src, struct ip_addr *dest); /* Get socket error */ int net_geterror(int fd); /* Get name of TCP service */ const char *net_getservbyport(in_port_t port) ATTR_CONST; bool is_ipv4_address(const char *addr) ATTR_PURE; bool is_ipv6_address(const char *addr) ATTR_PURE; /* Parse network as ip/bits. Returns 0 if successful, -1 if invalid input. */ int net_parse_range(const char *network, struct ip_addr *ip_r, unsigned int *bits_r); /* Returns TRUE if ip is in net_ip/bits network. IPv4-mapped IPv6 addresses in "ip" parameter are converted to plain IPv4 addresses before matching. No conversion is done to net_ip though, so using IPv4-mapped IPv6 addresses there will always fail. Invalid IPs (family=0) never match anything. */ bool net_is_in_network(const struct ip_addr *ip, const struct ip_addr *net_ip, unsigned int bits) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib/sha-common.h0000644000175000017500000000025413123174404014074 00000000000000#ifndef SHA_COMMON #define SHA256_RESULTLEN (256 / 8) #define SHA256_BLOCK_SIZE (512 / 8) #define SHA512_RESULTLEN (512 / 8) #define SHA512_BLOCK_SIZE (1024 / 8) #endif dovecot-2.2.33.2/src/lib/test-mempool-alloconly.c0000644000175000017500000000411413123174404016444 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" static bool mem_has_bytes(const void *mem, size_t size, uint8_t b) { const uint8_t *bytes = mem; unsigned int i; for (i = 0; i < size; i++) { if (bytes[i] != b) return FALSE; } return TRUE; } void test_mempool_alloconly(void) { #define SENTRY_SIZE 32 #define SENTRY_CHAR 0xDE #define PMALLOC_MAX_COUNT 128 pool_t pool; unsigned int i, j, k; void *mem[PMALLOC_MAX_COUNT + 1]; char *sentry; test_begin("mempool_alloconly"); for (i = 0; i < 64; i++) { for (j = 1; j <= 128; j++) { pool = pool_alloconly_create(MEMPOOL_GROWING"test", i); /* make sure p_malloc() doesn't overwrite unallocated data in data stack. parts of the code relies on this. */ sentry = t_buffer_get(SENTRY_SIZE); memset(sentry, SENTRY_CHAR, SENTRY_SIZE); mem[0] = p_malloc(pool, j); memset(mem[0], j, j); for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { mem[k] = p_malloc(pool, k); memset(mem[k], k, k); } test_assert(mem_has_bytes(sentry, SENTRY_SIZE, SENTRY_CHAR)); test_assert(t_buffer_get(SENTRY_SIZE) == sentry); test_assert(mem_has_bytes(mem[0], j, j)); for (k = 1; k <= PMALLOC_MAX_COUNT; k++) test_assert(mem_has_bytes(mem[k], k, k)); pool_unref(&pool); } } test_end(); } enum fatal_test_state fatal_mempool_alloconly(unsigned int stage) { static pool_t pool; switch(stage) { case 0: /* forbidden size */ test_begin("fatal_mempool_alloconly"); pool = pool_alloconly_create(MEMPOOL_GROWING"fatal", 1); (void)p_malloc(pool, 0); return FATAL_TEST_FAILURE; case 1: /* logically impossible size */ (void)p_malloc(pool, SSIZE_T_MAX + 1ULL); return FATAL_TEST_FAILURE; case 2: /* physically impossible size */ (void)p_malloc(pool, SSIZE_T_MAX - (size_t)MEM_ALIGN(1)); return FATAL_TEST_FAILURE; /* Continue with other tests as follows: case 3: something_fatal(); return FATAL_TEST_FAILURE; */ } /* Either our tests have finished, or the test suite has got confused. */ pool_unref(&pool); test_end(); return FATAL_TEST_FINISHED; } dovecot-2.2.33.2/src/lib/buffer.h0000644000175000017500000001355013165463624013322 00000000000000#ifndef BUFFER_H #define BUFFER_H struct buffer { const void *data; const size_t used; void *priv[5]; }; /* WARNING: Be careful with functions that return pointers to data. With dynamic buffers they are valid only as long as buffer is not realloc()ed. You shouldn't rely on it being valid if you have modified buffer in any way. */ /* Create a modifiable buffer from given data. Writes past this size will i_panic(). */ void buffer_create_from_data(buffer_t *buffer, void *data, size_t size); /* Create a non-modifiable buffer from given data. */ void buffer_create_from_const_data(buffer_t *buffer, const void *data, size_t size); #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 401 #define buffer_create_from_data(b,d,s) ({ \ (void)COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)); \ buffer_create_from_data((b), (d), (s)); }) #define buffer_create_from_const_data(b,d,s) ({ \ (void)COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)); \ buffer_create_from_const_data((b), (d), (s)); }) #endif /* Creates a dynamically growing buffer. Whenever write would exceed the current size it's grown. */ buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size); /* Free the memory used by buffer. Not needed if the memory is free'd directly from the memory pool. */ void buffer_free(buffer_t **buf); /* Free the memory used by buffer structure, but return the buffer data unfree'd. */ void *buffer_free_without_data(buffer_t **buf); /* Returns the pool buffer was created with. */ pool_t buffer_get_pool(const buffer_t *buf) ATTR_PURE; /* Reset the buffer. used size and it's contents are zeroed. */ void buffer_reset(buffer_t *buf); /* Write data to buffer at specified position. If pos is beyond the buffer's current size, it is zero-filled up to that point (even if data_size==0). */ void buffer_write(buffer_t *buf, size_t pos, const void *data, size_t data_size); /* Append data to buffer. */ void buffer_append(buffer_t *buf, const void *data, size_t data_size); /* Append character to buffer. */ void buffer_append_c(buffer_t *buf, unsigned char chr); /* Insert data to buffer. */ void buffer_insert(buffer_t *buf, size_t pos, const void *data, size_t data_size); /* Delete data from buffer. */ void buffer_delete(buffer_t *buf, size_t pos, size_t size); /* Fill buffer with zero bytes. */ void buffer_write_zero(buffer_t *buf, size_t pos, size_t data_size); void buffer_append_zero(buffer_t *buf, size_t data_size); void buffer_insert_zero(buffer_t *buf, size_t pos, size_t data_size); /* Copy data from buffer to another. The buffers may be same in which case it's internal copying, possibly with overlapping positions (ie. memmove() like functionality). copy_size may be set to (size_t)-1 to copy the rest of the used data in buffer. */ void buffer_copy(buffer_t *dest, size_t dest_pos, const buffer_t *src, size_t src_pos, size_t copy_size); /* Append data to buffer from another. copy_size may be set to (size_t)-1 to copy the rest of the used data in buffer. */ void buffer_append_buf(buffer_t *dest, const buffer_t *src, size_t src_pos, size_t copy_size); /* Returns pointer to specified position in buffer. WARNING: The returned address may become invalid if you add more data to buffer. */ void *buffer_get_space_unsafe(buffer_t *buf, size_t pos, size_t size); /* Increase the buffer usage by given size, and return a pointer to beginning of it. */ void *buffer_append_space_unsafe(buffer_t *buf, size_t size); /* Like buffer_get_data(), but don't return it as const. Returns NULL if the buffer is non-modifiable. WARNING: The returned address may become invalid if you add more data to buffer. */ void *buffer_get_modifiable_data(const buffer_t *buf, size_t *used_size_r) ATTR_NULL(2); /* Set the "used size" of buffer, ie. 0 would set the buffer empty. Must not be used to grow buffer. The data after the buffer's new size will be effectively lost, because e.g. buffer_get_space_unsafe() will zero out the contents. */ void buffer_set_used_size(buffer_t *buf, size_t used_size); /* Returns the current buffer size. */ size_t buffer_get_size(const buffer_t *buf) ATTR_PURE; /* Returns how many bytes we can write to buffer without increasing its size. With dynamic buffers this is buffer_get_size()-1, because the extra 1 byte is reserved for str_c()'s NUL. */ size_t buffer_get_writable_size(const buffer_t *buf) ATTR_PURE; /* Returns TRUE if buffer contents are identical. */ bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2); /* Returns pointer to beginning of buffer data. Current used size of buffer is stored in used_size if it's non-NULL. */ static inline const void * ATTR_NULL(2) buffer_get_data(const buffer_t *buf, size_t *used_size_r) { if (used_size_r != NULL) *used_size_r = buf->used; return buf->data; } /* Returns the current used buffer size. */ static inline size_t ATTR_PURE buffer_get_used_size(const buffer_t *buf) { return buf->used; } /* Crash if buffer was allocated from data stack and stack frame has changed. This can be used as an assert-like check to verify that it's valid to increase the buffer size here, instead of crashing only randomly when the buffer needs to be increased. */ void buffer_verify_pool(buffer_t *buf); /* This will truncate your byte buffer to contain at most given number of bits. 1 bits: 01 00000001 2 bits: 03 00000011 3 bits: 07 00000111 4 bits: 0f 00001111 5 bits: 1f 00011111 6 bits: 3f 00111111 7 bits: 7f 01111111 8 bits: ff 11111111 9 bits: 01ff 0000000111111111 10 bits: 03ff 0000001111111111 11 bits: 07ff 0000011111111111 12 bits: 0fff 0000111111111111 13 bits: 1fff 0001111111111111 14 bits: 3fff 0011111111111111 15 bits: 7fff 0111111111111111 16 bits: ffff 1111111111111111 and so forth */ void buffer_truncate_rshift_bits(buffer_t *buf, size_t bits); #endif dovecot-2.2.33.2/src/lib/bits.c0000644000175000017500000000107613165463624013005 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" size_t nearest_power(size_t num) { size_t n = 1; i_assert(num <= ((size_t)1 << (CHAR_BIT*sizeof(size_t) - 1))); while (n < num) n <<= 1; return n; } #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) /* Lucky you, it's all inline intrinsics */ #else unsigned int bits_required8(uint8_t num) { int ret = 0; if (num > 0xf) { ret += 4; num >>= 4; } if (num > 0x3) { ret += 2; num >>= 2; } num &= ~(num>>1); /* 3->2, else unchanged */ return ret + num; } #endif dovecot-2.2.33.2/src/lib/istream-chain.h0000644000175000017500000000154413123174404014562 00000000000000#ifndef ISTREAM_CHAIN_H #define ISTREAM_CHAIN_H struct istream_chain; /* Flexibly couple input streams into a single chain stream. Input streams can be added after creation of the chain stream, and the chain stream will not signal EOF until all streams are read to EOF and the last stream added was NULL. Streams that were finished to EOF are unreferenced. The chain stream is obviously not seekable and it has no determinable size. The chain_r argument returns a pointer to the chain object. */ struct istream *i_stream_create_chain(struct istream_chain **chain_r); /* Append an input stream to the chain. */ void i_stream_chain_append(struct istream_chain *chain, struct istream *stream); /* Mark the end of the chain. Only then reads from the chain stream can return EOF. */ void i_stream_chain_append_eof(struct istream_chain *chain); #endif dovecot-2.2.33.2/src/lib/backtrace-string.c0000644000175000017500000000325513123174404015255 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "backtrace-string.h" #define MAX_STACK_SIZE 30 #define STACK_SKIP_COUNT 2 #if defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H) /* Linux */ #include int backtrace_append(string_t *str) { void *stack[MAX_STACK_SIZE]; char **strings; int ret, i; ret = backtrace(stack, N_ELEMENTS(stack)); if (ret <= STACK_SKIP_COUNT) return -1; strings = backtrace_symbols(stack, ret); for (i = STACK_SKIP_COUNT; i < ret; i++) { if (i > STACK_SKIP_COUNT) str_append(str, " -> "); if (strings != NULL) str_append(str, strings[i]); else { /* out of memory case */ str_printfa(str, "0x%p", stack[i]); } } free(strings); return 0; } #elif defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H) /* Solaris */ #include struct walk_context { string_t *str; unsigned int pos; }; static int walk_callback(uintptr_t ptr, int signo ATTR_UNUSED, void *context) { struct walk_context *ctx = context; if (ctx->pos >= STACK_SKIP_COUNT) { if (ctx->pos > STACK_SKIP_COUNT) str_append(ctx->str, " -> "); str_printfa(ctx->str, "0x%p", (void *)ptr); } ctx->pos++; return 0; } int backtrace_append(string_t *str) { ucontext_t uc; struct walk_context ctx; if (getcontext(&uc) < 0) return -1; ctx.str = str; ctx.pos = 0; walkcontext(&uc, walk_callback, &ctx); return 0; } #else int backtrace_append(string_t *str ATTR_UNUSED) { return -1; } #endif int backtrace_get(const char **backtrace_r) { string_t *str; str = t_str_new(512); if (backtrace_append(str) < 0) return -1; *backtrace_r = str_c(str); return 0; } dovecot-2.2.33.2/src/lib/fdatasync-path.c0000644000175000017500000000122013165463624014741 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fdatasync-path.h" #include #include int fdatasync_path(const char *path) { int fd, ret = 0; /* Directories need to be opened as read-only. fsync() doesn't appear to care about it. */ fd = open(path, O_RDONLY); if (fd == -1) return -1; if (fdatasync(fd) < 0) { /* Some OSes/FSes don't allow fsyncing directores. Silently ignore the problem. */ if (errno == EBADF) { /* e.g. NetBSD */ } else if (errno == EINVAL) { /* e.g. Linux+CIFS */ } else { ret = -1; } } i_close_fd(&fd); return ret; } dovecot-2.2.33.2/src/lib/utc-mktime.c0000644000175000017500000000345113123174404014107 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "utc-mktime.h" static int tm_cmp(const struct tm *tm1, const struct tm *tm2) { int diff; if ((diff = tm1->tm_year - tm2->tm_year) != 0) return diff; if ((diff = tm1->tm_mon - tm2->tm_mon) != 0) return diff; if ((diff = tm1->tm_mday - tm2->tm_mday) != 0) return diff; if ((diff = tm1->tm_hour - tm2->tm_hour) != 0) return diff; if ((diff = tm1->tm_min - tm2->tm_min) != 0) return diff; return tm1->tm_sec - tm2->tm_sec; } static inline void adjust_leap_second(struct tm *tm) { if (tm->tm_sec == 60) tm->tm_sec = 59; } #ifdef HAVE_TIMEGM /* Normalization done by timegm is considered a failure here, since it means * the timestamp is not valid as-is. Leap second 60 is adjusted to 59 before * this though. */ time_t utc_mktime(const struct tm *tm) { struct tm leap_adj_tm = *tm; adjust_leap_second(&leap_adj_tm); struct tm tmp = leap_adj_tm; time_t t; t = timegm(&tmp); if (tm_cmp(&leap_adj_tm, &tmp) != 0) return (time_t)-1; return t; } #else time_t utc_mktime(const struct tm *tm) { struct tm leap_adj_tm = *tm; adjust_leap_second(&leap_adj_tm); const struct tm *try_tm; time_t t; int bits, dir; /* we'll do a binary search across the entire valid time_t range. when gmtime()'s output matches the tm parameter, we've found the correct time_t value. this also means that if tm contains invalid values, -1 is returned. */ #ifdef TIME_T_SIGNED t = 0; #else t = (time_t)1 << (TIME_T_MAX_BITS - 1); #endif for (bits = TIME_T_MAX_BITS - 2;; bits--) { try_tm = gmtime(&t); dir = tm_cmp(&leap_adj_tm, try_tm); if (dir == 0) return t; if (bits < 0) break; if (dir < 0) t -= (time_t)1 << bits; else t += (time_t)1 << bits; } return (time_t)-1; } #endif dovecot-2.2.33.2/src/lib/pkcs5.c0000644000175000017500000000506113123174404013054 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hash-method.h" #include "hmac.h" #include "pkcs5.h" #include #include static int pkcs5_pbkdf1(const struct hash_method *hash, const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, unsigned int iter, uint32_t length, buffer_t *result) { if (length < 1 || length > hash->digest_size) return -1; if (iter < 1) return -1; unsigned char dk[hash->digest_size]; unsigned char ctx[hash->context_size]; hash->init(ctx); hash->loop(ctx, password, password_len); hash->loop(ctx, salt, salt_len); hash->result(ctx, dk); length--; for(;length>0;length--) { hash->init(ctx); hash->loop(ctx, dk, hash->digest_size); hash->result(ctx, dk); } buffer_append(result, dk, hash->digest_size); return 0; } static int pkcs5_pbkdf2(const struct hash_method *hash, const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, unsigned int iter, uint32_t length, buffer_t *result) { if (length < 1 || iter < 1) return -1; size_t l = (length + hash->digest_size - 1)/hash->digest_size; /* same as ceil(length/hash->digest_size) */ unsigned char dk[l * hash->digest_size]; unsigned char *block; struct hmac_context hctx; unsigned int c,i,t; unsigned char U_c[hash->digest_size]; for(t = 0; t < l; t++) { block = &(dk[t*hash->digest_size]); /* U_1 = PRF(Password, Salt|| INT_BE32(Block_Number)) */ c = htonl(t+1); hmac_init(&hctx, password, password_len, hash); hmac_update(&hctx, salt, salt_len); hmac_update(&hctx, &c, sizeof(c)); hmac_final(&hctx, U_c); /* block = U_1 ^ .. ^ U_iter */ memcpy(block, U_c, hash->digest_size); /* U_c = PRF(Password, U_c-1) */ for(c = 1; c < iter; c++) { hmac_init(&hctx, password, password_len, hash); hmac_update(&hctx, U_c, hash->digest_size); hmac_final(&hctx, U_c); for(i = 0; i < hash->digest_size; i++) block[i] ^= U_c[i]; } } buffer_append(result, dk, length); return 0; } int pkcs5_pbkdf(enum pkcs5_pbkdf_mode mode, const struct hash_method *hash, const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, unsigned int iterations, uint32_t dk_len, buffer_t *result) { if (mode == PKCS5_PBKDF1) return pkcs5_pbkdf1(hash,password,password_len, salt,salt_len,iterations,dk_len,result); else if (mode == PKCS5_PBKDF2) return pkcs5_pbkdf2(hash,password,password_len, salt,salt_len,iterations,dk_len,result); i_unreached(); } dovecot-2.2.33.2/src/lib/istream-callback.h0000644000175000017500000000310613123174404015230 00000000000000#ifndef ISTREAM_CALLBACK_H #define ISTREAM_CALLBACK_H /* istream-callback can be used to implement an istream that returns data by calling the specified callback. The callback needs to do: a) Add data to buffer unless the buffer size is already too large (the callback can decide by itself what is too large). Return TRUE regardless of whether any data was added. b) Return FALSE when it's finished adding data or when it reaches an error. On error i_stream_callback_set_error() must be called before returning. i_stream_add_destroy_callback() can be also added to do any cleanups that the callback may need to do. */ typedef bool istream_callback_read_t(buffer_t *buf, void *context); struct istream * i_stream_create_callback(istream_callback_read_t *callback, void *context); #define i_stream_create_callback(callback, context) \ i_stream_create_callback(1 ? (istream_callback_read_t *)callback : \ CALLBACK_TYPECHECK(callback, bool (*)(buffer_t *buf, typeof(context))), \ context) /* Append data to the istream externally. Typically this is used to add a header to the stream before the callbacks are called. */ void i_stream_callback_append(struct istream *input, const void *data, size_t size); void i_stream_callback_append_str(struct istream *input, const char *str); /* Returns the istream-callback's internal buffer. This buffer can be used to append data to the stream. */ buffer_t *i_stream_callback_get_buffer(struct istream *input); void i_stream_callback_set_error(struct istream *input, int stream_errno, const char *error); #endif dovecot-2.2.33.2/src/lib/mempool.h0000644000175000017500000001156513165463624013525 00000000000000#ifndef MEMPOOL_H #define MEMPOOL_H #include "macros.h" /* When DEBUG is enabled, Dovecot warns whenever a memory pool is grown. This is done so that the initial pool size could be set large enough so that it wouldn't grow in normal use. For some memory pools it's too difficult to calculate a good initial size, so this prefix should be used with those pools to disable the warning. */ #define MEMPOOL_GROWING "GROWING-" /* Memory allocated and reallocated (the new data in it) in pools is always zeroed, it will cost only a few CPU cycles and may well save some debug time. */ typedef struct pool *pool_t; struct pool_vfuncs { const char *(*get_name)(pool_t pool); void (*ref)(pool_t pool); void (*unref)(pool_t *pool); void *(*malloc)(pool_t pool, size_t size) ATTR_RETURNS_NONNULL; void (*free)(pool_t pool, void *mem); /* memory in old_size..new_size will be zeroed */ void *(*realloc)(pool_t pool, void *mem, size_t old_size, size_t new_size) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /* Frees all the memory in pool. NOTE: system_pool doesn't support this and crashes if it's used */ void (*clear)(pool_t pool); /* Returns the maximum amount of bytes that can be allocated with minimal trouble. If there's no such concept, always returns 0. */ size_t (*get_max_easy_alloc_size)(pool_t pool); }; struct pool { const struct pool_vfuncs *v; unsigned int alloconly_pool:1; unsigned int datastack_pool:1; }; /* system_pool uses calloc() + realloc() + free() */ extern pool_t system_pool; extern struct pool static_system_pool; /* memory allocated from data_stack is valid only until next t_pop() call. No checks are performed. */ extern pool_t unsafe_data_stack_pool; /* Create a new alloc-only pool. Note that `size' specifies the initial malloc()ed block size, part of it is used internally. */ pool_t pool_alloconly_create(const char *name, size_t size); /* Like alloconly pool, but clear the memory before freeing it. The idea is that you could allocate memory for storing sensitive information from this pool, and be sure that it gets cleared from the memory when it's no longer needed. */ pool_t pool_alloconly_create_clean(const char *name, size_t size); /* When allocating memory from returned pool, the data stack frame must be the same as it was when calling this function. pool_unref() also checks that the stack frame is the same. This should make it quite safe to use. */ pool_t pool_datastack_create(void); /* Similar to nearest_power(), but try not to exceed buffer's easy allocation size. If you don't have any explicit minimum size, use old_size + 1. */ size_t pool_get_exp_grown_size(pool_t pool, size_t old_size, size_t min_size); /* We require sizeof(type) to be <= UINT_MAX. This allows compiler to optimize away the entire MALLOC_MULTIPLY() call on 64bit systems. */ #define p_new(pool, type, count) \ ((type *) p_malloc(pool, MALLOC_MULTIPLY((unsigned int)sizeof(type), (count))) + \ COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) #define p_realloc_type(pool, mem, type, old_count, new_count) \ ((type *) p_realloc(pool, mem, \ MALLOC_MULTIPLY((unsigned int)sizeof(type), (old_count)), \ MALLOC_MULTIPLY((unsigned int)sizeof(type), (new_count))) + \ COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) static inline void * ATTR_MALLOC ATTR_RETURNS_NONNULL p_malloc(pool_t pool, size_t size) { return pool->v->malloc(pool, size); } static inline void * ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL p_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size) { return pool->v->realloc(pool, mem, old_size, new_size); } /* Free the memory. Currently it also sets memory to NULL, but that shouldn't be relied on as it's only an extra safety check. It might as well be later changed to some invalid pointer causing a segfault, or removed completely in some "optimization".. */ #define p_free(pool, mem) \ STMT_START { \ p_free_internal(pool, mem); \ (mem) = NULL; \ } STMT_END /* A macro that's guaranteed to set mem = NULL. */ #define p_free_and_null(pool, mem) p_free(pool, mem) static inline void p_free_internal(pool_t pool, void *mem) { pool->v->free(pool, mem); } static inline void p_clear(pool_t pool) { pool->v->clear(pool); } static inline size_t p_get_max_easy_alloc_size(pool_t pool) { return pool->v->get_max_easy_alloc_size(pool); } static inline const char *pool_get_name(pool_t pool) { return pool->v->get_name(pool); } static inline void pool_ref(pool_t pool) { pool->v->ref(pool); } static inline void pool_unref(pool_t *pool) { (*pool)->v->unref(pool); } /* These functions are only for pools created with pool_alloconly_create(): */ /* Returns how much memory has been allocated from this pool. */ size_t pool_alloconly_get_total_used_size(pool_t pool); /* Returns how much system memory has been allocated for this pool. */ size_t pool_alloconly_get_total_alloc_size(pool_t pool); #endif dovecot-2.2.33.2/src/lib/file-set-size.c0000644000175000017500000000450713123174404014513 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_POSIX_FALLOCATE # define _XOPEN_SOURCE 600 /* Required by glibc, breaks Solaris 9 */ #endif #define _GNU_SOURCE /* for fallocate() */ #include "lib.h" #include "file-set-size.h" #include #include #include #if defined(HAVE_LINUX_FALLOC_H) && !defined(FALLOC_FL_KEEP_SIZE) /* Legacy Linux does not have the FALLOC_FL_* flags under fcntl.h */ # include #endif int file_set_size(int fd, off_t size) { #ifdef HAVE_POSIX_FALLOCATE static bool posix_fallocate_supported = TRUE; #endif char block[IO_BLOCK_SIZE]; off_t offset; ssize_t ret; struct stat st; i_assert(size >= 0); if (fstat(fd, &st) < 0) { i_error("fstat() failed: %m"); return -1; } if (size < st.st_size) { if (ftruncate(fd, size) < 0) { i_error("ftruncate() failed: %m"); return -1; } return 0; } if (size == st.st_size) return 0; #ifdef HAVE_POSIX_FALLOCATE if (posix_fallocate_supported) { int err; err = posix_fallocate(fd, st.st_size, size - st.st_size); if (err == 0) return 0; if (err != EINVAL /* Solaris */ && err != EOPNOTSUPP /* AOX */) { if (!ENOSPACE(err)) i_error("posix_fallocate() failed: %m"); return -1; } /* Not supported by kernel, fallback to writing. */ posix_fallocate_supported = FALSE; } #endif /* start growing the file */ offset = st.st_size; memset(block, 0, I_MIN((ssize_t)sizeof(block), size - offset)); while (offset < size) { ret = pwrite(fd, block, I_MIN((ssize_t)sizeof(block), size - offset), offset); if (ret < 0) { if (!ENOSPACE(errno)) i_error("pwrite() failed: %m"); return -1; } offset += ret; } return 0; } int file_preallocate(int fd ATTR_UNUSED, off_t size ATTR_UNUSED) { #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_KEEP_SIZE) /* Linux */ if (fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, size) < 0) return errno == ENOSYS ? 0 : -1; return 1; #elif defined (F_PREALLOCATE) /* OSX */ fstore_t fs; i_zero(&fs); fs.fst_flags = F_ALLOCATECONTIG; fs.fst_posmode = F_PEOFPOSMODE; fs.fst_offset = 0; fs.fst_length = size; fs.fst_bytesalloc = 0; if (fcntl(fd, F_PREALLOCATE, &fs) < 0) return -1; return fs.fst_bytesalloc > 0 ? 1 : 0; #else return 0; #endif } dovecot-2.2.33.2/src/lib/istream-crlf.h0000644000175000017500000000035713123174404014427 00000000000000#ifndef ISTREAM_CRLF_H #define ISTREAM_CRLF_H /* Read all linefeeds as CRLF */ struct istream *i_stream_create_crlf(struct istream *input); /* Read all linefeeds as LF */ struct istream *i_stream_create_lf(struct istream *input); #endif dovecot-2.2.33.2/src/lib/test-json-tree.c0000644000175000017500000000710113165463624014722 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "json-tree.h" struct { enum json_type type; const char *value; } test_input[] = { { JSON_TYPE_OBJECT_KEY, "key-str" }, { JSON_TYPE_STRING, "string" }, { JSON_TYPE_OBJECT_KEY, "key-num" }, { JSON_TYPE_NUMBER, "1234" }, { JSON_TYPE_OBJECT_KEY, "key-true" }, { JSON_TYPE_TRUE, "true" }, { JSON_TYPE_OBJECT_KEY, "key-false" }, { JSON_TYPE_FALSE, "false" }, { JSON_TYPE_OBJECT_KEY, "key-null" }, { JSON_TYPE_NULL, NULL }, { JSON_TYPE_OBJECT_KEY, "key-obj-empty" }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key-obj" }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "sub" }, { JSON_TYPE_STRING, "value" }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key-arr-empty" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT_KEY, "key-arr" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_STRING, "foo" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_TRUE, "true" }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "aobj" }, { JSON_TYPE_ARRAY, NULL }, { JSON_TYPE_ARRAY_END, NULL }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "aobj-key" }, { JSON_TYPE_STRING, "value1" }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_OBJECT, NULL }, { JSON_TYPE_OBJECT_KEY, "aobj-key" }, { JSON_TYPE_STRING, "value2" }, { JSON_TYPE_OBJECT_END, NULL }, { JSON_TYPE_ARRAY_END, NULL } }; void test_json_tree(void) { struct json_tree *tree; struct json_tree_node *root, *node, *node1, *node2; unsigned int i; test_begin("json tree"); tree = json_tree_init(); for (i = 0; i < N_ELEMENTS(test_input); i++) { test_assert_idx(json_tree_append(tree, test_input[i].type, test_input[i].value) == 0, i); } root = json_tree_root(tree); i_assert(root != NULL); test_assert(root->value_type == JSON_TYPE_OBJECT); root = root->value.child; i_assert(root != NULL); for (i = 0; i < 10+2; i += 2) { node = json_tree_find_key(root, test_input[i].value); test_assert(node != NULL && node->value_type == test_input[i+1].type && null_strcmp(node->value.str, test_input[i+1].value) == 0); } node = json_tree_find_key(root, "key-obj"); test_assert(node != NULL); node = json_tree_find_key(root, "key-arr-empty"); test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY && node->value.child == NULL); node = json_tree_find_key(root, "key-arr"); test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY); node = node->value.child; test_assert(node != NULL && node->value_type == JSON_TYPE_STRING && strcmp(node->value.str, "foo") == 0); node = node->next; test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY && node->value.child != NULL && node->value.child->next == NULL && node->value.child->value_type == JSON_TYPE_TRUE); node = node->next; test_assert(node != NULL && node->value_type == JSON_TYPE_OBJECT && node->value.child != NULL && node->value.child->next == NULL && node->value.child->value_type == JSON_TYPE_ARRAY && node->value.child->value.child == NULL); node1 = json_tree_find_child_with(node->parent, "aobj-key", "value1"); node2 = json_tree_find_child_with(node->parent, "aobj-key", "value2"); test_assert(node1 != NULL && node2 != NULL && node1 != node2); test_assert(json_tree_find_child_with(node->parent, "aobj-key", "value3") == NULL); json_tree_deinit(&tree); test_end(); } dovecot-2.2.33.2/src/lib/fd-close-on-exec.c0000644000175000017500000000400213165463624015064 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "fd-close-on-exec.h" #include #include #include #include void fd_close_on_exec(int fd, bool set) { int flags; flags = fcntl(fd, F_GETFD, 0); if (flags < 0) i_fatal("fcntl(F_GETFD, %d) failed: %m", fd); flags = set ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC); if (fcntl(fd, F_SETFD, flags) < 0) i_fatal("fcntl(F_SETFD, %d) failed: %m", fd); } void fd_debug_verify_leaks(int first_fd, int last_fd) { struct ip_addr addr, raddr; in_port_t port, rport; struct stat st; int old_errno; for (; first_fd <= last_fd; first_fd++) { if (fcntl(first_fd, F_GETFD, 0) == -1 && errno == EBADF) continue; old_errno = errno; if (net_getsockname(first_fd, &addr, &port) == 0) { if (addr.family == AF_UNIX) { struct sockaddr_un sa; socklen_t socklen = sizeof(sa); if (getsockname(first_fd, (void *)&sa, &socklen) < 0) sa.sun_path[0] = '\0'; i_panic("Leaked UNIX socket fd %d: %s", first_fd, sa.sun_path); } if (net_getpeername(first_fd, &raddr, &rport) < 0) { i_zero(&raddr); rport = 0; } i_panic("Leaked socket fd %d: %s:%u -> %s:%u", first_fd, net_ip2addr(&addr), port, net_ip2addr(&raddr), rport); } if (fstat(first_fd, &st) == 0) { #ifdef __APPLE__ /* OSX workaround: gettimeofday() calls shm_open() internally and the fd won't get closed on exec. We'll just skip all ino/dev=0 files and hope they weren't anything else. */ if (st.st_ino == 0 && st.st_dev == 0) continue; #endif #ifdef HAVE_SYS_SYSMACROS_H i_panic("Leaked file fd %d: dev %s.%s inode %s", first_fd, dec2str(major(st.st_dev)), dec2str(minor(st.st_dev)), dec2str(st.st_ino)); #else i_panic("Leaked file fd %d: dev %s inode %s", first_fd, dec2str(st.st_dev), dec2str(st.st_ino)); #endif } i_panic("Leaked unknown fd %d (errno = %s)", first_fd, strerror(old_errno)); } } dovecot-2.2.33.2/src/lib/log-throttle.c0000644000175000017500000000410013165463624014457 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "time-util.h" #include "log-throttle.h" struct log_throttle { struct log_throttle_settings set; log_throttle_callback_t *callback; void *context; struct timeval last_time; unsigned int last_count; struct timeout *to_throttled; }; #undef log_throttle_init struct log_throttle * log_throttle_init(const struct log_throttle_settings *set, log_throttle_callback_t *callback, void *context) { struct log_throttle *throttle; i_assert(set->throttle_at_max_per_interval > 0); i_assert(set->unthrottle_at_max_per_interval > 0); throttle = i_new(struct log_throttle, 1); throttle->set = *set; if (throttle->set.interval_msecs == 0) throttle->set.interval_msecs = 1000; throttle->callback = callback; throttle->context = context; return throttle; } void log_throttle_deinit(struct log_throttle **_throttle) { struct log_throttle *throttle = *_throttle; *_throttle = NULL; if (throttle->to_throttled != NULL) timeout_remove(&throttle->to_throttled); i_free(throttle); } static void log_throttle_callback(struct log_throttle *throttle) { if (throttle->last_count > 0) throttle->callback(throttle->last_count, throttle->context); if (throttle->last_count < throttle->set.unthrottle_at_max_per_interval) timeout_remove(&throttle->to_throttled); throttle->last_count = 0; } bool log_throttle_accept(struct log_throttle *throttle) { if (throttle->to_throttled != NULL) { /* unthrottling and last_count resets are done only by the callback */ throttle->last_count++; return FALSE; } else if (timeval_diff_msecs(&ioloop_timeval, &throttle->last_time) >= (int)throttle->set.interval_msecs) { throttle->last_time = ioloop_timeval; throttle->last_count = 1; return TRUE; } else if (++throttle->last_count <= throttle->set.throttle_at_max_per_interval) { return TRUE; } else { throttle->last_count = 1; throttle->to_throttled = timeout_add(throttle->set.interval_msecs, log_throttle_callback, throttle); return FALSE; } } dovecot-2.2.33.2/src/lib/test-unichar.c0000644000175000017500000000603013165463624014445 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "buffer.h" #include "unichar.h" static void test_unichar_uni_utf8_strlen(void) { static const char input[] = "\xC3\xA4\xC3\xA4\0a"; test_begin("uni_utf8_strlen()"); test_assert(uni_utf8_strlen(input) == 2); test_end(); test_begin("uni_utf8_strlen_n()"); test_assert(uni_utf8_strlen_n(input, 1) == 0); test_assert(uni_utf8_strlen_n(input, 2) == 1); test_assert(uni_utf8_strlen_n(input, 3) == 1); test_assert(uni_utf8_strlen_n(input, 4) == 2); test_end(); } static void test_unichar_uni_utf8_partial_strlen_n(void) { static const char input[] = "\xC3\xA4\xC3\xA4\0a"; size_t pos; test_begin("uni_utf8_partial_strlen_n()"); test_assert(uni_utf8_partial_strlen_n(input, 1, &pos) == 0 && pos == 0); test_assert(uni_utf8_partial_strlen_n(input, 2, &pos) == 1 && pos == 2); test_assert(uni_utf8_partial_strlen_n(input, 3, &pos) == 1 && pos == 2); test_assert(uni_utf8_partial_strlen_n(input, 4, &pos) == 2 && pos == 4); test_assert(uni_utf8_partial_strlen_n(input, 5, &pos) == 3 && pos == 5); test_assert(uni_utf8_partial_strlen_n(input, 6, &pos) == 4 && pos == 6); test_end(); } void test_unichar(void) { static const char overlong_utf8[] = "\xf8\x80\x95\x81\xa1"; static const char collate_in[] = "\xc3\xbc \xc2\xb3"; static const char collate_exp[] = "U\xcc\x88 3"; buffer_t *collate_out; unichar_t chr, chr2; string_t *str = t_str_new(16); test_begin("unichars encode/decode"); for (chr = 0; chr <= 0x10ffff; chr++) { /* The bottom 6 bits should be irrelevant to code coverage, only test 000000, 111111, and something in between. */ if ((chr & 63) == 1) chr += rand() % 62; /* After 0, somewhere between 1 and 62 */ else if ((chr & 63) > 0 && (chr & 63) < 63) chr |= 63; /* After random, straight to 63 */ str_truncate(str, 0); uni_ucs4_to_utf8_c(chr, str); test_assert(uni_utf8_str_is_valid(str_c(str))); test_assert(uni_utf8_get_char(str_c(str), &chr2) == (int)uni_utf8_char_bytes(*str_data(str))); test_assert(chr2 == chr); if ((chr & 0x63) == 0) { unsigned int utf8len = uni_utf8_char_bytes(*str_c(str)); /* virtually truncate the byte string */ while (--utf8len > 0) test_assert(uni_utf8_get_char_n(str_c(str), utf8len, &chr2) == 0); utf8len = uni_utf8_char_bytes(*str_c(str)); /* actually truncate the byte stream */ while (--utf8len > 0) { str_truncate(str, utf8len); test_assert(!uni_utf8_str_is_valid(str_c(str))); test_assert(uni_utf8_get_char(str_c(str), &chr2) == 0); } } } test_end(); test_begin("unichar collation"); collate_out = buffer_create_dynamic(default_pool, 32); uni_utf8_to_decomposed_titlecase(collate_in, sizeof(collate_in), collate_out); test_assert(!strcmp(collate_out->data, collate_exp)); buffer_free(&collate_out); test_assert(!uni_utf8_str_is_valid(overlong_utf8)); test_assert(uni_utf8_get_char(overlong_utf8, &chr2) < 0); test_end(); test_unichar_uni_utf8_strlen(); test_unichar_uni_utf8_partial_strlen_n(); } dovecot-2.2.33.2/src/lib/test-iso8601-date.c0000644000175000017500000000757013123174404015037 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "test-common.h" #include "iso8601-date.h" #include struct iso8601_date_test { const char *date_in; const char *date_out; struct tm tm; int zone_offset; }; /* Valid date tests */ struct iso8601_date_test valid_date_tests[] = { { .date_in = "2007-11-07T23:05:34+00:00", .tm = { .tm_year = 107, .tm_mon = 10, .tm_mday = 7, .tm_hour = 23, .tm_min = 5, .tm_sec = 34 }, },{ .date_in = "2011-01-07T21:03:31+00:30", .tm = { .tm_year = 111, .tm_mon = 0, .tm_mday = 7, .tm_hour = 21, .tm_min = 3, .tm_sec = 31 }, .zone_offset = 30 },{ .date_in = "2006-05-09T18:04:12+05:30", .tm = { .tm_year = 106, .tm_mon = 4, .tm_mday = 9, .tm_hour = 18, .tm_min = 4, .tm_sec = 12 }, .zone_offset = 5*60+30 },{ .date_in = "1975-10-30T06:33:29Z", .date_out = "1975-10-30T06:33:29+00:00", .tm = { .tm_year = 75, .tm_mon = 9, .tm_mday = 30, .tm_hour = 6, .tm_min = 33, .tm_sec = 29 }, },{ .date_in = "1988-04-24t15:02:12z", .date_out = "1988-04-24T15:02:12+00:00", .tm = { .tm_year = 88, .tm_mon = 3, .tm_mday = 24, .tm_hour = 15, .tm_min = 2, .tm_sec = 12 }, },{ .date_in = "2012-02-29T08:12:34.23198Z", .date_out = "2012-02-29T08:12:34+00:00", .tm = { .tm_year = 112, .tm_mon = 1, .tm_mday = 29, .tm_hour = 8, .tm_min = 12, .tm_sec = 34 }, } }; unsigned int valid_date_test_count = N_ELEMENTS(valid_date_tests); static void test_iso8601_date_valid(void) { unsigned int i; for (i = 0; i < valid_date_test_count; i++) T_BEGIN { const char *date_in, *date_out, *pdate_out; struct tm *tm = &valid_date_tests[i].tm, ptm; int zone_offset = valid_date_tests[i].zone_offset, pzone_offset; bool result; date_in = valid_date_tests[i].date_in; date_out = valid_date_tests[i].date_out == NULL ? date_in : valid_date_tests[i].date_out; test_begin(t_strdup_printf("iso8601 date valid [%d]", i)); result = iso8601_date_parse_tm ((const unsigned char *)date_in, strlen(date_in), &ptm, &pzone_offset); test_out(t_strdup_printf("parse %s", date_in), result); if (result) { bool equal = tm->tm_year == ptm.tm_year && tm->tm_mon == ptm.tm_mon && tm->tm_mday == ptm.tm_mday && tm->tm_hour == ptm.tm_hour && tm->tm_min == ptm.tm_min && tm->tm_sec == ptm.tm_sec; test_out("valid timestamp", equal); test_out_reason("valid timezone", zone_offset == pzone_offset, t_strdup_printf("%d", pzone_offset)); pdate_out = iso8601_date_create_tm(tm, zone_offset); test_out_reason("valid create", strcmp(date_out, pdate_out) == 0, pdate_out); } test_end(); } T_END; } /* Invalid date tests */ const char *invalid_date_tests[] = { "200-11-17T23:05:34+00:00", "2007:11-17T23:05:34+00:00", "2007-11?17T23:05:34+00:00", "2007-49-17T23:05:34+00:00", "2007-11-77T23:05:34+00:00", "2007-11-17K23:05:34+00:00", "2007-11-13T59:05:34+00:00", "2007-112-13T12:15:34+00:00", "2007-11-133T12:15:34+00:00", "2007-11-13T12J15:34+00:00", "2007-11-13T12:15*34+00:00", "2007-11-13T12:15:34/00:00", "2007-11-13T12:15:34+00-00", "2007-11-13T123:15:34+00:00", "2007-11-13T12:157:34+00:00", "2007-11-13T12:15:342+00:00", "2007-11-13T12:15:34+001:00", "2007-11-13T12:15:32+00:006", "2007-02-29T15:13:21Z" }; unsigned int invalid_date_test_count = N_ELEMENTS(invalid_date_tests); static void test_iso8601_date_invalid(void) { unsigned int i; for (i = 0; i < invalid_date_test_count; i++) T_BEGIN { const char *date_in; struct tm tm; int tz; bool result; date_in = invalid_date_tests[i]; test_begin(t_strdup_printf("iso8601 date invalid [%d]", i)); result = iso8601_date_parse_tm ((const unsigned char *)date_in, strlen(date_in), &tm, &tz); test_out(t_strdup_printf("parse %s", date_in), !result); test_end(); } T_END; } void test_iso8601_date(void) { test_iso8601_date_valid(); test_iso8601_date_invalid(); } dovecot-2.2.33.2/src/lib/guid.h0000644000175000017500000000326413123174404012767 00000000000000#ifndef GUID_H #define GUID_H #define GUID_128_SIZE 16 typedef uint8_t guid_128_t[GUID_128_SIZE]; #define GUID_128_HOST_HASH_SIZE 4 ARRAY_DEFINE_TYPE(guid_128_t, guid_128_t); enum uuid_format { FORMAT_RECORD, FORMAT_COMPACT, FORMAT_MICROSOFT, }; /* Generate a GUID (contains host name) */ const char *guid_generate(void); /* Generate 128 bit GUID */ void guid_128_generate(guid_128_t guid_r); /* Returns TRUE if GUID is empty (not set / unknown). */ bool guid_128_is_empty(const guid_128_t guid) ATTR_PURE; static inline void guid_128_empty(guid_128_t guid) { memset(guid, 0, GUID_128_SIZE); } /* Returns TRUE if two GUIDs are equal. */ bool guid_128_equals(const guid_128_t guid1, const guid_128_t guid2) ATTR_PURE; /* Copy GUID */ static inline void guid_128_copy(guid_128_t dest, const guid_128_t src) { memcpy(dest, src, GUID_128_SIZE); } /* Returns GUID as a hex string. */ const char *guid_128_to_string(const guid_128_t guid); /* Parse GUID from a string. Returns 0 if ok, -1 if GUID isn't valid. */ int guid_128_from_string(const char *str, guid_128_t guid_r); /* Returns GUID as a UUID hex string. */ const char *guid_128_to_uuid_string(const guid_128_t guid, enum uuid_format format); /* Parse GUID from a UUID string. Returns 0 if ok, -1 if UIID isn't valid. */ int guid_128_from_uuid_string(const char *str, guid_128_t guid_r); /* guid_128 hash/cmp functions for hash.h */ unsigned int guid_128_hash(const guid_128_t guid) ATTR_PURE; int guid_128_cmp(const guid_128_t guid1, const guid_128_t guid2) ATTR_PURE; /* Return the hash of host used by guid_128_generate(). */ void guid_128_host_hash_get(const char *host, unsigned char hash_r[STATIC_ARRAY GUID_128_HOST_HASH_SIZE]); #endif dovecot-2.2.33.2/src/lib/test-buffer.c0000644000175000017500000002107013165463624014266 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "buffer.h" static void test_buffer_random(void) { #define BUF_TEST_SIZE (1024*2) #define BUF_TEST_COUNT 1000 buffer_t *buf; unsigned char *p, testdata[BUF_TEST_SIZE], shadowbuf[BUF_TEST_SIZE]; unsigned int i, shadowbuf_size; size_t pos, pos2, size; int test = -1; bool zero; buf = buffer_create_dynamic(default_pool, 1); for (i = 0; i < BUF_TEST_SIZE; i++) testdata[i] = rand(); memset(shadowbuf, 0, sizeof(shadowbuf)); shadowbuf_size = 0; for (i = 0; i < BUF_TEST_COUNT; i++) { if (buf->used == BUF_TEST_SIZE) { size = shadowbuf_size = rand() % (buf->used - 1); buffer_set_used_size(buf, size); memset(shadowbuf + shadowbuf_size, 0, BUF_TEST_SIZE - shadowbuf_size); i_assert(buf->used < BUF_TEST_SIZE); } test = rand() % 6; zero = rand() % 10 == 0; switch (test) { case 0: pos = rand() % (BUF_TEST_SIZE-1); size = rand() % (BUF_TEST_SIZE - pos); if (!zero) { buffer_write(buf, pos, testdata, size); memcpy(shadowbuf + pos, testdata, size); } else { buffer_write_zero(buf, pos, size); memset(shadowbuf + pos, 0, size); } if (pos + size > shadowbuf_size) shadowbuf_size = pos + size; break; case 1: size = rand() % (BUF_TEST_SIZE - buf->used); if (!zero) { buffer_append(buf, testdata, size); memcpy(shadowbuf + shadowbuf_size, testdata, size); } else { buffer_append_zero(buf, size); memset(shadowbuf + shadowbuf_size, 0, size); } shadowbuf_size += size; break; case 2: pos = rand() % (BUF_TEST_SIZE-1); size = rand() % (BUF_TEST_SIZE - I_MAX(buf->used, pos)); if (!zero) { buffer_insert(buf, pos, testdata, size); memmove(shadowbuf + pos + size, shadowbuf + pos, BUF_TEST_SIZE - (pos + size)); memcpy(shadowbuf + pos, testdata, size); } else { buffer_insert_zero(buf, pos, size); memmove(shadowbuf + pos + size, shadowbuf + pos, BUF_TEST_SIZE - (pos + size)); memset(shadowbuf + pos, 0, size); } if (pos < shadowbuf_size) shadowbuf_size += size; else shadowbuf_size = pos + size; break; case 3: pos = rand() % (BUF_TEST_SIZE-1); size = rand() % (BUF_TEST_SIZE - pos); buffer_delete(buf, pos, size); if (pos < shadowbuf_size) { if (pos + size > shadowbuf_size) size = shadowbuf_size - pos; memmove(shadowbuf + pos, shadowbuf + pos + size, BUF_TEST_SIZE - (pos + size)); shadowbuf_size -= size; memset(shadowbuf + shadowbuf_size, 0, BUF_TEST_SIZE - shadowbuf_size); } break; case 4: if (shadowbuf_size <= 1) break; pos = rand() % (shadowbuf_size-1); /* dest */ pos2 = rand() % (shadowbuf_size-1); /* source */ size = rand() % (shadowbuf_size - I_MAX(pos, pos2)); buffer_copy(buf, pos, buf, pos2, size); memmove(shadowbuf + pos, shadowbuf + pos2, size); if (pos > pos2 && pos + size > shadowbuf_size) shadowbuf_size = pos + size; break; case 5: pos = rand() % (BUF_TEST_SIZE-1); size = rand() % (BUF_TEST_SIZE - pos); p = buffer_get_space_unsafe(buf, pos, size); memcpy(p, testdata, size); memcpy(shadowbuf + pos, testdata, size); if (pos + size > shadowbuf_size) shadowbuf_size = pos + size; break; } i_assert(shadowbuf_size <= BUF_TEST_SIZE); if (buf->used != shadowbuf_size || memcmp(buf->data, shadowbuf, buf->used) != 0) break; } if (i == BUF_TEST_COUNT) test_out("buffer", TRUE); else { test_out_reason("buffer", FALSE, t_strdup_printf("round %u test %d failed", i, test)); } buffer_free(&buf); } static void test_buffer_write(void) { buffer_t *buf; test_begin("buffer_write"); buf = buffer_create_dynamic(pool_datastack_create(), 8); buffer_write(buf, 5, buf, 0); test_assert(buf->used == 5); test_end(); } static void test_buffer_set_used_size(void) { buffer_t *buf; test_begin("buffer_set_used_size"); buf = buffer_create_dynamic(pool_datastack_create(), 8); memset(buffer_append_space_unsafe(buf, 7), 'a', 7); buffer_set_used_size(buf, 4); test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 7), "aaaa\0\0\0", 7) == 0); memset(buffer_get_space_unsafe(buf, 4, 7), 'b', 7); buffer_set_used_size(buf, 10); test_assert(memcmp(buffer_append_space_unsafe(buf, 1), "\0", 1) == 0); buffer_set_used_size(buf, 11); test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 11), "aaaabbbbbb\0", 11) == 0); test_end(); } #if 0 /* this code is left here to produce the output found in * buffer.h should it be needed for debugging purposes */ #include "str.h" #include "hex-binary.h" static const char *binary_to_10(const unsigned char *data, size_t size) { string_t *str = t_str_new(size*8); for (size_t i = 0; i < size; i++) { for (int j = 0; j < 8; j++) { if ((data[i] & (1 << (7-j))) != 0) str_append_c(str, '1'); else str_append_c(str, '0'); } } return str_c(str); } static void test_foo(void) { buffer_t *buf = buffer_create_dynamic(default_pool, 100); for (int i = 1; i <= 24; i++) { buffer_set_used_size(buf, 0); buffer_append_c(buf, 0xff); buffer_append_c(buf, 0xff); buffer_append_c(buf, 0xff); buffer_truncate_rshift_bits(buf, i); printf("%2d bits: %24s %s\n", i, binary_to_hex(buf->data, buf->used), binary_to_10(buf->data, buf->used)); } } #endif static void test_buffer_truncate_bits(void) { buffer_t *buf; test_begin("buffer_test_truncate_bits"); struct { buffer_t input; size_t bits; buffer_t output; } test_cases[] = { { { "\xff\xff\xff", 3, {0} }, 0, { "", 0, {0} } }, { { "\xff\xff\xff", 3, {0} }, 1, { "\x01", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 2, { "\x03", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 3, { "\x07", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 4, { "\x0f", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 5, { "\x1f", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 6, { "\x3f", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 7, { "\x7f", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 8, { "\xff", 1, {0} } }, { { "\xff\xff\xff", 3, {0} }, 9, { "\x01\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 10, { "\x03\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 11, { "\x07\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 12, { "\x0f\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 13, { "\x1f\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 14, { "\x3f\xff", 2, {0} } }, { { "\xff\xff\xff", 3, {0} }, 15, { "\x7f\xff", 2, {0} } }, { { "0123456789", 10, {0} }, 16, { "01", 2, {0} } }, { { "0123456789", 10, {0} }, 24, { "012", 3, {0} } }, { { "0123456789", 10, {0} }, 32, { "0123", 4, {0} } }, { { "0123456789", 10, {0} }, 40, { "01234", 5, {0} } }, { { "0123456789", 10, {0} }, 48, { "012345", 6, {0} } }, { { "0123456789", 10, {0} }, 56, { "0123456", 7, {0} } }, { { "0123456789", 10, {0} }, 64, { "01234567", 8, {0} } }, { { "0123456789", 10, {0} }, 72, { "012345678", 9, {0} } }, { { "0123456789", 10, {0} }, 80, { "0123456789", 10, {0} } }, { { "\x58\x11\xed\x02\x4d\x87\x4a\xe2\x5c\xb2\xfa\x69\xf0\xa9\x46\x2e\x04\xca\x5d\x82", 20, {0} }, 13, { "\x0b\x02", 2, {0} } }, /* special test cases for auth policy */ { { "\x34\x40\xc8\xc9\x3a\xb6\xe7\xc4\x3f\xc1\xc3\x4d\xd5\x56\xa3\xea\xfb\x5a\x33\x57\xac\x11\x39\x2c\x71\xcb\xee\xbb\xc8\x66\x2f\x64", 32, {0} }, 12, { "\x03\x44", 2, {0} } }, { { "\x49\xe5\x8a\x88\x76\xd3\x25\x68\xc9\x89\x4a\xe0\x64\xe4\x04\xf4\xf9\x13\xec\x88\x97\x47\x30\x7f\x3f\xcd\x8f\x74\x4f\x40\xd1\x25", 32, {0} }, 12, { "\x04\x9e", 2, {0} } }, { { "\x08\x3c\xdc\x14\x61\x80\x1c\xe8\x43\x81\x98\xfa\xc0\x64\x04\x7a\xa2\x73\x25\x6e\xe6\x4b\x85\x42\xd0\xe2\x78\xd7\x91\xb4\x89\x3f", 32, {0} }, 12, { "\x00\x83", 2, {0} } }, }; buf = buffer_create_dynamic(pool_datastack_create(), 10); for(size_t i = 0; i < N_ELEMENTS(test_cases); i++) { buffer_set_used_size(buf, 0); buffer_copy(buf, 0, &test_cases[i].input, 0, (size_t)-1); buffer_truncate_rshift_bits(buf, test_cases[i].bits); test_assert_idx(buffer_cmp(buf, &test_cases[i].output) == TRUE, i); } test_end(); } void test_buffer(void) { test_buffer_random(); test_buffer_write(); test_buffer_set_used_size(); test_buffer_truncate_bits(); } dovecot-2.2.33.2/src/lib/json-tree.c0000644000175000017500000001006613165463624013751 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "json-tree.h" struct json_tree { pool_t pool; struct json_tree_node *root, *cur, *cur_child; }; struct json_tree *json_tree_init(void) { struct json_tree *tree; pool_t pool; pool = pool_alloconly_create("json tree", 1024); tree = p_new(pool, struct json_tree, 1); tree->pool = pool; tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1); tree->cur->value_type = JSON_TYPE_OBJECT; return tree; } void json_tree_deinit(struct json_tree **_tree) { struct json_tree *tree = *_tree; *_tree = NULL; pool_unref(&tree->pool); } static void json_tree_append_child(struct json_tree *tree, enum json_type type, const char *value) { struct json_tree_node *node; node = p_new(tree->pool, struct json_tree_node, 1); node->parent = tree->cur; node->value_type = type; node->value.str = p_strdup(tree->pool, value); if (tree->cur_child == NULL) tree->cur->value.child = node; else tree->cur_child->next = node; tree->cur_child = node; } static void json_tree_set_cur(struct json_tree *tree, struct json_tree_node *node) { tree->cur = node; tree->cur_child = tree->cur->value.child; if (tree->cur_child != NULL) { while (tree->cur_child->next != NULL) tree->cur_child = tree->cur_child->next; } } static int json_tree_append_value(struct json_tree *tree, enum json_type type, const char *value) { switch (tree->cur->value_type) { case JSON_TYPE_OBJECT_KEY: /* "key": value - we already added the node and set its key, so now just set the value */ tree->cur->value_type = type; tree->cur->value.str = p_strdup(tree->pool, value); json_tree_set_cur(tree, tree->cur->parent); break; case JSON_TYPE_ARRAY: /* element in array - add a new node */ json_tree_append_child(tree, type, value); break; default: return -1; } return 0; } int json_tree_append(struct json_tree *tree, enum json_type type, const char *value) { switch (type) { case JSON_TYPE_OBJECT_KEY: if (tree->cur->value_type != JSON_TYPE_OBJECT) return -1; json_tree_append_child(tree, type, NULL); json_tree_set_cur(tree, tree->cur_child); tree->cur->key = p_strdup(tree->pool, value); break; case JSON_TYPE_ARRAY: if (json_tree_append_value(tree, type, NULL) < 0) return -1; json_tree_set_cur(tree, tree->cur_child); break; case JSON_TYPE_OBJECT: if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY) tree->cur->value_type = JSON_TYPE_OBJECT; else if (tree->cur->value_type == JSON_TYPE_ARRAY) { json_tree_append_child(tree, type, NULL); json_tree_set_cur(tree, tree->cur_child); } else { return -1; } break; case JSON_TYPE_OBJECT_END: if (tree->cur->parent == NULL || tree->cur->value_type != JSON_TYPE_OBJECT) return -1; json_tree_set_cur(tree, tree->cur->parent); break; case JSON_TYPE_ARRAY_END: if (tree->cur->parent == NULL || tree->cur->value_type != JSON_TYPE_ARRAY) return -1; json_tree_set_cur(tree, tree->cur->parent); break; case JSON_TYPE_STRING: case JSON_TYPE_NUMBER: case JSON_TYPE_TRUE: case JSON_TYPE_FALSE: case JSON_TYPE_NULL: if (json_tree_append_value(tree, type, value) < 0) return -1; break; } return 0; } struct json_tree_node *json_tree_root(struct json_tree *tree) { return tree->root; } struct json_tree_node * json_tree_find_key(struct json_tree_node *node, const char *key) { for (; node != NULL; node = node->next) { if (node->key != NULL && strcmp(node->key, key) == 0) return node; } return NULL; } struct json_tree_node * json_tree_find_child_with(struct json_tree_node *node, const char *key, const char *value) { struct json_tree_node *child; i_assert(node->value_type == JSON_TYPE_OBJECT || node->value_type == JSON_TYPE_ARRAY); for (node = node->value.child; node != NULL; node = node->next) { if (node->value_type != JSON_TYPE_OBJECT) continue; child = json_tree_find_key(node->value.child, key); if (child != NULL && child->value.str != NULL && strcmp(child->value.str, value) == 0) return node; } return NULL; } dovecot-2.2.33.2/src/lib/base64.h0000644000175000017500000000226213165463624013133 00000000000000#ifndef BASE64_H #define BASE64_H /* Translates binary data into base64. The src must not point to dest buffer. */ void base64_encode(const void *src, size_t src_size, buffer_t *dest); /* Translates base64 data into binary and appends it to dest buffer. dest may point to same buffer as src. Returns 1 if all ok, 0 if end of base64 data found, -1 if data is invalid. Any CR, LF characters are ignored, as well as whitespace at beginning or end of line. This function may be called multiple times for parsing the same stream. If src_pos is non-NULL, it's updated to first non-translated character in src. */ int base64_decode(const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) ATTR_NULL(3); /* Decode given string to a buffer allocated from data stack. */ buffer_t *t_base64_decode_str(const char *str); /* Returns TRUE if c is a valid base64 encoding character (excluding '=') */ bool base64_is_valid_char(char c); /* max. buffer size required for base64_encode() */ #define MAX_BASE64_ENCODED_SIZE(size) \ ((size) / 3 * 4 + 2+2) /* max. buffer size required for base64_decode() */ #define MAX_BASE64_DECODED_SIZE(size) \ ((size) / 4 * 3 + 3) #endif dovecot-2.2.33.2/src/lib/hostpid.h0000644000175000017500000000104213123174404013501 00000000000000#ifndef HOSTPID_H #define HOSTPID_H /* These environments override the hostname/hostdomain if they're set. Master process normally sets these to child processes. */ #define MY_HOSTNAME_ENV "DOVECOT_HOSTNAME" #define MY_HOSTDOMAIN_ENV "DOVECOT_HOSTDOMAIN" extern const char *my_hostname; extern const char *my_pid; /* Initializes my_hostname and my_pid. */ void hostpid_init(void); void hostpid_deinit(void); /* Returns the current host+domain, or if it fails fallback to returning hostname. */ const char *my_hostdomain(void); #endif dovecot-2.2.33.2/src/lib/test-array.c0000644000175000017500000001764113165463624014144 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "array.h" struct foo { unsigned int a, b, c; }; static void test_array_count(void) { ARRAY(struct foo) foos; struct foo nfoo; test_begin("array count/empty"); t_array_init(&foos, 32); test_assert(array_count(&foos) == 0); test_assert(array_is_empty(&foos)); test_assert(!array_not_empty(&foos)); nfoo.a = nfoo.b = nfoo.c = 9; array_append(&foos, &nfoo, 1); test_assert(array_count(&foos) == 1); test_assert(!array_is_empty(&foos)); test_assert(array_not_empty(&foos)); test_end(); } static void test_array_foreach(void) { ARRAY(struct foo) foos; const struct foo *foo; struct foo nfoo; unsigned int i; test_begin("array foreach"); t_array_init(&foos, 32); for (i = 0; i < 10; i++) { nfoo.a = nfoo.b = nfoo.c = i; array_append(&foos, &nfoo, 1); } array_foreach(&foos, foo) { i = array_foreach_idx(&foos, foo); test_assert(foo->a == i); test_assert(foo->b == i); test_assert(foo->c == i); } test_end(); } static void test_array_swap(void) { ARRAY(struct foo) foos[3]; struct foo nfoo; int i, j; test_begin("array swap"); for (i = 1; i <= 3; i++) { t_array_init(&foos[i-1], i); for (j = 1; j <= 2*i+1; j++) { nfoo.a = nfoo.b = nfoo.c = j; array_append(&foos[i-1], &nfoo, 1); } } for (i = 0; i < 1000; i++) array_swap(&foos[rand()%3], &foos[rand()%3]); /* Just want size 3, 5, and 7 in any order */ test_assert(array_count(&foos[0]) * array_count(&foos[1]) * array_count(&foos[2]) == 3*5*7); test_assert(array_count(&foos[0]) + array_count(&foos[1]) + array_count(&foos[2]) == 3+5+7); test_end(); } static int test_int_compare(const int *key, const int *elem) { return (*key < *elem) ? -1 : (*key > *elem) ? 1 : 0; } static void test_array_reverse(void) { ARRAY(int) intarr; int input[] = { -1234567890, -272585721, 272485922, 824725652 }; const int tmpi = 999, *output; unsigned int i, j; test_begin("array reverse"); t_array_init(&intarr, 5); for (i = 0; i <= N_ELEMENTS(input); i++) { array_clear(&intarr); array_append(&intarr, input, i); array_reverse(&intarr); output = i == 0 ? NULL : array_idx(&intarr, 0); for (j = 0; j < i; j++) test_assert(input[i-j-1] == output[j]); } test_end(); test_begin("array_lsearch"); for (i = 0; i < N_ELEMENTS(input); i++) { output = array_lsearch(&intarr, &input[i], test_int_compare); test_assert(output != NULL); j = array_ptr_to_idx(&intarr, output); test_assert_idx(j == N_ELEMENTS(input) - 1 - i, i); } output = array_lsearch(&intarr, &tmpi, test_int_compare); test_assert(output == NULL); test_end(); } static int test_compare_ushort(const unsigned short *c1, const unsigned short *c2) { return *c1 > *c2 ? 1 : *c1 < *c2 ? -1 : 0; } static int test_compare_ushort_fuzz(const unsigned short *c1, const unsigned short *c2, const int *pfuzz) { int d = (int)*c1 - (int)*c2; if (d <= *pfuzz && -d <= *pfuzz) return 0; return d; } static void test_array_cmp(void) { static const unsigned short deltas[] = { 0x8000, 0xc000, 0xfe00, 0xff00, 0xff80, 0xffc0, 0xfffe, 0xffff, 0, 1, 2, 64, 128, 256, 512, 16384, 32768 }; #define NELEMS 5u ARRAY(unsigned short) arr1, arr2; unsigned short elems[NELEMS+1]; unsigned int i; int fuzz; test_begin("array compare (ushort)"); t_array_init(&arr1, NELEMS); t_array_init(&arr2, NELEMS); for (i = 0; i < NELEMS; i++) { elems[i] = rand(); array_append(&arr2, &elems[i], 1); } array_append(&arr1, elems, NELEMS); test_assert(array_cmp(&arr1, &arr2) == 1); test_assert(array_equal_fn(&arr1, &arr2, test_compare_ushort) == 1); fuzz = 0; test_assert(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == 1); for (i = 0; i < 256; i++) { unsigned int j = rand() % NELEMS; const unsigned short *ptmp = array_idx(&arr2, j); unsigned short tmp = *ptmp; unsigned short repl = tmp + deltas[rand() % N_ELEMENTS(deltas)]; array_idx_set(&arr2, j, &repl); test_assert_idx(array_cmp(&arr1, &arr2) == (tmp == repl), i); test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_ushort) == (tmp == repl), i); fuzz = (int)tmp - (int)repl; if (fuzz < 0) fuzz = -fuzz; test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == 1, i); if (fuzz > 0) { fuzz--; test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == 0, i); } array_idx_set(&arr2, j, &tmp); test_assert_idx(array_cmp(&arr1, &arr2) == TRUE, i); test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_ushort) == 1, i); fuzz = 0; test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == 1, i); } elems[NELEMS] = 0; array_append(&arr2, &elems[NELEMS], 1); test_assert(array_cmp(&arr1, &arr2) == 0); test_assert(array_equal_fn(&arr1, &arr2, test_compare_ushort) == 0); test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == 0, i); test_end(); } static int test_compare_string(const char *const *c1, const char *const *c2) { return strcmp(*c1, *c2); } static void test_array_cmp_str(void) { #define NELEMS 5u ARRAY(const char *) arr1, arr2; const char *elemstrs[NELEMS+1]; unsigned int i; test_begin("array compare (char*)"); t_array_init(&arr1, NELEMS); t_array_init(&arr2, NELEMS); for (i = 0; i < NELEMS; i++) { elemstrs[i] = t_strdup_printf("%x", rand()); /* never 0-length */ array_append(&arr2, &elemstrs[i], 1); } array_append(&arr1, elemstrs, NELEMS); test_assert(array_cmp(&arr1, &arr2) == 1); /* pointers shared, so identical */ test_assert(array_equal_fn(&arr1, &arr2, test_compare_string) == 1); /* therefore value same */ for (i = 0; i < 2560; i++) { unsigned int j = rand() % NELEMS; const char *const *ostr_p = array_idx(&arr2, j); const char *ostr = *ostr_p; unsigned int olen = strlen(ostr); unsigned int rc = rand() % (olen + 1); char ochar = ostr[rc]; char buf[12]; const char *bufp = buf; memcpy(buf, ostr, olen+1); buf[rc] = rand() % (CHAR_MAX + 1 - CHAR_MIN) + CHAR_MIN; if(rc == olen) buf[rc+1] = '\0'; array_idx_set(&arr2, j, &bufp); test_assert(array_cmp(&arr1, &arr2) == 0); /* pointers now differ */ test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_string) == (strcmp(ostr, buf) == 0), i); /* sometimes still the same */ test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_string) == (ochar == buf[rc]), i); /* ditto */ array_idx_set(&arr2, j, &ostr); test_assert(array_cmp(&arr1, &arr2) == 1); /* pointers now same again */ test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_string) == 1, i); /* duh! */ } /* length differences being detected are tested in other tests */ test_end(); } void test_array(void) { test_array_count(); test_array_foreach(); test_array_reverse(); test_array_cmp(); test_array_cmp_str(); test_array_swap(); } enum fatal_test_state fatal_array(unsigned int stage) { double tmpd[2] = { 42., -42. }; short tmps[8] = {1,2,3,4,5,6,7,8}; static const void *useless_ptr; /* persuade gcc to not optimise the tests */ switch(stage) { case 0: { ARRAY(double) ad; test_begin("fatal_array"); t_array_init(&ad, 3); /* allocation big enough, but memory not initialised */ useless_ptr = array_idx(&ad, 0); return FATAL_TEST_FAILURE; } break; case 1: { ARRAY(double) ad; t_array_init(&ad, 2); array_append(&ad, tmpd, 2); /* actual out of range address requested */ useless_ptr = array_idx(&ad, 2); return FATAL_TEST_FAILURE; } break; case 2: { ARRAY(double) ad; ARRAY(short) as; t_array_init(&ad, 2); t_array_init(&as, 8); array_append(&as, tmps, 2); array_copy(&ad.arr, 1, &as.arr, 0, 4); return FATAL_TEST_FAILURE; } break; } test_end(); /* Forces the compiler to check the value of useless_ptr, so that it must call array_idx (which is marked as pure, and gcc was desperate to optimise out. Of course, gcc is unaware stage is never UINT_MAX.*/ return (useless_ptr != NULL && stage == UINT_MAX) ? FATAL_TEST_FAILURE : FATAL_TEST_FINISHED; } dovecot-2.2.33.2/src/lib/test-base32.c0000644000175000017500000001123213165463624014073 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "base32.h" static void test_base32_encode(void) { static const char *input[] = { "toedeledokie!!", "bye bye world", "hoeveel onzin kun je testen?????", "c'est pas vrai! ", "dit is het einde van deze test" }; static const char *output[] = { "ORXWKZDFNRSWI33LNFSSCII=", "MJ4WKIDCPFSSA53POJWGI===", "NBXWK5TFMVWCA33OPJUW4IDLOVXCA2TFEB2GK43UMVXD6PZ7H47Q====", "MMTWK43UEBYGC4ZAOZZGC2JBEA======", "MRUXIIDJOMQGQZLUEBSWS3TEMUQHMYLOEBSGK6TFEB2GK43U" }; string_t *str; unsigned int i; test_begin("base32_encode() with padding"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { str_truncate(str, 0); base32_encode(TRUE, input[i], strlen(input[i]), str); test_assert(strcmp(output[i], str_c(str)) == 0); } test_end(); test_begin("base32_encode() no padding"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { const char *p = strchr(output[i], '='); size_t len; if (p == NULL) len = strlen(output[i]); else len = (size_t)(p - output[i]); str_truncate(str, 0); base32_encode(FALSE, input[i], strlen(input[i]), str); test_assert(strncmp(output[i], str_c(str), len) == 0); } test_end(); } static void test_base32hex_encode(void) { static const char *input[] = { "toedeledokie!!", "bye bye world", "hoeveel onzin kun je testen?????", "c'est pas vrai! ", "dit is het einde van deze test" }; static const char *output[] = { "EHNMAP35DHIM8RRBD5II288=", "C9SMA832F5II0TRFE9M68===", "D1NMATJ5CLM20RREF9KMS83BELN20QJ541Q6ASRKCLN3UFPV7SVG====", "CCJMASRK41O62SP0EPP62Q9140======", "CHKN8839ECG6GPBK41IMIRJ4CKG7COBE41I6AUJ541Q6ASRK" }; string_t *str; unsigned int i; test_begin("base32hex_encode() with padding"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { str_truncate(str, 0); base32hex_encode(TRUE, input[i], strlen(input[i]), str); test_assert(strcmp(output[i], str_c(str)) == 0); } test_end(); test_begin("base32hex_encode() no padding"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { const char *p = strchr(output[i], '='); size_t len; if (p == NULL) len = strlen(output[i]); else len = (size_t)(p - output[i]); str_truncate(str, 0); base32hex_encode(FALSE, input[i], strlen(input[i]), str); test_assert(strncmp(output[i], str_c(str), len) == 0); } test_end(); } struct test_base32_decode_output { const char *text; int ret; unsigned int src_pos; }; static void test_base32_decode(void) { static const char *input[] = { "ORXWKZDFNRSWI33LNFSSCII=", "MJ4WKIDCPFSSA53POJWGI===", "NBXWK5TFMVWCA33OPJUW4IDLOVXCA2TFEB2GK43UMVXD6PZ7H47Q====", "MMTWK43UEBYGC4ZAOZZGC2JBEA======", "MRUXIIDJOMQGQZLUEBSWS3TEMUQHMYLOEBSGK6TFEB2GK43U" }; static const struct test_base32_decode_output output[] = { { "toedeledokie!!", 0, 24 }, { "bye bye world", 0, 24 }, { "hoeveel onzin kun je testen?????", 0, 56 }, { "c'est pas vrai! ", 0, 32 }, { "dit is het einde van deze test", 1, 48 }, }; string_t *str; unsigned int i; size_t src_pos; int ret; test_begin("base32_decode()"); str = t_str_new(256); for (i = 0; i < N_ELEMENTS(input); i++) { str_truncate(str, 0); src_pos = 0; ret = base32_decode(input[i], strlen(input[i]), &src_pos, str); test_assert(output[i].ret == ret && strcmp(output[i].text, str_c(str)) == 0 && (src_pos == output[i].src_pos || (output[i].src_pos == UINT_MAX && src_pos == strlen(input[i])))); } test_end(); } static void test_base32_random(void) { string_t *str, *dest; char buf[10]; unsigned int i, j, max; str = t_str_new(256); dest = t_str_new(256); test_begin("padded base32 encode/decode with random input"); for (i = 0; i < 1000; i++) { max = rand() % sizeof(buf); for (j = 0; j < max; j++) buf[j] = rand(); str_truncate(str, 0); str_truncate(dest, 0); base32_encode(TRUE, buf, max, str); test_assert(base32_decode(str_data(str), str_len(str), NULL, dest) >= 0); test_assert(str_len(dest) == max && memcmp(buf, str_data(dest), max) == 0); } test_end(); test_begin("padded base32hex encode/decode with random input"); for (i = 0; i < 1000; i++) { max = rand() % sizeof(buf); for (j = 0; j < max; j++) buf[j] = rand(); str_truncate(str, 0); str_truncate(dest, 0); base32hex_encode(TRUE, buf, max, str); test_assert(base32hex_decode(str_data(str), str_len(str), NULL, dest) >= 0); test_assert(str_len(dest) == max && memcmp(buf, str_data(dest), max) == 0); } test_end(); } void test_base32(void) { test_base32_encode(); test_base32hex_encode(); test_base32_decode(); test_base32_random(); } dovecot-2.2.33.2/src/lib/ioloop-select.c0000644000175000017500000000720213165463624014617 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop-private.h" #ifdef IOLOOP_SELECT #ifdef HAVE_SYS_SELECT_H # include /* According to POSIX 1003.1-2001 */ #endif #include #include struct ioloop_handler_context { int highest_fd; fd_set read_fds, write_fds, except_fds; fd_set tmp_read_fds, tmp_write_fds, tmp_except_fds; }; static void update_highest_fd(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct io_file *io; int max_highest_fd; max_highest_fd = ctx->highest_fd-1; ctx->highest_fd = -1; for (io = ioloop->io_files; io != NULL; io = io->next) { if (io->fd <= ctx->highest_fd) continue; ctx->highest_fd = io->fd; if (ctx->highest_fd == max_highest_fd) break; } } void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count ATTR_UNUSED) { struct ioloop_handler_context *ctx; ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); ctx->highest_fd = -1; FD_ZERO(&ctx->read_fds); FD_ZERO(&ctx->write_fds); FD_ZERO(&ctx->except_fds); } void io_loop_handler_deinit(struct ioloop *ioloop) { i_free(ioloop->handler_context); } void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; int fd = io->fd; i_assert(fd >= 0); if (fd >= FD_SETSIZE) i_fatal("fd %d too large for select()", fd); if ((condition & (IO_READ | IO_ERROR)) != 0) FD_SET(fd, &ctx->read_fds); if ((condition & IO_WRITE) != 0) FD_SET(fd, &ctx->write_fds); FD_SET(fd, &ctx->except_fds); if (io->fd > ctx->highest_fd) ctx->highest_fd = io->fd; } void io_loop_handle_remove(struct io_file *io, bool closed ATTR_UNUSED) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; int fd = io->fd; i_assert(fd >= 0 && fd < FD_SETSIZE); if ((condition & (IO_READ | IO_ERROR)) != 0) FD_CLR(fd, &ctx->read_fds); if ((condition & IO_WRITE) != 0) FD_CLR(fd, &ctx->write_fds); if (!FD_ISSET(fd, &ctx->read_fds) && !FD_ISSET(fd, &ctx->write_fds)) { FD_CLR(fd, &ctx->except_fds); /* check if we removed the highest fd */ if (io->fd == ctx->highest_fd) update_highest_fd(io->io.ioloop); } i_free(io); } #define io_check_condition(ctx, fd, cond) \ ((FD_ISSET((fd), &(ctx)->tmp_read_fds) && ((cond) & (IO_READ|IO_ERROR))) || \ (FD_ISSET((fd), &(ctx)->tmp_write_fds) && ((cond) & IO_WRITE)) || \ (FD_ISSET((fd), &(ctx)->tmp_except_fds))) void io_loop_handler_run_internal(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct timeval tv; struct io_file *io; int ret; /* get the time left for next timeout task */ io_loop_get_wait_time(ioloop, &tv); memcpy(&ctx->tmp_read_fds, &ctx->read_fds, sizeof(fd_set)); memcpy(&ctx->tmp_write_fds, &ctx->write_fds, sizeof(fd_set)); memcpy(&ctx->tmp_except_fds, &ctx->except_fds, sizeof(fd_set)); ret = select(ctx->highest_fd + 1, &ctx->tmp_read_fds, &ctx->tmp_write_fds, &ctx->tmp_except_fds, &tv); if (ret < 0 && errno != EINTR) i_warning("select() : %m"); /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); if (ret <= 0 || !ioloop->running) { /* no I/O events */ return; } io = ioloop->io_files; for (; io != NULL && ret > 0; io = ioloop->next_io_file) { ioloop->next_io_file = io->next; if (io_check_condition(ctx, io->fd, io->io.condition)) { ret--; io_loop_call_io(&io->io); } } } #endif dovecot-2.2.33.2/src/lib/strescape.c0000644000175000017500000001110613123174404014015 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" const char *str_escape(const char *str) { const char *p; string_t *ret; /* see if we need to quote it */ for (p = str; *p != '\0'; p++) { if (IS_ESCAPED_CHAR(*p)) break; } if (*p == '\0') return str; /* quote */ ret = t_str_new((size_t) (p - str) + 128); str_append_n(ret, str, (size_t) (p - str)); for (; *p != '\0'; p++) { if (IS_ESCAPED_CHAR(*p)) str_append_c(ret, '\\'); str_append_c(ret, *p); } return str_c(ret); } void str_append_unescaped(string_t *dest, const void *src, size_t src_size) { const unsigned char *src_c = src; size_t start = 0, i = 0; while (i < src_size) { for (; i < src_size; i++) { if (src_c[i] == '\\') break; } str_append_n(dest, src_c + start, i-start); if (i < src_size) { if (++i == src_size) break; str_append_c(dest, src_c[i++]); } start = i; } } char *str_unescape(char *str) { /* @UNSAFE */ char *dest, *start = str; while (*str != '\\') { if (*str == '\0') return start; str++; } for (dest = str; *str != '\0'; str++) { if (*str == '\\') { str++; if (*str == '\0') break; } *dest++ = *str; } *dest = '\0'; return start; } int str_unescape_next(const char **str, const char **unescaped_r) { const char *p; char *escaped; bool esc_found = FALSE; for (p = *str; *p != '\0'; p++) { if (*p == '"') break; else if (*p == '\\') { if (p[1] == '\0') return -1; esc_found = TRUE; p++; } } if (*p != '"') return -1; escaped = p_strdup_until(unsafe_data_stack_pool, *str, p); *str = p+1; *unescaped_r = !esc_found ? escaped : str_unescape(escaped); return 0; } void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size) { for (size_t i = 0; i < src_size; i++) { switch (src[i]) { case '\000': str_append_c(dest, '\001'); str_append_c(dest, '0'); break; case '\001': str_append_c(dest, '\001'); str_append_c(dest, '1'); break; case '\t': str_append_c(dest, '\001'); str_append_c(dest, 't'); break; case '\r': str_append_c(dest, '\001'); str_append_c(dest, 'r'); break; case '\n': str_append_c(dest, '\001'); str_append_c(dest, 'n'); break; default: str_append_c(dest, src[i]); break; } } } void str_append_tabescaped(string_t *dest, const char *src) { str_append_tabescaped_n(dest, (const unsigned char*)src, strlen(src)); } const char *str_tabescape(const char *str) { string_t *tmp; const char *p; for (p = str; *p != '\0'; p++) { if (*p <= '\r') { tmp = t_str_new(128); str_append_n(tmp, str, p-str); str_append_tabescaped(tmp, p); return str_c(tmp); } } return str; } void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size) { const unsigned char *src_c = src; size_t start = 0, i = 0; while (i < src_size) { for (; i < src_size; i++) { if (src_c[i] == '\001') break; } str_append_n(dest, src_c + start, i-start); if (i < src_size) { i++; if (i < src_size) { switch (src_c[i]) { case '0': str_append_c(dest, '\000'); break; case '1': str_append_c(dest, '\001'); break; case 't': str_append_c(dest, '\t'); break; case 'r': str_append_c(dest, '\r'); break; case 'n': str_append_c(dest, '\n'); break; default: str_append_c(dest, src_c[i]); break; } i++; } } start = i; } } char *str_tabunescape(char *str) { /* @UNSAFE */ char *dest, *start = str; while (*str != '\001') { if (*str == '\0') return start; str++; } for (dest = str; *str != '\0'; str++) { if (*str != '\001') *dest++ = *str; else { str++; if (*str == '\0') break; switch (*str) { case '0': *dest++ = '\000'; break; case '1': *dest++ = '\001'; break; case 't': *dest++ = '\t'; break; case 'r': *dest++ = '\r'; break; case 'n': *dest++ = '\n'; break; default: *dest++ = *str; break; } } } *dest = '\0'; return start; } const char *t_str_tabunescape(const char *str) { if (strchr(str, '\001') == NULL) return str; else return str_tabunescape(t_strdup_noconst(str)); } char **p_strsplit_tabescaped(pool_t pool, const char *str) { char **args; unsigned int i; args = p_strsplit(pool, str, "\t"); for (i = 0; args[i] != NULL; i++) args[i] = str_tabunescape(args[i]); return args; } const char *const *t_strsplit_tabescaped(const char *str) { return (void *)p_strsplit_tabescaped(pool_datastack_create(), str); } dovecot-2.2.33.2/src/lib/ostream-escaped.h0000644000175000017500000000133113123174404015104 00000000000000#ifndef OSTREAM_ESCAPED_H #define OSTREAM_ESCAPED_H /** * Provides escape filter for ostream * This is intended to be used when certain (or all) * characters need to be escaped before sending. * Such usecases are f.ex. * - JSON, ostream_escaped_json_format * - hex, ostream_escaped_hex_format * To implement your own filter, create function * that matches ostream_escaped_escape_formatter_t * and use it as parameter */ typedef void (*ostream_escaped_escape_formatter_t) (string_t *dest, unsigned char chr); void ostream_escaped_hex_format(string_t *dest, unsigned char chr); struct ostream * o_stream_create_escaped(struct ostream *output, ostream_escaped_escape_formatter_t formatter); #endif dovecot-2.2.33.2/src/lib/json-parser.c0000644000175000017500000004231613165463543014311 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "istream.h" #include "hex-dec.h" #include "unichar.h" #include "istream-jsonstr.h" #include "json-parser.h" enum json_state { JSON_STATE_ROOT = 0, JSON_STATE_OBJECT_OPEN, JSON_STATE_OBJECT_KEY, JSON_STATE_OBJECT_COLON, JSON_STATE_OBJECT_VALUE, JSON_STATE_OBJECT_SKIP_STRING, JSON_STATE_OBJECT_NEXT, JSON_STATE_ARRAY_OPEN, JSON_STATE_ARRAY_VALUE, JSON_STATE_ARRAY_SKIP_STRING, JSON_STATE_ARRAY_NEXT, JSON_STATE_ARRAY_NEXT_SKIP, JSON_STATE_VALUE, JSON_STATE_DONE }; struct json_parser { struct istream *input; uoff_t highwater_offset; enum json_parser_flags flags; const unsigned char *start, *end, *data; const char *error; string_t *value; struct istream *strinput; enum json_state state; ARRAY(enum json_state) nesting; unsigned int nested_skip_count; bool skipping; bool seen_eof; }; static int json_parser_read_more(struct json_parser *parser) { uoff_t cur_highwater = parser->input->v_offset + i_stream_get_data_size(parser->input); size_t size; ssize_t ret; i_assert(parser->highwater_offset <= cur_highwater); if (parser->error != NULL) return -1; if (parser->highwater_offset == cur_highwater) { ret = i_stream_read(parser->input); if (ret == -2) { parser->error = "Token too large"; return -1; } if (ret < 0 && !parser->seen_eof && i_stream_get_data_size(parser->input) > 0 && parser->input->stream_errno == 0) { /* call it once more to finish any pending number */ parser->seen_eof = TRUE; } else if (ret <= 0) { return ret; } else { cur_highwater = parser->input->v_offset + i_stream_get_data_size(parser->input); i_assert(parser->highwater_offset < cur_highwater); parser->highwater_offset = cur_highwater; } } parser->start = parser->data = i_stream_get_data(parser->input, &size); parser->end = parser->start + size; i_assert(size > 0); return 1; } static void json_parser_update_input_pos(struct json_parser *parser) { size_t size; if (parser->data == parser->start) return; i_stream_skip(parser->input, parser->data - parser->start); parser->start = parser->data = i_stream_get_data(parser->input, &size); parser->end = parser->start + size; if (size > 0) { /* we skipped over some data and there's still data left. no need to read() the next time. */ parser->highwater_offset = 0; } else { parser->highwater_offset = parser->input->v_offset; } } struct json_parser *json_parser_init(struct istream *input) { return json_parser_init_flags(input, 0); } struct json_parser *json_parser_init_flags(struct istream *input, enum json_parser_flags flags) { struct json_parser *parser; parser = i_new(struct json_parser, 1); parser->input = input; parser->flags = flags; parser->value = str_new(default_pool, 128); i_array_init(&parser->nesting, 8); i_stream_ref(input); if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0) parser->state = JSON_STATE_VALUE; return parser; } int json_parser_deinit(struct json_parser **_parser, const char **error_r) { struct json_parser *parser = *_parser; *_parser = NULL; if (parser->error != NULL) { /* actual parser error */ *error_r = parser->error; } else if (parser->input->stream_errno != 0) { *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(parser->input), i_stream_get_error(parser->input)); } else if (parser->data == parser->end && !i_stream_have_bytes_left(parser->input) && parser->state != JSON_STATE_DONE) { *error_r = "Missing '}'"; } else { *error_r = NULL; } i_stream_unref(&parser->input); array_free(&parser->nesting); str_free(&parser->value); i_free(parser); return *error_r != NULL ? -1 : 0; } static bool json_parse_whitespace(struct json_parser *parser) { for (; parser->data != parser->end; parser->data++) { switch (*parser->data) { case ' ': case '\t': case '\r': case '\n': break; default: json_parser_update_input_pos(parser); return TRUE; } } json_parser_update_input_pos(parser); return FALSE; } static int json_skip_string(struct json_parser *parser) { for (; parser->data != parser->end; parser->data++) { if (*parser->data == '"') { parser->data++; json_parser_update_input_pos(parser); return 1; } if (*parser->data == '\\') { switch (*++parser->data) { case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': break; case 'u': if (parser->end - parser->data < 4) return -1; parser->data += 3; break; default: return -1; } } } json_parser_update_input_pos(parser); return 0; } static int json_parse_string(struct json_parser *parser, bool allow_skip, const char **value_r) { if (*parser->data != '"') return -1; parser->data++; if (parser->skipping && allow_skip) { *value_r = NULL; return json_skip_string(parser); } str_truncate(parser->value, 0); for (; parser->data != parser->end; parser->data++) { if (*parser->data == '"') { parser->data++; *value_r = str_c(parser->value); return 1; } if (*parser->data != '\\') str_append_c(parser->value, *parser->data); else { if (++parser->data == parser->end) return 0; switch (*parser->data) { case '"': case '\\': case '/': str_append_c(parser->value, *parser->data); break; case 'b': str_append_c(parser->value, '\b'); break; case 'f': str_append_c(parser->value, '\f'); break; case 'n': str_append_c(parser->value, '\n'); break; case 'r': str_append_c(parser->value, '\r'); break; case 't': str_append_c(parser->value, '\t'); break; case 'u': parser->data++; if (parser->end - parser->data < 4) { /* wait for more data */ parser->data = parser->end; return 0; } uni_ucs4_to_utf8_c(hex2dec(parser->data, 4), parser->value); parser->data += 3; break; default: return -1; } } } return 0; } static int json_parse_digits(struct json_parser *parser) { if (parser->data == parser->end) return 0; if (*parser->data < '0' || *parser->data > '9') return -1; while (parser->data != parser->end && *parser->data >= '0' && *parser->data <= '9') str_append_c(parser->value, *parser->data++); return 1; } static int json_parse_int(struct json_parser *parser) { int ret; if (*parser->data == '-') { str_append_c(parser->value, *parser->data++); if (parser->data == parser->end) return 0; } if (*parser->data == '0') str_append_c(parser->value, *parser->data++); else { if ((ret = json_parse_digits(parser)) <= 0) return ret; } return 1; } static int json_parse_number(struct json_parser *parser, const char **value_r) { int ret; str_truncate(parser->value, 0); if ((ret = json_parse_int(parser)) <= 0) return ret; if (parser->data != parser->end && *parser->data == '.') { /* frac */ str_append_c(parser->value, *parser->data++); if ((ret = json_parse_digits(parser)) <= 0) return ret; } if (parser->data != parser->end && (*parser->data == 'e' || *parser->data == 'E')) { /* exp */ str_append_c(parser->value, *parser->data++); if (parser->data == parser->end) return 0; if (*parser->data == '+' || *parser->data == '-') str_append_c(parser->value, *parser->data++); if ((ret = json_parse_digits(parser)) <= 0) return ret; } if (parser->data == parser->end && !parser->input->eof) return 0; *value_r = str_c(parser->value); return 1; } static int json_parse_atom(struct json_parser *parser, const char *atom) { size_t avail, len = strlen(atom); avail = parser->end - parser->data; if (avail < len) { if (memcmp(parser->data, atom, avail) != 0) return -1; /* everything matches so far, but we need more data */ parser->data += avail; return 0; } if (memcmp(parser->data, atom, len) != 0) return -1; parser->data += len; return 1; } static int json_parse_denest(struct json_parser *parser) { const enum json_state *nested_states; unsigned count; parser->data++; json_parser_update_input_pos(parser); nested_states = array_get(&parser->nesting, &count); i_assert(count > 0); if (count == 1) { /* closing root */ parser->state = JSON_STATE_DONE; if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0) return 0; /* we want to return the ending "]" or "}" to caller */ return 1; } /* closing a nested object */ parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; array_delete(&parser->nesting, count-1, 1); if (parser->nested_skip_count > 0) { parser->nested_skip_count--; return 0; } return 1; } static int json_parse_close_object(struct json_parser *parser, enum json_type *type_r) { if (json_parse_denest(parser) == 0) return 0; *type_r = JSON_TYPE_OBJECT_END; return 1; } static int json_parse_close_array(struct json_parser *parser, enum json_type *type_r) { if (json_parse_denest(parser) == 0) return 0; *type_r = JSON_TYPE_ARRAY_END; return 1; } static void json_parser_object_open(struct json_parser *parser) { parser->data++; parser->state = JSON_STATE_OBJECT_OPEN; array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); } static int json_try_parse_next(struct json_parser *parser, enum json_type *type_r, const char **value_r) { bool skipping = parser->skipping; int ret; if (!json_parse_whitespace(parser)) return -1; switch (parser->state) { case JSON_STATE_ROOT: if (*parser->data != '{') { parser->error = "Object doesn't begin with '{'"; return -1; } json_parser_object_open(parser); return 0; case JSON_STATE_OBJECT_VALUE: case JSON_STATE_ARRAY_VALUE: case JSON_STATE_VALUE: if (*parser->data == '{') { json_parser_object_open(parser); if (parser->skipping) { parser->nested_skip_count++; return 0; } *type_r = JSON_TYPE_OBJECT; return 1; } else if (*parser->data == '[') { parser->data++; parser->state = JSON_STATE_ARRAY_OPEN; array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); if (parser->skipping) { parser->nested_skip_count++; return 0; } *type_r = JSON_TYPE_ARRAY; return 1; } if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) { *type_r = JSON_TYPE_STRING; } else if ((ret = json_parse_number(parser, value_r)) >= 0) { *type_r = JSON_TYPE_NUMBER; } else if ((ret = json_parse_atom(parser, "true")) >= 0) { *type_r = JSON_TYPE_TRUE; *value_r = "true"; } else if ((ret = json_parse_atom(parser, "false")) >= 0) { *type_r = JSON_TYPE_FALSE; *value_r = "false"; } else if ((ret = json_parse_atom(parser, "null")) >= 0) { *type_r = JSON_TYPE_NULL; *value_r = NULL; } else { parser->error = "Invalid data as value"; return -1; } if (ret == 0) { i_assert(parser->data == parser->end); if (parser->skipping && *type_r == JSON_TYPE_STRING) { /* a large string that we want to skip over. */ json_parser_update_input_pos(parser); parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; return 0; } return -1; } switch (parser->state) { case JSON_STATE_OBJECT_VALUE: parser->state = JSON_STATE_OBJECT_NEXT; break; case JSON_STATE_ARRAY_VALUE: parser->state = JSON_STATE_ARRAY_NEXT; break; case JSON_STATE_VALUE: parser->state = JSON_STATE_DONE; break; default: i_unreached(); } break; case JSON_STATE_OBJECT_OPEN: if (*parser->data == '}') return json_parse_close_object(parser, type_r); parser->state = JSON_STATE_OBJECT_KEY; /* fall through */ case JSON_STATE_OBJECT_KEY: if (json_parse_string(parser, FALSE, value_r) <= 0) { parser->error = "Expected string as object key"; return -1; } *type_r = JSON_TYPE_OBJECT_KEY; parser->state = JSON_STATE_OBJECT_COLON; break; case JSON_STATE_OBJECT_COLON: if (*parser->data != ':') { parser->error = "Expected ':' after key"; return -1; } parser->data++; parser->state = JSON_STATE_OBJECT_VALUE; json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_NEXT: if (parser->skipping && parser->nested_skip_count == 0) { /* we skipped over the previous value */ parser->skipping = FALSE; } if (*parser->data == '}') return json_parse_close_object(parser, type_r); if (*parser->data != ',') { parser->error = "Expected ',' or '}' after object value"; return -1; } parser->state = JSON_STATE_OBJECT_KEY; parser->data++; json_parser_update_input_pos(parser); return 0; case JSON_STATE_ARRAY_OPEN: if (*parser->data == ']') return json_parse_close_array(parser, type_r); parser->state = JSON_STATE_ARRAY_VALUE; return 0; case JSON_STATE_ARRAY_NEXT: if (parser->skipping && parser->nested_skip_count == 0) { /* we skipped over the previous value */ parser->skipping = FALSE; } /* fall through */ case JSON_STATE_ARRAY_NEXT_SKIP: if (*parser->data == ']') return json_parse_close_array(parser, type_r); if (*parser->data != ',') { parser->error = "Expected ',' or '}' after array value"; return -1; } parser->state = JSON_STATE_ARRAY_VALUE; parser->data++; json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_SKIP_STRING: case JSON_STATE_ARRAY_SKIP_STRING: if (json_skip_string(parser) <= 0) return -1; parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; return 0; case JSON_STATE_DONE: parser->error = "Unexpected data at the end"; return -1; } json_parser_update_input_pos(parser); return skipping ? 0 : 1; } int json_parse_next(struct json_parser *parser, enum json_type *type_r, const char **value_r) { int ret; i_assert(parser->strinput == NULL); *value_r = NULL; while ((ret = json_parser_read_more(parser)) > 0) { while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0) ; if (ret > 0) break; if (parser->data != parser->end) return -1; /* parsing probably failed because there wasn't enough input. reset the error and try reading more. */ parser->error = NULL; parser->highwater_offset = parser->input->v_offset + i_stream_get_data_size(parser->input); } return ret; } void json_parse_skip_next(struct json_parser *parser) { i_assert(!parser->skipping); i_assert(parser->strinput == NULL); i_assert(parser->state == JSON_STATE_OBJECT_COLON || parser->state == JSON_STATE_OBJECT_VALUE || parser->state == JSON_STATE_ARRAY_VALUE || parser->state == JSON_STATE_ARRAY_NEXT); parser->skipping = TRUE; if (parser->state == JSON_STATE_ARRAY_NEXT) parser->state = JSON_STATE_ARRAY_NEXT_SKIP; } static void json_strinput_destroyed(struct json_parser *parser) { i_assert(parser->strinput != NULL); parser->strinput = NULL; } static int json_try_parse_stream_start(struct json_parser *parser, struct istream **input_r) { if (!json_parse_whitespace(parser)) return -1; if (parser->state == JSON_STATE_OBJECT_COLON) { if (*parser->data != ':') { parser->error = "Expected ':' after key"; return -1; } parser->data++; parser->state = JSON_STATE_OBJECT_VALUE; if (!json_parse_whitespace(parser)) return -1; } if (*parser->data != '"') return -1; parser->data++; json_parser_update_input_pos(parser); parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; parser->strinput = i_stream_create_jsonstr(parser->input); i_stream_add_destroy_callback(parser->strinput, json_strinput_destroyed, parser); *input_r = parser->strinput; return 0; } int json_parse_next_stream(struct json_parser *parser, struct istream **input_r) { int ret; i_assert(!parser->skipping); i_assert(parser->strinput == NULL); i_assert(parser->state == JSON_STATE_OBJECT_COLON || parser->state == JSON_STATE_OBJECT_VALUE || parser->state == JSON_STATE_ARRAY_VALUE); *input_r = NULL; while ((ret = json_parser_read_more(parser)) > 0) { if (json_try_parse_stream_start(parser, input_r) == 0) break; if (parser->data != parser->end) return -1; /* parsing probably failed because there wasn't enough input. reset the error and try reading more. */ parser->error = NULL; parser->highwater_offset = parser->input->v_offset + i_stream_get_data_size(parser->input); } return ret; } static void json_append_escaped_char(string_t *dest, unsigned char src) { switch (src) { case '\b': str_append(dest, "\\b"); break; case '\f': str_append(dest, "\\f"); break; case '\n': str_append(dest, "\\n"); break; case '\r': str_append(dest, "\\r"); break; case '\t': str_append(dest, "\\t"); break; case '"': str_append(dest, "\\\""); break; case '\\': str_append(dest, "\\\\"); break; default: if (src < 32) str_printfa(dest, "\\u%04x", src); else str_append_c(dest, src); break; } } void ostream_escaped_json_format(string_t *dest, unsigned char src) { json_append_escaped_char(dest, src); } void json_append_escaped(string_t *dest, const char *src) { for (; *src != '\0'; src++) json_append_escaped_char(dest, *src); } void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size) { size_t i; for (i = 0; i < size; i++) json_append_escaped_char(dest, src[i]); } dovecot-2.2.33.2/src/lib/fdatasync-path.h0000644000175000017500000000024713123174404014743 00000000000000#ifndef FDATASYNC_PATH_H #define FDATASYNC_PATH_H /* Open and fdatasync() the path. Works for files and directories. */ int fdatasync_path(const char *path); #endif dovecot-2.2.33.2/src/lib/lib.c0000644000175000017500000001102013165463624012600 00000000000000/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dovecot-version.h" #include "array.h" #include "env-util.h" #include "hostpid.h" #include "fd-close-on-exec.h" #include "ipwd.h" #include "process-title.h" #include "var-expand-private.h" #include #include #include /* Mainly for including the full version information in core dumps. NOTE: Don't set this const - otherwise it won't end up in core dumps. */ char dovecot_build_info[] = DOVECOT_BUILD_INFO; static bool lib_initialized = FALSE; int dev_null_fd = -1; struct atexit_callback { int priority; lib_atexit_callback_t *callback; }; static ARRAY(struct atexit_callback) atexit_callbacks = ARRAY_INIT; int close_keep_errno(int *fd) { int ret, old_errno = errno; i_assert(*fd != -1); ret = close(*fd); *fd = -1; errno = old_errno; return ret; } void fd_close_maybe_stdio(int *fd_in, int *fd_out) { int *fdp[2] = { fd_in, fd_out }; if (*fd_in == *fd_out) *fd_in = -1; for (unsigned int i = 0; i < N_ELEMENTS(fdp); i++) { if (*fdp[i] == -1) ; else if (*fdp[i] > 1) i_close_fd(fdp[i]); else if (dup2(dev_null_fd, *fdp[i]) == *fdp[i]) *fdp[i] = -1; else i_fatal("dup2(/dev/null, %d) failed: %m", *fdp[i]); } } #undef i_unlink int i_unlink(const char *path, const char *source_fname, unsigned int source_linenum) { if (unlink(path) < 0) { i_error("unlink(%s) failed: %m (in %s:%u)", path, source_fname, source_linenum); return -1; } return 0; } #undef i_unlink_if_exists int i_unlink_if_exists(const char *path, const char *source_fname, unsigned int source_linenum) { if (unlink(path) == 0) return 1; else if (errno == ENOENT) return 0; else { i_error("unlink(%s) failed: %m (in %s:%u)", path, source_fname, source_linenum); return -1; } } void i_getopt_reset(void) { #ifdef __GLIBC__ /* a) for subcommands allow -options anywhere in command line b) this is actually required for the reset to work (glibc bug?) */ optind = 0; #else optind = 1; #endif } void lib_atexit(lib_atexit_callback_t *callback) { lib_atexit_priority(callback, 0); } void lib_atexit_priority(lib_atexit_callback_t *callback, int priority) { struct atexit_callback *cb; const struct atexit_callback *callbacks; unsigned int i, count; if (!array_is_created(&atexit_callbacks)) i_array_init(&atexit_callbacks, 8); else { /* skip if it's already added */ callbacks = array_get(&atexit_callbacks, &count); for (i = count; i > 0; i--) { if (callbacks[i-1].callback == callback) { i_assert(callbacks[i-1].priority == priority); return; } } } cb = array_append_space(&atexit_callbacks); cb->priority = priority; cb->callback = callback; } static int atexit_callback_priority_cmp(const struct atexit_callback *cb1, const struct atexit_callback *cb2) { return cb1->priority - cb2->priority; } void lib_atexit_run(void) { const struct atexit_callback *cb; if (array_is_created(&atexit_callbacks)) { array_sort(&atexit_callbacks, atexit_callback_priority_cmp); array_foreach(&atexit_callbacks, cb) (*cb->callback)(); array_free(&atexit_callbacks); } } static void lib_open_non_stdio_dev_null(void) { dev_null_fd = open("/dev/null", O_WRONLY); if (dev_null_fd == -1) i_fatal("open(/dev/null) failed: %m"); /* Make sure stdin, stdout and stderr fds exist. We especially rely on stderr being available and a lot of code doesn't like fd being 0. We'll open /dev/null as write-only also for stdin, since if any reads are attempted from it we'll want them to fail. */ while (dev_null_fd < STDERR_FILENO) { dev_null_fd = dup(dev_null_fd); if (dev_null_fd == -1) i_fatal("dup(/dev/null) failed: %m"); } /* close the actual /dev/null fd on exec*(), but keep it in stdio fds */ fd_close_on_exec(dev_null_fd, TRUE); } void lib_init(void) { struct timeval tv; i_assert(!lib_initialized); /* standard way to get rand() return different values. */ if (gettimeofday(&tv, NULL) < 0) i_fatal("gettimeofday(): %m"); rand_set_seed((unsigned int) (tv.tv_sec ^ tv.tv_usec ^ getpid())); data_stack_init(); hostpid_init(); lib_open_non_stdio_dev_null(); var_expand_extensions_init(); lib_initialized = TRUE; } bool lib_is_initialized(void) { return lib_initialized; } void lib_deinit(void) { i_assert(lib_initialized); lib_initialized = FALSE; lib_atexit_run(); ipwd_deinit(); hostpid_deinit(); var_expand_extensions_deinit(); i_close_fd(&dev_null_fd); data_stack_deinit(); env_deinit(); failures_deinit(); process_title_deinit(); } dovecot-2.2.33.2/src/lib/randgen.h0000644000175000017500000000101613165463624013461 00000000000000#ifndef RANDGEN_H #define RANDGEN_H /* Fill given buffer with semi-strong randomness, usually from /dev/urandom. */ void random_fill(void *buf, size_t size); /* Fill given buffer with weak randomness, ie. with rand(). This is better if no real randomness is required, as reading from /dev/urandom usually also consumes /dev/random entropy, which may disturb other processes. */ void random_fill_weak(void *buf, size_t size); /* may be called multiple times */ void random_init(void); void random_deinit(void); #endif dovecot-2.2.33.2/src/lib/imem.h0000644000175000017500000000231313165463624012773 00000000000000#ifndef IMEM_H #define IMEM_H /* For easy allocation of memory from default memory pool. */ extern pool_t default_pool; #define i_new(type, count) p_new(default_pool, type, count) #define i_realloc_type(mem, type, old_count, new_count) \ p_realloc_type(default_pool, mem, type, old_count, new_count) void *i_malloc(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; void *i_realloc(void *mem, size_t old_size, size_t new_size) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; #define i_free(mem) p_free(default_pool, mem) #define i_free_and_null(mem) p_free_and_null(default_pool, mem) /* string functions */ char *i_strdup(const char *str) ATTR_MALLOC; /* like i_strdup(), but if str == "", return NULL */ char *i_strdup_empty(const char *str) ATTR_MALLOC; /* *end isn't included */ char *i_strdup_until(const void *str, const void *end) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *i_strndup(const void *str, size_t max_chars) ATTR_MALLOC; char *i_strdup_printf(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *i_strdup_vprintf(const char *format, va_list args) ATTR_FORMAT(1, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; char *i_strconcat(const char *str1, ...) ATTR_SENTINEL ATTR_MALLOC; #endif dovecot-2.2.33.2/src/lib/priorityq.h0000644000175000017500000000301613123174404014074 00000000000000#ifndef PRIORITYQ_H #define PRIORITYQ_H /* Priority queue implementation using heap. The items you add to the queue must begin with a struct priorityq_item. This is necessary for priorityq_remove() to work fast. */ struct priorityq_item { /* Current index in the queue array, updated automatically. */ unsigned int idx; /* [your own data] */ }; /* Returns <0, 0 or >0 */ typedef int priorityq_cmp_callback_t(const void *p1, const void *p2); /* Create a new priority queue. Callback is used to compare added items. */ struct priorityq * priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size); void priorityq_deinit(struct priorityq **pq); /* Return number of items in the queue. */ unsigned int priorityq_count(const struct priorityq *pq) ATTR_PURE; /* Add a new item to the queue. */ void priorityq_add(struct priorityq *pq, struct priorityq_item *item); /* Remove the specified item from the queue. */ void priorityq_remove(struct priorityq *pq, struct priorityq_item *item); /* Return the item with the highest priority. Returns NULL if queue is empty. */ struct priorityq_item *priorityq_peek(struct priorityq *pq); /* Like priorityq_peek(), but also remove the returned item from the queue. */ struct priorityq_item *priorityq_pop(struct priorityq *pq); /* Returns array containing all the priorityq_items. Only the first item is guaranteed to be the highest priority item, the rest can't be assumed to be in any order. */ struct priorityq_item *const *priorityq_items(struct priorityq *pq); #endif dovecot-2.2.33.2/src/lib/process-title.h0000644000175000017500000000061613165463624014645 00000000000000#ifndef PROCESS_TITLE_H #define PROCESS_TITLE_H /* Initialize title changing. */ void process_title_init(char **argv[]); /* Change the process title if possible. */ void process_title_set(const char *title); /* Free all memory used by process title hacks. This should be the last function called by the process, since it frees argv and environment. */ void process_title_deinit(void); #endif dovecot-2.2.33.2/src/lib/ioloop-notify-inotify.c0000644000175000017500000001402613165463624016331 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #define _GNU_SOURCE #include "lib.h" #ifdef IOLOOP_NOTIFY_INOTIFY #include "fd-close-on-exec.h" #include "fd-set-nonblock.h" #include "ioloop-private.h" #include "ioloop-notify-fd.h" #include "buffer.h" #include "net.h" #include "ipwd.h" #include #include #include #include #include #define INOTIFY_BUFLEN (32*1024) struct ioloop_notify_handler_context { struct ioloop_notify_fd_context fd_ctx; int inotify_fd; struct io *event_io; bool disabled; }; static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void); static bool inotify_input_more(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; const struct inotify_event *event; unsigned char event_buf[INOTIFY_BUFLEN]; struct io_notify *io; ssize_t ret, pos; /* read as many events as there is available and fit into our buffer. only full events are returned by the kernel. */ ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf)); if (ret <= 0) { if (ret == 0 || errno == EAGAIN) { /* nothing more to read */ return FALSE; } i_fatal("read(inotify) failed: %m"); } if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); ioloop_time = ioloop_timeval.tv_sec; for (pos = 0; pos < ret; ) { if ((size_t)(ret - pos) < sizeof(*event)) break; event = (struct inotify_event *)(event_buf + pos); i_assert(event->len < (size_t)ret); pos += sizeof(*event) + event->len; io = io_notify_fd_find(&ctx->fd_ctx, event->wd); if (io != NULL) { if ((event->mask & IN_IGNORED) != 0) { /* calling inotify_rm_watch() would now give EINVAL */ io->fd = -1; } io_loop_call_io(&io->io); } } if (pos != ret) i_error("read(inotify) returned partial event"); return (size_t)ret >= sizeof(event_buf)-512; } static void inotify_input(struct ioloop *ioloop) { while (inotify_input_more(ioloop)) ; } #undef io_add_notify enum io_notify_result io_add_notify(const char *path, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context, struct io **io_r) { struct ioloop_notify_handler_context *ctx = current_ioloop->notify_handler_context; int wd; *io_r = NULL; if (ctx == NULL) ctx = io_loop_notify_handler_init(); if (ctx->disabled) return IO_NOTIFY_NOSUPPORT; wd = inotify_add_watch(ctx->inotify_fd, path, IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE | IN_MODIFY); if (wd < 0) { /* ESTALE could happen with NFS. Don't bother giving an error message then. */ if (errno == ENOENT || errno == ESTALE) return IO_NOTIFY_NOTFOUND; if (errno != ENOSPC) i_error("inotify_add_watch(%s) failed: %m", path); else { i_warning("Inotify watch limit for user exceeded, " "disabling. Increase " "/proc/sys/fs/inotify/max_user_watches"); } ctx->disabled = TRUE; return IO_NOTIFY_NOSUPPORT; } if (ctx->event_io == NULL) { ctx->event_io = io_add(ctx->inotify_fd, IO_READ, inotify_input, current_ioloop); } *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context); (*io_r)->source_filename = source_filename; (*io_r)->source_linenum = source_linenum; return IO_NOTIFY_ADDED; } void io_loop_notify_remove(struct io *_io) { struct ioloop_notify_handler_context *ctx = _io->ioloop->notify_handler_context; struct io_notify *io = (struct io_notify *)_io; if (io->fd != -1) { /* ernro=EINVAL happens if the file itself is deleted and kernel has sent IN_IGNORED event which we haven't read. */ if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 && errno != EINVAL) i_error("inotify_rm_watch() failed: %m"); } io_notify_fd_free(&ctx->fd_ctx, io); if (ctx->fd_ctx.notifies == NULL && ctx->event_io != NULL) io_remove(&ctx->event_io); } static void ioloop_inotify_user_limit_exceeded(void) { struct passwd pw; const char *name; uid_t uid = geteuid(); if (i_getpwuid(uid, &pw) <= 0) name = t_strdup_printf("UID %s", dec2str(uid)); else { name = t_strdup_printf("%s (UID %s)", dec2str(uid), pw.pw_name); } i_warning("Inotify instance limit for user %s exceeded, disabling. " "Increase /proc/sys/fs/inotify/max_user_instances", name); } static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void) { struct ioloop *ioloop = current_ioloop; struct ioloop_notify_handler_context *ctx; ctx = ioloop->notify_handler_context = i_new(struct ioloop_notify_handler_context, 1); ctx->inotify_fd = inotify_init(); if (ctx->inotify_fd == -1) { if (errno != EMFILE) i_error("inotify_init() failed: %m"); else ioloop_inotify_user_limit_exceeded(); ctx->disabled = TRUE; } else { fd_close_on_exec(ctx->inotify_fd, TRUE); fd_set_nonblock(ctx->inotify_fd, TRUE); } return ctx; } void io_loop_notify_handler_deinit(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; while (ctx->fd_ctx.notifies != NULL) { struct io_notify *io = ctx->fd_ctx.notifies; struct io *_io = &io->io; i_warning("I/O notify leak: %p (%s:%u, fd %d)", (void *)_io->callback, _io->source_filename, _io->source_linenum, io->fd); io_remove(&_io); } if (ctx->inotify_fd != -1) { if (close(ctx->inotify_fd) < 0) i_error("close(inotify) failed: %m"); ctx->inotify_fd = -1; } i_free(ctx); } int io_loop_extract_notify_fd(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; struct io_notify *io; int fd, new_inotify_fd; if (ctx == NULL || ctx->inotify_fd == -1) return -1; new_inotify_fd = inotify_init(); if (new_inotify_fd == -1) { if (errno != EMFILE) i_error("inotify_init() failed: %m"); else ioloop_inotify_user_limit_exceeded(); return -1; } for (io = ctx->fd_ctx.notifies; io != NULL; io = io->next) io->fd = -1; if (ctx->event_io != NULL) io_remove(&ctx->event_io); fd = ctx->inotify_fd; ctx->inotify_fd = new_inotify_fd; return fd; } #endif dovecot-2.2.33.2/src/lib/module-context.h0000644000175000017500000000604113123174404015002 00000000000000#ifndef MODULE_CONTEXT_H #define MODULE_CONTEXT_H #include "array.h" /* This is a bit complex to use, but it prevents using wrong module IDs in module_contexts arrays. --------- The main structure is implemented like this: struct STRUCT_NAME_module_register { unsigned int id; }; union STRUCT_NAME_module_context { struct STRUCT_NAME_module_register *reg; // it's allowed to have some structure here so it won't waste space. // for example: struct STRUCT_NAME_vfuncs super; }; struct STRUCT_NAME { ARRAY(union STRUCT_NAME_module_context *) module_contexts; }; extern struct STRUCT_NAME_module_register STRUCT_NAME_module_register; --------- The usage in modules goes like: static MODULE_CONTEXT_DEFINE(mymodule_STRUCT_NAME_module, &STRUCT_NAME_module_register); struct mymodule_STRUCT_NAME { union STRUCT_NAME_module_context module_ctx; // module-specific data }; struct mymodule_STRUCT_NAME *ctx = i_new(...); MODULE_CONTEXT_SET(obj, mymodule_STRUCT_NAME_module, ctx); struct mymodule_STRUCT_NAME *ctx = MODULE_CONTEXT(obj, mymodule_STRUCT_NAME_module); */ #define OBJ_REGISTER(obj) \ ((**(obj)->module_contexts.v)->reg) #define OBJ_REGISTER_COMPATIBLE(obj, id_ctx) \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(OBJ_REGISTER(obj), (id_ctx).reg) #define MODULE_CONTEXT(obj, id_ctx) \ (*((void **)array_idx_modifiable(&(obj)->module_contexts, \ module_get_context_id(&(id_ctx).id)) + \ OBJ_REGISTER_COMPATIBLE(obj, id_ctx))) #ifdef HAVE_TYPEOF # define MODULE_CONTEXT_DEFINE(_name, _reg) \ struct _name { \ struct module_context_id id; \ typeof(_reg) reg; \ } _name # define MODULE_CONTEXT_INIT(_reg) \ { { &(_reg)->id, 0, FALSE }, NULL } #else # define MODULE_CONTEXT_DEFINE(_name, _reg) \ struct _name { \ struct module_context_id id; \ } _name # define MODULE_CONTEXT_INIT(_reg) \ { { &(_reg)->id, 0, FALSE } } #endif #define MODULE_CONTEXT_DEFINE_INIT(_name, _reg) \ MODULE_CONTEXT_DEFINE(_name, _reg) = MODULE_CONTEXT_INIT(_reg) struct module_context_id { unsigned int *module_id_register; unsigned int module_id; bool module_id_set; }; static inline unsigned int module_get_context_id(struct module_context_id *id) { if (!id->module_id_set) { id->module_id = *id->module_id_register; id->module_id_set = TRUE; *id->module_id_register += 1; } return id->module_id; } #define MODULE_CONTEXT_SET_FULL(obj, id_ctx, ctx, module_ctx) STMT_START { \ void *_module_tmp = ctx + \ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(module_ctx, \ (**(obj)->module_contexts.v)) + \ OBJ_REGISTER_COMPATIBLE(obj, id_ctx); \ array_idx_set_i(&(obj)->module_contexts.arr, \ module_get_context_id(&(id_ctx).id), &_module_tmp); \ } STMT_END #define MODULE_CONTEXT_SET(obj, id_ctx, context) \ MODULE_CONTEXT_SET_FULL(obj, id_ctx, context, &(context)->module_ctx) #define MODULE_CONTEXT_SET_SELF(obj, id_ctx, context) \ MODULE_CONTEXT_SET_FULL(obj, id_ctx, context, context) #define MODULE_CONTEXT_UNSET(obj, id_ctx) \ array_idx_clear(&(obj)->module_contexts, (id_ctx).id.module_id) #endif dovecot-2.2.33.2/src/lib/istream-file.c0000644000175000017500000001515013165463624014423 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* @UNSAFE: whole file */ #include "lib.h" #include "ioloop.h" #include "istream-file-private.h" #include "net.h" #include #include #include #include void i_stream_file_close(struct iostream_private *stream, bool close_parent ATTR_UNUSED) { struct file_istream *fstream = (struct file_istream *)stream; struct istream_private *_stream = (struct istream_private *)stream; if (fstream->autoclose_fd && _stream->fd != -1) { if (close(_stream->fd) < 0) { i_error("file_istream.close(%s) failed: %m", i_stream_get_name(&_stream->istream)); } } _stream->fd = -1; } static int i_stream_file_open(struct istream_private *stream) { const char *path = i_stream_get_name(&stream->istream); stream->fd = open(path, O_RDONLY); if (stream->fd == -1) { io_stream_set_error(&stream->iostream, "open(%s) failed: %m", path); stream->istream.stream_errno = errno; return -1; } return 0; } ssize_t i_stream_file_read(struct istream_private *stream) { struct file_istream *fstream = (struct file_istream *) stream; uoff_t offset; size_t size; ssize_t ret; if (!i_stream_try_alloc(stream, 1, &size)) return -2; if (stream->fd == -1) { if (i_stream_file_open(stream) < 0) return -1; i_assert(stream->fd != -1); } offset = stream->istream.v_offset + (stream->pos - stream->skip); do { if (fstream->file) { ret = pread(stream->fd, stream->w_buffer + stream->pos, size, offset); } else if (fstream->seen_eof) { /* don't try to read() again. EOF from keyboard (^D) requires this to work right. */ ret = 0; } else { ret = read(stream->fd, stream->w_buffer + stream->pos, size); } } while (unlikely(ret < 0 && errno == EINTR && stream->istream.blocking)); if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; fstream->seen_eof = TRUE; return -1; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) { i_assert(!stream->istream.blocking); ret = 0; } else { i_assert(errno != 0); /* if we get EBADF for a valid fd, it means something's really wrong and we'd better just crash. */ i_assert(errno != EBADF); if (fstream->file) { io_stream_set_error(&stream->iostream, "pread(size=%"PRIuSIZE_T " offset=%"PRIuUOFF_T") failed: %m", size, offset); } else { io_stream_set_error(&stream->iostream, "read(size=%"PRIuSIZE_T") failed: %m", size); } stream->istream.stream_errno = errno; return -1; } } if (ret > 0 && fstream->skip_left > 0) { i_assert(!fstream->file); i_assert(stream->skip == stream->pos); if (fstream->skip_left >= (size_t)ret) { fstream->skip_left -= ret; ret = 0; } else { ret -= fstream->skip_left; stream->pos += fstream->skip_left; stream->skip += fstream->skip_left; fstream->skip_left = 0; } } stream->pos += ret; i_assert(ret != 0 || !fstream->file); i_assert(ret != -1); return ret; } static void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct file_istream *fstream = (struct file_istream *) stream; if (!stream->istream.seekable) { if (v_offset < stream->istream.v_offset) i_panic("stream doesn't support seeking backwards"); fstream->skip_left += v_offset - stream->istream.v_offset; } stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; fstream->seen_eof = FALSE; } static void i_stream_file_sync(struct istream_private *stream) { if (!stream->istream.seekable) { /* can't do anything or data would be lost */ return; } stream->skip = stream->pos = 0; stream->istream.eof = FALSE; } static int i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct file_istream *fstream = (struct file_istream *) stream; const char *name = i_stream_get_name(&stream->istream); if (!fstream->file) { /* return defaults */ } else if (stream->fd != -1) { if (fstat(stream->fd, &stream->statbuf) < 0) { stream->istream.stream_errno = errno; io_stream_set_error(&stream->iostream, "file_istream.fstat(%s) failed: %m", name); i_error("%s", i_stream_get_error(&stream->istream)); return -1; } } else { if (stat(name, &stream->statbuf) < 0) { stream->istream.stream_errno = errno; io_stream_set_error(&stream->iostream, "file_istream.stat(%s) failed: %m", name); i_error("%s", i_stream_get_error(&stream->istream)); return -1; } } return 0; } struct istream * i_stream_create_file_common(struct file_istream *fstream, int fd, const char *path, size_t max_buffer_size, bool autoclose_fd) { struct istream *input; struct stat st; bool is_file; fstream->autoclose_fd = autoclose_fd; fstream->istream.iostream.close = i_stream_file_close; fstream->istream.max_buffer_size = max_buffer_size; fstream->istream.read = i_stream_file_read; fstream->istream.seek = i_stream_file_seek; fstream->istream.sync = i_stream_file_sync; fstream->istream.stat = i_stream_file_stat; /* if it's a file, set the flags properly */ if (fd == -1) is_file = TRUE; else if (fstat(fd, &st) < 0) is_file = FALSE; else if (S_ISREG(st.st_mode)) is_file = TRUE; else if (!S_ISDIR(st.st_mode)) is_file = FALSE; else { /* we're trying to open a directory. we're not designed for it. */ io_stream_set_error(&fstream->istream.iostream, "%s is a directory, can't read it as file", path != NULL ? path : t_strdup_printf("", fd)); fstream->istream.istream.stream_errno = EISDIR; is_file = FALSE; } if (is_file) { fstream->file = TRUE; fstream->istream.istream.blocking = TRUE; fstream->istream.istream.seekable = TRUE; } fstream->istream.istream.readable_fd = TRUE; input = i_stream_create(&fstream->istream, NULL, fd); i_stream_set_name(input, is_file ? "(file)" : "(fd)"); return input; } struct istream *i_stream_create_fd(int fd, size_t max_buffer_size, bool autoclose_fd) { struct file_istream *fstream; i_assert(fd != -1); fstream = i_new(struct file_istream, 1); return i_stream_create_file_common(fstream, fd, NULL, max_buffer_size, autoclose_fd); } struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) { struct istream *input; input = i_stream_create_fd(*fd, max_buffer_size, TRUE); *fd = -1; return input; } struct istream *i_stream_create_file(const char *path, size_t max_buffer_size) { struct file_istream *fstream; struct istream *input; fstream = i_new(struct file_istream, 1); input = i_stream_create_file_common(fstream, -1, path, max_buffer_size, TRUE); i_stream_set_name(input, path); return input; } dovecot-2.2.33.2/src/lib/execv-const.c0000644000175000017500000000147613123174404014273 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "execv-const.h" #include static char **argv_drop_const(const char *const argv[]) { char **ret; unsigned int i, count; for (count = 0; argv[count] != NULL; count++) ; ret = t_new(char *, count + 1); for (i = 0; i < count; i++) ret[i] = t_strdup_noconst(argv[i]); return ret; } void execv_const(const char *path, const char *const argv[]) { (void)execv(path, argv_drop_const(argv)); i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC, "execv(%s) failed: %m", path); } void execvp_const(const char *file, const char *const argv[]) { (void)execvp(file, argv_drop_const(argv)); i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC, "execvp(%s) failed: %m", file); } dovecot-2.2.33.2/src/lib/restrict-process-size.c0000644000175000017500000000416013123174404016311 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "restrict-process-size.h" #include void restrict_process_size(rlim_t bytes) { struct rlimit rlim; rlim.rlim_max = rlim.rlim_cur = bytes; if (setrlimit(RLIMIT_DATA, &rlim) < 0) { i_fatal("setrlimit(RLIMIT_DATA, %llu): %m", (unsigned long long)bytes); } #ifdef HAVE_RLIMIT_AS if (setrlimit(RLIMIT_AS, &rlim) < 0) { i_fatal("setrlimit(RLIMIT_AS, %llu): %m", (unsigned long long)bytes); } #endif } void restrict_process_count(rlim_t count ATTR_UNUSED) { #ifdef HAVE_RLIMIT_NPROC struct rlimit rlim; rlim.rlim_max = rlim.rlim_cur = count; if (setrlimit(RLIMIT_NPROC, &rlim) < 0) { i_error("setrlimit(RLIMIT_NPROC, %llu): %m", (unsigned long long)count); } #endif } void restrict_fd_limit(rlim_t count) { #ifdef HAVE_SETRLIMIT struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = count; if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { i_error("setrlimit(RLIMIT_NOFILE, %llu): %m", (unsigned long long)count); } #endif } int restrict_get_process_size(rlim_t *limit_r) { struct rlimit rlim; #ifdef HAVE_RLIMIT_AS if (getrlimit(RLIMIT_AS, &rlim) < 0) { i_error("getrlimit(RLIMIT_AS): %m"); return -1; } #else if (getrlimit(RLIMIT_DATA, &rlim) < 0) { i_error("getrlimit(RLIMIT_DATA): %m"); return -1; } #endif *limit_r = rlim.rlim_cur; return 0; } int restrict_get_core_limit(rlim_t *limit_r) { #ifdef HAVE_RLIMIT_CORE struct rlimit rlim; if (getrlimit(RLIMIT_CORE, &rlim) < 0) { i_error("getrlimit(RLIMIT_CORE) failed: %m"); return -1; } *limit_r = rlim.rlim_cur; return 0; #else return -1; #endif } int restrict_get_process_limit(rlim_t *limit_r) { #ifdef HAVE_RLIMIT_NPROC struct rlimit rlim; if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { i_error("getrlimit(RLIMIT_NPROC) failed: %m"); return -1; } *limit_r = rlim.rlim_cur; return 0; #else return -1; #endif } int restrict_get_fd_limit(rlim_t *limit_r) { struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { i_error("getrlimit(RLIMIT_NOFILE) failed: %m"); return -1; } *limit_r = rlim.rlim_cur; return 0; } dovecot-2.2.33.2/src/lib/test-str-sanitize.c0000644000175000017500000000355213165463624015456 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "str.h" #include "str-sanitize.h" struct str_sanitize_test { const char *str; unsigned int max_len; const char *sanitized; /* NULL for no change */ }; void test_str_sanitize(void) { static struct str_sanitize_test tests[] = { { NULL, 2, NULL }, { "", 2, NULL }, { "a", 2, NULL }, { "ab", 2, NULL }, { "abc", 2, "..." }, { "abcd", 3, "..." }, { "abcde", 4, "a..." }, { "\xD1\x81", 1, "..." }, { "\xD1\x81", 2, "\xD1\x81" }, { "\xD1\x81", 3, NULL }, { "\xC3\xA4\xC3\xA4zyxa", 1, "..." }, { "\xC3\xA4\xC3\xA4zyxa", 2, "..." }, { "\xC3\xA4\xC3\xA4zyxa", 3, "..." }, { "\xC3\xA4\xC3\xA4zyxa", 4, "..." }, { "\xC3\xA4\xC3\xA4zyxa", 5, "\xC3\xA4..." }, { "\xC3\xA4\xC3\xA4zyxa", 6, "\xC3\xA4..." }, { "\xC3\xA4\xC3\xA4zyxa", 7, "\xC3\xA4\xC3\xA4..." }, { "\xC3\xA4\xC3\xA4zyxa", 8, "\xC3\xA4\xC3\xA4zyxa" }, { "\001x\x1fy\x81", 10, "?x?y?" } }; const char *str; string_t *str2; unsigned int i; test_begin("str_sanitize"); for (i = 0; i < N_ELEMENTS(tests); i++) { str = str_sanitize(tests[i].str, tests[i].max_len); if (tests[i].sanitized != NULL) test_assert_idx(null_strcmp(str, tests[i].sanitized) == 0, i); else test_assert_idx(str == tests[i].str, i); } test_end(); test_begin("str_sanitize_append"); str2 = t_str_new(128); for (i = 0; i < N_ELEMENTS(tests); i++) { if (tests[i].str == NULL) continue; str_truncate(str2, 0); str_append(str2, "1234567890"); str_sanitize_append(str2, tests[i].str, tests[i].max_len); test_assert_idx(strncmp(str_c(str2), "1234567890", 10) == 0, i); if (tests[i].sanitized != NULL) test_assert_idx(strcmp(str_c(str2)+10, tests[i].sanitized) == 0, i); else test_assert_idx(strcmp(str_c(str2)+10, tests[i].str) == 0, i); } test_end(); } dovecot-2.2.33.2/src/lib/rand.h0000644000175000017500000000113513165463624012771 00000000000000#ifndef RAND_H #define RAND_H /* Wrap srand() so that we can reproduce fuzzed tests */ /* If we have seeded the prng precisely once, and we remember what * value that was with, then we can reproduce any failing test cases * that depend on that randomness by forcing the seed value (e.g. * in a debugger, by putting a breakpoint on rand_set_seed()). */ /* Number of times we've been seeded */ int rand_get_seed_count(void); /* That last seed */ unsigned int rand_get_last_seed(void); /* Actually seed the prng (could add char* for name of function?) */ void rand_set_seed(unsigned int s); #endif dovecot-2.2.33.2/src/lib/test-bsearch-insert-pos.c0000644000175000017500000000206113123174404016511 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "bsearch-insert-pos.h" static int cmp_uint(const unsigned int *i1, const unsigned int *i2) { return (int)*i1 - (int)*i2; } void test_bsearch_insert_pos(void) { static const unsigned int input[] = { 1, 5, 9, 15, 16, UINT_MAX, 1, 5, 9, 15, 16, 17, UINT_MAX, UINT_MAX }; static const unsigned int max_key = 18; const unsigned int *cur; unsigned int key, len, i, idx; bool success; cur = input; for (i = 0; cur[0] != UINT_MAX; i++) { for (len = 0; cur[len] != UINT_MAX; len++) ; for (key = 0; key < max_key; key++) { if (bsearch_insert_pos(&key, cur, len, sizeof(*cur), cmp_uint, &idx)) success = cur[idx] == key; else if (idx == 0) success = cur[0] > key; else if (idx == len) success = cur[len-1] < key; else { success = cur[idx-1] < key && cur[idx+1] > key; } if (!success) break; } cur += len + 1; test_out(t_strdup_printf("bsearch_insert_pos(%d,%d)", i, key), success); } } dovecot-2.2.33.2/src/lib/file-dotlock.c0000644000175000017500000005530613165463624014425 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "hex-binary.h" #include "hostpid.h" #include "file-lock.h" #include "eacces-error.h" #include "write-full.h" #include "safe-mkstemp.h" #include "nfs-workarounds.h" #include "file-dotlock.h" #include #include #include #include #include #define DEFAULT_LOCK_SUFFIX ".lock" /* 0.1 .. 0.2msec */ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) /* Maximum 3 second wait between dotlock checks */ #define LOCK_MAX_WAIT_USECS (1000000 * 3) /* If the dotlock is newer than this, don't verify that the PID it contains is valid (since it most likely is). */ #define STALE_PID_CHECK_SECS 2 /* Maximum difference between current time and create file's ctime before logging a warning. Should be less than a second in normal operation. */ #define MAX_TIME_DIFF 30 /* NFS may return a cached mtime in stat(). A later non-cached stat() may return a slightly different mtime. Allow the difference to be this much and still consider it to be the same mtime. */ #define FILE_DOTLOCK_MAX_STAT_MTIME_DIFF 1 struct dotlock { struct dotlock_settings settings; dev_t dev; ino_t ino; time_t mtime; char *path; char *lock_path; int fd; time_t lock_time; }; struct file_change_info { dev_t dev; ino_t ino; off_t size; time_t ctime, mtime; }; struct lock_info { const struct dotlock_settings *set; const char *path, *lock_path, *temp_path; int fd; struct file_change_info lock_info; struct file_change_info file_info; time_t last_pid_check; time_t last_change; unsigned int wait_usecs; unsigned int have_pid:1; unsigned int pid_read:1; unsigned int use_io_notify:1; unsigned int lock_stated:1; }; static struct dotlock * file_dotlock_alloc(const struct dotlock_settings *settings, const char *path) { struct dotlock *dotlock; dotlock = i_new(struct dotlock, 1); dotlock->settings = *settings; if (dotlock->settings.lock_suffix == NULL) dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX; dotlock->path = i_strdup(path); dotlock->fd = -1; return dotlock; } static pid_t read_local_pid(const char *lock_path) { char buf[512], *host; int fd; ssize_t ret; pid_t pid; fd = open(lock_path, O_RDONLY); if (fd == -1) return -1; /* ignore the actual error */ /* read line */ ret = read(fd, buf, sizeof(buf)-1); i_close_fd(&fd); if (ret <= 0) return -1; /* fix the string */ if (buf[ret-1] == '\n') ret--; buf[ret] = '\0'; /* it should contain pid:host */ host = strchr(buf, ':'); if (host == NULL) return -1; *host++ = '\0'; /* host must be ours */ if (strcmp(host, my_hostname) != 0) return -1; if (str_to_pid(buf, &pid) < 0) return -1; if (pid <= 0) return -1; return pid; } static bool update_change_info(const struct stat *st, struct file_change_info *change, time_t *last_change_r, time_t now, bool check_ctime) { /* ctime is checked only if we're not doing NFS attribute cache flushes. it changes them. */ if (change->ino != st->st_ino || !CMP_DEV_T(change->dev, st->st_dev) || (change->ctime != st->st_ctime && check_ctime) || change->mtime != st->st_mtime || change->size != st->st_size) { time_t change_time = now; if (change->ctime == 0) { /* First check, set last_change to file's change time. Use mtime instead if it's higher, but only if it's not higher than current time, because the mtime can also be used for keeping metadata. */ change_time = st->st_mtime <= now && (st->st_mtime > st->st_ctime || !check_ctime) ? st->st_mtime : st->st_ctime; } if (*last_change_r < change_time) *last_change_r = change_time; change->ino = st->st_ino; change->dev = st->st_dev; change->ctime = st->st_ctime; change->mtime = st->st_mtime; change->size = st->st_size; return TRUE; } return FALSE; } static int update_lock_info(time_t now, struct lock_info *lock_info, bool *changed_r) { struct stat st; /* don't waste time flushing attribute cache the first time we're here. if it's stale we'll get back here soon. */ if (lock_info->set->nfs_flush && lock_info->lock_stated) { nfs_flush_file_handle_cache(lock_info->lock_path); nfs_flush_attr_cache_unlocked(lock_info->lock_path); } lock_info->lock_stated = TRUE; if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) { if (errno != ENOENT) { i_error("lstat(%s) failed: %m", lock_info->lock_path); return -1; } return 1; } *changed_r = update_change_info(&st, &lock_info->lock_info, &lock_info->last_change, now, !lock_info->set->nfs_flush); return 0; } static int dotlock_override(struct lock_info *lock_info) { if (i_unlink_if_exists(lock_info->lock_path) < 0) return -1; /* make sure we sleep for a while after overriding the lock file. otherwise another process might try to override it at the same time and unlink our newly created dotlock. */ if (lock_info->use_io_notify) usleep(LOCK_RANDOM_USLEEP_TIME); return 0; } static int check_lock(time_t now, struct lock_info *lock_info) { time_t stale_timeout = lock_info->set->stale_timeout; pid_t pid = -1; bool changed; int ret; if ((ret = update_lock_info(now, lock_info, &changed)) != 0) return ret; if (changed || !lock_info->pid_read) { /* either our first check or someone else got the lock file. if the dotlock was created only a couple of seconds ago, don't bother to read its PID. */ if (lock_info->lock_info.mtime >= now - STALE_PID_CHECK_SECS) lock_info->pid_read = FALSE; else { pid = read_local_pid(lock_info->lock_path); lock_info->pid_read = TRUE; } lock_info->have_pid = pid != -1; } else if (!lock_info->have_pid) { /* no pid checking */ } else { if (lock_info->last_pid_check == now) { /* we just checked the pid */ return 0; } /* re-read the pid. even if all times and inodes are the same, the PID in the file might have changed if lock files were rapidly being recreated. */ pid = read_local_pid(lock_info->lock_path); lock_info->have_pid = pid != -1; } if (lock_info->have_pid) { /* we've local PID. Check if it exists. */ if (kill(pid, 0) == 0 || errno != ESRCH) { if (pid != getpid()) { /* process exists, don't override */ return 0; } /* it's us. either we're locking it again, or it's a stale lock file with same pid than us. either way, recreate it.. */ } /* doesn't exist - now check again if the dotlock was just deleted or replaced */ if ((ret = update_lock_info(now, lock_info, &changed)) != 0) return ret; if (!changed) { /* still there, go ahead and override it */ return dotlock_override(lock_info); } return 1; } if (stale_timeout == 0) { /* no change checking */ return 0; } if (now > lock_info->last_change + stale_timeout) { struct stat st; /* possibly stale lock file. check also the timestamp of the file we're protecting. */ if (lock_info->set->nfs_flush) { nfs_flush_file_handle_cache(lock_info->path); nfs_flush_attr_cache_maybe_locked(lock_info->path); } if (nfs_safe_stat(lock_info->path, &st) < 0) { if (errno == ENOENT) { /* file doesn't exist. treat it as if it hasn't changed */ } else { i_error("stat(%s) failed: %m", lock_info->path); return -1; } } else { (void)update_change_info(&st, &lock_info->file_info, &lock_info->last_change, now, !lock_info->set->nfs_flush); } } if (now > lock_info->last_change + stale_timeout) { /* no changes for a while, assume stale lock */ return dotlock_override(lock_info); } return 0; } static int file_write_pid(int fd, const char *path, bool nfs_flush) { const char *str; /* write our pid and host, if possible */ str = t_strdup_printf("%s:%s", my_pid, my_hostname); if (write_full(fd, str, strlen(str)) < 0 || (nfs_flush && fdatasync(fd) < 0)) { /* failed, leave it empty then */ if (ftruncate(fd, 0) < 0) { i_error("ftruncate(%s) failed: %m", path); return -1; } } return 0; } static int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid, string_t *tmp_path, time_t now) { const char *temp_prefix = lock_info->set->temp_prefix; const char *p; mode_t old_mask; struct stat st; if (lock_info->temp_path == NULL) { /* we'll need our temp file first. */ i_assert(lock_info->fd == -1); p = strrchr(lock_info->lock_path, '/'); str_truncate(tmp_path, 0); if (temp_prefix != NULL) { if (*temp_prefix != '/' && p != NULL) { /* add directory */ str_append_n(tmp_path, lock_info->lock_path, p - lock_info->lock_path); str_append_c(tmp_path, '/'); } str_append(tmp_path, temp_prefix); } else { if (p != NULL) { /* add directory */ str_append_n(tmp_path, lock_info->lock_path, p - lock_info->lock_path); str_append_c(tmp_path, '/'); } str_printfa(tmp_path, ".temp.%s.%s.", my_hostname, my_pid); } old_mask = umask(0666); lock_info->fd = safe_mkstemp(tmp_path, 0666 ^ old_mask, (uid_t)-1, (gid_t)-1); umask(old_mask); if (lock_info->fd == -1) return -1; if (write_pid) { if (file_write_pid(lock_info->fd, str_c(tmp_path), lock_info->set->nfs_flush) < 0) { i_close_fd(&lock_info->fd); return -1; } } lock_info->temp_path = str_c(tmp_path); } else if (fstat(lock_info->fd, &st) < 0) { i_error("fstat(%s) failed: %m", lock_info->temp_path); return -1; } else if (st.st_ctime < now) { /* we've been waiting for a while. refresh the file's timestamp. */ if (utime(lock_info->temp_path, NULL) < 0) i_error("utime(%s) failed: %m", lock_info->temp_path); } if (nfs_safe_link(lock_info->temp_path, lock_info->lock_path, TRUE) < 0) { if (errno == EEXIST) return 0; if (errno != EACCES) { i_error("link(%s, %s) failed: %m", lock_info->temp_path, lock_info->lock_path); } return -1; } if (i_unlink(lock_info->temp_path) < 0) { /* non-fatal, continue */ } lock_info->temp_path = NULL; return 1; } static int try_create_lock_excl(struct lock_info *lock_info, bool write_pid) { int fd; fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666); if (fd == -1) { if (errno == EEXIST) return 0; if (errno != ENOENT && errno != EACCES) i_error("open(%s) failed: %m", lock_info->lock_path); return -1; } if (write_pid) { if (file_write_pid(fd, lock_info->lock_path, lock_info->set->nfs_flush) < 0) { i_close_fd(&fd); return -1; } } lock_info->fd = fd; return 1; } static void dotlock_wait_end(struct ioloop *ioloop) { io_loop_stop(ioloop); } static void dotlock_wait(struct lock_info *lock_info) { struct ioloop *ioloop; struct io *io; struct timeout *to; if (!lock_info->use_io_notify) { usleep(lock_info->wait_usecs); return; } ioloop = io_loop_create(); switch (io_add_notify(lock_info->lock_path, dotlock_wait_end, ioloop, &io)) { case IO_NOTIFY_ADDED: break; case IO_NOTIFY_NOTFOUND: /* the lock file doesn't exist anymore, don't sleep */ io_loop_destroy(&ioloop); return; case IO_NOTIFY_NOSUPPORT: /* listening for files not supported */ io_loop_destroy(&ioloop); lock_info->use_io_notify = FALSE; usleep(LOCK_RANDOM_USLEEP_TIME); return; } /* timeout after a random time even when using notify, since it doesn't work reliably with e.g. NFS. */ to = timeout_add(lock_info->wait_usecs/1000, dotlock_wait_end, ioloop); io_loop_run(ioloop); io_remove(&io); timeout_remove(&to); io_loop_destroy(&ioloop); } static int dotlock_create(struct dotlock *dotlock, enum dotlock_create_flags flags, bool write_pid, const char **lock_path_r) { const struct dotlock_settings *set = &dotlock->settings; const char *lock_path; struct lock_info lock_info; struct stat st; unsigned int stale_notify_threshold; unsigned int change_secs, wait_left; time_t now, max_wait_time, last_notify; time_t prev_last_change = 0, prev_wait_update = 0; string_t *tmp_path; int ret; bool do_wait; now = time(NULL); lock_path = *lock_path_r = t_strconcat(dotlock->path, set->lock_suffix, NULL); stale_notify_threshold = set->stale_timeout / 2; max_wait_time = (flags & DOTLOCK_CREATE_FLAG_NONBLOCK) != 0 ? 0 : now + set->timeout; tmp_path = t_str_new(256); i_zero(&lock_info); lock_info.path = dotlock->path; lock_info.set = set; lock_info.lock_path = lock_path; lock_info.fd = -1; lock_info.use_io_notify = set->use_io_notify; last_notify = 0; do_wait = FALSE; file_lock_wait_start(); do { if (do_wait) { if (prev_last_change != lock_info.last_change) { /* dotlock changed since last check, reset the wait time */ lock_info.wait_usecs = LOCK_RANDOM_USLEEP_TIME; prev_last_change = lock_info.last_change; prev_wait_update = now; } else if (prev_wait_update != now && lock_info.wait_usecs < LOCK_MAX_WAIT_USECS) { /* we've been waiting for a while now, increase the wait time to avoid wasting CPU */ prev_wait_update = now; lock_info.wait_usecs += lock_info.wait_usecs/2; } dotlock_wait(&lock_info); now = time(NULL); } ret = check_lock(now, &lock_info); if (ret < 0) break; if (ret == 1) { if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) break; ret = set->use_excl_lock ? try_create_lock_excl(&lock_info, write_pid) : try_create_lock_hardlink(&lock_info, write_pid, tmp_path, now); if (ret != 0) { /* if we succeeded, get the current time once more in case disk I/O usage was really high and it took a long time to create the lock */ now = time(NULL); break; } } if (last_notify != now && set->callback != NULL) { last_notify = now; change_secs = now - lock_info.last_change; wait_left = max_wait_time - now; if (change_secs >= stale_notify_threshold && change_secs <= wait_left) { unsigned int secs_left = set->stale_timeout < change_secs ? 0 : set->stale_timeout - change_secs; if (!set->callback(secs_left, TRUE, set->context)) { /* we don't want to override */ lock_info.last_change = now; } } else if (wait_left > 0) { (void)set->callback(wait_left, FALSE, set->context); } } do_wait = TRUE; now = time(NULL); } while (now < max_wait_time); file_lock_wait_end(dotlock->path); if (ret > 0) { i_assert(lock_info.fd != -1); if (fstat(lock_info.fd, &st) < 0) { i_error("fstat(%s) failed: %m", lock_path); ret = -1; } else { /* successful dotlock creation */ dotlock->dev = st.st_dev; dotlock->ino = st.st_ino; dotlock->fd = lock_info.fd; dotlock->lock_time = now; lock_info.fd = -1; if (st.st_ctime + MAX_TIME_DIFF < now || st.st_ctime - MAX_TIME_DIFF > now) { i_warning("Created dotlock file's timestamp is " "different than current time " "(%s vs %s): %s", dec2str(st.st_ctime), dec2str(now), dotlock->path); } } } if (lock_info.fd != -1) { int old_errno = errno; if (close(lock_info.fd) < 0) i_error("close(%s) failed: %m", lock_path); errno = old_errno; } if (lock_info.temp_path != NULL) i_unlink(lock_info.temp_path); if (ret == 0) errno = EAGAIN; return ret; } static void file_dotlock_free(struct dotlock **_dotlock) { struct dotlock *dotlock = *_dotlock; int old_errno; *_dotlock = NULL; if (dotlock->fd != -1) { old_errno = errno; if (close(dotlock->fd) < 0) i_error("close(%s) failed: %m", dotlock->path); dotlock->fd = -1; errno = old_errno; } i_free(dotlock->path); i_free(dotlock->lock_path); i_free(dotlock); } static int file_dotlock_create_real(struct dotlock *dotlock, enum dotlock_create_flags flags) { const char *lock_path; struct stat st; int fd, ret; ret = dotlock_create(dotlock, flags, TRUE, &lock_path); if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) return ret; fd = dotlock->fd; dotlock->fd = -1; if (close(fd) < 0) { i_error("close(%s) failed: %m", lock_path); return -1; } /* With NFS the writes may have been flushed only when closing the file. Get the mtime again after that to avoid "dotlock was modified" errors. */ if (lstat(lock_path, &st) < 0) { if (errno != ENOENT) i_error("stat(%s) failed: %m", lock_path); else { i_error("dotlock %s was immediately deleted under us", lock_path); } return -1; } /* extra sanity check won't hurt.. */ if (st.st_dev != dotlock->dev || st.st_ino != dotlock->ino) { errno = ENOENT; i_error("dotlock %s was immediately recreated under us", lock_path); return -1; } dotlock->mtime = st.st_mtime; return 1; } int file_dotlock_create(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, struct dotlock **dotlock_r) { struct dotlock *dotlock; int ret; dotlock = file_dotlock_alloc(set, path); T_BEGIN { ret = file_dotlock_create_real(dotlock, flags); } T_END; if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) file_dotlock_free(&dotlock); *dotlock_r = dotlock; return ret; } static void dotlock_replaced_warning(struct dotlock *dotlock, bool deleted) { const char *lock_path; time_t now = time(NULL); lock_path = file_dotlock_get_lock_path(dotlock); if (dotlock->mtime == dotlock->lock_time) { i_warning("Our dotlock file %s was %s " "(locked %d secs ago, touched %d secs ago)", lock_path, deleted ? "deleted" : "overridden", (int)(now - dotlock->lock_time), (int)(now - dotlock->mtime)); } else { i_warning("Our dotlock file %s was %s " "(kept it %d secs)", lock_path, deleted ? "deleted" : "overridden", (int)(now - dotlock->lock_time)); } } static bool file_dotlock_has_mtime_changed(time_t t1, time_t t2) { time_t diff; if (t1 == t2) return FALSE; /* with NFS t1 may have been looked up from local cache. allow it to be a little bit different. */ diff = t1 > t2 ? t1-t2 : t2-t1; return diff > FILE_DOTLOCK_MAX_STAT_MTIME_DIFF; } int file_dotlock_delete(struct dotlock **dotlock_p) { struct dotlock *dotlock; const char *lock_path; struct stat st; int ret; dotlock = *dotlock_p; *dotlock_p = NULL; lock_path = file_dotlock_get_lock_path(dotlock); if (nfs_safe_lstat(lock_path, &st) < 0) { if (errno == ENOENT) { dotlock_replaced_warning(dotlock, TRUE); file_dotlock_free(&dotlock); return 0; } i_error("lstat(%s) failed: %m", lock_path); file_dotlock_free(&dotlock); return -1; } if (dotlock->ino != st.st_ino || !CMP_DEV_T(dotlock->dev, st.st_dev)) { dotlock_replaced_warning(dotlock, FALSE); errno = EEXIST; file_dotlock_free(&dotlock); return 0; } if (file_dotlock_has_mtime_changed(dotlock->mtime, st.st_mtime) && dotlock->fd == -1) { i_warning("Our dotlock file %s was modified (%s vs %s), " "assuming it wasn't overridden (kept it %d secs)", lock_path, dec2str(dotlock->mtime), dec2str(st.st_mtime), (int)(time(NULL) - dotlock->lock_time)); } if ((ret = i_unlink_if_exists(lock_path)) == 0) dotlock_replaced_warning(dotlock, TRUE); file_dotlock_free(&dotlock); return ret; } int file_dotlock_open(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, struct dotlock **dotlock_r) { struct dotlock *dotlock; int ret; dotlock = file_dotlock_alloc(set, path); T_BEGIN { const char *lock_path; ret = dotlock_create(dotlock, flags, FALSE, &lock_path); } T_END; if (ret <= 0) { file_dotlock_free(&dotlock); *dotlock_r = NULL; return -1; } *dotlock_r = dotlock; return dotlock->fd; } static int ATTR_NULL(7) file_dotlock_open_mode_full(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin, struct dotlock **dotlock_r) { struct dotlock *dotlock; mode_t old_mask; int fd; old_mask = umask(0666 ^ mode); fd = file_dotlock_open(set, path, flags, &dotlock); umask(old_mask); if (fd != -1 && (uid != (uid_t)-1 || gid != (gid_t)-1)) { if (fchown(fd, uid, gid) < 0) { if (errno == EPERM && uid == (uid_t)-1) { i_error("%s", eperm_error_get_chgrp("fchown", file_dotlock_get_lock_path(dotlock), gid, gid_origin)); } else { i_error("fchown(%s, %ld, %ld) failed: %m", file_dotlock_get_lock_path(dotlock), (long)uid, (long)gid); } file_dotlock_delete(&dotlock); return -1; } } *dotlock_r = dotlock; return fd; } int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, mode_t mode, uid_t uid, gid_t gid, struct dotlock **dotlock_r) { return file_dotlock_open_mode_full(set, path, flags, mode, uid, gid, NULL, dotlock_r); } int file_dotlock_open_group(const struct dotlock_settings *set, const char *path, enum dotlock_create_flags flags, mode_t mode, gid_t gid, const char *gid_origin, struct dotlock **dotlock_r) { return file_dotlock_open_mode_full(set, path, flags, mode, (uid_t)-1, gid, gid_origin, dotlock_r); } int file_dotlock_replace(struct dotlock **dotlock_p, enum dotlock_replace_flags flags) { struct dotlock *dotlock; const char *lock_path; bool is_locked; dotlock = *dotlock_p; *dotlock_p = NULL; is_locked = (flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) == 0 ? TRUE : file_dotlock_is_locked(dotlock); if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0) dotlock->fd = -1; if (!is_locked) { dotlock_replaced_warning(dotlock, FALSE); errno = EEXIST; file_dotlock_free(&dotlock); return 0; } lock_path = file_dotlock_get_lock_path(dotlock); if (rename(lock_path, dotlock->path) < 0) { i_error("rename(%s, %s) failed: %m", lock_path, dotlock->path); if (errno == ENOENT) dotlock_replaced_warning(dotlock, TRUE); file_dotlock_free(&dotlock); return -1; } file_dotlock_free(&dotlock); return 1; } int file_dotlock_touch(struct dotlock *dotlock) { time_t now = time(NULL); struct utimbuf buf; int ret = 0; if (dotlock->mtime == now) return 0; dotlock->mtime = now; buf.actime = buf.modtime = now; T_BEGIN { const char *lock_path = file_dotlock_get_lock_path(dotlock); if (utime(lock_path, &buf) < 0) { i_error("utime(%s) failed: %m", lock_path); ret = -1; } } T_END; return ret; } bool file_dotlock_is_locked(struct dotlock *dotlock) { struct stat st, st2; const char *lock_path; lock_path = file_dotlock_get_lock_path(dotlock); if (fstat(dotlock->fd, &st) < 0) { i_error("fstat(%s) failed: %m", lock_path); return FALSE; } if (nfs_safe_lstat(lock_path, &st2) < 0) { i_error("lstat(%s) failed: %m", lock_path); return FALSE; } return st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev); } const char *file_dotlock_get_lock_path(struct dotlock *dotlock) { if (dotlock->lock_path == NULL) { dotlock->lock_path = i_strconcat(dotlock->path, dotlock->settings.lock_suffix, NULL); } return dotlock->lock_path; } dovecot-2.2.33.2/src/lib/test-file-create-locked.c0000644000175000017500000000622513165463624016441 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "unlink-directory.h" #include "file-create-locked.h" #include #include #include static void create_file(const char *path) { int fd; fd = creat(path, 0600); if (fd == -1) i_fatal("creat(%s) failed: %m", path); i_close_fd(&fd); } static bool wait_for_file(pid_t pid, const char *path) { struct stat st; for (unsigned int i = 0; i < 1000; i++) { if (stat(path, &st) == 0) return TRUE; if (errno != ENOENT) i_fatal("stat(%s) failed: %m", path); if (kill(pid, 0) < 0) { if (errno == ESRCH) return FALSE; i_fatal("kill(SIGSRCH) failed: %m"); } usleep(10000); } i_error("%s isn't being created", path); return FALSE; } static void test_file_create_locked_basic(void) { struct file_create_settings set = { .lock_timeout_secs = 0, .lock_method = FILE_LOCK_METHOD_FCNTL, }; const char *path = ".test-file-create-locked"; struct file_lock *lock; const char *error; bool created; pid_t pid; int fd; test_begin("file_create_locked()"); i_unlink_if_exists(path); i_unlink_if_exists(".test-temp-file-create-locked-child"); pid = fork(); switch (pid) { case (pid_t)-1: i_error("fork() failed: %m"); break; case 0: /* child */ fd = file_create_locked(path, &set, &lock, &created, &error); test_assert(fd > 0); test_assert(created); if (test_has_failed()) exit(1); create_file(".test-temp-file-create-locked-child"); sleep(60); i_close_fd(&fd); exit(0); default: /* parent */ test_assert(wait_for_file(pid, ".test-temp-file-create-locked-child")); if (test_has_failed()) break; test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); test_assert(errno == EAGAIN); if (kill(pid, SIGKILL) < 0) i_error("kill(SIGKILL) failed: %m"); break; } i_unlink_if_exists(".test-temp-file-create-locked-child"); i_unlink_if_exists(path); test_end(); } static void test_file_create_locked_mkdir(void) { struct file_create_settings set = { .lock_timeout_secs = 0, .lock_method = FILE_LOCK_METHOD_FCNTL, }; const char *path; struct file_lock *lock; const char *error, *dir; bool created; int fd; test_begin("file_create_locked() with mkdir"); dir = ".test-temp-file-create-locked-dir"; if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR) < 0) i_fatal("unlink_directory(%s) failed: %m", dir); path = t_strconcat(dir, "/lockfile", NULL); /* try without mkdir enabled */ test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); test_assert(errno == ENOENT); /* try with mkdir enabled */ set.mkdir_mode = 0700; fd = file_create_locked(path, &set, &lock, &created, &error); test_assert(fd > 0); test_assert(created); i_close_fd(&fd); struct stat st; if (stat(dir, &st) < 0) i_error("stat(%s) failed: %m", dir); test_assert((st.st_mode & 0777) == 0700); i_unlink(path); file_lock_free(&lock); if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR) < 0) i_fatal("unlink_directory(%s) failed: %m", dir); test_end(); } void test_file_create_locked(void) { test_file_create_locked_basic(); test_file_create_locked_mkdir(); } dovecot-2.2.33.2/src/lib/hash-decl.h0000644000175000017500000000101713123174404013661 00000000000000#ifndef HASH_DECL_H #define HASH_DECL_H #define HASH_TABLE_UNION(key_type, value_type) { \ struct hash_table *_table; \ key_type _key; \ key_type *_keyp; \ const key_type _const_key; \ value_type _value; \ value_type *_valuep; \ } #define HASH_TABLE_DEFINE_TYPE(name, key_type, value_type) \ union hash ## __ ## name HASH_TABLE_UNION(key_type, value_type) #define HASH_TABLE(key_type, value_type) \ union HASH_TABLE_UNION(key_type, value_type) #define HASH_TABLE_TYPE(name) \ union hash ## __ ## name #endif dovecot-2.2.33.2/src/lib/Makefile.am0000644000175000017500000001573313165463624013741 00000000000000noinst_LTLIBRARIES = liblib.la BUILT_SOURCES = unicodemap.c EXTRA_DIST = unicodemap.c unicodemap.pl UnicodeData.txt UnicodeData.txt: test -f UnicodeData.txt || wget https://dovecot.org/res/UnicodeData.txt $(srcdir)/unicodemap.c: unicodemap.pl UnicodeData.txt perl $(srcdir)/unicodemap.pl < UnicodeData.txt > $@ liblib_la_SOURCES = \ abspath.c \ array.c \ aqueue.c \ askpass.c \ backtrace-string.c \ base32.c \ base64.c \ bits.c \ bsearch-insert-pos.c \ buffer.c \ child-wait.c \ compat.c \ connection.c \ crc32.c \ data-stack.c \ eacces-error.c \ env-util.c \ execv-const.c \ failures.c \ fd-close-on-exec.c \ fd-set-nonblock.c \ fdatasync-path.c \ fdpass.c \ file-cache.c \ file-create-locked.c \ file-copy.c \ file-dotlock.c \ file-lock.c \ file-set-size.c \ guid.c \ hash.c \ hash-format.c \ hash-method.c \ hash2.c \ hex-binary.c \ hex-dec.c \ hmac.c \ hmac-cram-md5.c \ home-expand.c \ hook-build.c \ hostpid.c \ imem.c \ ipwd.c \ iostream.c \ iostream-rawlog.c \ iostream-temp.c \ iso8601-date.c \ istream.c \ istream-base64-decoder.c \ istream-base64-encoder.c \ istream-callback.c \ istream-chain.c \ istream-concat.c \ istream-crlf.c \ istream-data.c \ istream-failure-at.c \ istream-file.c \ istream-hash.c \ istream-jsonstr.c \ istream-limit.c \ istream-mmap.c \ istream-multiplex.c \ istream-rawlog.c \ istream-seekable.c \ istream-sized.c \ istream-tee.c \ istream-timeout.c \ istream-unix.c \ ioloop.c \ ioloop-iolist.c \ ioloop-notify-none.c \ ioloop-notify-fd.c \ ioloop-notify-inotify.c \ ioloop-notify-kqueue.c \ ioloop-poll.c \ ioloop-select.c \ ioloop-epoll.c \ ioloop-kqueue.c \ json-parser.c \ json-tree.c \ lib.c \ lib-signals.c \ log-throttle.c \ md4.c \ md5.c \ mempool.c \ mempool-alloconly.c \ mempool-datastack.c \ mempool-system.c \ mempool-unsafe-datastack.c \ mkdir-parents.c \ mmap-anon.c \ mmap-util.c \ module-dir.c \ mountpoint.c \ net.c \ nfs-workarounds.c \ numpack.c \ ostream.c \ ostream-buffer.c \ ostream-escaped.c \ ostream-failure-at.c \ ostream-file.c \ ostream-hash.c \ ostream-multiplex.c \ ostream-null.c \ ostream-rawlog.c \ ostream-unix.c \ pkcs5.c \ primes.c \ printf-format-fix.c \ process-title.c \ priorityq.c \ randgen.c \ rand.c \ read-full.c \ restrict-access.c \ restrict-process-size.c \ safe-memset.c \ safe-mkdir.c \ safe-mkstemp.c \ sendfile-util.c \ seq-range-array.c \ sha1.c \ sha2.c \ sha3.c \ str.c \ str-find.c \ str-sanitize.c \ str-table.c \ strescape.c \ strfuncs.c \ strnum.c \ time-util.c \ timing.c \ unix-socket-create.c \ unlink-directory.c \ unlink-old-files.c \ unichar.c \ uri-util.c \ utc-offset.c \ utc-mktime.c \ var-expand.c \ var-expand-if.c \ wildcard-match.c \ write-full.c headers = \ abspath.h \ aqueue.h \ array.h \ array-decl.h \ askpass.h \ backtrace-string.h \ base32.h \ base64.h \ bits.h \ bsearch-insert-pos.h \ buffer.h \ byteorder.h \ child-wait.h \ compat.h \ connection.h \ crc32.h \ data-stack.h \ eacces-error.h \ env-util.h \ execv-const.h \ failures.h \ fd-close-on-exec.h \ fd-set-nonblock.h \ fdatasync-path.h \ fdpass.h \ file-cache.h \ file-create-locked.h \ file-copy.h \ file-dotlock.h \ file-lock.h \ file-set-size.h \ fsync-mode.h \ guid.h \ hash.h \ hash-decl.h \ hash-format.h \ hash-method.h \ hash2.h \ hex-binary.h \ hex-dec.h \ hmac.h \ hmac-cram-md5.h \ home-expand.h \ hook-build.h \ hostpid.h \ imem.h \ ipwd.h \ iostream.h \ iostream-private.h \ iostream-rawlog.h \ iostream-rawlog-private.h \ iostream-temp.h \ iso8601-date.h \ istream.h \ istream-base64.h \ istream-callback.h \ istream-chain.h \ istream-concat.h \ istream-crlf.h \ istream-failure-at.h \ istream-file-private.h \ istream-hash.h \ istream-jsonstr.h \ istream-multiplex.h \ istream-private.h \ istream-rawlog.h \ istream-seekable.h \ istream-sized.h \ istream-tee.h \ istream-timeout.h \ istream-unix.h \ ioloop.h \ ioloop-iolist.h \ ioloop-private.h \ ioloop-notify-fd.h \ json-parser.h \ json-tree.h \ lib.h \ lib-signals.h \ llist.h \ log-throttle.h \ macros.h \ md4.h \ md5.h \ malloc-overflow.h \ mempool.h \ mkdir-parents.h \ mmap-util.h \ module-context.h \ module-dir.h \ mountpoint.h \ net.h \ nfs-workarounds.h \ numpack.h \ ostream.h \ ostream-escaped.h \ ostream-failure-at.h \ ostream-file-private.h \ ostream-hash.h \ ostream-multiplex.h \ ostream-private.h \ ostream-null.h \ ostream-rawlog.h \ ostream-unix.h \ pkcs5.h \ primes.h \ printf-format-fix.h \ process-title.h \ priorityq.h \ rand.h \ randgen.h \ read-full.h \ restrict-access.h \ restrict-process-size.h \ safe-memset.h \ safe-mkdir.h \ safe-mkstemp.h \ sendfile-util.h \ seq-range-array.h \ sha-common.h \ sha1.h \ sha2.h \ sha3.h \ sort.h \ str.h \ str-find.h \ str-sanitize.h \ str-table.h \ strescape.h \ strfuncs.h \ strnum.h \ time-util.h \ timing.h \ unix-socket-create.h \ unlink-directory.h \ unlink-old-files.h \ unichar.h \ uri-util.h \ utc-offset.h \ utc-mktime.h \ var-expand.h \ var-expand-private.h \ wildcard-match.h \ write-full.h test_programs = test-lib noinst_PROGRAMS = $(test_programs) test_lib_CPPFLAGS = \ -I$(top_srcdir)/src/lib-test test_libs = \ ../lib-test/libtest.la \ liblib.la test_lib_SOURCES = \ test-lib.c \ test-array.c \ test-aqueue.c \ test-base32.c \ test-base64.c \ test-bits.c \ test-bsearch-insert-pos.c \ test-buffer.c \ test-byteorder.c \ test-crc32.c \ test-data-stack.c \ test-failures.c \ test-file-create-locked.c \ test-guid.c \ test-hash.c \ test-hash-format.c \ test-hash-method.c \ test-hex-binary.c \ test-imem.c \ test-ioloop.c \ test-iso8601-date.c \ test-iostream-temp.c \ test-istream.c \ test-istream-base64-decoder.c \ test-istream-base64-encoder.c \ test-istream-chain.c \ test-istream-concat.c \ test-istream-crlf.c \ test-istream-failure-at.c \ test-istream-multiplex.c \ test-istream-seekable.c \ test-istream-tee.c \ test-istream-unix.c \ test-json-parser.c \ test-json-tree.c \ test-llist.c \ test-log-throttle.c \ test-malloc-overflow.c \ test-mempool.c \ test-mempool-alloconly.c \ test-pkcs5.c \ test-net.c \ test-numpack.c \ test-ostream-buffer.c \ test-ostream-escaped.c \ test-ostream-failure-at.c \ test-ostream-file.c \ test-ostream-multiplex.c \ test-multiplex.c \ test-primes.c \ test-printf-format-fix.c \ test-priorityq.c \ test-seq-range-array.c \ test-str.c \ test-strescape.c \ test-strfuncs.c \ test-strnum.c \ test-str-find.c \ test-str-sanitize.c \ test-str-table.c \ test-time-util.c \ test-timing.c \ test-unichar.c \ test-utc-mktime.c \ test-var-expand.c \ test-wildcard-match.c test_headers = \ test-lib.h test_lib_LDADD = $(test_libs) test_lib_DEPENDENCIES = $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) noinst_HEADERS = $(test_headers) dovecot-2.2.33.2/src/lib/istream-concat.c0000644000175000017500000002503513165463624014756 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream-private.h" #include "istream-concat.h" struct concat_istream { struct istream_private istream; struct istream **input, *cur_input; uoff_t *input_size; unsigned int input_count; unsigned int cur_idx, unknown_size_idx; size_t prev_stream_left, prev_stream_skip, prev_skip; }; static void i_stream_concat_skip(struct concat_istream *cstream); static void i_stream_concat_close(struct iostream_private *stream, bool close_parent) { struct concat_istream *cstream = (struct concat_istream *)stream; unsigned int i; if (cstream->istream.istream.stream_errno == 0) { /* get the parent streams to the wanted offset */ (void)i_stream_concat_skip(cstream); } if (close_parent) { for (i = 0; i < cstream->input_count; i++) i_stream_close(cstream->input[i]); } } static void i_stream_concat_destroy(struct iostream_private *stream) { struct concat_istream *cstream = (struct concat_istream *)stream; unsigned int i; for (i = 0; i < cstream->input_count; i++) i_stream_unref(&cstream->input[i]); i_free(cstream->input); i_free(cstream->input_size); i_free(cstream->istream.w_buffer); } static void i_stream_concat_set_max_buffer_size(struct iostream_private *stream, size_t max_size) { struct concat_istream *cstream = (struct concat_istream *)stream; unsigned int i; cstream->istream.max_buffer_size = max_size; for (i = 0; i < cstream->input_count; i++) i_stream_set_max_buffer_size(cstream->input[i], max_size); } static void i_stream_concat_read_next(struct concat_istream *cstream) { struct istream *prev_input = cstream->cur_input; const unsigned char *data; size_t data_size, size; i_assert(cstream->cur_input->eof); if (cstream->prev_stream_skip != 0) { i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip); cstream->prev_stream_skip = 0; } data = i_stream_get_data(cstream->cur_input, &data_size); cstream->cur_idx++; cstream->cur_input = cstream->input[cstream->cur_idx]; i_stream_seek(cstream->cur_input, 0); if (cstream->prev_stream_left > 0 || cstream->istream.pos == 0) { /* all the pending data is already in w_buffer */ cstream->prev_stream_skip = data_size; cstream->prev_stream_left += data_size; i_assert(cstream->prev_stream_left == cstream->istream.pos - cstream->istream.skip); return; } i_assert(cstream->prev_stream_skip == 0); /* we already verified that the data size is less than the maximum buffer size */ cstream->istream.pos = 0; if (data_size > 0) { if (!i_stream_try_alloc(&cstream->istream, data_size, &size)) i_unreached(); i_assert(size >= data_size); } cstream->prev_stream_left = data_size; memcpy(cstream->istream.w_buffer, data, data_size); i_stream_skip(prev_input, data_size); cstream->istream.skip = 0; cstream->istream.pos = data_size; } static void i_stream_concat_skip(struct concat_istream *cstream) { struct istream_private *stream = &cstream->istream; size_t bytes_skipped; i_assert(stream->skip >= cstream->prev_skip); bytes_skipped = stream->skip - cstream->prev_skip; if (cstream->prev_stream_left == 0) { /* no need to worry about buffers, skip everything */ } else if (bytes_skipped < cstream->prev_stream_left) { /* we're still skipping inside buffer */ cstream->prev_stream_left -= bytes_skipped; bytes_skipped = 0; } else { /* done with the buffer */ i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip); cstream->prev_stream_skip = 0; bytes_skipped -= cstream->prev_stream_left; cstream->prev_stream_left = 0; } stream->pos -= bytes_skipped; stream->skip -= bytes_skipped; stream->buffer += bytes_skipped; cstream->prev_skip = stream->skip; i_stream_skip(cstream->cur_input, bytes_skipped); } static ssize_t i_stream_concat_read(struct istream_private *stream) { struct concat_istream *cstream = (struct concat_istream *)stream; const unsigned char *data; size_t size, data_size, cur_data_pos, new_pos; size_t new_bytes_count; ssize_t ret; bool last_stream; i_assert(cstream->cur_input != NULL); i_stream_concat_skip(cstream); i_assert(stream->pos >= stream->skip + cstream->prev_stream_left); cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left); data = i_stream_get_data(cstream->cur_input, &data_size); if (data_size > cur_data_pos) ret = 0; else { /* need to read more */ i_assert(cur_data_pos == data_size); ret = i_stream_read(cstream->cur_input); if (ret == -2 || ret == 0) return ret; if (ret == -1 && cstream->cur_input->stream_errno != 0) { io_stream_set_error(&cstream->istream.iostream, "read(%s) failed: %s", i_stream_get_name(cstream->cur_input), i_stream_get_error(cstream->cur_input)); stream->istream.stream_errno = cstream->cur_input->stream_errno; return -1; } /* we either read something or we're at EOF */ last_stream = cstream->cur_idx+1 >= cstream->input_count; if (ret == -1 && !last_stream) { if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) return -2; i_stream_concat_read_next(cstream); cstream->prev_skip = stream->skip; return i_stream_concat_read(stream); } stream->istream.eof = cstream->cur_input->eof && last_stream; i_assert(ret != -1 || stream->istream.eof); data = i_stream_get_data(cstream->cur_input, &data_size); } if (cstream->prev_stream_left == 0) { /* we can point directly to the current stream's buffers */ stream->buffer = data; stream->pos -= stream->skip; stream->skip = 0; new_pos = data_size; } else if (data_size == cur_data_pos) { /* nothing new read */ i_assert(ret == 0 || ret == -1); stream->buffer = stream->w_buffer; new_pos = stream->pos; } else { /* we still have some of the previous stream left. merge the new data with it. */ i_assert(data_size > cur_data_pos); new_bytes_count = data_size - cur_data_pos; if (!i_stream_try_alloc(stream, new_bytes_count, &size)) { stream->buffer = stream->w_buffer; return -2; } stream->buffer = stream->w_buffer; /* we'll copy all the new input to w_buffer. if we skip over prev_stream_left bytes, the next read will switch to pointing to cur_input's data directly. */ if (new_bytes_count > size) new_bytes_count = size; memcpy(stream->w_buffer + stream->pos, data + cur_data_pos, new_bytes_count); new_pos = stream->pos + new_bytes_count; } ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = new_pos; cstream->prev_skip = stream->skip; return ret; } static int find_v_offset(struct concat_istream *cstream, uoff_t *v_offset, unsigned int *idx_r) { const struct stat *st; unsigned int i; for (i = 0; i < cstream->input_count; i++) { if (*v_offset == 0) { /* seek to beginning of this stream */ break; } if (i == cstream->unknown_size_idx) { /* we'll need to figure out this stream's size */ if (i_stream_stat(cstream->input[i], TRUE, &st) < 0) { io_stream_set_error(&cstream->istream.iostream, "stat(%s) failed: %s", i_stream_get_name(cstream->input[i]), i_stream_get_error(cstream->input[i])); i_error("istream-concat: stat(%s) failed: %s", i_stream_get_name(cstream->input[i]), i_stream_get_error(cstream->input[i])); cstream->istream.istream.stream_errno = cstream->input[i]->stream_errno; return -1; } /* @UNSAFE */ cstream->input_size[i] = st->st_size; cstream->unknown_size_idx = i + 1; } if (*v_offset < cstream->input_size[i]) break; *v_offset -= cstream->input_size[i]; } *idx_r = i; return 0; } static void i_stream_concat_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct concat_istream *cstream = (struct concat_istream *)stream; stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; cstream->prev_stream_left = 0; cstream->prev_stream_skip = 0; cstream->prev_skip = 0; if (find_v_offset(cstream, &v_offset, &cstream->cur_idx) < 0) { /* failed */ stream->istream.stream_errno = EINVAL; return; } if (cstream->cur_idx < cstream->input_count) cstream->cur_input = cstream->input[cstream->cur_idx]; else { /* we allow seeking to EOF, but not past it. */ if (v_offset != 0) { io_stream_set_error(&cstream->istream.iostream, "Seeking past EOF by %"PRIuUOFF_T" bytes", v_offset); cstream->istream.istream.stream_errno = EINVAL; return; } i_assert(cstream->cur_idx > 0); cstream->cur_input = cstream->input[cstream->cur_idx-1]; v_offset = cstream->input_size[cstream->cur_idx-1]; } i_stream_seek(cstream->cur_input, v_offset); } static int i_stream_concat_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct concat_istream *cstream = (struct concat_istream *)stream; uoff_t v_offset = (uoff_t)-1; unsigned int i, cur_idx; /* make sure we have all sizes */ if (find_v_offset(cstream, &v_offset, &cur_idx) < 0) return -1; stream->statbuf.st_size = 0; for (i = 0; i < cstream->unknown_size_idx; i++) stream->statbuf.st_size += cstream->input_size[i]; return 0; } struct istream *i_stream_create_concat(struct istream *input[]) { struct concat_istream *cstream; unsigned int count; size_t max_buffer_size = I_STREAM_MIN_SIZE; bool blocking = TRUE, seekable = TRUE; /* if any of the streams isn't blocking or seekable, set ourself also nonblocking/nonseekable */ for (count = 0; input[count] != NULL; count++) { size_t cur_max = i_stream_get_max_buffer_size(input[count]); if (cur_max > max_buffer_size) max_buffer_size = cur_max; if (!input[count]->blocking) blocking = FALSE; if (!input[count]->seekable) seekable = FALSE; i_stream_ref(input[count]); } i_assert(count != 0); cstream = i_new(struct concat_istream, 1); cstream->input_count = count; cstream->input = p_memdup(default_pool, input, sizeof(*input) * count); cstream->input_size = i_new(uoff_t, count); cstream->cur_input = cstream->input[0]; i_stream_seek(cstream->cur_input, 0); cstream->istream.iostream.close = i_stream_concat_close; cstream->istream.iostream.destroy = i_stream_concat_destroy; cstream->istream.iostream.set_max_buffer_size = i_stream_concat_set_max_buffer_size; cstream->istream.max_buffer_size = max_buffer_size; cstream->istream.read = i_stream_concat_read; cstream->istream.seek = i_stream_concat_seek; cstream->istream.stat = i_stream_concat_stat; cstream->istream.istream.readable_fd = FALSE; cstream->istream.istream.blocking = blocking; cstream->istream.istream.seekable = seekable; return i_stream_create(&cstream->istream, NULL, -1); } dovecot-2.2.33.2/src/lib/unicodemap.pl0000755000175000017500000001046113123174404014347 00000000000000#!/usr/bin/env perl use strict; my (%titlecase8, %uni8_decomp); my (@titlecase16_keys, @titlecase16_values); my (@titlecase32_keys, @titlecase32_values); my (@uni16_decomp_keys, @uni16_decomp_values); my (@uni32_decomp_keys, @uni32_decomp_values); my (@multidecomp_keys, @multidecomp_offsets, @multidecomp_values); while (<>) { chomp $_; my @arr = split(";"); my $code = eval("0x".$arr[0]); my $decomp = $arr[5]; my $titlecode = $arr[14]; if ($titlecode ne "") { # titlecase mapping my $value = eval("0x$titlecode"); if ($value == $code) { # the same character, ignore } elsif ($code <= 0xff) { die "Error: We've assumed 8bit keys have max. 16bit values" if ($value > 0xffff); $titlecase8{$code} = $value; } elsif ($code <= 0xffff) { die "Error: We've assumed 16bit keys have max. 16bit values" if ($value > 0xffff); push @titlecase16_keys, $code; push @titlecase16_values, $value; } else { push @titlecase32_keys, $code; push @titlecase32_values, $value; } } elsif ($decomp =~ /(?:\<[^>]*> )?(.+)/) { # decompositions my $decomp_codes = $1; if ($decomp_codes =~ /^([0-9A-Z]*)$/i) { # unicharacter decomposition. use separate lists for this my $value = eval("0x$1"); if ($value > 0xffffffff) { print STDERR "Error: We've assumed decomposition codes are max. 32bit\n"; exit 1; } if ($code <= 0xff) { $uni8_decomp{$code} = $value; } elsif ($code <= 0xffff) { push @uni16_decomp_keys, $code; push @uni16_decomp_values, $value; } else { push @uni32_decomp_keys, $code; push @uni32_decomp_values, $value; } } else { # multicharacter decomposition. if ($code > 0xffffffff) { print STDERR "Error: We've assumed multi-decomposition key codes are max. 32bit\n"; exit 1; } push @multidecomp_keys, $code; push @multidecomp_offsets, scalar(@multidecomp_values); foreach my $dcode (split(" ", $decomp_codes)) { my $value = eval("0x$dcode"); if ($value > 0xffffffff) { print STDERR "Error: We've assumed decomposition codes are max. 32bit\n"; exit 1; } push @multidecomp_values, $value; } push @multidecomp_values, 0; } } } sub print_list { my @list = @{$_[0]}; my $last = $#list; my $n = 0; foreach my $key (@list) { printf("0x%05x", $key); last if ($n == $last); print ","; $n++; if (($n % 8) == 0) { print "\n\t"; } else { print " "; } } } print "/* This file is automatically generated by unicodemap.pl from UnicodeData.txt NOTE: decompositions for characters having titlecase characters are not included, because we first translate everything to titlecase */\n"; sub print_map8 { my %map = %{$_[0]}; my @list; for (my $i = 0; $i <= 0xff; $i++) { if (defined($map{$i})) { push @list, $map{$i}; } else { push @list, $i; } } print_list(\@list); } print "static const uint16_t titlecase8_map[256] = {\n\t"; print_map8(\%titlecase8); print "\n};\n"; print "static const uint16_t titlecase16_keys[] = {\n\t"; print_list(\@titlecase16_keys); print "\n};\n"; print "static const uint16_t titlecase16_values[] = {\n\t"; print_list(\@titlecase16_values); print "\n};\n"; print "static const uint32_t titlecase32_keys[] = {\n\t"; print_list(\@titlecase32_keys); print "\n};\n"; print "static const uint32_t titlecase32_values[] = {\n\t"; print_list(\@titlecase32_values); print "\n};\n"; print "static const uint16_t uni8_decomp_map[256] = {\n\t"; print_map8(\%uni8_decomp); print "\n};\n"; print "static const uint16_t uni16_decomp_keys[] = {\n\t"; print_list(\@uni16_decomp_keys); print "\n};\n"; print "static const uint32_t uni16_decomp_values[] = {\n\t"; print_list(\@uni16_decomp_values); print "\n};\n"; print "static const uint32_t uni32_decomp_keys[] = {\n\t"; print_list(\@uni32_decomp_keys); print "\n};\n"; print "static const uint32_t uni32_decomp_values[] = {\n\t"; print_list(\@uni32_decomp_values); print "\n};\n"; print "static const uint32_t multidecomp_keys[] = {\n\t"; print_list(\@multidecomp_keys); print "\n};\n"; print "static const uint16_t multidecomp_offsets[] = {\n\t"; print_list(\@multidecomp_offsets); print "\n};\n"; print "static const uint32_t multidecomp_values[] = {\n\t"; print_list(\@multidecomp_values); print "\n};\n"; dovecot-2.2.33.2/src/lib/printf-format-fix.h0000644000175000017500000000132713123174404015411 00000000000000#ifndef PRINTF_FORMAT_FIX_H #define PRINTF_FORMAT_FIX_H /* Replaces %m in format with strerror(errno) and panics if %n modifier is used. If the format string was modified, it's returned from data stack. */ const char *printf_format_fix(const char *format) ATTR_FORMAT_ARG(1); /* Like printf_format_fix(), except return also the format string's length. */ const char *printf_format_fix_get_len(const char *format, size_t *len_r) ATTR_FORMAT_ARG(1); /* Like printf_format_fix(), except the format string is written to data stack without actually allocating it. Data stack must not be used until format string is no longer needed. */ const char *printf_format_fix_unsafe(const char *format) ATTR_FORMAT_ARG(1); #endif dovecot-2.2.33.2/src/lib/istream-failure-at.c0000644000175000017500000000546013165463624015540 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "istream-failure-at.h" struct failure_at_istream { struct istream_private istream; char *error_string; uoff_t failure_offset; }; static void i_stream_failure_at_destroy(struct iostream_private *stream) { struct failure_at_istream *fstream = (struct failure_at_istream *)stream; i_free(fstream->error_string); i_stream_unref(&fstream->istream.parent); } static ssize_t i_stream_failure_at_read(struct istream_private *stream) { struct failure_at_istream *fstream = (struct failure_at_istream *)stream; uoff_t new_offset; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); new_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (ret >= 0 && new_offset >= fstream->failure_offset) { if (stream->istream.v_offset >= fstream->failure_offset) { /* we already passed the wanted failure offset, return error immediately. */ stream->pos = stream->skip; stream->istream.stream_errno = errno = EIO; io_stream_set_error(&stream->iostream, "%s", fstream->error_string); ret = -1; } else { /* return data up to the wanted failure offset and on the next read() call return failure */ size_t new_pos = fstream->failure_offset - stream->istream.v_offset + stream->skip; i_assert(new_pos >= stream->skip && stream->pos >= new_pos); ret -= stream->pos - new_pos; stream->pos = new_pos; } } else if (ret < 0 && stream->istream.stream_errno == 0 && fstream->failure_offset == (uoff_t)-1) { /* failure at EOF */ stream->istream.stream_errno = errno = EIO; io_stream_set_error(&stream->iostream, "%s", fstream->error_string); } return ret; } struct istream * i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, const char *error_string) { struct failure_at_istream *fstream; fstream = i_new(struct failure_at_istream, 1); fstream->istream.max_buffer_size = input->real_stream->max_buffer_size; fstream->istream.stream_size_passthrough = TRUE; fstream->istream.read = i_stream_failure_at_read; fstream->istream.iostream.destroy = i_stream_failure_at_destroy; fstream->istream.istream.readable_fd = input->readable_fd; fstream->istream.istream.blocking = input->blocking; fstream->istream.istream.seekable = input->seekable; fstream->error_string = i_strdup(error_string); fstream->failure_offset = failure_offset; return i_stream_create(&fstream->istream, input, i_stream_get_fd(input)); } struct istream * i_stream_create_failure_at_eof(struct istream *input, const char *error_string) { return i_stream_create_failure_at(input, (uoff_t)-1, error_string); } dovecot-2.2.33.2/src/lib/test-istream.c0000644000175000017500000000362013123174404014447 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "istream.h" static void test_istream_children(void) { struct istream *parent, *child1, *child2; const unsigned char *data; size_t size; test_begin("istream children"); parent = test_istream_create_data("123456789", 9); test_istream_set_max_buffer_size(parent, 3); child1 = i_stream_create_limit(parent, (uoff_t)-1); child2 = i_stream_create_limit(parent, (uoff_t)-1); /* child1 read beginning */ test_assert(i_stream_read(child1) == 3); data = i_stream_get_data(child1, &size); test_assert(size == 3 && memcmp(data, "123", 3) == 0); i_stream_skip(child1, 3); /* child1 read middle.. */ test_assert(i_stream_read(child1) == 3); data = i_stream_get_data(child1, &size); test_assert(size == 3 && memcmp(data, "456", 3) == 0); /* child2 read beginning.. */ test_assert(i_stream_read(child2) == 3); data = i_stream_get_data(child2, &size); test_assert(size == 3 && memcmp(data, "123", 3) == 0); /* child1 check middle again.. the parent has been modified, so it can't return the original data (without some code changes). */ test_assert(i_stream_get_data_size(child1) == 0); i_stream_skip(child1, 3); /* child1 read end */ test_assert(i_stream_read(child1) == 3); data = i_stream_get_data(child1, &size); test_assert(size == 3 && memcmp(data, "789", 3) == 0); i_stream_skip(child1, 3); test_assert(i_stream_read(child1) == -1); /* child2 check beginning again.. */ test_assert(i_stream_get_data_size(child1) == 0); i_stream_skip(child2, 3); /* child2 read middle */ test_assert(i_stream_read(child2) == 3); data = i_stream_get_data(child2, &size); test_assert(size == 3 && memcmp(data, "456", 3) == 0); i_stream_skip(child2, 3); i_stream_destroy(&child1); i_stream_destroy(&child2); i_stream_destroy(&parent); test_end(); } void test_istream(void) { test_istream_children(); } dovecot-2.2.33.2/src/director/0002755000175000017500000000000013172375612013020 500000000000000dovecot-2.2.33.2/src/director/Makefile.in0000644000175000017500000006244713172375572015025 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = director$(EXEEXT) noinst_PROGRAMS = director-test$(EXEEXT) $(am__EXEEXT_1) subdir = src/director ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = test-user-directory$(EXEEXT) am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) am_director_OBJECTS = main.$(OBJEXT) auth-connection.$(OBJEXT) \ director.$(OBJEXT) director-connection.$(OBJEXT) \ director-host.$(OBJEXT) director-request.$(OBJEXT) \ director-settings.$(OBJEXT) doveadm-connection.$(OBJEXT) \ login-connection.$(OBJEXT) mail-host.$(OBJEXT) \ notify-connection.$(OBJEXT) user-directory.$(OBJEXT) director_OBJECTS = $(am_director_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_director_test_OBJECTS = director-test.$(OBJEXT) director_test_OBJECTS = $(am_director_test_OBJECTS) am_test_user_directory_OBJECTS = test-user-directory.$(OBJEXT) test_user_directory_OBJECTS = $(am_test_user_directory_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(director_SOURCES) $(director_test_SOURCES) \ $(test_user_directory_SOURCES) DIST_SOURCES = $(director_SOURCES) $(director_test_SOURCES) \ $(test_user_directory_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-program-client director_LDADD = $(LIBDOVECOT) director_DEPENDENCIES = $(LIBDOVECOT_DEPS) director_SOURCES = \ main.c \ auth-connection.c \ director.c \ director-connection.c \ director-host.c \ director-request.c \ director-settings.c \ doveadm-connection.c \ login-connection.c \ mail-host.c \ notify-connection.c \ user-directory.c noinst_HEADERS = \ auth-connection.h \ director.h \ director-connection.h \ director-host.h \ director-request.h \ director-settings.h \ doveadm-connection.h \ login-connection.h \ mail-host.h \ notify-connection.h \ user-directory.h director_test_LDADD = $(LIBDOVECOT) director_test_DEPENDENCIES = $(LIBDOVECOT_DEPS) director_test_SOURCES = \ director-test.c test_programs = \ test-user-directory test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_user_directory_SOURCES = test-user-directory.c test_user_directory_LDADD = user-directory.o $(test_libs) test_user_directory_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/director/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/director/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list director$(EXEEXT): $(director_OBJECTS) $(director_DEPENDENCIES) $(EXTRA_director_DEPENDENCIES) @rm -f director$(EXEEXT) $(AM_V_CCLD)$(LINK) $(director_OBJECTS) $(director_LDADD) $(LIBS) director-test$(EXEEXT): $(director_test_OBJECTS) $(director_test_DEPENDENCIES) $(EXTRA_director_test_DEPENDENCIES) @rm -f director-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(director_test_OBJECTS) $(director_test_LDADD) $(LIBS) test-user-directory$(EXEEXT): $(test_user_directory_OBJECTS) $(test_user_directory_DEPENDENCIES) $(EXTRA_test_user_directory_DEPENDENCIES) @rm -f test-user-directory$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_user_directory_OBJECTS) $(test_user_directory_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director-host.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director-request.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/director.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-host.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-user-directory.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user-directory.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ clean-pkglibexecPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstPROGRAMS clean-pkglibexecPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkglibexecPROGRAMS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/director/login-connection.c0000644000175000017500000002214613165463624016357 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "strescape.h" #include "master-service.h" #include "director.h" #include "director-request.h" #include "auth-connection.h" #include "login-connection.h" #include #define AUTHREPLY_PROTOCOL_MAJOR_VERSION 1 #define AUTHREPLY_PROTOCOL_MINOR_VERSION 0 struct login_connection { struct login_connection *prev, *next; int refcount; enum login_connection_type type; int fd; struct io *io; struct istream *input; struct ostream *output; struct auth_connection *auth; struct director *dir; unsigned int handshaked:1; unsigned int destroyed:1; }; struct login_host_request { struct login_connection *conn; char *line, *username; struct ip_addr local_ip; in_port_t local_port; in_port_t dest_port; bool director_proxy_maybe; }; static struct login_connection *login_connections; static void auth_input_line(const char *line, void *context); static void login_connection_unref(struct login_connection **_conn); static void login_connection_input(struct login_connection *conn) { struct ostream *output; unsigned char buf[4096]; ssize_t ret; ret = read(conn->fd, buf, sizeof(buf)); if (ret <= 0) { if (ret < 0) { if (errno == EAGAIN) return; if (errno != ECONNRESET) i_error("read(login connection) failed: %m"); } login_connection_deinit(&conn); return; } output = auth_connection_get_output(conn->auth); o_stream_nsend(output, buf, ret); } static void login_connection_authreply_input(struct login_connection *conn) { bool bail = FALSE; const char *line; while (!bail && (line = i_stream_read_next_line(conn->input)) != NULL) T_BEGIN { if (!conn->handshaked) { if (!version_string_verify(line, "director-authreply-client", AUTHREPLY_PROTOCOL_MAJOR_VERSION)) { i_error("authreply client sent invalid handshake: %s", line); login_connection_deinit(&conn); bail = TRUE; /* don't return from within a T_BEGIN {...} T_END */ } else { conn->handshaked = TRUE; } } else { auth_input_line(line, conn); } } T_END; if (bail) return; if (conn->input->eof) { if (conn->input->stream_errno != 0 && conn->input->stream_errno != ECONNRESET) { i_error("read(authreply connection) failed: %s", i_stream_get_error(conn->input)); } login_connection_deinit(&conn); } } static void login_connection_send_line(struct login_connection *conn, const char *line) { struct const_iovec iov[2]; if (conn->destroyed) return; iov[0].iov_base = line; iov[0].iov_len = strlen(line); iov[1].iov_base = "\n"; iov[1].iov_len = 1; o_stream_nsendv(conn->output, iov, N_ELEMENTS(iov)); } static bool login_host_request_is_self(struct login_host_request *request, const struct ip_addr *dest_ip) { if (!net_ip_compare(dest_ip, &request->local_ip)) return FALSE; if (request->dest_port != 0 && request->local_port != 0 && request->dest_port != request->local_port) return FALSE; return TRUE; } static void login_host_callback(const struct ip_addr *ip, const char *hostname, const char *errormsg, void *context) { struct login_host_request *request = context; struct director *dir = request->conn->dir; const char *line, *line_params; unsigned int secs; if (ip == NULL) { if (strncmp(request->line, "OK\t", 3) == 0) line_params = request->line + 3; else if (strncmp(request->line, "PASS\t", 5) == 0) line_params = request->line + 5; else i_panic("BUG: Unexpected line: %s", request->line); i_error("director: User %s host lookup failed: %s", request->username, errormsg); line = t_strconcat("FAIL\t", t_strcut(line_params, '\t'), "\ttemp", NULL); } else if (request->director_proxy_maybe && login_host_request_is_self(request, ip)) { line = request->line; } else { string_t *str = t_str_new(64); secs = dir->set->director_user_expire / 2; str_printfa(str, "%s\tproxy_refresh=%u\t", request->line, secs); if (hostname == NULL || hostname[0] == '\0') str_printfa(str, "host=%s", net_ip2addr(ip)); else { str_printfa(str, "host=%s\thostip=%s", hostname, net_ip2addr(ip)); } line = str_c(str); } login_connection_send_line(request->conn, line); login_connection_unref(&request->conn); i_free(request->username); i_free(request->line); i_free(request); } static void auth_input_line(const char *line, void *context) { struct login_connection *conn = context; struct login_host_request *request, temp_request; const char *const *args, *line_params, *username = NULL, *tag = ""; bool proxy = FALSE, host = FALSE; if (line == NULL) { /* auth connection died -> kill also this login connection */ login_connection_deinit(&conn); return; } if (conn->type != LOGIN_CONNECTION_TYPE_USERDB && strncmp(line, "OK\t", 3) == 0) line_params = line + 3; else if (conn->type == LOGIN_CONNECTION_TYPE_USERDB && strncmp(line, "PASS\t", 5) == 0) line_params = line + 5; else { login_connection_send_line(conn, line); return; } /* OK [] */ args = t_strsplit_tabescaped(line_params); if (*args != NULL) { /* we should always get here, but in case we don't just forward as-is and let login process handle the error. */ args++; } i_zero(&temp_request); for (; *args != NULL; args++) { if (strncmp(*args, "proxy", 5) == 0 && ((*args)[5] == '=' || (*args)[5] == '\0')) proxy = TRUE; else if (strncmp(*args, "host=", 5) == 0) host = TRUE; else if (strncmp(*args, "lip=", 4) == 0) { if (net_addr2ip((*args) + 4, &temp_request.local_ip) < 0) i_error("auth sent invalid lip field: %s", (*args) + 6); } else if (strncmp(*args, "lport=", 6) == 0) { if (net_str2port((*args) + 6, &temp_request.local_port) < 0) i_error("auth sent invalid lport field: %s", (*args) + 6); } else if (strncmp(*args, "port=", 5) == 0) { if (net_str2port((*args) + 5, &temp_request.dest_port) < 0) i_error("auth sent invalid port field: %s", (*args) + 6); } else if (strncmp(*args, "destuser=", 9) == 0) username = *args + 9; else if (strncmp(*args, "director_tag=", 13) == 0) tag = *args + 13; else if (strncmp(*args, "director_proxy_maybe", 20) == 0 && ((*args)[20] == '=' || (*args)[20] == '\0')) temp_request.director_proxy_maybe = TRUE; else if (strncmp(*args, "user=", 5) == 0) { if (username == NULL) username = *args + 5; } } if ((!proxy && !temp_request.director_proxy_maybe) || host || username == NULL) { login_connection_send_line(conn, line); return; } if (*conn->dir->set->master_user_separator != '\0') { /* with master user logins we still want to use only the login username */ username = t_strcut(username, *conn->dir->set->master_user_separator); } /* we need to add the host. the lookup might be asynchronous */ request = i_new(struct login_host_request, 1); *request = temp_request; request->conn = conn; request->line = i_strdup(line); request->username = i_strdup(username); conn->refcount++; director_request(conn->dir, username, tag, login_host_callback, request); } struct login_connection * login_connection_init(struct director *dir, int fd, struct auth_connection *auth, enum login_connection_type type) { struct login_connection *conn; conn = i_new(struct login_connection, 1); conn->refcount = 1; conn->fd = fd; conn->dir = dir; conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); if (type != LOGIN_CONNECTION_TYPE_AUTHREPLY) { i_assert(auth != NULL); conn->auth = auth; conn->io = io_add(conn->fd, IO_READ, login_connection_input, conn); auth_connection_set_callback(conn->auth, auth_input_line, conn); } else { i_assert(auth == NULL); conn->input = i_stream_create_fd(conn->fd, IO_BLOCK_SIZE, FALSE); conn->io = io_add(conn->fd, IO_READ, login_connection_authreply_input, conn); o_stream_nsend_str(conn->output, t_strdup_printf( "VERSION\tdirector-authreply-server\t%d\t%d\n", AUTHREPLY_PROTOCOL_MAJOR_VERSION, AUTHREPLY_PROTOCOL_MINOR_VERSION)); } conn->type = type; DLLIST_PREPEND(&login_connections, conn); return conn; } void login_connection_deinit(struct login_connection **_conn) { struct login_connection *conn = *_conn; *_conn = NULL; if (conn->destroyed) return; conn->destroyed = TRUE; DLLIST_REMOVE(&login_connections, conn); io_remove(&conn->io); if (conn->input != NULL) i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(login connection) failed: %m"); conn->fd = -1; if (conn->auth != NULL) auth_connection_deinit(&conn->auth); login_connection_unref(&conn); master_service_client_connection_destroyed(master_service); } static void login_connection_unref(struct login_connection **_conn) { struct login_connection *conn = *_conn; *_conn = NULL; i_assert(conn->refcount > 0); if (--conn->refcount == 0) i_free(conn); } void login_connections_deinit(void) { while (login_connections != NULL) { struct login_connection *conn = login_connections; login_connection_deinit(&conn); } } dovecot-2.2.33.2/src/director/doveadm-connection.c0000644000175000017500000007301613165463624016670 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "array.h" #include "str.h" #include "strescape.h" #include "llist.h" #include "master-service.h" #include "user-directory.h" #include "mail-host.h" #include "director.h" #include "director-host.h" #include "director-request.h" #include "director-connection.h" #include "doveadm-connection.h" #include #define DOVEADM_PROTOCOL_VERSION_MAJOR 1 #define DOVEADM_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" #define MAX_VALID_VHOST_COUNT 1000 #define DEFAULT_MAX_MOVING_USERS 100 #define DOVEADM_CONNECTION_RING_SYNC_TIMEOUT_MSECS (30*1000) enum doveadm_director_cmd_ret { DOVEADM_DIRECTOR_CMD_RET_FAIL = -1, DOVEADM_DIRECTOR_CMD_RET_UNFINISHED = 0, DOVEADM_DIRECTOR_CMD_RET_OK = 1, DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK, }; enum doveadm_director_cmd_flag { DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC = 0x01, }; typedef void doveadm_connection_ring_sync_callback_t(struct doveadm_connection *); struct director_reset_cmd { struct director_reset_cmd *prev, *next; struct director *dir; struct doveadm_connection *_conn; struct director_user_iter *iter; unsigned int host_idx, hosts_count; unsigned int max_moving_users; bool users_killed; }; struct doveadm_connection { struct doveadm_connection *prev, *next; int fd; struct io *io; struct istream *input; struct ostream *output; struct director *dir; struct timeout *to_ring_sync_abort; struct director_reset_cmd *reset_cmd; doveadm_connection_ring_sync_callback_t *ring_sync_callback; const char **cmd_pending_args; unsigned int cmd_pending_idx; unsigned int handshaked:1; }; static struct doveadm_connection *doveadm_connections; static struct doveadm_connection *doveadm_ring_sync_pending_connections; static struct director_reset_cmd *reset_cmds = NULL; static void doveadm_connection_set_io(struct doveadm_connection *conn); static void doveadm_connection_deinit(struct doveadm_connection **_conn); static void doveadm_connection_ring_sync_list_move(struct doveadm_connection *conn); static void doveadm_connection_cmd_run_synced(struct doveadm_connection *conn); static enum doveadm_director_cmd_ret doveadm_cmd_host_list(struct doveadm_connection *conn, const char *const *args ATTR_UNUSED) { struct mail_host *const *hostp; string_t *str = t_str_new(1024); array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) { str_printfa(str, "%s\t%u\t%u\t", net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count, (*hostp)->user_count); str_append_tabescaped(str, mail_host_get_tag(*hostp)); str_printfa(str, "\t%c\t%ld", (*hostp)->down ? 'D' : 'U', (long)(*hostp)->last_updown_change); str_append_c(str, '\n'); } str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_host_list_removed(struct doveadm_connection *conn, const char *const *args ATTR_UNUSED) { struct mail_host_list *orig_hosts_list; struct mail_host *const *orig_hosts, *const *cur_hosts; unsigned int i, j, orig_hosts_count, cur_hosts_count; string_t *str = t_str_new(1024); int ret; orig_hosts_list = mail_hosts_init(conn->dir->set->director_user_expire, conn->dir->set->director_consistent_hashing, NULL); (void)mail_hosts_parse_and_add(orig_hosts_list, conn->dir->set->director_mail_servers); orig_hosts = array_get(mail_hosts_get(orig_hosts_list), &orig_hosts_count); cur_hosts = array_get(mail_hosts_get(conn->dir->mail_hosts), &cur_hosts_count); /* the hosts are sorted by IP */ for (i = j = 0; i < orig_hosts_count && j < cur_hosts_count; ) { ret = net_ip_cmp(&orig_hosts[i]->ip, &cur_hosts[j]->ip); if (ret == 0) i++, j++; else if (ret > 0) j++; else { str_printfa(str, "%s\n", net_ip2addr(&orig_hosts[i]->ip)); i++; } } for (; i < orig_hosts_count; i++) str_printfa(str, "%s\n", net_ip2addr(&orig_hosts[i]->ip)); str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); mail_hosts_deinit(&orig_hosts_list); return DOVEADM_DIRECTOR_CMD_RET_OK; } static void doveadm_director_append_status(struct director *dir, string_t *str) { if (!dir->ring_handshaked) str_append(str, "handshaking"); else if (dir->ring_synced) str_append(str, "synced"); else { str_printfa(str, "syncing - last sync %d secs ago", (int)(ioloop_time - dir->ring_last_sync_time)); } } static void doveadm_director_connection_append_status(struct director_connection *conn, string_t *str) { if (!director_connection_is_handshaked(conn)) str_append(str, "handshaking"); else if (director_connection_is_synced(conn)) str_append(str, "synced"); else str_append(str, "syncing"); } static void doveadm_director_host_append_status(struct director *dir, const struct director_host *host, string_t *str) { struct director_connection *conn = NULL; if (dir->left != NULL && director_connection_get_host(dir->left) == host) conn = dir->left; else if (dir->right != NULL && director_connection_get_host(dir->right) == host) conn = dir->right; else { /* we might have a connection that is being connected */ struct director_connection *const *connp; array_foreach(&dir->connections, connp) { if (director_connection_get_host(*connp) == host) { conn = *connp; break; } } } if (conn != NULL) doveadm_director_connection_append_status(conn, str); } static enum doveadm_director_cmd_ret doveadm_cmd_director_list(struct doveadm_connection *conn, const char *const *args ATTR_UNUSED) { struct director *dir = conn->dir; struct director_host *const *hostp; string_t *str = t_str_new(1024); const char *type; bool left, right; time_t last_failed; array_foreach(&dir->dir_hosts, hostp) { const struct director_host *host = *hostp; left = dir->left != NULL && director_connection_get_host(dir->left) == host; right = dir->right != NULL && director_connection_get_host(dir->right) == host; if (host->removed) type = "removed"; else if (dir->self_host == host) type = "self"; else if (left) type = right ? "l+r" : "left"; else if (right) type = "right"; else type = ""; last_failed = I_MAX(host->last_network_failure, host->last_protocol_failure); str_printfa(str, "%s\t%u\t%s\t%lu\t", net_ip2addr(&host->ip), host->port, type, (unsigned long)last_failed); if (dir->self_host == host) doveadm_director_append_status(dir, str); else doveadm_director_host_append_status(dir, host, str); str_append_c(str, '\n'); } str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_director_add(struct doveadm_connection *conn, const char *const *args) { struct director_host *host; struct ip_addr ip; in_port_t port = conn->dir->self_port; if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0 || (args[1] != NULL && net_str2port(args[1], &port) < 0)) { i_error("doveadm sent invalid DIRECTOR-ADD parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } if (director_host_lookup(conn->dir, &ip, port) == NULL) { host = director_host_add(conn->dir, &ip, port); director_notify_ring_added(host, conn->dir->self_host, TRUE); } o_stream_nsend(conn->output, "OK\n", 3); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_director_remove(struct doveadm_connection *conn, const char *const *args) { struct director_host *host; struct ip_addr ip; in_port_t port = 0; if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0 || (args[1] != NULL && net_str2port(args[1], &port) < 0)) { i_error("doveadm sent invalid DIRECTOR-REMOVE parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } host = port != 0 ? director_host_lookup(conn->dir, &ip, port) : director_host_lookup_ip(conn->dir, &ip); if (host == NULL) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else { director_ring_remove(host, conn->dir->self_host); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } } static enum doveadm_director_cmd_ret doveadm_cmd_host_set_or_update(struct doveadm_connection *conn, const char *const *args, bool update) { struct director *dir = conn->dir; const char *ip_str, *tag = ""; struct mail_host *host; struct ip_addr ip; unsigned int vhost_count = UINT_MAX; ip_str = args[0]; if (ip_str != NULL) { tag = strchr(ip_str, '@'); if (tag == NULL) tag = ""; else ip_str = t_strdup_until(ip_str, tag++); } if (ip_str == NULL || net_addr2ip(ip_str, &ip) < 0 || (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0) || (args[1] == NULL && update)) { i_error("doveadm sent invalid %s parameters", update ? "HOST-UPDATE" : "HOST-SET"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } if (vhost_count > MAX_VALID_VHOST_COUNT && vhost_count != UINT_MAX) { o_stream_nsend_str(conn->output, "vhost count too large\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } host = mail_host_lookup(dir->mail_hosts, &ip); if (host == NULL) { if (update) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } host = mail_host_add_ip(dir->mail_hosts, &ip, tag); } else if (tag[0] != '\0' && strcmp(mail_host_get_tag(host), tag) != 0) { o_stream_nsend_str(conn->output, "host tag can't be changed\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else if (host->desynced) { o_stream_nsend_str(conn->output, "host is already being updated - try again later\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } if (vhost_count != UINT_MAX) mail_host_set_vhost_count(host, vhost_count, "doveadm: "); /* NOTE: we don't support changing a tag for an existing host. it needs to be removed first. otherwise it would be a bit ugly to handle. */ director_update_host(dir, dir->self_host, NULL, host); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_host_set(struct doveadm_connection *conn, const char *const *args) { return doveadm_cmd_host_set_or_update(conn, args, FALSE); } static enum doveadm_director_cmd_ret doveadm_cmd_host_update(struct doveadm_connection *conn, const char *const *args) { return doveadm_cmd_host_set_or_update(conn, args, TRUE); } static enum doveadm_director_cmd_ret doveadm_cmd_host_updown(struct doveadm_connection *conn, bool down, const char *const *args) { struct mail_host *host; struct ip_addr ip; if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) { i_error("doveadm sent invalid %s parameters: %s", down ? "HOST-DOWN" : "HOST-UP", args[0] == NULL ? "" : args[0]); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } if (host->down == down) { o_stream_nsend_str(conn->output, "OK\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else if (host->desynced) { o_stream_nsend_str(conn->output, "host is already being updated - try again later\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else { mail_host_set_down(host, down, ioloop_time, "doveadm: "); director_update_host(conn->dir, conn->dir->self_host, NULL, host); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } } static enum doveadm_director_cmd_ret doveadm_cmd_host_up(struct doveadm_connection *conn, const char *const *args) { return doveadm_cmd_host_updown(conn, FALSE, args); } static enum doveadm_director_cmd_ret doveadm_cmd_host_down(struct doveadm_connection *conn, const char *const *args) { return doveadm_cmd_host_updown(conn, TRUE, args); } static enum doveadm_director_cmd_ret doveadm_cmd_host_remove(struct doveadm_connection *conn, const char *const *args) { struct mail_host *host; struct ip_addr ip; if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) { i_error("doveadm sent invalid HOST-REMOVE parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else { director_remove_host(conn->dir, conn->dir->self_host, NULL, host); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } } static void doveadm_cmd_host_flush_all(struct doveadm_connection *conn) { struct mail_host *const *hostp; unsigned int total_user_count = 0; array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) { total_user_count += (*hostp)->user_count; director_flush_host(conn->dir, conn->dir->self_host, NULL, *hostp); } i_warning("Flushed all backend hosts with %u users. This is an unsafe " "operation and may cause the same users to end up in multiple backends.", total_user_count); o_stream_nsend(conn->output, "OK\n", 3); } static enum doveadm_director_cmd_ret doveadm_cmd_host_flush(struct doveadm_connection *conn, const char *const *args) { struct mail_host *host; struct ip_addr ip; if (args[0] == NULL || args[0][0] == '\0') { doveadm_cmd_host_flush_all(conn); return DOVEADM_DIRECTOR_CMD_RET_OK; } if (net_addr2ip(args[0], &ip) < 0) { i_error("doveadm sent invalid HOST-FLUSH parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } else { director_flush_host(conn->dir, conn->dir->self_host, NULL, host); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } } static void doveadm_reset_cmd_free(struct director_reset_cmd *cmd) { DLLIST_REMOVE(&reset_cmds, cmd); if (cmd->iter != NULL) director_iterate_users_deinit(&cmd->iter); if (cmd->_conn != NULL) cmd->_conn->reset_cmd = NULL; i_free(cmd); } static bool director_host_reset_users(struct director_reset_cmd *cmd, struct mail_host *host) { struct director *dir = cmd->dir; struct user *user; struct mail_host *new_host; if (dir->users_moving_count >= cmd->max_moving_users) return FALSE; if (dir->right != NULL) director_connection_cork(dir->right); if (cmd->iter == NULL) { cmd->iter = director_iterate_users_init(dir); cmd->users_killed = FALSE; } while ((user = director_iterate_users_next(cmd->iter)) != NULL) { if (user->host != host) continue; new_host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash, mail_host_get_tag(host)); if (new_host != host) T_BEGIN { if (new_host != NULL) { director_move_user(dir, dir->self_host, NULL, user->username_hash, new_host); } else { /* there are no more available backends. kick the user instead. */ director_kill_user(dir, dir->self_host, user, user->host->tag, user->host, TRUE); cmd->users_killed = TRUE; } } T_END; if (dir->users_moving_count >= cmd->max_moving_users) break; } if (user == NULL) { director_iterate_users_deinit(&cmd->iter); if (cmd->users_killed) { /* no more backends. we already sent kills. now remove the users entirely from the host. */ director_flush_host(dir, dir->self_host, NULL, host); } } if (dir->right != NULL) director_connection_uncork(dir->right); return user == NULL; } static bool director_reset_cmd_run(struct director_reset_cmd *cmd) { struct mail_host *const *hosts; unsigned int count; hosts = array_get(mail_hosts_get(cmd->dir->mail_hosts), &count); if (count > cmd->hosts_count) count = cmd->hosts_count; while (cmd->host_idx < count) { if (!director_host_reset_users(cmd, hosts[cmd->host_idx])) return FALSE; cmd->host_idx++; } if (cmd->_conn != NULL) { struct doveadm_connection *conn = cmd->_conn; o_stream_nsend(conn->output, "OK\n", 3); if (conn->io == NULL) doveadm_connection_set_io(conn); } doveadm_reset_cmd_free(cmd); return TRUE; } static enum doveadm_director_cmd_ret doveadm_cmd_host_reset_users(struct doveadm_connection *conn, const char *const *args) { struct director_reset_cmd *cmd; struct ip_addr ip; struct mail_host *const *hosts; unsigned int i = 0, count; unsigned int max_moving_users = DEFAULT_MAX_MOVING_USERS; if (args[0] != NULL && args[1] != NULL && str_to_uint(args[1], &max_moving_users) < 0) { i_error("doveadm sent invalid HOST-RESET-USERS parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } hosts = array_get(mail_hosts_get(conn->dir->mail_hosts), &count); if (args[0] != NULL && args[0][0] != '\0') { if (net_addr2ip(args[0], &ip) < 0) { i_error("doveadm sent invalid HOST-RESET-USERS ip: %s", args[0]); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } for (i = 0; i < count; i++) { if (net_ip_compare(&hosts[i]->ip, &ip)) break; } if (i == count) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } count = i+1; } conn->reset_cmd = cmd = i_new(struct director_reset_cmd, 1); cmd->dir = conn->dir; cmd->_conn = conn; cmd->max_moving_users = max_moving_users; cmd->host_idx = i; cmd->hosts_count = count; DLLIST_PREPEND(&reset_cmds, cmd); if (!director_reset_cmd_run(cmd)) { /* we still have work to do. don't handle any more doveadm input until we're finished. */ io_remove(&conn->io); return DOVEADM_DIRECTOR_CMD_RET_UNFINISHED; } return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_user_lookup(struct doveadm_connection *conn, const char *const *args) { struct user *user; struct mail_host *host; const char *username, *tag; unsigned int username_hash; struct mail_tag *mail_tag; string_t *str = t_str_new(256); if (args[0] == NULL) { username = ""; tag = ""; } else { username = args[0]; tag = args[1] != NULL ? args[1] : ""; } if (str_to_uint(username, &username_hash) < 0) username_hash = director_get_username_hash(conn->dir, username); /* get user's current host */ mail_tag = mail_tag_find(conn->dir->mail_hosts, tag); user = mail_tag == NULL ? NULL : user_directory_lookup(mail_tag->users, username_hash); if (user == NULL) str_append(str, "\t0"); else { str_printfa(str, "%s\t%u", net_ip2addr(&user->host->ip), user->timestamp + conn->dir->set->director_user_expire); } /* get host if it wasn't in user directory */ host = mail_host_get_by_hash(conn->dir->mail_hosts, username_hash, tag); if (host == NULL) str_append(str, "\t"); else str_printfa(str, "\t%s", net_ip2addr(&host->ip)); /* get host with default configuration */ host = mail_host_get_by_hash(conn->dir->orig_config_hosts, username_hash, tag); if (host == NULL) str_append(str, "\t\n"); else str_printfa(str, "\t%s\n", net_ip2addr(&host->ip)); o_stream_nsend(conn->output, str_data(str), str_len(str)); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_user_list(struct doveadm_connection *conn, const char *const *args) { struct director_user_iter *iter; struct user *user; struct ip_addr ip; if (args[0] != NULL && args[0][0] != '\0') { if (net_addr2ip(args[0], &ip) < 0) { i_error("doveadm sent invalid USER-LIST parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } } else { ip.family = 0; } iter = director_iterate_users_init(conn->dir); while ((user = director_iterate_users_next(iter)) != NULL) { if (ip.family == 0 || net_ip_compare(&ip, &user->host->ip)) T_BEGIN { unsigned int expire_time = user->timestamp + conn->dir->set->director_user_expire; o_stream_nsend_str(conn->output, t_strdup_printf( "%u\t%u\t%s\n", user->username_hash, expire_time, net_ip2addr(&user->host->ip))); } T_END; } director_iterate_users_deinit(&iter); o_stream_nsend(conn->output, "\n", 1); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_user_move(struct doveadm_connection *conn, const char *const *args) { unsigned int username_hash; struct user *user; struct mail_host *host; struct ip_addr ip; if (args[0] == NULL || args[1] == NULL || net_addr2ip(args[1], &ip) < 0) { i_error("doveadm sent invalid USER-MOVE parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { o_stream_nsend_str(conn->output, "NOTFOUND\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } if (str_to_uint(args[0], &username_hash) < 0) username_hash = director_get_username_hash(conn->dir, args[0]); user = user_directory_lookup(host->tag->users, username_hash); if (user != NULL && USER_IS_BEING_KILLED(user)) { o_stream_nsend_str(conn->output, "TRYAGAIN\n"); return DOVEADM_DIRECTOR_CMD_RET_OK; } if (user == NULL || user->host != host) { director_move_user(conn->dir, conn->dir->self_host, NULL, username_hash, host); } else { /* already the correct host. reset the user's timeout. */ user_directory_refresh(host->tag->users, user); director_update_user(conn->dir, conn->dir->self_host, user); } o_stream_nsend(conn->output, "OK\n", 3); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_user_kick(struct doveadm_connection *conn, const char *const *args) { if (args[0] == NULL) { i_error("doveadm sent invalid USER-KICK parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } director_kick_user(conn->dir, conn->dir->self_host, NULL, args[0]); o_stream_nsend(conn->output, "OK\n", 3); return DOVEADM_DIRECTOR_CMD_RET_OK; } static enum doveadm_director_cmd_ret doveadm_cmd_user_kick_alt(struct doveadm_connection *conn, const char *const *args) { if (str_array_length(args) < 2) { i_error("doveadm sent invalid USER-KICK-ALT parameters"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } director_kick_user_alt(conn->dir, conn->dir->self_host, NULL, args[0], args[1]); o_stream_nsend(conn->output, "OK\n", 3); return DOVEADM_DIRECTOR_CMD_RET_OK; } struct { const char *name; enum doveadm_director_cmd_ret (*cmd) (struct doveadm_connection *conn, const char *const *args); enum doveadm_director_cmd_flag flags; } doveadm_director_commands[] = { { "HOST-LIST", doveadm_cmd_host_list, 0 }, { "HOST-LIST-REMOVED", doveadm_cmd_host_list_removed, 0 }, { "DIRECTOR-LIST", doveadm_cmd_director_list, 0 }, { "DIRECTOR-ADD", doveadm_cmd_director_add, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "DIRECTOR-REMOVE", doveadm_cmd_director_remove, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-SET", doveadm_cmd_host_set, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-UPDATE", doveadm_cmd_host_update, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-UP", doveadm_cmd_host_up, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-DOWN", doveadm_cmd_host_down, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-REMOVE", doveadm_cmd_host_remove, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-FLUSH", doveadm_cmd_host_flush, DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC }, { "HOST-RESET-USERS", doveadm_cmd_host_reset_users, 0 }, { "USER-LOOKUP", doveadm_cmd_user_lookup, 0 }, { "USER-LIST", doveadm_cmd_user_list, 0 }, { "USER-MOVE", doveadm_cmd_user_move, 0 }, { "USER-KICK", doveadm_cmd_user_kick, 0 }, { "USER-KICK-ALT", doveadm_cmd_user_kick_alt, 0 }, }; static void doveadm_connection_ring_sync_timeout(struct doveadm_connection *conn) { doveadm_connection_ring_sync_list_move(conn); o_stream_nsend_str(conn->output, "Ring sync timed out\n"); doveadm_connection_set_io(conn); io_set_pending(conn->io); i_free_and_null(conn->cmd_pending_args); } static void doveadm_connection_set_ring_sync_callback(struct doveadm_connection *conn, doveadm_connection_ring_sync_callback_t *callback) { i_assert(conn->ring_sync_callback == NULL); i_assert(conn->to_ring_sync_abort == NULL); conn->ring_sync_callback = callback; io_remove(&conn->io); DLLIST_REMOVE(&doveadm_connections, conn); DLLIST_PREPEND(&doveadm_ring_sync_pending_connections, conn); conn->to_ring_sync_abort = timeout_add(DOVEADM_CONNECTION_RING_SYNC_TIMEOUT_MSECS, doveadm_connection_ring_sync_timeout, conn); } static void doveadm_connection_ret_ok(struct doveadm_connection *conn) { o_stream_nsend(conn->output, "OK\n", 3); } static enum doveadm_director_cmd_ret doveadm_connection_cmd_run(struct doveadm_connection *conn, const char *const *args, unsigned int i) { enum doveadm_director_cmd_ret ret; if ((doveadm_director_commands[i].flags & DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC) != 0 && !conn->dir->ring_synced) { /* wait for ring to be synced before running the command */ conn->cmd_pending_args = p_strarray_dup(default_pool, args); conn->cmd_pending_idx = i; doveadm_connection_set_ring_sync_callback(conn, doveadm_connection_cmd_run_synced); return DOVEADM_DIRECTOR_CMD_RET_UNFINISHED; } ret = doveadm_director_commands[i].cmd(conn, args); if (ret != DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK) return ret; /* Delay sending OK until ring is synced. This way doveadm will know whether the call actually succeeded or not. */ if (conn->dir->ring_synced) { /* director is alone */ i_assert(conn->dir->right == NULL && conn->dir->left == NULL); o_stream_nsend(conn->output, "OK\n", 3); return DOVEADM_DIRECTOR_CMD_RET_OK; } doveadm_connection_set_ring_sync_callback(conn, doveadm_connection_ret_ok); return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK; } static void doveadm_connection_cmd_run_synced(struct doveadm_connection *conn) { const char **args = conn->cmd_pending_args; conn->cmd_pending_args = NULL; (void)doveadm_connection_cmd_run(conn, args, conn->cmd_pending_idx); i_free(args); } static enum doveadm_director_cmd_ret doveadm_connection_cmd(struct doveadm_connection *conn, const char *line) { const char *cmd, *const *args; args = t_strsplit_tabescaped(line); if (args[0] == NULL) { i_error("doveadm sent empty command line"); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } cmd = args[0]; args++; for (unsigned int i = 0; i < N_ELEMENTS(doveadm_director_commands); i++) { if (strcmp(doveadm_director_commands[i].name, cmd) == 0) return doveadm_connection_cmd_run(conn, args, i); } i_error("doveadm sent unknown command: %s", line); return DOVEADM_DIRECTOR_CMD_RET_FAIL; } static void doveadm_connection_input(struct doveadm_connection *conn) { const char *line; enum doveadm_director_cmd_ret ret = DOVEADM_DIRECTOR_CMD_RET_OK; if (!conn->handshaked) { if ((line = i_stream_read_next_line(conn->input)) == NULL) { if (conn->input->eof || conn->input->stream_errno != 0) doveadm_connection_deinit(&conn); return; } if (!version_string_verify(line, "director-doveadm", DOVEADM_PROTOCOL_VERSION_MAJOR)) { i_error("doveadm not compatible with this server " "(mixed old and new binaries?)"); doveadm_connection_deinit(&conn); return; } conn->handshaked = TRUE; } while ((line = i_stream_read_next_line(conn->input)) != NULL && ret == DOVEADM_DIRECTOR_CMD_RET_OK) { T_BEGIN { ret = doveadm_connection_cmd(conn, line); } T_END; } if (conn->input->eof || conn->input->stream_errno != 0 || ret == DOVEADM_DIRECTOR_CMD_RET_FAIL) doveadm_connection_deinit(&conn); } static void doveadm_connection_set_io(struct doveadm_connection *conn) { conn->io = io_add(conn->fd, IO_READ, doveadm_connection_input, conn); } struct doveadm_connection * doveadm_connection_init(struct director *dir, int fd) { struct doveadm_connection *conn; conn = i_new(struct doveadm_connection, 1); conn->fd = fd; conn->dir = dir; conn->input = i_stream_create_fd(conn->fd, 1024, FALSE); conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); doveadm_connection_set_io(conn); o_stream_nsend_str(conn->output, DOVEADM_HANDSHAKE); DLLIST_PREPEND(&doveadm_connections, conn); return conn; } static void doveadm_connection_deinit(struct doveadm_connection **_conn) { struct doveadm_connection *conn = *_conn; *_conn = NULL; i_assert(conn->to_ring_sync_abort == NULL); if (conn->reset_cmd != NULL) { /* finish the move even if doveadm disconnected */ conn->reset_cmd->_conn = NULL; } DLLIST_REMOVE(&doveadm_connections, conn); io_remove(&conn->io); i_stream_unref(&conn->input); o_stream_unref(&conn->output); if (close(conn->fd) < 0) i_error("close(doveadm connection) failed: %m"); i_free(conn); master_service_client_connection_destroyed(master_service); } static void doveadm_connection_ring_sync_list_move(struct doveadm_connection *conn) { timeout_remove(&conn->to_ring_sync_abort); DLLIST_REMOVE(&doveadm_ring_sync_pending_connections, conn); DLLIST_PREPEND(&doveadm_connections, conn); } void doveadm_connections_deinit(void) { while (reset_cmds != NULL) doveadm_reset_cmd_free(reset_cmds); unsigned int pending_count = 0; while (doveadm_ring_sync_pending_connections != NULL) { doveadm_connection_ring_sync_list_move(doveadm_ring_sync_pending_connections); pending_count++; } if (pending_count > 0) i_warning("Shutting down while %u doveadm connections were waiting for ring sync", pending_count); while (doveadm_connections != NULL) { struct doveadm_connection *conn = doveadm_connections; doveadm_connection_deinit(&conn); } } static void doveadm_connections_continue_reset_cmds(void) { while (reset_cmds != NULL) { if (!director_reset_cmd_run(reset_cmds)) break; } } void doveadm_connections_ring_synced(void) { while (doveadm_ring_sync_pending_connections != NULL) { struct doveadm_connection *conn = doveadm_ring_sync_pending_connections; doveadm_connection_ring_sync_callback_t *callback = conn->ring_sync_callback; conn->ring_sync_callback = NULL; doveadm_connection_ring_sync_list_move(conn); doveadm_connection_set_io(conn); io_set_pending(conn->io); callback(conn); } doveadm_connections_continue_reset_cmds(); } dovecot-2.2.33.2/src/director/director.c0000644000175000017500000012723613165463624014733 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "strescape.h" #include "log-throttle.h" #include "ipc-client.h" #include "program-client.h" #include "var-expand.h" #include "istream.h" #include "ostream.h" #include "iostream-temp.h" #include "mail-user-hash.h" #include "user-directory.h" #include "mail-host.h" #include "director-host.h" #include "director-connection.h" #include "director.h" #define DIRECTOR_IPC_PROXY_PATH "ipc" #define DIRECTOR_RECONNECT_RETRY_SECS 60 #define DIRECTOR_RECONNECT_TIMEOUT_MSECS (30*1000) #define DIRECTOR_USER_MOVE_TIMEOUT_MSECS (30*1000) #define DIRECTOR_SYNC_TIMEOUT_MSECS (5*1000) #define DIRECTOR_RING_MIN_WAIT_SECS 20 #define DIRECTOR_QUICK_RECONNECT_TIMEOUT_MSECS 1000 #define DIRECTOR_DELAYED_DIR_REMOVE_MSECS (1000*30) bool director_debug; const char *user_kill_state_names[USER_KILL_STATE_DELAY+1] = { "none", "killing", "notify-received", "waiting-for-notify", "waiting-for-everyone", "flushing", "delay", }; static struct log_throttle *user_move_throttle; static struct log_throttle *user_kill_fail_throttle; static void director_hosts_purge_removed(struct director *dir); static const struct log_throttle_settings director_log_throttle_settings = { .throttle_at_max_per_interval = 100, .unthrottle_at_max_per_interval = 2, }; static void director_user_kill_finish_delayed(struct director *dir, struct user *user, bool skip_delay); static bool director_is_self_ip_set(struct director *dir) { struct ip_addr ip; net_get_ip_any4(&ip); if (net_ip_compare(&dir->self_ip, &ip)) return FALSE; net_get_ip_any6(&ip); if (net_ip_compare(&dir->self_ip, &ip)) return FALSE; return TRUE; } static void director_find_self_ip(struct director *dir) { struct director_host *const *hosts; unsigned int i, count; hosts = array_get(&dir->dir_hosts, &count); for (i = 0; i < count; i++) { if (net_try_bind(&hosts[i]->ip) == 0) { dir->self_ip = hosts[i]->ip; return; } } i_fatal("director_servers doesn't list ourself"); } void director_find_self(struct director *dir) { if (dir->self_host != NULL) return; if (!director_is_self_ip_set(dir)) director_find_self_ip(dir); dir->self_host = director_host_lookup(dir, &dir->self_ip, dir->self_port); if (dir->self_host == NULL) { i_fatal("director_servers doesn't list ourself (%s:%u)", net_ip2addr(&dir->self_ip), dir->self_port); } dir->self_host->self = TRUE; } static unsigned int director_find_self_idx(struct director *dir) { struct director_host *const *hosts; unsigned int i, count; i_assert(dir->self_host != NULL); hosts = array_get(&dir->dir_hosts, &count); for (i = 0; i < count; i++) { if (hosts[i] == dir->self_host) return i; } i_unreached(); } static bool director_has_outgoing_connection(struct director *dir, struct director_host *host) { struct director_connection *const *connp; array_foreach(&dir->connections, connp) { if (director_connection_get_host(*connp) == host && !director_connection_is_incoming(*connp)) return TRUE; } return FALSE; } static void director_log_connect(struct director *dir, struct director_host *host, const char *reason) { string_t *str = t_str_new(128); if (host->last_network_failure > 0) { str_printfa(str, ", last network failure %ds ago", (int)(ioloop_time - host->last_network_failure)); } if (host->last_protocol_failure > 0) { str_printfa(str, ", last protocol failure %ds ago", (int)(ioloop_time - host->last_protocol_failure)); } i_info("Connecting to %s:%u (as %s%s): %s", net_ip2addr(&host->ip), host->port, net_ip2addr(&dir->self_ip), str_c(str), reason); } int director_connect_host(struct director *dir, struct director_host *host, const char *reason) { in_port_t port; int fd; if (director_has_outgoing_connection(dir, host)) return 0; director_log_connect(dir, host, reason); port = dir->test_port != 0 ? dir->test_port : host->port; fd = net_connect_ip(&host->ip, port, &dir->self_ip); if (fd == -1) { host->last_network_failure = ioloop_time; i_error("connect(%s) failed: %m", host->name); return -1; } /* Reset timestamp so that director_connect() won't skip this host while we're still trying to connect to it */ host->last_network_failure = 0; (void)director_connection_init_out(dir, fd, host); return 0; } static struct director_host * director_get_preferred_right_host(struct director *dir) { struct director_host *const *hosts, *host; unsigned int i, count, self_idx; hosts = array_get(&dir->dir_hosts, &count); if (count == 1) { /* self */ return NULL; } self_idx = director_find_self_idx(dir); for (i = 0; i < count; i++) { host = hosts[(self_idx + i + 1) % count]; if (!host->removed) return host; } /* self, with some removed hosts */ return NULL; } static void director_quick_reconnect_retry(struct director *dir) { director_connect(dir, "Alone in director ring - trying to connect to others"); } static bool director_wait_for_others(struct director *dir) { struct director_host *const *hostp; /* don't assume we're alone until we've attempted to connect to others for a while */ if (dir->ring_first_alone != 0 && ioloop_time - dir->ring_first_alone > DIRECTOR_RING_MIN_WAIT_SECS) return FALSE; if (dir->ring_first_alone == 0) dir->ring_first_alone = ioloop_time; /* reset all failures and try again */ array_foreach(&dir->dir_hosts, hostp) { (*hostp)->last_network_failure = 0; (*hostp)->last_protocol_failure = 0; } if (dir->to_reconnect != NULL) timeout_remove(&dir->to_reconnect); dir->to_reconnect = timeout_add(DIRECTOR_QUICK_RECONNECT_TIMEOUT_MSECS, director_quick_reconnect_retry, dir); return TRUE; } void director_connect(struct director *dir, const char *reason) { struct director_host *const *hosts; unsigned int i, count, self_idx; self_idx = director_find_self_idx(dir); /* try to connect to first working server on our right side. the left side is supposed to connect to us. */ hosts = array_get(&dir->dir_hosts, &count); for (i = 1; i < count; i++) { unsigned int idx = (self_idx + i) % count; if (hosts[idx]->removed) continue; if (hosts[idx]->last_network_failure + DIRECTOR_RECONNECT_RETRY_SECS > ioloop_time) { /* connection failed recently, don't try retrying here */ continue; } if (hosts[idx]->last_protocol_failure + DIRECTOR_PROTOCOL_FAILURE_RETRY_SECS > ioloop_time) { /* the director recently sent invalid protocol data, don't try retrying yet */ continue; } if (director_connect_host(dir, hosts[idx], reason) == 0) { /* success */ return; } } if (count > 1 && director_wait_for_others(dir)) return; /* we're the only one */ if (count > 1) { i_warning("director: Couldn't connect to right side, " "we must be the only director left"); } if (dir->left != NULL) { /* since we couldn't connect to it, it must have failed recently */ i_warning("director: Assuming %s is dead, disconnecting", director_connection_get_name(dir->left)); director_connection_deinit(&dir->left, "This connection is dead?"); } dir->ring_min_version = DIRECTOR_VERSION_MINOR; if (!dir->ring_handshaked) director_set_ring_handshaked(dir); else if (!dir->ring_synced) director_set_ring_synced(dir); } void director_set_ring_handshaked(struct director *dir) { i_assert(!dir->ring_handshaked); if (dir->to_handshake_warning != NULL) timeout_remove(&dir->to_handshake_warning); if (dir->ring_handshake_warning_sent) { i_warning("Directors have been connected, " "continuing delayed requests"); dir->ring_handshake_warning_sent = FALSE; } dir_debug("Director ring handshaked"); dir->ring_handshaked = TRUE; director_set_ring_synced(dir); } static void director_reconnect_timeout(struct director *dir) { struct director_host *cur_host, *preferred_host = director_get_preferred_right_host(dir); cur_host = dir->right == NULL ? NULL : director_connection_get_host(dir->right); if (preferred_host == NULL) { /* all directors have been removed, try again later */ } else if (cur_host != preferred_host) { (void)director_connect_host(dir, preferred_host, "Reconnect attempt to preferred director"); } else { /* the connection hasn't finished sync yet. keep this timeout for now. */ } } void director_set_ring_synced(struct director *dir) { struct director_host *host; i_assert(!dir->ring_synced); i_assert((dir->left != NULL && dir->right != NULL) || (dir->left == NULL && dir->right == NULL)); if (dir->to_handshake_warning != NULL) timeout_remove(&dir->to_handshake_warning); if (dir->ring_handshake_warning_sent) { i_warning("Ring is synced, continuing delayed requests " "(syncing took %d secs, hosts_hash=%u)", (int)(ioloop_time - dir->ring_last_sync_time), mail_hosts_hash(dir->mail_hosts)); dir->ring_handshake_warning_sent = FALSE; } host = dir->right == NULL ? NULL : director_connection_get_host(dir->right); if (dir->to_reconnect != NULL) timeout_remove(&dir->to_reconnect); if (host != director_get_preferred_right_host(dir)) { /* try to reconnect to preferred host later */ dir->to_reconnect = timeout_add(DIRECTOR_RECONNECT_TIMEOUT_MSECS, director_reconnect_timeout, dir); } if (dir->left != NULL) director_connection_set_synced(dir->left, TRUE); if (dir->right != NULL) director_connection_set_synced(dir->right, TRUE); if (dir->to_sync != NULL) timeout_remove(&dir->to_sync); dir->ring_synced = TRUE; dir->ring_last_sync_time = ioloop_time; /* If there are any director hosts still marked as "removed", we can safely remove those now. The entire director cluster knows about the removal now. */ director_hosts_purge_removed(dir); mail_hosts_set_synced(dir->mail_hosts); director_set_state_changed(dir); } void director_sync_send(struct director *dir, struct director_host *host, uint32_t seq, unsigned int minor_version, unsigned int timestamp, unsigned int hosts_hash) { string_t *str; if (host == dir->self_host) dir->last_sync_sent_ring_change_counter = dir->ring_change_counter; str = t_str_new(128); str_printfa(str, "SYNC\t%s\t%u\t%u", net_ip2addr(&host->ip), host->port, seq); if (minor_version > 0 && director_connection_get_minor_version(dir->right) > 0) { /* only minor_version>0 supports extra parameters */ str_printfa(str, "\t%u\t%u\t%u", minor_version, timestamp, hosts_hash); } str_append_c(str, '\n'); director_connection_send(dir->right, str_c(str)); /* ping our connections in case either of them are hanging. if they are, we want to know it fast. */ if (dir->left != NULL) director_connection_ping(dir->left); director_connection_ping(dir->right); } bool director_resend_sync(struct director *dir) { if (!dir->ring_synced && dir->left != NULL && dir->right != NULL) { /* send a new SYNC in case the previous one got dropped */ dir->self_host->last_sync_timestamp = ioloop_time; director_sync_send(dir, dir->self_host, dir->sync_seq, DIRECTOR_VERSION_MINOR, ioloop_time, mail_hosts_hash(dir->mail_hosts)); if (dir->to_sync != NULL) timeout_reset(dir->to_sync); return TRUE; } return FALSE; } static void director_sync_timeout(struct director *dir) { i_assert(!dir->ring_synced); if (director_resend_sync(dir)) i_error("Ring SYNC seq=%u appears to have got lost, resending", dir->sync_seq); } void director_set_ring_unsynced(struct director *dir) { if (dir->ring_synced) { dir->ring_synced = FALSE; dir->ring_last_sync_time = ioloop_time; } if (dir->to_sync == NULL) { dir->to_sync = timeout_add(DIRECTOR_SYNC_TIMEOUT_MSECS, director_sync_timeout, dir); } else { timeout_reset(dir->to_sync); } } static void director_sync(struct director *dir) { /* we're synced again when we receive this SYNC back */ dir->sync_seq++; if (dir->right == NULL && dir->left == NULL) { /* we're alone. if we're already synced, don't become unsynced. */ return; } director_set_ring_unsynced(dir); if (dir->sync_frozen) { dir->sync_pending = TRUE; return; } if (dir->right == NULL) { i_assert(!dir->ring_synced || (dir->left == NULL && dir->right == NULL)); dir_debug("Ring is desynced (seq=%u, no right connection)", dir->sync_seq); return; } dir_debug("Ring is desynced (seq=%u, sending SYNC to %s)", dir->sync_seq, dir->right == NULL ? "(nowhere)" : director_connection_get_name(dir->right)); /* send PINGs to our connections more rapidly until we've synced again. if the connection has actually died, we don't need to wait (and delay requests) for as long to detect it */ if (dir->left != NULL) director_connection_set_synced(dir->left, FALSE); director_connection_set_synced(dir->right, FALSE); director_sync_send(dir, dir->self_host, dir->sync_seq, DIRECTOR_VERSION_MINOR, ioloop_time, mail_hosts_hash(dir->mail_hosts)); } void director_sync_freeze(struct director *dir) { struct director_connection *const *connp; i_assert(!dir->sync_frozen); i_assert(!dir->sync_pending); array_foreach(&dir->connections, connp) director_connection_cork(*connp); dir->sync_frozen = TRUE; } void director_sync_thaw(struct director *dir) { struct director_connection *const *connp; i_assert(dir->sync_frozen); dir->sync_frozen = FALSE; if (dir->sync_pending) { dir->sync_pending = FALSE; director_sync(dir); } array_foreach(&dir->connections, connp) director_connection_uncork(*connp); } void director_notify_ring_added(struct director_host *added_host, struct director_host *src, bool log) { const char *cmd; if (log) { i_info("Adding director %s to ring (requested by %s)", added_host->name, src->name); } added_host->dir->ring_change_counter++; cmd = t_strdup_printf("DIRECTOR\t%s\t%u\n", net_ip2addr(&added_host->ip), added_host->port); director_update_send(added_host->dir, src, cmd); } static void director_hosts_purge_removed(struct director *dir) { struct director_host *const *hosts, *host; unsigned int i, count; if (dir->to_remove_dirs != NULL) timeout_remove(&dir->to_remove_dirs); hosts = array_get(&dir->dir_hosts, &count); for (i = 0; i < count; ) { if (hosts[i]->removed) { host = hosts[i]; director_host_free(&host); hosts = array_get(&dir->dir_hosts, &count); } else { i++; } } } void director_ring_remove(struct director_host *removed_host, struct director_host *src) { struct director *dir = removed_host->dir; struct director_connection *const *conns, *conn; unsigned int i, count; const char *cmd; i_info("Removing director %s from ring (requested by %s)", removed_host->name, src->name); if (removed_host->self && !src->self) { /* others will just disconnect us */ return; } if (!removed_host->self) { /* mark the host as removed and fully remove it later. this delay is needed, because the removal may trigger director reconnections, which may send the director back and we don't want to re-add it */ removed_host->removed = TRUE; if (dir->to_remove_dirs == NULL) { dir->to_remove_dirs = timeout_add(DIRECTOR_DELAYED_DIR_REMOVE_MSECS, director_hosts_purge_removed, dir); } } /* if our left or ride side gets removed, notify them first before disconnecting. */ cmd = t_strdup_printf("DIRECTOR-REMOVE\t%s\t%u\n", net_ip2addr(&removed_host->ip), removed_host->port); director_update_send_version(dir, src, DIRECTOR_VERSION_RING_REMOVE, cmd); /* disconnect any connections to the host */ conns = array_get(&dir->connections, &count); for (i = 0; i < count; ) { conn = conns[i]; if (director_connection_get_host(conn) != removed_host || removed_host->self) i++; else { director_connection_deinit(&conn, "Removing from ring"); conns = array_get(&dir->connections, &count); } } if (dir->right == NULL) director_connect(dir, "Reconnecting after director was removed"); director_sync(dir); } static void director_send_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) { const char *host_tag = mail_host_get_tag(host); string_t *str; if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } str = t_str_new(128); str_printfa(str, "HOST\t%s\t%u\t%u\t%s\t%u", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, net_ip2addr(&host->ip), host->vhost_count); if (dir->ring_min_version >= DIRECTOR_VERSION_TAGS_V2) { str_append_c(str, '\t'); str_append_tabescaped(str, host_tag); } else if (host_tag[0] != '\0' && dir->ring_min_version < DIRECTOR_VERSION_TAGS_V2) { if (dir->ring_min_version < DIRECTOR_VERSION_TAGS) { i_error("Ring has directors that don't support tags - removing host %s with tag '%s'", net_ip2addr(&host->ip), host_tag); } else { i_error("Ring has directors that support mixed versions of tags - removing host %s with tag '%s'", net_ip2addr(&host->ip), host_tag); } director_remove_host(dir, NULL, NULL, host); return; } if (dir->ring_min_version >= DIRECTOR_VERSION_UPDOWN) { str_printfa(str, "\t%c%ld\t", host->down ? 'D' : 'U', (long)host->last_updown_change); /* add any further version checks here - these directors ignore any extra unknown arguments */ if (host->hostname != NULL) str_append_tabescaped(str, host->hostname); } str_append_c(str, '\n'); director_update_send(dir, src, str_c(str)); } void director_resend_hosts(struct director *dir) { struct mail_host *const *hostp; array_foreach(mail_hosts_get(dir->mail_hosts), hostp) director_send_host(dir, dir->self_host, NULL, *hostp); } void director_update_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) { /* update state in case this is the first mail host being added */ director_set_state_changed(dir); dir_debug("Updating host %s vhost_count=%u " "down=%d last_updown_change=%ld (hosts_hash=%u)", net_ip2addr(&host->ip), host->vhost_count, host->down, (long)host->last_updown_change, mail_hosts_hash(dir->mail_hosts)); director_send_host(dir, src, orig_src, host); /* mark the host desynced until ring is synced again. except if we're alone in the ring that never happens. */ if (dir->right != NULL || dir->left != NULL) host->desynced = TRUE; director_sync(dir); } void director_remove_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) { struct user_directory *users = host->tag->users; if (src != NULL) { if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } director_update_send(dir, src, t_strdup_printf( "HOST-REMOVE\t%s\t%u\t%u\t%s\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, net_ip2addr(&host->ip))); } user_directory_remove_host(users, host); mail_host_remove(host); director_sync(dir); } void director_flush_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) { struct user_directory *users = host->tag->users; if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } director_update_send(dir, src, t_strdup_printf( "HOST-FLUSH\t%s\t%u\t%u\t%s\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, net_ip2addr(&host->ip))); user_directory_remove_host(users, host); director_sync(dir); } void director_update_user(struct director *dir, struct director_host *src, struct user *user) { i_assert(src != NULL); i_assert(!user->weak); director_update_send(dir, src, t_strdup_printf("USER\t%u\t%s\n", user->username_hash, net_ip2addr(&user->host->ip))); } void director_update_user_weak(struct director *dir, struct director_host *src, struct director_connection *src_conn, struct director_host *orig_src, struct user *user) { const char *cmd; i_assert(src != NULL); i_assert(user->weak); if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } cmd = t_strdup_printf("USER-WEAK\t%s\t%u\t%u\t%u\t%s\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, user->username_hash, net_ip2addr(&user->host->ip)); if (src != dir->self_host && dir->left != NULL && dir->right != NULL && director_connection_get_host(dir->left) == director_connection_get_host(dir->right)) { /* only two directors in this ring and we're forwarding USER-WEAK from one director back to itself via another so it sees we've received it. we can't use director_update_send() for this, because it doesn't send data back to the source. */ if (dir->right == src_conn) director_connection_send(dir->left, cmd); else if (dir->left == src_conn) director_connection_send(dir->right, cmd); else i_unreached(); } else { director_update_send(dir, src, cmd); } } static void director_flush_user_continue(int result, struct director_kill_context *ctx) { struct director *dir = ctx->dir; ctx->callback_pending = FALSE; struct user *user = user_directory_lookup(ctx->tag->users, ctx->username_hash); if (result == 0) { struct istream *is = iostream_temp_finish(&ctx->reply, (size_t)-1); char *data; i_stream_set_return_partial_line(is, TRUE); data = i_stream_read_next_line(is); i_error("%s: Failed to flush user hash %u in host %s: %s", ctx->socket_path, ctx->username_hash, net_ip2addr(&ctx->host_ip), data == NULL ? "(no output to stdout)" : data); while((data = i_stream_read_next_line(is)) != NULL) { i_error("%s: Failed to flush user hash %u in host %s: %s", ctx->socket_path, ctx->username_hash, net_ip2addr(&ctx->host_ip), data); } i_stream_unref(&is); } else { o_stream_unref(&ctx->reply); } program_client_destroy(&ctx->pclient); if (!DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx)) { /* user was already freed - ignore */ dir_debug("User %u freed while flushing, result=%d", ctx->username_hash, result); i_assert(ctx->to_move == NULL); i_free(ctx); } else { /* ctx is freed later via user->kill_ctx */ dir_debug("Flushing user %u finished, result=%d", ctx->username_hash, result); director_user_kill_finish_delayed(dir, user, result == 1); } } static void director_flush_user(struct director *dir, struct user *user) { struct director_kill_context *ctx = user->kill_ctx; struct var_expand_table tab[] = { { 'i', net_ip2addr(&user->host->ip), "ip" }, { 'h', user->host->hostname, "host" }, { '\0', NULL, NULL } }; /* Execute flush script, if set. Only the director that started the user moving will call the flush script. Having each director do it would be redundant since they're all supposed to be performing the same flush task to the same backend. Flushing is also not triggered if we're moving a user that we just created due to the user move. This means that the user doesn't have an old host, so we couldn't really even perform any flushing on the backend. */ if (*dir->set->director_flush_socket == '\0' || ctx->old_host_ip.family == 0 || !ctx->kill_is_self_initiated) { director_user_kill_finish_delayed(dir, user, FALSE); return; } ctx->host_ip = user->host->ip; string_t *s_sock = str_new(default_pool, 32); var_expand(s_sock, dir->set->director_flush_socket, tab); ctx->socket_path = str_free_without_data(&s_sock); const char *error; struct program_client_settings set = { .client_connect_timeout_msecs = 10000, }; restrict_access_init(&set.restrict_set); const char *const args[] = { "FLUSH", t_strdup_printf("%u", user->username_hash), net_ip2addr(&ctx->old_host_ip), net_ip2addr(&user->host->ip), ctx->old_host_down ? "down" : "up", dec2str(ctx->old_host_vhost_count), NULL }; ctx->kill_state = USER_KILL_STATE_FLUSHING; dir_debug("Flushing user %u via %s", user->username_hash, ctx->socket_path); if ((program_client_create(ctx->socket_path, args, &set, FALSE, &ctx->pclient, &error)) != 0) { i_error("%s: Failed to flush user hash %u in host %s: %s", ctx->socket_path, user->username_hash, net_ip2addr(&user->host->ip), error); director_flush_user_continue(0, ctx); return; } ctx->reply = iostream_temp_create_named("/tmp", 0, t_strdup_printf("flush response from %s", net_ip2addr(&user->host->ip))); o_stream_set_no_error_handling(ctx->reply, TRUE); program_client_set_output(ctx->pclient, ctx->reply); ctx->callback_pending = TRUE; program_client_run_async(ctx->pclient, director_flush_user_continue, ctx); } static void director_user_move_finished(struct director *dir) { i_assert(dir->users_moving_count > 0); dir->users_moving_count--; director_set_state_changed(dir); } static void director_user_move_free(struct user *user) { struct director *dir = user->kill_ctx->dir; struct director_kill_context *kill_ctx = user->kill_ctx; i_assert(kill_ctx != NULL); dir_debug("User %u move finished at state=%s", user->username_hash, user_kill_state_names[kill_ctx->kill_state]); if (kill_ctx->to_move != NULL) timeout_remove(&kill_ctx->to_move); i_free(kill_ctx->socket_path); i_free(kill_ctx); user->kill_ctx = NULL; director_user_move_finished(dir); } static void director_user_kill_finish_delayed_to(struct user *user) { i_assert(user->kill_ctx != NULL); i_assert(user->kill_ctx->kill_state == USER_KILL_STATE_DELAY); director_user_move_free(user); } static void director_user_kill_finish_delayed(struct director *dir, struct user *user, bool skip_delay) { if (skip_delay) { user->kill_ctx->kill_state = USER_KILL_STATE_NONE; director_user_move_free(user); return; } user->kill_ctx->kill_state = USER_KILL_STATE_DELAY; /* wait for a while for the kills to finish in the backend server, so there are no longer any processes running for the user before we start letting new in connections to the new server. */ timeout_remove(&user->kill_ctx->to_move); user->kill_ctx->to_move = timeout_add(dir->set->director_user_kick_delay * 1000, director_user_kill_finish_delayed_to, user); } static void director_finish_user_kill(struct director *dir, struct user *user, bool self) { struct director_kill_context *kill_ctx = user->kill_ctx; i_assert(kill_ctx != NULL); i_assert(kill_ctx->kill_state != USER_KILL_STATE_FLUSHING); i_assert(kill_ctx->kill_state != USER_KILL_STATE_DELAY); dir_debug("User %u kill finished - %sstate=%s", user->username_hash, self ? "we started it " : "", user_kill_state_names[kill_ctx->kill_state]); if (dir->right == NULL) { /* we're alone */ director_flush_user(dir, user); } else if (self || kill_ctx->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED) { director_connection_send(dir->right, t_strdup_printf( "USER-KILLED\t%u\n", user->username_hash)); kill_ctx->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE; } else { i_assert(kill_ctx->kill_state == USER_KILL_STATE_KILLING); kill_ctx->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY; } } static void director_user_kill_fail_throttled(unsigned int new_events_count, void *context ATTR_UNUSED) { i_error("Failed to kill %u users' connections", new_events_count); } static void director_kill_user_callback(enum ipc_client_cmd_state state, const char *data, void *context) { struct director_kill_context *ctx = context; struct user *user; /* this is an asynchronous notification about user being killed. there are no guarantees about what might have happened to the user in the mean time. */ switch (state) { case IPC_CLIENT_CMD_STATE_REPLY: /* shouldn't get here. the command reply isn't finished yet. */ return; case IPC_CLIENT_CMD_STATE_OK: break; case IPC_CLIENT_CMD_STATE_ERROR: if (log_throttle_accept(user_kill_fail_throttle)) { i_error("Failed to kill user %u connections: %s", ctx->username_hash, data); } /* we can't really do anything but continue anyway */ break; } ctx->callback_pending = FALSE; user = user_directory_lookup(ctx->tag->users, ctx->username_hash); if (!DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx)) { /* user was already freed - ignore */ i_assert(ctx->to_move == NULL); director_user_move_finished(ctx->dir); i_free(ctx); } else { i_assert(ctx->kill_state == USER_KILL_STATE_KILLING || ctx->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED); /* we were still waiting for the kill notification */ director_finish_user_kill(ctx->dir, user, ctx->kill_is_self_initiated); } } static void director_user_move_throttled(unsigned int new_events_count, void *context ATTR_UNUSED) { i_error("%u users' move timed out, their state may now be inconsistent", new_events_count); } static void director_user_move_timeout(struct user *user) { i_assert(user->kill_ctx != NULL); i_assert(user->kill_ctx->kill_state != USER_KILL_STATE_DELAY); if (log_throttle_accept(user_move_throttle)) { i_error("Finishing user %u move timed out, " "its state may now be inconsistent (state=%s)", user->username_hash, user_kill_state_names[user->kill_ctx->kill_state]); } if (user->kill_ctx->kill_state == USER_KILL_STATE_FLUSHING) { o_stream_unref(&user->kill_ctx->reply); program_client_destroy(&user->kill_ctx->pclient); } director_user_move_free(user); } void director_kill_user(struct director *dir, struct director_host *src, struct user *user, struct mail_tag *tag, struct mail_host *old_host, bool forced_kick) { struct director_kill_context *ctx; const char *cmd; if (USER_IS_BEING_KILLED(user)) { /* User is being moved again before the previous move finished. We'll just continue wherever we left off earlier. */ dir_debug("User %u move restarted - previous kill_state=%s", user->username_hash, user_kill_state_names[user->kill_ctx->kill_state]); return; } user->kill_ctx = ctx = i_new(struct director_kill_context, 1); ctx->dir = dir; ctx->tag = tag; ctx->username_hash = user->username_hash; ctx->kill_is_self_initiated = src->self; if (old_host != NULL) { ctx->old_host_ip = old_host->ip; ctx->old_host_down = old_host->down; ctx->old_host_vhost_count = old_host->vhost_count; } dir->users_moving_count++; ctx->to_move = timeout_add(DIRECTOR_USER_MOVE_TIMEOUT_MSECS, director_user_move_timeout, user); ctx->kill_state = USER_KILL_STATE_KILLING; if ((old_host != NULL && old_host != user->host) || forced_kick) { cmd = t_strdup_printf("proxy\t*\tKICK-DIRECTOR-HASH\t%u", user->username_hash); ctx->callback_pending = TRUE; ipc_client_cmd(dir->ipc_proxy, cmd, director_kill_user_callback, ctx); } else { /* a) we didn't even know about the user before now. don't bother performing a local kick, since it wouldn't kick anything. b) our host was already correct. notify others that we have killed the user, but don't really do it. */ director_finish_user_kill(ctx->dir, user, ctx->kill_is_self_initiated); } } void director_move_user(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash, struct mail_host *host) { struct user_directory *users = host->tag->users; struct mail_host *old_host = NULL; struct user *user; /* 1. move this user's host, and set its "killing" flag to delay all of its future connections until all directors have killed the connections and notified us about it. 2. tell the other directors about the move 3. once user kill callback is called, tell the other directors with USER-KILLED that we're done killing the user. 4. when some director gets a duplicate USER-KILLED, it's responsible for notifying all directors that user is completely killed. 5. after receiving USER-KILLED-EVERYWHERE notification, new connections are again allowed for the user. */ user = user_directory_lookup(users, username_hash); if (user == NULL) { dir_debug("User %u move started: User was nonexistent", username_hash); user = user_directory_add(users, username_hash, host, ioloop_time); } else if (user->host == host) { /* User is already in the wanted host, but another director didn't think so. We'll need to finish the move without killing any of our connections. */ old_host = user->host; user->timestamp = ioloop_time; dir_debug("User %u move forwarded: host is already %s", username_hash, net_ip2addr(&host->ip)); } else { /* user is looked up via the new host's tag, so if it's found the old tag has to be the same. */ i_assert(user->host->tag == host->tag); old_host = user->host; user->host->user_count--; user->host = host; user->host->user_count++; user->timestamp = ioloop_time; dir_debug("User %u move started: host %s -> %s", username_hash, net_ip2addr(&old_host->ip), net_ip2addr(&host->ip)); } if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } director_update_send(dir, src, t_strdup_printf( "USER-MOVE\t%s\t%u\t%u\t%u\t%s\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, user->username_hash, net_ip2addr(&user->host->ip))); /* kill the user only after sending the USER-MOVE, because the kill may finish instantly. */ director_kill_user(dir, src, user, host->tag, old_host, FALSE); } static void director_kick_user_callback(enum ipc_client_cmd_state state ATTR_UNUSED, const char *data ATTR_UNUSED, void *context ATTR_UNUSED) { } void director_kick_user(struct director *dir, struct director_host *src, struct director_host *orig_src, const char *username) { string_t *cmd = t_str_new(64); str_append(cmd, "proxy\t*\tKICK\t"); str_append_tabescaped(cmd, username); ipc_client_cmd(dir->ipc_proxy, str_c(cmd), director_kick_user_callback, (void *)NULL); if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } str_truncate(cmd, 0); str_printfa(cmd, "USER-KICK\t%s\t%u\t%u\t", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq); str_append_tabescaped(cmd, username); str_append_c(cmd, '\n'); director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK, str_c(cmd)); } void director_kick_user_alt(struct director *dir, struct director_host *src, struct director_host *orig_src, const char *field, const char *value) { string_t *cmd = t_str_new(64); str_append(cmd, "proxy\t*\tKICK-ALT\t"); str_append_tabescaped(cmd, field); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, value); ipc_client_cmd(dir->ipc_proxy, str_c(cmd), director_kick_user_callback, (void *)NULL); if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } str_truncate(cmd, 0); str_printfa(cmd, "USER-KICK-ALT\t%s\t%u\t%u\t", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq); str_append_tabescaped(cmd, field); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, value); str_append_c(cmd, '\n'); director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK_ALT, str_c(cmd)); } void director_kick_user_hash(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash, const struct ip_addr *except_ip) { const char *cmd; cmd = t_strdup_printf("proxy\t*\tKICK-DIRECTOR-HASH\t%u\t%s", username_hash, net_ip2addr(except_ip)); ipc_client_cmd(dir->ipc_proxy, cmd, director_kick_user_callback, (void *)NULL); if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } cmd = t_strdup_printf("USER-KICK-HASH\t%s\t%u\t%u\t%u\t%s\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, username_hash, net_ip2addr(except_ip)); director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK, cmd); } static void director_send_user_killed_everywhere(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash) { if (orig_src == NULL) { orig_src = dir->self_host; orig_src->last_seq++; } director_update_send(dir, src, t_strdup_printf( "USER-KILLED-EVERYWHERE\t%s\t%u\t%u\t%u\n", net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, username_hash)); } static void director_user_tag_killed(struct director *dir, struct mail_tag *tag, unsigned int username_hash) { struct user *user; user = user_directory_lookup(tag->users, username_hash); if (user == NULL || !USER_IS_BEING_KILLED(user)) return; switch (user->kill_ctx->kill_state) { case USER_KILL_STATE_KILLING: user->kill_ctx->kill_state = USER_KILL_STATE_KILLING_NOTIFY_RECEIVED; break; case USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY: director_finish_user_kill(dir, user, TRUE); break; case USER_KILL_STATE_KILLING_NOTIFY_RECEIVED: dir_debug("User %u kill_state=%s - ignoring USER-KILLED", username_hash, user_kill_state_names[user->kill_ctx->kill_state]); break; case USER_KILL_STATE_NONE: case USER_KILL_STATE_FLUSHING: case USER_KILL_STATE_DELAY: /* move restarted. state=none can also happen if USER-MOVE was sent while we were still moving. send back USER-KILLED-EVERYWHERE to avoid hangs. */ director_send_user_killed_everywhere(dir, dir->self_host, NULL, username_hash); break; case USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE: director_user_killed_everywhere(dir, dir->self_host, NULL, username_hash); break; } } void director_user_killed(struct director *dir, unsigned int username_hash) { struct mail_tag *const *tagp; array_foreach(mail_hosts_get_tags(dir->mail_hosts), tagp) director_user_tag_killed(dir, *tagp, username_hash); } static void director_user_tag_killed_everywhere(struct director *dir, struct mail_tag *tag, struct director_host *src, struct director_host *orig_src, unsigned int username_hash) { struct user *user; user = user_directory_lookup(tag->users, username_hash); if (user == NULL) { dir_debug("User %u no longer exists - ignoring USER-KILLED-EVERYWHERE", username_hash); return; } if (!USER_IS_BEING_KILLED(user)) { dir_debug("User %u is no longer being killed - ignoring USER-KILLED-EVERYWHERE", username_hash); return; } if (user->kill_ctx->kill_state != USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE) { dir_debug("User %u kill_state=%s - ignoring USER-KILLED-EVERYWHERE", username_hash, user_kill_state_names[user->kill_ctx->kill_state]); return; } director_flush_user(dir, user); director_send_user_killed_everywhere(dir, src, orig_src, username_hash); } void director_user_killed_everywhere(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash) { struct mail_tag *const *tagp; array_foreach(mail_hosts_get_tags(dir->mail_hosts), tagp) { director_user_tag_killed_everywhere(dir, *tagp, src, orig_src, username_hash); } } static void director_state_callback_timeout(struct director *dir) { timeout_remove(&dir->to_callback); dir->state_change_callback(dir); } void director_set_state_changed(struct director *dir) { /* we may get called to here from various places. use a timeout to make sure the state callback is called with a clean state. */ if (dir->to_callback == NULL) { dir->to_callback = timeout_add(0, director_state_callback_timeout, dir); } } void director_update_send(struct director *dir, struct director_host *src, const char *cmd) { director_update_send_version(dir, src, 0, cmd); } void director_update_send_version(struct director *dir, struct director_host *src, unsigned int min_version, const char *cmd) { struct director_connection *const *connp; i_assert(src != NULL); array_foreach(&dir->connections, connp) { if (director_connection_get_host(*connp) != src && director_connection_get_minor_version(*connp) >= min_version) director_connection_send(*connp, cmd); } } static void director_user_freed(struct user *user) { if (user->kill_ctx != NULL) { /* director_user_expire is very short. user expired before moving the user finished or timed out. */ if (user->kill_ctx->callback_pending) { /* kill_ctx is used as a callback parameter. only remove the timeout and finish the free later. */ if (user->kill_ctx->to_move != NULL) timeout_remove(&user->kill_ctx->to_move); } else { director_user_move_free(user); } } } struct director * director_init(const struct director_settings *set, const struct ip_addr *listen_ip, in_port_t listen_port, director_state_change_callback_t *callback) { struct director *dir; dir = i_new(struct director, 1); dir->set = set; dir->self_port = listen_port; dir->self_ip = *listen_ip; dir->state_change_callback = callback; i_array_init(&dir->dir_hosts, 16); i_array_init(&dir->pending_requests, 16); i_array_init(&dir->connections, 8); dir->mail_hosts = mail_hosts_init(set->director_user_expire, set->director_consistent_hashing, director_user_freed); dir->ipc_proxy = ipc_client_init(DIRECTOR_IPC_PROXY_PATH); dir->ring_min_version = DIRECTOR_VERSION_MINOR; return dir; } void director_deinit(struct director **_dir) { struct director *dir = *_dir; struct director_host *const *hostp, *host; struct director_connection *conn, *const *connp; *_dir = NULL; while (array_count(&dir->connections) > 0) { connp = array_idx(&dir->connections, 0); conn = *connp; director_connection_deinit(&conn, "Shutting down"); } mail_hosts_deinit(&dir->mail_hosts); mail_hosts_deinit(&dir->orig_config_hosts); ipc_client_deinit(&dir->ipc_proxy); if (dir->to_reconnect != NULL) timeout_remove(&dir->to_reconnect); if (dir->to_handshake_warning != NULL) timeout_remove(&dir->to_handshake_warning); if (dir->to_request != NULL) timeout_remove(&dir->to_request); if (dir->to_sync != NULL) timeout_remove(&dir->to_sync); if (dir->to_remove_dirs != NULL) timeout_remove(&dir->to_remove_dirs); if (dir->to_callback != NULL) timeout_remove(&dir->to_callback); while (array_count(&dir->dir_hosts) > 0) { hostp = array_idx(&dir->dir_hosts, 0); host = *hostp; director_host_free(&host); } array_free(&dir->pending_requests); array_free(&dir->dir_hosts); array_free(&dir->connections); i_free(dir); } void dir_debug(const char *fmt, ...) { va_list args; if (!director_debug) return; va_start(args, fmt); T_BEGIN { i_debug("%s", t_strdup_vprintf(fmt, args)); } T_END; va_end(args); } struct director_user_iter { struct director *dir; unsigned int tag_idx; struct user_directory_iter *user_iter; }; struct director_user_iter *director_iterate_users_init(struct director *dir) { struct director_user_iter *iter = i_new(struct director_user_iter, 1); iter->dir = dir; return iter; } struct user *director_iterate_users_next(struct director_user_iter *iter) { const ARRAY_TYPE(mail_tag) *tags; struct user *user; i_assert(iter != NULL); if (iter->user_iter == NULL) { tags = mail_hosts_get_tags(iter->dir->mail_hosts); if (iter->tag_idx >= array_count(tags)) return NULL; struct mail_tag *const *tagp = array_idx(tags, iter->tag_idx); iter->user_iter = user_directory_iter_init((*tagp)->users); } user = user_directory_iter_next(iter->user_iter); if (user == NULL) { user_directory_iter_deinit(&iter->user_iter); iter->tag_idx++; return director_iterate_users_next(iter); } else return user; } void director_iterate_users_deinit(struct director_user_iter **_iter) { i_assert(_iter != NULL && *_iter != NULL); struct director_user_iter *iter = *_iter; *_iter = NULL; if (iter->user_iter != NULL) user_directory_iter_deinit(&iter->user_iter); i_free(iter); } unsigned int director_get_username_hash(struct director *dir, const char *username) { return mail_user_hash(username, dir->set->director_username_hash); } void directors_init(void) { user_move_throttle = log_throttle_init(&director_log_throttle_settings, director_user_move_throttled, NULL); user_kill_fail_throttle = log_throttle_init(&director_log_throttle_settings, director_user_kill_fail_throttled, NULL); } void directors_deinit(void) { log_throttle_deinit(&user_move_throttle); log_throttle_deinit(&user_kill_fail_throttle); } dovecot-2.2.33.2/src/director/auth-connection.h0000644000175000017500000000136113123174404016176 00000000000000#ifndef AUTH_CONNECTION_H #define AUTH_CONNECTION_H /* Called for each input line. This is also called with line=NULL if connection gets disconnected. */ typedef void auth_input_callback(const char *line, void *context); struct auth_connection *auth_connection_init(const char *path); void auth_connection_deinit(struct auth_connection **conn); void auth_connection_set_callback(struct auth_connection *conn, auth_input_callback *callback, void *context); /* Start connecting. Returns 0 if ok, -1 if connect failed. */ int auth_connection_connect(struct auth_connection *conn); /* Get auth connection's output stream. */ struct ostream *auth_connection_get_output(struct auth_connection *conn); void auth_connections_deinit(void); #endif dovecot-2.2.33.2/src/director/mail-host.c0000644000175000017500000003306313165463624015007 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "crc32.h" #include "md5.h" #include "user-directory.h" #include "mail-host.h" #define VHOST_MULTIPLIER 100 struct mail_host_list { ARRAY_TYPE(mail_tag) tags; ARRAY_TYPE(mail_host) hosts; user_free_hook_t *user_free_hook; unsigned int hosts_hash; unsigned int user_expire_secs; bool consistent_hashing; bool vhosts_unsorted; bool have_vhosts; }; static int mail_host_cmp(struct mail_host *const *h1, struct mail_host *const *h2) { return net_ip_cmp(&(*h1)->ip, &(*h2)->ip); } static int mail_vhost_cmp(const struct mail_vhost *h1, const struct mail_vhost *h2) { if (h1->hash < h2->hash) return -1; else if (h1->hash > h2->hash) return 1; /* hash collision. not ideal, but we'll need to keep the order consistent across directors so compare the IPs next. */ return net_ip_cmp(&h1->host->ip, &h2->host->ip); } static int mail_vhost_hash_cmp(const unsigned int *hash, const struct mail_vhost *vhost) { if (vhost->hash < *hash) return 1; else if (vhost->hash > *hash) return -1; else return 0; } static void mail_vhost_add(struct mail_tag *tag, struct mail_host *host) { struct mail_vhost *vhost; struct md5_context md5_ctx, md5_ctx2; unsigned char md5[MD5_RESULTLEN]; const char *ip_str; char num_str[MAX_INT_STRLEN]; unsigned int i, j; if (host->down || host->tag != tag) return; ip_str = net_ip2addr(&host->ip); md5_init(&md5_ctx); md5_update(&md5_ctx, ip_str, strlen(ip_str)); for (i = 0; i < host->vhost_count; i++) { md5_ctx2 = md5_ctx; i_snprintf(num_str, sizeof(num_str), "-%u", i); md5_update(&md5_ctx2, num_str, strlen(num_str)); md5_final(&md5_ctx2, md5); vhost = array_append_space(&tag->vhosts); vhost->host = host; for (j = 0; j < sizeof(vhost->hash); j++) vhost->hash = (vhost->hash << CHAR_BIT) | md5[j]; } } static void mail_tag_vhosts_sort_ring(struct mail_host_list *list, struct mail_tag *tag) { struct mail_host *const *hostp; /* rebuild vhosts */ array_clear(&tag->vhosts); array_foreach(&list->hosts, hostp) mail_vhost_add(tag, *hostp); array_sort(&tag->vhosts, mail_vhost_cmp); } static void mail_tag_vhosts_sort_direct(struct mail_host_list *list, struct mail_tag *tag) { struct mail_vhost *vhost; struct mail_host *const *hostp; unsigned int i; /* rebuild vhosts */ array_clear(&tag->vhosts); array_foreach(&list->hosts, hostp) { if ((*hostp)->down || (*hostp)->tag != tag) continue; for (i = 0; i < (*hostp)->vhost_count; i++) { vhost = array_append_space(&tag->vhosts); vhost->host = *hostp; } } } static void mail_hosts_sort(struct mail_host_list *list) { struct mail_host *const *hostp; struct mail_tag *const *tagp; uint32_t num; array_sort(&list->hosts, mail_host_cmp); list->have_vhosts = FALSE; array_foreach(&list->tags, tagp) { if (list->consistent_hashing) mail_tag_vhosts_sort_ring(list, *tagp); else mail_tag_vhosts_sort_direct(list, *tagp); if (array_count(&(*tagp)->vhosts) > 0) list->have_vhosts = TRUE; } list->vhosts_unsorted = FALSE; /* recalculate the hosts_hash */ list->hosts_hash = 0; array_foreach(&list->hosts, hostp) { num = ((*hostp)->down ? 1 : 0) ^ (*hostp)->vhost_count; list->hosts_hash = crc32_data_more(list->hosts_hash, &num, sizeof(num)); num = net_ip_hash(&(*hostp)->ip); list->hosts_hash = crc32_data_more(list->hosts_hash, &num, sizeof(num)); list->hosts_hash = crc32_str_more(list->hosts_hash, (*hostp)->tag->name); } } struct mail_tag * mail_tag_find(struct mail_host_list *list, const char *tag_name) { struct mail_tag *const *tagp; array_foreach(&list->tags, tagp) { if (strcmp((*tagp)->name, tag_name) == 0) return *tagp; } return NULL; } static struct mail_tag * mail_tag_get(struct mail_host_list *list, const char *tag_name) { struct mail_tag *tag; tag = mail_tag_find(list, tag_name); if (tag == NULL) { tag = i_new(struct mail_tag, 1); tag->name = i_strdup(tag_name); i_array_init(&tag->vhosts, 16*VHOST_MULTIPLIER); tag->users = user_directory_init(list->user_expire_secs, list->user_free_hook); array_append(&list->tags, &tag, 1); } return tag; } static void mail_tag_free(struct mail_tag *tag) { user_directory_deinit(&tag->users); array_free(&tag->vhosts); i_free(tag->name); i_free(tag); } struct mail_host * mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip, const char *tag_name) { struct mail_host *host; i_assert(tag_name != NULL); host = i_new(struct mail_host, 1); host->list = list; host->vhost_count = VHOST_MULTIPLIER; host->ip = *ip; host->tag = mail_tag_get(list, tag_name); array_append(&list->hosts, &host, 1); list->vhosts_unsorted = TRUE; return host; } struct mail_host * mail_host_add_hostname(struct mail_host_list *list, const char *hostname, const struct ip_addr *ip, const char *tag_name) { struct mail_host *host; host = mail_host_add_ip(list, ip, tag_name); if (hostname != NULL && hostname[0] != '\0') host->hostname = i_strdup(hostname); return host; } static int mail_host_add(struct mail_host_list *list, const char *hostname, const char *tag_name) { struct ip_addr *ips, ip; unsigned int i, ips_count; if (net_addr2ip(hostname, &ip) == 0) { (void)mail_host_add_ip(list, &ip, tag_name); return 0; } if (net_gethostbyname(hostname, &ips, &ips_count) < 0) { i_error("Unknown mail host: %s", hostname); return -1; } for (i = 0; i < ips_count; i++) (void)mail_host_add_hostname(list, hostname, &ips[i], tag_name); return 0; } static int mail_hosts_add_range(struct mail_host_list *list, struct ip_addr ip1, struct ip_addr ip2, const char *tag_name) { uint32_t *ip1_arr, *ip2_arr; uint32_t i1, i2; unsigned int i, j, max_bits, last_bits; if (ip1.family != ip2.family) { i_error("IP address family mismatch: %s vs %s", net_ip2addr(&ip1), net_ip2addr(&ip2)); return -1; } if (net_ip_cmp(&ip1, &ip2) > 0) { i_error("IP addresses reversed: %s-%s", net_ip2addr(&ip1), net_ip2addr(&ip2)); return -1; } if (IPADDR_IS_V4(&ip1)) { ip1_arr = &ip1.u.ip4.s_addr; ip2_arr = &ip2.u.ip4.s_addr; max_bits = 32; last_bits = 8; } else { #ifndef HAVE_IPV6 i_error("IPv6 not supported"); return -1; #else ip1_arr = (void *)&ip1.u.ip6; ip2_arr = (void *)&ip2.u.ip6; max_bits = 128; last_bits = 16; #endif } /* make sure initial bits match */ for (i = 0; i < (max_bits-last_bits)/32; i++) { if (ip1_arr[i] != ip2_arr[i]) { i_error("IP address range too large: %s-%s", net_ip2addr(&ip1), net_ip2addr(&ip2)); return -1; } } i1 = htonl(ip1_arr[i]); i2 = htonl(ip2_arr[i]); for (j = last_bits; j < 32; j++) { if ((i1 & (1U << j)) != (i2 & (1U << j))) { i_error("IP address range too large: %s-%s", net_ip2addr(&ip1), net_ip2addr(&ip2)); return -1; } } /* create hosts from the final bits */ do { ip1_arr[i] = ntohl(i1); (void)mail_host_add_ip(list, &ip1, tag_name); i1++; } while (ip1_arr[i] != ip2_arr[i]); return 0; } int mail_hosts_parse_and_add(struct mail_host_list *list, const char *hosts_string) { int ret = 0; T_BEGIN { const char *const *tmp, *p, *host1, *host2; struct ip_addr ip1, ip2; tmp = t_strsplit_spaces(hosts_string, " "); for (; *tmp != NULL; tmp++) { const char *tag, *value = *tmp; p = strchr(value, '@'); if (p == NULL) tag = ""; else { value = t_strdup_until(value, p++); tag = p; } p = strchr(value, '-'); if (p != NULL) { /* see if this is ip1-ip2 range */ host1 = t_strdup_until(value, p); host2 = p + 1; if (net_addr2ip(host1, &ip1) == 0 && net_addr2ip(host2, &ip2) == 0) { if (mail_hosts_add_range(list, ip1, ip2, tag) < 0) ret = -1; continue; } } if (mail_host_add(list, value, tag) < 0) ret = -1; } } T_END; if (array_count(&list->hosts) == 0) { if (ret < 0) i_error("No valid servers specified"); else i_error("Empty server list"); ret = -1; } return ret; } const char *mail_host_get_tag(const struct mail_host *host) { return host->tag->name; } void mail_host_set_tag(struct mail_host *host, const char *tag_name) { i_assert(tag_name != NULL); host->tag = mail_tag_get(host->list, tag_name); host->list->vhosts_unsorted = TRUE; } void mail_host_set_down(struct mail_host *host, bool down, time_t timestamp, const char *log_prefix) { if (host->down != down) { const char *updown = down ? "down" : "up"; i_info("%sHost %s changed %s " "(vhost_count=%u last_updown_change=%ld)", log_prefix, net_ip2addr(&host->ip), updown, host->vhost_count, (long)host->last_updown_change); host->down = down; host->last_updown_change = timestamp; host->list->vhosts_unsorted = TRUE; } } void mail_host_set_vhost_count(struct mail_host *host, unsigned int vhost_count, const char *log_prefix) { i_info("%sHost %s vhost count changed from %u to %u", log_prefix, net_ip2addr(&host->ip), host->vhost_count, vhost_count); host->vhost_count = vhost_count; host->list->vhosts_unsorted = TRUE; } static void mail_host_free(struct mail_host *host) { i_free(host->hostname); i_free(host); } void mail_host_remove(struct mail_host *host) { struct mail_host_list *list = host->list; struct mail_host *const *hosts; unsigned int i, count; hosts = array_get(&list->hosts, &count); for (i = 0; i < count; i++) { if (hosts[i] == host) { array_delete(&host->list->hosts, i, 1); break; } } mail_host_free(host); list->vhosts_unsorted = TRUE; } struct mail_host * mail_host_lookup(struct mail_host_list *list, const struct ip_addr *ip) { struct mail_host *const *hostp; if (list->vhosts_unsorted) mail_hosts_sort(list); array_foreach(&list->hosts, hostp) { if (net_ip_compare(&(*hostp)->ip, ip)) return *hostp; } return NULL; } static struct mail_host * mail_host_get_by_hash_ring(struct mail_tag *tag, unsigned int hash) { const struct mail_vhost *vhosts; unsigned int count, idx; vhosts = array_get(&tag->vhosts, &count); array_bsearch_insert_pos(&tag->vhosts, &hash, mail_vhost_hash_cmp, &idx); i_assert(idx <= count); if (idx == count) { if (count == 0) return NULL; idx = 0; } return vhosts[idx % count].host; } static struct mail_host * mail_host_get_by_hash_direct(struct mail_tag *tag, unsigned int hash) { const struct mail_vhost *vhosts; unsigned int count; vhosts = array_get(&tag->vhosts, &count); if (count == 0) return NULL; return vhosts[hash % count].host; } struct mail_host * mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash, const char *tag_name) { struct mail_tag *tag; if (list->vhosts_unsorted) mail_hosts_sort(list); tag = mail_tag_find(list, tag_name); if (tag == NULL) return NULL; if (list->consistent_hashing) return mail_host_get_by_hash_ring(tag, hash); else return mail_host_get_by_hash_direct(tag, hash); } void mail_hosts_set_synced(struct mail_host_list *list) { struct mail_host *const *hostp; array_foreach(&list->hosts, hostp) (*hostp)->desynced = FALSE; } unsigned int mail_hosts_hash(struct mail_host_list *list) { if (list->vhosts_unsorted) mail_hosts_sort(list); /* don't retun 0 as hash, since we're using it as "doesn't exist" in some places. */ return list->hosts_hash == 0 ? 1 : list->hosts_hash; } bool mail_hosts_have_usable(struct mail_host_list *list) { if (list->vhosts_unsorted) mail_hosts_sort(list); return list->have_vhosts; } const ARRAY_TYPE(mail_host) *mail_hosts_get(struct mail_host_list *list) { if (list->vhosts_unsorted) mail_hosts_sort(list); return &list->hosts; } bool mail_hosts_have_tags(struct mail_host_list *list) { struct mail_tag *const *tagp; if (list->vhosts_unsorted) mail_hosts_sort(list); array_foreach(&list->tags, tagp) { if ((*tagp)->name[0] != '\0' && array_count(&(*tagp)->vhosts) > 0) return TRUE; } return FALSE; } const ARRAY_TYPE(mail_tag) *mail_hosts_get_tags(struct mail_host_list *list) { return &list->tags; } struct mail_host_list * mail_hosts_init(unsigned int user_expire_secs, bool consistent_hashing, user_free_hook_t *user_free_hook) { struct mail_host_list *list; list = i_new(struct mail_host_list, 1); list->user_expire_secs = user_expire_secs; list->consistent_hashing = consistent_hashing; list->user_free_hook = user_free_hook; i_array_init(&list->hosts, 16); i_array_init(&list->tags, 4); return list; } void mail_hosts_deinit(struct mail_host_list **_list) { struct mail_host_list *list = *_list; struct mail_host *const *hostp; struct mail_tag *const *tagp; *_list = NULL; array_foreach(&list->tags, tagp) mail_tag_free(*tagp); array_foreach(&list->hosts, hostp) mail_host_free(*hostp); array_free(&list->hosts); array_free(&list->tags); i_free(list); } static struct mail_host * mail_host_dup(struct mail_host_list *dest_list, const struct mail_host *src) { struct mail_host *dest; dest = i_new(struct mail_host, 1); *dest = *src; dest->tag = mail_tag_get(dest_list, src->tag->name); dest->hostname = i_strdup(src->hostname); return dest; } struct mail_host_list *mail_hosts_dup(const struct mail_host_list *src) { struct mail_host_list *dest; struct mail_host *const *hostp, *dest_host; dest = mail_hosts_init(src->user_expire_secs, src->consistent_hashing, src->user_free_hook); array_foreach(&src->hosts, hostp) { dest_host = mail_host_dup(dest, *hostp); array_append(&dest->hosts, &dest_host, 1); } mail_hosts_sort(dest); return dest; } void mail_hosts_sort_users(struct mail_host_list *list) { struct mail_tag *const *tagp; array_foreach(&list->tags, tagp) user_directory_sort((*tagp)->users); } dovecot-2.2.33.2/src/director/notify-connection.c0000644000175000017500000000421113165463624016550 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "master-service.h" #include "director.h" #include "mail-host.h" #include "notify-connection.h" #include struct notify_connection { int fd; struct io *io; struct istream *input; struct director *dir; }; static void notify_update_user(struct director *dir, struct mail_tag *tag, const char *username, unsigned int username_hash) { struct user *user; int diff; user = user_directory_lookup(tag->users, username_hash); if (user == NULL) return; diff = ioloop_time - user->timestamp; if (diff >= (int)dir->set->director_user_expire) { i_warning("notify: User %s refreshed too late (%d secs)", username, diff); } user_directory_refresh(tag->users, user); director_update_user(dir, dir->self_host, user); } static void notify_connection_input(struct notify_connection *conn) { struct mail_tag *const *tagp; const char *line; unsigned int hash; while ((line = i_stream_read_next_line(conn->input)) != NULL) { hash = director_get_username_hash(conn->dir, line); array_foreach(mail_hosts_get_tags(conn->dir->mail_hosts), tagp) notify_update_user(conn->dir, *tagp, line, hash); } if (conn->input->eof) { i_error("notify: read() unexpectedly returned EOF"); notify_connection_deinit(&conn); } else if (conn->input->stream_errno != 0) { i_error("notify: read() failed: %s", i_stream_get_error(conn->input)); notify_connection_deinit(&conn); } } struct notify_connection * notify_connection_init(struct director *dir, int fd) { struct notify_connection *conn; conn = i_new(struct notify_connection, 1); conn->fd = fd; conn->dir = dir; conn->input = i_stream_create_fd(conn->fd, 1024, FALSE); conn->io = io_add(conn->fd, IO_READ, notify_connection_input, conn); return conn; } void notify_connection_deinit(struct notify_connection **_conn) { struct notify_connection *conn = *_conn; *_conn = NULL; io_remove(&conn->io); i_stream_unref(&conn->input); if (close(conn->fd) < 0) i_error("close(notify connection) failed: %m"); i_free(conn); } dovecot-2.2.33.2/src/director/director-test.c0000644000175000017500000003717213165463624015707 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ /* This program accepts incoming unauthenticated IMAP connections from port 14300. If the same user is connecting to multiple different local IPs, it logs an error (i.e. director is not working right then). This program also accepts incoming director connections on port 9091 and forwards them to local_ip:9090. To make this work properly, director executable must be given -t 9091 parameter. The idea is that this test tool hooks between all director connections and can then add delays or break the connections. Finally, this program connects to director-admin socket where it adds and removes mail hosts. */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "write-full.h" #include "hash.h" #include "llist.h" #include "strescape.h" #include "imap-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "director-settings.h" #include #define IMAP_PORT 14300 #define DIRECTOR_IN_PORT 9091 #define DIRECTOR_OUT_PORT 9090 #define USER_TIMEOUT_MSECS (1000*10) /* FIXME: this should be based on director_user_expire */ #define ADMIN_RANDOM_TIMEOUT_MSECS 500 #define DIRECTOR_CONN_MAX_DELAY_MSECS 100 #define DIRECTOR_DISCONNECT_TIMEOUT_SECS 10 struct host { int refcount; struct ip_addr ip; unsigned int vhost_count; }; struct user { char *username; struct host *host; time_t last_seen; unsigned int connections; struct timeout *to; }; struct imap_client { struct imap_client *prev, *next; int fd; struct io *io; struct istream *input; struct ostream *output; struct imap_parser *parser; struct user *user; char *username; }; struct director_connection { struct director_connection *prev, *next; int in_fd, out_fd; struct io *in_io, *out_io; struct istream *in_input, *out_input; struct ostream *in_output, *out_output; struct timeout *to_delay; }; struct admin_connection { char *path; int fd; struct io *io; struct istream *input; struct timeout *to_random; bool pending_command; }; static struct imap_client *imap_clients; static struct director_connection *director_connections; static HASH_TABLE(char *, struct user *) users; static HASH_TABLE(struct ip_addr *, struct host *) hosts; static ARRAY(struct host *) hosts_array; static struct admin_connection *admin; static struct timeout *to_disconnect; static void imap_client_destroy(struct imap_client **client); static void director_connection_destroy(struct director_connection **conn); static void director_connection_timeout(struct director_connection *conn); static void host_unref(struct host **_host) { struct host *host = *_host; *_host = NULL; i_assert(host->refcount > 0); if (--host->refcount > 0) return; i_free(host); } static void client_username_check(struct imap_client *client) { struct user *user; struct host *host; struct ip_addr local_ip; if (net_getsockname(client->fd, &local_ip, NULL) < 0) i_fatal("net_getsockname() failed: %m"); host = hash_table_lookup(hosts, &local_ip); if (host == NULL) { i_error("User logging into unknown host %s", net_ip2addr(&local_ip)); host = i_new(struct host, 1); host->refcount = 1; host->ip = local_ip; host->vhost_count = 100; hash_table_insert(hosts, &host->ip, host); array_append(&hosts_array, &host, 1); } user = hash_table_lookup(users, client->username); if (user == NULL) { user = i_new(struct user, 1); user->username = i_strdup(client->username); hash_table_insert(users, user->username, user); } else if (user->host != host) { i_error("user %s: old connection from %s, new from %s. " "%u old connections, last was %u secs ago", user->username, net_ip2addr(&user->host->ip), net_ip2addr(&host->ip), user->connections, (unsigned int)(ioloop_time - user->last_seen)); host_unref(&user->host); } client->user = user; user->host = host; user->connections++; user->last_seen = ioloop_time; user->host->refcount++; if (user->to != NULL) timeout_remove(&user->to); } static void user_free(struct user *user) { host_unref(&user->host); if (user->to != NULL) timeout_remove(&user->to); hash_table_remove(users, user->username); i_free(user->username); i_free(user); } static int imap_client_parse_input(struct imap_client *client) { const char *tag, *cmd, *str; const struct imap_arg *args; int ret; ret = imap_parser_read_args(client->parser, 0, 0, &args); if (ret < 0) { if (ret == -2) return 0; return -1; } if (!imap_arg_get_atom(args, &tag)) return -1; args++; if (!imap_arg_get_atom(args, &cmd)) return -1; args++; if (strcasecmp(cmd, "login") == 0) { if (client->username != NULL) return -1; if (!imap_arg_get_astring(args, &str)) return -1; o_stream_nsend_str(client->output, t_strconcat(tag, " OK Logged in.\r\n", NULL)); client->username = i_strdup(str); client_username_check(client); } else if (strcasecmp(cmd, "logout") == 0) { o_stream_nsend_str(client->output, t_strconcat( "* BYE Out.\r\n", tag, " OK Logged out.\r\n", NULL)); imap_client_destroy(&client); return 0; } else if (strcasecmp(cmd, "capability") == 0) { o_stream_nsend_str(client->output, t_strconcat("* CAPABILITY IMAP4rev1\r\n", tag, " OK Done.\r\n", NULL)); } else { o_stream_nsend_str(client->output, t_strconcat(tag, " BAD Not supported.\r\n", NULL)); } (void)i_stream_read_next_line(client->input); /* eat away LF */ imap_parser_reset(client->parser); return 1; } static void imap_client_input(struct imap_client *client) { int ret; switch (i_stream_read(client->input)) { case -2: i_error("imap: Too much input"); imap_client_destroy(&client); return; case -1: imap_client_destroy(&client); return; default: break; } while ((ret = imap_client_parse_input(client)) > 0) ; if (ret < 0) { i_error("imap: Invalid input"); imap_client_destroy(&client); } } static void imap_client_create(int fd) { struct imap_client *client; client = i_new(struct imap_client, 1); client->fd = fd; client->input = i_stream_create_fd(fd, 4096, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); client->io = io_add(fd, IO_READ, imap_client_input, client); client->parser = imap_parser_create(client->input, client->output, 4096); o_stream_nsend_str(client->output, "* OK [CAPABILITY IMAP4rev1] director-test ready.\r\n"); DLLIST_PREPEND(&imap_clients, client); } static void imap_client_destroy(struct imap_client **_client) { struct imap_client *client = *_client; struct user *user = client->user; *_client = NULL; if (user != NULL) { i_assert(user->connections > 0); if (--user->connections == 0) { i_assert(user->to == NULL); user->to = timeout_add(USER_TIMEOUT_MSECS, user_free, user); } user->last_seen = ioloop_time; } DLLIST_REMOVE(&imap_clients, client); imap_parser_unref(&client->parser); io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); net_disconnect(client->fd); i_free(client->username); i_free(client); master_service_client_connection_destroyed(master_service); } static void director_connection_input(struct director_connection *conn, struct istream *input, struct ostream *output) { const unsigned char *data; size_t size; if (i_stream_read_data(input, &data, &size, 0) == -1) { director_connection_destroy(&conn); return; } o_stream_nsend(output, data, size); i_stream_skip(input, size); if (rand() % 3 == 0 && conn->to_delay == NULL) { conn->to_delay = timeout_add(rand() % DIRECTOR_CONN_MAX_DELAY_MSECS, director_connection_timeout, conn); io_remove(&conn->in_io); io_remove(&conn->out_io); } } static void director_connection_in_input(struct director_connection *conn) { director_connection_input(conn, conn->in_input, conn->out_output); } static void director_connection_out_input(struct director_connection *conn) { director_connection_input(conn, conn->out_input, conn->in_output); } static void director_connection_timeout(struct director_connection *conn) { timeout_remove(&conn->to_delay); conn->in_io = io_add(conn->in_fd, IO_READ, director_connection_in_input, conn); conn->out_io = io_add(conn->out_fd, IO_READ, director_connection_out_input, conn); } static void director_connection_create(int in_fd, const struct ip_addr *local_ip, const struct ip_addr *remote_ip) { struct director_connection *conn; int out_fd; out_fd = net_connect_ip(local_ip, DIRECTOR_OUT_PORT, remote_ip); if (out_fd == -1) { i_close_fd(&in_fd); return; } conn = i_new(struct director_connection, 1); conn->in_fd = in_fd; conn->in_input = i_stream_create_fd(conn->in_fd, (size_t)-1, FALSE); conn->in_output = o_stream_create_fd(conn->in_fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->in_output, TRUE); conn->in_io = io_add(conn->in_fd, IO_READ, director_connection_in_input, conn); conn->out_fd = out_fd; conn->out_input = i_stream_create_fd(conn->out_fd, (size_t)-1, FALSE); conn->out_output = o_stream_create_fd(conn->out_fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->out_output, TRUE); conn->out_io = io_add(conn->out_fd, IO_READ, director_connection_out_input, conn); DLLIST_PREPEND(&director_connections, conn); } static void director_connection_destroy(struct director_connection **_conn) { struct director_connection *conn = *_conn; DLLIST_REMOVE(&director_connections, conn); if (conn->to_delay != NULL) timeout_remove(&conn->to_delay); if (conn->in_io != NULL) io_remove(&conn->in_io); i_stream_unref(&conn->in_input); o_stream_unref(&conn->in_output); net_disconnect(conn->in_fd); if (conn->out_io != NULL) io_remove(&conn->out_io); i_stream_unref(&conn->out_input); o_stream_unref(&conn->out_output); net_disconnect(conn->out_fd); i_free(conn); } static void client_connected(struct master_service_connection *conn) { struct ip_addr local_ip, remote_ip; in_port_t local_port; if (net_getsockname(conn->fd, &local_ip, &local_port) < 0) i_fatal("net_getsockname() failed: %m"); if (net_getpeername(conn->fd, &remote_ip, NULL) < 0) i_fatal("net_getsockname() failed: %m"); if (local_port == IMAP_PORT) imap_client_create(conn->fd); else if (local_port == DIRECTOR_IN_PORT) director_connection_create(conn->fd, &local_ip, &remote_ip); else { i_error("Connection to unknown port %u", local_port); return; } master_service_client_connection_accept(conn); } static void admin_send(struct admin_connection *conn, const char *data) { if (write_full(i_stream_get_fd(conn->input), data, strlen(data)) < 0) i_fatal("write(%s) failed: %m", conn->path); } static void admin_input(struct admin_connection *conn) { const char *line; while ((line = i_stream_read_next_line(conn->input)) != NULL) { if (strcmp(line, "OK") != 0) i_error("director-doveadm: Unexpected input: %s", line); conn->pending_command = FALSE; } if (conn->input->stream_errno != 0 || conn->input->eof) i_fatal("director-doveadm: Connection lost"); } static void admin_random_action(struct admin_connection *conn) { struct host *const *hosts; unsigned int i, count; if (conn->pending_command) return; hosts = array_get(&hosts_array, &count); i = rand() % count; hosts[i]->vhost_count = (rand() % 20) * 10; admin_send(conn, t_strdup_printf("HOST-SET\t%s\t%u\n", net_ip2addr(&hosts[i]->ip), hosts[i]->vhost_count)); conn->pending_command = TRUE; } static struct admin_connection *admin_connect(const char *path) { #define DIRECTOR_ADMIN_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" struct admin_connection *conn; const char *line; conn = i_new(struct admin_connection, 1); conn->path = i_strdup(path); conn->fd = net_connect_unix(path); if (conn->fd == -1) i_fatal("net_connect_unix(%s) failed: %m", path); conn->io = io_add(conn->fd, IO_READ, admin_input, conn); conn->to_random = timeout_add_short(ADMIN_RANDOM_TIMEOUT_MSECS, admin_random_action, conn); net_set_nonblock(conn->fd, FALSE); conn->input = i_stream_create_fd(conn->fd, (size_t)-1, FALSE); admin_send(conn, DIRECTOR_ADMIN_HANDSHAKE); line = i_stream_read_next_line(conn->input); if (line == NULL) i_fatal("%s disconnected", conn->path); if (!version_string_verify(line, "director-doveadm", 1)) { i_fatal("%s not a compatible director-doveadm socket", conn->path); } net_set_nonblock(conn->fd, TRUE); return conn; } static void admin_disconnect(struct admin_connection **_conn) { struct admin_connection *conn = *_conn; *_conn = NULL; if (conn->to_random != NULL) timeout_remove(&conn->to_random); i_stream_destroy(&conn->input); io_remove(&conn->io); net_disconnect(conn->fd); i_free(conn->path); i_free(conn); } static void admin_read_hosts(struct admin_connection *conn) { const char *line; net_set_nonblock(admin->fd, FALSE); while ((line = i_stream_read_next_line(conn->input)) != NULL) { if (*line == '\0') break; /* ip vhost-count user-count */ T_BEGIN { const char *const *args = t_strsplit_tabescaped(line); struct host *host; host = i_new(struct host, 1); host->refcount = 1; if (net_addr2ip(args[0], &host->ip) < 0 || str_to_uint(args[1], &host->vhost_count) < 0) i_fatal("host list broken"); hash_table_insert(hosts, &host->ip, host); array_append(&hosts_array, &host, 1); } T_END; } if (line == NULL) i_fatal("Couldn't read hosts list"); net_set_nonblock(admin->fd, TRUE); } static void ATTR_NULL(1) director_connection_disconnect_timeout(void *context ATTR_UNUSED) { struct director_connection *conn; unsigned int i, count = 0; for (conn = director_connections; conn != NULL; conn = conn->next) count++; if (count != 0) { i = 0; count = rand() % count; for (conn = director_connections; i < count; conn = conn->next) { i_assert(conn != NULL); i++; } i_assert(conn != NULL); director_connection_destroy(&conn); } } static void main_init(const char *admin_path) { hash_table_create(&users, default_pool, 0, str_hash, strcmp); hash_table_create(&hosts, default_pool, 0, net_ip_hash, net_ip_cmp); i_array_init(&hosts_array, 256); admin = admin_connect(admin_path); admin_send(admin, "HOST-LIST\n"); admin_read_hosts(admin); to_disconnect = timeout_add(1000*(5 + rand()%DIRECTOR_DISCONNECT_TIMEOUT_SECS), director_connection_disconnect_timeout, (void *)NULL); } static void main_deinit(void) { struct hash_iterate_context *iter; char *username; struct ip_addr *ip; struct user *user; struct host *host; while (imap_clients != NULL) { struct imap_client *client = imap_clients; imap_client_destroy(&client); } timeout_remove(&to_disconnect); while (director_connections != NULL) { struct director_connection *conn = director_connections; director_connection_destroy(&conn); } iter = hash_table_iterate_init(users); while (hash_table_iterate(iter, users, &username, &user)) user_free(user); hash_table_iterate_deinit(&iter); hash_table_destroy(&users); iter = hash_table_iterate_init(hosts); while (hash_table_iterate(iter, hosts, &ip, &host)) host_unref(&host); hash_table_iterate_deinit(&iter); hash_table_destroy(&hosts); array_free(&hosts_array); admin_disconnect(&admin); } int main(int argc, char *argv[]) { const char *admin_path; master_service = master_service_init("director-test", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; admin_path = argv[optind]; if (admin_path == NULL) i_fatal("director-doveadm socket path missing"); master_service_init_log(master_service, "director-test: "); main_init(admin_path); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/director/director-host.c0000644000175000017500000001034713123174404015665 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "director.h" #include "director-host.h" static int director_host_cmp(const struct director_host *b1, const struct director_host *b2) { int ret; ret = net_ip_cmp(&b1->ip, &b2->ip); if (ret != 0) return ret; return (int)b1->port - (int)b2->port; } static int director_host_cmp_p(struct director_host *const *host1, struct director_host *const *host2) { return director_host_cmp(*host1, *host2); } struct director_host * director_host_add(struct director *dir, const struct ip_addr *ip, in_port_t port) { struct director_host *host; i_assert(director_host_lookup(dir, ip, port) == NULL); host = i_new(struct director_host, 1); host->dir = dir; host->refcount = 1; host->ip = *ip; host->port = port; host->name = i_strdup_printf("%s:%u", net_ip2addr(ip), port); array_append(&dir->dir_hosts, &host, 1); /* there are few enough directors that sorting after each addition should be fine */ array_sort(&dir->dir_hosts, director_host_cmp_p); return host; } void director_host_free(struct director_host **_host) { struct director_host *host = *_host; i_assert(host->refcount == 1); *_host = NULL; director_host_unref(host); } void director_host_ref(struct director_host *host) { i_assert(host->refcount > 0); host->refcount++; } void director_host_unref(struct director_host *host) { struct director_host *const *hosts; unsigned int i, count; i_assert(host->refcount > 0); if (--host->refcount > 0) return; hosts = array_get(&host->dir->dir_hosts, &count); for (i = 0; i < count; i++) { if (hosts[i] == host) { array_delete(&host->dir->dir_hosts, i, 1); break; } } i_free(host->name); i_free(host); } void director_host_restarted(struct director_host *host) { host->last_seq = 0; host->last_sync_seq = 0; host->last_sync_seq_counter = 0; host->last_sync_timestamp = 0; } struct director_host * director_host_get(struct director *dir, const struct ip_addr *ip, in_port_t port) { struct director_host *host; host = director_host_lookup(dir, ip, port); if (host == NULL) host = director_host_add(dir, ip, port); return host; } struct director_host * director_host_lookup(struct director *dir, const struct ip_addr *ip, in_port_t port) { struct director_host *const *hostp; array_foreach(&dir->dir_hosts, hostp) { if (net_ip_compare(&(*hostp)->ip, ip) && (*hostp)->port == port) return *hostp; } return NULL; } struct director_host * director_host_lookup_ip(struct director *dir, const struct ip_addr *ip) { struct director_host *const *hostp; array_foreach(&dir->dir_hosts, hostp) { if (net_ip_compare(&(*hostp)->ip, ip)) return *hostp; } return NULL; } int director_host_cmp_to_self(const struct director_host *b1, const struct director_host *b2, const struct director_host *self) { int ret; if ((ret = director_host_cmp(b1, b2)) >= 0) return ret == 0 ? 0 : -director_host_cmp_to_self(b2, b1, self); /* order -> return: self, b1, b2 -> b2 b1, self, b2 -> b1 b1, b2, self -> b2 */ if (director_host_cmp(self, b1) < 0) return 1; /* self, b1, b2 */ if (director_host_cmp(self, b2) < 0) return -1; /* b1, self, b2 */ return 1; /* b1, b2, self */ } static void director_host_add_string(struct director *dir, const char *host) { struct ip_addr *ips; in_port_t port; unsigned int i, ips_count; if (net_str2hostport(host, dir->self_port, &host, &port) < 0) i_fatal("Invalid director host:port in '%s'", host); if (net_gethostbyname(host, &ips, &ips_count) < 0) i_fatal("Unknown director host: %s", host); for (i = 0; i < ips_count; i++) { if (director_host_lookup(dir, &ips[i], port) == NULL) (void)director_host_add(dir, &ips[i], port); } } void director_host_add_from_string(struct director *dir, const char *hosts) { T_BEGIN { const char *const *tmp; tmp = t_strsplit_spaces(hosts, " "); for (; *tmp != NULL; tmp++) director_host_add_string(dir, *tmp); } T_END; if (array_count(&dir->dir_hosts) == 0) { /* standalone director */ struct ip_addr ip; if (net_addr2ip("127.0.0.1", &ip) < 0) i_unreached(); dir->self_host = director_host_add(dir, &ip, 0); dir->self_host->self = TRUE; } } dovecot-2.2.33.2/src/director/main.c0000644000175000017500000002443213165463624014036 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "restrict-access.h" #include "process-title.h" #include "master-interface.h" #include "master-service.h" #include "master-service-settings.h" #include "auth-connection.h" #include "doveadm-connection.h" #include "login-connection.h" #include "notify-connection.h" #include "user-directory.h" #include "director.h" #include "director-host.h" #include "director-connection.h" #include "director-request.h" #include "mail-host.h" #include #include #define AUTH_SOCKET_PATH "auth-login" #define AUTH_USERDB_SOCKET_PATH "auth-userdb" enum director_socket_type { DIRECTOR_SOCKET_TYPE_UNKNOWN = 0, DIRECTOR_SOCKET_TYPE_AUTH, DIRECTOR_SOCKET_TYPE_USERDB, DIRECTOR_SOCKET_TYPE_AUTHREPLY, DIRECTOR_SOCKET_TYPE_RING, DIRECTOR_SOCKET_TYPE_DOVEADM }; static struct director *director; static struct notify_connection *notify_conn; static struct timeout *to_proctitle_refresh; static ARRAY(enum director_socket_type) listener_socket_types; static unsigned int director_total_users_count(void) { struct mail_tag *const *tagp; unsigned int count = 0; array_foreach(mail_hosts_get_tags(director->mail_hosts), tagp) count += user_directory_count((*tagp)->users); return count; } static void director_refresh_proctitle_timeout(void *context ATTR_UNUSED) { static uint64_t prev_requests = 0, prev_input = 0, prev_output; string_t *str; str = t_str_new(64); str_printfa(str, "[%u users", director_total_users_count()); if (director->users_moving_count > 0) str_printfa(str, ", %u moving", director->users_moving_count); str_printfa(str, ", %lu req/s", (unsigned long)(director->num_requests - prev_requests)); str_printfa(str, ", %llu+%llu kB/s", (unsigned long long)(director->ring_traffic_input - prev_input)/1024, (unsigned long long)(director->ring_traffic_output - prev_output)/1024); str_append_c(str, ']'); prev_requests = director->num_requests; prev_input = director->ring_traffic_input; prev_output = director->ring_traffic_output; process_title_set(str_c(str)); } static enum director_socket_type director_socket_type_get_from_name(const char *path) { const char *name, *suffix; name = strrchr(path, '/'); if (name == NULL) name = path; else name++; suffix = strrchr(name, '-'); if (suffix == NULL) suffix = name; else suffix++; if (strcmp(suffix, "auth") == 0) return DIRECTOR_SOCKET_TYPE_AUTH; else if (strcmp(suffix, "userdb") == 0) return DIRECTOR_SOCKET_TYPE_USERDB; else if (strcmp(suffix, "authreply") == 0) return DIRECTOR_SOCKET_TYPE_AUTHREPLY; else if (strcmp(suffix, "ring") == 0) return DIRECTOR_SOCKET_TYPE_RING; else if (strcmp(suffix, "admin") == 0 || strcmp(suffix, "doveadm") == 0) return DIRECTOR_SOCKET_TYPE_DOVEADM; else return DIRECTOR_SOCKET_TYPE_UNKNOWN; } static enum director_socket_type listener_get_socket_type_fallback(const struct director_settings *set, int listen_fd) { in_port_t local_port; if (net_getsockname(listen_fd, NULL, &local_port) == 0 && local_port != 0) { /* TCP/IP connection */ if (local_port == set->director_doveadm_port) return DIRECTOR_SOCKET_TYPE_DOVEADM; else return DIRECTOR_SOCKET_TYPE_RING; } return DIRECTOR_SOCKET_TYPE_AUTH; } static void listener_sockets_init(const struct director_settings *set, struct ip_addr *listen_ip_r, in_port_t *listen_port_r) { const char *name; unsigned int i, socket_count; struct ip_addr ip; in_port_t port; enum director_socket_type type; *listen_port_r = 0; i_array_init(&listener_socket_types, 8); socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { int listen_fd = MASTER_LISTEN_FD_FIRST + i; name = master_service_get_socket_name(master_service, listen_fd); type = director_socket_type_get_from_name(name); if (type == DIRECTOR_SOCKET_TYPE_UNKNOWN) { /* mainly for backwards compatibility */ type = listener_get_socket_type_fallback(set, listen_fd); } if (type == DIRECTOR_SOCKET_TYPE_RING && *listen_port_r == 0 && net_getsockname(listen_fd, &ip, &port) == 0 && port > 0) { *listen_ip_r = ip; *listen_port_r = port; } array_idx_set(&listener_socket_types, listen_fd, &type); } } static int director_client_connected(int fd, const struct ip_addr *ip) { struct director_host *host; host = director_host_lookup_ip(director, ip); if (host == NULL || host->removed) { i_warning("Connection from %s: Server not listed in " "director_servers, dropping", net_ip2addr(ip)); return -1; } (void)director_connection_init_in(director, fd, ip); return 0; } static void client_connected(struct master_service_connection *conn) { struct auth_connection *auth; const char *socket_path; const enum director_socket_type *typep; bool userdb; if (conn->fifo) { if (notify_conn != NULL) { i_error("Received another proxy-notify connection"); return; } master_service_client_connection_accept(conn); notify_conn = notify_connection_init(director, conn->fd); return; } typep = array_idx(&listener_socket_types, conn->listen_fd); switch (*typep) { case DIRECTOR_SOCKET_TYPE_UNKNOWN: i_unreached(); case DIRECTOR_SOCKET_TYPE_AUTH: case DIRECTOR_SOCKET_TYPE_USERDB: /* a) userdb connection, probably for lmtp proxy b) login connection Both of them are handled exactly the same, except for which auth socket they connect to. */ userdb = *typep == DIRECTOR_SOCKET_TYPE_USERDB; socket_path = userdb ? AUTH_USERDB_SOCKET_PATH : AUTH_SOCKET_PATH; auth = auth_connection_init(socket_path); if (auth_connection_connect(auth) < 0) { auth_connection_deinit(&auth); break; } master_service_client_connection_accept(conn); (void)login_connection_init(director, conn->fd, auth, userdb ? LOGIN_CONNECTION_TYPE_USERDB : LOGIN_CONNECTION_TYPE_AUTH); break; case DIRECTOR_SOCKET_TYPE_AUTHREPLY: master_service_client_connection_accept(conn); (void)login_connection_init(director, conn->fd, NULL, LOGIN_CONNECTION_TYPE_AUTHREPLY); break; case DIRECTOR_SOCKET_TYPE_RING: if (director_client_connected(conn->fd, &conn->remote_ip) == 0) master_service_client_connection_accept(conn); break; case DIRECTOR_SOCKET_TYPE_DOVEADM: master_service_client_connection_accept(conn); (void)doveadm_connection_init(director, conn->fd); break; } } static void director_state_changed(struct director *dir) { struct director_request *const *requestp; ARRAY(struct director_request *) new_requests; bool ret; if (!dir->ring_synced) return; /* if there are any pending client requests, finish them now */ t_array_init(&new_requests, 8); array_foreach(&dir->pending_requests, requestp) { ret = director_request_continue(*requestp); if (!ret) { /* a) request for a user being killed b) user is weak */ array_append(&new_requests, requestp, 1); } } array_clear(&dir->pending_requests); array_append_array(&dir->pending_requests, &new_requests); if (dir->to_request != NULL && array_count(&new_requests) == 0) timeout_remove(&dir->to_request); doveadm_connections_ring_synced(); } static void main_preinit(void) { const struct director_settings *set; struct ip_addr listen_ip; in_port_t listen_port; /* make sure we die with master even with shutdown_clients=no. otherwise there will be two director processes and everything is broken. it's only the login processes that need to stay alive. */ master_service_set_die_with_master(master_service, TRUE); if (master_service_settings_get(master_service)->verbose_proctitle) { to_proctitle_refresh = timeout_add(1000, director_refresh_proctitle_timeout, (void *)NULL); } set = master_service_settings_get_others(master_service)[0]; listener_sockets_init(set, &listen_ip, &listen_port); if (listen_port == 0 && *set->director_servers != '\0') { i_fatal("No inet_listeners defined for director service " "(for standalone keep director_servers empty)"); } directors_init(); director = director_init(set, &listen_ip, listen_port, director_state_changed); director_host_add_from_string(director, set->director_servers); director_find_self(director); if (mail_hosts_parse_and_add(director->mail_hosts, set->director_mail_servers) < 0) i_fatal("Invalid value for director_mail_servers setting"); director->orig_config_hosts = mail_hosts_dup(director->mail_hosts); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } static void main_deinit(void) { if (to_proctitle_refresh != NULL) timeout_remove(&to_proctitle_refresh); if (notify_conn != NULL) notify_connection_deinit(¬ify_conn); /* deinit doveadm connections before director, so it can clean up its pending work, such as abort user moves. */ doveadm_connections_deinit(); director_deinit(&director); directors_deinit(); login_connections_deinit(); auth_connections_deinit(); array_free(&listener_socket_types); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &director_setting_parser_info, NULL }; const enum master_service_flags service_flags = MASTER_SERVICE_FLAG_NO_IDLE_DIE; in_port_t test_port = 0; const char *error; bool debug = FALSE; int c; master_service = master_service_init("director", service_flags, &argc, &argv, "Dt:"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': debug = TRUE; break; case 't': if (net_str2port(optarg, &test_port) < 0) i_fatal("-t: Not a port number: %s", optarg); break; default: return FATAL_DEFAULT; } } if (master_service_settings_read_simple(master_service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "director: "); main_preinit(); director->test_port = test_port; director_debug = debug; director_connect(director, "Initial connection"); if (director->test_port != 0) { /* we're testing, possibly writing to same log file. make it clear which director we are. */ master_service_init_log(master_service, t_strdup_printf("director(%s): ", net_ip2addr(&director->self_ip))); } master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/director/director-settings.h0000644000175000017500000000077313165463624016572 00000000000000#ifndef DIRECTOR_SETTINGS_H #define DIRECTOR_SETTINGS_H #include "net.h" struct director_settings { const char *master_user_separator; const char *director_servers; const char *director_mail_servers; const char *director_username_hash; const char *director_flush_socket; unsigned int director_user_expire; unsigned int director_user_kick_delay; in_port_t director_doveadm_port; bool director_consistent_hashing; }; extern const struct setting_parser_info director_setting_parser_info; #endif dovecot-2.2.33.2/src/director/user-directory.c0000644000175000017500000002265513165463543016077 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "hash.h" #include "llist.h" #include "mail-host.h" /* n% of timeout_secs */ #define USER_NEAR_EXPIRING_PERCENTAGE 10 /* but min/max. of this many secs */ #define USER_NEAR_EXPIRING_MIN 3 #define USER_NEAR_EXPIRING_MAX 30 /* This shouldn't matter what it is exactly, just try it sometimes later. */ #define USER_BEING_KILLED_EXPIRE_RETRY_SECS 60 struct user_directory_iter { struct user_directory *dir; struct user *pos; }; struct user_directory { /* unsigned int username_hash => user */ HASH_TABLE(void *, struct user *) hash; /* sorted by time */ struct user *head, *tail; struct user *prev_insert_pos; ARRAY(struct user_directory_iter *) iters; user_free_hook_t *user_free_hook; unsigned int timeout_secs; /* If user's expire time is less than this many seconds away, don't assume that other directors haven't yet expired it */ unsigned int user_near_expiring_secs; struct timeout *to_expire; }; static void user_move_iters(struct user_directory *dir, struct user *user) { struct user_directory_iter *const *iterp; array_foreach(&dir->iters, iterp) { if ((*iterp)->pos == user) (*iterp)->pos = user->next; } if (dir->prev_insert_pos == user) dir->prev_insert_pos = user->next; } static void user_free(struct user_directory *dir, struct user *user) { i_assert(user->host->user_count > 0); user->host->user_count--; if (dir->user_free_hook != NULL) dir->user_free_hook(user); user_move_iters(dir, user); hash_table_remove(dir->hash, POINTER_CAST(user->username_hash)); DLLIST2_REMOVE(&dir->head, &dir->tail, user); i_free(user); } static bool user_directory_user_has_connections(struct user_directory *dir, struct user *user, unsigned int *retry_secs_r) { time_t expire_timestamp = user->timestamp + dir->timeout_secs; if (expire_timestamp > ioloop_time) { *retry_secs_r = expire_timestamp - ioloop_time; return TRUE; } if (USER_IS_BEING_KILLED(user)) { /* don't free this user until the kill is finished */ *retry_secs_r = USER_BEING_KILLED_EXPIRE_RETRY_SECS; return TRUE; } if (user->weak) { if (expire_timestamp + USER_NEAR_EXPIRING_MAX >= ioloop_time) { *retry_secs_r = expire_timestamp + USER_NEAR_EXPIRING_MAX - ioloop_time; return TRUE; } i_warning("User %u weakness appears to be stuck, removing it", user->username_hash); } return FALSE; } static void user_directory_drop_expired(struct user_directory *dir) { unsigned int retry_secs = 0; while (dir->head != NULL && !user_directory_user_has_connections(dir, dir->head, &retry_secs)) user_free(dir, dir->head); if (dir->to_expire != NULL) timeout_remove(&dir->to_expire); if (retry_secs > 0) { dir->to_expire = timeout_add(retry_secs * 1000, user_directory_drop_expired, dir); } } unsigned int user_directory_count(struct user_directory *dir) { return hash_table_count(dir->hash); } struct user *user_directory_lookup(struct user_directory *dir, unsigned int username_hash) { struct user *user; unsigned int retry_secs; user_directory_drop_expired(dir); user = hash_table_lookup(dir->hash, POINTER_CAST(username_hash)); if (user != NULL && !user_directory_user_has_connections(dir, user, &retry_secs)) { user_free(dir, user); user = NULL; } return user; } static void user_directory_insert_backwards(struct user_directory *dir, struct user *pos, struct user *user) { for (; pos != NULL; pos = pos->prev) { if (pos->timestamp <= user->timestamp) break; } if (pos == NULL) DLLIST2_PREPEND(&dir->head, &dir->tail, user); else { user->prev = pos; user->next = pos->next; user->prev->next = user; if (user->next != NULL) user->next->prev = user; else dir->tail = user; } } static void user_directory_insert_forwards(struct user_directory *dir, struct user *pos, struct user *user) { for (; pos != NULL; pos = pos->next) { if (pos->timestamp >= user->timestamp) break; } if (pos == NULL) DLLIST2_APPEND(&dir->head, &dir->tail, user); else { user->prev = pos->prev; user->next = pos; if (user->prev != NULL) user->prev->next = user; else dir->head = user; user->next->prev = user; } } struct user * user_directory_add(struct user_directory *dir, unsigned int username_hash, struct mail_host *host, time_t timestamp) { struct user *user; /* make sure we don't add timestamps higher than ioloop time */ if (timestamp > ioloop_time) timestamp = ioloop_time; user = i_new(struct user, 1); user->username_hash = username_hash; user->host = host; user->host->user_count++; user->timestamp = timestamp; if (dir->tail == NULL || (time_t)dir->tail->timestamp <= timestamp) DLLIST2_APPEND(&dir->head, &dir->tail, user); else { /* need to insert to correct position. we should get here only when handshaking. the handshaking USER requests should come sorted by timestamp. so keep track of the previous insert position, the next USER should be inserted after it. */ if (dir->prev_insert_pos == NULL) { /* find the position starting from tail */ user_directory_insert_backwards(dir, dir->tail, user); } else if (timestamp < (time_t)dir->prev_insert_pos->timestamp) { user_directory_insert_backwards(dir, dir->prev_insert_pos, user); } else { user_directory_insert_forwards(dir, dir->prev_insert_pos, user); } } if (dir->to_expire == NULL) { dir->to_expire = timeout_add(dir->timeout_secs * 1000, user_directory_drop_expired, dir); } dir->prev_insert_pos = user; hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user); return user; } void user_directory_refresh(struct user_directory *dir, struct user *user) { user_move_iters(dir, user); user->timestamp = ioloop_time; DLLIST2_REMOVE(&dir->head, &dir->tail, user); DLLIST2_APPEND(&dir->head, &dir->tail, user); } void user_directory_remove_host(struct user_directory *dir, struct mail_host *host) { struct user *user, *next; for (user = dir->head; user != NULL; user = next) { next = user->next; if (user->host == host) user_free(dir, user); } } static int user_timestamp_cmp(struct user *const *user1, struct user *const *user2) { if ((*user1)->timestamp < (*user2)->timestamp) return -1; if ((*user1)->timestamp > (*user2)->timestamp) return 1; return 0; } void user_directory_sort(struct user_directory *dir) { ARRAY(struct user *) users; struct user *user, *const *userp; unsigned int i, users_count = hash_table_count(dir->hash); if (users_count == 0) { i_assert(dir->head == NULL); return; } /* place all users into array and sort it */ i_array_init(&users, users_count); user = dir->head; for (i = 0; i < users_count; i++, user = user->next) array_append(&users, &user, 1); i_assert(user == NULL); array_sort(&users, user_timestamp_cmp); /* recreate the linked list */ dir->head = dir->tail = NULL; array_foreach(&users, userp) DLLIST2_APPEND(&dir->head, &dir->tail, *userp); i_assert(dir->head != NULL && dir->head->timestamp <= dir->tail->timestamp); array_free(&users); } bool user_directory_user_is_recently_updated(struct user_directory *dir, struct user *user) { return (time_t)(user->timestamp + dir->timeout_secs/2) >= ioloop_time; } bool user_directory_user_is_near_expiring(struct user_directory *dir, struct user *user) { time_t expire_timestamp; expire_timestamp = user->timestamp + (dir->timeout_secs - dir->user_near_expiring_secs); return expire_timestamp < ioloop_time; } struct user_directory * user_directory_init(unsigned int timeout_secs, user_free_hook_t *user_free_hook) { struct user_directory *dir; i_assert(timeout_secs > USER_NEAR_EXPIRING_MIN); dir = i_new(struct user_directory, 1); dir->timeout_secs = timeout_secs; dir->user_near_expiring_secs = timeout_secs * USER_NEAR_EXPIRING_PERCENTAGE / 100; dir->user_near_expiring_secs = I_MIN(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MAX); dir->user_near_expiring_secs = I_MAX(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MIN); i_assert(dir->timeout_secs/2 > dir->user_near_expiring_secs); dir->user_free_hook = user_free_hook; hash_table_create_direct(&dir->hash, default_pool, 0); i_array_init(&dir->iters, 8); return dir; } void user_directory_deinit(struct user_directory **_dir) { struct user_directory *dir = *_dir; *_dir = NULL; i_assert(array_count(&dir->iters) == 0); while (dir->head != NULL) user_free(dir, dir->head); if (dir->to_expire != NULL) timeout_remove(&dir->to_expire); hash_table_destroy(&dir->hash); array_free(&dir->iters); i_free(dir); } struct user_directory_iter * user_directory_iter_init(struct user_directory *dir) { struct user_directory_iter *iter; iter = i_new(struct user_directory_iter, 1); iter->dir = dir; iter->pos = dir->head; array_append(&dir->iters, &iter, 1); user_directory_drop_expired(dir); return iter; } struct user *user_directory_iter_next(struct user_directory_iter *iter) { struct user *user; user = iter->pos; if (user == NULL) return NULL; iter->pos = user->next; return user; } void user_directory_iter_deinit(struct user_directory_iter **_iter) { struct user_directory_iter *iter = *_iter; struct user_directory_iter *const *iters; unsigned int i, count; *_iter = NULL; iters = array_get(&iter->dir->iters, &count); for (i = 0; i < count; i++) { if (iters[i] == iter) { array_delete(&iter->dir->iters, i, 1); break; } } i_free(iter); } dovecot-2.2.33.2/src/director/login-connection.h0000644000175000017500000000072113123174404016344 00000000000000#ifndef LOGIN_CONNECTION_H #define LOGIN_CONNECTION_H struct director; enum login_connection_type { LOGIN_CONNECTION_TYPE_AUTH, LOGIN_CONNECTION_TYPE_USERDB, LOGIN_CONNECTION_TYPE_AUTHREPLY }; struct login_connection * login_connection_init(struct director *dir, int fd, struct auth_connection *auth, enum login_connection_type type); void login_connection_deinit(struct login_connection **conn); void login_connections_deinit(void); #endif dovecot-2.2.33.2/src/director/director-settings.c0000644000175000017500000000630413165463624016561 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "director-settings.h" /* */ static bool director_settings_verify(void *_set, pool_t pool, const char **error_r); static struct file_listener_settings director_unix_listeners_array[] = { { "login/director", 0, "", "" }, { "director-admin", 0600, "", "" } }; static struct file_listener_settings *director_unix_listeners[] = { &director_unix_listeners_array[0], &director_unix_listeners_array[1] }; static buffer_t director_unix_listeners_buf = { director_unix_listeners, sizeof(director_unix_listeners), { NULL, } }; static struct file_listener_settings director_fifo_listeners_array[] = { { "login/proxy-notify", 0, "", "" } }; static struct file_listener_settings *director_fifo_listeners[] = { &director_fifo_listeners_array[0] }; static buffer_t director_fifo_listeners_buf = { director_fifo_listeners, sizeof(director_fifo_listeners), { NULL, } }; /* */ struct service_settings director_service_settings = { .name = "director", .protocol = "", .type = "", .executable = "director", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = ".", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &director_unix_listeners_buf, sizeof(director_unix_listeners[0]) } }, .fifo_listeners = { { &director_fifo_listeners_buf, sizeof(director_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct director_settings, name), NULL } static const struct setting_define director_setting_defines[] = { DEF(SET_STR, master_user_separator), DEF(SET_STR, director_servers), DEF(SET_STR, director_mail_servers), DEF(SET_STR, director_username_hash), DEF(SET_STR, director_flush_socket), DEF(SET_TIME, director_user_expire), DEF(SET_TIME, director_user_kick_delay), DEF(SET_IN_PORT, director_doveadm_port), DEF(SET_BOOL, director_consistent_hashing), SETTING_DEFINE_LIST_END }; const struct director_settings director_default_settings = { .master_user_separator = "", .director_servers = "", .director_mail_servers = "", .director_username_hash = "%Lu", .director_flush_socket = "", .director_user_expire = 60*15, .director_user_kick_delay = 2, .director_doveadm_port = 0 }; const struct setting_parser_info director_setting_parser_info = { .module_name = "director", .defines = director_setting_defines, .defaults = &director_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct director_settings), .parent_offset = (size_t)-1, .check_func = director_settings_verify }; /* */ static bool director_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct director_settings *set = _set; if (set->director_user_expire < 10) { *error_r = "director_user_expire is too low"; return FALSE; } return TRUE; } /* */ dovecot-2.2.33.2/src/director/doveadm-connection.h0000644000175000017500000000037413165463543016672 00000000000000#ifndef DOVEADM_CONNECTION_H #define DOVEADM_CONNECTION_H struct director; struct doveadm_connection * doveadm_connection_init(struct director *dir, int fd); void doveadm_connections_deinit(void); void doveadm_connections_ring_synced(void); #endif dovecot-2.2.33.2/src/director/notify-connection.h0000644000175000017500000000034313123174404016544 00000000000000#ifndef NOTIFY_CONNECTION_H #define NOTIFY_CONNECTION_H struct director; struct notify_connection *notify_connection_init(struct director *dir, int fd); void notify_connection_deinit(struct notify_connection **conn); #endif dovecot-2.2.33.2/src/director/director-request.c0000644000175000017500000002341313165463624016411 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "mail-host.h" #include "director.h" #include "director-request.h" #define DIRECTOR_REQUEST_TIMEOUT_SECS 30 #define RING_NOCONN_WARNING_DELAY_MSECS (2*1000) enum director_request_delay_reason { REQUEST_DELAY_NONE = 0, REQUEST_DELAY_RINGNOTHANDSHAKED, REQUEST_DELAY_RINGNOTSYNCED, REQUEST_DELAY_NOHOSTS, REQUEST_DELAY_WEAK, REQUEST_DELAY_KILL }; static const char *delay_reason_strings[] = { "unknown", "ring not handshaked", "ring not synced", "no hosts", "weak user", "kill waiting" }; struct director_request { struct director *dir; time_t create_time; unsigned int username_hash; enum director_request_delay_reason delay_reason; char *username_tag; director_request_callback *callback; void *context; }; static void director_request_free(struct director_request *request) { i_free(request->username_tag); i_free(request); } static const char * director_request_get_timeout_error(struct director_request *request, struct user *user, string_t *str) { unsigned int secs; str_truncate(str, 0); str_printfa(str, "Timeout because %s - queued for %u secs (", delay_reason_strings[request->delay_reason], (unsigned int)(ioloop_time - request->create_time)); if (request->dir->ring_last_sync_time == 0) str_append(str, "Ring has never been synced"); else { secs = ioloop_time - request->dir->ring_last_sync_time; if (request->dir->ring_synced) str_printfa(str, "Ring synced for %u secs", secs); else str_printfa(str, "Ring not synced for %u secs", secs); } if (user != NULL) { if (user->weak) str_append(str, ", weak user"); str_printfa(str, ", user refreshed %u secs ago", (unsigned int)(ioloop_time - user->timestamp)); } str_printfa(str, ", hash=%u", request->username_hash); if (request->username_tag != NULL) str_printfa(str, ", tag=%s", request->username_tag); str_append_c(str, ')'); return str_c(str); } static void director_request_timeout(struct director *dir) { struct director_request **requestp, *request; struct user *user; const char *errormsg; string_t *str = t_str_new(128); while (array_count(&dir->pending_requests) > 0) { requestp = array_idx_modifiable(&dir->pending_requests, 0); request = *requestp; if (request->create_time + DIRECTOR_REQUEST_TIMEOUT_SECS > ioloop_time) break; const char *tag_name = request->username_tag == NULL ? "" : request->username_tag; struct mail_tag *tag = mail_tag_find(dir->mail_hosts, tag_name); user = tag == NULL ? NULL : user_directory_lookup(tag->users, request->username_hash); errormsg = director_request_get_timeout_error(request, user, str); if (user != NULL && request->delay_reason == REQUEST_DELAY_WEAK) { /* weakness appears to have gotten stuck. this is a bug, but try to fix it for future requests by removing the weakness. */ user->weak = FALSE; } array_delete(&dir->pending_requests, 0, 1); T_BEGIN { request->callback(NULL, NULL, errormsg, request->context); } T_END; director_request_free(request); } if (array_count(&dir->pending_requests) == 0 && dir->to_request != NULL) timeout_remove(&dir->to_request); } void director_request(struct director *dir, const char *username, const char *tag, director_request_callback *callback, void *context) { struct director_request *request; unsigned int username_hash = director_get_username_hash(dir, username); dir->num_requests++; request = i_new(struct director_request, 1); request->dir = dir; request->create_time = ioloop_time; request->username_hash = username_hash; request->username_tag = tag[0] == '\0' ? NULL : i_strdup(tag); request->callback = callback; request->context = context; if (director_request_continue(request)) return; /* need to queue it */ if (dir->to_request == NULL) { dir->to_request = timeout_add(DIRECTOR_REQUEST_TIMEOUT_SECS * 1000, director_request_timeout, dir); } array_append(&dir->pending_requests, &request, 1); } static void ring_noconn_warning(struct director *dir) { if (!dir->ring_handshaked) { i_warning("Delaying all requests " "until all directors have connected"); } else { i_warning("Delaying new user requests until ring is synced"); } dir->ring_handshake_warning_sent = TRUE; timeout_remove(&dir->to_handshake_warning); } static void ring_log_delayed_warning(struct director *dir) { if (dir->ring_handshake_warning_sent || dir->to_handshake_warning != NULL) return; dir->to_handshake_warning = timeout_add(RING_NOCONN_WARNING_DELAY_MSECS, ring_noconn_warning, dir); } static bool director_request_existing(struct director_request *request, struct user *user) { struct director *dir = request->dir; struct mail_host *host; if (USER_IS_BEING_KILLED(user)) { /* delay processing this user's connections until its existing connections have been killed */ request->delay_reason = REQUEST_DELAY_KILL; dir_debug("request: %u waiting for kill to finish", user->username_hash); return FALSE; } if (dir->right == NULL && dir->ring_synced) { /* looks like all the other directors have died. we can do whatever we want without breaking anything. remove the user's weakness just in case it was set to TRUE when we had more directors. */ user->weak = FALSE; return TRUE; } if (user->weak) { /* wait for user to become non-weak */ request->delay_reason = REQUEST_DELAY_WEAK; dir_debug("request: %u waiting for weakness", request->username_hash); return FALSE; } if (!user_directory_user_is_near_expiring(user->host->tag->users, user)) return TRUE; /* user is close to being expired. another director may have already expired it. */ host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash, user->host->tag->name); if (!dir->ring_synced) { /* try again later once ring is synced */ request->delay_reason = REQUEST_DELAY_RINGNOTSYNCED; dir_debug("request: %u waiting for sync for making weak", request->username_hash); return FALSE; } if (user->host == host) { /* doesn't matter, other directors would assign the user the same way regardless */ dir_debug("request: %u would be weak, but host doesn't change", request->username_hash); return TRUE; } /* We have to worry about two separate timepoints in here: a) some directors think the user isn't expiring, and others think the user is near expiring b) some directors think the user is near expiring, and others think the user has already expired What we don't have to worry about is: !c) some directors think the user isn't expiring, and others think the user has already expired If !c) happens, the user might get redirected to different backends. We'll use a large enough timeout between a) and b) states, so that !c) should never happen. So what we'll do here is: 1. Send a USER-WEAK notification to all directors with the new host. 2. Each director receiving USER-WEAK refreshes the user's timestamp and host, but marks the user as being weak. 3. Once USER-WEAK has reached all directors, a real USER update is sent, which removes the weak-flag. 4. If a director ever receives a USER update for a weak user, the USER update overrides the host and removes the weak-flag. 5. Director doesn't let any weak user log in, until the weak-flag gets removed. */ if (dir->ring_min_version < DIRECTOR_VERSION_WEAK_USERS) { /* weak users not supported by ring currently */ return TRUE; } else { user->weak = TRUE; director_update_user_weak(dir, dir->self_host, NULL, NULL, user); request->delay_reason = REQUEST_DELAY_WEAK; dir_debug("request: %u set to weak", request->username_hash); return FALSE; } } bool director_request_continue(struct director_request *request) { struct director *dir = request->dir; struct mail_host *host; struct user *user; const char *tag; struct mail_tag *mail_tag; if (!dir->ring_handshaked) { /* delay requests until ring handshaking is complete */ dir_debug("request: %u waiting for handshake", request->username_hash); ring_log_delayed_warning(dir); request->delay_reason = REQUEST_DELAY_RINGNOTHANDSHAKED; return FALSE; } tag = request->username_tag == NULL ? "" : request->username_tag; mail_tag = mail_tag_find(dir->mail_hosts, tag); user = mail_tag == NULL ? NULL : user_directory_lookup(mail_tag->users, request->username_hash); if (user != NULL) { i_assert(user->host->tag == mail_tag); if (!director_request_existing(request, user)) return FALSE; user_directory_refresh(mail_tag->users, user); dir_debug("request: %u refreshed timeout to %u", request->username_hash, user->timestamp); } else { if (!dir->ring_synced) { /* delay adding new users until ring is again synced */ ring_log_delayed_warning(dir); request->delay_reason = REQUEST_DELAY_RINGNOTSYNCED; dir_debug("request: %u waiting for sync for adding", request->username_hash); return FALSE; } host = mail_host_get_by_hash(dir->mail_hosts, request->username_hash, tag); if (host == NULL) { /* all hosts have been removed */ request->delay_reason = REQUEST_DELAY_NOHOSTS; dir_debug("request: %u waiting for hosts", request->username_hash); return FALSE; } user = user_directory_add(host->tag->users, request->username_hash, host, ioloop_time); dir_debug("request: %u added timeout to %u (hosts_hash=%u)", request->username_hash, user->timestamp, mail_hosts_hash(dir->mail_hosts)); } i_assert(!user->weak); director_update_user(dir, dir->self_host, user); T_BEGIN { request->callback(&user->host->ip, user->host->hostname, NULL, request->context); } T_END; director_request_free(request); return TRUE; } dovecot-2.2.33.2/src/director/director-connection.h0000644000175000017500000000242713123174404017054 00000000000000#ifndef DIRECTOR_CONNECTION_H #define DIRECTOR_CONNECTION_H struct director_host; struct director; struct director_connection * director_connection_init_in(struct director *dir, int fd, const struct ip_addr *ip); struct director_connection * director_connection_init_out(struct director *dir, int fd, struct director_host *host); void director_connection_deinit(struct director_connection **conn, const char *remote_reason); void director_connection_send(struct director_connection *conn, const char *data); void director_connection_set_synced(struct director_connection *conn, bool synced); void director_connection_ping(struct director_connection *conn); const char *director_connection_get_name(struct director_connection *conn); struct director_host * director_connection_get_host(struct director_connection *conn); bool director_connection_is_handshaked(struct director_connection *conn); bool director_connection_is_synced(struct director_connection *conn); bool director_connection_is_incoming(struct director_connection *conn); unsigned int director_connection_get_minor_version(struct director_connection *conn); void director_connection_cork(struct director_connection *conn); void director_connection_uncork(struct director_connection *conn); #endif dovecot-2.2.33.2/src/director/user-directory.h0000644000175000017500000000506513165463624016100 00000000000000#ifndef USER_DIRECTORY_H #define USER_DIRECTORY_H #define USER_IS_BEING_KILLED(user) \ ((user)->kill_ctx != NULL) struct user { /* sorted by time */ struct user *prev, *next; /* first 32 bits of MD5(username). collisions are quite unlikely, but even if they happen it doesn't matter - the users are just redirected to same server */ unsigned int username_hash; unsigned int timestamp; struct mail_host *host; /* If non-NULL, don't allow new connections until all directors have killed the user's connections. */ struct director_kill_context *kill_ctx; /* TRUE, if the user's timestamp was close to being expired and we're now doing a ring-wide sync for this user to make sure we don't assign conflicting hosts to it */ unsigned int weak:1; }; typedef void user_free_hook_t(struct user *); /* Create a new directory. Users are dropped if their time gets older than timeout_secs. */ struct user_directory * user_directory_init(unsigned int timeout_secs, user_free_hook_t *user_free_hook); void user_directory_deinit(struct user_directory **dir); /* Returns the number of users currently in directory. */ unsigned int user_directory_count(struct user_directory *dir); /* Look up username from directory. Returns NULL if not found. */ struct user *user_directory_lookup(struct user_directory *dir, unsigned int username_hash); /* Add a user to directory and return it. */ struct user * user_directory_add(struct user_directory *dir, unsigned int username_hash, struct mail_host *host, time_t timestamp); /* Refresh user's timestamp */ void user_directory_refresh(struct user_directory *dir, struct user *user); /* Remove all users that have pointers to given host */ void user_directory_remove_host(struct user_directory *dir, struct mail_host *host); /* Sort users based on the timestamp. This is called only after updating timestamps based on remote director's user list after handshake. */ void user_directory_sort(struct user_directory *dir); bool user_directory_user_is_recently_updated(struct user_directory *dir, struct user *user); bool user_directory_user_is_near_expiring(struct user_directory *dir, struct user *user); /* Iterate through users in the directory. It's safe to modify user directory while iterators are running. The moved/removed users will just be skipped over. */ struct user_directory_iter * user_directory_iter_init(struct user_directory *dir); struct user *user_directory_iter_next(struct user_directory_iter *iter); void user_directory_iter_deinit(struct user_directory_iter **iter); #endif dovecot-2.2.33.2/src/director/director-host.h0000644000175000017500000000474713165463624015714 00000000000000#ifndef DIRECTOR_HOST_H #define DIRECTOR_HOST_H #include "net.h" struct director; struct director_host { struct director *dir; int refcount; struct ip_addr ip; in_port_t port; /* name contains "ip:port" */ char *name; /* change commands each have originating host and originating sequence. we'll keep track of the highest sequence we've seen from the host. if we find a lower sequence, we've already handled the command and it can be ignored (or: it must be ignored to avoid potential command loops) */ unsigned int last_seq; /* use these to avoid infinitely sending SYNCs for directors that aren't connected in the ring. */ unsigned int last_sync_seq, last_sync_seq_counter, last_sync_timestamp; /* whenever we receive a SYNC with stale hosts_hash, set this. if it's already set and equals the current hosts_hash, re-send our hosts to everybody in case they somehow got out of sync. */ unsigned int desynced_hosts_hash; /* Last time host was detected to be down */ time_t last_network_failure; time_t last_protocol_failure; /* When we finish getting a right connection, send a SYNC with these parameters (if delayed_sync_seq != 0) */ uint32_t delayed_sync_seq; unsigned int delayed_sync_minor_version; unsigned int delayed_sync_timestamp; unsigned int delayed_sync_hosts_hash; /* we are this director */ unsigned int self:1; unsigned int removed:1; }; struct director_host * director_host_add(struct director *dir, const struct ip_addr *ip, in_port_t port); void director_host_free(struct director_host **host); void director_host_ref(struct director_host *host); void director_host_unref(struct director_host *host); void director_host_restarted(struct director_host *host); struct director_host * director_host_get(struct director *dir, const struct ip_addr *ip, in_port_t port); struct director_host * director_host_lookup(struct director *dir, const struct ip_addr *ip, in_port_t port); struct director_host * director_host_lookup_ip(struct director *dir, const struct ip_addr *ip); /* Returns 0 if b1 equals b2. -1 if b1 is closer to our left side than b2 or -1 if b2 is closer to our right side than b1 1 vice versa */ int director_host_cmp_to_self(const struct director_host *b1, const struct director_host *b2, const struct director_host *self); /* Parse hosts list (e.g. "host1:port host2 host3:port") and them as directors */ void director_host_add_from_string(struct director *dir, const char *hosts); #endif dovecot-2.2.33.2/src/director/mail-host.h0000644000175000017500000000563513165463624015020 00000000000000#ifndef MAIL_HOST_H #define MAIL_HOST_H #include "net.h" #include "user-directory.h" struct mail_host_list; struct mail_vhost { unsigned int hash; struct mail_host *host; }; /* mail_tags aren't removed/freed before mail_hosts_deinit(), so it's safe to add pointers to them. */ struct mail_tag { /* "" = no tag */ char *name; ARRAY(struct mail_vhost) vhosts; /* temporary user -> host associations */ struct user_directory *users; }; ARRAY_DEFINE_TYPE(mail_tag, struct mail_tag *); struct mail_host { struct mail_host_list *list; unsigned int user_count; unsigned int vhost_count; /* server up/down. down=TRUE has effectively the same result as if vhost_count=0. */ bool down; time_t last_updown_change; struct ip_addr ip; char *hostname; struct mail_tag *tag; /* host was recently changed and ring hasn't synced yet since */ unsigned int desynced:1; }; ARRAY_DEFINE_TYPE(mail_host, struct mail_host *); struct mail_host * mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip, const char *tag_name); struct mail_host * mail_host_add_hostname(struct mail_host_list *list, const char *hostname, const struct ip_addr *ip, const char *tag_name); struct mail_host * mail_host_add_hostname(struct mail_host_list *list, const char *hostname, const struct ip_addr *ip, const char *tag); struct mail_host * mail_host_lookup(struct mail_host_list *list, const struct ip_addr *ip); struct mail_host * mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash, const char *tag_name); int mail_hosts_parse_and_add(struct mail_host_list *list, const char *hosts_string); const char *mail_host_get_tag(const struct mail_host *host); void mail_host_set_tag(struct mail_host *host, const char *tag_name); void mail_host_set_down(struct mail_host *host, bool down, time_t timestamp, const char *log_prefix); void mail_host_set_vhost_count(struct mail_host *host, unsigned int vhost_count, const char *log_prefix); void mail_host_remove(struct mail_host *host); void mail_hosts_set_synced(struct mail_host_list *list); unsigned int mail_hosts_hash(struct mail_host_list *list); bool mail_hosts_have_usable(struct mail_host_list *list); const ARRAY_TYPE(mail_host) *mail_hosts_get(struct mail_host_list *list); bool mail_hosts_have_tags(struct mail_host_list *list); const ARRAY_TYPE(mail_tag) *mail_hosts_get_tags(struct mail_host_list *list); struct mail_tag * mail_tag_find(struct mail_host_list *list, const char *tag_name); struct user * mail_hosts_find_user(struct mail_host_list *list, const char *tag_name, unsigned int username_hash); struct mail_host_list * mail_hosts_init(unsigned int user_expire_secs, bool consistent_hashing, user_free_hook_t *user_free_hook); void mail_hosts_deinit(struct mail_host_list **list); struct mail_host_list *mail_hosts_dup(const struct mail_host_list *src); void mail_hosts_sort_users(struct mail_host_list *list); #endif dovecot-2.2.33.2/src/director/auth-connection.c0000644000175000017500000000605513165463624016211 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "net.h" #include "llist.h" #include "safe-memset.h" #include "auth-client-interface.h" #include "auth-connection.h" #include struct auth_connection { struct auth_connection *prev, *next; char *path; int fd; struct io *io; struct istream *input; struct ostream *output; auth_input_callback *callback; void *context; }; static struct auth_connection *auth_connections; static void auth_connection_disconnected(struct auth_connection **conn); static void auth_connection_input(struct auth_connection *conn) { char *line; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ i_error("Auth server disconnected unexpectedly"); auth_connection_disconnected(&conn); return; case -2: /* buffer full */ i_error("BUG: Auth server sent us more than %d bytes", (int)AUTH_CLIENT_MAX_LINE_LENGTH); auth_connection_disconnected(&conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { conn->callback(line, conn->context); safe_memset(line, 0, strlen(line)); } T_END; } } struct auth_connection *auth_connection_init(const char *path) { struct auth_connection *conn; conn = i_new(struct auth_connection, 1); conn->fd = -1; conn->path = i_strdup(path); DLLIST_PREPEND(&auth_connections, conn); return conn; } void auth_connection_set_callback(struct auth_connection *conn, auth_input_callback *callback, void *context) { conn->callback = callback; conn->context = context; } int auth_connection_connect(struct auth_connection *conn) { i_assert(conn->fd == -1); conn->fd = net_connect_unix_with_retries(conn->path, 1000); if (conn->fd == -1) { i_error("connect(%s) failed: %m", conn->path); return -1; } conn->input = i_stream_create_fd(conn->fd, AUTH_CLIENT_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); conn->io = io_add(conn->fd, IO_READ, auth_connection_input, conn); return 0; } void auth_connection_deinit(struct auth_connection **_conn) { struct auth_connection *conn = *_conn; *_conn = NULL; DLLIST_REMOVE(&auth_connections, conn); if (conn->fd != -1) { io_remove(&conn->io); i_stream_unref(&conn->input); o_stream_unref(&conn->output); if (close(conn->fd) < 0) i_error("close(auth connection) failed: %m"); } i_free(conn->path); i_free(conn); } static void auth_connection_disconnected(struct auth_connection **_conn) { struct auth_connection *conn = *_conn; *_conn = NULL; /* notify callback. it should deinit this connection */ conn->callback(NULL, conn->context); } struct ostream *auth_connection_get_output(struct auth_connection *conn) { i_assert(conn->output != NULL); return conn->output; } void auth_connections_deinit(void) { while (auth_connections != NULL) { struct auth_connection *conn = auth_connections; auth_connection_disconnected(&conn); } } dovecot-2.2.33.2/src/director/director.h0000644000175000017500000002244113165463624014730 00000000000000#ifndef DIRECTOR_H #define DIRECTOR_H #include "net.h" #include "director-settings.h" #define DIRECTOR_VERSION_NAME "director" #define DIRECTOR_VERSION_MAJOR 1 #define DIRECTOR_VERSION_MINOR 8 /* weak users supported in protocol */ #define DIRECTOR_VERSION_WEAK_USERS 1 /* director ring remove supported */ #define DIRECTOR_VERSION_RING_REMOVE 2 /* quit reason supported */ #define DIRECTOR_VERSION_QUIT 3 /* user-kick supported */ #define DIRECTOR_VERSION_USER_KICK 4 /* options supported in handshake */ #define DIRECTOR_VERSION_OPTIONS 5 /* user tags supported */ #define DIRECTOR_VERSION_TAGS 5 /* up/down state is tracked */ #define DIRECTOR_VERSION_UPDOWN 6 /* user tag version 2 supported */ #define DIRECTOR_VERSION_TAGS_V2 7 /* user-kick-alt supported */ #define DIRECTOR_VERSION_USER_KICK_ALT 8 /* Minimum time between even attempting to communicate with a director that failed due to a protocol error. */ #define DIRECTOR_PROTOCOL_FAILURE_RETRY_SECS 60 struct director; struct mail_host; struct user; struct director_user_init; enum user_kill_state { /* User isn't being killed */ USER_KILL_STATE_NONE, /* We're still killing the user's connections */ USER_KILL_STATE_KILLING, /* Like above, but our left side already announced it was finished with killing its user connections */ USER_KILL_STATE_KILLING_NOTIFY_RECEIVED, /* We're done killing, but we have to wait for the left side to finish killing its user connections before sending USER-KILLED to our right side */ USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY, /* We're done killing, but waiting for USER-KILLED-EVERYWHERE notification until this state gets reset. */ USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE, /* Waiting for the flush socket to finish. */ USER_KILL_STATE_FLUSHING, /* Wait for a while for the user connections to actually die. Note that only at this stage we can be sure that all the directors know about the user move (although it could be earlier if we added a new USER-MOVED notification). */ USER_KILL_STATE_DELAY /* NOTE: remember to update also user_kill_state_names[] */ }; extern const char *user_kill_state_names[USER_KILL_STATE_DELAY+1]; typedef void director_state_change_callback_t(struct director *dir); /* When a user gets freed, the kill_ctx may still be left alive. It's also possible for the user to come back, in which case the kill_ctx is usually NULL, but another kill could have also started. The previous kill_ctx is valid only if it matches the current user's kill_ctx. */ #define DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx) \ ((user) != NULL && (user)->kill_ctx == ctx) struct director_kill_context { struct director *dir; struct mail_tag *tag; unsigned int username_hash; struct ip_addr old_host_ip; unsigned int old_host_vhost_count; bool old_host_down; bool kill_is_self_initiated; bool callback_pending; enum user_kill_state kill_state; /* Move timeout to make sure user's connections won't silently hang indefinitely if there is some trouble moving it. */ struct timeout *to_move; /* these are set only for director_flush_socket handling: */ struct ip_addr host_ip; struct program_client *pclient; struct ostream *reply; char *socket_path; }; struct director { const struct director_settings *set; /* IP and port of this director. self_host->ip/port must equal these. */ struct ip_addr self_ip; in_port_t self_port; in_port_t test_port; struct director_host *self_host; /* left and right connections are set only after they have finished handshaking. until then they're in the connections list, although updates are still sent to them during handshaking if the USER list is long. */ struct director_connection *left, *right; /* all director connections */ ARRAY(struct director_connection *) connections; struct timeout *to_reconnect; struct timeout *to_sync; struct timeout *to_callback; /* current mail hosts */ struct mail_host_list *mail_hosts; /* original mail hosts configured in config file. this is used only for doveadm lookups */ struct mail_host_list *orig_config_hosts; /* Number of users currently being moved */ unsigned int users_moving_count; /* these requests are waiting for directors to be in synced */ ARRAY(struct director_request *) pending_requests; struct timeout *to_request; struct timeout *to_handshake_warning; director_state_change_callback_t *state_change_callback; /* director hosts are sorted by IP (and port) */ ARRAY(struct director_host *) dir_hosts; struct timeout *to_remove_dirs; struct ipc_client *ipc_proxy; unsigned int sync_seq; unsigned int ring_change_counter; unsigned int last_sync_sent_ring_change_counter; /* the lowest minor version supported by the ring */ unsigned int ring_min_version; time_t ring_last_sync_time; time_t ring_first_alone; uint64_t num_requests; uint64_t ring_traffic_input, ring_traffic_output; /* director ring handshaking is complete. director can start serving clients. */ unsigned int ring_handshaked:1; unsigned int ring_handshake_warning_sent:1; unsigned int ring_synced:1; unsigned int sync_frozen:1; unsigned int sync_pending:1; }; extern bool director_debug; /* Create a new director. If listen_ip specifies an actual IP, it's used with listen_port for finding ourself from the director_servers setting. listen_port is used regardless by director_host_add_from_string() for hosts without specified port. */ struct director * director_init(const struct director_settings *set, const struct ip_addr *listen_ip, in_port_t listen_port, director_state_change_callback_t *callback); void director_deinit(struct director **dir); void director_find_self(struct director *dir); /* Start connecting to other directors */ void director_connect(struct director *dir, const char *reason); void director_set_ring_handshaked(struct director *dir); void director_set_ring_synced(struct director *dir); void director_set_ring_unsynced(struct director *dir); void director_set_state_changed(struct director *dir); void director_sync_send(struct director *dir, struct director_host *host, uint32_t seq, unsigned int minor_version, unsigned int timestamp, unsigned int hosts_hash); bool director_resend_sync(struct director *dir); void director_notify_ring_added(struct director_host *added_host, struct director_host *src, bool log); void director_ring_remove(struct director_host *removed_host, struct director_host *src); void director_update_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) ATTR_NULL(3); void director_resend_hosts(struct director *dir); void director_remove_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) ATTR_NULL(2, 3); void director_flush_host(struct director *dir, struct director_host *src, struct director_host *orig_src, struct mail_host *host) ATTR_NULL(3); void director_update_user(struct director *dir, struct director_host *src, struct user *user); void director_update_user_weak(struct director *dir, struct director_host *src, struct director_connection *src_conn, struct director_host *orig_src, struct user *user) ATTR_NULL(3); void director_kill_user(struct director *dir, struct director_host *src, struct user *user, struct mail_tag *tag, struct mail_host *old_host, bool forced_kick); void director_move_user(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash, struct mail_host *host) ATTR_NULL(3); void director_kick_user(struct director *dir, struct director_host *src, struct director_host *orig_src, const char *username) ATTR_NULL(3); void director_kick_user_alt(struct director *dir, struct director_host *src, struct director_host *orig_src, const char *field, const char *value) ATTR_NULL(3); void director_kick_user_hash(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash, const struct ip_addr *except_ip) ATTR_NULL(3); void director_user_killed(struct director *dir, unsigned int username_hash); void director_user_killed_everywhere(struct director *dir, struct director_host *src, struct director_host *orig_src, unsigned int username_hash) ATTR_NULL(3); void director_user_weak(struct director *dir, struct user *user); void director_sync_freeze(struct director *dir); void director_sync_thaw(struct director *dir); /* Send data to all directors using both left and right connections (unless they're the same). */ void director_update_send(struct director *dir, struct director_host *src, const char *cmd); void director_update_send_version(struct director *dir, struct director_host *src, unsigned int min_version, const char *cmd); int director_connect_host(struct director *dir, struct director_host *host, const char *reason); unsigned int director_get_username_hash(struct director *dir, const char *username); void directors_init(void); void directors_deinit(void); void dir_debug(const char *fmt, ...) ATTR_FORMAT(1, 2); struct director_user_iter *director_iterate_users_init(struct director *dir); struct user *director_iterate_users_next(struct director_user_iter *iter); void director_iterate_users_deinit(struct director_user_iter **_iter); #endif dovecot-2.2.33.2/src/director/director-connection.c0000644000175000017500000021372213165463624017064 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ /* Handshaking: Incoming director connections send: VERSION ME DONE Outgoing director connections send: VERSION ME [0..n] DIRECTOR HOST-HAND-START [0..n] HOST HOST-HAND-END [0..n] USER DONE */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "time-util.h" #include "master-service.h" #include "mail-host.h" #include "director.h" #include "director-host.h" #include "director-request.h" #include "director-connection.h" #include #define MAX_INBUF_SIZE 1024 #define MAX_OUTBUF_SIZE (1024*1024*10) #define OUTBUF_FLUSH_THRESHOLD (1024*128) /* Max time to wait for connect() to finish before aborting */ #define DIRECTOR_CONNECTION_CONNECT_TIMEOUT_MSECS (10*1000) /* Max idling time before "ME" command must have been received, or we'll disconnect. */ #define DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS (10*1000) /* Max time to wait for USERs in handshake to be sent. With a lot of users the kernel may quickly eat up everything we send, while the receiver is busy parsing the data. */ #define DIRECTOR_CONNECTION_SEND_USERS_TIMEOUT_MSECS (30*1000) /* Max idling time before "DONE" command must have been received, or we'll disconnect. Use a slightly larger value than for _SEND_USERS_ so that we'll get a better error if the sender decides to disconnect. */ #define DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS (40*1000) /* How long to wait for PONG for an idling connection */ #define DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS (10*1000) /* Maximum time to wait for PONG reply */ #define DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS (60*1000) /* How long to wait to send PING when connection is idle */ #define DIRECTOR_CONNECTION_PING_INTERVAL_MSECS (15*1000) /* How long to wait before sending PING while waiting for SYNC reply */ #define DIRECTOR_CONNECTION_PING_SYNC_INTERVAL_MSECS 1000 /* If outgoing director connection exists for less than this many seconds, mark the host as failed so we won't try to reconnect to it immediately */ #define DIRECTOR_SUCCESS_MIN_CONNECT_SECS 40 #define DIRECTOR_RECONNECT_AFTER_WRONG_CONNECT_MSECS 1000 #define DIRECTOR_WAIT_DISCONNECT_SECS 10 #define DIRECTOR_HANDSHAKE_WARN_SECS 29 #define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30) #define DIRECTOR_MAX_SYNC_SEQ_DUPLICATES 4 /* If we receive SYNCs with a timestamp this many seconds higher than the last valid received SYNC timestamp, assume that we lost the director's restart notification and reset the last_sync_seq */ #define DIRECTOR_SYNC_STALE_TIMESTAMP_RESET_SECS (60*2) #define DIRECTOR_MAX_CLOCK_DIFF_WARN_SECS 1 #if DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS # error DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS is too low #endif #if DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS # error DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS is too low #endif #define CMD_IS_USER_HANDHAKE(args) \ (str_array_length(args) > 2) #define DIRECTOR_OPT_CONSISTENT_HASHING "consistent-hashing" struct director_connection { int refcount; struct director *dir; char *name; struct timeval created, connected_time, me_received_time; unsigned int minor_version; struct timeval last_input, last_output; /* for incoming connections the director host isn't known until ME-line is received */ struct director_host *host; /* this is set only for wrong connections: */ struct director_host *connect_request_to; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_disconnect, *to_ping, *to_pong; struct director_user_iter *user_iter; /* set during command execution */ const char *cur_cmd, *cur_line; unsigned int in:1; unsigned int connected:1; unsigned int version_received:1; unsigned int me_received:1; unsigned int handshake_received:1; unsigned int ignore_host_events:1; unsigned int handshake_sending_hosts:1; unsigned int ping_waiting:1; unsigned int synced:1; unsigned int wrong_host:1; unsigned int verifying_left:1; unsigned int users_unsorted:1; }; static bool director_connection_unref(struct director_connection *conn); static void director_finish_sending_handshake(struct director_connection *conn); static void director_connection_disconnected(struct director_connection **conn, const char *reason); static void director_connection_reconnect(struct director_connection **conn, const char *reason); static void director_connection_log_disconnect(struct director_connection *conn, int err, const char *errstr); static int director_connection_send_done(struct director_connection *conn); static void ATTR_FORMAT(2, 3) director_cmd_error(struct director_connection *conn, const char *fmt, ...) { va_list args; va_start(args, fmt); i_error("director(%s): Command %s: %s (input: %s)", conn->name, conn->cur_cmd, t_strdup_vprintf(fmt, args), conn->cur_line); va_end(args); if (conn->host != NULL) conn->host->last_protocol_failure = ioloop_time; } static void director_connection_append_stats(struct director_connection *conn, string_t *str) { int input_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->last_input); int output_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->last_output); int connected_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_time); str_printfa(str, "bytes in=%"PRIuUOFF_T", bytes out=%"PRIuUOFF_T, conn->input->v_offset, conn->output->offset); if (conn->last_input.tv_sec > 0) { str_printfa(str, ", last input %u.%03u s ago", input_msecs/1000, input_msecs%1000); } if (conn->last_output.tv_sec > 0) { str_printfa(str, ", last output %u.%03u s ago", output_msecs/1000, output_msecs%1000); } if (conn->connected) { str_printfa(str, ", connected %u.%03u s ago", connected_msecs/1000, connected_msecs%1000); } if (o_stream_get_buffer_used_size(conn->output) > 0) { str_printfa(str, ", %"PRIuSIZE_T" bytes in output buffer", o_stream_get_buffer_used_size(conn->output)); } } static void director_connection_init_timeout(struct director_connection *conn) { struct timeval start_time; string_t *reason = t_str_new(128); if (!conn->connected) { start_time = conn->created; str_append(reason, "Connect timed out"); } else if (!conn->me_received) { start_time = conn->connected_time; str_append(reason, "Handshaking ME timed out"); } else if (!conn->in) { start_time = conn->me_received_time; str_append(reason, "Sending handshake timed out"); } else { start_time = conn->me_received_time; str_append(reason, "Handshaking DONE timed out"); } int msecs = timeval_diff_msecs(&ioloop_timeval, &start_time); str_printfa(reason, " (%u.%03u secs, ", msecs/1000, msecs%1000); director_connection_append_stats(conn, reason); str_append_c(reason, ')'); i_error("director(%s): %s", conn->name, str_c(reason)); director_connection_disconnected(&conn, "Handshake timeout"); } static void director_connection_set_ping_timeout(struct director_connection *conn) { unsigned int msecs; msecs = conn->synced || !conn->handshake_received ? DIRECTOR_CONNECTION_PING_INTERVAL_MSECS : DIRECTOR_CONNECTION_PING_SYNC_INTERVAL_MSECS; timeout_remove(&conn->to_ping); conn->to_ping = timeout_add(msecs, director_connection_ping, conn); } static void director_connection_wait_timeout(struct director_connection *conn) { director_connection_log_disconnect(conn, ETIMEDOUT, ""); director_connection_deinit(&conn, "Timeout waiting for disconnect after CONNECT"); } static void director_connection_send_connect(struct director_connection *conn, struct director_host *host) { const char *connect_str; if (conn->to_disconnect != NULL) return; connect_str = t_strdup_printf("CONNECT\t%s\t%u\n", net_ip2addr(&host->ip), host->port); director_connection_send(conn, connect_str); o_stream_uncork(conn->output); /* wait for a while for the remote to disconnect, so it will hopefully see our CONNECT command. we'll also log the warning later to avoid multiple log lines about it. */ conn->connect_request_to = host; director_host_ref(conn->connect_request_to); conn->to_disconnect = timeout_add(DIRECTOR_WAIT_DISCONNECT_SECS*1000, director_connection_wait_timeout, conn); } static void director_connection_assigned(struct director_connection *conn) { struct director *dir = conn->dir; if (dir->left != NULL && dir->right != NULL) { /* we're connected to both directors. see if the ring is finished by sending a SYNC. if we get it back, it's done. */ dir->sync_seq++; director_set_ring_unsynced(dir); director_sync_send(dir, dir->self_host, dir->sync_seq, DIRECTOR_VERSION_MINOR, ioloop_time, mail_hosts_hash(dir->mail_hosts)); } director_connection_set_ping_timeout(conn); } static bool director_connection_assign_left(struct director_connection *conn) { struct director *dir = conn->dir; i_assert(conn->in); i_assert(dir->left != conn); /* make sure this is the correct incoming connection */ if (conn->host->self) { i_error("Connection from self, dropping"); return FALSE; } else if (dir->left == NULL) { /* no conflicts yet */ } else if (dir->left->host == conn->host) { i_warning("Replacing left director connection %s with %s", dir->left->host->name, conn->host->name); director_connection_deinit(&dir->left, t_strdup_printf( "Replacing with %s", conn->host->name)); } else if (dir->left->verifying_left) { /* we're waiting to verify if our current left is still working. if we don't receive a PONG, the current left gets disconnected and a new left gets assigned. if we do receive a PONG, we'll wait until the current left disconnects us and then reassign the new left. */ return TRUE; } else if (director_host_cmp_to_self(dir->left->host, conn->host, dir->self_host) < 0) { /* the old connection is the correct one. refer the client there (FIXME: do we ever get here?) */ director_connection_send_connect(conn, dir->left->host); return TRUE; } else { /* this new connection is the correct one, but wait until the old connection gets disconnected before using this one. that guarantees that the director inserting itself into the ring has finished handshaking its left side, so the switch will be fast. */ return TRUE; } dir->left = conn; i_free(conn->name); conn->name = i_strdup_printf("%s/left", conn->host->name); director_connection_assigned(conn); return TRUE; } static void director_assign_left(struct director *dir) { struct director_connection *conn, *const *connp; array_foreach(&dir->connections, connp) { conn = *connp; if (conn->in && conn->handshake_received && conn->to_disconnect == NULL && conn != dir->left) { /* either use this or disconnect it */ if (!director_connection_assign_left(conn)) { /* we don't want this */ director_connection_deinit(&conn, "Unwanted incoming connection"); director_assign_left(dir); break; } } } } static bool director_has_outgoing_connections(struct director *dir) { struct director_connection *const *connp; array_foreach(&dir->connections, connp) { if (!(*connp)->in && (*connp)->to_disconnect == NULL) return TRUE; } return FALSE; } static void director_send_delayed_syncs(struct director *dir) { struct director_host *const *hostp; i_assert(dir->right != NULL); dir_debug("director(%s): Sending delayed SYNCs", dir->right->name); array_foreach(&dir->dir_hosts, hostp) { if ((*hostp)->delayed_sync_seq == 0) continue; director_sync_send(dir, *hostp, (*hostp)->delayed_sync_seq, (*hostp)->delayed_sync_minor_version, (*hostp)->delayed_sync_timestamp, (*hostp)->delayed_sync_hosts_hash); (*hostp)->delayed_sync_seq = 0; } } static bool director_connection_assign_right(struct director_connection *conn) { struct director *dir = conn->dir; i_assert(!conn->in); if (dir->right != NULL) { /* see if we should disconnect or keep the existing connection. */ if (director_host_cmp_to_self(conn->host, dir->right->host, dir->self_host) <= 0) { /* the old connection is the correct one */ i_warning("Aborting incorrect outgoing connection to %s " "(already connected to correct one: %s)", conn->host->name, dir->right->host->name); conn->wrong_host = TRUE; return FALSE; } i_warning("Replacing right director connection %s with %s", dir->right->host->name, conn->host->name); director_connection_deinit(&dir->right, t_strdup_printf( "Replacing with %s", conn->host->name)); } dir->right = conn; i_free(conn->name); conn->name = i_strdup_printf("%s/right", conn->host->name); director_connection_assigned(conn); director_send_delayed_syncs(dir); return TRUE; } static bool director_args_parse_ip_port(struct director_connection *conn, const char *const *args, struct ip_addr *ip_r, in_port_t *port_r) { if (args[0] == NULL || args[1] == NULL) { director_cmd_error(conn, "Missing IP+port parameters"); return FALSE; } if (net_addr2ip(args[0], ip_r) < 0) { director_cmd_error(conn, "Invalid IP address: %s", args[0]); return FALSE; } if (net_str2port(args[1], port_r) < 0) { director_cmd_error(conn, "Invalid port: %s", args[1]); return FALSE; } return TRUE; } static bool director_cmd_me(struct director_connection *conn, const char *const *args) { struct director *dir = conn->dir; const char *connect_str; struct ip_addr ip; in_port_t port; time_t next_comm_attempt; if (!director_args_parse_ip_port(conn, args, &ip, &port)) return FALSE; if (conn->me_received) { director_cmd_error(conn, "Duplicate ME"); return FALSE; } if (!conn->in && (!net_ip_compare(&conn->host->ip, &ip) || conn->host->port != port)) { i_error("Remote director thinks it's someone else " "(connected to %s:%u, remote says it's %s:%u)", net_ip2addr(&conn->host->ip), conn->host->port, net_ip2addr(&ip), port); return FALSE; } conn->me_received = TRUE; conn->me_received_time = ioloop_timeval; if (args[2] != NULL) { time_t remote_time; int diff; if (str_to_time(args[2], &remote_time) < 0) { director_cmd_error(conn, "Invalid ME timestamp"); return FALSE; } diff = ioloop_time - remote_time; if (diff > DIRECTOR_MAX_CLOCK_DIFF_WARN_SECS || (diff < 0 && -diff > DIRECTOR_MAX_CLOCK_DIFF_WARN_SECS)) { i_warning("Director %s clock differs from ours by %d secs", conn->name, diff); } } timeout_remove(&conn->to_ping); if (conn->in) { conn->to_ping = timeout_add(DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS, director_connection_init_timeout, conn); } else { conn->to_ping = timeout_add(DIRECTOR_CONNECTION_SEND_USERS_TIMEOUT_MSECS, director_connection_init_timeout, conn); } if (!conn->in) return TRUE; /* Incoming connection: a) we don't have an established ring yet. make sure we're connecting to our right side (which might become our left side). b) it's our current "left" connection. the previous connection is most likely dead. c) we have an existing ring. tell our current "left" to connect to it with CONNECT command. d) the incoming connection doesn't belong to us at all, refer it elsewhere with CONNECT. however, before disconnecting it verify first that our left side is actually still functional. */ i_assert(conn->host == NULL); conn->host = director_host_get(dir, &ip, port); /* the host shouldn't be removed at this point, but if for some reason it is we don't want to crash */ conn->host->removed = FALSE; director_host_ref(conn->host); /* make sure we don't keep old sequence values across restarts */ director_host_restarted(conn->host); next_comm_attempt = conn->host->last_protocol_failure + DIRECTOR_PROTOCOL_FAILURE_RETRY_SECS; if (next_comm_attempt > ioloop_time) { /* the director recently sent invalid protocol data, don't try retrying yet */ i_error("director(%s): Remote sent invalid protocol data recently, " "waiting %u secs before allowing further communication", conn->name, (unsigned int)(next_comm_attempt-ioloop_time)); return FALSE; } else if (dir->left == NULL) { /* a) - just in case the left is also our right side reset its failed state, so we can connect to it */ conn->host->last_network_failure = 0; if (!director_has_outgoing_connections(dir)) director_connect(dir, "Connecting to left"); } else if (dir->left->host == conn->host) { /* b) */ i_assert(dir->left != conn); director_connection_deinit(&dir->left, "Replacing with new incoming connection"); } else if (director_host_cmp_to_self(conn->host, dir->left->host, dir->self_host) < 0) { /* c) */ connect_str = t_strdup_printf("CONNECT\t%s\t%u\n", net_ip2addr(&conn->host->ip), conn->host->port); director_connection_send(dir->left, connect_str); } else { /* d) */ dir->left->verifying_left = TRUE; director_connection_ping(dir->left); } return TRUE; } static bool director_user_refresh(struct director_connection *conn, unsigned int username_hash, struct mail_host *host, time_t timestamp, bool weak, bool *forced_r, struct user **user_r) { struct director *dir = conn->dir; struct user *user; bool ret = FALSE, unset_weak_user = FALSE; struct user_directory *users = host->tag->users; *forced_r = FALSE; user = user_directory_lookup(users, username_hash); if (user == NULL) { *user_r = user_directory_add(users, username_hash, host, timestamp); (*user_r)->weak = weak; dir_debug("user refresh: %u added", username_hash); return TRUE; } if (user->weak) { if (!weak) { /* removing user's weakness */ dir_debug("user refresh: %u weakness removed", username_hash); unset_weak_user = TRUE; user->weak = FALSE; ret = TRUE; } else { /* weak user marked again as weak */ } } else if (weak && !user_directory_user_is_recently_updated(users, user)) { /* mark the user as weak */ dir_debug("user refresh: %u set weak", username_hash); user->weak = TRUE; ret = TRUE; } else if (weak) { dir_debug("user refresh: %u weak update to %s ignored, " "we recently changed it to %s", username_hash, net_ip2addr(&host->ip), net_ip2addr(&user->host->ip)); host = user->host; ret = TRUE; } else if (user->host == host) { /* update to the same host */ } else if (user_directory_user_is_near_expiring(users, user)) { /* host conflict for a user that is already near expiring. we can assume that the other director had already dropped this user and we should have as well. use the new host. */ dir_debug("user refresh: %u is nearly expired, " "replacing host %s with %s", username_hash, net_ip2addr(&user->host->ip), net_ip2addr(&host->ip)); ret = TRUE; } else if (USER_IS_BEING_KILLED(user)) { /* user is still being moved - ignore conflicting host updates from other directors who don't yet know about the move. */ dir_debug("user refresh: %u is being moved, " "preserve its host %s instead of replacing with %s", username_hash, net_ip2addr(&user->host->ip), net_ip2addr(&host->ip)); host = user->host; } else { /* non-weak user received a non-weak update with conflicting host. this shouldn't happen. */ string_t *str = t_str_new(128); str_printfa(str, "User hash %u " "is being redirected to two hosts: %s and %s", username_hash, net_ip2addr(&user->host->ip), net_ip2addr(&host->ip)); str_printfa(str, " (old_ts=%ld", (long)user->timestamp); if (!conn->handshake_received) { str_printfa(str, ",handshaking,recv_ts=%ld", (long)timestamp); } if (USER_IS_BEING_KILLED(user)) { if (user->kill_ctx->to_move != NULL) str_append(str, ",moving"); str_printfa(str, ",kill_state=%s", user_kill_state_names[user->kill_ctx->kill_state]); } str_append_c(str, ')'); i_error("%s", str_c(str)); /* we want all the directors to redirect the user to same server, but we don't want two directors fighting over which server it belongs to, so always use the lower IP address */ if (net_ip_cmp(&user->host->ip, &host->ip) > 0) { /* change the host. we'll also need to remove the user from the old host's user_count, because we can't keep track of the user for more than one host. send the updated USER back to the sender as well. */ *forced_r = TRUE; } else { /* keep the host */ host = user->host; } /* especially IMAP connections can take a long time to die. make sure we kill off the connections in the wrong backends. */ director_kick_user_hash(dir, dir->self_host, NULL, username_hash, &host->ip); ret = TRUE; } if (user->host != host) { user->host->user_count--; user->host = host; user->host->user_count++; ret = TRUE; } if (timestamp == ioloop_time && (time_t)user->timestamp != timestamp) { user_directory_refresh(users, user); ret = TRUE; } dir_debug("user refresh: %u refreshed timeout to %ld", username_hash, (long)user->timestamp); if (unset_weak_user) { /* user is no longer weak. handle pending requests for this user if there are any */ director_set_state_changed(conn->dir); } *user_r = user; return ret; } static bool director_handshake_cmd_user(struct director_connection *conn, const char *const *args) { unsigned int username_hash, timestamp; struct ip_addr ip; struct mail_host *host; struct user *user; bool weak, forced; if (str_array_length(args) < 3 || str_to_uint(args[0], &username_hash) < 0 || net_addr2ip(args[1], &ip) < 0 || str_to_uint(args[2], ×tamp) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } weak = args[3] != NULL && args[3][0] == 'w'; host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { i_error("director(%s): USER used unknown host %s in handshake", conn->name, args[1]); return FALSE; } (void)director_user_refresh(conn, username_hash, host, timestamp, weak, &forced, &user); if (user->timestamp < timestamp) { conn->users_unsorted = TRUE; user->timestamp = timestamp; } return TRUE; } static bool director_cmd_user(struct director_connection *conn, const char *const *args) { unsigned int username_hash; struct ip_addr ip; struct mail_host *host; struct user *user; bool forced; /* NOTE: if more parameters are added, update also CMD_IS_USER_HANDHAKE() macro */ if (str_array_length(args) != 2 || str_to_uint(args[0], &username_hash) < 0 || net_addr2ip(args[1], &ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { /* we probably just removed this host. */ return TRUE; } if (director_user_refresh(conn, username_hash, host, ioloop_time, FALSE, &forced, &user)) { struct director_host *src_host = forced ? conn->dir->self_host : conn->host; i_assert(!user->weak); director_update_user(conn->dir, src_host, user); } return TRUE; } static bool director_cmd_director(struct director_connection *conn, const char *const *args) { struct director_host *host; struct ip_addr ip; in_port_t port; bool log_add = FALSE; if (!director_args_parse_ip_port(conn, args, &ip, &port)) return FALSE; host = director_host_lookup(conn->dir, &ip, port); if (host != NULL) { if (host == conn->dir->self_host) { /* ignore updates to ourself */ return TRUE; } if (host->removed) { /* ignore re-adds of removed directors */ return TRUE; } /* already have this. just reset its last_network_failure timestamp, since it might be up now, but only if this isn't part of the handshake. (if it was, reseting the timestamp could cause us to rapidly keep trying to connect to it) */ if (conn->handshake_received) host->last_network_failure = 0; /* it also may have been restarted, reset its state */ director_host_restarted(host); } else { /* save the director and forward it */ host = director_host_add(conn->dir, &ip, port); log_add = TRUE; } /* just forward this to the entire ring until it reaches back to itself. some hosts may see this twice, but that's the only way to guarantee that it gets seen by everyone. reseting the host multiple times may cause us to handle its commands multiple times, but the commands can handle that. however, we need to also handle a situation where the added director never comes back - we don't want to send the director information in a loop forever. */ if (conn->dir->right != NULL && director_host_cmp_to_self(host, conn->dir->right->host, conn->dir->self_host) > 0) { dir_debug("Received DIRECTOR update for a host where we should be connected to. " "Not forwarding it since it's probably crashed."); } else { director_notify_ring_added(host, director_connection_get_host(conn), log_add); } return TRUE; } static bool director_cmd_director_remove(struct director_connection *conn, const char *const *args) { struct director_host *host; struct ip_addr ip; in_port_t port; if (!director_args_parse_ip_port(conn, args, &ip, &port)) return FALSE; host = director_host_lookup(conn->dir, &ip, port); if (host != NULL && !host->removed) director_ring_remove(host, director_connection_get_host(conn)); return TRUE; } static bool director_cmd_host_hand_start(struct director_connection *conn, const char *const *args) { const ARRAY_TYPE(mail_host) *hosts; struct mail_host *const *hostp; unsigned int remote_ring_completed; if (args[0] == NULL || str_to_uint(args[0], &remote_ring_completed) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (remote_ring_completed && !conn->dir->ring_handshaked) { /* clear everything we have and use only what remote sends us */ dir_debug("%s: We're joining a ring - replace all hosts", conn->name); hosts = mail_hosts_get(conn->dir->mail_hosts); while (array_count(hosts) > 0) { hostp = array_idx(hosts, 0); director_remove_host(conn->dir, NULL, NULL, *hostp); } } else if (!remote_ring_completed && conn->dir->ring_handshaked) { /* ignore whatever remote sends */ dir_debug("%s: Remote is joining our ring - " "ignore all remote HOSTs", conn->name); conn->ignore_host_events = TRUE; } else { dir_debug("%s: Merge rings' hosts", conn->name); } conn->handshake_sending_hosts = TRUE; return TRUE; } static int director_cmd_is_seen_full(struct director_connection *conn, const char *const **_args, unsigned int *seq_r, struct director_host **host_r) { const char *const *args = *_args; struct ip_addr ip; in_port_t port; unsigned int seq; struct director_host *host; if (str_array_length(args) < 3 || net_addr2ip(args[0], &ip) < 0 || net_str2port(args[1], &port) < 0 || str_to_uint(args[2], &seq) < 0) { director_cmd_error(conn, "Invalid parameters"); return -1; } *_args = args + 3; *seq_r = seq; host = director_host_lookup(conn->dir, &ip, port); if (host == NULL || host->removed) { /* director is already gone, but we can't be sure if this command was sent everywhere. re-send it as if it was from ourself. */ *host_r = NULL; } else { *host_r = host; if (seq <= host->last_seq) { /* already seen this */ return 1; } host->last_seq = seq; } return 0; } static int director_cmd_is_seen(struct director_connection *conn, const char *const **_args, struct director_host **host_r) { unsigned int seq; return director_cmd_is_seen_full(conn, _args, &seq, host_r); } static bool director_cmd_user_weak(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; struct ip_addr ip; unsigned int username_hash; struct mail_host *host; struct user *user; struct director_host *src_host = conn->host; bool weak = TRUE, weak_forward = FALSE, forced; int ret; /* note that unlike other commands we don't want to just ignore duplicate commands */ if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) < 0) return FALSE; if (str_array_length(args) != 2 || str_to_uint(args[0], &username_hash) < 0 || net_addr2ip(args[1], &ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { /* we probably just removed this host. */ return TRUE; } if (ret == 0) { /* First time we're seeing this - forward it to others also. We'll want to do it even if the user was already marked as weak, because otherwise if two directors mark the user weak at the same time both the USER-WEAK notifications reach only half the directors until they collide and neither one finishes going through the whole ring marking the user non-weak. */ weak_forward = TRUE; } else if (dir_host == conn->dir->self_host) { /* We originated this USER-WEAK request. The entire ring has seen it and there weren't any conflicts. Make the user non-weak. */ dir_debug("user refresh: %u Our USER-WEAK seen by the entire ring", username_hash); src_host = conn->dir->self_host; weak = FALSE; } else { /* The original USER-WEAK sender will send a new non-weak USER update saying what really happened. We'll still need to forward this around the ring to the origin so it also knows it has travelled through the ring. */ dir_debug("user refresh: %u Remote USER-WEAK from %s seen by the entire ring, ignoring", username_hash, net_ip2addr(&dir_host->ip)); weak_forward = TRUE; } if (director_user_refresh(conn, username_hash, host, ioloop_time, weak, &forced, &user) || weak_forward) { if (forced) src_host = conn->dir->self_host; if (!user->weak) director_update_user(conn->dir, src_host, user); else { director_update_user_weak(conn->dir, src_host, conn, dir_host, user); } } return TRUE; } static bool ATTR_NULL(3) director_cmd_host_int(struct director_connection *conn, const char *const *args, struct director_host *dir_host) { struct director_host *src_host = conn->host; struct mail_host *host; struct ip_addr ip; const char *tag = "", *host_tag, *hostname = NULL; unsigned int arg_count, vhost_count; bool update, down = FALSE; time_t last_updown_change = 0; arg_count = str_array_length(args); if (arg_count < 2 || net_addr2ip(args[0], &ip) < 0 || str_to_uint(args[1], &vhost_count) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (arg_count >= 3) tag = args[2]; if (arg_count >= 4) { if ((args[3][0] != 'D' && args[3][0] != 'U') || str_to_time(args[3]+1, &last_updown_change) < 0) { director_cmd_error(conn, "Invalid updown parameters"); return FALSE; } down = args[3][0] == 'D'; } if (arg_count >= 5) hostname = args[4]; if (conn->ignore_host_events) { /* remote is sending hosts in a handshake, but it doesn't have a completed ring and we do. */ i_assert(conn->handshake_sending_hosts); return TRUE; } if (tag[0] != '\0' && conn->minor_version < DIRECTOR_VERSION_TAGS_V2) { director_cmd_error(conn, "Received a host tag from older director version with incompatible tagging support"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { host = mail_host_add_hostname(conn->dir->mail_hosts, hostname, &ip, tag); update = TRUE; } else { update = host->vhost_count != vhost_count || host->down != down; host_tag = mail_host_get_tag(host); if (strcmp(tag, host_tag) != 0) { i_error("director(%s): Host %s changed tag from '%s' to '%s'", conn->name, net_ip2addr(&host->ip), host_tag, tag); mail_host_set_tag(host, tag); update = TRUE; } if (update && host->desynced) { string_t *str = t_str_new(128); str_printfa(str, "director(%s): Host %s is being updated before previous update had finished (", conn->name, net_ip2addr(&host->ip)); if (host->down != down && host->last_updown_change > last_updown_change) { /* our host has a newer change. preserve it. */ down = host->down; } if (host->down != down) { if (host->down) str_append(str, "down -> up"); else str_append(str, "up -> down"); } if (host->vhost_count != vhost_count) { if (host->down != down) str_append(str, ", "); str_printfa(str, "vhosts %u -> %u", host->vhost_count, vhost_count); } str_append(str, ") - "); vhost_count = I_MIN(vhost_count, host->vhost_count); last_updown_change = I_MAX(last_updown_change, host->last_updown_change); str_printfa(str, "setting to state=%s vhosts=%u", down ? "down" : "up", vhost_count); i_warning("%s", str_c(str)); /* make the change appear to come from us, so it reaches the full ring */ dir_host = NULL; src_host = conn->dir->self_host; } } if (update) { const char *log_prefix = t_strdup_printf("director(%s): ", conn->name); mail_host_set_down(host, down, last_updown_change, log_prefix); mail_host_set_vhost_count(host, vhost_count, log_prefix); director_update_host(conn->dir, src_host, dir_host, host); } else { dir_debug("Ignoring host %s update vhost_count=%u " "down=%d last_updown_change=%ld (hosts_hash=%u)", net_ip2addr(&ip), vhost_count, down, (long)last_updown_change, mail_hosts_hash(conn->dir->mail_hosts)); } return TRUE; } static bool director_cmd_host_handshake(struct director_connection *conn, const char *const *args) { return director_cmd_host_int(conn, args, NULL); } static bool director_cmd_host(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; return director_cmd_host_int(conn, args, dir_host); } static bool director_cmd_host_remove(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; struct mail_host *host; struct ip_addr ip; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 1 || net_addr2ip(args[0], &ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host != NULL) director_remove_host(conn->dir, conn->host, dir_host, host); return TRUE; } static bool director_cmd_host_flush(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; struct mail_host *host; struct ip_addr ip; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 1 || net_addr2ip(args[0], &ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host != NULL) director_flush_host(conn->dir, conn->host, dir_host, host); return TRUE; } static bool director_cmd_user_move(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; struct mail_host *host; struct ip_addr ip; unsigned int username_hash; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 2 || str_to_uint(args[0], &username_hash) < 0 || net_addr2ip(args[1], &ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host != NULL) { director_move_user(conn->dir, conn->host, dir_host, username_hash, host); } return TRUE; } static bool director_cmd_user_kick(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 1) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } director_kick_user(conn->dir, conn->host, dir_host, args[0]); return TRUE; } static bool director_cmd_user_kick_alt(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 2) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } director_kick_user_alt(conn->dir, conn->host, dir_host, args[0], args[1]); return TRUE; } static bool director_cmd_user_kick_hash(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; unsigned int username_hash; struct ip_addr except_ip; int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) return ret > 0; if (str_array_length(args) != 2 || str_to_uint(args[0], &username_hash) < 0 || net_addr2ip(args[1], &except_ip) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } director_kick_user_hash(conn->dir, conn->host, dir_host, username_hash, &except_ip); return TRUE; } static bool director_cmd_user_killed(struct director_connection *conn, const char *const *args) { unsigned int username_hash; if (str_array_length(args) != 1 || str_to_uint(args[0], &username_hash) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } director_user_killed(conn->dir, username_hash); return TRUE; } static bool director_cmd_user_killed_everywhere(struct director_connection *conn, const char *const *args) { struct director_host *dir_host; unsigned int seq, username_hash; int ret; if ((ret = director_cmd_is_seen_full(conn, &args, &seq, &dir_host)) < 0) return FALSE; if (str_array_length(args) != 1 || str_to_uint(args[0], &username_hash) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (ret > 0) { i_assert(dir_host != NULL); dir_debug("User %u - ignoring already seen USER-KILLED-EVERYWHERE " "with seq=%u <= %s.last_seq=%u", username_hash, seq, dir_host->name, dir_host->last_seq); return TRUE; } director_user_killed_everywhere(conn->dir, conn->host, dir_host, username_hash); return TRUE; } static bool director_handshake_cmd_done(struct director_connection *conn) { struct director *dir = conn->dir; int handshake_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_time); string_t *str; if (conn->users_unsorted && conn->user_iter == NULL) { /* we sent our user list before receiving remote's */ conn->users_unsorted = FALSE; mail_hosts_sort_users(conn->dir->mail_hosts); } str = t_str_new(128); str_printfa(str, "director(%s): Handshake finished in %u.%03u secs (", conn->name, handshake_msecs/1000, handshake_msecs%1000); director_connection_append_stats(conn, str); str_append_c(str, ')'); if (handshake_msecs >= DIRECTOR_HANDSHAKE_WARN_SECS*1000) i_warning("%s", str_c(str)); else i_info("%s", str_c(str)); /* the host is up now, make sure we can connect to it immediately if needed */ conn->host->last_network_failure = 0; conn->handshake_received = TRUE; if (conn->in) { /* handshaked to left side. tell it we've received the whole handshake. */ director_connection_send(conn, "DONE\n"); /* tell the "right" director about the "left" one */ director_update_send(dir, director_connection_get_host(conn), t_strdup_printf("DIRECTOR\t%s\t%u\n", net_ip2addr(&conn->host->ip), conn->host->port)); /* this is our "left" side. */ return director_connection_assign_left(conn); } else { /* handshaked to "right" side. */ return director_connection_assign_right(conn); } } static int director_handshake_cmd_options(struct director_connection *conn, const char *const *args) { bool consistent_hashing = FALSE; unsigned int i; for (i = 0; args[i] != NULL; i++) { if (strcmp(args[i], DIRECTOR_OPT_CONSISTENT_HASHING) == 0) consistent_hashing = TRUE; } if (consistent_hashing != conn->dir->set->director_consistent_hashing) { i_error("director(%s): director_consistent_hashing settings differ between directors", conn->name); return -1; } return 1; } static int director_connection_handle_handshake(struct director_connection *conn, const char *cmd, const char *const *args) { unsigned int major_version; /* both incoming and outgoing connections get VERSION and ME */ if (strcmp(cmd, "VERSION") == 0 && str_array_length(args) >= 3) { if (strcmp(args[0], DIRECTOR_VERSION_NAME) != 0) { i_error("director(%s): Wrong protocol in socket " "(%s vs %s)", conn->name, args[0], DIRECTOR_VERSION_NAME); return -1; } else if (str_to_uint(args[1], &major_version) < 0 || str_to_uint(args[2], &conn->minor_version) < 0) { i_error("director(%s): Invalid protocol version: " "%s.%s", conn->name, args[1], args[2]); return -1; } else if (major_version != DIRECTOR_VERSION_MAJOR) { i_error("director(%s): Incompatible protocol version: " "%u vs %u", conn->name, major_version, DIRECTOR_VERSION_MAJOR); return -1; } if (conn->minor_version < DIRECTOR_VERSION_TAGS_V2 && mail_hosts_have_tags(conn->dir->mail_hosts)) { i_error("director(%s): Director version supports incompatible tags", conn->name); return -1; } conn->version_received = TRUE; director_finish_sending_handshake(conn); return 1; } if (!conn->version_received) { director_cmd_error(conn, "Incompatible protocol"); return -1; } if (strcmp(cmd, "ME") == 0) return director_cmd_me(conn, args) ? 1 : -1; if (!conn->me_received) { director_cmd_error(conn, "Expecting ME command first"); return -1; } /* incoming connections get a HOST list */ if (conn->handshake_sending_hosts) { if (strcmp(cmd, "HOST") == 0) return director_cmd_host_handshake(conn, args) ? 1 : -1; if (strcmp(cmd, "HOST-HAND-END") == 0) { conn->ignore_host_events = FALSE; conn->handshake_sending_hosts = FALSE; return 1; } director_cmd_error(conn, "Unexpected command during host list"); return -1; } if (strcmp(cmd, "OPTIONS") == 0) return director_handshake_cmd_options(conn, args); if (strcmp(cmd, "HOST-HAND-START") == 0) { if (!conn->in) { director_cmd_error(conn, "Host list is only for incoming connections"); return -1; } return director_cmd_host_hand_start(conn, args) ? 1 : -1; } if (conn->in && strcmp(cmd, "USER") == 0 && CMD_IS_USER_HANDHAKE(args)) return director_handshake_cmd_user(conn, args) ? 1 : -1; /* both get DONE */ if (strcmp(cmd, "DONE") == 0) return director_handshake_cmd_done(conn) ? 1 : -1; return 0; } static bool director_connection_sync_host(struct director_connection *conn, struct director_host *host, uint32_t seq, unsigned int minor_version, unsigned int timestamp, unsigned int hosts_hash) { struct director *dir = conn->dir; if (minor_version > DIRECTOR_VERSION_MINOR) { /* we're not up to date */ minor_version = DIRECTOR_VERSION_MINOR; } if (host->self) { if (dir->sync_seq != seq) { /* stale SYNC event */ return FALSE; } /* sync_seq increases when we get disconnected, so we must be successfully connected to both directions */ i_assert(dir->left != NULL && dir->right != NULL); if (hosts_hash != 0 && hosts_hash != mail_hosts_hash(conn->dir->mail_hosts)) { i_error("director(%s): Hosts unexpectedly changed during SYNC reply - resending" "(seq=%u, old hosts_hash=%u, new hosts_hash=%u)", conn->name, seq, hosts_hash, mail_hosts_hash(dir->mail_hosts)); (void)director_resend_sync(dir); return FALSE; } dir->ring_min_version = minor_version; if (!dir->ring_handshaked) { /* the ring is handshaked */ director_set_ring_handshaked(dir); } else if (dir->ring_synced) { /* duplicate SYNC (which was sent just in case the previous one got lost) */ } else { dir_debug("Ring is synced (%s sent seq=%u, hosts_hash=%u)", conn->name, seq, mail_hosts_hash(dir->mail_hosts)); director_set_ring_synced(dir); } } else { if (seq < host->last_sync_seq && timestamp < host->last_sync_timestamp + DIRECTOR_SYNC_STALE_TIMESTAMP_RESET_SECS) { /* stale SYNC event */ dir_debug("Ignore stale SYNC event for %s " "(seq %u < %u, timestamp=%u)", host->name, seq, host->last_sync_seq, timestamp); return FALSE; } else if (seq < host->last_sync_seq) { i_warning("Last SYNC seq for %s appears to be stale, reseting " "(seq=%u, timestamp=%u -> seq=%u, timestamp=%u)", host->name, host->last_sync_seq, host->last_sync_timestamp, seq, timestamp); host->last_sync_seq = seq; host->last_sync_timestamp = timestamp; host->last_sync_seq_counter = 1; } else if (seq > host->last_sync_seq || timestamp > host->last_sync_timestamp) { host->last_sync_seq = seq; host->last_sync_timestamp = timestamp; host->last_sync_seq_counter = 1; dir_debug("Update SYNC for %s " "(seq=%u, timestamp=%u -> seq=%u, timestamp=%u)", host->name, host->last_sync_seq, host->last_sync_timestamp, seq, timestamp); } else if (++host->last_sync_seq_counter > DIRECTOR_MAX_SYNC_SEQ_DUPLICATES) { /* we've received this too many times already */ dir_debug("Ignore duplicate #%u SYNC event for %s " "(seq=%u, timestamp %u <= %u)", host->last_sync_seq_counter, host->name, seq, timestamp, host->last_sync_timestamp); return FALSE; } if (hosts_hash != 0 && hosts_hash != mail_hosts_hash(conn->dir->mail_hosts)) { if (host->desynced_hosts_hash != hosts_hash) { dir_debug("Ignore director %s stale SYNC request whose hosts don't match us " "(seq=%u, remote hosts_hash=%u, my hosts_hash=%u)", net_ip2addr(&host->ip), seq, hosts_hash, mail_hosts_hash(dir->mail_hosts)); host->desynced_hosts_hash = hosts_hash; return FALSE; } /* we'll get here only if we received a SYNC twice with the same wrong hosts_hash. FIXME: this gets triggered unnecessarily sometimes if hosts are changing rapidly. */ i_error("director(%s): Director %s SYNC request hosts don't match us - resending hosts " "(seq=%u, remote hosts_hash=%u, my hosts_hash=%u)", conn->name, net_ip2addr(&host->ip), seq, hosts_hash, mail_hosts_hash(dir->mail_hosts)); director_resend_hosts(dir); return FALSE; } host->desynced_hosts_hash = 0; if (dir->right != NULL) { /* forward it to the connection on right */ director_sync_send(dir, host, seq, minor_version, timestamp, hosts_hash); } else { dir_debug("director(%s): We have no right connection - " "delay replying to SYNC until finished", conn->name); host->delayed_sync_seq = seq; host->delayed_sync_minor_version = minor_version; host->delayed_sync_timestamp = timestamp; host->delayed_sync_hosts_hash = hosts_hash; } } return TRUE; } static bool director_connection_sync(struct director_connection *conn, const char *const *args) { struct director *dir = conn->dir; struct director_host *host; struct ip_addr ip; in_port_t port; unsigned int arg_count, seq, minor_version = 0, timestamp = ioloop_time; unsigned int hosts_hash = 0; arg_count = str_array_length(args); if (arg_count < 3 || !director_args_parse_ip_port(conn, args, &ip, &port) || str_to_uint(args[2], &seq) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (arg_count >= 4 && str_to_uint(args[3], &minor_version) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (arg_count >= 5 && str_to_uint(args[4], ×tamp) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } if (arg_count >= 6 && str_to_uint(args[5], &hosts_hash) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } /* find the originating director. if we don't see it, it was already removed and we can ignore this sync. */ host = director_host_lookup(dir, &ip, port); if (host != NULL) { if (!director_connection_sync_host(conn, host, seq, minor_version, timestamp, hosts_hash)) return TRUE; } /* If directors got disconnected while we were waiting a SYNC reply, it might have gotten lost. If we've received a DIRECTOR update since the last time we sent a SYNC, retry sending it here to make sure it doesn't get stuck. We don't want to do this too eagerly because it may trigger desynced_hosts_hash != hosts_hash mismatch, which causes unnecessary error logging and hosts-resending. */ if ((host == NULL || !host->self) && dir->last_sync_sent_ring_change_counter != dir->ring_change_counter && (time_t)dir->self_host->last_sync_timestamp != ioloop_time) (void)director_resend_sync(dir); return TRUE; } static void director_disconnect_timeout(struct director_connection *conn) { director_connection_deinit(&conn, "CONNECT requested"); } static void director_reconnect_after_wrong_connect_timeout(struct director_connection *conn) { struct director *dir = conn->dir; director_connection_deinit(&conn, "Wrong CONNECT requested"); if (dir->right == NULL) director_connect(dir, "Reconnecting after wrong CONNECT request"); } static void director_reconnect_after_wrong_connect(struct director_connection *conn) { if (conn->to_disconnect != NULL) return; conn->to_disconnect = timeout_add_short(DIRECTOR_RECONNECT_AFTER_WRONG_CONNECT_MSECS, director_reconnect_after_wrong_connect_timeout, conn); } static bool director_cmd_connect(struct director_connection *conn, const char *const *args) { struct director *dir = conn->dir; struct director_host *host; struct ip_addr ip; in_port_t port; const char *right_state; if (str_array_length(args) != 2 || !director_args_parse_ip_port(conn, args, &ip, &port)) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } host = director_host_get(conn->dir, &ip, port); /* remote suggests us to connect elsewhere */ if (dir->right != NULL && director_host_cmp_to_self(host, dir->right->host, dir->self_host) <= 0) { /* the old connection is the correct one */ dir_debug("Ignoring CONNECT request to %s (current right is %s)", host->name, dir->right->name); director_reconnect_after_wrong_connect(conn); return TRUE; } if (host->removed) { dir_debug("Ignoring CONNECT request to %s (director is removed)", host->name); director_reconnect_after_wrong_connect(conn); return TRUE; } /* reset failure timestamp so we'll actually try to connect there. */ host->last_network_failure = 0; /* reset removed-flag, so we don't crash */ host->removed = FALSE; if (dir->right == NULL) { right_state = "initializing right"; } else { right_state = t_strdup_printf("replacing current right %s", dir->right->name); /* disconnect from right side immediately - it's not accepting any further commands from us. */ if (conn->dir->right != conn) director_connection_deinit(&conn->dir->right, "CONNECT requested"); else if (conn->to_disconnect == NULL) { conn->to_disconnect = timeout_add_short(0, director_disconnect_timeout, conn); } } /* connect here */ (void)director_connect_host(dir, host, t_strdup_printf( "Received CONNECT request from %s - %s", conn->name, right_state)); return TRUE; } static void director_disconnect_wrong_lefts(struct director *dir) { struct director_connection *const *connp, *conn; array_foreach(&dir->connections, connp) { conn = *connp; if (conn->in && conn != dir->left && conn->me_received && conn->to_disconnect == NULL && director_host_cmp_to_self(dir->left->host, conn->host, dir->self_host) < 0) director_connection_send_connect(conn, dir->left->host); } } static bool director_cmd_pong(struct director_connection *conn) { if (!conn->ping_waiting) return TRUE; conn->ping_waiting = FALSE; timeout_remove(&conn->to_pong); if (conn->verifying_left) { conn->verifying_left = FALSE; if (conn == conn->dir->left) { /* our left side is functional. tell all the wrong incoming connections to connect to it instead. */ director_disconnect_wrong_lefts(conn->dir); } } director_connection_set_ping_timeout(conn); return TRUE; } static bool director_connection_handle_cmd(struct director_connection *conn, const char *cmd, const char *const *args) { int ret; if (!conn->handshake_received) { ret = director_connection_handle_handshake(conn, cmd, args); if (ret > 0) return TRUE; if (ret < 0) { /* invalid commands during handshake, we probably don't want to reconnect here */ return FALSE; } /* allow also other commands during handshake */ } if (strcmp(cmd, "PING") == 0) { director_connection_send(conn, "PONG\n"); return TRUE; } if (strcmp(cmd, "PONG") == 0) return director_cmd_pong(conn); if (strcmp(cmd, "USER") == 0) return director_cmd_user(conn, args); if (strcmp(cmd, "USER-WEAK") == 0) return director_cmd_user_weak(conn, args); if (strcmp(cmd, "HOST") == 0) return director_cmd_host(conn, args); if (strcmp(cmd, "HOST-REMOVE") == 0) return director_cmd_host_remove(conn, args); if (strcmp(cmd, "HOST-FLUSH") == 0) return director_cmd_host_flush(conn, args); if (strcmp(cmd, "USER-MOVE") == 0) return director_cmd_user_move(conn, args); if (strcmp(cmd, "USER-KICK") == 0) return director_cmd_user_kick(conn, args); if (strcmp(cmd, "USER-KICK-ALT") == 0) return director_cmd_user_kick_alt(conn, args); if (strcmp(cmd, "USER-KICK-HASH") == 0) return director_cmd_user_kick_hash(conn, args); if (strcmp(cmd, "USER-KILLED") == 0) return director_cmd_user_killed(conn, args); if (strcmp(cmd, "USER-KILLED-EVERYWHERE") == 0) return director_cmd_user_killed_everywhere(conn, args); if (strcmp(cmd, "DIRECTOR") == 0) return director_cmd_director(conn, args); if (strcmp(cmd, "DIRECTOR-REMOVE") == 0) return director_cmd_director_remove(conn, args); if (strcmp(cmd, "SYNC") == 0) return director_connection_sync(conn, args); if (strcmp(cmd, "CONNECT") == 0) return director_cmd_connect(conn, args); if (strcmp(cmd, "QUIT") == 0) { i_warning("Director %s disconnected us with reason: %s", conn->name, t_strarray_join(args, " ")); return FALSE; } director_cmd_error(conn, "Unknown command %s", cmd); return FALSE; } static bool director_connection_handle_line(struct director_connection *conn, const char *line) { const char *cmd, *const *args; bool ret; dir_debug("input: %s: %s", conn->name, line); args = t_strsplit_tabescaped(line); cmd = args[0]; args++; if (cmd == NULL) { director_cmd_error(conn, "Received empty line"); return FALSE; } conn->cur_cmd = cmd; conn->cur_line = line; ret = director_connection_handle_cmd(conn, cmd, args); conn->cur_cmd = NULL; conn->cur_line = NULL; return ret; } static void director_connection_log_disconnect(struct director_connection *conn, int err, const char *errstr) { string_t *str = t_str_new(128); i_assert(conn->connected); if (conn->connect_request_to != NULL) { i_warning("Director %s tried to connect to us, " "should use %s instead", conn->name, conn->connect_request_to->name); return; } str_printfa(str, "Director %s disconnected: ", conn->name); str_append(str, "Connection closed"); if (err != 0 && err != EPIPE) { errno = err; if (errstr[0] == '\0') str_printfa(str, ": %m"); else str_printfa(str, ": %s", errstr); } str_append(str, " ("); director_connection_append_stats(conn, str); if (!conn->me_received) str_append(str, ", handshake ME not received"); else if (!conn->handshake_received) str_append(str, ", handshake DONE not received"); str_append_c(str, ')'); i_error("%s", str_c(str)); } static void director_connection_input(struct director_connection *conn) { struct director *dir = conn->dir; char *line; uoff_t prev_offset; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ director_connection_log_disconnect(conn, conn->input->stream_errno, i_stream_get_error(conn->input)); director_connection_disconnected(&conn, i_stream_get_error(conn->input)); return; case -2: /* buffer full */ director_cmd_error(conn, "Director sent us more than %d bytes", MAX_INBUF_SIZE); director_connection_reconnect(&conn, "Too long input line"); return; } if (conn->to_disconnect != NULL) { /* just read everything the remote sends, and wait for it to disconnect. we mainly just want the remote to read the CONNECT we sent it. */ i_stream_skip(conn->input, i_stream_get_data_size(conn->input)); return; } conn->last_input = ioloop_timeval; conn->refcount++; director_sync_freeze(dir); prev_offset = conn->input->v_offset; while ((line = i_stream_next_line(conn->input)) != NULL) { dir->ring_traffic_input += conn->input->v_offset - prev_offset; prev_offset = conn->input->v_offset; T_BEGIN { ret = director_connection_handle_line(conn, line); } T_END; if (!ret) { if (!director_connection_unref(conn)) break; director_connection_reconnect(&conn, t_strdup_printf( "Invalid input: %s", line)); break; } } director_sync_thaw(dir); if (conn != NULL) { if (director_connection_unref(conn)) timeout_reset(conn->to_ping); } } static void director_connection_send_directors(struct director_connection *conn) { struct director_host *const *hostp; string_t *str = t_str_new(64); array_foreach(&conn->dir->dir_hosts, hostp) { if ((*hostp)->removed) continue; str_truncate(str, 0); str_printfa(str, "DIRECTOR\t%s\t%u\n", net_ip2addr(&(*hostp)->ip), (*hostp)->port); director_connection_send(conn, str_c(str)); } } static void director_connection_send_hosts(struct director_connection *conn) { struct mail_host *const *hostp; bool send_updowns; string_t *str = t_str_new(128); i_assert(conn->version_received); send_updowns = conn->minor_version >= DIRECTOR_VERSION_UPDOWN; str_printfa(str, "HOST-HAND-START\t%u\n", conn->dir->ring_handshaked); array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) { struct mail_host *host = *hostp; const char *host_tag = mail_host_get_tag(host); str_printfa(str, "HOST\t%s\t%u", net_ip2addr(&host->ip), host->vhost_count); if (host_tag[0] != '\0' || send_updowns) { str_append_c(str, '\t'); str_append_tabescaped(str, host_tag); } if (send_updowns) { str_printfa(str, "\t%c%ld\t", host->down ? 'D' : 'U', (long)host->last_updown_change); if (host->hostname != NULL) str_append_tabescaped(str, host->hostname); } str_append_c(str, '\n'); director_connection_send(conn, str_c(str)); str_truncate(str, 0); } str_printfa(str, "HOST-HAND-END\t%u\n", conn->dir->ring_handshaked); director_connection_send(conn, str_c(str)); } static int director_connection_send_done(struct director_connection *conn) { i_assert(conn->version_received); if (!conn->dir->set->director_consistent_hashing) ; else if (conn->minor_version >= DIRECTOR_VERSION_OPTIONS) { director_connection_send(conn, "OPTIONS\t"DIRECTOR_OPT_CONSISTENT_HASHING"\n"); } else { i_error("director(%s): Director version is too old for supporting director_consistent_hashing=yes", conn->name); return -1; } director_connection_send(conn, "DONE\n"); return 0; } static int director_connection_send_users(struct director_connection *conn) { struct user *user; int ret; i_assert(conn->version_received); while ((user = director_iterate_users_next(conn->user_iter)) != NULL) { T_BEGIN { string_t *str = t_str_new(128); str_printfa(str, "USER\t%u\t%s\t%u", user->username_hash, net_ip2addr(&user->host->ip), user->timestamp); if (user->weak) str_append(str, "\tw"); str_append_c(str, '\n'); director_connection_send(conn, str_c(str)); } T_END; if (o_stream_get_buffer_used_size(conn->output) >= OUTBUF_FLUSH_THRESHOLD) { if ((ret = o_stream_flush(conn->output)) <= 0) { /* continue later */ timeout_reset(conn->to_ping); return ret; } } } director_iterate_users_deinit(&conn->user_iter); if (director_connection_send_done(conn) < 0) return -1; if (conn->users_unsorted && conn->handshake_received) { /* we received remote's list of users before sending ours */ conn->users_unsorted = FALSE; mail_hosts_sort_users(conn->dir->mail_hosts); } ret = o_stream_flush(conn->output); timeout_reset(conn->to_ping); return ret; } static int director_connection_output(struct director_connection *conn) { int ret; conn->last_output = ioloop_timeval; if (conn->user_iter != NULL) { /* still handshaking USER list */ o_stream_cork(conn->output); ret = director_connection_send_users(conn); o_stream_uncork(conn->output); if (ret < 0) { director_connection_disconnected(&conn, o_stream_get_error(conn->output)); } else { o_stream_set_flush_pending(conn->output, TRUE); } return ret; } return o_stream_flush(conn->output); } static struct director_connection * director_connection_init_common(struct director *dir, int fd) { struct director_connection *conn; conn = i_new(struct director_connection, 1); conn->refcount = 1; conn->created = ioloop_timeval; conn->fd = fd; conn->dir = dir; conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); array_append(&dir->connections, &conn, 1); return conn; } static void director_connection_send_handshake(struct director_connection *conn) { director_connection_send(conn, t_strdup_printf( "VERSION\t"DIRECTOR_VERSION_NAME"\t%u\t%u\n" "ME\t%s\t%u\t%lld\n", DIRECTOR_VERSION_MAJOR, DIRECTOR_VERSION_MINOR, net_ip2addr(&conn->dir->self_ip), conn->dir->self_port, (long long)time(NULL))); } struct director_connection * director_connection_init_in(struct director *dir, int fd, const struct ip_addr *ip) { struct director_connection *conn; conn = director_connection_init_common(dir, fd); conn->in = TRUE; conn->connected = TRUE; conn->connected_time = ioloop_timeval; conn->name = i_strdup_printf("%s/in", net_ip2addr(ip)); conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn); conn->to_ping = timeout_add(DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS, director_connection_init_timeout, conn); i_info("Incoming connection from director %s", conn->name); director_connection_send_handshake(conn); return conn; } static void director_connection_connected(struct director_connection *conn) { int err; if ((err = net_geterror(conn->fd)) != 0) { i_error("director(%s): connect() failed: %s", conn->name, strerror(err)); director_connection_disconnected(&conn, strerror(err)); return; } conn->connected_time = ioloop_timeval; conn->connected = TRUE; o_stream_set_flush_callback(conn->output, director_connection_output, conn); io_remove(&conn->io); conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn); timeout_remove(&conn->to_ping); conn->to_ping = timeout_add(DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS, director_connection_init_timeout, conn); o_stream_cork(conn->output); director_connection_send_handshake(conn); director_connection_send_directors(conn); o_stream_uncork(conn->output); /* send the rest of the handshake after we've received the remote's version number */ } static void director_finish_sending_handshake(struct director_connection *conn) { if ( conn->in) { /* only outgoing connections send hosts & users */ return; } o_stream_cork(conn->output); director_connection_send_hosts(conn); i_assert(conn->user_iter == NULL); conn->user_iter = director_iterate_users_init(conn->dir); if (director_connection_send_users(conn) == 0) o_stream_set_flush_pending(conn->output, TRUE); o_stream_uncork(conn->output); } struct director_connection * director_connection_init_out(struct director *dir, int fd, struct director_host *host) { struct director_connection *conn; i_assert(!host->removed); /* make sure we don't keep old sequence values across restarts */ director_host_restarted(host); conn = director_connection_init_common(dir, fd); conn->name = i_strdup_printf("%s/out", host->name); conn->host = host; director_host_ref(host); conn->io = io_add(conn->fd, IO_WRITE, director_connection_connected, conn); conn->to_ping = timeout_add(DIRECTOR_CONNECTION_CONNECT_TIMEOUT_MSECS, director_connection_init_timeout, conn); return conn; } void director_connection_deinit(struct director_connection **_conn, const char *remote_reason) { struct director_connection *const *conns, *conn = *_conn; struct director *dir = conn->dir; unsigned int i, count; *_conn = NULL; i_assert(conn->fd != -1); if (conn->host != NULL) { dir_debug("Disconnecting from %s: %s", conn->host->name, remote_reason); } if (*remote_reason != '\0' && conn->minor_version >= DIRECTOR_VERSION_QUIT) { o_stream_send_str(conn->output, t_strdup_printf( "QUIT\t%s\n", remote_reason)); } conns = array_get(&dir->connections, &count); for (i = 0; i < count; i++) { if (conns[i] == conn) { array_delete(&dir->connections, i, 1); break; } } i_assert(i < count); if (dir->left == conn) { dir->left = NULL; /* if there is already another handshaked incoming connection, use it as the new "left" */ director_assign_left(dir); } if (dir->right == conn) dir->right = NULL; if (conn->connect_request_to != NULL) { director_host_unref(conn->connect_request_to); conn->connect_request_to = NULL; } if (conn->user_iter != NULL) director_iterate_users_deinit(&conn->user_iter); if (conn->to_disconnect != NULL) timeout_remove(&conn->to_disconnect); if (conn->to_pong != NULL) timeout_remove(&conn->to_pong); timeout_remove(&conn->to_ping); if (conn->io != NULL) io_remove(&conn->io); i_stream_close(conn->input); o_stream_close(conn->output); i_close_fd(&conn->fd); if (conn->in) master_service_client_connection_destroyed(master_service); director_connection_unref(conn); if (dir->left == NULL || dir->right == NULL) { /* we aren't synced until we're again connected to a ring */ dir->sync_seq++; director_set_ring_unsynced(dir); } } static bool director_connection_unref(struct director_connection *conn) { i_assert(conn->refcount > 0); if (--conn->refcount > 0) return TRUE; if (conn->host != NULL) director_host_unref(conn->host); i_stream_unref(&conn->input); o_stream_unref(&conn->output); i_free(conn->name); i_free(conn); return FALSE; } static void director_connection_disconnected(struct director_connection **_conn, const char *reason) { struct director_connection *conn = *_conn; struct director *dir = conn->dir; if ((conn->connected_time.tv_sec == 0 || conn->connected_time.tv_sec + DIRECTOR_SUCCESS_MIN_CONNECT_SECS > ioloop_time) && conn->host != NULL) { /* connection didn't exist for very long, assume it has a network problem */ conn->host->last_network_failure = ioloop_time; } director_connection_deinit(_conn, reason); if (dir->right == NULL) director_connect(dir, "Reconnecting after disconnection"); } static void director_connection_reconnect(struct director_connection **_conn, const char *reason) { struct director_connection *conn = *_conn; struct director *dir = conn->dir; director_connection_deinit(_conn, reason); if (dir->right == NULL) director_connect(dir, "Reconnecting after error"); } void director_connection_send(struct director_connection *conn, const char *data) { size_t len = strlen(data); off_t ret; if (conn->output->closed || !conn->connected) return; if (director_debug) T_BEGIN { const char *const *lines = t_strsplit(data, "\n"); for (; lines[1] != NULL; lines++) dir_debug("output: %s: %s", conn->name, *lines); } T_END; ret = o_stream_send(conn->output, data, len); if (ret != (off_t)len) { if (ret < 0) { i_error("director(%s): write() failed: %s", conn->name, o_stream_get_error(conn->output)); } else { i_error("director(%s): Output buffer full, " "disconnecting", conn->name); } o_stream_close(conn->output); } else { conn->dir->ring_traffic_output += len; } } static void director_connection_ping_idle_timeout(struct director_connection *conn) { string_t *str = t_str_new(128); str_printfa(str, "Ping timed out in %u secs, disconnecting (", DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS/1000); director_connection_append_stats(conn, str); if (conn->handshake_received) str_append(str, ", handshaked"); if (conn->synced) str_append(str, ", synced"); str_append_c(str, ')'); i_error("director(%s): %s", conn->name, str_c(str)); director_connection_disconnected(&conn, "Ping timeout"); } static void director_connection_pong_timeout(struct director_connection *conn) { i_error("director(%s): PONG reply not received although other " "input keeps coming, disconnecting", conn->name); director_connection_disconnected(&conn, "Pong timeout"); } void director_connection_ping(struct director_connection *conn) { if (conn->ping_waiting) return; timeout_remove(&conn->to_ping); conn->to_ping = timeout_add(DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS, director_connection_ping_idle_timeout, conn); conn->to_pong = timeout_add(DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS, director_connection_pong_timeout, conn); director_connection_send(conn, "PING\n"); conn->ping_waiting = TRUE; } const char *director_connection_get_name(struct director_connection *conn) { return conn->name; } struct director_host * director_connection_get_host(struct director_connection *conn) { return conn->host; } bool director_connection_is_handshaked(struct director_connection *conn) { return conn->handshake_received; } bool director_connection_is_synced(struct director_connection *conn) { return conn->synced; } bool director_connection_is_incoming(struct director_connection *conn) { return conn->in; } unsigned int director_connection_get_minor_version(struct director_connection *conn) { return conn->minor_version; } void director_connection_cork(struct director_connection *conn) { o_stream_cork(conn->output); } void director_connection_uncork(struct director_connection *conn) { o_stream_uncork(conn->output); } void director_connection_set_synced(struct director_connection *conn, bool synced) { if (conn->synced == synced) return; conn->synced = synced; /* switch ping timeout, unless we're already waiting for PONG */ if (conn->ping_waiting) return; director_connection_set_ping_timeout(conn); } dovecot-2.2.33.2/src/director/test-user-directory.c0000644000175000017500000000531613165463624017047 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mail-user-hash.h" #include "mail-host.h" #include "test-common.h" #define USER_DIR_TIMEOUT 1000000 unsigned int mail_user_hash(const char *username ATTR_UNUSED, const char *format ATTR_UNUSED) { return 0; } static void verify_user_directory(struct user_directory *dir, unsigned int user_count) { struct user_directory_iter *iter; struct user *user, *prev = NULL; unsigned int prev_stamp = 0, iter_count = 0; iter = user_directory_iter_init(dir); while ((user = user_directory_iter_next(iter)) != NULL) { test_assert(prev_stamp <= user->timestamp); test_assert(user->prev == prev); test_assert(prev == NULL || user->prev->next == user); iter_count++; prev = user; } test_assert(prev == NULL || prev->next == NULL); user_directory_iter_deinit(&iter); test_assert(iter_count == user_count); } static void test_user_directory_ascending(void) { const unsigned int count = 100000; struct user_directory *dir; struct mail_host *host = t_new(struct mail_host, 1); unsigned int i; test_begin("user directory ascending"); dir = user_directory_init(USER_DIR_TIMEOUT, NULL); (void)user_directory_add(dir, 1, host, ioloop_time + count+1); for (i = 0; i < count; i++) (void)user_directory_add(dir, i+2, host, ioloop_time + i); verify_user_directory(dir, count+1); user_directory_deinit(&dir); test_end(); } static void test_user_directory_descending(void) { const unsigned int count = 1000; struct user_directory *dir; struct mail_host *host = t_new(struct mail_host, 1); unsigned int i; test_begin("user directory descending"); dir = user_directory_init(USER_DIR_TIMEOUT, NULL); for (i = 0; i < count; i++) (void)user_directory_add(dir, i+1, host, ioloop_time - i); verify_user_directory(dir, count); user_directory_deinit(&dir); test_end(); } static void test_user_directory_random(void) { struct user_directory *dir; struct mail_host *host = t_new(struct mail_host, 1); time_t timestamp; unsigned int i, count = 10000 + rand()%10000; test_begin("user directory random"); dir = user_directory_init(USER_DIR_TIMEOUT, NULL); for (i = 0; i < count; i++) { if (rand() % 10 == 0) timestamp = ioloop_time; else timestamp = ioloop_time-rand()%100; (void)user_directory_add(dir, i+1, host, timestamp); } verify_user_directory(dir, count); user_directory_deinit(&dir); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_user_directory_ascending, test_user_directory_descending, test_user_directory_random, NULL }; struct ioloop *ioloop = io_loop_create(); int ret = test_run(test_functions); io_loop_destroy(&ioloop); return ret; } dovecot-2.2.33.2/src/director/director-request.h0000644000175000017500000000070713123174404016404 00000000000000#ifndef DIRECTOR_REQUEST_H #define DIRECTOR_REQUEST_H struct director; struct director_request; typedef void director_request_callback(const struct ip_addr *ip, const char *hostname, const char *errormsg, void *context); void director_request(struct director *dir, const char *username, const char *tag, director_request_callback *callback, void *context); bool director_request_continue(struct director_request *request); #endif dovecot-2.2.33.2/src/director/Makefile.am0000644000175000017500000000276713165463624015011 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = director AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-program-client director_LDADD = $(LIBDOVECOT) director_DEPENDENCIES = $(LIBDOVECOT_DEPS) director_SOURCES = \ main.c \ auth-connection.c \ director.c \ director-connection.c \ director-host.c \ director-request.c \ director-settings.c \ doveadm-connection.c \ login-connection.c \ mail-host.c \ notify-connection.c \ user-directory.c noinst_HEADERS = \ auth-connection.h \ director.h \ director-connection.h \ director-host.h \ director-request.h \ director-settings.h \ doveadm-connection.h \ login-connection.h \ mail-host.h \ notify-connection.h \ user-directory.h noinst_PROGRAMS = director-test $(test_programs) director_test_LDADD = $(LIBDOVECOT) director_test_DEPENDENCIES = $(LIBDOVECOT_DEPS) director_test_SOURCES = \ director-test.c test_programs = \ test-user-directory test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_user_directory_SOURCES = test-user-directory.c test_user_directory_LDADD = user-directory.o $(test_libs) test_user_directory_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-lda/0002755000175000017500000000000013172375612012511 500000000000000dovecot-2.2.33.2/src/lib-lda/smtp-client.c0000644000175000017500000002277313165463624015050 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "safe-mkstemp.h" #include "execv-const.h" #include "istream.h" #include "ostream.h" #include "master-service.h" #include "lmtp-client.h" #include "lda-settings.h" #include "mail-deliver.h" #include "smtp-client.h" #include #include #include #include #define DEFAULT_SUBMISSION_PORT 25 struct smtp_client { pool_t pool; struct ostream *output; int temp_fd; pid_t pid; bool use_smtp; bool success; bool finished; const struct lda_settings *set; const char *temp_path; ARRAY_TYPE(const_string) destinations; const char *return_path; const char *error; bool tempfail; }; static void ATTR_NORETURN smtp_client_run_sendmail(struct smtp_client *client, int fd) { const char *const *sendmail_args, *const *argv, *str; ARRAY_TYPE(const_string) args; unsigned int i; sendmail_args = t_strsplit(client->set->sendmail_path, " "); t_array_init(&args, 16); for (i = 0; sendmail_args[i] != NULL; i++) array_append(&args, &sendmail_args[i], 1); str = "-i"; array_append(&args, &str, 1); /* ignore dots */ str = "-f"; array_append(&args, &str, 1); str = client->return_path != NULL && *client->return_path != '\0' ? client->return_path : "<>"; array_append(&args, &str, 1); str = "--"; array_append(&args, &str, 1); array_append_array(&args, &client->destinations); array_append_zero(&args); argv = array_idx(&args, 0); if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); master_service_env_clean(); execv_const(argv[0], argv); } static int create_temp_file(const char **path_r) { string_t *path; int fd; path = t_str_new(128); str_append(path, "/tmp/dovecot."); str_append(path, master_service_get_name(master_service)); str_append_c(path, '.'); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } struct smtp_client * smtp_client_init(const struct lda_settings *set, const char *return_path) { struct smtp_client *client; pool_t pool; pool = pool_alloconly_create("smtp client", 256); client = p_new(pool, struct smtp_client, 1); client->pool = pool; client->set = set; client->return_path = p_strdup(pool, return_path); client->use_smtp = *set->submission_host != '\0'; p_array_init(&client->destinations, pool, 2); client->pid = (pid_t)-1; return client; } void smtp_client_add_rcpt(struct smtp_client *client, const char *address) { i_assert(client->output == NULL); address = p_strdup(client->pool, address); array_append(&client->destinations, &address, 1); } static struct ostream *smtp_client_send_sendmail(struct smtp_client *client) { int fd[2]; pid_t pid; if (pipe(fd) < 0) { i_error("pipe() failed: %m"); return o_stream_create_error(errno); } if ((pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); i_close_fd(&fd[0]); i_close_fd(&fd[1]); return o_stream_create_error(errno); } if (pid == 0) { /* child */ i_close_fd(&fd[1]); smtp_client_run_sendmail(client, fd[0]); } i_close_fd(&fd[0]); client->output = o_stream_create_fd_autoclose(&fd[1], IO_BLOCK_SIZE); o_stream_set_no_error_handling(client->output, TRUE); client->pid = pid; return client->output; } struct ostream *smtp_client_send(struct smtp_client *client) { const char *path; int fd; i_assert(array_count(&client->destinations) > 0); if (!client->use_smtp) return smtp_client_send_sendmail(client); if ((fd = create_temp_file(&path)) == -1) return o_stream_create_error(errno); client->temp_path = p_strdup(client->pool, path); client->temp_fd = fd; client->output = o_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE); o_stream_set_no_error_handling(client->output, TRUE); return client->output; } static int smtp_client_deinit_sendmail(struct smtp_client *client) { int ret = EX_TEMPFAIL, status; o_stream_destroy(&client->output); if (client->pid == (pid_t)-1) { /* smtp_client_send() failed already */ } else if (waitpid(client->pid, &status, 0) < 0) i_error("waitpid() failed: %m"); else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret != 0) { i_error("Sendmail process terminated abnormally, " "exit status %d", ret); } } else if (WIFSIGNALED(status)) { i_error("Sendmail process terminated abnormally, signal %d", WTERMSIG(status)); } else if (WIFSTOPPED(status)) { i_error("Sendmail process stopped, signal %d", WSTOPSIG(status)); } else { i_error("Sendmail process terminated abnormally, " "return status %d", status); } pool_unref(&client->pool); return ret; } static void smtp_client_send_finished(void *context) { struct smtp_client *smtp_client = context; smtp_client->finished = TRUE; io_loop_stop(current_ioloop); } static void smtp_client_error(struct smtp_client *client, bool tempfail, const char *error) { if (client->error == NULL) { client->tempfail = tempfail; client->error = p_strdup_printf(client->pool, "smtp(%s): %s", client->set->submission_host, error); } } static void rcpt_to_callback(enum lmtp_client_result result, const char *reply, void *context) { struct smtp_client *client = context; if (result != LMTP_CLIENT_RESULT_OK) { smtp_client_error(client, (reply[0] != '5'), t_strdup_printf("RCPT TO failed: %s", reply)); smtp_client_send_finished(client); } } static void data_callback(enum lmtp_client_result result, const char *reply, void *context) { struct smtp_client *client = context; if (result != LMTP_CLIENT_RESULT_OK) { smtp_client_error(client, (reply[0] != '5'), t_strdup_printf("DATA failed: %s", reply)); smtp_client_send_finished(client); } else { client->success = TRUE; } } static int smtp_client_send_flush(struct smtp_client *client, unsigned int timeout_secs, const char **error_r) { struct lmtp_client_settings client_set; struct lmtp_client *lmtp_client; struct ioloop *ioloop; struct istream *input; const char *host, *const *destp; in_port_t port; if (net_str2hostport(client->set->submission_host, DEFAULT_SUBMISSION_PORT, &host, &port) < 0) { *error_r = t_strdup_printf( "Invalid submission_host: %s", host); return -1; } if (o_stream_nfinish(client->output) < 0) { *error_r = t_strdup_printf("write(%s) failed: %s", client->temp_path, o_stream_get_error(client->output)); return -1; } if (o_stream_seek(client->output, 0) < 0) { *error_r = t_strdup_printf("lseek(%s) failed: %s", client->temp_path, o_stream_get_error(client->output)); return -1; } i_zero(&client_set); client_set.mail_from = client->return_path == NULL ? "<>" : t_strconcat("<", client->return_path, ">", NULL); client_set.my_hostname = client->set->hostname; client_set.timeout_secs = timeout_secs; ioloop = io_loop_create(); lmtp_client = lmtp_client_init(&client_set, smtp_client_send_finished, client); if (lmtp_client_connect_tcp(lmtp_client, LMTP_CLIENT_PROTOCOL_SMTP, host, port) < 0) { lmtp_client_deinit(&lmtp_client); io_loop_destroy(&ioloop); *error_r = t_strdup_printf("Couldn't connect to %s:%u", host, port); return -1; } array_foreach(&client->destinations, destp) { lmtp_client_add_rcpt(lmtp_client, *destp, rcpt_to_callback, data_callback, client); } input = i_stream_create_fd(client->temp_fd, (size_t)-1, FALSE); lmtp_client_send(lmtp_client, input); i_stream_unref(&input); if (!client->finished) io_loop_run(ioloop); lmtp_client_deinit(&lmtp_client); io_loop_destroy(&ioloop); if (client->success) return 1; else if (client->tempfail) { i_assert(client->error != NULL); *error_r = t_strdup(client->error); return -1; } else { i_assert(client->error != NULL); *error_r = t_strdup(client->error); return 0; } } void smtp_client_abort(struct smtp_client **_client) { struct smtp_client *client = *_client; *_client = NULL; o_stream_ignore_last_errors(client->output); if (!client->use_smtp) { if (client->pid != (pid_t)-1) (void)kill(client->pid, SIGTERM); (void)smtp_client_deinit_sendmail(client); } else { o_stream_destroy(&client->output); pool_unref(&client->pool); } } int smtp_client_deinit(struct smtp_client *client, const char **error_r) { return smtp_client_deinit_timeout(client, 0, error_r); } int smtp_client_deinit_timeout(struct smtp_client *client, unsigned int timeout_secs, const char **error_r) { int ret; if (!client->use_smtp) { if (smtp_client_deinit_sendmail(client) != 0) { *error_r = "Failed to execute sendmail"; return -1; } return 1; } /* the mail has been written to a file. now actually send it. */ ret = smtp_client_send_flush(client, timeout_secs, error_r); smtp_client_abort(&client); return ret; } struct smtp_client * smtp_client_open(const struct lda_settings *set, const char *destination, const char *return_path, struct ostream **output_r) { struct smtp_client *client; client = smtp_client_init(set, return_path); smtp_client_add_rcpt(client, destination); *output_r = smtp_client_send(client); return client; } int smtp_client_close(struct smtp_client *client) { const char *error; int ret; if (!client->use_smtp) return smtp_client_deinit_sendmail(client); ret = smtp_client_deinit(client, &error); if (ret < 0) { i_error("%s", error); return EX_TEMPFAIL; } if (ret == 0) { i_error("%s", error); return EX_NOPERM; } return 0; } dovecot-2.2.33.2/src/lib-lda/Makefile.in0000644000175000017500000006175713172375573014522 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-lda ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am_libdovecot_lda_la_OBJECTS = libdovecot_lda_la_OBJECTS = $(am_libdovecot_lda_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_lda_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_lda_la_LDFLAGS) $(LDFLAGS) \ -o $@ liblda_la_LIBADD = am_liblda_la_OBJECTS = duplicate.lo lda-settings.lo mail-deliver.lo \ mail-send.lo smtp-client.lo liblda_la_OBJECTS = $(am_liblda_la_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_lda_la_SOURCES) $(liblda_la_SOURCES) DIST_SOURCES = $(libdovecot_lda_la_SOURCES) $(liblda_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = liblda.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-smtp \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage liblda_la_SOURCES = \ duplicate.c \ lda-settings.c \ mail-deliver.c \ mail-send.c \ smtp-client.c headers = \ duplicate.h \ lda-settings.h \ mail-deliver.h \ mail-send.h \ smtp-client.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) deps = ../lib-storage/libdovecot-storage.la ../lib-dovecot/libdovecot.la pkglib_LTLIBRARIES = libdovecot-lda.la libdovecot_lda_la_SOURCES = libdovecot_lda_la_LIBADD = liblda.la $(deps) libdovecot_lda_la_DEPENDENCIES = liblda.la $(deps) libdovecot_lda_la_LDFLAGS = -export-dynamic all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-lda/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-lda/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-lda.la: $(libdovecot_lda_la_OBJECTS) $(libdovecot_lda_la_DEPENDENCIES) $(EXTRA_libdovecot_lda_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_lda_la_LINK) -rpath $(pkglibdir) $(libdovecot_lda_la_OBJECTS) $(libdovecot_lda_la_LIBADD) $(LIBS) liblda.la: $(liblda_la_OBJECTS) $(liblda_la_DEPENDENCIES) $(EXTRA_liblda_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(liblda_la_OBJECTS) $(liblda_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/duplicate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lda-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-deliver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-send.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-client.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-lda/duplicate.h0000644000175000017500000000110613165463624014553 00000000000000#ifndef DUPLICATE_H #define DUPLICATE_H struct duplicate_context; struct mail_storage_settings; #define DUPLICATE_DEFAULT_KEEP (3600 * 24) bool duplicate_check(struct duplicate_context *ctx, const void *id, size_t id_size, const char *user); void duplicate_mark(struct duplicate_context *ctx, const void *id, size_t id_size, const char *user, time_t timestamp); void duplicate_flush(struct duplicate_context *ctx); struct duplicate_context *duplicate_init(struct mail_user *user); void duplicate_deinit(struct duplicate_context **ctx); #endif dovecot-2.2.33.2/src/lib-lda/mail-send.c0000644000175000017500000001515213165463624014453 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hostpid.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "str-sanitize.h" #include "var-expand.h" #include "message-date.h" #include "message-size.h" #include "istream-header-filter.h" #include "mail-storage.h" #include "mail-storage-settings.h" #include "lda-settings.h" #include "mail-deliver.h" #include "smtp-client.h" #include "mail-send.h" #include static const struct var_expand_table * get_var_expand_table(struct mail *mail, const char *reason, const char *recipient) { static struct var_expand_table static_tab[] = { { 'n', NULL, "crlf" }, { 'r', NULL, "reason" }, { 's', NULL, "subject" }, { 't', NULL, "to" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; const char *subject; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = "\r\n"; tab[1].value = reason; if (mail_get_first_header(mail, "Subject", &subject) <= 0) subject = ""; tab[2].value = str_sanitize(subject, 80); tab[3].value = recipient; return tab; } int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, const char *reason) { struct mail *mail = ctx->src_mail; struct istream *input; struct smtp_client *smtp_client; struct ostream *output; const char *return_addr, *hdr; const char *value, *msgid, *orig_msgid, *boundary, *error; string_t *str; int ret; if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0) orig_msgid = NULL; if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 && strcasecmp(value, "no") != 0) { i_info("msgid=%s: Auto-submitted message discarded: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } return_addr = mail_deliver_get_return_address(ctx); if (return_addr == NULL) { i_info("msgid=%s: Return-Path missing, rejection reason: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } if (mailbox_get_settings(mail->box)->mail_debug) { i_debug("Sending a rejection to %s: %s", recipient, str_sanitize(reason, 512)); } smtp_client = smtp_client_init(ctx->set, NULL); smtp_client_add_rcpt(smtp_client, return_addr); output = smtp_client_send(smtp_client); msgid = mail_deliver_get_new_message_id(ctx); boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname); str = t_str_new(512); str_printfa(str, "Message-ID: %s\r\n", msgid); str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time)); str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n", ctx->set->postmaster_address); str_printfa(str, "To: <%s>\r\n", return_addr); str_append(str, "MIME-Version: 1.0\r\n"); str_printfa(str, "Content-Type: " "multipart/report; report-type=%s;\r\n" "\tboundary=\"%s\"\r\n", ctx->dsn ? "delivery-status" : "disposition-notification", boundary); str_append(str, "Subject: "); var_expand(str, ctx->set->rejection_subject, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n"); str_append(str, "Precedence: bulk\r\n"); str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* human readable status report */ str_printfa(str, "--%s\r\n", boundary); str_append(str, "Content-Type: text/plain; charset=utf-8\r\n"); str_append(str, "Content-Disposition: inline\r\n"); str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n"); var_expand(str, ctx->set->rejection_reason, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); if (ctx->dsn) { /* DSN status report: For LDA rejects. currently only used when user is out of quota */ str_printfa(str, "--%s\r\n" "Content-Type: message/delivery-status\r\n\r\n", boundary); str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); str_append(str, "Action: failed\r\n"); str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0"); } else { /* MDN status report: For Sieve "reject" */ str_printfa(str, "--%s\r\n" "Content-Type: message/disposition-notification\r\n\r\n", boundary); str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); if (orig_msgid != NULL) str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid); str_append(str, "Disposition: " "automatic-action/MDN-sent-automatically; deleted\r\n"); } str_append(str, "\r\n"); /* original message's headers */ str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); if (mail_get_hdr_stream(mail, NULL, &input) == 0) { /* Note: If you add more headers, they need to be sorted. We'll drop Content-Type because we're not including the message body, and having a multipart Content-Type may confuse some MIME parsers when they don't see the message boundaries. */ static const char *const exclude_headers[] = { "Content-Type" }; input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, exclude_headers, N_ELEMENTS(exclude_headers), *null_header_filter_callback, (void *)NULL); ret = o_stream_send_istream(output, input); i_stream_unref(&input); i_assert(ret != 0); } str_truncate(str, 0); str_printfa(str, "\r\n\r\n--%s--\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); if ((ret = smtp_client_deinit_timeout(smtp_client, ctx->timeout_secs, &error)) < 0) { i_error("msgid=%s: Temporarily failed to send rejection: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(error, 512)); } else if (ret == 0) { i_info("msgid=%s: Permanently failed to send rejection: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(error, 512)); } return ret < 0 ? -1 : 0; } dovecot-2.2.33.2/src/lib-lda/lda-settings.h0000644000175000017500000000105113165463624015176 00000000000000#ifndef LDA_SETTINGS_H #define LDA_SETTINGS_H struct mail_user_settings; struct lda_settings { const char *postmaster_address; const char *hostname; const char *submission_host; const char *sendmail_path; const char *rejection_subject; const char *rejection_reason; const char *deliver_log_format; const char *recipient_delimiter; const char *lda_original_recipient_header; bool quota_full_tempfail; bool lda_mailbox_autocreate; bool lda_mailbox_autosubscribe; }; extern const struct setting_parser_info lda_setting_parser_info; #endif dovecot-2.2.33.2/src/lib-lda/duplicate.c0000644000175000017500000002147413165463624014560 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "home-expand.h" #include "file-dotlock.h" #include "hash.h" #include "mail-user.h" #include "mail-storage-settings.h" #include "duplicate.h" #include #include #define DUPLICATE_FNAME ".dovecot.lda-dupes" #define COMPRESS_PERCENTAGE 10 #define DUPLICATE_BUFSIZE 4096 #define DUPLICATE_VERSION 2 struct duplicate { const void *id; unsigned int id_size; const char *user; time_t time; }; struct duplicate_file_header { uint32_t version; }; struct duplicate_record_header { uint32_t stamp; uint32_t id_size; uint32_t user_size; }; struct duplicate_file { pool_t pool; HASH_TABLE(struct duplicate *, struct duplicate *) hash; const char *path; int new_fd; struct dotlock *dotlock; unsigned int changed:1; }; struct duplicate_context { char *path; struct dotlock_settings dotlock_set; struct duplicate_file *file; }; static const struct dotlock_settings default_duplicate_dotlock_set = { .timeout = 20, .stale_timeout = 10, }; static int duplicate_cmp(const struct duplicate *d1, const struct duplicate *d2) { return (d1->id_size == d2->id_size && memcmp(d1->id, d2->id, d1->id_size) == 0 && strcasecmp(d1->user, d2->user) == 0) ? 0 : 1; } static unsigned int duplicate_hash(const struct duplicate *d) { /* a char* hash function from ASU -- from glib */ const unsigned char *s = d->id, *end = s + d->id_size; unsigned int g, h = 0; while (s != end) { h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h ^ strcase_hash(d->user); } static int duplicate_read_records(struct duplicate_file *file, struct istream *input, unsigned int record_size) { const unsigned char *data; struct duplicate_record_header hdr; size_t size; unsigned int change_count; change_count = 0; while (i_stream_read_data(input, &data, &size, record_size) > 0) { if (record_size == sizeof(hdr)) memcpy(&hdr, data, sizeof(hdr)); else { /* FIXME: backwards compatibility with v1.0 */ time_t stamp; i_assert(record_size == sizeof(time_t) + sizeof(uint32_t)*2); memcpy(&stamp, data, sizeof(stamp)); hdr.stamp = stamp; memcpy(&hdr.id_size, data + sizeof(time_t), sizeof(hdr.id_size)); memcpy(&hdr.user_size, data + sizeof(time_t) + sizeof(uint32_t), sizeof(hdr.user_size)); } i_stream_skip(input, record_size); if (hdr.id_size == 0 || hdr.user_size == 0 || hdr.id_size > DUPLICATE_BUFSIZE || hdr.user_size > DUPLICATE_BUFSIZE) { i_error("broken duplicate file %s", file->path); return -1; } if (i_stream_read_data(input, &data, &size, hdr.id_size + hdr.user_size - 1) <= 0) { i_error("unexpected end of file in %s", file->path); return -1; } if ((time_t)hdr.stamp >= ioloop_time) { /* still valid, save it */ struct duplicate *d; void *new_id; new_id = p_malloc(file->pool, hdr.id_size); memcpy(new_id, data, hdr.id_size); d = p_new(file->pool, struct duplicate, 1); d->id = new_id; d->id_size = hdr.id_size; d->user = p_strndup(file->pool, data + hdr.id_size, hdr.user_size); d->time = hdr.stamp; hash_table_insert(file->hash, d, d); } else { change_count++; } i_stream_skip(input, hdr.id_size + hdr.user_size); } if (hash_table_count(file->hash) * COMPRESS_PERCENTAGE / 100 > change_count) file->changed = TRUE; return 0; } static int duplicate_read(struct duplicate_file *file) { struct istream *input; struct duplicate_file_header hdr; const unsigned char *data; size_t size; int fd; unsigned int record_size = 0; fd = open(file->path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", file->path); return -1; } /* */ input = i_stream_create_fd(fd, DUPLICATE_BUFSIZE, FALSE); if (i_stream_read_data(input, &data, &size, sizeof(hdr)) > 0) { memcpy(&hdr, data, sizeof(hdr)); if (hdr.version == 0 || hdr.version > DUPLICATE_VERSION + 10) { /* FIXME: backwards compatibility with v1.0 */ record_size = sizeof(time_t) + sizeof(uint32_t)*2; } else if (hdr.version == DUPLICATE_VERSION) { record_size = sizeof(struct duplicate_record_header); i_stream_skip(input, sizeof(hdr)); } } if (record_size == 0 || duplicate_read_records(file, input, record_size) < 0) i_unlink_if_exists(file->path); i_stream_unref(&input); if (close(fd) < 0) i_error("close(%s) failed: %m", file->path); return 0; } static struct duplicate_file *duplicate_file_new(struct duplicate_context *ctx) { struct duplicate_file *file; pool_t pool; i_assert(ctx->path != NULL); pool = pool_alloconly_create("duplicates", 10240); file = p_new(pool, struct duplicate_file, 1); file->pool = pool; file->path = p_strdup(pool, ctx->path); file->new_fd = file_dotlock_open(&ctx->dotlock_set, file->path, 0, &file->dotlock); if (file->new_fd != -1) ; else if (errno != EAGAIN) i_error("file_dotlock_open(%s) failed: %m", file->path); else { i_error("Creating lock file for %s timed out in %u secs", file->path, ctx->dotlock_set.timeout); } hash_table_create(&file->hash, pool, 0, duplicate_hash, duplicate_cmp); (void)duplicate_read(file); return file; } static void duplicate_file_free(struct duplicate_file **_file) { struct duplicate_file *file = *_file; *_file = NULL; if (file->dotlock != NULL) file_dotlock_delete(&file->dotlock); hash_table_destroy(&file->hash); pool_unref(&file->pool); } bool duplicate_check(struct duplicate_context *ctx, const void *id, size_t id_size, const char *user) { struct duplicate d; if (ctx->file == NULL) { if (ctx->path == NULL) { /* duplicate database disabled */ return FALSE; } ctx->file = duplicate_file_new(ctx); } d.id = id; d.id_size = id_size; d.user = user; return hash_table_lookup(ctx->file->hash, &d) != NULL; } void duplicate_mark(struct duplicate_context *ctx, const void *id, size_t id_size, const char *user, time_t timestamp) { struct duplicate *d; void *new_id; if (ctx->file == NULL) { if (ctx->path == NULL) { /* duplicate database disabled */ return; } ctx->file = duplicate_file_new(ctx); } new_id = p_malloc(ctx->file->pool, id_size); memcpy(new_id, id, id_size); d = p_new(ctx->file->pool, struct duplicate, 1); d->id = new_id; d->id_size = id_size; d->user = p_strdup(ctx->file->pool, user); d->time = timestamp; ctx->file->changed = TRUE; hash_table_insert(ctx->file->hash, d, d); } void duplicate_flush(struct duplicate_context *ctx) { struct duplicate_file *file = ctx->file; struct duplicate_file_header hdr; struct duplicate_record_header rec; struct ostream *output; struct hash_iterate_context *iter; struct duplicate *d; if (file == NULL) return; if (!file->changed || file->new_fd == -1) { /* unlock the duplicate database */ duplicate_file_free(&ctx->file); return; } i_zero(&hdr); hdr.version = DUPLICATE_VERSION; output = o_stream_create_fd_file(file->new_fd, 0, FALSE); o_stream_cork(output); o_stream_nsend(output, &hdr, sizeof(hdr)); i_zero(&rec); iter = hash_table_iterate_init(file->hash); while (hash_table_iterate(iter, file->hash, &d, &d)) { rec.stamp = d->time; rec.id_size = d->id_size; rec.user_size = strlen(d->user); o_stream_nsend(output, &rec, sizeof(rec)); o_stream_nsend(output, d->id, rec.id_size); o_stream_nsend(output, d->user, rec.user_size); } hash_table_iterate_deinit(&iter); if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", file->path, o_stream_get_error(output)); o_stream_unref(&output); duplicate_file_free(&ctx->file); return; } o_stream_unref(&output); if (file_dotlock_replace(&file->dotlock, 0) < 0) i_error("file_dotlock_replace(%s) failed: %m", file->path); duplicate_file_free(&ctx->file); } struct duplicate_context *duplicate_init(struct mail_user *user) { struct duplicate_context *ctx; const struct mail_storage_settings *mail_set; const char *home = NULL; if (mail_user_get_home(user, &home) <= 0) { i_error("User %s doesn't have home dir set, " "disabling duplicate database", user->username); } ctx = i_new(struct duplicate_context, 1); ctx->path = home == NULL ? NULL : i_strconcat(home, "/"DUPLICATE_FNAME, NULL); ctx->dotlock_set = default_duplicate_dotlock_set; mail_set = mail_user_set_get_storage_set(user); ctx->dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; ctx->dotlock_set.nfs_flush = mail_set->mail_nfs_storage; return ctx; } void duplicate_deinit(struct duplicate_context **_ctx) { struct duplicate_context *ctx = *_ctx; *_ctx = NULL; if (ctx->file != NULL) { duplicate_flush(ctx); i_assert(ctx->file == NULL); } i_free(ctx->path); i_free(ctx); } dovecot-2.2.33.2/src/lib-lda/mail-deliver.h0000644000175000017500000000734613165463624015167 00000000000000#ifndef MAIL_DELIVER_H #define MAIL_DELIVER_H #include "guid.h" #include "mail-types.h" #include "mail-error.h" #include /* How many seconds to wait for replies from SMTP before failing. Used for sending rejects, forward, etc. */ #define LDA_SUBMISSION_TIMEOUT_SECS 30 struct mail_storage; struct mail_save_context; struct mailbox; struct mail_deliver_session { pool_t pool; /* List of INBOX GUIDs where this mail has already been saved to */ ARRAY(guid_128_t) inbox_guids; }; struct mail_deliver_context { pool_t pool; const struct lda_settings *set; struct mail_deliver_session *session; unsigned int timeout_secs; unsigned int session_time_msecs; struct timeval delivery_time_started; struct duplicate_context *dup_ctx; /* Session ID, used as log line prefix if non-NULL. */ const char *session_id; /* Mail to save */ struct mail *src_mail; /* Envelope sender, if known. */ const char *src_envelope_sender; /* Destination user */ struct mail_user *dest_user; /* Original recipient address */ const char *dest_addr; /* Final recipient address (typically same as dest_addr) */ const char *final_dest_addr; /* Mailbox where mail should be saved, unless e.g. Sieve does something to it. */ const char *dest_mailbox_name; /* Filled with destination mail, if save_dest_mail=TRUE. The caller must free the mail, its transaction and close the mailbox. */ struct mail *dest_mail; /* mail_deliver_log() caches the var expand table values here */ struct mail_deliver_cache *cache; /* Error message for a temporary failure. This is necessary only when there is no storage where to get the error message from. */ const char *tempfail_error; bool tried_default_save; bool saved_mail; bool save_dest_mail; /* Delivery failed because user is out of quota / disk space */ bool mailbox_full; /* Send DSN instead of MDN */ bool dsn; }; struct mail_deliver_save_open_context { struct mail_user *user; bool lda_mailbox_autocreate; bool lda_mailbox_autosubscribe; }; typedef int deliver_mail_func_t(struct mail_deliver_context *ctx, struct mail_storage **storage_r); extern deliver_mail_func_t *deliver_mail; const struct var_expand_table * mail_deliver_ctx_get_log_var_expand_table(struct mail_deliver_context *ctx, const char *message); void mail_deliver_log(struct mail_deliver_context *ctx, const char *fmt, ...) ATTR_FORMAT(2, 3); const char *mail_deliver_get_address(struct mail *mail, const char *header); const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx); const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx); struct mail_deliver_session *mail_deliver_session_init(void); void mail_deliver_session_deinit(struct mail_deliver_session **session); /* Try to open mailbox for saving. Returns 0 if ok, -1 if error. The box may be returned even with -1, and the caller must free it then. */ int mail_deliver_save_open(struct mail_deliver_save_open_context *ctx, const char *name, struct mailbox **box_r, enum mail_error *error_r, const char **error_str_r); int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r) ATTR_NULL(4); void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_session *session, struct mail_save_context *save_ctx); int mail_deliver(struct mail_deliver_context *ctx, struct mail_storage **storage_r); /* Sets the deliver_mail hook and returns the previous hook, which the new_hook should call if it's non-NULL. */ deliver_mail_func_t *mail_deliver_hook_set(deliver_mail_func_t *new_hook); /* Must be called before any storage is created. */ void mail_deliver_hooks_init(void); #endif dovecot-2.2.33.2/src/lib-lda/mail-deliver.c0000644000175000017500000005053213165463624015155 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "str-sanitize.h" #include "time-util.h" #include "unichar.h" #include "var-expand.h" #include "message-address.h" #include "lda-settings.h" #include "mail-storage.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "duplicate.h" #include "mail-deliver.h" #define MAIL_DELIVER_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_deliver_user_module) #define MAIL_DELIVER_STORAGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_deliver_storage_module) struct mail_deliver_user { union mail_user_module_context module_ctx; struct mail_deliver_context *deliver_ctx; bool want_storage_id; }; deliver_mail_func_t *deliver_mail = NULL; struct mail_deliver_cache { bool filled; const char *message_id; const char *subject; const char *from; const char *from_envelope; const char *storage_id; uoff_t psize, vsize; }; struct mail_deliver_mailbox { union mailbox_module_context module_ctx; bool delivery_box; }; struct mail_deliver_transaction { union mailbox_transaction_module_context module_ctx; struct mail_deliver_cache cache; }; static const char *lda_log_wanted_headers[] = { "From", "Message-ID", "Subject", NULL }; static enum mail_fetch_field lda_log_wanted_fetch_fields = MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE; static MODULE_CONTEXT_DEFINE_INIT(mail_deliver_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(mail_deliver_storage_module, &mail_storage_module_register); const char *mail_deliver_get_address(struct mail *mail, const char *header) { struct message_address *addr; const char *str; if (mail_get_first_header(mail, header, &str) <= 0) return NULL; addr = message_address_parse(pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, FALSE); return addr == NULL || addr->mailbox == NULL || addr->domain == NULL || *addr->mailbox == '\0' || *addr->domain == '\0' ? NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL); } static void update_cache(pool_t pool, const char **old_str, const char *new_str) { if (new_str == NULL || new_str[0] == '\0') *old_str = NULL; else if (*old_str == NULL || strcmp(*old_str, new_str) != 0) *old_str = p_strdup(pool, new_str); } static void mail_deliver_log_update_cache(struct mail_deliver_cache *cache, pool_t pool, struct mail *mail) { const char *message_id = NULL, *subject = NULL, *from_envelope = NULL; const char *from; if (cache->filled) return; cache->filled = TRUE; if (mail_get_first_header(mail, "Message-ID", &message_id) > 0) message_id = str_sanitize(message_id, 200); update_cache(pool, &cache->message_id, message_id); if (mail_get_first_header_utf8(mail, "Subject", &subject) > 0) subject = str_sanitize(subject, 80); update_cache(pool, &cache->subject, subject); from = str_sanitize(mail_deliver_get_address(mail, "From"), 80); update_cache(pool, &cache->from, from); if (mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE, &from_envelope) > 0) from_envelope = str_sanitize(from_envelope, 80); update_cache(pool, &cache->from_envelope, from_envelope); if (mail_get_physical_size(mail, &cache->psize) < 0) cache->psize = 0; if (mail_get_virtual_size(mail, &cache->vsize) < 0) cache->vsize = 0; } const struct var_expand_table * mail_deliver_ctx_get_log_var_expand_table(struct mail_deliver_context *ctx, const char *message) { unsigned int delivery_time_msecs; /* If a mail was saved/copied, the cache is already filled and the following call is ignored. Otherwise, only the source mail exists. */ if (ctx->cache == NULL) ctx->cache = p_new(ctx->pool, struct mail_deliver_cache, 1); mail_deliver_log_update_cache(ctx->cache, ctx->pool, ctx->src_mail); /* This call finishes a mail delivery. With Sieve there may be multiple mail deliveries. */ ctx->cache->filled = FALSE; io_loop_time_refresh(); delivery_time_msecs = timeval_diff_msecs(&ioloop_timeval, &ctx->delivery_time_started); const struct var_expand_table stack_tab[] = { { '$', message, NULL }, { 'm', ctx->cache->message_id != NULL ? ctx->cache->message_id : "unspecified", "msgid" }, { 's', ctx->cache->subject, "subject" }, { 'f', ctx->cache->from, "from" }, { 'e', ctx->cache->from_envelope, "from_envelope" }, { 'p', dec2str(ctx->cache->psize), "size" }, { 'w', dec2str(ctx->cache->vsize), "vsize" }, { '\0', dec2str(delivery_time_msecs), "delivery_time" }, { '\0', dec2str(ctx->session_time_msecs), "session_time" }, { '\0', ctx->dest_addr, "to_envelope" }, { '\0', ctx->cache->storage_id, "storage_id" }, { '\0', NULL, NULL } }; return p_memdup(unsafe_data_stack_pool, stack_tab, sizeof(stack_tab)); } void mail_deliver_log(struct mail_deliver_context *ctx, const char *fmt, ...) { va_list args; string_t *str; const struct var_expand_table *tab; const char *msg; if (*ctx->set->deliver_log_format == '\0') return; va_start(args, fmt); msg = t_strdup_vprintf(fmt, args); str = t_str_new(256); tab = mail_deliver_ctx_get_log_var_expand_table(ctx, msg); var_expand(str, ctx->set->deliver_log_format, tab); i_info("%s", str_c(str)); va_end(args); } struct mail_deliver_session *mail_deliver_session_init(void) { struct mail_deliver_session *session; pool_t pool; pool = pool_alloconly_create("mail deliver session", 1024); session = p_new(pool, struct mail_deliver_session, 1); session->pool = pool; return session; } void mail_deliver_session_deinit(struct mail_deliver_session **_session) { struct mail_deliver_session *session = *_session; *_session = NULL; pool_unref(&session->pool); } int mail_deliver_save_open(struct mail_deliver_save_open_context *ctx, const char *name, struct mailbox **box_r, enum mail_error *error_r, const char **error_str_r) { struct mail_namespace *ns; struct mailbox *box; enum mailbox_flags flags = MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_POST_SESSION; *box_r = NULL; *error_r = MAIL_ERROR_NONE; *error_str_r = NULL; if (!uni_utf8_str_is_valid(name)) { *error_str_r = "Mailbox name not valid UTF-8"; *error_r = MAIL_ERROR_PARAMS; return -1; } ns = mail_namespace_find(ctx->user->namespaces, name); if (strcmp(name, ns->prefix) == 0 && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* delivering to a namespace prefix means we actually want to deliver to the INBOX instead */ name = "INBOX"; ns = mail_namespace_find_inbox(ctx->user->namespaces); } if (strcasecmp(name, "INBOX") == 0) { /* deliveries to INBOX must always succeed, regardless of ACLs */ flags |= MAILBOX_FLAG_IGNORE_ACLS; } *box_r = box = mailbox_alloc(ns->list, name, flags); mailbox_set_reason(box, "lib-lda delivery"); /* flag that this mailbox is used for delivering the mail. the context isn't set in pigeonhole testuite. */ struct mail_deliver_mailbox *mbox = MAIL_DELIVER_STORAGE_CONTEXT(box); if (mbox != NULL) mbox->delivery_box = TRUE; if (mailbox_open(box) == 0) return 0; *error_str_r = mailbox_get_last_internal_error(box, error_r); if (!ctx->lda_mailbox_autocreate || *error_r != MAIL_ERROR_NOTFOUND) return -1; /* try creating it. */ if (mailbox_create(box, NULL, FALSE) < 0) { *error_str_r = mailbox_get_last_internal_error(box, error_r); if (*error_r != MAIL_ERROR_EXISTS) return -1; /* someone else just created it */ } if (ctx->lda_mailbox_autosubscribe) { /* (try to) subscribe to it */ (void)mailbox_set_subscribed(box, TRUE); } /* and try opening again */ if (mailbox_open(box) < 0) { *error_str_r = mailbox_get_last_internal_error(box, error_r); return -1; } return 0; } static bool mail_deliver_check_duplicate(struct mail_deliver_session *session, struct mailbox *box) { struct mailbox_metadata metadata; const guid_128_t *guid; if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { /* just play it safe and assume a duplicate */ return TRUE; } /* there shouldn't be all that many recipients, so just do a linear search */ if (!array_is_created(&session->inbox_guids)) p_array_init(&session->inbox_guids, session->pool, 8); array_foreach(&session->inbox_guids, guid) { if (memcmp(metadata.guid, *guid, sizeof(metadata.guid)) == 0) return TRUE; } array_append(&session->inbox_guids, &metadata.guid, 1); return FALSE; } void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_session *session, struct mail_save_context *save_ctx) { struct mailbox_transaction_context *trans = mailbox_save_get_transaction(save_ctx); struct mailbox *box = mailbox_transaction_get_mailbox(trans); guid_128_t guid; if (strcmp(mailbox_get_name(box), "INBOX") != 0) return; /* avoid storing duplicate GUIDs to delivered mails to INBOX. this happens if mail is delivered to same user multiple times within a session. the problem with this is that if GUIDs are used as POP3 UIDLs, some clients can't handle the duplicates well. */ if (mail_deliver_check_duplicate(session, box)) { guid_128_generate(guid); mailbox_save_set_guid(save_ctx, guid_128_to_string(guid)); } } static struct mail * mail_deliver_open_mail(struct mailbox *box, uint32_t uid, enum mail_fetch_field wanted_fields, struct mailbox_transaction_context **trans_r) { struct mailbox_transaction_context *t; struct mail *mail; *trans_r = NULL; if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) return NULL; t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, wanted_fields, NULL); if (!mail_set_uid(mail, uid)) { mail_free(&mail); mailbox_transaction_rollback(&t); } *trans_r = t; return mail; } int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r) { struct mail_deliver_save_open_context open_ctx; struct mailbox *box; enum mailbox_transaction_flags trans_flags; struct mailbox_transaction_context *t; struct mail_save_context *save_ctx; struct mailbox_header_lookup_ctx *headers_ctx; struct mail_keywords *kw; struct mail *dest_mail; enum mail_error error; const char *mailbox_name, *errstr, *guid; struct mail_transaction_commit_changes changes; bool default_save; int ret = 0; i_assert(ctx->dest_mail == NULL); default_save = strcmp(mailbox, ctx->dest_mailbox_name) == 0; if (default_save) ctx->tried_default_save = TRUE; i_zero(&open_ctx); open_ctx.user = ctx->dest_user; open_ctx.lda_mailbox_autocreate = ctx->set->lda_mailbox_autocreate; open_ctx.lda_mailbox_autosubscribe = ctx->set->lda_mailbox_autosubscribe; mailbox_name = str_sanitize(mailbox, 80); if (mail_deliver_save_open(&open_ctx, mailbox, &box, &error, &errstr) < 0) { if (box != NULL) { *storage_r = mailbox_get_storage(box); mailbox_free(&box); } mail_deliver_log(ctx, "save failed to open mailbox %s: %s", mailbox_name, errstr); return -1; } *storage_r = mailbox_get_storage(box); trans_flags = MAILBOX_TRANSACTION_FLAG_EXTERNAL; if (ctx->save_dest_mail) trans_flags |= MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS; t = mailbox_transaction_begin(box, trans_flags); kw = str_array_length(keywords) == 0 ? NULL : mailbox_keywords_create_valid(box, keywords); save_ctx = mailbox_save_alloc(t); if (ctx->src_envelope_sender != NULL) mailbox_save_set_from_envelope(save_ctx, ctx->src_envelope_sender); mailbox_save_set_flags(save_ctx, flags, kw); headers_ctx = mailbox_header_lookup_init(box, lda_log_wanted_headers); dest_mail = mailbox_save_get_dest_mail(save_ctx); mail_add_temp_wanted_fields(dest_mail, lda_log_wanted_fetch_fields, NULL); mailbox_header_lookup_unref(&headers_ctx); mail_deliver_deduplicate_guid_if_needed(ctx->session, save_ctx); if (mailbox_save_using_mail(&save_ctx, ctx->src_mail) < 0) ret = -1; if (kw != NULL) mailbox_keywords_unref(&kw); if (ret < 0) mailbox_transaction_rollback(&t); else ret = mailbox_transaction_commit_get_changes(&t, &changes); if (ret == 0) { ctx->saved_mail = TRUE; if (ctx->save_dest_mail) { /* copying needs the message body. with maildir we also need to get the GUID in case the message gets expunged */ i_assert(array_count(&changes.saved_uids) == 1); const struct seq_range *range = array_idx(&changes.saved_uids, 0); i_assert(range->seq1 == range->seq2); ctx->dest_mail = mail_deliver_open_mail(box, range->seq1, MAIL_FETCH_STREAM_BODY | MAIL_FETCH_GUID, &t); if (mail_get_special(ctx->dest_mail, MAIL_FETCH_GUID, &guid) < 0) { mail_free(&ctx->dest_mail); mailbox_transaction_rollback(&t); } } mail_deliver_log(ctx, "saved mail to %s", mailbox_name); pool_unref(&changes.pool); } else { mail_deliver_log(ctx, "save failed to %s: %s", mailbox_name, mail_storage_get_last_internal_error(*storage_r, &error)); } if (ctx->dest_mail == NULL) mailbox_free(&box); return ret; } const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx) { if (ctx->src_envelope_sender != NULL) return ctx->src_envelope_sender; return mail_deliver_get_address(ctx->src_mail, "Return-Path"); } const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx) { static int count = 0; return t_strdup_printf("", dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec), count++, ctx->set->hostname); } static bool mail_deliver_is_tempfailed(struct mail_deliver_context *ctx, struct mail_storage *storage) { enum mail_error error; if (ctx->tempfail_error != NULL) return TRUE; if (storage != NULL) { (void)mail_storage_get_last_error(storage, &error); return error == MAIL_ERROR_TEMP; } return FALSE; } int mail_deliver(struct mail_deliver_context *ctx, struct mail_storage **storage_r) { struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(ctx->dest_user); int ret; i_assert(muser->deliver_ctx == NULL); muser->want_storage_id = var_has_key(ctx->set->deliver_log_format, '\0', "storage_id"); muser->deliver_ctx = ctx; *storage_r = NULL; if (deliver_mail == NULL) ret = -1; else { ctx->dup_ctx = duplicate_init(ctx->dest_user); if (deliver_mail(ctx, storage_r) <= 0) { /* if message was saved, don't bounce it even though the script failed later. */ ret = ctx->saved_mail ? 0 : -1; } else { /* success. message may or may not have been saved. */ ret = 0; } duplicate_deinit(&ctx->dup_ctx); if (ret < 0 && mail_deliver_is_tempfailed(ctx, *storage_r)) { muser->deliver_ctx = NULL; return -1; } } if (ret < 0 && !ctx->tried_default_save) { /* plugins didn't handle this. save into the default mailbox. */ ret = mail_deliver_save(ctx, ctx->dest_mailbox_name, 0, NULL, storage_r); if (ret < 0 && mail_deliver_is_tempfailed(ctx, *storage_r)) { muser->deliver_ctx = NULL; return -1; } } if (ret < 0 && strcasecmp(ctx->dest_mailbox_name, "INBOX") != 0) { /* still didn't work. try once more to save it to INBOX. */ ret = mail_deliver_save(ctx, "INBOX", 0, NULL, storage_r); } muser->deliver_ctx = NULL; return ret; } deliver_mail_func_t *mail_deliver_hook_set(deliver_mail_func_t *new_hook) { deliver_mail_func_t *old_hook = deliver_mail; deliver_mail = new_hook; return old_hook; } static int mail_deliver_save_finish(struct mail_save_context *ctx) { struct mailbox *box = ctx->transaction->box; struct mail_deliver_mailbox *mbox = MAIL_DELIVER_STORAGE_CONTEXT(box); struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(box->storage->user); struct mail_deliver_transaction *dt = MAIL_DELIVER_STORAGE_CONTEXT(ctx->transaction); if (mbox->module_ctx.super.save_finish(ctx) < 0) return -1; /* initialize most of the fields from dest_mail */ mail_deliver_log_update_cache(&dt->cache, muser->deliver_ctx->pool, ctx->dest_mail); return 0; } static int mail_deliver_copy(struct mail_save_context *ctx, struct mail *mail) { struct mailbox *box = ctx->transaction->box; struct mail_deliver_mailbox *mbox = MAIL_DELIVER_STORAGE_CONTEXT(box); struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(box->storage->user); struct mail_deliver_transaction *dt = MAIL_DELIVER_STORAGE_CONTEXT(ctx->transaction); if (mbox->module_ctx.super.copy(ctx, mail) < 0) return -1; /* initialize most of the fields from dest_mail */ mail_deliver_log_update_cache(&dt->cache, muser->deliver_ctx->pool, ctx->dest_mail); return 0; } static void mail_deliver_cache_update_post_commit(struct mailbox *orig_box, uint32_t uid) { struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(orig_box->storage->user); struct mailbox *box; struct mailbox_transaction_context *t; struct mail *mail; const char *storage_id; if (!muser->want_storage_id) return; /* getting storage_id requires a whole new mailbox view that is synced, so it'll contain the newly written mail. this is racy, so it's possible another process has already deleted the mail. */ box = mailbox_alloc(orig_box->list, orig_box->vname, 0); mailbox_set_reason(box, "lib-lda storage-id"); mail = mail_deliver_open_mail(box, uid, MAIL_FETCH_STORAGE_ID, &t); if (mail != NULL) { if (mail_get_special(mail, MAIL_FETCH_STORAGE_ID, &storage_id) < 0 || storage_id[0] == '\0') storage_id = NULL; muser->deliver_ctx->cache->storage_id = p_strdup(muser->deliver_ctx->pool, storage_id); mail_free(&mail); (void)mailbox_transaction_commit(&t); } else { muser->deliver_ctx->cache->storage_id = NULL; } mailbox_free(&box); } static struct mailbox_transaction_context * mail_deliver_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct mail_deliver_mailbox *mbox = MAIL_DELIVER_STORAGE_CONTEXT(box); struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(box->storage->user); struct mailbox_transaction_context *t; struct mail_deliver_transaction *dt; i_assert(muser != NULL); i_assert(muser->deliver_ctx != NULL); t = mbox->module_ctx.super.transaction_begin(box, flags); dt = p_new(muser->deliver_ctx->pool, struct mail_deliver_transaction, 1); MODULE_CONTEXT_SET(t, mail_deliver_storage_module, dt); return t; } static int mail_deliver_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { struct mailbox *box = ctx->box; struct mail_deliver_mailbox *mbox = MAIL_DELIVER_STORAGE_CONTEXT(box); struct mail_deliver_transaction *dt = MAIL_DELIVER_STORAGE_CONTEXT(ctx); struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(box->storage->user); i_assert(dt != NULL); i_assert(muser != NULL); i_assert(muser->deliver_ctx != NULL); /* sieve creates multiple transactions, saves the mails and then commits all of them at the end. we'll need to keep switching the deliver_ctx->cache for each commit. we also want to do this only for commits generated by sieve. other plugins or storage backends may be creating transactions as well, which we need to ignore. */ if (mbox->delivery_box) muser->deliver_ctx->cache = &dt->cache; if (mbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) return -1; if (array_count(&changes_r->saved_uids) > 0) { const struct seq_range *range = array_idx(&changes_r->saved_uids, 0); mail_deliver_cache_update_post_commit(box, range->seq1); } return 0; } static void mail_deliver_mail_user_created(struct mail_user *user) { struct mail_deliver_user *muser; muser = p_new(user->pool, struct mail_deliver_user, 1); MODULE_CONTEXT_SET(user, mail_deliver_user_module, muser); } static void mail_deliver_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct mail_deliver_mailbox *mbox; struct mail_deliver_user *muser = MAIL_DELIVER_USER_CONTEXT(box->storage->user); /* we are doing something other than lda/lmtp delivery and should not be involved */ if (muser->deliver_ctx == NULL) return; mbox = p_new(box->pool, struct mail_deliver_mailbox, 1); mbox->module_ctx.super = *v; box->vlast = &mbox->module_ctx.super; v->save_finish = mail_deliver_save_finish; v->copy = mail_deliver_copy; v->transaction_begin = mail_deliver_transaction_begin; v->transaction_commit = mail_deliver_transaction_commit; MODULE_CONTEXT_SET(box, mail_deliver_storage_module, mbox); } static struct mail_storage_hooks mail_deliver_hooks = { .mail_user_created = mail_deliver_mail_user_created, .mailbox_allocated = mail_deliver_mailbox_allocated }; void mail_deliver_hooks_init(void) { mail_storage_hooks_add_internal(&mail_deliver_hooks); } dovecot-2.2.33.2/src/lib-lda/mail-send.h0000644000175000017500000000030513123174404014437 00000000000000#ifndef MAIL_SEND_H #define MAIL_SEND_H struct mail; struct mail_deliver_context; int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, const char *reason); #endif dovecot-2.2.33.2/src/lib-lda/lda-settings.c0000644000175000017500000000515313165463624015200 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "settings-parser.h" #include "mail-storage-settings.h" #include "lda-settings.h" #include static bool lda_settings_check(void *_set, pool_t pool, const char **error_r); #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct lda_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct lda_settings, field), defines } static const struct setting_define lda_setting_defines[] = { DEF(SET_STR_VARS, postmaster_address), DEF(SET_STR, hostname), DEF(SET_STR_VARS, submission_host), DEF(SET_STR_VARS, sendmail_path), DEF(SET_STR, rejection_subject), DEF(SET_STR, rejection_reason), DEF(SET_STR, deliver_log_format), DEF(SET_STR, recipient_delimiter), DEF(SET_STR, lda_original_recipient_header), DEF(SET_BOOL, quota_full_tempfail), DEF(SET_BOOL, lda_mailbox_autocreate), DEF(SET_BOOL, lda_mailbox_autosubscribe), SETTING_DEFINE_LIST_END }; static const struct lda_settings lda_default_settings = { .postmaster_address = "postmaster@%d", .hostname = "", .submission_host = "", .sendmail_path = "/usr/sbin/sendmail", .rejection_subject = "Rejected: %s", .rejection_reason = "Your message to <%t> was automatically rejected:%n%r", .deliver_log_format = "msgid=%m: %$", .recipient_delimiter = "+", .lda_original_recipient_header = "", .quota_full_tempfail = FALSE, .lda_mailbox_autocreate = FALSE, .lda_mailbox_autosubscribe = FALSE }; static const struct setting_parser_info *lda_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info lda_setting_parser_info = { .module_name = "lda", .defines = lda_setting_defines, .defaults = &lda_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct lda_settings), .parent_offset = (size_t)-1, #ifndef CONFIG_BINARY .check_func = lda_settings_check, #endif .dependencies = lda_setting_dependencies }; static bool lda_settings_check(void *_set, pool_t pool, const char **error_r) { struct lda_settings *set = _set; if (*set->hostname == '\0') set->hostname = p_strdup(pool, my_hostdomain()); if (set->postmaster_address[0] == SETTING_STRVAR_UNEXPANDED[0] && set->postmaster_address[1] == '\0') { /* check for valid looking fqdn in hostname */ if (strchr(set->hostname, '.') == NULL) { *error_r = "postmaster_address setting not given"; return FALSE; } set->postmaster_address = p_strconcat(pool, SETTING_STRVAR_UNEXPANDED, "postmaster@", set->hostname, NULL); } return TRUE; } dovecot-2.2.33.2/src/lib-lda/smtp-client.h0000644000175000017500000000224513165463624015045 00000000000000#ifndef SMTP_CLIENT_H #define SMTP_CLIENT_H struct smtp_client * ATTR_NULL(3) smtp_client_init(const struct lda_settings *set, const char *return_path); /* Add a new recipient */ void smtp_client_add_rcpt(struct smtp_client *client, const char *address); /* Get an output stream where the message can be written to. The recipients must already be added before calling this. */ struct ostream *smtp_client_send(struct smtp_client *client); void smtp_client_abort(struct smtp_client **client); /* Returns 1 on success, 0 on permanent failure (e.g. invalid destination), -1 on temporary failure. */ int smtp_client_deinit(struct smtp_client *client, const char **error_r); /* Same as smtp_client_deinit(), but timeout after given number of seconds. */ int smtp_client_deinit_timeout(struct smtp_client *client, unsigned int timeout_secs, const char **error_r); /* FIXME: obsolete API, remove in v2.3: */ struct smtp_client * ATTR_NULL(3) smtp_client_open(const struct lda_settings *set, const char *destination, const char *return_path, struct ostream **output_r); /* Returns sysexits-compatible return value */ int smtp_client_close(struct smtp_client *client); #endif dovecot-2.2.33.2/src/lib-lda/Makefile.am0000644000175000017500000000156113165463624014471 00000000000000noinst_LTLIBRARIES = liblda.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-smtp \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage liblda_la_SOURCES = \ duplicate.c \ lda-settings.c \ mail-deliver.c \ mail-send.c \ smtp-client.c headers = \ duplicate.h \ lda-settings.h \ mail-deliver.h \ mail-send.h \ smtp-client.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) deps=../lib-storage/libdovecot-storage.la ../lib-dovecot/libdovecot.la pkglib_LTLIBRARIES = libdovecot-lda.la libdovecot_lda_la_SOURCES = libdovecot_lda_la_LIBADD = liblda.la $(deps) libdovecot_lda_la_DEPENDENCIES = liblda.la $(deps) libdovecot_lda_la_LDFLAGS = -export-dynamic dovecot-2.2.33.2/src/lmtp/0002755000175000017500000000000013172375612012161 500000000000000dovecot-2.2.33.2/src/lmtp/Makefile.in0000644000175000017500000005524513172375574014166 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = lmtp$(EXEEXT) subdir = src/lmtp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_lmtp_OBJECTS = main.$(OBJEXT) client.$(OBJEXT) commands.$(OBJEXT) \ lmtp-proxy.$(OBJEXT) lmtp-settings.$(OBJEXT) lmtp_OBJECTS = $(am_lmtp_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lmtp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(lmtp_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lmtp_SOURCES) DIST_SOURCES = $(lmtp_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-smtp \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw lmtp_LDFLAGS = -export-dynamic lmtp_LDADD = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) lmtp_DEPENDENCIES = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) lmtp_SOURCES = \ main.c \ client.c \ commands.c \ lmtp-proxy.c \ lmtp-settings.c noinst_HEADERS = \ main.h \ client.h \ commands.h \ lmtp-proxy.h \ lmtp-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lmtp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lmtp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list lmtp$(EXEEXT): $(lmtp_OBJECTS) $(lmtp_DEPENDENCIES) $(EXTRA_lmtp_DEPENDENCIES) @rm -f lmtp$(EXEEXT) $(AM_V_CCLD)$(lmtp_LINK) $(lmtp_OBJECTS) $(lmtp_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commands.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-proxy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lmtp/main.h0000644000175000017500000000034013165463624013174 00000000000000#ifndef MAIN_H #define MAIN_H extern const char *dns_client_socket_path, *base_dir; extern struct mail_storage_service_ctx *storage_service; extern struct anvil_client *anvil; void listener_client_destroyed(void); #endif dovecot-2.2.33.2/src/lmtp/lmtp-proxy.c0000644000175000017500000002442213165463624014405 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "istream-sized.h" #include "ostream.h" #include "str.h" #include "time-util.h" #include "lmtp-client.h" #include "lmtp-proxy.h" #define LMTP_MAX_LINE_LEN 1024 struct lmtp_proxy_recipient { struct lmtp_proxy_connection *conn; const char *address; const char *reply; unsigned int idx; unsigned int rcpt_to_failed:1; unsigned int data_reply_received:1; }; struct lmtp_proxy_connection { struct lmtp_proxy *proxy; struct lmtp_proxy_rcpt_settings set; struct lmtp_client *client; struct istream *data_input; struct timeout *to; unsigned int finished:1; unsigned int failed:1; }; struct lmtp_proxy { pool_t pool; const char *mail_from; struct lmtp_proxy_settings set; ARRAY(struct lmtp_proxy_connection *) connections; ARRAY(struct lmtp_proxy_recipient *) rcpt_to; unsigned int next_data_reply_idx; struct timeout *to_finish; struct istream *data_input; struct ostream *client_output; unsigned int max_timeout_msecs; lmtp_proxy_finish_callback_t *finish_callback; void *finish_context; unsigned int finished:1; }; static void lmtp_conn_finish(void *context); struct lmtp_proxy * lmtp_proxy_init(const struct lmtp_proxy_settings *set, struct ostream *client_output) { struct lmtp_proxy *proxy; pool_t pool; i_assert(set->proxy_ttl > 0); o_stream_ref(client_output); pool = pool_alloconly_create("lmtp proxy", 1024); proxy = p_new(pool, struct lmtp_proxy, 1); proxy->pool = pool; proxy->client_output = client_output; proxy->set.my_hostname = p_strdup(pool, set->my_hostname); proxy->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); proxy->set.session_id = p_strdup(pool, set->session_id); proxy->set.source_ip = set->source_ip; proxy->set.source_port = set->source_port; proxy->set.proxy_ttl = set->proxy_ttl; i_array_init(&proxy->rcpt_to, 32); i_array_init(&proxy->connections, 32); return proxy; } static void lmtp_proxy_connections_deinit(struct lmtp_proxy *proxy) { struct lmtp_proxy_connection *const *conns; array_foreach(&proxy->connections, conns) { struct lmtp_proxy_connection *conn = *conns; lmtp_client_deinit(&conn->client); } } void lmtp_proxy_deinit(struct lmtp_proxy **_proxy) { struct lmtp_proxy *proxy = *_proxy; *_proxy = NULL; lmtp_proxy_connections_deinit(proxy); if (proxy->data_input != NULL) i_stream_unref(&proxy->data_input); if (proxy->client_output != NULL) o_stream_unref(&proxy->client_output); if (proxy->to_finish != NULL) timeout_remove(&proxy->to_finish); array_free(&proxy->rcpt_to); array_free(&proxy->connections); pool_unref(&proxy->pool); } void lmtp_proxy_mail_from(struct lmtp_proxy *proxy, const char *value) { proxy->mail_from = p_strdup(proxy->pool, value); } static struct lmtp_proxy_connection * lmtp_proxy_get_connection(struct lmtp_proxy *proxy, const struct lmtp_proxy_rcpt_settings *set) { struct lmtp_proxy_connection *const *conns, *conn; struct lmtp_client_settings client_set; i_assert(set->timeout_msecs > 0); array_foreach(&proxy->connections, conns) { conn = *conns; if (conn->set.port == set->port && strcmp(conn->set.host, set->host) == 0) return conn; } i_zero(&client_set); client_set.mail_from = proxy->mail_from; client_set.my_hostname = proxy->set.my_hostname; client_set.dns_client_socket_path = proxy->set.dns_client_socket_path; client_set.source_ip = proxy->set.source_ip; client_set.source_port = proxy->set.source_port; client_set.proxy_ttl = proxy->set.proxy_ttl; client_set.proxy_timeout_secs = set->timeout_msecs/1000; conn = p_new(proxy->pool, struct lmtp_proxy_connection, 1); conn->proxy = proxy; if (set->hostip.family == 0) conn->set.host = p_strdup(proxy->pool, set->host); else conn->set.host = p_strdup(proxy->pool, net_ip2addr(&set->hostip)); conn->set.port = set->port; conn->set.timeout_msecs = set->timeout_msecs; array_append(&proxy->connections, &conn, 1); conn->client = lmtp_client_init(&client_set, lmtp_conn_finish, conn); if (lmtp_client_connect_tcp(conn->client, set->protocol, conn->set.host, conn->set.port) < 0) conn->failed = TRUE; if (proxy->max_timeout_msecs < set->timeout_msecs) proxy->max_timeout_msecs = set->timeout_msecs; return conn; } static bool lmtp_proxy_send_data_replies(struct lmtp_proxy *proxy) { struct lmtp_proxy_recipient *const *rcpt; unsigned int i, count; o_stream_cork(proxy->client_output); rcpt = array_get(&proxy->rcpt_to, &count); for (i = proxy->next_data_reply_idx; i < count; i++) { if (!(rcpt[i]->rcpt_to_failed || rcpt[i]->data_reply_received)) break; o_stream_nsend_str(proxy->client_output, t_strconcat(rcpt[i]->reply, "\r\n", NULL)); } o_stream_uncork(proxy->client_output); proxy->next_data_reply_idx = i; return i == count; } static void lmtp_proxy_finish_timeout(struct lmtp_proxy *proxy) { i_assert(!proxy->finished); timeout_remove(&proxy->to_finish); proxy->finished = TRUE; proxy->finish_callback(proxy->finish_context); } static void lmtp_proxy_try_finish(struct lmtp_proxy *proxy) { if (proxy->finish_callback == NULL) { /* DATA command hasn't been sent yet */ return; } if (!lmtp_proxy_send_data_replies(proxy)) { /* we can't received reply from all clients yet */ return; } /* do the actual finishing in a timeout handler, since the finish callback causes the proxy to be destroyed and the code leading up to this function can be called from many different places. it's easier this way rather than having all the callers check if the proxy was already destroyed. */ if (proxy->to_finish == NULL) { proxy->to_finish = timeout_add(0, lmtp_proxy_finish_timeout, proxy); } } static void lmtp_conn_finish(void *context) { struct lmtp_proxy_connection *conn = context; conn->finished = TRUE; if (conn->to != NULL) timeout_remove(&conn->to); if (conn->data_input != NULL) i_stream_unref(&conn->data_input); lmtp_proxy_try_finish(conn->proxy); } static void lmtp_proxy_conn_rcpt_to(enum lmtp_client_result result, const char *reply, void *context) { struct lmtp_proxy_recipient *rcpt = context; struct lmtp_proxy_connection *conn = rcpt->conn; i_assert(rcpt->reply == NULL); rcpt->reply = p_strdup(conn->proxy->pool, reply); rcpt->rcpt_to_failed = result != LMTP_CLIENT_RESULT_OK; } static void lmtp_proxy_conn_data(enum lmtp_client_result result, const char *reply, void *context) { struct lmtp_proxy_recipient *rcpt = context; struct lmtp_proxy_connection *conn = rcpt->conn; const struct lmtp_client_times *times = lmtp_client_get_times(conn->client); string_t *msg; i_assert(!rcpt->rcpt_to_failed); i_assert(rcpt->reply != NULL); /* reset timeout in case there are a lot of RCPT TOs */ if (conn->to != NULL) timeout_reset(conn->to); rcpt->reply = p_strdup(conn->proxy->pool, reply); rcpt->data_reply_received = TRUE; msg = t_str_new(128); str_printfa(msg, "%s: ", conn->proxy->set.session_id); switch (result) { case LMTP_CLIENT_RESULT_OK: str_append(msg, "Sent message to"); break; case LMTP_CLIENT_RESULT_REMOTE_ERROR: case LMTP_CLIENT_RESULT_INTERNAL_ERROR: str_append(msg, "Failed to send message to"); break; } str_printfa(msg, " <%s> at %s:%u: %s (%u/%u at %u ms)", rcpt->address, conn->set.host, conn->set.port, reply, rcpt->idx + 1, array_count(&conn->proxy->rcpt_to), timeval_diff_msecs(&ioloop_timeval, ×->connect_started)); switch (result) { case LMTP_CLIENT_RESULT_OK: case LMTP_CLIENT_RESULT_REMOTE_ERROR: /* the problem isn't with the proxy, it's with the remote side. so the remote side will log an error, while for us this is just an info event */ i_info("%s", str_c(msg)); break; case LMTP_CLIENT_RESULT_INTERNAL_ERROR: i_error("%s", str_c(msg)); break; } lmtp_proxy_try_finish(conn->proxy); } int lmtp_proxy_add_rcpt(struct lmtp_proxy *proxy, const char *address, const struct lmtp_proxy_rcpt_settings *set) { struct lmtp_proxy_connection *conn; struct lmtp_proxy_recipient *rcpt; conn = lmtp_proxy_get_connection(proxy, set); if (conn->failed) return -1; rcpt = p_new(proxy->pool, struct lmtp_proxy_recipient, 1); rcpt->idx = array_count(&proxy->rcpt_to); rcpt->conn = conn; rcpt->address = p_strdup(proxy->pool, address); array_append(&proxy->rcpt_to, &rcpt, 1); lmtp_client_add_rcpt_params(conn->client, address, &set->params, lmtp_proxy_conn_rcpt_to, lmtp_proxy_conn_data, rcpt); return 0; } static void lmtp_proxy_conn_timeout(struct lmtp_proxy_connection *conn) { const char *line; line = t_strdup_printf(ERRSTR_TEMP_REMOTE_FAILURE " (timeout while waiting for reply to %s) <%s>", lmtp_client_state_to_string(conn->client), conn->proxy->set.session_id); lmtp_client_fail(conn->client, line); } void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input, lmtp_proxy_finish_callback_t *callback, void *context) { struct lmtp_proxy_connection *const *conns; uoff_t size; i_assert(data_input->seekable); i_assert(proxy->data_input == NULL); proxy->finish_callback = callback; proxy->finish_context = context; proxy->data_input = data_input; i_stream_ref(proxy->data_input); if (i_stream_get_size(proxy->data_input, TRUE, &size) < 0) { i_error("i_stream_get_size(data_input) failed: %s", i_stream_get_error(proxy->data_input)); size = (uoff_t)-1; } /* create the data_input streams first */ array_foreach(&proxy->connections, conns) { struct lmtp_proxy_connection *conn = *conns; if (conn->finished) { /* this connection had already failed */ continue; } conn->to = timeout_add(proxy->max_timeout_msecs, lmtp_proxy_conn_timeout, conn); if (size == (uoff_t)-1) conn->data_input = i_stream_create_limit(data_input, (uoff_t)-1); else conn->data_input = i_stream_create_sized(data_input, size); } /* now that all the streams are created, start reading them (reading them earlier could have caused the data_input parent's offset to change) */ array_foreach(&proxy->connections, conns) { struct lmtp_proxy_connection *conn = *conns; if (conn->data_input != NULL) { lmtp_client_send(conn->client, conn->data_input); lmtp_client_send_more(conn->client); } } /* finish if all of the connections have already failed */ lmtp_proxy_try_finish(proxy); } dovecot-2.2.33.2/src/lmtp/commands.h0000644000175000017500000000113213123174404014036 00000000000000#ifndef COMMANDS_H #define COMMANDS_H struct client; int cmd_lhlo(struct client *client, const char *args); int cmd_starttls(struct client *client); int cmd_mail(struct client *client, const char *args); int cmd_rcpt(struct client *client, const char *args); int cmd_quit(struct client *client, const char *args); int cmd_vrfy(struct client *client, const char *args); int cmd_rset(struct client *client, const char *args); int cmd_noop(struct client *client, const char *args); int cmd_data(struct client *client, const char *args); int cmd_xclient(struct client *client, const char *args); #endif dovecot-2.2.33.2/src/lmtp/main.c0000644000175000017500000000667513165463624013210 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "hostpid.h" #include "abspath.h" #include "restrict-access.h" #include "fd-close-on-exec.h" #include "anvil-client.h" #include "master-service.h" #include "master-service-settings.h" #include "master-interface.h" #include "mail-deliver.h" #include "mail-storage-service.h" #include "lda-settings.h" #include "lmtp-settings.h" #include "client.h" #include "main.h" #include #define DNS_CLIENT_SOCKET_PATH "dns-client" #define LMTP_MASTER_FIRST_LISTEN_FD 3 #define IS_STANDALONE() \ (getenv(MASTER_IS_PARENT_ENV) == NULL) const char *dns_client_socket_path, *base_dir; struct mail_storage_service_ctx *storage_service; struct anvil_client *anvil; static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); (void)client_create(conn->fd, conn->fd, conn); } static void drop_privileges(void) { struct restrict_access_settings set; const char *error; /* by default we don't drop any privileges, but keep running as root. */ restrict_access_get_env(&set); /* open config connection before dropping privileges */ struct master_service_settings_input input; struct master_service_settings_output output; i_zero(&input); input.module = "lmtp"; input.service = "lmtp"; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); restrict_access_by_env(NULL, FALSE); } static void main_init(void) { struct master_service_connection conn; if (IS_STANDALONE()) { i_zero(&conn); (void)client_create(STDIN_FILENO, STDOUT_FILENO, &conn); } dns_client_socket_path = t_abspath(DNS_CLIENT_SOCKET_PATH); mail_deliver_hooks_init(); } static void main_deinit(void) { clients_destroy(); if (anvil != NULL) anvil_client_deinit(&anvil); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &lda_setting_parser_info, &lmtp_setting_parser_info, NULL }; enum master_service_flags service_flags = MASTER_SERVICE_FLAG_USE_SSL_SETTINGS; enum mail_storage_service_flags storage_service_flags = MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT | MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT; int c; if (IS_STANDALONE()) { service_flags |= MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_STD_CLIENT; } else { service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN ; } master_service = master_service_init("lmtp", service_flags, &argc, &argv, "D"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': storage_service_flags |= MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS; break; default: return FATAL_DEFAULT; } } if (t_get_current_dir(&base_dir) < 0) i_fatal("getcwd() failed: %m"); drop_privileges(); master_service_init_log(master_service, t_strdup_printf("lmtp(%s): ", my_pid)); storage_service = mail_storage_service_init(master_service, set_roots, storage_service_flags); restrict_access_allow_coredumps(TRUE); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); mail_storage_service_deinit(&storage_service); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/lmtp/commands.c0000644000175000017500000011321113165463624014046 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "strescape.h" #include "hostpid.h" #include "istream.h" #include "istream-concat.h" #include "ostream.h" #include "istream-dot.h" #include "safe-mkstemp.h" #include "hex-dec.h" #include "time-util.h" #include "var-expand.h" #include "restrict-access.h" #include "settings-parser.h" #include "anvil-client.h" #include "master-service.h" #include "master-service-ssl.h" #include "iostream-ssl.h" #include "rfc822-parser.h" #include "message-date.h" #include "auth-master.h" #include "mail-storage-service.h" #include "index/raw/raw-storage.h" #include "lda-settings.h" #include "lmtp-settings.h" #include "mail-autoexpunge.h" #include "mail-namespace.h" #include "mail-deliver.h" #include "message-address.h" #include "main.h" #include "client.h" #include "commands.h" #include "lmtp-proxy.h" #define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error" #define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> " #define ERRSTR_TEMP_USERDB_FAIL \ ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure" #define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125) int cmd_lhlo(struct client *client, const char *args) { struct rfc822_parser_context parser; string_t *domain = t_str_new(128); const char *p; int ret = 0; if (*args == '\0') { client_send_line(client, "501 Missing hostname"); return 0; } /* domain / address-literal */ rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args), NULL); if (*args != '[') ret = rfc822_parse_dot_atom(&parser, domain); else { for (p = args+1; *p != ']'; p++) { if (*p == '\\' || *p == '[') break; } if (strcmp(p, "]") != 0) ret = -1; } if (ret < 0) { str_truncate(domain, 0); str_append(domain, "invalid"); } client_state_reset(client, "LHLO"); client_send_line(client, "250-%s", client->my_domain); if (master_service_ssl_is_enabled(master_service) && client->ssl_iostream == NULL) client_send_line(client, "250-STARTTLS"); if (client_is_trusted(client)) client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT"); client_send_line(client, "250-8BITMIME"); client_send_line(client, "250-ENHANCEDSTATUSCODES"); client_send_line(client, "250 PIPELINING"); i_free(client->lhlo); client->lhlo = i_strdup(str_c(domain)); client_state_set(client, "LHLO", ""); return 0; } int cmd_starttls(struct client *client) { struct ostream *plain_output = client->output; const char *error; if (client->ssl_iostream != NULL) { o_stream_nsend_str(client->output, "443 5.5.1 TLS is already active.\r\n"); return 0; } if (master_service_ssl_init(master_service, &client->input, &client->output, &client->ssl_iostream, &error) < 0) { i_error("TLS initialization failed: %s", error); o_stream_nsend_str(client->output, "454 4.7.0 Internal error, TLS not available.\r\n"); return 0; } o_stream_nsend_str(plain_output, "220 2.0.0 Begin TLS negotiation now.\r\n"); if (ssl_iostream_handshake(client->ssl_iostream) < 0) { client_destroy(client, NULL, NULL); return -1; } return 0; } static int parse_address(const char *str, const char **address_r, const char **rest_r) { const char *start; if (*str++ != '<') return -1; start = str; if (*str == '"') { /* "quoted-string"@domain */ for (str++; *str != '"'; str++) { if (*str == '\\') str++; if (*str == '\0') return -1; } str++; } for (; *str != '>'; str++) { if (*str == '\0' || *str == ' ') return -1; } *address_r = t_strdup_until(start, str); if (*str++ != '>') return -1; if (*str == ' ') str++; else if (*str != '\0') return -1; *rest_r = str; return 0; } static const char * parse_xtext(struct client *client, const char *value) { const char *p; string_t *str; unsigned int i; p = strchr(value, '+'); if (p == NULL) return p_strdup(client->state_pool, value); /* hexchar = ASCII "+" immediately followed by two upper case hexadecimal digits */ str = t_str_new(128); for (i = 0; value[i] != '\0'; i++) { if (value[i] == '+' && value[i+1] != '\0' && value[i+2] != '\0') { str_append_c(str, hex2dec((const void *)(value+i+1), 2)); i += 2; } else { str_append_c(str, value[i]); } } return p_strdup(client->state_pool, str_c(str)); } static void lmtp_anvil_init(void) { if (anvil == NULL) { const char *path = t_strdup_printf("%s/anvil", base_dir); anvil = anvil_client_init(path, NULL, 0); } } int cmd_mail(struct client *client, const char *args) { const char *addr, *const *argv; if (client->state.mail_from != NULL) { client_send_line(client, "503 5.5.1 MAIL already given"); return 0; } if (strncasecmp(args, "FROM:", 5) != 0 || parse_address(args + 5, &addr, &args) < 0) { client_send_line(client, "501 5.5.4 Invalid parameters"); return 0; } argv = t_strsplit(args, " "); for (; *argv != NULL; argv++) { if (strcasecmp(*argv, "BODY=7BIT") == 0) client->state.mail_body_7bit = TRUE; else if (strcasecmp(*argv, "BODY=8BITMIME") == 0) client->state.mail_body_8bitmime = TRUE; else { client_send_line(client, "501 5.5.4 Unsupported options"); return 0; } } client->state.mail_from = p_strdup(client->state_pool, addr); p_array_init(&client->state.rcpt_to, client->state_pool, 64); client_send_line(client, "250 2.1.0 OK"); client_state_set(client, "MAIL FROM", client->state.mail_from); if (client->lmtp_set->lmtp_user_concurrency_limit > 0) { /* connect to anvil before dropping privileges */ lmtp_anvil_init(); } client->state.mail_from_timeval = ioloop_timeval; return 0; } static bool client_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set, const char *const *args, const char **address) { const char *p, *key, *value; bool proxying = FALSE, port_set = FALSE; for (; *args != NULL; args++) { p = strchr(*args, '='); if (p == NULL) { key = *args; value = ""; } else { key = t_strdup_until(*args, p); value = p + 1; } if (strcmp(key, "proxy") == 0) proxying = TRUE; else if (strcmp(key, "host") == 0) set->host = value; else if (strcmp(key, "hostip") == 0) { if (net_addr2ip(value, &set->hostip) < 0) { i_error("proxy: Invalid hostip %s", value); return FALSE; } } else if (strcmp(key, "port") == 0) { if (net_str2port(value, &set->port) < 0) { i_error("proxy: Invalid port number %s", value); return FALSE; } port_set = TRUE; } else if (strcmp(key, "proxy_timeout") == 0) { if (str_to_uint(value, &set->timeout_msecs) < 0) { i_error("proxy: Invalid proxy_timeout value %s", value); return FALSE; } set->timeout_msecs *= 1000; } else if (strcmp(key, "protocol") == 0) { if (strcmp(value, "lmtp") == 0) set->protocol = LMTP_CLIENT_PROTOCOL_LMTP; else if (strcmp(value, "smtp") == 0) { set->protocol = LMTP_CLIENT_PROTOCOL_SMTP; if (!port_set) set->port = 25; } else { i_error("proxy: Unknown protocol %s", value); return FALSE; } } else if (strcmp(key, "user") == 0 || strcmp(key, "destuser") == 0) { /* changing the username */ *address = value; } else { /* just ignore it */ } } if (proxying && set->host == NULL) { i_error("proxy: host not given"); return FALSE; } return proxying; } static bool client_proxy_is_ourself(const struct client *client, const struct lmtp_proxy_rcpt_settings *set) { struct ip_addr ip; if (set->port != client->local_port) return FALSE; if (set->hostip.family != 0) ip = set->hostip; else { if (net_addr2ip(set->host, &ip) < 0) return FALSE; } if (!net_ip_compare(&ip, &client->local_ip)) return FALSE; return TRUE; } static const char * address_add_detail(struct client *client, const char *username, const char *detail) { const char *delim = client->unexpanded_lda_set->recipient_delimiter; const char *domain; domain = strchr(username, '@'); if (domain == NULL) return t_strconcat(username, delim, detail, NULL); else { username = t_strdup_until(username, domain); return t_strconcat(username, delim, detail, domain, NULL); } } static bool client_proxy_rcpt(struct client *client, const char *address, const char *username, const char *detail, const struct lmtp_recipient_params *params) { struct auth_master_connection *auth_conn; struct lmtp_proxy_rcpt_settings set; struct auth_user_info info; struct mail_storage_service_input input; const char *args, *const *fields, *errstr, *orig_username = username; pool_t pool; int ret; i_zero(&input); input.module = input.service = "lmtp"; mail_storage_service_init_settings(storage_service, &input); i_zero(&info); info.service = master_service_get_name(master_service); info.local_ip = client->local_ip; info.remote_ip = client->remote_ip; info.local_port = client->local_port; info.remote_port = client->remote_port; pool = pool_alloconly_create("auth lookup", 1024); auth_conn = mail_storage_service_get_auth_conn(storage_service); ret = auth_master_pass_lookup(auth_conn, username, &info, pool, &fields); if (ret <= 0) { errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) : t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address); pool_unref(&pool); if (ret < 0) { client_send_line(client, "%s", errstr); return TRUE; } else { /* user not found from passdb. try userdb also. */ return FALSE; } } i_zero(&set); set.port = client->local_port; set.protocol = LMTP_CLIENT_PROTOCOL_LMTP; set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS; set.params = *params; if (!client_proxy_rcpt_parse_fields(&set, fields, &username)) { /* not proxying this user */ pool_unref(&pool); return FALSE; } if (strcmp(username, orig_username) != 0) { /* username changed. change the address as well */ if (*detail == '\0') address = username; else address = address_add_detail(client, username, detail); } else if (client_proxy_is_ourself(client, &set)) { i_error("Proxying to <%s> loops to itself", username); client_send_line(client, "554 5.4.6 <%s> " "Proxying loops to itself", address); pool_unref(&pool); return TRUE; } if (client->proxy_ttl <= 1) { i_error("Proxying to <%s> appears to be looping (TTL=0)", username); client_send_line(client, "554 5.4.6 <%s> " "Proxying appears to be looping (TTL=0)", username); pool_unref(&pool); return TRUE; } if (array_count(&client->state.rcpt_to) != 0) { client_send_line(client, "451 4.3.0 <%s> " "Can't handle mixed proxy/non-proxy destinations", address); pool_unref(&pool); return TRUE; } if (client->proxy == NULL) { struct lmtp_proxy_settings proxy_set; i_zero(&proxy_set); proxy_set.my_hostname = client->my_domain; proxy_set.dns_client_socket_path = dns_client_socket_path; proxy_set.session_id = client->state.session_id; proxy_set.source_ip = client->remote_ip; proxy_set.source_port = client->remote_port; proxy_set.proxy_ttl = client->proxy_ttl-1; client->proxy = lmtp_proxy_init(&proxy_set, client->output); if (client->state.mail_body_8bitmime) args = " BODY=8BITMIME"; else if (client->state.mail_body_7bit) args = " BODY=7BIT"; else args = ""; lmtp_proxy_mail_from(client->proxy, t_strdup_printf( "<%s>%s", client->state.mail_from, args)); } if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0) client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE); else client_send_line(client, "250 2.1.5 OK"); pool_unref(&pool); return TRUE; } static const char *lmtp_unescape_address(const char *name) { string_t *str; const char *p; if (*name != '"') return name; /* quoted-string local-part. drop the quotes unless there's a '@' character inside or there's an error. */ str = t_str_new(128); for (p = name+1; *p != '"'; p++) { if (*p == '\0') return name; if (*p == '\\') { if (p[1] == '\0') { /* error */ return name; } p++; } if (*p == '@') return name; str_append_c(str, *p); } p++; if (*p != '@' && *p != '\0') return name; str_append(str, p); return str_c(str); } static void lmtp_address_translate(struct client *client, const char **address) { const char *transpos = client->lmtp_set->lmtp_address_translate; const char *p, *nextstr, *addrpos = *address; size_t len; string_t *username, *domain, *dest = NULL; if (*transpos == '\0') return; username = t_str_new(64); domain = t_str_new(64); /* check that string matches up to the first '%' */ p = strchr(transpos, '%'); if (p == NULL) len = strlen(transpos); else len = p-transpos; if (strncmp(transpos, addrpos, len) != 0) return; transpos += len; addrpos += len; while (*transpos != '\0') { switch (transpos[1]) { case 'n': case 'u': dest = username; break; case 'd': dest = domain; break; default: return; } transpos += 2; /* find where the next string starts */ if (*transpos == '\0') { str_append(dest, addrpos); break; } p = strchr(transpos, '%'); if (p == NULL) nextstr = transpos; else nextstr = t_strdup_until(transpos, p); p = strstr(addrpos, nextstr); if (p == NULL) return; str_append_n(dest, addrpos, p-addrpos); len = strlen(nextstr); transpos += len; addrpos = p + len; } str_append_c(username, '@'); if (domain != NULL) str_append_str(username, domain); *address = str_c(username); } static void client_send_line_overquota(struct client *client, const struct mail_recipient *rcpt, const char *error) { struct lda_settings *lda_set = mail_storage_service_user_get_set(rcpt->service_user)[1]; client_send_line(client, "%s <%s> %s", lda_set->quota_full_tempfail ? "452 4.2.2" : "552 5.2.2", rcpt->address, error); } static int lmtp_rcpt_to_is_over_quota(struct client *client, const struct mail_recipient *rcpt) { struct mail_user *user; struct mail_namespace *ns; struct mailbox *box; struct mailbox_status status; const char *errstr; enum mail_error error; int ret; if (!client->lmtp_set->lmtp_rcpt_check_quota) return 0; /* mail user will be created second time when mail is saved, so it's session_id needs to be different, but second time session_id needs to be the same as rcpt session_id and mail user session id for the first rcpt should not overlap with session id of the second recipient, so add custom ":quota" suffix to the session_id without session_id counter increment, so next time mail user will get the same session id as rcpt */ ret = mail_storage_service_next_with_session_suffix(storage_service, rcpt->service_user, "quota", &user); if (ret < 0) return -1; ns = mail_namespace_find_inbox(user->namespaces); box = mailbox_alloc(ns->list, "INBOX", 0); mailbox_set_reason(box, "over-quota check"); ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status); if (ret < 0) { errstr = mailbox_get_last_error(box, &error); if (error == MAIL_ERROR_NOQUOTA) { client_send_line_overquota(client, rcpt, errstr); ret = 1; } else { i_error("mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) " "failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); } } mailbox_free(&box); mail_user_unref(&user); return ret; } static bool cmd_rcpt_finish(struct client *client, struct mail_recipient *rcpt) { int ret; if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) { if (ret < 0) { client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->address); } mail_storage_service_user_unref(&rcpt->service_user); return FALSE; } array_append(&client->state.rcpt_to, &rcpt, 1); client_send_line(client, "250 2.1.5 OK"); return TRUE; } static void rcpt_anvil_lookup_callback(const char *reply, void *context) { struct mail_recipient *rcpt = context; struct client *client = rcpt->client; const struct mail_storage_service_input *input; unsigned int parallel_count = 0; rcpt->anvil_query = NULL; if (reply == NULL) { /* lookup failed */ } else if (str_to_uint(reply, ¶llel_count) < 0) { i_error("Invalid reply from anvil: %s", reply); } if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) { client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX "Too many concurrent deliveries for user", rcpt->address); mail_storage_service_user_unref(&rcpt->service_user); } else if (cmd_rcpt_finish(client, rcpt)) { rcpt->anvil_connect_sent = TRUE; input = mail_storage_service_user_get_input(rcpt->service_user); master_service_anvil_send(master_service, t_strconcat( "CONNECT\t", my_pid, "\t", master_service_get_name(master_service), "/", input->username, "\n", NULL)); } client_io_reset(client); client_input_handle(client); } int cmd_rcpt(struct client *client, const char *args) { struct mail_recipient *rcpt; struct mail_storage_service_input input; const char *params, *address, *username, *detail, *prefix; const char *const *argv; const char *error = NULL; int ret = 0; if (client->state.mail_from == NULL) { client_send_line(client, "503 5.5.1 MAIL needed first"); return 0; } if (strncasecmp(args, "TO:", 3) != 0 || parse_address(args + 3, &address, ¶ms) < 0) { client_send_line(client, "501 5.5.4 Invalid parameters"); return 0; } rcpt = p_new(client->state_pool, struct mail_recipient, 1); rcpt->client = client; address = lmtp_unescape_address(address); argv = t_strsplit(params, " "); for (; *argv != NULL; argv++) { if (strncasecmp(*argv, "ORCPT=", 6) == 0) { rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6); } else { client_send_line(client, "501 5.5.4 Unsupported options"); return 0; } } message_detail_address_parse(client->unexpanded_lda_set->recipient_delimiter, address, &username, &detail); client_state_set(client, "RCPT TO", address); if (client->lmtp_set->lmtp_proxy) { if (client_proxy_rcpt(client, address, username, detail, &rcpt->params)) return 0; } /* Use a unique session_id for each mail delivery. This is especially important for stats process to not see duplicate sessions. */ if (array_count(&client->state.rcpt_to) == 0) rcpt->session_id = client->state.session_id; else { rcpt->session_id = p_strdup_printf(client->state_pool, "%s:%u", client->state.session_id, array_count(&client->state.rcpt_to)+1); } i_zero(&input); input.module = input.service = "lmtp"; input.username = username; input.local_ip = client->local_ip; input.remote_ip = client->remote_ip; input.local_port = client->local_port; input.remote_port = client->remote_port; input.session_id = rcpt->session_id; ret = mail_storage_service_lookup(storage_service, &input, &rcpt->service_user, &error); if (ret < 0) { prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX, username); client_send_line(client, "%s%s", prefix, error); return 0; } if (ret == 0) { client_send_line(client, "550 5.1.1 <%s> User doesn't exist: %s", address, username); return 0; } if (client->proxy != NULL) { /* NOTE: if this restriction is ever removed, we'll also need to send different message bodies to local and proxy (with and without Return-Path: header) */ client_send_line(client, "451 4.3.0 <%s> " "Can't handle mixed proxy/non-proxy destinations", address); mail_storage_service_user_unref(&rcpt->service_user); return 0; } lmtp_address_translate(client, &address); rcpt->address = p_strdup(client->state_pool, address); rcpt->detail = p_strdup(client->state_pool, detail); if (client->lmtp_set->lmtp_user_concurrency_limit == 0) { (void)cmd_rcpt_finish(client, rcpt); return 0; } else { /* NOTE: username may change as the result of the userdb lookup. Look up the new one via service_user. */ const struct mail_storage_service_input *input = mail_storage_service_user_get_input(rcpt->service_user); const char *query = t_strconcat("LOOKUP\t", master_service_get_name(master_service), "/", str_tabescape(input->username), NULL); io_remove(&client->io); rcpt->anvil_query = anvil_client_query(anvil, query, rcpt_anvil_lookup_callback, rcpt); /* stop processing further commands while anvil query is pending */ return rcpt->anvil_query == NULL ? 0 : -1; } } int cmd_quit(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "221 2.0.0 OK"); /* don't log the (state name) for successful QUITs */ i_info("Disconnect from %s: Successful quit", client_remote_id(client)); client->disconnected = TRUE; client_destroy(client, NULL, NULL); return -1; } int cmd_vrfy(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "252 2.3.3 Try RCPT instead"); return 0; } int cmd_rset(struct client *client, const char *args ATTR_UNUSED) { client_state_reset(client, "RSET"); client_send_line(client, "250 2.0.0 OK"); return 0; } int cmd_noop(struct client *client, const char *args ATTR_UNUSED) { client_send_line(client, "250 2.0.0 OK"); return 0; } static bool orcpt_get_valid_rfc822(const char *orcpt, const char **addr_r) { if (orcpt == NULL || strncasecmp(orcpt, "rfc822;", 7) != 0) return FALSE; /* FIXME: we should verify the address further */ *addr_r = orcpt + 7; return TRUE; } static int client_deliver(struct client *client, const struct mail_recipient *rcpt, struct mail *src_mail, struct mail_deliver_session *session) { struct mail_deliver_context dctx; struct mail_storage *storage; const struct mail_storage_service_input *input; const struct mail_storage_settings *mail_set; struct lda_settings *lda_set; struct mail_namespace *ns; struct setting_parser_context *set_parser; struct timeval delivery_time_started; void **sets; const char *line, *error, *username; string_t *str; enum mail_error mail_error; int ret; input = mail_storage_service_user_get_input(rcpt->service_user); username = t_strdup(input->username); mail_set = mail_storage_service_user_get_mail_set(rcpt->service_user); set_parser = mail_storage_service_user_get_settings_parser(rcpt->service_user); if (client->proxy_timeout_secs > 0 && (mail_set->mail_max_lock_timeout == 0 || mail_set->mail_max_lock_timeout > client->proxy_timeout_secs)) { /* set lock timeout waits to be less than when proxy has advertised that it's going to timeout the connection. this avoids duplicate deliveries in case the delivery succeeds after the proxy has already disconnected from us. */ line = t_strdup_printf("mail_max_lock_timeout=%u", client->proxy_timeout_secs <= 1 ? 1 : client->proxy_timeout_secs-1); if (settings_parse_line(set_parser, line) < 0) i_unreached(); } /* get the timestamp before user is created, since it starts the I/O */ io_loop_time_refresh(); delivery_time_started = ioloop_timeval; client_state_set(client, "DATA", username); i_set_failure_prefix("lmtp(%s, %s): ", my_pid, username); if (mail_storage_service_next(storage_service, rcpt->service_user, &client->state.dest_user) < 0) { client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->address); return -1; } str = t_str_new(256); var_expand_with_funcs(str, client->state.dest_user->set->mail_log_prefix, mail_user_var_expand_table(client->state.dest_user), mail_user_var_expand_func_table, client->state.dest_user); i_set_failure_prefix("%s", str_c(str)); sets = mail_storage_service_user_get_set(rcpt->service_user); lda_set = sets[1]; settings_var_expand(&lda_setting_parser_info, lda_set, client->pool, mail_user_var_expand_table(client->state.dest_user)); i_zero(&dctx); dctx.session = session; dctx.pool = session->pool; dctx.set = lda_set; dctx.timeout_secs = LDA_SUBMISSION_TIMEOUT_SECS; dctx.session_id = rcpt->session_id; dctx.src_mail = src_mail; dctx.src_envelope_sender = client->state.mail_from; dctx.dest_user = client->state.dest_user; dctx.session_time_msecs = timeval_diff_msecs(&client->state.data_end_timeval, &client->state.mail_from_timeval); dctx.delivery_time_started = delivery_time_started; if (orcpt_get_valid_rfc822(rcpt->params.dsn_orcpt, &dctx.dest_addr)) { /* used ORCPT */ } else if (*dctx.set->lda_original_recipient_header != '\0') { dctx.dest_addr = mail_deliver_get_address(src_mail, dctx.set->lda_original_recipient_header); } if (dctx.dest_addr == NULL) dctx.dest_addr = rcpt->address; dctx.final_dest_addr = rcpt->address; if (*rcpt->detail == '\0' || !client->lmtp_set->lmtp_save_to_detail_mailbox) dctx.dest_mailbox_name = "INBOX"; else { ns = mail_namespace_find_inbox(dctx.dest_user->namespaces); dctx.dest_mailbox_name = t_strconcat(ns->prefix, rcpt->detail, NULL); } dctx.save_dest_mail = array_count(&client->state.rcpt_to) > 1 && client->state.first_saved_mail == NULL; if (mail_deliver(&dctx, &storage) == 0) { if (dctx.dest_mail != NULL) { i_assert(client->state.first_saved_mail == NULL); client->state.first_saved_mail = dctx.dest_mail; } client_send_line(client, "250 2.0.0 <%s> %s Saved", rcpt->address, rcpt->session_id); ret = 0; } else if (dctx.tempfail_error != NULL) { client_send_line(client, "451 4.2.0 <%s> %s", rcpt->address, dctx.tempfail_error); ret = -1; } else if (storage != NULL) { error = mail_storage_get_last_error(storage, &mail_error); if (mail_error == MAIL_ERROR_NOQUOTA) { client_send_line_overquota(client, rcpt, error); } else { client_send_line(client, "451 4.2.0 <%s> %s", rcpt->address, error); } ret = -1; } else { /* This shouldn't happen */ i_error("BUG: Saving failed to unknown storage"); client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->address); ret = -1; } return ret; } static bool client_rcpt_to_is_last(struct client *client) { return client->state.rcpt_idx >= array_count(&client->state.rcpt_to); } static bool client_deliver_next(struct client *client, struct mail *src_mail, struct mail_deliver_session *session) { struct mail_recipient *const *rcpts; unsigned int count; int ret; rcpts = array_get(&client->state.rcpt_to, &count); while (client->state.rcpt_idx < count) { ret = client_deliver(client, rcpts[client->state.rcpt_idx], src_mail, session); client_state_set(client, "DATA", ""); i_set_failure_prefix("lmtp(%s): ", my_pid); client->state.rcpt_idx++; if (ret == 0) return TRUE; /* failed. try the next one. */ if (client->state.dest_user != NULL) { if (client_rcpt_to_is_last(client)) mail_user_autoexpunge(client->state.dest_user); mail_user_unref(&client->state.dest_user); } } return FALSE; } static void client_rcpt_fail_all(struct client *client) { struct mail_recipient *const *rcptp; array_foreach(&client->state.rcpt_to, rcptp) { client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, (*rcptp)->address); } } static struct istream *client_get_input(struct client *client) { struct client_state *state = &client->state; struct istream *cinput, *inputs[3]; inputs[0] = i_stream_create_from_data(state->added_headers, strlen(state->added_headers)); if (state->mail_data_output != NULL) { o_stream_unref(&state->mail_data_output); inputs[1] = i_stream_create_fd(state->mail_data_fd, MAIL_READ_FULL_BLOCK_SIZE, FALSE); i_stream_set_init_buffer_size(inputs[1], MAIL_READ_FULL_BLOCK_SIZE); } else { inputs[1] = i_stream_create_from_data(state->mail_data->data, state->mail_data->used); } inputs[2] = NULL; cinput = i_stream_create_concat(inputs); i_stream_set_name(cinput, ""); i_stream_unref(&inputs[0]); i_stream_unref(&inputs[1]); return cinput; } static int client_open_raw_mail(struct client *client, struct istream *input) { static const char *wanted_headers[] = { "From", "To", "Message-ID", "Subject", "Return-Path", NULL }; struct mailbox *box; struct mailbox_transaction_context *trans; struct mailbox_header_lookup_ctx *headers_ctx; enum mail_error error; if (raw_mailbox_alloc_stream(client->raw_mail_user, input, (time_t)-1, client->state.mail_from, &box) < 0) { i_error("Can't open delivery mail as raw: %s", mailbox_get_last_internal_error(box, &error)); mailbox_free(&box); client_rcpt_fail_all(client); return -1; } trans = mailbox_transaction_begin(box, 0); headers_ctx = mailbox_header_lookup_init(box, wanted_headers); client->state.raw_mail = mail_alloc(trans, 0, headers_ctx); mailbox_header_lookup_unref(&headers_ctx); mail_set_seq(client->state.raw_mail, 1); return 0; } static void client_input_data_write_local(struct client *client, struct istream *input) { struct mail_deliver_session *session; struct mail *src_mail; uid_t old_uid, first_uid = (uid_t)-1; if (client_open_raw_mail(client, input) < 0) return; session = mail_deliver_session_init(); old_uid = geteuid(); src_mail = client->state.raw_mail; while (client_deliver_next(client, src_mail, session)) { if (client->state.first_saved_mail == NULL || client->state.first_saved_mail == src_mail) { if (client_rcpt_to_is_last(client)) mail_user_autoexpunge(client->state.dest_user); mail_user_unref(&client->state.dest_user); } else { /* use the first saved message to save it elsewhere too. this might allow hard linking the files. */ client->state.dest_user = NULL; src_mail = client->state.first_saved_mail; first_uid = geteuid(); i_assert(first_uid != 0); } } mail_deliver_session_deinit(&session); if (client->state.first_saved_mail != NULL) { struct mail *mail = client->state.first_saved_mail; struct mailbox_transaction_context *trans = mail->transaction; struct mailbox *box = trans->box; struct mail_user *user = box->storage->user; /* just in case these functions are going to write anything, change uid back to user's own one */ if (first_uid != old_uid) { if (seteuid(0) < 0) i_fatal("seteuid(0) failed: %m"); if (seteuid(first_uid) < 0) i_fatal("seteuid() failed: %m"); } mail_free(&mail); mailbox_transaction_rollback(&trans); mailbox_free(&box); mail_user_autoexpunge(user); mail_user_unref(&user); } if (old_uid == 0) { /* switch back to running as root, since that's what we're practically doing anyway. it's also important in case we lose e.g. config connection and need to reconnect to it. */ if (seteuid(0) < 0) i_fatal("seteuid(0) failed: %m"); /* enable core dumping again. we need to chdir also to root-owned directory to get core dumps. */ restrict_access_allow_coredumps(TRUE); if (chdir(base_dir) < 0) i_error("chdir(%s) failed: %m", base_dir); } } static void client_input_data_finish(struct client *client) { client_io_reset(client); client_state_reset(client, "DATA finished"); if (i_stream_have_bytes_left(client->input)) client_input_handle(client); } static void client_proxy_finish(void *context) { struct client *client = context; lmtp_proxy_deinit(&client->proxy); client_input_data_finish(client); } static const char *client_get_added_headers(struct client *client) { string_t *str = t_str_new(200); void **sets; const struct lmtp_settings *lmtp_set; const char *host, *rcpt_to = NULL; if (array_count(&client->state.rcpt_to) == 1) { struct mail_recipient *const *rcptp = array_idx(&client->state.rcpt_to, 0); sets = mail_storage_service_user_get_set((*rcptp)->service_user); lmtp_set = sets[2]; switch (lmtp_set->parsed_lmtp_hdr_delivery_address) { case LMTP_HDR_DELIVERY_ADDRESS_NONE: break; case LMTP_HDR_DELIVERY_ADDRESS_FINAL: rcpt_to = (*rcptp)->address; break; case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL: if (!orcpt_get_valid_rfc822((*rcptp)->params.dsn_orcpt, &rcpt_to)) rcpt_to = (*rcptp)->address; break; } } /* don't set Return-Path when proxying so it won't get added twice */ if (array_count(&client->state.rcpt_to) > 0) { str_printfa(str, "Return-Path: <%s>\r\n", client->state.mail_from); if (rcpt_to != NULL) str_printfa(str, "Delivered-To: %s\r\n", rcpt_to); } str_printfa(str, "Received: from %s", client->lhlo); host = net_ip2addr(&client->remote_ip); if (host[0] != '\0') str_printfa(str, " ([%s])", host); str_append(str, "\r\n"); if (client->ssl_iostream != NULL) { str_printfa(str, "\t(using %s)\r\n", ssl_iostream_get_security_string(client->ssl_iostream)); } str_printfa(str, "\tby %s with LMTP id %s", client->my_domain, client->state.session_id); str_append(str, "\r\n\t"); if (rcpt_to != NULL) str_printfa(str, "for <%s>", rcpt_to); str_printfa(str, "; %s\r\n", message_date_create(ioloop_time)); return str_c(str); } static void client_input_data_write(struct client *client) { struct istream *input; /* stop handling client input until saving/proxying is finished */ if (client->to_idle != NULL) timeout_remove(&client->to_idle); io_remove(&client->io); i_stream_destroy(&client->dot_input); client->state.data_end_timeval = ioloop_timeval; input = client_get_input(client); if (array_count(&client->state.rcpt_to) != 0) client_input_data_write_local(client, input); if (client->proxy != NULL) { client_state_set(client, "DATA", "proxying"); lmtp_proxy_start(client->proxy, input, client_proxy_finish, client); } else { client_input_data_finish(client); } i_stream_unref(&input); } static int client_input_add_file(struct client *client, const unsigned char *data, size_t size) { struct client_state *state = &client->state; string_t *path; int fd; if (state->mail_data_output != NULL) { /* continue writing to file */ if (o_stream_send(state->mail_data_output, data, size) != (ssize_t)size) return -1; return 0; } /* move everything to a temporary file. */ path = t_str_new(256); mail_user_set_get_temp_prefix(path, client->raw_mail_user->set); fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("Temp file creation to %s failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } state->mail_data_fd = fd; state->mail_data_output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_set_name(state->mail_data_output, str_c(path)); o_stream_cork(state->mail_data_output); o_stream_nsend(state->mail_data_output, state->mail_data->data, state->mail_data->used); o_stream_nsend(client->state.mail_data_output, data, size); if (o_stream_nfinish(client->state.mail_data_output) < 0) { i_error("write(%s) failed: %s", str_c(path), o_stream_get_error(client->state.mail_data_output)); return -1; } return 0; } static int client_input_add(struct client *client, const unsigned char *data, size_t size) { if (client->state.mail_data->used + size <= CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE && client->state.mail_data_output == NULL) { buffer_append(client->state.mail_data, data, size); return 0; } else { return client_input_add_file(client, data, size); } } static void client_input_data_handle(struct client *client) { const unsigned char *data; size_t size; ssize_t ret; while ((ret = i_stream_read(client->dot_input)) > 0 || ret == -2) { data = i_stream_get_data(client->dot_input, &size); if (client_input_add(client, data, size) < 0) { client_destroy(client, "451 4.3.0", "Temporary internal failure"); return; } i_stream_skip(client->dot_input, size); } if (ret == 0) return; if (client->dot_input->stream_errno != 0) { /* client probably disconnected */ client_destroy(client, NULL, NULL); return; } /* the ending "." line was seen. begin saving the mail. */ client_input_data_write(client); } static void client_input_data(struct client *client) { if (client_input_read(client) < 0) return; client_input_data_handle(client); } int cmd_data(struct client *client, const char *args ATTR_UNUSED) { if (client->state.mail_from == NULL) { client_send_line(client, "503 5.5.1 MAIL needed first"); return 0; } if (array_count(&client->state.rcpt_to) == 0 && client->proxy == NULL) { client_send_line(client, "554 5.5.1 No valid recipients"); return 0; } client->state.added_headers = p_strdup(client->state_pool, client_get_added_headers(client)); i_assert(client->state.mail_data == NULL); client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64); i_assert(client->dot_input == NULL); client->dot_input = i_stream_create_dot(client->input, TRUE); client_send_line(client, "354 OK"); /* send the DATA reply immediately before we start handling any data */ o_stream_uncork(client->output); io_remove(&client->io); client_state_set(client, "DATA", ""); client->io = io_add(client->fd_in, IO_READ, client_input_data, client); client_input_data_handle(client); return -1; } int cmd_xclient(struct client *client, const char *args) { const char *const *tmp; struct ip_addr remote_ip; in_port_t remote_port = 0; unsigned int ttl = UINT_MAX, timeout_secs = 0; bool args_ok = TRUE; if (!client_is_trusted(client)) { client_send_line(client, "550 You are not from trusted IP"); return 0; } remote_ip.family = 0; for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { if (strncasecmp(*tmp, "ADDR=", 5) == 0) { if (net_addr2ip(*tmp + 5, &remote_ip) < 0) args_ok = FALSE; } else if (strncasecmp(*tmp, "PORT=", 5) == 0) { if (net_str2port(*tmp + 5, &remote_port) < 0) args_ok = FALSE; } else if (strncasecmp(*tmp, "TTL=", 4) == 0) { if (str_to_uint(*tmp + 4, &ttl) < 0) args_ok = FALSE; } else if (strncasecmp(*tmp, "TIMEOUT=", 8) == 0) { if (str_to_uint(*tmp + 8, &timeout_secs) < 0) args_ok = FALSE; } } if (!args_ok) { client_send_line(client, "501 Invalid parameters"); return 0; } /* args ok, set them and reset the state */ client_state_reset(client, "XCLIENT"); if (remote_ip.family != 0) client->remote_ip = remote_ip; if (remote_port != 0) client->remote_port = remote_port; if (ttl != UINT_MAX) client->proxy_ttl = ttl; client->proxy_timeout_secs = timeout_secs; client_send_line(client, "220 %s %s", client->my_domain, client->lmtp_set->login_greeting); return 0; } dovecot-2.2.33.2/src/lmtp/lmtp-settings.h0000644000175000017500000000160413123174404015053 00000000000000#ifndef LMTP_SETTINGS_H #define LMTP_SETTINGS_H struct lda_settings; struct lmtp_settings; /* */ enum lmtp_hdr_delivery_address { LMTP_HDR_DELIVERY_ADDRESS_NONE, LMTP_HDR_DELIVERY_ADDRESS_FINAL, LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL }; /* */ struct lmtp_settings { bool lmtp_proxy; bool lmtp_save_to_detail_mailbox; bool lmtp_rcpt_check_quota; unsigned int lmtp_user_concurrency_limit; const char *lmtp_address_translate; const char *lmtp_hdr_delivery_address; const char *login_greeting; const char *login_trusted_networks; enum lmtp_hdr_delivery_address parsed_lmtp_hdr_delivery_address; }; extern const struct setting_parser_info lmtp_setting_parser_info; void lmtp_settings_dup(const struct setting_parser_context *set_parser, pool_t pool, struct lmtp_settings **lmtp_set_r, struct lda_settings **lda_set_r); #endif dovecot-2.2.33.2/src/lmtp/client.c0000644000175000017500000002662113165463624013533 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "base64.h" #include "str.h" #include "llist.h" #include "iostream.h" #include "istream.h" #include "ostream.h" #include "hostpid.h" #include "process-title.h" #include "var-expand.h" #include "settings-parser.h" #include "anvil-client.h" #include "master-service.h" #include "master-service-ssl.h" #include "master-service-settings.h" #include "iostream-ssl.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "raw-storage.h" #include "main.h" #include "lda-settings.h" #include "lmtp-settings.h" #include "lmtp-proxy.h" #include "commands.h" #include "client.h" #include #define CLIENT_IDLE_TIMEOUT_MSECS (1000*60*5) #define CLIENT_MAX_INPUT_SIZE 4096 static struct client *clients = NULL; unsigned int clients_count = 0; void client_state_set(struct client *client, const char *name, const char *args) { string_t *str; client->state.name = name; if (!client->service_set->verbose_proctitle) return; if (clients_count == 0) { process_title_set("[idling]"); return; } if (clients_count > 1) { process_title_set(t_strdup_printf("[%u clients]", clients_count)); return; } str = t_str_new(128); str_printfa(str, "[%s", client->state.name); if (client->remote_ip.family != 0) str_printfa(str, " %s", net_ip2addr(&client->remote_ip)); if (args[0] != '\0') str_printfa(str, " %s", args); str_append_c(str, ']'); process_title_set(str_c(str)); } static void client_idle_timeout(struct client *client) { client_destroy(client, t_strdup_printf("421 4.4.2 %s", client->my_domain), "Disconnected client for inactivity"); } static int client_input_line(struct client *client, const char *line) { const char *cmd, *args; args = strchr(line, ' '); if (args == NULL) { cmd = line; args = ""; } else { cmd = t_strdup_until(line, args); args++; } cmd = t_str_ucase(cmd); if (strcmp(cmd, "LHLO") == 0) return cmd_lhlo(client, args); if (strcmp(cmd, "STARTTLS") == 0 && master_service_ssl_is_enabled(master_service)) return cmd_starttls(client); if (strcmp(cmd, "MAIL") == 0) return cmd_mail(client, args); if (strcmp(cmd, "RCPT") == 0) return cmd_rcpt(client, args); if (strcmp(cmd, "DATA") == 0) return cmd_data(client, args); if (strcmp(cmd, "QUIT") == 0) return cmd_quit(client, args); if (strcmp(cmd, "VRFY") == 0) return cmd_vrfy(client, args); if (strcmp(cmd, "RSET") == 0) return cmd_rset(client, args); if (strcmp(cmd, "NOOP") == 0) return cmd_noop(client, args); if (strcmp(cmd, "XCLIENT") == 0) return cmd_xclient(client, args); client_send_line(client, "502 5.5.2 Unknown command"); return 0; } int client_input_read(struct client *client) { client->last_input = ioloop_time; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -2: /* buffer full */ client_destroy(client, "502 5.5.2", "Disconnected: Input buffer full"); return -1; case -1: /* disconnected */ client_destroy(client, NULL, NULL); return -1; case 0: /* nothing new read */ return 0; default: /* something was read */ return 0; } } void client_input_handle(struct client *client) { struct ostream *output; const char *line; int ret; output = client->output; o_stream_ref(output); while ((line = i_stream_next_line(client->input)) != NULL) { T_BEGIN { o_stream_cork(output); ret = client_input_line(client, line); o_stream_uncork(output); } T_END; if (ret < 0) break; } o_stream_unref(&output); } static void client_input(struct client *client) { if (client_input_read(client) < 0) return; client_input_handle(client); } static void client_raw_user_create(struct client *client) { void **sets; sets = master_service_settings_get_others(master_service); client->raw_mail_user = raw_storage_create_from_set(client->user_set_info, sets[0]); } static void client_read_settings(struct client *client) { struct mail_storage_service_input input; const struct setting_parser_context *set_parser; struct lmtp_settings *lmtp_set; struct lda_settings *lda_set; const char *error; i_zero(&input); input.module = input.service = "lmtp"; input.local_ip = client->local_ip; input.remote_ip = client->remote_ip; input.username = ""; if (mail_storage_service_read_settings(storage_service, &input, client->pool, &client->user_set_info, &set_parser, &error) < 0) i_fatal("%s", error); lmtp_settings_dup(set_parser, client->pool, &lmtp_set, &lda_set); settings_var_expand(&lmtp_setting_parser_info, lmtp_set, client->pool, mail_storage_service_get_var_expand_table(storage_service, &input)); client->service_set = master_service_settings_get(master_service); client->lmtp_set = lmtp_set; client->unexpanded_lda_set = lda_set; } static void client_generate_session_id(struct client *client) { guid_128_t guid; string_t *id = t_str_new(30); guid_128_generate(guid); base64_encode(guid, sizeof(guid), id); i_assert(str_c(id)[str_len(id)-2] == '='); str_truncate(id, str_len(id)-2); /* drop trailing "==" */ client->state.session_id = p_strdup(client->state_pool, str_c(id)); } const char *client_remote_id(struct client *client) { const char *addr; addr = net_ip2addr(&client->remote_ip); if (addr[0] == '\0') addr = "local"; return addr; } void client_io_reset(struct client *client) { if (client->io != NULL) io_remove(&client->io); if (client->to_idle != NULL) timeout_remove(&client->to_idle); client->io = io_add(client->fd_in, IO_READ, client_input, client); client->last_input = ioloop_time; client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, client_idle_timeout, client); } struct client *client_create(int fd_in, int fd_out, const struct master_service_connection *conn) { struct client *client; pool_t pool; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); pool = pool_alloconly_create("lmtp client", 2048); client = p_new(pool, struct client, 1); client->pool = pool; client->fd_in = fd_in; client->fd_out = fd_out; client->remote_ip = conn->remote_ip; client->remote_port = conn->remote_port; client->local_ip = conn->local_ip; client->local_port = conn->local_port; client->input = i_stream_create_fd(fd_in, CLIENT_MAX_INPUT_SIZE, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); client_io_reset(client); client->state_pool = pool_alloconly_create("client state", 4096); client->state.mail_data_fd = -1; client_read_settings(client); client_raw_user_create(client); client_generate_session_id(client); client->my_domain = client->unexpanded_lda_set->hostname; client->lhlo = i_strdup("missing"); client->proxy_ttl = LMTP_PROXY_DEFAULT_TTL; DLLIST_PREPEND(&clients, client); clients_count++; client_state_set(client, "banner", ""); client_send_line(client, "220 %s %s", client->my_domain, client->lmtp_set->login_greeting); i_info("Connect from %s", client_remote_id(client)); return client; } void client_destroy(struct client *client, const char *prefix, const char *reason) { client_disconnect(client, prefix, reason); o_stream_uncork(client->output); clients_count--; DLLIST_REMOVE(&clients, client); client_state_set(client, "destroyed", ""); if (client->raw_mail_user != NULL) mail_user_unref(&client->raw_mail_user); if (client->proxy != NULL) lmtp_proxy_deinit(&client->proxy); if (client->io != NULL) io_remove(&client->io); if (client->to_idle != NULL) timeout_remove(&client->to_idle); if (client->ssl_iostream != NULL) ssl_iostream_destroy(&client->ssl_iostream); i_stream_destroy(&client->input); o_stream_destroy(&client->output); fd_close_maybe_stdio(&client->fd_in, &client->fd_out); client_state_reset(client, "destroyed"); i_free(client->lhlo); pool_unref(&client->state_pool); pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); } static const char *client_get_disconnect_reason(struct client *client) { const char *err; if (client->ssl_iostream != NULL && !ssl_iostream_is_handshaked(client->ssl_iostream)) { err = ssl_iostream_get_last_error(client->ssl_iostream); if (err != NULL) { return t_strdup_printf("TLS handshaking failed: %s", err); } } return io_stream_get_disconnect_reason(client->input, client->output); } void client_disconnect(struct client *client, const char *prefix, const char *reason) { if (client->disconnected) return; if (reason != NULL) client_send_line(client, "%s %s", prefix, reason); else reason = client_get_disconnect_reason(client); i_info("Disconnect from %s: %s (in %s)", client_remote_id(client), reason, client->state.name); client->disconnected = TRUE; } void client_rcpt_anvil_disconnect(const struct mail_recipient *rcpt) { const struct mail_storage_service_input *input; if (!rcpt->anvil_connect_sent) return; input = mail_storage_service_user_get_input(rcpt->service_user); master_service_anvil_send(master_service, t_strconcat( "DISCONNECT\t", my_pid, "\t", master_service_get_name(master_service), "/", input->username, "\n", NULL)); } void client_state_reset(struct client *client, const char *state_name) { struct mail_recipient *const *rcptp; if (client->proxy != NULL) lmtp_proxy_deinit(&client->proxy); if (array_is_created(&client->state.rcpt_to)) { array_foreach_modifiable(&client->state.rcpt_to, rcptp) { if ((*rcptp)->anvil_query != NULL) anvil_client_query_abort(anvil, &(*rcptp)->anvil_query); client_rcpt_anvil_disconnect(*rcptp); mail_storage_service_user_unref(&(*rcptp)->service_user); } } if (client->state.raw_mail != NULL) { struct mailbox_transaction_context *raw_trans = client->state.raw_mail->transaction; struct mailbox *raw_box = client->state.raw_mail->box; mail_free(&client->state.raw_mail); mailbox_transaction_rollback(&raw_trans); mailbox_free(&raw_box); } if (client->state.mail_data != NULL) buffer_free(&client->state.mail_data); if (client->state.mail_data_output != NULL) o_stream_unref(&client->state.mail_data_output); if (client->state.mail_data_fd != -1) { if (close(client->state.mail_data_fd) < 0) i_error("close(mail data fd) failed: %m"); } i_zero(&client->state); p_clear(client->state_pool); client->state.mail_data_fd = -1; client_generate_session_id(client); client_state_set(client, state_name, ""); } void client_send_line(struct client *client, const char *fmt, ...) { va_list args; va_start(args, fmt); T_BEGIN { string_t *str; str = t_str_new(256); str_vprintfa(str, fmt, args); str_append(str, "\r\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); } T_END; va_end(args); } bool client_is_trusted(struct client *client) { const char *const *net; struct ip_addr net_ip; unsigned int bits; if (client->lmtp_set->login_trusted_networks == NULL) return FALSE; net = t_strsplit_spaces(client->lmtp_set->login_trusted_networks, ", "); for (; *net != NULL; net++) { if (net_parse_range(*net, &net_ip, &bits) < 0) { i_error("login_trusted_networks: " "Invalid network '%s'", *net); break; } if (net_is_in_network(&client->remote_ip, &net_ip, bits)) return TRUE; } return FALSE; } void clients_destroy(void) { while (clients != NULL) { client_destroy(clients, t_strdup_printf("421 4.3.2 %s", clients->my_domain), "Shutting down"); } } dovecot-2.2.33.2/src/lmtp/client.h0000644000175000017500000000526513165463624013541 00000000000000#ifndef CLIENT_H #define CLIENT_H #include "net.h" #include "lmtp-client.h" #define CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128) struct mail_recipient { struct client *client; const char *session_id; const char *address; const char *detail; /* +detail part is also in address */ struct lmtp_recipient_params params; struct anvil_query *anvil_query; bool anvil_connect_sent; struct mail_storage_service_user *service_user; }; struct client_state { const char *name; const char *session_id; const char *mail_from; ARRAY(struct mail_recipient *) rcpt_to; unsigned int rcpt_idx; unsigned int data_end_idx; /* Initially we start writing to mail_data. If it grows too large, start using mail_data_fd. */ buffer_t *mail_data; int mail_data_fd; struct ostream *mail_data_output; const char *added_headers; struct timeval mail_from_timeval, data_end_timeval; struct mail *raw_mail; struct mail_user *dest_user; struct mail *first_saved_mail; unsigned int mail_body_7bit:1; unsigned int mail_body_8bitmime:1; }; struct client { struct client *prev, *next; pool_t pool; const struct setting_parser_info *user_set_info; const struct lda_settings *unexpanded_lda_set; const struct lmtp_settings *lmtp_set; const struct master_service_settings *service_set; int fd_in, fd_out; struct io *io; struct istream *input; struct ostream *output; struct ssl_iostream *ssl_iostream; struct timeout *to_idle; time_t last_input; struct ip_addr remote_ip, local_ip; in_port_t remote_port, local_port; struct mail_user *raw_mail_user; const char *my_domain; char *lhlo; pool_t state_pool; struct client_state state; struct istream *dot_input; struct lmtp_proxy *proxy; unsigned int proxy_ttl; unsigned int proxy_timeout_secs; unsigned int disconnected:1; }; extern unsigned int clients_count; struct client *client_create(int fd_in, int fd_out, const struct master_service_connection *conn); void client_destroy(struct client *client, const char *prefix, const char *reason) ATTR_NULL(2, 3); void client_disconnect(struct client *client, const char *prefix, const char *reason); void client_io_reset(struct client *client); void client_rcpt_anvil_disconnect(const struct mail_recipient *rcpt); void client_state_reset(struct client *client, const char *state_name); void client_state_set(struct client *client, const char *name, const char *args); const char *client_remote_id(struct client *client); void client_input_handle(struct client *client); int client_input_read(struct client *client); void client_send_line(struct client *client, const char *fmt, ...) ATTR_FORMAT(2, 3); bool client_is_trusted(struct client *client); void clients_destroy(void); #endif dovecot-2.2.33.2/src/lmtp/lmtp-settings.c0000644000175000017500000000744413165463624015071 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "var-expand.h" #include "settings-parser.h" #include "service-settings.h" #include "master-service.h" #include "master-service-settings.h" #include "lda-settings.h" #include "lmtp-settings.h" #include "mail-storage-settings.h" #include #include static bool lmtp_settings_check(void *_set, pool_t pool, const char **error_r); /* */ static struct file_listener_settings lmtp_unix_listeners_array[] = { { "lmtp", 0666, "", "" } }; static struct file_listener_settings *lmtp_unix_listeners[] = { &lmtp_unix_listeners_array[0] }; static buffer_t lmtp_unix_listeners_buf = { lmtp_unix_listeners, sizeof(lmtp_unix_listeners), { NULL, } }; /* */ struct service_settings lmtp_service_settings = { .name = "lmtp", .protocol = "lmtp", .type = "", .executable = "lmtp", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &lmtp_unix_listeners_buf, sizeof(lmtp_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct lmtp_settings, name), NULL } static const struct setting_define lmtp_setting_defines[] = { DEF(SET_BOOL, lmtp_proxy), DEF(SET_BOOL, lmtp_save_to_detail_mailbox), DEF(SET_BOOL, lmtp_rcpt_check_quota), DEF(SET_UINT, lmtp_user_concurrency_limit), DEF(SET_STR, lmtp_address_translate), DEF(SET_ENUM, lmtp_hdr_delivery_address), DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, login_trusted_networks), SETTING_DEFINE_LIST_END }; static const struct lmtp_settings lmtp_default_settings = { .lmtp_proxy = FALSE, .lmtp_save_to_detail_mailbox = FALSE, .lmtp_rcpt_check_quota = FALSE, .lmtp_user_concurrency_limit = 0, .lmtp_address_translate = "", .lmtp_hdr_delivery_address = "final:none:original", .login_greeting = PACKAGE_NAME" ready.", .login_trusted_networks = "" }; static const struct setting_parser_info *lmtp_setting_dependencies[] = { &lda_setting_parser_info, NULL }; const struct setting_parser_info lmtp_setting_parser_info = { .module_name = "lmtp", .defines = lmtp_setting_defines, .defaults = &lmtp_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct lmtp_settings), .parent_offset = (size_t)-1, .check_func = lmtp_settings_check, .dependencies = lmtp_setting_dependencies }; /* */ static bool lmtp_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct lmtp_settings *set = _set; if (strcmp(set->lmtp_hdr_delivery_address, "none") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_NONE; } else if (strcmp(set->lmtp_hdr_delivery_address, "final") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_FINAL; } else if (strcmp(set->lmtp_hdr_delivery_address, "original") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL; } else { *error_r = t_strdup_printf("Unknown lmtp_hdr_delivery_address: %s", set->lmtp_hdr_delivery_address); return FALSE; } return TRUE; } /* */ void lmtp_settings_dup(const struct setting_parser_context *set_parser, pool_t pool, struct lmtp_settings **lmtp_set_r, struct lda_settings **lda_set_r) { void **sets; sets = master_service_settings_parser_get_others(master_service, set_parser); *lda_set_r = settings_dup(&lda_setting_parser_info, sets[1], pool); *lmtp_set_r = settings_dup(&lmtp_setting_parser_info, sets[2], pool); } dovecot-2.2.33.2/src/lmtp/lmtp-proxy.h0000644000175000017500000000247513165463543014416 00000000000000#ifndef LMTP_PROXY_H #define LMTP_PROXY_H #include "net.h" #include "lmtp-client.h" #define LMTP_PROXY_DEFAULT_TTL 5 struct lmtp_proxy_settings { const char *my_hostname; const char *dns_client_socket_path; const char *session_id; /* the original client's IP/port that connected to the proxy */ struct ip_addr source_ip; in_port_t source_port; unsigned int proxy_ttl; }; struct lmtp_proxy_rcpt_settings { const char *host; struct ip_addr hostip; in_port_t port; unsigned int timeout_msecs; enum lmtp_client_protocol protocol; struct lmtp_recipient_params params; }; typedef void lmtp_proxy_finish_callback_t(void *context); struct lmtp_proxy * lmtp_proxy_init(const struct lmtp_proxy_settings *set, struct ostream *client_output); void lmtp_proxy_deinit(struct lmtp_proxy **proxy); /* Set the "MAIL FROM:" line, including <> and options */ void lmtp_proxy_mail_from(struct lmtp_proxy *proxy, const char *value); /* Add a new recipient. Returns -1 if we already know that the destination host can't be reached. */ int lmtp_proxy_add_rcpt(struct lmtp_proxy *proxy, const char *address, const struct lmtp_proxy_rcpt_settings *set); /* Start proxying */ void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input, lmtp_proxy_finish_callback_t *callback, void *context) ATTR_NULL(3); #endif dovecot-2.2.33.2/src/lmtp/Makefile.am0000644000175000017500000000161513165463624014141 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = lmtp AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-smtp \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-lda \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw lmtp_LDFLAGS = -export-dynamic lmtp_LDADD = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) lmtp_DEPENDENCIES = \ $(LIBDOVECOT_LDA) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) lmtp_SOURCES = \ main.c \ client.c \ commands.c \ lmtp-proxy.c \ lmtp-settings.c noinst_HEADERS = \ main.h \ client.h \ commands.h \ lmtp-proxy.h \ lmtp-settings.h dovecot-2.2.33.2/src/lib-imap-urlauth/0002755000175000017500000000000013172375611014360 500000000000000dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth.h0000644000175000017500000000267213123174404017057 00000000000000#ifndef IMAP_URLAUTH_H #define IMAP_URLAUTH_H #include "net.h" #define IMAP_URLAUTH_SOCKET_NAME "imap-urlauth" struct imap_url; struct imap_msgpart_url; struct imap_urlauth_context; struct imap_urlauth_config { const char *url_host; in_port_t url_port; const char *socket_path; const char *session_id; const char *access_user; const char *const *access_applications; bool access_anonymous; }; struct imap_urlauth_context * imap_urlauth_init(struct mail_user *user, const struct imap_urlauth_config *config); void imap_urlauth_deinit(struct imap_urlauth_context **_uctx); int imap_urlauth_generate(struct imap_urlauth_context *uctx, const char *mechanism, const char *rumpurl, const char **urlauth_r, const char **error_r); bool imap_urlauth_check(struct imap_urlauth_context *uctx, struct imap_url *url, bool ignore_unknown_access, const char **error_r); int imap_urlauth_fetch_parsed(struct imap_urlauth_context *uctx, struct imap_url *url, struct imap_msgpart_url **mpurl_r, enum mail_error *error_code_r, const char **error_r); int imap_urlauth_fetch(struct imap_urlauth_context *uctx, const char *urlauth, struct imap_msgpart_url **mpurl_r, enum mail_error *error_code_r, const char **error_r); int imap_urlauth_reset_mailbox_key(struct imap_urlauth_context *uctx, struct mailbox *box); int imap_urlauth_reset_all_keys(struct imap_urlauth_context *uctx); #endif dovecot-2.2.33.2/src/lib-imap-urlauth/Makefile.in0000644000175000017500000005533713172375573016367 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-imap-urlauth ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libimap_urlauth_la_LIBADD = am_libimap_urlauth_la_OBJECTS = imap-urlauth.lo imap-urlauth-fetch.lo \ imap-urlauth-backend.lo imap-urlauth-connection.lo libimap_urlauth_la_OBJECTS = $(am_libimap_urlauth_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libimap_urlauth_la_SOURCES) DIST_SOURCES = $(libimap_urlauth_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libimap-urlauth.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-storage libimap_urlauth_la_SOURCES = \ imap-urlauth.c \ imap-urlauth-fetch.c \ imap-urlauth-backend.c \ imap-urlauth-connection.c headers = \ imap-urlauth.h \ imap-urlauth-private.h \ imap-urlauth-fetch.h \ imap-urlauth-backend.h \ imap-urlauth-connection.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-imap-urlauth/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-imap-urlauth/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libimap-urlauth.la: $(libimap_urlauth_la_OBJECTS) $(libimap_urlauth_la_DEPENDENCIES) $(EXTRA_libimap_urlauth_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libimap_urlauth_la_OBJECTS) $(libimap_urlauth_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-backend.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-fetch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-connection.h0000644000175000017500000000314413123174404021207 00000000000000#ifndef IMAP_URLAUTH_CONNECTION_H #define IMAP_URLAUTH_CONNECTION_H struct imap_urlauth_request; struct imap_urlauth_fetch_reply; typedef int imap_urlauth_request_callback_t(struct imap_urlauth_fetch_reply *reply, void *context); /* If reconnect_callback is specified, it's called when connection is lost. If the callback returns FALSE, reconnection isn't attempted. */ struct imap_urlauth_connection * imap_urlauth_connection_init(const char *path, struct mail_user *user, const char *session_id, unsigned int idle_timeout_msecs); void imap_urlauth_connection_deinit(struct imap_urlauth_connection **conn); /* Connect to imap-urlauth (even if failed for previous requests). */ int imap_urlauth_connection_connect(struct imap_urlauth_connection *conn); /* Continue after request callback returned 0 */ void imap_urlauth_connection_continue(struct imap_urlauth_connection *conn); /* Create a new URL fetch request */ struct imap_urlauth_request * imap_urlauth_request_new(struct imap_urlauth_connection *conn, const char *target_user, const char *url, enum imap_urlauth_fetch_flags flags, imap_urlauth_request_callback_t *callback, void *context); /* Abort request */ void imap_urlauth_request_abort(struct imap_urlauth_connection *conn, struct imap_urlauth_request *urlreq); /* Abort all requests with matching context value */ void imap_urlauth_request_abort_by_context(struct imap_urlauth_connection *conn, void *context); /* Returns TRUE if currently connected imap-urlauth service. */ bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn); #endif dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-fetch.h0000644000175000017500000000353413165463624020157 00000000000000#ifndef IMAP_URLAUTH_FETCH_H #define IMAP_URLAUTH_FETCH_H struct imap_url; struct imap_urlauth_context; struct imap_urlauth_fetch; enum imap_urlauth_fetch_flags { /* Indicates that this is an extended request */ IMAP_URLAUTH_FETCH_FLAG_EXTENDED = 0x01, /* Fetch body part unmodified */ IMAP_URLAUTH_FETCH_FLAG_BODY = 0x02, /* Fetch body part as binary, i.e. without content encoding */ IMAP_URLAUTH_FETCH_FLAG_BINARY = 0x04, /* Fetch IMAP bodypartstructure */ IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE = 0x08, }; struct imap_urlauth_fetch_reply { const char *url; enum imap_urlauth_fetch_flags flags; struct istream *input; uoff_t size; const char *bodypartstruct; const char *error; unsigned int succeeded:1; unsigned int binary_has_nuls:1; }; /* Callback to handle fetch reply. Returns 1 if handled completely and ready for next reply, 0 if not all data was processed, and -1 for error. If a callback returns 0, imap_urlauth_fetch_continue() must be called once new replies may be processed. If this is the last request to yield a reply, argument last is TRUE. */ typedef int imap_urlauth_fetch_callback_t(struct imap_urlauth_fetch_reply *reply, bool last, void *context); struct imap_urlauth_fetch * imap_urlauth_fetch_init(struct imap_urlauth_context *uctx, imap_urlauth_fetch_callback_t *callback, void *context); void imap_urlauth_fetch_deinit(struct imap_urlauth_fetch **ufetch); int imap_urlauth_fetch_url(struct imap_urlauth_fetch *ufetch, const char *url, enum imap_urlauth_fetch_flags url_flags); int imap_urlauth_fetch_url_parsed(struct imap_urlauth_fetch *ufetch, const char *url, struct imap_url *imap_url, enum imap_urlauth_fetch_flags url_flags); bool imap_urlauth_fetch_continue(struct imap_urlauth_fetch *ufetch); bool imap_urlauth_fetch_is_pending(struct imap_urlauth_fetch *ufetch); #endif dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-backend.c0000644000175000017500000001045013165463624020443 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "randgen.h" #include "mail-user.h" #include "mail-storage.h" #include "mailbox-list-iter.h" #include "imap-urlauth-private.h" #include "imap-urlauth-backend.h" #define IMAP_URLAUTH_KEY MAILBOX_ATTRIBUTE_PREFIX_DOVECOT"imap-urlauth" static int imap_urlauth_backend_trans_get_mailbox_key(struct mailbox_transaction_context *trans, bool create, unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], const char **error_r, enum mail_error *error_code_r) { struct mailbox *box = mailbox_transaction_get_mailbox(trans); struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct mail_attribute_value urlauth_key; const char *mailbox_key_hex = NULL; buffer_t key_buf; int ret; *error_r = "Internal server error"; *error_code_r = MAIL_ERROR_TEMP; ret = mailbox_attribute_get(trans, MAIL_ATTRIBUTE_TYPE_PRIVATE, IMAP_URLAUTH_KEY, &urlauth_key); if (ret < 0) return -1; if (user->mail_debug) { i_debug("imap-urlauth: %skey found for mailbox %s", (ret > 0 ? "" : "no "), mailbox_get_vname(box)); } if (ret == 0) { if (!create) return 0; /* create new key */ random_fill(mailbox_key_r, IMAP_URLAUTH_KEY_LEN); mailbox_key_hex = binary_to_hex(mailbox_key_r, IMAP_URLAUTH_KEY_LEN); i_zero(&urlauth_key); urlauth_key.value = mailbox_key_hex; ret = mailbox_attribute_set(trans, MAIL_ATTRIBUTE_TYPE_PRIVATE, IMAP_URLAUTH_KEY, &urlauth_key); if (ret < 0) return -1; if (user->mail_debug) { i_debug("imap-urlauth: created key for mailbox %s", mailbox_get_vname(box)); } } else { /* read existing key */ buffer_create_from_data(&key_buf, mailbox_key_r, IMAP_URLAUTH_KEY_LEN); mailbox_key_hex = urlauth_key.value; if (strlen(mailbox_key_hex) != 2*IMAP_URLAUTH_KEY_LEN || hex_to_binary(mailbox_key_hex, &key_buf) < 0 || key_buf.used != IMAP_URLAUTH_KEY_LEN) { i_error("imap-urlauth: key found for mailbox %s is invalid", mailbox_get_vname(box)); return -1; } } return 1; } int imap_urlauth_backend_get_mailbox_key(struct mailbox *box, bool create, unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], const char **error_r, enum mail_error *error_code_r) { struct mailbox_transaction_context *t; int ret; t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); ret = imap_urlauth_backend_trans_get_mailbox_key(t, create, mailbox_key_r, error_r, error_code_r); if (mailbox_transaction_commit(&t) < 0) ret = -1; return ret; } int imap_urlauth_backend_reset_mailbox_key(struct mailbox *box) { struct mailbox_transaction_context *t; int ret; t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); ret = mailbox_attribute_unset(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, IMAP_URLAUTH_KEY); if (mailbox_transaction_commit(&t) < 0) ret = -1; return ret; } static int imap_urlauth_backend_mailbox_reset_key(struct mailbox *box) { const char *errstr; enum mail_error error; if (mailbox_open(box) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_PERM) return 0; i_error("urlauth key reset: Couldn't open mailbox %s: %s", mailbox_get_vname(box), errstr); return -1; } return imap_urlauth_backend_reset_mailbox_key(box); } int imap_urlauth_backend_reset_all_keys(struct mail_user *user) { const char *const patterns[] = { "*", NULL }; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; struct mailbox *box; int ret = 0; iter = mailbox_list_iter_init_namespaces(user->namespaces, patterns, MAIL_NAMESPACE_TYPE_MASK_ALL, MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { box = mailbox_alloc(info->ns->list, info->vname, 0); mailbox_set_reason(box, "URLAUTH reset all keys"); if (imap_urlauth_backend_mailbox_reset_key(box) < 0) ret = -1; mailbox_free(&box); } if (mailbox_list_iter_deinit(&iter) < 0) { i_error("urlauth key reset: Couldn't iterate mailboxes: %s", mailbox_list_get_last_internal_error(user->namespaces->list, NULL)); ret = -1; } return ret; } dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-fetch.c0000644000175000017500000003333113165463624020150 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "array.h" #include "net.h" #include "istream.h" #include "mail-user.h" #include "mail-error.h" #include "mail-storage.h" #include "imap-url.h" #include "imap-msgpart-url.h" #include "imap-urlauth-private.h" #include "imap-urlauth-fetch.h" #include "imap-urlauth-connection.h" struct imap_urlauth_fetch_url { struct imap_urlauth_fetch_url *prev, *next; char *url; enum imap_urlauth_fetch_flags flags; }; struct imap_urlauth_fetch { unsigned int refcount; struct imap_urlauth_context *uctx; imap_urlauth_fetch_callback_t *callback; void *context; /* local urls */ struct imap_urlauth_fetch_url *local_urls_head, *local_urls_tail; struct imap_msgpart_url *local_url; unsigned int pending_requests; struct { char *url; enum imap_urlauth_fetch_flags flags; struct istream *input; uoff_t size; char *bodypartstruct; char *error; unsigned int succeeded:1; unsigned int binary_has_nuls:1; } pending_reply; unsigned int failed:1; unsigned int waiting_local:1; unsigned int waiting_service:1; }; static void imap_urlauth_fetch_abort_local(struct imap_urlauth_fetch *ufetch) { struct imap_urlauth_fetch_url *url, *url_next; if (ufetch->local_url != NULL) { ufetch->pending_requests--; imap_msgpart_url_free(&ufetch->local_url); } i_free_and_null(ufetch->pending_reply.url); i_free_and_null(ufetch->pending_reply.bodypartstruct); i_free_and_null(ufetch->pending_reply.error); if (ufetch->pending_reply.input != NULL) i_stream_unref(&ufetch->pending_reply.input); url = ufetch->local_urls_head; for (; url != NULL; url = url_next) { url_next = url->next; i_free(url->url); i_free(url); ufetch->pending_requests--; } ufetch->local_urls_head = ufetch->local_urls_tail = NULL; } static void imap_urlauth_fetch_abort(struct imap_urlauth_fetch *ufetch) { if (ufetch->pending_requests > 0) imap_urlauth_request_abort_by_context(ufetch->uctx->conn, ufetch); imap_urlauth_fetch_abort_local(ufetch); i_assert(ufetch->pending_requests == 0); } static void imap_urlauth_fetch_fail(struct imap_urlauth_fetch *ufetch) { imap_urlauth_fetch_abort(ufetch); ufetch->failed = TRUE; } struct imap_urlauth_fetch * imap_urlauth_fetch_init(struct imap_urlauth_context *uctx, imap_urlauth_fetch_callback_t *callback, void *context) { struct imap_urlauth_fetch *ufetch; ufetch = i_new(struct imap_urlauth_fetch, 1); ufetch->refcount = 1; ufetch->uctx = uctx; ufetch->callback = callback; ufetch->context = context; return ufetch; } static void imap_urlauth_fetch_ref(struct imap_urlauth_fetch *ufetch) { i_assert(ufetch->refcount > 0); ufetch->refcount++; } static void imap_urlauth_fetch_unref(struct imap_urlauth_fetch **_ufetch) { struct imap_urlauth_fetch *ufetch = *_ufetch; i_assert(ufetch->refcount > 0); *_ufetch = NULL; if (--ufetch->refcount > 0) return; ufetch->refcount++; imap_urlauth_fetch_abort(ufetch); ufetch->refcount--; i_assert(ufetch->refcount == 0); /* dont leave the connection in limbo; make sure continue is called */ if (ufetch->waiting_service) imap_urlauth_connection_continue(ufetch->uctx->conn); i_free(ufetch); } void imap_urlauth_fetch_deinit(struct imap_urlauth_fetch **_ufetch) { imap_urlauth_fetch_unref(_ufetch); } static void imap_urlauth_fetch_error(struct imap_urlauth_fetch *ufetch, const char *url, enum imap_urlauth_fetch_flags url_flags, const char *error) { struct imap_urlauth_fetch_reply reply; int ret; ufetch->pending_requests--; i_zero(&reply); reply.url = url; reply.flags = url_flags; reply.succeeded = FALSE; reply.error = error; T_BEGIN { ret = ufetch->callback(&reply, ufetch->pending_requests == 0, ufetch->context); } T_END; if (ret == 0) { ufetch->waiting_local = TRUE; ufetch->pending_requests++; } else if (ret < 0) { imap_urlauth_fetch_fail(ufetch); } } static void imap_urlauth_fetch_local(struct imap_urlauth_fetch *ufetch, const char *url, enum imap_urlauth_fetch_flags url_flags, struct imap_url *imap_url) { struct imap_urlauth_fetch_reply reply; struct imap_msgpart_open_result mpresult; const char *error, *errormsg = NULL, *bpstruct = NULL; bool debug = ufetch->uctx->user->mail_debug, success; enum mail_error error_code; struct imap_msgpart_url *mpurl = NULL; int ret; success = TRUE; if (debug) i_debug("Fetching local URLAUTH %s", url); if (url_flags == 0) url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY; /* fetch URL */ if (imap_url == NULL) { ret = imap_urlauth_fetch(ufetch->uctx, url, &mpurl, &error_code, &error); } else { ret = imap_urlauth_fetch_parsed(ufetch->uctx, imap_url, &mpurl, &error_code, &error); } if (ret <= 0) { if (ret == 0) { errormsg = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s", url, error); if (debug) i_debug("%s", errormsg); } success = FALSE; } /* fetch metadata */ if (success && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) imap_msgpart_url_set_decode_to_binary(mpurl); if (success && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) { ret = imap_msgpart_url_get_bodypartstructure(mpurl, &bpstruct, &error); if (ret <= 0) { if (ret == 0) { errormsg = t_strdup_printf ("Failed to read URLAUTH \"%s\": %s", url, error); if (debug) i_debug("%s", errormsg); } success = FALSE; } } /* if requested, read the message part the URL points to */ i_zero(&mpresult); if (success && ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 || (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)) { ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error); if (ret <= 0) { if (ret == 0) { errormsg = t_strdup_printf ("Failed to read URLAUTH \"%s\": %s", url, error); if (debug) i_debug("%s", errormsg); } success = FALSE; } } if (debug && success) { if (bpstruct != NULL) i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct); if (mpresult.size == 0 || mpresult.input == NULL) i_debug("Fetched URLAUTH yielded empty result"); else { i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes " "of %smessage data", mpresult.size, (mpresult.binary_decoded_input_has_nuls ? "binary " : "")); } } ufetch->pending_requests--; if (!success && ret < 0) { if (mpurl != NULL) imap_msgpart_url_free(&mpurl); (void)ufetch->callback(NULL, TRUE, ufetch->context); imap_urlauth_fetch_fail(ufetch); return; } i_zero(&reply); reply.url = url; reply.flags = url_flags; reply.error = errormsg; reply.succeeded = success; reply.bodypartstruct = bpstruct; reply.binary_has_nuls = mpresult.binary_decoded_input_has_nuls; reply.size = mpresult.size; reply.input = mpresult.input; ret = ufetch->callback(&reply, ufetch->pending_requests == 0, ufetch->context); if (ret == 0) { ufetch->local_url = mpurl; ufetch->waiting_local = TRUE; ufetch->pending_requests++; } else { if (mpurl != NULL) imap_msgpart_url_free(&mpurl); if (ret < 0) imap_urlauth_fetch_fail(ufetch); } } static int imap_urlauth_fetch_request_callback(struct imap_urlauth_fetch_reply *reply, void *context) { struct imap_urlauth_fetch *ufetch = (struct imap_urlauth_fetch *)context; int ret = 1; if (ufetch->waiting_local && reply != NULL) { i_assert(ufetch->pending_reply.url == NULL); ufetch->pending_reply.url = i_strdup(reply->url); ufetch->pending_reply.flags = reply->flags; ufetch->pending_reply.bodypartstruct = i_strdup(reply->bodypartstruct); ufetch->pending_reply.error = i_strdup(reply->error); if (reply->input != NULL) { ufetch->pending_reply.input = reply->input; i_stream_ref(ufetch->pending_reply.input); } ufetch->pending_reply.size = reply->size; ufetch->pending_reply.succeeded = reply->succeeded; ufetch->pending_reply.binary_has_nuls = reply->binary_has_nuls; ufetch->waiting_service = TRUE; return 0; } ufetch->waiting_local = FALSE; ufetch->pending_requests--; imap_urlauth_fetch_ref(ufetch); if (!ufetch->failed) { bool last = ufetch->pending_requests == 0 || reply == NULL; ret = ufetch->callback(reply, last, ufetch->context); } /* report failure only once */ if (ret < 0 || reply == NULL) { if (!ufetch->failed) imap_urlauth_fetch_abort_local(ufetch); ufetch->failed = TRUE; } else if (ret == 0) { ufetch->waiting_service = TRUE; ufetch->pending_requests++; } imap_urlauth_fetch_unref(&ufetch); return ret; } int imap_urlauth_fetch_url(struct imap_urlauth_fetch *ufetch, const char *url, enum imap_urlauth_fetch_flags url_flags) { struct imap_urlauth_context *uctx = ufetch->uctx; enum imap_url_parse_flags url_parse_flags = IMAP_URL_PARSE_ALLOW_URLAUTH; struct mail_user *mail_user = uctx->user; struct imap_url *imap_url; const char *error, *errormsg; /* parse the url */ if (imap_url_parse(url, NULL, url_parse_flags, &imap_url, &error) < 0) { errormsg = t_strdup_printf( "Failed to fetch URLAUTH \"%s\": %s", url, error); if (mail_user->mail_debug) i_debug("%s", errormsg); ufetch->pending_requests++; imap_urlauth_fetch_ref(ufetch); imap_urlauth_fetch_error(ufetch, url, url_flags, errormsg); imap_urlauth_fetch_unref(&ufetch); return 1; } return imap_urlauth_fetch_url_parsed(ufetch, url, imap_url, url_flags); } int imap_urlauth_fetch_url_parsed(struct imap_urlauth_fetch *ufetch, const char *url, struct imap_url *imap_url, enum imap_urlauth_fetch_flags url_flags) { struct imap_urlauth_context *uctx = ufetch->uctx; struct mail_user *mail_user = uctx->user; const char *error, *errormsg; int ret = 0; ufetch->failed = FALSE; ufetch->pending_requests++; imap_urlauth_fetch_ref(ufetch); /* if access user and target user match, handle fetch request locally */ if (imap_url->userid != NULL && strcmp(mail_user->username, imap_url->userid) == 0) { if (ufetch->waiting_local) { struct imap_urlauth_fetch_url *url_local; url_local = i_new(struct imap_urlauth_fetch_url, 1); url_local->url = i_strdup(url); url_local->flags = url_flags; DLLIST2_APPEND(&ufetch->local_urls_head, &ufetch->local_urls_tail, url_local); } else T_BEGIN { imap_urlauth_fetch_local(ufetch, url, url_flags, imap_url); } T_END; imap_url = NULL; /* don't try to fetch remote URLs that are already known to fail access */ } else if (!imap_urlauth_check(uctx, imap_url, TRUE, &error)) { errormsg = t_strdup_printf( "Failed to fetch URLAUTH \"%s\": %s", url, error); if (mail_user->mail_debug) i_debug("%s", errormsg); imap_urlauth_fetch_error(ufetch, url, url_flags, errormsg); imap_url = NULL; } /* create request for url */ if (imap_url != NULL && imap_url->userid != NULL) { i_assert(uctx->conn != NULL); (void)imap_urlauth_request_new(uctx->conn, imap_url->userid, url, url_flags, imap_urlauth_fetch_request_callback, ufetch); i_assert(uctx->conn != NULL); if (imap_urlauth_connection_connect(uctx->conn) < 0) ret = -1; } if (ret >= 0) ret = (ufetch->pending_requests > 0 ? 0 : 1); imap_urlauth_fetch_unref(&ufetch); return ret; } static bool imap_urlauth_fetch_do_continue(struct imap_urlauth_fetch *ufetch) { struct imap_urlauth_fetch_url *url, *url_next; int ret; if (ufetch->failed) return FALSE; if (!ufetch->waiting_local && !ufetch->waiting_service) { /* not currently waiting for anything */ return ufetch->pending_requests > 0; } /* we finished a request */ ufetch->pending_requests--; if (!ufetch->waiting_local) { /* not waiting for local request handling */ ufetch->waiting_service = FALSE; imap_urlauth_connection_continue(ufetch->uctx->conn); return ufetch->pending_requests > 0; } /* finished local request */ if (ufetch->local_url != NULL) { imap_msgpart_url_free(&ufetch->local_url); } ufetch->waiting_local = FALSE; /* handle pending remote reply */ if (ufetch->pending_reply.url != NULL) { struct imap_urlauth_fetch_reply reply; ufetch->pending_requests--; i_zero(&reply); reply.url = ufetch->pending_reply.url; reply.flags = ufetch->pending_reply.flags; reply.bodypartstruct = ufetch->pending_reply.bodypartstruct; reply.error = ufetch->pending_reply.error; reply.input = ufetch->pending_reply.input; reply.size = ufetch->pending_reply.size; reply.succeeded = ufetch->pending_reply.succeeded; reply.binary_has_nuls = ufetch->pending_reply.binary_has_nuls; ret = ufetch->callback(&reply, ufetch->pending_requests == 0, ufetch->context); if (ufetch->pending_reply.url != NULL) i_free(ufetch->pending_reply.url); if (ufetch->pending_reply.input != NULL) i_stream_unref(&ufetch->pending_reply.input); if (ufetch->pending_reply.bodypartstruct != NULL) i_free(ufetch->pending_reply.bodypartstruct); if (ufetch->pending_reply.error != NULL) i_free(ufetch->pending_reply.error); if (ret < 0) { imap_urlauth_fetch_fail(ufetch); return FALSE; } if (ret == 0) { ufetch->waiting_service = TRUE; ufetch->pending_requests++; return TRUE; } ufetch->waiting_service = FALSE; imap_urlauth_connection_continue(ufetch->uctx->conn); } /* handle pending local urls */ url = ufetch->local_urls_head; while (url != NULL) { url_next = url->next; T_BEGIN { imap_urlauth_fetch_local(ufetch, url->url, url->flags, NULL); } T_END; DLLIST2_REMOVE(&ufetch->local_urls_head, &ufetch->local_urls_tail, url); i_free(url->url); i_free(url); if (ufetch->waiting_local) return TRUE; url = url_next; } return ufetch->pending_requests > 0; } bool imap_urlauth_fetch_continue(struct imap_urlauth_fetch *ufetch) { bool pending; imap_urlauth_fetch_ref(ufetch); pending = imap_urlauth_fetch_do_continue(ufetch); imap_urlauth_fetch_unref(&ufetch); return pending; } bool imap_urlauth_fetch_is_pending(struct imap_urlauth_fetch *ufetch) { return ufetch->pending_requests > 0; } dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-private.h0000644000175000017500000000050513165463624020533 00000000000000#ifndef IMAP_URLAUTH_PRIVATE_H #define IMAP_URLAUTH_PRIVATE_H #include "imap-urlauth.h" struct imap_urlauth_context { struct mail_user *user; struct imap_urlauth_connection *conn; char *url_host; in_port_t url_port; char *access_user; const char **access_applications; unsigned int access_anonymous:1; }; #endif dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-connection.c0000644000175000017500000006565113165463624021230 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "str.h" #include "str-sanitize.h" #include "strescape.h" #include "ioloop.h" #include "safe-mkstemp.h" #include "hostpid.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "write-full.h" #include "array.h" #include "aqueue.h" #include "mail-user.h" #include "imap-urlauth-fetch.h" #include "imap-urlauth-connection.h" enum imap_urlauth_state { IMAP_URLAUTH_STATE_DISCONNECTED = 0, IMAP_URLAUTH_STATE_AUTHENTICATING, IMAP_URLAUTH_STATE_AUTHENTICATED, IMAP_URLAUTH_STATE_SELECTING_TARGET, IMAP_URLAUTH_STATE_UNSELECTING_TARGET, IMAP_URLAUTH_STATE_READY, IMAP_URLAUTH_STATE_REQUEST_PENDING, IMAP_URLAUTH_STATE_REQUEST_WAIT, }; struct imap_urlauth_request { struct imap_urlauth_target *target; struct imap_urlauth_request *prev, *next; char *url; enum imap_urlauth_fetch_flags flags; char *bodypartstruct; imap_urlauth_request_callback_t *callback; void *context; unsigned int binary_has_nuls; }; struct imap_urlauth_target { struct imap_urlauth_target *prev, *next; char *userid; struct imap_urlauth_request *requests_head, *requests_tail; }; struct imap_urlauth_connection { int refcount; char *path, *session_id; struct mail_user *user; int fd; struct istream *input; struct ostream *output; struct io *io; struct timeout *to_reconnect, *to_idle, *to_response; time_t last_reconnect; unsigned int reconnect_attempts; unsigned int idle_timeout_msecs; char *literal_temp_path; int literal_fd; buffer_t *literal_buf; uoff_t literal_size, literal_bytes_left; enum imap_urlauth_state state; /* userid => target struct */ struct imap_urlauth_target *targets_head, *targets_tail; unsigned int reading_literal:1; }; #define IMAP_URLAUTH_RECONNECT_MIN_SECS 2 #define IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS 3 #define IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS 2*60*1000 #define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t1\t0\n" #define IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE (1024*32) static void imap_urlauth_connection_disconnect (struct imap_urlauth_connection *conn, const char *reason); static void imap_urlauth_connection_abort (struct imap_urlauth_connection *conn, const char *reason); static void imap_urlauth_connection_reconnect (struct imap_urlauth_connection *conn); static void imap_urlauth_connection_idle_disconnect (struct imap_urlauth_connection *conn); static void imap_urlauth_connection_timeout_abort (struct imap_urlauth_connection *conn); static void imap_urlauth_connection_fail (struct imap_urlauth_connection *conn); struct imap_urlauth_connection * imap_urlauth_connection_init(const char *path, struct mail_user *user, const char *session_id, unsigned int idle_timeout_msecs) { struct imap_urlauth_connection *conn; conn = i_new(struct imap_urlauth_connection, 1); conn->refcount = 1; conn->path = i_strdup(path); if (session_id != NULL) conn->session_id = i_strdup(session_id); conn->user = user; conn->fd = -1; conn->literal_fd = -1; conn->idle_timeout_msecs = idle_timeout_msecs; return conn; } void imap_urlauth_connection_deinit(struct imap_urlauth_connection **_conn) { struct imap_urlauth_connection *conn = *_conn; *_conn = NULL; imap_urlauth_connection_abort(conn, NULL); i_free(conn->path); if (conn->session_id != NULL) i_free(conn->session_id); i_assert(conn->to_idle == NULL); i_assert(conn->to_reconnect == NULL); i_assert(conn->to_response == NULL); i_free(conn); } static void imap_urlauth_stop_response_timeout(struct imap_urlauth_connection *conn) { if (conn->to_response != NULL) timeout_remove(&conn->to_response); } static void imap_urlauth_start_response_timeout(struct imap_urlauth_connection *conn) { imap_urlauth_stop_response_timeout(conn); conn->to_response = timeout_add(IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS, imap_urlauth_connection_timeout_abort, conn); } static struct imap_urlauth_target * imap_urlauth_connection_get_target(struct imap_urlauth_connection *conn, const char *target_user) { struct imap_urlauth_target *target = conn->targets_head; while (target != NULL) { if (strcmp(target->userid, target_user) == 0) return target; target = target->next; } target = i_new(struct imap_urlauth_target, 1); target->userid = i_strdup(target_user); DLLIST2_APPEND(&conn->targets_head, &conn->targets_tail, target); return target; } static void imap_urlauth_target_free(struct imap_urlauth_connection *conn, struct imap_urlauth_target *target) { DLLIST2_REMOVE(&conn->targets_head, &conn->targets_tail, target); i_free(target->userid); i_free(target); } static void imap_urlauth_connection_select_target(struct imap_urlauth_connection *conn) { struct imap_urlauth_target *target = conn->targets_head; const char *cmd; if (target == NULL || conn->state != IMAP_URLAUTH_STATE_AUTHENTICATED) return; if (conn->user->mail_debug) i_debug("imap-urlauth: Selecting target user `%s'", target->userid); conn->state = IMAP_URLAUTH_STATE_SELECTING_TARGET; cmd = t_strdup_printf("USER\t%s\n", str_tabescape(target->userid)); if (o_stream_send_str(conn->output, cmd) < 0) { i_warning("Error sending USER request to imap-urlauth server: %m"); imap_urlauth_connection_fail(conn); } imap_urlauth_start_response_timeout(conn); } static void imap_urlauth_connection_send_request(struct imap_urlauth_connection *conn) { struct imap_urlauth_request *urlreq; string_t *cmd; if (conn->targets_head == NULL || (conn->targets_head->requests_head == NULL && conn->targets_head->next == NULL && conn->state == IMAP_URLAUTH_STATE_READY)) { if (conn->user->mail_debug) i_debug("imap-urlauth: No more requests pending; scheduling disconnect"); if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); if (conn->idle_timeout_msecs > 0) { conn->to_idle = timeout_add(conn->idle_timeout_msecs, imap_urlauth_connection_idle_disconnect, conn); } return; } if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATED) { imap_urlauth_connection_select_target(conn); return; } if (conn->state != IMAP_URLAUTH_STATE_READY) return; urlreq = conn->targets_head->requests_head; if (urlreq == NULL) { if (conn->targets_head->next == NULL) return; conn->state = IMAP_URLAUTH_STATE_UNSELECTING_TARGET; imap_urlauth_target_free(conn, conn->targets_head); if (o_stream_send_str(conn->output, "END\n") < 0) { i_warning("Error sending END request to imap-urlauth server: %m"); imap_urlauth_connection_fail(conn); } imap_urlauth_start_response_timeout(conn); return; } if (conn->user->mail_debug) i_debug("imap-urlauth: Fetching URL `%s'", urlreq->url); cmd = t_str_new(128); str_append(cmd, "URL\t"); str_append_tabescaped(cmd, urlreq->url); if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) str_append(cmd, "\tbpstruct"); if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) str_append(cmd, "\tbinary"); else if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0) str_append(cmd, "\tbody"); str_append_c(cmd, '\n'); conn->state = IMAP_URLAUTH_STATE_REQUEST_PENDING; if (o_stream_send(conn->output, str_data(cmd), str_len(cmd)) < 0) { i_warning("Error sending URL request to imap-urlauth server: %m"); imap_urlauth_connection_fail(conn); } imap_urlauth_start_response_timeout(conn); } struct imap_urlauth_request * imap_urlauth_request_new(struct imap_urlauth_connection *conn, const char *target_user, const char *url, enum imap_urlauth_fetch_flags flags, imap_urlauth_request_callback_t *callback, void *context) { struct imap_urlauth_request *urlreq; struct imap_urlauth_target *target; target = imap_urlauth_connection_get_target(conn, target_user); urlreq = i_new(struct imap_urlauth_request, 1); urlreq->url = i_strdup(url); urlreq->flags = flags; urlreq->target = target; urlreq->callback = callback; urlreq->context = context; DLLIST2_APPEND(&target->requests_head, &target->requests_tail, urlreq); if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); if (conn->user->mail_debug) { i_debug("imap-urlauth: Added request for URL `%s' from user `%s'", url, target_user); } imap_urlauth_connection_send_request(conn); return urlreq; } static void imap_urlauth_request_free(struct imap_urlauth_request *urlreq) { struct imap_urlauth_target *target = urlreq->target; DLLIST2_REMOVE(&target->requests_head, &target->requests_tail, urlreq); i_free(urlreq->url); i_free(urlreq->bodypartstruct); i_free(urlreq); } static void imap_urlauth_request_drop(struct imap_urlauth_connection *conn, struct imap_urlauth_request *urlreq) { if ((conn->state == IMAP_URLAUTH_STATE_REQUEST_PENDING || conn->state == IMAP_URLAUTH_STATE_REQUEST_WAIT) && conn->targets_head != NULL && conn->targets_head->requests_head == urlreq) { /* cannot just drop pending request without breaking protocol state */ return; } imap_urlauth_request_free(urlreq); } void imap_urlauth_request_abort(struct imap_urlauth_connection *conn, struct imap_urlauth_request *urlreq) { imap_urlauth_request_callback_t *callback; callback = urlreq->callback; urlreq->callback = NULL; if (callback != NULL) { T_BEGIN { callback(NULL, urlreq->context); } T_END; } imap_urlauth_request_drop(conn, urlreq); } static void imap_urlauth_request_fail(struct imap_urlauth_connection *conn, struct imap_urlauth_request *urlreq, const char *error) { struct imap_urlauth_fetch_reply reply; imap_urlauth_request_callback_t *callback; int ret = 1; callback = urlreq->callback; urlreq->callback = NULL; if (callback != NULL) { i_zero(&reply); reply.url = urlreq->url; reply.flags = urlreq->flags; reply.succeeded = FALSE; reply.error = error; T_BEGIN { ret = callback(&reply, urlreq->context); } T_END; } void *urlreq_context = urlreq->context; imap_urlauth_request_drop(conn, urlreq); if (ret < 0) { /* Drop any related requests upon error */ imap_urlauth_request_abort_by_context(conn, urlreq_context); } if (ret != 0) imap_urlauth_connection_continue(conn); } static void imap_urlauth_target_abort(struct imap_urlauth_connection *conn, struct imap_urlauth_target *target) { struct imap_urlauth_request *urlreq, *next; urlreq = target->requests_head; while (urlreq != NULL) { next = urlreq->next; imap_urlauth_request_abort(conn, urlreq); urlreq = next; } imap_urlauth_target_free(conn, target); } static void imap_urlauth_target_fail(struct imap_urlauth_connection *conn, struct imap_urlauth_target *target, const char *error) { struct imap_urlauth_request *urlreq, *next; urlreq = target->requests_head; while (urlreq != NULL) { next = urlreq->next; imap_urlauth_request_fail(conn, urlreq, error); urlreq = next; } imap_urlauth_target_free(conn, target); } static void imap_urlauth_target_abort_by_context(struct imap_urlauth_connection *conn, struct imap_urlauth_target *target, void *context) { struct imap_urlauth_request *urlreq, *next; /* abort all matching requests */ urlreq = target->requests_head; while (urlreq != NULL) { next = urlreq->next; if (urlreq->context == context) imap_urlauth_request_abort(conn, urlreq); urlreq = next; } if (target->requests_head == NULL) imap_urlauth_target_free(conn, target); } static void imap_urlauth_connection_abort(struct imap_urlauth_connection *conn, const char *reason) { struct imap_urlauth_target *target, *next; if (reason == NULL) reason = "Aborting due to error"; imap_urlauth_connection_disconnect(conn, reason); /* abort all requests */ target = conn->targets_head; while (target != NULL) { next = target->next; imap_urlauth_target_abort(conn, target); target = next; } } void imap_urlauth_request_abort_by_context(struct imap_urlauth_connection *conn, void *context) { struct imap_urlauth_target *target, *next; /* abort all matching requests */ target = conn->targets_head; while (target != NULL) { next = target->next; imap_urlauth_target_abort_by_context(conn, target, context); target = next; } } static void imap_urlauth_connection_fail(struct imap_urlauth_connection *conn) { if (conn->reconnect_attempts > IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) { imap_urlauth_connection_abort(conn, "Connection failed and connection attempts exhausted"); } else { imap_urlauth_connection_reconnect(conn); } } static int imap_urlauth_connection_create_temp_fd(struct imap_urlauth_connection *conn, const char **path_r) { string_t *path; int fd; path = t_str_new(128); mail_user_set_get_temp_prefix(path, conn->user->set); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static int imap_urlauth_connection_read_literal_init(struct imap_urlauth_connection *conn, uoff_t size) { const char *path; i_assert(conn->literal_fd == -1 && conn->literal_buf == NULL); if (size <= IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE) { /* read the literal directly */ if (size > 0) { conn->literal_buf = buffer_create_dynamic(default_pool, size); } } else { /* read it into a file */ conn->literal_fd = imap_urlauth_connection_create_temp_fd(conn, &path); if (conn->literal_fd == -1) return -1; conn->literal_temp_path = i_strdup(path); } conn->literal_size = size; conn->literal_bytes_left = size; conn->reading_literal = TRUE; return 1; } void imap_urlauth_connection_continue(struct imap_urlauth_connection *conn) { i_assert(conn->targets_head != NULL); i_assert(conn->targets_head->requests_head != NULL); if (conn->state != IMAP_URLAUTH_STATE_REQUEST_WAIT) return; conn->state = IMAP_URLAUTH_STATE_READY; imap_urlauth_request_free(conn->targets_head->requests_head); imap_urlauth_connection_send_request(conn); } static int imap_urlauth_connection_read_literal_data(struct imap_urlauth_connection *conn) { const unsigned char *data; size_t size; /* read data */ data = i_stream_get_data(conn->input, &size); if (size > conn->literal_bytes_left) size = conn->literal_bytes_left; /* write to buffer or file */ if (size > 0) { if (conn->literal_fd >= 0) { if (write_full(conn->literal_fd, data, size) < 0) { i_error("imap-urlauth: write(%s) failed: %m", conn->literal_temp_path); return -1; } } else { i_assert(conn->literal_buf != NULL); buffer_append(conn->literal_buf, data, size); } i_stream_skip(conn->input, size); conn->literal_bytes_left -= size; } /* exit if not finished */ if (conn->literal_bytes_left > 0) return 0; /* read LF guard */ data = i_stream_get_data(conn->input, &size); if (size < 1) return 0; /* check LF guard */ if (data[0] != '\n') { i_error("imap-urlauth: no LF at end of literal (found 0x%x)", data[0]); return -1; } i_stream_skip(conn->input, 1); return 1; } static void literal_stream_destroy(buffer_t *buffer) { buffer_free(&buffer); } static int imap_urlauth_fetch_reply_set_literal_stream(struct imap_urlauth_connection *conn, struct imap_urlauth_fetch_reply *reply) { const unsigned char *data; size_t size; uoff_t fd_size; if (conn->literal_fd != -1) { reply->input = i_stream_create_fd_autoclose(&conn->literal_fd, (size_t)-1); if (i_stream_get_size(reply->input, TRUE, &fd_size) < 1 || fd_size != conn->literal_size) { i_stream_unref(&reply->input); i_error("imap-urlauth: Failed to obtain proper size from literal stream"); imap_urlauth_connection_abort(conn, "Failed during literal transfer"); return -1; } } else { data = buffer_get_data(conn->literal_buf, &size); i_assert(size == conn->literal_size); reply->input = i_stream_create_from_data(data, size); i_stream_add_destroy_callback(reply->input, literal_stream_destroy, conn->literal_buf); } reply->size = conn->literal_size; return 0; } static int imap_urlauth_connection_read_literal(struct imap_urlauth_connection *conn) { struct imap_urlauth_request *urlreq = conn->targets_head->requests_head; struct imap_urlauth_fetch_reply reply; imap_urlauth_request_callback_t *callback; int ret; i_assert(conn->reading_literal); i_assert(urlreq != NULL); if (conn->literal_size > 0) { ret = imap_urlauth_connection_read_literal_data(conn); if (ret <= 0) return ret; } i_assert(conn->literal_bytes_left == 0); /* reply */ i_zero(&reply); reply.url = urlreq->url; reply.flags = urlreq->flags; reply.bodypartstruct = urlreq->bodypartstruct; reply.binary_has_nuls = urlreq->binary_has_nuls; if (conn->literal_size > 0) { if (imap_urlauth_fetch_reply_set_literal_stream(conn, &reply) < 0) return -1; } reply.succeeded = TRUE; ret = 1; callback = urlreq->callback; urlreq->callback = NULL; if (callback != NULL) T_BEGIN { ret = callback(&reply, urlreq->context); } T_END; if (reply.input != NULL) i_stream_unref(&reply.input); if (ret < 0) { /* Drop any related requests upon error */ imap_urlauth_request_abort_by_context(conn, urlreq->context); } conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT; if (ret != 0) imap_urlauth_connection_continue(conn); /* finished */ i_free_and_null(conn->literal_temp_path); conn->literal_fd = -1; conn->literal_buf = NULL; conn->reading_literal = FALSE; return 1; } static int imap_urlauth_input_pending(struct imap_urlauth_connection *conn) { struct imap_urlauth_request *urlreq; const char *response, *const *args, *bpstruct = NULL; uoff_t literal_size; i_assert(conn->targets_head != NULL); i_assert(conn->targets_head->requests_head != NULL); urlreq = conn->targets_head->requests_head; if (conn->reading_literal) { /* Read pending literal; may callback */ return imap_urlauth_connection_read_literal(conn); } /* "OK"[]"\t""\n" or "NO"["\terror="]"\n" */ if ((response = i_stream_next_line(conn->input)) == NULL) return 0; imap_urlauth_stop_response_timeout(conn); args = t_strsplit_tabescaped(response); if (args[0] == NULL) { i_error("imap-urlauth: Empty URL response: %s", str_sanitize(response, 80)); return -1; } if (strcmp(args[0], "OK") != 0 || args[1] == NULL) { if (strcmp(args[0], "NO") == 0) { const char *param = args[1], *error = NULL; if (param != NULL && strncasecmp(param, "error=", 6) == 0 && param[6] != '\0') { error = param+6; } conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT; imap_urlauth_request_fail(conn, conn->targets_head->requests_head, error); return 1; } i_error("imap-urlauth: Unexpected URL response: %s", str_sanitize(response, 80)); return -1; } /* read metadata */ args++; for (; args[1] != NULL; args++) { const char *param = args[0]; if (strcasecmp(param, "hasnuls") == 0) { urlreq->binary_has_nuls = TRUE; } else if (strncasecmp(param, "bpstruct=", 9) == 0 && param[9] != '\0') { bpstruct = param+9; } } /* read literal size */ if (str_to_uoff(args[0], &literal_size) < 0) { i_error("imap-urlauth: " "Overflowing unsigned integer value for literal size: %s", args[1]); return -1; } /* read literal */ if (imap_urlauth_connection_read_literal_init(conn, literal_size) < 0) return -1; urlreq->bodypartstruct = i_strdup(bpstruct); return imap_urlauth_connection_read_literal(conn); } static int imap_urlauth_input_next(struct imap_urlauth_connection *conn) { const char *response; int ret; switch (conn->state) { case IMAP_URLAUTH_STATE_AUTHENTICATING: case IMAP_URLAUTH_STATE_UNSELECTING_TARGET: if ((response = i_stream_next_line(conn->input)) == NULL) return 0; imap_urlauth_stop_response_timeout(conn); if (strcasecmp(response, "OK") != 0) { if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING) i_error("imap-urlauth: Failed to authenticate to service: " "Got unexpected response: %s", str_sanitize(response, 80)); else i_error("imap-urlauth: Failed to unselect target user: " "Got unexpected response: %s", str_sanitize(response, 80)); imap_urlauth_connection_abort(conn, NULL); return -1; } if (conn->user->mail_debug) { if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING) i_debug("imap-urlauth: Successfully authenticated to service"); else i_debug("imap-urlauth: Successfully unselected target user"); } conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED; imap_urlauth_connection_select_target(conn); return 0; case IMAP_URLAUTH_STATE_SELECTING_TARGET: if ((response = i_stream_next_line(conn->input)) == NULL) return 0; imap_urlauth_stop_response_timeout(conn); i_assert(conn->targets_head != NULL); if (strcasecmp(response, "NO") == 0) { if (conn->user->mail_debug) { i_debug("imap-urlauth: Failed to select target user %s", conn->targets_head->userid); } imap_urlauth_target_fail(conn, conn->targets_head, NULL); conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED; imap_urlauth_connection_select_target(conn); return 0; } if (strcasecmp(response, "OK") != 0) { i_error("imap-urlauth: Failed to select target user %s: " "Got unexpected response: %s", conn->targets_head->userid, str_sanitize(response, 80)); imap_urlauth_connection_abort(conn, NULL); return -1; } if (conn->user->mail_debug) { i_debug("imap-urlauth: Successfully selected target user %s", conn->targets_head->userid); } conn->state = IMAP_URLAUTH_STATE_READY; imap_urlauth_connection_send_request(conn); return 0; case IMAP_URLAUTH_STATE_AUTHENTICATED: case IMAP_URLAUTH_STATE_READY: case IMAP_URLAUTH_STATE_REQUEST_WAIT: if ((response = i_stream_next_line(conn->input)) == NULL) return 0; i_error("imap-urlauth: Received input while no requests were pending: %s", str_sanitize(response, 80)); imap_urlauth_connection_abort(conn, NULL); return -1; case IMAP_URLAUTH_STATE_REQUEST_PENDING: if ((ret = imap_urlauth_input_pending(conn)) < 0) imap_urlauth_connection_fail(conn); return ret; case IMAP_URLAUTH_STATE_DISCONNECTED: break; } i_unreached(); } static void imap_urlauth_input(struct imap_urlauth_connection *conn) { int ret; i_assert(conn->state != IMAP_URLAUTH_STATE_DISCONNECTED); if (conn->input->closed) { /* disconnected */ i_error("imap-urlauth: Service disconnected unexpectedly"); imap_urlauth_connection_fail(conn); return; } switch (i_stream_read(conn->input)) { case -1: /* disconnected */ i_error("imap-urlauth: Service disconnected unexpectedly"); imap_urlauth_connection_fail(conn); return; case -2: /* input buffer full */ i_error("imap-urlauth: Service sent too large input"); imap_urlauth_connection_abort(conn, NULL); return; } while (!conn->input->closed) { if ((ret = imap_urlauth_input_next(conn)) <= 0) break; } } static int imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn) { string_t *str; int fd; if (conn->state != IMAP_URLAUTH_STATE_DISCONNECTED) { imap_urlauth_connection_send_request(conn); return 1; } if (conn->user->auth_token == NULL) { i_error("imap-urlauth: cannot authenticate because no auth token " "is available for this session (standalone IMAP?)."); imap_urlauth_connection_abort(conn, NULL); return -1; } if (conn->user->mail_debug) i_debug("imap-urlauth: Connecting to service at %s", conn->path); i_assert(conn->fd == -1); fd = net_connect_unix(conn->path); if (fd == -1) { i_error("imap-urlauth: net_connect_unix(%s) failed: %m", conn->path); imap_urlauth_connection_abort(conn, NULL); return -1; } if (conn->to_reconnect != NULL) timeout_remove(&conn->to_reconnect); conn->fd = fd; conn->input = i_stream_create_fd(fd, (size_t)-1, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); conn->io = io_add(fd, IO_READ, imap_urlauth_input, conn); conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING; str = t_str_new(128); str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t", my_pid); str_append_tabescaped(str, conn->user->username); str_append_c(str, '\t'); if (conn->session_id != NULL) str_append_tabescaped(str, conn->session_id); str_append_c(str, '\t'); str_append_tabescaped(str, conn->user->auth_token); str_append_c(str, '\n'); if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) { i_warning("Error sending handshake to imap-urlauth server: %m"); imap_urlauth_connection_abort(conn, NULL); return -1; } imap_urlauth_start_response_timeout(conn); return 0; } int imap_urlauth_connection_connect(struct imap_urlauth_connection *conn) { conn->reconnect_attempts = 0; if (conn->to_reconnect == NULL) return imap_urlauth_connection_do_connect(conn); return 0; } static void imap_urlauth_connection_disconnect (struct imap_urlauth_connection *conn, const char *reason) { conn->state = IMAP_URLAUTH_STATE_DISCONNECTED; if (conn->fd != -1) { if (conn->user->mail_debug) { if (reason == NULL) i_debug("imap-urlauth: Disconnecting from service"); else i_debug("imap-urlauth: Disconnected: %s", reason); } io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); net_disconnect(conn->fd); conn->fd = -1; } conn->reading_literal = FALSE; if (conn->literal_fd != -1) { if (close(conn->literal_fd) < 0) i_error("imap-urlauth: close(%s) failed: %m", conn->literal_temp_path); i_free_and_null(conn->literal_temp_path); conn->literal_fd = -1; } if (conn->literal_buf != NULL) buffer_free(&conn->literal_buf); if (conn->to_reconnect != NULL) timeout_remove(&conn->to_reconnect); if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); imap_urlauth_stop_response_timeout(conn); } static void imap_urlauth_connection_do_reconnect(struct imap_urlauth_connection *conn) { if (conn->reconnect_attempts >= IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) { imap_urlauth_connection_abort(conn, "Connection failed and connection attempts exhausted"); return; } if (ioloop_time - conn->last_reconnect < IMAP_URLAUTH_RECONNECT_MIN_SECS) { if (conn->user->mail_debug) i_debug("imap-urlauth: Scheduling reconnect"); if (conn->to_reconnect != NULL) timeout_remove(&conn->to_reconnect); conn->to_reconnect = timeout_add(IMAP_URLAUTH_RECONNECT_MIN_SECS*1000, imap_urlauth_connection_do_reconnect, conn); } else { conn->reconnect_attempts++; conn->last_reconnect = ioloop_time; (void)imap_urlauth_connection_do_connect(conn); } } static void imap_urlauth_connection_reconnect(struct imap_urlauth_connection *conn) { imap_urlauth_connection_disconnect(conn, NULL); /* don't reconnect if there are no requests */ if (conn->targets_head == NULL) return; imap_urlauth_connection_do_reconnect(conn); } static void imap_urlauth_connection_idle_disconnect(struct imap_urlauth_connection *conn) { imap_urlauth_connection_disconnect(conn, "Idle timeout"); } static void imap_urlauth_connection_timeout_abort(struct imap_urlauth_connection *conn) { imap_urlauth_connection_abort(conn, "Service is not responding"); } bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn) { return conn->fd != -1 && conn->state != IMAP_URLAUTH_STATE_DISCONNECTED; } dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth-backend.h0000644000175000017500000000071713123174404020442 00000000000000#ifndef IMAP_URLAUTH_BACKEND_H #define IMAP_URLAUTH_BACKEND_H #define IMAP_URLAUTH_KEY_LEN 64 struct imap_urlauth_backend; int imap_urlauth_backend_get_mailbox_key(struct mailbox *box, bool create, unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], const char **error_r, enum mail_error *error_code_r); int imap_urlauth_backend_reset_mailbox_key(struct mailbox *box); int imap_urlauth_backend_reset_all_keys(struct mail_user *user); #endif dovecot-2.2.33.2/src/lib-imap-urlauth/Makefile.am0000644000175000017500000000120613123174404016322 00000000000000noinst_LTLIBRARIES = libimap-urlauth.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-storage libimap_urlauth_la_SOURCES = \ imap-urlauth.c \ imap-urlauth-fetch.c \ imap-urlauth-backend.c \ imap-urlauth-connection.c headers = \ imap-urlauth.h \ imap-urlauth-private.h \ imap-urlauth-fetch.h \ imap-urlauth-backend.h \ imap-urlauth-connection.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-imap-urlauth/imap-urlauth.c0000644000175000017500000003077513165463624017072 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "var-expand.h" #include "hmac.h" #include "sha1.h" #include "randgen.h" #include "safe-memset.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "mail-user.h" #include "imap-url.h" #include "imap-msgpart-url.h" #include "imap-urlauth-backend.h" #include "imap-urlauth-fetch.h" #include "imap-urlauth-connection.h" #include "imap-urlauth-private.h" #include #define IMAP_URLAUTH_MECH_INTERNAL_VERSION 0x01 #define IMAP_URLAUTH_NORMAL_TIMEOUT_MSECS 5*1000 #define IMAP_URLAUTH_SPECIAL_TIMEOUT_MSECS 3*60*1000 #define URL_HOST_ALLOW_ANY "*" struct imap_urlauth_context * imap_urlauth_init(struct mail_user *user, const struct imap_urlauth_config *config) { struct imap_urlauth_context *uctx; unsigned int timeout; i_assert(*config->url_host != '\0'); uctx = i_new(struct imap_urlauth_context, 1); uctx->user = user; uctx->url_host = i_strdup(config->url_host); uctx->url_port = config->url_port; if (config->access_anonymous) uctx->access_user = i_strdup("anonymous"); else uctx->access_user = i_strdup(config->access_user); uctx->access_anonymous = config->access_anonymous; if (config->access_applications != NULL && *config->access_applications != NULL) { uctx->access_applications = p_strarray_dup(default_pool, config->access_applications); timeout = IMAP_URLAUTH_SPECIAL_TIMEOUT_MSECS; } else { timeout = IMAP_URLAUTH_NORMAL_TIMEOUT_MSECS; } if (config->socket_path != NULL) { uctx->conn = imap_urlauth_connection_init(config->socket_path, user, config->session_id, timeout); } return uctx; } void imap_urlauth_deinit(struct imap_urlauth_context **_uctx) { struct imap_urlauth_context *uctx = *_uctx; *_uctx = NULL; if (uctx->conn != NULL) imap_urlauth_connection_deinit(&uctx->conn); i_free(uctx->url_host); i_free(uctx->access_user); i_free(uctx->access_applications); i_free(uctx); } static const unsigned char * imap_urlauth_internal_generate(const char *rumpurl, const unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN], size_t *token_len_r) { struct hmac_context hmac; unsigned char *token; token = t_new(unsigned char, SHA1_RESULTLEN + 1); token[0] = IMAP_URLAUTH_MECH_INTERNAL_VERSION; hmac_init(&hmac, mailbox_key, IMAP_URLAUTH_KEY_LEN, &hash_method_sha1); hmac_update(&hmac, rumpurl, strlen(rumpurl)); hmac_final(&hmac, token+1); *token_len_r = SHA1_RESULTLEN + 1; return token; } static bool imap_urlauth_internal_verify(const char *rumpurl, const unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN], const unsigned char *token, size_t token_len) { const unsigned char *valtoken; size_t valtoken_len; if (rumpurl == NULL || token == NULL) return FALSE; valtoken = imap_urlauth_internal_generate(rumpurl, mailbox_key, &valtoken_len); if (token_len != valtoken_len) return FALSE; return memcmp(token, valtoken, valtoken_len) == 0; } static bool access_applications_have_access(struct imap_url *url, const char *const *access_applications) { const char *const *application; if (access_applications == NULL) return FALSE; application = access_applications; for (; *application != NULL; application++) { const char *app = *application; bool have_userid = FALSE; size_t len = strlen(app); if (app[len-1] == '+') { have_userid = TRUE; app = t_strndup(app, len-1); } if (strcasecmp(url->uauth_access_application, app) == 0) { if (have_userid) return url->uauth_access_user != NULL; else return url->uauth_access_user == NULL; } } return FALSE; } static bool imap_urlauth_check_access(struct imap_urlauth_context *uctx, struct imap_url *url, bool ignore_unknown, const char **error_r) { if (url->uauth_access_application == NULL) { *error_r = "URL is missing URLAUTH"; return FALSE; } if (strcasecmp(url->uauth_access_application, "user") == 0) { /* user+ */ if (uctx->access_anonymous || strcasecmp(url->uauth_access_user, uctx->access_user) != 0) { if (uctx->access_anonymous) { *error_r = t_strdup_printf( "No 'user+%s' access allowed for anonymous user", url->uauth_access_user); } else { *error_r = t_strdup_printf("No 'user+%s' access allowed for user %s", url->uauth_access_user, uctx->access_user); } return FALSE; } } else if (strcasecmp(url->uauth_access_application, "authuser") == 0) { /* authuser */ if (uctx->access_anonymous) { *error_r = "No 'authuser' access allowed for anonymous user"; return FALSE; } } else if (strcasecmp(url->uauth_access_application, "anonymous") == 0) { /* anonymous */ } else if (!ignore_unknown && !access_applications_have_access(url, uctx->access_applications)) { const char *userid = url->uauth_access_user == NULL ? "" : t_strdup_printf("+%s", url->uauth_access_user); if (uctx->access_anonymous) { *error_r = t_strdup_printf( "No '%s%s' access allowed for anonymous user", url->uauth_access_application, userid); } else { *error_r = t_strdup_printf( "No '%s%s' access allowed for user %s", url->uauth_access_application, userid, uctx->access_user); } return FALSE; } return TRUE; } static bool imap_urlauth_check_hostport(struct imap_urlauth_context *uctx, struct imap_url *url, const char **error_r) { /* validate host */ /* FIXME: allow host ip/ip6 as well? */ if (strcmp(uctx->url_host, URL_HOST_ALLOW_ANY) != 0 && strcmp(url->host_name, uctx->url_host) != 0) { *error_r = "Invalid URL: Inappropriate host name"; return FALSE; } /* validate port */ if ((!url->have_port && uctx->url_port != 143) || (url->have_port && uctx->url_port != url->port)) { *error_r = "Invalid URL: Inappropriate server port"; return FALSE; } return TRUE; } int imap_urlauth_generate(struct imap_urlauth_context *uctx, const char *mechanism, const char *rumpurl, const char **urlauth_r, const char **error_r) { struct mail_user *user = uctx->user; enum imap_url_parse_flags url_flags = IMAP_URL_PARSE_ALLOW_URLAUTH; struct imap_url *url; struct imap_msgpart_url *mpurl = NULL; struct mailbox *box; const char *error; enum mail_error error_code; unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN]; const unsigned char *token; size_t token_len; int ret; /* validate mechanism */ if (strcasecmp(mechanism, "INTERNAL") != 0) { *error_r = t_strdup_printf("Unsupported URLAUTH mechanism: %s", mechanism); return 0; } /* validate URL */ if (imap_url_parse(rumpurl, NULL, url_flags, &url, &error) < 0) { *error_r = t_strdup_printf("Invalid URL: %s", error); return 0; } if (url->mailbox == NULL || url->uid == 0 || url->search_program != NULL || url->uauth_rumpurl == NULL || url->uauth_mechanism != NULL) { *error_r = "Invalid URL: Must be an URLAUTH rump URL"; return 0; } /* validate expiry time */ if (url->uauth_expire != (time_t)-1) { time_t now = time(NULL); if (now > url->uauth_expire) { *error_r = t_strdup_printf("URLAUTH has already expired"); return 0; } } /* validate user */ if (url->userid == NULL) { *error_r = "Invalid URL: Missing user name"; return 0; } if (user->anonymous || strcmp(url->userid, user->username) != 0) { *error_r = t_strdup_printf( "Not permitted to generate URLAUTH for user %s", url->userid); return 0; } /* validate host:port */ if (!imap_urlauth_check_hostport(uctx, url, error_r)) return 0; /* validate mailbox */ if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0 || imap_msgpart_url_verify(mpurl, &error) <= 0) { *error_r = t_strdup_printf("Invalid URL: %s", error); if (mpurl != NULL) imap_msgpart_url_free(&mpurl); return ret; } box = imap_msgpart_url_get_mailbox(mpurl); /* obtain mailbox key */ ret = imap_urlauth_backend_get_mailbox_key(box, TRUE, mailbox_key, error_r, &error_code); if (ret < 0) { imap_msgpart_url_free(&mpurl); return ret; } token = imap_urlauth_internal_generate(rumpurl, mailbox_key, &token_len); imap_msgpart_url_free(&mpurl); *urlauth_r = imap_url_add_urlauth(rumpurl, mechanism, token, token_len); return 1; } bool imap_urlauth_check(struct imap_urlauth_context *uctx, struct imap_url *url, bool ignore_unknown_access, const char **error_r) { /* validate URL fields */ if (url->mailbox == NULL || url->uid == 0 || url->search_program != NULL || url->uauth_rumpurl == NULL || url->uauth_mechanism == NULL) { *error_r = "Invalid URL: Must be a full URLAUTH URL"; return FALSE; } /* check presence of userid */ if (url->userid == NULL) { *error_r = "Invalid URLAUTH: Missing user name"; return FALSE; } /* validate mechanism */ if (strcasecmp(url->uauth_mechanism, "INTERNAL") != 0) { *error_r = t_strdup_printf("Unsupported URLAUTH mechanism: %s", url->uauth_mechanism); return FALSE; } /* validate expiry time */ if (url->uauth_expire != (time_t)-1) { time_t now = time(NULL); if (now > url->uauth_expire) { *error_r = t_strdup_printf("URLAUTH has expired"); return FALSE; } } /* validate access */ if (!imap_urlauth_check_access(uctx, url, ignore_unknown_access, error_r)) return FALSE; /* validate host:port */ if (!imap_urlauth_check_hostport(uctx, url, error_r)) return FALSE; return TRUE; } int imap_urlauth_fetch_parsed(struct imap_urlauth_context *uctx, struct imap_url *url, struct imap_msgpart_url **mpurl_r, enum mail_error *error_code_r, const char **error_r) { struct mail_user *user = uctx->user; struct imap_msgpart_url *mpurl; struct mailbox *box; const char *error; unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN]; int ret; *mpurl_r = NULL; *error_r = NULL; *error_code_r = MAIL_ERROR_NONE; /* check urlauth mechanism, access, userid and authority */ if (!imap_urlauth_check(uctx, url, FALSE, error_r)) { *error_code_r = MAIL_ERROR_PARAMS; return 0; } /* validate target user */ if (user->anonymous || strcmp(url->userid, user->username) != 0) { *error_r = t_strdup_printf("Not permitted to fetch URLAUTH for user %s", url->userid); *error_code_r = MAIL_ERROR_PARAMS; return 0; } /* validate mailbox */ if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0) { *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); *error_code_r = MAIL_ERROR_PARAMS; return ret; } if ((ret = imap_msgpart_url_open_mailbox(mpurl, &box, error_code_r, &error)) < 0) { *error_r = "Internal server error"; imap_msgpart_url_free(&mpurl); return -1; } if (ret == 0) { /* RFC says: `If the mailbox cannot be identified, an authorization token is calculated on the rump URL, using random "plausible" keys (selected by the server) as needed, before returning a validation failure. This prevents timing attacks aimed at identifying mailbox names.' */ random_fill(mailbox_key, sizeof(mailbox_key)); (void)imap_urlauth_internal_verify(url->uauth_rumpurl, mailbox_key, url->uauth_token, url->uauth_token_size); *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); imap_msgpart_url_free(&mpurl); return 0; } /* obtain mailbox key */ ret = imap_urlauth_backend_get_mailbox_key(box, FALSE, mailbox_key, error_r, error_code_r); if (ret < 0) { imap_msgpart_url_free(&mpurl); return -1; } if (ret == 0 || !imap_urlauth_internal_verify(url->uauth_rumpurl, mailbox_key, url->uauth_token, url->uauth_token_size)) { *error_r = "URLAUTH verification failed"; *error_code_r = MAIL_ERROR_PERM; imap_msgpart_url_free(&mpurl); ret = 0; } else { ret = 1; } safe_memset(mailbox_key, 0, sizeof(mailbox_key)); *mpurl_r = mpurl; return ret; } int imap_urlauth_fetch(struct imap_urlauth_context *uctx, const char *urlauth, struct imap_msgpart_url **mpurl_r, enum mail_error *error_code_r, const char **error_r) { struct imap_url *url; enum imap_url_parse_flags url_flags = IMAP_URL_PARSE_ALLOW_URLAUTH; const char *error; /* validate URL */ if (imap_url_parse(urlauth, NULL, url_flags, &url, &error) < 0) { *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); *error_code_r = MAIL_ERROR_PARAMS; return 0; } return imap_urlauth_fetch_parsed(uctx, url, mpurl_r, error_code_r, error_r); } int imap_urlauth_reset_mailbox_key(struct imap_urlauth_context *uctx ATTR_UNUSED, struct mailbox *box) { return imap_urlauth_backend_reset_mailbox_key(box); } int imap_urlauth_reset_all_keys(struct imap_urlauth_context *uctx) { return imap_urlauth_backend_reset_all_keys(uctx->user); } dovecot-2.2.33.2/src/lib-oauth2/0002755000175000017500000000000013172375611013152 500000000000000dovecot-2.2.33.2/src/lib-oauth2/Makefile.in0000644000175000017500000005463213172375573015156 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-oauth2 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) liboauth2_la_LIBADD = am_liboauth2_la_OBJECTS = oauth2.lo oauth2-token-validate.lo \ oauth2-introspect.lo oauth2-refresh.lo liboauth2_la_OBJECTS = $(am_liboauth2_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(liboauth2_la_SOURCES) DIST_SOURCES = $(liboauth2_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-settings noinst_LTLIBRARIES = liboauth2.la pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ oauth2.h noinst_HEADERS = \ oauth2-private.h liboauth2_la_SOURCES = \ oauth2.c \ oauth2-token-validate.c \ oauth2-introspect.c \ oauth2-refresh.c check_programs = \ oauth2-server \ test-oauth2 all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-oauth2/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-oauth2/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } liboauth2.la: $(liboauth2_la_OBJECTS) $(liboauth2_la_DEPENDENCIES) $(EXTRA_liboauth2_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(liboauth2_la_OBJECTS) $(liboauth2_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oauth2-introspect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oauth2-refresh.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oauth2-token-validate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oauth2.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-oauth2/oauth2-introspect.c0000644000175000017500000000756413123174404016633 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "ioloop.h" #include "istream.h" #include "http-client.h" #include "http-url.h" #include "json-parser.h" #include "oauth2.h" #include "oauth2-private.h" static void oauth2_introspection_callback(struct oauth2_request *req, struct oauth2_introspection_result *res) { i_assert(res->success == (res->error == NULL)); i_assert(req->is_callback != NULL); oauth2_introspection_callback_t *callback = req->is_callback; req->is_callback = NULL; callback(res, req->is_context); oauth2_request_free_internal(req); } static void oauth2_introspect_continue(struct oauth2_request *req, bool success, const char *error) { struct oauth2_introspection_result res; i_zero(&res); res.success = success; res.error = error; res.fields = &req->fields; oauth2_introspection_callback(req, &res); } static void oauth2_introspect_response(const struct http_response *response, struct oauth2_request *req) { if (response->status / 100 != 2) { oauth2_introspect_continue(req, FALSE, response->reason); } else { if (response->payload == NULL) { oauth2_introspect_continue(req, FALSE, "Missing response body"); return; } p_array_init(&req->fields, req->pool, 1); req->is = response->payload; i_stream_ref(req->is); req->parser = json_parser_init(req->is); req->json_parsed_cb = oauth2_introspect_continue; req->io = io_add_istream(req->is, oauth2_parse_json, req); oauth2_parse_json(req); } } static void oauth2_introspection_delayed_error(struct oauth2_request *req) { struct oauth2_introspection_result fail = { .success = FALSE, .error = req->delayed_error }; oauth2_introspection_callback(req, &fail); } #undef oauth2_introspection_start struct oauth2_request* oauth2_introspection_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_introspection_callback_t *callback, void *context) { i_assert(oauth2_valid_token(input->token)); pool_t pool = pool_alloconly_create_clean("oauth2 introspection", 1024); struct oauth2_request *req = p_new(pool, struct oauth2_request, 1); struct http_url *url; const char *error; req->pool = pool; req->set = set; req->is_callback = callback; req->is_context = context; string_t *enc = t_str_new(64); str_append(enc, req->set->introspection_url); if (set->introspection_mode == INTROSPECTION_MODE_GET) { http_url_escape_param(enc, input->token); } if (http_url_parse(str_c(enc), NULL, HTTP_URL_ALLOW_USERINFO_PART, pool, &url, &error) < 0) { req->delayed_error = p_strdup_printf(pool, "http_url_parse(%s) failed: %s", str_c(enc), error); req->to_delayed_error = timeout_add_short(0, oauth2_introspection_delayed_error, req); return req; } if (set->introspection_mode == INTROSPECTION_MODE_POST) { req->req = http_client_request_url(req->set->client, "POST", url, oauth2_introspect_response, req); /* add token */ enc = t_str_new(strlen(input->token)+6); str_append(enc, "token="); http_url_escape_param(enc, input->token); http_client_request_add_header(req->req, "Content-Type", "application/x-www-form-urlencoded"); http_client_request_set_payload_data(req->req, enc->data, enc->used); } else { req->req = http_client_request_url(req->set->client, "GET", url, oauth2_introspect_response, req); } if (url->user != NULL) http_client_request_set_auth_simple(req->req, url->user, url->password); else if (set->introspection_mode == INTROSPECTION_MODE_GET_AUTH) http_client_request_add_header(req->req, "Authorization", t_strdup_printf("Bearer %s", input->token)); oauth2_request_set_headers(req, input); http_client_request_set_timeout_msecs(req->req, req->set->timeout_msecs); http_client_request_submit(req->req); return req; } dovecot-2.2.33.2/src/lib-oauth2/oauth2.h0000644000175000017500000000653013123174404014440 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #ifndef OAUTH2_H #define OAUTH2_H #include "net.h" struct oauth2_request; struct oauth2_field { const char *name; const char *value; }; ARRAY_DEFINE_TYPE(oauth2_field, struct oauth2_field); struct oauth2_settings { struct http_client *client; /* GET tokeninfo from this URL, token is appended to URL http://some.host/path?access_token= */ const char *tokeninfo_url; /* GET more information from this URL, uses Bearer authentication */ const char *introspection_url; /* POST refresh here, needs refresh token and client_* settings */ const char *refresh_url; const char *client_id; const char *client_secret; enum { INTROSPECTION_MODE_GET_AUTH, INTROSPECTION_MODE_GET, INTROSPECTION_MODE_POST } introspection_mode; unsigned int timeout_msecs; /* Should X-Dovecot-Auth-* headers be sent */ bool send_auth_headers; }; struct oauth2_token_validation_result { ARRAY_TYPE(oauth2_field) *fields; const char *error; time_t expires_at; bool success:1; bool valid:1; }; struct oauth2_introspection_result { ARRAY_TYPE(oauth2_field) *fields; const char *error; bool success:1; }; struct oauth2_refresh_result { ARRAY_TYPE(oauth2_field) *fields; const char *bearer_token; const char *error; time_t expires_at; bool success:1; }; struct oauth2_request_input { const char *token; const char *service; struct ip_addr local_ip, real_local_ip, remote_ip, real_remote_ip; in_port_t local_port, real_local_port, remote_port, real_remote_port; }; typedef void oauth2_token_validation_callback_t(struct oauth2_token_validation_result*, void*); typedef void oauth2_introspection_callback_t(struct oauth2_introspection_result*, void*); typedef void oauth2_refresh_callback_t(struct oauth2_refresh_result*, void*); bool oauth2_valid_token(const char *token); struct oauth2_request* oauth2_token_validation_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_token_validation_callback_t *callback, void *context); #define oauth2_token_validation_start(set, input, callback, context) \ oauth2_token_validation_start(set, input + \ CALLBACK_TYPECHECK(callback, void(*)(struct oauth2_token_validation_result*, typeof(context))), \ (oauth2_token_validation_callback_t*)callback, (void*)context); struct oauth2_request* oauth2_introspection_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_introspection_callback_t *callback, void *context); #define oauth2_introspection_start(set, input, callback, context) \ oauth2_introspection_start(set, input + \ CALLBACK_TYPECHECK(callback, void(*)(struct oauth2_introspection_result*, typeof(context))), \ (oauth2_introspection_callback_t*)callback, (void*)context); struct oauth2_request* oauth2_refresh_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_refresh_callback_t *callback, void *context); #define oauth2_refresh_start(set, input, callback, context) \ oauth2_refresh_start(set, input + \ CALLBACK_TYPECHECK(callback, void(*)(struct oauth2_refresh_result*, typeof(context))), \ (oauth2_refresh_callback_t*)callback, (void*)context); /* abort without calling callback, use this to cancel the request */ void oauth2_request_abort(struct oauth2_request **); #endif dovecot-2.2.33.2/src/lib-oauth2/oauth2-refresh.c0000644000175000017500000001100613123174404016061 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "str.h" #include "http-client.h" #include "http-url.h" #include "json-parser.h" #include "oauth2.h" #include "oauth2-private.h" static void oauth2_refresh_callback(struct oauth2_request *req, struct oauth2_refresh_result *res) { i_assert(res->success == (res->error == NULL)); i_assert(req->re_callback != NULL); oauth2_refresh_callback_t *callback = req->re_callback; req->re_callback = NULL; callback(res, req->re_context); oauth2_request_free_internal(req); } static bool oauth2_refresh_field_parse(const struct oauth2_field *field, struct oauth2_refresh_result *res) { if (strcasecmp(field->name, "expires_in") == 0) { uint32_t expires_in = 0; if (str_to_uint32(field->value, &expires_in) < 0) { res->success = FALSE; res->error = t_strdup_printf( "Malformed number '%s' in expires_in", field->value); return FALSE; } else { res->expires_at = ioloop_time + expires_in; } } else if (strcasecmp(field->name, "token_type") == 0) { if (strcasecmp(field->value,"bearer") != 0) { res->success = FALSE; res->error = t_strdup_printf( "Expected Bearer token, got '%s'", field->value); return FALSE; } } else if (strcasecmp(field->name, "access_token") == 0) { /* pooled memory */ res->bearer_token = field->value; } return TRUE; } static void oauth2_refresh_continue(struct oauth2_request *req, bool success, const char *error) { struct oauth2_refresh_result res; i_zero(&res); res.success = success; res.error = error; if (res.success) { const struct oauth2_field *field; /* see if we can figure out when it expires */ array_foreach(&req->fields, field) { if (!oauth2_refresh_field_parse(field, &res)) break; } } res.fields = &req->fields; oauth2_refresh_callback(req, &res); } static void oauth2_refresh_response(const struct http_response *response, struct oauth2_request *req) { if (response->status / 100 != 2) { oauth2_refresh_continue(req, FALSE, response->reason); } else { if (response->payload == NULL) { oauth2_refresh_continue(req, FALSE, "Missing response body"); return; } p_array_init(&req->fields, req->pool, 1); req->is = response->payload; i_stream_ref(req->is); req->parser = json_parser_init(req->is); req->json_parsed_cb = oauth2_refresh_continue; req->io = io_add_istream(req->is, oauth2_parse_json, req); oauth2_parse_json(req); } } static void oauth2_refresh_delayed_error(struct oauth2_request *req) { struct oauth2_refresh_result fail = { .success = FALSE, .error = req->delayed_error }; oauth2_refresh_callback(req, &fail); } #undef oauth2_refresh_start struct oauth2_request* oauth2_refresh_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_refresh_callback_t *callback, void *context) { i_assert(oauth2_valid_token(input->token)); pool_t pool = pool_alloconly_create_clean("oauth2 refresh", 1024); struct oauth2_request *req = p_new(pool, struct oauth2_request, 1); struct http_url *url; const char *error; req->pool = pool; req->set = set; req->re_callback = callback; req->re_context = context; if (http_url_parse(req->set->refresh_url, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool, &url, &error) < 0) { req->delayed_error = p_strdup_printf(pool, "http_url_parse(%s) failed: %s", req->set->refresh_url, error); req->to_delayed_error = timeout_add_short(0, oauth2_refresh_delayed_error, req); return req; } req->req = http_client_request_url(req->set->client, "POST", url, oauth2_refresh_response, req); string_t *payload = str_new(req->pool, 128); str_append(payload, "client_secret="); http_url_escape_param(payload, req->set->client_secret); str_append(payload, "&grant_type=refresh_token&refresh_token="); http_url_escape_param(payload, input->token); str_append(payload, "&client_id="); http_url_escape_param(payload, req->set->client_id); struct istream *is = i_stream_create_from_string(payload); if (url->user != NULL) http_client_request_set_auth_simple(req->req, url->user, url->password); http_client_request_add_header(req->req, "Content-Type", "application/x-www-form-urlencoded"); oauth2_request_set_headers(req, input); http_client_request_set_payload(req->req, is, FALSE); i_stream_unref(&is); http_client_request_set_timeout_msecs(req->req, req->set->timeout_msecs); http_client_request_submit(req->req); return req; } dovecot-2.2.33.2/src/lib-oauth2/oauth2-private.h0000644000175000017500000000200113123174404016075 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #ifndef OAUTH2_PRIVATE_H #define OAUTH2_PRIVATE_H 1 struct oauth2_request { pool_t pool; const struct oauth2_settings *set; struct http_client_request *req; struct json_parser *parser; struct istream *is; struct io *io; const char *delayed_error; struct timeout *to_delayed_error; const char *username; void (*json_parsed_cb)(struct oauth2_request*, bool success, const char *error); ARRAY_TYPE(oauth2_field) fields; char *field_name; oauth2_token_validation_callback_t *tv_callback; void *tv_context; oauth2_introspection_callback_t *is_callback; void *is_context; oauth2_refresh_callback_t *re_callback; void *re_context; /* indicates whether token is valid */ bool valid:1; }; void oauth2_request_set_headers(struct oauth2_request *req, const struct oauth2_request_input *input); void oauth2_request_free_internal(struct oauth2_request *req); void oauth2_parse_json(struct oauth2_request *req); #endif dovecot-2.2.33.2/src/lib-oauth2/oauth2-token-validate.c0000644000175000017500000001003313123174404017331 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "ioloop.h" #include "istream.h" #include "strnum.h" #include "http-client.h" #include "http-url.h" #include "json-parser.h" #include "oauth2.h" #include "oauth2-private.h" static void oauth2_token_validation_callback(struct oauth2_request *req, struct oauth2_token_validation_result *res) { i_assert(res->success == (res->error == NULL)); i_assert(req->tv_callback != NULL); oauth2_token_validation_callback_t *callback = req->tv_callback; req->tv_callback = NULL; callback(res, req->tv_context); oauth2_request_free_internal(req); } static void oauth2_token_validate_continue(struct oauth2_request *req, bool success, const char *error) { struct oauth2_token_validation_result res; i_zero(&res); i_assert(array_is_created(&req->fields) || !success); res.success = success; res.error = error; res.valid = req->valid; if (res.success) { const struct oauth2_field *field; /* see if we can figure out when it expires */ array_foreach(&req->fields, field) { if (strcasecmp(field->name, "expires_in") == 0) { uint32_t expires_in = 0; if (str_to_uint32(field->value, &expires_in) < 0) { res.success = FALSE; res.error = "Malformed number in expires_in"; } else { res.expires_at = ioloop_time + expires_in; } break; } } } res.fields = &req->fields; oauth2_token_validation_callback(req, &res); } static void oauth2_token_validate_response(const struct http_response *response, struct oauth2_request *req) { unsigned int status_1 = response->status / 100; if (status_1 != 2 && status_1 != 4) { oauth2_token_validate_continue(req, FALSE, response->reason); } else { if (status_1 == 2) req->valid = TRUE; else req->valid = FALSE; p_array_init(&req->fields, req->pool, 1); /* 2xx is sufficient for token validation */ if (response->payload == NULL) { oauth2_token_validate_continue(req, TRUE, NULL); return; } req->is = response->payload; i_stream_ref(req->is); req->parser = json_parser_init(req->is); req->json_parsed_cb = oauth2_token_validate_continue; req->io = io_add_istream(req->is, oauth2_parse_json, req); oauth2_parse_json(req); } } static void oauth2_token_validation_delayed_error(struct oauth2_request *req) { struct oauth2_token_validation_result fail = { .success = FALSE, .error = req->delayed_error }; oauth2_token_validation_callback(req, &fail); } #undef oauth2_token_validation_start struct oauth2_request* oauth2_token_validation_start(const struct oauth2_settings *set, const struct oauth2_request_input *input, oauth2_token_validation_callback_t *callback, void *context) { i_assert(oauth2_valid_token(input->token)); struct http_url *url; const char *error; pool_t pool = pool_alloconly_create_clean("oauth2 token_validation", 1024); struct oauth2_request *req = p_new(pool, struct oauth2_request, 1); req->pool = pool; req->set = set; req->tv_callback = callback; req->tv_context = context; string_t *enc = t_str_new(64); str_append(enc, req->set->tokeninfo_url); http_url_escape_param(enc, input->token); if (http_url_parse(str_c(enc), NULL, HTTP_URL_ALLOW_USERINFO_PART, pool, &url, &error) < 0) { req->delayed_error = p_strdup_printf(pool, "http_url_parse(%s) failed: %s", str_c(enc), error); req->to_delayed_error = timeout_add_short(0, oauth2_token_validation_delayed_error, req); return req; } req->req = http_client_request_url(req->set->client, "GET", url, oauth2_token_validate_response, req); if (url->user != NULL) http_client_request_set_auth_simple(req->req, url->user, url->password); else http_client_request_add_header(req->req, "Authorization", t_strdup_printf("Bearer %s", input->token)); oauth2_request_set_headers(req, input); http_client_request_set_timeout_msecs(req->req, req->set->timeout_msecs); http_client_request_submit(req->req); return req; } dovecot-2.2.33.2/src/lib-oauth2/oauth2.c0000644000175000017500000000555613165463624014455 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "http-client.h" #include "json-parser.h" #include "oauth2.h" #include "oauth2-private.h" #include "safe-memset.h" void oauth2_parse_json(struct oauth2_request *req) { bool success; enum json_type type; const char *token, *error; int ret; req->field_name = NULL; while((ret = json_parse_next(req->parser, &type, &token)) > 0) { if (req->field_name == NULL) { if (type != JSON_TYPE_OBJECT_KEY) break; /* cannot use t_strdup because we might have to read more */ req->field_name = p_strdup(req->pool, token); } else if (type < JSON_TYPE_STRING) { /* this should be last allocation */ p_free(req->pool, req->field_name); json_parse_skip_next(req->parser); } else { if (!array_is_created(&req->fields)) p_array_init(&req->fields, req->pool, 4); struct oauth2_field *field = array_append_space(&req->fields); field->name = req->field_name; req->field_name = NULL; field->value = p_strdup(req->pool, token); } } /* read more */ if (ret == 0) return; io_remove(&req->io); if (ret > 0) { (void)json_parser_deinit(&req->parser, &error); error = "Invalid response data"; success = FALSE; } else if (i_stream_is_eof(req->is) && req->is->v_offset == 0 && req->is->stream_errno == 0) { /* discard error, empty response is OK. */ (void)json_parser_deinit(&req->parser, &error); error = NULL; success = TRUE; } else { ret = json_parser_deinit(&req->parser, &error); success = (ret == 0); } i_stream_unref(&req->is); req->json_parsed_cb(req, success, error); } void oauth2_request_abort(struct oauth2_request **_req) { struct oauth2_request *req = *_req; *_req = NULL; if (req->req != NULL) http_client_request_abort(&req->req); oauth2_request_free_internal(req); } void oauth2_request_free_internal(struct oauth2_request *req) { if (req->to_delayed_error != NULL) timeout_remove(&req->to_delayed_error); pool_unref(&req->pool); } bool oauth2_valid_token(const char *token) { if (token == NULL || *token == '\0' || strpbrk(token, "\r\n") != NULL) return FALSE; return TRUE; } void oauth2_request_set_headers(struct oauth2_request *req, const struct oauth2_request_input *input) { if (!req->set->send_auth_headers) return; if (input->service != NULL) { http_client_request_add_header(req->req, "X-Dovecot-Auth-Service", input->service); } if (input->local_ip.family != 0) { const char *addr; if (net_ipport2str(&input->local_ip, input->local_port, &addr) == 0) http_client_request_add_header(req->req, "X-Dovecot-Auth-Local", addr); } if (input->remote_ip.family != 0) { const char *addr; if (net_ipport2str(&input->remote_ip, input->remote_port, &addr) == 0) http_client_request_add_header(req->req, "X-Dovecot-Auth-Remote", addr); } } dovecot-2.2.33.2/src/lib-oauth2/Makefile.am0000644000175000017500000000062113123174404015114 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-settings noinst_LTLIBRARIES=liboauth2.la pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = \ oauth2.h noinst_HEADERS = \ oauth2-private.h liboauth2_la_SOURCES = \ oauth2.c \ oauth2-token-validate.c \ oauth2-introspect.c \ oauth2-refresh.c check_programs = \ oauth2-server \ test-oauth2 dovecot-2.2.33.2/src/login-common/0002755000175000017500000000000013172375612013603 500000000000000dovecot-2.2.33.2/src/login-common/ssl-proxy.c0000644000175000017500000000436713123174404015647 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ssl-proxy.h" bool ssl_initialized = FALSE; #ifndef HAVE_SSL /* no SSL support */ int ssl_proxy_alloc(int fd ATTR_UNUSED, const struct ip_addr *ip ATTR_UNUSED, pool_t set_pool ATTR_UNUSED, const struct login_settings *login_set ATTR_UNUSED, const struct master_service_ssl_settings *ssl_set ATTR_UNUSED, struct ssl_proxy **proxy_r ATTR_UNUSED) { i_error("Dovecot wasn't built with SSL support"); return -1; } int ssl_proxy_client_alloc(int fd ATTR_UNUSED, struct ip_addr *ip ATTR_UNUSED, pool_t set_pool ATTR_UNUSED, const struct login_settings *login_set ATTR_UNUSED, const struct master_service_ssl_settings *ssl_set ATTR_UNUSED, ssl_handshake_callback_t *callback ATTR_UNUSED, void *context ATTR_UNUSED, struct ssl_proxy **proxy_r ATTR_UNUSED) { i_error("Dovecot wasn't built with SSL support"); return -1; } void ssl_proxy_start(struct ssl_proxy *proxy ATTR_UNUSED) { } void ssl_proxy_set_client(struct ssl_proxy *proxy ATTR_UNUSED, struct client *client ATTR_UNUSED) { } bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy ATTR_UNUSED) { return FALSE; } bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy ATTR_UNUSED) { return FALSE; } int ssl_proxy_cert_match_name(struct ssl_proxy *proxy ATTR_UNUSED, const char *verify_name ATTR_UNUSED) { return -1; } const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy ATTR_UNUSED) { return NULL; } bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy ATTR_UNUSED) { return FALSE; } const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy ATTR_UNUSED) { return NULL; } const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy ATTR_UNUSED) { return ""; } const char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED) { return NULL; } const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy ATTR_UNUSED) { return ""; } void ssl_proxy_destroy(struct ssl_proxy *proxy ATTR_UNUSED) {} void ssl_proxy_free(struct ssl_proxy **proxy ATTR_UNUSED) {} unsigned int ssl_proxy_get_count(void) { return 0; } void ssl_proxy_init(void) {} void ssl_proxy_deinit(void) {} #endif dovecot-2.2.33.2/src/login-common/login-proxy.h0000644000175000017500000000471213123174404016155 00000000000000#ifndef LOGIN_PROXY_H #define LOGIN_PROXY_H #include "net.h" /* Max. number of embedded proxying connections until proxying fails. This is intended to avoid an accidental configuration where two proxies keep connecting to each others, both thinking the other one is supposed to handle the user. This only works if both proxies support the Dovecot TTL extension feature. */ #define LOGIN_PROXY_TTL 5 struct client; struct login_proxy; enum login_proxy_ssl_flags { /* Use SSL/TLS enabled */ PROXY_SSL_FLAG_YES = 0x01, /* Don't do SSL handshake immediately after connected */ PROXY_SSL_FLAG_STARTTLS = 0x02, /* Don't require that the received certificate is valid */ PROXY_SSL_FLAG_ANY_CERT = 0x04 }; struct login_proxy_settings { const char *host; struct ip_addr ip, source_ip; in_port_t port; unsigned int connect_timeout_msecs; /* send a notification about proxy connection to proxy-notify pipe every n seconds */ unsigned int notify_refresh_secs; enum login_proxy_ssl_flags ssl_flags; }; /* Called when new input comes from proxy. */ typedef void proxy_callback_t(struct client *client); /* Create a proxy to given host. Returns NULL if failed. Given callback is called when new input is available from proxy. */ int login_proxy_new(struct client *client, const struct login_proxy_settings *set, proxy_callback_t *callback); /* Free the proxy. This should be called if authentication fails. */ void login_proxy_free(struct login_proxy **proxy); /* Return TRUE if host/port/destuser combination points to same as current connection. */ bool login_proxy_is_ourself(const struct client *client, const char *host, in_port_t port, const char *destuser); /* Detach proxy from client. This is done after the authentication is successful and all that is left is the dummy proxying. */ void login_proxy_detach(struct login_proxy *proxy); /* STARTTLS command was issued. */ int login_proxy_starttls(struct login_proxy *proxy); struct istream *login_proxy_get_istream(struct login_proxy *proxy); struct ostream *login_proxy_get_ostream(struct login_proxy *proxy); const char *login_proxy_get_host(const struct login_proxy *proxy) ATTR_PURE; in_port_t login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE; enum login_proxy_ssl_flags login_proxy_get_ssl_flags(const struct login_proxy *proxy) ATTR_PURE; void login_proxy_kill_idle(void); void login_proxy_init(const char *proxy_notify_pipe_path); void login_proxy_deinit(void); #endif dovecot-2.2.33.2/src/login-common/login-settings.h0000644000175000017500000000237413123174404016636 00000000000000#ifndef LOGIN_SETTINGS_H #define LOGIN_SETTINGS_H struct master_service_ssl_settings; struct login_settings { const char *login_trusted_networks; const char *login_source_ips; const char *login_greeting; const char *login_log_format_elements, *login_log_format; const char *login_access_sockets; const char *login_plugin_dir; const char *login_plugins; unsigned int login_proxy_max_disconnect_delay; const char *director_username_hash; const char *ssl_client_cert; const char *ssl_client_key; bool ssl_require_crl; bool auth_ssl_require_client_cert; bool auth_ssl_username_from_cert; bool disable_plaintext_auth; bool auth_verbose; bool auth_debug; bool auth_debug_passwords; bool verbose_proctitle; unsigned int mail_max_userip_connections; /* generated: */ char *const *log_format_elements_split; }; extern const struct setting_parser_info **login_set_roots; extern const struct setting_parser_info login_setting_parser_info; struct login_settings * login_settings_read(pool_t pool, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, const char *local_name, const struct master_service_ssl_settings **ssl_set_r, void ***other_settings_r) ATTR_NULL(2, 3, 4); void login_settings_deinit(void); #endif dovecot-2.2.33.2/src/login-common/Makefile.in0000644000175000017500000006404513172375574015606 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/login-common ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = am_libdovecot_login_la_OBJECTS = libdovecot_login_la_OBJECTS = $(am_libdovecot_login_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_login_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdovecot_login_la_LDFLAGS) \ $(LDFLAGS) -o $@ liblogin_la_DEPENDENCIES = $(openssl_obj) $(am__DEPENDENCIES_1) am_liblogin_la_OBJECTS = access-lookup.lo client-common.lo \ client-common-auth.lo login-proxy.lo login-proxy-state.lo \ login-settings.lo main.lo sasl-server.lo ssl-proxy.lo \ ssl-proxy-gnutls.lo ssl-proxy-openssl.lo liblogin_la_OBJECTS = $(am_liblogin_la_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_login_la_SOURCES) $(liblogin_la_SOURCES) DIST_SOURCES = $(libdovecot_login_la_SOURCES) $(liblogin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = liblogin.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DMODULEDIR=\""$(moduledir)"\" liblogin_la_SOURCES = \ access-lookup.c \ client-common.c \ client-common-auth.c \ login-proxy.c \ login-proxy-state.c \ login-settings.c \ main.c \ sasl-server.c \ ssl-proxy.c \ ssl-proxy-gnutls.c \ ssl-proxy-openssl.c @BUILD_OPENSSL_TRUE@openssl_obj = ../lib-ssl-iostream/iostream-openssl-common.lo liblogin_la_LIBADD = \ $(openssl_obj) \ $(SSL_LIBS) headers = \ access-lookup.h \ client-common.h \ login-common.h \ login-proxy.h \ login-proxy-state.h \ login-settings.h \ sasl-server.h \ ssl-proxy.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) pkglib_LTLIBRARIES = libdovecot-login.la libdovecot_login_la_SOURCES = libdovecot_login_la_LIBADD = liblogin.la ../lib-ssl-iostream/libssl_iostream.la ../lib-dovecot/libdovecot.la $(SSL_LIBS) libdovecot_login_la_DEPENDENCIES = liblogin.la libdovecot_login_la_LDFLAGS = -export-dynamic all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/login-common/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/login-common/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-login.la: $(libdovecot_login_la_OBJECTS) $(libdovecot_login_la_DEPENDENCIES) $(EXTRA_libdovecot_login_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_login_la_LINK) -rpath $(pkglibdir) $(libdovecot_login_la_OBJECTS) $(libdovecot_login_la_LIBADD) $(LIBS) liblogin.la: $(liblogin_la_OBJECTS) $(liblogin_la_DEPENDENCIES) $(EXTRA_liblogin_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(liblogin_la_OBJECTS) $(liblogin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/access-lookup.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-common-auth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-proxy-state.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-proxy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sasl-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl-proxy-gnutls.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl-proxy-openssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl-proxy.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/login-common/client-common.h0000644000175000017500000002300413165463624016440 00000000000000#ifndef CLIENT_COMMON_H #define CLIENT_COMMON_H struct module; #include "net.h" #include "login-proxy.h" #include "sasl-server.h" #include "master-login.h" /* for LOGIN_MAX_SESSION_ID_LEN */ #define LOGIN_MAX_SESSION_ID_LEN 64 #define LOGIN_MAX_MASTER_PREFIX_LEN 128 #define LOGIN_MAX_CLIENT_ID_LEN 256 /* max. size of input buffer. this means: IMAP: Max. length of command's all parameters. SASL-IR is read into a separate larger buffer. POP3: Max. length of a command line (spec says 512 would be enough) */ #define LOGIN_MAX_INBUF_SIZE \ (MASTER_AUTH_MAX_DATA_SIZE - LOGIN_MAX_MASTER_PREFIX_LEN - \ LOGIN_MAX_SESSION_ID_LEN) /* max. size of output buffer. if it gets full, the client is disconnected. SASL authentication gives the largest output. */ #define LOGIN_MAX_OUTBUF_SIZE 4096 /* Max. length of SASL authentication buffer. */ #define LOGIN_MAX_AUTH_BUF_SIZE 8192 /* Disconnect client after this many milliseconds if it hasn't managed to log in yet. */ #define CLIENT_LOGIN_TIMEOUT_MSECS (MASTER_LOGIN_TIMEOUT_SECS*1000) #define AUTH_SERVER_WAITING_MSG \ "Waiting for authentication process to respond.." #define AUTH_MASTER_WAITING_MSG \ "Waiting for authentication master process to respond.." struct master_service_connection; enum client_disconnect_reason { CLIENT_DISCONNECT_TIMEOUT, CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, CLIENT_DISCONNECT_INTERNAL_ERROR }; enum client_auth_result { CLIENT_AUTH_RESULT_SUCCESS, CLIENT_AUTH_RESULT_REFERRAL_SUCCESS, CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN, CLIENT_AUTH_RESULT_ABORTED, CLIENT_AUTH_RESULT_AUTHFAILED, CLIENT_AUTH_RESULT_AUTHFAILED_REASON, CLIENT_AUTH_RESULT_AUTHZFAILED, CLIENT_AUTH_RESULT_TEMPFAIL, CLIENT_AUTH_RESULT_SSL_REQUIRED }; struct client_auth_reply { const char *master_user, *reason; /* for proxying */ const char *host, *hostip, *source_ip; const char *destuser, *password, *proxy_mech; in_port_t port; unsigned int proxy_timeout_msecs; unsigned int proxy_refresh_secs; enum login_proxy_ssl_flags ssl_flags; /* all the key=value fields returned by passdb */ const char *const *all_fields; unsigned int proxy:1; unsigned int proxy_nopipelining:1; unsigned int proxy_not_trusted:1; unsigned int temp:1; unsigned int nologin:1; unsigned int authz_failure:1; }; struct client_vfuncs { struct client *(*alloc)(pool_t pool); void (*create)(struct client *client, void **other_sets); void (*destroy)(struct client *client); void (*notify_auth_ready)(struct client *client); void (*notify_disconnect)(struct client *client, enum client_disconnect_reason reason, const char *text); void (*notify_status)(struct client *client, bool bad, const char *text); void (*notify_starttls)(struct client *client, bool success, const char *text); void (*starttls)(struct client *client); void (*input)(struct client *client); void (*auth_send_challenge)(struct client *client, const char *data); void (*auth_parse_response)(struct client *client); void (*auth_result)(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text); void (*proxy_reset)(struct client *client); int (*proxy_parse_line)(struct client *client, const char *line); void (*proxy_error)(struct client *client, const char *text); const char *(*proxy_get_state)(struct client *client); void (*send_raw_data)(struct client *client, const void *data, size_t size); bool (*input_next_cmd)(struct client *client); void (*free)(struct client *client); }; struct client { struct client *prev, *next; pool_t pool; /* this pool gets free'd once proxying starts */ pool_t preproxy_pool; struct client_vfuncs v; struct client_vfuncs *vlast; time_t created; int refcount; struct ip_addr local_ip; struct ip_addr ip; struct ip_addr real_remote_ip, real_local_ip; in_port_t local_port, remote_port; in_port_t real_local_port, real_remote_port; struct ssl_proxy *ssl_proxy; const struct login_settings *set; const struct master_service_ssl_settings *ssl_set; const char *session_id, *listener_name, *postlogin_socket_path; const char *local_name; string_t *client_id; string_t *forward_fields; int fd; struct istream *input; struct ostream *output; struct io *io; struct timeout *to_auth_waiting; struct timeout *to_disconnect; unsigned char *master_data_prefix; unsigned int master_data_prefix_len; struct login_proxy *login_proxy; char *proxy_user, *proxy_master_user, *proxy_password; const struct dsasl_client_mech *proxy_mech; struct dsasl_client *proxy_sasl_client; unsigned int proxy_ttl; char *auth_mech_name; struct auth_client_request *auth_request; string_t *auth_response; time_t auth_first_started, auth_finished; const char *sasl_final_resp; const char *const *auth_passdb_args; unsigned int master_auth_id; unsigned int master_tag; sasl_server_callback_t *sasl_callback; unsigned int bad_counter; unsigned int auth_attempts, auth_successes; pid_t mail_pid; /* Module-specific contexts. */ ARRAY(union login_client_module_context *) module_contexts; char *virtual_user, *virtual_user_orig, *virtual_auth_user; /* passdb user_* fields are set here after a successful auth. This is a NULL-terminated array where fields are in the same order as in global_alt_usernames. If some field doesn't exist, it's "". Can also be NULL if there are no user_* fields. */ const char **alt_usernames; /* director_username_hash cached, if non-zero */ unsigned int director_username_hash_cache; unsigned int destroyed:1; unsigned int input_blocked:1; unsigned int login_success:1; unsigned int starttls:1; unsigned int tls:1; unsigned int secured:1; unsigned int trusted:1; unsigned int ssl_servername_settings_read:1; unsigned int banner_sent:1; unsigned int authenticating:1; unsigned int auth_tried_disabled_plaintext:1; unsigned int auth_tried_unsupported_mech:1; unsigned int auth_try_aborted:1; unsigned int auth_initializing:1; unsigned int auth_process_comm_fail:1; unsigned int proxy_auth_failed:1; unsigned int proxy_nopipelining:1; unsigned int proxy_not_trusted:1; unsigned int auth_waiting:1; unsigned int auth_user_disabled:1; unsigned int auth_pass_expired:1; unsigned int notified_auth_ready:1; unsigned int notified_disconnect:1; /* ... */ }; union login_client_module_context { struct client_vfuncs super; struct login_module_register *reg; }; struct login_client_hooks { void (*client_allocated)(struct client *client); }; extern struct client *clients; typedef void login_client_allocated_func_t(struct client *client); void login_client_hooks_add(struct module *module, const struct login_client_hooks *hooks); void login_client_hooks_remove(const struct login_client_hooks *hooks); struct client * client_create(int fd, bool ssl, pool_t pool, const struct master_service_connection *conn, const struct login_settings *set, const struct master_service_ssl_settings *ssl_set, void **other_sets); void client_destroy(struct client *client, const char *reason); void client_destroy_success(struct client *client, const char *reason); void client_destroy_internal_failure(struct client *client); void client_ref(struct client *client); bool client_unref(struct client **client) ATTR_NOWARN_UNUSED_RESULT; void client_cmd_starttls(struct client *client); unsigned int clients_get_count(void) ATTR_PURE; void client_add_forward_field(struct client *client, const char *key, const char *value); void client_set_title(struct client *client); void client_log(struct client *client, const char *msg); void client_log_err(struct client *client, const char *msg); void client_log_warn(struct client *client, const char *msg); const char *client_get_extra_disconnect_reason(struct client *client); void client_auth_respond(struct client *client, const char *response); void client_auth_abort(struct client *client); bool client_is_tls_enabled(struct client *client); void client_auth_fail(struct client *client, const char *text); const char *client_get_session_id(struct client *client); bool client_read(struct client *client); void client_input(struct client *client); void client_notify_auth_ready(struct client *client); void client_notify_status(struct client *client, bool bad, const char *text); void client_notify_disconnect(struct client *client, enum client_disconnect_reason reason, const char *text); void client_send_raw_data(struct client *client, const void *data, size_t size); void client_send_raw(struct client *client, const char *data); void client_common_send_raw_data(struct client *client, const void *data, size_t size); void client_common_default_free(struct client *client); void client_set_auth_waiting(struct client *client); void client_auth_send_challenge(struct client *client, const char *data); void client_auth_parse_response(struct client *client); int client_auth_begin(struct client *client, const char *mech_name, const char *init_resp); bool client_check_plaintext_auth(struct client *client, bool pass_sent); int client_auth_read_line(struct client *client); void client_proxy_finish_destroy_client(struct client *client); void client_proxy_log_failure(struct client *client, const char *line); void client_proxy_failed(struct client *client, bool send_line); const char *client_proxy_get_state(struct client *client); void clients_notify_auth_connected(void); void client_destroy_oldest(void); void clients_destroy_all(void); void clients_destroy_all_reason(const char *reason); void client_common_init(void); void client_common_deinit(void); #endif dovecot-2.2.33.2/src/login-common/login-settings.c0000644000175000017500000001337513165463624016647 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "hostpid.h" #include "var-expand.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "master-service-ssl-settings.h" #include "master-service-settings-cache.h" #include "login-settings.h" #include #include static bool login_settings_check(void *_set, pool_t pool, const char **error_r); #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct login_settings, name), NULL } static const struct setting_define login_setting_defines[] = { DEF(SET_STR, login_trusted_networks), DEF(SET_STR, login_source_ips), DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, login_log_format_elements), DEF(SET_STR, login_log_format), DEF(SET_STR, login_access_sockets), DEF(SET_STR, login_plugin_dir), DEF(SET_STR, login_plugins), DEF(SET_TIME, login_proxy_max_disconnect_delay), DEF(SET_STR, director_username_hash), DEF(SET_STR, ssl_client_cert), DEF(SET_STR, ssl_client_key), DEF(SET_BOOL, ssl_require_crl), DEF(SET_BOOL, auth_ssl_require_client_cert), DEF(SET_BOOL, auth_ssl_username_from_cert), DEF(SET_BOOL, disable_plaintext_auth), DEF(SET_BOOL, auth_verbose), DEF(SET_BOOL, auth_debug), DEF(SET_BOOL, verbose_proctitle), DEF(SET_UINT, mail_max_userip_connections), SETTING_DEFINE_LIST_END }; static const struct login_settings login_default_settings = { .login_trusted_networks = "", .login_source_ips = "", .login_greeting = PACKAGE_NAME" ready.", .login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c session=<%{session}>", .login_log_format = "%$: %s", .login_access_sockets = "", .login_plugin_dir = MODULEDIR"/login", .login_plugins = "", .login_proxy_max_disconnect_delay = 0, .director_username_hash = "%u", .ssl_client_cert = "", .ssl_client_key = "", .ssl_require_crl = TRUE, .auth_ssl_require_client_cert = FALSE, .auth_ssl_username_from_cert = FALSE, .disable_plaintext_auth = TRUE, .auth_verbose = FALSE, .auth_debug = FALSE, .verbose_proctitle = FALSE, .mail_max_userip_connections = 10 }; const struct setting_parser_info login_setting_parser_info = { .module_name = "login", .defines = login_setting_defines, .defaults = &login_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct login_settings), .parent_offset = (size_t)-1, .check_func = login_settings_check }; static const struct setting_parser_info *default_login_set_roots[] = { &login_setting_parser_info, NULL }; const struct setting_parser_info **login_set_roots = default_login_set_roots; static struct master_service_settings_cache *set_cache; /* */ static bool login_settings_check(void *_set, pool_t pool, const char **error_r ATTR_UNUSED) { struct login_settings *set = _set; set->log_format_elements_split = p_strsplit(pool, set->login_log_format_elements, " "); if (set->auth_debug_passwords) set->auth_debug = TRUE; if (set->auth_debug) set->auth_verbose = TRUE; return TRUE; } /* */ static const struct var_expand_table * login_set_var_expand_table(const struct master_service_settings_input *input) { static struct var_expand_table static_tab[] = { { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 's', NULL, "service" }, { '\0', NULL, "local_name" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = net_ip2addr(&input->local_ip); tab[1].value = net_ip2addr(&input->remote_ip); tab[2].value = my_pid; tab[3].value = input->service; tab[4].value = input->local_name; return tab; } static void * login_setting_dup(pool_t pool, const struct setting_parser_info *info, const void *src_set) { const char *error; void *dest; dest = settings_dup(info, src_set, pool); if (!settings_check(info, pool, dest, &error)) { const char *name = info->module_name; i_fatal("settings_check(%s) failed: %s", name != NULL ? name : "unknown", error); } return dest; } struct login_settings * login_settings_read(pool_t pool, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, const char *local_name, const struct master_service_ssl_settings **ssl_set_r, void ***other_settings_r) { struct master_service_settings_input input; const char *error; const struct setting_parser_context *parser; void *const *cache_sets; void **sets; unsigned int i, count; i_zero(&input); input.roots = login_set_roots; input.module = login_binary->process_name; input.service = login_binary->protocol; input.local_name = local_name; if (local_ip != NULL) input.local_ip = *local_ip; if (remote_ip != NULL) input.remote_ip = *remote_ip; if (set_cache == NULL) { set_cache = master_service_settings_cache_init(master_service, input.module, input.service); } if (master_service_settings_cache_read(set_cache, &input, NULL, &parser, &error) < 0) i_fatal("Error reading configuration: %s", error); cache_sets = master_service_settings_parser_get_others(master_service, parser); for (count = 0; input.roots[count] != NULL; count++) ; i_assert(cache_sets[count] == NULL); sets = p_new(pool, void *, count + 1); for (i = 0; i < count; i++) sets[i] = login_setting_dup(pool, input.roots[i], cache_sets[i]); settings_var_expand(&login_setting_parser_info, sets[0], pool, login_set_var_expand_table(&input)); *ssl_set_r = login_setting_dup(pool, &master_service_ssl_setting_parser_info, settings_parser_get_list(parser)[1]); *other_settings_r = sets + 1; return sets[0]; } void login_settings_deinit(void) { if (set_cache != NULL) master_service_settings_cache_deinit(&set_cache); } dovecot-2.2.33.2/src/login-common/login-proxy-state.c0000644000175000017500000001004413165463624017274 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "ioloop.h" #include "hash.h" #include "strescape.h" #include "fd-set-nonblock.h" #include "login-proxy-state.h" #include #include #define NOTIFY_RETRY_REOPEN_MSECS (60*1000) struct login_proxy_state { HASH_TABLE(struct login_proxy_record *, struct login_proxy_record *) hash; pool_t pool; const char *notify_path; int notify_fd; struct timeout *to_reopen; }; static int login_proxy_state_notify_open(struct login_proxy_state *state); static unsigned int login_proxy_record_hash(const struct login_proxy_record *rec) { return net_ip_hash(&rec->ip) ^ rec->port; } static int login_proxy_record_cmp(struct login_proxy_record *rec1, struct login_proxy_record *rec2) { if (!net_ip_compare(&rec1->ip, &rec2->ip)) return 1; return (int)rec1->port - (int)rec2->port; } struct login_proxy_state *login_proxy_state_init(const char *notify_path) { struct login_proxy_state *state; state = i_new(struct login_proxy_state, 1); state->pool = pool_alloconly_create("login proxy state", 1024); hash_table_create(&state->hash, state->pool, 0, login_proxy_record_hash, login_proxy_record_cmp); state->notify_path = p_strdup(state->pool, notify_path); state->notify_fd = -1; return state; } static void login_proxy_state_close(struct login_proxy_state *state) { if (state->notify_fd != -1) { if (close(state->notify_fd) < 0) i_error("close(%s) failed: %m", state->notify_path); state->notify_fd = -1; } } void login_proxy_state_deinit(struct login_proxy_state **_state) { struct login_proxy_state *state = *_state; struct hash_iterate_context *iter; struct login_proxy_record *rec; *_state = NULL; /* sanity check: */ iter = hash_table_iterate_init(state->hash); while (hash_table_iterate(iter, state->hash, &rec, &rec)) i_assert(rec->num_waiting_connections == 0); hash_table_iterate_deinit(&iter); if (state->to_reopen != NULL) timeout_remove(&state->to_reopen); login_proxy_state_close(state); hash_table_destroy(&state->hash); pool_unref(&state->pool); i_free(state); } struct login_proxy_record * login_proxy_state_get(struct login_proxy_state *state, const struct ip_addr *ip, in_port_t port) { struct login_proxy_record *rec, key; i_zero(&key); key.ip = *ip; key.port = port; rec = hash_table_lookup(state->hash, &key); if (rec == NULL) { rec = p_new(state->pool, struct login_proxy_record, 1); rec->ip = *ip; rec->port = port; hash_table_insert(state->hash, rec, rec); } return rec; } static void login_proxy_state_reopen(struct login_proxy_state *state) { timeout_remove(&state->to_reopen); (void)login_proxy_state_notify_open(state); } static int login_proxy_state_notify_open(struct login_proxy_state *state) { if (state->to_reopen != NULL) { /* reopen later */ return -1; } state->notify_fd = open(state->notify_path, O_WRONLY); if (state->notify_fd == -1) { i_error("open(%s) failed: %m", state->notify_path); state->to_reopen = timeout_add(NOTIFY_RETRY_REOPEN_MSECS, login_proxy_state_reopen, state); return -1; } fd_set_nonblock(state->notify_fd, TRUE); return 0; } static bool login_proxy_state_try_notify(struct login_proxy_state *state, const char *user) { size_t len; ssize_t ret; if (state->notify_fd == -1) { if (login_proxy_state_notify_open(state) < 0) return TRUE; i_assert(state->notify_fd != -1); } T_BEGIN { const char *cmd; cmd = t_strconcat(str_tabescape(user), "\n", NULL); len = strlen(cmd); ret = write(state->notify_fd, cmd, len); } T_END; if (ret != (ssize_t)len) { if (ret < 0) i_error("write(%s) failed: %m", state->notify_path); else { i_error("write(%s) wrote partial update", state->notify_path); } login_proxy_state_close(state); /* retry sending */ return FALSE; } return TRUE; } void login_proxy_state_notify(struct login_proxy_state *state, const char *user) { if (!login_proxy_state_try_notify(state, user)) (void)login_proxy_state_try_notify(state, user); } dovecot-2.2.33.2/src/login-common/client-common-auth.c0000644000175000017500000005340013165463624017375 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "hostpid.h" #include "login-common.h" #include "array.h" #include "iostream.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "safe-memset.h" #include "time-util.h" #include "login-proxy.h" #include "auth-client.h" #include "dsasl-client.h" #include "master-service-ssl-settings.h" #include "client-common.h" #define PROXY_FAILURE_MSG "Account is temporarily unavailable." #define PROXY_DEFAULT_TIMEOUT_MSECS (1000*30) /* If we've been waiting auth server to respond for over this many milliseconds, send a "waiting" message. */ #define AUTH_WAITING_TIMEOUT_MSECS (30*1000) #define AUTH_WAITING_WARNING_TIMEOUT_MSECS (10*1000) static void client_auth_failed(struct client *client) { i_free_and_null(client->master_data_prefix); if (client->auth_response != NULL) str_truncate(client->auth_response, 0); if (client->auth_initializing || client->destroyed) return; if (client->io != NULL) io_remove(&client->io); client->io = io_add(client->fd, IO_READ, client_input, client); client_input(client); } static void client_auth_waiting_timeout(struct client *client) { if (!client->notified_auth_ready) { client_log_warn(client, "Auth process not responding, " "delayed sending initial response (greeting)"); } client_notify_status(client, FALSE, client->master_tag == 0 ? AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); timeout_remove(&client->to_auth_waiting); } void client_set_auth_waiting(struct client *client) { i_assert(client->to_auth_waiting == NULL); client->to_auth_waiting = timeout_add(!client->notified_auth_ready ? AUTH_WAITING_WARNING_TIMEOUT_MSECS : AUTH_WAITING_TIMEOUT_MSECS, client_auth_waiting_timeout, client); } static void alt_username_set(ARRAY_TYPE(const_string) *alt_usernames, pool_t pool, const char *key, const char *value) { char *const *fields; unsigned int i, count; fields = array_get(&global_alt_usernames, &count); for (i = 0; i < count; i++) { if (strcmp(fields[i], key) == 0) break; } if (i == count) { char *new_key = i_strdup(key); array_append(&global_alt_usernames, &new_key, 1); } value = p_strdup(pool, value); if (i < array_count(alt_usernames)) { array_idx_set(alt_usernames, i, &value); return; } /* array is NULL-terminated, so if there are unused fields in the middle set them as "" */ while (array_count(alt_usernames) < i) { const char *empty_str = ""; array_append(alt_usernames, &empty_str, 1); } array_append(alt_usernames, &value, 1); } static void client_auth_parse_args(struct client *client, bool success, const char *const *args, struct client_auth_reply *reply_r) { const char *key, *value, *p; ARRAY_TYPE(const_string) alt_usernames; t_array_init(&alt_usernames, 4); i_zero(reply_r); for (; *args != NULL; args++) { p = strchr(*args, '='); if (p == NULL) { key = *args; value = ""; } else { key = t_strdup_until(*args, p); value = p + 1; } if (strcmp(key, "nologin") == 0) reply_r->nologin = TRUE; else if (strcmp(key, "proxy") == 0) reply_r->proxy = TRUE; else if (strcmp(key, "temp") == 0) reply_r->temp = TRUE; else if (strcmp(key, "authz") == 0) reply_r->authz_failure = TRUE; else if (strcmp(key, "user_disabled") == 0) client->auth_user_disabled = TRUE; else if (strcmp(key, "pass_expired") == 0) client->auth_pass_expired = TRUE; else if (strcmp(key, "reason") == 0) reply_r->reason = value; else if (strcmp(key, "host") == 0) reply_r->host = value; else if (strcmp(key, "hostip") == 0) reply_r->hostip = value; else if (strcmp(key, "source_ip") == 0) reply_r->source_ip = value; else if (strcmp(key, "port") == 0) { if (net_str2port(value, &reply_r->port) < 0) { i_error("Auth service returned invalid " "port number: %s", value); } } else if (strcmp(key, "destuser") == 0) reply_r->destuser = value; else if (strcmp(key, "pass") == 0) reply_r->password = value; else if (strcmp(key, "proxy_timeout") == 0) { if (str_to_uint(value, &reply_r->proxy_timeout_msecs) < 0) { i_error("BUG: Auth service returned invalid " "proxy_timeout value: %s", value); } reply_r->proxy_timeout_msecs *= 1000; } else if (strcmp(key, "proxy_refresh") == 0) { if (str_to_uint(value, &reply_r->proxy_refresh_secs) < 0) { i_error("BUG: Auth service returned invalid " "proxy_refresh value: %s", value); } } else if (strcmp(key, "proxy_mech") == 0) reply_r->proxy_mech = value; else if (strcmp(key, "proxy_nopipelining") == 0) reply_r->proxy_nopipelining = TRUE; else if (strcmp(key, "proxy_not_trusted") == 0) reply_r->proxy_not_trusted = TRUE; else if (strcmp(key, "master") == 0) reply_r->master_user = value; else if (strcmp(key, "ssl") == 0) { reply_r->ssl_flags |= PROXY_SSL_FLAG_YES; if (strcmp(value, "any-cert") == 0) reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT; if (reply_r->port == 0) reply_r->port = login_binary->default_ssl_port; } else if (strcmp(key, "starttls") == 0) { reply_r->ssl_flags |= PROXY_SSL_FLAG_YES | PROXY_SSL_FLAG_STARTTLS; if (strcmp(value, "any-cert") == 0) reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT; } else if (strcmp(key, "user") == 0 || strcmp(key, "postlogin_socket") == 0) { /* already handled in sasl-server.c */ } else if (strncmp(key, "user_", 5) == 0) { if (success) { alt_username_set(&alt_usernames, client->pool, key, value); } } else if (strncmp(key, "forward_", 8) == 0) { /* these are passed to upstream */ } else if (client->set->auth_debug) i_debug("Ignoring unknown passdb extra field: %s", key); } if (array_count(&alt_usernames) > 0) { const char **alt; alt = p_new(client->pool, const char *, array_count(&alt_usernames) + 1); memcpy(alt, array_idx(&alt_usernames, 0), sizeof(*alt) * array_count(&alt_usernames)); client->alt_usernames = alt; } if (reply_r->port == 0) reply_r->port = login_binary->default_port; if (reply_r->destuser == NULL) reply_r->destuser = client->virtual_user; } static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } void client_proxy_finish_destroy_client(struct client *client) { string_t *str = t_str_new(128); if (client->input->closed) { /* input stream got closed in client_send_raw_data(). In most places we don't have to check for this explicitly, but login_proxy_detach() attempts to get and use the istream's fd, which is now -1. */ client_destroy(client, "Disconnected"); return; } str_printfa(str, "proxy(%s): started proxying to %s:%u", client->virtual_user, login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy)); if (strcmp(client->virtual_user, client->proxy_user) != 0) { /* remote username is different, log it */ str_append_c(str, '/'); str_append(str, client->proxy_user); } if (client->proxy_master_user != NULL) str_printfa(str, " (master %s)", client->proxy_master_user); login_proxy_detach(client->login_proxy); client_destroy_success(client, str_c(str)); } static void client_proxy_error(struct client *client, const char *text) { client->v.proxy_error(client, text); } const char *client_proxy_get_state(struct client *client) { return client->v.proxy_get_state(client); } void client_proxy_log_failure(struct client *client, const char *line) { string_t *str = t_str_new(128); str_printfa(str, "proxy(%s): Login failed to %s:%u", client->virtual_user, login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy)); if (strcmp(client->virtual_user, client->proxy_user) != 0) { /* remote username is different, log it */ str_append_c(str, '/'); str_append(str, client->proxy_user); } if (client->proxy_master_user != NULL) str_printfa(str, " (master %s)", client->proxy_master_user); str_append(str, ": "); str_append(str, line); client_log(client, str_c(str)); } void client_proxy_failed(struct client *client, bool send_line) { if (send_line) { client_proxy_error(client, PROXY_FAILURE_MSG); } if (client->proxy_sasl_client != NULL) dsasl_client_free(&client->proxy_sasl_client); login_proxy_free(&client->login_proxy); proxy_free_password(client); i_free_and_null(client->proxy_user); i_free_and_null(client->proxy_master_user); /* call this last - it may destroy the client */ client_auth_failed(client); } static void proxy_input(struct client *client) { struct istream *input; struct ostream *output; const char *line; unsigned int duration; if (client->login_proxy == NULL) { /* we're just freeing the proxy */ return; } input = login_proxy_get_istream(client->login_proxy); if (input == NULL) { if (client->destroyed) { /* we came here from client_destroy() */ return; } /* failed for some reason, probably server disconnected */ client_proxy_failed(client, TRUE); return; } i_assert(!client->destroyed); switch (i_stream_read(input)) { case -2: client_log_err(client, "proxy: Remote input buffer full"); client_proxy_failed(client, TRUE); return; case -1: line = i_stream_next_line(input); duration = ioloop_time - client->created; client_log_err(client, t_strdup_printf( "proxy: Remote %s:%u disconnected: %s " "(state=%s, duration=%us)%s", login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy), io_stream_get_disconnect_reason(input, NULL), client_proxy_get_state(client), duration, line == NULL ? "" : t_strdup_printf( " - BUG: line not read: %s", line))); client_proxy_failed(client, TRUE); return; } output = client->output; o_stream_ref(output); o_stream_cork(output); while ((line = i_stream_next_line(input)) != NULL) { if (client->v.proxy_parse_line(client, line) != 0) break; } o_stream_uncork(output); o_stream_unref(&output); } static int proxy_start(struct client *client, const struct client_auth_reply *reply) { struct login_proxy_settings proxy_set; const struct dsasl_client_mech *sasl_mech = NULL; i_assert(reply->destuser != NULL); i_assert(!client->destroyed); i_assert(client->proxy_sasl_client == NULL); client->proxy_mech = NULL; client->v.proxy_reset(client); if (reply->password == NULL) { client_log_err(client, "proxy: password not given"); client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } if (reply->host == NULL || *reply->host == '\0') { client_log_err(client, "proxy: host not given"); client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } if (reply->proxy_mech != NULL) { sasl_mech = dsasl_client_mech_find(reply->proxy_mech); if (sasl_mech == NULL) { client_log_err(client, t_strdup_printf( "proxy: Unsupported SASL mechanism %s", reply->proxy_mech)); client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } } else if (reply->master_user != NULL) { /* have to use PLAIN authentication with master user logins */ sasl_mech = &dsasl_client_mech_plain; } i_assert(client->refcount > 1); if (client->destroyed) { /* connection_queue_add() decided that we were the oldest connection and killed us. */ return -1; } if (login_proxy_is_ourself(client, reply->host, reply->port, reply->destuser)) { client_log_err(client, "Proxying loops to itself"); client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } i_zero(&proxy_set); proxy_set.host = reply->host; if (reply->hostip != NULL && net_addr2ip(reply->hostip, &proxy_set.ip) < 0) proxy_set.ip.family = 0; if (reply->source_ip != NULL) { if (net_addr2ip(reply->source_ip, &proxy_set.source_ip) < 0) proxy_set.source_ip.family = 0; } else if (login_source_ips_count > 0) { /* select the next source IP with round robin. */ proxy_set.source_ip = login_source_ips[login_source_ips_idx]; login_source_ips_idx = (login_source_ips_idx + 1) % login_source_ips_count; } proxy_set.port = reply->port; proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs; if (proxy_set.connect_timeout_msecs == 0) proxy_set.connect_timeout_msecs = PROXY_DEFAULT_TIMEOUT_MSECS; proxy_set.notify_refresh_secs = reply->proxy_refresh_secs; proxy_set.ssl_flags = reply->ssl_flags; if (login_proxy_new(client, &proxy_set, proxy_input) < 0) { client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } client->proxy_mech = sasl_mech; client->proxy_user = i_strdup(reply->destuser); client->proxy_master_user = i_strdup(reply->master_user); client->proxy_password = i_strdup(reply->password); client->proxy_nopipelining = reply->proxy_nopipelining; client->proxy_not_trusted = reply->proxy_not_trusted; /* disable input until authentication is finished */ if (client->io != NULL) io_remove(&client->io); return 0; } static void ATTR_NULL(3, 4) client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text) { o_stream_cork(client->output); client->v.auth_result(client, result, reply, text); o_stream_uncork(client->output); } static bool client_auth_handle_reply(struct client *client, const struct client_auth_reply *reply, bool success) { if (reply->proxy) { /* we want to proxy the connection to another server. don't do this unless authentication succeeded. with master user proxying we can get FAIL with proxy still set. proxy host=.. [port=..] [destuser=..] pass=.. */ if (!success) return FALSE; if (proxy_start(client, reply) < 0) client_auth_failed(client); else { /* this for plugins being able th hook into auth reply when proxying is used */ client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS, reply, NULL); } return TRUE; } if (reply->host != NULL) { const char *reason; if (reply->reason != NULL) reason = reply->reason; else if (reply->nologin) reason = "Try this server instead."; else reason = "Logged in, but you should use this server instead."; if (reply->nologin) { client_auth_result(client, CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN, reply, reason); } else { client_auth_result(client, CLIENT_AUTH_RESULT_REFERRAL_SUCCESS, reply, reason); return TRUE; } } else if (reply->nologin) { /* Authentication went ok, but for some reason user isn't allowed to log in. Shouldn't probably happen. */ if (reply->reason != NULL) { client_auth_result(client, CLIENT_AUTH_RESULT_AUTHFAILED_REASON, reply, reply->reason); } else if (reply->temp) { const char *timestamp, *msg; timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", my_hostname, timestamp); client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL, reply, msg); } else if (reply->authz_failure) { client_auth_result(client, CLIENT_AUTH_RESULT_AUTHZFAILED, reply, "Authorization failed"); } else { client_auth_result(client, CLIENT_AUTH_RESULT_AUTHFAILED, reply, AUTH_FAILED_MSG); } } else { /* normal login/failure */ return FALSE; } i_assert(reply->nologin); if (!client->destroyed) client_auth_failed(client); return TRUE; } void client_auth_respond(struct client *client, const char *response) { client->auth_waiting = FALSE; client_set_auth_waiting(client); auth_client_request_continue(client->auth_request, response); io_remove(&client->io); } void client_auth_abort(struct client *client) { sasl_server_auth_abort(client); } void client_auth_fail(struct client *client, const char *text) { sasl_server_auth_failed(client, text); } int client_auth_read_line(struct client *client) { const unsigned char *data; size_t i, size, len; if (i_stream_read_data(client->input, &data, &size, 0) == -1) { client_destroy(client, "Disconnected"); return -1; } /* see if we have a full line */ for (i = 0; i < size; i++) { if (data[i] == '\n') break; } if (client->auth_response == NULL) client->auth_response = str_new(default_pool, I_MAX(i+1, 256)); if (str_len(client->auth_response) + i > LOGIN_MAX_AUTH_BUF_SIZE) { client_destroy(client, "Authentication response too large"); return -1; } str_append_n(client->auth_response, data, i); i_stream_skip(client->input, i == size ? size : i+1); /* drop trailing \r */ len = str_len(client->auth_response); if (len > 0 && str_c(client->auth_response)[len-1] == '\r') str_truncate(client->auth_response, len-1); return i < size; } void client_auth_parse_response(struct client *client) { if (client_auth_read_line(client) <= 0) return; if (strcmp(str_c(client->auth_response), "*") == 0) { sasl_server_auth_abort(client); return; } client_auth_respond(client, str_c(client->auth_response)); memset(str_c_modifiable(client->auth_response), 0, str_len(client->auth_response)); } static void client_auth_input(struct client *client) { i_assert(client->v.auth_parse_response != NULL); client->v.auth_parse_response(client); } void client_auth_send_challenge(struct client *client, const char *data) { struct const_iovec iov[3]; iov[0].iov_base = "+ "; iov[0].iov_len = 2; iov[1].iov_base = data; iov[1].iov_len = strlen(data); iov[2].iov_base = "\r\n"; iov[2].iov_len = 2; o_stream_nsendv(client->output, iov, 3); } static void sasl_callback(struct client *client, enum sasl_server_reply sasl_reply, const char *data, const char *const *args) { struct client_auth_reply reply; i_assert(!client->destroyed || sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED || sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED); i_zero(&reply); switch (sasl_reply) { case SASL_SERVER_REPLY_SUCCESS: if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (args != NULL) { client_auth_parse_args(client, TRUE, args, &reply); reply.all_fields = args; if (client_auth_handle_reply(client, &reply, TRUE)) break; } client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS, &reply, NULL); client_destroy_success(client, "Login"); break; case SASL_SERVER_REPLY_AUTH_FAILED: case SASL_SERVER_REPLY_AUTH_ABORTED: if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (args != NULL) { client_auth_parse_args(client, FALSE, args, &reply); reply.nologin = TRUE; reply.all_fields = args; if (client_auth_handle_reply(client, &reply, FALSE)) break; } if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) { client_auth_result(client, CLIENT_AUTH_RESULT_ABORTED, &reply, "Authentication aborted by client."); } else if (data == NULL) { client_auth_result(client, CLIENT_AUTH_RESULT_AUTHFAILED, &reply, AUTH_FAILED_MSG); } else { client_auth_result(client, CLIENT_AUTH_RESULT_AUTHFAILED_REASON, &reply, data); } if (!client->destroyed) client_auth_failed(client); break; case SASL_SERVER_REPLY_MASTER_FAILED: if (data != NULL) { /* authentication itself succeeded, we just hit some internal failure. */ client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL, &reply, data); } /* the fd may still be hanging somewhere in kernel or another process. make sure the client gets disconnected. */ if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN) i_error("shutdown() failed: %m"); if (data == NULL) client_destroy_internal_failure(client); else client_destroy_success(client, data); break; case SASL_SERVER_REPLY_CONTINUE: i_assert(client->v.auth_send_challenge != NULL); client->v.auth_send_challenge(client, data); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (client->auth_response != NULL) str_truncate(client->auth_response, 0); i_assert(client->io == NULL); client->auth_waiting = TRUE; client->io = io_add(client->fd, IO_READ, client_auth_input, client); client_auth_input(client); return; } client_unref(&client); } int client_auth_begin(struct client *client, const char *mech_name, const char *init_resp) { if (!client->secured && strcmp(client->ssl_set->ssl, "required") == 0) { if (client->set->auth_verbose) { client_log(client, "Login failed: " "SSL required for authentication"); } client->auth_attempts++; client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL, "Authentication not allowed until SSL/TLS is enabled."); return 1; } client_ref(client); client->auth_initializing = TRUE; sasl_server_auth_begin(client, login_binary->protocol, mech_name, init_resp, sasl_callback); client->auth_initializing = FALSE; if (!client->authenticating) return 1; /* don't handle input until we get the initial auth reply */ if (client->io != NULL) io_remove(&client->io); client_set_auth_waiting(client); return 0; } bool client_check_plaintext_auth(struct client *client, bool pass_sent) { if (client->secured || (!client->set->disable_plaintext_auth && strcmp(client->ssl_set->ssl, "required") != 0)) return TRUE; if (client->set->auth_verbose) { client_log(client, "Login failed: " "Plaintext authentication disabled"); } if (pass_sent) { client_notify_status(client, TRUE, "Plaintext authentication not allowed " "without SSL/TLS, but your client did it anyway. " "If anyone was listening, the password was exposed."); } client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL, AUTH_PLAINTEXT_DISABLED_MSG); client->auth_tried_disabled_plaintext = TRUE; client->auth_attempts++; return FALSE; } void clients_notify_auth_connected(void) { struct client *client, *next; for (client = clients; client != NULL; client = next) { next = client->next; if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); client_notify_auth_ready(client); if (client->input_blocked) { client->input_blocked = FALSE; client_input(client); } } } dovecot-2.2.33.2/src/login-common/ssl-proxy-openssl.c0000644000175000017500000011313413165463624017334 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "ostream.h" #include "read-full.h" #include "safe-memset.h" #include "hash.h" #include "llist.h" #include "master-interface.h" #include "master-service-ssl-settings.h" #include "client-common.h" #include "ssl-proxy.h" #include #include #include #ifdef HAVE_OPENSSL #include "iostream-openssl.h" #include #include #include #include #include #include #include #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L # define HAVE_ECDH #endif /* Check every 30 minutes if parameters file has been updated */ #define SSL_PARAMFILE_CHECK_INTERVAL (60*30) #define SSL_PARAMETERS_PATH "ssl-params" #ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */ # undef HAVE_SSL_GET_SERVERNAME #endif enum ssl_io_action { SSL_ADD_INPUT, SSL_REMOVE_INPUT, SSL_ADD_OUTPUT, SSL_REMOVE_OUTPUT }; struct ssl_proxy { int refcount; struct ssl_proxy *prev, *next; SSL *ssl; struct client *client; struct ip_addr ip; const struct login_settings *login_set; const struct master_service_ssl_settings *ssl_set; pool_t set_pool; int fd_ssl, fd_plain; struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write; unsigned char plainout_buf[1024]; unsigned int plainout_size; unsigned char sslout_buf[1024]; unsigned int sslout_size; ssl_handshake_callback_t *handshake_callback; void *handshake_context; const char *cert_error; char *last_error; unsigned int handshaked:1; unsigned int destroyed:1; unsigned int cert_received:1; unsigned int cert_broken:1; unsigned int client_proxy:1; unsigned int flushing:1; unsigned int failed:1; }; struct ssl_parameters { const char *path; time_t last_refresh; int fd; DH *dh_512, *dh_default; }; struct ssl_server_cert { const char *cert; const char *key; }; struct ssl_server_context { SSL_CTX *ctx; pool_t pool; struct ssl_server_cert pri, alt; const char *ca; const char *cipher_list; const char *protocols; bool verify_client_cert; bool prefer_server_ciphers; bool compression; bool tickets; }; static int extdata_index; static HASH_TABLE(struct ssl_server_context *, struct ssl_server_context *) ssl_servers; static SSL_CTX *ssl_client_ctx; static unsigned int ssl_proxy_count; static struct ssl_proxy *ssl_proxies; static struct ssl_parameters ssl_params; static int ssl_username_nid; static ENGINE *ssl_engine; static void plain_read(struct ssl_proxy *proxy); static void ssl_read(struct ssl_proxy *proxy); static void ssl_write(struct ssl_proxy *proxy); static void ssl_step(struct ssl_proxy *proxy); static void ssl_proxy_unref(struct ssl_proxy *proxy); static struct ssl_server_context * ssl_server_context_init(const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set); static void ssl_server_context_deinit(struct ssl_server_context **_ctx); static void ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx, const struct master_service_ssl_settings *set); #if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO) static int ssl_proxy_ctx_get_pkey_ec_curve_name(const struct master_service_ssl_settings *set); #endif static void ssl_proxy_destroy_failed(struct ssl_proxy *proxy) { proxy->failed = TRUE; ssl_proxy_destroy(proxy); } static unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx) { unsigned int n, i, g, h = 0; const char *cert[] = { ctx->pri.cert, ctx->alt.cert }; /* checking for different certs is typically good enough, and it should be enough to check only the first few bytes. */ for(n=0;n> 24); h = h ^ g; } } } return h; } static int ssl_server_context_cmp(const struct ssl_server_context *ctx1, const struct ssl_server_context *ctx2) { if (((ctx1->pri.cert != ctx2->pri.cert) && (ctx1->pri.cert == NULL || ctx2->pri.cert == NULL)) || ((ctx1->alt.cert != ctx2->alt.cert) && (ctx1->alt.cert == NULL || ctx2->alt.cert == NULL))) return 1; if (ctx1->pri.cert != NULL && strcmp(ctx1->pri.cert, ctx2->pri.cert) != 0) return 1; if (ctx1->pri.key != NULL && strcmp(ctx1->pri.key, ctx2->pri.key) != 0) return 1; if (ctx1->alt.cert != NULL && strcmp(ctx1->alt.cert, ctx2->alt.cert) != 0) return 1; if (ctx1->alt.key != NULL && strcmp(ctx1->alt.key, ctx2->alt.key) != 0) return 1; if (null_strcmp(ctx1->ca, ctx2->ca) != 0) return 1; if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0) return 1; if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0) return 1; return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1; } static void ssl_params_corrupted(const char *reason) { i_fatal("Corrupted SSL ssl-parameters.dat in state_dir: %s", reason); } static void read_next(struct ssl_parameters *params, void *data, size_t size) { int ret; if ((ret = read_full(params->fd, data, size)) < 0) i_fatal("read(%s) failed: %m", params->path); if (ret == 0) ssl_params_corrupted("Truncated file"); } static bool read_dh_parameters_next(struct ssl_parameters *params) { unsigned char *buf; const unsigned char *cbuf; unsigned int len; int bits; /* read bit size. 0 ends the DH parameters list. */ read_next(params, &bits, sizeof(bits)); if (bits == 0) return FALSE; /* read data size. */ read_next(params, &len, sizeof(len)); if (len > 1024*100) /* should be enough? */ ssl_params_corrupted("File too large"); buf = i_malloc(len); read_next(params, buf, len); cbuf = buf; switch (bits) { case 512: if (params->dh_512 != NULL) ssl_params_corrupted("Duplicate 512bit parameters"); params->dh_512 = d2i_DHparams(NULL, &cbuf, len); break; default: if (params->dh_default != NULL) ssl_params_corrupted("Duplicate default parameters"); params->dh_default = d2i_DHparams(NULL, &cbuf, len); break; } i_free(buf); return TRUE; } static void ssl_free_parameters(struct ssl_parameters *params) { if (params->dh_512 != NULL) { DH_free(params->dh_512); params->dh_512 = NULL; } if (params->dh_default != NULL) { DH_free(params->dh_default); params->dh_default = NULL; } } static void ssl_refresh_parameters(struct ssl_parameters *params) { char c; int ret; if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL) return; params->last_refresh = ioloop_time; params->fd = net_connect_unix(params->path); if (params->fd == -1) { i_error("connect(%s) failed: %m", params->path); return; } net_set_nonblock(params->fd, FALSE); ssl_free_parameters(params); while (read_dh_parameters_next(params)) ; if ((ret = read_full(params->fd, &c, 1)) < 0) i_fatal("read(%s) failed: %m", params->path); else if (ret != 0) { /* more data than expected */ ssl_params_corrupted("More data than expected"); } if (close(params->fd) < 0) i_error("close(%s) failed: %m", params->path); params->fd = -1; } static void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action) { switch (action) { case SSL_ADD_INPUT: if (proxy->io_ssl_read != NULL) break; proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ, ssl_step, proxy); break; case SSL_REMOVE_INPUT: if (proxy->io_ssl_read != NULL) io_remove(&proxy->io_ssl_read); break; case SSL_ADD_OUTPUT: if (proxy->io_ssl_write != NULL) break; proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE, ssl_step, proxy); break; case SSL_REMOVE_OUTPUT: if (proxy->io_ssl_write != NULL) io_remove(&proxy->io_ssl_write); break; } } static void plain_block_input(struct ssl_proxy *proxy, bool block) { if (block) { if (proxy->io_plain_read != NULL) io_remove(&proxy->io_plain_read); } else { if (proxy->io_plain_read == NULL) { proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ, plain_read, proxy); } } } static void plain_read(struct ssl_proxy *proxy) { ssize_t ret; bool corked = FALSE; if (proxy->sslout_size == sizeof(proxy->sslout_buf)) { /* buffer full, block input until it's written */ plain_block_input(proxy, TRUE); return; } proxy->refcount++; while (proxy->sslout_size < sizeof(proxy->sslout_buf) && !proxy->destroyed) { ret = net_receive(proxy->fd_plain, proxy->sslout_buf + proxy->sslout_size, sizeof(proxy->sslout_buf) - proxy->sslout_size); if (ret <= 0) { if (ret < 0) ssl_proxy_destroy(proxy); break; } else { proxy->sslout_size += ret; if (!corked) { if (net_set_cork(proxy->fd_ssl, TRUE) == 0) corked = TRUE; } ssl_write(proxy); } } if (corked) (void)net_set_cork(proxy->fd_ssl, FALSE); ssl_proxy_unref(proxy); } static void plain_write(struct ssl_proxy *proxy) { ssize_t ret; proxy->refcount++; ret = net_transmit(proxy->fd_plain, proxy->plainout_buf, proxy->plainout_size); if (ret < 0) ssl_proxy_destroy(proxy); else { proxy->plainout_size -= ret; memmove(proxy->plainout_buf, proxy->plainout_buf + ret, proxy->plainout_size); if (proxy->plainout_size > 0) { if (proxy->io_plain_write == NULL) { proxy->io_plain_write = io_add(proxy->fd_plain, IO_WRITE, plain_write, proxy); } } else { if (proxy->io_plain_write != NULL) io_remove(&proxy->io_plain_write); } ssl_set_io(proxy, SSL_ADD_INPUT); if (SSL_pending(proxy->ssl) > 0) ssl_read(proxy); } ssl_proxy_unref(proxy); } static void ssl_handle_error(struct ssl_proxy *proxy, int ret, const char *func_name) { const char *errstr = NULL; int err; proxy->refcount++; i_free_and_null(proxy->last_error); err = SSL_get_error(proxy->ssl, ret); switch (err) { case SSL_ERROR_WANT_READ: ssl_set_io(proxy, SSL_ADD_INPUT); break; case SSL_ERROR_WANT_WRITE: ssl_set_io(proxy, SSL_ADD_OUTPUT); break; case SSL_ERROR_SYSCALL: /* eat up the error queue */ if (ERR_peek_error() != 0) errstr = openssl_iostream_error(); else if (ret != 0) errstr = strerror(errno); else { /* EOF. */ errstr = "Disconnected"; break; } errstr = t_strdup_printf("%s syscall failed: %s", func_name, errstr); break; case SSL_ERROR_ZERO_RETURN: /* clean connection closing */ ssl_proxy_destroy(proxy); break; case SSL_ERROR_SSL: if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) { i_error("OpenSSL malloc() failed. " "You may need to increase service %s { vsz_limit }", login_binary->process_name); } errstr = t_strdup_printf("%s failed: %s", func_name, openssl_iostream_error()); break; default: errstr = t_strdup_printf("%s failed: unknown failure %d (%s)", func_name, err, openssl_iostream_error()); break; } if (errstr != NULL) { if (proxy->ssl_set->verbose_ssl) i_debug("SSL error: %s", errstr); proxy->last_error = i_strdup(errstr); ssl_proxy_destroy_failed(proxy); } ssl_proxy_unref(proxy); } static void ssl_handshake(struct ssl_proxy *proxy) { int ret; if (proxy->client_proxy) { ret = SSL_connect(proxy->ssl); if (ret != 1) { ssl_handle_error(proxy, ret, "SSL_connect()"); return; } } else { ret = SSL_accept(proxy->ssl); if (ret != 1) { ssl_handle_error(proxy, ret, "SSL_accept()"); return; } } i_free_and_null(proxy->last_error); proxy->handshaked = TRUE; ssl_set_io(proxy, SSL_ADD_INPUT); plain_block_input(proxy, FALSE); if (proxy->handshake_callback != NULL) { if (proxy->handshake_callback(proxy->handshake_context) < 0) ssl_proxy_destroy_failed(proxy); } } static void ssl_read(struct ssl_proxy *proxy) { int ret; while (proxy->plainout_size < sizeof(proxy->plainout_buf) && !proxy->destroyed) { ret = SSL_read(proxy->ssl, proxy->plainout_buf + proxy->plainout_size, sizeof(proxy->plainout_buf) - proxy->plainout_size); if (ret <= 0) { ssl_handle_error(proxy, ret, "SSL_read()"); break; } else { i_free_and_null(proxy->last_error); proxy->plainout_size += ret; plain_write(proxy); } } } static void ssl_write(struct ssl_proxy *proxy) { int ret; ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size); if (ret <= 0) ssl_handle_error(proxy, ret, "SSL_write()"); else { i_free_and_null(proxy->last_error); proxy->sslout_size -= ret; memmove(proxy->sslout_buf, proxy->sslout_buf + ret, proxy->sslout_size); ssl_set_io(proxy, proxy->sslout_size > 0 ? SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT); plain_block_input(proxy, FALSE); } } static void ssl_step(struct ssl_proxy *proxy) { proxy->refcount++; if (!proxy->handshaked) { ssl_set_io(proxy, SSL_REMOVE_OUTPUT); ssl_handshake(proxy); } if (proxy->handshaked) { if (proxy->plainout_size == sizeof(proxy->plainout_buf)) ssl_set_io(proxy, SSL_REMOVE_INPUT); else ssl_read(proxy); if (proxy->sslout_size == 0) ssl_set_io(proxy, SSL_REMOVE_OUTPUT); else { (void)net_set_cork(proxy->fd_ssl, TRUE); ssl_write(proxy); (void)net_set_cork(proxy->fd_ssl, FALSE); } } ssl_proxy_unref(proxy); } static int ssl_proxy_alloc_common(SSL_CTX *ssl_ctx, int fd, const struct ip_addr *ip, pool_t set_pool, const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set, struct ssl_proxy **proxy_r) { struct ssl_proxy *proxy; SSL *ssl; int sfd[2]; i_assert(fd != -1); *proxy_r = NULL; if (!ssl_initialized) { i_error("SSL support not enabled in configuration"); return -1; } ssl_refresh_parameters(&ssl_params); ssl = SSL_new(ssl_ctx); if (ssl == NULL) { i_error("SSL_new() failed: %s", openssl_iostream_error()); return -1; } if (SSL_set_fd(ssl, fd) != 1) { i_error("SSL_set_fd() failed: %s", openssl_iostream_error()); SSL_free(ssl); return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) { i_error("socketpair() failed: %m"); SSL_free(ssl); return -1; } net_set_nonblock(sfd[0], TRUE); net_set_nonblock(sfd[1], TRUE); net_set_nonblock(fd, TRUE); proxy = i_new(struct ssl_proxy, 1); proxy->refcount = 2; proxy->ssl = ssl; proxy->login_set = login_set; proxy->ssl_set = ssl_set; proxy->fd_ssl = fd; proxy->fd_plain = sfd[0]; proxy->ip = *ip; proxy->set_pool = set_pool; pool_ref(set_pool); SSL_set_ex_data(ssl, extdata_index, proxy); ssl_proxy_count++; DLLIST_PREPEND(&ssl_proxies, proxy); *proxy_r = proxy; return sfd[1]; } static struct ssl_server_context * ssl_server_context_get(const struct login_settings *login_set, const struct master_service_ssl_settings *set) { struct ssl_server_context *ctx, lookup_ctx; i_zero(&lookup_ctx); lookup_ctx.pri.cert = set->ssl_cert; lookup_ctx.pri.key = set->ssl_key; lookup_ctx.alt.cert = set->ssl_alt_cert; lookup_ctx.alt.key = set->ssl_alt_key; lookup_ctx.ca = set->ssl_ca; lookup_ctx.cipher_list = set->ssl_cipher_list; lookup_ctx.protocols = set->ssl_protocols; lookup_ctx.verify_client_cert = set->ssl_verify_client_cert || login_set->auth_ssl_require_client_cert || login_set->auth_ssl_username_from_cert; lookup_ctx.prefer_server_ciphers = set->ssl_prefer_server_ciphers; lookup_ctx.compression = set->parsed_opts.compression; lookup_ctx.tickets = set->parsed_opts.tickets; ctx = hash_table_lookup(ssl_servers, &lookup_ctx); if (ctx == NULL) ctx = ssl_server_context_init(login_set, set); return ctx; } int ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool, const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set, struct ssl_proxy **proxy_r) { struct ssl_server_context *ctx; ctx = ssl_server_context_get(login_set, ssl_set); return ssl_proxy_alloc_common(ctx->ctx, fd, ip, set_pool, login_set, ssl_set, proxy_r); } int ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool, const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set, ssl_handshake_callback_t *callback, void *context, struct ssl_proxy **proxy_r) { int ret; ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip, set_pool, login_set, ssl_set, proxy_r); if (ret < 0) return -1; (*proxy_r)->handshake_callback = callback; (*proxy_r)->handshake_context = context; (*proxy_r)->client_proxy = TRUE; return ret; } void ssl_proxy_start(struct ssl_proxy *proxy) { ssl_step(proxy); } void ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client) { i_assert(proxy->client == NULL); client_ref(client); proxy->client = client; } bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) { return proxy->cert_received && !proxy->cert_broken; } bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy) { return proxy->cert_received && proxy->cert_broken; } int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name) { return openssl_cert_match_name(proxy->ssl, verify_name); } const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy) { X509 *x509; char *name; int len; if (!ssl_proxy_has_valid_client_cert(proxy)) return NULL; x509 = SSL_get_peer_certificate(proxy->ssl); if (x509 == NULL) return NULL; /* we should have had it.. */ len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509), ssl_username_nid, NULL, 0); if (len < 0) name = ""; else { name = t_malloc(len + 1); if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509), ssl_username_nid, name, len + 1) < 0) name = ""; else if (strlen(name) != (size_t)len) { /* NUL characters in name. Someone's trying to fake being another user? Don't allow it. */ name = ""; } } X509_free(x509); return *name == '\0' ? NULL : name; } bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy) { return proxy->handshaked; } const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy) { return proxy->last_error; } const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy) { const SSL_CIPHER *cipher; int bits, alg_bits; const char *comp_str; if (!proxy->handshaked) return ""; cipher = SSL_get_current_cipher(proxy->ssl); bits = SSL_CIPHER_get_bits(cipher, &alg_bits); comp_str = ssl_proxy_get_compression(proxy); comp_str = comp_str == NULL ? "" : t_strconcat(" ", comp_str, NULL); return t_strdup_printf("%s with cipher %s (%d/%d bits)%s", SSL_get_version(proxy->ssl), SSL_CIPHER_get_name(cipher), bits, alg_bits, comp_str); } const char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED) { #if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP) const COMP_METHOD *comp; comp = SSL_get_current_compression(proxy->ssl); return comp == NULL ? NULL : SSL_COMP_get_name(comp); #else return NULL; #endif } const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy) { return proxy->cert_error != NULL ? proxy->cert_error : "(Unknown error)"; } void ssl_proxy_free(struct ssl_proxy **_proxy) { struct ssl_proxy *proxy = *_proxy; *_proxy = NULL; ssl_proxy_unref(proxy); } static void ssl_proxy_unref(struct ssl_proxy *proxy) { if (--proxy->refcount > 0) return; i_assert(proxy->refcount == 0); SSL_free(proxy->ssl); pool_unref(&proxy->set_pool); i_free(proxy->last_error); i_free(proxy); } static void ssl_proxy_flush(struct ssl_proxy *proxy) { /* this is pretty kludgy. mainly this is just for flushing the final LOGOUT command output. */ plain_read(proxy); ssl_step(proxy); } void ssl_proxy_destroy(struct ssl_proxy *proxy) { if (proxy->destroyed || proxy->flushing) return; proxy->flushing = TRUE; if (!proxy->failed && proxy->handshaked) ssl_proxy_flush(proxy); proxy->destroyed = TRUE; ssl_proxy_count--; DLLIST_REMOVE(&ssl_proxies, proxy); if (proxy->io_ssl_read != NULL) io_remove(&proxy->io_ssl_read); if (proxy->io_ssl_write != NULL) io_remove(&proxy->io_ssl_write); if (proxy->io_plain_read != NULL) io_remove(&proxy->io_plain_read); if (proxy->io_plain_write != NULL) io_remove(&proxy->io_plain_write); if (SSL_shutdown(proxy->ssl) != 1) { /* if bidirectional shutdown fails we need to clear the error queue. */ openssl_iostream_clear_errors(); } net_disconnect(proxy->fd_ssl); net_disconnect(proxy->fd_plain); if (proxy->client != NULL) client_unref(&proxy->client); ssl_proxy_unref(proxy); } static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED, int is_export ATTR_UNUSED, int keylength) { RSA *rsa = RSA_new(); BIGNUM *e = BN_new(); BN_set_word(e, RSA_F4); RSA_generate_key_ex(rsa, keylength, e, NULL); BN_free(e); return rsa; } static DH *ssl_tmp_dh_callback(SSL *ssl ATTR_UNUSED, int is_export, int keylength) { if (is_export && keylength == 512 && ssl_params.dh_512 != NULL) return ssl_params.dh_512; return ssl_params.dh_default; } static void ssl_info_callback(const SSL *ssl, int where, int ret) { struct ssl_proxy *proxy; proxy = SSL_get_ex_data(ssl, extdata_index); if (!proxy->ssl_set->verbose_ssl) return; if ((where & SSL_CB_ALERT) != 0) { switch (ret & 0xff) { case SSL_AD_CLOSE_NOTIFY: i_debug("SSL alert: %s [%s]", SSL_alert_desc_string_long(ret), net_ip2addr(&proxy->ip)); break; default: i_warning("SSL alert: where=0x%x, ret=%d: %s %s [%s]", where, ret, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret), net_ip2addr(&proxy->ip)); break; } } else if (ret == 0) { i_warning("SSL failed: where=0x%x: %s [%s]", where, SSL_state_string_long(ssl), net_ip2addr(&proxy->ip)); } else { i_debug("SSL: where=0x%x, ret=%d: %s [%s]", where, ret, SSL_state_string_long(ssl), net_ip2addr(&proxy->ip)); } } static int ssl_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx) { SSL *ssl; struct ssl_proxy *proxy; int ctxerr; char buf[1024]; X509_NAME *subject; ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); proxy = SSL_get_ex_data(ssl, extdata_index); proxy->cert_received = TRUE; ctxerr = X509_STORE_CTX_get_error(ctx); if (proxy->client_proxy && !proxy->login_set->ssl_require_crl && (ctxerr == X509_V_ERR_UNABLE_TO_GET_CRL || ctxerr == X509_V_ERR_CRL_HAS_EXPIRED)) { /* no CRL given with the CA list. don't worry about it. */ preverify_ok = 1; } if (!preverify_ok) proxy->cert_broken = TRUE; subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx)); (void)X509_NAME_oneline(subject, buf, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; /* just in case.. */ ctxerr = X509_STORE_CTX_get_error(ctx); if (proxy->cert_error == NULL) { proxy->cert_error = p_strdup_printf(proxy->client->pool, "%s: %s", X509_verify_cert_error_string(ctxerr), buf); } if (proxy->ssl_set->verbose_ssl || (proxy->login_set->auth_verbose && !preverify_ok)) { if (preverify_ok) { client_log(proxy->client, t_strdup_printf( "Valid certificate: %s", buf)); } else { client_log(proxy->client, t_strdup_printf( "Invalid certificate: %s: %s", X509_verify_cert_error_string(ctxerr), buf)); } } /* Return success anyway, because if ssl_require_client_cert=no we could still allow authentication. */ return 1; } static int pem_password_callback(char *buf, int size, int rwflag ATTR_UNUSED, void *userdata) { if (userdata == NULL) { i_error("SSL private key file is password protected, " "but password isn't given"); return 0; } if (i_strocpy(buf, userdata, size) < 0) return 0; return strlen(buf); } unsigned int ssl_proxy_get_count(void) { return ssl_proxy_count; } static void load_ca(X509_STORE *store, const char *ca, STACK_OF(X509_NAME) **xnames_r) { /* mostly just copy&pasted from X509_load_cert_crl_file() */ STACK_OF(X509_INFO) *inf; X509_INFO *itmp; X509_NAME *xname; BIO *bio; int i; bio = BIO_new_mem_buf(t_strdup_noconst(ca), strlen(ca)); if (bio == NULL) i_fatal("BIO_new_mem_buf() failed"); inf = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); if (inf == NULL) i_fatal("Couldn't parse ssl_ca: %s", openssl_iostream_error()); BIO_free(bio); if (xnames_r != NULL) { *xnames_r = sk_X509_NAME_new_null(); if (*xnames_r == NULL) i_fatal_status(FATAL_OUTOFMEM, "sk_X509_NAME_new_null() failed"); } for(i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { X509_STORE_add_cert(store, itmp->x509); xname = X509_get_subject_name(itmp->x509); if (xname != NULL && xnames_r != NULL) { xname = X509_NAME_dup(xname); if (xname == NULL) i_fatal_status(FATAL_OUTOFMEM, "X509_NAME_dup() failed"); sk_X509_NAME_push(*xnames_r, xname); } } if(itmp->crl) X509_STORE_add_crl(store, itmp->crl); } sk_X509_INFO_pop_free(inf, X509_INFO_free); } static STACK_OF(X509_NAME) * ssl_proxy_ctx_init(SSL_CTX *ssl_ctx, const struct master_service_ssl_settings *set, bool load_xnames) { X509_STORE *store; STACK_OF(X509_NAME) *xnames = NULL; /* enable all SSL workarounds, except empty fragments as it makes SSL more vulnerable against attacks */ long ssl_ops = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #ifdef SSL_OP_NO_COMPRESSION if (!set->parsed_opts.compression) ssl_ops |= SSL_OP_NO_COMPRESSION; #endif #ifdef SSL_OP_NO_TICKET if (!set->parsed_opts.tickets) ssl_ops |= SSL_OP_NO_TICKET; #endif SSL_CTX_set_options(ssl_ctx, ssl_ops); #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif if (*set->ssl_ca != '\0') { /* set trusted CA certs */ store = SSL_CTX_get_cert_store(ssl_ctx); load_ca(store, set->ssl_ca, load_xnames ? &xnames : NULL); } ssl_proxy_ctx_set_crypto_params(ssl_ctx, set); SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback); return xnames; } static void ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx, const struct master_service_ssl_settings *set ATTR_UNUSED) { #if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO) EC_KEY *ecdh; int nid; const char *curve_name; #endif if (SSL_CTX_need_tmp_RSA(ssl_ctx)) SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); #ifdef HAVE_ECDH /* In the non-recommended situation where ECDH cipher suites are being used instead of ECDHE, do not reuse the same ECDH key pair for different sessions. This option improves forward secrecy. */ SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_ECDH_USE); #ifdef SSL_CTRL_SET_ECDH_AUTO /* OpenSSL >= 1.0.2 automatically handles ECDH temporary key parameter selection. */ SSL_CTX_set_ecdh_auto(ssl_ctx, 1); #else /* For OpenSSL < 1.0.2, ECDH temporary key parameter selection must be performed manually. Attempt to select the same curve as that used in the server's private EC key file. Otherwise fall back to the NIST P-384 (secp384r1) curve to be compliant with RFC 6460 when AES-256 TLS cipher suites are in use. This fall back option does however make Dovecot non-compliant with RFC 6460 which requires curve NIST P-256 (prime256v1) be used when AES-128 TLS cipher suites are in use. At least the non-compliance is in the form of providing too much security rather than too little. */ nid = ssl_proxy_ctx_get_pkey_ec_curve_name(set); ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { /* Fall back option */ nid = NID_secp384r1; ecdh = EC_KEY_new_by_curve_name(nid); } if ((curve_name = OBJ_nid2sn(nid)) != NULL && set->verbose_ssl) i_debug("SSL: elliptic curve %s will be used for ECDH and" " ECDHE key exchanges", curve_name); if (ecdh != NULL) { SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); EC_KEY_free(ecdh); } #endif #endif } static void ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx, STACK_OF(X509_NAME) *ca_names) { #if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE *store; store = SSL_CTX_get_cert_store(ssl_ctx); X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #endif SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_client_cert); /* set list of CA names that are sent to client */ SSL_CTX_set_client_CA_list(ssl_ctx, ca_names); } static EVP_PKEY * ATTR_NULL(2) ssl_proxy_load_key(const char *key, const char *password) { EVP_PKEY *pkey; BIO *bio; char *dup_password; bio = BIO_new_mem_buf(t_strdup_noconst(key), strlen(key)); if (bio == NULL) i_fatal("BIO_new_mem_buf() failed"); dup_password = t_strdup_noconst(password); pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback, dup_password); if (pkey == NULL) { i_fatal("Couldn't parse private ssl_key: %s", openssl_iostream_key_load_error()); } BIO_free(bio); return pkey; } static void ssl_proxy_ctx_use_key(SSL_CTX *ctx, const struct master_service_ssl_settings *set) { EVP_PKEY *pkey; const char *password; password = *set->ssl_key_password != '\0' ? set->ssl_key_password : getenv(MASTER_SSL_KEY_PASSWORD_ENV); if (*set->ssl_key != '\0') { pkey = ssl_proxy_load_key(set->ssl_key, password); if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) i_fatal("Can't load private ssl_key: %s", openssl_iostream_key_load_error()); EVP_PKEY_free(pkey); } if (*set->ssl_alt_key != '\0') { pkey = ssl_proxy_load_key(set->ssl_alt_key, password); if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) i_fatal("Can't load private ssl_alt_key: %s", openssl_iostream_key_load_error()); EVP_PKEY_free(pkey); } } #if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO) static int ssl_proxy_ctx_get_pkey_ec_curve_name(const struct master_service_ssl_settings *set) { int nid = 0; EVP_PKEY *pkey; const char *password; EC_KEY *eckey; const EC_GROUP *ecgrp; password = *set->ssl_key_password != '\0' ? set->ssl_key_password : getenv(MASTER_SSL_KEY_PASSWORD_ENV); pkey = ssl_proxy_load_key(set->ssl_key, password); if (pkey != NULL && (eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL && (ecgrp = EC_KEY_get0_group(eckey)) != NULL) nid = EC_GROUP_get_curve_name(ecgrp); else { /* clear errors added by the above calls */ openssl_iostream_clear_errors(); } EVP_PKEY_free(pkey); return nid; } #endif static int ssl_proxy_ctx_use_certificate_chain(SSL_CTX *ctx, const char *cert) { /* mostly just copy&pasted from SSL_CTX_use_certificate_chain_file() */ BIO *in; X509 *x; int ret = 0; in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert)); if (in == NULL) i_fatal("BIO_new_mem_buf() failed"); x = PEM_read_bio_X509(in, NULL, NULL, NULL); if (x == NULL) goto end; ret = SSL_CTX_use_certificate(ctx, x); #if 0 /* This is in OpenSSL code, but it seems to cause failures.. */ if (ERR_peek_error() != 0) ret = 0; #endif if (ret != 0) { /* If we could set up our certificate, now proceed to * the CA certificates. */ X509 *ca; int r; unsigned long err; while ((ca = PEM_read_bio_X509(in,NULL,NULL,NULL)) != NULL) { r = SSL_CTX_add_extra_chain_cert(ctx, ca); if (!r) { X509_free(ca); ret = 0; goto end; } } /* When the while loop ends, it's usually just EOF. */ err = ERR_peek_last_error(); if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) ERR_clear_error(); else ret = 0; /* some real error */ } end: if (x != NULL) X509_free(x); BIO_free(in); return ret; } #ifdef HAVE_SSL_GET_SERVERNAME static void ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED, void *context ATTR_UNUSED) { struct ssl_server_context *ctx; struct ssl_proxy *proxy; struct client *client; const char *host; void **other_sets; proxy = SSL_get_ex_data(ssl, extdata_index); host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); client = proxy->client; if (!client->ssl_servername_settings_read) { client->ssl_servername_settings_read = TRUE; client->set = login_settings_read(client->pool, &client->local_ip, &client->ip, host, &client->ssl_set, &other_sets); } client->local_name = p_strdup(client->pool, host); ctx = ssl_server_context_get(client->set, client->ssl_set); SSL_set_SSL_CTX(ssl, ctx->ctx); } #endif static struct ssl_server_context * ssl_server_context_init(const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set) { struct ssl_server_context *ctx; SSL_CTX *ssl_ctx; pool_t pool; STACK_OF(X509_NAME) *xnames; pool = pool_alloconly_create("ssl server context", 4096); ctx = p_new(pool, struct ssl_server_context, 1); ctx->pool = pool; ctx->pri.cert = p_strdup(pool, ssl_set->ssl_cert); ctx->pri.key = p_strdup(pool, ssl_set->ssl_key); ctx->alt.cert = p_strdup(pool, ssl_set->ssl_alt_cert); ctx->alt.key = p_strdup(pool, ssl_set->ssl_alt_key); ctx->ca = p_strdup(pool, ssl_set->ssl_ca); ctx->cipher_list = p_strdup(pool, ssl_set->ssl_cipher_list); ctx->protocols = p_strdup(pool, ssl_set->ssl_protocols); ctx->verify_client_cert = ssl_set->ssl_verify_client_cert || login_set->auth_ssl_require_client_cert || login_set->auth_ssl_username_from_cert; ctx->prefer_server_ciphers = ssl_set->ssl_prefer_server_ciphers; ctx->compression = ssl_set->parsed_opts.compression; ctx->tickets = ssl_set->parsed_opts.tickets; ctx->ctx = ssl_ctx = SSL_CTX_new(SSLv23_server_method()); if (ssl_ctx == NULL) i_fatal("SSL_CTX_new() failed"); xnames = ssl_proxy_ctx_init(ssl_ctx, ssl_set, ctx->verify_client_cert); if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->cipher_list) != 1) { i_fatal("Can't set cipher list to '%s': %s", ctx->cipher_list, openssl_iostream_error()); } if (ctx->prefer_server_ciphers) SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); SSL_CTX_set_options(ssl_ctx, openssl_get_protocol_options(ctx->protocols)); if (ctx->pri.cert != NULL && *ctx->pri.cert != '\0' && ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->pri.cert) != 1) { i_fatal("Can't load ssl_cert: %s", openssl_iostream_use_certificate_error(ctx->pri.cert, "ssl_cert")); } if (ctx->alt.cert != NULL && *ctx->alt.cert != '\0' && ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->alt.cert) != 1) { i_fatal("Can't load ssl_alt_cert: %s", openssl_iostream_use_certificate_error(ctx->alt.cert, "ssl_cert")); } #ifdef HAVE_SSL_GET_SERVERNAME if (SSL_CTX_set_tlsext_servername_callback(ctx->ctx, ssl_servername_callback) != 1) { if (ssl_set->verbose_ssl) i_debug("OpenSSL library doesn't support SNI"); } #endif ssl_proxy_ctx_use_key(ctx->ctx, ssl_set); if (ctx->verify_client_cert) ssl_proxy_ctx_verify_client(ctx->ctx, xnames); i_assert(hash_table_lookup(ssl_servers, ctx) == NULL); hash_table_insert(ssl_servers, ctx, ctx); return ctx; } static void ssl_server_context_deinit(struct ssl_server_context **_ctx) { struct ssl_server_context *ctx = *_ctx; SSL_CTX_free(ctx->ctx); pool_unref(&ctx->pool); } static void ssl_proxy_client_ctx_set_client_cert(SSL_CTX *ctx, const struct login_settings *set) { EVP_PKEY *pkey; if (*set->ssl_client_cert == '\0') return; if (ssl_proxy_ctx_use_certificate_chain(ctx, set->ssl_client_cert) != 1) { i_fatal("Can't load ssl_client_cert: %s", openssl_iostream_use_certificate_error( set->ssl_client_cert, "ssl_client_cert")); } pkey = ssl_proxy_load_key(set->ssl_client_key, NULL); if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { i_fatal("Can't load private ssl_client_key: %s", openssl_iostream_key_load_error()); } EVP_PKEY_free(pkey); } static void ssl_proxy_init_client(const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set) { STACK_OF(X509_NAME) *xnames; if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) i_fatal("SSL_CTX_new() failed"); xnames = ssl_proxy_ctx_init(ssl_client_ctx, ssl_set, TRUE); ssl_proxy_ctx_verify_client(ssl_client_ctx, xnames); ssl_proxy_client_ctx_set_client_cert(ssl_client_ctx, login_set); } void ssl_proxy_init(void) { const struct login_settings *login_set = global_login_settings; const struct master_service_ssl_settings *ssl_set = global_ssl_settings; static char dovecot[] = "dovecot"; unsigned char buf; if (strcmp(ssl_set->ssl, "no") == 0) return; SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); if (*ssl_set->ssl_crypto_device != '\0') { ENGINE_load_builtin_engines(); ssl_engine = ENGINE_by_id(ssl_set->ssl_crypto_device); if (ssl_engine == NULL) { i_fatal("Unknown ssl_crypto_device: %s", ssl_set->ssl_crypto_device); } ENGINE_init(ssl_engine); ENGINE_set_default_RSA(ssl_engine); ENGINE_set_default_DSA(ssl_engine); ENGINE_set_default_ciphers(ssl_engine); } extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL); hash_table_create(&ssl_servers, default_pool, 0, ssl_server_context_hash, ssl_server_context_cmp); (void)ssl_server_context_init(login_set, ssl_set); ssl_proxy_init_client(login_set, ssl_set); ssl_username_nid = OBJ_txt2nid(ssl_set->ssl_cert_username_field); if (ssl_username_nid == NID_undef) { i_fatal("Invalid ssl_cert_username_field: %s", ssl_set->ssl_cert_username_field); } /* PRNG initialization might want to use /dev/urandom, make sure it does it before chrooting. We might not have enough entropy at the first try, so this function may fail. It's still been initialized though. */ (void)RAND_bytes(&buf, 1); i_zero(&ssl_params); ssl_params.path = SSL_PARAMETERS_PATH; ssl_proxy_count = 0; ssl_proxies = NULL; ssl_initialized = TRUE; } void ssl_proxy_deinit(void) { struct hash_iterate_context *iter; struct ssl_server_context *ctx; if (!ssl_initialized) return; while (ssl_proxies != NULL) ssl_proxy_destroy(ssl_proxies); iter = hash_table_iterate_init(ssl_servers); while (hash_table_iterate(iter, ssl_servers, &ctx, &ctx)) ssl_server_context_deinit(&ctx); hash_table_iterate_deinit(&iter); hash_table_destroy(&ssl_servers); ssl_free_parameters(&ssl_params); SSL_CTX_free(ssl_client_ctx); if (ssl_engine != NULL) { ENGINE_finish(ssl_engine); ENGINE_cleanup(); } EVP_cleanup(); ERR_free_strings(); } #endif dovecot-2.2.33.2/src/login-common/access-lookup.c0000644000175000017500000000531213165463624016441 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "fdpass.h" #include "access-lookup.h" #include #define ACCESS_LOOKUP_TIMEOUT_MSECS (1000*60) struct access_lookup { int refcount; int fd; char *path; struct io *io; struct timeout *to; access_lookup_callback_t *callback; void *context; }; static void access_lookup_input(struct access_lookup *lookup) { unsigned char buf[3]; ssize_t ret; bool success = FALSE; ret = read(lookup->fd, buf, sizeof(buf)); if (ret < 0) { i_error("read(%s) failed: %m", lookup->path); } else if (ret == 0) { /* connection close -> no success */ } else if (ret == 2 && buf[0] == '0' && buf[1] == '\n') { /* no success */ } else if (ret == 2 && buf[0] == '1' && buf[1] == '\n') { success = TRUE; } else { i_error("access(%s): Invalid input", lookup->path); } lookup->refcount++; lookup->callback(success, lookup->context); if (lookup->refcount > 1) access_lookup_destroy(&lookup); access_lookup_destroy(&lookup); } static void access_lookup_timeout(struct access_lookup *lookup) { i_error("access(%s): Timed out while waiting for reply", lookup->path); lookup->refcount++; lookup->callback(FALSE, lookup->context); if (lookup->refcount > 1) access_lookup_destroy(&lookup); access_lookup_destroy(&lookup); } struct access_lookup * access_lookup(const char *path, int client_fd, const char *daemon_name, access_lookup_callback_t *callback, void *context) { struct access_lookup *lookup; const char *cmd; ssize_t ret; int fd; fd = net_connect_unix(path); if (fd == -1) { i_error("connect(%s) failed: %m", path); return NULL; } cmd = t_strconcat(daemon_name, "\n", NULL); ret = fd_send(fd, client_fd, cmd, strlen(cmd)); if (ret != (ssize_t)strlen(cmd)) { if (ret < 0) i_error("fd_send(%s) failed: %m", path); else i_error("fd_send(%s) didn't write enough bytes", path); i_close_fd(&fd); return NULL; } lookup = i_new(struct access_lookup, 1); lookup->refcount = 1; lookup->fd = fd; lookup->path = i_strdup(path); lookup->io = io_add(fd, IO_READ, access_lookup_input, lookup); lookup->to = timeout_add(ACCESS_LOOKUP_TIMEOUT_MSECS, access_lookup_timeout, lookup); lookup->callback = callback; lookup->context = context; return lookup; } void access_lookup_destroy(struct access_lookup **_lookup) { struct access_lookup *lookup = *_lookup; i_assert(lookup->refcount > 0); if (--lookup->refcount > 0) return; *_lookup = NULL; if (lookup->to != NULL) timeout_remove(&lookup->to); io_remove(&lookup->io); if (close(lookup->fd) < 0) i_error("close(%s) failed: %m", lookup->path); i_free(lookup->path); i_free(lookup); } dovecot-2.2.33.2/src/login-common/main.c0000644000175000017500000003324113165463624014617 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "ioloop.h" #include "array.h" #include "randgen.h" #include "module-dir.h" #include "process-title.h" #include "restrict-access.h" #include "restrict-process-size.h" #include "master-auth.h" #include "master-service.h" #include "master-interface.h" #include "client-common.h" #include "access-lookup.h" #include "anvil-client.h" #include "auth-client.h" #include "dsasl-client.h" #include "master-service-ssl-settings.h" #include "ssl-proxy.h" #include "login-proxy.h" #include #include #define AUTH_CLIENT_IDLE_TIMEOUT_MSECS (1000*60) struct login_access_lookup { struct master_service_connection conn; struct io *io; char **sockets, **next_socket; struct access_lookup *access; }; const struct login_binary *login_binary; struct auth_client *auth_client; struct master_auth *master_auth; bool closing_down, login_debug; struct anvil_client *anvil; const char *login_rawlog_dir = NULL; unsigned int initial_service_count; struct login_module_register login_module_register; ARRAY_TYPE(string) global_alt_usernames; const struct login_settings *global_login_settings; const struct master_service_ssl_settings *global_ssl_settings; void **global_other_settings; const struct ip_addr *login_source_ips; unsigned int login_source_ips_idx, login_source_ips_count; static struct module *modules; static struct timeout *auth_client_to; static const char *post_login_socket; static bool shutting_down = FALSE; static bool ssl_connections = FALSE; static bool auth_connected_once = FALSE; static void login_access_lookup_next(struct login_access_lookup *lookup); void login_refresh_proctitle(void) { struct client *client = clients; const char *addr; if (!global_login_settings->verbose_proctitle) return; if (clients_get_count() == 0) { process_title_set(""); } else if (clients_get_count() > 1 || client == NULL) { process_title_set(t_strdup_printf("[%u connections (%u TLS)]", clients_get_count(), ssl_proxy_get_count())); } else { addr = net_ip2addr(&client->ip); if (addr[0] != '\0') { process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]", addr)); } else { process_title_set(client->tls ? "[TLS]" : ""); } } } static void auth_client_idle_timeout(struct auth_client *auth_client) { i_assert(clients == NULL); auth_client_disconnect(auth_client, "idle disconnect"); timeout_remove(&auth_client_to); } void login_client_destroyed(void) { if (clients == NULL && auth_client_to == NULL) { auth_client_to = timeout_add(AUTH_CLIENT_IDLE_TIMEOUT_MSECS, auth_client_idle_timeout, auth_client); } } static void login_die(void) { shutting_down = TRUE; login_proxy_kill_idle(); if (!auth_client_is_connected(auth_client)) { /* we don't have auth client, and we might never get one */ clients_destroy_all(); } } static void client_connected_finish(const struct master_service_connection *conn) { struct client *client; struct ssl_proxy *proxy; const struct login_settings *set; const struct master_service_ssl_settings *ssl_set; pool_t pool; int fd_ssl; void **other_sets; pool = pool_alloconly_create("login client", 8*1024); set = login_settings_read(pool, &conn->local_ip, &conn->remote_ip, NULL, &ssl_set, &other_sets); if (!ssl_connections && !conn->ssl) { (void)client_create(conn->fd, FALSE, pool, conn, set, ssl_set, other_sets); } else { fd_ssl = ssl_proxy_alloc(conn->fd, &conn->remote_ip, pool, set, ssl_set, &proxy); if (fd_ssl == -1) { net_disconnect(conn->fd); pool_unref(&pool); master_service_client_connection_destroyed(master_service); return; } client = client_create(fd_ssl, TRUE, pool, conn, set, ssl_set, other_sets); client->ssl_proxy = proxy; ssl_proxy_set_client(proxy, client); ssl_proxy_start(proxy); } if (auth_client_to != NULL) timeout_remove(&auth_client_to); } static void login_access_lookup_free(struct login_access_lookup *lookup) { if (lookup->io != NULL) io_remove(&lookup->io); if (lookup->access != NULL) access_lookup_destroy(&lookup->access); if (lookup->conn.fd != -1) { if (close(lookup->conn.fd) < 0) i_error("close(client) failed: %m"); master_service_client_connection_destroyed(master_service); } p_strsplit_free(default_pool, lookup->sockets); i_free(lookup); } static void login_access_callback(bool success, void *context) { struct login_access_lookup *lookup = context; if (!success) { i_info("access(%s): Client refused (rip=%s)", *lookup->next_socket, net_ip2addr(&lookup->conn.remote_ip)); login_access_lookup_free(lookup); } else { lookup->next_socket++; login_access_lookup_next(lookup); } } static void login_access_lookup_next(struct login_access_lookup *lookup) { if (*lookup->next_socket == NULL) { /* last one */ if (lookup->io != NULL) io_remove(&lookup->io); client_connected_finish(&lookup->conn); lookup->conn.fd = -1; login_access_lookup_free(lookup); return; } lookup->access = access_lookup(*lookup->next_socket, lookup->conn.fd, login_binary->protocol, login_access_callback, lookup); if (lookup->access == NULL) login_access_lookup_free(lookup); } static void client_input_error(struct login_access_lookup *lookup) { char c; int ret; ret = recv(lookup->conn.fd, &c, 1, MSG_PEEK); if (ret <= 0) { i_info("access(%s): Client disconnected during lookup (rip=%s)", *lookup->next_socket, net_ip2addr(&lookup->conn.remote_ip)); login_access_lookup_free(lookup); } else { /* actual input. stop listening until lookup is done. */ io_remove(&lookup->io); } } static void client_connected(struct master_service_connection *conn) { const char *access_sockets = global_login_settings->login_access_sockets; struct login_access_lookup *lookup; master_service_client_connection_accept(conn); if (conn->remote_ip.family != 0) { /* log the connection's IP address in case we crash. it's of course possible that another earlier client causes the crash, but this is better than nothing. */ i_set_failure_send_ip(&conn->remote_ip); } /* make sure we're connected (or attempting to connect) to auth */ auth_client_connect(auth_client); if (*access_sockets == '\0') { /* no access checks */ client_connected_finish(conn); return; } lookup = i_new(struct login_access_lookup, 1); lookup->conn = *conn; lookup->io = io_add(conn->fd, IO_READ, client_input_error, lookup); lookup->sockets = p_strsplit_spaces(default_pool, access_sockets, " "); lookup->next_socket = lookup->sockets; login_access_lookup_next(lookup); } static void auth_connect_notify(struct auth_client *client ATTR_UNUSED, bool connected, void *context ATTR_UNUSED) { if (connected) { auth_connected_once = TRUE; clients_notify_auth_connected(); } else if (shutting_down) clients_destroy_all(); else if (!auth_connected_once) { /* auth disconnected without having ever succeeded, so the auth process is probably misconfigured. no point in keeping the client connections hanging. */ clients_destroy_all_reason("Disconnected: Auth process broken"); } } static bool anvil_reconnect_callback(void) { /* we got disconnected from anvil. we can't reconnect to it since we're chrooted, so just die after we've finished handling the current connections. */ master_service_stop_new_connections(master_service); return FALSE; } void login_anvil_init(void) { if (anvil != NULL) return; anvil = anvil_client_init("anvil", anvil_reconnect_callback, 0); if (anvil_client_connect(anvil, TRUE) < 0) i_fatal("Couldn't connect to anvil"); } static const struct ip_addr * parse_login_source_ips(const char *ips_str, unsigned int *count_r) { ARRAY(struct ip_addr) ips; const char *const *tmp; struct ip_addr *tmp_ips; bool skip_nonworking = FALSE; unsigned int i, tmp_ips_count; int ret; if (ips_str[0] == '?') { /* try binding to the IP immediately. if it doesn't work, skip it. (this allows using the same config file for all the servers.) */ skip_nonworking = TRUE; ips_str++; } t_array_init(&ips, 4); for (tmp = t_strsplit_spaces(ips_str, ", "); *tmp != NULL; tmp++) { ret = net_gethostbyname(*tmp, &tmp_ips, &tmp_ips_count); if (ret != 0) { i_error("login_source_ips: net_gethostbyname(%s) failed: %s", *tmp, net_gethosterror(ret)); continue; } for (i = 0; i < tmp_ips_count; i++) { if (skip_nonworking && net_try_bind(&tmp_ips[i]) < 0) continue; array_append(&ips, &tmp_ips[i], 1); } } return array_get(&ips, count_r); } static void login_load_modules(void) { struct module_dir_load_settings mod_set; if (global_login_settings->login_plugins[0] == '\0') return; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.binary_name = login_binary->process_name; mod_set.setting_name = "login_plugins"; mod_set.require_init_funcs = TRUE; mod_set.debug = login_debug; modules = module_dir_load(global_login_settings->login_plugin_dir, global_login_settings->login_plugins, &mod_set); module_dir_init(modules); } static void main_preinit(void) { unsigned int max_fds; random_init(); /* Initialize SSL proxy so it can read certificate and private key file. */ ssl_proxy_init(); dsasl_clients_init(); client_common_init(); /* set the number of fds we want to use. it may get increased or decreased. leave a couple of extra fds for auth sockets and such. worst case each connection can use: - 1 for client - 1 for login proxy - 2 for client-side ssl proxy - 2 for server-side ssl proxy (with login proxy) However, login process nowadays supports plugins, there are rawlogs and so on. Don't enforce the fd limit anymore, but use this value for optimizing the ioloop's fd table size. */ max_fds = MASTER_LISTEN_FD_FIRST + 16 + master_service_get_socket_count(master_service) + master_service_get_client_limit(master_service)*6; io_loop_set_max_fd_count(current_ioloop, max_fds); i_assert(strcmp(global_ssl_settings->ssl, "no") == 0 || ssl_initialized); if (global_login_settings->mail_max_userip_connections > 0) login_anvil_init(); /* read the login_source_ips before chrooting so it can access /etc/hosts */ login_source_ips = parse_login_source_ips(global_login_settings->login_source_ips, &login_source_ips_count); if (login_source_ips_count > 0) { /* randomize the initial index in case service_count=1 (although in that case it's unlikely this setting is even used..) */ login_source_ips_idx = rand() % login_source_ips_count; } login_load_modules(); restrict_access_by_env(NULL, TRUE); if (login_debug) restrict_access_allow_coredumps(TRUE); initial_service_count = master_service_get_service_count(master_service); if (restrict_access_get_current_chroot() == NULL) { if (chdir("login") < 0) i_fatal("chdir(login) failed: %m"); } if (login_rawlog_dir != NULL && access(login_rawlog_dir, W_OK | X_OK) < 0) { i_error("access(%s, wx) failed: %m - disabling rawlog", login_rawlog_dir); login_rawlog_dir = NULL; } } static void main_init(const char *login_socket) { /* make sure we can't fork() */ restrict_process_count(1); i_array_init(&global_alt_usernames, 4); master_service_set_avail_overflow_callback(master_service, client_destroy_oldest); master_service_set_die_callback(master_service, login_die); auth_client = auth_client_init(login_socket, (unsigned int)getpid(), FALSE); auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL); master_auth = master_auth_init(master_service, post_login_socket); login_binary->init(); login_proxy_init("proxy-notify"); } static void main_deinit(void) { ssl_proxy_deinit(); login_proxy_deinit(); login_binary->deinit(); module_dir_unload(&modules); auth_client_deinit(&auth_client); master_auth_deinit(&master_auth); char **strp; array_foreach_modifiable(&global_alt_usernames, strp) i_free(*strp); array_free(&global_alt_usernames); if (anvil != NULL) anvil_client_deinit(&anvil); if (auth_client_to != NULL) timeout_remove(&auth_client_to); client_common_deinit(); dsasl_clients_deinit(); login_settings_deinit(); } int login_binary_run(const struct login_binary *binary, int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN | MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE | MASTER_SERVICE_FLAG_USE_SSL_SETTINGS | MASTER_SERVICE_FLAG_NO_SSL_INIT; pool_t set_pool; const char *login_socket; int c; login_binary = binary; login_socket = binary->default_login_socket != NULL ? binary->default_login_socket : LOGIN_DEFAULT_SOCKET; post_login_socket = binary->protocol; master_service = master_service_init(login_binary->process_name, service_flags, &argc, &argv, "Dl:R:S"); master_service_init_log(master_service, t_strconcat( login_binary->process_name, ": ", NULL)); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'D': login_debug = TRUE; break; case 'l': post_login_socket = optarg; break; case 'R': login_rawlog_dir = optarg; break; case 'S': ssl_connections = TRUE; break; default: return FATAL_DEFAULT; } } if (argv[optind] != NULL) login_socket = argv[optind]; login_binary->preinit(); set_pool = pool_alloconly_create("global login settings", 4096); global_login_settings = login_settings_read(set_pool, NULL, NULL, NULL, &global_ssl_settings, &global_other_settings); main_preinit(); master_service_init_finish(master_service); main_init(login_socket); master_service_run(master_service, client_connected); main_deinit(); pool_unref(&set_pool); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/login-common/ssl-proxy.h0000644000175000017500000000356513123174404015653 00000000000000#ifndef SSL_PROXY_H #define SSL_PROXY_H struct ip_addr; struct ssl_proxy; struct master_service_ssl_settings; struct login_settings; struct client; extern bool ssl_initialized; typedef int ssl_handshake_callback_t(void *context); /* establish SSL connection with the given fd, returns a new fd which you must use from now on, or -1 if error occurred. Unless -1 is returned, the given fd must be simply forgotten. */ int ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool, const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set, struct ssl_proxy **proxy_r); int ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool, const struct login_settings *login_set, const struct master_service_ssl_settings *ssl_set, ssl_handshake_callback_t *callback, void *context, struct ssl_proxy **proxy_r); void ssl_proxy_start(struct ssl_proxy *proxy); void ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client); bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE; bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy); int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name); const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy); bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy) ATTR_PURE; const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy) ATTR_PURE; const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy); const char *ssl_proxy_get_compression(struct ssl_proxy *proxy); const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy); void ssl_proxy_destroy(struct ssl_proxy *proxy); void ssl_proxy_free(struct ssl_proxy **proxy); /* Return number of active SSL proxies */ unsigned int ssl_proxy_get_count(void) ATTR_PURE; void ssl_proxy_init(void); void ssl_proxy_deinit(void); #endif dovecot-2.2.33.2/src/login-common/login-proxy.c0000644000175000017500000007363313165463624016173 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "array.h" #include "str.h" #include "strescape.h" #include "str-sanitize.h" #include "time-util.h" #include "master-service.h" #include "ipc-server.h" #include "mail-user-hash.h" #include "client-common.h" #include "ssl-proxy.h" #include "login-proxy-state.h" #include "login-proxy.h" #define MAX_PROXY_INPUT_SIZE 4096 #define OUTBUF_THRESHOLD 1024 #define LOGIN_PROXY_DIE_IDLE_SECS 2 #define LOGIN_PROXY_IPC_PATH "ipc-proxy" #define LOGIN_PROXY_IPC_NAME "proxy" #define KILLED_BY_ADMIN_REASON "Kicked by admin" #define KILLED_BY_DIRECTOR_REASON "Kicked via director" #define KILLED_BY_SHUTDOWN_REASON "Process shutting down" #define PROXY_IMMEDIATE_FAILURE_SECS 30 #define PROXY_CONNECT_RETRY_MSECS 1000 #define PROXY_DISCONNECT_INTERVAL_MSECS 100 struct login_proxy { struct login_proxy *prev, *next; struct client *client; int client_fd, server_fd; struct io *client_io, *server_io; struct istream *client_input, *server_input; struct ostream *client_output, *server_output; struct ssl_proxy *ssl_server_proxy; time_t last_io; struct timeval created; struct timeout *to, *to_notify; struct login_proxy_record *state_rec; struct ip_addr ip, source_ip; char *host; in_port_t port; unsigned int connect_timeout_msecs; unsigned int notify_refresh_secs; unsigned int reconnect_count; enum login_proxy_ssl_flags ssl_flags; proxy_callback_t *callback; unsigned int connected:1; unsigned int destroying:1; unsigned int disconnecting:1; unsigned int delayed_disconnect:1; unsigned int num_waiting_connections_updated:1; }; static struct login_proxy_state *proxy_state; static struct login_proxy *login_proxies = NULL; static struct login_proxy *login_proxies_pending = NULL; static struct login_proxy *login_proxies_disconnecting = NULL; static struct ipc_server *login_proxy_ipc_server; static int login_proxy_connect(struct login_proxy *proxy); static void login_proxy_disconnect(struct login_proxy *proxy); static void login_proxy_ipc_cmd(struct ipc_cmd *cmd, const char *line); static void login_proxy_free_final(struct login_proxy *proxy); static void login_proxy_free_reason(struct login_proxy **_proxy, const char *reason) ATTR_NULL(2); static void login_proxy_free_delayed(struct login_proxy **_proxy, const char *reason) ATTR_NULL(2); static void login_proxy_free_errstr(struct login_proxy **_proxy, const char *errstr, bool server) { struct login_proxy *proxy = *_proxy; string_t *reason = t_str_new(128); str_printfa(reason, "Disconnected by %s", server ? "server" : "client"); if (errstr[0] != '\0') str_printfa(reason, ": %s", errstr); str_printfa(reason, "(%ds idle, in=%"PRIuUOFF_T", out=%"PRIuUOFF_T, (int)(ioloop_time - proxy->last_io), proxy->server_output->offset, proxy->client_output->offset); if (o_stream_get_buffer_used_size(proxy->client_output) > 0) { str_printfa(reason, "+%"PRIuSIZE_T, o_stream_get_buffer_used_size(proxy->client_output)); } if (proxy->server_io == NULL) str_append(reason, ", client output blocked"); if (proxy->client_io == NULL) str_append(reason, ", server output blocked"); str_append_c(reason, ')'); if (server) login_proxy_free_delayed(_proxy, str_c(reason)); else login_proxy_free_reason(_proxy, str_c(reason)); } static void login_proxy_free_errno(struct login_proxy **_proxy, int err, bool server) { const char *errstr; errstr = err == 0 || err == EPIPE ? "" : strerror(err); login_proxy_free_errstr(_proxy, errstr, server); } static void login_proxy_free_ostream(struct login_proxy **_proxy, struct ostream *output, bool server) { const char *errstr; errstr = output->stream_errno == 0 || output->stream_errno == EPIPE ? "" : o_stream_get_error(output); login_proxy_free_errstr(_proxy, errstr, server); } static void server_input(struct login_proxy *proxy) { unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret, ret2; proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->client_output) > OUTBUF_THRESHOLD) { /* client's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->server_io); return; } ret = net_receive(proxy->server_fd, buf, sizeof(buf)); if (ret < 0) { login_proxy_free_errno(&proxy, errno, TRUE); return; } o_stream_cork(proxy->client_output); ret2 = o_stream_send(proxy->client_output, buf, ret); o_stream_uncork(proxy->client_output); if (ret2 != ret) login_proxy_free_ostream(&proxy, proxy->client_output, FALSE); } static void proxy_client_input(struct login_proxy *proxy) { const unsigned char *data; size_t size; ssize_t ret; proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->server_output) > OUTBUF_THRESHOLD) { /* proxy's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->client_io); return; } if (i_stream_read_data(proxy->client_input, &data, &size, 0) < 0) { const char *errstr = i_stream_get_error(proxy->client_input); login_proxy_free_errstr(&proxy, errstr, FALSE); return; } o_stream_cork(proxy->server_output); ret = o_stream_send(proxy->server_output, data, size); o_stream_uncork(proxy->server_output); if (ret != (ssize_t)size) login_proxy_free_ostream(&proxy, proxy->server_output, TRUE); else i_stream_skip(proxy->client_input, ret); } static void proxy_client_disconnected_input(struct login_proxy *proxy) { /* we're already disconnected from server. either wait for disconnection timeout or for client to disconnect itself. */ if (i_stream_read(proxy->client_input) < 0) login_proxy_free_final(proxy); else { i_stream_skip(proxy->client_input, i_stream_get_data_size(proxy->client_input)); } } static int server_output(struct login_proxy *proxy) { proxy->last_io = ioloop_time; o_stream_cork(proxy->server_output); if (o_stream_flush(proxy->server_output) < 0) { login_proxy_free_ostream(&proxy, proxy->server_output, TRUE); return 1; } o_stream_uncork(proxy->server_output); if (proxy->client_io == NULL && o_stream_get_buffer_used_size(proxy->server_output) < OUTBUF_THRESHOLD) { /* there's again space in proxy's output buffer, so we can read more from client. */ proxy->client_io = io_add_istream(proxy->client_input, proxy_client_input, proxy); } return 1; } static int proxy_client_output(struct login_proxy *proxy) { proxy->last_io = ioloop_time; o_stream_cork(proxy->client_output); if (o_stream_flush(proxy->client_output) < 0) { login_proxy_free_ostream(&proxy, proxy->client_output, FALSE); return 1; } o_stream_uncork(proxy->client_output); if (proxy->server_io == NULL && o_stream_get_buffer_used_size(proxy->client_output) < OUTBUF_THRESHOLD) { /* there's again space in client's output buffer, so we can read more from proxy. */ proxy->server_io = io_add(proxy->server_fd, IO_READ, server_input, proxy); } return 1; } static void proxy_prelogin_input(struct login_proxy *proxy) { proxy->callback(proxy->client); } static void proxy_plain_connected(struct login_proxy *proxy) { proxy->server_input = i_stream_create_fd(proxy->server_fd, MAX_PROXY_INPUT_SIZE, FALSE); proxy->server_output = o_stream_create_fd(proxy->server_fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(proxy->server_output, TRUE); proxy->server_io = io_add(proxy->server_fd, IO_READ, proxy_prelogin_input, proxy); } static void proxy_fail_connect(struct login_proxy *proxy) { if (timeval_cmp(&proxy->created, &proxy->state_rec->last_success) < 0) { /* there was a successful connection done since we started connecting. perhaps this is just a temporary one-off failure. */ } else { proxy->state_rec->last_failure = ioloop_timeval; } i_assert(proxy->state_rec->num_waiting_connections > 0); proxy->state_rec->num_waiting_connections--; proxy->num_waiting_connections_updated = TRUE; } static void proxy_log_connect_error(struct login_proxy *proxy) { string_t *str = t_str_new(128); struct ip_addr local_ip; in_port_t local_port; str_printfa(str, "proxy(%s): ", proxy->client->virtual_user); if (!proxy->connected) { str_printfa(str, "connect(%s, %u) failed: %m", proxy->host, proxy->port); } else { str_printfa(str, "Login for %s:%u timed out in state=%s", proxy->host, proxy->port, client_proxy_get_state(proxy->client)); } str_printfa(str, " (after %u secs", (unsigned int)(ioloop_time - proxy->created.tv_sec)); if (proxy->reconnect_count > 0) str_printfa(str, ", %u reconnects", proxy->reconnect_count); if (proxy->server_fd != -1 && net_getsockname(proxy->server_fd, &local_ip, &local_port) == 0) { str_printfa(str, ", local=%s:%u", net_ip2addr(&local_ip), local_port); } else if (proxy->source_ip.family != 0) { str_printfa(str, ", local=%s", net_ip2addr(&proxy->source_ip)); } str_append_c(str, ')'); client_log_err(proxy->client, str_c(str)); } static void proxy_reconnect_timeout(struct login_proxy *proxy) { timeout_remove(&proxy->to); if (login_proxy_connect(proxy) < 0) login_proxy_free(&proxy); } static bool proxy_try_reconnect(struct login_proxy *proxy) { int since_started_msecs, left_msecs; since_started_msecs = timeval_diff_msecs(&ioloop_timeval, &proxy->created); if (since_started_msecs < 0) return FALSE; /* time moved backwards */ left_msecs = proxy->connect_timeout_msecs - since_started_msecs; if (left_msecs <= 0) return FALSE; login_proxy_disconnect(proxy); proxy->to = timeout_add(I_MIN(PROXY_CONNECT_RETRY_MSECS, left_msecs), proxy_reconnect_timeout, proxy); proxy->reconnect_count++; return TRUE; } static void proxy_wait_connect(struct login_proxy *proxy) { errno = net_geterror(proxy->server_fd); if (errno != 0) { proxy_fail_connect(proxy); if (!proxy_try_reconnect(proxy)) { proxy_log_connect_error(proxy); login_proxy_free(&proxy); } return; } proxy->connected = TRUE; proxy->num_waiting_connections_updated = TRUE; proxy->state_rec->last_success = ioloop_timeval; i_assert(proxy->state_rec->num_waiting_connections > 0); proxy->state_rec->num_waiting_connections--; proxy->state_rec->num_proxying_connections++; proxy->state_rec->num_disconnects_since_ts = 0; if ((proxy->ssl_flags & PROXY_SSL_FLAG_YES) != 0 && (proxy->ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) { if (login_proxy_starttls(proxy) < 0) { login_proxy_free(&proxy); return; } } else { io_remove(&proxy->server_io); proxy_plain_connected(proxy); } } static void proxy_connect_timeout(struct login_proxy *proxy) { errno = ETIMEDOUT; proxy_log_connect_error(proxy); if (!proxy->connected) proxy_fail_connect(proxy); login_proxy_free(&proxy); } static int login_proxy_connect(struct login_proxy *proxy) { struct login_proxy_record *rec = proxy->state_rec; /* this needs to be done early, since login_proxy_free() shrinks num_waiting_connections. */ proxy->num_waiting_connections_updated = FALSE; rec->num_waiting_connections++; if (proxy->ip.family == 0 && net_addr2ip(proxy->host, &proxy->ip) < 0) { client_log_err(proxy->client, t_strdup_printf( "proxy(%s): BUG: host %s is not an IP " "(auth should have changed it)", proxy->client->virtual_user, proxy->host)); return -1; } if (rec->last_success.tv_sec == 0) { /* first connect to this IP. don't start immediately failing the check below. */ rec->last_success.tv_sec = ioloop_timeval.tv_sec - 1; } if (timeval_cmp(&rec->last_failure, &rec->last_success) > 0 && rec->last_failure.tv_sec - rec->last_success.tv_sec > PROXY_IMMEDIATE_FAILURE_SECS && rec->num_waiting_connections > 1) { /* the server is down. fail immediately */ client_log_err(proxy->client, t_strdup_printf( "proxy(%s): Host %s:%u is down", proxy->client->virtual_user, proxy->host, proxy->port)); return -1; } proxy->server_fd = net_connect_ip(&proxy->ip, proxy->port, proxy->source_ip.family == 0 ? NULL : &proxy->source_ip); if (proxy->server_fd == -1) { proxy_log_connect_error(proxy); return -1; } proxy->server_io = io_add(proxy->server_fd, IO_WRITE, proxy_wait_connect, proxy); if (proxy->connect_timeout_msecs != 0) { proxy->to = timeout_add(proxy->connect_timeout_msecs, proxy_connect_timeout, proxy); } return 0; } int login_proxy_new(struct client *client, const struct login_proxy_settings *set, proxy_callback_t *callback) { struct login_proxy *proxy; i_assert(client->login_proxy == NULL); if (set->host == NULL || *set->host == '\0') { client_log_err(client, t_strdup_printf( "proxy(%s): host not given", client->virtual_user)); return -1; } if (client->proxy_ttl <= 1) { client_log_err(client, t_strdup_printf( "proxy(%s): TTL reached zero - " "proxies appear to be looping?", client->virtual_user)); return -1; } proxy = i_new(struct login_proxy, 1); proxy->client = client; proxy->client_fd = -1; proxy->server_fd = -1; proxy->created = ioloop_timeval; proxy->ip = set->ip; proxy->source_ip = set->source_ip; proxy->host = i_strdup(set->host); proxy->port = set->port; proxy->connect_timeout_msecs = set->connect_timeout_msecs; proxy->notify_refresh_secs = set->notify_refresh_secs; proxy->ssl_flags = set->ssl_flags; proxy->state_rec = login_proxy_state_get(proxy_state, &proxy->ip, proxy->port); client_ref(client); if (login_proxy_connect(proxy) < 0) { login_proxy_free(&proxy); return -1; } DLLIST_PREPEND(&login_proxies_pending, proxy); proxy->callback = callback; client->login_proxy = proxy; return 0; } static void login_proxy_disconnect(struct login_proxy *proxy) { if (proxy->to != NULL) timeout_remove(&proxy->to); if (proxy->to_notify != NULL) timeout_remove(&proxy->to_notify); if (!proxy->num_waiting_connections_updated) { i_assert(proxy->state_rec->num_waiting_connections > 0); proxy->state_rec->num_waiting_connections--; } if (proxy->connected) { i_assert(proxy->state_rec->num_proxying_connections > 0); proxy->state_rec->num_proxying_connections--; } if (proxy->server_io != NULL) io_remove(&proxy->server_io); if (proxy->server_input != NULL) i_stream_destroy(&proxy->server_input); if (proxy->server_output != NULL) o_stream_destroy(&proxy->server_output); if (proxy->server_fd != -1) { net_disconnect(proxy->server_fd); proxy->server_fd = -1; } } static void login_proxy_free_final(struct login_proxy *proxy) { if (proxy->delayed_disconnect) { DLLIST_REMOVE(&login_proxies_disconnecting, proxy); i_assert(proxy->state_rec->num_delayed_client_disconnects > 0); if (--proxy->state_rec->num_delayed_client_disconnects == 0) proxy->state_rec->num_disconnects_since_ts = 0; timeout_remove(&proxy->to); } if (proxy->client_io != NULL) io_remove(&proxy->client_io); if (proxy->client_input != NULL) i_stream_destroy(&proxy->client_input); if (proxy->client_output != NULL) o_stream_destroy(&proxy->client_output); if (proxy->client_fd != -1) net_disconnect(proxy->client_fd); if (proxy->ssl_server_proxy != NULL) { ssl_proxy_destroy(proxy->ssl_server_proxy); ssl_proxy_free(&proxy->ssl_server_proxy); } i_free(proxy->host); i_free(proxy); } static unsigned int login_proxy_delay_disconnect(struct login_proxy *proxy) { struct login_proxy_record *rec = proxy->state_rec; const unsigned int max_delay = proxy->client->set->login_proxy_max_disconnect_delay; struct timeval disconnect_time_offset; unsigned int max_disconnects_per_sec, delay_msecs_since_ts, max_conns; int delay_msecs; if (rec->num_disconnects_since_ts == 0) { rec->disconnect_timestamp = ioloop_timeval; /* start from a slightly random timestamp. this way all proxy processes will disconnect at slightly different times to spread the load. */ timeval_add_msecs(&rec->disconnect_timestamp, rand() % PROXY_DISCONNECT_INTERVAL_MSECS); } rec->num_disconnects_since_ts++; if (proxy->to != NULL) { /* we were already lazily disconnecting this */ return 0; } if (max_delay == 0) { /* delaying is disabled */ return 0; } max_conns = rec->num_proxying_connections + rec->num_disconnects_since_ts; max_disconnects_per_sec = (max_conns + max_delay-1) / max_delay; if (rec->num_disconnects_since_ts <= max_disconnects_per_sec && rec->num_delayed_client_disconnects == 0) { /* wait delaying until we have 1 second's worth of clients disconnected */ return 0; } /* see at which time we should be disconnecting the client. do it in 100ms intervals so the timeouts are triggered together. */ disconnect_time_offset = rec->disconnect_timestamp; delay_msecs_since_ts = PROXY_DISCONNECT_INTERVAL_MSECS * (max_delay * rec->num_disconnects_since_ts * (1000/PROXY_DISCONNECT_INTERVAL_MSECS) / max_conns); timeval_add_msecs(&disconnect_time_offset, delay_msecs_since_ts); delay_msecs = timeval_diff_msecs(&disconnect_time_offset, &ioloop_timeval); if (delay_msecs <= 0) { /* we already reached the time */ return 0; } rec->num_delayed_client_disconnects++; proxy->delayed_disconnect = TRUE; proxy->to = timeout_add(delay_msecs, login_proxy_free_final, proxy); DLLIST_PREPEND(&login_proxies_disconnecting, proxy); return delay_msecs; } static void ATTR_NULL(2) login_proxy_free_full(struct login_proxy **_proxy, const char *reason, bool delayed) { struct login_proxy *proxy = *_proxy; struct client *client = proxy->client; const char *ipstr; unsigned int delay_ms = 0; *_proxy = NULL; if (proxy->destroying) return; proxy->destroying = TRUE; /* we'll disconnect server side in any case. */ login_proxy_disconnect(proxy); if (proxy->client_fd != -1) { /* detached proxy */ DLLIST_REMOVE(&login_proxies, proxy); if (delayed) delay_ms = login_proxy_delay_disconnect(proxy); ipstr = net_ip2addr(&proxy->client->ip); client_log(proxy->client, t_strdup_printf( "proxy(%s): disconnecting %s%s%s", proxy->client->virtual_user, ipstr != NULL ? ipstr : "", reason == NULL ? "" : t_strdup_printf(" (%s)", reason), delay_ms == 0 ? "" : t_strdup_printf(" - disconnecting client in %ums", delay_ms))); if (proxy->client_io != NULL) io_remove(&proxy->client_io); } else { i_assert(proxy->client_io == NULL); i_assert(proxy->client_input == NULL); i_assert(proxy->client_output == NULL); i_assert(proxy->client_fd == -1); DLLIST_REMOVE(&login_proxies_pending, proxy); if (proxy->callback != NULL) proxy->callback(proxy->client); } if (delay_ms == 0) login_proxy_free_final(proxy); else { proxy->client_io = io_add_istream(proxy->client_input, proxy_client_disconnected_input, proxy); } client->login_proxy = NULL; client_unref(&client); } static void ATTR_NULL(2) login_proxy_free_reason(struct login_proxy **_proxy, const char *reason) { login_proxy_free_full(_proxy, reason, FALSE); } static void ATTR_NULL(2) login_proxy_free_delayed(struct login_proxy **_proxy, const char *reason) { login_proxy_free_full(_proxy, reason, TRUE); } void login_proxy_free(struct login_proxy **_proxy) { login_proxy_free_reason(_proxy, NULL); } bool login_proxy_is_ourself(const struct client *client, const char *host, in_port_t port, const char *destuser) { struct ip_addr ip; if (port != client->local_port) return FALSE; if (net_addr2ip(host, &ip) < 0) return FALSE; if (!net_ip_compare(&ip, &client->local_ip)) return FALSE; return strcmp(client->virtual_user, destuser) == 0; } struct istream *login_proxy_get_istream(struct login_proxy *proxy) { return proxy->disconnecting ? NULL : proxy->server_input; } struct ostream *login_proxy_get_ostream(struct login_proxy *proxy) { return proxy->server_output; } const char *login_proxy_get_host(const struct login_proxy *proxy) { return proxy->host; } in_port_t login_proxy_get_port(const struct login_proxy *proxy) { return proxy->port; } enum login_proxy_ssl_flags login_proxy_get_ssl_flags(const struct login_proxy *proxy) { return proxy->ssl_flags; } static void login_proxy_notify(struct login_proxy *proxy) { login_proxy_state_notify(proxy_state, proxy->client->proxy_user); } void login_proxy_detach(struct login_proxy *proxy) { struct client *client = proxy->client; const unsigned char *data; size_t size; if (proxy->client->preproxy_pool != NULL) pool_unref(&proxy->client->preproxy_pool); i_assert(proxy->client_fd == -1); i_assert(proxy->server_input != NULL); i_assert(proxy->server_output != NULL); if (proxy->to != NULL) timeout_remove(&proxy->to); proxy->client_fd = i_stream_get_fd(client->input); proxy->client_input = client->input; proxy->client_output = client->output; i_stream_set_persistent_buffers(client->input, FALSE); o_stream_set_max_buffer_size(client->output, (size_t)-1); o_stream_set_flush_callback(client->output, proxy_client_output, proxy); client->input = NULL; client->output = NULL; /* send all pending client input to proxy */ data = i_stream_get_data(proxy->client_input, &size); if (size != 0) o_stream_nsend(proxy->server_output, data, size); /* from now on, just do dummy proxying */ io_remove(&proxy->server_io); proxy->server_io = io_add(proxy->server_fd, IO_READ, server_input, proxy); proxy->client_io = io_add_istream(proxy->client_input, proxy_client_input, proxy); o_stream_set_flush_callback(proxy->server_output, server_output, proxy); i_stream_destroy(&proxy->server_input); if (proxy->notify_refresh_secs != 0) { proxy->to_notify = timeout_add(proxy->notify_refresh_secs * 1000, login_proxy_notify, proxy); } proxy->callback = NULL; if (login_proxy_ipc_server == NULL) { login_proxy_ipc_server = ipc_server_init(LOGIN_PROXY_IPC_PATH, LOGIN_PROXY_IPC_NAME, login_proxy_ipc_cmd); } DLLIST_REMOVE(&login_proxies_pending, proxy); DLLIST_PREPEND(&login_proxies, proxy); client->fd = -1; client->login_proxy = NULL; } static int login_proxy_ssl_handshaked(void *context) { struct login_proxy *proxy = context; if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0) return 0; if (ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) { client_log_err(proxy->client, t_strdup_printf( "proxy: Received invalid SSL certificate from %s:%u: %s", proxy->host, proxy->port, ssl_proxy_get_cert_error(proxy->ssl_server_proxy))); } else if (!ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy)) { client_log_err(proxy->client, t_strdup_printf( "proxy: SSL certificate not received from %s:%u", proxy->host, proxy->port)); } else if (ssl_proxy_cert_match_name(proxy->ssl_server_proxy, proxy->host) < 0) { client_log_err(proxy->client, t_strdup_printf( "proxy: hostname doesn't match SSL certificate at %s:%u", proxy->host, proxy->port)); } else { return 0; } proxy->disconnecting = TRUE; return -1; } int login_proxy_starttls(struct login_proxy *proxy) { int fd; if (proxy->server_input != NULL) i_stream_destroy(&proxy->server_input); if (proxy->server_output != NULL) o_stream_destroy(&proxy->server_output); io_remove(&proxy->server_io); fd = ssl_proxy_client_alloc(proxy->server_fd, &proxy->client->ip, proxy->client->pool, proxy->client->set, proxy->client->ssl_set, login_proxy_ssl_handshaked, proxy, &proxy->ssl_server_proxy); if (fd < 0) { client_log_err(proxy->client, t_strdup_printf( "proxy: SSL handshake failed to %s:%u", proxy->host, proxy->port)); return -1; } ssl_proxy_set_client(proxy->ssl_server_proxy, proxy->client); ssl_proxy_start(proxy->ssl_server_proxy); proxy->server_fd = fd; proxy_plain_connected(proxy); return 0; } static void proxy_kill_idle(struct login_proxy *proxy) { login_proxy_free_reason(&proxy, KILLED_BY_SHUTDOWN_REASON); } void login_proxy_kill_idle(void) { struct login_proxy *proxy, *next; time_t now = time(NULL); time_t stop_timestamp = now - LOGIN_PROXY_DIE_IDLE_SECS; unsigned int stop_msecs; for (proxy = login_proxies; proxy != NULL; proxy = next) { next = proxy->next; if (proxy->last_io <= stop_timestamp) proxy_kill_idle(proxy); else { i_assert(proxy->to == NULL); stop_msecs = (proxy->last_io - stop_timestamp) * 1000; proxy->to = timeout_add(stop_msecs, proxy_kill_idle, proxy); } } } static bool want_kick_virtual_user(struct client *client, const char *const *args, unsigned int key_idx ATTR_UNUSED) { return str_array_find(args, client->virtual_user); } static bool want_kick_alt_username(struct client *client, const char *const *args, unsigned int key_idx) { unsigned int i; if (client->alt_usernames == NULL) return FALSE; for (i = 0; i < key_idx; i++) { if (client->alt_usernames[i] == NULL) return FALSE; } return str_array_find(args, client->alt_usernames[i]); } static void login_proxy_cmd_kick_full(struct ipc_cmd *cmd, const char *const *args, bool (*want_kick)(struct client *, const char *const *, unsigned int), unsigned int key_idx) { struct login_proxy *proxy, *next; unsigned int count = 0; if (args[0] == NULL) { ipc_cmd_fail(&cmd, "Missing parameter"); return; } for (proxy = login_proxies; proxy != NULL; proxy = next) { next = proxy->next; if (want_kick(proxy->client, args, key_idx)) { login_proxy_free_delayed(&proxy, KILLED_BY_ADMIN_REASON); count++; } } for (proxy = login_proxies_pending; proxy != NULL; proxy = next) { next = proxy->next; if (want_kick(proxy->client, args, key_idx)) { client_destroy(proxy->client, "Connection kicked"); count++; } } ipc_cmd_success_reply(&cmd, t_strdup_printf("%u", count)); } static void login_proxy_cmd_kick(struct ipc_cmd *cmd, const char *const *args) { login_proxy_cmd_kick_full(cmd, args, want_kick_virtual_user, 0); } static void login_proxy_cmd_kick_alt(struct ipc_cmd *cmd, const char *const *args) { char *const *fields; unsigned int i, count; if (args[0] == NULL) { ipc_cmd_fail(&cmd, "Missing parameter"); return; } fields = array_get(&global_alt_usernames, &count); for (i = 0; i < count; i++) { if (strcmp(fields[i], args[0]) == 0) break; } if (i == count) { /* field doesn't exist, but it's not an error necessarily */ ipc_cmd_success_reply(&cmd, "0"); return; } login_proxy_cmd_kick_full(cmd, args+1, want_kick_alt_username, i); } static unsigned int director_username_hash(struct client *client) { if (client->director_username_hash_cache != 0) { /* already set */ } else { client->director_username_hash_cache = mail_user_hash(client->virtual_user, client->set->director_username_hash); } return client->director_username_hash_cache; } static void login_proxy_cmd_kick_director_hash(struct ipc_cmd *cmd, const char *const *args) { struct login_proxy *proxy, *next; struct ip_addr except_ip; unsigned int hash, count = 0; if (args[0] == NULL || str_to_uint(args[0], &hash) < 0) { ipc_cmd_fail(&cmd, "Invalid parameters"); return; } /* optional except_ip parameter specifies that we're not killing the connections that are proxying to the except_ip backend */ except_ip.family = 0; if (args[1] != NULL && args[1][0] != '\0' && net_addr2ip(args[1], &except_ip) < 0) { ipc_cmd_fail(&cmd, "Invalid except_ip parameter"); return; } for (proxy = login_proxies; proxy != NULL; proxy = next) { next = proxy->next; if (director_username_hash(proxy->client) == hash && !net_ip_compare(&proxy->ip, &except_ip)) { login_proxy_free_delayed(&proxy, KILLED_BY_DIRECTOR_REASON); count++; } } for (proxy = login_proxies_pending; proxy != NULL; proxy = next) { next = proxy->next; if (director_username_hash(proxy->client) == hash && !net_ip_compare(&proxy->ip, &except_ip)) { client_destroy(proxy->client, "Connection kicked"); count++; } } ipc_cmd_success_reply(&cmd, t_strdup_printf("%u", count)); } static void login_proxy_cmd_list_reply(struct ipc_cmd *cmd, string_t *str, struct login_proxy *proxy) { unsigned int i, alt_count = array_count(&global_alt_usernames); str_truncate(str, 0); str_append_tabescaped(str, proxy->client->virtual_user); str_append_c(str, '\t'); i = 0; if (proxy->client->alt_usernames != NULL) { for (; proxy->client->alt_usernames[i] != NULL; i++) { str_append_tabescaped(str, proxy->client->alt_usernames[i]); str_append_c(str, '\t'); } i_assert(i <= alt_count); } for (; i < alt_count; i++) str_append_c(str, '\t'); str_printfa(str, "%s\t%s\t%s\t%u", login_binary->protocol, net_ip2addr(&proxy->client->ip), net_ip2addr(&proxy->ip), proxy->port); ipc_cmd_send(cmd, str_c(str)); } static void login_proxy_cmd_list(struct ipc_cmd *cmd, const char *const *args ATTR_UNUSED) { struct login_proxy *proxy; char *const *fieldp; string_t *str = t_str_new(64); str_append(str, "username\t"); array_foreach(&global_alt_usernames, fieldp) { str_append_tabescaped(str, *fieldp); str_append_c(str, '\t'); } str_append(str, "service\tsrc-ip\tdest-ip\tdest-port"); ipc_cmd_send(cmd, str_c(str)); for (proxy = login_proxies; proxy != NULL; proxy = proxy->next) login_proxy_cmd_list_reply(cmd, str, proxy); for (proxy = login_proxies_pending; proxy != NULL; proxy = proxy->next) login_proxy_cmd_list_reply(cmd, str, proxy); ipc_cmd_success(&cmd); } static void login_proxy_ipc_cmd(struct ipc_cmd *cmd, const char *line) { const char *const *args = t_strsplit_tabescaped(line); const char *name = args[0]; args++; if (strcmp(name, "KICK") == 0) login_proxy_cmd_kick(cmd, args); else if (strcmp(name, "KICK-ALT") == 0) login_proxy_cmd_kick_alt(cmd, args); else if (strcmp(name, "KICK-DIRECTOR-HASH") == 0) login_proxy_cmd_kick_director_hash(cmd, args); else if (strcmp(name, "LIST-FULL") == 0) login_proxy_cmd_list(cmd, args); else ipc_cmd_fail(&cmd, "Unknown command"); } void login_proxy_init(const char *proxy_notify_pipe_path) { proxy_state = login_proxy_state_init(proxy_notify_pipe_path); } void login_proxy_deinit(void) { struct login_proxy *proxy; while (login_proxies != NULL) { proxy = login_proxies; login_proxy_free_reason(&proxy, KILLED_BY_SHUTDOWN_REASON); } while (login_proxies_disconnecting != NULL) login_proxy_free_final(login_proxies_disconnecting); if (login_proxy_ipc_server != NULL) ipc_server_deinit(&login_proxy_ipc_server); login_proxy_state_deinit(&proxy_state); } dovecot-2.2.33.2/src/login-common/login-proxy-state.h0000644000175000017500000000253213123174404017271 00000000000000#ifndef LOGIN_PROXY_STATE_H #define LOGIN_PROXY_STATE_H #include struct login_proxy_record { struct ip_addr ip; in_port_t port; /* These are used to spread client-visible disconnects over longer periods of time to avoid reconnect spikes when a server dies. If num_disconnects_since_ts=0 when server disconnects us, it's increased and disconnect_timestamp is updated. Afterwards it's increased for each new disconnection. num_disconnects_since_ts gets reset back to zero whenever a) last_success gets updated or b) num_delayed_client_disconnects drops to 0. */ struct timeval disconnect_timestamp; unsigned int num_disconnects_since_ts; unsigned int num_delayed_client_disconnects; /* these are tracking connect()s, not necessarily logins: */ unsigned int num_waiting_connections; /* number of connections we're proxying now (post-login) */ unsigned int num_proxying_connections; struct timeval last_failure; struct timeval last_success; }; struct login_proxy_state *login_proxy_state_init(const char *notify_path); void login_proxy_state_deinit(struct login_proxy_state **state); struct login_proxy_record * login_proxy_state_get(struct login_proxy_state *state, const struct ip_addr *ip, in_port_t port); void login_proxy_state_notify(struct login_proxy_state *state, const char *user); #endif dovecot-2.2.33.2/src/login-common/sasl-server.h0000644000175000017500000000146713165463624016153 00000000000000#ifndef SASL_SERVER_H #define SASL_SERVER_H struct client; enum sasl_server_reply { SASL_SERVER_REPLY_SUCCESS, SASL_SERVER_REPLY_AUTH_FAILED, SASL_SERVER_REPLY_AUTH_ABORTED, SASL_SERVER_REPLY_MASTER_FAILED, SASL_SERVER_REPLY_CONTINUE }; typedef void sasl_server_callback_t(struct client *client, enum sasl_server_reply reply, const char *data, const char *const *args); const struct auth_mech_desc * sasl_server_get_advertised_mechs(struct client *client, unsigned int *count_r); void sasl_server_auth_begin(struct client *client, const char *service, const char *mech_name, const char *initial_resp_base64, sasl_server_callback_t *callback); void sasl_server_auth_failed(struct client *client, const char *reason); void sasl_server_auth_abort(struct client *client); #endif dovecot-2.2.33.2/src/login-common/ssl-proxy-gnutls.c0000644000175000017500000003121613123174404017152 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "ioloop.h" #include "net.h" #include "hash.h" #include "ssl-proxy.h" #ifdef HAVE_GNUTLS #error broken currently #include #include #include #include #include struct ssl_proxy { int refcount; gnutls_session session; struct ip_addr ip; int fd_ssl, fd_plain; struct io *io_ssl, *io_plain; int io_ssl_dir; unsigned char outbuf_plain[1024]; unsigned int outbuf_pos_plain; size_t send_left_ssl, send_left_plain; }; const int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; const int kx_priority[] = { GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 }; const int cipher_priority[] = { GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 }; const int comp_priority[] = { GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; const int mac_priority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; static struct hash_table *ssl_proxies; static gnutls_certificate_credentials x509_cred; static gnutls_dh_params dh_params; static gnutls_rsa_params rsa_params; static void ssl_input(struct ssl_proxy *proxy); static void plain_input(struct ssl_proxy *proxy); static bool ssl_proxy_destroy(struct ssl_proxy *proxy); static const char *get_alert_text(struct ssl_proxy *proxy) { return gnutls_alert_get_name(gnutls_alert_get(proxy->session)); } static int handle_ssl_error(struct ssl_proxy *proxy, int error) { if (!gnutls_error_is_fatal(error)) { if (!verbose_ssl) return 0; if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) { i_warning("Received SSL warning alert: %s [%s]", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } else { i_warning("Non-fatal SSL error: %s: %s", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } return 0; } if (verbose_ssl) { /* fatal error occurred */ if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) { i_warning("Received SSL fatal alert: %s [%s]", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } else { i_warning("Error reading from SSL client: %s [%s]", gnutls_strerror(error), net_ip2addr(&proxy->ip)); } } gnutls_alert_send_appropriate(proxy->session, error); ssl_proxy_destroy(proxy); return -1; } static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size) { int rcvd; rcvd = gnutls_record_recv(proxy->session, data, size); if (rcvd > 0) return rcvd; if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { /* disconnected, either by nicely telling us that we'll close the connection, or by simply killing the connection which gives us the packet length error. */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, rcvd); } static int proxy_send_ssl(struct ssl_proxy *proxy, const void *data, size_t size) { int sent; sent = gnutls_record_send(proxy->session, data, size); if (sent >= 0) return sent; if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) { /* don't warn about errors related to unexpected disconnection */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, sent); } static int ssl_proxy_destroy(struct ssl_proxy *proxy) { if (--proxy->refcount > 0) return TRUE; hash_table_remove(ssl_proxies, proxy); gnutls_deinit(proxy->session); if (proxy->io_ssl != NULL) io_remove(proxy->io_ssl); if (proxy->io_plain != NULL) io_remove(proxy->io_plain); net_disconnect(proxy->fd_ssl); net_disconnect(proxy->fd_plain); i_free(proxy); main_unref(); return FALSE; } static void ssl_output(struct ssl_proxy *proxy) { int sent; sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain + proxy->outbuf_pos_plain, proxy->send_left_plain); if (sent < 0) { /* disconnected */ ssl_proxy_destroy(proxy); return; } proxy->send_left_plain -= sent; proxy->outbuf_pos_plain += sent; if (proxy->send_left_plain > 0) return; /* everything is sent, start reading again */ io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy); } static void ssl_input(struct ssl_proxy *proxy) { int rcvd, sent; rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain, sizeof(proxy->outbuf_plain)); if (rcvd <= 0) return; sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd); if (sent == rcvd) return; if (sent < 0) { /* disconnected */ ssl_proxy_destroy(proxy); return; } /* everything wasn't sent - don't read anything until we've sent it all */ proxy->outbuf_pos_plain = 0; proxy->send_left_plain = rcvd - sent; io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy); } static void plain_output(struct ssl_proxy *proxy) { int sent; sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl); if (sent <= 0) return; proxy->send_left_ssl -= sent; if (proxy->send_left_ssl > 0) return; /* everything is sent, start reading again */ io_remove(proxy->io_plain); proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy); } static void plain_input(struct ssl_proxy *proxy) { char buf[1024]; ssize_t rcvd, sent; rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf)); if (rcvd < 0) { /* disconnected */ gnutls_bye(proxy->session, 1); ssl_proxy_destroy(proxy); return; } sent = proxy_send_ssl(proxy, buf, (size_t)rcvd); if (sent < 0 || sent == rcvd) return; /* everything wasn't sent - don't read anything until we've sent it all */ proxy->send_left_ssl = rcvd - sent; io_remove(proxy->io_plain); proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy); } static void ssl_handshake(struct ssl_proxy *proxy) { int ret, dir; ret = gnutls_handshake(proxy->session); if (ret >= 0) { /* handshake done, now we can start reading */ if (proxy->io_ssl != NULL) io_remove(proxy->io_ssl); proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy); proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy); return; } if (handle_ssl_error(proxy, ret) < 0) return; /* i/o interrupted */ dir = gnutls_record_get_direction(proxy->session) == 0 ? IO_READ : IO_WRITE; if (proxy->io_ssl_dir != dir) { if (proxy->io_ssl != NULL) io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_handshake, proxy); proxy->io_ssl_dir = dir; } } static gnutls_session initialize_state(void) { gnutls_session session; gnutls_init(&session, GNUTLS_SERVER); gnutls_protocol_set_priority(session, protocol_priority); gnutls_cipher_set_priority(session, cipher_priority); gnutls_compression_set_priority(session, comp_priority); gnutls_kx_set_priority(session, kx_priority); gnutls_mac_set_priority(session, mac_priority); gnutls_certificate_type_set_priority(session, cert_type_priority); gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); return session; } int ssl_proxy_new(int fd, struct ip_addr *ip) { struct ssl_proxy *proxy; gnutls_session session; int sfd[2]; if (!ssl_initialized) { i_error("SSL support not enabled in configuration"); return -1; } session = initialize_state(); gnutls_transport_set_ptr(session, fd); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) { i_error("socketpair() failed: %m"); gnutls_deinit(session); return -1; } net_set_nonblock(sfd[0], TRUE); net_set_nonblock(sfd[1], TRUE); net_set_nonblock(fd, TRUE); proxy = i_new(struct ssl_proxy, 1); proxy->refcount = 1; proxy->session = session; proxy->fd_ssl = fd; proxy->fd_plain = sfd[0]; proxy->ip = *ip; hash_table_insert(ssl_proxies, proxy, proxy); proxy->refcount++; ssl_handshake(proxy); if (!ssl_proxy_destroy(proxy)) { /* handshake failed. return the disconnected socket anyway so the caller doesn't try to use the old closed fd */ return sfd[1]; } main_ref(); return sfd[1]; } static void read_next_field(int fd, gnutls_datum *datum, const char *fname, const char *field_name) { ssize_t ret; /* get size */ ret = read(fd, &datum->size, sizeof(datum->size)); if (ret < 0) i_fatal("read() failed for %s: %m", fname); if (ret != sizeof(datum->size)) { i_unlink(fname); i_fatal("Corrupted SSL parameter file %s: File too small", fname); } if (datum->size > 10240) { i_unlink(fname); i_fatal("Corrupted SSL parameter file %s: " "Field '%s' too large (%u)", fname, field_name, datum->size); } /* read the actual data */ datum->data = t_malloc(datum->size); ret = read(fd, datum->data, datum->size); if (ret < 0) i_fatal("read() failed for %s: %m", fname); if ((size_t)ret != datum->size) { i_unlink(fname); i_fatal("Corrupted SSL parameter file %s: " "Field '%s' not fully in file (%u < %u)", fname, field_name, datum->size - ret, datum->size); } } static void read_dh_parameters(int fd, const char *fname) { gnutls_datum dbits, prime, generator; int ret, bits; if ((ret = gnutls_dh_params_init(&dh_params)) < 0) { i_fatal("gnutls_dh_params_init() failed: %s", gnutls_strerror(ret)); } /* read until bits field is 0 */ for (;;) { read_next_field(fd, &dbits, fname, "DH bits"); if (dbits.size != sizeof(int)) { i_unlink(fname); i_fatal("Corrupted SSL parameter file %s: " "Field 'DH bits' has invalid size %u", fname, dbits.size); } bits = *((int *) dbits.data); if (bits == 0) break; read_next_field(fd, &prime, fname, "DH prime"); read_next_field(fd, &generator, fname, "DH generator"); ret = gnutls_dh_params_set(dh_params, prime, generator, bits); if (ret < 0) { i_fatal("gnutls_dh_params_set() failed: %s", gnutls_strerror(ret)); } } } static void read_rsa_parameters(int fd, const char *fname) { gnutls_datum m, e, d, p, q, u; int ret; read_next_field(fd, &m, fname, "RSA m"); read_next_field(fd, &e, fname, "RSA e"); read_next_field(fd, &d, fname, "RSA d"); read_next_field(fd, &p, fname, "RSA p"); read_next_field(fd, &q, fname, "RSA q"); read_next_field(fd, &u, fname, "RSA u"); if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) { i_fatal("gnutls_rsa_params_init() failed: %s", gnutls_strerror(ret)); } /* only 512bit is allowed */ ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512); if (ret < 0) { i_fatal("gnutls_rsa_params_set() failed: %s", gnutls_strerror(ret)); } } static void read_parameters(const char *fname) { int fd; /* we'll wait until parameter file exists */ for (;;) { fd = open(fname, O_RDONLY); if (fd != -1) break; if (errno != ENOENT) i_fatal("Can't open SSL parameter file %s: %m", fname); sleep(1); } read_dh_parameters(fd, fname); read_rsa_parameters(fd, fname); i_close_fd(&fd); } static void gcrypt_log_handler(void *context ATTR_UNUSED, int level, const char *fmt, va_list args) { if (level != GCRY_LOG_FATAL) return; T_BEGIN { i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args)); } T_END; } void ssl_proxy_init(void) { const char *certfile, *keyfile, *paramfile; unsigned char buf[4]; int ret; certfile = getenv("SSL_CERT_FILE"); keyfile = getenv("SSL_KEY_FILE"); paramfile = getenv("SSL_PARAM_FILE"); if (certfile == NULL || keyfile == NULL || paramfile == NULL) { /* SSL support is disabled */ return; } if ((ret = gnutls_global_init() < 0)) { i_fatal("gnu_tls_global_init() failed: %s", gnutls_strerror(ret)); } /* gcrypt initialization - set log handler and make sure randomizer opens /dev/urandom now instead of after we've chrooted */ gcry_set_log_handler(gcrypt_log_handler, NULL); gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM); read_parameters(paramfile); if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { i_fatal("gnutls_certificate_allocate_credentials() failed: %s", gnutls_strerror(ret)); } ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile, GNUTLS_X509_FMT_PEM); if (ret < 0) { i_fatal("Can't load certificate files %s and %s: %s", certfile, keyfile, gnutls_strerror(ret)); } gnutls_certificate_set_dh_params(x509_cred, dh_params); gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params); ssl_proxies = hash_table_create(default_pool, 0, NULL, NULL); ssl_initialized = TRUE; } void ssl_proxy_deinit(void) { struct hash_iterate_context *iter; void *key, *value; if (!ssl_initialized) return; iter = hash_table_iterate_init(ssl_proxies); while (hash_table_iterate(iter, &key, &value)) ssl_proxy_destroy(value); hash_table_iterate_deinit(iter); hash_table_destroy(ssl_proxies); gnutls_certificate_free_credentials(x509_cred); gnutls_global_deinit(); } #endif dovecot-2.2.33.2/src/login-common/client-common.c0000644000175000017500000006210713165463624016442 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "array.h" #include "hostpid.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "iostream-rawlog.h" #include "process-title.h" #include "hook-build.h" #include "buffer.h" #include "str.h" #include "strescape.h" #include "base64.h" #include "str-sanitize.h" #include "safe-memset.h" #include "var-expand.h" #include "master-interface.h" #include "master-service.h" #include "master-service-ssl-settings.h" #include "master-auth.h" #include "auth-client.h" #include "dsasl-client.h" #include "login-proxy.h" #include "ssl-proxy.h" #include "client-common.h" struct client *clients = NULL; static struct client *last_client = NULL; static unsigned int clients_count = 0; struct login_client_module_hooks { struct module *module; const struct login_client_hooks *hooks; }; static ARRAY(struct login_client_module_hooks) module_hooks = ARRAY_INIT; void login_client_hooks_add(struct module *module, const struct login_client_hooks *hooks) { struct login_client_module_hooks *hook; hook = array_append_space(&module_hooks); hook->module = module; hook->hooks = hooks; } void login_client_hooks_remove(const struct login_client_hooks *hooks) { const struct login_client_module_hooks *module_hook; unsigned int idx = UINT_MAX; array_foreach(&module_hooks, module_hook) { if (module_hook->hooks == hooks) { idx = array_foreach_idx(&module_hooks, module_hook); break; } } i_assert(idx != UINT_MAX); array_delete(&module_hooks, idx, 1); } static void hook_login_client_allocated(struct client *client) { const struct login_client_module_hooks *module_hook; struct hook_build_context *ctx; ctx = hook_build_init((void *)&client->v, sizeof(client->v)); client->vlast = &client->v; array_foreach(&module_hooks, module_hook) { if (module_hook->hooks->client_allocated != NULL) T_BEGIN { module_hook->hooks->client_allocated(client); hook_build_update(ctx, client->vlast); } T_END; } client->vlast = NULL; hook_build_deinit(&ctx); } static void client_idle_disconnect_timeout(struct client *client) { const char *user_reason, *destroy_reason; unsigned int secs; if (client->master_tag != 0) { secs = ioloop_time - client->auth_finished; user_reason = "Timeout while finishing login."; destroy_reason = t_strdup_printf( "Timeout while finishing login (waited %u secs)", secs); client_log_err(client, destroy_reason); } else if (client->auth_request != NULL) { user_reason = "Disconnected for inactivity during authentication."; destroy_reason = "Disconnected: Inactivity during authentication"; } else if (client->login_proxy != NULL) { secs = ioloop_time - client->created; user_reason = "Timeout while finishing login."; destroy_reason = t_strdup_printf( "proxy: Logging in to %s:%u timed out " "(state=%s, duration=%us)", login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy), client_proxy_get_state(client), secs); client_log_err(client, destroy_reason); } else { user_reason = "Disconnected for inactivity."; destroy_reason = "Disconnected: Inactivity"; } client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason); client_destroy(client, destroy_reason); } static void client_open_streams(struct client *client) { client->input = i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE); o_stream_set_no_error_handling(client->output, TRUE); if (login_rawlog_dir != NULL) { if (iostream_rawlog_create(login_rawlog_dir, &client->input, &client->output) < 0) login_rawlog_dir = NULL; } } static bool client_is_trusted(struct client *client) { const char *const *net; struct ip_addr net_ip; unsigned int bits; if (client->set->login_trusted_networks == NULL) return FALSE; net = t_strsplit_spaces(client->set->login_trusted_networks, ", "); for (; *net != NULL; net++) { if (net_parse_range(*net, &net_ip, &bits) < 0) { i_error("login_trusted_networks: " "Invalid network '%s'", *net); break; } if (net_is_in_network(&client->ip, &net_ip, bits)) return TRUE; } return FALSE; } struct client * client_create(int fd, bool ssl, pool_t pool, const struct master_service_connection *conn, const struct login_settings *set, const struct master_service_ssl_settings *ssl_set, void **other_sets) { struct client *client; i_assert(fd != -1); client = login_binary->client_vfuncs->alloc(pool); client->v = *login_binary->client_vfuncs; if (client->v.auth_send_challenge == NULL) client->v.auth_send_challenge = client_auth_send_challenge; if (client->v.auth_parse_response == NULL) client->v.auth_parse_response = client_auth_parse_response; client->created = ioloop_time; client->refcount = 1; client->pool = pool; client->preproxy_pool = pool_alloconly_create(MEMPOOL_GROWING"preproxy pool", 256); client->set = set; client->ssl_set = ssl_set; p_array_init(&client->module_contexts, client->pool, 5); client->fd = fd; client->tls = ssl; client->local_ip = conn->local_ip; client->local_port = conn->local_port; client->ip = conn->remote_ip; client->remote_port = conn->remote_port; client->real_local_ip = conn->real_local_ip; client->real_local_port = conn->real_local_port; client->real_remote_ip = conn->real_remote_ip; client->real_remote_port = conn->real_remote_port; client->listener_name = p_strdup(client->pool, conn->name); client->trusted = client_is_trusted(client); client->secured = ssl || client->trusted || net_ip_compare(&conn->real_remote_ip, &conn->real_local_ip); client->proxy_ttl = LOGIN_PROXY_TTL; if (last_client == NULL) last_client = client; DLLIST_PREPEND(&clients, client); clients_count++; client->to_disconnect = timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS, client_idle_disconnect_timeout, client); client_open_streams(client); hook_login_client_allocated(client); client->v.create(client, other_sets); if (auth_client_is_connected(auth_client)) client_notify_auth_ready(client); else client_set_auth_waiting(client); login_refresh_proctitle(); return client; } void client_destroy(struct client *client, const char *reason) { if (client->destroyed) return; client->destroyed = TRUE; if (client->preproxy_pool != NULL) pool_unref(&client->preproxy_pool); if (!client->login_success && reason != NULL) { const char *extra_reason = client_get_extra_disconnect_reason(client); if (extra_reason[0] != '\0') reason = t_strconcat(reason, " ", extra_reason, NULL); } if (reason != NULL) client_log(client, reason); if (last_client == client) last_client = client->prev; DLLIST_REMOVE(&clients, client); if (client->output != NULL) o_stream_uncork(client->output); if (!client->login_success && client->ssl_proxy != NULL) ssl_proxy_destroy(client->ssl_proxy); if (client->input != NULL) i_stream_close(client->input); if (client->output != NULL) o_stream_close(client->output); if (client->master_tag != 0) { i_assert(client->auth_request == NULL); i_assert(client->authenticating); i_assert(client->refcount > 1); client->authenticating = FALSE; master_auth_request_abort(master_auth, client->master_tag); client->refcount--; } else if (client->auth_request != NULL) { i_assert(client->authenticating); sasl_server_auth_abort(client); } else { i_assert(!client->authenticating); } if (client->io != NULL) io_remove(&client->io); if (client->to_disconnect != NULL) timeout_remove(&client->to_disconnect); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (client->auth_response != NULL) str_free(&client->auth_response); if (client->fd != -1) { net_disconnect(client->fd); client->fd = -1; } if (client->proxy_password != NULL) { safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } if (client->proxy_sasl_client != NULL) dsasl_client_free(&client->proxy_sasl_client); if (client->login_proxy != NULL) login_proxy_free(&client->login_proxy); if (client->v.destroy != NULL) client->v.destroy(client); if (client_unref(&client) && initial_service_count == 1) { /* as soon as this connection is done with proxying (or whatever), the process will die. there's no need for authentication anymore, so close the connection. do this only with initial service_count=1, in case there are other clients with pending authentications */ auth_client_disconnect(auth_client, "unnecessary connection"); } login_client_destroyed(); login_refresh_proctitle(); } void client_destroy_success(struct client *client, const char *reason) { client->login_success = TRUE; client_destroy(client, reason); } void client_destroy_internal_failure(struct client *client) { client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR, "Internal login failure. " "Refer to server log for more information."); client_destroy(client, t_strdup_printf( "Internal login failure (pid=%s id=%u)", my_pid, client->master_auth_id)); } void client_ref(struct client *client) { client->refcount++; } bool client_unref(struct client **_client) { struct client *client = *_client; i_assert(client->refcount > 0); if (--client->refcount > 0) return TRUE; *_client = NULL; i_assert(client->destroyed); i_assert(client->login_proxy == NULL); if (client->v.free != NULL) client->v.free(client); if (client->ssl_proxy != NULL) ssl_proxy_free(&client->ssl_proxy); if (client->input != NULL) i_stream_unref(&client->input); if (client->output != NULL) o_stream_unref(&client->output); i_free(client->proxy_user); i_free(client->proxy_master_user); i_free(client->virtual_user); i_free(client->virtual_user_orig); i_free(client->virtual_auth_user); i_free(client->auth_mech_name); i_free(client->master_data_prefix); pool_unref(&client->pool); i_assert(clients_count > 0); clients_count--; master_service_client_connection_destroyed(master_service); login_refresh_proctitle(); return FALSE; } void client_common_default_free(struct client *client ATTR_UNUSED) { } void client_destroy_oldest(void) { struct client *client; if (last_client == NULL) { /* we have no clients */ return; } /* destroy the last client that hasn't successfully authenticated yet. this is usually the last client, but don't kill it if it's just waiting for master to finish its job. */ for (client = last_client; client != NULL; client = client->prev) { if (client->master_tag == 0) break; } if (client == NULL) client = last_client; client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, "Connection queue full"); client_destroy(client, "Disconnected: Connection queue full"); } void clients_destroy_all_reason(const char *reason) { struct client *client, *next; for (client = clients; client != NULL; client = next) { next = client->next; client_notify_disconnect(client, CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, reason); client_destroy(client, reason); } } void clients_destroy_all(void) { clients_destroy_all_reason("Disconnected: Shutting down"); } static void client_start_tls(struct client *client) { int fd_ssl; client_ref(client); if (!client_unref(&client) || client->destroyed) return; fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool, client->set, client->ssl_set, &client->ssl_proxy); if (fd_ssl == -1) { client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR, "TLS initialization failed."); client_destroy(client, "Disconnected: TLS initialization failed."); return; } ssl_proxy_set_client(client->ssl_proxy, client); ssl_proxy_start(client->ssl_proxy); client->starttls = TRUE; client->tls = TRUE; client->secured = TRUE; login_refresh_proctitle(); client->fd = fd_ssl; client->io = io_add(client->fd, IO_READ, client_input, client); i_stream_unref(&client->input); o_stream_unref(&client->output); client_open_streams(client); client->v.starttls(client); } static int client_output_starttls(struct client *client) { int ret; if ((ret = o_stream_flush(client->output)) < 0) { client_destroy(client, "Disconnected"); return 1; } if (ret > 0) { o_stream_unset_flush_callback(client->output); client_start_tls(client); } return 1; } void client_cmd_starttls(struct client *client) { if (client->tls) { client->v.notify_starttls(client, FALSE, "TLS is already active."); return; } if (!client_is_tls_enabled(client)) { client->v.notify_starttls(client, FALSE, "TLS support isn't enabled."); return; } /* remove input handler, SSL proxy gives us a new fd. we also have to remove it in case we have to wait for buffer to be flushed */ if (client->io != NULL) io_remove(&client->io); client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now."); /* uncork the old fd */ o_stream_uncork(client->output); if (o_stream_flush(client->output) <= 0) { /* the buffer has to be flushed */ o_stream_set_flush_pending(client->output, TRUE); o_stream_set_flush_callback(client->output, client_output_starttls, client); } else { client_start_tls(client); } } unsigned int clients_get_count(void) { return clients_count; } void client_add_forward_field(struct client *client, const char *key, const char *value) { if (client->forward_fields == NULL) client->forward_fields = str_new(client->preproxy_pool, 32); else str_append_c(client->forward_fields, '\t'); /* prefixing is done by auth process */ str_append_tabescaped(client->forward_fields, key); str_append_c(client->forward_fields, '='); str_append_tabescaped(client->forward_fields, value); } const char *client_get_session_id(struct client *client) { buffer_t *buf, *base64_buf; struct timeval tv; uint64_t timestamp; unsigned int i; if (client->session_id != NULL) return client->session_id; buf = buffer_create_dynamic(pool_datastack_create(), 24); base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2); if (gettimeofday(&tv, NULL) < 0) i_fatal("gettimeofday(): %m"); timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL; /* add lowest 48 bits of the timestamp. this gives us a bit less than 9 years until it wraps */ for (i = 0; i < 48; i += 8) buffer_append_c(buf, (timestamp >> i) & 0xff); buffer_append_c(buf, client->remote_port & 0xff); buffer_append_c(buf, (client->remote_port >> 8) & 0xff); #ifdef HAVE_IPV6 if (IPADDR_IS_V6(&client->ip)) buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6)); else #endif buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4)); base64_encode(buf->data, buf->used, base64_buf); client->session_id = p_strdup(client->pool, str_c(base64_buf)); return client->session_id; } static struct var_expand_table login_var_expand_empty_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 's', NULL, "service" }, { 'h', NULL, "home" }, { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 'm', NULL, "mech" }, { 'a', NULL, "lport" }, { 'b', NULL, "rport" }, { 'c', NULL, "secured" }, { 'k', NULL, "ssl_security" }, { 'e', NULL, "mail_pid" }, { '\0', NULL, "session" }, { '\0', NULL, "real_lip" }, { '\0', NULL, "real_rip" }, { '\0', NULL, "real_lport" }, { '\0', NULL, "real_rport" }, { '\0', NULL, "orig_user" }, { '\0', NULL, "orig_username" }, { '\0', NULL, "orig_domain" }, { '\0', NULL, "auth_user" }, { '\0', NULL, "auth_username" }, { '\0', NULL, "auth_domain" }, { '\0', NULL, "listener" }, { '\0', NULL, "local_name" }, { '\0', NULL, NULL } }; static void get_var_expand_users(struct var_expand_table *tab, const char *user) { unsigned int i; tab[0].value = user; tab[1].value = t_strcut(user, '@'); tab[2].value = strchr(user, '@'); if (tab[2].value != NULL) tab[2].value++; for (i = 0; i < 3; i++) tab[i].value = str_sanitize(tab[i].value, 80); } static const struct var_expand_table * get_var_expand_table(struct client *client) { struct var_expand_table *tab; tab = t_malloc(sizeof(login_var_expand_empty_tab)); memcpy(tab, login_var_expand_empty_tab, sizeof(login_var_expand_empty_tab)); if (client->virtual_user != NULL) get_var_expand_users(tab, client->virtual_user); tab[3].value = login_binary->protocol; tab[4].value = getenv("HOME"); tab[5].value = net_ip2addr(&client->local_ip); tab[6].value = net_ip2addr(&client->ip); tab[7].value = my_pid; tab[8].value = client->auth_mech_name == NULL ? NULL : str_sanitize(client->auth_mech_name, MAX_MECH_NAME); tab[9].value = dec2str(client->local_port); tab[10].value = dec2str(client->remote_port); if (!client->tls) { tab[11].value = client->secured ? "secured" : NULL; tab[12].value = ""; } else { const char *ssl_state = ssl_proxy_is_handshaked(client->ssl_proxy) ? "TLS" : "TLS handshaking"; const char *ssl_error = ssl_proxy_get_last_error(client->ssl_proxy); tab[11].value = ssl_error == NULL ? ssl_state : t_strdup_printf("%s: %s", ssl_state, ssl_error); tab[12].value = ssl_proxy_get_security_string(client->ssl_proxy); } tab[13].value = client->mail_pid == 0 ? "" : dec2str(client->mail_pid); tab[14].value = client_get_session_id(client); tab[15].value = net_ip2addr(&client->real_local_ip); tab[16].value = net_ip2addr(&client->real_remote_ip); tab[17].value = dec2str(client->real_local_port); tab[18].value = dec2str(client->real_remote_port); if (client->virtual_user_orig != NULL) get_var_expand_users(tab+19, client->virtual_user_orig); else { tab[19].value = tab[0].value; tab[20].value = tab[1].value; tab[21].value = tab[2].value; } if (client->virtual_auth_user != NULL) get_var_expand_users(tab+22, client->virtual_auth_user); else { tab[22].value = tab[19].value; tab[23].value = tab[20].value; tab[24].value = tab[21].value; } tab[25].value = client->listener_name; tab[26].value = str_sanitize(client->local_name, 256); return tab; } static bool have_username_key(const char *str) { char key; for (; *str != '\0'; str++) { if (str[0] == '%' && str[1] != '\0') { str++; key = var_get_key(str); if (key == 'u' || key == 'n') return TRUE; } } return FALSE; } static const char * client_var_expand_func_passdb(const char *data, void *context) { struct client *client = context; const char *field_name = data; unsigned int i; size_t field_name_len; if (client->auth_passdb_args == NULL) return NULL; field_name_len = strlen(field_name); for (i = 0; client->auth_passdb_args[i] != NULL; i++) { if (strncmp(client->auth_passdb_args[i], field_name, field_name_len) == 0 && client->auth_passdb_args[i][field_name_len] == '=') return client->auth_passdb_args[i] + field_name_len+1; } return NULL; } static const char * client_get_log_str(struct client *client, const char *msg) { static const struct var_expand_table static_tab[3] = { { 's', NULL, NULL }, { '$', NULL, NULL }, { '\0', NULL, NULL } }; static const struct var_expand_func_table func_table[] = { { "passdb", client_var_expand_func_passdb }, { NULL, NULL } }; const struct var_expand_table *var_expand_table; struct var_expand_table *tab; char *const *e; string_t *str, *str2; unsigned int pos; var_expand_table = get_var_expand_table(client); tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); str = t_str_new(256); str2 = t_str_new(128); for (e = client->set->log_format_elements_split; *e != NULL; e++) { pos = str_len(str); var_expand_with_funcs(str, *e, var_expand_table, func_table, client); if (have_username_key(*e)) { /* username is added even if it's empty */ } else { str_truncate(str2, 0); var_expand(str2, *e, login_var_expand_empty_tab); if (strcmp(str_c(str)+pos, str_c(str2)) == 0) { /* empty %variables, don't add */ str_truncate(str, pos); continue; } } if (str_len(str) > 0) str_append(str, ", "); } if (str_len(str) > 0) str_truncate(str, str_len(str)-2); tab[0].value = t_strdup(str_c(str)); tab[1].value = msg; str_truncate(str, 0); var_expand(str, client->set->login_log_format, tab); return str_c(str); } void client_log(struct client *client, const char *msg) { T_BEGIN { i_info("%s", client_get_log_str(client, msg)); } T_END; } void client_log_err(struct client *client, const char *msg) { T_BEGIN { i_error("%s", client_get_log_str(client, msg)); } T_END; } void client_log_warn(struct client *client, const char *msg) { T_BEGIN { i_warning("%s", client_get_log_str(client, msg)); } T_END; } bool client_is_tls_enabled(struct client *client) { return ssl_initialized && strcmp(client->ssl_set->ssl, "no") != 0; } const char *client_get_extra_disconnect_reason(struct client *client) { unsigned int auth_secs = client->auth_first_started == 0 ? 0 : ioloop_time - client->auth_first_started; if (client->set->auth_ssl_require_client_cert && client->ssl_proxy != NULL) { if (ssl_proxy_has_broken_client_cert(client->ssl_proxy)) return "(client sent an invalid cert)"; if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy)) return "(client didn't send a cert)"; } if (!client->notified_auth_ready) return t_strdup_printf( "(disconnected before auth was ready, waited %u secs)", (unsigned int)(ioloop_time - client->created)); if (client->auth_attempts == 0) { if (!client->banner_sent) { /* disconnected by a plugin */ return ""; } return t_strdup_printf("(no auth attempts in %u secs)", (unsigned int)(ioloop_time - client->created)); } /* some auth attempts without SSL/TLS */ if (client->auth_tried_disabled_plaintext) return "(tried to use disallowed plaintext auth)"; if (client->set->auth_ssl_require_client_cert && client->ssl_proxy == NULL) return "(cert required, client didn't start TLS)"; if (client->auth_tried_unsupported_mech) return "(tried to use unsupported auth mechanism)"; if (client->auth_waiting && client->auth_attempts == 1) { return t_strdup_printf("(client didn't finish SASL auth, " "waited %u secs)", auth_secs); } if (client->auth_request != NULL && client->auth_attempts == 1) { return t_strdup_printf("(disconnected while authenticating, " "waited %u secs)", auth_secs); } if (client->authenticating && client->auth_attempts == 1) { return t_strdup_printf("(disconnected while finishing login, " "waited %u secs)", auth_secs); } if (client->auth_try_aborted && client->auth_attempts == 1) return "(aborted authentication)"; if (client->auth_process_comm_fail) return "(auth process communication failure)"; if (client->proxy_auth_failed) return "(proxy dest auth failed)"; if (client->auth_successes > 0) { return t_strdup_printf("(internal failure, %u successful auths)", client->auth_successes); } if (client->auth_user_disabled) return "(user disabled)"; if (client->auth_pass_expired) return "(password expired)"; return t_strdup_printf("(auth failed, %u attempts in %u secs)", client->auth_attempts, auth_secs); } void client_notify_disconnect(struct client *client, enum client_disconnect_reason reason, const char *text) { if (!client->notified_disconnect) { if (client->v.notify_disconnect != NULL) client->v.notify_disconnect(client, reason, text); client->notified_disconnect = TRUE; } } void client_notify_auth_ready(struct client *client) { if (!client->notified_auth_ready) { if (client->v.notify_auth_ready != NULL) client->v.notify_auth_ready(client); client->notified_auth_ready = TRUE; } } void client_notify_status(struct client *client, bool bad, const char *text) { if (client->v.notify_status != NULL) client->v.notify_status(client, bad, text); } void client_common_send_raw_data(struct client *client, const void *data, size_t size) { ssize_t ret; ret = o_stream_send(client->output, data, size); if (ret < 0 || (size_t)ret != size) { /* either disconnection or buffer full. in either case we want this connection destroyed. however destroying it here might break things if client is still tried to be accessed without being referenced.. */ i_stream_close(client->input); } } void client_send_raw_data(struct client *client, const void *data, size_t size) { /* FIXME: NULL check is only for backwards compatibility - remove */ if (client->v.send_raw_data != NULL) client->v.send_raw_data(client, data, size); else client_common_send_raw_data(client, data, size); } void client_send_raw(struct client *client, const char *data) { client_send_raw_data(client, data, strlen(data)); } bool client_read(struct client *client) { switch (i_stream_read(client->input)) { case -2: /* buffer full */ client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, "Input buffer full, aborting"); client_destroy(client, "Disconnected: Input buffer full"); return FALSE; case -1: /* disconnected */ client_destroy(client, "Disconnected"); return FALSE; case 0: /* nothing new read */ return TRUE; default: /* something was read */ return TRUE; } } void client_input(struct client *client) { client->v.input(client); } void client_common_init(void) { i_array_init(&module_hooks, 32); } void client_common_deinit(void) { array_free(&module_hooks); } dovecot-2.2.33.2/src/login-common/access-lookup.h0000644000175000017500000000052313123174404016432 00000000000000#ifndef ACCESS_LOOKUP_H #define ACCESS_LOOKUP_H typedef void access_lookup_callback_t(bool success, void *context); struct access_lookup * access_lookup(const char *path, int client_fd, const char *daemon_name, access_lookup_callback_t *callback, void *context); void access_lookup_destroy(struct access_lookup **lookup); #endif dovecot-2.2.33.2/src/login-common/login-common.h0000644000175000017500000000406513123174404016265 00000000000000#ifndef LOGIN_COMMON_H #define LOGIN_COMMON_H #include "lib.h" #include "net.h" #include "login-settings.h" /* Used only for string sanitization */ #define MAX_MECH_NAME 64 #define AUTH_FAILED_MSG "Authentication failed." #define AUTH_TEMP_FAILED_MSG "Temporary authentication failure." #define AUTH_PLAINTEXT_DISABLED_MSG \ "Plaintext authentication disallowed on non-secure (SSL/TLS) connections." #define LOGIN_DEFAULT_SOCKET "login" #define LOGIN_TOKEN_DEFAULT_SOCKET "tokenlogin" struct login_binary { /* e.g. imap, pop3 */ const char *protocol; /* e.g. imap-login, pop3-login */ const char *process_name; /* e.g. 143, 110 */ in_port_t default_port; /* e.g. 993, 995. if there is no ssl port, use 0. */ in_port_t default_ssl_port; /* if value is NULL, LOGIN_DEFAULT_SOCKET is used as the default */ const char *default_login_socket; const struct client_vfuncs *client_vfuncs; void (*preinit)(void); void (*init)(void); void (*deinit)(void); bool sasl_support_final_reply; }; struct login_module_register { unsigned int id; }; extern struct login_module_register login_module_register; extern const struct login_binary *login_binary; extern struct auth_client *auth_client; extern struct master_auth *master_auth; extern bool closing_down, login_debug; extern struct anvil_client *anvil; extern const char *login_rawlog_dir; extern unsigned int initial_service_count; /* NULL-terminated array of all alt_usernames seen so far. Existing fields are never removed. */ extern ARRAY_TYPE(string) global_alt_usernames; extern const struct login_settings *global_login_settings; extern const struct master_service_ssl_settings *global_ssl_settings; extern void **global_other_settings; extern const struct ip_addr *login_source_ips; extern unsigned int login_source_ips_idx, login_source_ips_count; void login_refresh_proctitle(void); void login_client_destroyed(void); /* Call to guarantee that the "anvil" global variable is initialized. */ void login_anvil_init(void); int login_binary_run(const struct login_binary *binary, int argc, char *argv[]); #endif dovecot-2.2.33.2/src/login-common/sasl-server.c0000644000175000017500000003036713165463624016147 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "str.h" #include "base64.h" #include "buffer.h" #include "hex-binary.h" #include "ioloop.h" #include "istream.h" #include "write-full.h" #include "strescape.h" #include "str-sanitize.h" #include "anvil-client.h" #include "auth-client.h" #include "ssl-proxy.h" #include "master-service.h" #include "master-service-ssl-settings.h" #include "master-interface.h" #include "master-auth.h" #include "client-common.h" #include #define ERR_TOO_MANY_USERIP_CONNECTIONS \ "Maximum number of connections from user+IP exceeded " \ "(mail_max_userip_connections=%u)" struct anvil_request { struct client *client; unsigned int auth_pid, auth_id; unsigned char cookie[MASTER_AUTH_COOKIE_SIZE]; }; const struct auth_mech_desc * sasl_server_get_advertised_mechs(struct client *client, unsigned int *count_r) { const struct auth_mech_desc *mech; struct auth_mech_desc *ret_mech; unsigned int i, j, count; mech = auth_client_get_available_mechs(auth_client, &count); if (count == 0 || (!client->secured && strcmp(client->ssl_set->ssl, "required") == 0)) { *count_r = 0; return NULL; } ret_mech = t_new(struct auth_mech_desc, count); for (i = j = 0; i < count; i++) { /* a) transport is secured b) auth mechanism isn't plaintext c) we allow insecure authentication */ if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && (client->secured || !client->set->disable_plaintext_auth || (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) ret_mech[j++] = mech[i]; } *count_r = j; return ret_mech; } static enum auth_request_flags client_get_auth_flags(struct client *client) { enum auth_request_flags auth_flags = 0; if (client->ssl_proxy != NULL && ssl_proxy_has_valid_client_cert(client->ssl_proxy)) auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT; if (client->secured) auth_flags |= AUTH_REQUEST_FLAG_SECURED; if (client->trusted) { /* e.g. webmail */ auth_flags |= AUTH_REQUEST_FLAG_NO_PENALTY; } if (login_binary->sasl_support_final_reply) auth_flags |= AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP; return auth_flags; } static void ATTR_NULL(3, 4) call_client_callback(struct client *client, enum sasl_server_reply reply, const char *data, const char *const *args) { sasl_server_callback_t *sasl_callback; i_assert(reply != SASL_SERVER_REPLY_CONTINUE); sasl_callback = client->sasl_callback; client->sasl_callback = NULL; sasl_callback(client, reply, data, args); /* NOTE: client may be destroyed now */ } static void master_auth_callback(const struct master_auth_reply *reply, void *context) { struct client *client = context; enum sasl_server_reply sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED; const char *data = NULL; client->master_tag = 0; client->authenticating = FALSE; if (reply != NULL) { switch (reply->status) { case MASTER_AUTH_STATUS_OK: sasl_reply = SASL_SERVER_REPLY_SUCCESS; break; case MASTER_AUTH_STATUS_INTERNAL_ERROR: sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED; break; } client->mail_pid = reply->mail_pid; } else { auth_client_send_cancel(auth_client, client->master_auth_id); } call_client_callback(client, sasl_reply, data, NULL); } static void master_send_request(struct anvil_request *anvil_request) { struct client *client = anvil_request->client; struct master_auth_request_params params; struct master_auth_request req; const unsigned char *data; size_t size; buffer_t *buf; const char *session_id = client_get_session_id(client); i_zero(&req); req.auth_pid = anvil_request->auth_pid; req.auth_id = anvil_request->auth_id; req.local_ip = client->local_ip; req.remote_ip = client->ip; req.client_pid = getpid(); if (client->ssl_proxy != NULL && ssl_proxy_get_compression(client->ssl_proxy)) req.flags |= MAIL_AUTH_REQUEST_FLAG_TLS_COMPRESSION; memcpy(req.cookie, anvil_request->cookie, sizeof(req.cookie)); buf = buffer_create_dynamic(pool_datastack_create(), 256); /* session ID */ buffer_append(buf, session_id, strlen(session_id)+1); /* protocol specific data (e.g. IMAP tag) */ buffer_append(buf, client->master_data_prefix, client->master_data_prefix_len); /* buffered client input */ data = i_stream_get_data(client->input, &size); buffer_append(buf, data, size); req.data_size = buf->used; client->auth_finished = ioloop_time; client->master_auth_id = req.auth_id; i_zero(¶ms); params.client_fd = client->fd; params.socket_path = client->postlogin_socket_path; params.request = req; params.data = buf->data; master_auth_request_full(master_auth, ¶ms, master_auth_callback, client, &client->master_tag); } static void ATTR_NULL(1) anvil_lookup_callback(const char *reply, void *context) { struct anvil_request *req = context; struct client *client = req->client; const struct login_settings *set = client->set; const char *errmsg; unsigned int conn_count; conn_count = 0; if (reply != NULL && str_to_uint(reply, &conn_count) < 0) i_fatal("Received invalid reply from anvil: %s", reply); /* reply=NULL if we didn't need to do anvil lookup, or if the anvil lookup failed. allow failed anvil lookups in. */ if (reply == NULL || conn_count < set->mail_max_userip_connections) master_send_request(req); else { client->authenticating = FALSE; auth_client_send_cancel(auth_client, req->auth_id); errmsg = t_strdup_printf(ERR_TOO_MANY_USERIP_CONNECTIONS, set->mail_max_userip_connections); call_client_callback(client, SASL_SERVER_REPLY_MASTER_FAILED, errmsg, NULL); } i_free(req); } static void anvil_check_too_many_connections(struct client *client, struct auth_client_request *request) { struct anvil_request *req; const char *query, *cookie; buffer_t buf; req = i_new(struct anvil_request, 1); req->client = client; req->auth_pid = auth_client_request_get_server_pid(request); req->auth_id = auth_client_request_get_id(request); buffer_create_from_data(&buf, req->cookie, sizeof(req->cookie)); cookie = auth_client_request_get_cookie(request); if (strlen(cookie) == MASTER_AUTH_COOKIE_SIZE*2) (void)hex_to_binary(cookie, &buf); if (client->virtual_user == NULL || client->set->mail_max_userip_connections == 0) { anvil_lookup_callback(NULL, req); return; } query = t_strconcat("LOOKUP\t", login_binary->protocol, "/", net_ip2addr(&client->ip), "/", str_tabescape(client->virtual_user), NULL); anvil_client_query(anvil, query, anvil_lookup_callback, req); } static void authenticate_callback(struct auth_client_request *request, enum auth_request_status status, const char *data_base64, const char *const *args, void *context) { struct client *client = context; unsigned int i; bool nologin; if (!client->authenticating) { /* client aborted */ i_assert(status < 0); return; } client->auth_waiting = FALSE; i_assert(client->auth_request == request); switch (status) { case AUTH_REQUEST_STATUS_CONTINUE: /* continue */ client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE, data_base64, NULL); break; case AUTH_REQUEST_STATUS_OK: client->auth_request = NULL; client->auth_successes++; client->auth_passdb_args = p_strarray_dup(client->pool, args); client->postlogin_socket_path = NULL; nologin = FALSE; for (i = 0; args[i] != NULL; i++) { if (strncmp(args[i], "user=", 5) == 0) { i_free(client->virtual_user); i_free_and_null(client->virtual_user_orig); i_free_and_null(client->virtual_auth_user); client->virtual_user = i_strdup(args[i] + 5); } else if (strncmp(args[i], "original_user=", 14) == 0) { i_free(client->virtual_user_orig); client->virtual_user_orig = i_strdup(args[i] + 14); } else if (strncmp(args[i], "auth_user=", 10) == 0) { i_free(client->virtual_auth_user); client->virtual_auth_user = i_strdup(args[i] + 10); } else if (strncmp(args[i], "postlogin_socket=", 17) == 0) { client->postlogin_socket_path = p_strdup(client->pool, args[i] + 17); } else if (strcmp(args[i], "nologin") == 0 || strcmp(args[i], "proxy") == 0) { /* user can't login */ nologin = TRUE; } else if (strncmp(args[i], "resp=", 5) == 0 && login_binary->sasl_support_final_reply) { client->sasl_final_resp = p_strdup(client->pool, args[i] + 5); } } if (nologin) { client->authenticating = FALSE; call_client_callback(client, SASL_SERVER_REPLY_SUCCESS, NULL, args); } else { anvil_check_too_many_connections(client, request); } break; case AUTH_REQUEST_STATUS_INTERNAL_FAIL: client->auth_process_comm_fail = TRUE; /* fall through */ case AUTH_REQUEST_STATUS_FAIL: case AUTH_REQUEST_STATUS_ABORT: client->auth_request = NULL; if (args != NULL) { /* parse our username if it's there */ for (i = 0; args[i] != NULL; i++) { if (strncmp(args[i], "user=", 5) == 0) { i_free(client->virtual_user); i_free_and_null(client->virtual_user_orig); i_free_and_null(client->virtual_auth_user); client->virtual_user = i_strdup(args[i] + 5); } else if (strncmp(args[i], "original_user=", 14) == 0) { i_free(client->virtual_user_orig); client->virtual_user_orig = i_strdup(args[i] + 14); } else if (strncmp(args[i], "auth_user=", 10) == 0) { i_free(client->virtual_auth_user); client->virtual_auth_user = i_strdup(args[i] + 10); } } } client->authenticating = FALSE; call_client_callback(client, SASL_SERVER_REPLY_AUTH_FAILED, NULL, args); break; } } void sasl_server_auth_begin(struct client *client, const char *service, const char *mech_name, const char *initial_resp_base64, sasl_server_callback_t *callback) { struct auth_request_info info; const struct auth_mech_desc *mech; i_assert(auth_client_is_connected(auth_client)); client->auth_attempts++; client->authenticating = TRUE; if (client->auth_first_started == 0) client->auth_first_started = ioloop_time; i_free(client->auth_mech_name); client->auth_mech_name = str_ucase(i_strdup(mech_name)); client->sasl_callback = callback; mech = auth_client_find_mech(auth_client, mech_name); if (mech == NULL) { client->auth_tried_unsupported_mech = TRUE; sasl_server_auth_failed(client, "Unsupported authentication mechanism."); return; } if (!client->secured && client->set->disable_plaintext_auth && (mech->flags & MECH_SEC_PLAINTEXT) != 0) { client->auth_tried_disabled_plaintext = TRUE; sasl_server_auth_failed(client, "Plaintext authentication disabled."); return; } i_zero(&info); info.mech = mech->name; info.service = service; info.session_id = client_get_session_id(client); info.cert_username = client->ssl_proxy == NULL ? NULL : ssl_proxy_get_peer_name(client->ssl_proxy); info.flags = client_get_auth_flags(client); info.local_ip = client->local_ip; info.remote_ip = client->ip; info.local_port = client->local_port; info.local_name = client->local_name; info.remote_port = client->remote_port; info.real_local_ip = client->real_local_ip; info.real_remote_ip = client->real_remote_ip; info.real_local_port = client->real_local_port; info.real_remote_port = client->real_remote_port; if (client->client_id != NULL) info.client_id = str_c(client->client_id); if (client->forward_fields != NULL) info.forward_fields = str_c(client->forward_fields); info.initial_resp_base64 = initial_resp_base64; client->auth_request = auth_client_request_new(auth_client, &info, authenticate_callback, client); } static void ATTR_NULL(2) sasl_server_auth_cancel(struct client *client, const char *reason, enum sasl_server_reply reply) { i_assert(client->authenticating); if (client->set->auth_verbose && reason != NULL) { const char *auth_name = str_sanitize(client->auth_mech_name, MAX_MECH_NAME); client_log(client, t_strdup_printf( "Authenticate %s failed: %s", auth_name, reason)); } client->authenticating = FALSE; if (client->auth_request != NULL) auth_client_request_abort(&client->auth_request); call_client_callback(client, reply, reason, NULL); } void sasl_server_auth_failed(struct client *client, const char *reason) { sasl_server_auth_cancel(client, reason, SASL_SERVER_REPLY_AUTH_FAILED); } void sasl_server_auth_abort(struct client *client) { client->auth_try_aborted = TRUE; sasl_server_auth_cancel(client, NULL, SASL_SERVER_REPLY_AUTH_ABORTED); } dovecot-2.2.33.2/src/login-common/Makefile.am0000644000175000017500000000232113123174404015543 00000000000000noinst_LTLIBRARIES = liblogin.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DMODULEDIR=\""$(moduledir)"\" liblogin_la_SOURCES = \ access-lookup.c \ client-common.c \ client-common-auth.c \ login-proxy.c \ login-proxy-state.c \ login-settings.c \ main.c \ sasl-server.c \ ssl-proxy.c \ ssl-proxy-gnutls.c \ ssl-proxy-openssl.c if BUILD_OPENSSL openssl_obj = ../lib-ssl-iostream/iostream-openssl-common.lo endif liblogin_la_LIBADD = \ $(openssl_obj) \ $(SSL_LIBS) headers = \ access-lookup.h \ client-common.h \ login-common.h \ login-proxy.h \ login-proxy-state.h \ login-settings.h \ sasl-server.h \ ssl-proxy.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) pkglib_LTLIBRARIES = libdovecot-login.la libdovecot_login_la_SOURCES = libdovecot_login_la_LIBADD = liblogin.la ../lib-ssl-iostream/libssl_iostream.la ../lib-dovecot/libdovecot.la $(SSL_LIBS) libdovecot_login_la_DEPENDENCIES = liblogin.la libdovecot_login_la_LDFLAGS = -export-dynamic dovecot-2.2.33.2/src/dns/0002755000175000017500000000000013172375612011771 500000000000000dovecot-2.2.33.2/src/dns/Makefile.in0000644000175000017500000005330713172375572013771 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = dns-client$(EXEEXT) subdir = src/dns ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_dns_client_OBJECTS = dns-client.$(OBJEXT) \ dns-client-settings.$(OBJEXT) dns_client_OBJECTS = $(am_dns_client_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dns_client_SOURCES) DIST_SOURCES = $(dns_client_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings dns_client_LDADD = $(LIBDOVECOT) dns_client_DEPENDENCIES = $(LIBDOVECOT_DEPS) dns_client_SOURCES = \ dns-client.c \ dns-client-settings.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dns/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dns/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list dns-client$(EXEEXT): $(dns_client_OBJECTS) $(dns_client_DEPENDENCIES) $(EXTRA_dns_client_DEPENDENCIES) @rm -f dns-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dns_client_OBJECTS) $(dns_client_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns-client-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns-client.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/dns/dns-client.c0000644000175000017500000000726513165463624014130 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "restrict-access.h" #include "master-service.h" #include struct dns_client { int fd; struct istream *input; struct ostream *output; struct io *io; }; #define MAX_INBUF_SIZE 1024 #define MAX_OUTBUF_SIZE (1024*64) #define INPUT_TIMEOUT_MSECS (1000*10) static struct dns_client *dns_client = NULL; static void dns_client_destroy(struct dns_client **client); static int dns_client_input_line(struct dns_client *client, const char *line) { struct ip_addr *ips, ip; const char *name; unsigned int i, ips_count; int ret; if (strncmp(line, "IP\t", 3) == 0) { ret = net_gethostbyname(line + 3, &ips, &ips_count); if (ret == 0 && ips_count == 0) { /* shouldn't happen, but fix it anyway.. */ ret = EAI_NONAME; } if (ret != 0) { o_stream_nsend_str(client->output, t_strdup_printf("%d\n", ret)); } else { o_stream_nsend_str(client->output, t_strdup_printf("0 %u\n", ips_count)); for (i = 0; i < ips_count; i++) { o_stream_nsend_str(client->output, t_strconcat( net_ip2addr(&ips[i]), "\n", NULL)); } } } else if (strncmp(line, "NAME\t", 5) == 0) { if (net_addr2ip(line+5, &ip) < 0) o_stream_nsend_str(client->output, "-1\n"); else if ((ret = net_gethostbyaddr(&ip, &name)) != 0) { o_stream_nsend_str(client->output, t_strdup_printf("%d\n", ret)); } else { o_stream_nsend_str(client->output, t_strdup_printf("0 %s\n", name)); } } else if (strcmp(line, "QUIT") == 0) { return -1; } else { o_stream_nsend_str(client->output, "Unknown command\n"); } if (client->output->overflow) return -1; return 0; } static void dns_client_input(struct dns_client *client) { const char *line; int ret = 0; o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { if (dns_client_input_line(client, line) < 0) { ret = -1; break; } } o_stream_uncork(client->output); if (client->input->eof || client->input->stream_errno != 0 || ret < 0) dns_client_destroy(&client); } static struct dns_client *dns_client_create(int fd) { struct dns_client *client; client = i_new(struct dns_client, 1); client->fd = fd; client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE); o_stream_set_no_error_handling(client->output, TRUE); client->io = io_add(fd, IO_READ, dns_client_input, client); return client; } static void dns_client_destroy(struct dns_client **_client) { struct dns_client *client = *_client; *_client = NULL; io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); if (close(client->fd) < 0) i_error("close() failed: %m"); i_free(client); dns_client = NULL; master_service_client_connection_destroyed(master_service); } static void client_connected(struct master_service_connection *conn) { if (dns_client != NULL) { i_error("dns-client must be configured with client_limit=1"); return; } master_service_client_connection_accept(conn); dns_client = dns_client_create(conn->fd); } int main(int argc, char *argv[]) { master_service = master_service_init("dns-client", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; master_service_init_log(master_service, "dns-client: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); master_service_init_finish(master_service); master_service_run(master_service, client_connected); if (dns_client != NULL) dns_client_destroy(&dns_client); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/dns/dns-client-settings.c0000644000175000017500000000223013123174404015736 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings dns_client_unix_listeners_array[] = { { "dns-client", 0666, "", "" } }; static struct file_listener_settings *dns_client_unix_listeners[] = { &dns_client_unix_listeners_array[0] }; static buffer_t dns_client_unix_listeners_buf = { dns_client_unix_listeners, sizeof(dns_client_unix_listeners), { NULL, } }; /* */ struct service_settings dns_client_service_settings = { .name = "dns_client", .protocol = "", .type = "", .executable = "dns-client", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dns_client_unix_listeners_buf, sizeof(dns_client_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; dovecot-2.2.33.2/src/dns/Makefile.am0000644000175000017500000000050513165463624013746 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = dns-client AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings dns_client_LDADD = $(LIBDOVECOT) dns_client_DEPENDENCIES = $(LIBDOVECOT_DEPS) dns_client_SOURCES = \ dns-client.c \ dns-client-settings.c dovecot-2.2.33.2/src/lib-imap-client/0002755000175000017500000000000013172375611014152 500000000000000dovecot-2.2.33.2/src/lib-imap-client/imapc-msgmap.h0000644000175000017500000000123613123174404016607 00000000000000#ifndef IMAPC_MSGMAP_H #define IMAPC_MSGMAP_H struct imapc_msgmap *imapc_msgmap_init(void); void imapc_msgmap_deinit(struct imapc_msgmap **msgmap); uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap); uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap); uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq); bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap, uint32_t uid, uint32_t *rseq_r); void imapc_msgmap_append(struct imapc_msgmap *msgmap, uint32_t rseq, uint32_t uid); void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq); void imapc_msgmap_reset(struct imapc_msgmap *msgmap); #endif dovecot-2.2.33.2/src/lib-imap-client/Makefile.in0000644000175000017500000006010113172375573016142 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-imap-client ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libimap_client_la_LIBADD = am_libimap_client_la_OBJECTS = imapc-client.lo imapc-connection.lo \ imapc-msgmap.lo libimap_client_la_OBJECTS = $(am_libimap_client_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-imapc-client$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_imapc_client_OBJECTS = test-imapc-client.$(OBJEXT) test_imapc_client_OBJECTS = $(am_test_imapc_client_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(test_deps) $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libimap_client_la_SOURCES) $(test_imapc_client_SOURCES) DIST_SOURCES = $(libimap_client_la_SOURCES) \ $(test_imapc_client_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libimap_client.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap libimap_client_la_SOURCES = \ imapc-client.c \ imapc-connection.c \ imapc-msgmap.c headers = \ imapc-client.h \ imapc-client-private.h \ imapc-connection.h \ imapc-msgmap.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-imapc-client test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-sasl/libsasl.la \ ../lib-imap/libimap.la \ ../lib-mail/libmail.la \ ../lib-charset/libcharset.la \ ../lib-dns/libdns.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_libs = \ $(test_deps) \ $(MODULE_LIBS) test_imapc_client_SOURCES = test-imapc-client.c test_imapc_client_LDADD = $(test_libs) test_imapc_client_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-imap-client/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-imap-client/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libimap_client.la: $(libimap_client_la_OBJECTS) $(libimap_client_la_DEPENDENCIES) $(EXTRA_libimap_client_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libimap_client_la_OBJECTS) $(libimap_client_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-imapc-client$(EXEEXT): $(test_imapc_client_OBJECTS) $(test_imapc_client_DEPENDENCIES) $(EXTRA_test_imapc_client_DEPENDENCIES) @rm -f test-imapc-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_imapc_client_OBJECTS) $(test_imapc_client_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imapc-msgmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-imapc-client.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-imap-client/imapc-msgmap.c0000644000175000017500000000340013165463624016610 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "imapc-msgmap.h" struct imapc_msgmap { ARRAY_TYPE(uint32_t) uids; uint32_t uid_next; }; struct imapc_msgmap *imapc_msgmap_init(void) { struct imapc_msgmap *msgmap; msgmap = i_new(struct imapc_msgmap, 1); i_array_init(&msgmap->uids, 128); msgmap->uid_next = 1; return msgmap; } void imapc_msgmap_deinit(struct imapc_msgmap **_msgmap) { struct imapc_msgmap *msgmap = *_msgmap; *_msgmap = NULL; array_free(&msgmap->uids); i_free(msgmap); } uint32_t imapc_msgmap_count(struct imapc_msgmap *msgmap) { return array_count(&msgmap->uids); } uint32_t imapc_msgmap_uidnext(struct imapc_msgmap *msgmap) { return msgmap->uid_next; } uint32_t imapc_msgmap_rseq_to_uid(struct imapc_msgmap *msgmap, uint32_t rseq) { const uint32_t *uidp; uidp = array_idx(&msgmap->uids, rseq-1); return *uidp; } bool imapc_msgmap_uid_to_rseq(struct imapc_msgmap *msgmap, uint32_t uid, uint32_t *rseq_r) { const uint32_t *p, *first; p = array_bsearch(&msgmap->uids, &uid, uint32_cmp); if (p == NULL) { *rseq_r = 0; return FALSE; } first = array_idx(&msgmap->uids, 0); *rseq_r = (p - first) + 1; return TRUE; } void imapc_msgmap_append(struct imapc_msgmap *msgmap, uint32_t rseq, uint32_t uid) { i_assert(rseq == imapc_msgmap_count(msgmap) + 1); i_assert(uid >= msgmap->uid_next); msgmap->uid_next = uid + 1; array_append(&msgmap->uids, &uid, 1); } void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq) { i_assert(rseq > 0); i_assert(rseq <= imapc_msgmap_count(msgmap)); array_delete(&msgmap->uids, rseq-1, 1); } void imapc_msgmap_reset(struct imapc_msgmap *msgmap) { array_clear(&msgmap->uids); msgmap->uid_next = 1; } dovecot-2.2.33.2/src/lib-imap-client/imapc-connection.h0000644000175000017500000000415113123174404017461 00000000000000#ifndef IMAPC_CONNECTION_H #define IMAPC_CONNECTION_H #include "imapc-client.h" /* [THROTTLED] handling behavior */ #define IMAPC_THROTTLE_DEFAULT_INIT_MSECS 50 #define IMAPC_THROTTLE_DEFAULT_MAX_MSECS (16*1000) #define IMAPC_THROTTLE_DEFAULT_SHRINK_MIN_MSECS 500 struct imapc_client; struct imapc_connection; enum imapc_connection_state { /* No connection */ IMAPC_CONNECTION_STATE_DISCONNECTED = 0, /* Trying to connect */ IMAPC_CONNECTION_STATE_CONNECTING, /* Connected, trying to authenticate */ IMAPC_CONNECTION_STATE_AUTHENTICATING, /* Authenticated, ready to accept commands */ IMAPC_CONNECTION_STATE_DONE }; struct imapc_connection * imapc_connection_init(struct imapc_client *client, imapc_command_callback_t *login_callback, void *login_context); void imapc_connection_deinit(struct imapc_connection **conn); void imapc_connection_connect(struct imapc_connection *conn); void imapc_connection_set_no_reconnect(struct imapc_connection *conn); void imapc_connection_disconnect(struct imapc_connection *conn); void imapc_connection_disconnect_full(struct imapc_connection *conn, bool reconnecting); void imapc_connection_try_reconnect(struct imapc_connection *conn, const char *errstr, unsigned int delay_msecs, bool connect_error); void imapc_connection_abort_commands(struct imapc_connection *conn, struct imapc_client_mailbox *only_box, bool keep_retriable) ATTR_NULL(2); void imapc_connection_ioloop_changed(struct imapc_connection *conn); void imapc_connection_input_pending(struct imapc_connection *conn); struct imapc_command * imapc_connection_cmd(struct imapc_connection *conn, imapc_command_callback_t *callback, void *context) ATTR_NULL(3); void imapc_connection_unselect(struct imapc_client_mailbox *box); enum imapc_connection_state imapc_connection_get_state(struct imapc_connection *conn); enum imapc_capability imapc_connection_get_capabilities(struct imapc_connection *conn); struct imapc_client_mailbox * imapc_connection_get_mailbox(struct imapc_connection *conn); void imapc_connection_idle(struct imapc_connection *conn); #endif dovecot-2.2.33.2/src/lib-imap-client/imapc-client-private.h0000644000175000017500000000254613123174404020256 00000000000000#ifndef IMAPC_CLIENT_PRIVATE_H #define IMAPC_CLIENT_PRIVATE_H #include "imapc-client.h" #define IMAPC_CLIENT_IDLE_SEND_DELAY_MSECS 100 struct imapc_client_connection { struct imapc_connection *conn; struct imapc_client *client; struct imapc_client_mailbox *box; }; struct imapc_client { pool_t pool; int refcount; struct imapc_client_settings set; struct ssl_iostream_context *ssl_ctx; imapc_untagged_callback_t *untagged_callback; void *untagged_context; imapc_state_change_callback_t *state_change_callback; void *state_change_context; imapc_command_callback_t *login_callback; void *login_context; ARRAY(struct imapc_client_connection *) conns; bool logging_out; struct ioloop *ioloop; bool stop_on_state_finish; }; struct imapc_client_mailbox { struct imapc_client *client; struct imapc_connection *conn; struct imapc_msgmap *msgmap; struct timeout *to_send_idle; void (*reopen_callback)(void *context); void *reopen_context; void *untagged_box_context; bool reconnect_ok; bool reconnecting; bool closing; }; extern unsigned int imapc_client_cmd_tag_counter; void imapc_client_ref(struct imapc_client *client); void imapc_client_unref(struct imapc_client **client); void imapc_command_set_mailbox(struct imapc_command *cmd, struct imapc_client_mailbox *box); void imapc_client_try_stop(struct imapc_client *client); #endif dovecot-2.2.33.2/src/lib-imap-client/test-imapc-client.c0000644000175000017500000005255413165463624017575 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hostpid.h" #include "net.h" #include "fd-set-nonblock.h" #include "istream.h" #include "ostream.h" #include "ioloop.h" #include "unlink-directory.h" #include "test-common.h" #include "imapc-client-private.h" #include #include #include #include #include #define IMAPC_COMMAND_STATE_INVALID (enum imapc_command_state)-1 typedef void test_server_init_t(void); typedef void test_client_init_t(void); struct test_server { in_port_t port; pid_t pid; int fd_listen, fd; struct istream *input; struct ostream *output; }; static struct ip_addr bind_ip; static struct test_server server; static struct imapc_client *imapc_client; static enum imapc_command_state imapc_login_last_reply; static ARRAY(enum imapc_command_state) imapc_cmd_last_replies; static bool debug = FALSE; static struct imapc_client_settings test_imapc_default_settings = { .host = "127.0.0.1", .username = "testuser", .password = "testpass", .dns_client_socket_path = "", .temp_path_prefix = ".test-tmp/", .rawlog_dir = "", .connect_timeout_msecs = 500, .connect_retry_count = 3, .connect_retry_interval_msecs = 10, .max_idle_time = 10000, }; static int test_open_server_fd(in_port_t *bind_port) { int fd = net_listen(&bind_ip, bind_port, 128); if (debug) i_debug("server listening on %u", *bind_port); if (fd == -1) { i_fatal("listen(%s:%u) failed: %m", net_ip2addr(&bind_ip), *bind_port); } fd_set_nonblock(fd, FALSE); return fd; } static void test_server_wait_connection(struct test_server *server, bool send_banner) { server->fd = net_accept(server->fd_listen, NULL, NULL); i_assert(server->fd >= 0); fd_set_nonblock(server->fd, FALSE); server->input = i_stream_create_fd(server->fd, (size_t)-1, FALSE); server->output = o_stream_create_fd(server->fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(server->output, TRUE); if (send_banner) { o_stream_nsend_str(server->output, "* OK [CAPABILITY IMAP4rev1 UNSELECT QUOTA] ready\r\n"); } } static void test_server_disconnect(struct test_server *server) { if (server->input != NULL) i_stream_unref(&server->input); if (server->output != NULL) o_stream_unref(&server->output); if (server->fd != -1) i_close_fd(&server->fd); } static void test_server_disconnect_and_wait(bool send_banner) { test_server_disconnect(&server); test_server_wait_connection(&server, send_banner); } static void test_server_kill(void) { if (server.pid != (pid_t)-1) { if (kill(server.pid, SIGKILL) < 0) i_fatal("kill(%ld) failed: %m", (long)server.pid); if (waitpid(server.pid, NULL, 0) < 0) i_fatal("waitpid(%ld) failed: %m", (long)server.pid); server.pid = -1; } } static void test_run_client_server( const struct imapc_client_settings *client_set, test_client_init_t *client_test, test_server_init_t *server_test) { struct imapc_client_settings client_set_copy = *client_set; struct ioloop *ioloop; imapc_client_cmd_tag_counter = 0; imapc_login_last_reply = IMAPC_COMMAND_STATE_INVALID; t_array_init(&imapc_cmd_last_replies, 4); i_zero(&server); server.pid = (pid_t)-1; server.fd = -1; server.fd_listen = test_open_server_fd(&server.port); client_set_copy.port = server.port; if (server_test == NULL) i_close_fd(&server.fd_listen); if (mkdir(client_set->temp_path_prefix, 0700) < 0 && errno != EEXIST) i_fatal("mkdir(%s) failed: %m", client_set->temp_path_prefix); if ((server.pid = fork()) == (pid_t)-1) i_fatal("fork() failed: %m"); if (server.pid == 0) { server.pid = (pid_t)-1; hostpid_init(); if (debug) i_debug("server: PID=%s", my_pid); /* child: server */ ioloop = io_loop_create(); if (server_test != NULL) server_test(); test_server_disconnect(&server); io_loop_destroy(&ioloop); /* wait for it to be killed; this way, valgrind will not object to this process going away inelegantly. */ sleep(60); exit(1); } /* parent: client */ usleep(100000); /* wait a little for server setup */ ioloop = io_loop_create(); imapc_client = imapc_client_init(&client_set_copy); client_test(); test_assert(array_count(&imapc_cmd_last_replies) == 0); if (imapc_client != NULL) imapc_client_deinit(&imapc_client); io_loop_destroy(&ioloop); if (server.fd_listen != -1) i_close_fd(&server.fd_listen); test_server_kill(); (void)unlink_directory(client_set->temp_path_prefix, UNLINK_DIRECTORY_FLAG_RMDIR); } static enum imapc_command_state test_imapc_cmd_last_reply_pop(void) { const enum imapc_command_state *replies; enum imapc_command_state reply; unsigned int count; replies = array_get(&imapc_cmd_last_replies, &count); if (count == 0) return IMAPC_COMMAND_STATE_INVALID; reply = replies[0]; array_delete(&imapc_cmd_last_replies, 0, 1); return reply; } static bool test_imapc_cmd_last_reply_expect(enum imapc_command_state state) { if (array_count(&imapc_cmd_last_replies) == 0) imapc_client_run(imapc_client); return test_imapc_cmd_last_reply_pop() == state; } static bool test_imapc_server_expect(const char *expected_line) { const char *line = i_stream_read_next_line(server.input); if (line == NULL) { printf("imapc client disconnected unexpectedly: %s\n", i_stream_get_error(server.input)); return FALSE; } else if (strcmp(line, expected_line) != 0) { printf("imapc client sent '%s' when expecting '%s'\n", line, expected_line); return FALSE; } else { return TRUE; } } static void imapc_login_callback(const struct imapc_command_reply *reply, void *context ATTR_UNUSED) { if (debug) { i_debug("Login reply: %s %s", imapc_command_state_names[reply->state], reply->text_full); } imapc_login_last_reply = reply->state; imapc_client_stop(imapc_client); } static void imapc_command_callback(const struct imapc_command_reply *reply, void *context ATTR_UNUSED) { if (debug) { i_debug("Command reply: %s %s", imapc_command_state_names[reply->state], reply->text_full); } array_append(&imapc_cmd_last_replies, &reply->state, 1); imapc_client_stop(imapc_client); } static void imapc_reopen_callback(void *context) { struct imapc_client_mailbox *box = context; struct imapc_command *cmd; cmd = imapc_client_mailbox_cmd(box, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); imapc_command_send(cmd, "SELECT"); } static void test_imapc_connect_failed_client(void) { imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); /* connection refused & one reconnect */ test_expect_errors(2); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_DISCONNECTED); } static void test_imapc_connect_failed(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc connect failed"); test_run_client_server(&set, test_imapc_connect_failed_client, NULL); test_end(); } static void test_imapc_banner_hangs_client(void) { imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); test_expect_errors(2); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_DISCONNECTED); } static void test_imapc_banner_hangs_server(void) { struct test_server server2 = { .fd_listen = server.fd_listen }; test_server_wait_connection(&server, FALSE); test_server_wait_connection(&server2, FALSE); test_assert(i_stream_read_next_line(server2.input) == NULL); test_server_disconnect(&server2); } static void test_imapc_banner_hangs(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc banner hangs"); test_run_client_server(&set, test_imapc_banner_hangs_client, test_imapc_banner_hangs_server); test_end(); } static void test_imapc_login_hangs_client(void) { imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); test_expect_errors(2); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_DISCONNECTED); } static void test_imapc_login_hangs_server(void) { struct test_server server2 = { .fd_listen = server.fd_listen }; test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); test_server_wait_connection(&server2, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); test_assert(i_stream_read_next_line(server2.input) == NULL); test_server_disconnect(&server2); } static void test_imapc_login_hangs(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc login hangs"); test_run_client_server(&set, test_imapc_login_hangs_client, test_imapc_login_hangs_server); test_end(); } static void test_imapc_reconnect_client(void) { struct imapc_command *cmd; /* login to server */ imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); imapc_client_run(imapc_client); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_OK); imapc_login_last_reply = IMAPC_COMMAND_STATE_INVALID; /* disconnect */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_send(cmd, "DISCONNECT"); test_expect_error_string("reconnecting"); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(test_imapc_cmd_last_reply_pop() == IMAPC_COMMAND_STATE_DISCONNECTED); /* we should be reconnected now. try a command. */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_send(cmd, "NOOP"); imapc_client_run(imapc_client); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_INVALID); test_assert(test_imapc_cmd_last_reply_pop() == IMAPC_COMMAND_STATE_OK); } static void test_imapc_reconnect_server(void) { test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "1 OK \r\n"); test_assert(test_imapc_server_expect("2 DISCONNECT")); test_server_disconnect_and_wait(TRUE); test_assert(test_imapc_server_expect("4 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "4 OK \r\n"); test_assert(test_imapc_server_expect("3 NOOP")); o_stream_nsend_str(server.output, "3 OK \r\n"); test_assert(i_stream_read_next_line(server.input) == NULL); } static void test_imapc_reconnect(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc reconnect"); test_run_client_server(&set, test_imapc_reconnect_client, test_imapc_reconnect_server); test_end(); } static void test_imapc_reconnect_resend_cmds_client(void) { struct imapc_command *cmd; /* login to server */ imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); imapc_client_run(imapc_client); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_OK); imapc_login_last_reply = IMAPC_COMMAND_STATE_INVALID; /* send two commands */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "RETRY1"); cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "RETRY2"); /* disconnect & reconnect automatically */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_send(cmd, "DISCONNECT"); test_expect_error_string("reconnecting"); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_DISCONNECTED)); /* continue reconnection */ test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_OK)); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_OK)); } static void test_imapc_reconnect_resend_cmds_server(void) { test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "1 OK \r\n"); test_assert(test_imapc_server_expect("2 RETRY1")); test_assert(test_imapc_server_expect("3 RETRY2")); test_assert(test_imapc_server_expect("4 DISCONNECT")); test_server_disconnect_and_wait(TRUE); test_assert(test_imapc_server_expect("5 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "5 OK \r\n"); test_assert(test_imapc_server_expect("2 RETRY1")); o_stream_nsend_str(server.output, "2 OK \r\n"); test_assert(test_imapc_server_expect("3 RETRY2")); o_stream_nsend_str(server.output, "3 OK \r\n"); test_assert(i_stream_read_next_line(server.input) == NULL); } static void test_imapc_reconnect_resend_commands(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc reconnect resend commands"); test_run_client_server(&set, test_imapc_reconnect_resend_cmds_client, test_imapc_reconnect_resend_cmds_server); test_end(); } static void test_imapc_reconnect_resend_cmds_failed_client(void) { struct imapc_command *cmd; /* login to server */ imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); imapc_client_run(imapc_client); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_OK); imapc_login_last_reply = IMAPC_COMMAND_STATE_INVALID; /* send two commands */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "RETRY1"); cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "RETRY2"); /* disconnect & try to reconnect automatically */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_send(cmd, "DISCONNECT"); test_expect_error_string("reconnecting"); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_DISCONNECTED)); test_expect_error_string("timed out"); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_DISCONNECTED)); test_expect_no_more_errors(); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_DISCONNECTED)); } static void test_imapc_reconnect_resend_cmds_failed_server(void) { test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "1 OK \r\n"); test_assert(test_imapc_server_expect("2 RETRY1")); test_assert(test_imapc_server_expect("3 RETRY2")); test_assert(test_imapc_server_expect("4 DISCONNECT")); test_server_disconnect(&server); } static void test_imapc_reconnect_resend_commands_failed(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc reconnect resend commands failed"); test_run_client_server(&set, test_imapc_reconnect_resend_cmds_failed_client, test_imapc_reconnect_resend_cmds_failed_server); test_end(); } static void test_imapc_reconnect_mailbox_client(void) { struct imapc_command *cmd; struct imapc_client_mailbox *box; /* login to server */ imapc_client_set_login_callback(imapc_client, imapc_login_callback, NULL); imapc_client_login(imapc_client); imapc_client_run(imapc_client); test_assert(imapc_login_last_reply == IMAPC_COMMAND_STATE_OK); imapc_login_last_reply = IMAPC_COMMAND_STATE_INVALID; /* select a mailbox */ box = imapc_client_mailbox_open(imapc_client, NULL); imapc_client_mailbox_set_reopen_cb(box, imapc_reopen_callback, box); cmd = imapc_client_mailbox_cmd(box, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); imapc_command_send(cmd, "SELECT"); imapc_client_run(imapc_client); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_OK)); /* send a command */ cmd = imapc_client_mailbox_cmd(box, imapc_command_callback, NULL); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); imapc_command_send(cmd, "RETRY"); /* disconnect & reconnect automatically */ cmd = imapc_client_cmd(imapc_client, imapc_command_callback, NULL); imapc_command_send(cmd, "DISCONNECT"); test_expect_error_string("reconnecting"); imapc_client_run(imapc_client); test_expect_no_more_errors(); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_DISCONNECTED)); /* continue reconnection */ test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_OK)); test_assert(test_imapc_cmd_last_reply_expect(IMAPC_COMMAND_STATE_OK)); imapc_client_mailbox_close(&box); } static void test_imapc_reconnect_mailbox_server(void) { test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "1 OK \r\n"); test_assert(test_imapc_server_expect("2 SELECT")); o_stream_nsend_str(server.output, "2 OK \r\n"); test_assert(test_imapc_server_expect("3 RETRY")); test_assert(test_imapc_server_expect("4 DISCONNECT")); test_server_disconnect_and_wait(TRUE); test_assert(test_imapc_server_expect("5 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "5 OK \r\n"); test_assert(test_imapc_server_expect("6 SELECT")); o_stream_nsend_str(server.output, "6 OK \r\n"); test_assert(test_imapc_server_expect("3 RETRY")); o_stream_nsend_str(server.output, "3 OK \r\n"); test_assert(i_stream_read_next_line(server.input) == NULL); } static void test_imapc_reconnect_mailbox(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc reconnect mailbox"); test_run_client_server(&set, test_imapc_reconnect_mailbox_client, test_imapc_reconnect_mailbox_server); test_end(); } static void test_imapc_client_get_capabilities_client(void) { enum imapc_capability capabilities; test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) == 0); test_assert(capabilities == (IMAPC_CAPABILITY_IMAP4REV1 | IMAPC_CAPABILITY_UNSELECT | IMAPC_CAPABILITY_QUOTA)); } static void test_imapc_client_get_capabilities_server(void) { test_server_wait_connection(&server, TRUE); test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "1 OK \r\n"); test_assert(test_imapc_server_expect("2 LOGOUT")); o_stream_nsend_str(server.output, "2 OK \r\n"); test_assert(i_stream_read_next_line(server.input) == NULL); } static void test_imapc_client_get_capabilities(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc_client_get_capabilities()"); test_run_client_server(&set, test_imapc_client_get_capabilities_client, test_imapc_client_get_capabilities_server); test_end(); } static void test_imapc_client_get_capabilities_reconnected_client(void) { enum imapc_capability capabilities; test_expect_errors(2); test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) == 0); test_assert(capabilities == (IMAPC_CAPABILITY_IMAP4REV1 | IMAPC_CAPABILITY_UNSELECT | IMAPC_CAPABILITY_QUOTA)); test_expect_no_more_errors(); } static void test_imapc_client_get_capabilities_reconnected_server(void) { test_server_wait_connection(&server, TRUE); test_server_disconnect_and_wait(TRUE); test_assert(test_imapc_server_expect("2 LOGIN \"testuser\" \"testpass\"")); o_stream_nsend_str(server.output, "2 OK \r\n"); test_assert(test_imapc_server_expect("3 LOGOUT")); o_stream_nsend_str(server.output, "3 OK \r\n"); test_assert(i_stream_read_next_line(server.input) == NULL); } static void test_imapc_client_get_capabilities_reconnected(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc_client_get_capabilities() reconnected"); test_run_client_server(&set, test_imapc_client_get_capabilities_reconnected_client, test_imapc_client_get_capabilities_reconnected_server); test_end(); } static void test_imapc_client_get_capabilities_disconnected_client(void) { enum imapc_capability capabilities; test_expect_errors(4); test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) < 0); test_expect_no_more_errors(); } static void test_imapc_client_get_capabilities_disconnected_server(void) { test_server_wait_connection(&server, TRUE); test_server_disconnect_and_wait(TRUE); } static void test_imapc_client_get_capabilities_disconnected(void) { struct imapc_client_settings set = test_imapc_default_settings; test_begin("imapc_client_get_capabilities() disconnected"); test_run_client_server(&set, test_imapc_client_get_capabilities_disconnected_client, test_imapc_client_get_capabilities_disconnected_server); test_end(); } int main(int argc ATTR_UNUSED, char *argv[]) { static void (*test_functions[])(void) = { test_imapc_connect_failed, test_imapc_banner_hangs, test_imapc_login_hangs, test_imapc_reconnect, test_imapc_reconnect_resend_commands, test_imapc_reconnect_resend_commands_failed, test_imapc_reconnect_mailbox, test_imapc_client_get_capabilities, test_imapc_client_get_capabilities_reconnected, test_imapc_client_get_capabilities_disconnected, NULL }; debug = null_strcmp(argv[1], "-D") == 0; test_imapc_default_settings.debug = debug; /* listen on localhost */ i_zero(&bind_ip); bind_ip.family = AF_INET; bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK); return test_run(test_functions); } dovecot-2.2.33.2/src/lib-imap-client/imapc-connection.c0000644000175000017500000020447013165463624017475 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "base64.h" #include "write-full.h" #include "str.h" #include "time-util.h" #include "dns-lookup.h" #include "dsasl-client.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "imap-quote.h" #include "imap-util.h" #include "imap-parser.h" #include "imapc-client-private.h" #include "imapc-connection.h" #include #include #define IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE 10000 #define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32) /* If LOGOUT reply takes longer than this, disconnect. */ #define IMAPC_LOGOUT_TIMEOUT_MSECS 5000 enum imapc_input_state { IMAPC_INPUT_STATE_NONE = 0, IMAPC_INPUT_STATE_PLUS, IMAPC_INPUT_STATE_UNTAGGED, IMAPC_INPUT_STATE_UNTAGGED_NUM, IMAPC_INPUT_STATE_TAGGED }; struct imapc_command_stream { unsigned int pos; uoff_t size; struct istream *input; }; struct imapc_command { pool_t pool; buffer_t *data; unsigned int send_pos; unsigned int tag; enum imapc_command_flags flags; struct imapc_connection *conn; /* If non-NULL, points to the mailbox where this command should be executed */ struct imapc_client_mailbox *box; ARRAY(struct imapc_command_stream) streams; imapc_command_callback_t *callback; void *context; /* This is the AUTHENTICATE command */ unsigned int authenticate:1; /* This is the IDLE command */ unsigned int idle:1; /* Waiting for '+' literal reply before we can continue */ unsigned int wait_for_literal:1; }; ARRAY_DEFINE_TYPE(imapc_command, struct imapc_command *); struct imapc_connection_literal { char *temp_path; int fd; uoff_t bytes_left; const struct imap_arg *parent_arg; unsigned int list_idx; }; struct imapc_connection { struct imapc_client *client; char *name; int refcount; int fd; struct io *io; struct istream *input, *raw_input; struct ostream *output, *raw_output; struct imap_parser *parser; struct timeout *to; struct timeout *to_output; struct dns_lookup *dns_lookup; struct dsasl_client *sasl_client; struct ssl_iostream *ssl_iostream; int (*input_callback)(struct imapc_connection *conn); enum imapc_input_state input_state; unsigned int cur_tag; uint32_t cur_num; struct timeval last_connect; unsigned int reconnect_count; struct imapc_client_mailbox *selecting_box, *selected_box; enum imapc_connection_state state; char *disconnect_reason; enum imapc_capability capabilities; char **capabilities_list; imapc_command_callback_t *login_callback; void *login_context; /* commands pending in queue to be sent */ ARRAY_TYPE(imapc_command) cmd_send_queue; /* commands that have been sent, waiting for their tagged reply */ ARRAY_TYPE(imapc_command) cmd_wait_list; unsigned int reconnect_command_count; unsigned int ips_count, prev_connect_idx; struct ip_addr *ips; struct imapc_connection_literal literal; ARRAY(struct imapc_arg_file) literal_files; unsigned int throttle_msecs; unsigned int throttle_shrink_msecs; unsigned int last_successful_throttle_msecs; bool throttle_pending; struct timeval throttle_end_timeval; struct timeout *to_throttle, *to_throttle_shrink; unsigned int reconnecting:1; unsigned int reconnect_waiting:1; unsigned int reconnect_ok:1; unsigned int idling:1; unsigned int idle_stopping:1; unsigned int idle_plus_waiting:1; }; static void imapc_connection_capability_cb(const struct imapc_command_reply *reply, void *context); static int imapc_connection_output(struct imapc_connection *conn); static int imapc_connection_ssl_init(struct imapc_connection *conn); static void imapc_command_free(struct imapc_command *cmd); static void imapc_command_send_more(struct imapc_connection *conn); static void imapc_login_callback(struct imapc_connection *conn, const struct imapc_command_reply *reply); static void imapc_auth_ok(struct imapc_connection *conn) { if (conn->client->set.debug) i_debug("imapc(%s): Authenticated successfully", conn->name); if (conn->client->state_change_callback == NULL) return; conn->client->state_change_callback(conn->client->state_change_context, IMAPC_STATE_CHANGE_AUTH_OK, NULL); } static void imapc_auth_failed(struct imapc_connection *conn, const struct imapc_command_reply *_reply, const char *error) { struct imapc_command_reply reply = *_reply; if (reply.state != IMAPC_COMMAND_STATE_DISCONNECTED) reply.state = IMAPC_COMMAND_STATE_AUTH_FAILED; reply.text_without_resp = reply.text_full = t_strdup_printf("Authentication failed: %s", error); i_error("imapc(%s): %s", conn->name, reply.text_full); imapc_login_callback(conn, &reply); if (conn->client->state_change_callback == NULL) return; conn->client->state_change_callback(conn->client->state_change_context, IMAPC_STATE_CHANGE_AUTH_FAILED, error); } struct imapc_connection * imapc_connection_init(struct imapc_client *client, imapc_command_callback_t *login_callback, void *login_context) { struct imapc_connection *conn; conn = i_new(struct imapc_connection, 1); conn->refcount = 1; conn->client = client; conn->login_callback = login_callback; conn->login_context = login_context; conn->fd = -1; conn->name = i_strdup_printf("%s:%u", client->set.host, client->set.port); conn->literal.fd = -1; conn->reconnect_ok = (client->set.connect_retry_count>0); i_array_init(&conn->cmd_send_queue, 8); i_array_init(&conn->cmd_wait_list, 32); i_array_init(&conn->literal_files, 4); if (client->set.debug) i_debug("imapc(%s): Created new connection", conn->name); imapc_client_ref(client); return conn; } static void imapc_connection_ref(struct imapc_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } static void imapc_connection_unref(struct imapc_connection **_conn) { struct imapc_connection *conn = *_conn; i_assert(conn->refcount > 0); *_conn = NULL; if (--conn->refcount > 0) return; i_assert(conn->disconnect_reason == NULL); if (conn->capabilities_list != NULL) p_strsplit_free(default_pool, conn->capabilities_list); array_free(&conn->cmd_send_queue); array_free(&conn->cmd_wait_list); array_free(&conn->literal_files); imapc_client_unref(&conn->client); i_free(conn->ips); i_free(conn->name); i_free(conn); } void imapc_connection_deinit(struct imapc_connection **_conn) { imapc_connection_disconnect(*_conn); imapc_connection_unref(_conn); } void imapc_connection_ioloop_changed(struct imapc_connection *conn) { if (conn->io != NULL) conn->io = io_loop_move_io(&conn->io); if (conn->to != NULL) conn->to = io_loop_move_timeout(&conn->to); if (conn->to_throttle != NULL) conn->to_throttle = io_loop_move_timeout(&conn->to_throttle); if (conn->to_throttle_shrink != NULL) conn->to_throttle_shrink = io_loop_move_timeout(&conn->to_throttle_shrink); if (conn->output != NULL) o_stream_switch_ioloop(conn->output); if (conn->dns_lookup != NULL) dns_lookup_switch_ioloop(conn->dns_lookup); if (conn->client->ioloop == NULL && conn->to_output != NULL) { /* we're only once moving the to_output to the main ioloop, since timeout moves currently also reset the timeout. (the rest of the times this is a no-op) */ conn->to_output = io_loop_move_timeout(&conn->to_output); } } static const char *imapc_command_get_readable(struct imapc_command *cmd) { string_t *str = t_str_new(256); const unsigned char *data = cmd->data->data; unsigned int i; for (i = 0; i < cmd->data->used; i++) { if (data[i] != '\r' && data[i] != '\n') str_append_c(str, data[i]); } return str_c(str); } static void imapc_connection_abort_commands_array(ARRAY_TYPE(imapc_command) *cmd_array, ARRAY_TYPE(imapc_command) *dest_array, struct imapc_client_mailbox *only_box, bool keep_retriable) { struct imapc_command *const *cmdp, *cmd; unsigned int i; for (i = 0; i < array_count(cmd_array); ) { cmdp = array_idx(cmd_array, i); cmd = *cmdp; if (cmd->box != only_box && only_box != NULL) i++; else if (keep_retriable && (cmd->flags & IMAPC_COMMAND_FLAG_RETRIABLE) != 0) { cmd->send_pos = 0; cmd->wait_for_literal = 0; cmd->flags |= IMAPC_COMMAND_FLAG_RECONNECTED; i++; } else { array_delete(cmd_array, i, 1); array_append(dest_array, &cmd, 1); } } } void imapc_connection_abort_commands(struct imapc_connection *conn, struct imapc_client_mailbox *only_box, bool keep_retriable) { struct imapc_command *const *cmdp, *cmd; ARRAY_TYPE(imapc_command) tmp_array; struct imapc_command_reply reply; t_array_init(&tmp_array, 8); imapc_connection_abort_commands_array(&conn->cmd_wait_list, &tmp_array, only_box, keep_retriable); imapc_connection_abort_commands_array(&conn->cmd_send_queue, &tmp_array, only_box, keep_retriable); if (array_count(&conn->cmd_wait_list) > 0 && only_box == NULL) { /* need to move all the waiting commands to send queue */ array_append_array(&conn->cmd_wait_list, &conn->cmd_send_queue); array_clear(&conn->cmd_send_queue); array_append_array(&conn->cmd_send_queue, &conn->cmd_wait_list); array_clear(&conn->cmd_wait_list); } /* abort the commands. we'll do it here later so that if the callback recurses us back here we don't crash */ i_zero(&reply); reply.state = IMAPC_COMMAND_STATE_DISCONNECTED; reply.text_without_resp = reply.text_full = "Disconnected from server"; array_foreach(&tmp_array, cmdp) { cmd = *cmdp; cmd->callback(&reply, cmd->context); imapc_command_free(cmd); } if (conn->to != NULL) timeout_remove(&conn->to); } static void imapc_login_callback(struct imapc_connection *conn, const struct imapc_command_reply *reply) { if (conn->login_callback != NULL) conn->login_callback(reply, conn->login_context); } static void imapc_connection_set_state(struct imapc_connection *conn, enum imapc_connection_state state) { struct imapc_command_reply reply; conn->state = state; switch (state) { case IMAPC_CONNECTION_STATE_DISCONNECTED: i_zero(&reply); reply.state = IMAPC_COMMAND_STATE_DISCONNECTED; reply.text_full = "Disconnected from server"; if (conn->disconnect_reason != NULL) { reply.text_full = t_strdup_printf("%s: %s", reply.text_full, conn->disconnect_reason); i_free_and_null(conn->disconnect_reason); } reply.text_without_resp = reply.text_full; if (!conn->reconnecting) { imapc_login_callback(conn, &reply); i_free(conn->ips); conn->ips_count = 0; } conn->idling = FALSE; conn->idle_plus_waiting = FALSE; conn->idle_stopping = FALSE; conn->selecting_box = NULL; conn->selected_box = NULL; /* fall through */ case IMAPC_CONNECTION_STATE_DONE: /* if we came from imapc_client_get_capabilities(), stop so it can finish up and not just hang indefinitely. */ if (conn->client->stop_on_state_finish && !conn->reconnecting) imapc_client_stop(conn->client); break; default: break; } } static void imapc_connection_lfiles_free(struct imapc_connection *conn) { struct imapc_arg_file *lfile; array_foreach_modifiable(&conn->literal_files, lfile) { if (close(lfile->fd) < 0) i_error("imapc: close(literal file) failed: %m"); } array_clear(&conn->literal_files); } static void imapc_connection_literal_reset(struct imapc_connection_literal *literal) { if (literal->fd != -1) { if (close(literal->fd) < 0) i_error("close(%s) failed: %m", literal->temp_path); } i_free_and_null(literal->temp_path); i_zero(literal); literal->fd = -1; } void imapc_connection_disconnect_full(struct imapc_connection *conn, bool reconnecting) { /* timeout may be set also in disconnected state */ if (conn->to != NULL) timeout_remove(&conn->to); conn->reconnecting = reconnecting; if (conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED) return; if (conn->client->set.debug) i_debug("imapc(%s): Disconnected", conn->name); if (conn->dns_lookup != NULL) dns_lookup_abort(&conn->dns_lookup); imapc_connection_lfiles_free(conn); imapc_connection_literal_reset(&conn->literal); if (conn->to_output != NULL) timeout_remove(&conn->to_output); if (conn->to_throttle != NULL) timeout_remove(&conn->to_throttle); if (conn->to_throttle_shrink != NULL) timeout_remove(&conn->to_throttle_shrink); if (conn->parser != NULL) imap_parser_unref(&conn->parser); if (conn->io != NULL) io_remove(&conn->io); if (conn->ssl_iostream != NULL) ssl_iostream_unref(&conn->ssl_iostream); if (conn->fd != -1) { i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); net_disconnect(conn->fd); conn->fd = -1; } /* get capabilities again after reconnection. this is especially important because post-login capabilities often do not contain AUTH= capabilities. */ conn->capabilities = 0; if (conn->capabilities_list != NULL) { p_strsplit_free(default_pool, conn->capabilities_list); conn->capabilities_list = NULL; } imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED); imapc_connection_abort_commands(conn, NULL, reconnecting); if (!reconnecting) { imapc_client_try_stop(conn->client); } } void imapc_connection_set_no_reconnect(struct imapc_connection *conn) { conn->reconnect_ok = FALSE; } void imapc_connection_disconnect(struct imapc_connection *conn) { imapc_connection_disconnect_full(conn, FALSE); } static void imapc_connection_set_disconnected(struct imapc_connection *conn) { imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED); imapc_connection_abort_commands(conn, NULL, FALSE); } static bool imapc_connection_can_reconnect(struct imapc_connection *conn) { if (conn->client->logging_out) return FALSE; if (conn->client->set.connect_retry_count == 0 || (conn->client->set.connect_retry_count < UINT_MAX && conn->reconnect_count >= conn->client->set.connect_retry_count)) return FALSE; if (conn->selected_box != NULL) return imapc_client_mailbox_can_reconnect(conn->selected_box); else { return conn->reconnect_command_count == 0 && conn->reconnect_ok; } } static void imapc_connection_reconnect(struct imapc_connection *conn) { conn->reconnect_ok = FALSE; conn->reconnect_waiting = FALSE; if (conn->selected_box != NULL) { i_assert(!conn->selected_box->reconnecting); conn->selected_box->reconnecting = TRUE; /* if we fail again, avoid reconnecting immediately. if the server is broken we could just get into an infinitely failing reconnection loop. */ conn->selected_box->reconnect_ok = FALSE; } imapc_connection_disconnect_full(conn, TRUE); imapc_connection_connect(conn); } void imapc_connection_try_reconnect(struct imapc_connection *conn, const char *errstr, unsigned int delay_msecs, bool connect_error) { /* Try the next IP address only for connect() problems. */ if (conn->prev_connect_idx + 1 < conn->ips_count && connect_error) { i_warning("imapc(%s): %s - trying the next IP", conn->name, errstr); conn->reconnect_ok = TRUE; imapc_connection_disconnect_full(conn, TRUE); imapc_connection_connect(conn); return; } if (!imapc_connection_can_reconnect(conn)) { i_error("imapc(%s): %s - disconnecting", conn->name, errstr); imapc_connection_disconnect(conn); } else { conn->reconnecting = TRUE; i_warning("imapc(%s): %s - reconnecting (delay %u ms)", conn->name, errstr, delay_msecs); if (delay_msecs == 0) imapc_connection_reconnect(conn); else { imapc_connection_disconnect_full(conn, TRUE); conn->to = timeout_add(delay_msecs, imapc_connection_reconnect, conn); conn->reconnect_count++; conn->reconnect_waiting = TRUE; } } } static void ATTR_FORMAT(2, 3) imapc_connection_input_error(struct imapc_connection *conn, const char *fmt, ...) { va_list va; va_start(va, fmt); i_error("imapc(%s): Server sent invalid input: %s", conn->name, t_strdup_vprintf(fmt, va)); imapc_connection_disconnect(conn); va_end(va); } static bool last_arg_is_fetch_body(const struct imap_arg *args, const struct imap_arg **parent_arg_r, unsigned int *idx_r) { const struct imap_arg *list; const char *name; unsigned int count; if (args[0].type == IMAP_ARG_ATOM && imap_arg_atom_equals(&args[1], "FETCH") && imap_arg_get_list_full(&args[2], &list, &count) && count >= 2 && list[count].type == IMAP_ARG_LITERAL_SIZE && imap_arg_get_atom(&list[count-1], &name) && strncasecmp(name, "BODY[", 5) == 0) { *parent_arg_r = &args[2]; *idx_r = count; return TRUE; } return FALSE; } static int imapc_connection_read_literal_init(struct imapc_connection *conn, uoff_t size, const struct imap_arg *args) { const char *path; const struct imap_arg *parent_arg; unsigned int idx; i_assert(conn->literal.fd == -1); if (size <= IMAPC_MAX_INLINE_LITERAL_SIZE || !last_arg_is_fetch_body(args, &parent_arg, &idx)) { /* read the literal directly into parser */ return 0; } conn->literal.fd = imapc_client_create_temp_fd(conn->client, &path); if (conn->literal.fd == -1) return -1; conn->literal.temp_path = i_strdup(path); conn->literal.bytes_left = size; conn->literal.parent_arg = parent_arg; conn->literal.list_idx = idx; return 1; } static int imapc_connection_read_literal(struct imapc_connection *conn) { struct imapc_arg_file *lfile; const unsigned char *data; size_t size; if (conn->literal.bytes_left == 0) return 1; data = i_stream_get_data(conn->input, &size); if (size > conn->literal.bytes_left) size = conn->literal.bytes_left; if (size > 0) { if (write_full(conn->literal.fd, data, size) < 0) { i_error("imapc(%s): write(%s) failed: %m", conn->name, conn->literal.temp_path); imapc_connection_disconnect(conn); return -1; } i_stream_skip(conn->input, size); conn->literal.bytes_left -= size; } if (conn->literal.bytes_left > 0) return 0; /* finished */ lfile = array_append_space(&conn->literal_files); lfile->fd = conn->literal.fd; lfile->parent_arg = conn->literal.parent_arg; lfile->list_idx = conn->literal.list_idx; conn->literal.fd = -1; imapc_connection_literal_reset(&conn->literal); return 1; } static int imapc_connection_read_line_more(struct imapc_connection *conn, const struct imap_arg **imap_args_r) { uoff_t literal_size; bool fatal; int ret; if ((ret = imapc_connection_read_literal(conn)) <= 0) return ret; ret = imap_parser_read_args(conn->parser, 0, IMAP_PARSE_FLAG_LITERAL_SIZE | IMAP_PARSE_FLAG_ATOM_ALLCHARS | IMAP_PARSE_FLAG_LITERAL8 | IMAP_PARSE_FLAG_SERVER_TEXT, imap_args_r); if (ret == -2) { /* need more data */ return 0; } if (ret < 0) { imapc_connection_input_error(conn, "Error parsing input: %s", imap_parser_get_error(conn->parser, &fatal)); return -1; } if (imap_parser_get_literal_size(conn->parser, &literal_size)) { if (imapc_connection_read_literal_init(conn, literal_size, *imap_args_r) <= 0) { imap_parser_read_last_literal(conn->parser); return 2; } return imapc_connection_read_line_more(conn, imap_args_r); } return 1; } static int imapc_connection_read_line(struct imapc_connection *conn, const struct imap_arg **imap_args_r) { const unsigned char *data; size_t size; int ret; while ((ret = imapc_connection_read_line_more(conn, imap_args_r)) == 2) ; if (ret > 0) { data = i_stream_get_data(conn->input, &size); if (size >= 2 && data[0] == '\r' && data[1] == '\n') i_stream_skip(conn->input, 2); else if (size >= 1 && data[0] == '\n') i_stream_skip(conn->input, 1); else i_panic("imapc: Missing LF from input line"); } return ret; } static int imapc_connection_parse_capability(struct imapc_connection *conn, const char *value) { const char *const *tmp; unsigned int i; if (conn->client->set.debug) { i_debug("imapc(%s): Server capabilities: %s", conn->name, value); } conn->capabilities = 0; if (conn->capabilities_list != NULL) p_strsplit_free(default_pool, conn->capabilities_list); conn->capabilities_list = p_strsplit(default_pool, value, " "); for (tmp = t_strsplit(value, " "); *tmp != NULL; tmp++) { for (i = 0; imapc_capability_names[i].name != NULL; i++) { const struct imapc_capability_name *cap = &imapc_capability_names[i]; if (strcasecmp(*tmp, cap->name) == 0) { conn->capabilities |= cap->capability; break; } } } if ((conn->capabilities & IMAPC_CAPABILITY_IMAP4REV1) == 0) { imapc_connection_input_error(conn, "CAPABILITY list is missing IMAP4REV1"); return -1; } return 0; } static int imapc_connection_handle_resp_text_code(struct imapc_connection *conn, const char *key, const char *value) { if (strcasecmp(key, "CAPABILITY") == 0) { if (imapc_connection_parse_capability(conn, value) < 0) return -1; } if (strcasecmp(key, "CLOSED") == 0) { /* QRESYNC: SELECTing another mailbox */ if (conn->selecting_box != NULL) { conn->selected_box = conn->selecting_box; conn->selecting_box = NULL; } } return 0; } static int imapc_connection_handle_resp_text(struct imapc_connection *conn, const char *text, const char **key_r, const char **value_r) { const char *p, *value; i_assert(text[0] == '['); p = strchr(text, ']'); if (p == NULL) { imapc_connection_input_error(conn, "Missing ']' in resp-text"); return -1; } text = t_strdup_until(text + 1, p); value = strchr(text, ' '); if (value != NULL) { *key_r = t_strdup_until(text, value); *value_r = value + 1; } else { *key_r = text; *value_r = ""; } return imapc_connection_handle_resp_text_code(conn, *key_r, *value_r); } static int imapc_connection_handle_imap_resp_text(struct imapc_connection *conn, const struct imap_arg *args, const char **key_r, const char **value_r) { const char *text; if (args->type != IMAP_ARG_ATOM) return 0; text = imap_args_to_str(args); if (*text != '[') { if (*text == '\0') { imapc_connection_input_error(conn, "Missing text in resp-text"); return -1; } return 0; } return imapc_connection_handle_resp_text(conn, text, key_r, value_r); } static bool need_literal(const char *str) { unsigned int i; for (i = 0; str[i] != '\0'; i++) { unsigned char c = str[i]; if ((c & 0x80) != 0 || c == '\r' || c == '\n') return TRUE; } return FALSE; } static void imapc_connection_input_reset(struct imapc_connection *conn) { conn->input_state = IMAPC_INPUT_STATE_NONE; conn->cur_tag = 0; conn->cur_num = 0; if (conn->parser != NULL) imap_parser_reset(conn->parser); imapc_connection_lfiles_free(conn); } static void imapc_connection_auth_finish(struct imapc_connection *conn, const struct imapc_command_reply *reply) { if (reply->state != IMAPC_COMMAND_STATE_OK) { imapc_auth_failed(conn, reply, reply->text_full); imapc_connection_disconnect(conn); return; } imapc_auth_ok(conn); timeout_remove(&conn->to); imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE); imapc_login_callback(conn, reply); imapc_command_send_more(conn); } static void imapc_connection_login_cb(const struct imapc_command_reply *reply, void *context) { struct imapc_connection *conn = context; imapc_connection_auth_finish(conn, reply); } static void imapc_connection_proxyauth_login_cb(const struct imapc_command_reply *reply, void *context) { struct imapc_connection *conn = context; const struct imapc_client_settings *set = &conn->client->set; struct imapc_command *cmd; if (reply->state == IMAPC_COMMAND_STATE_OK) { cmd = imapc_connection_cmd(conn, imapc_connection_login_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_sendf(cmd, "PROXYAUTH %s", set->username); imapc_command_send_more(conn); } else { imapc_connection_auth_finish(conn, reply); } } static void imapc_connection_authenticate_cb(const struct imapc_command_reply *reply, void *context) { struct imapc_connection *conn = context; const unsigned char *sasl_output; unsigned int sasl_output_len; size_t input_len; buffer_t *buf; const char *error; if ((int)reply->state != IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE) { dsasl_client_free(&conn->sasl_client); imapc_connection_auth_finish(conn, reply); return; } input_len = strlen(reply->text_full); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(input_len)); if (base64_decode(reply->text_full, input_len, NULL, buf) < 0) { imapc_auth_failed(conn, reply, t_strdup_printf("Server sent non-base64 input for AUTHENTICATE: %s", reply->text_full)); } else if (dsasl_client_input(conn->sasl_client, buf->data, buf->used, &error) < 0) { imapc_auth_failed(conn, reply, error); } else if (dsasl_client_output(conn->sasl_client, &sasl_output, &sasl_output_len, &error) < 0) { imapc_auth_failed(conn, reply, error); } else { string_t *imap_output = t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len)+2); base64_encode(sasl_output, sasl_output_len, imap_output); str_append(imap_output, "\r\n"); o_stream_nsend(conn->output, str_data(imap_output), str_len(imap_output)); return; } imapc_connection_disconnect(conn); } static bool imapc_connection_have_auth(struct imapc_connection *conn, const char *mech_name) { char *const *capa; for (capa = conn->capabilities_list; *capa != NULL; capa++) { if (strncasecmp(*capa, "AUTH=", 5) == 0 && strcasecmp((*capa)+5, mech_name) == 0) return TRUE; } return FALSE; } static int imapc_connection_get_sasl_mech(struct imapc_connection *conn, const struct dsasl_client_mech **mech_r, const char **error_r) { const struct imapc_client_settings *set = &conn->client->set; const char *const *mechanisms = t_strsplit_spaces(set->sasl_mechanisms, ", "); /* find one of the specified SASL mechanisms */ for (; *mechanisms != NULL; mechanisms++) { if (imapc_connection_have_auth(conn, *mechanisms)) { *mech_r = dsasl_client_mech_find(*mechanisms); if (*mech_r != NULL) return 0; *error_r = t_strdup_printf( "Support for SASL method '%s' is missing", *mechanisms); return -1; } } *error_r = t_strdup_printf("IMAP server doesn't support any of the requested SASL mechanisms: %s", set->sasl_mechanisms); return -1; } static void imapc_connection_authenticate(struct imapc_connection *conn) { const struct imapc_client_settings *set = &conn->client->set; struct imapc_command *cmd; struct dsasl_client_settings sasl_set; const struct dsasl_client_mech *sasl_mech = NULL; const char *error; if (conn->client->set.debug) { if (set->master_user == NULL) { i_debug("imapc(%s): Authenticating as %s", conn->name, set->username); } else { i_debug("imapc(%s): Authenticating as %s for user %s", conn->name, set->master_user, set->username); } } if (set->sasl_mechanisms != NULL && set->sasl_mechanisms[0] != '\0') { if (imapc_connection_get_sasl_mech(conn, &sasl_mech, &error) < 0) { struct imapc_command_reply reply; i_zero(&reply); reply.state = IMAPC_COMMAND_STATE_DISCONNECTED; reply.text_full = ""; imapc_auth_failed(conn, &reply, error); imapc_connection_disconnect(conn); return; } } if (set->use_proxyauth && set->master_user != NULL) { /* We can use LOGIN command */ cmd = imapc_connection_cmd(conn, imapc_connection_proxyauth_login_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_sendf(cmd, "LOGIN %s %s", set->master_user, set->password); return; } if (sasl_mech == NULL && ((set->master_user == NULL && !need_literal(set->username) && !need_literal(set->password)) || (conn->capabilities & IMAPC_CAPABILITY_AUTH_PLAIN) == 0)) { /* We can use LOGIN command */ cmd = imapc_connection_cmd(conn, imapc_connection_login_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_sendf(cmd, "LOGIN %s %s", set->username, set->password); return; } i_zero(&sasl_set); if (set->master_user == NULL) sasl_set.authid = set->username; else { sasl_set.authid = set->master_user; sasl_set.authzid = set->username; } sasl_set.password = set->password; if (sasl_mech == NULL) sasl_mech = &dsasl_client_mech_plain; conn->sasl_client = dsasl_client_new(sasl_mech, &sasl_set); cmd = imapc_connection_cmd(conn, imapc_connection_authenticate_cb, conn); cmd->authenticate = TRUE; imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) { const unsigned char *sasl_output; unsigned int sasl_output_len; string_t *sasl_output_base64; const char *error; if (dsasl_client_output(conn->sasl_client, &sasl_output, &sasl_output_len, &error) < 0) { i_error("imapc(%s): Failed to create initial SASL reply: %s", conn->name, error); imapc_connection_disconnect(conn); return; } sasl_output_base64 = t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len)); base64_encode(sasl_output, sasl_output_len, sasl_output_base64); imapc_command_sendf(cmd, "AUTHENTICATE %1s %1s", dsasl_client_mech_get_name(sasl_mech), str_c(sasl_output_base64)); } else { imapc_command_sendf(cmd, "AUTHENTICATE %1s", dsasl_client_mech_get_name(sasl_mech)); } } static void imapc_connection_starttls_cb(const struct imapc_command_reply *reply, void *context) { struct imapc_connection *conn = context; struct imapc_command *cmd; if (reply->state != IMAPC_COMMAND_STATE_OK) { imapc_connection_input_error(conn, "STARTTLS failed: %s", reply->text_full); return; } if (imapc_connection_ssl_init(conn) < 0) imapc_connection_disconnect(conn); else { /* get updated capabilities */ cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_send(cmd, "CAPABILITY"); } } static void imapc_connection_starttls(struct imapc_connection *conn) { struct imapc_command *cmd; if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_STARTTLS && conn->ssl_iostream == NULL) { if ((conn->capabilities & IMAPC_CAPABILITY_STARTTLS) == 0) { i_error("imapc(%s): Requested STARTTLS, " "but server doesn't support it", conn->name); imapc_connection_disconnect(conn); return; } cmd = imapc_connection_cmd(conn, imapc_connection_starttls_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_send(cmd, "STARTTLS"); return; } imapc_connection_authenticate(conn); } static void imapc_connection_capability_cb(const struct imapc_command_reply *reply, void *context) { struct imapc_connection *conn = context; if (reply->state != IMAPC_COMMAND_STATE_OK) { imapc_connection_input_error(conn, "Failed to get capabilities: %s", reply->text_full); } else if (conn->capabilities == 0) { imapc_connection_input_error(conn, "Capabilities not returned by server"); } else { imapc_connection_starttls(conn); } } static int imapc_connection_input_banner(struct imapc_connection *conn) { const struct imap_arg *imap_args; const char *key, *value; struct imapc_command *cmd; int ret; if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0) return ret; /* we already verified that the banner beigns with OK */ i_assert(imap_arg_atom_equals(imap_args, "OK")); imap_args++; if (imapc_connection_handle_imap_resp_text(conn, imap_args, &key, &value) < 0) return -1; imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_AUTHENTICATING); if (conn->capabilities == 0) { /* capabilities weren't sent in the banner. ask for them. */ cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb, conn); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_send(cmd, "CAPABILITY"); } else { imapc_connection_starttls(conn); } conn->input_callback = NULL; imapc_connection_input_reset(conn); return 1; } static int imapc_connection_input_untagged(struct imapc_connection *conn) { const struct imap_arg *imap_args; const unsigned char *data; size_t size; const char *name, *value; struct imap_parser *parser; struct imapc_untagged_reply reply; int ret; if (conn->state == IMAPC_CONNECTION_STATE_CONNECTING) { /* input banner */ data = i_stream_get_data(conn->input, &size); if (size < 3 && memchr(data, '\n', size) == NULL) return 0; if (i_memcasecmp(data, "OK ", 3) != 0) { imapc_connection_input_error(conn, "Banner doesn't begin with OK: %s", t_strcut(t_strndup(data, size), '\n')); return -1; } conn->input_callback = imapc_connection_input_banner; return 1; } if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0) return ret; if (!imap_arg_get_atom(&imap_args[0], &name)) { imapc_connection_input_error(conn, "Invalid untagged reply"); return -1; } imap_args++; if (conn->input_state == IMAPC_INPUT_STATE_UNTAGGED && str_to_uint32(name, &conn->cur_num) == 0) { /* */ conn->input_state = IMAPC_INPUT_STATE_UNTAGGED_NUM; if (!imap_arg_get_atom(&imap_args[0], &name)) { imapc_connection_input_error(conn, "Invalid untagged reply"); return -1; } imap_args++; } i_zero(&reply); if (strcasecmp(name, "OK") == 0) { if (imapc_connection_handle_imap_resp_text(conn, imap_args, &reply.resp_text_key, &reply.resp_text_value) < 0) return -1; } else if (strcasecmp(name, "CAPABILITY") == 0) { value = imap_args_to_str(imap_args); if (imapc_connection_parse_capability(conn, value) < 0) return -1; } else if (strcasecmp(name, "BYE") == 0) { i_free(conn->disconnect_reason); conn->disconnect_reason = i_strdup(imap_args_to_str(imap_args)); } reply.name = name; reply.num = conn->cur_num; reply.args = imap_args; reply.file_args = array_get(&conn->literal_files, &reply.file_args_count); if (conn->selected_box != NULL) { reply.untagged_box_context = conn->selected_box->untagged_box_context; } /* the callback may disconnect and destroy the parser */ parser = conn->parser; imap_parser_ref(parser); conn->client->untagged_callback(&reply, conn->client->untagged_context); imap_parser_unref(&parser); imapc_connection_input_reset(conn); return 1; } static int imapc_connection_input_plus(struct imapc_connection *conn) { struct imapc_command *const *cmds; unsigned int cmds_count; const char *line; if ((line = i_stream_next_line(conn->input)) == NULL) return 0; cmds = array_get(&conn->cmd_send_queue, &cmds_count); if (conn->idle_plus_waiting) { /* "+ idling" reply for IDLE command */ conn->idle_plus_waiting = FALSE; conn->idling = TRUE; /* no timeouting while IDLEing */ if (conn->to != NULL && !conn->idle_stopping) timeout_remove(&conn->to); } else if (cmds_count > 0 && cmds[0]->wait_for_literal) { /* reply for literal */ cmds[0]->wait_for_literal = FALSE; imapc_command_send_more(conn); } else { cmds = array_get(&conn->cmd_wait_list, &cmds_count); if (cmds_count > 0 && cmds[0]->authenticate) { /* continue AUTHENTICATE */ struct imapc_command_reply reply; i_zero(&reply); reply.state = (enum imapc_command_state)IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE; reply.text_full = line; cmds[0]->callback(&reply, cmds[0]->context); } else { imapc_connection_input_error(conn, "Unexpected '+': %s", line); return -1; } } imapc_connection_input_reset(conn); return 1; } static void imapc_connection_throttle_shrink_timeout(struct imapc_connection *conn) { if (conn->throttle_msecs <= 1) conn->throttle_msecs = 0; else conn->throttle_msecs = conn->throttle_msecs*3 / 4; if (conn->throttle_shrink_msecs <= conn->client->set.throttle_set.shrink_min_msecs) conn->throttle_shrink_msecs = 0; else conn->throttle_shrink_msecs = conn->throttle_shrink_msecs*3 / 4; timeout_remove(&conn->to_throttle_shrink); if (conn->throttle_shrink_msecs > 0) { conn->to_throttle_shrink = timeout_add(conn->throttle_shrink_msecs, imapc_connection_throttle_shrink_timeout, conn); } } static void imapc_connection_throttle(struct imapc_connection *conn, const struct imapc_command_reply *reply) { if (conn->to_throttle != NULL) timeout_remove(&conn->to_throttle); /* If GMail returns [THROTTLED], start slowing down commands. Unfortunately this isn't a nice resp-text-code, but just appended at the end of the line (although we kind of support it as resp-text-code also in here if it's uppercased). */ if (strstr(reply->text_full, "[THROTTLED]") != NULL) { if (conn->throttle_msecs == 0) conn->throttle_msecs = conn->client->set.throttle_set.init_msecs; else if (conn->throttle_msecs < conn->last_successful_throttle_msecs) conn->throttle_msecs = conn->last_successful_throttle_msecs; else { conn->throttle_msecs *= 2; if (conn->throttle_msecs > conn->client->set.throttle_set.max_msecs) conn->throttle_msecs = conn->client->set.throttle_set.max_msecs; } if (conn->throttle_shrink_msecs == 0) conn->throttle_shrink_msecs = conn->client->set.throttle_set.shrink_min_msecs; else conn->throttle_shrink_msecs *= 2; if (conn->to_throttle_shrink != NULL) timeout_reset(conn->to_throttle_shrink); } else { if (conn->throttle_shrink_msecs > 0 && conn->to_throttle_shrink == NULL) { conn->to_throttle_shrink = timeout_add(conn->throttle_shrink_msecs, imapc_connection_throttle_shrink_timeout, conn); } conn->last_successful_throttle_msecs = conn->throttle_msecs; } if (conn->throttle_msecs > 0) { conn->throttle_end_timeval = ioloop_timeval; timeval_add_msecs(&conn->throttle_end_timeval, conn->throttle_msecs); conn->throttle_pending = TRUE; } } static void imapc_command_reply_free(struct imapc_command *cmd, const struct imapc_command_reply *reply) { cmd->callback(reply, cmd->context); imapc_command_free(cmd); } static int imapc_connection_input_tagged(struct imapc_connection *conn) { struct imapc_command *const *cmds, *cmd = NULL; unsigned int i, count; char *line, *linep; const char *p; struct imapc_command_reply reply; line = i_stream_next_line(conn->input); if (line == NULL) return 0; /* make sure reply texts stays valid if input stream gets freed */ line = t_strdup_noconst(line); i_zero(&reply); linep = strchr(line, ' '); if (linep == NULL) reply.text_full = ""; else { *linep = '\0'; reply.text_full = linep + 1; } if (strcasecmp(line, "ok") == 0) reply.state = IMAPC_COMMAND_STATE_OK; else if (strcasecmp(line, "no") == 0) reply.state = IMAPC_COMMAND_STATE_NO; else if (strcasecmp(line, "bad") == 0) reply.state = IMAPC_COMMAND_STATE_BAD; else { imapc_connection_input_error(conn, "Invalid state in tagged reply: %u %s %s", conn->cur_tag, line, reply.text_full); return -1; } if (reply.text_full[0] == '[') { /* get resp-text */ if (imapc_connection_handle_resp_text(conn, reply.text_full, &reply.resp_text_key, &reply.resp_text_value) < 0) return -1; p = strchr(reply.text_full, ']'); i_assert(p != NULL); reply.text_without_resp = p + 1; if (reply.text_without_resp[0] == ' ') reply.text_without_resp++; } else { reply.text_without_resp = reply.text_full; } /* if we've pipelined multiple commands, handle [THROTTLED] reply from only one of them */ if (!conn->throttle_pending) imapc_connection_throttle(conn, &reply); /* find the command. it's either the first command in send queue (literal failed) or somewhere in wait list. */ cmds = array_get(&conn->cmd_send_queue, &count); if (count > 0 && cmds[0]->tag == conn->cur_tag) { cmd = cmds[0]; array_delete(&conn->cmd_send_queue, 0, 1); } else { cmds = array_get(&conn->cmd_wait_list, &count); for (i = 0; i < count; i++) { if (cmds[i]->tag == conn->cur_tag) { cmd = cmds[i]; array_delete(&conn->cmd_wait_list, i, 1); break; } } } if (array_count(&conn->cmd_wait_list) == 0 && array_count(&conn->cmd_send_queue) == 0 && conn->state == IMAPC_CONNECTION_STATE_DONE && conn->to != NULL) timeout_remove(&conn->to); if (cmd == NULL) { imapc_connection_input_error(conn, "Unknown tag in a reply: %u %s %s", conn->cur_tag, line, reply.text_full); return -1; } if (reply.state == IMAPC_COMMAND_STATE_BAD) { i_error("imapc(%s): Command '%s' failed with BAD: %u %s", conn->name, imapc_command_get_readable(cmd), conn->cur_tag, reply.text_full); imapc_connection_disconnect(conn); } if (reply.state == IMAPC_COMMAND_STATE_NO && (cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0 && conn->selected_box != NULL) { /* EXAMINE/SELECT failed: mailbox is no longer selected */ imapc_connection_unselect(conn->selected_box); } if (conn->reconnect_command_count > 0 && (cmd->flags & IMAPC_COMMAND_FLAG_RECONNECTED) != 0) { if (--conn->reconnect_command_count == 0) { /* we've received replies for all the commands started before reconnection. if we get disconnected now, we can safely reconnect without worrying about infinite reconnect loops. */ if (conn->selected_box != NULL) conn->selected_box->reconnect_ok = TRUE; } } if (conn->reconnect_command_count == 0) { /* we've successfully received replies to some commands. */ conn->reconnect_ok = TRUE; } imapc_connection_input_reset(conn); imapc_command_reply_free(cmd, &reply); imapc_command_send_more(conn); return 1; } static int imapc_connection_input_one(struct imapc_connection *conn) { const char *tag; int ret = -1; if (conn->input_callback != NULL) return conn->input_callback(conn); switch (conn->input_state) { case IMAPC_INPUT_STATE_NONE: tag = imap_parser_read_word(conn->parser); if (tag == NULL) return 0; if (strcmp(tag, "*") == 0) { conn->input_state = IMAPC_INPUT_STATE_UNTAGGED; conn->cur_num = 0; ret = imapc_connection_input_untagged(conn); } else if (strcmp(tag, "+") == 0) { conn->input_state = IMAPC_INPUT_STATE_PLUS; ret = imapc_connection_input_plus(conn); } else { conn->input_state = IMAPC_INPUT_STATE_TAGGED; if (str_to_uint(tag, &conn->cur_tag) < 0 || conn->cur_tag == 0) { imapc_connection_input_error(conn, "Invalid command tag: %s", tag); ret = -1; } else { ret = imapc_connection_input_tagged(conn); } } break; case IMAPC_INPUT_STATE_PLUS: ret = imapc_connection_input_plus(conn); break; case IMAPC_INPUT_STATE_UNTAGGED: case IMAPC_INPUT_STATE_UNTAGGED_NUM: ret = imapc_connection_input_untagged(conn); break; case IMAPC_INPUT_STATE_TAGGED: ret = imapc_connection_input_tagged(conn); break; } return ret; } static void imapc_connection_input(struct imapc_connection *conn) { const char *errstr; string_t *str; ssize_t ret = 0; /* we need to read as much as we can with SSL streams to avoid hanging */ imapc_connection_ref(conn); while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0) imapc_connection_input_pending(conn); if (ret < 0 && conn->client->logging_out && conn->disconnect_reason != NULL) { /* expected disconnection */ } else if (ret < 0) { /* disconnected or buffer full */ str = t_str_new(128); if (conn->disconnect_reason != NULL) { str_printfa(str, "Server disconnected with message: %s", conn->disconnect_reason); } else if (ret == -2) { str_printfa(str, "Server sent too large input " "(buffer full at %"PRIuSIZE_T")", i_stream_get_data_size(conn->input)); } else if (conn->ssl_iostream == NULL) { errstr = conn->input->stream_errno == 0 ? "EOF" : i_stream_get_error(conn->input); str_printfa(str, "Server disconnected unexpectedly: %s", errstr); } else { errstr = ssl_iostream_get_last_error(conn->ssl_iostream); if (errstr == NULL) { errstr = conn->input->stream_errno == 0 ? "EOF" : i_stream_get_error(conn->input); } str_printfa(str, "Server disconnected unexpectedly: %s", errstr); } imapc_connection_try_reconnect(conn, str_c(str), 0, FALSE); } imapc_connection_unref(&conn); } static int imapc_connection_ssl_handshaked(const char **error_r, void *context) { struct imapc_connection *conn = context; const char *error; if (ssl_iostream_check_cert_validity(conn->ssl_iostream, conn->client->set.host, &error) == 0) { if (conn->client->set.debug) { i_debug("imapc(%s): SSL handshake successful", conn->name); } return 0; } else if (!conn->client->set.ssl_verify) { if (conn->client->set.debug) { i_debug("imapc(%s): SSL handshake successful, " "ignoring invalid certificate: %s", conn->name, error); } return 0; } else { *error_r = error; return -1; } } static int imapc_connection_ssl_init(struct imapc_connection *conn) { struct ssl_iostream_settings ssl_set; const char *error; if (conn->client->ssl_ctx == NULL) { i_error("imapc(%s): No SSL context", conn->name); return -1; } i_zero(&ssl_set); if (conn->client->set.ssl_verify) { ssl_set.verbose_invalid_cert = TRUE; ssl_set.verify_remote_cert = TRUE; ssl_set.require_valid_cert = TRUE; } if (conn->client->set.debug) i_debug("imapc(%s): Starting SSL handshake", conn->name); if (conn->raw_input != conn->input) { /* recreate rawlog after STARTTLS */ i_stream_ref(conn->raw_input); o_stream_ref(conn->raw_output); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); conn->input = conn->raw_input; conn->output = conn->raw_output; } if (io_stream_create_ssl_client(conn->client->ssl_ctx, conn->client->set.host, &ssl_set, &conn->input, &conn->output, &conn->ssl_iostream, &error) < 0) { i_error("imapc(%s): Couldn't initialize SSL client: %s", conn->name, error); return -1; } ssl_iostream_set_handshake_callback(conn->ssl_iostream, imapc_connection_ssl_handshaked, conn); if (ssl_iostream_handshake(conn->ssl_iostream) < 0) { i_error("imapc(%s): SSL handshake failed: %s", conn->name, ssl_iostream_get_last_error(conn->ssl_iostream)); return -1; } if (*conn->client->set.rawlog_dir != '\0') { iostream_rawlog_create(conn->client->set.rawlog_dir, &conn->input, &conn->output); } imap_parser_set_streams(conn->parser, conn->input, NULL); return 0; } static void imapc_connection_connected(struct imapc_connection *conn) { const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx]; struct ip_addr local_ip; in_port_t local_port; int err; if (conn->io != NULL) io_remove(&conn->io); err = net_geterror(conn->fd); if (err != 0) { imapc_connection_try_reconnect(conn, t_strdup_printf( "connect(%s, %u) failed: %s", net_ip2addr(ip), conn->client->set.port, strerror(err)), conn->client->set.connect_retry_interval_msecs, TRUE); return; } if (net_getsockname(conn->fd, &local_ip, &local_port) < 0) local_port = 0; i_info("imapc(%s): Connected to %s:%u (local %s:%u)", conn->name, net_ip2addr(ip), conn->client->set.port, net_ip2addr(&local_ip), local_port); conn->io = io_add(conn->fd, IO_READ, imapc_connection_input, conn); if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_IMMEDIATE) { if (imapc_connection_ssl_init(conn) < 0) imapc_connection_disconnect(conn); } } static void imapc_connection_timeout(struct imapc_connection *conn) { const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx]; const char *errstr; bool connect_error = FALSE; switch (conn->state) { case IMAPC_CONNECTION_STATE_CONNECTING: errstr = t_strdup_printf("connect(%s, %u) timed out after %u seconds", net_ip2addr(ip), conn->client->set.port, conn->client->set.connect_timeout_msecs/1000); connect_error = TRUE; break; case IMAPC_CONNECTION_STATE_AUTHENTICATING: errstr = t_strdup_printf("Authentication timed out after %u seconds", conn->client->set.connect_timeout_msecs/1000); break; default: i_unreached(); } imapc_connection_try_reconnect(conn, errstr, 0, connect_error); } static void imapc_noop_callback(const struct imapc_command_reply *reply ATTR_UNUSED, void *context ATTR_UNUSED) { } static void imapc_reidle_callback(const struct imapc_command_reply *reply ATTR_UNUSED, void *context) { struct imapc_connection *conn = context; imapc_connection_idle(conn); } static void imapc_connection_reset_idle(struct imapc_connection *conn) { struct imapc_command *cmd; if (conn->idling) cmd = imapc_connection_cmd(conn, imapc_reidle_callback, conn); else if (array_count(&conn->cmd_wait_list) == 0) cmd = imapc_connection_cmd(conn, imapc_noop_callback, NULL); else { /* IMAP command reply is taking a long time */ return; } imapc_command_send(cmd, "NOOP"); } static void imapc_connection_connect_next_ip(struct imapc_connection *conn) { const struct ip_addr *ip = NULL; unsigned int i; int fd; i_assert(conn->client->set.max_idle_time > 0); for (i = 0; iips_count;) { conn->prev_connect_idx = (conn->prev_connect_idx+1) % conn->ips_count; ip = &conn->ips[conn->prev_connect_idx]; fd = net_connect_ip(ip, conn->client->set.port, NULL); if (fd != -1) break; /* failed to connect to one of the IPs immediately (e.g. IPv6 address without connectivity). try all IPs before failing completely. */ i_error("net_connect_ip(%s:%u) failed: %m", net_ip2addr(ip), conn->client->set.port); if (conn->prev_connect_idx+1 == conn->ips_count) { imapc_connection_try_reconnect(conn, "No more IP address(es) to try", conn->client->set.connect_retry_interval_msecs, TRUE); return; } } i_assert(ip != NULL); conn->fd = fd; conn->input = conn->raw_input = i_stream_create_fd(fd, conn->client->set.max_line_length, FALSE); conn->output = conn->raw_output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); if (*conn->client->set.rawlog_dir != '\0' && conn->client->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_IMMEDIATE) { iostream_rawlog_create(conn->client->set.rawlog_dir, &conn->input, &conn->output); } o_stream_set_flush_callback(conn->output, imapc_connection_output, conn); conn->io = io_add(fd, IO_WRITE, imapc_connection_connected, conn); conn->parser = imap_parser_create(conn->input, NULL, conn->client->set.max_line_length); conn->to = timeout_add(conn->client->set.connect_timeout_msecs, imapc_connection_timeout, conn); conn->to_output = timeout_add(conn->client->set.max_idle_time*1000, imapc_connection_reset_idle, conn); if (conn->client->set.debug) { i_debug("imapc(%s): Connecting to %s:%u", conn->name, net_ip2addr(ip), conn->client->set.port); } } static void imapc_connection_dns_callback(const struct dns_lookup_result *result, struct imapc_connection *conn) { conn->dns_lookup = NULL; if (result->ret != 0) { i_error("imapc(%s): dns_lookup(%s) failed: %s", conn->name, conn->client->set.host, result->error); imapc_connection_set_disconnected(conn); return; } i_assert(result->ips_count > 0); conn->ips_count = result->ips_count; conn->ips = i_new(struct ip_addr, conn->ips_count); memcpy(conn->ips, result->ips, sizeof(*conn->ips) * conn->ips_count); conn->prev_connect_idx = conn->ips_count - 1; imapc_connection_connect_next_ip(conn); } void imapc_connection_connect(struct imapc_connection *conn) { struct dns_lookup_settings dns_set; struct ip_addr ip, *ips; unsigned int ips_count; int ret; if (conn->fd != -1 || conn->dns_lookup != NULL) return; if (conn->reconnect_waiting) { /* wait for the reconnection delay to finish before doing anything. */ return; } conn->reconnecting = FALSE; /* if we get disconnected before we've finished all the pending commands, don't reconnect */ conn->reconnect_command_count = array_count(&conn->cmd_wait_list) + array_count(&conn->cmd_send_queue); imapc_connection_input_reset(conn); conn->last_connect = ioloop_timeval; if (conn->client->set.debug) { i_debug("imapc(%s): Looking up IP address " "(reconnect_ok=%s, last_connect=%ld)", conn->name, (conn->reconnect_ok ? "true" : "false"), (long)conn->last_connect.tv_sec); } i_zero(&dns_set); dns_set.dns_client_socket_path = conn->client->set.dns_client_socket_path; dns_set.timeout_msecs = conn->client->set.connect_timeout_msecs; imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_CONNECTING); if (conn->ips_count > 0) { /* do nothing */ } else if (net_addr2ip(conn->client->set.host, &ip) == 0) { conn->ips_count = 1; conn->ips = i_new(struct ip_addr, conn->ips_count); conn->ips[0] = ip; } else if (*dns_set.dns_client_socket_path == '\0') { ret = net_gethostbyname(conn->client->set.host, &ips, &ips_count); if (ret != 0) { i_error("imapc(%s): net_gethostbyname(%s) failed: %s", conn->name, conn->client->set.host, net_gethosterror(ret)); imapc_connection_set_disconnected(conn); return; } conn->ips_count = ips_count; conn->ips = i_new(struct ip_addr, ips_count); memcpy(conn->ips, ips, ips_count * sizeof(*ips)); } else { (void)dns_lookup(conn->client->set.host, &dns_set, imapc_connection_dns_callback, conn, &conn->dns_lookup); return; } imapc_connection_connect_next_ip(conn); } void imapc_connection_input_pending(struct imapc_connection *conn) { int ret = 1; if (conn->input == NULL) return; if (conn->to != NULL && !conn->idle_stopping) timeout_reset(conn->to); o_stream_cork(conn->output); while (ret > 0 && conn->input != NULL) { T_BEGIN { ret = imapc_connection_input_one(conn); } T_END; } if (conn->output != NULL) o_stream_uncork(conn->output); } static struct imapc_command * imapc_command_begin(imapc_command_callback_t *callback, void *context) { struct imapc_command *cmd; pool_t pool; i_assert(callback != NULL); pool = pool_alloconly_create("imapc command", 2048); cmd = p_new(pool, struct imapc_command, 1); cmd->pool = pool; cmd->callback = callback; cmd->context = context; /* use a globally unique tag counter so looking at rawlogs is somewhat easier */ if (++imapc_client_cmd_tag_counter == 0) imapc_client_cmd_tag_counter++; cmd->tag = imapc_client_cmd_tag_counter; return cmd; } static void imapc_command_free(struct imapc_command *cmd) { struct imapc_command_stream *stream; if (array_is_created(&cmd->streams)) { array_foreach_modifiable(&cmd->streams, stream) i_stream_unref(&stream->input); } pool_unref(&cmd->pool); } const char *imapc_command_get_tag(struct imapc_command *cmd) { return t_strdup_printf("%u", cmd->tag); } void imapc_command_abort(struct imapc_command **_cmd) { struct imapc_command *cmd = *_cmd; *_cmd = NULL; imapc_command_free(cmd); } static void imapc_command_timeout(struct imapc_connection *conn) { struct imapc_command *const *cmds; unsigned int count; cmds = array_get(&conn->cmd_wait_list, &count); i_assert(count > 0); imapc_connection_try_reconnect(conn, t_strdup_printf( "Command '%s' timed out", imapc_command_get_readable(cmds[0])), 0, FALSE); } static bool parse_sync_literal(const unsigned char *data, unsigned int pos, unsigned int *value_r) { unsigned int value = 0, mul = 1; /* data should contain "{size}\r\n" and pos points after \n */ if (pos <= 4 || data[pos-1] != '\n' || data[pos-2] != '\r' || data[pos-3] != '}' || !i_isdigit(data[pos-4])) return FALSE; pos -= 4; do { value += (data[pos] - '0') * mul; mul = mul*10; pos--; } while (pos > 0 && i_isdigit(data[pos])); if (pos == 0 || data[pos] != '{') return FALSE; *value_r = value; return TRUE; } static void imapc_command_send_finished(struct imapc_connection *conn, struct imapc_command *cmd) { struct imapc_command *const *cmdp; if (cmd->idle) conn->idle_plus_waiting = TRUE; /* everything sent. move command to wait list. */ cmdp = array_idx(&conn->cmd_send_queue, 0); i_assert(*cmdp == cmd); array_delete(&conn->cmd_send_queue, 0, 1); array_append(&conn->cmd_wait_list, &cmd, 1); /* send the next command in queue */ imapc_command_send_more(conn); } static struct imapc_command_stream * imapc_command_get_sending_stream(struct imapc_command *cmd) { struct imapc_command_stream *stream; if (!array_is_created(&cmd->streams) || array_count(&cmd->streams) == 0) return NULL; stream = array_idx_modifiable(&cmd->streams, 0); if (stream->pos != cmd->send_pos) return NULL; return stream; } static int imapc_command_try_send_stream(struct imapc_connection *conn, struct imapc_command *cmd) { struct imapc_command_stream *stream; stream = imapc_command_get_sending_stream(cmd); if (stream == NULL) return -1; /* we're sending the stream now */ o_stream_set_max_buffer_size(conn->output, 0); (void)o_stream_send_istream(conn->output, stream->input); o_stream_set_max_buffer_size(conn->output, (size_t)-1); if (!i_stream_is_eof(stream->input)) { o_stream_set_flush_pending(conn->output, TRUE); i_assert(stream->input->v_offset < stream->size); return 0; } i_assert(stream->input->v_offset == stream->size); /* finished with the stream */ i_stream_unref(&stream->input); array_delete(&cmd->streams, 0, 1); i_assert(cmd->send_pos != cmd->data->used); return 1; } static void imapc_connection_set_selecting(struct imapc_client_mailbox *box) { struct imapc_connection *conn = box->conn; i_assert(conn->selecting_box == NULL); if (conn->selected_box != NULL && (conn->capabilities & IMAPC_CAPABILITY_QRESYNC) != 0) { /* server will send a [CLOSED] once selected mailbox is closed */ conn->selecting_box = box; } else { /* we'll have to assume that all the future untagged messages are for the mailbox we're selecting */ conn->selected_box = box; } } static bool imapc_connection_is_throttled(struct imapc_connection *conn) { if (conn->to_throttle != NULL) timeout_remove(&conn->to_throttle); if (conn->throttle_msecs == 0) { /* we haven't received [THROTTLED] recently */ return FALSE; } if (array_count(&conn->cmd_wait_list) > 0) { /* wait until we have received the existing commands' tagged replies to see if we're still throttled */ return TRUE; } if (timeval_cmp(&ioloop_timeval, &conn->throttle_end_timeval) >= 0) { /* we reached the throttle timeout - send the next command */ conn->throttle_pending = FALSE; return FALSE; } /* we're still being throttled - wait for it to end */ conn->to_throttle = timeout_add_absolute(&conn->throttle_end_timeval, imapc_command_send_more, conn); return TRUE; } static void imapc_command_send_more(struct imapc_connection *conn) { struct imapc_command *const *cmds, *cmd; struct imapc_command_reply reply; const unsigned char *p, *data; unsigned int count, size; size_t seek_pos, start_pos, end_pos; int ret; if (imapc_connection_is_throttled(conn)) return; cmds = array_get(&conn->cmd_send_queue, &count); if (count == 0) return; cmd = cmds[0]; if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) == 0 && conn->state != IMAPC_CONNECTION_STATE_DONE) { /* wait until we're fully connected */ return; } if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0 && array_count(&conn->cmd_wait_list) > 0) { /* wait until existing commands have finished */ return; } if (cmd->wait_for_literal) { /* wait until we received '+' */ return; } i_assert(cmd->send_pos < cmd->data->used); if (cmd->box == NULL) { /* non-mailbox command */ } else if (cmd->send_pos == 0 && (cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0) { /* SELECT/EXAMINE command */ imapc_connection_set_selecting(cmd->box); } else if (!imapc_client_mailbox_is_opened(cmd->box)) { if (cmd->box->reconnecting) { /* wait for SELECT/EXAMINE */ return; } /* shouldn't normally happen */ i_zero(&reply); reply.text_without_resp = reply.text_full = "Mailbox not open"; reply.state = IMAPC_COMMAND_STATE_DISCONNECTED; array_delete(&conn->cmd_send_queue, 0, 1); imapc_command_reply_free(cmd, &reply); imapc_command_send_more(conn); return; } /* add timeout for commands if there's not one yet (pre-login has its own timeout) */ if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0) { /* LOGOUT has a shorter timeout */ if (conn->to != NULL) timeout_remove(&conn->to); conn->to = timeout_add(IMAPC_LOGOUT_TIMEOUT_MSECS, imapc_command_timeout, conn); } else if (conn->to == NULL) { conn->to = timeout_add(conn->client->set.cmd_timeout_msecs, imapc_command_timeout, conn); } timeout_reset(conn->to_output); if ((ret = imapc_command_try_send_stream(conn, cmd)) == 0) return; seek_pos = cmd->send_pos; if (seek_pos != 0 && ret < 0) { /* skip over the literal. we can also get here from AUTHENTICATE command, which doesn't use a literal */ if (parse_sync_literal(cmd->data->data, seek_pos, &size)) { seek_pos += size; i_assert(seek_pos <= cmd->data->used); } } do { start_pos = seek_pos; p = memchr(CONST_PTR_OFFSET(cmd->data->data, seek_pos), '\n', cmd->data->used - seek_pos); i_assert(p != NULL); seek_pos = p - (const unsigned char *)cmd->data->data + 1; /* keep going for LITERAL+ command */ } while (start_pos + 3 < seek_pos && p[-1] == '\r' && p[-2] == '}' && p[-3] == '+'); end_pos = seek_pos; data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos); size = end_pos - cmd->send_pos; o_stream_nsend(conn->output, data, size); cmd->send_pos = end_pos; if (cmd->send_pos == cmd->data->used) { i_assert(!array_is_created(&cmd->streams) || array_count(&cmd->streams) == 0); imapc_command_send_finished(conn, cmd); } else { cmd->wait_for_literal = TRUE; } } static void imapc_connection_send_idle_done(struct imapc_connection *conn) { if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) { conn->idle_stopping = TRUE; o_stream_nsend_str(conn->output, "DONE\r\n"); if (conn->to == NULL) { conn->to = timeout_add(conn->client->set.cmd_timeout_msecs, imapc_command_timeout, conn); } } } static void imapc_connection_cmd_send(struct imapc_command *cmd) { struct imapc_connection *conn = cmd->conn; imapc_connection_send_idle_done(conn); if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) != 0 && conn->state == IMAPC_CONNECTION_STATE_AUTHENTICATING) { /* pre-login commands get inserted before everything else */ array_insert(&conn->cmd_send_queue, 0, &cmd, 1); imapc_command_send_more(conn); return; } if ((cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0 && conn->selected_box == NULL) { /* reopening the mailbox. add it before other queued commands. */ array_insert(&conn->cmd_send_queue, 0, &cmd, 1); } else { array_append(&conn->cmd_send_queue, &cmd, 1); } imapc_command_send_more(conn); } static int imapc_connection_output(struct imapc_connection *conn) { struct imapc_command *const *cmds; unsigned int count; int ret; if (conn->to != NULL) timeout_reset(conn->to); o_stream_cork(conn->output); if ((ret = o_stream_flush(conn->output)) < 0) return 1; imapc_connection_ref(conn); cmds = array_get(&conn->cmd_send_queue, &count); if (count > 0) { if (imapc_command_get_sending_stream(cmds[0]) != NULL && !cmds[0]->wait_for_literal) { /* we're sending a stream. send more. */ imapc_command_send_more(conn); } } o_stream_uncork(conn->output); imapc_connection_unref(&conn); return ret; } struct imapc_command * imapc_connection_cmd(struct imapc_connection *conn, imapc_command_callback_t *callback, void *context) { struct imapc_command *cmd; cmd = imapc_command_begin(callback, context); cmd->conn = conn; return cmd; } void imapc_command_set_flags(struct imapc_command *cmd, enum imapc_command_flags flags) { cmd->flags = flags; } void imapc_command_set_mailbox(struct imapc_command *cmd, struct imapc_client_mailbox *box) { cmd->box = box; } bool imapc_command_connection_is_selected(struct imapc_command *cmd) { return cmd->conn->selected_box != NULL || cmd->conn->selecting_box != NULL; } void imapc_command_send(struct imapc_command *cmd, const char *cmd_str) { size_t len = strlen(cmd_str); cmd->data = str_new(cmd->pool, 6 + len + 2); str_printfa(cmd->data, "%u %s\r\n", cmd->tag, cmd_str); imapc_connection_cmd_send(cmd); } void imapc_command_sendf(struct imapc_command *cmd, const char *cmd_fmt, ...) { va_list args; va_start(args, cmd_fmt); imapc_command_sendvf(cmd, cmd_fmt, args); va_end(args); } void imapc_command_sendvf(struct imapc_command *cmd, const char *cmd_fmt, va_list args) { unsigned int i; cmd->data = str_new(cmd->pool, 128); str_printfa(cmd->data, "%u ", cmd->tag); for (i = 0; cmd_fmt[i] != '\0'; i++) { if (cmd_fmt[i] != '%') { str_append_c(cmd->data, cmd_fmt[i]); continue; } switch (cmd_fmt[++i]) { case '\0': i_unreached(); case 'u': { unsigned int arg = va_arg(args, unsigned int); str_printfa(cmd->data, "%u", arg); break; } case 'p': { struct istream *input = va_arg(args, struct istream *); struct imapc_command_stream *s; uoff_t size; if (!array_is_created(&cmd->streams)) p_array_init(&cmd->streams, cmd->pool, 2); if (i_stream_get_size(input, TRUE, &size) < 0) size = 0; str_printfa(cmd->data, "{%"PRIuUOFF_T"}\r\n", size); s = array_append_space(&cmd->streams); s->pos = str_len(cmd->data); s->size = size; s->input = input; i_stream_ref(input); break; } case 's': { const char *arg = va_arg(args, const char *); if (!need_literal(arg)) imap_append_quoted(cmd->data, arg); else if ((cmd->conn->capabilities & IMAPC_CAPABILITY_LITERALPLUS) != 0) { str_printfa(cmd->data, "{%"PRIuSIZE_T"+}\r\n%s", strlen(arg), arg); } else { str_printfa(cmd->data, "{%"PRIuSIZE_T"}\r\n%s", strlen(arg), arg); } break; } case '1': { /* %1s - no quoting */ const char *arg = va_arg(args, const char *); i++; i_assert(cmd_fmt[i] == 's'); str_append(cmd->data, arg); break; } } } str_append(cmd->data, "\r\n"); imapc_connection_cmd_send(cmd); } enum imapc_connection_state imapc_connection_get_state(struct imapc_connection *conn) { return conn->state; } enum imapc_capability imapc_connection_get_capabilities(struct imapc_connection *conn) { return conn->capabilities; } void imapc_connection_unselect(struct imapc_client_mailbox *box) { struct imapc_connection *conn = box->conn; if (conn->selected_box != NULL || conn->selecting_box != NULL) { i_assert(conn->selected_box == box || conn->selecting_box == box); conn->selected_box = NULL; conn->selecting_box = NULL; } imapc_connection_send_idle_done(conn); imapc_connection_abort_commands(conn, box, FALSE); } struct imapc_client_mailbox * imapc_connection_get_mailbox(struct imapc_connection *conn) { if (conn->selecting_box != NULL) return conn->selecting_box; return conn->selected_box; } static void imapc_connection_idle_callback(const struct imapc_command_reply *reply ATTR_UNUSED, void *context) { struct imapc_connection *conn = context; conn->idling = FALSE; conn->idle_plus_waiting = FALSE; conn->idle_stopping = FALSE; } void imapc_connection_idle(struct imapc_connection *conn) { struct imapc_command *cmd; if (array_count(&conn->cmd_send_queue) != 0 || array_count(&conn->cmd_wait_list) != 0 || conn->idling || conn->idle_plus_waiting || (conn->capabilities & IMAPC_CAPABILITY_IDLE) == 0) return; cmd = imapc_connection_cmd(conn, imapc_connection_idle_callback, conn); cmd->idle = TRUE; imapc_command_send(cmd, "IDLE"); } dovecot-2.2.33.2/src/lib-imap-client/imapc-client.c0000644000175000017500000003773213165463624016621 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "ioloop.h" #include "safe-mkstemp.h" #include "iostream-ssl.h" #include "imapc-msgmap.h" #include "imapc-connection.h" #include "imapc-client-private.h" #include const char *imapc_command_state_names[] = { "OK", "NO", "BAD", "(auth failed)", "(disconnected)" }; const struct imapc_capability_name imapc_capability_names[] = { { "SASL-IR", IMAPC_CAPABILITY_SASL_IR }, { "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS }, { "QRESYNC", IMAPC_CAPABILITY_QRESYNC }, { "IDLE", IMAPC_CAPABILITY_IDLE }, { "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS }, { "AUTH=PLAIN", IMAPC_CAPABILITY_AUTH_PLAIN }, { "STARTTLS", IMAPC_CAPABILITY_STARTTLS }, { "X-GM-EXT-1", IMAPC_CAPABILITY_X_GM_EXT_1 }, { "CONDSTORE", IMAPC_CAPABILITY_CONDSTORE }, { "NAMESPACE", IMAPC_CAPABILITY_NAMESPACE }, { "UNSELECT", IMAPC_CAPABILITY_UNSELECT }, { "ESEARCH", IMAPC_CAPABILITY_ESEARCH }, { "WITHIN", IMAPC_CAPABILITY_WITHIN }, { "QUOTA", IMAPC_CAPABILITY_QUOTA }, { "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 }, { NULL, 0 } }; unsigned int imapc_client_cmd_tag_counter = 0; static void default_untagged_callback(const struct imapc_untagged_reply *reply ATTR_UNUSED, void *context ATTR_UNUSED) { } struct imapc_client * imapc_client_init(const struct imapc_client_settings *set) { struct imapc_client *client; struct ssl_iostream_settings ssl_set; const char *error; pool_t pool; i_assert(set->connect_retry_count == 0 || set->connect_retry_interval_msecs > 0); pool = pool_alloconly_create("imapc client", 1024); client = p_new(pool, struct imapc_client, 1); client->pool = pool; client->refcount = 1; client->set.debug = set->debug; client->set.host = p_strdup(pool, set->host); client->set.port = set->port; client->set.master_user = p_strdup_empty(pool, set->master_user); client->set.username = p_strdup(pool, set->username); client->set.password = p_strdup(pool, set->password); client->set.sasl_mechanisms = p_strdup(pool, set->sasl_mechanisms); client->set.use_proxyauth = set->use_proxyauth; client->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); client->set.temp_path_prefix = p_strdup(pool, set->temp_path_prefix); client->set.rawlog_dir = p_strdup(pool, set->rawlog_dir); client->set.max_idle_time = set->max_idle_time; client->set.connect_timeout_msecs = set->connect_timeout_msecs != 0 ? set->connect_timeout_msecs : IMAPC_DEFAULT_CONNECT_TIMEOUT_MSECS; client->set.connect_retry_count = set->connect_retry_count; client->set.connect_retry_interval_msecs = set->connect_retry_interval_msecs; client->set.cmd_timeout_msecs = set->cmd_timeout_msecs != 0 ? set->cmd_timeout_msecs : IMAPC_DEFAULT_COMMAND_TIMEOUT_MSECS; client->set.max_line_length = set->max_line_length != 0 ? set->max_line_length : IMAPC_DEFAULT_MAX_LINE_LENGTH; client->set.throttle_set = set->throttle_set; if (client->set.throttle_set.init_msecs == 0) client->set.throttle_set.init_msecs = IMAPC_THROTTLE_DEFAULT_INIT_MSECS; if (client->set.throttle_set.max_msecs == 0) client->set.throttle_set.max_msecs = IMAPC_THROTTLE_DEFAULT_MAX_MSECS; if (client->set.throttle_set.shrink_min_msecs == 0) client->set.throttle_set.shrink_min_msecs = IMAPC_THROTTLE_DEFAULT_SHRINK_MIN_MSECS; if (set->ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE) { client->set.ssl_mode = set->ssl_mode; client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir); client->set.ssl_ca_file = p_strdup(pool, set->ssl_ca_file); client->set.ssl_verify = set->ssl_verify; i_zero(&ssl_set); ssl_set.ca_dir = set->ssl_ca_dir; ssl_set.ca_file = set->ssl_ca_file; ssl_set.verify_remote_cert = set->ssl_verify; ssl_set.crypto_device = set->ssl_crypto_device; if (ssl_iostream_context_init_client(&ssl_set, &client->ssl_ctx, &error) < 0) { i_error("imapc(%s:%u): Couldn't initialize SSL context: %s", set->host, set->port, error); } } client->untagged_callback = default_untagged_callback; p_array_init(&client->conns, pool, 8); return client; } void imapc_client_ref(struct imapc_client *client) { i_assert(client->refcount > 0); client->refcount++; } void imapc_client_unref(struct imapc_client **_client) { struct imapc_client *client = *_client; *_client = NULL; i_assert(client->refcount > 0); if (--client->refcount > 0) return; if (client->ssl_ctx != NULL) ssl_iostream_context_deinit(&client->ssl_ctx); pool_unref(&client->pool); } void imapc_client_disconnect(struct imapc_client *client) { struct imapc_client_connection *const *conns, *conn; unsigned int i, count; conns = array_get(&client->conns, &count); for (i = count; i > 0; i--) { conn = conns[i-1]; array_delete(&client->conns, i-1, 1); i_assert(imapc_connection_get_mailbox(conn->conn) == NULL); imapc_connection_deinit(&conn->conn); i_free(conn); } } void imapc_client_deinit(struct imapc_client **_client) { struct imapc_client *client = *_client; imapc_client_disconnect(client); imapc_client_unref(_client); } void imapc_client_register_untagged(struct imapc_client *client, imapc_untagged_callback_t *callback, void *context) { client->untagged_callback = callback; client->untagged_context = context; } static void imapc_client_run_pre(struct imapc_client *client) { struct imapc_client_connection *const *connp; struct ioloop *prev_ioloop = current_ioloop; i_assert(client->ioloop == NULL); client->ioloop = io_loop_create(); io_loop_set_running(client->ioloop); array_foreach(&client->conns, connp) { imapc_connection_ioloop_changed((*connp)->conn); if (imapc_connection_get_state((*connp)->conn) == IMAPC_CONNECTION_STATE_DISCONNECTED) imapc_connection_connect((*connp)->conn); } if (io_loop_is_running(client->ioloop)) io_loop_run(client->ioloop); io_loop_set_current(prev_ioloop); } static void imapc_client_run_post(struct imapc_client *client) { struct imapc_client_connection *const *connp; struct ioloop *ioloop = client->ioloop; client->ioloop = NULL; array_foreach(&client->conns, connp) imapc_connection_ioloop_changed((*connp)->conn); io_loop_set_current(ioloop); io_loop_destroy(&ioloop); } void imapc_client_run(struct imapc_client *client) { imapc_client_run_pre(client); imapc_client_run_post(client); } void imapc_client_stop(struct imapc_client *client) { if (client->ioloop != NULL) io_loop_stop(client->ioloop); } void imapc_client_try_stop(struct imapc_client *client) { struct imapc_client_connection *const *connp; array_foreach(&client->conns, connp) if (imapc_connection_get_state((*connp)->conn) != IMAPC_CONNECTION_STATE_DISCONNECTED) return; imapc_client_stop(client); } bool imapc_client_is_running(struct imapc_client *client) { return client->ioloop != NULL; } static void imapc_client_login_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_client_connection *conn = context; struct imapc_client *client = conn->client; struct imapc_client_mailbox *box = conn->box; if (box != NULL && box->reconnecting) { box->reconnecting = FALSE; if (reply->state == IMAPC_COMMAND_STATE_OK) { /* reopen the mailbox */ box->reopen_callback(box->reopen_context); } else { imapc_connection_abort_commands(box->conn, NULL, FALSE); } } /* call the login callback only once */ if (client->login_callback != NULL) { imapc_command_callback_t *callback = client->login_callback; void *context = client->login_context; client->login_callback = NULL; client->login_context = NULL; callback(reply, context); } } static struct imapc_client_connection * imapc_client_add_connection(struct imapc_client *client) { struct imapc_client_connection *conn; conn = i_new(struct imapc_client_connection, 1); conn->client = client; conn->conn = imapc_connection_init(client, imapc_client_login_callback, conn); array_append(&client->conns, &conn, 1); return conn; } static struct imapc_connection * imapc_client_find_connection(struct imapc_client *client) { struct imapc_client_connection *const *connp; /* FIXME: stupid algorithm */ if (array_count(&client->conns) == 0) return imapc_client_add_connection(client)->conn; connp = array_idx(&client->conns, 0); return (*connp)->conn; } struct imapc_command * imapc_client_cmd(struct imapc_client *client, imapc_command_callback_t *callback, void *context) { struct imapc_connection *conn; conn = imapc_client_find_connection(client); return imapc_connection_cmd(conn, callback, context); } static struct imapc_client_connection * imapc_client_get_unboxed_connection(struct imapc_client *client) { struct imapc_client_connection *const *conns; unsigned int i, count; conns = array_get(&client->conns, &count); for (i = 0; i < count; i++) { if (conns[i]->box == NULL) return conns[i]; } return imapc_client_add_connection(client); } void imapc_client_login(struct imapc_client *client) { struct imapc_client_connection *conn; i_assert(client->login_callback); i_assert(array_count(&client->conns) == 0); conn = imapc_client_add_connection(client); imapc_connection_connect(conn->conn); } struct imapc_logout_ctx { struct imapc_client *client; unsigned int logout_count; }; static void imapc_client_logout_callback(const struct imapc_command_reply *reply ATTR_UNUSED, void *context) { struct imapc_logout_ctx *ctx = context; i_assert(ctx->logout_count > 0); if (--ctx->logout_count == 0) imapc_client_stop(ctx->client); } void imapc_client_logout(struct imapc_client *client) { struct imapc_logout_ctx ctx = { .client = client }; struct imapc_client_connection *const *connp; struct imapc_command *cmd; client->logging_out = TRUE; /* send LOGOUT to all connections */ array_foreach(&client->conns, connp) { if (imapc_connection_get_state((*connp)->conn) == IMAPC_CONNECTION_STATE_DISCONNECTED) continue; imapc_connection_set_no_reconnect((*connp)->conn); ctx.logout_count++; cmd = imapc_connection_cmd((*connp)->conn, imapc_client_logout_callback, &ctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN | IMAPC_COMMAND_FLAG_LOGOUT); imapc_command_send(cmd, "LOGOUT"); } /* wait for LOGOUT to finish */ while (ctx.logout_count > 0) imapc_client_run(client); /* we should have disconnected all clients already, but if there were any timeouts there may be some clients left. */ imapc_client_disconnect(client); } struct imapc_client_mailbox * imapc_client_mailbox_open(struct imapc_client *client, void *untagged_box_context) { struct imapc_client_mailbox *box; struct imapc_client_connection *conn; box = i_new(struct imapc_client_mailbox, 1); box->client = client; box->untagged_box_context = untagged_box_context; conn = imapc_client_get_unboxed_connection(client); conn->box = box; box->conn = conn->conn; box->msgmap = imapc_msgmap_init(); /* if we get disconnected before the SELECT is finished, allow one reconnect retry. */ box->reconnect_ok = TRUE; return box; } void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box, void (*callback)(void *context), void *context) { box->reopen_callback = callback; box->reopen_context = context; } bool imapc_client_mailbox_can_reconnect(struct imapc_client_mailbox *box) { /* the reconnect_ok flag attempts to avoid infinite reconnection loops to a server that keeps disconnecting us (e.g. some of the commands we send keeps crashing it always) */ return box->reopen_callback != NULL && box->reconnect_ok; } void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box, const char *errmsg) { i_assert(!box->reconnecting); imapc_connection_try_reconnect(box->conn, errmsg, 0, FALSE); } void imapc_client_mailbox_close(struct imapc_client_mailbox **_box) { struct imapc_client_mailbox *box = *_box; struct imapc_client_connection *const *connp; box->closing = TRUE; /* cancel any pending commands */ imapc_connection_unselect(box); if (box->reconnecting) { /* need to abort the reconnection so it won't try to access the box */ imapc_connection_disconnect(box->conn); } /* set this only after unselect, which may cancel some commands that reference this box */ *_box = NULL; array_foreach(&box->client->conns, connp) { if ((*connp)->box == box) { (*connp)->box = NULL; break; } } imapc_msgmap_deinit(&box->msgmap); if (box->to_send_idle != NULL) timeout_remove(&box->to_send_idle); i_free(box); } struct imapc_command * imapc_client_mailbox_cmd(struct imapc_client_mailbox *box, imapc_command_callback_t *callback, void *context) { struct imapc_command *cmd; i_assert(!box->closing); cmd = imapc_connection_cmd(box->conn, callback, context); imapc_command_set_mailbox(cmd, box); return cmd; } struct imapc_msgmap * imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box) { return box->msgmap; } static void imapc_client_mailbox_idle_send(struct imapc_client_mailbox *box) { timeout_remove(&box->to_send_idle); if (imapc_client_mailbox_is_opened(box)) imapc_connection_idle(box->conn); } void imapc_client_mailbox_idle(struct imapc_client_mailbox *box) { /* send the IDLE with a delay to avoid unnecessary IDLEs that are immediately aborted */ if (box->to_send_idle == NULL && imapc_client_mailbox_is_opened(box)) { box->to_send_idle = timeout_add_short(IMAPC_CLIENT_IDLE_SEND_DELAY_MSECS, imapc_client_mailbox_idle_send, box); } /* we're done with all work at this point. */ box->reconnect_ok = TRUE; } bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box) { struct imapc_client_mailbox *selected_box; if (box->closing || imapc_connection_get_state(box->conn) != IMAPC_CONNECTION_STATE_DONE) return FALSE; selected_box = imapc_connection_get_mailbox(box->conn); if (selected_box != box) { if (selected_box != NULL) i_error("imapc: Selected mailbox changed unexpectedly"); return FALSE; } return TRUE; } static bool imapc_client_get_any_capabilities(struct imapc_client *client, enum imapc_capability *capabilities_r) { struct imapc_client_connection *const *connp; struct imapc_connection *conn = NULL; array_foreach(&client->conns, connp) { conn = (*connp)->conn; if (imapc_connection_get_state(conn) == IMAPC_CONNECTION_STATE_DONE) { *capabilities_r = imapc_connection_get_capabilities(conn); return TRUE; } } return FALSE; } int imapc_client_get_capabilities(struct imapc_client *client, enum imapc_capability *capabilities_r) { /* try to find a connection that is already logged in */ if (imapc_client_get_any_capabilities(client, capabilities_r)) return 0; /* if there are no connections yet, create one */ if (array_count(&client->conns) == 0) (void)imapc_client_add_connection(client); /* wait for any of the connections to login */ client->stop_on_state_finish = TRUE; imapc_client_run(client); client->stop_on_state_finish = FALSE; if (imapc_client_get_any_capabilities(client, capabilities_r)) return 0; /* failed */ return -1; } int imapc_client_create_temp_fd(struct imapc_client *client, const char **path_r) { string_t *path; int fd; if (client->set.temp_path_prefix == NULL) { i_error("imapc: temp_path_prefix not set, " "can't create temp file"); return -1; } path = t_str_new(128); str_append(path, client->set.temp_path_prefix); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } void imapc_client_register_state_change_callback(struct imapc_client *client, imapc_state_change_callback_t *cb, void *context) { i_assert(client->state_change_callback == NULL); i_assert(client->state_change_context == NULL); client->state_change_callback = cb; client->state_change_context = context; } void imapc_client_set_login_callback(struct imapc_client *client, imapc_command_callback_t *callback, void *context) { client->login_callback = callback; client->login_context = context; } dovecot-2.2.33.2/src/lib-imap-client/imapc-client.h0000644000175000017500000002026013165463624016612 00000000000000#ifndef IMAPC_CLIENT_H #define IMAPC_CLIENT_H #include "net.h" /* IMAP RFC defines this to be at least 30 minutes. */ #define IMAPC_DEFAULT_MAX_IDLE_TIME (60*29) enum imapc_command_state { IMAPC_COMMAND_STATE_OK = 0, IMAPC_COMMAND_STATE_NO, IMAPC_COMMAND_STATE_BAD, /* Authentication to IMAP server failed (NO or BAD) */ IMAPC_COMMAND_STATE_AUTH_FAILED, /* Client was unexpectedly disconnected. */ IMAPC_COMMAND_STATE_DISCONNECTED }; extern const char *imapc_command_state_names[]; enum imapc_capability { IMAPC_CAPABILITY_SASL_IR = 0x01, IMAPC_CAPABILITY_LITERALPLUS = 0x02, IMAPC_CAPABILITY_QRESYNC = 0x04, IMAPC_CAPABILITY_IDLE = 0x08, IMAPC_CAPABILITY_UIDPLUS = 0x10, IMAPC_CAPABILITY_AUTH_PLAIN = 0x20, IMAPC_CAPABILITY_STARTTLS = 0x40, IMAPC_CAPABILITY_X_GM_EXT_1 = 0x80, IMAPC_CAPABILITY_CONDSTORE = 0x100, IMAPC_CAPABILITY_NAMESPACE = 0x200, IMAPC_CAPABILITY_UNSELECT = 0x400, IMAPC_CAPABILITY_ESEARCH = 0x800, IMAPC_CAPABILITY_WITHIN = 0x1000, IMAPC_CAPABILITY_QUOTA = 0x2000, IMAPC_CAPABILITY_IMAP4REV1 = 0x40000000 }; struct imapc_capability_name { const char *name; enum imapc_capability capability; }; extern const struct imapc_capability_name imapc_capability_names[]; enum imapc_command_flags { /* The command changes the selected mailbox (SELECT, EXAMINE) */ IMAPC_COMMAND_FLAG_SELECT = 0x01, /* The command is sent to server before login (or is the login command itself). Non-prelogin commands will be queued until login is successful. */ IMAPC_COMMAND_FLAG_PRELOGIN = 0x02, /* Allow command to be automatically retried if disconnected before it finishes. */ IMAPC_COMMAND_FLAG_RETRIABLE = 0x04, /* This is the LOGOUT command. Use a small timeout for it. */ IMAPC_COMMAND_FLAG_LOGOUT = 0x08, /* Command is being resent after a reconnection. */ IMAPC_COMMAND_FLAG_RECONNECTED = 0x10 }; enum imapc_client_ssl_mode { IMAPC_CLIENT_SSL_MODE_NONE, IMAPC_CLIENT_SSL_MODE_IMMEDIATE, IMAPC_CLIENT_SSL_MODE_STARTTLS }; #define IMAPC_DEFAULT_CONNECT_TIMEOUT_MSECS (1000*30) #define IMAPC_DEFAULT_COMMAND_TIMEOUT_MSECS (1000*60*5) #define IMAPC_DEFAULT_MAX_LINE_LENGTH ((size_t)-1) struct imapc_throttling_settings { unsigned int init_msecs; unsigned int max_msecs; unsigned int shrink_min_msecs; }; struct imapc_client_settings { const char *host; in_port_t port; const char *master_user; const char *username; const char *password; /* Space-separated list of SASL mechanisms to try (in the specified order). The default is to use only LOGIN command or SASL PLAIN. */ const char *sasl_mechanisms; bool use_proxyauth; /* Use Sun/Oracle PROXYAUTH command */ unsigned int max_idle_time; const char *dns_client_socket_path; const char *temp_path_prefix; enum imapc_client_ssl_mode ssl_mode; const char *ssl_ca_dir, *ssl_ca_file; bool ssl_verify; const char *rawlog_dir; const char *ssl_crypto_device; bool debug; /* Timeout for logging in. 0 = default. */ unsigned int connect_timeout_msecs; /* Number of retries, -1 = infinity */ unsigned int connect_retry_count; /* Interval between retries, must be > 0 if retries > 0 */ unsigned int connect_retry_interval_msecs; /* Timeout for IMAP commands. Reset every time more data is being sent or received. 0 = default. */ unsigned int cmd_timeout_msecs; /* Maximum allowed line length (not including literals read as streams). 0 = unlimited. */ size_t max_line_length; struct imapc_throttling_settings throttle_set; }; struct imapc_command_reply { enum imapc_command_state state; /* "[RESP TEXT]" produces key=RESP, value=TEXT. "[RESP]" produces key=RESP, value=NULL otherwise both are NULL */ const char *resp_text_key, *resp_text_value; /* The full tagged reply, including [RESP TEXT]. */ const char *text_full; /* Tagged reply text without [RESP TEXT] */ const char *text_without_resp; }; struct imapc_arg_file { /* file descriptor containing the value */ int fd; /* parent_arg.list[list_idx] points to the IMAP_ARG_LITERAL_SIZE argument */ const struct imap_arg *parent_arg; unsigned int list_idx; }; struct imapc_untagged_reply { /* name of the untagged reply, e.g. EXISTS */ const char *name; /* number at the beginning of the reply, or 0 if there wasn't any. Set for EXISTS, EXPUNGE, etc. */ uint32_t num; /* the rest of the reply can be read from these args. */ const struct imap_arg *args; /* arguments whose contents are stored into files. only "FETCH (BODY[" arguments can be here. */ const struct imapc_arg_file *file_args; unsigned int file_args_count; /* "* OK [RESP TEXT]" produces key=RESP, value=TEXT. "* OK [RESP]" produces key=RESP, value=NULL otherwise both are NULL */ const char *resp_text_key, *resp_text_value; /* If this reply occurred while a mailbox was selected, this contains the mailbox's untagged_context. */ void *untagged_box_context; }; enum imapc_state_change_event { IMAPC_STATE_CHANGE_AUTH_OK, IMAPC_STATE_CHANGE_AUTH_FAILED, }; /* Called when tagged reply is received for command. */ typedef void imapc_command_callback_t(const struct imapc_command_reply *reply, void *context); /* Called each time untagged input is received. */ typedef void imapc_untagged_callback_t(const struct imapc_untagged_reply *reply, void *context); typedef void imapc_state_change_callback_t(void *context, enum imapc_state_change_event event, const char *error); struct imapc_client * imapc_client_init(const struct imapc_client_settings *set); void imapc_client_disconnect(struct imapc_client *client); void imapc_client_deinit(struct imapc_client **client); /* Set login callback, must be set before calling other commands. This is called only for the first login, not for any reconnects or if there are multiple connections created. */ void imapc_client_set_login_callback(struct imapc_client *client, imapc_command_callback_t *callback, void *context); /* Explicitly login to server (also done automatically). */ void imapc_client_login(struct imapc_client *client); /* Send a LOGOUT and wait for disconnection. */ void imapc_client_logout(struct imapc_client *client); struct imapc_command * imapc_client_cmd(struct imapc_client *client, imapc_command_callback_t *callback, void *context); void imapc_command_set_flags(struct imapc_command *cmd, enum imapc_command_flags flags); bool imapc_command_connection_is_selected(struct imapc_command *cmd); void imapc_command_send(struct imapc_command *cmd, const char *cmd_str); void imapc_command_sendf(struct imapc_command *cmd, const char *cmd_fmt, ...) ATTR_FORMAT(2, 3); void imapc_command_sendvf(struct imapc_command *cmd, const char *cmd_fmt, va_list args) ATTR_FORMAT(2, 0); const char *imapc_command_get_tag(struct imapc_command *cmd); void imapc_command_abort(struct imapc_command **cmd); void imapc_client_register_untagged(struct imapc_client *client, imapc_untagged_callback_t *callback, void *context); void imapc_client_run(struct imapc_client *client); void imapc_client_stop(struct imapc_client *client); bool imapc_client_is_running(struct imapc_client *client); struct imapc_client_mailbox * imapc_client_mailbox_open(struct imapc_client *client, void *untagged_box_context); void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box, void (*callback)(void *context), void *context); void imapc_client_mailbox_close(struct imapc_client_mailbox **box); bool imapc_client_mailbox_can_reconnect(struct imapc_client_mailbox *box); void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box, const char *errmsg); struct imapc_command * imapc_client_mailbox_cmd(struct imapc_client_mailbox *box, imapc_command_callback_t *callback, void *context); struct imapc_msgmap * imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box); void imapc_client_mailbox_idle(struct imapc_client_mailbox *box); bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box); int imapc_client_get_capabilities(struct imapc_client *client, enum imapc_capability *capabilities_r); int imapc_client_create_temp_fd(struct imapc_client *client, const char **path_r); void imapc_client_register_state_change_callback(struct imapc_client *client, imapc_state_change_callback_t *cb, void *context); #endif dovecot-2.2.33.2/src/lib-imap-client/Makefile.am0000644000175000017500000000223113123174404016113 00000000000000noinst_LTLIBRARIES = libimap_client.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap libimap_client_la_SOURCES = \ imapc-client.c \ imapc-connection.c \ imapc-msgmap.c headers = \ imapc-client.h \ imapc-client-private.h \ imapc-connection.h \ imapc-msgmap.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-imapc-client noinst_PROGRAMS = $(test_programs) test_deps = \ $(noinst_LTLIBRARIES) \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-sasl/libsasl.la \ ../lib-imap/libimap.la \ ../lib-mail/libmail.la \ ../lib-charset/libcharset.la \ ../lib-dns/libdns.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_libs = \ $(test_deps) \ $(MODULE_LIBS) test_imapc_client_SOURCES = test-imapc-client.c test_imapc_client_LDADD = $(test_libs) test_imapc_client_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/stats/0002755000175000017500000000000013172375613012344 500000000000000dovecot-2.2.33.2/src/stats/Makefile.in0000644000175000017500000005657513172375575014360 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = stats$(EXEEXT) subdir = src/stats ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_stats_OBJECTS = client.$(OBJEXT) client-export.$(OBJEXT) \ client-reset.$(OBJEXT) fifo-input-connection.$(OBJEXT) \ global-memory.$(OBJEXT) mail-command.$(OBJEXT) \ mail-domain.$(OBJEXT) mail-ip.$(OBJEXT) mail-session.$(OBJEXT) \ mail-stats.$(OBJEXT) mail-user.$(OBJEXT) main.$(OBJEXT) \ stats-carbon.$(OBJEXT) stats-settings.$(OBJEXT) stats_OBJECTS = $(am_stats_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(stats_SOURCES) DIST_SOURCES = $(stats_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ stats_moduledir = $(moduledir)/stats AM_CPPFLAGS = \ -DSTATS_MODULE_DIR=\""$(stats_moduledir)"\" \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-stats stats_LDADD = $(LIBDOVECOT) stats_DEPENDENCIES = $(LIBDOVECOT_DEPS) stats_SOURCES = \ client.c \ client-export.c \ client-reset.c \ fifo-input-connection.c \ global-memory.c \ mail-command.c \ mail-domain.c \ mail-ip.c \ mail-session.c \ mail-stats.c \ mail-user.c \ main.c \ stats-carbon.c \ stats-settings.c noinst_HEADERS = \ client.h \ client-export.h \ client-reset.h \ fifo-input-connection.h \ global-memory.h \ mail-command.h \ mail-domain.h \ mail-ip.h \ mail-session.h \ mail-stats.h \ mail-user.h \ stats-carbon.h \ stats-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/stats/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/stats/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list stats$(EXEEXT): $(stats_OBJECTS) $(stats_DEPENDENCIES) $(EXTRA_stats_DEPENDENCIES) @rm -f stats$(EXEEXT) $(AM_V_CCLD)$(LINK) $(stats_OBJECTS) $(stats_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-export.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-reset.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fifo-input-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global-memory.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-command.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-domain.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-ip.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-stats.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-user.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-carbon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-settings.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/stats/stats-settings.h0000644000175000017500000000072313123174404015420 00000000000000#ifndef STATS_SETTINGS_H #define STATS_SETTINGS_H struct stats_settings { uoff_t memory_limit; unsigned int command_min_time; unsigned int session_min_time; unsigned int user_min_time; unsigned int domain_min_time; unsigned int ip_min_time; unsigned int carbon_interval; const char *carbon_server; const char *carbon_name; }; extern const struct setting_parser_info stats_setting_parser_info; extern const struct stats_settings *stats_settings; #endif dovecot-2.2.33.2/src/stats/mail-command.h0000644000175000017500000000072713123174404014766 00000000000000#ifndef MAIL_COMMAND_H #define MAIL_COMMAND_H struct mail_command; extern struct mail_command *stable_mail_commands_head; extern struct mail_command *stable_mail_commands_tail; int mail_command_update_parse(const char *const *args, const char **error_r); void mail_command_ref(struct mail_command *cmd); void mail_command_unref(struct mail_command **cmd); void mail_commands_free_memory(void); void mail_commands_init(void); void mail_commands_deinit(void); #endif dovecot-2.2.33.2/src/stats/fifo-input-connection.h0000644000175000017500000000040613123174404016637 00000000000000#ifndef FIFO_INPUT_CONNECTION_H #define FIFO_INPUT_CONNECTION_H struct fifo_input_connection *fifo_input_connection_create(int fd); void fifo_input_connection_destroy(struct fifo_input_connection **conn); void fifo_input_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/stats/global-memory.c0000644000175000017500000000221713123174404015165 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-command.h" #include "mail-session.h" #include "mail-user.h" #include "mail-domain.h" #include "mail-ip.h" #include "stats-settings.h" #include "global-memory.h" size_t global_used_memory = 0; static bool global_memory_free_something(void) { size_t orig_used_memory = global_used_memory; mail_commands_free_memory(); if (global_used_memory > stats_settings->memory_limit) mail_sessions_free_memory(); if (global_used_memory > stats_settings->memory_limit) mail_users_free_memory(); if (global_used_memory > stats_settings->memory_limit) mail_ips_free_memory(); if (global_used_memory > stats_settings->memory_limit) mail_domains_free_memory(); return global_used_memory < orig_used_memory; } void global_memory_alloc(size_t size) { i_assert(size < (size_t)-1 - global_used_memory); global_used_memory += size; while (global_used_memory > stats_settings->memory_limit) { if (!global_memory_free_something()) break; } } void global_memory_free(size_t size) { i_assert(size <= global_used_memory); global_used_memory -= size; } dovecot-2.2.33.2/src/stats/mail-domain.h0000644000175000017500000000122113123174404014605 00000000000000#ifndef MAIL_DOMAIN_H #define MAIL_DOMAIN_H struct stats; extern struct mail_domain *stable_mail_domains; struct mail_domain *mail_domain_login_create(const char *name); void mail_domain_login(struct mail_domain *domain); void mail_domain_disconnected(struct mail_domain *domain); struct mail_domain *mail_domain_lookup(const char *name); void mail_domain_refresh(struct mail_domain *domain, const struct stats *diff_stats) ATTR_NULL(2); void mail_domain_ref(struct mail_domain *domain); void mail_domain_unref(struct mail_domain **domain); void mail_domains_free_memory(void); void mail_domains_init(void); void mail_domains_deinit(void); #endif dovecot-2.2.33.2/src/stats/mail-user.h0000644000175000017500000000117613123174404014325 00000000000000#ifndef MAIL_USER_H #define MAIL_USER_H struct stats; extern struct mail_user *stable_mail_users; struct mail_user *mail_user_login(const char *username); void mail_user_disconnected(struct mail_user *user); struct mail_user *mail_user_lookup(const char *username); void mail_user_refresh(struct mail_user *user, const struct stats *diff_stats) ATTR_NULL(2); int mail_user_add_parse(const char *const *args, const char **error_r); void mail_user_ref(struct mail_user *user); void mail_user_unref(struct mail_user **user); void mail_users_free_memory(void); void mail_users_init(void); void mail_users_deinit(void); #endif dovecot-2.2.33.2/src/stats/client-reset.h0000644000175000017500000000025013123174404015015 00000000000000#ifndef CLIENT_RESET_H #define CLIENT_RESET_H struct client; int client_stats_reset(struct client *client, const char *const *args, const char **error_r); #endif dovecot-2.2.33.2/src/stats/mail-user.c0000644000175000017500000001107113165463624014326 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "ioloop.h" #include "hash.h" #include "llist.h" #include "base64.h" #include "global-memory.h" #include "stats-settings.h" #include "mail-stats.h" #include "mail-domain.h" #include "mail-user.h" static HASH_TABLE(char *, struct mail_user *) mail_users_hash; /* users are sorted by their last_update timestamp, oldest first */ static struct mail_user *mail_users_head, *mail_users_tail; struct mail_user *stable_mail_users; static size_t mail_user_memsize(const struct mail_user *user) { return sizeof(*user) + strlen(user->name) + 1; } struct mail_user *mail_user_login(const char *username) { struct mail_user *user; const char *domain; user = hash_table_lookup(mail_users_hash, username); if (user != NULL) { mail_user_refresh(user, NULL); return user; } domain = strchr(username, '@'); if (domain != NULL) domain++; else domain = ""; user = i_malloc(MALLOC_ADD(sizeof(struct mail_user), stats_alloc_size())); user->stats = (void *)(user + 1); user->name = i_strdup(username); user->reset_timestamp = ioloop_time; user->domain = mail_domain_login_create(domain); hash_table_insert(mail_users_hash, user->name, user); DLLIST_PREPEND_FULL(&stable_mail_users, user, stable_prev, stable_next); DLLIST2_APPEND_FULL(&mail_users_head, &mail_users_tail, user, sorted_prev, sorted_next); DLLIST_PREPEND_FULL(&user->domain->users, user, domain_prev, domain_next); mail_domain_ref(user->domain); user->last_update = ioloop_timeval; global_memory_alloc(mail_user_memsize(user)); return user; } void mail_user_disconnected(struct mail_user *user) { mail_domain_disconnected(user->domain); } struct mail_user *mail_user_lookup(const char *username) { return hash_table_lookup(mail_users_hash, username); } void mail_user_ref(struct mail_user *user) { user->refcount++; } void mail_user_unref(struct mail_user **_user) { struct mail_user *user = *_user; i_assert(user->refcount > 0); user->refcount--; *_user = NULL; } static void mail_user_free(struct mail_user *user) { i_assert(user->refcount == 0); i_assert(user->sessions == NULL); global_memory_free(mail_user_memsize(user)); hash_table_remove(mail_users_hash, user->name); DLLIST_REMOVE_FULL(&stable_mail_users, user, stable_prev, stable_next); DLLIST2_REMOVE_FULL(&mail_users_head, &mail_users_tail, user, sorted_prev, sorted_next); DLLIST_REMOVE_FULL(&user->domain->users, user, domain_prev, domain_next); mail_domain_unref(&user->domain); i_free(user->name); i_free(user); } void mail_user_refresh(struct mail_user *user, const struct stats *diff_stats) { if (diff_stats != NULL) stats_add(user->stats, diff_stats); user->last_update = ioloop_timeval; DLLIST2_REMOVE_FULL(&mail_users_head, &mail_users_tail, user, sorted_prev, sorted_next); DLLIST2_APPEND_FULL(&mail_users_head, &mail_users_tail, user, sorted_prev, sorted_next); mail_domain_refresh(user->domain, diff_stats); } int mail_user_add_parse(const char *const *args, const char **error_r) { struct mail_user *user; struct stats *empty_stats, *diff_stats; buffer_t *buf; const char *service, *error; /* */ if (str_array_length(args) < 3) { *error_r = "ADD-USER: Too few parameters"; return -1; } user = mail_user_login(args[0]); service = args[1]; buf = buffer_create_dynamic(pool_datastack_create(), 256); if (base64_decode(args[2], strlen(args[2]), NULL, buf) < 0) { *error_r = t_strdup_printf("ADD-USER %s %s: Invalid base64 input", user->name, service); return -1; } empty_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); if (!stats_import(buf->data, buf->used, empty_stats, diff_stats, &error)) { *error_r = t_strdup_printf("ADD-USER %s %s: %s", user->name, service, error); return -1; } mail_user_refresh(user, diff_stats); return 0; } void mail_users_free_memory(void) { unsigned int diff; while (mail_users_head != NULL && mail_users_head->refcount == 0) { mail_user_free(mail_users_head); if (global_used_memory < stats_settings->memory_limit || mail_users_head == NULL) break; diff = ioloop_time - mail_users_head->last_update.tv_sec; if (diff < stats_settings->user_min_time) break; } } void mail_users_init(void) { hash_table_create(&mail_users_hash, default_pool, 0, str_hash, strcmp); } void mail_users_deinit(void) { while (mail_users_head != NULL) mail_user_free(mail_users_head); hash_table_destroy(&mail_users_hash); } dovecot-2.2.33.2/src/stats/main.c0000644000175000017500000000451613123174404013347 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "global-memory.h" #include "stats-settings.h" #include "fifo-input-connection.h" #include "mail-command.h" #include "mail-session.h" #include "mail-user.h" #include "mail-domain.h" #include "mail-ip.h" #include "mail-stats.h" #include "client.h" static struct module *modules = NULL; static void client_connected(struct master_service_connection *conn) { if (conn->fifo) (void)fifo_input_connection_create(conn->fd); else (void)client_create(conn->fd); master_service_client_connection_accept(conn); } static void main_preinit(void) { struct module_dir_load_settings mod_set; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; modules = module_dir_load(STATS_MODULE_DIR, NULL, &mod_set); module_dir_init(modules); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } int main(int argc, char *argv[]) { const struct setting_parser_info *set_roots[] = { &stats_setting_parser_info, NULL }; const enum master_service_flags service_flags = MASTER_SERVICE_FLAG_NO_IDLE_DIE | MASTER_SERVICE_FLAG_UPDATE_PROCTITLE; const char *error; void **sets; master_service = master_service_init("stats", service_flags, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "stats: "); main_preinit(); sets = master_service_settings_get_others(master_service); stats_settings = sets[0]; mail_commands_init(); mail_sessions_init(); mail_users_init(); mail_domains_init(); mail_ips_init(); mail_global_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); clients_destroy_all(); fifo_input_connections_destroy_all(); mail_commands_deinit(); mail_sessions_deinit(); mail_users_deinit(); mail_domains_deinit(); mail_ips_deinit(); mail_global_deinit(); module_dir_unload(&modules); i_assert(global_used_memory == 0); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/stats/mail-ip.c0000644000175000017500000000603313123174404013747 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "llist.h" #include "global-memory.h" #include "stats-settings.h" #include "mail-stats.h" #include "mail-ip.h" static HASH_TABLE(struct ip_addr *, struct mail_ip *) mail_ips_hash; /* ips are sorted by their last_update timestamp, oldest first */ static struct mail_ip *mail_ips_head, *mail_ips_tail; struct mail_ip *stable_mail_ips; static size_t mail_ip_memsize(const struct mail_ip *ip) { return sizeof(*ip); } struct mail_ip *mail_ip_login(const struct ip_addr *ip_addr) { struct mail_ip *ip; ip = hash_table_lookup(mail_ips_hash, ip_addr); if (ip != NULL) { ip->num_logins++; ip->num_connected_sessions++; mail_ip_refresh(ip, NULL); return ip; } ip = i_malloc(MALLOC_ADD(sizeof(struct mail_ip), stats_alloc_size())); ip->stats = (void *)(ip + 1); ip->ip = *ip_addr; ip->reset_timestamp = ioloop_time; hash_table_insert(mail_ips_hash, &ip->ip, ip); DLLIST_PREPEND_FULL(&stable_mail_ips, ip, stable_prev, stable_next); DLLIST2_APPEND_FULL(&mail_ips_head, &mail_ips_tail, ip, sorted_prev, sorted_next); ip->num_logins++; ip->num_connected_sessions++; ip->last_update = ioloop_timeval; global_memory_alloc(mail_ip_memsize(ip)); return ip; } void mail_ip_disconnected(struct mail_ip *ip) { i_assert(ip->num_connected_sessions > 0); ip->num_connected_sessions--; } struct mail_ip *mail_ip_lookup(const struct ip_addr *ip_addr) { return hash_table_lookup(mail_ips_hash, ip_addr); } void mail_ip_ref(struct mail_ip *ip) { ip->refcount++; } void mail_ip_unref(struct mail_ip **_ip) { struct mail_ip *ip = *_ip; i_assert(ip->refcount > 0); ip->refcount--; *_ip = NULL; } static void mail_ip_free(struct mail_ip *ip) { i_assert(ip->refcount == 0); i_assert(ip->sessions == NULL); global_memory_free(mail_ip_memsize(ip)); hash_table_remove(mail_ips_hash, &ip->ip); DLLIST_REMOVE_FULL(&stable_mail_ips, ip, stable_prev, stable_next); DLLIST2_REMOVE_FULL(&mail_ips_head, &mail_ips_tail, ip, sorted_prev, sorted_next); i_free(ip); } void mail_ip_refresh(struct mail_ip *ip, const struct stats *diff_stats) { if (diff_stats != NULL) stats_add(ip->stats, diff_stats); ip->last_update = ioloop_timeval; DLLIST2_REMOVE_FULL(&mail_ips_head, &mail_ips_tail, ip, sorted_prev, sorted_next); DLLIST2_APPEND_FULL(&mail_ips_head, &mail_ips_tail, ip, sorted_prev, sorted_next); } void mail_ips_free_memory(void) { unsigned int diff; while (mail_ips_head != NULL && mail_ips_head->refcount == 0) { mail_ip_free(mail_ips_head); if (global_used_memory < stats_settings->memory_limit || mail_ips_head == NULL) break; diff = ioloop_time - mail_ips_head->last_update.tv_sec; if (diff < stats_settings->ip_min_time) break; } } void mail_ips_init(void) { hash_table_create(&mail_ips_hash, default_pool, 0, net_ip_hash, net_ip_cmp); } void mail_ips_deinit(void) { while (mail_ips_head != NULL) mail_ip_free(mail_ips_head); hash_table_destroy(&mail_ips_hash); } dovecot-2.2.33.2/src/stats/stats-carbon.h0000644000175000017500000000042313123174404015021 00000000000000#ifndef STATS_CARBON #define STATS_CARBON 1 struct stats_send_ctx; int stats_carbon_send(const char *endpoint, const char *data, void (*callback)(void *), void *cb_ctx, struct stats_send_ctx **ctx_r); void stats_carbon_destroy(struct stats_send_ctx **ctx); #endif dovecot-2.2.33.2/src/stats/client-reset.c0000644000175000017500000000105613123174404015015 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream.h" #include "strescape.h" #include "mail-stats.h" #include "client.h" #include "client-reset.h" int client_stats_reset(struct client *client, const char *const *args ATTR_UNUSED, const char **error_r ATTR_UNUSED) { struct mail_global *g = &mail_global_stats; stats_reset(g->stats); g->num_logins = 0; g->num_cmds = 0; g->reset_timestamp = ioloop_time; i_zero(&g->last_update); o_stream_nsend_str(client->output, "OK\n"); return 0; } dovecot-2.2.33.2/src/stats/client-export.h0000644000175000017500000000024613123174404015221 00000000000000#ifndef CLIENT_EXPORT_H #define CLIENT_EXPORT_H struct client; int client_export(struct client *client, const char *const *args, const char **error_r); #endif dovecot-2.2.33.2/src/stats/stats-carbon.c0000644000175000017500000000561613165463624015040 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "time-util.h" #include "stats-settings.h" #include "mail-stats.h" #include "istream.h" #include "ostream.h" #include "net.h" #include "str.h" #include "write-full.h" #include "stats-carbon.h" #define CARBON_SERVER_DEFAULT_PORT 2003 struct stats_send_ctx { pool_t pool; int fd; unsigned long to_msecs; const char *endpoint; const char *str; struct io *io; struct timeout *to; void (*callback)(void *); void *ctx; }; void stats_carbon_destroy(struct stats_send_ctx **_ctx) { struct stats_send_ctx *ctx = *_ctx; *_ctx = NULL; if (ctx->io != NULL) io_remove(&ctx->io); if (ctx->to != NULL) timeout_remove(&ctx->to); if (ctx->fd != -1) i_close_fd(&ctx->fd); pool_unref(&ctx->pool); } static void stats_carbon_callback(struct stats_send_ctx *ctx) { i_assert(ctx->callback != NULL); void (*callback)(void *) = ctx->callback; ctx->callback = NULL; callback(ctx->ctx); } static void stats_carbon_timeout(struct stats_send_ctx *ctx) { i_error("Stats submit(%s) failed: endpoint timeout after %lu msecs", ctx->endpoint, ctx->to_msecs); stats_carbon_callback(ctx); } static void stats_carbon_connected(struct stats_send_ctx *ctx) { io_remove(&ctx->io); if ((errno = net_geterror(ctx->fd)) != 0) { i_error("connect(%s) failed: %m", ctx->endpoint); stats_carbon_callback(ctx); return; } if (write_full(ctx->fd, ctx->str, strlen(ctx->str)) < 0) i_error("write(%s) failed: %m", ctx->endpoint); stats_carbon_callback(ctx); } int stats_carbon_send(const char *endpoint, const char *data, void (*callback)(void *), void *cb_ctx, struct stats_send_ctx **ctx_r) { const char *host; in_port_t port; struct ip_addr ip; if (net_str2hostport(endpoint, CARBON_SERVER_DEFAULT_PORT, &host, &port) < 0 || net_addr2ip(host, &ip) < 0) { i_error("stats_submit: Cannot parse endpoint '%s'", endpoint); return -1; } pool_t pool = pool_alloconly_create("stats carbon send", 1024); struct stats_send_ctx *ctx = p_new(pool, struct stats_send_ctx, 1); ctx->pool = pool; ctx->str = p_strdup(ctx->pool, data); ctx->fd = net_connect_ip(&ip, port, NULL); if (ctx->fd < 0) { i_error("connect(%s) failed: %m", endpoint); stats_carbon_callback(ctx); return -1; } ctx->io = io_add(ctx->fd, IO_WRITE, stats_carbon_connected, ctx); /* give time for almost until next update this is to ensure we leave a little pause between attempts. Multiplier 800 gives us 20% window, and ensures the number stays positive. */ ctx->to_msecs = stats_settings->carbon_interval*800; ctx->to = timeout_add(ctx->to_msecs, stats_carbon_timeout, ctx); if (net_ipport2str(&ip, port, &host) < 0) i_unreached(); ctx->endpoint = p_strdup(ctx->pool, host); ctx->callback = callback; ctx->ctx = cb_ctx; *ctx_r = ctx; return 0; } dovecot-2.2.33.2/src/stats/mail-command.c0000644000175000017500000001366213123174404014763 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "ioloop.h" #include "llist.h" #include "global-memory.h" #include "stats-settings.h" #include "mail-stats.h" #include "mail-session.h" #include "mail-command.h" #define MAIL_COMMAND_TIMEOUT_SECS (60*15) /* commands are sorted by their last_update timestamp, oldest first */ struct mail_command *stable_mail_commands_head; struct mail_command *stable_mail_commands_tail; static size_t mail_command_memsize(const struct mail_command *cmd) { return sizeof(*cmd) + strlen(cmd->name) + 1 + strlen(cmd->args) + 1; } static struct mail_command * mail_command_find(struct mail_session *session, unsigned int id) { struct mail_command *cmd; i_assert(id != 0); if (id > session->highest_cmd_id) { /* fast path for new commands */ return NULL; } for (cmd = session->commands; cmd != NULL; cmd = cmd->session_next) { if (cmd->id == id) return cmd; } /* expired */ return NULL; } static struct mail_command * mail_command_add(struct mail_session *session, const char *name, const char *args) { struct mail_command *cmd; cmd = i_malloc(MALLOC_ADD(sizeof(struct mail_command), stats_alloc_size())); cmd->stats = (void *)(cmd + 1); cmd->refcount = 1; /* unrefed at "done" */ cmd->session = session; cmd->name = i_strdup(name); cmd->args = i_strdup(args); cmd->last_update = ioloop_timeval; DLLIST2_APPEND_FULL(&stable_mail_commands_head, &stable_mail_commands_tail, cmd, stable_prev, stable_next); DLLIST_PREPEND_FULL(&session->commands, cmd, session_prev, session_next); mail_session_ref(cmd->session); global_memory_alloc(mail_command_memsize(cmd)); return cmd; } static void mail_command_free(struct mail_command *cmd) { i_assert(cmd->refcount == 0); global_memory_free(mail_command_memsize(cmd)); DLLIST2_REMOVE_FULL(&stable_mail_commands_head, &stable_mail_commands_tail, cmd, stable_prev, stable_next); DLLIST_REMOVE_FULL(&cmd->session->commands, cmd, session_prev, session_next); mail_session_unref(&cmd->session); i_free(cmd->name); i_free(cmd->args); i_free(cmd); } void mail_command_ref(struct mail_command *cmd) { cmd->refcount++; } void mail_command_unref(struct mail_command **_cmd) { struct mail_command *cmd = *_cmd; i_assert(cmd->refcount > 0); cmd->refcount--; *_cmd = NULL; } int mail_command_update_parse(const char *const *args, const char **error_r) { struct mail_session *session; struct mail_command *cmd; struct stats *new_stats, *diff_stats; buffer_t *buf; const char *error; unsigned int i, cmd_id; bool done = FALSE, continued = FALSE; /* [d] c[d] */ if (str_array_length(args) < 3) { *error_r = "UPDATE-CMD: Too few parameters"; return -1; } if (mail_session_get(args[0], &session, error_r) < 0) return -1; if (str_to_uint(args[1], &cmd_id) < 0 || cmd_id == 0) { *error_r = "UPDATE-CMD: Invalid command id"; return -1; } for (i = 0; args[2][i] != '\0'; i++) { switch (args[2][i]) { case 'd': done = TRUE; break; case 'c': continued = TRUE; break; default: *error_r = "UPDATE-CMD: Invalid flags parameter"; return -1; } } cmd = mail_command_find(session, cmd_id); if (!continued) { /* new command */ if (cmd != NULL) { *error_r = "UPDATE-CMD: Duplicate new command id"; return -1; } if (str_array_length(args) < 5) { *error_r = "UPDATE-CMD: Too few parameters"; return -1; } cmd = mail_command_add(session, args[3], args[4]); cmd->id = cmd_id; session->highest_cmd_id = I_MAX(session->highest_cmd_id, cmd_id); session->num_cmds++; session->user->num_cmds++; session->user->domain->num_cmds++; if (session->ip != NULL) session->ip->num_cmds++; mail_global_stats.num_cmds++; args += 5; } else { if (cmd == NULL) { /* already expired command, ignore */ i_warning("UPDATE-CMD: Already expired"); return 0; } args += 3; cmd->last_update = ioloop_timeval; } buf = buffer_create_dynamic(pool_datastack_create(), 256); if (args[0] == NULL || base64_decode(args[0], strlen(args[0]), NULL, buf) < 0) { *error_r = t_strdup_printf("UPDATE-CMD: Invalid base64 input"); return -1; } new_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); if (!stats_import(buf->data, buf->used, cmd->stats, new_stats, &error)) { *error_r = t_strdup_printf("UPDATE-CMD: %s", error); return -1; } if (!stats_diff(cmd->stats, new_stats, diff_stats, &error)) { *error_r = t_strdup_printf("UPDATE-CMD: stats shrank: %s", error); return -1; } stats_add(cmd->stats, diff_stats); if (done) { cmd->id = 0; mail_command_unref(&cmd); } mail_session_refresh(session, NULL); return 0; } static bool mail_command_is_timed_out(struct mail_command *cmd) { /* some commands like IDLE can run forever */ return ioloop_time - cmd->last_update.tv_sec > MAIL_COMMAND_TIMEOUT_SECS; } void mail_commands_free_memory(void) { unsigned int diff; while (stable_mail_commands_head != NULL) { struct mail_command *cmd = stable_mail_commands_head; if (cmd->refcount == 0) i_assert(cmd->id == 0); else if (cmd->refcount == 1 && (cmd->session->disconnected || mail_command_is_timed_out(cmd))) { /* session was probably lost */ mail_command_unref(&cmd); } else { break; } mail_command_free(stable_mail_commands_head); if (global_used_memory < stats_settings->memory_limit || stable_mail_commands_head == NULL) break; diff = ioloop_time - stable_mail_commands_head->last_update.tv_sec; if (diff < stats_settings->command_min_time) break; } } void mail_commands_init(void) { } void mail_commands_deinit(void) { while (stable_mail_commands_head != NULL) { struct mail_command *cmd = stable_mail_commands_head; if (cmd->id != 0) mail_command_unref(&cmd); mail_command_free(stable_mail_commands_head); } } dovecot-2.2.33.2/src/stats/client.c0000644000175000017500000001127713165463624013716 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "master-service.h" #include "mail-command.h" #include "mail-session.h" #include "mail-user.h" #include "mail-domain.h" #include "mail-ip.h" #include "client-export.h" #include "client-reset.h" #include "client.h" #include #define CLIENT_MAX_SIMULTANEOUS_ITER_COUNT 1000 #define MAX_INBUF_SIZE 1024 #define OUTBUF_THROTTLE_SIZE (1024*64) static struct client *clients; bool client_is_busy(struct client *client) { client->iter_count++; if (client->iter_count % CLIENT_MAX_SIMULTANEOUS_ITER_COUNT == 0) return TRUE; if (o_stream_get_buffer_used_size(client->output) < OUTBUF_THROTTLE_SIZE) return FALSE; if (o_stream_flush(client->output) < 0) return TRUE; return o_stream_get_buffer_used_size(client->output) >= OUTBUF_THROTTLE_SIZE; } static int client_handle_request(struct client *client, const char *const *args, const char **error_r) { const char *cmd = args[0]; if (cmd == NULL) { *error_r = "Missing command"; return -1; } args++; if (strcmp(cmd, "EXPORT") == 0) return client_export(client, args, error_r); if (strcmp(cmd, "RESET") == 0) return client_stats_reset(client, args, error_r); *error_r = "Unknown command"; return -1; } static const char *const* client_read_next_line(struct client *client) { const char *line; char **args; unsigned int i; line = i_stream_next_line(client->input); if (line == NULL) return NULL; args = p_strsplit(pool_datastack_create(), line, "\t"); for (i = 0; args[i] != NULL; i++) args[i] = str_tabunescape(args[i]); return (void *)args; } static void client_input(struct client *client) { const char *const *args, *error; int ret; if (client->to_pending != NULL) timeout_remove(&client->to_pending); switch (i_stream_read(client->input)) { case -2: i_error("BUG: Stats client sent too much data"); client_destroy(&client); return; case -1: client_destroy(&client); return; } o_stream_cork(client->output); while ((args = client_read_next_line(client)) != NULL) { ret = client_handle_request(client, args, &error); if (ret < 0) { i_error("Stats client input error: %s", error); client_destroy(&client); return; } if (ret == 0) { o_stream_set_flush_pending(client->output, TRUE); io_remove(&client->io); break; } client->cmd_more = NULL; } o_stream_uncork(client->output); } static int client_output(struct client *client) { int ret = 1; o_stream_cork(client->output); if (o_stream_flush(client->output) < 0) { client_destroy(&client); return 1; } if (client->cmd_more != NULL) ret = client->cmd_more(client); o_stream_uncork(client->output); if (ret > 0) { client->cmd_more = NULL; if (client->io == NULL) client_enable_io(client); } return ret; } void client_enable_io(struct client *client) { i_assert(client->io == NULL); client->io = io_add(client->fd, IO_READ, client_input, client); if (client->to_pending == NULL) client->to_pending = timeout_add(0, client_input, client); } struct client *client_create(int fd) { struct client *client; client = i_new(struct client, 1); client->fd = fd; client->io = io_add(fd, IO_READ, client_input, client); client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); o_stream_set_flush_callback(client->output, client_output, client); client->cmd_pool = pool_alloconly_create("cmd pool", 1024); DLLIST_PREPEND(&clients, client); return client; } static void client_unref_iters(struct client *client) { if (client->mail_cmd_iter != NULL) mail_command_unref(&client->mail_cmd_iter); if (client->mail_session_iter != NULL) mail_session_unref(&client->mail_session_iter); if (client->mail_user_iter != NULL) mail_user_unref(&client->mail_user_iter); if (client->mail_domain_iter != NULL) mail_domain_unref(&client->mail_domain_iter); if (client->mail_ip_iter != NULL) mail_ip_unref(&client->mail_ip_iter); } void client_destroy(struct client **_client) { struct client *client = *_client; *_client = NULL; DLLIST_REMOVE(&clients, client); if (client->io != NULL) io_remove(&client->io); i_stream_destroy(&client->input); o_stream_destroy(&client->output); if (close(client->fd) < 0) i_error("close(client) failed: %m"); client_unref_iters(client); pool_unref(&client->cmd_pool); i_free(client); master_service_client_connection_destroyed(master_service); } void clients_destroy_all(void) { while (clients != NULL) { struct client *client = clients; client_destroy(&client); } } dovecot-2.2.33.2/src/stats/mail-domain.c0000644000175000017500000000661613123174404014615 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "llist.h" #include "global-memory.h" #include "stats-settings.h" #include "mail-stats.h" #include "mail-domain.h" static HASH_TABLE(char *, struct mail_domain *) mail_domains_hash; /* domains are sorted by their last_update timestamp, oldest first */ static struct mail_domain *mail_domains_head, *mail_domains_tail; struct mail_domain *stable_mail_domains; static size_t mail_domain_memsize(const struct mail_domain *domain) { return sizeof(*domain) + strlen(domain->name) + 1; } struct mail_domain *mail_domain_login_create(const char *name) { struct mail_domain *domain; domain = hash_table_lookup(mail_domains_hash, name); if (domain != NULL) { return domain; } domain = i_malloc(MALLOC_ADD(sizeof(struct mail_domain), stats_alloc_size())); domain->stats = (void *)(domain + 1); domain->name = i_strdup(name); domain->reset_timestamp = ioloop_time; hash_table_insert(mail_domains_hash, domain->name, domain); DLLIST_PREPEND_FULL(&stable_mail_domains, domain, stable_prev, stable_next); global_memory_alloc(mail_domain_memsize(domain)); return domain; } void mail_domain_login(struct mail_domain *domain) { domain->num_logins++; domain->num_connected_sessions++; mail_domain_refresh(domain, NULL); } void mail_domain_disconnected(struct mail_domain *domain) { i_assert(domain->num_connected_sessions > 0); domain->num_connected_sessions--; mail_global_disconnected(); } struct mail_domain *mail_domain_lookup(const char *name) { return hash_table_lookup(mail_domains_hash, name); } void mail_domain_ref(struct mail_domain *domain) { domain->refcount++; } void mail_domain_unref(struct mail_domain **_domain) { struct mail_domain *domain = *_domain; i_assert(domain->refcount > 0); domain->refcount--; *_domain = NULL; } static void mail_domain_free(struct mail_domain *domain) { i_assert(domain->refcount == 0); i_assert(domain->users == NULL); global_memory_free(mail_domain_memsize(domain)); hash_table_remove(mail_domains_hash, domain->name); DLLIST_REMOVE_FULL(&stable_mail_domains, domain, stable_prev, stable_next); DLLIST2_REMOVE_FULL(&mail_domains_head, &mail_domains_tail, domain, sorted_prev, sorted_next); i_free(domain->name); i_free(domain); } void mail_domain_refresh(struct mail_domain *domain, const struct stats *diff_stats) { if (diff_stats != NULL) stats_add(domain->stats, diff_stats); domain->last_update = ioloop_timeval; DLLIST2_REMOVE_FULL(&mail_domains_head, &mail_domains_tail, domain, sorted_prev, sorted_next); DLLIST2_APPEND_FULL(&mail_domains_head, &mail_domains_tail, domain, sorted_prev, sorted_next); mail_global_refresh(diff_stats); } void mail_domains_free_memory(void) { unsigned int diff; while (mail_domains_head != NULL && mail_domains_head->refcount == 0) { mail_domain_free(mail_domains_head); if (global_used_memory < stats_settings->memory_limit || mail_domains_head == NULL) break; diff = ioloop_time - mail_domains_head->last_update.tv_sec; if (diff < stats_settings->domain_min_time) break; } } void mail_domains_init(void) { hash_table_create(&mail_domains_hash, default_pool, 0, str_hash, strcmp); } void mail_domains_deinit(void) { while (mail_domains_head != NULL) mail_domain_free(mail_domains_head); hash_table_destroy(&mail_domains_hash); } dovecot-2.2.33.2/src/stats/client.h0000644000175000017500000000150613123174404013702 00000000000000#ifndef CLIENT_H #define CLIENT_H struct client { struct client *prev, *next; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_pending; pool_t cmd_pool; struct client_export_cmd *cmd_export; int (*cmd_more)(struct client *client); /* command iterators. while non-NULL, they've increased the struct's refcount so it won't be deleted during iteration */ unsigned int iter_count; struct mail_command *mail_cmd_iter; struct mail_session *mail_session_iter; struct mail_user *mail_user_iter; struct mail_domain *mail_domain_iter; struct mail_ip *mail_ip_iter; }; struct client *client_create(int fd); void client_destroy(struct client **client); bool client_is_busy(struct client *client); void client_enable_io(struct client *client); void clients_destroy_all(void); #endif dovecot-2.2.33.2/src/stats/fifo-input-connection.c0000644000175000017500000000511213165463624016644 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "strescape.h" #include "istream.h" #include "ostream.h" #include "master-service.h" #include "mail-session.h" #include "mail-user.h" #include "mail-command.h" #include "fifo-input-connection.h" #include #define MAX_INBUF_SIZE (PIPE_BUF*2) struct fifo_input_connection { struct fifo_input_connection *prev, *next; int fd; struct istream *input; struct io *io; }; static struct fifo_input_connection *fifo_conns = NULL; static int fifo_input_connection_request(const char *const *args, const char **error_r) { const char *cmd = args[0]; if (cmd == NULL) { *error_r = "Missing command"; return -1; } args++; if (strcmp(cmd, "CONNECT") == 0) return mail_session_connect_parse(args, error_r); if (strcmp(cmd, "DISCONNECT") == 0) return mail_session_disconnect_parse(args, error_r); if (strcmp(cmd, "UPDATE-SESSION") == 0) return mail_session_update_parse(args, error_r); if (strcmp(cmd, "ADD-USER") == 0) return mail_user_add_parse(args, error_r); if (strcmp(cmd, "UPDATE-CMD") == 0) return mail_command_update_parse(args, error_r); *error_r = "Unknown command"; return -1; } static void fifo_input_connection_input(struct fifo_input_connection *conn) { const char *line, *const *args, *error; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Mail server sent too much data"); fifo_input_connection_destroy(&conn); return; case -1: fifo_input_connection_destroy(&conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) T_BEGIN { args = t_strsplit_tabescaped(line); if (fifo_input_connection_request(args, &error) < 0) i_error("FIFO input error: %s", error); } T_END; } struct fifo_input_connection *fifo_input_connection_create(int fd) { struct fifo_input_connection *conn; conn = i_new(struct fifo_input_connection, 1); conn->fd = fd; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->io = io_add(fd, IO_READ, fifo_input_connection_input, conn); DLLIST_PREPEND(&fifo_conns, conn); return conn; } void fifo_input_connection_destroy(struct fifo_input_connection **_conn) { struct fifo_input_connection *conn = *_conn; *_conn = NULL; DLLIST_REMOVE(&fifo_conns, conn); io_remove(&conn->io); i_stream_destroy(&conn->input); if (close(conn->fd) < 0) i_error("close(conn) failed: %m"); i_free(conn); } void fifo_input_connections_destroy_all(void) { while (fifo_conns != NULL) { struct fifo_input_connection *conn = fifo_conns; fifo_input_connection_destroy(&conn); } } dovecot-2.2.33.2/src/stats/mail-ip.h0000644000175000017500000000100213123174404013743 00000000000000#ifndef MAIL_IP_H #define MAIL_IP_H extern struct mail_ip *stable_mail_ips; struct mail_ip *mail_ip_login(const struct ip_addr *ip_addr); void mail_ip_disconnected(struct mail_ip *ip); struct mail_ip *mail_ip_lookup(const struct ip_addr *ip_addr); void mail_ip_refresh(struct mail_ip *ip, const struct stats *diff_stats) ATTR_NULL(2); void mail_ip_ref(struct mail_ip *ip); void mail_ip_unref(struct mail_ip **ip); void mail_ips_free_memory(void); void mail_ips_init(void); void mail_ips_deinit(void); #endif dovecot-2.2.33.2/src/stats/mail-session.c0000644000175000017500000002370113165463624015036 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "base64.h" #include "ioloop.h" #include "hash.h" #include "llist.h" #include "str-table.h" #include "global-memory.h" #include "stats-settings.h" #include "mail-stats.h" #include "mail-user.h" #include "mail-ip.h" #include "mail-session.h" #include "mail-domain.h" /* If session doesn't receive any updates for this long, assume that the process associated with it has crashed, and forcibly disconnect the session. Must be larger than SESSION_STATS_FORCE_REFRESH_SECS in stats plugin */ #define MAIL_SESSION_IDLE_TIMEOUT_MSECS (1000*60*15) /* If stats process crashes/restarts, existing processes keep sending status updates to it, but this process doesn't know their session IDs. If these missing IDs are found within this many seconds of starting the stats process, don't log a warning about them. (On a larger installation this avoids flooding the error log with hundreds of warnings.) */ #define SESSION_ID_WARN_HIDE_SECS (60*5) static HASH_TABLE(char *, struct mail_session *) mail_sessions_hash; /* sessions are sorted by their last_update timestamp, oldest first */ static struct mail_session *mail_sessions_head, *mail_sessions_tail; static time_t session_id_warn_hide_until; static bool session_id_hide_warned = FALSE; static struct str_table *services; struct mail_session *stable_mail_sessions; static size_t mail_session_memsize(const struct mail_session *session) { return sizeof(*session) + strlen(session->id) + 1; } static void mail_session_disconnect(struct mail_session *session) { i_assert(!session->disconnected); mail_user_disconnected(session->user); if (session->ip != NULL) mail_ip_disconnected(session->ip); hash_table_remove(mail_sessions_hash, session->id); session->disconnected = TRUE; timeout_remove(&session->to_idle); mail_session_unref(&session); } static void mail_session_idle_timeout(struct mail_session *session) { /* user="" service="" pid=0 is used for incoming sessions that were received after we detected a stats process crash/restart. there's no point in logging anything about them, since they contain no useful information. */ if (session->user->name[0] == '\0' && session->service[0] != '\0' && session->pid == 0) { i_warning("Session %s (user %s, service %s) " "appears to have crashed, disconnecting it", session->id, session->user->name, session->service); } mail_session_disconnect(session); } int mail_session_connect_parse(const char *const *args, const char **error_r) { struct mail_session *session; const char *session_id; pid_t pid; struct ip_addr ip; unsigned int i; /* [key=value ..] */ if (str_array_length(args) < 4) { *error_r = "CONNECT: Too few parameters"; return -1; } session_id = args[0]; if (str_to_pid(args[3], &pid) < 0) { *error_r = t_strdup_printf("CONNECT: Invalid pid %s for session ID %s", args[3], session_id); return -1; } session = hash_table_lookup(mail_sessions_hash, session_id); if (session != NULL) { *error_r = t_strdup_printf( "CONNECT: Duplicate session ID %s for user %s service %s (old PID %ld, new PID %ld)", session_id, args[1], args[2], (long)session->pid, (long)pid); return -1; } session = i_malloc(MALLOC_ADD(sizeof(struct mail_session), stats_alloc_size())); session->stats = (void *)(session + 1); session->refcount = 1; /* unrefed at disconnect */ session->id = i_strdup(session_id); session->service = str_table_ref(services, args[2]); session->pid = pid; session->last_update = ioloop_timeval; session->to_idle = timeout_add(MAIL_SESSION_IDLE_TIMEOUT_MSECS, mail_session_idle_timeout, session); session->user = mail_user_login(args[1]); session->user->num_logins++; mail_domain_login(session->user->domain); for (i = 3; args[i] != NULL; i++) { if (strncmp(args[i], "rip=", 4) == 0 && net_addr2ip(args[i] + 4, &ip) == 0) session->ip = mail_ip_login(&ip); } hash_table_insert(mail_sessions_hash, session->id, session); DLLIST_PREPEND_FULL(&stable_mail_sessions, session, stable_prev, stable_next); DLLIST2_APPEND_FULL(&mail_sessions_head, &mail_sessions_tail, session, sorted_prev, sorted_next); DLLIST_PREPEND_FULL(&session->user->sessions, session, user_prev, user_next); mail_user_ref(session->user); if (session->ip != NULL) { DLLIST_PREPEND_FULL(&session->ip->sessions, session, ip_prev, ip_next); mail_ip_ref(session->ip); } global_memory_alloc(mail_session_memsize(session)); mail_global_login(); return 0; } void mail_session_ref(struct mail_session *session) { session->refcount++; } void mail_session_unref(struct mail_session **_session) { struct mail_session *session = *_session; i_assert(session->refcount > 0); session->refcount--; *_session = NULL; } static void mail_session_free(struct mail_session *session) { i_assert(session->refcount == 0); global_memory_free(mail_session_memsize(session)); if (session->to_idle != NULL) timeout_remove(&session->to_idle); if (!session->disconnected) hash_table_remove(mail_sessions_hash, session->id); DLLIST_REMOVE_FULL(&stable_mail_sessions, session, stable_prev, stable_next); DLLIST2_REMOVE_FULL(&mail_sessions_head, &mail_sessions_tail, session, sorted_prev, sorted_next); DLLIST_REMOVE_FULL(&session->user->sessions, session, user_prev, user_next); mail_user_unref(&session->user); if (session->ip != NULL) { DLLIST_REMOVE_FULL(&session->ip->sessions, session, ip_prev, ip_next); mail_ip_unref(&session->ip); } str_table_unref(services, &session->service); i_free(session->id); i_free(session); } static void mail_session_id_lost(const char *session_id) { if (ioloop_time < session_id_warn_hide_until) { if (session_id_hide_warned) return; session_id_hide_warned = TRUE; i_warning("stats process appears to have crashed/restarted, " "hiding missing session ID warnings for %d seconds", (int)(session_id_warn_hide_until - ioloop_time)); return; } i_warning("Couldn't find session ID: %s", session_id); } int mail_session_lookup(const char *id, struct mail_session **session_r, const char **error_r) { if (id == NULL) { *error_r = "Too few parameters"; return -1; } *session_r = hash_table_lookup(mail_sessions_hash, id); if (*session_r == NULL) { mail_session_id_lost(id); return 0; } return 1; } int mail_session_get(const char *id, struct mail_session **session_r, const char **error_r) { const char *new_args[5]; int ret; if ((ret = mail_session_lookup(id, session_r, error_r)) != 0) return ret; /* Create a new dummy session to avoid repeated warnings */ new_args[0] = id; new_args[1] = ""; /* username */ new_args[2] = ""; /* service */ new_args[3] = "0"; /* pid */ new_args[4] = NULL; if (mail_session_connect_parse(new_args, error_r) < 0) i_unreached(); if (mail_session_lookup(id, session_r, error_r) != 1) i_unreached(); return 0; } int mail_session_disconnect_parse(const char *const *args, const char **error_r) { struct mail_session *session; int ret; /* */ if ((ret = mail_session_lookup(args[0], &session, error_r)) <= 0) return ret; if (!session->disconnected) mail_session_disconnect(session); return 0; } void mail_session_refresh(struct mail_session *session, const struct stats *diff_stats) { timeout_reset(session->to_idle); if (diff_stats != NULL) stats_add(session->stats, diff_stats); session->last_update = ioloop_timeval; DLLIST2_REMOVE_FULL(&mail_sessions_head, &mail_sessions_tail, session, sorted_prev, sorted_next); DLLIST2_APPEND_FULL(&mail_sessions_head, &mail_sessions_tail, session, sorted_prev, sorted_next); mail_user_refresh(session->user, diff_stats); if (session->ip != NULL) mail_ip_refresh(session->ip, diff_stats); } int mail_session_update_parse(const char *const *args, const char **error_r) { struct mail_session *session; struct stats *new_stats, *diff_stats; buffer_t *buf; const char *error; /* */ if (mail_session_get(args[0], &session, error_r) < 0) return -1; buf = buffer_create_dynamic(pool_datastack_create(), 256); if (args[1] == NULL || base64_decode(args[1], strlen(args[1]), NULL, buf) < 0) { *error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: Invalid base64 input", session->user->name, session->service, session->id); return -1; } new_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); if (!stats_import(buf->data, buf->used, session->stats, new_stats, &error)) { *error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: %s", session->user->name, session->service, session->id, error); return -1; } if (!stats_diff(session->stats, new_stats, diff_stats, &error)) { *error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: stats shrank: %s", session->user->name, session->service, session->id, error); return -1; } mail_session_refresh(session, diff_stats); return 0; } void mail_sessions_free_memory(void) { unsigned int diff; while (mail_sessions_head != NULL && mail_sessions_head->refcount == 0) { i_assert(mail_sessions_head->disconnected); mail_session_free(mail_sessions_head); if (global_used_memory < stats_settings->memory_limit || mail_sessions_head == NULL) break; diff = ioloop_time - mail_sessions_head->last_update.tv_sec; if (diff < stats_settings->session_min_time) break; } } void mail_sessions_init(void) { session_id_warn_hide_until = ioloop_time + SESSION_ID_WARN_HIDE_SECS; hash_table_create(&mail_sessions_hash, default_pool, 0, str_hash, strcmp); services = str_table_init(); } void mail_sessions_deinit(void) { while (mail_sessions_head != NULL) { struct mail_session *session = mail_sessions_head; if (!session->disconnected) mail_session_unref(&session); mail_session_free(mail_sessions_head); } hash_table_destroy(&mail_sessions_hash); str_table_deinit(&services); } dovecot-2.2.33.2/src/stats/mail-stats.c0000644000175000017500000000472413123174404014502 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mail-stats.h" #include "stats-carbon.h" #include "stats-settings.h" #include "str.h" struct mail_global mail_global_stats; static void mail_global_stats_sent(void *ctx) { struct mail_global *stats = ctx; stats_carbon_destroy(&stats->stats_send_ctx); } static void mail_global_stats_send(void *u0 ATTR_UNUSED) { unsigned long ts = (unsigned long)ioloop_time; if (*stats_settings->carbon_name != '\0' && *stats_settings->carbon_server != '\0') { string_t *str = t_str_new(256); const char *prefix = t_strdup_printf("dovecot.%s.global", stats_settings->carbon_name); str_printfa(str, "%s.logins %u %lu\r\n", prefix, mail_global_stats.num_logins, ts); str_printfa(str, "%s.cmds %u %lu\r\n", prefix, mail_global_stats.num_cmds, ts); str_printfa(str, "%s.connected_sessions %u %lu\r\n", prefix, mail_global_stats.num_connected_sessions, ts); str_printfa(str, "%s.last_reset %lu %lu\r\n", prefix, mail_global_stats.reset_timestamp, ts); /* then export rest of the stats */ for(size_t i = 0; i < stats_field_count(); i++) { str_printfa(str, "%s.%s ", prefix, stats_field_name(i)); stats_field_value(str, mail_global_stats.stats, i); str_printfa(str, " %lu\r\n", ts); } /* and send them along */ (void)stats_carbon_send(stats_settings->carbon_server, str_c(str), mail_global_stats_sent, &mail_global_stats, &mail_global_stats.stats_send_ctx); } } void mail_global_init(void) { mail_global_stats.reset_timestamp = ioloop_time; mail_global_stats.stats = stats_alloc(default_pool); mail_global_stats.to_stats_send = timeout_add(stats_settings->carbon_interval*1000, mail_global_stats_send, NULL); } void mail_global_deinit(void) { if (mail_global_stats.stats_send_ctx != NULL) stats_carbon_destroy(&mail_global_stats.stats_send_ctx); timeout_remove(&mail_global_stats.to_stats_send); i_free(mail_global_stats.stats); } void mail_global_login(void) { mail_global_stats.num_logins++; mail_global_stats.num_connected_sessions++; } void mail_global_disconnected(void) { i_assert(mail_global_stats.num_connected_sessions > 0); mail_global_stats.num_connected_sessions--; } void mail_global_refresh(const struct stats *diff_stats) { if (diff_stats != NULL) stats_add(mail_global_stats.stats, diff_stats); mail_global_stats.last_update = ioloop_timeval; } dovecot-2.2.33.2/src/stats/mail-stats.h0000644000175000017500000000507513123174404014507 00000000000000#ifndef MAIL_STATS_H #define MAIL_STATS_H #include #include "net.h" #include "guid.h" #include "stats.h" struct stats_send_ctx; struct mail_command { struct mail_command *stable_prev, *stable_next; struct mail_command *session_prev, *session_next; struct mail_session *session; char *name, *args; /* non-zero id means the command is still running */ unsigned int id; struct timeval last_update; struct stats *stats; int refcount; }; struct mail_session { struct mail_session *stable_prev, *stable_next; struct mail_session *sorted_prev, *sorted_next; struct mail_session *user_prev, *user_next; struct mail_session *ip_prev, *ip_next; /* if id="", the session no longer exists */ char *id; struct mail_user *user; const char *service; pid_t pid; /* ip address may be NULL if there's none */ struct mail_ip *ip; struct timeout *to_idle; struct stats *stats; struct timeval last_update; unsigned int num_cmds; bool disconnected; unsigned int highest_cmd_id; int refcount; struct mail_command *commands; }; struct mail_user { struct mail_user *stable_prev, *stable_next; struct mail_user *sorted_prev, *sorted_next; struct mail_user *domain_prev, *domain_next; char *name; struct mail_domain *domain; time_t reset_timestamp; struct timeval last_update; struct stats *stats; unsigned int num_logins; unsigned int num_cmds; int refcount; struct mail_session *sessions; }; struct mail_domain { struct mail_domain *stable_prev, *stable_next; struct mail_domain *sorted_prev, *sorted_next; char *name; time_t reset_timestamp; struct timeval last_update; struct stats *stats; unsigned int num_logins; unsigned int num_cmds; unsigned int num_connected_sessions; int refcount; struct mail_user *users; }; struct mail_ip { struct mail_ip *stable_prev, *stable_next; struct mail_ip *sorted_prev, *sorted_next; struct ip_addr ip; time_t reset_timestamp; struct timeval last_update; struct stats *stats; unsigned int num_logins; unsigned int num_cmds; unsigned int num_connected_sessions; int refcount; struct mail_session *sessions; }; struct mail_global { time_t reset_timestamp; struct timeval last_update; struct stats *stats; unsigned int num_logins; unsigned int num_cmds; unsigned int num_connected_sessions; struct timeout *to_stats_send; struct stats_send_ctx *stats_send_ctx; }; extern struct mail_global mail_global_stats; void mail_global_init(void); void mail_global_deinit(void); void mail_global_login(void); void mail_global_disconnected(void); void mail_global_refresh(const struct stats *diff_stats); #endif dovecot-2.2.33.2/src/stats/global-memory.h0000644000175000017500000000025113123174404015166 00000000000000#ifndef GLOBAL_MEMORY_H #define GLOBAL_MEMORY_H extern size_t global_used_memory; void global_memory_alloc(size_t size); void global_memory_free(size_t size); #endif dovecot-2.2.33.2/src/stats/stats-settings.c0000644000175000017500000000533413123174404015416 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "stats-settings.h" /* */ static struct file_listener_settings stats_unix_listeners_array[] = { { "stats", 0600, "", "" } }; static struct file_listener_settings *stats_unix_listeners[] = { &stats_unix_listeners_array[0] }; static buffer_t stats_unix_listeners_buf = { stats_unix_listeners, sizeof(stats_unix_listeners), { NULL, } }; static struct file_listener_settings stats_fifo_listeners_array[] = { { "stats-mail", 0600, "", "" }, { "stats-user", 0600, "", "" } }; static struct file_listener_settings *stats_fifo_listeners[] = { &stats_fifo_listeners_array[0], &stats_fifo_listeners_array[1] }; static buffer_t stats_fifo_listeners_buf = { stats_fifo_listeners, sizeof(stats_fifo_listeners), { NULL, } }; /* */ struct service_settings stats_service_settings = { .name = "stats", .protocol = "", .type = "", .executable = "stats", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &stats_unix_listeners_buf, sizeof(stats_unix_listeners[0]) } }, .fifo_listeners = { { &stats_fifo_listeners_buf, sizeof(stats_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; /* we're kind of kludging here to avoid "stats_" prefix in the struct fields */ #undef DEF #define DEF(type, name) \ { type, "stats_"#name, offsetof(struct stats_settings, name), NULL } static const struct setting_define stats_setting_defines[] = { DEF(SET_SIZE, memory_limit), DEF(SET_TIME, command_min_time), DEF(SET_TIME, session_min_time), DEF(SET_TIME, user_min_time), DEF(SET_TIME, domain_min_time), DEF(SET_TIME, ip_min_time), DEF(SET_STR, carbon_server), DEF(SET_TIME, carbon_interval), DEF(SET_STR, carbon_name), SETTING_DEFINE_LIST_END }; const struct stats_settings stats_default_settings = { .memory_limit = 1024*1024*16, .command_min_time = 60, .session_min_time = 60*15, .user_min_time = 60*60, .domain_min_time = 60*60*12, .ip_min_time = 60*60*12, .carbon_interval = 30, .carbon_server = "", .carbon_name = "" }; const struct setting_parser_info stats_setting_parser_info = { .module_name = "stats", .defines = stats_setting_defines, .defaults = &stats_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct stats_settings), .parent_offset = (size_t)-1 }; const struct stats_settings *stats_settings; dovecot-2.2.33.2/src/stats/mail-session.h0000644000175000017500000000173313123174404015031 00000000000000#ifndef MAIL_SESSION_H #define MAIL_SESSION_H struct stats; struct mail_session; extern struct mail_session *stable_mail_sessions; int mail_session_connect_parse(const char *const *args, const char **error_r); int mail_session_disconnect_parse(const char *const *args, const char **error_r); int mail_session_update_parse(const char *const *args, const char **error_r); int mail_session_cmd_update_parse(const char *const *args, const char **error_r); void mail_session_ref(struct mail_session *session); void mail_session_unref(struct mail_session **session); int mail_session_lookup(const char *guid, struct mail_session **session_r, const char **error_r); int mail_session_get(const char *guid, struct mail_session **session_r, const char **error_r); void mail_session_refresh(struct mail_session *session, const struct stats *diff_stats) ATTR_NULL(2); void mail_sessions_free_memory(void); void mail_sessions_init(void); void mail_sessions_deinit(void); #endif dovecot-2.2.33.2/src/stats/Makefile.am0000644000175000017500000000154713165463624014327 00000000000000stats_moduledir = $(moduledir)/stats pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = stats AM_CPPFLAGS = \ -DSTATS_MODULE_DIR=\""$(stats_moduledir)"\" \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-stats stats_LDADD = $(LIBDOVECOT) stats_DEPENDENCIES = $(LIBDOVECOT_DEPS) stats_SOURCES = \ client.c \ client-export.c \ client-reset.c \ fifo-input-connection.c \ global-memory.c \ mail-command.c \ mail-domain.c \ mail-ip.c \ mail-session.c \ mail-stats.c \ mail-user.c \ main.c \ stats-carbon.c \ stats-settings.c noinst_HEADERS = \ client.h \ client-export.h \ client-reset.h \ fifo-input-connection.h \ global-memory.h \ mail-command.h \ mail-domain.h \ mail-ip.h \ mail-session.h \ mail-stats.h \ mail-user.h \ stats-carbon.h \ stats-settings.h dovecot-2.2.33.2/src/stats/client-export.c0000644000175000017500000004371613123174404015225 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "wildcard-match.h" #include "mail-stats.h" #include "mail-command.h" #include "mail-session.h" #include "mail-user.h" #include "mail-domain.h" #include "mail-ip.h" #include "client.h" #include "client-export.h" enum mail_export_level { MAIL_EXPORT_LEVEL_COMMAND, MAIL_EXPORT_LEVEL_SESSION, MAIL_EXPORT_LEVEL_USER, MAIL_EXPORT_LEVEL_DOMAIN, MAIL_EXPORT_LEVEL_IP, MAIL_EXPORT_LEVEL_GLOBAL }; static const char *mail_export_level_names[] = { "command", "session", "user", "domain", "ip", "global" }; struct mail_export_filter { const char *user, *domain, *session; struct ip_addr ip; unsigned int ip_bits; time_t since; bool connected; }; struct client_export_cmd { enum mail_export_level level; struct mail_export_filter filter; string_t *str; int (*export_iter)(struct client *client); bool header_sent; }; static int mail_export_level_parse(const char *str, enum mail_export_level *level_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(mail_export_level_names); i++) { if (strcmp(mail_export_level_names[i], str) == 0) { *level_r = (enum mail_export_level)i; return 0; } } return -1; } static int mail_export_parse_filter(const char *const *args, pool_t pool, struct mail_export_filter *filter_r, const char **error_r) { unsigned long l; /* filters: user= | domain= | session= ip=[/] since= connected */ i_zero(filter_r); for (; *args != NULL; args++) { if (strncmp(*args, "user=", 5) == 0) filter_r->user = p_strdup(pool, *args + 5); else if (strncmp(*args, "domain=", 7) == 0) filter_r->domain = p_strdup(pool, *args + 7); else if (strncmp(*args, "session=", 8) == 0) filter_r->session = p_strdup(pool, *args + 8); else if (strncmp(*args, "ip=", 3) == 0) { if (net_parse_range(*args + 3, &filter_r->ip, &filter_r->ip_bits) < 0) { *error_r = "Invalid ip filter"; return -1; } } else if (strncmp(*args, "since=", 6) == 0) { if (str_to_ulong(*args + 6, &l) < 0) { *error_r = "Invalid since filter"; return -1; } filter_r->since = (time_t)l; } else if (strcmp(*args, "connected") == 0) { filter_r->connected = TRUE; } } return 0; } static void client_export_stats_headers(struct client *client) { unsigned int i, count = stats_field_count(); string_t *str = t_str_new(128); i_assert(count > 0); str_append(str, stats_field_name(0)); for (i = 1; i < count; i++) { str_append_c(str, '\t'); str_append(str, stats_field_name(i)); } str_append_c(str, '\n'); o_stream_nsend(client->output, str_data(str), str_len(str)); } static void client_export_stats(string_t *str, const struct stats *stats) { unsigned int i, count = stats_field_count(); i_assert(count > 0); stats_field_value(str, stats, 0); for (i = 1; i < count; i++) { str_append_c(str, '\t'); stats_field_value(str, stats, i); } } static bool mail_export_filter_match_session(const struct mail_export_filter *filter, const struct mail_session *session) { if (filter->connected && session->disconnected) return FALSE; if (filter->since > session->last_update.tv_sec) return FALSE; if (filter->session != NULL && strcmp(session->id, filter->session) != 0) return FALSE; if (filter->user != NULL && !wildcard_match(session->user->name, filter->user)) return FALSE; if (filter->domain != NULL && !wildcard_match(session->user->domain->name, filter->domain)) return FALSE; if (filter->ip_bits > 0 && !net_is_in_network(&session->ip->ip, &filter->ip, filter->ip_bits)) return FALSE; return TRUE; } static bool mail_export_filter_match_user_common(const struct mail_export_filter *filter, const struct mail_user *user) { struct mail_session *s; bool connected = FALSE, ip_ok = FALSE; if (filter->user != NULL && !wildcard_match(user->name, filter->user)) return FALSE; if (filter->connected || filter->ip_bits > 0) { for (s = user->sessions; s != NULL; s = s->user_next) { if (!s->disconnected) connected = TRUE; if (filter->ip_bits > 0 && net_is_in_network(&s->ip->ip, &filter->ip, filter->ip_bits)) ip_ok = TRUE; } if (filter->connected && !connected) return FALSE; if (filter->ip_bits > 0 && !ip_ok) return FALSE; } return TRUE; } static bool mail_export_filter_match_user(const struct mail_export_filter *filter, const struct mail_user *user) { if (filter->since > user->last_update.tv_sec) return FALSE; if (filter->domain != NULL && !wildcard_match(user->domain->name, filter->domain)) return FALSE; return mail_export_filter_match_user_common(filter, user); } static bool mail_export_filter_match_domain(const struct mail_export_filter *filter, const struct mail_domain *domain) { struct mail_user *user; if (filter->since > domain->last_update.tv_sec) return FALSE; if (filter->domain != NULL && !wildcard_match(domain->name, filter->domain)) return FALSE; if (filter->user != NULL || filter->connected || filter->ip_bits > 0) { for (user = domain->users; user != NULL; user = user->domain_next) { if (mail_export_filter_match_user_common(filter, user)) break; } if (user == NULL) return FALSE; } return TRUE; } static bool mail_export_filter_match_ip(const struct mail_export_filter *filter, const struct mail_ip *ip) { struct mail_session *s; bool connected = FALSE, user_ok = FALSE, domain_ok = FALSE; if (filter->connected || filter->ip_bits > 0) { for (s = ip->sessions; s != NULL; s = s->ip_next) { if (!s->disconnected) connected = TRUE; if (filter->user != NULL && wildcard_match(s->user->name, filter->user)) user_ok = TRUE; if (filter->domain != NULL && wildcard_match(s->user->domain->name, filter->domain)) domain_ok = TRUE; } if (filter->connected && !connected) return FALSE; if (filter->user != NULL && !user_ok) return FALSE; if (filter->domain != NULL && !domain_ok) return FALSE; } if (filter->since > ip->last_update.tv_sec) return FALSE; if (filter->ip_bits > 0 && !net_is_in_network(&ip->ip, &filter->ip, filter->ip_bits)) return FALSE; return TRUE; } static void client_export_timeval(string_t *str, const struct timeval *tv) { str_printfa(str, "\t%ld.%06u", (long)tv->tv_sec, (unsigned int)tv->tv_usec); } static int client_export_iter_command(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_command *command = client->mail_cmd_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_COMMAND); mail_command_unref(&client->mail_cmd_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "cmd\targs\tsession\tuser\tlast_update\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } for (; command != NULL; command = command->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_session(&cmd->filter, command->session)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, command->name); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, command->args); str_append_c(cmd->str, '\t'); str_append(cmd->str, command->session->id); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, command->session->user->name); client_export_timeval(cmd->str, &command->last_update); str_append_c(cmd->str, '\t'); client_export_stats(cmd->str, command->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (command != NULL) { client->mail_cmd_iter = command; mail_command_ref(command); return 0; } return 1; } static int client_export_iter_session(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_session *session = client->mail_session_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_SESSION); mail_session_unref(&client->mail_session_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "session\tuser\tip\tservice\tpid\tconnected" "\tlast_update\tnum_cmds\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } for (; session != NULL; session = session->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_session(&cmd->filter, session)) continue; str_truncate(cmd->str, 0); str_append(cmd->str, session->id); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, session->user->name); str_append_c(cmd->str, '\t'); if (session->ip != NULL) T_BEGIN { str_append(cmd->str, net_ip2addr(&session->ip->ip)); } T_END; str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, session->service); str_printfa(cmd->str, "\t%ld", (long)session->pid); str_printfa(cmd->str, "\t%d", !session->disconnected); client_export_timeval(cmd->str, &session->last_update); str_printfa(cmd->str, "\t%u\t", session->num_cmds); client_export_stats(cmd->str, session->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (session != NULL) { client->mail_session_iter = session; mail_session_ref(session); return 0; } return 1; } static int client_export_iter_user(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_user *user = client->mail_user_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_USER); mail_user_unref(&client->mail_user_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "user\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } for (; user != NULL; user = user->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_user(&cmd->filter, user)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, user->name); str_printfa(cmd->str, "\t%ld", (long)user->reset_timestamp); client_export_timeval(cmd->str, &user->last_update); str_printfa(cmd->str, "\t%u\t%u\t", user->num_logins, user->num_cmds); client_export_stats(cmd->str, user->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (user != NULL) { client->mail_user_iter = user; mail_user_ref(user); return 0; } return 1; } static int client_export_iter_domain(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_domain *domain = client->mail_domain_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_DOMAIN); mail_domain_unref(&client->mail_domain_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "domain\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds\tnum_connected_sessions\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } for (; domain != NULL; domain = domain->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_domain(&cmd->filter, domain)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, domain->name); str_printfa(cmd->str, "\t%ld", (long)domain->reset_timestamp); client_export_timeval(cmd->str, &domain->last_update); str_printfa(cmd->str, "\t%u\t%u\t%u\t", domain->num_logins, domain->num_cmds, domain->num_connected_sessions); client_export_stats(cmd->str, domain->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (domain != NULL) { client->mail_domain_iter = domain; mail_domain_ref(domain); return 0; } return 1; } static int client_export_iter_ip(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_ip *ip = client->mail_ip_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_IP); mail_ip_unref(&client->mail_ip_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "ip\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds\tnum_connected_sessions\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } for (; ip != NULL; ip = ip->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_ip(&cmd->filter, ip)) continue; str_truncate(cmd->str, 0); T_BEGIN { str_append(cmd->str, net_ip2addr(&ip->ip)); } T_END; str_printfa(cmd->str, "\t%ld", (long)ip->reset_timestamp); client_export_timeval(cmd->str, &ip->last_update); str_printfa(cmd->str, "\t%u\t%u\t%u\t", ip->num_logins, ip->num_cmds, ip->num_connected_sessions); client_export_stats(cmd->str, ip->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (ip != NULL) { client->mail_ip_iter = ip; mail_ip_ref(ip); return 0; } return 1; } static int client_export_iter_global(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_global *g = &mail_global_stats; i_assert(cmd->level == MAIL_EXPORT_LEVEL_GLOBAL); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "reset_timestamp\tlast_update" "\tnum_logins\tnum_cmds\tnum_connected_sessions\t"); client_export_stats_headers(client); cmd->header_sent = TRUE; } str_truncate(cmd->str, 0); str_printfa(cmd->str, "%ld", (long)g->reset_timestamp); client_export_timeval(cmd->str, &g->last_update); str_printfa(cmd->str, "\t%u\t%u\t%u\t", g->num_logins, g->num_cmds, g->num_connected_sessions); client_export_stats(cmd->str, g->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); return 1; } static int client_export_more(struct client *client) { if (client->cmd_export->export_iter(client) == 0) return 0; o_stream_nsend_str(client->output, "\n"); return 1; } static bool client_export_iter_init(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; if (cmd->filter.user != NULL && strchr(cmd->filter.user, '*') == NULL && (cmd->level == MAIL_EXPORT_LEVEL_USER || cmd->level == MAIL_EXPORT_LEVEL_SESSION)) { /* exact user */ struct mail_user *user = mail_user_lookup(cmd->filter.user); if (user == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_SESSION) { client->mail_session_iter = user->sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; } else { client->mail_user_iter = user; mail_user_ref(user); cmd->export_iter = client_export_iter_user; } return TRUE; } if (cmd->filter.ip_bits == IPADDR_BITS(&cmd->filter.ip) && (cmd->level == MAIL_EXPORT_LEVEL_IP || cmd->level == MAIL_EXPORT_LEVEL_SESSION)) { /* exact IP address */ struct mail_ip *ip = mail_ip_lookup(&cmd->filter.ip); if (ip == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_SESSION) { client->mail_session_iter = ip->sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; } else { client->mail_ip_iter = ip; mail_ip_ref(ip); cmd->export_iter = client_export_iter_ip; } return TRUE; } if (cmd->filter.domain != NULL && strchr(cmd->filter.domain, '*') == NULL && (cmd->level == MAIL_EXPORT_LEVEL_DOMAIN || cmd->level == MAIL_EXPORT_LEVEL_USER)) { /* exact domain */ struct mail_domain *domain = mail_domain_lookup(cmd->filter.domain); if (domain == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_USER) { client->mail_user_iter = domain->users; mail_user_ref(client->mail_user_iter); cmd->export_iter = client_export_iter_user; } else { client->mail_domain_iter = domain; mail_domain_ref(domain); cmd->export_iter = client_export_iter_domain; } return TRUE; } switch (cmd->level) { case MAIL_EXPORT_LEVEL_COMMAND: client->mail_cmd_iter = stable_mail_commands_head; if (client->mail_cmd_iter == NULL) return FALSE; mail_command_ref(client->mail_cmd_iter); cmd->export_iter = client_export_iter_command; break; case MAIL_EXPORT_LEVEL_SESSION: client->mail_session_iter = stable_mail_sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; break; case MAIL_EXPORT_LEVEL_USER: client->mail_user_iter = stable_mail_users; if (client->mail_user_iter == NULL) return FALSE; mail_user_ref(client->mail_user_iter); cmd->export_iter = client_export_iter_user; break; case MAIL_EXPORT_LEVEL_DOMAIN: client->mail_domain_iter = stable_mail_domains; if (client->mail_domain_iter == NULL) return FALSE; mail_domain_ref(client->mail_domain_iter); cmd->export_iter = client_export_iter_domain; break; case MAIL_EXPORT_LEVEL_IP: client->mail_ip_iter = stable_mail_ips; if (client->mail_ip_iter == NULL) return FALSE; mail_ip_ref(client->mail_ip_iter); cmd->export_iter = client_export_iter_ip; break; case MAIL_EXPORT_LEVEL_GLOBAL: cmd->export_iter = client_export_iter_global; break; } i_assert(cmd->export_iter != NULL); return TRUE; } int client_export(struct client *client, const char *const *args, const char **error_r) { const char *level_str = args[0]; struct client_export_cmd *cmd; p_clear(client->cmd_pool); cmd = p_new(client->cmd_pool, struct client_export_cmd, 1); cmd->str = str_new(client->cmd_pool, 256); if (level_str == NULL) { *error_r = "Missing level parameter"; return -1; } if (mail_export_level_parse(level_str, &cmd->level) < 0) { *error_r = "Invalid level"; return -1; } if (mail_export_parse_filter(args + 1, client->cmd_pool, &cmd->filter, error_r) < 0) return -1; client->cmd_export = cmd; if (!client_export_iter_init(client)) { /* nothing to export */ o_stream_nsend_str(client->output, "\n"); return 1; } client->cmd_more = client_export_more; return client_export_more(client); } dovecot-2.2.33.2/src/lib-dict/0002755000175000017500000000000013172375610012672 500000000000000dovecot-2.2.33.2/src/lib-dict/Makefile.in0000644000175000017500000006501713172375572014675 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-dict ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libdict_backend_a_AR = $(AR) $(ARFLAGS) libdict_backend_a_LIBADD = am_libdict_backend_a_OBJECTS = dict-db.$(OBJEXT) dict-cdb.$(OBJEXT) \ dict-sql.$(OBJEXT) dict-sql-settings.$(OBJEXT) nodist_libdict_backend_a_OBJECTS = dict-drivers-register.$(OBJEXT) libdict_backend_a_OBJECTS = $(am_libdict_backend_a_OBJECTS) \ $(nodist_libdict_backend_a_OBJECTS) LTLIBRARIES = $(noinst_LTLIBRARIES) libdict_la_LIBADD = am__objects_1 = dict.lo dict-client.lo dict-file.lo dict-memcached.lo \ dict-memcached-ascii.lo dict-redis.lo dict-fail.lo \ dict-transaction-memory.lo am_libdict_la_OBJECTS = $(am__objects_1) libdict_la_OBJECTS = $(am_libdict_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-dict$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_dict_OBJECTS = test-dict.$(OBJEXT) test_dict_OBJECTS = $(am_test_dict_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdict_backend_a_SOURCES) \ $(nodist_libdict_backend_a_SOURCES) $(libdict_la_SOURCES) \ $(test_dict_SOURCES) DIST_SOURCES = $(libdict_backend_a_SOURCES) $(libdict_la_SOURCES) \ $(test_dict_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libdict.la noinst_LIBRARIES = libdict_backend.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ $(SQL_CFLAGS) base_sources = \ dict.c \ dict-client.c \ dict-file.c \ dict-memcached.c \ dict-memcached-ascii.c \ dict-redis.c \ dict-fail.c \ dict-transaction-memory.c libdict_la_SOURCES = \ $(base_sources) libdict_backend_a_SOURCES = \ dict-db.c \ dict-cdb.c \ dict-sql.c \ dict-sql-settings.c nodist_libdict_backend_a_SOURCES = \ dict-drivers-register.c headers = \ dict.h \ dict-client.h \ dict-private.h \ dict-sql.h \ dict-sql-settings.h \ dict-transaction-memory.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) test_programs = \ test-dict test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_dict_SOURCES = test-dict.c test_dict_LDADD = libdict.la $(test_libs) test_dict_DEPENDENCIES = $(noinst_LTLIBRARIES) $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-dict/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-dict/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libdict_backend.a: $(libdict_backend_a_OBJECTS) $(libdict_backend_a_DEPENDENCIES) $(EXTRA_libdict_backend_a_DEPENDENCIES) $(AM_V_at)-rm -f libdict_backend.a $(AM_V_AR)$(libdict_backend_a_AR) libdict_backend.a $(libdict_backend_a_OBJECTS) $(libdict_backend_a_LIBADD) $(AM_V_at)$(RANLIB) libdict_backend.a clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdict.la: $(libdict_la_OBJECTS) $(libdict_la_DEPENDENCIES) $(EXTRA_libdict_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libdict_la_OBJECTS) $(libdict_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-dict$(EXEEXT): $(test_dict_OBJECTS) $(test_dict_DEPENDENCIES) $(EXTRA_test_dict_DEPENDENCIES) @rm -f test-dict$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_dict_OBJECTS) $(test_dict_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-cdb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-db.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-drivers-register.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-fail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-memcached-ascii.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-memcached.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-redis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-sql-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-sql.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-transaction-memory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-dict.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ clean-noinstLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile dict-drivers-register.c: Makefile $(top_builddir)/config.h rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "dict.h"' >>$@ echo '#include "dict-sql.h"' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "extern struct dict dict_driver_$${i};" >>$@ ; \ fi; \ done echo 'void dict_drivers_register_all(void) {' >>$@ echo 'dict_drivers_register_builtin();' >>$@ echo 'dict_sql_register();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "dict_driver_register(&dict_driver_$${i});" >>$@ ; \ fi; \ done echo '}' >>$@ echo 'void dict_drivers_unregister_all(void) {' >>$@ echo 'dict_drivers_unregister_builtin();' >>$@ echo 'dict_sql_unregister();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "dict_driver_unregister(&dict_driver_$${i});" >>$@ ; \ fi; \ done echo '}' >>$@ distclean-generic: rm -f Makefile dict-drivers-register.c check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-dict/dict-transaction-memory.c0000644000175000017500000000427613165463624017546 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "dict-transaction-memory.h" void dict_transaction_memory_init(struct dict_transaction_memory_context *ctx, struct dict *dict, pool_t pool) { ctx->ctx.dict = dict; ctx->pool = pool; p_array_init(&ctx->changes, pool, 32); } void dict_transaction_memory_rollback(struct dict_transaction_context *_ctx) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; pool_unref(&ctx->pool); } void dict_transaction_memory_set(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; struct dict_transaction_memory_change *change; change = array_append_space(&ctx->changes); change->type = DICT_CHANGE_TYPE_SET; change->key = p_strdup(ctx->pool, key); change->value.str = p_strdup(ctx->pool, value); } void dict_transaction_memory_unset(struct dict_transaction_context *_ctx, const char *key) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; struct dict_transaction_memory_change *change; change = array_append_space(&ctx->changes); change->type = DICT_CHANGE_TYPE_UNSET; change->key = p_strdup(ctx->pool, key); } void dict_transaction_memory_append(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; struct dict_transaction_memory_change *change; change = array_append_space(&ctx->changes); change->type = DICT_CHANGE_TYPE_APPEND; change->key = p_strdup(ctx->pool, key); change->value.str = p_strdup(ctx->pool, value); } void dict_transaction_memory_atomic_inc(struct dict_transaction_context *_ctx, const char *key, long long diff) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; struct dict_transaction_memory_change *change; change = array_append_space(&ctx->changes); change->type = DICT_CHANGE_TYPE_INC; change->key = p_strdup(ctx->pool, key); change->value.diff = diff; } dovecot-2.2.33.2/src/lib-dict/dict-sql-settings.h0000644000175000017500000000172313165463624016347 00000000000000#ifndef DICT_SQL_SETTINGS_H #define DICT_SQL_SETTINGS_H enum dict_sql_type { DICT_SQL_TYPE_STRING = 0, DICT_SQL_TYPE_INT, DICT_SQL_TYPE_UINT, DICT_SQL_TYPE_HEXBLOB }; struct dict_sql_field { const char *name; enum dict_sql_type value_type; }; struct dict_sql_map { /* pattern is in simplified form: all variables are stored as simple '$' character. fields array is sorted by the variable index. */ const char *pattern; const char *table; const char *username_field; const char *value_field; const char *value_type; bool value_hexblob; ARRAY(struct dict_sql_field) sql_fields; /* generated: */ unsigned int values_count; const char *const *value_fields; const enum dict_sql_type *value_types; }; struct dict_sql_settings { const char *connect; unsigned int max_field_count; ARRAY(struct dict_sql_map) maps; }; struct dict_sql_settings * dict_sql_settings_read(const char *path, const char **error_r); void dict_sql_settings_deinit(void); #endif dovecot-2.2.33.2/src/lib-dict/dict-memcached-ascii.c0000644000175000017500000004327713165463624016713 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING memcached_ascii */ #include "lib.h" #include "array.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "connection.h" #include "dict-transaction-memory.h" #include "dict-private.h" #define MEMCACHED_DEFAULT_PORT 11211 #define MEMCACHED_DEFAULT_LOOKUP_TIMEOUT_MSECS (1000*30) #define DICT_USERNAME_SEPARATOR '/' enum memcached_ascii_input_state { /* GET: expecting VALUE or END */ MEMCACHED_INPUT_STATE_GET, /* SET/(APPEND+ADD): expecting STORED / NOT_STORED */ MEMCACHED_INPUT_STATE_STORED, /* DELETE: expecting DELETED */ MEMCACHED_INPUT_STATE_DELETED, /* (INCR+ADD)/DECR: expecting number / NOT_FOUND / STORED / NOT_STORED */ MEMCACHED_INPUT_STATE_INCRDECR }; struct memcached_ascii_connection { struct connection conn; struct memcached_ascii_dict *dict; string_t *reply_str; unsigned int reply_bytes_left; bool value_received; bool value_waiting_end; }; struct memcached_ascii_dict_reply { unsigned int reply_count; dict_transaction_commit_callback_t *callback; void *context; }; struct dict_memcached_ascii_commit_ctx { struct memcached_ascii_dict *dict; struct dict_transaction_memory_context *memctx; string_t *str; dict_transaction_commit_callback_t *callback; void *context; }; struct memcached_ascii_dict { struct dict dict; struct ip_addr ip; char *username, *key_prefix; in_port_t port; unsigned int timeout_msecs; struct ioloop *ioloop, *prev_ioloop; struct timeout *to; struct memcached_ascii_connection conn; ARRAY(enum memcached_ascii_input_state) input_states; ARRAY(struct memcached_ascii_dict_reply) replies; }; static struct connection_list *memcached_ascii_connections; static void memcached_ascii_callback(struct memcached_ascii_dict *dict, const struct memcached_ascii_dict_reply *reply, int ret) { if (reply->callback != NULL) { if (dict->prev_ioloop != NULL) { /* Don't let callback see that we've created our internal ioloop in case it wants to add some ios or timeouts. */ current_ioloop = dict->prev_ioloop; } reply->callback(ret, reply->context); if (dict->prev_ioloop != NULL) current_ioloop = dict->ioloop; } } static void memcached_ascii_conn_destroy(struct connection *_conn) { struct memcached_ascii_connection *conn = (struct memcached_ascii_connection *)_conn; const struct memcached_ascii_dict_reply *reply; connection_disconnect(_conn); if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); array_foreach(&conn->dict->replies, reply) memcached_ascii_callback(conn->dict, reply, -1); array_clear(&conn->dict->replies); array_clear(&conn->dict->input_states); conn->reply_bytes_left = 0; } static bool memcached_ascii_input_value(struct memcached_ascii_connection *conn) { const unsigned char *data; size_t size; data = i_stream_get_data(conn->conn.input, &size); if (size > conn->reply_bytes_left) size = conn->reply_bytes_left; conn->reply_bytes_left -= size; str_append_n(conn->reply_str, data, size); i_stream_skip(conn->conn.input, size); if (conn->reply_bytes_left > 0) return FALSE; /* finished. drop the trailing CRLF */ str_truncate(conn->reply_str, str_len(conn->reply_str)-2); conn->value_received = TRUE; return TRUE; } static int memcached_ascii_input_reply_read(struct memcached_ascii_dict *dict) { struct memcached_ascii_connection *conn = &dict->conn; const enum memcached_ascii_input_state *states; const char *line, *p; unsigned int count; long long num; if (conn->reply_bytes_left > 0) { /* continue reading bulk reply */ if (!memcached_ascii_input_value(conn)) return 0; conn->value_waiting_end = TRUE; } else if (conn->value_waiting_end) { conn->value_waiting_end = FALSE; } else { str_truncate(conn->reply_str, 0); conn->value_received = FALSE; } line = i_stream_next_line(conn->conn.input); if (line == NULL) return 0; states = array_get(&dict->input_states, &count); if (count == 0) { i_error("memcached_ascii: Unexpected input (expected nothing): %s", line); return -1; } switch (states[0]) { case MEMCACHED_INPUT_STATE_GET: /* VALUE END */ if (strncmp(line, "VALUE ", 6) == 0) { p = strrchr(line, ' '); if (str_to_uint(p+1, &conn->reply_bytes_left) < 0) break; conn->reply_bytes_left += 2; /* CRLF */ return memcached_ascii_input_reply_read(dict); } else if (strcmp(line, "END") == 0) return 1; break; case MEMCACHED_INPUT_STATE_STORED: if (strcmp(line, "STORED") != 0 && strcmp(line, "NOT_STORED") != 0) break; return 1; case MEMCACHED_INPUT_STATE_DELETED: if (strcmp(line, "DELETED") != 0) break; return 1; case MEMCACHED_INPUT_STATE_INCRDECR: if (strcmp(line, "NOT_FOUND") != 0 && strcmp(line, "STORED") != 0 && strcmp(line, "NOT_STORED") != 0 && str_to_llong(line, &num) < 0) break; return 1; } i_error("memcached_ascii: Unexpected input (state=%d): %s", states[0], line); return -1; } static int memcached_ascii_input_reply(struct memcached_ascii_dict *dict) { struct memcached_ascii_dict_reply *replies; unsigned int count; int ret; if ((ret = memcached_ascii_input_reply_read(dict)) <= 0) return ret; /* finished a reply */ array_delete(&dict->input_states, 0, 1); replies = array_get_modifiable(&dict->replies, &count); i_assert(count > 0); i_assert(replies[0].reply_count > 0); if (--replies[0].reply_count == 0) { memcached_ascii_callback(dict, &replies[0], 1); array_delete(&dict->replies, 0, 1); } return 1; } static void memcached_ascii_conn_input(struct connection *_conn) { struct memcached_ascii_connection *conn = (struct memcached_ascii_connection *)_conn; int ret; switch (i_stream_read(_conn->input)) { case 0: return; case -1: memcached_ascii_conn_destroy(_conn); return; default: break; } while ((ret = memcached_ascii_input_reply(conn->dict)) > 0) ; if (ret < 0) memcached_ascii_conn_destroy(_conn); io_loop_stop(conn->dict->ioloop); } static int memcached_ascii_input_wait(struct memcached_ascii_dict *dict) { dict->prev_ioloop = current_ioloop; io_loop_set_current(dict->ioloop); if (dict->to != NULL) dict->to = io_loop_move_timeout(&dict->to); connection_switch_ioloop(&dict->conn.conn); io_loop_run(dict->ioloop); io_loop_set_current(dict->prev_ioloop); dict->prev_ioloop = NULL; if (dict->to != NULL) dict->to = io_loop_move_timeout(&dict->to); connection_switch_ioloop(&dict->conn.conn); return dict->conn.conn.fd_in == -1 ? -1 : 0; } static void memcached_ascii_input_timeout(struct memcached_ascii_dict *dict) { i_error("memcached_ascii: Request timed out in %u.%03u secs", dict->timeout_msecs/1000, dict->timeout_msecs%1000); memcached_ascii_conn_destroy(&dict->conn.conn); } static int memcached_ascii_wait_replies(struct memcached_ascii_dict *dict) { int ret = 0; dict->to = timeout_add(dict->timeout_msecs, memcached_ascii_input_timeout, dict); while (array_count(&dict->input_states) > 0) { i_assert(array_count(&dict->replies) > 0); if ((ret = memcached_ascii_input_reply(dict)) != 0) { if (ret < 0) memcached_ascii_conn_destroy(&dict->conn.conn); break; } ret = memcached_ascii_input_wait(dict); if (ret != 0) break; } timeout_remove(&dict->to); return ret < 0 ? -1 : 0; } static int memcached_ascii_wait(struct memcached_ascii_dict *dict) { int ret; i_assert(dict->conn.conn.fd_in != -1); if (dict->conn.conn.input == NULL) { /* waiting for connection to finish */ dict->to = timeout_add(dict->timeout_msecs, memcached_ascii_input_timeout, dict); ret = memcached_ascii_input_wait(dict); timeout_remove(&dict->to); if (ret < 0) return -1; } if (memcached_ascii_wait_replies(dict) < 0) return -1; i_assert(array_count(&dict->input_states) == 0); i_assert(array_count(&dict->replies) == 0); return 0; } static void memcached_ascii_conn_connected(struct connection *_conn, bool success) { struct memcached_ascii_connection *conn = (struct memcached_ascii_connection *)_conn; if (!success) { i_error("memcached_ascii: connect(%s, %u) failed: %m", net_ip2addr(&conn->dict->ip), conn->dict->port); } if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } static const struct connection_settings memcached_ascii_conn_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE }; static const struct connection_vfuncs memcached_ascii_conn_vfuncs = { .destroy = memcached_ascii_conn_destroy, .input = memcached_ascii_conn_input, .client_connected = memcached_ascii_conn_connected }; static const char *memcached_ascii_escape_username(const char *username) { const char *p; string_t *str = t_str_new(64); for (p = username; *p != '\0'; p++) { switch (*p) { case DICT_USERNAME_SEPARATOR: str_append(str, "\\-"); break; case '\\': str_append(str, "\\\\"); break; default: str_append_c(str, *p); } } return str_c(str); } static int memcached_ascii_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct memcached_ascii_dict *dict; const char *const *args; struct ioloop *old_ioloop = current_ioloop; int ret = 0; if (memcached_ascii_connections == NULL) { memcached_ascii_connections = connection_list_init(&memcached_ascii_conn_set, &memcached_ascii_conn_vfuncs); } dict = i_new(struct memcached_ascii_dict, 1); if (net_addr2ip("127.0.0.1", &dict->ip) < 0) i_unreached(); dict->port = MEMCACHED_DEFAULT_PORT; dict->timeout_msecs = MEMCACHED_DEFAULT_LOOKUP_TIMEOUT_MSECS; dict->key_prefix = i_strdup(""); args = t_strsplit(uri, ":"); for (; *args != NULL; args++) { if (strncmp(*args, "host=", 5) == 0) { if (net_addr2ip(*args+5, &dict->ip) < 0) { *error_r = t_strdup_printf("Invalid IP: %s", *args+5); ret = -1; } } else if (strncmp(*args, "port=", 5) == 0) { if (net_str2port(*args+5, &dict->port) < 0) { *error_r = t_strdup_printf("Invalid port: %s", *args+5); ret = -1; } } else if (strncmp(*args, "prefix=", 7) == 0) { i_free(dict->key_prefix); dict->key_prefix = i_strdup(*args + 7); } else if (strncmp(*args, "timeout_msecs=", 14) == 0) { if (str_to_uint(*args+14, &dict->timeout_msecs) < 0) { *error_r = t_strdup_printf( "Invalid timeout_msecs: %s", *args+14); ret = -1; } } else { *error_r = t_strdup_printf("Unknown parameter: %s", *args); ret = -1; } } if (ret < 0) { i_free(dict->key_prefix); i_free(dict); return -1; } connection_init_client_ip(memcached_ascii_connections, &dict->conn.conn, &dict->ip, dict->port); dict->dict = *driver; dict->conn.reply_str = str_new(default_pool, 256); dict->conn.dict = dict; if (strchr(set->username, DICT_USERNAME_SEPARATOR) == NULL) dict->username = i_strdup(set->username); else { /* escape the username */ dict->username = i_strdup(memcached_ascii_escape_username(set->username)); } i_array_init(&dict->input_states, 4); i_array_init(&dict->replies, 4); dict->ioloop = io_loop_create(); io_loop_set_current(old_ioloop); *dict_r = &dict->dict; return 0; } static void memcached_ascii_dict_deinit(struct dict *_dict) { struct memcached_ascii_dict *dict = (struct memcached_ascii_dict *)_dict; struct ioloop *old_ioloop = current_ioloop; if (array_count(&dict->input_states) > 0) (void)memcached_ascii_wait(dict); connection_deinit(&dict->conn.conn); io_loop_set_current(dict->ioloop); io_loop_destroy(&dict->ioloop); io_loop_set_current(old_ioloop); str_free(&dict->conn.reply_str); array_free(&dict->replies); array_free(&dict->input_states); i_free(dict->key_prefix); i_free(dict->username); i_free(dict); if (memcached_ascii_connections->connections == NULL) connection_list_deinit(&memcached_ascii_connections); } static int memcached_ascii_connect(struct memcached_ascii_dict *dict) { if (dict->conn.conn.input != NULL) return 0; if (dict->conn.conn.fd_in == -1) { if (connection_client_connect(&dict->conn.conn) < 0) { i_error("memcached_ascii: Couldn't connect to %s:%u", net_ip2addr(&dict->ip), dict->port); return -1; } } return memcached_ascii_wait(dict); } static const char * memcached_ascii_dict_get_full_key(struct memcached_ascii_dict *dict, const char *key) { if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0) key += strlen(DICT_PATH_SHARED); else if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) { key = t_strdup_printf("%s%c%s", dict->username, DICT_USERNAME_SEPARATOR, key + strlen(DICT_PATH_PRIVATE)); } else { i_unreached(); } if (*dict->key_prefix != '\0') key = t_strconcat(dict->key_prefix, key, NULL); return key; } static int memcached_ascii_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct memcached_ascii_dict *dict = (struct memcached_ascii_dict *)_dict; struct memcached_ascii_dict_reply *reply; enum memcached_ascii_input_state state = MEMCACHED_INPUT_STATE_GET; if (memcached_ascii_connect(dict) < 0) return -1; key = memcached_ascii_dict_get_full_key(dict, key); o_stream_nsend_str(dict->conn.conn.output, t_strdup_printf("get %s\r\n", key)); array_append(&dict->input_states, &state, 1); reply = array_append_space(&dict->replies); reply->reply_count = 1; if (memcached_ascii_wait(dict) < 0) return -1; *value_r = p_strdup(pool, str_c(dict->conn.reply_str)); return dict->conn.value_received ? 1 : 0; } static struct dict_transaction_context * memcached_ascii_transaction_init(struct dict *_dict) { struct dict_transaction_memory_context *ctx; pool_t pool; pool = pool_alloconly_create("file dict transaction", 2048); ctx = p_new(pool, struct dict_transaction_memory_context, 1); dict_transaction_memory_init(ctx, _dict, pool); return &ctx->ctx; } static void memcached_send_change(struct dict_memcached_ascii_commit_ctx *ctx, const struct dict_transaction_memory_change *change) { enum memcached_ascii_input_state state; const char *key, *value; key = memcached_ascii_dict_get_full_key(ctx->dict, change->key); str_truncate(ctx->str, 0); switch (change->type) { case DICT_CHANGE_TYPE_SET: state = MEMCACHED_INPUT_STATE_STORED; str_printfa(ctx->str, "set %s 0 0 %"PRIuSIZE_T"\r\n%s\r\n", key, strlen(change->value.str), change->value.str); break; case DICT_CHANGE_TYPE_UNSET: state = MEMCACHED_INPUT_STATE_DELETED; str_printfa(ctx->str, "delete %s\r\n", key); break; case DICT_CHANGE_TYPE_APPEND: state = MEMCACHED_INPUT_STATE_STORED; str_printfa(ctx->str, "append %s 0 0 %"PRIuSIZE_T"\r\n%s\r\n", key, strlen(change->value.str), change->value.str); array_append(&ctx->dict->input_states, &state, 1); /* we'd preferably want an append that always works, but this kludge works for that too.. */ str_printfa(ctx->str, "add %s 0 0 %"PRIuSIZE_T"\r\n%s\r\n", key, strlen(change->value.str), change->value.str); break; case DICT_CHANGE_TYPE_INC: state = MEMCACHED_INPUT_STATE_INCRDECR; if (change->value.diff > 0) { str_printfa(ctx->str, "incr %s %lld\r\n", key, change->value.diff); array_append(&ctx->dict->input_states, &state, 1); /* same kludge as with append */ value = t_strdup_printf("%lld", change->value.diff); str_printfa(ctx->str, "add %s 0 0 %u\r\n%s\r\n", key, (unsigned int)strlen(value), value); } else { str_printfa(ctx->str, "decr %s %lld\r\n", key, -change->value.diff); } break; } array_append(&ctx->dict->input_states, &state, 1); o_stream_nsend(ctx->dict->conn.conn.output, str_data(ctx->str), str_len(ctx->str)); } static int memcached_ascii_transaction_send(struct dict_memcached_ascii_commit_ctx *ctx) { struct memcached_ascii_dict *dict = ctx->dict; struct memcached_ascii_dict_reply *reply; const struct dict_transaction_memory_change *changes; unsigned int i, count, old_state_count; if (memcached_ascii_connect(dict) < 0) return -1; old_state_count = array_count(&dict->input_states); changes = array_get(&ctx->memctx->changes, &count); i_assert(count > 0); o_stream_cork(dict->conn.conn.output); for (i = 0; i < count; i++) T_BEGIN { memcached_send_change(ctx, &changes[i]); } T_END; o_stream_uncork(dict->conn.conn.output); reply = array_append_space(&dict->replies); reply->callback = ctx->callback; reply->context = ctx->context; reply->reply_count = array_count(&dict->input_states) - old_state_count; return 1; } static int memcached_ascii_transaction_commit(struct dict_transaction_context *_ctx, bool async, dict_transaction_commit_callback_t *callback, void *context) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; struct memcached_ascii_dict *dict = (struct memcached_ascii_dict *)_ctx->dict; struct dict_memcached_ascii_commit_ctx commit_ctx; int ret = 1; if (_ctx->changed) { i_zero(&commit_ctx); commit_ctx.dict = dict; commit_ctx.memctx = ctx; commit_ctx.callback = callback; commit_ctx.context = context; commit_ctx.str = str_new(default_pool, 128); ret = memcached_ascii_transaction_send(&commit_ctx); if (!async && ret >= 0) { if (memcached_ascii_wait(dict) < 0) ret = -1; } str_free(&commit_ctx.str); } if (callback != NULL) callback(ret, context); pool_unref(&ctx->pool); return ret; } struct dict dict_driver_memcached_ascii = { .name = "memcached_ascii", { .init = memcached_ascii_dict_init, .deinit = memcached_ascii_dict_deinit, .lookup = memcached_ascii_dict_lookup, .transaction_init = memcached_ascii_transaction_init, .transaction_commit = memcached_ascii_transaction_commit, .transaction_rollback = dict_transaction_memory_rollback, .set = dict_transaction_memory_set, .unset = dict_transaction_memory_unset, .append = dict_transaction_memory_append, .atomic_inc = dict_transaction_memory_atomic_inc, } }; dovecot-2.2.33.2/src/lib-dict/dict-client.c0000644000175000017500000012101513165463624015160 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "str.h" #include "strescape.h" #include "file-lock.h" #include "time-util.h" #include "connection.h" #include "ostream.h" #include "eacces-error.h" #include "dict-private.h" #include "dict-client.h" #include #include /* Disconnect from dict server after this many milliseconds of idling after sending a command. Because dict server does blocking dict accesses, it can handle only one client at a time. This is why the default timeout is zero, so that there won't be many dict processes just doing nothing. Zero means that the socket is disconnected immediately after returning to ioloop. */ #define DICT_CLIENT_DEFAULT_TIMEOUT_MSECS 0 /* Abort dict lookup after this many seconds. */ #define DICT_CLIENT_REQUEST_TIMEOUT_MSECS 30000 /* When dict lookup timeout is reached, wait a bit longer if the last dict ioloop wait was shorter than this. */ #define DICT_CLIENT_REQUEST_TIMEOUT_MIN_LAST_IOLOOP_WAIT_MSECS 1000 /* Log a warning if dict lookup takes longer than this many milliseconds. */ #define DICT_CLIENT_DEFAULT_WARN_SLOW_MSECS 5000 struct client_dict_cmd { int refcount; struct client_dict *dict; struct timeval start_time; char *query; unsigned int async_id; struct timeval async_id_received_time; uint64_t start_global_ioloop_usecs; uint64_t start_dict_ioloop_usecs; uint64_t start_lock_usecs; bool reconnected; bool retry_errors; bool no_replies; bool unfinished; bool background; void (*callback)(struct client_dict_cmd *cmd, enum dict_protocol_reply reply, const char *value, const char *const *extra_args, const char *error, bool disconnected); struct client_dict_iterate_context *iter; struct client_dict_transaction_context *trans; struct { dict_lookup_callback_t *lookup; dict_transaction_commit_callback_t *commit; void *context; } api_callback; }; struct dict_connection { struct connection conn; struct client_dict *dict; }; struct client_dict { struct dict dict; struct dict_connection conn; char *uri, *username; enum dict_data_type value_type; unsigned warn_slow_msecs; time_t last_failed_connect; char *last_connect_error; struct ioloop *ioloop, *prev_ioloop; struct io_wait_timer *wait_timer; uint64_t last_timer_switch_usecs; struct timeout *to_requests; struct timeout *to_idle; unsigned int idle_msecs; ARRAY(struct client_dict_cmd *) cmds; struct client_dict_transaction_context *transactions; unsigned int transaction_id_counter; }; struct client_dict_iter_result { const char *key, *value; }; struct client_dict_iterate_context { struct dict_iterate_context ctx; char *error; const char **paths; enum dict_iterate_flags flags; pool_t results_pool; ARRAY(struct client_dict_iter_result) results; unsigned int result_idx; bool cmd_sent; bool seen_results; bool finished; bool deinit; }; struct client_dict_transaction_context { struct dict_transaction_context ctx; struct client_dict_transaction_context *prev, *next; char *first_query; char *error; unsigned int id; unsigned int query_count; bool sent_begin:1; }; static struct connection_list *dict_connections; static int client_dict_connect(struct client_dict *dict, const char **error_r); static int client_dict_reconnect(struct client_dict *dict, const char *reason, const char **error_r); static void client_dict_disconnect(struct client_dict *dict, const char *reason); static const char *dict_wait_warnings(const struct client_dict_cmd *cmd); static struct client_dict_cmd * client_dict_cmd_init(struct client_dict *dict, const char *query) { struct client_dict_cmd *cmd; io_loop_time_refresh(); cmd = i_new(struct client_dict_cmd, 1); cmd->refcount = 1; cmd->dict = dict; cmd->query = i_strdup(query); cmd->start_time = ioloop_timeval; cmd->start_global_ioloop_usecs = ioloop_global_wait_usecs; cmd->start_dict_ioloop_usecs = io_wait_timer_get_usecs(dict->wait_timer); cmd->start_lock_usecs = file_lock_wait_get_total_usecs(); return cmd; } static void client_dict_cmd_ref(struct client_dict_cmd *cmd) { i_assert(cmd->refcount > 0); cmd->refcount++; } static bool client_dict_cmd_unref(struct client_dict_cmd *cmd) { i_assert(cmd->refcount > 0); if (--cmd->refcount > 0) return TRUE; i_assert(cmd->trans == NULL); i_free(cmd->query); i_free(cmd); return FALSE; } static void dict_pre_api_callback(struct client_dict *dict) { if (dict->prev_ioloop != NULL) { /* Don't let callback see that we've created our internal ioloop in case it wants to add some ios or timeouts. */ current_ioloop = dict->prev_ioloop; } } static void dict_post_api_callback(struct client_dict *dict) { if (dict->prev_ioloop != NULL) { current_ioloop = dict->ioloop; /* stop client_dict_wait() */ io_loop_stop(dict->ioloop); } } static bool dict_cmd_callback_line(struct client_dict_cmd *cmd, const char *const *args) { const char *value = args[0]; enum dict_protocol_reply reply; if (value == NULL) { /* "" is a valid iteration reply */ reply = DICT_PROTOCOL_REPLY_ITER_FINISHED; } else { reply = value[0]; value++; args++; } cmd->unfinished = FALSE; cmd->callback(cmd, reply, value, args, NULL, FALSE); return !cmd->unfinished; } static void dict_cmd_callback_error(struct client_dict_cmd *cmd, const char *error, bool disconnected) { const char *null_arg = NULL; cmd->unfinished = FALSE; if (cmd->callback != NULL) { cmd->callback(cmd, DICT_PROTOCOL_REPLY_ERROR, "", &null_arg, error, disconnected); } i_assert(!cmd->unfinished); } static struct client_dict_cmd * client_dict_cmd_first_nonbg(struct client_dict *dict) { struct client_dict_cmd *const *cmds; unsigned int i, count; cmds = array_get(&dict->cmds, &count); for (i = 0; i < count; i++) { if (!cmds[i]->background) return cmds[i]; } return NULL; } static void client_dict_input_timeout(struct client_dict *dict) { struct client_dict_cmd *cmd; const char *error; uint64_t msecs_in_last_dict_ioloop_wait; int cmd_diff; /* find the first non-background command. there must be at least one. */ cmd = client_dict_cmd_first_nonbg(dict); i_assert(cmd != NULL); cmd_diff = timeval_diff_msecs(&ioloop_timeval, &cmd->start_time); if (cmd_diff < DICT_CLIENT_REQUEST_TIMEOUT_MSECS) { /* need to re-create this timeout. the currently-oldest command was added when another command was still running with an older timeout. */ timeout_remove(&dict->to_requests); dict->to_requests = timeout_add(DICT_CLIENT_REQUEST_TIMEOUT_MSECS - cmd_diff, client_dict_input_timeout, dict); return; } /* If we've gotten here because all the time was spent in other ioloops or locks, make sure there's a bit of time waiting for the dict ioloop as well. There's a good chance that the reply can be read. */ msecs_in_last_dict_ioloop_wait = (io_wait_timer_get_usecs(dict->wait_timer) - dict->last_timer_switch_usecs + 999) / 1000; if (msecs_in_last_dict_ioloop_wait < DICT_CLIENT_REQUEST_TIMEOUT_MIN_LAST_IOLOOP_WAIT_MSECS) { timeout_remove(&dict->to_requests); dict->to_requests = timeout_add(DICT_CLIENT_REQUEST_TIMEOUT_MIN_LAST_IOLOOP_WAIT_MSECS - msecs_in_last_dict_ioloop_wait, client_dict_input_timeout, dict); return; } (void)client_dict_reconnect(dict, t_strdup_printf( "Dict server timeout: %s " "(%u commands pending, oldest sent %u.%03u secs ago: %s, %s)", connection_input_timeout_reason(&dict->conn.conn), array_count(&dict->cmds), cmd_diff/1000, cmd_diff%1000, cmd->query, dict_wait_warnings(cmd)), &error); } static int client_dict_cmd_query_send(struct client_dict *dict, const char *query) { struct const_iovec iov[2]; ssize_t ret; iov[0].iov_base = query; iov[0].iov_len = strlen(query); iov[1].iov_base = "\n"; iov[1].iov_len = 1; ret = o_stream_sendv(dict->conn.conn.output, iov, 2); if (ret < 0) return -1; i_assert((size_t)ret == iov[0].iov_len + 1); return 0; } static bool client_dict_cmd_send(struct client_dict *dict, struct client_dict_cmd **_cmd, const char **error_r) { struct client_dict_cmd *cmd = *_cmd; const char *error = NULL; bool retry = cmd->retry_errors; int ret; *_cmd = NULL; /* we're no longer idling. even with no_replies=TRUE we're going to wait for COMMIT/ROLLBACK. */ if (dict->to_idle != NULL) timeout_remove(&dict->to_idle); if (client_dict_connect(dict, &error) < 0) { retry = FALSE; ret = -1; } else { ret = client_dict_cmd_query_send(dict, cmd->query); if (ret < 0) { error = t_strdup_printf("write(%s) failed: %s", dict->conn.conn.name, o_stream_get_error(dict->conn.conn.output)); } } if (ret < 0 && retry) { /* Reconnect and try again. */ if (client_dict_reconnect(dict, error, &error) < 0) ; else if (client_dict_cmd_query_send(dict, cmd->query) < 0) { error = t_strdup_printf("write(%s) failed: %s", dict->conn.conn.name, o_stream_get_error(dict->conn.conn.output)); } else { ret = 0; } } if (cmd->no_replies) { /* just send and forget */ client_dict_cmd_unref(cmd); return TRUE; } else if (ret < 0) { i_assert(error != NULL); /* we didn't successfully send this command to dict */ dict_cmd_callback_error(cmd, error, cmd->reconnected); client_dict_cmd_unref(cmd); if (error_r != NULL) *error_r = error; return FALSE; } else { if (dict->to_requests == NULL && !cmd->background) { dict->to_requests = timeout_add(DICT_CLIENT_REQUEST_TIMEOUT_MSECS, client_dict_input_timeout, dict); } array_append(&dict->cmds, &cmd, 1); return TRUE; } } static bool client_dict_transaction_send_begin(struct client_dict_transaction_context *ctx) { struct client_dict *dict = (struct client_dict *)ctx->ctx.dict; struct client_dict_cmd *cmd; const char *query, *error; i_assert(ctx->error == NULL); ctx->sent_begin = TRUE; /* transactions commands don't have replies. only COMMIT has. */ query = t_strdup_printf("%c%u", DICT_PROTOCOL_CMD_BEGIN, ctx->id); cmd = client_dict_cmd_init(dict, query); cmd->no_replies = TRUE; cmd->retry_errors = TRUE; if (!client_dict_cmd_send(dict, &cmd, &error)) { ctx->error = i_strdup(error); return FALSE; } return TRUE; } static void client_dict_send_transaction_query(struct client_dict_transaction_context *ctx, const char *query) { struct client_dict *dict = (struct client_dict *)ctx->ctx.dict; struct client_dict_cmd *cmd; const char *error; if (ctx->error != NULL) return; if (!ctx->sent_begin) { if (!client_dict_transaction_send_begin(ctx)) return; } ctx->query_count++; if (ctx->first_query == NULL) ctx->first_query = i_strdup(query); cmd = client_dict_cmd_init(dict, query); cmd->no_replies = TRUE; if (!client_dict_cmd_send(dict, &cmd, &error)) ctx->error = i_strdup(error); } static bool client_dict_is_finished(struct client_dict *dict) { return dict->transactions == NULL && array_count(&dict->cmds) == 0; } static void client_dict_timeout(struct client_dict *dict) { if (client_dict_is_finished(dict)) client_dict_disconnect(dict, "Idle disconnection"); } static bool client_dict_have_nonbackground_cmds(struct client_dict *dict) { struct client_dict_cmd *const *cmdp; array_foreach(&dict->cmds, cmdp) { if (!(*cmdp)->background) return TRUE; } return FALSE; } static void client_dict_add_timeout(struct client_dict *dict) { if (dict->to_idle != NULL) { if (dict->idle_msecs > 0) timeout_reset(dict->to_idle); } else if (client_dict_is_finished(dict)) { dict->to_idle = timeout_add(dict->idle_msecs, client_dict_timeout, dict); if (dict->to_requests != NULL) timeout_remove(&dict->to_requests); } else if (dict->transactions == NULL && !client_dict_have_nonbackground_cmds(dict)) { /* we had non-background commands, but now we're back to having only background commands. remove timeouts. */ if (dict->to_requests != NULL) timeout_remove(&dict->to_requests); } } static void client_dict_cmd_backgrounded(struct client_dict *dict) { if (dict->to_requests == NULL) return; if (!client_dict_have_nonbackground_cmds(dict)) { /* we only have background-commands. remove the request timeout. */ timeout_remove(&dict->to_requests); } } static int dict_conn_assign_next_async_id(struct dict_connection *conn, const char *line) { struct client_dict_cmd *const *cmds; unsigned int i, count, async_id; i_assert(line[0] == DICT_PROTOCOL_REPLY_ASYNC_ID); if (str_to_uint(line+1, &async_id) < 0 || async_id == 0) { i_error("%s: Received invalid async-id line: %s", conn->conn.name, line); return -1; } cmds = array_get(&conn->dict->cmds, &count); for (i = 0; i < count; i++) { if (cmds[i]->async_id == 0) { cmds[i]->async_id = async_id; cmds[i]->async_id_received_time = ioloop_timeval; return 0; } } i_error("%s: Received async-id line, but all %u commands already have it: %s", conn->conn.name, count, line); return -1; } static int dict_conn_find_async_id(struct dict_connection *conn, const char *async_arg, const char *line, unsigned int *idx_r) { struct client_dict_cmd *const *cmds; unsigned int i, count, async_id; i_assert(async_arg[0] == DICT_PROTOCOL_REPLY_ASYNC_REPLY); if (str_to_uint(async_arg+1, &async_id) < 0 || async_id == 0) { i_error("%s: Received invalid async-reply line: %s", conn->conn.name, line); return -1; } cmds = array_get(&conn->dict->cmds, &count); for (i = 0; i < count; i++) { if (cmds[i]->async_id == async_id) { *idx_r = i; return 0; } } i_error("%s: Received reply for nonexistent async-id %u: %s", conn->conn.name, async_id, line); return -1; } static int dict_conn_input_line(struct connection *_conn, const char *line) { struct dict_connection *conn = (struct dict_connection *)_conn; struct client_dict *dict = conn->dict; struct client_dict_cmd *const *cmds; const char *const *args; unsigned int i, count; bool finished; if (dict->to_requests != NULL) timeout_reset(dict->to_requests); if (line[0] == DICT_PROTOCOL_REPLY_ASYNC_ID) return dict_conn_assign_next_async_id(conn, line) < 0 ? -1 : 1; cmds = array_get(&conn->dict->cmds, &count); if (count == 0) { i_error("%s: Received reply without pending commands: %s", dict->conn.conn.name, line); return -1; } args = t_strsplit_tabescaped(line); if (args[0] != NULL && args[0][0] == DICT_PROTOCOL_REPLY_ASYNC_REPLY) { if (dict_conn_find_async_id(conn, args[0], line, &i) < 0) return -1; args++; } else { i = 0; } i_assert(!cmds[i]->no_replies); client_dict_cmd_ref(cmds[i]); finished = dict_cmd_callback_line(cmds[i], args); if (!client_dict_cmd_unref(cmds[i])) { /* disconnected during command handling */ return -1; } if (!finished) { /* more lines needed for this command */ return 1; } client_dict_cmd_unref(cmds[i]); array_delete(&dict->cmds, i, 1); client_dict_add_timeout(dict); return 1; } static int client_dict_connect(struct client_dict *dict, const char **error_r) { const char *query, *error; if (dict->conn.conn.fd_in != -1) return 0; if (dict->last_failed_connect == ioloop_time) { /* Try again later */ *error_r = dict->last_connect_error; return -1; } if (connection_client_connect(&dict->conn.conn) < 0) { dict->last_failed_connect = ioloop_time; if (errno == EACCES) { error = eacces_error_get("net_connect_unix", dict->conn.conn.name); } else { error = t_strdup_printf( "net_connect_unix(%s) failed: %m", dict->conn.conn.name); } i_free(dict->last_connect_error); dict->last_connect_error = i_strdup(error); *error_r = error; return -1; } query = t_strdup_printf("%c%u\t%u\t%d\t%s\t%s\n", DICT_PROTOCOL_CMD_HELLO, DICT_CLIENT_PROTOCOL_MAJOR_VERSION, DICT_CLIENT_PROTOCOL_MINOR_VERSION, dict->value_type, dict->username, dict->uri); o_stream_nsend_str(dict->conn.conn.output, query); client_dict_add_timeout(dict); return 0; } static void client_dict_abort_commands(struct client_dict *dict, const char *reason) { ARRAY(struct client_dict_cmd *) cmds_copy; struct client_dict_cmd *const *cmdp; /* abort all commands */ t_array_init(&cmds_copy, array_count(&dict->cmds)); array_append_array(&cmds_copy, &dict->cmds); array_clear(&dict->cmds); array_foreach(&cmds_copy, cmdp) { dict_cmd_callback_error(*cmdp, reason, TRUE); client_dict_cmd_unref(*cmdp); } } static void client_dict_disconnect(struct client_dict *dict, const char *reason) { struct client_dict_transaction_context *ctx, *next; client_dict_abort_commands(dict, reason); /* all transactions that have sent BEGIN are no longer valid */ for (ctx = dict->transactions; ctx != NULL; ctx = next) { next = ctx->next; if (ctx->sent_begin && ctx->error == NULL) ctx->error = i_strdup(reason); } if (dict->to_idle != NULL) timeout_remove(&dict->to_idle); if (dict->to_requests != NULL) timeout_remove(&dict->to_requests); connection_disconnect(&dict->conn.conn); } static int client_dict_reconnect(struct client_dict *dict, const char *reason, const char **error_r) { ARRAY(struct client_dict_cmd *) retry_cmds; struct client_dict_cmd *const *cmdp, *cmd; const char *error; int ret; t_array_init(&retry_cmds, array_count(&dict->cmds)); for (unsigned int i = 0; i < array_count(&dict->cmds); ) { cmdp = array_idx(&dict->cmds, i); if (!(*cmdp)->retry_errors) { i++; } else if ((*cmdp)->iter != NULL && (*cmdp)->iter->seen_results) { /* don't retry iteration that already returned something to the caller. otherwise we'd return duplicates. */ i++; } else { array_append(&retry_cmds, cmdp, 1); array_delete(&dict->cmds, i, 1); } } client_dict_disconnect(dict, reason); if (client_dict_connect(dict, error_r) < 0) { reason = t_strdup_printf("%s - reconnect failed: %s", reason, *error_r); array_foreach(&retry_cmds, cmdp) { dict_cmd_callback_error(*cmdp, reason, TRUE); client_dict_cmd_unref(*cmdp); } return -1; } if (array_count(&retry_cmds) == 0) return 0; i_warning("%s - reconnected", reason); ret = 0; error = ""; array_foreach(&retry_cmds, cmdp) { cmd = *cmdp; cmd->reconnected = TRUE; cmd->async_id = 0; /* if it fails again, don't retry anymore */ cmd->retry_errors = FALSE; if (ret < 0) { dict_cmd_callback_error(cmd, error, TRUE); client_dict_cmd_unref(cmd); } else if (!client_dict_cmd_send(dict, &cmd, &error)) ret = -1; } return ret; } static void dict_conn_destroy(struct connection *_conn) { struct dict_connection *conn = (struct dict_connection *)_conn; client_dict_disconnect(conn->dict, connection_disconnect_reason(_conn)); } static const struct connection_settings dict_conn_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .unix_client_connect_msecs = 1000, .client = TRUE }; static const struct connection_vfuncs dict_conn_vfuncs = { .destroy = dict_conn_destroy, .input_line = dict_conn_input_line }; static int client_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct ioloop *old_ioloop = current_ioloop; struct client_dict *dict; const char *p, *dest_uri, *path; unsigned int idle_msecs = DICT_CLIENT_DEFAULT_TIMEOUT_MSECS; unsigned int warn_slow_msecs = DICT_CLIENT_DEFAULT_WARN_SLOW_MSECS; /* uri = [idle_msecs=:] [warn_slow_msecs=:] [] ":" */ for (;;) { if (strncmp(uri, "idle_msecs=", 11) == 0) { p = strchr(uri+11, ':'); if (p == NULL) { *error_r = t_strdup_printf("Invalid URI: %s", uri); return -1; } if (str_to_uint(t_strdup_until(uri+11, p), &idle_msecs) < 0) { *error_r = "Invalid idle_msecs"; return -1; } uri = p+1; } else if (strncmp(uri, "warn_slow_msecs=", 16) == 0) { p = strchr(uri+11, ':'); if (p == NULL) { *error_r = t_strdup_printf("Invalid URI: %s", uri); return -1; } if (str_to_uint(t_strdup_until(uri+16, p), &warn_slow_msecs) < 0) { *error_r = "Invalid warn_slow_msecs"; return -1; } uri = p+1; } else { break; } } dest_uri = strchr(uri, ':'); if (dest_uri == NULL) { *error_r = t_strdup_printf("Invalid URI: %s", uri); return -1; } if (dict_connections == NULL) { dict_connections = connection_list_init(&dict_conn_set, &dict_conn_vfuncs); } dict = i_new(struct client_dict, 1); dict->dict = *driver; dict->conn.dict = dict; dict->value_type = set->value_type; dict->username = i_strdup(set->username); dict->idle_msecs = idle_msecs; dict->warn_slow_msecs = warn_slow_msecs; i_array_init(&dict->cmds, 32); if (uri[0] == ':') { /* default path */ path = t_strconcat(set->base_dir, "/"DEFAULT_DICT_SERVER_SOCKET_FNAME, NULL); } else if (uri[0] == '/') { /* absolute path */ path = t_strdup_until(uri, dest_uri); } else { /* relative path to base_dir */ path = t_strconcat(set->base_dir, "/", t_strdup_until(uri, dest_uri), NULL); } connection_init_client_unix(dict_connections, &dict->conn.conn, path); dict->uri = i_strdup(dest_uri + 1); dict->ioloop = io_loop_create(); dict->wait_timer = io_wait_timer_add(); io_loop_set_current(old_ioloop); *dict_r = &dict->dict; return 0; } static void client_dict_deinit(struct dict *_dict) { struct client_dict *dict = (struct client_dict *)_dict; struct ioloop *old_ioloop = current_ioloop; client_dict_disconnect(dict, "Deinit"); connection_deinit(&dict->conn.conn); io_wait_timer_remove(&dict->wait_timer); i_assert(dict->transactions == NULL); i_assert(array_count(&dict->cmds) == 0); io_loop_set_current(dict->ioloop); io_loop_destroy(&dict->ioloop); io_loop_set_current(old_ioloop); array_free(&dict->cmds); i_free(dict->last_connect_error); i_free(dict->username); i_free(dict->uri); i_free(dict); if (dict_connections->connections == NULL) connection_list_deinit(&dict_connections); } static int client_dict_wait(struct dict *_dict) { struct client_dict *dict = (struct client_dict *)_dict; if (array_count(&dict->cmds) == 0) return 0; dict->prev_ioloop = current_ioloop; io_loop_set_current(dict->ioloop); dict_switch_ioloop(_dict); while (array_count(&dict->cmds) > 0) io_loop_run(dict->ioloop); io_loop_set_current(dict->prev_ioloop); dict->prev_ioloop = NULL; dict_switch_ioloop(_dict); return 0; } static bool client_dict_switch_ioloop(struct dict *_dict) { struct client_dict *dict = (struct client_dict *)_dict; dict->last_timer_switch_usecs = io_wait_timer_get_usecs(dict->wait_timer); dict->wait_timer = io_wait_timer_move(&dict->wait_timer); if (dict->to_idle != NULL) dict->to_idle = io_loop_move_timeout(&dict->to_idle); if (dict->to_requests != NULL) dict->to_requests = io_loop_move_timeout(&dict->to_requests); connection_switch_ioloop(&dict->conn.conn); return array_count(&dict->cmds) > 0; } static const char *dict_wait_warnings(const struct client_dict_cmd *cmd) { int global_ioloop_msecs = (ioloop_global_wait_usecs - cmd->start_global_ioloop_usecs + 999) / 1000; int dict_ioloop_msecs = (io_wait_timer_get_usecs(cmd->dict->wait_timer) - cmd->start_dict_ioloop_usecs + 999) / 1000; int other_ioloop_msecs = global_ioloop_msecs - dict_ioloop_msecs; int lock_msecs = (file_lock_wait_get_total_usecs() - cmd->start_lock_usecs + 999) / 1000; return t_strdup_printf( "%d.%03d in dict wait, %d.%03d in other ioloops, %d.%03d in locks", dict_ioloop_msecs/1000, dict_ioloop_msecs%1000, other_ioloop_msecs/1000, other_ioloop_msecs%1000, lock_msecs/1000, lock_msecs%1000); } static const char * dict_warnings_sec(const struct client_dict_cmd *cmd, int msecs, const char *const *extra_args) { string_t *str = t_str_new(64); struct timeval tv_start, tv_end; unsigned int tv_start_usec, tv_end_usec; str_printfa(str, "%d.%03d secs (%s", msecs/1000, msecs%1000, dict_wait_warnings(cmd)); if (cmd->reconnected) { int reconnected_msecs = timeval_diff_msecs(&ioloop_timeval, &cmd->dict->conn.conn.connect_started); str_printfa(str, ", reconnected %u.%03u secs ago", reconnected_msecs/1000, reconnected_msecs%1000); } if (cmd->async_id != 0) { int async_reply_msecs = timeval_diff_msecs(&ioloop_timeval, &cmd->async_id_received_time); str_printfa(str, ", async-id reply %u.%03u secs ago", async_reply_msecs/1000, async_reply_msecs%1000); } if (extra_args != NULL && str_array_length(extra_args) >= 4 && str_to_time(extra_args[0], &tv_start.tv_sec) == 0 && str_to_uint(extra_args[1], &tv_start_usec) == 0 && str_to_time(extra_args[2], &tv_end.tv_sec) == 0 && str_to_uint(extra_args[3], &tv_end_usec) == 0) { tv_start.tv_usec = tv_start_usec; tv_end.tv_usec = tv_end_usec; int server_msecs_since_start = timeval_diff_msecs(&ioloop_timeval, &tv_start); int server_msecs = timeval_diff_msecs(&tv_end, &tv_start); str_printfa(str, ", started on dict-server %u.%03d secs ago, " "took %u.%03d secs", server_msecs_since_start/1000, server_msecs_since_start%1000, server_msecs/1000, server_msecs%1000); } str_append_c(str, ')'); return str_c(str); } static void client_dict_lookup_async_callback(struct client_dict_cmd *cmd, enum dict_protocol_reply reply, const char *value, const char *const *extra_args, const char *error, bool disconnected ATTR_UNUSED) { struct client_dict *dict = cmd->dict; struct dict_lookup_result result; const char *const values[] = { value, NULL }; i_zero(&result); if (error != NULL) { result.ret = -1; result.error = error; } else switch (reply) { case DICT_PROTOCOL_REPLY_OK: result.value = value; result.values = values; result.ret = 1; break; case DICT_PROTOCOL_REPLY_MULTI_OK: result.values = t_strsplit_tabescaped(value); result.value = result.values[0]; result.ret = 1; break; case DICT_PROTOCOL_REPLY_NOTFOUND: result.ret = 0; break; case DICT_PROTOCOL_REPLY_FAIL: result.error = value[0] == '\0' ? "dict-server returned failure" : t_strdup_printf("dict-server returned failure: %s", value); result.ret = -1; break; default: result.error = t_strdup_printf( "dict-client: Invalid lookup '%s' reply: %c%s", cmd->query, reply, value); client_dict_disconnect(dict, result.error); result.ret = -1; break; } int diff = timeval_diff_msecs(&ioloop_timeval, &cmd->start_time); if (result.error != NULL) { /* include timing info always in error messages */ result.error = t_strdup_printf("%s (reply took %s)", result.error, dict_warnings_sec(cmd, diff, extra_args)); } else if (!cmd->background && diff >= (int)dict->warn_slow_msecs) { i_warning("read(%s): dict lookup took %s: %s", dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args), cmd->query); } dict_pre_api_callback(dict); cmd->api_callback.lookup(&result, cmd->api_callback.context); dict_post_api_callback(dict); } static void client_dict_lookup_async(struct dict *_dict, const char *key, dict_lookup_callback_t *callback, void *context) { struct client_dict *dict = (struct client_dict *)_dict; struct client_dict_cmd *cmd; const char *query; query = t_strdup_printf("%c%s", DICT_PROTOCOL_CMD_LOOKUP, str_tabescape(key)); cmd = client_dict_cmd_init(dict, query); cmd->callback = client_dict_lookup_async_callback; cmd->api_callback.lookup = callback; cmd->api_callback.context = context; cmd->retry_errors = TRUE; client_dict_cmd_send(dict, &cmd, NULL); } struct client_dict_sync_lookup { char *error; char *value; int ret; }; static void client_dict_lookup_callback(const struct dict_lookup_result *result, void *context) { struct client_dict_sync_lookup *lookup = context; lookup->ret = result->ret; if (result->ret == -1) lookup->error = i_strdup(result->error); else if (result->ret == 1) lookup->value = i_strdup(result->value); } static int client_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct client_dict_sync_lookup lookup; i_zero(&lookup); lookup.ret = -2; client_dict_lookup_async(_dict, key, client_dict_lookup_callback, &lookup); if (lookup.ret == -2) client_dict_wait(_dict); switch (lookup.ret) { case -1: i_error("dict-client: Lookup '%s' failed: %s", key, lookup.error); i_free(lookup.error); return -1; case 0: i_assert(lookup.value == NULL); *value_r = NULL; return 0; case 1: *value_r = p_strdup(pool, lookup.value); i_free(lookup.value); return 1; } i_unreached(); } static void client_dict_iterate_free(struct client_dict_iterate_context *ctx) { if (!ctx->deinit || !ctx->finished) return; i_free(ctx->error); i_free(ctx); } static void client_dict_iter_api_callback(struct client_dict_iterate_context *ctx, struct client_dict_cmd *cmd, const char *const *extra_args) { struct client_dict *dict = cmd->dict; if (ctx->deinit) { /* iterator was already deinitialized */ return; } if (ctx->finished) { int diff = timeval_diff_msecs(&ioloop_timeval, &cmd->start_time); if (ctx->error != NULL) { /* include timing info always in error messages */ char *new_error = i_strdup_printf("%s (reply took %s)", ctx->error, dict_warnings_sec(cmd, diff, extra_args)); i_free(ctx->error); ctx->error = new_error; } else if (!cmd->background && diff >= (int)dict->warn_slow_msecs) { i_warning("read(%s): dict iteration took %s: %s", dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args), cmd->query); } } if (ctx->ctx.async_callback != NULL) { dict_pre_api_callback(dict); ctx->ctx.async_callback(ctx->ctx.async_context); dict_post_api_callback(dict); } else { /* synchronous lookup */ io_loop_stop(dict->ioloop); } } static void client_dict_iter_async_callback(struct client_dict_cmd *cmd, enum dict_protocol_reply reply, const char *value, const char *const *extra_args, const char *error, bool disconnected ATTR_UNUSED) { struct client_dict_iterate_context *ctx = cmd->iter; struct client_dict *dict = cmd->dict; struct client_dict_iter_result *result; const char *iter_key = NULL, *iter_value = NULL; if (ctx->deinit) { cmd->background = TRUE; client_dict_cmd_backgrounded(dict); } if (error != NULL) { /* failed */ } else switch (reply) { case DICT_PROTOCOL_REPLY_ITER_FINISHED: /* end of iteration */ ctx->finished = TRUE; client_dict_iter_api_callback(ctx, cmd, extra_args); client_dict_iterate_free(ctx); return; case DICT_PROTOCOL_REPLY_OK: /* key \t value */ iter_key = value; iter_value = extra_args[0]; extra_args++; break; case DICT_PROTOCOL_REPLY_FAIL: error = t_strdup_printf("dict-server returned failure: %s", value); break; default: break; } if (iter_value == NULL && error == NULL) { /* broken protocol */ error = t_strdup_printf("dict client (%s) sent broken iterate reply: %c%s", dict->conn.conn.name, reply, value); client_dict_disconnect(dict, error); } if (error != NULL) { if (ctx->error == NULL) ctx->error = i_strdup(error); ctx->finished = TRUE; client_dict_iter_api_callback(ctx, cmd, extra_args); client_dict_iterate_free(ctx); return; } cmd->unfinished = TRUE; if (ctx->deinit) { /* iterator was already deinitialized */ return; } result = array_append_space(&ctx->results); result->key = p_strdup(ctx->results_pool, iter_key); result->value = p_strdup(ctx->results_pool, iter_value); client_dict_iter_api_callback(ctx, cmd, NULL); } static struct dict_iterate_context * client_dict_iterate_init(struct dict *_dict, const char *const *paths, enum dict_iterate_flags flags) { struct client_dict_iterate_context *ctx; ctx = i_new(struct client_dict_iterate_context, 1); ctx->ctx.dict = _dict; ctx->results_pool = pool_alloconly_create("client dict iteration", 512); ctx->flags = flags; ctx->paths = p_strarray_dup(system_pool, paths); i_array_init(&ctx->results, 64); return &ctx->ctx; } static void client_dict_iterate_cmd_send(struct client_dict_iterate_context *ctx) { struct client_dict *dict = (struct client_dict *)ctx->ctx.dict; struct client_dict_cmd *cmd; unsigned int i; string_t *query = t_str_new(256); /* we can't do this query in _iterate_init(), because _set_limit() hasn't been called yet at that point. */ str_printfa(query, "%c%d\t%llu", DICT_PROTOCOL_CMD_ITERATE, ctx->flags, (unsigned long long)ctx->ctx.max_rows); for (i = 0; ctx->paths[i] != NULL; i++) { str_append_c(query, '\t'); str_append(query, str_tabescape(ctx->paths[i])); } cmd = client_dict_cmd_init(dict, str_c(query)); cmd->iter = ctx; cmd->callback = client_dict_iter_async_callback; cmd->retry_errors = TRUE; client_dict_cmd_send(dict, &cmd, NULL); } static bool client_dict_iterate(struct dict_iterate_context *_ctx, const char **key_r, const char **value_r) { struct client_dict_iterate_context *ctx = (struct client_dict_iterate_context *)_ctx; const struct client_dict_iter_result *results; unsigned int count; if (ctx->error != NULL) { ctx->ctx.has_more = FALSE; return FALSE; } results = array_get(&ctx->results, &count); if (ctx->result_idx < count) { *key_r = results[ctx->result_idx].key; *value_r = results[ctx->result_idx].value; ctx->ctx.has_more = TRUE; ctx->result_idx++; ctx->seen_results = TRUE; return TRUE; } if (!ctx->cmd_sent) { ctx->cmd_sent = TRUE; client_dict_iterate_cmd_send(ctx); return client_dict_iterate(_ctx, key_r, value_r); } ctx->ctx.has_more = !ctx->finished; ctx->result_idx = 0; array_clear(&ctx->results); p_clear(ctx->results_pool); if ((ctx->flags & DICT_ITERATE_FLAG_ASYNC) == 0 && ctx->ctx.has_more) { client_dict_wait(_ctx->dict); return client_dict_iterate(_ctx, key_r, value_r); } return FALSE; } static int client_dict_iterate_deinit(struct dict_iterate_context *_ctx) { struct client_dict *dict = (struct client_dict *)_ctx->dict; struct client_dict_iterate_context *ctx = (struct client_dict_iterate_context *)_ctx; int ret = ctx->error != NULL ? -1 : 0; ctx->deinit = TRUE; if (ret < 0) i_error("dict-client: Iteration failed: %s", ctx->error); array_free(&ctx->results); pool_unref(&ctx->results_pool); i_free(ctx->paths); client_dict_iterate_free(ctx); client_dict_add_timeout(dict); return ret; } static struct dict_transaction_context * client_dict_transaction_init(struct dict *_dict) { struct client_dict *dict = (struct client_dict *)_dict; struct client_dict_transaction_context *ctx; ctx = i_new(struct client_dict_transaction_context, 1); ctx->ctx.dict = _dict; ctx->id = ++dict->transaction_id_counter; DLLIST_PREPEND(&dict->transactions, ctx); return &ctx->ctx; } static void client_dict_transaction_free(struct client_dict_transaction_context **_ctx) { struct client_dict_transaction_context *ctx = *_ctx; *_ctx = NULL; i_free(ctx->first_query); i_free(ctx->error); i_free(ctx); } static void client_dict_transaction_commit_callback(struct client_dict_cmd *cmd, enum dict_protocol_reply reply, const char *value, const char *const *extra_args, const char *error, bool disconnected) { struct client_dict *dict = cmd->dict; int ret = -1; i_assert(cmd->trans != NULL); int diff = timeval_diff_msecs(&ioloop_timeval, &cmd->start_time); if (error != NULL) { /* failed */ i_error("dict-client: Commit %sfailed: %s " "(reply took %u.%03u secs)", disconnected ? "may have " : "", error, diff/1000, diff%1000); if (disconnected) ret = DICT_COMMIT_RET_WRITE_UNCERTAIN; } else switch (reply) { case DICT_PROTOCOL_REPLY_OK: ret = 1; break; case DICT_PROTOCOL_REPLY_NOTFOUND: ret = 0; break; case DICT_PROTOCOL_REPLY_WRITE_UNCERTAIN: ret = DICT_COMMIT_RET_WRITE_UNCERTAIN; /* fallthrough */ case DICT_PROTOCOL_REPLY_FAIL: { /* value contains the obsolete trans_id */ const char *error = extra_args[0]; i_error("dict-client: server returned %sfailure: %s " "(reply took %u.%03u secs)", ret == DICT_COMMIT_RET_WRITE_UNCERTAIN ? "uncertain " : "", error != NULL ? error : "", diff/1000, diff%1000); if (error != NULL) extra_args++; break; } default: ret = -1; error = t_strdup_printf("dict-client: Invalid commit reply: %c%s " "(reply took %u.%03u secs)", reply, value, diff/1000, diff%1000); i_error("%s", error); client_dict_disconnect(dict, error); break; } if (ret >= 0 && !cmd->background && !cmd->trans->ctx.no_slowness_warning && diff >= (int)dict->warn_slow_msecs) { i_warning("read(%s): dict commit took %s: " "%s (%u commands, first: %s)", dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args), cmd->query, cmd->trans->query_count, cmd->trans->first_query); } client_dict_transaction_free(&cmd->trans); dict_pre_api_callback(dict); cmd->api_callback.commit(ret, cmd->api_callback.context); dict_post_api_callback(dict); } static void commit_sync_callback(int ret, void *context) { if (context != NULL) { int *ret_p = context; *ret_p = ret; } } static int client_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async, dict_transaction_commit_callback_t *callback, void *context) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; struct client_dict *dict = (struct client_dict *)_ctx->dict; struct client_dict_cmd *cmd; const char *query; int ret = -1; DLLIST_REMOVE(&dict->transactions, ctx); if (ctx->sent_begin && ctx->error == NULL) { query = t_strdup_printf("%c%u", DICT_PROTOCOL_CMD_COMMIT, ctx->id); cmd = client_dict_cmd_init(dict, query); cmd->trans = ctx; cmd->callback = client_dict_transaction_commit_callback; if (callback != NULL) { cmd->api_callback.commit = callback; cmd->api_callback.context = context; } else { cmd->api_callback.commit = commit_sync_callback; cmd->api_callback.context = (async ? NULL : &ret); if (async) cmd->background = TRUE; } if (client_dict_cmd_send(dict, &cmd, NULL)) { if (!async) client_dict_wait(_ctx->dict); } } else if (ctx->error != NULL) { /* already failed */ if (callback != NULL) callback(-1, context); client_dict_transaction_free(&ctx); ret = -1; } else { /* nothing changed */ if (callback != NULL) callback(1, context); client_dict_transaction_free(&ctx); ret = 1; } client_dict_add_timeout(dict); return ret; } static void client_dict_transaction_rollback(struct dict_transaction_context *_ctx) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; struct client_dict *dict = (struct client_dict *)_ctx->dict; if (ctx->sent_begin) { const char *query; query = t_strdup_printf("%c%u", DICT_PROTOCOL_CMD_ROLLBACK, ctx->id); client_dict_send_transaction_query(ctx, query); } DLLIST_REMOVE(&dict->transactions, ctx); client_dict_transaction_free(&ctx); client_dict_add_timeout(dict); } static void client_dict_set(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; const char *query; query = t_strdup_printf("%c%u\t%s\t%s", DICT_PROTOCOL_CMD_SET, ctx->id, str_tabescape(key), str_tabescape(value)); client_dict_send_transaction_query(ctx, query); } static void client_dict_unset(struct dict_transaction_context *_ctx, const char *key) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; const char *query; query = t_strdup_printf("%c%u\t%s", DICT_PROTOCOL_CMD_UNSET, ctx->id, str_tabescape(key)); client_dict_send_transaction_query(ctx, query); } static void client_dict_atomic_inc(struct dict_transaction_context *_ctx, const char *key, long long diff) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; const char *query; query = t_strdup_printf("%c%u\t%s\t%lld", DICT_PROTOCOL_CMD_ATOMIC_INC, ctx->id, str_tabescape(key), diff); client_dict_send_transaction_query(ctx, query); } static void client_dict_set_timestamp(struct dict_transaction_context *_ctx, const struct timespec *ts) { struct client_dict_transaction_context *ctx = (struct client_dict_transaction_context *)_ctx; const char *query; query = t_strdup_printf("%c%u\t%s\t%u", DICT_PROTOCOL_CMD_TIMESTAMP, ctx->id, dec2str(ts->tv_sec), (unsigned int)ts->tv_nsec); client_dict_send_transaction_query(ctx, query); } struct dict dict_driver_client = { .name = "proxy", { .init = client_dict_init, .deinit = client_dict_deinit, .wait = client_dict_wait, .lookup = client_dict_lookup, .iterate_init = client_dict_iterate_init, .iterate = client_dict_iterate, .iterate_deinit = client_dict_iterate_deinit, .transaction_init = client_dict_transaction_init, .transaction_commit = client_dict_transaction_commit, .transaction_rollback = client_dict_transaction_rollback, .set = client_dict_set, .unset = client_dict_unset, .atomic_inc = client_dict_atomic_inc, .lookup_async = client_dict_lookup_async, .switch_ioloop = client_dict_switch_ioloop, .set_timestamp = client_dict_set_timestamp, } }; dovecot-2.2.33.2/src/lib-dict/dict-private.h0000644000175000017500000000510313165463624015360 00000000000000#ifndef DICT_PRIVATE_H #define DICT_PRIVATE_H #include #include "dict.h" struct dict_vfuncs { int (*init)(struct dict *dict_driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r); void (*deinit)(struct dict *dict); int (*wait)(struct dict *dict); int (*lookup)(struct dict *dict, pool_t pool, const char *key, const char **value_r); struct dict_iterate_context * (*iterate_init)(struct dict *dict, const char *const *paths, enum dict_iterate_flags flags); bool (*iterate)(struct dict_iterate_context *ctx, const char **key_r, const char **value_r); int (*iterate_deinit)(struct dict_iterate_context *ctx); struct dict_transaction_context *(*transaction_init)(struct dict *dict); int (*transaction_commit)(struct dict_transaction_context *ctx, bool async, dict_transaction_commit_callback_t *callback, void *context); void (*transaction_rollback)(struct dict_transaction_context *ctx); void (*set)(struct dict_transaction_context *ctx, const char *key, const char *value); void (*unset)(struct dict_transaction_context *ctx, const char *key); void (*append)(struct dict_transaction_context *ctx, const char *key, const char *value); void (*atomic_inc)(struct dict_transaction_context *ctx, const char *key, long long diff); void (*lookup_async)(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context); bool (*switch_ioloop)(struct dict *dict); void (*set_timestamp)(struct dict_transaction_context *ctx, const struct timespec *ts); }; struct dict { const char *name; struct dict_vfuncs v; unsigned int iter_count; unsigned int transaction_count; struct dict_transaction_context *transactions; }; struct dict_iterate_context { struct dict *dict; dict_iterate_callback_t *async_callback; void *async_context; unsigned int has_more:1; uint64_t row_count, max_rows; }; struct dict_transaction_context { struct dict *dict; struct dict_transaction_context *prev, *next; struct timespec timestamp; unsigned int changed:1; unsigned int no_slowness_warning:1; }; extern struct dict dict_driver_client; extern struct dict dict_driver_file; extern struct dict dict_driver_fs; extern struct dict dict_driver_memcached; extern struct dict dict_driver_memcached_ascii; extern struct dict dict_driver_redis; extern struct dict dict_driver_cdb; extern struct dict dict_driver_fail; extern struct dict_iterate_context dict_iter_unsupported; extern struct dict_transaction_context dict_transaction_unsupported; #endif dovecot-2.2.33.2/src/lib-dict/dict-sql.c0000644000175000017500000012307013165463624014504 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "hex-binary.h" #include "hash.h" #include "str.h" #include "sql-api-private.h" #include "sql-db-cache.h" #include "dict-private.h" #include "dict-sql-settings.h" #include "dict-sql.h" #include #include #define DICT_SQL_MAX_UNUSED_CONNECTIONS 10 enum sql_recurse_type { SQL_DICT_RECURSE_NONE, SQL_DICT_RECURSE_ONE, SQL_DICT_RECURSE_FULL }; struct sql_dict { struct dict dict; pool_t pool; struct sql_db *db; const char *username; const struct dict_sql_settings *set; /* query template => prepared statement */ HASH_TABLE(const char *, struct sql_prepared_statement *) prep_stmt_hash; unsigned int has_on_duplicate_key:1; }; struct sql_dict_param { enum dict_sql_type value_type; const char *value_str; int64_t value_int64; const void *value_binary; size_t value_binary_size; }; ARRAY_DEFINE_TYPE(sql_dict_param, struct sql_dict_param); struct sql_dict_iterate_context { struct dict_iterate_context ctx; pool_t pool; enum dict_iterate_flags flags; const char **paths; struct sql_result *result; string_t *key; const struct dict_sql_map *map; size_t key_prefix_len, pattern_prefix_len; unsigned int path_idx, sql_fields_start_idx, next_map_idx; bool synchronous_result; bool failed; bool iter_query_sent; bool allow_null_map; /* allow next map to be NULL */ }; struct sql_dict_inc_row { struct sql_dict_inc_row *prev; unsigned int rows; }; struct sql_dict_transaction_context { struct dict_transaction_context ctx; struct sql_transaction_context *sql_ctx; const struct dict_sql_map *prev_inc_map; char *prev_inc_key; long long prev_inc_diff; pool_t inc_row_pool; struct sql_dict_inc_row *inc_row; const struct dict_sql_map *prev_set_map; char *prev_set_key; char *prev_set_value; dict_transaction_commit_callback_t *async_callback; void *async_context; unsigned int failed:1; }; static struct sql_db_cache *dict_sql_db_cache; static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx); static void sql_dict_prev_set_flush(struct sql_dict_transaction_context *ctx); static int sql_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct sql_dict *dict; pool_t pool; pool = pool_alloconly_create("sql dict", 2048); dict = p_new(pool, struct sql_dict, 1); dict->pool = pool; dict->dict = *driver; dict->username = p_strdup(pool, set->username); dict->set = dict_sql_settings_read(uri, error_r); if (dict->set == NULL) { pool_unref(&pool); return -1; } /* currently pgsql and sqlite don't support "ON DUPLICATE KEY" */ dict->has_on_duplicate_key = strcmp(driver->name, "mysql") == 0; dict->db = sql_db_cache_new(dict_sql_db_cache, driver->name, dict->set->connect); if ((sql_get_flags(dict->db) & SQL_DB_FLAG_PREP_STATEMENTS) != 0) { hash_table_create(&dict->prep_stmt_hash, dict->pool, 0, str_hash, strcmp); } *dict_r = &dict->dict; return 0; } static void sql_dict_prep_stmt_hash_free(struct sql_dict *dict) { struct hash_iterate_context *iter; struct sql_prepared_statement *prep_stmt; const char *query; if (!hash_table_is_created(dict->prep_stmt_hash)) return; iter = hash_table_iterate_init(dict->prep_stmt_hash); while (hash_table_iterate(iter, dict->prep_stmt_hash, &query, &prep_stmt)) sql_prepared_statement_deinit(&prep_stmt); hash_table_iterate_deinit(&iter); hash_table_destroy(&dict->prep_stmt_hash); } static void sql_dict_deinit(struct dict *_dict) { struct sql_dict *dict = (struct sql_dict *)_dict; sql_dict_prep_stmt_hash_free(dict); sql_deinit(&dict->db); pool_unref(&dict->pool); } static int sql_dict_wait(struct dict *dict ATTR_UNUSED) { /* FIXME: lib-sql doesn't support this yet */ return 0; } static bool dict_sql_map_match(const struct dict_sql_map *map, const char *path, ARRAY_TYPE(const_string) *values, size_t *pat_len_r, size_t *path_len_r, bool partial_ok, bool recurse) { const char *path_start = path; const char *pat, *field, *p; size_t len; array_clear(values); pat = map->pattern; while (*pat != '\0' && *path != '\0') { if (*pat == '$') { /* variable */ pat++; if (*pat == '\0') { /* pattern ended with this variable, it'll match the rest of the path */ len = strlen(path); if (partial_ok) { /* iterating - the last field never matches fully. if there's a trailing '/', drop it. */ pat--; if (path[len-1] == '/') { field = t_strndup(path, len-1); array_append(values, &field, 1); } else { array_append(values, &path, 1); } } else { array_append(values, &path, 1); path += len; } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; return TRUE; } /* pattern matches until the next '/' in path */ p = strchr(path, '/'); if (p != NULL) { field = t_strdup_until(path, p); array_append(values, &field, 1); path = p; } else { /* no '/' anymore, but it'll still match a partial */ array_append(values, &path, 1); path += strlen(path); pat++; } } else if (*pat == *path) { pat++; path++; } else { return FALSE; } } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; if (*pat == '\0') return *path == '\0'; else if (!partial_ok) return FALSE; else { /* partial matches must end with '/'. */ if (pat != map->pattern && pat[-1] != '/') return FALSE; /* if we're not recursing, there should be only one $variable left. */ if (recurse) return TRUE; return pat[0] == '$' && strchr(pat, '/') == NULL; } } static const struct dict_sql_map * sql_dict_find_map(struct sql_dict *dict, const char *path, ARRAY_TYPE(const_string) *values) { const struct dict_sql_map *maps; unsigned int i, count; size_t len; t_array_init(values, dict->set->max_field_count); maps = array_get(&dict->set->maps, &count); for (i = 0; i < count; i++) { if (dict_sql_map_match(&maps[i], path, values, &len, &len, FALSE, FALSE)) return &maps[i]; } return NULL; } static void sql_dict_statement_bind(struct sql_statement *stmt, unsigned int column_idx, const struct sql_dict_param *param) { switch (param->value_type) { case DICT_SQL_TYPE_STRING: sql_statement_bind_str(stmt, column_idx, param->value_str); break; case DICT_SQL_TYPE_INT: case DICT_SQL_TYPE_UINT: sql_statement_bind_int64(stmt, column_idx, param->value_int64); break; case DICT_SQL_TYPE_HEXBLOB: sql_statement_bind_binary(stmt, column_idx, param->value_binary, param->value_binary_size); break; } } static struct sql_statement * sql_dict_statement_init(struct sql_dict *dict, const char *query, const ARRAY_TYPE(sql_dict_param) *params) { struct sql_statement *stmt; struct sql_prepared_statement *prep_stmt; const struct sql_dict_param *param; if (hash_table_is_created(dict->prep_stmt_hash)) { prep_stmt = hash_table_lookup(dict->prep_stmt_hash, query); if (prep_stmt == NULL) { const char *query_dup = p_strdup(dict->pool, query); prep_stmt = sql_prepared_statement_init(dict->db, query); hash_table_insert(dict->prep_stmt_hash, query_dup, prep_stmt); } stmt = sql_statement_init_prepared(prep_stmt); } else { /* Prepared statements not supported by the backend. Just use regular statements to avoid wasting memory. */ stmt = sql_statement_init(dict->db, query); } array_foreach(params, param) { sql_dict_statement_bind(stmt, array_foreach_idx(params, param), param); } return stmt; } static int sql_dict_value_get(const struct dict_sql_map *map, enum dict_sql_type value_type, const char *field_name, const char *value, const char *value_suffix, ARRAY_TYPE(sql_dict_param) *params, const char **error_r) { struct sql_dict_param *param; buffer_t *buf; param = array_append_space(params); param->value_type = value_type; switch (value_type) { case DICT_SQL_TYPE_STRING: if (value_suffix[0] != '\0') value = t_strconcat(value, value_suffix, NULL); param->value_str = value; return 0; case DICT_SQL_TYPE_INT: if (value_suffix[0] != '\0' || str_to_int64(value, ¶m->value_int64) < 0) { *error_r = t_strdup_printf( "%s field's value isn't 64bit signed integer: %s%s (in pattern: %s)", field_name, value, value_suffix, map->pattern); return -1; } return 0; case DICT_SQL_TYPE_UINT: if (value_suffix[0] != '\0' || value[0] == '-' || str_to_int64(value, ¶m->value_int64) < 0) { *error_r = t_strdup_printf( "%s field's value isn't 64bit unsigned integer: %s%s (in pattern: %s)", field_name, value, value_suffix, map->pattern); return -1; } return 0; case DICT_SQL_TYPE_HEXBLOB: break; } buf = buffer_create_dynamic(pool_datastack_create(), strlen(value)/2); if (hex_to_binary(value, buf) < 0) { /* we shouldn't get untrusted input here. it's also a bit annoying to handle this error. */ *error_r = t_strdup_printf("%s field's value isn't hexblob: %s (in pattern: %s)", field_name, value, map->pattern); return -1; } str_append(buf, value_suffix); param->value_binary = buf->data; param->value_binary_size = buf->used; return 0; } static int sql_dict_field_get_value(const struct dict_sql_map *map, const struct dict_sql_field *field, const char *value, const char *value_suffix, ARRAY_TYPE(sql_dict_param) *params, const char **error_r) { return sql_dict_value_get(map, field->value_type, field->name, value, value_suffix, params, error_r); } static int sql_dict_where_build(struct sql_dict *dict, const struct dict_sql_map *map, const ARRAY_TYPE(const_string) *values_arr, char key1, enum sql_recurse_type recurse_type, string_t *query, ARRAY_TYPE(sql_dict_param) *params, const char **error_r) { const struct dict_sql_field *sql_fields; const char *const *values; unsigned int i, count, count2, exact_count; bool priv = key1 == DICT_PATH_PRIVATE[0]; sql_fields = array_get(&map->sql_fields, &count); values = array_get(values_arr, &count2); /* if we came here from iteration code there may be less values */ i_assert(count2 <= count); if (count2 == 0 && !priv) { /* we want everything */ return 0; } str_append(query, " WHERE"); exact_count = count == count2 && recurse_type != SQL_DICT_RECURSE_NONE ? count2-1 : count2; for (i = 0; i < exact_count; i++) { if (i > 0) str_append(query, " AND"); str_printfa(query, " %s = ?", sql_fields[i].name); if (sql_dict_field_get_value(map, &sql_fields[i], values[i], "", params, error_r) < 0) return -1; } switch (recurse_type) { case SQL_DICT_RECURSE_NONE: break; case SQL_DICT_RECURSE_ONE: if (i > 0) str_append(query, " AND"); if (i < count2) { str_printfa(query, " %s LIKE ?", sql_fields[i].name); if (sql_dict_field_get_value(map, &sql_fields[i], values[i], "/%", params, error_r) < 0) return -1; str_printfa(query, " AND %s NOT LIKE ?", sql_fields[i].name); if (sql_dict_field_get_value(map, &sql_fields[i], values[i], "/%/%", params, error_r) < 0) return -1; } else { str_printfa(query, " %s LIKE '%%' AND " "%s NOT LIKE '%%/%%'", sql_fields[i].name, sql_fields[i].name); } break; case SQL_DICT_RECURSE_FULL: if (i < count2) { if (i > 0) str_append(query, " AND"); str_printfa(query, " %s LIKE ", sql_fields[i].name); if (sql_dict_field_get_value(map, &sql_fields[i], values[i], "/%", params, error_r) < 0) return -1; } break; } if (priv) { if (count2 > 0) str_append(query, " AND"); str_printfa(query, " %s = '%s'", map->username_field, sql_escape_string(dict->db, dict->username)); } return 0; } static int sql_lookup_get_query(struct sql_dict *dict, const char *key, const struct dict_sql_map **map_r, struct sql_statement **stmt_r, const char **error_r) { const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; const char *error; map = *map_r = sql_dict_find_map(dict, key, &values); if (map == NULL) { *error_r = t_strdup_printf( "sql dict lookup: Invalid/unmapped key: %s", key); return -1; } string_t *query = t_str_new(256); ARRAY_TYPE(sql_dict_param) params; t_array_init(¶ms, 4); str_printfa(query, "SELECT %s FROM %s", map->value_field, map->table); if (sql_dict_where_build(dict, map, &values, key[0], SQL_DICT_RECURSE_NONE, query, ¶ms, &error) < 0) { *error_r = t_strdup_printf( "sql dict lookup: Failed to lookup key %s: %s", key, error); return -1; } *stmt_r = sql_dict_statement_init(dict, str_c(query), ¶ms); return 0; } static const char * sql_dict_result_unescape(enum dict_sql_type type, pool_t pool, struct sql_result *result, unsigned int result_idx) { const unsigned char *data; size_t size; string_t *str; switch (type) { case DICT_SQL_TYPE_STRING: case DICT_SQL_TYPE_INT: case DICT_SQL_TYPE_UINT: return p_strdup(pool, sql_result_get_field_value(result, result_idx)); case DICT_SQL_TYPE_HEXBLOB: break; } data = sql_result_get_field_value_binary(result, result_idx, &size); str = str_new(pool, size*2 + 1); binary_to_hex_append(str, data, size); return str_c(str); } static const char * sql_dict_result_unescape_value(const struct dict_sql_map *map, pool_t pool, struct sql_result *result) { return sql_dict_result_unescape(map->value_types[0], pool, result, 0); } static const char *const * sql_dict_result_unescape_values(const struct dict_sql_map *map, pool_t pool, struct sql_result *result) { const char **values; unsigned int i; values = p_new(pool, const char *, map->values_count + 1); for (i = 0; i < map->values_count; i++) { values[i] = sql_dict_result_unescape(map->value_types[i], pool, result, i); } return values; } static const char * sql_dict_result_unescape_field(const struct dict_sql_map *map, pool_t pool, struct sql_result *result, unsigned int result_idx, unsigned int sql_field_idx) { const struct dict_sql_field *sql_field; sql_field = array_idx(&map->sql_fields, sql_field_idx); return sql_dict_result_unescape(sql_field->value_type, pool, result, result_idx); } static int sql_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct sql_dict *dict = (struct sql_dict *)_dict; const struct dict_sql_map *map; struct sql_statement *stmt; struct sql_result *result = NULL; const char *error; int ret; *value_r = NULL; if (sql_lookup_get_query(dict, key, &map, &stmt, &error) < 0) { i_error("%s", error); return -1; } result = sql_statement_query_s(&stmt); ret = sql_result_next_row(result); if (ret <= 0) { if (ret < 0) { i_error("dict sql lookup failed: %s", sql_result_get_error(result)); } *value_r = NULL; } else { *value_r = sql_dict_result_unescape_value(map, pool, result); } sql_result_unref(result); return ret; } struct sql_dict_lookup_context { const struct dict_sql_map *map; dict_lookup_callback_t *callback; void *context; }; static void sql_dict_lookup_async_callback(struct sql_result *sql_result, struct sql_dict_lookup_context *ctx) { struct dict_lookup_result result; i_zero(&result); result.ret = sql_result_next_row(sql_result); if (result.ret < 0) result.error = sql_result_get_error(sql_result); else if (result.ret > 0) { result.values = sql_dict_result_unescape_values(ctx->map, pool_datastack_create(), sql_result); result.value = result.values[0]; if (result.value == NULL) { /* NULL value returned. we'll treat this as "not found", which is probably what is usually wanted. */ result.ret = 0; } } ctx->callback(&result, ctx->context); i_free(ctx); } static void sql_dict_lookup_async(struct dict *_dict, const char *key, dict_lookup_callback_t *callback, void *context) { struct sql_dict *dict = (struct sql_dict *)_dict; const struct dict_sql_map *map; struct sql_dict_lookup_context *ctx; struct sql_statement *stmt; const char *error; if (sql_lookup_get_query(dict, key, &map, &stmt, &error) < 0) { struct dict_lookup_result result; i_zero(&result); result.ret = -1; result.error = error; callback(&result, context); } else { ctx = i_new(struct sql_dict_lookup_context, 1); ctx->callback = callback; ctx->context = context; ctx->map = map; sql_statement_query(&stmt, sql_dict_lookup_async_callback, ctx); } } static const struct dict_sql_map * sql_dict_iterate_find_next_map(struct sql_dict_iterate_context *ctx, ARRAY_TYPE(const_string) *values) { struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict; const struct dict_sql_map *maps; unsigned int i, count; size_t pat_len, path_len; bool recurse = (ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0; t_array_init(values, dict->set->max_field_count); maps = array_get(&dict->set->maps, &count); for (i = ctx->next_map_idx; i < count; i++) { if (dict_sql_map_match(&maps[i], ctx->paths[ctx->path_idx], values, &pat_len, &path_len, TRUE, recurse) && (recurse || array_count(values)+1 >= array_count(&maps[i].sql_fields))) { ctx->key_prefix_len = path_len; ctx->pattern_prefix_len = pat_len; ctx->next_map_idx = i + 1; str_truncate(ctx->key, 0); str_append(ctx->key, ctx->paths[ctx->path_idx]); return &maps[i]; } } /* try the next path, if there is any */ ctx->path_idx++; if (ctx->paths[ctx->path_idx] != NULL) return sql_dict_iterate_find_next_map(ctx, values); return NULL; } static int sql_dict_iterate_build_next_query(struct sql_dict_iterate_context *ctx, struct sql_statement **stmt_r, const char **error_r) { struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict; const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; const struct dict_sql_field *sql_fields; enum sql_recurse_type recurse_type; unsigned int i, count; map = sql_dict_iterate_find_next_map(ctx, &values); /* NULL map is allowed if we have already done some lookups */ if (map == NULL) { if (!ctx->allow_null_map) { *error_r = "Invalid/unmapped path"; return -1; } return 0; } if (ctx->result != NULL) { sql_result_unref(ctx->result); ctx->result = NULL; } string_t *query = t_str_new(256); str_append(query, "SELECT "); if ((ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) str_printfa(query, "%s,", map->value_field); /* get all missing fields */ sql_fields = array_get(&map->sql_fields, &count); i = array_count(&values); if (i == count) { /* we always want to know the last field since we're iterating its children */ i_assert(i > 0); i--; } ctx->sql_fields_start_idx = i; for (; i < count; i++) str_printfa(query, "%s,", sql_fields[i].name); str_truncate(query, str_len(query)-1); str_printfa(query, " FROM %s", map->table); if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0) recurse_type = SQL_DICT_RECURSE_FULL; else if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) recurse_type = SQL_DICT_RECURSE_NONE; else recurse_type = SQL_DICT_RECURSE_ONE; ARRAY_TYPE(sql_dict_param) params; t_array_init(¶ms, 4); if (sql_dict_where_build(dict, map, &values, ctx->paths[ctx->path_idx][0], recurse_type, query, ¶ms, error_r) < 0) return -1; if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_KEY) != 0) { str_append(query, " ORDER BY "); for (i = 0; i < count; i++) { str_printfa(query, "%s", sql_fields[i].name); if (i < count-1) str_append_c(query, ','); } } else if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_VALUE) != 0) str_printfa(query, " ORDER BY %s", map->value_field); if (ctx->ctx.max_rows > 0) { i_assert(ctx->ctx.row_count < ctx->ctx.max_rows); str_printfa(query, " LIMIT %llu", (unsigned long long)(ctx->ctx.max_rows - ctx->ctx.row_count)); } *stmt_r = sql_dict_statement_init(dict, str_c(query), ¶ms); ctx->map = map; return 1; } static void sql_dict_iterate_callback(struct sql_result *result, struct sql_dict_iterate_context *ctx) { sql_result_ref(result); ctx->result = result; if (ctx->ctx.async_callback != NULL && !ctx->synchronous_result) ctx->ctx.async_callback(ctx->ctx.async_context); } static int sql_dict_iterate_next_query(struct sql_dict_iterate_context *ctx) { struct sql_statement *stmt; const char *error; unsigned int path_idx = ctx->path_idx; int ret; ret = sql_dict_iterate_build_next_query(ctx, &stmt, &error); if (ret < 0) { /* failed */ i_error("sql dict iterate failed for %s: %s", ctx->paths[path_idx], error); return -1; } else if (ret == 0) { /* this is expected error */ return 0; } if ((ctx->flags & DICT_ITERATE_FLAG_ASYNC) == 0) { ctx->result = sql_statement_query_s(&stmt); } else { i_assert(ctx->result == NULL); ctx->synchronous_result = TRUE; sql_statement_query(&stmt, sql_dict_iterate_callback, ctx); ctx->synchronous_result = FALSE; } return ret; } static struct dict_iterate_context * sql_dict_iterate_init(struct dict *_dict, const char *const *paths, enum dict_iterate_flags flags) { struct sql_dict_iterate_context *ctx; unsigned int i, path_count; pool_t pool; pool = pool_alloconly_create("sql dict iterate", 512); ctx = p_new(pool, struct sql_dict_iterate_context, 1); ctx->ctx.dict = _dict; ctx->pool = pool; ctx->flags = flags; for (path_count = 0; paths[path_count] != NULL; path_count++) ; ctx->paths = p_new(pool, const char *, path_count + 1); for (i = 0; i < path_count; i++) ctx->paths[i] = p_strdup(pool, paths[i]); ctx->key = str_new(pool, 256); return &ctx->ctx; } static bool sql_dict_iterate(struct dict_iterate_context *_ctx, const char **key_r, const char **value_r) { struct sql_dict_iterate_context *ctx = (struct sql_dict_iterate_context *)_ctx; const char *p, *value; unsigned int i, sql_field_i, count; int ret; _ctx->has_more = FALSE; if (ctx->failed) return FALSE; if (!ctx->iter_query_sent) { ctx->iter_query_sent = TRUE; if (sql_dict_iterate_next_query(ctx) <= 0) return FALSE; } if (ctx->result == NULL) { /* wait for async lookup to finish */ i_assert((ctx->flags & DICT_ITERATE_FLAG_ASYNC) != 0); _ctx->has_more = TRUE; return FALSE; } ret = sql_result_next_row(ctx->result); while (ret == SQL_RESULT_NEXT_MORE) { if ((ctx->flags & DICT_ITERATE_FLAG_ASYNC) == 0) sql_result_more_s(&ctx->result); else { /* get more results asynchronously */ ctx->synchronous_result = TRUE; sql_result_more(&ctx->result, sql_dict_iterate_callback, ctx); ctx->synchronous_result = FALSE; if (ctx->result == NULL) { _ctx->has_more = TRUE; return FALSE; } } ret = sql_result_next_row(ctx->result); } if (ret == 0) { /* see if there are more results in the next map. don't do it if we're looking for an exact match, since we already should have handled it. */ if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) return FALSE; ctx->iter_query_sent = FALSE; /* we have gotten *SOME* results, so can allow unmapped next key now. */ ctx->allow_null_map = TRUE; return sql_dict_iterate(_ctx, key_r, value_r); } if (ret < 0) { ctx->failed = TRUE; i_error("dict sql iterate failed: %s", sql_result_get_error(ctx->result)); return FALSE; } /* convert fetched row to dict key */ str_truncate(ctx->key, ctx->key_prefix_len); if (ctx->key_prefix_len > 0 && str_c(ctx->key)[ctx->key_prefix_len-1] != '/') str_append_c(ctx->key, '/'); count = sql_result_get_fields_count(ctx->result); i = (ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) != 0 ? 0 : ctx->map->values_count; sql_field_i = ctx->sql_fields_start_idx; for (p = ctx->map->pattern + ctx->pattern_prefix_len; *p != '\0'; p++) { if (*p != '$') str_append_c(ctx->key, *p); else { i_assert(i < count); value = sql_dict_result_unescape_field(ctx->map, pool_datastack_create(), ctx->result, i, sql_field_i); if (value != NULL) str_append(ctx->key, value); i++; sql_field_i++; } } *key_r = str_c(ctx->key); if ((ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) != 0) *value_r = ""; else { *value_r = sql_dict_result_unescape_value(ctx->map, pool_datastack_create(), ctx->result); } return TRUE; } static int sql_dict_iterate_deinit(struct dict_iterate_context *_ctx) { struct sql_dict_iterate_context *ctx = (struct sql_dict_iterate_context *)_ctx; int ret = ctx->failed ? -1 : 0; if (ctx->result != NULL) sql_result_unref(ctx->result); pool_unref(&ctx->pool); return ret; } static struct dict_transaction_context * sql_dict_transaction_init(struct dict *_dict) { struct sql_dict *dict = (struct sql_dict *)_dict; struct sql_dict_transaction_context *ctx; ctx = i_new(struct sql_dict_transaction_context, 1); ctx->ctx.dict = _dict; ctx->sql_ctx = sql_transaction_begin(dict->db); return &ctx->ctx; } static void sql_dict_transaction_free(struct sql_dict_transaction_context *ctx) { if (ctx->inc_row_pool != NULL) pool_unref(&ctx->inc_row_pool); i_free(ctx->prev_inc_key); i_free(ctx); } static bool sql_dict_transaction_has_nonexistent(struct sql_dict_transaction_context *ctx) { struct sql_dict_inc_row *inc_row; for (inc_row = ctx->inc_row; inc_row != NULL; inc_row = inc_row->prev) { i_assert(inc_row->rows != UINT_MAX); if (inc_row->rows == 0) return TRUE; } return FALSE; } static void sql_dict_transaction_commit_callback(const struct sql_commit_result *sql_result, struct sql_dict_transaction_context *ctx) { int ret; if (sql_result->error == NULL) ret = sql_dict_transaction_has_nonexistent(ctx) ? 0 : 1; else { i_error("sql dict: commit failed: %s", sql_result->error); switch (sql_result->error_type) { case SQL_RESULT_ERROR_TYPE_UNKNOWN: default: ret = DICT_COMMIT_RET_FAILED; break; case SQL_RESULT_ERROR_TYPE_WRITE_UNCERTAIN: ret = DICT_COMMIT_RET_WRITE_UNCERTAIN; break; } } if (ctx->async_callback != NULL) ctx->async_callback(ret, ctx->async_context); sql_dict_transaction_free(ctx); } static int sql_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async, dict_transaction_commit_callback_t *callback, void *context) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; const char *error; int ret = 1; if (ctx->prev_inc_map != NULL) sql_dict_prev_inc_flush(ctx); if (ctx->prev_set_map != NULL) sql_dict_prev_set_flush(ctx); if (ctx->failed) { sql_transaction_rollback(&ctx->sql_ctx); ret = -1; } else if (!_ctx->changed) { /* nothing changed, no need to commit */ sql_transaction_rollback(&ctx->sql_ctx); } else if (async) { ctx->async_callback = callback; ctx->async_context = context; sql_transaction_commit2(&ctx->sql_ctx, sql_dict_transaction_commit_callback, ctx); return 1; } else { if (sql_transaction_commit_s(&ctx->sql_ctx, &error) < 0) { i_error("sql dict: commit failed: %s", error); ret = -1; } else { if (sql_dict_transaction_has_nonexistent(ctx)) ret = 0; } } sql_dict_transaction_free(ctx); if (callback != NULL) callback(ret, context); return ret; } static void sql_dict_transaction_rollback(struct dict_transaction_context *_ctx) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; sql_transaction_rollback(&ctx->sql_ctx); sql_dict_transaction_free(ctx); } static struct sql_statement * sql_dict_transaction_stmt_init(struct sql_dict_transaction_context *ctx, const char *query, const ARRAY_TYPE(sql_dict_param) *params) { struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict; struct sql_statement *stmt = sql_dict_statement_init(dict, query, params); if (ctx->ctx.timestamp.tv_sec != 0) sql_statement_set_timestamp(stmt, &ctx->ctx.timestamp); return stmt; } struct dict_sql_build_query_field { const struct dict_sql_map *map; const char *value; }; struct dict_sql_build_query { struct sql_dict *dict; ARRAY(struct dict_sql_build_query_field) fields; const ARRAY_TYPE(const_string) *extra_values; char key1; }; static int sql_dict_set_query(struct sql_dict_transaction_context *ctx, const struct dict_sql_build_query *build, struct sql_statement **stmt_r, const char **error_r) { struct sql_dict *dict = build->dict; const struct dict_sql_build_query_field *fields; const struct dict_sql_field *sql_fields; ARRAY_TYPE(sql_dict_param) params; const char *const *extra_values; unsigned int i, field_count, count, count2; string_t *prefix, *suffix; fields = array_get(&build->fields, &field_count); i_assert(field_count > 0); t_array_init(¶ms, 4); prefix = t_str_new(64); suffix = t_str_new(256); str_printfa(prefix, "INSERT INTO %s", fields[0].map->table); str_append(prefix, " ("); str_append(suffix, ") VALUES ("); for (i = 0; i < field_count; i++) { if (i > 0) { str_append_c(prefix, ','); str_append_c(suffix, ','); } str_append(prefix, t_strcut(fields[i].map->value_field, ',')); enum dict_sql_type value_type = fields[i].map->value_types[0]; str_append_c(suffix, '?'); if (sql_dict_value_get(fields[i].map, value_type, "value", fields[i].value, "", ¶ms, error_r) < 0) return -1; } if (build->key1 == DICT_PATH_PRIVATE[0]) { str_printfa(prefix, ",%s", fields[0].map->username_field); str_printfa(suffix, ",'%s'", sql_escape_string(dict->db, dict->username)); } /* add the other fields from the key */ sql_fields = array_get(&fields[0].map->sql_fields, &count); extra_values = array_get(build->extra_values, &count2); i_assert(count == count2); for (i = 0; i < count; i++) { str_printfa(prefix, ",%s", sql_fields[i].name); str_append(suffix, ",?"); if (sql_dict_field_get_value(fields[0].map, &sql_fields[i], extra_values[i], "", ¶ms, error_r) < 0) return -1; } str_append_str(prefix, suffix); str_append_c(prefix, ')'); if (!dict->has_on_duplicate_key) { *stmt_r = sql_dict_transaction_stmt_init(ctx, str_c(prefix), ¶ms); return 0; } str_append(prefix, " ON DUPLICATE KEY UPDATE "); for (i = 0; i < field_count; i++) { const char *first_value_field = t_strcut(fields[i].map->value_field, ','); if (i > 0) str_append_c(prefix, ','); str_append(prefix, first_value_field); str_append_c(prefix, '='); enum dict_sql_type value_type = fields[i].map->value_types[0]; str_append_c(prefix, '?'); if (sql_dict_value_get(fields[i].map, value_type, "value", fields[i].value, "", ¶ms, error_r) < 0) return -1; } *stmt_r = sql_dict_transaction_stmt_init(ctx, str_c(prefix), ¶ms); return 0; } static int sql_dict_update_query(const struct dict_sql_build_query *build, const char **query_r, ARRAY_TYPE(sql_dict_param) *params, const char **error_r) { struct sql_dict *dict = build->dict; const struct dict_sql_build_query_field *fields; unsigned int i, field_count; string_t *query; fields = array_get(&build->fields, &field_count); i_assert(field_count > 0); query = t_str_new(64); str_printfa(query, "UPDATE %s SET ", fields[0].map->table); for (i = 0; i < field_count; i++) { const char *first_value_field = t_strcut(fields[i].map->value_field, ','); if (i > 0) str_append_c(query, ','); str_printfa(query, "%s=%s+?", first_value_field, first_value_field); } if (sql_dict_where_build(dict, fields[0].map, build->extra_values, build->key1, SQL_DICT_RECURSE_NONE, query, params, error_r) < 0) return -1; *query_r = str_c(query); return 0; } static void sql_dict_set_real(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; struct sql_dict *dict = (struct sql_dict *)_ctx->dict; const struct dict_sql_map *map; struct sql_statement *stmt; ARRAY_TYPE(const_string) values; struct dict_sql_build_query build; struct dict_sql_build_query_field field; const char *error; map = sql_dict_find_map(dict, key, &values); if (map == NULL) { i_error("sql dict set: Invalid/unmapped key: %s", key); ctx->failed = TRUE; return; } field.map = map; field.value = value; i_zero(&build); build.dict = dict; t_array_init(&build.fields, 1); array_append(&build.fields, &field, 1); build.extra_values = &values; build.key1 = key[0]; if (sql_dict_set_query(ctx, &build, &stmt, &error) < 0) { i_error("dict-sql: Failed to set %s=%s: %s", key, value, error); ctx->failed = TRUE; } else { sql_update_stmt(ctx->sql_ctx, &stmt); } } static void sql_dict_unset(struct dict_transaction_context *_ctx, const char *key) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; struct sql_dict *dict = (struct sql_dict *)_ctx->dict; const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; string_t *query = t_str_new(256); ARRAY_TYPE(sql_dict_param) params; const char *error; if (ctx->prev_inc_map != NULL) sql_dict_prev_inc_flush(ctx); if (ctx->prev_set_map != NULL) sql_dict_prev_set_flush(ctx); map = sql_dict_find_map(dict, key, &values); if (map == NULL) { i_error("sql dict unset: Invalid/unmapped key: %s", key); ctx->failed = TRUE; return; } str_printfa(query, "DELETE FROM %s", map->table); t_array_init(¶ms, 4); if (sql_dict_where_build(dict, map, &values, key[0], SQL_DICT_RECURSE_NONE, query, ¶ms, &error) < 0) { i_error("dict-sql: Failed to delete %s: %s", key, error); ctx->failed = TRUE; } else { struct sql_statement *stmt = sql_dict_transaction_stmt_init(ctx, str_c(query), ¶ms); sql_update_stmt(ctx->sql_ctx, &stmt); } } static void sql_dict_append(struct dict_transaction_context *_ctx, const char *key ATTR_UNUSED, const char *value ATTR_UNUSED) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; i_error("sql dict: Append command not implemented currently"); ctx->failed = TRUE; } static unsigned int * sql_dict_next_inc_row(struct sql_dict_transaction_context *ctx) { struct sql_dict_inc_row *row; if (ctx->inc_row_pool == NULL) { ctx->inc_row_pool = pool_alloconly_create("sql dict inc rows", 128); } row = p_new(ctx->inc_row_pool, struct sql_dict_inc_row, 1); row->prev = ctx->inc_row; row->rows = UINT_MAX; ctx->inc_row = row; return &row->rows; } static void sql_dict_atomic_inc_real(struct sql_dict_transaction_context *ctx, const char *key, long long diff) { struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict; const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; struct dict_sql_build_query build; struct dict_sql_build_query_field field; ARRAY_TYPE(sql_dict_param) params; struct sql_dict_param *param; const char *query, *error; map = sql_dict_find_map(dict, key, &values); i_assert(map != NULL); field.map = map; field.value = NULL; /* unused */ i_zero(&build); build.dict = dict; t_array_init(&build.fields, 1); array_append(&build.fields, &field, 1); build.extra_values = &values; build.key1 = key[0]; t_array_init(¶ms, 4); param = array_append_space(¶ms); param->value_type = DICT_SQL_TYPE_INT; param->value_int64 = diff; if (sql_dict_update_query(&build, &query, ¶ms, &error) < 0) { i_error("dict-sql: Failed to increase %s: %s", key, error); ctx->failed = TRUE; } else { struct sql_statement *stmt = sql_dict_transaction_stmt_init(ctx, query, ¶ms); sql_update_stmt_get_rows(ctx->sql_ctx, &stmt, sql_dict_next_inc_row(ctx)); } } static void sql_dict_prev_set_flush(struct sql_dict_transaction_context *ctx) { sql_dict_set_real(&ctx->ctx, ctx->prev_set_key, ctx->prev_set_value); i_free_and_null(ctx->prev_set_value); i_free_and_null(ctx->prev_set_key); ctx->prev_set_map = NULL; } static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx) { sql_dict_atomic_inc_real(ctx, ctx->prev_inc_key, ctx->prev_inc_diff); i_free_and_null(ctx->prev_inc_key); ctx->prev_inc_map = NULL; } static bool sql_dict_maps_are_mergeable(struct sql_dict *dict, const struct dict_sql_map *map1, const struct dict_sql_map *map2, const char *map1_key, const char *map2_key, const ARRAY_TYPE(const_string) *map2_values) { const struct dict_sql_map *map3; ARRAY_TYPE(const_string) map1_values; const char *const *v1, *const *v2; unsigned int i, count1, count2; if (strcmp(map1->table, map2->table) != 0) return FALSE; if (map1_key[0] != map2_key[0]) return FALSE; if (map1_key[0] == DICT_PATH_PRIVATE[0]) { if (strcmp(map1->username_field, map2->username_field) != 0) return FALSE; } map3 = sql_dict_find_map(dict, map1_key, &map1_values); i_assert(map3 == map1); v1 = array_get(&map1_values, &count1); v2 = array_get(map2_values, &count2); if (count1 != count2) return FALSE; for (i = 0; i < count1; i++) { if (strcmp(v1[i], v2[i]) != 0) return FALSE; } return TRUE; } static void sql_dict_set(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; struct sql_dict *dict = (struct sql_dict *)_ctx->dict; const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; if (ctx->failed) return; if (ctx->prev_inc_map != NULL) sql_dict_prev_inc_flush(ctx); map = sql_dict_find_map(dict, key, &values); if (map == NULL) { ctx->failed = TRUE; i_error("sql dict set: Invalid/unmapped key: %s", key); return; } if (ctx->prev_set_map == NULL) { /* see if we can merge this increment SQL query with the next one */ ctx->prev_set_map = map; ctx->prev_set_key = i_strdup(key); ctx->prev_set_value = i_strdup(value); return; } if (!sql_dict_maps_are_mergeable(dict, ctx->prev_set_map, map, ctx->prev_set_key, key, &values)) { sql_dict_prev_set_flush(ctx); sql_dict_set_real(&ctx->ctx, key, value); } else { struct dict_sql_build_query build; struct dict_sql_build_query_field *field; struct sql_statement *stmt; const char *error; i_zero(&build); build.dict = dict; t_array_init(&build.fields, 1); build.extra_values = &values; build.key1 = key[0]; field = array_append_space(&build.fields); field->map = ctx->prev_set_map; field->value = ctx->prev_set_value; field = array_append_space(&build.fields); field->map = map; field->value = value; if (sql_dict_set_query(ctx, &build, &stmt, &error) < 0) { i_error("dict-sql: Failed to set %s: %s", key, error); ctx->failed = TRUE; } else { sql_update_stmt(ctx->sql_ctx, &stmt); } i_free_and_null(ctx->prev_set_value); i_free_and_null(ctx->prev_set_key); ctx->prev_set_map = NULL; } } static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx, const char *key, long long diff) { struct sql_dict_transaction_context *ctx = (struct sql_dict_transaction_context *)_ctx; struct sql_dict *dict = (struct sql_dict *)_ctx->dict; const struct dict_sql_map *map; ARRAY_TYPE(const_string) values; if (ctx->prev_set_map != NULL) sql_dict_prev_set_flush(ctx); map = sql_dict_find_map(dict, key, &values); if (map == NULL) { i_error("sql dict atomic inc: Invalid/unmapped key: %s", key); ctx->failed = TRUE; return; } if (ctx->prev_inc_map == NULL) { /* see if we can merge this increment SQL query with the next one */ ctx->prev_inc_map = map; ctx->prev_inc_key = i_strdup(key); ctx->prev_inc_diff = diff; return; } if (!sql_dict_maps_are_mergeable(dict, ctx->prev_inc_map, map, ctx->prev_inc_key, key, &values)) { sql_dict_prev_inc_flush(ctx); sql_dict_atomic_inc_real(ctx, key, diff); } else { struct dict_sql_build_query build; struct dict_sql_build_query_field *field; ARRAY_TYPE(sql_dict_param) params; struct sql_dict_param *param; const char *query, *error; i_zero(&build); build.dict = dict; t_array_init(&build.fields, 1); build.extra_values = &values; build.key1 = key[0]; field = array_append_space(&build.fields); field->map = ctx->prev_inc_map; field = array_append_space(&build.fields); field->map = map; /* field->value is unused */ t_array_init(¶ms, 4); param = array_append_space(¶ms); param->value_type = DICT_SQL_TYPE_INT; param->value_int64 = ctx->prev_inc_diff; param = array_append_space(¶ms); param->value_type = DICT_SQL_TYPE_INT; param->value_int64 = diff; if (sql_dict_update_query(&build, &query, ¶ms, &error) < 0) { i_error("dict-sql: Failed to increase %s: %s", key, error); ctx->failed = TRUE; } else { struct sql_statement *stmt = sql_dict_transaction_stmt_init(ctx, query, ¶ms); sql_update_stmt_get_rows(ctx->sql_ctx, &stmt, sql_dict_next_inc_row(ctx)); } i_free_and_null(ctx->prev_inc_key); ctx->prev_inc_map = NULL; } } static struct dict sql_dict = { .name = "sql", { .init = sql_dict_init, .deinit = sql_dict_deinit, .wait = sql_dict_wait, .lookup = sql_dict_lookup, .iterate_init = sql_dict_iterate_init, .iterate = sql_dict_iterate, .iterate_deinit = sql_dict_iterate_deinit, .transaction_init = sql_dict_transaction_init, .transaction_commit = sql_dict_transaction_commit, .transaction_rollback = sql_dict_transaction_rollback, .set = sql_dict_set, .unset = sql_dict_unset, .append = sql_dict_append, .atomic_inc = sql_dict_atomic_inc, .lookup_async = sql_dict_lookup_async, } }; static struct dict *dict_sql_drivers; void dict_sql_register(void) { const struct sql_db *const *drivers; unsigned int i, count; dict_sql_db_cache = sql_db_cache_init(DICT_SQL_MAX_UNUSED_CONNECTIONS); /* @UNSAFE */ drivers = array_get(&sql_drivers, &count); dict_sql_drivers = i_new(struct dict, count + 1); for (i = 0; i < count; i++) { dict_sql_drivers[i] = sql_dict; dict_sql_drivers[i].name = drivers[i]->name; dict_driver_register(&dict_sql_drivers[i]); } } void dict_sql_unregister(void) { int i; for (i = 0; dict_sql_drivers[i].name != NULL; i++) dict_driver_unregister(&dict_sql_drivers[i]); i_free(dict_sql_drivers); sql_db_cache_deinit(&dict_sql_db_cache); dict_sql_settings_deinit(); } dovecot-2.2.33.2/src/lib-dict/dict-cdb.c0000644000175000017500000000530013165463624014430 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #ifdef BUILD_CDB #include "dict-private.h" #include #include #include #include #define CDB_WITH_NULL 1 #define CDB_WITHOUT_NULL 2 struct cdb_dict { struct dict dict; struct cdb cdb; char *path; int fd, flag; }; static void cdb_dict_deinit(struct dict *_dict); static int cdb_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set ATTR_UNUSED, struct dict **dict_r, const char **error_r) { struct cdb_dict *dict; dict = i_new(struct cdb_dict, 1); dict->dict = *driver; dict->path = i_strdup(uri); dict->flag = CDB_WITH_NULL | CDB_WITHOUT_NULL; /* initialize cdb to 0 (unallocated) */ memset(&dict->cdb, 0, sizeof(struct cdb)); dict->fd = open(dict->path, O_RDONLY); if (dict->fd == -1) { *error_r = t_strdup_printf("open(%s) failed: %m", dict->path); cdb_dict_deinit(&dict->dict); return -1; } #ifdef TINYCDB_VERSION if (cdb_init(&dict->cdb, dict->fd) < 0) { *error_r = t_strdup_printf("cdb_init(%s) failed: %m", dict->path); cdb_dict_deinit(&dict->dict); return -1; } #else cdb_init(&dict->cdb, dict->fd); #endif *dict_r = &dict->dict; return 0; } static void cdb_dict_deinit(struct dict *_dict) { struct cdb_dict *dict = (struct cdb_dict *)_dict; /* we can safely deinit unallocated cdb */ cdb_free(&dict->cdb); if (dict->fd != -1) { if (close(dict->fd) < 0) i_error("close(%s) failed: %m", dict->path); } i_free(dict->path); i_free(dict); } static int cdb_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct cdb_dict *dict = (struct cdb_dict *)_dict; unsigned datalen; int ret = 0; char *data; /* keys and values may be null terminated... */ if ((dict->flag & CDB_WITH_NULL) != 0) { ret = cdb_find(&dict->cdb, key, (unsigned)strlen(key)+1); if (ret > 0) dict->flag &= ~CDB_WITHOUT_NULL; } /* ...or not */ if (ret == 0 && (dict->flag & CDB_WITHOUT_NULL) != 0) { ret = cdb_find(&dict->cdb, key, (unsigned)strlen(key)); if (ret > 0) dict->flag &= ~CDB_WITH_NULL; } if (ret <= 0) { *value_r = NULL; /* something bad with db */ if (ret < 0) { i_error("cdb_find(%s) failed: %m", dict->path); return -1; } /* found nothing */ return 0; } datalen = cdb_datalen(&dict->cdb); data = p_malloc(pool, datalen + 1); if (cdb_read(&dict->cdb, data, datalen, cdb_datapos(&dict->cdb)) < 0) { i_error("cdb_read(%s) failed: %m", dict->path); return -1; } *value_r = data; return 1; } struct dict dict_driver_cdb = { .name = "cdb", { .init = cdb_dict_init, .deinit = cdb_dict_deinit, .lookup = cdb_dict_lookup, } }; #endif dovecot-2.2.33.2/src/lib-dict/dict-redis.c0000644000175000017500000005120613165463624015014 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING redis */ #include "lib.h" #include "array.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "connection.h" #include "dict-private.h" #define REDIS_DEFAULT_PORT 6379 #define REDIS_DEFAULT_LOOKUP_TIMEOUT_MSECS (1000*30) #define DICT_USERNAME_SEPARATOR '/' enum redis_input_state { /* expecting +OK reply for SELECT */ REDIS_INPUT_STATE_SELECT, /* expecting $-1 / $ followed by GET reply */ REDIS_INPUT_STATE_GET, /* expecting +QUEUED */ REDIS_INPUT_STATE_MULTI, /* expecting +OK reply for DISCARD */ REDIS_INPUT_STATE_DISCARD, /* expecting * */ REDIS_INPUT_STATE_EXEC, /* expecting EXEC reply */ REDIS_INPUT_STATE_EXEC_REPLY }; struct redis_connection { struct connection conn; struct redis_dict *dict; string_t *last_reply; unsigned int bytes_left; bool value_not_found; bool value_received; }; struct redis_dict_reply { unsigned int reply_count; dict_transaction_commit_callback_t *callback; void *context; }; struct redis_dict { struct dict dict; char *username, *key_prefix, *expire_value; unsigned int timeout_msecs, db_id; struct ioloop *ioloop, *prev_ioloop; struct redis_connection conn; ARRAY(enum redis_input_state) input_states; ARRAY(struct redis_dict_reply) replies; bool connected; bool transaction_open; bool db_id_set; }; struct redis_dict_transaction_context { struct dict_transaction_context ctx; unsigned int cmd_count; bool failed; }; static struct connection_list *redis_connections; static void redis_input_state_add(struct redis_dict *dict, enum redis_input_state state) { array_append(&dict->input_states, &state, 1); } static void redis_input_state_remove(struct redis_dict *dict) { array_delete(&dict->input_states, 0, 1); } static void redis_callback(struct redis_dict *dict, const struct redis_dict_reply *reply, int ret) { if (reply->callback != NULL) { if (dict->prev_ioloop != NULL) { /* Don't let callback see that we've created our internal ioloop in case it wants to add some ios or timeouts. */ current_ioloop = dict->prev_ioloop; } reply->callback(ret, reply->context); if (dict->prev_ioloop != NULL) current_ioloop = dict->ioloop; } } static void redis_conn_destroy(struct connection *_conn) { struct redis_connection *conn = (struct redis_connection *)_conn; const struct redis_dict_reply *reply; conn->dict->db_id_set = FALSE; conn->dict->connected = FALSE; connection_disconnect(_conn); array_foreach(&conn->dict->replies, reply) redis_callback(conn->dict, reply, -1); array_clear(&conn->dict->replies); array_clear(&conn->dict->input_states); if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } static void redis_dict_wait_timeout(struct redis_dict *dict) { i_error("redis: Commit timed out in %u.%03u secs", dict->timeout_msecs/1000, dict->timeout_msecs%1000); redis_conn_destroy(&dict->conn.conn); } static void redis_wait(struct redis_dict *dict) { struct timeout *to; i_assert(dict->ioloop == NULL); dict->prev_ioloop = current_ioloop; dict->ioloop = io_loop_create(); to = timeout_add(dict->timeout_msecs, redis_dict_wait_timeout, dict); connection_switch_ioloop(&dict->conn.conn); do { io_loop_run(dict->ioloop); } while (array_count(&dict->input_states) > 0); timeout_remove(&to); io_loop_set_current(dict->prev_ioloop); connection_switch_ioloop(&dict->conn.conn); io_loop_set_current(dict->ioloop); io_loop_destroy(&dict->ioloop); dict->prev_ioloop = NULL; } static int redis_input_get(struct redis_connection *conn) { const unsigned char *data; size_t size; const char *line; if (conn->bytes_left == 0) { /* read the size first */ line = i_stream_next_line(conn->conn.input); if (line == NULL) return 0; if (strcmp(line, "$-1") == 0) { conn->value_received = TRUE; conn->value_not_found = TRUE; if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); redis_input_state_remove(conn->dict); return 1; } if (line[0] != '$' || str_to_uint(line+1, &conn->bytes_left) < 0) { i_error("redis: Unexpected input (wanted $size): %s", line); return -1; } conn->bytes_left += 2; /* include trailing CRLF */ } data = i_stream_get_data(conn->conn.input, &size); if (size > conn->bytes_left) size = conn->bytes_left; str_append_n(conn->last_reply, data, size); conn->bytes_left -= size; i_stream_skip(conn->conn.input, size); if (conn->bytes_left > 0) return 0; /* reply fully read - drop trailing CRLF */ conn->value_received = TRUE; str_truncate(conn->last_reply, str_len(conn->last_reply)-2); if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); redis_input_state_remove(conn->dict); return 1; } static int redis_conn_input_more(struct redis_connection *conn) { struct redis_dict *dict = conn->dict; struct redis_dict_reply *reply; const enum redis_input_state *states; enum redis_input_state state; unsigned int count, num_replies; const char *line; states = array_get(&dict->input_states, &count); if (count == 0) { line = i_stream_next_line(conn->conn.input); if (line == NULL) return 0; i_error("redis: Unexpected input (expected nothing): %s", line); return -1; } state = states[0]; if (state == REDIS_INPUT_STATE_GET) return redis_input_get(conn); line = i_stream_next_line(conn->conn.input); if (line == NULL) return 0; redis_input_state_remove(dict); switch (state) { case REDIS_INPUT_STATE_GET: i_unreached(); case REDIS_INPUT_STATE_SELECT: case REDIS_INPUT_STATE_MULTI: case REDIS_INPUT_STATE_DISCARD: if (line[0] != '+') break; return 1; case REDIS_INPUT_STATE_EXEC: if (line[0] != '*' || str_to_uint(line+1, &num_replies) < 0) break; reply = array_idx_modifiable(&dict->replies, 0); i_assert(reply->reply_count > 0); if (reply->reply_count != num_replies) { i_error("redis: EXEC expected %u replies, not %u", reply->reply_count, num_replies); return -1; } return 1; case REDIS_INPUT_STATE_EXEC_REPLY: if (*line != '+' && *line != ':') break; /* success, just ignore the actual reply */ reply = array_idx_modifiable(&dict->replies, 0); i_assert(reply->reply_count > 0); if (--reply->reply_count == 0) { redis_callback(dict, reply, 1); array_delete(&dict->replies, 0, 1); /* if we're running in a dict-ioloop, we're handling a synchronous commit and need to stop now */ if (array_count(&dict->replies) == 0 && conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } return 1; } i_error("redis: Unexpected input (state=%d): %s", state, line); return -1; } static void redis_conn_input(struct connection *_conn) { struct redis_connection *conn = (struct redis_connection *)_conn; int ret; switch (i_stream_read(_conn->input)) { case 0: return; case -1: if (conn->dict->ioloop != NULL) i_error("redis: Disconnected unexpectedly"); redis_conn_destroy(_conn); return; default: break; } while ((ret = redis_conn_input_more(conn)) > 0) ; if (ret < 0) redis_conn_destroy(_conn); } static void redis_conn_connected(struct connection *_conn, bool success) { struct redis_connection *conn = (struct redis_connection *)_conn; if (!success) { i_error("redis: connect(%s) failed: %m", _conn->name); } else { conn->dict->connected = TRUE; } if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } static const struct connection_settings redis_conn_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE }; static const struct connection_vfuncs redis_conn_vfuncs = { .destroy = redis_conn_destroy, .input = redis_conn_input, .client_connected = redis_conn_connected }; static const char *redis_escape_username(const char *username) { const char *p; string_t *str = t_str_new(64); for (p = username; *p != '\0'; p++) { switch (*p) { case DICT_USERNAME_SEPARATOR: str_append(str, "\\-"); break; case '\\': str_append(str, "\\\\"); break; default: str_append_c(str, *p); } } return str_c(str); } static int redis_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct redis_dict *dict; struct ip_addr ip; unsigned int secs; in_port_t port = REDIS_DEFAULT_PORT; const char *const *args, *unix_path = NULL; int ret = 0; if (redis_connections == NULL) { redis_connections = connection_list_init(&redis_conn_set, &redis_conn_vfuncs); } dict = i_new(struct redis_dict, 1); if (net_addr2ip("127.0.0.1", &ip) < 0) i_unreached(); dict->timeout_msecs = REDIS_DEFAULT_LOOKUP_TIMEOUT_MSECS; dict->key_prefix = i_strdup(""); args = t_strsplit(uri, ":"); for (; *args != NULL; args++) { if (strncmp(*args, "path=", 5) == 0) { unix_path = *args + 5; } else if (strncmp(*args, "host=", 5) == 0) { if (net_addr2ip(*args+5, &ip) < 0) { *error_r = t_strdup_printf("Invalid IP: %s", *args+5); ret = -1; } } else if (strncmp(*args, "port=", 5) == 0) { if (net_str2port(*args+5, &port) < 0) { *error_r = t_strdup_printf("Invalid port: %s", *args+5); ret = -1; } } else if (strncmp(*args, "prefix=", 7) == 0) { i_free(dict->key_prefix); dict->key_prefix = i_strdup(*args + 7); } else if (strncmp(*args, "db=", 3) == 0) { if (str_to_uint(*args+3, &dict->db_id) < 0) { *error_r = t_strdup_printf( "Invalid db number: %s", *args+3); ret = -1; } } else if (strncmp(*args, "expire_secs=", 12) == 0) { const char *value = *args + 12; if (str_to_uint(value, &secs) < 0 || secs == 0) { *error_r = t_strdup_printf( "Invalid expire_secs: %s", value); ret = -1; } i_free(dict->expire_value); dict->expire_value = i_strdup(value); } else if (strncmp(*args, "timeout_msecs=", 14) == 0) { if (str_to_uint(*args+14, &dict->timeout_msecs) < 0) { *error_r = t_strdup_printf( "Invalid timeout_msecs: %s", *args+14); ret = -1; } } else { *error_r = t_strdup_printf("Unknown parameter: %s", *args); ret = -1; } } if (ret < 0) { i_free(dict->key_prefix); i_free(dict); return -1; } if (unix_path != NULL) { connection_init_client_unix(redis_connections, &dict->conn.conn, unix_path); } else { connection_init_client_ip(redis_connections, &dict->conn.conn, &ip, port); } dict->dict = *driver; dict->conn.last_reply = str_new(default_pool, 256); dict->conn.dict = dict; i_array_init(&dict->input_states, 4); i_array_init(&dict->replies, 4); if (strchr(set->username, DICT_USERNAME_SEPARATOR) == NULL) dict->username = i_strdup(set->username); else { /* escape the username */ dict->username = i_strdup(redis_escape_username(set->username)); } *dict_r = &dict->dict; return 0; } static void redis_dict_deinit(struct dict *_dict) { struct redis_dict *dict = (struct redis_dict *)_dict; if (array_count(&dict->input_states) > 0) { i_assert(dict->connected); redis_wait(dict); } connection_deinit(&dict->conn.conn); str_free(&dict->conn.last_reply); array_free(&dict->replies); array_free(&dict->input_states); i_free(dict->expire_value); i_free(dict->key_prefix); i_free(dict->username); i_free(dict); if (redis_connections->connections == NULL) connection_list_deinit(&redis_connections); } static void redis_dict_lookup_timeout(struct redis_dict *dict) { i_error("redis: Lookup timed out in %u.%03u secs", dict->timeout_msecs/1000, dict->timeout_msecs%1000); redis_conn_destroy(&dict->conn.conn); } static const char * redis_dict_get_full_key(struct redis_dict *dict, const char *key) { if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0) key += strlen(DICT_PATH_SHARED); else if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) { key = t_strdup_printf("%s%c%s", dict->username, DICT_USERNAME_SEPARATOR, key + strlen(DICT_PATH_PRIVATE)); } else { i_unreached(); } if (*dict->key_prefix != '\0') key = t_strconcat(dict->key_prefix, key, NULL); return key; } static void redis_dict_select_db(struct redis_dict *dict) { const char *cmd, *db_str; if (dict->db_id_set) return; dict->db_id_set = TRUE; if (dict->db_id == 0) { /* 0 is the default */ return; } db_str = dec2str(dict->db_id); cmd = t_strdup_printf("*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n", (int)strlen(db_str), db_str); o_stream_nsend_str(dict->conn.conn.output, cmd); redis_input_state_add(dict, REDIS_INPUT_STATE_SELECT); } static int redis_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct redis_dict *dict = (struct redis_dict *)_dict; struct timeout *to; const char *cmd; key = redis_dict_get_full_key(dict, key); dict->conn.value_received = FALSE; dict->conn.value_not_found = FALSE; i_assert(dict->ioloop == NULL); dict->prev_ioloop = current_ioloop; dict->ioloop = io_loop_create(); connection_switch_ioloop(&dict->conn.conn); if (dict->conn.conn.fd_in == -1 && connection_client_connect(&dict->conn.conn) < 0) { i_error("redis: Couldn't connect to %s", dict->conn.conn.name); } else { to = timeout_add(dict->timeout_msecs, redis_dict_lookup_timeout, dict); if (!dict->connected) { /* wait for connection */ io_loop_run(dict->ioloop); } if (dict->connected) { redis_dict_select_db(dict); cmd = t_strdup_printf("*2\r\n$3\r\nGET\r\n$%d\r\n%s\r\n", (int)strlen(key), key); o_stream_nsend_str(dict->conn.conn.output, cmd); str_truncate(dict->conn.last_reply, 0); redis_input_state_add(dict, REDIS_INPUT_STATE_GET); do { io_loop_run(dict->ioloop); } while (array_count(&dict->input_states) > 0); } timeout_remove(&to); } io_loop_set_current(dict->prev_ioloop); connection_switch_ioloop(&dict->conn.conn); io_loop_set_current(dict->ioloop); io_loop_destroy(&dict->ioloop); dict->prev_ioloop = NULL; if (!dict->conn.value_received) { /* we failed in some way. make sure we disconnect since the connection state isn't known anymore */ redis_conn_destroy(&dict->conn.conn); return -1; } if (dict->conn.value_not_found) return 0; *value_r = p_strdup(pool, str_c(dict->conn.last_reply)); return 1; } static struct dict_transaction_context * redis_transaction_init(struct dict *_dict) { struct redis_dict *dict = (struct redis_dict *)_dict; struct redis_dict_transaction_context *ctx; i_assert(!dict->transaction_open); dict->transaction_open = TRUE; ctx = i_new(struct redis_dict_transaction_context, 1); ctx->ctx.dict = _dict; if (dict->conn.conn.fd_in == -1 && connection_client_connect(&dict->conn.conn) < 0) { i_error("redis: Couldn't connect to %s", dict->conn.conn.name); } else if (!dict->connected) { /* wait for connection */ redis_wait(dict); } if (dict->connected) redis_dict_select_db(dict); return &ctx->ctx; } static int redis_transaction_commit(struct dict_transaction_context *_ctx, bool async, dict_transaction_commit_callback_t *callback, void *context) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; struct redis_dict_reply *reply; unsigned int i; int ret = 1; i_assert(dict->transaction_open); dict->transaction_open = FALSE; if (ctx->failed) { /* make sure we're disconnected */ redis_conn_destroy(&dict->conn.conn); ret = -1; } else if (_ctx->changed) { i_assert(ctx->cmd_count > 0); o_stream_nsend_str(dict->conn.conn.output, "*1\r\n$4\r\nEXEC\r\n"); reply = array_append_space(&dict->replies); reply->callback = callback; reply->context = context; reply->reply_count = ctx->cmd_count; redis_input_state_add(dict, REDIS_INPUT_STATE_EXEC); for (i = 0; i < ctx->cmd_count; i++) redis_input_state_add(dict, REDIS_INPUT_STATE_EXEC_REPLY); if (async) { i_free(ctx); return 1; } redis_wait(dict); } if (callback != NULL) callback(ret, context); i_free(ctx); return ret; } static void redis_transaction_rollback(struct dict_transaction_context *_ctx) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; struct redis_dict_reply *reply; i_assert(dict->transaction_open); dict->transaction_open = FALSE; if (ctx->failed) { /* make sure we're disconnected */ redis_conn_destroy(&dict->conn.conn); } else if (_ctx->changed) { o_stream_nsend_str(dict->conn.conn.output, "*1\r\n$7\r\nDISCARD\r\n"); reply = array_append_space(&dict->replies); reply->reply_count = 1; redis_input_state_add(dict, REDIS_INPUT_STATE_DISCARD); } i_free(ctx); } static int redis_check_transaction(struct redis_dict_transaction_context *ctx) { struct redis_dict *dict = (struct redis_dict *)ctx->ctx.dict; if (ctx->failed) return -1; if (!dict->connected) { /* disconnected during transaction */ ctx->failed = TRUE; return -1; } if (ctx->ctx.changed) return 0; redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); if (o_stream_send_str(dict->conn.conn.output, "*1\r\n$5\r\nMULTI\r\n") < 0) { ctx->failed = TRUE; return -1; } return 0; } static void redis_append_expire(struct redis_dict_transaction_context *ctx, string_t *cmd, const char *key) { struct redis_dict *dict = (struct redis_dict *)ctx->ctx.dict; if (dict->expire_value == NULL) return; str_printfa(cmd, "*3\r\n$6\r\nEXPIRE\r\n$%u\r\n%s\r\n$%u\r\n%s\r\n", (unsigned int)strlen(key), key, (unsigned int)strlen(dict->expire_value), dict->expire_value); redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); ctx->cmd_count++; } static void redis_set(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; string_t *cmd; if (redis_check_transaction(ctx) < 0) return; key = redis_dict_get_full_key(dict, key); cmd = t_str_new(128); str_printfa(cmd, "*3\r\n$3\r\nSET\r\n$%u\r\n%s\r\n$%u\r\n%s\r\n", (unsigned int)strlen(key), key, (unsigned int)strlen(value), value); redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); ctx->cmd_count++; redis_append_expire(ctx, cmd, key); if (o_stream_send(dict->conn.conn.output, str_data(cmd), str_len(cmd)) < 0) ctx->failed = TRUE; } static void redis_unset(struct dict_transaction_context *_ctx, const char *key) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; const char *cmd; if (redis_check_transaction(ctx) < 0) return; key = redis_dict_get_full_key(dict, key); cmd = t_strdup_printf("*2\r\n$3\r\nDEL\r\n$%u\r\n%s\r\n", (unsigned int)strlen(key), key); if (o_stream_send_str(dict->conn.conn.output, cmd) < 0) ctx->failed = TRUE; redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); ctx->cmd_count++; } static void redis_append(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; const char *cmd; if (redis_check_transaction(ctx) < 0) return; key = redis_dict_get_full_key(dict, key); cmd = t_strdup_printf("*3\r\n$6\r\nAPPEND\r\n$%u\r\n%s\r\n$%u\r\n%s\r\n", (unsigned int)strlen(key), key, (unsigned int)strlen(value), value); if (o_stream_send_str(dict->conn.conn.output, cmd) < 0) ctx->failed = TRUE; redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); ctx->cmd_count++; } static void redis_atomic_inc(struct dict_transaction_context *_ctx, const char *key, long long diff) { struct redis_dict_transaction_context *ctx = (struct redis_dict_transaction_context *)_ctx; struct redis_dict *dict = (struct redis_dict *)_ctx->dict; const char *diffstr; string_t *cmd; if (redis_check_transaction(ctx) < 0) return; key = redis_dict_get_full_key(dict, key); diffstr = t_strdup_printf("%lld", diff); cmd = t_str_new(128); str_printfa(cmd, "*3\r\n$6\r\nINCRBY\r\n$%u\r\n%s\r\n$%u\r\n%s\r\n", (unsigned int)strlen(key), key, (unsigned int)strlen(diffstr), diffstr); redis_input_state_add(dict, REDIS_INPUT_STATE_MULTI); ctx->cmd_count++; redis_append_expire(ctx, cmd, key); if (o_stream_send(dict->conn.conn.output, str_data(cmd), str_len(cmd)) < 0) ctx->failed = TRUE; } struct dict dict_driver_redis = { .name = "redis", { .init = redis_dict_init, .deinit = redis_dict_deinit, .lookup = redis_dict_lookup, .transaction_init = redis_transaction_init, .transaction_commit = redis_transaction_commit, .transaction_rollback = redis_transaction_rollback, .set = redis_set, .unset = redis_unset, .append = redis_append, .atomic_inc = redis_atomic_inc, } }; dovecot-2.2.33.2/src/lib-dict/dict-file.c0000644000175000017500000004072613165463624014632 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "home-expand.h" #include "mkdir-parents.h" #include "eacces-error.h" #include "file-lock.h" #include "file-dotlock.h" #include "nfs-workarounds.h" #include "istream.h" #include "ostream.h" #include "dict-transaction-memory.h" #include "dict-private.h" #include #include #include #include struct file_dict { struct dict dict; pool_t hash_pool; enum file_lock_method lock_method; char *path; HASH_TABLE(char *, char *) hash; int fd; bool refreshed; }; struct file_dict_iterate_path { const char *path; size_t len; }; struct file_dict_iterate_context { struct dict_iterate_context ctx; pool_t pool; struct hash_iterate_context *iter; struct file_dict_iterate_path *paths; enum dict_iterate_flags flags; unsigned int failed:1; }; static struct dotlock_settings file_dict_dotlock_settings = { .timeout = 60*2, .stale_timeout = 60, .use_io_notify = TRUE }; static int file_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct file_dict *dict; const char *p, *path; dict = i_new(struct file_dict, 1); dict->lock_method = FILE_LOCK_METHOD_DOTLOCK; p = strchr(uri, ':'); if (p == NULL) { /* no parameters */ path = uri; } else { path = t_strdup_until(uri, p++); if (strcmp(p, "lock=fcntl") == 0) dict->lock_method = FILE_LOCK_METHOD_FCNTL; else if (strcmp(p, "lock=flock") == 0) dict->lock_method = FILE_LOCK_METHOD_FLOCK; else { *error_r = t_strdup_printf("Invalid parameter: %s", p+1); i_free(dict); return -1; } } dict->path = set->home_dir == NULL ? i_strdup(path) : i_strdup(home_expand_tilde(path, set->home_dir)); dict->dict = *driver; dict->hash_pool = pool_alloconly_create("file dict", 1024); hash_table_create(&dict->hash, dict->hash_pool, 0, str_hash, strcmp); dict->fd = -1; *dict_r = &dict->dict; return 0; } static void file_dict_deinit(struct dict *_dict) { struct file_dict *dict = (struct file_dict *)_dict; if (dict->fd != -1) { if (close(dict->fd) < 0) i_error("close(%s) failed: %m", dict->path); } hash_table_destroy(&dict->hash); pool_unref(&dict->hash_pool); i_free(dict->path); i_free(dict); } static bool file_dict_need_refresh(struct file_dict *dict) { struct stat st1, st2; if (dict->dict.iter_count > 0) { /* Change nothing while there are iterators or they can crash because the hash table content recreated. */ return FALSE; } if (dict->fd == -1) return TRUE; /* Disable NFS flushing for now since it can cause unnecessary problems and there's no easy way for us to know here if mail_nfs_storage=yes. In any case it's pretty much an unsupported setting nowadays. */ /*nfs_flush_file_handle_cache(dict->path);*/ if (nfs_safe_stat(dict->path, &st1) < 0) { i_error("stat(%s) failed: %m", dict->path); return FALSE; } if (fstat(dict->fd, &st2) < 0) { if (errno != ESTALE) i_error("fstat(%s) failed: %m", dict->path); return TRUE; } if (st1.st_ino != st2.st_ino || !CMP_DEV_T(st1.st_dev, st2.st_dev)) { /* file changed */ return TRUE; } return FALSE; } static int file_dict_open_latest(struct file_dict *dict) { int open_type; if (!file_dict_need_refresh(dict)) return 0; if (dict->fd != -1) { if (close(dict->fd) < 0) i_error("close(%s) failed: %m", dict->path); } open_type = dict->lock_method == FILE_LOCK_METHOD_DOTLOCK ? O_RDONLY : O_RDWR; dict->fd = open(dict->path, open_type); if (dict->fd == -1) { if (errno == ENOENT) return 0; if (errno == EACCES) i_error("%s", eacces_error_get("open", dict->path)); else i_error("open(%s) failed: %m", dict->path); return -1; } dict->refreshed = FALSE; return 1; } static int file_dict_refresh(struct file_dict *dict) { struct istream *input; char *key, *value; if (file_dict_open_latest(dict) < 0) return -1; if (dict->refreshed || dict->dict.iter_count > 0) return 0; hash_table_clear(dict->hash, TRUE); p_clear(dict->hash_pool); if (dict->fd != -1) { input = i_stream_create_fd(dict->fd, (size_t)-1, FALSE); while ((key = i_stream_read_next_line(input)) != NULL) { /* strdup() before the second read */ key = str_tabunescape(p_strdup(dict->hash_pool, key)); if ((value = i_stream_read_next_line(input)) == NULL) break; value = str_tabunescape(p_strdup(dict->hash_pool, value)); hash_table_insert(dict->hash, key, value); } i_stream_destroy(&input); } dict->refreshed = TRUE; return 0; } static int file_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct file_dict *dict = (struct file_dict *)_dict; if (file_dict_refresh(dict) < 0) return -1; *value_r = p_strdup(pool, hash_table_lookup(dict->hash, key)); return *value_r == NULL ? 0 : 1; } static struct dict_iterate_context * file_dict_iterate_init(struct dict *_dict, const char *const *paths, enum dict_iterate_flags flags) { struct file_dict_iterate_context *ctx; struct file_dict *dict = (struct file_dict *)_dict; unsigned int i, path_count; pool_t pool; pool = pool_alloconly_create("file dict iterate", 256); ctx = p_new(pool, struct file_dict_iterate_context, 1); ctx->ctx.dict = _dict; ctx->pool = pool; for (path_count = 0; paths[path_count] != NULL; path_count++) ; ctx->paths = p_new(pool, struct file_dict_iterate_path, path_count + 1); for (i = 0; i < path_count; i++) { ctx->paths[i].path = p_strdup(pool, paths[i]); ctx->paths[i].len = strlen(paths[i]); } ctx->flags = flags; if (file_dict_refresh(dict) < 0) ctx->failed = TRUE; ctx->iter = hash_table_iterate_init(dict->hash); return &ctx->ctx; } static const struct file_dict_iterate_path * file_dict_iterate_find_path(struct file_dict_iterate_context *ctx, const char *key) { unsigned int i; for (i = 0; ctx->paths[i].path != NULL; i++) { if (strncmp(ctx->paths[i].path, key, ctx->paths[i].len) == 0) return &ctx->paths[i]; } return NULL; } static bool file_dict_iterate(struct dict_iterate_context *_ctx, const char **key_r, const char **value_r) { struct file_dict_iterate_context *ctx = (struct file_dict_iterate_context *)_ctx; const struct file_dict_iterate_path *path; char *key, *value; while (hash_table_iterate(ctx->iter, ((struct file_dict *)_ctx->dict)->hash, &key, &value)) { path = file_dict_iterate_find_path(ctx, key); if (path == NULL) continue; if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0) { /* match everything */ } else if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) { if (key[path->len] != '\0') continue; } else { if (strchr(key + path->len, '/') != NULL) continue; } *key_r = key; *value_r = value; return TRUE; } return FALSE; } static int file_dict_iterate_deinit(struct dict_iterate_context *_ctx) { struct file_dict_iterate_context *ctx = (struct file_dict_iterate_context *)_ctx; int ret = ctx->failed ? -1 : 0; hash_table_iterate_deinit(&ctx->iter); pool_unref(&ctx->pool); return ret; } static struct dict_transaction_context * file_dict_transaction_init(struct dict *_dict) { struct dict_transaction_memory_context *ctx; pool_t pool; pool = pool_alloconly_create("file dict transaction", 2048); ctx = p_new(pool, struct dict_transaction_memory_context, 1); dict_transaction_memory_init(ctx, _dict, pool); return &ctx->ctx; } static void file_dict_apply_changes(struct dict_transaction_memory_context *ctx, bool *atomic_inc_not_found_r) { struct file_dict *dict = (struct file_dict *)ctx->ctx.dict; const char *tmp; char *key, *value, *old_value; char *orig_key, *orig_value; const struct dict_transaction_memory_change *change; size_t new_len; long long diff; array_foreach(&ctx->changes, change) { if (hash_table_lookup_full(dict->hash, change->key, &orig_key, &orig_value)) { key = orig_key; old_value = orig_value; } else { key = NULL; old_value = NULL; } value = NULL; switch (change->type) { case DICT_CHANGE_TYPE_INC: if (old_value == NULL) { *atomic_inc_not_found_r = TRUE; break; } if (str_to_llong(old_value, &diff) < 0) i_unreached(); diff += change->value.diff; tmp = t_strdup_printf("%lld", diff); new_len = strlen(tmp); if (old_value == NULL || new_len > strlen(old_value)) value = p_strdup(dict->hash_pool, tmp); else { memcpy(old_value, tmp, new_len + 1); value = old_value; } /* fall through */ case DICT_CHANGE_TYPE_SET: if (key == NULL) key = p_strdup(dict->hash_pool, change->key); if (value == NULL) { value = p_strdup(dict->hash_pool, change->value.str); } hash_table_update(dict->hash, key, value); break; case DICT_CHANGE_TYPE_APPEND: if (key == NULL) key = p_strdup(dict->hash_pool, change->key); if (old_value == NULL) { value = p_strdup(dict->hash_pool, change->value.str); } else { value = p_strconcat(dict->hash_pool, old_value, change->value.str, NULL); } hash_table_update(dict->hash, key, value); break; case DICT_CHANGE_TYPE_UNSET: if (old_value != NULL) hash_table_remove(dict->hash, key); break; } } } static int fd_copy_stat_permissions(const struct stat *src_st, int dest_fd, const char *dest_path) { struct stat dest_st; if (fstat(dest_fd, &dest_st) < 0) { i_error("fstat(%s) failed: %m", dest_path); return -1; } if (src_st->st_gid != dest_st.st_gid && ((src_st->st_mode & 0070) >> 3 != (src_st->st_mode & 0007))) { /* group has different permissions from world. preserve the group. */ if (fchown(dest_fd, (uid_t)-1, src_st->st_gid) < 0) { i_error("fchown(%s, -1, %s) failed: %m", dest_path, dec2str(src_st->st_gid)); return -1; } } if ((src_st->st_mode & 07777) != (dest_st.st_mode & 07777)) { if (fchmod(dest_fd, src_st->st_mode & 07777) < 0) { i_error("fchmod(%s, %o) failed: %m", dest_path, (int)(src_st->st_mode & 0777)); return -1; } } return 0; } static int fd_copy_permissions(int src_fd, const char *src_path, int dest_fd, const char *dest_path) { struct stat src_st; if (fstat(src_fd, &src_st) < 0) { i_error("fstat(%s) failed: %m", src_path); return -1; } return fd_copy_stat_permissions(&src_st, dest_fd, dest_path); } static int fd_copy_parent_dir_permissions(const char *src_path, int dest_fd, const char *dest_path) { struct stat src_st; const char *src_dir, *p; p = strrchr(src_path, '/'); if (p == NULL) src_dir = "."; else src_dir = t_strdup_until(src_path, p); if (stat(src_dir, &src_st) < 0) { i_error("stat(%s) failed: %m", src_dir); return -1; } src_st.st_mode &= 0666; return fd_copy_stat_permissions(&src_st, dest_fd, dest_path); } static int file_dict_mkdir(struct file_dict *dict) { const char *path, *p, *root; struct stat st; mode_t mode = 0700; p = strrchr(dict->path, '/'); if (p == NULL) return 0; path = t_strdup_until(dict->path, p); if (stat_first_parent(path, &root, &st) < 0) { if (errno == EACCES) i_error("%s", eacces_error_get("stat", root)); else i_error("stat(%s) failed: %m", root); return -1; } if ((st.st_mode & S_ISGID) != 0) { /* preserve parent's permissions when it has setgid bit */ mode = st.st_mode; } if (mkdir_parents(path, mode) < 0 && errno != EEXIST) { if (errno == EACCES) i_error("%s", eacces_error_get("mkdir_parents", path)); else i_error("mkdir_parents(%s) failed: %m", path); return -1; } return 0; } static int file_dict_lock(struct file_dict *dict, struct file_lock **lock_r) { int ret; if (file_dict_open_latest(dict) < 0) return -1; if (dict->fd == -1) { /* quota file doesn't exist yet, we need to create it */ dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600); if (dict->fd == -1 && errno == ENOENT) { if (file_dict_mkdir(dict) < 0) return -1; dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600); } if (dict->fd == -1) { if (errno == EACCES) i_error("%s", eacces_error_get("creat", dict->path)); else i_error("creat(%s) failed: %m", dict->path); return -1; } (void)fd_copy_parent_dir_permissions(dict->path, dict->fd, dict->path); } do { if (file_wait_lock(dict->fd, dict->path, F_WRLCK, dict->lock_method, file_dict_dotlock_settings.timeout, lock_r) <= 0) { i_error("file_wait_lock(%s) failed: %m", dict->path); return -1; } /* check again if we need to reopen the file because it was just replaced */ } while ((ret = file_dict_open_latest(dict)) > 0); return ret < 0 ? -1 : 0; } static int file_dict_write_changes(struct dict_transaction_memory_context *ctx, bool *atomic_inc_not_found_r) { struct file_dict *dict = (struct file_dict *)ctx->ctx.dict; struct dotlock *dotlock = NULL; struct file_lock *lock = NULL; const char *temp_path = NULL; struct hash_iterate_context *iter; struct ostream *output; char *key, *value; string_t *str; int fd = -1; *atomic_inc_not_found_r = FALSE; switch (dict->lock_method) { case FILE_LOCK_METHOD_FCNTL: case FILE_LOCK_METHOD_FLOCK: if (file_dict_lock(dict, &lock) < 0) return -1; temp_path = t_strdup_printf("%s.tmp", dict->path); fd = creat(temp_path, 0600); if (fd == -1) { i_error("file dict commit: creat(%s) failed: %m", temp_path); file_unlock(&lock); return -1; } break; case FILE_LOCK_METHOD_DOTLOCK: fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0, &dotlock); if (fd == -1 && errno == ENOENT) { if (file_dict_mkdir(dict) < 0) return -1; fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0, &dotlock); } if (fd == -1) { i_error("file dict commit: file_dotlock_open(%s) failed: %m", dict->path); return -1; } temp_path = file_dotlock_get_lock_path(dotlock); break; } /* refresh once more now that we're locked */ if (file_dict_refresh(dict) < 0) { if (dotlock != NULL) file_dotlock_delete(&dotlock); else { i_close_fd(&fd); file_unlock(&lock); } return -1; } if (dict->fd != -1) { /* preserve the permissions */ (void)fd_copy_permissions(dict->fd, dict->path, fd, temp_path); } else { /* get initial permissions from parent directory */ (void)fd_copy_parent_dir_permissions(dict->path, fd, temp_path); } file_dict_apply_changes(ctx, atomic_inc_not_found_r); output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(output); iter = hash_table_iterate_init(dict->hash); str = t_str_new(256); while (hash_table_iterate(iter, dict->hash, &key, &value)) { str_truncate(str, 0); str_append_tabescaped(str, key); str_append_c(str, '\n'); str_append_tabescaped(str, value); str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } hash_table_iterate_deinit(&iter); if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %m", temp_path); o_stream_destroy(&output); i_close_fd(&fd); return -1; } o_stream_destroy(&output); if (dotlock != NULL) { if (file_dotlock_replace(&dotlock, DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) { i_close_fd(&fd); return -1; } } else { if (rename(temp_path, dict->path) < 0) { i_error("rename(%s, %s) failed: %m", temp_path, dict->path); file_unlock(&lock); i_close_fd(&fd); return -1; } file_lock_free(&lock); } if (dict->fd != -1) i_close_fd(&dict->fd); dict->fd = fd; return 0; } static int file_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async ATTR_UNUSED, dict_transaction_commit_callback_t *callback, void *context) { struct dict_transaction_memory_context *ctx = (struct dict_transaction_memory_context *)_ctx; bool atomic_inc_not_found; int ret; if (file_dict_write_changes(ctx, &atomic_inc_not_found) < 0) ret = -1; else if (atomic_inc_not_found) ret = 0; else ret = 1; pool_unref(&ctx->pool); if (callback != NULL) callback(ret, context); return ret; } struct dict dict_driver_file = { .name = "file", { .init = file_dict_init, .deinit = file_dict_deinit, .lookup = file_dict_lookup, .iterate_init = file_dict_iterate_init, .iterate = file_dict_iterate, .iterate_deinit = file_dict_iterate_deinit, .transaction_init = file_dict_transaction_init, .transaction_commit = file_dict_transaction_commit, .transaction_rollback = dict_transaction_memory_rollback, .set = dict_transaction_memory_set, .unset = dict_transaction_memory_unset, .append = dict_transaction_memory_append, .atomic_inc = dict_transaction_memory_atomic_inc, } }; dovecot-2.2.33.2/src/lib-dict/dict-sql.h0000644000175000017500000000015513165463624014507 00000000000000#ifndef DICT_SQL_H #define DICT_SQL_H void dict_sql_register(void); void dict_sql_unregister(void); #endif dovecot-2.2.33.2/src/lib-dict/dict-memcached.c0000644000175000017500000002326013165463624015613 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING memcached */ #include "lib.h" #include "array.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "connection.h" #include "dict-private.h" #define MEMCACHED_DEFAULT_PORT 11211 #define MEMCACHED_DEFAULT_LOOKUP_TIMEOUT_MSECS (1000*30) /* we need only very limited memcached functionality, so just define the binary protocol ourself instead requiring protocol_binary.h */ #define MEMCACHED_REQUEST_HDR_MAGIC 0x80 #define MEMCACHED_REPLY_HDR_MAGIC 0x81 #define MEMCACHED_REQUEST_HDR_LENGTH 24 #define MEMCACHED_REPLY_HDR_LENGTH 24 #define MEMCACHED_CMD_GET 0x00 #define MEMCACHED_DATA_TYPE_RAW 0x00 enum memcached_response { MEMCACHED_RESPONSE_OK = 0x0000, MEMCACHED_RESPONSE_NOTFOUND = 0x0001, MEMCACHED_RESPONSE_INTERNALERROR= 0x0084, MEMCACHED_RESPONSE_BUSY = 0x0085, MEMCACHED_RESPONSE_TEMPFAILURE = 0x0086 }; struct memcached_connection { struct connection conn; struct memcached_dict *dict; buffer_t *cmd; struct { const unsigned char *value; size_t value_len; enum memcached_response status; bool reply_received; } reply; }; struct memcached_dict { struct dict dict; struct ip_addr ip; char *key_prefix; in_port_t port; unsigned int timeout_msecs; struct ioloop *ioloop; struct memcached_connection conn; bool connected; }; static struct connection_list *memcached_connections; static void memcached_conn_destroy(struct connection *_conn) { struct memcached_connection *conn = (struct memcached_connection *)_conn; conn->dict->connected = FALSE; connection_disconnect(_conn); if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } static int memcached_input_get(struct memcached_connection *conn) { const unsigned char *data; size_t size; uint32_t body_len, value_pos; uint16_t key_len, key_pos, status; uint8_t extras_len, data_type; data = i_stream_get_data(conn->conn.input, &size); if (size < MEMCACHED_REPLY_HDR_LENGTH) return 0; if (data[0] != MEMCACHED_REPLY_HDR_MAGIC) { i_error("memcached: Invalid reply magic: %u != %u", data[0], MEMCACHED_REPLY_HDR_MAGIC); return -1; } memcpy(&body_len, data+8, 4); body_len = ntohl(body_len); body_len += MEMCACHED_REPLY_HDR_LENGTH; if (size < body_len) { /* we haven't read the whole response yet */ return 0; } memcpy(&key_len, data+2, 2); key_len = ntohs(key_len); extras_len = data[4]; data_type = data[5]; memcpy(&status, data+6, 2); status = ntohs(status); if (data_type != MEMCACHED_DATA_TYPE_RAW) { i_error("memcached: Unsupported data type: %u != %u", data[0], MEMCACHED_DATA_TYPE_RAW); return -1; } key_pos = MEMCACHED_REPLY_HDR_LENGTH + extras_len; value_pos = key_pos + key_len; if (value_pos > body_len) { i_error("memcached: Invalid key/extras lengths"); return -1; } conn->reply.value = data + value_pos; conn->reply.value_len = body_len - value_pos; conn->reply.status = status; i_stream_skip(conn->conn.input, body_len); conn->reply.reply_received = TRUE; if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); return 1; } static void memcached_conn_input(struct connection *_conn) { struct memcached_connection *conn = (struct memcached_connection *)_conn; switch (i_stream_read(_conn->input)) { case 0: return; case -1: memcached_conn_destroy(_conn); return; default: break; } if (memcached_input_get(conn) < 0) memcached_conn_destroy(_conn); } static void memcached_conn_connected(struct connection *_conn, bool success) { struct memcached_connection *conn = (struct memcached_connection *)_conn; if (!success) { i_error("memcached: connect(%s, %u) failed: %m", net_ip2addr(&conn->dict->ip), conn->dict->port); } else { conn->dict->connected = TRUE; } if (conn->dict->ioloop != NULL) io_loop_stop(conn->dict->ioloop); } static const struct connection_settings memcached_conn_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = TRUE }; static const struct connection_vfuncs memcached_conn_vfuncs = { .destroy = memcached_conn_destroy, .input = memcached_conn_input, .client_connected = memcached_conn_connected }; static int memcached_dict_init(struct dict *driver, const char *uri, const struct dict_settings *set ATTR_UNUSED, struct dict **dict_r, const char **error_r) { struct memcached_dict *dict; const char *const *args; int ret = 0; if (memcached_connections == NULL) { memcached_connections = connection_list_init(&memcached_conn_set, &memcached_conn_vfuncs); } dict = i_new(struct memcached_dict, 1); if (net_addr2ip("127.0.0.1", &dict->ip) < 0) i_unreached(); dict->port = MEMCACHED_DEFAULT_PORT; dict->timeout_msecs = MEMCACHED_DEFAULT_LOOKUP_TIMEOUT_MSECS; dict->key_prefix = i_strdup(""); args = t_strsplit(uri, ":"); for (; *args != NULL; args++) { if (strncmp(*args, "host=", 5) == 0) { if (net_addr2ip(*args+5, &dict->ip) < 0) { *error_r = t_strdup_printf("Invalid IP: %s", *args+5); ret = -1; } } else if (strncmp(*args, "port=", 5) == 0) { if (net_str2port(*args+5, &dict->port) < 0) { *error_r = t_strdup_printf("Invalid port: %s", *args+5); ret = -1; } } else if (strncmp(*args, "prefix=", 7) == 0) { i_free(dict->key_prefix); dict->key_prefix = i_strdup(*args + 7); } else if (strncmp(*args, "timeout_msecs=", 14) == 0) { if (str_to_uint(*args+14, &dict->timeout_msecs) < 0) { *error_r = t_strdup_printf( "Invalid timeout_msecs: %s", *args+14); ret = -1; } } else { *error_r = t_strdup_printf("Unknown parameter: %s", *args); ret = -1; } } if (ret < 0) { i_free(dict->key_prefix); i_free(dict); return -1; } connection_init_client_ip(memcached_connections, &dict->conn.conn, &dict->ip, dict->port); dict->dict = *driver; dict->conn.cmd = buffer_create_dynamic(default_pool, 256); dict->conn.dict = dict; *dict_r = &dict->dict; return 0; } static void memcached_dict_deinit(struct dict *_dict) { struct memcached_dict *dict = (struct memcached_dict *)_dict; connection_deinit(&dict->conn.conn); buffer_free(&dict->conn.cmd); i_free(dict->key_prefix); i_free(dict); if (memcached_connections->connections == NULL) connection_list_deinit(&memcached_connections); } static void memcached_dict_lookup_timeout(struct memcached_dict *dict) { i_error("memcached: Lookup timed out in %u.%03u secs", dict->timeout_msecs/1000, dict->timeout_msecs%1000); io_loop_stop(dict->ioloop); } static void memcached_add_header(buffer_t *buf, unsigned int key_len) { uint32_t body_len = htonl(key_len); i_assert(key_len <= 0xffff); buffer_append_c(buf, MEMCACHED_REQUEST_HDR_MAGIC); buffer_append_c(buf, MEMCACHED_CMD_GET); buffer_append_c(buf, (key_len >> 8) & 0xff); buffer_append_c(buf, key_len & 0xff); buffer_append_c(buf, 0); /* extras length */ buffer_append_c(buf, MEMCACHED_DATA_TYPE_RAW); buffer_append_zero(buf, 2); /* vbucket id - we probably don't care? */ buffer_append(buf, &body_len, sizeof(body_len)); buffer_append_zero(buf, 4+8); /* opaque + cas */ i_assert(buf->used == MEMCACHED_REQUEST_HDR_LENGTH); } static int memcached_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct memcached_dict *dict = (struct memcached_dict *)_dict; struct ioloop *prev_ioloop = current_ioloop; struct timeout *to; size_t key_len; if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0) key += strlen(DICT_PATH_SHARED); else { i_error("memcached: Only shared keys supported currently"); return -1; } if (*dict->key_prefix != '\0') key = t_strconcat(dict->key_prefix, key, NULL); key_len = strlen(key); if (key_len > 0xffff) { i_error("memcached: Key is too long (%"PRIuSIZE_T" bytes): %s", key_len, key); return -1; } i_assert(dict->ioloop == NULL); dict->ioloop = io_loop_create(); connection_switch_ioloop(&dict->conn.conn); if (dict->conn.conn.fd_in == -1 && connection_client_connect(&dict->conn.conn) < 0) { i_error("memcached: Couldn't connect to %s:%u", net_ip2addr(&dict->ip), dict->port); } else { to = timeout_add(dict->timeout_msecs, memcached_dict_lookup_timeout, dict); if (!dict->connected) { /* wait for connection */ io_loop_run(dict->ioloop); } if (dict->connected) { buffer_set_used_size(dict->conn.cmd, 0); memcached_add_header(dict->conn.cmd, key_len); buffer_append(dict->conn.cmd, key, key_len); o_stream_nsend(dict->conn.conn.output, dict->conn.cmd->data, dict->conn.cmd->used); i_zero(&dict->conn.reply); io_loop_run(dict->ioloop); } timeout_remove(&to); } io_loop_set_current(prev_ioloop); connection_switch_ioloop(&dict->conn.conn); io_loop_set_current(dict->ioloop); io_loop_destroy(&dict->ioloop); if (!dict->conn.reply.reply_received) { /* we failed in some way. make sure we disconnect since the connection state isn't known anymore */ memcached_conn_destroy(&dict->conn.conn); return -1; } switch (dict->conn.reply.status) { case MEMCACHED_RESPONSE_OK: *value_r = p_strndup(pool, dict->conn.reply.value, dict->conn.reply.value_len); return 1; case MEMCACHED_RESPONSE_NOTFOUND: return 0; case MEMCACHED_RESPONSE_INTERNALERROR: i_error("memcached: Lookup(%s) failed: Internal error", key); return -1; case MEMCACHED_RESPONSE_BUSY: i_error("memcached: Lookup(%s) failed: Busy", key); return -1; case MEMCACHED_RESPONSE_TEMPFAILURE: i_error("memcached: Lookup(%s) failed: Temporary failure", key); return -1; } i_error("memcached: Lookup(%s) failed: Error code=%u", key, dict->conn.reply.status); return -1; } struct dict dict_driver_memcached = { .name = "memcached", { .init = memcached_dict_init, .deinit = memcached_dict_deinit, .lookup = memcached_dict_lookup, } }; dovecot-2.2.33.2/src/lib-dict/dict-transaction-memory.h0000644000175000017500000000222313165463624017541 00000000000000#ifndef DICT_TRANSACTION_MEMORY_H #define DICT_TRANSACTION_MEMORY_H #include "dict-private.h" enum dict_change_type { DICT_CHANGE_TYPE_SET, DICT_CHANGE_TYPE_UNSET, DICT_CHANGE_TYPE_APPEND, DICT_CHANGE_TYPE_INC }; struct dict_transaction_memory_change { enum dict_change_type type; const char *key; union { const char *str; long long diff; } value; }; struct dict_transaction_memory_context { struct dict_transaction_context ctx; pool_t pool; ARRAY(struct dict_transaction_memory_change) changes; }; void dict_transaction_memory_init(struct dict_transaction_memory_context *ctx, struct dict *dict, pool_t pool); void dict_transaction_memory_rollback(struct dict_transaction_context *ctx); void dict_transaction_memory_set(struct dict_transaction_context *ctx, const char *key, const char *value); void dict_transaction_memory_unset(struct dict_transaction_context *ctx, const char *key); void dict_transaction_memory_append(struct dict_transaction_context *_ctx, const char *key, const char *value); void dict_transaction_memory_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); #endif dovecot-2.2.33.2/src/lib-dict/dict.c0000644000175000017500000002276113165463624013714 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "str.h" #include "dict-sql.h" #include "dict-private.h" static ARRAY(struct dict *) dict_drivers; static struct dict *dict_driver_lookup(const char *name) { struct dict *const *dicts; array_foreach(&dict_drivers, dicts) { struct dict *dict = *dicts; if (strcmp(dict->name, name) == 0) return dict; } return NULL; } void dict_driver_register(struct dict *driver) { if (!array_is_created(&dict_drivers)) i_array_init(&dict_drivers, 8); if (dict_driver_lookup(driver->name) != NULL) { i_fatal("dict_driver_register(%s): Already registered", driver->name); } array_append(&dict_drivers, &driver, 1); } void dict_driver_unregister(struct dict *driver) { struct dict *const *dicts; unsigned int idx = UINT_MAX; array_foreach(&dict_drivers, dicts) { if (*dicts == driver) { idx = array_foreach_idx(&dict_drivers, dicts); break; } } i_assert(idx != UINT_MAX); array_delete(&dict_drivers, idx, 1); if (array_count(&dict_drivers) == 0) array_free(&dict_drivers); } int dict_init(const char *uri, enum dict_data_type value_type, const char *username, const char *base_dir, struct dict **dict_r, const char **error_r) { struct dict_settings set; i_zero(&set); set.value_type = value_type; set.username = username; set.base_dir = base_dir; return dict_init_full(uri, &set, dict_r, error_r); } int dict_init_full(const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { struct dict *dict; const char *p, *name, *error; i_assert(set->username != NULL); p = strchr(uri, ':'); if (p == NULL) { *error_r = t_strdup_printf("Dictionary URI is missing ':': %s", uri); return -1; } name = t_strdup_until(uri, p); dict = dict_driver_lookup(name); if (dict == NULL) { *error_r = t_strdup_printf("Unknown dict module: %s", name); return -1; } if (dict->v.init(dict, p+1, set, dict_r, &error) < 0) { *error_r = t_strdup_printf("dict %s: %s", name, error); return -1; } i_assert(*dict_r != NULL); return 0; } void dict_deinit(struct dict **_dict) { struct dict *dict = *_dict; *_dict = NULL; i_assert(dict->iter_count == 0); i_assert(dict->transaction_count == 0); i_assert(dict->transactions == NULL); dict->v.deinit(dict); } int dict_wait(struct dict *dict) { return dict->v.wait == NULL ? 1 : dict->v.wait(dict); } bool dict_switch_ioloop(struct dict *dict) { if (dict->v.switch_ioloop != NULL) return dict->v.switch_ioloop(dict); else return FALSE; } static bool dict_key_prefix_is_valid(const char *key) { return strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0 || strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0; } int dict_lookup(struct dict *dict, pool_t pool, const char *key, const char **value_r) { i_assert(dict_key_prefix_is_valid(key)); return dict->v.lookup(dict, pool, key, value_r); } void dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context) { if (dict->v.lookup_async == NULL) { struct dict_lookup_result result; i_zero(&result); result.ret = dict_lookup(dict, pool_datastack_create(), key, &result.value); if (result.ret < 0) result.error = "Lookup failed"; const char *const values[] = { result.value, NULL }; result.values = values; callback(&result, context); return; } dict->v.lookup_async(dict, key, callback, context); } struct dict_iterate_context * dict_iterate_init(struct dict *dict, const char *path, enum dict_iterate_flags flags) { const char *paths[2]; paths[0] = path; paths[1] = NULL; return dict_iterate_init_multiple(dict, paths, flags); } struct dict_iterate_context * dict_iterate_init_multiple(struct dict *dict, const char *const *paths, enum dict_iterate_flags flags) { struct dict_iterate_context *ctx; unsigned int i; i_assert(paths[0] != NULL); for (i = 0; paths[i] != NULL; i++) i_assert(dict_key_prefix_is_valid(paths[i])); if (dict->v.iterate_init == NULL) { /* not supported by backend */ i_error("%s: dict iteration not supported", dict->name); ctx = &dict_iter_unsupported; } else { ctx = dict->v.iterate_init(dict, paths, flags); } /* the dict in context can differ from the dict passed as parameter, e.g. it can be dict-fail when iteration is not supported. */ ctx->dict->iter_count++; return ctx; } bool dict_iterate(struct dict_iterate_context *ctx, const char **key_r, const char **value_r) { if (ctx->max_rows > 0 && ctx->row_count >= ctx->max_rows) { /* row count was limited */ ctx->has_more = FALSE; return FALSE; } if (!ctx->dict->v.iterate(ctx, key_r, value_r)) return FALSE; ctx->row_count++; return TRUE; } void dict_iterate_set_async_callback(struct dict_iterate_context *ctx, dict_iterate_callback_t *callback, void *context) { ctx->async_callback = callback; ctx->async_context = context; } void dict_iterate_set_limit(struct dict_iterate_context *ctx, uint64_t max_rows) { ctx->max_rows = max_rows; } bool dict_iterate_has_more(struct dict_iterate_context *ctx) { return ctx->has_more; } int dict_iterate_deinit(struct dict_iterate_context **_ctx) { struct dict_iterate_context *ctx = *_ctx; i_assert(ctx->dict->iter_count > 0); ctx->dict->iter_count--; *_ctx = NULL; return ctx->dict->v.iterate_deinit(ctx); } struct dict_transaction_context *dict_transaction_begin(struct dict *dict) { struct dict_transaction_context *ctx; if (dict->v.transaction_init == NULL) ctx = &dict_transaction_unsupported; else ctx = dict->v.transaction_init(dict); /* the dict in context can differ from the dict passed as parameter, e.g. it can be dict-fail when transactions are not supported. */ ctx->dict->transaction_count++; DLLIST_PREPEND(&ctx->dict->transactions, ctx); return ctx; } void dict_transaction_no_slowness_warning(struct dict_transaction_context *ctx) { ctx->no_slowness_warning = TRUE; } void dict_transaction_set_timestamp(struct dict_transaction_context *ctx, const struct timespec *ts) { /* These asserts are mainly here to guarantee a possibility in future to change the API to support multiple timestamps within the same transaction, so this call would apply only to the following changes. */ i_assert(!ctx->changed); i_assert(ctx->timestamp.tv_sec == 0); i_assert(ts->tv_sec > 0); ctx->timestamp = *ts; if (ctx->dict->v.set_timestamp != NULL) ctx->dict->v.set_timestamp(ctx, ts); } int dict_transaction_commit(struct dict_transaction_context **_ctx) { struct dict_transaction_context *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->dict->transaction_count > 0); ctx->dict->transaction_count--; DLLIST_REMOVE(&ctx->dict->transactions, ctx); return ctx->dict->v.transaction_commit(ctx, FALSE, NULL, NULL); } void dict_transaction_commit_async(struct dict_transaction_context **_ctx, dict_transaction_commit_callback_t *callback, void *context) { struct dict_transaction_context *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->dict->transaction_count > 0); ctx->dict->transaction_count--; DLLIST_REMOVE(&ctx->dict->transactions, ctx); ctx->dict->v.transaction_commit(ctx, TRUE, callback, context); } void dict_transaction_rollback(struct dict_transaction_context **_ctx) { struct dict_transaction_context *ctx = *_ctx; *_ctx = NULL; i_assert(ctx->dict->transaction_count > 0); ctx->dict->transaction_count--; DLLIST_REMOVE(&ctx->dict->transactions, ctx); ctx->dict->v.transaction_rollback(ctx); } void dict_set(struct dict_transaction_context *ctx, const char *key, const char *value) { i_assert(dict_key_prefix_is_valid(key)); T_BEGIN { ctx->dict->v.set(ctx, key, value); } T_END; ctx->changed = TRUE; } void dict_unset(struct dict_transaction_context *ctx, const char *key) { i_assert(dict_key_prefix_is_valid(key)); T_BEGIN { ctx->dict->v.unset(ctx, key); } T_END; ctx->changed = TRUE; } void dict_append(struct dict_transaction_context *ctx, const char *key, const char *value) { i_assert(dict_key_prefix_is_valid(key)); ctx->dict->v.append(ctx, key, value); ctx->changed = TRUE; } void dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff) { i_assert(dict_key_prefix_is_valid(key)); if (diff != 0) T_BEGIN { ctx->dict->v.atomic_inc(ctx, key, diff); ctx->changed = TRUE; } T_END; } const char *dict_escape_string(const char *str) { const char *p; string_t *ret; /* see if we need to escape it */ for (p = str; *p != '\0'; p++) { if (*p == '/' || *p == '\\') break; } if (*p == '\0') return str; /* escape */ ret = t_str_new((size_t) (p - str) + 128); str_append_n(ret, str, (size_t) (p - str)); for (; *p != '\0'; p++) { switch (*p) { case '/': str_append_c(ret, '\\'); str_append_c(ret, '|'); break; case '\\': str_append_c(ret, '\\'); str_append_c(ret, '\\'); break; default: str_append_c(ret, *p); break; } } return str_c(ret); } const char *dict_unescape_string(const char *str) { const char *p; string_t *ret; /* see if we need to unescape it */ for (p = str; *p != '\0'; p++) { if (*p == '\\') break; } if (*p == '\0') return str; /* unescape */ ret = t_str_new((size_t) (p - str) + strlen(p) + 1); str_append_n(ret, str, (size_t) (p - str)); for (; *p != '\0'; p++) { if (*p != '\\') str_append_c(ret, *p); else { if (*++p == '|') str_append_c(ret, '/'); else if (*p == '\0') break; else str_append_c(ret, *p); } } return str_c(ret); } dovecot-2.2.33.2/src/lib-dict/dict-db.c0000644000175000017500000002747413165463624014305 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ /* FIXME: BDB isn't being used correctly/safely. */ #include "lib.h" #include "dict-private.h" #ifdef BUILD_DB #include struct db_dict { struct dict dict; enum dict_data_type value_type; pool_t pool; DB_ENV *db_env; DB *pdb; DB *sdb; DB_TXN *tid; }; struct db_dict_iterate_context { struct dict_iterate_context ctx; pool_t pool; DBC *cursor; char *path; size_t path_len; DBT pkey, pdata; int (*iterate_next)(struct db_dict_iterate_context *ctx, const char **key_r, const char **value_r); enum dict_iterate_flags flags; bool failed; }; struct db_dict_transaction_context { struct dict_transaction_context ctx; DB_TXN *tid; }; static void db_dict_deinit(struct dict *_dict); static int associate_key(DB *pdb ATTR_UNUSED, const DBT *pkey ATTR_UNUSED, const DBT *pdata, DBT *skey) { i_zero(skey); skey->data = pdata->data; skey->size = pdata->size; return 0; } static int uint32_t_compare(DB *db ATTR_UNUSED, const DBT *keya, const DBT *keyb) { const uint32_t *ua = keya->data, *ub = keyb->data; return *ua > *ub ? 1 : (*ua < *ub ? -1 : 0); } static int db_dict_open(struct db_dict *dict, const char *uri, const char *username) { const char *dir; int ret; dict->db_env->set_errfile(dict->db_env, stderr); dict->db_env->set_errpfx(dict->db_env, p_strdup_printf(dict->pool, "db_env(%s)", username)); dir = strrchr(uri, '/'); if (dir != NULL) dir = t_strdup_until(uri, dir); else dir = "."; ret = dict->db_env->open(dict->db_env, dir, DB_CREATE | DB_INIT_MPOOL | DB_INIT_TXN, 0); if (ret != 0) { i_error("db_env.open(%s) failed: %s\n", dir, db_strerror(ret)); return -1; } ret = dict->db_env->txn_begin(dict->db_env, NULL, &dict->tid, 0); if (ret != 0) { i_error("db_env.txn_begin() failed: %s\n", db_strerror(ret)); return -1; } /* create both primary and secondary databases */ ret = db_create(&dict->pdb, dict->db_env, 0); if (ret != 0) { i_error("db_create(primary) failed: %s\n", db_strerror(ret)); return -1; } dict->pdb->set_errfile(dict->pdb, stderr); dict->pdb->set_errpfx(dict->pdb, p_strdup_printf(dict->pool, "db(primary, %s)", username)); ret = db_create(&dict->sdb, dict->db_env, 0); if (ret != 0) { i_error("db_create(secondary) failed: %s\n", db_strerror(ret)); return -1; } dict->sdb->set_errfile(dict->sdb, stderr); dict->sdb->set_errpfx(dict->sdb, p_strdup_printf(dict->pool, "db(secondary, %s)", username)); if ((ret = dict->pdb->open(dict->pdb, dict->tid, uri, NULL, DB_BTREE, DB_CREATE, 0)) != 0) { i_error("pdb.open() failed: %s\n", db_strerror(ret)); return -1; } if (dict->sdb->set_flags(dict->sdb, DB_DUP) != 0) return -1; /* by default keys are compared as strings. if we store uint32_t, we need a customized compare function */ if (dict->value_type == DICT_DATA_TYPE_UINT32) { if (dict->sdb->set_bt_compare(dict->sdb, uint32_t_compare) != 0) return -1; } if ((ret = dict->sdb->open(dict->sdb, dict->tid, NULL, NULL, DB_BTREE, DB_CREATE, 0)) != 0) { i_error("sdb.open() failed: %s\n", db_strerror(ret)); return -1; } if ((ret = dict->pdb->associate(dict->pdb, dict->tid, dict->sdb, associate_key, DB_CREATE)) != 0) { i_error("pdb.associate() failed: %s\n", db_strerror(ret)); return -1; } return 0; } static int db_dict_init(struct dict *driver, const char *uri, enum dict_data_type value_type, const char *username, const char *base_dir ATTR_UNUSED, struct dict **dict_r) { struct db_dict *dict; pool_t pool; int ret, major, minor, patch; (void)db_version(&major, &minor, &patch); if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) { i_error("Berkeley DB version mismatch: " "Compiled against %d.%d.%d headers, " "run-time linked against %d.%d.%d library", DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, major, minor, patch); return -1; } pool = pool_alloconly_create("db dict", 1024); dict = p_new(pool, struct db_dict, 1); dict->pool = pool; dict->dict = *driver; dict->value_type = value_type; /* prepare the environment */ ret = db_env_create(&dict->db_env, 0); if (ret != 0) { i_error("db_env_create() failed: %s\n", db_strerror(ret)); pool_unref(&pool); return -1; } if (db_dict_open(dict, uri, username) < 0) { i_error("db(%s) open failed", uri); db_dict_deinit(&dict->dict); return -1; } *dict_r = &dict->dict; return 0; } static void db_dict_deinit(struct dict *_dict) { struct db_dict *dict = (struct db_dict *)_dict; if (dict->tid != NULL) (void)dict->tid->commit(dict->tid, 0); if (dict->pdb != NULL) dict->pdb->close(dict->pdb, 0); if (dict->sdb != NULL) dict->sdb->close(dict->sdb, 0); dict->db_env->close(dict->db_env, 0); pool_unref(&dict->pool); } static int db_dict_iterate_set(struct db_dict_iterate_context *ctx, int ret, const char **key_r, const char **value_r) { struct db_dict *dict = (struct db_dict *)ctx->ctx.dict; uint32_t value; if (ret == DB_NOTFOUND) return 0; else if (ret != 0) return -1; p_clear(ctx->pool); *key_r = p_strndup(ctx->pool, ctx->pkey.data, ctx->pkey.size); switch (dict->value_type) { case DICT_DATA_TYPE_UINT32: i_assert(ctx->pdata.size == sizeof(uint32_t)); /* data may not be aligned, so use memcpy() */ memcpy(&value, ctx->pdata.data, sizeof(value)); *value_r = p_strdup(ctx->pool, dec2str(value)); break; case DICT_DATA_TYPE_STRING: *value_r = p_strndup(ctx->pool, ctx->pdata.data, ctx->pdata.size); break; } return 1; } static int db_dict_lookup(struct dict *_dict, pool_t pool, const char *key, const char **value_r) { struct db_dict *dict = (struct db_dict *)_dict; DBT pkey, pdata; uint32_t value; int ret; memset(&pkey, 0, sizeof(DBT)); memset(&pdata, 0, sizeof(DBT)); pkey.data = (char *)key; pkey.size = strlen(key); ret = dict->pdb->get(dict->pdb, NULL, &pkey, &pdata, 0); if (ret == DB_NOTFOUND) return 0; else if (ret != 0) return -1; switch (dict->value_type) { case DICT_DATA_TYPE_UINT32: i_assert(pdata.size == sizeof(uint32_t)); /* data may not be aligned, so use memcpy() */ memcpy(&value, pdata.data, sizeof(value)); *value_r = p_strdup(pool, dec2str(value)); break; case DICT_DATA_TYPE_STRING: *value_r = p_strndup(pool, pdata.data, pdata.size); break; } return 1; } static int db_dict_iterate_next(struct db_dict_iterate_context *ctx, const char **key_r, const char **value_r) { DBT pkey, pdata, skey; int ret; i_zero(&pkey); i_zero(&pdata); i_zero(&skey); if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_VALUE) != 0) { while ((ret = ctx->cursor->c_pget(ctx->cursor, &skey, &ctx->pkey, &ctx->pdata, DB_NEXT)) == 0) { /* make sure the path matches */ if (ctx->path == NULL) break; if (ctx->path_len <= ctx->pkey.size && strncmp(ctx->path, ctx->pkey.data, ctx->path_len) == 0) break; } } else { ret = ctx->cursor->c_get(ctx->cursor, &ctx->pkey, &ctx->pdata, DB_NEXT); if (ctx->path != NULL && ret == 0 && (ctx->path_len > ctx->pkey.size || strncmp(ctx->path, ctx->pkey.data, ctx->path_len) != 0)) { /* there are no more matches */ return 0; } } return db_dict_iterate_set(ctx, ret, key_r, value_r); } static int db_dict_iterate_first(struct db_dict_iterate_context *ctx, const char **key_r, const char **value_r) { struct db_dict *dict = (struct db_dict *)ctx->ctx.dict; int ret; ctx->iterate_next = db_dict_iterate_next; if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_VALUE) != 0) { /* iterating through secondary database returns values sorted */ ret = dict->sdb->cursor(dict->sdb, NULL, &ctx->cursor, 0); } else { ret = dict->pdb->cursor(dict->pdb, NULL, &ctx->cursor, 0); if (ret == 0 && ctx->path != NULL) { ctx->pkey.data = ctx->path; ctx->pkey.size = strlen(ctx->path); ret = ctx->cursor->c_get(ctx->cursor, &ctx->pkey, &ctx->pdata, DB_SET_RANGE); if (ret == 0 && strncmp(ctx->path, ctx->pkey.data, ctx->pkey.size) != 0) return 0; return db_dict_iterate_set(ctx, ret, key_r, value_r); } } return db_dict_iterate_next(ctx, key_r, value_r); } static struct dict_iterate_context * db_dict_iterate_init(struct dict *_dict, const char *path, enum dict_iterate_flags flags) { struct db_dict_iterate_context *ctx; ctx = i_new(struct db_dict_iterate_context, 1); ctx->pool = pool_alloconly_create("db iter", 1024); ctx->cursor = NULL; ctx->ctx.dict = _dict; ctx->flags = flags; ctx->path = i_strdup_empty(path); ctx->path_len = ctx->path == NULL ? 0 : strlen(ctx->path); ctx->iterate_next = db_dict_iterate_first; return &ctx->ctx; } static bool db_dict_iterate(struct dict_iterate_context *_ctx, const char **key_r, const char **value_r) { struct db_dict_iterate_context *ctx = (struct db_dict_iterate_context *)_ctx; int ret; ret = ctx->iterate_next(ctx, key_r, value_r); if (ret < 0) ctx->failed = TRUE; return ret > 0; } static int db_dict_iterate_deinit(struct dict_iterate_context *_ctx) { struct db_dict_iterate_context *ctx = (struct db_dict_iterate_context *)_ctx; int ret = ctx->failed ? -1 : 0; ctx->cursor->c_close(ctx->cursor); pool_unref(&ctx->pool); i_free(ctx->path); i_free(ctx); return ret; } static struct dict_transaction_context * db_dict_transaction_init(struct dict *_dict) { struct db_dict *dict = (struct db_dict *)_dict; struct db_dict_transaction_context *ctx; ctx = i_new(struct db_dict_transaction_context, 1); ctx->ctx.dict = _dict; dict->db_env->txn_begin(dict->db_env, NULL, &ctx->tid, 0); return &ctx->ctx; } static int db_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async ATTR_UNUSED, dict_transaction_commit_callback_t *callback, void *context) { struct db_dict_transaction_context *ctx = (struct db_dict_transaction_context *)_ctx; int ret; ret = ctx->tid->commit(ctx->tid, 0) < 0 ? -1 : 1; i_free(ctx); if (callback != NULL) callback(ret, context); return ret; } static void db_dict_transaction_rollback(struct dict_transaction_context *_ctx) { struct db_dict_transaction_context *ctx = (struct db_dict_transaction_context *)_ctx; ctx->tid->abort(ctx->tid); i_free(ctx); } static void db_dict_set(struct dict_transaction_context *_ctx, const char *key, const char *value) { struct db_dict_transaction_context *ctx = (struct db_dict_transaction_context *)_ctx; struct db_dict *dict = (struct db_dict *)_ctx->dict; DBT dkey, ddata; i_zero(&dkey); i_zero(&ddata); dkey.data = (char *)key; dkey.size = strlen(key); if (dict->value_type == DICT_DATA_TYPE_UINT32) { uint32_t ivalue; if (str_to_uint32(value, &ivalue) < 0) { i_error("db: Value not uint32: %s", value); ivalue = 0; } ddata.data = &ivalue; ddata.size = sizeof(ivalue); } else { ddata.data = (char *)value; ddata.size = strlen(value); } dict->pdb->put(dict->pdb, ctx->tid, &dkey, &ddata, 0); } static void db_dict_unset(struct dict_transaction_context *_ctx, const char *key) { struct db_dict_transaction_context *ctx = (struct db_dict_transaction_context *)_ctx; struct db_dict *dict = (struct db_dict *)_ctx->dict; DBT dkey; i_zero(&dkey); dkey.data = (char *)key; dkey.size = strlen(key); dict->pdb->del(dict->pdb, ctx->tid, &dkey, 0); } static void db_dict_atomic_inc(struct dict_transaction_context *_ctx ATTR_UNUSED, const char *key ATTR_UNUSED, long long diff ATTR_UNUSED) { /* FIXME */ } struct dict dict_driver_db = { .name = "db", { .init = db_dict_init, .deinit = db_dict_deinit, .lookup = db_dict_lookup, .iterate_init = db_dict_iterate_init, .iterate = db_dict_iterate, .iterate_deinit = db_dict_iterate_deinit, .transaction_init = db_dict_transaction_init, .transaction_commit = db_dict_transaction_commit, .transaction_rollback = db_dict_transaction_rollback, .set = db_dict_set, .unset = db_dict_unset, .atomic_inc = db_dict_atomic_inc, } }; #endif dovecot-2.2.33.2/src/lib-dict/dict.h0000644000175000017500000001626613165463624013724 00000000000000#ifndef DICT_H #define DICT_H #define DICT_PATH_PRIVATE "priv/" #define DICT_PATH_SHARED "shared/" struct timespec; struct dict; struct dict_iterate_context; enum dict_iterate_flags { /* Recurse to all the sub-hierarchies (e.g. iterating "foo/" will return "foo/a", but should it return "foo/a/b"?) */ DICT_ITERATE_FLAG_RECURSE = 0x01, /* Sort returned results by key */ DICT_ITERATE_FLAG_SORT_BY_KEY = 0x02, /* Sort returned results by value */ DICT_ITERATE_FLAG_SORT_BY_VALUE = 0x04, /* Don't return values, only keys */ DICT_ITERATE_FLAG_NO_VALUE = 0x08, /* Don't recurse at all. This is basically the same as dict_lookup(), but it'll return all the rows instead of only the first one. */ DICT_ITERATE_FLAG_EXACT_KEY = 0x10, /* Perform iteration asynchronously. */ DICT_ITERATE_FLAG_ASYNC = 0x20 }; enum dict_data_type { DICT_DATA_TYPE_STRING = 0, DICT_DATA_TYPE_UINT32, DICT_DATA_TYPE_LAST }; struct dict_settings { enum dict_data_type value_type; const char *username; const char *base_dir; /* home directory for the user, if known */ const char *home_dir; }; struct dict_lookup_result { int ret; /* First returned value (ret > 0) */ const char *value; /* NULL-terminated list of all returned values (ret > 0) */ const char *const *values; /* Error message for a failed lookup (ret < 0) */ const char *error; }; enum dict_commit_ret { DICT_COMMIT_RET_OK = 1, DICT_COMMIT_RET_NOTFOUND = 0, DICT_COMMIT_RET_FAILED = -1, /* write may or may not have succeeded (e.g. write timeout or disconnected from server) */ DICT_COMMIT_RET_WRITE_UNCERTAIN = -2, }; typedef void dict_lookup_callback_t(const struct dict_lookup_result *result, void *context); typedef void dict_iterate_callback_t(void *context); /* ret = enum dict_commit_ret */ typedef void dict_transaction_commit_callback_t(int ret, void *context); void dict_driver_register(struct dict *driver); void dict_driver_unregister(struct dict *driver); void dict_drivers_register_builtin(void); void dict_drivers_unregister_builtin(void); void dict_drivers_register_all(void); void dict_drivers_unregister_all(void); /* Open dictionary with given URI (type:data). Returns 0 if ok, -1 if URI is invalid. */ int dict_init(const char *uri, enum dict_data_type value_type, const char *username, const char *base_dir, struct dict **dict_r, const char **error_r); int dict_init_full(const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r); /* Close dictionary. */ void dict_deinit(struct dict **dict); /* Wait for all pending asynchronous operations to finish. Returns 0 if ok, -1 if error. */ int dict_wait(struct dict *dict); /* Switch the dict to the current ioloop. This can be used to do dict_wait() among other IO work. Returns TRUE if there is actually some work that can be waited on. */ bool dict_switch_ioloop(struct dict *dict) ATTR_NOWARN_UNUSED_RESULT; /* Lookup value for key. Set it to NULL if it's not found. Returns 1 if found, 0 if not found and -1 if lookup failed. */ int dict_lookup(struct dict *dict, pool_t pool, const char *key, const char **value_r); void dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context); /* Iterate through all values in a path. flag indicates how iteration is carried out */ struct dict_iterate_context * dict_iterate_init(struct dict *dict, const char *path, enum dict_iterate_flags flags); struct dict_iterate_context * dict_iterate_init_multiple(struct dict *dict, const char *const *paths, enum dict_iterate_flags flags); /* Set async callback. Note that if dict_iterate_init() already did all the work, this callback may never be called. So after dict_iterate_init() you should call dict_iterate() in any case to see if all the results are already available. */ void dict_iterate_set_async_callback(struct dict_iterate_context *ctx, dict_iterate_callback_t *callback, void *context); /* Limit how many rows will be returned by the iteration (0 = unlimited). This allows backends to optimize the query (e.g. use LIMIT 1 with SQL). */ void dict_iterate_set_limit(struct dict_iterate_context *ctx, uint64_t max_rows); /* If dict_iterate() returns FALSE, the iteration may be finished or if this is an async iteration it may be waiting for more data. If this function returns TRUE, the dict callback is called again with more data. */ bool dict_iterate_has_more(struct dict_iterate_context *ctx); bool dict_iterate(struct dict_iterate_context *ctx, const char **key_r, const char **value_r); /* Returns 0 = ok, -1 = iteration failed */ int dict_iterate_deinit(struct dict_iterate_context **ctx); /* Start a new dictionary transaction. */ struct dict_transaction_context *dict_transaction_begin(struct dict *dict); /* Don't log a warning if the transaction commit took a long time. This is needed if there are no guarantees that an asynchronous commit will finish up anytime soon. Mainly useful for transactions which aren't especially important whether they finish or not. */ void dict_transaction_no_slowness_warning(struct dict_transaction_context *ctx); /* Set write timestamp for the entire transaction. This must be set before any changes are done and can't be changed afterwards. Currently only dict-sql with Cassandra backend does anything with this. */ void dict_transaction_set_timestamp(struct dict_transaction_context *ctx, const struct timespec *ts); /* Commit the transaction. Returns 1 if ok, 0 if dict_atomic_inc() was used on a nonexistent key, -1 if failed. */ int dict_transaction_commit(struct dict_transaction_context **ctx); /* Commit the transaction, but don't wait to see if it finishes successfully. If callback isn't NULL, it's called eventually. If it's not called by the time you want to deinitialize dict, call dict_flush() to wait for the result. */ void dict_transaction_commit_async(struct dict_transaction_context **ctx, dict_transaction_commit_callback_t *callback, void *context) ATTR_NULL(2, 3); /* Rollback all changes made in transaction. */ void dict_transaction_rollback(struct dict_transaction_context **ctx); /* Set key=value in dictionary. */ void dict_set(struct dict_transaction_context *ctx, const char *key, const char *value); /* Unset a record in dictionary, identified by key*/ void dict_unset(struct dict_transaction_context *ctx, const char *key); /* Append to an existing key in dictionary. Preferably an atomic operation. */ void dict_append(struct dict_transaction_context *ctx, const char *key, const char *value); /* Increase/decrease a numeric value in dictionary. Note that the value is changed when transaction is being committed, so you can't know beforehand what the value will become. The value is updated only if it already exists, otherwise commit() will return 0. */ void dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); /* Escape/unescape '/' characters in a string, so that it can be safely added into path components in dict keys. */ const char *dict_escape_string(const char *str); const char *dict_unescape_string(const char *str); #endif dovecot-2.2.33.2/src/lib-dict/dict-client.h0000644000175000017500000000303513165463624015166 00000000000000#ifndef DICT_CLIENT_H #define DICT_CLIENT_H #include "dict.h" #define DEFAULT_DICT_SERVER_SOCKET_FNAME "dict" #define DICT_CLIENT_PROTOCOL_MAJOR_VERSION 2 #define DICT_CLIENT_PROTOCOL_MINOR_VERSION 2 #define DICT_CLIENT_PROTOCOL_VERSION_MIN_MULTI_OK 2 #define DICT_CLIENT_MAX_LINE_LENGTH (64*1024) enum dict_protocol_cmd { /* */ DICT_PROTOCOL_CMD_HELLO = 'H', DICT_PROTOCOL_CMD_LOOKUP = 'L', /* */ DICT_PROTOCOL_CMD_ITERATE = 'I', /* */ DICT_PROTOCOL_CMD_BEGIN = 'B', /* */ DICT_PROTOCOL_CMD_COMMIT = 'C', /* */ DICT_PROTOCOL_CMD_COMMIT_ASYNC = 'D', /* */ DICT_PROTOCOL_CMD_ROLLBACK = 'R', /* */ DICT_PROTOCOL_CMD_SET = 'S', /* */ DICT_PROTOCOL_CMD_UNSET = 'U', /* */ DICT_PROTOCOL_CMD_APPEND = 'P', /* */ DICT_PROTOCOL_CMD_ATOMIC_INC = 'A', /* */ DICT_PROTOCOL_CMD_TIMESTAMP = 'T', /* */ }; enum dict_protocol_reply { DICT_PROTOCOL_REPLY_ERROR = -1, DICT_PROTOCOL_REPLY_OK = 'O', /* */ DICT_PROTOCOL_REPLY_MULTI_OK = 'M', /* protocol v2.2+ */ DICT_PROTOCOL_REPLY_NOTFOUND = 'N', DICT_PROTOCOL_REPLY_FAIL = 'F', DICT_PROTOCOL_REPLY_WRITE_UNCERTAIN = 'W', DICT_PROTOCOL_REPLY_ASYNC_COMMIT = 'A', DICT_PROTOCOL_REPLY_ITER_FINISHED = '\0', DICT_PROTOCOL_REPLY_ASYNC_ID = '*', DICT_PROTOCOL_REPLY_ASYNC_REPLY = '+', }; const char *dict_client_escape(const char *src); const char *dict_client_unescape(const char *src); #endif dovecot-2.2.33.2/src/lib-dict/dict-fail.c0000644000175000017500000000624513165463624014624 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dict.h" #include "dict-private.h" struct dict_iterate_context dict_iter_unsupported = { .dict = &dict_driver_fail, }; struct dict_transaction_context dict_transaction_unsupported = { .dict = &dict_driver_fail, }; static int dict_fail_init(struct dict *dict_driver ATTR_UNUSED, const char *uri ATTR_UNUSED, const struct dict_settings *set ATTR_UNUSED, struct dict **dict_r ATTR_UNUSED, const char **error_r) { *error_r = "Unsupported operation (dict does not support this feature)"; return -1; } static void dict_fail_deinit(struct dict *dict ATTR_UNUSED) { } static int dict_fail_wait(struct dict *dict ATTR_UNUSED) { return -1; } static int dict_fail_lookup(struct dict *dict ATTR_UNUSED, pool_t pool ATTR_UNUSED, const char *key ATTR_UNUSED, const char **value_r ATTR_UNUSED) { return -1; } static struct dict_iterate_context * dict_fail_iterate_init(struct dict *dict ATTR_UNUSED, const char *const *paths ATTR_UNUSED, enum dict_iterate_flags flags ATTR_UNUSED) { return &dict_iter_unsupported; } static bool dict_fail_iterate(struct dict_iterate_context *ctx ATTR_UNUSED, const char **key_r ATTR_UNUSED, const char **value_r ATTR_UNUSED) { return FALSE; } static int dict_fail_iterate_deinit(struct dict_iterate_context *ctx ATTR_UNUSED) { return -1; } static struct dict_transaction_context *dict_fail_transaction_init(struct dict *dict ATTR_UNUSED) { return &dict_transaction_unsupported; } static int dict_fail_transaction_commit(struct dict_transaction_context *ctx ATTR_UNUSED, bool async ATTR_UNUSED, dict_transaction_commit_callback_t *callback, void *context) { if (callback != NULL) callback(DICT_COMMIT_RET_FAILED, context); return -1; } static void dict_fail_transaction_rollback(struct dict_transaction_context *ctx ATTR_UNUSED) { } static void dict_fail_set(struct dict_transaction_context *ctx ATTR_UNUSED, const char *key ATTR_UNUSED, const char *value ATTR_UNUSED) { } static void dict_fail_unset(struct dict_transaction_context *ctx ATTR_UNUSED, const char *key ATTR_UNUSED) { } static void dict_fail_atomic_inc(struct dict_transaction_context *ctx ATTR_UNUSED, const char *key ATTR_UNUSED, long long diff ATTR_UNUSED) { } static bool dict_fail_switch_ioloop(struct dict *dict ATTR_UNUSED) { return TRUE; } static void dict_fail_set_timestamp(struct dict_transaction_context *ctx ATTR_UNUSED, const struct timespec *ts ATTR_UNUSED) { } struct dict dict_driver_fail = { .name = "fail", .v = { .init = dict_fail_init, .deinit = dict_fail_deinit, .wait = dict_fail_wait, .lookup = dict_fail_lookup, .iterate_init = dict_fail_iterate_init, .iterate = dict_fail_iterate, .iterate_deinit = dict_fail_iterate_deinit, .transaction_init = dict_fail_transaction_init, .transaction_commit = dict_fail_transaction_commit, .transaction_rollback = dict_fail_transaction_rollback, .set = dict_fail_set, .unset = dict_fail_unset, .atomic_inc = dict_fail_atomic_inc, .lookup_async = NULL, .switch_ioloop = dict_fail_switch_ioloop, .set_timestamp = dict_fail_set_timestamp }, }; dovecot-2.2.33.2/src/lib-dict/test-dict.c0000644000175000017500000000210513165463624014657 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dict-private.h" #include "test-common.h" struct dict dict_driver_client; struct dict dict_driver_file; struct dict dict_driver_memcached; struct dict dict_driver_memcached_ascii; struct dict dict_driver_redis; static void test_dict_escape(void) { static const char *input[] = { "", "", "foo", "foo", "foo\\", "foo\\\\", "foo\\bar", "foo\\\\bar", "\\bar", "\\\\bar", "foo/", "foo\\|", "foo/bar", "foo\\|bar", "/bar", "\\|bar", "////", "\\|\\|\\|\\|", "/", "\\|" }; unsigned int i; test_begin("dict escape"); for (i = 0; i < N_ELEMENTS(input); i += 2) { test_assert(strcmp(dict_escape_string(input[i]), input[i+1]) == 0); test_assert(strcmp(dict_unescape_string(input[i+1]), input[i]) == 0); } test_assert(strcmp(dict_unescape_string("x\\"), "x") == 0); test_assert(strcmp(dict_unescape_string("\\"), "") == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_dict_escape, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-dict/dict-sql-settings.c0000644000175000017500000002157213165463624016346 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "settings.h" #include "dict-sql-settings.h" #include enum section_type { SECTION_ROOT = 0, SECTION_MAP, SECTION_FIELDS }; struct dict_sql_map_field { struct dict_sql_field sql_field; const char *variable; }; struct setting_parser_ctx { pool_t pool; struct dict_sql_settings *set; enum section_type type; struct dict_sql_map cur_map; ARRAY(struct dict_sql_map_field) cur_fields; }; #define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map) static const struct setting_def dict_sql_map_setting_defs[] = { DEF_STR(pattern), DEF_STR(table), DEF_STR(username_field), DEF_STR(value_field), DEF_STR(value_type), DEF_BOOL(value_hexblob), { 0, NULL, 0 } }; struct dict_sql_settings_cache { pool_t pool; const char *path; struct dict_sql_settings *set; }; static HASH_TABLE(const char *, struct dict_sql_settings_cache *) dict_sql_settings_cache; static const char *pattern_read_name(const char **pattern) { const char *p = *pattern, *name; if (*p == '{') { /* ${name} */ name = ++p; p = strchr(p, '}'); if (p == NULL) { /* error, but allow anyway */ *pattern += strlen(*pattern); return ""; } *pattern = p + 1; } else { /* $name - ends at the first non-alnum_ character */ name = p; for (; *p != '\0'; p++) { if (!i_isalnum(*p) && *p != '_') break; } *pattern = p; } name = t_strdup_until(name, p); return name; } static const char *dict_sql_fields_map(struct setting_parser_ctx *ctx) { struct dict_sql_map_field *fields; string_t *pattern; const char *p, *name; unsigned int i, count; /* go through the variables in the pattern, replace them with plain '$' character and add its sql field */ pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1); fields = array_get_modifiable(&ctx->cur_fields, &count); p_array_init(&ctx->cur_map.sql_fields, ctx->pool, count); for (p = ctx->cur_map.pattern; *p != '\0';) { if (*p != '$') { str_append_c(pattern, *p); p++; continue; } p++; str_append_c(pattern, '$'); name = pattern_read_name(&p); for (i = 0; i < count; i++) { if (fields[i].variable != NULL && strcmp(fields[i].variable, name) == 0) break; } if (i == count) { return t_strconcat("Missing SQL field for variable: ", name, NULL); } /* mark this field as used */ fields[i].variable = NULL; array_append(&ctx->cur_map.sql_fields, &fields[i].sql_field, 1); } /* make sure there aren't any unused fields */ for (i = 0; i < count; i++) { if (fields[i].variable != NULL) { return t_strconcat("Unused variable: ", fields[i].variable, NULL); } } if (ctx->set->max_field_count < count) ctx->set->max_field_count = count; ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern)); return NULL; } static bool dict_sql_value_type_parse(const char *value_type, enum dict_sql_type *type_r) { if (strcmp(value_type, "string") == 0) *type_r = DICT_SQL_TYPE_STRING; else if (strcmp(value_type, "hexblob") == 0) *type_r = DICT_SQL_TYPE_HEXBLOB; else if (strcmp(value_type, "int") == 0) *type_r = DICT_SQL_TYPE_INT; else if (strcmp(value_type, "uint") == 0) *type_r = DICT_SQL_TYPE_UINT; else return FALSE; return TRUE; } static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx) { unsigned int i; if (ctx->cur_map.pattern == NULL) return "Missing setting: pattern"; if (ctx->cur_map.table == NULL) return "Missing setting: table"; if (ctx->cur_map.value_field == NULL) return "Missing setting: value_field"; ctx->cur_map.value_fields = (const char *const *) p_strsplit_spaces(ctx->pool, ctx->cur_map.value_field, ","); ctx->cur_map.values_count = str_array_length(ctx->cur_map.value_fields); enum dict_sql_type *value_types = p_new(ctx->pool, enum dict_sql_type, ctx->cur_map.values_count); if (ctx->cur_map.value_type != NULL) { const char *const *types = t_strsplit_spaces(ctx->cur_map.value_type, ","); if (str_array_length(types) != ctx->cur_map.values_count) return "Number of fields in value_fields doesn't match value_type"; for (i = 0; i < ctx->cur_map.values_count; i++) { if (!dict_sql_value_type_parse(types[i], &value_types[i])) return "Invalid value in value_type"; } } else { for (i = 0; i < ctx->cur_map.values_count; i++) { value_types[i] = ctx->cur_map.value_hexblob ? DICT_SQL_TYPE_HEXBLOB : DICT_SQL_TYPE_STRING; } } ctx->cur_map.value_types = value_types; if (ctx->cur_map.username_field == NULL) { /* not all queries require this */ ctx->cur_map.username_field = "'username_field not set'"; } if (!array_is_created(&ctx->cur_map.sql_fields)) { /* no fields besides value. allocate the array anyway. */ p_array_init(&ctx->cur_map.sql_fields, ctx->pool, 1); if (strchr(ctx->cur_map.pattern, '$') != NULL) return "Missing fields for pattern variables"; } array_append(&ctx->set->maps, &ctx->cur_map, 1); i_zero(&ctx->cur_map); return NULL; } static const char * parse_setting(const char *key, const char *value, struct setting_parser_ctx *ctx) { struct dict_sql_map_field *field; size_t value_len; switch (ctx->type) { case SECTION_ROOT: if (strcmp(key, "connect") == 0) { ctx->set->connect = p_strdup(ctx->pool, value); return NULL; } break; case SECTION_MAP: return parse_setting_from_defs(ctx->pool, dict_sql_map_setting_defs, &ctx->cur_map, key, value); case SECTION_FIELDS: if (*value != '$') { return t_strconcat("Value is missing '$' for field: ", key, NULL); } field = array_append_space(&ctx->cur_fields); field->sql_field.name = p_strdup(ctx->pool, key); value_len = strlen(value); if (strncmp(value, "${hexblob:", 10) == 0 && value[value_len-1] == '}') { field->variable = p_strndup(ctx->pool, value + 10, value_len-10-1); field->sql_field.value_type = DICT_SQL_TYPE_HEXBLOB; } else if (strncmp(value, "${int:", 6) == 0 && value[value_len-1] == '}') { field->variable = p_strndup(ctx->pool, value + 6, value_len-6-1); field->sql_field.value_type = DICT_SQL_TYPE_INT; } else if (strncmp(value, "${uint:", 7) == 0 && value[value_len-1] == '}') { field->variable = p_strndup(ctx->pool, value + 7, value_len-7-1); field->sql_field.value_type = DICT_SQL_TYPE_UINT; } else { field->variable = p_strdup(ctx->pool, value + 1); } return NULL; } return t_strconcat("Unknown setting: ", key, NULL); } static bool parse_section(const char *type, const char *name ATTR_UNUSED, struct setting_parser_ctx *ctx, const char **error_r) { switch (ctx->type) { case SECTION_ROOT: if (type == NULL) return FALSE; if (strcmp(type, "map") == 0) { array_clear(&ctx->cur_fields); ctx->type = SECTION_MAP; return TRUE; } break; case SECTION_MAP: if (type == NULL) { ctx->type = SECTION_ROOT; *error_r = dict_sql_map_finish(ctx); return FALSE; } if (strcmp(type, "fields") == 0) { ctx->type = SECTION_FIELDS; return TRUE; } break; case SECTION_FIELDS: if (type == NULL) { ctx->type = SECTION_MAP; *error_r = dict_sql_fields_map(ctx); return FALSE; } break; } *error_r = t_strconcat("Unknown section: ", type, NULL); return FALSE; } struct dict_sql_settings * dict_sql_settings_read(const char *path, const char **error_r) { struct setting_parser_ctx ctx; struct dict_sql_settings_cache *cache; pool_t pool; if (!hash_table_is_created(dict_sql_settings_cache)) { hash_table_create(&dict_sql_settings_cache, default_pool, 0, str_hash, strcmp); } cache = hash_table_lookup(dict_sql_settings_cache, path); if (cache != NULL) return cache->set; i_zero(&ctx); pool = pool_alloconly_create("dict sql settings", 1024); ctx.pool = pool; ctx.set = p_new(pool, struct dict_sql_settings, 1); t_array_init(&ctx.cur_fields, 16); p_array_init(&ctx.set->maps, pool, 8); if (!settings_read(path, NULL, parse_setting, parse_section, &ctx, error_r)) { pool_unref(&pool); return NULL; } if (ctx.set->connect == NULL) { *error_r = t_strdup_printf("Error in configuration file %s: " "Missing connect setting", path); pool_unref(&pool); return NULL; } cache = p_new(pool, struct dict_sql_settings_cache, 1); cache->pool = pool; cache->path = p_strdup(pool, path); cache->set = ctx.set; hash_table_insert(dict_sql_settings_cache, cache->path, cache); return ctx.set; } void dict_sql_settings_deinit(void) { struct hash_iterate_context *iter; struct dict_sql_settings_cache *cache; const char *key; if (!hash_table_is_created(dict_sql_settings_cache)) return; iter = hash_table_iterate_init(dict_sql_settings_cache); while (hash_table_iterate(iter, dict_sql_settings_cache, &key, &cache)) pool_unref(&cache->pool); hash_table_iterate_deinit(&iter); hash_table_destroy(&dict_sql_settings_cache); } dovecot-2.2.33.2/src/lib-dict/Makefile.am0000644000175000017500000000425713165463624014661 00000000000000noinst_LTLIBRARIES = libdict.la noinst_LIBRARIES = libdict_backend.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ $(SQL_CFLAGS) base_sources = \ dict.c \ dict-client.c \ dict-file.c \ dict-memcached.c \ dict-memcached-ascii.c \ dict-redis.c \ dict-fail.c \ dict-transaction-memory.c libdict_la_SOURCES = \ $(base_sources) libdict_backend_a_SOURCES = \ dict-db.c \ dict-cdb.c \ dict-sql.c \ dict-sql-settings.c nodist_libdict_backend_a_SOURCES = \ dict-drivers-register.c headers = \ dict.h \ dict-client.h \ dict-private.h \ dict-sql.h \ dict-sql-settings.h \ dict-transaction-memory.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dict-drivers-register.c: Makefile $(top_builddir)/config.h rm -f $@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "dict.h"' >>$@ echo '#include "dict-sql.h"' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "extern struct dict dict_driver_$${i};" >>$@ ; \ fi; \ done echo 'void dict_drivers_register_all(void) {' >>$@ echo 'dict_drivers_register_builtin();' >>$@ echo 'dict_sql_register();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "dict_driver_register(&dict_driver_$${i});" >>$@ ; \ fi; \ done echo '}' >>$@ echo 'void dict_drivers_unregister_all(void) {' >>$@ echo 'dict_drivers_unregister_builtin();' >>$@ echo 'dict_sql_unregister();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ echo "dict_driver_unregister(&dict_driver_$${i});" >>$@ ; \ fi; \ done echo '}' >>$@ distclean-generic: rm -f Makefile dict-drivers-register.c test_programs = \ test-dict noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_dict_SOURCES = test-dict.c test_dict_LDADD = libdict.la $(test_libs) test_dict_DEPENDENCIES = $(noinst_LTLIBRARIES) $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/plugins/0002755000175000017500000000000013172375614012670 500000000000000dovecot-2.2.33.2/src/plugins/push-notification/0002755000175000017500000000000013172375613016332 500000000000000dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messagetrash.h0000644000175000017500000000050713123174404025660 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGETRASH_H #define PUSH_NOTIFICATION_EVENT_MESSAGETRASH_H struct push_notification_event_messagetrash_data { /* Can only be true. */ bool trash; }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGETRASH_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-driver-dlog.c0000644000175000017500000000701713123174404024267 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-txn-mbox.h" #include "push-notification-txn-msg.h" static int push_notification_driver_dlog_init(struct push_notification_driver_config *config, struct mail_user *user ATTR_UNUSED, pool_t pool ATTR_UNUSED, void **context ATTR_UNUSED, const char **error_r ATTR_UNUSED) { i_debug("Called init push_notification plugin hook."); if (config->raw_config != NULL) { i_debug("Config string for dlog push_notification driver: %s", config->raw_config); } return 0; } static bool push_notification_driver_dlog_begin_txn (struct push_notification_driver_txn *dtxn) { const struct push_notification_event *const *event; i_debug("Called begin_txn push_notification plugin hook."); array_foreach(&push_notification_events, event) { push_notification_event_init(dtxn, (*event)->name, NULL); } return TRUE; } static void push_notification_driver_dlog_process_mbox (struct push_notification_driver_txn *dtxn ATTR_UNUSED, struct push_notification_txn_mbox *mbox) { struct push_notification_txn_event *const *event; i_debug("Called process_mbox push_notification plugin hook."); i_debug("Mailbox data: Mailbox [%s]", mbox->mailbox); if (array_is_created(&mbox->eventdata)) { array_foreach(&mbox->eventdata, event) { if ((*event)->event->event->mbox.debug_mbox != NULL) { (*event)->event->event->mbox.debug_mbox(*event); } } } } static void push_notification_driver_dlog_process_msg (struct push_notification_driver_txn *dtxn ATTR_UNUSED, struct push_notification_txn_msg *msg) { struct push_notification_txn_event *const *event; i_debug("Called process_msg push_notification plugin hook."); i_debug("Message data: Mailbox [%s], UID [%u], UIDVALIDITY [%u]", msg->mailbox, msg->uid, msg->uid_validity); if (array_is_created(&msg->eventdata)) { array_foreach(&msg->eventdata, event) { if ((*event)->event->event->msg.debug_msg != NULL) { (*event)->event->event->msg.debug_msg(*event); } } } } static void push_notification_driver_dlog_end_txn (struct push_notification_driver_txn *dtxn ATTR_UNUSED, bool success ATTR_UNUSED) { i_debug("Called end_txn push_notification plugin hook."); } static void push_notification_driver_dlog_deinit (struct push_notification_driver_user *duser ATTR_UNUSED) { i_debug("Called deinit push_notification plugin hook."); } static void push_notification_driver_dlog_cleanup(void) { i_debug("Called cleanup push_notification plugin hook."); } /* Driver definition */ extern struct push_notification_driver push_notification_driver_dlog; struct push_notification_driver push_notification_driver_dlog = { .name = "dlog", .v = { .init = push_notification_driver_dlog_init, .begin_txn = push_notification_driver_dlog_begin_txn, .process_mbox = push_notification_driver_dlog_process_mbox, .process_msg = push_notification_driver_dlog_process_msg, .end_txn = push_notification_driver_dlog_end_txn, .deinit = push_notification_driver_dlog_deinit, .cleanup = push_notification_driver_dlog_cleanup } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageappend.c0000644000175000017500000000677313165463624026027 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "istream.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-message-common.h" #include "push-notification-event-messageappend.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "MessageAppend" static struct push_notification_event_messageappend_config default_config; static void *push_notification_event_messageappend_default_config(void) { i_zero(&default_config); return &default_config; } static void push_notification_event_messageappend_debug_msg (struct push_notification_txn_event *event) { struct push_notification_event_messageappend_data *data = event->data; if (data->from != NULL) { i_debug("%s: From [%s]", EVENT_NAME, data->from); } if (data->snippet != NULL) { i_debug("%s: Snippet [%s]", EVENT_NAME, data->snippet); } if (data->subject != NULL) { i_debug("%s: Subject [%s]", EVENT_NAME, data->subject); } if (data->to != NULL) { i_debug("%s: To [%s]", EVENT_NAME, data->to); } } static void push_notification_event_messageappend_event(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail) { struct push_notification_event_messageappend_config *config = (struct push_notification_event_messageappend_config *)ec->config; struct push_notification_event_messageappend_data *data; const char *value; if (!config->flags) { return; } data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if (data == NULL) { data = p_new(ptxn->pool, struct push_notification_event_messageappend_data, 1); push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } if ((data->to == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_TO) && (mail_get_first_header(mail, "To", &value) >= 0)) { data->to = p_strdup(ptxn->pool, value); } if ((data->from == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_FROM) && (mail_get_first_header(mail, "From", &value) >= 0)) { data->from = p_strdup(ptxn->pool, value); } if ((data->subject == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_SUBJECT) && (mail_get_first_header(mail, "Subject", &value) >= 0)) { data->subject = p_strdup(ptxn->pool, value); } if ((data->snippet == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_BODY_SNIPPET) && (mail_get_special(mail, MAIL_FETCH_BODY_SNIPPET, &value) >= 0)) { /* [0] contains the snippet algorithm, skip over it */ i_assert(value[0] != '\0'); data->snippet = p_strdup(ptxn->pool, value + 1); } } /* Event definition */ extern struct push_notification_event push_notification_event_messageappend; struct push_notification_event push_notification_event_messageappend = { .name = EVENT_NAME, .init = { .default_config = push_notification_event_messageappend_default_config }, .msg = { .debug_msg = push_notification_event_messageappend_debug_msg }, .msg_triggers = { .append = push_notification_event_messageappend_event } }; dovecot-2.2.33.2/src/plugins/push-notification/Makefile.in0000644000175000017500000007054313172375575020335 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/push-notification ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib20_push_notification_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la am_lib20_push_notification_plugin_la_OBJECTS = \ push-notification-driver-dlog.lo \ push-notification-driver-ox.lo push-notification-drivers.lo \ push-notification-event-flagsclear.lo \ push-notification-event-flagsset.lo \ push-notification-event-mailboxcreate.lo \ push-notification-event-mailboxdelete.lo \ push-notification-event-mailboxrename.lo \ push-notification-event-mailboxsubscribe.lo \ push-notification-event-mailboxunsubscribe.lo \ push-notification-event-messageappend.lo \ push-notification-event-messageexpunge.lo \ push-notification-event-messagenew.lo \ push-notification-event-messageread.lo \ push-notification-event-messagetrash.lo \ push-notification-events.lo \ push-notification-events-rfc5423.lo \ push-notification-plugin.lo push-notification-triggers.lo \ push-notification-txn-mbox.lo push-notification-txn-msg.lo lib20_push_notification_plugin_la_OBJECTS = \ $(am_lib20_push_notification_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_push_notification_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) \ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(lib20_push_notification_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_push_notification_plugin_la_SOURCES) DIST_SOURCES = $(lib20_push_notification_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/notify lib20_push_notification_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = lib20_push_notification_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib20_push_notification_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la lib20_push_notification_plugin_la_SOURCES = \ push-notification-driver-dlog.c \ push-notification-driver-ox.c \ push-notification-drivers.c \ push-notification-event-flagsclear.c \ push-notification-event-flagsset.c \ push-notification-event-mailboxcreate.c \ push-notification-event-mailboxdelete.c \ push-notification-event-mailboxrename.c \ push-notification-event-mailboxsubscribe.c \ push-notification-event-mailboxunsubscribe.c \ push-notification-event-messageappend.c \ push-notification-event-messageexpunge.c \ push-notification-event-messagenew.c \ push-notification-event-messageread.c \ push-notification-event-messagetrash.c \ push-notification-events.c \ push-notification-events-rfc5423.c \ push-notification-plugin.c \ push-notification-triggers.c \ push-notification-txn-mbox.c \ push-notification-txn-msg.c headers = \ push-notification-drivers.h \ push-notification-event-flagsclear.h \ push-notification-event-flagsset.h \ push-notification-event-mailboxcreate.h \ push-notification-event-mailboxdelete.h \ push-notification-event-mailboxrename.h \ push-notification-event-mailboxsubscribe.h \ push-notification-event-mailboxunsubscribe.h \ push-notification-event-message-common.h \ push-notification-event-messageappend.h \ push-notification-event-messageexpunge.h \ push-notification-event-messagenew.h \ push-notification-event-messageread.h \ push-notification-event-messagetrash.h \ push-notification-events.h \ push-notification-events-rfc5423.h \ push-notification-plugin.h \ push-notification-triggers.h \ push-notification-txn-mbox.h \ push-notification-txn-msg.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/push-notification/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/push-notification/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_push_notification_plugin.la: $(lib20_push_notification_plugin_la_OBJECTS) $(lib20_push_notification_plugin_la_DEPENDENCIES) $(EXTRA_lib20_push_notification_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_push_notification_plugin_la_LINK) -rpath $(moduledir) $(lib20_push_notification_plugin_la_OBJECTS) $(lib20_push_notification_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-driver-dlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-driver-ox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-drivers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-flagsclear.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-flagsset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-mailboxcreate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-mailboxdelete.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-mailboxrename.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-mailboxsubscribe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-mailboxunsubscribe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-messageappend.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-messageexpunge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-messagenew.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-messageread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-event-messagetrash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-events-rfc5423.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-events.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-triggers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-txn-mbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push-notification-txn-msg.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-flagsclear.c0000644000175000017500000001302013165463624025275 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage.h" #include "mail-types.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-flagsclear.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "FlagsClear" static struct push_notification_event_flagsclear_config default_config; static void *push_notification_event_flagsclear_default_config(void) { i_zero(&default_config); return &default_config; } static void push_notification_event_flagsclear_debug_msg (struct push_notification_txn_event *event) { struct push_notification_event_flagsclear_data *data = event->data; const char *const *keyword; if (data->flags_clear & MAIL_ANSWERED) { i_debug("%s: Answered flag cleared", EVENT_NAME); } if (data->flags_clear & MAIL_FLAGGED) { i_debug("%s: Flagged flag cleared", EVENT_NAME); } if (data->flags_clear & MAIL_DELETED) { i_debug("%s: Deleted flag cleared", EVENT_NAME); } if (data->flags_clear & MAIL_SEEN) { i_debug("%s: Seen flag cleared", EVENT_NAME); } if (data->flags_clear & MAIL_DRAFT) { i_debug("%s: Draft flag cleared", EVENT_NAME); } array_foreach(&data->keywords_clear, keyword) { i_debug("%s: Keyword clear [%s]", EVENT_NAME, *keyword); } } static struct push_notification_event_flagsclear_data * push_notification_event_flagsclear_get_data(struct push_notification_txn *ptxn, struct push_notification_txn_msg *msg, struct push_notification_event_config *ec) { struct push_notification_event_flagsclear_config *config = (struct push_notification_event_flagsclear_config *)ec->config; struct push_notification_event_flagsclear_data *data; data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if (data == NULL) { data = p_new(ptxn->pool, struct push_notification_event_flagsclear_data, 1); data->flags_clear = 0; data->flags_old = 0; p_array_init(&data->keywords_clear, ptxn->pool, 4); if (config->store_old == TRUE) { p_array_init(&data->keywords_old, ptxn->pool, 4); } push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } return data; } static void push_notification_event_flagsclear_flags_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, enum mail_flags old_flags) { struct push_notification_event_flagsclear_config *config = (struct push_notification_event_flagsclear_config *)ec->config; struct push_notification_event_flagsclear_data *data; enum mail_flags flag_check_always[] = { MAIL_ANSWERED, MAIL_DELETED, MAIL_DRAFT, MAIL_FLAGGED, MAIL_SEEN }; enum mail_flags flags; unsigned int i; data = push_notification_event_flagsclear_get_data(ptxn, msg, ec); flags = mail_get_flags(mail); for (i = 0; i < N_ELEMENTS(flag_check_always); i++) { if (!(flags & flag_check_always[i]) && (old_flags & flag_check_always[i])) { data->flags_clear |= flag_check_always[i]; } } if (config->store_old == TRUE) { data->flags_old = old_flags; } } static void push_notification_event_flagsclear_keywords_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, const char *const *old_keywords) { struct push_notification_event_flagsclear_config *config = (struct push_notification_event_flagsclear_config *)ec->config; struct push_notification_event_flagsclear_data *data; const char *const *keywords, *const *kp, *ok; data = push_notification_event_flagsclear_get_data(ptxn, msg, ec); keywords = mail_get_keywords(mail); for (; *old_keywords != NULL; old_keywords++) { for (kp = keywords; *kp != NULL; kp++) { if (strcmp(*old_keywords, *kp) == 0) { break; } } if (*kp == NULL) { ok = p_strdup(ptxn->pool, *old_keywords); array_append(&data->keywords_clear, &ok, 1); } if (config->store_old == TRUE) { ok = p_strdup(ptxn->pool, *old_keywords); array_append(&data->keywords_old, &ok, 1); } } } static void push_notification_event_flagsclear_free_msg( struct push_notification_txn_event *event) { struct push_notification_event_flagsclear_data *data = event->data; if (array_is_created(&data->keywords_clear)) { array_free(&data->keywords_clear); } if (array_is_created(&data->keywords_old)) { array_free(&data->keywords_old); } } /* Event definition */ extern struct push_notification_event push_notification_event_flagsclear; struct push_notification_event push_notification_event_flagsclear = { .name = EVENT_NAME, .init = { .default_config = push_notification_event_flagsclear_default_config }, .msg = { .debug_msg = push_notification_event_flagsclear_debug_msg, .free_msg = push_notification_event_flagsclear_free_msg }, .msg_triggers = { .flagchange = push_notification_event_flagsclear_flags_event, .keywordchange = push_notification_event_flagsclear_keywords_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageexpunge.h0000644000175000017500000000052113123174404026206 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGEEXPUNGE_H #define PUSH_NOTIFICATION_EVENT_MESSAGEEXPUNGE_H struct push_notification_event_messageexpunge_data { /* Can only be true. */ bool expunge; }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGEEXPUNGE_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageexpunge.c0000644000175000017500000000305413123174404026205 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "mail-types.h" #include "push-notification-drivers.h" #include "push-notification-event-messageexpunge.h" #include "push-notification-events.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "MessageExpunge" static void push_notification_event_messageexpunge_debug_msg (struct push_notification_txn_event *event) { struct push_notification_event_messageexpunge_data *data = event->data; if (data != NULL) { i_debug("%s: Message was expunged", EVENT_NAME); } } static void push_notification_event_messageexpunge_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg) { struct push_notification_event_messageexpunge_data *data; data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if (data == NULL) { data = p_new(ptxn->pool, struct push_notification_event_messageexpunge_data, 1); data->expunge = TRUE; push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } } /* Event definition */ extern struct push_notification_event push_notification_event_messageexpunge; struct push_notification_event push_notification_event_messageexpunge = { .name = EVENT_NAME, .msg = { .debug_msg = push_notification_event_messageexpunge_debug_msg }, .msg_triggers = { .expunge = push_notification_event_messageexpunge_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-triggers.c0000644000175000017500000001751013123174404023676 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-triggers.h" #include "push-notification-txn-mbox.h" #include "push-notification-txn-msg.h" static void push_notification_trigger_mbox_common(struct push_notification_txn *txn, struct mailbox *box, struct push_notification_txn_mbox **mbox, enum push_notification_event_trigger trigger) { if (*mbox == NULL) { *mbox = push_notification_txn_mbox_create(txn, box); } txn->trigger |= trigger; } void push_notification_trigger_mbox_create(struct push_notification_txn *txn, struct mailbox *box, struct push_notification_txn_mbox *mbox) { struct push_notification_event_config **ec; push_notification_trigger_mbox_common(txn, box, &mbox, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_CREATE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->mbox_triggers.create != NULL) { (*ec)->event->mbox_triggers.create(txn, *ec, mbox); } } } } void push_notification_trigger_mbox_delete(struct push_notification_txn *txn, struct mailbox *box, struct push_notification_txn_mbox *mbox) { struct push_notification_event_config **ec; push_notification_trigger_mbox_common(txn, box, &mbox, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_DELETE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->mbox_triggers.delete != NULL) { (*ec)->event->mbox_triggers.delete(txn, *ec, mbox); } } } } void push_notification_trigger_mbox_rename(struct push_notification_txn *txn, struct mailbox *src, struct mailbox *dest, struct push_notification_txn_mbox *mbox) { struct push_notification_event_config **ec; push_notification_trigger_mbox_common(txn, dest, &mbox, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_RENAME); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->mbox_triggers.rename != NULL) { (*ec)->event->mbox_triggers.rename(txn, *ec, mbox, src); } } } } void push_notification_trigger_mbox_subscribe(struct push_notification_txn *txn, struct mailbox *box, bool subscribed, struct push_notification_txn_mbox *mbox) { struct push_notification_event_config **ec; push_notification_trigger_mbox_common(txn, box, &mbox, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_SUBSCRIBE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if (subscribed == TRUE) { if ((*ec)->event->mbox_triggers.subscribe != NULL) { (*ec)->event->mbox_triggers.subscribe(txn, *ec, mbox); } } else { if ((*ec)->event->mbox_triggers.unsubscribe != NULL) { (*ec)->event->mbox_triggers.unsubscribe(txn, *ec, mbox); } } } } } static void push_notification_trigger_msg_common(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg **msg, enum push_notification_event_trigger trigger) { if (*msg == NULL) { *msg = push_notification_txn_msg_create(txn, mail); } txn->trigger |= trigger; } void push_notification_trigger_msg_save_new(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg) { struct push_notification_event_config **ec; push_notification_trigger_msg_common(txn, mail, &msg, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_SAVE_NEW); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->msg_triggers.save != NULL) { (*ec)->event->msg_triggers.save(txn, *ec, msg, mail); } } } } void push_notification_trigger_msg_save_append(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg) { struct push_notification_event_config **ec; push_notification_trigger_msg_common(txn, mail, &msg, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_SAVE_APPEND); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->msg_triggers.append != NULL) { (*ec)->event->msg_triggers.append(txn, *ec, msg, mail); } } } } void push_notification_trigger_msg_save_expunge(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg) { struct push_notification_event_config **ec; push_notification_trigger_msg_common(txn, mail, &msg, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_EXPUNGE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->msg_triggers.expunge != NULL) { (*ec)->event->msg_triggers.expunge(txn, *ec, msg); } } } } void push_notification_trigger_msg_flag_change(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg, enum mail_flags old_flags) { struct push_notification_event_config **ec; push_notification_trigger_msg_common(txn, mail, &msg, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_FLAGCHANGE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->msg_triggers.flagchange != NULL) { (*ec)->event->msg_triggers.flagchange(txn, *ec, msg, mail, old_flags); } } } } void push_notification_trigger_msg_keyword_change(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg, const char *const *old_keywords) { struct push_notification_event_config **ec; push_notification_trigger_msg_common(txn, mail, &msg, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_KEYWORDCHANGE); if (array_is_created(&txn->events)) { array_foreach_modifiable(&txn->events, ec) { if ((*ec)->event->msg_triggers.keywordchange != NULL) { (*ec)->event->msg_triggers.keywordchange(txn, *ec, msg, mail, old_keywords); } } } } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxcreate.c0000644000175000017500000000325013123174404026002 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-mailboxcreate.h" #include "push-notification-txn-mbox.h" #define EVENT_NAME "MailboxCreate" static void push_notification_event_mailboxcreate_debug_mbox (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Mailbox was created", EVENT_NAME); } static void push_notification_event_mailboxcreate_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox) { struct push_notification_event_mailboxcreate_data *data; struct mailbox_status status; if (mailbox_get_status(ptxn->mbox, STATUS_UIDVALIDITY, &status) < 0) { i_error(EVENT_NAME "Failed to get created mailbox '%s' uidvalidity: %s", mailbox_get_vname(ptxn->mbox), mailbox_get_last_internal_error(ptxn->mbox, NULL)); status.uidvalidity = 0; } data = p_new(ptxn->pool, struct push_notification_event_mailboxcreate_data, 1); data->uid_validity = status.uidvalidity; push_notification_txn_mbox_set_eventdata(ptxn, mbox, ec, data); } /* Event definition */ extern struct push_notification_event push_notification_event_mailboxcreate; struct push_notification_event push_notification_event_mailboxcreate = { .name = EVENT_NAME, .mbox = { .debug_mbox = push_notification_event_mailboxcreate_debug_mbox }, .mbox_triggers = { .create = push_notification_event_mailboxcreate_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-events-rfc5423.c0000644000175000017500000000437513123174404024447 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "push-notification-events.h" #include "push-notification-events-rfc5423.h" /* These are the RFC 5423 Mail Store Events currently handled within the core * push-notification code. * * @todo: These events are not currently handled: * - Login * - Logout * - QuotaExceed * - Quota Within */ extern struct push_notification_event push_notification_event_flagsclear; extern struct push_notification_event push_notification_event_flagsset; extern struct push_notification_event push_notification_event_mailboxcreate; extern struct push_notification_event push_notification_event_mailboxdelete; extern struct push_notification_event push_notification_event_mailboxrename; extern struct push_notification_event push_notification_event_mailboxsubscribe; extern struct push_notification_event push_notification_event_mailboxunsubscribe; extern struct push_notification_event push_notification_event_messageappend; extern struct push_notification_event push_notification_event_messageexpunge; extern struct push_notification_event push_notification_event_messagenew; extern struct push_notification_event push_notification_event_messageread; extern struct push_notification_event push_notification_event_messagetrash; static struct push_notification_event *rfc5423_events[] = { &push_notification_event_flagsclear, &push_notification_event_flagsset, &push_notification_event_mailboxcreate, &push_notification_event_mailboxdelete, &push_notification_event_mailboxrename, &push_notification_event_mailboxsubscribe, &push_notification_event_mailboxunsubscribe, &push_notification_event_messageappend, &push_notification_event_messageexpunge, &push_notification_event_messagenew, &push_notification_event_messageread, &push_notification_event_messagetrash }; void push_notification_event_register_rfc5423_events(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(rfc5423_events); i++) push_notification_event_register(rfc5423_events[i]); } void push_notification_event_unregister_rfc5423_events(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(rfc5423_events); i++) push_notification_event_unregister(rfc5423_events[i]); } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxrename.h0000644000175000017500000000047113123174404026015 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MAILBOXRENAME_H #define PUSH_NOTIFICATION_EVENT_MAILBOXRENAME_H struct push_notification_event_mailboxrename_data { const char *old_mbox; }; #endif /* PUSH_NOTIFICATION_EVENT_MAILBOXRENAME_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxsubscribe.h0000644000175000017500000000053313123174404026526 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MAILBOXSUBSCRIBE_H #define PUSH_NOTIFICATION_EVENT_MAILBOXSUBSCRIBE_H struct push_notification_event_mailboxsubscribe_data { /* Can only be true. */ bool subscribe; }; #endif /* PUSH_NOTIFICATION_EVENT_MAILBOXSUBSCRIBE_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-txn-msg.h0000644000175000017500000000256513123174404023456 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_TXN_MSG_H #define PUSH_NOTIFICATION_TXN_MSG_H struct mail_transaction_commit_changes; struct push_notification_event_config; struct push_notification_txn; struct push_notification_txn_event; struct push_notification_txn_msg { const char *mailbox; uint32_t uid; uint32_t uid_validity; ARRAY(struct push_notification_txn_event *) eventdata; /* Private */ unsigned int seq; }; struct push_notification_txn_msg * push_notification_txn_msg_create(struct push_notification_txn *txn, struct mail *mail); void push_notification_txn_msg_end(struct push_notification_txn *ptxn, struct mail_transaction_commit_changes *changes); void * push_notification_txn_msg_get_eventdata(struct push_notification_txn_msg *msg, const char *event_name); void push_notification_txn_msg_set_eventdata(struct push_notification_txn *txn, struct push_notification_txn_msg *msg, struct push_notification_event_config *event, void *data); void push_notification_txn_msg_deinit_eventdata(struct push_notification_txn_msg *msg); #endif /* PUSH_NOTIFICATION_TXN_MSG_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-events-rfc5423.h0000644000175000017500000000051313123174404024442 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENTS_RFC5423_H #define PUSH_NOTIFICATION_EVENTS_RFC5423_H void push_notification_event_register_rfc5423_events(void); void push_notification_event_unregister_rfc5423_events(void); #endif /* PUSH_NOTIFICATION_EVENTS_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-txn-mbox.c0000644000175000017500000000516113123174404023623 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-storage-private.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-txn-mbox.h" struct push_notification_txn_mbox * push_notification_txn_mbox_create(struct push_notification_txn *txn, struct mailbox *box) { if (txn->mbox_txn == NULL) { txn->mbox_txn = p_new(txn->pool, struct push_notification_txn_mbox, 1); txn->mbox_txn->mailbox = mailbox_get_vname(box); } return txn->mbox_txn; } void push_notification_txn_mbox_end(struct push_notification_txn *ptxn) { struct push_notification_driver_txn **dtxn; if (ptxn->mbox_txn != NULL) { array_foreach_modifiable(&ptxn->drivers, dtxn) { if ((*dtxn)->duser->driver->v.process_mbox != NULL) { (*dtxn)->duser->driver->v.process_mbox(*dtxn, ptxn->mbox_txn); } } push_notification_txn_mbox_deinit_eventdata(ptxn->mbox_txn); } } void * push_notification_txn_mbox_get_eventdata(struct push_notification_txn_mbox *mbox, const char *event_name) { struct push_notification_txn_event **mevent; if (array_is_created(&mbox->eventdata)) { array_foreach_modifiable(&mbox->eventdata, mevent) { if (strcmp((*mevent)->event->event->name, event_name) == 0) { return (*mevent)->data; } } } return NULL; } void push_notification_txn_mbox_set_eventdata(struct push_notification_txn *txn, struct push_notification_txn_mbox *mbox, struct push_notification_event_config *event, void *data) { struct push_notification_txn_event *mevent; if (!array_is_created(&mbox->eventdata)) { p_array_init(&mbox->eventdata, txn->pool, 4); } mevent = p_new(txn->pool, struct push_notification_txn_event, 1); mevent->data = data; mevent->event = event; array_append(&mbox->eventdata, &mevent, 1); } void push_notification_txn_mbox_deinit_eventdata (struct push_notification_txn_mbox *mbox) { struct push_notification_txn_event **mevent; if (array_is_created(&mbox->eventdata)) { array_foreach_modifiable(&mbox->eventdata, mevent) { if (((*mevent)->data != NULL) && ((*mevent)->event->event->mbox.free_mbox != NULL)) { (*mevent)->event->event->mbox.free_mbox(*mevent); } } } } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-txn-mbox.h0000644000175000017500000000217213123174404023627 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_TXN_MBOX_H #define PUSH_NOTIFICATION_TXN_MBOX_H struct push_notification_txn_event; struct push_notification_txn_mbox { const char *mailbox; ARRAY(struct push_notification_txn_event *) eventdata; }; struct push_notification_txn_mbox * push_notification_txn_mbox_create(struct push_notification_txn *txn, struct mailbox *box); void push_notification_txn_mbox_end(struct push_notification_txn *ptxn); void * push_notification_txn_mbox_get_eventdata(struct push_notification_txn_mbox *mbox, const char *event_name); void push_notification_txn_mbox_set_eventdata(struct push_notification_txn *txn, struct push_notification_txn_mbox *mbox, struct push_notification_event_config *event, void *data); void push_notification_txn_mbox_deinit_eventdata(struct push_notification_txn_mbox *mbox); #endif /* PUSH_NOTIFICATION_TXN_MBOX_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-flagsclear.h0000644000175000017500000000106113123174404025271 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_FLAGSCLEAR_H #define PUSH_NOTIFICATION_EVENT_FLAGSCLEAR_H #include "mail-types.h" struct push_notification_event_flagsclear_config { /* Store the old flags/keywords? */ bool store_old; }; struct push_notification_event_flagsclear_data { enum mail_flags flags_clear; ARRAY_TYPE(keywords) keywords_clear; enum mail_flags flags_old; ARRAY_TYPE(keywords) keywords_old; }; #endif /* PUSH_NOTIFICATION_EVENT_FLAGSCLEAR_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-flagsset.c0000644000175000017500000001205513165463624025011 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage.h" #include "mail-types.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-flagsset.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "FlagsSet" static struct push_notification_event_flagsset_config default_config; static void *push_notification_event_flagsset_default_config(void) { i_zero(&default_config); return &default_config; } static void push_notification_event_flagsset_debug_msg (struct push_notification_txn_event *event) { struct push_notification_event_flagsset_data *data = event->data; const char *const *keyword; if (data->flags_set & MAIL_ANSWERED) { i_debug("%s: Answered flag set", EVENT_NAME); } if (data->flags_set & MAIL_FLAGGED) { i_debug("%s: Flagged flag set", EVENT_NAME); } if (data->flags_set & MAIL_DELETED) { i_debug("%s: Deleted flag set", EVENT_NAME); } if (data->flags_set & MAIL_SEEN) { i_debug("%s: Seen flag set", EVENT_NAME); } if (data->flags_set & MAIL_DRAFT) { i_debug("%s: Draft flag set", EVENT_NAME); } array_foreach(&data->keywords_set, keyword) { i_debug("%s: Keyword set [%s]", EVENT_NAME, *keyword); } } static struct push_notification_event_flagsset_data * push_notification_event_flagsset_get_data(struct push_notification_txn *ptxn, struct push_notification_txn_msg *msg, struct push_notification_event_config *ec) { struct push_notification_event_flagsset_data *data; data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if (data == NULL) { data = p_new(ptxn->pool, struct push_notification_event_flagsset_data, 1); data->flags_set = 0; p_array_init(&data->keywords_set, ptxn->pool, 4); push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } return data; } static void push_notification_event_flagsset_flags_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, enum mail_flags old_flags) { struct push_notification_event_flagsset_config *config = (struct push_notification_event_flagsset_config *)ec->config; struct push_notification_event_flagsset_data *data; enum mail_flags flag_check_always[] = { MAIL_ANSWERED, MAIL_DRAFT, MAIL_FLAGGED }; enum mail_flags flags, flags_set = 0; unsigned int i; flags = mail_get_flags(mail); for (i = 0; i < N_ELEMENTS(flag_check_always); i++) { if ((flags & flag_check_always[i]) && !(old_flags & flag_check_always[i])) { flags_set |= flag_check_always[i]; } } if (!config->hide_deleted && (flags & MAIL_DELETED) && !(old_flags & MAIL_DELETED)) { flags_set |= MAIL_DELETED; } if (!config->hide_seen && (flags & MAIL_SEEN) && !(old_flags & MAIL_SEEN)) { flags_set |= MAIL_SEEN; } /* Only create data element if at least one flag was set. */ if (flags_set) { data = push_notification_event_flagsset_get_data(ptxn, msg, ec); data->flags_set |= flags_set; } } static void push_notification_event_flagsset_keywords_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, const char *const *old_keywords) { struct push_notification_event_flagsset_data *data; const char *k, *const *keywords, *const *op; data = push_notification_event_flagsset_get_data(ptxn, msg, ec); keywords = mail_get_keywords(mail); for (; *keywords != NULL; keywords++) { for (op = old_keywords; *op != NULL; op++) { if (strcmp(*keywords, *op) == 0) { break; } } if (*op == NULL) { k = p_strdup(ptxn->pool, *keywords); array_append(&data->keywords_set, &k, 1); } } } static void push_notification_event_flagsset_free_msg( struct push_notification_txn_event *event) { struct push_notification_event_flagsset_data *data = event->data; if (array_is_created(&data->keywords_set)) { array_free(&data->keywords_set); } } /* Event definition */ extern struct push_notification_event push_notification_event_flagsset; struct push_notification_event push_notification_event_flagsset = { .name = EVENT_NAME, .init = { .default_config = push_notification_event_flagsset_default_config }, .msg = { .debug_msg = push_notification_event_flagsset_debug_msg, .free_msg = push_notification_event_flagsset_free_msg }, .msg_triggers = { .flagchange = push_notification_event_flagsset_flags_event, .keywordchange = push_notification_event_flagsset_keywords_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-events.c0000644000175000017500000000532313123174404023353 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "push-notification-drivers.h" #include "push-notification-events.h" ARRAY_TYPE(push_notification_event) push_notification_events; static bool push_notification_event_find(const char *name, unsigned int *idx_r) { unsigned int count, i; const struct push_notification_event *const *events; events = array_get(&push_notification_events, &count); for (i = 0; i < count; i++) { if (strcasecmp(events[i]->name, name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } static const struct push_notification_event * push_notification_event_find_class(const char *driver) { const struct push_notification_event *const *class_p; unsigned int idx; if (!push_notification_event_find(driver, &idx)) { return NULL; } class_p = array_idx(&push_notification_events, idx); return *class_p; } void push_notification_event_init(struct push_notification_driver_txn *dtxn, const char *event_name, void *config) { const struct push_notification_event *event; struct push_notification_event_config *ec; if (!array_is_created(&dtxn->ptxn->events)) { p_array_init(&dtxn->ptxn->events, dtxn->ptxn->pool, 4); } event = push_notification_event_find_class(event_name); if (event != NULL) { if ((config == NULL) && (event->init.default_config != NULL)) { config = event->init.default_config(); } ec = p_new(dtxn->ptxn->pool, struct push_notification_event_config, 1); ec->config = config; ec->event = event; array_append(&dtxn->ptxn->events, &ec, 1); } } void push_notification_event_register (const struct push_notification_event *event) { unsigned int idx; if (!array_is_created(&push_notification_events)) { i_array_init(&push_notification_events, 16); } if (push_notification_event_find(event->name, &idx)) { i_panic("push_notification_event_register(%s): duplicate event", event->name); } array_append(&push_notification_events, &event, 1); } void push_notification_event_unregister (const struct push_notification_event *event) { unsigned int idx; if (!push_notification_event_find(event->name, &idx)) { i_panic("push_notification_event_register(%s): unknown event", event->name); } if (array_is_created(&push_notification_events)) { array_delete(&push_notification_events, idx, 1); if (array_is_empty(&push_notification_events)) { array_free(&push_notification_events); } } } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-plugin.h0000644000175000017500000000052113123174404023345 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_PLUGIN_H #define PUSH_NOTIFICATION_PLUGIN_H extern const char *push_notification_plugin_dependencies[]; struct module; void push_notification_plugin_init(struct module *module); void push_notification_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/push-notification/push-notification-txn-msg.c0000644000175000017500000001015613123174404023444 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-storage-private.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-txn-msg.h" struct push_notification_txn_msg * push_notification_txn_msg_create(struct push_notification_txn *txn, struct mail *mail) { struct push_notification_txn_msg *msg = NULL; if (hash_table_is_created(txn->messages)) { msg = hash_table_lookup(txn->messages, POINTER_CAST(txn->t->save_count + 1)); } else { hash_table_create_direct(&txn->messages, txn->pool, 4); } if (msg == NULL) { msg = p_new(txn->pool, struct push_notification_txn_msg, 1); msg->mailbox = mailbox_get_vname(mail->box); /* Save sequence number - used to determine UID later. */ msg->seq = txn->t->save_count; msg->uid = mail->uid; hash_table_insert(txn->messages, POINTER_CAST(txn->t->save_count + 1), msg); } return msg; } void push_notification_txn_msg_end(struct push_notification_txn *ptxn, struct mail_transaction_commit_changes *changes) { struct hash_iterate_context *hiter; void *key; struct push_notification_driver_txn **dtxn; struct seq_range_iter siter; struct mailbox_status status; uint32_t uid, uid_validity; struct push_notification_txn_msg *value; if (!hash_table_is_created(ptxn->messages)) { return; } hiter = hash_table_iterate_init(ptxn->messages); seq_range_array_iter_init(&siter, &changes->saved_uids); /* uid_validity is only set in changes if message is new. */ if (changes->uid_validity == 0) { mailbox_get_open_status(ptxn->mbox, STATUS_UIDVALIDITY, &status); uid_validity = status.uidvalidity; } else { uid_validity = changes->uid_validity; } while (hash_table_iterate(hiter, ptxn->messages, &key, &value)) { if (value->uid == 0) { if (seq_range_array_iter_nth(&siter, value->seq, &uid)) { value->uid = uid; } } value->uid_validity = uid_validity; array_foreach_modifiable(&ptxn->drivers, dtxn) { if ((*dtxn)->duser->driver->v.process_msg != NULL) { (*dtxn)->duser->driver->v.process_msg(*dtxn, value); } } push_notification_txn_msg_deinit_eventdata(value); } hash_table_iterate_deinit(&hiter); hash_table_destroy(&ptxn->messages); } void * push_notification_txn_msg_get_eventdata(struct push_notification_txn_msg *msg, const char *event_name) { struct push_notification_txn_event **mevent; if (array_is_created(&msg->eventdata)) { array_foreach_modifiable(&msg->eventdata, mevent) { if (strcmp((*mevent)->event->event->name, event_name) == 0) { return (*mevent)->data; } } } return NULL; } void push_notification_txn_msg_set_eventdata(struct push_notification_txn *txn, struct push_notification_txn_msg *msg, struct push_notification_event_config *event, void *data) { struct push_notification_txn_event *mevent; if (!array_is_created(&msg->eventdata)) { p_array_init(&msg->eventdata, txn->pool, 4); } mevent = p_new(txn->pool, struct push_notification_txn_event, 1); mevent->data = data; mevent->event = event; array_append(&msg->eventdata, &mevent, 1); } void push_notification_txn_msg_deinit_eventdata(struct push_notification_txn_msg *msg) { struct push_notification_txn_event **mevent; if (array_is_created(&msg->eventdata)) { array_foreach_modifiable(&msg->eventdata, mevent) { if (((*mevent)->data != NULL) && ((*mevent)->event->event->msg.free_msg != NULL)) { (*mevent)->event->event->msg.free_msg(*mevent); } } } } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-drivers.c0000644000175000017500000001171713165463624023544 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "mail-user.h" #include "push-notification-drivers.h" #include "push-notification-events.h" static ARRAY(const struct push_notification_driver *) push_notification_drivers; static bool push_notification_driver_find(const char *name, unsigned int *idx_r) { unsigned int count, i; const struct push_notification_driver *const *drivers; drivers = array_get(&push_notification_drivers, &count); for (i = 0; i < count; i++) { if (strcasecmp(drivers[i]->name, name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } static const struct push_notification_driver * push_notification_driver_find_class(const char *driver) { const struct push_notification_driver *const *class_p; unsigned int idx; if (!push_notification_driver_find(driver, &idx)) { return NULL; } class_p = array_idx(&push_notification_drivers, idx); return *class_p; } static struct push_notification_driver_config * push_notification_driver_parse_config(const char *p) { const char **args, *key, *p2, *value; struct push_notification_driver_config *config; config = t_new(struct push_notification_driver_config, 1); config->raw_config = p; hash_table_create(&config->config, unsafe_data_stack_pool, 0, str_hash, strcmp); if (p == NULL) { return config; } args = t_strsplit_spaces(p, " "); for (; *args != NULL; args++) { p2 = strchr(*args, '='); if (p2 != NULL) { key = t_strdup_until(*args, p2); value = t_strdup(p2 + 1); } else { key = *args; value = ""; } hash_table_insert(config->config, key, value); } return config; } int push_notification_driver_init(struct mail_user *user, const char *config_in, pool_t pool, struct push_notification_driver_user **duser_r) { void *context = NULL; const struct push_notification_driver *driver; const char *driver_name, *error_r, *p; struct push_notification_driver_user *duser; int ret; /* [:] */ p = strchr(config_in, ':'); if (p == NULL) { driver_name = config_in; } else { driver_name = t_strdup_until(config_in, p); } driver = push_notification_driver_find_class(driver_name); if (driver == NULL) { i_error("Unknown push notification driver: %s", driver_name); return -1; } if (driver->v.init != NULL) { T_BEGIN { struct push_notification_driver_config *config; config = push_notification_driver_parse_config( (p == NULL) ? p : p + 1); ret = driver->v.init(config, user, pool, &context, &error_r); hash_table_destroy(&config->config); } T_END; if (ret < 0) { i_error("%s: %s", driver_name, error_r); return -1; } } duser = p_new(pool, struct push_notification_driver_user, 1); duser->context = context; duser->driver = driver; *duser_r = duser; return 0; } void push_notification_driver_cleanup_all(void) { const struct push_notification_driver *const *driver; /* Loop through driver list and perform global cleanup tasks. We may not * have used all drivers in this plugin/worker, but the cleanup hooks are * designed to ignore these unused drivers. */ array_foreach(&push_notification_drivers, driver) { if ((*driver)->v.cleanup != NULL) { (*driver)->v.cleanup(); } } } void ATTR_FORMAT(3, 4) push_notification_driver_debug(const char *label, struct mail_user *user, const char *fmt, ...) { va_list args; if (user->mail_debug) T_BEGIN { va_start(args, fmt); i_debug("%s%s", label, t_strdup_vprintf(fmt, args)); va_end(args); } T_END; } void push_notification_driver_register (const struct push_notification_driver *driver) { unsigned int idx; if (!array_is_created(&push_notification_drivers)) { i_array_init(&push_notification_drivers, 4); } if (push_notification_driver_find(driver->name, &idx)) { i_panic("push_notification_driver_register(%s): duplicate driver", driver->name); } array_append(&push_notification_drivers, &driver, 1); } void push_notification_driver_unregister (const struct push_notification_driver *driver) { unsigned int idx; if (!push_notification_driver_find(driver->name, &idx)) { i_panic("push_notification_driver_register(%s): unknown driver", driver->name); } if (array_is_created(&push_notification_drivers)) { array_delete(&push_notification_drivers, idx, 1); if (array_is_empty(&push_notification_drivers)) { array_free(&push_notification_drivers); } } } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageappend.h0000644000175000017500000000075313123174404026011 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGEAPPEND_H #define PUSH_NOTIFICATION_EVENT_MESSAGEAPPEND_H struct push_notification_event_messageappend_config { enum push_notification_event_message_flags flags; }; struct push_notification_event_messageappend_data { const char *from; const char *to; const char *subject; const char *snippet; }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGEAPPEND_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxsubscribe.c0000644000175000017500000000254613123174404026527 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-mailboxsubscribe.h" #include "push-notification-txn-mbox.h" #define EVENT_NAME "MailboxSubscribe" static void push_notification_event_mailboxsubscribe_debug_mbox (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Mailbox was subscribed to", EVENT_NAME); } static void push_notification_event_mailboxsubscribe_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox) { struct push_notification_event_mailboxsubscribe_data *data; data = p_new(ptxn->pool, struct push_notification_event_mailboxsubscribe_data, 1); data->subscribe = TRUE; push_notification_txn_mbox_set_eventdata(ptxn, mbox, ec, data); } /* Event definition */ extern struct push_notification_event push_notification_event_mailboxsubscribe; struct push_notification_event push_notification_event_mailboxsubscribe = { .name = EVENT_NAME, .mbox = { .debug_mbox = push_notification_event_mailboxsubscribe_debug_mbox }, .mbox_triggers = { .subscribe = push_notification_event_mailboxsubscribe_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-triggers.h0000644000175000017500000000646013123174404023705 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_TRIGGERS_H #define PUSH_NOTIFICATION_TRIGGERS_H #include "mail-types.h" struct mail; struct mailbox; struct push_notification_txn; struct push_notification_txn_mbox; struct push_notification_txn_msg; enum push_notification_event_trigger { PUSH_NOTIFICATION_EVENT_TRIGGER_NONE, /* Mailbox actions */ PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_CREATE = 0x001, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_DELETE = 0x002, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_RENAME = 0x004, PUSH_NOTIFICATION_EVENT_TRIGGER_MBOX_SUBSCRIBE = 0x008, /* Message actions */ PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_SAVE_NEW = 0x010, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_SAVE_APPEND = 0x020, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_EXPUNGE = 0x040, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_FLAGCHANGE = 0x080, PUSH_NOTIFICATION_EVENT_TRIGGER_MSG_KEYWORDCHANGE = 0x100, }; /* Mailbox actions. */ void push_notification_trigger_mbox_create(struct push_notification_txn *txn, struct mailbox *box, struct push_notification_txn_mbox *mbox); void push_notification_trigger_mbox_delete(struct push_notification_txn *txn, struct mailbox *box, struct push_notification_txn_mbox *mbox); void push_notification_trigger_mbox_rename(struct push_notification_txn *txn, struct mailbox *src, struct mailbox *dest, struct push_notification_txn_mbox *mbox); void push_notification_trigger_mbox_subscribe(struct push_notification_txn *txn, struct mailbox *box, bool subscribed, struct push_notification_txn_mbox *mbox); /* Message actions. */ void push_notification_trigger_msg_save_new(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg); void push_notification_trigger_msg_save_append(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg); void push_notification_trigger_msg_save_expunge(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg); void push_notification_trigger_msg_flag_change(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg, enum mail_flags old_flags); void push_notification_trigger_msg_keyword_change(struct push_notification_txn *txn, struct mail *mail, struct push_notification_txn_msg *msg, const char *const *old_keywords); #endif /* PUSH_NOTIFICATION_TRIGGERS_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxunsubscribe.h0000644000175000017500000000054413123174404027073 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MAILBOXUNSUBSCRIBE_H #define PUSH_NOTIFICATION_EVENT_MAILBOXUNSUBSCRIBE_H struct push_notification_event_mailboxunsubscribe_data { /* Can only be false. */ bool subscribe; }; #endif /* PUSH_NOTIFICATION_EVENT_MAILBOXUNSUBSCRIBE_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxcreate.h0000644000175000017500000000057313123174404026014 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MAILBOXCREATE_H #define PUSH_NOTIFICATION_EVENT_MAILBOXCREATE_H struct push_notification_event_mailboxcreate_data { /* RFC 5423 [4.4]: UIDVALIDITY required for create event. */ uint32_t uid_validity; }; #endif /* PUSH_NOTIFICATION_EVENT_MAILBOXCREATE_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-events.h0000644000175000017500000001156213123174404023362 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENTS_H #define PUSH_NOTIFICATION_EVENTS_H #include "mail-types.h" struct mail; struct mailbox; struct push_notification_event_config; struct push_notification_driver_txn; struct push_notification_txn; struct push_notification_txn_event; struct push_notification_txn_mbox; struct push_notification_txn_msg; struct push_notification_event_vfuncs_init { /* Return the default config for an event (or NULL if config is * required). */ void *(*default_config)(void); }; struct push_notification_event_vfuncs_mbox { /* Output debug information about a message event. */ void (*debug_mbox)(struct push_notification_txn_event *event); /* Called when message data is about to be free'd. */ void (*free_mbox)(struct push_notification_txn_event *event); }; struct push_notification_event_vfuncs_mbox_triggers { /* Mailbox event: create mailbox. */ void (*create)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox); /* Mailbox event: delete mailbox. */ void (*delete)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox); /* Mailbox event: rename mailbox. */ void (*rename)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox, struct mailbox *old); /* Mailbox event: subscribe mailbox. */ void (*subscribe)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox); /* Mailbox event: unsubscribe mailbox. */ void (*unsubscribe)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox); }; struct push_notification_event_vfuncs_msg { /* Output debug information about a message event. */ void (*debug_msg)(struct push_notification_txn_event *event); /* Called when message data is about to be free'd. */ void (*free_msg)(struct push_notification_txn_event *event); }; struct push_notification_event_vfuncs_msg_triggers { /* Message event: save message (from MTA). */ void (*save)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail); /* Message event: append message (from MUA). */ void (*append)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail); /* Message event: expunge message. */ void (*expunge)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg); /* Message event: flag change. */ void (*flagchange)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, enum mail_flags old_flags); /* Message event: keyword change. */ void (*keywordchange)(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, const char *const *old_keywords); }; struct push_notification_event_config { const struct push_notification_event *event; void *config; }; struct push_notification_event { const char *name; struct push_notification_event_vfuncs_init init; struct push_notification_event_vfuncs_mbox mbox; struct push_notification_event_vfuncs_mbox_triggers mbox_triggers; struct push_notification_event_vfuncs_msg msg; struct push_notification_event_vfuncs_msg_triggers msg_triggers; }; struct push_notification_txn_event { struct push_notification_event_config *event; void *data; }; ARRAY_DEFINE_TYPE(push_notification_event, const struct push_notification_event *); extern ARRAY_TYPE(push_notification_event) push_notification_events; void push_notification_event_init(struct push_notification_driver_txn *dtxn, const char *event_name, void *config); void push_notification_event_register (const struct push_notification_event *event); void push_notification_event_unregister (const struct push_notification_event *event); #endif /* PUSH_NOTIFICATION_EVENTS_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messagetrash.c0000644000175000017500000000333713165463624025672 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "mail-types.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-messagetrash.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "MessageTrash" static void push_notification_event_messagetrash_debug_msg (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Message was marked as deleted", EVENT_NAME); } static void push_notification_event_messagetrash_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, enum mail_flags old_flags) { struct push_notification_event_messagetrash_data *data; enum mail_flags flags; /* If data struct exists, that means the deleted flag was changed. */ data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if ((data == NULL) && !(old_flags & MAIL_DELETED)) { flags = mail_get_flags(mail); if (flags & MAIL_DELETED) { data = p_new(ptxn->pool, struct push_notification_event_messagetrash_data, 1); data->trash = TRUE; push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } } } /* Event definition */ extern struct push_notification_event push_notification_event_messagetrash; struct push_notification_event push_notification_event_messagetrash = { .name = EVENT_NAME, .msg = { .debug_msg = push_notification_event_messagetrash_debug_msg }, .msg_triggers = { .flagchange = push_notification_event_messagetrash_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-plugin.c0000644000175000017500000002731613165463624023366 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-private.h" #include "notify-plugin.h" #include "str.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-events-rfc5423.h" #include "push-notification-plugin.h" #include "push-notification-triggers.h" #include "push-notification-txn-mbox.h" #include "push-notification-txn-msg.h" #define PUSH_NOTIFICATION_CONFIG "push_notification_driver" #define PUSH_NOTIFICATION_CONFIG_OLD "push_notification_backend" #define PUSH_NOTIFICATION_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, push_notification_user_module) static MODULE_CONTEXT_DEFINE_INIT(push_notification_user_module, &mail_user_module_register); static struct ioloop *main_ioloop; static void push_notification_transaction_init(struct push_notification_txn *ptxn) { struct push_notification_driver_txn *dtxn; struct push_notification_driver_user **duser; struct mail_storage *storage; if (ptxn->initialized) { return; } ptxn->initialized = TRUE; storage = mailbox_get_storage(ptxn->mbox); if (storage->user->autocreated && (strcmp(storage->name, "raw") == 0)) { /* no notifications for autocreated raw users */ return; } array_foreach_modifiable(&ptxn->puser->driverlist->drivers, duser) { dtxn = p_new(ptxn->pool, struct push_notification_driver_txn, 1); dtxn->duser = *duser; dtxn->ptxn = ptxn; if ((dtxn->duser->driver->v.begin_txn == NULL) || dtxn->duser->driver->v.begin_txn(dtxn)) { array_append(&ptxn->drivers, &dtxn, 1); } } } static struct push_notification_txn * push_notification_transaction_create(struct mailbox *box, struct mailbox_transaction_context *t) { pool_t pool; struct push_notification_txn *ptxn; struct mail_storage *storage; pool = pool_alloconly_create("push notification transaction", 2048); ptxn = p_new(pool, struct push_notification_txn, 1); ptxn->mbox = box; storage = mailbox_get_storage(box); ptxn->muser = mail_storage_get_user(storage); ptxn->pool = pool; ptxn->puser = PUSH_NOTIFICATION_USER_CONTEXT(ptxn->muser); ptxn->t = t; ptxn->trigger = PUSH_NOTIFICATION_EVENT_TRIGGER_NONE; p_array_init(&ptxn->drivers, pool, 4); return ptxn; } static void push_notification_transaction_end (struct push_notification_txn *ptxn, bool success) { struct push_notification_driver_txn **dtxn; if (ptxn->initialized) { array_foreach_modifiable(&ptxn->drivers, dtxn) { if ((*dtxn)->duser->driver->v.end_txn != NULL) { (*dtxn)->duser->driver->v.end_txn(*dtxn, success); } } } pool_unref(&ptxn->pool); } static void push_notification_transaction_commit (void *txn, struct mail_transaction_commit_changes *changes) { struct push_notification_txn *ptxn = (struct push_notification_txn *)txn; struct ioloop *prev_ioloop = current_ioloop; /* Make sure we're not in just any random ioloop, which could get destroyed soon. This way the push-notification drivers can do async operations that finish in the main ioloop. */ io_loop_set_current(main_ioloop); if (changes == NULL) { push_notification_txn_mbox_end(ptxn); } else { push_notification_txn_msg_end(ptxn, changes); } push_notification_transaction_end(ptxn, TRUE); io_loop_set_current(prev_ioloop); } static void push_notification_mailbox_create(struct mailbox *box) { struct push_notification_txn *ptxn; ptxn = push_notification_transaction_create(box, NULL); push_notification_trigger_mbox_create(ptxn, box, NULL); push_notification_transaction_commit(ptxn, NULL); } static void push_notification_mailbox_delete(void *txn ATTR_UNUSED, struct mailbox *box) { struct push_notification_txn *ptxn; ptxn = push_notification_transaction_create(box, NULL); push_notification_trigger_mbox_delete(ptxn, box, NULL); push_notification_transaction_commit(ptxn, NULL); } static void push_notification_mailbox_rename(struct mailbox *src, struct mailbox *dest) { struct push_notification_txn *ptxn; ptxn = push_notification_transaction_create(dest, NULL); push_notification_trigger_mbox_rename(ptxn, src, dest, NULL); push_notification_transaction_commit(ptxn, NULL); } static void push_notification_mailbox_subscribe(struct mailbox *box, bool subscribed) { struct push_notification_txn *ptxn; ptxn = push_notification_transaction_create(box, NULL); push_notification_trigger_mbox_subscribe(ptxn, box, subscribed, NULL); push_notification_transaction_commit(ptxn, NULL); } static void push_notification_mail_save(void *txn, struct mail *mail) { struct push_notification_txn *ptxn = txn; push_notification_transaction_init(ptxn); /* POST_SESSION means MTA delivery. */ if (mail->box->flags & MAILBOX_FLAG_POST_SESSION) { push_notification_trigger_msg_save_new(ptxn, mail, NULL); } else { push_notification_trigger_msg_save_append(ptxn, mail, NULL); } } static void push_notification_mail_copy(void *txn, struct mail *src ATTR_UNUSED, struct mail *dest) { push_notification_mail_save(txn, dest); } static void push_notification_mail_expunge(void *txn, struct mail *mail) { struct push_notification_txn *ptxn = txn; push_notification_transaction_init(ptxn); push_notification_trigger_msg_save_expunge(txn, mail, NULL); } static void push_notification_mail_update_flags(void *txn, struct mail *mail, enum mail_flags old_flags) { struct push_notification_txn *ptxn = txn; push_notification_transaction_init(ptxn); push_notification_trigger_msg_flag_change(txn, mail, NULL, old_flags); } static void push_notification_mail_update_keywords(void *txn, struct mail *mail, const char *const *old_keywords) { struct push_notification_txn *ptxn = txn; push_notification_transaction_init(ptxn); push_notification_trigger_msg_keyword_change(txn, mail, NULL, old_keywords); } static void * push_notification_transaction_begin(struct mailbox_transaction_context *t) { return push_notification_transaction_create(mailbox_transaction_get_mailbox(t), t); } static void push_notification_transaction_rollback(void *txn) { struct push_notification_txn *ptxn = txn; push_notification_transaction_end(ptxn, FALSE); } static void push_notification_config_init(const char *config_name, struct mail_user *user, struct push_notification_driver_list *dlist) { struct push_notification_driver_user *duser; const char *env; unsigned int i; string_t *root_name; root_name = t_str_new(32); str_append(root_name, config_name); for (i = 2;; i++) { env = mail_user_plugin_getenv(user, str_c(root_name)); if ((env == NULL) || (*env == '\0')) { break; } if (push_notification_driver_init(user, env, user->pool, &duser) < 0) { break; } // Add driver. array_append(&dlist->drivers, &duser, 1); str_truncate(root_name, strlen(config_name)); str_printfa(root_name, "%d", i); } } static struct push_notification_driver_list * push_notification_driver_list_init(struct mail_user *user) { struct push_notification_driver_list *dlist; dlist = p_new(user->pool, struct push_notification_driver_list, 1); p_array_init(&dlist->drivers, user->pool, 4); push_notification_config_init(PUSH_NOTIFICATION_CONFIG, user, dlist); if (array_is_empty(&dlist->drivers)) { /* Support old configuration (it was available at time initial OX * driver was first released). */ push_notification_config_init(PUSH_NOTIFICATION_CONFIG_OLD, user, dlist); } return dlist; } static void push_notification_user_deinit(struct mail_user *user) { struct push_notification_user *puser = PUSH_NOTIFICATION_USER_CONTEXT(user); struct push_notification_driver_list *dlist = puser->driverlist; struct push_notification_driver_user **duser; struct ioloop *prev_ioloop = current_ioloop; /* Make sure we're in the main ioloop, so if the deinit/cleanup moves any I/Os or timeouts they won't get moved to some temporary ioloop. */ io_loop_set_current(main_ioloop); array_foreach_modifiable(&dlist->drivers, duser) { if ((*duser)->driver->v.deinit != NULL) { (*duser)->driver->v.deinit(*duser); } if ((*duser)->driver->v.cleanup != NULL) { (*duser)->driver->v.cleanup(); } } io_loop_set_current(prev_ioloop); puser->module_ctx.super.deinit(user); } static void push_notification_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct push_notification_user *puser; puser = p_new(user->pool, struct push_notification_user, 1); puser->module_ctx.super = *v; user->vlast = &puser->module_ctx.super; v->deinit = push_notification_user_deinit; puser->driverlist = push_notification_driver_list_init(user); MODULE_CONTEXT_SET(user, push_notification_user_module, puser); } /* Plugin interface. */ const char *push_notification_plugin_version = DOVECOT_ABI_VERSION; const char *push_notification_plugin_dependencies[] = { "notify", NULL }; extern struct push_notification_driver push_notification_driver_dlog; extern struct push_notification_driver push_notification_driver_ox; static struct notify_context *push_notification_ctx; static const struct notify_vfuncs push_notification_vfuncs = { /* Mailbox Events */ .mailbox_create = push_notification_mailbox_create, .mailbox_delete_commit = push_notification_mailbox_delete, .mailbox_rename = push_notification_mailbox_rename, .mailbox_set_subscribed = push_notification_mailbox_subscribe, /* Mail Events */ .mail_copy = push_notification_mail_copy, .mail_save = push_notification_mail_save, .mail_expunge = push_notification_mail_expunge, .mail_update_flags = push_notification_mail_update_flags, .mail_update_keywords = push_notification_mail_update_keywords, .mail_transaction_begin = push_notification_transaction_begin, .mail_transaction_commit = push_notification_transaction_commit, .mail_transaction_rollback = push_notification_transaction_rollback }; static struct mail_storage_hooks push_notification_storage_hooks = { .mail_user_created = push_notification_user_created }; void push_notification_plugin_init(struct module *module) { push_notification_ctx = notify_register(&push_notification_vfuncs); mail_storage_hooks_add(module, &push_notification_storage_hooks); push_notification_driver_register(&push_notification_driver_dlog); push_notification_driver_register(&push_notification_driver_ox); push_notification_event_register_rfc5423_events(); main_ioloop = current_ioloop; i_assert(main_ioloop != NULL); } void push_notification_plugin_deinit(void) { push_notification_driver_unregister(&push_notification_driver_dlog); push_notification_driver_unregister(&push_notification_driver_ox); push_notification_event_unregister_rfc5423_events(); mail_storage_hooks_remove(&push_notification_storage_hooks); notify_unregister(push_notification_ctx); } dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxdelete.h0000644000175000017500000000051513123174404026007 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MAILBOXDELETE_H #define PUSH_NOTIFICATION_EVENT_MAILBOXDELETE_H struct push_notification_event_mailboxdelete_data { /* Can only be true. */ bool deleted; }; #endif /* PUSH_NOTIFICATION_EVENT_MAILBOXDELETE_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxunsubscribe.c0000644000175000017500000000257413123174404027073 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-mailboxunsubscribe.h" #include "push-notification-txn-mbox.h" #define EVENT_NAME "MailboxUnsubscribe" static void push_notification_event_mailboxunsubscribe_debug_mbox (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Mailbox was subscribed to", EVENT_NAME); } static void push_notification_event_mailboxunsubscribe_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox) { struct push_notification_event_mailboxunsubscribe_data *data; data = p_new(ptxn->pool, struct push_notification_event_mailboxunsubscribe_data, 1); data->subscribe = TRUE; push_notification_txn_mbox_set_eventdata(ptxn, mbox, ec, data); } /* Event definition */ extern struct push_notification_event push_notification_event_mailboxunsubscribe; struct push_notification_event push_notification_event_mailboxunsubscribe = { .name = EVENT_NAME, .mbox = { .debug_mbox = push_notification_event_mailboxunsubscribe_debug_mbox }, .mbox_triggers = { .unsubscribe = push_notification_event_mailboxunsubscribe_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxrename.c0000644000175000017500000000272413123174404026013 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-mailboxrename.h" #include "push-notification-txn-mbox.h" #define EVENT_NAME "MailboxRename" static void push_notification_event_mailboxrename_debug_mbox (struct push_notification_txn_event *event) { struct push_notification_event_mailboxrename_data *data = event->data; i_debug("%s: Mailbox was renamed (old name: %s)", EVENT_NAME, data->old_mbox); } static void push_notification_event_mailboxrename_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox, struct mailbox *old) { struct push_notification_event_mailboxrename_data *data; data = p_new(ptxn->pool, struct push_notification_event_mailboxrename_data, 1); data->old_mbox = mailbox_get_vname(old); push_notification_txn_mbox_set_eventdata(ptxn, mbox, ec, data); } /* Event definition */ extern struct push_notification_event push_notification_event_mailboxrename; struct push_notification_event push_notification_event_mailboxrename = { .name = EVENT_NAME, .mbox = { .debug_mbox = push_notification_event_mailboxrename_debug_mbox }, .mbox_triggers = { .rename = push_notification_event_mailboxrename_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageread.h0000644000175000017500000000050113123174404025444 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGEREAD_H #define PUSH_NOTIFICATION_EVENT_MESSAGEREAD_H struct push_notification_event_messageread_data { /* Can only be true. */ bool read; }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGEREAD_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messageread.c0000644000175000017500000000331113165463624025454 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "mail-types.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-messageread.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "MessageRead" static void push_notification_event_messageread_debug_msg (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Message was flagged as seen", EVENT_NAME); } static void push_notification_event_messageread_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail, enum mail_flags old_flags) { struct push_notification_event_messageread_data *data; enum mail_flags flags; /* If data struct exists, that means the read flag was changed. */ data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if ((data == NULL) && !(old_flags & MAIL_SEEN)) { flags = mail_get_flags(mail); if (flags & MAIL_SEEN) { data = p_new(ptxn->pool, struct push_notification_event_messageread_data, 1); data->read = TRUE; push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } } } /* Event definition */ extern struct push_notification_event push_notification_event_messageread; struct push_notification_event push_notification_event_messageread = { .name = EVENT_NAME, .msg = { .debug_msg = push_notification_event_messageread_debug_msg }, .msg_triggers = { .flagchange = push_notification_event_messageread_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-flagsset.h0000644000175000017500000000126013123174404024777 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_FLAGSSET_H #define PUSH_NOTIFICATION_EVENT_FLAGSSET_H #include "mail-types.h" struct push_notification_event_flagsset_config { /* RFC 5423[4.2] - allow configuration whether FlagsSet event returns * Deleted and/or Seen flags, since these flags are also settable * via MessageRead/MessageTrash events. By default, include them * here. */ bool hide_deleted; bool hide_seen; }; struct push_notification_event_flagsset_data { enum mail_flags flags_set; ARRAY_TYPE(keywords) keywords_set; }; #endif /* PUSH_NOTIFICATION_EVENT_FLAGSSET_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-mailboxdelete.c0000644000175000017500000000244313123174404026004 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-mailboxdelete.h" #include "push-notification-txn-mbox.h" #define EVENT_NAME "MailboxDelete" static void push_notification_event_mailboxdelete_debug_mbox (struct push_notification_txn_event *event ATTR_UNUSED) { i_debug("%s: Mailbox was deleted", EVENT_NAME); } static void push_notification_event_mailboxdelete_event( struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_mbox *mbox) { struct push_notification_event_mailboxdelete_data *data; data = p_new(ptxn->pool, struct push_notification_event_mailboxdelete_data, 1); data->deleted = TRUE; push_notification_txn_mbox_set_eventdata(ptxn, mbox, ec, data); } /* Event definition */ extern struct push_notification_event push_notification_event_mailboxdelete; struct push_notification_event push_notification_event_mailboxdelete = { .name = EVENT_NAME, .mbox = { .debug_mbox = push_notification_event_mailboxdelete_debug_mbox }, .mbox_triggers = { .delete = push_notification_event_mailboxdelete_event } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-message-common.h0000644000175000017500000000117713123174404026110 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGE_COMMON_H #define PUSH_NOTIFICATION_EVENT_MESSAGE_COMMON_H enum push_notification_event_message_flags { /* Header: From */ PUSH_NOTIFICATION_MESSAGE_HDR_FROM = 0x01, /* Header: To */ PUSH_NOTIFICATION_MESSAGE_HDR_TO = 0x02, /* Header: Subject */ PUSH_NOTIFICATION_MESSAGE_HDR_SUBJECT = 0x04, /* Header: Date */ PUSH_NOTIFICATION_MESSAGE_HDR_DATE = 0x08, /* Body: Snippet */ PUSH_NOTIFICATION_MESSAGE_BODY_SNIPPET = 0x10 }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGE_COMMON_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messagenew.h0000644000175000017500000000143213123174404025326 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_EVENT_MESSAGENEW_H #define PUSH_NOTIFICATION_EVENT_MESSAGENEW_H #include "push-notification-event-message-common.h" struct push_notification_event_messagenew_config { enum push_notification_event_message_flags flags; }; struct push_notification_event_messagenew_data { /* PUSH_NOTIFICATION_MESSAGE_HDR_FROM */ const char *from; /* PUSH_NOTIFICATION_MESSAGE_HDR_TO */ const char *to; /* PUSH_NOTIFICATION_MESSAGE_HDR_SUBJECT */ const char *subject; /* PUSH_NOTIFICATION_MESSAGE_HDR_DATE */ time_t date; int date_tz; /* PUSH_NOTIFICATION_MESSAGE_BODY_SNIPPET */ const char *snippet; }; #endif /* PUSH_NOTIFICATION_EVENT_MESSAGENEW_H */ dovecot-2.2.33.2/src/plugins/push-notification/push-notification-driver-ox.c0000644000175000017500000003715513165463624024011 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "http-client.h" #include "http-url.h" #include "ioloop.h" #include "istream.h" #include "settings-parser.h" #include "json-parser.h" #include "mailbox-attribute.h" #include "mail-storage-private.h" #include "str.h" #include "strescape.h" #include "push-notification-drivers.h" #include "push-notification-event-messagenew.h" #include "push-notification-events.h" #include "push-notification-txn-msg.h" #define OX_LOG_LABEL "OX Push Notification: " #define OX_METADATA_KEY \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER "vendor/vendor.dovecot/http-notify" /* Default values. */ static const char *const default_events[] = { "MessageNew", NULL }; static const char *const default_mboxes[] = { "INBOX", NULL }; #define DEFAULT_CACHE_LIFETIME_SECS 60 #define DEFAULT_TIMEOUT_MSECS 2000 #define DEFAULT_RETRY_COUNT 1 /* This is data that is shared by all plugin users. */ struct push_notification_driver_ox_global { struct http_client *http_client; int refcount; }; static struct push_notification_driver_ox_global *ox_global = NULL; /* This is data specific to an OX driver. */ struct push_notification_driver_ox_config { struct http_url *http_url; unsigned int cached_ox_metadata_lifetime_secs; bool use_unsafe_username; unsigned int http_max_retries; unsigned int http_timeout_msecs; char *cached_ox_metadata; time_t cached_ox_metadata_timestamp; }; /* This is data specific to an OX driver transaction. */ struct push_notification_driver_ox_txn { const char *unsafe_user; }; static void push_notification_driver_ox_init_global(struct mail_user *user, struct push_notification_driver_ox_config *config) { struct http_client_settings http_set; if (ox_global->http_client == NULL) { /* this is going to use the first user's settings, but these are unlikely to change between users so it shouldn't matter much. */ i_zero(&http_set); http_set.debug = user->mail_debug; http_set.max_attempts = config->http_max_retries+1; http_set.request_timeout_msecs = config->http_timeout_msecs; ox_global->http_client = http_client_init(&http_set); } } static int push_notification_driver_ox_init(struct push_notification_driver_config *config, struct mail_user *user, pool_t pool, void **context, const char **error_r) { struct push_notification_driver_ox_config *dconfig; const char *error, *tmp; /* Valid config keys: cache_lifetime, url */ tmp = hash_table_lookup(config->config, (const char *)"url"); if (tmp == NULL) { *error_r = OX_LOG_LABEL "Driver requires the url parameter"; return -1; } dconfig = p_new(pool, struct push_notification_driver_ox_config, 1); if (http_url_parse(tmp, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool, &dconfig->http_url, &error) < 0) { *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX REST URL %s: %s", tmp, error); return -1; } dconfig->use_unsafe_username = hash_table_lookup(config->config, (const char *)"user_from_metadata") != NULL; push_notification_driver_debug(OX_LOG_LABEL, user, "Using URL %s", tmp); tmp = hash_table_lookup(config->config, (const char *)"cache_lifetime"); if (tmp == NULL) dconfig->cached_ox_metadata_lifetime_secs = DEFAULT_CACHE_LIFETIME_SECS; else if (settings_get_time(tmp, &dconfig->cached_ox_metadata_lifetime_secs, &error) < 0) { *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX cache_lifetime %s: %s", tmp, error); return -1; } tmp = hash_table_lookup(config->config, (const char *)"max_retries"); if ((tmp == NULL) || (str_to_uint(tmp, &dconfig->http_max_retries) < 0)) { dconfig->http_max_retries = DEFAULT_RETRY_COUNT; } tmp = hash_table_lookup(config->config, (const char *)"timeout_msecs"); if ((tmp == NULL) || (str_to_uint(tmp, &dconfig->http_timeout_msecs) < 0)) { dconfig->http_timeout_msecs = DEFAULT_TIMEOUT_MSECS; } push_notification_driver_debug(OX_LOG_LABEL, user, "Using cache lifetime: %u", dconfig->cached_ox_metadata_lifetime_secs); if (ox_global == NULL) { ox_global = i_new(struct push_notification_driver_ox_global, 1); ox_global->refcount = 0; } ++ox_global->refcount; *context = dconfig; return 0; } static const char *push_notification_driver_ox_get_metadata (struct push_notification_driver_txn *dtxn) { struct push_notification_driver_ox_config *dconfig = dtxn->duser->context; struct mail_attribute_value attr; struct mailbox *inbox; struct mailbox_transaction_context *mctx = NULL; struct mail_namespace *ns; bool success = FALSE, use_existing_txn = FALSE; int ret; if ((dconfig->cached_ox_metadata != NULL) && ((dconfig->cached_ox_metadata_timestamp + (time_t)dconfig->cached_ox_metadata_lifetime_secs) > ioloop_time)) { return dconfig->cached_ox_metadata; } /* Get canonical INBOX, where private server-level metadata is stored. * See imap/cmd-getmetadata.c */ if ((dtxn->ptxn->t != NULL) && dtxn->ptxn->mbox->inbox_user) { /* Use the currently open transaction. */ inbox = dtxn->ptxn->mbox; mctx = dtxn->ptxn->t; use_existing_txn = TRUE; } else { ns = mail_namespace_find_inbox(dtxn->ptxn->muser->namespaces); inbox = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); if (mailbox_open(inbox) < 0) { i_error(OX_LOG_LABEL "Skipped because unable to open INBOX: %s", mailbox_get_last_internal_error(inbox, NULL)); } else { mctx = mailbox_transaction_begin(inbox, 0); } } if (mctx != NULL) { ret = mailbox_attribute_get(mctx, MAIL_ATTRIBUTE_TYPE_PRIVATE, OX_METADATA_KEY, &attr); if (ret < 0) { i_error(OX_LOG_LABEL "Skipped because unable to get attribute: %s", mailbox_get_last_internal_error(inbox, NULL)); } else if (ret == 0) { push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser, "Skipped because not active (/private/"OX_METADATA_KEY" METADATA not set)"); } else { success = TRUE; } if (!use_existing_txn && (mailbox_transaction_commit(&mctx) < 0)) { i_error(OX_LOG_LABEL "Transaction commit failed: %s", mailbox_get_last_internal_error(inbox, NULL)); /* the commit doesn't matter though. */ } } if (!use_existing_txn) { mailbox_free(&inbox); } if (!success) return NULL; i_free(dconfig->cached_ox_metadata); dconfig->cached_ox_metadata = i_strdup(attr.value); dconfig->cached_ox_metadata_timestamp = ioloop_time; return dconfig->cached_ox_metadata; } static bool push_notification_driver_ox_begin_txn (struct push_notification_driver_txn *dtxn) { const char *const *args; struct push_notification_event_messagenew_config *config; const char *key, *mbox_curr, *md_value, *value; bool mbox_found = FALSE; struct push_notification_driver_ox_txn *txn; md_value = push_notification_driver_ox_get_metadata(dtxn); if (md_value == NULL) { return FALSE; } struct mail_user *user = dtxn->ptxn->muser; /* Unused keys: events, expire, folder */ /* TODO: To be implemented later(?) */ const char *const *events = default_events; time_t expire = INT_MAX; const char *const *mboxes = default_mboxes; if (expire < ioloop_time) { push_notification_driver_debug(OX_LOG_LABEL, user, "Skipped due to expiration (%ld < %ld)", (long)expire, (long)ioloop_time); return FALSE; } mbox_curr = mailbox_get_vname(dtxn->ptxn->mbox); for (; *mboxes != NULL; mboxes++) { if (strcmp(mbox_curr, *mboxes) == 0) { mbox_found = TRUE; break; } } if (mbox_found == FALSE) { push_notification_driver_debug(OX_LOG_LABEL, user, "Skipped because %s is not a watched mailbox", mbox_curr); return FALSE; } txn = p_new(dtxn->ptxn->pool, struct push_notification_driver_ox_txn, 1); /* Valid keys: user */ args = t_strsplit_tabescaped(md_value); for (; *args != NULL; args++) { key = *args; value = strchr(key, '='); if (value != NULL) { key = t_strdup_until(key, value++); if (strcmp(key, "user") == 0) { txn->unsafe_user = p_strdup(dtxn->ptxn->pool, value); } } } if (txn->unsafe_user == NULL) { i_error(OX_LOG_LABEL "No user provided in config"); return FALSE; } push_notification_driver_debug(OX_LOG_LABEL, user, "User (%s)", txn->unsafe_user); for (; *events != NULL; events++) { if (strcmp(*events, "MessageNew") == 0) { config = p_new(dtxn->ptxn->pool, struct push_notification_event_messagenew_config, 1); config->flags = PUSH_NOTIFICATION_MESSAGE_HDR_FROM | PUSH_NOTIFICATION_MESSAGE_HDR_SUBJECT | PUSH_NOTIFICATION_MESSAGE_BODY_SNIPPET; push_notification_event_init(dtxn, "MessageNew", config); push_notification_driver_debug(OX_LOG_LABEL, user, "Handling MessageNew event"); } } dtxn->context = txn; return TRUE; } static void push_notification_driver_ox_http_callback (const struct http_response *response, struct mail_user *user) { switch (response->status / 100) { case 2: // Success. if (user->mail_debug) { push_notification_driver_debug(OX_LOG_LABEL, user, "Notification sent successfully: %s", http_response_get_message(response)); } break; default: // Error. i_error(OX_LOG_LABEL "Error when sending notification: %s", http_response_get_message(response)); break; } } /* Callback needed for i_stream_add_destroy_callback() in * push_notification_driver_ox_process_msg. */ static void str_free_i(string_t *str) { str_free(&str); } static int push_notification_driver_ox_get_mailbox_status (struct push_notification_driver_txn *dtxn, struct mailbox_status *r_box_status) { /* The already opened mailbox. We cannot use or sync it, because we are within a save transaction. */ struct mailbox *mbox = dtxn->ptxn->mbox; struct mailbox *box; int ret; /* open and sync new instance of the same mailbox to get most recent status */ box = mailbox_alloc(mailbox_get_namespace(mbox)->list, mailbox_get_name(mbox), MAILBOX_FLAG_READONLY); if (mailbox_sync(box, 0) < 0) { i_error("mailbox_sync(%s) failed: %s", mailbox_get_vname(mbox), mailbox_get_last_internal_error(box, NULL)); ret = -1; } else { /* only 'unseen' is needed at the moment */ mailbox_get_open_status(box, STATUS_UNSEEN, r_box_status); push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser, "Got status of mailbox '%s': (unseen: %u)", mailbox_get_vname(box), r_box_status->unseen); ret = 0; } mailbox_free(&box); return ret; } static void push_notification_driver_ox_process_msg (struct push_notification_driver_txn *dtxn, struct push_notification_txn_msg *msg) { struct push_notification_driver_ox_config *dconfig = (struct push_notification_driver_ox_config *)dtxn->duser->context; struct http_client_request *http_req; struct push_notification_event_messagenew_data *messagenew; struct istream *payload; string_t *str; struct push_notification_driver_ox_txn *txn = (struct push_notification_driver_ox_txn *)dtxn->context; struct mail_user *user = dtxn->ptxn->muser; struct mailbox_status box_status; bool status_success = TRUE; if (push_notification_driver_ox_get_mailbox_status(dtxn, &box_status) < 0) { status_success = FALSE; } messagenew = push_notification_txn_msg_get_eventdata(msg, "MessageNew"); if (messagenew == NULL) { return; } push_notification_driver_ox_init_global(user, dconfig); http_req = http_client_request_url(ox_global->http_client, "PUT", dconfig->http_url, push_notification_driver_ox_http_callback, user); http_client_request_add_header(http_req, "Content-Type", "application/json; charset=utf-8"); str = str_new(default_pool, 256); str_append(str, "{\"user\":\""); json_append_escaped(str, dconfig->use_unsafe_username ? txn->unsafe_user : user->username); str_append(str, "\",\"event\":\"messageNew\",\"folder\":\""); json_append_escaped(str, msg->mailbox); str_printfa(str, "\",\"imap-uidvalidity\":%u,\"imap-uid\":%u", msg->uid_validity, msg->uid); if (messagenew->from != NULL) { str_append(str, ",\"from\":\""); json_append_escaped(str, messagenew->from); str_append(str, "\""); } if (messagenew->subject != NULL) { str_append(str, ",\"subject\":\""); json_append_escaped(str, messagenew->subject); str_append(str, "\""); } if (messagenew->snippet != NULL) { str_append(str, ",\"snippet\":\""); json_append_escaped(str, messagenew->snippet); str_append(str, "\""); } if (status_success) { str_printfa(str, ",\"unseen\":%u", box_status.unseen); } str_append(str, "}"); push_notification_driver_debug(OX_LOG_LABEL, user, "Sending notification: %s", str_c(str)); payload = i_stream_create_from_data(str_data(str), str_len(str)); i_stream_add_destroy_callback(payload, str_free_i, str); http_client_request_set_payload(http_req, payload, FALSE); http_client_request_submit(http_req); i_stream_unref(&payload); } static void push_notification_driver_ox_deinit (struct push_notification_driver_user *duser ATTR_UNUSED) { struct push_notification_driver_ox_config *dconfig = duser->context; i_free(dconfig->cached_ox_metadata); if (ox_global != NULL) { if (ox_global->http_client != NULL) http_client_wait(ox_global->http_client); i_assert(ox_global->refcount > 0); --ox_global->refcount; } } static void push_notification_driver_ox_cleanup(void) { if ((ox_global != NULL) && (ox_global->refcount <= 0)) { if (ox_global->http_client != NULL) { http_client_deinit(&ox_global->http_client); } i_free_and_null(ox_global); } } /* Driver definition */ extern struct push_notification_driver push_notification_driver_ox; struct push_notification_driver push_notification_driver_ox = { .name = "ox", .v = { .init = push_notification_driver_ox_init, .begin_txn = push_notification_driver_ox_begin_txn, .process_msg = push_notification_driver_ox_process_msg, .deinit = push_notification_driver_ox_deinit, .cleanup = push_notification_driver_ox_cleanup } }; dovecot-2.2.33.2/src/plugins/push-notification/push-notification-drivers.h0000644000175000017500000001004013123174404023522 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #ifndef PUSH_NOTIFICATION_DRIVERS_H #define PUSH_NOTIFICATION_DRIVERS_H #include "mail-user.h" #include "push-notification-triggers.h" struct mail_user; struct push_notification_driver_config; struct push_notification_driver_txn; struct push_notification_driver_user; struct push_notification_txn_mbox; struct push_notification_txn_msg; HASH_TABLE_DEFINE_TYPE(push_notification_config, const char *, const char *); HASH_TABLE_DEFINE_TYPE(push_notification_msgs, void *, struct push_notification_txn_msg *); struct push_notification_driver_vfuncs { /* Init driver. Config (from plugin configuration) is parsed once (no * user variable substitutions). Return 0 on success, or -1 if this * driver should be disabled (or on error). */ int (*init)(struct push_notification_driver_config *config, struct mail_user *user, pool_t pool, void **context, const char **error_r); /* Called at the beginning of a notification transaction. Return TRUE on * success, or FALSE if this driver should be ignored for this * transaction. */ bool (*begin_txn)(struct push_notification_driver_txn *dtxn); /* Called once for every mailbox processed. */ void (*process_mbox)(struct push_notification_driver_txn *dtxn, struct push_notification_txn_mbox *mbox); /* Called once for every message processed. */ void (*process_msg)(struct push_notification_driver_txn *dtxn, struct push_notification_txn_msg *msg); /* Called at the end of a successful notification transaction. */ void (*end_txn)(struct push_notification_driver_txn *dtxn, bool success); /* Called when plugin is deinitialized. */ void (*deinit)(struct push_notification_driver_user *duser); /* Called to cleanup any global resources used in plugin. */ void (*cleanup)(void); }; struct push_notification_driver { const char *name; struct push_notification_driver_vfuncs v; }; struct push_notification_driver_config { HASH_TABLE_TYPE(push_notification_config) config; const char *raw_config; }; struct push_notification_driver_user { const struct push_notification_driver *driver; void *context; }; struct push_notification_driver_txn { const struct push_notification_driver_user *duser; struct push_notification_txn *ptxn; /* Transaction context. */ void *context; }; struct push_notification_driver_list { ARRAY(struct push_notification_driver_user *) drivers; }; struct push_notification_user { union mail_user_module_context module_ctx; struct push_notification_driver_list *driverlist; }; struct push_notification_trigger_ctx { const char *name; void *context; }; struct push_notification_txn { pool_t pool; struct mailbox *mbox; struct mail_user *muser; struct push_notification_user *puser; bool initialized; enum push_notification_event_trigger trigger; struct push_notification_trigger_ctx *trigger_ctx; ARRAY(struct push_notification_driver_txn *) drivers; ARRAY(struct push_notification_event_config *) events; /* Used with mailbox events. */ struct push_notification_txn_mbox *mbox_txn; /* Used with mailbox events. */ HASH_TABLE_TYPE(push_notification_msgs) messages; /* Private (used with message events). */ struct mailbox_transaction_context *t; }; int push_notification_driver_init(struct mail_user *user, const char *config_in, pool_t pool, struct push_notification_driver_user **duser_r); void push_notification_driver_cleanup_all(void); void ATTR_FORMAT(3, 4) push_notification_driver_debug(const char *label, struct mail_user *user, const char *fmt, ...); void push_notification_driver_register (const struct push_notification_driver *driver); void push_notification_driver_unregister (const struct push_notification_driver *driver); #endif /* PUSH_NOTIFICATION_DRIVERS_H */ dovecot-2.2.33.2/src/plugins/push-notification/Makefile.am0000644000175000017500000000424213123174404020275 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/notify NOPLUGIN_LDFLAGS = lib20_push_notification_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = lib20_push_notification_plugin.la if DOVECOT_PLUGIN_DEPS lib20_push_notification_plugin_la_LIBADD = \ ../notify/lib15_notify_plugin.la endif lib20_push_notification_plugin_la_SOURCES = \ push-notification-driver-dlog.c \ push-notification-driver-ox.c \ push-notification-drivers.c \ push-notification-event-flagsclear.c \ push-notification-event-flagsset.c \ push-notification-event-mailboxcreate.c \ push-notification-event-mailboxdelete.c \ push-notification-event-mailboxrename.c \ push-notification-event-mailboxsubscribe.c \ push-notification-event-mailboxunsubscribe.c \ push-notification-event-messageappend.c \ push-notification-event-messageexpunge.c \ push-notification-event-messagenew.c \ push-notification-event-messageread.c \ push-notification-event-messagetrash.c \ push-notification-events.c \ push-notification-events-rfc5423.c \ push-notification-plugin.c \ push-notification-triggers.c \ push-notification-txn-mbox.c \ push-notification-txn-msg.c headers = \ push-notification-drivers.h \ push-notification-event-flagsclear.h \ push-notification-event-flagsset.h \ push-notification-event-mailboxcreate.h \ push-notification-event-mailboxdelete.h \ push-notification-event-mailboxrename.h \ push-notification-event-mailboxsubscribe.h \ push-notification-event-mailboxunsubscribe.h \ push-notification-event-message-common.h \ push-notification-event-messageappend.h \ push-notification-event-messageexpunge.h \ push-notification-event-messagenew.h \ push-notification-event-messageread.h \ push-notification-event-messagetrash.h \ push-notification-events.h \ push-notification-events-rfc5423.h \ push-notification-plugin.h \ push-notification-triggers.h \ push-notification-txn-mbox.h \ push-notification-txn-msg.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/plugins/push-notification/push-notification-event-messagenew.c0000644000175000017500000000766313165463624025350 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "iso8601-date.h" #include "istream.h" #include "mail-storage.h" #include #include "push-notification-drivers.h" #include "push-notification-events.h" #include "push-notification-event-message-common.h" #include "push-notification-event-messagenew.h" #include "push-notification-txn-msg.h" #define EVENT_NAME "MessageNew" static struct push_notification_event_messagenew_config default_config; static void *push_notification_event_messagenew_default_config(void) { i_zero(&default_config); return &default_config; } static void push_notification_event_messagenew_debug_msg (struct push_notification_txn_event *event) { struct push_notification_event_messagenew_data *data = event->data; struct tm *tm; if (data->date != -1) { tm = gmtime(&data->date); i_debug("%s: Date [%s]", EVENT_NAME, iso8601_date_create_tm(tm, data->date_tz)); } if (data->from != NULL) { i_debug("%s: From [%s]", EVENT_NAME, data->from); } if (data->snippet != NULL) { i_debug("%s: Snippet [%s]", EVENT_NAME, data->snippet); } if (data->subject != NULL) { i_debug("%s: Subject [%s]", EVENT_NAME, data->subject); } if (data->to != NULL) { i_debug("%s: To [%s]", EVENT_NAME, data->to); } } static void push_notification_event_messagenew_event(struct push_notification_txn *ptxn, struct push_notification_event_config *ec, struct push_notification_txn_msg *msg, struct mail *mail) { struct push_notification_event_messagenew_config *config = (struct push_notification_event_messagenew_config *)ec->config; struct push_notification_event_messagenew_data *data; time_t date; int tz; const char *value; if (!config->flags) { return; } data = push_notification_txn_msg_get_eventdata(msg, EVENT_NAME); if (data == NULL) { data = p_new(ptxn->pool, struct push_notification_event_messagenew_data, 1); data->date = -1; push_notification_txn_msg_set_eventdata(ptxn, msg, ec, data); } if ((data->to == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_TO) && (mail_get_first_header(mail, "To", &value) >= 0)) { data->to = p_strdup(ptxn->pool, value); } if ((data->from == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_FROM) && (mail_get_first_header(mail, "From", &value) >= 0)) { data->from = p_strdup(ptxn->pool, value); } if ((data->subject == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_SUBJECT) && (mail_get_first_header(mail, "Subject", &value) >= 0)) { data->subject = p_strdup(ptxn->pool, value); } if ((data->date == -1) && (config->flags & PUSH_NOTIFICATION_MESSAGE_HDR_DATE) && (mail_get_date(mail, &date, &tz) >= 0)) { data->date = date; data->date_tz = tz; } if ((data->snippet == NULL) && (config->flags & PUSH_NOTIFICATION_MESSAGE_BODY_SNIPPET) && (mail_get_special(mail, MAIL_FETCH_BODY_SNIPPET, &value) >= 0)) { /* [0] contains the snippet algorithm, skip over it */ i_assert(value[0] != '\0'); data->snippet = p_strdup(ptxn->pool, value + 1); } } /* Event definition */ extern struct push_notification_event push_notification_event_messagenew; struct push_notification_event push_notification_event_messagenew = { .name = EVENT_NAME, .init = { .default_config = push_notification_event_messagenew_default_config }, .msg = { .debug_msg = push_notification_event_messagenew_debug_msg }, .msg_triggers = { .save = push_notification_event_messagenew_event } }; dovecot-2.2.33.2/src/plugins/trash/0002755000175000017500000000000013172375613014010 500000000000000dovecot-2.2.33.2/src/plugins/trash/Makefile.in0000644000175000017500000005537213172375575016016 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/trash ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib11_trash_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../quota/lib10_quota_plugin.la am_lib11_trash_plugin_la_OBJECTS = trash-plugin.lo lib11_trash_plugin_la_OBJECTS = $(am_lib11_trash_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib11_trash_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib11_trash_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib11_trash_plugin_la_SOURCES) DIST_SOURCES = $(lib11_trash_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/quota lib11_trash_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib11_trash_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib11_trash_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../quota/lib10_quota_plugin.la lib11_trash_plugin_la_SOURCES = \ trash-plugin.c noinst_HEADERS = \ trash-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/trash/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/trash/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib11_trash_plugin.la: $(lib11_trash_plugin_la_OBJECTS) $(lib11_trash_plugin_la_DEPENDENCIES) $(EXTRA_lib11_trash_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib11_trash_plugin_la_LINK) -rpath $(moduledir) $(lib11_trash_plugin_la_OBJECTS) $(lib11_trash_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trash-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/trash/trash-plugin.h0000644000175000017500000000026713123174404016510 00000000000000#ifndef TRASH_PLUGIN_H #define TRASH_PLUGIN_H extern const char *trash_plugin_dependencies[]; void trash_plugin_init(struct module *module); void trash_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/trash/trash-plugin.c0000644000175000017500000002377113165463624016523 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "unichar.h" #include "istream.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "quota-private.h" #include "quota-plugin.h" #include "trash-plugin.h" #include #include #define INIT_TRASH_MAILBOX_COUNT 4 #define MAX_RETRY_COUNT 3 #define TRASH_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, trash_user_module) struct trash_mailbox { const char *name; int priority; /* lower number = higher priority */ struct mail_namespace *ns; /* temporarily set while cleaning: */ struct mailbox *box; struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail *mail; }; struct trash_user { union mail_user_module_context module_ctx; const char *config_file; /* ordered by priority, highest first */ ARRAY(struct trash_mailbox) trash_boxes; }; const char *trash_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(trash_user_module, &mail_user_module_register); static enum quota_alloc_result (*trash_next_quota_test_alloc)( struct quota_transaction_context *, uoff_t); static int trash_clean_mailbox_open(struct trash_mailbox *trash) { struct mail_search_args *search_args; trash->box = mailbox_alloc(trash->ns->list, trash->name, 0); mailbox_set_reason(trash->box, "trash plugin"); if (mailbox_open(trash->box) < 0) { mailbox_free(&trash->box); return 0; } if (mailbox_sync(trash->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) return -1; trash->trans = mailbox_transaction_begin(trash->box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); trash->search_ctx = mailbox_search_init(trash->trans, search_args, NULL, MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_RECEIVED_DATE, NULL); mail_search_args_unref(&search_args); return mailbox_search_next(trash->search_ctx, &trash->mail) ? 1 : 0; } static int trash_clean_mailbox_get_next(struct trash_mailbox *trash, time_t *received_time_r) { int ret; if (trash->mail == NULL) { if (trash->box == NULL) ret = trash_clean_mailbox_open(trash); else { ret = mailbox_search_next(trash->search_ctx, &trash->mail) ? 1 : 0; } if (ret <= 0) { *received_time_r = 0; return ret; } } if (mail_get_received_date(trash->mail, received_time_r) < 0) return -1; return 1; } static int trash_try_clean_mails(struct quota_transaction_context *ctx, uint64_t size_needed, unsigned int count_needed) { struct trash_user *tuser = TRASH_USER_CONTEXT(ctx->quota->user); struct trash_mailbox *trashes; unsigned int i, j, count, oldest_idx; time_t oldest, received = 0; uint64_t size, size_expunged = 0; unsigned int expunged_count = 0; int ret = 0; trashes = array_get_modifiable(&tuser->trash_boxes, &count); for (i = 0; i < count; ) { /* expunge oldest mails first in all trash boxes with same priority */ oldest_idx = count; oldest = (time_t)-1; for (j = i; j < count; j++) { if (trashes[j].priority != trashes[i].priority) break; ret = trash_clean_mailbox_get_next(&trashes[j], &received); if (ret < 0) goto err; if (ret > 0) { if (oldest == (time_t)-1 || received < oldest) { oldest = received; oldest_idx = j; } } } if (oldest_idx < count) { if (mail_get_physical_size(trashes[oldest_idx].mail, &size) < 0) { /* maybe expunged already? */ trashes[oldest_idx].mail = NULL; continue; } mail_expunge(trashes[oldest_idx].mail); expunged_count++; size_expunged += size; if (size_expunged >= size_needed && expunged_count >= count_needed) break; trashes[oldest_idx].mail = NULL; } else { /* find more mails from next priority's mailbox */ i = j; } } err: for (i = 0; i < count; i++) { struct trash_mailbox *trash = &trashes[i]; if (trash->box == NULL) continue; trash->mail = NULL; (void)mailbox_search_deinit(&trash->search_ctx); if (size_expunged >= size_needed && expunged_count >= count_needed) { (void)mailbox_transaction_commit(&trash->trans); (void)mailbox_sync(trash->box, 0); } else { /* couldn't get enough space, don't expunge anything */ mailbox_transaction_rollback(&trash->trans); } mailbox_free(&trash->box); } if (size_expunged < size_needed) { if (ctx->quota->user->mail_debug) { i_debug("trash plugin: Failed to remove enough messages " "(needed %llu bytes, expunged only %llu bytes)", (unsigned long long)size_needed, (unsigned long long)size_expunged); } return 0; } if (expunged_count < count_needed) { if (ctx->quota->user->mail_debug) { i_debug("trash plugin: Failed to remove enough messages " "(needed %u messages, expunged only %u messages)", count_needed, expunged_count); } return 0; } if (ctx->bytes_over > 0) { /* user is over quota. drop the over-bytes first. */ i_assert(ctx->bytes_over <= size_expunged); size_expunged -= ctx->bytes_over; ctx->bytes_over = 0; } if (ctx->count_over > 0) { /* user is over quota. drop the over-count first. */ i_assert(ctx->count_over <= expunged_count); expunged_count -= ctx->count_over; ctx->count_over = 0; } if (ctx->bytes_ceil > ((uint64_t)-1 - size_expunged)) { ctx->bytes_ceil = (uint64_t)-1; } else { ctx->bytes_ceil += size_expunged; } if (ctx->count_ceil < ((uint64_t)-1 - expunged_count)) { ctx->count_ceil = (uint64_t)-1; } else { ctx->count_ceil += expunged_count; } return 1; } static enum quota_alloc_result trash_quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size) { int i; uint64_t size_needed = 0; unsigned int count_needed = 0; for (i = 0; ; i++) { enum quota_alloc_result ret; ret = trash_next_quota_test_alloc(ctx, size); if (ret != QUOTA_ALLOC_RESULT_OVER_QUOTA) { if (ret == QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT && ctx->quota->user->mail_debug) i_debug("trash plugin: Mail is larger than " "quota, won't even try to handle"); return ret; } if (i == MAX_RETRY_COUNT) { /* trash_try_clean_mails() should have returned 0 if it couldn't get enough space, but allow retrying it a couple of times if there was some extra space that was needed.. */ break; } if (ctx->bytes_ceil != (uint64_t)-1 && ctx->bytes_ceil < size + ctx->bytes_over) size_needed = size + ctx->bytes_over - ctx->bytes_ceil; if (ctx->count_ceil != (uint64_t)-1 && ctx->count_ceil < 1 + ctx->count_over) count_needed = 1 + ctx->count_over - ctx->count_ceil; /* not enough space. try deleting some from mailbox. */ if (trash_try_clean_mails(ctx, size_needed, count_needed) <= 0) return QUOTA_ALLOC_RESULT_OVER_QUOTA; } return QUOTA_ALLOC_RESULT_OVER_QUOTA; } static bool trash_find_storage(struct mail_user *user, struct trash_mailbox *trash) { struct mail_namespace *ns; ns = mail_namespace_find(user->namespaces, trash->name); if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) return FALSE; trash->ns = ns; return TRUE; } static int trash_mailbox_priority_cmp(const struct trash_mailbox *t1, const struct trash_mailbox *t2) { return t1->priority - t2->priority; } static int read_configuration(struct mail_user *user, const char *path) { struct trash_user *tuser = TRASH_USER_CONTEXT(user); struct istream *input; const char *line, *name; struct trash_mailbox *trash; int fd, ret = 0; fd = open(path, O_RDONLY); if (fd == -1) { i_error("trash plugin: open(%s) failed: %m", path); return -1; } p_array_init(&tuser->trash_boxes, user->pool, INIT_TRASH_MAILBOX_COUNT); input = i_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(input, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { /* */ name = strchr(line, ' '); if (name == NULL || name[1] == '\0' || *line == '#') continue; trash = array_append_space(&tuser->trash_boxes); trash->name = p_strdup(user->pool, name+1); if (str_to_int(t_strdup_until(line, name), &trash->priority) < 0) { i_error("trash: Invalid priority for mailbox '%s'", trash->name); ret = -1; } if (!uni_utf8_str_is_valid(trash->name)) { i_error("trash: Mailbox name not UTF-8: %s", trash->name); ret = -1; } if (!trash_find_storage(user, trash)) { i_error("trash: Namespace not found for mailbox '%s'", trash->name); ret = -1; } if (user->mail_debug) { i_debug("trash plugin: Added '%s' with priority %d", trash->name, trash->priority); } } i_stream_destroy(&input); i_close_fd(&fd); array_sort(&tuser->trash_boxes, trash_mailbox_priority_cmp); return ret; } static void trash_mail_user_created(struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct trash_user *tuser; const char *env; env = mail_user_plugin_getenv(user, "trash"); if (env == NULL) { if (user->mail_debug) i_debug("trash: No trash setting - plugin disabled"); } else if (quser == NULL) { i_error("trash plugin: quota plugin not initialized"); } else { tuser = p_new(user->pool, struct trash_user, 1); tuser->config_file = env; MODULE_CONTEXT_SET(user, trash_user_module, tuser); } } static void trash_mail_namespaces_created(struct mail_namespace *namespaces) { struct mail_user *user = namespaces->user; struct trash_user *tuser = TRASH_USER_CONTEXT(user); struct quota_user *quser = QUOTA_USER_CONTEXT(user); if (tuser != NULL && read_configuration(user, tuser->config_file) == 0) { trash_next_quota_test_alloc = quser->quota->set->test_alloc; quser->quota->set->test_alloc = trash_quota_test_alloc; } } static struct mail_storage_hooks trash_mail_storage_hooks = { .mail_user_created = trash_mail_user_created, .mail_namespaces_created = trash_mail_namespaces_created, }; void trash_plugin_init(struct module *module) { mail_storage_hooks_add(module, &trash_mail_storage_hooks); } void trash_plugin_deinit(void) { mail_storage_hooks_remove(&trash_mail_storage_hooks); } const char *trash_plugin_dependencies[] = { "quota", NULL }; dovecot-2.2.33.2/src/plugins/trash/Makefile.am0000644000175000017500000000074213123174404015754 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/quota NOPLUGIN_LDFLAGS = lib11_trash_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib11_trash_plugin.la if DOVECOT_PLUGIN_DEPS lib11_trash_plugin_la_LIBADD = \ ../quota/lib10_quota_plugin.la endif lib11_trash_plugin_la_SOURCES = \ trash-plugin.c noinst_HEADERS = \ trash-plugin.h dovecot-2.2.33.2/src/plugins/fs-compress/0002755000175000017500000000000013172375614015131 500000000000000dovecot-2.2.33.2/src/plugins/fs-compress/Makefile.in0000644000175000017500000005506613172375574017135 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/fs-compress ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(fs_moduledir)" LTLIBRARIES = $(fs_module_LTLIBRARIES) am_libfs_compress_la_OBJECTS = fs-compress.lo libfs_compress_la_OBJECTS = $(am_libfs_compress_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libfs_compress_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libfs_compress_la_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libfs_compress_la_SOURCES) DIST_SOURCES = $(libfs_compress_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ fs_moduledir = $(moduledir) fs_module_LTLIBRARIES = \ libfs_compress.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-fs libfs_compress_la_SOURCES = fs-compress.c libfs_compress_la_LIBADD = ../../lib-compression/libdovecot-compression.la libfs_compress_la_DEPENDENCIES = ../../lib-compression/libdovecot-compression.la libfs_compress_la_LDFLAGS = -module -avoid-version all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/fs-compress/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/fs-compress/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-fs_moduleLTLIBRARIES: $(fs_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(fs_module_LTLIBRARIES)'; test -n "$(fs_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(fs_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(fs_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(fs_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(fs_moduledir)"; \ } uninstall-fs_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(fs_module_LTLIBRARIES)'; test -n "$(fs_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(fs_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(fs_moduledir)/$$f"; \ done clean-fs_moduleLTLIBRARIES: -test -z "$(fs_module_LTLIBRARIES)" || rm -f $(fs_module_LTLIBRARIES) @list='$(fs_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libfs_compress.la: $(libfs_compress_la_OBJECTS) $(libfs_compress_la_DEPENDENCIES) $(EXTRA_libfs_compress_la_DEPENDENCIES) $(AM_V_CCLD)$(libfs_compress_la_LINK) -rpath $(fs_moduledir) $(libfs_compress_la_OBJECTS) $(libfs_compress_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-compress.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(fs_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-fs_moduleLTLIBRARIES clean-generic clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-fs_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-fs_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-fs_moduleLTLIBRARIES clean-generic clean-libtool \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-fs_moduleLTLIBRARIES \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-fs_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/fs-compress/fs-compress.c0000644000175000017500000001620213165463624017456 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "iostream-temp.h" #include "compression.h" #include "fs-api-private.h" struct compress_fs { struct fs fs; const struct compression_handler *handler; unsigned int compress_level; }; struct compress_fs_file { struct fs_file file; struct compress_fs *fs; struct fs_file *super_read; enum fs_open_mode open_mode; struct istream *input; struct ostream *super_output; struct ostream *temp_output; }; extern const struct fs fs_class_compress; static struct fs *fs_compress_alloc(void) { struct compress_fs *fs; fs = i_new(struct compress_fs, 1); fs->fs = fs_class_compress; return &fs->fs; } static int fs_compress_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct compress_fs *fs = (struct compress_fs *)_fs; const char *p, *compression_name, *level_str, *error; const char *parent_name, *parent_args; /* get compression handler name */ p = strchr(args, ':'); if (p == NULL) { fs_set_error(_fs, "Compression method not given as parameter"); return -1; } compression_name = t_strdup_until(args, p++); args = p; /* get compression level */ p = strchr(args, ':'); if (p == NULL || p[1] == '\0') { fs_set_error(_fs, "Parent filesystem not given as parameter"); return -1; } level_str = t_strdup_until(args, p++); if (str_to_uint(level_str, &fs->compress_level) < 0 || fs->compress_level < 1 || fs->compress_level > 9) { fs_set_error(_fs, "Invalid compression level parameter '%s'", level_str); return -1; } args = p; fs->handler = compression_lookup_handler(compression_name); if (fs->handler == NULL) { fs_set_error(_fs, "Compression method '%s' not support", compression_name); return -1; } parent_args = strchr(args, ':'); if (parent_args == NULL) { parent_name = args; parent_args = ""; } else { parent_name = t_strdup_until(args, parent_args); parent_args++; } if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s: %s", parent_name, error); return -1; } return 0; } static void fs_compress_deinit(struct fs *_fs) { struct compress_fs *fs = (struct compress_fs *)_fs; if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs); } static struct fs_file * fs_compress_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct compress_fs *fs = (struct compress_fs *)_fs; struct compress_fs_file *file; file = i_new(struct compress_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->fs = fs; file->open_mode = mode; /* avoid unnecessarily creating two seekable streams */ flags &= ~FS_OPEN_FLAG_SEEKABLE; file->file.parent = fs_file_init(_fs->parent, path, mode | flags); if (mode == FS_OPEN_MODE_READONLY && (flags & FS_OPEN_FLAG_ASYNC) == 0) { /* use async stream for parent, so fs_read_stream() won't create another seekable stream unneededly */ file->super_read = fs_file_init(_fs->parent, path, mode | flags | FS_OPEN_FLAG_ASYNC); } else { file->super_read = file->file.parent; } return &file->file; } static void fs_compress_file_deinit(struct fs_file *_file) { struct compress_fs_file *file = (struct compress_fs_file *)_file; if (file->super_read != _file->parent && file->super_read != NULL) fs_file_deinit(&file->super_read); fs_file_deinit(&_file->parent); i_free(file->file.path); i_free(file); } static void fs_compress_file_close(struct fs_file *_file) { struct compress_fs_file *file = (struct compress_fs_file *)_file; if (file->input != NULL) i_stream_unref(&file->input); if (file->super_read != NULL) fs_file_close(file->super_read); if (_file->parent != NULL) fs_file_close(_file->parent); } static struct istream * fs_compress_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct compress_fs_file *file = (struct compress_fs_file *)_file; struct istream *input; if (file->input != NULL) { i_stream_ref(file->input); i_stream_seek(file->input, 0); return file->input; } input = fs_read_stream(file->super_read, max_buffer_size); file->input = file->fs->handler->create_istream(input, FALSE); i_stream_unref(&input); i_stream_ref(file->input); return file->input; } static void fs_compress_write_stream(struct fs_file *_file) { struct compress_fs_file *file = (struct compress_fs_file *)_file; i_assert(_file->output == NULL); file->temp_output = iostream_temp_create_named(_file->fs->temp_path_prefix, IOSTREAM_TEMP_FLAG_TRY_FD_DUP, fs_file_path(_file)); _file->output = file->fs->handler-> create_ostream(file->temp_output, file->fs->compress_level); } static int fs_compress_write_stream_finish(struct fs_file *_file, bool success) { struct compress_fs_file *file = (struct compress_fs_file *)_file; struct istream *input; int ret; if (_file->output != NULL) { if (_file->output->closed) success = FALSE; if (_file->output == file->super_output) _file->output = NULL; else o_stream_unref(&_file->output); } if (!success) { if (file->temp_output != NULL) o_stream_destroy(&file->temp_output); if (file->super_output != NULL) fs_write_stream_abort_parent(_file, &file->super_output); return -1; } if (file->super_output != NULL) { i_assert(file->temp_output == NULL); return fs_write_stream_finish(_file->parent, &file->super_output); } if (file->temp_output == NULL) { /* finishing up */ i_assert(file->super_output == NULL); return fs_write_stream_finish(_file->parent, &file->temp_output); } /* finish writing the temporary file */ input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE); file->super_output = fs_write_stream(_file->parent); if (o_stream_send_istream(file->super_output, input) >= 0) ret = fs_write_stream_finish(_file->parent, &file->super_output); else if (input->stream_errno != 0) { fs_write_stream_abort_error(_file->parent, &file->super_output, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); ret = -1; } else { i_assert(file->super_output->stream_errno != 0); fs_write_stream_abort_error(_file->parent, &file->super_output, "write(%s) failed: %s", o_stream_get_name(file->super_output), o_stream_get_error(file->super_output)); ret = -1; } i_stream_unref(&input); return ret; } const struct fs fs_class_compress = { .name = "compress", .v = { fs_compress_alloc, fs_compress_init, fs_compress_deinit, fs_wrapper_get_properties, fs_compress_file_init, fs_compress_file_deinit, fs_compress_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_wrapper_get_metadata, fs_wrapper_prefetch, fs_read_via_stream, fs_compress_read_stream, fs_write_via_stream, fs_compress_write_stream, fs_compress_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_wrapper_stat, fs_wrapper_copy, fs_wrapper_rename, fs_wrapper_delete, fs_wrapper_iter_init, fs_wrapper_iter_next, fs_wrapper_iter_deinit, NULL, fs_wrapper_get_nlinks } }; dovecot-2.2.33.2/src/plugins/fs-compress/Makefile.am0000644000175000017500000000070513123174404017073 00000000000000fs_moduledir = $(moduledir) fs_module_LTLIBRARIES = \ libfs_compress.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-fs NOPLUGIN_LDFLAGS = libfs_compress_la_SOURCES = fs-compress.c libfs_compress_la_LIBADD = ../../lib-compression/libdovecot-compression.la libfs_compress_la_DEPENDENCIES = ../../lib-compression/libdovecot-compression.la libfs_compress_la_LDFLAGS = -module -avoid-version dovecot-2.2.33.2/src/plugins/Makefile.in0000644000175000017500000005243213172375574014666 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = acl imap-acl autocreate expire fts fts-squat last-login \ lazy-expunge listescape notify notify-status push-notification \ mail-filter mail-log mailbox-alias quota quota-clone \ imap-quota pop3-migration replication snarf stats imap-stats \ mail-crypt trash virtual welcome zlib imap-zlib fts-lucene \ fts-solr dict-ldap apparmor fs-compress var-expand-crypt am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @BUILD_ZLIB_PLUGIN_TRUE@ZLIB = zlib imap-zlib @BUILD_LUCENE_TRUE@FTS_LUCENE = fts-lucene @BUILD_SOLR_TRUE@FTS_SOLR = fts-solr @HAVE_LDAP_TRUE@DICT_LDAP = dict-ldap @HAVE_APPARMOR_TRUE@APPARMOR = apparmor SUBDIRS = \ acl \ imap-acl \ autocreate \ expire \ fts \ fts-squat \ last-login \ lazy-expunge \ listescape \ notify \ notify-status \ push-notification \ mail-filter \ mail-log \ mailbox-alias \ quota \ quota-clone \ imap-quota \ pop3-migration \ replication \ snarf \ stats \ imap-stats \ mail-crypt \ trash \ virtual \ welcome \ $(ZLIB) \ $(FTS_LUCENE) \ $(FTS_SOLR) \ $(DICT_LDAP) \ $(APPARMOR) \ fs-compress \ var-expand-crypt all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/pop3-migration/0002755000175000017500000000000013172375613015537 500000000000000dovecot-2.2.33.2/src/plugins/pop3-migration/Makefile.in0000644000175000017500000006074113172375574017540 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/plugins/pop3-migration ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib05_pop3_migration_plugin_la_LIBADD = am_lib05_pop3_migration_plugin_la_OBJECTS = pop3-migration-plugin.lo lib05_pop3_migration_plugin_la_OBJECTS = \ $(am_lib05_pop3_migration_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib05_pop3_migration_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib05_pop3_migration_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ am__EXEEXT_1 = test-pop3-migration-plugin$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_pop3_migration_plugin_OBJECTS = \ test-pop3-migration-plugin.$(OBJEXT) test_pop3_migration_plugin_OBJECTS = \ $(am_test_pop3_migration_plugin_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib05_pop3_migration_plugin_la_SOURCES) \ $(test_pop3_migration_plugin_SOURCES) DIST_SOURCES = $(lib05_pop3_migration_plugin_la_SOURCES) \ $(test_pop3_migration_plugin_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index lib05_pop3_migration_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib05_pop3_migration_plugin.la lib05_pop3_migration_plugin_la_SOURCES = \ pop3-migration-plugin.c noinst_HEADERS = \ pop3-migration-plugin.h test_programs = \ test-pop3-migration-plugin test_libs = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) test_deps = \ $(module_LTLIBRARIES) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) test_pop3_migration_plugin_SOURCES = test-pop3-migration-plugin.c test_pop3_migration_plugin_LDADD = pop3-migration-plugin.lo $(test_libs) test_pop3_migration_plugin_DEPENDENCIES = $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/pop3-migration/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/pop3-migration/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib05_pop3_migration_plugin.la: $(lib05_pop3_migration_plugin_la_OBJECTS) $(lib05_pop3_migration_plugin_la_DEPENDENCIES) $(EXTRA_lib05_pop3_migration_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib05_pop3_migration_plugin_la_LINK) -rpath $(moduledir) $(lib05_pop3_migration_plugin_la_OBJECTS) $(lib05_pop3_migration_plugin_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-pop3-migration-plugin$(EXEEXT): $(test_pop3_migration_plugin_OBJECTS) $(test_pop3_migration_plugin_DEPENDENCIES) $(EXTRA_test_pop3_migration_plugin_DEPENDENCIES) @rm -f test-pop3-migration-plugin$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_pop3_migration_plugin_OBJECTS) $(test_pop3_migration_plugin_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-migration-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-pop3-migration-plugin.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/pop3-migration/test-pop3-migration-plugin.c0000644000175000017500000000446113165463624022751 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "sha1.h" #include "hex-binary.h" #include "istream.h" #include "test-common.h" #include "pop3-migration-plugin.h" static void test_pop3_migration_get_hdr_sha1(void) { struct { const char *input; const char *sha1; bool have_eoh; } tests[] = { { "", "da39a3ee5e6b4b0d3255bfef95601890afd80709", FALSE }, { "\n", "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc", TRUE }, { "a: \r\n", "a3871371f2d468493005286282ae10549dab2c57", FALSE }, { "a: b\r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b \r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b \r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b \r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\r\n\r\n", "938b96404495cced816e3a4f6031734eab4e71b3", TRUE }, { "a: b\r\n\r\r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\r\n\r\r\nc: d\r\n\r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", TRUE }, { "a: b\r\n \r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\r\n \r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\r\n\t\r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\t\t\t\t\r\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\nfoo\n", "44ef6a20971148dd54a161f79814e22e2d098ddb", FALSE }, { "a: b\nc: d\n", "4dbea2c1bdd1323e15931382c1835200d9286230", FALSE }, { "a:b\nc:d\n", "4dbea2c1bdd1323e15931382c1835200d9286230", FALSE }, { "a: b\nfoo\nc: d\n", "4dbea2c1bdd1323e15931382c1835200d9286230", FALSE }, }; struct istream *input; unsigned char digest[SHA1_RESULTLEN]; unsigned int i; bool have_eoh; test_begin("pop3 migration get hdr sha1"); for (i = 0; i < N_ELEMENTS(tests); i++) { input = i_stream_create_from_data(tests[i].input, strlen(tests[i].input)); test_assert_idx(pop3_migration_get_hdr_sha1(1, input, digest, &have_eoh) == 0, i); test_assert_idx(strcasecmp(binary_to_hex(digest, sizeof(digest)), tests[i].sha1) == 0, i); test_assert_idx(tests[i].have_eoh == have_eoh, i); i_stream_unref(&input); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_pop3_migration_get_hdr_sha1, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/plugins/pop3-migration/pop3-migration-plugin.h0000644000175000017500000000052313123174404021761 00000000000000#ifndef POP3_MIGRATION_PLUGIN_H #define POP3_MIGRATION_PLUGIN_H struct module; void pop3_migration_plugin_init(struct module *module); void pop3_migration_plugin_deinit(void); int pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input, unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN], bool *have_eoh_r); #endif dovecot-2.2.33.2/src/plugins/pop3-migration/pop3-migration-plugin.c0000644000175000017500000007622313165463624022001 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "istream-header-filter.h" #include "str.h" #include "sha1.h" #include "message-size.h" #include "message-header-hash.h" #include "message-header-parser.h" #include "mail-cache.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "index-storage.h" #include "index-mail.h" #include "pop3-migration-plugin.h" #define POP3_MIGRATION_CONTEXT(obj) \ MODULE_CONTEXT(obj, pop3_migration_storage_module) #define POP3_MIGRATION_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, pop3_migration_mail_module) struct msg_map_common { /* sha1(header) - set only when needed */ unsigned char hdr_sha1[SHA1_RESULTLEN]; unsigned int hdr_sha1_set:1; }; struct pop3_uidl_map { struct msg_map_common common; uint32_t pop3_seq; uint32_t imap_uid; /* UIDL */ const char *pop3_uidl; /* LIST size */ uoff_t size; }; struct imap_msg_map { struct msg_map_common common; uint32_t uid, pop3_seq; uoff_t psize; const char *pop3_uidl; }; struct pop3_migration_mail_storage { union mail_storage_module_context module_ctx; const char *pop3_box_vname; ARRAY(struct pop3_uidl_map) pop3_uidl_map; unsigned int all_mailboxes:1; unsigned int pop3_all_hdr_sha1_set:1; unsigned int ignore_missing_uidls:1; unsigned int ignore_extra_uidls:1; unsigned int skip_size_check:1; unsigned int skip_uidl_cache:1; }; struct pop3_migration_mailbox { union mailbox_module_context module_ctx; ARRAY(struct imap_msg_map) imap_msg_map; unsigned int first_unfound_idx; struct mail_cache_field cache_field; unsigned int cache_field_registered:1; unsigned int uidl_synced:1; unsigned int uidl_sync_failed:1; }; /* NOTE: these headers must be sorted */ static const char *hdr_hash_skip_headers[] = { "Content-Length", "Return-Path", /* Yahoo IMAP has Return-Path, Yahoo POP3 doesn't */ "Status", "X-IMAP", "X-IMAPbase", "X-Keywords", "X-Message-Flag", "X-Status", "X-UID", "X-UIDL", "X-Yahoo-Newman-Property" }; const char *pop3_migration_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(pop3_migration_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(pop3_migration_mail_module, &mail_module_register); static int imap_msg_map_uid_cmp(const struct imap_msg_map *map1, const struct imap_msg_map *map2) { if (map1->uid < map2->uid) return -1; if (map1->uid > map2->uid) return 1; return 0; } static int pop3_uidl_map_pop3_seq_cmp(const struct pop3_uidl_map *map1, const struct pop3_uidl_map *map2) { if (map1->pop3_seq < map2->pop3_seq) return -1; if (map1->pop3_seq > map2->pop3_seq) return 1; return 0; } static int pop3_uidl_map_uidl_cmp(const struct pop3_uidl_map *map1, const struct pop3_uidl_map *map2) { return strcmp(map1->pop3_uidl, map2->pop3_uidl); } static int imap_msg_map_uidl_cmp(const struct imap_msg_map *map1, const struct imap_msg_map *map2) { return null_strcmp(map1->pop3_uidl, map2->pop3_uidl); } static int pop3_uidl_map_hdr_cmp(const struct pop3_uidl_map *map1, const struct pop3_uidl_map *map2) { return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1, sizeof(map1->common.hdr_sha1)); } static int imap_msg_map_hdr_cmp(const struct imap_msg_map *map1, const struct imap_msg_map *map2) { return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1, sizeof(map1->common.hdr_sha1)); } struct pop3_hdr_context { bool have_eoh; bool stop; }; static bool header_name_is_valid(const char *name) { unsigned int i; for (i = 0; name[i] != '\0'; i++) { if ((uint8_t)name[i] <= 0x20 || name[i] >= 0x7f) return FALSE; } return TRUE; } static bool header_value_want_skip(const struct message_header_line *hdr) { for (size_t i = 0; i < hdr->value_len; i++) { if (hdr->value[i] != ' ' && hdr->value[i] != '\t') return FALSE; } /* "header: \r\n \r\n" - Zimbra's BODY[HEADER] strips this line away. */ return TRUE; } static void pop3_header_filter_callback(struct header_filter_istream *input ATTR_UNUSED, struct message_header_line *hdr, bool *matched, struct pop3_hdr_context *ctx) { if (hdr == NULL) return; if (hdr->eoh) { ctx->have_eoh = TRUE; if (ctx->stop) { /* matched is handled differently for eoh by istream-header-filter. a design bug I guess.. */ *matched = FALSE; } } else { if (strspn(hdr->name, "\r") == hdr->name_len) { /* CR+CR+LF - some servers stop the header processing here while others don't. To make sure they can be matched correctly we want to stop here entirely. */ ctx->stop = TRUE; } else if (!hdr->continued && hdr->middle_len == 0) { /* not a valid "key: value" header - Zimbra's BODY[HEADER] strips this line away. */ *matched = TRUE; } else if (hdr->continued && header_value_want_skip(hdr)) { *matched = TRUE; } if (ctx->stop) *matched = TRUE; else if (!header_name_is_valid(hdr->name)) { /* Yahoo IMAP drops headers with invalid names, while Yahoo POP3 preserves them. Drop them all. */ *matched = TRUE; } } } int pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input, unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN], bool *have_eoh_r) { const unsigned char *data; size_t size; struct message_header_hash_context hash_ctx; struct sha1_ctxt sha1_ctx; struct pop3_hdr_context hdr_ctx; i_zero(&hdr_ctx); /* hide headers that might change or be different in IMAP vs. POP3 */ input = i_stream_create_header_filter(input, HEADER_FILTER_HIDE_BODY | HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hdr_hash_skip_headers, N_ELEMENTS(hdr_hash_skip_headers), pop3_header_filter_callback, &hdr_ctx); sha1_init(&sha1_ctx); i_zero(&hash_ctx); while (i_stream_read_data(input, &data, &size, 0) > 0) { message_header_hash_more(&hash_ctx, &hash_method_sha1, &sha1_ctx, MESSAGE_HEADER_HASH_MAX_VERSION, data, size); i_stream_skip(input, size); } if (input->stream_errno != 0) { i_error("pop3_migration: Failed to read header for msg %u: %s", mail_seq, i_stream_get_error(input)); i_stream_unref(&input); return -1; } sha1_result(&sha1_ctx, sha1_r); i_stream_unref(&input); *have_eoh_r = hdr_ctx.have_eoh; return 0; } static unsigned int get_cache_idx(struct mail *mail) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(mail->box); if (mbox->cache_field_registered) return mbox->cache_field.idx; mbox->cache_field.name = "pop3-migration.hdr"; mbox->cache_field.type = MAIL_CACHE_FIELD_FIXED_SIZE; mbox->cache_field.field_size = SHA1_RESULTLEN; mail_cache_register_fields(mail->box->cache, &mbox->cache_field, 1); mbox->cache_field_registered = TRUE; return mbox->cache_field.idx; } static int get_hdr_sha1(struct mail *mail, unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN]) { struct istream *input; const char *errstr; enum mail_error error; bool have_eoh; int ret; if (mail_get_hdr_stream(mail, NULL, &input) < 0) { errstr = mailbox_get_last_internal_error(mail->box, &error); i_error("pop3_migration: Failed to get header for msg %u: %s", mail->seq, errstr); return error == MAIL_ERROR_EXPUNGED ? 0 : -1; } if (pop3_migration_get_hdr_sha1(mail->seq, input, sha1_r, &have_eoh) < 0) return -1; if (have_eoh) { struct index_mail *imail = (struct index_mail *)mail; index_mail_cache_add_idx(imail, get_cache_idx(mail), sha1_r, SHA1_RESULTLEN); return 1; } /* The empty "end of headers" line is missing. Either this means that the headers ended unexpectedly (which is ok) or that the remote server is buggy. Some servers have problems with 1) header line continuations that contain only whitespace and 2) headers that have no ":". The header gets truncated when such line is reached. At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not returning the whitespace line and 2) by returning the line but truncating the rest. POP3 TOP instead returns the entire header. This causes the IMAP and POP3 hashes not to match. If there's LF+CR+CR+LF in the middle of headers, Courier IMAP's FETCH BODY[HEADER] stops after that, but Courier POP3's TOP doesn't. So we'll try to avoid this by falling back to full FETCH BODY[] (and/or RETR) and we'll parse the header ourself from it. This should work around any similar bugs in all IMAP/POP3 servers. */ if (mail_get_stream_because(mail, NULL, NULL, "pop3-migration", &input) < 0) { errstr = mailbox_get_last_internal_error(mail->box, &error); i_error("pop3_migration: Failed to get body for msg %u: %s", mail->seq, errstr); return error == MAIL_ERROR_EXPUNGED ? 0 : -1; } ret = pop3_migration_get_hdr_sha1(mail->seq, input, sha1_r, &have_eoh); if (ret == 0) { if (!have_eoh) i_warning("pop3_migration: Truncated email with UID %u stored as truncated", mail->uid); struct index_mail *imail = (struct index_mail *)mail; index_mail_cache_add_idx(imail, get_cache_idx(mail), sha1_r, SHA1_RESULTLEN); return 1; } else { return -1; } } static bool get_cached_hdr_sha1(struct mail *mail, buffer_t *cache_buf, unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN]) { struct index_mail *imail = (struct index_mail *)mail; buffer_set_used_size(cache_buf, 0); if (index_mail_cache_lookup_field(imail, cache_buf, get_cache_idx(mail)) > 0 && cache_buf->used == SHA1_RESULTLEN) { memcpy(sha1_r, cache_buf->data, cache_buf->used); return TRUE; } return FALSE; } static struct mailbox *pop3_mailbox_alloc(struct mail_storage *storage) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(storage); struct mail_namespace *ns; struct mailbox *box; ns = mail_namespace_find(storage->user->namespaces, mstorage->pop3_box_vname); i_assert(ns != NULL); box = mailbox_alloc(ns->list, mstorage->pop3_box_vname, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_POP3_SESSION); mailbox_set_reason(box, "pop3_migration"); return box; } static int pop3_map_read(struct mail_storage *storage, struct mailbox *pop3_box) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(storage); struct mailbox_transaction_context *t; struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; struct pop3_uidl_map *map; const char *uidl; uoff_t size = (uoff_t)-1; int ret = 0; if (array_is_created(&mstorage->pop3_uidl_map)) { /* already read these, just reset the imap_uids */ array_foreach_modifiable(&mstorage->pop3_uidl_map, map) map->imap_uid = 0; return 0; } i_array_init(&mstorage->pop3_uidl_map, 128); if (mailbox_sync(pop3_box, 0) < 0) { i_error("pop3_migration: Couldn't sync mailbox %s: %s", pop3_box->vname, mailbox_get_last_internal_error(pop3_box, NULL)); return -1; } t = mailbox_transaction_begin(pop3_box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); ctx = mailbox_search_init(t, search_args, NULL, mstorage->skip_size_check ? 0 : MAIL_FETCH_PHYSICAL_SIZE, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { /* get the size with LIST instead of RETR */ mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; if (mstorage->skip_size_check) ; else if (mail_get_physical_size(mail, &size) < 0) { i_error("pop3_migration: Failed to get size for msg %u: %s", mail->seq, mailbox_get_last_internal_error(pop3_box, NULL)); ret = -1; break; } mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) < 0) { i_error("pop3_migration: Failed to get UIDL for msg %u: %s", mail->seq, mailbox_get_last_internal_error(pop3_box, NULL)); ret = -1; break; } if (*uidl == '\0') { i_warning("pop3_migration: UIDL for msg %u is empty", mail->seq); continue; } map = array_append_space(&mstorage->pop3_uidl_map); map->pop3_seq = mail->seq; map->pop3_uidl = p_strdup(storage->pool, uidl); map->size = size; } if (mailbox_search_deinit(&ctx) < 0) { i_error("pop3_migration: Failed to search all POP3 mails: %s", mailbox_get_last_internal_error(pop3_box, NULL)); ret = -1; } (void)mailbox_transaction_commit(&t); return ret; } static void pop3_map_read_cached_hdr_hashes(struct mailbox_transaction_context *t, struct mail_search_args *search_args, struct array *msg_map) { struct mail_search_context *ctx; struct mail *mail; struct msg_map_common *map; buffer_t *cache_buf; ctx = mailbox_search_init(t, search_args, NULL, 0, NULL); cache_buf = buffer_create_dynamic(pool_datastack_create(), SHA1_RESULTLEN); while (mailbox_search_next(ctx, &mail)) { map = array_idx_modifiable_i(msg_map, mail->seq-1); if (get_cached_hdr_sha1(mail, cache_buf, map->hdr_sha1)) map->hdr_sha1_set = TRUE; } if (mailbox_search_deinit(&ctx) < 0) { i_warning("pop3_migration: Failed to search all cached POP3 header hashes: %s - ignoring", mailbox_get_last_internal_error(t->box, NULL)); } } static void map_remove_found_seqs(struct mail_search_arg *search_arg, struct array *msg_map, uint32_t seq1) { const struct msg_map_common *map; uint32_t seq, count = array_count_i(msg_map); i_assert(search_arg->type == SEARCH_SEQSET); for (seq = seq1; seq <= count; seq++) { map = array_idx_i(msg_map, seq-1); if (map->hdr_sha1_set) seq_range_array_remove(&search_arg->value.seqset, seq); } } static int map_read_hdr_hashes(struct mailbox *box, struct array *msg_map, uint32_t seq1) { struct mailbox_transaction_context *t; struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; struct msg_map_common *map; int ret = 0; t = mailbox_transaction_begin(box, 0); /* get all the cached hashes */ search_args = mail_search_build_init(); mail_search_build_add_seqset(search_args, seq1, array_count_i(msg_map)); pop3_map_read_cached_hdr_hashes(t, search_args, msg_map); /* read all the non-cached hashes. doing this in two passes allows us to set wanted_fields=MAIL_FETCH_STREAM_HEADER, which allows prefetching to work without downloading all the headers even for mails that already are cached. */ map_remove_found_seqs(search_args->args, msg_map, seq1); ctx = mailbox_search_init(t, search_args, NULL, MAIL_FETCH_STREAM_HEADER, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { map = array_idx_modifiable_i(msg_map, mail->seq-1); if ((ret = get_hdr_sha1(mail, map->hdr_sha1)) < 0) { ret = -1; break; } if (ret > 0) map->hdr_sha1_set = TRUE; } if (mailbox_search_deinit(&ctx) < 0) { i_error("pop3_migration: Failed to search all mail headers: %s", mailbox_get_last_internal_error(box, NULL)); ret = -1; } (void)mailbox_transaction_commit(&t); return ret < 0 ? -1 : 0; } static int pop3_map_read_hdr_hashes(struct mail_storage *storage, struct mailbox *pop3_box, unsigned first_seq) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(storage); if (mstorage->pop3_all_hdr_sha1_set) return 0; if (mstorage->all_mailboxes) { /* we may be matching against multiple mailboxes. read all the hashes only once. */ first_seq = 1; } if (map_read_hdr_hashes(pop3_box, &mstorage->pop3_uidl_map.arr, first_seq) < 0) return -1; if (first_seq == 1) mstorage->pop3_all_hdr_sha1_set = TRUE; return 0; } static int imap_map_read(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); const unsigned int uidl_cache_idx = ibox->cache_fields[MAIL_CACHE_POP3_UIDL].idx; struct mailbox_status status; struct mailbox_transaction_context *t; struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; struct imap_msg_map *map; uoff_t psize = (uoff_t)-1; string_t *uidl; int ret = 0; mailbox_get_open_status(box, STATUS_MESSAGES, &status); i_assert(!array_is_created(&mbox->imap_msg_map)); p_array_init(&mbox->imap_msg_map, box->pool, status.messages); t = mailbox_transaction_begin(box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); ctx = mailbox_search_init(t, search_args, NULL, mstorage->skip_size_check ? 0 : MAIL_FETCH_PHYSICAL_SIZE, NULL); mail_search_args_unref(&search_args); uidl = t_str_new(64); while (mailbox_search_next(ctx, &mail)) { if (mstorage->skip_size_check) ; else if (mail_get_physical_size(mail, &psize) < 0) { i_error("pop3_migration: Failed to get psize for imap uid %u: %s", mail->uid, mailbox_get_last_internal_error(box, NULL)); ret = -1; break; } if (!mstorage->skip_uidl_cache) { str_truncate(uidl, 0); (void)mail_cache_lookup_field(mail->transaction->cache_view, uidl, mail->seq, uidl_cache_idx); } map = array_append_space(&mbox->imap_msg_map); map->uid = mail->uid; map->psize = psize; map->pop3_uidl = p_strdup_empty(box->pool, str_c(uidl)); } if (mailbox_search_deinit(&ctx) < 0) { i_error("pop3_migration: Failed to search all IMAP mails: %s", mailbox_get_last_internal_error(box, NULL)); ret = -1; } (void)mailbox_transaction_commit(&t); return ret; } static int imap_map_read_hdr_hashes(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); return map_read_hdr_hashes(box, &mbox->imap_msg_map.arr, mbox->first_unfound_idx+1); } static void pop3_uidl_assign_cached(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); struct pop3_uidl_map *pop3_map; struct imap_msg_map *imap_map; unsigned int imap_idx, pop3_idx, pop3_count, imap_count; int ret; if (mstorage->skip_uidl_cache) return; array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_uidl_cmp); array_sort(&mbox->imap_msg_map, imap_msg_map_uidl_cmp); pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count); imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count); /* see if we can match the messages using sizes */ for (imap_idx = pop3_idx = 0; imap_idx < imap_count; imap_idx++) { if (imap_map[imap_idx].pop3_uidl == NULL) continue; ret = 1; for (; pop3_idx < pop3_count; pop3_idx++) { ret = strcmp(imap_map[imap_idx].pop3_uidl, pop3_map[pop3_idx].pop3_uidl); if (ret >= 0) break; } if (ret == 0) { imap_map[imap_idx].pop3_seq = pop3_map[pop3_idx].pop3_seq; pop3_map[pop3_idx].imap_uid = imap_map[imap_idx].uid; } } } static bool pop3_uidl_assign_by_size(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); struct pop3_uidl_map *pop3_map; struct imap_msg_map *imap_map; unsigned int i, pop3_count, imap_count, count; unsigned int size_match = 0, uidl_match = 0; pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count); imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count); count = I_MIN(pop3_count, imap_count); /* see if we can match the messages using sizes */ for (i = 0; i < count; i++) { if (imap_map[i].pop3_uidl != NULL) { /* some of the UIDLs were already found cached. */ if (strcmp(pop3_map[i].pop3_uidl, imap_map[i].pop3_uidl) == 0) { uidl_match++; continue; } /* mismatch - can't trust the sizes */ break; } if (pop3_map[i].size != imap_map[i].psize || mstorage->skip_size_check) break; if (i+1 < count && pop3_map[i].size == pop3_map[i+1].size) { /* two messages with same size, don't trust them */ break; } size_match++; pop3_map[i].imap_uid = imap_map[i].uid; imap_map[i].pop3_uidl = pop3_map[i].pop3_uidl; imap_map[i].pop3_seq = pop3_map[i].pop3_seq; } mbox->first_unfound_idx = i; if (box->storage->user->mail_debug) { i_debug("pop3_migration: cached uidls=%u, size matches=%u, total=%u", uidl_match, size_match, count); } return i == count && imap_count == pop3_count; } static int pop3_uidl_assign_by_hdr_hash(struct mailbox *box, struct mailbox *pop3_box) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct pop3_uidl_map *pop3_map; struct imap_msg_map *imap_map; unsigned int pop3_idx, imap_idx, pop3_count, imap_count; unsigned int first_seq, missing_uids_count; uint32_t first_missing_idx = 0, first_missing_seq = (uint32_t)-1; int ret; first_seq = mbox->first_unfound_idx+1; if (pop3_map_read_hdr_hashes(box->storage, pop3_box, first_seq) < 0 || imap_map_read_hdr_hashes(box) < 0) return -1; array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_hdr_cmp); array_sort(&mbox->imap_msg_map, imap_msg_map_hdr_cmp); pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count); imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count); pop3_idx = imap_idx = 0; while (pop3_idx < pop3_count && imap_idx < imap_count) { if (!pop3_map[pop3_idx].common.hdr_sha1_set || pop3_map[pop3_idx].imap_uid != 0) { pop3_idx++; continue; } if (!imap_map[imap_idx].common.hdr_sha1_set || imap_map[imap_idx].pop3_uidl != NULL) { imap_idx++; continue; } ret = memcmp(pop3_map[pop3_idx].common.hdr_sha1, imap_map[imap_idx].common.hdr_sha1, sizeof(pop3_map[pop3_idx].common.hdr_sha1)); if (ret < 0) pop3_idx++; else if (ret > 0) imap_idx++; else { pop3_map[pop3_idx].imap_uid = imap_map[imap_idx].uid; imap_map[imap_idx].pop3_uidl = pop3_map[pop3_idx].pop3_uidl; imap_map[imap_idx].pop3_seq = pop3_map[pop3_idx].pop3_seq; } } missing_uids_count = 0; for (pop3_idx = 0; pop3_idx < pop3_count; pop3_idx++) { if (pop3_map[pop3_idx].imap_uid != 0) { /* matched */ } else if (!pop3_map[pop3_idx].common.hdr_sha1_set) { /* we treated this mail as expunged - ignore */ } else { uint32_t seq = pop3_map[pop3_idx].pop3_seq; if (first_missing_seq > seq) { first_missing_seq = seq; first_missing_idx = pop3_idx; } missing_uids_count++; } } if (missing_uids_count > 0 && !mstorage->all_mailboxes) { string_t *str = t_str_new(128); bool all_imap_mails_found = FALSE; str_printfa(str, "pop3_migration: %u POP3 messages have no " "matching IMAP messages (first POP3 msg %u UIDL %s)", missing_uids_count, first_missing_seq, pop3_map[first_missing_idx].pop3_uidl); if (imap_count + missing_uids_count == pop3_count) { str_append(str, " - all IMAP messages were found " "(POP3 contains more than IMAP INBOX - you may want to set pop3_migration_all_mailboxes=yes)"); all_imap_mails_found = TRUE; } if (all_imap_mails_found && mstorage->ignore_extra_uidls) { /* pop3 had more mails than imap. maybe it was just that a new mail was just delivered. */ } else if (!mstorage->ignore_missing_uidls) { str_append(str, " - set pop3_migration_ignore_missing_uidls=yes"); if (all_imap_mails_found) str_append(str, " or pop3_migration_ignore_extra_uidls=yes"); i_error("%s to continue anyway", str_c(str)); return -1; } i_warning("%s", str_c(str)); } else if (box->storage->user->mail_debug) { i_debug("pop3_migration: %u mails matched by headers", pop3_count); } array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_pop3_seq_cmp); array_sort(&mbox->imap_msg_map, imap_msg_map_uid_cmp); return 0; } static void imap_uidls_add_to_cache(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct mailbox_transaction_context *t; struct mail *mail; struct index_mail *imail; struct imap_msg_map *imap_map; unsigned int i, count; unsigned int field_idx; t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, 0, NULL); imail = (struct index_mail *)mail; field_idx = imail->ibox->cache_fields[MAIL_CACHE_POP3_UIDL].idx; imap_map = array_get_modifiable(&mbox->imap_msg_map, &count); for (i = 0; i < count; i++) { if (imap_map[i].pop3_uidl == NULL) continue; if (!mail_set_uid(mail, imap_map[i].uid)) i_unreached(); if (mail_cache_field_can_add(t->cache_trans, mail->seq, field_idx)) { index_mail_cache_add_idx(imail, field_idx, imap_map[i].pop3_uidl, strlen(imap_map[i].pop3_uidl)+1); } } mail_free(&mail); (void)mailbox_transaction_commit(&t); } static int pop3_migration_uidl_sync(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); struct mailbox *pop3_box; pop3_box = pop3_mailbox_alloc(box->storage); /* the POP3 server isn't connected to yet. handle all IMAP traffic first before connecting, so POP3 server won't disconnect us due to idling. */ if (imap_map_read(box) < 0 || pop3_map_read(box->storage, pop3_box) < 0) { mailbox_free(&pop3_box); return -1; } pop3_uidl_assign_cached(box); array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_pop3_seq_cmp); array_sort(&mbox->imap_msg_map, imap_msg_map_uid_cmp); if (!pop3_uidl_assign_by_size(box)) { /* everything wasn't assigned, figure out the rest with header hashes */ if (pop3_uidl_assign_by_hdr_hash(box, pop3_box) < 0) { mailbox_free(&pop3_box); return -1; } } if (!mstorage->skip_uidl_cache) imap_uidls_add_to_cache(box); mbox->uidl_synced = TRUE; mailbox_free(&pop3_box); return 0; } static int pop3_migration_uidl_sync_if_needed(struct mailbox *box) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box); if (mbox->uidl_synced) return 0; if (mbox->uidl_sync_failed || pop3_migration_uidl_sync(box) < 0) { mbox->uidl_sync_failed = TRUE; mail_storage_set_error(box->storage, MAIL_ERROR_TEMP, "POP3 UIDLs couldn't be synced"); return -1; } return 0; } static int pop3_migration_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *mmail = POP3_MIGRATION_MAIL_CONTEXT(mail); struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(_mail->box); struct imap_msg_map map_key, *map; if (field == MAIL_FETCH_UIDL_BACKEND || field == MAIL_FETCH_POP3_ORDER) { if (pop3_migration_uidl_sync_if_needed(_mail->box) < 0) return -1; i_zero(&map_key); map_key.uid = _mail->uid; map = array_bsearch(&mbox->imap_msg_map, &map_key, imap_msg_map_uid_cmp); if (map != NULL && map->pop3_uidl != NULL) { if (field == MAIL_FETCH_UIDL_BACKEND) *value_r = map->pop3_uidl; else *value_r = t_strdup_printf("%u", map->pop3_seq); return 0; } /* not found from POP3 server, fallback to default */ } return mmail->super.get_special(_mail, field, value_r); } static void pop3_migration_mail_allocated(struct mail *_mail) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(_mail->box->storage); struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *mmail; struct mail_namespace *ns; if (mstorage == NULL || (!mstorage->all_mailboxes && !_mail->box->inbox_user)) { /* assigns UIDLs only for INBOX */ return; } ns = mail_namespace_find(_mail->box->storage->user->namespaces, mstorage->pop3_box_vname); if (ns == mailbox_get_namespace(_mail->box)) { /* we're accessing the pop3-migration namespace itself */ return; } mmail = p_new(mail->pool, union mail_module_context, 1); mmail->super = *v; mail->vlast = &mmail->super; v->get_special = pop3_migration_get_special; MODULE_CONTEXT_SET_SELF(mail, pop3_migration_mail_module, mmail); } static struct mail_search_context * pop3_migration_mailbox_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(t->box); struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(t->box->storage); if ((wanted_fields & (MAIL_FETCH_UIDL_BACKEND | MAIL_FETCH_POP3_ORDER)) != 0 && (mstorage->all_mailboxes || t->box->inbox_user)) { /* Start POP3 UIDL syncing before the search, so we'll do it before we start sending any FETCH BODY[]s to IMAP. It shouldn't matter much, except this works around a bug in Yahoo IMAP where it sometimes breaks its state when doing a FETCH BODY[] followed by FETCH BODY[HEADER].. */ (void)pop3_migration_uidl_sync_if_needed(t->box); } return mbox->module_ctx.super.search_init(t, args, sort_program, wanted_fields, wanted_headers); } static void pop3_migration_mailbox_allocated(struct mailbox *box) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(box->storage); struct mailbox_vfuncs *v = box->vlast; struct pop3_migration_mailbox *mbox; if (mstorage == NULL) return; mbox = p_new(box->pool, struct pop3_migration_mailbox, 1); mbox->module_ctx.super = *v; box->vlast = &mbox->module_ctx.super; v->search_init = pop3_migration_mailbox_search_init; MODULE_CONTEXT_SET(box, pop3_migration_storage_module, mbox); } static void pop3_migration_mail_storage_destroy(struct mail_storage *storage) { struct pop3_migration_mail_storage *mstorage = POP3_MIGRATION_CONTEXT(storage); if (array_is_created(&mstorage->pop3_uidl_map)) array_free(&mstorage->pop3_uidl_map); mstorage->module_ctx.super.destroy(storage); } static void pop3_migration_mail_storage_created(struct mail_storage *storage) { struct pop3_migration_mail_storage *mstorage; struct mail_storage_vfuncs *v = storage->vlast; const char *pop3_box_vname; pop3_box_vname = mail_user_plugin_getenv(storage->user, "pop3_migration_mailbox"); if (pop3_box_vname == NULL) { if (storage->user->mail_debug) i_debug("pop3_migration: No pop3_migration_mailbox setting - disabled"); return; } mstorage = p_new(storage->pool, struct pop3_migration_mail_storage, 1); mstorage->module_ctx.super = *v; storage->vlast = &mstorage->module_ctx.super; v->destroy = pop3_migration_mail_storage_destroy; mstorage->pop3_box_vname = p_strdup(storage->pool, pop3_box_vname); mstorage->all_mailboxes = mail_user_plugin_getenv(storage->user, "pop3_migration_all_mailboxes") != NULL; mstorage->ignore_missing_uidls = mail_user_plugin_getenv(storage->user, "pop3_migration_ignore_missing_uidls") != NULL; mstorage->ignore_extra_uidls = mail_user_plugin_getenv(storage->user, "pop3_migration_ignore_extra_uidls") != NULL; mstorage->skip_size_check = mail_user_plugin_getenv(storage->user, "pop3_migration_skip_size_check") != NULL; mstorage->skip_uidl_cache = mail_user_plugin_getenv(storage->user, "pop3_migration_skip_uidl_cache") != NULL; MODULE_CONTEXT_SET(storage, pop3_migration_storage_module, mstorage); } static struct mail_storage_hooks pop3_migration_mail_storage_hooks = { .mail_allocated = pop3_migration_mail_allocated, .mailbox_allocated = pop3_migration_mailbox_allocated, .mail_storage_created = pop3_migration_mail_storage_created }; void pop3_migration_plugin_init(struct module *module) { mail_storage_hooks_add(module, &pop3_migration_mail_storage_hooks); } void pop3_migration_plugin_deinit(void) { mail_storage_hooks_remove(&pop3_migration_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/pop3-migration/Makefile.am0000644000175000017500000000200213123174404017472 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index NOPLUGIN_LDFLAGS = lib05_pop3_migration_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib05_pop3_migration_plugin.la lib05_pop3_migration_plugin_la_SOURCES = \ pop3-migration-plugin.c noinst_HEADERS = \ pop3-migration-plugin.h noinst_PROGRAMS = $(test_programs) test_programs = \ test-pop3-migration-plugin test_libs = \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) test_deps = \ $(module_LTLIBRARIES) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) test_pop3_migration_plugin_SOURCES = test-pop3-migration-plugin.c test_pop3_migration_plugin_LDADD = pop3-migration-plugin.lo $(test_libs) test_pop3_migration_plugin_DEPENDENCIES = $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/plugins/fts-lucene/0002755000175000017500000000000013172375613014734 500000000000000dovecot-2.2.33.2/src/plugins/fts-lucene/doveadm-fts-lucene.c0000644000175000017500000000327513123174404020476 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "doveadm-dump.h" #include "doveadm-fts.h" #include "lucene-wrapper.h" #include #include const char *doveadm_fts_lucene_plugin_version = DOVECOT_ABI_VERSION; void doveadm_fts_lucene_plugin_init(struct module *module); void doveadm_fts_lucene_plugin_deinit(void); static void cmd_dump_fts_lucene(int argc ATTR_UNUSED, char *argv[]) { struct lucene_index *index; struct lucene_index_iter *iter; guid_128_t prev_guid; const struct lucene_index_record *rec; bool first = TRUE; i_zero(&prev_guid); index = lucene_index_init(argv[1], NULL, NULL); iter = lucene_index_iter_init(index); while ((rec = lucene_index_iter_next(iter)) != NULL) { if (memcmp(prev_guid, rec->mailbox_guid, sizeof(prev_guid)) != 0) { if (first) first = FALSE; else printf("\n"); memcpy(prev_guid, rec->mailbox_guid, sizeof(prev_guid)); printf("%s: ", guid_128_to_string(prev_guid)); } printf("%u", rec->uid); if (rec->part_num != 0) printf("[%u]", rec->part_num); printf("\n"); } printf("\n"); if (lucene_index_iter_deinit(&iter) < 0) i_error("Lucene index iteration failed"); lucene_index_deinit(index); } static bool test_dump_fts_lucene(const char *path) { struct stat st; path = t_strconcat(path, "/segments.gen", NULL); return stat(path, &st) == 0; } static const struct doveadm_cmd_dump doveadm_cmd_dump_fts_lucene = { "fts-lucene", test_dump_fts_lucene, cmd_dump_fts_lucene }; void doveadm_fts_lucene_plugin_init(struct module *module ATTR_UNUSED) { doveadm_dump_register(&doveadm_cmd_dump_fts_lucene); } void doveadm_fts_lucene_plugin_deinit(void) { } dovecot-2.2.33.2/src/plugins/fts-lucene/SnowballFilter.h0000644000175000017500000000256713123174404017753 00000000000000/*------------------------------------------------------------------------------ * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team * * Distributable under the terms of either the Apache License (Version 2.0) or * the GNU Lesser General Public License, as specified in the COPYING file. ------------------------------------------------------------------------------*/ #ifndef _lucene_analysis_snowball_filter_ #define _lucene_analysis_snowball_filter_ #include "CLucene/analysis/AnalysisHeader.h" #include "libstemmer.h" CL_NS_DEF2(analysis,snowball) /** A filter that stems words using a Snowball-generated stemmer. * * Available stemmers are listed in {@link net.sf.snowball.ext}. The name of a * stemmer is the part of the class name before "Stemmer", e.g., the stemmer in * {@link EnglishStemmer} is named "English". * * Note: todo: This is not thread safe... */ class CLUCENE_CONTRIBS_EXPORT SnowballFilter: public TokenFilter { struct sb_stemmer * stemmer; normalizer_func_t *normalizer; public: /** Construct the named stemming filter. * * @param in the input tokens to stem * @param name the name of a stemmer */ SnowballFilter(TokenStream* in, normalizer_func_t *normalizer, const char* language, bool deleteTS); ~SnowballFilter(); /** Returns the next input Token, after being stemmed */ Token* next(Token* token); }; CL_NS_END2 #endif dovecot-2.2.33.2/src/plugins/fts-lucene/SnowballAnalyzer.h0000644000175000017500000000355213123174404020306 00000000000000/*------------------------------------------------------------------------------ * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team * * Distributable under the terms of either the Apache License (Version 2.0) or * the GNU Lesser General Public License, as specified in the COPYING file. ------------------------------------------------------------------------------*/ #ifndef _lucene_analysis_snowball_analyser_ #define _lucene_analysis_snowball_analyser_ extern "C" { #include "lib.h" #include "unichar.h" }; #include "CLucene/analysis/AnalysisHeader.h" CL_CLASS_DEF(util,BufferedReader) CL_NS_DEF2(analysis,snowball) /** Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link * LowerCaseFilter}, {@link StopFilter} and {@link SnowballFilter}. * * Available stemmers are listed in {@link net.sf.snowball.ext}. The name of a * stemmer is the part of the class name before "Stemmer", e.g., the stemmer in * {@link EnglishStemmer} is named "English". */ class CLUCENE_CONTRIBS_EXPORT SnowballAnalyzer: public Analyzer { char* language; normalizer_func_t *normalizer; CLTCSetList* stopSet; TokenStream *prevstream; public: /** Builds the named analyzer with no stop words. */ SnowballAnalyzer(normalizer_func_t *normalizer, const char* language="english"); /** Builds the named analyzer with the given stop words. */ SnowballAnalyzer(const char* language, const TCHAR** stopWords); ~SnowballAnalyzer(); /** Constructs a {@link StandardTokenizer} filtered by a {@link StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */ TokenStream* tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader); TokenStream* tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader, bool deleteReader); TokenStream* reusableTokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader); }; CL_NS_END2 #endif dovecot-2.2.33.2/src/plugins/fts-lucene/Makefile.in0000644000175000017500000007401213172375574016731 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/fts-lucene ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(exampledir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) lib20_doveadm_fts_lucene_plugin_la_LIBADD = am_lib20_doveadm_fts_lucene_plugin_la_OBJECTS = doveadm-fts-lucene.lo lib20_doveadm_fts_lucene_plugin_la_OBJECTS = \ $(am_lib20_doveadm_fts_lucene_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_doveadm_fts_lucene_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) \ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(lib20_doveadm_fts_lucene_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = @BUILD_FTS_EXTTEXTCAT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) lib21_fts_lucene_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) am__lib21_fts_lucene_plugin_la_SOURCES_DIST = fts-lucene-plugin.c \ fts-backend-lucene.c lucene-wrapper.cc Snowball.cc @BUILD_FTS_STEMMER_TRUE@am__objects_1 = Snowball.lo am_lib21_fts_lucene_plugin_la_OBJECTS = fts-lucene-plugin.lo \ fts-backend-lucene.lo lucene-wrapper.lo $(am__objects_1) lib21_fts_lucene_plugin_la_OBJECTS = \ $(am_lib21_fts_lucene_plugin_la_OBJECTS) lib21_fts_lucene_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) \ $(lib21_fts_lucene_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(lib20_doveadm_fts_lucene_plugin_la_SOURCES) \ $(lib21_fts_lucene_plugin_la_SOURCES) DIST_SOURCES = $(lib20_doveadm_fts_lucene_plugin_la_SOURCES) \ $(am__lib21_fts_lucene_plugin_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(example_DATA) HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts \ -I$(top_srcdir)/src/doveadm AM_CXXFLAGS = \ $(CLUCENE_CFLAGS) \ $(LIBEXTTEXTCAT_CFLAGS) lib21_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version lib20_doveadm_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_lucene_plugin.la @BUILD_FTS_STEMMER_TRUE@STEMMER_LIBS = -lstemmer @BUILD_FTS_STEMMER_TRUE@SHOWBALL_SOURCES = Snowball.cc @BUILD_FTS_EXTTEXTCAT_FALSE@@BUILD_FTS_TEXTCAT_TRUE@TEXTCAT_LIBS = -ltextcat @BUILD_FTS_EXTTEXTCAT_TRUE@TEXTCAT_LIBS = $(LIBEXTTEXTCAT_LIBS) lib21_fts_lucene_plugin_la_LIBADD = \ $(CLUCENE_LIBS) $(TEXTCAT_LIBS) $(STEMMER_LIBS) lib21_fts_lucene_plugin_la_SOURCES = \ fts-lucene-plugin.c \ fts-backend-lucene.c \ lucene-wrapper.cc \ $(SHOWBALL_SOURCES) noinst_HEADERS = \ fts-lucene-plugin.h \ lucene-wrapper.h \ SnowballAnalyzer.h \ SnowballFilter.h @BUILD_FTS_TEXTCAT_TRUE@exampledir = $(docdir)/example-config @BUILD_FTS_TEXTCAT_TRUE@example_DATA = \ @BUILD_FTS_TEXTCAT_TRUE@ textcat.conf EXTRA_DIST = textcat.conf doveadm_module_LTLIBRARIES = \ lib20_doveadm_fts_lucene_plugin.la lib20_doveadm_fts_lucene_plugin_la_SOURCES = \ doveadm-fts-lucene.c all: all-am .SUFFIXES: .SUFFIXES: .c .cc .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/fts-lucene/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/fts-lucene/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_doveadm_fts_lucene_plugin.la: $(lib20_doveadm_fts_lucene_plugin_la_OBJECTS) $(lib20_doveadm_fts_lucene_plugin_la_DEPENDENCIES) $(EXTRA_lib20_doveadm_fts_lucene_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_doveadm_fts_lucene_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib20_doveadm_fts_lucene_plugin_la_OBJECTS) $(lib20_doveadm_fts_lucene_plugin_la_LIBADD) $(LIBS) lib21_fts_lucene_plugin.la: $(lib21_fts_lucene_plugin_la_OBJECTS) $(lib21_fts_lucene_plugin_la_DEPENDENCIES) $(EXTRA_lib21_fts_lucene_plugin_la_DEPENDENCIES) $(AM_V_CXXLD)$(lib21_fts_lucene_plugin_la_LINK) -rpath $(moduledir) $(lib21_fts_lucene_plugin_la_OBJECTS) $(lib21_fts_lucene_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Snowball.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-fts-lucene.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-backend-lucene.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-lucene-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lucene-wrapper.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-exampleDATA: $(example_DATA) @$(NORMAL_INSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(exampledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(exampledir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(exampledir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(exampledir)" || exit $$?; \ done uninstall-exampleDATA: @$(NORMAL_UNINSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(exampledir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(exampledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES install-exampleDATA \ install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-exampleDATA uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-doveadm_moduleLTLIBRARIES install-dvi install-dvi-am \ install-exampleDATA install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES uninstall-exampleDATA \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/fts-lucene/fts-lucene-plugin.c0000644000175000017500000001036113123174404020347 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "crc32.h" #include "mail-storage-hooks.h" #include "lucene-wrapper.h" #include "fts-user.h" #include "fts-lucene-plugin.h" const char *fts_lucene_plugin_version = DOVECOT_ABI_VERSION; struct fts_lucene_user_module fts_lucene_user_module = MODULE_CONTEXT_INIT(&mail_user_module_register); static int fts_lucene_plugin_init_settings(struct mail_user *user, struct fts_lucene_settings *set, const char *str) { const char *const *tmp; for (tmp = t_strsplit_spaces(str, " "); *tmp != NULL; tmp++) { if (strncmp(*tmp, "default_language=", 17) == 0) { set->default_language = p_strdup(user->pool, *tmp + 17); } else if (strncmp(*tmp, "textcat_conf=", 13) == 0) { set->textcat_conf = p_strdup(user->pool, *tmp + 13); } else if (strncmp(*tmp, "textcat_dir=", 12) == 0) { set->textcat_dir = p_strdup(user->pool, *tmp + 12); } else if (strncmp(*tmp, "whitespace_chars=", 17) == 0) { set->whitespace_chars = p_strdup(user->pool, *tmp + 17); } else if (strcmp(*tmp, "normalize") == 0) { set->normalize = TRUE; } else if (strcmp(*tmp, "no_snowball") == 0) { set->no_snowball = TRUE; } else if (strcmp(*tmp, "mime_parts") == 0) { set->mime_parts = TRUE; } else if (strcmp(*tmp, "use_libfts") == 0) { set->use_libfts = TRUE; } else { i_error("fts_lucene: Invalid setting: %s", *tmp); return -1; } } if (set->textcat_conf != NULL && set->textcat_dir == NULL) { i_error("fts_lucene: textcat_conf set, but textcat_dir unset"); return -1; } if (set->textcat_conf == NULL && set->textcat_dir != NULL) { i_error("fts_lucene: textcat_dir set, but textcat_conf unset"); return -1; } if (set->whitespace_chars == NULL) set->whitespace_chars = ""; #ifndef HAVE_FTS_STEMMER if (set->default_language != NULL) { i_error("fts_lucene: default_language set, " "but Dovecot built without stemmer support"); return -1; } #else if (set->default_language == NULL) set->default_language = "english"; #endif #ifndef HAVE_FTS_TEXTCAT if (set->textcat_conf != NULL) { i_error("fts_lucene: textcat_dir set, " "but Dovecot built without textcat support"); return -1; } #endif return 0; } uint32_t fts_lucene_settings_checksum(const struct fts_lucene_settings *set) { uint32_t crc; if (set->use_libfts) return crc32_str("l"); /* checksum is always different when compiling with/without stemmer */ crc = set->default_language == NULL ? 0 : crc32_str(set->default_language); crc = crc32_str_more(crc, set->whitespace_chars); if (set->normalize) crc = crc32_str_more(crc, "n"); if (set->no_snowball) crc = crc32_str_more(crc, "s"); /* don't include mime_parts here, since changing it doesn't necessarily need the index to be rebuilt */ return crc; } static void fts_lucene_mail_user_deinit(struct mail_user *user) { struct fts_lucene_user *fuser = FTS_LUCENE_USER_CONTEXT(user); if (fuser->set.use_libfts) fts_mail_user_deinit(user); fuser->module_ctx.super.deinit(user); } static void fts_lucene_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct fts_lucene_user *fuser; const char *env, *error; fuser = p_new(user->pool, struct fts_lucene_user, 1); env = mail_user_plugin_getenv(user, "fts_lucene"); if (env == NULL) env = ""; if (fts_lucene_plugin_init_settings(user, &fuser->set, env) < 0) { /* invalid settings, disabling */ return; } if (fuser->set.use_libfts) { if (fts_mail_user_init(user, &error) < 0) { i_error("fts_lucene: %s", error); return; } } fuser->module_ctx.super = *v; user->vlast = &fuser->module_ctx.super; v->deinit = fts_lucene_mail_user_deinit; MODULE_CONTEXT_SET(user, fts_lucene_user_module, fuser); } static struct mail_storage_hooks fts_lucene_mail_storage_hooks = { .mail_user_created = fts_lucene_mail_user_created }; void fts_lucene_plugin_init(struct module *module ATTR_UNUSED) { fts_backend_register(&fts_backend_lucene); mail_storage_hooks_add(module, &fts_lucene_mail_storage_hooks); } void fts_lucene_plugin_deinit(void) { fts_backend_unregister(fts_backend_lucene.name); mail_storage_hooks_remove(&fts_lucene_mail_storage_hooks); lucene_shutdown(); } const char *fts_lucene_plugin_dependencies[] = { "fts", NULL }; dovecot-2.2.33.2/src/plugins/fts-lucene/Snowball.cc0000644000175000017500000001161213123174404016732 00000000000000/*------------------------------------------------------------------------------ * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team * * Distributable under the terms of either the Apache License (Version 2.0) or * the GNU Lesser General Public License, as specified in the COPYING file. ------------------------------------------------------------------------------*/ #include #include "SnowballAnalyzer.h" #include "SnowballFilter.h" #include #include #include #include extern "C" { #include "lib.h" #include "buffer.h" #include "unichar.h" #include "lucene-wrapper.h" }; CL_NS_USE(analysis) CL_NS_USE(util) CL_NS_USE2(analysis,standard) CL_NS_DEF2(analysis,snowball) /** Builds the named analyzer with no stop words. */ SnowballAnalyzer::SnowballAnalyzer(normalizer_func_t *_normalizer, const char* _language) : language(i_strdup(_language)), normalizer(_normalizer), stopSet(NULL), prevstream(NULL) { } SnowballAnalyzer::~SnowballAnalyzer() { if (prevstream) _CLDELETE(prevstream); i_free(language); if ( stopSet != NULL ) _CLDELETE(stopSet); } /** Builds the named analyzer with the given stop words. */ SnowballAnalyzer::SnowballAnalyzer(const char* language, const TCHAR** stopWords) : language(i_strdup(language)), normalizer(NULL), stopSet(_CLNEW CLTCSetList(true)), prevstream(NULL) { StopFilter::fillStopTable(stopSet,stopWords); } TokenStream* SnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader) { return this->tokenStream(fieldName,reader,false); } /** Constructs a {@link StandardTokenizer} filtered by a {@link StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */ TokenStream* SnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader, bool deleteReader) { BufferedReader* bufferedReader = reader->__asBufferedReader(); TokenStream* result; if ( bufferedReader == NULL ) result = _CLNEW StandardTokenizer( _CLNEW FilteredBufferedReader(reader, deleteReader), true ); else result = _CLNEW StandardTokenizer(bufferedReader, deleteReader); result = _CLNEW StandardFilter(result, true); result = _CLNEW CL_NS(analysis)::LowerCaseFilter(result, true); if (stopSet != NULL) result = _CLNEW CL_NS(analysis)::StopFilter(result, true, stopSet); result = _CLNEW SnowballFilter(result, normalizer, language, true); return result; } TokenStream* SnowballAnalyzer::reusableTokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader) { if (prevstream) _CLDELETE(prevstream); prevstream = this->tokenStream(fieldName, reader); return prevstream; } /** Construct the named stemming filter. * * @param in the input tokens to stem * @param name the name of a stemmer */ SnowballFilter::SnowballFilter(TokenStream* in, normalizer_func_t *normalizer, const char* language, bool deleteTS): TokenFilter(in,deleteTS) { stemmer = sb_stemmer_new(language, NULL); //use utf8 encoding this->normalizer = normalizer; if ( stemmer == NULL ){ _CLTHROWA(CL_ERR_IllegalArgument, "language not available for stemming\n"); //todo: richer error } } SnowballFilter::~SnowballFilter(){ sb_stemmer_delete(stemmer); } /** Returns the next input Token, after being stemmed */ Token* SnowballFilter::next(Token* token){ if (input->next(token) == NULL) return NULL; unsigned char utf8text[LUCENE_MAX_WORD_LEN*5+1]; unsigned int len = I_MIN(LUCENE_MAX_WORD_LEN, token->termLength()); buffer_t buf = { 0, 0, { 0, 0, 0, 0, 0 } }; i_assert(sizeof(wchar_t) == sizeof(unichar_t)); buffer_create_from_data(&buf, utf8text, sizeof(utf8text)); uni_ucs4_to_utf8((const unichar_t *)token->termBuffer(), len, &buf); const sb_symbol* stemmed = sb_stemmer_stem(stemmer, utf8text, buf.used); if ( stemmed == NULL ) _CLTHROWA(CL_ERR_Runtime,"Out of memory"); int stemmedLen=sb_stemmer_length(stemmer); if (normalizer == NULL) { unsigned int tchartext_size = uni_utf8_strlen_n(stemmed, stemmedLen) + 1; TCHAR tchartext[tchartext_size]; lucene_utf8_n_to_tchar(stemmed, stemmedLen, tchartext, tchartext_size); token->set(tchartext,token->startOffset(), token->endOffset(), token->type()); } else T_BEGIN { buffer_t *norm_buf = buffer_create_dynamic(pool_datastack_create(), stemmedLen); normalizer(stemmed, stemmedLen, norm_buf); unsigned int tchartext_size = uni_utf8_strlen_n(norm_buf->data, norm_buf->used) + 1; TCHAR tchartext[tchartext_size]; lucene_utf8_n_to_tchar((const unsigned char *)norm_buf->data, norm_buf->used, tchartext, tchartext_size); token->set(tchartext,token->startOffset(), token->endOffset(), token->type()); } T_END; return token; } CL_NS_END2 dovecot-2.2.33.2/src/plugins/fts-lucene/fts-lucene-plugin.h0000644000175000017500000000151213123174404020352 00000000000000#ifndef FTS_LUCENE_PLUGIN_H #define FTS_LUCENE_PLUGIN_H #include "module-context.h" #include "mail-user.h" #include "fts-api-private.h" #define FTS_LUCENE_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_lucene_user_module) struct fts_lucene_settings { const char *default_language; const char *textcat_conf, *textcat_dir; const char *whitespace_chars; bool normalize; bool no_snowball; bool mime_parts; bool use_libfts; }; struct fts_lucene_user { union mail_user_module_context module_ctx; struct fts_lucene_settings set; }; extern struct fts_backend fts_backend_lucene; extern MODULE_CONTEXT_DEFINE(fts_lucene_user_module, &mail_user_module_register); uint32_t fts_lucene_settings_checksum(const struct fts_lucene_settings *set); void fts_lucene_plugin_init(struct module *module); void fts_lucene_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/fts-lucene/lucene-wrapper.cc0000644000175000017500000012141413165463624020117 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ extern "C" { #include "lib.h" #include "array.h" #include "unichar.h" #include "hash.h" #include "hex-binary.h" #include "ioloop.h" #include "unlink-directory.h" #include "ioloop.h" #include "mail-index.h" #include "mail-search.h" #include "mail-namespace.h" #include "mailbox-list-private.h" #include "mail-storage.h" #include "fts-expunge-log.h" #include "fts-lucene-plugin.h" #include "lucene-wrapper.h" #include #ifdef HAVE_LIBEXTTEXTCAT_TEXTCAT_H # include #elif defined (HAVE_LIBTEXTCAT_TEXTCAT_H) # include #elif defined (HAVE_FTS_TEXTCAT) # include #endif }; #include #include #include #include "SnowballAnalyzer.h" /* Lucene's default is 10000. Use it here also.. */ #define MAX_TERMS_PER_DOCUMENT 10000 #define FTS_LUCENE_MAX_SEARCH_TERMS 1000 #define LUCENE_LOCK_OVERRIDE_SECS 60 #define LUCENE_INDEX_CLOSE_TIMEOUT_MSECS (120*1000) using namespace lucene::document; using namespace lucene::index; using namespace lucene::search; using namespace lucene::queryParser; using namespace lucene::analysis; using namespace lucene::analysis; using namespace lucene::util; struct lucene_query { Query *query; BooleanClause::Occur occur; }; ARRAY_DEFINE_TYPE(lucene_query, struct lucene_query); struct lucene_analyzer { char *lang; Analyzer *analyzer; }; struct lucene_index { char *path; struct mailbox_list *list; struct fts_lucene_settings set; normalizer_func_t *normalizer; wchar_t mailbox_guid[MAILBOX_GUID_HEX_LENGTH + 1]; IndexReader *reader; IndexWriter *writer; IndexSearcher *searcher; struct timeout *to_close; buffer_t *normalizer_buf; Analyzer *default_analyzer, *cur_analyzer; ARRAY(struct lucene_analyzer) analyzers; Document *doc; uint32_t prev_uid, prev_part_idx; bool no_analyzer; }; struct rescan_context { struct lucene_index *index; struct mailbox *box; guid_128_t box_guid; int box_ret; pool_t pool; HASH_TABLE(uint8_t *, uint8_t *) seen_mailbox_guids; ARRAY_TYPE(seq_range) uids; struct seq_range_iter uids_iter; unsigned int uids_iter_n; uint32_t last_existing_uid; bool warned; }; static void *textcat = NULL; #ifdef HAVE_FTS_TEXTCAT static bool textcat_broken = FALSE; #endif static int textcat_refcount = 0; static void lucene_handle_error(struct lucene_index *index, CLuceneError &err, const char *msg); static void rescan_clear_unseen_mailboxes(struct lucene_index *index, struct rescan_context *rescan_ctx); struct lucene_index *lucene_index_init(const char *path, struct mailbox_list *list, const struct fts_lucene_settings *set) { struct lucene_index *index; index = i_new(struct lucene_index, 1); index->path = i_strdup(path); index->list = list; if (set != NULL) { index->set = *set; index->normalizer = !set->normalize ? NULL : mailbox_list_get_namespace(list)->user->default_normalizer; } else { /* this is valid only for doveadm dump, so it doesn't matter */ index->set.default_language = ""; } if (index->set.use_libfts) { index->default_analyzer = _CLNEW KeywordAnalyzer(); } else #ifdef HAVE_FTS_STEMMER if (set == NULL || !set->no_snowball) { index->default_analyzer = _CLNEW snowball::SnowballAnalyzer(index->normalizer, index->set.default_language); } else #endif { index->default_analyzer = _CLNEW standard::StandardAnalyzer(); if (index->normalizer != NULL) { index->normalizer_buf = buffer_create_dynamic(default_pool, 1024); } } i_array_init(&index->analyzers, 32); textcat_refcount++; return index; } void lucene_index_close(struct lucene_index *index) { if (index->to_close != NULL) timeout_remove(&index->to_close); _CLDELETE(index->searcher); if (index->writer != NULL) { try { index->writer->close(); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter::close"); } _CLDELETE(index->writer); } if (index->reader != NULL) { try { index->reader->close(); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexReader::close"); } _CLDELETE(index->reader); } } void lucene_index_deinit(struct lucene_index *index) { struct lucene_analyzer *a; lucene_index_close(index); array_foreach_modifiable(&index->analyzers, a) { i_free(a->lang); _CLDELETE(a->analyzer); } array_free(&index->analyzers); if (--textcat_refcount == 0 && textcat != NULL) { #ifdef HAVE_FTS_TEXTCAT textcat_Done(textcat); #endif textcat = NULL; } _CLDELETE(index->default_analyzer); if (index->normalizer_buf != NULL) buffer_free(&index->normalizer_buf); i_free(index->path); i_free(index); } static void lucene_data_translate(struct lucene_index *index, wchar_t *data, unsigned int len) { const char *whitespace_chars = index->set.whitespace_chars; unsigned int i; if (*whitespace_chars == '\0' || index->set.use_libfts) return; for (i = 0; i < len; i++) { if (strchr(whitespace_chars, data[i]) != NULL) data[i] = ' '; } } void lucene_utf8_n_to_tchar(const unsigned char *src, size_t srcsize, wchar_t *dest, size_t destsize) { ARRAY_TYPE(unichars) dest_arr; buffer_t buf = { 0, 0, { 0, 0, 0, 0, 0 } }; i_assert(sizeof(wchar_t) == sizeof(unichar_t)); buffer_create_from_data(&buf, dest, sizeof(wchar_t) * destsize); array_create_from_buffer(&dest_arr, &buf, sizeof(wchar_t)); if (uni_utf8_to_ucs4_n(src, srcsize, &dest_arr) < 0) i_unreached(); i_assert(array_count(&dest_arr)+1 == destsize); dest[destsize-1] = 0; } static const wchar_t * t_lucene_utf8_to_tchar(struct lucene_index *index, const char *str) { ARRAY_TYPE(unichars) dest_arr; const unichar_t *chars; wchar_t *ret; unsigned int len; i_assert(sizeof(wchar_t) == sizeof(unichar_t)); t_array_init(&dest_arr, strlen(str) + 1); if (uni_utf8_to_ucs4(str, &dest_arr) < 0) i_unreached(); (void)array_append_space(&dest_arr); chars = array_get_modifiable(&dest_arr, &len); ret = (wchar_t *)chars; lucene_data_translate(index, ret, len - 1); return ret; } void lucene_index_select_mailbox(struct lucene_index *index, const wchar_t guid[MAILBOX_GUID_HEX_LENGTH]) { memcpy(index->mailbox_guid, guid, MAILBOX_GUID_HEX_LENGTH * sizeof(wchar_t)); index->mailbox_guid[MAILBOX_GUID_HEX_LENGTH] = '\0'; } void lucene_index_unselect_mailbox(struct lucene_index *index) { memset(index->mailbox_guid, 0, sizeof(index->mailbox_guid)); } static void lucene_handle_error(struct lucene_index *index, CLuceneError &err, const char *msg) { const char *what = err.what(); i_error("lucene index %s: %s failed (#%d): %s", index->path, msg, err.number(), what); if (index->list != NULL && (err.number() == CL_ERR_CorruptIndex || err.number() == CL_ERR_IO)) { /* delete corrupted index. most IO errors are also about missing files and other such corruption.. */ if (unlink_directory(index->path, (enum unlink_directory_flags)0) < 0 && errno != ENOENT) i_error("unlink_directory(%s) failed: %m", index->path); rescan_clear_unseen_mailboxes(index, NULL); } } static int lucene_index_open(struct lucene_index *index) { if (index->reader != NULL) { i_assert(index->to_close != NULL); timeout_reset(index->to_close); return 1; } if (!IndexReader::indexExists(index->path)) return 0; try { index->reader = IndexReader::open(index->path); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexReader::open()"); return -1; } i_assert(index->to_close == NULL); index->to_close = timeout_add(LUCENE_INDEX_CLOSE_TIMEOUT_MSECS, lucene_index_close, index); return 1; } static int lucene_index_open_search(struct lucene_index *index) { int ret; if (index->searcher != NULL) return 1; if ((ret = lucene_index_open(index)) <= 0) return ret; index->searcher = _CLNEW IndexSearcher(index->reader); return 1; } static int lucene_doc_get_uid(struct lucene_index *index, Document *doc, uint32_t *uid_r) { Field *field = doc->getField(_T("uid")); const TCHAR *uid = field == NULL ? NULL : field->stringValue(); if (uid == NULL) { i_error("lucene: Corrupted FTS index %s: No UID for document", index->path); return -1; } uint32_t num = 0; while (*uid != 0) { num = num*10 + (*uid - '0'); uid++; } *uid_r = num; return 0; } static uint32_t lucene_doc_get_part(struct lucene_index *index, Document *doc) { Field *field = doc->getField(_T("part")); const TCHAR *part = field == NULL ? NULL : field->stringValue(); if (part == NULL) return 0; uint32_t num = 0; while (*part != 0) { num = num*10 + (*part - '0'); part++; } return num; } int lucene_index_get_last_uid(struct lucene_index *index, uint32_t *last_uid_r) { int ret = 0; *last_uid_r = 0; if ((ret = lucene_index_open_search(index)) <= 0) return ret; Term mailbox_term(_T("box"), index->mailbox_guid); TermQuery query(&mailbox_term); uint32_t last_uid = 0; try { Hits *hits = index->searcher->search(&query); for (size_t i = 0; i < hits->length(); i++) { uint32_t uid; if (lucene_doc_get_uid(index, &hits->doc(i), &uid) < 0) { ret = -1; break; } if (uid > last_uid) last_uid = uid; } _CLDELETE(hits); } catch (CLuceneError &err) { lucene_handle_error(index, err, "last_uid search"); ret = -1; } *last_uid_r = last_uid; return ret; } int lucene_index_get_doc_count(struct lucene_index *index, uint32_t *count_r) { int ret; if (index->reader == NULL) { lucene_index_close(index); if ((ret = lucene_index_open(index)) < 0) return -1; if (ret == 0) { *count_r = 0; return 0; } } *count_r = index->reader->numDocs(); return 0; } static int lucene_settings_check(struct lucene_index *index) { uint32_t set_checksum; int ret = 0; set_checksum = fts_lucene_settings_checksum(&index->set); ret = fts_index_have_compatible_settings(index->list, set_checksum); if (ret != 0) return ret; i_warning("fts-lucene: Settings have changed, rebuilding index for mailbox"); /* settings changed, rebuild index */ if (unlink_directory(index->path, (enum unlink_directory_flags)0) < 0) { i_error("unlink_directory(%s) failed: %m", index->path); ret = -1; } else { rescan_clear_unseen_mailboxes(index, NULL); } return ret; } int lucene_index_build_init(struct lucene_index *index) { const char *lock_path; struct stat st; lucene_index_close(index); lock_path = t_strdup_printf("%s/write.lock", index->path); if (stat(lock_path, &st) == 0 && st.st_mtime < time(NULL) - LUCENE_LOCK_OVERRIDE_SECS) { if (unlink(lock_path) < 0) i_error("unlink(%s) failed: %m", lock_path); } if (lucene_settings_check(index) < 0) return -1; bool exists = IndexReader::indexExists(index->path); try { index->writer = _CLNEW IndexWriter(index->path, index->default_analyzer, !exists); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter()"); return -1; } index->writer->setMaxFieldLength(MAX_TERMS_PER_DOCUMENT); return 0; } #ifdef HAVE_FTS_TEXTCAT static Analyzer *get_analyzer(struct lucene_index *index, const char *lang) { normalizer_func_t *normalizer = index->normalizer; const struct lucene_analyzer *a; struct lucene_analyzer new_analyzer; Analyzer *analyzer; array_foreach(&index->analyzers, a) { if (strcmp(a->lang, lang) == 0) return a->analyzer; } memset(&new_analyzer, 0, sizeof(new_analyzer)); new_analyzer.lang = i_strdup(lang); new_analyzer.analyzer = _CLNEW snowball::SnowballAnalyzer(normalizer, lang); array_append_i(&index->analyzers.arr, &new_analyzer, 1); return new_analyzer.analyzer; } static void *textcat_init(struct lucene_index *index) { const char *textcat_dir = index->set.textcat_dir; unsigned int len; if (textcat_dir == NULL) return NULL; /* textcat really wants the '/' suffix */ len = strlen(textcat_dir); if (len > 0 && textcat_dir[len-1] != '/') textcat_dir = t_strconcat(textcat_dir, "/", NULL); return special_textcat_Init(index->set.textcat_conf, textcat_dir); } static Analyzer * guess_analyzer(struct lucene_index *index, const void *data, size_t size) { const char *lang; if (textcat_broken) return NULL; if (textcat == NULL) { textcat = textcat_init(index); if (textcat == NULL) { textcat_broken = TRUE; return NULL; } } /* try to guess the language */ lang = textcat_Classify(textcat, (const char *)data, I_MIN(size, 500)); const char *p = strchr(lang, ']'); if (lang[0] != '[' || p == NULL) return NULL; lang = t_strdup_until(lang+1, p); if (strcmp(lang, index->set.default_language) == 0) return index->default_analyzer; return get_analyzer(index, lang); } #else static Analyzer * guess_analyzer(struct lucene_index *index ATTR_UNUSED, const void *data ATTR_UNUSED, size_t size ATTR_UNUSED) { return NULL; } #endif static int lucene_index_build_flush(struct lucene_index *index) { int ret = 0; if (index->doc == NULL) return 0; try { CL_NS(analysis)::Analyzer *analyzer = NULL; if (!index->set.use_libfts) { analyzer = index->cur_analyzer != NULL ? index->cur_analyzer : index->default_analyzer; } index->writer->addDocument(index->doc, analyzer); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter::addDocument()"); ret = -1; } _CLDELETE(index->doc); index->doc = NULL; index->cur_analyzer = NULL; return ret; } int lucene_index_build_more(struct lucene_index *index, uint32_t uid, uint32_t part_idx, const unsigned char *data, size_t size, const char *hdr_name) { wchar_t id[MAX_INT_STRLEN]; size_t namesize, datasize; if (uid != index->prev_uid || part_idx != index->prev_part_idx) { if (lucene_index_build_flush(index) < 0) return -1; index->prev_uid = uid; index->prev_part_idx = part_idx; index->doc = _CLNEW Document(); swprintf(id, N_ELEMENTS(id), L"%u", uid); index->doc->add(*_CLNEW Field(_T("uid"), id, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); if (part_idx != 0) { swprintf(id, N_ELEMENTS(id), L"%u", part_idx); index->doc->add(*_CLNEW Field(_T("part"), id, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); } index->doc->add(*_CLNEW Field(_T("box"), index->mailbox_guid, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); } if (index->normalizer_buf != NULL && !index->set.use_libfts) { buffer_set_used_size(index->normalizer_buf, 0); index->normalizer(data, size, index->normalizer_buf); data = (const unsigned char *)index->normalizer_buf->data; size = index->normalizer_buf->used; } datasize = uni_utf8_strlen_n(data, size) + 1; wchar_t *dest, *dest_free = NULL; if (datasize < 4096) dest = t_new(wchar_t, datasize); else dest = dest_free = i_new(wchar_t, datasize); lucene_utf8_n_to_tchar(data, size, dest, datasize); lucene_data_translate(index, dest, datasize-1); int token_flag = index->set.use_libfts ? Field::INDEX_UNTOKENIZED : Field::INDEX_TOKENIZED; if (hdr_name != NULL) { /* hdr_name should be ASCII, but don't break in case it isn't */ hdr_name = t_str_lcase(hdr_name); namesize = uni_utf8_strlen(hdr_name) + 1; wchar_t wname[namesize]; lucene_utf8_n_to_tchar((const unsigned char *)hdr_name, strlen(hdr_name), wname, namesize); if (!index->set.use_libfts) index->doc->add(*_CLNEW Field(_T("hdr"), wname, Field::STORE_NO | token_flag)); index->doc->add(*_CLNEW Field(_T("hdr"), dest, Field::STORE_NO | token_flag)); if (fts_header_want_indexed(hdr_name)) index->doc->add(*_CLNEW Field(wname, dest, Field::STORE_NO | token_flag)); } else if (size > 0) { if (index->cur_analyzer == NULL && !index->set.use_libfts) index->cur_analyzer = guess_analyzer(index, data, size); index->doc->add(*_CLNEW Field(_T("body"), dest, Field::STORE_NO | token_flag)); } i_free(dest_free); return 0; } int lucene_index_build_deinit(struct lucene_index *index) { int ret = 0; if (index->prev_uid == 0) { /* no changes. */ return 0; } index->prev_uid = 0; index->prev_part_idx = 0; if (index->writer == NULL) { lucene_index_close(index); return -1; } if (lucene_index_build_flush(index) < 0) ret = -1; try { index->writer->close(); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter::close()"); ret = -1; } lucene_index_close(index); return ret; } static int wcharguid_to_guid(guid_128_t dest, const wchar_t *src) { buffer_t buf = { 0, 0, { 0, 0, 0, 0, 0 } }; char src_chars[GUID_128_SIZE*2 + 1]; unsigned int i; for (i = 0; i < sizeof(src_chars)-1; i++) { if ((src[i] >= '0' && src[i] <= '9') || (src[i] >= 'a' && src[i] <= 'f')) src_chars[i] = src[i]; else return -1; } if (src[i] != '\0') return -1; src_chars[i] = '\0'; buffer_create_from_data(&buf, dest, GUID_128_SIZE); return hex_to_binary(src_chars, &buf); } static int rescan_get_uids(struct mailbox *box, ARRAY_TYPE(seq_range) *uids) { struct mailbox_status status; if (mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) return -1; if (status.messages > 0) T_BEGIN { ARRAY_TYPE(seq_range) seqs; t_array_init(&seqs, 2); seq_range_array_add_range(&seqs, 1, status.messages); mailbox_get_uid_range(box, &seqs, uids); } T_END; return 0; } static int rescan_finish(struct rescan_context *ctx) { int ret; ret = fts_index_set_last_uid(ctx->box, ctx->last_existing_uid); mailbox_free(&ctx->box); return ret; } static int fts_lucene_get_mailbox_guid(struct lucene_index *index, Document *doc, guid_128_t guid_r) { Field *field = doc->getField(_T("box")); const TCHAR *box_guid = field == NULL ? NULL : field->stringValue(); if (box_guid == NULL) { i_error("lucene: Corrupted FTS index %s: No mailbox for document", index->path); return -1; } if (wcharguid_to_guid(guid_r, box_guid) < 0) { i_error("lucene: Corrupted FTS index %s: " "box field not in expected format", index->path); return -1; } return 0; } static int rescan_open_mailbox(struct rescan_context *ctx, Document *doc) { guid_128_t guid, *guidp; int ret; if (fts_lucene_get_mailbox_guid(ctx->index, doc, guid) < 0) return 0; if (memcmp(guid, ctx->box_guid, sizeof(guid)) == 0) { /* same as last one */ return ctx->box_ret; } memcpy(ctx->box_guid, guid, sizeof(ctx->box_guid)); guidp = p_new(ctx->pool, guid_128_t, 1); memcpy(guidp, guid, sizeof(*guidp)); hash_table_insert(ctx->seen_mailbox_guids, guidp, guidp); if (ctx->box != NULL) rescan_finish(ctx); ctx->box = mailbox_alloc_guid(ctx->index->list, guid, (enum mailbox_flags)0); if (mailbox_open(ctx->box) < 0) { enum mail_error error; const char *errstr; errstr = mailbox_get_last_internal_error(ctx->box, &error); if (error == MAIL_ERROR_NOTFOUND) ret = 0; else { i_error("lucene: Couldn't open mailbox %s: %s", mailbox_get_vname(ctx->box), errstr); ret = -1; } mailbox_free(&ctx->box); ctx->box_ret = ret; return ret; } if (mailbox_sync(ctx->box, (enum mailbox_sync_flags)0) < 0) { i_error("lucene: Failed to sync mailbox %s: %s", mailbox_get_vname(ctx->box), mailbox_get_last_internal_error(ctx->box, NULL)); mailbox_free(&ctx->box); ctx->box_ret = -1; return -1; } array_clear(&ctx->uids); rescan_get_uids(ctx->box, &ctx->uids); ctx->warned = FALSE; ctx->last_existing_uid = 0; ctx->uids_iter_n = 0; seq_range_array_iter_init(&ctx->uids_iter, &ctx->uids); ctx->box_ret = 1; return 1; } static int rescan_next(struct rescan_context *ctx, Document *doc) { uint32_t lucene_uid, idx_uid; if (lucene_doc_get_uid(ctx->index, doc, &lucene_uid) < 0) return 0; if (seq_range_array_iter_nth(&ctx->uids_iter, ctx->uids_iter_n, &idx_uid)) { if (idx_uid == lucene_uid) { ctx->uids_iter_n++; ctx->last_existing_uid = idx_uid; return 1; } if (idx_uid < lucene_uid) { /* lucene is missing an UID from the middle. delete the rest of the messages from this mailbox and reindex. */ if (!ctx->warned) { i_warning("lucene: Mailbox %s " "missing UIDs in the middle", mailbox_get_vname(ctx->box)); ctx->warned = TRUE; } } else { /* UID has been expunged from index. delete from lucene as well. */ } return 0; } else { /* the rest of the messages have been expunged from index */ return 0; } } static void rescan_clear_unseen_mailbox(struct lucene_index *index, struct rescan_context *rescan_ctx, const char *vname, const struct fts_index_header *hdr) { struct mailbox *box; struct mailbox_metadata metadata; box = mailbox_alloc(index->list, vname, (enum mailbox_flags)0); if (mailbox_open(box) == 0 && mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) == 0 && (rescan_ctx == NULL || hash_table_lookup(rescan_ctx->seen_mailbox_guids, metadata.guid) == NULL)) { /* this mailbox had no records in lucene index. make sure its last indexed uid is 0 */ (void)fts_index_set_header(box, hdr); } mailbox_free(&box); } static void rescan_clear_unseen_mailboxes(struct lucene_index *index, struct rescan_context *rescan_ctx) { const enum mailbox_list_iter_flags iter_flags = (enum mailbox_list_iter_flags) (MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; struct fts_index_header hdr; struct mail_namespace *ns = index->list->ns; const char *vname; memset(&hdr, 0, sizeof(hdr)); hdr.settings_checksum = fts_lucene_settings_checksum(&index->set); iter = mailbox_list_iter_init(index->list, "*", iter_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) rescan_clear_unseen_mailbox(index, rescan_ctx, info->vname, &hdr); (void)mailbox_list_iter_deinit(&iter); if (ns->prefix_len > 0 && ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) { /* namespace prefix itself isn't returned by the listing */ vname = t_strndup(index->list->ns->prefix, index->list->ns->prefix_len-1); rescan_clear_unseen_mailbox(index, rescan_ctx, vname, &hdr); } } int lucene_index_rescan(struct lucene_index *index) { static const TCHAR *sort_fields[] = { _T("box"), _T("uid"), NULL }; struct rescan_context ctx; bool failed = false; int ret; i_assert(index->list != NULL); if ((ret = lucene_index_open_search(index)) < 0) return ret; Term term(_T("box"), _T("*")); WildcardQuery query(&term); Sort sort(sort_fields); memset(&ctx, 0, sizeof(ctx)); ctx.index = index; ctx.pool = pool_alloconly_create("guids", 1024); hash_table_create(&ctx.seen_mailbox_guids, ctx.pool, 0, guid_128_hash, guid_128_cmp); i_array_init(&ctx.uids, 128); if (ret > 0) try { Hits *hits = index->searcher->search(&query, &sort); for (size_t i = 0; i < hits->length(); i++) { ret = rescan_open_mailbox(&ctx, &hits->doc(i)); if (ret > 0) ret = rescan_next(&ctx, &hits->doc(i)); if (ret < 0) failed = true; else if (ret == 0) index->reader->deleteDocument(hits->id(i)); } _CLDELETE(hits); } catch (CLuceneError &err) { lucene_handle_error(index, err, "rescan search"); failed = true; } lucene_index_close(index); if (ctx.box != NULL) rescan_finish(&ctx); array_free(&ctx.uids); rescan_clear_unseen_mailboxes(index, &ctx); hash_table_destroy(&ctx.seen_mailbox_guids); pool_unref(&ctx.pool); return failed ? -1 : 0; } static void guid128_to_wguid(const guid_128_t guid, wchar_t wguid_hex[MAILBOX_GUID_HEX_LENGTH + 1]) { buffer_t buf = { 0, 0, { 0, 0, 0, 0, 0 } }; unsigned char guid_hex[MAILBOX_GUID_HEX_LENGTH]; unsigned int i; buffer_create_from_data(&buf, guid_hex, MAILBOX_GUID_HEX_LENGTH); binary_to_hex_append(&buf, guid, GUID_128_SIZE); for (i = 0; i < MAILBOX_GUID_HEX_LENGTH; i++) wguid_hex[i] = guid_hex[i]; wguid_hex[i] = '\0'; } static bool lucene_index_add_uid_filter(BooleanQuery *query, const struct fts_expunge_log_read_record *rec) { struct seq_range_iter iter; wchar_t wuid[MAX_INT_STRLEN]; unsigned int n; uint32_t uid; /* RangeQuery and WildcardQuery work by enumerating through all terms that match them, and then adding TermQueries for them. So we can simply do the same directly, and if it looks like there are too many terms just go through everything. */ if (seq_range_count(&rec->uids) > FTS_LUCENE_MAX_SEARCH_TERMS) return false; seq_range_array_iter_init(&iter, &rec->uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { swprintf(wuid, N_ELEMENTS(wuid), L"%u", uid); Term *term = _CLNEW Term(_T("uid"), wuid); query->add(_CLNEW TermQuery(term), true, BooleanClause::SHOULD); _CLDECDELETE(term); } return true; } static int lucene_index_expunge_record(struct lucene_index *index, const struct fts_expunge_log_read_record *rec) { int ret; if ((ret = lucene_index_open_search(index)) <= 0) return ret; BooleanQuery query; BooleanQuery uids_query; if (lucene_index_add_uid_filter(&uids_query, rec)) query.add(&uids_query, BooleanClause::MUST); wchar_t wguid[MAILBOX_GUID_HEX_LENGTH + 1]; guid128_to_wguid(rec->mailbox_guid, wguid); Term term(_T("box"), wguid); TermQuery mailbox_query(&term); query.add(&mailbox_query, BooleanClause::MUST); try { Hits *hits = index->searcher->search(&query); for (size_t i = 0; i < hits->length(); i++) { uint32_t uid; if (lucene_doc_get_uid(index, &hits->doc(i), &uid) < 0 || seq_range_exists(&rec->uids, uid)) index->reader->deleteDocument(hits->id(i)); } _CLDELETE(hits); } catch (CLuceneError &err) { lucene_handle_error(index, err, "expunge search"); ret = -1; } return ret < 0 ? -1 : 0; } int lucene_index_expunge_from_log(struct lucene_index *index, struct fts_expunge_log *log) { struct fts_expunge_log_read_ctx *ctx; const struct fts_expunge_log_read_record *rec; int ret = 0, ret2; ctx = fts_expunge_log_read_begin(log); while ((rec = fts_expunge_log_read_next(ctx)) != NULL) { if (lucene_index_expunge_record(index, rec) < 0) { ret = -1; break; } } lucene_index_close(index); ret2 = fts_expunge_log_read_end(&ctx); if (ret < 0 || ret2 < 0) return -1; return ret2; } int lucene_index_optimize(struct lucene_index *index) { int ret = 0; if (!IndexReader::indexExists(index->path)) return 0; if (IndexReader::isLocked(index->path)) IndexReader::unlock(index->path); IndexWriter *writer = NULL; try { writer = _CLNEW IndexWriter(index->path, index->default_analyzer, false); writer->optimize(); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter::optimize()"); ret = -1; } try { writer->close(); } catch (CLuceneError &err) { lucene_handle_error(index, err, "IndexWriter::close()"); ret = -1; } if (writer != NULL) _CLDELETE(writer); return ret; } // Mostly copy&pasted from CLucene's QueryParser static Query* getFieldQuery(Analyzer *analyzer, const TCHAR* _field, const TCHAR* queryText, bool fuzzy) { // Use the analyzer to get all the tokens, and then build a TermQuery, // PhraseQuery, or nothing based on the term count StringReader reader(queryText); TokenStream* source = analyzer->tokenStream(_field, &reader); CLVector > v; CL_NS(analysis)::Token* t = NULL; int32_t positionCount = 0; bool severalTokensAtSamePosition = false; while (true) { t = _CLNEW Token(); try { Token* _t = source->next(t); if (_t == NULL) _CLDELETE(t); }_CLCATCH_ERR(CL_ERR_IO, _CLLDELETE(source);_CLLDELETE(t);,{ t = NULL; }); if (t == NULL) break; v.push_back(t); if (t->getPositionIncrement() != 0) positionCount += t->getPositionIncrement(); else severalTokensAtSamePosition = true; } try { source->close(); } _CLCATCH_ERR_CLEANUP(CL_ERR_IO, {_CLLDELETE(source);_CLLDELETE(t);} ); /* cleanup */ _CLLDELETE(source); if (v.size() == 0) return NULL; else if (v.size() == 1) { Term* tm = _CLNEW Term(_field, v.at(0)->termBuffer()); Query* ret; if (fuzzy) ret = _CLNEW FuzzyQuery( tm ); else ret = _CLNEW TermQuery( tm ); _CLDECDELETE(tm); return ret; } else { if (severalTokensAtSamePosition) { if (positionCount == 1) { // no phrase query: BooleanQuery* q = _CLNEW BooleanQuery(true); for(size_t i=0; itermBuffer()); q->add(_CLNEW TermQuery(tm), true, BooleanClause::SHOULD); _CLDECDELETE(tm); } return q; }else { MultiPhraseQuery* mpq = _CLNEW MultiPhraseQuery(); CLArrayList multiTerms; int32_t position = -1; for (size_t i = 0; i < v.size(); i++) { t = v.at(i); if (t->getPositionIncrement() > 0 && multiTerms.size() > 0) { ValueArray termsArray(multiTerms.size()); multiTerms.toArray(termsArray.values); mpq->add(&termsArray,position); multiTerms.clear(); } position += t->getPositionIncrement(); multiTerms.push_back(_CLNEW Term(_field, t->termBuffer())); } ValueArray termsArray(multiTerms.size()); multiTerms.toArray(termsArray.values); mpq->add(&termsArray,position); return mpq; } }else { PhraseQuery* pq = _CLNEW PhraseQuery(); int32_t position = -1; for (size_t i = 0; i < v.size(); i++) { t = v.at(i); Term* tm = _CLNEW Term(_field, t->termBuffer()); position += t->getPositionIncrement(); pq->add(tm,position); _CLDECDELETE(tm); } return pq; } } } static Query * lucene_get_query_str(struct lucene_index *index, const TCHAR *key, const char *str, bool fuzzy) { const TCHAR *wvalue; Analyzer *analyzer; if (index->set.use_libfts) { const wchar_t *wstr = t_lucene_utf8_to_tchar(index, str); Term* tm = _CLNEW Term(key, wstr); Query* ret; if (fuzzy) ret = _CLNEW FuzzyQuery( tm ); else ret = _CLNEW TermQuery( tm ); _CLDECDELETE(tm); return ret; } if (index->normalizer_buf != NULL) { buffer_set_used_size(index->normalizer_buf, 0); index->normalizer(str, strlen(str), index->normalizer_buf); buffer_append_c(index->normalizer_buf, '\0'); str = (const char *)index->normalizer_buf->data; } wvalue = t_lucene_utf8_to_tchar(index, str); analyzer = guess_analyzer(index, str, strlen(str)); if (analyzer == NULL) analyzer = index->default_analyzer; return getFieldQuery(analyzer, key, wvalue, fuzzy); } static Query * lucene_get_query(struct lucene_index *index, const TCHAR *key, const struct mail_search_arg *arg) { return lucene_get_query_str(index, key, arg->value.str, arg->fuzzy); } static bool lucene_add_definite_query(struct lucene_index *index, ARRAY_TYPE(lucene_query) &queries, struct mail_search_arg *arg, enum fts_lookup_flags flags) { bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; Query *q; if (arg->no_fts) return false; if (arg->match_not && !and_args) { /* FIXME: we could handle this by doing multiple queries.. */ return false; } switch (arg->type) { case SEARCH_TEXT: { BooleanQuery *bq = _CLNEW BooleanQuery(); Query *q1 = lucene_get_query(index, _T("hdr"), arg); Query *q2 = lucene_get_query(index, _T("body"), arg); if (q1 == NULL && q2 == NULL) q = NULL; else { if (q1 != NULL) bq->add(q1, true, BooleanClause::SHOULD); if (q2 != NULL) bq->add(q2, true, BooleanClause::SHOULD); q = bq; } break; } case SEARCH_BODY: q = lucene_get_query(index, _T("body"), arg); break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (!fts_header_want_indexed(arg->hdr_field_name) || *arg->value.str == '\0') return false; q = lucene_get_query(index, t_lucene_utf8_to_tchar(index, t_str_lcase(arg->hdr_field_name)), arg); break; default: return false; } if (q == NULL) { /* couldn't handle this search after all (e.g. trying to search a stop word) */ return false; } struct lucene_query *lq = array_append_space(&queries); lq->query = q; if (!and_args) lq->occur = BooleanClause::SHOULD; else if (!arg->match_not) lq->occur = BooleanClause::MUST; else lq->occur = BooleanClause::MUST_NOT; return true; } static bool lucene_add_maybe_query(struct lucene_index *index, ARRAY_TYPE(lucene_query) &queries, struct mail_search_arg *arg, enum fts_lookup_flags flags) { bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; Query *q = NULL; if (arg->no_fts) return false; if (arg->match_not) { /* FIXME: we could handle this by doing multiple queries.. */ return false; } switch (arg->type) { case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (*arg->value.str == '\0' && !index->set.use_libfts) { /* checking potential existence of the header name */ q = lucene_get_query_str(index, _T("hdr"), t_str_lcase(arg->hdr_field_name), FALSE); break; } if (fts_header_want_indexed(arg->hdr_field_name)) return false; /* we can check if the search key exists in some header and filter out the messages that have no chance of matching */ q = lucene_get_query(index, _T("hdr"), arg); break; default: return false; } if (q == NULL) { /* couldn't handle this search after all (e.g. trying to search a stop word) */ return false; } struct lucene_query *lq = array_append_space(&queries); lq->query = q; if (!and_args) lq->occur = BooleanClause::SHOULD; else if (!arg->match_not) lq->occur = BooleanClause::MUST; else lq->occur = BooleanClause::MUST_NOT; return true; } static bool queries_have_non_must_nots(ARRAY_TYPE(lucene_query) &queries) { const struct lucene_query *lq; array_foreach(&queries, lq) { if (lq->occur != BooleanClause::MUST_NOT) return TRUE; } return FALSE; } static void search_query_add(BooleanQuery &query, ARRAY_TYPE(lucene_query) &queries) { BooleanQuery *search_query = _CLNEW BooleanQuery(); const struct lucene_query *lq; if (queries_have_non_must_nots(queries)) { array_foreach(&queries, lq) search_query->add(lq->query, true, lq->occur); query.add(search_query, true, BooleanClause::MUST); } else { array_foreach(&queries, lq) search_query->add(lq->query, true, BooleanClause::SHOULD); query.add(search_query, true, BooleanClause::MUST_NOT); } } static int lucene_index_search(struct lucene_index *index, ARRAY_TYPE(lucene_query) &queries, struct fts_result *result, ARRAY_TYPE(seq_range) *uids_r) { struct fts_score_map *score; int ret = 0; BooleanQuery query; search_query_add(query, queries); Term mailbox_term(_T("box"), index->mailbox_guid); TermQuery mailbox_query(&mailbox_term); query.add(&mailbox_query, BooleanClause::MUST); try { Hits *hits = index->searcher->search(&query); uint32_t last_uid = 0; if (result != NULL) result->scores_sorted = true; for (size_t i = 0; i < hits->length(); i++) { uint32_t uid; if (lucene_doc_get_uid(index, &hits->doc(i), &uid) < 0) { ret = -1; break; } if (seq_range_array_add(uids_r, uid)) { /* duplicate result */ } else if (result != NULL) { if (uid < last_uid) result->scores_sorted = false; last_uid = uid; score = array_append_space(&result->scores); score->uid = uid; score->score = hits->score(i); } } _CLDELETE(hits); return ret; } catch (CLuceneError &err) { lucene_handle_error(index, err, "search"); return -1; } } int lucene_index_lookup(struct lucene_index *index, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { struct mail_search_arg *arg; if (lucene_index_open_search(index) <= 0) return -1; ARRAY_TYPE(lucene_query) def_queries; t_array_init(&def_queries, 16); bool have_definites = false; for (arg = args; arg != NULL; arg = arg->next) { if (lucene_add_definite_query(index, def_queries, arg, flags)) { arg->match_always = true; have_definites = true; } } if (have_definites) { ARRAY_TYPE(seq_range) *uids_arr = (flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0 ? &result->definite_uids : &result->maybe_uids; if (lucene_index_search(index, def_queries, result, uids_arr) < 0) return -1; } if (have_definites) { /* FIXME: mixing up definite + maybe queries is broken. if the definite query matched, it'll just assume that the maybe queries matched as well */ return 0; } ARRAY_TYPE(lucene_query) maybe_queries; t_array_init(&maybe_queries, 16); bool have_maybies = false; for (arg = args; arg != NULL; arg = arg->next) { if (lucene_add_maybe_query(index, maybe_queries, arg, flags)) { arg->match_always = true; have_maybies = true; } } if (have_maybies) { if (lucene_index_search(index, maybe_queries, NULL, &result->maybe_uids) < 0) return -1; } return 0; } static int lucene_index_search_multi(struct lucene_index *index, HASH_TABLE_TYPE(wguid_result) guids, ARRAY_TYPE(lucene_query) &queries, enum fts_lookup_flags flags, struct fts_multi_result *result) { struct fts_score_map *score; int ret = 0; BooleanQuery query; search_query_add(query, queries); BooleanQuery mailbox_query; struct hash_iterate_context *iter; void *key, *value; iter = hash_table_iterate_init(guids); while (hash_table_iterate(iter, guids, &key, &value)) { Term *term = _CLNEW Term(_T("box"), (wchar_t *)key); TermQuery *q = _CLNEW TermQuery(term); mailbox_query.add(q, true, BooleanClause::SHOULD); } hash_table_iterate_deinit(&iter); query.add(&mailbox_query, BooleanClause::MUST); try { Hits *hits = index->searcher->search(&query); for (size_t i = 0; i < hits->length(); i++) { uint32_t uid; Field *field = hits->doc(i).getField(_T("box")); const TCHAR *box_guid = field == NULL ? NULL : field->stringValue(); if (box_guid == NULL) { i_error("lucene: Corrupted FTS index %s: No mailbox for document", index->path); ret = -1; break; } struct fts_result *br = hash_table_lookup(guids, box_guid); if (br == NULL) { i_warning("lucene: Returned unexpected mailbox with GUID %ls", box_guid); continue; } if (lucene_doc_get_uid(index, &hits->doc(i), &uid) < 0) { ret = -1; break; } ARRAY_TYPE(seq_range) *uids_arr = (flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0 ? &br->maybe_uids : &br->definite_uids; if (!array_is_created(uids_arr)) { p_array_init(uids_arr, result->pool, 32); p_array_init(&br->scores, result->pool, 32); } if (seq_range_array_add(uids_arr, uid)) { /* duplicate result */ } else { score = array_append_space(&br->scores); score->uid = uid; score->score = hits->score(i); } } _CLDELETE(hits); return ret; } catch (CLuceneError &err) { lucene_handle_error(index, err, "multi search"); return -1; } } int lucene_index_lookup_multi(struct lucene_index *index, HASH_TABLE_TYPE(wguid_result) guids, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result) { struct mail_search_arg *arg; if (lucene_index_open_search(index) <= 0) return -1; ARRAY_TYPE(lucene_query) def_queries; t_array_init(&def_queries, 16); bool have_definites = false; for (arg = args; arg != NULL; arg = arg->next) { if (lucene_add_definite_query(index, def_queries, arg, flags)) { arg->match_always = true; have_definites = true; } } if (have_definites) { if (lucene_index_search_multi(index, guids, def_queries, flags, result) < 0) return -1; } return 0; } struct lucene_index_iter { struct lucene_index *index; struct lucene_index_record rec; Term *term; WildcardQuery *query; Sort *sort; Hits *hits; size_t i; bool failed; }; struct lucene_index_iter * lucene_index_iter_init(struct lucene_index *index) { static const TCHAR *sort_fields[] = { _T("box"), _T("uid"), NULL }; struct lucene_index_iter *iter; int ret; iter = i_new(struct lucene_index_iter, 1); iter->index = index; if ((ret = lucene_index_open_search(index)) <= 0) { if (ret < 0) iter->failed = true; return iter; } iter->term = _CLNEW Term(_T("box"), _T("*")); iter->query = _CLNEW WildcardQuery(iter->term); iter->sort = _CLNEW Sort(sort_fields); try { iter->hits = index->searcher->search(iter->query, iter->sort); } catch (CLuceneError &err) { lucene_handle_error(index, err, "rescan search"); iter->failed = true; } return iter; } const struct lucene_index_record * lucene_index_iter_next(struct lucene_index_iter *iter) { if (iter->hits == NULL) return NULL; if (iter->i == iter->hits->length()) return NULL; Document *doc = &iter->hits->doc(iter->i); iter->i++; memset(&iter->rec, 0, sizeof(iter->rec)); (void)fts_lucene_get_mailbox_guid(iter->index, doc, iter->rec.mailbox_guid); (void)lucene_doc_get_uid(iter->index, doc, &iter->rec.uid); iter->rec.part_num = lucene_doc_get_part(iter->index, doc); return &iter->rec; } int lucene_index_iter_deinit(struct lucene_index_iter **_iter) { struct lucene_index_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; if (iter->hits != NULL) _CLDELETE(iter->hits); if (iter->query != NULL) { _CLDELETE(iter->query); _CLDELETE(iter->sort); _CLDELETE(iter->term); } i_free(iter); return ret; } void lucene_shutdown(void) { _lucene_shutdown(); } dovecot-2.2.33.2/src/plugins/fts-lucene/textcat.conf0000644000175000017500000000146113123174404017166 00000000000000# # A sample config file for the language models # provided with Gertjan van Noords language guesser # (http://odur.let.rug.nl/~vannoord/TextCat/) # # Notes: # - You may consider eliminating a couple of small languages from this # list because they cause false positives with big languages and are # bad for performance. (Do you really want to recognize Drents?) # - Putting the most probable languages at the top of the list # improves performance, because this will raise the threshold for # likely candidates more quickly. # LM/english.lm english LM/italian.lm italian LM/danish.lm danish LM/dutch.lm dutch LM/finnish.lm finnish LM/french.lm french LM/german.lm german LM/norwegian.lm norwegian LM/portuguese.lm portuguese LM/russian.lm russian LM/spanish.lm spanish LM/swedish.lm swedish dovecot-2.2.33.2/src/plugins/fts-lucene/lucene-wrapper.h0000644000175000017500000000427013123174404017746 00000000000000#ifndef LUCENE_WRAPPER_H #define LUCENE_WRAPPER_H #include "fts-api-private.h" #include "guid.h" struct mailbox_list; struct fts_expunge_log; struct fts_lucene_settings; #define MAILBOX_GUID_HEX_LENGTH (GUID_128_SIZE*2) struct lucene_index_record { guid_128_t mailbox_guid; uint32_t uid, part_num; }; HASH_TABLE_DEFINE_TYPE(wguid_result, wchar_t *, struct fts_result *); struct lucene_index * lucene_index_init(const char *path, struct mailbox_list *list, const struct fts_lucene_settings *set) ATTR_NULL(2, 3); void lucene_index_deinit(struct lucene_index *index); void lucene_index_select_mailbox(struct lucene_index *index, const wchar_t guid[MAILBOX_GUID_HEX_LENGTH]); void lucene_index_unselect_mailbox(struct lucene_index *index); int lucene_index_get_last_uid(struct lucene_index *index, uint32_t *last_uid_r); int lucene_index_get_doc_count(struct lucene_index *index, uint32_t *count_r); int lucene_index_build_init(struct lucene_index *index); int lucene_index_build_more(struct lucene_index *index, uint32_t uid, uint32_t part_num, const unsigned char *data, size_t size, const char *hdr_name); int lucene_index_build_deinit(struct lucene_index *index); void lucene_index_close(struct lucene_index *index); int lucene_index_rescan(struct lucene_index *index); int lucene_index_expunge_from_log(struct lucene_index *index, struct fts_expunge_log *log); int lucene_index_optimize(struct lucene_index *index); int lucene_index_lookup(struct lucene_index *index, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result); int lucene_index_lookup_multi(struct lucene_index *index, HASH_TABLE_TYPE(wguid_result) guids, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result); struct lucene_index_iter * lucene_index_iter_init(struct lucene_index *index); const struct lucene_index_record * lucene_index_iter_next(struct lucene_index_iter *iter); int lucene_index_iter_deinit(struct lucene_index_iter **iter); /* internal: */ void lucene_utf8_n_to_tchar(const unsigned char *src, size_t srcsize, wchar_t *dest, size_t destsize); void lucene_shutdown(void); #endif dovecot-2.2.33.2/src/plugins/fts-lucene/fts-backend-lucene.c0000644000175000017500000003716613165463624020467 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "hex-binary.h" #include "strescape.h" #include "message-part.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "fts-expunge-log.h" #include "lucene-wrapper.h" #include "fts-indexer.h" #include "fts-lucene-plugin.h" #include #define LUCENE_INDEX_DIR_NAME "lucene-indexes" #define LUCENE_EXPUNGE_LOG_NAME "dovecot-expunges.log" #define LUCENE_OPTIMIZE_BATCH_MSGS_COUNT 100 struct lucene_fts_backend { struct fts_backend backend; char *dir_path; struct lucene_index *index; struct mailbox *selected_box; unsigned int selected_box_generation; guid_128_t selected_box_guid; struct fts_expunge_log *expunge_log; unsigned int dir_created:1; unsigned int updating:1; }; struct lucene_fts_backend_update_context { struct fts_backend_update_context ctx; struct mailbox *box; uint32_t last_uid; uint32_t last_indexed_uid; char *first_box_vname; uint32_t uid, part_num; char *hdr_name; unsigned int added_msgs; struct fts_expunge_log_append_ctx *expunge_ctx; bool lucene_opened; bool last_indexed_uid_set; bool mime_parts; }; static int fts_backend_lucene_mkdir(struct lucene_fts_backend *backend) { if (backend->dir_created) return 0; backend->dir_created = TRUE; if (mailbox_list_mkdir_root(backend->backend.ns->list, backend->dir_path, MAILBOX_LIST_PATH_TYPE_INDEX) < 0) return -1; return 0; } static int fts_lucene_get_mailbox_guid(struct mailbox *box, guid_128_t guid_r) { struct mailbox_metadata metadata; if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("lucene: Couldn't get mailbox %s GUID: %s", box->vname, mailbox_get_last_internal_error(box, NULL)); return -1; } memcpy(guid_r, metadata.guid, GUID_128_SIZE); return 0; } static int fts_backend_select(struct lucene_fts_backend *backend, struct mailbox *box) { guid_128_t guid; unsigned char guid_hex[MAILBOX_GUID_HEX_LENGTH]; wchar_t wguid_hex[MAILBOX_GUID_HEX_LENGTH]; buffer_t buf; unsigned int i; i_assert(box != NULL); if (backend->selected_box == box && backend->selected_box_generation == box->generation_sequence) return 0; if (fts_lucene_get_mailbox_guid(box, guid) < 0) return -1; buffer_create_from_data(&buf, guid_hex, MAILBOX_GUID_HEX_LENGTH); binary_to_hex_append(&buf, guid, GUID_128_SIZE); for (i = 0; i < N_ELEMENTS(wguid_hex); i++) wguid_hex[i] = guid_hex[i]; lucene_index_select_mailbox(backend->index, wguid_hex); backend->selected_box = box; memcpy(backend->selected_box_guid, guid, sizeof(backend->selected_box_guid)); backend->selected_box_generation = box->generation_sequence; return 0; } static struct fts_backend *fts_backend_lucene_alloc(void) { struct lucene_fts_backend *backend; backend = i_new(struct lucene_fts_backend, 1); backend->backend = fts_backend_lucene; return &backend->backend; } static int fts_backend_lucene_init(struct fts_backend *_backend, const char **error_r) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; struct fts_lucene_user *fuser = FTS_LUCENE_USER_CONTEXT(_backend->ns->user); const char *path; if (fuser == NULL) { /* invalid settings */ *error_r = "Invalid fts_lucene settings"; return -1; } /* fts already checked that index exists */ if (fuser->set.use_libfts) { /* change our flags so we get proper input */ _backend->flags &= ~FTS_BACKEND_FLAG_FUZZY_SEARCH; _backend->flags |= FTS_BACKEND_FLAG_TOKENIZED_INPUT; } path = mailbox_list_get_root_forced(_backend->ns->list, MAILBOX_LIST_PATH_TYPE_INDEX); backend->dir_path = i_strconcat(path, "/"LUCENE_INDEX_DIR_NAME, NULL); backend->index = lucene_index_init(backend->dir_path, _backend->ns->list, &fuser->set); path = t_strconcat(backend->dir_path, "/"LUCENE_EXPUNGE_LOG_NAME, NULL); backend->expunge_log = fts_expunge_log_init(path); return 0; } static void fts_backend_lucene_deinit(struct fts_backend *_backend) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; if (backend->index != NULL) lucene_index_deinit(backend->index); if (backend->expunge_log != NULL) fts_expunge_log_deinit(&backend->expunge_log); i_free(backend->dir_path); i_free(backend); } static int fts_backend_lucene_get_last_uid(struct fts_backend *_backend, struct mailbox *box, uint32_t *last_uid_r) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; struct fts_lucene_user *fuser = FTS_LUCENE_USER_CONTEXT(_backend->ns->user); struct fts_index_header hdr; uint32_t set_checksum; int ret; if (fts_index_get_header(box, &hdr)) { set_checksum = fts_lucene_settings_checksum(&fuser->set); ret = fts_index_have_compatible_settings(_backend->ns->list, set_checksum); if (ret < 0) return -1; if (ret == 0) { /* need to rebuild the index */ *last_uid_r = 0; } else { *last_uid_r = hdr.last_indexed_uid; } return 0; } /* either nothing has been indexed, or the index was corrupted. do it the slow way. */ if (fts_backend_select(backend, box) < 0) return -1; if (lucene_index_get_last_uid(backend->index, last_uid_r) < 0) return -1; fts_index_set_last_uid(box, *last_uid_r); return 0; } static struct fts_backend_update_context * fts_backend_lucene_update_init(struct fts_backend *_backend) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; struct lucene_fts_backend_update_context *ctx; struct fts_lucene_user *fuser = FTS_LUCENE_USER_CONTEXT(_backend->ns->user); i_assert(!backend->updating); ctx = i_new(struct lucene_fts_backend_update_context, 1); ctx->ctx.backend = _backend; ctx->mime_parts = fuser->set.mime_parts; backend->updating = TRUE; return &ctx->ctx; } static bool fts_backend_lucene_need_optimize(struct lucene_fts_backend_update_context *ctx) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)ctx->ctx.backend; unsigned int expunges; uint32_t numdocs; if (ctx->added_msgs >= LUCENE_OPTIMIZE_BATCH_MSGS_COUNT) return TRUE; if (lucene_index_get_doc_count(backend->index, &numdocs) < 0) return FALSE; if (fts_expunge_log_uid_count(backend->expunge_log, &expunges) < 0) return FALSE; return expunges > 0 && numdocs / expunges <= 50; /* >2% of index has been expunged */ } static int fts_backend_lucene_update_deinit(struct fts_backend_update_context *_ctx) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_ctx->backend; int ret = _ctx->failed ? -1 : 0; i_assert(backend->updating); backend->updating = FALSE; if (ctx->lucene_opened) { if (lucene_index_build_deinit(backend->index) < 0) ret = -1; } if (ctx->expunge_ctx != NULL) { if (fts_expunge_log_append_commit(&ctx->expunge_ctx) < 0) { struct stat st; if (stat(backend->dir_path, &st) < 0 && errno == ENOENT) { /* lucene-indexes directory doesn't even exist, so dovecot.index's last_index_uid is wrong. rescan to update them. */ (void)lucene_index_rescan(backend->index); ret = 0; } ret = -1; } } if (fts_backend_lucene_need_optimize(ctx)) { if (ctx->lucene_opened) (void)fts_backend_optimize(_ctx->backend); else if (ctx->first_box_vname != NULL) { struct mail_user *user = backend->backend.ns->user; const char *cmd, *path; int fd; /* the optimize affects all mailboxes within namespace, so just use any mailbox name in it */ cmd = t_strdup_printf("OPTIMIZE\t0\t%s\t%s\n", str_tabescape(user->username), str_tabescape(ctx->first_box_vname)); fd = fts_indexer_cmd(user, cmd, &path); if (fd != -1) i_close_fd(&fd); } } i_free(ctx->first_box_vname); i_free(ctx); return ret; } static void fts_backend_lucene_update_set_mailbox(struct fts_backend_update_context *_ctx, struct mailbox *box) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; if (ctx->last_uid != 0) { fts_index_set_last_uid(ctx->box, ctx->last_uid); ctx->last_uid = 0; } if (ctx->first_box_vname == NULL && box != NULL) ctx->first_box_vname = i_strdup(box->vname); ctx->box = box; ctx->last_indexed_uid_set = FALSE; } static void fts_backend_lucene_update_expunge(struct fts_backend_update_context *_ctx, uint32_t uid) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_ctx->backend; struct fts_index_header hdr; if (!ctx->last_indexed_uid_set) { if (!fts_index_get_header(ctx->box, &hdr)) ctx->last_indexed_uid = 0; else ctx->last_indexed_uid = hdr.last_indexed_uid; ctx->last_indexed_uid_set = TRUE; } if (ctx->last_indexed_uid == 0 || uid > ctx->last_indexed_uid + 100) { /* don't waste time adding expunge to log for a message that isn't even indexed. this check is racy, because indexer may just be in the middle of indexing this message. we'll attempt to avoid that by skipping the expunging only if indexing hasn't been done for a while (100 msgs). */ return; } if (ctx->expunge_ctx == NULL) { ctx->expunge_ctx = fts_expunge_log_append_begin(backend->expunge_log); } if (fts_backend_select(backend, ctx->box) < 0) _ctx->failed = TRUE; fts_expunge_log_append_next(ctx->expunge_ctx, backend->selected_box_guid, uid); } static bool fts_backend_lucene_update_set_build_key(struct fts_backend_update_context *_ctx, const struct fts_backend_build_key *key) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_ctx->backend; if (!ctx->lucene_opened) { if (fts_backend_lucene_mkdir(backend) < 0) ctx->ctx.failed = TRUE; if (lucene_index_build_init(backend->index) < 0) ctx->ctx.failed = TRUE; ctx->lucene_opened = TRUE; } if (fts_backend_select(backend, ctx->box) < 0) _ctx->failed = TRUE; switch (key->type) { case FTS_BACKEND_BUILD_KEY_HDR: case FTS_BACKEND_BUILD_KEY_MIME_HDR: i_assert(key->hdr_name != NULL); i_free(ctx->hdr_name); ctx->hdr_name = i_strdup(key->hdr_name); break; case FTS_BACKEND_BUILD_KEY_BODY_PART: i_free_and_null(ctx->hdr_name); break; case FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY: i_unreached(); } if (key->uid != ctx->last_uid) { i_assert(key->uid >= ctx->last_uid); ctx->last_uid = key->uid; ctx->added_msgs++; } ctx->uid = key->uid; if (ctx->mime_parts) ctx->part_num = message_part_to_idx(key->part); return TRUE; } static void fts_backend_lucene_update_unset_build_key(struct fts_backend_update_context *_ctx) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; ctx->uid = 0; ctx->part_num = 0; i_free_and_null(ctx->hdr_name); } static int fts_backend_lucene_update_build_more(struct fts_backend_update_context *_ctx, const unsigned char *data, size_t size) { struct lucene_fts_backend_update_context *ctx = (struct lucene_fts_backend_update_context *)_ctx; struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_ctx->backend; int ret; i_assert(ctx->uid != 0); if (_ctx->failed) return -1; T_BEGIN { ret = lucene_index_build_more(backend->index, ctx->uid, ctx->part_num, data, size, ctx->hdr_name); } T_END; return ret; } static int fts_backend_lucene_refresh(struct fts_backend *_backend) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; if (backend->index != NULL) lucene_index_close(backend->index); return 0; } static int fts_backend_lucene_rescan(struct fts_backend *_backend) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; if (lucene_index_rescan(backend->index) < 0) return -1; return lucene_index_optimize(backend->index); } static int fts_backend_lucene_optimize(struct fts_backend *_backend) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; int ret; ret = lucene_index_expunge_from_log(backend->index, backend->expunge_log); if (ret == 0) { /* log was corrupted, need to rescan */ ret = lucene_index_rescan(backend->index); } if (ret >= 0) ret = lucene_index_optimize(backend->index); return ret; } static int fts_backend_lucene_lookup(struct fts_backend *_backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; int ret; if (fts_backend_select(backend, box) < 0) return -1; T_BEGIN { ret = lucene_index_lookup(backend->index, args, flags, result); } T_END; return ret; } /* a char* hash function from ASU -- from glib */ static unsigned int wstr_hash(const wchar_t *s) { unsigned int g, h = 0; while (*s != '\0') { h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h; } static int mailboxes_get_guids(struct mailbox *const boxes[], HASH_TABLE_TYPE(wguid_result) guids, struct fts_multi_result *result) { ARRAY(struct fts_result) box_results; struct fts_result *box_result; const char *guid; wchar_t *guid_dup; unsigned int i, j; p_array_init(&box_results, result->pool, 32); /* first create the box_results - we'll be using pointers to them later on and appending to the array changes the pointers */ for (i = 0; boxes[i] != NULL; i++) { box_result = array_append_space(&box_results); box_result->box = boxes[i]; } for (i = 0; boxes[i] != NULL; i++) { if (fts_mailbox_get_guid(boxes[i], &guid) < 0) return -1; i_assert(strlen(guid) == MAILBOX_GUID_HEX_LENGTH); guid_dup = t_new(wchar_t, MAILBOX_GUID_HEX_LENGTH + 1); for (j = 0; j < MAILBOX_GUID_HEX_LENGTH; j++) guid_dup[j] = guid[j]; box_result = array_idx_modifiable(&box_results, i); hash_table_insert(guids, guid_dup, box_result); } array_append_zero(&box_results); result->box_results = array_idx_modifiable(&box_results, 0); return 0; } static int fts_backend_lucene_lookup_multi(struct fts_backend *_backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result) { struct lucene_fts_backend *backend = (struct lucene_fts_backend *)_backend; int ret; T_BEGIN { HASH_TABLE_TYPE(wguid_result) guids; hash_table_create(&guids, default_pool, 0, wstr_hash, wcscmp); ret = mailboxes_get_guids(boxes, guids, result); if (ret == 0) { ret = lucene_index_lookup_multi(backend->index, guids, args, flags, result); } hash_table_destroy(&guids); } T_END; return ret; } static void fts_backend_lucene_lookup_done(struct fts_backend *_backend) { /* the next refresh is going to close the index anyway, so we might as well do it now */ (void)fts_backend_lucene_refresh(_backend); } struct fts_backend fts_backend_lucene = { .name = "lucene", .flags = FTS_BACKEND_FLAG_BUILD_FULL_WORDS | FTS_BACKEND_FLAG_FUZZY_SEARCH, { fts_backend_lucene_alloc, fts_backend_lucene_init, fts_backend_lucene_deinit, fts_backend_lucene_get_last_uid, fts_backend_lucene_update_init, fts_backend_lucene_update_deinit, fts_backend_lucene_update_set_mailbox, fts_backend_lucene_update_expunge, fts_backend_lucene_update_set_build_key, fts_backend_lucene_update_unset_build_key, fts_backend_lucene_update_build_more, fts_backend_lucene_refresh, fts_backend_lucene_rescan, fts_backend_lucene_optimize, fts_backend_default_can_lookup, fts_backend_lucene_lookup, fts_backend_lucene_lookup_multi, fts_backend_lucene_lookup_done } }; dovecot-2.2.33.2/src/plugins/fts-lucene/Makefile.am0000644000175000017500000000242613123174404016701 00000000000000doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts \ -I$(top_srcdir)/src/doveadm AM_CXXFLAGS = \ $(CLUCENE_CFLAGS) \ $(LIBEXTTEXTCAT_CFLAGS) NOPLUGIN_LDFLAGS = lib21_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version lib20_doveadm_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_lucene_plugin.la if BUILD_FTS_STEMMER STEMMER_LIBS = -lstemmer SHOWBALL_SOURCES = Snowball.cc endif if BUILD_FTS_EXTTEXTCAT TEXTCAT_LIBS = $(LIBEXTTEXTCAT_LIBS) else if BUILD_FTS_TEXTCAT TEXTCAT_LIBS = -ltextcat endif endif lib21_fts_lucene_plugin_la_LIBADD = \ $(CLUCENE_LIBS) $(TEXTCAT_LIBS) $(STEMMER_LIBS) lib21_fts_lucene_plugin_la_SOURCES = \ fts-lucene-plugin.c \ fts-backend-lucene.c \ lucene-wrapper.cc \ $(SHOWBALL_SOURCES) noinst_HEADERS = \ fts-lucene-plugin.h \ lucene-wrapper.h \ SnowballAnalyzer.h \ SnowballFilter.h if BUILD_FTS_TEXTCAT exampledir = $(docdir)/example-config example_DATA = \ textcat.conf endif EXTRA_DIST = textcat.conf doveadm_module_LTLIBRARIES = \ lib20_doveadm_fts_lucene_plugin.la lib20_doveadm_fts_lucene_plugin_la_SOURCES = \ doveadm-fts-lucene.c dovecot-2.2.33.2/src/plugins/imap-stats/0002755000175000017500000000000013172375613014751 500000000000000dovecot-2.2.33.2/src/plugins/imap-stats/Makefile.in0000644000175000017500000005620713172375574016754 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/imap-stats ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(imap_moduledir)" LTLIBRARIES = $(imap_module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib95_imap_stats_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../stats/lib90_stats_plugin.la am_lib95_imap_stats_plugin_la_OBJECTS = imap-stats-plugin.lo lib95_imap_stats_plugin_la_OBJECTS = \ $(am_lib95_imap_stats_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib95_imap_stats_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib95_imap_stats_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib95_imap_stats_plugin_la_SOURCES) DIST_SOURCES = $(lib95_imap_stats_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/stats imap_moduledir = $(moduledir) lib95_imap_stats_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib95_imap_stats_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib95_imap_stats_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../stats/lib90_stats_plugin.la lib95_imap_stats_plugin_la_SOURCES = \ imap-stats-plugin.c noinst_HEADERS = \ imap-stats-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/imap-stats/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/imap-stats/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ } uninstall-imap_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ done clean-imap_moduleLTLIBRARIES: -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) @list='$(imap_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib95_imap_stats_plugin.la: $(lib95_imap_stats_plugin_la_OBJECTS) $(lib95_imap_stats_plugin_la_DEPENDENCIES) $(EXTRA_lib95_imap_stats_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib95_imap_stats_plugin_la_LINK) -rpath $(imap_moduledir) $(lib95_imap_stats_plugin_la_OBJECTS) $(lib95_imap_stats_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-stats-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(imap_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-imap_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-imap_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-imap_moduleLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-imap_moduleLTLIBRARIES install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-imap_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/imap-stats/imap-stats-plugin.h0000644000175000017500000000043113123174404020403 00000000000000#ifndef IMAP_STATS_PLUGIN_H #define IMAP_STATS_PLUGIN_H struct module; extern const char *imap_stats_plugin_dependencies[]; extern const char imap_stats_plugin_binary_dependency[]; void imap_stats_plugin_init(struct module *module); void imap_stats_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/imap-stats/imap-stats-plugin.c0000644000175000017500000000671213123174404020406 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "base64.h" #include "str.h" #include "imap-commands.h" #include "stats.h" #include "stats-plugin.h" #include "stats-connection.h" #include "imap-stats-plugin.h" #define IMAP_STATS_IMAP_CONTEXT(obj) \ MODULE_CONTEXT(obj, imap_stats_imap_module) struct stats_client_command { union imap_module_context module_ctx; unsigned int id; bool continued; struct stats *stats, *pre_stats; }; static MODULE_CONTEXT_DEFINE_INIT(imap_stats_imap_module, &imap_module_register); const char *imap_stats_plugin_version = DOVECOT_ABI_VERSION; static void stats_command_pre(struct client_command_context *cmd) { struct stats_user *suser = STATS_USER_CONTEXT(cmd->client->user); struct stats_client_command *scmd; static unsigned int stats_cmd_id_counter = 0; if (suser == NULL || !suser->track_commands) return; if (strcasecmp(cmd->name, "IDLE") == 0) { /* IDLE can run forever and waste stats process's memory while waiting for it to timeout. don't send them. */ return; } scmd = IMAP_STATS_IMAP_CONTEXT(cmd); if (scmd == NULL) { scmd = p_new(cmd->pool, struct stats_client_command, 1); scmd->id = ++stats_cmd_id_counter; scmd->stats = stats_alloc(cmd->pool); scmd->pre_stats = stats_alloc(cmd->pool); MODULE_CONTEXT_SET(cmd, imap_stats_imap_module, scmd); } mail_user_stats_fill(cmd->client->user, scmd->pre_stats); } static void stats_command_post(struct client_command_context *cmd) { struct stats_user *suser = STATS_USER_CONTEXT(cmd->client->user); struct stats_client_command *scmd = IMAP_STATS_IMAP_CONTEXT(cmd); struct stats *new_stats, *diff_stats; const char *error; size_t args_pos = 0, args_len = 0; string_t *str; buffer_t *buf; if (scmd == NULL) return; new_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); mail_user_stats_fill(cmd->client->user, new_stats); if (!stats_diff(scmd->pre_stats, new_stats, diff_stats, &error)) i_error("stats: command stats shrank: %s", error); stats_add(scmd->stats, diff_stats); str = t_str_new(128); str_append(str, "UPDATE-CMD\t"); str_append(str, suser->stats_session_id); str_printfa(str, "\t%u\t", scmd->id); if (cmd->state == CLIENT_COMMAND_STATE_DONE) str_append_c(str, 'd'); if (scmd->continued) str_append_c(str, 'c'); else { str_append_c(str, '\t'); str_append(str, cmd->name); str_append_c(str, '\t'); args_pos = str_len(str); if (cmd->args != NULL) str_append(str, cmd->args); args_len = str_len(str) - args_pos; scmd->continued = TRUE; } buf = buffer_create_dynamic(pool_datastack_create(), 128); stats_export(buf, scmd->stats); str_append_c(str, '\t'); base64_encode(buf->data, buf->used, str); str_append_c(str, '\n'); if (str_len(str) > PIPE_BUF) { /* truncate the args so it fits */ size_t delete_count = str_len(str) - PIPE_BUF; i_assert(args_pos != 0); if (delete_count > args_len) delete_count = args_len; str_delete(str, args_pos + args_len - delete_count, delete_count); } stats_connection_send(suser->stats_conn, str); } void imap_stats_plugin_init(struct module *module ATTR_UNUSED) { command_hook_register(stats_command_pre, stats_command_post); } void imap_stats_plugin_deinit(void) { command_hook_unregister(stats_command_pre, stats_command_post); } const char *imap_stats_plugin_dependencies[] = { "stats", NULL }; const char imap_stats_plugin_binary_dependency[] = "imap"; dovecot-2.2.33.2/src/plugins/imap-stats/Makefile.am0000644000175000017500000000120113123174404016704 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/stats imap_moduledir = $(moduledir) NOPLUGIN_LDFLAGS = lib95_imap_stats_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib95_imap_stats_plugin.la if DOVECOT_PLUGIN_DEPS lib95_imap_stats_plugin_la_LIBADD = \ ../stats/lib90_stats_plugin.la endif lib95_imap_stats_plugin_la_SOURCES = \ imap-stats-plugin.c noinst_HEADERS = \ imap-stats-plugin.h dovecot-2.2.33.2/src/plugins/virtual/0002755000175000017500000000000013172375613014355 500000000000000dovecot-2.2.33.2/src/plugins/virtual/virtual-plugin.c0000644000175000017500000000132213123174404017406 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-namespace.h" #include "virtual-storage.h" #include "virtual-plugin.h" const char *virtual_plugin_version = DOVECOT_ABI_VERSION; static struct mail_storage_hooks acl_mail_storage_hooks = { .mailbox_allocated = virtual_backend_mailbox_allocated, .mailbox_opened = virtual_backend_mailbox_opened }; void virtual_plugin_init(struct module *module ATTR_UNUSED) { mail_storage_class_register(&virtual_storage); mail_storage_hooks_add(module, &acl_mail_storage_hooks); } void virtual_plugin_deinit(void) { mail_storage_class_unregister(&virtual_storage); mail_storage_hooks_remove(&acl_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/virtual/Makefile.in0000644000175000017500000005702013172375575016353 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/virtual ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_virtual_plugin_la_LIBADD = am_lib20_virtual_plugin_la_OBJECTS = virtual-config.lo virtual-mail.lo \ virtual-plugin.lo virtual-search.lo virtual-storage.lo \ virtual-save.lo virtual-sync.lo virtual-transaction.lo lib20_virtual_plugin_la_OBJECTS = \ $(am_lib20_virtual_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_virtual_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_virtual_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_virtual_plugin_la_SOURCES) DIST_SOURCES = $(lib20_virtual_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-imap-storage lib20_virtual_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_virtual_plugin.la lib20_virtual_plugin_la_SOURCES = \ virtual-config.c \ virtual-mail.c \ virtual-plugin.c \ virtual-search.c \ virtual-storage.c \ virtual-save.c \ virtual-sync.c \ virtual-transaction.c noinst_HEADERS = \ virtual-plugin.h \ virtual-storage.h \ virtual-transaction.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/virtual/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/virtual/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_virtual_plugin.la: $(lib20_virtual_plugin_la_OBJECTS) $(lib20_virtual_plugin_la_DEPENDENCIES) $(EXTRA_lib20_virtual_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_virtual_plugin_la_LINK) -rpath $(moduledir) $(lib20_virtual_plugin_la_OBJECTS) $(lib20_virtual_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-save.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual-transaction.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/virtual/virtual-plugin.h0000644000175000017500000000021613123174404017414 00000000000000#ifndef VIRTUAL_PLUGIN_H #define VIRTUAL_PLUGIN_H void virtual_plugin_init(struct module *module); void virtual_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/virtual/virtual-sync.c0000644000175000017500000015212613165463624017110 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "ioloop.h" #include "str.h" #include "mail-index-modseq.h" #include "mail-search-build.h" #include "mailbox-search-result-private.h" #include "index-sync-private.h" #include "index-search-result.h" #include "virtual-storage.h" struct virtual_add_record { struct virtual_mail_index_record rec; time_t received_date; }; struct virtual_sync_mail { uint32_t vseq; struct virtual_mail_index_record vrec; }; struct virtual_sync_context { struct virtual_mailbox *mbox; struct mail_index_sync_ctx *index_sync_ctx; struct mail_index *index; struct mail_index_view *sync_view; struct mail_index_transaction *trans; const char *const *kw_all; /* messages expunged within this sync */ ARRAY_TYPE(seq_range) sync_expunges; ARRAY(struct virtual_add_record) all_adds; /* all messages in this sync, sorted by mailbox_id (but unsorted inside it for now, since it doesn't matter) */ ARRAY(struct virtual_sync_mail) all_mails; uint32_t all_mails_idx, all_mails_prev_mailbox_id; enum mailbox_sync_flags flags; uint32_t uid_validity; unsigned int ext_header_changed:1; unsigned int expunge_removed:1; unsigned int index_broken:1; }; static void virtual_sync_backend_box_deleted(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox); static void virtual_sync_set_uidvalidity(struct virtual_sync_context *ctx) { uint32_t uid_validity = ioloop_time; mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); ctx->uid_validity = uid_validity; } static void virtual_sync_external_flags(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, uint32_t vseq, uint32_t real_uid) { enum mail_flags flags; const char *const *kw_names; struct mail_keywords *keywords; if (!mail_set_uid(bbox->sync_mail, real_uid)) { /* we may have reopened the mailbox, which could have caused the mail to be expunged already. */ return; } /* copy flags */ flags = mail_get_flags(bbox->sync_mail); /* we don't need to keep recent flags here */ mail_index_update_flags(ctx->trans, vseq, MODIFY_REPLACE, flags & ~(MAIL_RECENT)); /* copy keywords */ kw_names = mail_get_keywords(bbox->sync_mail); keywords = mail_index_keywords_create(ctx->index, kw_names); mail_index_update_keywords(ctx->trans, vseq, MODIFY_REPLACE, keywords); mail_index_keywords_unref(&keywords); } static int virtual_sync_mail_uid_cmp(const void *p1, const void *p2) { const struct virtual_sync_mail *m1 = p1, *m2 = p2; if (m1->vrec.mailbox_id < m2->vrec.mailbox_id) return -1; if (m1->vrec.mailbox_id > m2->vrec.mailbox_id) return 1; if (m1->vrec.real_uid < m2->vrec.real_uid) return -1; if (m1->vrec.real_uid > m2->vrec.real_uid) return 1; /* broken */ return 0; } static void virtual_backend_box_sync_mail_set(struct virtual_backend_box *bbox) { struct mailbox_transaction_context *trans; if (bbox->sync_mail == NULL) { trans = mailbox_transaction_begin(bbox->box, 0); bbox->sync_mail = mail_alloc(trans, 0, NULL); } } static int bbox_mailbox_id_cmp(struct virtual_backend_box *const *b1, struct virtual_backend_box *const *b2) { if ((*b1)->mailbox_id < (*b2)->mailbox_id) return -1; if ((*b1)->mailbox_id > (*b2)->mailbox_id) return 1; return 0; } static int virtual_sync_get_backend_box(struct virtual_mailbox *mbox, const char *name, struct virtual_backend_box **bbox_r) { *bbox_r = virtual_backend_box_lookup_name(mbox, name); if (*bbox_r != NULL || !mbox->sync_initialized) return 0; /* another process just added a new mailbox. we can't handle this currently. */ mbox->inconsistent = TRUE; mail_storage_set_error(mbox->box.storage, MAIL_ERROR_TEMP, t_strdup_printf( "Backend mailbox '%s' added by another session. " "Reopen the virtual mailbox.", name)); return -1; } int virtual_mailbox_ext_header_read(struct virtual_mailbox *mbox, struct mail_index_view *view, bool *broken_r) { const char *box_path = mailbox_get_path(&mbox->box); const struct virtual_mail_index_header *ext_hdr; const struct mail_index_header *hdr; const struct virtual_mail_index_mailbox_record *mailboxes; struct virtual_backend_box *bbox, **bboxes; const void *ext_data; size_t ext_size; unsigned int i, count, ext_name_offset, ext_mailbox_count; uint32_t prev_mailbox_id; int ret = 1; *broken_r = FALSE; hdr = mail_index_get_header(view); mail_index_get_header_ext(view, mbox->virtual_ext_id, &ext_data, &ext_size); ext_hdr = ext_data; if (mbox->sync_initialized && mbox->prev_uid_validity == hdr->uid_validity && ext_size >= sizeof(*ext_hdr) && mbox->prev_change_counter == ext_hdr->change_counter) { /* fully refreshed */ return 1; } mbox->prev_uid_validity = hdr->uid_validity; if (ext_hdr == NULL || mbox->search_args_crc32 != ext_hdr->search_args_crc32) { mailboxes = NULL; ext_name_offset = 0; ext_mailbox_count = 0; ret = 0; } else { const void *guid_data; size_t guid_size; mail_index_get_header_ext(view, mbox->virtual_guid_ext_id, &guid_data, &guid_size); if (guid_size >= GUID_128_SIZE) guid_128_copy(mbox->guid, guid_data); mbox->prev_change_counter = ext_hdr->change_counter; mailboxes = (const void *)(ext_hdr + 1); ext_name_offset = sizeof(*ext_hdr) + ext_hdr->mailbox_count * sizeof(*mailboxes); if (ext_name_offset >= ext_size || ext_hdr->mailbox_count > INT_MAX/sizeof(*mailboxes)) { i_error("virtual index %s: Broken mailbox_count header", box_path); *broken_r = TRUE; ext_mailbox_count = 0; ret = 0; } else { ext_mailbox_count = ext_hdr->mailbox_count; } } /* update mailbox backends */ prev_mailbox_id = 0; for (i = 0; i < ext_mailbox_count; i++) { if (mailboxes[i].id > ext_hdr->highest_mailbox_id || mailboxes[i].id <= prev_mailbox_id) { i_error("virtual index %s: Broken mailbox id", box_path); break; } if (mailboxes[i].name_len == 0 || mailboxes[i].name_len > ext_size) { i_error("virtual index %s: Broken mailbox name_len", box_path); break; } if (ext_name_offset + mailboxes[i].name_len > ext_size) { i_error("virtual index %s: Broken mailbox list", box_path); break; } T_BEGIN { const unsigned char *nameptr; const char *name; nameptr = CONST_PTR_OFFSET(ext_data, ext_name_offset); name = t_strndup(nameptr, mailboxes[i].name_len); if (virtual_sync_get_backend_box(mbox, name, &bbox) < 0) ret = -1; } T_END; if (bbox == NULL) { if (ret < 0) return -1; /* mailbox no longer exists. */ ret = 0; } else { bbox->mailbox_id = mailboxes[i].id; bbox->sync_uid_validity = mailboxes[i].uid_validity; bbox->ondisk_highest_modseq = bbox->sync_highest_modseq = mailboxes[i].highest_modseq; bbox->sync_next_uid = mailboxes[i].next_uid; bbox->sync_mailbox_idx = i; } ext_name_offset += mailboxes[i].name_len; prev_mailbox_id = mailboxes[i].id; } if (i < ext_mailbox_count) { *broken_r = TRUE; mbox->ext_header_rewrite = TRUE; ret = 0; } mbox->highest_mailbox_id = ext_hdr == NULL ? 0 : ext_hdr->highest_mailbox_id; /* do not mark it initialized if it's broken */ mbox->sync_initialized = !*broken_r; /* assign new mailbox IDs if any are missing */ bboxes = array_get_modifiable(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->mailbox_id == 0) { bboxes[i]->mailbox_id = ++mbox->highest_mailbox_id; ret = 0; } } /* sort the backend mailboxes by mailbox_id. */ array_sort(&mbox->backend_boxes, bbox_mailbox_id_cmp); if (ret == 0) mbox->ext_header_rewrite = TRUE; return ret; } static void virtual_sync_ext_header_rewrite(struct virtual_sync_context *ctx) { struct virtual_mail_index_header ext_hdr; struct virtual_mail_index_mailbox_record mailbox; struct virtual_backend_box **bboxes; buffer_t *buf; const void *ext_data; size_t ext_size; unsigned int i, mailbox_pos, name_pos, count; bboxes = array_get_modifiable(&ctx->mbox->backend_boxes, &count); mailbox_pos = sizeof(ext_hdr); name_pos = mailbox_pos + sizeof(mailbox) * count; i_zero(&ext_hdr); i_zero(&mailbox); ext_hdr.change_counter = ++ctx->mbox->prev_change_counter; ext_hdr.mailbox_count = count; ext_hdr.highest_mailbox_id = ctx->mbox->highest_mailbox_id; ext_hdr.search_args_crc32 = ctx->mbox->search_args_crc32; buf = buffer_create_dynamic(pool_datastack_create(), name_pos + 256); buffer_append(buf, &ext_hdr, sizeof(ext_hdr)); for (i = 0; i < count; i++) { i_assert(i == 0 || bboxes[i]->mailbox_id > bboxes[i-1]->mailbox_id); bboxes[i]->sync_mailbox_idx = i; mailbox.id = bboxes[i]->mailbox_id; mailbox.name_len = strlen(bboxes[i]->name); mailbox.uid_validity = bboxes[i]->sync_uid_validity; mailbox.highest_modseq = bboxes[i]->ondisk_highest_modseq; mailbox.next_uid = bboxes[i]->sync_next_uid; buffer_write(buf, mailbox_pos, &mailbox, sizeof(mailbox)); buffer_write(buf, name_pos, bboxes[i]->name, mailbox.name_len); mailbox_pos += sizeof(mailbox); name_pos += mailbox.name_len; /* repair the value */ if (ctx->mbox->highest_mailbox_id < mailbox.id) ctx->mbox->highest_mailbox_id = mailbox.id; } if (ctx->mbox->highest_mailbox_id != ext_hdr.highest_mailbox_id) { ext_hdr.highest_mailbox_id = ctx->mbox->highest_mailbox_id; buffer_write(buf, 0, &ext_hdr, sizeof(ext_hdr)); } i_assert(buf->used == name_pos); mail_index_get_header_ext(ctx->sync_view, ctx->mbox->virtual_ext_id, &ext_data, &ext_size); if (ext_size < name_pos) { mail_index_ext_resize(ctx->trans, ctx->mbox->virtual_ext_id, name_pos, sizeof(struct virtual_mail_index_record), sizeof(uint32_t)); } mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id, 0, buf->data, name_pos); } static void virtual_sync_ext_header_update(struct virtual_sync_context *ctx) { struct virtual_mail_index_header ext_hdr; if (!ctx->ext_header_changed) return; /* we changed something - update the change counter in header */ ext_hdr.change_counter = ++ctx->mbox->prev_change_counter; mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id, offsetof(struct virtual_mail_index_header, change_counter), &ext_hdr.change_counter, sizeof(ext_hdr.change_counter)); } static int virtual_sync_index_rec(struct virtual_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; struct virtual_backend_box *bbox; const struct virtual_mail_index_record *vrec; const void *data; enum mail_flags flags; struct mail_keywords *keywords; enum modify_type modify_type; const char *kw_names[2]; uint32_t vseq, seq1, seq2; switch (sync_rec->type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: case MAIL_INDEX_SYNC_TYPE_FLAGS: case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: break; } if (!mail_index_lookup_seq_range(ctx->sync_view, sync_rec->uid1, sync_rec->uid2, &seq1, &seq2)) { /* already expunged, nothing to do. */ return 0; } for (vseq = seq1; vseq <= seq2; vseq++) { mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id, &data, NULL); vrec = data; bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); if (bbox == NULL) continue; if (!bbox->box->opened) { if (virtual_backend_box_open(ctx->mbox, bbox) < 0) { virtual_box_copy_error(&ctx->mbox->box, bbox->box); return -1; } } else { virtual_backend_box_accessed(ctx->mbox, bbox); } virtual_backend_box_sync_mail_set(bbox); if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) { /* message is already expunged from backend mailbox. */ continue; } switch (sync_rec->type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: mail_expunge(bbox->sync_mail); break; case MAIL_INDEX_SYNC_TYPE_FLAGS: flags = sync_rec->add_flags & MAIL_FLAGS_NONRECENT; if (flags != 0) { mail_update_flags(bbox->sync_mail, MODIFY_ADD, flags); } flags = sync_rec->remove_flags & MAIL_FLAGS_NONRECENT; if (flags != 0) { mail_update_flags(bbox->sync_mail, MODIFY_REMOVE, flags); } break; case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: kw_names[0] = ctx->kw_all[sync_rec->keyword_idx]; kw_names[1] = NULL; keywords = mailbox_keywords_create_valid(bbox->box, kw_names); modify_type = sync_rec->type == MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD ? MODIFY_ADD : MODIFY_REMOVE; mail_update_keywords(bbox->sync_mail, modify_type, keywords); mailbox_keywords_unref(&keywords); break; } } return 0; } static int virtual_sync_index_changes(struct virtual_sync_context *ctx) { const ARRAY_TYPE(keywords) *keywords; struct mail_index_sync_rec sync_rec; keywords = mail_index_get_keywords(ctx->index); ctx->kw_all = array_count(keywords) == 0 ? NULL : array_idx(keywords, 0); while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) { if (virtual_sync_index_rec(ctx, &sync_rec) < 0) return -1; } return 0; } static void virtual_sync_index_finish(struct virtual_sync_context *ctx) { struct mailbox *box = &ctx->mbox->box; const struct mail_index_header *hdr; struct mail_index_view *view; uint32_t seq1, seq2; view = mail_index_transaction_open_updated_view(ctx->trans); hdr = mail_index_get_header(ctx->sync_view); if (hdr->uid_validity != 0) ctx->uid_validity = hdr->uid_validity; else virtual_sync_set_uidvalidity(ctx); /* mark the newly seen messages as recent */ if (mail_index_lookup_seq_range(view, hdr->first_recent_uid, (uint32_t)-1, &seq1, &seq2)) { mailbox_recent_flags_set_seqs(&ctx->mbox->box, view, seq1, seq2); } mail_index_view_close(&view); if (ctx->mbox->ext_header_rewrite) { /* entire mailbox list needs to be rewritten */ virtual_sync_ext_header_rewrite(ctx); } else { /* update only changed parts in the header */ virtual_sync_ext_header_update(ctx); } if (box->v.sync_notify != NULL) box->v.sync_notify(box, 0, 0); } static int virtual_sync_backend_box_init(struct virtual_backend_box *bbox) { struct mailbox_transaction_context *trans; struct mail_search_context *search_ctx; struct mail *mail; struct virtual_backend_uidmap uidmap; enum mailbox_search_result_flags result_flags; int ret; trans = mailbox_transaction_begin(bbox->box, 0); if (!bbox->search_args_initialized) { mail_search_args_init(bbox->search_args, bbox->box, FALSE, NULL); bbox->search_args_initialized = TRUE; } search_ctx = mailbox_search_init(trans, bbox->search_args, NULL, 0, NULL); /* save the result and keep it updated */ result_flags = MAILBOX_SEARCH_RESULT_FLAG_UPDATE | MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC; bbox->search_result = mailbox_search_result_save(search_ctx, result_flags); /* add the found UIDs to uidmap. virtual_uid gets assigned later. */ i_zero(&uidmap); array_clear(&bbox->uids); while (mailbox_search_next(search_ctx, &mail)) { uidmap.real_uid = mail->uid; array_append(&bbox->uids, &uidmap, 1); } ret = mailbox_search_deinit(&search_ctx); (void)mailbox_transaction_commit(&trans); return ret; } static int virtual_backend_uidmap_bsearch_cmp(const uint32_t *uidp, const struct virtual_backend_uidmap *uidmap) { return *uidp < uidmap->real_uid ? -1 : (*uidp > uidmap->real_uid ? 1 : 0); } static void virtual_sync_mailbox_box_remove(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, const ARRAY_TYPE(seq_range) *removed_uids) { const struct seq_range *uids; struct virtual_backend_uidmap *uidmap; unsigned int i, src, dest, uid_count, rec_count; uint32_t uid, vseq; uids = array_get(removed_uids, &uid_count); if (uid_count == 0) return; /* everything in removed_uids should exist in bbox->uids */ uidmap = array_get_modifiable(&bbox->uids, &rec_count); i_assert(rec_count >= uid_count); /* find the first uidmap record to be removed */ if (!array_bsearch_insert_pos(&bbox->uids, &uids[0].seq1, virtual_backend_uidmap_bsearch_cmp, &src)) i_unreached(); /* remove the unwanted messages */ dest = src; for (i = 0; i < uid_count; i++) { uid = uids[i].seq1; while (uidmap[src].real_uid != uid) { uidmap[dest++] = uidmap[src++]; i_assert(src < rec_count); } for (; uid <= uids[i].seq2; uid++, src++) { i_assert(src < rec_count); i_assert(uidmap[src].real_uid == uid); if (uidmap[src].virtual_uid == 0) { /* has not been assigned yet */ continue; } if (mail_index_lookup_seq(ctx->sync_view, uidmap[src].virtual_uid, &vseq)) mail_index_expunge(ctx->trans, vseq); } } array_delete(&bbox->uids, dest, src - dest); } static void virtual_sync_mailbox_box_add(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, const ARRAY_TYPE(seq_range) *added_uids_arr) { const struct seq_range *added_uids; struct virtual_backend_uidmap *uidmap; struct virtual_add_record rec; unsigned int i, src, dest, uid_count, add_count, rec_count; uint32_t add_uid; added_uids = array_get(added_uids_arr, &uid_count); if (uid_count == 0) return; add_count = seq_range_count(added_uids_arr); /* none of added_uids should exist in bbox->uids. find the position of the first inserted index. */ uidmap = array_get_modifiable(&bbox->uids, &rec_count); if (rec_count == 0 || added_uids[0].seq1 > uidmap[rec_count-1].real_uid) { /* fast path: usually messages are appended */ dest = rec_count; } else if (array_bsearch_insert_pos(&bbox->uids, &added_uids[0].seq1, virtual_backend_uidmap_bsearch_cmp, &dest)) i_unreached(); /* make space for all added UIDs. */ if (rec_count == dest) array_idx_clear(&bbox->uids, dest + add_count-1); else { array_copy(&bbox->uids.arr, dest + add_count, &bbox->uids.arr, dest, rec_count - dest); } uidmap = array_get_modifiable(&bbox->uids, &rec_count); src = dest + add_count; /* add/move the UIDs to their correct positions */ i_zero(&rec); rec.rec.mailbox_id = bbox->mailbox_id; for (i = 0; i < uid_count; i++) { add_uid = added_uids[i].seq1; while (src < rec_count && uidmap[src].real_uid < add_uid) uidmap[dest++] = uidmap[src++]; for (; add_uid <= added_uids[i].seq2; add_uid++, dest++) { i_assert(dest < rec_count); uidmap[dest].real_uid = add_uid; uidmap[dest].virtual_uid = 0; if (ctx->mbox->uids_mapped) { rec.rec.real_uid = add_uid; array_append(&ctx->all_adds, &rec, 1); } } } } static void virtual_sync_mailbox_box_update_flags(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, const ARRAY_TYPE(seq_range) *uids_arr) { unsigned int i, uid, vseq; struct virtual_backend_uidmap *vuid; struct seq_range_iter iter; i = 0; seq_range_array_iter_init(&iter, uids_arr); while(seq_range_array_iter_nth(&iter, i++, &uid)) { vuid = array_bsearch(&bbox->uids, &uid, virtual_backend_uidmap_bsearch_cmp); if (vuid == NULL || vuid->virtual_uid == 0 || !mail_index_lookup_seq(ctx->sync_view, vuid->virtual_uid, &vseq)) { /* the entry has been already removed either by us or some other session. doesn't matter, we don't need to update the flags. it might also have not yet been assigned a uid so we don't want to update the flags then either. */ continue; } virtual_sync_external_flags(ctx, bbox, vseq, vuid->real_uid); } } static int virtual_backend_uidmap_cmp(const struct virtual_backend_uidmap *u1, const struct virtual_backend_uidmap *u2) { if (u1->real_uid < u2->real_uid) return -1; if (u1->real_uid > u2->real_uid) return 1; return 0; } static void virtual_sync_bbox_uids_sort(struct virtual_backend_box *bbox) { /* the uidmap must be sorted by real_uids */ array_sort(&bbox->uids, virtual_backend_uidmap_cmp); bbox->uids_nonsorted = FALSE; } static void virtual_sync_backend_boxes_sort_uids(struct virtual_mailbox *mbox) { struct virtual_backend_box *const *bboxes; unsigned int i, count; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->uids_nonsorted) virtual_sync_bbox_uids_sort(bboxes[i]); } } static void virtual_sync_backend_add_vmsgs_results(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, uint32_t real_uid, struct mail_search_result *result, const uint32_t vseq) { struct virtual_backend_uidmap uidmap; uint32_t vuid, seq; mail_index_lookup_uid(ctx->sync_view, vseq, &vuid); i_zero(&uidmap); uidmap.real_uid = real_uid; uidmap.virtual_uid = vuid; array_append(&bbox->uids, &uidmap, 1); if (result == NULL) ; else if (mail_index_lookup_seq(bbox->box->view, real_uid, &seq)) seq_range_array_add(&result->uids, real_uid); else seq_range_array_add(&result->removed_uids, real_uid); } static void virtual_sync_backend_handle_old_vmsgs(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, struct mail_search_result *result) { const struct virtual_mail_index_record *vrec; const struct virtual_sync_mail *sync_mail, *sync_mails; const void *data; uint32_t i, vseq, messages; /* find the messages that currently exist in virtual index and add them to the backend mailbox's list of uids. */ array_clear(&bbox->uids); if (array_is_created(&ctx->all_mails)) { i_assert(ctx->all_mails_prev_mailbox_id < bbox->mailbox_id); sync_mails = array_get(&ctx->all_mails, &messages); for (i = ctx->all_mails_idx; i < messages; i++) { sync_mail = &sync_mails[i]; if (sync_mail->vrec.mailbox_id != bbox->mailbox_id) { if (sync_mail->vrec.mailbox_id < bbox->mailbox_id) { /* stale mailbox_id, ignore */ continue; } /* Should be in mailbox_id order, so skip to next box */ break; } virtual_sync_backend_add_vmsgs_results(ctx, bbox, sync_mail->vrec.real_uid, result, sync_mail->vseq); } ctx->all_mails_idx = i; ctx->all_mails_prev_mailbox_id = bbox->mailbox_id; } else { /* there should be only a single backend mailbox, but in the existing index there may be stale mailbox_ids that we'll just skip over. */ messages = mail_index_view_get_messages_count(ctx->sync_view); for (vseq = 1; vseq <= messages; vseq++) { mail_index_lookup_ext(ctx->sync_view, vseq, ctx->mbox->virtual_ext_id, &data, NULL); vrec = data; if (vrec->mailbox_id == bbox->mailbox_id) { virtual_sync_backend_add_vmsgs_results(ctx, bbox, vrec->real_uid, result, vseq); } } } virtual_sync_bbox_uids_sort(bbox); } static int virtual_sync_backend_box_continue(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox) { const enum mailbox_search_result_flags result_flags = MAILBOX_SEARCH_RESULT_FLAG_UPDATE | MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC; struct mail_index_view *view = bbox->box->view; struct mail_search_result *result; ARRAY_TYPE(seq_range) expunged_uids = ARRAY_INIT, removed_uids; ARRAY_TYPE(seq_range) added_uids, flag_update_uids; uint64_t modseq, old_highest_modseq; uint32_t seq, uid, old_msg_count; /* initialize the search result from all the existing messages in virtual index. */ if (!bbox->search_args_initialized) { mail_search_args_init(bbox->search_args, bbox->box, FALSE, NULL); bbox->search_args_initialized = TRUE; } result = mailbox_search_result_alloc(bbox->box, bbox->search_args, result_flags); mailbox_search_result_initial_done(result); i_assert(array_count(&result->removed_uids) == 0); virtual_sync_backend_handle_old_vmsgs(ctx, bbox, result); if (array_count(&result->removed_uids) > 0) { /* these are all expunged messages. treat them separately from "no longer matching messages" (=removed_uids) */ t_array_init(&expunged_uids, array_count(&result->removed_uids)); array_append_array(&expunged_uids, &result->removed_uids); array_clear(&result->removed_uids); } /* get list of changed old messages (messages already once seen by virtual index), based on modseq changes. (we'll assume all modseq changes are due to flag changes, which may not be true in future) */ if (bbox->sync_next_uid <= 1 || !mail_index_lookup_seq_range(view, 1, bbox->sync_next_uid-1, &seq, &old_msg_count)) old_msg_count = 0; old_highest_modseq = mail_index_modseq_get_highest(view); t_array_init(&flag_update_uids, I_MIN(128, old_msg_count)); if (bbox->sync_highest_modseq < old_highest_modseq) { for (seq = 1; seq <= old_msg_count; seq++) { modseq = mail_index_modseq_lookup(view, seq); if (modseq > bbox->sync_highest_modseq) { mail_index_lookup_uid(view, seq, &uid); seq_range_array_add(&flag_update_uids, uid); } } } /* update the search result based on the flag changes and new messages */ if (index_search_result_update_flags(result, &flag_update_uids) < 0 || index_search_result_update_appends(result, old_msg_count) < 0) { mailbox_search_result_free(&result); return -1; } t_array_init(&removed_uids, 128); t_array_init(&added_uids, 128); mailbox_search_result_sync(result, &removed_uids, &added_uids); if (array_is_created(&expunged_uids)) { seq_range_array_remove_seq_range(&removed_uids, &expunged_uids); virtual_sync_mailbox_box_remove(ctx, bbox, &expunged_uids); } if (ctx->expunge_removed) virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids); else { /* delayed remove */ seq_range_array_merge(&bbox->sync_pending_removes, &removed_uids); } virtual_sync_mailbox_box_add(ctx, bbox, &added_uids); virtual_sync_mailbox_box_update_flags(ctx, bbox, &flag_update_uids); bbox->search_result = result; return 0; } static void virtual_sync_drop_existing(struct virtual_backend_box *bbox, ARRAY_TYPE(seq_range) *added_uids) { ARRAY_TYPE(seq_range) drop_uids; const struct virtual_backend_uidmap *uidmap; struct seq_range_iter iter; unsigned int i, n = 0, count; uint32_t add_uid; seq_range_array_iter_init(&iter, added_uids); if (!seq_range_array_iter_nth(&iter, n++, &add_uid)) return; (void)array_bsearch_insert_pos(&bbox->uids, &add_uid, virtual_backend_uidmap_bsearch_cmp, &i); uidmap = array_get_modifiable(&bbox->uids, &count); if (i == count) return; t_array_init(&drop_uids, array_count(added_uids)); for (; i < count; ) { if (uidmap[i].real_uid < add_uid) { i++; continue; } if (uidmap[i].real_uid == add_uid) { seq_range_array_add(&drop_uids, add_uid); i++; } if (!seq_range_array_iter_nth(&iter, n++, &add_uid)) break; } seq_range_array_remove_seq_range(added_uids, &drop_uids); } static void virtual_sync_drop_nonexistent(struct virtual_backend_box *bbox, ARRAY_TYPE(seq_range) *removed_uids) { ARRAY_TYPE(seq_range) drop_uids; const struct virtual_backend_uidmap *uidmap; struct seq_range_iter iter; unsigned int i, n = 0, count; uint32_t remove_uid; bool iter_done = FALSE; seq_range_array_iter_init(&iter, removed_uids); if (!seq_range_array_iter_nth(&iter, n++, &remove_uid)) return; (void)array_bsearch_insert_pos(&bbox->uids, &remove_uid, virtual_backend_uidmap_bsearch_cmp, &i); t_array_init(&drop_uids, array_count(removed_uids)); iter_done = FALSE; uidmap = array_get_modifiable(&bbox->uids, &count); for (; i < count; ) { if (uidmap[i].real_uid < remove_uid) { i++; continue; } if (uidmap[i].real_uid != remove_uid) seq_range_array_add(&drop_uids, remove_uid); else i++; if (!seq_range_array_iter_nth(&iter, n++, &remove_uid)) { iter_done = TRUE; break; } } if (!iter_done) { do { seq_range_array_add(&drop_uids, remove_uid); } while (seq_range_array_iter_nth(&iter, n++, &remove_uid)); } seq_range_array_remove_seq_range(removed_uids, &drop_uids); } static void virtual_sync_mailbox_box_update(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox) { ARRAY_TYPE(seq_range) removed_uids, added_uids, temp_uids; unsigned int count1, count2; t_array_init(&removed_uids, 128); t_array_init(&added_uids, 128); mailbox_search_result_sync(bbox->search_result, &removed_uids, &added_uids); if (array_is_created(&bbox->sync_outside_expunges)) { seq_range_array_remove_seq_range(&bbox->sync_outside_expunges, &added_uids); seq_range_array_merge(&removed_uids, &bbox->sync_outside_expunges); array_clear(&bbox->sync_outside_expunges); } virtual_sync_drop_existing(bbox, &added_uids); virtual_sync_drop_nonexistent(bbox, &removed_uids); /* if any of the pending removes came back, we don't want to expunge them anymore. also since they already exist, remove them from added_uids. */ count1 = array_count(&bbox->sync_pending_removes); count2 = array_count(&added_uids); if (count1 > 0 && count2 > 0) { t_array_init(&temp_uids, count1); array_append_array(&temp_uids, &bbox->sync_pending_removes); if (seq_range_array_remove_seq_range( &bbox->sync_pending_removes, &added_uids) > 0) { seq_range_array_remove_seq_range(&added_uids, &temp_uids); } } if (!ctx->expunge_removed) { /* delay removing messages that don't match the search criteria, but don't delay removing expunged messages */ if (array_count(&ctx->sync_expunges) > 0) { seq_range_array_remove_seq_range(&bbox->sync_pending_removes, &ctx->sync_expunges); seq_range_array_remove_seq_range(&removed_uids, &ctx->sync_expunges); virtual_sync_mailbox_box_remove(ctx, bbox, &ctx->sync_expunges); } seq_range_array_merge(&bbox->sync_pending_removes, &removed_uids); } else if (array_count(&bbox->sync_pending_removes) > 0) { /* remove all current and old */ seq_range_array_merge(&bbox->sync_pending_removes, &removed_uids); virtual_sync_mailbox_box_remove(ctx, bbox, &bbox->sync_pending_removes); array_clear(&bbox->sync_pending_removes); } else { virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids); } virtual_sync_mailbox_box_add(ctx, bbox, &added_uids); } static bool virtual_sync_find_seqs(struct virtual_backend_box *bbox, const struct mailbox_sync_rec *sync_rec, unsigned int *idx1_r, unsigned int *idx2_r) { const struct virtual_backend_uidmap *uidmap; unsigned int idx, count; uint32_t uid1, uid2; mail_index_lookup_uid(bbox->box->view, sync_rec->seq1, &uid1); mail_index_lookup_uid(bbox->box->view, sync_rec->seq2, &uid2); (void)array_bsearch_insert_pos(&bbox->uids, &uid1, virtual_backend_uidmap_bsearch_cmp, &idx); uidmap = array_get_modifiable(&bbox->uids, &count); if (idx == count || uidmap[idx].real_uid > uid2) return FALSE; *idx1_r = idx; while (idx < count && uidmap[idx].real_uid <= uid2) idx++; *idx2_r = idx - 1; return TRUE; } static void virtual_sync_expunge_add(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, const struct mailbox_sync_rec *sync_rec) { struct virtual_backend_uidmap *uidmap; uint32_t uid1, uid2; unsigned int i, idx1, count; mail_index_lookup_uid(bbox->box->view, sync_rec->seq1, &uid1); mail_index_lookup_uid(bbox->box->view, sync_rec->seq2, &uid2); /* remember only the expunges for messages that already exist for this mailbox */ (void)array_bsearch_insert_pos(&bbox->uids, &uid1, virtual_backend_uidmap_bsearch_cmp, &idx1); uidmap = array_get_modifiable(&bbox->uids, &count); for (i = idx1; i < count; i++) { if (uidmap[i].real_uid > uid2) break; seq_range_array_add(&ctx->sync_expunges, uidmap[i].real_uid); } } static int virtual_sync_backend_box_sync(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, enum mailbox_sync_flags sync_flags) { struct mailbox_sync_context *sync_ctx; const struct virtual_backend_uidmap *uidmap; struct mailbox_sync_rec sync_rec; struct mailbox_sync_status sync_status; unsigned int idx1, idx2; uint32_t vseq, vuid; sync_ctx = mailbox_sync_init(bbox->box, sync_flags); virtual_backend_box_sync_mail_set(bbox); while (mailbox_sync_next(sync_ctx, &sync_rec)) { switch (sync_rec.type) { case MAILBOX_SYNC_TYPE_EXPUNGE: if (ctx->expunge_removed) { /* no need to keep track of expunges */ break; } virtual_sync_expunge_add(ctx, bbox, &sync_rec); break; case MAILBOX_SYNC_TYPE_FLAGS: if (!virtual_sync_find_seqs(bbox, &sync_rec, &idx1, &idx2)) break; uidmap = array_idx(&bbox->uids, 0); for (; idx1 <= idx2; idx1++) { vuid = uidmap[idx1].virtual_uid; if (vuid == 0) { /* has not been even assigned yet */ continue; } if (!mail_index_lookup_seq(ctx->sync_view, vuid, &vseq)) { /* expunged by another session, but we haven't yet updated bbox->uids. */ continue; } virtual_sync_external_flags(ctx, bbox, vseq, uidmap[idx1].real_uid); } break; case MAILBOX_SYNC_TYPE_MODSEQ: break; } } if (mailbox_sync_deinit(&sync_ctx, &sync_status) < 0) { if (mailbox_get_last_mail_error(bbox->box) != MAIL_ERROR_NOTFOUND) return -1; /* mailbox was deleted */ virtual_sync_backend_box_deleted(ctx, bbox); return 0; } return 0; } static void virtual_sync_backend_ext_header(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox) { const unsigned int uidval_pos = offsetof(struct virtual_mail_index_mailbox_record, uid_validity); struct mailbox_status status; struct virtual_mail_index_mailbox_record mailbox; unsigned int mailbox_offset; uint64_t wanted_ondisk_highest_modseq; mailbox_get_open_status(bbox->box, STATUS_UIDVALIDITY | STATUS_HIGHESTMODSEQ, &status); wanted_ondisk_highest_modseq = array_count(&bbox->sync_pending_removes) > 0 ? 0 : status.highest_modseq; if (bbox->sync_uid_validity == status.uidvalidity && bbox->sync_next_uid == status.uidnext && bbox->sync_highest_modseq == status.highest_modseq && bbox->ondisk_highest_modseq == wanted_ondisk_highest_modseq) return; /* mailbox changed - update extension header */ bbox->sync_uid_validity = status.uidvalidity; bbox->sync_highest_modseq = status.highest_modseq; bbox->ondisk_highest_modseq = wanted_ondisk_highest_modseq; bbox->sync_next_uid = status.uidnext; if (ctx->mbox->ext_header_rewrite) { /* we'll rewrite the entire header later */ return; } i_zero(&mailbox); mailbox.uid_validity = bbox->sync_uid_validity; mailbox.highest_modseq = bbox->ondisk_highest_modseq; mailbox.next_uid = bbox->sync_next_uid; mailbox_offset = sizeof(struct virtual_mail_index_header) + bbox->sync_mailbox_idx * sizeof(mailbox); mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id, mailbox_offset + uidval_pos, CONST_PTR_OFFSET(&mailbox, uidval_pos), sizeof(mailbox) - uidval_pos); ctx->ext_header_changed = TRUE; } static void virtual_sync_backend_box_deleted(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox) { ARRAY_TYPE(seq_range) removed_uids; const struct virtual_backend_uidmap *uidmap; /* delay its full removal until the next time we open the virtual mailbox. for now just treat it as if it was empty. */ t_array_init(&removed_uids, 128); array_foreach(&bbox->uids, uidmap) seq_range_array_add(&removed_uids, uidmap->real_uid); virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids); bbox->deleted = TRUE; } static int virtual_try_open_and_sync_backend_box(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox, enum mailbox_sync_flags sync_flags) { int ret = 0; if (!bbox->box->opened) ret = virtual_backend_box_open(ctx->mbox, bbox); if (ret == 0) ret = mailbox_sync(bbox->box, sync_flags); if (ret < 0) { if (mailbox_get_last_mail_error(bbox->box) != MAIL_ERROR_NOTFOUND) return -1; /* mailbox was deleted */ virtual_sync_backend_box_deleted(ctx, bbox); return 0; } return 1; } static int virtual_sync_backend_box(struct virtual_sync_context *ctx, struct virtual_backend_box *bbox) { enum mailbox_sync_flags sync_flags; struct mailbox_status status; int ret; if (bbox->deleted) return 0; /* if we already did some changes to index, commit them before syncing starts. */ virtual_backend_box_sync_mail_unset(bbox); sync_flags = ctx->flags & (MAILBOX_SYNC_FLAG_FULL_READ | MAILBOX_SYNC_FLAG_FULL_WRITE | MAILBOX_SYNC_FLAG_FAST); if (bbox->search_result == NULL) { /* a) first sync in this process. b) we had auto-closed this backend mailbox. first try to quickly check if the mailbox has changed. if we can do that check from mailbox list index, we don't even need to open the mailbox. */ i_assert(array_count(&bbox->sync_pending_removes) == 0); if (bbox->box->opened || bbox->open_failed) { /* a) index already opened, refresh it b) delayed error handling for mailbox_open() that failed in virtual_notify_changes() */ if ((ret = virtual_try_open_and_sync_backend_box(ctx, bbox, sync_flags)) <= 0) return ret; bbox->open_failed = FALSE; } if (mailbox_get_status(bbox->box, STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ, &status) < 0) { if (mailbox_get_last_mail_error(bbox->box) != MAIL_ERROR_NOTFOUND) return -1; /* mailbox was deleted */ virtual_sync_backend_box_deleted(ctx, bbox); return 0; } if (status.uidvalidity == bbox->sync_uid_validity && status.uidnext == bbox->sync_next_uid && status.highest_modseq == bbox->sync_highest_modseq) { /* mailbox hasn't changed since we last opened it, skip it for now. we'll still need to create the bbox->uids mapping using the current index. */ if (array_count(&bbox->uids) == 0) virtual_sync_backend_handle_old_vmsgs(ctx, bbox, NULL); return 0; } if (!bbox->box->opened) { /* first time we're opening the index */ if ((ret = virtual_try_open_and_sync_backend_box(ctx, bbox, sync_flags)) <= 0) return ret; } virtual_backend_box_sync_mail_set(bbox); if (status.uidvalidity != bbox->sync_uid_validity) { /* UID validity changed since last sync (or this is the first sync), do a full search */ ret = virtual_sync_backend_box_init(bbox); } else { /* build the initial search using the saved modseq. */ ret = virtual_sync_backend_box_continue(ctx, bbox); } i_assert(bbox->search_result != NULL || ret < 0); } else { /* sync using the existing search result */ i_assert(bbox->box->opened); i_array_init(&ctx->sync_expunges, 32); ret = virtual_sync_backend_box_sync(ctx, bbox, sync_flags); if (ret == 0) T_BEGIN { virtual_sync_mailbox_box_update(ctx, bbox); } T_END; array_free(&ctx->sync_expunges); } virtual_sync_backend_ext_header(ctx, bbox); return ret; } static void virtual_sync_backend_map_uids(struct virtual_sync_context *ctx) { uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; struct virtual_sync_mail *vmails; struct virtual_backend_box *bbox; struct virtual_backend_uidmap *uidmap = NULL; struct virtual_add_record add_rec; const struct virtual_mail_index_record *vrec; const void *data; uint32_t i, vseq, vuid, messages; unsigned int j = 0, uidmap_count = 0; messages = mail_index_view_get_messages_count(ctx->sync_view); if (messages == 0) return; /* sort the messages in current view by their backend mailbox and real UID */ vmails = i_new(struct virtual_sync_mail, messages); for (vseq = 1; vseq <= messages; vseq++) { mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id, &data, NULL); vrec = data; vmails[vseq-1].vseq = vseq; vmails[vseq-1].vrec = *vrec; } qsort(vmails, messages, sizeof(*vmails), virtual_sync_mail_uid_cmp); /* create real mailbox uid -> virtual uid mapping and expunge messages no longer matching the search rule */ i_zero(&add_rec); bbox = NULL; for (i = 0; i < messages; i++) { vseq = vmails[i].vseq; vrec = &vmails[i].vrec; if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { /* add the rest of the newly seen messages */ for (; j < uidmap_count; j++) { add_rec.rec.real_uid = uidmap[j].real_uid; array_append(&ctx->all_adds, &add_rec, 1); } bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); if (bbox == NULL) { /* the entire mailbox is lost */ mail_index_expunge(ctx->trans, vseq); continue; } uidmap = array_get_modifiable(&bbox->uids, &uidmap_count); j = 0; add_rec.rec.mailbox_id = bbox->mailbox_id; bbox->sync_seen = TRUE; } mail_index_lookup_uid(ctx->sync_view, vseq, &vuid); /* if virtual record doesn't exist in uidmap, it's expunged */ for (; j < uidmap_count; j++) { if (uidmap[j].real_uid >= vrec->real_uid) break; /* newly seen message */ add_rec.rec.real_uid = uidmap[j].real_uid; array_append(&ctx->all_adds, &add_rec, 1); } if (j == uidmap_count || uidmap[j].real_uid != vrec->real_uid) mail_index_expunge(ctx->trans, vseq); else { /* exists - update uidmap and flags */ uidmap[j++].virtual_uid = vuid; if (bbox->search_result == NULL) { /* mailbox is completely unchanged since last sync - no need to sync flags */ } else { virtual_sync_external_flags(ctx, bbox, vseq, vrec->real_uid); } } } i_free(vmails); /* finish adding messages to the last mailbox */ for (; j < uidmap_count; j++) { add_rec.rec.real_uid = uidmap[j].real_uid; array_append(&ctx->all_adds, &add_rec, 1); } } static void virtual_sync_new_backend_boxes(struct virtual_sync_context *ctx) { struct virtual_backend_box *const *bboxes; struct virtual_add_record add_rec; struct virtual_backend_uidmap *uidmap; unsigned int i, j, count, uidmap_count; /* if there are any mailboxes we didn't yet sync, add new messages in them */ i_zero(&add_rec); bboxes = array_get(&ctx->mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->sync_seen) continue; add_rec.rec.mailbox_id = bboxes[i]->mailbox_id; uidmap = array_get_modifiable(&bboxes[i]->uids, &uidmap_count); for (j = 0; j < uidmap_count; j++) { add_rec.rec.real_uid = uidmap[j].real_uid; array_append(&ctx->all_adds, &add_rec, 1); } } } static int virtual_add_record_cmp(const struct virtual_add_record *add1, const struct virtual_add_record *add2) { if (add1->received_date < add2->received_date) return -1; if (add1->received_date > add2->received_date) return 1; /* if they're in same mailbox, we can order them correctly by the UID. if they're in different mailboxes, ordering by UID doesn't really help but it doesn't really harm either. */ if (add1->rec.real_uid < add2->rec.real_uid) return -1; if (add1->rec.real_uid > add2->rec.real_uid) return 1; /* two messages in different mailboxes have the same received date and UID. */ return 0; } static int virtual_sync_backend_sort_new(struct virtual_sync_context *ctx) { struct virtual_backend_box *bbox; struct virtual_add_record *adds; const struct virtual_mail_index_record *vrec; unsigned int i, count; /* get all messages' received dates */ adds = array_get_modifiable(&ctx->all_adds, &count); for (bbox = NULL, i = 0; i < count; i++) { vrec = &adds[i].rec; if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); if (!bbox->box->opened && virtual_backend_box_open(ctx->mbox, bbox) < 0) return -1; virtual_backend_box_sync_mail_set(bbox); } if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) { /* we may have reopened the mailbox, which could have caused the mail to be expunged already. */ adds[i].received_date = 0; } else if (mail_get_received_date(bbox->sync_mail, &adds[i].received_date) < 0) { if (!bbox->sync_mail->expunged) return -1; /* expunged already, just add it somewhere */ adds[i].received_date = 0; } } array_sort(&ctx->all_adds, virtual_add_record_cmp); return 0; } static int virtual_sync_backend_add_new(struct virtual_sync_context *ctx) { uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; struct virtual_add_record *adds; struct virtual_backend_box *bbox; struct virtual_backend_uidmap *uidmap; const struct mail_index_header *hdr; const struct virtual_mail_index_record *vrec; unsigned int i, count, idx; ARRAY_TYPE(seq_range) saved_uids; uint32_t vseq, first_uid; hdr = mail_index_get_header(ctx->sync_view); adds = array_get_modifiable(&ctx->all_adds, &count); if (count == 0) { ctx->mbox->sync_virtual_next_uid = hdr->next_uid; return 0; } if (adds[0].rec.mailbox_id == adds[count-1].rec.mailbox_id) { /* all messages are from a single mailbox. add them in the same order. */ } else { /* sort new messages by received date to get the add order */ if (virtual_sync_backend_sort_new(ctx) < 0) return -1; } for (bbox = NULL, i = 0; i < count; i++) { vrec = &adds[i].rec; if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); if (!bbox->box->opened && virtual_backend_box_open(ctx->mbox, bbox) < 0) return -1; virtual_backend_box_sync_mail_set(bbox); } mail_index_append(ctx->trans, 0, &vseq); mail_index_update_ext(ctx->trans, vseq, virtual_ext_id, vrec, NULL); virtual_sync_external_flags(ctx, bbox, vseq, vrec->real_uid); } /* assign UIDs to new messages */ first_uid = hdr->next_uid; t_array_init(&saved_uids, 1); mail_index_append_finish_uids(ctx->trans, first_uid, &saved_uids); i_assert(seq_range_count(&saved_uids) == count); /* update virtual UIDs in uidmap */ for (bbox = NULL, i = 0; i < count; i++) { vrec = &adds[i].rec; if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); } if (!array_bsearch_insert_pos(&bbox->uids, &vrec->real_uid, virtual_backend_uidmap_bsearch_cmp, &idx)) i_unreached(); uidmap = array_idx_modifiable(&bbox->uids, idx); i_assert(uidmap->virtual_uid == 0); uidmap->virtual_uid = first_uid + i; } ctx->mbox->sync_virtual_next_uid = first_uid + i; return 0; } static int virtual_sync_apply_existing_appends(struct virtual_sync_context *ctx) { uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; struct virtual_backend_box *bbox = NULL; const struct mail_index_header *hdr; const struct virtual_mail_index_record *vrec; struct virtual_backend_uidmap uidmap; const void *data; uint32_t seq, seq2; if (!ctx->mbox->uids_mapped) return 0; hdr = mail_index_get_header(ctx->sync_view); if (ctx->mbox->sync_virtual_next_uid >= hdr->next_uid) return 0; /* another process added messages to virtual index. get backend boxes' uid lists up-to-date by adding the new messages there. */ if (!mail_index_lookup_seq_range(ctx->sync_view, ctx->mbox->sync_virtual_next_uid, (uint32_t)-1, &seq, &seq2)) return 0; i_zero(&uidmap); for (; seq <= seq2; seq++) { mail_index_lookup_ext(ctx->sync_view, seq, virtual_ext_id, &data, NULL); vrec = data; uidmap.real_uid = vrec->real_uid; mail_index_lookup_uid(ctx->sync_view, seq, &uidmap.virtual_uid); if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); if (bbox == NULL) { mail_index_expunge(ctx->trans, seq); continue; } } array_append(&bbox->uids, &uidmap, 1); bbox->uids_nonsorted = TRUE; } virtual_sync_backend_boxes_sort_uids(ctx->mbox); return 0; } static void virtual_sync_apply_existing_expunges(struct virtual_mailbox *mbox, struct mailbox_sync_context *sync_ctx) { struct index_mailbox_sync_context *isync_ctx = (struct index_mailbox_sync_context *)sync_ctx; struct virtual_backend_box *bbox = NULL; struct seq_range_iter iter; const struct virtual_mail_index_record *vrec; const void *data; unsigned int n = 0; uint32_t seq; if (isync_ctx->expunges == NULL) return; seq_range_array_iter_init(&iter, isync_ctx->expunges); while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_ext(mbox->box.view, seq, mbox->virtual_ext_id, &data, NULL); vrec = data; if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { bbox = virtual_backend_box_lookup(mbox, vrec->mailbox_id); if (!array_is_created(&bbox->sync_outside_expunges)) i_array_init(&bbox->sync_outside_expunges, 32); } seq_range_array_add(&bbox->sync_outside_expunges, vrec->real_uid); } } static int virtual_sync_mail_mailbox_cmp(const struct virtual_sync_mail *m1, const struct virtual_sync_mail *m2) { if (m1->vrec.mailbox_id < m2->vrec.mailbox_id) return -1; if (m1->vrec.mailbox_id > m2->vrec.mailbox_id) return 1; return 0; } static void virtual_sync_bboxes_get_mails(struct virtual_sync_context *ctx) { uint32_t messages, vseq; const void *mail_data; const struct virtual_mail_index_record *vrec; struct virtual_sync_mail *sync_mail; messages = mail_index_view_get_messages_count(ctx->sync_view); i_array_init(&ctx->all_mails, messages); for (vseq = 1; vseq <= messages; vseq++) { mail_index_lookup_ext(ctx->sync_view, vseq, ctx->mbox->virtual_ext_id, &mail_data, NULL); vrec = mail_data; sync_mail = array_append_space(&ctx->all_mails); sync_mail->vseq = vseq; sync_mail->vrec = *vrec; } array_sort(&ctx->all_mails, virtual_sync_mail_mailbox_cmp); } static int virtual_sync_backend_boxes(struct virtual_sync_context *ctx) { struct virtual_backend_box *const *bboxes; unsigned int i, count; int ret; if (virtual_sync_apply_existing_appends(ctx) < 0) return -1; i_array_init(&ctx->all_adds, 128); bboxes = array_get(&ctx->mbox->backend_boxes, &count); /* we have different optimizations depending on whether the virtual mailbox consists of multiple backend boxes or just one */ if (count > 1) virtual_sync_bboxes_get_mails(ctx); for (i = 0; i < count; i++) { if (virtual_sync_backend_box(ctx, bboxes[i]) < 0) { /* backend failed, copy the error */ virtual_box_copy_error(&ctx->mbox->box, bboxes[i]->box); return -1; } } if (!ctx->mbox->uids_mapped) { /* initial sync: assign virtual UIDs to existing messages and sync all flags */ ctx->mbox->uids_mapped = TRUE; virtual_sync_backend_map_uids(ctx); virtual_sync_new_backend_boxes(ctx); } ret = virtual_sync_backend_add_new(ctx); #ifdef DEBUG for (i = 0; i < count; i++) { const struct virtual_backend_uidmap *uidmap; array_foreach(&bboxes[i]->uids, uidmap) i_assert(uidmap->virtual_uid > 0); } #endif array_free(&ctx->all_adds); if (array_is_created(&ctx->all_mails)) array_free(&ctx->all_mails); return ret; } static void virtual_sync_backend_boxes_finish(struct virtual_sync_context *ctx) { struct virtual_backend_box *const *bboxes; unsigned int i, count; bboxes = array_get(&ctx->mbox->backend_boxes, &count); for (i = 0; i < count; i++) virtual_backend_box_sync_mail_unset(bboxes[i]); } static int virtual_sync_finish(struct virtual_sync_context *ctx, bool success) { int ret = success ? 0 : -1; virtual_sync_backend_boxes_finish(ctx); if (success) { if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { mailbox_set_index_error(&ctx->mbox->box); ret = -1; } ctx->mbox->ext_header_rewrite = FALSE; } else { if (ctx->index_broken) { /* make sure we don't complain about the same errors over and over again. */ if (mail_index_unlink(ctx->index) < 0) { i_error("virtual index %s: Failed to unlink() " "broken indexes: %m", mailbox_get_path(&ctx->mbox->box)); } } mail_index_sync_rollback(&ctx->index_sync_ctx); } i_free(ctx); return ret; } static int virtual_sync(struct virtual_mailbox *mbox, enum mailbox_sync_flags flags) { struct virtual_sync_context *ctx; enum mail_index_sync_flags index_sync_flags; bool broken; int ret; ctx = i_new(struct virtual_sync_context, 1); ctx->mbox = mbox; ctx->flags = flags; ctx->index = mbox->box.index; /* Removed messages are expunged when a) EXPUNGE is used b) Mailbox is being opened (FIX_INCONSISTENT is set) */ ctx->expunge_removed = (ctx->flags & (MAILBOX_SYNC_FLAG_EXPUNGE | MAILBOX_SYNC_FLAG_FIX_INCONSISTENT)) != 0; index_sync_flags = MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY | MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES; if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) index_sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT; ret = mail_index_sync_begin(ctx->index, &ctx->index_sync_ctx, &ctx->sync_view, &ctx->trans, index_sync_flags); if (ret <= 0) { if (ret < 0) mailbox_set_index_error(&mbox->box); i_free(ctx); return ret; } ret = virtual_mailbox_ext_header_read(mbox, ctx->sync_view, &broken); if (ret < 0) return virtual_sync_finish(ctx, FALSE); if (broken) ctx->index_broken = TRUE; /* apply changes from virtual index to backend mailboxes */ if (virtual_sync_index_changes(ctx) < 0) return virtual_sync_finish(ctx, FALSE); /* update list of UIDs in backend mailboxes */ if (virtual_sync_backend_boxes(ctx) < 0) return virtual_sync_finish(ctx, FALSE); virtual_sync_index_finish(ctx); return virtual_sync_finish(ctx, TRUE); } struct mailbox_sync_context * virtual_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct mailbox_sync_context *sync_ctx; int ret = 0; if (!box->opened) { if (mailbox_open(box) < 0) ret = -1; } if (index_mailbox_want_full_sync(&mbox->box, flags) && ret == 0) ret = virtual_sync(mbox, flags); sync_ctx = index_mailbox_sync_init(box, flags, ret < 0); virtual_sync_apply_existing_expunges(mbox, sync_ctx); return sync_ctx; } dovecot-2.2.33.2/src/plugins/virtual/virtual-mail.c0000644000175000017500000003552613165463624017062 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "index-mail.h" #include "virtual-storage.h" #include "virtual-transaction.h" struct virtual_mail { struct index_mail imail; enum mail_fetch_field wanted_fields; struct mailbox_header_lookup_ctx *wanted_headers; /* currently active mail */ struct mail *cur_backend_mail; struct virtual_mail_index_record cur_vrec; /* all allocated mails */ ARRAY(struct mail *) backend_mails; /* mail is lost if backend_mail doesn't point to correct mail */ unsigned int cur_lost:1; }; struct mail * virtual_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)t->box; struct virtual_mail *vmail; pool_t pool; pool = pool_alloconly_create("vmail", 1024); vmail = p_new(pool, struct virtual_mail, 1); vmail->imail.mail.pool = pool; vmail->imail.mail.data_pool = pool_alloconly_create("virtual index_mail", 512); vmail->imail.mail.v = virtual_mail_vfuncs; vmail->imail.mail.mail.box = t->box; vmail->imail.mail.mail.transaction = t; array_create(&vmail->imail.mail.module_contexts, pool, sizeof(void *), 5); vmail->imail.ibox = INDEX_STORAGE_CONTEXT(t->box); vmail->wanted_fields = wanted_fields; if (wanted_headers != NULL) { vmail->wanted_headers = wanted_headers; mailbox_header_lookup_ref(wanted_headers); } i_array_init(&vmail->backend_mails, array_count(&mbox->backend_boxes)); return &vmail->imail.mail.mail; } static void virtual_mail_close(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail **mails; unsigned int i, count; mails = array_get_modifiable(&vmail->backend_mails, &count); for (i = 0; i < count; i++) { struct mail_private *p = (struct mail_private *)mails[i]; p->v.close(mails[i]); } index_mail_close(mail); } static void virtual_mail_free(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail **mails; unsigned int i, count; mails = array_get_modifiable(&vmail->backend_mails, &count); for (i = 0; i < count; i++) mail_free(&mails[i]); array_free(&vmail->backend_mails); if (vmail->wanted_headers != NULL) mailbox_header_lookup_unref(&vmail->wanted_headers); pool_unref(&vmail->imail.mail.data_pool); pool_unref(&vmail->imail.mail.pool); } static struct mail * backend_mail_find(struct virtual_mail *vmail, struct mailbox *box) { struct mail *const *mails; unsigned int i, count; mails = array_get(&vmail->backend_mails, &count); for (i = 0; i < count; i++) { if (mails[i]->box == box) return mails[i]; } return NULL; } static int backend_mail_get(struct virtual_mail *vmail, struct mail **backend_mail_r) { struct mail *mail = &vmail->imail.mail.mail; struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; struct virtual_backend_box *bbox; *backend_mail_r = NULL; if (vmail->cur_backend_mail != NULL) { if (vmail->cur_lost) { mail_set_expunged(&vmail->imail.mail.mail); return -1; } *backend_mail_r = vmail->cur_backend_mail; return 0; } bbox = virtual_backend_box_lookup(mbox, vmail->cur_vrec.mailbox_id); i_assert(bbox != NULL); vmail->cur_backend_mail = backend_mail_find(vmail, bbox->box); if (vmail->cur_backend_mail == NULL) { if (!bbox->box->opened && virtual_backend_box_open(mbox, bbox) < 0) { virtual_box_copy_error(mail->box, bbox->box); return -1; } (void)virtual_mail_set_backend_mail(mail, bbox); i_assert(vmail->cur_backend_mail != NULL); } virtual_backend_box_accessed(mbox, bbox); vmail->cur_lost = !mail_set_uid(vmail->cur_backend_mail, vmail->cur_vrec.real_uid); mail->expunged = vmail->cur_lost || vmail->cur_backend_mail->expunged; if (vmail->cur_lost) { mail_set_expunged(&vmail->imail.mail.mail); return -1; } *backend_mail_r = vmail->cur_backend_mail; return 0; } struct mail * virtual_mail_set_backend_mail(struct mail *mail, struct virtual_backend_box *bbox) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail_private *backend_pmail; struct mailbox_transaction_context *backend_trans; struct mailbox_header_lookup_ctx *backend_headers; i_assert(bbox->box->opened); backend_trans = virtual_transaction_get(mail->transaction, bbox->box); backend_headers = vmail->wanted_headers == NULL ? NULL : mailbox_header_lookup_init(bbox->box, vmail->wanted_headers->name); vmail->cur_backend_mail = mail_alloc(backend_trans, vmail->wanted_fields, backend_headers); if (backend_headers != NULL) mailbox_header_lookup_unref(&backend_headers); backend_pmail = (struct mail_private *)vmail->cur_backend_mail; backend_pmail->vmail = mail; array_append(&vmail->backend_mails, &vmail->cur_backend_mail, 1); return vmail->cur_backend_mail; } void virtual_mail_set_unattached_backend_mail(struct mail *mail, struct mail *backend_mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail_private *backend_pmail; vmail->cur_backend_mail = backend_mail; backend_pmail = (struct mail_private *)backend_mail; backend_pmail->vmail = mail; } static void virtual_mail_set_seq(struct mail *mail, uint32_t seq, bool saving) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; const void *data; i_assert(!saving); mail_index_lookup_ext(mail->transaction->view, seq, mbox->virtual_ext_id, &data, NULL); memcpy(&vmail->cur_vrec, data, sizeof(vmail->cur_vrec)); i_zero(&vmail->imail.data); p_clear(vmail->imail.mail.data_pool); vmail->imail.data.seq = seq; mail->seq = seq; mail_index_lookup_uid(mail->transaction->view, seq, &mail->uid); vmail->cur_backend_mail = NULL; } static bool virtual_mail_set_uid(struct mail *mail, uint32_t uid) { uint32_t seq; if (!mail_index_lookup_seq(mail->transaction->view, uid, &seq)) return FALSE; virtual_mail_set_seq(mail, seq, FALSE); return TRUE; } static void virtual_mail_set_uid_cache_updates(struct mail *mail, bool set) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; if (backend_mail_get(vmail, &backend_mail) < 0) return; p = (struct mail_private *)backend_mail; p->v.set_uid_cache_updates(backend_mail, set); } static bool virtual_mail_prefetch(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; if (backend_mail_get(vmail, &backend_mail) < 0) return TRUE; p = (struct mail_private *)backend_mail; return p->v.prefetch(backend_mail); } static void virtual_mail_precache(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; if (backend_mail_get(vmail, &backend_mail) < 0) return; p = (struct mail_private *)backend_mail; p->v.precache(backend_mail); } static void virtual_mail_add_temp_wanted_fields(struct mail *mail, enum mail_fetch_field fields, struct mailbox_header_lookup_ctx *headers) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; if (backend_mail_get(vmail, &backend_mail) < 0) return; p = (struct mail_private *)backend_mail; p->v.add_temp_wanted_fields(backend_mail, fields, headers); } static int virtual_mail_get_parts(struct mail *mail, struct message_part **parts_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_parts(backend_mail, parts_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; int tz; if (timezone_r == NULL) timezone_r = &tz; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_date(backend_mail, date_r, timezone_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_received_date(struct mail *mail, time_t *date_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_received_date(backend_mail, date_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_save_date(struct mail *mail, time_t *date_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_save_date(backend_mail, date_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_virtual_mail_size(struct mail *mail, uoff_t *size_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_virtual_size(backend_mail, size_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_physical_size(struct mail *mail, uoff_t *size_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_physical_size(backend_mail, size_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_first_header(struct mail *mail, const char *field, bool decode_to_utf8, const char **value_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; int ret; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; p = (struct mail_private *)backend_mail; ret = p->v.get_first_header(backend_mail, field, decode_to_utf8, value_r); if (ret < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return ret; } static int virtual_mail_get_headers(struct mail *mail, const char *field, bool decode_to_utf8, const char *const **value_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mail_private *p; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; p = (struct mail_private *)backend_mail; if (p->v.get_headers(backend_mail, field, decode_to_utf8, value_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_header_stream(struct mail *mail, struct mailbox_header_lookup_ctx *headers, struct istream **stream_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; struct mailbox_header_lookup_ctx *backend_headers; int ret; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; backend_headers = mailbox_header_lookup_init(backend_mail->box, headers->name); ret = mail_get_header_stream(backend_mail, backend_headers, stream_r); mailbox_header_lookup_unref(&backend_headers); if (ret < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_stream(struct mail *mail, bool get_body, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail_private *vp = (struct mail_private *)mail; struct mail *backend_mail; const char *reason = t_strdup_printf("virtual mailbox %s: Opened mail UID=%u: %s", mailbox_get_vname(mail->box), mail->uid, vp->get_stream_reason); int ret; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (get_body) { ret = mail_get_stream_because(backend_mail, hdr_size, body_size, reason, stream_r); } else { ret = mail_get_hdr_stream_because(backend_mail, hdr_size, reason, stream_r); } if (ret < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static int virtual_mail_get_special(struct mail *mail, enum mail_fetch_field field, const char **value_r) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return -1; if (mail_get_special(backend_mail, field, value_r) < 0) { virtual_box_copy_error(mail->box, backend_mail->box); return -1; } return 0; } static struct mail *virtual_mail_get_real_mail(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail, *real_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return NULL; if (mail_get_backend_mail(backend_mail, &real_mail) < 0) return NULL; return real_mail; } static void virtual_mail_update_pop3_uidl(struct mail *mail, const char *uidl) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return; mail_update_pop3_uidl(backend_mail, uidl); } static void virtual_mail_expunge(struct mail *mail) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return; mail_expunge(backend_mail); } static void virtual_mail_set_cache_corrupted_reason(struct mail *mail, enum mail_fetch_field field, const char *reason) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct mail *backend_mail; if (backend_mail_get(vmail, &backend_mail) < 0) return; mail_set_cache_corrupted_reason(backend_mail, field, reason); } static void virtual_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field) { virtual_mail_set_cache_corrupted_reason(mail, field, ""); } struct mail_vfuncs virtual_mail_vfuncs = { virtual_mail_close, virtual_mail_free, virtual_mail_set_seq, virtual_mail_set_uid, virtual_mail_set_uid_cache_updates, virtual_mail_prefetch, virtual_mail_precache, virtual_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, virtual_mail_get_parts, virtual_mail_get_date, virtual_mail_get_received_date, virtual_mail_get_save_date, virtual_mail_get_virtual_mail_size, virtual_mail_get_physical_size, virtual_mail_get_first_header, virtual_mail_get_headers, virtual_mail_get_header_stream, virtual_mail_get_stream, index_mail_get_binary_stream, virtual_mail_get_special, virtual_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, virtual_mail_update_pop3_uidl, virtual_mail_expunge, virtual_mail_set_cache_corrupted, NULL, virtual_mail_set_cache_corrupted_reason }; dovecot-2.2.33.2/src/plugins/virtual/virtual-config.c0000644000175000017500000003717613165463624017410 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "crc32.h" #include "istream.h" #include "str.h" #include "unichar.h" #include "wildcard-match.h" #include "imap-parser.h" #include "imap-match.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "mail-search-parser.h" #include "mailbox-attribute.h" #include "mailbox-list-iter.h" #include "imap-metadata.h" #include "virtual-storage.h" #include "virtual-plugin.h" #include #include struct virtual_parse_context { struct virtual_mailbox *mbox; struct istream *input; pool_t pool; string_t *rule; unsigned int rule_idx; char sep; bool have_wildcards; bool have_mailbox_defines; }; static struct mail_search_args * virtual_search_args_parse(const string_t *rule, const char **error_r) { struct istream *input; struct imap_parser *imap_parser; const struct imap_arg *args; struct mail_search_parser *parser; struct mail_search_args *sargs; const char *charset = "UTF-8"; bool fatal; int ret; if (str_len(rule) == 0) { sargs = mail_search_build_init(); mail_search_build_add_all(sargs); return sargs; } input = i_stream_create_from_data(str_data(rule), str_len(rule)); (void)i_stream_read(input); imap_parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(imap_parser, 0, 0, &args); if (ret < 0) { sargs = NULL; *error_r = t_strdup(imap_parser_get_error(imap_parser, &fatal)); } else { parser = mail_search_parser_init_imap(args); if (mail_search_build(mail_search_register_get_imap(), parser, &charset, &sargs, error_r) < 0) sargs = NULL; mail_search_parser_deinit(&parser); } imap_parser_unref(&imap_parser); i_stream_destroy(&input); return sargs; } static int virtual_config_add_rule(struct virtual_parse_context *ctx, const char **error_r) { struct virtual_backend_box *const *bboxes; struct mail_search_args *search_args; unsigned int i, count; *error_r = NULL; if (ctx->rule_idx == array_count(&ctx->mbox->backend_boxes)) { i_assert(str_len(ctx->rule) == 0); return 0; } ctx->mbox->search_args_crc32 = crc32_str_more(ctx->mbox->search_args_crc32, str_c(ctx->rule)); search_args = virtual_search_args_parse(ctx->rule, error_r); str_truncate(ctx->rule, 0); if (search_args == NULL) { i_assert(*error_r != NULL); *error_r = t_strconcat("Previous search rule is invalid: ", *error_r, NULL); return -1; } /* update at all the mailboxes that were introduced since the previous rule. */ bboxes = array_get(&ctx->mbox->backend_boxes, &count); i_assert(ctx->rule_idx < count); for (i = ctx->rule_idx; i < count; i++) { i_assert(bboxes[i]->search_args == NULL); mail_search_args_ref(search_args); bboxes[i]->search_args = search_args; } mail_search_args_unref(&search_args); ctx->rule_idx = array_count(&ctx->mbox->backend_boxes); return 0; } static int virtual_config_parse_line(struct virtual_parse_context *ctx, const char *line, const char **error_r) { struct mail_user *user = ctx->mbox->storage->storage.user; struct virtual_backend_box *bbox; const char *p; bool no_wildcards = FALSE; if (*line == ' ' || *line == '\t') { /* continues the previous search rule */ if (ctx->rule_idx == array_count(&ctx->mbox->backend_boxes)) { *error_r = "Search rule without a mailbox"; return -1; } while (*line == ' ' || *line == '\t') line++; str_append_c(ctx->rule, ' '); str_append(ctx->rule, line); return 0; } /* if there is no rule yet, it means we want the previous mailboxes to use the rule that comes later */ if (str_len(ctx->rule) > 0) { if (virtual_config_add_rule(ctx, error_r) < 0) return -1; } if (!uni_utf8_str_is_valid(line)) { *error_r = t_strdup_printf("Mailbox name not UTF-8: %s", line); return -1; } /* new mailbox. the search args are added to it later. */ bbox = p_new(ctx->pool, struct virtual_backend_box, 1); bbox->virtual_mbox = ctx->mbox; if (strcasecmp(line, "INBOX") == 0) line = "INBOX"; bbox->name = p_strdup(ctx->pool, line); switch (bbox->name[0]) { case '+': bbox->name++; bbox->clear_recent = TRUE; break; case '-': bbox->name++; bbox->negative_match = TRUE; break; case '!': /* save messages here */ if (ctx->mbox->save_bbox != NULL) { *error_r = "Multiple save mailboxes defined"; return -1; } bbox->name++; ctx->mbox->save_bbox = bbox; no_wildcards = TRUE; break; } if (bbox->name[0] == '/') { /* [+-!]/metadata entry:value */ if ((p = strchr(bbox->name, ':')) == NULL) { *error_r = "':' separator missing between metadata entry name and value"; return -1; } bbox->metadata_entry = p_strdup_until(ctx->pool, bbox->name, p++); bbox->metadata_value = p; if (!imap_metadata_verify_entry_name(bbox->metadata_entry, error_r)) return -1; no_wildcards = TRUE; } if (!no_wildcards && (strchr(bbox->name, '*') != NULL || strchr(bbox->name, '%') != NULL)) { bbox->glob = imap_match_init(ctx->pool, bbox->name, TRUE, ctx->sep); ctx->have_wildcards = TRUE; } if (bbox->metadata_entry == NULL) { /* now that the prefix characters have been processed, find the namespace */ bbox->ns = strcasecmp(bbox->name, "INBOX") == 0 ? mail_namespace_find_inbox(user->namespaces) : mail_namespace_find(user->namespaces, bbox->name); if (bbox->ns == NULL) { *error_r = t_strdup_printf("Namespace not found for %s", bbox->name); return -1; } if (strcmp(bbox->name, ctx->mbox->box.vname) == 0) { *error_r = "Virtual mailbox can't point to itself"; return -1; } ctx->have_mailbox_defines = TRUE; } array_append(&ctx->mbox->backend_boxes, &bbox, 1); return 0; } static void virtual_mailbox_get_list_patterns(struct virtual_parse_context *ctx) { struct virtual_mailbox *mbox = ctx->mbox; ARRAY_TYPE(mailbox_virtual_patterns) *dest; struct mailbox_virtual_pattern pattern; struct virtual_backend_box *const *bboxes; unsigned int i, count; i_zero(&pattern); bboxes = array_get_modifiable(&mbox->backend_boxes, &count); p_array_init(&mbox->list_include_patterns, ctx->pool, count); p_array_init(&mbox->list_exclude_patterns, ctx->pool, count); for (i = 0; i < count; i++) { if (bboxes[i]->metadata_entry == NULL) continue; pattern.ns = bboxes[i]->ns; pattern.pattern = bboxes[i]->name; if (bboxes[i]->negative_match) dest = &mbox->list_include_patterns; else { dest = &mbox->list_exclude_patterns; pattern.pattern++; } array_append(dest, &pattern, 1); } } static void separate_wildcard_mailboxes(struct virtual_mailbox *mbox, ARRAY_TYPE(virtual_backend_box) *wildcard_boxes, ARRAY_TYPE(virtual_backend_box) *neg_boxes, ARRAY_TYPE(virtual_backend_box) *metadata_boxes) { struct virtual_backend_box *const *bboxes; ARRAY_TYPE(virtual_backend_box) *dest; unsigned int i, count; bboxes = array_get_modifiable(&mbox->backend_boxes, &count); t_array_init(wildcard_boxes, I_MIN(16, count)); t_array_init(neg_boxes, 4); t_array_init(metadata_boxes, 4); for (i = 0; i < count;) { if (bboxes[i]->metadata_entry != NULL) dest = metadata_boxes; else if (bboxes[i]->negative_match) dest = neg_boxes; else if (bboxes[i]->glob != NULL) dest = wildcard_boxes; else { dest = NULL; i++; } if (dest != NULL) { array_append(dest, &bboxes[i], 1); array_delete(&mbox->backend_boxes, i, 1); bboxes = array_get_modifiable(&mbox->backend_boxes, &count); } } } static void virtual_config_copy_expanded(struct virtual_parse_context *ctx, struct virtual_backend_box *wbox, const char *name) { struct virtual_backend_box *bbox; bbox = p_new(ctx->pool, struct virtual_backend_box, 1); *bbox = *wbox; bbox->name = p_strdup(ctx->pool, name); bbox->glob = NULL; bbox->wildcard = TRUE; mail_search_args_ref(bbox->search_args); array_append(&ctx->mbox->backend_boxes, &bbox, 1); } static bool virtual_ns_match(struct mail_namespace *config_ns, struct mail_namespace *iter_ns) { /* we match only one namespace for each pattern, except with shared namespaces match also autocreated children */ if (config_ns == iter_ns) return TRUE; if (config_ns->type == iter_ns->type && (config_ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0 && (iter_ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0) return TRUE; if ((iter_ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && (config_ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0 && config_ns->prefix_len == 0) { /* prefix="" namespace was autocreated, so e.g. "*" would match only that empty namespace. but we want "*" to also match the inbox=yes namespace, so check it here separately. */ return TRUE; } return FALSE; } static bool virtual_config_match(const struct mailbox_info *info, ARRAY_TYPE(virtual_backend_box) *boxes_arr, unsigned int *idx_r) { struct virtual_backend_box *const *boxes; unsigned int i, count; boxes = array_get_modifiable(boxes_arr, &count); for (i = 0; i < count; i++) { if (boxes[i]->glob != NULL) { if (virtual_ns_match(boxes[i]->ns, info->ns) && imap_match(boxes[i]->glob, info->vname) == IMAP_MATCH_YES) { *idx_r = i; return TRUE; } } else { if (strcmp(boxes[i]->name, info->vname) == 0) { *idx_r = i; return TRUE; } } } return FALSE; } static int virtual_config_box_metadata_match(struct mailbox *box, struct virtual_backend_box *bbox, const char **error_r) { struct imap_metadata_transaction *imtrans; struct mail_attribute_value value; int ret; imtrans = imap_metadata_transaction_begin(box); ret = imap_metadata_get(imtrans, bbox->metadata_entry, &value); if (ret < 0) *error_r = t_strdup(imap_metadata_transaction_get_last_error(imtrans, NULL)); if (ret > 0) ret = wildcard_match(value.value, bbox->metadata_value) ? 1 : 0; if (ret >= 0 && bbox->negative_match) ret = ret > 0 ? 0 : 1; (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); return ret; } static int virtual_config_metadata_match(const struct mailbox_info *info, ARRAY_TYPE(virtual_backend_box) *boxes_arr, const char **error_r) { struct virtual_backend_box *const *boxes; struct mailbox *box; unsigned int i, count; int ret = 1; boxes = array_get_modifiable(boxes_arr, &count); if (count == 0) return 1; box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY); mailbox_set_reason(box, "virtual mailbox metadata match"); for (i = 0; i < count; i++) { /* break on error or match */ if ((ret = virtual_config_box_metadata_match(box, boxes[i], error_r)) < 0 || ret > 0) break; } mailbox_free(&box); return ret; } static int virtual_config_expand_wildcards(struct virtual_parse_context *ctx, const char **error_r) { const enum mail_namespace_type iter_ns_types = MAIL_NAMESPACE_TYPE_MASK_ALL; const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct mail_user *user = ctx->mbox->storage->storage.user; ARRAY_TYPE(virtual_backend_box) wildcard_boxes, neg_boxes, metadata_boxes; struct mailbox_list_iterate_context *iter; struct virtual_backend_box *const *wboxes; const char **patterns; const struct mailbox_info *info; unsigned int i, j, count; int ret = 0; separate_wildcard_mailboxes(ctx->mbox, &wildcard_boxes, &neg_boxes, &metadata_boxes); /* get patterns we want to list */ wboxes = array_get_modifiable(&wildcard_boxes, &count); if (count == 0) { /* only negative wildcards - doesn't really make sense. just ignore. */ return 0; } patterns = t_new(const char *, count + 1); for (i = 0; i < count; i++) patterns[i] = wboxes[i]->name; /* match listed mailboxes to wildcards */ iter = mailbox_list_iter_init_namespaces(user->namespaces, patterns, iter_ns_types, iter_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { /* skip non-selectable mailboxes (especially mbox directories) */ if ((info->flags & MAILBOX_NOSELECT) != 0) continue; if (strcmp(info->vname, ctx->mbox->box.vname) == 0) { /* don't allow virtual folder to point to itself */ continue; } if (virtual_config_match(info, &wildcard_boxes, &i) && !virtual_config_match(info, &neg_boxes, &j) && virtual_backend_box_lookup_name(ctx->mbox, info->vname) == NULL) { ret = virtual_config_metadata_match(info, &metadata_boxes, error_r); if (ret < 0) break; if (ret > 0) { virtual_config_copy_expanded(ctx, wboxes[i], info->vname); } } } for (i = 0; i < count; i++) mail_search_args_unref(&wboxes[i]->search_args); if (mailbox_list_iter_deinit(&iter) < 0) { *error_r = mailbox_list_get_last_internal_error(user->namespaces->list, NULL); return -1; } return ret < 0 ? -1 : 0; } static void virtual_config_search_args_dup(struct virtual_mailbox *mbox) { struct virtual_backend_box *const *bboxes; struct mail_search_args *old_args; unsigned int i, count; bboxes = array_get_modifiable(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { old_args = bboxes[i]->search_args; bboxes[i]->search_args = mail_search_args_dup(old_args); mail_search_args_unref(&old_args); } } int virtual_config_read(struct virtual_mailbox *mbox) { struct mail_storage *storage = mbox->box.storage; struct virtual_parse_context ctx; const char *box_path, *path, *line, *error; unsigned int linenum = 0; int fd, ret = 0; i_array_init(&mbox->backend_boxes, 8); mbox->search_args_crc32 = (uint32_t)-1; box_path = mailbox_get_path(&mbox->box); path = t_strconcat(box_path, "/"VIRTUAL_CONFIG_FNAME, NULL); fd = open(path, O_RDONLY); if (fd == -1) { if (errno == EACCES) { mail_storage_set_critical(storage, "%s", mail_error_eacces_msg("open", path)); } else if (errno != ENOENT) { mail_storage_set_critical(storage, "open(%s) failed: %m", path); } else if (errno == ENOENT) { mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(mbox->box.vname)); } else { mail_storage_set_critical(storage, "stat(%s) failed: %m", box_path); } return -1; } i_zero(&ctx); ctx.sep = mail_namespaces_get_root_sep(storage->user->namespaces); ctx.mbox = mbox; ctx.pool = mbox->box.pool; ctx.rule = t_str_new(256); ctx.input = i_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(ctx.input, TRUE); while ((line = i_stream_read_next_line(ctx.input)) != NULL) { linenum++; if (*line == '#') continue; if (*line == '\0') ret = virtual_config_add_rule(&ctx, &error); else ret = virtual_config_parse_line(&ctx, line, &error); if (ret < 0) { mail_storage_set_critical(storage, "%s: Error at line %u: %s", path, linenum, error); break; } } if (ret == 0) { ret = virtual_config_add_rule(&ctx, &error); if (ret < 0) { mail_storage_set_critical(storage, "%s: Error at line %u: %s", path, linenum, error); } } virtual_mailbox_get_list_patterns(&ctx); if (ret == 0 && ctx.have_wildcards) { ret = virtual_config_expand_wildcards(&ctx, &error); if (ret < 0) mail_storage_set_critical(storage, "%s: %s", path, error); } if (ret == 0 && !ctx.have_mailbox_defines) { mail_storage_set_critical(storage, "%s: No mailboxes defined", path); ret = -1; } if (ret == 0) virtual_config_search_args_dup(mbox); i_stream_unref(&ctx.input); i_close_fd(&fd); return ret; } void virtual_config_free(struct virtual_mailbox *mbox) { struct virtual_backend_box *const *bboxes; unsigned int i, count; if (!array_is_created(&mbox->backend_boxes)) { /* mailbox wasn't opened */ return; } bboxes = array_get_modifiable(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->search_args != NULL) mail_search_args_unref(&bboxes[i]->search_args); } array_free(&mbox->backend_boxes); } dovecot-2.2.33.2/src/plugins/virtual/virtual-transaction.h0000644000175000017500000000131513165463624020457 00000000000000#ifndef VIRTUAL_TRANSACTION_H #define VIRTUAL_TRANSACTION_H #include "index-storage.h" struct virtual_transaction_context { struct mailbox_transaction_context t; ARRAY(struct mailbox_transaction_context *) backend_transactions; }; struct mailbox_transaction_context * virtual_transaction_get(struct mailbox_transaction_context *trans, struct mailbox *backend_box); struct mailbox_transaction_context * virtual_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags); int virtual_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r); void virtual_transaction_rollback(struct mailbox_transaction_context *t); #endif dovecot-2.2.33.2/src/plugins/virtual/virtual-search.c0000644000175000017500000001315413123174404017363 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-search.h" #include "index-search-private.h" #include "virtual-storage.h" enum virtual_search_state { VIRTUAL_SEARCH_STATE_BUILD, VIRTUAL_SEARCH_STATE_RETURN, VIRTUAL_SEARCH_STATE_SORT, VIRTUAL_SEARCH_STATE_SORT_DONE }; struct virtual_search_record { uint32_t mailbox_id; uint32_t real_uid; uint32_t virtual_seq; }; struct virtual_search_context { union mail_search_module_context module_ctx; ARRAY_TYPE(seq_range) result; struct seq_range_iter result_iter; ARRAY(struct virtual_search_record) records; enum virtual_search_state search_state; unsigned int next_result_n; unsigned int next_record_idx; }; static int virtual_search_record_cmp(const struct virtual_search_record *r1, const struct virtual_search_record *r2) { if (r1->mailbox_id < r2->mailbox_id) return -1; if (r1->mailbox_id > r2->mailbox_id) return 1; if (r1->real_uid < r2->real_uid) return -1; if (r1->real_uid > r2->real_uid) return 1; return 0; } static int mail_search_get_result(struct mail_search_context *ctx) { const struct mail_search_arg *arg; int ret = 1; for (arg = ctx->args->args; arg != NULL; arg = arg->next) { if (arg->result < 0) return -1; if (arg->result == 0) ret = 0; } return ret; } static void virtual_search_get_records(struct mail_search_context *ctx, struct virtual_search_context *vctx) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)ctx->transaction->box; const struct virtual_mail_index_record *vrec; struct virtual_search_record srec; const void *data; int result; i_zero(&srec); while (index_storage_search_next_update_seq(ctx)) { result = mail_search_get_result(ctx); i_assert(result != 0); if (result > 0) { /* full match, no need to check this any further */ seq_range_array_add(&vctx->result, ctx->seq); } else { /* possible match, save and check later */ mail_index_lookup_ext(mbox->box.view, ctx->seq, mbox->virtual_ext_id, &data, NULL); vrec = data; srec.mailbox_id = vrec->mailbox_id; srec.real_uid = vrec->real_uid; srec.virtual_seq = ctx->seq; array_append(&vctx->records, &srec, 1); } mail_search_args_reset(ctx->args->args, FALSE); } array_sort(&vctx->records, virtual_search_record_cmp); ctx->progress_max = array_count(&vctx->records); } struct mail_search_context * virtual_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct mail_search_context *ctx; struct virtual_search_context *vctx; ctx = index_storage_search_init(t, args, sort_program, wanted_fields, wanted_headers); vctx = i_new(struct virtual_search_context, 1); vctx->search_state = VIRTUAL_SEARCH_STATE_BUILD; i_array_init(&vctx->result, 64); i_array_init(&vctx->records, 64); MODULE_CONTEXT_SET(ctx, virtual_storage_module, vctx); virtual_search_get_records(ctx, vctx); seq_range_array_iter_init(&vctx->result_iter, &vctx->result); return ctx; } int virtual_search_deinit(struct mail_search_context *ctx) { struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx); array_free(&vctx->result); array_free(&vctx->records); i_free(vctx); return index_storage_search_deinit(ctx); } bool virtual_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r) { struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx); struct index_search_context *ictx = (struct index_search_context *)ctx; uint32_t seq; switch (vctx->search_state) { case VIRTUAL_SEARCH_STATE_BUILD: if (ctx->sort_program == NULL) vctx->search_state = VIRTUAL_SEARCH_STATE_SORT; else vctx->search_state = VIRTUAL_SEARCH_STATE_RETURN; return virtual_search_next_nonblock(ctx, mail_r, tryagain_r); case VIRTUAL_SEARCH_STATE_RETURN: return index_storage_search_next_nonblock(ctx, mail_r, tryagain_r); case VIRTUAL_SEARCH_STATE_SORT: /* the messages won't be returned sorted, so we'll have to do it ourself */ while (index_storage_search_next_nonblock(ctx, mail_r, tryagain_r)) seq_range_array_add(&vctx->result, (*mail_r)->seq); if (*tryagain_r) return FALSE; vctx->next_result_n = 0; vctx->search_state = VIRTUAL_SEARCH_STATE_SORT_DONE; /* fall through */ case VIRTUAL_SEARCH_STATE_SORT_DONE: *tryagain_r = FALSE; if (!seq_range_array_iter_nth(&vctx->result_iter, vctx->next_result_n, &seq)) return FALSE; vctx->next_result_n++; *mail_r = index_search_get_mail(ictx); i_assert(*mail_r != NULL); mail_set_seq(*mail_r, seq); return TRUE; } i_unreached(); } static void search_args_set_full_match(struct mail_search_arg *args) { for (; args != NULL; args = args->next) args->result = 1; } bool virtual_search_next_update_seq(struct mail_search_context *ctx) { struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx); const struct virtual_search_record *recs; unsigned int count; recs = array_get(&vctx->records, &count); if (vctx->next_record_idx < count) { /* go through potential results first */ ctx->seq = recs[vctx->next_record_idx++].virtual_seq - 1; if (!index_storage_search_next_update_seq(ctx)) i_unreached(); ctx->progress_cur = vctx->next_record_idx; return TRUE; } if (ctx->sort_program != NULL && seq_range_array_iter_nth(&vctx->result_iter, vctx->next_result_n, &ctx->seq)) { /* this is known to match fully */ search_args_set_full_match(ctx->args->args); vctx->next_result_n++; return TRUE; } return FALSE; } dovecot-2.2.33.2/src/plugins/virtual/virtual-transaction.c0000644000175000017500000000451113165463624020453 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "virtual-storage.h" #include "virtual-transaction.h" struct mailbox_transaction_context * virtual_transaction_get(struct mailbox_transaction_context *trans, struct mailbox *backend_box) { struct virtual_transaction_context *vt = (struct virtual_transaction_context *)trans; struct mailbox_transaction_context *const *bt, *new_bt; unsigned int i, count; bt = array_get(&vt->backend_transactions, &count); for (i = 0; i < count; i++) { if (bt[i]->box == backend_box) return bt[i]; } new_bt = mailbox_transaction_begin(backend_box, trans->flags); array_append(&vt->backend_transactions, &new_bt, 1); return new_bt; } struct mailbox_transaction_context * virtual_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct virtual_transaction_context *vt; vt = i_new(struct virtual_transaction_context, 1); i_array_init(&vt->backend_transactions, array_count(&mbox->backend_boxes)); index_transaction_init(&vt->t, box, flags); return &vt->t; } int virtual_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct virtual_transaction_context *vt = (struct virtual_transaction_context *)t; struct mailbox_transaction_context **bt; unsigned int i, count; int ret = 0; if (t->save_ctx != NULL) { virtual_save_free(t->save_ctx); t->save_ctx = NULL; } bt = array_get_modifiable(&vt->backend_transactions, &count); for (i = 0; i < count; i++) { if (mailbox_transaction_commit(&bt[i]) < 0) ret = -1; } array_free(&vt->backend_transactions); if (index_transaction_commit(t, changes_r) < 0) ret = -1; return ret; } void virtual_transaction_rollback(struct mailbox_transaction_context *t) { struct virtual_transaction_context *vt = (struct virtual_transaction_context *)t; struct mailbox_transaction_context **bt; unsigned int i, count; if (t->save_ctx != NULL) { virtual_save_free(t->save_ctx); t->save_ctx = NULL; } bt = array_get_modifiable(&vt->backend_transactions, &count); for (i = 0; i < count; i++) mailbox_transaction_rollback(&bt[i]); array_free(&vt->backend_transactions); index_transaction_rollback(t); } dovecot-2.2.33.2/src/plugins/virtual/virtual-storage.h0000644000175000017500000001631213165463624017601 00000000000000#ifndef VIRTUAL_STORAGE_H #define VIRTUAL_STORAGE_H #include "seq-range-array.h" #include "index-storage.h" #define VIRTUAL_STORAGE_NAME "virtual" #define VIRTUAL_SUBSCRIPTION_FILE_NAME ".virtual-subscriptions" #define VIRTUAL_CONFIG_FNAME "dovecot-virtual" #define VIRTUAL_CONTEXT(obj) \ MODULE_CONTEXT(obj, virtual_storage_module) struct virtual_save_context; struct virtual_mail_index_header { /* Increased by one each time the header is modified */ uint32_t change_counter; /* Number of mailbox records following this header. Mailbox names follow the mailbox records - they have neither NUL terminator nor padding. */ uint32_t mailbox_count; /* Highest used mailbox ID. IDs are never reused. */ uint32_t highest_mailbox_id; /* CRC32 of all the search parameters. If it changes, the mailbox is rebuilt. */ uint32_t search_args_crc32; }; struct virtual_mail_index_mailbox_record { /* Unique mailbox ID used as mailbox_id in records. */ uint32_t id; /* Length of this mailbox's name. */ uint32_t name_len; /* Synced UID validity value */ uint32_t uid_validity; /* Next unseen UID */ uint32_t next_uid; /* Synced highest modseq value */ uint64_t highest_modseq; }; struct virtual_mail_index_record { uint32_t mailbox_id; uint32_t real_uid; }; struct virtual_storage { struct mail_storage storage; /* List of mailboxes while a virtual mailbox is being opened. Used to track loops. */ ARRAY_TYPE(const_string) open_stack; unsigned int max_open_mailboxes; }; struct virtual_backend_uidmap { uint32_t real_uid; /* can be 0 temporarily while syncing before the UID is assigned */ uint32_t virtual_uid; }; struct virtual_backend_box { union mailbox_module_context module_ctx; struct virtual_mailbox *virtual_mbox; /* linked list for virtual_mailbox->open_backend_boxes_{head,tail} */ struct virtual_backend_box *prev_open, *next_open; /* Initially zero, updated by syncing */ uint32_t mailbox_id; const char *name; unsigned int sync_mailbox_idx; uint32_t sync_uid_validity; uint32_t sync_next_uid; uint64_t sync_highest_modseq; /* this value is either 0 or same as sync_highest_modseq. it's kept 0 when there are pending removes that have yet to be expunged */ uint64_t ondisk_highest_modseq; struct mail_search_args *search_args; struct mail_search_result *search_result; struct mailbox *box; /* Messages currently included in the virtual mailbox, sorted by real_uid */ ARRAY(struct virtual_backend_uidmap) uids; /* temporary mail used while syncing */ struct mail *sync_mail; /* pending removed UIDs */ ARRAY_TYPE(seq_range) sync_pending_removes; /* another process expunged these UIDs. they need to be removed on next sync. */ ARRAY_TYPE(seq_range) sync_outside_expunges; /* name contains a wildcard, this is a glob for it */ struct imap_match_glob *glob; struct mail_namespace *ns; /* mailbox metadata matching */ const char *metadata_entry, *metadata_value; /* notify context */ struct mailbox_list_notify *notify; unsigned int open_tracked:1; unsigned int open_failed:1; unsigned int sync_seen:1; unsigned int wildcard:1; unsigned int clear_recent:1; unsigned int negative_match:1; unsigned int uids_nonsorted:1; unsigned int search_args_initialized:1; unsigned int deleted:1; unsigned int notify_changes_started:1; /* if the box was opened for notify_changes */ }; ARRAY_DEFINE_TYPE(virtual_backend_box, struct virtual_backend_box *); struct virtual_mailbox { struct mailbox box; struct virtual_storage *storage; uint32_t virtual_ext_id; uint32_t virtual_guid_ext_id; uint32_t prev_uid_validity; uint32_t prev_change_counter; uint32_t highest_mailbox_id; uint32_t search_args_crc32; guid_128_t guid; struct virtual_backend_box *lookup_prev_bbox; uint32_t sync_virtual_next_uid; /* Mailboxes this virtual mailbox consists of, sorted by mailbox_id */ ARRAY_TYPE(virtual_backend_box) backend_boxes; /* backend mailbox where to save messages when saving to this mailbox */ struct virtual_backend_box *save_bbox; /* linked list of open backend mailboxes. head will contain the oldest accessed mailbox, tail will contain the newest. */ struct virtual_backend_box *open_backend_boxes_head; struct virtual_backend_box *open_backend_boxes_tail; /* number of backend mailboxes that are open currently. */ unsigned int backends_open_count; ARRAY_TYPE(mailbox_virtual_patterns) list_include_patterns; ARRAY_TYPE(mailbox_virtual_patterns) list_exclude_patterns; unsigned int uids_mapped:1; unsigned int sync_initialized:1; unsigned int inconsistent:1; unsigned int have_guid_flags_set:1; unsigned int have_guids:1; unsigned int have_save_guids:1; unsigned int ext_header_rewrite:1; }; extern MODULE_CONTEXT_DEFINE(virtual_storage_module, &mail_storage_module_register); extern struct mail_storage virtual_storage; extern struct mail_vfuncs virtual_mail_vfuncs; int virtual_config_read(struct virtual_mailbox *mbox); void virtual_config_free(struct virtual_mailbox *mbox); int virtual_mailbox_ext_header_read(struct virtual_mailbox *mbox, struct mail_index_view *view, bool *broken_r); struct virtual_backend_box * virtual_backend_box_lookup_name(struct virtual_mailbox *mbox, const char *name); struct virtual_backend_box * virtual_backend_box_lookup(struct virtual_mailbox *mbox, uint32_t mailbox_id); int virtual_backend_box_open(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox); void virtual_backend_box_close(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox); void virtual_backend_box_accessed(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox); void virtual_backend_box_sync_mail_unset(struct virtual_backend_box *bbox); struct mail_search_context * virtual_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); int virtual_search_deinit(struct mail_search_context *ctx); bool virtual_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r); bool virtual_search_next_update_seq(struct mail_search_context *ctx); struct mail * virtual_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers); struct mail * virtual_mail_set_backend_mail(struct mail *mail, struct virtual_backend_box *bbox); void virtual_mail_set_unattached_backend_mail(struct mail *mail, struct mail *backend_mail); struct mailbox_sync_context * virtual_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); struct mail_save_context * virtual_save_alloc(struct mailbox_transaction_context *t); int virtual_save_begin(struct mail_save_context *ctx, struct istream *input); int virtual_save_continue(struct mail_save_context *ctx); int virtual_save_finish(struct mail_save_context *ctx); void virtual_save_cancel(struct mail_save_context *ctx); void virtual_save_free(struct mail_save_context *ctx); void virtual_box_copy_error(struct mailbox *dest, struct mailbox *src); void virtual_backend_mailbox_allocated(struct mailbox *box); void virtual_backend_mailbox_opened(struct mailbox *box); #endif dovecot-2.2.33.2/src/plugins/virtual/virtual-storage.c0000644000175000017500000006347413165463624017607 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "str.h" #include "llist.h" #include "mkdir-parents.h" #include "unlink-directory.h" #include "index-mail.h" #include "mail-copy.h" #include "mail-search.h" #include "mailbox-list-private.h" #include "virtual-plugin.h" #include "virtual-transaction.h" #include "virtual-storage.h" #include "mailbox-list-notify.h" #include #include #include #include #define VIRTUAL_DEFAULT_MAX_OPEN_MAILBOXES 64 #define VIRTUAL_BACKEND_CONTEXT(obj) \ MODULE_CONTEXT(obj, virtual_backend_storage_module) struct virtual_backend_mailbox { union mailbox_module_context module_ctx; }; extern struct mail_storage virtual_storage; extern struct mailbox virtual_mailbox; extern struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs; struct virtual_storage_module virtual_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(virtual_backend_storage_module, &mail_storage_module_register); static bool ns_is_visible(struct mail_namespace *ns) { return (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 || (ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 || (ns->flags & NAMESPACE_FLAG_HIDDEN) == 0; } static const char *get_user_visible_mailbox_name(struct mailbox *box) { if (ns_is_visible(box->list->ns)) return box->vname; else { return t_strdup_printf("%c%s", mail_namespace_get_sep(box->list->ns), box->vname); } } void virtual_box_copy_error(struct mailbox *dest, struct mailbox *src) { const char *name, *str; enum mail_error error; name = get_user_visible_mailbox_name(src); str = mailbox_get_last_error(src, &error); str = t_strdup_printf("%s (for backend mailbox %s)", str, name); mail_storage_set_error(dest->storage, error, str); } static struct mail_storage *virtual_storage_alloc(void) { struct virtual_storage *storage; pool_t pool; pool = pool_alloconly_create("virtual storage", 1024); storage = p_new(pool, struct virtual_storage, 1); storage->storage = virtual_storage; storage->storage.pool = pool; p_array_init(&storage->open_stack, pool, 8); return &storage->storage; } static int virtual_storage_create(struct mail_storage *_storage, struct mail_namespace *ns ATTR_UNUSED, const char **error_r) { struct virtual_storage *storage = (struct virtual_storage *)_storage; const char *value; value = mail_user_plugin_getenv(_storage->user, "virtual_max_open_mailboxes"); if (value == NULL) storage->max_open_mailboxes = VIRTUAL_DEFAULT_MAX_OPEN_MAILBOXES; else if (str_to_uint(value, &storage->max_open_mailboxes) < 0) { *error_r = "Invalid virtual_max_open_mailboxes setting"; return -1; } return 0; } static void virtual_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, struct mailbox_list_settings *set) { if (set->layout == NULL) set->layout = MAILBOX_LIST_NAME_FS; if (set->subscription_fname == NULL) set->subscription_fname = VIRTUAL_SUBSCRIPTION_FILE_NAME; } struct virtual_backend_box * virtual_backend_box_lookup_name(struct virtual_mailbox *mbox, const char *name) { struct virtual_backend_box *const *bboxes; unsigned int i, count; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (strcmp(bboxes[i]->name, name) == 0) return bboxes[i]; } return NULL; } struct virtual_backend_box * virtual_backend_box_lookup(struct virtual_mailbox *mbox, uint32_t mailbox_id) { struct virtual_backend_box *const *bboxes; unsigned int i, count; if (mailbox_id == 0) return NULL; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->mailbox_id == mailbox_id) return bboxes[i]; } return NULL; } static bool virtual_mailbox_is_in_open_stack(struct virtual_storage *storage, const char *name) { const char *const *names; unsigned int i, count; names = array_get(&storage->open_stack, &count); for (i = 0; i < count; i++) { if (strcmp(names[i], name) == 0) return TRUE; } return FALSE; } static int virtual_backend_box_open_failed(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox) { enum mail_error error; const char *str; str = t_strdup_printf( "Virtual mailbox open failed because of mailbox %s: %s", get_user_visible_mailbox_name(bbox->box), mailbox_get_last_error(bbox->box, &error)); mail_storage_set_error(mbox->box.storage, error, str); mailbox_free(&bbox->box); if (error == MAIL_ERROR_PERM && bbox->wildcard) { /* this mailbox wasn't explicitly specified. just skip it. */ return 0; } return -1; } static int virtual_backend_box_alloc(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox, enum mailbox_flags flags) { struct mail_user *user = mbox->storage->storage.user; struct mail_namespace *ns; const char *mailbox; enum mailbox_existence existence; i_assert(bbox->box == NULL); if (!bbox->clear_recent) flags &= ~MAILBOX_FLAG_DROP_RECENT; mailbox = bbox->name; ns = mail_namespace_find(user->namespaces, mailbox); bbox->box = mailbox_alloc(ns->list, mailbox, flags); MODULE_CONTEXT_SET(bbox->box, virtual_storage_module, bbox); mailbox_set_reason(bbox->box, mbox->box.reason == NULL ? t_strdup_printf("virtual mailbox %s", mailbox_get_vname(&mbox->box)) : t_strdup_printf("virtual mailbox %s: %s", mailbox_get_vname(&mbox->box), mbox->box.reason)); if (mailbox_exists(bbox->box, TRUE, &existence) < 0) return virtual_backend_box_open_failed(mbox, bbox); if (existence != MAILBOX_EXISTENCE_SELECT) { /* ignore this. it could be intentional. */ if (mbox->storage->storage.user->mail_debug) { i_debug("virtual mailbox %s: " "Skipping non-existing mailbox %s", mbox->box.vname, bbox->box->vname); } mailbox_free(&bbox->box); return 0; } i_array_init(&bbox->uids, 64); i_array_init(&bbox->sync_pending_removes, 64); /* we use modseqs for being able to check quickly if backend mailboxes have changed. make sure the backend has them enabled. */ (void)mailbox_enable(bbox->box, MAILBOX_FEATURE_CONDSTORE); return 1; } static int virtual_mailboxes_open(struct virtual_mailbox *mbox, enum mailbox_flags flags) { struct virtual_backend_box *const *bboxes; unsigned int i, count; int ret; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; ) { ret = virtual_backend_box_alloc(mbox, bboxes[i], flags); if (ret <= 0) { if (ret < 0) break; array_delete(&mbox->backend_boxes, i, 1); bboxes = array_get(&mbox->backend_boxes, &count); } else { i++; } } if (i == count) return 0; else { /* failed */ for (; i > 0; i--) { mailbox_free(&bboxes[i-1]->box); array_free(&bboxes[i-1]->uids); } return -1; } } static struct mailbox * virtual_mailbox_alloc(struct mail_storage *_storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct virtual_storage *storage = (struct virtual_storage *)_storage; struct virtual_mailbox *mbox; pool_t pool; pool = pool_alloconly_create("virtual mailbox", 2048); mbox = p_new(pool, struct virtual_mailbox, 1); mbox->box = virtual_mailbox; mbox->box.pool = pool; mbox->box.storage = _storage; mbox->box.list = list; mbox->box.mail_vfuncs = &virtual_mail_vfuncs; mbox->box.virtual_vfuncs = &virtual_mailbox_vfuncs; index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX); mbox->storage = storage; mbox->virtual_ext_id = (uint32_t)-1; mbox->virtual_guid_ext_id = (uint32_t)-1; return &mbox->box; } void virtual_backend_box_sync_mail_unset(struct virtual_backend_box *bbox) { struct mailbox_transaction_context *trans; if (bbox->sync_mail != NULL) { trans = bbox->sync_mail->transaction; mail_free(&bbox->sync_mail); (void)mailbox_transaction_commit(&trans); } } static bool virtual_backend_box_can_close(struct virtual_backend_box *bbox) { if (bbox->box->notify_callback != NULL) { /* we can close it if notify is set because we have no need to keep it open for tracking changes */ return bbox->notify != NULL; } if (array_count(&bbox->sync_pending_removes) > 0) { /* FIXME: we could probably close this by making syncing support it? */ return FALSE; } return TRUE; } static bool virtual_backend_box_close_any_except(struct virtual_mailbox *mbox, struct virtual_backend_box *except_bbox) { struct virtual_backend_box *bbox; /* first try to close a mailbox without any transactions. we'll also skip any mailbox that has notifications enabled (ideally these would be handled by mailbox list index) */ for (bbox = mbox->open_backend_boxes_head; bbox != NULL; bbox = bbox->next_open) { i_assert(bbox->box->opened); if (bbox != except_bbox && bbox->box->transaction_count == 0 && virtual_backend_box_can_close(bbox)) { i_assert(bbox->sync_mail == NULL); virtual_backend_box_close(mbox, bbox); return TRUE; } } /* next try to close a mailbox that has sync_mail, but no other transactions */ for (bbox = mbox->open_backend_boxes_head; bbox != NULL; bbox = bbox->next_open) { if (bbox != except_bbox && bbox->sync_mail != NULL && bbox->box->transaction_count == 1 && virtual_backend_box_can_close(bbox)) { virtual_backend_box_sync_mail_unset(bbox); i_assert(bbox->box->transaction_count == 0); virtual_backend_box_close(mbox, bbox); return TRUE; } } return FALSE; } static void virtual_backend_mailbox_close(struct mailbox *box) { struct virtual_backend_box *bbox = VIRTUAL_CONTEXT(box); struct virtual_backend_mailbox *vbox = VIRTUAL_BACKEND_CONTEXT(box); if (bbox != NULL && bbox->open_tracked) { /* we could have gotten here from e.g. mailbox_autocreate() without going through virtual_mailbox_close() */ virtual_backend_box_close(bbox->virtual_mbox, bbox); } vbox->module_ctx.super.close(box); } void virtual_backend_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct virtual_backend_mailbox *vbox; vbox = p_new(box->pool, struct virtual_backend_mailbox, 1); vbox->module_ctx.super = *v; box->vlast = &vbox->module_ctx.super; v->close = virtual_backend_mailbox_close; MODULE_CONTEXT_SET(box, virtual_backend_storage_module, vbox); } void virtual_backend_mailbox_opened(struct mailbox *box) { struct virtual_backend_box *bbox = VIRTUAL_CONTEXT(box); struct virtual_mailbox *mbox; if (bbox == NULL) { /* not a backend for a virtual mailbox */ return; } i_assert(!bbox->open_tracked); mbox = bbox->virtual_mbox; /* the backend mailbox was already opened. if we didn't get here from virtual_backend_box_open() we may need to close a mailbox */ while (mbox->backends_open_count >= mbox->storage->max_open_mailboxes && virtual_backend_box_close_any_except(mbox, bbox)) ; bbox->open_tracked = TRUE; mbox->backends_open_count++; DLLIST2_APPEND_FULL(&mbox->open_backend_boxes_head, &mbox->open_backend_boxes_tail, bbox, prev_open, next_open); } int virtual_backend_box_open(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox) { i_assert(!bbox->box->opened); /* try to keep the number of open mailboxes below the threshold before opening the mailbox */ while (mbox->backends_open_count >= mbox->storage->max_open_mailboxes && virtual_backend_box_close_any_except(mbox, bbox)) ; return mailbox_open(bbox->box); } void virtual_backend_box_close(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox) { i_assert(bbox->box->opened); i_assert(bbox->open_tracked); if (bbox->search_result != NULL) mailbox_search_result_free(&bbox->search_result); if (bbox->search_args != NULL && bbox->search_args_initialized) { mail_search_args_deinit(bbox->search_args); bbox->search_args_initialized = FALSE; } i_assert(mbox->backends_open_count > 0); mbox->backends_open_count--; bbox->open_tracked = FALSE; DLLIST2_REMOVE_FULL(&mbox->open_backend_boxes_head, &mbox->open_backend_boxes_tail, bbox, prev_open, next_open); /* stop receiving notifications */ if (bbox->notify_changes_started) mailbox_notify_changes_stop(bbox->box); bbox->notify_changes_started = FALSE; mailbox_close(bbox->box); } void virtual_backend_box_accessed(struct virtual_mailbox *mbox, struct virtual_backend_box *bbox) { DLLIST2_REMOVE_FULL(&mbox->open_backend_boxes_head, &mbox->open_backend_boxes_tail, bbox, prev_open, next_open); DLLIST2_APPEND_FULL(&mbox->open_backend_boxes_head, &mbox->open_backend_boxes_tail, bbox, prev_open, next_open); } static void virtual_mailbox_close_internal(struct virtual_mailbox *mbox) { struct virtual_backend_box **bboxes; unsigned int i, count; bboxes = array_get_modifiable(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (bboxes[i]->box == NULL) continue; if (bboxes[i]->notify != NULL) mailbox_list_notify_deinit(&bboxes[i]->notify); if (bboxes[i]->box->opened) virtual_backend_box_close(mbox, bboxes[i]); mailbox_free(&bboxes[i]->box); if (array_is_created(&bboxes[i]->sync_outside_expunges)) array_free(&bboxes[i]->sync_outside_expunges); array_free(&bboxes[i]->sync_pending_removes); array_free(&bboxes[i]->uids); } i_assert(mbox->backends_open_count == 0); } static int virtual_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED, enum mailbox_existence *existence_r) { return index_storage_mailbox_exists_full(box, VIRTUAL_CONFIG_FNAME, existence_r); } static int virtual_mailbox_open(struct mailbox *box) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; bool broken; int ret = 0; if (virtual_mailbox_is_in_open_stack(mbox->storage, box->name)) { mail_storage_set_critical(box->storage, "Virtual mailbox loops: %s", box->name); return -1; } if (!array_is_created(&mbox->backend_boxes)) ret = virtual_config_read(mbox); if (ret == 0) { array_append(&mbox->storage->open_stack, &box->name, 1); ret = virtual_mailboxes_open(mbox, box->flags); array_delete(&mbox->storage->open_stack, array_count(&mbox->storage->open_stack)-1, 1); } if (ret < 0) { virtual_mailbox_close_internal(mbox); return -1; } if (index_storage_mailbox_open(box, FALSE) < 0) return -1; mbox->virtual_ext_id = mail_index_ext_register(mbox->box.index, "virtual", 0, sizeof(struct virtual_mail_index_record), sizeof(uint32_t)); mbox->virtual_guid_ext_id = mail_index_ext_register(mbox->box.index, "virtual-guid", GUID_128_SIZE, 0, 0); if (virtual_mailbox_ext_header_read(mbox, box->view, &broken) < 0) { virtual_mailbox_close_internal(mbox); index_storage_mailbox_close(box); return -1; } /* if GUID is missing write it here */ if (guid_128_is_empty(mbox->guid)) { guid_128_generate(mbox->guid); struct mail_index_transaction *t = mail_index_transaction_begin(box->view, 0); mail_index_update_header_ext(t, mbox->virtual_guid_ext_id, 0, mbox->guid, GUID_128_SIZE); if (mail_index_transaction_commit(&t) < 0) { mail_storage_set_critical(box->storage, "Cannot write GUID for virtual mailbox %s to index", mailbox_get_vname(box)); virtual_mailbox_close_internal(mbox); index_storage_mailbox_close(box); return -1; } } return 0; } static void virtual_mailbox_close(struct mailbox *box) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; virtual_mailbox_close_internal(mbox); index_storage_mailbox_close(box); } static void virtual_mailbox_free(struct mailbox *box) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; virtual_config_free(mbox); index_storage_mailbox_free(box); } static int virtual_mailbox_create(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED, bool directory ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't create virtual mailboxes"); return -1; } static int virtual_mailbox_update(struct mailbox *box, const struct mailbox_update *update ATTR_UNUSED) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't update virtual mailboxes"); return -1; } static int virtual_storage_set_have_guid_flags(struct virtual_mailbox *mbox) { struct virtual_backend_box *const *bboxes; unsigned int i, count; struct mailbox_status status; if (!mbox->box.opened) { if (mailbox_open(&mbox->box) < 0) return -1; } mbox->have_guids = TRUE; mbox->have_save_guids = TRUE; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (mailbox_get_status(bboxes[i]->box, 0, &status) < 0) { const char *errstr; enum mail_error error; errstr = mailbox_get_last_error(bboxes[i]->box, &error); if (error == MAIL_ERROR_NOTFOUND) { /* backend mailbox was just lost - skip it */ continue; } /* Not expected to happen, but we can't return failure since this could be called from mailbox_get_open_status() and it would panic. So just log the error and skip the mailbox. */ mail_storage_set_critical(mbox->box.storage, "Virtual mailbox %s: Failed to get have_guid existence for backend mailbox %s: %s", mailbox_get_vname(&mbox->box), mailbox_get_vname(bboxes[i]->box), errstr); continue; } if (!status.have_guids) mbox->have_guids = FALSE; if (!status.have_save_guids) mbox->have_save_guids = FALSE; } return 0; } static int virtual_storage_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; if ((items & STATUS_LAST_CACHED_SEQ) != 0) items |= STATUS_MESSAGES; if (index_storage_get_status(box, items, status_r) < 0) return -1; if ((items & STATUS_LAST_CACHED_SEQ) != 0) { /* Virtual mailboxes have no cached data of their own, so the current value is always 0. The most important use for this functionality is for "doveadm index" to do FTS indexing and it doesn't really matter there if we set this value correctly or not. So for now just assume that everything is indexed. */ status_r->last_cached_seq = status_r->messages; } if (!mbox->have_guid_flags_set) { if (virtual_storage_set_have_guid_flags(mbox) < 0) return -1; mbox->have_guid_flags_set = TRUE; } if (mbox->have_guids) status_r->have_guids = TRUE; if (mbox->have_save_guids) status_r->have_save_guids = TRUE; return 0; } static int virtual_mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; if (index_mailbox_get_metadata(box, items, metadata_r) < 0) return -1; i_assert(box->opened); if ((items & MAILBOX_METADATA_GUID) != 0) { if (guid_128_is_empty(mbox->guid)) { mail_storage_set_critical(box->storage, "GUID missing for virtual folder %s", mailbox_get_vname(box)); return -1; } guid_128_copy(metadata_r->guid, mbox->guid); } return 0; } static void virtual_notify_callback(struct mailbox *bbox ATTR_UNUSED, struct mailbox *box) { box->notify_callback(box, box->notify_context); } static void virtual_backend_box_changed(struct virtual_backend_box *bbox) { virtual_notify_callback(bbox->box, &bbox->virtual_mbox->box); } static int virtual_notify_start(struct virtual_backend_box *bbox) { i_assert(bbox->notify == NULL); if (mailbox_list_notify_init(bbox->box->list, MAILBOX_LIST_NOTIFY_STATUS, &bbox->notify) < 0) /* did not support notifications */ return -1; mailbox_list_notify_wait(bbox->notify, virtual_backend_box_changed, bbox); return 0; } static void virtual_notify_changes(struct mailbox *box) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct virtual_backend_box **bboxp; if (box->notify_callback == NULL) { array_foreach_modifiable(&mbox->backend_boxes, bboxp) { if ((*bboxp)->notify_changes_started) { mailbox_notify_changes_stop((*bboxp)->box); (*bboxp)->notify_changes_started = FALSE; } if ((*bboxp)->notify != NULL) mailbox_list_notify_deinit(&(*bboxp)->notify); } return; } array_foreach_modifiable(&mbox->backend_boxes, bboxp) { if (array_count(&mbox->backend_boxes) == 1 && (*bboxp)->box->opened) { /* There's only a single backend mailbox and its indexes are already opened. Might as well use the backend directly for notifications. */ } else { /* we are already waiting for notifications */ if ((*bboxp)->notify != NULL) continue; /* wait for notifications */ if (virtual_notify_start(*bboxp) == 0) continue; /* it did not work, so open the mailbox and use alternative method */ } if (!(*bboxp)->box->opened && virtual_backend_box_open(mbox, *bboxp) < 0) { /* we can't report error in here, so do it later */ (*bboxp)->open_failed = TRUE; continue; } mailbox_notify_changes((*bboxp)->box, virtual_notify_callback, box); (*bboxp)->notify_changes_started = TRUE; } } static void virtual_get_virtual_uids(struct mailbox *box, struct mailbox *backend_mailbox, const ARRAY_TYPE(seq_range) *backend_uids, ARRAY_TYPE(seq_range) *virtual_uids_r) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct virtual_backend_box *bbox; const struct virtual_backend_uidmap *uids; struct seq_range_iter iter; unsigned int n, i, count; uint32_t uid; if (mbox->lookup_prev_bbox != NULL && strcmp(mbox->lookup_prev_bbox->box->vname, backend_mailbox->vname) == 0) bbox = mbox->lookup_prev_bbox; else { bbox = virtual_backend_box_lookup_name(mbox, backend_mailbox->vname); mbox->lookup_prev_bbox = bbox; } if (bbox == NULL) return; uids = array_get(&bbox->uids, &count); i = 0; seq_range_array_iter_init(&iter, backend_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { while (i < count && uids[i].real_uid < uid) i++; if (i < count && uids[i].real_uid == uid) { i_assert(uids[i].virtual_uid > 0); seq_range_array_add(virtual_uids_r, uids[i].virtual_uid); i++; } } } static void virtual_get_virtual_uid_map(struct mailbox *box, struct mailbox *backend_mailbox, const ARRAY_TYPE(seq_range) *backend_uids, ARRAY_TYPE(uint32_t) *virtual_uids_r) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct virtual_backend_box *bbox; const struct virtual_backend_uidmap *uids; struct seq_range_iter iter; unsigned int n, i, count; uint32_t uid; if (mbox->lookup_prev_bbox != NULL && strcmp(mbox->lookup_prev_bbox->box->vname, backend_mailbox->vname) == 0) bbox = mbox->lookup_prev_bbox; else { bbox = virtual_backend_box_lookup_name(mbox, backend_mailbox->vname); mbox->lookup_prev_bbox = bbox; } if (bbox == NULL) return; uids = array_get(&bbox->uids, &count); i = 0; seq_range_array_iter_init(&iter, backend_uids); n = 0; while (seq_range_array_iter_nth(&iter, n++, &uid)) { while (i < count && uids[i].real_uid < uid) i++; if (i == count || uids[i].real_uid > uid) { uint32_t zero = 0; array_append(virtual_uids_r, &zero, 1); } else { i_assert(uids[i].virtual_uid > 0); array_append(virtual_uids_r, &uids[i].virtual_uid, 1); i++; } } } static void virtual_get_virtual_backend_boxes(struct mailbox *box, ARRAY_TYPE(mailboxes) *mailboxes, bool only_with_msgs) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; struct virtual_backend_box *const *bboxes; unsigned int i, count; bboxes = array_get(&mbox->backend_boxes, &count); for (i = 0; i < count; i++) { if (!only_with_msgs || array_count(&bboxes[i]->uids) > 0) array_append(mailboxes, &bboxes[i]->box, 1); } } static bool virtual_is_inconsistent(struct mailbox *box) { struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; if (mbox->inconsistent) return TRUE; return index_storage_is_inconsistent(box); } static int virtual_list_index_has_changed(struct mailbox *box ATTR_UNUSED, struct mail_index_view *list_view ATTR_UNUSED, uint32_t seq ATTR_UNUSED) { /* we don't have any quick and easy optimizations for tracking virtual folders. ideally we'd completely disable mailbox list indexes for them, but this is the easiest way to do it for now. */ return 1; } static void virtual_list_index_update_sync(struct mailbox *box ATTR_UNUSED, struct mail_index_transaction *trans ATTR_UNUSED, uint32_t seq ATTR_UNUSED) { } struct mail_storage virtual_storage = { .name = VIRTUAL_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_NOQUOTA, .v = { NULL, virtual_storage_alloc, virtual_storage_create, index_storage_destroy, NULL, virtual_storage_get_list_settings, NULL, virtual_mailbox_alloc, NULL, NULL, } }; struct mailbox virtual_mailbox = { .v = { index_storage_is_readonly, index_storage_mailbox_enable, virtual_mailbox_exists, virtual_mailbox_open, virtual_mailbox_close, virtual_mailbox_free, virtual_mailbox_create, virtual_mailbox_update, index_storage_mailbox_delete, index_storage_mailbox_rename, virtual_storage_get_status, virtual_mailbox_get_metadata, index_storage_set_subscribed, index_storage_attribute_set, index_storage_attribute_get, index_storage_attribute_iter_init, index_storage_attribute_iter_next, index_storage_attribute_iter_deinit, virtual_list_index_has_changed, virtual_list_index_update_sync, virtual_storage_sync_init, index_mailbox_sync_next, index_mailbox_sync_deinit, NULL, virtual_notify_changes, virtual_transaction_begin, virtual_transaction_commit, virtual_transaction_rollback, NULL, virtual_mail_alloc, virtual_search_init, virtual_search_deinit, virtual_search_next_nonblock, virtual_search_next_update_seq, virtual_save_alloc, virtual_save_begin, virtual_save_continue, virtual_save_finish, virtual_save_cancel, mail_storage_copy, NULL, NULL, NULL, virtual_is_inconsistent } }; struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs = { virtual_get_virtual_uids, virtual_get_virtual_uid_map, virtual_get_virtual_backend_boxes }; dovecot-2.2.33.2/src/plugins/virtual/virtual-save.c0000644000175000017500000001076013147010712017051 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "virtual-transaction.h" #include "virtual-storage.h" struct virtual_save_context { struct mail_save_context ctx; struct mail_save_context *backend_save_ctx; struct mailbox *backend_box; char *open_errstr; enum mail_error open_error; }; struct mail_save_context * virtual_save_alloc(struct mailbox_transaction_context *_t) { struct virtual_transaction_context *t = (struct virtual_transaction_context *)_t; struct virtual_mailbox *mbox = (struct virtual_mailbox *)_t->box; struct mailbox_transaction_context *backend_trans; struct virtual_save_context *ctx; const char *errstr; if (_t->save_ctx == NULL) { ctx = i_new(struct virtual_save_context, 1); ctx->ctx.transaction = &t->t; _t->save_ctx = &ctx->ctx; } else { ctx = (struct virtual_save_context *)_t->save_ctx; } if (mbox->save_bbox != NULL) { i_assert(ctx->backend_save_ctx == NULL); i_assert(ctx->open_errstr == NULL); if (!mbox->save_bbox->box->opened && virtual_backend_box_open(mbox, mbox->save_bbox) < 0) { errstr = mailbox_get_last_error(mbox->save_bbox->box, &ctx->open_error); ctx->open_errstr = i_strdup(errstr); } else { backend_trans = virtual_transaction_get(_t, mbox->save_bbox->box); ctx->backend_save_ctx = mailbox_save_alloc(backend_trans); } virtual_backend_box_accessed(mbox, mbox->save_bbox); } return _t->save_ctx; } static struct mail_keywords * virtual_copy_keywords(struct mailbox *src_box, const struct mail_keywords *src_keywords, struct mailbox *dest_box) { struct mailbox_status status; ARRAY_TYPE(keywords) kw_strings; const char *const *kwp; unsigned int i; if (src_keywords == NULL || src_keywords->count == 0) return NULL; t_array_init(&kw_strings, src_keywords->count + 1); mailbox_get_open_status(src_box, STATUS_KEYWORDS, &status); for (i = 0; i < src_keywords->count; i++) { kwp = array_idx(status.keywords, src_keywords->idx[i]); array_append(&kw_strings, kwp, 1); } array_append_zero(&kw_strings); return mailbox_keywords_create_valid(dest_box, array_idx(&kw_strings, 0)); } int virtual_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx; struct mail_save_data *mdata = &_ctx->data; struct mail_keywords *keywords; if (ctx->backend_save_ctx == NULL) { if (ctx->open_errstr != NULL) { /* mailbox_open() failed */ mail_storage_set_error(_ctx->transaction->box->storage, ctx->open_error, ctx->open_errstr); } else { mail_storage_set_error(_ctx->transaction->box->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't save messages to this virtual mailbox"); } return -1; } ctx->backend_box = ctx->backend_save_ctx->transaction->box; keywords = virtual_copy_keywords(_ctx->transaction->box, mdata->keywords, ctx->backend_box); mailbox_save_set_flags(ctx->backend_save_ctx, mdata->flags | mdata->pvt_flags, keywords); if (keywords != NULL) mail_index_keywords_unref(&keywords); mailbox_save_set_received_date(ctx->backend_save_ctx, mdata->received_date, mdata->received_tz_offset); mailbox_save_set_from_envelope(ctx->backend_save_ctx, mdata->from_envelope); mailbox_save_set_guid(ctx->backend_save_ctx, mdata->guid); mailbox_save_set_min_modseq(ctx->backend_save_ctx, mdata->min_modseq); virtual_mail_set_unattached_backend_mail(_ctx->dest_mail, ctx->backend_save_ctx->dest_mail); return mailbox_save_begin(&ctx->backend_save_ctx, input); } int virtual_save_continue(struct mail_save_context *_ctx) { struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx; return mailbox_save_continue(ctx->backend_save_ctx); } int virtual_save_finish(struct mail_save_context *_ctx) { struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx; int ret; ret = mailbox_save_finish(&ctx->backend_save_ctx); index_save_context_free(_ctx); return ret; } void virtual_save_cancel(struct mail_save_context *_ctx) { struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx; if (ctx->backend_save_ctx != NULL) mailbox_save_cancel(&ctx->backend_save_ctx); i_free_and_null(ctx->open_errstr); _ctx->unfinished = FALSE; } void virtual_save_free(struct mail_save_context *_ctx) { struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx; virtual_save_cancel(_ctx); mailbox_save_context_deinit(_ctx); i_free(ctx); } dovecot-2.2.33.2/src/plugins/virtual/Makefile.am0000644000175000017500000000122313123174404016314 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-imap-storage NOPLUGIN_LDFLAGS = lib20_virtual_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_virtual_plugin.la lib20_virtual_plugin_la_SOURCES = \ virtual-config.c \ virtual-mail.c \ virtual-plugin.c \ virtual-search.c \ virtual-storage.c \ virtual-save.c \ virtual-sync.c \ virtual-transaction.c noinst_HEADERS = \ virtual-plugin.h \ virtual-storage.h \ virtual-transaction.h dovecot-2.2.33.2/src/plugins/notify-status/0002755000175000017500000000000013172375613015520 500000000000000dovecot-2.2.33.2/src/plugins/notify-status/Makefile.in0000644000175000017500000005531313172375574017520 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/notify-status ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_notify_status_plugin_la_LIBADD = am_lib20_notify_status_plugin_la_OBJECTS = notify-status-plugin.lo lib20_notify_status_plugin_la_OBJECTS = \ $(am_lib20_notify_status_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_notify_status_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib20_notify_status_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_notify_status_plugin_la_SOURCES) DIST_SOURCES = $(lib20_notify_status_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/plugins/notify lib20_notify_status_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_notify_status_plugin.la lib20_notify_status_plugin_la_SOURCES = \ notify-status-plugin.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/notify-status/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/notify-status/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_notify_status_plugin.la: $(lib20_notify_status_plugin_la_OBJECTS) $(lib20_notify_status_plugin_la_DEPENDENCIES) $(EXTRA_lib20_notify_status_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_notify_status_plugin_la_LINK) -rpath $(moduledir) $(lib20_notify_status_plugin_la_OBJECTS) $(lib20_notify_status_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-status-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/notify-status/notify-status-plugin.c0000644000175000017500000002565513165463624021746 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "json-parser.h" #include "str.h" #include "var-expand.h" #include "mail-user.h" #include "mail-storage.h" #include "mail-storage-private.h" #include "mail-namespace.h" #include "mail-storage-hooks.h" #include "imap-match.h" #include "dict.h" #include "notify-plugin.h" #define NOTIFY_STATUS_SETTING_DICT_URI "notify_status_dict" #define NOTIFY_STATUS_SETTING_MAILBOX_PREFIX "notify_status_mailbox" #define NOTIFY_STATUS_SETTING_VALUE_TEMPLATE "notify_status_value" #define NOTIFY_STATUS_SETTING_VALUE_TEMPLATE_DEFAULT "{\"messages\":%{messages},\"unseen\":%{unseen}}" #define NOTIFY_STATUS_KEY "priv/status/%s" #define NOTIFY_STATUS_USER_CONTEXT(obj) \ (struct notify_status_user*)MODULE_CONTEXT(obj, notify_status_user_module) static MODULE_CONTEXT_DEFINE_INIT(notify_status_user_module, &mail_user_module_register); void notify_status_plugin_init(struct module *module); void notify_status_plugin_deinit(void); const char *notify_status_plugin_version = DOVECOT_ABI_VERSION; const char *notify_status_plugin_dependencies[] = { "notify", NULL }; ARRAY_DEFINE_TYPE(imap_match_glob, struct imap_match_glob*); struct notify_status_mail_txn { struct mailbox *box; bool changed:1; }; struct notify_status_user { union mail_user_module_context module_ctx; ARRAY_TYPE(imap_match_glob) patterns; struct dict *dict; const char *value_template; struct notify_context *context; }; static int notify_status_dict_init(struct mail_user *user, const char *uri, struct dict **dict_r, const char **error_r) { if (dict_init(uri, DICT_DATA_TYPE_STRING, user->username, user->set->base_dir, dict_r, error_r) < 0) { *error_r = t_strdup_printf("dict_init(%s) failed: %s", uri, *error_r); return -1; } return 0; } static void notify_status_mailbox_patterns_init(struct mail_user *user, ARRAY_TYPE(imap_match_glob) *patterns) { const char *value; unsigned int i; p_array_init(patterns, user->pool, 2); for(i=1;;i++) { struct imap_match_glob **glob; const char *key = NOTIFY_STATUS_SETTING_MAILBOX_PREFIX; if (i > 1) key = t_strdup_printf("%s%u", key, i); value = mail_user_plugin_getenv(user, key); if (value == NULL) return; char sep = mail_namespace_get_sep(user->namespaces); glob = array_append_space(patterns); *glob = imap_match_init(user->pool, value, TRUE, sep); } } static bool notify_status_mailbox_enabled(struct mailbox *box) { struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user); struct imap_match_glob **glob; /* not enabled */ if (nuser == NULL) return FALSE; /* if no patterns defined, anything goes */ if (array_count(&nuser->patterns) == 0) return TRUE; array_foreach_modifiable(&nuser->patterns, glob) { if ((imap_match(*glob, mailbox_get_vname(box)) & IMAP_MATCH_YES) != 0) return TRUE; } return FALSE; } static void notify_update_callback(int ret, void *context ATTR_UNUSED) { if (ret == DICT_COMMIT_RET_OK || ret == DICT_COMMIT_RET_NOTFOUND) return; i_error("notify-status: dict_transaction_commit failed"); } #define MAILBOX_STATUS_NOTIFY (STATUS_MESSAGES|STATUS_UNSEEN|\ STATUS_RECENT|STATUS_UIDNEXT|\ STATUS_UIDVALIDITY|\ STATUS_HIGHESTMODSEQ|STATUS_FIRST_RECENT_UID|\ STATUS_HIGHESTPVTMODSEQ) static void notify_update_mailbox_status(struct mailbox *box) { struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user); i_assert(nuser != NULL); struct dict_transaction_context *t; struct mailbox_status status; if (user->mail_debug) i_debug("notify-status: Updating mailbox %s status", mailbox_get_vname(box)); box = mailbox_alloc(mailbox_get_namespace(box)->list, mailbox_get_vname(box), MAILBOX_FLAG_READONLY); if (mailbox_open(box) < 0) { i_error("notify-status: mailbox_open(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_error(box, NULL)); } else if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("notify-status: mailbox_sync(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_error(box, NULL)); } else if (mailbox_get_status(box, MAILBOX_STATUS_NOTIFY, &status) < 0) { i_error("notify-status: mailbox_get_status(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_error(box, NULL)); } else { string_t *username = t_str_new(strlen(user->username)); string_t *mboxname = t_str_new(64); json_append_escaped(username, user->username); json_append_escaped(mboxname, mailbox_get_vname(box)); const struct var_expand_table values[] = { { '\0', str_c(username), "username" }, { '\0', str_c(mboxname), "mailbox" }, { '\0', dec2str(status.messages), "messages" }, { '\0', dec2str(status.unseen), "unseen" }, { '\0', dec2str(status.recent), "recent" }, { '\0', dec2str(status.uidvalidity), "uidvalidity" }, { '\0', dec2str(status.uidnext), "uidnext" }, { '\0', dec2str(status.first_recent_uid), "first_recent_uid" }, { '\0', dec2str(status.highest_modseq), "highest_modseq" }, { '\0', dec2str(status.highest_pvt_modseq), "highest_pvt_modseq" }, { '\0', NULL, NULL } }; const char *key = t_strdup_printf(NOTIFY_STATUS_KEY, mailbox_get_vname(box)); string_t *dest = t_str_new(64); var_expand(dest, nuser->value_template, values); t = dict_transaction_begin(nuser->dict); dict_set(t, key, str_c(dest)); dict_transaction_commit_async(&t, notify_update_callback, NULL); } mailbox_free(&box); } static void notify_remove_mailbox_status(struct mailbox *box) { struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user); i_assert(nuser != NULL); struct dict_transaction_context *t; if (user->mail_debug) i_debug("notify-status: Removing mailbox %s status", mailbox_get_vname(box)); const char *key = t_strdup_printf(NOTIFY_STATUS_KEY, mailbox_get_vname(box)); t = dict_transaction_begin(nuser->dict); dict_unset(t, key); dict_transaction_commit_async(&t, notify_update_callback, NULL) ; } static void *notify_status_mail_transaction_begin(struct mailbox_transaction_context *t) { struct notify_status_mail_txn *txn = i_new(struct notify_status_mail_txn, 1); txn->box = mailbox_transaction_get_mailbox(t); return txn; } static void notify_status_mail_transaction_commit(void *t, struct mail_transaction_commit_changes *changes ATTR_UNUSED) { struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t; if (txn->changed && notify_status_mailbox_enabled(txn->box)) notify_update_mailbox_status(txn->box); i_free(txn); } static void notify_status_mail_transaction_rollback(void *t) { i_free(t); } static void notify_status_mailbox_create(struct mailbox *box) { if (notify_status_mailbox_enabled(box)) notify_update_mailbox_status(box); } static void notify_status_mailbox_delete_commit(void *txn ATTR_UNUSED, struct mailbox *box) { if (notify_status_mailbox_enabled(box)) notify_remove_mailbox_status(box); } static void notify_status_mailbox_rename(struct mailbox *src, struct mailbox *dest) { if (notify_status_mailbox_enabled(src)) notify_remove_mailbox_status(src); if (notify_status_mailbox_enabled(dest)) notify_update_mailbox_status(dest); } static void notify_status_mail_save(void *t, struct mail *mail ATTR_UNUSED) { struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t; txn->changed = TRUE; } static void notify_status_mail_copy(void *t, struct mail *src ATTR_UNUSED, struct mail *dst ATTR_UNUSED) { struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t; txn->changed = TRUE; } static void notify_status_mail_expunge(void *t, struct mail *mail ATTR_UNUSED) { struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t; txn->changed = TRUE; } static void notify_status_mail_update_flags(void *t, struct mail *mail, enum mail_flags old_flags) { struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t; if ((old_flags & MAIL_SEEN) != (mail_get_flags(mail) & MAIL_SEEN)) txn->changed = TRUE; } static const struct notify_vfuncs notify_vfuncs = { .mail_transaction_begin = notify_status_mail_transaction_begin, .mail_save = notify_status_mail_save, .mail_copy = notify_status_mail_copy, .mail_expunge = notify_status_mail_expunge, .mail_update_flags = notify_status_mail_update_flags, .mail_transaction_commit = notify_status_mail_transaction_commit, .mail_transaction_rollback = notify_status_mail_transaction_rollback, .mailbox_create = notify_status_mailbox_create, .mailbox_delete_commit = notify_status_mailbox_delete_commit, .mailbox_rename = notify_status_mailbox_rename, }; static void notify_status_mail_user_deinit(struct mail_user *user) { struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user); i_assert(nuser != NULL); dict_wait(nuser->dict); dict_deinit(&nuser->dict); notify_unregister(nuser->context); nuser->module_ctx.super.deinit(user); } static void notify_status_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct notify_status_user *nuser; struct dict *dict; const char *error; const char *template = mail_user_plugin_getenv(user, NOTIFY_STATUS_SETTING_VALUE_TEMPLATE); const char *uri = mail_user_plugin_getenv(user, NOTIFY_STATUS_SETTING_DICT_URI); if (user->autocreated) return; if (uri == NULL || *uri == '\0') { if (user->mail_debug) i_debug("notify-status: Disabled - Missing plugin/" NOTIFY_STATUS_SETTING_DICT_URI" setting"); return; } if (template == NULL || *template == '\0') template = NOTIFY_STATUS_SETTING_VALUE_TEMPLATE_DEFAULT; if (notify_status_dict_init(user, uri, &dict, &error) < 0) { i_error("notify-status: %s", error); return; } nuser = p_new(user->pool, struct notify_status_user, 1); nuser->module_ctx.super = *v; nuser->dict = dict; user->vlast = &nuser->module_ctx.super; v->deinit = notify_status_mail_user_deinit; /* either static value or lifetime is user object's lifetime */ nuser->value_template = template; MODULE_CONTEXT_SET(user, notify_status_user_module, nuser); } static void notify_status_mail_namespaces_created(struct mail_namespace *namespaces) { struct mail_user *user = namespaces->user; struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user); if (nuser == NULL) return; notify_status_mailbox_patterns_init(user, &nuser->patterns); nuser->context = notify_register(¬ify_vfuncs); } static const struct mail_storage_hooks notify_storage_hooks = { .mail_user_created = notify_status_mail_user_created, .mail_namespaces_created = notify_status_mail_namespaces_created, }; void notify_status_plugin_init(struct module *module) { mail_storage_hooks_add(module, ¬ify_storage_hooks); } void notify_status_plugin_deinit(void) { mail_storage_hooks_remove(¬ify_storage_hooks); } dovecot-2.2.33.2/src/plugins/notify-status/Makefile.am0000644000175000017500000000070013165463543017471 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/plugins/notify NOPLUGIN_LDFLAGS = lib20_notify_status_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_notify_status_plugin.la lib20_notify_status_plugin_la_SOURCES = \ notify-status-plugin.c dovecot-2.2.33.2/src/plugins/quota/0002755000175000017500000000000013172375613014020 500000000000000dovecot-2.2.33.2/src/plugins/quota/Makefile.in0000644000175000017500000010423513172375575016017 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = quota-status$(EXEEXT) noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/plugins/quota ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) lib10_doveadm_quota_plugin_la_LIBADD = am_lib10_doveadm_quota_plugin_la_OBJECTS = doveadm-quota.lo lib10_doveadm_quota_plugin_la_OBJECTS = \ $(am_lib10_doveadm_quota_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib10_doveadm_quota_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib10_doveadm_quota_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = lib10_quota_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am__objects_1 = quota.lo quota-count.lo quota-fs.lo quota-dict.lo \ quota-dirsize.lo quota-imapc.lo quota-maildir.lo \ quota-plugin.lo quota-storage.lo quota-util.lo am_lib10_quota_plugin_la_OBJECTS = $(am__objects_1) @HAVE_RQUOTA_TRUE@am__objects_2 = rquota_xdr.lo nodist_lib10_quota_plugin_la_OBJECTS = $(am__objects_2) lib10_quota_plugin_la_OBJECTS = $(am_lib10_quota_plugin_la_OBJECTS) \ $(nodist_lib10_quota_plugin_la_OBJECTS) lib10_quota_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib10_quota_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__EXEEXT_1 = test-quota-util$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) am_quota_status_OBJECTS = quota-status.$(OBJEXT) \ quota-status-settings.$(OBJEXT) quota_status_OBJECTS = $(am_quota_status_OBJECTS) am_test_quota_util_OBJECTS = test-quota-util.$(OBJEXT) test_quota_util_OBJECTS = $(am_test_quota_util_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib10_doveadm_quota_plugin_la_SOURCES) \ $(lib10_quota_plugin_la_SOURCES) \ $(nodist_lib10_quota_plugin_la_SOURCES) \ $(quota_status_SOURCES) $(test_quota_util_SOURCES) DIST_SOURCES = $(lib10_doveadm_quota_plugin_la_SOURCES) \ $(lib10_quota_plugin_la_SOURCES) $(quota_status_SOURCES) \ $(test_quota_util_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/imapc \ -I$(top_srcdir)/src/lib-storage/index/maildir \ -I$(top_srcdir)/src/doveadm lib10_doveadm_quota_plugin_la_LDFLAGS = -module -avoid-version lib10_quota_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_quota_plugin.la quota_dist_sources = \ quota.c \ quota-count.c \ quota-fs.c \ quota-dict.c \ quota-dirsize.c \ quota-imapc.c \ quota-maildir.c \ quota-plugin.c \ quota-storage.c \ quota-util.c quota_common_objects = \ quota.lo \ quota-count.lo \ quota-fs.lo \ quota-dict.lo \ quota-dirsize.lo \ quota-imapc.lo \ quota-maildir.lo \ quota-plugin.lo \ quota-storage.lo \ quota-util.lo \ $(RQUOTA_XDR_LO) lib10_quota_plugin_la_SOURCES = $(quota_dist_sources) nodist_lib10_quota_plugin_la_SOURCES = $(RQUOTA_XDR) lib10_quota_plugin_la_LIBADD = $(QUOTA_LIBS) doveadm_module_LTLIBRARIES = \ lib10_doveadm_quota_plugin.la lib10_doveadm_quota_plugin_la_SOURCES = \ doveadm-quota.c quota_status_SOURCES = \ quota-status.c \ quota-status-settings.c quota_status_LDADD = \ $(quota_common_objects) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) \ $(QUOTA_LIBS) quota_status_DEPENDENCIES = \ $(quota_common_objects) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) @HAVE_RQUOTA_TRUE@RQUOTA_XDR = rquota_xdr.c @HAVE_RQUOTA_TRUE@RQUOTA_XDR_LO = rquota_xdr.lo #RQUOTA_X = /usr/include/rpcsvc/rquota.x @HAVE_RQUOTA_TRUE@RQUOTA_X = $(srcdir)/rquota.x pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ quota.h \ quota-fs.h \ quota-plugin.h \ quota-private.h noinst_HEADERS = \ quota-status-settings.h EXTRA_DIST = rquota.x test_programs = \ test-quota-util test_libs = \ ../../lib-test/libtest.la \ ../../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_quota_util_SOURCES = test-quota-util.c test_quota_util_LDADD = quota-util.lo $(test_libs) test_quota_util_DEPENDENCIES = quota-util.lo $(test_deps) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/quota/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/quota/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib10_doveadm_quota_plugin.la: $(lib10_doveadm_quota_plugin_la_OBJECTS) $(lib10_doveadm_quota_plugin_la_DEPENDENCIES) $(EXTRA_lib10_doveadm_quota_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_doveadm_quota_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib10_doveadm_quota_plugin_la_OBJECTS) $(lib10_doveadm_quota_plugin_la_LIBADD) $(LIBS) lib10_quota_plugin.la: $(lib10_quota_plugin_la_OBJECTS) $(lib10_quota_plugin_la_DEPENDENCIES) $(EXTRA_lib10_quota_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_quota_plugin_la_LINK) -rpath $(moduledir) $(lib10_quota_plugin_la_OBJECTS) $(lib10_quota_plugin_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list quota-status$(EXEEXT): $(quota_status_OBJECTS) $(quota_status_DEPENDENCIES) $(EXTRA_quota_status_DEPENDENCIES) @rm -f quota-status$(EXEEXT) $(AM_V_CCLD)$(LINK) $(quota_status_OBJECTS) $(quota_status_LDADD) $(LIBS) test-quota-util$(EXEEXT): $(test_quota_util_OBJECTS) $(test_quota_util_DEPENDENCIES) $(EXTRA_test_quota_util_DEPENDENCIES) @rm -f test-quota-util$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_quota_util_OBJECTS) $(test_quota_util_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-quota.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-count.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-dirsize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-fs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-imapc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-maildir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-status-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rquota_xdr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-quota-util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibexecPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibexecPROGRAMS cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-doveadm_moduleLTLIBRARIES install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile @HAVE_RQUOTA_TRUE@rquota_xdr.c: Makefile rquota.h @HAVE_RQUOTA_TRUE@ if [ "$(top_srcdir)" != "$(top_builddir)" ]; then \ @HAVE_RQUOTA_TRUE@ cp $(RQUOTA_X) $(top_builddir)/src/plugins/quota/; \ @HAVE_RQUOTA_TRUE@ fi; \ @HAVE_RQUOTA_TRUE@ (echo '#include "lib.h"'; \ @HAVE_RQUOTA_TRUE@ echo '#include '; \ @HAVE_RQUOTA_TRUE@ $(RPCGEN) -c $(top_builddir)/src/plugins/quota/rquota.x | \ @HAVE_RQUOTA_TRUE@ sed -e 's/IXDR_PUT/(void)IXDR_PUT/g' \ @HAVE_RQUOTA_TRUE@ -e 's,/usr/include/rpcsvc/rquota.h,rquota.h,' \ @HAVE_RQUOTA_TRUE@ -e 's/int32_t \*buf/int32_t *buf ATTR_UNUSED/' \ @HAVE_RQUOTA_TRUE@ -e 's/^static char rcsid.*//' ) > rquota_xdr.c @HAVE_RQUOTA_TRUE@rquota.h: Makefile $(RQUOTA_X) @HAVE_RQUOTA_TRUE@ $(RPCGEN) -h $(RQUOTA_X) > rquota.h @HAVE_RQUOTA_TRUE@quota-fs.lo: rquota.h clean-generic: if [ "$(top_srcdir)" != "$(top_builddir)" ]; then \ rm -f $(top_builddir)/src/plugins/quota/rquota.x; \ fi; \ rm -f rquota_xdr.c rquota.h check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/quota/quota-imapc.c0000644000175000017500000003040213165463624016323 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "imap-arg.h" #include "imapc-storage.h" #include "mailbox-list-private.h" #include "quota-private.h" struct imapc_quota_refresh_root { const char *name; unsigned int order; uint64_t bytes_cur, count_cur; uint64_t bytes_limit, count_limit; }; struct imapc_quota_refresh { pool_t pool; const char *box_name; ARRAY(struct imapc_quota_refresh_root) roots; }; struct imapc_quota_root { struct quota_root root; const char *box_name, *root_name; struct mail_namespace *imapc_ns; struct imapc_storage_client *client; bool initialized; uint64_t bytes_last, count_last; struct timeval last_refresh; struct imapc_quota_refresh refresh; }; extern struct quota_backend quota_backend_imapc; static struct quota_root *imapc_quota_alloc(void) { struct imapc_quota_root *root; root = i_new(struct imapc_quota_root, 1); return &root->root; } static void handle_box_param(struct quota_root *_root, const char *param_value) { ((struct imapc_quota_root *)_root)->box_name = p_strdup(_root->pool, param_value); } static void handle_root_param(struct quota_root *_root, const char *param_value) { ((struct imapc_quota_root *)_root)->root_name = p_strdup(_root->pool, param_value); } static int imapc_quota_init(struct quota_root *_root, const char *args, const char **error_r) { struct imapc_quota_root *root = (struct imapc_quota_root *)_root; const struct quota_param_parser imapc_params[] = { {.param_name = "box=", .param_handler = handle_box_param}, {.param_name = "root=", .param_handler = handle_root_param}, quota_param_ns, {.param_name = NULL} }; if (quota_parse_parameters(_root, &args, error_r, imapc_params, TRUE) < 0) return -1; if (root->box_name == NULL && root->root_name == NULL) root->box_name = "INBOX"; _root->auto_updating = TRUE; /* we'll never try to enforce the quota - it's just a lot of unnecessary remote GETQUOTA calls. */ _root->no_enforcing = TRUE; return 0; } static void imapc_quota_deinit(struct quota_root *_root) { i_free(_root); } static void imapc_quota_root_namespace_added(struct quota_root *_root, struct mail_namespace *ns) { struct imapc_quota_root *root = (struct imapc_quota_root *)_root; if (root->imapc_ns == NULL) root->imapc_ns = ns; } static struct imapc_quota_refresh * imapc_quota_root_refresh_find(struct imapc_storage_client *client) { struct imapc_storage *storage = client->_storage; struct quota *quota; struct quota_root *const *rootp; i_assert(storage != NULL); quota = quota_get_mail_user_quota(storage->storage.user); i_assert(quota != NULL); /* find the quota root that is being refreshed */ array_foreach("a->roots, rootp) { if ((*rootp)->backend.name == quota_backend_imapc.name) { struct imapc_quota_root *root = (struct imapc_quota_root *)*rootp; if (root->refresh.pool != NULL) return &root->refresh; } } return NULL; } static struct imapc_quota_refresh_root * imapc_quota_refresh_root_get(struct imapc_quota_refresh *refresh, const char *root_name) { struct imapc_quota_refresh_root *refresh_root; array_foreach_modifiable(&refresh->roots, refresh_root) { if (strcmp(refresh_root->name, root_name) == 0) return refresh_root; } refresh_root = array_append_space(&refresh->roots); refresh_root->order = UINT_MAX; refresh_root->name = p_strdup(refresh->pool, root_name); refresh_root->bytes_limit = (uint64_t)-1; refresh_root->count_limit = (uint64_t)-1; return refresh_root; } static void imapc_untagged_quotaroot(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { struct imapc_quota_refresh *refresh; struct imapc_quota_refresh_root *refresh_root; const char *mailbox_name, *root_name; unsigned int i; if (!imap_arg_get_astring(&reply->args[0], &mailbox_name)) return; if ((refresh = imapc_quota_root_refresh_find(client)) == NULL || refresh->box_name == NULL || strcmp(refresh->box_name, mailbox_name) != 0) { /* unsolicited QUOTAROOT reply - ignore */ return; } if (array_count(&refresh->roots) > 0) { /* duplicate QUOTAROOT reply - ignore */ return; } i = 1; while (imap_arg_get_astring(&reply->args[i], &root_name)) { refresh_root = imapc_quota_refresh_root_get(refresh, root_name); refresh_root->order = i; i++; } } static void imapc_untagged_quota(const struct imapc_untagged_reply *reply, struct imapc_storage_client *client) { const struct imap_arg *list; struct imapc_quota_refresh *refresh; struct imapc_quota_refresh_root *refresh_root; const char *root_name, *resource, *value_str, *limit_str; uint64_t value, limit; unsigned int i; if (!imap_arg_get_astring(&reply->args[0], &root_name) || !imap_arg_get_list(&reply->args[1], &list)) return; if ((refresh = imapc_quota_root_refresh_find(client)) == NULL) { /* unsolicited QUOTA reply - ignore */ return; } refresh_root = imapc_quota_refresh_root_get(refresh, root_name); for (i = 0; list[i].type != IMAP_ARG_EOL; i += 3) { if (!imap_arg_get_atom(&list[i], &resource) || !imap_arg_get_atom(&list[i+1], &value_str) || !imap_arg_get_atom(&list[i+2], &limit_str) || /* RFC2087 uses 32bit number, but be ready for future */ str_to_uint64(value_str, &value) < 0 || str_to_uint64(limit_str, &limit) < 0) return; if (strcasecmp(resource, QUOTA_NAME_STORAGE_KILOBYTES) == 0) { refresh_root->bytes_cur = value * 1024; refresh_root->bytes_limit = limit * 1024; } else if (strcasecmp(resource, QUOTA_NAME_MESSAGES) == 0) { refresh_root->count_cur = value; refresh_root->count_limit = limit; } } } static bool imapc_quota_client_init(struct imapc_quota_root *root) { struct mailbox_list *list; struct mail_storage *storage; if (root->initialized) return root->client != NULL; root->initialized = TRUE; list = root->imapc_ns->list; if (mailbox_list_get_storage(&list, "", &storage) == 0 && strcmp(storage->name, IMAPC_STORAGE_NAME) != 0) { /* non-imapc namespace, skip */ if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) == 0) { i_warning("quota: Namespace '%s' is not imapc, " "skipping for imapc quota", root->imapc_ns->prefix); } return FALSE; } root->client = ((struct imapc_storage *)storage)->client; imapc_storage_client_register_untagged(root->client, "QUOTAROOT", imapc_untagged_quotaroot); imapc_storage_client_register_untagged(root->client, "QUOTA", imapc_untagged_quota); return TRUE; } static void imapc_quota_refresh_init(struct imapc_quota_refresh *refresh) { i_assert(refresh->pool == NULL); refresh->pool = pool_alloconly_create("imapc quota refresh", 256); p_array_init(&refresh->roots, refresh->pool, 4); } static void imapc_quota_refresh_update(struct quota *quota, struct imapc_quota_refresh *refresh) { struct quota_root *const *rootp; const struct imapc_quota_refresh_root *refresh_root; if (array_count(&refresh->roots) == 0) { i_error("quota: imapc didn't return any QUOTA results"); return; } /* use the first quota root for everything */ refresh_root = array_idx(&refresh->roots, 0); array_foreach("a->roots, rootp) { if ((*rootp)->backend.name == quota_backend_imapc.name) { struct imapc_quota_root *root = (struct imapc_quota_root *)*rootp; root->bytes_last = refresh_root->bytes_cur; root->count_last = refresh_root->count_cur; root->root.bytes_limit = refresh_root->bytes_limit; root->root.count_limit = refresh_root->count_limit; } } } static void imapc_quota_refresh_deinit(struct quota *quota, struct imapc_quota_refresh *refresh, bool success) { if (success) imapc_quota_refresh_update(quota, refresh); pool_unref(&refresh->pool); i_zero(refresh); } static int imapc_quota_refresh_root_order_cmp(const struct imapc_quota_refresh_root *root1, const struct imapc_quota_refresh_root *root2) { if (root1->order < root2->order) return -1; else if (root1->order > root2->order) return 1; else return 0; } static int imapc_quota_refresh_mailbox(struct imapc_quota_root *root) { struct imapc_simple_context sctx; struct imapc_command *cmd; i_assert(root->box_name != NULL); /* ask quotas for the configured mailbox */ imapc_quota_refresh_init(&root->refresh); root->refresh.box_name = root->box_name; imapc_simple_context_init(&sctx, root->client); cmd = imapc_client_cmd(root->client->client, imapc_simple_callback, &sctx); imapc_command_sendf(cmd, "GETQUOTAROOT %s", root->box_name); imapc_simple_run(&sctx); /* if there are multiple quota roots, use the first one returned by the QUOTAROOT */ array_sort(&root->refresh.roots, imapc_quota_refresh_root_order_cmp); imapc_quota_refresh_deinit(root->root.quota, &root->refresh, sctx.ret == 0); return sctx.ret; } static int imapc_quota_refresh_root(struct imapc_quota_root *root) { struct imapc_simple_context sctx; struct imapc_command *cmd; i_assert(root->root_name != NULL); /* ask quotas for the configured quota root */ imapc_quota_refresh_init(&root->refresh); imapc_simple_context_init(&sctx, root->client); cmd = imapc_client_cmd(root->client->client, imapc_simple_callback, &sctx); imapc_command_sendf(cmd, "GETQUOTA %s", root->root_name); imapc_simple_run(&sctx); /* there shouldn't be more than one QUOTA reply, but ignore anyway anything we didn't expect. */ while (array_count(&root->refresh.roots) > 0) { const struct imapc_quota_refresh_root *refresh_root = array_idx(&root->refresh.roots, 0); if (strcmp(refresh_root->name, root->root_name) == 0) break; array_delete(&root->refresh.roots, 0, 1); } imapc_quota_refresh_deinit(root->root.quota, &root->refresh, sctx.ret == 0); return sctx.ret; } static int imapc_quota_refresh(struct imapc_quota_root *root) { enum imapc_capability capa; int ret; if (root->imapc_ns == NULL) { /* imapc namespace is missing - disable this quota backend */ return 0; } if (root->last_refresh.tv_sec == ioloop_timeval.tv_sec && root->last_refresh.tv_usec == ioloop_timeval.tv_usec) return 0; if (!imapc_quota_client_init(root)) return 0; if (imapc_client_get_capabilities(root->client->client, &capa) < 0) return -1; if ((capa & IMAPC_CAPABILITY_QUOTA) == 0) { /* no QUOTA capability - disable quota */ i_warning("quota: Remote IMAP server doesn't support QUOTA - disabling"); root->client = NULL; return 0; } if (root->root_name == NULL) ret = imapc_quota_refresh_mailbox(root); else ret = imapc_quota_refresh_root(root); /* set the last_refresh only after the refresh, because it changes ioloop_timeval. */ root->last_refresh = ioloop_timeval; return ret; } static int imapc_quota_init_limits(struct quota_root *_root) { struct imapc_quota_root *root = (struct imapc_quota_root *)_root; return imapc_quota_refresh(root); } static void imapc_quota_namespace_added(struct quota *quota, struct mail_namespace *ns) { struct quota_root **roots; unsigned int i, count; roots = array_get_modifiable("a->roots, &count); for (i = 0; i < count; i++) { if (roots[i]->backend.name == quota_backend_imapc.name && ((roots[i]->ns_prefix == NULL && ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) || roots[i]->ns == ns)) imapc_quota_root_namespace_added(roots[i], ns); } } static const char *const * imapc_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources_both[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return resources_both; } static int imapc_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct imapc_quota_root *root = (struct imapc_quota_root *)_root; if (imapc_quota_refresh(root) < 0) return -1; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) *value_r = root->bytes_last; else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) *value_r = root->count_last; else return 0; return 1; } static int imapc_quota_update(struct quota_root *root ATTR_UNUSED, struct quota_transaction_context *ctx ATTR_UNUSED) { return 0; } struct quota_backend quota_backend_imapc = { "imapc", { imapc_quota_alloc, imapc_quota_init, imapc_quota_deinit, NULL, imapc_quota_init_limits, imapc_quota_namespace_added, imapc_quota_root_get_resources, imapc_quota_get_resource, imapc_quota_update, NULL, NULL } }; dovecot-2.2.33.2/src/plugins/quota/quota-status.c0000644000175000017500000001700713165463624016563 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream.h" #include "connection.h" #include "restrict-access.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-settings.h" #include "mail-storage-service.h" #include "message-address.h" #include "quota-private.h" #include "quota-plugin.h" #include "quota-status-settings.h" enum quota_protocol { QUOTA_PROTOCOL_UNKNOWN = 0, QUOTA_PROTOCOL_POSTFIX }; struct quota_client { struct connection conn; char *recipient; uoff_t size; }; static struct quota_status_settings *quota_status_settings; static pool_t quota_status_pool; static enum quota_protocol protocol; static struct mail_storage_service_ctx *storage_service; static struct connection_list *clients; static char *nouser_reply; static void client_connected(struct master_service_connection *conn) { struct quota_client *client; client = i_new(struct quota_client, 1); connection_init_server(clients, &client->conn, "(quota client)", conn->fd, conn->fd); master_service_client_connection_accept(conn); } static void client_reset(struct quota_client *client) { i_free_and_null(client->recipient); } static enum quota_alloc_result quota_check(struct mail_user *user, uoff_t mail_size, const char **error_r) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct mail_namespace *ns; struct mailbox *box; struct quota_transaction_context *ctx; enum quota_alloc_result ret; if (quser == NULL) { /* no quota for user */ return QUOTA_ALLOC_RESULT_OK; } ns = mail_namespace_find_inbox(user->namespaces); box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_POST_SESSION); mailbox_set_reason(box, "quota status"); ctx = quota_transaction_begin(box); ret = quota_test_alloc(ctx, I_MAX(1, mail_size)); *error_r = quota_alloc_result_errstr(ret, ctx); quota_transaction_rollback(&ctx); mailbox_free(&box); return ret; } static void client_handle_request(struct quota_client *client) { struct mail_storage_service_input input; struct mail_storage_service_user *service_user; struct mail_user *user; const char *value = NULL, *error; const char *detail ATTR_UNUSED; int ret; if (client->recipient == NULL) { o_stream_send_str(client->conn.output, "action=DUNNO\n\n"); return; } i_zero(&input); message_detail_address_parse(quota_status_settings->recipient_delimiter, client->recipient, &input.username, &detail); ret = mail_storage_service_lookup_next(storage_service, &input, &service_user, &user, &error); restrict_access_allow_coredumps(TRUE); if (ret == 0) { value = nouser_reply; } else if (ret > 0) { enum quota_alloc_result qret = quota_check(user, client->size, &error); switch (qret) { case QUOTA_ALLOC_RESULT_OK: /* under quota */ value = mail_user_plugin_getenv(user, "quota_status_success"); if (value == NULL) value = "OK"; break; case QUOTA_ALLOC_RESULT_OVER_MAXSIZE: /* even over maximum quota */ case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT: value = mail_user_plugin_getenv(user, "quota_status_toolarge"); /* fall through */ case QUOTA_ALLOC_RESULT_OVER_QUOTA: if (value == NULL) value = mail_user_plugin_getenv(user, "quota_status_overquota"); if (value == NULL) value = t_strdup_printf("554 5.2.2 %s", error); break; case QUOTA_ALLOC_RESULT_TEMPFAIL: ret = -1; break; } value = t_strdup(value); /* user's pool is being freed */ mail_user_unref(&user); mail_storage_service_user_unref(&service_user); } if (ret < 0) { /* temporary failure */ o_stream_send_str(client->conn.output, t_strdup_printf( "action=DEFER_IF_PERMIT %s\n\n", error)); } else { o_stream_send_str(client->conn.output, t_strdup_printf("action=%s\n\n", value)); } } static int client_input_line(struct connection *conn, const char *line) { struct quota_client *client = (struct quota_client *)conn; if (*line == '\0') { o_stream_cork(conn->output); client_handle_request(client); o_stream_uncork(conn->output); client_reset(client); return 1; } if (client->recipient == NULL && strncmp(line, "recipient=", 10) == 0) client->recipient = i_strdup(line + 10); else if (strncmp(line, "size=", 5) == 0) { if (str_to_uoff(line+5, &client->size) < 0) client->size = 0; } return 1; } static void client_destroy(struct connection *conn) { struct quota_client *client = (struct quota_client *)conn; connection_deinit(&client->conn); client_reset(client); i_free(client); master_service_client_connection_destroyed(master_service); } static struct connection_settings client_set = { .input_max_size = (size_t)-1, .output_max_size = (size_t)-1, .client = FALSE }; static const struct connection_vfuncs client_vfuncs = { .destroy = client_destroy, .input_line = client_input_line }; static void main_preinit(void) { restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } static void main_init(void) { static const struct setting_parser_info *set_roots[] = { "a_status_setting_parser_info, NULL }; struct mail_storage_service_input input; const struct setting_parser_info *user_info; const struct setting_parser_context *set_parser; const struct mail_user_settings *user_set; const struct quota_status_settings *set; const char *value, *error; pool_t pool; clients = connection_list_init(&client_set, &client_vfuncs); storage_service = mail_storage_service_init(master_service, set_roots, MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP | MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS | MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR); i_zero(&input); input.service = "quota-status"; input.module = "mail"; input.username = ""; quota_status_pool = pool_alloconly_create("quota status settings", 512); pool = pool_alloconly_create("service all settings", 4096); if (mail_storage_service_read_settings(storage_service, &input, pool, &user_info, &set_parser, &error) < 0) i_fatal("%s", error); user_set = master_service_settings_parser_get_others(master_service, set_parser)[0]; set = master_service_settings_get_others(master_service)[1]; quota_status_settings = settings_dup("a_status_setting_parser_info, set, quota_status_pool); value = mail_user_set_plugin_getenv(user_set, "quota_status_nouser"); nouser_reply = p_strdup(quota_status_pool, value != NULL ? value : "REJECT Unknown user"); pool_unref(&pool); } static void main_deinit(void) { pool_unref("a_status_pool); connection_list_deinit(&clients); mail_storage_service_deinit(&storage_service); } int main(int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; int c; protocol = QUOTA_PROTOCOL_UNKNOWN; master_service = master_service_init("quota-status", service_flags, &argc, &argv, "p:"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'p': if (strcmp(optarg, "postfix") == 0) protocol = QUOTA_PROTOCOL_POSTFIX; else i_fatal("Unknown -p parameter: '%s'", optarg); break; default: return FATAL_DEFAULT; } } if (protocol == QUOTA_PROTOCOL_UNKNOWN) i_fatal("Missing -p parameter"); master_service_init_log(master_service, "quota-status: "); main_preinit(); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/plugins/quota/quota-util.c0000644000175000017500000003161413147010712016177 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "wildcard-match.h" #include "quota-private.h" #include #define QUOTA_DEFAULT_GRACE "10%" #define RULE_NAME_DEFAULT_FORCE "*" #define RULE_NAME_DEFAULT_NONFORCE "?" struct quota_rule * quota_root_rule_find(struct quota_root_settings *root_set, const char *name) { struct quota_rule *rule; array_foreach_modifiable(&root_set->rules, rule) { if (wildcard_match(name, rule->mailbox_mask)) return rule; } return NULL; } static struct quota_rule * quota_root_rule_find_exact(struct quota_root_settings *root_set, const char *name) { struct quota_rule *rule; array_foreach_modifiable(&root_set->rules, rule) { if (strcmp(rule->mailbox_mask, name) == 0) return rule; } return NULL; } static int quota_rule_parse_percentage(struct quota_root_settings *root_set, struct quota_rule *rule, int64_t *limit, const char **error_r) { int64_t percentage = *limit; if (percentage <= -100 || percentage >= UINT_MAX) { *error_r = "Invalid percentage"; return -1; } if (rule == &root_set->default_rule) { *error_r = "Default rule can't be a percentage"; return -1; } if (limit == &rule->bytes_limit) rule->bytes_percent = percentage; else if (limit == &rule->count_limit) rule->count_percent = percentage; else i_unreached(); return 0; } static int quota_limit_parse(struct quota_root_settings *root_set, struct quota_rule *rule, const char *unit, uint64_t multiply, int64_t *limit, const char **error_r) { switch (i_toupper(*unit)) { case '\0': /* default */ break; case 'B': multiply = 1; break; case 'K': multiply = 1024; break; case 'M': multiply = 1024*1024; break; case 'G': multiply = 1024*1024*1024; break; case 'T': multiply = 1024ULL*1024*1024*1024; break; case '%': multiply = 0; if (quota_rule_parse_percentage(root_set, rule, limit, error_r) < 0) return -1; break; default: *error_r = t_strdup_printf("Unknown unit: %s", unit); return -1; } *limit *= multiply; return 0; } static void quota_rule_recalculate_relative_rules(struct quota_rule *rule, int64_t bytes_limit, int64_t count_limit) { if (rule->bytes_percent != 0) rule->bytes_limit = bytes_limit * rule->bytes_percent / 100; if (rule->count_percent != 0) rule->count_limit = count_limit * rule->count_percent / 100; } void quota_root_recalculate_relative_rules(struct quota_root_settings *root_set, int64_t bytes_limit, int64_t count_limit) { struct quota_rule *rule; struct quota_warning_rule *warning_rule; array_foreach_modifiable(&root_set->rules, rule) { quota_rule_recalculate_relative_rules(rule, bytes_limit, count_limit); } array_foreach_modifiable(&root_set->warning_rules, warning_rule) { quota_rule_recalculate_relative_rules(&warning_rule->rule, bytes_limit, count_limit); } quota_rule_recalculate_relative_rules(&root_set->grace_rule, bytes_limit, 0); root_set->last_mail_max_extra_bytes = root_set->grace_rule.bytes_limit; if (root_set->set->debug && root_set->set->initialized) { i_debug("Quota root %s: Recalculated relative rules with " "bytes=%lld count=%lld. Now grace=%llu", root_set->name, (long long)bytes_limit, (long long)count_limit, (unsigned long long)root_set->last_mail_max_extra_bytes); } } static int quota_rule_parse_limits(struct quota_root_settings *root_set, struct quota_rule *rule, const char *limits, const char *full_rule_def, bool relative_rule, const char **error_r) { const char **args, *key, *value, *error, *p; uint64_t multiply; int64_t *limit; args = t_strsplit(limits, ":"); for (; *args != NULL; args++) { multiply = 1; limit = NULL; key = *args; value = strchr(key, '='); if (value == NULL) value = ""; else key = t_strdup_until(key, value++); if (*value == '+') { if (!relative_rule) { *error_r = "Rule limit cannot have '+'"; return -1; } value++; } else if (*value != '-' && relative_rule) { i_warning("quota root %s rule %s: " "obsolete configuration for rule '%s' " "should be changed to '%s=+%s'", root_set->name, full_rule_def, *args, key, value); } if (strcmp(key, "storage") == 0) { multiply = 1024; limit = &rule->bytes_limit; if (str_parse_int64(value, limit, &p) < 0) { *error_r = p_strdup_printf(root_set->set->pool, "Invalid storage limit: %s", value); return -1; } } else if (strcmp(key, "bytes") == 0) { limit = &rule->bytes_limit; if (str_parse_int64(value, limit, &p) < 0) { *error_r = p_strdup_printf(root_set->set->pool, "Invalid bytes limit: %s", value); return -1; } } else if (strcmp(key, "messages") == 0) { limit = &rule->count_limit; if (str_parse_int64(value, limit, &p) < 0) { *error_r = p_strdup_printf(root_set->set->pool, "Invalid bytes messages: %s", value); return -1; } } else { *error_r = p_strdup_printf(root_set->set->pool, "Unknown rule limit name: %s", key); return -1; } if (quota_limit_parse(root_set, rule, p, multiply, limit, &error) < 0) { *error_r = p_strdup_printf(root_set->set->pool, "Invalid rule limit value '%s': %s", *args, error); return -1; } } if (!relative_rule) { if (rule->bytes_limit < 0) { *error_r = "Bytes limit can't be negative"; return -1; } if (rule->count_limit < 0) { *error_r = "Count limit can't be negative"; return -1; } } return 0; } int quota_root_add_rule(struct quota_root_settings *root_set, const char *rule_def, const char **error_r) { struct quota_rule *rule; const char *p, *mailbox_mask; int ret = 0; p = strchr(rule_def, ':'); if (p == NULL) { *error_r = "Invalid rule"; return -1; } /* : */ mailbox_mask = t_strdup_until(rule_def, p++); rule = quota_root_rule_find_exact(root_set, mailbox_mask); if (rule == NULL) { if (strcmp(mailbox_mask, RULE_NAME_DEFAULT_NONFORCE) == 0) rule = &root_set->default_rule; else if (strcmp(mailbox_mask, RULE_NAME_DEFAULT_FORCE) == 0) { rule = &root_set->default_rule; root_set->force_default_rule = TRUE; } else { rule = array_append_space(&root_set->rules); rule->mailbox_mask = strcasecmp(mailbox_mask, "INBOX") == 0 ? "INBOX" : p_strdup(root_set->set->pool, mailbox_mask); } } if (strcmp(p, "ignore") == 0) { rule->ignore = TRUE; if (root_set->set->debug) { i_debug("Quota rule: root=%s mailbox=%s ignored", root_set->name, mailbox_mask); } return 0; } if (strncmp(p, "backend=", 8) == 0) { if (root_set->backend->v.parse_rule == NULL) { *error_r = "backend rule not supported"; ret = -1; } else if (!root_set->backend->v.parse_rule(root_set, rule, p + 8, error_r)) ret = -1; } else { bool relative_rule = rule != &root_set->default_rule; if (quota_rule_parse_limits(root_set, rule, p, rule_def, relative_rule, error_r) < 0) ret = -1; } quota_root_recalculate_relative_rules(root_set, root_set->default_rule.bytes_limit, root_set->default_rule.count_limit); if (root_set->set->debug) { const char *rule_plus = rule == &root_set->default_rule ? "" : "+"; i_debug("Quota rule: root=%s mailbox=%s " "bytes=%s%lld%s messages=%s%lld%s", root_set->name, mailbox_mask, rule->bytes_limit > 0 ? rule_plus : "", (long long)rule->bytes_limit, rule->bytes_percent == 0 ? "" : t_strdup_printf(" (%u%%)", rule->bytes_percent), rule->count_limit > 0 ? rule_plus : "", (long long)rule->count_limit, rule->count_percent == 0 ? "" : t_strdup_printf(" (%u%%)", rule->count_percent)); } return ret; } int quota_root_add_warning_rule(struct quota_root_settings *root_set, const char *rule_def, const char **error_r) { struct quota_warning_rule *warning; struct quota_rule rule; const char *p, *q; int ret; bool reverse = FALSE; p = strchr(rule_def, ' '); if (p == NULL || p[1] == '\0') { *error_r = "No command specified"; return -1; } if (*rule_def == '+') { /* warn when exceeding quota */ q = rule_def+1; } else if (*rule_def == '-') { /* warn when going below quota */ q = rule_def+1; reverse = TRUE; } else { /* default: same as '+' */ q = rule_def; } i_zero(&rule); ret = quota_rule_parse_limits(root_set, &rule, t_strdup_until(q, p), rule_def, FALSE, error_r); if (ret < 0) return -1; warning = array_append_space(&root_set->warning_rules); warning->command = p_strdup(root_set->set->pool, p+1); warning->rule = rule; warning->reverse = reverse; if (reverse) root_set->have_reverse_warnings = TRUE; quota_root_recalculate_relative_rules(root_set, root_set->default_rule.bytes_limit, root_set->default_rule.count_limit); if (root_set->set->debug) { i_debug("Quota warning: bytes=%llu%s " "messages=%llu%s reverse=%s command=%s", (unsigned long long)warning->rule.bytes_limit, warning->rule.bytes_percent == 0 ? "" : t_strdup_printf(" (%u%%)", warning->rule.bytes_percent), (unsigned long long)warning->rule.count_limit, warning->rule.count_percent == 0 ? "" : t_strdup_printf(" (%u%%)", warning->rule.count_percent), warning->reverse ? "yes" : "no", warning->command); } return 0; } int quota_root_parse_grace(struct quota_root_settings *root_set, const char *value, const char **error_r) { const char *p; if (value == NULL) { /* default */ value = QUOTA_DEFAULT_GRACE; } if (str_parse_int64(value, &root_set->grace_rule.bytes_limit, &p) < 0) return -1; if (quota_limit_parse(root_set, &root_set->grace_rule, p, 1, &root_set->grace_rule.bytes_limit, error_r) < 0) return -1; quota_rule_recalculate_relative_rules(&root_set->grace_rule, root_set->default_rule.bytes_limit, 0); root_set->last_mail_max_extra_bytes = root_set->grace_rule.bytes_limit; if (root_set->set->debug) { i_debug("Quota grace: root=%s bytes=%lld%s", root_set->name, (long long)root_set->grace_rule.bytes_limit, root_set->grace_rule.bytes_percent == 0 ? "" : t_strdup_printf(" (%u%%)", root_set->grace_rule.bytes_percent)); } return 0; } bool quota_warning_match(const struct quota_warning_rule *w, uint64_t bytes_before, uint64_t bytes_current, uint64_t count_before, uint64_t count_current, const char **reason_r) { #define QUOTA_EXCEEDED(before, current, limit) \ ((before) < (uint64_t)(limit) && (current) >= (uint64_t)(limit)) if (!w->reverse) { /* over quota (default) */ if (QUOTA_EXCEEDED(bytes_before, bytes_current, w->rule.bytes_limit)) { *reason_r = t_strdup_printf("bytes=%llu -> %llu over limit %llu", (unsigned long long)bytes_before, (unsigned long long)bytes_current, (unsigned long long)w->rule.bytes_limit); return TRUE; } if (QUOTA_EXCEEDED(count_before, count_current, w->rule.count_limit)) { *reason_r = t_strdup_printf("count=%llu -> %llu over limit %llu", (unsigned long long)count_before, (unsigned long long)count_current, (unsigned long long)w->rule.count_limit); return TRUE; } } else { if (QUOTA_EXCEEDED(bytes_current, bytes_before, w->rule.bytes_limit)) { *reason_r = t_strdup_printf("bytes=%llu -> %llu below limit %llu", (unsigned long long)bytes_before, (unsigned long long)bytes_current, (unsigned long long)w->rule.bytes_limit); return TRUE; } if (QUOTA_EXCEEDED(count_current, count_before, w->rule.count_limit)) { *reason_r = t_strdup_printf("count=%llu -> %llu below limit %llu", (unsigned long long)count_before, (unsigned long long)count_current, (unsigned long long)w->rule.count_limit); return TRUE; } } return FALSE; } bool quota_transaction_is_over(struct quota_transaction_context *ctx, uoff_t size) { if (ctx->count_used < 0) { /* we've deleted some messages. we should be ok, unless we were already over quota and still are after these deletions. */ const uint64_t count_deleted = (uint64_t)-ctx->count_used; if (ctx->count_over > 0) { if (count_deleted - 1 < ctx->count_over) return TRUE; } } else { if (ctx->count_ceil < 1 || ctx->count_ceil - 1 < (uint64_t)ctx->count_used) { /* count limit reached */ return TRUE; } } if (ctx->bytes_used < 0) { const uint64_t bytes_deleted = (uint64_t)-ctx->bytes_used; /* we've deleted some messages. same logic as above. */ if (ctx->bytes_over > 0) { if (ctx->bytes_over > bytes_deleted) { /* even after deletions we're over quota */ return TRUE; } if (size > bytes_deleted - ctx->bytes_over) return TRUE; } else { if (size > bytes_deleted && size - bytes_deleted < ctx->bytes_ceil) return TRUE; } } else if (size == 0) { /* we need to explicitly test this case, since the generic check would fail if user is already over quota */ if (ctx->bytes_over > 0) return TRUE; } else { if (ctx->bytes_ceil < size || ctx->bytes_ceil - size < (uint64_t)ctx->bytes_used) { /* bytes limit reached */ return TRUE; } } return FALSE; } dovecot-2.2.33.2/src/plugins/quota/quota-maildir.c0000644000175000017500000005363613165463624016671 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "nfs-workarounds.h" #include "safe-mkstemp.h" #include "mkdir-parents.h" #include "read-full.h" #include "write-full.h" #include "str.h" #include "maildir-storage.h" #include "mailbox-list-private.h" #include "quota-private.h" #include #include #include #define MAILDIRSIZE_FILENAME "maildirsize" #define MAILDIRSIZE_STALE_SECS (60*15) struct maildir_quota_root { struct quota_root root; struct mail_namespace *maildirsize_ns; const char *maildirsize_path; uint64_t total_bytes; uint64_t total_count; int fd; time_t recalc_last_stamp; off_t last_size; unsigned int limits_initialized:1; }; struct maildir_list_context { struct mailbox_list *list; struct maildir_quota_root *root; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; string_t *path; int state; }; extern struct quota_backend quota_backend_maildir; static struct dotlock_settings dotlock_settings = { .timeout = 0, .stale_timeout = 30 }; static int maildir_sum_dir(const char *dir, uint64_t *total_bytes, uint64_t *total_count) { DIR *dirp; struct dirent *dp; string_t *path; const char *p; size_t len; uoff_t num; int ret = 0; dirp = opendir(dir); if (dirp == NULL) { if (errno == ENOENT || errno == ESTALE) return 0; i_error("opendir(%s) failed: %m", dir); return -1; } path = t_str_new(256); str_append(path, dir); str_append_c(path, '/'); len = str_len(path); while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || dp->d_name[1] == '.')) continue; p = strstr(dp->d_name, ",S="); num = (uoff_t)-1; if (p != NULL) { /* ,S=nnnn[:,] */ p += 3; for (num = 0; *p >= '0' && *p <= '9'; p++) num = num * 10 + (*p - '0'); if (*p != ':' && *p != '\0' && *p != ',') { /* not in expected format, fallback to stat() */ num = (uoff_t)-1; } else { *total_bytes += num; *total_count += 1; } } if (num == (uoff_t)-1) { struct stat st; str_truncate(path, len); str_append(path, dp->d_name); if (stat(str_c(path), &st) == 0) { *total_bytes += st.st_size; *total_count += 1; } else if (errno != ENOENT && errno != ESTALE) { i_error("stat(%s) failed: %m", str_c(path)); ret = -1; } } } if (closedir(dirp) < 0) { i_error("closedir(%s) failed: %m", dir); return -1; } return ret; } static struct maildir_list_context * maildir_list_init(struct maildir_quota_root *root, struct mailbox_list *list) { struct maildir_list_context *ctx; ctx = i_new(struct maildir_list_context, 1); ctx->root = root; ctx->path = str_new(default_pool, 512); ctx->list = list; ctx->iter = mailbox_list_iter_init(list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); return ctx; } static bool maildir_set_next_path(struct maildir_list_context *ctx) { T_BEGIN { const char *path, *storage_name; str_truncate(ctx->path, 0); storage_name = mailbox_list_get_storage_name( ctx->info->ns->list, ctx->info->vname); if (mailbox_list_get_path(ctx->list, storage_name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) > 0) { str_append(ctx->path, path); str_append(ctx->path, ctx->state == 0 ? "/new" : "/cur"); } } T_END; return str_len(ctx->path) > 0; } static const char * maildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r) { struct quota_rule *rule; struct stat st; for (;;) { if (ctx->state == 0) { ctx->info = mailbox_list_iter_next(ctx->iter); if (ctx->info == NULL) return NULL; rule = quota_root_rule_find(ctx->root->root.set, ctx->info->vname); if (rule != NULL && rule->ignore) { /* mailbox not included in quota */ continue; } } if (!maildir_set_next_path(ctx)) { ctx->state = 0; continue; } if (++ctx->state == 2) ctx->state = 0; if (stat(str_c(ctx->path), &st) == 0) break; /* ignore if the directory got lost, stale or if it was actually a file and not a directory */ if (errno != ENOENT && errno != ESTALE && errno != ENOTDIR) { i_error("stat(%s) failed: %m", str_c(ctx->path)); ctx->state = 0; } } *mtime_r = st.st_mtime; return str_c(ctx->path); } static int maildir_list_deinit(struct maildir_list_context *ctx) { int ret = mailbox_list_iter_deinit(&ctx->iter); str_free(&ctx->path); i_free(ctx); return ret; } static int maildirs_check_have_changed(struct maildir_quota_root *root, struct mail_namespace *ns, time_t latest_mtime) { struct maildir_list_context *ctx; time_t mtime; int ret = 0; ctx = maildir_list_init(root, ns->list); while (maildir_list_next(ctx, &mtime) != NULL) { if (mtime > latest_mtime) { ret = 1; break; } } if (maildir_list_deinit(ctx) < 0) return -1; return ret; } static int maildirsize_write(struct maildir_quota_root *root, const char *path) { const struct mail_storage_settings *set = root->maildirsize_ns->mail_set; struct quota_root *_root = &root->root; struct mail_namespace *const *namespaces; unsigned int i, count; struct mailbox_permissions perm; const char *p, *dir; string_t *str, *temp_path; int fd; i_assert(root->fd == -1); /* figure out what permissions we should use for maildirsize. use the inbox namespace's permissions if possible. */ perm.file_create_mode = 0600; perm.dir_create_mode = 0700; perm.file_create_gid = (gid_t)-1; perm.file_create_gid_origin = "default"; namespaces = array_get(&root->root.quota->namespaces, &count); i_assert(count > 0); for (i = 0; i < count; i++) { if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX_USER) == 0) continue; mailbox_list_get_root_permissions(namespaces[i]->list, &perm); break; } dotlock_settings.use_excl_lock = set->dotlock_use_excl; dotlock_settings.nfs_flush = set->mail_nfs_storage; temp_path = t_str_new(128); str_append(temp_path, path); fd = safe_mkstemp_hostpid_group(temp_path, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); if (fd == -1 && errno == ENOENT) { /* the control directory doesn't exist yet? create it */ p = strrchr(path, '/'); dir = t_strdup_until(path, p); if (mkdir_parents_chgrp(dir, perm.dir_create_mode, perm.file_create_gid, perm.file_create_gid_origin) < 0 && errno != EEXIST) { i_error("mkdir_parents(%s) failed: %m", dir); return -1; } fd = safe_mkstemp_hostpid_group(temp_path, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); } if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", path); return -1; } str = t_str_new(128); /* if we have no limits, write 0S instead of an empty line */ if (_root->bytes_limit != 0 || _root->count_limit == 0) { str_printfa(str, "%lluS", (unsigned long long)_root->bytes_limit); } if (_root->count_limit != 0) { if (str_len(str) > 0) str_append_c(str, ','); str_printfa(str, "%lluC", (unsigned long long)_root->count_limit); } str_printfa(str, "\n%llu %llu\n", (unsigned long long)root->total_bytes, (unsigned long long)root->total_count); if (write_full(fd, str_data(str), str_len(str)) < 0) { i_error("write_full(%s) failed: %m", str_c(temp_path)); i_close_fd(&fd); i_unlink(str_c(temp_path)); return -1; } i_close_fd(&fd); if (rename(str_c(temp_path), path) < 0) { i_error("rename(%s, %s) failed: %m", str_c(temp_path), path); i_unlink_if_exists(str_c(temp_path)); return -1; } return 0; } static void maildirsize_recalculate_init(struct maildir_quota_root *root) { root->total_bytes = root->total_count = 0; root->recalc_last_stamp = 0; } static int maildirsize_recalculate_namespace(struct maildir_quota_root *root, struct mail_namespace *ns) { struct maildir_list_context *ctx; const char *dir; time_t mtime; int ret = 0; ctx = maildir_list_init(root, ns->list); while ((dir = maildir_list_next(ctx, &mtime)) != NULL) { if (mtime > root->recalc_last_stamp) root->recalc_last_stamp = mtime; T_BEGIN { if (maildir_sum_dir(dir, &root->total_bytes, &root->total_count) < 0) ret = -1; } T_END; } if (maildir_list_deinit(ctx) < 0) ret = -1; return ret; } static void maildirsize_rebuild_later(struct maildir_quota_root *root) { if (!root->root.set->force_default_rule) { /* FIXME: can't unlink(), because the limits would be lost. */ return; } if (unlink(root->maildirsize_path) < 0 && errno != ENOENT && errno != ESTALE) i_error("unlink(%s) failed: %m", root->maildirsize_path); } static int maildirsize_recalculate_finish(struct maildir_quota_root *root, int ret) { if (ret == 0) { /* maildir didn't change, we can write the maildirsize file */ ret = maildirsize_write(root, root->maildirsize_path); } if (ret != 0) maildirsize_rebuild_later(root); return ret; } static int maildirsize_recalculate(struct maildir_quota_root *root) { struct mail_namespace *const *namespaces; unsigned int i, count; int ret = 0; maildirsize_recalculate_init(root); /* count mails from all namespaces */ namespaces = array_get(&root->root.quota->namespaces, &count); for (i = 0; i < count; i++) { if (!quota_root_is_namespace_visible(&root->root, namespaces[i])) continue; if (maildirsize_recalculate_namespace(root, namespaces[i]) < 0) { ret = -1; break; } } if (ret == 0) { /* check if any of the directories have changed */ for (i = 0; i < count; i++) { if (!quota_root_is_namespace_visible(&root->root, namespaces[i])) continue; ret = maildirs_check_have_changed(root, namespaces[i], root->recalc_last_stamp); if (ret != 0) break; } } return maildirsize_recalculate_finish(root, ret); } static bool maildir_parse_limit(const char *str, uint64_t *bytes_r, uint64_t *count_r) { const char *const *limit; unsigned long long value; const char *pos; bool ret = TRUE; *bytes_r = 0; *count_r = 0; /* 0 values mean unlimited */ for (limit = t_strsplit(str, ","); *limit != NULL; limit++) { if (str_parse_ullong(*limit, &value, &pos) < 0) { ret = FALSE; continue; } if (pos[0] != '\0' && pos[1] == '\0') { switch (pos[0]) { case 'C': if (value != 0) *count_r = value; break; case 'S': if (value != 0) *bytes_r = value; break; default: ret = FALSE; break; } } else { ret = FALSE; } } return ret; } static int maildirsize_parse(struct maildir_quota_root *root, int fd, const char *const *lines) { struct quota_root *_root = &root->root; uint64_t message_bytes_limit, message_count_limit; long long bytes_diff, total_bytes; int count_diff, total_count; unsigned int line_count = 0; if (*lines == NULL) return -1; /* first line contains the limits */ (void)maildir_parse_limit(lines[0], &message_bytes_limit, &message_count_limit); /* truncate too high limits to signed 64bit int range */ if (message_bytes_limit >= (1ULL << 63)) message_bytes_limit = (1ULL << 63) - 1; if (message_count_limit >= (1ULL << 63)) message_count_limit = (1ULL << 63) - 1; if (root->root.bytes_limit == (int64_t)message_bytes_limit && root->root.count_limit == (int64_t)message_count_limit) { /* limits haven't changed */ } else if (root->root.set->force_default_rule) { /* we know the limits and they've changed. the file must be rewritten. */ return 0; } else { /* we're using limits from the file. */ root->root.bytes_limit = message_bytes_limit; root->root.count_limit = message_count_limit; quota_root_recalculate_relative_rules(root->root.set, message_bytes_limit, message_count_limit); } if (*lines == NULL) { /* no quota lines. rebuild it. */ return 0; } /* rest of the lines contains diffs */ total_bytes = 0; total_count = 0; for (lines++; *lines != NULL; lines++, line_count++) { if (sscanf(*lines, "%lld %d", &bytes_diff, &count_diff) != 2) return -1; total_bytes += bytes_diff; total_count += count_diff; } if (total_bytes < 0 || total_count < 0) { /* corrupted */ return -1; } if ((total_bytes > _root->bytes_limit && _root->bytes_limit != 0) || (total_count > _root->count_limit && _root->count_limit != 0)) { /* we're over quota. don't trust these values if the file contains more than the initial summary line, or if the file is older than 15 minutes. */ struct stat st; if (line_count > 1) return 0; if (fstat(fd, &st) < 0 || st.st_mtime < ioloop_time - MAILDIRSIZE_STALE_SECS) return 0; } root->total_bytes = (uint64_t)total_bytes; root->total_count = (uint64_t)total_count; return 1; } static int maildirsize_open(struct maildir_quota_root *root) { if (root->fd != -1) { if (close(root->fd) < 0) i_error("close(%s) failed: %m", root->maildirsize_path); } root->fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND); if (root->fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", root->maildirsize_path); return -1; } return 1; } static bool maildirsize_has_changed(struct maildir_quota_root *root) { struct stat st1, st2; if (dotlock_settings.nfs_flush) { nfs_flush_file_handle_cache(root->maildirsize_path); nfs_flush_attr_cache_unlocked(root->maildirsize_path); } if (root->fd == -1) return TRUE; if (stat(root->maildirsize_path, &st1) < 0) return TRUE; if (fstat(root->fd, &st2) < 0) return TRUE; return root->last_size != st2.st_size || st1.st_ino != st2.st_ino || !CMP_DEV_T(st1.st_dev, st2.st_dev); } static int maildirsize_read(struct maildir_quota_root *root, bool *retry) { char buf[5120+1]; unsigned int i, size; bool retry_estale = *retry; int ret; *retry = FALSE; if (!maildirsize_has_changed(root)) return 1; if ((ret = maildirsize_open(root)) <= 0) return ret; /* @UNSAFE */ size = 0; while ((ret = read(root->fd, buf + size, sizeof(buf)-1 - size)) != 0) { if (ret < 0) { if (errno == ESTALE && retry_estale) { *retry = TRUE; break; } i_error("read(%s) failed: %m", root->maildirsize_path); break; } size += ret; if (size >= sizeof(buf)-1) { /* we'll need to recalculate the quota */ break; } } /* try to use the file even if we ran into some error. if we don't have forced limits, we'll need to read the header to get them */ root->total_bytes = root->total_count = 0; root->last_size = size; /* skip the last line if there's no LF at the end. Remove the last LF so we don't get one empty line in the strsplit. */ while (size > 0 && buf[size-1] != '\n') size--; if (size > 0) size--; buf[size] = '\0'; if (ret < 0 && size == 0) { /* the read failed and there's no usable header, fail. */ i_close_fd(&root->fd); return -1; } /* If there are any NUL bytes, the file is broken. */ for (i = 0; i < size; i++) { if (buf[i] == '\0') break; } if (i == size && maildirsize_parse(root, root->fd, t_strsplit(buf, "\n")) > 0 && ret == 0) ret = 1; else { /* broken file / need recalculation */ i_close_fd(&root->fd); ret = 0; } return ret; } static bool maildirquota_limits_init(struct maildir_quota_root *root) { struct mailbox_list *list; struct mail_storage *storage; const char *control_dir; if (root->limits_initialized) return root->maildirsize_path != NULL; root->limits_initialized = TRUE; if (root->maildirsize_ns == NULL) { i_assert(root->maildirsize_path == NULL); return FALSE; } list = root->maildirsize_ns->list; if (mailbox_list_get_storage(&list, "", &storage) == 0 && strcmp(storage->name, MAILDIR_STORAGE_NAME) != 0) { /* non-maildir namespace, skip */ if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) == 0) { i_warning("quota: Namespace '%s' is not Maildir, " "skipping for Maildir++ quota", root->maildirsize_ns->prefix); } root->maildirsize_path = NULL; return FALSE; } if (root->maildirsize_path == NULL) { if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_CONTROL, &control_dir)) i_unreached(); root->maildirsize_path = p_strconcat(root->root.pool, control_dir, "/"MAILDIRSIZE_FILENAME, NULL); } return TRUE; } static int maildirquota_read_limits(struct maildir_quota_root *root) { bool retry = TRUE; int ret, n = 0; if (!maildirquota_limits_init(root)) return 1; do { if (n == NFS_ESTALE_RETRY_COUNT) retry = FALSE; T_BEGIN { ret = maildirsize_read(root, &retry); } T_END; n++; } while (ret == -1 && retry); return ret; } static int maildirquota_refresh(struct maildir_quota_root *root, bool *recalculated_r) { int ret; *recalculated_r = FALSE; ret = maildirquota_read_limits(root); if (ret == 0) { if (root->root.bytes_limit == 0 && root->root.count_limit == 0 && root->root.set->default_rule.bytes_limit == 0 && root->root.set->default_rule.count_limit == 0) { /* no quota */ if (!root->root.set->force_default_rule) return 0; /* explicitly specified 0 as quota. keep the quota updated even if it's not enforced. */ } ret = maildirsize_recalculate(root); if (ret == 0) *recalculated_r = TRUE; } return ret < 0 ? -1 : 0; } static int maildirsize_update(struct maildir_quota_root *root, int count_diff, int64_t bytes_diff) { char str[MAX_INT_STRLEN*2 + 2 + 1]; int ret = 0; if (count_diff == 0 && bytes_diff == 0) return 0; /* We rely on O_APPEND working in here. That isn't NFS-safe, but it isn't necessarily that bad because the file is recreated once in a while, and sooner if corruption causes calculations to go over quota. This is also how Maildir++ spec specifies it should be done.. */ if (i_snprintf(str, sizeof(str), "%lld %d\n", (long long)bytes_diff, count_diff) < 0) i_unreached(); if (write_full(root->fd, str, strlen(str)) < 0) { ret = -1; if (errno == ESTALE) { /* deleted/replaced already, ignore */ } else { i_error("write_full(%s) failed: %m", root->maildirsize_path); } } else { /* close the file to force a flush with NFS */ if (close(root->fd) < 0) { ret = -1; if (errno != ESTALE) i_error("close(%s) failed: %m", root->maildirsize_path); } root->fd = -1; } return ret; } static struct quota_root *maildir_quota_alloc(void) { struct maildir_quota_root *root; root = i_new(struct maildir_quota_root, 1); root->fd = -1; return &root->root; } static int maildir_quota_init(struct quota_root *_root, const char *args, const char **error_r) { return quota_root_default_init(_root, args, error_r); } static void maildir_quota_deinit(struct quota_root *_root) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; if (root->fd != -1) i_close_fd(&root->fd); i_free(root); } static bool maildir_quota_parse_rule(struct quota_root_settings *root_set ATTR_UNUSED, struct quota_rule *rule, const char *str, const char **error_r) { uint64_t bytes, count; if (strcmp(str, "NOQUOTA") == 0) { bytes = 0; count = 0; } else if (!maildir_parse_limit(str, &bytes, &count)) { *error_r = "Invalid Maildir++ quota rule"; return FALSE; } rule->bytes_limit = bytes; rule->count_limit = count; return TRUE; } static int maildir_quota_init_limits(struct quota_root *_root) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; return maildirquota_read_limits(root) < 0 ? -1 : 0; } static void maildir_quota_root_namespace_added(struct quota_root *_root, struct mail_namespace *ns) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; if (root->maildirsize_ns == NULL) root->maildirsize_ns = ns; } static void maildir_quota_namespace_added(struct quota *quota, struct mail_namespace *ns) { struct quota_root **roots; unsigned int i, count; roots = array_get_modifiable("a->roots, &count); for (i = 0; i < count; i++) { if (roots[i]->backend.name == quota_backend_maildir.name && ((roots[i]->ns_prefix == NULL && ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) || roots[i]->ns == ns)) maildir_quota_root_namespace_added(roots[i], ns); } } static const char *const * maildir_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources_both[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return resources_both; } static int maildir_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; bool recalculated; if (maildirquota_refresh(root, &recalculated) < 0) return -1; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) { *value_r = root->total_bytes; } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { *value_r = root->total_count; } else return 0; return 1; } static int maildir_quota_update(struct quota_root *_root, struct quota_transaction_context *ctx) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; bool recalculated; if (!maildirquota_limits_init(root)) { /* no limits */ return 0; } /* even though we don't really care about the limits in here ourself, we do want to make sure the header gets updated if the limits have changed. also this makes sure the maildirsize file is created if it doesn't exist. */ if (maildirquota_refresh(root, &recalculated) < 0) return -1; if (recalculated) { /* quota was just recalculated and it already contains the changes we wanted to do. */ } else if (root->fd == -1) (void)maildirsize_recalculate(root); else if (ctx->recalculate != QUOTA_RECALCULATE_DONT) { i_close_fd(&root->fd); (void)maildirsize_recalculate(root); } else if (maildirsize_update(root, ctx->count_used, ctx->bytes_used) < 0) { if (root->fd != -1) i_close_fd(&root->fd); maildirsize_rebuild_later(root); } return 0; } struct quota_backend quota_backend_maildir = { "maildir", { maildir_quota_alloc, maildir_quota_init, maildir_quota_deinit, maildir_quota_parse_rule, maildir_quota_init_limits, maildir_quota_namespace_added, maildir_quota_root_get_resources, maildir_quota_get_resource, maildir_quota_update, NULL, NULL } }; dovecot-2.2.33.2/src/plugins/quota/quota-dict.c0000644000175000017500000001442013165463624016157 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "dict.h" #include "mail-user.h" #include "mail-namespace.h" #include "quota-private.h" #define DICT_QUOTA_CURRENT_PATH DICT_PATH_PRIVATE"quota/" #define DICT_QUOTA_CURRENT_BYTES_PATH DICT_QUOTA_CURRENT_PATH"storage" #define DICT_QUOTA_CURRENT_COUNT_PATH DICT_QUOTA_CURRENT_PATH"messages" struct dict_quota_root { struct quota_root root; struct dict *dict; struct timeout *to_update; bool disable_unset; }; extern struct quota_backend quota_backend_dict; static struct quota_root *dict_quota_alloc(void) { struct dict_quota_root *root; root = i_new(struct dict_quota_root, 1); return &root->root; } static void handle_nounset_param(struct quota_root *_root, const char *param_value ATTR_UNUSED) { ((struct dict_quota_root *)_root)->disable_unset = TRUE; } static int dict_quota_init(struct quota_root *_root, const char *args, const char **error_r) { struct dict_quota_root *root = (struct dict_quota_root *)_root; struct dict_settings set; const char *username, *p, *error; const struct quota_param_parser dict_params[] = { {.param_name = "no-unset", .param_handler = handle_nounset_param}, quota_param_hidden, quota_param_ignoreunlimited, quota_param_noenforcing, quota_param_ns, {.param_name = NULL} }; p = args == NULL ? NULL : strchr(args, ':'); if (p == NULL) { *error_r = "URI missing from parameters"; return -1; } username = t_strdup_until(args, p); args = p+1; if (quota_parse_parameters(_root, &args, error_r, dict_params, FALSE) < 0) i_unreached(); if (*username == '\0') username = _root->quota->user->username; if (_root->quota->set->debug) { i_debug("dict quota: user=%s, uri=%s, noenforcing=%d", username, args, _root->no_enforcing); } /* FIXME: we should use 64bit integer as datatype instead but before it can actually be used don't bother */ i_zero(&set); set.username = username; set.base_dir = _root->quota->user->set->base_dir; if (mail_user_get_home(_root->quota->user, &set.home_dir) <= 0) set.home_dir = NULL; if (dict_init_full(args, &set, &root->dict, &error) < 0) { *error_r = t_strdup_printf("dict_init(%s) failed: %s", args, error); return -1; } return 0; } static void dict_quota_deinit(struct quota_root *_root) { struct dict_quota_root *root = (struct dict_quota_root *)_root; i_assert(root->to_update == NULL); if (root->dict != NULL) { dict_wait(root->dict); dict_deinit(&root->dict); } i_free(root); } static const char *const * dict_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return resources; } static int dict_quota_count(struct dict_quota_root *root, bool want_bytes, uint64_t *value_r) { struct dict_transaction_context *dt; uint64_t bytes, count; if (quota_count(&root->root, &bytes, &count) < 0) return -1; T_BEGIN { dt = dict_transaction_begin(root->dict); /* these unsets are mainly necessary for pgsql, because its trigger otherwise increases quota without deleting it. but some people with other databases want to store the quota usage among other data in the same row, which shouldn't be deleted. */ if (!root->disable_unset) { dict_unset(dt, DICT_QUOTA_CURRENT_BYTES_PATH); dict_unset(dt, DICT_QUOTA_CURRENT_COUNT_PATH); } dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes)); dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count)); } T_END; dict_transaction_commit_async(&dt, NULL, NULL); *value_r = want_bytes ? bytes : count; return 1; } static int dict_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct dict_quota_root *root = (struct dict_quota_root *)_root; bool want_bytes; int ret; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) want_bytes = TRUE; else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) want_bytes = FALSE; else return 0; T_BEGIN { const char *value; ret = dict_lookup(root->dict, unsafe_data_stack_pool, want_bytes ? DICT_QUOTA_CURRENT_BYTES_PATH : DICT_QUOTA_CURRENT_COUNT_PATH, &value); if (ret < 0) *value_r = 0; else { intmax_t tmp; /* recalculate quota if it's negative or if it wasn't found */ if (ret == 0 || str_to_intmax(value, &tmp) < 0) tmp = -1; if (tmp >= 0) *value_r = tmp; else { ret = dict_quota_count(root, want_bytes, value_r); } } } T_END; return ret; } static void dict_quota_recalc_timeout(struct dict_quota_root *root) { uint64_t value; timeout_remove(&root->to_update); (void)dict_quota_count(root, TRUE, &value); } static void dict_quota_update_callback(int ret, void *context) { struct dict_quota_root *root = context; if (ret == 0) { /* row doesn't exist, need to recalculate it */ if (root->to_update == NULL) root->to_update = timeout_add_short(0, dict_quota_recalc_timeout, root); } else if (ret < 0) { i_error("dict quota: Quota update failed, it's now desynced"); } } static int dict_quota_update(struct quota_root *_root, struct quota_transaction_context *ctx) { struct dict_quota_root *root = (struct dict_quota_root *) _root; struct dict_transaction_context *dt; uint64_t value; if (ctx->recalculate != QUOTA_RECALCULATE_DONT) { if (dict_quota_count(root, TRUE, &value) < 0) return -1; } else { dt = dict_transaction_begin(root->dict); if (ctx->bytes_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH, ctx->bytes_used); } if (ctx->count_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH, ctx->count_used); } dict_transaction_no_slowness_warning(dt); dict_transaction_commit_async(&dt, dict_quota_update_callback, root); } return 0; } static void dict_quota_flush(struct quota_root *_root) { struct dict_quota_root *root = (struct dict_quota_root *)_root; (void)dict_wait(root->dict); if (root->to_update != NULL) { dict_quota_recalc_timeout(root); (void)dict_wait(root->dict); } } struct quota_backend quota_backend_dict = { "dict", { dict_quota_alloc, dict_quota_init, dict_quota_deinit, NULL, NULL, NULL, dict_quota_root_get_resources, dict_quota_get_resource, dict_quota_update, NULL, dict_quota_flush } }; dovecot-2.2.33.2/src/plugins/quota/quota.h0000644000175000017500000001203613165463624015244 00000000000000#ifndef QUOTA_H #define QUOTA_H struct mail; struct mailbox; struct mail_user; /* Message storage size kilobytes. */ #define QUOTA_NAME_STORAGE_KILOBYTES "STORAGE" /* Message storage size bytes. This is used only internally. */ #define QUOTA_NAME_STORAGE_BYTES "STORAGE_BYTES" /* Number of messages. */ #define QUOTA_NAME_MESSAGES "MESSAGE" struct quota; struct quota_settings; struct quota_root_settings; struct quota_root; struct quota_root_iter; struct quota_transaction_context; struct quota_param_parser { char *param_name; void (* param_handler)(struct quota_root *_root, const char *param_value); }; extern struct quota_param_parser quota_param_hidden; extern struct quota_param_parser quota_param_ignoreunlimited; extern struct quota_param_parser quota_param_noenforcing; extern struct quota_param_parser quota_param_ns; enum quota_recalculate { QUOTA_RECALCULATE_DONT = 0, /* We may want to recalculate quota because we weren't able to call quota_free*() correctly for all mails. Quota needs to be recalculated unless the backend does the quota tracking internally. */ QUOTA_RECALCULATE_MISSING_FREES, /* doveadm quota recalc called - make sure the quota is correct */ QUOTA_RECALCULATE_FORCED }; enum quota_alloc_result { QUOTA_ALLOC_RESULT_OK, QUOTA_ALLOC_RESULT_TEMPFAIL, QUOTA_ALLOC_RESULT_OVER_MAXSIZE, QUOTA_ALLOC_RESULT_OVER_QUOTA, /* Mail size is larger than even the maximum allowed quota. */ QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT, }; const char *quota_alloc_result_errstr(enum quota_alloc_result res, struct quota_transaction_context *qt); int quota_user_read_settings(struct mail_user *user, struct quota_settings **set_r, const char **error_r); void quota_settings_deinit(struct quota_settings **quota_set); /* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */ int quota_root_add_rule(struct quota_root_settings *root_set, const char *rule_def, const char **error_r); /* Add a new warning rule for the quota root. Returns 0 if ok, -1 if rule is invalid. */ int quota_root_add_warning_rule(struct quota_root_settings *root_set, const char *rule_def, const char **error_r); /* Initialize quota for the given user. Returns 0 and quota_r on success, -1 and error_r on failure. */ int quota_init(struct quota_settings *quota_set, struct mail_user *user, struct quota **quota_r, const char **error_r); void quota_deinit(struct quota **quota); /* List all visible quota roots. They don't need to be freed. */ struct quota_root_iter *quota_root_iter_init(struct mailbox *box); struct quota_root *quota_root_iter_next(struct quota_root_iter *iter); void quota_root_iter_deinit(struct quota_root_iter **iter); /* Return quota root or NULL. */ struct quota_root *quota_root_lookup(struct mail_user *user, const char *name); /* Returns name of the quota root. */ const char *quota_root_get_name(struct quota_root *root); /* Return a list of all resources set for the quota root. */ const char *const *quota_root_get_resources(struct quota_root *root); /* Returns TRUE if quota root is marked as hidden (so it shouldn't be visible to users via IMAP GETQUOTAROOT command). */ bool quota_root_is_hidden(struct quota_root *root); /* Returns 1 if values were successfully returned, 0 if resource name doesn't exist or isn't enabled, -1 if error. */ int quota_get_resource(struct quota_root *root, const char *mailbox_name, const char *name, uint64_t *value_r, uint64_t *limit_r); /* Returns 0 if OK, -1 if error (eg. permission denied, invalid name). */ int quota_set_resource(struct quota_root *root, const char *name, uint64_t value, const char **error_r); /* Start a new quota transaction. */ struct quota_transaction_context *quota_transaction_begin(struct mailbox *box); /* Commit quota transaction. Returns 0 if ok, -1 if failed. */ int quota_transaction_commit(struct quota_transaction_context **ctx); /* Rollback quota transaction changes. */ void quota_transaction_rollback(struct quota_transaction_context **ctx); /* Allocate from quota if there's space. */ enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail); /* Like quota_try_alloc(), but don't actually allocate anything. */ enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size); /* Update quota by allocating/freeing space used by mail. */ void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail); void quota_free(struct quota_transaction_context *ctx, struct mail *mail); void quota_free_bytes(struct quota_transaction_context *ctx, uoff_t physical_size); /* Mark the quota to be recalculated */ void quota_recalculate(struct quota_transaction_context *ctx, enum quota_recalculate recalculate); /* Execute quota_over_scripts if needed. */ void quota_over_flag_check_startup(struct quota *quota); /* Common quota parameters parsing loop */ int quota_parse_parameters(struct quota_root *root, const char **args, const char **error_r, const struct quota_param_parser *valid_params, bool fail_on_unknown); #endif dovecot-2.2.33.2/src/plugins/quota/quota-count.c0000644000175000017500000002103413165463624016363 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mailbox-list-iter.h" #include "quota-private.h" struct count_quota_root { struct quota_root root; struct timeval cache_timeval; uint64_t cached_bytes, cached_count; }; struct quota_mailbox_iter { struct quota_root *root; struct mail_namespace *ns; unsigned int ns_idx; struct mailbox_list_iterate_context *iter; struct mailbox_info info; bool failed; }; extern struct quota_backend quota_backend_count; static int quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns, const char *vname, uint64_t *bytes, uint64_t *count) { struct quota_rule *rule; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_status status; enum mail_error error; const char *errstr; int ret; rule = quota_root_rule_find(root->set, vname); if (rule != NULL && rule->ignore) { /* mailbox not included in quota */ return 0; } box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY); mailbox_set_reason(box, "quota count"); if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) { /* quota doesn't exist for this mailbox/storage */ ret = 0; } else if (mailbox_get_metadata(box, root->quota->set->vsizes ? MAILBOX_METADATA_VIRTUAL_SIZE : MAILBOX_METADATA_PHYSICAL_SIZE, &metadata) < 0 || mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error == MAIL_ERROR_TEMP) { i_error("quota: Couldn't get size of mailbox %s: %s", vname, errstr); ret = -1; } else if (error == MAIL_ERROR_INUSE) { /* started on background. don't log an error. */ ret = -1; } else { /* non-temporary error, e.g. ACLs denied access. */ ret = 0; } } else { ret = 0; *bytes += root->quota->set->vsizes ? metadata.virtual_size : metadata.physical_size; *count += status.messages; } mailbox_free(&box); return ret; } static struct quota_mailbox_iter * quota_mailbox_iter_begin(struct quota_root *root) { struct quota_mailbox_iter *iter; iter = i_new(struct quota_mailbox_iter, 1); iter->root = root; return iter; } static int quota_mailbox_iter_deinit(struct quota_mailbox_iter **_iter) { struct quota_mailbox_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; if (iter->iter != NULL) { if (mailbox_list_iter_deinit(&iter->iter) < 0) { i_error("quota: Listing namespace '%s' failed: %s", iter->ns->prefix, mailbox_list_get_last_internal_error(iter->ns->list, NULL)); ret = -1; } } i_free(iter); return ret; } static const struct mailbox_info * quota_mailbox_iter_next(struct quota_mailbox_iter *iter) { struct mail_namespace *const *namespaces; const struct mailbox_info *info; unsigned int count; if (iter->iter == NULL) { namespaces = array_get(&iter->root->quota->namespaces, &count); do { if (iter->ns_idx >= count) return NULL; iter->ns = namespaces[iter->ns_idx++]; } while (!quota_root_is_namespace_visible(iter->root, iter->ns)); iter->iter = mailbox_list_iter_init(iter->ns->list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_NO_AUTO_BOXES); } while ((info = mailbox_list_iter_next(iter->iter)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) == 0) return info; } if (mailbox_list_iter_deinit(&iter->iter) < 0) { i_error("quota: Listing namespace '%s' failed: %s", iter->ns->prefix, mailbox_list_get_last_internal_error(iter->ns->list, NULL)); iter->failed = TRUE; } if (iter->ns->prefix_len > 0 && (iter->ns->prefix_len != 6 || strncasecmp(iter->ns->prefix, "INBOX", 5) != 0)) { /* if the namespace prefix itself exists, count it also */ iter->info.ns = iter->ns; iter->info.vname = t_strndup(iter->ns->prefix, iter->ns->prefix_len-1); return &iter->info; } /* try the next namespace */ return quota_mailbox_iter_next(iter); } int quota_count(struct quota_root *root, uint64_t *bytes_r, uint64_t *count_r) { struct quota_mailbox_iter *iter; const struct mailbox_info *info; int ret = 1; *bytes_r = *count_r = 0; if (root->recounting) return 0; root->recounting = TRUE; iter = quota_mailbox_iter_begin(root); while ((info = quota_mailbox_iter_next(iter)) != NULL) { if (quota_count_mailbox(root, info->ns, info->vname, bytes_r, count_r) < 0) { ret = -1; break; } } quota_mailbox_iter_deinit(&iter); root->recounting = FALSE; return ret; } static int quota_count_cached(struct count_quota_root *root, uint64_t *bytes_r, uint64_t *count_r) { int ret; if (root->cache_timeval.tv_usec == ioloop_timeval.tv_usec && root->cache_timeval.tv_sec == ioloop_timeval.tv_sec && ioloop_timeval.tv_sec != 0) { *bytes_r = root->cached_bytes; *count_r = root->cached_count; return 1; } ret = quota_count(&root->root, bytes_r, count_r); if (ret > 0) { root->cache_timeval = ioloop_timeval; root->cached_bytes = *bytes_r; root->cached_count = *count_r; } return ret < 0 ? -1 : 0; } static struct quota_root *count_quota_alloc(void) { struct count_quota_root *root; root = i_new(struct count_quota_root, 1); return &root->root; } static int count_quota_init(struct quota_root *root, const char *args, const char **error_r) { if (!root->quota->set->vsizes) { *error_r = "quota count backend requires quota_vsizes=yes"; return -1; } root->auto_updating = TRUE; return quota_root_default_init(root, args, error_r); } static void count_quota_deinit(struct quota_root *_root) { i_free(_root); } static const char *const * count_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return resources; } static int count_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct count_quota_root *root = (struct count_quota_root *)_root; uint64_t bytes, count; if (quota_count_cached(root, &bytes, &count) < 0) return -1; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) *value_r = bytes; else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) *value_r = count; else return 0; return 1; } static int quota_count_recalculate_box(struct mailbox *box) { struct mail_index_transaction *trans; struct mailbox_metadata metadata; struct mailbox_index_vsize vsize_hdr; const char *errstr; enum mail_error error; if (mailbox_open(box) < 0) { errstr = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_TEMP) { /* non-temporary error, e.g. ACLs denied access. */ return 0; } i_error("Couldn't open mailbox %s: %s", box->vname, errstr); return -1; } /* reset the vsize header first */ trans = mail_index_transaction_begin(box->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); i_zero(&vsize_hdr); mail_index_update_header_ext(trans, box->vsize_hdr_ext_id, 0, &vsize_hdr, sizeof(vsize_hdr)); if (mail_index_transaction_commit(&trans) < 0) return -1; /* getting the vsize now forces its recalculation */ if (mailbox_get_metadata(box, MAILBOX_METADATA_VIRTUAL_SIZE, &metadata) < 0) { i_error("Couldn't get mailbox %s vsize: %s", box->vname, mailbox_get_last_internal_error(box, NULL)); return -1; } /* call sync to write the change to mailbox list index */ if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) { i_error("Couldn't sync mailbox %s: %s", box->vname, mailbox_get_last_internal_error(box, NULL)); return -1; } return 0; } static int quota_count_recalculate(struct quota_root *root) { struct quota_mailbox_iter *iter; const struct mailbox_info *info; struct mailbox *box; int ret = 0; iter = quota_mailbox_iter_begin(root); while ((info = quota_mailbox_iter_next(iter)) != NULL) { box = mailbox_alloc(info->ns->list, info->vname, 0); mailbox_set_reason(box, "quota recalculate"); if (quota_count_recalculate_box(box) < 0) ret = -1; mailbox_free(&box); } quota_mailbox_iter_deinit(&iter); return ret; } static int count_quota_update(struct quota_root *root, struct quota_transaction_context *ctx) { struct count_quota_root *croot = (struct count_quota_root *)root; croot->cache_timeval.tv_sec = 0; if (ctx->recalculate == QUOTA_RECALCULATE_FORCED) { if (quota_count_recalculate(root) < 0) return -1; } return 0; } struct quota_backend quota_backend_count = { "count", { count_quota_alloc, count_quota_init, count_quota_deinit, NULL, NULL, NULL, count_quota_root_get_resources, count_quota_get_resource, count_quota_update, NULL, NULL } }; dovecot-2.2.33.2/src/plugins/quota/quota-plugin.h0000644000175000017500000000144113123174404016523 00000000000000#ifndef QUOTA_PLUGIN_H #define QUOTA_PLUGIN_H #include "module-context.h" #include "mail-user.h" struct module; struct mailbox; struct mailbox_list; struct mail; #define QUOTA_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_user_module) struct quota_user { union mail_user_module_context module_ctx; struct quota *quota; }; struct mail_storage; extern MODULE_CONTEXT_DEFINE(quota_user_module, &mail_user_module_register); void quota_mail_user_created(struct mail_user *user); void quota_mailbox_list_created(struct mailbox_list *list); void quota_mail_namespaces_created(struct mail_namespace *namespaces); void quota_mailbox_allocated(struct mailbox *box); void quota_mail_allocated(struct mail *mail); void quota_plugin_init(struct module *module); void quota_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/quota/quota-status-settings.c0000644000175000017500000000176413147010712020406 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "quota-status-settings.h" #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct quota_status_settings, name), NULL } static const struct setting_define quota_status_setting_defines[] = { DEF(SET_STR, recipient_delimiter), SETTING_DEFINE_LIST_END }; static const struct quota_status_settings quota_status_default_settings = { .recipient_delimiter = "+", }; static const struct setting_parser_info *quota_status_setting_dependencies[] = { NULL }; const struct setting_parser_info quota_status_setting_parser_info = { .module_name = "mail", .defines = quota_status_setting_defines, .defaults = "a_status_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct quota_status_settings), .parent_offset = (size_t)-1, .dependencies = quota_status_setting_dependencies }; dovecot-2.2.33.2/src/plugins/quota/quota-dirsize.c0000644000175000017500000001172413123174404016676 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ /* Quota reporting based on simply summing sizes of all files in mailbox together. */ #include "lib.h" #include "array.h" #include "str.h" #include "quota-private.h" #include #include #include struct quota_count_path { const char *path; bool is_file; }; ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path); extern struct quota_backend quota_backend_dirsize; static struct quota_root *dirsize_quota_alloc(void) { return i_new(struct quota_root, 1); } static int dirsize_quota_init(struct quota_root *root, const char *args, const char **error_r) { root->auto_updating = TRUE; return quota_root_default_init(root, args, error_r); } static void dirsize_quota_deinit(struct quota_root *_root) { i_free(_root); } static const char *const * dirsize_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, NULL }; return resources; } static int get_dir_usage(const char *dir, uint64_t *value) { DIR *dirp; string_t *path; struct dirent *d; struct stat st; unsigned int path_pos; int ret; dirp = opendir(dir); if (dirp == NULL) { if (errno == ENOENT) return 0; i_error("opendir(%s) failed: %m", dir); return -1; } path = t_str_new(128); str_append(path, dir); str_append_c(path, '/'); path_pos = str_len(path); ret = 0; while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { /* skip . and .. */ continue; } str_truncate(path, path_pos); str_append(path, d->d_name); if (lstat(str_c(path), &st) < 0) { if (errno == ENOENT) continue; i_error("lstat(%s) failed: %m", dir); ret = -1; break; } else if (S_ISDIR(st.st_mode)) { if (get_dir_usage(str_c(path), value) < 0) { ret = -1; break; } } else { *value += st.st_size; } } (void)closedir(dirp); return ret; } static int get_usage(const char *path, bool is_file, uint64_t *value_r) { struct stat st; if (is_file) { if (lstat(path, &st) < 0) { if (errno == ENOENT) return 0; i_error("lstat(%s) failed: %m", path); return -1; } *value_r += st.st_size; } else { if (get_dir_usage(path, value_r) < 0) return -1; } return 0; } static void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths, const char *path, bool is_file) { struct quota_count_path *count_path; unsigned int i, count; size_t path_len; path_len = strlen(path); count_path = array_get_modifiable(paths, &count); for (i = 0; i < count; ) { if (strncmp(count_path[i].path, path, strlen(count_path[i].path)) == 0) { /* this path has already been counted */ return; } if (strncmp(count_path[i].path, path, path_len) == 0 && count_path[i].path[path_len] == '/') { /* the new path contains the existing path. drop it and see if there are more to drop. */ array_delete(paths, i, 1); count_path = array_get_modifiable(paths, &count); } else { i++; } } count_path = array_append_space(paths); count_path->path = t_strdup(path); count_path->is_file = is_file; } static int get_quota_root_usage(struct quota_root *root, uint64_t *value_r) { struct mail_namespace *const *namespaces; ARRAY_TYPE(quota_count_path) paths; const struct quota_count_path *count_paths; unsigned int i, count; const char *path; bool is_file; t_array_init(&paths, 8); namespaces = array_get(&root->quota->namespaces, &count); for (i = 0; i < count; i++) { if (!quota_root_is_namespace_visible(root, namespaces[i])) continue; is_file = mail_storage_is_mailbox_file(namespaces[i]->storage); if (mailbox_list_get_root_path(namespaces[i]->list, MAILBOX_LIST_PATH_TYPE_DIR, &path)) quota_count_path_add(&paths, path, FALSE); /* INBOX may be in different path. */ if (mailbox_list_get_path(namespaces[i]->list, "INBOX", MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) > 0) quota_count_path_add(&paths, path, is_file); } /* now sum up the found paths */ *value_r = 0; count_paths = array_get(&paths, &count); for (i = 0; i < count; i++) { if (get_usage(count_paths[i].path, count_paths[i].is_file, value_r) < 0) return -1; } return 0; } static int dirsize_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { int ret; if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0) return 0; T_BEGIN { ret = get_quota_root_usage(_root, value_r); } T_END; return ret < 0 ? -1 : 1; } static int dirsize_quota_update(struct quota_root *root ATTR_UNUSED, struct quota_transaction_context *ctx ATTR_UNUSED) { return 0; } struct quota_backend quota_backend_dirsize = { "dirsize", { dirsize_quota_alloc, dirsize_quota_init, dirsize_quota_deinit, NULL, NULL, NULL, dirsize_quota_root_get_resources, dirsize_quota_get_resource, dirsize_quota_update, NULL, NULL } }; dovecot-2.2.33.2/src/plugins/quota/quota.c0000644000175000017500000011567713165463624015256 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "ioloop.h" #include "net.h" #include "write-full.h" #include "eacces-error.h" #include "wildcard-match.h" #include "dict.h" #include "mailbox-list-private.h" #include "quota-private.h" #include "quota-fs.h" #include "settings-parser.h" #include #define DEFAULT_QUOTA_EXCEEDED_MSG \ "Quota exceeded (mailbox for user is full)" #define QUOTA_LIMIT_SET_PATH DICT_PATH_PRIVATE"quota/limit/" /* How many seconds after the userdb lookup do we still want to execute the quota_over_script. This applies to quota_over_flag_lazy_check=yes and also after unhibernating IMAP connections. */ #define QUOTA_OVER_FLAG_MAX_DELAY_SECS 10 struct quota_root_iter { struct quota *quota; struct mailbox *box; unsigned int i; }; unsigned int quota_module_id = 0; extern struct quota_backend quota_backend_count; extern struct quota_backend quota_backend_dict; extern struct quota_backend quota_backend_dirsize; extern struct quota_backend quota_backend_fs; extern struct quota_backend quota_backend_imapc; extern struct quota_backend quota_backend_maildir; static const struct quota_backend *quota_internal_backends[] = { #ifdef HAVE_FS_QUOTA "a_backend_fs, #endif "a_backend_count, "a_backend_dict, "a_backend_dirsize, "a_backend_imapc, "a_backend_maildir }; static ARRAY(const struct quota_backend*) quota_backends; static void hidden_param_handler(struct quota_root *_root, const char *param_value); static void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value); static void noenforcing_param_handler(struct quota_root *_root, const char *param_value); static void ns_param_handler(struct quota_root *_root, const char *param_value); struct quota_param_parser quota_param_hidden = {.param_name = "hidden", .param_handler = hidden_param_handler}; struct quota_param_parser quota_param_ignoreunlimited = {.param_name = "ignoreunlimited", .param_handler = ignoreunlim_param_handler}; struct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing", .param_handler = noenforcing_param_handler}; struct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler}; static enum quota_alloc_result quota_default_test_alloc( struct quota_transaction_context *ctx, uoff_t size); static void quota_over_flag_check_root(struct quota_root *root); static const struct quota_backend *quota_backend_find(const char *name) { const struct quota_backend *const *backend; array_foreach("a_backends, backend) { if (strcmp((*backend)->name, name) == 0) return *backend; } return NULL; } void quota_backend_register(const struct quota_backend *backend) { i_assert(quota_backend_find(backend->name) == NULL); array_append("a_backends, &backend, 1); } void quota_backend_unregister(const struct quota_backend *backend) { for(unsigned int i = 0; i < array_count("a_backends); i++) { const struct quota_backend *const *be = array_idx("a_backends, i); if (strcmp((*be)->name, backend->name) == 0) { array_delete("a_backends, i, 1); return; } } i_unreached(); } void quota_backends_register(void); void quota_backends_unregister(void); void quota_backends_register(void) { i_array_init("a_backends, 8); array_append("a_backends, quota_internal_backends, N_ELEMENTS(quota_internal_backends)); } void quota_backends_unregister(void) { for(size_t i = 0; i < N_ELEMENTS(quota_internal_backends); i++) { quota_backend_unregister(quota_internal_backends[i]); } i_assert(array_count("a_backends) == 0); array_free("a_backends); } static int quota_root_add_rules(struct mail_user *user, const char *root_name, struct quota_root_settings *root_set, const char **error_r) { const char *rule_name, *rule, *error; unsigned int i; rule_name = t_strconcat(root_name, "_rule", NULL); for (i = 2;; i++) { rule = mail_user_plugin_getenv(user, rule_name); if (rule == NULL) break; if (quota_root_add_rule(root_set, rule, &error) < 0) { *error_r = t_strdup_printf("Invalid rule %s: %s", rule, error); return -1; } rule_name = t_strdup_printf("%s_rule%d", root_name, i); } return 0; } static int quota_root_add_warning_rules(struct mail_user *user, const char *root_name, struct quota_root_settings *root_set, const char **error_r) { const char *rule_name, *rule, *error; unsigned int i; rule_name = t_strconcat(root_name, "_warning", NULL); for (i = 2;; i++) { rule = mail_user_plugin_getenv(user, rule_name); if (rule == NULL) break; if (quota_root_add_warning_rule(root_set, rule, &error) < 0) { *error_r = t_strdup_printf("Invalid warning rule: %s", rule); return -1; } rule_name = t_strdup_printf("%s_warning%d", root_name, i); } return 0; } static int quota_root_parse_set(struct mail_user *user, const char *root_name, struct quota_root_settings *root_set, const char **error_r) { const char *name, *value; name = t_strconcat(root_name, "_set", NULL); value = mail_user_plugin_getenv(user, name); if (value == NULL) return 0; if (strncmp(value, "dict:", 5) != 0) { *error_r = t_strdup_printf("%s supports only dict backend", name); return -1; } root_set->limit_set = p_strdup(root_set->set->pool, value+5); return 0; } static int quota_root_settings_init(struct quota_settings *quota_set, const char *root_def, struct quota_root_settings **set_r, const char **error_r) { struct quota_root_settings *root_set; const struct quota_backend *backend; const char *p, *args, *backend_name; /* [:[:]] */ p = strchr(root_def, ':'); if (p == NULL) { backend_name = root_def; args = NULL; } else { backend_name = t_strdup_until(root_def, p); args = p + 1; } backend = quota_backend_find(backend_name); if (backend == NULL) { *error_r = t_strdup_printf("Unknown quota backend: %s", backend_name); return -1; } root_set = p_new(quota_set->pool, struct quota_root_settings, 1); root_set->set = quota_set; root_set->backend = backend; if (args != NULL) { /* save root's name */ p = strchr(args, ':'); if (p == NULL) { root_set->name = p_strdup(quota_set->pool, args); args = NULL; } else { root_set->name = p_strdup_until(quota_set->pool, args, p); args = p + 1; } } else { root_set->name = ""; } root_set->args = p_strdup(quota_set->pool, args); if (quota_set->debug) { i_debug("Quota root: name=%s backend=%s args=%s", root_set->name, backend_name, args == NULL ? "" : args); } p_array_init(&root_set->rules, quota_set->pool, 4); p_array_init(&root_set->warning_rules, quota_set->pool, 4); array_append("a_set->root_sets, &root_set, 1); *set_r = root_set; return 0; } static int quota_root_add(struct quota_settings *quota_set, struct mail_user *user, const char *env, const char *root_name, const char **error_r) { struct quota_root_settings *root_set; const char *set_name, *value; if (quota_root_settings_init(quota_set, env, &root_set, error_r) < 0) return -1; root_set->set_name = p_strdup(quota_set->pool, root_name); if (quota_root_add_rules(user, root_name, root_set, error_r) < 0) return -1; if (quota_root_add_warning_rules(user, root_name, root_set, error_r) < 0) return -1; if (quota_root_parse_set(user, root_name, root_set, error_r) < 0) return -1; set_name = t_strconcat(root_name, "_grace", NULL); value = mail_user_plugin_getenv(user, set_name); if (quota_root_parse_grace(root_set, value, error_r) < 0) { *error_r = t_strdup_printf("Invalid %s value '%s': %s", set_name, value, *error_r); return -1; } return 0; } const char *quota_alloc_result_errstr(enum quota_alloc_result res, struct quota_transaction_context *qt) { switch (res) { case QUOTA_ALLOC_RESULT_OK: return "OK"; case QUOTA_ALLOC_RESULT_TEMPFAIL: return "Internal quota calculation error"; case QUOTA_ALLOC_RESULT_OVER_MAXSIZE: return "Mail size is larger than the maximum size allowed by " "server configuration"; case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT: case QUOTA_ALLOC_RESULT_OVER_QUOTA: return qt->quota->set->quota_exceeded_msg; } i_unreached(); } int quota_user_read_settings(struct mail_user *user, struct quota_settings **set_r, const char **error_r) { struct quota_settings *quota_set; char root_name[5 + MAX_INT_STRLEN + 1]; const char *env, *error; unsigned int i; pool_t pool; pool = pool_alloconly_create("quota settings", 2048); quota_set = p_new(pool, struct quota_settings, 1); quota_set->pool = pool; quota_set->test_alloc = quota_default_test_alloc; quota_set->debug = user->mail_debug; quota_set->quota_exceeded_msg = mail_user_plugin_getenv(user, "quota_exceeded_message"); if (quota_set->quota_exceeded_msg == NULL) quota_set->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG; quota_set->vsizes = mail_user_plugin_getenv(user, "quota_vsizes") != NULL; const char *max_size = mail_user_plugin_getenv(user, "quota_max_mail_size"); if (max_size != NULL) { const char *error = NULL; if (settings_get_size(max_size, "a_set->max_mail_size, &error) < 0) { *error_r = t_strdup_printf("quota_max_mail_size: %s", error); return -1; } } p_array_init("a_set->root_sets, pool, 4); if (i_strocpy(root_name, "quota", sizeof(root_name)) < 0) i_unreached(); for (i = 2;; i++) { env = mail_user_plugin_getenv(user, root_name); if (env == NULL || *env == '\0') break; if (quota_root_add(quota_set, user, env, root_name, &error) < 0) { *error_r = t_strdup_printf("Invalid quota root %s: %s", root_name, error); pool_unref(&pool); return -1; } if (i_snprintf(root_name, sizeof(root_name), "quota%d", i) < 0) i_unreached(); } if (quota_set->max_mail_size == 0 && array_count("a_set->root_sets) == 0) { pool_unref(&pool); return 0; } quota_set->initialized = TRUE; *set_r = quota_set; return 1; } void quota_settings_deinit(struct quota_settings **_quota_set) { struct quota_settings *quota_set = *_quota_set; *_quota_set = NULL; pool_unref("a_set->pool); } static void quota_root_deinit(struct quota_root *root) { pool_t pool = root->pool; if (root->limit_set_dict != NULL) dict_deinit(&root->limit_set_dict); root->backend.v.deinit(root); pool_unref(&pool); } int quota_root_default_init(struct quota_root *root, const char *args, const char **error_r) { const struct quota_param_parser default_params[] = { quota_param_hidden, quota_param_ignoreunlimited, quota_param_noenforcing, quota_param_ns, {.param_name = NULL} }; return quota_parse_parameters(root, &args, error_r, default_params, TRUE); } static int quota_root_init(struct quota_root_settings *root_set, struct quota *quota, struct quota_root **root_r, const char **error_r) { struct quota_root *root; root = root_set->backend->v.alloc(); root->pool = pool_alloconly_create("quota root", 512); root->set = root_set; root->quota = quota; root->backend = *root_set->backend; root->bytes_limit = root_set->default_rule.bytes_limit; root->count_limit = root_set->default_rule.count_limit; array_create(&root->quota_module_contexts, root->pool, sizeof(void *), 10); if (root->backend.v.init != NULL) { if (root->backend.v.init(root, root_set->args, error_r) < 0) { *error_r = t_strdup_printf("%s quota init failed: %s", root->backend.name, *error_r); return -1; } } else { if (quota_root_default_init(root, root_set->args, error_r) < 0) return -1; } if (root_set->default_rule.bytes_limit == 0 && root_set->default_rule.count_limit == 0 && root->disable_unlimited_tracking) { quota_root_deinit(root); return 0; } *root_r = root; return 1; } int quota_init(struct quota_settings *quota_set, struct mail_user *user, struct quota **quota_r, const char **error_r) { struct quota *quota; struct quota_root *root; struct quota_root_settings *const *root_sets; unsigned int i, count; const char *error; int ret; quota = i_new(struct quota, 1); quota->user = user; quota->set = quota_set; i_array_init("a->roots, 8); root_sets = array_get("a_set->root_sets, &count); i_array_init("a->namespaces, count); for (i = 0; i < count; i++) { ret = quota_root_init(root_sets[i], quota, &root, &error); if (ret < 0) { *error_r = t_strdup_printf("Quota root %s: %s", root_sets[i]->name, error); quota_deinit("a); return -1; } if (ret > 0) array_append("a->roots, &root, 1); } *quota_r = quota; return 0; } void quota_deinit(struct quota **_quota) { struct quota *quota = *_quota; struct quota_root *const *roots; unsigned int i, count; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) quota_root_deinit(roots[i]); /* deinit quota roots before setting quser->quota=NULL */ *_quota = NULL; array_free("a->roots); array_free("a->namespaces); i_free(quota); } static int quota_root_get_rule_limits(struct quota_root *root, const char *mailbox_name, uint64_t *bytes_limit_r, uint64_t *count_limit_r, bool *ignored_r) { struct quota_rule *rule; int64_t bytes_limit, count_limit; bool enabled; *ignored_r = FALSE; if (!root->set->force_default_rule) { if (root->backend.v.init_limits != NULL) { if (root->backend.v.init_limits(root) < 0) return -1; } } bytes_limit = root->bytes_limit; count_limit = root->count_limit; /* if default rule limits are 0, user has unlimited quota. ignore any specific quota rules */ enabled = bytes_limit != 0 || count_limit != 0; (void)mail_namespace_find_unalias(root->quota->user->namespaces, &mailbox_name); rule = enabled ? quota_root_rule_find(root->set, mailbox_name) : NULL; if (rule != NULL) { if (!rule->ignore) { bytes_limit += rule->bytes_limit; count_limit += rule->count_limit; } else { bytes_limit = 0; count_limit = 0; *ignored_r = TRUE; } } *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit; *count_limit_r = count_limit <= 0 ? 0 : count_limit; return enabled ? 1 : 0; } static bool quota_is_duplicate_namespace(struct quota *quota, struct mail_namespace *ns) { struct mail_namespace *const *namespaces; unsigned int i, count; const char *path, *path2; if (!mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path)) path = NULL; namespaces = array_get("a->namespaces, &count); for (i = 0; i < count; i++) { /* count namespace aliases only once. don't rely only on alias_for != NULL, because the alias might have been explicitly added as the wanted quota namespace. */ if (ns->alias_for == namespaces[i] || namespaces[i]->alias_for == ns) continue; if (path != NULL && mailbox_list_get_root_path(namespaces[i]->list, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path2) && strcmp(path, path2) == 0) { /* duplicate path */ if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) return TRUE; /* this is inbox=yes namespace, but the earlier one that had the same location was inbox=no. we need to include the INBOX also in quota calculations, so we can't just ignore this namespace. but since we've already called backend's namespace_added(), we can't just remove it either. so just mark the old one as unwanted namespace. an alternative would be to do a bit larger change so namespaces wouldn't be added until mail_namespaces_created() hook is called */ i_assert(quota->unwanted_ns == NULL); quota->unwanted_ns = namespaces[i]; return FALSE; } } return FALSE; } void quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns) { struct quota_root *const *roots; struct quota_backend **backends; unsigned int i, j, count; /* first check if there already exists a namespace with the exact same path. we don't want to count them twice. */ if (quota_is_duplicate_namespace(quota, ns)) return; array_append("a->namespaces, &ns, 1); roots = array_get("a->roots, &count); /* @UNSAFE: get different backends into one array */ backends = t_new(struct quota_backend *, count + 1); for (i = 0; i < count; i++) { for (j = 0; backends[j] != NULL; j++) { if (backends[j]->name == roots[i]->backend.name) break; } if (backends[j] == NULL) backends[j] = &roots[i]->backend; } for (i = 0; backends[i] != NULL; i++) { if (backends[i]->v.namespace_added != NULL) backends[i]->v.namespace_added(quota, ns); } } void quota_remove_user_namespace(struct mail_namespace *ns) { struct quota *quota; struct mail_namespace *const *namespaces; unsigned int i, count; quota = ns->owner != NULL ? quota_get_mail_user_quota(ns->owner) : quota_get_mail_user_quota(ns->user); if (quota == NULL) { /* no quota for this namespace */ return; } namespaces = array_get("a->namespaces, &count); for (i = 0; i < count; i++) { if (namespaces[i] == ns) { array_delete("a->namespaces, i, 1); break; } } } struct quota_root_iter * quota_root_iter_init(struct mailbox *box) { struct quota_root_iter *iter; iter = i_new(struct quota_root_iter, 1); iter->quota = box->list->ns->owner != NULL ? quota_get_mail_user_quota(box->list->ns->owner) : quota_get_mail_user_quota(box->list->ns->user); iter->box = box; return iter; } bool quota_root_is_namespace_visible(struct quota_root *root, struct mail_namespace *ns) { struct mailbox_list *list = ns->list; struct mail_storage *storage; /* this check works as long as there is only one storage per list */ if (mailbox_list_get_storage(&list, "", &storage) == 0 && (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) return FALSE; if (root->quota->unwanted_ns == ns) return FALSE; if (root->ns_prefix != NULL) { if (root->ns != ns) return FALSE; } else { if (ns->owner == NULL) return FALSE; } return TRUE; } static bool quota_root_is_visible(struct quota_root *root, struct mailbox *box, bool enforce) { if (root->no_enforcing && enforce) { /* we don't want to include this root in quota enforcing */ return FALSE; } if (!quota_root_is_namespace_visible(root, box->list->ns)) return FALSE; if (array_count(&root->quota->roots) == 1) { /* a single quota root: don't bother checking further */ return TRUE; } return root->backend.v.match_box == NULL ? TRUE : root->backend.v.match_box(root, box); } struct quota_root *quota_root_iter_next(struct quota_root_iter *iter) { struct quota_root *const *roots, *root = NULL; unsigned int count; if (iter->quota == NULL) return NULL; roots = array_get(&iter->quota->roots, &count); if (iter->i >= count) return NULL; for (; iter->i < count; iter->i++) { if (!quota_root_is_visible(roots[iter->i], iter->box, FALSE)) continue; root = roots[iter->i]; break; } iter->i++; return root; } void quota_root_iter_deinit(struct quota_root_iter **_iter) { struct quota_root_iter *iter = *_iter; *_iter = NULL; i_free(iter); } struct quota_root *quota_root_lookup(struct mail_user *user, const char *name) { struct quota *quota; struct quota_root *const *roots; unsigned int i, count; quota = quota_get_mail_user_quota(user); if (quota == NULL) return NULL; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { if (strcmp(roots[i]->set->name, name) == 0) return roots[i]; } return NULL; } const char *quota_root_get_name(struct quota_root *root) { return root->set->name; } const char *const *quota_root_get_resources(struct quota_root *root) { /* if we haven't checked the quota_over_flag yet, do it now */ quota_over_flag_check_root(root); return root->backend.v.get_resources(root); } bool quota_root_is_hidden(struct quota_root *root) { return root->hidden; } int quota_get_resource(struct quota_root *root, const char *mailbox_name, const char *name, uint64_t *value_r, uint64_t *limit_r) { uint64_t bytes_limit, count_limit; bool ignored, kilobytes = FALSE; int ret; *value_r = *limit_r = 0; if (strcmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) { name = QUOTA_NAME_STORAGE_BYTES; kilobytes = TRUE; } /* Get the value first. This call may also update quota limits if they're defined externally. */ ret = root->backend.v.get_resource(root, name, value_r); if (ret <= 0) return ret; if (quota_root_get_rule_limits(root, mailbox_name, &bytes_limit, &count_limit, &ignored) < 0) return -1; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) *limit_r = bytes_limit; else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) *limit_r = count_limit; else *limit_r = 0; if (kilobytes) { *value_r = (*value_r + 1023) / 1024; *limit_r = (*limit_r + 1023) / 1024; } return *limit_r == 0 ? 0 : 1; } int quota_set_resource(struct quota_root *root, const char *name, uint64_t value, const char **error_r) { struct dict_transaction_context *trans; const char *key; if (root->set->limit_set == NULL) { *error_r = MAIL_ERRSTR_NO_PERMISSION; return -1; } if (strcasecmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) key = "storage"; else if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) key = "bytes"; else if (strcasecmp(name, QUOTA_NAME_MESSAGES) == 0) key = "messages"; else { *error_r = t_strdup_printf("Unsupported resource name: %s", name); return -1; } if (root->limit_set_dict == NULL) { struct dict_settings set; i_zero(&set); set.username = root->quota->user->username; set.base_dir = root->quota->user->set->base_dir; if (mail_user_get_home(root->quota->user, &set.home_dir) <= 0) set.home_dir = NULL; if (dict_init_full(root->set->limit_set, &set, &root->limit_set_dict, error_r) < 0) return -1; } trans = dict_transaction_begin(root->limit_set_dict); key = t_strdup_printf(QUOTA_LIMIT_SET_PATH"%s", key); dict_set(trans, key, dec2str(value)); if (dict_transaction_commit(&trans) < 0) { *error_r = "Internal quota limit update error"; return -1; } return 0; } struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) { struct quota_transaction_context *ctx; struct quota_root *const *rootp; const struct quota_rule *rule; const char *mailbox_name; ctx = i_new(struct quota_transaction_context, 1); ctx->quota = box->list->ns->owner != NULL ? quota_get_mail_user_quota(box->list->ns->owner) : quota_get_mail_user_quota(box->list->ns->user); i_assert(ctx->quota != NULL); ctx->box = box; ctx->bytes_ceil = (uint64_t)-1; ctx->bytes_ceil2 = (uint64_t)-1; ctx->count_ceil = (uint64_t)-1; mailbox_name = mailbox_get_vname(box); (void)mail_namespace_find_unalias(box->storage->user->namespaces, &mailbox_name); ctx->auto_updating = TRUE; array_foreach(&ctx->quota->roots, rootp) { if (!quota_root_is_visible(*rootp, ctx->box, FALSE)) continue; rule = quota_root_rule_find((*rootp)->set, mailbox_name); if (rule != NULL && rule->ignore) { /* This mailbox isn't included in quota. This means it's also not included in quota_warnings, so make sure it's fully ignored. */ continue; } /* If there are reverse quota_warnings, we'll need to track how many bytes were expunged even with auto_updating roots. (An alternative could be to get the current quota usage before and after the expunges, but that's more complicated and probably isn't any better.) */ if (!(*rootp)->auto_updating || (*rootp)->set->have_reverse_warnings) ctx->auto_updating = FALSE; } if (box->storage->user->dsyncing) { /* ignore quota for dsync */ ctx->limits_set = TRUE; } return ctx; } int quota_transaction_set_limits(struct quota_transaction_context *ctx) { struct quota_root *const *roots; const char *mailbox_name; unsigned int i, count; uint64_t bytes_limit, count_limit, current, limit, diff; bool use_grace, ignored; int ret; if (ctx->limits_set) return 0; ctx->limits_set = TRUE; mailbox_name = mailbox_get_vname(ctx->box); /* use quota_grace only for LDA/LMTP */ use_grace = (ctx->box->flags & MAILBOX_FLAG_POST_SESSION) != 0; ctx->no_quota_updates = TRUE; /* find the lowest quota limits from all roots and use them */ roots = array_get(&ctx->quota->roots, &count); for (i = 0; i < count; i++) { if (!quota_root_is_visible(roots[i], ctx->box, TRUE)) continue; if (quota_root_get_rule_limits(roots[i], mailbox_name, &bytes_limit, &count_limit, &ignored) < 0) { ctx->failed = TRUE; return -1; } if (!ignored) ctx->no_quota_updates = FALSE; if (bytes_limit > 0) { ret = quota_get_resource(roots[i], mailbox_name, QUOTA_NAME_STORAGE_BYTES, ¤t, &limit); if (ret > 0) { if (limit <= current) { /* over quota */ ctx->bytes_ceil = 0; ctx->bytes_ceil2 = 0; diff = current - limit; if (ctx->bytes_over < diff) ctx->bytes_over = diff; } else { diff = limit - current; if (ctx->bytes_ceil2 > diff) ctx->bytes_ceil2 = diff; diff += !use_grace ? 0 : roots[i]->set->last_mail_max_extra_bytes; if (ctx->bytes_ceil > diff) ctx->bytes_ceil = diff; } } else if (ret < 0) { ctx->failed = TRUE; return -1; } } if (count_limit > 0) { ret = quota_get_resource(roots[i], mailbox_name, QUOTA_NAME_MESSAGES, ¤t, &limit); if (ret > 0) { if (limit <= current) { /* over quota */ ctx->count_ceil = 0; diff = current - limit; if (ctx->count_over < diff) ctx->count_over = diff; } else { diff = limit - current; if (ctx->count_ceil > diff) ctx->count_ceil = diff; } } else if (ret < 0) { ctx->failed = TRUE; return -1; } } } return 0; } static void quota_warning_execute(struct quota_root *root, const char *cmd, const char *last_arg, const char *reason) { const char *socket_path, *const *args; string_t *str; int fd; if (root->quota->set->debug) i_debug("quota: Executing warning: %s (because %s)", cmd, reason); args = t_strsplit_spaces(cmd, " "); if (last_arg != NULL) { unsigned int count = str_array_length(args); const char **new_args = t_new(const char *, count + 2); memcpy(new_args, args, sizeof(const char *) * count); new_args[count] = last_arg; args = new_args; } socket_path = args[0]; args++; if (*socket_path != '/') { socket_path = t_strconcat(root->quota->user->set->base_dir, "/", socket_path, NULL); } if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) { if (errno == EACCES) { i_error("quota: %s", eacces_error_get("net_connect_unix", socket_path)); } else { i_error("quota: net_connect_unix(%s) failed: %m", socket_path); } return; } str = t_str_new(1024); str_append(str, "VERSION\tscript\t4\t0\nnoreply\n"); for (; *args != NULL; args++) { str_append(str, *args); str_append_c(str, '\n'); } str_append_c(str, '\n'); net_set_nonblock(fd, FALSE); if (write_full(fd, str_data(str), str_len(str)) < 0) i_error("write(%s) failed: %m", socket_path); if (close(fd) < 0) i_error("close(%s) failed: %m", socket_path); } static void quota_warnings_execute(struct quota_transaction_context *ctx, struct quota_root *root) { struct quota_warning_rule *warnings; unsigned int i, count; uint64_t bytes_current, bytes_before, bytes_limit; uint64_t count_current, count_before, count_limit; const char *reason; warnings = array_get_modifiable(&root->set->warning_rules, &count); if (count == 0) return; if (quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES, &bytes_current, &bytes_limit) < 0) return; if (quota_get_resource(root, "", QUOTA_NAME_MESSAGES, &count_current, &count_limit) < 0) return; if (ctx->bytes_used > 0 && bytes_current < (uint64_t)ctx->bytes_used) bytes_before = 0; else bytes_before = bytes_current - ctx->bytes_used; if (ctx->count_used > 0 && count_current < (uint64_t)ctx->count_used) count_before = 0; else count_before = count_current - ctx->count_used; for (i = 0; i < count; i++) { if (quota_warning_match(&warnings[i], bytes_before, bytes_current, count_before, count_current, &reason)) { quota_warning_execute(root, warnings[i].command, NULL, reason); break; } } } int quota_transaction_commit(struct quota_transaction_context **_ctx) { struct quota_transaction_context *ctx = *_ctx; struct quota_rule *rule; struct quota_root *const *roots; unsigned int i, count; const char *mailbox_name; int ret = 0; *_ctx = NULL; if (ctx->failed) ret = -1; else if (ctx->bytes_used != 0 || ctx->count_used != 0 || ctx->recalculate != QUOTA_RECALCULATE_DONT) T_BEGIN { ARRAY(struct quota_root *) warn_roots; mailbox_name = mailbox_get_vname(ctx->box); (void)mail_namespace_find_unalias( ctx->box->storage->user->namespaces, &mailbox_name); roots = array_get(&ctx->quota->roots, &count); t_array_init(&warn_roots, count); for (i = 0; i < count; i++) { if (!quota_root_is_visible(roots[i], ctx->box, FALSE)) continue; rule = quota_root_rule_find(roots[i]->set, mailbox_name); if (rule != NULL && rule->ignore) { /* mailbox not included in quota */ continue; } if (roots[i]->backend.v.update(roots[i], ctx) < 0) ret = -1; else if (!ctx->sync_transaction) array_append(&warn_roots, &roots[i], 1); } /* execute quota warnings after all updates. this makes it work correctly regardless of whether backend.get_resource() returns updated values before backend.update() or not. warnings aren't executed when dsync bring the user over, because the user probably already got the warning on the other replica. */ array_foreach(&warn_roots, roots) quota_warnings_execute(ctx, *roots); } T_END; i_free(ctx); return ret; } static bool quota_over_flag_init_root(struct quota_root *root, const char **quota_over_script_r, const char **quota_over_flag_r, bool *status_r) { const char *name, *flag_mask; *quota_over_flag_r = NULL; *status_r = FALSE; name = t_strconcat(root->set->set_name, "_over_script", NULL); *quota_over_script_r = mail_user_plugin_getenv(root->quota->user, name); if (*quota_over_script_r == NULL) { if (root->quota->set->debug) { i_debug("quota: quota_over_flag check: " "%s unset - skipping", name); } return FALSE; } /* e.g.: quota_over_flag_value=TRUE or quota_over_flag_value=* */ name = t_strconcat(root->set->set_name, "_over_flag_value", NULL); flag_mask = mail_user_plugin_getenv(root->quota->user, name); if (flag_mask == NULL) { if (root->quota->set->debug) { i_debug("quota: quota_over_flag check: " "%s unset - skipping", name); } return FALSE; } /* compare quota_over_flag's value (that comes from userdb) to quota_over_flag_value and save the result. */ name = t_strconcat(root->set->set_name, "_over_flag", NULL); *quota_over_flag_r = mail_user_plugin_getenv(root->quota->user, name); *status_r = *quota_over_flag_r != NULL && wildcard_match_icase(*quota_over_flag_r, flag_mask); return TRUE; } static void quota_over_flag_check_root(struct quota_root *root) { const char *quota_over_script, *quota_over_flag; const char *const *resources; unsigned int i; uint64_t value, limit; bool cur_overquota = FALSE; bool quota_over_status; int ret; if (root->quota_over_flag_checked) return; if (root->quota->user->session_create_time + QUOTA_OVER_FLAG_MAX_DELAY_SECS < ioloop_time) { /* userdb's quota_over_flag lookup is too old. */ if (root->quota->set->debug) { i_debug("quota: quota_over_flag check: " "Flag lookup time is too old - skipping"); } return; } if (root->quota->user->session_restored) { /* we don't know whether the quota_over_script was executed before hibernation. just assume that it was, so we don't unnecessarily call it too often. */ if (root->quota->set->debug) { i_debug("quota: quota_over_flag check: " "Session was already hibernated - skipping"); } return; } root->quota_over_flag_checked = TRUE; if (!quota_over_flag_init_root(root, "a_over_script, "a_over_flag, "a_over_status)) return; resources = quota_root_get_resources(root); for (i = 0; resources[i] != NULL; i++) { ret = quota_get_resource(root, "", resources[i], &value, &limit); if (ret < 0) { /* can't reliably verify this */ if (root->quota->set->debug) { i_debug("quota: Quota %s lookup failed - can't verify quota_over_flag", resources[i]); } return; } if (root->quota->set->debug) { i_debug("quota: quota_over_flag check: %s ret=%d value=%llu limit=%llu", resources[i], ret, (unsigned long long)value, (unsigned long long)limit); } if (ret > 0 && value >= limit) cur_overquota = TRUE; } if (root->quota->set->debug) { i_debug("quota: quota_over_flag=%d(%s) vs currently overquota=%d", quota_over_status ? 1 : 0, quota_over_flag == NULL ? "(null)" : quota_over_flag, cur_overquota ? 1 : 0); } if (cur_overquota != quota_over_status) { quota_warning_execute(root, quota_over_script, quota_over_flag, "quota_over_flag mismatch"); } } void quota_over_flag_check_startup(struct quota *quota) { struct quota_root *const *roots; unsigned int i, count; const char *name; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { name = t_strconcat(roots[i]->set->set_name, "_over_flag_lazy_check", NULL); if (mail_user_plugin_getenv(roots[i]->quota->user, name) == NULL) quota_over_flag_check_root(roots[i]); } } void quota_transaction_rollback(struct quota_transaction_context **_ctx) { struct quota_transaction_context *ctx = *_ctx; *_ctx = NULL; i_free(ctx); } enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail) { uoff_t size; if (quota_transaction_set_limits(ctx) < 0) return QUOTA_ALLOC_RESULT_TEMPFAIL; if (ctx->no_quota_updates) return QUOTA_ALLOC_RESULT_OK; if (mail_get_physical_size(mail, &size) < 0) { enum mail_error error; const char *errstr = mailbox_get_last_internal_error(mail->box, &error); if (error == MAIL_ERROR_EXPUNGED) { /* mail being copied was already expunged. it'll fail, so just return success for the quota allocated. */ return QUOTA_ALLOC_RESULT_OK; } i_error("quota: Failed to get mail size (box=%s, uid=%u): %s", mail->box->vname, mail->uid, errstr); return QUOTA_ALLOC_RESULT_TEMPFAIL; } enum quota_alloc_result ret = quota_test_alloc(ctx, size); if (ret != QUOTA_ALLOC_RESULT_OK) return ret; /* with quota_try_alloc() we want to keep track of how many bytes we've been adding/removing, so disable auto_updating=TRUE optimization. this of course doesn't work perfectly if quota_alloc() or quota_free*() was already used within the same transaction, but that doesn't normally happen. */ ctx->auto_updating = FALSE; quota_alloc(ctx, mail); return QUOTA_ALLOC_RESULT_OK; } enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size) { if (ctx->failed) return QUOTA_ALLOC_RESULT_TEMPFAIL; if (quota_transaction_set_limits(ctx) < 0) return QUOTA_ALLOC_RESULT_TEMPFAIL; uoff_t max_size = ctx->quota->set->max_mail_size; if (max_size > 0 && size > max_size) return QUOTA_ALLOC_RESULT_OVER_MAXSIZE; if (ctx->no_quota_updates) return QUOTA_ALLOC_RESULT_OK; /* this is a virtual function mainly for trash plugin and similar, which may automatically delete mails to stay under quota. */ return ctx->quota->set->test_alloc(ctx, size); } static enum quota_alloc_result quota_default_test_alloc( struct quota_transaction_context *ctx, uoff_t size) { struct quota_root *const *roots; unsigned int i, count; bool ignore; int ret; if (!quota_transaction_is_over(ctx, size)) return QUOTA_ALLOC_RESULT_OK; /* limit reached. */ roots = array_get(&ctx->quota->roots, &count); for (i = 0; i < count; i++) { uint64_t bytes_limit, count_limit; if (!quota_root_is_visible(roots[i], ctx->box, TRUE)) continue; ret = quota_root_get_rule_limits(roots[i], mailbox_get_vname(ctx->box), &bytes_limit, &count_limit, &ignore); if (ret < 0) return QUOTA_ALLOC_RESULT_TEMPFAIL; /* if size is bigger than any limit, then it is bigger than the lowest limit */ if (bytes_limit > 0 && size > bytes_limit) return QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT; } return QUOTA_ALLOC_RESULT_OVER_QUOTA; } void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail) { uoff_t size; if (!ctx->auto_updating) { if (mail_get_physical_size(mail, &size) == 0) ctx->bytes_used += size; } ctx->bytes_ceil = ctx->bytes_ceil2; ctx->count_used++; } void quota_free(struct quota_transaction_context *ctx, struct mail *mail) { uoff_t size; if (ctx->auto_updating) return; if (mail_get_physical_size(mail, &size) < 0) quota_recalculate(ctx, QUOTA_RECALCULATE_MISSING_FREES); else quota_free_bytes(ctx, size); } void quota_free_bytes(struct quota_transaction_context *ctx, uoff_t physical_size) { ctx->bytes_used -= physical_size; ctx->count_used--; } void quota_recalculate(struct quota_transaction_context *ctx, enum quota_recalculate recalculate) { ctx->recalculate = recalculate; } static void hidden_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED) { _root->hidden = TRUE; } static void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED) { _root->disable_unlimited_tracking = TRUE; } static void noenforcing_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED) { _root->no_enforcing = TRUE; } static void ns_param_handler(struct quota_root *_root, const char *param_value) { _root->ns_prefix = p_strdup(_root->pool, param_value); } int quota_parse_parameters(struct quota_root *root, const char **args, const char **error_r, const struct quota_param_parser *valid_params, bool fail_on_unknown) { const char *tmp_param_name, *tmp_param_val; size_t tmp_param_len; while (*args != NULL && (*args)[0] != '\0') { for (; valid_params->param_name != NULL; ++valid_params) { tmp_param_name = valid_params->param_name; tmp_param_len = strlen(valid_params->param_name); i_assert(*args != NULL); if (strncmp(*args, tmp_param_name, tmp_param_len) == 0) { tmp_param_val = NULL; *args += tmp_param_len; if (tmp_param_name[tmp_param_len - 1] == '=') { const char *next_colon = strchr(*args, ':'); tmp_param_val = (next_colon == NULL)? t_strdup(*args): t_strdup_until(*args, next_colon); *args = (next_colon == NULL) ? NULL : next_colon + 1; } else if ((*args)[0] == '\0' || (*args)[0] == ':') { *args = ((*args)[0] == ':') ? *args + 1 : NULL; /* in case parameter is a boolean second parameter * string parameter value will be ignored by param_handler * we just need some non-NULL value * to indicate that argument is to be processed */ tmp_param_val = ""; } if (tmp_param_val != NULL) { valid_params->param_handler(root, tmp_param_val); break; } } } if (valid_params->param_name == NULL) { if (fail_on_unknown) { *error_r = t_strdup_printf( "Unknown parameter for backend %s: %s", root->backend.name, *args); return -1; } else { break; } } } return 0; } dovecot-2.2.33.2/src/plugins/quota/doveadm-quota.c0000644000175000017500000000723613165463624016662 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "quota-plugin.h" #include "quota-private.h" #include "doveadm-print.h" #include "doveadm-mail.h" const char *doveadm_quota_plugin_version = DOVECOT_ABI_VERSION; void doveadm_quota_plugin_init(struct module *module); void doveadm_quota_plugin_deinit(void); static void cmd_quota_get_root(struct quota_root *root) { const char *const *res; uint64_t value, limit; int ret; res = quota_root_get_resources(root); for (; *res != NULL; res++) { ret = quota_get_resource(root, "", *res, &value, &limit); doveadm_print(root->set->name); doveadm_print(*res); if (ret > 0) { doveadm_print_num(value); doveadm_print_num(limit); if (limit > 0) doveadm_print_num(value*100 / limit); else doveadm_print("0"); } else if (ret == 0) { doveadm_print_num(value); doveadm_print("-"); doveadm_print("0"); } else { doveadm_print("error"); doveadm_print("error"); doveadm_print("error"); } } } static int cmd_quota_get_run(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct quota_root *const *root; if (quser == NULL) { i_error("Quota not enabled"); doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } array_foreach(&quser->quota->roots, root) cmd_quota_get_root(*root); return 0; } static void cmd_quota_get_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[] ATTR_UNUSED) { doveadm_print_header("root", "Quota name", 0); doveadm_print_header("type", "Type", 0); doveadm_print_header("value", "Value", DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY); doveadm_print_header("limit", "Limit", DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY); doveadm_print_header("percent", "%", DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY); } static struct doveadm_mail_cmd_context * cmd_quota_get_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_quota_get_run; ctx->v.init = cmd_quota_get_init; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); return ctx; } static int cmd_quota_recalc_run(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct quota_root *const *root; struct quota_transaction_context trans; if (quser == NULL) { i_error("Quota not enabled"); doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } i_zero(&trans); trans.quota = quser->quota; trans.recalculate = QUOTA_RECALCULATE_FORCED; array_foreach(&quser->quota->roots, root) { (void)(*root)->backend.v.update(*root, &trans); if ((*root)->backend.v.flush != NULL) (*root)->backend.v.flush(*root); } return 0; } static struct doveadm_mail_cmd_context * cmd_quota_recalc_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_quota_recalc_run; return ctx; } static struct doveadm_cmd_ver2 quota_commands[] = { { .name = "quota get", .usage = "", .mail_cmd = cmd_quota_get_alloc, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAMS_END }, { .name = "quota recalc", .usage = "", .mail_cmd = cmd_quota_recalc_alloc, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAMS_END } }; void doveadm_quota_plugin_init(struct module *module ATTR_UNUSED) { unsigned int i; for (i = 0; i < N_ELEMENTS(quota_commands); i++) doveadm_cmd_register_ver2("a_commands[i]); } void doveadm_quota_plugin_deinit(void) { } dovecot-2.2.33.2/src/plugins/quota/quota-fs.c0000644000175000017500000005750313165463624015655 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ /* Only for reporting filesystem quota */ #include "lib.h" #include "array.h" #include "str.h" #include "hostpid.h" #include "mountpoint.h" #include "quota-private.h" #include "quota-fs.h" #ifdef HAVE_FS_QUOTA #include #include #include #include #ifdef HAVE_LINUX_DQBLK_XFS_H # include # define HAVE_XFS_QUOTA #elif defined (HAVE_XFS_XQM_H) # include /* CentOS 4.x at least uses this */ # define HAVE_XFS_QUOTA #endif #ifdef HAVE_RQUOTA # include "rquota.h" # define RQUOTA_GETQUOTA_TIMEOUT_SECS 10 #endif #ifndef DEV_BSIZE # ifdef DQBSIZE # define DEV_BSIZE DQBSIZE /* AIX */ # else # define DEV_BSIZE 512 # endif #endif #ifdef HAVE_STRUCT_DQBLK_CURSPACE # define dqb_curblocks dqb_curspace #endif /* Older sys/quota.h doesn't define _LINUX_QUOTA_VERSION at all, which means it supports only v1 quota */ #ifndef _LINUX_QUOTA_VERSION # define _LINUX_QUOTA_VERSION 1 #endif #define mount_type_is_nfs(mount) \ (strcmp((mount)->type, "nfs") == 0 || \ strcmp((mount)->type, "nfs4") == 0) struct fs_quota_mountpoint { int refcount; char *mount_path; char *device_path; char *type; unsigned int block_size; #ifdef FS_QUOTA_SOLARIS int fd; char *path; #endif }; struct fs_quota_root { struct quota_root root; char *storage_mount_path; uid_t uid; gid_t gid; struct fs_quota_mountpoint *mount; unsigned int inode_per_mail:1; unsigned int user_disabled:1; unsigned int group_disabled:1; #ifdef FS_QUOTA_NETBSD struct quotahandle *qh; #endif }; extern struct quota_backend quota_backend_fs; static struct quota_root *fs_quota_alloc(void) { struct fs_quota_root *root; root = i_new(struct fs_quota_root, 1); root->uid = geteuid(); root->gid = getegid(); return &root->root; } static void handle_user_param(struct quota_root *_root, const char *param_value ATTR_UNUSED) { ((struct fs_quota_root *)_root)->group_disabled = TRUE; } static void handle_group_param(struct quota_root *_root, const char *param_value ATTR_UNUSED) { ((struct fs_quota_root *)_root)->user_disabled = TRUE; } static void handle_inode_param(struct quota_root *_root, const char *param_value ATTR_UNUSED) { ((struct fs_quota_root *)_root)->inode_per_mail = TRUE; } static void handle_mount_param(struct quota_root *_root, const char *param_value) { ((struct fs_quota_root *)_root)->storage_mount_path = i_strdup(param_value); } static int fs_quota_init(struct quota_root *_root, const char *args, const char **error_r) { const struct quota_param_parser fs_params[] = { {.param_name = "user", .param_handler = handle_user_param}, {.param_name = "group", .param_handler = handle_group_param}, {.param_name = "mount", .param_handler = handle_mount_param}, {.param_name = "inode_per_mail", .param_handler = handle_inode_param}, quota_param_hidden, quota_param_noenforcing, quota_param_ns, {.param_name = NULL} }; if (quota_parse_parameters(_root, &args, error_r, fs_params, TRUE) < 0) return -1; _root->auto_updating = TRUE; return 0; } static void fs_quota_mountpoint_free(struct fs_quota_mountpoint *mount) { if (--mount->refcount > 0) return; #ifdef FS_QUOTA_SOLARIS if (mount->fd != -1) { if (close(mount->fd) < 0) i_error("close(%s) failed: %m", mount->path); } i_free(mount->path); #endif i_free(mount->device_path); i_free(mount->mount_path); i_free(mount->type); i_free(mount); } static void fs_quota_deinit(struct quota_root *_root) { struct fs_quota_root *root = (struct fs_quota_root *)_root; if (root->mount != NULL) fs_quota_mountpoint_free(root->mount); i_free(root->storage_mount_path); i_free(root); } static struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir) { struct fs_quota_mountpoint *mount; struct mountpoint point; int ret; ret = mountpoint_get(dir, default_pool, &point); if (ret <= 0) return NULL; mount = i_new(struct fs_quota_mountpoint, 1); mount->refcount = 1; mount->device_path = point.device_path; mount->mount_path = point.mount_path; mount->type = point.type; mount->block_size = point.block_size; #ifdef FS_QUOTA_SOLARIS mount->fd = -1; #endif if (mount_type_is_nfs(mount)) { if (strchr(mount->device_path, ':') == NULL) { i_error("quota-fs: %s is not a valid NFS device path", mount->device_path); fs_quota_mountpoint_free(mount); return NULL; } } return mount; } #define QUOTA_ROOT_MATCH(root, mount) \ ((root)->root.backend.name == quota_backend_fs.name && \ ((root)->storage_mount_path == NULL || \ strcmp((root)->storage_mount_path, (mount)->mount_path) == 0)) static struct fs_quota_root * fs_quota_root_find_mountpoint(struct quota *quota, const struct fs_quota_mountpoint *mount) { struct quota_root *const *roots; struct fs_quota_root *empty = NULL; unsigned int i, count; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { struct fs_quota_root *root = (struct fs_quota_root *)roots[i]; if (QUOTA_ROOT_MATCH(root, mount)) { if (root->mount == NULL) empty = root; else if (strcmp(root->mount->mount_path, mount->mount_path) == 0) return root; } } return empty; } static void fs_quota_mount_init(struct fs_quota_root *root, struct fs_quota_mountpoint *mount, const char *dir) { struct quota_root *const *roots; unsigned int i, count; #ifdef FS_QUOTA_SOLARIS #ifdef HAVE_RQUOTA if (mount_type_is_nfs(mount)) { /* using rquota for this mount */ } else #endif if (mount->path == NULL) { mount->path = i_strconcat(mount->mount_path, "/quotas", NULL); mount->fd = open(mount->path, O_RDONLY); if (mount->fd == -1 && errno != ENOENT) i_error("open(%s) failed: %m", mount->path); } #endif root->mount = mount; if (root->root.quota->set->debug) { i_debug("fs quota add mailbox dir = %s", dir); i_debug("fs quota block device = %s", mount->device_path); i_debug("fs quota mount point = %s", mount->mount_path); i_debug("fs quota mount type = %s", mount->type); } /* if there are more unused quota roots, copy this mount to them */ roots = array_get(&root->root.quota->roots, &count); for (i = 0; i < count; i++) { root = (struct fs_quota_root *)roots[i]; if (QUOTA_ROOT_MATCH(root, mount) && root->mount == NULL) { mount->refcount++; root->mount = mount; } } } static void fs_quota_add_missing_mounts(struct quota *quota) { struct fs_quota_mountpoint *mount; struct quota_root *const *roots; unsigned int i, count; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { struct fs_quota_root *root = (struct fs_quota_root *)roots[i]; if (root->root.backend.name != quota_backend_fs.name || root->storage_mount_path == NULL || root->mount != NULL) continue; mount = fs_quota_mountpoint_get(root->storage_mount_path); if (mount != NULL) { fs_quota_mount_init(root, mount, root->storage_mount_path); } } } static void fs_quota_namespace_added(struct quota *quota, struct mail_namespace *ns) { struct fs_quota_mountpoint *mount; struct fs_quota_root *root; const char *dir; if (!mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_MAILBOX, &dir)) mount = NULL; else mount = fs_quota_mountpoint_get(dir); if (mount != NULL) { root = fs_quota_root_find_mountpoint(quota, mount); if (root != NULL && root->mount == NULL) fs_quota_mount_init(root, mount, dir); else fs_quota_mountpoint_free(mount); } /* we would actually want to do this only once after all quota roots are created, but there's no way to do this right now */ fs_quota_add_missing_mounts(quota); } static const char *const * fs_quota_root_get_resources(struct quota_root *_root) { struct fs_quota_root *root = (struct fs_quota_root *)_root; static const char *resources_kb[] = { QUOTA_NAME_STORAGE_KILOBYTES, NULL }; static const char *resources_kb_messages[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return root->inode_per_mail ? resources_kb_messages : resources_kb; } #if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX) || \ defined(FS_QUOTA_NETBSD) || defined(HAVE_RQUOTA) static void fs_quota_root_disable(struct fs_quota_root *root, bool group) { if (group) root->group_disabled = TRUE; else root->user_disabled = TRUE; } #endif #ifdef HAVE_RQUOTA static void rquota_get_result(const rquota *rq, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { /* use soft limits if they exist, fallback to hard limits */ /* convert the results from blocks to bytes */ *bytes_value_r = (uint64_t)rq->rq_curblocks * (uint64_t)rq->rq_bsize; if (rq->rq_bsoftlimit != 0) { *bytes_limit_r = (uint64_t)rq->rq_bsoftlimit * (uint64_t)rq->rq_bsize; } else { *bytes_limit_r = (uint64_t)rq->rq_bhardlimit * (uint64_t)rq->rq_bsize; } *count_value_r = rq->rq_curfiles; if (rq->rq_fsoftlimit != 0) *count_limit_r = rq->rq_fsoftlimit; else *count_limit_r = rq->rq_fhardlimit; } static int do_rquota_user(struct fs_quota_root *root, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct getquota_rslt result; struct getquota_args args; struct timeval timeout; enum clnt_stat call_status; CLIENT *cl; struct fs_quota_mountpoint *mount = root->mount; const char *host; char *path; path = strchr(mount->device_path, ':'); i_assert(path != NULL); host = t_strdup_until(mount->device_path, path); path++; /* For NFSv4, we send the filesystem path without initial /. Server prepends proper NFS pseudoroot automatically and uses this for detection of NFSv4 mounts. */ if (strcmp(root->mount->type, "nfs4") == 0) { while (*path == '/') path++; } if (root->root.quota->set->debug) { i_debug("quota-fs: host=%s, path=%s, uid=%s", host, path, dec2str(root->uid)); } /* clnt_create() polls for a while to establish a connection */ cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp"); if (cl == NULL) { i_error("quota-fs: could not contact RPC service on %s", host); return -1; } /* Establish some RPC credentials */ auth_destroy(cl->cl_auth); cl->cl_auth = authunix_create_default(); /* make the rquota call on the remote host */ args.gqa_pathp = path; args.gqa_uid = root->uid; timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS; timeout.tv_usec = 0; call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&args, (xdrproc_t)xdr_getquota_rslt, (char *)&result, timeout); /* the result has been deserialized, let the client go */ auth_destroy(cl->cl_auth); clnt_destroy(cl); if (call_status != RPC_SUCCESS) { const char *rpc_error_msg = clnt_sperrno(call_status); i_error("quota-fs: remote rquota call failed: %s", rpc_error_msg); return -1; } switch (result.status) { case Q_OK: { rquota_get_result(&result.getquota_rslt_u.gqr_rquota, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); if (root->root.quota->set->debug) { i_debug("quota-fs: uid=%s, bytes=%llu/%llu files=%llu/%llu", dec2str(root->uid), (unsigned long long)*bytes_value_r, (unsigned long long)*bytes_limit_r, (unsigned long long)*count_value_r, (unsigned long long)*count_limit_r); } return 1; } case Q_NOQUOTA: if (root->root.quota->set->debug) { i_debug("quota-fs: uid=%s, limit=unlimited", dec2str(root->uid)); } fs_quota_root_disable(root, FALSE); return 0; case Q_EPERM: i_error("quota-fs: permission denied to rquota service"); return -1; default: i_error("quota-fs: unrecognized status code (%d) " "from rquota service", result.status); return -1; } } static int do_rquota_group(struct fs_quota_root *root ATTR_UNUSED, uint64_t *bytes_value_r ATTR_UNUSED, uint64_t *bytes_limit_r ATTR_UNUSED, uint64_t *count_value_r ATTR_UNUSED, uint64_t *count_limit_r ATTR_UNUSED) { #if defined(EXT_RQUOTAVERS) && defined(GRPQUOTA) struct getquota_rslt result; ext_getquota_args args; struct timeval timeout; enum clnt_stat call_status; CLIENT *cl; struct fs_quota_mountpoint *mount = root->mount; const char *host; char *path; path = strchr(mount->device_path, ':'); i_assert(path != NULL); host = t_strdup_until(mount->device_path, path); path++; if (root->root.quota->set->debug) { i_debug("quota-fs: host=%s, path=%s, gid=%s", host, path, dec2str(root->gid)); } /* clnt_create() polls for a while to establish a connection */ cl = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp"); if (cl == NULL) { i_error("quota-fs: could not contact RPC service on %s (group)", host); return -1; } /* Establish some RPC credentials */ auth_destroy(cl->cl_auth); cl->cl_auth = authunix_create_default(); /* make the rquota call on the remote host */ args.gqa_pathp = path; args.gqa_id = root->gid; args.gqa_type = GRPQUOTA; timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS; timeout.tv_usec = 0; call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&args, (xdrproc_t)xdr_getquota_rslt, (char *)&result, timeout); /* the result has been deserialized, let the client go */ auth_destroy(cl->cl_auth); clnt_destroy(cl); if (call_status != RPC_SUCCESS) { const char *rpc_error_msg = clnt_sperrno(call_status); i_error("quota-fs: remote ext rquota call failed: %s", rpc_error_msg); return -1; } switch (result.status) { case Q_OK: { rquota_get_result(&result.getquota_rslt_u.gqr_rquota, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); if (root->root.quota->set->debug) { i_debug("quota-fs: gid=%s, bytes=%llu/%llu files=%llu/%llu", dec2str(root->gid), (unsigned long long)*bytes_value_r, (unsigned long long)*bytes_limit_r, (unsigned long long)*count_value_r, (unsigned long long)*count_limit_r); } return 1; } case Q_NOQUOTA: if (root->root.quota->set->debug) { i_debug("quota-fs: gid=%s, limit=unlimited", dec2str(root->gid)); } fs_quota_root_disable(root, TRUE); return 0; case Q_EPERM: i_error("quota-fs: permission denied to ext rquota service"); return -1; default: i_error("quota-fs: unrecognized status code (%d) " "from ext rquota service", result.status); return -1; } #else i_error("quota-fs: rquota not compiled with group support"); return -1; #endif } #endif #ifdef FS_QUOTA_LINUX static int fs_quota_get_linux(struct fs_quota_root *root, bool group, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct dqblk dqblk; int type, id; type = group ? GRPQUOTA : USRQUOTA; id = group ? root->gid : root->uid; #ifdef HAVE_XFS_QUOTA if (strcmp(root->mount->type, "xfs") == 0) { struct fs_disk_quota xdqblk; if (quotactl(QCMD(Q_XGETQUOTA, type), root->mount->device_path, id, (caddr_t)&xdqblk) < 0) { if (errno == ESRCH) { fs_quota_root_disable(root, group); return 0; } i_error("%d quotactl(Q_XGETQUOTA, %s) failed: %m", errno, root->mount->device_path); return -1; } /* values always returned in 512 byte blocks */ *bytes_value_r = xdqblk.d_bcount * 512; *bytes_limit_r = xdqblk.d_blk_softlimit * 512; if (*bytes_limit_r == 0) { *bytes_limit_r = xdqblk.d_blk_hardlimit * 512; } *count_value_r = xdqblk.d_icount; *count_limit_r = xdqblk.d_ino_softlimit; if (*count_limit_r == 0) { *count_limit_r = xdqblk.d_ino_hardlimit; } } else #endif { /* ext2, ext3 */ if (quotactl(QCMD(Q_GETQUOTA, type), root->mount->device_path, id, (caddr_t)&dqblk) < 0) { if (errno == ESRCH) { fs_quota_root_disable(root, group); return 0; } i_error("quotactl(Q_GETQUOTA, %s) failed: %m", root->mount->device_path); if (errno == EINVAL) { i_error("Dovecot was compiled with Linux quota " "v%d support, try changing it " "(CPPFLAGS=-D_LINUX_QUOTA_VERSION=%d configure)", _LINUX_QUOTA_VERSION, _LINUX_QUOTA_VERSION == 1 ? 2 : 1); } return -1; } #if _LINUX_QUOTA_VERSION == 1 *bytes_value_r = dqblk.dqb_curblocks * 1024; #else *bytes_value_r = dqblk.dqb_curblocks; #endif *bytes_limit_r = dqblk.dqb_bsoftlimit * 1024; if (*bytes_limit_r == 0) { *bytes_limit_r = dqblk.dqb_bhardlimit * 1024; } *count_value_r = dqblk.dqb_curinodes; *count_limit_r = dqblk.dqb_isoftlimit; if (*count_limit_r == 0) { *count_limit_r = dqblk.dqb_ihardlimit; } } return 1; } #endif #ifdef FS_QUOTA_BSDAIX static int fs_quota_get_bsdaix(struct fs_quota_root *root, bool group, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct dqblk dqblk; int type, id; type = group ? GRPQUOTA : USRQUOTA; id = group ? root->gid : root->uid; if (quotactl(root->mount->mount_path, QCMD(Q_GETQUOTA, type), id, (void *)&dqblk) < 0) { if (errno == ESRCH) { fs_quota_root_disable(root, group); return 0; } i_error("quotactl(Q_GETQUOTA, %s) failed: %m", root->mount->mount_path); return -1; } *bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE; *bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE; if (*bytes_limit_r == 0) { *bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE; } *count_value_r = dqblk.dqb_curinodes; *count_limit_r = dqblk.dqb_isoftlimit; if (*count_limit_r == 0) { *count_limit_r = dqblk.dqb_ihardlimit; } return 1; } #endif #ifdef FS_QUOTA_NETBSD static int fs_quota_get_netbsd(struct fs_quota_root *root, bool group, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct quotakey qk; struct quotaval qv; struct quotahandle *qh; int ret; if ((qh = quota_open(root->mount->mount_path)) == NULL) { i_error("cannot open quota for %s: %m", root->mount->mount_path); fs_quota_root_disable(root, group); return 0; } qk.qk_idtype = group ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER; qk.qk_id = group ? root->gid : root->uid; for (int i = 0; i < 2; i++) { qk.qk_objtype = i == 0 ? QUOTA_OBJTYPE_BLOCKS : QUOTA_OBJTYPE_FILES; if (quota_get(qh, &qk, &qv) != 0) { if (errno == ESRCH) { fs_quota_root_disable(root, group); return 0; } i_error("quotactl(Q_GETQUOTA, %s) failed: %m", root->mount->mount_path); ret = -1; break; } if (i == 0) { *bytes_value_r = qv.qv_usage * DEV_BSIZE; *bytes_limit_r = qv.qv_softlimit * DEV_BSIZE; } else { *count_value_r = qv.qv_usage; *count_limit_r = qv.qv_softlimit; } ret = 1; } quota_close(qh); return ret; } #endif #ifdef FS_QUOTA_HPUX static int fs_quota_get_hpux(struct fs_quota_root *root, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct dqblk dqblk; if (quotactl(Q_GETQUOTA, root->mount->device_path, root->uid, &dqblk) < 0) { if (errno == ESRCH) { root->user_disabled = TRUE; return 0; } i_error("quotactl(Q_GETQUOTA, %s) failed: %m", root->mount->device_path); return -1; } *bytes_value_r = (uint64_t)dqblk.dqb_curblocks * root->mount->block_size; *bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * root->mount->block_size; if (*bytes_limit_r == 0) { *bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * root->mount->block_size; } *count_value_r = dqblk.dqb_curfiles; *count_limit_r = dqblk.dqb_fsoftlimit; if (*count_limit_r == 0) { *count_limit_r = dqblk.dqb_fhardlimit; } return 1; } #endif #ifdef FS_QUOTA_SOLARIS static int fs_quota_get_solaris(struct fs_quota_root *root, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { struct dqblk dqblk; struct quotctl ctl; if (root->mount->fd == -1) return 0; ctl.op = Q_GETQUOTA; ctl.uid = root->uid; ctl.addr = (caddr_t)&dqblk; if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) { i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path); return -1; } *bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE; *bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE; if (*bytes_limit_r == 0) { *bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE; } *count_value_r = dqblk.dqb_curfiles; *count_limit_r = dqblk.dqb_fsoftlimit; if (*count_limit_r == 0) { *count_limit_r = dqblk.dqb_fhardlimit; } return 1; } #endif static int fs_quota_get_resources(struct fs_quota_root *root, bool group, uint64_t *bytes_value_r, uint64_t *bytes_limit_r, uint64_t *count_value_r, uint64_t *count_limit_r) { if (group) { if (root->group_disabled) return 0; } else { if (root->user_disabled) return 0; } #ifdef FS_QUOTA_LINUX return fs_quota_get_linux(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); #elif defined (FS_QUOTA_NETBSD) return fs_quota_get_netbsd(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); #elif defined (FS_QUOTA_BSDAIX) return fs_quota_get_bsdaix(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); #else if (group) { /* not supported */ return 0; } #ifdef FS_QUOTA_HPUX return fs_quota_get_hpux(root, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); #else return fs_quota_get_solaris(root, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r); #endif #endif } static bool fs_quota_match_box(struct quota_root *_root, struct mailbox *box) { struct fs_quota_root *root = (struct fs_quota_root *)_root; struct stat mst, rst; const char *mailbox_path; bool match; if (root->storage_mount_path == NULL) return TRUE; if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &mailbox_path) <= 0) return FALSE; if (stat(mailbox_path, &mst) < 0) { if (errno != ENOENT) i_error("stat(%s) failed: %m", mailbox_path); return FALSE; } if (stat(root->storage_mount_path, &rst) < 0) { if (_root->quota->set->debug) { i_debug("stat(%s) failed: %m", root->storage_mount_path); } return FALSE; } match = CMP_DEV_T(mst.st_dev, rst.st_dev); if (_root->quota->set->debug) { i_debug("box=%s mount=%s match=%s", mailbox_path, root->storage_mount_path, match ? "yes" : "no"); } return match; } static int fs_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct fs_quota_root *root = (struct fs_quota_root *)_root; uint64_t bytes_value, count_value; uint64_t bytes_limit = 0, count_limit = 0; int ret; *value_r = 0; if (root->mount == NULL || (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 && strcasecmp(name, QUOTA_NAME_MESSAGES) != 0)) return 0; #ifdef HAVE_RQUOTA if (mount_type_is_nfs(root->mount)) { T_BEGIN { ret = root->user_disabled ? 0 : do_rquota_user(root, &bytes_value, &bytes_limit, &count_value, &count_limit); if (ret == 0 && !root->group_disabled) ret = do_rquota_group(root, &bytes_value, &bytes_limit, &count_value, &count_limit); } T_END; } else #endif { ret = fs_quota_get_resources(root, FALSE, &bytes_value, &bytes_limit, &count_value, &count_limit); if (ret == 0) { /* fallback to group quota */ ret = fs_quota_get_resources(root, TRUE, &bytes_value, &bytes_limit, &count_value, &count_limit); } } if (ret <= 0) return ret; if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) *value_r = bytes_value; else *value_r = count_value; if (_root->bytes_limit != (int64_t)bytes_limit || _root->count_limit != (int64_t)count_limit) { /* update limit */ _root->bytes_limit = bytes_limit; _root->count_limit = count_limit; /* limits have changed, so we'll need to recalculate relative quota rules */ quota_root_recalculate_relative_rules(_root->set, bytes_limit, count_limit); } return 1; } static int fs_quota_update(struct quota_root *root ATTR_UNUSED, struct quota_transaction_context *ctx ATTR_UNUSED) { return 0; } struct quota_backend quota_backend_fs = { "fs", { fs_quota_alloc, fs_quota_init, fs_quota_deinit, NULL, NULL, fs_quota_namespace_added, fs_quota_root_get_resources, fs_quota_get_resource, fs_quota_update, fs_quota_match_box, NULL } }; #endif dovecot-2.2.33.2/src/plugins/quota/quota-status-settings.h0000644000175000017500000000032413147010712020402 00000000000000#ifndef QUOTA_STATUS_SETTINGS_H #define QUOTA_STATUS_SETTINGS_H 1 struct quota_status_settings { char *recipient_delimiter; }; extern const struct setting_parser_info quota_status_setting_parser_info; #endif dovecot-2.2.33.2/src/plugins/quota/quota-private.h0000644000175000017500000001565513165463624016726 00000000000000#ifndef QUOTA_PRIVATE_H #define QUOTA_PRIVATE_H #include "mail-storage-private.h" #include "mail-namespace.h" #include "quota.h" /* Modules should use do "my_id = quota_module_id++" and use quota_module_contexts[id] for their own purposes. */ extern unsigned int quota_module_id; struct quota { struct mail_user *user; struct quota_settings *set; ARRAY(struct quota_root *) roots; ARRAY(struct mail_namespace *) namespaces; struct mail_namespace *unwanted_ns; }; struct quota_settings { pool_t pool; ARRAY(struct quota_root_settings *) root_sets; enum quota_alloc_result (*test_alloc)( struct quota_transaction_context *ctx, uoff_t size); uoff_t max_mail_size; const char *quota_exceeded_msg; unsigned int debug:1; unsigned int initialized:1; unsigned int vsizes:1; }; struct quota_rule { const char *mailbox_mask; int64_t bytes_limit, count_limit; /* relative to default_rule */ int bytes_percent, count_percent; /* Don't include this mailbox in quota */ unsigned int ignore:1; }; struct quota_warning_rule { struct quota_rule rule; const char *command; unsigned int reverse:1; }; struct quota_backend_vfuncs { struct quota_root *(*alloc)(void); int (*init)(struct quota_root *root, const char *args, const char **error_r); void (*deinit)(struct quota_root *root); bool (*parse_rule)(struct quota_root_settings *root_set, struct quota_rule *rule, const char *str, const char **error_r); int (*init_limits)(struct quota_root *root); /* called once for each namespace */ void (*namespace_added)(struct quota *quota, struct mail_namespace *ns); const char *const *(*get_resources)(struct quota_root *root); int (*get_resource)(struct quota_root *root, const char *name, uint64_t *value_r); int (*update)(struct quota_root *root, struct quota_transaction_context *ctx); bool (*match_box)(struct quota_root *root, struct mailbox *box); void (*flush)(struct quota_root *root); }; struct quota_backend { /* quota backends equal if backend1.name == backend2.name */ const char *name; struct quota_backend_vfuncs v; }; struct quota_root_settings { /* Unique quota root name. */ const char *name; /* Name in settings, e.g. "quota", "quota2", .. */ const char *set_name; struct quota_settings *set; const char *args; const struct quota_backend *backend; struct quota_rule default_rule; ARRAY(struct quota_rule) rules; ARRAY(struct quota_warning_rule) warning_rules; const char *limit_set; /* If user is under quota before saving a mail, allow the last mail to bring the user over quota by this many bytes. */ uint64_t last_mail_max_extra_bytes; struct quota_rule grace_rule; /* Limits in default_rule override backend's quota limits */ unsigned int force_default_rule:1; /* TRUE if any of the warning_rules have reverse==TRUE */ unsigned int have_reverse_warnings:1; }; struct quota_root { pool_t pool; struct quota_root_settings *set; struct quota *quota; struct quota_backend backend; struct dict *limit_set_dict; /* this quota root applies only to this namespace. it may also be a public namespace without an owner. */ struct mail_namespace *ns; /* this is set in quota init(), because namespaces aren't known yet. when accessing shared users the ns_prefix may be non-NULL but ns=NULL, so when checking if quota root applies only to a specific namespace use the ns_prefix!=NULL check. */ const char *ns_prefix; /* initially the same as set->default_rule.*_limit, but some backends may change these by reading the limits elsewhere (e.g. Maildir++, FS quota) */ int64_t bytes_limit, count_limit; /* Module-specific contexts. See quota_module_id. */ ARRAY(void) quota_module_contexts; /* don't enforce quota when saving */ unsigned int no_enforcing:1; /* quota is automatically updated. update() should be called but the bytes won't be changed. count is still changed, because it's cheap to do and it's internally used to figure out whether there have been some changes and that quota_warnings should be checked. */ unsigned int auto_updating:1; /* If user has unlimited quota, disable quota tracking */ unsigned int disable_unlimited_tracking:1; /* Set while quota is being recalculated to avoid recursion. */ unsigned int recounting:1; /* Quota root is hidden (to e.g. IMAP GETQUOTAROOT) */ unsigned int hidden:1; /* Did we already check quota_over_flag correctness? */ unsigned int quota_over_flag_checked:1; }; struct quota_transaction_context { union mailbox_transaction_module_context module_ctx; struct quota *quota; struct mailbox *box; int64_t bytes_used, count_used; /* how many bytes/mails can be saved until limit is reached. (set once, not updated by bytes_used/count_used). if last_mail_max_extra_bytes>0, the bytes_ceil is initially increased by that much, while bytes_ceil2 contains the real ceiling. after the first allocation is done, bytes_ceil is set to bytes_ceil2. */ uint64_t bytes_ceil, bytes_ceil2, count_ceil; /* How many bytes/mails we are over quota. Like *_ceil, these are set only once and not updated by bytes_used/count_used. (Either *_ceil or *_over is always zero.) */ uint64_t bytes_over, count_over; struct mail *tmp_mail; enum quota_recalculate recalculate; unsigned int limits_set:1; unsigned int failed:1; unsigned int sync_transaction:1; /* TRUE if all roots have auto_updating=TRUE */ unsigned int auto_updating:1; /* Quota doesn't need to be updated within this transaction. */ unsigned int no_quota_updates:1; }; /* Register storage to all user's quota roots. */ void quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns); void quota_remove_user_namespace(struct mail_namespace *ns); int quota_root_default_init(struct quota_root *root, const char *args, const char **error_r); struct quota *quota_get_mail_user_quota(struct mail_user *user); bool quota_root_is_namespace_visible(struct quota_root *root, struct mail_namespace *ns); struct quota_rule * quota_root_rule_find(struct quota_root_settings *root_set, const char *name); void quota_root_recalculate_relative_rules(struct quota_root_settings *root_set, int64_t bytes_limit, int64_t count_limit); /* Returns 1 if values were returned successfully, 0 if we're recursing into the same function, -1 if error. */ int quota_count(struct quota_root *root, uint64_t *bytes_r, uint64_t *count_r); int quota_root_parse_grace(struct quota_root_settings *root_set, const char *value, const char **error_r); bool quota_warning_match(const struct quota_warning_rule *w, uint64_t bytes_before, uint64_t bytes_current, uint64_t count_before, uint64_t count_current, const char **reason_r); bool quota_transaction_is_over(struct quota_transaction_context *ctx, uoff_t size); int quota_transaction_set_limits(struct quota_transaction_context *ctx); void quota_backend_register(const struct quota_backend *backend); void quota_backend_unregister(const struct quota_backend *backend); #endif dovecot-2.2.33.2/src/plugins/quota/quota-storage.c0000644000175000017500000005304013165463624016701 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "mail-search-build.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "maildir-storage.h" #include "index-mailbox-size.h" #include "quota-private.h" #include "quota-plugin.h" #include #define QUOTA_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_storage_module) #define QUOTA_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_mail_module) #define QUOTA_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_mailbox_list_module) struct quota_mailbox_list { union mailbox_list_module_context module_ctx; }; struct quota_mailbox { union mailbox_module_context module_ctx; struct mailbox_transaction_context *expunge_trans; struct quota_transaction_context *expunge_qt; ARRAY(uint32_t) expunge_uids; ARRAY(uoff_t) expunge_sizes; unsigned int prev_idx; unsigned int recalculate:1; unsigned int sync_transaction_expunge:1; }; struct quota_user_module quota_user_module = MODULE_CONTEXT_INIT(&mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(quota_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register); static MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module, &mailbox_list_module_register); static void quota_set_storage_error(struct quota_transaction_context *qt, struct mail_storage *storage, enum quota_alloc_result res) { const char *errstr = quota_alloc_result_errstr(res, qt); switch (res) { case QUOTA_ALLOC_RESULT_OVER_MAXSIZE: mail_storage_set_error(storage, MAIL_ERROR_LIMIT, errstr); break; case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT: case QUOTA_ALLOC_RESULT_OVER_QUOTA: mail_storage_set_error(storage, MAIL_ERROR_NOQUOTA, errstr); break; case QUOTA_ALLOC_RESULT_TEMPFAIL: mail_storage_set_internal_error(storage); break; case QUOTA_ALLOC_RESULT_OK: i_unreached(); } } static void quota_mail_expunge(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box); struct quota_user *quser = QUOTA_USER_CONTEXT(_mail->box->storage->user); union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail); struct quota_transaction_context *qt = QUOTA_CONTEXT(_mail->transaction); uoff_t size; int ret; if (qt->auto_updating) { qmail->super.expunge(_mail); return; } /* We need to handle the situation where multiple transactions expunged the mail at the same time. In here we'll just save the message's physical size and do the quota freeing later when the message was known to be expunged. */ if (quser->quota->set->vsizes) ret = mail_get_virtual_size(_mail, &size); else ret = mail_get_physical_size(_mail, &size); if (ret == 0) { if (!array_is_created(&qbox->expunge_uids)) { i_array_init(&qbox->expunge_uids, 64); i_array_init(&qbox->expunge_sizes, 64); } array_append(&qbox->expunge_uids, &_mail->uid, 1); array_append(&qbox->expunge_sizes, &size, 1); if ((_mail->transaction->flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0) { /* we're running dsync. if this brings the quota below a negative quota warning, don't execute it, because it probably was already executed by the replica. */ qbox->sync_transaction_expunge = TRUE; } else { qbox->sync_transaction_expunge = FALSE; } } qmail->super.expunge(_mail); } static int quota_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); struct quota_transaction_context *qt; int ret = 0; if ((items & STATUS_CHECK_OVER_QUOTA) != 0) { qt = quota_transaction_begin(box); enum quota_alloc_result qret = quota_test_alloc(qt, 0); if (qret != QUOTA_ALLOC_RESULT_OK) { quota_set_storage_error(qt, box->storage, qret); ret = -1; } quota_transaction_rollback(&qt); if ((items & ~STATUS_CHECK_OVER_QUOTA) == 0) { /* don't bother calling parent, it may unnecessarily try to open the mailbox */ return ret < 0 ? -1 : 0; } } if (qbox->module_ctx.super.get_status(box, items, status_r) < 0) ret = -1; return ret < 0 ? -1 : 0; } static struct mailbox_transaction_context * quota_mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); struct mailbox_transaction_context *t; struct quota_transaction_context *qt; t = qbox->module_ctx.super.transaction_begin(box, flags); qt = quota_transaction_begin(box); qt->sync_transaction = (flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0; MODULE_CONTEXT_SET(t, quota_storage_module, qt); return t; } static int quota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box); struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx); i_assert(qt->tmp_mail == NULL); if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) { quota_transaction_rollback(&qt); return -1; } else { (void)quota_transaction_commit(&qt); return 0; } } static void quota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx) { struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box); struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx); i_assert(qt->tmp_mail == NULL); qbox->module_ctx.super.transaction_rollback(ctx); quota_transaction_rollback(&qt); } void quota_mail_allocated(struct mail *_mail) { struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box); struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *qmail; if (qbox == NULL) return; qmail = p_new(mail->pool, union mail_module_context, 1); qmail->super = *v; mail->vlast = &qmail->super; v->expunge = quota_mail_expunge; MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail); } static bool quota_move_requires_check(struct mailbox *dest_box, struct mailbox *src_box) { struct mail_namespace *src_ns = src_box->list->ns; struct mail_namespace *dest_ns = dest_box->list->ns; struct quota_user *quser = QUOTA_USER_CONTEXT(src_ns->user); struct quota_root *const *rootp; array_foreach(&quser->quota->roots, rootp) { bool have_src_quota, have_dest_quota; have_src_quota = quota_root_is_namespace_visible(*rootp, src_ns); have_dest_quota = quota_root_is_namespace_visible(*rootp, dest_ns); if (have_src_quota == have_dest_quota) { /* Both/neither have this quota */ } else if (have_dest_quota) { /* Destination mailbox has a quota that doesn't exist in source. We'll need to check if it's being exceeded. */ return TRUE; } else { /* Source mailbox has a quota root that doesn't exist in destination. We're not increasing the source quota, so ignore it. */ } } return FALSE; } static int quota_check(struct mail_save_context *ctx, struct mailbox *src_box) { struct mailbox_transaction_context *t = ctx->transaction; struct quota_transaction_context *qt = QUOTA_CONTEXT(t); enum quota_alloc_result ret; i_assert(!ctx->moving || src_box != NULL); if (ctx->moving && !quota_move_requires_check(ctx->transaction->box, src_box)) { /* the mail is being moved. the quota won't increase (after the following expunge), so allow this even if user is currently over quota */ quota_alloc(qt, ctx->dest_mail); return 0; } ret = quota_try_alloc(qt, ctx->dest_mail); switch (ret) { case QUOTA_ALLOC_RESULT_OK: return 0; case QUOTA_ALLOC_RESULT_TEMPFAIL: /* allow saving anyway. don't log an error, because at this point we can't give very informative error without API changes. the real error should have been logged already (except if this was due to quota calculation on background, then we intentionally don't want to log anything) */ return 0; default: quota_set_storage_error(qt, t->box->storage, ret); return -1; } } static int quota_copy(struct mail_save_context *ctx, struct mail *mail) { struct mailbox_transaction_context *t = ctx->transaction; struct quota_transaction_context *qt = QUOTA_CONTEXT(t); struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box); /* we always want to know the mail size */ mail_add_temp_wanted_fields(ctx->dest_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL); /* get quota before copying any mails. this avoids .vsize.lock deadlocks with backends that lock mails for expunging/copying. */ (void)quota_transaction_set_limits(qt); if (qbox->module_ctx.super.copy(ctx, mail) < 0) return -1; if (ctx->copying_via_save) { /* copying used saving internally, we already checked the quota */ return 0; } return quota_check(ctx, mail->box); } static int quota_save_begin(struct mail_save_context *ctx, struct istream *input) { struct mailbox_transaction_context *t = ctx->transaction; struct quota_transaction_context *qt = QUOTA_CONTEXT(t); struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box); uoff_t size; if (!ctx->moving && i_stream_get_size(input, TRUE, &size) > 0) { /* Input size is known, check for quota immediately. This check isn't perfect, especially because input stream's linefeeds may contain CR+LFs while physical message would only contain LFs. With mbox some headers might be skipped entirely. I think these don't really matter though compared to the benefit of giving "out of quota" error before sending the full mail. */ enum quota_alloc_result qret = quota_test_alloc(qt, size); switch (qret) { case QUOTA_ALLOC_RESULT_OK: /* Great, there is space. */ break; case QUOTA_ALLOC_RESULT_TEMPFAIL: /* allow saving anyway. don't log an error - see quota_check() for reasons. */ break; default: quota_set_storage_error(qt, t->box->storage, qret); return -1; } } /* we always want to know the mail size */ mail_add_temp_wanted_fields(ctx->dest_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL); /* get quota before copying any mails. this avoids .vsize.lock deadlocks with backends that lock mails for expunging/copying. */ (void)quota_transaction_set_limits(qt); return qbox->module_ctx.super.save_begin(ctx, input); } static int quota_save_finish(struct mail_save_context *ctx) { struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box); struct mailbox *src_box; if (qbox->module_ctx.super.save_finish(ctx) < 0) return -1; src_box = ctx->copy_src_mail == NULL ? NULL : ctx->copy_src_mail->box; return quota_check(ctx, src_box); } static void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox) { if (array_is_created(&qbox->expunge_uids)) { array_clear(&qbox->expunge_uids); array_clear(&qbox->expunge_sizes); } if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) { mail_free(&qbox->expunge_qt->tmp_mail); (void)mailbox_transaction_commit(&qbox->expunge_trans); } qbox->sync_transaction_expunge = FALSE; } static void quota_mailbox_sync_commit(struct quota_mailbox *qbox) { quota_mailbox_sync_cleanup(qbox); if (qbox->expunge_qt != NULL) (void)quota_transaction_commit(&qbox->expunge_qt); qbox->recalculate = FALSE; } static void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid, enum mailbox_sync_type sync_type) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); struct quota_user *quser = QUOTA_USER_CONTEXT(box->storage->user); const uint32_t *uids; const uoff_t *sizep; unsigned int i, count; uoff_t size; if (qbox->module_ctx.super.sync_notify != NULL) qbox->module_ctx.super.sync_notify(box, uid, sync_type); if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate || (box->flags & MAILBOX_FLAG_DELETE_UNSAFE) != 0) { if (uid == 0) { /* free the transaction before view syncing begins, otherwise it'll crash. */ quota_mailbox_sync_cleanup(qbox); } return; } if (qbox->expunge_qt == NULL) { qbox->expunge_qt = quota_transaction_begin(box); qbox->expunge_qt->sync_transaction = qbox->sync_transaction_expunge; } if (qbox->expunge_qt->auto_updating) { /* even though backend doesn't care about size/count changes, make sure count_used changes so quota_warnings are executed */ quota_free_bytes(qbox->expunge_qt, 0); return; } /* we're in the middle of syncing the mailbox, so it's a bad idea to try and get the message sizes at this point. Rely on sizes that we saved earlier, or recalculate the whole quota if we don't know the size. */ if (!array_is_created(&qbox->expunge_uids) || array_is_empty(&qbox->expunge_uids)) { i = count = 0; } else { uids = array_get(&qbox->expunge_uids, &count); for (i = qbox->prev_idx; i < count; i++) { if (uids[i] == uid) break; } if (i >= count) { for (i = 0; i < qbox->prev_idx; i++) { if (uids[i] == uid) break; } if (i == qbox->prev_idx) i = count; } qbox->prev_idx = i; } if (i != count) { /* we already know the size */ sizep = array_idx(&qbox->expunge_sizes, i); quota_free_bytes(qbox->expunge_qt, *sizep); /* FIXME: it's not ideal that we do the vsize update here, but this is the easiest place for it for now.. maybe the mail size checking code could be moved to lib-storage */ if (ibox->vsize_update != NULL && quser->quota->set->vsizes) index_mailbox_vsize_hdr_expunge(ibox->vsize_update, uid, *sizep); return; } /* try to look up the size. this works only if it's cached. */ if (qbox->expunge_qt->tmp_mail == NULL) { /* FIXME: ugly kludge to open the transaction for sync_view. box->view may not have all the new messages that sync_notify() notifies about, and those messages would cause a quota recalculation. */ struct mail_index_view *box_view = box->view; if (box->tmp_sync_view != NULL) box->view = box->tmp_sync_view; qbox->expunge_trans = mailbox_transaction_begin(box, 0); mailbox_transaction_set_reason(qbox->expunge_trans, "quota"); box->view = box_view; qbox->expunge_qt->tmp_mail = mail_alloc(qbox->expunge_trans, MAIL_FETCH_PHYSICAL_SIZE, NULL); } if (!mail_set_uid(qbox->expunge_qt->tmp_mail, uid)) ; else if (!quser->quota->set->vsizes) { if (mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0) { quota_free_bytes(qbox->expunge_qt, size); return; } } else if (mail_get_virtual_size(qbox->expunge_qt->tmp_mail, &size) == 0) { quota_free_bytes(qbox->expunge_qt, size); if (ibox->vsize_update != NULL) index_mailbox_vsize_hdr_expunge(ibox->vsize_update, uid, size); } else { /* there's no way to get the size. recalculate the quota. */ quota_recalculate(qbox->expunge_qt, QUOTA_RECALCULATE_MISSING_FREES); qbox->recalculate = TRUE; } } static int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r) { struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box); int ret; ret = qbox->module_ctx.super.sync_deinit(ctx, status_r); /* update quota only after syncing is finished. the quota commit may recalculate the quota and cause all mailboxes to be synced, including the one we're already syncing. */ quota_mailbox_sync_commit(qbox); return ret; } static void quota_roots_flush(struct quota *quota) { struct quota_root *const *roots; unsigned int i, count; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { if (roots[i]->backend.v.flush != NULL) roots[i]->backend.v.flush(roots[i]); } } static void quota_mailbox_close(struct mailbox *box) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); struct quota_user *quser = QUOTA_USER_CONTEXT(box->storage->user); /* sync_notify() may be called outside sync_begin()..sync_deinit(). make sure we apply changes at close time at latest. */ quota_mailbox_sync_commit(qbox); /* make sure quota backend flushes all data. this could also be done somewhat later, but user.deinit() is too late, since the flushing can trigger quota recalculation which isn't safe to do anymore at user.deinit() when most of the loaded plugins have already been deinitialized. */ quota_roots_flush(quser->quota); qbox->module_ctx.super.close(box); } static void quota_mailbox_free(struct mailbox *box) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); if (array_is_created(&qbox->expunge_uids)) { array_free(&qbox->expunge_uids); array_free(&qbox->expunge_sizes); } i_assert(qbox->expunge_qt == NULL || qbox->expunge_qt->tmp_mail == NULL); qbox->module_ctx.super.free(box); } void quota_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct quota_mailbox *qbox; if (QUOTA_LIST_CONTEXT(box->list) == NULL) return; if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) return; qbox = p_new(box->pool, struct quota_mailbox, 1); qbox->module_ctx.super = *v; box->vlast = &qbox->module_ctx.super; v->get_status = quota_get_status; v->transaction_begin = quota_mailbox_transaction_begin; v->transaction_commit = quota_mailbox_transaction_commit; v->transaction_rollback = quota_mailbox_transaction_rollback; v->save_begin = quota_save_begin; v->save_finish = quota_save_finish; v->copy = quota_copy; v->sync_notify = quota_mailbox_sync_notify; v->sync_deinit = quota_mailbox_sync_deinit; v->close = quota_mailbox_close; v->free = quota_mailbox_free; MODULE_CONTEXT_SET(box, quota_storage_module, qbox); } static void quota_mailbox_list_deinit(struct mailbox_list *list) { struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list); quota_remove_user_namespace(list->ns); qlist->module_ctx.super.deinit(list); } struct quota *quota_get_mail_user_quota(struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); return quser == NULL ? NULL : quser->quota; } static void quota_user_deinit(struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct quota_settings *quota_set = quser->quota->set; quota_deinit(&quser->quota); quser->module_ctx.super.deinit(user); quota_settings_deinit("a_set); } void quota_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct quota_user *quser; struct quota_settings *set; struct quota *quota; const char *error; int ret; if ((ret = quota_user_read_settings(user, &set, &error)) > 0) { if (quota_init(set, user, "a, &error) < 0) { quota_settings_deinit(&set); ret = -1; } } if (ret < 0) { user->error = p_strdup_printf(user->pool, "Failed to initialize quota: %s", error); return; } if (ret > 0) { quser = p_new(user->pool, struct quota_user, 1); quser->module_ctx.super = *v; user->vlast = &quser->module_ctx.super; v->deinit = quota_user_deinit; quser->quota = quota; MODULE_CONTEXT_SET(user, quota_user_module, quser); } else if (user->mail_debug) { i_debug("quota: No quota setting - plugin disabled"); } } static struct quota_root * quota_find_root_for_ns(struct quota *quota, struct mail_namespace *ns) { struct quota_root *const *roots; unsigned int i, count; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { if (roots[i]->ns_prefix != NULL && strcmp(roots[i]->ns_prefix, ns->prefix) == 0) return roots[i]; } return NULL; } void quota_mailbox_list_created(struct mailbox_list *list) { struct quota_mailbox_list *qlist; struct quota *quota = NULL; struct quota_root *root; struct mail_user *quota_user; bool add; /* see if we have a quota explicitly defined for this namespace */ quota = quota_get_mail_user_quota(list->ns->user); if (quota == NULL) return; root = quota_find_root_for_ns(quota, list->ns); if (root != NULL) { /* explicit quota root */ root->ns = list->ns; quota_user = list->ns->user; } else { quota_user = list->ns->owner != NULL ? list->ns->owner : list->ns->user; } if ((list->ns->flags & NAMESPACE_FLAG_NOQUOTA) != 0) add = FALSE; else if (list->ns->owner == NULL) { /* public namespace - add quota only if namespace is explicitly defined for it */ add = root != NULL; } else { /* for shared namespaces add only if the owner has quota enabled */ add = QUOTA_USER_CONTEXT(quota_user) != NULL; } if (add) { struct mailbox_list_vfuncs *v = list->vlast; qlist = p_new(list->pool, struct quota_mailbox_list, 1); qlist->module_ctx.super = *v; list->vlast = &qlist->module_ctx.super; v->deinit = quota_mailbox_list_deinit; MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist); quota = quota_get_mail_user_quota(quota_user); i_assert(quota != NULL); quota_add_user_namespace(quota, list->ns); } } static void quota_root_set_namespace(struct quota_root *root, struct mail_namespace *namespaces) { const struct quota_rule *rule; const char *name; struct mail_namespace *ns; /* silence errors for autocreated (shared) users */ bool silent_errors = namespaces->user->autocreated; if (root->ns_prefix != NULL && root->ns == NULL) { root->ns = mail_namespace_find_prefix(namespaces, root->ns_prefix); if (root->ns == NULL && !silent_errors) { i_error("quota: Unknown namespace: %s", root->ns_prefix); } } array_foreach(&root->set->rules, rule) { name = rule->mailbox_mask; ns = mail_namespace_find(namespaces, name); if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0 && !silent_errors) i_error("quota: Unknown namespace: %s", name); } } void quota_mail_namespaces_created(struct mail_namespace *namespaces) { struct quota *quota; struct quota_root *const *roots; unsigned int i, count; quota = quota_get_mail_user_quota(namespaces->user); if (quota == NULL) return; roots = array_get("a->roots, &count); for (i = 0; i < count; i++) quota_root_set_namespace(roots[i], namespaces); quota_over_flag_check_startup(quota); } dovecot-2.2.33.2/src/plugins/quota/quota-plugin.c0000644000175000017500000000156013123174404016520 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-user.h" #include "mail-storage-hooks.h" #include "quota-plugin.h" void quota_backends_register(void); void quota_backends_unregister(void); const char *quota_plugin_version = DOVECOT_ABI_VERSION; static struct mail_storage_hooks quota_mail_storage_hooks = { .mail_user_created = quota_mail_user_created, .mail_namespaces_created = quota_mail_namespaces_created, .mailbox_list_created = quota_mailbox_list_created, .mailbox_allocated = quota_mailbox_allocated, .mail_allocated = quota_mail_allocated }; void quota_plugin_init(struct module *module) { mail_storage_hooks_add(module, "a_mail_storage_hooks); quota_backends_register(); } void quota_plugin_deinit(void) { mail_storage_hooks_remove("a_mail_storage_hooks); quota_backends_unregister(); } dovecot-2.2.33.2/src/plugins/quota/test-quota-util.c0000644000175000017500000000571213165463624017172 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "quota-private.h" #include "test-common.h" struct test { uint64_t limit, initial_size; int64_t transaction_diff; uint64_t new_size; bool is_over; }; static void test_quota_transaction_is_over(void) { #define MAXU64 (uint64_t)-1 #define MAXS64 9223372036854775807LL #define MINS64 (-MAXS64 - 1LL) static const struct test tests[] = { /* first test only with new_size=1. these are used for both count and bytes tests: */ /* limit, init, diff, new */ { 1, 0, 0, 1, FALSE }, { MAXU64, MAXU64, 0, 1, TRUE }, { MAXU64, MAXU64-1, 0, 1, FALSE }, { MAXU64, MAXU64-1, 1, 1, TRUE }, { MAXU64-1, MAXU64-1, 0, 1, TRUE }, { MAXU64-1, MAXU64-1, -1, 1, FALSE }, { MAXU64-2, MAXU64-1, -1, 1, TRUE }, { MAXU64-2, MAXU64-1, -2, 1, FALSE }, /* these are for bytes tests: */ /* limit, init, diff, new */ { MAXU64, MAXU64, 0, 0, FALSE }, { MAXU64, MAXU64-1, 1, 0, FALSE }, { MAXU64-1, MAXU64, 1, 0, TRUE }, { MAXU64-1, MAXU64, 0, 0, TRUE }, { MAXU64-1, MAXU64, -1, 0, FALSE }, { MAXU64, MAXU64, 0, 1, TRUE }, { MAXU64, 0, 0, MAXU64, FALSE }, { MAXU64, 1, 0, MAXU64, TRUE }, { MAXU64, 0, 1, MAXU64, TRUE }, { MAXU64-1, 0, 0, MAXU64, TRUE }, { MAXU64-1, 0, 0, MAXU64-1, FALSE }, { MAXU64-1, 1, 0, MAXU64-1, TRUE }, { MAXU64-1, 1, -1, MAXU64-1, FALSE }, { MAXU64, MAXU64, 0, MAXU64, TRUE }, }; struct quota_transaction_context ctx; unsigned int i; test_begin("quota transcation is over (count)"); for (i = 0; i < N_ELEMENTS(tests); i++) { if (tests[i].new_size != 1) continue; i_zero(&ctx); ctx.count_used = tests[i].transaction_diff; if (tests[i].initial_size > tests[i].limit) ctx.count_over = tests[i].initial_size - tests[i].limit; else { ctx.count_ceil = tests[i].limit - tests[i].initial_size; i_assert(ctx.count_used < 0 || (uint64_t)ctx.count_used <= ctx.count_ceil); /* test is broken otherwise */ } test_assert_idx(quota_transaction_is_over(&ctx, 0) == tests[i].is_over, i); } test_end(); test_begin("quota transcation is over (bytes)"); for (i = 0; i < N_ELEMENTS(tests); i++) { i_zero(&ctx); ctx.count_ceil = 1; ctx.bytes_used = tests[i].transaction_diff; if (tests[i].initial_size > tests[i].limit) ctx.bytes_over = tests[i].initial_size - tests[i].limit; else { ctx.bytes_ceil = tests[i].limit - tests[i].initial_size; i_assert(ctx.bytes_used < 0 || (uint64_t)ctx.bytes_used <= ctx.bytes_ceil); /* test is broken otherwise */ } test_assert_idx(quota_transaction_is_over(&ctx, tests[i].new_size) == tests[i].is_over, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_quota_transaction_is_over, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/plugins/quota/rquota.x0000644000175000017500000000661613123174404015442 00000000000000/* @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC */ /* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */ /* * Remote quota protocol * Requires unix authentication */ const RQ_PATHLEN = 1024; struct sq_dqblk { unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ unsigned int rq_curblocks; /* current block count */ unsigned int rq_fhardlimit; /* absolute limit on allocated files */ unsigned int rq_fsoftlimit; /* preferred file limit */ unsigned int rq_curfiles; /* current # allocated files */ unsigned int rq_btimeleft; /* time left for excessive disk use */ unsigned int rq_ftimeleft; /* time left for excessive files */ }; struct getquota_args { string gqa_pathp; /* path to filesystem of interest */ int gqa_uid; /* Inquire about quota for uid */ }; struct setquota_args { int sqa_qcmd; string sqa_pathp; /* path to filesystem of interest */ int sqa_id; /* Set quota for uid */ sq_dqblk sqa_dqblk; }; struct ext_getquota_args { string gqa_pathp; /* path to filesystem of interest */ int gqa_type; /* Type of quota info is needed about */ int gqa_id; /* Inquire about quota for id */ }; struct ext_setquota_args { int sqa_qcmd; string sqa_pathp; /* path to filesystem of interest */ int sqa_id; /* Set quota for id */ int sqa_type; /* Type of quota to set */ sq_dqblk sqa_dqblk; }; /* * remote quota structure */ struct rquota { int rq_bsize; /* block size for block counts */ bool rq_active; /* indicates whether quota is active */ unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ unsigned int rq_curblocks; /* current block count */ unsigned int rq_fhardlimit; /* absolute limit on allocated files */ unsigned int rq_fsoftlimit; /* preferred file limit */ unsigned int rq_curfiles; /* current # allocated files */ unsigned int rq_btimeleft; /* time left for excessive disk use */ unsigned int rq_ftimeleft; /* time left for excessive files */ }; enum qr_status { Q_OK = 1, /* quota returned */ Q_NOQUOTA = 2, /* noquota for uid */ Q_EPERM = 3 /* no permission to access quota */ }; union getquota_rslt switch (qr_status status) { case Q_OK: rquota gqr_rquota; /* valid if status == Q_OK */ case Q_NOQUOTA: void; case Q_EPERM: void; }; union setquota_rslt switch (qr_status status) { case Q_OK: rquota sqr_rquota; /* valid if status == Q_OK */ case Q_NOQUOTA: void; case Q_EPERM: void; }; program RQUOTAPROG { version RQUOTAVERS { /* * Get all quotas */ getquota_rslt RQUOTAPROC_GETQUOTA(getquota_args) = 1; /* * Get active quotas only */ getquota_rslt RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2; /* * Set all quotas */ setquota_rslt RQUOTAPROC_SETQUOTA(setquota_args) = 3; /* * Get active quotas only */ setquota_rslt RQUOTAPROC_SETACTIVEQUOTA(setquota_args) = 4; } = 1; version EXT_RQUOTAVERS { /* * Get all quotas */ getquota_rslt RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1; /* * Get active quotas only */ getquota_rslt RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2; /* * Set all quotas */ setquota_rslt RQUOTAPROC_SETQUOTA(ext_setquota_args) = 3; /* * Set active quotas only */ setquota_rslt RQUOTAPROC_SETACTIVEQUOTA(ext_setquota_args) = 4; } = 2; } = 100011; dovecot-2.2.33.2/src/plugins/quota/quota-fs.h0000644000175000017500000000227613123174404015644 00000000000000#ifndef QUOTA_FS_H #define QUOTA_FS_H #if defined (HAVE_STRUCT_DQBLK_CURBLOCKS) || \ defined (HAVE_STRUCT_DQBLK_CURSPACE) # define HAVE_FS_QUOTA #endif #ifdef HAVE_QUOTA_OPEN /* absolute path to avoid confusion with ./quota.h */ # include "/usr/include/quota.h" /* NetBSD with libquota */ #endif #ifdef HAVE_SYS_QUOTA_H # include /* Linux, HP-UX */ #elif defined(HAVE_SYS_FS_UFS_QUOTA_H) # include /* Solaris */ #elif defined(HAVE_UFS_UFS_QUOTA_H) # include /* BSDs */ #elif defined(HAVE_JFS_QUOTA_H) # include /* AIX */ # ifdef HAVE_SYS_FS_QUOTA_COMMON_H # include /* quotactl() */ # endif #else # undef HAVE_FS_QUOTA #endif #ifdef HAVE_QUOTACTL # ifdef HAVE_SYS_QUOTA_H # ifndef _HPUX_SOURCE # define FS_QUOTA_LINUX # else # define FS_QUOTA_HPUX # endif # else # define FS_QUOTA_BSDAIX # endif #elif defined (HAVE_Q_QUOTACTL) # define FS_QUOTA_SOLARIS #else # undef HAVE_FS_QUOTA #endif #ifdef HAVE_QUOTA_OPEN /* NetBSD with libquota */ # define FS_QUOTA_NETBSD # define HAVE_FS_QUOTA # undef FS_QUOTA_LINUX /* obtained because we also have */ #endif #endif dovecot-2.2.33.2/src/plugins/quota/Makefile.am0000644000175000017500000000637713165463624016011 00000000000000doveadm_moduledir = $(moduledir)/doveadm pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = quota-status AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/imapc \ -I$(top_srcdir)/src/lib-storage/index/maildir \ -I$(top_srcdir)/src/doveadm NOPLUGIN_LDFLAGS = lib10_doveadm_quota_plugin_la_LDFLAGS = -module -avoid-version lib10_quota_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_quota_plugin.la quota_dist_sources = \ quota.c \ quota-count.c \ quota-fs.c \ quota-dict.c \ quota-dirsize.c \ quota-imapc.c \ quota-maildir.c \ quota-plugin.c \ quota-storage.c \ quota-util.c quota_common_objects = \ quota.lo \ quota-count.lo \ quota-fs.lo \ quota-dict.lo \ quota-dirsize.lo \ quota-imapc.lo \ quota-maildir.lo \ quota-plugin.lo \ quota-storage.lo \ quota-util.lo \ $(RQUOTA_XDR_LO) lib10_quota_plugin_la_SOURCES = $(quota_dist_sources) nodist_lib10_quota_plugin_la_SOURCES = $(RQUOTA_XDR) lib10_quota_plugin_la_LIBADD = $(QUOTA_LIBS) doveadm_module_LTLIBRARIES = \ lib10_doveadm_quota_plugin.la lib10_doveadm_quota_plugin_la_SOURCES = \ doveadm-quota.c quota_status_SOURCES = \ quota-status.c \ quota-status-settings.c quota_status_LDADD = \ $(quota_common_objects) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) \ $(QUOTA_LIBS) quota_status_DEPENDENCIES = \ $(quota_common_objects) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) if HAVE_RQUOTA RQUOTA_XDR = rquota_xdr.c RQUOTA_XDR_LO = rquota_xdr.lo #RQUOTA_X = /usr/include/rpcsvc/rquota.x RQUOTA_X = $(srcdir)/rquota.x rquota_xdr.c: Makefile rquota.h if [ "$(top_srcdir)" != "$(top_builddir)" ]; then \ cp $(RQUOTA_X) $(top_builddir)/src/plugins/quota/; \ fi; \ (echo '#include "lib.h"'; \ echo '#include '; \ $(RPCGEN) -c $(top_builddir)/src/plugins/quota/rquota.x | \ sed -e 's/IXDR_PUT/(void)IXDR_PUT/g' \ -e 's,/usr/include/rpcsvc/rquota.h,rquota.h,' \ -e 's/int32_t \*buf/int32_t *buf ATTR_UNUSED/' \ -e 's/^static char rcsid.*//' ) > rquota_xdr.c rquota.h: Makefile $(RQUOTA_X) $(RPCGEN) -h $(RQUOTA_X) > rquota.h quota-fs.lo: rquota.h endif pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = \ quota.h \ quota-fs.h \ quota-plugin.h \ quota-private.h noinst_HEADERS = \ quota-status-settings.h EXTRA_DIST = rquota.x clean-generic: if [ "$(top_srcdir)" != "$(top_builddir)" ]; then \ rm -f $(top_builddir)/src/plugins/quota/rquota.x; \ fi; \ rm -f rquota_xdr.c rquota.h test_programs = \ test-quota-util noinst_PROGRAMS = $(test_programs) test_libs = \ ../../lib-test/libtest.la \ ../../lib/liblib.la test_deps = $(noinst_LTLIBRARIES) $(test_libs) test_quota_util_SOURCES = test-quota-util.c test_quota_util_LDADD = quota-util.lo $(test_libs) test_quota_util_DEPENDENCIES = quota-util.lo $(test_deps) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/plugins/zlib/0002755000175000017500000000000013172375613013627 500000000000000dovecot-2.2.33.2/src/plugins/zlib/Makefile.in0000644000175000017500000005541413172375575015632 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/zlib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_zlib_plugin_la_DEPENDENCIES = \ ../../lib-compression/libcompression.la am_lib20_zlib_plugin_la_OBJECTS = zlib-plugin.lo lib20_zlib_plugin_la_OBJECTS = $(am_lib20_zlib_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_zlib_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_zlib_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_zlib_plugin_la_SOURCES) DIST_SOURCES = $(lib20_zlib_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common lib20_zlib_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_zlib_plugin.la lib20_zlib_plugin_la_LIBADD = \ ../../lib-compression/libcompression.la lib20_zlib_plugin_la_SOURCES = \ zlib-plugin.c noinst_HEADERS = \ zlib-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/zlib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/zlib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_zlib_plugin.la: $(lib20_zlib_plugin_la_OBJECTS) $(lib20_zlib_plugin_la_DEPENDENCIES) $(EXTRA_lib20_zlib_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_zlib_plugin_la_LINK) -rpath $(moduledir) $(lib20_zlib_plugin_la_OBJECTS) $(lib20_zlib_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zlib-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/zlib/zlib-plugin.c0000644000175000017500000002620613165463624016155 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "istream-seekable.h" #include "ostream.h" #include "str.h" #include "mail-user.h" #include "index-storage.h" #include "index-mail.h" #include "compression.h" #include "zlib-plugin.h" #include #define ZLIB_PLUGIN_DEFAULT_LEVEL 6 #define ZLIB_CONTEXT(obj) \ MODULE_CONTEXT(obj, zlib_storage_module) #define ZLIB_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, zlib_mail_module) #define ZLIB_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, zlib_user_module) #define MAX_INBUF_SIZE (1024*1024) #define ZLIB_MAIL_CACHE_EXPIRE_MSECS (60*1000) struct zlib_mail { union mail_module_context module_ctx; bool verifying_save; }; struct zlib_mail_cache { struct timeout *to; struct mailbox *box; uint32_t uid; struct istream *input; }; struct zlib_user { union mail_user_module_context module_ctx; struct zlib_mail_cache cache; const struct compression_handler *save_handler; unsigned int save_level; }; const char *zlib_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(zlib_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(zlib_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(zlib_mail_module, &mail_module_register); static bool zlib_mailbox_is_permail(struct mailbox *box) { enum mail_storage_class_flags class_flags = box->storage->class_flags; return (class_flags & MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0 && (class_flags & MAIL_STORAGE_CLASS_FLAG_BINARY_DATA) != 0; } static void zlib_mail_cache_close(struct zlib_user *zuser) { struct zlib_mail_cache *cache = &zuser->cache; if (cache->to != NULL) timeout_remove(&cache->to); if (cache->input != NULL) i_stream_unref(&cache->input); i_zero(cache); } static struct istream * zlib_mail_cache_open(struct zlib_user *zuser, struct mail *mail, struct istream *input, bool do_cache) { struct zlib_mail_cache *cache = &zuser->cache; struct istream *inputs[2]; string_t *temp_prefix = t_str_new(128); if (do_cache) zlib_mail_cache_close(zuser); /* zlib istream is seekable, but very slow. create a seekable istream which we can use to quickly seek around in the stream that's been read so far. usually the partial IMAP FETCHes continue from where the previous left off, so this isn't strictly necessary, but with the way lib-imap-storage's CRLF-cache works it has to seek backwards somewhat, which causes a zlib stream reset. And the CRLF-cache isn't easy to fix.. */ input->seekable = FALSE; inputs[0] = input; inputs[1] = NULL; mail_user_set_get_temp_prefix(temp_prefix, mail->box->storage->user->set); input = i_stream_create_seekable_path(inputs, i_stream_get_max_buffer_size(inputs[0]), str_c(temp_prefix)); i_stream_set_name(input, t_strdup_printf("zlib(%s)", i_stream_get_name(inputs[0]))); i_stream_unref(&inputs[0]); if (do_cache) { cache->to = timeout_add(ZLIB_MAIL_CACHE_EXPIRE_MSECS, zlib_mail_cache_close, zuser); cache->box = mail->box; cache->uid = mail->uid; cache->input = input; /* index-mail wants the stream to be destroyed at close, so create a new stream instead of just increasing reference. */ return i_stream_create_limit(cache->input, (uoff_t)-1); } else { return input; } } static int zlib_istream_opened(struct mail *_mail, struct istream **stream) { struct zlib_user *zuser = ZLIB_USER_CONTEXT(_mail->box->storage->user); struct zlib_mail_cache *cache = &zuser->cache; struct mail_private *mail = (struct mail_private *)_mail; struct zlib_mail *zmail = ZLIB_MAIL_CONTEXT(mail); struct istream *input; const struct compression_handler *handler; if (zmail->verifying_save) { /* zlib_mail_save_finish() is verifying that the user-given input doesn't look compressed. */ return zmail->module_ctx.super.istream_opened(_mail, stream); } if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { /* use the cached stream. when doing partial reads it should already be seeked into the wanted offset. */ i_stream_unref(stream); i_stream_seek(cache->input, 0); *stream = i_stream_create_limit(cache->input, (uoff_t)-1); return zmail->module_ctx.super.istream_opened(_mail, stream); } handler = compression_detect_handler(*stream); if (handler != NULL) { if (handler->create_istream == NULL) { mail_storage_set_critical(_mail->box->storage, "zlib plugin: Detected %s compression " "but support not compiled in", handler->ext); return -1; } input = *stream; *stream = handler->create_istream(input, TRUE); i_stream_unref(&input); /* dont cache the stream if _mail->uid is 0 */ *stream = zlib_mail_cache_open(zuser, _mail, *stream, (_mail->uid > 0)); } return zmail->module_ctx.super.istream_opened(_mail, stream); } static void zlib_mail_close(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct zlib_mail *zmail = ZLIB_MAIL_CONTEXT(mail); struct zlib_user *zuser = ZLIB_USER_CONTEXT(_mail->box->storage->user); struct zlib_mail_cache *cache = &zuser->cache; uoff_t size; if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { /* make sure we have read the entire email into the seekable stream (which causes the original input stream to be unrefed). we can't safely keep the original input stream open after the mail is closed. */ if (i_stream_get_size(cache->input, TRUE, &size) < 0) zlib_mail_cache_close(zuser); } zmail->module_ctx.super.close(_mail); } static void zlib_mail_allocated(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; struct zlib_mail *zmail; if (!zlib_mailbox_is_permail(_mail->box)) return; zmail = p_new(mail->pool, struct zlib_mail, 1); zmail->module_ctx.super = *v; mail->vlast = &zmail->module_ctx.super; v->istream_opened = zlib_istream_opened; v->close = zlib_mail_close; MODULE_CONTEXT_SET(mail, zlib_mail_module, zmail); } static int zlib_mail_save_finish(struct mail_save_context *ctx) { struct mailbox *box = ctx->transaction->box; union mailbox_module_context *zbox = ZLIB_CONTEXT(box); struct mail_private *mail = (struct mail_private *)ctx->dest_mail; struct zlib_mail *zmail = ZLIB_MAIL_CONTEXT(mail); struct istream *input; int ret; if (zbox->super.save_finish(ctx) < 0) return -1; zmail->verifying_save = TRUE; ret = mail_get_stream(ctx->dest_mail, NULL, NULL, &input); zmail->verifying_save = FALSE; if (ret < 0) return -1; if (compression_detect_handler(input) != NULL) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Saving mails compressed by client isn't supported"); return -1; } return 0; } static int zlib_mail_save_compress_begin(struct mail_save_context *ctx, struct istream *input) { struct mailbox *box = ctx->transaction->box; struct zlib_user *zuser = ZLIB_USER_CONTEXT(box->storage->user); union mailbox_module_context *zbox = ZLIB_CONTEXT(box); struct ostream *output; if (zbox->super.save_begin(ctx, input) < 0) return -1; output = zuser->save_handler->create_ostream(ctx->data.output, zuser->save_level); o_stream_unref(&ctx->data.output); ctx->data.output = output; o_stream_cork(ctx->data.output); return 0; } static void zlib_permail_alloc_init(struct mailbox *box, struct mailbox_vfuncs *v) { struct zlib_user *zuser = ZLIB_USER_CONTEXT(box->storage->user); if (zuser->save_handler == NULL) { v->save_finish = zlib_mail_save_finish; } else { v->save_begin = zlib_mail_save_compress_begin; } } static int zlib_mailbox_open_input(struct mailbox *box) { const struct compression_handler *handler; struct istream *input; struct stat st; int fd; handler = compression_lookup_handler_from_ext(box->name); if (handler == NULL || handler->create_istream == NULL) return 0; if (mail_storage_is_mailbox_file(box->storage)) { /* looks like a compressed single file mailbox. we should be able to handle this. */ const char *box_path = mailbox_get_path(box); fd = open(box_path, O_RDONLY); if (fd == -1) { /* let the standard handler figure out what to do with the failure */ return 0; } if (fstat(fd, &st) == 0 && S_ISDIR(st.st_mode)) { i_close_fd(&fd); return 0; } input = i_stream_create_fd_autoclose(&fd, MAX_INBUF_SIZE); i_stream_set_name(input, box_path); box->input = handler->create_istream(input, TRUE); i_stream_unref(&input); box->flags |= MAILBOX_FLAG_READONLY; } return 0; } static int zlib_mailbox_open(struct mailbox *box) { union mailbox_module_context *zbox = ZLIB_CONTEXT(box); if (box->input == NULL && (box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) != 0) { if (zlib_mailbox_open_input(box) < 0) return -1; } return zbox->super.open(box); } static void zlib_mailbox_close(struct mailbox *box) { union mailbox_module_context *zbox = ZLIB_CONTEXT(box); struct zlib_user *zuser = ZLIB_USER_CONTEXT(box->storage->user); if (zuser->cache.box == box) zlib_mail_cache_close(zuser); zbox->super.close(box); } static void zlib_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; union mailbox_module_context *zbox; zbox = p_new(box->pool, union mailbox_module_context, 1); zbox->super = *v; box->vlast = &zbox->super; v->open = zlib_mailbox_open; v->close = zlib_mailbox_close; MODULE_CONTEXT_SET_SELF(box, zlib_storage_module, zbox); if (zlib_mailbox_is_permail(box)) zlib_permail_alloc_init(box, v); } static void zlib_mail_user_deinit(struct mail_user *user) { struct zlib_user *zuser = ZLIB_USER_CONTEXT(user); zlib_mail_cache_close(zuser); zuser->module_ctx.super.deinit(user); } static void zlib_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct zlib_user *zuser; const char *name; zuser = p_new(user->pool, struct zlib_user, 1); zuser->module_ctx.super = *v; user->vlast = &zuser->module_ctx.super; v->deinit = zlib_mail_user_deinit; name = mail_user_plugin_getenv(user, "zlib_save"); if (name != NULL && *name != '\0') { zuser->save_handler = compression_lookup_handler(name); if (zuser->save_handler == NULL) i_error("zlib_save: Unknown handler: %s", name); else if (zuser->save_handler->create_ostream == NULL) { i_error("zlib_save: Support not compiled in for handler: %s", name); zuser->save_handler = NULL; } } name = mail_user_plugin_getenv(user, "zlib_save_level"); if (name != NULL) { if (str_to_uint(name, &zuser->save_level) < 0 || zuser->save_level < 1 || zuser->save_level > 9) { i_error("zlib_save_level: Level must be between 1..9"); zuser->save_level = 0; } } if (zuser->save_level == 0) zuser->save_level = ZLIB_PLUGIN_DEFAULT_LEVEL; MODULE_CONTEXT_SET(user, zlib_user_module, zuser); } static struct mail_storage_hooks zlib_mail_storage_hooks = { .mail_user_created = zlib_mail_user_created, .mailbox_allocated = zlib_mailbox_allocated, .mail_allocated = zlib_mail_allocated }; void zlib_plugin_init(struct module *module) { mail_storage_hooks_add(module, &zlib_mail_storage_hooks); } void zlib_plugin_deinit(void) { mail_storage_hooks_remove(&zlib_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/zlib/zlib-plugin.h0000644000175000017500000000020213123174404016133 00000000000000#ifndef ZLIB_PLUGIN_H #define ZLIB_PLUGIN_H void zlib_plugin_init(struct module *module); void zlib_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/zlib/Makefile.am0000644000175000017500000000111213123174404015563 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/dbox-common NOPLUGIN_LDFLAGS = lib20_zlib_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_zlib_plugin.la lib20_zlib_plugin_la_LIBADD = \ ../../lib-compression/libcompression.la lib20_zlib_plugin_la_SOURCES = \ zlib-plugin.c noinst_HEADERS = \ zlib-plugin.h dovecot-2.2.33.2/src/plugins/mailbox-alias/0002755000175000017500000000000013172375613015411 500000000000000dovecot-2.2.33.2/src/plugins/mailbox-alias/Makefile.in0000644000175000017500000005535613172375574017420 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/mailbox-alias ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_mailbox_alias_plugin_la_LIBADD = am_lib20_mailbox_alias_plugin_la_OBJECTS = mailbox-alias-plugin.lo lib20_mailbox_alias_plugin_la_OBJECTS = \ $(am_lib20_mailbox_alias_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_mailbox_alias_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib20_mailbox_alias_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_mailbox_alias_plugin_la_SOURCES) DIST_SOURCES = $(lib20_mailbox_alias_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib20_mailbox_alias_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_mailbox_alias_plugin.la lib20_mailbox_alias_plugin_la_SOURCES = \ mailbox-alias-plugin.c noinst_HEADERS = \ mailbox-alias-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/mailbox-alias/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/mailbox-alias/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_mailbox_alias_plugin.la: $(lib20_mailbox_alias_plugin_la_OBJECTS) $(lib20_mailbox_alias_plugin_la_DEPENDENCIES) $(EXTRA_lib20_mailbox_alias_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_mailbox_alias_plugin_la_LINK) -rpath $(moduledir) $(lib20_mailbox_alias_plugin_la_OBJECTS) $(lib20_mailbox_alias_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailbox-alias-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/mailbox-alias/mailbox-alias-plugin.c0000644000175000017500000002417613123174404021512 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-storage-hooks.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "mailbox-alias-plugin.h" #define MAILBOX_ALIAS_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_alias_user_module) #define MAILBOX_ALIAS_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_alias_storage_module) #define MAILBOX_ALIAS_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_alias_mailbox_list_module) struct mailbox_alias { const char *old_vname, *new_vname; }; struct mailbox_alias_user { union mail_user_module_context module_ctx; ARRAY(struct mailbox_alias) aliases; }; struct mailbox_alias_mailbox_list { union mailbox_list_module_context module_ctx; }; struct mailbox_alias_mailbox { union mailbox_module_context module_ctx; }; enum mailbox_symlink_existence { MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT, MAILBOX_SYMLINK_EXISTENCE_SYMLINK, MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK }; static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_mailbox_list_module, &mailbox_list_module_register); const char *mailbox_alias_plugin_version = DOVECOT_ABI_VERSION; static const char * mailbox_alias_find_new(struct mail_user *user, const char *new_vname) { struct mailbox_alias_user *auser = MAILBOX_ALIAS_USER_CONTEXT(user); const struct mailbox_alias *alias; array_foreach(&auser->aliases, alias) { if (strcmp(alias->new_vname, new_vname) == 0) return alias->old_vname; } return NULL; } static int mailbox_symlink_exists(struct mailbox_list *list, const char *vname, enum mailbox_symlink_existence *existence_r) { struct mailbox_alias_mailbox_list *alist = MAILBOX_ALIAS_LIST_CONTEXT(list); struct stat st; const char *symlink_name, *symlink_path; int ret; symlink_name = alist->module_ctx.super.get_storage_name(list, vname); ret = mailbox_list_get_path(list, symlink_name, MAILBOX_LIST_PATH_TYPE_DIR, &symlink_path); if (ret < 0) return -1; i_assert(ret > 0); if (lstat(symlink_path, &st) < 0) { if (errno == ENOENT) { *existence_r = MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT; return 0; } mailbox_list_set_critical(list, "lstat(%s) failed: %m", symlink_path); return -1; } if (S_ISLNK(st.st_mode)) *existence_r = MAILBOX_SYMLINK_EXISTENCE_SYMLINK; else *existence_r = MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK; return 0; } static int mailbox_is_alias_symlink(struct mailbox *box) { enum mailbox_symlink_existence existence; if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) return 0; if (mailbox_symlink_exists(box->list, box->vname, &existence) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } return existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK ? 1 : 0; } static int mailbox_has_aliases(struct mailbox_list *list, const char *old_vname) { struct mailbox_alias_user *auser = MAILBOX_ALIAS_USER_CONTEXT(list->ns->user); const struct mailbox_alias *alias; enum mailbox_symlink_existence existence; int ret = 0; array_foreach(&auser->aliases, alias) { if (strcmp(alias->old_vname, old_vname) == 0) { if (mailbox_symlink_exists(list, alias->new_vname, &existence) < 0) ret = -1; else if (existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK) return 1; } } return ret; } static int mailbox_alias_create_symlink(struct mailbox *box, const char *old_name, const char *new_name) { const char *old_path, *new_path, *fname; int ret; ret = mailbox_list_get_path(box->list, old_name, MAILBOX_LIST_PATH_TYPE_DIR, &old_path); if (ret > 0) { ret = mailbox_list_get_path(box->list, new_name, MAILBOX_LIST_PATH_TYPE_DIR, &new_path); } if (ret < 0) return -1; if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Mailbox aliases not supported by storage"); return -1; } fname = strrchr(old_path, '/'); i_assert(fname != NULL); fname++; i_assert(strncmp(new_path, old_path, fname-old_path) == 0); if (symlink(fname, new_path) < 0) { if (errno == EEXIST) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } mail_storage_set_critical(box->storage, "symlink(%s, %s) failed: %m", fname, new_path); return -1; } return 0; } static const char * mailbox_alias_get_storage_name(struct mailbox_list *list, const char *vname) { struct mailbox_alias_mailbox_list *alist = MAILBOX_ALIAS_LIST_CONTEXT(list); const char *old_vname; enum mailbox_symlink_existence existence; /* access the old mailbox so that e.g. full text search won't index the mailbox twice. this also means that deletion must be careful to delete the symlink, box->name. */ old_vname = mailbox_alias_find_new(list->ns->user, vname); if (old_vname != NULL && mailbox_symlink_exists(list, vname, &existence) == 0 && existence != MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK) vname = old_vname; return alist->module_ctx.super.get_storage_name(list, vname); } static int mailbox_alias_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); struct mailbox_alias_mailbox_list *alist = MAILBOX_ALIAS_LIST_CONTEXT(box->list); const char *symlink_name; int ret; ret = abox->module_ctx.super.create_box(box, update, directory); if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) return ret; if (ret < 0 && mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) return ret; /* all the code so far has actually only created the original mailbox. now we'll create the symlink if it's missing. */ symlink_name = alist->module_ctx.super. get_storage_name(box->list, box->vname); return mailbox_alias_create_symlink(box, box->name, symlink_name); } static int mailbox_alias_delete(struct mailbox *box) { struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); struct mailbox_alias_mailbox_list *alist = MAILBOX_ALIAS_LIST_CONTEXT(box->list); const char *symlink_name; int ret; ret = mailbox_has_aliases(box->list, box->vname); if (ret < 0) return -1; if (ret > 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't delete mailbox while it has aliases"); return -1; } if ((ret = mailbox_is_alias_symlink(box)) < 0) return -1; if (ret > 0) { /* we're deleting an alias mailbox. we'll need to handle this explicitly since box->name points to the original mailbox */ symlink_name = alist->module_ctx.super. get_storage_name(box->list, box->vname); if (mailbox_list_delete_symlink(box->list, symlink_name) < 0) { mail_storage_copy_list_error(box->storage, box->list); return -1; } return 0; } return abox->module_ctx.super.delete_box(box); } static int mailbox_alias_rename(struct mailbox *src, struct mailbox *dest) { struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(src); int ret; if ((ret = mailbox_is_alias_symlink(src)) < 0) return -1; else if (ret > 0) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't rename alias mailboxes"); return -1; } if ((ret = mailbox_is_alias_symlink(dest)) < 0) return -1; else if (ret > 0) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't rename to mailbox alias"); return -1; } ret = mailbox_has_aliases(src->list, src->vname); if (ret < 0) return -1; if (ret > 0) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailbox while it has aliases"); return -1; } return abox->module_ctx.super.rename_box(src, dest); } static void mailbox_alias_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct mailbox_alias_user *auser; struct mailbox_alias *alias; string_t *oldkey, *newkey; const char *old_vname, *new_vname; unsigned int i; auser = p_new(user->pool, struct mailbox_alias_user, 1); auser->module_ctx.super = *v; user->vlast = &auser->module_ctx.super; p_array_init(&auser->aliases, user->pool, 8); oldkey = t_str_new(32); newkey = t_str_new(32); str_append(oldkey, "mailbox_alias_old"); str_append(newkey, "mailbox_alias_new"); for (i = 2;; i++) { old_vname = mail_user_plugin_getenv(user, str_c(oldkey)); new_vname = mail_user_plugin_getenv(user, str_c(newkey)); if (old_vname == NULL || new_vname == NULL) break; alias = array_append_space(&auser->aliases); alias->old_vname = old_vname; alias->new_vname = new_vname; str_truncate(oldkey, 0); str_truncate(newkey, 0); str_printfa(oldkey, "mailbox_alias_old%u", i); str_printfa(newkey, "mailbox_alias_new%u", i); } MODULE_CONTEXT_SET(user, mailbox_alias_user_module, auser); } static void mailbox_alias_mailbox_list_created(struct mailbox_list *list) { struct mailbox_list_vfuncs *v = list->vlast; struct mailbox_alias_mailbox_list *alist; alist = p_new(list->pool, struct mailbox_alias_mailbox_list, 1); alist->module_ctx.super = *v; list->vlast = &alist->module_ctx.super; v->get_storage_name = mailbox_alias_get_storage_name; MODULE_CONTEXT_SET(list, mailbox_alias_mailbox_list_module, alist); } static void mailbox_alias_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct mailbox_alias_mailbox *abox; abox = p_new(box->pool, struct mailbox_alias_mailbox, 1); abox->module_ctx.super = *v; box->vlast = &abox->module_ctx.super; v->create_box = mailbox_alias_create; v->delete_box = mailbox_alias_delete; v->rename_box = mailbox_alias_rename; MODULE_CONTEXT_SET(box, mailbox_alias_storage_module, abox); } static struct mail_storage_hooks mailbox_alias_mail_storage_hooks = { .mail_user_created = mailbox_alias_mail_user_created, .mailbox_list_created = mailbox_alias_mailbox_list_created, .mailbox_allocated = mailbox_alias_mailbox_allocated }; void mailbox_alias_plugin_init(struct module *module) { mail_storage_hooks_add(module, &mailbox_alias_mail_storage_hooks); } void mailbox_alias_plugin_deinit(void) { mail_storage_hooks_remove(&mailbox_alias_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/mailbox-alias/mailbox-alias-plugin.h0000644000175000017500000000024613123174404021507 00000000000000#ifndef MAILBOX_ALIAS_PLUGIN_H #define MAILBOX_ALIAS_PLUGIN_H void mailbox_alias_plugin_init(struct module *module); void mailbox_alias_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/mailbox-alias/Makefile.am0000644000175000017500000000064613123174404017360 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib20_mailbox_alias_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_mailbox_alias_plugin.la lib20_mailbox_alias_plugin_la_SOURCES = \ mailbox-alias-plugin.c noinst_HEADERS = \ mailbox-alias-plugin.h dovecot-2.2.33.2/src/plugins/mail-log/0002755000175000017500000000000013172375613014370 500000000000000dovecot-2.2.33.2/src/plugins/mail-log/Makefile.in0000644000175000017500000005556113172375574016375 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/mail-log ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib20_mail_log_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la am_lib20_mail_log_plugin_la_OBJECTS = mail-log-plugin.lo lib20_mail_log_plugin_la_OBJECTS = \ $(am_lib20_mail_log_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_mail_log_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_mail_log_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_mail_log_plugin_la_SOURCES) DIST_SOURCES = $(lib20_mail_log_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/notify lib20_mail_log_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_mail_log_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib20_mail_log_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la lib20_mail_log_plugin_la_SOURCES = \ mail-log-plugin.c noinst_HEADERS = \ mail-log-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/mail-log/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/mail-log/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_mail_log_plugin.la: $(lib20_mail_log_plugin_la_OBJECTS) $(lib20_mail_log_plugin_la_DEPENDENCIES) $(EXTRA_lib20_mail_log_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_mail_log_plugin_la_LINK) -rpath $(moduledir) $(lib20_mail_log_plugin_la_OBJECTS) $(lib20_mail_log_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-log-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/mail-log/mail-log-plugin.c0000644000175000017500000003550313165463624017457 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "str.h" #include "str-sanitize.h" #include "imap-util.h" #include "mail-user.h" #include "mail-storage-private.h" #include "notify-plugin.h" #include "mail-log-plugin.h" #define MAILBOX_NAME_LOG_LEN 64 #define HEADER_LOG_LEN 80 #define MAIL_LOG_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_log_user_module) enum mail_log_field { MAIL_LOG_FIELD_UID = 0x01, MAIL_LOG_FIELD_BOX = 0x02, MAIL_LOG_FIELD_MSGID = 0x04, MAIL_LOG_FIELD_PSIZE = 0x08, MAIL_LOG_FIELD_VSIZE = 0x10, MAIL_LOG_FIELD_FLAGS = 0x20, MAIL_LOG_FIELD_FROM = 0x40, MAIL_LOG_FIELD_SUBJECT = 0x80 }; #define MAIL_LOG_DEFAULT_FIELDS \ (MAIL_LOG_FIELD_UID | MAIL_LOG_FIELD_BOX | \ MAIL_LOG_FIELD_MSGID | MAIL_LOG_FIELD_PSIZE) enum mail_log_event { MAIL_LOG_EVENT_DELETE = 0x01, MAIL_LOG_EVENT_UNDELETE = 0x02, MAIL_LOG_EVENT_EXPUNGE = 0x04, MAIL_LOG_EVENT_SAVE = 0x08, MAIL_LOG_EVENT_COPY = 0x10, MAIL_LOG_EVENT_MAILBOX_CREATE = 0x20, MAIL_LOG_EVENT_MAILBOX_DELETE = 0x40, MAIL_LOG_EVENT_MAILBOX_RENAME = 0x80, MAIL_LOG_EVENT_FLAG_CHANGE = 0x100 }; #define MAIL_LOG_DEFAULT_EVENTS \ (MAIL_LOG_EVENT_DELETE | MAIL_LOG_EVENT_UNDELETE | \ MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_SAVE | MAIL_LOG_EVENT_COPY | \ MAIL_LOG_EVENT_MAILBOX_DELETE | MAIL_LOG_EVENT_MAILBOX_RENAME) static const char *field_names[] = { "uid", "box", "msgid", "size", "vsize", "flags", "from", "subject", NULL }; static const char *event_names[] = { "delete", "undelete", "expunge", "save", "copy", "mailbox_create", "mailbox_delete", "mailbox_rename", "flag_change", NULL }; struct mail_log_user { union mail_user_module_context module_ctx; enum mail_log_field fields; enum mail_log_event events; bool cached_only; }; struct mail_log_message { struct mail_log_message *prev, *next; enum mail_log_event event; bool ignore; const char *pretext, *text; }; struct mail_log_mail_txn_context { pool_t pool; struct mail_log_message *messages, *messages_tail; }; static MODULE_CONTEXT_DEFINE_INIT(mail_log_user_module, &mail_user_module_register); static enum mail_log_field mail_log_field_find(const char *name) { unsigned int i; for (i = 0; field_names[i] != NULL; i++) { if (strcmp(name, field_names[i]) == 0) return 1 << i; } return 0; } static enum mail_log_event mail_log_event_find(const char *name) { unsigned int i; if (strcmp(name, "append") == 0) { /* v1.x backwards compatibility */ name = "save"; } for (i = 0; event_names[i] != NULL; i++) { if (strcmp(name, event_names[i]) == 0) return 1 << i; } return 0; } static enum mail_log_field mail_log_parse_fields(const char *str) { const char *const *tmp; static enum mail_log_field field, fields = 0; for (tmp = t_strsplit_spaces(str, ", "); *tmp != NULL; tmp++) { field = mail_log_field_find(*tmp); if (field == 0) i_fatal("Unknown field in mail_log_fields: '%s'", *tmp); fields |= field; } return fields; } static enum mail_log_event mail_log_parse_events(const char *str) { const char *const *tmp; static enum mail_log_event event, events = 0; for (tmp = t_strsplit_spaces(str, ", "); *tmp != NULL; tmp++) { event = mail_log_event_find(*tmp); if (event == 0) i_fatal("Unknown event in mail_log_events: '%s'", *tmp); events |= event; } return events; } static void mail_log_mail_user_created(struct mail_user *user) { struct mail_log_user *muser; const char *str; muser = p_new(user->pool, struct mail_log_user, 1); MODULE_CONTEXT_SET(user, mail_log_user_module, muser); str = mail_user_plugin_getenv(user, "mail_log_fields"); muser->fields = str == NULL ? MAIL_LOG_DEFAULT_FIELDS : mail_log_parse_fields(str); str = mail_user_plugin_getenv(user, "mail_log_events"); muser->events = str == NULL ? MAIL_LOG_DEFAULT_EVENTS : mail_log_parse_events(str); muser->cached_only = mail_user_plugin_getenv(user, "mail_log_cached_only") != NULL; } static void mail_log_append_mailbox_name(string_t *str, struct mail *mail) { const char *mailbox_str; mailbox_str = mailbox_get_vname(mail->box); str_printfa(str, "box=%s", str_sanitize(mailbox_str, MAILBOX_NAME_LOG_LEN)); } static void mail_log_append_mail_header(string_t *str, struct mail *mail, const char *name, const char *header) { const char *value; if (mail_get_first_header(mail, header, &value) <= 0) value = ""; str_printfa(str, "%s=%s", name, str_sanitize(value, HEADER_LOG_LEN)); } static void mail_log_append_uid(struct mail_log_mail_txn_context *ctx, struct mail_log_message *msg, string_t *str, uint32_t uid) { if (uid != 0) str_printfa(str, "uid=%u", uid); else { /* we don't know the uid yet, assign it later */ str_printfa(str, "uid="); msg->pretext = p_strdup(ctx->pool, str_c(str)); str_truncate(str, 0); } } static void mail_log_update_wanted_fields(struct mail *mail, enum mail_log_field fields) { enum mail_fetch_field wanted_fields = 0; struct mailbox_header_lookup_ctx *wanted_headers = NULL; const char *headers[4]; unsigned int hdr_idx = 0; if ((fields & MAIL_LOG_FIELD_MSGID) != 0) headers[hdr_idx++] = "Message-ID"; if ((fields & MAIL_LOG_FIELD_FROM) != 0) headers[hdr_idx++] = "From"; if ((fields & MAIL_LOG_FIELD_SUBJECT) != 0) headers[hdr_idx++] = "Subject"; if (hdr_idx > 0) { i_assert(hdr_idx < N_ELEMENTS(headers)); headers[hdr_idx] = NULL; wanted_headers = mailbox_header_lookup_init(mail->box, headers); } if ((fields & MAIL_LOG_FIELD_PSIZE) != 0) wanted_fields |= MAIL_FETCH_PHYSICAL_SIZE; if ((fields & MAIL_LOG_FIELD_VSIZE) != 0) wanted_fields |= MAIL_FETCH_VIRTUAL_SIZE; mail_add_temp_wanted_fields(mail, wanted_fields, wanted_headers); if (wanted_headers != NULL) mailbox_header_lookup_unref(&wanted_headers); } static void mail_log_append_mail_message_real(struct mail_log_mail_txn_context *ctx, struct mail *mail, enum mail_log_event event, const char *desc) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(mail->box->storage->user); struct mail_log_message *msg; string_t *text; uoff_t size; msg = p_new(ctx->pool, struct mail_log_message, 1); /* avoid parsing through the message multiple times */ mail_log_update_wanted_fields(mail, muser->fields); text = t_str_new(128); str_append(text, desc); str_append(text, ": "); if ((muser->fields & MAIL_LOG_FIELD_BOX) != 0) { mail_log_append_mailbox_name(text, mail); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_UID) != 0) { if (event != MAIL_LOG_EVENT_SAVE && event != MAIL_LOG_EVENT_COPY) mail_log_append_uid(ctx, msg, text, mail->uid); else { /* with mbox mail->uid contains the uid, but handle this consistently with all mailbox formats */ mail_log_append_uid(ctx, msg, text, 0); } /* make sure UID is assigned to this mail */ mail->transaction->flags |= MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS; str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_MSGID) != 0) { mail_log_append_mail_header(text, mail, "msgid", "Message-ID"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_PSIZE) != 0) { if (mail_get_physical_size(mail, &size) == 0) str_printfa(text, "size=%"PRIuUOFF_T, size); else str_printfa(text, "size=error"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_VSIZE) != 0) { if (mail_get_virtual_size(mail, &size) == 0) str_printfa(text, "vsize=%"PRIuUOFF_T, size); else str_printfa(text, "vsize=error"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_FROM) != 0) { mail_log_append_mail_header(text, mail, "from", "From"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_SUBJECT) != 0) { mail_log_append_mail_header(text, mail, "subject", "Subject"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_FLAGS) != 0) { str_printfa(text, "flags=("); imap_write_flags(text, mail_get_flags(mail), mail_get_keywords(mail)); str_append(text, "), "); } str_truncate(text, str_len(text)-2); msg->event = event; msg->text = p_strdup(ctx->pool, str_c(text)); DLLIST2_APPEND(&ctx->messages, &ctx->messages_tail, msg); } static void mail_log_add_dummy_msg(struct mail_log_mail_txn_context *ctx, enum mail_log_event event) { struct mail_log_message *msg; msg = p_new(ctx->pool, struct mail_log_message, 1); msg->event = event; msg->ignore = TRUE; DLLIST2_APPEND(&ctx->messages, &ctx->messages_tail, msg); } static void mail_log_append_mail_message(struct mail_log_mail_txn_context *ctx, struct mail *mail, enum mail_log_event event, const char *desc) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(mail->box->storage->user); if ((muser->events & event) == 0) { if (event == MAIL_LOG_EVENT_SAVE || event == MAIL_LOG_EVENT_COPY) mail_log_add_dummy_msg(ctx, event); return; } T_BEGIN { enum mail_lookup_abort orig_lookup_abort = mail->lookup_abort; if (event != MAIL_LOG_EVENT_SAVE && muser->cached_only) mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE; mail_log_append_mail_message_real(ctx, mail, event, desc); mail->lookup_abort = orig_lookup_abort; } T_END; } static void * mail_log_mail_transaction_begin(struct mailbox_transaction_context *t ATTR_UNUSED) { pool_t pool; struct mail_log_mail_txn_context *ctx; pool = pool_alloconly_create("mail-log", 2048); ctx = p_new(pool, struct mail_log_mail_txn_context, 1); ctx->pool = pool; return ctx; } static void mail_log_mail_save(void *txn, struct mail *mail) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_SAVE, "save"); } static void mail_log_mail_copy(void *txn, struct mail *src, struct mail *dst) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; struct mail_private *src_pmail = (struct mail_private *)src; struct mailbox *src_box = src->box; const char *desc; if (src_pmail->vmail != NULL) { /* copying a mail from virtual storage. src points to the backend mail, but we want to log the virtual mailbox name. */ src_box = src_pmail->vmail->box; } desc = t_strdup_printf("copy from %s", str_sanitize(mailbox_get_vname(src_box), MAILBOX_NAME_LOG_LEN)); mail_log_append_mail_message(ctx, dst, MAIL_LOG_EVENT_COPY, desc); } static void mail_log_mail_expunge(void *txn, struct mail *mail) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; struct mail_private *p = (struct mail_private*)mail; mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_EXPUNGE, p->autoexpunged ? "autoexpunge" : "expunge"); } static void mail_log_mail_update_flags(void *txn, struct mail *mail, enum mail_flags old_flags) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; enum mail_flags new_flags = mail_get_flags(mail); if (((old_flags ^ new_flags) & MAIL_DELETED) == 0) { mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_FLAG_CHANGE, "flag_change"); } else if ((old_flags & MAIL_DELETED) == 0) { mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_DELETE, "delete"); } else { mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_UNDELETE, "undelete"); } } static void mail_log_mail_update_keywords(void *txn, struct mail *mail, const char *const *old_keywords ATTR_UNUSED) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_FLAG_CHANGE, "flag_change"); } static void mail_log_save(const struct mail_log_message *msg, uint32_t uid) { if (msg->ignore) { /* not logging this save/copy */ } else if (msg->pretext == NULL) i_info("%s", msg->text); else if (uid != 0) i_info("%s%u%s", msg->pretext, uid, msg->text); else i_info("%serror%s", msg->pretext, msg->text); } static void mail_log_mail_transaction_commit(void *txn, struct mail_transaction_commit_changes *changes) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; struct mail_log_message *msg; struct seq_range_iter iter; unsigned int n = 0; uint32_t uid; seq_range_array_iter_init(&iter, &changes->saved_uids); for (msg = ctx->messages; msg != NULL; msg = msg->next) { if (msg->event == MAIL_LOG_EVENT_SAVE || msg->event == MAIL_LOG_EVENT_COPY) { if (!seq_range_array_iter_nth(&iter, n++, &uid)) uid = 0; mail_log_save(msg, uid); } else { i_assert(msg->pretext == NULL); i_info("%s", msg->text); } } i_assert(!seq_range_array_iter_nth(&iter, n, &uid)); pool_unref(&ctx->pool); } static void mail_log_mail_transaction_rollback(void *txn) { struct mail_log_mail_txn_context *ctx = (struct mail_log_mail_txn_context *)txn; pool_unref(&ctx->pool); } static void mail_log_mailbox_create(struct mailbox *box) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(box->storage->user); if ((muser->events & MAIL_LOG_EVENT_MAILBOX_CREATE) == 0) return; i_info("Mailbox created: %s", str_sanitize(mailbox_get_vname(box), MAILBOX_NAME_LOG_LEN)); } static void mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, struct mailbox *box) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(box->storage->user); if ((muser->events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0) return; i_info("Mailbox deleted: %s", str_sanitize(mailbox_get_vname(box), MAILBOX_NAME_LOG_LEN)); } static void mail_log_mailbox_rename(struct mailbox *src, struct mailbox *dest) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(src->storage->user); if ((muser->events & MAIL_LOG_EVENT_MAILBOX_RENAME) == 0) return; i_info("Mailbox renamed: %s -> %s", str_sanitize(mailbox_get_vname(src), MAILBOX_NAME_LOG_LEN), str_sanitize(mailbox_get_vname(dest), MAILBOX_NAME_LOG_LEN)); } static const struct notify_vfuncs mail_log_vfuncs = { .mail_transaction_begin = mail_log_mail_transaction_begin, .mail_save = mail_log_mail_save, .mail_copy = mail_log_mail_copy, .mail_expunge = mail_log_mail_expunge, .mail_update_flags = mail_log_mail_update_flags, .mail_update_keywords = mail_log_mail_update_keywords, .mail_transaction_commit = mail_log_mail_transaction_commit, .mail_transaction_rollback = mail_log_mail_transaction_rollback, .mailbox_create = mail_log_mailbox_create, .mailbox_delete_commit = mail_log_mailbox_delete_commit, .mailbox_rename = mail_log_mailbox_rename }; static struct notify_context *mail_log_ctx; static struct mail_storage_hooks mail_log_mail_storage_hooks = { .mail_user_created = mail_log_mail_user_created }; void mail_log_plugin_init(struct module *module) { mail_log_ctx = notify_register(&mail_log_vfuncs); mail_storage_hooks_add(module, &mail_log_mail_storage_hooks); } void mail_log_plugin_deinit(void) { mail_storage_hooks_remove(&mail_log_mail_storage_hooks); notify_unregister(mail_log_ctx); } const char *mail_log_plugin_dependencies[] = { "notify", NULL }; dovecot-2.2.33.2/src/plugins/mail-log/mail-log-plugin.h0000644000175000017500000000030613123174404017442 00000000000000#ifndef MAIL_LOG_PLUGIN_H #define MAIL_LOG_PLUGIN_H extern const char *mail_log_plugin_dependencies[]; void mail_log_plugin_init(struct module *module); void mail_log_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/mail-log/Makefile.am0000644000175000017500000000102713123174404016331 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/notify NOPLUGIN_LDFLAGS = lib20_mail_log_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_mail_log_plugin.la if DOVECOT_PLUGIN_DEPS lib20_mail_log_plugin_la_LIBADD = \ ../notify/lib15_notify_plugin.la endif lib20_mail_log_plugin_la_SOURCES = \ mail-log-plugin.c noinst_HEADERS = \ mail-log-plugin.h dovecot-2.2.33.2/src/plugins/imap-zlib/0002755000175000017500000000000013172375613014553 500000000000000dovecot-2.2.33.2/src/plugins/imap-zlib/Makefile.in0000644000175000017500000005577013172375574016562 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/imap-zlib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(imap_moduledir)" LTLIBRARIES = $(imap_module_LTLIBRARIES) lib30_imap_zlib_plugin_la_DEPENDENCIES = \ ../../lib-compression/libcompression.la am_lib30_imap_zlib_plugin_la_OBJECTS = imap-zlib-plugin.lo lib30_imap_zlib_plugin_la_OBJECTS = \ $(am_lib30_imap_zlib_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib30_imap_zlib_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib30_imap_zlib_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib30_imap_zlib_plugin_la_SOURCES) DIST_SOURCES = $(lib30_imap_zlib_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap imap_moduledir = $(moduledir) lib30_imap_zlib_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib30_imap_zlib_plugin.la lib30_imap_zlib_plugin_la_LIBADD = \ ../../lib-compression/libcompression.la lib30_imap_zlib_plugin_la_SOURCES = \ imap-zlib-plugin.c noinst_HEADERS = \ imap-zlib-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/imap-zlib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/imap-zlib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ } uninstall-imap_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ done clean-imap_moduleLTLIBRARIES: -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) @list='$(imap_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib30_imap_zlib_plugin.la: $(lib30_imap_zlib_plugin_la_OBJECTS) $(lib30_imap_zlib_plugin_la_DEPENDENCIES) $(EXTRA_lib30_imap_zlib_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib30_imap_zlib_plugin_la_LINK) -rpath $(imap_moduledir) $(lib30_imap_zlib_plugin_la_OBJECTS) $(lib30_imap_zlib_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-zlib-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(imap_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-imap_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-imap_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-imap_moduleLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-imap_moduleLTLIBRARIES install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-imap_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/imap-zlib/imap-zlib-plugin.c0000644000175000017500000001125113123174404020004 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "module-context.h" #include "imap-commands.h" #include "compression.h" #include "imap-zlib-plugin.h" #define IMAP_COMPRESS_DEFAULT_LEVEL 6 #define IMAP_ZLIB_IMAP_CONTEXT(obj) \ MODULE_CONTEXT(obj, imap_zlib_imap_module) struct zlib_client { union imap_module_context module_ctx; int (*next_state_export)(struct client *client, bool internal, buffer_t *dest, const char **error_r); const struct compression_handler *handler; }; const char *imap_zlib_plugin_version = DOVECOT_ABI_VERSION; static struct module *imap_zlib_module; static imap_client_created_func_t *next_hook_client_created; static MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module, &imap_module_register); static void client_skip_line(struct client *client) { const unsigned char *data; size_t data_size; data = i_stream_get_data(client->input, &data_size); i_assert(data_size > 0); if (data[0] == '\n') i_stream_skip(client->input, 1); else if (data[0] == '\r' && data_size > 1 && data[1] == '\n') i_stream_skip(client->input, 2); else i_unreached(); client->input_skip_line = FALSE; } static void client_update_imap_parser_streams(struct client *client) { struct client_command_context *cmd; if (client->free_parser != NULL) { imap_parser_set_streams(client->free_parser, client->input, client->output); } for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { imap_parser_set_streams(cmd->parser, client->input, client->output); } } static bool cmd_compress(struct client_command_context *cmd) { struct client *client = cmd->client; struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); const struct compression_handler *handler; const struct imap_arg *args; struct istream *old_input; struct ostream *old_output; const char *mechanism, *value; unsigned int level; /* */ if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!imap_arg_get_atom(args, &mechanism) || !IMAP_ARG_IS_EOL(&args[1])) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (zclient->handler != NULL) { client_send_tagline(cmd, t_strdup_printf( "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.", t_str_ucase(zclient->handler->name))); return TRUE; } handler = compression_lookup_handler(t_str_lcase(mechanism)); if (handler == NULL || handler->create_istream == NULL) { client_send_tagline(cmd, "NO Unknown compression mechanism."); return TRUE; } client_skip_line(client); client_send_tagline(cmd, "OK Begin compression."); value = mail_user_plugin_getenv(client->user, "imap_zlib_compress_level"); if (value == NULL || str_to_uint(value, &level) < 0 || level <= 0 || level > 9) level = IMAP_COMPRESS_DEFAULT_LEVEL; old_input = client->input; old_output = client->output; client->input = handler->create_istream(old_input, FALSE); client->output = handler->create_ostream(old_output, level); /* preserve output offset so that the bytes out counter in logout message doesn't get reset here */ client->output->offset = old_output->offset; i_stream_unref(&old_input); o_stream_unref(&old_output); client_update_imap_parser_streams(client); zclient->handler = handler; return TRUE; } static int imap_zlib_state_export(struct client *client, bool internal, buffer_t *dest, const char **error_r) { struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); if (zclient->handler != NULL && internal) { *error_r = "COMPRESS enabled"; return 0; } return zclient->next_state_export(client, internal, dest, error_r); } static void imap_zlib_client_created(struct client **clientp) { struct client *client = *clientp; struct zlib_client *zclient; if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) && compression_lookup_handler("deflate") != NULL) { zclient = p_new(client->pool, struct zlib_client, 1); MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient); zclient->next_state_export = (*clientp)->v.state_export; (*clientp)->v.state_export = imap_zlib_state_export; client_add_capability(*clientp, "COMPRESS=DEFLATE"); } if (next_hook_client_created != NULL) next_hook_client_created(clientp); } void imap_zlib_plugin_init(struct module *module) { command_register("COMPRESS", cmd_compress, 0); imap_zlib_module = module; next_hook_client_created = imap_client_created_hook_set(imap_zlib_client_created); } void imap_zlib_plugin_deinit(void) { command_unregister("COMPRESS"); imap_client_created_hook_set(next_hook_client_created); } const char imap_zlib_plugin_binary_dependency[] = "imap"; dovecot-2.2.33.2/src/plugins/imap-zlib/imap-zlib-plugin.h0000644000175000017500000000042313123174404020010 00000000000000#ifndef IMAP_ZLIB_PLUGIN_H #define IMAP_ZLIB_PLUGIN_H struct module; extern const char *imap_zlib_plugin_dependencies[]; extern const char imap_zlib_plugin_binary_dependency[]; void imap_zlib_plugin_init(struct module *module); void imap_zlib_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/imap-zlib/Makefile.am0000644000175000017500000000111013123174404016505 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-compression \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap imap_moduledir = $(moduledir) NOPLUGIN_LDFLAGS = lib30_imap_zlib_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib30_imap_zlib_plugin.la lib30_imap_zlib_plugin_la_LIBADD = \ ../../lib-compression/libcompression.la lib30_imap_zlib_plugin_la_SOURCES = \ imap-zlib-plugin.c noinst_HEADERS = \ imap-zlib-plugin.h dovecot-2.2.33.2/src/plugins/var-expand-crypt/0002755000175000017500000000000013172375614016074 500000000000000dovecot-2.2.33.2/src/plugins/var-expand-crypt/Makefile.in0000644000175000017500000006236613172375575020102 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/var-expand-crypt ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(auth_moduledir)" \ "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(auth_module_LTLIBRARIES) $(module_LTLIBRARIES) lib20_auth_var_expand_crypt_la_LIBADD = am_lib20_auth_var_expand_crypt_la_OBJECTS = \ var-expand-crypt-plugin.lo lib20_auth_var_expand_crypt_la_OBJECTS = \ $(am_lib20_auth_var_expand_crypt_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_auth_var_expand_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib20_auth_var_expand_crypt_la_LDFLAGS) $(LDFLAGS) -o $@ lib20_var_expand_crypt_la_LIBADD = am_lib20_var_expand_crypt_la_OBJECTS = var-expand-crypt-plugin.lo lib20_var_expand_crypt_la_OBJECTS = \ $(am_lib20_var_expand_crypt_la_OBJECTS) lib20_var_expand_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_var_expand_crypt_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_auth_var_expand_crypt_la_SOURCES) \ $(lib20_var_expand_crypt_la_SOURCES) DIST_SOURCES = $(lib20_auth_var_expand_crypt_la_SOURCES) \ $(lib20_var_expand_crypt_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dcrypt lib20_var_expand_crypt_la_LDFLAGS = -module -avoid-version lib20_auth_var_expand_crypt_la_LDFLAGS = -module -avoid-version auth_moduledir = $(moduledir)/auth module_LTLIBRARIES = \ lib20_var_expand_crypt.la auth_module_LTLIBRARIES = \ lib20_auth_var_expand_crypt.la lib20_auth_var_expand_crypt_la_SOURCES = \ var-expand-crypt-plugin.c lib20_var_expand_crypt_la_SOURCES = \ var-expand-crypt-plugin.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/var-expand-crypt/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/var-expand-crypt/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-auth_moduleLTLIBRARIES: $(auth_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(auth_module_LTLIBRARIES)'; test -n "$(auth_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(auth_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(auth_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(auth_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(auth_moduledir)"; \ } uninstall-auth_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(auth_module_LTLIBRARIES)'; test -n "$(auth_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(auth_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(auth_moduledir)/$$f"; \ done clean-auth_moduleLTLIBRARIES: -test -z "$(auth_module_LTLIBRARIES)" || rm -f $(auth_module_LTLIBRARIES) @list='$(auth_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_auth_var_expand_crypt.la: $(lib20_auth_var_expand_crypt_la_OBJECTS) $(lib20_auth_var_expand_crypt_la_DEPENDENCIES) $(EXTRA_lib20_auth_var_expand_crypt_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_auth_var_expand_crypt_la_LINK) -rpath $(auth_moduledir) $(lib20_auth_var_expand_crypt_la_OBJECTS) $(lib20_auth_var_expand_crypt_la_LIBADD) $(LIBS) lib20_var_expand_crypt.la: $(lib20_var_expand_crypt_la_OBJECTS) $(lib20_var_expand_crypt_la_DEPENDENCIES) $(EXTRA_lib20_var_expand_crypt_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_var_expand_crypt_la_LINK) -rpath $(moduledir) $(lib20_var_expand_crypt_la_OBJECTS) $(lib20_var_expand_crypt_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var-expand-crypt-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(auth_moduledir)" "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-auth_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-auth_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-auth_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-auth_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-auth_moduleLTLIBRARIES install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-auth_moduleLTLIBRARIES uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/var-expand-crypt/var-expand-crypt-plugin.c0000644000175000017500000002007213165463624022660 00000000000000/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hex-binary.h" #include "base64.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "var-expand-private.h" #include "dcrypt.h" #define VAR_EXPAND_CRYPT_DEFAULT_ALGO "AES-256-CBC" struct module; enum crypt_field_format { FORMAT_HEX, FORMAT_BASE64 }; struct var_expand_crypt_context { struct var_expand_context *ctx; const char *algo; string_t *iv; string_t *enckey; enum crypt_field_format format; bool enc_result_only:1; }; static bool var_expand_crypt_initialize(const char **error_r); void var_expand_crypt_init(struct module *module); void var_expand_crypt_deinit(void); void auth_var_expand_crypt_init(struct module *module); void auth_var_expand_crypt_deinit(void); static bool has_been_init; static int var_expand_crypt_settings(struct var_expand_crypt_context *ctx, const char *const *args, const char **error_r) { while(args != NULL && *args != NULL) { const char *k = t_strcut(*args, '='); const char *value = strchr(*args, '='); if (value == NULL) { args++; continue; } else { value++; } if (strcmp(k, "iv") == 0) { str_truncate(ctx->iv, 0); var_expand_with_funcs(ctx->iv, value, ctx->ctx->table, ctx->ctx->func_table, ctx->ctx->context); const char *hexiv = t_strdup(str_c(ctx->iv)); /* try to decode IV */ str_truncate(ctx->iv, 0); hex_to_binary(hexiv, ctx->iv); if (str_len(ctx->iv) == 0) { *error_r = "iv is not valid hex encoded value"; return -1; } } if (strcmp(k, "noiv") == 0) { ctx->enc_result_only = strcasecmp(value, "yes")==0; } if (strcmp(k, "algo") == 0) { ctx->algo = value; } else if (strcmp(k, "key") == 0) { str_truncate(ctx->enckey, 0); var_expand_with_funcs(ctx->enckey, value, ctx->ctx->table, ctx->ctx->func_table, ctx->ctx->context); const char *hexkey = t_strdup(str_c(ctx->enckey)); str_truncate(ctx->enckey, 0); hex_to_binary(hexkey, ctx->enckey); if (str_len(ctx->enckey) == 0) { *error_r = "key is not valid hex encoded value"; return -1; } } else if (strcmp(k, "format") == 0) { if (strcmp(value, "hex") == 0) { ctx->format = FORMAT_HEX; } else if (strcmp(value, "base64") == 0) { ctx->format = FORMAT_BASE64; } else { *error_r = t_strdup_printf( "Cannot parse hash arguments:" "'%s' is not supported format", value); return -1; } } args++; } if (ctx->algo == NULL) { ctx->algo = "AES-256-CBC"; } return 0; } static int var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t *iv, const buffer_t *input, buffer_t *output, const char **error_r) { /* make sure IV is correct */ if (iv->used == 0) { dcrypt_ctx_sym_set_key_iv_random(dctx); /* acquire IV */ dcrypt_ctx_sym_get_iv(dctx, iv); } else if (dcrypt_ctx_sym_get_iv_length(dctx) != iv->used) { *error_r = t_strdup_printf("crypt: IV length invalid (%"PRIuSIZE_T" != %u)", iv->used, dcrypt_ctx_sym_get_iv_length(dctx)); return -1; } else { dcrypt_ctx_sym_set_iv(dctx, iv->data, iv->used); } if (dcrypt_ctx_sym_get_key_length(dctx) != key->used) { *error_r = t_strdup_printf("crypt: Key length invalid (%"PRIuSIZE_T" != %u)", key->used, dcrypt_ctx_sym_get_key_length(dctx)); return -1; } else { dcrypt_ctx_sym_set_key(dctx, key->data, key->used); } if (!dcrypt_ctx_sym_init(dctx, error_r) || !dcrypt_ctx_sym_update(dctx, input->data, input->used, output, error_r) || !dcrypt_ctx_sym_final(dctx, output, error_r)) return -1; return 0; } static int var_expand_encrypt(struct var_expand_context *_ctx, const char *key, const char *field, const char **result_r, const char **error_r) { if (!has_been_init && !var_expand_crypt_initialize(error_r)) return -1; const char *p = strchr(key, ';'); const char *const *args = NULL; const char *value; struct var_expand_crypt_context ctx; string_t *dest; int ret = 0; memset(&ctx, 0, sizeof(ctx)); ctx.ctx = _ctx; ctx.format = FORMAT_HEX; if (p != NULL) { args = t_strsplit(p+1, ","); } string_t *field_value = t_str_new(64); ctx.iv = t_str_new(64); ctx.enckey = t_str_new(64); string_t *tmp = t_str_new(128); if ((ret = var_expand_long(_ctx, field, strlen(field), &value, error_r)) < 1) { return ret; } if (*value == '\0') { *result_r = value; return ret; } if (var_expand_crypt_settings(&ctx, args, error_r) < 0) return -1; str_append(field_value, value); struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_ENCRYPT, &dctx, error_r)) return -1; ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r); dcrypt_ctx_sym_destroy(&dctx); if (ret == 0) { /* makes compiler happy */ const char *enciv = ""; const char *res = ""; switch(ctx.format) { case FORMAT_HEX: enciv = binary_to_hex(ctx.iv->data, ctx.iv->used); res = binary_to_hex(tmp->data, tmp->used); break; case FORMAT_BASE64: dest = t_str_new(32); base64_encode(ctx.iv->data, ctx.iv->used, dest); enciv = str_c(dest); dest = t_str_new(32); base64_encode(tmp->data, tmp->used, dest); res = str_c(dest); break; default: i_unreached(); } if (ctx.enc_result_only) *result_r = t_strdup(res); else *result_r = t_strdup_printf("%s$%s$", enciv, res); ret = 1; } return ret; } static int var_expand_decrypt(struct var_expand_context *_ctx, const char *key, const char *field, const char **result_r, const char **error_r) { if (!has_been_init && !var_expand_crypt_initialize(error_r)) return -1; const char *p = strchr(key, ';'); const char *const *args = NULL; const char *value; struct var_expand_crypt_context ctx; int ret = 0; memset(&ctx, 0, sizeof(ctx)); ctx.ctx = _ctx; ctx.format = FORMAT_HEX; if (p != NULL) { args = t_strsplit(p+1, ","); } string_t *field_value = t_str_new(64); ctx.iv = t_str_new(64); ctx.enckey = t_str_new(64); string_t *tmp = t_str_new(128); if ((ret = var_expand_long(_ctx, field, strlen(field), &value, error_r)) < 1) { return ret; } if (*value == '\0') { *result_r = value; return ret; } if (var_expand_crypt_settings(&ctx, args, error_r) < 0) return -1; const char *encdata = value; const char *enciv = ""; /* make sure IV is correct */ if (ctx.iv->used == 0 && (p = strchr(encdata, '$')) != NULL) { /* see if IV can be taken from data */ enciv = t_strcut(encdata, '$'); encdata = t_strcut(p+1,'$'); } str_truncate(field_value, 0); /* try to decode iv and encdata */ switch(ctx.format) { case FORMAT_HEX: if (ctx.iv->used == 0) hex_to_binary(enciv, ctx.iv); hex_to_binary(encdata, field_value); break; case FORMAT_BASE64: if (ctx.iv->used == 0) str_append_str(ctx.iv, t_base64_decode_str(enciv)); str_append_str(field_value, t_base64_decode_str(encdata)); break; } if (ctx.iv->used == 0) { *error_r = t_strdup_printf("decrypt: IV missing"); return -1; } struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_DECRYPT, &dctx, error_r)) return -1; ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r); dcrypt_ctx_sym_destroy(&dctx); if (ret == 0) { *result_r = str_c(tmp); ret = 1; } return ret; } static const struct var_expand_extension_func_table funcs[] = { { "encrypt", var_expand_encrypt }, { "decrypt", var_expand_decrypt }, { NULL, NULL, } }; static bool var_expand_crypt_initialize(const char **error_r) { return dcrypt_initialize(NULL, NULL, error_r); } void var_expand_crypt_init(struct module *module ATTR_UNUSED) { var_expand_register_func_array(funcs); /* do not initialize dcrypt here - saves alot of memory to not load openssl every time. Only load it if needed */ } void var_expand_crypt_deinit(void) { var_expand_unregister_func_array(funcs); if (has_been_init) dcrypt_deinitialize(); } void auth_var_expand_crypt_init(struct module *module) { var_expand_crypt_init(module); } void auth_var_expand_crypt_deinit(void) { var_expand_crypt_deinit(); } dovecot-2.2.33.2/src/plugins/var-expand-crypt/Makefile.am0000644000175000017500000000076713165463624020061 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dcrypt NOPLUGIN_LDFLAGS = lib20_var_expand_crypt_la_LDFLAGS = -module -avoid-version lib20_auth_var_expand_crypt_la_LDFLAGS = -module -avoid-version auth_moduledir = $(moduledir)/auth module_LTLIBRARIES = \ lib20_var_expand_crypt.la auth_module_LTLIBRARIES = \ lib20_auth_var_expand_crypt.la lib20_auth_var_expand_crypt_la_SOURCES = \ var-expand-crypt-plugin.c lib20_var_expand_crypt_la_SOURCES = \ var-expand-crypt-plugin.c dovecot-2.2.33.2/src/plugins/notify/0002755000175000017500000000000013172375613014177 500000000000000dovecot-2.2.33.2/src/plugins/notify/Makefile.in0000644000175000017500000005753713172375574016211 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/notify ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(module_LTLIBRARIES) lib15_notify_plugin_la_LIBADD = am_lib15_notify_plugin_la_OBJECTS = notify-plugin.lo notify-storage.lo lib15_notify_plugin_la_OBJECTS = $(am_lib15_notify_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib15_notify_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib15_notify_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib15_notify_plugin_la_SOURCES) DIST_SOURCES = $(lib15_notify_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/maildir lib15_notify_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib15_notify_plugin.la lib15_notify_plugin_la_SOURCES = \ notify-plugin.c \ notify-storage.c headers = \ notify-plugin.h \ notify-plugin-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/notify/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/notify/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib15_notify_plugin.la: $(lib15_notify_plugin_la_OBJECTS) $(lib15_notify_plugin_la_DEPENDENCIES) $(EXTRA_lib15_notify_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib15_notify_plugin_la_LINK) -rpath $(moduledir) $(lib15_notify_plugin_la_OBJECTS) $(lib15_notify_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify-storage.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/notify/notify-storage.c0000644000175000017500000001635613165463624017250 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "notify-plugin-private.h" #define NOTIFY_CONTEXT(obj) \ MODULE_CONTEXT(obj, notify_storage_module) #define NOTIFY_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, notify_mail_module) static MODULE_CONTEXT_DEFINE_INIT(notify_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(notify_mail_module, &mail_module_register); static void notify_mail_expunge(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail); notify_contexts_mail_expunge(_mail); lmail->super.expunge(_mail); } static void notify_mail_update_flags(struct mail *_mail, enum modify_type modify_type, enum mail_flags flags) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail); enum mail_flags old_flags, new_flags; old_flags = mail_get_flags(_mail); lmail->super.update_flags(_mail, modify_type, flags); new_flags = mail_get_flags(_mail); if ((old_flags ^ new_flags) == 0) return; notify_contexts_mail_update_flags(_mail, old_flags); } static void notify_mail_update_keywords(struct mail *_mail, enum modify_type modify_type, struct mail_keywords *keywords) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail); const char *const *old_keywords, *const *new_keywords; unsigned int i; old_keywords = mail_get_keywords(_mail); lmail->super.update_keywords(_mail, modify_type, keywords); new_keywords = mail_get_keywords(_mail); for (i = 0; old_keywords[i] != NULL && new_keywords[i] != NULL; i++) { if (strcmp(old_keywords[i], new_keywords[i]) != 0) break; } if (old_keywords[i] == NULL && new_keywords[i] == NULL) return; notify_contexts_mail_update_keywords(_mail, old_keywords); } static void notify_mail_allocated(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *lmail; if ((_mail->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) != 0) return; lmail = p_new(mail->pool, union mail_module_context, 1); lmail->super = *v; mail->vlast = &lmail->super; v->expunge = notify_mail_expunge; v->update_flags = notify_mail_update_flags; v->update_keywords = notify_mail_update_keywords; MODULE_CONTEXT_SET_SELF(mail, notify_mail_module, lmail); } static int notify_copy(struct mail_save_context *ctx, struct mail *mail) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(ctx->transaction->box); int ret; if ((ret = lbox->super.copy(ctx, mail)) < 0) return -1; if ((ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) != 0) { /* no notifications */ } else if (ctx->saving) { /* we came from mailbox_save_using_mail() */ notify_contexts_mail_save(ctx->dest_mail); } else { notify_contexts_mail_copy(mail, ctx->dest_mail); } return ret; } static int notify_save_finish(struct mail_save_context *ctx) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(ctx->transaction->box); struct mail *dest_mail = ctx->copying_via_save ? NULL : ctx->dest_mail; if (lbox->super.save_finish(ctx) < 0) return -1; if (dest_mail != NULL && (ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0) notify_contexts_mail_save(dest_mail); return 0; } static struct mailbox_transaction_context * notify_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); struct mailbox_transaction_context *t; t = lbox->super.transaction_begin(box, flags); if ((t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0) notify_contexts_mail_transaction_begin(t); return t; } static int notify_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(t->box); bool no_notify = (t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) != 0; if ((lbox->super.transaction_commit(t, changes_r)) < 0) { notify_contexts_mail_transaction_rollback(t); return -1; } /* FIXME: note that t is already freed at this stage. it's not actually being dereferenced anymore though. still, a bit unsafe.. */ if (!no_notify) notify_contexts_mail_transaction_commit(t, changes_r); return 0; } static void notify_transaction_rollback(struct mailbox_transaction_context *t) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(t->box); if ((t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0) notify_contexts_mail_transaction_rollback(t); lbox->super.transaction_rollback(t); } static int notify_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); if (lbox->super.create_box(box, update, directory) < 0) return -1; notify_contexts_mailbox_create(box); return 0; } static int notify_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); if (lbox->super.update_box(box, update) < 0) return -1; notify_contexts_mailbox_update(box); return 0; } static int notify_mailbox_delete(struct mailbox *box) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); notify_contexts_mailbox_delete_begin(box); if (lbox->super.delete_box(box) < 0) { notify_contexts_mailbox_delete_rollback(); return -1; } notify_contexts_mailbox_delete_commit(box); return 0; } static int notify_mailbox_rename(struct mailbox *src, struct mailbox *dest) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(src); if (lbox->super.rename_box(src, dest) < 0) return -1; notify_contexts_mailbox_rename(src, dest); return 0; } static int notify_mailbox_set_subscribed(struct mailbox *box, bool set) { union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); if (lbox->super.set_subscribed(box, set) < 0) return -1; notify_contexts_mailbox_set_subscribed(box, set); return 0; } static void notify_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; union mailbox_module_context *lbox; lbox = p_new(box->pool, union mailbox_module_context, 1); lbox->super = *v; box->vlast = &lbox->super; v->copy = notify_copy; v->save_finish = notify_save_finish; v->transaction_begin = notify_transaction_begin; v->transaction_commit = notify_transaction_commit; v->transaction_rollback = notify_transaction_rollback; v->create_box = notify_mailbox_create; v->update_box = notify_mailbox_update; v->delete_box = notify_mailbox_delete; v->rename_box = notify_mailbox_rename; v->set_subscribed = notify_mailbox_set_subscribed; MODULE_CONTEXT_SET_SELF(box, notify_storage_module, lbox); } static struct mail_storage_hooks notify_mail_storage_hooks = { .mailbox_allocated = notify_mailbox_allocated, .mail_allocated = notify_mail_allocated }; void notify_plugin_init_storage(struct module *module) { mail_storage_hooks_add(module, ¬ify_mail_storage_hooks); } void notify_plugin_deinit_storage(void) { mail_storage_hooks_remove(¬ify_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/notify/notify-plugin-private.h0000644000175000017500000000251313123174404020532 00000000000000#ifndef NOTIFY_PLUGIN_PRIVATE_H #define NOTIFY_PLUGIN_PRIVATE_H #include "notify-plugin.h" void notify_contexts_mail_transaction_begin(struct mailbox_transaction_context *t); void notify_contexts_mail_save(struct mail *mail); void notify_contexts_mail_copy(struct mail *src, struct mail *dst); void notify_contexts_mail_expunge(struct mail *mail); void notify_contexts_mail_update_flags(struct mail *mail, enum mail_flags old_flags); void notify_contexts_mail_update_keywords(struct mail *mail, const char *const *old_keywords); void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes); void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t); void notify_contexts_mailbox_create(struct mailbox *box); void notify_contexts_mailbox_update(struct mailbox *box); void notify_contexts_mailbox_delete_begin(struct mailbox *box); void notify_contexts_mailbox_delete_commit(struct mailbox *box); void notify_contexts_mailbox_delete_rollback(void); void notify_contexts_mailbox_rename(struct mailbox *src, struct mailbox *dest); void notify_contexts_mailbox_set_subscribed(struct mailbox *box, bool subscribed); void notify_plugin_init_storage(struct module *module); void notify_plugin_deinit_storage(void); #endif dovecot-2.2.33.2/src/plugins/notify/notify-plugin.c0000644000175000017500000001532713123174404017064 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "llist.h" #include "mail-storage.h" #include "notify-plugin-private.h" struct notify_mail_txn { struct notify_mail_txn *prev, *next; struct mailbox_transaction_context *parent_mailbox_txn; struct mail *tmp_mail; void *txn; }; struct notify_context { struct notify_context *prev, *next; struct notify_vfuncs v; struct notify_mail_txn *mail_txn_list; void *mailbox_delete_txn; }; const char *notify_plugin_version = DOVECOT_ABI_VERSION; static struct notify_context *ctx_list = NULL; static struct notify_mail_txn * notify_context_find_mail_txn(struct notify_context *ctx, struct mailbox_transaction_context *t) { struct notify_mail_txn *mail_txn = ctx->mail_txn_list; for (; mail_txn != NULL; mail_txn = mail_txn->next) { if (mail_txn->parent_mailbox_txn == t) return mail_txn; } i_panic("no notify_mail_txn found"); } void notify_contexts_mail_transaction_begin(struct mailbox_transaction_context *t) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { mail_txn = i_new(struct notify_mail_txn, 1); mail_txn->parent_mailbox_txn = t; mail_txn->txn = ctx->v.mail_transaction_begin == NULL ? NULL : ctx->v.mail_transaction_begin(t); DLLIST_PREPEND(&ctx->mail_txn_list, mail_txn); } } void notify_contexts_mail_save(struct mail *mail) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_save == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, mail->transaction); ctx->v.mail_save(mail_txn->txn, mail); } } void notify_contexts_mail_copy(struct mail *src, struct mail *dst) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_copy == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, dst->transaction); ctx->v.mail_copy(mail_txn->txn, src, dst); } } void notify_contexts_mail_expunge(struct mail *mail) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_expunge == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, mail->transaction); ctx->v.mail_expunge(mail_txn->txn, mail); } } void notify_contexts_mail_update_flags(struct mail *mail, enum mail_flags old_flags) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_update_flags == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, mail->transaction); ctx->v.mail_update_flags(mail_txn->txn, mail, old_flags); } } void notify_contexts_mail_update_keywords(struct mail *mail, const char *const *old_keywords) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_update_keywords == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, mail->transaction); ctx->v.mail_update_keywords(mail_txn->txn, mail, old_keywords); } } void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mail_transaction_commit == NULL) continue; mail_txn = notify_context_find_mail_txn(ctx, t); if (ctx->v.mail_transaction_commit != NULL) ctx->v.mail_transaction_commit(mail_txn->txn, changes); DLLIST_REMOVE(&ctx->mail_txn_list, mail_txn); i_free(mail_txn); } } void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t) { struct notify_context *ctx; struct notify_mail_txn *mail_txn; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { mail_txn = notify_context_find_mail_txn(ctx, t); if (ctx->v.mail_transaction_rollback != NULL) ctx->v.mail_transaction_rollback(mail_txn->txn); DLLIST_REMOVE(&ctx->mail_txn_list, mail_txn); i_free(mail_txn); } } void notify_contexts_mailbox_create(struct mailbox *box) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_create != NULL) ctx->v.mailbox_create(box); } } void notify_contexts_mailbox_update(struct mailbox *box) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_update != NULL) ctx->v.mailbox_update(box); } } void notify_contexts_mailbox_delete_begin(struct mailbox *box) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { ctx->mailbox_delete_txn = ctx->v.mailbox_delete_begin == NULL ? NULL : ctx->v.mailbox_delete_begin(box); } } void notify_contexts_mailbox_delete_commit(struct mailbox *box) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_delete_commit != NULL) { ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn, box); } ctx->mailbox_delete_txn = NULL; } } void notify_contexts_mailbox_delete_rollback(void) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_delete_rollback != NULL) ctx->v.mailbox_delete_rollback(ctx->mailbox_delete_txn); ctx->mailbox_delete_txn = NULL; } } void notify_contexts_mailbox_rename(struct mailbox *src, struct mailbox *dest) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_rename != NULL) ctx->v.mailbox_rename(src, dest); } } void notify_contexts_mailbox_set_subscribed(struct mailbox *box, bool subscribed) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { if (ctx->v.mailbox_set_subscribed != NULL) ctx->v.mailbox_set_subscribed(box, subscribed); } } struct notify_context * notify_register(const struct notify_vfuncs *v) { struct notify_context *ctx; ctx = i_new(struct notify_context, 1); ctx->v = *v; DLLIST_PREPEND(&ctx_list, ctx); return ctx; } void notify_unregister(struct notify_context *ctx) { struct notify_mail_txn *mail_txn = ctx->mail_txn_list; for (; mail_txn != NULL; mail_txn = mail_txn->next) { if (ctx->v.mail_transaction_rollback != NULL) ctx->v.mail_transaction_rollback(mail_txn->txn); } if (ctx->mailbox_delete_txn != NULL && ctx->v.mailbox_delete_rollback != NULL) ctx->v.mailbox_delete_rollback(ctx->mailbox_delete_txn); DLLIST_REMOVE(&ctx_list, ctx); i_free(ctx); } void notify_plugin_init(struct module *module) { notify_plugin_init_storage(module); } void notify_plugin_deinit(void) { notify_plugin_deinit_storage(); } dovecot-2.2.33.2/src/plugins/notify/notify-plugin.h0000644000175000017500000000271013123174404017061 00000000000000#ifndef NOTIFY_PLUGIN_H #define NOTIFY_PLUGIN_H #include "mail-types.h" struct mail; struct mail_transaction_commit_changes; struct mail_storage; struct mailbox_transaction_context; struct mailbox_list; struct mailbox; struct notify_context; struct module; struct notify_vfuncs { void *(*mail_transaction_begin)(struct mailbox_transaction_context *t); void (*mail_save)(void *txn, struct mail *mail); void (*mail_copy)(void *txn, struct mail *src, struct mail *dst); void (*mail_expunge)(void *txn, struct mail *mail); void (*mail_update_flags)(void *txn, struct mail *mail, enum mail_flags old_flags); void (*mail_update_keywords)(void *txn, struct mail *mail, const char *const *old_keywords); void (*mail_transaction_commit)(void *txn, struct mail_transaction_commit_changes *changes); void (*mail_transaction_rollback)(void *txn); void (*mailbox_create)(struct mailbox *box); void (*mailbox_update)(struct mailbox *box); void *(*mailbox_delete_begin)(struct mailbox *box); void (*mailbox_delete_commit)(void *txn, struct mailbox *box); void (*mailbox_delete_rollback)(void *txn); void (*mailbox_rename)(struct mailbox *src, struct mailbox *dest); void (*mailbox_set_subscribed)(struct mailbox *box, bool subscribed); }; struct notify_context * notify_register(const struct notify_vfuncs *vfuncs); void notify_unregister(struct notify_context *ctx); void notify_plugin_init(struct module *module); void notify_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/notify/Makefile.am0000644000175000017500000000110513123174404016135 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/maildir NOPLUGIN_LDFLAGS = lib15_notify_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib15_notify_plugin.la lib15_notify_plugin_la_SOURCES = \ notify-plugin.c \ notify-storage.c headers = \ notify-plugin.h \ notify-plugin-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/plugins/dict-ldap/0002755000175000017500000000000013172375613014530 500000000000000dovecot-2.2.33.2/src/plugins/dict-ldap/Makefile.in0000644000175000017500000005557313172375574016540 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/dict-ldap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(module_dictdir)" LTLIBRARIES = $(module_dict_LTLIBRARIES) am__DEPENDENCIES_1 = am_libdict_ldap_la_OBJECTS = dict-ldap.lo dict-ldap-settings.lo libdict_ldap_la_OBJECTS = $(am_libdict_ldap_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdict_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libdict_ldap_la_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdict_ldap_la_SOURCES) DIST_SOURCES = $(libdict_ldap_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-ldap \ -I$(top_srcdir)/src/lib-settings LIBDICT_LDAP = libdict_ldap.la libdict_ldap_la_DEPENDENCIES = $(LIBDOVECOT_LDAP) $(LIBDOVECOT_DEPS) libdict_ldap_la_LDFLAGS = -module -avoid-version libdict_ldap_la_LIBADD = $(LIBDOVECOT_LDAP) $(LIBDOVECOT) module_dictdir = $(moduledir)/dict module_dict_LTLIBRARIES = \ $(LIBDICT_LDAP) libdict_ldap_la_SOURCES = \ dict-ldap.c \ dict-ldap-settings.c noinst_HEADERS = \ dict-ldap-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/dict-ldap/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/dict-ldap/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-module_dictLTLIBRARIES: $(module_dict_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_dict_LTLIBRARIES)'; test -n "$(module_dictdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(module_dictdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(module_dictdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(module_dictdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(module_dictdir)"; \ } uninstall-module_dictLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_dict_LTLIBRARIES)'; test -n "$(module_dictdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(module_dictdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(module_dictdir)/$$f"; \ done clean-module_dictLTLIBRARIES: -test -z "$(module_dict_LTLIBRARIES)" || rm -f $(module_dict_LTLIBRARIES) @list='$(module_dict_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdict_ldap.la: $(libdict_ldap_la_OBJECTS) $(libdict_ldap_la_DEPENDENCIES) $(EXTRA_libdict_ldap_la_DEPENDENCIES) $(AM_V_CCLD)$(libdict_ldap_la_LINK) -rpath $(module_dictdir) $(libdict_ldap_la_OBJECTS) $(libdict_ldap_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-ldap-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict-ldap.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(module_dictdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-module_dictLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-module_dictLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-module_dictLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-module_dictLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-module_dictLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-module_dictLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/dict-ldap/dict-ldap-settings.h0000644000175000017500000000154513165463624020325 00000000000000#ifndef DICT_LDAP_SETTINGS_H #define DICT_LDAP_SETTINGS_H struct dict_ldap_map { /* pattern is in simplified form: all variables are stored as simple '$' character. fields array is sorted by the variable index. */ const char *pattern; const char *filter; const char *filter_iter; const char *username_attribute; const char *value_attribute; const char *base_dn; const char *scope; int scope_val; unsigned int timeout; ARRAY_TYPE(const_string) ldap_attributes; }; struct dict_ldap_settings { const char *uri; const char *bind_dn; const char *password; unsigned int timeout; unsigned int max_idle_time; unsigned int debug; unsigned int max_attribute_count; bool require_ssl; bool start_tls; ARRAY(struct dict_ldap_map) maps; }; struct dict_ldap_settings * dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r); #endif dovecot-2.2.33.2/src/plugins/dict-ldap/dict-ldap.c0000644000175000017500000002745213165463624016467 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING memcached */ #include "lib.h" #include "array.h" #include "module-dir.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "var-expand.h" #include "connection.h" #include "llist.h" #include "ldap-client.h" #include "dict.h" #include "dict-private.h" #include "dict-ldap-settings.h" static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= "; struct ldap_dict; struct dict_ldap_op { struct ldap_dict *dict; const struct dict_ldap_map *map; pool_t pool; unsigned long txid; struct dict_lookup_result res; dict_lookup_callback_t *callback; void *callback_ctx; }; struct ldap_dict { struct dict dict; struct dict_ldap_settings *set; const char *uri; const char *username; const char *base_dn; enum ldap_scope scope; pool_t pool; struct ldap_client *client; struct ioloop *ioloop, *prev_ioloop; unsigned long last_txid; unsigned int pending; struct ldap_dict *prev,*next; }; static void ldap_dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context); static bool dict_ldap_map_match(const struct dict_ldap_map *map, const char *path, ARRAY_TYPE(const_string) *values, size_t *pat_len_r, size_t *path_len_r, bool partial_ok, bool recurse) { const char *path_start = path; const char *pat, *attribute, *p; size_t len; array_clear(values); pat = map->pattern; while (*pat != '\0' && *path != '\0') { if (*pat == '$') { /* variable */ pat++; if (*pat == '\0') { /* pattern ended with this variable, it'll match the rest of the path */ len = strlen(path); if (partial_ok) { /* iterating - the last field never matches fully. if there's a trailing '/', drop it. */ pat--; if (path[len-1] == '/') { attribute = t_strndup(path, len-1); array_append(values, &attribute, 1); } else { array_append(values, &path, 1); } } else { array_append(values, &path, 1); path += len; } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; return TRUE; } /* pattern matches until the next '/' in path */ p = strchr(path, '/'); if (p != NULL) { attribute = t_strdup_until(path, p); array_append(values, &attribute, 1); path = p; } else { /* no '/' anymore, but it'll still match a partial */ array_append(values, &path, 1); path += strlen(path); pat++; } } else if (*pat == *path) { pat++; path++; } else { return FALSE; } } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; if (*pat == '\0') return *path == '\0'; else if (!partial_ok) return FALSE; else { /* partial matches must end with '/'. */ if (pat != map->pattern && pat[-1] != '/') return FALSE; /* if we're not recursing, there should be only one $variable left. */ if (recurse) return TRUE; return pat[0] == '$' && strchr(pat, '/') == NULL; } } static const struct dict_ldap_map * ldap_dict_find_map(struct ldap_dict *dict, const char *path, ARRAY_TYPE(const_string) *values) { const struct dict_ldap_map *maps; unsigned int i, count; size_t len; t_array_init(values, dict->set->max_attribute_count); maps = array_get(&dict->set->maps, &count); for (i = 0; i < count; i++) { if (dict_ldap_map_match(&maps[i], path, values, &len, &len, FALSE, FALSE)) return &maps[i]; } return NULL; } static int dict_ldap_connect(struct ldap_dict *dict, const char **error_r) { struct ldap_client_settings set; i_zero(&set); set.uri = dict->set->uri; set.bind_dn = dict->set->bind_dn; set.password = dict->set->password; set.timeout_secs = dict->set->timeout; set.max_idle_time_secs = dict->set->max_idle_time; set.debug = dict->set->debug; set.require_ssl = dict->set->require_ssl; set.start_tls = dict->set->start_tls; return ldap_client_init(&set, &dict->client, error_r); } #define IS_LDAP_ESCAPED_CHAR(c) \ ((((unsigned char)(c)) & 0x80) != 0 || strchr(LDAP_ESCAPE_CHARS, (c)) != NULL) static const char *ldap_escape(const char *str) { string_t *ret = NULL; for (const char *p = str; *p != '\0'; p++) { if (IS_LDAP_ESCAPED_CHAR(*p)) { if (ret == NULL) { ret = t_str_new((size_t) (p - str) + 64); str_append_n(ret, str, (size_t) (p - str)); } str_printfa(ret, "\\%02X", (unsigned char)*p); } else if (ret != NULL) str_append_c(ret, *p); } return ret == NULL ? str : str_c(ret); } static void ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv, string_t *query_r) { const char *template; ARRAY(struct var_expand_table) exp; struct var_expand_table entry; t_array_init(&exp, 8); entry.key = '\0'; entry.value = ldap_escape(dict->username); entry.long_key = "username"; array_append(&exp, &entry, 1); if (priv) { template = t_strdup_printf("(&(%s=%s)%s)", map->username_attribute, "%{username}", map->filter); } else { template = map->filter; } for(size_t i = 0; i < array_count(values) && i < array_count(&(map->ldap_attributes)); i++) { struct var_expand_table entry; const char *const *valuep = array_idx(values, i); const char *const *long_keyp = array_idx(&(map->ldap_attributes), i); entry.value = ldap_escape(*valuep); entry.long_key = *long_keyp; array_append(&exp, &entry, 1); } array_append_zero(&exp); var_expand(query_r, template, array_idx(&exp, 0)); } static int ldap_dict_init(struct dict *dict_driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { pool_t pool = pool_alloconly_create("ldap dict", 2048); struct ldap_dict *dict = p_new(pool, struct ldap_dict, 1); dict->pool = pool; dict->dict = *dict_driver; dict->username = p_strdup(pool, set->username); dict->uri = p_strdup(pool, uri); dict->set = dict_ldap_settings_read(pool, uri, error_r); if (dict->set == NULL) { pool_unref(&pool); return -1; } if (dict_ldap_connect(dict, error_r) < 0) { pool_unref(&pool); return -1; } *dict_r = (struct dict*)dict; *error_r = NULL; return 0; } static void ldap_dict_deinit(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; ldap_client_deinit(&ctx->client); pool_unref(&ctx->pool); } static int ldap_dict_wait(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; i_assert(ctx->ioloop == NULL); ctx->prev_ioloop = current_ioloop; ctx->ioloop = io_loop_create(); dict_switch_ioloop(dict); do { io_loop_run(current_ioloop); } while (ctx->pending > 0); io_loop_set_current(ctx->prev_ioloop); dict_switch_ioloop(dict); io_loop_set_current(ctx->ioloop); io_loop_destroy(&ctx->ioloop); ctx->prev_ioloop = NULL; return 0; } static bool ldap_dict_switch_ioloop(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; ldap_client_switch_ioloop(ctx->client); return ctx->pending > 0; } static void ldap_dict_lookup_done(const struct dict_lookup_result *result, void *ctx) { struct dict_lookup_result *res = ctx; res->ret = result->ret; res->value = t_strdup(result->value); res->error = t_strdup(result->error); } static void ldap_dict_lookup_callback(struct ldap_result *result, struct dict_ldap_op *op) { pool_t pool = op->pool; struct ldap_search_iterator *iter; const struct ldap_entry *entry; op->dict->pending--; if (ldap_result_has_failed(result)) { op->res.ret = -1; op->res.error = ldap_result_get_error(result); } else { iter = ldap_search_iterator_init(result); entry = ldap_search_iterator_next(iter); if (entry != NULL) { if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback got dn %s", ldap_entry_dn(entry)); /* try extract value */ const char *const *values = ldap_entry_get_attribute(entry, op->map->value_attribute); if (values != NULL) { const char **new_values; if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback got attribute %s", op->map->value_attribute); op->res.ret = 1; new_values = p_new(op->pool, const char *, 2); new_values[0] = p_strdup(op->pool, values[0]); op->res.values = new_values; op->res.value = op->res.values[0]; } else { if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback dit not get attribute %s", op->map->value_attribute); op->res.value = NULL; } } ldap_search_iterator_deinit(&iter); } op->callback(&(op->res), op->callback_ctx); pool_unref(&pool); } static int ldap_dict_lookup(struct dict *dict, pool_t pool, const char *key, const char **value_r) { struct dict_lookup_result res; pool_t orig_pool = pool; int ret; ldap_dict_lookup_async(dict, key, ldap_dict_lookup_done, &res); if ((ret = ldap_dict_wait(dict)) == 0) { if (res.ret == 0) { *value_r = p_strdup(orig_pool, res.value); } else ret = res.ret; } return ret; } /* static struct dict_iterate_context *ldap_dict_iterate_init(struct dict *dict, const char *const *paths, enum dict_iterate_flags flags) { return NULL; } static bool ldap_dict_iterate(struct dict_iterate_context *ctx, const char **key_r, const char **value_r) { return FALSE; } static int ldap_dict_iterate_deinit(struct dict_iterate_context *ctx) { return -1; } static struct dict_transaction_context ldap_dict_transaction_init(struct dict *dict); static int ldap_dict_transaction_commit(struct dict_transaction_context *ctx, bool async, dict_transaction_commit_callback_t *callback, void *context); static void ldap_dict_transaction_rollback(struct dict_transaction_context *ctx); static void ldap_dict_set(struct dict_transaction_context *ctx, const char *key, const char *value); static void ldap_dict_unset(struct dict_transaction_context *ctx, const char *key); static void ldap_dict_append(struct dict_transaction_context *ctx, const char *key, const char *value); static void ldap_dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); */ static void ldap_dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context) { struct ldap_search_input input; struct ldap_dict *ctx = (struct ldap_dict*)dict; struct dict_ldap_op *op; pool_t oppool = pool_alloconly_create("ldap dict lookup", 64); string_t *query = str_new(oppool, 64); op = p_new(oppool, struct dict_ldap_op, 1); op->pool = oppool; op->dict = ctx; op->callback = callback; op->callback_ctx = context; op->txid = ctx->last_txid++; /* key needs to be transformed into something else */ ARRAY_TYPE(const_string) values; const char *attributes[2] = {0, 0}; t_array_init(&values, 8); const struct dict_ldap_map *map = ldap_dict_find_map(ctx, key, &values); if (map != NULL) { op->map = map; attributes[0] = map->value_attribute; /* build lookup */ i_zero(&input); input.base_dn = map->base_dn; input.scope = map->scope_val; ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0, query); input.filter = str_c(query); input.attributes = attributes; input.timeout_secs = ctx->set->timeout; ctx->pending++; ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op); } else { op->res.error = "no such key"; callback(&(op->res), context); pool_unref(&oppool); } } struct dict dict_driver_ldap = { .name = "ldap", { .init = ldap_dict_init, .deinit = ldap_dict_deinit, .wait = ldap_dict_wait, .lookup = ldap_dict_lookup, .lookup_async = ldap_dict_lookup_async, .switch_ioloop = ldap_dict_switch_ioloop, } }; void dict_ldap_init(struct module *module ATTR_UNUSED); void dict_ldap_deinit(void); void dict_ldap_init(struct module *module ATTR_UNUSED) { dict_driver_register(&dict_driver_ldap); } void dict_ldap_deinit(void) { ldap_clients_cleanup(); dict_driver_unregister(&dict_driver_ldap); } const char *dict_ldap_plugin_dependencies[] = { NULL }; dovecot-2.2.33.2/src/plugins/dict-ldap/Makefile.am0000644000175000017500000000103213165463624016500 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-ldap \ -I$(top_srcdir)/src/lib-settings LIBDICT_LDAP = libdict_ldap.la libdict_ldap_la_DEPENDENCIES = $(LIBDOVECOT_LDAP) $(LIBDOVECOT_DEPS) libdict_ldap_la_LDFLAGS = -module -avoid-version libdict_ldap_la_LIBADD = $(LIBDOVECOT_LDAP) $(LIBDOVECOT) module_dictdir = $(moduledir)/dict module_dict_LTLIBRARIES = \ $(LIBDICT_LDAP) libdict_ldap_la_SOURCES = \ dict-ldap.c \ dict-ldap-settings.c noinst_HEADERS = \ dict-ldap-settings.h dovecot-2.2.33.2/src/plugins/dict-ldap/dict-ldap-settings.c0000644000175000017500000001745413165463624020326 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "settings.h" #include "dict-ldap-settings.h" #include static const char *dict_ldap_commonName = "cn"; static const char *dict_ldap_empty_filter = ""; enum section_type { SECTION_ROOT = 0, SECTION_MAP, SECTION_FIELDS }; struct dict_ldap_map_attribute { const char *name; const char *variable; }; struct setting_parser_ctx { pool_t pool; struct dict_ldap_settings *set; enum section_type type; struct dict_ldap_map cur_map; ARRAY(struct dict_ldap_map_attribute) cur_attributes; }; #undef DEF_STR #undef DEF_BOOL #undef DEF_UINT #define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map) #define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map) static const struct setting_def dict_ldap_map_setting_defs[] = { DEF_STR(pattern), DEF_STR(filter), DEF_STR(filter_iter), DEF_STR(username_attribute), DEF_STR(value_attribute), DEF_STR(base_dn), DEF_STR(scope), { 0, NULL, 0 } }; static const char *pattern_read_name(const char **pattern) { const char *p = *pattern, *name; if (*p == '{') { /* ${name} */ name = ++p; p = strchr(p, '}'); if (p == NULL) { /* error, but allow anyway */ *pattern += strlen(*pattern); return ""; } *pattern = p + 1; } else { /* $name - ends at the first non-alnum_ character */ name = p; for (; *p != '\0'; p++) { if (!i_isalnum(*p) && *p != '_') break; } *pattern = p; } name = t_strdup_until(name, p); return name; } static const char *dict_ldap_attributes_map(struct setting_parser_ctx *ctx) { struct dict_ldap_map_attribute *attributes; string_t *pattern; const char *p, *name; unsigned int i, count; /* go through the variables in the pattern, replace them with plain '$' character and add its ldap attribute */ pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1); attributes = array_get_modifiable(&ctx->cur_attributes, &count); p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, count); for (p = ctx->cur_map.pattern; *p != '\0';) { if (*p != '$') { str_append_c(pattern, *p); p++; continue; } p++; str_append_c(pattern, '$'); name = pattern_read_name(&p); for (i = 0; i < count; i++) { if (attributes[i].variable != NULL && strcmp(attributes[i].variable, name) == 0) break; } if (i == count) { return t_strconcat("Missing LDAP attribute for variable: ", name, NULL); } /* mark this attribute as used */ attributes[i].variable = NULL; array_append(&ctx->cur_map.ldap_attributes, &attributes[i].name, 1); } /* make sure there aren't any unused attributes */ for (i = 0; i < count; i++) { if (attributes[i].variable != NULL) { return t_strconcat("Unused variable: ", attributes[i].variable, NULL); } } if (ctx->set->max_attribute_count < count) ctx->set->max_attribute_count = count; ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern)); return NULL; } static const char *dict_ldap_map_finish(struct setting_parser_ctx *ctx) { if (ctx->cur_map.pattern == NULL) return "Missing setting: pattern"; if (ctx->cur_map.filter == NULL) ctx->cur_map.filter = dict_ldap_empty_filter; if (*ctx->cur_map.filter != '\0') { const char *ptr = ctx->cur_map.filter; if (*ptr != '(') return "Filter must start with ("; while(*ptr != '\0') ptr++; ptr--; if (*ptr != ')') return "Filter must end with )"; } if (ctx->cur_map.value_attribute == NULL) return "Missing setting: value_attribute"; if (ctx->cur_map.username_attribute == NULL) { /* default to commonName */ ctx->cur_map.username_attribute = dict_ldap_commonName; } if (ctx->cur_map.scope == NULL) { ctx->cur_map.scope_val = 2; /* subtree */ } else { if (!strcasecmp(ctx->cur_map.scope, "one")) ctx->cur_map.scope_val = 1; else if (!strcasecmp(ctx->cur_map.scope, "base")) ctx->cur_map.scope_val = 0; else if (!strcasecmp(ctx->cur_map.scope, "subtree")) ctx->cur_map.scope_val = 2; else return "Scope must be one, base or subtree"; } if (!array_is_created(&ctx->cur_map.ldap_attributes)) { /* no attributes besides value. allocate the array anyway. */ p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, 1); if (strchr(ctx->cur_map.pattern, '$') != NULL) return "Missing attributes for pattern variables"; } array_append(&ctx->set->maps, &ctx->cur_map, 1); i_zero(&ctx->cur_map); return NULL; } static const char * parse_setting(const char *key, const char *value, struct setting_parser_ctx *ctx) { struct dict_ldap_map_attribute *attribute; switch (ctx->type) { case SECTION_ROOT: if (strcmp(key, "uri") == 0) { ctx->set->uri = p_strdup(ctx->pool, value); return NULL; } if (strcmp(key, "bind_dn") == 0) { ctx->set->bind_dn = p_strdup(ctx->pool, value); return NULL; } if (strcmp(key, "password") == 0) { ctx->set->password = p_strdup(ctx->pool, value); return NULL; } if (strcmp(key, "timeout") == 0) { if (str_to_uint(value, &ctx->set->timeout) != 0) { return "Invalid timeout value"; } return NULL; } if (strcmp(key, "max_idle_time") == 0) { if (str_to_uint(value, &ctx->set->max_idle_time) != 0) { return "Invalid max_idle_time value"; } return NULL; } if (strcmp(key, "debug") == 0) { if (str_to_uint(value, &ctx->set->debug) != 0) { return "invalid debug value"; } return NULL; } if (strcmp(key, "tls") == 0) { if (strcasecmp(value, "yes") == 0) { ctx->set->require_ssl = TRUE; ctx->set->start_tls = TRUE; } else if (strcasecmp(value, "no") == 0) { ctx->set->require_ssl = FALSE; ctx->set->start_tls = FALSE; } else if (strcasecmp(value, "try") == 0) { ctx->set->require_ssl = FALSE; ctx->set->start_tls = TRUE; } else { return "tls must be yes, try or no"; } return NULL; } break; case SECTION_MAP: return parse_setting_from_defs(ctx->pool, dict_ldap_map_setting_defs, &ctx->cur_map, key, value); case SECTION_FIELDS: if (*value != '$') { return t_strconcat("Value is missing '$' for attribute: ", key, NULL); } attribute = array_append_space(&ctx->cur_attributes); attribute->name = p_strdup(ctx->pool, key); attribute->variable = p_strdup(ctx->pool, value + 1); return NULL; } return t_strconcat("Unknown setting: ", key, NULL); } static bool parse_section(const char *type, const char *name ATTR_UNUSED, struct setting_parser_ctx *ctx, const char **error_r) { switch (ctx->type) { case SECTION_ROOT: if (type == NULL) return FALSE; if (strcmp(type, "map") == 0) { array_clear(&ctx->cur_attributes); ctx->type = SECTION_MAP; return TRUE; } break; case SECTION_MAP: if (type == NULL) { ctx->type = SECTION_ROOT; *error_r = dict_ldap_map_finish(ctx); return FALSE; } if (strcmp(type, "fields") == 0) { ctx->type = SECTION_FIELDS; return TRUE; } break; case SECTION_FIELDS: if (type == NULL) { ctx->type = SECTION_MAP; *error_r = dict_ldap_attributes_map(ctx); return FALSE; } break; } *error_r = t_strconcat("Unknown section: ", type, NULL); return FALSE; } struct dict_ldap_settings * dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r) { struct setting_parser_ctx ctx; i_zero(&ctx); ctx.pool = pool; ctx.set = p_new(pool, struct dict_ldap_settings, 1); t_array_init(&ctx.cur_attributes, 16); p_array_init(&ctx.set->maps, pool, 8); ctx.set->timeout = 30; /* default timeout */ ctx.set->require_ssl = FALSE; /* try to start SSL */ ctx.set->start_tls = TRUE; if (!settings_read(path, NULL, parse_setting, parse_section, &ctx, error_r)) return NULL; if (ctx.set->uri == NULL) { *error_r = t_strdup_printf("Error in configuration file %s: " "Missing ldap uri", path); return NULL; } return ctx.set; } dovecot-2.2.33.2/src/plugins/replication/0002755000175000017500000000000013172375613015200 500000000000000dovecot-2.2.33.2/src/plugins/replication/Makefile.in0000644000175000017500000005574213172375575017207 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/replication ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib20_replication_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la am_lib20_replication_plugin_la_OBJECTS = replication-plugin.lo lib20_replication_plugin_la_OBJECTS = \ $(am_lib20_replication_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_replication_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_replication_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_replication_plugin_la_SOURCES) DIST_SOURCES = $(lib20_replication_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/replication \ -I$(top_srcdir)/src/plugins/notify lib20_replication_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_replication_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib20_replication_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../notify/lib15_notify_plugin.la lib20_replication_plugin_la_SOURCES = \ replication-plugin.c noinst_HEADERS = \ replication-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/replication/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/replication/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_replication_plugin.la: $(lib20_replication_plugin_la_OBJECTS) $(lib20_replication_plugin_la_DEPENDENCIES) $(EXTRA_lib20_replication_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_replication_plugin_la_LINK) -rpath $(moduledir) $(lib20_replication_plugin_la_OBJECTS) $(lib20_replication_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replication-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/replication/replication-plugin.c0000644000175000017500000002472513165463624021103 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "fd-set-nonblock.h" #include "ioloop.h" #include "net.h" #include "write-full.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "notify-plugin.h" #include "replication-common.h" #include "replication-plugin.h" #define REPLICATION_SOCKET_NAME "replication-notify" #define REPLICATION_FIFO_NAME "replication-notify-fifo" #define REPLICATION_NOTIFY_DELAY_MSECS 500 #define REPLICATION_SYNC_TIMEOUT_SECS 10 #define REPLICATION_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, replication_user_module) struct replication_user { union mail_user_module_context module_ctx; const char *socket_path; struct timeout *to; enum replication_priority priority; unsigned int sync_secs; }; struct replication_mail_txn_context { struct mail_namespace *ns; bool new_messages; }; static MODULE_CONTEXT_DEFINE_INIT(replication_user_module, &mail_user_module_register); static int fifo_fd; static bool fifo_failed; static char *fifo_path; static int replication_fifo_notify(struct mail_user *user, enum replication_priority priority) { string_t *str; ssize_t ret; if (fifo_failed) return -1; if (fifo_fd == -1) { fifo_fd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (fifo_fd == -1) { i_error("open(%s) failed: %m", fifo_path); fifo_failed = TRUE; return -1; } } /* \t */ str = t_str_new(256); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); switch (priority) { case REPLICATION_PRIORITY_NONE: case REPLICATION_PRIORITY_SYNC: i_unreached(); case REPLICATION_PRIORITY_LOW: str_append(str, "low"); break; case REPLICATION_PRIORITY_HIGH: str_append(str, "high"); break; } str_append_c(str, '\n'); ret = write(fifo_fd, str_data(str), str_len(str)); i_assert(ret != 0); if (ret != (ssize_t)str_len(str)) { if (ret > 0) i_error("write(%s) wrote partial data", fifo_path); else if (errno == EAGAIN) { /* busy, try again later */ return 0; } else if (errno != EPIPE) { i_error("write(%s) failed: %m", fifo_path); } else { /* server was probably restarted, don't bother logging this. */ } if (close(fifo_fd) < 0) i_error("close(%s) failed: %m", fifo_path); fifo_fd = -1; return -1; } return 1; } static void replication_notify_now(struct mail_user *user) { struct replication_user *ruser = REPLICATION_USER_CONTEXT(user); int ret; i_assert(ruser->priority != REPLICATION_PRIORITY_NONE); i_assert(ruser->priority != REPLICATION_PRIORITY_SYNC); if ((ret = replication_fifo_notify(user, ruser->priority)) < 0 && !fifo_failed) { /* retry once, in case replication server was restarted */ ret = replication_fifo_notify(user, ruser->priority); } if (ret != 0) { timeout_remove(&ruser->to); ruser->priority = REPLICATION_PRIORITY_NONE; } } static int replication_notify_sync(struct mail_user *user) { struct replication_user *ruser = REPLICATION_USER_CONTEXT(user); string_t *str; char buf[1024]; int fd; ssize_t ret; bool success = FALSE; fd = net_connect_unix(ruser->socket_path); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m", ruser->socket_path); return -1; } net_set_nonblock(fd, FALSE); /* \t "sync" */ str = t_str_new(256); str_append_tabescaped(str, user->username); str_append(str, "\tsync\n"); alarm(ruser->sync_secs); if (write_full(fd, str_data(str), str_len(str)) < 0) { i_error("write(%s) failed: %m", ruser->socket_path); } else { /* + | - */ ret = read(fd, buf, sizeof(buf)); if (ret < 0) { if (errno != EINTR) { i_error("read(%s) failed: %m", ruser->socket_path); } else { i_warning("replication(%s): Sync failure: " "Timeout in %u secs", user->username, ruser->sync_secs); } } else if (ret == 0) { i_error("read(%s) failed: EOF", ruser->socket_path); } else if (buf[0] == '+') { /* success */ success = TRUE; } else if (buf[0] == '-') { /* failure */ if (buf[ret-1] == '\n') ret--; i_warning("replication(%s): Sync failure: %s", user->username, t_strndup(buf+1, ret-1)); i_warning("replication(%s): " "Remote sent invalid input: %s", user->username, t_strndup(buf, ret)); } } alarm(0); if (close(fd) < 0) i_error("close(%s) failed: %m", ruser->socket_path); return success ? 0 : -1; } static void replication_notify(struct mail_namespace *ns, enum replication_priority priority, const char *event) { struct replication_user *ruser; ruser = REPLICATION_USER_CONTEXT(ns->user); if (ruser == NULL) return; if (ns->user->mail_debug) { i_debug("replication: Replication requested by '%s', priority=%d", event, priority); } if (priority == REPLICATION_PRIORITY_SYNC) { if (replication_notify_sync(ns->user) == 0) { timeout_remove(&ruser->to); ruser->priority = REPLICATION_PRIORITY_NONE; return; } /* sync replication failed, try as "high" via fifo */ priority = REPLICATION_PRIORITY_HIGH; } if (ruser->priority < priority) ruser->priority = priority; if (ruser->to == NULL) { ruser->to = timeout_add_short(REPLICATION_NOTIFY_DELAY_MSECS, replication_notify_now, ns->user); } } static void * replication_mail_transaction_begin(struct mailbox_transaction_context *t) { struct replication_mail_txn_context *ctx; ctx = i_new(struct replication_mail_txn_context, 1); ctx->ns = mailbox_get_namespace(t->box); return ctx; } static void replication_mail_save(void *txn, struct mail *mail ATTR_UNUSED) { struct replication_mail_txn_context *ctx = (struct replication_mail_txn_context *)txn; ctx->new_messages = TRUE; } static void replication_mail_copy(void *txn, struct mail *src, struct mail *dst) { struct replication_mail_txn_context *ctx = (struct replication_mail_txn_context *)txn; if (src->box->storage != dst->box->storage) { /* copy between storages, e.g. new mail delivery */ ctx->new_messages = TRUE; } else { /* copy within storage, which isn't as high priority since the mail already exists. and especially copies to Trash or to lazy-expunge namespace is pretty low priority. */ } } static void replication_mail_transaction_commit(void *txn, struct mail_transaction_commit_changes *changes) { struct replication_mail_txn_context *ctx = (struct replication_mail_txn_context *)txn; struct replication_user *ruser = REPLICATION_USER_CONTEXT(ctx->ns->user); enum replication_priority priority; if (ruser != NULL && (ctx->new_messages || changes->changed)) { priority = !ctx->new_messages ? REPLICATION_PRIORITY_LOW : ruser->sync_secs == 0 ? REPLICATION_PRIORITY_HIGH : REPLICATION_PRIORITY_SYNC; replication_notify(ctx->ns, priority, "transaction commit"); } i_free(ctx); } static void replication_mailbox_create(struct mailbox *box) { replication_notify(mailbox_get_namespace(box), REPLICATION_PRIORITY_LOW, "mailbox create"); } static void replication_mailbox_delete_commit(void *txn ATTR_UNUSED, struct mailbox *box) { replication_notify(mailbox_get_namespace(box), REPLICATION_PRIORITY_LOW, "mailbox delete"); } static void replication_mailbox_rename(struct mailbox *src ATTR_UNUSED, struct mailbox *dest) { replication_notify(mailbox_get_namespace(dest), REPLICATION_PRIORITY_LOW, "mailbox rename"); } static void replication_mailbox_set_subscribed(struct mailbox *box, bool subscribed ATTR_UNUSED) { replication_notify(mailbox_get_namespace(box), REPLICATION_PRIORITY_LOW, "mailbox subscribe"); } static void replication_user_deinit(struct mail_user *user) { struct replication_user *ruser = REPLICATION_USER_CONTEXT(user); if (ruser->to != NULL) { replication_notify_now(user); if (ruser->to != NULL) { i_warning("%s: Couldn't send final notification " "due to fifo being busy", fifo_path); timeout_remove(&ruser->to); } } ruser->module_ctx.super.deinit(user); } static void replication_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct replication_user *ruser; const char *value; value = mail_user_plugin_getenv(user, "mail_replica"); if (value == NULL || value[0] == '\0') { if (user->mail_debug) i_debug("replication: No mail_replica setting - replication disabled"); return; } if (user->dsyncing) { /* we're running dsync, which means that the remote is telling us about a change. don't trigger a replication back to it */ if (user->mail_debug) i_debug("replication: We're running dsync - replication disabled"); return; } ruser = p_new(user->pool, struct replication_user, 1); ruser->module_ctx.super = *v; user->vlast = &ruser->module_ctx.super; v->deinit = replication_user_deinit; MODULE_CONTEXT_SET(user, replication_user_module, ruser); if (fifo_path == NULL) { /* we'll assume that all users have the same base_dir. they really should. */ fifo_path = i_strconcat(user->set->base_dir, "/"REPLICATION_FIFO_NAME, NULL); } ruser->socket_path = p_strconcat(user->pool, user->set->base_dir, "/"REPLICATION_SOCKET_NAME, NULL); value = mail_user_plugin_getenv(user, "replication_sync_timeout"); if (value != NULL && str_to_uint(value, &ruser->sync_secs) < 0) { i_error("replication(%s): " "Invalid replication_sync_timeout value: %s", user->username, value); } } static const struct notify_vfuncs replication_vfuncs = { .mail_transaction_begin = replication_mail_transaction_begin, .mail_save = replication_mail_save, .mail_copy = replication_mail_copy, .mail_transaction_commit = replication_mail_transaction_commit, .mailbox_create = replication_mailbox_create, .mailbox_delete_commit = replication_mailbox_delete_commit, .mailbox_rename = replication_mailbox_rename, .mailbox_set_subscribed = replication_mailbox_set_subscribed }; static struct notify_context *replication_ctx; static struct mail_storage_hooks replication_mail_storage_hooks = { .mail_user_created = replication_user_created }; void replication_plugin_init(struct module *module) { fifo_fd = -1; replication_ctx = notify_register(&replication_vfuncs); mail_storage_hooks_add(module, &replication_mail_storage_hooks); } void replication_plugin_deinit(void) { if (fifo_fd != -1) { if (close(fifo_fd) < 0) i_error("close(%s) failed: %m", fifo_path); fifo_fd = -1; } i_free_and_null(fifo_path); mail_storage_hooks_remove(&replication_mail_storage_hooks); notify_unregister(replication_ctx); } const char *replication_plugin_dependencies[] = { "notify", NULL }; dovecot-2.2.33.2/src/plugins/replication/replication-plugin.h0000644000175000017500000000032513123174404021063 00000000000000#ifndef REPLICATION_PLUGIN_H #define REPLICATION_PLUGIN_H extern const char *replication_plugin_dependencies[]; void replication_plugin_init(struct module *module); void replication_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/replication/Makefile.am0000644000175000017500000000111413123174404017136 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/replication \ -I$(top_srcdir)/src/plugins/notify NOPLUGIN_LDFLAGS = lib20_replication_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_replication_plugin.la if DOVECOT_PLUGIN_DEPS lib20_replication_plugin_la_LIBADD = \ ../notify/lib15_notify_plugin.la endif lib20_replication_plugin_la_SOURCES = \ replication-plugin.c noinst_HEADERS = \ replication-plugin.h dovecot-2.2.33.2/src/plugins/fts-solr/0002755000175000017500000000000013172375613014440 500000000000000dovecot-2.2.33.2/src/plugins/fts-solr/Makefile.in0000644000175000017500000005634713172375574016450 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/fts-solr ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib21_fts_solr_plugin_la_DEPENDENCIES = $(fts_plugin_dep) am_lib21_fts_solr_plugin_la_OBJECTS = fts-backend-solr.lo \ fts-backend-solr-old.lo fts-solr-plugin.lo solr-connection.lo lib21_fts_solr_plugin_la_OBJECTS = \ $(am_lib21_fts_solr_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib21_fts_solr_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib21_fts_solr_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib21_fts_solr_plugin_la_SOURCES) DIST_SOURCES = $(lib21_fts_solr_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts lib21_fts_solr_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_solr_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@fts_plugin_dep = ../fts/lib20_fts_plugin.la lib21_fts_solr_plugin_la_LIBADD = \ $(fts_plugin_dep) \ -lexpat lib21_fts_solr_plugin_la_SOURCES = \ fts-backend-solr.c \ fts-backend-solr-old.c \ fts-solr-plugin.c \ solr-connection.c noinst_HEADERS = \ fts-solr-plugin.h \ solr-connection.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/fts-solr/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/fts-solr/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib21_fts_solr_plugin.la: $(lib21_fts_solr_plugin_la_OBJECTS) $(lib21_fts_solr_plugin_la_DEPENDENCIES) $(EXTRA_lib21_fts_solr_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib21_fts_solr_plugin_la_LINK) -rpath $(moduledir) $(lib21_fts_solr_plugin_la_OBJECTS) $(lib21_fts_solr_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-backend-solr-old.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-backend-solr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-solr-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/solr-connection.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/fts-solr/fts-backend-solr-old.c0000644000175000017500000005361313165463624020446 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "strescape.h" #include "unichar.h" #include "http-url.h" #include "imap-utf7.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "mail-search.h" #include "fts-api.h" #include "solr-connection.h" #include "fts-solr-plugin.h" #include #define SOLR_CMDBUF_SIZE (1024*64) #define SOLR_MAX_MULTI_ROWS 100000 struct solr_fts_backend { struct fts_backend backend; struct solr_connection *solr_conn; char *id_username, *id_namespace; struct mail_namespace *default_ns; }; struct solr_fts_backend_update_context { struct fts_backend_update_context ctx; struct mailbox *cur_box; char *id_box_name; struct solr_connection_post *post; uint32_t prev_uid, uid_validity; string_t *cmd, *hdr; bool headers_open; bool body_open; bool documents_added; }; static const char *solr_escape_chars = "+-&|!(){}[]^\"~*?:\\/ "; static bool is_valid_xml_char(unichar_t chr) { /* Valid characters in XML: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] This function gets called only for #x80 and higher */ if (chr > 0xd7ff && chr < 0xe000) return FALSE; if (chr > 0xfffd && chr < 0x10000) return FALSE; return chr < 0x10ffff; } static void xml_encode_data(string_t *dest, const unsigned char *data, size_t len) { unichar_t chr; size_t i; for (i = 0; i < len; i++) { switch (data[i]) { case '&': str_append(dest, "&"); break; case '<': str_append(dest, "<"); break; case '>': str_append(dest, ">"); break; case '\t': case '\n': case '\r': /* exceptions to the following control char check */ str_append_c(dest, data[i]); break; default: if (data[i] < 32) { /* SOLR doesn't like control characters. replace them with spaces. */ str_append_c(dest, ' '); } else if (data[i] >= 0x80) { /* make sure the character is valid for XML so we don't get XML parser errors */ unsigned int char_len = uni_utf8_get_char_n(data + i, len - i, &chr); if (char_len > 0 && is_valid_xml_char(chr)) str_append_n(dest, data + i, char_len); else { str_append_n(dest, utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN); } i += char_len - 1; } else { str_append_c(dest, data[i]); } break; } } } static void xml_encode(string_t *dest, const char *str) { xml_encode_data(dest, (const unsigned char *)str, strlen(str)); } static const char *solr_escape_id_str(const char *str) { string_t *tmp; const char *p; for (p = str; *p != '\0'; p++) { if (*p == '/' || *p == '!') break; } if (*p == '\0') return str; tmp = t_str_new(64); for (p = str; *p != '\0'; p++) { switch (*p) { case '/': str_append(tmp, "!\\"); break; case '!': str_append(tmp, "!!"); break; default: str_append_c(tmp, *p); break; } } return str_c(tmp); } static const char *solr_escape(const char *str) { string_t *ret; unsigned int i; if (str[0] == '\0') return "\"\""; ret = t_str_new(strlen(str) + 16); for (i = 0; str[i] != '\0'; i++) { if (strchr(solr_escape_chars, str[i]) != NULL) str_append_c(ret, '\\'); str_append_c(ret, str[i]); } return str_c(ret); } static void solr_quote(string_t *dest, const char *str) { str_append(dest, solr_escape(str)); } static void solr_quote_http(string_t *dest, const char *str) { http_url_escape_param(dest, solr_escape(str)); } static void fts_solr_set_default_ns(struct solr_fts_backend *backend) { struct mail_namespace *ns = backend->backend.ns; struct fts_solr_user *fuser = FTS_SOLR_USER_CONTEXT(ns->user); const struct fts_solr_settings *set = &fuser->set; const char *str; if (backend->default_ns != NULL) return; if (set->default_ns_prefix != NULL) { backend->default_ns = mail_namespace_find_prefix(ns->user->namespaces, set->default_ns_prefix); if (backend->default_ns == NULL) { i_error("fts_solr: default_ns setting points to " "nonexistent namespace"); } } if (backend->default_ns == NULL) { backend->default_ns = mail_namespace_find_inbox(ns->user->namespaces); } while (backend->default_ns->alias_for != NULL) backend->default_ns = backend->default_ns->alias_for; if (ns != backend->default_ns) { str = solr_escape_id_str(ns->prefix); backend->id_namespace = i_strdup(str); } } static void fts_box_name_get_root(struct mail_namespace **ns, const char **name) { struct mail_namespace *orig_ns = *ns; while ((*ns)->alias_for != NULL) *ns = (*ns)->alias_for; if (**name == '\0' && *ns != orig_ns && ((*ns)->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { /* ugly workaround to allow selecting INBOX from a Maildir/ when it's not in the inbox=yes namespace. */ *name = "INBOX"; } } static const char * fts_box_get_root(struct mailbox *box, struct mail_namespace **ns_r) { struct mail_namespace *ns = mailbox_get_namespace(box); const char *name; if (t_imap_utf8_to_utf7(box->name, &name) < 0) i_unreached(); fts_box_name_get_root(&ns, &name); *ns_r = ns; return name; } static struct fts_backend *fts_backend_solr_alloc(void) { struct solr_fts_backend *backend; backend = i_new(struct solr_fts_backend, 1); backend->backend = fts_backend_solr_old; return &backend->backend; } static int fts_backend_solr_init(struct fts_backend *_backend, const char **error_r) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; struct fts_solr_user *fuser = FTS_SOLR_USER_CONTEXT(_backend->ns->user); const char *str; if (fuser == NULL) { *error_r = "Invalid fts_solr setting"; return -1; } if (solr_connection_init(fuser->set.url, fuser->set.debug, &backend->solr_conn, error_r) < 0) return -1; str = solr_escape_id_str(_backend->ns->user->username); backend->id_username = i_strdup(str); return 0; } static void fts_backend_solr_deinit(struct fts_backend *_backend) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; solr_connection_deinit(&backend->solr_conn); i_free(backend->id_namespace); i_free(backend->id_username); i_free(backend); } static void solr_add_ns_query(string_t *str, struct solr_fts_backend *backend, struct mail_namespace *ns, bool neg) { while (ns->alias_for != NULL) ns = ns->alias_for; if (ns == backend->default_ns || *ns->prefix == '\0') { if (!neg) str_append(str, " -ns:[* TO *]"); else str_append(str, " +ns:[* TO *]"); } else { if (!neg) str_append(str, " +ns:"); else str_append(str, " -ns:"); solr_quote(str, ns->prefix); } } static void solr_add_ns_query_http(string_t *str, struct solr_fts_backend *backend, struct mail_namespace *ns) { string_t *tmp; tmp = t_str_new(64); solr_add_ns_query(tmp, backend, ns, FALSE); http_url_escape_param(str, str_c(tmp)); } static int fts_backend_solr_get_last_uid_fallback(struct solr_fts_backend *backend, struct mailbox *box, uint32_t *last_uid_r) { struct mail_namespace *ns; struct mailbox_status status; struct solr_result **results; const struct seq_range *uidvals; const char *box_name; unsigned int count; string_t *str; pool_t pool; int ret = 0; str = t_str_new(256); str_append(str, "fl=uid&rows=1&sort=uid+desc&q="); box_name = fts_box_get_root(box, &ns); mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status); str_printfa(str, "uidv:%u+AND+box:", status.uidvalidity); solr_quote_http(str, box_name); solr_add_ns_query_http(str, backend, ns); str_append(str, "+AND+user:"); solr_quote_http(str, ns->user->username); pool = pool_alloconly_create("solr last uid lookup", 1024); if (solr_connection_select(backend->solr_conn, str_c(str), pool, &results) < 0) ret = -1; else if (results[0] == NULL) { /* no UIDs */ *last_uid_r = 0; } else { uidvals = array_get(&results[0]->uids, &count); i_assert(count > 0); if (count == 1 && uidvals[0].seq1 == uidvals[0].seq2) { *last_uid_r = uidvals[0].seq1; } else { i_error("fts_solr: Last UID lookup returned multiple rows"); ret = -1; } } pool_unref(&pool); return ret; } static int fts_backend_solr_get_last_uid(struct fts_backend *_backend, struct mailbox *box, uint32_t *last_uid_r) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; struct fts_index_header hdr; if (fts_index_get_header(box, &hdr)) { *last_uid_r = hdr.last_indexed_uid; return 0; } /* either nothing has been indexed, or the index was corrupted. do it the slow way. */ if (fts_backend_solr_get_last_uid_fallback(backend, box, last_uid_r) < 0) return -1; fts_index_set_last_uid(box, *last_uid_r); return 0; } static struct fts_backend_update_context * fts_backend_solr_update_init(struct fts_backend *_backend) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; struct solr_fts_backend_update_context *ctx; ctx = i_new(struct solr_fts_backend_update_context, 1); ctx->ctx.backend = _backend; ctx->cmd = str_new(default_pool, SOLR_CMDBUF_SIZE); ctx->hdr = str_new(default_pool, 4096); fts_solr_set_default_ns(backend); return &ctx->ctx; } static void xml_encode_id(struct solr_fts_backend_update_context *ctx, string_t *str, uint32_t uid) { struct solr_fts_backend *backend = (struct solr_fts_backend *)ctx->ctx.backend; if (uid != 0) str_printfa(str, "%u/", uid); else str_append(str, "L/"); if (backend->id_namespace != NULL) { xml_encode(str, backend->id_namespace); str_append_c(str, '/'); } str_printfa(str, "%u/", ctx->uid_validity); xml_encode(str, backend->id_username); str_append_c(str, '/'); xml_encode(str, ctx->id_box_name); } static void fts_backend_solr_add_doc_prefix(struct solr_fts_backend_update_context *ctx, uint32_t uid) { struct solr_fts_backend *backend = (struct solr_fts_backend *)ctx->ctx.backend; struct mailbox *box = ctx->cur_box; struct mail_namespace *ns; const char *box_name; ctx->documents_added = TRUE; str_printfa(ctx->cmd, "" "%u" "%u", uid, ctx->uid_validity); box_name = fts_box_get_root(box, &ns); if (ns != backend->default_ns) { str_append(ctx->cmd, ""); xml_encode(ctx->cmd, ns->prefix); str_append(ctx->cmd, ""); } str_append(ctx->cmd, ""); xml_encode(ctx->cmd, box_name); str_append(ctx->cmd, ""); xml_encode(ctx->cmd, ns->user->username); str_append(ctx->cmd, ""); } static int fts_backed_solr_build_commit(struct solr_fts_backend_update_context *ctx) { if (ctx->post == NULL) return 0; str_append(ctx->cmd, ""); solr_connection_post_more(ctx->post, str_data(ctx->cmd), str_len(ctx->cmd)); return solr_connection_post_end(&ctx->post); } static int fts_backend_solr_update_deinit(struct fts_backend_update_context *_ctx) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; struct solr_fts_backend *backend = (struct solr_fts_backend *)_ctx->backend; const char *str; int ret; ret = fts_backed_solr_build_commit(ctx); /* commit and wait until the documents we just indexed are visible to the following search */ str = t_strdup_printf("", ctx->documents_added ? "true" : "false"); if (solr_connection_post(backend->solr_conn, str) < 0) ret = -1; str_free(&ctx->cmd); str_free(&ctx->hdr); i_free(ctx->id_box_name); i_free(ctx); return ret; } static void fts_backend_solr_update_set_mailbox(struct fts_backend_update_context *_ctx, struct mailbox *box) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; struct mailbox_status status; struct mail_namespace *ns; if (ctx->prev_uid != 0) { fts_index_set_last_uid(ctx->cur_box, ctx->prev_uid); ctx->prev_uid = 0; } ctx->cur_box = box; ctx->uid_validity = 0; i_free_and_null(ctx->id_box_name); if (box != NULL) { ctx->id_box_name = i_strdup(fts_box_get_root(box, &ns)); mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status); ctx->uid_validity = status.uidvalidity; } } static void fts_backend_solr_update_expunge(struct fts_backend_update_context *_ctx, uint32_t uid) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; struct solr_fts_backend *backend = (struct solr_fts_backend *)_ctx->backend; T_BEGIN { string_t *cmd; cmd = t_str_new(256); str_append(cmd, ""); xml_encode_id(ctx, cmd, uid); str_append(cmd, ""); (void)solr_connection_post(backend->solr_conn, str_c(cmd)); } T_END; } static void fts_backend_solr_uid_changed(struct solr_fts_backend_update_context *ctx, uint32_t uid) { struct solr_fts_backend *backend = (struct solr_fts_backend *)ctx->ctx.backend; if (ctx->post == NULL) { i_assert(ctx->prev_uid == 0); ctx->post = solr_connection_post_begin(backend->solr_conn); str_append(ctx->cmd, ""); } else { ctx->headers_open = FALSE; if (ctx->body_open) { ctx->body_open = FALSE; str_append(ctx->cmd, ""); } str_append(ctx->cmd, ""); str_append_str(ctx->cmd, ctx->hdr); str_append(ctx->cmd, ""); str_truncate(ctx->hdr, 0); str_append(ctx->cmd, ""); } ctx->prev_uid = uid; fts_backend_solr_add_doc_prefix(ctx, uid); str_printfa(ctx->cmd, ""); xml_encode_id(ctx, ctx->cmd, uid); str_append(ctx->cmd, ""); } static bool fts_backend_solr_update_set_build_key(struct fts_backend_update_context *_ctx, const struct fts_backend_build_key *key) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; if (key->uid != ctx->prev_uid) fts_backend_solr_uid_changed(ctx, key->uid); switch (key->type) { case FTS_BACKEND_BUILD_KEY_HDR: case FTS_BACKEND_BUILD_KEY_MIME_HDR: xml_encode(ctx->hdr, key->hdr_name); str_append(ctx->hdr, ": "); ctx->headers_open = TRUE; break; case FTS_BACKEND_BUILD_KEY_BODY_PART: ctx->headers_open = FALSE; if (!ctx->body_open) { ctx->body_open = TRUE; str_append(ctx->cmd, ""); } break; case FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY: i_unreached(); } return TRUE; } static void fts_backend_solr_update_unset_build_key(struct fts_backend_update_context *_ctx) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; if (ctx->headers_open) str_append_c(ctx->hdr, '\n'); else { i_assert(ctx->body_open); str_append_c(ctx->cmd, '\n'); } } static int fts_backend_solr_update_build_more(struct fts_backend_update_context *_ctx, const unsigned char *data, size_t size) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; xml_encode_data(ctx->cmd, data, size); if (str_len(ctx->cmd) > SOLR_CMDBUF_SIZE-128) { solr_connection_post_more(ctx->post, str_data(ctx->cmd), str_len(ctx->cmd)); str_truncate(ctx->cmd, 0); } return 0; } static int fts_backend_solr_refresh(struct fts_backend *backend ATTR_UNUSED) { return 0; } static int fts_backend_solr_optimize(struct fts_backend *backend ATTR_UNUSED) { return 0; } static bool solr_add_definite_query(string_t *str, struct mail_search_arg *arg) { if (arg->no_fts) return FALSE; switch (arg->type) { case SEARCH_TEXT: { if (arg->match_not) str_append_c(str, '-'); str_append(str, "(hdr:"); solr_quote_http(str, arg->value.str); str_append(str, "+OR+body:"); solr_quote_http(str, arg->value.str); str_append(str, ")"); break; } case SEARCH_BODY: if (arg->match_not) str_append_c(str, '-'); str_append(str, "body:"); solr_quote_http(str, arg->value.str); break; default: return FALSE; } return TRUE; } static bool solr_add_definite_query_args(string_t *str, struct mail_search_arg *arg, bool and_args) { size_t last_len; last_len = str_len(str); for (; arg != NULL; arg = arg->next) { if (solr_add_definite_query(str, arg)) { arg->match_always = TRUE; last_len = str_len(str); if (and_args) str_append(str, "+AND+"); else str_append(str, "+OR+"); } } if (str_len(str) == last_len) return FALSE; str_truncate(str, last_len); return TRUE; } static int fts_backend_solr_lookup(struct fts_backend *_backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; struct mail_namespace *ns; struct mailbox_status status; string_t *str; const char *box_name; pool_t pool; struct solr_result **results; int ret; fts_solr_set_default_ns(backend); mailbox_get_open_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, &status); str = t_str_new(256); str_printfa(str, "fl=uid,score&rows=%u&sort=uid+asc&q=%%7b!lucene+q.op%%3dAND%%7d", status.uidnext); if (!solr_add_definite_query_args(str, args, and_args)) { /* can't search this query */ return 0; } /* use a separate filter query for selecting the mailbox. it shouldn't affect the score and there could be some caching benefits too. */ str_append(str, "&fq=%2Buser:"); solr_quote_http(str, box->storage->user->username); box_name = fts_box_get_root(box, &ns); str_printfa(str, "+%%2Buidv:%u+%%2Bbox:", status.uidvalidity); solr_quote_http(str, box_name); solr_add_ns_query_http(str, backend, ns); pool = pool_alloconly_create("fts solr search", 1024); ret = solr_connection_select(backend->solr_conn, str_c(str), pool, &results); if (ret == 0 && results[0] != NULL) { if ((flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0) array_append_array(&result->definite_uids, &results[0]->uids); else array_append_array(&result->maybe_uids, &results[0]->uids); array_append_array(&result->scores, &results[0]->scores); } result->scores_sorted = TRUE; pool_unref(&pool); return ret; } static char * mailbox_get_id(struct solr_fts_backend *backend, struct mail_namespace *ns, const char *mailbox, uint32_t uidvalidity) { string_t *str = t_str_new(64); str_printfa(str, "%u\001", uidvalidity); str_append(str, mailbox); if (ns != backend->default_ns) str_printfa(str, "\001%s", ns->prefix); return str_c_modifiable(str); } static int solr_search_multi(struct solr_fts_backend *backend, string_t *str, struct mailbox *const boxes[], enum fts_lookup_flags flags, struct fts_multi_result *result) { struct solr_result **solr_results; struct fts_result *fts_result; ARRAY(struct fts_result) fts_results; struct mail_namespace *ns; struct mailbox_status status; HASH_TABLE(char *, struct mailbox *) mailboxes; struct mailbox *box; const char *box_name; char *box_id; unsigned int i; size_t len; /* use a separate filter query for selecting the mailbox. it shouldn't affect the score and there could be some caching benefits too. */ str_append(str, "&fq=%2Buser:"); if (backend->backend.ns->owner != NULL) solr_quote_http(str, backend->backend.ns->owner->username); else str_append(str, "%22%22"); hash_table_create(&mailboxes, default_pool, 0, str_hash, strcmp); str_append(str, "%2B("); len = str_len(str); for (i = 0; boxes[i] != NULL; i++) { if (str_len(str) != len) str_append(str, "+OR+"); box_name = fts_box_get_root(boxes[i], &ns); mailbox_get_open_status(boxes[i], STATUS_UIDVALIDITY, &status); str_printfa(str, "%%2B(%%2Buidv:%u+%%2Bbox:", status.uidvalidity); solr_quote_http(str, box_name); solr_add_ns_query_http(str, backend, ns); str_append_c(str, ')'); box_id = mailbox_get_id(backend, ns, box_name, status.uidvalidity); hash_table_insert(mailboxes, box_id, boxes[i]); } str_append_c(str, ')'); if (solr_connection_select(backend->solr_conn, str_c(str), result->pool, &solr_results) < 0) { hash_table_destroy(&mailboxes); return -1; } p_array_init(&fts_results, result->pool, 32); for (i = 0; solr_results[i] != NULL; i++) { box = hash_table_lookup(mailboxes, solr_results[i]->box_id); if (box == NULL) { i_warning("fts_solr: Lookup returned unexpected mailbox " "with id=%s", solr_results[i]->box_id); continue; } fts_result = array_append_space(&fts_results); fts_result->box = box; if ((flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0) fts_result->definite_uids = solr_results[i]->uids; else fts_result->maybe_uids = solr_results[i]->uids; fts_result->scores = solr_results[i]->scores; fts_result->scores_sorted = TRUE; } array_append_zero(&fts_results); result->box_results = array_idx_modifiable(&fts_results, 0); hash_table_destroy(&mailboxes); return 0; } static int fts_backend_solr_lookup_multi(struct fts_backend *_backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result) { bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; string_t *str; fts_solr_set_default_ns(backend); str = t_str_new(256); str_printfa(str, "fl=ns,box,uidv,uid,score&rows=%u&sort=box+asc,uid+asc&q=%%7b!lucene+q.op%%3dAND%%7d", SOLR_MAX_MULTI_ROWS); if (solr_add_definite_query_args(str, args, and_args)) { if (solr_search_multi(backend, str, boxes, flags, result) < 0) return -1; } /* FIXME: maybe_uids could be handled also with some more work.. */ return 0; } struct fts_backend fts_backend_solr_old = { .name = "solr_old", .flags = 0, { fts_backend_solr_alloc, fts_backend_solr_init, fts_backend_solr_deinit, fts_backend_solr_get_last_uid, fts_backend_solr_update_init, fts_backend_solr_update_deinit, fts_backend_solr_update_set_mailbox, fts_backend_solr_update_expunge, fts_backend_solr_update_set_build_key, fts_backend_solr_update_unset_build_key, fts_backend_solr_update_build_more, fts_backend_solr_refresh, NULL, fts_backend_solr_optimize, fts_backend_default_can_lookup, fts_backend_solr_lookup, fts_backend_solr_lookup_multi, NULL } }; dovecot-2.2.33.2/src/plugins/fts-solr/fts-solr-plugin.c0000644000175000017500000000576413123174404017572 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "http-client.h" #include "mail-user.h" #include "mail-storage-hooks.h" #include "solr-connection.h" #include "fts-user.h" #include "fts-solr-plugin.h" const char *fts_solr_plugin_version = DOVECOT_ABI_VERSION; struct http_client *solr_http_client = NULL; struct fts_solr_user_module fts_solr_user_module = MODULE_CONTEXT_INIT(&mail_user_module_register); static int fts_solr_plugin_init_settings(struct mail_user *user, struct fts_solr_settings *set, const char *str) { const char *const *tmp; if (str == NULL) str = ""; for (tmp = t_strsplit_spaces(str, " "); *tmp != NULL; tmp++) { if (strncmp(*tmp, "url=", 4) == 0) { set->url = p_strdup(user->pool, *tmp + 4); } else if (strcmp(*tmp, "debug") == 0) { set->debug = TRUE; } else if (strcmp(*tmp, "use_libfts") == 0) { set->use_libfts = TRUE; } else if (strcmp(*tmp, "break-imap-search") == 0) { /* for backwards compatibility */ } else if (strcmp(*tmp, "default_ns=") == 0) { set->default_ns_prefix = p_strdup(user->pool, *tmp + 11); } else { i_error("fts_solr: Invalid setting: %s", *tmp); return -1; } } if (set->url == NULL) { i_error("fts_solr: url setting missing"); return -1; } return 0; } static void fts_solr_mail_user_deinit(struct mail_user *user) { struct fts_solr_user *fuser = FTS_SOLR_USER_CONTEXT(user); if (fuser->set.use_libfts) fts_mail_user_deinit(user); fuser->module_ctx.super.deinit(user); } static void fts_solr_mail_user_create(struct mail_user *user, const char *env) { struct mail_user_vfuncs *v = user->vlast; struct fts_solr_user *fuser; const char *error; fuser = p_new(user->pool, struct fts_solr_user, 1); if (fts_solr_plugin_init_settings(user, &fuser->set, env) < 0) { /* invalid settings, disabling */ return; } if (fuser->set.use_libfts) { if (fts_mail_user_init(user, &error) < 0) { i_error("fts-solr: %s", error); return; } } fuser->module_ctx.super = *v; user->vlast = &fuser->module_ctx.super; v->deinit = fts_solr_mail_user_deinit; MODULE_CONTEXT_SET(user, fts_solr_user_module, fuser); } static void fts_solr_mail_user_created(struct mail_user *user) { const char *env; env = mail_user_plugin_getenv(user, "fts_solr"); if (env != NULL) fts_solr_mail_user_create(user, env); } static struct mail_storage_hooks fts_solr_mail_storage_hooks = { .mail_user_created = fts_solr_mail_user_created }; void fts_solr_plugin_init(struct module *module) { fts_backend_register(&fts_backend_solr); fts_backend_register(&fts_backend_solr_old); mail_storage_hooks_add(module, &fts_solr_mail_storage_hooks); } void fts_solr_plugin_deinit(void) { fts_backend_unregister(fts_backend_solr.name); fts_backend_unregister(fts_backend_solr_old.name); mail_storage_hooks_remove(&fts_solr_mail_storage_hooks); if (solr_http_client != NULL) http_client_deinit(&solr_http_client); } const char *fts_solr_plugin_dependencies[] = { "fts", NULL }; dovecot-2.2.33.2/src/plugins/fts-solr/fts-backend-solr.c0000644000175000017500000006331713165463624017674 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "strescape.h" #include "unichar.h" #include "http-url.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "mail-search.h" #include "fts-api.h" #include "solr-connection.h" #include "fts-solr-plugin.h" #include #define SOLR_CMDBUF_SIZE (1024*64) #define SOLR_CMDBUF_FLUSH_SIZE (SOLR_CMDBUF_SIZE-128) #define SOLR_MAX_MULTI_ROWS 100000 /* If header is larger than this, truncate it. */ #define SOLR_HEADER_MAX_SIZE (1024*1024) /* If SOLR_HEADER_MAX_SIZE was already reached, write still to individual header fields as long as they're smaller than this */ #define SOLR_HEADER_LINE_MAX_TRUNC_SIZE 1024 #define SOLR_QUERY_MAX_MAILBOX_COUNT 10 /* How often to flush indexing request to Solr before beginning a new one. */ #define SOLR_MAIL_FLUSH_INTERVAL 1000 struct solr_fts_backend { struct fts_backend backend; struct solr_connection *solr_conn; }; struct solr_fts_field { char *key; string_t *value; }; struct solr_fts_backend_update_context { struct fts_backend_update_context ctx; struct mailbox *cur_box; char box_guid[MAILBOX_GUID_HEX_LENGTH+1]; struct solr_connection_post *post; uint32_t prev_uid; string_t *cmd, *cur_value, *cur_value2; string_t *cmd_expunge; ARRAY(struct solr_fts_field) fields; uint32_t last_indexed_uid; unsigned int mails_since_flush; unsigned int tokenized_input:1; unsigned int last_indexed_uid_set:1; unsigned int body_open:1; unsigned int documents_added:1; unsigned int expunges:1; unsigned int truncate_header:1; }; static const char *solr_escape_chars = "+-&|!(){}[]^\"~*?:\\/ "; static bool is_valid_xml_char(unichar_t chr) { /* Valid characters in XML: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] This function gets called only for #x80 and higher */ if (chr > 0xd7ff && chr < 0xe000) return FALSE; if (chr > 0xfffd && chr < 0x10000) return FALSE; return chr < 0x10ffff; } static size_t xml_encode_data_max(string_t *dest, const unsigned char *data, size_t len, unsigned int max_len) { unichar_t chr; size_t i; i_assert(max_len > 0 || len == 0); if (max_len > len) max_len = len; for (i = 0; i < max_len; i++) { switch (data[i]) { case '&': str_append(dest, "&"); break; case '<': str_append(dest, "<"); break; case '>': str_append(dest, ">"); break; case '\t': case '\n': case '\r': /* exceptions to the following control char check */ str_append_c(dest, data[i]); break; default: if (data[i] < 32) { /* SOLR doesn't like control characters. replace them with spaces. */ str_append_c(dest, ' '); } else if (data[i] >= 0x80) { /* make sure the character is valid for XML so we don't get XML parser errors */ unsigned int char_len = uni_utf8_get_char_n(data + i, len - i, &chr); if (char_len > 0 && is_valid_xml_char(chr)) str_append_n(dest, data + i, char_len); else { str_append_n(dest, utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN); } i += char_len - 1; } else { str_append_c(dest, data[i]); } break; } } return i; } static void xml_encode_data(string_t *dest, const unsigned char *data, size_t len) { (void)xml_encode_data_max(dest, data, len, len); } static void xml_encode(string_t *dest, const char *str) { xml_encode_data(dest, (const unsigned char *)str, strlen(str)); } static const char *solr_escape(const char *str) { string_t *ret; unsigned int i; ret = t_str_new(strlen(str) + 16); for (i = 0; str[i] != '\0'; i++) { if (strchr(solr_escape_chars, str[i]) != NULL) str_append_c(ret, '\\'); str_append_c(ret, str[i]); } return str_c(ret); } static void solr_quote_http(string_t *dest, const char *str) { if (str[0] != '\0') http_url_escape_param(dest, solr_escape(str)); else str_append(dest, "\"\""); } static struct fts_backend *fts_backend_solr_alloc(void) { struct solr_fts_backend *backend; backend = i_new(struct solr_fts_backend, 1); backend->backend = fts_backend_solr; return &backend->backend; } static int fts_backend_solr_init(struct fts_backend *_backend, const char **error_r) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; struct fts_solr_user *fuser = FTS_SOLR_USER_CONTEXT(_backend->ns->user); if (fuser == NULL) { *error_r = "Invalid fts_solr setting"; return -1; } if (fuser->set.use_libfts) { /* change our flags so we get proper input */ _backend->flags &= ~FTS_BACKEND_FLAG_FUZZY_SEARCH; _backend->flags |= FTS_BACKEND_FLAG_TOKENIZED_INPUT; } return solr_connection_init(fuser->set.url, fuser->set.debug, &backend->solr_conn, error_r); } static void fts_backend_solr_deinit(struct fts_backend *_backend) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; solr_connection_deinit(&backend->solr_conn); i_free(backend); } static int get_last_uid_fallback(struct fts_backend *_backend, struct mailbox *box, uint32_t *last_uid_r) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; const struct seq_range *uidvals; const char *box_guid; unsigned int count; struct solr_result **results; string_t *str; pool_t pool; int ret = 0; str = t_str_new(256); str_append(str, "fl=uid&rows=1&sort=uid+desc&q="); if (fts_mailbox_get_guid(box, &box_guid) < 0) return -1; str_printfa(str, "box:%s+AND+user:", box_guid); if (_backend->ns->owner != NULL) solr_quote_http(str, _backend->ns->owner->username); else str_append(str, "%22%22"); pool = pool_alloconly_create("solr last uid lookup", 1024); if (solr_connection_select(backend->solr_conn, str_c(str), pool, &results) < 0) ret = -1; else if (results[0] == NULL) { /* no UIDs */ *last_uid_r = 0; } else { uidvals = array_get(&results[0]->uids, &count); i_assert(count > 0); if (count == 1 && uidvals[0].seq1 == uidvals[0].seq2) { *last_uid_r = uidvals[0].seq1; } else { i_error("fts_solr: Last UID lookup returned multiple rows"); ret = -1; } } pool_unref(&pool); return ret; } static int fts_backend_solr_get_last_uid(struct fts_backend *_backend, struct mailbox *box, uint32_t *last_uid_r) { struct fts_index_header hdr; if (fts_index_get_header(box, &hdr)) { *last_uid_r = hdr.last_indexed_uid; return 0; } /* either nothing has been indexed, or the index was corrupted. do it the slow way. */ if (get_last_uid_fallback(_backend, box, last_uid_r) < 0) return -1; fts_index_set_last_uid(box, *last_uid_r); return 0; } static struct fts_backend_update_context * fts_backend_solr_update_init(struct fts_backend *_backend) { struct solr_fts_backend_update_context *ctx; ctx = i_new(struct solr_fts_backend_update_context, 1); ctx->ctx.backend = _backend; ctx->tokenized_input = (_backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0; i_array_init(&ctx->fields, 16); return &ctx->ctx; } static void xml_encode_id(struct solr_fts_backend_update_context *ctx, string_t *str, uint32_t uid) { str_printfa(str, "%u/%s", uid, ctx->box_guid); if (ctx->ctx.backend->ns->owner != NULL) { str_append_c(str, '/'); xml_encode(str, ctx->ctx.backend->ns->owner->username); } } static void fts_backend_solr_doc_open(struct solr_fts_backend_update_context *ctx, uint32_t uid) { ctx->documents_added = TRUE; str_printfa(ctx->cmd, "" "%u" "%s", uid, ctx->box_guid); str_append(ctx->cmd, ""); if (ctx->ctx.backend->ns->owner != NULL) xml_encode(ctx->cmd, ctx->ctx.backend->ns->owner->username); str_append(ctx->cmd, ""); str_printfa(ctx->cmd, ""); xml_encode_id(ctx, ctx->cmd, uid); str_append(ctx->cmd, ""); } static string_t * fts_solr_field_get(struct solr_fts_backend_update_context *ctx, const char *key) { const struct solr_fts_field *field; struct solr_fts_field new_field; /* there are only a few fields. this lookup is fast enough. */ array_foreach(&ctx->fields, field) { if (strcasecmp(field->key, key) == 0) return field->value; } i_zero(&new_field); new_field.key = str_lcase(i_strdup(key)); new_field.value = str_new(default_pool, 128); array_append(&ctx->fields, &new_field, 1); return new_field.value; } static void fts_backend_solr_doc_close(struct solr_fts_backend_update_context *ctx) { struct solr_fts_field *field; if (ctx->body_open) { ctx->body_open = FALSE; str_append(ctx->cmd, ""); } array_foreach_modifiable(&ctx->fields, field) { str_printfa(ctx->cmd, "", field->key); /* the values are already xml-escaped */ str_append_str(ctx->cmd, field->value); str_append(ctx->cmd, ""); str_truncate(field->value, 0); } str_append(ctx->cmd, ""); } static int fts_backed_solr_build_flush(struct solr_fts_backend_update_context *ctx) { if (ctx->post == NULL) return 0; fts_backend_solr_doc_close(ctx); str_append(ctx->cmd, ""); ctx->mails_since_flush = 0; solr_connection_post_more(ctx->post, str_data(ctx->cmd), str_len(ctx->cmd)); str_truncate(ctx->cmd, 0); return solr_connection_post_end(&ctx->post); } static void fts_backend_solr_expunge_flush(struct solr_fts_backend_update_context *ctx) { struct solr_fts_backend *backend = (struct solr_fts_backend *)ctx->ctx.backend; str_append(ctx->cmd_expunge, ""); (void)solr_connection_post(backend->solr_conn, str_c(ctx->cmd_expunge)); str_truncate(ctx->cmd_expunge, 0); str_append(ctx->cmd_expunge, ""); } static int fts_backend_solr_update_deinit(struct fts_backend_update_context *_ctx) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; struct solr_fts_backend *backend = (struct solr_fts_backend *)_ctx->backend; struct solr_fts_field *field; const char *str; int ret = _ctx->failed ? -1 : 0; if (fts_backed_solr_build_flush(ctx) < 0) ret = -1; if (ctx->documents_added || ctx->expunges) { /* commit and wait until the documents we just indexed are visible to the following search */ if (ctx->expunges) fts_backend_solr_expunge_flush(ctx); str = t_strdup_printf("", ctx->documents_added ? "true" : "false"); if (solr_connection_post(backend->solr_conn, str) < 0) ret = -1; } if (ctx->cmd != NULL) str_free(&ctx->cmd); if (ctx->cmd_expunge != NULL) str_free(&ctx->cmd_expunge); array_foreach_modifiable(&ctx->fields, field) { str_free(&field->value); i_free(field->key); } array_free(&ctx->fields); i_free(ctx); return ret; } static void fts_backend_solr_update_set_mailbox(struct fts_backend_update_context *_ctx, struct mailbox *box) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; const char *box_guid; if (ctx->prev_uid != 0) { i_assert(ctx->cur_box != NULL); /* flush solr between mailboxes, so we don't wrongly update last_uid before we know it has succeeded */ if (fts_backed_solr_build_flush(ctx) < 0) _ctx->failed = TRUE; else if (!_ctx->failed) fts_index_set_last_uid(ctx->cur_box, ctx->prev_uid); ctx->prev_uid = 0; } if (box != NULL) { if (fts_mailbox_get_guid(box, &box_guid) < 0) _ctx->failed = TRUE; i_assert(strlen(box_guid) == sizeof(ctx->box_guid)-1); memcpy(ctx->box_guid, box_guid, sizeof(ctx->box_guid)-1); } else { memset(ctx->box_guid, 0, sizeof(ctx->box_guid)); } ctx->cur_box = box; } static void fts_backend_solr_update_expunge(struct fts_backend_update_context *_ctx, uint32_t uid) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; struct fts_index_header hdr; if (!ctx->last_indexed_uid_set) { if (!fts_index_get_header(ctx->cur_box, &hdr)) ctx->last_indexed_uid = 0; else ctx->last_indexed_uid = hdr.last_indexed_uid; ctx->last_indexed_uid_set = TRUE; } if (ctx->last_indexed_uid == 0 || uid > ctx->last_indexed_uid + 100) { /* don't waste time asking Solr to expunge a message that is highly unlikely to be indexed at this time. */ return; } if (!ctx->expunges) { ctx->expunges = TRUE; ctx->cmd_expunge = str_new(default_pool, 1024); str_append(ctx->cmd_expunge, ""); } if (str_len(ctx->cmd_expunge) >= SOLR_CMDBUF_FLUSH_SIZE) fts_backend_solr_expunge_flush(ctx); str_append(ctx->cmd_expunge, ""); xml_encode_id(ctx, ctx->cmd_expunge, uid); str_append(ctx->cmd_expunge, ""); } static void fts_backend_solr_uid_changed(struct solr_fts_backend_update_context *ctx, uint32_t uid) { struct solr_fts_backend *backend = (struct solr_fts_backend *)ctx->ctx.backend; if (ctx->mails_since_flush++ >= SOLR_MAIL_FLUSH_INTERVAL) { if (fts_backed_solr_build_flush(ctx) < 0) ctx->ctx.failed = TRUE; } if (ctx->post == NULL) { if (ctx->cmd == NULL) ctx->cmd = str_new(default_pool, SOLR_CMDBUF_SIZE); ctx->post = solr_connection_post_begin(backend->solr_conn); str_append(ctx->cmd, ""); } else { fts_backend_solr_doc_close(ctx); } ctx->prev_uid = uid; ctx->truncate_header = FALSE; fts_backend_solr_doc_open(ctx, uid); } static bool fts_backend_solr_update_set_build_key(struct fts_backend_update_context *_ctx, const struct fts_backend_build_key *key) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; if (key->uid != ctx->prev_uid) fts_backend_solr_uid_changed(ctx, key->uid); switch (key->type) { case FTS_BACKEND_BUILD_KEY_HDR: if (fts_header_want_indexed(key->hdr_name)) { ctx->cur_value2 = fts_solr_field_get(ctx, key->hdr_name); } /* fall through */ case FTS_BACKEND_BUILD_KEY_MIME_HDR: ctx->cur_value = fts_solr_field_get(ctx, "hdr"); xml_encode(ctx->cur_value, key->hdr_name); str_append(ctx->cur_value, ": "); break; case FTS_BACKEND_BUILD_KEY_BODY_PART: if (!ctx->body_open) { ctx->body_open = TRUE; str_append(ctx->cmd, ""); } ctx->cur_value = ctx->cmd; break; case FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY: i_unreached(); } return TRUE; } static void fts_backend_solr_update_unset_build_key(struct fts_backend_update_context *_ctx) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; /* There can be multiple duplicate keys (duplicate header lines, multiple MIME body parts). Make sure they are separated by whitespace. */ str_append_c(ctx->cur_value, '\n'); ctx->cur_value = NULL; if (ctx->cur_value2 != NULL) { str_append_c(ctx->cur_value2, '\n'); ctx->cur_value2 = NULL; } } static int fts_backend_solr_update_build_more(struct fts_backend_update_context *_ctx, const unsigned char *data, size_t size) { struct solr_fts_backend_update_context *ctx = (struct solr_fts_backend_update_context *)_ctx; size_t len; if (_ctx->failed) return -1; if (ctx->cur_value2 == NULL && ctx->cur_value == ctx->cmd) { /* we're writing to message body. if size is huge, flush it once in a while */ while (size >= SOLR_CMDBUF_FLUSH_SIZE) { if (str_len(ctx->cmd) >= SOLR_CMDBUF_FLUSH_SIZE) { solr_connection_post_more(ctx->post, str_data(ctx->cmd), str_len(ctx->cmd)); str_truncate(ctx->cmd, 0); } len = xml_encode_data_max(ctx->cmd, data, size, SOLR_CMDBUF_FLUSH_SIZE - str_len(ctx->cmd)); i_assert(len > 0); i_assert(len <= size); data += len; size -= len; } xml_encode_data(ctx->cmd, data, size); if (ctx->tokenized_input) str_append_c(ctx->cmd, ' '); } else { if (!ctx->truncate_header) { xml_encode_data(ctx->cur_value, data, size); if (ctx->tokenized_input) str_append_c(ctx->cur_value, ' '); } if (ctx->cur_value2 != NULL && (!ctx->truncate_header || str_len(ctx->cur_value2) < SOLR_HEADER_LINE_MAX_TRUNC_SIZE)) { xml_encode_data(ctx->cur_value2, data, size); if (ctx->tokenized_input) str_append_c(ctx->cur_value2, ' '); } } if (str_len(ctx->cmd) >= SOLR_CMDBUF_FLUSH_SIZE) { solr_connection_post_more(ctx->post, str_data(ctx->cmd), str_len(ctx->cmd)); str_truncate(ctx->cmd, 0); } if (!ctx->truncate_header && str_len(ctx->cur_value) >= SOLR_HEADER_MAX_SIZE) { /* a large header */ i_assert(ctx->cur_value != ctx->cmd); i_warning("fts-solr(%s): Mailbox %s UID=%u header size is huge, truncating", ctx->cur_box->storage->user->username, mailbox_get_vname(ctx->cur_box), ctx->prev_uid); ctx->truncate_header = TRUE; } return 0; } static int fts_backend_solr_refresh(struct fts_backend *backend ATTR_UNUSED) { return 0; } static int fts_backend_solr_rescan(struct fts_backend *backend) { /* FIXME: proper rescan needed. for now we'll just reset the last-uids */ return fts_backend_reset_last_uids(backend); } static int fts_backend_solr_optimize(struct fts_backend *backend ATTR_UNUSED) { return 0; } static bool solr_need_escaping(const char *str) { for (; *str != '\0'; str++) { if (strchr(solr_escape_chars, *str) != NULL) return TRUE; } return FALSE; } static void solr_add_str_arg(string_t *str, struct mail_search_arg *arg) { /* currently we'll just disable fuzzy searching if there are any parameters that need escaping. solr doesn't seem to give good fuzzy results even if we did escape them.. */ if (!arg->fuzzy || arg->value.str[0] == '\0' || solr_need_escaping(arg->value.str)) solr_quote_http(str, arg->value.str); else { http_url_escape_param(str, arg->value.str); str_append_c(str, '~'); } } static bool solr_add_definite_query(string_t *str, struct mail_search_arg *arg) { if (arg->no_fts) return FALSE; switch (arg->type) { case SEARCH_TEXT: { if (arg->match_not) str_append_c(str, '-'); str_append(str, "(hdr:"); solr_add_str_arg(str, arg); str_append(str, "+OR+body:"); solr_add_str_arg(str, arg); str_append(str, ")"); break; } case SEARCH_BODY: if (arg->match_not) str_append_c(str, '-'); str_append(str, "body:"); solr_add_str_arg(str, arg); break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (!fts_header_want_indexed(arg->hdr_field_name)) return FALSE; if (arg->match_not) str_append_c(str, '-'); str_append(str, t_str_lcase(arg->hdr_field_name)); str_append_c(str, ':'); solr_add_str_arg(str, arg); break; default: return FALSE; } return TRUE; } static bool solr_add_definite_query_args(string_t *str, struct mail_search_arg *arg, bool and_args) { size_t last_len; last_len = str_len(str); for (; arg != NULL; arg = arg->next) { if (solr_add_definite_query(str, arg)) { arg->match_always = TRUE; last_len = str_len(str); if (and_args) str_append(str, "+AND+"); else str_append(str, "+OR+"); } } if (str_len(str) == last_len) return FALSE; str_truncate(str, last_len); return TRUE; } static bool solr_add_maybe_query(string_t *str, struct mail_search_arg *arg) { if (arg->no_fts) return FALSE; switch (arg->type) { case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (fts_header_want_indexed(arg->hdr_field_name)) return FALSE; if (arg->match_not) { /* all matches would be definite, but all non-matches would be maybies. too much trouble to optimize. */ return FALSE; } /* we can check if the search key exists in some header and filter out the messages that have no chance of matching */ str_append(str, "hdr:"); if (*arg->value.str != '\0') solr_quote_http(str, arg->value.str); else { /* checking potential existence of the header name */ solr_quote_http(str, t_str_lcase(arg->hdr_field_name)); } break; default: return FALSE; } return TRUE; } static bool solr_add_maybe_query_args(string_t *str, struct mail_search_arg *arg, bool and_args) { size_t last_len; last_len = str_len(str); for (; arg != NULL; arg = arg->next) { if (solr_add_maybe_query(str, arg)) { arg->match_always = TRUE; last_len = str_len(str); if (and_args) str_append(str, "+AND+"); else str_append(str, "+OR+"); } } if (str_len(str) == last_len) return FALSE; str_truncate(str, last_len); return TRUE; } static int solr_search(struct fts_backend *_backend, string_t *str, const char *box_guid, ARRAY_TYPE(seq_range) *uids_r, ARRAY_TYPE(fts_score_map) *scores_r) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; pool_t pool = pool_alloconly_create("fts solr search", 1024); struct solr_result **results; int ret; /* use a separate filter query for selecting the mailbox. it shouldn't affect the score and there could be some caching benefits too. */ str_printfa(str, "&fq=%%2Bbox:%s+%%2Buser:", box_guid); if (_backend->ns->owner != NULL) solr_quote_http(str, _backend->ns->owner->username); else str_append(str, "%22%22"); ret = solr_connection_select(backend->solr_conn, str_c(str), pool, &results); if (ret == 0 && results[0] != NULL) { array_append_array(uids_r, &results[0]->uids); array_append_array(scores_r, &results[0]->scores); } pool_unref(&pool); return ret; } static int fts_backend_solr_lookup(struct fts_backend *_backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; struct mailbox_status status; string_t *str; const char *box_guid; size_t prefix_len; if (fts_mailbox_get_guid(box, &box_guid) < 0) return -1; mailbox_get_open_status(box, STATUS_UIDNEXT, &status); str = t_str_new(256); str_printfa(str, "fl=uid,score&rows=%u&sort=uid+asc&q=%%7b!lucene+q.op%%3dAND%%7d", status.uidnext); prefix_len = str_len(str); if (solr_add_definite_query_args(str, args, and_args)) { ARRAY_TYPE(seq_range) *uids_arr = (flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0 ? &result->definite_uids : &result->maybe_uids; if (solr_search(_backend, str, box_guid, uids_arr, &result->scores) < 0) return -1; } str_truncate(str, prefix_len); if (solr_add_maybe_query_args(str, args, and_args)) { if (solr_search(_backend, str, box_guid, &result->maybe_uids, &result->scores) < 0) return -1; } result->scores_sorted = TRUE; return 0; } static int solr_search_multi(struct fts_backend *_backend, string_t *str, struct mailbox *const boxes[], enum fts_lookup_flags flags, struct fts_multi_result *result) { struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; struct solr_result **solr_results; struct fts_result *fts_result; ARRAY(struct fts_result) fts_results; HASH_TABLE(char *, struct mailbox *) mailboxes; struct mailbox *box; const char *box_guid; unsigned int i; size_t len; bool search_all_mailboxes; /* use a separate filter query for selecting the mailbox. it shouldn't affect the score and there could be some caching benefits too. */ str_append(str, "&fq=%2Buser:"); if (_backend->ns->owner != NULL) solr_quote_http(str, _backend->ns->owner->username); else str_append(str, "%22%22"); hash_table_create(&mailboxes, default_pool, 0, str_hash, strcmp); for (i = 0; boxes[i] != NULL; i++) ; search_all_mailboxes = i > SOLR_QUERY_MAX_MAILBOX_COUNT; if (!search_all_mailboxes) str_append(str, "+%2B("); len = str_len(str); for (i = 0; boxes[i] != NULL; i++) { if (fts_mailbox_get_guid(boxes[i], &box_guid) < 0) continue; if (!search_all_mailboxes) { if (str_len(str) != len) str_append(str, "+OR+"); str_printfa(str, "box:%s", box_guid); } hash_table_insert(mailboxes, t_strdup_noconst(box_guid), boxes[i]); } if (!search_all_mailboxes) str_append_c(str, ')'); if (solr_connection_select(backend->solr_conn, str_c(str), result->pool, &solr_results) < 0) { hash_table_destroy(&mailboxes); return -1; } p_array_init(&fts_results, result->pool, 32); for (i = 0; solr_results[i] != NULL; i++) { box = hash_table_lookup(mailboxes, solr_results[i]->box_id); if (box == NULL) { if (!search_all_mailboxes) { i_warning("fts_solr: Lookup returned unexpected mailbox " "with guid=%s", solr_results[i]->box_id); } continue; } fts_result = array_append_space(&fts_results); fts_result->box = box; if ((flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0) fts_result->definite_uids = solr_results[i]->uids; else fts_result->maybe_uids = solr_results[i]->uids; fts_result->scores = solr_results[i]->scores; fts_result->scores_sorted = TRUE; } array_append_zero(&fts_results); result->box_results = array_idx_modifiable(&fts_results, 0); hash_table_destroy(&mailboxes); return 0; } static int fts_backend_solr_lookup_multi(struct fts_backend *backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result) { bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; string_t *str; str = t_str_new(256); str_printfa(str, "fl=box,uid,score&rows=%u&sort=box+asc,uid+asc&q=%%7b!lucene+q.op%%3dAND%%7d", SOLR_MAX_MULTI_ROWS); if (solr_add_definite_query_args(str, args, and_args)) { if (solr_search_multi(backend, str, boxes, flags, result) < 0) return -1; } /* FIXME: maybe_uids could be handled also with some more work.. */ return 0; } struct fts_backend fts_backend_solr = { .name = "solr", .flags = FTS_BACKEND_FLAG_FUZZY_SEARCH, { fts_backend_solr_alloc, fts_backend_solr_init, fts_backend_solr_deinit, fts_backend_solr_get_last_uid, fts_backend_solr_update_init, fts_backend_solr_update_deinit, fts_backend_solr_update_set_mailbox, fts_backend_solr_update_expunge, fts_backend_solr_update_set_build_key, fts_backend_solr_update_unset_build_key, fts_backend_solr_update_build_more, fts_backend_solr_refresh, fts_backend_solr_rescan, fts_backend_solr_optimize, fts_backend_default_can_lookup, fts_backend_solr_lookup, fts_backend_solr_lookup_multi, NULL } }; dovecot-2.2.33.2/src/plugins/fts-solr/solr-connection.c0000644000175000017500000003566113165463624017653 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "ioloop.h" #include "istream.h" #include "http-url.h" #include "http-client.h" #include "fts-solr-plugin.h" #include "solr-connection.h" #include enum solr_xml_response_state { SOLR_XML_RESPONSE_STATE_ROOT, SOLR_XML_RESPONSE_STATE_RESPONSE, SOLR_XML_RESPONSE_STATE_RESULT, SOLR_XML_RESPONSE_STATE_DOC, SOLR_XML_RESPONSE_STATE_CONTENT }; enum solr_xml_content_state { SOLR_XML_CONTENT_STATE_NONE = 0, SOLR_XML_CONTENT_STATE_UID, SOLR_XML_CONTENT_STATE_SCORE, SOLR_XML_CONTENT_STATE_MAILBOX, SOLR_XML_CONTENT_STATE_NAMESPACE, SOLR_XML_CONTENT_STATE_UIDVALIDITY, SOLR_XML_CONTENT_STATE_ERROR }; struct solr_lookup_xml_context { enum solr_xml_response_state state; enum solr_xml_content_state content_state; int depth; uint32_t uid, uidvalidity; float score; char *mailbox, *ns; pool_t result_pool; /* box_id -> solr_result */ HASH_TABLE(char *, struct solr_result *) mailboxes; ARRAY(struct solr_result *) results; }; struct solr_connection_post { struct solr_connection *conn; struct http_client_request *http_req; unsigned int failed:1; }; struct solr_connection { XML_Parser xml_parser; char *http_host; in_port_t http_port; char *http_base_url; char *http_failure; char *http_user; char *http_password; int request_status; struct istream *payload; struct io *io; unsigned int debug:1; unsigned int posting:1; unsigned int xml_failed:1; unsigned int http_ssl:1; }; static int solr_xml_parse(struct solr_connection *conn, const void *data, size_t size, bool done) { enum XML_Error err; int line, col; if (conn->xml_failed) return -1; if (XML_Parse(conn->xml_parser, data, size, done)) return 0; err = XML_GetErrorCode(conn->xml_parser); if (err != XML_ERROR_FINISHED) { line = XML_GetCurrentLineNumber(conn->xml_parser); col = XML_GetCurrentColumnNumber(conn->xml_parser); i_error("fts_solr: Invalid XML input at %d:%d: %s " "(near: %.*s)", line, col, XML_ErrorString(err), (int)I_MIN(size, 128), (const char *)data); conn->xml_failed = TRUE; return -1; } return 0; } int solr_connection_init(const char *url, bool debug, struct solr_connection **conn_r, const char **error_r) { struct http_client_settings http_set; struct solr_connection *conn; struct http_url *http_url; const char *error; if (http_url_parse(url, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool_datastack_create(), &http_url, &error) < 0) { *error_r = t_strdup_printf( "fts_solr: Failed to parse HTTP url: %s", error); return -1; } conn = i_new(struct solr_connection, 1); conn->http_host = i_strdup(http_url->host_name); conn->http_port = http_url->port; conn->http_base_url = i_strconcat(http_url->path, http_url->enc_query, NULL); conn->http_ssl = http_url->have_ssl; if (http_url->user != NULL) { conn->http_user = i_strdup(http_url->user); /* allow empty password */ conn->http_password = i_strdup(http_url->password != NULL ? http_url->password : ""); } conn->debug = debug; if (solr_http_client == NULL) { i_zero(&http_set); http_set.max_idle_time_msecs = 5*1000; http_set.max_parallel_connections = 1; http_set.max_pipelined_requests = 1; http_set.max_redirects = 1; http_set.max_attempts = 3; http_set.debug = debug; http_set.connect_timeout_msecs = 5*1000; http_set.request_timeout_msecs = 60*1000; solr_http_client = http_client_init(&http_set); } conn->xml_parser = XML_ParserCreate("UTF-8"); if (conn->xml_parser == NULL) { i_fatal_status(FATAL_OUTOFMEM, "fts_solr: Failed to allocate XML parser"); } *conn_r = conn; return 0; } void solr_connection_deinit(struct solr_connection **_conn) { struct solr_connection *conn = *_conn; *_conn = NULL; XML_ParserFree(conn->xml_parser); i_free(conn->http_host); i_free(conn->http_base_url); i_free(conn->http_user); i_free(conn->http_password); i_free(conn); } static const char *attrs_get_name(const char **attrs) { for (; *attrs != NULL; attrs += 2) { if (strcmp(attrs[0], "name") == 0) return attrs[1]; } return ""; } static void solr_lookup_xml_start(void *context, const char *name, const char **attrs) { struct solr_lookup_xml_context *ctx = context; const char *name_attr; i_assert(ctx->depth >= (int)ctx->state); ctx->depth++; if (ctx->depth - 1 > (int)ctx->state) { /* skipping over unwanted elements */ return; } /* response -> result -> doc */ switch (ctx->state) { case SOLR_XML_RESPONSE_STATE_ROOT: if (strcmp(name, "response") == 0) ctx->state++; break; case SOLR_XML_RESPONSE_STATE_RESPONSE: if (strcmp(name, "result") == 0) ctx->state++; break; case SOLR_XML_RESPONSE_STATE_RESULT: if (strcmp(name, "doc") == 0) { ctx->state++; ctx->uid = 0; ctx->score = 0; i_free_and_null(ctx->mailbox); i_free_and_null(ctx->ns); ctx->uidvalidity = 0; } break; case SOLR_XML_RESPONSE_STATE_DOC: name_attr = attrs_get_name(attrs); if (strcmp(name_attr, "uid") == 0) ctx->content_state = SOLR_XML_CONTENT_STATE_UID; else if (strcmp(name_attr, "score") == 0) ctx->content_state = SOLR_XML_CONTENT_STATE_SCORE; else if (strcmp(name_attr, "box") == 0) ctx->content_state = SOLR_XML_CONTENT_STATE_MAILBOX; else if (strcmp(name_attr, "ns") == 0) ctx->content_state = SOLR_XML_CONTENT_STATE_NAMESPACE; else if (strcmp(name_attr, "uidv") == 0) ctx->content_state = SOLR_XML_CONTENT_STATE_UIDVALIDITY; else break; ctx->state++; break; case SOLR_XML_RESPONSE_STATE_CONTENT: break; } } static struct solr_result * solr_result_get(struct solr_lookup_xml_context *ctx, const char *box_id) { struct solr_result *result; char *box_id_dup; result = hash_table_lookup(ctx->mailboxes, box_id); if (result != NULL) return result; box_id_dup = p_strdup(ctx->result_pool, box_id); result = p_new(ctx->result_pool, struct solr_result, 1); result->box_id = box_id_dup; p_array_init(&result->uids, ctx->result_pool, 32); p_array_init(&result->scores, ctx->result_pool, 32); hash_table_insert(ctx->mailboxes, box_id_dup, result); array_append(&ctx->results, &result, 1); return result; } static int solr_lookup_add_doc(struct solr_lookup_xml_context *ctx) { struct fts_score_map *score; struct solr_result *result; const char *box_id; if (ctx->uid == 0) { i_error("fts_solr: uid missing from inside doc"); return -1; } if (ctx->mailbox == NULL) { /* looking up from a single mailbox only */ box_id = ""; } else if (ctx->uidvalidity != 0) { /* old style lookup */ string_t *str = t_str_new(64); str_printfa(str, "%u\001", ctx->uidvalidity); str_append(str, ctx->mailbox); if (ctx->ns != NULL) str_printfa(str, "\001%s", ctx->ns); box_id = str_c(str); } else { /* new style lookup */ box_id = ctx->mailbox; } result = solr_result_get(ctx, box_id); if (seq_range_array_add(&result->uids, ctx->uid)) { /* duplicate result */ } else if (ctx->score != 0) { score = array_append_space(&result->scores); score->uid = ctx->uid; score->score = ctx->score; } return 0; } static void solr_lookup_xml_end(void *context, const char *name ATTR_UNUSED) { struct solr_lookup_xml_context *ctx = context; int ret; if (ctx->content_state == SOLR_XML_CONTENT_STATE_ERROR) return; i_assert(ctx->depth >= (int)ctx->state); if (ctx->state == SOLR_XML_RESPONSE_STATE_CONTENT && ctx->content_state == SOLR_XML_CONTENT_STATE_MAILBOX && ctx->mailbox == NULL) { /* mailbox is namespace prefix */ ctx->mailbox = i_strdup(""); } if (ctx->depth == (int)ctx->state) { ret = 0; if (ctx->state == SOLR_XML_RESPONSE_STATE_DOC) { T_BEGIN { ret = solr_lookup_add_doc(ctx); } T_END; } ctx->state--; if (ret < 0) ctx->content_state = SOLR_XML_CONTENT_STATE_ERROR; else ctx->content_state = SOLR_XML_CONTENT_STATE_NONE; } ctx->depth--; } static int uint32_parse(const char *str, int len, uint32_t *value_r) { uint32_t value = 0; int i; for (i = 0; i < len; i++) { if (str[i] < '0' || str[i] > '9') break; value = value*10 + str[i]-'0'; } if (i != len) return -1; *value_r = value; return 0; } static void solr_lookup_xml_data(void *context, const char *str, int len) { struct solr_lookup_xml_context *ctx = context; char *new_name; switch (ctx->content_state) { case SOLR_XML_CONTENT_STATE_NONE: break; case SOLR_XML_CONTENT_STATE_UID: if (uint32_parse(str, len, &ctx->uid) < 0 || ctx->uid == 0) { i_error("fts_solr: received invalid uid '%s'", t_strndup(str, len)); ctx->content_state = SOLR_XML_CONTENT_STATE_ERROR; } break; case SOLR_XML_CONTENT_STATE_SCORE: T_BEGIN { ctx->score = strtod(t_strndup(str, len), NULL); } T_END; break; case SOLR_XML_CONTENT_STATE_MAILBOX: /* this may be called multiple times, for example if input contains '&' characters */ new_name = ctx->mailbox == NULL ? i_strndup(str, len) : i_strconcat(ctx->mailbox, t_strndup(str, len), NULL); i_free(ctx->mailbox); ctx->mailbox = new_name; break; case SOLR_XML_CONTENT_STATE_NAMESPACE: new_name = ctx->ns == NULL ? i_strndup(str, len) : i_strconcat(ctx->ns, t_strndup(str, len), NULL); i_free(ctx->ns); ctx->ns = new_name; break; case SOLR_XML_CONTENT_STATE_UIDVALIDITY: if (uint32_parse(str, len, &ctx->uidvalidity) < 0) i_error("fts_solr: received invalid uidvalidity"); break; case SOLR_XML_CONTENT_STATE_ERROR: break; } } static void solr_connection_payload_input(struct solr_connection *conn) { const unsigned char *data; size_t size; int ret; /* read payload */ while ((ret = i_stream_read_data(conn->payload, &data, &size, 0)) > 0) { (void)solr_xml_parse(conn, data, size, FALSE); i_stream_skip(conn->payload, size); } if (ret == 0) { /* we will be called again for more data */ } else { if (conn->payload->stream_errno != 0) { i_error("fts_solr: failed to read payload from HTTP server: %m"); conn->request_status = -1; } io_remove(&conn->io); i_stream_unref(&conn->payload); } } static void solr_connection_select_response(const struct http_response *response, struct solr_connection *conn) { if (response->status / 100 != 2) { i_error("fts_solr: Lookup failed: %s", http_response_get_message(response)); conn->request_status = -1; return; } if (response->payload == NULL) { i_error("fts_solr: Lookup failed: Empty response payload"); conn->request_status = -1; return; } i_stream_ref(response->payload); conn->payload = response->payload; conn->io = io_add_istream(response->payload, solr_connection_payload_input, conn); solr_connection_payload_input(conn); } int solr_connection_select(struct solr_connection *conn, const char *query, pool_t pool, struct solr_result ***box_results_r) { struct solr_lookup_xml_context solr_lookup_context; struct http_client_request *http_req; const char *url; int parse_ret; i_zero(&solr_lookup_context); solr_lookup_context.result_pool = pool; hash_table_create(&solr_lookup_context.mailboxes, default_pool, 0, str_hash, strcmp); p_array_init(&solr_lookup_context.results, pool, 32); i_free_and_null(conn->http_failure); conn->xml_failed = FALSE; XML_ParserReset(conn->xml_parser, "UTF-8"); XML_SetElementHandler(conn->xml_parser, solr_lookup_xml_start, solr_lookup_xml_end); XML_SetCharacterDataHandler(conn->xml_parser, solr_lookup_xml_data); XML_SetUserData(conn->xml_parser, &solr_lookup_context); url = t_strconcat(conn->http_base_url, "select?", query, NULL); http_req = http_client_request(solr_http_client, "GET", conn->http_host, url, solr_connection_select_response, conn); if (conn->http_user != NULL) { http_client_request_set_auth_simple(http_req, conn->http_user, conn->http_password); } http_client_request_set_port(http_req, conn->http_port); http_client_request_set_ssl(http_req, conn->http_ssl); http_client_request_submit(http_req); conn->request_status = 0; http_client_wait(solr_http_client); if (conn->request_status < 0 || solr_lookup_context.content_state == SOLR_XML_CONTENT_STATE_ERROR) return -1; parse_ret = solr_xml_parse(conn, "", 0, TRUE); hash_table_destroy(&solr_lookup_context.mailboxes); array_append_zero(&solr_lookup_context.results); *box_results_r = array_idx_modifiable(&solr_lookup_context.results, 0); return parse_ret; } static void solr_connection_update_response(const struct http_response *response, struct solr_connection *conn) { if (response->status / 100 != 2) { i_error("fts_solr: Indexing failed: %s", http_response_get_message(response)); conn->request_status = -1; } } static struct http_client_request * solr_connection_post_request(struct solr_connection *conn) { struct http_client_request *http_req; const char *url; url = t_strconcat(conn->http_base_url, "update", NULL); http_req = http_client_request(solr_http_client, "POST", conn->http_host, url, solr_connection_update_response, conn); if (conn->http_user != NULL) { http_client_request_set_auth_simple(http_req, conn->http_user, conn->http_password); } http_client_request_set_port(http_req, conn->http_port); http_client_request_set_ssl(http_req, conn->http_ssl); http_client_request_add_header(http_req, "Content-Type", "text/xml"); return http_req; } struct solr_connection_post * solr_connection_post_begin(struct solr_connection *conn) { struct solr_connection_post *post; i_assert(!conn->posting); conn->posting = TRUE; post = i_new(struct solr_connection_post, 1); post->conn = conn; post->http_req = solr_connection_post_request(conn); XML_ParserReset(conn->xml_parser, "UTF-8"); return post; } void solr_connection_post_more(struct solr_connection_post *post, const unsigned char *data, size_t size) { struct solr_connection *conn = post->conn; i_assert(post->conn->posting); if (post->failed) return; if (conn->request_status == 0) (void)http_client_request_send_payload(&post->http_req, data, size); if (conn->request_status < 0) post->failed = TRUE; } int solr_connection_post_end(struct solr_connection_post **_post) { struct solr_connection_post *post = *_post; struct solr_connection *conn = post->conn; int ret = post->failed ? -1 : 0; i_assert(conn->posting); *_post = NULL; if (!post->failed) { if (http_client_request_finish_payload(&post->http_req) < 0 || conn->request_status < 0) { ret = -1; } } else { if (post->http_req != NULL) http_client_request_abort(&post->http_req); } i_free(post); conn->posting = FALSE; return ret; } int solr_connection_post(struct solr_connection *conn, const char *cmd) { struct http_client_request *http_req; struct istream *post_payload; i_assert(!conn->posting); http_req = solr_connection_post_request(conn); post_payload = i_stream_create_from_data(cmd, strlen(cmd)); http_client_request_set_payload(http_req, post_payload, TRUE); i_stream_unref(&post_payload); http_client_request_submit(http_req); XML_ParserReset(conn->xml_parser, "UTF-8"); conn->request_status = 0; http_client_wait(solr_http_client); return conn->request_status; } dovecot-2.2.33.2/src/plugins/fts-solr/solr-connection.h0000644000175000017500000000160513165463624017647 00000000000000#ifndef SOLR_CONNECTION_H #define SOLR_CONNECTION_H #include "seq-range-array.h" #include "fts-api.h" struct solr_connection; struct solr_result { const char *box_id; ARRAY_TYPE(seq_range) uids; ARRAY_TYPE(fts_score_map) scores; }; int solr_connection_init(const char *url, bool debug, struct solr_connection **conn_r, const char **error_r); void solr_connection_deinit(struct solr_connection **conn); int solr_connection_select(struct solr_connection *conn, const char *query, pool_t pool, struct solr_result ***box_results_r); int solr_connection_post(struct solr_connection *conn, const char *cmd); struct solr_connection_post * solr_connection_post_begin(struct solr_connection *conn); void solr_connection_post_more(struct solr_connection_post *post, const unsigned char *data, size_t size); int solr_connection_post_end(struct solr_connection_post **post); #endif dovecot-2.2.33.2/src/plugins/fts-solr/fts-solr-plugin.h0000644000175000017500000000141313123174404017562 00000000000000#ifndef FTS_SOLR_PLUGIN_H #define FTS_SOLR_PLUGIN_H #include "module-context.h" #include "mail-user.h" #include "fts-api-private.h" #define FTS_SOLR_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_solr_user_module) struct fts_solr_settings { const char *url, *default_ns_prefix; bool use_libfts; bool debug; }; struct fts_solr_user { union mail_user_module_context module_ctx; struct fts_solr_settings set; }; extern const char *fts_solr_plugin_dependencies[]; extern struct fts_backend fts_backend_solr; extern struct fts_backend fts_backend_solr_old; extern MODULE_CONTEXT_DEFINE(fts_solr_user_module, &mail_user_module_register); extern struct http_client *solr_http_client; void fts_solr_plugin_init(struct module *module); void fts_solr_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/fts-solr/Makefile.am0000644000175000017500000000126713165463624016422 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts NOPLUGIN_LDFLAGS = lib21_fts_solr_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_solr_plugin.la if DOVECOT_PLUGIN_DEPS fts_plugin_dep = ../fts/lib20_fts_plugin.la endif lib21_fts_solr_plugin_la_LIBADD = \ $(fts_plugin_dep) \ -lexpat lib21_fts_solr_plugin_la_SOURCES = \ fts-backend-solr.c \ fts-backend-solr-old.c \ fts-solr-plugin.c \ solr-connection.c noinst_HEADERS = \ fts-solr-plugin.h \ solr-connection.h dovecot-2.2.33.2/src/plugins/lazy-expunge/0002755000175000017500000000000013172375613015317 500000000000000dovecot-2.2.33.2/src/plugins/lazy-expunge/Makefile.in0000644000175000017500000005552413172375574017323 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/lazy-expunge ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib02_lazy_expunge_plugin_la_LIBADD = am_lib02_lazy_expunge_plugin_la_OBJECTS = lazy-expunge-plugin.lo lib02_lazy_expunge_plugin_la_OBJECTS = \ $(am_lib02_lazy_expunge_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib02_lazy_expunge_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib02_lazy_expunge_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib02_lazy_expunge_plugin_la_SOURCES) DIST_SOURCES = $(lib02_lazy_expunge_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/maildir \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/plugins/quota lib02_lazy_expunge_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib02_lazy_expunge_plugin.la lib02_lazy_expunge_plugin_la_SOURCES = \ lazy-expunge-plugin.c noinst_HEADERS = \ lazy-expunge-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/lazy-expunge/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/lazy-expunge/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib02_lazy_expunge_plugin.la: $(lib02_lazy_expunge_plugin_la_OBJECTS) $(lib02_lazy_expunge_plugin_la_DEPENDENCIES) $(EXTRA_lib02_lazy_expunge_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib02_lazy_expunge_plugin_la_LINK) -rpath $(moduledir) $(lib02_lazy_expunge_plugin_la_OBJECTS) $(lib02_lazy_expunge_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lazy-expunge-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/lazy-expunge/lazy-expunge-plugin.h0000644000175000017500000000024213123174404021317 00000000000000#ifndef LAZY_EXPUNGE_PLUGIN_H #define LAZY_EXPUNGE_PLUGIN_H void lazy_expunge_plugin_init(struct module *module); void lazy_expunge_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/lazy-expunge/lazy-expunge-plugin.c0000644000175000017500000004354713165463624021344 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "hash.h" #include "seq-range-array.h" #include "mkdir-parents.h" #include "mail-storage-private.h" #include "mail-search-build.h" #include "mailbox-list-private.h" #include "mail-namespace.h" #include "lazy-expunge-plugin.h" #include #include #include #include #define LAZY_EXPUNGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, lazy_expunge_mail_storage_module) #define LAZY_EXPUNGE_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, lazy_expunge_mailbox_list_module) #define LAZY_EXPUNGE_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, lazy_expunge_mail_user_module) #define LAZY_EXPUNGE_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, lazy_expunge_mail_module) struct lazy_expunge_mail { union mail_module_context module_ctx; bool moving; bool recursing; }; struct lazy_expunge_mail_user { union mail_user_module_context module_ctx; struct mail_namespace *lazy_ns; const char *lazy_mailbox_vname; const char *env; bool copy_only_last_instance; }; struct lazy_expunge_mailbox_list { union mailbox_list_module_context module_ctx; unsigned int allow_rename:1; unsigned int internal_namespace:1; }; struct lazy_expunge_transaction { union mailbox_transaction_module_context module_ctx; struct mailbox *dest_box; struct mailbox_transaction_context *dest_trans; pool_t pool; HASH_TABLE(const char *, void *) guids; char *delayed_errstr; enum mail_error delayed_error; bool copy_only_last_instance; }; const char *lazy_expunge_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_module, &mail_module_register); static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mailbox_list_module, &mailbox_list_module_register); static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_user_module, &mail_user_module_register); static const char * get_dest_vname(struct mailbox_list *list, struct mailbox *src_box) { struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(list->ns->user); const char *name; char src_sep, dest_sep; if (luser->lazy_mailbox_vname != NULL) return luser->lazy_mailbox_vname; /* use the (canonical / unaliased) storage name */ name = src_box->name; /* replace hierarchy separators with destination virtual separator */ src_sep = mailbox_list_get_hierarchy_sep(src_box->list); dest_sep = mail_namespace_get_sep(list->ns); if (src_sep != dest_sep) { string_t *str = t_str_new(128); unsigned int i; for (i = 0; name[i] != '\0'; i++) { if (name[i] == src_sep) str_append_c(str, dest_sep); else str_append_c(str, name[i]); } name = str_c(str); } /* add expunge namespace prefix. the name is now a proper vname */ return t_strconcat(list->ns->prefix, name, NULL); } static struct mailbox * mailbox_open_or_create(struct mailbox_list *list, struct mailbox *src_box, const char **error_r) { struct mailbox *box; enum mail_error error; const char *name; name = get_dest_vname(list, src_box); box = mailbox_alloc(list, name, MAILBOX_FLAG_NO_INDEX_FILES | MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_IGNORE_ACLS); mailbox_set_reason(box, "lazy_expunge"); if (mailbox_open(box) == 0) { *error_r = NULL; return box; } *error_r = mailbox_get_last_internal_error(box, &error); if (error != MAIL_ERROR_NOTFOUND) { *error_r = t_strdup_printf("Failed to open mailbox %s: %s", name, *error_r); mailbox_free(&box); return NULL; } /* try creating and re-opening it. */ if (mailbox_create(box, NULL, FALSE) < 0 && mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) { *error_r = t_strdup_printf("Failed to create mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return NULL; } if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("Failed to open created mailbox %s: %s", name, mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return NULL; } return box; } static unsigned int lazy_expunge_count_in_transaction(struct lazy_expunge_transaction *lt, const char *guid) { void *refcountp; unsigned int refcount; if (lt->pool == NULL) { lt->pool = pool_alloconly_create("lazy expunge transaction", 1024); hash_table_create(<->guids, lt->pool, 0, str_hash, strcmp); } refcountp = hash_table_lookup(lt->guids, guid); refcount = POINTER_CAST_TO(refcountp, unsigned int) + 1; refcountp = POINTER_CAST(refcount); if (refcount == 1) { guid = p_strdup(lt->pool, guid); hash_table_insert(lt->guids, guid, refcountp); } else { hash_table_update(lt->guids, guid, refcountp); } return refcount-1; } static int lazy_expunge_mail_is_last_instace(struct mail *_mail) { struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(_mail->transaction); const char *value, *errstr; unsigned long refcount; enum mail_error error; if (mail_get_special(_mail, MAIL_FETCH_REFCOUNT, &value) < 0) { errstr = mailbox_get_last_internal_error(_mail->box, &error); if (error == MAIL_ERROR_EXPUNGED) { /* already expunged - just ignore it */ return 0; } mail_storage_set_critical(_mail->box->storage, "lazy_expunge: Couldn't lookup message's refcount: %s", errstr); return -1; } if (*value == '\0') { /* refcounts not supported by backend. assume all mails are the last instance. */ return 1; } if (str_to_ulong(value, &refcount) < 0) i_panic("Invalid mail refcount number: %s", value); if (refcount > 1) { /* this probably isn't the last instance of the mail, but it's possible that the same mail was copied to this mailbox multiple times and we're deleting more than one instance within this transaction. in those cases each expunge will see the same refcount, so we need to adjust the refcount by tracking the expunged message GUIDs. */ if (mail_get_special(_mail, MAIL_FETCH_GUID, &value) < 0) { errstr = mailbox_get_last_internal_error(_mail->box, &error); if (error == MAIL_ERROR_EXPUNGED) { /* already expunged - just ignore it */ return 0; } mail_storage_set_critical(_mail->box->storage, "lazy_expunge: Couldn't lookup message's GUID: %s", errstr); return -1; } if (*value == '\0') { /* GUIDs not supported by backend, but refcounts are? not with our current backends. */ mail_storage_set_critical(_mail->box->storage, "lazy_expunge: Message unexpectedly has no GUID"); return -1; } refcount -= lazy_expunge_count_in_transaction(lt, value); } return refcount <= 1 ? 1 : 0; } static bool lazy_expunge_is_internal_mailbox(struct mailbox *box) { struct mail_namespace *ns = box->list->ns; struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(ns->user); struct lazy_expunge_mailbox_list *llist = LAZY_EXPUNGE_LIST_CONTEXT(box->list); if (llist == NULL) { /* lazy_expunge not enabled at all */ return FALSE; } if (llist->internal_namespace) { /* lazy-expunge namespace */ return TRUE; } if (luser->lazy_mailbox_vname != NULL && strcmp(luser->lazy_mailbox_vname, box->vname) == 0) { /* lazy-expunge mailbox */ return TRUE; } return FALSE; } static void lazy_expunge_set_error(struct lazy_expunge_transaction *lt, struct mail_storage *storage) { const char *errstr; enum mail_error error; errstr = mail_storage_get_last_error(storage, &error); if (error == MAIL_ERROR_EXPUNGED) { /* expunging failed because the mail was already expunged. we don't want to fail because of that. */ return; } if (lt->delayed_error != MAIL_ERROR_NONE) return; lt->delayed_error = error; lt->delayed_errstr = i_strdup(errstr); } static void lazy_expunge_mail_expunge(struct mail *_mail) { struct mail_namespace *ns = _mail->box->list->ns; struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(ns->user); struct mail_private *mail = (struct mail_private *)_mail; struct lazy_expunge_mail *mmail = LAZY_EXPUNGE_MAIL_CONTEXT(mail); struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(_mail->transaction); struct mail *real_mail; struct mail_save_context *save_ctx; const char *error; bool moving = mmail->moving; int ret; if (lt->delayed_error != MAIL_ERROR_NONE) return; if (mmail->recursing) { mmail->module_ctx.super.expunge(_mail); return; } /* Clear this in case the mail is used for non-move later on. */ mmail->moving = FALSE; /* don't copy the mail if we're expunging from lazy_expunge namespace (even if it's via a virtual mailbox) */ if (mail_get_backend_mail(_mail, &real_mail) < 0) { lazy_expunge_set_error(lt, _mail->box->storage); return; } if (lazy_expunge_is_internal_mailbox(real_mail->box)) { mmail->module_ctx.super.expunge(_mail); return; } if (lt->copy_only_last_instance) { /* we want to copy only the last instance of the mail to lazy_expunge namespace. other instances will be expunged immediately. */ if (moving) ret = 0; else if ((ret = lazy_expunge_mail_is_last_instace(_mail)) < 0) { lazy_expunge_set_error(lt, _mail->box->storage); return; } if (ret == 0) { mmail->module_ctx.super.expunge(_mail); return; } } if (lt->dest_box == NULL) { lt->dest_box = mailbox_open_or_create(luser->lazy_ns->list, _mail->box, &error); if (lt->dest_box == NULL) { mail_storage_set_critical(_mail->box->storage, "lazy_expunge: Couldn't open expunge mailbox: " "%s", error); lazy_expunge_set_error(lt, _mail->box->storage); return; } if (mailbox_sync(lt->dest_box, 0) < 0) { mail_storage_set_critical(_mail->box->storage, "lazy_expunge: Couldn't sync expunge mailbox"); lazy_expunge_set_error(lt, lt->dest_box->storage); mailbox_free(<->dest_box); return; } lt->dest_trans = mailbox_transaction_begin(lt->dest_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); } save_ctx = mailbox_save_alloc(lt->dest_trans); mailbox_save_copy_flags(save_ctx, _mail); save_ctx->data.flags &= ~MAIL_DELETED; mmail->recursing = TRUE; if (mailbox_move(&save_ctx, _mail) < 0 && !_mail->expunged) lazy_expunge_set_error(lt, lt->dest_box->storage); mmail->recursing = FALSE; } static int lazy_expunge_copy(struct mail_save_context *ctx, struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(ctx->transaction->box); struct lazy_expunge_mail *mmail = LAZY_EXPUNGE_MAIL_CONTEXT(mail); if (mmail != NULL) mmail->moving = ctx->moving; return mbox->super.copy(ctx, _mail); } static struct mailbox_transaction_context * lazy_expunge_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(box->list->ns->user); union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(box); struct mailbox_transaction_context *t; struct lazy_expunge_transaction *lt; t = mbox->super.transaction_begin(box, flags); lt = i_new(struct lazy_expunge_transaction, 1); lt->copy_only_last_instance = luser->copy_only_last_instance; MODULE_CONTEXT_SET(t, lazy_expunge_mail_storage_module, lt); return t; } static void lazy_expunge_transaction_free(struct lazy_expunge_transaction *lt) { if (lt->dest_trans != NULL) mailbox_transaction_rollback(<->dest_trans); if (lt->dest_box != NULL) mailbox_free(<->dest_box); if (hash_table_is_created(lt->guids)) hash_table_destroy(<->guids); if (lt->pool != NULL) pool_unref(<->pool); i_free(lt->delayed_errstr); i_free(lt); } static int lazy_expunge_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(ctx->box); struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(ctx); int ret; if (lt->dest_trans != NULL && lt->delayed_error == MAIL_ERROR_NONE) { if (mailbox_transaction_commit(<->dest_trans) < 0) { lazy_expunge_set_error(lt, ctx->box->storage); } } if (lt->delayed_error == MAIL_ERROR_NONE) ret = mbox->super.transaction_commit(ctx, changes_r); else if (lt->delayed_error != MAIL_ERROR_TEMP) { mail_storage_set_error(ctx->box->storage, lt->delayed_error, lt->delayed_errstr); mbox->super.transaction_rollback(ctx); ret = -1; } else { mail_storage_set_critical(ctx->box->storage, "Lazy-expunge transaction failed: %s", lt->delayed_errstr); mbox->super.transaction_rollback(ctx); ret = -1; } lazy_expunge_transaction_free(lt); return ret; } static void lazy_expunge_transaction_rollback(struct mailbox_transaction_context *ctx) { union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(ctx->box); struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(ctx); mbox->super.transaction_rollback(ctx); lazy_expunge_transaction_free(lt); } static void lazy_expunge_mail_allocated(struct mail *_mail) { struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(_mail->transaction); struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; struct lazy_expunge_mail *mmail; if (lt == NULL) return; mmail = p_new(mail->pool, struct lazy_expunge_mail, 1); mmail->module_ctx.super = *v; mail->vlast = &mmail->module_ctx.super; v->expunge = lazy_expunge_mail_expunge; MODULE_CONTEXT_SET(mail, lazy_expunge_mail_module, mmail); } static int lazy_expunge_mailbox_rename(struct mailbox *src, struct mailbox *dest) { union mailbox_module_context *lbox = LAZY_EXPUNGE_CONTEXT(src); struct lazy_expunge_mailbox_list *src_llist = LAZY_EXPUNGE_LIST_CONTEXT(src->list); struct lazy_expunge_mailbox_list *dest_llist = LAZY_EXPUNGE_LIST_CONTEXT(dest->list); if (!src_llist->allow_rename && (src_llist->internal_namespace || dest_llist->internal_namespace)) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailboxes to/from expunge namespace."); return -1; } return lbox->super.rename_box(src, dest); } static void lazy_expunge_mailbox_allocated(struct mailbox *box) { struct lazy_expunge_mailbox_list *llist = LAZY_EXPUNGE_LIST_CONTEXT(box->list); union mailbox_module_context *mbox; struct mailbox_vfuncs *v = box->vlast; if (llist == NULL || (box->flags & MAILBOX_FLAG_DELETE_UNSAFE) != 0) return; mbox = p_new(box->pool, union mailbox_module_context, 1); mbox->super = *v; box->vlast = &mbox->super; MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module, mbox); if (!lazy_expunge_is_internal_mailbox(box)) { v->copy = lazy_expunge_copy; v->transaction_begin = lazy_expunge_transaction_begin; v->transaction_commit = lazy_expunge_transaction_commit; v->transaction_rollback = lazy_expunge_transaction_rollback; v->rename_box = lazy_expunge_mailbox_rename; } else if (llist->internal_namespace) { v->rename_box = lazy_expunge_mailbox_rename; } else { /* internal mailbox in a non-internal namespace - don't add any unnecessary restrictions to it. if it's not wanted, just use the ACL plugin. */ } } static void lazy_expunge_mailbox_list_created(struct mailbox_list *list) { struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(list->ns->user); struct lazy_expunge_mailbox_list *llist; if (luser == NULL) return; /* if this is one of our internal namespaces, mark it as such before quota plugin sees it */ if (strcmp(list->ns->prefix, luser->env) == 0) list->ns->flags |= NAMESPACE_FLAG_NOQUOTA; if (list->ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) { llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1); MODULE_CONTEXT_SET(list, lazy_expunge_mailbox_list_module, llist); } } static void lazy_expunge_mail_namespaces_created(struct mail_namespace *namespaces) { struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(namespaces->user); struct lazy_expunge_mailbox_list *llist; if (luser == NULL) return; luser->lazy_ns = mail_namespace_find_prefix(namespaces, luser->env); if (luser->lazy_ns != NULL) { /* we don't want to override this namespace's expunge operation. */ llist = LAZY_EXPUNGE_LIST_CONTEXT(luser->lazy_ns->list); llist->internal_namespace = TRUE; } else { /* store the the expunged mails to the specified mailbox. */ luser->lazy_ns = mail_namespace_find(namespaces, luser->env); luser->lazy_mailbox_vname = luser->env; } mail_namespace_ref(luser->lazy_ns); } static void lazy_expunge_user_deinit(struct mail_user *user) { struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(user); /* mail_namespaces_created hook isn't necessarily ever called */ if (luser->lazy_ns != NULL) mail_namespace_unref(&luser->lazy_ns); luser->module_ctx.super.deinit(user); } static void lazy_expunge_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct lazy_expunge_mail_user *luser; const char *env; env = mail_user_plugin_getenv(user, "lazy_expunge"); if (env != NULL && env[0] != '\0') { luser = p_new(user->pool, struct lazy_expunge_mail_user, 1); luser->module_ctx.super = *v; user->vlast = &luser->module_ctx.super; v->deinit = lazy_expunge_user_deinit; luser->env = env; luser->copy_only_last_instance = mail_user_plugin_getenv(user, "lazy_expunge_only_last_instance") != NULL; MODULE_CONTEXT_SET(user, lazy_expunge_mail_user_module, luser); } else if (user->mail_debug) { i_debug("lazy_expunge: No lazy_expunge setting - " "plugin disabled"); } } static struct mail_storage_hooks lazy_expunge_mail_storage_hooks = { .mail_user_created = lazy_expunge_mail_user_created, .mail_namespaces_created = lazy_expunge_mail_namespaces_created, .mailbox_list_created = lazy_expunge_mailbox_list_created, .mailbox_allocated = lazy_expunge_mailbox_allocated, .mail_allocated = lazy_expunge_mail_allocated }; void lazy_expunge_plugin_init(struct module *module) { mail_storage_hooks_add(module, &lazy_expunge_mail_storage_hooks); } void lazy_expunge_plugin_deinit(void) { mail_storage_hooks_remove(&lazy_expunge_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/lazy-expunge/Makefile.am0000644000175000017500000000104013123174404017253 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/maildir \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/plugins/quota NOPLUGIN_LDFLAGS = lib02_lazy_expunge_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib02_lazy_expunge_plugin.la lib02_lazy_expunge_plugin_la_SOURCES = \ lazy-expunge-plugin.c noinst_HEADERS = \ lazy-expunge-plugin.h dovecot-2.2.33.2/src/plugins/last-login/0002755000175000017500000000000013172375613014740 500000000000000dovecot-2.2.33.2/src/plugins/last-login/last-login-plugin.h0000644000175000017500000000023213123174404020360 00000000000000#ifndef LAST_LOGIN_PLUGIN_H #define LAST_LOGIN_PLUGIN_H void last_login_plugin_init(struct module *module); void last_login_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/last-login/Makefile.in0000644000175000017500000005530313172375574016737 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/last-login ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib10_last_login_plugin_la_LIBADD = am_lib10_last_login_plugin_la_OBJECTS = last-login-plugin.lo lib10_last_login_plugin_la_OBJECTS = \ $(am_lib10_last_login_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib10_last_login_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib10_last_login_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib10_last_login_plugin_la_SOURCES) DIST_SOURCES = $(lib10_last_login_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib10_last_login_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_last_login_plugin.la lib10_last_login_plugin_la_SOURCES = \ last-login-plugin.c noinst_HEADERS = \ last-login-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/last-login/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/last-login/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib10_last_login_plugin.la: $(lib10_last_login_plugin_la_OBJECTS) $(lib10_last_login_plugin_la_DEPENDENCIES) $(EXTRA_lib10_last_login_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_last_login_plugin_la_LINK) -rpath $(moduledir) $(lib10_last_login_plugin_la_OBJECTS) $(lib10_last_login_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/last-login-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/last-login/last-login-plugin.c0000644000175000017500000001105413165463624020372 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "dict.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mail-storage-hooks.h" #include "last-login-plugin.h" #define LAST_LOGIN_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, last_login_user_module) #define LAST_LOGIN_DEFAULT_KEY_PREFIX "last-login/" struct last_login_user { union mail_user_module_context module_ctx; struct dict *dict; struct timeout *to; }; const char *last_login_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(last_login_user_module, &mail_user_module_register); static void last_login_dict_deinit(struct mail_user *user) { struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user); if (luser->dict != NULL) { (void)dict_wait(luser->dict); dict_deinit(&luser->dict); } /* remove timeout after dict_wait(), which may trigger last_login_dict_commit() */ if (luser->to != NULL) timeout_remove(&luser->to); } static void last_login_user_deinit(struct mail_user *user) { struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user); last_login_dict_deinit(user); luser->module_ctx.super.deinit(user); } static void last_login_dict_commit(int ret, void *context) { struct mail_user *user = context; struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user); switch(ret) { case DICT_COMMIT_RET_OK: case DICT_COMMIT_RET_NOTFOUND: break; case DICT_COMMIT_RET_FAILED: i_error("last_login_dict: Failed to write value for user %s", user->username); break; case DICT_COMMIT_RET_WRITE_UNCERTAIN: i_error("last_login_dict: Write was unconfirmed (timeout or disconnect) for user %s", user->username); break; }; /* don't deinit the dict immediately here, lib-dict will just crash */ luser->to = timeout_add(0, last_login_dict_deinit, user); } static void last_login_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct last_login_user *luser; struct dict *dict; struct dict_settings set; struct dict_transaction_context *trans; const char *dict_value, *key_name, *precision, *error; if (user->autocreated) { /* we want to handle only logged in users, not lda's raw user or accessed shared users */ return; } if (user->session_restored) { /* This is IMAP unhibernation, not a real login. */ return; } dict_value = mail_user_plugin_getenv(user, "last_login_dict"); if (dict_value == NULL || dict_value[0] == '\0') return; i_zero(&set); set.username = user->username; set.base_dir = user->set->base_dir; if (mail_user_get_home(user, &set.home_dir) <= 0) set.home_dir = NULL; if (dict_init_full(dict_value, &set, &dict, &error) < 0) { i_error("last_login_dict: dict_init(%s) failed: %s", dict_value, error); return; } luser = p_new(user->pool, struct last_login_user, 1); luser->module_ctx.super = *v; user->vlast = &luser->module_ctx.super; v->deinit = last_login_user_deinit; luser->dict = dict; MODULE_CONTEXT_SET(user, last_login_user_module, luser); key_name = mail_user_plugin_getenv(user, "last_login_key"); if (key_name == NULL) { key_name = t_strdup_printf(LAST_LOGIN_DEFAULT_KEY_PREFIX"%s", user->username); } key_name = t_strconcat(DICT_PATH_SHARED, key_name, NULL); precision = mail_user_plugin_getenv(user, "last_login_precision"); trans = dict_transaction_begin(dict); if (precision == NULL || strcmp(precision, "s") == 0) dict_set(trans, key_name, dec2str(ioloop_time)); else if (strcmp(precision, "ms") == 0) { dict_set(trans, key_name, t_strdup_printf( "%ld%03u", (long)ioloop_timeval.tv_sec, (unsigned int)(ioloop_timeval.tv_usec/1000))); } else if (strcmp(precision, "us") == 0) { dict_set(trans, key_name, t_strdup_printf( "%ld%06u", (long)ioloop_timeval.tv_sec, (unsigned int)ioloop_timeval.tv_usec)); } else if (strcmp(precision, "ns") == 0) { dict_set(trans, key_name, t_strdup_printf( "%ld%06u000", (long)ioloop_timeval.tv_sec, (unsigned int)ioloop_timeval.tv_usec)); } else { i_error("last_login_dict: Invalid last_login_precision '%s'", precision); } dict_transaction_no_slowness_warning(trans); dict_transaction_commit_async(&trans, last_login_dict_commit, user); } static struct mail_storage_hooks last_login_mail_storage_hooks = { .mail_user_created = last_login_mail_user_created }; void last_login_plugin_init(struct module *module) { mail_storage_hooks_add(module, &last_login_mail_storage_hooks); } void last_login_plugin_deinit(void) { mail_storage_hooks_remove(&last_login_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/last-login/Makefile.am0000644000175000017500000000066713123174404016712 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib10_last_login_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_last_login_plugin.la lib10_last_login_plugin_la_SOURCES = \ last-login-plugin.c noinst_HEADERS = \ last-login-plugin.h dovecot-2.2.33.2/src/plugins/fts-squat/0002755000175000017500000000000013172375613014616 500000000000000dovecot-2.2.33.2/src/plugins/fts-squat/Makefile.in0000644000175000017500000006052313172375574016615 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = squat-test$(EXEEXT) subdir = src/plugins/fts-squat ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib21_fts_squat_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../fts/lib20_fts_plugin.la am_lib21_fts_squat_plugin_la_OBJECTS = fts-squat-plugin.lo \ fts-backend-squat.lo squat-trie.lo squat-uidlist.lo lib21_fts_squat_plugin_la_OBJECTS = \ $(am_lib21_fts_squat_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib21_fts_squat_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib21_fts_squat_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ PROGRAMS = $(noinst_PROGRAMS) am_squat_test_OBJECTS = squat-test.$(OBJEXT) squat_test_OBJECTS = $(am_squat_test_OBJECTS) am__DEPENDENCIES_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib21_fts_squat_plugin_la_SOURCES) $(squat_test_SOURCES) DIST_SOURCES = $(lib21_fts_squat_plugin_la_SOURCES) \ $(squat_test_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts lib21_fts_squat_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_squat_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib21_fts_squat_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../fts/lib20_fts_plugin.la lib21_fts_squat_plugin_la_SOURCES = \ fts-squat-plugin.c \ fts-backend-squat.c \ squat-trie.c \ squat-uidlist.c noinst_HEADERS = \ fts-squat-plugin.h \ squat-trie.h \ squat-trie-private.h \ squat-uidlist.h squat_test_SOURCES = \ squat-test.c common_objects = \ squat-trie.lo \ squat-uidlist.lo squat_test_LDADD = \ $(common_objects) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) squat_test_DEPENDENCIES = \ $(common_objects) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/fts-squat/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/fts-squat/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib21_fts_squat_plugin.la: $(lib21_fts_squat_plugin_la_OBJECTS) $(lib21_fts_squat_plugin_la_DEPENDENCIES) $(EXTRA_lib21_fts_squat_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib21_fts_squat_plugin_la_LINK) -rpath $(moduledir) $(lib21_fts_squat_plugin_la_OBJECTS) $(lib21_fts_squat_plugin_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list squat-test$(EXEEXT): $(squat_test_OBJECTS) $(squat_test_DEPENDENCIES) $(EXTRA_squat_test_DEPENDENCIES) @rm -f squat-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(squat_test_OBJECTS) $(squat_test_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-backend-squat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-squat-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/squat-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/squat-trie.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/squat-uidlist.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/fts-squat/fts-squat-plugin.h0000644000175000017500000000044613123174404020123 00000000000000#ifndef FTS_SQUAT_PLUGIN_H #define FTS_SQUAT_PLUGIN_H #include "fts-api-private.h" struct module; extern const char *fts_squat_plugin_dependencies[]; extern struct fts_backend fts_backend_squat; void fts_squat_plugin_init(struct module *module); void fts_squat_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/fts-squat/squat-trie.h0000644000175000017500000000347413123174404017002 00000000000000#ifndef SQUAT_TRIE_H #define SQUAT_TRIE_H #include "file-lock.h" #include "seq-range-array.h" enum squat_index_flags { SQUAT_INDEX_FLAG_MMAP_DISABLE = 0x01, SQUAT_INDEX_FLAG_NFS_FLUSH = 0x02, SQUAT_INDEX_FLAG_DOTLOCK_USE_EXCL = 0x04 }; enum squat_index_type { SQUAT_INDEX_TYPE_HEADER = 0x01, SQUAT_INDEX_TYPE_BODY = 0x02 }; struct squat_trie_build_context; struct squat_trie * squat_trie_init(const char *path, uint32_t uidvalidity, enum file_lock_method lock_method, enum squat_index_flags flags, mode_t mode, gid_t gid); void squat_trie_deinit(struct squat_trie **trie); void squat_trie_set_partial_len(struct squat_trie *trie, unsigned int len); void squat_trie_set_full_len(struct squat_trie *trie, unsigned int len); int squat_trie_open(struct squat_trie *trie); int squat_trie_refresh(struct squat_trie *trie); int squat_trie_build_init(struct squat_trie *trie, struct squat_trie_build_context **ctx_r); /* bodies must be added before headers */ int squat_trie_build_more(struct squat_trie_build_context *ctx, uint32_t uid, enum squat_index_type type, const unsigned char *data, unsigned int size); /* if expunged_uids is non-NULL, they may be removed from the index if they still exist. */ int squat_trie_build_deinit(struct squat_trie_build_context **ctx, const ARRAY_TYPE(seq_range) *expunged_uids) ATTR_NULL(2); int squat_trie_get_last_uid(struct squat_trie *trie, uint32_t *last_uid_r); /* type specifies if we're looking at header, body or both */ int squat_trie_lookup(struct squat_trie *trie, const char *str, enum squat_index_type type, ARRAY_TYPE(seq_range) *definite_uids, ARRAY_TYPE(seq_range) *maybe_uids); struct squat_uidlist *squat_trie_get_uidlist(struct squat_trie *trie); size_t squat_trie_mem_used(struct squat_trie *trie, unsigned int *count_r); #endif dovecot-2.2.33.2/src/plugins/fts-squat/squat-test.c0000644000175000017500000001307213165463624017017 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "file-lock.h" #include "istream.h" #include "time-util.h" #include "unichar.h" #include "squat-trie.h" #include "squat-uidlist.h" #include #include #include #include #include static void result_print(ARRAY_TYPE(seq_range) *result) { const struct seq_range *range; unsigned int i, count; range = array_get(result, &count); for (i = 0; i < count; i++) { if (i != 0) printf(","); printf("%u", range[i].seq1); if (range[i].seq1 != range[i].seq2) printf("-%u", range[i].seq2); } printf("\n"); } int main(int argc ATTR_UNUSED, char *argv[]) { const char *trie_path = "/tmp/squat-test-index.search"; const char *uidlist_path = "/tmp/squat-test-index.search.uids"; struct squat_trie *trie; struct squat_trie_build_context *build_ctx; struct istream *input; struct stat trie_st, uidlist_st; ARRAY_TYPE(seq_range) definite_uids, maybe_uids; char *line, *str, buf[4096]; buffer_t *valid; int ret, fd; unsigned int last = 0, seq = 1, node_count, uidlist_count; size_t len; enum squat_index_type index_type; bool data_header = TRUE, first = TRUE, skip_body = FALSE; bool mime_header = TRUE; size_t trie_mem, uidlist_mem; clock_t clock_start, clock_end; struct timeval tv_start, tv_end; double cputime; lib_init(); i_unlink_if_exists(trie_path); i_unlink_if_exists(uidlist_path); trie = squat_trie_init(trie_path, time(NULL), FILE_LOCK_METHOD_FCNTL, FALSE, 0600, (gid_t)-1); clock_start = clock(); gettimeofday(&tv_start, NULL); fd = open(argv[1], O_RDONLY); if (fd == -1) return 1; if (squat_trie_build_init(trie, &build_ctx) < 0) return 1; valid = buffer_create_dynamic(default_pool, 4096); input = i_stream_create_fd(fd, (size_t)-1, FALSE); ret = 0; while (ret == 0 && (line = i_stream_read_next_line(input)) != NULL) { if (last != input->v_offset/(1024*100)) { fprintf(stderr, "\r%ukB", (unsigned)(input->v_offset/1024)); fflush(stderr); last = input->v_offset/(1024*100); } if (strncmp(line, "From ", 5) == 0) { if (!first) seq++; data_header = TRUE; skip_body = FALSE; mime_header = TRUE; continue; } first = FALSE; if (strncmp(line, "--", 2) == 0) { skip_body = FALSE; mime_header = TRUE; } if (mime_header) { if (*line == '\0') { data_header = FALSE; mime_header = FALSE; continue; } if (strncasecmp(line, "Content-Type:", 13) == 0 && strncasecmp(line, "Content-Type: text/", 19) != 0 && strncasecmp(line, "Content-Type: message/", 22) != 0) skip_body = TRUE; else if (strncasecmp(line, "Content-Transfer-Encoding: base64", 33) == 0) skip_body = TRUE; } else if (skip_body) continue; if (*line == '\0') continue; /* we're actually indexing here headers as bodies and bodies as headers. it doesn't really matter in this test, and fixing it would require storing headers temporarily elsewhere and index them only after the body */ index_type = !data_header ? SQUAT_INDEX_TYPE_HEADER : SQUAT_INDEX_TYPE_BODY; buffer_set_used_size(valid, 0); len = strlen(line); if (uni_utf8_get_valid_data((const unsigned char *)line, len, valid)) { ret = squat_trie_build_more(build_ctx, seq, index_type, (const void *)line, len); } else if (valid->used > 0) { ret = squat_trie_build_more(build_ctx, seq, index_type, valid->data, valid->used); } } buffer_free(&valid); if (squat_trie_build_deinit(&build_ctx, NULL) < 0) ret = -1; if (ret < 0) { printf("build broken\n"); return 1; } clock_end = clock(); (void)gettimeofday(&tv_end, NULL); cputime = (double)(clock_end - clock_start) / CLOCKS_PER_SEC; fprintf(stderr, "\n - Index time: %.2f CPU seconds, " "%.2f real seconds (%.02fMB/CPUs)\n", cputime, timeval_diff_msecs(&tv_end, &tv_start)/1000.0, input->v_offset / cputime / (1024*1024)); if (stat(trie_path, &trie_st) < 0) i_error("stat(%s) failed: %m", trie_path); if (stat(uidlist_path, &uidlist_st) < 0) i_error("stat(%s) failed: %m", uidlist_path); trie_mem = squat_trie_mem_used(trie, &node_count); uidlist_mem = squat_uidlist_mem_used(squat_trie_get_uidlist(trie), &uidlist_count); fprintf(stderr, " - memory: %uk for trie, %uk for uidlist\n", (unsigned)(trie_mem/1024), (unsigned)(uidlist_mem/1024)); fprintf(stderr, " - %"PRIuUOFF_T" bytes in %u nodes (%.02f%%)\n", trie_st.st_size, node_count, trie_st.st_size / (float)input->v_offset * 100.0); fprintf(stderr, " - %"PRIuUOFF_T" bytes in %u UID lists (%.02f%%)\n", uidlist_st.st_size, uidlist_count, uidlist_st.st_size / (float)input->v_offset * 100.0); fprintf(stderr, " - %"PRIuUOFF_T" bytes total of %" PRIuUOFF_T" (%.02f%%)\n", (trie_st.st_size + uidlist_st.st_size), input->v_offset, (trie_st.st_size + uidlist_st.st_size) / (float)input->v_offset * 100.0); i_stream_unref(&input); close(fd); i_array_init(&definite_uids, 128); i_array_init(&maybe_uids, 128); while ((str = fgets(buf, sizeof(buf), stdin)) != NULL) { ret = strlen(str)-1; str[ret] = 0; gettimeofday(&tv_start, NULL); ret = squat_trie_lookup(trie, str, SQUAT_INDEX_TYPE_HEADER | SQUAT_INDEX_TYPE_BODY, &definite_uids, &maybe_uids); if (ret < 0) printf("error\n"); else { gettimeofday(&tv_end, NULL); printf(" - Search took %.05f CPU seconds\n", timeval_diff_usecs(&tv_end, &tv_start)/1000000.0); printf(" - definite uids: "); result_print(&definite_uids); printf(" - maybe uids: "); result_print(&maybe_uids); } } return 0; } dovecot-2.2.33.2/src/plugins/fts-squat/squat-trie-private.h0000644000175000017500000001133413165463624020457 00000000000000#ifndef SQUAT_TRIE_PRIVATE_H #define SQUAT_TRIE_PRIVATE_H #include "file-dotlock.h" #include "squat-trie.h" #define SQUAT_TRIE_VERSION 2 #define SQUAT_TRIE_LOCK_TIMEOUT 60 #define SQUAT_TRIE_DOTLOCK_STALE_TIMEOUT (15*60) struct squat_file_header { uint8_t version; uint8_t unused[3]; uint32_t indexid; uint32_t uidvalidity; uint32_t used_file_size; uint32_t deleted_space; uint32_t node_count; uint32_t root_offset; uint32_t root_unused_uids; uint32_t root_next_uid; uint32_t root_uidlist_idx; uint8_t partial_len; uint8_t full_len; uint8_t normalize_map[256]; }; /* node file: FIXME: no up-to-date struct squat_file_header; // children are written before their parents node[] { uint8_t child_count; unsigned char chars[child_count]; packed neg_diff_to_first_child_offset; // relative to node packed diff_to_prev_offset[child_count-1]; packed[child_count] { // unused_uids_count == uid if have_uid_offset bit is zero (unused_uids_count << 1) | (have_uid_offset); [diff_to_prev_uid_offset;] // first one is relative to zero } } */ struct squat_node { unsigned int child_count:8; /* children.leaf_string contains this many bytes */ unsigned int leaf_string_length:16; /* TRUE = children.data contains our children. FALSE = children.offset contains offset to our children in the index file. */ unsigned int children_not_mapped:1; /* When allocating our children, use a sequential array. */ unsigned int want_sequential:1; /* This node's children are in a sequential array, meaning that the first SEQUENTIAL_COUNT children have chars[n] = n. */ unsigned int have_sequential:1; /* Number of UIDs that exists in parent node but not in this one (i.e. number of UIDs [0..next_uid-1] not in this node's uidlist). This is mainly used when adding new UIDs to our children to set the UID to be relative to this node's UID list. */ uint32_t unused_uids; /* next_uid=0 means there are no UIDs in this node, otherwise next_uid-1 is the last UID added to this node. */ uint32_t next_uid; uint32_t uid_list_idx; /* struct { unsigned char chars[child_count]; struct squat_node[child_count]; } *children; */ union { /* children_not_mapped determines if data or offset should be used. */ void *data; unsigned char *leaf_string; unsigned char static_leaf_string[sizeof(void *)]; uint32_t offset; } children; }; /* Return pointer to node.children.chars[] */ #define NODE_CHILDREN_CHARS(node) \ ((unsigned char *)(node)->children.data) /* Return pointer to node.children.node[] */ #define NODE_CHILDREN_NODES(_node) \ ((struct squat_node *)(NODE_CHILDREN_CHARS(_node) + \ MEM_ALIGN((_node)->child_count))) /* Return number of bytes allocated in node.children.data */ #define NODE_CHILDREN_ALLOC_SIZE(child_count) \ (MEM_ALIGN(child_count) + \ ((child_count) / 8 + 1) * 8 * sizeof(struct squat_node)) /* Return TRUE if children.leaf_string is set. */ #define NODE_IS_DYNAMIC_LEAF(node) \ ((node)->leaf_string_length > \ sizeof((node)->children.static_leaf_string)) /* Return node's leaf string. Assumes that it is set. */ #define NODE_LEAF_STRING(node) \ (NODE_IS_DYNAMIC_LEAF(node) ? \ (node)->children.leaf_string : (node)->children.static_leaf_string) struct squat_trie { struct squat_node root; struct squat_uidlist *uidlist; struct squat_file_header hdr; size_t node_alloc_size; unsigned int unmapped_child_count; enum squat_index_flags flags; enum file_lock_method lock_method; mode_t create_mode; gid_t create_gid; uint32_t uidvalidity; char *path; int fd; struct file_cache *file_cache; struct dotlock_settings dotlock_set; uoff_t locked_file_size; const void *data; size_t data_size; void *mmap_base; size_t mmap_size; unsigned char default_normalize_map[256]; unsigned int default_partial_len; unsigned int default_full_len; unsigned int corrupted:1; }; #define SQUAT_PACK_MAX_SIZE ((sizeof(uint32_t) * 8 + 7) / 7) static inline void squat_pack_num(uint8_t **p, uint32_t num) { /* number continues as long as the highest bit is set */ while (num >= 0x80) { **p = (num & 0x7f) | 0x80; *p += 1; num >>= 7; } **p = num; *p += 1; } static inline uint32_t squat_unpack_num(const uint8_t **p, const uint8_t *end) { const uint8_t *c = *p; uint32_t value = 0; unsigned int bits = 0; for (;;) { if (unlikely(c == end)) { /* we should never see EOF */ return 0; } value |= (*c & 0x7f) << bits; if (*c < 0x80) break; bits += 7; c++; } if (unlikely(bits >= 32)) { /* broken input */ *p = end; return 0; } *p = c + 1; return value; } int squat_trie_create_fd(struct squat_trie *trie, const char *path, int flags); void squat_trie_delete(struct squat_trie *trie); #endif dovecot-2.2.33.2/src/plugins/fts-squat/fts-squat-plugin.c0000644000175000017500000000070113123174404020110 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "fts-squat-plugin.h" const char *fts_squat_plugin_version = DOVECOT_ABI_VERSION; void fts_squat_plugin_init(struct module *module ATTR_UNUSED) { fts_backend_register(&fts_backend_squat); } void fts_squat_plugin_deinit(void) { fts_backend_unregister(fts_backend_squat.name); } const char *fts_squat_plugin_dependencies[] = { "fts", NULL }; dovecot-2.2.33.2/src/plugins/fts-squat/squat-trie.c0000644000175000017500000015055213165463624017010 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "read-full.h" #include "istream.h" #include "ostream.h" #include "unichar.h" #include "nfs-workarounds.h" #include "file-cache.h" #include "seq-range-array.h" #include "squat-uidlist.h" #include "squat-trie-private.h" #include #include #include #define DEFAULT_NORMALIZE_MAP_CHARS \ "EOTIRSACDNLMVUGPHBFWYXKJQZ0123456789@.-+#$%_&" #define DEFAULT_PARTIAL_LEN 4 #define DEFAULT_FULL_LEN 4 #define MAX_FAST_LEVEL 3 #define SEQUENTIAL_COUNT 46 #define TRIE_BYTES_LEFT(n) \ ((n) * SQUAT_PACK_MAX_SIZE) #define TRIE_READAHEAD_SIZE \ I_MAX(4096, 1 + 256 + TRIE_BYTES_LEFT(256)) struct squat_trie_build_context { struct squat_trie *trie; struct ostream *output; struct squat_uidlist_build_context *uidlist_build_ctx; struct file_lock *file_lock; struct dotlock *dotlock; uint32_t first_uid; unsigned int compress_nodes:1; }; struct squat_trie_iterate_node { struct squat_node *node; ARRAY_TYPE(seq_range) shifts; unsigned int idx; }; struct squat_trie_iterate_context { struct squat_trie *trie; struct squat_trie_iterate_node cur; ARRAY(struct squat_trie_iterate_node) parents; bool failed; }; static int squat_trie_map(struct squat_trie *trie, bool building); void squat_trie_delete(struct squat_trie *trie) { i_unlink_if_exists(trie->path); squat_uidlist_delete(trie->uidlist); } static void squat_trie_set_corrupted(struct squat_trie *trie) { trie->corrupted = TRUE; i_error("Corrupted file %s", trie->path); squat_trie_delete(trie); } static void squat_trie_normalize_map_build(struct squat_trie *trie) { static unsigned char valid_chars[] = DEFAULT_NORMALIZE_MAP_CHARS; unsigned int i, j; memset(trie->default_normalize_map, 0, sizeof(trie->default_normalize_map)); #if 1 for (i = 0, j = 1; i < sizeof(valid_chars)-1; i++) { unsigned char chr = valid_chars[i]; if (chr >= 'A' && chr <= 'Z') trie->default_normalize_map[chr-'A'+'a'] = j; trie->default_normalize_map[chr] = j++; } i_assert(j <= SEQUENTIAL_COUNT); for (i = 128; i < 256; i++) trie->default_normalize_map[i] = j++; #else for (i = 0; i < sizeof(valid_chars)-1; i++) { unsigned char chr = valid_chars[i]; if (chr >= 'A' && chr <= 'Z') trie->default_normalize_map[chr-'A'+'a'] = chr; trie->default_normalize_map[chr] = chr; } for (i = 128; i < 256; i++) trie->default_normalize_map[i] = i_toupper(i); #endif } static void node_free(struct squat_trie *trie, struct squat_node *node) { struct squat_node *children; unsigned int i; if (node->leaf_string_length > 0) { if (NODE_IS_DYNAMIC_LEAF(node)) i_free(node->children.leaf_string); } else if (!node->children_not_mapped) { children = NODE_CHILDREN_NODES(node); trie->node_alloc_size -= NODE_CHILDREN_ALLOC_SIZE(node->child_count); for (i = 0; i < node->child_count; i++) node_free(trie, &children[i]); i_free(node->children.data); } } struct squat_trie * squat_trie_init(const char *path, uint32_t uidvalidity, enum file_lock_method lock_method, enum squat_index_flags flags, mode_t mode, gid_t gid) { struct squat_trie *trie; trie = i_new(struct squat_trie, 1); trie->path = i_strdup(path); trie->uidlist = squat_uidlist_init(trie); trie->fd = -1; trie->lock_method = lock_method; trie->uidvalidity = uidvalidity; trie->flags = flags; trie->create_mode = mode; trie->create_gid = gid; squat_trie_normalize_map_build(trie); trie->dotlock_set.use_excl_lock = (flags & SQUAT_INDEX_FLAG_DOTLOCK_USE_EXCL) != 0; trie->dotlock_set.nfs_flush = (flags & SQUAT_INDEX_FLAG_NFS_FLUSH) != 0; trie->dotlock_set.timeout = SQUAT_TRIE_LOCK_TIMEOUT; trie->dotlock_set.stale_timeout = SQUAT_TRIE_DOTLOCK_STALE_TIMEOUT; trie->default_partial_len = DEFAULT_PARTIAL_LEN; trie->default_full_len = DEFAULT_FULL_LEN; return trie; } static void squat_trie_close_fd(struct squat_trie *trie) { trie->data = NULL; trie->data_size = 0; if (trie->mmap_size != 0) { if (munmap(trie->mmap_base, trie->mmap_size) < 0) i_error("munmap(%s) failed: %m", trie->path); trie->mmap_base = NULL; trie->mmap_size = 0; } if (trie->fd != -1) { if (close(trie->fd) < 0) i_error("close(%s) failed: %m", trie->path); trie->fd = -1; } } static void squat_trie_close(struct squat_trie *trie) { trie->corrupted = FALSE; node_free(trie, &trie->root); i_zero(&trie->root); i_zero(&trie->hdr); squat_trie_close_fd(trie); if (trie->file_cache != NULL) file_cache_free(&trie->file_cache); trie->locked_file_size = 0; } void squat_trie_deinit(struct squat_trie **_trie) { struct squat_trie *trie = *_trie; *_trie = NULL; squat_trie_close(trie); squat_uidlist_deinit(trie->uidlist); i_free(trie->path); i_free(trie); } void squat_trie_set_partial_len(struct squat_trie *trie, unsigned int len) { trie->default_partial_len = len; } void squat_trie_set_full_len(struct squat_trie *trie, unsigned int len) { trie->default_full_len = len; } static void squat_trie_header_init(struct squat_trie *trie) { i_zero(&trie->hdr); trie->hdr.version = SQUAT_TRIE_VERSION; trie->hdr.indexid = time(NULL); trie->hdr.uidvalidity = trie->uidvalidity; trie->hdr.partial_len = trie->default_partial_len; trie->hdr.full_len = trie->default_full_len; i_assert(sizeof(trie->hdr.normalize_map) == sizeof(trie->default_normalize_map)); memcpy(trie->hdr.normalize_map, trie->default_normalize_map, sizeof(trie->hdr.normalize_map)); } static int squat_trie_open_fd(struct squat_trie *trie) { trie->fd = open(trie->path, O_RDWR); if (trie->fd == -1) { if (errno == ENOENT) { squat_trie_header_init(trie); return 0; } i_error("open(%s) failed: %m", trie->path); return -1; } if (trie->file_cache != NULL) file_cache_set_fd(trie->file_cache, trie->fd); return 0; } int squat_trie_open(struct squat_trie *trie) { squat_trie_close(trie); if (squat_trie_open_fd(trie) < 0) return -1; return squat_trie_map(trie, FALSE); } static int squat_trie_is_file_stale(struct squat_trie *trie) { struct stat st, st2; if ((trie->flags & SQUAT_INDEX_FLAG_NFS_FLUSH) != 0) nfs_flush_file_handle_cache(trie->path); if (nfs_safe_stat(trie->path, &st) < 0) { if (errno == ENOENT) return 1; i_error("stat(%s) failed: %m", trie->path); return -1; } if (fstat(trie->fd, &st2) < 0) { if (errno == ESTALE) return 1; i_error("fstat(%s) failed: %m", trie->path); return -1; } trie->locked_file_size = st2.st_size; if (st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev)) { i_assert(trie->locked_file_size >= trie->data_size); return 0; } return 1; } int squat_trie_refresh(struct squat_trie *trie) { int ret; ret = squat_trie_is_file_stale(trie); if (ret > 0) ret = squat_trie_open(trie); return ret; } static int squat_trie_lock(struct squat_trie *trie, int lock_type, struct file_lock **file_lock_r, struct dotlock **dotlock_r) { int ret; i_assert(trie->fd != -1); *file_lock_r = NULL; *dotlock_r = NULL; for (;;) { if (trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { ret = file_wait_lock(trie->fd, trie->path, lock_type, trie->lock_method, SQUAT_TRIE_LOCK_TIMEOUT, file_lock_r); } else { ret = file_dotlock_create(&trie->dotlock_set, trie->path, 0, dotlock_r); } if (ret == 0) { i_error("squat trie %s: Locking timed out", trie->path); return 0; } if (ret < 0) return -1; /* if the trie has been compressed, we need to reopen the file and try to lock again */ ret = squat_trie_is_file_stale(trie); if (ret == 0) break; if (*file_lock_r != NULL) file_unlock(file_lock_r); else file_dotlock_delete(dotlock_r); if (ret < 0) return -1; squat_trie_close(trie); if (squat_trie_open_fd(trie) < 0) return -1; if (trie->fd == -1) return 0; } if ((trie->flags & SQUAT_INDEX_FLAG_NFS_FLUSH) != 0) nfs_flush_read_cache_locked(trie->path, trie->fd); return 1; } static void node_make_squential(struct squat_trie *trie, struct squat_node *node, int level) { const unsigned int alloc_size = NODE_CHILDREN_ALLOC_SIZE(SEQUENTIAL_COUNT); struct squat_node *children; unsigned char *chars; unsigned int i; i_assert(node->child_count == 0); trie->node_alloc_size += alloc_size; node->want_sequential = FALSE; node->have_sequential = TRUE; node->child_count = SEQUENTIAL_COUNT; node->children.data = i_malloc(alloc_size); chars = NODE_CHILDREN_CHARS(node); for (i = 0; i < SEQUENTIAL_COUNT; i++) chars[i] = i; if (level < MAX_FAST_LEVEL) { children = NODE_CHILDREN_NODES(node); for (i = 0; i < SEQUENTIAL_COUNT; i++) children[i].want_sequential = TRUE; } } static unsigned int node_add_child(struct squat_trie *trie, struct squat_node *node, unsigned char chr, int level) { unsigned int old_child_count = node->child_count; struct squat_node *children, *old_children; unsigned char *chars; size_t old_size, new_size; i_assert(node->leaf_string_length == 0); if (node->want_sequential) { node_make_squential(trie, node, level); if (chr < SEQUENTIAL_COUNT) return chr; old_child_count = SEQUENTIAL_COUNT; } node->child_count++; new_size = NODE_CHILDREN_ALLOC_SIZE(node->child_count); if (old_child_count == 0) { /* first child */ node->children.data = i_malloc(new_size); trie->node_alloc_size += new_size; } else { old_size = NODE_CHILDREN_ALLOC_SIZE(old_child_count); if (old_size != new_size) { trie->node_alloc_size += new_size - old_size; node->children.data = i_realloc(node->children.data, old_size, new_size); } children = NODE_CHILDREN_NODES(node); old_children = (void *)(NODE_CHILDREN_CHARS(node) + MEM_ALIGN(old_child_count)); if (children != old_children) { memmove(children, old_children, old_child_count * sizeof(struct squat_node)); } } chars = NODE_CHILDREN_CHARS(node); i_assert(chars != NULL); chars[node->child_count - 1] = chr; return node->child_count - 1; } static int trie_file_cache_read(struct squat_trie *trie, size_t offset, size_t size) { if (trie->file_cache == NULL) return 0; if (file_cache_read(trie->file_cache, offset, size) < 0) { i_error("read(%s) failed: %m", trie->path); return -1; } trie->data = file_cache_get_map(trie->file_cache, &trie->data_size); return 0; } static int node_read_children(struct squat_trie *trie, struct squat_node *node, int level) { const uint8_t *data, *end; const unsigned char *child_chars; struct squat_node *child, *children = NULL; uoff_t node_offset; unsigned int i, child_idx, child_count; uoff_t base_offset; uint32_t num; i_assert(node->children_not_mapped); i_assert(!node->have_sequential); i_assert(trie->unmapped_child_count > 0); i_assert(trie->data_size <= trie->locked_file_size); trie->unmapped_child_count--; node_offset = node->children.offset; node->children_not_mapped = FALSE; node->children.data = NULL; if (trie_file_cache_read(trie, node_offset, TRIE_READAHEAD_SIZE) < 0) return -1; if (unlikely(node_offset >= trie->data_size)) { squat_trie_set_corrupted(trie); return -1; } data = CONST_PTR_OFFSET(trie->data, node_offset); end = CONST_PTR_OFFSET(trie->data, trie->data_size); child_count = *data++; if (unlikely(node_offset + child_count >= trie->data_size)) { squat_trie_set_corrupted(trie); return -1; } if (child_count == 0) return 0; child_chars = data; data += child_count; /* get child offsets */ base_offset = node_offset; for (i = 0; i < child_count; i++) { /* we always start with !have_sequential, so at i=0 this check always goes to add the first child */ if (node->have_sequential && child_chars[i] < SEQUENTIAL_COUNT) child_idx = child_chars[i]; else { child_idx = node_add_child(trie, node, child_chars[i], level); children = NODE_CHILDREN_NODES(node); } i_assert(children != NULL); child = &children[child_idx]; /* 1) child offset */ num = squat_unpack_num(&data, end); if (num == 0) { /* no children */ } else { if ((num & 1) != 0) { base_offset += num >> 1; } else { base_offset -= num >> 1; } if (base_offset >= trie->locked_file_size) { squat_trie_set_corrupted(trie); return -1; } trie->unmapped_child_count++; child->children_not_mapped = TRUE; child->children.offset = base_offset; } /* 2) uidlist */ child->uid_list_idx = squat_unpack_num(&data, end); if (child->uid_list_idx == 0) { /* we don't write nodes with empty uidlists */ squat_trie_set_corrupted(trie); return -1; } if (!UIDLIST_IS_SINGLETON(child->uid_list_idx)) { /* 3) next uid */ child->next_uid = squat_unpack_num(&data, end) + 1; } else { uint32_t idx = child->uid_list_idx; child->next_uid = 1 + squat_uidlist_singleton_last_uid(idx); } /* 4) unused uids + leaf string flag */ num = squat_unpack_num(&data, end); child->unused_uids = num >> 1; if ((num & 1) != 0) { /* leaf string */ unsigned int len; unsigned char *dest; /* 5) leaf string length */ len = child->leaf_string_length = squat_unpack_num(&data, end) + 1; if (!NODE_IS_DYNAMIC_LEAF(child)) dest = child->children.static_leaf_string; else { dest = child->children.leaf_string = i_malloc(len); } if (trie->file_cache != NULL) { /* the string may be long - recalculate the end pos */ size_t offset, size; offset = (const char *)data - (const char *)trie->data; size = len + TRIE_BYTES_LEFT(child_count - i); if (trie_file_cache_read(trie, offset, size) < 0) return -1; data = CONST_PTR_OFFSET(trie->data, offset); end = CONST_PTR_OFFSET(trie->data, trie->data_size); child_chars = CONST_PTR_OFFSET(trie->data, node_offset + 1); } if ((size_t)(end - data) < len) { squat_trie_set_corrupted(trie); return -1; } memcpy(dest, data, len); data += len; } } if (unlikely(data == end)) { /* we should never get this far */ squat_trie_set_corrupted(trie); return -1; } return 0; } static void node_write_children(struct squat_trie_build_context *ctx, struct squat_node *node, const uoff_t *node_offsets) { struct squat_node *children; const unsigned char *chars; uint8_t child_count, buf[SQUAT_PACK_MAX_SIZE * 5], *bufp; uoff_t base_offset; unsigned int i; chars = NODE_CHILDREN_CHARS(node); children = NODE_CHILDREN_NODES(node); base_offset = ctx->output->offset; child_count = node->child_count; o_stream_nsend(ctx->output, &child_count, 1); o_stream_nsend(ctx->output, chars, child_count); for (i = 0; i < child_count; i++) { bufp = buf; /* 1) child offset */ if (node_offsets[i] == 0) *bufp++ = 0; else if (node_offsets[i] >= base_offset) { squat_pack_num(&bufp, ((node_offsets[i] - base_offset) << 1) | 1); base_offset = node_offsets[i]; } else { squat_pack_num(&bufp, (base_offset - node_offsets[i]) << 1); base_offset = node_offsets[i]; } /* 2) uidlist */ squat_pack_num(&bufp, children[i].uid_list_idx); if (!UIDLIST_IS_SINGLETON(children[i].uid_list_idx)) { /* 3) next uid */ squat_pack_num(&bufp, children[i].next_uid - 1); } if (children[i].leaf_string_length == 0) { /* 4a) unused uids */ squat_pack_num(&bufp, children[i].unused_uids << 1); o_stream_nsend(ctx->output, buf, bufp - buf); } else { i_assert(node_offsets[i] == 0); /* 4b) unused uids + flag */ squat_pack_num(&bufp, (children[i].unused_uids << 1) | 1); /* 5) leaf string length */ squat_pack_num(&bufp, children[i].leaf_string_length - 1); o_stream_nsend(ctx->output, buf, bufp - buf); o_stream_nsend(ctx->output, NODE_LEAF_STRING(&children[i]), children[i].leaf_string_length); } } } static inline void node_add_uid(struct squat_trie_build_context *ctx, uint32_t uid, struct squat_node *node) { if (uid < node->next_uid) { /* duplicate */ return; } node->unused_uids += uid - node->next_uid; node->next_uid = uid + 1; node->uid_list_idx = squat_uidlist_build_add_uid(ctx->uidlist_build_ctx, node->uid_list_idx, uid); } static void node_split_string(struct squat_trie_build_context *ctx, struct squat_node *node) { struct squat_node *child; unsigned char *str; unsigned int uid, idx, leafstr_len = node->leaf_string_length; i_assert(leafstr_len > 0); /* make a copy of the leaf string and convert to normal node by removing it. */ str = t_malloc(leafstr_len); if (!NODE_IS_DYNAMIC_LEAF(node)) memcpy(str, node->children.static_leaf_string, leafstr_len); else { memcpy(str, node->children.leaf_string, leafstr_len); i_free(node->children.leaf_string); } node->leaf_string_length = 0; /* create a new child node for the rest of the string */ idx = node_add_child(ctx->trie, node, str[0], MAX_FAST_LEVEL); child = NODE_CHILDREN_NODES(node) + idx; /* update uidlist to contain all of parent's UIDs */ child->next_uid = node->next_uid - node->unused_uids; for (uid = 0; uid < child->next_uid; uid++) { child->uid_list_idx = squat_uidlist_build_add_uid(ctx->uidlist_build_ctx, child->uid_list_idx, uid); } i_assert(!child->have_sequential && child->children.data == NULL); if (leafstr_len > 1) { /* make the child a leaf string */ leafstr_len--; child->leaf_string_length = leafstr_len; if (!NODE_IS_DYNAMIC_LEAF(child)) { memcpy(child->children.static_leaf_string, str + 1, leafstr_len); } else { child->children.leaf_string = i_malloc(leafstr_len); memcpy(child->children.leaf_string, str + 1, leafstr_len); } } } static bool node_leaf_string_add_or_split(struct squat_trie_build_context *ctx, struct squat_node *node, const unsigned char *data, unsigned int data_len) { const unsigned char *str = NODE_LEAF_STRING(node); const unsigned int leafstr_len = node->leaf_string_length; unsigned int i; if (data_len != leafstr_len) { /* different lengths, can't match */ T_BEGIN { node_split_string(ctx, node); } T_END; return FALSE; } for (i = 0; i < data_len; i++) { if (data[i] != str[i]) { /* non-match */ T_BEGIN { node_split_string(ctx, node); } T_END; return FALSE; } } return TRUE; } static int squat_build_add(struct squat_trie_build_context *ctx, uint32_t uid, const unsigned char *data, unsigned int size) { struct squat_trie *trie = ctx->trie; struct squat_node *node = &trie->root; const unsigned char *end = data + size; unsigned char *chars; unsigned int idx; int level = 0; for (;;) { if (node->children_not_mapped) { if (unlikely(node_read_children(trie, node, level) < 0)) return -1; } if (node->leaf_string_length != 0) { /* the whole string must match or we need to split the node */ if (node_leaf_string_add_or_split(ctx, node, data, end - data)) { node_add_uid(ctx, uid, node); return 0; } } node_add_uid(ctx, uid, node); if (unlikely(uid < node->unused_uids)) { squat_trie_set_corrupted(trie); return -1; } /* child node's UIDs are relative to ours. so for example if we're adding UID 4 and this node now has [2,4] UIDs, unused_uids=3 and so the child node will be adding UID 4-3 = 1. */ uid -= node->unused_uids; if (data == end) return 0; level++; if (node->have_sequential) { i_assert(node->child_count >= SEQUENTIAL_COUNT); if (*data < SEQUENTIAL_COUNT) { idx = *data; goto found; } idx = SEQUENTIAL_COUNT; } else { idx = 0; } chars = NODE_CHILDREN_CHARS(node); for (; idx < node->child_count; idx++) { if (chars[idx] == *data) goto found; } break; found: data++; node = NODE_CHILDREN_NODES(node) + idx; } /* create new children */ i_assert(node->leaf_string_length == 0); for (;;) { idx = node_add_child(trie, node, *data, size - (end - data) + 1); node = NODE_CHILDREN_NODES(node) + idx; node_add_uid(ctx, uid, node); uid = 0; if (++data == end) break; if (!node->have_sequential) { /* convert the node into a leaf string */ unsigned int len = end - data; i_assert(node->children.data == NULL); node->leaf_string_length = len; if (!NODE_IS_DYNAMIC_LEAF(node)) { memcpy(node->children.static_leaf_string, data, len); } else { node->children.leaf_string = i_malloc(len); memcpy(node->children.leaf_string, data, len); } break; } } return 0; } static int squat_build_word_bytes(struct squat_trie_build_context *ctx, uint32_t uid, const unsigned char *data, unsigned int size) { struct squat_trie *trie = ctx->trie; unsigned int i; if (trie->hdr.full_len <= trie->hdr.partial_len) i = 0; else { /* the first word is longer than others */ if (squat_build_add(ctx, uid, data, I_MIN(size, trie->hdr.full_len)) < 0) return -1; i = 1; } for (; i < size; i++) { if (squat_build_add(ctx, uid, data + i, I_MIN(trie->hdr.partial_len, size-i)) < 0) return -1; } return 0; } static int squat_build_word(struct squat_trie_build_context *ctx, uint32_t uid, const unsigned char *data, const uint8_t *char_lengths, unsigned int size) { struct squat_trie *trie = ctx->trie; unsigned int i, j, bytelen; if (char_lengths == NULL) { /* optimization path: all characters are bytes */ return squat_build_word_bytes(ctx, uid, data, size); } if (trie->hdr.full_len <= trie->hdr.partial_len) i = 0; else { /* the first word is longer than others */ bytelen = 0; for (j = 0; j < trie->hdr.full_len && bytelen < size; j++) bytelen += char_lengths[bytelen]; i_assert(bytelen <= size); if (squat_build_add(ctx, uid, data, bytelen) < 0) return -1; i = char_lengths[0]; } for (; i < size; i += char_lengths[i]) { bytelen = 0; for (j = 0; j < trie->hdr.partial_len && i+bytelen < size; j++) bytelen += char_lengths[i + bytelen]; i_assert(i + bytelen <= size); if (squat_build_add(ctx, uid, data + i, bytelen) < 0) return -1; } return 0; } static unsigned char * squat_data_normalize(struct squat_trie *trie, const unsigned char *data, unsigned int size) { static const unsigned char replacement_utf8[] = { 0xef, 0xbf, 0xbd }; unsigned char *dest; unsigned int i; dest = t_malloc(size); for (i = 0; i < size; i++) { if (data[i] == replacement_utf8[0] && i + 2 < size && data[i+1] == replacement_utf8[1] && data[i+2] == replacement_utf8[2]) { /* Don't index replacement character */ dest[i++] = 0; dest[i++] = 0; dest[i] = 0; } else { dest[i] = trie->hdr.normalize_map[data[i]]; } } return dest; } static int squat_trie_build_more_real(struct squat_trie_build_context *ctx, uint32_t uid, enum squat_index_type type, const unsigned char *input, unsigned int size) { struct squat_trie *trie = ctx->trie; const unsigned char *data; uint8_t *char_lengths; unsigned int i, start = 0; bool multibyte_chars = FALSE; int ret = 0; uid = uid * 2 + (type == SQUAT_INDEX_TYPE_HEADER ? 1 : 0); char_lengths = t_malloc(size); data = squat_data_normalize(trie, input, size); for (i = 0; i < size; i++) { char_lengths[i] = uni_utf8_char_bytes(input[i]); if (char_lengths[i] != 1) multibyte_chars = TRUE; if (data[i] != '\0') continue; while (start < i && data[start] == '\0') start++; if (i != start) { if (squat_build_word(ctx, uid, data + start, !multibyte_chars ? NULL : char_lengths + start, i - start) < 0) { ret = -1; start = i; break; } } start = i + 1; } while (start < i && data[start] == '\0') start++; if (i != start) { if (squat_build_word(ctx, uid, data + start, !multibyte_chars ? NULL : char_lengths + start, i - start) < 0) ret = -1; } return ret; } int squat_trie_build_more(struct squat_trie_build_context *ctx, uint32_t uid, enum squat_index_type type, const unsigned char *input, unsigned int size) { int ret = 0; if (size != 0) T_BEGIN { ret = squat_trie_build_more_real(ctx, uid, type, input, size); } T_END; return ret; } static void node_drop_unused_children(struct squat_trie *trie, struct squat_node *node) { unsigned char *chars; struct squat_node *children_src, *children_dest; unsigned int i, j, orig_child_count = node->child_count; chars = NODE_CHILDREN_CHARS(node); children_src = NODE_CHILDREN_NODES(node); /* move chars */ for (i = j = 0; i < orig_child_count; i++) { if (children_src[i].next_uid != 0) chars[j++] = chars[i]; } node->child_count = j; /* move children. note that children_dest may point to different location than children_src, although they both point to the same node. */ children_dest = NODE_CHILDREN_NODES(node); for (i = j = 0; i < orig_child_count; i++) { if (children_src[i].next_uid != 0) children_dest[j++] = children_src[i]; else node_free(trie, &children_src[i]); } } static int squat_write_node(struct squat_trie_build_context *ctx, struct squat_node *node, uoff_t *node_offset_r, int level) { struct squat_trie *trie = ctx->trie; struct squat_node *children; unsigned int i; uoff_t *node_offsets; uint8_t child_count; int ret; i_assert(node->next_uid != 0); if (node->children_not_mapped && ctx->compress_nodes) { if (node_read_children(trie, node, MAX_FAST_LEVEL) < 0) return -1; } node->have_sequential = FALSE; node_drop_unused_children(trie, node); child_count = node->child_count; if (child_count == 0) { i_assert(!node->children_not_mapped || node->leaf_string_length == 0); *node_offset_r = !node->children_not_mapped ? 0 : node->children.offset; return 0; } i_assert(!node->children_not_mapped); trie->hdr.node_count++; children = NODE_CHILDREN_NODES(node); node_offsets = t_new(uoff_t, child_count); for (i = 0; i < child_count; i++) { T_BEGIN { ret = squat_write_node(ctx, &children[i], &node_offsets[i], level + 1); } T_END; if (ret < 0) return -1; } *node_offset_r = ctx->output->offset; node_write_children(ctx, node, node_offsets); return 0; } static int squat_write_nodes(struct squat_trie_build_context *ctx) { struct squat_trie *trie = ctx->trie; uoff_t node_offset; int ret; if (ctx->trie->root.next_uid == 0) return 0; T_BEGIN { ret = squat_write_node(ctx, &ctx->trie->root, &node_offset, 0); } T_END; if (ret < 0) return -1; trie->hdr.root_offset = node_offset; trie->hdr.root_unused_uids = trie->root.unused_uids; trie->hdr.root_next_uid = trie->root.next_uid; trie->hdr.root_uidlist_idx = trie->root.uid_list_idx; return 0; } static struct squat_trie_iterate_context * squat_trie_iterate_init(struct squat_trie *trie) { struct squat_trie_iterate_context *ctx; ctx = i_new(struct squat_trie_iterate_context, 1); ctx->trie = trie; ctx->cur.node = &trie->root; i_array_init(&ctx->parents, trie->hdr.partial_len*2); return ctx; } static int squat_trie_iterate_deinit(struct squat_trie_iterate_context *ctx) { struct squat_trie_iterate_node *node; int ret = ctx->failed ? -1 : 0; if (array_is_created(&ctx->cur.shifts)) { array_foreach_modifiable(&ctx->parents, node) array_free(&node->shifts); array_free(&ctx->cur.shifts); } array_free(&ctx->parents); i_free(ctx); return ret; } static struct squat_node * squat_trie_iterate_first(struct squat_trie_iterate_context *ctx) { if (ctx->cur.node->children_not_mapped) { if (node_read_children(ctx->trie, ctx->cur.node, 1) < 0) { ctx->failed = TRUE; return NULL; } } return ctx->cur.node; } static struct squat_node * squat_trie_iterate_next(struct squat_trie_iterate_context *ctx, ARRAY_TYPE(seq_range) *shifts_r) { struct squat_trie_iterate_node *iter_nodes; struct squat_node *children; unsigned int count, shift_count = 0; while (ctx->cur.idx == ctx->cur.node->child_count || ctx->cur.node->uid_list_idx == 0) { iter_nodes = array_get_modifiable(&ctx->parents, &count); if (count == 0) return NULL; if (array_is_created(&ctx->cur.shifts)) array_free(&ctx->cur.shifts); ctx->cur = iter_nodes[count-1]; array_delete(&ctx->parents, count-1, 1); } *shifts_r = ctx->cur.shifts; if (array_is_created(&ctx->cur.shifts)) shift_count = array_count(&ctx->cur.shifts); children = NODE_CHILDREN_NODES(ctx->cur.node); while (children[ctx->cur.idx++].uid_list_idx == 0) { if (ctx->cur.idx == ctx->cur.node->child_count) { /* no more non-empty children in this node */ return squat_trie_iterate_next(ctx, shifts_r); } } array_append(&ctx->parents, &ctx->cur, 1); ctx->cur.node = &children[ctx->cur.idx-1]; ctx->cur.idx = 0; if (shift_count != 0) i_array_init(&ctx->cur.shifts, shift_count); else i_zero(&ctx->cur.shifts); return squat_trie_iterate_first(ctx); } static void squat_uidlist_update_expunged_uids(const ARRAY_TYPE(seq_range) *shifts_arr, ARRAY_TYPE(seq_range) *child_shifts, ARRAY_TYPE(seq_range) *uids_arr, struct squat_trie *trie, struct squat_node *node, bool do_shifts) { const struct seq_range *shifts; struct seq_range *uids, shift; unsigned int i, uid_idx, uid_count, shift_count; uint32_t child_shift_seq1, child_shift_count, seq_high; unsigned int shift_sum = 0, child_sum = 0; if (!array_is_created(shifts_arr)) { i_assert(node->uid_list_idx != 0 || node->child_count == 0); return; } /* we'll recalculate this */ node->unused_uids = 0; uids = array_get_modifiable(uids_arr, &uid_count); shifts = array_get(shifts_arr, &shift_count); for (i = 0, uid_idx = 0, seq_high = 0;; ) { /* skip UID ranges until we skip/overlap shifts */ while (uid_idx < uid_count && (i == shift_count || I_MAX(shifts[i].seq1, seq_high) > uids[uid_idx].seq2)) { i_assert(uids[uid_idx].seq1 >= shift_sum); uids[uid_idx].seq1 -= shift_sum; uids[uid_idx].seq2 -= shift_sum; child_sum += uids[uid_idx].seq2 - uids[uid_idx].seq1 + 1; if (uid_idx > 0 && uids[uid_idx-1].seq2 >= uids[uid_idx].seq1 - 1) { /* we can merge this and the previous range */ i_assert(uids[uid_idx-1].seq2 == uids[uid_idx].seq1 - 1); uids[uid_idx-1].seq2 = uids[uid_idx].seq2; array_delete(uids_arr, uid_idx, 1); uids = array_get_modifiable(uids_arr, &uid_count); } else { if (uid_idx == 0) node->unused_uids += uids[0].seq1; else { node->unused_uids += uids[uid_idx].seq1 - uids[uid_idx-1].seq2 - 1; } uid_idx++; } } if (uid_idx == uid_count) break; shift.seq1 = I_MAX(shifts[i].seq1, seq_high); shift.seq2 = shifts[i].seq2; if (shift.seq2 < uids[uid_idx].seq1) { /* shift is entirely before UID range */ shift_sum += shift.seq2 - shift.seq1 + 1; i++; } else { /* handle shifts before UID range */ if (shift.seq1 < uids[uid_idx].seq1) { shift_sum += uids[uid_idx].seq1 - shift.seq1; shift.seq1 = uids[uid_idx].seq1; } /* update child shifts */ child_shift_seq1 = child_sum + shift.seq1 - uids[uid_idx].seq1; child_shift_count = I_MIN(shift.seq2, uids[uid_idx].seq2) - shift.seq1 + 1; seq_range_array_add_range(child_shifts, child_shift_seq1, child_shift_seq1 + child_shift_count - 1); child_sum += child_shift_count; /* if the shifts continue after the UID range, treat it in the next loop iteration */ if (shift.seq2 <= uids[uid_idx].seq2) i++; else seq_high = uids[uid_idx].seq2 + 1; /* update UIDs - no matter where within the UID range the shifts happened, the result is the same: shift number of UIDs are removed, and the rest are decreased by shift_sum. 123 uids child_shifts a) s -> 12 1 b) s -> 12 2 c) s -> 12 3 */ if (uids[uid_idx].seq1 + child_shift_count > uids[uid_idx].seq2) { /* removed completely */ array_delete(uids_arr, uid_idx, 1); uids = array_get_modifiable(uids_arr, &uid_count); } else if (do_shifts) { /* the next loop iteration fixes the UIDs */ uids[uid_idx].seq1 += child_shift_count; } else { seq_range_array_remove_range(uids_arr, shift.seq1, I_MIN(shift.seq2, uids[uid_idx].seq2)); uids = array_get_modifiable(uids_arr, &uid_count); } shift_sum += child_shift_count; } if (!do_shifts) { /* root node - UIDs are only removed, not shifted */ shift_sum = 0; } } if (uid_count == 0) { /* no UIDs left, delete the node's children and mark it unused */ if (!NODE_IS_DYNAMIC_LEAF(node)) node_free(trie, node); node->child_count = 0; node->have_sequential = FALSE; node->next_uid = 0; } else { if (do_shifts) node->next_uid = uids[uid_count-1].seq2 + 1; else { node->unused_uids += (node->next_uid - 1) - uids[uid_count-1].seq2; } } } static int squat_trie_expunge_uidlists(struct squat_trie_build_context *ctx, struct squat_uidlist_rebuild_context *rebuild_ctx, struct squat_trie_iterate_context *iter, const ARRAY_TYPE(seq_range) *expunged_uids) { struct squat_node *node; ARRAY_TYPE(seq_range) uid_range, root_shifts, shifts; bool shift = FALSE; int ret = 0; node = squat_trie_iterate_first(iter); if (node->uid_list_idx == 0) return 0; i_array_init(&uid_range, 1024); i_array_init(&root_shifts, array_count(expunged_uids)); array_append_array(&root_shifts, expunged_uids); if (array_count(expunged_uids) > 0) i_array_init(&iter->cur.shifts, array_count(expunged_uids)); shifts = root_shifts; do { i_assert(node->uid_list_idx != 0); array_clear(&uid_range); if (squat_uidlist_get_seqrange(ctx->trie->uidlist, node->uid_list_idx, &uid_range) < 0) { ret = -1; break; } squat_uidlist_update_expunged_uids(&shifts, &iter->cur.shifts, &uid_range, ctx->trie, node, shift); node->uid_list_idx = squat_uidlist_rebuild_nextu(rebuild_ctx, &uid_range); i_assert(node->uid_list_idx != 0 || node->next_uid == 0); node = squat_trie_iterate_next(iter, &shifts); shift = TRUE; } while (node != NULL); array_free(&uid_range); array_free(&root_shifts); return ret; } static int squat_trie_renumber_uidlists2(struct squat_trie_build_context *ctx, struct squat_uidlist_rebuild_context *rebuild_ctx, struct squat_trie_iterate_context *iter) { struct squat_node *node; ARRAY_TYPE(seq_range) shifts; ARRAY_TYPE(uint32_t) uids; int ret = 0; node = squat_trie_iterate_first(iter); if (node->uid_list_idx == 0) return 0; i_array_init(&uids, 1024); while (node != NULL) { i_assert(node->uid_list_idx != 0); if (!UIDLIST_IS_SINGLETON(node->uid_list_idx)) { /* rebuild the uidlist */ array_clear(&uids); if (squat_uidlist_get(ctx->trie->uidlist, node->uid_list_idx, &uids) < 0) { ret = -1; break; } node->uid_list_idx = squat_uidlist_rebuild_next(rebuild_ctx, &uids); } node = squat_trie_iterate_next(iter, &shifts); } array_free(&uids); return ret; } static int squat_trie_renumber_uidlists(struct squat_trie_build_context *ctx, const ARRAY_TYPE(seq_range) *expunged_uids, bool compress) { struct squat_trie_iterate_context *iter; struct squat_uidlist_rebuild_context *rebuild_ctx; time_t now; int ret = 0; if ((ret = squat_uidlist_rebuild_init(ctx->uidlist_build_ctx, compress, &rebuild_ctx)) <= 0) return ret; now = time(NULL); ctx->trie->hdr.indexid = I_MAX((unsigned int)now, ctx->trie->hdr.indexid + 1); iter = squat_trie_iterate_init(ctx->trie); if (expunged_uids != NULL) { ret = squat_trie_expunge_uidlists(ctx, rebuild_ctx, iter, expunged_uids); } else { ret = squat_trie_renumber_uidlists2(ctx, rebuild_ctx, iter); } if (squat_trie_iterate_deinit(iter) < 0) ret = -1; /* lock the trie before we rename uidlist */ i_assert(ctx->file_lock == NULL && ctx->dotlock == NULL); if (squat_trie_lock(ctx->trie, F_WRLCK, &ctx->file_lock, &ctx->dotlock) <= 0) ret = -1; return squat_uidlist_rebuild_finish(rebuild_ctx, ret < 0); } static bool squat_trie_check_header(struct squat_trie *trie) { if (trie->hdr.version != SQUAT_TRIE_VERSION || trie->hdr.uidvalidity != trie->uidvalidity) return FALSE; if (trie->hdr.partial_len > trie->hdr.full_len) { i_error("Corrupted %s: partial len > full len", trie->path); return FALSE; } if (trie->hdr.full_len == 0) { i_error("Corrupted %s: full len=0", trie->path); return FALSE; } return TRUE; } static int squat_trie_map_header(struct squat_trie *trie) { int ret; if (trie->locked_file_size == 0) { /* newly created file */ squat_trie_header_init(trie); return 1; } i_assert(trie->fd != -1); if ((trie->flags & SQUAT_INDEX_FLAG_MMAP_DISABLE) != 0) { ret = pread_full(trie->fd, &trie->hdr, sizeof(trie->hdr), 0); if (ret <= 0) { if (ret < 0) { i_error("pread(%s) failed: %m", trie->path); return -1; } i_error("Corrupted %s: File too small", trie->path); return 0; } trie->data = NULL; trie->data_size = 0; } else { if (trie->locked_file_size < sizeof(trie->hdr)) { i_error("Corrupted %s: File too small", trie->path); return 0; } if (trie->mmap_size != 0) { if (munmap(trie->mmap_base, trie->mmap_size) < 0) i_error("munmap(%s) failed: %m", trie->path); } trie->mmap_size = trie->locked_file_size; trie->mmap_base = mmap(NULL, trie->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, trie->fd, 0); if (trie->mmap_base == MAP_FAILED) { trie->data = trie->mmap_base = NULL; trie->data_size = trie->mmap_size = 0; i_error("mmap(%s) failed: %m", trie->path); return -1; } memcpy(&trie->hdr, trie->mmap_base, sizeof(trie->hdr)); trie->data = trie->mmap_base; trie->data_size = trie->mmap_size; } return squat_trie_check_header(trie) ? 1 : 0; } static int squat_trie_map(struct squat_trie *trie, bool building) { struct file_lock *file_lock = NULL; struct dotlock *dotlock = NULL; bool changed; int ret; if (trie->fd != -1) { if (squat_trie_lock(trie, F_RDLCK, &file_lock, &dotlock) <= 0) return -1; if ((trie->flags & SQUAT_INDEX_FLAG_MMAP_DISABLE) != 0 && trie->file_cache == NULL) trie->file_cache = file_cache_new_path(trie->fd, trie->path); } ret = squat_trie_map_header(trie); if (ret == 0) { if (file_lock != NULL) file_unlock(&file_lock); else file_dotlock_delete(&dotlock); squat_trie_delete(trie); squat_trie_close(trie); squat_trie_header_init(trie); } changed = trie->root.children.offset != trie->hdr.root_offset; if (changed || trie->hdr.root_offset == 0) { node_free(trie, &trie->root); i_zero(&trie->root); trie->root.want_sequential = TRUE; trie->root.unused_uids = trie->hdr.root_unused_uids; trie->root.next_uid = trie->hdr.root_next_uid; trie->root.uid_list_idx = trie->hdr.root_uidlist_idx; trie->root.children.offset = trie->hdr.root_offset; if (trie->hdr.root_offset == 0) { trie->unmapped_child_count = 0; trie->root.children_not_mapped = FALSE; } else { trie->unmapped_child_count = 1; trie->root.children_not_mapped = TRUE; } } if (ret >= 0 && !building) { /* do this while we're still locked */ ret = squat_uidlist_refresh(trie->uidlist); } if (file_lock != NULL) file_unlock(&file_lock); if (dotlock != NULL) file_dotlock_delete(&dotlock); if (ret < 0) return -1; return trie->hdr.root_offset == 0 || !changed ? 0 : node_read_children(trie, &trie->root, 1); } int squat_trie_create_fd(struct squat_trie *trie, const char *path, int flags) { mode_t old_mask; int fd; old_mask = umask(0); fd = open(path, O_RDWR | O_CREAT | flags, trie->create_mode); umask(old_mask); if (fd == -1) { i_error("creat(%s) failed: %m", path); return -1; } if (trie->create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, trie->create_gid) < 0) { i_error("fchown(%s, -1, %ld) failed: %m", path, (long)trie->create_gid); i_close_fd(&fd); return -1; } } return fd; } int squat_trie_build_init(struct squat_trie *trie, struct squat_trie_build_context **ctx_r) { struct squat_trie_build_context *ctx; struct squat_uidlist_build_context *uidlist_build_ctx; if (trie->fd == -1) { trie->fd = squat_trie_create_fd(trie, trie->path, 0); if (trie->fd == -1) return -1; if (trie->file_cache != NULL) file_cache_set_fd(trie->file_cache, trie->fd); i_assert(trie->locked_file_size == 0); } /* uidlist locks building */ if (squat_uidlist_build_init(trie->uidlist, &uidlist_build_ctx) < 0) return -1; if (squat_trie_map(trie, TRUE) < 0) { squat_uidlist_build_deinit(&uidlist_build_ctx); return -1; } ctx = i_new(struct squat_trie_build_context, 1); ctx->trie = trie; ctx->uidlist_build_ctx = uidlist_build_ctx; ctx->first_uid = trie->root.next_uid; *ctx_r = ctx; return 0; } static int squat_trie_write_lock(struct squat_trie_build_context *ctx) { if (ctx->file_lock != NULL || ctx->dotlock != NULL) return 0; if (squat_trie_lock(ctx->trie, F_WRLCK, &ctx->file_lock, &ctx->dotlock) <= 0) return -1; return 0; } static int squat_trie_write(struct squat_trie_build_context *ctx) { struct squat_trie *trie = ctx->trie; struct file_lock *file_lock = NULL; struct ostream *output; const char *path; int fd = -1, ret = 0; if ((trie->hdr.used_file_size > sizeof(trie->hdr) && trie->unmapped_child_count < trie->hdr.node_count/4) || 1) { /* we might as well recreate the file */ ctx->compress_nodes = TRUE; path = t_strconcat(trie->path, ".tmp", NULL); fd = squat_trie_create_fd(trie, path, O_TRUNC); if (fd == -1) return -1; if (trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { ret = file_wait_lock(fd, path, F_WRLCK, trie->lock_method, SQUAT_TRIE_LOCK_TIMEOUT, &file_lock); if (ret <= 0) { if (ret == 0) { i_error("file_wait_lock(%s) failed: %m", path); } i_close_fd(&fd); return -1; } } output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(output); o_stream_nsend(output, &trie->hdr, sizeof(trie->hdr)); } else { /* we need to lock only while header is being written */ path = trie->path; ctx->compress_nodes = trie->hdr.used_file_size == sizeof(trie->hdr); if (trie->hdr.used_file_size == 0) { /* lock before opening the file, in case we reopen it */ if (squat_trie_write_lock(ctx) < 0) return -1; } output = o_stream_create_fd(trie->fd, 0, FALSE); o_stream_cork(output); if (trie->hdr.used_file_size != 0) (void)o_stream_seek(output, trie->hdr.used_file_size); else o_stream_nsend(output, &trie->hdr, sizeof(trie->hdr)); } ctx->output = output; ret = squat_write_nodes(ctx); ctx->output = NULL; /* write 1 byte guard at the end of file, so that we can verify broken squat_unpack_num() input by checking if data==end */ o_stream_nsend(output, "", 1); if (trie->corrupted) ret = -1; if (ret == 0) ret = squat_trie_write_lock(ctx); if (ret == 0) { trie->hdr.used_file_size = output->offset; (void)o_stream_seek(output, 0); o_stream_nsend(output, &trie->hdr, sizeof(trie->hdr)); } if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", path, o_stream_get_error(output)); ret = -1; } o_stream_destroy(&output); if (fd == -1) { /* appended to the existing file */ i_assert(file_lock == NULL); return ret; } /* recreating the trie file */ if (ret < 0) { if (close(fd) < 0) i_error("close(%s) failed: %m", path); fd = -1; } else if (rename(path, trie->path) < 0) { i_error("rename(%s, %s) failed: %m", path, trie->path); ret = -1; } if (ret < 0) { i_unlink_if_exists(path); if (file_lock != NULL) file_lock_free(&file_lock); } else { squat_trie_close_fd(trie); trie->fd = fd; trie->locked_file_size = trie->hdr.used_file_size; if (trie->file_cache != NULL) file_cache_set_fd(trie->file_cache, trie->fd); if (ctx->file_lock != NULL) file_lock_free(&ctx->file_lock); ctx->file_lock = file_lock; } return ret; } int squat_trie_build_deinit(struct squat_trie_build_context **_ctx, const ARRAY_TYPE(seq_range) *expunged_uids) { struct squat_trie_build_context *ctx = *_ctx; bool compress, unlock = TRUE; int ret; *_ctx = NULL; compress = (ctx->trie->root.next_uid - ctx->first_uid) > 10; /* keep trie locked while header is being written and when files are being renamed, so that while trie is read locked, uidlist can't change under. */ squat_uidlist_build_flush(ctx->uidlist_build_ctx); ret = squat_trie_renumber_uidlists(ctx, expunged_uids, compress); if (ret == 0) { ret = squat_trie_write(ctx); if (ret < 0) unlock = FALSE; } if (ret == 0) ret = squat_uidlist_build_finish(ctx->uidlist_build_ctx); if (ctx->file_lock != NULL) { if (unlock) file_unlock(&ctx->file_lock); else file_lock_free(&ctx->file_lock); } if (ctx->dotlock != NULL) file_dotlock_delete(&ctx->dotlock); squat_uidlist_build_deinit(&ctx->uidlist_build_ctx); i_free(ctx); return ret; } int squat_trie_get_last_uid(struct squat_trie *trie, uint32_t *last_uid_r) { if (trie->fd == -1) { if (squat_trie_open(trie) < 0) return -1; } *last_uid_r = I_MAX((trie->root.next_uid+1)/2, 1) - 1; return 0; } static int squat_trie_lookup_data(struct squat_trie *trie, const unsigned char *data, unsigned int size, ARRAY_TYPE(seq_range) *uids) { struct squat_node *node = &trie->root; unsigned char *chars; unsigned int idx; int level = 0; array_clear(uids); for (;;) { if (node->children_not_mapped) { if (node_read_children(trie, node, level) < 0) return -1; } if (node->leaf_string_length != 0) { unsigned int len = node->leaf_string_length; const unsigned char *str; if (len > sizeof(node->children.static_leaf_string)) str = node->children.leaf_string; else str = node->children.static_leaf_string; if (size > len || memcmp(data, str, size) != 0) return 0; /* match */ break; } if (size == 0) break; level++; if (node->have_sequential) { if (*data < SEQUENTIAL_COUNT) { idx = *data; goto found; } idx = SEQUENTIAL_COUNT; } else { idx = 0; } chars = NODE_CHILDREN_CHARS(node); for (; idx < node->child_count; idx++) { if (chars[idx] == *data) goto found; } return 0; found: /* follow to children */ if (level == 1) { /* root level, add all UIDs */ if (squat_uidlist_get_seqrange(trie->uidlist, node->uid_list_idx, uids) < 0) return -1; } else { if (squat_uidlist_filter(trie->uidlist, node->uid_list_idx, uids) < 0) return -1; } data++; size--; node = NODE_CHILDREN_NODES(node) + idx; } if (squat_uidlist_filter(trie->uidlist, node->uid_list_idx, uids) < 0) return -1; return 1; } static void squat_trie_filter_type(enum squat_index_type type, const ARRAY_TYPE(seq_range) *src, ARRAY_TYPE(seq_range) *dest) { const struct seq_range *src_range; struct seq_range new_range; unsigned int i, count, mask; uint32_t next_seq, uid; array_clear(dest); src_range = array_get(src, &count); if (count == 0) return; if ((type & SQUAT_INDEX_TYPE_HEADER) != 0 && (type & SQUAT_INDEX_TYPE_BODY) != 0) { /* everything is fine, just fix the UIDs */ new_range.seq1 = src_range[0].seq1 / 2; new_range.seq2 = src_range[0].seq2 / 2; for (i = 1; i < count; i++) { next_seq = src_range[i].seq1 / 2; if (next_seq == new_range.seq2 + 1) { /* we can continue the previous range */ } else { array_append(dest, &new_range, 1); new_range.seq1 = src_range[i].seq1 / 2; } new_range.seq2 = src_range[i].seq2 / 2; } array_append(dest, &new_range, 1); return; } /* we'll have to drop either header or body UIDs */ mask = (type & SQUAT_INDEX_TYPE_HEADER) != 0 ? 1 : 0; for (i = 0; i < count; i++) { for (uid = src_range[i].seq1; uid <= src_range[i].seq2; uid++) { if ((uid & 1) == mask) seq_range_array_add(dest, uid/2); } } } struct squat_trie_lookup_context { struct squat_trie *trie; enum squat_index_type type; ARRAY_TYPE(seq_range) *definite_uids, *maybe_uids; ARRAY_TYPE(seq_range) tmp_uids, tmp_uids2; bool first; }; static int squat_trie_lookup_partial(struct squat_trie_lookup_context *ctx, const unsigned char *data, uint8_t *char_lengths, unsigned int size) { const unsigned int partial_len = ctx->trie->hdr.partial_len; unsigned int char_idx, max_chars, i, j, bytelen; int ret; for (i = 0, max_chars = 0; i < size; max_chars++) i += char_lengths[i]; i_assert(max_chars > 0); i = 0; char_idx = 0; do { bytelen = 0; for (j = 0; j < partial_len && i+bytelen < size; j++) bytelen += char_lengths[i + bytelen]; ret = squat_trie_lookup_data(ctx->trie, data + i, bytelen, &ctx->tmp_uids); if (ret <= 0) { array_clear(ctx->maybe_uids); return ret; } if (ctx->first) { squat_trie_filter_type(ctx->type, &ctx->tmp_uids, ctx->maybe_uids); ctx->first = FALSE; } else { squat_trie_filter_type(ctx->type, &ctx->tmp_uids, &ctx->tmp_uids2); seq_range_array_intersect(ctx->maybe_uids, &ctx->tmp_uids2); } i += char_lengths[i]; char_idx++; } while (max_chars - char_idx >= partial_len); return 1; } static void squat_trie_add_unknown(struct squat_trie *trie, ARRAY_TYPE(seq_range) *maybe_uids) { struct seq_range *range, new_range; unsigned int count; uint32_t last_uid; last_uid = I_MAX((trie->root.next_uid+1)/2, 1) - 1; range = array_get_modifiable(maybe_uids, &count); if (count > 0 && range[count-1].seq2 == last_uid) { /* increase the range */ range[count-1].seq2 = (uint32_t)-1; } else { new_range.seq1 = last_uid + 1; new_range.seq2 = (uint32_t)-1; array_append(maybe_uids, &new_range, 1); } } static int squat_trie_lookup_real(struct squat_trie *trie, const char *str, enum squat_index_type type, ARRAY_TYPE(seq_range) *definite_uids, ARRAY_TYPE(seq_range) *maybe_uids) { struct squat_trie_lookup_context ctx; unsigned char *data; uint8_t *char_lengths; unsigned int i, start, bytes, str_bytelen, str_charlen; bool searched = FALSE; int ret = 0; array_clear(definite_uids); array_clear(maybe_uids); i_zero(&ctx); ctx.trie = trie; ctx.type = type; ctx.definite_uids = definite_uids; ctx.maybe_uids = maybe_uids; i_array_init(&ctx.tmp_uids, 128); i_array_init(&ctx.tmp_uids2, 128); ctx.first = TRUE; str_bytelen = strlen(str); char_lengths = str_bytelen == 0 ? NULL : t_malloc0(str_bytelen); for (i = 0, str_charlen = 0; i < str_bytelen; str_charlen++) { bytes = uni_utf8_char_bytes(str[i]); char_lengths[i] = bytes; i += bytes; } data = squat_data_normalize(trie, (const unsigned char *)str, str_bytelen); for (i = start = 0; i < str_bytelen && ret >= 0; i += char_lengths[i]) { if (data[i] != '\0') continue; /* string has nonindexed characters. search it in parts. */ if (i != start) { ret = squat_trie_lookup_partial(&ctx, data + start, char_lengths + start, i - start); searched = TRUE; } start = i + char_lengths[i]; } if (start == 0) { if (str_charlen <= trie->hdr.partial_len || trie->hdr.full_len > trie->hdr.partial_len) { ret = squat_trie_lookup_data(trie, data, str_bytelen, &ctx.tmp_uids); if (ret > 0) { squat_trie_filter_type(type, &ctx.tmp_uids, definite_uids); } } else { array_clear(definite_uids); } if (str_charlen <= trie->hdr.partial_len || trie->hdr.partial_len == 0) { /* we have the result */ array_clear(maybe_uids); } else { ret = squat_trie_lookup_partial(&ctx, data + start, char_lengths + start, i - start); } } else if (str_bytelen > 0) { /* string has nonindexed characters. finish the search. */ array_clear(definite_uids); if (i != start && ret >= 0) { ret = squat_trie_lookup_partial(&ctx, data + start, char_lengths + start, i - start); } else if (!searched) { /* string has only nonindexed chars, list all root UIDs as maybes */ ret = squat_uidlist_get_seqrange(trie->uidlist, trie->root.uid_list_idx, &ctx.tmp_uids); squat_trie_filter_type(type, &ctx.tmp_uids, maybe_uids); } } else { /* zero string length - list all root UIDs as definite answers */ #if 0 /* FIXME: this code is never actually reached now. */ ret = squat_uidlist_get_seqrange(trie->uidlist, trie->root.uid_list_idx, &ctx.tmp_uids); squat_trie_filter_type(type, &ctx.tmp_uids, definite_uids); #else i_unreached(); #endif } seq_range_array_remove_seq_range(maybe_uids, definite_uids); squat_trie_add_unknown(trie, maybe_uids); array_free(&ctx.tmp_uids); array_free(&ctx.tmp_uids2); return ret < 0 ? -1 : 0; } int squat_trie_lookup(struct squat_trie *trie, const char *str, enum squat_index_type type, ARRAY_TYPE(seq_range) *definite_uids, ARRAY_TYPE(seq_range) *maybe_uids) { int ret; T_BEGIN { ret = squat_trie_lookup_real(trie, str, type, definite_uids, maybe_uids); } T_END; return ret; } struct squat_uidlist *squat_trie_get_uidlist(struct squat_trie *trie) { return trie->uidlist; } size_t squat_trie_mem_used(struct squat_trie *trie, unsigned int *count_r) { *count_r = trie->hdr.node_count; return trie->node_alloc_size; } dovecot-2.2.33.2/src/plugins/fts-squat/fts-backend-squat.c0000644000175000017500000003120313165463624020215 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "unichar.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mail-search-build.h" #include "squat-trie.h" #include "fts-squat-plugin.h" #define SQUAT_FILE_PREFIX "dovecot.index.search" struct squat_fts_backend { struct fts_backend backend; struct mailbox *box; struct squat_trie *trie; unsigned int partial_len, full_len; bool refresh; }; struct squat_fts_backend_update_context { struct fts_backend_update_context ctx; struct squat_trie_build_context *build_ctx; enum squat_index_type squat_type; uint32_t uid; string_t *hdr; bool failed; }; static struct fts_backend *fts_backend_squat_alloc(void) { struct squat_fts_backend *backend; backend = i_new(struct squat_fts_backend, 1); backend->backend = fts_backend_squat; return &backend->backend; } static int fts_backend_squat_init(struct fts_backend *_backend, const char **error_r) { struct squat_fts_backend *backend = (struct squat_fts_backend *)_backend; const char *const *tmp, *env; unsigned int len; env = mail_user_plugin_getenv(_backend->ns->user, "fts_squat"); if (env == NULL) return 0; for (tmp = t_strsplit_spaces(env, " "); *tmp != NULL; tmp++) { if (strncmp(*tmp, "partial=", 8) == 0) { if (str_to_uint(*tmp + 8, &len) < 0 || len == 0) { *error_r = t_strdup_printf( "Invalid partial length: %s", *tmp + 8); return -1; } backend->partial_len = len; } else if (strncmp(*tmp, "full=", 5) == 0) { if (str_to_uint(*tmp + 5, &len) < 0 || len == 0) { *error_r = t_strdup_printf( "Invalid full length: %s", *tmp + 5); return -1; } backend->full_len = len; } else { *error_r = t_strdup_printf("Invalid setting: %s", *tmp); return -1; } } return 0; } static void fts_backend_squat_unset_box(struct squat_fts_backend *backend) { if (backend->trie != NULL) squat_trie_deinit(&backend->trie); backend->box = NULL; } static void fts_backend_squat_deinit(struct fts_backend *_backend) { struct squat_fts_backend *backend = (struct squat_fts_backend *)_backend; fts_backend_squat_unset_box(backend); i_free(backend); } static int fts_backend_squat_set_box(struct squat_fts_backend *backend, struct mailbox *box) { const struct mailbox_permissions *perm; struct mail_storage *storage; struct mailbox_status status; const char *path; enum squat_index_flags flags = 0; int ret; if (backend->box == box) { if (backend->refresh) { ret = squat_trie_refresh(backend->trie); if (ret < 0) return ret; backend->refresh = FALSE; } return 0; } fts_backend_squat_unset_box(backend); backend->refresh = FALSE; if (box == NULL) return 0; perm = mailbox_get_permissions(box); storage = mailbox_get_storage(box); if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0) i_unreached(); /* fts already checked this */ mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status); if (storage->set->mmap_disable) flags |= SQUAT_INDEX_FLAG_MMAP_DISABLE; if (storage->set->mail_nfs_index) flags |= SQUAT_INDEX_FLAG_NFS_FLUSH; if (storage->set->dotlock_use_excl) flags |= SQUAT_INDEX_FLAG_DOTLOCK_USE_EXCL; backend->trie = squat_trie_init(t_strconcat(path, "/"SQUAT_FILE_PREFIX, NULL), status.uidvalidity, storage->set->parsed_lock_method, flags, perm->file_create_mode, perm->file_create_gid); if (backend->partial_len != 0) squat_trie_set_partial_len(backend->trie, backend->partial_len); if (backend->full_len != 0) squat_trie_set_full_len(backend->trie, backend->full_len); backend->box = box; return squat_trie_open(backend->trie); } static int fts_backend_squat_get_last_uid(struct fts_backend *_backend, struct mailbox *box, uint32_t *last_uid_r) { struct squat_fts_backend *backend = (struct squat_fts_backend *)_backend; int ret = fts_backend_squat_set_box(backend, box); if (ret < 0) return -1; return squat_trie_get_last_uid(backend->trie, last_uid_r); } static struct fts_backend_update_context * fts_backend_squat_update_init(struct fts_backend *_backend) { struct squat_fts_backend_update_context *ctx; ctx = i_new(struct squat_fts_backend_update_context, 1); ctx->ctx.backend = _backend; ctx->hdr = str_new(default_pool, 1024*32); return &ctx->ctx; } static int get_all_msg_uids(struct mailbox *box, ARRAY_TYPE(seq_range) *uids) { struct mailbox_transaction_context *t; struct mail_search_context *search_ctx; struct mail_search_args *search_args; struct mail *mail; int ret; t = mailbox_transaction_begin(box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); search_ctx = mailbox_search_init(t, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(search_ctx, &mail)) { /* *2 because even/odd is for body/header */ seq_range_array_add_range(uids, mail->uid * 2, mail->uid * 2 + 1); } ret = mailbox_search_deinit(&search_ctx); (void)mailbox_transaction_commit(&t); return ret; } static int fts_backend_squat_update_uid_changed(struct squat_fts_backend_update_context *ctx) { int ret = 0; if (ctx->uid == 0) return 0; if (squat_trie_build_more(ctx->build_ctx, ctx->uid, SQUAT_INDEX_TYPE_HEADER, str_data(ctx->hdr), str_len(ctx->hdr)) < 0) ret = -1; str_truncate(ctx->hdr, 0); return ret; } static int fts_backend_squat_build_deinit(struct squat_fts_backend_update_context *ctx) { struct squat_fts_backend *backend = (struct squat_fts_backend *)ctx->ctx.backend; ARRAY_TYPE(seq_range) uids; int ret = 0; if (ctx->build_ctx == NULL) return 0; if (fts_backend_squat_update_uid_changed(ctx) < 0) ret = -1; i_array_init(&uids, 1024); if (get_all_msg_uids(backend->box, &uids) < 0) { (void)squat_trie_build_deinit(&ctx->build_ctx, NULL); ret = -1; } else { seq_range_array_invert(&uids, 2, (uint32_t)-2); if (squat_trie_build_deinit(&ctx->build_ctx, &uids) < 0) ret = -1; } array_free(&uids); return ret; } static int fts_backend_squat_update_deinit(struct fts_backend_update_context *_ctx) { struct squat_fts_backend_update_context *ctx = (struct squat_fts_backend_update_context *)_ctx; int ret = ctx->failed ? -1 : 0; if (fts_backend_squat_build_deinit(ctx) < 0) ret = -1; str_free(&ctx->hdr); i_free(ctx); return ret; } static void fts_backend_squat_update_set_mailbox(struct fts_backend_update_context *_ctx, struct mailbox *box) { struct squat_fts_backend_update_context *ctx = (struct squat_fts_backend_update_context *)_ctx; struct squat_fts_backend *backend = (struct squat_fts_backend *)ctx->ctx.backend; if (fts_backend_squat_build_deinit(ctx) < 0) ctx->failed = TRUE; if (fts_backend_squat_set_box(backend, box) < 0) ctx->failed = TRUE; else if (box != NULL) { if (squat_trie_build_init(backend->trie, &ctx->build_ctx) < 0) ctx->failed = TRUE; } } static void fts_backend_squat_update_expunge(struct fts_backend_update_context *_ctx ATTR_UNUSED, uint32_t last_uid ATTR_UNUSED) { /* FIXME */ } static bool fts_backend_squat_update_set_build_key(struct fts_backend_update_context *_ctx, const struct fts_backend_build_key *key) { struct squat_fts_backend_update_context *ctx = (struct squat_fts_backend_update_context *)_ctx; if (ctx->failed) return FALSE; if (key->uid != ctx->uid) { if (fts_backend_squat_update_uid_changed(ctx) < 0) ctx->failed = TRUE; } switch (key->type) { case FTS_BACKEND_BUILD_KEY_HDR: case FTS_BACKEND_BUILD_KEY_MIME_HDR: str_printfa(ctx->hdr, "%s: ", key->hdr_name); ctx->squat_type = SQUAT_INDEX_TYPE_HEADER; break; case FTS_BACKEND_BUILD_KEY_BODY_PART: ctx->squat_type = SQUAT_INDEX_TYPE_BODY; break; case FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY: i_unreached(); } ctx->uid = key->uid; return TRUE; } static void fts_backend_squat_update_unset_build_key(struct fts_backend_update_context *_ctx) { struct squat_fts_backend_update_context *ctx = (struct squat_fts_backend_update_context *)_ctx; if (ctx->squat_type == SQUAT_INDEX_TYPE_HEADER) str_append_c(ctx->hdr, '\n'); } static int fts_backend_squat_update_build_more(struct fts_backend_update_context *_ctx, const unsigned char *data, size_t size) { struct squat_fts_backend_update_context *ctx = (struct squat_fts_backend_update_context *)_ctx; if (ctx->squat_type == SQUAT_INDEX_TYPE_HEADER) { str_append_n(ctx->hdr, data, size); return 0; } return squat_trie_build_more(ctx->build_ctx, ctx->uid, ctx->squat_type, data, size); } static int fts_backend_squat_refresh(struct fts_backend *_backend) { struct squat_fts_backend *backend = (struct squat_fts_backend *)_backend; backend->refresh = TRUE; return 0; } static int fts_backend_squat_optimize(struct fts_backend *_backend ATTR_UNUSED) { /* FIXME: drop expunged messages */ return 0; } static int squat_lookup_arg(struct squat_fts_backend *backend, const struct mail_search_arg *arg, bool and_args, ARRAY_TYPE(seq_range) *definite_uids, ARRAY_TYPE(seq_range) *maybe_uids) { enum squat_index_type squat_type; ARRAY_TYPE(seq_range) tmp_definite_uids, tmp_maybe_uids; string_t *dtc; uint32_t last_uid; int ret; switch (arg->type) { case SEARCH_TEXT: squat_type = SQUAT_INDEX_TYPE_HEADER | SQUAT_INDEX_TYPE_BODY; break; case SEARCH_BODY: squat_type = SQUAT_INDEX_TYPE_BODY; break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: squat_type = SQUAT_INDEX_TYPE_HEADER; break; default: return 0; } i_array_init(&tmp_definite_uids, 128); i_array_init(&tmp_maybe_uids, 128); dtc = t_str_new(128); if (backend->backend.ns->user-> default_normalizer(arg->value.str, strlen(arg->value.str), dtc) < 0) i_panic("squat: search key not utf8"); ret = squat_trie_lookup(backend->trie, str_c(dtc), squat_type, &tmp_definite_uids, &tmp_maybe_uids); if (arg->match_not) { /* definite -> non-match maybe -> maybe non-match -> maybe */ array_clear(&tmp_maybe_uids); if (squat_trie_get_last_uid(backend->trie, &last_uid) < 0) i_unreached(); seq_range_array_add_range(&tmp_maybe_uids, 1, last_uid); seq_range_array_remove_seq_range(&tmp_maybe_uids, &tmp_definite_uids); array_clear(&tmp_definite_uids); } if (and_args) { /* AND: definite && definite -> definite definite && maybe -> maybe maybe && maybe -> maybe */ /* put definites among maybies, so they can be intersected */ seq_range_array_merge(maybe_uids, definite_uids); seq_range_array_merge(&tmp_maybe_uids, &tmp_definite_uids); seq_range_array_intersect(maybe_uids, &tmp_maybe_uids); seq_range_array_intersect(definite_uids, &tmp_definite_uids); /* remove duplicate maybies that are also definites */ seq_range_array_remove_seq_range(maybe_uids, definite_uids); } else { /* OR: definite || definite -> definite definite || maybe -> definite maybe || maybe -> maybe */ /* remove maybies that are now definites */ seq_range_array_remove_seq_range(&tmp_maybe_uids, definite_uids); seq_range_array_remove_seq_range(maybe_uids, &tmp_definite_uids); seq_range_array_merge(definite_uids, &tmp_definite_uids); seq_range_array_merge(maybe_uids, &tmp_maybe_uids); } array_free(&tmp_definite_uids); array_free(&tmp_maybe_uids); return ret < 0 ? -1 : 1; } static int fts_backend_squat_lookup(struct fts_backend *_backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { struct squat_fts_backend *backend = (struct squat_fts_backend *)_backend; bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; bool first = TRUE; int ret; ret = fts_backend_squat_set_box(backend, box); if (ret < 0) return -1; for (; args != NULL; args = args->next) { ret = squat_lookup_arg(backend, args, first ? FALSE : and_args, &result->definite_uids, &result->maybe_uids); if (ret < 0) return -1; if (ret > 0) { args->match_always = TRUE; first = FALSE; } } return 0; } struct fts_backend fts_backend_squat = { .name = "squat", .flags = FTS_BACKEND_FLAG_NORMALIZE_INPUT, { fts_backend_squat_alloc, fts_backend_squat_init, fts_backend_squat_deinit, fts_backend_squat_get_last_uid, fts_backend_squat_update_init, fts_backend_squat_update_deinit, fts_backend_squat_update_set_mailbox, fts_backend_squat_update_expunge, fts_backend_squat_update_set_build_key, fts_backend_squat_update_unset_build_key, fts_backend_squat_update_build_more, fts_backend_squat_refresh, NULL, fts_backend_squat_optimize, fts_backend_default_can_lookup, fts_backend_squat_lookup, NULL, NULL } }; dovecot-2.2.33.2/src/plugins/fts-squat/squat-uidlist.c0000644000175000017500000012414713165463624017523 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "file-cache.h" #include "file-lock.h" #include "read-full.h" #include "write-full.h" #include "ostream.h" #include "mmap-util.h" #include "squat-trie-private.h" #include "squat-uidlist.h" #include #include #define UIDLIST_LIST_SIZE 31 #define UIDLIST_BLOCK_LIST_COUNT 100 #define UID_LIST_MASK_RANGE 0x80000000 /* set = points to uidlist index number, unset = points to uidlist offset */ #define UID_LIST_POINTER_MASK_LIST_IDX 0x80000000 #define UIDLIST_PACKED_FLAG_BITMASK 1 #define UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER 2 struct uidlist_list { unsigned int uid_count:31; unsigned int uid_begins_with_pointer:1; uint32_t uid_list[UIDLIST_LIST_SIZE]; }; struct squat_uidlist { struct squat_trie *trie; char *path; int fd; struct file_cache *file_cache; struct file_lock *file_lock; struct dotlock *dotlock; uoff_t locked_file_size; void *mmap_base; size_t mmap_size; struct squat_uidlist_file_header hdr; const void *data; size_t data_size; unsigned int cur_block_count; const uint32_t *cur_block_offsets; const uint32_t *cur_block_end_indexes; size_t max_size; unsigned int corrupted:1; unsigned int building:1; }; struct squat_uidlist_build_context { struct squat_uidlist *uidlist; struct ostream *output; ARRAY_TYPE(uint32_t) block_offsets; ARRAY_TYPE(uint32_t) block_end_indexes; ARRAY(struct uidlist_list) lists; uint32_t list_start_idx; struct squat_uidlist_file_header build_hdr; unsigned int need_reopen:1; }; struct squat_uidlist_rebuild_context { struct squat_uidlist *uidlist; struct squat_uidlist_build_context *build_ctx; int fd; struct ostream *output; ARRAY_TYPE(uint32_t) new_block_offsets, new_block_end_indexes; uoff_t cur_block_start_offset; uint32_t list_sizes[UIDLIST_BLOCK_LIST_COUNT]; uint32_t next_uid_list_idx; unsigned int list_idx; unsigned int new_count; }; static void squat_uidlist_close(struct squat_uidlist *uidlist); void squat_uidlist_delete(struct squat_uidlist *uidlist) { i_unlink_if_exists(uidlist->path); } static void squat_uidlist_set_corrupted(struct squat_uidlist *uidlist, const char *reason) { if (uidlist->corrupted) return; uidlist->corrupted = TRUE; i_error("Corrupted squat uidlist file %s: %s", uidlist->path, reason); squat_trie_delete(uidlist->trie); } static int uidlist_write_array(struct ostream *output, const uint32_t *uid_list, unsigned int uid_count, uint32_t packed_flags, uint32_t offset, bool write_size, uint32_t *size_r) { uint8_t *uidbuf, *bufp, sizebuf[SQUAT_PACK_MAX_SIZE], *sizebufp; uint8_t listbuf[SQUAT_PACK_MAX_SIZE], *listbufp = listbuf; uint32_t uid, uid2, prev, base_uid, size_value; unsigned int i, bitmask_len, uid_list_len; unsigned int idx, max_idx, mask; bool datastack; int num; if ((packed_flags & UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER) != 0) squat_pack_num(&listbufp, offset); /* @UNSAFE */ base_uid = uid_list[0] & ~UID_LIST_MASK_RANGE; datastack = uid_count < 1024*8/SQUAT_PACK_MAX_SIZE; if (datastack) uidbuf = t_malloc(SQUAT_PACK_MAX_SIZE * uid_count); else uidbuf = i_malloc(SQUAT_PACK_MAX_SIZE * uid_count); bufp = uidbuf; squat_pack_num(&bufp, base_uid); bitmask_len = (uid_list[uid_count-1] - base_uid + 7) / 8 + (bufp - uidbuf); if (bitmask_len < uid_count) { bitmask_build: i_assert(bitmask_len < SQUAT_PACK_MAX_SIZE*uid_count); memset(bufp, 0, bitmask_len - (bufp - uidbuf)); if ((uid_list[0] & UID_LIST_MASK_RANGE) == 0) { i = 1; uid = i == uid_count ? 0 : uid_list[i]; } else { i = 0; uid = uid_list[0] + 1; } base_uid++; for (; i < uid_count; i++) { i_assert((uid & ~UID_LIST_MASK_RANGE) >= base_uid); if ((uid & UID_LIST_MASK_RANGE) == 0) { uid -= base_uid; uid2 = uid; } else { uid &= ~UID_LIST_MASK_RANGE; uid -= base_uid; uid2 = uid_list[i+1] - base_uid; i++; } if (uid2 - uid < 3*8) { for (; uid <= uid2; uid++) bufp[uid / 8] |= 1 << (uid % 8); } else { /* first byte */ idx = uid / 8; num = uid % 8; if (num != 0) { uid += 8 - num; for (mask = 0; num < 8; num++) mask |= 1 << num; bufp[idx++] |= mask; } /* middle bytes */ num = uid2 % 8; max_idx = idx + (uid2 - num - uid)/8; for (; idx < max_idx; idx++, uid += 8) bufp[idx] = 0xff; /* last byte */ for (mask = 0; num >= 0; num--) mask |= 1 << num; bufp[idx] |= mask; } uid = i+1 == uid_count ? 0 : uid_list[i+1]; } uid_list_len = bitmask_len; packed_flags |= UIDLIST_PACKED_FLAG_BITMASK; } else { bufp = uidbuf; prev = 0; for (i = 0; i < uid_count; i++) { uid = uid_list[i]; if (unlikely((uid & ~UID_LIST_MASK_RANGE) < prev)) { if (!datastack) i_free(uidbuf); return -1; } if ((uid & UID_LIST_MASK_RANGE) == 0) { squat_pack_num(&bufp, (uid - prev) << 1); prev = uid + 1; } else { uid &= ~UID_LIST_MASK_RANGE; squat_pack_num(&bufp, 1 | (uid - prev) << 1); squat_pack_num(&bufp, uid_list[i+1] - uid - 1); prev = uid_list[i+1] + 1; i++; } } uid_list_len = bufp - uidbuf; if (uid_list_len > bitmask_len) { bufp = uidbuf; squat_pack_num(&bufp, base_uid); goto bitmask_build; } } size_value = ((uid_list_len + (listbufp - listbuf)) << 2) | packed_flags; if (write_size) { sizebufp = sizebuf; squat_pack_num(&sizebufp, size_value); o_stream_nsend(output, sizebuf, sizebufp - sizebuf); } o_stream_nsend(output, listbuf, listbufp - listbuf); o_stream_nsend(output, uidbuf, uid_list_len); if (!datastack) i_free(uidbuf); *size_r = size_value; return 0; } static int uidlist_write(struct ostream *output, const struct uidlist_list *list, bool write_size, uint32_t *size_r) { const uint32_t *uid_list = list->uid_list; uint8_t buf[SQUAT_PACK_MAX_SIZE], *bufp; uint32_t uid_count = list->uid_count; uint32_t packed_flags = 0; uint32_t offset = 0; int ret; if (list->uid_begins_with_pointer) { /* continued UID list */ packed_flags |= UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER; if ((uid_list[0] & UID_LIST_POINTER_MASK_LIST_IDX) != 0) { offset = ((uid_list[0] & ~UID_LIST_POINTER_MASK_LIST_IDX) << 1) | 1; if (list->uid_count == 1) { bufp = buf; squat_pack_num(&bufp, offset); o_stream_nsend(output, buf, bufp - buf); *size_r = (bufp - buf) << 2 | packed_flags; return 0; } } else if (unlikely(output->offset <= uid_list[0])) { i_assert(output->closed); return -1; } else { i_assert(list->uid_count > 1); offset = (output->offset - uid_list[0]) << 1; } uid_list++; uid_count--; } T_BEGIN { ret = uidlist_write_array(output, uid_list, uid_count, packed_flags, offset, write_size, size_r); } T_END; return ret; } static void squat_uidlist_map_blocks_set_pointers(struct squat_uidlist *uidlist) { const void *base; size_t end_index_size, end_size; base = CONST_PTR_OFFSET(uidlist->data, uidlist->hdr.block_list_offset + sizeof(uint32_t)); end_index_size = uidlist->cur_block_count * sizeof(uint32_t); end_size = end_index_size + uidlist->cur_block_count * sizeof(uint32_t); if (end_size <= uidlist->data_size) { uidlist->cur_block_end_indexes = base; uidlist->cur_block_offsets = CONST_PTR_OFFSET(base, end_index_size); } else { uidlist->cur_block_end_indexes = NULL; uidlist->cur_block_offsets = NULL; } } static int uidlist_file_cache_read(struct squat_uidlist *uidlist, size_t offset, size_t size) { if (uidlist->file_cache == NULL) return 0; if (file_cache_read(uidlist->file_cache, offset, size) < 0) { i_error("read(%s) failed: %m", uidlist->path); return -1; } uidlist->data = file_cache_get_map(uidlist->file_cache, &uidlist->data_size); squat_uidlist_map_blocks_set_pointers(uidlist); return 0; } static int squat_uidlist_map_blocks(struct squat_uidlist *uidlist) { const struct squat_uidlist_file_header *hdr = &uidlist->hdr; const void *base; uint32_t block_count, blocks_offset, blocks_size, i, verify_count; if (hdr->block_list_offset == 0) { /* empty file */ uidlist->cur_block_count = 0; return 1; } /* get number of blocks */ if (uidlist_file_cache_read(uidlist, hdr->block_list_offset, sizeof(block_count)) < 0) return -1; blocks_offset = hdr->block_list_offset + sizeof(block_count); if (blocks_offset > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "block list outside file"); return 0; } i_assert(uidlist->data != NULL); base = CONST_PTR_OFFSET(uidlist->data, hdr->block_list_offset); memcpy(&block_count, base, sizeof(block_count)); /* map the blocks */ blocks_size = block_count * sizeof(uint32_t)*2; if (uidlist_file_cache_read(uidlist, blocks_offset, blocks_size) < 0) return -1; if (blocks_offset + blocks_size > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "block list outside file"); return 0; } uidlist->cur_block_count = block_count; squat_uidlist_map_blocks_set_pointers(uidlist); i_assert(uidlist->cur_block_end_indexes != NULL); /* verify just a couple of the end indexes to make sure they look correct */ verify_count = I_MIN(block_count, 8); for (i = 1; i < verify_count; i++) { if (unlikely(uidlist->cur_block_end_indexes[i-1] >= uidlist->cur_block_end_indexes[i])) { squat_uidlist_set_corrupted(uidlist, "block list corrupted"); return 0; } } return 1; } static int squat_uidlist_map_header(struct squat_uidlist *uidlist) { if (uidlist->hdr.indexid == 0) { /* still being built */ return 1; } if (uidlist->hdr.indexid != uidlist->trie->hdr.indexid) { /* see if trie was recreated */ (void)squat_trie_open(uidlist->trie); } if (uidlist->hdr.indexid != uidlist->trie->hdr.indexid) { squat_uidlist_set_corrupted(uidlist, "wrong indexid"); return 0; } if (uidlist->hdr.used_file_size < sizeof(uidlist->hdr) || (uidlist->hdr.used_file_size > uidlist->mmap_size && uidlist->mmap_base != NULL)) { squat_uidlist_set_corrupted(uidlist, "broken used_file_size"); return 0; } return squat_uidlist_map_blocks(uidlist); } static void squat_uidlist_unmap(struct squat_uidlist *uidlist) { if (uidlist->mmap_size != 0) { if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0) i_error("munmap(%s) failed: %m", uidlist->path); uidlist->mmap_base = NULL; uidlist->mmap_size = 0; } uidlist->cur_block_count = 0; uidlist->cur_block_end_indexes = NULL; uidlist->cur_block_offsets = NULL; } static int squat_uidlist_mmap(struct squat_uidlist *uidlist) { struct stat st; if (fstat(uidlist->fd, &st) < 0) { i_error("fstat(%s) failed: %m", uidlist->path); return -1; } if (st.st_size < (off_t)sizeof(uidlist->hdr)) { squat_uidlist_set_corrupted(uidlist, "File too small"); return -1; } squat_uidlist_unmap(uidlist); uidlist->mmap_size = st.st_size; uidlist->mmap_base = mmap(NULL, uidlist->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, uidlist->fd, 0); if (uidlist->mmap_base == MAP_FAILED) { uidlist->data = uidlist->mmap_base = NULL; uidlist->data_size = uidlist->mmap_size = 0; i_error("mmap(%s) failed: %m", uidlist->path); return -1; } uidlist->data = uidlist->mmap_base; uidlist->data_size = uidlist->mmap_size; return 0; } static int squat_uidlist_map(struct squat_uidlist *uidlist) { const struct squat_uidlist_file_header *mmap_hdr = uidlist->mmap_base; int ret; if (mmap_hdr != NULL && !uidlist->building && uidlist->hdr.block_list_offset == mmap_hdr->block_list_offset) { /* file hasn't changed */ return 1; } if ((uidlist->trie->flags & SQUAT_INDEX_FLAG_MMAP_DISABLE) == 0) { if (mmap_hdr == NULL || uidlist->building || uidlist->mmap_size < mmap_hdr->used_file_size) { if (squat_uidlist_mmap(uidlist) < 0) return -1; } if (!uidlist->building) { memcpy(&uidlist->hdr, uidlist->mmap_base, sizeof(uidlist->hdr)); } } else if (uidlist->building) { /* we want to update blocks mapping, but using the header in memory */ } else { ret = pread_full(uidlist->fd, &uidlist->hdr, sizeof(uidlist->hdr), 0); if (ret <= 0) { if (ret < 0) { i_error("pread(%s) failed: %m", uidlist->path); return -1; } i_error("Corrupted %s: File too small", uidlist->path); return 0; } uidlist->data = NULL; uidlist->data_size = 0; } if (uidlist->file_cache == NULL && (uidlist->trie->flags & SQUAT_INDEX_FLAG_MMAP_DISABLE) != 0) uidlist->file_cache = file_cache_new_path(uidlist->fd, uidlist->path); return squat_uidlist_map_header(uidlist); } static int squat_uidlist_read_to_memory(struct squat_uidlist *uidlist) { size_t i, page_size = mmap_get_page_size(); if (uidlist->file_cache != NULL) { return uidlist_file_cache_read(uidlist, 0, uidlist->hdr.used_file_size); } /* Tell the kernel we're going to use the uidlist data, so it loads it into memory and keeps it there. */ (void)madvise(uidlist->mmap_base, uidlist->mmap_size, MADV_WILLNEED); /* It also speeds up a bit for us to sequentially load everything into memory, although at least Linux catches up quite fast even without this code. Compiler can quite easily optimize away this entire for loop, but volatile seems to help with gcc 4.2. */ for (i = 0; i < uidlist->mmap_size; i += page_size) ((const volatile char *)uidlist->data)[i]; return 0; } static void squat_uidlist_free_from_memory(struct squat_uidlist *uidlist) { size_t page_size = mmap_get_page_size(); if (uidlist->file_cache != NULL) { file_cache_invalidate(uidlist->file_cache, page_size, (uoff_t)-1); } else { (void)madvise(uidlist->mmap_base, uidlist->mmap_size, MADV_DONTNEED); } } struct squat_uidlist *squat_uidlist_init(struct squat_trie *trie) { struct squat_uidlist *uidlist; uidlist = i_new(struct squat_uidlist, 1); uidlist->trie = trie; uidlist->path = i_strconcat(trie->path, ".uids", NULL); uidlist->fd = -1; return uidlist; } void squat_uidlist_deinit(struct squat_uidlist *uidlist) { squat_uidlist_close(uidlist); i_free(uidlist->path); i_free(uidlist); } static int squat_uidlist_open(struct squat_uidlist *uidlist) { squat_uidlist_close(uidlist); uidlist->fd = open(uidlist->path, O_RDWR); if (uidlist->fd == -1) { if (errno == ENOENT) { i_zero(&uidlist->hdr); return 0; } i_error("open(%s) failed: %m", uidlist->path); return -1; } return squat_uidlist_map(uidlist) <= 0 ? -1 : 0; } static void squat_uidlist_close(struct squat_uidlist *uidlist) { i_assert(!uidlist->building); squat_uidlist_unmap(uidlist); if (uidlist->file_cache != NULL) file_cache_free(&uidlist->file_cache); if (uidlist->file_lock != NULL) file_lock_free(&uidlist->file_lock); if (uidlist->dotlock != NULL) file_dotlock_delete(&uidlist->dotlock); if (uidlist->fd != -1) { if (close(uidlist->fd) < 0) i_error("close(%s) failed: %m", uidlist->path); uidlist->fd = -1; } uidlist->corrupted = FALSE; } int squat_uidlist_refresh(struct squat_uidlist *uidlist) { /* we assume here that trie is locked, so that we don't need to worry about it when reading the header */ if (uidlist->fd == -1 || uidlist->hdr.indexid != uidlist->trie->hdr.indexid) { if (squat_uidlist_open(uidlist) < 0) return -1; } else { if (squat_uidlist_map(uidlist) <= 0) return -1; } return 0; } static int squat_uidlist_is_file_stale(struct squat_uidlist *uidlist) { struct stat st, st2; i_assert(uidlist->fd != -1); if (stat(uidlist->path, &st) < 0) { if (errno == ENOENT) return 1; i_error("stat(%s) failed: %m", uidlist->path); return -1; } if (fstat(uidlist->fd, &st2) < 0) { i_error("fstat(%s) failed: %m", uidlist->path); return -1; } uidlist->locked_file_size = st2.st_size; return st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev) ? 0 : 1; } static int squat_uidlist_lock(struct squat_uidlist *uidlist) { int ret; for (;;) { i_assert(uidlist->fd != -1); i_assert(uidlist->file_lock == NULL); i_assert(uidlist->dotlock == NULL); if (uidlist->trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { ret = file_wait_lock(uidlist->fd, uidlist->path, F_WRLCK, uidlist->trie->lock_method, SQUAT_TRIE_LOCK_TIMEOUT, &uidlist->file_lock); } else { ret = file_dotlock_create(&uidlist->trie->dotlock_set, uidlist->path, 0, &uidlist->dotlock); } if (ret == 0) { i_error("squat uidlist %s: Locking timed out", uidlist->path); return 0; } if (ret < 0) return -1; ret = squat_uidlist_is_file_stale(uidlist); if (ret == 0) break; if (uidlist->file_lock != NULL) file_unlock(&uidlist->file_lock); else file_dotlock_delete(&uidlist->dotlock); if (ret < 0) return -1; squat_uidlist_close(uidlist); uidlist->fd = squat_trie_create_fd(uidlist->trie, uidlist->path, 0); if (uidlist->fd == -1) return -1; } return 1; } static int squat_uidlist_open_or_create(struct squat_uidlist *uidlist) { int ret; if (uidlist->fd == -1) { uidlist->fd = squat_trie_create_fd(uidlist->trie, uidlist->path, 0); if (uidlist->fd == -1) return -1; } if (squat_uidlist_lock(uidlist) <= 0) return -1; if (uidlist->locked_file_size != 0) { if ((ret = squat_uidlist_map(uidlist)) < 0) return -1; if (ret == 0) { /* broken file, truncate */ if (ftruncate(uidlist->fd, 0) < 0) { i_error("ftruncate(%s) failed: %m", uidlist->path); return -1; } uidlist->locked_file_size = 0; } } if (uidlist->locked_file_size == 0) { /* write using 0 until we're finished */ i_zero(&uidlist->hdr); if (write_full(uidlist->fd, &uidlist->hdr, sizeof(uidlist->hdr)) < 0) { i_error("write(%s) failed: %m", uidlist->path); return -1; } } return 0; } int squat_uidlist_build_init(struct squat_uidlist *uidlist, struct squat_uidlist_build_context **ctx_r) { struct squat_uidlist_build_context *ctx; int ret; i_assert(!uidlist->building); ret = squat_uidlist_open_or_create(uidlist); if (ret == 0 && lseek(uidlist->fd, uidlist->hdr.used_file_size, SEEK_SET) < 0) { i_error("lseek(%s) failed: %m", uidlist->path); ret = -1; } if (ret < 0) { if (uidlist->file_lock != NULL) file_unlock(&uidlist->file_lock); if (uidlist->dotlock != NULL) file_dotlock_delete(&uidlist->dotlock); return -1; } ctx = i_new(struct squat_uidlist_build_context, 1); ctx->uidlist = uidlist; ctx->output = o_stream_create_fd(uidlist->fd, 0, FALSE); if (ctx->output->offset == 0) { struct squat_uidlist_file_header hdr; i_zero(&hdr); o_stream_nsend(ctx->output, &hdr, sizeof(hdr)); } o_stream_cork(ctx->output); i_array_init(&ctx->lists, 10240); i_array_init(&ctx->block_offsets, 128); i_array_init(&ctx->block_end_indexes, 128); ctx->list_start_idx = uidlist->hdr.count; ctx->build_hdr = uidlist->hdr; uidlist->building = TRUE; *ctx_r = ctx; return 0; } static void uidlist_write_block_list_and_header(struct squat_uidlist_build_context *ctx, struct ostream *output, ARRAY_TYPE(uint32_t) *block_offsets, ARRAY_TYPE(uint32_t) *block_end_indexes, bool write_old_blocks) { struct squat_uidlist *uidlist = ctx->uidlist; unsigned int align, old_block_count, new_block_count; uint32_t block_offset_count; uoff_t block_list_offset; i_assert(uidlist->trie->hdr.indexid != 0); ctx->build_hdr.indexid = uidlist->trie->hdr.indexid; if (array_count(block_end_indexes) == 0) { ctx->build_hdr.used_file_size = output->offset; ctx->build_hdr.block_list_offset = 0; uidlist->hdr = ctx->build_hdr; return; } align = output->offset % sizeof(uint32_t); if (align != 0) { static char null[sizeof(uint32_t)-1] = { 0, }; o_stream_nsend(output, null, sizeof(uint32_t) - align); } block_list_offset = output->offset; new_block_count = array_count(block_offsets); old_block_count = write_old_blocks ? uidlist->cur_block_count : 0; block_offset_count = new_block_count + old_block_count; o_stream_nsend(output, &block_offset_count, sizeof(block_offset_count)); /* write end indexes */ o_stream_nsend(output, uidlist->cur_block_end_indexes, old_block_count * sizeof(uint32_t)); o_stream_nsend(output, array_idx(block_end_indexes, 0), new_block_count * sizeof(uint32_t)); /* write offsets */ o_stream_nsend(output, uidlist->cur_block_offsets, old_block_count * sizeof(uint32_t)); o_stream_nsend(output, array_idx(block_offsets, 0), new_block_count * sizeof(uint32_t)); (void)o_stream_flush(output); /* update header - it's written later when trie is locked */ ctx->build_hdr.block_list_offset = block_list_offset; ctx->build_hdr.used_file_size = output->offset; uidlist->hdr = ctx->build_hdr; } void squat_uidlist_build_flush(struct squat_uidlist_build_context *ctx) { struct uidlist_list *lists; uint8_t buf[SQUAT_PACK_MAX_SIZE], *bufp; unsigned int i, j, count, max; uint32_t block_offset, block_end_idx, start_offset; uint32_t list_sizes[UIDLIST_BLOCK_LIST_COUNT]; size_t mem_size; if (ctx->uidlist->corrupted) return; lists = array_get_modifiable(&ctx->lists, &count); if (count == 0) return; /* write the lists and save the written sizes to uid_list[0] */ for (i = 0; i < count; i += UIDLIST_BLOCK_LIST_COUNT) { start_offset = ctx->output->offset; max = I_MIN(count - i, UIDLIST_BLOCK_LIST_COUNT); for (j = 0; j < max; j++) { if (uidlist_write(ctx->output, &lists[i+j], FALSE, &list_sizes[j]) < 0) { squat_uidlist_set_corrupted(ctx->uidlist, "Broken uidlists"); return; } } block_offset = ctx->output->offset; block_end_idx = ctx->list_start_idx + i + max; array_append(&ctx->block_offsets, &block_offset, 1); array_append(&ctx->block_end_indexes, &block_end_idx, 1); /* write the full size of the uidlists */ bufp = buf; squat_pack_num(&bufp, block_offset - start_offset); o_stream_nsend(ctx->output, buf, bufp - buf); /* write the sizes/flags */ for (j = 0; j < max; j++) { bufp = buf; squat_pack_num(&bufp, list_sizes[j]); o_stream_nsend(ctx->output, buf, bufp - buf); } } mem_size = ctx->lists.arr.buffer->used + ctx->block_offsets.arr.buffer->used + ctx->block_end_indexes.arr.buffer->used; if (ctx->uidlist->max_size < mem_size) ctx->uidlist->max_size = mem_size; ctx->list_start_idx += count; array_clear(&ctx->lists); uidlist_write_block_list_and_header(ctx, ctx->output, &ctx->block_offsets, &ctx->block_end_indexes, TRUE); (void)squat_uidlist_map(ctx->uidlist); array_clear(&ctx->block_offsets); array_clear(&ctx->block_end_indexes); } int squat_uidlist_build_finish(struct squat_uidlist_build_context *ctx) { if (ctx->uidlist->corrupted) return -1; if (!ctx->output->closed) { (void)o_stream_seek(ctx->output, 0); o_stream_nsend(ctx->output, &ctx->build_hdr, sizeof(ctx->build_hdr)); (void)o_stream_seek(ctx->output, ctx->build_hdr.used_file_size); } if (o_stream_nfinish(ctx->output) < 0) { i_error("write() to %s failed: %s", ctx->uidlist->path, o_stream_get_error(ctx->output)); return -1; } return 0; } void squat_uidlist_build_deinit(struct squat_uidlist_build_context **_ctx) { struct squat_uidlist_build_context *ctx = *_ctx; *_ctx = NULL; i_assert(array_count(&ctx->lists) == 0 || ctx->uidlist->corrupted); i_assert(ctx->uidlist->building); ctx->uidlist->building = FALSE; if (ctx->uidlist->file_lock != NULL) file_unlock(&ctx->uidlist->file_lock); else file_dotlock_delete(&ctx->uidlist->dotlock); if (ctx->need_reopen) (void)squat_uidlist_open(ctx->uidlist); array_free(&ctx->block_offsets); array_free(&ctx->block_end_indexes); array_free(&ctx->lists); o_stream_ignore_last_errors(ctx->output); o_stream_unref(&ctx->output); i_free(ctx); } int squat_uidlist_rebuild_init(struct squat_uidlist_build_context *build_ctx, bool compress, struct squat_uidlist_rebuild_context **ctx_r) { struct squat_uidlist_rebuild_context *ctx; struct squat_uidlist_file_header hdr; const char *temp_path; int fd; if (build_ctx->build_hdr.link_count == 0) return 0; if (!compress) { if (build_ctx->build_hdr.link_count < build_ctx->build_hdr.count*2/3) return 0; } /* make sure the entire uidlist is in memory before beginning, otherwise the pages are faulted to memory in random order which takes forever. */ if (squat_uidlist_read_to_memory(build_ctx->uidlist) < 0) return -1; temp_path = t_strconcat(build_ctx->uidlist->path, ".tmp", NULL); fd = squat_trie_create_fd(build_ctx->uidlist->trie, temp_path, O_TRUNC); if (fd == -1) return -1; ctx = i_new(struct squat_uidlist_rebuild_context, 1); ctx->uidlist = build_ctx->uidlist; ctx->build_ctx = build_ctx; ctx->fd = fd; ctx->output = o_stream_create_fd(ctx->fd, 0, FALSE); ctx->next_uid_list_idx = 0x100; o_stream_cork(ctx->output); i_zero(&hdr); o_stream_nsend(ctx->output, &hdr, sizeof(hdr)); ctx->cur_block_start_offset = ctx->output->offset; i_array_init(&ctx->new_block_offsets, build_ctx->build_hdr.count / UIDLIST_BLOCK_LIST_COUNT); i_array_init(&ctx->new_block_end_indexes, build_ctx->build_hdr.count / UIDLIST_BLOCK_LIST_COUNT); *ctx_r = ctx; return 1; } static void uidlist_rebuild_flush_block(struct squat_uidlist_rebuild_context *ctx) { uint8_t buf[SQUAT_PACK_MAX_SIZE], *bufp; uint32_t block_offset, block_end_idx; unsigned int i; ctx->new_count += ctx->list_idx; block_offset = ctx->output->offset; block_end_idx = ctx->new_count; array_append(&ctx->new_block_offsets, &block_offset, 1); array_append(&ctx->new_block_end_indexes, &block_end_idx, 1); /* this block's contents started from cur_block_start_offset and ended to current offset. write the size of this area. */ bufp = buf; squat_pack_num(&bufp, block_offset - ctx->cur_block_start_offset); o_stream_nsend(ctx->output, buf, bufp - buf); /* write the sizes/flags */ for (i = 0; i < ctx->list_idx; i++) { bufp = buf; squat_pack_num(&bufp, ctx->list_sizes[i]); o_stream_nsend(ctx->output, buf, bufp - buf); } ctx->cur_block_start_offset = ctx->output->offset; } uint32_t squat_uidlist_rebuild_next(struct squat_uidlist_rebuild_context *ctx, const ARRAY_TYPE(uint32_t) *uids) { int ret; T_BEGIN { ret = uidlist_write_array(ctx->output, array_idx(uids, 0), array_count(uids), 0, 0, FALSE, &ctx->list_sizes[ctx->list_idx]); } T_END; if (ret < 0) squat_uidlist_set_corrupted(ctx->uidlist, "Broken uidlists"); if (++ctx->list_idx == UIDLIST_BLOCK_LIST_COUNT) { uidlist_rebuild_flush_block(ctx); ctx->list_idx = 0; } return ctx->next_uid_list_idx++ << 1; } uint32_t squat_uidlist_rebuild_nextu(struct squat_uidlist_rebuild_context *ctx, const ARRAY_TYPE(seq_range) *uids) { const struct seq_range *range; ARRAY_TYPE(uint32_t) tmp_uids; uint32_t seq, uid1, ret; unsigned int i, count; range = array_get(uids, &count); if (count == 0) return 0; if (range[count-1].seq2 < 8) { /* we can use a singleton bitmask */ ret = 0; for (i = 0; i < count; i++) { for (seq = range[i].seq1; seq <= range[i].seq2; seq++) ret |= 1 << (seq+1); } return ret; } if (count == 1 && range[0].seq1 == range[0].seq2) { /* single UID */ return (range[0].seq1 << 1) | 1; } /* convert seq range to our internal representation and use the normal _rebuild_next() to write it */ i_array_init(&tmp_uids, 128); for (i = 0; i < count; i++) { if (range[i].seq1 == range[i].seq2) array_append(&tmp_uids, &range[i].seq1, 1); else { uid1 = range[i].seq1 | UID_LIST_MASK_RANGE; array_append(&tmp_uids, &uid1, 1); array_append(&tmp_uids, &range[i].seq2, 1); } } ret = squat_uidlist_rebuild_next(ctx, &tmp_uids); array_free(&tmp_uids); return ret; } int squat_uidlist_rebuild_finish(struct squat_uidlist_rebuild_context *ctx, bool cancel) { const char *temp_path; int ret = 1; if (ctx->list_idx != 0) uidlist_rebuild_flush_block(ctx); if (cancel || ctx->uidlist->corrupted) ret = 0; temp_path = t_strconcat(ctx->uidlist->path, ".tmp", NULL); if (ret > 0) { ctx->build_ctx->build_hdr.indexid = ctx->uidlist->trie->hdr.indexid; ctx->build_ctx->build_hdr.count = ctx->new_count; ctx->build_ctx->build_hdr.link_count = 0; uidlist_write_block_list_and_header(ctx->build_ctx, ctx->output, &ctx->new_block_offsets, &ctx->new_block_end_indexes, FALSE); (void)o_stream_seek(ctx->output, 0); o_stream_nsend(ctx->output, &ctx->build_ctx->build_hdr, sizeof(ctx->build_ctx->build_hdr)); (void)o_stream_seek(ctx->output, ctx->build_ctx->build_hdr.used_file_size); if (ctx->uidlist->corrupted) ret = -1; else if (o_stream_nfinish(ctx->output) < 0) { i_error("write(%s) failed: %s", temp_path, o_stream_get_error(ctx->output)); ret = -1; } else if (rename(temp_path, ctx->uidlist->path) < 0) { i_error("rename(%s, %s) failed: %m", temp_path, ctx->uidlist->path); ret = -1; } ctx->build_ctx->need_reopen = TRUE; } /* we no longer require the entire uidlist to be in memory, let it be used for something more useful. */ squat_uidlist_free_from_memory(ctx->uidlist); o_stream_ignore_last_errors(ctx->output); o_stream_unref(&ctx->output); if (close(ctx->fd) < 0) i_error("close(%s) failed: %m", temp_path); if (ret <= 0) i_unlink(temp_path); array_free(&ctx->new_block_offsets); array_free(&ctx->new_block_end_indexes); i_free(ctx); return ret < 0 ? -1 : 0; } static void uidlist_flush(struct squat_uidlist_build_context *ctx, struct uidlist_list *list, uint32_t uid) { uint32_t size, offset = ctx->output->offset; ctx->build_hdr.link_count++; if (uidlist_write(ctx->output, list, TRUE, &size) < 0) squat_uidlist_set_corrupted(ctx->uidlist, "Broken uidlists"); list->uid_count = 2; list->uid_begins_with_pointer = TRUE; list->uid_list[0] = offset; list->uid_list[1] = uid; } static struct uidlist_list * uidlist_add_new(struct squat_uidlist_build_context *ctx, unsigned int count, uint32_t *uid_list_idx_r) { struct uidlist_list *list; i_assert(array_count(&ctx->lists) + ctx->list_start_idx == ctx->build_hdr.count); *uid_list_idx_r = (ctx->build_hdr.count + 0x100) << 1; list = array_append_space(&ctx->lists); ctx->build_hdr.count++; list->uid_count = count; return list; } uint32_t squat_uidlist_build_add_uid(struct squat_uidlist_build_context *ctx, uint32_t uid_list_idx, uint32_t uid) { struct uidlist_list *list; unsigned int idx, mask; uint32_t *p; if ((uid_list_idx & 1) != 0) { /* adding second UID */ uint32_t prev_uid = uid_list_idx >> 1; i_assert(prev_uid != uid); list = uidlist_add_new(ctx, 2, &uid_list_idx); list->uid_list[0] = prev_uid; if (prev_uid + 1 == uid) list->uid_list[0] |= UID_LIST_MASK_RANGE; list->uid_list[1] = uid; return uid_list_idx; } else if (uid_list_idx < (0x100 << 1)) { uint32_t old_list_idx; if (uid < 8) { /* UID lists containing only UIDs 0-7 are saved as uidlist values 2..511. think of it as a bitmask. */ uid_list_idx |= 1 << (uid + 1); i_assert((uid_list_idx & 1) == 0); return uid_list_idx; } if (uid_list_idx == 0) { /* first UID */ return (uid << 1) | 1; } /* create a new list */ old_list_idx = uid_list_idx >> 1; list = uidlist_add_new(ctx, 1, &uid_list_idx); /* add the first UID ourself */ idx = 0; i_assert((old_list_idx & 0xff) != 0); for (mask = 1; mask <= 128; mask <<= 1, idx++) { if ((old_list_idx & mask) != 0) { list->uid_list[0] = idx; idx++; mask <<= 1; break; } } for (; mask <= 128; mask <<= 1, idx++) { if ((old_list_idx & mask) != 0) { (void)squat_uidlist_build_add_uid(ctx, uid_list_idx, idx); } } } /* add to existing list */ idx = (uid_list_idx >> 1) - 0x100; if (idx < ctx->list_start_idx) { list = uidlist_add_new(ctx, 2, &uid_list_idx); list->uid_list[0] = UID_LIST_POINTER_MASK_LIST_IDX | idx; list->uid_list[1] = uid; list->uid_begins_with_pointer = TRUE; ctx->build_hdr.link_count++; return uid_list_idx; } idx -= ctx->list_start_idx; if (idx >= array_count(&ctx->lists)) { squat_uidlist_set_corrupted(ctx->uidlist, "missing/broken uidlist"); return 0; } list = array_idx_modifiable(&ctx->lists, idx); i_assert(list->uid_count > 0); p = &list->uid_list[list->uid_count-1]; i_assert(uid != *p || ctx->uidlist->corrupted || (list->uid_count == 1 && list->uid_begins_with_pointer)); if (uid == *p + 1 && (list->uid_count > 1 || !list->uid_begins_with_pointer)) { /* use a range */ if (list->uid_count > 1 && (p[-1] & UID_LIST_MASK_RANGE) != 0 && (list->uid_count > 2 || !list->uid_begins_with_pointer)) { /* increase the existing range */ *p += 1; return uid_list_idx; } if (list->uid_count == UIDLIST_LIST_SIZE) { uidlist_flush(ctx, list, uid); return uid_list_idx; } /* create a new range */ *p |= UID_LIST_MASK_RANGE; } else { if (list->uid_count == UIDLIST_LIST_SIZE) { uidlist_flush(ctx, list, uid); return uid_list_idx; } } p++; list->uid_count++; *p = uid; return uid_list_idx; } static void uidlist_array_append(ARRAY_TYPE(uint32_t) *uids, uint32_t uid) { uint32_t *uidlist; unsigned int count; uidlist = array_get_modifiable(uids, &count); if (count == 0) { array_append(uids, &uid, 1); return; } if (uidlist[count-1] + 1 == uid) { if (count > 1 && (uidlist[count-2] & UID_LIST_MASK_RANGE) != 0) { uidlist[count-1]++; return; } uidlist[count-1] |= UID_LIST_MASK_RANGE; } array_append(uids, &uid, 1); } static void uidlist_array_append_range(ARRAY_TYPE(uint32_t) *uids, uint32_t uid1, uint32_t uid2) { uint32_t *uidlist; unsigned int count; i_assert(uid1 < uid2); uidlist = array_get_modifiable(uids, &count); if (count == 0) { uid1 |= UID_LIST_MASK_RANGE; array_append(uids, &uid1, 1); array_append(uids, &uid2, 1); return; } if (uidlist[count-1] + 1 == uid1) { if (count > 1 && (uidlist[count-2] & UID_LIST_MASK_RANGE) != 0) { uidlist[count-1] = uid2; return; } uidlist[count-1] |= UID_LIST_MASK_RANGE; } else { uid1 |= UID_LIST_MASK_RANGE; array_append(uids, &uid1, 1); } array_append(uids, &uid2, 1); } static int squat_uidlist_get_at_offset(struct squat_uidlist *uidlist, uoff_t offset, uint32_t num, ARRAY_TYPE(uint32_t) *uids) { const uint32_t *uid_list; const uint8_t *p, *end; uint32_t size, base_uid, next_uid, flags, prev; uoff_t uidlist_data_offset; unsigned int i, j, count; if (num != 0) uidlist_data_offset = offset; else { /* not given, read it */ if (uidlist_file_cache_read(uidlist, offset, SQUAT_PACK_MAX_SIZE) < 0) return -1; p = CONST_PTR_OFFSET(uidlist->data, offset); end = CONST_PTR_OFFSET(uidlist->data, uidlist->data_size); num = squat_unpack_num(&p, end); uidlist_data_offset = p - (const uint8_t *)uidlist->data; } size = num >> 2; if (uidlist_file_cache_read(uidlist, uidlist_data_offset, size) < 0) return -1; if (uidlist_data_offset + size > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "size points outside file"); return -1; } p = CONST_PTR_OFFSET(uidlist->data, uidlist_data_offset); end = p + size; flags = num; if ((flags & UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER) != 0) { /* link to the file */ prev = squat_unpack_num(&p, end); if ((prev & 1) != 0) { /* pointer to uidlist */ prev = ((prev >> 1) + 0x100) << 1; if (squat_uidlist_get(uidlist, prev, uids) < 0) return -1; } else { prev = offset - (prev >> 1); if (squat_uidlist_get_at_offset(uidlist, prev, 0, uids) < 0) return -1; } uid_list = array_get(uids, &count); next_uid = count == 0 ? 0 : uid_list[count-1] + 1; } else { next_uid = 0; } num = base_uid = squat_unpack_num(&p, end); if ((flags & UIDLIST_PACKED_FLAG_BITMASK) == 0) base_uid >>= 1; if (base_uid < next_uid) { squat_uidlist_set_corrupted(uidlist, "broken continued uidlist"); return -1; } if ((flags & UIDLIST_PACKED_FLAG_BITMASK) != 0) { /* bitmask */ size = end - p; uidlist_array_append(uids, base_uid++); for (i = 0; i < size; i++) { for (j = 0; j < 8; j++, base_uid++) { if ((p[i] & (1 << j)) != 0) uidlist_array_append(uids, base_uid); } } } else { /* range */ for (;;) { if ((num & 1) == 0) { uidlist_array_append(uids, base_uid); } else { /* range */ uint32_t seq1 = base_uid; base_uid += squat_unpack_num(&p, end) + 1; uidlist_array_append_range(uids, seq1, base_uid); } if (p == end) break; num = squat_unpack_num(&p, end); base_uid += (num >> 1) + 1; } } return 0; } static int squat_uidlist_get_offset(struct squat_uidlist *uidlist, uint32_t uid_list_idx, uint32_t *offset_r, uint32_t *num_r) { const uint8_t *p, *end; unsigned int idx; uint32_t num, skip_bytes, uidlists_offset; size_t max_map_size; if (uidlist->fd == -1) { squat_uidlist_set_corrupted(uidlist, "no uidlists"); return -1; } if (bsearch_insert_pos(&uid_list_idx, uidlist->cur_block_end_indexes, uidlist->cur_block_count, sizeof(uint32_t), uint32_cmp, &idx)) idx++; if (unlikely(idx == uidlist->cur_block_count)) { squat_uidlist_set_corrupted(uidlist, "uidlist not found"); return -1; } i_assert(uidlist->cur_block_end_indexes != NULL); if (unlikely(idx > 0 && uidlist->cur_block_end_indexes[idx-1] > uid_list_idx)) { squat_uidlist_set_corrupted(uidlist, "broken block list"); return -1; } /* make sure everything is mapped */ uid_list_idx -= idx == 0 ? 0 : uidlist->cur_block_end_indexes[idx-1]; max_map_size = SQUAT_PACK_MAX_SIZE * (1+uid_list_idx); if (uidlist_file_cache_read(uidlist, uidlist->cur_block_offsets[idx], max_map_size) < 0) return -1; /* find the uidlist inside the block */ i_assert(uidlist->cur_block_offsets != NULL); p = CONST_PTR_OFFSET(uidlist->data, uidlist->cur_block_offsets[idx]); end = CONST_PTR_OFFSET(uidlist->data, uidlist->data_size); uidlists_offset = uidlist->cur_block_offsets[idx] - squat_unpack_num(&p, end); for (skip_bytes = 0; uid_list_idx > 0; uid_list_idx--) { num = squat_unpack_num(&p, end); skip_bytes += num >> 2; } *offset_r = uidlists_offset + skip_bytes; *num_r = squat_unpack_num(&p, end); if (unlikely(p == end)) { squat_uidlist_set_corrupted(uidlist, "broken file"); return -1; } if (unlikely(*offset_r > uidlist->mmap_size && uidlist->mmap_base != NULL)) { squat_uidlist_set_corrupted(uidlist, "broken offset"); return -1; } return 0; } int squat_uidlist_get(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(uint32_t) *uids) { unsigned int mask; uint32_t uid, offset, num; if ((uid_list_idx & 1) != 0) { /* single UID */ uid = uid_list_idx >> 1; uidlist_array_append(uids, uid); return 0; } else if (uid_list_idx < (0x100 << 1)) { /* bitmask */ for (uid = 0, mask = 2; mask <= 256; mask <<= 1, uid++) { if ((uid_list_idx & mask) != 0) uidlist_array_append(uids, uid); } return 0; } uid_list_idx = (uid_list_idx >> 1) - 0x100; if (squat_uidlist_get_offset(uidlist, uid_list_idx, &offset, &num) < 0) return -1; return squat_uidlist_get_at_offset(uidlist, offset, num, uids); } uint32_t squat_uidlist_singleton_last_uid(uint32_t uid_list_idx) { unsigned int idx, mask; if ((uid_list_idx & 1) != 0) { /* single UID */ return uid_list_idx >> 1; } else if (uid_list_idx < (0x100 << 1)) { /* bitmask */ if (uid_list_idx == 2) { /* just a quick optimization */ return 0; } for (idx = 7, mask = 256; mask > 2; mask >>= 1, idx--) { if ((uid_list_idx & mask) != 0) return idx; } } i_unreached(); return 0; } int squat_uidlist_get_seqrange(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(seq_range) *seq_range_arr) { ARRAY_TYPE(uint32_t) tmp_uid_arr; struct seq_range range; const uint32_t *tmp_uids; unsigned int i, count; int ret; i_array_init(&tmp_uid_arr, 128); ret = squat_uidlist_get(uidlist, uid_list_idx, &tmp_uid_arr); if (ret == 0) { tmp_uids = array_get(&tmp_uid_arr, &count); for (i = 0; i < count; i++) { if ((tmp_uids[i] & UID_LIST_MASK_RANGE) == 0) range.seq1 = range.seq2 = tmp_uids[i]; else { range.seq1 = tmp_uids[i] & ~UID_LIST_MASK_RANGE; range.seq2 = tmp_uids[++i]; } array_append(seq_range_arr, &range, 1); } } array_free(&tmp_uid_arr); return ret; } int squat_uidlist_filter(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(seq_range) *uids) { const struct seq_range *parent_range; ARRAY_TYPE(seq_range) dest_uids; ARRAY_TYPE(uint32_t) relative_uids; const uint32_t *rel_range; unsigned int i, rel_count, parent_idx, parent_count, diff, parent_uid; uint32_t prev_seq, seq1, seq2; int ret = 0; parent_range = array_get(uids, &parent_count); if (parent_count == 0) return 0; i_array_init(&relative_uids, 128); i_array_init(&dest_uids, 128); if (squat_uidlist_get(uidlist, uid_list_idx, &relative_uids) < 0) ret = -1; parent_idx = 0; rel_range = array_get(&relative_uids, &rel_count); prev_seq = 0; parent_uid = parent_range[0].seq1; for (i = 0; i < rel_count; i++) { if (unlikely(parent_uid == (uint32_t)-1)) { i_error("broken UID ranges"); ret = -1; break; } if ((rel_range[i] & UID_LIST_MASK_RANGE) == 0) seq1 = seq2 = rel_range[i]; else { seq1 = (rel_range[i] & ~UID_LIST_MASK_RANGE); seq2 = rel_range[++i]; } i_assert(seq1 >= prev_seq); diff = seq1 - prev_seq; while (diff > 0) { if (unlikely(parent_uid == (uint32_t)-1)) { i_error("broken UID ranges"); ret = -1; break; } for (; parent_idx < parent_count; parent_idx++) { if (parent_range[parent_idx].seq2 <= parent_uid) continue; if (parent_uid < parent_range[parent_idx].seq1) parent_uid = parent_range[parent_idx].seq1; else parent_uid++; break; } diff--; } diff = seq2 - seq1 + 1; while (diff > 0) { if (unlikely(parent_uid == (uint32_t)-1)) { i_error("broken UID ranges"); ret = -1; break; } seq_range_array_add(&dest_uids, parent_uid); for (; parent_idx < parent_count; parent_idx++) { if (parent_range[parent_idx].seq2 <= parent_uid) continue; if (parent_uid < parent_range[parent_idx].seq1) parent_uid = parent_range[parent_idx].seq1; else parent_uid++; break; } diff--; } prev_seq = seq2 + 1; } buffer_set_used_size(uids->arr.buffer, 0); array_append_array(uids, &dest_uids); array_free(&relative_uids); array_free(&dest_uids); return ret; } size_t squat_uidlist_mem_used(struct squat_uidlist *uidlist, unsigned int *count_r) { *count_r = uidlist->hdr.count; return uidlist->max_size; } dovecot-2.2.33.2/src/plugins/fts-squat/squat-uidlist.h0000644000175000017500000000474513123174404017516 00000000000000#ifndef SQUAT_UIDLIST_H #define SQUAT_UIDLIST_H struct squat_trie; struct squat_uidlist_build_context; struct squat_uidlist_rebuild_context; struct squat_uidlist_file_header { uint32_t indexid; uint32_t used_file_size; uint32_t block_list_offset; uint32_t count, link_count; }; /* uidlist file: struct uidlist_header; // size includes both prev_offset and uidlist packed (size << 2) | packed_flags; // UIDLIST_PACKED_FLAG_* [packed prev_offset;] // If UIDLIST_PACKED_FLAG_BEGINS_WITH_OFFSET is set if (UIDLIST_PACKED_FLAG_BITMASK) { packed base_uid; // first UID in uidlist uint8_t bitmask[]; // first bit is base_uid+1 } else { // FIXME: packed range } */ #define UIDLIST_IS_SINGLETON(idx) \ (((idx) & 1) != 0 || (idx) < (0x100 << 1)) struct squat_uidlist *squat_uidlist_init(struct squat_trie *trie); void squat_uidlist_deinit(struct squat_uidlist *uidlist); int squat_uidlist_refresh(struct squat_uidlist *uidlist); int squat_uidlist_build_init(struct squat_uidlist *uidlist, struct squat_uidlist_build_context **ctx_r); uint32_t squat_uidlist_build_add_uid(struct squat_uidlist_build_context *ctx, uint32_t uid_list_idx, uint32_t uid); void squat_uidlist_build_flush(struct squat_uidlist_build_context *ctx); int squat_uidlist_build_finish(struct squat_uidlist_build_context *ctx); void squat_uidlist_build_deinit(struct squat_uidlist_build_context **ctx); int squat_uidlist_rebuild_init(struct squat_uidlist_build_context *build_ctx, bool compress, struct squat_uidlist_rebuild_context **ctx_r); uint32_t squat_uidlist_rebuild_next(struct squat_uidlist_rebuild_context *ctx, const ARRAY_TYPE(uint32_t) *uids); uint32_t squat_uidlist_rebuild_nextu(struct squat_uidlist_rebuild_context *ctx, const ARRAY_TYPE(seq_range) *uids); int squat_uidlist_rebuild_finish(struct squat_uidlist_rebuild_context *ctx, bool cancel); int squat_uidlist_get(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(uint32_t) *uids); uint32_t squat_uidlist_singleton_last_uid(uint32_t uid_list_idx); int squat_uidlist_get_seqrange(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(seq_range) *seq_range_arr); int squat_uidlist_filter(struct squat_uidlist *uidlist, uint32_t uid_list_idx, ARRAY_TYPE(seq_range) *uids); void squat_uidlist_delete(struct squat_uidlist *uidlist); size_t squat_uidlist_mem_used(struct squat_uidlist *uidlist, unsigned int *count_r); #endif dovecot-2.2.33.2/src/plugins/fts-squat/Makefile.am0000644000175000017500000000163213123174404016561 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/fts NOPLUGIN_LDFLAGS = lib21_fts_squat_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib21_fts_squat_plugin.la if DOVECOT_PLUGIN_DEPS lib21_fts_squat_plugin_la_LIBADD = \ ../fts/lib20_fts_plugin.la endif lib21_fts_squat_plugin_la_SOURCES = \ fts-squat-plugin.c \ fts-backend-squat.c \ squat-trie.c \ squat-uidlist.c noinst_HEADERS = \ fts-squat-plugin.h \ squat-trie.h \ squat-trie-private.h \ squat-uidlist.h noinst_PROGRAMS = squat-test squat_test_SOURCES = \ squat-test.c common_objects = \ squat-trie.lo \ squat-uidlist.lo squat_test_LDADD = \ $(common_objects) \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) squat_test_DEPENDENCIES = \ $(common_objects) \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) dovecot-2.2.33.2/src/plugins/listescape/0002755000175000017500000000000013172375613015023 500000000000000dovecot-2.2.33.2/src/plugins/listescape/Makefile.in0000644000175000017500000005524313172375574017025 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/listescape ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_listescape_plugin_la_LIBADD = am_lib20_listescape_plugin_la_OBJECTS = listescape-plugin.lo lib20_listescape_plugin_la_OBJECTS = \ $(am_lib20_listescape_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_listescape_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_listescape_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_listescape_plugin_la_SOURCES) DIST_SOURCES = $(lib20_listescape_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib20_listescape_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_listescape_plugin.la lib20_listescape_plugin_la_SOURCES = \ listescape-plugin.c noinst_HEADERS = \ listescape-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/listescape/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/listescape/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_listescape_plugin.la: $(lib20_listescape_plugin_la_OBJECTS) $(lib20_listescape_plugin_la_DEPENDENCIES) $(EXTRA_lib20_listescape_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_listescape_plugin_la_LINK) -rpath $(moduledir) $(lib20_listescape_plugin_la_OBJECTS) $(lib20_listescape_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listescape-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/listescape/listescape-plugin.c0000644000175000017500000000164313123174404020530 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage-hooks.h" #include "mailbox-list-private.h" #include "listescape-plugin.h" #define DEFAULT_ESCAPE_CHAR '\\' const char *listescape_plugin_version = DOVECOT_ABI_VERSION; static void listescape_mailbox_list_created(struct mailbox_list *list) { const char *env; if (list->set.escape_char == '\0') { env = mail_user_plugin_getenv(list->ns->user, "listescape_char"); list->set.escape_char = env != NULL && *env != '\0' ? env[0] : DEFAULT_ESCAPE_CHAR; } } static struct mail_storage_hooks listescape_mail_storage_hooks = { .mailbox_list_created = listescape_mailbox_list_created }; void listescape_plugin_init(struct module *module) { mail_storage_hooks_add(module, &listescape_mail_storage_hooks); } void listescape_plugin_deinit(void) { mail_storage_hooks_remove(&listescape_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/listescape/listescape-plugin.h0000644000175000017500000000023213123174404020526 00000000000000#ifndef LISTESCAPE_PLUGIN_H #define LISTESCAPE_PLUGIN_H void listescape_plugin_init(struct module *module); void listescape_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/listescape/Makefile.am0000644000175000017500000000062713123174404016771 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib20_listescape_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_listescape_plugin.la lib20_listescape_plugin_la_SOURCES = \ listescape-plugin.c noinst_HEADERS = \ listescape-plugin.h dovecot-2.2.33.2/src/plugins/mail-filter/0002755000175000017500000000000013172375613015074 500000000000000dovecot-2.2.33.2/src/plugins/mail-filter/Makefile.in0000644000175000017500000005576113172375574017103 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/mail-filter ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib10_mail_filter_plugin_la_LIBADD = am_lib10_mail_filter_plugin_la_OBJECTS = mail-filter-plugin.lo \ istream-ext-filter.lo ostream-ext-filter.lo lib10_mail_filter_plugin_la_OBJECTS = \ $(am_lib10_mail_filter_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib10_mail_filter_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib10_mail_filter_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib10_mail_filter_plugin_la_SOURCES) DIST_SOURCES = $(lib10_mail_filter_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib10_mail_filter_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_mail_filter_plugin.la lib10_mail_filter_plugin_la_SOURCES = \ mail-filter-plugin.c \ istream-ext-filter.c \ ostream-ext-filter.c noinst_HEADERS = \ istream-ext-filter.h \ ostream-ext-filter.h \ mail-filter-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/mail-filter/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/mail-filter/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib10_mail_filter_plugin.la: $(lib10_mail_filter_plugin_la_OBJECTS) $(lib10_mail_filter_plugin_la_DEPENDENCIES) $(EXTRA_lib10_mail_filter_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_mail_filter_plugin_la_LINK) -rpath $(moduledir) $(lib10_mail_filter_plugin_la_OBJECTS) $(lib10_mail_filter_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-ext-filter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-filter-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-ext-filter.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/mail-filter/mail-filter-plugin.h0000644000175000017500000000023613123174404020654 00000000000000#ifndef MAIL_FILTER_PLUGIN_H #define MAIL_FILTER_PLUGIN_H void mail_filter_plugin_init(struct module *module); void mail_filter_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/mail-filter/istream-ext-filter.h0000644000175000017500000000027213123174404020700 00000000000000#ifndef ISTREAM_MAIL_FILTER_H #define ISTREAM_MAIL_FILTER_H struct istream * i_stream_create_ext_filter(struct istream *input, const char *socket_path, const char *args); #endif dovecot-2.2.33.2/src/plugins/mail-filter/mail-filter-plugin.c0000644000175000017500000001404313123174404020650 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "safe-mkstemp.h" #include "mail-user.h" #include "mail-storage-private.h" #include "istream.h" #include "istream-seekable.h" #include "istream-ext-filter.h" #include "ostream-ext-filter.h" #include "mail-filter-plugin.h" /* After buffer grows larger than this, create a temporary file to /tmp where to read the mail. */ #define MAIL_MAX_MEMORY_BUFFER (1024*128) #define MAIL_FILTER_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_filter_mail_module) #define MAIL_FILTER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_filter_storage_module) #define MAIL_FILTER_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_filter_user_module) struct mail_filter_user { union mail_user_module_context module_ctx; const char *socket_path, *args; const char *out_socket_path, *out_args; }; const char *mail_filter_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(mail_filter_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(mail_filter_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(mail_filter_mail_module, &mail_module_register); static int mail_filter_mail_save_begin(struct mail_save_context *ctx, struct istream *input) { struct mailbox *box = ctx->transaction->box; struct mail_filter_user *muser = MAIL_FILTER_USER_CONTEXT(box->storage->user); union mailbox_module_context *mbox = MAIL_FILTER_CONTEXT(box); struct ostream *output; if (mbox->super.save_begin(ctx, input) < 0) return -1; output = o_stream_create_ext_filter(ctx->data.output, muser->out_socket_path, muser->out_args); ctx->data.output = output; return 0; } static int seekable_fd_callback(const char **path_r, void *context) { struct mail_user *user = context; string_t *path; int fd; path = t_str_new(128); mail_user_set_get_temp_prefix(path, user->set); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) { i_error("safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } /* we just want the fd, unlink it */ if (i_unlink(str_c(path)) < 0) { /* shouldn't happen.. */ i_close_fd(&fd); return -1; } *path_r = str_c(path); return fd; } static int mail_filter_istream_opened(struct mail *_mail, struct istream **stream) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_user *user = _mail->box->storage->user; struct mail_filter_user *muser = MAIL_FILTER_USER_CONTEXT(user); union mail_module_context *mmail = MAIL_FILTER_MAIL_CONTEXT(mail); struct istream *input, *inputs[2]; input = *stream; *stream = i_stream_create_ext_filter(input, muser->socket_path, muser->args); i_stream_unref(&input); inputs[0] = *stream; inputs[1] = NULL; *stream = i_stream_create_seekable(inputs, MAIL_MAX_MEMORY_BUFFER, seekable_fd_callback, user); i_stream_unref(&inputs[0]); return mmail->super.istream_opened(_mail, stream); } static void mail_filter_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct mail_filter_user *muser = MAIL_FILTER_USER_CONTEXT(box->storage->user); union mailbox_module_context *mbox; enum mail_storage_class_flags class_flags = box->storage->class_flags; mbox = p_new(box->pool, union mailbox_module_context, 1); mbox->super = *v; box->vlast = &mbox->super; MODULE_CONTEXT_SET_SELF(box, mail_filter_storage_module, mbox); if ((class_flags & MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0 && (class_flags & MAIL_STORAGE_CLASS_FLAG_BINARY_DATA) != 0 && muser->out_socket_path != NULL) v->save_begin = mail_filter_mail_save_begin; } static void mail_filter_mail_allocated(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_filter_user *muser = MAIL_FILTER_USER_CONTEXT(_mail->box->storage->user); struct mail_vfuncs *v = mail->vlast; union mail_module_context *mmail; mmail = p_new(mail->pool, union mail_module_context, 1); mmail->super = *v; mail->vlast = &mmail->super; if (muser->socket_path != NULL) v->istream_opened = mail_filter_istream_opened; MODULE_CONTEXT_SET_SELF(mail, mail_filter_mail_module, mmail); } static void mail_filter_parse_setting(struct mail_user *user, const char *name, const char **socket_path_r, const char **args_r) { const char *value, *p; value = mail_user_plugin_getenv(user, name); if (value == NULL) return; p = strchr(value, ' '); if (p == NULL) { *socket_path_r = p_strdup(user->pool, value); *args_r = ""; } else { *socket_path_r = p_strdup_until(user->pool, value, p); *args_r = p_strdup(user->pool, p + 1); } if (**socket_path_r != '/') { /* relative to base_dir */ *socket_path_r = p_strdup_printf(user->pool, "%s/%s", user->set->base_dir, *socket_path_r); } if (user->mail_debug) { i_debug("mail_filter: Filtering %s via socket %s", name, *socket_path_r); } } static void mail_filter_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct mail_filter_user *muser; muser = p_new(user->pool, struct mail_filter_user, 1); muser->module_ctx.super = *v; user->vlast = &muser->module_ctx.super; mail_filter_parse_setting(user, "mail_filter", &muser->socket_path, &muser->args); mail_filter_parse_setting(user, "mail_filter_out", &muser->out_socket_path, &muser->out_args); if (user->mail_debug && muser->socket_path == NULL && muser->out_socket_path == NULL) { i_debug("mail_filter and mail_filter_out settings missing, " "ignoring mail_filter plugin"); } MODULE_CONTEXT_SET(user, mail_filter_user_module, muser); } static struct mail_storage_hooks mail_filter_mail_storage_hooks = { .mail_user_created = mail_filter_mail_user_created, .mailbox_allocated = mail_filter_mailbox_allocated, .mail_allocated = mail_filter_mail_allocated }; void mail_filter_plugin_init(struct module *module) { mail_storage_hooks_add(module, &mail_filter_mail_storage_hooks); } void mail_filter_plugin_deinit(void) { mail_storage_hooks_remove(&mail_filter_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/mail-filter/ostream-ext-filter.c0000644000175000017500000001163413165463624020720 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "net.h" #include "eacces-error.h" #include "istream.h" #include "ostream-private.h" #include "ostream-ext-filter.h" #include struct mail_filter_ostream { struct ostream_private ostream; int fd; struct istream *ext_in; struct ostream *ext_out; bool flushed; }; static void o_stream_mail_filter_close(struct iostream_private *stream, bool close_parent) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; if (mstream->ext_in != NULL) i_stream_destroy(&mstream->ext_in); if (mstream->ext_out != NULL) o_stream_destroy(&mstream->ext_out); if (mstream->fd != -1) { if (close(mstream->fd) < 0) i_error("ext-filter: close() failed: %m"); mstream->fd = -1; } if (close_parent) o_stream_close(mstream->ostream.parent); } static ssize_t o_stream_mail_filter_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; ssize_t ret; if (mstream->ext_out == NULL) { /* connect failed */ mstream->ostream.ostream.stream_errno = EIO; return -1; } /* send the data to the filter */ ret = o_stream_sendv(mstream->ext_out, iov, iov_count); if (ret < 0) { io_stream_set_error(&stream->iostream, "%s", o_stream_get_error(mstream->ext_out)); stream->ostream.stream_errno = mstream->ext_out->stream_errno; return -1; } stream->ostream.offset += ret; return ret; } static int o_stream_mail_filter_flush(struct ostream_private *stream) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; const unsigned char *data; size_t size; ssize_t ret; if (mstream->ext_out == NULL) { /* connect failed */ return -1; } if (mstream->flushed) return 0; if (shutdown(mstream->fd, SHUT_WR) < 0) i_error("ext-filter: shutdown() failed: %m"); while ((ret = i_stream_read_data(mstream->ext_in, &data, &size, 0)) > 0) { ret = o_stream_send(stream->parent, data, size); if (ret != (ssize_t)size) { i_assert(ret < 0); o_stream_copy_error_from_parent(stream); return -1; } i_stream_skip(mstream->ext_in, size); } i_assert(ret == -1); if (!i_stream_have_bytes_left(mstream->ext_in) && mstream->ext_in->v_offset == 0) { /* EOF without any input -> assume the script is repoting failure. pretty ugly way, but currently there's no error reporting channel. */ io_stream_set_error(&stream->iostream, "EOF without input"); stream->ostream.stream_errno = EIO; return -1; } if (mstream->ext_in->stream_errno != 0) { io_stream_set_error(&stream->iostream, "%s", i_stream_get_error(mstream->ext_in)); stream->ostream.stream_errno = mstream->ext_in->stream_errno; return -1; } ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); else mstream->flushed = TRUE; return ret; } static int filter_connect(struct mail_filter_ostream *mstream, const char *socket_path, const char *args) { const char **argv; string_t *str; int fd; argv = t_strsplit(args, " "); if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) { if (errno == EACCES) { io_stream_set_error(&mstream->ostream.iostream, "%s", eacces_error_get("net_connect_unix", socket_path)); } else { io_stream_set_error(&mstream->ostream.iostream, "net_connect_unix(%s) failed: %m", socket_path); } return -1; } net_set_nonblock(fd, FALSE); mstream->fd = fd; mstream->ext_in = i_stream_create_fd(fd, IO_BLOCK_SIZE, FALSE); mstream->ext_out = o_stream_create_fd(fd, 0, FALSE); str = t_str_new(256); str_append(str, "VERSION\tscript\t4\t0\nnoreply\n"); for (; *argv != NULL; argv++) { str_append_tabescaped(str, *argv); str_append_c(str, '\t'); } str_append_c(str, '\n'); ssize_t ret = o_stream_send(mstream->ext_out, str_data(str), str_len(str)); if (ret < 0) { io_stream_set_error(&mstream->ostream.iostream, "%s", o_stream_get_error(mstream->ext_out)); mstream->ostream.ostream.stream_errno = mstream->ext_out->stream_errno; } else if ((size_t)ret != str_len(str)) { io_stream_set_error(&mstream->ostream.iostream, "write(%s): Wrote only %"PRIuSIZE_T" of %"PRIuSIZE_T" bytes", socket_path, (size_t)ret, str_len(str)); mstream->ostream.ostream.stream_errno = ENOBUFS; } return 0; } struct ostream * o_stream_create_ext_filter(struct ostream *output, const char *socket_path, const char *args) { struct mail_filter_ostream *mstream; mstream = i_new(struct mail_filter_ostream, 1); mstream->fd = -1; mstream->ostream.iostream.close = o_stream_mail_filter_close; mstream->ostream.sendv = o_stream_mail_filter_sendv; mstream->ostream.flush = o_stream_mail_filter_flush; (void)filter_connect(mstream, socket_path, args); return o_stream_create(&mstream->ostream, output, mstream->fd); } dovecot-2.2.33.2/src/plugins/mail-filter/ostream-ext-filter.h0000644000175000017500000000027313123174404020707 00000000000000#ifndef OSTREAM_MAIL_FILTER_H #define OSTREAM_MAIL_FILTER_H struct ostream * o_stream_create_ext_filter(struct ostream *output, const char *socket_path, const char *args); #endif dovecot-2.2.33.2/src/plugins/mail-filter/Makefile.am0000644000175000017500000000077413123174404017045 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib10_mail_filter_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib10_mail_filter_plugin.la lib10_mail_filter_plugin_la_SOURCES = \ mail-filter-plugin.c \ istream-ext-filter.c \ ostream-ext-filter.c noinst_HEADERS = \ istream-ext-filter.h \ ostream-ext-filter.h \ mail-filter-plugin.h dovecot-2.2.33.2/src/plugins/mail-filter/istream-ext-filter.c0000644000175000017500000001320213165463624020703 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "strescape.h" #include "net.h" #include "eacces-error.h" #include "fd-set-nonblock.h" #include "ostream.h" #include "istream-private.h" #include "istream-ext-filter.h" #include struct mail_filter_istream { struct istream_private istream; int fd; struct istream *ext_in; struct ostream *ext_out; size_t prev_ret; }; static void i_stream_mail_filter_close(struct iostream_private *stream, bool close_parent) { struct mail_filter_istream *mstream = (struct mail_filter_istream *)stream; if (mstream->ext_in != NULL) i_stream_destroy(&mstream->ext_in); if (mstream->ext_out != NULL) o_stream_destroy(&mstream->ext_out); if (mstream->fd != -1) { if (close(mstream->fd) < 0) i_error("ext-filter: close() failed: %m"); mstream->fd = -1; } if (close_parent) i_stream_close(mstream->istream.parent); } static ssize_t i_stream_read_copy_from(struct istream *istream, struct istream *source) { struct istream_private *stream = istream->real_stream; size_t pos; ssize_t ret; stream->pos -= stream->skip; stream->skip = 0; stream->buffer = i_stream_get_data(source, &pos); if (pos > stream->pos) ret = 0; else do { if ((ret = i_stream_read(source)) == -2) return -2; stream->istream.stream_errno = source->stream_errno; stream->istream.eof = source->eof; stream->buffer = i_stream_get_data(source, &pos); /* check again, in case the source stream had been seeked backwards and the previous read() didn't get us far enough. */ } while (pos <= stream->pos && ret > 0); ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : (ret == 0 ? 0 : -1); stream->pos = pos; i_assert(ret != -1 || stream->istream.eof || stream->istream.stream_errno != 0); return ret; } static ssize_t i_stream_mail_filter_read_once(struct mail_filter_istream *mstream) { struct istream_private *stream = &mstream->istream; ssize_t ret; if (mstream->ext_out != NULL) { /* we haven't sent everything yet */ (void)o_stream_send_istream(mstream->ext_out, stream->parent); if (mstream->ext_out->stream_errno != 0) { stream->istream.stream_errno = mstream->ext_out->stream_errno; return -1; } if (i_stream_is_eof(stream->parent)) { o_stream_destroy(&mstream->ext_out); /* if we wanted to be a blocking stream, from now on the rest of the reads are */ if (stream->istream.blocking) net_set_nonblock(mstream->fd, FALSE); if (shutdown(mstream->fd, SHUT_WR) < 0) i_error("ext-filter: shutdown() failed: %m"); } } i_stream_skip(mstream->ext_in, mstream->prev_ret); ret = i_stream_read_copy_from(&stream->istream, mstream->ext_in); mstream->prev_ret = ret < 0 ? 0 : ret; return ret; } static ssize_t i_stream_mail_filter_read(struct istream_private *stream) { struct mail_filter_istream *mstream = (struct mail_filter_istream *)stream; ssize_t ret; if (mstream->ext_in == NULL) { stream->istream.stream_errno = EIO; return -1; } while ((ret = i_stream_mail_filter_read_once(mstream)) == 0) { if (!stream->istream.blocking) break; } if (ret == -1 && !i_stream_have_bytes_left(&stream->istream) && stream->istream.v_offset == 0) { /* EOF without any input -> assume the script is repoting failure. pretty ugly way, but currently there's no error reporting channel. */ stream->istream.stream_errno = EIO; } return ret; } static int i_stream_mail_filter_stat(struct istream_private *stream, bool exact) { const struct stat *st; i_assert(!exact); if (i_stream_stat(stream->parent, exact, &st) < 0) { stream->istream.stream_errno = stream->parent->stream_errno; return -1; } stream->statbuf = *st; return 0; } static int filter_connect(struct mail_filter_istream *mstream, const char *socket_path, const char *args) { const char **argv; string_t *str; ssize_t ret; int fd; argv = t_strsplit(args, " "); if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) { if (errno == EACCES) { i_error("ext-filter: %s", eacces_error_get("net_connect_unix", socket_path)); } else { i_error("ext-filter: net_connect_unix(%s) failed: %m", socket_path); } return -1; } if (mstream->istream.istream.blocking) net_set_nonblock(fd, FALSE); mstream->fd = fd; mstream->ext_in = i_stream_create_fd(fd, mstream->istream.max_buffer_size, FALSE); mstream->ext_out = o_stream_create_fd(fd, 0, FALSE); str = t_str_new(256); str_append(str, "VERSION\tscript\t4\t0\nnoreply\n"); for (; *argv != NULL; argv++) { str_append_tabescaped(str, *argv); str_append_c(str, '\t'); } str_append_c(str, '\n'); ret = o_stream_send(mstream->ext_out, str_data(str), str_len(str)); if (ret < 0) { i_error("ext-filter: write(%s) failed: %s", socket_path, o_stream_get_error(mstream->ext_out)); i_stream_mail_filter_close(&mstream->istream.iostream, FALSE); return -1; } i_assert((size_t)ret == str_len(str)); return 0; } struct istream * i_stream_create_ext_filter(struct istream *input, const char *socket_path, const char *args) { struct mail_filter_istream *mstream; mstream = i_new(struct mail_filter_istream, 1); mstream->istream.iostream.close = i_stream_mail_filter_close; mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; mstream->istream.read = i_stream_mail_filter_read; mstream->istream.stat = i_stream_mail_filter_stat; mstream->istream.istream.readable_fd = FALSE; mstream->istream.istream.blocking = input->blocking; mstream->istream.istream.seekable = FALSE; mstream->fd = -1; (void)filter_connect(mstream, socket_path, args); return i_stream_create(&mstream->istream, input, mstream->fd); } dovecot-2.2.33.2/src/plugins/quota-clone/0002755000175000017500000000000013172375613015116 500000000000000dovecot-2.2.33.2/src/plugins/quota-clone/Makefile.in0000644000175000017500000005534113172375575017120 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/quota-clone ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_quota_clone_plugin_la_LIBADD = am_lib20_quota_clone_plugin_la_OBJECTS = quota-clone-plugin.lo lib20_quota_clone_plugin_la_OBJECTS = \ $(am_lib20_quota_clone_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_quota_clone_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_quota_clone_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_quota_clone_plugin_la_SOURCES) DIST_SOURCES = $(lib20_quota_clone_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/quota lib20_quota_clone_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_quota_clone_plugin.la lib20_quota_clone_plugin_la_SOURCES = \ quota-clone-plugin.c noinst_HEADERS = \ quota-clone-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/quota-clone/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/quota-clone/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_quota_clone_plugin.la: $(lib20_quota_clone_plugin_la_OBJECTS) $(lib20_quota_clone_plugin_la_DEPENDENCIES) $(EXTRA_lib20_quota_clone_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_quota_clone_plugin_la_LINK) -rpath $(moduledir) $(lib20_quota_clone_plugin_la_OBJECTS) $(lib20_quota_clone_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-clone-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/quota-clone/quota-clone-plugin.c0000644000175000017500000001537313165463624020736 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-context.h" #include "ioloop.h" #include "dict.h" #include "mail-storage-private.h" #include "quota.h" #include "quota-clone-plugin.h" /* If mailbox is kept open for this many milliseconds after quota update, flush quota-clone. */ #define QUOTA_CLONE_FLUSH_DELAY_MSECS (10*1000) #define DICT_QUOTA_CLONE_PATH DICT_PATH_PRIVATE"quota/" #define DICT_QUOTA_CLONE_BYTES_PATH DICT_QUOTA_CLONE_PATH"storage" #define DICT_QUOTA_CLONE_COUNT_PATH DICT_QUOTA_CLONE_PATH"messages" #define QUOTA_CLONE_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_clone_user_module) #define QUOTA_CLONE_CONTEXT(obj) \ MODULE_CONTEXT(obj, quota_clone_storage_module) static MODULE_CONTEXT_DEFINE_INIT(quota_clone_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(quota_clone_storage_module, &mail_storage_module_register); struct quota_clone_user { union mail_user_module_context module_ctx; struct dict *dict; bool quota_flushing; }; struct quota_clone_mailbox { union mailbox_module_context module_ctx; struct timeout *to_quota_flush; bool quota_changed; }; static void quota_clone_flush_real(struct mailbox *box) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT(box->storage->user); struct dict_transaction_context *trans; struct quota_root_iter *iter; struct quota_root *root; uint64_t bytes_value, count_value, limit; int ret_bytes, ret_count; /* we'll clone the first quota root */ iter = quota_root_iter_init(box); root = quota_root_iter_next(iter); quota_root_iter_deinit(&iter); if (root == NULL) { /* no quota roots defined for this mailbox - ignore */ qbox->quota_changed = FALSE; return; } /* get new values first */ ret_bytes = quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES, &bytes_value, &limit); if (ret_bytes < 0) { i_error("quota_clone_plugin: Failed to lookup current quota bytes"); return; } ret_count = quota_get_resource(root, "", QUOTA_NAME_MESSAGES, &count_value, &limit); if (ret_count < 0) { i_error("quota_clone_plugin: Failed to lookup current quota count"); return; } /* update resources always regardless of existence, FIXME: This should be fixed in v2.3 */ trans = dict_transaction_begin(quser->dict); dict_set(trans, DICT_QUOTA_CLONE_BYTES_PATH, t_strdup_printf("%llu", (unsigned long long)bytes_value)); dict_set(trans, DICT_QUOTA_CLONE_COUNT_PATH, t_strdup_printf("%llu", (unsigned long long)count_value)); if (dict_transaction_commit(&trans) < 0) i_error("quota_clone_plugin: Failed to commit dict update"); else qbox->quota_changed = FALSE; } static void quota_clone_flush(struct mailbox *box) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT(box->storage->user); if (qbox->to_quota_flush != NULL) timeout_remove(&qbox->to_quota_flush); if (quser->quota_flushing) { /* recursing back from quota recalculation */ } else if (qbox->quota_changed) { quser->quota_flushing = TRUE; quota_clone_flush_real(box); quser->quota_flushing = FALSE; } } static void quota_clone_changed(struct mailbox *box) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); qbox->quota_changed = TRUE; if (qbox->to_quota_flush == NULL) { qbox->to_quota_flush = timeout_add(QUOTA_CLONE_FLUSH_DELAY_MSECS, quota_clone_flush, box); } } static int quota_clone_save_finish(struct mail_save_context *ctx) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(ctx->transaction->box); quota_clone_changed(ctx->transaction->box); return qbox->module_ctx.super.save_finish(ctx); } static int quota_clone_copy(struct mail_save_context *ctx, struct mail *mail) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(ctx->transaction->box); quota_clone_changed(ctx->transaction->box); return qbox->module_ctx.super.copy(ctx, mail); } static void quota_clone_mailbox_sync_notify(struct mailbox *box, uint32_t uid, enum mailbox_sync_type sync_type) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); if (qbox->module_ctx.super.sync_notify != NULL) qbox->module_ctx.super.sync_notify(box, uid, sync_type); if (sync_type == MAILBOX_SYNC_TYPE_EXPUNGE) quota_clone_changed(box); } static void quota_clone_mailbox_close(struct mailbox *box) { struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); qbox->module_ctx.super.close(box); quota_clone_flush(box); } static void quota_clone_mailbox_allocated(struct mailbox *box) { struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT(box->storage->user); struct mailbox_vfuncs *v = box->vlast; struct quota_clone_mailbox *qbox; if (quser == NULL) return; qbox = p_new(box->pool, struct quota_clone_mailbox, 1); qbox->module_ctx.super = *v; box->vlast = &qbox->module_ctx.super; v->save_finish = quota_clone_save_finish; v->copy = quota_clone_copy; v->sync_notify = quota_clone_mailbox_sync_notify; v->close = quota_clone_mailbox_close; MODULE_CONTEXT_SET(box, quota_clone_storage_module, qbox); } static void quota_clone_mail_user_deinit(struct mail_user *user) { struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT(user); dict_deinit(&quser->dict); quser->module_ctx.super.deinit(user); } static void quota_clone_mail_user_created(struct mail_user *user) { struct quota_clone_user *quser; struct mail_user_vfuncs *v = user->vlast; struct dict_settings dict_set; struct dict *dict; const char *uri, *error; uri = mail_user_plugin_getenv(user, "quota_clone_dict"); if (uri == NULL || uri[0] == '\0') { if (user->mail_debug) { i_debug("The quota_clone_dict setting is missing from configuration"); } return; } i_zero(&dict_set); dict_set.username = user->username; dict_set.base_dir = user->set->base_dir; (void)mail_user_get_home(user, &dict_set.home_dir); if (dict_init_full(uri, &dict_set, &dict, &error) < 0) { i_error("quota_clone_dict: Failed to initialize '%s': %s", uri, error); return; } quser = p_new(user->pool, struct quota_clone_user, 1); quser->module_ctx.super = *v; user->vlast = &quser->module_ctx.super; v->deinit = quota_clone_mail_user_deinit; quser->dict = dict; MODULE_CONTEXT_SET(user, quota_clone_user_module, quser); } static struct mail_storage_hooks quota_clone_mail_storage_hooks = { .mailbox_allocated = quota_clone_mailbox_allocated, .mail_user_created = quota_clone_mail_user_created }; void quota_clone_plugin_init(struct module *module ATTR_UNUSED) { mail_storage_hooks_add(module, "a_clone_mail_storage_hooks); } void quota_clone_plugin_deinit(void) { mail_storage_hooks_remove("a_clone_mail_storage_hooks); } const char *quota_clone_plugin_dependencies[] = { "quota", NULL }; dovecot-2.2.33.2/src/plugins/quota-clone/quota-clone-plugin.h0000644000175000017500000000023613123174404020720 00000000000000#ifndef QUOTA_CLONE_PLUGIN_H #define QUOTA_CLONE_PLUGIN_H void quota_clone_plugin_init(struct module *module); void quota_clone_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/quota-clone/Makefile.am0000644000175000017500000000070113123174404017055 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/plugins/quota NOPLUGIN_LDFLAGS = lib20_quota_clone_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_quota_clone_plugin.la lib20_quota_clone_plugin_la_SOURCES = \ quota-clone-plugin.c noinst_HEADERS = \ quota-clone-plugin.h dovecot-2.2.33.2/src/plugins/acl/0002755000175000017500000000000013172375613013426 500000000000000dovecot-2.2.33.2/src/plugins/acl/Makefile.in0000644000175000017500000007031313172375574015423 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/acl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) lib01_acl_plugin_la_LIBADD = am_lib01_acl_plugin_la_OBJECTS = acl-api.lo acl-attributes.lo \ acl-backend.lo acl-backend-vfile.lo \ acl-backend-vfile-acllist.lo acl-backend-vfile-update.lo \ acl-cache.lo acl-global-file.lo acl-lookup-dict.lo \ acl-mailbox.lo acl-mailbox-list.lo acl-plugin.lo \ acl-shared-storage.lo acl-storage.lo lib01_acl_plugin_la_OBJECTS = $(am_lib01_acl_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib01_acl_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib01_acl_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ lib10_doveadm_acl_plugin_la_LIBADD = am_lib10_doveadm_acl_plugin_la_OBJECTS = doveadm-acl.lo lib10_doveadm_acl_plugin_la_OBJECTS = \ $(am_lib10_doveadm_acl_plugin_la_OBJECTS) lib10_doveadm_acl_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib10_doveadm_acl_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib01_acl_plugin_la_SOURCES) \ $(lib10_doveadm_acl_plugin_la_SOURCES) DIST_SOURCES = $(lib01_acl_plugin_la_SOURCES) \ $(lib10_doveadm_acl_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/list \ -I$(top_srcdir)/src/doveadm lib10_doveadm_acl_plugin_la_LDFLAGS = -module -avoid-version lib01_acl_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib01_acl_plugin.la lib01_acl_plugin_la_SOURCES = \ acl-api.c \ acl-attributes.c \ acl-backend.c \ acl-backend-vfile.c \ acl-backend-vfile-acllist.c \ acl-backend-vfile-update.c \ acl-cache.c \ acl-global-file.c \ acl-lookup-dict.c \ acl-mailbox.c \ acl-mailbox-list.c \ acl-plugin.c \ acl-shared-storage.c \ acl-storage.c noinst_HEADERS = \ acl-backend-vfile.h \ acl-shared-storage.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ acl-api.h \ acl-api-private.h \ acl-cache.h \ acl-global-file.h \ acl-lookup-dict.h \ acl-plugin.h \ acl-storage.h doveadm_module_LTLIBRARIES = \ lib10_doveadm_acl_plugin.la lib10_doveadm_acl_plugin_la_SOURCES = \ doveadm-acl.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/acl/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/acl/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib01_acl_plugin.la: $(lib01_acl_plugin_la_OBJECTS) $(lib01_acl_plugin_la_DEPENDENCIES) $(EXTRA_lib01_acl_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib01_acl_plugin_la_LINK) -rpath $(moduledir) $(lib01_acl_plugin_la_OBJECTS) $(lib01_acl_plugin_la_LIBADD) $(LIBS) lib10_doveadm_acl_plugin.la: $(lib10_doveadm_acl_plugin_la_OBJECTS) $(lib10_doveadm_acl_plugin_la_DEPENDENCIES) $(EXTRA_lib10_doveadm_acl_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_doveadm_acl_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib10_doveadm_acl_plugin_la_OBJECTS) $(lib10_doveadm_acl_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-attributes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-backend-vfile-acllist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-backend-vfile-update.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-backend-vfile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-backend.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-global-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-lookup-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-mailbox-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-mailbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-shared-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-acl.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-doveadm_moduleLTLIBRARIES install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/acl/acl-attributes.c0000644000175000017500000001654513165463624016450 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-storage-private.h" #include "acl-api-private.h" #include "acl-plugin.h" #include "acl-storage.h" struct acl_mailbox_attribute_iter { struct mailbox_attribute_iter iter; struct mailbox_attribute_iter *super; struct acl_object_list_iter *acl_iter; string_t *acl_name; bool failed; }; static int acl_attribute_update_acl(struct mailbox_transaction_context *t, const char *key, const struct mail_attribute_value *value) { const char *value_str, *id, *const *rights, *error; struct acl_rights_update update; /* for now allow only dsync to update ACLs this way. if this check is removed, it should be replaced by a setting, since some admins may still have configured Dovecot using dovecot-acl files directly that they don't want users to update. and in any case ACL_STORAGE_RIGHT_ADMIN must be checked then. */ if (!t->box->storage->user->dsyncing) { mail_storage_set_error(t->box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return -1; } if (mailbox_attribute_value_to_string(t->box->storage, value, &value_str) < 0) return -1; i_zero(&update); update.modify_mode = ACL_MODIFY_MODE_REPLACE; update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; update.last_change = value->last_change; id = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL); rights = value_str == NULL ? NULL : t_strsplit(value_str, " "); if (acl_rights_update_import(&update, id, rights, &error) < 0) { mail_storage_set_error(t->box->storage, MAIL_ERROR_PARAMS, error); return -1; } /* FIXME: this should actually be done only at commit().. */ return acl_mailbox_update_acl(t, &update); } static int acl_attribute_get_acl(struct mailbox *box, const char *key, struct mail_attribute_value *value_r) { struct acl_object *aclobj = acl_mailbox_get_aclobj(box); struct acl_object_list_iter *iter; struct acl_rights rights, wanted_rights; const char *id; int ret; i_zero(value_r); if (!box->storage->user->dsyncing) { mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return -1; } /* set last_change for all ACL objects, even if they don't exist (because they could have been removed by the last change, and dsync can use this information) */ (void)acl_object_last_changed(aclobj, &value_r->last_change); i_zero(&wanted_rights); id = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL); if (acl_identifier_parse(id, &wanted_rights) < 0) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("Invalid ID: %s", id)); return -1; } iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (!rights.global && rights.id_type == wanted_rights.id_type && null_strcmp(rights.identifier, wanted_rights.identifier) == 0) { value_r->value = acl_rights_export(&rights); break; } } if (ret < 0) mail_storage_set_internal_error(box->storage); acl_object_list_deinit(&iter); return ret; } static int acl_have_attribute_rights(struct mailbox *box) { int ret; if (box->deleting) { /* deleting attributes during mailbox deletion */ return 1; } /* RFC 5464: When the ACL extension [RFC4314] is present, users can only set and retrieve private or shared mailbox annotations on a mailbox on which they have the "l" right and any one of the "r", "s", "w", "i", or "p" rights. */ ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); if (ret <= 0) { if (ret < 0) return -1; mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_READ) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_INSERT) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_POST) > 0) return 0; return -1; } int acl_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value) { struct acl_mailbox *abox = ACL_CONTEXT(t->box); if (acl_have_attribute_rights(t->box) < 0) return -1; if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_ACL, strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL)) == 0) return acl_attribute_update_acl(t, key, value); return abox->module_ctx.super.attribute_set(t, type, key, value); } int acl_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r) { struct acl_mailbox *abox = ACL_CONTEXT(t->box); if (acl_have_attribute_rights(t->box) < 0) return -1; if (strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_ACL, strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL)) == 0) return acl_attribute_get_acl(t->box, key, value_r); return abox->module_ctx.super.attribute_get(t, type, key, value_r); } struct mailbox_attribute_iter * acl_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_attribute_iter *aiter; aiter = i_new(struct acl_mailbox_attribute_iter, 1); aiter->iter.box = box; if (acl_have_attribute_rights(box) < 0) aiter->failed = TRUE; else { aiter->super = abox->module_ctx.super. attribute_iter_init(box, type, prefix); if (box->storage->user->dsyncing && type == MAIL_ATTRIBUTE_TYPE_SHARED && strncmp(prefix, MAILBOX_ATTRIBUTE_PREFIX_ACL, strlen(prefix)) == 0) { aiter->acl_iter = acl_object_list_init(abox->aclobj); aiter->acl_name = str_new(default_pool, 128); str_append(aiter->acl_name, MAILBOX_ATTRIBUTE_PREFIX_ACL); } } return &aiter->iter; } static const char * acl_attribute_iter_next_acl(struct acl_mailbox_attribute_iter *aiter) { struct acl_rights rights; int ret; while ((ret = acl_object_list_next(aiter->acl_iter, &rights)) > 0) { if (rights.global) continue; str_truncate(aiter->acl_name, strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL)); acl_rights_write_id(aiter->acl_name, &rights); return str_c(aiter->acl_name); } if (ret < 0) { mail_storage_set_internal_error(aiter->iter.box->storage); aiter->failed = TRUE; return NULL; } acl_object_list_deinit(&aiter->acl_iter); return NULL; } const char *acl_attribute_iter_next(struct mailbox_attribute_iter *iter) { struct acl_mailbox_attribute_iter *aiter = (struct acl_mailbox_attribute_iter *)iter; struct acl_mailbox *abox = ACL_CONTEXT(iter->box); const char *key; if (aiter->super == NULL) return NULL; if (aiter->acl_iter != NULL) { if ((key = acl_attribute_iter_next_acl(aiter)) != NULL) return key; } return abox->module_ctx.super.attribute_iter_next(aiter->super); } int acl_attribute_iter_deinit(struct mailbox_attribute_iter *iter) { struct acl_mailbox_attribute_iter *aiter = (struct acl_mailbox_attribute_iter *)iter; struct acl_mailbox *abox = ACL_CONTEXT(iter->box); int ret = aiter->failed ? -1 : 0; if (aiter->super != NULL) { if (abox->module_ctx.super.attribute_iter_deinit(aiter->super) < 0) ret = -1; } if (aiter->acl_iter != NULL) acl_object_list_deinit(&aiter->acl_iter); if (aiter->acl_name != NULL) str_free(&aiter->acl_name); i_free(aiter); return ret; } dovecot-2.2.33.2/src/plugins/acl/acl-shared-storage.h0000644000175000017500000000017513123174404017154 00000000000000#ifndef ACL_SHARED_STORAGE_H #define ACL_SHARED_STORAGE_H int acl_shared_namespaces_add(struct mail_namespace *ns); #endif dovecot-2.2.33.2/src/plugins/acl/acl-api.c0000644000175000017500000005340213165463624015024 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "hash.h" #include "mail-user.h" #include "mailbox-list.h" #include "acl-global-file.h" #include "acl-cache.h" #include "acl-api-private.h" struct acl_letter_map { char letter; const char *name; }; static const struct acl_letter_map acl_letter_map[] = { { 'l', MAIL_ACL_LOOKUP }, { 'r', MAIL_ACL_READ }, { 'w', MAIL_ACL_WRITE }, { 's', MAIL_ACL_WRITE_SEEN }, { 't', MAIL_ACL_WRITE_DELETED }, { 'i', MAIL_ACL_INSERT }, { 'p', MAIL_ACL_POST }, { 'e', MAIL_ACL_EXPUNGE }, { 'k', MAIL_ACL_CREATE }, { 'x', MAIL_ACL_DELETE }, { 'a', MAIL_ACL_ADMIN }, { '\0', NULL } }; struct acl_object *acl_object_init_from_name(struct acl_backend *backend, const char *name) { return backend->v.object_init(backend, name); } struct acl_object *acl_object_init_from_parent(struct acl_backend *backend, const char *child_name) { return backend->v.object_init_parent(backend, child_name); } void acl_object_deinit(struct acl_object **_aclobj) { struct acl_object *aclobj = *_aclobj; *_aclobj = NULL; aclobj->backend->v.object_deinit(aclobj); } int acl_object_have_right(struct acl_object *aclobj, unsigned int right_idx) { struct acl_backend *backend = aclobj->backend; const struct acl_mask *have_mask; unsigned int read_idx; if (backend->v.object_refresh_cache(aclobj) < 0) return -1; have_mask = acl_cache_get_my_rights(backend->cache, aclobj->name); if (have_mask == NULL) { if (acl_backend_get_default_rights(backend, &have_mask) < 0) return -1; } if (acl_cache_mask_isset(have_mask, right_idx)) return 1; if (mailbox_list_get_user(aclobj->backend->list)->dsyncing) { /* when dsync is running on a shared mailbox, it must be able to do everything inside it. however, dsync shouldn't touch mailboxes where user doesn't have any read access, because that could make them readable on the replica. */ read_idx = acl_backend_lookup_right(aclobj->backend, MAIL_ACL_READ); if (acl_cache_mask_isset(have_mask, read_idx)) return 1; } return 0; } const char *const * acl_backend_mask_get_names(struct acl_backend *backend, const struct acl_mask *mask, pool_t pool) { const char *const *names; const char **buf, **rights; unsigned int names_count, count, i, j, name_idx; names = acl_cache_get_names(backend->cache, &names_count); buf = t_new(const char *, (mask->size * CHAR_BIT) + 1); count = 0; for (i = 0, name_idx = 0; i < mask->size; i++) { if (mask->mask[i] == 0) name_idx += CHAR_BIT; else { for (j = 1; j < (1 << CHAR_BIT); j <<= 1, name_idx++) { if ((mask->mask[i] & j) == 0) continue; /* @UNSAFE */ i_assert(name_idx < names_count); buf[count++] = p_strdup(pool, names[name_idx]); } } } /* @UNSAFE */ rights = p_new(pool, const char *, count + 1); memcpy(rights, buf, count * sizeof(const char *)); return rights; } static int acl_object_get_my_rights_real(struct acl_object *aclobj, pool_t pool, const char *const **rights_r) { struct acl_backend *backend = aclobj->backend; const struct acl_mask *mask; if (backend->v.object_refresh_cache(aclobj) < 0) return -1; mask = acl_cache_get_my_rights(backend->cache, aclobj->name); if (mask == NULL) { if (acl_backend_get_default_rights(backend, &mask) < 0) return -1; } *rights_r = acl_backend_mask_get_names(backend, mask, pool); return 0; } int acl_object_get_my_rights(struct acl_object *aclobj, pool_t pool, const char *const **rights_r) { int ret; if (pool->datastack_pool) return acl_object_get_my_rights_real(aclobj, pool, rights_r); T_BEGIN { ret = acl_object_get_my_rights_real(aclobj, pool, rights_r); } T_END; return ret; } const char *const *acl_object_get_default_rights(struct acl_object *aclobj) { return acl_backend_mask_get_names(aclobj->backend, aclobj->backend->default_aclmask, pool_datastack_create()); } int acl_object_last_changed(struct acl_object *aclobj, time_t *last_changed_r) { return aclobj->backend->v.last_changed(aclobj, last_changed_r); } int acl_object_update(struct acl_object *aclobj, const struct acl_rights_update *update) { return aclobj->backend->v.object_update(aclobj, update); } struct acl_object_list_iter *acl_object_list_init(struct acl_object *aclobj) { return aclobj->backend->v.object_list_init(aclobj); } int acl_object_list_next(struct acl_object_list_iter *iter, struct acl_rights *rights_r) { if (iter->failed) return -1; return iter->aclobj->backend->v.object_list_next(iter, rights_r); } void acl_object_list_deinit(struct acl_object_list_iter **_iter) { struct acl_object_list_iter *iter = *_iter; *_iter = NULL; iter->aclobj->backend->v.object_list_deinit(iter); } struct acl_object_list_iter * acl_default_object_list_init(struct acl_object *aclobj) { struct acl_object_list_iter *iter; const struct acl_rights *aclobj_rights; unsigned int i; pool_t pool; pool = pool_alloconly_create("acl object list", 512); iter = p_new(pool, struct acl_object_list_iter, 1); iter->pool = pool; iter->aclobj = aclobj; if (!array_is_created(&aclobj->rights)) { /* we may have the object cached, but we don't have all the rights read into memory */ acl_cache_flush(aclobj->backend->cache, aclobj->name); } if (aclobj->backend->v.object_refresh_cache(aclobj) < 0) iter->failed = TRUE; aclobj_rights = array_get(&aclobj->rights, &iter->count); if (iter->count > 0) { iter->rights = p_new(pool, struct acl_rights, iter->count); for (i = 0; i < iter->count; i++) acl_rights_dup(&aclobj_rights[i], pool, &iter->rights[i]); } return iter; } int acl_default_object_list_next(struct acl_object_list_iter *iter, struct acl_rights *rights_r) { if (iter->failed) return -1; if (iter->idx == iter->count) return 0; *rights_r = iter->rights[iter->idx++]; return 1; } void acl_default_object_list_deinit(struct acl_object_list_iter *iter) { pool_unref(&iter->pool); } struct acl_mailbox_list_context * acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend) { return backend->v.nonowner_lookups_iter_init(backend); } int acl_backend_nonowner_lookups_iter_next(struct acl_mailbox_list_context *ctx, const char **name_r) { return ctx->backend->v.nonowner_lookups_iter_next(ctx, name_r); } void acl_backend_nonowner_lookups_iter_deinit(struct acl_mailbox_list_context **_ctx) { struct acl_mailbox_list_context *ctx = *_ctx; *_ctx = NULL; ctx->backend->v.nonowner_lookups_iter_deinit(ctx); } int acl_backend_nonowner_lookups_rebuild(struct acl_backend *backend) { return backend->v.nonowner_lookups_rebuild(backend); } void acl_rights_write_id(string_t *dest, const struct acl_rights *right) { switch (right->id_type) { case ACL_ID_ANYONE: str_append(dest, ACL_ID_NAME_ANYONE); break; case ACL_ID_AUTHENTICATED: str_append(dest, ACL_ID_NAME_AUTHENTICATED); break; case ACL_ID_OWNER: str_append(dest, ACL_ID_NAME_OWNER); break; case ACL_ID_USER: str_append(dest, ACL_ID_NAME_USER_PREFIX); str_append(dest, right->identifier); break; case ACL_ID_GROUP: str_append(dest, ACL_ID_NAME_GROUP_PREFIX); str_append(dest, right->identifier); break; case ACL_ID_GROUP_OVERRIDE: str_append(dest, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX); str_append(dest, right->identifier); break; case ACL_ID_TYPE_COUNT: i_unreached(); } } const char *acl_rights_get_id(const struct acl_rights *right) { string_t *str = t_str_new(32); acl_rights_write_id(str, right); return str_c(str); } static bool is_standard_right(const char *name) { unsigned int i; for (i = 0; all_mailbox_rights[i] != NULL; i++) { if (strcmp(all_mailbox_rights[i], name) == 0) return TRUE; } return FALSE; } int acl_rights_update_import(struct acl_rights_update *update, const char *id, const char *const *rights, const char **error_r) { ARRAY_TYPE(const_string) dest_rights, dest_neg_rights, *dest; unsigned int i, j; if (acl_identifier_parse(id, &update->rights) < 0) { *error_r = t_strdup_printf("Invalid ID: %s", id); return -1; } if (rights == NULL) { update->modify_mode = ACL_MODIFY_MODE_CLEAR; update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR; return 0; } t_array_init(&dest_rights, 8); t_array_init(&dest_neg_rights, 8); for (i = 0; rights[i] != NULL; i++) { const char *right = rights[i]; if (right[0] != '-') dest = &dest_rights; else { right++; dest = &dest_neg_rights; } if (strcmp(right, "all") != 0) { if (*right == ':') { /* non-standard right */ right++; array_append(dest, &right, 1); } else if (is_standard_right(right)) { array_append(dest, &right, 1); } else { *error_r = t_strdup_printf("Invalid right '%s'", right); return -1; } } else { for (j = 0; all_mailbox_rights[j] != NULL; j++) array_append(dest, &all_mailbox_rights[j], 1); } } if (array_count(&dest_rights) > 0) { array_append_zero(&dest_rights); update->rights.rights = array_idx(&dest_rights, 0); } else if (update->modify_mode == ACL_MODIFY_MODE_REPLACE) { update->modify_mode = ACL_MODIFY_MODE_CLEAR; } if (array_count(&dest_neg_rights) > 0) { array_append_zero(&dest_neg_rights); update->rights.neg_rights = array_idx(&dest_neg_rights, 0); } else if (update->neg_modify_mode == ACL_MODIFY_MODE_REPLACE) { update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR; } return 0; } const char *acl_rights_export(const struct acl_rights *rights) { string_t *str = t_str_new(128); if (rights->rights != NULL) str_append(str, t_strarray_join(rights->rights, " ")); if (rights->neg_rights != NULL && rights->neg_rights[0] != NULL) { if (str_len(str) > 0) str_append_c(str, ' '); str_append_c(str, '-'); str_append(str, t_strarray_join(rights->neg_rights, " -")); } return str_c(str); } int acl_rights_parse_line(const char *line, pool_t pool, struct acl_rights *rights_r, const char **error_r) { const char *id_str, *const *right_names, *error = NULL; /* [] [:] */ if (*line == '"') { line++; if (str_unescape_next(&line, &id_str) < 0 || (line[0] != ' ' && line[0] != '\0')) { *error_r = "Invalid quoted ID"; return -1; } if (line[0] == ' ') line++; } else { id_str = line; line = strchr(id_str, ' '); if (line == NULL) line = ""; else id_str = t_strdup_until(id_str, line++); } i_zero(rights_r); right_names = acl_right_names_parse(pool, line, &error); if (*id_str != '-') rights_r->rights = right_names; else { id_str++; rights_r->neg_rights = right_names; } if (acl_identifier_parse(id_str, rights_r) < 0) error = t_strdup_printf("Unknown ID '%s'", id_str); if (error != NULL) { *error_r = error; return -1; } rights_r->identifier = p_strdup(pool, rights_r->identifier); return 0; } void acl_rights_dup(const struct acl_rights *src, pool_t pool, struct acl_rights *dest_r) { i_zero(dest_r); dest_r->id_type = src->id_type; dest_r->identifier = p_strdup(pool, src->identifier); dest_r->rights = src->rights == NULL ? NULL : p_strarray_dup(pool, src->rights); dest_r->neg_rights = src->neg_rights == NULL ? NULL : p_strarray_dup(pool, src->neg_rights); dest_r->global = src->global; } int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2) { int ret; if (r1->global != r2->global) { /* globals have higher priority than locals */ return r1->global ? 1 : -1; } ret = r1->id_type - r2->id_type; if (ret != 0) return ret; return null_strcmp(r1->identifier, r2->identifier); } void acl_rights_sort(struct acl_object *aclobj) { struct acl_rights *rights; unsigned int i, dest, count; if (!array_is_created(&aclobj->rights)) return; array_sort(&aclobj->rights, acl_rights_cmp); /* merge identical identifiers */ rights = array_get_modifiable(&aclobj->rights, &count); for (dest = 0, i = 1; i < count; i++) { if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) { /* add i's rights to dest and delete i */ acl_right_names_merge(aclobj->rights_pool, &rights[dest].rights, rights[i].rights, FALSE); acl_right_names_merge(aclobj->rights_pool, &rights[dest].neg_rights, rights[i].neg_rights, FALSE); } else { if (++dest != i) rights[dest] = rights[i]; } } if (++dest != count) array_delete(&aclobj->rights, dest, count - dest); } bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights) { const char *const *p; if (rights->id_type == ACL_ID_OWNER) { /* ignore owner rights */ return FALSE; } if (rights->rights == NULL) return FALSE; for (p = rights->rights; *p != NULL; p++) { if (strcmp(*p, MAIL_ACL_LOOKUP) == 0) return TRUE; } return FALSE; } int acl_identifier_parse(const char *line, struct acl_rights *rights) { if (strncmp(line, ACL_ID_NAME_USER_PREFIX, strlen(ACL_ID_NAME_USER_PREFIX)) == 0) { rights->id_type = ACL_ID_USER; rights->identifier = line + 5; } else if (strcmp(line, ACL_ID_NAME_OWNER) == 0) { rights->id_type = ACL_ID_OWNER; } else if (strncmp(line, ACL_ID_NAME_GROUP_PREFIX, strlen(ACL_ID_NAME_GROUP_PREFIX)) == 0) { rights->id_type = ACL_ID_GROUP; rights->identifier = line + 6; } else if (strncmp(line, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX, strlen(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) == 0) { rights->id_type = ACL_ID_GROUP_OVERRIDE; rights->identifier = line + 15; } else if (strcmp(line, ACL_ID_NAME_AUTHENTICATED) == 0) { rights->id_type = ACL_ID_AUTHENTICATED; } else if (strcmp(line, ACL_ID_NAME_ANYONE) == 0 || strcmp(line, "anonymous") == 0) { rights->id_type = ACL_ID_ANYONE; } else { return -1; } return 0; } static const char *const * acl_right_names_alloc(pool_t pool, ARRAY_TYPE(const_string) *rights_arr, bool dup_strings) { const char **ret, *const *rights; unsigned int i, dest, count; /* sort the rights first so we can easily drop duplicates */ array_sort(rights_arr, i_strcmp_p); /* @UNSAFE */ rights = array_get(rights_arr, &count); ret = p_new(pool, const char *, count + 1); if (count > 0) { ret[0] = rights[0]; for (i = dest = 1; i < count; i++) { if (strcmp(rights[i-1], rights[i]) != 0) ret[dest++] = rights[i]; } ret[dest] = NULL; if (dup_strings) { for (i = 0; i < dest; i++) ret[i] = p_strdup(pool, ret[i]); } } return ret; } const char *const * acl_right_names_parse(pool_t pool, const char *acl, const char **error_r) { ARRAY_TYPE(const_string) rights; const char *const *names; unsigned int i; /* parse IMAP ACL list */ while (*acl == ' ' || *acl == '\t') acl++; t_array_init(&rights, 64); while (*acl != '\0' && *acl != ' ' && *acl != '\t' && *acl != ':') { for (i = 0; acl_letter_map[i].letter != '\0'; i++) { if (acl_letter_map[i].letter == *acl) break; } if (acl_letter_map[i].letter == '\0') { *error_r = t_strdup_printf("Unknown ACL '%c'", *acl); return NULL; } array_append(&rights, &acl_letter_map[i].name, 1); acl++; } while (*acl == ' ' || *acl == '\t') acl++; if (*acl != '\0') { /* parse our own extended ACLs */ if (*acl != ':') { *error_r = "Missing ':' prefix in ACL extensions"; return NULL; } names = t_strsplit_spaces(acl + 1, ", \t"); for (; *names != NULL; names++) { const char *name = p_strdup(pool, *names); array_append(&rights, &name, 1); } } return acl_right_names_alloc(pool, &rights, FALSE); } void acl_right_names_write(string_t *dest, const char *const *rights) { char c2[2]; unsigned int i, j, pos; c2[1] = '\0'; pos = str_len(dest); for (i = 0; rights[i] != NULL; i++) { /* use letters if possible */ for (j = 0; acl_letter_map[j].name != NULL; j++) { if (strcmp(rights[i], acl_letter_map[j].name) == 0) { c2[0] = acl_letter_map[j].letter; str_insert(dest, pos, c2); pos++; break; } } if (acl_letter_map[j].name == NULL) { /* fallback to full name */ str_append_c(dest, ' '); str_append(dest, rights[i]); } } if (pos + 1 < str_len(dest)) { c2[0] = ':'; str_insert(dest, pos + 1, c2); } } void acl_right_names_merge(pool_t pool, const char *const **destp, const char *const *src, bool dup_strings) { const char *const *dest = *destp; ARRAY_TYPE(const_string) rights; unsigned int i; t_array_init(&rights, 64); if (dest != NULL) { for (i = 0; dest[i] != NULL; i++) array_append(&rights, &dest[i], 1); } if (src != NULL) { for (i = 0; src[i] != NULL; i++) array_append(&rights, &src[i], 1); } *destp = acl_right_names_alloc(pool, &rights, dup_strings); } bool acl_right_names_modify(pool_t pool, const char *const **rightsp, const char *const *modify_rights, enum acl_modify_mode modify_mode) { const char *const *old_rights = *rightsp; const char *const *new_rights = NULL; const char *null = NULL; ARRAY_TYPE(const_string) rights; unsigned int i, j; if (modify_rights == NULL && modify_mode != ACL_MODIFY_MODE_CLEAR) { /* nothing to do here */ return FALSE; } switch (modify_mode) { case ACL_MODIFY_MODE_REMOVE: if (old_rights == NULL || *old_rights == NULL) { /* nothing to do */ return FALSE; } t_array_init(&rights, 64); for (i = 0; old_rights[i] != NULL; i++) { for (j = 0; modify_rights[j] != NULL; j++) { if (strcmp(old_rights[i], modify_rights[j]) == 0) break; } if (modify_rights[j] == NULL) array_append(&rights, &old_rights[i], 1); } new_rights = &null; modify_rights = array_count(&rights) == 0 ? NULL : array_idx(&rights, 0); acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); break; case ACL_MODIFY_MODE_ADD: new_rights = old_rights; acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); break; case ACL_MODIFY_MODE_REPLACE: new_rights = &null; acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); break; case ACL_MODIFY_MODE_CLEAR: if (*rightsp == NULL) { /* ACL didn't exist before either */ return FALSE; } *rightsp = NULL; return TRUE; } i_assert(new_rights != NULL); *rightsp = new_rights; if (old_rights == NULL) return new_rights[0] != NULL; /* see if anything changed */ for (i = 0; old_rights[i] != NULL && new_rights[i] != NULL; i++) { if (strcmp(old_rights[i], new_rights[i]) != 0) return TRUE; } return old_rights[i] != NULL || new_rights[i] != NULL; } static void apply_owner_default_rights(struct acl_object *aclobj) { struct acl_rights_update ru; const char *null = NULL; i_zero(&ru); ru.modify_mode = ACL_MODIFY_MODE_REPLACE; ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; ru.rights.id_type = ACL_ID_OWNER; ru.rights.rights = aclobj->backend->default_rights; ru.rights.neg_rights = &null; acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); } void acl_object_rebuild_cache(struct acl_object *aclobj) { struct acl_rights_update ru; enum acl_modify_mode add_mode; const struct acl_rights *rights, *prev_match = NULL; unsigned int i, count; bool first_global = TRUE; acl_cache_flush(aclobj->backend->cache, aclobj->name); if (!array_is_created(&aclobj->rights)) return; /* Rights are sorted by their 1) locals first, globals next, 2) acl_id_type. We'll apply only the rights matching ourself. Every time acl_id_type or local/global changes, the new ACLs will replace all of the existing ACLs. Basically this means that if user belongs to multiple matching groups or group-overrides, their ACLs are merged. In all other situations the ACLs are replaced (because there aren't duplicate rights entries and a user can't match multiple usernames). */ i_zero(&ru); rights = array_get(&aclobj->rights, &count); if (!acl_backend_user_is_owner(aclobj->backend)) i = 0; else { /* we're the owner. skip over all rights entries until we reach ACL_ID_OWNER or higher, or alternatively when we reach a global ACL (even ACL_ID_ANYONE overrides owner's rights if it's global) */ for (i = 0; i < count; i++) { if (rights[i].id_type >= ACL_ID_OWNER || rights[i].global) break; } apply_owner_default_rights(aclobj); /* now continue applying the rest of the rights, if there are any */ } for (; i < count; i++) { if (!acl_backend_rights_match_me(aclobj->backend, &rights[i])) continue; if (prev_match == NULL || prev_match->id_type != rights[i].id_type || prev_match->global != rights[i].global) { /* replace old ACLs */ add_mode = ACL_MODIFY_MODE_REPLACE; } else { /* merging to existing ACLs */ i_assert(rights[i].id_type == ACL_ID_GROUP || rights[i].id_type == ACL_ID_GROUP_OVERRIDE); add_mode = ACL_MODIFY_MODE_ADD; } prev_match = &rights[i]; /* If [neg_]rights is NULL it needs to be ignored. The easiest way to do that is to just mark it with REMOVE mode */ ru.modify_mode = rights[i].rights == NULL ? ACL_MODIFY_MODE_REMOVE : add_mode; ru.neg_modify_mode = rights[i].neg_rights == NULL ? ACL_MODIFY_MODE_REMOVE : add_mode; ru.rights = rights[i]; if (rights[i].global && first_global) { /* first global: reset negative ACLs so local ACLs can't mess things up via them */ first_global = FALSE; ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; } acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); } } void acl_object_remove_all_access(struct acl_object *aclobj) { static const char *null = NULL; struct acl_rights rights; i_zero(&rights); rights.id_type = ACL_ID_ANYONE; rights.rights = &null; array_append(&aclobj->rights, &rights, 1); rights.id_type = ACL_ID_OWNER; rights.rights = &null; array_append(&aclobj->rights, &rights, 1); } void acl_object_add_global_acls(struct acl_object *aclobj) { struct acl_backend *backend = aclobj->backend; const char *vname, *error; if (mailbox_list_is_valid_name(backend->list, aclobj->name, &error)) vname = mailbox_list_get_vname(backend->list, aclobj->name); else vname = ""; acl_global_file_get(backend->global_file, vname, aclobj->rights_pool, &aclobj->rights); } dovecot-2.2.33.2/src/plugins/acl/acl-lookup-dict.h0000644000175000017500000000110413123174404016467 00000000000000#ifndef ACL_LOOKUP_DICT_H #define ACL_LOOKUP_DICT_H struct acl_lookup_dict *acl_lookup_dict_init(struct mail_user *user); void acl_lookup_dict_deinit(struct acl_lookup_dict **dict); bool acl_lookup_dict_is_enabled(struct acl_lookup_dict *dict); int acl_lookup_dict_rebuild(struct acl_lookup_dict *dict); struct acl_lookup_dict_iter * acl_lookup_dict_iterate_visible_init(struct acl_lookup_dict *dict); const char * acl_lookup_dict_iterate_visible_next(struct acl_lookup_dict_iter *iter); int acl_lookup_dict_iterate_visible_deinit(struct acl_lookup_dict_iter **iter); #endif dovecot-2.2.33.2/src/plugins/acl/acl-backend-vfile.h0000644000175000017500000000400313165463624016743 00000000000000#ifndef ACL_BACKEND_VFILE_H #define ACL_BACKEND_VFILE_H #include "acl-api-private.h" #define ACL_FILENAME "dovecot-acl" #define ACLLIST_FILENAME "dovecot-acl-list" #define ACL_VFILE_VALIDITY_MTIME_NOTFOUND 0 #define ACL_VFILE_VALIDITY_MTIME_NOACCESS -1 struct acl_vfile_validity { time_t last_check; time_t last_read_time; time_t last_mtime; off_t last_size; }; struct acl_backend_vfile_validity { struct acl_vfile_validity global_validity, local_validity; struct acl_vfile_validity mailbox_validity; }; struct acl_object_vfile { struct acl_object aclobj; /* if backend->global_file is NULL, assume legacy separate global ACL file per mailbox */ char *global_path, *local_path; }; struct acl_backend_vfile_acllist { time_t mtime; const char *name; }; struct acl_backend_vfile { struct acl_backend backend; const char *global_path; pool_t acllist_pool; ARRAY(struct acl_backend_vfile_acllist) acllist; time_t acllist_last_check; time_t acllist_mtime; unsigned int acllist_change_counter; unsigned int cache_secs; unsigned int rebuilding_acllist:1; unsigned int iterating_acllist:1; }; void acl_vfile_write_rights_list(string_t *dest, const char *const *rights); int acl_backend_vfile_object_update(struct acl_object *aclobj, const struct acl_rights_update *update); void acl_backend_vfile_acllist_refresh(struct acl_backend_vfile *backend); int acl_backend_vfile_acllist_rebuild(struct acl_backend_vfile *backend); void acl_backend_vfile_acllist_verify(struct acl_backend_vfile *backend, const char *name, time_t mtime); struct acl_mailbox_list_context * acl_backend_vfile_nonowner_iter_init(struct acl_backend *backend); int acl_backend_vfile_nonowner_iter_next(struct acl_mailbox_list_context *ctx, const char **name_r); void acl_backend_vfile_nonowner_iter_deinit(struct acl_mailbox_list_context *ctx); int acl_backend_vfile_nonowner_lookups_rebuild(struct acl_backend *backend); int acl_backend_vfile_object_get_mtime(struct acl_object *aclobj, time_t *mtime_r); #endif dovecot-2.2.33.2/src/plugins/acl/acl-cache.c0000644000175000017500000002511713123174404015305 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash.h" #include "acl-cache.h" #include "acl-api-private.h" /* Give more than enough so that the arrays should never have to be grown. IMAP ACLs define only 10 standard rights and 10 user-defined rights. */ #define DEFAULT_ACL_RIGHTS_COUNT 64 #define ACL_GLOBAL_COUNT 2 struct acl_object_cache { char *name; struct acl_mask *my_rights, *my_neg_rights; struct acl_mask *my_current_rights; }; struct acl_cache { struct acl_backend *backend; /* name => object */ HASH_TABLE(char *, struct acl_object_cache *) objects; size_t validity_rec_size; /* Right names mapping is used for faster rights checking. Note that acl_mask bitmask relies on the order to never change, so only new rights can be added to the mapping. */ pool_t right_names_pool; /* idx => right name. */ ARRAY(const char *) right_idx_name_map; /* name => idx+1 */ HASH_TABLE(char *, void *) right_name_idx_map; }; static struct acl_mask negative_cache_entry; struct acl_cache *acl_cache_init(struct acl_backend *backend, size_t validity_rec_size) { struct acl_cache *cache; cache = i_new(struct acl_cache, 1); cache->backend = backend; cache->validity_rec_size = validity_rec_size; cache->right_names_pool = pool_alloconly_create("ACL right names", 1024); hash_table_create(&cache->objects, default_pool, 0, str_hash, strcmp); hash_table_create(&cache->right_name_idx_map, cache->right_names_pool, 0, str_hash, strcmp); i_array_init(&cache->right_idx_name_map, DEFAULT_ACL_RIGHTS_COUNT); return cache; } void acl_cache_deinit(struct acl_cache **_cache) { struct acl_cache *cache = *_cache; *_cache = NULL; acl_cache_flush_all(cache); array_free(&cache->right_idx_name_map); hash_table_destroy(&cache->right_name_idx_map); hash_table_destroy(&cache->objects); pool_unref(&cache->right_names_pool); i_free(cache); } static void acl_cache_free_object_cache(struct acl_object_cache *obj_cache) { if (obj_cache->my_current_rights != NULL && obj_cache->my_current_rights != &negative_cache_entry) acl_cache_mask_deinit(&obj_cache->my_current_rights); if (obj_cache->my_rights != NULL) acl_cache_mask_deinit(&obj_cache->my_rights); if (obj_cache->my_neg_rights != NULL) acl_cache_mask_deinit(&obj_cache->my_neg_rights); i_free(obj_cache->name); i_free(obj_cache); } static struct acl_mask * acl_cache_mask_init_real(struct acl_cache *cache, pool_t pool, const char *const *rights) { struct acl_mask *mask; unsigned int rights_count, i, idx; unsigned char *p; buffer_t *bitmask; rights_count = str_array_length(rights); bitmask = buffer_create_dynamic(pool_datastack_create(), DEFAULT_ACL_RIGHTS_COUNT / CHAR_BIT); for (i = 0; i < rights_count; i++) { idx = acl_cache_right_lookup(cache, rights[i]); p = buffer_get_space_unsafe(bitmask, idx / CHAR_BIT, 1); *p |= 1 << (idx % CHAR_BIT); } /* @UNSAFE */ mask = p_malloc(pool, SIZEOF_ACL_MASK(bitmask->used)); memcpy(mask->mask, bitmask->data, bitmask->used); mask->pool = pool; mask->size = bitmask->used; return mask; } struct acl_mask *acl_cache_mask_init(struct acl_cache *cache, pool_t pool, const char *const *rights) { struct acl_mask *mask; T_BEGIN { mask = acl_cache_mask_init_real(cache, pool, rights); } T_END; return mask; } static struct acl_mask * acl_cache_mask_dup(pool_t pool, const struct acl_mask *src) { struct acl_mask *mask; mask = p_malloc(pool, SIZEOF_ACL_MASK(src->size)); memcpy(mask->mask, src->mask, src->size); mask->pool = pool; mask->size = src->size; return mask; } void acl_cache_mask_deinit(struct acl_mask **_mask) { struct acl_mask *mask = *_mask; *_mask = NULL; p_free(mask->pool, mask); } unsigned int acl_cache_right_lookup(struct acl_cache *cache, const char *right) { unsigned int idx; void *idx_p; char *name; const char *const_name; /* use +1 for right_name_idx_map values because we can't add NULL values. */ idx_p = hash_table_lookup(cache->right_name_idx_map, right); if (idx_p == NULL) { /* new right name, add it */ const_name = name = p_strdup(cache->right_names_pool, right); idx = array_count(&cache->right_idx_name_map); array_append(&cache->right_idx_name_map, &const_name, 1); hash_table_insert(cache->right_name_idx_map, name, POINTER_CAST(idx + 1)); } else { idx = POINTER_CAST_TO(idx_p, unsigned int)-1; } return idx; } void acl_cache_flush(struct acl_cache *cache, const char *objname) { struct acl_object_cache *obj_cache; obj_cache = hash_table_lookup(cache->objects, objname); if (obj_cache != NULL) { hash_table_remove(cache->objects, objname); acl_cache_free_object_cache(obj_cache); } } void acl_cache_flush_all(struct acl_cache *cache) { struct hash_iterate_context *iter; char *key; struct acl_object_cache *obj_cache; iter = hash_table_iterate_init(cache->objects); while (hash_table_iterate(iter, cache->objects, &key, &obj_cache)) acl_cache_free_object_cache(obj_cache); hash_table_iterate_deinit(&iter); hash_table_clear(cache->objects, FALSE); } static void acl_cache_update_rights_mask(struct acl_cache *cache, struct acl_object_cache *obj_cache, enum acl_modify_mode modify_mode, const char *const *rights, struct acl_mask **mask_p) { struct acl_mask *change_mask, *old_mask, *new_mask; unsigned int i, size; bool changed = TRUE; change_mask = rights == NULL ? NULL : acl_cache_mask_init(cache, default_pool, rights); old_mask = *mask_p; new_mask = old_mask; switch (modify_mode) { case ACL_MODIFY_MODE_ADD: if (old_mask == NULL) { new_mask = change_mask; break; } if (change_mask == NULL) { /* no changes */ changed = FALSE; break; } /* merge the masks */ if (old_mask->size >= change_mask->size) { /* keep using the old mask */ for (i = 0; i < change_mask->size; i++) old_mask->mask[i] |= change_mask->mask[i]; } else { /* use the new mask, put old changes into it */ for (i = 0; i < old_mask->size; i++) change_mask->mask[i] |= old_mask->mask[i]; new_mask = change_mask; } break; case ACL_MODIFY_MODE_REMOVE: if (old_mask == NULL || change_mask == NULL) { changed = FALSE; break; } /* remove changed bits from old mask */ size = I_MIN(old_mask->size, change_mask->size); for (i = 0; i < size; i++) old_mask->mask[i] &= ~change_mask->mask[i]; break; case ACL_MODIFY_MODE_REPLACE: if (old_mask == NULL && change_mask == NULL) changed = FALSE; new_mask = change_mask; break; case ACL_MODIFY_MODE_CLEAR: i_unreached(); } if (new_mask != old_mask) { *mask_p = new_mask; if (old_mask != NULL) acl_cache_mask_deinit(&old_mask); } if (new_mask != change_mask && change_mask != NULL) acl_cache_mask_deinit(&change_mask); if (changed && obj_cache->my_current_rights != NULL) { /* current rights need to be recalculated */ if (obj_cache->my_current_rights == &negative_cache_entry) obj_cache->my_current_rights = NULL; else acl_cache_mask_deinit(&obj_cache->my_current_rights); } } static struct acl_object_cache * acl_cache_object_get(struct acl_cache *cache, const char *objname, bool *created_r) { struct acl_object_cache *obj_cache; obj_cache = hash_table_lookup(cache->objects, objname); if (obj_cache == NULL) { obj_cache = i_malloc(MALLOC_ADD(sizeof(struct acl_object_cache), cache->validity_rec_size)); obj_cache->name = i_strdup(objname); hash_table_insert(cache->objects, obj_cache->name, obj_cache); *created_r = TRUE; } else { *created_r = FALSE; } return obj_cache; } void acl_cache_update(struct acl_cache *cache, const char *objname, const struct acl_rights_update *update) { struct acl_object_cache *obj_cache; bool created; obj_cache = acl_cache_object_get(cache, objname, &created); i_assert(obj_cache->my_current_rights != &negative_cache_entry); if (created && update->modify_mode != ACL_MODIFY_MODE_REPLACE) { /* since the rights aren't being replaced, start with our default rights */ obj_cache->my_rights = acl_cache_mask_dup(default_pool, cache->backend->default_aclmask); } acl_cache_update_rights_mask(cache, obj_cache, update->modify_mode, update->rights.rights, &obj_cache->my_rights); acl_cache_update_rights_mask(cache, obj_cache, update->neg_modify_mode, update->rights.neg_rights, &obj_cache->my_neg_rights); } void acl_cache_set_validity(struct acl_cache *cache, const char *objname, const void *validity) { struct acl_object_cache *obj_cache; bool created; obj_cache = acl_cache_object_get(cache, objname, &created); /* @UNSAFE: validity is stored after the cache record */ memcpy(obj_cache + 1, validity, cache->validity_rec_size); if (created) { /* negative cache entry */ obj_cache->my_current_rights = &negative_cache_entry; } } void *acl_cache_get_validity(struct acl_cache *cache, const char *objname) { struct acl_object_cache *obj_cache; obj_cache = hash_table_lookup(cache->objects, objname); return obj_cache == NULL ? NULL : (obj_cache + 1); } const char *const *acl_cache_get_names(struct acl_cache *cache, unsigned int *count_r) { *count_r = array_count(&cache->right_idx_name_map); return array_idx(&cache->right_idx_name_map, 0); } static void acl_cache_my_current_rights_recalculate(struct acl_object_cache *obj_cache) { struct acl_mask *mask; unsigned int i, size; /* @UNSAFE */ size = obj_cache->my_rights == NULL ? 0 : obj_cache->my_rights->size; mask = i_malloc(SIZEOF_ACL_MASK(size)); mask->pool = default_pool; mask->size = size; /* apply the positive rights */ if (obj_cache->my_rights != NULL) memcpy(mask->mask, obj_cache->my_rights->mask, mask->size); if (obj_cache->my_neg_rights != NULL) { /* apply the negative rights. they override positive rights. */ size = I_MIN(mask->size, obj_cache->my_neg_rights->size); for (i = 0; i < size; i++) mask->mask[i] &= ~obj_cache->my_neg_rights->mask[i]; } obj_cache->my_current_rights = mask; } const struct acl_mask * acl_cache_get_my_rights(struct acl_cache *cache, const char *objname) { struct acl_object_cache *obj_cache; obj_cache = hash_table_lookup(cache->objects, objname); if (obj_cache == NULL || obj_cache->my_current_rights == &negative_cache_entry) return NULL; if (obj_cache->my_current_rights == NULL) { T_BEGIN { acl_cache_my_current_rights_recalculate(obj_cache); } T_END; } return obj_cache->my_current_rights; } bool acl_cache_mask_isset(const struct acl_mask *mask, unsigned int right_idx) { unsigned int mask_idx; mask_idx = right_idx / CHAR_BIT; return mask_idx < mask->size && (mask->mask[mask_idx] & (1 << (right_idx % CHAR_BIT))) != 0; } dovecot-2.2.33.2/src/plugins/acl/acl-global-file.h0000644000175000017500000000157113167162046016431 00000000000000#ifndef ACL_GLOBAL_FILE_H #define ACL_GLOBAL_FILE_H #include "acl-api.h" struct acl_global_file * acl_global_file_init(const char *path, unsigned int refresh_interval_secs, bool debug); void acl_global_file_deinit(struct acl_global_file **file); /* Read the global ACLs into memory. */ int acl_global_file_refresh(struct acl_global_file *file); /* Return stat data for the last refresh. */ void acl_global_file_last_stat(struct acl_global_file *file, struct stat *st_r); /* Return global ACL rights matching the mailbox name. The file must already have been refreshed at least once. */ void acl_global_file_get(struct acl_global_file *file, const char *vname, pool_t pool, ARRAY_TYPE(acl_rights) *rights_r); /* Returns TRUE if there are any global ACLs matching the mailbox name. */ bool acl_global_file_have_any(struct acl_global_file *file, const char *vname); #endif dovecot-2.2.33.2/src/plugins/acl/acl-backend-vfile-acllist.c0000644000175000017500000002563613165463624020406 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "safe-mkstemp.h" #include "istream.h" #include "ostream.h" #include "mail-namespace.h" #include "mail-storage.h" #include "acl-plugin.h" #include "acl-cache.h" #include "acl-lookup-dict.h" #include "acl-backend-vfile.h" #include #include #include #include struct acl_mailbox_list_context_vfile { struct acl_mailbox_list_context ctx; unsigned int idx; }; static void acllist_clear(struct acl_backend_vfile *backend, uoff_t file_size) { if (backend->acllist_pool == NULL) { backend->acllist_pool = pool_alloconly_create("vfile acllist", I_MAX(file_size / 2, 128)); i_array_init(&backend->acllist, I_MAX(16, file_size / 60)); } else { p_clear(backend->acllist_pool); array_clear(&backend->acllist); } } static bool acl_list_get_root_dir(struct acl_backend_vfile *backend, const char **root_dir_r, enum mailbox_list_path_type *type_r) { struct mail_storage *storage; const char *rootdir, *maildir; enum mailbox_list_path_type type; if (backend->backend.globals_only) return FALSE; storage = mailbox_list_get_namespace(backend->backend.list)->storage; type = (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0 ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; if (!mailbox_list_get_root_path(backend->backend.list, type, &rootdir)) return FALSE; *type_r = type; if (type == MAILBOX_LIST_PATH_TYPE_DIR && mail_storage_is_mailbox_file(storage)) { maildir = mailbox_list_get_root_forced(backend->backend.list, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(maildir, rootdir) == 0) { /* dovecot-acl-list would show up as a mailbox if we created it to root dir. since we don't really have any other good alternatives, place it to control dir */ rootdir = mailbox_list_get_root_forced(backend->backend.list, MAILBOX_LIST_PATH_TYPE_CONTROL); *type_r = MAILBOX_LIST_PATH_TYPE_CONTROL; } } *root_dir_r = rootdir; return TRUE; } static bool acl_list_get_path(struct acl_backend_vfile *backend, const char **path_r) { enum mailbox_list_path_type type; const char *root_dir; if (!acl_list_get_root_dir(backend, &root_dir, &type)) return FALSE; *path_r = t_strconcat(root_dir, "/"ACLLIST_FILENAME, NULL); return TRUE; } static int acl_backend_vfile_acllist_read(struct acl_backend_vfile *backend) { struct acl_backend_vfile_acllist acllist; struct istream *input; struct stat st; const char *path, *line, *p; int fd, ret = 0; backend->acllist_last_check = ioloop_time; if (!acl_list_get_path(backend, &path)) { /* we're never going to build acllist for this namespace. */ acllist_clear(backend, 0); return 0; } if (backend->acllist_mtime != 0) { /* see if the file's mtime has changed */ if (stat(path, &st) < 0) { if (errno == ENOENT) backend->acllist_mtime = 0; else i_error("stat(%s) failed: %m", path); return -1; } if (st.st_mtime == backend->acllist_mtime) return 0; } fd = open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { backend->acllist_mtime = 0; return -1; } i_error("open(%s) failed: %m", path); return -1; } if (fstat(fd, &st) < 0) { i_error("fstat(%s) failed: %m", path); i_close_fd(&fd); return -1; } backend->acllist_mtime = st.st_mtime; acllist_clear(backend, st.st_size); input = i_stream_create_fd(fd, (size_t)-1, FALSE); while ((line = i_stream_read_next_line(input)) != NULL) { acllist.mtime = 0; for (p = line; *p >= '0' && *p <= '9'; p++) acllist.mtime = acllist.mtime * 10 + (*p - '0'); if (p == line || *p != ' ' || p[1] == '\0') { i_error("Broken acllist file: %s", path); i_unlink_if_exists(path); i_close_fd(&fd); return -1; } acllist.name = p_strdup(backend->acllist_pool, p + 1); array_append(&backend->acllist, &acllist, 1); } if (input->stream_errno != 0) ret = -1; i_stream_destroy(&input); if (close(fd) < 0) i_error("close(%s) failed: %m", path); return ret; } void acl_backend_vfile_acllist_refresh(struct acl_backend_vfile *backend) { i_assert(!backend->iterating_acllist); if (backend->acllist_last_check + (time_t)backend->cache_secs > ioloop_time) return; if (acl_backend_vfile_acllist_read(backend) < 0) { acllist_clear(backend, 0); if (!backend->rebuilding_acllist) (void)acl_backend_vfile_acllist_rebuild(backend); } } static int acllist_append(struct acl_backend_vfile *backend, struct ostream *output, const char *vname) { struct acl_object *aclobj; struct acl_object_list_iter *iter; struct acl_rights rights; struct acl_backend_vfile_acllist acllist; const char *name; int ret; name = mailbox_list_get_storage_name(backend->backend.list, vname); acl_cache_flush(backend->backend.cache, name); aclobj = acl_object_init_from_name(&backend->backend, name); iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (acl_rights_has_nonowner_lookup_changes(&rights)) break; } acl_object_list_deinit(&iter); if (acl_backend_vfile_object_get_mtime(aclobj, &acllist.mtime) < 0) ret = -1; if (ret > 0) { acllist.name = p_strdup(backend->acllist_pool, name); array_append(&backend->acllist, &acllist, 1); T_BEGIN { const char *line; line = t_strdup_printf("%s %s\n", dec2str(acllist.mtime), name); o_stream_nsend_str(output, line); } T_END; } acl_object_deinit(&aclobj); return ret < 0 ? -1 : 0; } static int acl_backend_vfile_acllist_try_rebuild(struct acl_backend_vfile *backend) { struct mailbox_list *list = backend->backend.list; struct mail_namespace *ns; struct mailbox_list_iterate_context *iter; enum mailbox_list_path_type type; const struct mailbox_info *info; const char *rootdir, *acllist_path; struct ostream *output; struct stat st; struct mailbox_permissions perm; string_t *path; int fd, ret; i_assert(!backend->rebuilding_acllist); if (!acl_list_get_root_dir(backend, &rootdir, &type)) return 0; ns = mailbox_list_get_namespace(list); if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) { /* we can't write anything here */ return 0; } path = t_str_new(256); str_printfa(path, "%s/%s", rootdir, mailbox_list_get_temp_prefix(list)); /* Build it into a temporary file and rename() over. There's no need to use locking, because even if multiple processes are rebuilding the file at the same time the result should be the same. */ mailbox_list_get_root_permissions(list, &perm); fd = safe_mkstemp_group(path, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); if (fd == -1 && errno == ENOENT) { if (mailbox_list_mkdir_root(backend->backend.list, rootdir, type) < 0) return -1; fd = safe_mkstemp_group(path, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin); } if (fd == -1) { if (errno == EACCES) { /* Ignore silently if we can't create it */ return 0; } i_error("dovecot-acl-list creation failed: " "safe_mkstemp(%s) failed: %m", str_c(path)); return -1; } output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_cork(output); ret = 0; acllist_clear(backend, 0); backend->rebuilding_acllist = TRUE; iter = mailbox_list_iter_init(list, "*", MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (acllist_append(backend, output, info->vname) < 0) { ret = -1; break; } } if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", str_c(path), o_stream_get_error(output)); ret = -1; } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; o_stream_destroy(&output); if (ret == 0) { if (fstat(fd, &st) < 0) { i_error("fstat(%s) failed: %m", str_c(path)); ret = -1; } } if (close(fd) < 0) { i_error("close(%s) failed: %m", str_c(path)); ret = -1; } if (ret == 0) { if (!acl_list_get_path(backend, &acllist_path)) i_unreached(); if (rename(str_c(path), acllist_path) < 0) { i_error("rename(%s, %s) failed: %m", str_c(path), acllist_path); ret = -1; } } if (ret == 0) { struct acl_user *auser = ACL_USER_CONTEXT(ns->user); backend->acllist_mtime = st.st_mtime; backend->acllist_last_check = ioloop_time; /* FIXME: dict reubild is expensive, try to avoid it */ (void)acl_lookup_dict_rebuild(auser->acl_lookup_dict); } else { acllist_clear(backend, 0); i_unlink_if_exists(str_c(path)); } backend->rebuilding_acllist = FALSE; return ret; } int acl_backend_vfile_acllist_rebuild(struct acl_backend_vfile *backend) { const char *acllist_path; if (acl_backend_vfile_acllist_try_rebuild(backend) == 0) return 0; else { /* delete it to make sure it gets rebuilt later */ if (!acl_list_get_path(backend, &acllist_path)) i_unreached(); i_unlink_if_exists(acllist_path); return -1; } } static const struct acl_backend_vfile_acllist * acl_backend_vfile_acllist_find(struct acl_backend_vfile *backend, const char *name) { const struct acl_backend_vfile_acllist *acllist; array_foreach(&backend->acllist, acllist) { if (strcmp(acllist->name, name) == 0) return acllist; } return NULL; } void acl_backend_vfile_acllist_verify(struct acl_backend_vfile *backend, const char *name, time_t mtime) { const struct acl_backend_vfile_acllist *acllist; if (backend->rebuilding_acllist || backend->iterating_acllist) return; acl_backend_vfile_acllist_refresh(backend); acllist = acl_backend_vfile_acllist_find(backend, name); if (acllist != NULL && acllist->mtime != mtime) (void)acl_backend_vfile_acllist_rebuild(backend); } struct acl_mailbox_list_context * acl_backend_vfile_nonowner_iter_init(struct acl_backend *_backend) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; struct acl_mailbox_list_context_vfile *ctx; acl_backend_vfile_acllist_refresh(backend); ctx = i_new(struct acl_mailbox_list_context_vfile, 1); ctx->ctx.backend = _backend; backend->iterating_acllist = TRUE; return &ctx->ctx; } int acl_backend_vfile_nonowner_iter_next(struct acl_mailbox_list_context *_ctx, const char **name_r) { struct acl_mailbox_list_context_vfile *ctx = (struct acl_mailbox_list_context_vfile *)_ctx; struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_ctx->backend; const struct acl_backend_vfile_acllist *acllist; unsigned int count; acllist = array_get(&backend->acllist, &count); if (ctx->idx == count) return 0; *name_r = acllist[ctx->idx++].name; return 1; } void acl_backend_vfile_nonowner_iter_deinit(struct acl_mailbox_list_context *ctx) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)ctx->backend; backend->iterating_acllist = FALSE; i_free(ctx); } int acl_backend_vfile_nonowner_lookups_rebuild(struct acl_backend *_backend) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; return acl_backend_vfile_acllist_rebuild(backend); } dovecot-2.2.33.2/src/plugins/acl/acl-api-private.h0000644000175000017500000001040513165463624016475 00000000000000#ifndef ACL_API_PRIVATE_H #define ACL_API_PRIVATE_H #include "acl-api.h" #define ACL_ID_NAME_ANYONE "anyone" #define ACL_ID_NAME_AUTHENTICATED "authenticated" #define ACL_ID_NAME_OWNER "owner" #define ACL_ID_NAME_USER_PREFIX "user=" #define ACL_ID_NAME_GROUP_PREFIX "group=" #define ACL_ID_NAME_GROUP_OVERRIDE_PREFIX "group-override=" struct acl_backend_vfuncs { struct acl_backend *(*alloc)(void); int (*init)(struct acl_backend *backend, const char *data); void (*deinit)(struct acl_backend *backend); struct acl_mailbox_list_context * (*nonowner_lookups_iter_init)(struct acl_backend *backend); int (*nonowner_lookups_iter_next)(struct acl_mailbox_list_context *ctx, const char **name_r); void (*nonowner_lookups_iter_deinit) (struct acl_mailbox_list_context *ctx); int (*nonowner_lookups_rebuild)(struct acl_backend *backend); struct acl_object *(*object_init)(struct acl_backend *backend, const char *name); struct acl_object *(*object_init_parent)(struct acl_backend *backend, const char *child_name); void (*object_deinit)(struct acl_object *aclobj); int (*object_refresh_cache)(struct acl_object *aclobj); int (*object_update)(struct acl_object *aclobj, const struct acl_rights_update *update); int (*last_changed)(struct acl_object *aclobj, time_t *last_changed_r); struct acl_object_list_iter * (*object_list_init)(struct acl_object *aclobj); int (*object_list_next)(struct acl_object_list_iter *iter, struct acl_rights *rights_r); void (*object_list_deinit)(struct acl_object_list_iter *iter); }; struct acl_backend { pool_t pool; const char *username; const char **groups; unsigned int group_count; struct mailbox_list *list; struct acl_cache *cache; struct acl_global_file *global_file; struct acl_object *default_aclobj; struct acl_mask *default_aclmask; const char *const *default_rights; struct acl_backend_vfuncs v; unsigned int owner:1; unsigned int debug:1; unsigned int globals_only:1; }; struct acl_mailbox_list_context { struct acl_backend *backend; }; struct acl_object { struct acl_backend *backend; char *name; pool_t rights_pool; ARRAY_TYPE(acl_rights) rights; }; struct acl_object_list_iter { struct acl_object *aclobj; pool_t pool; struct acl_rights *rights; unsigned int idx, count; unsigned int failed:1; }; extern const char *const all_mailbox_rights[]; struct acl_object_list_iter * acl_default_object_list_init(struct acl_object *aclobj); int acl_default_object_list_next(struct acl_object_list_iter *iter, struct acl_rights *rights_r); void acl_default_object_list_deinit(struct acl_object_list_iter *iter); const char *const * acl_backend_mask_get_names(struct acl_backend *backend, const struct acl_mask *mask, pool_t pool); struct acl_object *acl_backend_get_default_object(struct acl_backend *backend); int acl_backend_get_default_rights(struct acl_backend *backend, const struct acl_mask **mask_r); void acl_rights_write_id(string_t *dest, const struct acl_rights *right); bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights); int acl_identifier_parse(const char *line, struct acl_rights *rights); int acl_rights_update_import(struct acl_rights_update *update, const char *id, const char *const *rights, const char **error_r); const char *acl_rights_export(const struct acl_rights *rights); int acl_rights_parse_line(const char *line, pool_t pool, struct acl_rights *rights_r, const char **error_r); void acl_rights_dup(const struct acl_rights *src, pool_t pool, struct acl_rights *dest_r); int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2); void acl_rights_sort(struct acl_object *aclobj); const char *const * acl_right_names_parse(pool_t pool, const char *acl, const char **error_r); void acl_right_names_write(string_t *dest, const char *const *rights); void acl_right_names_merge(pool_t pool, const char *const **destp, const char *const *src, bool dup_strings); bool acl_right_names_modify(pool_t pool, const char *const **rightsp, const char *const *modify_rights, enum acl_modify_mode modify_mode); void acl_object_rebuild_cache(struct acl_object *aclobj); void acl_object_remove_all_access(struct acl_object *aclobj); void acl_object_add_global_acls(struct acl_object *aclobj); #endif dovecot-2.2.33.2/src/plugins/acl/acl-shared-storage.c0000644000175000017500000000605213165463624017162 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "str.h" #include "var-expand.h" #include "acl-plugin.h" #include "acl-lookup-dict.h" #include "acl-shared-storage.h" #include "index/shared/shared-storage.h" #define SHARED_NS_RETRY_SECS (60*60) static bool acl_ns_prefix_exists(struct mail_namespace *ns) { struct mailbox *box; const char *vname; enum mailbox_existence existence; bool ret; if (ns->list->mail_set->mail_shared_explicit_inbox) return FALSE; vname = t_strndup(ns->prefix, ns->prefix_len-1); box = mailbox_alloc(ns->list, vname, 0); ret = mailbox_exists(box, FALSE, &existence) == 0 && existence == MAILBOX_EXISTENCE_SELECT; mailbox_free(&box); return ret; } static void acl_shared_namespace_add(struct mail_namespace *ns, struct mail_storage *storage, const char *userdomain) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { '\0', NULL, NULL } }; struct shared_storage *sstorage = (struct shared_storage *)storage; struct mail_namespace *new_ns = ns; struct var_expand_table *tab; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; const char *p, *mailbox; string_t *str; if (strcmp(ns->user->username, userdomain) == 0) { /* skip ourself */ return; } p = strchr(userdomain, '@'); tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = userdomain; tab[1].value = p == NULL ? userdomain : t_strdup_until(userdomain, p); tab[2].value = p == NULL ? "" : p + 1; str = t_str_new(128); var_expand(str, sstorage->ns_prefix_pattern, tab); mailbox = str_c(str); if (shared_storage_get_namespace(&new_ns, &mailbox) < 0) return; /* check if there are any mailboxes really visible to us */ iter = mailbox_list_iter_init(new_ns->list, "*", MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) break; (void)mailbox_list_iter_deinit(&iter); if (info == NULL && !acl_ns_prefix_exists(new_ns)) { /* no visible mailboxes, remove the namespace */ mail_namespace_destroy(new_ns); } } int acl_shared_namespaces_add(struct mail_namespace *ns) { struct acl_user *auser = ACL_USER_CONTEXT(ns->user); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); struct mail_storage *storage = mail_namespace_get_default_storage(ns); struct acl_lookup_dict_iter *iter; const char *name; i_assert(ns->type == MAIL_NAMESPACE_TYPE_SHARED); i_assert(strcmp(storage->name, MAIL_SHARED_STORAGE_NAME) == 0); if (ioloop_time < alist->last_shared_add_check + SHARED_NS_RETRY_SECS) { /* already added, don't bother rechecking */ return 0; } alist->last_shared_add_check = ioloop_time; iter = acl_lookup_dict_iterate_visible_init(auser->acl_lookup_dict); while ((name = acl_lookup_dict_iterate_visible_next(iter)) != NULL) { T_BEGIN { acl_shared_namespace_add(ns, storage, name); } T_END; } return acl_lookup_dict_iterate_visible_deinit(&iter); } dovecot-2.2.33.2/src/plugins/acl/doveadm-acl.c0000644000175000017500000004176513165463624015703 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "module-dir.h" #include "imap-util.h" #include "acl-plugin.h" #include "acl-api-private.h" #include "acl-lookup-dict.h" #include "doveadm-print.h" #include "doveadm-mail.h" struct doveadm_acl_cmd_context { struct doveadm_mail_cmd_context ctx; bool get_match_me; enum acl_modify_mode modify_mode; }; const char *doveadm_acl_plugin_version = DOVECOT_ABI_VERSION; void doveadm_acl_plugin_init(struct module *module); void doveadm_acl_plugin_deinit(void); static int cmd_acl_mailbox_open(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, const char *mailbox, struct mailbox **box_r) { struct acl_user *auser = ACL_USER_CONTEXT(user); struct mail_namespace *ns; struct mailbox *box; if (auser == NULL) { i_error("ACL not enabled for %s", user->username); doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } ns = mail_namespace_find(user->namespaces, mailbox); box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); if (mailbox_open(box) < 0) { i_error("Can't open mailbox %s: %s", mailbox, mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(ctx, box); mailbox_free(&box); return -1; } *box_r = box; return 0; } static void cmd_acl_get_right(const struct acl_rights *rights) { string_t *str; doveadm_print(acl_rights_get_id(rights)); if (rights->global) doveadm_print("global"); else doveadm_print(""); str = t_str_new(256); if (rights->rights != NULL) str_append(str, t_strarray_join(rights->rights, " ")); if (rights->neg_rights != NULL) { if (str_len(str) > 0) str_append_c(str, ' '); str_append_c(str, '-'); str_append(str, t_strarray_join(rights->neg_rights, " -")); } doveadm_print(str_c(str)); } static int cmd_acl_get_mailbox(struct doveadm_acl_cmd_context *ctx, struct mailbox *box) { struct acl_object *aclobj = acl_mailbox_get_aclobj(box); struct acl_backend *backend; struct acl_object_list_iter *iter; struct acl_rights rights; int ret; backend = acl_mailbox_list_get_backend(box->list); iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) T_BEGIN { if (!ctx->get_match_me || acl_backend_rights_match_me(backend, &rights)) cmd_acl_get_right(&rights); } T_END; acl_object_list_deinit(&iter); if (ret < 0) { i_error("ACL iteration failed"); doveadm_mail_failed_error(&ctx->ctx, MAIL_ERROR_TEMP); } return ret; } static int cmd_acl_get_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct doveadm_acl_cmd_context *ctx = (struct doveadm_acl_cmd_context *)_ctx; const char *mailbox = _ctx->args[0]; struct mailbox *box; int ret; if (cmd_acl_mailbox_open(_ctx, user, mailbox, &box) < 0) return -1; ret = cmd_acl_get_mailbox(ctx, box); mailbox_free(&box); return ret; } static bool cmd_acl_get_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct doveadm_acl_cmd_context *ctx = (struct doveadm_acl_cmd_context *)_ctx; switch (c) { case 'm': ctx->get_match_me = TRUE; break; default: return FALSE; } return TRUE; } static void cmd_acl_get_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("acl get"); doveadm_print_header("id", "ID", 0); doveadm_print_header("global", "Global", 0); doveadm_print_header("rights", "Rights", 0); } static struct doveadm_mail_cmd_context * cmd_acl_get_alloc(void) { struct doveadm_acl_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_acl_cmd_context); ctx->ctx.getopt_args = "m"; ctx->ctx.v.parse_arg = cmd_acl_get_parse_arg; ctx->ctx.v.run = cmd_acl_get_run; ctx->ctx.v.init = cmd_acl_get_init; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); return &ctx->ctx; } static int cmd_acl_rights_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const char *mailbox = ctx->args[0]; struct mailbox *box; struct acl_object *aclobj; const char *const *rights; int ret = 0; if (cmd_acl_mailbox_open(ctx, user, mailbox, &box) < 0) return -1; aclobj = acl_mailbox_get_aclobj(box); if (acl_object_get_my_rights(aclobj, pool_datastack_create(), &rights) < 0) { doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); i_error("Failed to get rights"); ret = -1; } else { doveadm_print(t_strarray_join(rights, " ")); } mailbox_free(&box); return ret; } static void cmd_acl_rights_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("acl rights"); doveadm_print_header("rights", "Rights", 0); } static struct doveadm_mail_cmd_context * cmd_acl_rights_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_acl_rights_run; ctx->v.init = cmd_acl_rights_init; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); return ctx; } static int cmd_acl_mailbox_update(struct mailbox *box, const struct acl_rights_update *update) { struct mailbox_transaction_context *t; int ret; t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); ret = acl_mailbox_update_acl(t, update); if (mailbox_transaction_commit(&t) < 0) ret = -1; return ret; } static int cmd_acl_set_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct doveadm_acl_cmd_context *ctx = (struct doveadm_acl_cmd_context *)_ctx; const char *mailbox = _ctx->args[0], *id = _ctx->args[1]; const char *const *rights = _ctx->args + 2; struct mailbox *box; struct acl_rights_update update; const char *error; int ret; if (cmd_acl_mailbox_open(_ctx, user, mailbox, &box) < 0) return -1; i_zero(&update); update.modify_mode = ctx->modify_mode; update.neg_modify_mode = ctx->modify_mode; if (acl_rights_update_import(&update, id, rights, &error) < 0) i_fatal_status(EX_USAGE, "%s", error); if ((ret = cmd_acl_mailbox_update(box, &update)) < 0) { i_error("Failed to set ACL: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_error(_ctx, MAIL_ERROR_TEMP); } mailbox_free(&box); return ret; } static void cmd_acl_set_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (str_array_length(args) < 3) doveadm_mail_help_name("acl set"); } static struct doveadm_mail_cmd_context * cmd_acl_change_alloc(enum acl_modify_mode modify_mode) { struct doveadm_acl_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_acl_cmd_context); ctx->ctx.v.run = cmd_acl_set_run; ctx->ctx.v.init = cmd_acl_set_init; ctx->modify_mode = modify_mode; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_acl_set_alloc(void) { return cmd_acl_change_alloc(ACL_MODIFY_MODE_REPLACE); } static struct doveadm_mail_cmd_context *cmd_acl_add_alloc(void) { return cmd_acl_change_alloc(ACL_MODIFY_MODE_ADD); } static struct doveadm_mail_cmd_context *cmd_acl_remove_alloc(void) { return cmd_acl_change_alloc(ACL_MODIFY_MODE_REMOVE); } static int cmd_acl_delete_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const char *mailbox = ctx->args[0], *id = ctx->args[1]; struct mailbox *box; struct acl_rights_update update; const char *error; int ret = 0; if (cmd_acl_mailbox_open(ctx, user, mailbox, &box) < 0) return -1; i_zero(&update); if (acl_rights_update_import(&update, id, NULL, &error) < 0) i_fatal_status(EX_USAGE, "%s", error); if ((ret = cmd_acl_mailbox_update(box, &update)) < 0) { i_error("Failed to delete ACL: %s", mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); } mailbox_free(&box); return ret; } static void cmd_acl_delete_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (str_array_length(args) < 2) doveadm_mail_help_name("acl delete"); } static struct doveadm_mail_cmd_context * cmd_acl_delete_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_acl_delete_run; ctx->v.init = cmd_acl_delete_init; return ctx; } static int cmd_acl_recalc_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { struct acl_user *auser = ACL_USER_CONTEXT(user); if (auser == NULL) { i_error("ACL not enabled for %s", user->username); doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } if (acl_lookup_dict_rebuild(auser->acl_lookup_dict) < 0) { i_error("Failed to recalculate ACL dicts"); doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); return -1; } return 0; } static struct doveadm_mail_cmd_context * cmd_acl_recalc_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_acl_recalc_run; return ctx; } static int cmd_acl_debug_mailbox_open(struct doveadm_mail_cmd_context *ctx, struct mail_user *user, const char *mailbox, struct mailbox **box_r) { struct acl_user *auser = ACL_USER_CONTEXT(user); struct mail_namespace *ns; struct mailbox *box; const char *path, *errstr; enum mail_error error; ns = mail_namespace_find(user->namespaces, mailbox); box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); if (mailbox_open(box) < 0) { errstr = mail_storage_get_last_internal_error(box->storage, &error); errstr = t_strdup(errstr); doveadm_mail_failed_error(ctx, error); if (error != MAIL_ERROR_NOTFOUND || mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0) i_error("Can't open mailbox %s: %s", mailbox, errstr); else { i_error("Mailbox '%s' in namespace '%s' doesn't exist in %s", box->name, ns->prefix, path); } mailbox_free(&box); return -1; } if (auser == NULL) { i_info("ACL not enabled for user %s, mailbox can be accessed", user->username); doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); mailbox_free(&box); return -1; } *box_r = box; return 0; } static bool cmd_acl_debug_mailbox(struct mailbox *box, bool *retry_r) { struct mail_namespace *ns = mailbox_get_namespace(box); struct acl_user *auser = ACL_USER_CONTEXT(ns->user); struct acl_object *aclobj = acl_mailbox_get_aclobj(box); struct acl_backend *backend = acl_mailbox_list_get_backend(box->list); struct acl_mailbox_list_context *iter; struct acl_lookup_dict_iter *diter; const char *const *rights, *name, *path; enum mail_flags private_flags_mask; string_t *str; int ret; bool all_ok = TRUE; *retry_r = FALSE; i_info("Mailbox '%s' is in namespace '%s'", box->name, box->list->ns->prefix); if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) > 0) i_info("Mailbox path: %s", path); private_flags_mask = mailbox_get_private_flags_mask(box); if (private_flags_mask == 0) i_info("All message flags are shared across users in mailbox"); else { str = t_str_new(64); imap_write_flags(str, private_flags_mask, NULL); i_info("Per-user private flags in mailbox: %s", str_c(str)); } /* check if user has lookup right */ if (acl_object_get_my_rights(aclobj, pool_datastack_create(), &rights) < 0) i_fatal("Failed to get rights"); if (rights[0] == NULL) i_info("User %s has no rights for mailbox", ns->user->username); else { i_info("User %s has rights: %s", ns->user->username, t_strarray_join(rights, " ")); } if (!str_array_find(rights, MAIL_ACL_LOOKUP)) { i_error("User %s is missing 'lookup' right", ns->user->username); return FALSE; } /* check if mailbox is listable */ if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) { i_info("Mailbox in user's private namespace"); return TRUE; } iter = acl_backend_nonowner_lookups_iter_init(backend); while ((ret = acl_backend_nonowner_lookups_iter_next(iter, &name)) > 0) { if (strcmp(name, box->name) == 0) break; } acl_backend_nonowner_lookups_iter_deinit(&iter); if (ret < 0) i_fatal("ACL non-owner iteration failed"); if (ret == 0) { i_error("Mailbox not found from dovecot-acl-list, rebuilding"); if (acl_backend_nonowner_lookups_rebuild(backend) < 0) i_fatal("dovecot-acl-list rebuilding failed"); all_ok = FALSE; *retry_r = TRUE; } else { i_info("Mailbox found from dovecot-acl-list"); } if (ns->type == MAIL_NAMESPACE_TYPE_PUBLIC) { i_info("Mailbox is in public namespace"); return TRUE; } if (!acl_lookup_dict_is_enabled(auser->acl_lookup_dict)) { i_error("acl_lookup_dict not enabled"); return FALSE; } /* shared namespace. see if it's in acl lookup dict */ diter = acl_lookup_dict_iterate_visible_init(auser->acl_lookup_dict); while ((name = acl_lookup_dict_iterate_visible_next(diter)) != NULL) { if (strcmp(name, ns->owner->username) == 0) break; } if (acl_lookup_dict_iterate_visible_deinit(&diter) < 0) i_fatal("ACL shared dict iteration failed"); if (name == NULL) { i_error("User %s not found from ACL shared dict, rebuilding", ns->owner->username); if (acl_lookup_dict_rebuild(auser->acl_lookup_dict) < 0) i_fatal("ACL lookup dict rebuild failed"); all_ok = FALSE; *retry_r = TRUE; } else { i_info("User %s found from ACL shared dict", ns->owner->username); } return all_ok; } static int cmd_acl_debug_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const char *mailbox = ctx->args[0]; struct mailbox *box; bool ret, retry; if (cmd_acl_debug_mailbox_open(ctx, user, mailbox, &box) < 0) return -1; ret = cmd_acl_debug_mailbox(box, &retry); if (!ret && retry) { i_info("Retrying after rebuilds:"); ret = cmd_acl_debug_mailbox(box, &retry); } if (ret) i_info("Mailbox %s is visible in LIST", box->vname); else i_info("Mailbox %s is NOT visible in LIST", box->vname); mailbox_free(&box); return 0; } static void cmd_acl_debug_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("acl debug"); } static struct doveadm_mail_cmd_context * cmd_acl_debug_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_acl_debug_run; ctx->v.init = cmd_acl_debug_init; return ctx; } static struct doveadm_cmd_ver2 acl_commands[] = { { .name = "acl get", .mail_cmd = cmd_acl_get_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-m] ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('m', "match-me", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl rights", .mail_cmd = cmd_acl_rights_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl set", .mail_cmd = cmd_acl_set_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " [ ...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "id", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "right", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl add", .mail_cmd = cmd_acl_add_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " [ ...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "id", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "right", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl remove", .mail_cmd = cmd_acl_remove_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " [ ...]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "id", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "right", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl delete", .mail_cmd = cmd_acl_delete_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX " ", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAM('\0', "id", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "acl recalc", .mail_cmd = cmd_acl_recalc_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX, DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAMS_END }, { .name = "acl debug", .mail_cmd = cmd_acl_debug_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END } }; void doveadm_acl_plugin_init(struct module *module ATTR_UNUSED) { unsigned int i; for (i = 0; i < N_ELEMENTS(acl_commands); i++) doveadm_cmd_register_ver2(&acl_commands[i]); } void doveadm_acl_plugin_deinit(void) { } dovecot-2.2.33.2/src/plugins/acl/acl-storage.c0000644000175000017500000000322613123174404015703 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "mail-namespace.h" #include "mailbox-list-private.h" #include "acl-api-private.h" #include "acl-lookup-dict.h" #include "acl-plugin.h" struct acl_storage_module acl_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); struct acl_user_module acl_user_module = MODULE_CONTEXT_INIT(&mail_user_module_register); static void acl_user_deinit(struct mail_user *user) { struct acl_user *auser = ACL_USER_CONTEXT(user); acl_lookup_dict_deinit(&auser->acl_lookup_dict); auser->module_ctx.super.deinit(user); } static void acl_mail_user_create(struct mail_user *user, const char *env) { struct mail_user_vfuncs *v = user->vlast; struct acl_user *auser; auser = p_new(user->pool, struct acl_user, 1); auser->module_ctx.super = *v; user->vlast = &auser->module_ctx.super; v->deinit = acl_user_deinit; auser->acl_lookup_dict = acl_lookup_dict_init(user); auser->acl_env = env; auser->acl_user = mail_user_plugin_getenv(user, "acl_user"); if (auser->acl_user == NULL) auser->acl_user = mail_user_plugin_getenv(user, "master_user"); env = mail_user_plugin_getenv(user, "acl_groups"); if (env != NULL) { auser->groups = (const char *const *)p_strsplit(user->pool, env, ","); } MODULE_CONTEXT_SET(user, acl_user_module, auser); } void acl_mail_user_created(struct mail_user *user) { const char *env; env = mail_user_plugin_getenv(user, "acl"); if (env != NULL && *env != '\0') acl_mail_user_create(user, env); else { if (user->mail_debug) i_debug("acl: No acl setting - ACLs are disabled"); } } dovecot-2.2.33.2/src/plugins/acl/acl-cache.h0000644000175000017500000000402513123174404015305 00000000000000#ifndef ACL_CACHE_H #define ACL_CACHE_H struct acl_backend; struct acl_rights_update; struct acl_mask { pool_t pool; /* mask[] size as bytes */ unsigned int size; /* variable length bitmask */ unsigned char mask[1]; }; #define SIZEOF_ACL_MASK(bitmask_size) \ (MALLOC_ADD((bitmask_size), sizeof(pool_t) + sizeof(unsigned int))) struct acl_cache *acl_cache_init(struct acl_backend *backend, size_t validity_rec_size); void acl_cache_deinit(struct acl_cache **cache); struct acl_mask *acl_cache_mask_init(struct acl_cache *cache, pool_t pool, const char *const *rights); void acl_cache_mask_deinit(struct acl_mask **mask); unsigned int acl_cache_right_lookup(struct acl_cache *cache, const char *right); /* Flush cache for given object name */ void acl_cache_flush(struct acl_cache *cache, const char *objname); /* Flush cache for all objects */ void acl_cache_flush_all(struct acl_cache *cache); /* Update object ACLs. The new rights are always applied on top of the existing rights. The ordering by acl_id_type must be done by the caller. */ void acl_cache_update(struct acl_cache *cache, const char *objname, const struct acl_rights_update *update); /* Return ACL object validity, or NULL if object doesn't exit. */ void *acl_cache_get_validity(struct acl_cache *cache, const char *objname); /* Update ACL object validity, creating the object if needed. */ void acl_cache_set_validity(struct acl_cache *cache, const char *objname, const void *validity); /* Returns all the right names currently created. The returned pointer may change after calling acl_cache_update(). */ const char *const *acl_cache_get_names(struct acl_cache *cache, unsigned int *count_r); /* Returns user's current rights, or NULL if no rights have been specified for this object. */ const struct acl_mask * acl_cache_get_my_rights(struct acl_cache *cache, const char *objname); /* Returns TRUE if given right index is set in mask. */ bool acl_cache_mask_isset(const struct acl_mask *mask, unsigned int right_idx); #endif dovecot-2.2.33.2/src/plugins/acl/acl-mailbox-list.c0000644000175000017500000004407113165463624016661 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "imap-match.h" #include "mailbox-tree.h" #include "mail-namespace.h" #include "mailbox-list-iter-private.h" #include "acl-api-private.h" #include "acl-cache.h" #include "acl-shared-storage.h" #include "acl-plugin.h" #define MAILBOX_FLAG_MATCHED 0x40000000 struct acl_mailbox_list_iterate_context { union mailbox_list_iterate_module_context module_ctx; struct mailbox_tree_context *lookup_boxes; struct mailbox_info info; char sep; unsigned int hide_nonlistable_subscriptions:1; unsigned int simple_star_glob:1; unsigned int autocreate_acls_checked:1; }; static const char *acl_storage_right_names[ACL_STORAGE_RIGHT_COUNT] = { MAIL_ACL_LOOKUP, MAIL_ACL_READ, MAIL_ACL_WRITE, MAIL_ACL_WRITE_SEEN, MAIL_ACL_WRITE_DELETED, MAIL_ACL_INSERT, MAIL_ACL_POST, MAIL_ACL_EXPUNGE, MAIL_ACL_CREATE, MAIL_ACL_DELETE, MAIL_ACL_ADMIN }; #define ACL_LIST_ITERATE_CONTEXT(obj) \ MODULE_CONTEXT(obj, acl_mailbox_list_module) struct acl_mailbox_list_module acl_mailbox_list_module = MODULE_CONTEXT_INIT(&mailbox_list_module_register); struct acl_backend *acl_mailbox_list_get_backend(struct mailbox_list *list) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); return alist->rights.backend; } int acl_mailbox_list_have_right(struct mailbox_list *list, const char *name, bool parent, unsigned int acl_storage_right_idx, bool *can_see_r) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct acl_backend *backend = alist->rights.backend; const unsigned int *idx_arr = alist->rights.acl_storage_right_idx; struct acl_object *aclobj; int ret, ret2; aclobj = !parent ? acl_object_init_from_name(backend, name) : acl_object_init_from_parent(backend, name); ret = acl_object_have_right(aclobj, idx_arr[acl_storage_right_idx]); if (can_see_r != NULL) { ret2 = acl_object_have_right(aclobj, idx_arr[ACL_STORAGE_RIGHT_LOOKUP]); if (ret2 < 0) ret = -1; *can_see_r = ret2 > 0; } acl_object_deinit(&aclobj); if (ret < 0) mailbox_list_set_internal_error(list); return ret; } static void acl_mailbox_try_list_fast(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(_ctx->list); struct acl_backend *backend = alist->rights.backend; const unsigned int *idxp; const struct acl_mask *acl_mask; struct acl_mailbox_list_context *nonowner_list_ctx; struct mail_namespace *ns = _ctx->list->ns; struct mailbox_list_iter_update_context update_ctx; const char *name; int ret; if ((_ctx->flags & (MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) return; if (ns->type == MAIL_NAMESPACE_TYPE_PUBLIC) { /* mailboxes in public namespace should all be listable to someone. we don't benefit from fast listing. */ return; } /* if this namespace's default rights contain LOOKUP, we'll need to go through all mailboxes in any case. */ idxp = alist->rights.acl_storage_right_idx + ACL_STORAGE_RIGHT_LOOKUP; if (acl_backend_get_default_rights(backend, &acl_mask) < 0 || acl_cache_mask_isset(acl_mask, *idxp)) return; /* no LOOKUP right by default, we can optimize this */ i_zero(&update_ctx); update_ctx.iter_ctx = _ctx; update_ctx.glob = imap_match_init(pool_datastack_create(), "*", (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0, ctx->sep); update_ctx.match_parents = TRUE; update_ctx.tree_ctx = mailbox_tree_init(ctx->sep); nonowner_list_ctx = acl_backend_nonowner_lookups_iter_init(backend); while ((ret = acl_backend_nonowner_lookups_iter_next(nonowner_list_ctx, &name)) > 0) { T_BEGIN { const char *vname = mailbox_list_get_vname(ns->list, name); mailbox_list_iter_update(&update_ctx, vname); } T_END; } acl_backend_nonowner_lookups_iter_deinit(&nonowner_list_ctx); if (ret == 0) ctx->lookup_boxes = update_ctx.tree_ctx; else mailbox_tree_deinit(&update_ctx.tree_ctx); } static struct mailbox_list_iterate_context * acl_mailbox_list_iter_init_shared(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct mailbox_list_iterate_context *ctx; int ret; /* before listing anything add namespaces for all users who may have visible mailboxes */ ret = acl_shared_namespaces_add(list->ns); ctx = alist->module_ctx.super.iter_init(list, patterns, flags); if (ret < 0) ctx->failed = TRUE; return ctx; } static struct mailbox_list_iterate_context * acl_mailbox_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct mailbox_list_iterate_context *_ctx; struct acl_mailbox_list_iterate_context *ctx; const char *p; unsigned int i; _ctx = alist->module_ctx.super.iter_init(list, patterns, flags); ctx = p_new(_ctx->pool, struct acl_mailbox_list_iterate_context, 1); if (list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE && (list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) { /* non-private namespace with subscriptions=yes. this could be a site-global subscriptions file, so hide subscriptions for mailboxes the user doesn't see. */ ctx->hide_nonlistable_subscriptions = TRUE; } ctx->sep = mail_namespace_get_sep(list->ns); /* see if all patterns have only a single '*' and it's at the end. we can use it to do some optimizations. */ ctx->simple_star_glob = TRUE; for (i = 0; patterns[i] != NULL; i++) { p = strchr(patterns[i], '*'); if (p == NULL || p[1] != '\0') { ctx->simple_star_glob = FALSE; break; } } MODULE_CONTEXT_SET(_ctx, acl_mailbox_list_module, ctx); /* Try to avoid reading ACLs from all mailboxes by getting a smaller list of mailboxes that have even potential to be visible. If we couldn't get such a list, we'll go through all mailboxes. */ T_BEGIN { acl_mailbox_try_list_fast(_ctx); } T_END; return _ctx; } static const struct mailbox_info * acl_mailbox_list_iter_next_info(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(_ctx->list); const struct mailbox_info *info; while ((info = alist->module_ctx.super.iter_next(_ctx)) != NULL) { /* if we've a list of mailboxes with LOOKUP rights, skip the mailboxes not in the list (since we know they can't be visible to us). */ if (ctx->lookup_boxes == NULL || mailbox_tree_lookup(ctx->lookup_boxes, info->vname) != NULL) break; if (_ctx->list->ns->user->mail_debug) { i_debug("acl: Mailbox not in dovecot-acl-list: %s", info->vname); } } return info; } static const char * acl_mailbox_list_iter_get_name(struct mailbox_list_iterate_context *ctx, const char *vname) { struct mail_namespace *ns = ctx->list->ns; const char *name; size_t len; name = mailbox_list_get_storage_name(ns->list, vname); len = strlen(name); if (len > 0 && name[len-1] == mailbox_list_get_hierarchy_sep(ns->list)) { /* name ends with separator. this can happen if doing e.g. LIST "" foo/% and it lists "foo/". */ name = t_strndup(name, len-1); } return name; } static bool iter_is_listing_all_children(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); const char *child; /* If all patterns (with '.' separator) are in "name*", "name.*" or "%.*" style format, simple_star_glob=TRUE and we can easily test this by simply checking if name/child mailbox matches. */ child = t_strdup_printf("%s%cx", ctx->info.vname, ctx->sep); return ctx->simple_star_glob && imap_match(_ctx->glob, child) == IMAP_MATCH_YES; } static bool iter_mailbox_has_visible_children(struct mailbox_list_iterate_context *_ctx, bool only_nonpatterns, bool subscribed) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; string_t *pattern; const char *prefix; size_t i, prefix_len; bool stars = FALSE, ret = FALSE; /* do we have child mailboxes with LOOKUP right that don't match the list pattern? */ if (ctx->lookup_boxes != NULL) { /* we have a list of mailboxes with LOOKUP rights. before starting the slow list iteration, check check first if there even are any children with LOOKUP rights. */ struct mailbox_node *node; node = mailbox_tree_lookup(ctx->lookup_boxes, ctx->info.vname); i_assert(node != NULL); if (node->children == NULL) return FALSE; } /* if mailbox name has '*' characters in it, they'll conflict with the LIST wildcard. replace then with '%' and verify later that all results have the correct prefix. */ pattern = t_str_new(128); for (i = 0; ctx->info.vname[i] != '\0'; i++) { if (ctx->info.vname[i] != '*') str_append_c(pattern, ctx->info.vname[i]); else { stars = TRUE; str_append_c(pattern, '%'); } } if (i > 0 && ctx->info.vname[i-1] != ctx->sep) str_append_c(pattern, ctx->sep); str_append_c(pattern, '*'); prefix = str_c(pattern); prefix_len = str_len(pattern) - 1; iter = mailbox_list_iter_init(_ctx->list, str_c(pattern), (!subscribed ? 0 : MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (only_nonpatterns && imap_match(_ctx->glob, info->vname) == IMAP_MATCH_YES) { /* at least one child matches also the original list patterns. we don't need to show this mailbox. */ ret = FALSE; break; } if (!stars || strncmp(info->vname, prefix, prefix_len) == 0) ret = TRUE; } (void)mailbox_list_iter_deinit(&iter); return ret; } static int acl_mailbox_list_info_is_visible(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); #define PRESERVE_MAILBOX_FLAGS (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED) struct mailbox_info *info = &ctx->info; const char *acl_name; int ret; if ((_ctx->flags & MAILBOX_LIST_ITER_RAW_LIST) != 0) { /* skip ACL checks. */ return 1; } if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 && (_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && !ctx->hide_nonlistable_subscriptions) { /* don't waste time doing an ACL check. we're going to list all subscriptions anyway. */ info->flags &= MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED; return 1; } acl_name = acl_mailbox_list_iter_get_name(_ctx, info->vname); ret = acl_mailbox_list_have_right(_ctx->list, acl_name, FALSE, ACL_STORAGE_RIGHT_LOOKUP, NULL); if (ret != 0) { if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0) { /* don't waste time checking if there are visible children, but also don't return incorrect flags */ info->flags &= ~MAILBOX_CHILDREN; } else if ((info->flags & MAILBOX_CHILDREN) != 0 && !iter_mailbox_has_visible_children(_ctx, FALSE, FALSE)) { info->flags &= ~MAILBOX_CHILDREN; info->flags |= MAILBOX_NOCHILDREN; } return ret; } /* no permission to see this mailbox */ if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* we're listing subscribed mailboxes. this one or its child is subscribed, so we'll need to list it. but since we don't have LOOKUP right, we'll need to show it as nonexistent. */ i_assert((info->flags & PRESERVE_MAILBOX_FLAGS) != 0); info->flags = MAILBOX_NONEXISTENT | (info->flags & PRESERVE_MAILBOX_FLAGS); if (ctx->hide_nonlistable_subscriptions) { /* global subscriptions file. hide this entry if there are no visible subscribed children or if we're going to list the subscribed children anyway. */ if ((info->flags & MAILBOX_CHILD_SUBSCRIBED) == 0) return 0; if (iter_is_listing_all_children(_ctx) || !iter_mailbox_has_visible_children(_ctx, TRUE, TRUE)) return 0; /* e.g. LSUB "" % with visible subscribed children */ } return 1; } if (!iter_is_listing_all_children(_ctx) && iter_mailbox_has_visible_children(_ctx, TRUE, FALSE)) { /* no child mailboxes match the list pattern(s), but mailbox has visible children. we'll need to show this as non-existent. */ info->flags = MAILBOX_NONEXISTENT | MAILBOX_CHILDREN | (info->flags & PRESERVE_MAILBOX_FLAGS); return 1; } return 0; } static int acl_mailbox_list_iter_check_autocreate_acls(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); struct mailbox_settings *const *box_sets; unsigned int i, count; int ret; ctx->autocreate_acls_checked = TRUE; if (_ctx->autocreate_ctx == NULL) return 0; if ((_ctx->flags & MAILBOX_LIST_ITER_RAW_LIST) != 0) { /* skip ACL checks. */ return 0; } box_sets = array_get(&_ctx->autocreate_ctx->box_sets, &count); i_assert(array_count(&_ctx->autocreate_ctx->boxes) == count); for (i = 0; i < count; ) { const char *acl_name = acl_mailbox_list_iter_get_name(_ctx, box_sets[i]->name); ret = acl_mailbox_list_have_right(_ctx->list, acl_name, FALSE, ACL_STORAGE_RIGHT_LOOKUP, NULL); if (ret < 0) return -1; if (ret > 0) i++; else { /* no list right - remove the whole autobox */ array_delete(&_ctx->autocreate_ctx->box_sets, i, 1); array_delete(&_ctx->autocreate_ctx->boxes, i, 1); box_sets = array_get(&_ctx->autocreate_ctx->box_sets, &count); } } return 0; } static const struct mailbox_info * acl_mailbox_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); const struct mailbox_info *info; int ret; if (!ctx->autocreate_acls_checked) { if (acl_mailbox_list_iter_check_autocreate_acls(_ctx) < 0) { _ctx->failed = TRUE; return NULL; } } while ((info = acl_mailbox_list_iter_next_info(_ctx)) != NULL) { ctx->info = *info; T_BEGIN { ret = acl_mailbox_list_info_is_visible(_ctx); } T_END; if (ret > 0) break; if (ret < 0) { _ctx->failed = TRUE; return NULL; } /* skip to next one */ if (_ctx->list->ns->user->mail_debug) { i_debug("acl: No lookup right to mailbox: %s", info->vname); } } return info == NULL ? NULL : &ctx->info; } static int acl_mailbox_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(_ctx->list); int ret = _ctx->failed ? -1 : 0; if (ctx->lookup_boxes != NULL) mailbox_tree_deinit(&ctx->lookup_boxes); if (alist->module_ctx.super.iter_deinit(_ctx) < 0) ret = -1; return ret; } static void acl_mailbox_list_deinit(struct mailbox_list *list) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); if (alist->rights.backend != NULL) acl_backend_deinit(&alist->rights.backend); alist->module_ctx.super.deinit(list); } static void acl_mailbox_list_init_shared(struct mailbox_list *list) { struct acl_mailbox_list *alist; struct mailbox_list_vfuncs *v = list->vlast; alist = p_new(list->pool, struct acl_mailbox_list, 1); alist->module_ctx.super = *v; list->vlast = &alist->module_ctx.super; v->deinit = acl_mailbox_list_deinit; v->iter_init = acl_mailbox_list_iter_init_shared; MODULE_CONTEXT_SET(list, acl_mailbox_list_module, alist); } static void acl_storage_rights_ctx_init(struct acl_storage_rights_context *ctx, struct acl_backend *backend) { unsigned int i; ctx->backend = backend; for (i = 0; i < ACL_STORAGE_RIGHT_COUNT; i++) { ctx->acl_storage_right_idx[i] = acl_backend_lookup_right(backend, acl_storage_right_names[i]); } } static void acl_mailbox_list_init_default(struct mailbox_list *list) { struct mailbox_list_vfuncs *v = list->vlast; struct acl_mailbox_list *alist; if (list->mail_set->mail_full_filesystem_access) { /* not necessarily, but safer to do this for now. */ i_fatal("mail_full_filesystem_access=yes is " "incompatible with ACLs"); } alist = p_new(list->pool, struct acl_mailbox_list, 1); alist->module_ctx.super = *v; list->vlast = &alist->module_ctx.super; v->deinit = acl_mailbox_list_deinit; v->iter_init = acl_mailbox_list_iter_init; v->iter_next = acl_mailbox_list_iter_next; v->iter_deinit = acl_mailbox_list_iter_deinit; MODULE_CONTEXT_SET(list, acl_mailbox_list_module, alist); } void acl_mail_namespace_storage_added(struct mail_namespace *ns) { struct acl_user *auser = ACL_USER_CONTEXT(ns->user); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); struct acl_backend *backend; const char *current_username, *owner_username; bool owner = TRUE; if (alist == NULL) return; owner_username = ns->user->username; current_username = auser->acl_user; if (current_username == NULL) current_username = owner_username; else owner = strcmp(current_username, owner_username) == 0; /* We don't care about the username for non-private mailboxes. It's used only when checking if we're the mailbox owner. We never are for shared/public mailboxes. */ if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE) owner = FALSE; /* we need to know the storage when initializing backend */ backend = acl_backend_init(auser->acl_env, ns->list, current_username, auser->groups, owner); if (backend == NULL) i_fatal("ACL backend initialization failed"); acl_storage_rights_ctx_init(&alist->rights, backend); } void acl_mailbox_list_created(struct mailbox_list *list) { struct acl_user *auser = ACL_USER_CONTEXT(list->ns->user); if (auser == NULL) { /* ACLs disabled for this user */ } else if ((list->ns->flags & NAMESPACE_FLAG_NOACL) != 0) { /* no ACL checks for internal namespaces (lda, shared) */ if (list->ns->type == MAIL_NAMESPACE_TYPE_SHARED) acl_mailbox_list_init_shared(list); } else if ((list->ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) { /* this namespace is empty. don't attempt to lookup ACLs, because they're not going to work anyway and we could crash doing it. */ } else { acl_mailbox_list_init_default(list); } } dovecot-2.2.33.2/src/plugins/acl/acl-lookup-dict.c0000644000175000017500000002333313165463624016505 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "dict.h" #include "mail-user.h" #include "mail-namespace.h" #include "acl-api-private.h" #include "acl-storage.h" #include "acl-plugin.h" #include "acl-lookup-dict.h" #define DICT_SHARED_BOXES_PATH "shared-boxes/" struct acl_lookup_dict { struct mail_user *user; struct dict *dict; }; struct acl_lookup_dict_iter { pool_t pool; struct acl_lookup_dict *dict; pool_t iter_value_pool; ARRAY_TYPE(const_string) iter_ids; ARRAY_TYPE(const_string) iter_values; unsigned int iter_idx, iter_value_idx; unsigned int failed:1; }; struct acl_lookup_dict *acl_lookup_dict_init(struct mail_user *user) { struct acl_lookup_dict *dict; const char *uri, *error; dict = i_new(struct acl_lookup_dict, 1); dict->user = user; uri = mail_user_plugin_getenv(user, "acl_shared_dict"); if (uri != NULL) { if (dict_init(uri, DICT_DATA_TYPE_STRING, "", user->set->base_dir, &dict->dict, &error) < 0) i_error("acl: dict_init(%s) failed: %s", uri, error); } else if (user->mail_debug) { i_debug("acl: No acl_shared_dict setting - " "shared mailbox listing is disabled"); } return dict; } void acl_lookup_dict_deinit(struct acl_lookup_dict **_dict) { struct acl_lookup_dict *dict = *_dict; *_dict = NULL; if (dict->dict != NULL) dict_deinit(&dict->dict); i_free(dict); } bool acl_lookup_dict_is_enabled(struct acl_lookup_dict *dict) { return dict->dict != NULL; } static void acl_lookup_dict_write_rights_id(string_t *dest, const struct acl_rights *right) { switch (right->id_type) { case ACL_ID_ANYONE: case ACL_ID_AUTHENTICATED: /* don't bother separating these */ str_append(dest, "anyone"); break; case ACL_ID_USER: str_append(dest, "user/"); str_append(dest, right->identifier); break; case ACL_ID_GROUP: case ACL_ID_GROUP_OVERRIDE: str_append(dest, "group/"); str_append(dest, right->identifier); break; case ACL_ID_OWNER: case ACL_ID_TYPE_COUNT: i_unreached(); } } static bool acl_rights_is_same_user(const struct acl_rights *right, struct mail_user *user) { return right->id_type == ACL_ID_USER && strcmp(right->identifier, user->username) == 0; } static int acl_lookup_dict_rebuild_add_backend(struct mail_namespace *ns, ARRAY_TYPE(const_string) *ids) { struct acl_backend *backend; struct acl_mailbox_list_context *ctx; struct acl_object *aclobj; struct acl_object_list_iter *iter; struct acl_rights rights; const char *name, *id_dup; string_t *id; int ret, ret2 = 0; if ((ns->flags & NAMESPACE_FLAG_NOACL) != 0 || ns->owner == NULL || ACL_LIST_CONTEXT(ns->list) == NULL) return 0; id = t_str_new(128); backend = acl_mailbox_list_get_backend(ns->list); ctx = acl_backend_nonowner_lookups_iter_init(backend); while ((ret = acl_backend_nonowner_lookups_iter_next(ctx, &name)) > 0) { aclobj = acl_object_init_from_name(backend, name); iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { /* avoid pointless user -> user entries, which some clients do */ if (acl_rights_has_nonowner_lookup_changes(&rights) && !acl_rights_is_same_user(&rights, ns->owner)) { str_truncate(id, 0); acl_lookup_dict_write_rights_id(id, &rights); str_append_c(id, '/'); str_append(id, ns->owner->username); id_dup = t_strdup(str_c(id)); array_append(ids, &id_dup, 1); } } acl_object_list_deinit(&iter); if (ret < 0) ret2 = -1; acl_object_deinit(&aclobj); } acl_backend_nonowner_lookups_iter_deinit(&ctx); return ret < 0 || ret2 < 0 ? -1 : 0; } static int acl_lookup_dict_rebuild_update(struct acl_lookup_dict *dict, const ARRAY_TYPE(const_string) *new_ids_arr, bool no_removes) { const char *username = dict->user->username; struct dict_iterate_context *iter; struct dict_transaction_context *dt; const char *prefix, *key, *value, *const *old_ids, *const *new_ids, *p; ARRAY_TYPE(const_string) old_ids_arr; unsigned int newi, oldi, old_count, new_count; string_t *path; size_t prefix_len; int ret; /* get all existing identifiers for the user. we might be able to sync identifiers also for other users whose shared namespaces we have, but it's possible that the other users have other namespaces that aren't visible to us, so we don't want to remove anything that could break them. */ t_array_init(&old_ids_arr, 128); prefix = DICT_PATH_SHARED DICT_SHARED_BOXES_PATH; prefix_len = strlen(prefix); iter = dict_iterate_init(dict->dict, prefix, DICT_ITERATE_FLAG_RECURSE); while (dict_iterate(iter, &key, &value)) { /* prefix/$type/$dest/$source */ key += prefix_len; p = strrchr(key, '/'); if (p != NULL && strcmp(p + 1, username) == 0) { key = t_strdup_until(key, p); array_append(&old_ids_arr, &key, 1); } } if (dict_iterate_deinit(&iter) < 0) { i_error("acl: dict iteration failed, can't update dict"); return -1; } /* sort the existing identifiers */ array_sort(&old_ids_arr, i_strcmp_p); /* sync the identifiers */ path = t_str_new(256); str_append(path, prefix); dt = dict_transaction_begin(dict->dict); old_ids = array_get(&old_ids_arr, &old_count); new_ids = array_get(new_ids_arr, &new_count); for (newi = oldi = 0; newi < new_count || oldi < old_count; ) { ret = newi == new_count ? 1 : oldi == old_count ? -1 : strcmp(new_ids[newi], old_ids[oldi]); if (ret == 0) { newi++; oldi++; } else if (ret < 0) { /* new identifier, add it */ str_truncate(path, prefix_len); str_append(path, new_ids[newi]); dict_set(dt, str_c(path), "1"); newi++; } else if (!no_removes) { /* old identifier removed */ str_truncate(path, prefix_len); str_append(path, old_ids[oldi]); str_append_c(path, '/'); str_append(path, username); dict_unset(dt, str_c(path)); oldi++; } } if (dict_transaction_commit(&dt) < 0) { i_error("acl: dict commit failed"); return -1; } return 0; } int acl_lookup_dict_rebuild(struct acl_lookup_dict *dict) { struct mail_namespace *ns; ARRAY_TYPE(const_string) ids_arr; const char **ids; unsigned int i, dest, count; int ret = 0; if (dict->dict == NULL) return 0; /* get all ACL identifiers with a positive lookup right */ t_array_init(&ids_arr, 128); for (ns = dict->user->namespaces; ns != NULL; ns = ns->next) { if (acl_lookup_dict_rebuild_add_backend(ns, &ids_arr) < 0) ret = -1; } /* sort identifiers and remove duplicates */ array_sort(&ids_arr, i_strcmp_p); ids = array_get_modifiable(&ids_arr, &count); for (i = 1, dest = 0; i < count; i++) { if (strcmp(ids[dest], ids[i]) != 0) { if (++dest != i) ids[dest] = ids[i]; } } if (++dest < count) array_delete(&ids_arr, dest, count-dest); /* if lookup failed at some point we can still add new ids, but we can't remove any existing ones */ if (acl_lookup_dict_rebuild_update(dict, &ids_arr, ret < 0) < 0) ret = -1; return ret; } static void acl_lookup_dict_iterate_read(struct acl_lookup_dict_iter *iter) { struct dict_iterate_context *dict_iter; const char *const *idp, *prefix, *key, *value; size_t prefix_len; idp = array_idx(&iter->iter_ids, iter->iter_idx); iter->iter_idx++; iter->iter_value_idx = 0; prefix = t_strconcat(DICT_PATH_SHARED DICT_SHARED_BOXES_PATH, *idp, "/", NULL); prefix_len = strlen(prefix); /* read all of it to memory. at least currently dict-proxy can support only one iteration at a time, but the acl code can end up rebuilding the dict, which opens another iteration. */ p_clear(iter->iter_value_pool); array_clear(&iter->iter_values); dict_iter = dict_iterate_init(iter->dict->dict, prefix, DICT_ITERATE_FLAG_RECURSE); while (dict_iterate(dict_iter, &key, &value)) { i_assert(prefix_len < strlen(key)); key = p_strdup(iter->iter_value_pool, key + prefix_len); array_append(&iter->iter_values, &key, 1); } if (dict_iterate_deinit(&dict_iter) < 0) iter->failed = TRUE; } struct acl_lookup_dict_iter * acl_lookup_dict_iterate_visible_init(struct acl_lookup_dict *dict) { struct acl_user *auser = ACL_USER_CONTEXT(dict->user); struct acl_lookup_dict_iter *iter; const char *id; unsigned int i; pool_t pool; pool = pool_alloconly_create("acl lookup dict iter", 1024); iter = p_new(pool, struct acl_lookup_dict_iter, 1); iter->pool = pool; iter->dict = dict; p_array_init(&iter->iter_ids, pool, 16); id = "anyone"; array_append(&iter->iter_ids, &id, 1); id = p_strconcat(pool, "user/", dict->user->username, NULL); array_append(&iter->iter_ids, &id, 1); i_array_init(&iter->iter_values, 64); iter->iter_value_pool = pool_alloconly_create("acl lookup dict iter values", 1024); /* get all groups we belong to */ if (auser->groups != NULL) { for (i = 0; auser->groups[i] != NULL; i++) { id = p_strconcat(pool, "group/", auser->groups[i], NULL); array_append(&iter->iter_ids, &id, 1); } } /* iterate through all identifiers that match us, start with the first one */ if (dict->dict != NULL) acl_lookup_dict_iterate_read(iter); else array_clear(&iter->iter_ids); return iter; } const char * acl_lookup_dict_iterate_visible_next(struct acl_lookup_dict_iter *iter) { const char *const *keys; unsigned int count; keys = array_get(&iter->iter_values, &count); if (iter->iter_value_idx < count) return keys[iter->iter_value_idx++]; if (iter->iter_idx < array_count(&iter->iter_ids)) { /* get to the next iterator */ acl_lookup_dict_iterate_read(iter); return acl_lookup_dict_iterate_visible_next(iter); } return NULL; } int acl_lookup_dict_iterate_visible_deinit(struct acl_lookup_dict_iter **_iter) { struct acl_lookup_dict_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; array_free(&iter->iter_values); pool_unref(&iter->iter_value_pool); pool_unref(&iter->pool); return ret; } dovecot-2.2.33.2/src/plugins/acl/acl-backend-vfile-update.c0000644000175000017500000001644113123174404020214 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" #include "ioloop.h" #include "str.h" #include "strescape.h" #include "file-dotlock.h" #include "ostream.h" #include "mail-storage.h" #include "acl-cache.h" #include "acl-backend-vfile.h" #include #include static struct dotlock_settings dotlock_set = { .timeout = 30, .stale_timeout = 120 }; static int acl_backend_vfile_update_begin(struct acl_object_vfile *aclobj, struct dotlock **dotlock_r) { struct acl_object *_aclobj = &aclobj->aclobj; struct mailbox_permissions perm; int fd; if (aclobj->local_path == NULL) { i_error("Can't update acl object '%s': No local acl file path", aclobj->aclobj.name); return -1; } /* first lock the ACL file */ mailbox_list_get_permissions(_aclobj->backend->list, _aclobj->name, &perm); fd = file_dotlock_open_group(&dotlock_set, aclobj->local_path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, dotlock_r); if (fd == -1) { i_error("file_dotlock_open(%s) failed: %m", aclobj->local_path); return -1; } /* locked successfully, re-read the existing file to make sure we don't lose any changes. */ acl_cache_flush(_aclobj->backend->cache, _aclobj->name); if (_aclobj->backend->v.object_refresh_cache(_aclobj) < 0) { file_dotlock_delete(dotlock_r); return -1; } return fd; } static bool vfile_object_modify_right(struct acl_object *aclobj, unsigned int idx, const struct acl_rights_update *update) { struct acl_rights *right; bool c1, c2; right = array_idx_modifiable(&aclobj->rights, idx); c1 = acl_right_names_modify(aclobj->rights_pool, &right->rights, update->rights.rights, update->modify_mode); c2 = acl_right_names_modify(aclobj->rights_pool, &right->neg_rights, update->rights.neg_rights, update->neg_modify_mode); if (right->rights == NULL && right->neg_rights == NULL) { /* this identifier no longer exists */ array_delete(&aclobj->rights, idx, 1); c1 = TRUE; } return c1 || c2; } static bool vfile_object_add_right(struct acl_object *aclobj, unsigned int idx, const struct acl_rights_update *update) { struct acl_rights right; bool c1, c2; if (update->modify_mode == ACL_MODIFY_MODE_REMOVE && update->neg_modify_mode == ACL_MODIFY_MODE_REMOVE) { /* nothing to do */ return FALSE; } i_zero(&right); right.id_type = update->rights.id_type; right.identifier = p_strdup(aclobj->rights_pool, update->rights.identifier); c1 = acl_right_names_modify(aclobj->rights_pool, &right.rights, update->rights.rights, update->modify_mode); c2 = acl_right_names_modify(aclobj->rights_pool, &right.neg_rights, update->rights.neg_rights, update->neg_modify_mode); if (c1 || c2) { array_insert(&aclobj->rights, idx, &right, 1); return TRUE; } return FALSE; } static void vfile_write_right(string_t *dest, const struct acl_rights *right, bool neg) { const char *const *rights = neg ? right->neg_rights : right->rights; if (neg) str_append_c(dest,'-'); acl_rights_write_id(dest, right); if (strchr(str_c(dest), ' ') != NULL) T_BEGIN { /* need to escape it */ const char *escaped = t_strdup(str_escape(str_c(dest))); str_truncate(dest, 0); str_printfa(dest, "\"%s\"", escaped); } T_END; str_append_c(dest, ' '); acl_right_names_write(dest, rights); str_append_c(dest, '\n'); } static int acl_backend_vfile_update_write(struct acl_object *aclobj, int fd, const char *path) { struct ostream *output; string_t *str; const struct acl_rights *rights; unsigned int i, count; int ret = 0; output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_cork(output); str = str_new(default_pool, 256); /* rights are sorted with globals at the end, so we can stop at the first global */ rights = array_get(&aclobj->rights, &count); for (i = 0; i < count && !rights[i].global; i++) { if (rights[i].rights != NULL) { vfile_write_right(str, &rights[i], FALSE); o_stream_nsend(output, str_data(str), str_len(str)); str_truncate(str, 0); } if (rights[i].neg_rights != NULL) { vfile_write_right(str, &rights[i], TRUE); o_stream_nsend(output, str_data(str), str_len(str)); str_truncate(str, 0); } } str_free(&str); if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %s", path, o_stream_get_error(output)); ret = -1; } o_stream_destroy(&output); /* we really don't want to lose ACL files' contents, so fsync() always before renaming */ if (fsync(fd) < 0) { i_error("fsync(%s) failed: %m", path); ret = -1; } return ret; } static void acl_backend_vfile_update_cache(struct acl_object *_aclobj, int fd) { struct acl_backend_vfile_validity *validity; struct stat st; if (fstat(fd, &st) < 0) { /* we'll just recalculate or fail it later */ acl_cache_flush(_aclobj->backend->cache, _aclobj->name); return; } validity = acl_cache_get_validity(_aclobj->backend->cache, _aclobj->name); validity->local_validity.last_read_time = ioloop_time; validity->local_validity.last_mtime = st.st_mtime; validity->local_validity.last_size = st.st_size; } int acl_backend_vfile_object_update(struct acl_object *_aclobj, const struct acl_rights_update *update) { struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_aclobj->backend; struct acl_backend_vfile_validity *validity; struct dotlock *dotlock; struct utimbuf ut; time_t orig_mtime; const char *path; unsigned int i; int fd; bool changed; /* global ACLs can't be updated here */ i_assert(!update->rights.global); fd = acl_backend_vfile_update_begin(aclobj, &dotlock); if (fd == -1) return -1; if (!array_bsearch_insert_pos(&_aclobj->rights, &update->rights, acl_rights_cmp, &i)) changed = vfile_object_add_right(_aclobj, i, update); else changed = vfile_object_modify_right(_aclobj, i, update); if (!changed) { file_dotlock_delete(&dotlock); return 0; } validity = acl_cache_get_validity(_aclobj->backend->cache, _aclobj->name); orig_mtime = validity->local_validity.last_mtime; /* ACLs were really changed, write the new ones */ path = file_dotlock_get_lock_path(dotlock); if (acl_backend_vfile_update_write(_aclobj, fd, path) < 0) { file_dotlock_delete(&dotlock); acl_cache_flush(_aclobj->backend->cache, _aclobj->name); return -1; } if (orig_mtime < update->last_change && update->last_change != 0) { /* set mtime to last_change, if it's higher than the file's original mtime. if original mtime is higher, then we're merging some changes and it's better for the mtime to get updated. */ ut.actime = ioloop_time; ut.modtime = update->last_change; if (utime(path, &ut) < 0) i_error("utime(%s) failed: %m", path); } acl_backend_vfile_update_cache(_aclobj, fd); if (file_dotlock_replace(&dotlock, 0) < 0) { acl_cache_flush(_aclobj->backend->cache, _aclobj->name); return -1; } /* make sure dovecot-acl-list gets updated if we changed any lookup rights. */ if (acl_rights_has_nonowner_lookup_changes(&update->rights) || update->modify_mode == ACL_MODIFY_MODE_REPLACE || update->modify_mode == ACL_MODIFY_MODE_CLEAR) (void)acl_backend_vfile_acllist_rebuild(backend); return 0; } dovecot-2.2.33.2/src/plugins/acl/acl-backend-vfile.c0000644000175000017500000004337613167162046016752 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "istream.h" #include "nfs-workarounds.h" #include "mail-storage-private.h" #include "acl-global-file.h" #include "acl-cache.h" #include "acl-backend-vfile.h" #include #include #include #define ACL_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT #define ACL_VFILE_DEFAULT_CACHE_SECS 30 static struct acl_backend *acl_backend_vfile_alloc(void) { struct acl_backend_vfile *backend; pool_t pool; pool = pool_alloconly_create("ACL backend", 512); backend = p_new(pool, struct acl_backend_vfile, 1); backend->backend.pool = pool; return &backend->backend; } static int acl_backend_vfile_init(struct acl_backend *_backend, const char *data) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; struct stat st; const char *const *tmp; tmp = t_strsplit(data, ":"); backend->global_path = p_strdup_empty(_backend->pool, *tmp); backend->cache_secs = ACL_VFILE_DEFAULT_CACHE_SECS; if (*tmp != NULL) tmp++; for (; *tmp != NULL; tmp++) { if (strncmp(*tmp, "cache_secs=", 11) == 0) { if (str_to_uint(*tmp + 11, &backend->cache_secs) < 0) { i_error("acl vfile: Invalid cache_secs value: %s", *tmp + 11); return -1; } } else { i_error("acl vfile: Unknown parameter: %s", *tmp); return -1; } } if (backend->global_path != NULL) { if (stat(backend->global_path, &st) < 0) { if (errno != ENOENT) { i_error("acl vfile: stat(%s) failed: %m", backend->global_path); return -1; } } else if (!S_ISDIR(st.st_mode)) { _backend->global_file = acl_global_file_init(backend->global_path, backend->cache_secs, _backend->debug); } } if (_backend->debug) { if (backend->global_path == NULL) i_debug("acl vfile: Global ACLs disabled"); else if (_backend->global_file != NULL) { i_debug("acl vfile: Global ACL file: %s", backend->global_path); } else { i_debug("acl vfile: Global ACL legacy directory: %s", backend->global_path); } } _backend->cache = acl_cache_init(_backend, sizeof(struct acl_backend_vfile_validity)); return 0; } static void acl_backend_vfile_deinit(struct acl_backend *_backend) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; if (backend->acllist_pool != NULL) { array_free(&backend->acllist); pool_unref(&backend->acllist_pool); } if (_backend->global_file != NULL) acl_global_file_deinit(&_backend->global_file); pool_unref(&backend->backend.pool); } static const char * acl_backend_vfile_get_local_dir(struct acl_backend *backend, const char *name, const char *vname) { struct mail_namespace *ns = mailbox_list_get_namespace(backend->list); struct mailbox_list *list = ns->list; struct mail_storage *storage; enum mailbox_list_path_type type; const char *dir, *inbox; if (*name == '\0') name = NULL; if (backend->globals_only) return NULL; /* ACL files are very important. try to keep them among the main mail files. that's not possible though with a) if the mailbox is a file or b) if the mailbox path doesn't point to filesystem. */ if (mailbox_list_get_storage(&list, vname, &storage) < 0) return NULL; i_assert(list == ns->list); type = mail_storage_is_mailbox_file(storage) || (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0 ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_MAILBOX; if (name == NULL) { if (!mailbox_list_get_root_path(list, type, &dir)) return NULL; } else { if (mailbox_list_get_path(list, name, type, &dir) <= 0) return NULL; } /* verify that the directory isn't same as INBOX's directory. this is mainly for Maildir. */ if (name == NULL && mailbox_list_get_path(list, "INBOX", MAILBOX_LIST_PATH_TYPE_MAILBOX, &inbox) > 0 && strcmp(inbox, dir) == 0) { /* can't have default ACLs with this setup */ return NULL; } return dir; } static struct acl_object * acl_backend_vfile_object_init(struct acl_backend *_backend, const char *name) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; struct acl_object_vfile *aclobj; const char *dir, *vname, *error; aclobj = i_new(struct acl_object_vfile, 1); aclobj->aclobj.backend = _backend; aclobj->aclobj.name = i_strdup(name); T_BEGIN { if (*name == '\0' || mailbox_list_is_valid_name(_backend->list, name, &error)) { vname = *name == '\0' ? "" : mailbox_list_get_vname(_backend->list, name); dir = acl_backend_vfile_get_local_dir(_backend, name, vname); aclobj->local_path = dir == NULL ? NULL : i_strconcat(dir, "/"ACL_FILENAME, NULL); if (backend->global_path != NULL && _backend->global_file == NULL) { aclobj->global_path = i_strconcat(backend->global_path, "/", vname, NULL); } } else { /* Invalid mailbox name, just use the default global ACL files */ } } T_END; return &aclobj->aclobj; } static const char * get_parent_mailbox(struct acl_backend *backend, const char *name) { const char *p; p = strrchr(name, mailbox_list_get_hierarchy_sep(backend->list)); return p == NULL ? NULL : t_strdup_until(name, p); } static int acl_backend_vfile_exists(struct acl_backend_vfile *backend, const char *path, struct acl_vfile_validity *validity) { struct stat st; if (validity->last_check + (time_t)backend->cache_secs > ioloop_time) { /* use the cached value */ return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOTFOUND; } validity->last_check = ioloop_time; if (stat(path, &st) < 0) { if (errno == ENOENT || errno == ENOTDIR) { validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND; return 0; } if (errno == EACCES) { validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS; return 1; } i_error("stat(%s) failed: %m", path); return -1; } validity->last_mtime = st.st_mtime; validity->last_size = st.st_size; return 1; } static bool acl_backend_vfile_has_acl(struct acl_backend *_backend, const char *name) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_backend; struct acl_backend_vfile_validity *old_validity, new_validity; const char *path, *local_path, *global_path, *dir, *vname = ""; const char *error; int ret; old_validity = acl_cache_get_validity(_backend->cache, name); if (old_validity != NULL) new_validity = *old_validity; else i_zero(&new_validity); /* See if the mailbox exists. If we wanted recursive lookups we could skip this, but at least for now we assume that if an existing mailbox has no ACL it's equivalent to default ACLs. */ if (mailbox_list_get_path(_backend->list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0) ret = -1; else { ret = acl_backend_vfile_exists(backend, path, &new_validity.mailbox_validity); } if (ret == 0 && (*name == '\0' || mailbox_list_is_valid_name(_backend->list, name, &error))) { vname = *name == '\0' ? "" : mailbox_list_get_vname(_backend->list, name); dir = acl_backend_vfile_get_local_dir(_backend, name, vname); if (dir != NULL) { local_path = t_strconcat(dir, "/", name, NULL); ret = acl_backend_vfile_exists(backend, local_path, &new_validity.local_validity); } } if (ret == 0 && backend->global_path != NULL) { if (_backend->global_file != NULL) { ret = acl_global_file_refresh(_backend->global_file); if (ret == 0 && acl_global_file_have_any(_backend->global_file, vname)) ret = 1; } else { global_path = t_strconcat(backend->global_path, "/", name, NULL); ret = acl_backend_vfile_exists(backend, global_path, &new_validity.global_validity); } } acl_cache_set_validity(_backend->cache, name, &new_validity); return ret > 0; } static struct acl_object * acl_backend_vfile_object_init_parent(struct acl_backend *backend, const char *child_name) { const char *parent; /* stop at the first parent that a) has global ACL file b) has local ACL file c) exists */ while ((parent = get_parent_mailbox(backend, child_name)) != NULL) { if (acl_backend_vfile_has_acl(backend, parent)) break; child_name = parent; } if (parent == NULL) { /* use the root */ parent = acl_backend_get_default_object(backend)->name; } return acl_backend_vfile_object_init(backend, parent); } static void acl_backend_vfile_object_deinit(struct acl_object *_aclobj) { struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; i_free(aclobj->local_path); i_free(aclobj->global_path); if (array_is_created(&aclobj->aclobj.rights)) array_free(&aclobj->aclobj.rights); if (aclobj->aclobj.rights_pool != NULL) pool_unref(&aclobj->aclobj.rights_pool); i_free(aclobj->aclobj.name); i_free(aclobj); } static int acl_backend_vfile_read(struct acl_object *aclobj, bool global, const char *path, struct acl_vfile_validity *validity, bool try_retry, bool *is_dir_r) { struct istream *input; struct stat st; struct acl_rights rights; const char *line, *error; unsigned int linenum; int fd, ret = 0; *is_dir_r = FALSE; fd = nfs_safe_open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT || errno == ENOTDIR) { if (aclobj->backend->debug) i_debug("acl vfile: file %s not found", path); validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND; } else if (errno == EACCES) { if (aclobj->backend->debug) i_debug("acl vfile: no access to file %s", path); acl_object_remove_all_access(aclobj); validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS; } else { i_error("open(%s) failed: %m", path); return -1; } validity->last_size = 0; validity->last_read_time = ioloop_time; return 1; } if (fstat(fd, &st) < 0) { if (errno == ESTALE && try_retry) { i_close_fd(&fd); return 0; } i_error("fstat(%s) failed: %m", path); i_close_fd(&fd); return -1; } if (S_ISDIR(st.st_mode)) { /* we opened a directory. */ *is_dir_r = TRUE; i_close_fd(&fd); return 0; } if (aclobj->backend->debug) i_debug("acl vfile: reading file %s", path); input = i_stream_create_fd(fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(input, TRUE); linenum = 0; while ((line = i_stream_read_next_line(input)) != NULL) { linenum++; if (line[0] == '\0' || line[0] == '#') continue; T_BEGIN { ret = acl_rights_parse_line(line, aclobj->rights_pool, &rights, &error); rights.global = global; if (ret < 0) { i_error("ACL file %s line %u: %s", path, linenum, error); } else { array_append(&aclobj->rights, &rights, 1); } } T_END; if (ret < 0) break; } if (ret < 0) { /* parsing failure */ } else if (input->stream_errno != 0) { if (input->stream_errno == ESTALE && try_retry) ret = 0; else { ret = -1; i_error("read(%s) failed: %s", path, i_stream_get_error(input)); } } else { if (fstat(fd, &st) < 0) { if (errno == ESTALE && try_retry) ret = 0; else { ret = -1; i_error("fstat(%s) failed: %m", path); } } else { ret = 1; validity->last_read_time = ioloop_time; validity->last_mtime = st.st_mtime; validity->last_size = st.st_size; } } i_stream_unref(&input); if (close(fd) < 0) { if (errno == ESTALE && try_retry) return 0; i_error("close(%s) failed: %m", path); return -1; } return ret; } static int acl_backend_vfile_read_with_retry(struct acl_object *aclobj, bool global, const char *path, struct acl_vfile_validity *validity) { unsigned int i; int ret; bool is_dir; if (path == NULL) return 0; for (i = 0;; i++) { ret = acl_backend_vfile_read(aclobj, global, path, validity, i < ACL_ESTALE_RETRY_COUNT, &is_dir); if (ret != 0) break; if (is_dir) { /* opened a directory. use dir/.DEFAULT instead */ path = t_strconcat(path, "/.DEFAULT", NULL); } else { /* ESTALE - try again */ } } return ret <= 0 ? -1 : 0; } static bool acl_vfile_validity_has_changed(struct acl_backend_vfile *backend, const struct acl_vfile_validity *validity, const struct stat *st) { if (st->st_mtime == validity->last_mtime && st->st_size == validity->last_size) { /* same timestamp, but if it was modified within the same second we want to refresh it again later (but do it only after a couple of seconds so we don't keep re-reading it all the time within those seconds) */ time_t cache_secs = backend->cache_secs; if (validity->last_read_time != 0 && (st->st_mtime < validity->last_read_time - cache_secs || ioloop_time - validity->last_read_time <= cache_secs)) return FALSE; } return TRUE; } static int acl_backend_vfile_refresh(struct acl_object *aclobj, const char *path, struct acl_vfile_validity *validity) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)aclobj->backend; struct stat st; int ret; if (validity == NULL) return 1; if (path == NULL || validity->last_check + (time_t)backend->cache_secs > ioloop_time) return 0; validity->last_check = ioloop_time; ret = stat(path, &st); if (ret == 0 && S_ISDIR(st.st_mode)) { /* it's a directory. use dir/.DEFAULT instead */ path = t_strconcat(path, "/.DEFAULT", NULL); ret = stat(path, &st); } if (ret < 0) { if (errno == ENOENT || errno == ENOTDIR) { /* if the file used to exist, we have to re-read it */ return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOTFOUND; } if (errno == EACCES) return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOACCESS; i_error("stat(%s) failed: %m", path); return -1; } return acl_vfile_validity_has_changed(backend, validity, &st) ? 1 : 0; } int acl_backend_vfile_object_get_mtime(struct acl_object *aclobj, time_t *mtime_r) { struct acl_backend_vfile_validity *validity; validity = acl_cache_get_validity(aclobj->backend->cache, aclobj->name); if (validity == NULL) return -1; if (validity->local_validity.last_mtime != 0) *mtime_r = validity->local_validity.last_mtime; else if (validity->global_validity.last_mtime != 0) *mtime_r = validity->global_validity.last_mtime; else *mtime_r = 0; return 0; } static int acl_backend_global_file_refresh(struct acl_object *_aclobj, struct acl_vfile_validity *validity) { struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_aclobj->backend; struct stat st; if (acl_global_file_refresh(_aclobj->backend->global_file) < 0) return -1; acl_global_file_last_stat(_aclobj->backend->global_file, &st); if (validity == NULL) return 1; return acl_vfile_validity_has_changed(backend, validity, &st) ? 1 : 0; } static int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj) { struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_aclobj->backend; struct acl_backend_vfile_validity *old_validity; struct acl_backend_vfile_validity validity; time_t mtime; int ret; old_validity = acl_cache_get_validity(_aclobj->backend->cache, _aclobj->name); ret = _aclobj->backend->global_file != NULL ? acl_backend_global_file_refresh(_aclobj, old_validity == NULL ? NULL : &old_validity->global_validity) : acl_backend_vfile_refresh(_aclobj, aclobj->global_path, old_validity == NULL ? NULL : &old_validity->global_validity); if (ret == 0) { ret = acl_backend_vfile_refresh(_aclobj, aclobj->local_path, old_validity == NULL ? NULL : &old_validity->local_validity); } if (ret <= 0) return ret; /* either global or local ACLs changed, need to re-read both */ if (!array_is_created(&_aclobj->rights)) { _aclobj->rights_pool = pool_alloconly_create("acl rights", 256); i_array_init(&_aclobj->rights, 16); } else { array_clear(&_aclobj->rights); p_clear(_aclobj->rights_pool); } i_zero(&validity); if (_aclobj->backend->global_file != NULL) { struct stat st; acl_object_add_global_acls(_aclobj); acl_global_file_last_stat(_aclobj->backend->global_file, &st); validity.global_validity.last_read_time = ioloop_time; validity.global_validity.last_mtime = st.st_mtime; validity.global_validity.last_size = st.st_size; } else { if (acl_backend_vfile_read_with_retry(_aclobj, TRUE, aclobj->global_path, &validity.global_validity) < 0) return -1; } if (acl_backend_vfile_read_with_retry(_aclobj, FALSE, aclobj->local_path, &validity.local_validity) < 0) return -1; acl_rights_sort(_aclobj); /* update cache only after we've successfully read everything */ acl_object_rebuild_cache(_aclobj); acl_cache_set_validity(_aclobj->backend->cache, _aclobj->name, &validity); if (acl_backend_vfile_object_get_mtime(_aclobj, &mtime) == 0) acl_backend_vfile_acllist_verify(backend, _aclobj->name, mtime); return 0; } static int acl_backend_vfile_object_last_changed(struct acl_object *_aclobj, time_t *last_changed_r) { struct acl_backend_vfile_validity *old_validity; *last_changed_r = 0; old_validity = acl_cache_get_validity(_aclobj->backend->cache, _aclobj->name); if (old_validity == NULL) { if (acl_backend_vfile_object_refresh_cache(_aclobj) < 0) return -1; old_validity = acl_cache_get_validity(_aclobj->backend->cache, _aclobj->name); if (old_validity == NULL) return 0; } *last_changed_r = old_validity->local_validity.last_mtime; return 0; } struct acl_backend_vfuncs acl_backend_vfile = { acl_backend_vfile_alloc, acl_backend_vfile_init, acl_backend_vfile_deinit, acl_backend_vfile_nonowner_iter_init, acl_backend_vfile_nonowner_iter_next, acl_backend_vfile_nonowner_iter_deinit, acl_backend_vfile_nonowner_lookups_rebuild, acl_backend_vfile_object_init, acl_backend_vfile_object_init_parent, acl_backend_vfile_object_deinit, acl_backend_vfile_object_refresh_cache, acl_backend_vfile_object_update, acl_backend_vfile_object_last_changed, acl_default_object_list_init, acl_default_object_list_next, acl_default_object_list_deinit }; dovecot-2.2.33.2/src/plugins/acl/acl-backend.c0000644000175000017500000001222213165463624015635 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-storage-settings.h" #include "mailbox-list.h" #include "mail-namespace.h" #include "mail-user.h" #include "acl-cache.h" #include "acl-api-private.h" extern struct acl_backend_vfuncs acl_backend_vfile; const char *const all_mailbox_rights[] = { MAIL_ACL_LOOKUP, MAIL_ACL_READ, MAIL_ACL_WRITE, MAIL_ACL_WRITE_SEEN, MAIL_ACL_WRITE_DELETED, MAIL_ACL_INSERT, MAIL_ACL_POST, MAIL_ACL_EXPUNGE, MAIL_ACL_CREATE, MAIL_ACL_DELETE, MAIL_ACL_ADMIN, NULL }; static const char *const *owner_mailbox_rights = all_mailbox_rights; static const char *const non_owner_mailbox_rights[] = { NULL }; struct acl_backend * acl_backend_init(const char *data, struct mailbox_list *list, const char *acl_username, const char *const *groups, bool owner) { struct mail_user *user = mailbox_list_get_user(list); struct acl_backend *backend; unsigned int i, group_count; if (user->mail_debug) { i_debug("acl: initializing backend with data: %s", data); i_debug("acl: acl username = %s", acl_username); i_debug("acl: owner = %d", owner); } group_count = str_array_length(groups); if (strncmp(data, "vfile:", 6) == 0) data += 6; else if (strcmp(data, "vfile") == 0) data = ""; else i_fatal("Unknown ACL backend: %s", t_strcut(data, ':')); backend = acl_backend_vfile.alloc(); backend->debug = user->mail_debug; backend->v = acl_backend_vfile; backend->list = list; backend->username = p_strdup(backend->pool, acl_username); backend->owner = owner; backend->globals_only = mail_user_plugin_getenv(user, "acl_globals_only") != NULL; if (group_count > 0) { backend->group_count = group_count; backend->groups = p_new(backend->pool, const char *, group_count); for (i = 0; i < group_count; i++) { backend->groups[i] = p_strdup(backend->pool, groups[i]); if (user->mail_debug) i_debug("acl: group added: %s", groups[i]); } i_qsort(backend->groups, group_count, sizeof(const char *), i_strcmp_p); } T_BEGIN { if (acl_backend_vfile.init(backend, data) < 0) i_fatal("acl: backend vfile init failed with data: %s", data); } T_END; backend->default_rights = owner ? owner_mailbox_rights : non_owner_mailbox_rights; backend->default_aclmask = acl_cache_mask_init(backend->cache, backend->pool, backend->default_rights); return backend; } void acl_backend_deinit(struct acl_backend **_backend) { struct acl_backend *backend = *_backend; *_backend = NULL; if (backend->default_aclobj != NULL) acl_object_deinit(&backend->default_aclobj); acl_cache_deinit(&backend->cache); backend->v.deinit(backend); } const char *acl_backend_get_acl_username(struct acl_backend *backend) { return backend->username; } bool acl_backend_user_is_authenticated(struct acl_backend *backend) { return backend->username != NULL; } bool acl_backend_user_is_owner(struct acl_backend *backend) { return backend->owner; } bool acl_backend_user_name_equals(struct acl_backend *backend, const char *username) { if (backend->username == NULL) { /* anonymous user never matches */ return FALSE; } return strcmp(backend->username, username) == 0; } bool acl_backend_user_is_in_group(struct acl_backend *backend, const char *group_name) { return i_bsearch(group_name, backend->groups, backend->group_count, sizeof(const char *), bsearch_strcmp) != NULL; } bool acl_backend_rights_match_me(struct acl_backend *backend, const struct acl_rights *rights) { switch (rights->id_type) { case ACL_ID_ANYONE: return TRUE; case ACL_ID_AUTHENTICATED: return acl_backend_user_is_authenticated(backend); case ACL_ID_GROUP: case ACL_ID_GROUP_OVERRIDE: return acl_backend_user_is_in_group(backend, rights->identifier); case ACL_ID_USER: return acl_backend_user_name_equals(backend, rights->identifier); case ACL_ID_OWNER: return acl_backend_user_is_owner(backend); case ACL_ID_TYPE_COUNT: break; } i_unreached(); } unsigned int acl_backend_lookup_right(struct acl_backend *backend, const char *right) { return acl_cache_right_lookup(backend->cache, right); } struct acl_object *acl_backend_get_default_object(struct acl_backend *backend) { struct mail_user *user = mailbox_list_get_user(backend->list); struct mail_namespace *ns = mailbox_list_get_namespace(backend->list); const char *default_name = ""; if (backend->default_aclobj != NULL) return backend->default_aclobj; /* FIXME: this should probably be made default in v2.3 */ if (mail_user_plugin_getenv(user, "acl_defaults_from_inbox") != NULL) { if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE || ns->type == MAIL_NAMESPACE_TYPE_SHARED) default_name = "INBOX"; } backend->default_aclobj = acl_object_init_from_name(backend, default_name); return backend->default_aclobj; } int acl_backend_get_default_rights(struct acl_backend *backend, const struct acl_mask **mask_r) { struct acl_object *aclobj = acl_backend_get_default_object(backend); if (backend->v.object_refresh_cache(aclobj) < 0) return -1; *mask_r = acl_cache_get_my_rights(backend->cache, aclobj->name); if (*mask_r == NULL) *mask_r = backend->default_aclmask; return 0; } dovecot-2.2.33.2/src/plugins/acl/acl-global-file.c0000644000175000017500000001353113167162046016423 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "strescape.h" #include "wildcard-match.h" #include "acl-api-private.h" #include "acl-global-file.h" #include struct acl_global_rights { const char *vpattern; ARRAY_TYPE(acl_rights) rights; }; struct acl_global_parse_rights { const char *vpattern; struct acl_rights rights; }; struct acl_global_file { char *path; struct stat prev_st; time_t last_refresh_time; pool_t rights_pool; ARRAY(struct acl_global_rights) rights; unsigned int refresh_interval_secs; bool debug; }; struct acl_global_file * acl_global_file_init(const char *path, unsigned int refresh_interval_secs, bool debug) { struct acl_global_file *file; file = i_new(struct acl_global_file, 1); file->path = i_strdup(path); file->refresh_interval_secs = refresh_interval_secs; file->debug = debug; i_array_init(&file->rights, 32); file->rights_pool = pool_alloconly_create("acl global file rights", 1024); return file; } void acl_global_file_deinit(struct acl_global_file **_file) { struct acl_global_file *file = *_file; *_file = NULL; array_free(&file->rights); pool_unref(&file->rights_pool); i_free(file->path); i_free(file); } static int acl_global_parse_rights_cmp(const struct acl_global_parse_rights *r1, const struct acl_global_parse_rights *r2) { return strcmp(r1->vpattern, r2->vpattern); } struct acl_global_file_parse_ctx { struct acl_global_file *file; ARRAY(struct acl_global_parse_rights) parse_rights; }; static int acl_global_file_parse_line(struct acl_global_file_parse_ctx *ctx, const char *line, const char **error_r) { struct acl_global_parse_rights *pright; const char *p, *vpattern; if (*line == '"') { line++; if (str_unescape_next(&line, &vpattern) < 0) { *error_r = "Missing '\"'"; return -1; } if (line[0] != ' ') { *error_r = "Expecting space after '\"'"; return -1; } line++; } else { p = strchr(line, ' '); if (p == NULL) { *error_r = "Missing ACL rights"; return -1; } if (p == line) { *error_r = "Empty ACL pattern"; return -1; } vpattern = t_strdup_until(line, p); line = p + 1; } pright = array_append_space(&ctx->parse_rights); pright->vpattern = p_strdup(ctx->file->rights_pool, vpattern); if (acl_rights_parse_line(line, ctx->file->rights_pool, &pright->rights, error_r) < 0) return -1; pright->rights.global = TRUE; return 0; } static int acl_global_file_read(struct acl_global_file *file) { struct acl_global_file_parse_ctx ctx; struct acl_global_parse_rights *pright; struct acl_global_rights *right; struct istream *input; const char *line, *error, *prev_vpattern; unsigned int linenum = 0; int ret = 0; array_clear(&file->rights); p_clear(file->rights_pool); i_zero(&ctx); ctx.file = file; i_array_init(&ctx.parse_rights, 32); input = i_stream_create_file(file->path, (size_t)-1); while ((line = i_stream_read_next_line(input)) != NULL) { linenum++; if (line[0] == '\0' || line[0] == '#') continue; T_BEGIN { ret = acl_global_file_parse_line(&ctx, line, &error); if (ret < 0) { i_error("Global ACL file %s line %u: %s", file->path, linenum, error); } } T_END; if (ret < 0) break; } if (ret == 0 && input->stream_errno != 0) { i_error("Couldn't read global ACL file %s: %s", file->path, i_stream_get_error(input)); ret = -1; } if (ret == 0) { const struct stat *st; if (i_stream_stat(input, TRUE, &st) < 0) { i_error("Couldn't stat global ACL file %s: %s", file->path, i_stream_get_error(input)); } file->prev_st = *st; } i_stream_destroy(&input); /* sort all parsed rights */ array_sort(&ctx.parse_rights, acl_global_parse_rights_cmp); /* combine identical patterns into same structs */ prev_vpattern = ""; right = NULL; array_foreach_modifiable(&ctx.parse_rights, pright) { if (right == NULL || strcmp(prev_vpattern, pright->vpattern) != 0) { right = array_append_space(&file->rights); right->vpattern = pright->vpattern; p_array_init(&right->rights, file->rights_pool, 4); } array_append(&right->rights, &pright->rights, 1); } array_free(&ctx.parse_rights); return ret; } int acl_global_file_refresh(struct acl_global_file *file) { struct stat st; if (file->last_refresh_time + (time_t)file->refresh_interval_secs > ioloop_time) return 0; if (file->last_refresh_time != 0) { if (stat(file->path, &st) < 0) { i_error("stat(%s) failed: %m", file->path); return -1; } if (st.st_ino == file->prev_st.st_ino && st.st_size == file->prev_st.st_size && CMP_ST_MTIME(&st, &file->prev_st)) { /* no change to the file */ file->last_refresh_time = ioloop_time; return 0; } } if (acl_global_file_read(file) < 0) return -1; file->last_refresh_time = ioloop_time; return 0; } void acl_global_file_last_stat(struct acl_global_file *file, struct stat *st_r) { *st_r = file->prev_st; } void acl_global_file_get(struct acl_global_file *file, const char *vname, pool_t pool, ARRAY_TYPE(acl_rights) *rights_r) { struct acl_global_rights *global_rights; const struct acl_rights *rights; struct acl_rights *new_rights; array_foreach_modifiable(&file->rights, global_rights) { if (!wildcard_match(vname, global_rights->vpattern)) continue; if (file->debug) { i_debug("Mailbox '%s' matches global ACL pattern '%s'", vname, global_rights->vpattern); } array_foreach(&global_rights->rights, rights) { new_rights = array_append_space(rights_r); acl_rights_dup(rights, pool, new_rights); } } } bool acl_global_file_have_any(struct acl_global_file *file, const char *vname) { struct acl_global_rights *rights; i_assert(file->last_refresh_time != 0); array_foreach_modifiable(&file->rights, rights) { if (wildcard_match(vname, rights->vpattern)) return TRUE; } return FALSE; } dovecot-2.2.33.2/src/plugins/acl/acl-mailbox.c0000644000175000017500000004502613165463624015711 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ /* FIXME: If we don't have permission to change flags/keywords, the changes should still be stored temporarily for this session. However most clients don't care and it's a huge job, so I currently this isn't done. The same problem actually exists when opening read-only mailboxes. */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "mailbox-list-private.h" #include "acl-api-private.h" #include "acl-plugin.h" #include #define ACL_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, acl_mail_module) struct acl_transaction_context { union mailbox_transaction_module_context module_ctx; }; static MODULE_CONTEXT_DEFINE_INIT(acl_mail_module, &mail_module_register); static struct acl_transaction_context acl_transaction_failure; struct acl_object *acl_mailbox_get_aclobj(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); return abox->aclobj; } int acl_mailbox_right_lookup(struct mailbox *box, unsigned int right_idx) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); int ret; if (abox->skip_acl_checks) return 1; ret = acl_object_have_right(abox->aclobj, alist->rights.acl_storage_right_idx[right_idx]); if (ret > 0) return 1; if (ret < 0) { mail_storage_set_internal_error(box->storage); return -1; } mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return 0; } static bool acl_is_readonly(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); enum acl_storage_rights save_right; if (abox->module_ctx.super.is_readonly(box)) return TRUE; save_right = (box->flags & MAILBOX_FLAG_POST_SESSION) != 0 ? ACL_STORAGE_RIGHT_POST : ACL_STORAGE_RIGHT_INSERT; if (acl_mailbox_right_lookup(box, save_right) > 0) return FALSE; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_EXPUNGE) > 0) return FALSE; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE) > 0) return FALSE; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_DELETED) > 0) return FALSE; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN) > 0) return FALSE; return TRUE; } static void acl_mailbox_free(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); acl_object_deinit(&abox->aclobj); abox->module_ctx.super.free(box); } static void acl_mailbox_copy_acls_from_parent(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); struct acl_object *parent_aclobj; struct acl_object_list_iter *iter; struct acl_rights_update update; i_zero(&update); update.modify_mode = ACL_MODIFY_MODE_REPLACE; update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; parent_aclobj = acl_object_init_from_parent(alist->rights.backend, box->name); iter = acl_object_list_init(parent_aclobj); while (acl_object_list_next(iter, &update.rights) > 0) { /* don't copy global ACL rights. */ if (!update.rights.global) (void)acl_object_update(abox->aclobj, &update); } acl_object_list_deinit(&iter); acl_object_deinit(&parent_aclobj); } static int acl_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct acl_mailbox *abox = ACL_CONTEXT(box); int ret; if (!mailbox_is_autocreated(box)) { /* we're looking up CREATE permission from our parent's rights */ ret = acl_mailbox_list_have_right(box->list, box->name, TRUE, ACL_STORAGE_RIGHT_CREATE, NULL); } else { /* mailbox is autocreated, so we need to treat it as if it already exists. ignore the "create" ACL here. */ ret = 1; } if (ret <= 0) { if (ret < 0) { mail_storage_set_internal_error(box->storage); return -1; } /* Note that if user didn't have LOOKUP permission to parent mailbox, this may reveal the mailbox's existence to user. Can't help it. */ mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return -1; } /* ignore ACLs in this mailbox until creation is complete, because super.create() may call e.g. mailbox_open() which will fail since we haven't yet copied ACLs to this mailbox. */ abox->skip_acl_checks = TRUE; ret = abox->module_ctx.super.create_box(box, update, directory); abox->skip_acl_checks = FALSE; if (ret == 0) acl_mailbox_copy_acls_from_parent(box); return ret; } static int acl_mailbox_update(struct mailbox *box, const struct mailbox_update *update) { struct acl_mailbox *abox = ACL_CONTEXT(box); int ret; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN); if (ret <= 0) return -1; return abox->module_ctx.super.update_box(box, update); } static void acl_mailbox_fail_not_found(struct mailbox *box) { int ret; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); if (ret > 0) { mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); } else if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); } } static int acl_mailbox_delete(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); int ret; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_DELETE); if (ret <= 0) { if (ret == 0) acl_mailbox_fail_not_found(box); return -1; } return abox->module_ctx.super.delete_box(box); } static int acl_mailbox_rename(struct mailbox *src, struct mailbox *dest) { struct acl_mailbox *abox = ACL_CONTEXT(src); int ret; /* renaming requires rights to delete the old mailbox */ ret = acl_mailbox_right_lookup(src, ACL_STORAGE_RIGHT_DELETE); if (ret <= 0) { if (ret == 0) acl_mailbox_fail_not_found(src); return -1; } /* and create the new one under the parent mailbox */ T_BEGIN { ret = acl_mailbox_list_have_right(dest->list, dest->name, TRUE, ACL_STORAGE_RIGHT_CREATE, NULL); } T_END; if (ret <= 0) { if (ret == 0) { /* Note that if the mailbox didn't have LOOKUP permission, this now reveals to user the mailbox's existence. Can't help it. */ mail_storage_set_error(src->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); } else { mail_storage_set_internal_error(src->storage); } return -1; } return abox->module_ctx.super.rename_box(src, dest); } static int acl_get_write_rights(struct mailbox *box, bool *flags_r, bool *flag_seen_r, bool *flag_del_r) { int ret; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE); if (ret < 0) return -1; *flags_r = ret > 0; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN); if (ret < 0) return -1; *flag_seen_r = ret > 0; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_DELETED); if (ret < 0) return -1; *flag_del_r = ret > 0; return 0; } static void acl_transaction_set_failure(struct mailbox_transaction_context *t) { MODULE_CONTEXT_SET(t, acl_storage_module, &acl_transaction_failure); } static void acl_mail_update_flags(struct mail *_mail, enum modify_type modify_type, enum mail_flags flags) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *amail = ACL_MAIL_CONTEXT(mail); bool acl_flags, acl_flag_seen, acl_flag_del; if (acl_get_write_rights(_mail->box, &acl_flags, &acl_flag_seen, &acl_flag_del) < 0) { acl_transaction_set_failure(_mail->transaction); return; } if (modify_type != MODIFY_REPLACE) { /* adding/removing flags. just remove the disallowed flags from the mask. */ if (!acl_flags) flags &= MAIL_SEEN | MAIL_DELETED; if (!acl_flag_seen) flags &= ~MAIL_SEEN; if (!acl_flag_del) flags &= ~MAIL_DELETED; } else if (!acl_flags || !acl_flag_seen || !acl_flag_del) { /* we don't have permission to replace all the flags. */ if (!acl_flags && !acl_flag_seen && !acl_flag_del) { /* no flag changes allowed. ignore silently. */ return; } /* handle this by first removing the allowed flags and then adding the allowed flags */ acl_mail_update_flags(_mail, MODIFY_REMOVE, ~flags); if (flags != 0) acl_mail_update_flags(_mail, MODIFY_ADD, flags); return; } amail->super.update_flags(_mail, modify_type, flags); } static void acl_mail_update_keywords(struct mail *_mail, enum modify_type modify_type, struct mail_keywords *keywords) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *amail = ACL_MAIL_CONTEXT(mail); int ret; ret = acl_mailbox_right_lookup(_mail->box, ACL_STORAGE_RIGHT_WRITE); if (ret <= 0) { /* if we don't have permission, just silently return success. */ if (ret < 0) acl_transaction_set_failure(_mail->transaction); return; } amail->super.update_keywords(_mail, modify_type, keywords); } static void acl_mail_expunge(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *amail = ACL_MAIL_CONTEXT(mail); int ret; ret = acl_mailbox_right_lookup(_mail->box, ACL_STORAGE_RIGHT_EXPUNGE); if (ret <= 0) { /* if we don't have permission, silently return success so users won't see annoying error messages in case their clients try automatic expunging. */ acl_transaction_set_failure(_mail->transaction); return; } amail->super.expunge(_mail); } void acl_mail_allocated(struct mail *_mail) { struct acl_mailbox *abox = ACL_CONTEXT(_mail->box); struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *amail; if (abox == NULL || !abox->acl_enabled) return; amail = p_new(mail->pool, union mail_module_context, 1); amail->super = *v; mail->vlast = &amail->super; v->update_flags = acl_mail_update_flags; v->update_keywords = acl_mail_update_keywords; v->expunge = acl_mail_expunge; MODULE_CONTEXT_SET_SELF(mail, acl_mail_module, amail); } static int acl_save_get_flags(struct mailbox *box, enum mail_flags *flags, enum mail_flags *pvt_flags, struct mail_keywords **keywords) { bool acl_flags, acl_flag_seen, acl_flag_del; if (acl_get_write_rights(box, &acl_flags, &acl_flag_seen, &acl_flag_del) < 0) return -1; if (!acl_flag_seen) { *flags &= ~MAIL_SEEN; *pvt_flags &= ~MAIL_SEEN; } if (!acl_flag_del) { *flags &= ~MAIL_DELETED; *pvt_flags &= ~MAIL_DELETED; } if (!acl_flags) { *flags &= MAIL_SEEN | MAIL_DELETED; *pvt_flags &= MAIL_SEEN | MAIL_DELETED; *keywords = NULL; } return 0; } static int acl_save_begin(struct mail_save_context *ctx, struct istream *input) { struct mailbox *box = ctx->transaction->box; struct acl_mailbox *abox = ACL_CONTEXT(box); enum acl_storage_rights save_right; save_right = (box->flags & MAILBOX_FLAG_POST_SESSION) != 0 ? ACL_STORAGE_RIGHT_POST : ACL_STORAGE_RIGHT_INSERT; if (acl_mailbox_right_lookup(box, save_right) <= 0) return -1; if (acl_save_get_flags(box, &ctx->data.flags, &ctx->data.pvt_flags, &ctx->data.keywords) < 0) return -1; return abox->module_ctx.super.save_begin(ctx, input); } static bool acl_copy_has_rights(struct mail_save_context *ctx, struct mail *mail) { struct mailbox *destbox = ctx->transaction->box; enum acl_storage_rights save_right; if (ctx->moving) { if (acl_mailbox_right_lookup(mail->box, ACL_STORAGE_RIGHT_EXPUNGE) <= 0) return FALSE; } save_right = (destbox->flags & MAILBOX_FLAG_POST_SESSION) != 0 ? ACL_STORAGE_RIGHT_POST : ACL_STORAGE_RIGHT_INSERT; if (acl_mailbox_right_lookup(destbox, save_right) <= 0) return FALSE; if (acl_save_get_flags(destbox, &ctx->data.flags, &ctx->data.pvt_flags, &ctx->data.keywords) < 0) return FALSE; return TRUE; } static int acl_copy(struct mail_save_context *ctx, struct mail *mail) { struct mailbox_transaction_context *t = ctx->transaction; struct acl_mailbox *abox = ACL_CONTEXT(t->box); if (!acl_copy_has_rights(ctx, mail)) { mailbox_save_cancel(&ctx); return -1; } return abox->module_ctx.super.copy(ctx, mail); } static int acl_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { struct acl_mailbox *abox = ACL_CONTEXT(ctx->box); void *at = ACL_CONTEXT(ctx); int ret; if (at != NULL) { abox->module_ctx.super.transaction_rollback(ctx); return -1; } ret = abox->module_ctx.super.transaction_commit(ctx, changes_r); if (abox->no_read_right) { /* don't allow IMAP client to see what UIDs the messages got */ changes_r->no_read_perm = TRUE; } return ret; } static int acl_mailbox_exists(struct mailbox *box, bool auto_boxes, enum mailbox_existence *existence_r) { struct acl_mailbox *abox = ACL_CONTEXT(box); const char *const *rights; unsigned int i; if (acl_object_get_my_rights(abox->aclobj, pool_datastack_create(), &rights) < 0) return -1; /* for now this is used only by IMAP SUBSCRIBE. we'll intentionally violate RFC 4314 here, because it says SUBSCRIBE should succeed only when mailbox has 'l' right. But there's no point in not allowing a subscribe for a mailbox that can be selected anyway. Just the opposite: subscribing to such mailboxes is a very useful feature. */ for (i = 0; rights[i] != NULL; i++) { if (strcmp(rights[i], MAIL_ACL_LOOKUP) == 0 || strcmp(rights[i], MAIL_ACL_READ) == 0 || strcmp(rights[i], MAIL_ACL_INSERT) == 0) return abox->module_ctx.super.exists(box, auto_boxes, existence_r); } *existence_r = MAILBOX_EXISTENCE_NONE; return 0; } static int acl_mailbox_open_check_acl(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); const unsigned int *idx_arr = alist->rights.acl_storage_right_idx; enum acl_storage_rights open_right; int ret; /* mailbox can be opened either for reading or appending new messages */ if ((box->flags & MAILBOX_FLAG_IGNORE_ACLS) != 0 || (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0 || abox->skip_acl_checks) return 0; if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) { open_right = (box->flags & MAILBOX_FLAG_POST_SESSION) != 0 ? ACL_STORAGE_RIGHT_POST : ACL_STORAGE_RIGHT_INSERT; } else if (box->deleting) { open_right = ACL_STORAGE_RIGHT_DELETE; } else { open_right = ACL_STORAGE_RIGHT_READ; } ret = acl_object_have_right(abox->aclobj, idx_arr[open_right]); if (ret <= 0) { if (ret == 0) { /* no access. */ acl_mailbox_fail_not_found(box); } return -1; } if (open_right != ACL_STORAGE_RIGHT_READ) { ret = acl_object_have_right(abox->aclobj, idx_arr[ACL_STORAGE_RIGHT_READ]); if (ret < 0) return -1; if (ret == 0) abox->no_read_right = TRUE; } return 0; } static int acl_mailbox_open(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); if (acl_mailbox_open_check_acl(box) < 0) return -1; return abox->module_ctx.super.open(box); } static int acl_mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct acl_mailbox *abox = ACL_CONTEXT(box); if (abox->module_ctx.super.get_status(box, items, status_r) < 0) return -1; if ((items & STATUS_PERMANENT_FLAGS) != 0) { if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE) <= 0) { status_r->permanent_flags &= MAIL_DELETED|MAIL_SEEN; status_r->permanent_keywords = FALSE; status_r->allow_new_keywords = FALSE; } if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_DELETED) <= 0) status_r->permanent_flags &= ~MAIL_DELETED; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN) <= 0) status_r->permanent_flags &= ~MAIL_SEEN; } return 0; } void acl_mailbox_allocated(struct mailbox *box) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); struct mailbox_vfuncs *v = box->vlast; struct acl_mailbox *abox; bool ignore_acls = (box->flags & MAILBOX_FLAG_IGNORE_ACLS) != 0; if (alist == NULL) { /* ACLs disabled */ return; } if (mail_namespace_is_shared_user_root(box->list->ns)) { /* this is the root shared namespace, which itself doesn't have any existing mailboxes. */ ignore_acls = TRUE; } abox = p_new(box->pool, struct acl_mailbox, 1); abox->module_ctx.super = *v; box->vlast = &abox->module_ctx.super; /* aclobj can be used for setting ACLs, even when mailbox is opened with IGNORE_ACLS flag */ abox->aclobj = acl_object_init_from_name(alist->rights.backend, mailbox_get_name(box)); v->free = acl_mailbox_free; if (!ignore_acls) { abox->acl_enabled = TRUE; v->is_readonly = acl_is_readonly; v->exists = acl_mailbox_exists; v->open = acl_mailbox_open; v->get_status = acl_mailbox_get_status; v->create_box = acl_mailbox_create; v->update_box = acl_mailbox_update; v->delete_box = acl_mailbox_delete; v->rename_box = acl_mailbox_rename; v->save_begin = acl_save_begin; v->copy = acl_copy; v->transaction_commit = acl_transaction_commit; v->attribute_set = acl_attribute_set; v->attribute_get = acl_attribute_get; v->attribute_iter_init = acl_attribute_iter_init; v->attribute_iter_next = acl_attribute_iter_next; v->attribute_iter_deinit = acl_attribute_iter_deinit; } MODULE_CONTEXT_SET(box, acl_storage_module, abox); } static bool acl_mailbox_update_removed_id(struct acl_object *aclobj, const struct acl_rights_update *update) { struct acl_object_list_iter *iter; struct acl_rights rights; int ret; if (update->modify_mode != ACL_MODIFY_MODE_CLEAR && update->neg_modify_mode != ACL_MODIFY_MODE_CLEAR) return FALSE; if (update->modify_mode == ACL_MODIFY_MODE_CLEAR && update->neg_modify_mode == ACL_MODIFY_MODE_CLEAR) return TRUE; /* mixed clear/non-clear. see if the identifier exists anymore */ iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (rights.id_type == update->rights.id_type && null_strcmp(rights.identifier, update->rights.identifier) == 0) break; } acl_object_list_deinit(&iter); return ret == 0; } int acl_mailbox_update_acl(struct mailbox_transaction_context *t, const struct acl_rights_update *update) { struct acl_object *aclobj; const char *key; time_t ts = update->last_change != 0 ? update->last_change : ioloop_time; key = t_strdup_printf(MAILBOX_ATTRIBUTE_PREFIX_ACL"%s", acl_rights_get_id(&update->rights)); aclobj = acl_mailbox_get_aclobj(t->box); if (acl_object_update(aclobj, update) < 0) { mail_storage_set_critical(t->box->storage, "Failed to set ACL"); return -1; } /* FIXME: figure out some value lengths, so maybe some day quota could apply to ACLs as well. */ if (acl_mailbox_update_removed_id(aclobj, update)) mail_index_attribute_unset(t->itrans, FALSE, key, ts); else mail_index_attribute_set(t->itrans, FALSE, key, ts, 0); return 0; } dovecot-2.2.33.2/src/plugins/acl/acl-storage.h0000644000175000017500000000302413165463624015717 00000000000000#ifndef ACL_STORAGE_H #define ACL_STORAGE_H #include "mail-storage.h" struct acl_rights_update; enum acl_storage_rights { ACL_STORAGE_RIGHT_LOOKUP, ACL_STORAGE_RIGHT_READ, ACL_STORAGE_RIGHT_WRITE, ACL_STORAGE_RIGHT_WRITE_SEEN, ACL_STORAGE_RIGHT_WRITE_DELETED, ACL_STORAGE_RIGHT_INSERT, ACL_STORAGE_RIGHT_POST, ACL_STORAGE_RIGHT_EXPUNGE, ACL_STORAGE_RIGHT_CREATE, ACL_STORAGE_RIGHT_DELETE, ACL_STORAGE_RIGHT_ADMIN, ACL_STORAGE_RIGHT_COUNT }; /* Returns acl_object for the given mailbox. */ struct acl_object *acl_mailbox_get_aclobj(struct mailbox *box); /* Returns 1 if we have the requested right. If not, returns 0 and sets storage error to MAIL_ERROR_PERM. Returns -1 if internal error occurred and also sets storage error. */ int acl_mailbox_right_lookup(struct mailbox *box, unsigned int right_idx); int acl_mailbox_update_acl(struct mailbox_transaction_context *t, const struct acl_rights_update *update); int acl_attribute_set(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, const struct mail_attribute_value *value); int acl_attribute_get(struct mailbox_transaction_context *t, enum mail_attribute_type type, const char *key, struct mail_attribute_value *value_r); struct mailbox_attribute_iter * acl_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix); const char *acl_attribute_iter_next(struct mailbox_attribute_iter *iter); int acl_attribute_iter_deinit(struct mailbox_attribute_iter *iter); #endif dovecot-2.2.33.2/src/plugins/acl/acl-plugin.h0000644000175000017500000000347213123174404015545 00000000000000#ifndef ACL_PLUGIN_H #define ACL_PLUGIN_H #include "mail-user.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "acl-storage.h" #define ACL_CONTEXT(obj) \ MODULE_CONTEXT(obj, acl_storage_module) #define ACL_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, acl_mailbox_list_module) #define ACL_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, acl_user_module) struct acl_user { union mail_user_module_context module_ctx; const char *acl_user; const char *acl_env; const char *const *groups; struct acl_lookup_dict *acl_lookup_dict; }; struct acl_storage_rights_context { struct acl_backend *backend; unsigned int acl_storage_right_idx[ACL_STORAGE_RIGHT_COUNT]; }; struct acl_mailbox_list { union mailbox_list_module_context module_ctx; struct acl_storage_rights_context rights; time_t last_shared_add_check; }; struct acl_mailbox { union mailbox_module_context module_ctx; struct acl_object *aclobj; bool skip_acl_checks; bool acl_enabled; bool no_read_right; }; extern MODULE_CONTEXT_DEFINE(acl_storage_module, &mail_storage_module_register); extern MODULE_CONTEXT_DEFINE(acl_user_module, &mail_user_module_register); extern MODULE_CONTEXT_DEFINE(acl_mailbox_list_module, &mailbox_list_module_register); void acl_mailbox_list_created(struct mailbox_list *list); void acl_mail_namespace_storage_added(struct mail_namespace *ns); void acl_mail_user_created(struct mail_user *list); void acl_mailbox_allocated(struct mailbox *box); void acl_mail_allocated(struct mail *mail); struct acl_backend *acl_mailbox_list_get_backend(struct mailbox_list *list); int acl_mailbox_list_have_right(struct mailbox_list *list, const char *name, bool parent, unsigned int acl_storage_right_idx, bool *can_see_r) ATTR_NULL(5); void acl_plugin_init(struct module *module); void acl_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/acl/acl-plugin.c0000644000175000017500000000133613123174404015535 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mailbox-list-private.h" #include "acl-api.h" #include "acl-plugin.h" const char *acl_plugin_version = DOVECOT_ABI_VERSION; static struct mail_storage_hooks acl_mail_storage_hooks = { .mail_user_created = acl_mail_user_created, .mailbox_list_created = acl_mailbox_list_created, .mail_namespace_storage_added = acl_mail_namespace_storage_added, .mailbox_allocated = acl_mailbox_allocated, .mail_allocated = acl_mail_allocated }; void acl_plugin_init(struct module *module) { mail_storage_hooks_add(module, &acl_mail_storage_hooks); } void acl_plugin_deinit(void) { mail_storage_hooks_remove(&acl_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/acl/acl-api.h0000644000175000017500000001357313165463624015036 00000000000000#ifndef ACL_API_H #define ACL_API_H struct mailbox_list; struct mail_storage; struct mailbox; struct acl_object; /* Show mailbox in mailbox list. Allow subscribing to it. */ #define MAIL_ACL_LOOKUP "lookup" /* Allow opening mailbox for reading */ #define MAIL_ACL_READ "read" /* Allow permanent flag changes (except for seen/deleted). If not set, doesn't allow save/copy to set any flags either. */ #define MAIL_ACL_WRITE "write" /* Allow permanent seen-flag changes */ #define MAIL_ACL_WRITE_SEEN "write-seen" /* Allow permanent deleted-flag changes */ #define MAIL_ACL_WRITE_DELETED "write-deleted" /* Allow saving and copying mails into the mailbox */ #define MAIL_ACL_INSERT "insert" /* Allow posting mails to the mailbox (e.g. Sieve fileinto) */ #define MAIL_ACL_POST "post" /* Allow expunging mails */ #define MAIL_ACL_EXPUNGE "expunge" /* Allow creating child mailboxes */ #define MAIL_ACL_CREATE "create" /* Allow deleting this mailbox */ #define MAIL_ACL_DELETE "delete" /* Allow changing ACL state in this mailbox */ #define MAIL_ACL_ADMIN "admin" #define MAILBOX_ATTRIBUTE_PREFIX_ACL \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"acl/" /* ACL identifiers in override order */ enum acl_id_type { /* Anyone's rights, including anonymous's. identifier name is ignored. */ ACL_ID_ANYONE, /* Authenticate users' rights. identifier name is ignored. */ ACL_ID_AUTHENTICATED, /* Group's rights */ ACL_ID_GROUP, /* Owner's rights, used when user is the storage's owner. identifier name is ignored. */ ACL_ID_OWNER, /* User's rights */ ACL_ID_USER, /* Same as group's rights, but also overrides user's rights */ ACL_ID_GROUP_OVERRIDE, ACL_ID_TYPE_COUNT }; enum acl_modify_mode { /* Remove rights from existing ACL */ ACL_MODIFY_MODE_REMOVE = 0, /* Add rights to existing ACL (or create a new one) */ ACL_MODIFY_MODE_ADD, /* Replace existing ACL with given rights */ ACL_MODIFY_MODE_REPLACE, /* Clear all the rights from an existing ACL */ ACL_MODIFY_MODE_CLEAR }; struct acl_rights { /* Type of the identifier, user/group */ enum acl_id_type id_type; /* Identifier, eg. username / group name */ const char *identifier; /* Rights assigned. NULL entry can be ignored, but { NULL } means user has no rights. */ const char *const *rights; /* Negative rights assigned */ const char *const *neg_rights; /* These rights are global for all users */ unsigned int global:1; }; ARRAY_DEFINE_TYPE(acl_rights, struct acl_rights); struct acl_rights_update { struct acl_rights rights; enum acl_modify_mode modify_mode; enum acl_modify_mode neg_modify_mode; /* These changes' "last changed" timestamp */ time_t last_change; }; /* data contains the information needed to initialize ACL backend. If username is NULL, it means the user is anonymous. Username and groups are matched case-sensitively. */ struct acl_backend * acl_backend_init(const char *data, struct mailbox_list *list, const char *acl_username, const char *const *groups, bool owner); void acl_backend_deinit(struct acl_backend **backend); /* Returns the acl_username passed to acl_backend_init(). Note that with anonymous users NULL is returned. */ const char *acl_backend_get_acl_username(struct acl_backend *backend); /* Returns TRUE if user isn't anonymous. */ bool acl_backend_user_is_authenticated(struct acl_backend *backend); /* Returns TRUE if user owns the storage. */ bool acl_backend_user_is_owner(struct acl_backend *backend); /* Returns TRUE if given name matches the ACL user name. */ bool acl_backend_user_name_equals(struct acl_backend *backend, const char *username); /* Returns TRUE if ACL user is in given group. */ bool acl_backend_user_is_in_group(struct acl_backend *backend, const char *group_name); /* Returns index for the right name. If it doesn't exist, it's created. */ unsigned int acl_backend_lookup_right(struct acl_backend *backend, const char *right); /* Returns TRUE if acl_rights matches backend user. */ bool acl_backend_rights_match_me(struct acl_backend *backend, const struct acl_rights *rights); /* List mailboxes that have lookup right to some non-owners. */ struct acl_mailbox_list_context * acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend); int acl_backend_nonowner_lookups_iter_next(struct acl_mailbox_list_context *ctx, const char **name_r); void acl_backend_nonowner_lookups_iter_deinit(struct acl_mailbox_list_context **ctx); /* Force a rebuild for nonowner lookups index */ int acl_backend_nonowner_lookups_rebuild(struct acl_backend *backend); struct acl_object *acl_object_init_from_name(struct acl_backend *backend, const char *name); struct acl_object *acl_object_init_from_parent(struct acl_backend *backend, const char *child_name); void acl_object_deinit(struct acl_object **aclobj); /* Returns 1 if we have the requested rights, 0 if not, or -1 if internal error occurred. */ int acl_object_have_right(struct acl_object *aclobj, unsigned int right_idx); /* Returns 0 = ok, -1 = internal error */ int acl_object_get_my_rights(struct acl_object *aclobj, pool_t pool, const char *const **rights_r); /* Returns the default rights for the object. */ const char *const *acl_object_get_default_rights(struct acl_object *aclobj); /* Returns timestamp of when the ACLs were last changed for this object, or 0 = never. */ int acl_object_last_changed(struct acl_object *aclobj, time_t *last_changed_r); /* Update ACL of given object. */ int acl_object_update(struct acl_object *aclobj, const struct acl_rights_update *update); /* List all identifiers. */ struct acl_object_list_iter *acl_object_list_init(struct acl_object *aclobj); int acl_object_list_next(struct acl_object_list_iter *iter, struct acl_rights *rights_r); void acl_object_list_deinit(struct acl_object_list_iter **iter); /* Returns the canonical ID for the right. */ const char *acl_rights_get_id(const struct acl_rights *right); #endif dovecot-2.2.33.2/src/plugins/acl/Makefile.am0000644000175000017500000000222613123174404015371 00000000000000doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/list \ -I$(top_srcdir)/src/doveadm NOPLUGIN_LDFLAGS = lib10_doveadm_acl_plugin_la_LDFLAGS = -module -avoid-version lib01_acl_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib01_acl_plugin.la lib01_acl_plugin_la_SOURCES = \ acl-api.c \ acl-attributes.c \ acl-backend.c \ acl-backend-vfile.c \ acl-backend-vfile-acllist.c \ acl-backend-vfile-update.c \ acl-cache.c \ acl-global-file.c \ acl-lookup-dict.c \ acl-mailbox.c \ acl-mailbox-list.c \ acl-plugin.c \ acl-shared-storage.c \ acl-storage.c noinst_HEADERS = \ acl-backend-vfile.h \ acl-shared-storage.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = \ acl-api.h \ acl-api-private.h \ acl-cache.h \ acl-global-file.h \ acl-lookup-dict.h \ acl-plugin.h \ acl-storage.h doveadm_module_LTLIBRARIES = \ lib10_doveadm_acl_plugin.la lib10_doveadm_acl_plugin_la_SOURCES = \ doveadm-acl.c dovecot-2.2.33.2/src/plugins/snarf/0002755000175000017500000000000013172375613014000 500000000000000dovecot-2.2.33.2/src/plugins/snarf/Makefile.in0000644000175000017500000005500213172375575015774 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/snarf ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib05_snarf_plugin_la_LIBADD = am_lib05_snarf_plugin_la_OBJECTS = snarf-plugin.lo lib05_snarf_plugin_la_OBJECTS = $(am_lib05_snarf_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib05_snarf_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib05_snarf_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib05_snarf_plugin_la_SOURCES) DIST_SOURCES = $(lib05_snarf_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib05_snarf_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib05_snarf_plugin.la lib05_snarf_plugin_la_SOURCES = \ snarf-plugin.c noinst_HEADERS = \ snarf-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/snarf/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/snarf/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib05_snarf_plugin.la: $(lib05_snarf_plugin_la_OBJECTS) $(lib05_snarf_plugin_la_DEPENDENCIES) $(EXTRA_lib05_snarf_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib05_snarf_plugin_la_LINK) -rpath $(moduledir) $(lib05_snarf_plugin_la_OBJECTS) $(lib05_snarf_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snarf-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/snarf/snarf-plugin.h0000644000175000017500000000020613123174404016461 00000000000000#ifndef SNARF_PLUGIN_H #define SNARF_PLUGIN_H void snarf_plugin_init(struct module *module); void snarf_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/snarf/snarf-plugin.c0000644000175000017500000001521213165463624016472 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "unichar.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "mail-storage-private.h" #include "snarf-plugin.h" #define SNARF_CONTEXT(obj) \ MODULE_CONTEXT(obj, snarf_storage_module) struct snarf_mail_storage { union mail_storage_module_context module_ctx; const char *snarf_path; bool snarfing_disabled; }; struct snarf_mailbox { union mailbox_module_context module_ctx; struct mailbox *snarf_box; }; const char *snarf_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(snarf_storage_module, &mail_storage_module_register); static int snarf(struct mailbox *srcbox, struct mailbox *destbox) { struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mailbox_transaction_context *src_trans, *dest_trans; struct mail_save_context *save_ctx; struct mail *mail; enum mail_error error; int ret; /* make sure the destination mailbox has been opened. note that this locks the mailbox. */ if (mailbox_open(destbox) < 0) return -1; if (mailbox_sync(srcbox, MAILBOX_SYNC_FLAG_FULL_READ) < 0) return -1; src_trans = mailbox_transaction_begin(srcbox, 0); dest_trans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); search_ctx = mailbox_search_init(src_trans, search_args, NULL, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); mail_search_args_unref(&search_args); ret = 0; while (mailbox_search_next(search_ctx, &mail)) { if (mail->expunged) continue; save_ctx = mailbox_save_alloc(dest_trans); if (mailbox_copy(&save_ctx, mail) < 0 && !mail->expunged) { error = mailbox_get_last_mail_error(destbox); /* if we failed because of out of disk space, just move those messages we managed to move so far. */ if (error != MAIL_ERROR_NOQUOTA) ret = -1; break; } mail_expunge(mail); } if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; /* commit the copied messages to the destination mailbox. if we crash between that and between expunging the messages from the source mailbox, we're left with duplicates. */ if (ret < 0) mailbox_transaction_rollback(&dest_trans); else if (mailbox_transaction_commit(&dest_trans) < 0) ret = -1; if (ret < 0) mailbox_transaction_rollback(&src_trans); else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } if (ret == 0) { if (mailbox_sync(srcbox, 0) < 0) ret = -1; } return ret; } static struct mailbox_sync_context * snarf_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct snarf_mailbox *sbox = SNARF_CONTEXT(box); (void)snarf(sbox->snarf_box, box); /* close the mailbox so that we don't have to keep it locked */ (void)mailbox_close(sbox->snarf_box); return sbox->module_ctx.super.sync_init(box, flags); } static void snarf_mailbox_free(struct mailbox *box) { struct snarf_mailbox *sbox = SNARF_CONTEXT(box); mailbox_free(&sbox->snarf_box); sbox->module_ctx.super.free(box); } static bool snarf_box_find(struct mail_user *user, struct mailbox_list **list_r, const char **name_r) { struct mail_namespace *snarf_ns; const char *snarf_name; snarf_name = mail_user_plugin_getenv(user, "snarf"); if (snarf_name == NULL) return FALSE; if (!uni_utf8_str_is_valid(snarf_name)) { i_error("snarf: Mailbox name not UTF-8: %s", snarf_name); return FALSE; } snarf_ns = mail_namespace_find(user->namespaces, snarf_name); *list_r = snarf_ns->list; *name_r = snarf_name; return TRUE; } static void snarf_mailbox_allocated(struct mailbox *box) { struct snarf_mail_storage *sstorage = SNARF_CONTEXT(box->storage); struct mailbox_vfuncs *v = box->vlast; struct snarf_mailbox *sbox; struct mailbox_list *snarf_list; const char *snarf_name; if (!box->inbox_user) return; if (sstorage != NULL && sstorage->snarfing_disabled) return; if (!snarf_box_find(box->storage->user, &snarf_list, &snarf_name)) return; sbox = p_new(box->pool, struct snarf_mailbox, 1); sbox->module_ctx.super = *v; box->vlast = &sbox->module_ctx.super; sbox->snarf_box = mailbox_alloc(snarf_list, snarf_name, MAILBOX_FLAG_KEEP_LOCKED); v->sync_init = snarf_sync_init; v->free = snarf_mailbox_free; MODULE_CONTEXT_SET(box, snarf_storage_module, sbox); } static struct mailbox * snarf_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *vname, enum mailbox_flags flags) { struct snarf_mail_storage *sstorage = SNARF_CONTEXT(storage); struct mail_namespace *ns = mailbox_list_get_namespace(list); struct mailbox *box; struct mailbox_list *snarf_list; const char *snarf_name; struct stat st; if (strcmp(vname, "INBOX") == 0 && (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { if (stat(sstorage->snarf_path, &st) == 0) sstorage->snarfing_disabled = FALSE; else { if (errno != ENOENT) { mail_storage_set_critical(storage, "stat(%s) failed: %m", sstorage->snarf_path); } sstorage->snarfing_disabled = TRUE; /* use the snarf box as our real INBOX */ if (snarf_box_find(storage->user, &snarf_list, &snarf_name)) { list = snarf_list; vname = snarf_name; } } } box = sstorage->module_ctx.super. mailbox_alloc(storage, list, vname, flags); if (sstorage->snarfing_disabled) { box->inbox_user = TRUE; box->inbox_any = TRUE; } return box; } static void snarf_mail_storage_create(struct mail_storage *storage, const char *path) { struct snarf_mail_storage *mstorage; struct mail_storage_vfuncs *v = storage->vlast; path = mail_user_home_expand(storage->user, path); mstorage = p_new(storage->pool, struct snarf_mail_storage, 1); mstorage->snarf_path = p_strdup(storage->pool, path); mstorage->module_ctx.super = *v; storage->vlast = &mstorage->module_ctx.super; v->mailbox_alloc = snarf_mailbox_alloc; MODULE_CONTEXT_SET(storage, snarf_storage_module, mstorage); } static void snarf_mail_storage_created(struct mail_storage *storage) { const char *path; /* snarfing is optional: do it only if the path specified by mbox_snarf exists */ path = mail_user_plugin_getenv(storage->user, "mbox_snarf"); if (path != NULL) snarf_mail_storage_create(storage, path); } static struct mail_storage_hooks snarf_mail_storage_hooks = { .mailbox_allocated = snarf_mailbox_allocated, .mail_storage_created = snarf_mail_storage_created }; void snarf_plugin_init(struct module *module) { mail_storage_hooks_add(module, &snarf_mail_storage_hooks); } void snarf_plugin_deinit(void) { mail_storage_hooks_remove(&snarf_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/snarf/Makefile.am0000644000175000017500000000053613123174404015745 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib05_snarf_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib05_snarf_plugin.la lib05_snarf_plugin_la_SOURCES = \ snarf-plugin.c noinst_HEADERS = \ snarf-plugin.h dovecot-2.2.33.2/src/plugins/stats/0002755000175000017500000000000013172375613014025 500000000000000dovecot-2.2.33.2/src/plugins/stats/Makefile.in0000644000175000017500000006311013172375575016020 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/stats ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" \ "$(DESTDIR)$(stats_moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) $(stats_module_LTLIBRARIES) lib90_stats_plugin_la_LIBADD = am_lib90_stats_plugin_la_OBJECTS = mail-stats.lo mail-stats-fill.lo \ mail-stats-connection.lo stats-plugin.lo lib90_stats_plugin_la_OBJECTS = $(am_lib90_stats_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib90_stats_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib90_stats_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = am_libstats_mail_la_OBJECTS = libstats_mail_la_OBJECTS = $(am_libstats_mail_la_OBJECTS) libstats_mail_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libstats_mail_la_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib90_stats_plugin_la_SOURCES) $(libstats_mail_la_SOURCES) DIST_SOURCES = $(lib90_stats_plugin_la_SOURCES) \ $(libstats_mail_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib90_stats_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib90_stats_plugin.la lib90_stats_plugin_la_SOURCES = \ mail-stats.c \ mail-stats-fill.c \ mail-stats-connection.c \ stats-plugin.c noinst_HEADERS = \ mail-stats.h \ mail-stats-connection.h \ stats-plugin.h stats_moduledir = $(moduledir)/stats stats_module_LTLIBRARIES = libstats_mail.la libstats_mail_la_LDFLAGS = -module -avoid-version libstats_mail_la_LIBADD = mail-stats.lo $(LIBDOVECOT) libstats_mail_la_DEPENDENCIES = mail-stats.lo libstats_mail_la_SOURCES = all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/stats/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/stats/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-stats_moduleLTLIBRARIES: $(stats_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(stats_module_LTLIBRARIES)'; test -n "$(stats_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(stats_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(stats_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(stats_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(stats_moduledir)"; \ } uninstall-stats_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(stats_module_LTLIBRARIES)'; test -n "$(stats_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(stats_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(stats_moduledir)/$$f"; \ done clean-stats_moduleLTLIBRARIES: -test -z "$(stats_module_LTLIBRARIES)" || rm -f $(stats_module_LTLIBRARIES) @list='$(stats_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib90_stats_plugin.la: $(lib90_stats_plugin_la_OBJECTS) $(lib90_stats_plugin_la_DEPENDENCIES) $(EXTRA_lib90_stats_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib90_stats_plugin_la_LINK) -rpath $(moduledir) $(lib90_stats_plugin_la_OBJECTS) $(lib90_stats_plugin_la_LIBADD) $(LIBS) libstats_mail.la: $(libstats_mail_la_OBJECTS) $(libstats_mail_la_DEPENDENCIES) $(EXTRA_libstats_mail_la_DEPENDENCIES) $(AM_V_CCLD)$(libstats_mail_la_LINK) -rpath $(stats_moduledir) $(libstats_mail_la_OBJECTS) $(libstats_mail_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-stats-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-stats-fill.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(stats_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ clean-stats_moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES \ install-stats_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES \ uninstall-stats_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES \ clean-stats_moduleLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-stats_moduleLTLIBRARIES install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES uninstall-stats_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/stats/stats-plugin.h0000644000175000017500000000312413123174404016535 00000000000000#ifndef STATS_PLUGIN_H #define STATS_PLUGIN_H #include "module-context.h" #include "mail-user.h" #include "mail-storage-private.h" #define STATS_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, stats_user_module) struct stats_user { union mail_user_module_context module_ctx; struct ioloop_context *ioloop_ctx; struct stats_connection *stats_conn; const char *stats_session_id; bool stats_connected; unsigned int refresh_secs; bool track_commands; unsigned int refresh_check_counter; /* current session statistics */ struct stats *session_stats; /* cumulative trans_stats for all already freed transactions. */ struct mailbox_transaction_stats finished_transaction_stats; /* stats before calling IO callback. after IO callback this value is compared to current stats to see the difference */ struct stats *pre_io_stats; time_t last_session_update; struct timeout *to_stats_timeout; /* stats that were last sent to stats server */ struct stats *last_sent_session_stats; bool session_sent_duplicate; /* list of all currently existing transactions for this user */ struct stats_transaction_context *transactions; }; struct stats_transaction_context { union mailbox_transaction_module_context module_ctx; struct stats_transaction_context *prev, *next; struct mailbox_transaction_context *trans; struct mailbox_transaction_stats prev_stats; }; extern MODULE_CONTEXT_DEFINE(stats_user_module, &mail_user_module_register); extern MODULE_CONTEXT_DEFINE(stats_storage_module, &mail_storage_module_register); void stats_plugin_init(struct module *module); void stats_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/stats/mail-stats-fill.c0000644000175000017500000000742613165463624017124 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "time-util.h" #include "stats-plugin.h" #include "mail-stats.h" #include #define PROC_IO_PATH "/proc/self/io" static bool proc_io_disabled = FALSE; static int proc_io_fd = -1; static int process_io_buffer_parse(const char *buf, struct mail_stats *stats) { const char *const *tmp; tmp = t_strsplit(buf, "\n"); for (; *tmp != NULL; tmp++) { if (strncmp(*tmp, "rchar: ", 7) == 0) { if (str_to_uint64(*tmp + 7, &stats->read_bytes) < 0) return -1; } else if (strncmp(*tmp, "wchar: ", 7) == 0) { if (str_to_uint64(*tmp + 7, &stats->write_bytes) < 0) return -1; } else if (strncmp(*tmp, "syscr: ", 7) == 0) { if (str_to_uint32(*tmp + 7, &stats->read_count) < 0) return -1; } else if (strncmp(*tmp, "syscw: ", 7) == 0) { if (str_to_uint32(*tmp + 7, &stats->write_count) < 0) return -1; } } return 0; } static int process_io_open(void) { uid_t uid; if (proc_io_fd != -1) return proc_io_fd; if (proc_io_disabled) return -1; proc_io_fd = open(PROC_IO_PATH, O_RDONLY); if (proc_io_fd == -1 && errno == EACCES) { /* kludge: if we're running with permissions temporarily dropped, get them temporarily back so we can open /proc/self/io. */ uid = geteuid(); if (seteuid(0) == 0) { proc_io_fd = open(PROC_IO_PATH, O_RDONLY); if (seteuid(uid) < 0) { /* oops, this is bad */ i_fatal("stats: seteuid(%s) failed", dec2str(uid)); } } errno = EACCES; } if (proc_io_fd == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", PROC_IO_PATH); proc_io_disabled = TRUE; return -1; } return proc_io_fd; } static void process_read_io_stats(struct mail_stats *stats) { char buf[1024]; int fd, ret; if ((fd = process_io_open()) == -1) return; ret = pread(fd, buf, sizeof(buf), 0); if (ret <= 0) { if (ret == -1) i_error("read(%s) failed: %m", PROC_IO_PATH); else i_error("read(%s) returned EOF", PROC_IO_PATH); } else if (ret == sizeof(buf)) { /* just shouldn't happen.. */ i_error("%s is larger than expected", PROC_IO_PATH); proc_io_disabled = TRUE; } else { buf[ret] = '\0'; T_BEGIN { if (process_io_buffer_parse(buf, stats) < 0) { i_error("Invalid input in file %s", PROC_IO_PATH); proc_io_disabled = TRUE; } } T_END; } } static void user_trans_stats_get(struct stats_user *suser, struct mail_stats *dest_r) { struct stats_transaction_context *strans; mail_stats_add_transaction(dest_r, &suser->finished_transaction_stats); for (strans = suser->transactions; strans != NULL; strans = strans->next) mail_stats_add_transaction(dest_r, &strans->trans->stats); } void mail_stats_fill(struct stats_user *suser, struct mail_stats *stats_r) { static bool getrusage_broken = FALSE; static struct rusage prev_usage; struct rusage usage; i_zero(stats_r); /* cputime */ if (getrusage(RUSAGE_SELF, &usage) < 0) { if (!getrusage_broken) { i_error("getrusage() failed: %m"); getrusage_broken = TRUE; } usage = prev_usage; } else if (timeval_diff_usecs(&usage.ru_stime, &prev_usage.ru_stime) < 0) { /* This seems to be a Linux bug. */ usage.ru_stime = prev_usage.ru_stime; } prev_usage = usage; stats_r->user_cpu = usage.ru_utime; stats_r->sys_cpu = usage.ru_stime; stats_r->min_faults = usage.ru_minflt; stats_r->maj_faults = usage.ru_majflt; stats_r->vol_cs = usage.ru_nvcsw; stats_r->invol_cs = usage.ru_nivcsw; stats_r->disk_input = (unsigned long long)usage.ru_inblock * 512ULL; stats_r->disk_output = (unsigned long long)usage.ru_oublock * 512ULL; (void)gettimeofday(&stats_r->clock_time, NULL); process_read_io_stats(stats_r); user_trans_stats_get(suser, stats_r); } void mail_stats_fill_global_deinit(void) { if (proc_io_fd != -1) i_close_fd(&proc_io_fd); } dovecot-2.2.33.2/src/plugins/stats/mail-stats-connection.c0000644000175000017500000000402413123174404020311 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "hostpid.h" #include "net.h" #include "str.h" #include "strescape.h" #include "mail-storage.h" #include "stats.h" #include "stats-plugin.h" #include "mail-stats-connection.h" int mail_stats_connection_connect(struct stats_connection *conn, struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(128); str_append(str, "CONNECT\t"); /* required fields */ str_append(str, suser->stats_session_id); str_append_c(str, '\t'); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); str_append_tabescaped(str, user->service); str_printfa(str, "\t%s", my_pid); /* optional fields */ if (user->local_ip != NULL) { str_append(str, "\tlip="); str_append(str, net_ip2addr(user->local_ip)); } if (user->remote_ip != NULL) { str_append(str, "\trip="); str_append(str, net_ip2addr(user->remote_ip)); } str_append_c(str, '\n'); return stats_connection_send(conn, str); } void mail_stats_connection_disconnect(struct stats_connection *conn, struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(128); str_append(str, "DISCONNECT\t"); str_append(str, suser->stats_session_id); str_append_c(str, '\n'); if (stats_connection_send(conn, str) < 0) { /* we could retry this later, but stats process will forget it anyway after 15 minutes. */ } } void mail_stats_connection_send_session(struct stats_connection *conn, struct mail_user *user, const struct stats *stats) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(256); buffer_t *buf; buf = buffer_create_dynamic(pool_datastack_create(), 128); stats_export(buf, stats); str_append(str, "UPDATE-SESSION\t"); str_append(str, suser->stats_session_id); str_append_c(str, '\t'); base64_encode(buf->data, buf->used, str); str_append_c(str, '\n'); (void)stats_connection_send(conn, str); } dovecot-2.2.33.2/src/plugins/stats/stats-plugin.c0000644000175000017500000003463113165463624016552 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "llist.h" #include "str.h" #include "time-util.h" #include "settings-parser.h" #include "mail-stats.h" #include "stats.h" #include "mail-stats-connection.h" #include "stats-plugin.h" #define STATS_CONTEXT(obj) \ MODULE_CONTEXT(obj, stats_storage_module) /* If session isn't refreshed every 15 minutes, it's dropped. Must be smaller than MAIL_SESSION_IDLE_TIMEOUT_MSECS in stats server */ #define SESSION_STATS_FORCE_REFRESH_SECS (5*60) #define REFRESH_CHECK_INTERVAL 100 #define MAIL_STATS_FIFO_NAME "stats-mail" struct stats_storage { union mail_storage_module_context module_ctx; struct mail_storage_callbacks old_callbacks; void *old_context; }; struct stats_mailbox { union mailbox_module_context module_ctx; }; const char *stats_plugin_version = DOVECOT_ABI_VERSION; struct stats_user_module stats_user_module = MODULE_CONTEXT_INIT(&mail_user_module_register); struct stats_storage_module stats_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); static struct stats_item *mail_stats_item; static struct stats_connection *global_stats_conn = NULL; static struct mail_user *stats_global_user = NULL; static unsigned int stats_user_count = 0; static void session_stats_refresh_timeout(struct mail_user *user); static void stats_io_activate(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct mail_stats *mail_stats; if (stats_user_count == 1) { /* the first user sets the global user. the second user sets it to NULL. when we get back to one user we'll need to set the global user again somewhere. do it here. */ stats_global_user = user; /* skip time spent waiting in ioloop */ mail_stats = stats_fill_ptr(suser->pre_io_stats, mail_stats_item); mail_stats->clock_time = ioloop_timeval; } else { i_assert(stats_global_user == NULL); mail_user_stats_fill(user, suser->pre_io_stats); } } static void stats_add_session(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct stats *new_stats, *diff_stats; const char *error; new_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); mail_user_stats_fill(user, new_stats); /* we'll count new_stats-pre_io_stats and add the changes to session_stats. the new_stats can't be directly copied to session_stats because there are some fields that don't start from zero, like clock_time. (actually with stats_global_user code we're requiring that clock_time is the only such field..) */ if (!stats_diff(suser->pre_io_stats, new_stats, diff_stats, &error)) i_error("stats: session stats shrank: %s", error); stats_add(suser->session_stats, diff_stats); /* copying is only needed if stats_global_user=NULL */ stats_copy(suser->pre_io_stats, new_stats); } static bool session_stats_need_send(struct stats_user *suser, time_t now, bool *changed_r, unsigned int *to_next_secs_r) { unsigned int diff; *to_next_secs_r = SESSION_STATS_FORCE_REFRESH_SECS; if (stats_have_changed(suser->last_sent_session_stats, suser->session_stats)) { *to_next_secs_r = suser->refresh_secs; *changed_r = TRUE; return TRUE; } *changed_r = FALSE; diff = now - suser->last_session_update; if (diff >= SESSION_STATS_FORCE_REFRESH_SECS) return TRUE; *to_next_secs_r = SESSION_STATS_FORCE_REFRESH_SECS - diff; if (!suser->session_sent_duplicate) { if (suser->last_session_update != now) { /* send one duplicate notification so stats reader knows that this session is idle now */ return TRUE; } } return FALSE; } static void session_stats_refresh(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); unsigned int to_next_secs; time_t now = time(NULL); bool changed; if (!suser->stats_connected) { if (mail_stats_connection_connect(suser->stats_conn, user) == 0) suser->stats_connected = TRUE; } if (session_stats_need_send(suser, now, &changed, &to_next_secs) && suser->stats_connected) { suser->session_sent_duplicate = !changed; suser->last_session_update = now; stats_copy(suser->last_sent_session_stats, suser->session_stats); mail_stats_connection_send_session(suser->stats_conn, user, suser->session_stats); } if (suser->to_stats_timeout != NULL) timeout_remove(&suser->to_stats_timeout); suser->to_stats_timeout = timeout_add(to_next_secs*1000, session_stats_refresh_timeout, user); } static struct mailbox_transaction_context * stats_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user); struct stats_mailbox *sbox = STATS_CONTEXT(box); struct mailbox_transaction_context *trans; struct stats_transaction_context *strans; trans = sbox->module_ctx.super.transaction_begin(box, flags); trans->stats_track = TRUE; strans = i_new(struct stats_transaction_context, 1); strans->trans = trans; DLLIST_PREPEND(&suser->transactions, strans); MODULE_CONTEXT_SET(trans, stats_storage_module, strans); return trans; } static void stats_transaction_free(struct stats_user *suser, struct stats_transaction_context *strans) { const struct mailbox_transaction_stats *src = &strans->trans->stats; struct mailbox_transaction_stats *dest = &suser->finished_transaction_stats; DLLIST_REMOVE(&suser->transactions, strans); dest->open_lookup_count += src->open_lookup_count; dest->stat_lookup_count += src->stat_lookup_count; dest->fstat_lookup_count += src->fstat_lookup_count; dest->files_read_count += src->files_read_count; dest->files_read_bytes += src->files_read_bytes; dest->cache_hit_count += src->cache_hit_count; i_free(strans); } static int stats_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { struct stats_transaction_context *strans = STATS_CONTEXT(ctx); struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box); struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user); stats_transaction_free(suser, strans); return sbox->module_ctx.super.transaction_commit(ctx, changes_r); } static void stats_transaction_rollback(struct mailbox_transaction_context *ctx) { struct stats_transaction_context *strans = STATS_CONTEXT(ctx); struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box); struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user); stats_transaction_free(suser, strans); sbox->module_ctx.super.transaction_rollback(ctx); } static bool stats_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r) { struct stats_mailbox *sbox = STATS_CONTEXT(ctx->transaction->box); struct mail_user *user = ctx->transaction->box->storage->user; struct stats_user *suser = STATS_USER_CONTEXT(user); bool ret; ret = sbox->module_ctx.super. search_next_nonblock(ctx, mail_r, tryagain_r); if (!ret && !*tryagain_r) { /* end of search */ return FALSE; } if (*tryagain_r || ++suser->refresh_check_counter % REFRESH_CHECK_INTERVAL == 0) { /* a) retrying, so this is a long running search. b) we've returned enough matches */ if (time(NULL) != suser->last_session_update) session_stats_refresh(user); } return ret; } static void stats_notify_ok(struct mailbox *box, const char *text, void *context) { struct stats_storage *sstorage = STATS_CONTEXT(box->storage); /* most importantly we want to refresh stats for very long running mailbox syncs */ session_stats_refresh(box->storage->user); if (sstorage->old_callbacks.notify_ok != NULL) sstorage->old_callbacks.notify_ok(box, text, context); } static void stats_register_notify_callbacks(struct mail_storage *storage) { struct stats_storage *sstorage = STATS_CONTEXT(storage); if (sstorage != NULL) return; sstorage = p_new(storage->pool, struct stats_storage, 1); sstorage->old_callbacks = storage->callbacks; storage->callbacks.notify_ok = stats_notify_ok; MODULE_CONTEXT_SET(storage, stats_storage_module, sstorage); } static void stats_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct stats_mailbox *sbox; struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user); if (suser == NULL) return; stats_register_notify_callbacks(box->storage); sbox = p_new(box->pool, struct stats_mailbox, 1); sbox->module_ctx.super = *v; box->vlast = &sbox->module_ctx.super; v->transaction_begin = stats_transaction_begin; v->transaction_commit = stats_transaction_commit; v->transaction_rollback = stats_transaction_rollback; v->search_next_nonblock = stats_search_next_nonblock; MODULE_CONTEXT_SET(box, stats_storage_module, sbox); } static void session_stats_refresh_timeout(struct mail_user *user) { if (stats_global_user != NULL) stats_add_session(user); session_stats_refresh(user); } static void stats_io_deactivate(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); unsigned int last_update_secs; if (stats_global_user == NULL) stats_add_session(user); last_update_secs = time(NULL) - suser->last_session_update; if (last_update_secs >= suser->refresh_secs) { if (stats_global_user != NULL) stats_add_session(user); session_stats_refresh(user); } else if (suser->to_stats_timeout == NULL) { suser->to_stats_timeout = timeout_add(suser->refresh_secs*1000, session_stats_refresh_timeout, user); } } static void stats_user_stats_fill(struct mail_user *user, struct stats *stats) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct mail_stats *mail_stats; mail_stats = stats_fill_ptr(stats, mail_stats_item); mail_stats_fill(suser, mail_stats); suser->module_ctx.super.stats_fill(user, stats); } static void stats_user_deinit(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct stats_connection *stats_conn = suser->stats_conn; i_assert(stats_user_count > 0); stats_user_count--; if (stats_global_user != NULL) { /* we were updating the session lazily. do one final update. */ i_assert(stats_global_user == user); stats_add_session(user); stats_global_user = NULL; } io_loop_context_remove_callbacks(suser->ioloop_ctx, stats_io_activate, stats_io_deactivate, user); /* send final stats before disconnection */ session_stats_refresh(user); if (suser->stats_connected) mail_stats_connection_disconnect(stats_conn, user); if (suser->to_stats_timeout != NULL) timeout_remove(&suser->to_stats_timeout); suser->module_ctx.super.deinit(user); stats_connection_unref(&stats_conn); } static void stats_user_created(struct mail_user *user) { struct ioloop_context *ioloop_ctx = io_loop_get_current_context(current_ioloop); struct stats_user *suser; struct mail_user_vfuncs *v = user->vlast; const char *path, *str, *error; unsigned int refresh_secs; if (ioloop_ctx == NULL) { /* we're probably running some test program, or at least mail-storage-service wasn't used to create this user. disable stats tracking. */ return; } if (user->autocreated) { /* lda / shared user. we're not tracking this one. */ return; } /* get refresh time */ str = mail_user_plugin_getenv(user, "stats_refresh"); if (str == NULL) return; if (settings_get_time(str, &refresh_secs, &error) < 0) { i_error("stats: Invalid stats_refresh setting: %s", error); return; } if (refresh_secs == 0) return; if (refresh_secs > SESSION_STATS_FORCE_REFRESH_SECS) { i_warning("stats: stats_refresh too large, changing to %u", SESSION_STATS_FORCE_REFRESH_SECS); refresh_secs = SESSION_STATS_FORCE_REFRESH_SECS; } if (global_stats_conn == NULL) { path = mail_user_plugin_getenv(user, "stats_notify_path"); if (path == NULL) path = MAIL_STATS_FIFO_NAME; if (path[0] != '/') path = t_strconcat(user->set->base_dir, "/", path, NULL); global_stats_conn = stats_connection_create(path); } stats_connection_ref(global_stats_conn); if (stats_user_count == 0) { /* first user connection */ stats_global_user = user; } else if (stats_user_count == 1) { /* second user connection. we'll need to start doing per-io callback tracking now. (we might have been doing it also previously but just temporarily quickly dropped to having 1 user, in which case stats_global_user=NULL) */ if (stats_global_user != NULL) { stats_add_session(stats_global_user); stats_global_user = NULL; } } stats_user_count++; suser = p_new(user->pool, struct stats_user, 1); suser->module_ctx.super = *v; user->vlast = &suser->module_ctx.super; v->deinit = stats_user_deinit; v->stats_fill = stats_user_stats_fill; suser->refresh_secs = refresh_secs; str = mail_user_plugin_getenv(user, "stats_track_cmds"); if (str != NULL && strcmp(str, "yes") == 0) suser->track_commands = TRUE; suser->stats_conn = global_stats_conn; if (user->session_id != NULL && user->session_id[0] != '\0') suser->stats_session_id = user->session_id; else { guid_128_t guid; guid_128_generate(guid); suser->stats_session_id = p_strdup(user->pool, guid_128_to_string(guid)); } suser->last_session_update = time(NULL); user->stats_enabled = TRUE; suser->ioloop_ctx = ioloop_ctx; io_loop_context_add_callbacks(ioloop_ctx, stats_io_activate, stats_io_deactivate, user); suser->pre_io_stats = stats_alloc(user->pool); suser->session_stats = stats_alloc(user->pool); suser->last_sent_session_stats = stats_alloc(user->pool); MODULE_CONTEXT_SET(user, stats_user_module, suser); if (mail_stats_connection_connect(suser->stats_conn, user) == 0) suser->stats_connected = TRUE; suser->to_stats_timeout = timeout_add(suser->refresh_secs*1000, session_stats_refresh_timeout, user); /* fill the initial values. this is necessary for the process-global values (e.g. getrusage()) if the process is reused for multiple users. otherwise the next user will start with the previous one's last values. */ mail_user_stats_fill(user, suser->pre_io_stats); } static struct mail_storage_hooks stats_mail_storage_hooks = { .mailbox_allocated = stats_mailbox_allocated, .mail_user_created = stats_user_created }; void stats_plugin_init(struct module *module) { mail_stats_item = stats_register(&mail_stats_vfuncs); mail_storage_hooks_add(module, &stats_mail_storage_hooks); } void stats_plugin_deinit(void) { if (global_stats_conn != NULL) stats_connection_unref(&global_stats_conn); mail_stats_fill_global_deinit(); mail_storage_hooks_remove(&stats_mail_storage_hooks); stats_unregister(&mail_stats_item); } dovecot-2.2.33.2/src/plugins/stats/mail-stats.c0000644000175000017500000001150513123174404016156 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "time-util.h" #include "stats.h" #include "stats-parser.h" #include "mail-stats.h" static struct stats_parser_field mail_stats_fields[] = { #define E(parsename, name, type) { parsename, offsetof(struct mail_stats, name), sizeof(((struct mail_stats *)0)->name), type } #define EN(parsename, name) E(parsename, name, STATS_PARSER_TYPE_UINT) E("user_cpu", user_cpu, STATS_PARSER_TYPE_TIMEVAL), E("sys_cpu", sys_cpu, STATS_PARSER_TYPE_TIMEVAL), E("clock_time", clock_time, STATS_PARSER_TYPE_TIMEVAL), EN("min_faults", min_faults), EN("maj_faults", maj_faults), EN("vol_cs", vol_cs), EN("invol_cs", invol_cs), EN("disk_input", disk_input), EN("disk_output", disk_output), EN("read_count", read_count), EN("read_bytes", read_bytes), EN("write_count", write_count), EN("write_bytes", write_bytes), /*EN("mopen", trans_stats.open_lookup_count), EN("mstat", trans_stats.stat_lookup_count), EN("mfstat", trans_stats.fstat_lookup_count),*/ EN("mail_lookup_path", trans_lookup_path), EN("mail_lookup_attr", trans_lookup_attr), EN("mail_read_count", trans_files_read_count), EN("mail_read_bytes", trans_files_read_bytes), EN("mail_cache_hits", trans_cache_hit_count) }; static size_t mail_stats_alloc_size(void) { return sizeof(struct mail_stats); } static unsigned int mail_stats_field_count(void) { return N_ELEMENTS(mail_stats_fields); } static const char *mail_stats_field_name(unsigned int n) { i_assert(n < N_ELEMENTS(mail_stats_fields)); return mail_stats_fields[n].name; } static void mail_stats_field_value(string_t *str, const struct stats *stats, unsigned int n) { i_assert(n < N_ELEMENTS(mail_stats_fields)); stats_parser_value(str, &mail_stats_fields[n], stats); } static bool mail_stats_diff(const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r) { return stats_parser_diff(mail_stats_fields, N_ELEMENTS(mail_stats_fields), stats1, stats2, diff_stats_r, error_r); } static void mail_stats_add(struct stats *dest, const struct stats *src) { stats_parser_add(mail_stats_fields, N_ELEMENTS(mail_stats_fields), dest, src); } static bool mail_stats_have_changed(const struct stats *_prev, const struct stats *_cur) { const struct mail_stats *prev = (const struct mail_stats *)_prev; const struct mail_stats *cur = (const struct mail_stats *)_cur; if (cur->disk_input != prev->disk_input || cur->disk_output != prev->disk_output || cur->trans_lookup_path != prev->trans_lookup_path || cur->trans_lookup_attr != prev->trans_lookup_attr || cur->trans_files_read_count != prev->trans_files_read_count || cur->trans_files_read_bytes != prev->trans_files_read_bytes || cur->trans_cache_hit_count != prev->trans_cache_hit_count) return TRUE; /* allow a tiny bit of changes that are caused by this timeout handling */ if (timeval_diff_msecs(&cur->user_cpu, &prev->user_cpu) != 0) return TRUE; if (timeval_diff_msecs(&cur->sys_cpu, &prev->sys_cpu) != 0) return TRUE; if (cur->maj_faults > prev->maj_faults+10) return TRUE; if (cur->invol_cs > prev->invol_cs+10) return TRUE; /* don't check for read/write count/bytes changes, since they get changed by stats checking itself */ return FALSE; } static void mail_stats_export(buffer_t *buf, const struct stats *_stats) { const struct mail_stats *stats = (const struct mail_stats *)_stats; buffer_append(buf, stats, sizeof(*stats)); } static bool mail_stats_import(const unsigned char *data, size_t size, size_t *pos_r, struct stats *_stats, const char **error_r) { struct mail_stats *stats = (struct mail_stats *)_stats; if (size < sizeof(*stats)) { *error_r = "mail_stats too small"; return FALSE; } memcpy(stats, data, sizeof(*stats)); *pos_r = sizeof(*stats); return TRUE; } void mail_stats_add_transaction(struct mail_stats *stats, const struct mailbox_transaction_stats *trans_stats) { stats->trans_lookup_path += trans_stats->open_lookup_count; stats->trans_lookup_attr += trans_stats->stat_lookup_count + trans_stats->fstat_lookup_count; stats->trans_files_read_count += trans_stats->files_read_count; stats->trans_files_read_bytes += trans_stats->files_read_bytes; stats->trans_cache_hit_count += trans_stats->cache_hit_count; } const struct stats_vfuncs mail_stats_vfuncs = { "mail", mail_stats_alloc_size, mail_stats_field_count, mail_stats_field_name, mail_stats_field_value, mail_stats_diff, mail_stats_add, mail_stats_have_changed, mail_stats_export, mail_stats_import }; /* for the stats_mail plugin: */ void stats_mail_init(void); void stats_mail_deinit(void); static struct stats_item *mail_stats_item; void stats_mail_init(void) { mail_stats_item = stats_register(&mail_stats_vfuncs); } void stats_mail_deinit(void) { stats_unregister(&mail_stats_item); } dovecot-2.2.33.2/src/plugins/stats/mail-stats.h0000644000175000017500000000217213123174404016163 00000000000000#ifndef MAIL_STATS_H #define MAIL_STATS_H #include #include "mail-storage-private.h" struct stats_user; struct mail_stats { /* user/system CPU time used */ struct timeval user_cpu, sys_cpu; /* clock time used (not counting the time in ioloop wait) */ struct timeval clock_time; /* minor / major page faults */ uint32_t min_faults, maj_faults; /* voluntary / involuntary context switches */ uint32_t vol_cs, invol_cs; /* disk input/output bytes */ uint64_t disk_input, disk_output; /* read()/write() syscall count and number of bytes */ uint32_t read_count, write_count; uint64_t read_bytes, write_bytes; /* based on struct mailbox_transaction_stats: */ uint32_t trans_lookup_path; uint32_t trans_lookup_attr; uint32_t trans_files_read_count; uint64_t trans_files_read_bytes; uint64_t trans_cache_hit_count; }; extern const struct stats_vfuncs mail_stats_vfuncs; void mail_stats_fill(struct stats_user *suser, struct mail_stats *mail_stats); void mail_stats_add_transaction(struct mail_stats *stats, const struct mailbox_transaction_stats *trans_stats); void mail_stats_fill_global_deinit(void); #endif dovecot-2.2.33.2/src/plugins/stats/mail-stats-connection.h0000644000175000017500000000106113123174404020314 00000000000000#ifndef MAIL_STATS_CONNECTION_H #define MAIL_STATS_CONNECTION_H #include "stats-connection.h" struct mail_stats; struct mail_user; int mail_stats_connection_connect(struct stats_connection *conn, struct mail_user *user); void mail_stats_connection_disconnect(struct stats_connection *conn, struct mail_user *user); void mail_stats_connection_send_session(struct stats_connection *conn, struct mail_user *user, const struct stats *stats); void mail_stats_connection_send(struct stats_connection *conn, const string_t *str); #endif dovecot-2.2.33.2/src/plugins/stats/Makefile.am0000644000175000017500000000146413123174404015773 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib90_stats_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib90_stats_plugin.la lib90_stats_plugin_la_SOURCES = \ mail-stats.c \ mail-stats-fill.c \ mail-stats-connection.c \ stats-plugin.c noinst_HEADERS = \ mail-stats.h \ mail-stats-connection.h \ stats-plugin.h stats_moduledir = $(moduledir)/stats stats_module_LTLIBRARIES = libstats_mail.la libstats_mail_la_LDFLAGS = -module -avoid-version libstats_mail_la_LIBADD = mail-stats.lo $(LIBDOVECOT) libstats_mail_la_DEPENDENCIES = mail-stats.lo libstats_mail_la_SOURCES = dovecot-2.2.33.2/src/plugins/mail-crypt/0002755000175000017500000000000013172375613014750 500000000000000dovecot-2.2.33.2/src/plugins/mail-crypt/fs-crypt-settings.c0000644000175000017500000000201313123174404020422 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "mail-storage-settings.h" #include "fs-crypt-settings.h" #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct fs_crypt_settings, name), NULL } static const struct setting_define fs_crypt_setting_defines[] = { { SET_STRLIST, "plugin", offsetof(struct fs_crypt_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; const struct fs_crypt_settings fs_crypt_default_settings = { .plugin_envs = ARRAY_INIT }; static const struct setting_parser_info *fs_crypt_setting_dependencies[] = { NULL }; const struct setting_parser_info fs_crypt_setting_parser_info = { .module_name = "fs-crypt", .defines = fs_crypt_setting_defines, .defaults = &fs_crypt_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct fs_crypt_settings), .parent_offset = (size_t)-1, .dependencies = fs_crypt_setting_dependencies }; dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-key.h0000644000175000017500000001125013165463624017707 00000000000000#ifndef MAIL_CRYPT_KEY #define MAIL_CRYPT_KEY #include "mail-crypt-common.h" #include "mail-crypt-global-key.h" #include "mail-storage.h" /* For mailboxes: shared//.../crypt/active = digest for the active public key that is used for encrypting new emails shared//.../crypt/pubkeys/ = private//.../crypt/privkeys/ = Similarly for users: shared//.../crypt/active = digest for the active public key that is used for encrypting new folder keys shared//.../crypt/pubkeys/ = private//.../crypt/privkeys/ = */ struct mail_crypt_key_cache_entry; /** * key cache management functions */ void mail_crypt_key_cache_destroy(struct mail_crypt_key_cache_entry **cache); void mail_crypt_key_register_mailbox_internal_attributes(void); /* returns -1 on error, 0 not found, 1 = found */ int mail_crypt_get_private_key(struct mailbox_transaction_context *t, const char *pubid, bool user_key, bool shared, struct dcrypt_private_key **key_r, const char **error_r); int mail_crypt_user_get_private_key(struct mail_user *user, const char *pubid, struct dcrypt_private_key **key_r, const char **error_r); int mail_crypt_box_get_private_key(struct mailbox_transaction_context *t, struct dcrypt_private_key **key_r, const char **error_r); int mail_crypt_box_get_private_keys(struct mailbox_transaction_context *t, ARRAY_TYPE(dcrypt_private_key) *keys_r, const char **error_r); int mail_crypt_user_get_public_key(struct mail_user *user, struct dcrypt_public_key **key_r, const char **error_r); int mail_crypt_box_get_public_key(struct mailbox_transaction_context *t, struct dcrypt_public_key **key_r, const char **error_r); int mail_crypt_box_get_shared_key(struct mailbox_transaction_context *t, const char *pubid, struct dcrypt_private_key **key_r, const char **error_r); /* returns -1 on error, 0 no match , 1 = match */ int mail_crypt_private_key_id_match(struct dcrypt_private_key *key, const char *pubid, const char **error_r); int mail_crypt_public_key_id_match(struct dcrypt_public_key *key, const char *pubid, const char **error_r); /* returns -1 on error, 0 = ok */ int mail_crypt_user_set_private_key(struct mail_user *user, const char *pubid, struct dcrypt_private_key *key, const char **error_r); int mail_crypt_box_set_private_key(struct mailbox *box, const char *pubid, struct dcrypt_private_key *key, struct dcrypt_public_key *user_key, const char **error_r); int mail_crypt_user_set_public_key(struct mail_user *user, const char *pubid, struct dcrypt_public_key *key, const char **error_r); int mail_crypt_box_set_public_key(struct mailbox *box, const char *pubid, struct dcrypt_public_key *key, const char **error_r); int mail_crypt_user_generate_keypair(struct mail_user *user, struct dcrypt_keypair *pair, const char **pubid_r, const char **error_r); int mail_crypt_box_generate_keypair(struct mailbox *box, struct dcrypt_keypair *pair, struct dcrypt_public_key *user_key, const char **pubid_r, const char **error_r); /* returns -1 on error, 0 = ok */ int mail_crypt_box_set_shared_key(struct mailbox_transaction_context *t, const char *pubid, struct dcrypt_private_key *privkey, const char *target_uid, struct dcrypt_public_key *user_key, const char **error_r); int mail_crypt_box_unset_shared_key(struct mailbox_transaction_context *t, const char *pubid, const char *target_uid, const char **error_r); int mail_crypt_box_share_private_keys(struct mailbox_transaction_context *t, struct dcrypt_public_key *dest_pub_key, const char *dest_user, const ARRAY_TYPE(dcrypt_private_key) *priv_keys, const char **error_r); /* returns -1 on error, 0 = ok these will also attempt to generate a keypair */ int mail_crypt_user_get_or_gen_public_key(struct mail_user *user, struct dcrypt_public_key **pub_key_r, const char **error_r); int mail_crypt_box_get_or_gen_public_key(struct mailbox_transaction_context *t, struct dcrypt_public_key **pub_key_r, const char **error_r); /* Lookup all private keys' digests. Returns 0 if ok, -1 on error. */ int mail_crypt_box_get_pvt_digests(struct mailbox *box, pool_t pool, enum mail_attribute_type type, ARRAY_TYPE(const_string) *digests, const char **error_r); /* is secure sharing enabled */ bool mail_crypt_acl_secure_sharing_enabled(struct mail_user *user); #endif dovecot-2.2.33.2/src/plugins/mail-crypt/Makefile.in0000644000175000017500000014241313172375574016746 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/plugins/mail-crypt ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib05_mail_crypt_acl_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ lib10_mail_crypt_plugin.la am_lib05_mail_crypt_acl_plugin_la_OBJECTS = mail-crypt-acl-plugin.lo lib05_mail_crypt_acl_plugin_la_OBJECTS = \ $(am_lib05_mail_crypt_acl_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib05_mail_crypt_acl_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib05_mail_crypt_acl_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = lib10_mail_crypt_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_lib10_mail_crypt_plugin_la_OBJECTS = mail-crypt-global-key.lo \ mail-crypt-userenv.lo mail-crypt-key.lo mail-crypt-plugin.lo lib10_mail_crypt_plugin_la_OBJECTS = \ $(am_lib10_mail_crypt_plugin_la_OBJECTS) lib10_mail_crypt_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib10_mail_crypt_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ am_libdoveadm_mail_crypt_plugin_la_OBJECTS = doveadm-mail-crypt.lo libdoveadm_mail_crypt_plugin_la_OBJECTS = \ $(am_libdoveadm_mail_crypt_plugin_la_OBJECTS) libdoveadm_mail_crypt_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(libdoveadm_mail_crypt_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ am_libfs_crypt_la_OBJECTS = fs-crypt.lo mail-crypt-global-key.lo \ mail-crypt-pluginenv.lo fs-crypt-settings.lo libfs_crypt_la_OBJECTS = $(am_libfs_crypt_la_OBJECTS) libfs_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libfs_crypt_la_LDFLAGS) $(LDFLAGS) -o \ $@ am_libfs_mail_crypt_la_OBJECTS = fs-mail-crypt.lo \ mail-crypt-global-key.lo mail-crypt-userenv.lo libfs_mail_crypt_la_OBJECTS = $(am_libfs_mail_crypt_la_OBJECTS) libfs_mail_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libfs_mail_crypt_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__EXEEXT_1 = test-mail-global-key$(EXEEXT) test-mail-key$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_mail_global_key_OBJECTS = \ test_mail_global_key-test-mail-global-key.$(OBJEXT) \ test_mail_global_key-fs-crypt-settings.$(OBJEXT) \ test_mail_global_key-mail-crypt-global-key.$(OBJEXT) test_mail_global_key_OBJECTS = $(am_test_mail_global_key_OBJECTS) test_mail_global_key_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(test_mail_global_key_CFLAGS) $(CFLAGS) \ $(test_mail_global_key_LDFLAGS) $(LDFLAGS) -o $@ am_test_mail_key_OBJECTS = test_mail_key-test-mail-key.$(OBJEXT) \ test_mail_key-mail-crypt-key.$(OBJEXT) \ test_mail_key-mail-crypt-global-key.$(OBJEXT) \ test_mail_key-mail-crypt-userenv.$(OBJEXT) test_mail_key_OBJECTS = $(am_test_mail_key_OBJECTS) test_mail_key_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_mail_key_CFLAGS) \ $(CFLAGS) $(test_mail_key_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib05_mail_crypt_acl_plugin_la_SOURCES) \ $(lib10_mail_crypt_plugin_la_SOURCES) \ $(libdoveadm_mail_crypt_plugin_la_SOURCES) \ $(libfs_crypt_la_SOURCES) $(libfs_mail_crypt_la_SOURCES) \ $(test_mail_global_key_SOURCES) $(test_mail_key_SOURCES) DIST_SOURCES = $(lib05_mail_crypt_acl_plugin_la_SOURCES) \ $(lib10_mail_crypt_plugin_la_SOURCES) \ $(libdoveadm_mail_crypt_plugin_la_SOURCES) \ $(libfs_crypt_la_SOURCES) $(libfs_mail_crypt_la_SOURCES) \ $(test_mail_global_key_SOURCES) $(test_mail_key_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-dcrypt \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/doveadm \ -I$(top_srcdir)/src/plugins/acl @SSL_VERSION_GE_102_FALSE@test_options = NOUNDEF=1 @SSL_VERSION_GE_102_TRUE@test_options = doveadm_moduledir = $(moduledir)/doveadm module_LTLIBRARIES = \ lib10_mail_crypt_plugin.la \ lib05_mail_crypt_acl_plugin.la \ libfs_crypt.la \ libfs_mail_crypt.la doveadm_module_LTLIBRARIES = \ libdoveadm_mail_crypt_plugin.la lib10_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version lib10_mail_crypt_plugin_la_LIBADD = \ $(LIBDCRYPT_LIBS) \ $(LIBDOVECOT) lib05_mail_crypt_acl_plugin_la_LDFLAGS = -module -avoid-version @DOVECOT_PLUGIN_DEPS_TRUE@lib05_mail_crypt_acl_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ $(LIBDCRYPT_LIBS) \ @DOVECOT_PLUGIN_DEPS_TRUE@ lib10_mail_crypt_plugin.la lib10_mail_crypt_plugin_la_SOURCES = \ mail-crypt-global-key.c \ mail-crypt-userenv.c \ mail-crypt-key.c \ mail-crypt-plugin.c lib05_mail_crypt_acl_plugin_la_SOURCES = \ mail-crypt-acl-plugin.c libfs_crypt_la_SOURCES = fs-crypt.c \ mail-crypt-global-key.c \ mail-crypt-pluginenv.c \ fs-crypt-settings.c libfs_crypt_la_LIBADD = $(LIBDOVECOT) libfs_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libfs_crypt_la_LDFLAGS = -module -avoid-version libfs_mail_crypt_la_SOURCES = fs-mail-crypt.c \ mail-crypt-global-key.c \ mail-crypt-userenv.c libfs_mail_crypt_la_LIBADD = $(LIBDOVECOT) libfs_mail_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libfs_mail_crypt_la_LDFLAGS = -module -avoid-version libdoveadm_mail_crypt_plugin_la_SOURCES = \ doveadm-mail-crypt.c libdoveadm_mail_crypt_plugin_la_LIBADD = $(LIBDOVECOT) libdoveadm_mail_crypt_plugin_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libdoveadm_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version test_programs = \ test-mail-global-key \ test-mail-key test_mail_global_key_SOURCES = \ test-mail-global-key.c \ fs-crypt-settings.c \ mail-crypt-global-key.c test_mail_global_key_LDADD = $(LIBDOVECOT) test_mail_global_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) test_mail_global_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) test_mail_global_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" test_mail_key_SOURCES = \ test-mail-key.c \ mail-crypt-key.c \ mail-crypt-global-key.c \ mail-crypt-userenv.c test_mail_key_LDADD = $(LIBDOVECOT) $(LIBDOVECOT_STORAGE) test_mail_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(LIBDOVECOT_STORAGE_DEPS) test_mail_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" EXTRA_DIST = fs-crypt-common.c noinst_HEADERS = \ mail-crypt-plugin.h \ mail-crypt-common.h \ mail-crypt-global-key.h \ mail-crypt-key.h \ fs-crypt-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/mail-crypt/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/mail-crypt/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib05_mail_crypt_acl_plugin.la: $(lib05_mail_crypt_acl_plugin_la_OBJECTS) $(lib05_mail_crypt_acl_plugin_la_DEPENDENCIES) $(EXTRA_lib05_mail_crypt_acl_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib05_mail_crypt_acl_plugin_la_LINK) -rpath $(moduledir) $(lib05_mail_crypt_acl_plugin_la_OBJECTS) $(lib05_mail_crypt_acl_plugin_la_LIBADD) $(LIBS) lib10_mail_crypt_plugin.la: $(lib10_mail_crypt_plugin_la_OBJECTS) $(lib10_mail_crypt_plugin_la_DEPENDENCIES) $(EXTRA_lib10_mail_crypt_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_mail_crypt_plugin_la_LINK) -rpath $(moduledir) $(lib10_mail_crypt_plugin_la_OBJECTS) $(lib10_mail_crypt_plugin_la_LIBADD) $(LIBS) libdoveadm_mail_crypt_plugin.la: $(libdoveadm_mail_crypt_plugin_la_OBJECTS) $(libdoveadm_mail_crypt_plugin_la_DEPENDENCIES) $(EXTRA_libdoveadm_mail_crypt_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(libdoveadm_mail_crypt_plugin_la_LINK) -rpath $(doveadm_moduledir) $(libdoveadm_mail_crypt_plugin_la_OBJECTS) $(libdoveadm_mail_crypt_plugin_la_LIBADD) $(LIBS) libfs_crypt.la: $(libfs_crypt_la_OBJECTS) $(libfs_crypt_la_DEPENDENCIES) $(EXTRA_libfs_crypt_la_DEPENDENCIES) $(AM_V_CCLD)$(libfs_crypt_la_LINK) -rpath $(moduledir) $(libfs_crypt_la_OBJECTS) $(libfs_crypt_la_LIBADD) $(LIBS) libfs_mail_crypt.la: $(libfs_mail_crypt_la_OBJECTS) $(libfs_mail_crypt_la_DEPENDENCIES) $(EXTRA_libfs_mail_crypt_la_DEPENDENCIES) $(AM_V_CCLD)$(libfs_mail_crypt_la_LINK) -rpath $(moduledir) $(libfs_mail_crypt_la_OBJECTS) $(libfs_mail_crypt_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-mail-global-key$(EXEEXT): $(test_mail_global_key_OBJECTS) $(test_mail_global_key_DEPENDENCIES) $(EXTRA_test_mail_global_key_DEPENDENCIES) @rm -f test-mail-global-key$(EXEEXT) $(AM_V_CCLD)$(test_mail_global_key_LINK) $(test_mail_global_key_OBJECTS) $(test_mail_global_key_LDADD) $(LIBS) test-mail-key$(EXEEXT): $(test_mail_key_OBJECTS) $(test_mail_key_DEPENDENCIES) $(EXTRA_test_mail_key_DEPENDENCIES) @rm -f test-mail-key$(EXEEXT) $(AM_V_CCLD)$(test_mail_key_LINK) $(test_mail_key_OBJECTS) $(test_mail_key_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-crypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-crypt-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-crypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-mail-crypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-acl-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-global-key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-pluginenv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-userenv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-test-mail-global-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-global-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-userenv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-test-mail-key.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< test_mail_global_key-test-mail-global-key.o: test-mail-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-test-mail-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo -c -o test_mail_global_key-test-mail-global-key.o `test -f 'test-mail-global-key.c' || echo '$(srcdir)/'`test-mail-global-key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo $(DEPDIR)/test_mail_global_key-test-mail-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-global-key.c' object='test_mail_global_key-test-mail-global-key.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-test-mail-global-key.o `test -f 'test-mail-global-key.c' || echo '$(srcdir)/'`test-mail-global-key.c test_mail_global_key-test-mail-global-key.obj: test-mail-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-test-mail-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo -c -o test_mail_global_key-test-mail-global-key.obj `if test -f 'test-mail-global-key.c'; then $(CYGPATH_W) 'test-mail-global-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-global-key.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo $(DEPDIR)/test_mail_global_key-test-mail-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-global-key.c' object='test_mail_global_key-test-mail-global-key.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-test-mail-global-key.obj `if test -f 'test-mail-global-key.c'; then $(CYGPATH_W) 'test-mail-global-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-global-key.c'; fi` test_mail_global_key-fs-crypt-settings.o: fs-crypt-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-fs-crypt-settings.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo -c -o test_mail_global_key-fs-crypt-settings.o `test -f 'fs-crypt-settings.c' || echo '$(srcdir)/'`fs-crypt-settings.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fs-crypt-settings.c' object='test_mail_global_key-fs-crypt-settings.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-fs-crypt-settings.o `test -f 'fs-crypt-settings.c' || echo '$(srcdir)/'`fs-crypt-settings.c test_mail_global_key-fs-crypt-settings.obj: fs-crypt-settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-fs-crypt-settings.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo -c -o test_mail_global_key-fs-crypt-settings.obj `if test -f 'fs-crypt-settings.c'; then $(CYGPATH_W) 'fs-crypt-settings.c'; else $(CYGPATH_W) '$(srcdir)/fs-crypt-settings.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fs-crypt-settings.c' object='test_mail_global_key-fs-crypt-settings.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-fs-crypt-settings.obj `if test -f 'fs-crypt-settings.c'; then $(CYGPATH_W) 'fs-crypt-settings.c'; else $(CYGPATH_W) '$(srcdir)/fs-crypt-settings.c'; fi` test_mail_global_key-mail-crypt-global-key.o: mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-mail-crypt-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo -c -o test_mail_global_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_global_key-mail-crypt-global-key.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c test_mail_global_key-mail-crypt-global-key.obj: mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-mail-crypt-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo -c -o test_mail_global_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_global_key-mail-crypt-global-key.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` test_mail_key-test-mail-key.o: test-mail-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-test-mail-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-test-mail-key.Tpo -c -o test_mail_key-test-mail-key.o `test -f 'test-mail-key.c' || echo '$(srcdir)/'`test-mail-key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-test-mail-key.Tpo $(DEPDIR)/test_mail_key-test-mail-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-key.c' object='test_mail_key-test-mail-key.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-test-mail-key.o `test -f 'test-mail-key.c' || echo '$(srcdir)/'`test-mail-key.c test_mail_key-test-mail-key.obj: test-mail-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-test-mail-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-test-mail-key.Tpo -c -o test_mail_key-test-mail-key.obj `if test -f 'test-mail-key.c'; then $(CYGPATH_W) 'test-mail-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-key.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-test-mail-key.Tpo $(DEPDIR)/test_mail_key-test-mail-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-key.c' object='test_mail_key-test-mail-key.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-test-mail-key.obj `if test -f 'test-mail-key.c'; then $(CYGPATH_W) 'test-mail-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-key.c'; fi` test_mail_key-mail-crypt-key.o: mail-crypt-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo -c -o test_mail_key-mail-crypt-key.o `test -f 'mail-crypt-key.c' || echo '$(srcdir)/'`mail-crypt-key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-key.c' object='test_mail_key-mail-crypt-key.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-key.o `test -f 'mail-crypt-key.c' || echo '$(srcdir)/'`mail-crypt-key.c test_mail_key-mail-crypt-key.obj: mail-crypt-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo -c -o test_mail_key-mail-crypt-key.obj `if test -f 'mail-crypt-key.c'; then $(CYGPATH_W) 'mail-crypt-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-key.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-key.c' object='test_mail_key-mail-crypt-key.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-key.obj `if test -f 'mail-crypt-key.c'; then $(CYGPATH_W) 'mail-crypt-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-key.c'; fi` test_mail_key-mail-crypt-global-key.o: mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo -c -o test_mail_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_key-mail-crypt-global-key.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c test_mail_key-mail-crypt-global-key.obj: mail-crypt-global-key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo -c -o test_mail_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-global-key.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_key-mail-crypt-global-key.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` test_mail_key-mail-crypt-userenv.o: mail-crypt-userenv.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-userenv.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo -c -o test_mail_key-mail-crypt-userenv.o `test -f 'mail-crypt-userenv.c' || echo '$(srcdir)/'`mail-crypt-userenv.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo $(DEPDIR)/test_mail_key-mail-crypt-userenv.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-userenv.c' object='test_mail_key-mail-crypt-userenv.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-userenv.o `test -f 'mail-crypt-userenv.c' || echo '$(srcdir)/'`mail-crypt-userenv.c test_mail_key-mail-crypt-userenv.obj: mail-crypt-userenv.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-userenv.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo -c -o test_mail_key-mail-crypt-userenv.obj `if test -f 'mail-crypt-userenv.c'; then $(CYGPATH_W) 'mail-crypt-userenv.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-userenv.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo $(DEPDIR)/test_mail_key-mail-crypt-userenv.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-userenv.c' object='test_mail_key-mail-crypt-userenv.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-userenv.obj `if test -f 'mail-crypt-userenv.c'; then $(CYGPATH_W) 'mail-crypt-userenv.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-userenv.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-noinstPROGRAMS cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-doveadm_moduleLTLIBRARIES install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! env $(test_options) $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-userenv.c0000644000175000017500000000367613123174404020603 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mail-user.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" static int mail_crypt_load_global_private_keys(struct mail_user *user, const char *set_prefix, struct mail_crypt_global_keys *global_keys, bool ignore_errors, const char **error_r) { string_t *set_key = t_str_new(64); str_append(set_key, set_prefix); str_append(set_key, "_private_key"); size_t prefix_len = str_len(set_key); unsigned int i = 1; const char *key_data; while ((key_data = mail_user_plugin_getenv(user, str_c(set_key))) != NULL) { const char *set_pw = t_strconcat(str_c(set_key), "_password", NULL); const char *password = mail_user_plugin_getenv(user, set_pw); if (mail_crypt_load_global_private_key(str_c(set_key), key_data, set_pw, password, global_keys, error_r) < 0) { /* skip this key */ if (ignore_errors) { if (user->namespaces->mail_set->mail_debug) i_debug("mail-crypt-plugin: " "mail_crypt_load_global_private_key failed: %s", *error_r); *error_r = NULL; continue; } return -1; } str_truncate(set_key, prefix_len); str_printfa(set_key, "%u", ++i); } return 0; } int mail_crypt_global_keys_load(struct mail_user *user, const char *set_prefix, struct mail_crypt_global_keys *global_keys_r, bool ignore_privkey_errors, const char **error_r) { const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); const char *key_data = mail_user_plugin_getenv(user, set_key); mail_crypt_global_keys_init(global_keys_r); if (key_data != NULL) { if (mail_crypt_load_global_public_key(set_key, key_data, global_keys_r, error_r) < 0) return -1; } if (mail_crypt_load_global_private_keys(user, set_prefix, global_keys_r, ignore_privkey_errors, error_r) < 0) return -1; return 0; } dovecot-2.2.33.2/src/plugins/mail-crypt/fs-crypt-common.c0000644000175000017500000002316113165463624020074 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "randgen.h" #include "istream.h" #include "ostream.h" #include "istream-decrypt.h" #include "ostream-encrypt.h" #include "iostream-temp.h" #include "mailbox-list.h" #include "mail-namespace.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "dcrypt-iostream.h" #include "fs-api-private.h" struct crypt_fs { struct fs fs; struct mail_crypt_global_keys keys; bool keys_loaded; char *enc_algo; char *set_prefix; char *public_key_path; char *private_key_path; char *password; }; struct crypt_fs_file { struct fs_file file; struct crypt_fs *fs; struct fs_file *super_read; enum fs_open_mode open_mode; struct istream *input; struct ostream *super_output; struct ostream *temp_output; }; /* defined outside this file */ extern const struct fs FS_CLASS_CRYPT; static int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r); static struct fs *fs_crypt_alloc(void) { struct crypt_fs *fs; fs = i_new(struct crypt_fs, 1); fs->fs = FS_CLASS_CRYPT; return &fs->fs; } static int fs_crypt_init(struct fs *_fs, const char *args, const struct fs_settings *set) { struct crypt_fs *fs = (struct crypt_fs *)_fs; const char *enc_algo, *set_prefix; const char *p, *arg, *value, *error, *parent_name, *parent_args; const char *public_key_path = "", *private_key_path = "", *password = ""; random_init(); if (!dcrypt_initialize("openssl", NULL, &error)) i_fatal("dcrypt_initialize(): %s", error); /* [algo=:][set_prefix=:][public_key_path=:] [private_key_path=:[password=:]] */ set_prefix = "mail_crypt_global"; enc_algo = "aes-256-gcm-sha256"; for (;;) { p = strchr(args, ':'); if (p == NULL) { fs_set_error(_fs, "Missing parameters"); return -1; } arg = t_strdup_until(args, p); if ((value = strchr(arg, '=')) == NULL) break; arg = t_strdup_until(arg, value++); args = p+1; if (strcmp(arg, "algo") == 0) enc_algo = value; else if (strcmp(arg, "set_prefix") == 0) set_prefix = value; else if (strcmp(arg, "public_key_path") == 0) public_key_path = value; else if (strcmp(arg, "private_key_path") == 0) private_key_path = value; else if (strcmp(arg, "password") == 0) password = value; else { fs_set_error(_fs, "Invalid parameter '%s'", arg); return -1; } } parent_args = strchr(args, ':'); if (parent_args == NULL) { parent_name = args; parent_args = ""; } else { parent_name = t_strdup_until(args, parent_args); parent_args++; } if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) { fs_set_error(_fs, "%s: %s", parent_name, error); return -1; } fs->enc_algo = i_strdup(enc_algo); fs->set_prefix = i_strdup(set_prefix); fs->public_key_path = i_strdup_empty(public_key_path); fs->private_key_path = i_strdup_empty(private_key_path); fs->password = i_strdup_empty(password); return 0; } static void fs_crypt_deinit(struct fs *_fs) { struct crypt_fs *fs = (struct crypt_fs *)_fs; mail_crypt_global_keys_free(&fs->keys); if (_fs->parent != NULL) fs_deinit(&_fs->parent); i_free(fs->enc_algo); i_free(fs->set_prefix); i_free(fs->public_key_path); i_free(fs->private_key_path); i_free(fs->password); i_free(fs); random_deinit(); } static struct fs_file * fs_crypt_file_init(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags) { struct crypt_fs *fs = (struct crypt_fs *)_fs; struct crypt_fs_file *file; file = i_new(struct crypt_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(path); file->fs = fs; file->open_mode = mode; /* avoid unnecessarily creating two seekable streams */ flags &= ~FS_OPEN_FLAG_SEEKABLE; file->file.parent = fs_file_init(_fs->parent, path, mode | flags); if (mode == FS_OPEN_MODE_READONLY && (flags & FS_OPEN_FLAG_ASYNC) == 0) { /* use async stream for super, so fs_read_stream() won't create another seekable stream unneededly */ file->super_read = fs_file_init(_fs->parent, path, mode | flags | FS_OPEN_FLAG_ASYNC); } else { file->super_read = file->file.parent; } return &file->file; } static void fs_crypt_file_deinit(struct fs_file *_file) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; if (file->super_read != _file->parent && file->super_read != NULL) fs_file_deinit(&file->super_read); fs_file_deinit(&_file->parent); i_free(file->file.path); i_free(file); } static void fs_crypt_file_close(struct fs_file *_file) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; if (file->input != NULL) i_stream_unref(&file->input); if (file->super_read != NULL) fs_file_close(file->super_read); if (_file->parent != NULL) fs_file_close(_file->parent); } static int fs_crypt_read_file(const char *set_name, const char *path, char **key_data_r, const char **error_r) { struct istream *input; int ret; input = i_stream_create_file(path, (size_t)-1); while (i_stream_read(input) > 0) ; if (input->stream_errno != 0) { *error_r = t_strdup_printf("%s: read(%s) failed: %s", set_name, path, i_stream_get_error(input)); ret = -1; } else { size_t size; const unsigned char *data = i_stream_get_data(input, &size); *key_data_r = i_strndup(data, size); ret = 0; } i_stream_unref(&input); return ret; } static int fs_crypt_load_keys_from_path(struct crypt_fs *fs, const char **error_r) { char *key_data; mail_crypt_global_keys_init(&fs->keys); if (fs->public_key_path != NULL) { if (fs_crypt_read_file("crypt:public_key_path", fs->public_key_path, &key_data, error_r) < 0) return -1; if (mail_crypt_load_global_public_key("crypt:public_key_path", key_data, &fs->keys, error_r) < 0) { i_free(key_data); return -1; } i_free(key_data); } if (fs->private_key_path != NULL) { if (fs_crypt_read_file("crypt:private_key_path", fs->private_key_path, &key_data, error_r) < 0) return -1; if (mail_crypt_load_global_private_key("crypt:private_key_path", key_data, "crypt:password", fs->password, &fs->keys, error_r) < 0) { i_free(key_data); return -1; } i_free(key_data); } return 0; } static int fs_crypt_istream_get_key(const char *pubkey_digest, struct dcrypt_private_key **priv_key_r, const char **error_r, void *context) { struct crypt_fs_file *file = context; if (fs_crypt_load_keys(file->fs, error_r) < 0) return -1; *priv_key_r = mail_crypt_global_key_find(&file->fs->keys, pubkey_digest); return *priv_key_r == NULL ? 0 : 1; } static struct istream * fs_crypt_read_stream(struct fs_file *_file, size_t max_buffer_size) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; struct istream *input; if (file->input != NULL) { i_stream_ref(file->input); i_stream_seek(file->input, 0); return file->input; } input = fs_read_stream(file->super_read, max_buffer_size); file->input = i_stream_create_decrypt_callback(input, fs_crypt_istream_get_key, file); i_stream_unref(&input); i_stream_ref(file->input); return file->input; } static void fs_crypt_write_stream(struct fs_file *_file) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; const char *error; i_assert(_file->output == NULL); if (fs_crypt_load_keys(file->fs, &error) < 0) { _file->output = o_stream_create_error_str(EIO, "Couldn't read settings: %s", error); return; } if (file->fs->keys.public_key == NULL) { if (_file->fs->set.debug) i_debug("No public key provided, " "NOT encrypting stream %s", fs_file_path(_file)); file->super_output = fs_write_stream(_file->parent); _file->output = file->super_output; return; } enum io_stream_encrypt_flags flags; if (strstr(file->fs->enc_algo, "gcm") != NULL || strstr(file->fs->enc_algo, "ccm") != NULL) { flags = IO_STREAM_ENC_INTEGRITY_AEAD; } else { flags = IO_STREAM_ENC_INTEGRITY_HMAC; } file->temp_output = iostream_temp_create_named(_file->fs->temp_path_prefix, IOSTREAM_TEMP_FLAG_TRY_FD_DUP, fs_file_path(_file)); _file->output = o_stream_create_encrypt(file->temp_output, file->fs->enc_algo, file->fs->keys.public_key, flags); } static int fs_crypt_write_stream_finish(struct fs_file *_file, bool success) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; struct istream *input; ssize_t ret; struct const_iovec iov; const unsigned char *data; if (_file->output != NULL) { if (_file->output == file->super_output) _file->output = NULL; else o_stream_unref(&_file->output); } if (!success) { if (file->super_output != NULL) { /* no encryption */ i_assert(file->temp_output == NULL); fs_write_stream_abort_error(_file->parent, &file->super_output, "write(%s) failed: %s", o_stream_get_name(file->super_output), o_stream_get_error(file->super_output)); } else if (file->temp_output != NULL) { o_stream_destroy(&file->temp_output); } return -1; } if (file->super_output != NULL) { /* no encrypt */ i_assert(file->temp_output == NULL); return fs_write_stream_finish(_file->parent, &file->super_output); } if (file->temp_output == NULL) { /* finishing up */ i_assert(file->super_output == NULL); return fs_write_stream_finish_async(_file->parent); } /* finish writing the temporary file */ input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE); file->super_output = fs_write_stream(_file->parent); while (i_stream_read_more(input, &data, &iov.iov_len) > 0) { iov.iov_base = data; o_stream_nsendv(file->super_output, &iov, 1); i_stream_skip(input, iov.iov_len); } ret = fs_write_stream_finish(_file->parent, &file->super_output); i_stream_unref(&input); return ret; } dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-plugin.h0000644000175000017500000000123613123174404020405 00000000000000#ifndef MAIL_CRYPT_PLUGIN_H #define MAIL_CRYPT_PLUGIN_H struct mailbox; struct module; struct mail_crypt_cache { struct timeout *to; struct mailbox *box; uint32_t uid; struct istream *input; }; struct mail_crypt_user { union mail_user_module_context module_ctx; struct mail_crypt_global_keys global_keys; struct mail_crypt_cache cache; struct mail_crypt_key_cache_entry *key_cache; const char *curve; int save_version; }; void mail_crypt_plugin_init(struct module *module); void mail_crypt_plugin_deinit(void); #define MAIL_CRYPT_MAIL_CACHE_EXPIRE_MSECS (60*1000) struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user); #endif dovecot-2.2.33.2/src/plugins/mail-crypt/fs-crypt-settings.h0000644000175000017500000000031113123174404020426 00000000000000#ifndef FS_CRYPT_SETTINGS_H #define FS_CRYPT_SETTINGS_H struct fs_crypt_settings { ARRAY(const char *) plugin_envs; }; extern const struct setting_parser_info fs_crypt_setting_parser_info; #endif dovecot-2.2.33.2/src/plugins/mail-crypt/test-mail-global-key.c0000644000175000017500000001075713165463624020771 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "randgen.h" #include "array.h" #include "dcrypt.h" #include "hex-binary.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "fs-crypt-settings.h" #include "mail-crypt-pluginenv.c" static struct fs_crypt_settings fs_set; static const char *settings[] = { "mail_crypt_global_private_key", "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1lJdWZKWlplMlk2aUZ6NXgKa29Jb3lzYjNkWkxaV3N5ZWtqT2MvR2pzTGQyaFJBTkNBQVNuSVdnUXVoRThqcUFMY21maXVuUnlFazd2a3EveQphOXZZSzUwYjNjRmhDc0xVNHRmVlRMa0IxWS82VmxaajYzUUtNelhOdms1RzVPRDFvZkVsY3B5agotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", "mail_crypt_global_public_key", "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcHlGb0VMb1JQSTZnQzNKbjRycDBjaEpPNzVLdgo4bXZiMkN1ZEc5M0JZUXJDMU9MWDFVeTVBZFdQK2xaV1krdDBDak0xemI1T1J1VGc5YUh4SlhLY293PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", "mail_crypt_global_private_key2", "LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLQpNSUhlTUVrR0NTcUdTSWIzRFFFRkRUQThNQnNHQ1NxR1NJYjNEUUVGRERBT0JBaXA2cUpja1FET3F3SUNDQUF3CkhRWUpZSVpJQVdVREJBRXFCQkFXN09oUFRlU0xSOExLcGYwZjZHa3ZCSUdRZk5rYUpodnM2VWVWS2RkN2NzdFMKMURSNXJYTWtON09FbVNjTTljRlk2UDVrMzdnY1VJUFZudTQrOTFYZUE1MTU2cnBpUEpycEdkZnprcjhPNVFqZApsMWRycmR6Z0hqZHE4T2VmbUR1MEEzMjRZd25SS3hGRExUcjlHMkxVMkhoYmV6a0xjV1FwMVJISDZsNXRRcUtwCjZid05iMnc3OXhCb01YSjN6MVZqcElOZk9wRnJ6M3lucVlqUXhseTIrQjg2Ci0tLS0tRU5EIEVOQ1JZUFRFRCBQUklWQVRFIEtFWS0tLS0tCg==", "mail_crypt_global_private_key2_password", "password", }; int mail_crypt_load_global_private_keys(const struct fs_crypt_settings *set, const char *set_prefix, struct mail_crypt_global_keys *global_keys, const char **error_r); static void test_setup(void) { struct dcrypt_settings set = { .module_dir = top_builddir "/src/lib-dcrypt/.libs" }; if (!dcrypt_initialize(NULL, &set, NULL)) { i_info("No functional dcrypt backend found - skipping tests"); test_exit(0); } i_array_init(&fs_set.plugin_envs, 8); array_append(&fs_set.plugin_envs, settings, N_ELEMENTS(settings)); } static void test_try_load_keys(void) { const char *pubid1 = "c79e262924842de291a8bcd413f4122a570abd033adeff7c1cdfdc9d05998c75"; const char *pubid2 = "aaf927444bff8b63425e852c6b3f769e8221b952b42cf886fae7d326c5be098e"; buffer_t *key_id = buffer_create_dynamic(pool_datastack_create(), 128); const char *error = NULL; test_begin("try_load_keys"); struct mail_crypt_global_keys keys; i_zero(&keys); mail_crypt_global_keys_init(&keys); const char *set_prefix = "mail_crypt_global"; const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); const char *key_data = mail_crypt_plugin_getenv(&fs_set, set_key); test_assert(key_data != NULL); if (key_data != NULL) { test_assert(mail_crypt_load_global_public_key(set_key, key_data, &keys, &error) == 0); test_assert(mail_crypt_load_global_private_keys(&fs_set, set_prefix, &keys, &error) == 0); /* did we get two private keys? */ test_assert(array_count(&keys.private_keys) == 2); /* public key id checks */ buffer_set_used_size(key_id, 0); test_assert(dcrypt_key_id_public(keys.public_key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid1) == 0); const struct mail_crypt_global_private_key *key = array_idx(&keys.private_keys, 0); buffer_set_used_size(key_id, 0); test_assert(dcrypt_key_id_private(key->key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid1) == 0); key = array_idx(&keys.private_keys, 1); buffer_set_used_size(key_id, 0); test_assert(dcrypt_key_id_private(key->key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid2) == 0); } mail_crypt_global_keys_free(&keys); test_end(); } static void test_empty_keyset(void) { test_begin("test_empty_keyset"); /* this should not crash */ struct mail_crypt_global_keys keys; i_zero(&keys); test_assert(mail_crypt_global_key_find(&keys, "423423423423") == NULL); test_end(); } static void test_teardown(void) { array_free(&fs_set.plugin_envs); dcrypt_deinitialize(); } int main(void) { void (*tests[])(void) = { test_setup, test_try_load_keys, test_empty_keyset, test_teardown, NULL }; random_init(); int ret = test_run(tests); return ret; } dovecot-2.2.33.2/src/plugins/mail-crypt/fs-crypt.c0000644000175000017500000000265013123174404016573 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #define FS_CLASS_CRYPT fs_class_crypt #include "fs-crypt-common.c" static int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r) { const char *error; if (fs->keys_loaded) return 0; if (fs->public_key_path != NULL || fs->private_key_path != NULL) { /* overrides using settings */ if (fs_crypt_load_keys_from_path(fs, error_r) < 0) return -1; fs->keys_loaded = TRUE; return 0; } if (mail_crypt_global_keys_load_pluginenv(fs->set_prefix, &fs->keys, &error) < 0) { *error_r = t_strdup_printf("%s: %s", fs->set_prefix, error); return -1; } fs->keys_loaded = TRUE; return 0; } const struct fs fs_class_crypt = { .name = "crypt", .v = { fs_crypt_alloc, fs_crypt_init, fs_crypt_deinit, fs_wrapper_get_properties, fs_crypt_file_init, fs_crypt_file_deinit, fs_crypt_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_wrapper_get_metadata, fs_wrapper_prefetch, fs_read_via_stream, fs_crypt_read_stream, fs_write_via_stream, fs_crypt_write_stream, fs_crypt_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_wrapper_stat, fs_wrapper_copy, fs_wrapper_rename, fs_wrapper_delete, fs_wrapper_iter_init, fs_wrapper_iter_next, fs_wrapper_iter_deinit, NULL, fs_wrapper_get_nlinks, } }; dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-plugin.c0000644000175000017500000003534513165463624020423 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ /* FIXME: cache handling could be useful to move to Dovecot core, so that if we're using this plugin together with zlib plugin there would be just one cache. */ #include "lib.h" #include "ioloop.h" #include "randgen.h" #include "module-dir.h" #include "str.h" #include "safe-mkstemp.h" #include "istream.h" #include "istream-decrypt.h" #include "istream-seekable.h" #include "ostream.h" #include "ostream-encrypt.h" #include "mail-user.h" #include "mail-copy.h" #include "index-storage.h" #include "index-mail.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mail-crypt-plugin.h" #include "sha2.h" #include "dcrypt-iostream.h" #include "hex-binary.h" struct mail_crypt_mailbox { union mailbox_module_context module_ctx; struct dcrypt_public_key *pub_key; }; const char *mail_crypt_plugin_version = DOVECOT_ABI_VERSION; #define MAIL_CRYPT_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_crypt_mail_module) #define MAIL_CRYPT_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_crypt_storage_module) #define MAIL_CRYPT_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_crypt_user_module) static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_user_module, &mail_user_module_register); static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_mail_module, &mail_module_register); struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user) { return MAIL_CRYPT_USER_CONTEXT(user); } static bool mail_crypt_is_stream_encrypted(struct istream *input) { const unsigned char *data = NULL; size_t size; if (i_stream_read_data(input, &data, &size, sizeof(IOSTREAM_CRYPT_MAGIC)) <= 0) { return FALSE; } if (memcmp(data, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) { return FALSE; } return TRUE; } static void mail_crypt_cache_close(struct mail_crypt_user *muser) { struct mail_crypt_cache *cache = &muser->cache; if (cache->to != NULL) timeout_remove(&cache->to); if (cache->input != NULL) i_stream_unref(&cache->input); i_zero(cache); } static struct istream * mail_crypt_cache_open(struct mail_crypt_user *muser, struct mail *mail, struct istream *input) { struct mail_crypt_cache *cache = &muser->cache; struct istream *inputs[2]; string_t *temp_prefix = t_str_new(128); mail_crypt_cache_close(muser); input->seekable = FALSE; inputs[0] = input; inputs[1] = NULL; mail_user_set_get_temp_prefix(temp_prefix, mail->box->storage->user->set); input = i_stream_create_seekable_path(inputs, i_stream_get_max_buffer_size(inputs[0]), str_c(temp_prefix)); i_stream_unref(&inputs[0]); if (mail->uid > 0) { cache->to = timeout_add(MAIL_CRYPT_MAIL_CACHE_EXPIRE_MSECS, mail_crypt_cache_close, muser); cache->box = mail->box; cache->uid = mail->uid; cache->input = input; /* index-mail wants the stream to be destroyed at close, so create a new stream instead of just increasing reference. */ return i_stream_create_limit(cache->input, (uoff_t)-1); } return input; } static int mail_crypt_istream_get_private_key(const char *pubkey_digest, struct dcrypt_private_key **priv_key_r, const char **error_r, void *context) { /* mailbox_crypt_search_all_private_keys requires error_r != NULL */ i_assert(error_r != NULL); int ret; struct mail *_mail = context; struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(_mail->box->storage->user); i_assert(muser != NULL); *priv_key_r = mail_crypt_global_key_find(&muser->global_keys, pubkey_digest); if (*priv_key_r != NULL) return 1; struct mail_namespace *ns = mailbox_get_namespace(_mail->box); struct mailbox_transaction_context *t = mailbox_transaction_begin(_mail->box, 0); if (ns->type == MAIL_NAMESPACE_TYPE_SHARED) { ret = mail_crypt_box_get_shared_key(t, pubkey_digest, priv_key_r, error_r); } else if (ns->type != MAIL_NAMESPACE_TYPE_PUBLIC) { ret = mail_crypt_get_private_key(t, pubkey_digest, FALSE, FALSE, priv_key_r, error_r); } else { *error_r = "Public emails cannot have keys"; ret = -1; } (void)mailbox_transaction_commit(&t); i_assert(ret <= 0 || *priv_key_r != NULL); return ret; } static int mail_crypt_istream_opened(struct mail *_mail, struct istream **stream) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_user *user = _mail->box->storage->user; struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(user); struct mail_crypt_cache *cache = &muser->cache; union mail_module_context *mmail = MAIL_CRYPT_MAIL_CONTEXT(mail); struct istream *input; if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { /* use the cached stream. when doing partial reads it should already be seeked into the wanted offset. */ i_stream_unref(stream); i_stream_seek(cache->input, 0); *stream = i_stream_create_limit(cache->input, (uoff_t)-1); return mmail->super.istream_opened(_mail, stream); } /* decryption is the outmost stream, so add it before others (e.g. zlib) */ if (!mail_crypt_is_stream_encrypted(*stream)) return mmail->super.istream_opened(_mail, stream); input = *stream; *stream = i_stream_create_decrypt_callback(input, mail_crypt_istream_get_private_key, _mail); i_stream_unref(&input); *stream = mail_crypt_cache_open(muser, _mail, *stream); return mmail->super.istream_opened(_mail, stream); } static void mail_crypt_close(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *mmail = MAIL_CRYPT_MAIL_CONTEXT(mail); struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(_mail->box->storage->user); struct mail_crypt_cache *cache = &muser->cache; uoff_t size; if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { /* make sure we have read the entire email into the seekable stream (which causes the original input stream to be unrefed). we can't safely keep the original input stream open after the mail is closed. */ if (i_stream_get_size(cache->input, TRUE, &size) < 0) mail_crypt_cache_close(muser); } mmail->super.close(_mail); } static void mail_crypt_mail_allocated(struct mail *_mail) { struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(_mail->box->storage->user); if (muser == NULL) return; struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *mmail; mmail = p_new(mail->pool, union mail_module_context, 1); mmail->super = *v; mail->vlast = &mmail->super; v->istream_opened = mail_crypt_istream_opened; v->close = mail_crypt_close; MODULE_CONTEXT_SET_SELF(mail, mail_crypt_mail_module, mmail); } static int mail_crypt_mail_save_finish(struct mail_save_context *ctx) { struct mailbox *box = ctx->transaction->box; union mailbox_module_context *zbox = MAIL_CRYPT_CONTEXT(box); struct istream *input; if (zbox->super.save_finish(ctx) < 0) return -1; /* we're here only if mail-crypt plugin is disabled. we want to make sure that even though we're saving an unencrypted mail, the mail can't be faked to look like an encrypted mail. */ if (mail_get_stream(ctx->dest_mail, NULL, NULL, &input) < 0) return -1; if (mail_crypt_is_stream_encrypted(input)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, "Saving mails encrypted by client isn't supported"); return -1; } return 0; } static int mail_crypt_mail_save_begin(struct mail_save_context *ctx, struct istream *input) { const char *pubid; struct mailbox *box = ctx->transaction->box; struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(box); struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(box->storage->user); i_assert(muser != NULL); enum io_stream_encrypt_flags enc_flags; if (muser->save_version == 1) { enc_flags = IO_STREAM_ENC_VERSION_1; } else if (muser->save_version == 2) { enc_flags = IO_STREAM_ENC_INTEGRITY_AEAD; } else { i_assert(muser->save_version == 0); i_panic("mail_crypt_mail_save_begin not supposed to be called" "when mail_crypt_save_version is 0"); } if (mbox->module_ctx.super.save_begin(ctx, input) < 0) return -1; struct dcrypt_public_key *pub_key; if (muser->global_keys.public_key != NULL) pub_key = muser->global_keys.public_key; else if (mbox->pub_key != NULL) pub_key = mbox->pub_key; else { const char *error; int ret; if ((ret = mail_crypt_box_get_public_key(ctx->transaction, &pub_key, &error)) <= 0) { struct dcrypt_keypair pair; if (ret < 0) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("get_public_key(%s) failed: %s", mailbox_get_vname(box), error)); return ret; } if (muser->save_version < 2) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("generate_keypair(%s) failed: " "unsupported save_version=%d", mailbox_get_vname(box), muser->save_version)); return -1; } if (mail_crypt_box_generate_keypair(box, &pair, NULL, &pubid, &error) < 0) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf("generate_keypair(%s) failed: %s", mailbox_get_vname(box), error)); return -1; } pub_key = pair.pub; dcrypt_key_unref_private(&pair.priv); } mbox->pub_key = pub_key; } /* encryption is the outermost layer (zlib etc. are inside) */ struct ostream *output = o_stream_create_encrypt(ctx->data.output, MAIL_CRYPT_ENC_ALGORITHM, pub_key, enc_flags); o_stream_unref(&ctx->data.output); ctx->data.output = output; o_stream_cork(ctx->data.output); return 0; } static int mail_crypt_mailbox_copy(struct mail_save_context *ctx, struct mail *mail) { struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(ctx->transaction->box); if (ctx->transaction->box != mail->box) return mail_storage_copy(ctx, mail); return mbox->module_ctx.super.copy(ctx, mail); } static void mail_crypt_mailbox_close(struct mailbox *box) { struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(box); struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(box->storage->user); if (mbox->pub_key != NULL) dcrypt_key_unref_public(&mbox->pub_key); if (muser != NULL && muser->cache.box == box) mail_crypt_cache_close(muser); mbox->module_ctx.super.close(box); } static void mail_crypt_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(box->storage->user); struct mail_crypt_mailbox *mbox; enum mail_storage_class_flags class_flags = box->storage->class_flags; mbox = p_new(box->pool, struct mail_crypt_mailbox, 1); mbox->module_ctx.super = *v; box->vlast = &mbox->module_ctx.super; v->close = mail_crypt_mailbox_close; MODULE_CONTEXT_SET(box, mail_crypt_storage_module, mbox); if ((class_flags & MAIL_STORAGE_CLASS_FLAG_BINARY_DATA) != 0) { if (muser != NULL) { if (muser->save_version > 0) { v->save_begin = mail_crypt_mail_save_begin; /* if global keys are used, re-encrypting on copy/move is not necessary, so do not attempt to do it. with per-folder keys, emails must be re-encrypted when moving to another folder */ if (muser->global_keys.public_key == NULL) v->copy = mail_crypt_mailbox_copy; } } else { v->save_finish = mail_crypt_mail_save_finish; } } } static void mail_crypt_mail_user_deinit(struct mail_user *user) { struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(user); mail_crypt_key_cache_destroy(&muser->key_cache); mail_crypt_global_keys_free(&muser->global_keys); mail_crypt_cache_close(muser); muser->module_ctx.super.deinit(user); } static void mail_crypt_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct mail_crypt_user *muser; const char *error = NULL; muser = p_new(user->pool, struct mail_crypt_user, 1); muser->module_ctx.super = *v; user->vlast = &muser->module_ctx.super; const char *curve = mail_user_plugin_getenv(user, "mail_crypt_curve"); buffer_t *tmp = t_str_new(64); if (curve == NULL || *curve == '\0') { if (user->mail_debug) { i_debug("mail_crypt_plugin: mail_crypt_curve setting " "missing - generating EC keys disabled"); } } else if (!dcrypt_name2oid(curve, tmp, &error)) { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: " "invalid mail_crypt_curve setting %s: %s - " "plugin disabled", curve, error); } else { muser->curve = p_strdup(user->pool, curve); } const char *version = mail_user_plugin_getenv(user, "mail_crypt_save_version"); if (version == NULL || *version == '\0') { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: " "mail_crypt_save_version setting missing " "- plugin disabled"); } else if (version[0] == '0') { muser->save_version = 0; } else if (version[0] == '1') { muser->save_version = 1; } else if (version[0] == '2') { muser->save_version = 2; } else { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: Invalid " "mail_crypt_save_version %s: use 0, 1, or 2 ", version); } if (mail_crypt_global_keys_load(user, "mail_crypt_global", &muser->global_keys, FALSE, &error) < 0) { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: %s", error); } v->deinit = mail_crypt_mail_user_deinit; MODULE_CONTEXT_SET(user, mail_crypt_user_module, muser); } static struct mail_storage_hooks mail_crypt_mail_storage_hooks = { .mail_user_created = mail_crypt_mail_user_created, .mail_allocated = mail_crypt_mail_allocated }; static struct mail_storage_hooks mail_crypt_mail_storage_hooks_post = { .mailbox_allocated = mail_crypt_mailbox_allocated }; static struct module crypto_post_module = { .path = "lib95_mail_crypt_plugin.so" }; void mail_crypt_plugin_init(struct module *module) { const char* error; random_init(); if (!dcrypt_initialize("openssl", NULL, &error)) i_fatal("dcrypt_initialize(): %s", error); mail_storage_hooks_add(module, &mail_crypt_mail_storage_hooks); /* rather kludgy. we need to hook into mail reading as early as possible, but we need to hook into mail writing as late as possible. we could create just two real plugins, but that's a bit annoying to configure. */ mail_storage_hooks_add_forced(&crypto_post_module, &mail_crypt_mail_storage_hooks_post); mail_crypt_key_register_mailbox_internal_attributes(); } void mail_crypt_plugin_deinit(void) { mail_storage_hooks_remove(&mail_crypt_mail_storage_hooks); mail_storage_hooks_remove(&mail_crypt_mail_storage_hooks_post); random_deinit(); } dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-global-key.h0000644000175000017500000000246513123174404021142 00000000000000#ifndef MAIL_CRYPT_GLOBAL_KEY_H #define MAIL_CRYPT_GLOBAL_KEY_H struct mail_crypt_global_private_key { struct dcrypt_private_key *key; char *key_id, *key_id_old; }; struct mail_crypt_global_keys { struct dcrypt_public_key *public_key; ARRAY(struct mail_crypt_global_private_key) private_keys; }; struct mail_user; int mail_crypt_global_keys_load(struct mail_user *user, const char *set_prefix, struct mail_crypt_global_keys *global_keys_r, bool ignore_privkey_errors, const char **error_r); int mail_crypt_global_keys_load_pluginenv(const char *set_prefix, struct mail_crypt_global_keys *global_keys_r, const char **error_r); void mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r); void mail_crypt_global_keys_free(struct mail_crypt_global_keys *global_keys); int mail_crypt_load_global_public_key(const char *set_key, const char *key_data, struct mail_crypt_global_keys *global_keys, const char **error_r); int mail_crypt_load_global_private_key(const char *set_key, const char *key_data, const char *set_pw, const char *key_password, struct mail_crypt_global_keys *global_keys, const char **error_r); struct dcrypt_private_key * mail_crypt_global_key_find(struct mail_crypt_global_keys *global_keys, const char *pubkey_digest); #endif dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-pluginenv.c0000644000175000017500000000547613123174404021123 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "array.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "fs-crypt-settings.h" static const struct fs_crypt_settings * fs_crypt_load_settings(void) { static const struct setting_parser_info *set_roots[] = { &fs_crypt_setting_parser_info, NULL }; struct master_service_settings_input input; struct master_service_settings_output output; const char *error; i_zero(&input); input.roots = set_roots; input.module = "fs-crypt"; input.service = "fs-crypt"; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); return master_service_settings_get_others(master_service)[0]; } static const char *mail_crypt_plugin_getenv(const struct fs_crypt_settings *set, const char *name) { const char *const *envs; unsigned int i, count; if (set == NULL) return NULL; if (!array_is_created(&set->plugin_envs)) return NULL; envs = array_get(&set->plugin_envs, &count); for (i = 0; i < count; i += 2) { if (strcmp(envs[i], name) == 0) return envs[i+1]; } return NULL; } static int mail_crypt_load_global_private_keys(const struct fs_crypt_settings *set, const char *set_prefix, struct mail_crypt_global_keys *global_keys, const char **error_r) { string_t *set_key = t_str_new(64); str_append(set_key, set_prefix); str_append(set_key, "_private_key"); size_t prefix_len = str_len(set_key); unsigned int i = 1; const char *key_data; while ((key_data = mail_crypt_plugin_getenv(set, str_c(set_key))) != NULL) { const char *set_pw = t_strconcat(str_c(set_key), "_password", NULL); const char *password = mail_crypt_plugin_getenv(set, set_pw); if (mail_crypt_load_global_private_key(str_c(set_key), key_data, set_pw, password, global_keys, error_r) < 0) return -1; str_truncate(set_key, prefix_len); str_printfa(set_key, "%u", ++i); } return 0; } int mail_crypt_global_keys_load_pluginenv(const char *set_prefix, struct mail_crypt_global_keys *global_keys_r, const char **error_r) { const struct fs_crypt_settings *set = fs_crypt_load_settings(); const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); const char *key_data = mail_crypt_plugin_getenv(set, set_key); int ret = 0; mail_crypt_global_keys_init(global_keys_r); if (key_data != NULL) { if (mail_crypt_load_global_public_key(set_key, key_data, global_keys_r, error_r) < 0) ret = -1; } if (ret == 0 && mail_crypt_load_global_private_keys(set, set_prefix, global_keys_r, error_r) < 0) ret = -1; mail_crypt_global_keys_free(global_keys_r); return ret; } dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-common.h0000644000175000017500000000216313123174404020377 00000000000000#ifndef MAIL_CRYPT_COMMON_H #define MAIL_CRYPT_COMMON_H #include "dcrypt.h" #define MAIL_CRYPT_PW_CIPHER "aes-256-ctr" #define MAIL_CRYPT_KEY_CIPHER "ecdh-aes-256-ctr" #define MAIL_CRYPT_ENC_ALGORITHM "aes-256-gcm-sha256" #define MAIL_CRYPT_KEY_ID_ALGORITHM "sha256" #define MAIL_CRYPT_KEY_ATTRIBUTE_FORMAT DCRYPT_FORMAT_DOVECOT #define MAIL_CRYPT_ACL_SECURE_SHARE_SETTING "mail_crypt_acl_require_secure_key_sharing" #define MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY "mail_crypt_require_encrypted_user_key" #define MAIL_CRYPT_HASH_BUF_SIZE 128 #define MAIL_CRYPT_KEY_BUF_SIZE 1024 #define ACTIVE_KEY_NAME "active" #define PUBKEYS_PREFIX "pubkeys/" #define PRIVKEYS_PREFIX "privkeys/" #define BOX_CRYPT_PREFIX MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"crypt/" #define USER_CRYPT_PREFIX \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"crypt/" #define MAIL_CRYPT_USERENV_PASSWORD "mail_crypt_private_password" #define MAIL_CRYPT_USERENV_KEY "mail_crypt_private_key" #define MAIL_CRYPT_USERENV_CURVE "mail_crypt_curve" ARRAY_DEFINE_TYPE(dcrypt_private_key, struct dcrypt_private_key*); #endif dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-global-key.c0000644000175000017500000001172613123174404021135 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hex-binary.h" #include "base64.h" #include "mail-user.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mail-crypt-plugin.h" int mail_crypt_load_global_public_key(const char *set_key, const char *key_data, struct mail_crypt_global_keys *global_keys, const char **error_r) { const char *error; enum dcrypt_key_format format; enum dcrypt_key_kind kind; if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, NULL, NULL, NULL, &error)) { key_data = str_c(t_base64_decode_str(key_data)); if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, NULL, NULL, NULL, &error)) { *error_r = t_strdup_printf("%s: Couldn't parse public key: %s", set_key, error); return -1; } } if (kind != DCRYPT_KEY_KIND_PUBLIC) { *error_r = t_strdup_printf("%s: key is not public", set_key); return -1; } if (!dcrypt_key_load_public(&global_keys->public_key, key_data, &error)) { *error_r = t_strdup_printf("%s: Couldn't load public key: %s", set_key, error); return -1; } return 0; } static int mail_crypt_key_get_ids(struct dcrypt_private_key *key, const char **key_id_r, const char **key_id_old_r, const char **error_r) { const char *error; buffer_t *key_id; *key_id_r = NULL; *key_id_old_r = NULL; /* new key ID */ key_id = buffer_create_dynamic(pool_datastack_create(), MAIL_CRYPT_HASH_BUF_SIZE); if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error)) { *error_r = t_strdup_printf("Failed to get private key ID: %s", error); return -1; } *key_id_r = binary_to_hex(key_id->data, key_id->used); buffer_set_used_size(key_id, 0); /* old key ID */ if (dcrypt_key_type_private(key) != DCRYPT_KEY_EC) return 0; if (!dcrypt_key_id_private_old(key, key_id, &error)) { *error_r = t_strdup_printf("Failed to get private key old ID: %s", error); return -1; } *key_id_old_r = binary_to_hex(key_id->data, key_id->used); return 0; } int mail_crypt_load_global_private_key(const char *set_key, const char *key_data, const char *set_pw, const char *key_password, struct mail_crypt_global_keys *global_keys, const char **error_r) { enum dcrypt_key_format format; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type enc_type; const char *error; if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, &enc_type, NULL, NULL, &error)) { key_data = str_c(t_base64_decode_str(key_data)); if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, &enc_type, NULL, NULL, &error)) { *error_r = t_strdup_printf("%s: Couldn't parse private" " key: %s", set_key, error); return -1; } } if (kind != DCRYPT_KEY_KIND_PRIVATE) { *error_r = t_strdup_printf("%s: key is not private", set_key); return -1; } if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) { /* Fail here if password is not set since openssl will prompt * for it otherwise */ if (key_password == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("%s: %s unset, no " "password to decrypt the key", set_key, set_pw); return -1; } } struct dcrypt_private_key *key = NULL; if (!dcrypt_key_load_private(&key, key_data, key_password, NULL, &error)) { *error_r = t_strdup_printf("%s: Couldn't load private key: %s", set_key, error); return -1; } const char *key_id, *key_id_old; if (mail_crypt_key_get_ids(key, &key_id, &key_id_old, error_r) < 0) { dcrypt_key_unref_private(&key); return -1; } struct mail_crypt_global_private_key *priv_key = array_append_space(&global_keys->private_keys); priv_key->key = key; priv_key->key_id = i_strdup(key_id); priv_key->key_id_old = i_strdup(key_id_old); return 0; } void mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r) { i_zero(global_keys_r); i_array_init(&global_keys_r->private_keys, 4); } void mail_crypt_global_keys_free(struct mail_crypt_global_keys *global_keys) { struct mail_crypt_global_private_key *priv_key; if (global_keys->public_key != NULL) dcrypt_key_unref_public(&global_keys->public_key); if (!array_is_created(&global_keys->private_keys)) return; array_foreach_modifiable(&global_keys->private_keys, priv_key) { dcrypt_key_unref_private(&priv_key->key); i_free(priv_key->key_id); i_free(priv_key->key_id_old); } array_free(&global_keys->private_keys); } struct dcrypt_private_key * mail_crypt_global_key_find(struct mail_crypt_global_keys *global_keys, const char *pubkey_digest) { const struct mail_crypt_global_private_key *priv_key; if (!array_is_created(&global_keys->private_keys)) return NULL; array_foreach(&global_keys->private_keys, priv_key) { if (strcmp(priv_key->key_id, pubkey_digest) == 0) return priv_key->key; if (priv_key->key_id_old != NULL && strcmp(priv_key->key_id_old, pubkey_digest) == 0) return priv_key->key; } return NULL; } dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-acl-plugin.c0000644000175000017500000002664213165463624021160 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop-private.h" #include "str.h" #include "sha2.h" #include "module-dir.h" #include "var-expand.h" #include "hex-binary.h" #include "mail-namespace.h" #include "mail-storage-hooks.h" #include "mail-storage-service.h" #include "acl-plugin.h" #include "acl-api-private.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mail-crypt-plugin.h" #define MAIL_CRYPT_ACL_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_crypt_acl_mailbox_list_module) struct mail_crypt_acl_mailbox_list { union mailbox_list_module_context module_ctx; struct acl_backend_vfuncs acl_vprev; }; static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_acl_mailbox_list_module, &mailbox_list_module_register); void mail_crypt_acl_plugin_init(struct module *module); void mail_crypt_acl_plugin_deinit(void); static int mail_crypt_acl_has_user_read_right(struct acl_object *aclobj, const char *username, const char **error_r ATTR_UNUSED) { struct acl_object_list_iter *iter; struct acl_rights rights; int ret; iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (rights.id_type == ACL_ID_USER && strcmp(rights.identifier, username) == 0) { ret = str_array_find(rights.rights, MAIL_ACL_READ) ? 1 : 0; break; } } acl_object_list_deinit(&iter); return ret; } static int mail_crypt_acl_has_nonuser_read_right(struct acl_object *aclobj, const char **error_r ATTR_UNUSED) { struct acl_object_list_iter *iter; struct acl_rights rights; int ret; iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (rights.id_type != ACL_ID_USER && rights.id_type != ACL_ID_OWNER && rights.rights != NULL && str_array_find(rights.rights, MAIL_ACL_READ)) { ret = 1; break; } } acl_object_list_deinit(&iter); return ret; } static int mail_crypt_acl_unset_private_keys(struct mailbox *src_box, const char *dest_user, enum mail_attribute_type type, const char **error_r) { ARRAY_TYPE(const_string) digests; const char *error; int ret = 1; t_array_init(&digests, 4); if (mail_crypt_box_get_pvt_digests(src_box, pool_datastack_create(), type, &digests, &error) < 0) { *error_r = t_strdup_printf("mail-crypt-acl-plugin: " "Failed to lookup public key digests: %s", error); mailbox_free(&src_box); return -1; } struct mailbox_transaction_context *t; t = mailbox_transaction_begin(src_box, 0); const char *const *hash; array_foreach(&digests, hash) { const char *ptr; /* if the id contains username part, skip to key public id */ if ((ptr = strchr(*hash, '/')) != NULL) ptr++; else ptr = *hash; if ((ret = mail_crypt_box_unset_shared_key(t, ptr, dest_user, error_r)) < 0) { ret = -1; break; } } if (ret < 0) { mailbox_transaction_rollback(&t); } else if (mailbox_transaction_commit(&t) < 0) { *error_r = t_strdup_printf("mail-crypt-acl-plugin: " "mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(src_box), mailbox_get_last_internal_error(src_box, NULL)); return -1; } return 0; } static int mail_crypt_acl_user_create(struct mail_user *user, const char *dest_username, struct mail_user **dest_user_r, struct mail_storage_service_user **dest_service_user_r, const char **error_r) { const struct mail_storage_service_input *old_input; struct mail_storage_service_input input; struct mail_storage_service_ctx *service_ctx; struct ioloop_context *cur_ioloop_ctx; int ret; i_assert(user->_service_user != NULL); service_ctx = mail_storage_service_user_get_service_ctx(user->_service_user); old_input = mail_storage_service_user_get_input(user->_service_user); if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) io_loop_context_deactivate(cur_ioloop_ctx); i_zero(&input); input.module = old_input->module; input.service = old_input->service; input.username = dest_username; input.session_id_prefix = user->session_id; input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; input.flags_override_remove = MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; ret = mail_storage_service_lookup_next(service_ctx, &input, dest_service_user_r, dest_user_r, error_r); return ret; } static int mail_crypt_acl_update_private_key(struct mailbox *src_box, struct mail_user *dest_user, bool set, bool disallow_insecure, const char **error_r) { struct dcrypt_public_key *key = NULL; struct dcrypt_private_key **keyp; int ret = 0; if (!set) { return mail_crypt_acl_unset_private_keys(src_box, dest_user->username, MAIL_ATTRIBUTE_TYPE_SHARED, error_r); } if (dest_user != NULL) { /* get public key from target user */ if ((ret = mail_crypt_user_get_public_key(dest_user, &key, error_r)) <= 0) { if (ret == 0 && disallow_insecure) { *error_r = t_strdup_printf("User %s has no active public key", dest_user->username); return -1; } else if (ret < 0) { return -1; } else if (ret == 0) { /* perform insecure sharing */ dest_user = NULL; key = NULL; } } } ARRAY_TYPE(dcrypt_private_key) keys; t_array_init(&keys, 8); struct mailbox_transaction_context *t = mailbox_transaction_begin(src_box, 0); /* get private keys from box */ if (mail_crypt_box_get_private_keys(t, &keys, error_r) < 0 || mail_crypt_box_share_private_keys(t, key, dest_user == NULL ? NULL : dest_user->username, &keys, error_r) < 0) ret = -1; if (ret >= 0) { array_foreach_modifiable(&keys, keyp) { dcrypt_key_unref_private(keyp); } } if (mailbox_transaction_commit(&t) < 0) { *error_r = mailbox_get_last_internal_error(src_box, NULL); ret = -1; } return ret; } static int mail_crypt_acl_object_update(struct acl_object *aclobj, const struct acl_rights_update *update) { const char *error; struct mail_crypt_acl_mailbox_list *mlist = MAIL_CRYPT_ACL_LIST_CONTEXT(aclobj->backend->list); const char *username; struct mail_user *dest_user; struct mail_storage_service_user *dest_service_user; struct ioloop_context *cur_ioloop_ctx; bool have_rights; int ret = 0; if (mlist->acl_vprev.object_update(aclobj, update) < 0) return -1; bool disallow_insecure = mail_crypt_acl_secure_sharing_enabled(aclobj->backend->list->ns->user); const char *box_name = mailbox_list_get_vname(aclobj->backend->list, aclobj->name); struct mailbox *box = mailbox_alloc(aclobj->backend->list, box_name, 0); if (mailbox_open(box) < 0) { i_error("mail-crypt-acl-plugin: " "mailbox_open(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); return -1; } switch (update->rights.id_type) { case ACL_ID_USER: /* setting rights for specific user: we can encrypt the mailbox key for the user. */ username = update->rights.identifier; ret = mail_crypt_acl_has_user_read_right(aclobj, username, NULL); if (ret < 0) { i_error("mail-crypt-acl-plugin: " "mail_crypt_acl_has_user_read_right(%s) failed", username); break; } have_rights = ret > 0; ret = mail_crypt_acl_user_create(aclobj->backend->list->ns->user, username, &dest_user, &dest_service_user, &error); /* to make sure we get correct logging context */ if (ret > 0) mail_storage_service_io_deactivate_user(dest_service_user); mail_storage_service_io_activate_user( aclobj->backend->list->ns->user->_service_user ); if (ret <= 0) { i_error("mail-crypt-acl-plugin: " "Cannot initialize destination user %s: %s", username, error); break; } else { i_assert(dest_user != NULL); if ((ret = mail_crypt_acl_update_private_key(box, dest_user, have_rights, disallow_insecure, &error)) < 0) { i_error("mail-crypt-acl-plugin: " "acl_update_private_key(%s, %s) failed: %s", mailbox_get_vname(box), username, error); } } /* logging context swap again */ mail_storage_service_io_deactivate_user( aclobj->backend->list->ns->user->_service_user ); mail_storage_service_io_activate_user(dest_service_user); mail_user_unref(&dest_user); mail_storage_service_user_unref(&dest_service_user); if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) io_loop_context_deactivate(cur_ioloop_ctx); mail_storage_service_io_activate_user( aclobj->backend->list->ns->user->_service_user ); break; case ACL_ID_OWNER: /* we should be the one doing this? ignore */ break; case ACL_ID_ANYONE: case ACL_ID_AUTHENTICATED: case ACL_ID_GROUP: case ACL_ID_GROUP_OVERRIDE: if (disallow_insecure) { i_error("mail-crypt-acl-plugin: " "Secure key sharing is enabled -" "Remove or set plugin { %s = no }", MAIL_CRYPT_ACL_SECURE_SHARE_SETTING); ret = -1; break; } /* the mailbox key needs to be stored unencrypted. for groups we could in theory use per-group encrypted keys, which the users belonging to the group would able to decrypt with their private key, but that becomes quite complicated. */ if ((ret = mail_crypt_acl_has_nonuser_read_right(aclobj, NULL)) < 0) { i_error("mail-crypt-acl-plugin: " "mail_crypt_acl_has_nonuser_read_right failed"); } else if ((ret = mail_crypt_acl_update_private_key(box, NULL, TRUE, disallow_insecure, &error)) < 0) { i_error("mail-crypt-acl-plugin: " "acl_update_private_key(%s, %s) failed: %s", mailbox_get_vname(box), "", error); } break; case ACL_ID_TYPE_COUNT: i_unreached(); } mailbox_free(&box); return ret; } static void mail_crypt_acl_mail_namespace_storage_added(struct mail_namespace *ns) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); struct mail_crypt_acl_mailbox_list *mlist = MAIL_CRYPT_ACL_LIST_CONTEXT(ns->list); struct acl_backend *backend; if (alist == NULL) return; /* FIXME: this method works only if there's a single plugin doing it. if there are ever multiple plugins hooking into ACL commands the ACL core code would need some changing to make it work correctly. */ backend = alist->rights.backend; mlist->acl_vprev = backend->v; backend->v.object_update = mail_crypt_acl_object_update; } static void mail_crypt_acl_mailbox_list_deinit(struct mailbox_list *list) { struct mail_crypt_acl_mailbox_list *mlist = MAIL_CRYPT_ACL_LIST_CONTEXT(list); mlist->module_ctx.super.deinit(list); } static void mail_crypt_acl_mailbox_list_created(struct mailbox_list *list) { struct mailbox_list_vfuncs *v = list->vlast; struct mail_crypt_acl_mailbox_list *mlist; mlist = p_new(list->pool, struct mail_crypt_acl_mailbox_list, 1); mlist->module_ctx.super = *v; list->vlast = &mlist->module_ctx.super; v->deinit = mail_crypt_acl_mailbox_list_deinit; MODULE_CONTEXT_SET(list, mail_crypt_acl_mailbox_list_module, mlist); } static struct mail_storage_hooks mail_crypt_acl_mail_storage_hooks = { .mailbox_list_created = mail_crypt_acl_mailbox_list_created, .mail_namespace_storage_added = mail_crypt_acl_mail_namespace_storage_added }; void mail_crypt_acl_plugin_init(struct module *module) { mail_storage_hooks_add(module, &mail_crypt_acl_mail_storage_hooks); } void mail_crypt_acl_plugin_deinit(void) { mail_storage_hooks_remove(&mail_crypt_acl_mail_storage_hooks); } const char *mail_crypt_acl_plugin_dependencies[] = { "acl", NULL }; dovecot-2.2.33.2/src/plugins/mail-crypt/mail-crypt-key.c0000644000175000017500000010325713165463624017713 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "dict.h" #include "array.h" #include "var-expand.h" #include "mail-storage.h" #include "mailbox-attribute.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mail-crypt-plugin.h" #include "mail-user.h" #include "hex-binary.h" #include "safe-memset.h" #include "base64.h" #include "sha2.h" struct mail_crypt_key_cache_entry { struct mail_crypt_key_cache_entry *next; char *pubid; /* this is lazily initialized */ struct dcrypt_keypair pair; }; static int mail_crypt_get_key_cache(struct mail_crypt_key_cache_entry *cache, const char *pubid, struct dcrypt_private_key **privkey_r, struct dcrypt_public_key **pubkey_r) { for(struct mail_crypt_key_cache_entry *ent = cache; ent != NULL; ent = ent->next) { if (strcmp(pubid, ent->pubid) == 0) { if (privkey_r != NULL && ent->pair.priv != NULL) { dcrypt_key_ref_private(ent->pair.priv); *privkey_r = ent->pair.priv; return 1; } else if (pubkey_r != NULL && ent->pair.pub != NULL) { dcrypt_key_ref_public(ent->pair.pub); *pubkey_r = ent->pair.pub; return 1; } else if ((privkey_r == NULL && pubkey_r == NULL) || (ent->pair.priv == NULL && ent->pair.pub == NULL)) { i_unreached(); } } } return 0; } static void mail_crypt_put_key_cache(struct mail_crypt_key_cache_entry **cache, const char *pubid, struct dcrypt_private_key *privkey, struct dcrypt_public_key *pubkey) { for(struct mail_crypt_key_cache_entry *ent = *cache; ent != NULL; ent = ent->next) { if (strcmp(pubid, ent->pubid) == 0) { if (privkey != NULL) { if (ent->pair.priv == NULL) { ent->pair.priv = privkey; dcrypt_key_ref_private(ent->pair.priv); } } else if (pubkey != NULL) { if (ent->pair.pub == NULL) { ent->pair.pub = pubkey; dcrypt_key_ref_public(ent->pair.pub); } } else i_unreached(); return; } } /* not found */ struct mail_crypt_key_cache_entry *ent = i_new(struct mail_crypt_key_cache_entry, 1); ent->pubid = i_strdup(pubid); ent->pair.priv = privkey; ent->pair.pub = pubkey; if (ent->pair.priv != NULL) dcrypt_key_ref_private(ent->pair.priv); if (ent->pair.pub != NULL) dcrypt_key_ref_public(ent->pair.pub); if (*cache == NULL) { *cache = ent; } else { ent->next = *cache; *cache = ent; } } void mail_crypt_key_cache_destroy(struct mail_crypt_key_cache_entry **cache) { struct mail_crypt_key_cache_entry *next, *cur = *cache; *cache = NULL; while(cur != NULL) { next = cur->next; i_free(cur->pubid); if (cur->pair.priv != NULL) dcrypt_key_unref_private(&cur->pair.priv); if (cur->pair.pub != NULL) dcrypt_key_unref_public(&cur->pair.pub); i_free(cur); cur = next; } } int mail_crypt_private_key_id_match(struct dcrypt_private_key *key, const char *pubid, const char **error_r) { i_assert(key != NULL); buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, error_r)) return -1; const char *hash = binary_to_hex(key_id->data, key_id->used); if (strcmp(pubid, hash) == 0) return 1; buffer_set_used_size(key_id, 0); if (!dcrypt_key_id_private_old(key, key_id, error_r)) { return -1; } hash = binary_to_hex(key_id->data, key_id->used); if (strcmp(pubid, hash) != 0) { *error_r = t_strdup_printf("Key %s does not match given ID %s", hash, pubid); return 0; } return 1; } int mail_crypt_public_key_id_match(struct dcrypt_public_key *key, const char *pubid, const char **error_r) { i_assert(key != NULL); buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); if (!dcrypt_key_id_public(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, error_r)) return -1; const char *hash = binary_to_hex(key_id->data, key_id->used); if (strcmp(pubid, hash) == 0) return 1; buffer_set_used_size(key_id, 0); if (!dcrypt_key_id_public_old(key, key_id, error_r)) { return -1; } hash = binary_to_hex(key_id->data, key_id->used); if (strcmp(pubid, hash) != 0) { *error_r = t_strdup_printf("Key %s does not match given ID %s", hash, pubid); return 0; } return 1; } static int mail_crypt_env_get_private_key(struct mail_user *user, const char *pubid, struct dcrypt_private_key **key_r, const char **error_r) { struct mail_crypt_global_keys global_keys; int ret = 0; if (mail_crypt_global_keys_load(user, "mail_crypt", &global_keys, TRUE, error_r) < 0) { mail_crypt_global_keys_free(&global_keys); return -1; } /* see if we got a key */ struct dcrypt_private_key *key = mail_crypt_global_key_find(&global_keys, pubid); if (key != NULL) { dcrypt_key_ref_private(key); *key_r = key; ret = 1; } mail_crypt_global_keys_free(&global_keys); return ret; } static const char *mail_crypt_get_key_path(bool user_key, bool public, const char *pubid) { const char *ret = t_strdup_printf("%s%s%s", user_key ? USER_CRYPT_PREFIX : BOX_CRYPT_PREFIX, public ? PUBKEYS_PREFIX : PRIVKEYS_PREFIX, pubid); return ret; } static int mail_crypt_decrypt_private_key(struct mailbox *box, const char *pubid, const char *data, struct dcrypt_private_key **key_r, const char **error_r) { enum dcrypt_key_kind key_kind; enum dcrypt_key_encryption_type enc_type; const char *enc_hash = NULL, *key_hash = NULL, *pw = NULL; struct dcrypt_private_key *key = NULL, *dec_key = NULL; struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); int ret = 0; i_assert(pubid != NULL); i_assert(data != NULL); /* see what the key needs for decrypting */ if (!dcrypt_key_string_get_info(data, NULL, NULL, &key_kind, &enc_type, &enc_hash, &key_hash, error_r)) { return -1; } if (key_kind != DCRYPT_KEY_KIND_PRIVATE) { *error_r = t_strdup_printf("Cannot use key %s: " "Expected private key, got public key", pubid); return -1; } if (key_hash != NULL && strcmp(key_hash, pubid) != 0) { *error_r = t_strdup_printf("Cannot use key %s: " "Incorrect key hash %s stored", pubid, key_hash); return -1; } /* see if it needs decrypting */ if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { /* no key or password */ } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) { pw = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD); if (pw == NULL) { *error_r = t_strdup_printf("Cannot decrypt key %s: " "Password not available", pubid); return -1; } } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY) { if ((ret = mail_crypt_user_get_private_key(user, enc_hash, &dec_key, error_r)) <= 0) { /* last resort, look at environment */ if (ret == 0 && (ret = mail_crypt_env_get_private_key(user, enc_hash, &dec_key, error_r)) == 0) { *error_r = t_strdup_printf("Cannot decrypt key %s: " "Private key %s not available:", pubid, enc_hash); return -1; } else if (ret < 0) { *error_r = t_strdup_printf("Cannot decrypt key %s: %s", pubid, *error_r); return ret; } } } bool res = dcrypt_key_load_private(&key, data, pw, dec_key, error_r); if (dec_key != NULL) dcrypt_key_unref_private(&dec_key); if (!res) return -1; if (mail_crypt_private_key_id_match(key, pubid, error_r) <= 0) { if (key != NULL) dcrypt_key_unref_private(&key); return -1; } i_assert(key != NULL); *key_r = key; return 1; } int mail_crypt_get_private_key(struct mailbox_transaction_context *t, const char *pubid, bool user_key, bool shared, struct dcrypt_private_key **key_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); /* check cache */ if (mail_crypt_get_key_cache(muser->key_cache, pubid, key_r, NULL) > 0) { return 1; } struct mail_attribute_value value; struct dcrypt_private_key *key; int ret; const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid); if ((ret = mailbox_attribute_get(t, shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE, attr_name, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s%s) failed: %s", mailbox_get_vname(box), shared ? "/shared/" : "/priv/", attr_name, mailbox_get_last_internal_error(box, NULL)); } return ret; } if ((ret = mail_crypt_decrypt_private_key(box, pubid, value.value, &key, error_r)) <= 0) return ret; i_assert(key != NULL); mail_crypt_put_key_cache(&muser->key_cache, pubid, key, NULL); *key_r = key; return 1; } int mail_crypt_user_get_private_key(struct mail_user *user, const char *pubid, struct dcrypt_private_key **key_r, const char **error_r) { struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); struct mail_attribute_value value; int ret; /* try retrieve currently active user key */ if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", "INBOX", mailbox_get_last_internal_error(box, NULL)); return -1; } struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); if (pubid == NULL) { if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, USER_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", mailbox_get_vname(box), USER_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } } else { pubid = value.value; ret = 1; } } else ret = 1; /* try to open key */ if (ret > 0) ret = mail_crypt_get_private_key(t, pubid, TRUE, FALSE, key_r, error_r); (void)mailbox_transaction_commit(&t); mailbox_free(&box); return ret; } int mail_crypt_box_get_private_key(struct mailbox_transaction_context *t, struct dcrypt_private_key **key_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mail_attribute_value value; int ret; /* get active key */ if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", mailbox_get_vname(box), USER_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } return ret; } return mail_crypt_get_private_key(t, value.value, FALSE, FALSE, key_r, error_r); } static int mail_crypt_set_private_key(struct mailbox_transaction_context *t, bool user_key, bool shared, const char *pubid, struct dcrypt_public_key *enc_key, struct dcrypt_private_key *key, const char **error_r) { /* folder keys must be encrypted with some other key, unless they are shared keys */ i_assert(user_key || shared || enc_key != NULL); buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); const char *pw = NULL; const char *algo = NULL; struct mail_user *user = mail_storage_get_user( mailbox_get_storage( mailbox_transaction_get_mailbox(t))); const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid); struct mail_attribute_value value; int ret; if (enc_key != NULL) { algo = MAIL_CRYPT_KEY_CIPHER; } else if (user_key && (pw = mail_user_plugin_getenv(user,MAIL_CRYPT_USERENV_PASSWORD)) != NULL) { algo = MAIL_CRYPT_PW_CIPHER; } /* export key */ if (!dcrypt_key_store_private(key, DCRYPT_FORMAT_DOVECOT, algo, data, pw, enc_key, error_r)) { return -1; } /* store it */ value.value_stream = NULL; value.value = str_c(data); value.last_change = 0; if ((ret = mailbox_attribute_set(t, shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE, attr_name, &value)) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s/%s) failed: %s", mailbox_get_vname(mailbox_transaction_get_mailbox(t)), shared ? "/shared" : "/priv", attr_name, mailbox_get_last_internal_error( mailbox_transaction_get_mailbox(t), NULL)); } safe_memset(buffer_get_modifiable_data(data, NULL), 0, data->used); return ret; } int mail_crypt_user_set_private_key(struct mail_user *user, const char *pubid, struct dcrypt_private_key *key, const char **error_r) { struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); struct dcrypt_private_key *env_key = NULL; struct dcrypt_public_key *enc_key = NULL; struct mailbox_transaction_context *t; int ret; if ((ret = mail_crypt_env_get_private_key(user, NULL, &env_key, error_r)) < 0) { return -1; } else if (ret > 0) { dcrypt_key_convert_private_to_public(env_key, &enc_key); dcrypt_key_unref_private(&env_key); } if (mail_user_plugin_getenv(user, MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY) != NULL && mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD) == NULL && mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_KEY) == NULL) { *error_r = MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY " set, cannot " "generate user keypair without password or key"; return -1; } if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", "INBOX", mailbox_get_last_internal_error(box, NULL)); return -1; } t = mailbox_transaction_begin(box, 0); if ((ret = mail_crypt_set_private_key(t, TRUE, FALSE, pubid, enc_key, key, error_r)) < 0) { mailbox_transaction_rollback(&t); } else if ((ret = mailbox_transaction_commit(&t)) < 0) { *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); } mailbox_free(&box); return ret; } int mail_crypt_box_set_private_key(struct mailbox *box, const char *pubid, struct dcrypt_private_key *key, struct dcrypt_public_key *user_key, const char **error_r) { int ret; struct mailbox_transaction_context *t; t = mailbox_transaction_begin(box, 0); if ((ret = mail_crypt_set_private_key(t, FALSE, FALSE, pubid, user_key, key, error_r)) < 0) { mailbox_transaction_rollback(&t); } else if ((ret = mailbox_transaction_commit(&t)) < 0) { *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); } return ret; } static int mail_crypt_get_public_key(struct mailbox_transaction_context *t, const char *pubid, bool user_key, struct dcrypt_public_key **key_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); /* check cache */ if (mail_crypt_get_key_cache(muser->key_cache, pubid, NULL, key_r) > 0) { return 1; } enum dcrypt_key_kind key_kind; const char *key_hash = NULL; struct dcrypt_public_key *key; struct mail_attribute_value value; int ret; const char *attr_name = mail_crypt_get_key_path(user_key, TRUE, pubid); if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, attr_name, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", mailbox_get_vname(box), attr_name, mailbox_get_last_internal_error(box, NULL)); } return ret; } if (!dcrypt_key_string_get_info(value.value, NULL, NULL, &key_kind, NULL, NULL, &key_hash, error_r)) { return -1; } if (key_kind != DCRYPT_KEY_KIND_PUBLIC) { *error_r = t_strdup_printf("Cannot use key %s: " "Expected public key, got private key", pubid); return -1; } if (key_hash != NULL && strcmp(key_hash, pubid) != 0) { *error_r = t_strdup_printf("Cannot use key %s: " "Incorrect key hash %s stored", pubid, key_hash); return -1; } /* load the key */ if (!dcrypt_key_load_public(&key, value.value, error_r)) { return -1; } if (pubid != NULL && mail_crypt_public_key_id_match(key, pubid, error_r) <= 0) { dcrypt_key_unref_public(&key); return -1; } mail_crypt_put_key_cache(&muser->key_cache, pubid, NULL, key); *key_r = key; return 1; } int mail_crypt_user_get_public_key(struct mail_user *user, struct dcrypt_public_key **key_r, const char **error_r) { struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); struct mail_attribute_value value; int ret; /* try retrieve currently active user key */ if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", "INBOX", mailbox_get_last_internal_error(box, NULL)); return -1; } struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, USER_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", mailbox_get_vname(box), USER_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } } else { ret = mail_crypt_get_public_key(t, value.value, TRUE, key_r, error_r); } (void)mailbox_transaction_commit(&t); mailbox_free(&box); return ret; } int mail_crypt_box_get_public_key(struct mailbox_transaction_context *t, struct dcrypt_public_key **key_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mail_attribute_value value; int ret; if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", mailbox_get_vname(box), BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } return ret; } return mail_crypt_get_public_key(t, value.value, FALSE, key_r, error_r); } static int mail_crypt_set_public_key(struct mailbox_transaction_context *t, bool user_key, const char *pubid, struct dcrypt_public_key *key, const char **error_r) { buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); const char *attr_name = mail_crypt_get_key_path(user_key, TRUE, pubid); struct mail_attribute_value value; /* export key */ if (!dcrypt_key_store_public(key, DCRYPT_FORMAT_DOVECOT, data, error_r)) { return -1; } /* store it */ value.value_stream = NULL; value.value = str_c(data); value.last_change = 0; if (mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, attr_name, &value) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s/%s) failed: %s", mailbox_get_vname(mailbox_transaction_get_mailbox(t)), "/shared", attr_name, mailbox_get_last_internal_error( mailbox_transaction_get_mailbox(t), NULL)); return -1; } return 0; } int mail_crypt_user_set_public_key(struct mail_user *user, const char *pubid, struct dcrypt_public_key *key, const char **error_r) { struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); struct mailbox_transaction_context *t; struct mail_attribute_value value; int ret; /* try retrieve currently active user key */ if (mailbox_open(box) < 0) { *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", "INBOX", mailbox_get_last_internal_error(box, NULL)); return -1; } t = mailbox_transaction_begin(box, 0); if ((ret = mail_crypt_set_public_key(t, TRUE, pubid, key, error_r)) == 0) { value.value_stream = NULL; value.value = pubid; value.last_change = 0; if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, USER_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", mailbox_get_vname(box), USER_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } } if (ret < 0) { mailbox_transaction_rollback(&t); } else if (mailbox_transaction_commit(&t) < 0) { *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; } mailbox_free(&box); return ret; } int mail_crypt_box_set_public_key(struct mailbox *box, const char *pubid, struct dcrypt_public_key *key, const char **error_r) { int ret; struct mailbox_transaction_context *t; struct mail_attribute_value value; t = mailbox_transaction_begin(box, 0); if ((ret = mail_crypt_set_public_key(t, FALSE, pubid, key, error_r)) == 0) { value.value_stream = NULL; value.value = pubid; value.last_change = 0; if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", mailbox_get_vname(box), BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } } if (ret < 0) { mailbox_transaction_rollback(&t); } else if (mailbox_transaction_commit(&t) < 0) { *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); ret = -1; } return ret; } static int mail_crypt_user_set_keys(struct mail_user *user, const char *pubid, struct dcrypt_private_key *privkey, struct dcrypt_public_key *pubkey, const char **error_r) { if (mail_crypt_user_set_private_key(user, pubid, privkey, error_r) < 0) return -1; if (mail_crypt_user_set_public_key(user, pubid, pubkey, error_r) < 0) return -1; return 0; } static int mail_crypt_box_set_keys(struct mailbox *box, const char *pubid, struct dcrypt_private_key *privkey, struct dcrypt_public_key *user_key, struct dcrypt_public_key *pubkey, const char **error_r) { if (mail_crypt_box_set_private_key(box, pubid, privkey, user_key, error_r) < 0) return -1; if (mail_crypt_box_set_public_key(box, pubid, pubkey, error_r) < 0) return -1; return 0; } int mail_crypt_box_get_shared_key(struct mailbox_transaction_context *t, const char *pubid, struct dcrypt_private_key **key_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); struct dcrypt_private_key *key = NULL; struct mail_attribute_value value; int ret; /* check cache */ if (mail_crypt_get_key_cache(muser->key_cache, pubid, key_r, NULL) > 0) { return 1; } const char *hexname = binary_to_hex((const unsigned char*)user->username, strlen(user->username)); const char *attr_name = t_strdup_printf(BOX_CRYPT_PREFIX PRIVKEYS_PREFIX"%s/%s", hexname, pubid); if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, attr_name, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", mailbox_get_vname(box), attr_name, mailbox_get_last_internal_error(box, NULL)); return ret; } return mail_crypt_get_private_key(t, pubid, FALSE, TRUE, key_r, error_r); } else { if ((ret = mail_crypt_decrypt_private_key(box, pubid, value.value, &key, error_r)) <= 0) return ret; } mail_crypt_put_key_cache(&muser->key_cache, pubid, key, NULL); *key_r = key; return 1; } int mail_crypt_box_set_shared_key(struct mailbox_transaction_context *t, const char *pubid, struct dcrypt_private_key *privkey, const char *target_uid, struct dcrypt_public_key *user_key, const char **error_r) { struct mail_attribute_value value; buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); int ret; const char *attr_name; const char *algo = NULL; i_assert(target_uid == NULL || user_key != NULL); if (target_uid != NULL) { /* hash target UID */ algo = MAIL_CRYPT_KEY_CIPHER; const char *hexname = binary_to_hex((const unsigned char*)target_uid, strlen(target_uid)); attr_name = t_strdup_printf(BOX_CRYPT_PREFIX PRIVKEYS_PREFIX"%s/%s", hexname, pubid); } else { attr_name = t_strdup_printf(BOX_CRYPT_PREFIX PRIVKEYS_PREFIX"%s", pubid); } if (!dcrypt_key_store_private(privkey, DCRYPT_FORMAT_DOVECOT, algo, data, NULL, user_key, error_r)) { return -1; } value.value_stream = NULL; value.value = str_c(data); value.last_change = 0; if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, attr_name, &value)) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", mailbox_get_vname( mailbox_transaction_get_mailbox(t)), attr_name, mailbox_get_last_internal_error( mailbox_transaction_get_mailbox(t), NULL)); } safe_memset(buffer_get_modifiable_data(data, NULL), 0, data->used); return ret; } int mail_crypt_box_unset_shared_key(struct mailbox_transaction_context *t, const char *pubid, const char *target_uid, const char **error_r) { int ret; const char *hexname = binary_to_hex((const unsigned char*)target_uid, strlen(target_uid)); const char *attr_name = t_strdup_printf(BOX_CRYPT_PREFIX PRIVKEYS_PREFIX"%s/%s", hexname, pubid); if ((ret = mailbox_attribute_unset(t, MAIL_ATTRIBUTE_TYPE_SHARED, attr_name)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_unset(%s, " " /shared/%s): failed: %s", mailbox_get_vname( mailbox_transaction_get_mailbox(t)), attr_name, mailbox_get_last_internal_error( mailbox_transaction_get_mailbox(t), NULL)); } } return ret; } static int mail_crypt_generate_keypair(const char *curve, struct dcrypt_keypair *pair_r, const char **pubid_r, const char **error_r) { if (curve == NULL) { *error_r = MAIL_CRYPT_USERENV_CURVE " not set, cannot generate EC key"; return -1; } if (!dcrypt_keypair_generate(pair_r, DCRYPT_KEY_EC, 0, curve, error_r)) { return -1; } buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); if (!dcrypt_key_id_public(pair_r->pub, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, error_r)) { dcrypt_keypair_unref(pair_r); return -1; } *pubid_r = binary_to_hex(key_id->data, key_id->used); return 0; } int mail_crypt_user_generate_keypair(struct mail_user *user, struct dcrypt_keypair *pair, const char **pubid_r, const char **error_r) { struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); const char *curve = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_CURVE); if (mail_crypt_generate_keypair(curve, pair, pubid_r, error_r) < 0) { return -1; } if (mail_crypt_user_set_keys(user, *pubid_r, pair->priv, pair->pub, error_r) < 0) { dcrypt_keypair_unref(pair); return -1; } mail_crypt_put_key_cache(&muser->key_cache, *pubid_r, pair->priv, pair->pub); return 0; } int mail_crypt_box_generate_keypair(struct mailbox *box, struct dcrypt_keypair *pair, struct dcrypt_public_key *user_key, const char **pubid_r, const char **error_r) { int ret; struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); const char *curve = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_CURVE); if (user_key == NULL) { if ((ret = mail_crypt_user_get_public_key(user, &user_key, error_r)) <= 0) { if (ret < 0) return ret; /* generate keypair */ struct dcrypt_keypair user_pair; const char *user_pubid; if (mail_crypt_user_generate_keypair(user, &user_pair, &user_pubid, error_r) < 0) { return -1; } mail_crypt_put_key_cache(&muser->key_cache, user_pubid, user_pair.priv, user_pair.pub); user_key = user_pair.pub; dcrypt_key_unref_private(&user_pair.priv); } } else { dcrypt_key_ref_public(user_key); } if ((ret = mail_crypt_generate_keypair(curve, pair, pubid_r, error_r)) < 0) { /* failed */ } else if ((ret = mail_crypt_box_set_keys(box, *pubid_r, pair->priv, user_key, pair->pub, error_r)) < 0) { dcrypt_keypair_unref(pair); } else { mail_crypt_put_key_cache(&muser->key_cache, *pubid_r, pair->priv, pair->pub); } dcrypt_key_unref_public(&user_key); return ret; } int mail_crypt_box_get_pvt_digests(struct mailbox *box, pool_t pool, enum mail_attribute_type type, ARRAY_TYPE(const_string) *digests, const char **error_r) { struct mailbox_attribute_iter *iter; const char *key; int ret; iter = mailbox_attribute_iter_init(box, type, BOX_CRYPT_PREFIX PRIVKEYS_PREFIX); while ((key = mailbox_attribute_iter_next(iter)) != NULL) { key = p_strdup(pool, key); array_append(digests, &key, 1); } ret = mailbox_attribute_iter_deinit(&iter); if (ret < 0) *error_r = mailbox_get_last_internal_error(box, NULL); return ret; } int mail_crypt_box_get_private_keys(struct mailbox_transaction_context *t, ARRAY_TYPE(dcrypt_private_key) *keys_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); struct mailbox_attribute_iter *iter; iter = mailbox_attribute_iter_init(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, BOX_CRYPT_PREFIX PRIVKEYS_PREFIX); const char *id; int ret; while ((id = mailbox_attribute_iter_next(iter)) != NULL) { struct dcrypt_private_key *key = NULL; if ((ret = mail_crypt_get_private_key(t, id, FALSE, FALSE, &key, error_r)) < 0) { (void)mailbox_attribute_iter_deinit(&iter); return -1; } else if (ret > 0) array_append(keys_r, &key, 1); } ret = mailbox_attribute_iter_deinit(&iter); if (ret < 0) *error_r = mailbox_get_last_internal_error(box, NULL); return ret; } int mail_crypt_box_share_private_keys(struct mailbox_transaction_context *t, struct dcrypt_public_key *dest_pub_key, const char *dest_user, const ARRAY_TYPE(dcrypt_private_key) *priv_keys, const char **error_r) { i_assert(dest_user == NULL || dest_pub_key != NULL); struct dcrypt_private_key *const *priv_keyp, *priv_key; buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); int ret = 0; array_foreach(priv_keys, priv_keyp) { priv_key = *priv_keyp; ret = -1; if (!dcrypt_key_id_private(priv_key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, error_r) || (ret = mail_crypt_box_set_shared_key(t, binary_to_hex(key_id->data, key_id->used), priv_key, dest_user, dest_pub_key, error_r)) < 0) break; } return ret; } int mail_crypt_user_get_or_gen_public_key(struct mail_user *user, struct dcrypt_public_key **pub_r, const char **error_r) { i_assert(user != NULL); i_assert(pub_r != NULL); i_assert(error_r != NULL); int ret; if ((ret = mail_crypt_user_get_public_key(user, pub_r, error_r)) == 0) { struct dcrypt_keypair pair; const char *pubid = NULL; if (mail_crypt_user_generate_keypair(user, &pair, &pubid, error_r) < 0) { return -1; } *pub_r = pair.pub; dcrypt_key_unref_private(&pair.priv); } else return ret; return 0; } int mail_crypt_box_get_or_gen_public_key(struct mailbox_transaction_context *t, struct dcrypt_public_key **pub_r, const char **error_r) { struct mailbox *box = mailbox_transaction_get_mailbox(t); i_assert(pub_r != NULL); i_assert(error_r != NULL); struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); int ret; if ((ret = mail_crypt_box_get_public_key(t, pub_r, error_r)) == 0) { struct dcrypt_public_key *user_key; if (mail_crypt_user_get_or_gen_public_key(user, &user_key, error_r) < 0) { return -1; } struct dcrypt_keypair pair; const char *pubid = NULL; if (mail_crypt_box_generate_keypair(box, &pair, user_key, &pubid, error_r) < 0) { return -1; } *pub_r = pair.pub; dcrypt_key_unref_public(&user_key); dcrypt_key_unref_private(&pair.priv); } else return ret; return 0; } bool mail_crypt_acl_secure_sharing_enabled(struct mail_user *user) { const char *env = mail_user_plugin_getenv(user, MAIL_CRYPT_ACL_SECURE_SHARE_SETTING); /* disabled by default */ bool ret = FALSE; if (env != NULL) { /* enable unless specifically requested not to */ ret = TRUE; switch (env[0]) { case 'n': case 'N': case '0': case 'f': case 'F': ret = FALSE; } } return ret; } static const struct mailbox_attribute_internal mailbox_internal_attributes[] = { { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, .key = BOX_CRYPT_PREFIX, .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN }, { .type = MAIL_ATTRIBUTE_TYPE_SHARED, .key = BOX_CRYPT_PREFIX, .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN }, { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, .key = USER_CRYPT_PREFIX, .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN }, { .type = MAIL_ATTRIBUTE_TYPE_SHARED, .key = USER_CRYPT_PREFIX, .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN } }; void mail_crypt_key_register_mailbox_internal_attributes(void) { mailbox_attribute_register_internals(mailbox_internal_attributes, N_ELEMENTS(mailbox_internal_attributes)); } dovecot-2.2.33.2/src/plugins/mail-crypt/test-mail-key.c0000644000175000017500000003370413165463624017530 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "ioloop.h" #include "lib-signals.h" #include "master-service.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mailbox-list.h" #include "settings-parser.h" #include "mail-user.h" #include "safe-mkstemp.h" #include "safe-mkdir.h" #include "str.h" #include "unlink-directory.h" #include "randgen.h" #include "dcrypt.h" #include "hex-binary.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mail-crypt-plugin.h" static const char *mcp_old_user_key = "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; static const char *mcp_old_user_key_id = "d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; static const char *mcp_old_box_key = "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c940e978d00686cbb52bd5014bc318563375876255\t0300E46DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A34235A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; static const char *mcp_old_box_key_id = "7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; static struct mail_storage_service_ctx *mail_storage_service = NULL; static struct mail_user *test_mail_user = NULL; static struct mail_storage_service_user *test_service_user = NULL; static struct ioloop *test_ioloop = NULL; static const char *mail_home; static const char *test_user_key_id; static const char *test_box_key_id; static pool_t test_pool; static struct mail_crypt_user mail_crypt_user; struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user ATTR_UNUSED) { return &mail_crypt_user; } static int test_mail_attribute_get(struct mailbox *box, bool user_key, bool shared, const char *pubid, const char **value_r, const char **error_r) { const char *attr_name; enum mail_attribute_type attr_type; if (strcmp(pubid, ACTIVE_KEY_NAME) == 0) { attr_name = user_key ? USER_CRYPT_PREFIX ACTIVE_KEY_NAME : BOX_CRYPT_PREFIX ACTIVE_KEY_NAME; attr_type = MAIL_ATTRIBUTE_TYPE_SHARED; } else { attr_name = t_strdup_printf("%s%s%s", user_key ? USER_CRYPT_PREFIX : BOX_CRYPT_PREFIX, shared ? PUBKEYS_PREFIX : PRIVKEYS_PREFIX, pubid); attr_type = shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE; } struct mail_attribute_value value; int ret; struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); if ((ret = mailbox_attribute_get(t, attr_type, attr_name, &value)) <= 0) { if (ret < 0) { *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", mailbox_get_vname(box), attr_name, mailbox_get_last_internal_error(box, NULL)); } } else { *value_r = t_strdup(value.value); } (void)mailbox_transaction_commit(&t); return ret; } static int test_mail_attribute_set(struct mailbox_transaction_context *t, bool user_key, bool shared, const char *pubid, const char *value, const char **error_r) { const char *attr_name; enum mail_attribute_type attr_type; if (strcmp(pubid, ACTIVE_KEY_NAME) == 0) { attr_name = user_key ? USER_CRYPT_PREFIX ACTIVE_KEY_NAME : BOX_CRYPT_PREFIX ACTIVE_KEY_NAME; attr_type = MAIL_ATTRIBUTE_TYPE_SHARED; } else { attr_name = t_strdup_printf("%s%s%s", user_key ? USER_CRYPT_PREFIX : BOX_CRYPT_PREFIX, shared ? PUBKEYS_PREFIX : PRIVKEYS_PREFIX, pubid); attr_type = shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE; } struct mail_attribute_value attr_value; int ret; i_zero(&attr_value); attr_value.value = value; if ((ret = mailbox_attribute_set(t, attr_type, attr_name, &attr_value)) < 0) { *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s) failed: %s", mailbox_get_vname(mailbox_transaction_get_mailbox(t)), attr_name, mailbox_get_last_internal_error(mailbox_transaction_get_mailbox(t), NULL)); } return ret; } static int init_test_mail_user(void) { struct setting_parser_context *set_parser; const char *error; char path_buf[4096]; if (getcwd(path_buf, sizeof(path_buf)) == NULL) i_fatal("getcwd() failed: %m"); mail_home = p_strdup_printf(test_pool, "%s/mcp_user/", path_buf); struct mail_storage_service_input input = { .userdb_fields = (const char*const[]){ t_strdup_printf("mail=maildir:~/"), t_strdup_printf("home=%s", mail_home), t_strdup_printf("mail_crypt_curve=prime256v1"), NULL }, .username = "mcp_test", .no_userdb_lookup = TRUE, .debug = TRUE, }; mail_storage_service = mail_storage_service_init(master_service, NULL, MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS); if (mail_storage_service_lookup(mail_storage_service, &input, &test_service_user, &error) < 0) { i_error("Cannot lookup test user: %s", error); return -1; } set_parser = mail_storage_service_user_get_settings_parser(test_service_user); if (settings_parse_line(set_parser, t_strdup_printf("mail_attribute_dict=file:%s/dovecot-attributes", mail_home)) < 0) { i_error("Cannot set mail_attribute_dict: %s", settings_parser_get_error(set_parser)); return -1; } if (mail_storage_service_next(mail_storage_service, test_service_user, &test_mail_user) < 0) { i_error("Cannot lookup test user"); return -1; } return 0; } static void deinit_test_mail_user() { mail_user_unref(&test_mail_user); mail_storage_service_user_unref(&test_service_user); mail_storage_service_deinit(&mail_storage_service); if (unlink_directory(mail_home, UNLINK_DIRECTORY_FLAG_RMDIR) < 0) i_error("unlink_directory(%s) failed", mail_home); } static void test_generate_user_key(void) { struct dcrypt_keypair pair; const char *pubid; const char *error = NULL; test_begin("generate user key"); /* try to generate a keypair for user */ if (mail_crypt_user_generate_keypair(test_mail_user, &pair, &pubid, &error) < 0) { i_error("generate_keypair failed: %s", error); test_exit(1); } test_assert(pubid != NULL); test_user_key_id = p_strdup(test_pool, pubid); dcrypt_keypair_unref(&pair); error = NULL; /* keys ought to be in cache or somewhere...*/ if (mail_crypt_user_get_private_key(test_mail_user, NULL, &pair.priv, &error) <= 0) { i_error("Cannot get user private key: %s", error); } test_assert(pair.priv != NULL); if (pair.priv != NULL) dcrypt_key_unref_private(&pair.priv); test_end(); } static void test_generate_inbox_key(void) { struct dcrypt_public_key *user_key; struct dcrypt_keypair pair; const char *error = NULL, *pubid = NULL; test_begin("generate inbox key"); if (mail_crypt_user_get_public_key(test_mail_user, &user_key, &error) <= 0) { i_error("Cannot get user private key: %s", error); } struct mail_namespace *ns = mail_namespace_find_inbox(test_mail_user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); if (mailbox_open(box) < 0) i_fatal("mailbox_open(INBOX) failed: %s", mailbox_get_last_internal_error(box, NULL)); if (mail_crypt_box_generate_keypair(box, &pair, user_key, &pubid, &error) < 0) { i_error("generate_keypair failed: %s", error); test_exit(1); } i_assert(pubid != NULL); dcrypt_keypair_unref(&pair); dcrypt_key_unref_public(&user_key); mailbox_free(&box); test_box_key_id = p_strdup(test_pool, pubid); test_end(); } static void test_cache_reset(void) { struct dcrypt_keypair pair; const char *error = NULL; test_begin("cache reset"); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(test_mail_user); mail_crypt_key_cache_destroy(&muser->key_cache); test_assert(mail_crypt_user_get_private_key(test_mail_user, NULL, &pair.priv, &error) > 0); if (error != NULL) i_error("mail_crypt_user_get_private_key() failed: %s", error); error = NULL; test_assert(mail_crypt_user_get_public_key(test_mail_user, &pair.pub, &error) > 0); if (error != NULL) i_error("mail_crypt_user_get_public_key() failed: %s", error); dcrypt_keypair_unref(&pair); test_end(); } static void test_verify_keys(void) { const char *value, *error = NULL; const char *enc_id; enum dcrypt_key_encryption_type enc_type; test_begin("verify keys"); struct dcrypt_private_key *privkey = NULL, *user_key = NULL; struct dcrypt_public_key *pubkey = NULL; struct mail_namespace *ns = mail_namespace_find_inbox(test_mail_user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); if (mailbox_open(box) < 0) i_fatal("mailbox_open(INBOX) failed: %s", mailbox_get_last_internal_error(box, NULL)); /* verify links */ /* user's public key */ test_assert(test_mail_attribute_get(box, TRUE, TRUE, ACTIVE_KEY_NAME, &value, &error) > 0); test_assert(strcmp(value, test_user_key_id) == 0); test_assert(test_mail_attribute_get(box, TRUE, TRUE, value, &value, &error) > 0); /* load key */ test_assert(dcrypt_key_load_public(&pubkey, value, &error) == TRUE); /* see if it matches */ test_assert(mail_crypt_public_key_id_match(pubkey, test_user_key_id, &error) > 0); dcrypt_key_unref_public(&pubkey); /* user's private key */ test_assert(test_mail_attribute_get(box, TRUE, FALSE, ACTIVE_KEY_NAME, &value, &error) > 0); test_assert(strcmp(value, test_user_key_id) == 0); test_assert(test_mail_attribute_get(box, TRUE, FALSE, value, &value, &error) > 0); /* load key */ test_assert(dcrypt_key_load_private(&user_key, value, NULL, NULL, &error) == TRUE); /* see if it matches */ test_assert(mail_crypt_private_key_id_match(user_key, test_user_key_id, &error) > 0); /* inbox's public key */ test_assert(test_mail_attribute_get(box, FALSE, TRUE, ACTIVE_KEY_NAME, &value, &error) > 0); test_assert(strcmp(value, test_box_key_id) == 0); test_assert(test_mail_attribute_get(box, FALSE, TRUE, value, &value, &error) > 0); /* load key */ test_assert(dcrypt_key_load_public(&pubkey, value, &error) == TRUE); /* see if it matches */ test_assert(mail_crypt_public_key_id_match(pubkey, test_box_key_id, &error) > 0); dcrypt_key_unref_public(&pubkey); /* user's private key */ test_assert(test_mail_attribute_get(box, FALSE, FALSE, ACTIVE_KEY_NAME, &value, &error) > 0); test_assert(strcmp(value, test_box_key_id) == 0); test_assert(test_mail_attribute_get(box, FALSE, FALSE, value, &value, &error) > 0); test_assert(dcrypt_key_string_get_info(value, NULL, NULL, NULL, &enc_type, &enc_id, NULL, &error) == TRUE); test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); test_assert(strcmp(enc_id, test_user_key_id) == 0); /* load key */ test_assert(dcrypt_key_load_private(&privkey, value, NULL, user_key, &error) == TRUE); /* see if it matches */ test_assert(mail_crypt_private_key_id_match(privkey, test_box_key_id, &error) > 0); dcrypt_key_unref_private(&privkey); dcrypt_key_unref_private(&user_key); mailbox_free(&box); test_end(); } static void test_old_key(void) { test_begin("old keys"); const char *error = NULL; struct dcrypt_private_key *privkey = NULL; struct mail_namespace *ns = mail_namespace_find_inbox(test_mail_user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); if (mailbox_open(box) < 0) i_fatal("mailbox_open(INBOX) failed: %s", mailbox_get_last_internal_error(box, NULL)); struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); test_mail_attribute_set(t, TRUE, FALSE, mcp_old_user_key_id, mcp_old_user_key, &error); test_mail_attribute_set(t, FALSE, FALSE, mcp_old_box_key_id, mcp_old_box_key, &error); (void)mailbox_transaction_commit(&t); t = mailbox_transaction_begin(box, 0); error = NULL; /* try to load old key */ test_assert(mail_crypt_get_private_key(t, mcp_old_box_key_id, FALSE, FALSE, &privkey, &error) > 0); (void)mailbox_transaction_commit(&t); if (error != NULL) i_error("mail_crypt_get_private_key(%s) failed: %s", mcp_old_box_key_id, error); test_assert(privkey != NULL); if (privkey != NULL) { buffer_t *key_id = buffer_create_dynamic(pool_datastack_create(), 32); test_assert(dcrypt_key_id_private_old(privkey, key_id, &error)); test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), mcp_old_box_key_id) == 0); dcrypt_key_unref_private(&privkey); } mailbox_free(&box); test_end(); } static void test_setup(void) { struct dcrypt_settings set = { .module_dir = top_builddir "/src/lib-dcrypt/.libs" }; test_pool = pool_alloconly_create(MEMPOOL_GROWING "mcp test pool", 128); test_ioloop = io_loop_create(); if (!dcrypt_initialize(NULL, &set, NULL)) { i_info("No functional dcrypt backend found - skipping tests"); test_exit(0); } /* allocate a user */ if (init_test_mail_user() < 0) { test_exit(1); } mail_crypt_key_register_mailbox_internal_attributes(); } static void test_teardown(void) { deinit_test_mail_user(); struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(test_mail_user); mail_crypt_key_cache_destroy(&muser->key_cache); dcrypt_deinitialize(); io_loop_destroy(&test_ioloop); pool_unref(&test_pool); } int main(int argc, char **argv) { void (*tests[])(void) = { test_setup, test_generate_user_key, test_generate_inbox_key, test_cache_reset, test_verify_keys, test_old_key, test_teardown, NULL }; master_service = master_service_init("test-mail-key", MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS | MASTER_SERVICE_FLAG_NO_SSL_INIT, &argc, &argv, ""); random_init(); int ret = test_run(tests); master_service_deinit(&master_service); return ret; } dovecot-2.2.33.2/src/plugins/mail-crypt/doveadm-mail-crypt.c0000644000175000017500000007120613167162046020534 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "askpass.h" #include "doveadm-mail.h" #include "getopt.h" #include "array.h" #include "str.h" #include "buffer.h" #include "ioloop.h" #include "ioloop-private.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-storage-settings.h" #include "mailbox-attribute.h" #include "mail-crypt-common.h" #include "mail-crypt-key.h" #include "mailbox-list-iter.h" #include "doveadm-print.h" #include "hex-binary.h" #define DOVEADM_MCP_SUCCESS "\xE2\x9C\x93" /* emits a utf-8 CHECK MARK (U+2713) */ #define DOVEADM_MCP_FAIL "x" #define DOVEADM_MCP_USERKEY "" struct generated_key { const char *name; const char *id; const char *error; struct mailbox *box; bool success:1; bool active:1; }; ARRAY_DEFINE_TYPE(generated_keys, struct generated_key); struct mcp_cmd_context { struct doveadm_mail_cmd_context ctx; const char *old_password; const char *new_password; unsigned int matched_keys; bool userkey_only:1; bool recrypt_box_keys:1; bool force:1; bool ask_old_password:1; bool ask_new_password:1; bool clear_password:1; }; struct mcp_key_iter_ctx { pool_t pool; ARRAY_TYPE(generated_keys) keys; }; void doveadm_mail_crypt_plugin_init(struct module *mod ATTR_UNUSED); void doveadm_mail_crypt_plugin_deinit(void); static int mcp_user_create(struct mail_user *user, const char *dest_username, struct mail_user **dest_user_r, struct mail_storage_service_user **dest_service_user_r, const char **error_r) { const struct mail_storage_service_input *old_input; struct mail_storage_service_input input; struct mail_storage_service_ctx *service_ctx; struct ioloop_context *cur_ioloop_ctx; int ret; i_assert(user->_service_user != NULL); service_ctx = mail_storage_service_user_get_service_ctx(user->_service_user); old_input = mail_storage_service_user_get_input(user->_service_user); if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) io_loop_context_deactivate(cur_ioloop_ctx); i_zero(&input); input.module = old_input->module; input.service = old_input->service; input.username = dest_username; input.session_id_prefix = user->session_id; input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; ret = mail_storage_service_lookup_next(service_ctx, &input, dest_service_user_r, dest_user_r, error_r); if (ret == 0) *error_r = "User not found"; return ret; } static int mcp_update_shared_key(struct mailbox_transaction_context *t, struct mail_user *user, const char *target_uid, struct dcrypt_private_key *key, const char **error_r) { const char *error; struct mail_user *dest_user; struct mail_storage_service_user *dest_service_user; struct ioloop_context *cur_ioloop_ctx; struct dcrypt_public_key *pkey; const char *dest_username; int ret = 0; bool disallow_insecure = mail_crypt_acl_secure_sharing_enabled(user); ret = mcp_user_create(user, target_uid, &dest_user, &dest_service_user, &error); /* to make sure we get correct logging context */ if (ret > 0) mail_storage_service_io_deactivate_user(dest_service_user); mail_storage_service_io_activate_user(user->_service_user); if (ret <= 0) { i_error("Cannot initialize destination user %s: %s", target_uid, error); return ret; } else { i_assert(dest_user != NULL); dest_username = dest_user->username; /* get public key from target user */ if ((ret = mail_crypt_user_get_public_key(dest_user, &pkey, error_r)) <= 0) { if (ret == 0 && disallow_insecure) { *error_r = t_strdup_printf("User %s has no active public key", dest_user->username); ret = -1; } else if (ret == 0) { /* perform insecure sharing */ dest_username = NULL; pkey = NULL; ret = 1; } } if (ret == 1) { ARRAY_TYPE(dcrypt_private_key) keys; t_array_init(&keys, 1); array_append(&keys, &key, 1); ret = mail_crypt_box_share_private_keys(t, pkey, dest_username, &keys, error_r); } } /* logging context swap again */ mail_storage_service_io_deactivate_user(user->_service_user); mail_storage_service_io_activate_user(dest_service_user); mail_user_unref(&dest_user); mail_storage_service_user_unref(&dest_service_user); if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) io_loop_context_deactivate(cur_ioloop_ctx); mail_storage_service_io_activate_user(user->_service_user); return ret; } static int mcp_update_shared_keys(struct mailbox *box, struct mail_user *user, const char *pubid, struct dcrypt_private_key *key) { const char *error; int ret; ARRAY_TYPE(const_string) ids; t_array_init(&ids, 8); /* figure out who needs the key */ if ((ret = mail_crypt_box_get_pvt_digests(box, pool_datastack_create(), MAIL_ATTRIBUTE_TYPE_SHARED, &ids, &error)) < 0) { i_error("mail_crypt_box_get_pvt_digests(%s, /shared) failed: %s", mailbox_get_vname(box), error); return -1; } const char *const *id; bool found = FALSE; string_t *uid = t_str_new(64); struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); ret = 0; /* then perform sharing */ array_foreach(&ids, id) { if (strchr(*id, '/') != NULL) { str_truncate(uid, 0); const char *hexuid = t_strcut(*id, '/'); hex_to_binary(hexuid, uid); if (mcp_update_shared_key(t, user, str_c(uid), key, &error) < 0) { i_error("mcp_update_shared_key(%s, %s) failed: %s", mailbox_get_vname(box), str_c(uid), error); ret = -1; break; } } else if (!found) { found = TRUE; if (mail_crypt_box_set_shared_key(t, pubid, key, NULL, NULL, &error) < 0) { i_error("mail_crypt_box_set_shared_key(%s) failed: %s", mailbox_get_vname(box), error); ret = -1; break; } } } if (ret < 0) { mailbox_transaction_rollback(&t); } else if (mailbox_transaction_commit(&t) < 0) { i_error("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), error); ret = -1; } return ret; } static int mcp_keypair_generate(struct mcp_cmd_context *ctx, struct dcrypt_public_key *user_key, struct mailbox *box, struct dcrypt_keypair *pair_r, const char **pubid_r, const char **error_r) { struct dcrypt_keypair pair = {NULL, NULL}; int ret; struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); if ((ret = mail_crypt_box_get_public_key(t, &pair.pub, error_r)) < 0) { ret = -1; } else if (ret == 1 && !ctx->force) { i_info("Folder key exists. Use -f to generate a new one"); buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); const char *error; if (!dcrypt_key_id_public(pair.pub, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error)) { i_error("dcrypt_key_id_public() failed: %s", error); ret = -1; } else { *pubid_r = p_strdup(ctx->ctx.pool, binary_to_hex(key_id->data, key_id->used)); *pair_r = pair; ret = 1; } } else if (ret == 1 && ctx->recrypt_box_keys) { /* do nothing, because force isn't being used *OR* we are recrypting box keys and force refers to user keypair. FIXME: this could be less confusing altogether */ ret = 0; } else { if ((ret = mail_crypt_box_generate_keypair(box, &pair, user_key, pubid_r, error_r)) < 0) { ret = -1; } else { *pubid_r = p_strdup(ctx->ctx.pool, *pubid_r); *pair_r = pair; ret = 1; } } if (ret < 1) { if (pair.pub != NULL) dcrypt_key_unref_public(&pair.pub); if (pair.priv != NULL) dcrypt_key_unref_private(&pair.priv); } (void)mailbox_transaction_commit(&t); return ret; } static int mcp_keypair_generate_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user, ARRAY_TYPE(generated_keys) *result) { const char *error; int ret; struct dcrypt_public_key *user_key; struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; const char *pubid; bool user_key_generated = FALSE; struct generated_key *res; if ((ret = mail_crypt_user_get_public_key(user, &user_key, &error)) <= 0) { struct dcrypt_keypair pair; if (ret < 0) { i_error("mail_crypt_user_get_public_key(%s) failed: %s", user->username, error); } else if (mail_crypt_user_generate_keypair(user, &pair, &pubid, &error) < 0) { ret = -1; i_error("mail_crypt_user_generate_keypair(%s) failed: %s", user->username, error); res = array_append_space(result); res->name = ""; res->error = p_strdup(_ctx->pool, error); res->success = FALSE; } else { res = array_append_space(result); res->name = DOVEADM_MCP_USERKEY; res->id = p_strdup(_ctx->pool, pubid); res->success = TRUE; /* don't do it again later on */ user_key_generated = TRUE; ret = 1; user_key = pair.pub; dcrypt_key_unref_private(&pair.priv); } if (ret < 0) return ret; ctx->matched_keys++; } if (ret == 1 && ctx->userkey_only && !user_key_generated) { if (!ctx->force) { i_info("userkey exists. Use -f to generate a new one"); buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); if (!dcrypt_key_id_public(user_key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error)) { i_error("dcrypt_key_id_public() failed: %s", error); return -1; } const char *hash = binary_to_hex(key_id->data, key_id->used); res = array_append_space(result); res->name = DOVEADM_MCP_USERKEY; res->id = p_strdup(_ctx->pool, hash); res->success = TRUE; ctx->matched_keys++; return 1; } struct dcrypt_keypair pair; dcrypt_key_unref_public(&user_key); /* regen user key */ res = array_append_space(result); res->name = DOVEADM_MCP_USERKEY; if (mail_crypt_user_generate_keypair(user, &pair, &pubid, &error) < 0) { res->success = FALSE; res->error = p_strdup(_ctx->pool, error); return -1; } res->success = TRUE; res->id = p_strdup(_ctx->pool, pubid); user_key = pair.pub; dcrypt_key_unref_private(&pair.priv); ctx->matched_keys++; } if (ctx->userkey_only) return 0; const char *const *patterns = (const char *const[]){ "*", NULL }; /* only re-encrypt all folder keys if wanted */ if (!ctx->recrypt_box_keys) { patterns = ctx->ctx.args; } const struct mailbox_info *info; struct mailbox_list_iterate_context *iter = mailbox_list_iter_init_namespaces(user->namespaces, patterns, MAIL_NAMESPACE_TYPE_PRIVATE, MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & MAILBOX_NOSELECT) != 0 || (info->flags & MAILBOX_NONEXISTENT) != 0) continue; struct dcrypt_keypair pair; struct mailbox *box = mailbox_alloc(info->ns->list, info->vname, 0); if (mailbox_open(box) < 0) { res = array_append_space(result); res->name = p_strdup(_ctx->pool, info->vname); res->success = FALSE; res->error = p_strdup(_ctx->pool, mailbox_get_last_internal_error(box, NULL)); } else if ((ret = mcp_keypair_generate(ctx, user_key, box, &pair, &pubid, &error)) < 0) { res = array_append_space(result); res->name = p_strdup(_ctx->pool, info->vname); res->success = FALSE; res->error = p_strdup(_ctx->pool, error); } else if (ret == 0) { /* nothing happened because key already existed and force wasn't used, skip */ } else if (ret > 0) { res = array_append_space(result); res->name = p_strdup(_ctx->pool, info->vname); res->success = TRUE; res->id = pubid; T_BEGIN { mcp_update_shared_keys(box, user, pubid, pair.priv); } T_END; if (pair.pub != NULL) dcrypt_key_unref_public(&pair.pub); if (pair.priv != NULL) dcrypt_key_unref_private(&pair.priv); ctx->matched_keys++; } mailbox_free(&box); } (void)mailbox_list_iter_deinit(&iter); dcrypt_key_unref_public(&user_key); return 0; } static int cmd_mcp_keypair_generate_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; int ret = 0; ARRAY_TYPE(generated_keys) result; p_array_init(&result, _ctx->pool, 8); if (mcp_keypair_generate_run(_ctx, user, &result) < 0) _ctx->exit_code = EX_DATAERR; doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("success", " ", 0); doveadm_print_header("box", "Folder", 0); doveadm_print_header("pubid", "Public ID", 0); const struct generated_key *res; array_foreach(&result, res) { if (res->success) doveadm_print(DOVEADM_MCP_SUCCESS); else { _ctx->exit_code = EX_DATAERR; ret = -1; doveadm_print(DOVEADM_MCP_FAIL); } doveadm_print(res->name); if (!res->success) doveadm_print(t_strdup_printf("ERROR: %s", res->error)); else doveadm_print(res->id); } if (ctx->matched_keys == 0) i_warning("mailbox cryptokey generate: Nothing was matched. " "Use -U or specify mask?"); return ret; } static void mcp_key_list(struct mcp_cmd_context *ctx, struct mail_user *user, void(*callback)(const struct generated_key *, void *), void *context) { const char *error; int ret; /* we need to use the mailbox attribute API here, as we are not necessarely able to decrypt any of these keys */ ARRAY_TYPE(const_string) ids; t_array_init(&ids, 8); if (ctx->userkey_only) { struct mailbox_attribute_iter *iter; struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); struct mail_attribute_value value; i_zero(&value); if (mailbox_open(box) < 0) { i_error("mailbox_open(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); return; } struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, USER_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) < 0) { i_error("mailbox_get_attribute(%s, %s) failed: %s", mailbox_get_vname(box), USER_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } iter = mailbox_attribute_iter_init(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, USER_CRYPT_PREFIX PRIVKEYS_PREFIX); const char *key_id; if (value.value == NULL) value.value = ""; while ((key_id = mailbox_attribute_iter_next(iter)) != NULL) { struct generated_key key; key.id = key_id; key.active = strcmp(value.value, key_id) == 0; key.name = ""; key.box = box; callback(&key, context); ctx->matched_keys++; } if (mailbox_attribute_iter_deinit(&iter) < 0) i_error("mailbox_attribute_iter_deinit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); (void)mailbox_transaction_commit(&t); mailbox_free(&box); return; } const struct mailbox_info *info; struct mailbox_list_iterate_context *iter = mailbox_list_iter_init_namespaces(user->namespaces, ctx->ctx.args, MAIL_NAMESPACE_TYPE_PRIVATE, MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & MAILBOX_NOSELECT) != 0 || (info->flags & MAILBOX_NONEXISTENT) != 0) continue; struct mailbox *box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY); if (mailbox_open(box) < 0) { i_error("mailbox_open(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); mailbox_free(&box); continue; } struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); struct mail_attribute_value value; i_zero(&value); array_clear(&ids); /* get active ID */ if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_SHARED, BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) < 0) { i_error("mailbox_get_attribute(%s, %s) failed: %s", mailbox_get_vname(box), BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, mailbox_get_last_internal_error(box, NULL)); } else if ((ret = mail_crypt_box_get_pvt_digests(box, pool_datastack_create(), MAIL_ATTRIBUTE_TYPE_PRIVATE, &ids, &error)) < 0) { i_error("mail_crypt_box_get_pvt_digests(%s) failed: %s", mailbox_get_vname(box), error); } else { const char *const *id; const char *boxname = mailbox_get_vname(box); if (value.value == NULL) value.value = ""; array_foreach(&ids, id) { struct generated_key key; key.name = boxname; key.id = *id; if (value.value != NULL) key.active = strcmp(*id, value.value) == 0; else key.active = FALSE; key.box = box; callback(&key, context); ctx->matched_keys++; } } (void)mailbox_transaction_commit(&t); mailbox_free(&box); } (void)mailbox_list_iter_deinit(&iter); } static void cmd_mcp_key_list_cb(const struct generated_key *_key, void *context) { struct mcp_key_iter_ctx *ctx = context; struct generated_key *key = array_append_space(&ctx->keys); key->name = p_strdup(ctx->pool, _key->name); key->id = p_strdup(ctx->pool, _key->id); key->active = _key->active; } static int cmd_mcp_key_list_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; struct mcp_key_iter_ctx iter_ctx; i_zero(&iter_ctx); iter_ctx.pool = _ctx->pool; p_array_init(&iter_ctx.keys, _ctx->pool, 8); mcp_key_list(ctx, user, cmd_mcp_key_list_cb, &iter_ctx); doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header("box", "Folder", 0); doveadm_print_header("active", "Active", 0); doveadm_print_header("pubid", "Public ID", 0); const struct generated_key *key; array_foreach(&iter_ctx.keys, key) { doveadm_print(key->name); doveadm_print(key->active ? "yes" : "no"); doveadm_print(key->id); } if (ctx->matched_keys == 0) i_warning("mailbox cryptokey list: Nothing was matched. " "Use -U or specify mask?"); return 0; } static void cmd_mcp_key_export_cb(const struct generated_key *key, void *context ATTR_UNUSED) { struct dcrypt_private_key *pkey; bool user_key = FALSE; const char *error = NULL; int ret; if (*key->name == '\0') user_key = TRUE; doveadm_print(key->name); doveadm_print(key->id); struct mailbox_transaction_context *t = mailbox_transaction_begin(key->box, 0); if ((ret = mail_crypt_get_private_key(t, key->id, user_key, FALSE, &pkey, &error)) <= 0) { if (ret == 0) error = "key not found"; doveadm_print(t_strdup_printf("ERROR: %s", error)); doveadm_print(""); } else { string_t *out = t_str_new(64); if (!dcrypt_key_store_private(pkey, DCRYPT_FORMAT_PEM, NULL, out, NULL, NULL, &error)) { doveadm_print(t_strdup_printf("ERROR: %s", error)); doveadm_print(""); } else { /* this is to make it more compatible with openssl cli as it expects BEGIN on it's own line */ doveadm_print(t_strdup_printf("\n%s", str_c(out))); } dcrypt_key_unref_private(&pkey); } (void)mailbox_transaction_commit(&t); } static int cmd_mcp_key_export_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); doveadm_print_header("box", "Folder", 0); doveadm_print_header("name", "Public ID", 0); doveadm_print_header("error", "Error", 0); doveadm_print_header("key", "Key", 0); mcp_key_list(ctx, user, cmd_mcp_key_export_cb, NULL); return 0; } static int cmd_mcp_key_password_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; struct raw_key { const char *attr; const char *id; const char *data; }; ARRAY(struct raw_key) raw_keys; doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); doveadm_print_header_simple("result"); if (ctx->ask_old_password) { if (ctx->old_password != NULL) { doveadm_print("old password specified, cannot ask for it"); _ctx->exit_code = EX_USAGE; return -1; } if (!_ctx->cli) { doveadm_print("No cli - cannot ask for password"); _ctx->exit_code = EX_USAGE; return -1; } ctx->old_password = p_strdup(_ctx->pool, t_askpass("Old password: ")); } if (ctx->ask_new_password) { if (ctx->new_password != NULL) { doveadm_print("new password specified, cannot ask for it"); _ctx->exit_code = EX_USAGE; return -1; } if (!_ctx->cli) { doveadm_print("No cli - cannot ask for password"); _ctx->exit_code = EX_USAGE; return -1; } ctx->new_password = p_strdup(_ctx->pool, t_askpass("New password: ")); } if (ctx->clear_password && (ctx->new_password != NULL || mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD) != NULL)) { doveadm_print("clear password and new password specified"); _ctx->exit_code = EX_USAGE; return -1; } struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box = mailbox_alloc(ns->list, "INBOX", 0); if (mailbox_open(box) < 0) { doveadm_print(t_strdup_printf("mailbox_open(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL))); _ctx->exit_code = EX_TEMPFAIL; return -1; } struct mailbox_transaction_context *t = mailbox_transaction_begin(box, 0); t_array_init(&raw_keys, 8); /* then get the current user keys, all of them */ struct mailbox_attribute_iter *iter = mailbox_attribute_iter_init(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, USER_CRYPT_PREFIX PRIVKEYS_PREFIX); const char *error; const char *key_id; int ret = 1; unsigned int count = 0; while ((key_id = mailbox_attribute_iter_next(iter)) != NULL) { const char *attr = t_strdup_printf(USER_CRYPT_PREFIX PRIVKEYS_PREFIX "%s", key_id); struct mail_attribute_value value; if ((ret = mailbox_attribute_get(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, attr, &value)) < 0) { doveadm_print(t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", mailbox_get_vname(box), attr, mailbox_get_last_internal_error(box, NULL))); _ctx->exit_code = EX_TEMPFAIL; break; } else if (ret > 0) { struct raw_key *raw_key = array_append_space(&raw_keys); raw_key->attr = p_strdup(_ctx->pool, attr); raw_key->id = p_strdup(_ctx->pool, key_id); raw_key->data = p_strdup(_ctx->pool, value.value); } } if (ret == 1) { struct dcrypt_private_key *key; const struct raw_key *raw_key; const char *algo = ctx->new_password != NULL ? MAIL_CRYPT_PW_CIPHER : NULL; string_t *newkey = t_str_new(256); array_foreach(&raw_keys, raw_key) { struct mail_attribute_value value; if (!dcrypt_key_load_private(&key, raw_key->data, ctx->old_password, NULL, &error)) { doveadm_print(t_strdup_printf("dcrypt_key_load_private(%s) failed: %s", raw_key->id, error)); _ctx->exit_code = EX_DATAERR; ret = -1; break; } /* save it */ str_truncate(newkey, 0); if (!dcrypt_key_store_private(key, DCRYPT_FORMAT_DOVECOT, algo, newkey, ctx->new_password, NULL, &error)) { doveadm_print(t_strdup_printf("dcrypt_key_store_private(%s) failed: %s", raw_key->id, error)); _ctx->exit_code = EX_DATAERR; ret = -1; } dcrypt_key_unref_private(&key); if (ret == -1) break; i_zero(&value); value.value = str_c(newkey); /* and store it */ if (mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, raw_key->attr, &value) < 0) { doveadm_print(t_strdup_printf("mailbox_attribute_set(%s, %s) failed: %s", mailbox_get_vname(box), raw_key->attr, mailbox_get_last_internal_error(box, NULL))); _ctx->exit_code = EX_TEMPFAIL; ret = -1; break; } count++; } } if (ret < 1) { mailbox_transaction_rollback(&t); } else { if (mailbox_transaction_commit(&t) < 0) { doveadm_print(t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL))); } else { doveadm_print(t_strdup_printf("Changed password for %u key(s)", count)); } } (void)mailbox_attribute_iter_deinit(&iter); mailbox_free(&box); return ret; } static bool cmd_mcp_keypair_generate_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; switch (c) { case 'U': ctx->userkey_only = TRUE; break; case 'R': ctx->recrypt_box_keys = TRUE; break; case 'f': ctx->force = TRUE; break; default: return FALSE; } return TRUE; } static bool cmd_mcp_key_password_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; switch (c) { case 'N': ctx->ask_new_password = TRUE; break; case 'O': ctx->ask_old_password = TRUE; break; case 'C': ctx->clear_password = TRUE; break; case 'o': ctx->old_password = p_strdup(_ctx->pool, optarg); break; case 'n': ctx->new_password = p_strdup(_ctx->pool, optarg); break; default: return FALSE; } return TRUE; } static bool cmd_mcp_key_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct mcp_cmd_context *ctx = (struct mcp_cmd_context *)_ctx; switch (c) { case 'U': ctx->userkey_only = TRUE; break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context *cmd_mcp_keypair_generate_alloc(void) { struct mcp_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); ctx->ctx.getopt_args = "URf"; ctx->ctx.v.parse_arg = cmd_mcp_keypair_generate_parse_arg; ctx->ctx.v.run = cmd_mcp_keypair_generate_run; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_mcp_key_list_alloc(void) { struct mcp_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); ctx->ctx.getopt_args = "U"; ctx->ctx.v.parse_arg = cmd_mcp_key_parse_arg; ctx->ctx.v.run = cmd_mcp_key_list_run; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_mcp_key_export_alloc(void) { struct mcp_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); ctx->ctx.getopt_args = "U"; ctx->ctx.v.parse_arg = cmd_mcp_key_parse_arg; ctx->ctx.v.run = cmd_mcp_key_export_run; return &ctx->ctx; } static struct doveadm_mail_cmd_context *cmd_mcp_key_password_alloc(void) { struct mcp_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); ctx->ctx.getopt_args = "NOCo:n:"; ctx->ctx.v.parse_arg = cmd_mcp_key_password_parse_arg; ctx->ctx.v.run = cmd_mcp_key_password_run; return &ctx->ctx; } struct doveadm_cmd_ver2 doveadm_cmd_mcp_keypair_generate = { .name = "mailbox cryptokey generate", .mail_cmd = cmd_mcp_keypair_generate_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-URf] mailbox [ mailbox .. ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('U', "user-key-only", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('R', "re-encrypt-box-keys", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('f', "force", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_list = { .name = "mailbox cryptokey list", .mail_cmd = cmd_mcp_key_list_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "-U | mailbox [ mailbox .. ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('U', "user-key", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_export = { .name = "mailbox cryptokey export", .mail_cmd = cmd_mcp_key_export_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "-U | mailbox [ mailbox .. ]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('U', "user-key", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }; struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_password = { .name = "mailbox cryptokey password", .mail_cmd = cmd_mcp_key_password_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-NOC] [-opassword] [-npassword]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('C', "clear-password", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('N', "ask-new-password", CMD_PARAM_BOOL, 0) DOVEADM_CMD_PARAM('n', "new-password", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('O', "ask-old-password", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('o', "old-password", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAMS_END }; void doveadm_mail_crypt_plugin_init(struct module *mod ATTR_UNUSED) { doveadm_cmd_register_ver2(&doveadm_cmd_mcp_keypair_generate); doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_list); doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_export); doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_password); } void doveadm_mail_crypt_plugin_deinit(void) { } dovecot-2.2.33.2/src/plugins/mail-crypt/fs-mail-crypt.c0000644000175000017500000000320713123174404017512 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #define FS_CLASS_CRYPT fs_class_mail_crypt #include "fs-crypt-common.c" static int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r) { struct mailbox_list *list = mailbox_list_fs_get_list(&fs->fs); const char *error; if (fs->keys_loaded) return 0; if (fs->public_key_path != NULL || fs->private_key_path != NULL) { /* overrides using settings */ if (fs_crypt_load_keys_from_path(fs, error_r) < 0) return -1; fs->keys_loaded = TRUE; return 0; } if (list == NULL) { *error_r = "fs-mail-crypt can be used only via lib-storage"; return -1; } if (mail_crypt_global_keys_load(mailbox_list_get_namespace(list)->user, fs->set_prefix, &fs->keys, FALSE, &error) < 0) { *error_r = t_strdup_printf("%s: %s", fs->set_prefix, error); return -1; } fs->keys_loaded = TRUE; return 0; } const struct fs fs_class_mail_crypt = { .name = "mail-crypt", .v = { fs_crypt_alloc, fs_crypt_init, fs_crypt_deinit, fs_wrapper_get_properties, fs_crypt_file_init, fs_crypt_file_deinit, fs_crypt_file_close, fs_wrapper_file_get_path, fs_wrapper_set_async_callback, fs_wrapper_wait_async, fs_wrapper_set_metadata, fs_wrapper_get_metadata, fs_wrapper_prefetch, fs_read_via_stream, fs_crypt_read_stream, fs_write_via_stream, fs_crypt_write_stream, fs_crypt_write_stream_finish, fs_wrapper_lock, fs_wrapper_unlock, fs_wrapper_exists, fs_wrapper_stat, fs_wrapper_copy, fs_wrapper_rename, fs_wrapper_delete, fs_wrapper_iter_init, fs_wrapper_iter_next, fs_wrapper_iter_deinit, NULL, fs_wrapper_get_nlinks, } }; dovecot-2.2.33.2/src/plugins/mail-crypt/Makefile.am0000644000175000017500000000630313165463624016726 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-dcrypt \ -I$(top_srcdir)/src/lib-fs \ -I$(top_srcdir)/src/doveadm \ -I$(top_srcdir)/src/plugins/acl if SSL_VERSION_GE_102 test_options = else !SSL_VERSION_GE_102 test_options = NOUNDEF=1 endif !SSL_VERSION_GE_102 doveadm_moduledir = $(moduledir)/doveadm NOPLUGIN_LDFLAGS = module_LTLIBRARIES = \ lib10_mail_crypt_plugin.la \ lib05_mail_crypt_acl_plugin.la \ libfs_crypt.la \ libfs_mail_crypt.la doveadm_module_LTLIBRARIES = \ libdoveadm_mail_crypt_plugin.la lib10_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version lib10_mail_crypt_plugin_la_LIBADD = \ $(LIBDCRYPT_LIBS) \ $(LIBDOVECOT) lib05_mail_crypt_acl_plugin_la_LDFLAGS = -module -avoid-version if DOVECOT_PLUGIN_DEPS lib05_mail_crypt_acl_plugin_la_LIBADD = \ $(LIBDCRYPT_LIBS) \ lib10_mail_crypt_plugin.la endif lib10_mail_crypt_plugin_la_SOURCES = \ mail-crypt-global-key.c \ mail-crypt-userenv.c \ mail-crypt-key.c \ mail-crypt-plugin.c lib05_mail_crypt_acl_plugin_la_SOURCES = \ mail-crypt-acl-plugin.c libfs_crypt_la_SOURCES = fs-crypt.c \ mail-crypt-global-key.c \ mail-crypt-pluginenv.c \ fs-crypt-settings.c libfs_crypt_la_LIBADD = $(LIBDOVECOT) libfs_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libfs_crypt_la_LDFLAGS = -module -avoid-version libfs_mail_crypt_la_SOURCES = fs-mail-crypt.c \ mail-crypt-global-key.c \ mail-crypt-userenv.c libfs_mail_crypt_la_LIBADD = $(LIBDOVECOT) libfs_mail_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libfs_mail_crypt_la_LDFLAGS = -module -avoid-version libdoveadm_mail_crypt_plugin_la_SOURCES = \ doveadm-mail-crypt.c libdoveadm_mail_crypt_plugin_la_LIBADD = $(LIBDOVECOT) libdoveadm_mail_crypt_plugin_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libdoveadm_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version test_programs = \ test-mail-global-key \ test-mail-key test_mail_global_key_SOURCES = \ test-mail-global-key.c \ fs-crypt-settings.c \ mail-crypt-global-key.c test_mail_global_key_LDADD = $(LIBDOVECOT) test_mail_global_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) test_mail_global_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) test_mail_global_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" test_mail_key_SOURCES = \ test-mail-key.c \ mail-crypt-key.c \ mail-crypt-global-key.c \ mail-crypt-userenv.c test_mail_key_LDADD = $(LIBDOVECOT) $(LIBDOVECOT_STORAGE) test_mail_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(LIBDOVECOT_STORAGE_DEPS) test_mail_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" EXTRA_DIST = fs-crypt-common.c noinst_HEADERS = \ mail-crypt-plugin.h \ mail-crypt-common.h \ mail-crypt-global-key.h \ mail-crypt-key.h \ fs-crypt-settings.h check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! env $(test_options) $(RUN_TEST) ./$$bin; then exit 1; fi; \ done noinst_PROGRAMS = $(test_programs) dovecot-2.2.33.2/src/plugins/apparmor/0002755000175000017500000000000013172375614014511 500000000000000dovecot-2.2.33.2/src/plugins/apparmor/Makefile.in0000644000175000017500000005512113172375574016505 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/apparmor ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) am__DEPENDENCIES_1 = lib01_apparmor_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_lib01_apparmor_plugin_la_OBJECTS = apparmor-plugin.lo lib01_apparmor_plugin_la_OBJECTS = \ $(am_lib01_apparmor_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib01_apparmor_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib01_apparmor_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib01_apparmor_plugin_la_SOURCES) DIST_SOURCES = $(lib01_apparmor_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib01_apparmor_plugin_la_LDFLAGS = -module -avoid-version lib01_apparmor_plugin_la_LIBADD = $(APPARMOR_LIBS) lib01_apparmor_plugin_la_SOURCES = \ apparmor-plugin.c module_LTLIBRARIES = \ lib01_apparmor_plugin.la all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/apparmor/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/apparmor/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib01_apparmor_plugin.la: $(lib01_apparmor_plugin_la_OBJECTS) $(lib01_apparmor_plugin_la_DEPENDENCIES) $(EXTRA_lib01_apparmor_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib01_apparmor_plugin_la_LINK) -rpath $(moduledir) $(lib01_apparmor_plugin_la_OBJECTS) $(lib01_apparmor_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apparmor-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/apparmor/apparmor-plugin.c0000644000175000017500000000564313165463624017721 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "module-dir.h" #include "randgen.h" #include "mail-user.h" #include "mail-storage-private.h" #include "mail-storage-hooks.h" #include #define APPARMOR_PLUGIN_SETTING_HAT_PREFIX "apparmor_hat" const char *apparmor_plugin_version = DOVECOT_ABI_VERSION; /* hooks into user creation and deinit, will try to use hats provided by apparmor_hat, apparmor_hat1... etc */ #define APPARMOR_USER_CONTEXT(obj) \ (struct apparmor_mail_user*)MODULE_CONTEXT(obj, apparmor_mail_user_module) static MODULE_CONTEXT_DEFINE_INIT(apparmor_mail_user_module, &mail_user_module_register); struct apparmor_mail_user { union mail_user_module_context module_ctx; unsigned long token; }; void apparmor_plugin_init(struct module*); void apparmor_plugin_deinit(void); static void apparmor_log_current_context(struct mail_user *user) { char *con, *mode; if (!user->mail_debug) return; if (aa_getcon(&con, &mode) < 0) { i_debug("aa_getcon() failed: %m"); } else { i_debug("apparmor: Current context=%s, mode=%s", con, mode); free(con); } } static void apparmor_mail_user_deinit(struct mail_user *user) { struct apparmor_mail_user *auser = APPARMOR_USER_CONTEXT(user); if (user == NULL) return; if (aa_change_hat(NULL, auser->token)<0) i_fatal("aa_change_hat(NULL) failed: %m"); apparmor_log_current_context(user); } static void apparmor_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct apparmor_mail_user *auser; ARRAY_TYPE(const_string) hats; /* see if we can find any hats */ const char *hat = mail_user_plugin_getenv(user, APPARMOR_PLUGIN_SETTING_HAT_PREFIX); if (hat == NULL) return; t_array_init(&hats, 8); array_append(&hats, &hat, 1); for(unsigned int i = 2;; i++) { hat = mail_user_plugin_getenv(user, t_strdup_printf("%s%u", APPARMOR_PLUGIN_SETTING_HAT_PREFIX, i)); if (hat == NULL) break; array_append(&hats, &hat, 1); } array_append_zero(&hats); /* we got hat(s) to try */ auser = p_new(user->pool, struct apparmor_mail_user, 1); auser->module_ctx.super = *v; user->vlast = &auser->module_ctx.super; v->deinit = apparmor_mail_user_deinit; MODULE_CONTEXT_SET(user, apparmor_mail_user_module, auser); /* generate a magic token */ random_fill(&auser->token, sizeof(auser->token)); /* try change hat */ if (aa_change_hatv(array_idx_modifiable(&hats, 0), auser->token) < 0) { i_fatal("aa_change_hatv(%s) failed: %m", t_array_const_string_join(&hats, ",")); } apparmor_log_current_context(user); } static const struct mail_storage_hooks apparmor_hooks = { .mail_user_created = apparmor_mail_user_created }; void apparmor_plugin_init(struct module *module) { random_init(); mail_storage_hooks_add(module, &apparmor_hooks); } void apparmor_plugin_deinit(void) { random_deinit(); mail_storage_hooks_remove(&apparmor_hooks); } dovecot-2.2.33.2/src/plugins/apparmor/Makefile.am0000644000175000017500000000057013147010713016451 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib01_apparmor_plugin_la_LDFLAGS = -module -avoid-version lib01_apparmor_plugin_la_LIBADD = $(APPARMOR_LIBS) lib01_apparmor_plugin_la_SOURCES = \ apparmor-plugin.c module_LTLIBRARIES = \ lib01_apparmor_plugin.la dovecot-2.2.33.2/src/plugins/imap-quota/0002755000175000017500000000000013172375613014744 500000000000000dovecot-2.2.33.2/src/plugins/imap-quota/Makefile.in0000644000175000017500000005610513172375574016744 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/imap-quota ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(imap_moduledir)" LTLIBRARIES = $(imap_module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib11_imap_quota_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../quota/lib10_quota_plugin.la am_lib11_imap_quota_plugin_la_OBJECTS = imap-quota-plugin.lo lib11_imap_quota_plugin_la_OBJECTS = \ $(am_lib11_imap_quota_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib11_imap_quota_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib11_imap_quota_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib11_imap_quota_plugin_la_SOURCES) DIST_SOURCES = $(lib11_imap_quota_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/quota imap_moduledir = $(moduledir) lib11_imap_quota_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib11_imap_quota_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib11_imap_quota_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../quota/lib10_quota_plugin.la lib11_imap_quota_plugin_la_SOURCES = \ imap-quota-plugin.c noinst_HEADERS = \ imap-quota-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/imap-quota/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/imap-quota/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ } uninstall-imap_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ done clean-imap_moduleLTLIBRARIES: -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) @list='$(imap_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib11_imap_quota_plugin.la: $(lib11_imap_quota_plugin_la_OBJECTS) $(lib11_imap_quota_plugin_la_DEPENDENCIES) $(EXTRA_lib11_imap_quota_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib11_imap_quota_plugin_la_LINK) -rpath $(imap_moduledir) $(lib11_imap_quota_plugin_la_OBJECTS) $(lib11_imap_quota_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-quota-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(imap_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-imap_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-imap_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-imap_moduleLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-imap_moduleLTLIBRARIES install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-imap_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/imap-quota/imap-quota-plugin.c0000644000175000017500000001612713165463624020410 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "imap-quote.h" #include "mail-namespace.h" #include "imap-commands.h" #include "quota.h" #include "quota-plugin.h" #include "imap-quota-plugin.h" #define QUOTA_USER_SEPARATOR ':' const char *imap_quota_plugin_version = DOVECOT_ABI_VERSION; static struct module *imap_quota_module; static imap_client_created_func_t *next_hook_client_created; static const char * imap_quota_root_get_name(struct mail_user *user, struct mail_user *owner, struct quota_root *root) { const char *name; name = quota_root_get_name(root); if (user == owner || owner == NULL) return name; return t_strdup_printf("%s%c%s", owner->username, QUOTA_USER_SEPARATOR, name); } static int quota_reply_write(string_t *str, struct mail_user *user, struct mail_user *owner, struct quota_root *root) { const char *name, *const *list; unsigned int i; uint64_t value, limit; size_t prefix_len, orig_len = str_len(str); int ret = 0; str_append(str, "* QUOTA "); name = imap_quota_root_get_name(user, owner, root); imap_append_astring(str, name); str_append(str, " ("); prefix_len = str_len(str); list = quota_root_get_resources(root); for (i = 0; *list != NULL; list++) { ret = quota_get_resource(root, "", *list, &value, &limit); if (ret < 0) break; if (ret > 0) { if (i > 0) str_append_c(str, ' '); str_printfa(str, "%s %llu %llu", *list, (unsigned long long)value, (unsigned long long)limit); i++; } } if (ret <= 0 && str_len(str) == prefix_len) { /* this quota root doesn't have any quota actually enabled. */ str_truncate(str, orig_len); return ret; } str_append(str, ")\r\n"); return 1; } static bool cmd_getquotaroot(struct client_command_context *cmd) { struct client *client = cmd->client; struct quota_user *quser = QUOTA_USER_CONTEXT(client->user); struct mail_namespace *ns; struct mailbox *box; struct quota_root_iter *iter; struct quota_root *root; const char *mailbox, *orig_mailbox, *name; string_t *quotaroot_reply, *quota_reply; int ret; /* */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; if (quser == NULL) { client_send_tagline(cmd, "OK No quota."); return TRUE; } if (ns->owner != NULL && ns->owner != client->user) { client_send_tagline(cmd, "NO Not showing other users' quota."); return TRUE; } box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY); /* build QUOTAROOT reply and QUOTA reply for all quota roots */ quotaroot_reply = t_str_new(128); quota_reply = t_str_new(256); str_append(quotaroot_reply, "* QUOTAROOT "); imap_append_astring(quotaroot_reply, orig_mailbox); ret = 0; iter = quota_root_iter_init(box); while ((root = quota_root_iter_next(iter)) != NULL) { if (quota_root_is_hidden(root)) continue; str_append_c(quotaroot_reply, ' '); name = imap_quota_root_get_name(client->user, ns->owner, root); imap_append_astring(quotaroot_reply, name); if (quota_reply_write(quota_reply, client->user, ns->owner, root) < 0) ret = -1; } quota_root_iter_deinit(&iter); mailbox_free(&box); /* send replies */ if (ret < 0) client_send_tagline(cmd, "NO Internal quota calculation error."); else if (str_len(quota_reply) == 0) client_send_tagline(cmd, "OK No quota."); else { client_send_line(client, str_c(quotaroot_reply)); o_stream_nsend(client->output, str_data(quota_reply), str_len(quota_reply)); client_send_tagline(cmd, "OK Getquotaroot completed."); } return TRUE; } static bool parse_quota_root(struct mail_user *user, const char *root_name, struct mail_user **owner_r, struct quota_root **root_r) { const char *p; *owner_r = user; *root_r = quota_root_lookup(user, root_name); if (*root_r != NULL || !user->admin) return *root_r != NULL; /* we're an admin. see if there's a quota root for another user. */ p = strchr(root_name, QUOTA_USER_SEPARATOR); if (p != NULL) { *owner_r = mail_user_find(user, t_strdup_until(root_name, p)); *root_r = *owner_r == NULL ? NULL : quota_root_lookup(*owner_r, p + 1); } return *root_r != NULL; } static bool cmd_getquota(struct client_command_context *cmd) { struct mail_user *owner; struct quota_root *root; const char *root_name; string_t *quota_reply; /* */ if (!client_read_string_args(cmd, 1, &root_name)) return FALSE; if (!parse_quota_root(cmd->client->user, root_name, &owner, &root)) { client_send_tagline(cmd, "NO Quota root doesn't exist."); return TRUE; } quota_reply = t_str_new(128); if (quota_reply_write(quota_reply, cmd->client->user, owner, root) < 0) client_send_tagline(cmd, "NO Internal quota calculation error."); else { o_stream_nsend(cmd->client->output, str_data(quota_reply), str_len(quota_reply)); client_send_tagline(cmd, "OK Getquota completed."); } return TRUE; } static bool cmd_setquota(struct client_command_context *cmd) { struct quota_root *root; struct mail_user *owner; const struct imap_arg *args, *list_args; const char *root_name, *name, *value_str, *error; uint64_t value; /* */ if (!client_read_args(cmd, 2, 0, &args)) return FALSE; if (!imap_arg_get_astring(&args[0], &root_name) || !imap_arg_get_list(&args[1], &list_args)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (!cmd->client->user->admin) { client_send_tagline(cmd, "NO Quota can be changed only by admin."); return TRUE; } if (!parse_quota_root(cmd->client->user, root_name, &owner, &root)) { client_send_tagline(cmd, "NO Quota root doesn't exist."); return TRUE; } for (; !IMAP_ARG_IS_EOL(list_args); list_args += 2) { if (!imap_arg_get_atom(&list_args[0], &name) || !imap_arg_get_atom(&list_args[1], &value_str) || str_to_uint64(value_str, &value) < 0) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (quota_set_resource(root, name, value, &error) < 0) { client_send_command_error(cmd, error); return TRUE; } } client_send_tagline(cmd, "OK Setquota completed."); return TRUE; } static void imap_quota_client_created(struct client **client) { if (mail_user_is_plugin_loaded((*client)->user, imap_quota_module)) client_add_capability(*client, "QUOTA"); if (next_hook_client_created != NULL) next_hook_client_created(client); } void imap_quota_plugin_init(struct module *module) { command_register("GETQUOTAROOT", cmd_getquotaroot, 0); command_register("GETQUOTA", cmd_getquota, 0); command_register("SETQUOTA", cmd_setquota, 0); imap_quota_module = module; next_hook_client_created = imap_client_created_hook_set(imap_quota_client_created); } void imap_quota_plugin_deinit(void) { command_unregister("GETQUOTAROOT"); command_unregister("GETQUOTA"); command_unregister("SETQUOTA"); imap_client_created_hook_set(next_hook_client_created); } const char *imap_quota_plugin_dependencies[] = { "quota", NULL }; const char imap_quota_plugin_binary_dependency[] = "imap"; dovecot-2.2.33.2/src/plugins/imap-quota/imap-quota-plugin.h0000644000175000017500000000043113123174404020371 00000000000000#ifndef IMAP_QUOTA_PLUGIN_H #define IMAP_QUOTA_PLUGIN_H struct module; extern const char *imap_quota_plugin_dependencies[]; extern const char imap_quota_plugin_binary_dependency[]; void imap_quota_plugin_init(struct module *module); void imap_quota_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/imap-quota/Makefile.am0000644000175000017500000000107713123174404016712 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/quota imap_moduledir = $(moduledir) NOPLUGIN_LDFLAGS = lib11_imap_quota_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib11_imap_quota_plugin.la if DOVECOT_PLUGIN_DEPS lib11_imap_quota_plugin_la_LIBADD = \ ../quota/lib10_quota_plugin.la endif lib11_imap_quota_plugin_la_SOURCES = \ imap-quota-plugin.c noinst_HEADERS = \ imap-quota-plugin.h dovecot-2.2.33.2/src/plugins/autocreate/0002755000175000017500000000000013172375613015023 500000000000000dovecot-2.2.33.2/src/plugins/autocreate/Makefile.in0000644000175000017500000005524313172375574017025 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/autocreate ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib20_autocreate_plugin_la_LIBADD = am_lib20_autocreate_plugin_la_OBJECTS = autocreate-plugin.lo lib20_autocreate_plugin_la_OBJECTS = \ $(am_lib20_autocreate_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_autocreate_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_autocreate_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_autocreate_plugin_la_SOURCES) DIST_SOURCES = $(lib20_autocreate_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib20_autocreate_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_autocreate_plugin.la lib20_autocreate_plugin_la_SOURCES = \ autocreate-plugin.c noinst_HEADERS = \ autocreate-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/autocreate/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/autocreate/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_autocreate_plugin.la: $(lib20_autocreate_plugin_la_OBJECTS) $(lib20_autocreate_plugin_la_DEPENDENCIES) $(EXTRA_lib20_autocreate_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_autocreate_plugin_la_LINK) -rpath $(moduledir) $(lib20_autocreate_plugin_la_OBJECTS) $(lib20_autocreate_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/autocreate-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/autocreate/autocreate-plugin.h0000644000175000017500000000023213123174404020526 00000000000000#ifndef AUTOCREATE_PLUGIN_H #define AUTOCREATE_PLUGIN_H void autocreate_plugin_init(struct module *module); void autocreate_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/autocreate/autocreate-plugin.c0000644000175000017500000000622513123174404020531 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ /* FIXME: this plugin is only for backwards compatibility. log a warning in v2.2 about this and in later versions remove completely */ #include "lib.h" #include "array.h" #include "unichar.h" #include "mail-user.h" #include "mail-namespace.h" #include "mail-storage-hooks.h" #include "autocreate-plugin.h" static struct mailbox_settings * mailbox_settings_find(struct mail_namespace *ns, const char *vname) { struct mailbox_settings *const *box_set; array_foreach(&ns->set->mailboxes, box_set) { if (strcmp((*box_set)->name, vname) == 0) return *box_set; } return NULL; } static void add_autobox(struct mail_user *user, const char *vname, bool subscriptions) { struct mail_namespace *ns; struct mailbox_settings *set; struct mail_namespace_settings tmp_ns_set; if (!uni_utf8_str_is_valid(vname)) { i_error("autocreate: Mailbox name isn't valid UTF-8: %s", vname); return; } ns = mail_namespace_find(user->namespaces, vname); if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) { i_error("autocreate: No namespace found for mailbox: %s", vname); return; } if (array_is_created(&ns->set->mailboxes)) tmp_ns_set.mailboxes = ns->set->mailboxes; else { p_array_init(&tmp_ns_set.mailboxes, user->pool, 16); /* work around ns->set being a const pointer. pretty ugly, but this plugin is deprecated anyway. */ memcpy((void *)&ns->set->mailboxes.arr, &tmp_ns_set.mailboxes.arr, sizeof(ns->set->mailboxes.arr)); } if (strncmp(vname, ns->prefix, ns->prefix_len) == 0) vname += ns->prefix_len; set = mailbox_settings_find(ns, vname); if (set == NULL) { set = p_new(user->pool, struct mailbox_settings, 1); set->name = p_strdup(user->pool, vname); set->autocreate = MAILBOX_SET_AUTO_NO; set->special_use = ""; array_append(&tmp_ns_set.mailboxes, &set, 1); } if (subscriptions) set->autocreate = MAILBOX_SET_AUTO_SUBSCRIBE; else if (strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0) set->autocreate = MAILBOX_SET_AUTO_CREATE; } static void read_autobox_settings(struct mail_user *user, const char *env_name_base, bool subscriptions) { const char *value; char env_name[13+MAX_INT_STRLEN+1]; unsigned int i = 1; value = mail_user_plugin_getenv(user, env_name_base); while (value != NULL) { add_autobox(user, value, subscriptions); if (i_snprintf(env_name, sizeof(env_name), "%s%u", env_name_base, ++i) < 0) i_unreached(); value = mail_user_plugin_getenv(user, env_name); } } static void autocreate_mail_namespaces_created(struct mail_namespace *namespaces) { read_autobox_settings(namespaces->user, "autocreate", FALSE); read_autobox_settings(namespaces->user, "autosubscribe", TRUE); } static struct mail_storage_hooks autocreate_mail_storage_hooks = { .mail_namespaces_created = autocreate_mail_namespaces_created }; void autocreate_plugin_init(struct module *module) { i_warning("autocreate plugin is deprecated, use mailbox { auto } setting instead"); mail_storage_hooks_add(module, &autocreate_mail_storage_hooks); } void autocreate_plugin_deinit(void) { mail_storage_hooks_remove(&autocreate_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/autocreate/Makefile.am0000644000175000017500000000062713123174404016771 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib20_autocreate_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_autocreate_plugin.la lib20_autocreate_plugin_la_SOURCES = \ autocreate-plugin.c noinst_HEADERS = \ autocreate-plugin.h dovecot-2.2.33.2/src/plugins/welcome/0002755000175000017500000000000013172375613014322 500000000000000dovecot-2.2.33.2/src/plugins/welcome/Makefile.in0000644000175000017500000005472513172375575016331 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/welcome ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(module_LTLIBRARIES) lib99_welcome_plugin_la_LIBADD = am_lib99_welcome_plugin_la_OBJECTS = welcome-plugin.lo lib99_welcome_plugin_la_OBJECTS = \ $(am_lib99_welcome_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib99_welcome_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib99_welcome_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib99_welcome_plugin_la_SOURCES) DIST_SOURCES = $(lib99_welcome_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage lib99_welcome_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib99_welcome_plugin.la lib99_welcome_plugin_la_SOURCES = \ welcome-plugin.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/welcome/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/welcome/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib99_welcome_plugin.la: $(lib99_welcome_plugin_la_OBJECTS) $(lib99_welcome_plugin_la_DEPENDENCIES) $(EXTRA_lib99_welcome_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib99_welcome_plugin_la_LINK) -rpath $(moduledir) $(lib99_welcome_plugin_la_OBJECTS) $(lib99_welcome_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/welcome-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-moduleLTLIBRARIES \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/welcome/welcome-plugin.c0000644000175000017500000000740213165463624017340 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "strescape.h" #include "eacces-error.h" #include "write-full.h" #include "module-context.h" #include "mail-storage-private.h" #define WELCOME_SOCKET_TIMEOUT_SECS 30 #define WELCOME_CONTEXT(obj) \ MODULE_CONTEXT(obj, welcome_storage_module) struct welcome_mailbox { union mailbox_module_context module_ctx; bool created; }; static MODULE_CONTEXT_DEFINE_INIT(welcome_storage_module, &mail_storage_module_register); static void script_execute(struct mail_user *user, const char *cmd, bool wait) { const char *socket_path, *const *args; string_t *str; char buf[1024]; int fd, ret; if (user->mail_debug) i_debug("welcome: Executing %s (wait=%d)", cmd, wait ? 1 : 0); args = t_strsplit_spaces(cmd, " "); socket_path = args[0]; args++; if (*socket_path != '/') { socket_path = t_strconcat(user->set->base_dir, "/", socket_path, NULL); } if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) { if (errno == EACCES) { i_error("welcome: %s", eacces_error_get("net_connect_unix", socket_path)); } else { i_error("welcome: net_connect_unix(%s) failed: %m", socket_path); } return; } str = t_str_new(1024); str_append(str, "VERSION\tscript\t4\t0\n"); if (!wait) str_append(str, "noreply\n"); else str_append(str, "-\n"); for (; *args != NULL; args++) { str_append_tabescaped(str, *args); str_append_c(str, '\n'); } str_append_c(str, '\n'); alarm(WELCOME_SOCKET_TIMEOUT_SECS); net_set_nonblock(fd, FALSE); if (write_full(fd, str_data(str), str_len(str)) < 0) i_error("write(%s) failed: %m", socket_path); else if (wait) { ret = read(fd, buf, sizeof(buf)); if (ret < 0) i_error("welcome: read(%s) failed: %m", socket_path); else if (ret < 2) i_error("welcome: %s failed: Only %d bytes read", socket_path, ret); else if (buf[0] != '+') i_error("welcome: %s failed: Script returned error", socket_path); } if (close(fd) < 0) i_error("close(%s) failed: %m", socket_path); } static int welcome_create_box(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct welcome_mailbox *wbox = WELCOME_CONTEXT(box); if (wbox->module_ctx.super.create_box(box, update, directory) < 0) return -1; /* the mailbox isn't fully created here yet, so just mark it as created and wait until open() time to actually run it */ wbox->created = TRUE; return 0; } static int welcome_open_box(struct mailbox *box) { struct welcome_mailbox *wbox = WELCOME_CONTEXT(box); const char *cmd; cmd = !wbox->created ? NULL : mail_user_plugin_getenv(box->storage->user, "welcome_script"); if (cmd != NULL) { bool wait = mail_user_plugin_getenv(box->storage->user, "welcome_wait") != NULL; script_execute(box->storage->user, cmd, wait); } return wbox->module_ctx.super.open(box); } static void welcome_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct welcome_mailbox *wbox; if (!box->inbox_user) return; wbox = p_new(box->pool, struct welcome_mailbox, 1); wbox->module_ctx.super = *v; box->vlast = &wbox->module_ctx.super; v->create_box = welcome_create_box; v->open = welcome_open_box; MODULE_CONTEXT_SET(box, welcome_storage_module, wbox); } static struct mail_storage_hooks welcome_mail_storage_hooks = { .mailbox_allocated = welcome_mailbox_allocated }; void welcome_plugin_init(struct module *module); void welcome_plugin_deinit(void); void welcome_plugin_init(struct module *module) { mail_storage_hooks_add(module, &welcome_mail_storage_hooks); } void welcome_plugin_deinit(void) { mail_storage_hooks_remove(&welcome_mail_storage_hooks); } const char *welcome_plugin_version = DOVECOT_ABI_VERSION; dovecot-2.2.33.2/src/plugins/welcome/Makefile.am0000644000175000017500000000050213123174404016260 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage NOPLUGIN_LDFLAGS = lib99_welcome_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib99_welcome_plugin.la lib99_welcome_plugin_la_SOURCES = \ welcome-plugin.c dovecot-2.2.33.2/src/plugins/expire/0002755000175000017500000000000013172375613014163 500000000000000dovecot-2.2.33.2/src/plugins/expire/Makefile.in0000644000175000017500000006344413172375574016167 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/expire ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) lib10_doveadm_expire_plugin_la_LIBADD = am_lib10_doveadm_expire_plugin_la_OBJECTS = doveadm-expire.lo lib10_doveadm_expire_plugin_la_OBJECTS = \ $(am_lib10_doveadm_expire_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib10_doveadm_expire_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(lib10_doveadm_expire_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ lib20_expire_plugin_la_LIBADD = am_lib20_expire_plugin_la_OBJECTS = expire-set.lo expire-plugin.lo lib20_expire_plugin_la_OBJECTS = $(am_lib20_expire_plugin_la_OBJECTS) lib20_expire_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_expire_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib10_doveadm_expire_plugin_la_SOURCES) \ $(lib20_expire_plugin_la_SOURCES) DIST_SOURCES = $(lib10_doveadm_expire_plugin_la_SOURCES) \ $(lib20_expire_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/doveadm lib10_doveadm_expire_plugin_la_LDFLAGS = -module -avoid-version lib20_expire_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_expire_plugin.la lib20_expire_plugin_la_SOURCES = \ expire-set.c \ expire-plugin.c noinst_HEADERS = \ expire-set.h \ expire-plugin.h doveadm_module_LTLIBRARIES = \ lib10_doveadm_expire_plugin.la lib10_doveadm_expire_plugin_la_SOURCES = \ doveadm-expire.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/expire/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/expire/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib10_doveadm_expire_plugin.la: $(lib10_doveadm_expire_plugin_la_OBJECTS) $(lib10_doveadm_expire_plugin_la_DEPENDENCIES) $(EXTRA_lib10_doveadm_expire_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib10_doveadm_expire_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib10_doveadm_expire_plugin_la_OBJECTS) $(lib10_doveadm_expire_plugin_la_LIBADD) $(LIBS) lib20_expire_plugin.la: $(lib20_expire_plugin_la_OBJECTS) $(lib20_expire_plugin_la_DEPENDENCIES) $(EXTRA_lib20_expire_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_expire_plugin_la_LINK) -rpath $(moduledir) $(lib20_expire_plugin_la_OBJECTS) $(lib20_expire_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-expire.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expire-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expire-set.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-doveadm_moduleLTLIBRARIES install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/expire/expire-set.h0000644000175000017500000000044113123174404016325 00000000000000#ifndef EXPIRE_SET_H #define EXPIRE_SET_H #define DICT_EXPIRE_PREFIX DICT_PATH_SHARED"expire/" struct expire_set *expire_set_init(const char *const *patterns); void expire_set_deinit(struct expire_set **set); bool expire_set_lookup(struct expire_set *set, const char *mailbox); #endif dovecot-2.2.33.2/src/plugins/expire/expire-plugin.c0000644000175000017500000003037113165463624017043 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ /* There are several race conditions in this plugin, but they should be happening pretty rarely and usually it's not a big problem if the results are temporarily wrong. Fixing the races would likely be a lot of work, so it's not really worth it. */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "master-service.h" #include "dict.h" #include "mail-namespace.h" #include "index-mail.h" #include "index-storage.h" #include "expire-set.h" #include "expire-plugin.h" #define EXPIRE_CONTEXT(obj) \ MODULE_CONTEXT(obj, expire_storage_module) #define EXPIRE_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, expire_mail_module) #define EXPIRE_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, expire_mail_user_module) struct expire_mail_index_header { uint32_t timestamp; }; struct expire_mail_user { union mail_user_module_context module_ctx; struct dict *db; struct expire_set *set; bool expire_cache; }; struct expire_mailbox { union mailbox_module_context module_ctx; uint32_t expire_ext_id; }; struct expire_transaction_context { union mailbox_transaction_module_context module_ctx; unsigned int saves:1; unsigned int first_expunged:1; }; const char *expire_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(expire_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(expire_mail_module, &mail_module_register); static MODULE_CONTEXT_DEFINE_INIT(expire_mail_user_module, &mail_user_module_register); static struct mailbox_transaction_context * expire_mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(box); struct mailbox_transaction_context *t; struct expire_transaction_context *xt; t = xpr_box->module_ctx.super.transaction_begin(box, flags); xt = i_new(struct expire_transaction_context, 1); MODULE_CONTEXT_SET(t, expire_storage_module, xt); return t; } static void first_save_timestamp(struct mailbox *box, time_t *stamp_r) { struct mailbox_transaction_context *t; const struct mail_index_header *hdr; struct mail *mail; *stamp_r = ioloop_time; t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, 0, NULL); /* find the first non-expunged mail. we're here because the first mail was expunged, so don't bother checking it. */ hdr = mail_index_get_header(box->view); if (hdr->messages_count > 0) { mail_set_seq(mail, 1); (void)mail_get_save_date(mail, stamp_r); } mail_free(&mail); (void)mailbox_transaction_commit(&t); } static uint32_t expire_get_ext_id(struct mailbox *box) { struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(box); if (xpr_box->expire_ext_id != (uint32_t)-1) return xpr_box->expire_ext_id; xpr_box->expire_ext_id = mail_index_ext_register(box->index, "expire", sizeof(struct expire_mail_index_header), 0, 0); return xpr_box->expire_ext_id; } static int expire_lookup(struct mailbox *box, const char *key, time_t *new_stamp_r) { struct expire_mail_user *euser = EXPIRE_USER_CONTEXT(box->storage->user); const struct expire_mail_index_header *hdr; const void *data; size_t data_size; const char *value; int ret; /* default to ioloop_time for newly saved mails. it may not be exactly the first message's save time, but a few seconds difference doesn't matter */ *new_stamp_r = ioloop_time; if (euser->expire_cache) { mail_index_get_header_ext(box->view, expire_get_ext_id(box), &data, &data_size); if (data_size == sizeof(*hdr)) { hdr = data; if (hdr->timestamp == 0) return 0; /* preserve the original timestamp */ *new_stamp_r = hdr->timestamp; return 1; } /* cache doesn't exist yet */ } ret = dict_lookup(euser->db, pool_datastack_create(), key, &value); if (ret <= 0) { if (ret < 0) return -1; first_save_timestamp(box, new_stamp_r); return 0; } return strcmp(value, "0") != 0 ? 1 : 0; } static void expire_update(struct mailbox *box, const char *key, time_t timestamp) { struct expire_mail_user *euser = EXPIRE_USER_CONTEXT(box->storage->user); struct dict_transaction_context *dctx; struct mail_index_transaction *trans; struct expire_mail_index_header hdr; dctx = dict_transaction_begin(euser->db); dict_set(dctx, key, dec2str(timestamp)); if (dict_transaction_commit(&dctx) < 0) i_error("expire: dict commit failed"); else if (euser->expire_cache) { i_zero(&hdr); hdr.timestamp = timestamp; trans = mail_index_transaction_begin(box->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header_ext(trans, expire_get_ext_id(box), 0, &hdr, sizeof(hdr)); if (mail_index_transaction_commit(&trans) < 0) i_error("expire: index transaction commit failed"); } } static void first_nonexpunged_timestamp(struct mailbox_transaction_context *t, time_t *stamp_r) { struct mail_index_view *view = t->view; const struct mail_index_header *hdr; struct mail *mail; uint32_t seq; mail = mail_alloc(t, 0, NULL); /* find the first non-expunged mail. we're here because the first mail was expunged, so don't bother checking it. */ hdr = mail_index_get_header(view); for (seq = 2; seq <= hdr->messages_count; seq++) { if (!mail_index_is_expunged(view, seq)) { mail_set_seq(mail, seq); if (mail_get_save_date(mail, stamp_r) == 0) break; } } mail_free(&mail); if (seq > hdr->messages_count) { /* everything expunged */ *stamp_r = 0; } } static int expire_mailbox_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct mail_user *user = t->box->storage->user; struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box); struct expire_transaction_context *xt = EXPIRE_CONTEXT(t); struct mailbox *box = t->box; time_t new_stamp = 0; bool update_dict = FALSE; int ret; if (xt->first_expunged) { /* first mail expunged. dict needs updating. */ first_nonexpunged_timestamp(t, &new_stamp); if (new_stamp == 0 && xt->saves) { /* everything was expunged, but also within this transaction a new message was saved */ new_stamp = ioloop_time; } if (user->mail_debug) { i_debug("expire: Expunging first message in %s, " "updating timestamp to %ld", box->vname, (long)new_stamp); } update_dict = TRUE; } if (xpr_box->module_ctx.super.transaction_commit(t, changes_r) < 0) { i_free(xt); return -1; } /* transaction is freed now */ t = NULL; if (xt->first_expunged || xt->saves) T_BEGIN { const char *key; key = t_strconcat(DICT_EXPIRE_PREFIX, box->storage->user->username, "/", mailbox_get_vname(box), NULL); if (xt->first_expunged) { /* new_stamp is already set */ } else { i_assert(xt->saves); /* saved new mails. dict needs to be updated only if this is the first mail in the database */ ret = expire_lookup(box, key, &new_stamp); if (ret <= 0) { /* first time saving here with expire enabled. also handle lookup errors by just assuming it didn't exist */ if (ret < 0) { i_warning("expire: dict lookup failed, " "assuming update is needed"); } update_dict = TRUE; } else { /* already exists */ } if (user->mail_debug && update_dict) { i_debug("expire: Saving first message to %s, " "updating timestamp to %ld", box->vname, (long)new_stamp); } } if (update_dict) expire_update(box, key, new_stamp); } T_END; i_free(xt); return 0; } static void expire_mailbox_transaction_rollback(struct mailbox_transaction_context *t) { struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box); struct expire_transaction_context *xt = EXPIRE_CONTEXT(t); xpr_box->module_ctx.super.transaction_rollback(t); i_free(xt); } static void expire_mail_expunge(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; union mail_module_context *xpr_mail = EXPIRE_MAIL_CONTEXT(mail); struct expire_transaction_context *xt = EXPIRE_CONTEXT(_mail->transaction); if (_mail->seq == 1) { /* first mail expunged, database needs to be updated */ xt->first_expunged = TRUE; } xpr_mail->super.expunge(_mail); } static void expire_mail_allocated(struct mail *_mail) { struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(_mail->box); struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; union mail_module_context *xpr_mail; if (xpr_box == NULL) return; xpr_mail = p_new(mail->pool, union mail_module_context, 1); xpr_mail->super = *v; mail->vlast = &xpr_mail->super; v->expunge = expire_mail_expunge; MODULE_CONTEXT_SET_SELF(mail, expire_mail_module, xpr_mail); } static int expire_save_finish(struct mail_save_context *ctx) { struct expire_transaction_context *xt = EXPIRE_CONTEXT(ctx->transaction); struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(ctx->transaction->box); xt->saves = TRUE; return xpr_box->module_ctx.super.save_finish(ctx); } static int expire_copy(struct mail_save_context *ctx, struct mail *mail) { struct expire_transaction_context *xt = EXPIRE_CONTEXT(ctx->transaction); struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(ctx->transaction->box); xt->saves = TRUE; return xpr_box->module_ctx.super.copy(ctx, mail); } static void expire_mailbox_allocate_init(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct expire_mailbox *xpr_box; xpr_box = p_new(box->pool, struct expire_mailbox, 1); xpr_box->module_ctx.super = *v; box->vlast = &xpr_box->module_ctx.super; xpr_box->expire_ext_id = (uint32_t)-1; v->transaction_begin = expire_mailbox_transaction_begin; v->transaction_commit = expire_mailbox_transaction_commit; v->transaction_rollback = expire_mailbox_transaction_rollback; v->save_finish = expire_save_finish; v->copy = expire_copy; MODULE_CONTEXT_SET(box, expire_storage_module, xpr_box); } static void expire_mailbox_allocated(struct mailbox *box) { struct expire_mail_user *euser = EXPIRE_USER_CONTEXT(box->storage->user); if (euser != NULL && expire_set_lookup(euser->set, box->vname)) expire_mailbox_allocate_init(box); } static void expire_mail_user_deinit(struct mail_user *user) { struct expire_mail_user *euser = EXPIRE_USER_CONTEXT(user); dict_deinit(&euser->db); expire_set_deinit(&euser->set); euser->module_ctx.super.deinit(user); } static const char *const *expire_get_patterns(struct mail_user *user) { ARRAY_TYPE(const_string) patterns; const char *str; char set_name[6+MAX_INT_STRLEN+1]; unsigned int i; t_array_init(&patterns, 16); str = mail_user_set_plugin_getenv(user->set, "expire"); for (i = 2; str != NULL; i++) { array_append(&patterns, &str, 1); if (i_snprintf(set_name, sizeof(set_name), "expire%u", i) < 0) i_unreached(); str = mail_user_set_plugin_getenv(user->set, set_name); } array_append_zero(&patterns); return array_idx(&patterns, 0); } static void expire_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct expire_mail_user *euser; struct dict *db; const char *dict_uri, *error; dict_uri = mail_user_plugin_getenv(user, "expire_dict"); if (mail_user_plugin_getenv(user, "expire") == NULL) { if (user->mail_debug) i_debug("expire: No expire setting - plugin disabled"); return; } if (dict_uri == NULL) { i_error("expire plugin: expire_dict setting missing"); return; } /* we're using only shared dictionary, the username doesn't matter. */ if (dict_init(dict_uri, DICT_DATA_TYPE_UINT32, "", user->set->base_dir, &db, &error) < 0) { i_error("expire plugin: dict_init(%s) failed: %s", dict_uri, error); return; } euser = p_new(user->pool, struct expire_mail_user, 1); euser->module_ctx.super = *v; user->vlast = &euser->module_ctx.super; v->deinit = expire_mail_user_deinit; euser->db = db; euser->set = expire_set_init(expire_get_patterns(user)); euser->expire_cache = mail_user_plugin_getenv(user, "expire_cache") != NULL; MODULE_CONTEXT_SET(user, expire_mail_user_module, euser); } static struct mail_storage_hooks expire_mail_storage_hooks = { .mail_user_created = expire_mail_user_created, .mailbox_allocated = expire_mailbox_allocated, .mail_allocated = expire_mail_allocated }; void expire_plugin_init(struct module *module) { mail_storage_hooks_add(module, &expire_mail_storage_hooks); } void expire_plugin_deinit(void) { mail_storage_hooks_remove(&expire_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/expire/doveadm-expire.c0000644000175000017500000003011613165463624017161 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "str.h" #include "hash.h" #include "dict.h" #include "imap-match.h" #include "expire-set.h" #include "mail-search.h" #include "doveadm-settings.h" #include "doveadm-mail.h" #define DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(obj) \ MODULE_CONTEXT(obj, doveadm_expire_mail_cmd_module) enum expire_user_state { EXPIRE_USER_STATE_NONEXISTENT = 0, EXPIRE_USER_STATE_EXISTS = 1, EXPIRE_USER_STATE_SEEN = 2 }; struct expire_query { const char *mailbox; struct imap_match_glob *glob; time_t before_time; }; struct doveadm_expire_mail_cmd_context { union doveadm_mail_cmd_module_context module_ctx; struct dict *dict; struct dict_transaction_context *trans; struct dict_iterate_context *iter; /* username => enum expire_user_state */ HASH_TABLE(char *, void *) user_states; ARRAY(struct expire_query) queries; time_t oldest_before_time; bool delete_nonexistent_users; }; const char *doveadm_expire_plugin_version = DOVECOT_ABI_VERSION; void doveadm_expire_plugin_init(struct module *module); void doveadm_expire_plugin_deinit(void); static MODULE_CONTEXT_DEFINE_INIT(doveadm_expire_mail_cmd_module, &doveadm_mail_cmd_module_register); static void (*next_hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx); static bool doveadm_expire_mail_match_mailbox(struct doveadm_expire_mail_cmd_context *ectx, const char *mailbox, time_t oldest_savedate) { const struct expire_query *query; array_foreach(&ectx->queries, query) { if (oldest_savedate >= query->before_time) continue; if (query->glob == NULL) { if (strcmp(query->mailbox, mailbox) == 0) return TRUE; } else { if (imap_match(query->glob, mailbox) == IMAP_MATCH_YES) return TRUE; } } return FALSE; } static int doveadm_expire_mail_want(struct doveadm_mail_cmd_context *ctx, const char *dict_key, time_t oldest_savedate, const char **username_r) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); const char *username, *mailbox; enum expire_user_state state; char *orig_username; void *value; /* dict_key = DICT_EXPIRE_PREFIX/ */ username = dict_key + strlen(DICT_EXPIRE_PREFIX); mailbox = strchr(username, '/'); if (mailbox == NULL) { /* invalid record, ignore */ i_error("expire: Invalid key: %s", dict_key); return -1; } username = t_strdup_until(username, mailbox++); if (!hash_table_lookup_full(ectx->user_states, username, &orig_username, &value)) { /* user no longer exists, delete the record */ return -1; } state = POINTER_CAST_TO(value, enum expire_user_state); switch (state) { case EXPIRE_USER_STATE_NONEXISTENT: i_unreached(); case EXPIRE_USER_STATE_EXISTS: break; case EXPIRE_USER_STATE_SEEN: /* seen this user already, skip the record */ return 0; } if (!doveadm_expire_mail_match_mailbox(ectx, mailbox, oldest_savedate)) { /* this mailbox doesn't have any matching messages */ return 0; } state = EXPIRE_USER_STATE_SEEN; hash_table_update(ectx->user_states, orig_username, POINTER_CAST(state)); *username_r = orig_username; return 1; } static int doveadm_expire_mail_cmd_get_next_user(struct doveadm_mail_cmd_context *ctx, const char **username_r) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); const char *key, *value; unsigned long oldest_savedate; int ret; while (dict_iterate(ectx->iter, &key, &value)) { if (str_to_ulong(value, &oldest_savedate) < 0) { /* invalid record */ i_error("expire: Invalid timestamp: %s", value); continue; } if ((time_t)oldest_savedate > ectx->oldest_before_time) { if (doveadm_debug) { i_debug("expire: Stopping iteration on key %s " "(%lu > %ld)", key, oldest_savedate, (long)ectx->oldest_before_time); } break; } T_BEGIN { ret = doveadm_expire_mail_want(ctx, key, oldest_savedate, username_r); } T_END; if (ret > 0) return TRUE; if (ret < 0 && ectx->delete_nonexistent_users) { /* user has been deleted */ dict_unset(ectx->trans, key); } } /* finished */ if (dict_iterate_deinit(&ectx->iter) < 0) { i_error("Dictionary iteration failed"); return -1; } return 0; } static const char *const *doveadm_expire_get_patterns(void) { ARRAY_TYPE(const_string) patterns; const char *str; char set_name[6+MAX_INT_STRLEN+1]; unsigned int i; t_array_init(&patterns, 16); str = doveadm_plugin_getenv("expire"); for (i = 2; str != NULL; i++) { array_append(&patterns, &str, 1); if (i_snprintf(set_name, sizeof(set_name), "expire%u", i) < 0) i_unreached(); str = doveadm_plugin_getenv(set_name); } array_append_zero(&patterns); return array_idx(&patterns, 0); } static bool doveadm_expire_get_or_mailboxes(struct doveadm_mail_cmd_context *ctx, const struct mail_search_arg *args, struct expire_query query) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); const struct mail_search_arg *arg; unsigned int query_count; query.mailbox = NULL; query_count = array_count(&ectx->queries); for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_MAILBOX_GLOB: query.glob = imap_match_init(ctx->pool, arg->value.str, TRUE, '/'); /* fall through */ case SEARCH_MAILBOX: /* require mailbox to be in expire patterns */ query.mailbox = p_strdup(ctx->pool, arg->value.str); array_append(&ectx->queries, &query, 1); break; default: /* there are something else besides mailboxes, can't optimize this. */ array_delete(&ectx->queries, query_count, array_count(&ectx->queries) - query_count); return FALSE; } } return query.mailbox != NULL; } static bool doveadm_expire_analyze_and_query(struct doveadm_mail_cmd_context *ctx, const struct mail_search_arg *args) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); const struct mail_search_arg *arg; struct expire_query query; bool have_or = FALSE; i_zero(&query); query.before_time = (time_t)-1; for (arg = args; arg != NULL; arg = arg->next) { switch (arg->type) { case SEARCH_OR: have_or = TRUE; break; case SEARCH_MAILBOX_GLOB: query.glob = imap_match_init(ctx->pool, arg->value.str, TRUE, '/'); /* fall through */ case SEARCH_MAILBOX: /* require mailbox to be in expire patterns */ query.mailbox = p_strdup(ctx->pool, arg->value.str); break; case SEARCH_BEFORE: if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SAVED) break; if ((arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) break; query.before_time = arg->value.time; break; default: break; } } if (query.before_time == (time_t)-1) { /* no SAVEDBEFORE, can't optimize */ return FALSE; } if (query.mailbox != NULL) { /* one mailbox */ array_append(&ectx->queries, &query, 1); return TRUE; } /* no MAILBOX, but check if one of the ORs lists mailboxes */ if (!have_or) return FALSE; for (arg = args; arg != NULL; arg = arg->next) { if (arg->type == SEARCH_OR && doveadm_expire_get_or_mailboxes(ctx, arg->value.subargs, query)) return TRUE; } return FALSE; } static time_t doveadm_expire_analyze_or_query(struct doveadm_mail_cmd_context *ctx, const struct mail_search_arg *args) { const struct mail_search_arg *arg; /* all of the subqueries must have mailbox and savedbefore */ for (arg = args; arg != NULL; arg = arg->next) { if (arg->type != SEARCH_SUB) return FALSE; if (!doveadm_expire_analyze_and_query(ctx, arg->value.subargs)) return FALSE; } return TRUE; } static bool doveadm_expire_analyze_query(struct doveadm_mail_cmd_context *ctx) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); struct mail_search_arg *args = ctx->search_args->args; struct expire_set *set; const struct expire_query *queries; unsigned int i, count; i_assert(args != NULL); /* we support two kinds of queries: 1) mailbox-pattern savedbefore ... 2) or 2*(mailbox-pattern savedbefore ...) mailbox-pattern can be: a) mailbox b) or 2*(mailbox ) */ p_array_init(&ectx->queries, ctx->pool, 8); if (!doveadm_expire_analyze_and_query(ctx, args) && (args->type != SEARCH_OR || args->next != NULL || !doveadm_expire_analyze_or_query(ctx, args->value.subargs))) { if (doveadm_debug) i_debug("expire: Couldn't optimize search query"); return FALSE; } /* make sure all mailboxes match expire patterns */ set = expire_set_init(doveadm_expire_get_patterns()); queries = array_get(&ectx->queries, &count); for (i = 0; i < count; i++) { if (!expire_set_lookup(set, queries[i].mailbox)) { if (doveadm_debug) { i_debug("expire: Couldn't optimize search query: " "mailbox %s not in expire database", queries[i].mailbox); } break; } } expire_set_deinit(&set); return i == count; } static void doveadm_expire_mail_cmd_deinit(struct doveadm_mail_cmd_context *ctx) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); if (ectx->iter != NULL) { if (dict_iterate_deinit(&ectx->iter) < 0) i_error("expire: Dictionary iteration failed"); } if (dict_transaction_commit(&ectx->trans) < 0) i_error("expire: Dictionary commit failed"); dict_deinit(&ectx->dict); hash_table_destroy(&ectx->user_states); ectx->module_ctx.super.deinit(ctx); } static void doveadm_expire_mail_init(struct doveadm_mail_cmd_context *ctx) { struct doveadm_expire_mail_cmd_context *ectx; struct dict *dict; const struct expire_query *query; const char *expire_dict, *username, *value, *error; char *username_dup; enum expire_user_state state; if (ctx->search_args == NULL) return; expire_dict = doveadm_plugin_getenv("expire_dict"); if (expire_dict == NULL) return; /* doveadm proxying uses expire database only locally. the remote doveadm handles each user one at a time (even though iterate_single_user=FALSE) */ if (ctx->iterate_single_user || ctx->proxying) { if (doveadm_debug) { i_debug("expire: Iterating only a single user, " "ignoring expire database"); } return; } ectx = p_new(ctx->pool, struct doveadm_expire_mail_cmd_context, 1); ectx->module_ctx.super = ctx->v; value = doveadm_plugin_getenv("expire_keep_nonexistent_users"); ectx->delete_nonexistent_users = value == NULL || strcmp(value, "yes") != 0; MODULE_CONTEXT_SET(ctx, doveadm_expire_mail_cmd_module, ectx); /* we can potentially optimize this query. see if the search args are valid for optimization. */ if (!doveadm_expire_analyze_query(ctx)) return; if (doveadm_debug) i_debug("expire: Searching only users listed in expire database"); if (dict_init(expire_dict, DICT_DATA_TYPE_UINT32, "", doveadm_settings->base_dir, &dict, &error) < 0) { i_error("dict_init(%s) failed, not using it: %s", expire_dict, error); return; } ectx->oldest_before_time = (time_t)-1; array_foreach(&ectx->queries, query) { if (ectx->oldest_before_time > query->before_time || ectx->oldest_before_time == (time_t)-1) ectx->oldest_before_time = query->before_time; } ctx->v.deinit = doveadm_expire_mail_cmd_deinit; ctx->v.get_next_user = doveadm_expire_mail_cmd_get_next_user; hash_table_create(&ectx->user_states, ctx->pool, 0, str_hash, strcmp); while (mail_storage_service_all_next(ctx->storage_service, &username) > 0) { username_dup = p_strdup(ctx->pool, username); state = EXPIRE_USER_STATE_EXISTS; hash_table_insert(ectx->user_states, username_dup, POINTER_CAST(state)); } ectx->dict = dict; ectx->trans = dict_transaction_begin(dict); ectx->iter = dict_iterate_init(dict, DICT_EXPIRE_PREFIX, DICT_ITERATE_FLAG_RECURSE | DICT_ITERATE_FLAG_SORT_BY_VALUE); } void doveadm_expire_plugin_init(struct module *module ATTR_UNUSED) { next_hook_doveadm_mail_init = hook_doveadm_mail_init; hook_doveadm_mail_init = doveadm_expire_mail_init; } void doveadm_expire_plugin_deinit(void) { i_assert(hook_doveadm_mail_init == doveadm_expire_mail_init); hook_doveadm_mail_init = next_hook_doveadm_mail_init; } dovecot-2.2.33.2/src/plugins/expire/expire-set.c0000644000175000017500000000211513123174404016320 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "imap-match.h" #include "expire-set.h" struct expire_set { pool_t pool; ARRAY(struct imap_match_glob *) globs; }; struct expire_set *expire_set_init(const char *const *patterns) { struct expire_set *set; struct imap_match_glob *glob; const char *const *pattern; pool_t pool; pool = pool_alloconly_create("Expire pool", 512); set = p_new(pool, struct expire_set, 1); set->pool = pool; p_array_init(&set->globs, set->pool, 16); for (pattern = patterns; *pattern != NULL; pattern++) { glob = imap_match_init(set->pool, *pattern, TRUE, '/'); array_append(&set->globs, &glob, 1); } return set; } void expire_set_deinit(struct expire_set **_set) { struct expire_set *set = *_set; *_set = NULL; pool_unref(&set->pool); } bool expire_set_lookup(struct expire_set *set, const char *mailbox) { struct imap_match_glob *const *globp; array_foreach(&set->globs, globp) { if (imap_match(*globp, mailbox) == IMAP_MATCH_YES) return TRUE; } return FALSE; } dovecot-2.2.33.2/src/plugins/expire/expire-plugin.h0000644000175000017500000000021213123174404017024 00000000000000#ifndef EXPIRE_PLUGIN_H #define EXPIRE_PLUGIN_H void expire_plugin_init(struct module *module); void expire_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/expire/Makefile.am0000644000175000017500000000153113123174404016124 00000000000000doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/doveadm NOPLUGIN_LDFLAGS = lib10_doveadm_expire_plugin_la_LDFLAGS = -module -avoid-version lib20_expire_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_expire_plugin.la lib20_expire_plugin_la_SOURCES = \ expire-set.c \ expire-plugin.c noinst_HEADERS = \ expire-set.h \ expire-plugin.h doveadm_module_LTLIBRARIES = \ lib10_doveadm_expire_plugin.la lib10_doveadm_expire_plugin_la_SOURCES = \ doveadm-expire.c dovecot-2.2.33.2/src/plugins/imap-acl/0002755000175000017500000000000013172375613014352 500000000000000dovecot-2.2.33.2/src/plugins/imap-acl/Makefile.in0000644000175000017500000005605013172375574016351 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/imap-acl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(imap_moduledir)" LTLIBRARIES = $(imap_module_LTLIBRARIES) @DOVECOT_PLUGIN_DEPS_TRUE@lib02_imap_acl_plugin_la_DEPENDENCIES = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../acl/lib01_acl_plugin.la am_lib02_imap_acl_plugin_la_OBJECTS = imap-acl-plugin.lo lib02_imap_acl_plugin_la_OBJECTS = \ $(am_lib02_imap_acl_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib02_imap_acl_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib02_imap_acl_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib02_imap_acl_plugin_la_SOURCES) DIST_SOURCES = $(lib02_imap_acl_plugin_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/acl imap_moduledir = $(moduledir) lib02_imap_acl_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib02_imap_acl_plugin.la @DOVECOT_PLUGIN_DEPS_TRUE@lib02_imap_acl_plugin_la_LIBADD = \ @DOVECOT_PLUGIN_DEPS_TRUE@ ../acl/lib01_acl_plugin.la lib02_imap_acl_plugin_la_SOURCES = \ imap-acl-plugin.c noinst_HEADERS = \ imap-acl-plugin.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/imap-acl/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/imap-acl/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ } uninstall-imap_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ done clean-imap_moduleLTLIBRARIES: -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) @list='$(imap_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib02_imap_acl_plugin.la: $(lib02_imap_acl_plugin_la_OBJECTS) $(lib02_imap_acl_plugin_la_DEPENDENCIES) $(EXTRA_lib02_imap_acl_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib02_imap_acl_plugin_la_LINK) -rpath $(imap_moduledir) $(lib02_imap_acl_plugin_la_OBJECTS) $(lib02_imap_acl_plugin_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-acl-plugin.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(imap_moduledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-imap_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-imap_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-imap_moduleLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-imap_moduleLTLIBRARIES install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-imap_moduleLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/imap-acl/imap-acl-plugin.c0000644000175000017500000004556113165463624017430 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "imap-quote.h" #include "imap-resp-code.h" #include "imap-commands.h" #include "mail-storage.h" #include "mail-namespace.h" #include "acl-api.h" #include "acl-storage.h" #include "acl-plugin.h" #include "imap-acl-plugin.h" #define ERROR_NOT_ADMIN "["IMAP_RESP_CODE_NOPERM"] " \ "You lack administrator privileges on this mailbox." #define IMAP_ACL_ANYONE "anyone" #define IMAP_ACL_AUTHENTICATED "authenticated" #define IMAP_ACL_OWNER "owner" #define IMAP_ACL_GROUP_PREFIX "$" #define IMAP_ACL_GROUP_OVERRIDE_PREFIX "!$" #define IMAP_ACL_GLOBAL_PREFIX "#" struct imap_acl_letter_map { char letter; const char *name; }; static const struct imap_acl_letter_map imap_acl_letter_map[] = { { 'l', MAIL_ACL_LOOKUP }, { 'r', MAIL_ACL_READ }, { 'w', MAIL_ACL_WRITE }, { 's', MAIL_ACL_WRITE_SEEN }, { 't', MAIL_ACL_WRITE_DELETED }, { 'i', MAIL_ACL_INSERT }, { 'p', MAIL_ACL_POST }, { 'e', MAIL_ACL_EXPUNGE }, { 'k', MAIL_ACL_CREATE }, { 'x', MAIL_ACL_DELETE }, { 'a', MAIL_ACL_ADMIN }, { '\0', NULL } }; const char *imap_acl_plugin_version = DOVECOT_ABI_VERSION; static struct module *imap_acl_module; static imap_client_created_func_t *next_hook_client_created; static struct mailbox * acl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name) { struct mail_namespace *ns; struct mailbox *box; enum mailbox_existence existence = MAILBOX_EXISTENCE_NONE; int ret; if (ACL_USER_CONTEXT(cmd->client->user) == NULL) { client_send_command_error(cmd, "ACLs disabled."); return NULL; } ns = client_find_namespace(cmd, &name); if (ns == NULL) return NULL; /* Force opening the mailbox so that we can give a nicer error message if mailbox isn't selectable but is listable. */ box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); if (mailbox_exists(box, TRUE, &existence) == 0 && existence == MAILBOX_EXISTENCE_SELECT) { ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN); if (ret > 0) return box; } /* mailbox doesn't exist / not an administrator. */ if (existence != MAILBOX_EXISTENCE_SELECT || acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP) <= 0) { client_send_tagline(cmd, t_strdup_printf( "NO ["IMAP_RESP_CODE_NONEXISTENT"] " MAIL_ERRSTR_MAILBOX_NOT_FOUND, name)); } else { client_send_tagline(cmd, "NO "ERROR_NOT_ADMIN); } mailbox_free(&box); return NULL; } static const struct imap_acl_letter_map * imap_acl_letter_map_find(const char *name) { unsigned int i; for (i = 0; imap_acl_letter_map[i].name != NULL; i++) { if (strcmp(imap_acl_letter_map[i].name, name) == 0) return &imap_acl_letter_map[i]; } return NULL; } static void imap_acl_write_rights_list(string_t *dest, const char *const *rights) { const struct imap_acl_letter_map *map; unsigned int i; size_t orig_len = str_len(dest); bool append_c = FALSE, append_d = FALSE; for (i = 0; rights[i] != NULL; i++) { /* write only letters */ map = imap_acl_letter_map_find(rights[i]); if (map != NULL) { str_append_c(dest, map->letter); if (map->letter == 'k' || map->letter == 'x') append_c = TRUE; if (map->letter == 't' || map->letter == 'e') append_d = TRUE; } } if (append_c) str_append_c(dest, 'c'); if (append_d) str_append_c(dest, 'd'); if (orig_len == str_len(dest)) str_append(dest, "\"\""); } static void imap_acl_write_right(string_t *dest, string_t *tmp, const struct acl_rights *right, bool neg) { const char *const *rights = neg ? right->neg_rights : right->rights; str_truncate(tmp, 0); if (neg) str_append_c(tmp,'-'); if (right->global) str_append(tmp, IMAP_ACL_GLOBAL_PREFIX); switch (right->id_type) { case ACL_ID_ANYONE: str_append(tmp, IMAP_ACL_ANYONE); break; case ACL_ID_AUTHENTICATED: str_append(tmp, IMAP_ACL_AUTHENTICATED); break; case ACL_ID_OWNER: str_append(tmp, IMAP_ACL_OWNER); break; case ACL_ID_USER: str_append(tmp, right->identifier); break; case ACL_ID_GROUP: str_append(tmp, IMAP_ACL_GROUP_PREFIX); str_append(tmp, right->identifier); break; case ACL_ID_GROUP_OVERRIDE: str_append(tmp, IMAP_ACL_GROUP_OVERRIDE_PREFIX); str_append(tmp, right->identifier); break; case ACL_ID_TYPE_COUNT: i_unreached(); } imap_append_astring(dest, str_c(tmp)); str_append_c(dest, ' '); imap_acl_write_rights_list(dest, rights); } static bool acl_rights_is_owner(struct acl_backend *backend, const struct acl_rights *rights) { switch (rights->id_type) { case ACL_ID_OWNER: return TRUE; case ACL_ID_USER: return acl_backend_user_name_equals(backend, rights->identifier); default: return FALSE; } } static bool have_positive_owner_rights(struct acl_backend *backend, struct acl_object *aclobj) { struct acl_object_list_iter *iter; struct acl_rights rights; bool ret = FALSE; iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (acl_rights_is_owner(backend, &rights)) { if (rights.rights != NULL) { ret = TRUE; break; } } } acl_object_list_deinit(&iter); return ret; } static int imap_acl_write_aclobj(string_t *dest, struct acl_backend *backend, struct acl_object *aclobj, bool convert_owner, bool add_default) { struct acl_object_list_iter *iter; struct acl_rights rights; string_t *tmp; const char *username; size_t orig_len = str_len(dest); bool seen_owner = FALSE, seen_positive_owner = FALSE; int ret; username = acl_backend_get_acl_username(backend); if (username == NULL) convert_owner = FALSE; tmp = t_str_new(128); iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (acl_rights_is_owner(backend, &rights)) { if (rights.id_type == ACL_ID_OWNER && convert_owner) { rights.id_type = ACL_ID_USER; rights.identifier = username; } if (seen_owner && convert_owner) { /* oops, we have both owner and user=myself. can't do the conversion, so try again. */ str_truncate(dest, orig_len); return imap_acl_write_aclobj(dest, backend, aclobj, FALSE, add_default); } seen_owner = TRUE; if (rights.rights != NULL) seen_positive_owner = TRUE; } if (rights.rights != NULL) { str_append_c(dest, ' '); imap_acl_write_right(dest, tmp, &rights, FALSE); } if (rights.neg_rights != NULL) { str_append_c(dest, ' '); imap_acl_write_right(dest, tmp, &rights, TRUE); } } acl_object_list_deinit(&iter); if (!seen_positive_owner && username != NULL && add_default) { /* no positive owner rights returned, write default ACLs */ i_zero(&rights); if (!convert_owner) { rights.id_type = ACL_ID_OWNER; } else { rights.id_type = ACL_ID_USER; rights.identifier = username; } rights.rights = acl_object_get_default_rights(aclobj); if (rights.rights != NULL) { str_append_c(dest, ' '); imap_acl_write_right(dest, tmp, &rights, FALSE); } } return ret; } static bool cmd_getacl(struct client_command_context *cmd) { struct acl_backend *backend; struct mail_namespace *ns; struct mailbox *box; const char *mailbox; string_t *str; int ret; if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; box = acl_mailbox_open_as_admin(cmd, mailbox); if (box == NULL) return TRUE; str = t_str_new(128); str_append(str, "* ACL "); imap_append_astring(str, mailbox); ns = mailbox_get_namespace(box); backend = acl_mailbox_list_get_backend(ns->list); ret = imap_acl_write_aclobj(str, backend, acl_mailbox_get_aclobj(box), TRUE, ns->type == MAIL_NAMESPACE_TYPE_PRIVATE); if (ret == 0) { client_send_line(cmd->client, str_c(str)); client_send_tagline(cmd, "OK Getacl completed."); } else { client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG); } mailbox_free(&box); return TRUE; } static bool cmd_myrights(struct client_command_context *cmd) { struct mail_namespace *ns; struct mailbox *box; const char *mailbox, *orig_mailbox; const char *const *rights; string_t *str; if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; if (ACL_USER_CONTEXT(cmd->client->user) == NULL) { client_send_command_error(cmd, "ACLs disabled."); return TRUE; } ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS); if (acl_object_get_my_rights(acl_mailbox_get_aclobj(box), pool_datastack_create(), &rights) < 0) { client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG); mailbox_free(&box); return TRUE; } /* Post right alone doesn't give permissions to see if the mailbox exists or not. Only mail deliveries care about that. */ if (*rights == NULL || (strcmp(*rights, MAIL_ACL_POST) == 0 && rights[1] == NULL)) { client_send_tagline(cmd, t_strdup_printf( "NO ["IMAP_RESP_CODE_NONEXISTENT"] " MAIL_ERRSTR_MAILBOX_NOT_FOUND, mailbox)); mailbox_free(&box); return TRUE; } str = t_str_new(128); str_append(str, "* MYRIGHTS "); imap_append_astring(str, orig_mailbox); str_append_c(str,' '); imap_acl_write_rights_list(str, rights); client_send_line(cmd->client, str_c(str)); client_send_tagline(cmd, "OK Myrights completed."); mailbox_free(&box); return TRUE; } static bool cmd_listrights(struct client_command_context *cmd) { struct mailbox *box; const char *mailbox, *identifier; string_t *str; if (!client_read_string_args(cmd, 2, &mailbox, &identifier)) return FALSE; box = acl_mailbox_open_as_admin(cmd, mailbox); if (box == NULL) return TRUE; str = t_str_new(128); str_append(str, "* LISTRIGHTS "); imap_append_astring(str, mailbox); str_append_c(str, ' '); imap_append_astring(str, identifier); str_append_c(str, ' '); str_append(str, "\"\" l r w s t p i e k x a c d"); client_send_line(cmd->client, str_c(str)); client_send_tagline(cmd, "OK Listrights completed."); mailbox_free(&box); return TRUE; } static int imap_acl_letters_parse(const char *letters, const char *const **rights_r, const char **error_r) { static const char *acl_k = MAIL_ACL_CREATE; static const char *acl_x = MAIL_ACL_DELETE; static const char *acl_e = MAIL_ACL_EXPUNGE; static const char *acl_t = MAIL_ACL_WRITE_DELETED; ARRAY_TYPE(const_string) rights; unsigned int i; t_array_init(&rights, 64); for (; *letters != '\0'; letters++) { for (i = 0; imap_acl_letter_map[i].name != NULL; i++) { if (imap_acl_letter_map[i].letter == *letters) { array_append(&rights, &imap_acl_letter_map[i].name, 1); break; } } if (imap_acl_letter_map[i].name == NULL) { /* Handling of obsolete rights as virtual rights according to RFC 4314 */ switch (*letters) { case 'c': array_append(&rights, &acl_k, 1); array_append(&rights, &acl_x, 1); break; case 'd': array_append(&rights, &acl_e, 1); array_append(&rights, &acl_t, 1); break; default: *error_r = t_strdup_printf( "Invalid ACL right: %c", *letters); return -1; } } } array_append_zero(&rights); *rights_r = array_idx(&rights, 0); return 0; } static bool acl_anyone_allow(struct mail_user *user) { const char *env; env = mail_user_plugin_getenv(user, "acl_anyone"); return env != NULL && strcmp(env, "allow") == 0; } static int imap_acl_identifier_parse(struct client_command_context *cmd, const char *id, struct acl_rights *rights, bool check_anyone, const char **error_r) { struct mail_user *user = cmd->client->user; if (strncmp(id, IMAP_ACL_GLOBAL_PREFIX, strlen(IMAP_ACL_GLOBAL_PREFIX)) == 0) { *error_r = t_strdup_printf("Global ACLs can't be modified: %s", id); return -1; } if (strcmp(id, IMAP_ACL_ANYONE) == 0) { if (check_anyone && !acl_anyone_allow(user)) { *error_r = "'anyone' identifier is disallowed"; return -1; } rights->id_type = ACL_ID_ANYONE; } else if (strcmp(id, IMAP_ACL_AUTHENTICATED) == 0) { if (check_anyone && !acl_anyone_allow(user)) { *error_r = "'authenticated' identifier is disallowed"; return -1; } rights->id_type = ACL_ID_AUTHENTICATED; } else if (strcmp(id, IMAP_ACL_OWNER) == 0) rights->id_type = ACL_ID_OWNER; else if (strncmp(id, IMAP_ACL_GROUP_PREFIX, strlen(IMAP_ACL_GROUP_PREFIX)) == 0) { rights->id_type = ACL_ID_GROUP; rights->identifier = id + strlen(IMAP_ACL_GROUP_PREFIX); } else if (strncmp(id, IMAP_ACL_GROUP_OVERRIDE_PREFIX, strlen(IMAP_ACL_GROUP_OVERRIDE_PREFIX)) == 0) { rights->id_type = ACL_ID_GROUP_OVERRIDE; rights->identifier = id + strlen(IMAP_ACL_GROUP_OVERRIDE_PREFIX); } else { rights->id_type = ACL_ID_USER; rights->identifier = id; } return 0; } static void imap_acl_update_ensure_keep_admins(struct acl_backend *backend, struct acl_object *aclobj, struct acl_rights_update *update) { static const char *acl_admin = MAIL_ACL_ADMIN; const char *const *rights = update->rights.rights; const char *const *default_rights; ARRAY_TYPE(const_string) new_rights; unsigned int i; t_array_init(&new_rights, 64); for (i = 0; rights[i] != NULL; i++) { if (strcmp(rights[i], MAIL_ACL_ADMIN) == 0) break; array_append(&new_rights, &rights[i], 1); } switch (update->modify_mode) { case ACL_MODIFY_MODE_ADD: if (have_positive_owner_rights(backend, aclobj)) return; /* adding initial rights for a user. we need to add the defaults also. don't worry about duplicates. */ for (; rights[i] != NULL; i++) array_append(&new_rights, &rights[i], 1); default_rights = acl_object_get_default_rights(aclobj); for (i = 0; default_rights[i] != NULL; i++) array_append(&new_rights, &default_rights[i], 1); break; case ACL_MODIFY_MODE_REMOVE: if (rights[i] == NULL) return; /* skip over the ADMIN removal and add the rest */ for (i++; rights[i] != NULL; i++) array_append(&new_rights, &rights[i], 1); break; case ACL_MODIFY_MODE_REPLACE: if (rights[i] != NULL) return; /* add the missing ADMIN right */ array_append(&new_rights, &acl_admin, 1); break; default: return; } array_append_zero(&new_rights); update->rights.rights = array_idx(&new_rights, 0); } static int cmd_acl_mailbox_update(struct mailbox *box, const struct acl_rights_update *update, const char **error_r) { struct mailbox_transaction_context *t; int ret; if (mailbox_open(box) < 0) { *error_r = mailbox_get_last_error(box, NULL); return -1; } t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); ret = acl_mailbox_update_acl(t, update); if (mailbox_transaction_commit(&t) < 0) ret = -1; *error_r = MAIL_ERRSTR_CRITICAL_MSG; return ret; } static bool cmd_setacl(struct client_command_context *cmd) { struct mail_namespace *ns; struct mailbox *box; struct acl_backend *backend; struct acl_object *aclobj; struct acl_rights_update update; struct acl_rights *r; const char *mailbox, *identifier, *rights, *error; bool negative = FALSE; if (!client_read_string_args(cmd, 3, &mailbox, &identifier, &rights)) return FALSE; if (*identifier == '\0') { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } i_zero(&update); if (*identifier == '-') { negative = TRUE; identifier++; } switch (*rights) { case '-': update.modify_mode = ACL_MODIFY_MODE_REMOVE; rights++; break; case '+': update.modify_mode = ACL_MODIFY_MODE_ADD; rights++; break; default: update.modify_mode = ACL_MODIFY_MODE_REPLACE; break; } if (imap_acl_identifier_parse(cmd, identifier, &update.rights, TRUE, &error) < 0) { client_send_command_error(cmd, error); return TRUE; } if (imap_acl_letters_parse(rights, &update.rights.rights, &error) < 0) { client_send_command_error(cmd, error); return TRUE; } r = &update.rights; box = acl_mailbox_open_as_admin(cmd, mailbox); if (box == NULL) return TRUE; ns = mailbox_get_namespace(box); backend = acl_mailbox_list_get_backend(ns->list); if (ns->type == MAIL_NAMESPACE_TYPE_PUBLIC && r->id_type == ACL_ID_OWNER) { client_send_tagline(cmd, "NO Public namespaces have no owner"); mailbox_free(&box); return TRUE; } aclobj = acl_mailbox_get_aclobj(box); if (negative) { update.neg_modify_mode = update.modify_mode; update.modify_mode = ACL_MODIFY_MODE_REMOVE; update.rights.neg_rights = update.rights.rights; update.rights.rights = NULL; } else if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE && r->rights != NULL && ((r->id_type == ACL_ID_USER && acl_backend_user_name_equals(backend, r->identifier)) || (r->id_type == ACL_ID_OWNER && strcmp(acl_backend_get_acl_username(backend), ns->user->username) == 0))) { /* make sure client doesn't (accidentally) remove admin privileges from its own mailboxes */ imap_acl_update_ensure_keep_admins(backend, aclobj, &update); } if (cmd_acl_mailbox_update(box, &update, &error) < 0) client_send_tagline(cmd, t_strdup_printf("NO %s", error)); else client_send_tagline(cmd, "OK Setacl complete."); mailbox_free(&box); return TRUE; } static bool cmd_deleteacl(struct client_command_context *cmd) { struct mailbox *box; struct acl_rights_update update; const char *mailbox, *identifier, *error; if (!client_read_string_args(cmd, 2, &mailbox, &identifier)) return FALSE; if (*identifier == '\0') { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } i_zero(&update); if (*identifier != '-') update.modify_mode = ACL_MODIFY_MODE_CLEAR; else { update.neg_modify_mode = ACL_MODIFY_MODE_CLEAR; identifier++; } if (imap_acl_identifier_parse(cmd, identifier, &update.rights, FALSE, &error) < 0) { client_send_command_error(cmd, error); return TRUE; } box = acl_mailbox_open_as_admin(cmd, mailbox); if (box == NULL) return TRUE; if (cmd_acl_mailbox_update(box, &update, &error) < 0) client_send_tagline(cmd, t_strdup_printf("NO %s", error)); else client_send_tagline(cmd, "OK Deleteacl complete."); mailbox_free(&box); return TRUE; } static void imap_acl_client_created(struct client **client) { if (mail_user_is_plugin_loaded((*client)->user, imap_acl_module)) { client_add_capability(*client, "ACL"); client_add_capability(*client, "RIGHTS=texk"); } if (next_hook_client_created != NULL) next_hook_client_created(client); } void imap_acl_plugin_init(struct module *module) { command_register("LISTRIGHTS", cmd_listrights, 0); command_register("GETACL", cmd_getacl, 0); command_register("MYRIGHTS", cmd_myrights, 0); command_register("SETACL", cmd_setacl, 0); command_register("DELETEACL", cmd_deleteacl, 0); imap_acl_module = module; next_hook_client_created = imap_client_created_hook_set(imap_acl_client_created); } void imap_acl_plugin_deinit(void) { command_unregister("GETACL"); command_unregister("MYRIGHTS"); command_unregister("SETACL"); command_unregister("DELETEACL"); command_unregister("LISTRIGHTS"); imap_client_created_hook_set(next_hook_client_created); } const char *imap_acl_plugin_dependencies[] = { "acl", NULL }; const char imap_acl_plugin_binary_dependency[] = "imap"; dovecot-2.2.33.2/src/plugins/imap-acl/imap-acl-plugin.h0000644000175000017500000000037513123174404017414 00000000000000#ifndef IMAP_ACL_PLUGIN_H #define IMAP_ACL_PLUGIN_H extern const char *imap_acl_plugin_dependencies[]; extern const char imap_acl_plugin_binary_dependency[]; void imap_acl_plugin_init(struct module *module); void imap_acl_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/imap-acl/Makefile.am0000644000175000017500000000111613123174404016312 00000000000000AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/imap \ -I$(top_srcdir)/src/plugins/acl imap_moduledir = $(moduledir) NOPLUGIN_LDFLAGS = lib02_imap_acl_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ lib02_imap_acl_plugin.la if DOVECOT_PLUGIN_DEPS lib02_imap_acl_plugin_la_LIBADD = \ ../acl/lib01_acl_plugin.la endif lib02_imap_acl_plugin_la_SOURCES = \ imap-acl-plugin.c noinst_HEADERS = \ imap-acl-plugin.h dovecot-2.2.33.2/src/plugins/fts/0002755000175000017500000000000013172375613013463 500000000000000dovecot-2.2.33.2/src/plugins/fts/Makefile.in0000644000175000017500000010212613172375574015456 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = xml2text$(EXEEXT) subdir = src/plugins/fts ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) lib20_doveadm_fts_plugin_la_LIBADD = am_lib20_doveadm_fts_plugin_la_OBJECTS = doveadm-fts.lo \ doveadm-dump-fts-expunge-log.lo lib20_doveadm_fts_plugin_la_OBJECTS = \ $(am_lib20_doveadm_fts_plugin_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib20_doveadm_fts_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_doveadm_fts_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ lib20_fts_plugin_la_DEPENDENCIES = ../../lib-fts/libfts.la am_lib20_fts_plugin_la_OBJECTS = fts-api.lo fts-build-mail.lo \ fts-expunge-log.lo fts-indexer.lo fts-parser.lo \ fts-parser-html.lo fts-parser-script.lo fts-parser-tika.lo \ fts-plugin.lo fts-search.lo fts-search-args.lo \ fts-search-serialize.lo fts-storage.lo fts-user.lo lib20_fts_plugin_la_OBJECTS = $(am_lib20_fts_plugin_la_OBJECTS) lib20_fts_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib20_fts_plugin_la_LDFLAGS) \ $(LDFLAGS) -o $@ PROGRAMS = $(pkglibexec_PROGRAMS) am_xml2text_OBJECTS = xml2text.$(OBJEXT) xml2text_OBJECTS = $(am_xml2text_OBJECTS) am__DEPENDENCIES_1 = SCRIPTS = $(pkglibexec_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib20_doveadm_fts_plugin_la_SOURCES) \ $(lib20_fts_plugin_la_SOURCES) $(xml2text_SOURCES) DIST_SOURCES = $(lib20_doveadm_fts_plugin_la_SOURCES) \ $(lib20_fts_plugin_la_SOURCES) $(xml2text_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-fts \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/doveadm lib20_doveadm_fts_plugin_la_LDFLAGS = -module -avoid-version lib20_fts_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_fts_plugin.la lib20_fts_plugin_la_LIBADD = ../../lib-fts/libfts.la lib20_fts_plugin_la_SOURCES = \ fts-api.c \ fts-build-mail.c \ fts-expunge-log.c \ fts-indexer.c \ fts-parser.c \ fts-parser-html.c \ fts-parser-script.c \ fts-parser-tika.c \ fts-plugin.c \ fts-search.c \ fts-search-args.c \ fts-search-serialize.c \ fts-storage.c \ fts-user.c pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = \ fts-api.h \ fts-api-private.h \ fts-expunge-log.h \ fts-indexer.h \ fts-parser.h \ fts-storage.h \ fts-user.h noinst_HEADERS = \ doveadm-fts.h \ fts-build-mail.h \ fts-plugin.h \ fts-search-args.h \ fts-search-serialize.h xml2text_SOURCES = xml2text.c xml2text_LDADD = fts-parser-html.lo $(LIBDOVECOT) xml2text_DEPENDENCIES = $(module_LTLIBRARIES) $(LIBDOVECOT_DEPS) pkglibexec_SCRIPTS = decode2text.sh EXTRA_DIST = $(pkglibexec_SCRIPTS) doveadm_module_LTLIBRARIES = \ lib20_doveadm_fts_plugin.la lib20_doveadm_fts_plugin_la_SOURCES = \ doveadm-fts.c \ doveadm-dump-fts-expunge-log.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/fts/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/fts/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ } uninstall-doveadm_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ done clean-doveadm_moduleLTLIBRARIES: -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) @list='$(doveadm_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib20_doveadm_fts_plugin.la: $(lib20_doveadm_fts_plugin_la_OBJECTS) $(lib20_doveadm_fts_plugin_la_DEPENDENCIES) $(EXTRA_lib20_doveadm_fts_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_doveadm_fts_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib20_doveadm_fts_plugin_la_OBJECTS) $(lib20_doveadm_fts_plugin_la_LIBADD) $(LIBS) lib20_fts_plugin.la: $(lib20_fts_plugin_la_OBJECTS) $(lib20_fts_plugin_la_DEPENDENCIES) $(EXTRA_lib20_fts_plugin_la_DEPENDENCIES) $(AM_V_CCLD)$(lib20_fts_plugin_la_LINK) -rpath $(moduledir) $(lib20_fts_plugin_la_OBJECTS) $(lib20_fts_plugin_la_LIBADD) $(LIBS) install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list xml2text$(EXEEXT): $(xml2text_OBJECTS) $(xml2text_DEPENDENCIES) $(EXTRA_xml2text_DEPENDENCIES) @rm -f xml2text$(EXEEXT) $(AM_V_CCLD)$(LINK) $(xml2text_OBJECTS) $(xml2text_LDADD) $(LIBS) install-pkglibexecSCRIPTS: $(pkglibexec_SCRIPTS) @$(NORMAL_INSTALL) @list='$(pkglibexec_SCRIPTS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_SCRIPTS)'; test -n "$(pkglibexecdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(pkglibexecdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-dump-fts-expunge-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-fts.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-build-mail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-expunge-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-indexer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-parser-html.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-parser-script.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-parser-tika.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-search-args.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-search-serialize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fts-user.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml2text.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-doveadm_moduleLTLIBRARIES \ install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-pkglibexecSCRIPTS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-pkglibexecSCRIPTS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ clean-moduleLTLIBRARIES clean-pkglibexecPROGRAMS cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-doveadm_moduleLTLIBRARIES install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-pkglibexecSCRIPTS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-doveadm_moduleLTLIBRARIES \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-pkglibexecSCRIPTS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/plugins/fts/fts-search-serialize.h0000644000175000017500000000121713123174404017566 00000000000000#ifndef FTS_SEARCH_SERIALIZE_H #define FTS_SEARCH_SERIALIZE_H /* serialize [non]match_always fields (clearing buffer) */ void fts_search_serialize(buffer_t *buf, const struct mail_search_arg *args); /* add/remove [non]match_always fields in search args */ void fts_search_deserialize(struct mail_search_arg *args, const buffer_t *buf); /* add match_always=TRUE fields to search args */ void fts_search_deserialize_add_matches(struct mail_search_arg *args, const buffer_t *buf); /* add nonmatch_always=TRUE fields to search args */ void fts_search_deserialize_add_nonmatches(struct mail_search_arg *args, const buffer_t *buf); #endif dovecot-2.2.33.2/src/plugins/fts/fts-search-args.h0000644000175000017500000000023313123174404016530 00000000000000#ifndef FTS_SEARCH_ARGS_H #define FTS_SEARCH_ARGS_H int fts_search_args_expand(struct fts_backend *backend, struct mail_search_args *args); #endif dovecot-2.2.33.2/src/plugins/fts/fts-search.c0000644000175000017500000002461713123174404015605 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "seq-range-array.h" #include "mail-search.h" #include "fts-api-private.h" #include "fts-search-args.h" #include "fts-search-serialize.h" #include "fts-storage.h" static void uid_range_to_seqs(struct fts_search_context *fctx, const ARRAY_TYPE(seq_range) *uid_range, ARRAY_TYPE(seq_range) *seq_range) { const struct seq_range *range; unsigned int i, count; uint32_t seq1, seq2; range = array_get(uid_range, &count); if (!array_is_created(seq_range)) p_array_init(seq_range, fctx->result_pool, count); for (i = 0; i < count; i++) { if (range[i].seq1 > range[i].seq2) continue; mailbox_get_seq_range(fctx->box, range[i].seq1, range[i].seq2, &seq1, &seq2); if (seq1 != 0) seq_range_array_add_range(seq_range, seq1, seq2); } } static int fts_search_lookup_level_single(struct fts_search_context *fctx, struct mail_search_arg *args, bool and_args) { enum fts_lookup_flags flags = fctx->flags | (and_args ? FTS_LOOKUP_FLAG_AND_ARGS : 0); struct fts_search_level *level; struct fts_result result; i_zero(&result); p_array_init(&result.definite_uids, fctx->result_pool, 32); p_array_init(&result.maybe_uids, fctx->result_pool, 32); p_array_init(&result.scores, fctx->result_pool, 32); mail_search_args_reset(args, TRUE); if (fts_backend_lookup(fctx->backend, fctx->box, args, flags, &result) < 0) return -1; level = array_append_space(&fctx->levels); level->args_matches = buffer_create_dynamic(fctx->result_pool, 16); fts_search_serialize(level->args_matches, args); uid_range_to_seqs(fctx, &result.definite_uids, &level->definite_seqs); uid_range_to_seqs(fctx, &result.maybe_uids, &level->maybe_seqs); level->score_map = result.scores; return 0; } static void level_scores_add_vuids(struct mailbox *box, struct fts_search_level *level, struct fts_result *br) { const struct fts_score_map *scores; unsigned int i, count; ARRAY_TYPE(seq_range) backend_uids; ARRAY_TYPE(uint32_t) vuids_arr; const uint32_t *vuids; struct fts_score_map *score; scores = array_get(&br->scores, &count); t_array_init(&vuids_arr, count); t_array_init(&backend_uids, 64); for (i = 0; i < count; i++) seq_range_array_add(&backend_uids, scores[i].uid); box->virtual_vfuncs->get_virtual_uid_map(box, br->box, &backend_uids, &vuids_arr); i_assert(array_count(&vuids_arr) == array_count(&br->scores)); vuids = array_get(&vuids_arr, &count); for (i = 0; i < count; i++) { score = array_append_space(&level->score_map); score->uid = vuids[i]; score->score = scores[i].score; } } static int mailbox_cmp_fts_backend(struct mailbox *const *m1, struct mailbox *const *m2) { struct fts_backend *b1, *b2; b1 = fts_mailbox_backend(*m1); b2 = fts_mailbox_backend(*m2); if (b1 < b2) return -1; if (b1 > b2) return 1; return 0; } static int multi_add_lookup_result(struct fts_search_context *fctx, struct fts_search_level *level, struct mail_search_arg *args, struct fts_multi_result *result) { ARRAY_TYPE(seq_range) vuids; size_t orig_size; unsigned int i; orig_size = level->args_matches->used; fts_search_serialize(level->args_matches, args); if (orig_size > 0) { if (level->args_matches->used != orig_size * 2 || memcmp(level->args_matches->data, CONST_PTR_OFFSET(level->args_matches->data, orig_size), orig_size) != 0) i_panic("incompatible fts backends for namespaces"); buffer_set_used_size(level->args_matches, orig_size); } t_array_init(&vuids, 64); for (i = 0; result->box_results[i].box != NULL; i++) { struct fts_result *br = &result->box_results[i]; array_clear(&vuids); if (array_is_created(&br->definite_uids)) { fctx->box->virtual_vfuncs->get_virtual_uids(fctx->box, br->box, &br->definite_uids, &vuids); } uid_range_to_seqs(fctx, &vuids, &level->definite_seqs); array_clear(&vuids); if (array_is_created(&br->maybe_uids)) { fctx->box->virtual_vfuncs->get_virtual_uids(fctx->box, br->box, &br->maybe_uids, &vuids); } uid_range_to_seqs(fctx, &vuids, &level->maybe_seqs); if (array_is_created(&br->scores)) level_scores_add_vuids(fctx->box, level, br); } return 0; } static int fts_search_lookup_level_multi(struct fts_search_context *fctx, struct mail_search_arg *args, bool and_args) { enum fts_lookup_flags flags = fctx->flags | (and_args ? FTS_LOOKUP_FLAG_AND_ARGS : 0); ARRAY_TYPE(mailboxes) mailboxes_arr, tmp_mailboxes; struct mailbox *const *mailboxes; struct fts_backend *backend; struct fts_search_level *level; struct fts_multi_result result; unsigned int i, j, mailbox_count; p_array_init(&mailboxes_arr, fctx->result_pool, 8); fctx->box->virtual_vfuncs->get_virtual_backend_boxes(fctx->box, &mailboxes_arr, TRUE); array_sort(&mailboxes_arr, mailbox_cmp_fts_backend); i_zero(&result); result.pool = fctx->result_pool; level = array_append_space(&fctx->levels); level->args_matches = buffer_create_dynamic(fctx->result_pool, 16); p_array_init(&level->score_map, fctx->result_pool, 1); mailboxes = array_get(&mailboxes_arr, &mailbox_count); t_array_init(&tmp_mailboxes, mailbox_count); for (i = 0; i < mailbox_count; i = j) { array_clear(&tmp_mailboxes); array_append(&tmp_mailboxes, &mailboxes[i], 1); backend = fts_mailbox_backend(mailboxes[i]); for (j = i + 1; j < mailbox_count; j++) { if (fts_mailbox_backend(mailboxes[j]) != backend) break; array_append(&tmp_mailboxes, &mailboxes[j], 1); } array_append_zero(&tmp_mailboxes); mail_search_args_reset(args, TRUE); if (fts_backend_lookup_multi(backend, array_idx(&tmp_mailboxes, 0), args, flags, &result) < 0) return -1; if (multi_add_lookup_result(fctx, level, args, &result) < 0) return -1; } return 0; } static int fts_search_lookup_level(struct fts_search_context *fctx, struct mail_search_arg *args, bool and_args) { int ret; T_BEGIN { ret = !fctx->virtual_mailbox ? fts_search_lookup_level_single(fctx, args, and_args) : fts_search_lookup_level_multi(fctx, args, and_args); } T_END; if (ret < 0) return -1; for (; args != NULL; args = args->next) { if (args->type != SEARCH_OR && args->type != SEARCH_SUB) continue; if (fts_search_lookup_level(fctx, args->value.subargs, args->type == SEARCH_SUB) < 0) return -1; } return 0; } static void fts_search_merge_scores_and(ARRAY_TYPE(fts_score_map) *dest, const ARRAY_TYPE(fts_score_map) *src) { struct fts_score_map *dest_map; const struct fts_score_map *src_map; unsigned int desti, srci, dest_count, src_count; dest_map = array_get_modifiable(dest, &dest_count); src_map = array_get(src, &src_count); /* arg_scores are summed to current scores. we could drop UIDs that don't exist in both, but that's just extra work so don't bother */ for (desti = srci = 0; desti < dest_count && srci < src_count;) { if (dest_map[desti].uid < src_map[srci].uid) desti++; else if (dest_map[desti].uid > src_map[srci].uid) srci++; else { if (dest_map[desti].score < src_map[srci].score) dest_map[desti].score = src_map[srci].score; desti++; srci++; } } } static void fts_search_merge_scores_or(ARRAY_TYPE(fts_score_map) *dest, const ARRAY_TYPE(fts_score_map) *src) { ARRAY_TYPE(fts_score_map) src2; const struct fts_score_map *src_map, *src2_map; unsigned int srci, src2i, src_count, src2_count; t_array_init(&src2, array_count(dest)); array_append_array(&src2, dest); array_clear(dest); src_map = array_get(src, &src_count); src2_map = array_get(&src2, &src2_count); /* add any missing UIDs to current scores. if any existing UIDs have lower scores than in arg_scores, increase them. */ for (srci = src2i = 0; srci < src_count || src2i < src2_count;) { if (src2i == src2_count || src_map[srci].uid < src2_map[src2i].uid) { array_append(dest, &src_map[srci], 1); srci++; } else if (srci == src_count || src_map[srci].uid > src2_map[src2i].uid) { array_append(dest, &src2_map[src2i], 1); src2i++; } else { i_assert(src_map[srci].uid == src2_map[src2i].uid); if (src_map[srci].score > src2_map[src2i].score) array_append(dest, &src_map[srci], 1); else array_append(dest, &src2_map[src2i], 1); srci++; src2i++; } } } static void fts_search_merge_scores_level(struct fts_search_context *fctx, struct mail_search_arg *args, unsigned int *idx, bool and_args, ARRAY_TYPE(fts_score_map) *scores) { const struct fts_search_level *level; ARRAY_TYPE(fts_score_map) arg_scores; i_assert(array_count(scores) == 0); /* The (simplified) args can look like: A and B and (C or D) and (E or F) and ... A or B or (C and D) or (E and F) or ... The A op B part's scores are in level->scores. The child args' scores are in the sub levels' scores. */ level = array_idx(&fctx->levels, *idx); array_append_array(scores, &level->score_map); t_array_init(&arg_scores, 64); for (; args != NULL; args = args->next) { if (args->type != SEARCH_OR && args->type != SEARCH_SUB) continue; *idx += 1; array_clear(&arg_scores); fts_search_merge_scores_level(fctx, args->value.subargs, idx, args->type == SEARCH_OR, &arg_scores); if (and_args) fts_search_merge_scores_and(scores, &arg_scores); else fts_search_merge_scores_or(scores, &arg_scores); } } static void fts_search_merge_scores(struct fts_search_context *fctx) { unsigned int idx = 0; fts_search_merge_scores_level(fctx, fctx->args->args, &idx, TRUE, &fctx->scores->score_map); } void fts_search_lookup(struct fts_search_context *fctx) { uint32_t last_uid, seq1, seq2; i_assert(array_count(&fctx->levels) == 0); i_assert(fctx->args->simplified); if (fts_backend_refresh(fctx->backend) < 0) return; if (fts_backend_get_last_uid(fctx->backend, fctx->box, &last_uid) < 0) return; mailbox_get_seq_range(fctx->box, last_uid+1, (uint32_t)-1, &seq1, &seq2); fctx->first_unindexed_seq = seq1 != 0 ? seq1 : (uint32_t)-1; if ((fctx->backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0) { if (fts_search_args_expand(fctx->backend, fctx->args) < 0) return; } fts_search_serialize(fctx->orig_matches, fctx->args->args); if (fts_search_lookup_level(fctx, fctx->args->args, TRUE) == 0) { fctx->fts_lookup_success = TRUE; fts_search_merge_scores(fctx); } fts_search_deserialize(fctx->args->args, fctx->orig_matches); fts_backend_lookup_done(fctx->backend); } dovecot-2.2.33.2/src/plugins/fts/fts-indexer.c0000644000175000017500000001431213165463624016000 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "write-full.h" #include "strescape.h" #include "time-util.h" #include "settings-parser.h" #include "mail-user.h" #include "mail-storage-private.h" #include "fts-api.h" #include "fts-indexer.h" #define INDEXER_NOTIFY_INTERVAL_SECS 10 #define INDEXER_SOCKET_NAME "indexer" #define INDEXER_WAIT_MSECS 250 #define INDEXER_HANDSHAKE "VERSION\tindexer\t1\t0\n" struct fts_indexer_context { struct mailbox *box; struct timeval search_start_time, last_notify; unsigned int percentage; unsigned int timeout_secs; char *path; int fd; struct istream *input; unsigned int notified:1; unsigned int failed:1; }; int fts_indexer_cmd(struct mail_user *user, const char *cmd, const char **path_r) { const char *path; int fd; path = t_strconcat(user->set->base_dir, "/"INDEXER_SOCKET_NAME, NULL); fd = net_connect_unix_with_retries(path, 1000); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m", path); return -1; } cmd = t_strconcat(INDEXER_HANDSHAKE, cmd, NULL); if (write_full(fd, cmd, strlen(cmd)) < 0) { i_error("write(%s) failed: %m", path); i_close_fd(&fd); return -1; } *path_r = path; return fd; } static void fts_indexer_notify(struct fts_indexer_context *ctx) { unsigned long long elapsed_msecs, est_total_msecs; unsigned int eta_secs; if (ioloop_time - ctx->last_notify.tv_sec < INDEXER_NOTIFY_INTERVAL_SECS) return; ctx->last_notify = ioloop_timeval; if (ctx->box->storage->callbacks.notify_ok == NULL || ctx->percentage == 0) return; elapsed_msecs = timeval_diff_msecs(&ioloop_timeval, &ctx->search_start_time); est_total_msecs = elapsed_msecs * 100 / ctx->percentage; eta_secs = (est_total_msecs - elapsed_msecs) / 1000; T_BEGIN { const char *text; text = t_strdup_printf("Indexed %d%% of the mailbox, " "ETA %d:%02d", ctx->percentage, eta_secs/60, eta_secs%60); ctx->box->storage->callbacks. notify_ok(ctx->box, text, ctx->box->storage->callback_context); ctx->notified = TRUE; } T_END; } int fts_indexer_init(struct fts_backend *backend, struct mailbox *box, struct fts_indexer_context **ctx_r) { struct fts_indexer_context *ctx; struct mailbox_status status; uint32_t last_uid, seq1, seq2; const char *path, *cmd, *value, *error; int fd; if (fts_backend_get_last_uid(backend, box, &last_uid) < 0) return -1; mailbox_get_open_status(box, STATUS_UIDNEXT, &status); if (status.uidnext == last_uid+1) { /* everything is already indexed */ return 0; } mailbox_get_seq_range(box, last_uid+1, (uint32_t)-1, &seq1, &seq2); if (seq1 == 0) { /* no new messages (last messages in mailbox were expunged) */ return 0; } cmd = t_strdup_printf("PREPEND\t1\t%s\t%s\t0\t%s\n", str_tabescape(box->storage->user->username), str_tabescape(box->vname), str_tabescape(box->storage->user->session_id)); fd = fts_indexer_cmd(box->storage->user, cmd, &path); if (fd == -1) return -1; /* connect to indexer and request immediate indexing of the mailbox */ ctx = i_new(struct fts_indexer_context, 1); ctx->box = box; ctx->path = i_strdup(path); ctx->fd = fd; ctx->input = i_stream_create_fd(fd, 128, FALSE); ctx->search_start_time = ioloop_timeval; value = mail_user_plugin_getenv(box->storage->user, "fts_index_timeout"); if (value != NULL) { if (settings_get_time(value, &ctx->timeout_secs, &error) < 0) i_error("Invalid fts_index_timeout setting: %s", error); } *ctx_r = ctx; return 1; } int fts_indexer_deinit(struct fts_indexer_context **_ctx) { struct fts_indexer_context *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; i_stream_destroy(&ctx->input); if (close(ctx->fd) < 0) i_error("close(%s) failed: %m", ctx->path); if (ctx->notified) { /* we notified at least once */ ctx->box->storage->callbacks. notify_ok(ctx->box, "Mailbox indexing finished", ctx->box->storage->callback_context); } i_free(ctx->path); i_free(ctx); return ret; } static int fts_indexer_input(struct fts_indexer_context *ctx) { const char *line; int percentage; while ((line = i_stream_read_next_line(ctx->input)) != NULL) { /* initial reply: \t OK following: \t */ if (strncmp(line, "1\t", 2) != 0) { i_error("indexer sent invalid reply: %s", line); return -1; } line += 2; if (strcmp(line, "OK") == 0) continue; if (str_to_int(line, &percentage) < 0 || percentage > 100) { i_error("indexer sent invalid percentage: %s", line); return -1; } if (percentage < 0) { /* indexing failed */ i_error("indexer failed to index mailbox %s", ctx->box->vname); return -1; } ctx->percentage = percentage; if (percentage == 100) { /* finished */ return 1; } } if (ctx->input->stream_errno != 0) { i_error("indexer read(%s) failed: %s", i_stream_get_name(ctx->input), i_stream_get_error(ctx->input)); return -1; } if (ctx->input->eof) { i_error("indexer disconnected unexpectedly"); return -1; } return 0; } static int fts_indexer_more_int(struct fts_indexer_context *ctx) { struct ioloop *ioloop; struct io *io; struct timeout *to; int ret; if ((ret = fts_indexer_input(ctx)) != 0) return ret; /* wait for a while for the reply. FIXME: once search API supports asynchronous waits, get rid of this wait and use the mail IO loop */ ioloop = io_loop_create(); io = io_add(ctx->fd, IO_READ, io_loop_stop, ioloop); to = timeout_add_short(INDEXER_WAIT_MSECS, io_loop_stop, ioloop); io_loop_run(ioloop); io_remove(&io); timeout_remove(&to); io_loop_destroy(&ioloop); return fts_indexer_input(ctx); } int fts_indexer_more(struct fts_indexer_context *ctx) { int ret, diff; if ((ret = fts_indexer_more_int(ctx)) < 0) { mail_storage_set_internal_error(ctx->box->storage); ctx->failed = TRUE; return -1; } if (ctx->timeout_secs > 0) { diff = ioloop_time - ctx->search_start_time.tv_sec; if (diff > (int)ctx->timeout_secs) { mail_storage_set_error(ctx->box->storage, MAIL_ERROR_INUSE, "Timeout while waiting for indexing to finish"); ctx->failed = TRUE; return -1; } } if (ret == 0) fts_indexer_notify(ctx); return ret; } dovecot-2.2.33.2/src/plugins/fts/fts-plugin.h0000644000175000017500000000017613123174404015635 00000000000000#ifndef FTS_PLUGIN_H #define FTS_PLUGIN_H void fts_plugin_init(struct module *module); void fts_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/fts/xml2text.c0000644000175000017500000000163313123174404015326 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "message-parser.h" #include "fts-parser.h" #include int main(void) { struct fts_parser *parser; unsigned char buf[IO_BLOCK_SIZE]; struct message_block block; ssize_t ret; lib_init(); parser = fts_parser_html.try_init(NULL, "text/html", NULL); i_assert(parser != NULL); i_zero(&block); while ((ret = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { block.data = buf; block.size = ret; parser->v.more(parser, &block); if (write(STDOUT_FILENO, block.data, block.size) < 0) i_fatal("write(stdout) failed: %m"); } if (ret < 0) i_fatal("read(stdin) failed: %m"); for (;;) { block.size = 0; parser->v.more(parser, &block); if (block.size == 0) break; if (write(STDOUT_FILENO, block.data, block.size) < 0) i_fatal("write(stdout) failed: %m"); } lib_deinit(); return 0; } dovecot-2.2.33.2/src/plugins/fts/fts-search-serialize.c0000644000175000017500000000436413123174404017567 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "mail-search.h" #include "fts-search-serialize.h" #define HAVE_SUBARGS(arg) \ ((arg)->type == SEARCH_SUB || (arg)->type == SEARCH_OR) void fts_search_serialize(buffer_t *buf, const struct mail_search_arg *args) { char chr; for (; args != NULL; args = args->next) { chr = (args->match_always ? 1 : 0) | (args->nonmatch_always ? 2 : 0); buffer_append_c(buf, chr); if (HAVE_SUBARGS(args)) fts_search_serialize(buf, args->value.subargs); } } static void fts_search_deserialize_idx(struct mail_search_arg *args, const buffer_t *buf, unsigned int *idx) { const char *data = buf->data; for (; args != NULL; args = args->next) { i_assert(*idx < buf->used); args->match_always = (data[*idx] & 1) != 0; args->nonmatch_always = (data[*idx] & 2) != 0; args->result = args->match_always ? 1 : (args->nonmatch_always ? 0 : -1); *idx += 1; if (HAVE_SUBARGS(args)) { fts_search_deserialize_idx(args->value.subargs, buf, idx); } } } void fts_search_deserialize(struct mail_search_arg *args, const buffer_t *buf) { unsigned int idx = 0; fts_search_deserialize_idx(args, buf, &idx); i_assert(idx == buf->used); } static void fts_search_deserialize_add_idx(struct mail_search_arg *args, const buffer_t *buf, unsigned int *idx, bool matches) { const char *data = buf->data; for (; args != NULL; args = args->next) { i_assert(*idx < buf->used); if (data[*idx] != 0) { if (matches) { args->match_always = TRUE; args->result = 1; } else { args->nonmatch_always = TRUE; args->result = 0; } } *idx += 1; if (HAVE_SUBARGS(args)) { fts_search_deserialize_add_idx(args->value.subargs, buf, idx, matches); } } } void fts_search_deserialize_add_matches(struct mail_search_arg *args, const buffer_t *buf) { unsigned int idx = 0; fts_search_deserialize_add_idx(args, buf, &idx, TRUE); i_assert(idx == buf->used); } void fts_search_deserialize_add_nonmatches(struct mail_search_arg *args, const buffer_t *buf) { unsigned int idx = 0; fts_search_deserialize_add_idx(args, buf, &idx, FALSE); i_assert(idx == buf->used); } dovecot-2.2.33.2/src/plugins/fts/fts-search-args.c0000644000175000017500000001613313165463543016544 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "mail-namespace.h" #include "mail-search.h" #include "fts-api-private.h" #include "fts-tokenizer.h" #include "fts-filter.h" #include "fts-user.h" #include "fts-search-args.h" static void strings_deduplicate(ARRAY_TYPE(const_string) *arr) { const char *const *strings; unsigned int i, count; strings = array_get(arr, &count); for (i = 1; i < count; ) { if (strcmp(strings[i-1], strings[i]) == 0) { array_delete(arr, i, 1); strings = array_get(arr, &count); } else { i++; } } } static struct mail_search_arg * fts_search_arg_create_or(const struct mail_search_arg *orig_arg, pool_t pool, const ARRAY_TYPE(const_string) *tokens) { struct mail_search_arg *arg, *or_arg, **argp; const char *const *tokenp; /* create the OR arg first as the parent */ or_arg = p_new(pool, struct mail_search_arg, 1); or_arg->type = SEARCH_OR; /* now create all the child args for the OR */ argp = &or_arg->value.subargs; array_foreach(tokens, tokenp) { arg = p_new(pool, struct mail_search_arg, 1); *arg = *orig_arg; arg->match_not = FALSE; /* we copied this to the root OR */ arg->next = NULL; arg->value.str = p_strdup(pool, *tokenp); *argp = arg; argp = &arg->next; } return or_arg; } static int fts_backend_dovecot_expand_tokens(struct fts_filter *filter, pool_t pool, struct mail_search_arg *parent_arg, const struct mail_search_arg *orig_arg, const char *orig_token, const char *token, const char **error_r) { struct mail_search_arg *arg; ARRAY_TYPE(const_string) tokens; const char *token2, *error; int ret; t_array_init(&tokens, 4); /* first add the word exactly as it without any tokenization */ array_append(&tokens, &orig_token, 1); /* then add it tokenized, but without filtering */ array_append(&tokens, &token, 1); /* add the word filtered */ if (filter != NULL) { token2 = t_strdup(token); ret = fts_filter_filter(filter, &token2, &error); if (ret > 0) { token2 = t_strdup(token2); array_append(&tokens, &token2, 1); } else if (ret < 0) { *error_r = t_strdup_printf("Couldn't filter search token: %s", error); return -1; } else { /* The filter dropped the token, which means it was never even indexed. Ignore this word entirely in the search query. */ return 0; } } array_sort(&tokens, i_strcmp_p); strings_deduplicate(&tokens); arg = fts_search_arg_create_or(orig_arg, pool, &tokens); arg->next = parent_arg->value.subargs; parent_arg->value.subargs = arg; return 0; } static int fts_backend_dovecot_tokenize_lang(struct fts_user_language *user_lang, pool_t pool, struct mail_search_arg *or_arg, struct mail_search_arg *orig_arg, const char *orig_token, const char **error_r) { size_t orig_token_len = strlen(orig_token); struct mail_search_arg *and_arg; const char *token, *error; int ret; /* we want all the tokens found from the string to be found, so create a parent AND and place all the filtered token alternatives under it */ and_arg = p_new(pool, struct mail_search_arg, 1); and_arg->type = SEARCH_SUB; and_arg->next = or_arg->value.subargs; or_arg->value.subargs = and_arg; /* reset tokenizer between search args in case there's any state left from some previous failure */ fts_tokenizer_reset(user_lang->search_tokenizer); while ((ret = fts_tokenizer_next(user_lang->search_tokenizer, (const void *)orig_token, orig_token_len, &token, &error)) > 0) { if (fts_backend_dovecot_expand_tokens(user_lang->filter, pool, and_arg, orig_arg, orig_token, token, error_r) < 0) return -1; } while (ret >= 0 && (ret = fts_tokenizer_final(user_lang->search_tokenizer, &token, &error)) > 0) { if (fts_backend_dovecot_expand_tokens(user_lang->filter, pool, and_arg, orig_arg, orig_token, token, error_r) < 0) return -1; } if (ret < 0) { *error_r = t_strdup_printf("Couldn't tokenize search args: %s", error); return -1; } if (and_arg->value.subargs == NULL) { /* nothing was actually expanded, remove the empty and_arg */ or_arg->value.subargs = NULL; } return 0; } static int fts_search_arg_expand(struct fts_backend *backend, pool_t pool, struct mail_search_arg **argp) { const ARRAY_TYPE(fts_user_language) *languages; struct fts_user_language *const *langp; struct mail_search_arg *or_arg, *orig_arg = *argp; const char *error, *orig_token = orig_arg->value.str; if ((*argp)->type == SEARCH_HEADER && !fts_header_has_language((*argp)->hdr_field_name)) { /* use only the data-language */ languages = fts_user_get_data_languages(backend->ns->user); } else { languages = fts_user_get_all_languages(backend->ns->user); } /* OR together all the different expansions for different languages. it's enough for one of them to match. */ or_arg = p_new(pool, struct mail_search_arg, 1); or_arg->type = SEARCH_OR; or_arg->match_not = orig_arg->match_not; or_arg->next = orig_arg->next; array_foreach(languages, langp) { if (fts_backend_dovecot_tokenize_lang(*langp, pool, or_arg, orig_arg, orig_token, &error) < 0) { i_error("fts: %s", error); return -1; } } if (or_arg->value.subargs == NULL) { /* we couldn't parse any tokens from the input */ or_arg->type = SEARCH_ALL; or_arg->match_not = !or_arg->match_not; } *argp = or_arg; return 0; } static int fts_search_args_expand_tree(struct fts_backend *backend, pool_t pool, struct mail_search_arg **argp) { int ret; for (; *argp != NULL; argp = &(*argp)->next) { switch ((*argp)->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: if (fts_search_args_expand_tree(backend, pool, &(*argp)->value.subargs) < 0) return -1; break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if ((*argp)->value.str[0] == '\0') { /* we're testing for the existence of the header */ break; } /* fall through */ case SEARCH_BODY: case SEARCH_TEXT: T_BEGIN { ret = fts_search_arg_expand(backend, pool, argp); } T_END; if (ret < 0) return -1; break; default: break; } } return 0; } int fts_search_args_expand(struct fts_backend *backend, struct mail_search_args *args) { struct mail_search_arg *args_dup, *orig_args = args->args; /* don't keep re-expanding every time the search args are used. this is especially important to avoid an assert-crash in index_search_result_update_flags(). */ if (args->fts_expanded) return 0; args->fts_expanded = TRUE; /* duplicate the args, so if expansion fails we haven't changed anything */ args_dup = mail_search_arg_dup(args->pool, args->args); if (fts_search_args_expand_tree(backend, args->pool, &args_dup) < 0) return -1; /* we'll need to re-simplify the args if we changed anything */ args->simplified = FALSE; args->args = args_dup; mail_search_args_simplify(args); /* duplicated args aren't initialized */ i_assert(args->init_refcount > 0); mail_search_arg_init(args, args_dup, FALSE, NULL); mail_search_arg_deinit(orig_args); return 0; } dovecot-2.2.33.2/src/plugins/fts/fts-expunge-log.c0000644000175000017500000004207613165463624016604 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "crc32.h" #include "hash.h" #include "istream.h" #include "write-full.h" #include "seq-range-array.h" #include "mail-storage.h" #include "fts-expunge-log.h" #include #include #include struct fts_expunge_log_record { /* CRC32 of this entire record (except this checksum) */ uint32_t checksum; /* Size of this entire record */ uint32_t record_size; /* Mailbox GUID */ guid_128_t guid; /* { uid1, uid2 } pairs */ /* uint32_t expunge_uid_ranges[]; */ /* Total number of messages expunged so far in this log */ /* uint32_t expunge_count; */ }; struct fts_expunge_log { char *path; int fd; struct stat st; }; struct fts_expunge_log_mailbox { guid_128_t guid; ARRAY_TYPE(seq_range) uids; unsigned uids_count; }; struct fts_expunge_log_append_ctx { struct fts_expunge_log *log; pool_t pool; HASH_TABLE(uint8_t *, struct fts_expunge_log_mailbox *) mailboxes; struct fts_expunge_log_mailbox *prev_mailbox; bool failed; }; struct fts_expunge_log_read_ctx { struct fts_expunge_log *log; struct istream *input; buffer_t buffer; struct fts_expunge_log_read_record read_rec; bool failed; bool corrupted; bool unlink; }; struct fts_expunge_log *fts_expunge_log_init(const char *path) { struct fts_expunge_log *log; log = i_new(struct fts_expunge_log, 1); log->path = i_strdup(path); log->fd = -1; return log; } void fts_expunge_log_deinit(struct fts_expunge_log **_log) { struct fts_expunge_log *log = *_log; *_log = NULL; if (log->fd != -1) i_close_fd(&log->fd); i_free(log->path); i_free(log); } static int fts_expunge_log_open(struct fts_expunge_log *log, bool create) { int fd; i_assert(log->fd == -1); /* FIXME: use proper permissions */ fd = open(log->path, O_RDWR | O_APPEND | (create ? O_CREAT : 0), 0600); if (fd == -1) { if (errno == ENOENT && !create) return 0; i_error("open(%s) failed: %m", log->path); return -1; } if (fstat(fd, &log->st) < 0) { i_error("fstat(%s) failed: %m", log->path); i_close_fd(&fd); return -1; } log->fd = fd; return 1; } static int fts_expunge_log_reopen_if_needed(struct fts_expunge_log *log, bool create) { struct stat st; if (log->fd == -1) return fts_expunge_log_open(log, create); if (stat(log->path, &st) == 0) { if (st.st_ino == log->st.st_ino && CMP_DEV_T(st.st_dev, log->st.st_dev)) { /* same file */ return 0; } /* file changed */ } else if (errno == ENOENT) { /* recreate the file */ } else { i_error("stat(%s) failed: %m", log->path); return -1; } if (close(log->fd) < 0) i_error("close(%s) failed: %m", log->path); log->fd = -1; return fts_expunge_log_open(log, create); } static int fts_expunge_log_read_expunge_count(struct fts_expunge_log *log, uint32_t *expunge_count_r) { ssize_t ret; i_assert(log->fd != -1); if (fstat(log->fd, &log->st) < 0) { i_error("fstat(%s) failed: %m", log->path); return -1; } if ((uoff_t)log->st.st_size < sizeof(*expunge_count_r)) { *expunge_count_r = 0; return 0; } /* we'll assume that write()s atomically grow the file size, as O_APPEND almost guarantees. even if not, having a race condition isn't the end of the world. the expunge count is simply read wrong and fts optimize is performed earlier or later than intended. */ ret = pread(log->fd, expunge_count_r, sizeof(*expunge_count_r), log->st.st_size - 4); if (ret < 0) { i_error("pread(%s) failed: %m", log->path); return -1; } if (ret != sizeof(*expunge_count_r)) { i_error("pread(%s) read only %d of %d bytes", log->path, (int)ret, (int)sizeof(*expunge_count_r)); return -1; } return 0; } struct fts_expunge_log_append_ctx * fts_expunge_log_append_begin(struct fts_expunge_log *log) { struct fts_expunge_log_append_ctx *ctx; pool_t pool; pool = pool_alloconly_create("fts expunge log append", 1024); ctx = p_new(pool, struct fts_expunge_log_append_ctx, 1); ctx->log = log; ctx->pool = pool; hash_table_create(&ctx->mailboxes, pool, 0, guid_128_hash, guid_128_cmp); if (log != NULL && fts_expunge_log_reopen_if_needed(log, TRUE) < 0) ctx->failed = TRUE; return ctx; } static struct fts_expunge_log_mailbox * fts_expunge_log_mailbox_alloc(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid) { uint8_t *guid_p; struct fts_expunge_log_mailbox *mailbox; mailbox = p_new(ctx->pool, struct fts_expunge_log_mailbox, 1); guid_128_copy(mailbox->guid, mailbox_guid); p_array_init(&mailbox->uids, ctx->pool, 16); guid_p = mailbox->guid; hash_table_insert(ctx->mailboxes, guid_p, mailbox); return mailbox; } static struct fts_expunge_log_mailbox * fts_expunge_log_append_mailbox(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid) { const uint8_t *guid_p = mailbox_guid; struct fts_expunge_log_mailbox *mailbox; if (ctx->prev_mailbox != NULL && guid_128_equals(mailbox_guid, ctx->prev_mailbox->guid)) mailbox = ctx->prev_mailbox; else { mailbox = hash_table_lookup(ctx->mailboxes, guid_p); if (mailbox == NULL) mailbox = fts_expunge_log_mailbox_alloc(ctx, mailbox_guid); ctx->prev_mailbox = mailbox; } return mailbox; } void fts_expunge_log_append_next(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, uint32_t uid) { struct fts_expunge_log_mailbox *mailbox; mailbox = fts_expunge_log_append_mailbox(ctx, mailbox_guid); if (!seq_range_array_add(&mailbox->uids, uid)) mailbox->uids_count++; } void fts_expunge_log_append_range(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, const struct seq_range *uids) { struct fts_expunge_log_mailbox *mailbox; mailbox = fts_expunge_log_append_mailbox(ctx, mailbox_guid); mailbox->uids_count += seq_range_array_add_range_count(&mailbox->uids, uids->seq1, uids->seq2); /* To be honest, an unbacked log doesn't need to maintain the uids_count, but we don't know here if we're supporting an unbacked log or not, so we have to maintain the value, just in case. At the moment, the only caller of this function is for unbacked logs. */ } void fts_expunge_log_append_record(struct fts_expunge_log_append_ctx *ctx, const struct fts_expunge_log_read_record *record) { const struct seq_range *range; /* FIXME: Optimise with a merge */ array_foreach(&record->uids, range) fts_expunge_log_append_range(ctx, record->mailbox_guid, range); } static void fts_expunge_log_append_mailbox_record(struct fts_expunge_log_append_ctx *ctx, struct fts_expunge_log_mailbox *mailbox) { const struct seq_range *range; /* FIXME: Optimise with a merge */ array_foreach(&mailbox->uids, range) fts_expunge_log_append_range(ctx, mailbox->guid, range); } static void fts_expunge_log_export(struct fts_expunge_log_append_ctx *ctx, uint32_t expunge_count, buffer_t *output) { struct hash_iterate_context *iter; uint8_t *guid_p; struct fts_expunge_log_mailbox *mailbox; struct fts_expunge_log_record *rec; size_t rec_offset; iter = hash_table_iterate_init(ctx->mailboxes); while (hash_table_iterate(iter, ctx->mailboxes, &guid_p, &mailbox)) { rec_offset = output->used; rec = buffer_append_space_unsafe(output, sizeof(*rec)); memcpy(rec->guid, mailbox->guid, sizeof(rec->guid)); /* uint32_t expunge_uid_ranges[]; */ buffer_append(output, array_idx(&mailbox->uids, 0), array_count(&mailbox->uids) * sizeof(struct seq_range)); /* uint32_t expunge_count; */ expunge_count += mailbox->uids_count; buffer_append(output, &expunge_count, sizeof(expunge_count)); /* update the header now that we know the record contents */ rec = buffer_get_space_unsafe(output, rec_offset, output->used - rec_offset); rec->record_size = output->used - rec_offset; rec->checksum = crc32_data(&rec->record_size, rec->record_size - sizeof(rec->checksum)); } hash_table_iterate_deinit(&iter); } static int fts_expunge_log_write(struct fts_expunge_log_append_ctx *ctx) { struct fts_expunge_log *log = ctx->log; buffer_t *buf; uint32_t expunge_count, *e; int ret; /* Unbacked expunge logs cannot be written, by definition */ i_assert(log != NULL); /* try to append to the latest file */ if (fts_expunge_log_reopen_if_needed(log, TRUE) < 0) return -1; if (fts_expunge_log_read_expunge_count(log, &expunge_count) < 0) return -1; buf = buffer_create_dynamic(default_pool, 1024); fts_expunge_log_export(ctx, expunge_count, buf); /* the file was opened with O_APPEND, so this write() should be appended atomically without any need for locking. */ for (;;) { if ((ret = write_full(log->fd, buf->data, buf->used)) < 0) { i_error("write(%s) failed: %m", log->path); if (ftruncate(log->fd, log->st.st_size) < 0) i_error("ftruncate(%s) failed: %m", log->path); } if ((ret = fts_expunge_log_reopen_if_needed(log, TRUE)) <= 0) break; /* the log was unlinked, so we'll need to write again to the new file. the expunge_count needs to be reset to zero from here. */ e = buffer_get_space_unsafe(buf, buf->used - sizeof(uint32_t), sizeof(uint32_t)); i_assert(*e > expunge_count); *e -= expunge_count; expunge_count = 0; } buffer_free(&buf); if (ret == 0) { /* finish by closing the log. this forces NFS to flush the changes to disk without our having to explicitly play with fsync() */ if (close(log->fd) < 0) { /* FIXME: we should ftruncate() in case there were partial writes.. */ i_error("close(%s) failed: %m", log->path); ret = -1; } log->fd = -1; } return ret; } static int fts_expunge_log_append_finalise(struct fts_expunge_log_append_ctx **_ctx, bool commit) { struct fts_expunge_log_append_ctx *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; *_ctx = NULL; if (commit && ret == 0) ret = fts_expunge_log_write(ctx); hash_table_destroy(&ctx->mailboxes); pool_unref(&ctx->pool); return ret; } int fts_expunge_log_uid_count(struct fts_expunge_log *log, unsigned int *expunges_r) { int ret; if ((ret = fts_expunge_log_reopen_if_needed(log, FALSE)) <= 0) { *expunges_r = 0; return ret; } return fts_expunge_log_read_expunge_count(log, expunges_r); } int fts_expunge_log_append_commit(struct fts_expunge_log_append_ctx **_ctx) { return fts_expunge_log_append_finalise(_ctx, TRUE); } int fts_expunge_log_append_abort(struct fts_expunge_log_append_ctx **_ctx) { return fts_expunge_log_append_finalise(_ctx, FALSE); } struct fts_expunge_log_read_ctx * fts_expunge_log_read_begin(struct fts_expunge_log *log) { struct fts_expunge_log_read_ctx *ctx; ctx = i_new(struct fts_expunge_log_read_ctx, 1); ctx->log = log; if (fts_expunge_log_reopen_if_needed(log, FALSE) < 0) ctx->failed = TRUE; else if (log->fd != -1) ctx->input = i_stream_create_fd(log->fd, (size_t)-1, FALSE); ctx->unlink = TRUE; return ctx; } static bool fts_expunge_log_record_size_is_valid(const struct fts_expunge_log_record *rec, unsigned int *uids_size_r) { if (rec->record_size < sizeof(*rec) + sizeof(uint32_t)*3) return FALSE; *uids_size_r = rec->record_size - sizeof(*rec) - sizeof(uint32_t); return *uids_size_r % sizeof(uint32_t)*2 == 0; } static void fts_expunge_log_read_failure(struct fts_expunge_log_read_ctx *ctx, unsigned int wanted_size) { size_t size; if (ctx->input->stream_errno != 0) { ctx->failed = TRUE; i_error("read(%s) failed: %s", ctx->log->path, i_stream_get_error(ctx->input)); } else { size = i_stream_get_data_size(ctx->input); ctx->corrupted = TRUE; i_error("Corrupted fts expunge log %s: " "Unexpected EOF (read %"PRIuSIZE_T" / %u bytes)", ctx->log->path, size, wanted_size); } } const struct fts_expunge_log_read_record * fts_expunge_log_read_next(struct fts_expunge_log_read_ctx *ctx) { const unsigned char *data; const struct fts_expunge_log_record *rec; unsigned int uids_size; size_t size; uint32_t checksum; if (ctx->input == NULL) return NULL; /* initial read to try to get the record */ (void)i_stream_read_data(ctx->input, &data, &size, IO_BLOCK_SIZE); if (size == 0 && ctx->input->stream_errno == 0) { /* expected EOF - mark the file as read by unlinking it */ if (ctx->unlink) i_unlink_if_exists(ctx->log->path); /* try reading again, in case something new was written */ i_stream_sync(ctx->input); (void)i_stream_read_data(ctx->input, &data, &size, IO_BLOCK_SIZE); } if (size < sizeof(*rec)) { if (size == 0 && ctx->input->stream_errno == 0) { /* expected EOF */ return NULL; } fts_expunge_log_read_failure(ctx, sizeof(*rec)); return NULL; } rec = (const void *)data; if (!fts_expunge_log_record_size_is_valid(rec, &uids_size)) { ctx->corrupted = TRUE; i_error("Corrupted fts expunge log %s: " "Invalid record size: %u", ctx->log->path, rec->record_size); return NULL; } /* read the entire record */ while (size < rec->record_size) { if (i_stream_read_data(ctx->input, &data, &size, rec->record_size-1) < 0) { fts_expunge_log_read_failure(ctx, rec->record_size); return NULL; } rec = (const void *)data; } /* verify that the record checksum is valid */ checksum = crc32_data(&rec->record_size, rec->record_size - sizeof(rec->checksum)); if (checksum != rec->checksum) { ctx->corrupted = TRUE; i_error("Corrupted fts expunge log %s: " "Record checksum mismatch: %u != %u", ctx->log->path, checksum, rec->checksum); return NULL; } memcpy(ctx->read_rec.mailbox_guid, rec->guid, sizeof(ctx->read_rec.mailbox_guid)); /* create the UIDs array by pointing it directly into input stream's buffer */ buffer_create_from_const_data(&ctx->buffer, rec + 1, uids_size); array_create_from_buffer(&ctx->read_rec.uids, &ctx->buffer, sizeof(struct seq_range)); i_stream_skip(ctx->input, rec->record_size); return &ctx->read_rec; } int fts_expunge_log_read_end(struct fts_expunge_log_read_ctx **_ctx) { struct fts_expunge_log_read_ctx *ctx = *_ctx; int ret = ctx->failed ? -1 : (ctx->corrupted ? 0 : 1); *_ctx = NULL; if (ctx->corrupted) { if (ctx->unlink) i_unlink_if_exists(ctx->log->path); } if (ctx->input != NULL) i_stream_unref(&ctx->input); i_free(ctx); return ret; } int fts_expunge_log_flatten(const char *path, struct fts_expunge_log_append_ctx **flattened_r) { struct fts_expunge_log *read; struct fts_expunge_log_read_ctx *read_ctx; const struct fts_expunge_log_read_record *record; struct fts_expunge_log_append_ctx *append; int ret; i_assert(path != NULL && flattened_r != NULL); read = fts_expunge_log_init(path); read_ctx = fts_expunge_log_read_begin(read); read_ctx->unlink = FALSE; append = fts_expunge_log_append_begin(NULL); while((record = fts_expunge_log_read_next(read_ctx)) != NULL) { fts_expunge_log_append_record(append, record); } if ((ret = fts_expunge_log_read_end(&read_ctx)) > 0) *flattened_r = append; fts_expunge_log_deinit(&read); return ret; } bool fts_expunge_log_contains(const struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, uint32_t uid) { const struct fts_expunge_log_mailbox *mailbox; const uint8_t *guid_p = mailbox_guid; mailbox = hash_table_lookup(ctx->mailboxes, guid_p); if (mailbox == NULL) return FALSE; return seq_range_exists(&mailbox->uids, uid); } int fts_expunge_log_append_remove(struct fts_expunge_log_append_ctx *from, const struct fts_expunge_log_read_record *record) { const uint8_t *guid_p = record->mailbox_guid; struct fts_expunge_log_mailbox *mailbox = hash_table_lookup(from->mailboxes, guid_p); if (mailbox == NULL) return 0; /* may only remove things that exist */ mailbox->uids_count -= seq_range_array_remove_seq_range(&mailbox->uids, &record->uids); return 1; } int fts_expunge_log_subtract(struct fts_expunge_log_append_ctx *from, struct fts_expunge_log *subtract) { unsigned int failures = 0; struct fts_expunge_log_read_ctx *read_ctx = fts_expunge_log_read_begin(subtract); read_ctx->unlink = FALSE; const struct fts_expunge_log_read_record *record; while ((record = fts_expunge_log_read_next(read_ctx)) != NULL) { if (fts_expunge_log_append_remove(from, record) <= 0) failures++; } if (failures > 0) i_warning("fts: Expunge log subtract ignored %u nonexistent mailbox GUIDs", failures); return fts_expunge_log_read_end(&read_ctx); } /* It could be argued that somehow adding a log (file) to the append context and then calling the _write() helper would be easier. But then there's the _commit() vs. _abort() cleanup that would need to be addressed. Just creating a copy is simpler. */ int fts_expunge_log_flat_write(const struct fts_expunge_log_append_ctx *read_log, const char *path) { int ret; struct fts_expunge_log *nlog = fts_expunge_log_init(path); struct fts_expunge_log_append_ctx *nappend = fts_expunge_log_append_begin(nlog); struct hash_iterate_context *iter; uint8_t *guid_p; struct fts_expunge_log_mailbox *mailbox; iter = hash_table_iterate_init(read_log->mailboxes); while (hash_table_iterate(iter, read_log->mailboxes, &guid_p, &mailbox)) fts_expunge_log_append_mailbox_record(nappend, mailbox); hash_table_iterate_deinit(&iter); ret = fts_expunge_log_append_commit(&nappend); fts_expunge_log_deinit(&nlog); return ret; } dovecot-2.2.33.2/src/plugins/fts/doveadm-fts.c0000644000175000017500000002762513123174404015761 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-util.h" #include "mail-namespace.h" #include "mail-search.h" #include "mailbox-list-iter.h" #include "fts-tokenizer.h" #include "fts-filter.h" #include "fts-language.h" #include "fts-storage.h" #include "fts-search-args.h" #include "fts-user.h" #include "doveadm-print.h" #include "doveadm-mail.h" #include "doveadm-mailbox-list-iter.h" #include "doveadm-fts.h" const char *doveadm_fts_plugin_version = DOVECOT_ABI_VERSION; struct fts_tokenize_cmd_context { struct doveadm_mail_cmd_context ctx; const char *language; const char *tokens; }; static int cmd_search_box(struct doveadm_mail_cmd_context *ctx, const struct mailbox_info *info) { struct mailbox *box; struct fts_backend *backend; struct fts_result result; int ret = 0; backend = fts_list_backend(info->ns->list); if (backend == NULL) { i_error("fts not enabled for %s", info->vname); ctx->exit_code = EX_CONFIG; return -1; } i_zero(&result); i_array_init(&result.definite_uids, 16); i_array_init(&result.maybe_uids, 16); i_array_init(&result.scores, 16); box = mailbox_alloc(info->ns->list, info->vname, 0); if (fts_backend_lookup(backend, box, ctx->search_args->args, FTS_LOOKUP_FLAG_AND_ARGS, &result) < 0) { i_error("fts lookup failed"); doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); ret = -1; } else { printf("%s: ", info->vname); if (array_count(&result.definite_uids) == 0) printf("no results\n"); else T_BEGIN { string_t *str = t_str_new(128); imap_write_seq_range(str, &result.definite_uids); printf("%s\n", str_c(str)); } T_END; if (array_count(&result.maybe_uids) > 0) T_BEGIN { string_t *str = t_str_new(128); imap_write_seq_range(str, &result.maybe_uids); printf(" - maybe: %s\n", str_c(str)); } T_END; fts_backend_lookup_done(backend); } mailbox_free(&box); array_free(&result.definite_uids); array_free(&result.maybe_uids); array_free(&result.scores); return ret; } static int cmd_fts_lookup_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const enum mailbox_list_iter_flags iter_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct doveadm_mailbox_list_iter *iter; const struct mailbox_info *info; int ret = 0; iter = doveadm_mailbox_list_iter_init(ctx, user, ctx->search_args, iter_flags); while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (cmd_search_box(ctx, info) < 0) ret = -1; } T_END; if (doveadm_mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static void cmd_fts_lookup_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("fts lookup"); ctx->search_args = doveadm_mail_build_search_args(args); } static struct doveadm_mail_cmd_context * cmd_fts_lookup_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_fts_lookup_run; ctx->v.init = cmd_fts_lookup_init; return ctx; } static int cmd_fts_expand_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct mailbox *box; struct fts_backend *backend; string_t *str = t_str_new(128); backend = fts_list_backend(ns->list); if (backend == NULL) { i_error("fts not enabled for INBOX"); ctx->exit_code = EX_CONFIG; return -1; } box = mailbox_alloc(ns->list, "INBOX", 0); mail_search_args_init(ctx->search_args, box, FALSE, NULL); if (fts_search_args_expand(backend, ctx->search_args) < 0) i_fatal("Couldn't expand search args"); mail_search_args_to_cmdline(str, ctx->search_args->args); printf("%s\n", str_c(str)); mailbox_free(&box); return 0; } static void cmd_fts_expand_init(struct doveadm_mail_cmd_context *ctx, const char *const args[]) { if (args[0] == NULL) doveadm_mail_help_name("fts expand"); ctx->search_args = doveadm_mail_build_search_args(args); } static struct doveadm_mail_cmd_context * cmd_fts_expand_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_fts_expand_run; ctx->v.init = cmd_fts_expand_init; return ctx; } static int cmd_fts_tokenize_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) { struct fts_tokenize_cmd_context *ctx = (struct fts_tokenize_cmd_context *)_ctx; struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); struct fts_backend *backend; struct fts_user_language *user_lang; const struct fts_language *lang = NULL; int ret, ret2; bool final = FALSE; backend = fts_list_backend(ns->list); if (backend == NULL) { i_error("fts not enabled for INBOX"); _ctx->exit_code = EX_CONFIG; return -1; } if (ctx->language == NULL) { struct fts_language_list *lang_list = fts_user_get_language_list(user); enum fts_language_result result; result = fts_language_detect(lang_list, (const unsigned char *)ctx->tokens, strlen(ctx->tokens), &lang); if (lang == NULL) lang = fts_language_list_get_first(lang_list); switch (result) { case FTS_LANGUAGE_RESULT_SHORT: i_warning("Text too short, can't detect its language - assuming %s", lang->name); break; case FTS_LANGUAGE_RESULT_UNKNOWN: i_warning("Can't detect its language - assuming %s", lang->name); break; case FTS_LANGUAGE_RESULT_OK: break; case FTS_LANGUAGE_RESULT_ERROR: i_error("Language detection library initialization failed"); _ctx->exit_code = EX_CONFIG; return -1; default: i_unreached(); } } else { lang = fts_language_find(ctx->language); if (lang == NULL) { i_error("Unknown language: %s", ctx->language); _ctx->exit_code = EX_USAGE; return -1; } } user_lang = fts_user_language_find(user, lang); if (user_lang == NULL) { i_error("Language not enabled for user: %s", ctx->language); _ctx->exit_code = EX_USAGE; return -1; } fts_tokenizer_reset(user_lang->index_tokenizer); for (;;) { const char *token, *error; if (!final) { ret = fts_tokenizer_next(user_lang->index_tokenizer, (const unsigned char *)ctx->tokens, strlen(ctx->tokens), &token, &error); } else { ret = fts_tokenizer_final(user_lang->index_tokenizer, &token, &error); } if (ret < 0) break; if (ret > 0 && user_lang->filter != NULL) { ret2 = fts_filter_filter(user_lang->filter, &token, &error); if (ret2 > 0) doveadm_print(token); else if (ret2 < 0) i_error("Couldn't create indexable tokens: %s", error); } if (ret == 0) { if (final) break; final = TRUE; } } return 0; } static void cmd_fts_tokenize_init(struct doveadm_mail_cmd_context *_ctx, const char *const args[]) { struct fts_tokenize_cmd_context *ctx = (struct fts_tokenize_cmd_context *)_ctx; if (args[0] == NULL) doveadm_mail_help_name("fts tokenize"); ctx->tokens = p_strdup(_ctx->pool, t_strarray_join(args, " ")); doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); doveadm_print_header("token", "token", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); } static bool cmd_fts_tokenize_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) { struct fts_tokenize_cmd_context *ctx = (struct fts_tokenize_cmd_context *)_ctx; switch (c) { case 'l': ctx->language = p_strdup(_ctx->pool, optarg); break; default: return FALSE; } return TRUE; } static struct doveadm_mail_cmd_context * cmd_fts_tokenize_alloc(void) { struct fts_tokenize_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct fts_tokenize_cmd_context); ctx->ctx.v.run = cmd_fts_tokenize_run; ctx->ctx.v.init = cmd_fts_tokenize_init; ctx->ctx.v.parse_arg = cmd_fts_tokenize_parse_arg; ctx->ctx.getopt_args = "l"; return &ctx->ctx; } static int fts_namespace_find(struct mail_user *user, const char *ns_prefix, struct mail_namespace **ns_r) { struct mail_namespace *ns; if (ns_prefix == NULL) ns = mail_namespace_find_inbox(user->namespaces); else { ns = mail_namespace_find_prefix(user->namespaces, ns_prefix); if (ns == NULL) { i_error("Namespace prefix not found: %s", ns_prefix); return -1; } } if (fts_list_backend(ns->list) == NULL) { i_error("fts not enabled for user's namespace %s", ns_prefix != NULL ? ns_prefix : "INBOX"); return -1; } *ns_r = ns; return 0; } static int cmd_fts_optimize_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const char *ns_prefix = ctx->args[0]; struct mail_namespace *ns; struct fts_backend *backend; if (fts_namespace_find(user, ns_prefix, &ns) < 0) { doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } backend = fts_list_backend(ns->list); if (fts_backend_optimize(backend) < 0) { i_error("fts optimize failed"); doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); return -1; } return 0; } static void cmd_fts_optimize_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (str_array_length(args) > 1) doveadm_mail_help_name("fts optimize"); } static struct doveadm_mail_cmd_context * cmd_fts_optimize_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_fts_optimize_run; ctx->v.init = cmd_fts_optimize_init; return ctx; } static int cmd_fts_rescan_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) { const char *ns_prefix = ctx->args[0]; struct mail_namespace *ns; struct fts_backend *backend; if (fts_namespace_find(user, ns_prefix, &ns) < 0) { doveadm_mail_failed_error(ctx, MAIL_ERROR_NOTFOUND); return -1; } backend = fts_list_backend(ns->list); if (fts_backend_rescan(backend) < 0) { i_error("fts rescan failed"); doveadm_mail_failed_error(ctx, MAIL_ERROR_TEMP); return -1; } return 0; } static void cmd_fts_rescan_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, const char *const args[]) { if (str_array_length(args) > 1) doveadm_mail_help_name("fts rescan"); } static struct doveadm_mail_cmd_context * cmd_fts_rescan_alloc(void) { struct doveadm_mail_cmd_context *ctx; ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); ctx->v.run = cmd_fts_rescan_run; ctx->v.init = cmd_fts_rescan_init; return ctx; } static struct doveadm_cmd_ver2 fts_commands[] = { { .name = "fts lookup", .mail_cmd = cmd_fts_lookup_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fts expand", .mail_cmd = cmd_fts_expand_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fts tokenize", .mail_cmd = cmd_fts_tokenize_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('l', "language", CMD_PARAM_STR, 0) DOVEADM_CMD_PARAM('\0', "text", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fts optimize", .mail_cmd = cmd_fts_optimize_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "namespace", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, { .name = "fts rescan", .mail_cmd = cmd_fts_rescan_alloc, .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[]", DOVEADM_CMD_PARAMS_START DOVEADM_CMD_MAIL_COMMON DOVEADM_CMD_PARAM('\0', "namespace", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) DOVEADM_CMD_PARAMS_END }, }; void doveadm_fts_plugin_init(struct module *module ATTR_UNUSED) { unsigned int i; for (i = 0; i < N_ELEMENTS(fts_commands); i++) doveadm_cmd_register_ver2(&fts_commands[i]); doveadm_dump_fts_expunge_log_init(); } void doveadm_fts_plugin_deinit(void) { } dovecot-2.2.33.2/src/plugins/fts/fts-storage.c0000644000175000017500000006203513165463624016013 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "net.h" #include "str.h" #include "strescape.h" #include "write-full.h" #include "wildcard-match.h" #include "mail-search-build.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "fts-api-private.h" #include "fts-tokenizer.h" #include "fts-indexer.h" #include "fts-build-mail.h" #include "fts-search-serialize.h" #include "fts-plugin.h" #include "fts-storage.h" #define FTS_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_storage_module) #define FTS_MAIL_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_mail_module) #define FTS_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_mailbox_list_module) #define INDEXER_SOCKET_NAME "indexer" #define INDEXER_HANDSHAKE "VERSION\tindexer\t1\t0\n" struct fts_mailbox_list { union mailbox_list_module_context module_ctx; struct fts_backend *backend; const char *backend_name; struct fts_backend_update_context *update_ctx; unsigned int update_ctx_refcount; bool failed:1; }; struct fts_mailbox { union mailbox_module_context module_ctx; struct fts_backend_update_context *sync_update_ctx; bool fts_mailbox_excluded; }; struct fts_transaction_context { union mailbox_transaction_module_context module_ctx; struct fts_scores *scores; uint32_t next_index_seq; uint32_t highest_virtual_uid; unsigned int precache_extra_count; unsigned int precached:1; unsigned int mails_saved:1; unsigned int failed:1; }; struct fts_mail { union mail_module_context module_ctx; char score[30]; unsigned int virtual_mail:1; }; static MODULE_CONTEXT_DEFINE_INIT(fts_storage_module, &mail_storage_module_register); static MODULE_CONTEXT_DEFINE_INIT(fts_mail_module, &mail_module_register); static MODULE_CONTEXT_DEFINE_INIT(fts_mailbox_list_module, &mailbox_list_module_register); static int fts_mailbox_get_last_cached_seq(struct mailbox *box, uint32_t *seq_r) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(box->list); uint32_t seq1, seq2, last_uid; if (fts_backend_get_last_uid(flist->backend, box, &last_uid) < 0) { mail_storage_set_internal_error(box->storage); return -1; } if (last_uid == 0) *seq_r = 0; else { mailbox_get_seq_range(box, 1, last_uid, &seq1, &seq2); *seq_r = seq2; } return 0; } static int fts_mailbox_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r) { struct fts_mailbox *fbox = FTS_CONTEXT(box); uint32_t seq; if (fbox->module_ctx.super.get_status(box, items, status_r) < 0) return -1; if ((items & STATUS_LAST_CACHED_SEQ) != 0) { if (fts_mailbox_get_last_cached_seq(box, &seq) < 0) return -1; /* use whichever is smaller */ if (status_r->last_cached_seq > seq) status_r->last_cached_seq = seq; } return 0; } static void fts_scores_unref(struct fts_scores **_scores) { struct fts_scores *scores = *_scores; *_scores = NULL; if (--scores->refcount == 0) { array_free(&scores->score_map); i_free(scores); } } static void fts_try_build_init(struct mail_search_context *ctx, struct fts_search_context *fctx) { int ret; i_assert(!fts_backend_is_updating(fctx->backend)); ret = fts_indexer_init(fctx->backend, ctx->transaction->box, &fctx->indexer_ctx); if (ret < 0) return; if (ret == 0) { /* the index was up to date */ fts_search_lookup(fctx); } else { /* hide "searching" notifications while building index */ ctx->progress_hidden = TRUE; } } static bool fts_want_build_args(const struct mail_search_arg *args) { /* we want to update index only when searching from message body. it's not worth the wait for searching only from headers, which could be in cache file already */ for (; args != NULL; args = args->next) { switch (args->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: if (fts_want_build_args(args->value.subargs)) return TRUE; break; case SEARCH_BODY: case SEARCH_TEXT: if (!args->no_fts) return TRUE; break; default: break; } } return FALSE; } static bool fts_args_have_fuzzy(const struct mail_search_arg *args) { for (; args != NULL; args = args->next) { if (args->fuzzy) return TRUE; switch (args->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: if (fts_args_have_fuzzy(args->value.subargs)) return TRUE; break; default: break; } } return FALSE; } static struct mail_search_context * fts_mailbox_search_init(struct mailbox_transaction_context *t, struct mail_search_args *args, const enum mail_sort_type *sort_program, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct fts_transaction_context *ft = FTS_CONTEXT(t); struct fts_mailbox *fbox = FTS_CONTEXT(t->box); struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(t->box->list); struct mail_search_context *ctx; struct fts_search_context *fctx; ctx = fbox->module_ctx.super.search_init(t, args, sort_program, wanted_fields, wanted_headers); if (!fts_backend_can_lookup(flist->backend, args->args)) return ctx; fctx = i_new(struct fts_search_context, 1); fctx->box = t->box; fctx->backend = flist->backend; fctx->t = t; fctx->args = args; fctx->result_pool = pool_alloconly_create("fts results", 1024*64); fctx->orig_matches = buffer_create_dynamic(default_pool, 64); fctx->virtual_mailbox = t->box->virtual_vfuncs != NULL; fctx->enforced = mail_user_plugin_getenv(t->box->storage->user, "fts_enforced") != NULL; i_array_init(&fctx->levels, 8); fctx->scores = i_new(struct fts_scores, 1); fctx->scores->refcount = 1; i_array_init(&fctx->scores->score_map, 64); MODULE_CONTEXT_SET(ctx, fts_storage_module, fctx); /* FIXME: we'll assume that all the args are fuzzy. not good, but would require much more work to fix it. */ if (!fts_args_have_fuzzy(args->args) && mail_user_plugin_getenv(t->box->storage->user, "fts_no_autofuzzy") != NULL) fctx->flags |= FTS_LOOKUP_FLAG_NO_AUTO_FUZZY; /* transaction contains the last search's scores. they can be queried later with mail_get_special() */ if (ft->scores != NULL) fts_scores_unref(&ft->scores); ft->scores = fctx->scores; ft->scores->refcount++; if (fctx->enforced || fts_want_build_args(args->args)) fts_try_build_init(ctx, fctx); else fts_search_lookup(fctx); return ctx; } static bool fts_mailbox_build_continue(struct mail_search_context *ctx) { struct fts_search_context *fctx = FTS_CONTEXT(ctx); int ret; ret = fts_indexer_more(fctx->indexer_ctx); if (ret == 0) return FALSE; /* indexing finished */ ctx->progress_hidden = FALSE; if (fts_indexer_deinit(&fctx->indexer_ctx) < 0) ret = -1; if (ret > 0) fts_search_lookup(fctx); if (ret < 0) { /* if indexing timed out, it probably means that the mailbox is still being indexed, but it's a large mailbox and it takes a while. in this situation we'll simply abort the search. if indexing failed for any other reason, just fallback to searching the slow way. */ fctx->indexing_timed_out = mailbox_get_last_mail_error(fctx->box) == MAIL_ERROR_INUSE; } return TRUE; } static bool fts_mailbox_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r) { struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box); struct fts_search_context *fctx = FTS_CONTEXT(ctx); struct fts_transaction_context *ft = FTS_CONTEXT(ctx->transaction); if (fctx == NULL && ft->failed) { /* precaching already failed - stop now instead of potentially going through the same failure for all the mails */ return FALSE; } if (fctx != NULL && fctx->indexer_ctx != NULL) { /* this command is still building the indexes */ if (!fts_mailbox_build_continue(ctx)) { *tryagain_r = TRUE; return FALSE; } if (fctx->indexing_timed_out) { *tryagain_r = FALSE; return FALSE; } } if (fctx != NULL && !fctx->fts_lookup_success && fctx->enforced) return FALSE; return fbox->module_ctx.super. search_next_nonblock(ctx, mail_r, tryagain_r); } static void fts_search_apply_results_level(struct mail_search_context *ctx, struct mail_search_arg *args, unsigned int *idx) { struct fts_search_context *fctx = FTS_CONTEXT(ctx); const struct fts_search_level *level; level = array_idx(&fctx->levels, *idx); if (array_is_created(&level->definite_seqs) && seq_range_exists(&level->definite_seqs, ctx->seq)) fts_search_deserialize_add_matches(args, level->args_matches); else if (!array_is_created(&level->maybe_seqs) || !seq_range_exists(&level->maybe_seqs, ctx->seq)) fts_search_deserialize_add_nonmatches(args, level->args_matches); for (; args != NULL; args = args->next) { if (args->type != SEARCH_OR && args->type != SEARCH_SUB) continue; *idx += 1; fts_search_apply_results_level(ctx, args->value.subargs, idx); } } static bool fts_mailbox_search_next_update_seq(struct mail_search_context *ctx) { struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box); struct fts_search_context *fctx = FTS_CONTEXT(ctx); unsigned int idx; if (fctx == NULL || !fctx->fts_lookup_success) { /* fts lookup not done for this search */ if (fctx != NULL && fctx->indexing_timed_out) return FALSE; return fbox->module_ctx.super.search_next_update_seq(ctx); } /* restore original [non]matches */ fts_search_deserialize(ctx->args->args, fctx->orig_matches); if (!fbox->module_ctx.super.search_next_update_seq(ctx)) return FALSE; if (ctx->seq >= fctx->first_unindexed_seq) { /* we've not indexed this far */ return TRUE; } /* apply [non]matches based on the FTS lookup results */ idx = 0; fts_search_apply_results_level(ctx, ctx->args->args, &idx); return TRUE; } static int fts_mailbox_search_deinit(struct mail_search_context *ctx) { struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box); struct fts_transaction_context *ft = FTS_CONTEXT(ctx->transaction); struct fts_search_context *fctx = FTS_CONTEXT(ctx); int ret = 0; if (fctx != NULL) { if (fctx->indexer_ctx != NULL) { if (fts_indexer_deinit(&fctx->indexer_ctx) < 0) ft->failed = TRUE; } if (fctx->indexing_timed_out) ret = -1; if (!fctx->fts_lookup_success && fctx->enforced) { /* FTS lookup failed and we didn't want to fallback to opening all the mails and searching manually */ mail_storage_set_internal_error(ctx->transaction->box->storage); ret = -1; } buffer_free(&fctx->orig_matches); array_free(&fctx->levels); pool_unref(&fctx->result_pool); fts_scores_unref(&fctx->scores); i_free(fctx); } else { if (ft->failed) ret = -1; } if (fbox->module_ctx.super.search_deinit(ctx) < 0) ret = -1; return ret; } static int fts_score_cmp(const uint32_t *uid, const struct fts_score_map *score) { return *uid < score->uid ? -1 : (*uid > score->uid ? 1 : 0); } static int fts_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct mail_private *mail = (struct mail_private *)_mail; struct fts_mail *fmail = FTS_MAIL_CONTEXT(mail); struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction); const struct fts_score_map *scores; if (field != MAIL_FETCH_SEARCH_RELEVANCY || ft->scores == NULL) scores = NULL; else { scores = array_bsearch(&ft->scores->score_map, &_mail->uid, fts_score_cmp); } if (scores != NULL) { i_assert(scores->uid == _mail->uid); (void)i_snprintf(fmail->score, sizeof(fmail->score), "%f", scores->score); *value_r = fmail->score; return 0; } return fmail->module_ctx.super.get_special(_mail, field, value_r); } static int fts_mail_precache_range(struct mailbox_transaction_context *trans, struct fts_backend_update_context *update_ctx, uint32_t seq1, uint32_t seq2, unsigned int *extra_count) { struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; int ret = 0; search_args = mail_search_build_init(); mail_search_build_add_seqset(search_args, seq1, seq2); ctx = mailbox_search_init(trans, search_args, NULL, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { if (fts_build_mail(update_ctx, mail) < 0) { mail_storage_set_internal_error(trans->box->storage); ret = -1; break; } mail_precache(mail); *extra_count += 1; } if (mailbox_search_deinit(&ctx) < 0) ret = -1; return ret; } static int fts_mail_precache_init(struct mail *_mail) { struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction); struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(_mail->box->list); uint32_t last_seq; if (fts_mailbox_get_last_cached_seq(_mail->box, &last_seq) < 0) return -1; ft->precached = TRUE; ft->next_index_seq = last_seq + 1; if (flist->update_ctx == NULL) flist->update_ctx = fts_backend_update_init(flist->backend); flist->update_ctx_refcount++; return 0; } static void fts_mail_index(struct mail *_mail) { struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction); struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(_mail->box->list); if (ft->failed) return; if (!ft->precached) { if (fts_mail_precache_init(_mail) < 0) { ft->failed = TRUE; return; } } if (ft->next_index_seq < _mail->seq) { /* most likely a virtual mailbox. we'll first need to index all mails up to the current one. */ fts_backend_update_set_mailbox(flist->update_ctx, _mail->box); if (fts_mail_precache_range(_mail->transaction, flist->update_ctx, ft->next_index_seq, _mail->seq-1, &ft->precache_extra_count) < 0) { ft->failed = TRUE; return; } } if (ft->next_index_seq == _mail->seq) { fts_backend_update_set_mailbox(flist->update_ctx, _mail->box); if (fts_build_mail(flist->update_ctx, _mail) < 0) { mail_storage_set_internal_error(_mail->box->storage); ft->failed = TRUE; } ft->next_index_seq = _mail->seq + 1; } } static void fts_mail_precache(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct fts_mail *fmail = FTS_MAIL_CONTEXT(mail); struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction); fmail->module_ctx.super.precache(_mail); if (fmail->virtual_mail) { if (ft->highest_virtual_uid < _mail->uid) ft->highest_virtual_uid = _mail->uid; } else T_BEGIN { fts_mail_index(_mail); } T_END; } void fts_mail_allocated(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; struct mail_vfuncs *v = mail->vlast; struct fts_mailbox *fbox = FTS_CONTEXT(_mail->box); struct fts_mail *fmail; if (fbox == NULL) return; fmail = p_new(mail->pool, struct fts_mail, 1); fmail->module_ctx.super = *v; mail->vlast = &fmail->module_ctx.super; fmail->virtual_mail = _mail->box->virtual_vfuncs != NULL; v->get_special = fts_mail_get_special; v->precache = fts_mail_precache; MODULE_CONTEXT_SET(mail, fts_mail_module, fmail); } static struct mailbox_transaction_context * fts_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct fts_mailbox *fbox = FTS_CONTEXT(box); struct mailbox_transaction_context *t; struct fts_transaction_context *ft; ft = i_new(struct fts_transaction_context, 1); t = fbox->module_ctx.super.transaction_begin(box, flags); MODULE_CONTEXT_SET(t, fts_storage_module, ft); return t; } static int fts_transaction_end(struct mailbox_transaction_context *t, const char **error_r) { struct fts_transaction_context *ft = FTS_CONTEXT(t); struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(t->box->list); int ret = ft->failed ? -1 : 0; if (ft->failed) *error_r = "transaction context"; if (ft->precached) { i_assert(flist->update_ctx_refcount > 0); if (--flist->update_ctx_refcount == 0) { if (fts_backend_update_deinit(&flist->update_ctx) < 0) { ret = -1; *error_r = "backend deinit"; } } } else if (ft->highest_virtual_uid > 0) { if (fts_index_set_last_uid(t->box, ft->highest_virtual_uid) < 0) { ret = -1; *error_r = "index last uid setting"; } } if (ft->scores != NULL) fts_scores_unref(&ft->scores); if (ft->precache_extra_count > 0) { if (ret < 0) { i_error("fts: Failed after indexing %u extra mails internally in %s: %s", ft->precache_extra_count, t->box->vname, *error_r); } else { i_info("fts: Indexed %u extra mails internally in %s", ft->precache_extra_count, t->box->vname); } } i_free(ft); return ret; } static void fts_transaction_rollback(struct mailbox_transaction_context *t) { struct fts_mailbox *fbox = FTS_CONTEXT(t->box); const char *error; (void)fts_transaction_end(t, &error); fbox->module_ctx.super.transaction_rollback(t); } static void fts_queue_index(struct mailbox *box) { struct mail_user *user = box->storage->user; string_t *str = t_str_new(256); const char *path, *value; unsigned int max_recent_msgs; int fd; path = t_strconcat(user->set->base_dir, "/"INDEXER_SOCKET_NAME, NULL); fd = net_connect_unix(path); if (fd == -1) { i_error("net_connect_unix(%s) failed: %m", path); return; } value = mail_user_plugin_getenv(user, "fts_autoindex_max_recent_msgs"); if (value == NULL || str_to_uint(value, &max_recent_msgs) < 0) max_recent_msgs = 0; str_append(str, INDEXER_HANDSHAKE); str_append(str, "APPEND\t0\t"); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); str_append_tabescaped(str, box->vname); str_printfa(str, "\t%u", max_recent_msgs); str_append_c(str, '\t'); str_append_tabescaped(str, box->storage->user->session_id); str_append_c(str, '\n'); if (write_full(fd, str_data(str), str_len(str)) < 0) i_error("write(%s) failed: %m", path); i_close_fd(&fd); } static int fts_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) { struct fts_transaction_context *ft = FTS_CONTEXT(t); struct fts_mailbox *fbox = FTS_CONTEXT(t->box); struct mailbox *box = t->box; bool autoindex; int ret = 0; const char *error; autoindex = ft->mails_saved && !fbox->fts_mailbox_excluded && mail_user_plugin_getenv(box->storage->user, "fts_autoindex") != NULL; if (fts_transaction_end(t, &error) < 0) { mail_storage_set_error(t->box->storage, MAIL_ERROR_TEMP, t_strdup_printf("FTS transaction commit failed: %s", error)); ret = -1; } if (fbox->module_ctx.super.transaction_commit(t, changes_r) < 0) ret = -1; if (ret < 0) return -1; if (autoindex) fts_queue_index(box); return 0; } static void fts_mailbox_sync_notify(struct mailbox *box, uint32_t uid, enum mailbox_sync_type sync_type) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(box->list); struct fts_mailbox *fbox = FTS_CONTEXT(box); if (fbox->module_ctx.super.sync_notify != NULL) fbox->module_ctx.super.sync_notify(box, uid, sync_type); if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE) { if (uid == 0 && fbox->sync_update_ctx != NULL) { /* this sync is finished */ (void)fts_backend_update_deinit(&fbox->sync_update_ctx); } return; } if (fbox->sync_update_ctx == NULL) { if (fts_backend_is_updating(flist->backend)) { /* FIXME: maildir workaround - we could get here because we're building an index, which doesn't find some mail and starts syncing the mailbox.. */ return; } fbox->sync_update_ctx = fts_backend_update_init(flist->backend); fts_backend_update_set_mailbox(fbox->sync_update_ctx, box); } fts_backend_update_expunge(fbox->sync_update_ctx, uid); } static int fts_sync_deinit(struct mailbox_sync_context *ctx, struct mailbox_sync_status *status_r) { struct mailbox *box = ctx->box; struct fts_mailbox *fbox = FTS_CONTEXT(box); struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(box->list); bool optimize; int ret = 0; optimize = (ctx->flags & (MAILBOX_SYNC_FLAG_FORCE_RESYNC | MAILBOX_SYNC_FLAG_OPTIMIZE)) != 0; if (fbox->module_ctx.super.sync_deinit(ctx, status_r) < 0) return -1; ctx = NULL; if (optimize) { if (fts_backend_optimize(flist->backend) < 0) { mail_storage_set_critical(box->storage, "FTS optimize for mailbox %s failed", box->vname); ret = -1; } } return ret; } static int fts_save_finish(struct mail_save_context *ctx) { struct fts_transaction_context *ft = FTS_CONTEXT(ctx->transaction); struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box); if (fbox->module_ctx.super.save_finish(ctx) < 0) return -1; ft->mails_saved = TRUE; return 0; } static int fts_copy(struct mail_save_context *ctx, struct mail *mail) { struct fts_transaction_context *ft = FTS_CONTEXT(ctx->transaction); struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box); if (fbox->module_ctx.super.copy(ctx, mail) < 0) return -1; ft->mails_saved = TRUE; return 0; } static const char *const *fts_exclude_get_patterns(struct mail_user *user) { ARRAY_TYPE(const_string) patterns; const char *str; char set_name[21+MAX_INT_STRLEN+1]; unsigned int i; str = mail_user_plugin_getenv(user, "fts_autoindex_exclude"); if (str == NULL) return NULL; t_array_init(&patterns, 16); for (i = 2; str != NULL; i++) { array_append(&patterns, &str, 1); if (i_snprintf(set_name, sizeof(set_name), "fts_autoindex_exclude%u", i) < 0) i_unreached(); str = mail_user_plugin_getenv(user, set_name); } array_append_zero(&patterns); return array_idx(&patterns, 0); } static bool fts_autoindex_exclude_match(struct mailbox *box) { const char *const *exclude_list; unsigned int i; const struct mailbox_settings *set; const char *const *special_use; struct mail_user *user = box->storage->user; exclude_list = fts_exclude_get_patterns(user); if (exclude_list == NULL) return FALSE; set = mailbox_settings_find(mailbox_get_namespace(box), mailbox_get_vname(box)); special_use = set == NULL ? NULL : t_strsplit_spaces(set->special_use, " "); for (i = 0; exclude_list[i] != NULL; i++) { if (exclude_list[i][0] == '\\') { /* \Special-use flag */ if (special_use != NULL && str_array_icase_find(special_use, exclude_list[i])) return TRUE; } else { /* mailbox name with wildcards */ if (wildcard_match(box->name, exclude_list[i])) return TRUE; } } return FALSE; } void fts_mailbox_allocated(struct mailbox *box) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(box->list); struct mailbox_vfuncs *v = box->vlast; struct fts_mailbox *fbox; if (flist == NULL || flist->failed) return; fbox = p_new(box->pool, struct fts_mailbox, 1); fbox->module_ctx.super = *v; box->vlast = &fbox->module_ctx.super; fbox->fts_mailbox_excluded = fts_autoindex_exclude_match(box); v->get_status = fts_mailbox_get_status; v->search_init = fts_mailbox_search_init; v->search_next_nonblock = fts_mailbox_search_next_nonblock; v->search_next_update_seq = fts_mailbox_search_next_update_seq; v->search_deinit = fts_mailbox_search_deinit; v->transaction_begin = fts_transaction_begin; v->transaction_rollback = fts_transaction_rollback; v->transaction_commit = fts_transaction_commit; v->sync_notify = fts_mailbox_sync_notify; v->sync_deinit = fts_sync_deinit; v->save_finish = fts_save_finish; v->copy = fts_copy; MODULE_CONTEXT_SET(box, fts_storage_module, fbox); } static void fts_mailbox_list_deinit(struct mailbox_list *list) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(list); if (flist->backend != NULL) fts_backend_deinit(&flist->backend); flist->module_ctx.super.deinit(list); } static int fts_init_namespace(struct fts_mailbox_list *flist, struct mail_namespace *ns, const char **error_r) { struct fts_backend *backend; if (fts_backend_init(flist->backend_name, ns, error_r, &backend) < 0) { flist->failed = TRUE; return -1; } flist->backend = backend; if ((flist->backend->flags & FTS_BACKEND_FLAG_FUZZY_SEARCH) != 0) ns->user->fuzzy_search = TRUE; return 0; } void fts_mail_namespaces_added(struct mail_namespace *ns) { while(ns != NULL) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(ns->list); const char *error; if (flist != NULL && !flist->failed && flist->backend == NULL && fts_init_namespace(flist, ns, &error) < 0) { i_error("fts: Failed to initialize backend '%s': %s", flist->backend_name, error); } ns = ns->next; } } void fts_mailbox_list_created(struct mailbox_list *list) { const char *name = mail_user_plugin_getenv(list->ns->user, "fts"); const char *path; if (name == NULL || name[0] == '\0') { if (list->mail_set->mail_debug) i_debug("fts: No fts setting - plugin disabled"); return; } if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path)) { if (list->mail_set->mail_debug) { i_debug("fts: Indexes disabled for namespace '%s'", list->ns->prefix); } return; } struct fts_mailbox_list *flist; struct mailbox_list_vfuncs *v = list->vlast; flist = p_new(list->pool, struct fts_mailbox_list, 1); flist->module_ctx.super = *v; flist->backend_name = name; list->vlast = &flist->module_ctx.super; v->deinit = fts_mailbox_list_deinit; MODULE_CONTEXT_SET(list, fts_mailbox_list_module, flist); } struct fts_backend *fts_mailbox_backend(struct mailbox *box) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(box->list); return flist->backend; } struct fts_backend *fts_list_backend(struct mailbox_list *list) { struct fts_mailbox_list *flist = FTS_LIST_CONTEXT(list); return flist == NULL ? NULL : flist->backend; } dovecot-2.2.33.2/src/plugins/fts/fts-parser-html.c0000644000175000017500000000307513123174404016571 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "message-parser.h" #include "mail-html2text.h" #include "fts-parser.h" struct html_fts_parser { struct fts_parser parser; struct mail_html2text *html2text; buffer_t *output; }; static struct fts_parser * fts_parser_html_try_init(struct mail_user *user ATTR_UNUSED, const char *content_type, const char *content_disposition ATTR_UNUSED) { struct html_fts_parser *parser; if (!mail_html2text_content_type_match(content_type)) return NULL; parser = i_new(struct html_fts_parser, 1); parser->parser.v = fts_parser_html; parser->html2text = mail_html2text_init(0); parser->output = buffer_create_dynamic(default_pool, 4096); return &parser->parser; } static void fts_parser_html_more(struct fts_parser *_parser, struct message_block *block) { struct html_fts_parser *parser = (struct html_fts_parser *)_parser; if (block->size == 0) { /* finished */ return; } buffer_set_used_size(parser->output, 0); mail_html2text_more(parser->html2text, block->data, block->size, parser->output); block->data = parser->output->data; block->size = parser->output->used; } static int fts_parser_html_deinit(struct fts_parser *_parser) { struct html_fts_parser *parser = (struct html_fts_parser *)_parser; mail_html2text_deinit(&parser->html2text); buffer_free(&parser->output); i_free(parser); return 0; } struct fts_parser_vfuncs fts_parser_html = { fts_parser_html_try_init, fts_parser_html_more, fts_parser_html_deinit, NULL }; dovecot-2.2.33.2/src/plugins/fts/fts-build-mail.c0000644000175000017500000004027113165463624016364 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream.h" #include "buffer.h" #include "str.h" #include "rfc822-parser.h" #include "message-address.h" #include "message-parser.h" #include "message-decoder.h" #include "mail-storage.h" #include "index-mail.h" #include "fts-parser.h" #include "fts-user.h" #include "fts-language.h" #include "fts-tokenizer.h" #include "fts-filter.h" #include "fts-api-private.h" #include "fts-build-mail.h" /* there are other characters as well, but this doesn't have to be exact */ #define IS_WORD_WHITESPACE(c) \ ((c) == ' ' || (c) == '\t' || (c) == '\n') /* if we see a word larger than this, just go ahead and split it from wherever */ #define MAX_WORD_SIZE 1024 struct fts_mail_build_context { struct mail *mail; struct fts_backend_update_context *update_ctx; char *content_type, *content_disposition; struct fts_parser *body_parser; buffer_t *word_buf, *pending_input; struct fts_user_language *cur_user_lang; }; static int fts_build_data(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size, bool last); static void fts_build_parse_content_type(struct fts_mail_build_context *ctx, const struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *content_type; if (ctx->content_type != NULL) return; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); T_BEGIN { content_type = t_str_new(64); (void)rfc822_parse_content_type(&parser, content_type); ctx->content_type = str_lcase(i_strdup(str_c(content_type))); } T_END; } static void fts_build_parse_content_disposition(struct fts_mail_build_context *ctx, const struct message_header_line *hdr) { /* just pass it as-is to backend. */ i_free(ctx->content_disposition); ctx->content_disposition = i_strndup(hdr->full_value, hdr->full_value_len); } static void fts_parse_mail_header(struct fts_mail_build_context *ctx, const struct message_block *raw_block) { const struct message_header_line *hdr = raw_block->hdr; if (strcasecmp(hdr->name, "Content-Type") == 0) fts_build_parse_content_type(ctx, hdr); else if (strcasecmp(hdr->name, "Content-Disposition") == 0) fts_build_parse_content_disposition(ctx, hdr); } static int fts_build_unstructured_header(struct fts_mail_build_context *ctx, const struct message_header_line *hdr) { const unsigned char *data = hdr->full_value; unsigned char *buf = NULL; unsigned int i; int ret; /* @UNSAFE: if there are any NULs, replace them with spaces */ for (i = 0; i < hdr->full_value_len; i++) { if (data[i] == '\0') { if (buf == NULL) { buf = i_malloc(hdr->full_value_len); memcpy(buf, data, i); data = buf; } buf[i] = ' '; } else if (buf != NULL) { buf[i] = data[i]; } } ret = fts_build_data(ctx, data, hdr->full_value_len, TRUE); i_free(buf); return ret; } static bool data_has_8bit(const unsigned char *data, size_t size) { size_t i; for (i = 0; i < size; i++) { if ((data[i] & 0x80) != 0) return TRUE; } return FALSE; } static void fts_mail_build_ctx_set_lang(struct fts_mail_build_context *ctx, struct fts_user_language *user_lang) { i_assert(user_lang != NULL); ctx->cur_user_lang = user_lang; /* reset tokenizer between fields - just to be sure no state leaks between fields (especially if previous indexing had failed) */ fts_tokenizer_reset(user_lang->index_tokenizer); } static void fts_build_tokenized_hdr_update_lang(struct fts_mail_build_context *ctx, const struct message_header_line *hdr) { /* Headers that don't contain any human language will only be translated to lowercase - no stemming or other filtering. There's unfortunately no pefect way of detecting which headers contain human languages, so we have a list of some hardcoded header names and we'll also assume that if there's any 8bit content it's a human language. */ if (fts_header_has_language(hdr->name) || data_has_8bit(hdr->full_value, hdr->full_value_len)) ctx->cur_user_lang = NULL; else { fts_mail_build_ctx_set_lang(ctx, fts_user_get_data_lang(ctx->update_ctx->backend->ns->user)); } } static int fts_build_mail_header(struct fts_mail_build_context *ctx, const struct message_block *block) { const struct message_header_line *hdr = block->hdr; struct fts_backend_build_key key; int ret; if (hdr->eoh) return 0; /* hdr->full_value is always set because we get the block from message_decoder */ i_zero(&key); key.uid = ctx->mail->uid; key.type = block->part->physical_pos == 0 ? FTS_BACKEND_BUILD_KEY_HDR : FTS_BACKEND_BUILD_KEY_MIME_HDR; key.part = block->part; key.hdr_name = hdr->name; if ((ctx->update_ctx->backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0) fts_build_tokenized_hdr_update_lang(ctx, hdr); if (!fts_backend_update_set_build_key(ctx->update_ctx, &key)) return 0; if (!message_header_is_address(hdr->name)) { /* regular unstructured header */ ret = fts_build_unstructured_header(ctx, hdr); } else T_BEGIN { /* message address. normalize it to give better search results. */ struct message_address *addr; string_t *str; addr = message_address_parse(pool_datastack_create(), hdr->full_value, hdr->full_value_len, UINT_MAX, FALSE); str = t_str_new(hdr->full_value_len); message_address_write(str, addr); ret = fts_build_data(ctx, str_data(str), str_len(str), TRUE); } T_END; if ((ctx->update_ctx->backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0) { /* index the header name itself using data-language. */ struct fts_user_language *prev_lang = ctx->cur_user_lang; fts_mail_build_ctx_set_lang(ctx, fts_user_get_data_lang(ctx->update_ctx->backend->ns->user)); key.hdr_name = ""; if (fts_backend_update_set_build_key(ctx->update_ctx, &key)) { if (fts_build_data(ctx, (const void *)hdr->name, strlen(hdr->name), TRUE) < 0) ret = -1; } fts_mail_build_ctx_set_lang(ctx, prev_lang); } return ret; } static bool fts_build_body_begin(struct fts_mail_build_context *ctx, struct message_part *part, bool *binary_body_r) { struct mail_storage *storage; const char *content_type; struct fts_backend_build_key key; i_assert(ctx->body_parser == NULL); *binary_body_r = FALSE; i_zero(&key); key.uid = ctx->mail->uid; key.part = part; content_type = ctx->content_type != NULL ? ctx->content_type : "text/plain"; if (strncmp(content_type, "multipart/", 10) == 0) { /* multiparts are never indexed, only their contents */ return FALSE; } storage = mailbox_get_storage(ctx->mail->box); if (fts_parser_init(mail_storage_get_user(storage), content_type, ctx->content_disposition, &ctx->body_parser)) { /* extract text using the the returned parser */ *binary_body_r = TRUE; key.type = FTS_BACKEND_BUILD_KEY_BODY_PART; } else if (strncmp(content_type, "text/", 5) == 0 || strncmp(content_type, "message/", 8) == 0) { /* text body parts */ key.type = FTS_BACKEND_BUILD_KEY_BODY_PART; ctx->body_parser = fts_parser_text_init(); } else { /* possibly binary */ if ((ctx->update_ctx->backend->flags & FTS_BACKEND_FLAG_BINARY_MIME_PARTS) == 0) return FALSE; *binary_body_r = TRUE; key.type = FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY; } key.body_content_type = content_type; key.body_content_disposition = ctx->content_disposition; ctx->cur_user_lang = NULL; if (!fts_backend_update_set_build_key(ctx->update_ctx, &key)) { if (ctx->body_parser != NULL) (void)fts_parser_deinit(&ctx->body_parser); return FALSE; } return TRUE; } static int fts_build_add_tokens_with_filter(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size) { struct fts_tokenizer *tokenizer = ctx->cur_user_lang->index_tokenizer; struct fts_filter *filter = ctx->cur_user_lang->filter; const char *token, *error; int ret = 1, ret2; while (ret > 0) T_BEGIN { ret = ret2 = fts_tokenizer_next(tokenizer, data, size, &token, &error); if (ret2 > 0 && filter != NULL) ret2 = fts_filter_filter(filter, &token, &error); if (ret2 < 0) i_error("fts: Couldn't create indexable tokens: %s", error); if (ret2 > 0) { if (fts_backend_update_build_more(ctx->update_ctx, (const void *)token, strlen(token)) < 0) ret = -1; } } T_END; return ret; } static int fts_detect_language(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size, bool last, const struct fts_language **lang_r) { struct mail_user *user = ctx->update_ctx->backend->ns->user; struct fts_language_list *lang_list = fts_user_get_language_list(user); const struct fts_language *lang; switch (fts_language_detect(lang_list, data, size, &lang)) { case FTS_LANGUAGE_RESULT_SHORT: /* save the input so far and try again later */ buffer_append(ctx->pending_input, data, size); if (last) { /* we've run out of data. use the default language. */ *lang_r = fts_language_list_get_first(lang_list); return 1; } return 0; case FTS_LANGUAGE_RESULT_UNKNOWN: /* use the default language */ *lang_r = fts_language_list_get_first(lang_list); return 1; case FTS_LANGUAGE_RESULT_OK: *lang_r = lang; return 1; case FTS_LANGUAGE_RESULT_ERROR: /* internal language detection library failure (e.g. invalid config). don't index anything. */ return -1; default: i_unreached(); } } static int fts_build_tokenized(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size, bool last) { struct mail_user *user = ctx->update_ctx->backend->ns->user; const struct fts_language *lang; int ret; if (ctx->cur_user_lang != NULL) { /* we already have a language */ } else if ((ret = fts_detect_language(ctx, data, size, last, &lang)) < 0) { return -1; } else if (ret == 0) { /* wait for more data */ return 0; } else { fts_mail_build_ctx_set_lang(ctx, fts_user_language_find(user, lang)); if (ctx->pending_input->used > 0) { if (fts_build_add_tokens_with_filter(ctx, ctx->pending_input->data, ctx->pending_input->used) < 0) return -1; buffer_set_used_size(ctx->pending_input, 0); } } if (fts_build_add_tokens_with_filter(ctx, data, size) < 0) return -1; if (last) { if (fts_build_add_tokens_with_filter(ctx, NULL, 0) < 0) return -1; } return 0; } static int fts_build_full_words(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size, bool last) { size_t i; /* we'll need to send only full words to the backend */ if (ctx->word_buf != NULL && ctx->word_buf->used > 0) { /* continuing previous word */ for (i = 0; i < size; i++) { if (IS_WORD_WHITESPACE(data[i])) break; } buffer_append(ctx->word_buf, data, i); data += i; size -= i; if (size == 0 && ctx->word_buf->used < MAX_WORD_SIZE && !last) { /* word is still not finished */ return 0; } /* we have a full word, index it */ if (fts_backend_update_build_more(ctx->update_ctx, ctx->word_buf->data, ctx->word_buf->used) < 0) return -1; buffer_set_used_size(ctx->word_buf, 0); } /* find the boundary for last word */ if (last) i = size; else { for (i = size; i > 0; i--) { if (IS_WORD_WHITESPACE(data[i-1])) break; } } if (fts_backend_update_build_more(ctx->update_ctx, data, i) < 0) return -1; if (i < size) { if (ctx->word_buf == NULL) { ctx->word_buf = buffer_create_dynamic(default_pool, 128); } buffer_append(ctx->word_buf, data + i, size - i); } return 0; } static int fts_build_data(struct fts_mail_build_context *ctx, const unsigned char *data, size_t size, bool last) { if ((ctx->update_ctx->backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0) { return fts_build_tokenized(ctx, data, size, last); } else if ((ctx->update_ctx->backend->flags & FTS_BACKEND_FLAG_BUILD_FULL_WORDS) != 0) { return fts_build_full_words(ctx, data, size, last); } else { return fts_backend_update_build_more(ctx->update_ctx, data, size); } } static int fts_build_body_block(struct fts_mail_build_context *ctx, const struct message_block *block, bool last) { i_assert(block->hdr == NULL); return fts_build_data(ctx, block->data, block->size, last); } static int fts_body_parser_finish(struct fts_mail_build_context *ctx) { struct message_block block; int ret = 0; do { i_zero(&block); fts_parser_more(ctx->body_parser, &block); if (fts_build_body_block(ctx, &block, FALSE) < 0) { ret = -1; break; } } while (block.size > 0); if (fts_parser_deinit(&ctx->body_parser) < 0) ret = -1; return ret; } static int fts_build_mail_real(struct fts_backend_update_context *update_ctx, struct mail *mail) { struct fts_mail_build_context ctx; struct istream *input; struct message_parser_ctx *parser; struct message_decoder_context *decoder; struct message_block raw_block, block; struct message_part *prev_part, *parts; bool skip_body = FALSE, body_part = FALSE, body_added = FALSE; bool binary_body; const char *error; int ret; if (mail_get_stream_because(mail, NULL, NULL, "fts indexing", &input) < 0) { if (mail->expunged) return 0; i_error("Failed to read mailbox %s mail UID=%u stream: %s", mailbox_get_vname(mail->box), mail->uid, mailbox_get_last_internal_error(mail->box, NULL)); return -1; } i_zero(&ctx); ctx.update_ctx = update_ctx; ctx.mail = mail; if ((update_ctx->backend->flags & FTS_BACKEND_FLAG_TOKENIZED_INPUT) != 0) ctx.pending_input = buffer_create_dynamic(default_pool, 128); prev_part = NULL; parser = message_parser_init(pool_datastack_create(), input, MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE, 0); decoder = message_decoder_init(update_ctx->normalizer, 0); for (;;) { ret = message_parser_parse_next_block(parser, &raw_block); i_assert(ret != 0); if (ret < 0) { if (input->stream_errno == 0) ret = 0; else { i_error("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); } break; } if (raw_block.part != prev_part) { /* body part changed. we're now parsing the end of boundary, possibly followed by message epilogue */ if (ctx.body_parser != NULL) { if (fts_body_parser_finish(&ctx) < 0) { ret = -1; break; } } message_decoder_set_return_binary(decoder, FALSE); fts_backend_update_unset_build_key(update_ctx); prev_part = raw_block.part; i_free_and_null(ctx.content_type); i_free_and_null(ctx.content_disposition); if (raw_block.size != 0) { /* multipart. skip until beginning of next part's headers */ skip_body = TRUE; } } if (raw_block.hdr != NULL) { /* always handle headers */ } else if (raw_block.size == 0) { /* end of headers */ skip_body = !fts_build_body_begin(&ctx, raw_block.part, &binary_body); if (binary_body) message_decoder_set_return_binary(decoder, TRUE); body_part = TRUE; } else { if (skip_body) continue; } if (!message_decoder_decode_next_block(decoder, &raw_block, &block)) continue; if (block.hdr != NULL) { fts_parse_mail_header(&ctx, &raw_block); if (fts_build_mail_header(&ctx, &block) < 0) { ret = -1; break; } } else if (block.size == 0) { /* end of headers */ } else { i_assert(body_part); if (ctx.body_parser != NULL) fts_parser_more(ctx.body_parser, &block); if (fts_build_body_block(&ctx, &block, FALSE) < 0) { ret = -1; break; } body_added = TRUE; } } if (ctx.body_parser != NULL) { if (ret == 0) ret = fts_body_parser_finish(&ctx); else (void)fts_parser_deinit(&ctx.body_parser); } if (ret == 0 && body_part && !skip_body && !body_added) { /* make sure body is added even when it doesn't exist */ block.data = NULL; block.size = 0; ret = fts_build_body_block(&ctx, &block, TRUE); } if (message_parser_deinit_from_parts(&parser, &parts, &error) < 0) index_mail_set_message_parts_corrupted(mail, error); message_decoder_deinit(&decoder); i_free(ctx.content_type); i_free(ctx.content_disposition); if (ctx.word_buf != NULL) buffer_free(&ctx.word_buf); if (ctx.pending_input != NULL) buffer_free(&ctx.pending_input); return ret < 0 ? -1 : 1; } int fts_build_mail(struct fts_backend_update_context *update_ctx, struct mail *mail) { int ret; T_BEGIN { ret = fts_build_mail_real(update_ctx, mail); } T_END; return ret; } dovecot-2.2.33.2/src/plugins/fts/fts-expunge-log.h0000644000175000017500000000514213123174404016567 00000000000000#ifndef FTS_EXPUNGE_LOG #define FTS_EXPUNGE_LOG #include "seq-range-array.h" #include "guid.h" struct fts_expunge_log_read_record { guid_128_t mailbox_guid; ARRAY_TYPE(seq_range) uids; }; struct fts_expunge_log *fts_expunge_log_init(const char *path); void fts_expunge_log_deinit(struct fts_expunge_log **log); struct fts_expunge_log_append_ctx * fts_expunge_log_append_begin(struct fts_expunge_log *log); void fts_expunge_log_append_next(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, uint32_t uid); void fts_expunge_log_append_range(struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, const struct seq_range *uids); void fts_expunge_log_append_record(struct fts_expunge_log_append_ctx *ctx, const struct fts_expunge_log_read_record *record); /* In-memory flattened structures may have records removed from them, file-backed ones may not. Non-existence of UIDs is not an error, non-existence of mailbox GUID causes an error return of 0. */ int fts_expunge_log_append_remove(struct fts_expunge_log_append_ctx *ctx, const struct fts_expunge_log_read_record *record); int fts_expunge_log_append_commit(struct fts_expunge_log_append_ctx **ctx); /* Do not commit non-backed structures, abort them after use. */ int fts_expunge_log_append_abort(struct fts_expunge_log_append_ctx **ctx); int fts_expunge_log_uid_count(struct fts_expunge_log *log, unsigned int *expunges_r); struct fts_expunge_log_read_ctx * fts_expunge_log_read_begin(struct fts_expunge_log *log); const struct fts_expunge_log_read_record * fts_expunge_log_read_next(struct fts_expunge_log_read_ctx *ctx); /* Returns 1 if all ok, 0 if there was corruption, -1 if I/O error. If end() is called before reading all records, the log isn't unlinked. */ int fts_expunge_log_read_end(struct fts_expunge_log_read_ctx **ctx); /* Read an entire log file, and flatten it into one hash of arrays. The struct it returns cannot be written, as it has no backing store */ int fts_expunge_log_flatten(const char *path, struct fts_expunge_log_append_ctx **flattened_r); bool fts_expunge_log_contains(const struct fts_expunge_log_append_ctx *ctx, const guid_128_t mailbox_guid, uint32_t uid); /* Modify in-place a flattened log. If non-existent mailbox GUIDs are encountered, a warning will be logged. */ int fts_expunge_log_subtract(struct fts_expunge_log_append_ctx *from, struct fts_expunge_log *subtract); /* Write a modified flattened log as a new file. */ int fts_expunge_log_flat_write(const struct fts_expunge_log_append_ctx *flattened, const char *path); #endif dovecot-2.2.33.2/src/plugins/fts/fts-parser-tika.c0000644000175000017500000001663113165463624016572 00000000000000/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "module-context.h" #include "http-url.h" #include "http-client.h" #include "message-parser.h" #include "mail-user.h" #include "fts-parser.h" #define TIKA_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_parser_tika_user_module) struct fts_parser_tika_user { union mail_user_module_context module_ctx; struct http_url *http_url; }; struct tika_fts_parser { struct fts_parser parser; struct mail_user *user; struct http_client_request *http_req; struct ioloop *ioloop; struct io *io; struct istream *payload; bool failed; }; static struct http_client *tika_http_client = NULL; static MODULE_CONTEXT_DEFINE_INIT(fts_parser_tika_user_module, &mail_user_module_register); static int tika_get_http_client_url(struct mail_user *user, struct http_url **http_url_r) { struct fts_parser_tika_user *tuser = TIKA_USER_CONTEXT(user); struct http_client_settings http_set; const char *url, *error; url = mail_user_plugin_getenv(user, "fts_tika"); if (url == NULL) { /* fts_tika disabled */ return -1; } if (tuser != NULL) { *http_url_r = tuser->http_url; return *http_url_r == NULL ? -1 : 0; } tuser = p_new(user->pool, struct fts_parser_tika_user, 1); MODULE_CONTEXT_SET(user, fts_parser_tika_user_module, tuser); if (http_url_parse(url, NULL, 0, user->pool, &tuser->http_url, &error) < 0) { i_error("fts_tika: Failed to parse HTTP url %s: %s", url, error); return -1; } if (tika_http_client == NULL) { i_zero(&http_set); http_set.max_idle_time_msecs = 100; http_set.max_parallel_connections = 1; http_set.max_pipelined_requests = 1; http_set.max_redirects = 1; http_set.max_attempts = 3; http_set.connect_timeout_msecs = 5*1000; http_set.request_timeout_msecs = 60*1000; http_set.debug = user->mail_debug; tika_http_client = http_client_init(&http_set); } *http_url_r = tuser->http_url; return 0; } static void fts_tika_parser_response(const struct http_response *response, struct tika_fts_parser *parser) { i_assert(parser->payload == NULL); switch (response->status) { case 200: /* read response */ if (response->payload == NULL) parser->payload = i_stream_create_from_data("", 0); else { i_stream_ref(response->payload); parser->payload = response->payload; } break; case 204: /* empty response */ case 415: /* Unsupported Media Type */ case 422: /* Unprocessable Entity */ if (parser->user->mail_debug) { i_debug("fts_tika: PUT %s failed: %s", mail_user_plugin_getenv(parser->user, "fts_tika"), http_response_get_message(response)); } parser->payload = i_stream_create_from_data("", 0); break; case 500: /* Server Error - the problem could be anything (in Tika or HTTP server or proxy) and might be retriable, but Tika has trouble processing some documents and throws up this error every time for those documents. Unfortunately we can't easily re-send the request here, because we would have to re-send the entire payload, which isn't available anymore here. So we'd need to indicate in fts_parser_deinit() that we want to retry. FIXME: do this in v2.3. For now we'll just ignore it. */ i_info("fts_tika: PUT %s failed: %s - ignoring", mail_user_plugin_getenv(parser->user, "fts_tika"), http_response_get_message(response)); parser->payload = i_stream_create_from_data("", 0); break; default: i_error("fts_tika: PUT %s failed: %s", mail_user_plugin_getenv(parser->user, "fts_tika"), http_response_get_message(response)); parser->failed = TRUE; break; } parser->http_req = NULL; io_loop_stop(current_ioloop); } static struct fts_parser * fts_parser_tika_try_init(struct mail_user *user, const char *content_type, const char *content_disposition) { struct tika_fts_parser *parser; struct http_url *http_url; struct http_client_request *http_req; if (tika_get_http_client_url(user, &http_url) < 0) return NULL; parser = i_new(struct tika_fts_parser, 1); parser->parser.v = fts_parser_tika; parser->user = user; http_req = http_client_request(tika_http_client, "PUT", http_url->host_name, t_strconcat(http_url->path, http_url->enc_query, NULL), fts_tika_parser_response, parser); http_client_request_set_port(http_req, http_url->port); http_client_request_set_ssl(http_req, http_url->have_ssl); if (content_type != NULL) http_client_request_add_header(http_req, "Content-Type", content_type); if (content_disposition != NULL) http_client_request_add_header(http_req, "Content-Disposition", content_disposition); http_client_request_add_header(http_req, "Accept", "text/plain"); parser->http_req = http_req; return &parser->parser; } static void fts_parser_tika_more(struct fts_parser *_parser, struct message_block *block) { struct tika_fts_parser *parser = (struct tika_fts_parser *)_parser; struct ioloop *prev_ioloop = current_ioloop; const unsigned char *data; size_t size; ssize_t ret; if (block->size > 0) { /* first we'll send everything to Tika */ if (!parser->failed && http_client_request_send_payload(&parser->http_req, block->data, block->size) < 0) parser->failed = TRUE; block->size = 0; return; } if (parser->payload == NULL) { /* read the result from Tika */ if (!parser->failed && http_client_request_finish_payload(&parser->http_req) < 0) parser->failed = TRUE; if (!parser->failed && parser->payload == NULL) http_client_wait(tika_http_client); if (parser->failed) return; i_assert(parser->payload != NULL); } /* continue returning data from Tika. we'll create a new ioloop just for reading this one payload. */ while ((ret = i_stream_read_data(parser->payload, &data, &size, 0)) == 0) { if (parser->failed) break; /* wait for more input from Tika */ if (parser->ioloop == NULL) { parser->ioloop = io_loop_create(); parser->io = io_add_istream(parser->payload, io_loop_stop, current_ioloop); } else { io_loop_set_current(parser->ioloop); } io_loop_run(current_ioloop); } /* switch back to original ioloop. */ io_loop_set_current(prev_ioloop); if (parser->failed) ; else if (size > 0) { i_assert(ret > 0); block->data = data; block->size = size; i_stream_skip(parser->payload, size); } else { /* finished */ i_assert(ret == -1); if (parser->payload->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(parser->payload), i_stream_get_error(parser->payload)); parser->failed = TRUE; } } } static int fts_parser_tika_deinit(struct fts_parser *_parser) { struct tika_fts_parser *parser = (struct tika_fts_parser *)_parser; int ret = parser->failed ? -1 : 0; /* remove io before unrefing payload - otherwise lib-http adds another timeout to ioloop unnecessarily */ if (parser->payload != NULL) i_stream_unref(&parser->payload); if (parser->io != NULL) io_remove(&parser->io); if (parser->http_req != NULL) http_client_request_abort(&parser->http_req); if (parser->ioloop != NULL) { io_loop_set_current(parser->ioloop); io_loop_destroy(&parser->ioloop); } i_free(parser); return ret; } static void fts_parser_tika_unload(void) { if (tika_http_client != NULL) http_client_deinit(&tika_http_client); } struct fts_parser_vfuncs fts_parser_tika = { fts_parser_tika_try_init, fts_parser_tika_more, fts_parser_tika_deinit, fts_parser_tika_unload }; dovecot-2.2.33.2/src/plugins/fts/fts-parser-script.c0000644000175000017500000001546213165463624017147 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "net.h" #include "istream.h" #include "write-full.h" #include "module-context.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "message-parser.h" #include "mail-user.h" #include "fts-parser.h" #define SCRIPT_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_parser_script_user_module) #define SCRIPT_HANDSHAKE "VERSION\tscript\t4\t0\nalarm=10\nnoreply\n" struct content { const char *content_type; const char *const *extensions; }; struct fts_parser_script_user { union mail_user_module_context module_ctx; ARRAY(struct content) content; }; struct script_fts_parser { struct fts_parser parser; int fd; char *path; unsigned char outbuf[IO_BLOCK_SIZE]; bool failed; bool shutdown; }; static MODULE_CONTEXT_DEFINE_INIT(fts_parser_script_user_module, &mail_user_module_register); static int script_connect(struct mail_user *user, const char **path_r) { const char *path; int fd; path = mail_user_plugin_getenv(user, "fts_decoder"); if (path == NULL) return -1; if (*path != '/') path = t_strconcat(user->set->base_dir, "/", path, NULL); fd = net_connect_unix_with_retries(path, 1000); if (fd == -1) i_error("net_connect_unix(%s) failed: %m", path); else net_set_nonblock(fd, FALSE); *path_r = path; return fd; } static int script_contents_read(struct mail_user *user) { struct fts_parser_script_user *suser = SCRIPT_USER_CONTEXT(user); const char *path, *cmd, *line; char **args; struct istream *input; struct content *content; bool eof_seen = FALSE; int fd, ret = 0; fd = script_connect(user, &path); if (fd == -1) return -1; cmd = t_strdup_printf(SCRIPT_HANDSHAKE"\n"); if (write_full(fd, cmd, strlen(cmd)) < 0) { i_error("write(%s) failed: %m", path); i_close_fd(&fd); return -1; } input = i_stream_create_fd_autoclose(&fd, 1024); while ((line = i_stream_read_next_line(input)) != NULL) { /* [ ...] */ args = p_strsplit_spaces(user->pool, line, " "); if (args[0] == NULL) { eof_seen = TRUE; break; } if (args[0][0] == '\0' || args[1] == NULL) { i_error("parser script sent invalid input: %s", line); continue; } content = array_append_space(&suser->content); content->content_type = args[0]; content->extensions = (const void *)(args+1); } if (input->stream_errno != 0) { i_error("parser script read(%s) failed: %s", path, i_stream_get_error(input)); ret = -1; } else if (!eof_seen) { if (input->v_offset == 0) i_error("parser script didn't send any data"); else i_error("parser script didn't send empty EOF line"); } i_stream_destroy(&input); return ret; } static bool script_support_content(struct mail_user *user, const char **content_type, const char *filename) { struct fts_parser_script_user *suser = SCRIPT_USER_CONTEXT(user); const struct content *content; const char *extension; if (suser == NULL) { suser = p_new(user->pool, struct fts_parser_script_user, 1); p_array_init(&suser->content, user->pool, 32); MODULE_CONTEXT_SET(user, fts_parser_script_user_module, suser); } if (array_count(&suser->content) == 0) { if (script_contents_read(user) < 0) return FALSE; } if (strcmp(*content_type, "application/octet-stream") == 0) { if (filename == NULL) return FALSE; extension = strrchr(filename, '.'); if (extension == NULL) return FALSE; extension = filename + 1; array_foreach(&suser->content, content) { if (content->extensions != NULL && str_array_icase_find(content->extensions, extension)) { *content_type = content->content_type; return TRUE; } } } else { array_foreach(&suser->content, content) { if (strcmp(content->content_type, *content_type) == 0) return TRUE; } } return FALSE; } static void parse_content_disposition(const char *content_disposition, const char **filename_r) { struct rfc822_parser_context parser; const char *const *results, *filename2; string_t *str; *filename_r = NULL; if (content_disposition == NULL) return; rfc822_parser_init(&parser, (const unsigned char *)content_disposition, strlen(content_disposition), NULL); rfc822_skip_lwsp(&parser); /* type; param; param; .. */ str = t_str_new(32); if (rfc822_parse_mime_token(&parser, str) < 0) return; rfc2231_parse(&parser, &results); filename2 = NULL; for (; *results != NULL; results += 2) { if (strcasecmp(results[0], "filename") == 0) { *filename_r = results[1]; break; } if (strcasecmp(results[0], "filename*") == 0) filename2 = results[1]; } if (*filename_r == NULL) { /* RFC 2231 style non-ascii filename. we don't really care much about the filename actually, just about its extension */ *filename_r = filename2; } } static struct fts_parser * fts_parser_script_try_init(struct mail_user *user, const char *content_type, const char *content_disposition) { struct script_fts_parser *parser; const char *filename, *path, *cmd; int fd; parse_content_disposition(content_disposition, &filename); if (script_support_content(user, &content_type, filename) <= 0) return NULL; fd = script_connect(user, &path); if (fd == -1) return NULL; cmd = t_strdup_printf(SCRIPT_HANDSHAKE"%s\n\n", content_type); if (write_full(fd, cmd, strlen(cmd)) < 0) { i_error("write(%s) failed: %m", path); i_close_fd(&fd); return NULL; } parser = i_new(struct script_fts_parser, 1); parser->parser.v = fts_parser_script; parser->path = i_strdup(path); parser->fd = fd; return &parser->parser; } static void fts_parser_script_more(struct fts_parser *_parser, struct message_block *block) { struct script_fts_parser *parser = (struct script_fts_parser *)_parser; ssize_t ret; if (block->size > 0) { /* first we'll send everything to the script */ if (!parser->failed && write_full(parser->fd, block->data, block->size) < 0) { i_error("write(%s) failed: %m", parser->path); parser->failed = TRUE; } block->size = 0; } else { if (!parser->shutdown) { if (shutdown(parser->fd, SHUT_WR) < 0) i_error("shutdown(%s) failed: %m", parser->path); parser->shutdown = TRUE; } /* read the result from the script */ ret = read(parser->fd, parser->outbuf, sizeof(parser->outbuf)); if (ret < 0) i_error("read(%s) failed: %m", parser->path); else { block->data = parser->outbuf; block->size = ret; } } } static int fts_parser_script_deinit(struct fts_parser *_parser) { struct script_fts_parser *parser = (struct script_fts_parser *)_parser; int ret = parser->failed ? -1 : 0; if (close(parser->fd) < 0) i_error("close(%s) failed: %m", parser->path); i_free(parser->path); i_free(parser); return ret; } struct fts_parser_vfuncs fts_parser_script = { fts_parser_script_try_init, fts_parser_script_more, fts_parser_script_deinit, NULL }; dovecot-2.2.33.2/src/plugins/fts/fts-storage.h0000644000175000017500000000326213165463624016015 00000000000000#ifndef FTS_STORAGE_H #define FTS_STORAGE_H #include "mail-storage-private.h" #include "fts-api.h" struct fts_scores { int refcount; ARRAY_TYPE(fts_score_map) score_map; }; struct fts_search_level { ARRAY_TYPE(seq_range) definite_seqs, maybe_seqs; buffer_t *args_matches; ARRAY_TYPE(fts_score_map) score_map; }; struct fts_search_context { union mail_search_module_context module_ctx; struct fts_backend *backend; struct mailbox *box; struct mailbox_transaction_context *t; struct mail_search_args *args; enum fts_lookup_flags flags; pool_t result_pool; ARRAY(struct fts_search_level) levels; buffer_t *orig_matches; uint32_t first_unindexed_seq; /* final scores, combined from all levels */ struct fts_scores *scores; struct fts_indexer_context *indexer_ctx; unsigned int virtual_mailbox:1; unsigned int fts_lookup_success:1; unsigned int indexing_timed_out:1; unsigned int enforced:1; }; /* Figure out if we want to use full text search indexes and update backends in fctx accordingly. */ void fts_search_analyze(struct fts_search_context *fctx); /* Perform the actual index lookup and update definite_uids and maybe_uids. */ void fts_search_lookup(struct fts_search_context *fctx); /* Returns FTS backend for the given mailbox (assumes it has one). */ struct fts_backend *fts_mailbox_backend(struct mailbox *box); /* Returns FTS backend for the given mailbox list, or NULL if it has none. */ struct fts_backend *fts_list_backend(struct mailbox_list *list); void fts_mail_allocated(struct mail *mail); void fts_mail_namespaces_added(struct mail_namespace *ns); void fts_mailbox_allocated(struct mailbox *box); void fts_mailbox_list_created(struct mailbox_list *list); #endif dovecot-2.2.33.2/src/plugins/fts/doveadm-fts.h0000644000175000017500000000031713123174404015753 00000000000000#ifndef DOVEADM_FTS_H #define DOVEADM_FTS_H struct module; void doveadm_dump_fts_expunge_log_init(void); void doveadm_fts_plugin_init(struct module *module); void doveadm_fts_plugin_deinit(void); #endif dovecot-2.2.33.2/src/plugins/fts/fts-api.h0000644000175000017500000001312313123174404015104 00000000000000#ifndef FTS_API_H #define FTS_API_H struct mail; struct mailbox; struct mail_namespace; struct mail_search_arg; struct fts_backend; #include "seq-range-array.h" enum fts_lookup_flags { /* Specifies if the args should be ANDed or ORed together. */ FTS_LOOKUP_FLAG_AND_ARGS = 0x01, /* Require exact matching for non-fuzzy search args by returning all such matches as maybe_uids instead of definite_uids */ FTS_LOOKUP_FLAG_NO_AUTO_FUZZY = 0x02 }; enum fts_backend_build_key_type { /* Header */ FTS_BACKEND_BUILD_KEY_HDR, /* MIME part header */ FTS_BACKEND_BUILD_KEY_MIME_HDR, /* MIME body part */ FTS_BACKEND_BUILD_KEY_BODY_PART, /* Binary MIME body part, if backend supports binary data */ FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY }; struct fts_backend_build_key { uint32_t uid; enum fts_backend_build_key_type type; struct message_part *part; /* for _KEY_HDR: */ const char *hdr_name; /* for _KEY_BODY_PART and _KEY_BODY_PART_BINARY: */ /* Contains a valid parsed "type/subtype" string. For messages without (valid) Content-Type: header, it's set to "text/plain". */ const char *body_content_type; /* Content-Disposition: header without parsing/validation if it exists, otherwise NULL. */ const char *body_content_disposition; }; struct fts_score_map { uint32_t uid; float score; }; ARRAY_DEFINE_TYPE(fts_score_map, struct fts_score_map); struct fts_result { struct mailbox *box; ARRAY_TYPE(seq_range) definite_uids; /* The maybe_uids is useful with backends that can only filter out messages, but can't definitively say if the search matched a message. */ ARRAY_TYPE(seq_range) maybe_uids; ARRAY_TYPE(fts_score_map) scores; bool scores_sorted; }; struct fts_multi_result { pool_t pool; /* box=NULL-terminated array of mailboxes and matching UIDs, all allocated from the given pool. */ struct fts_result *box_results; }; int fts_backend_init(const char *backend_name, struct mail_namespace *ns, const char **error_r, struct fts_backend **backend_r); void fts_backend_deinit(struct fts_backend **backend); /* Get the last_uid for the mailbox. */ int fts_backend_get_last_uid(struct fts_backend *backend, struct mailbox *box, uint32_t *last_uid_r); /* Returns TRUE if there exists an update context. */ bool fts_backend_is_updating(struct fts_backend *backend); /* Start an index update. */ struct fts_backend_update_context * fts_backend_update_init(struct fts_backend *backend); /* Finish an index update. Returns 0 if ok, -1 if some updates failed. If updates failed, the index is in unspecified state. */ int fts_backend_update_deinit(struct fts_backend_update_context **ctx); /* Switch to updating the specified mailbox. box may also be set to NULL to make sure the previous mailbox won't tried to be accessed anymore. */ void fts_backend_update_set_mailbox(struct fts_backend_update_context *ctx, struct mailbox *box); /* Expunge the specified mail. */ void fts_backend_update_expunge(struct fts_backend_update_context *ctx, uint32_t uid); /* Switch to building index for specified key. If backend doesn't want to index this key, it can return FALSE and caller will skip to next key. */ bool fts_backend_update_set_build_key(struct fts_backend_update_context *ctx, const struct fts_backend_build_key *key); /* Make sure that if _build_more() is called, we'll assert-crash. */ void fts_backend_update_unset_build_key(struct fts_backend_update_context *ctx); /* Add more content to the index for the currently specified build key. Non-BODY_PART_BINARY data must contain only full valid UTF-8 characters, but it doesn't need to be NUL-terminated. size contains the data size in bytes, not characters. This function may be called many times and the data block sizes may be small. Backend returns 0 if ok, -1 if build should be aborted. */ int fts_backend_update_build_more(struct fts_backend_update_context *ctx, const unsigned char *data, size_t size); /* Refresh index to make sure we see latest changes from lookups. Returns 0 if ok, -1 if error. */ int fts_backend_refresh(struct fts_backend *backend); /* Go through the entire index and make sure all mails are indexed, and delete any extra mails in the index. */ int fts_backend_rescan(struct fts_backend *backend); /* Optimize the index. This can be a somewhat heavy operation. */ int fts_backend_optimize(struct fts_backend *backend); /* Returns TRUE if fts_backend_lookup() should even be tried for the given args. */ bool fts_backend_can_lookup(struct fts_backend *backend, const struct mail_search_arg *args); /* Do a FTS lookup for the given search args. Backends can support different kinds of search arguments, so match_always=TRUE must be set to all search args that were actually used to produce the search results. The other args are handled by the regular search code. The backends MUST ignore all args that have subargs (SEARCH_OR, SEARCH_SUB), since they are looked up separately. The arrays in result must be initialized by caller. */ int fts_backend_lookup(struct fts_backend *backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result); /* Search from multiple mailboxes. result->pool must be initialized. */ int fts_backend_lookup_multi(struct fts_backend *backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result); /* Called after the lookups are done. The next lookup will be preceded by a refresh. */ void fts_backend_lookup_done(struct fts_backend *backend); #endif dovecot-2.2.33.2/src/plugins/fts/decode2text.sh0000755000175000017500000000542713123174404016151 00000000000000#!/bin/sh # Example attachment decoder script. The attachment comes from stdin, and # the script is expected to output UTF-8 data to stdout. (If the output isn't # UTF-8, everything except valid UTF-8 sequences are dropped from it.) # The attachment decoding is enabled by setting: # # plugin { # fts_decoder = decode2text # } # service decode2text { # executable = script /usr/local/libexec/dovecot/decode2text.sh # user = dovecot # unix_listener decode2text { # mode = 0666 # } # } libexec_dir=`dirname $0` content_type=$1 # The second parameter is the format's filename extension, which is used when # found from a filename of application/octet-stream. You can also add more # extensions by giving more parameters. formats='application/pdf pdf application/x-pdf pdf application/msword doc application/mspowerpoint ppt application/vnd.ms-powerpoint ppt application/ms-excel xls application/x-msexcel xls application/vnd.ms-excel xls application/vnd.openxmlformats-officedocument.wordprocessingml.document docx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx application/vnd.openxmlformats-officedocument.presentationml.presentation pptx application/vnd.oasis.opendocument.text odt application/vnd.oasis.opendocument.spreadsheet ods application/vnd.oasis.opendocument.presentation odp ' if [ "$content_type" = "" ]; then echo "$formats" exit 0 fi fmt=`echo "$formats" | grep -w "^$content_type" | cut -d ' ' -f 2` if [ "$fmt" = "" ]; then echo "Content-Type: $content_type not supported" >&2 exit 1 fi # most decoders can't handle stdin directly, so write the attachment # to a temp file path=`mktemp` trap "rm -f $path" 0 1 2 3 14 15 cat > $path xmlunzip() { name=$1 tempdir=`mktemp -d` if [ "$tempdir" = "" ]; then exit 1 fi trap "rm -rf $path $tempdir" 0 1 2 3 14 15 cd $tempdir || exit 1 unzip -q "$path" 2>/dev/null || exit 0 find . -name "$name" -print0 | xargs -0 cat | $libexec_dir/xml2text } wait_timeout() { childpid=$! trap "kill -9 $childpid; rm -f $path" 1 2 3 14 15 wait $childpid } LANG=en_US.UTF-8 export LANG if [ $fmt = "pdf" ]; then /usr/bin/pdftotext $path - 2>/dev/null& wait_timeout 2>/dev/null elif [ $fmt = "doc" ]; then (/usr/bin/catdoc $path; true) 2>/dev/null& wait_timeout 2>/dev/null elif [ $fmt = "ppt" ]; then (/usr/bin/catppt $path; true) 2>/dev/null& wait_timeout 2>/dev/null elif [ $fmt = "xls" ]; then (/usr/bin/xls2csv $path; true) 2>/dev/null& wait_timeout 2>/dev/null elif [ $fmt = "odt" -o $fmt = "ods" -o $fmt = "odp" ]; then xmlunzip "content.xml" elif [ $fmt = "docx" ]; then xmlunzip "document.xml" elif [ $fmt = "xlsx" ]; then xmlunzip "sharedStrings.xml" elif [ $fmt = "pptx" ]; then xmlunzip "slide*.xml" else echo "Buggy decoder script: $fmt not handled" >&2 exit 1 fi exit 0 dovecot-2.2.33.2/src/plugins/fts/fts-indexer.h0000644000175000017500000000137113123174404015773 00000000000000#ifndef FTS_BUILD_H #define FTS_BUILD_H struct fts_backend; struct fts_indexer_context; /* Initialize indexing the given mailbox via indexer service. Returns 1 if indexing started, 0 if there was no need to index or -1 if error. */ int fts_indexer_init(struct fts_backend *backend, struct mailbox *box, struct fts_indexer_context **ctx_r); /* Returns 0 if ok, -1 if error. */ int fts_indexer_deinit(struct fts_indexer_context **ctx); /* Build more. Returns 1 if finished, 0 if this function needs to be called again, -1 if error. */ int fts_indexer_more(struct fts_indexer_context *ctx); /* Returns fd, which you can either read from or close. */ int fts_indexer_cmd(struct mail_user *user, const char *cmd, const char **path_r); #endif dovecot-2.2.33.2/src/plugins/fts/fts-user.c0000644000175000017500000002462013165463624015323 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-context.h" #include "mail-user.h" #include "fts-language.h" #include "fts-filter.h" #include "fts-tokenizer.h" #include "fts-user.h" #define FTS_USER_CONTEXT(obj) \ MODULE_CONTEXT(obj, fts_user_module) struct fts_user { union mail_user_module_context module_ctx; int refcount; struct fts_language_list *lang_list; struct fts_user_language *data_lang; ARRAY_TYPE(fts_user_language) languages, data_languages; }; static MODULE_CONTEXT_DEFINE_INIT(fts_user_module, &mail_user_module_register); static const char *const *str_keyvalues_to_array(const char *str) { const char *key, *value, *const *keyvalues; ARRAY_TYPE(const_string) arr; unsigned int i; if (str == NULL) return NULL; t_array_init(&arr, 8); keyvalues = t_strsplit_spaces(str, " "); for (i = 0; keyvalues[i] != NULL; i++) { value = strchr(keyvalues[i], '='); if (value != NULL) key = t_strdup_until(keyvalues[i], value++); else { key = keyvalues[i]; value = ""; } array_append(&arr, &key, 1); array_append(&arr, &value, 1); } array_append_zero(&arr); return array_idx(&arr, 0); } static int fts_user_init_languages(struct mail_user *user, struct fts_user *fuser, const char **error_r) { const char *languages, *unknown; const char *lang_config[3] = {NULL, NULL, NULL}; languages = mail_user_plugin_getenv(user, "fts_languages"); if (languages == NULL) { *error_r = "fts_languages setting is missing"; return -1; } lang_config[1] = mail_user_plugin_getenv(user, "fts_language_config"); if (lang_config[1] != NULL) lang_config[0] = "fts_language_config"; if (fts_language_list_init(lang_config, &fuser->lang_list, error_r) < 0) return -1; if (!fts_language_list_add_names(fuser->lang_list, languages, &unknown)) { *error_r = t_strdup_printf( "fts_languages: Unknown language '%s'", unknown); return -1; } if (array_count(fts_language_list_get_all(fuser->lang_list)) == 0) { *error_r = "fts_languages setting is empty"; return -1; } return 0; } static int fts_user_create_filters(struct mail_user *user, const struct fts_language *lang, struct fts_filter **filter_r, const char **error_r) { const struct fts_filter *filter_class; struct fts_filter *filter = NULL, *parent = NULL; const char *filters_key, *const *filters, *filter_set_name; const char *str, *error, *set_key; unsigned int i; int ret = 0; /* try to get the language-specific filters first */ filters_key = t_strconcat("fts_filters_", lang->name, NULL); str = mail_user_plugin_getenv(user, filters_key); if (str == NULL) { /* fallback to global filters */ filters_key = "fts_filters"; str = mail_user_plugin_getenv(user, filters_key); if (str == NULL) { /* No filters */ *filter_r = NULL; return 0; } } filters = t_strsplit_spaces(str, " "); for (i = 0; filters[i] != NULL; i++) { filter_class = fts_filter_find(filters[i]); if (filter_class == NULL) { *error_r = t_strdup_printf("%s: Unknown filter '%s'", filters_key, filters[i]); ret = -1; break; } /* try the language-specific setting first */ filter_set_name = t_str_replace(filters[i], '-', '_'); set_key = t_strdup_printf("fts_filter_%s_%s", lang->name, filter_set_name); str = mail_user_plugin_getenv(user, set_key); if (str == NULL) { set_key = t_strdup_printf("fts_filter_%s", filter_set_name); str = mail_user_plugin_getenv(user, set_key); } if (fts_filter_create(filter_class, parent, lang, str_keyvalues_to_array(str), &filter, &error) < 0) { *error_r = t_strdup_printf("%s: %s", set_key, error); ret = -1; break; } if (parent != NULL) fts_filter_unref(&parent); parent = filter; } if (ret < 0) { if (parent != NULL) fts_filter_unref(&parent); return -1; } *filter_r = filter; return 0; } static int fts_user_create_tokenizer(struct mail_user *user, const struct fts_language *lang, struct fts_tokenizer **tokenizer_r, bool search, const char **error_r) { const struct fts_tokenizer *tokenizer_class; struct fts_tokenizer *tokenizer = NULL, *parent = NULL; const char *tokenizers_key, *const *tokenizers, *tokenizer_set_name; const char *str, *error, *set_key; unsigned int i; int ret = 0; tokenizers_key = t_strconcat("fts_tokenizers_", lang->name, NULL); str = mail_user_plugin_getenv(user, tokenizers_key); if (str == NULL) { str = mail_user_plugin_getenv(user, "fts_tokenizers"); if (str == NULL) { *error_r = t_strdup_printf("%s or fts_tokenizers setting must exist", tokenizers_key); return -1; } tokenizers_key = "fts_tokenizers"; } tokenizers = t_strsplit_spaces(str, " "); for (i = 0; tokenizers[i] != NULL; i++) { tokenizer_class = fts_tokenizer_find(tokenizers[i]); if (tokenizer_class == NULL) { *error_r = t_strdup_printf("%s: Unknown tokenizer '%s'", tokenizers_key, tokenizers[i]); ret = -1; break; } tokenizer_set_name = t_str_replace(tokenizers[i], '-', '_'); set_key = t_strdup_printf("fts_tokenizer_%s_%s", tokenizer_set_name, lang->name); str = mail_user_plugin_getenv(user, set_key); if (str == NULL) { set_key = t_strdup_printf("fts_tokenizer_%s", tokenizer_set_name); str = mail_user_plugin_getenv(user, set_key); } /* tell the tokenizers that we're tokenizing a search string (instead of tokenizing indexed data) */ if (search) str = t_strconcat("search=yes ", str, NULL); if (fts_tokenizer_create(tokenizer_class, parent, str_keyvalues_to_array(str), &tokenizer, &error) < 0) { *error_r = t_strdup_printf("%s: %s", set_key, error); ret = -1; break; } if (parent != NULL) fts_tokenizer_unref(&parent); parent = tokenizer; } if (ret < 0) { if (parent != NULL) fts_tokenizer_unref(&parent); return -1; } *tokenizer_r = tokenizer; return 0; } static int fts_user_language_init_tokenizers(struct mail_user *user, struct fts_user_language *user_lang, const char **error_r) { if (fts_user_create_tokenizer(user, user_lang->lang, &user_lang->index_tokenizer, FALSE, error_r) < 0) return -1; if (fts_user_create_tokenizer(user, user_lang->lang, &user_lang->search_tokenizer, TRUE, error_r) < 0) return -1; return 0; } struct fts_user_language * fts_user_language_find(struct mail_user *user, const struct fts_language *lang) { struct fts_user_language *const *user_langp; struct fts_user *fuser = FTS_USER_CONTEXT(user); array_foreach(&fuser->languages, user_langp) { if (strcmp((*user_langp)->lang->name, lang->name) == 0) return *user_langp; } return NULL; } static int fts_user_language_create(struct mail_user *user, struct fts_user *fuser, const struct fts_language *lang, const char **error_r) { struct fts_user_language *user_lang; user_lang = p_new(user->pool, struct fts_user_language, 1); user_lang->lang = lang; array_append(&fuser->languages, &user_lang, 1); if (fts_user_language_init_tokenizers(user, user_lang, error_r) < 0) return -1; if (fts_user_create_filters(user, lang, &user_lang->filter, error_r) < 0) return -1; return 0; } static int fts_user_languages_fill_all(struct mail_user *user, struct fts_user *fuser, const char **error_r) { const struct fts_language *const *langp; array_foreach(fts_language_list_get_all(fuser->lang_list), langp) { if (fts_user_language_create(user, fuser, *langp, error_r) < 0) return -1; } return 0; } static int fts_user_init_data_language(struct mail_user *user, struct fts_user *fuser, const char **error_r) { struct fts_user_language *user_lang; const char *error; user_lang = p_new(user->pool, struct fts_user_language, 1); user_lang->lang = &fts_language_data; if (fts_user_language_init_tokenizers(user, user_lang, error_r) < 0) return -1; if (fts_filter_create(fts_filter_lowercase, NULL, user_lang->lang, NULL, &user_lang->filter, &error) < 0) i_unreached(); i_assert(user_lang->filter != NULL); p_array_init(&fuser->data_languages, user->pool, 1); array_append(&fuser->data_languages, &user_lang, 1); fuser->data_lang = user_lang; return 0; } struct fts_language_list *fts_user_get_language_list(struct mail_user *user) { struct fts_user *fuser = FTS_USER_CONTEXT(user); return fuser->lang_list; } const ARRAY_TYPE(fts_user_language) * fts_user_get_all_languages(struct mail_user *user) { struct fts_user *fuser = FTS_USER_CONTEXT(user); return &fuser->languages; } const ARRAY_TYPE(fts_user_language) * fts_user_get_data_languages(struct mail_user *user) { struct fts_user *fuser = FTS_USER_CONTEXT(user); return &fuser->data_languages; } struct fts_user_language *fts_user_get_data_lang(struct mail_user *user) { struct fts_user *fuser = FTS_USER_CONTEXT(user); return fuser->data_lang; } static void fts_user_language_free(struct fts_user_language *user_lang) { if (user_lang->filter != NULL) fts_filter_unref(&user_lang->filter); if (user_lang->index_tokenizer != NULL) fts_tokenizer_unref(&user_lang->index_tokenizer); if (user_lang->search_tokenizer != NULL) fts_tokenizer_unref(&user_lang->search_tokenizer); } static void fts_user_free(struct fts_user *fuser) { struct fts_user_language *const *user_langp; if (fuser->lang_list != NULL) fts_language_list_deinit(&fuser->lang_list); array_foreach(&fuser->languages, user_langp) fts_user_language_free(*user_langp); if (fuser->data_lang != NULL) fts_user_language_free(fuser->data_lang); } int fts_mail_user_init(struct mail_user *user, const char **error_r) { struct fts_user *fuser = FTS_USER_CONTEXT(user); if (fuser != NULL) { /* multiple fts plugins are loaded */ fuser->refcount++; return 0; } fuser = p_new(user->pool, struct fts_user, 1); fuser->refcount = 1; p_array_init(&fuser->languages, user->pool, 4); if (fts_user_init_languages(user, fuser, error_r) < 0 || fts_user_init_data_language(user, fuser, error_r)) { fts_user_free(fuser); return -1; } if (fts_user_languages_fill_all(user, fuser, error_r) < 0) { fts_user_free(fuser); return -1; } MODULE_CONTEXT_SET(user, fts_user_module, fuser); return 0; } void fts_mail_user_deinit(struct mail_user *user) { struct fts_user *fuser = FTS_USER_CONTEXT(user); if (fuser != NULL) { i_assert(fuser->refcount > 0); if (--fuser->refcount == 0) fts_user_free(fuser); } } dovecot-2.2.33.2/src/plugins/fts/fts-parser.h0000644000175000017500000000221013123174404015622 00000000000000#ifndef FTS_PARSER_H #define FTS_PARSER_H struct message_block; struct mail_user; struct fts_parser_vfuncs { struct fts_parser *(*try_init)(struct mail_user *user, const char *content_type, const char *content_disposition); void (*more)(struct fts_parser *parser, struct message_block *block); int (*deinit)(struct fts_parser *parser); void (*unload)(void); }; struct fts_parser { struct fts_parser_vfuncs v; buffer_t *utf8_output; }; extern struct fts_parser_vfuncs fts_parser_html; extern struct fts_parser_vfuncs fts_parser_script; extern struct fts_parser_vfuncs fts_parser_tika; bool fts_parser_init(struct mail_user *user, const char *content_type, const char *content_disposition, struct fts_parser **parser_r); struct fts_parser *fts_parser_text_init(void); /* The parser is initially called with message body blocks. Once message is finished, it's still called with incoming size=0 while the parser increases it to non-zero. */ void fts_parser_more(struct fts_parser *parser, struct message_block *block); int fts_parser_deinit(struct fts_parser **parser); void fts_parsers_unload(void); #endif dovecot-2.2.33.2/src/plugins/fts/fts-parser.c0000644000175000017500000000506513165463624015643 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "unichar.h" #include "message-parser.h" #include "fts-parser.h" static const struct fts_parser_vfuncs *parsers[] = { &fts_parser_html, &fts_parser_script, &fts_parser_tika }; static const char *plaintext_content_types[] = { "text/plain", "message/delivery-status", "message/disposition-notification", "application/pgp-signature", NULL }; bool fts_parser_init(struct mail_user *user, const char *content_type, const char *content_disposition, struct fts_parser **parser_r) { unsigned int i; if (str_array_find(plaintext_content_types, content_type)) { /* we probably don't want/need to allow parsers to handle plaintext? */ return FALSE; } for (i = 0; i < N_ELEMENTS(parsers); i++) { *parser_r = parsers[i]->try_init(user, content_type, content_disposition); if (*parser_r != NULL) return TRUE; } return FALSE; } struct fts_parser *fts_parser_text_init(void) { return i_new(struct fts_parser, 1); } static bool data_has_nuls(const unsigned char *data, size_t size) { size_t i; for (i = 0; i < size; i++) { if (data[i] == '\0') return TRUE; } return FALSE; } static void replace_nul_bytes(buffer_t *buf) { unsigned char *data; size_t i, size; data = buffer_get_modifiable_data(buf, &size); for (i = 0; i < size; i++) { if (data[i] == '\0') data[i] = ' '; } } void fts_parser_more(struct fts_parser *parser, struct message_block *block) { if (parser->v.more != NULL) parser->v.more(parser, block); if (!uni_utf8_data_is_valid(block->data, block->size) || data_has_nuls(block->data, block->size)) { /* output isn't valid UTF-8. make it. */ if (parser->utf8_output == NULL) { parser->utf8_output = buffer_create_dynamic(default_pool, 4096); } else { buffer_set_used_size(parser->utf8_output, 0); } (void)uni_utf8_get_valid_data(block->data, block->size, parser->utf8_output); replace_nul_bytes(parser->utf8_output); block->data = parser->utf8_output->data; block->size = parser->utf8_output->used; } } int fts_parser_deinit(struct fts_parser **_parser) { struct fts_parser *parser = *_parser; int ret = 0; *_parser = NULL; if (parser->utf8_output != NULL) buffer_free(&parser->utf8_output); if (parser->v.deinit != NULL) ret = parser->v.deinit(parser); else i_free(parser); return ret; } void fts_parsers_unload(void) { unsigned int i; for (i = 0; i < N_ELEMENTS(parsers); i++) { if (parsers[i]->unload != NULL) parsers[i]->unload(); } } dovecot-2.2.33.2/src/plugins/fts/fts-user.h0000644000175000017500000000152313123174404015312 00000000000000#ifndef FTS_USER_H #define FTS_USER_H struct fts_user_language { const struct fts_language *lang; struct fts_filter *filter; struct fts_tokenizer *index_tokenizer, *search_tokenizer; }; ARRAY_DEFINE_TYPE(fts_user_language, struct fts_user_language *); struct fts_user_language * fts_user_language_find(struct mail_user *user, const struct fts_language *lang); struct fts_language_list *fts_user_get_language_list(struct mail_user *user); const ARRAY_TYPE(fts_user_language) * fts_user_get_all_languages(struct mail_user *user); struct fts_user_language *fts_user_get_data_lang(struct mail_user *user); const ARRAY_TYPE(fts_user_language) * fts_user_get_data_languages(struct mail_user *user); int fts_mail_user_init(struct mail_user *user, const char **error_r); void fts_mail_user_deinit(struct mail_user *user); #endif dovecot-2.2.33.2/src/plugins/fts/fts-plugin.c0000644000175000017500000000154113123174404015625 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage-hooks.h" #include "fts-filter.h" #include "fts-tokenizer.h" #include "fts-parser.h" #include "fts-storage.h" #include "fts-user.h" #include "fts-plugin.h" #include "fts-library.h" const char *fts_plugin_version = DOVECOT_ABI_VERSION; static struct mail_storage_hooks fts_mail_storage_hooks = { .mail_namespaces_added = fts_mail_namespaces_added, .mailbox_list_created = fts_mailbox_list_created, .mailbox_allocated = fts_mailbox_allocated, .mail_allocated = fts_mail_allocated }; void fts_plugin_init(struct module *module) { fts_library_init(); mail_storage_hooks_add(module, &fts_mail_storage_hooks); } void fts_plugin_deinit(void) { fts_library_deinit(); fts_parsers_unload(); mail_storage_hooks_remove(&fts_mail_storage_hooks); } dovecot-2.2.33.2/src/plugins/fts/fts-build-mail.h0000644000175000017500000000022613123174404016352 00000000000000#ifndef FTS_BUILD_MAIL_H #define FTS_BUILD_MAIL_H int fts_build_mail(struct fts_backend_update_context *update_ctx, struct mail *mail); #endif dovecot-2.2.33.2/src/plugins/fts/fts-api.c0000644000175000017500000003237013123174404015104 00000000000000/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hex-binary.h" #include "mail-index.h" #include "mail-namespace.h" #include "mail-storage-private.h" #include "mailbox-list-iter.h" #include "mail-search.h" #include "fts-api-private.h" static ARRAY(const struct fts_backend *) backends; void fts_backend_register(const struct fts_backend *backend) { if (!array_is_created(&backends)) i_array_init(&backends, 4); array_append(&backends, &backend, 1); } void fts_backend_unregister(const char *name) { const struct fts_backend *const *be; unsigned int i, count; be = array_get(&backends, &count); for (i = 0; i < count; i++) { if (strcmp(be[i]->name, name) == 0) { array_delete(&backends, i, 1); break; } } if (i == count) i_panic("fts_backend_unregister(%s): unknown backend", name); if (count == 1) array_free(&backends); } static const struct fts_backend * fts_backend_class_lookup(const char *backend_name) { const struct fts_backend *const *be; unsigned int i, count; if (array_is_created(&backends)) { be = array_get(&backends, &count); for (i = 0; i < count; i++) { if (strcmp(be[i]->name, backend_name) == 0) return be[i]; } } return NULL; } int fts_backend_init(const char *backend_name, struct mail_namespace *ns, const char **error_r, struct fts_backend **backend_r) { const struct fts_backend *be; struct fts_backend *backend; be = fts_backend_class_lookup(backend_name); if (be == NULL) { *error_r = "Unknown backend"; return -1; } backend = be->v.alloc(); backend->ns = ns; if (backend->v.init(backend, error_r) < 0) { i_free(backend); return -1; } *backend_r = backend; return 0; } void fts_backend_deinit(struct fts_backend **_backend) { struct fts_backend *backend = *_backend; *_backend = NULL; backend->v.deinit(backend); } int fts_backend_get_last_uid(struct fts_backend *backend, struct mailbox *box, uint32_t *last_uid_r) { struct fts_index_header hdr; if (box->virtual_vfuncs != NULL) { /* virtual mailboxes themselves don't have any indexes, so catch this call here */ if (!fts_index_get_header(box, &hdr)) *last_uid_r = 0; else *last_uid_r = hdr.last_indexed_uid; return 0; } return backend->v.get_last_uid(backend, box, last_uid_r); } bool fts_backend_is_updating(struct fts_backend *backend) { return backend->updating; } struct fts_backend_update_context * fts_backend_update_init(struct fts_backend *backend) { struct fts_backend_update_context *ctx; i_assert(!backend->updating); backend->updating = TRUE; ctx = backend->v.update_init(backend); if ((backend->flags & FTS_BACKEND_FLAG_NORMALIZE_INPUT) != 0) ctx->normalizer = backend->ns->user->default_normalizer; return ctx; } static void fts_backend_set_cur_mailbox(struct fts_backend_update_context *ctx) { fts_backend_update_unset_build_key(ctx); if (ctx->backend_box != ctx->cur_box) { ctx->backend->v.update_set_mailbox(ctx, ctx->cur_box); ctx->backend_box = ctx->cur_box; } } int fts_backend_update_deinit(struct fts_backend_update_context **_ctx) { struct fts_backend_update_context *ctx = *_ctx; struct fts_backend *backend = ctx->backend; int ret; *_ctx = NULL; ctx->cur_box = NULL; fts_backend_set_cur_mailbox(ctx); ret = backend->v.update_deinit(ctx); backend->updating = FALSE; return ret; } void fts_backend_update_set_mailbox(struct fts_backend_update_context *ctx, struct mailbox *box) { if (ctx->backend_box != NULL && box != ctx->backend_box) { /* make sure we don't reference the backend box anymore */ ctx->backend->v.update_set_mailbox(ctx, NULL); ctx->backend_box = NULL; } ctx->cur_box = box; } void fts_backend_update_expunge(struct fts_backend_update_context *ctx, uint32_t uid) { fts_backend_set_cur_mailbox(ctx); ctx->backend->v.update_expunge(ctx, uid); } bool fts_backend_update_set_build_key(struct fts_backend_update_context *ctx, const struct fts_backend_build_key *key) { fts_backend_set_cur_mailbox(ctx); i_assert(ctx->cur_box != NULL); if (!ctx->backend->v.update_set_build_key(ctx, key)) return FALSE; ctx->build_key_open = TRUE; return TRUE; } void fts_backend_update_unset_build_key(struct fts_backend_update_context *ctx) { if (ctx->build_key_open) { ctx->backend->v.update_unset_build_key(ctx); ctx->build_key_open = FALSE; } } int fts_backend_update_build_more(struct fts_backend_update_context *ctx, const unsigned char *data, size_t size) { i_assert(ctx->build_key_open); return ctx->backend->v.update_build_more(ctx, data, size); } int fts_backend_refresh(struct fts_backend *backend) { return backend->v.refresh(backend); } int fts_backend_reset_last_uids(struct fts_backend *backend) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; struct mailbox *box; int ret = 0; iter = mailbox_list_iter_init(backend->ns->list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) continue; box = mailbox_alloc(info->ns->list, info->vname, 0); if (mailbox_open(box) == 0) { if (fts_index_set_last_uid(box, 0) < 0) ret = -1; } mailbox_free(&box); } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } int fts_backend_rescan(struct fts_backend *backend) { struct mailbox *box; bool virtual_storage; box = mailbox_alloc(backend->ns->list, "", 0); virtual_storage = box->virtual_vfuncs != NULL; mailbox_free(&box); if (virtual_storage) { /* just reset the last-uids for a virtual storage. */ return fts_backend_reset_last_uids(backend); } return backend->v.rescan == NULL ? 0 : backend->v.rescan(backend); } int fts_backend_optimize(struct fts_backend *backend) { return backend->v.optimize == NULL ? 0 : backend->v.optimize(backend); } static void fts_merge_maybies(ARRAY_TYPE(seq_range) *dest_maybe, const ARRAY_TYPE(seq_range) *dest_definite, const ARRAY_TYPE(seq_range) *src_maybe, const ARRAY_TYPE(seq_range) *src_definite) { ARRAY_TYPE(seq_range) src_unwanted; const struct seq_range *range; struct seq_range new_range; unsigned int i, count; uint32_t seq; /* add/leave to dest_maybe if at least one list has maybe, and no lists have none */ /* create unwanted sequences list from both sources */ t_array_init(&src_unwanted, 128); new_range.seq1 = 0; new_range.seq2 = (uint32_t)-1; array_append(&src_unwanted, &new_range, 1); seq_range_array_remove_seq_range(&src_unwanted, src_maybe); seq_range_array_remove_seq_range(&src_unwanted, src_definite); /* drop unwanted uids */ seq_range_array_remove_seq_range(dest_maybe, &src_unwanted); /* add uids that are in dest_definite and src_maybe lists */ range = array_get(dest_definite, &count); for (i = 0; i < count; i++) { for (seq = range[i].seq1; seq <= range[i].seq2; seq++) { if (seq_range_exists(src_maybe, seq)) seq_range_array_add(dest_maybe, seq); } } } void fts_filter_uids(ARRAY_TYPE(seq_range) *definite_dest, const ARRAY_TYPE(seq_range) *definite_filter, ARRAY_TYPE(seq_range) *maybe_dest, const ARRAY_TYPE(seq_range) *maybe_filter) { T_BEGIN { fts_merge_maybies(maybe_dest, definite_dest, maybe_filter, definite_filter); } T_END; /* keep only what exists in both lists. the rest is in maybies or not wanted */ seq_range_array_intersect(definite_dest, definite_filter); } bool fts_backend_default_can_lookup(struct fts_backend *backend, const struct mail_search_arg *args) { for (; args != NULL; args = args->next) { switch (args->type) { case SEARCH_OR: case SEARCH_SUB: case SEARCH_INTHREAD: if (fts_backend_default_can_lookup(backend, args->value.subargs)) return TRUE; break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: case SEARCH_BODY: case SEARCH_TEXT: if (!args->no_fts) return TRUE; break; default: break; } } return FALSE; } bool fts_backend_can_lookup(struct fts_backend *backend, const struct mail_search_arg *args) { return backend->v.can_lookup(backend, args); } static int fts_score_map_sort(const struct fts_score_map *m1, const struct fts_score_map *m2) { if (m1->uid < m2->uid) return -1; if (m1->uid > m2->uid) return 1; return 0; } int fts_backend_lookup(struct fts_backend *backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result) { array_clear(&result->definite_uids); array_clear(&result->maybe_uids); array_clear(&result->scores); if (backend->v.lookup(backend, box, args, flags, result) < 0) return -1; if (!result->scores_sorted && array_is_created(&result->scores)) { array_sort(&result->scores, fts_score_map_sort); result->scores_sorted = TRUE; } return 0; } int fts_backend_lookup_multi(struct fts_backend *backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result) { unsigned int i; i_assert(boxes[0] != NULL); if (backend->v.lookup_multi != NULL) { if (backend->v.lookup_multi(backend, boxes, args, flags, result) < 0) return -1; if (result->box_results == NULL) { result->box_results = p_new(result->pool, struct fts_result, 1); } return 0; } for (i = 0; boxes[i] != NULL; i++) ; result->box_results = p_new(result->pool, struct fts_result, i+1); for (i = 0; boxes[i] != NULL; i++) { struct fts_result *box_result = &result->box_results[i]; p_array_init(&box_result->definite_uids, result->pool, 32); p_array_init(&box_result->maybe_uids, result->pool, 32); p_array_init(&box_result->scores, result->pool, 32); if (backend->v.lookup(backend, boxes[i], args, flags, box_result) < 0) return -1; } return 0; } void fts_backend_lookup_done(struct fts_backend *backend) { if (backend->v.lookup_done != NULL) backend->v.lookup_done(backend); } static uint32_t fts_index_get_ext_id(struct mailbox *box) { return mail_index_ext_register(box->index, "fts", sizeof(struct fts_index_header), 0, 0); } bool fts_index_get_header(struct mailbox *box, struct fts_index_header *hdr_r) { struct mail_index_view *view; const void *data; size_t data_size; bool ret; mail_index_refresh(box->index); view = mail_index_view_open(box->index); mail_index_get_header_ext(view, fts_index_get_ext_id(box), &data, &data_size); if (data_size < sizeof(*hdr_r)) { i_zero(hdr_r); ret = FALSE; } else { memcpy(hdr_r, data, data_size); ret = TRUE; } mail_index_view_close(&view); return ret; } int fts_index_set_header(struct mailbox *box, const struct fts_index_header *hdr) { struct mail_index_transaction *trans; uint32_t ext_id = fts_index_get_ext_id(box); trans = mail_index_transaction_begin(box->view, 0); mail_index_update_header_ext(trans, ext_id, 0, hdr, sizeof(*hdr)); return mail_index_transaction_commit(&trans); } int fts_index_set_last_uid(struct mailbox *box, uint32_t last_uid) { struct fts_index_header hdr; (void)fts_index_get_header(box, &hdr); hdr.last_indexed_uid = last_uid; return fts_index_set_header(box, &hdr); } int fts_index_have_compatible_settings(struct mailbox_list *list, uint32_t checksum) { struct mail_namespace *ns = mailbox_list_get_namespace(list); struct mailbox *box; struct fts_index_header hdr; const char *vname; size_t len; int ret; if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) vname = "INBOX"; else { len = strlen(ns->prefix); if (len > 0 && ns->prefix[len-1] == mail_namespace_get_sep(ns)) len--; vname = t_strndup(ns->prefix, len); } box = mailbox_alloc(list, vname, 0); if (mailbox_sync(box, (enum mailbox_sync_flags)0) < 0) { i_error("fts: Failed to sync mailbox %s: %s", vname, mailbox_get_last_internal_error(box, NULL)); ret = -1; } else { ret = fts_index_get_header(box, &hdr) && hdr.settings_checksum == checksum ? 1 : 0; } mailbox_free(&box); return ret; } static const char *indexed_headers[] = { "From", "To", "Cc", "Bcc", "Subject" }; bool fts_header_want_indexed(const char *hdr_name) { unsigned int i; for (i = 0; i < N_ELEMENTS(indexed_headers); i++) { if (strcasecmp(hdr_name, indexed_headers[i]) == 0) return TRUE; } return FALSE; } bool fts_header_has_language(const char *hdr_name) { /* FIXME: should email address headers be detected as different languages? That mainly contains people's names.. */ /*if (message_header_is_address(hdr_name)) return TRUE;*/ /* Subject definitely contains language-specific data that can be detected. Comment and Keywords headers also could contain, although just about nobody uses those headers. For now we assume that other headers contain non-language specific data that we don't want to filter in special ways. For example it is good to be able to search for Message-IDs. */ return strcasecmp(hdr_name, "Subject") == 0 || strcasecmp(hdr_name, "Comments") == 0 || strcasecmp(hdr_name, "Keywords") == 0; } int fts_mailbox_get_guid(struct mailbox *box, const char **guid_r) { struct mailbox_metadata metadata; if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) return -1; *guid_r = guid_128_to_string(metadata.guid); return 0; } dovecot-2.2.33.2/src/plugins/fts/doveadm-dump-fts-expunge-log.c0000644000175000017500000000512513123174404021143 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "hex-binary.h" #include "guid.h" #include "doveadm-dump.h" #include "doveadm-fts.h" #include #include #include struct fts_expunge_log_record { uint32_t checksum; uint32_t record_size; guid_128_t guid; }; static int dump_record(int fd, buffer_t *buf) { struct fts_expunge_log_record rec; off_t offset; void *data; const uint32_t *expunges, *uids; ssize_t ret; size_t data_size; unsigned int i, uids_count; offset = lseek(fd, 0, SEEK_CUR); ret = read(fd, &rec, sizeof(rec)); if (ret == 0) return 0; if (ret != sizeof(rec)) i_fatal("rec read() %d != %d", (int)ret, (int)sizeof(rec)); if (rec.record_size < sizeof(rec) + sizeof(uint32_t) || rec.record_size > INT_MAX) { i_fatal("Invalid record_size=%u at offset %"PRIuUOFF_T, rec.record_size, offset); } data_size = rec.record_size - sizeof(rec); buffer_set_used_size(buf, 0); data = buffer_append_space_unsafe(buf, data_size); ret = read(fd, data, data_size); if (ret != (ssize_t)data_size) i_fatal("rec read() %d != %d", (int)ret, (int)data_size); printf("#%"PRIuUOFF_T":\n", offset); printf(" checksum = %8x\n", rec.checksum); printf(" size .... = %u\n", rec.record_size); printf(" mailbox . = %s\n", guid_128_to_string(rec.guid)); expunges = CONST_PTR_OFFSET(data, data_size - sizeof(uint32_t)); printf(" expunges = %u\n", *expunges); printf(" uids .... = "); uids = data; uids_count = (rec.record_size - sizeof(rec) - sizeof(uint32_t)) / sizeof(uint32_t); for (i = 0; i < uids_count; i += 2) { if (i != 0) printf(","); if (uids[i] == uids[i+1]) printf("%u", uids[i]); else printf("%u-%u", uids[i], uids[i+1]); } printf("\n"); return 1; } static void cmd_dump_fts_expunge_log(int argc ATTR_UNUSED, char *argv[]) { buffer_t *buf; int fd, ret; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); buf = buffer_create_dynamic(default_pool, 1024); do { T_BEGIN { ret = dump_record(fd, buf); } T_END; } while (ret > 0); buffer_free(&buf); i_close_fd(&fd); } static bool test_dump_fts_expunge_log(const char *path) { const char *p; if ((p = strrchr(path, '/')) != NULL) p++; else p = path; return strcmp(p, "dovecot-expunges.log") == 0; } static const struct doveadm_cmd_dump doveadm_cmd_dump_fts_expunge_log = { "fts-expunge-log", test_dump_fts_expunge_log, cmd_dump_fts_expunge_log }; void doveadm_dump_fts_expunge_log_init(void) { doveadm_dump_register(&doveadm_cmd_dump_fts_expunge_log); } dovecot-2.2.33.2/src/plugins/fts/fts-api-private.h0000644000175000017500000001047213165463624016573 00000000000000#ifndef FTS_API_PRIVATE_H #define FTS_API_PRIVATE_H #include "unichar.h" #include "fts-api.h" struct mail_user; struct mailbox_list; #define MAILBOX_GUID_HEX_LENGTH (GUID_128_SIZE*2) struct fts_backend_vfuncs { struct fts_backend *(*alloc)(void); int (*init)(struct fts_backend *backend, const char **error_r); void (*deinit)(struct fts_backend *backend); int (*get_last_uid)(struct fts_backend *backend, struct mailbox *box, uint32_t *last_uid_r); struct fts_backend_update_context * (*update_init)(struct fts_backend *backend); int (*update_deinit)(struct fts_backend_update_context *ctx); void (*update_set_mailbox)(struct fts_backend_update_context *ctx, struct mailbox *box); void (*update_expunge)(struct fts_backend_update_context *ctx, uint32_t uid); /* Start a build for specified key */ bool (*update_set_build_key)(struct fts_backend_update_context *ctx, const struct fts_backend_build_key *key); /* Finish a build for specified key - guaranteed to be called */ void (*update_unset_build_key)(struct fts_backend_update_context *ctx); /* Add data for current build key */ int (*update_build_more)(struct fts_backend_update_context *ctx, const unsigned char *data, size_t size); int (*refresh)(struct fts_backend *backend); int (*rescan)(struct fts_backend *backend); int (*optimize)(struct fts_backend *backend); bool (*can_lookup)(struct fts_backend *backend, const struct mail_search_arg *args); int (*lookup)(struct fts_backend *backend, struct mailbox *box, struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_result *result); int (*lookup_multi)(struct fts_backend *backend, struct mailbox *const boxes[], struct mail_search_arg *args, enum fts_lookup_flags flags, struct fts_multi_result *result); void (*lookup_done)(struct fts_backend *backend); }; enum fts_backend_flags { /* Backend supports indexing binary MIME parts */ FTS_BACKEND_FLAG_BINARY_MIME_PARTS = 0x01, /* Send built text to backend normalized rather than preserving original case */ FTS_BACKEND_FLAG_NORMALIZE_INPUT = 0x02, /* Send only fully indexable words rather than randomly sized blocks */ FTS_BACKEND_FLAG_BUILD_FULL_WORDS = 0x04, /* Fuzzy search works */ FTS_BACKEND_FLAG_FUZZY_SEARCH = 0x08, /* Tokenize all the input. update_build_more() will be called a single directly indexable token at a time. Searching will modify the search args so that lookup() sees only tokens that can be directly searched. */ FTS_BACKEND_FLAG_TOKENIZED_INPUT = 0x10 }; struct fts_backend { const char *name; enum fts_backend_flags flags; struct fts_backend_vfuncs v; struct mail_namespace *ns; unsigned int updating:1; }; struct fts_backend_update_context { struct fts_backend *backend; normalizer_func_t *normalizer; struct mailbox *cur_box, *backend_box; unsigned int build_key_open:1; unsigned int failed:1; }; struct fts_index_header { uint32_t last_indexed_uid; /* Checksum of settings. If the settings change, the index should be rebuilt. */ uint32_t settings_checksum; uint32_t unused; }; void fts_backend_register(const struct fts_backend *backend); void fts_backend_unregister(const char *name); bool fts_backend_default_can_lookup(struct fts_backend *backend, const struct mail_search_arg *args); void fts_filter_uids(ARRAY_TYPE(seq_range) *definite_dest, const ARRAY_TYPE(seq_range) *definite_filter, ARRAY_TYPE(seq_range) *maybe_dest, const ARRAY_TYPE(seq_range) *maybe_filter); /* Returns TRUE if ok, FALSE if no fts header */ bool fts_index_get_header(struct mailbox *box, struct fts_index_header *hdr_r); int fts_index_set_header(struct mailbox *box, const struct fts_index_header *hdr); int ATTR_NOWARN_UNUSED_RESULT fts_index_set_last_uid(struct mailbox *box, uint32_t last_uid); int fts_backend_reset_last_uids(struct fts_backend *backend); int fts_index_have_compatible_settings(struct mailbox_list *list, uint32_t checksum); /* Returns TRUE if FTS backend should index the header for optimizing separate lookups */ bool fts_header_want_indexed(const char *hdr_name); /* Returns TRUE if header's values should be considered to have a language. */ bool fts_header_has_language(const char *hdr_name); int fts_mailbox_get_guid(struct mailbox *box, const char **guid_r); #endif dovecot-2.2.33.2/src/plugins/fts/Makefile.am0000644000175000017500000000315213165463624015440 00000000000000pkglibexecdir = $(libexecdir)/dovecot doveadm_moduledir = $(moduledir)/doveadm AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-fts \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/doveadm NOPLUGIN_LDFLAGS = lib20_doveadm_fts_plugin_la_LDFLAGS = -module -avoid-version lib20_fts_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib20_fts_plugin.la lib20_fts_plugin_la_LIBADD = ../../lib-fts/libfts.la lib20_fts_plugin_la_SOURCES = \ fts-api.c \ fts-build-mail.c \ fts-expunge-log.c \ fts-indexer.c \ fts-parser.c \ fts-parser-html.c \ fts-parser-script.c \ fts-parser-tika.c \ fts-plugin.c \ fts-search.c \ fts-search-args.c \ fts-search-serialize.c \ fts-storage.c \ fts-user.c pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = \ fts-api.h \ fts-api-private.h \ fts-expunge-log.h \ fts-indexer.h \ fts-parser.h \ fts-storage.h \ fts-user.h noinst_HEADERS = \ doveadm-fts.h \ fts-build-mail.h \ fts-plugin.h \ fts-search-args.h \ fts-search-serialize.h pkglibexec_PROGRAMS = xml2text xml2text_SOURCES = xml2text.c xml2text_LDADD = fts-parser-html.lo $(LIBDOVECOT) xml2text_DEPENDENCIES = $(module_LTLIBRARIES) $(LIBDOVECOT_DEPS) pkglibexec_SCRIPTS = decode2text.sh EXTRA_DIST = $(pkglibexec_SCRIPTS) doveadm_module_LTLIBRARIES = \ lib20_doveadm_fts_plugin.la lib20_doveadm_fts_plugin_la_SOURCES = \ doveadm-fts.c \ doveadm-dump-fts-expunge-log.c dovecot-2.2.33.2/src/plugins/Makefile.am0000644000175000017500000000126213165463624014644 00000000000000if BUILD_ZLIB_PLUGIN ZLIB = zlib imap-zlib endif if BUILD_LUCENE FTS_LUCENE = fts-lucene endif if BUILD_SOLR FTS_SOLR = fts-solr endif if HAVE_LDAP DICT_LDAP = dict-ldap endif if HAVE_APPARMOR APPARMOR = apparmor endif SUBDIRS = \ acl \ imap-acl \ autocreate \ expire \ fts \ fts-squat \ last-login \ lazy-expunge \ listescape \ notify \ notify-status \ push-notification \ mail-filter \ mail-log \ mailbox-alias \ quota \ quota-clone \ imap-quota \ pop3-migration \ replication \ snarf \ stats \ imap-stats \ mail-crypt \ trash \ virtual \ welcome \ $(ZLIB) \ $(FTS_LUCENE) \ $(FTS_SOLR) \ $(DICT_LDAP) \ $(APPARMOR) \ fs-compress \ var-expand-crypt dovecot-2.2.33.2/src/auth/0002755000175000017500000000000013172375612012146 500000000000000dovecot-2.2.33.2/src/auth/mech-plain-common.h0000644000175000017500000000023413123174404015527 00000000000000#ifndef MECH_PLAIN_COMMON_H #define MECH_PLAIN_COMMON_H void plain_verify_callback(enum passdb_result result, struct auth_request *request); #endif dovecot-2.2.33.2/src/auth/userdb-checkpassword.c0000644000175000017500000000433613123174404016350 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_CHECKPASSWORD #include "db-checkpassword.h" struct checkpassword_userdb_module { struct userdb_module module; struct db_checkpassword *db; }; static void userdb_checkpassword_callback(struct auth_request *request, enum db_checkpassword_status status, const char *const *extra_fields, userdb_callback_t *callback) { unsigned int i; switch (status) { case DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE: callback(USERDB_RESULT_INTERNAL_FAILURE, request); break; case DB_CHECKPASSWORD_STATUS_FAILURE: callback(USERDB_RESULT_USER_UNKNOWN, request); break; case DB_CHECKPASSWORD_STATUS_OK: for (i = 0; extra_fields[i] != NULL; i++) { if (strncmp(extra_fields[i], "userdb_", 7) != 0) continue; auth_request_set_field_keyvalue(request, extra_fields[i], NULL); } callback(USERDB_RESULT_OK, request); break; } } static void checkpassword_lookup(struct auth_request *request, userdb_callback_t *callback) { struct userdb_module *_module = request->userdb->userdb; struct checkpassword_userdb_module *module = (struct checkpassword_userdb_module *)_module; db_checkpassword_call(module->db, request, NULL, userdb_checkpassword_callback, callback); } static struct userdb_module * checkpassword_preinit(pool_t pool, const char *args) { struct checkpassword_userdb_module *module; const char *checkpassword_path = args; const char *checkpassword_reply_path = PKG_LIBEXECDIR"/checkpassword-reply"; module = p_new(pool, struct checkpassword_userdb_module, 1); module->db = db_checkpassword_init(checkpassword_path, checkpassword_reply_path); return &module->module; } static void checkpassword_deinit(struct userdb_module *_module) { struct checkpassword_userdb_module *module = (struct checkpassword_userdb_module *)_module; db_checkpassword_deinit(&module->db); } struct userdb_module_interface userdb_checkpassword = { "checkpassword", checkpassword_preinit, NULL, checkpassword_deinit, checkpassword_lookup, NULL, NULL, NULL }; #else struct userdb_module_interface userdb_checkpassword = { .name = "checkpassword" }; #endif dovecot-2.2.33.2/src/auth/auth-worker-server.h0000644000175000017500000000100613123174404015776 00000000000000#ifndef AUTH_WORKER_SERVER_H #define AUTH_WORKER_SERVER_H struct auth_request; struct auth_stream_reply; typedef bool auth_worker_callback_t(const char *reply, void *context); struct auth_worker_connection * ATTR_NOWARN_UNUSED_RESULT auth_worker_call(pool_t pool, const char *username, const char *data, auth_worker_callback_t *callback, void *context); void auth_worker_server_resume_input(struct auth_worker_connection *conn); void auth_worker_server_init(void); void auth_worker_server_deinit(void); #endif dovecot-2.2.33.2/src/auth/password-scheme.c0000644000175000017500000005755313165463624015356 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "base64.h" #include "hex-binary.h" #include "md4.h" #include "md5.h" #include "hmac.h" #include "hmac-cram-md5.h" #include "ntlm.h" #include "mycrypt.h" #include "randgen.h" #include "sha1.h" #include "sha2.h" #include "otp.h" #include "str.h" #include "password-scheme.h" static const char salt_chars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; ARRAY_TYPE(password_scheme_p) password_schemes; static const struct password_scheme * password_scheme_lookup_name(const char *name) { const struct password_scheme *const *schemes; array_foreach(&password_schemes, schemes) { const struct password_scheme *scheme = *schemes; if (strcasecmp(scheme->name, name) == 0) return scheme; } return NULL; } /* Lookup scheme and encoding by given name. The encoding is taken from ".base64", ".b64" or ".hex" suffix if it exists, otherwise the default encoding is used. */ static const struct password_scheme * password_scheme_lookup(const char *name, enum password_encoding *encoding_r) { const struct password_scheme *scheme; const char *encoding = NULL; size_t scheme_len; *encoding_r = PW_ENCODING_NONE; for (scheme_len = 0; name[scheme_len] != '\0'; scheme_len++) { if (name[scheme_len] == '.') { encoding = name + scheme_len + 1; name = t_strndup(name, scheme_len); break; } } scheme = password_scheme_lookup_name(name); if (scheme == NULL) return NULL; if (encoding == NULL) *encoding_r = scheme->default_encoding; else if (strcasecmp(encoding, "b64") == 0 || strcasecmp(encoding, "base64") == 0) *encoding_r = PW_ENCODING_BASE64; else if (strcasecmp(encoding, "hex") == 0) *encoding_r = PW_ENCODING_HEX; else { /* unknown encoding. treat as invalid scheme. */ return NULL; } return scheme; } int password_verify(const char *plaintext, const char *user, const char *scheme, const unsigned char *raw_password, size_t size, const char **error_r) { const struct password_scheme *s; enum password_encoding encoding; const unsigned char *generated; size_t generated_size; int ret; s = password_scheme_lookup(scheme, &encoding); if (s == NULL) { *error_r = "Unknown password scheme"; return -1; } if (s->password_verify != NULL) { ret = s->password_verify(plaintext, user, raw_password, size, error_r); } else { /* generic verification handler: generate the password and compare it to the one in database */ s->password_generate(plaintext, user, &generated, &generated_size); ret = size != generated_size ? 0 : mem_equals_timing_safe(generated, raw_password, size) ? 1 : 0; } if (ret == 0) *error_r = "Password mismatch"; return ret; } const char *password_get_scheme(const char **password) { const char *p, *scheme; if (*password == NULL) return NULL; if (strncmp(*password, "$1$", 3) == 0) { /* $1$$[$] */ p = strchr(*password + 3, '$'); if (p != NULL) { /* stop at next '$' after password */ p = strchr(p+1, '$'); if (p != NULL) *password = t_strdup_until(*password, p); return "MD5-CRYPT"; } } if (**password != '{') return NULL; p = strchr(*password, '}'); if (p == NULL) return NULL; scheme = t_strdup_until(*password + 1, p); *password = p + 1; return scheme; } int password_decode(const char *password, const char *scheme, const unsigned char **raw_password_r, size_t *size_r, const char **error_r) { const struct password_scheme *s; enum password_encoding encoding; buffer_t *buf; size_t len; bool guessed_encoding; *error_r = NULL; s = password_scheme_lookup(scheme, &encoding); if (s == NULL) { *error_r = "Unknown scheme"; return 0; } len = strlen(password); if (encoding != PW_ENCODING_NONE && s->raw_password_len != 0 && strchr(scheme, '.') == NULL) { /* encoding not specified. we can guess quite well between base64 and hex encodings. the only problem is distinguishing 2 character strings, but there shouldn't be any that short raw_password_lens. */ encoding = len == s->raw_password_len * 2 ? PW_ENCODING_HEX : PW_ENCODING_BASE64; guessed_encoding = TRUE; } else { guessed_encoding = FALSE; } switch (encoding) { case PW_ENCODING_NONE: *raw_password_r = (const unsigned char *)password; *size_r = len; break; case PW_ENCODING_HEX: buf = buffer_create_dynamic(pool_datastack_create(), len / 2 + 1); if (hex_to_binary(password, buf) == 0) { *raw_password_r = buf->data; *size_r = buf->used; break; } if (!guessed_encoding) { *error_r = "Input isn't valid HEX encoded data"; return -1; } /* check if it's base64-encoded after all. some input lengths produce matching hex and base64 encoded lengths. */ /* fall through */ case PW_ENCODING_BASE64: buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(len)); if (base64_decode(password, len, NULL, buf) < 0) { *error_r = "Input isn't valid base64 encoded data"; return -1; } *raw_password_r = buf->data; *size_r = buf->used; break; } if (s->raw_password_len != *size_r && s->raw_password_len != 0) { /* password has invalid length */ *error_r = t_strdup_printf( "Input length isn't valid (%u instead of %u)", (unsigned int)*size_r, s->raw_password_len); return -1; } return 1; } bool password_generate(const char *plaintext, const char *user, const char *scheme, const unsigned char **raw_password_r, size_t *size_r) { const struct password_scheme *s; enum password_encoding encoding; s = password_scheme_lookup(scheme, &encoding); if (s == NULL) return FALSE; s->password_generate(plaintext, user, raw_password_r, size_r); return TRUE; } bool password_generate_encoded(const char *plaintext, const char *user, const char *scheme, const char **password_r) { const struct password_scheme *s; const unsigned char *raw_password; enum password_encoding encoding; string_t *str; size_t size; s = password_scheme_lookup(scheme, &encoding); if (s == NULL) return FALSE; s->password_generate(plaintext, user, &raw_password, &size); switch (encoding) { case PW_ENCODING_NONE: *password_r = t_strndup(raw_password, size); break; case PW_ENCODING_BASE64: str = t_str_new(MAX_BASE64_ENCODED_SIZE(size) + 1); base64_encode(raw_password, size, str); *password_r = str_c(str); break; case PW_ENCODING_HEX: *password_r = binary_to_hex(raw_password, size); break; } return TRUE; } const char *password_generate_salt(size_t len) { unsigned int i; char *salt; salt = t_malloc(len + 1); random_fill(salt, len); for (i = 0; i < len; i++) salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)]; salt[len] = '\0'; return salt; } bool password_scheme_is_alias(const char *scheme1, const char *scheme2) { const struct password_scheme *const *schemes, *s1 = NULL, *s2 = NULL; scheme1 = t_strcut(scheme1, '.'); scheme2 = t_strcut(scheme2, '.'); if (strcasecmp(scheme1, scheme2) == 0) return TRUE; array_foreach(&password_schemes, schemes) { const struct password_scheme *scheme = *schemes; if (strcasecmp(scheme->name, scheme1) == 0) s1 = scheme; else if (strcasecmp(scheme->name, scheme2) == 0) s2 = scheme; } /* if they've the same generate function, they're equivalent */ return s1 != NULL && s2 != NULL && s1->password_generate == s2->password_generate; } const char * password_scheme_detect(const char *plain_password, const char *crypted_password, const char *user) { const struct password_scheme *const *schemes; unsigned int i, count; const unsigned char *raw_password; size_t raw_password_size; const char *error; schemes = array_get(&password_schemes, &count); for (i = 0; i < count; i++) { if (password_decode(crypted_password, schemes[i]->name, &raw_password, &raw_password_size, &error) <= 0) continue; if (password_verify(plain_password, user, schemes[i]->name, raw_password, raw_password_size, &error) > 0) return schemes[i]->name; } return NULL; } int crypt_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { const char *password, *crypted; if (size == 0) { /* the default mycrypt() handler would return match */ return 0; } password = t_strndup(raw_password, size); crypted = mycrypt(plaintext, password); if (crypted == NULL) { /* really shouldn't happen unless the system is broken */ *error_r = t_strdup_printf("crypt() failed: %m"); return -1; } return strcmp(crypted, password) == 0 ? 1 : 0; } static int md5_verify(const char *plaintext, const char *user, const unsigned char *raw_password, size_t size, const char **error_r) { const char *password, *str, *error; const unsigned char *md5_password; size_t md5_size; password = t_strndup(raw_password, size); if (strncmp(password, "$1$", 3) == 0) { /* MD5-CRYPT */ str = password_generate_md5_crypt(plaintext, password); return strcmp(str, password) == 0 ? 1 : 0; } else if (password_decode(password, "PLAIN-MD5", &md5_password, &md5_size, &error) <= 0) { *error_r = "Not a valid MD5-CRYPT or PLAIN-MD5 password"; return -1; } else { return password_verify(plaintext, user, "PLAIN-MD5", md5_password, md5_size, error_r); } } static int md5_crypt_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r ATTR_UNUSED) { const char *password, *str; password = t_strndup(raw_password, size); str = password_generate_md5_crypt(plaintext, password); return strcmp(str, password) == 0 ? 1 : 0; } static void md5_crypt_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password; char salt[9]; unsigned int i; random_fill(salt, sizeof(salt)-1); for (i = 0; i < sizeof(salt)-1; i++) salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)]; salt[sizeof(salt)-1] = '\0'; password = password_generate_md5_crypt(plaintext, salt); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void sha1_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(SHA1_RESULTLEN); sha1_get_digest(plaintext, strlen(plaintext), digest); *raw_password_r = digest; *size_r = SHA1_RESULTLEN; } static void sha256_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(SHA256_RESULTLEN); sha256_get_digest(plaintext, strlen(plaintext), digest); *raw_password_r = digest; *size_r = SHA256_RESULTLEN; } static void sha512_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(SHA512_RESULTLEN); sha512_get_digest(plaintext, strlen(plaintext), digest); *raw_password_r = digest; *size_r = SHA512_RESULTLEN; } static void ssha_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { #define SSHA_SALT_LEN 4 unsigned char *digest, *salt; struct sha1_ctxt ctx; digest = t_malloc(SHA1_RESULTLEN + SSHA_SALT_LEN); salt = digest + SHA1_RESULTLEN; random_fill(salt, SSHA_SALT_LEN); sha1_init(&ctx); sha1_loop(&ctx, plaintext, strlen(plaintext)); sha1_loop(&ctx, salt, SSHA_SALT_LEN); sha1_result(&ctx, digest); *raw_password_r = digest; *size_r = SHA1_RESULTLEN + SSHA_SALT_LEN; } static int ssha_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { unsigned char sha1_digest[SHA1_RESULTLEN]; struct sha1_ctxt ctx; /* format: */ if (size <= SHA1_RESULTLEN) { *error_r = "SSHA password is too short"; return -1; } sha1_init(&ctx); sha1_loop(&ctx, plaintext, strlen(plaintext)); sha1_loop(&ctx, raw_password + SHA1_RESULTLEN, size - SHA1_RESULTLEN); sha1_result(&ctx, sha1_digest); return mem_equals_timing_safe(sha1_digest, raw_password, SHA1_RESULTLEN) ? 1 : 0; } static void ssha256_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { #define SSHA256_SALT_LEN 4 unsigned char *digest, *salt; struct sha256_ctx ctx; digest = t_malloc(SHA256_RESULTLEN + SSHA256_SALT_LEN); salt = digest + SHA256_RESULTLEN; random_fill(salt, SSHA256_SALT_LEN); sha256_init(&ctx); sha256_loop(&ctx, plaintext, strlen(plaintext)); sha256_loop(&ctx, salt, SSHA256_SALT_LEN); sha256_result(&ctx, digest); *raw_password_r = digest; *size_r = SHA256_RESULTLEN + SSHA256_SALT_LEN; } static int ssha256_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { unsigned char sha256_digest[SHA256_RESULTLEN]; struct sha256_ctx ctx; /* format: */ if (size <= SHA256_RESULTLEN) { *error_r = "SSHA256 password is too short"; return -1; } sha256_init(&ctx); sha256_loop(&ctx, plaintext, strlen(plaintext)); sha256_loop(&ctx, raw_password + SHA256_RESULTLEN, size - SHA256_RESULTLEN); sha256_result(&ctx, sha256_digest); return mem_equals_timing_safe(sha256_digest, raw_password, SHA256_RESULTLEN) ? 1 : 0; } static void ssha512_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { #define SSHA512_SALT_LEN 4 unsigned char *digest, *salt; struct sha512_ctx ctx; digest = t_malloc(SHA512_RESULTLEN + SSHA512_SALT_LEN); salt = digest + SHA512_RESULTLEN; random_fill(salt, SSHA512_SALT_LEN); sha512_init(&ctx); sha512_loop(&ctx, plaintext, strlen(plaintext)); sha512_loop(&ctx, salt, SSHA512_SALT_LEN); sha512_result(&ctx, digest); *raw_password_r = digest; *size_r = SHA512_RESULTLEN + SSHA512_SALT_LEN; } static int ssha512_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { unsigned char sha512_digest[SHA512_RESULTLEN]; struct sha512_ctx ctx; /* format: */ if (size <= SHA512_RESULTLEN) { *error_r = "SSHA512 password is too short"; return -1; } sha512_init(&ctx); sha512_loop(&ctx, plaintext, strlen(plaintext)); sha512_loop(&ctx, raw_password + SHA512_RESULTLEN, size - SHA512_RESULTLEN); sha512_result(&ctx, sha512_digest); return mem_equals_timing_safe(sha512_digest, raw_password, SHA512_RESULTLEN) ? 1 : 0; } static void smd5_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { #define SMD5_SALT_LEN 4 unsigned char *digest, *salt; struct md5_context ctx; digest = t_malloc(MD5_RESULTLEN + SMD5_SALT_LEN); salt = digest + MD5_RESULTLEN; random_fill(salt, SMD5_SALT_LEN); md5_init(&ctx); md5_update(&ctx, plaintext, strlen(plaintext)); md5_update(&ctx, salt, SMD5_SALT_LEN); md5_final(&ctx, digest); *raw_password_r = digest; *size_r = MD5_RESULTLEN + SMD5_SALT_LEN; } static int smd5_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { unsigned char md5_digest[MD5_RESULTLEN]; struct md5_context ctx; /* format: */ if (size <= MD5_RESULTLEN) { *error_r = "SMD5 password is too short"; return -1; } md5_init(&ctx); md5_update(&ctx, plaintext, strlen(plaintext)); md5_update(&ctx, raw_password + MD5_RESULTLEN, size - MD5_RESULTLEN); md5_final(&ctx, md5_digest); return mem_equals_timing_safe(md5_digest, raw_password, MD5_RESULTLEN) ? 1 : 0; } static void plain_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { *raw_password_r = (const unsigned char *)plaintext, *size_r = strlen(plaintext); } static int plain_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r ATTR_UNUSED) { size_t plaintext_len = strlen(plaintext); if (plaintext_len != size) return 0; return mem_equals_timing_safe(plaintext, raw_password, size) ? 1 : 0; } static int plain_trunc_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { size_t i, plaintext_len, trunc_len = 0; /* format: - */ for (i = 0; i < size; i++) { if (raw_password[i] >= '0' && raw_password[i] <= '9') trunc_len = trunc_len*10 + raw_password[i]-'0'; else break; } if (i == size || raw_password[i] != '-') { *error_r = "PLAIN-TRUNC missing length: prefix"; return -1; } i++; plaintext_len = strlen(plaintext); if (size-i == trunc_len && plaintext_len >= trunc_len) { /* possibly truncated password. allow the given password as long as the prefix matches. */ return mem_equals_timing_safe(raw_password+i, plaintext, trunc_len) ? 1 : 0; } return plaintext_len == size-i && mem_equals_timing_safe(raw_password+i, plaintext, plaintext_len) ? 1 : 0; } static void cram_md5_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { struct hmac_context ctx; unsigned char *context_digest; context_digest = t_malloc(CRAM_MD5_CONTEXTLEN); hmac_init(&ctx, (const unsigned char *)plaintext, strlen(plaintext), &hash_method_md5); hmac_md5_get_cram_context(&ctx, context_digest); *raw_password_r = context_digest; *size_r = CRAM_MD5_CONTEXTLEN; } static void digest_md5_generate(const char *plaintext, const char *user, const unsigned char **raw_password_r, size_t *size_r) { const char *realm, *str; unsigned char *digest; if (user == NULL) i_fatal("digest_md5_generate(): username not given"); /* assume user@realm format for username. If user@domain is wanted in the username, allow also user@domain@realm. */ realm = strrchr(user, '@'); if (realm != NULL) { user = t_strdup_until(user, realm); realm++; } else { realm = ""; } /* user:realm:passwd */ digest = t_malloc(MD5_RESULTLEN); str = t_strdup_printf("%s:%s:%s", user, realm, plaintext); md5_get_digest(str, strlen(str), digest); *raw_password_r = digest; *size_r = MD5_RESULTLEN; } static void plain_md4_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(MD4_RESULTLEN); md4_get_digest(plaintext, strlen(plaintext), digest); *raw_password_r = digest; *size_r = MD4_RESULTLEN; } static void plain_md5_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(MD5_RESULTLEN); md5_get_digest(plaintext, strlen(plaintext), digest); *raw_password_r = digest; *size_r = MD5_RESULTLEN; } static void lm_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(LM_HASH_SIZE); lm_hash(plaintext, digest); *raw_password_r = digest; *size_r = LM_HASH_SIZE; } static void ntlm_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(NTLMSSP_HASH_SIZE); ntlm_v1_hash(plaintext, digest); *raw_password_r = digest; *size_r = NTLMSSP_HASH_SIZE; } static int otp_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { const char *password, *generated; password = t_strndup(raw_password, size); if (password_generate_otp(plaintext, password, -1, &generated) < 0) { *error_r = "Invalid OTP data in passdb"; return -1; } return strcasecmp(password, generated) == 0 ? 1 : 0; } static void otp_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password; if (password_generate_otp(plaintext, NULL, OTP_HASH_SHA1, &password) < 0) i_unreached(); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void skey_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password; if (password_generate_otp(plaintext, NULL, OTP_HASH_MD4, &password) < 0) i_unreached(); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void rpa_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char *digest; digest = t_malloc(MD5_RESULTLEN); password_generate_rpa(plaintext, digest); *raw_password_r = digest; *size_r = MD5_RESULTLEN; } static const struct password_scheme builtin_schemes[] = { { "MD5", PW_ENCODING_NONE, 0, md5_verify, md5_crypt_generate }, { "MD5-CRYPT", PW_ENCODING_NONE, 0, md5_crypt_verify, md5_crypt_generate }, { "SHA", PW_ENCODING_BASE64, SHA1_RESULTLEN, NULL, sha1_generate }, { "SHA1", PW_ENCODING_BASE64, SHA1_RESULTLEN, NULL, sha1_generate }, { "SHA256", PW_ENCODING_BASE64, SHA256_RESULTLEN, NULL, sha256_generate }, { "SHA512", PW_ENCODING_BASE64, SHA512_RESULTLEN, NULL, sha512_generate }, { "SMD5", PW_ENCODING_BASE64, 0, smd5_verify, smd5_generate }, { "SSHA", PW_ENCODING_BASE64, 0, ssha_verify, ssha_generate }, { "SSHA256", PW_ENCODING_BASE64, 0, ssha256_verify, ssha256_generate }, { "SSHA512", PW_ENCODING_BASE64, 0, ssha512_verify, ssha512_generate }, { "PLAIN", PW_ENCODING_NONE, 0, plain_verify, plain_generate }, { "CLEAR", PW_ENCODING_NONE, 0, plain_verify, plain_generate }, { "CLEARTEXT", PW_ENCODING_NONE, 0, plain_verify, plain_generate }, { "PLAIN-TRUNC", PW_ENCODING_NONE, 0, plain_trunc_verify, plain_generate }, { "CRAM-MD5", PW_ENCODING_HEX, CRAM_MD5_CONTEXTLEN, NULL, cram_md5_generate }, { "SCRAM-SHA-1", PW_ENCODING_NONE, 0, scram_sha1_verify, scram_sha1_generate}, { "HMAC-MD5", PW_ENCODING_HEX, CRAM_MD5_CONTEXTLEN, NULL, cram_md5_generate }, { "DIGEST-MD5", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, digest_md5_generate }, { "PLAIN-MD4", PW_ENCODING_HEX, MD4_RESULTLEN, NULL, plain_md4_generate }, { "PLAIN-MD5", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, plain_md5_generate }, { "LDAP-MD5", PW_ENCODING_BASE64, MD5_RESULTLEN, NULL, plain_md5_generate }, { "LANMAN", PW_ENCODING_HEX, LM_HASH_SIZE, NULL, lm_generate }, { "NTLM", PW_ENCODING_HEX, NTLMSSP_HASH_SIZE, NULL, ntlm_generate }, { "OTP", PW_ENCODING_NONE, 0, otp_verify, otp_generate }, { "SKEY", PW_ENCODING_NONE, 0, otp_verify, skey_generate }, { "RPA", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, rpa_generate }, { "PBKDF2", PW_ENCODING_NONE, 0, pbkdf2_verify, pbkdf2_generate }, }; void password_scheme_register(const struct password_scheme *scheme) { if (password_scheme_lookup_name(scheme->name) != NULL) { i_panic("password_scheme_register(%s): Already registered", scheme->name); } array_append(&password_schemes, &scheme, 1); } void password_scheme_unregister(const struct password_scheme *scheme) { const struct password_scheme *const *schemes; unsigned int idx; array_foreach(&password_schemes, schemes) { if (strcasecmp((*schemes)->name, scheme->name) == 0) { idx = array_foreach_idx(&password_schemes, schemes); array_delete(&password_schemes, idx, 1); return; } } i_panic("password_scheme_unregister(%s): Not registered", scheme->name); } void password_schemes_init(void) { unsigned int i; i_array_init(&password_schemes, N_ELEMENTS(builtin_schemes) + 4); for (i = 0; i < N_ELEMENTS(builtin_schemes); i++) password_scheme_register(&builtin_schemes[i]); password_scheme_register_crypt(); } void password_schemes_deinit(void) { array_free(&password_schemes); } dovecot-2.2.33.2/src/auth/auth-settings.c0000644000175000017500000003471413165463624015043 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "hash-method.h" #include "settings-parser.h" #include "master-service-private.h" #include "master-service-settings.h" #include "service-settings.h" #include "auth-settings.h" #include static bool auth_settings_check(void *_set, pool_t pool, const char **error_r); static bool auth_passdb_settings_check(void *_set, pool_t pool, const char **error_r); static bool auth_userdb_settings_check(void *_set, pool_t pool, const char **error_r); /* */ static struct file_listener_settings auth_unix_listeners_array[] = { { "login/login", 0666, "", "" }, { "token-login/tokenlogin", 0666, "", "" }, { "auth-login", 0600, "$default_internal_user", "" }, { "auth-client", 0600, "$default_internal_user", "" }, { "auth-userdb", 0666, "$default_internal_user", "" }, { "auth-master", 0600, "", "" } }; static struct file_listener_settings *auth_unix_listeners[] = { &auth_unix_listeners_array[0], &auth_unix_listeners_array[1], &auth_unix_listeners_array[2], &auth_unix_listeners_array[3], &auth_unix_listeners_array[4], &auth_unix_listeners_array[5] }; static buffer_t auth_unix_listeners_buf = { auth_unix_listeners, sizeof(auth_unix_listeners), { NULL, } }; /* */ struct service_settings auth_service_settings = { .name = "auth", .protocol = "", .type = "", .executable = "auth", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &auth_unix_listeners_buf, sizeof(auth_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; /* */ static struct file_listener_settings auth_worker_unix_listeners_array[] = { { "auth-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *auth_worker_unix_listeners[] = { &auth_worker_unix_listeners_array[0] }; static buffer_t auth_worker_unix_listeners_buf = { auth_worker_unix_listeners, sizeof(auth_worker_unix_listeners), { NULL, } }; /* */ struct service_settings auth_worker_service_settings = { .name = "auth-worker", .protocol = "", .type = "", .executable = "auth -w", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &auth_worker_unix_listeners_buf, sizeof(auth_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct auth_passdb_settings, name), NULL } static const struct setting_define auth_passdb_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, driver), DEF(SET_STR, args), DEF(SET_STR, default_fields), DEF(SET_STR, override_fields), DEF(SET_STR, mechanisms), DEF(SET_STR, username_filter), DEF(SET_ENUM, skip), DEF(SET_ENUM, result_success), DEF(SET_ENUM, result_failure), DEF(SET_ENUM, result_internalfail), DEF(SET_BOOL, deny), DEF(SET_BOOL, pass), DEF(SET_BOOL, master), DEF(SET_ENUM, auth_verbose), SETTING_DEFINE_LIST_END }; static const struct auth_passdb_settings auth_passdb_default_settings = { .name = "", .driver = "", .args = "", .default_fields = "", .override_fields = "", .mechanisms = "", .username_filter = "", .skip = "never:authenticated:unauthenticated", .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail", .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .deny = FALSE, .pass = FALSE, .master = FALSE, .auth_verbose = "default:yes:no" }; const struct setting_parser_info auth_passdb_setting_parser_info = { .defines = auth_passdb_setting_defines, .defaults = &auth_passdb_default_settings, .type_offset = offsetof(struct auth_passdb_settings, name), .struct_size = sizeof(struct auth_passdb_settings), .parent_offset = (size_t)-1, .parent = &auth_setting_parser_info, .check_func = auth_passdb_settings_check }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct auth_userdb_settings, name), NULL } static const struct setting_define auth_userdb_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, driver), DEF(SET_STR, args), DEF(SET_STR, default_fields), DEF(SET_STR, override_fields), DEF(SET_ENUM, skip), DEF(SET_ENUM, result_success), DEF(SET_ENUM, result_failure), DEF(SET_ENUM, result_internalfail), DEF(SET_ENUM, auth_verbose), SETTING_DEFINE_LIST_END }; static const struct auth_userdb_settings auth_userdb_default_settings = { /* NOTE: when adding fields, update also auth.c:userdb_dummy_set */ .name = "", .driver = "", .args = "", .default_fields = "", .override_fields = "", .skip = "never:found:notfound", .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail", .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .auth_verbose = "default:yes:no" }; const struct setting_parser_info auth_userdb_setting_parser_info = { .defines = auth_userdb_setting_defines, .defaults = &auth_userdb_default_settings, .type_offset = offsetof(struct auth_userdb_settings, name), .struct_size = sizeof(struct auth_userdb_settings), .parent_offset = (size_t)-1, .parent = &auth_setting_parser_info, .check_func = auth_userdb_settings_check }; /* we're kind of kludging here to avoid "auth_" prefix in the struct fields */ #undef DEF #undef DEF_NOPREFIX #undef DEFLIST #define DEF(type, name) \ { type, "auth_"#name, offsetof(struct auth_settings, name), NULL } #define DEF_NOPREFIX(type, name) \ { type, #name, offsetof(struct auth_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct auth_settings, field), defines } static const struct setting_define auth_setting_defines[] = { DEF(SET_STR, mechanisms), DEF(SET_STR, realms), DEF(SET_STR, default_realm), DEF(SET_SIZE, cache_size), DEF(SET_TIME, cache_ttl), DEF(SET_TIME, cache_negative_ttl), DEF(SET_STR, username_chars), DEF(SET_STR, username_translation), DEF(SET_STR, username_format), DEF(SET_STR, master_user_separator), DEF(SET_STR, anonymous_username), DEF(SET_STR, krb5_keytab), DEF(SET_STR, gssapi_hostname), DEF(SET_STR, winbind_helper_path), DEF(SET_STR, proxy_self), DEF(SET_TIME, failure_delay), DEF(SET_STR, policy_server_url), DEF(SET_STR, policy_server_api_header), DEF(SET_UINT, policy_server_timeout_msecs), DEF(SET_STR, policy_hash_mech), DEF(SET_STR, policy_hash_nonce), DEF(SET_STR, policy_request_attributes), DEF(SET_BOOL, policy_reject_on_fail), DEF(SET_UINT, policy_hash_truncate), DEF(SET_BOOL, stats), DEF(SET_BOOL, verbose), DEF(SET_BOOL, debug), DEF(SET_BOOL, debug_passwords), DEF(SET_STR, verbose_passwords), DEF(SET_BOOL, ssl_require_client_cert), DEF(SET_BOOL, ssl_username_from_cert), DEF(SET_BOOL, use_winbind), DEF(SET_UINT, worker_max_count), DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info), DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info), DEF_NOPREFIX(SET_STR, base_dir), DEF_NOPREFIX(SET_BOOL, verbose_proctitle), DEF_NOPREFIX(SET_UINT, first_valid_uid), DEF_NOPREFIX(SET_UINT, last_valid_uid), DEF_NOPREFIX(SET_STR, ssl_client_ca_dir), DEF_NOPREFIX(SET_STR, ssl_client_ca_file), SETTING_DEFINE_LIST_END }; static const struct auth_settings auth_default_settings = { .mechanisms = "plain", .realms = "", .default_realm = "", .cache_size = 0, .cache_ttl = 60*60, .cache_negative_ttl = 60*60, .username_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@", .username_translation = "", .username_format = "%Lu", .master_user_separator = "", .anonymous_username = "anonymous", .krb5_keytab = "", .gssapi_hostname = "", .winbind_helper_path = "/usr/bin/ntlm_auth", .proxy_self = "", .failure_delay = 2, .policy_server_url = "", .policy_server_api_header = "", .policy_server_timeout_msecs = 2000, .policy_hash_mech = "sha256", .policy_hash_nonce = "", .policy_request_attributes = "login=%{orig_username} pwhash=%{hashed_password} remote=%{real_rip} device_id=%{client_id} protocol=%s", .policy_reject_on_fail = FALSE, .policy_hash_truncate = 12, .stats = FALSE, .verbose = FALSE, .debug = FALSE, .debug_passwords = FALSE, .verbose_passwords = "no", .ssl_require_client_cert = FALSE, .ssl_username_from_cert = FALSE, .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .use_winbind = FALSE, .worker_max_count = 30, .passdbs = ARRAY_INIT, .userdbs = ARRAY_INIT, .base_dir = PKG_RUNDIR, .verbose_proctitle = FALSE, .first_valid_uid = 500, .last_valid_uid = 0, }; const struct setting_parser_info auth_setting_parser_info = { .module_name = "auth", .defines = auth_setting_defines, .defaults = &auth_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct auth_settings), .parent_offset = (size_t)-1, .check_func = auth_settings_check }; /* */ static bool auth_settings_set_self_ips(struct auth_settings *set, pool_t pool, const char **error_r) { const char *const *tmp; ARRAY(struct ip_addr) ips_array; struct ip_addr *ips; unsigned int ips_count; int ret; if (*set->proxy_self == '\0') { set->proxy_self_ips = p_new(pool, struct ip_addr, 1); return TRUE; } p_array_init(&ips_array, pool, 4); tmp = t_strsplit_spaces(set->proxy_self, " "); for (; *tmp != NULL; tmp++) { ret = net_gethostbyname(*tmp, &ips, &ips_count); if (ret != 0) { *error_r = t_strdup_printf("auth_proxy_self_ips: " "gethostbyname(%s) failed: %s", *tmp, net_gethosterror(ret)); } array_append(&ips_array, ips, ips_count); } array_append_zero(&ips_array); set->proxy_self_ips = array_idx(&ips_array, 0); return TRUE; } static bool auth_verify_verbose_password(struct auth_settings *set, const char **error_r) { const char *p, *value = set->verbose_passwords; unsigned int num; p = strchr(value, ':'); if (p != NULL) { if (str_to_uint(p+1, &num) < 0 || num == 0) { *error_r = t_strdup_printf("auth_verbose_passwords: " "Invalid truncation number: '%s'", p+1); return FALSE; } value = t_strdup_until(value, p); } if (strcmp(value, "no") == 0) return TRUE; else if (strcmp(value, "plain") == 0) return TRUE; else if (strcmp(value, "sha1") == 0) return TRUE; else if (strcmp(value, "yes") == 0) { /* just use it as alias for "plain" */ set->verbose_passwords = "plain"; return TRUE; } else { *error_r = "auth_verbose_passwords: Invalid value"; return FALSE; } } static bool auth_settings_check(void *_set, pool_t pool, const char **error_r) { struct auth_settings *set = _set; const char *p; if (set->debug_passwords) set->debug = TRUE; if (set->debug) set->verbose = TRUE; if (set->worker_max_count == 0) { *error_r = "auth_worker_max_count must be above zero"; return FALSE; } if (set->cache_size > 0 && set->cache_size < 1024) { /* probably a configuration error. older versions used megabyte numbers */ *error_r = t_strdup_printf("auth_cache_size value is too small " "(%"PRIuUOFF_T" bytes)", set->cache_size); return FALSE; } if (!auth_verify_verbose_password(set, error_r)) return FALSE; if (*set->username_chars == '\0') { /* all chars are allowed */ memset(set->username_chars_map, 1, sizeof(set->username_chars_map)); } else { for (p = set->username_chars; *p != '\0'; p++) set->username_chars_map[(int)(uint8_t)*p] = 1; } if (*set->username_translation != '\0') { p = set->username_translation; for (; *p != '\0' && p[1] != '\0'; p += 2) set->username_translation_map[(int)(uint8_t)*p] = p[1]; } set->realms_arr = (const char *const *)p_strsplit_spaces(pool, set->realms, " "); if (*set->policy_server_url != '\0') { if (*set->policy_hash_nonce == '\0') { *error_r = "auth_policy_hash_nonce must be set when policy server is used"; return FALSE; } const struct hash_method *digest = hash_method_lookup(set->policy_hash_mech); if (digest == NULL) { *error_r = "invalid auth_policy_hash_mech given"; return FALSE; } if (set->policy_hash_truncate > 0 && set->policy_hash_truncate >= digest->digest_size*8) { *error_r = t_strdup_printf("policy_hash_truncate is not smaller than digest size (%u >= %u)", set->policy_hash_truncate, digest->digest_size*8); return FALSE; } } if (!auth_settings_set_self_ips(set, pool, error_r)) return FALSE; return TRUE; } static bool auth_passdb_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct auth_passdb_settings *set = _set; if (set->driver == NULL || *set->driver == '\0') { *error_r = "passdb is missing driver"; return FALSE; } if (set->pass && strcmp(set->result_success, "return-ok") != 0) { *error_r = "Obsolete pass=yes setting mixed with non-default result_success"; return FALSE; } return TRUE; } static bool auth_userdb_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct auth_userdb_settings *set = _set; if (set->driver == NULL || *set->driver == '\0') { *error_r = "userdb is missing driver"; return FALSE; } return TRUE; } /* */ struct auth_settings *global_auth_settings; struct auth_settings * auth_settings_read(const char *service, pool_t pool, struct master_service_settings_output *output_r) { static const struct setting_parser_info *set_roots[] = { &auth_setting_parser_info, NULL }; struct master_service_settings_input input; struct setting_parser_context *set_parser; const char *error; void **sets; i_zero(&input); input.roots = set_roots; input.module = "auth"; input.service = service; if (master_service_settings_read(master_service, &input, output_r, &error) < 0) i_fatal("Error reading configuration: %s", error); pool_ref(pool); set_parser = settings_parser_dup(master_service->set_parser, pool); if (!settings_parser_check(set_parser, pool, &error)) i_unreached(); sets = master_service_settings_parser_get_others(master_service, set_parser); settings_parser_deinit(&set_parser); return sets[0]; } dovecot-2.2.33.2/src/auth/auth-request-stats.c0000644000175000017500000000375713123174404016017 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "str.h" #include "strescape.h" #include "buffer.h" #include "base64.h" #include "stats.h" #include "stats-connection.h" #include "auth-stats.h" #include "auth-request.h" #include "auth-request-stats.h" #define USER_STATS_SOCKET_NAME "stats-user" static struct stats_connection *auth_stats_conn = NULL; static struct stats_item *auth_stats_item; struct auth_stats *auth_request_stats_get(struct auth_request *request) { if (request->stats == NULL) request->stats = stats_alloc(request->pool); return stats_fill_ptr(request->stats, auth_stats_item); } void auth_request_stats_add_tempfail(struct auth_request *request) { struct auth_stats *stats = auth_request_stats_get(request); stats->auth_db_tempfail_count++; } void auth_request_stats_send(struct auth_request *request) { string_t *str; buffer_t *buf; /* we'll send stats only when the request is finished. this reduces memory usage and is a bit simpler. auth requests are typically pretty short lived anyway. */ i_assert(!request->stats_sent); request->stats_sent = TRUE; if (request->stats == NULL) { /* nothing happened in this request - don't send it */ return; } if (!request->set->stats) return; buf = buffer_create_dynamic(pool_datastack_create(), 128); stats_export(buf, request->stats); str = t_str_new(256); str_append(str, "ADD-USER\t"); if (request->user != NULL) str_append_tabescaped(str, request->user); str_append_c(str, '\t'); str_append_tabescaped(str, request->service); str_append_c(str, '\t'); base64_encode(buf->data, buf->used, str); str_append_c(str, '\n'); stats_connection_send(auth_stats_conn, str); } void auth_request_stats_init(void) { auth_stats_conn = stats_connection_create(USER_STATS_SOCKET_NAME); auth_stats_item = stats_register(&auth_stats_vfuncs); } void auth_request_stats_deinit(void) { stats_connection_unref(&auth_stats_conn); stats_unregister(&auth_stats_item); } dovecot-2.2.33.2/src/auth/passdb-pam.c0000644000175000017500000002465413165463624014275 00000000000000/* Based on auth_pam.c from popa3d by Solar Designer . You're allowed to do whatever you like with this software (including re-distribution in source and/or binary form, with or without modification), provided that credit is given where it is due and any modified versions are marked as such. There's absolutely no warranty. */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_PAM #include "lib-signals.h" #include "str.h" #include "net.h" #include "safe-memset.h" #include "auth-cache.h" #include #ifdef HAVE_SECURITY_PAM_APPL_H # include #elif defined(HAVE_PAM_PAM_APPL_H) # include #endif #if defined(sun) || defined(__sun__) || defined(_HPUX_SOURCE) # define pam_const #else # define pam_const const #endif typedef pam_const void *pam_item_t; #define PASSDB_PAM_DEFAULT_MAX_REQUESTS 100 struct pam_passdb_module { struct passdb_module module; const char *service_name, *pam_cache_key; unsigned int requests_left; unsigned int pam_setcred:1; unsigned int pam_session:1; unsigned int failure_show_msg:1; }; struct pam_conv_context { struct auth_request *request; const char *pass; const char *failure_msg; }; static int pam_userpass_conv(int num_msg, pam_const struct pam_message **msg, struct pam_response **resp_r, void *appdata_ptr) { /* @UNSAFE */ struct pam_conv_context *ctx = appdata_ptr; struct passdb_module *_passdb = ctx->request->passdb->passdb; struct pam_passdb_module *passdb = (struct pam_passdb_module *)_passdb; struct pam_response *resp; char *string; int i; *resp_r = NULL; resp = calloc(num_msg, sizeof(struct pam_response)); if (resp == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); for (i = 0; i < num_msg; i++) { auth_request_log_debug(ctx->request, AUTH_SUBSYS_DB, "#%d/%d style=%d msg=%s", i+1, num_msg, msg[i]->msg_style, msg[i]->msg != NULL ? msg[i]->msg : ""); switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* Assume we're asking for user. We might not ever get here because PAM already knows the user. */ string = strdup(ctx->request->user); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; case PAM_PROMPT_ECHO_OFF: /* Assume we're asking for password */ if (passdb->failure_show_msg) ctx->failure_msg = t_strdup(msg[i]->msg); string = strdup(ctx->pass); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: string = NULL; break; default: while (--i >= 0) { if (resp[i].resp != NULL) { safe_memset(resp[i].resp, 0, strlen(resp[i].resp)); free(resp[i].resp); } } free(resp); return PAM_CONV_ERR; } resp[i].resp_retcode = PAM_SUCCESS; resp[i].resp = string; } *resp_r = resp; return PAM_SUCCESS; } static const char * pam_get_missing_service_file_path(const char *service ATTR_UNUSED) { #ifdef SUNPAM /* Uses /etc/pam.conf - we're not going to parse that */ return NULL; #else static bool service_checked = FALSE; const char *path; struct stat st; if (service_checked) { /* check and complain only once */ return NULL; } service_checked = TRUE; path = t_strdup_printf("/etc/pam.d/%s", service); if (stat(path, &st) < 0 && errno == ENOENT) { /* looks like it's missing. but before assuming that the system even uses /etc/pam.d, make sure that it exists. */ if (stat("/etc/pam.d", &st) == 0) return path; } /* exists or is unknown */ return NULL; #endif } static int try_pam_auth(struct auth_request *request, pam_handle_t *pamh, const char *service) { struct passdb_module *_module = request->passdb->passdb; struct pam_passdb_module *module = (struct pam_passdb_module *)_module; const char *path, *str; pam_item_t item; int status; if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { path = pam_get_missing_service_file_path(service); switch (status) { case PAM_USER_UNKNOWN: str = "unknown user"; break; default: str = t_strconcat("pam_authenticate() failed: ", pam_strerror(pamh, status), NULL); break; } if (path != NULL) { /* log this as error, since it probably is */ str = t_strdup_printf("%s (%s missing?)", str, path); auth_request_log_error(request, AUTH_SUBSYS_DB, "%s", str); } else if (status == PAM_AUTH_ERR) { str = t_strconcat(str, " (password mismatch?)", NULL); if (request->set->debug_passwords) { str = t_strconcat(str, " (given password: ", request->mech_password, ")", NULL); } auth_request_log_info(request, AUTH_SUBSYS_DB, "%s", str); } else { if (status == PAM_USER_UNKNOWN) auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); else { auth_request_log_info(request, AUTH_SUBSYS_DB, "%s", str); } } return status; } #ifdef HAVE_PAM_SETCRED if (module->pam_setcred) { if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_setcred() failed: %s", pam_strerror(pamh, status)); return status; } } #endif if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_acct_mgmt() failed: %s", pam_strerror(pamh, status)); return status; } if (module->pam_session) { if ((status = pam_open_session(pamh, 0)) != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_open_session() failed: %s", pam_strerror(pamh, status)); return status; } if ((status = pam_close_session(pamh, 0)) != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_close_session() failed: %s", pam_strerror(pamh, status)); return status; } } status = pam_get_item(pamh, PAM_USER, &item); if (status != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_get_item(PAM_USER) failed: %s", pam_strerror(pamh, status)); return status; } auth_request_set_field(request, "user", item, NULL); return PAM_SUCCESS; } static void set_pam_items(struct auth_request *request, pam_handle_t *pamh) { const char *host; /* These shouldn't fail, and we don't really care if they do. */ host = net_ip2addr(&request->remote_ip); if (host[0] != '\0') (void)pam_set_item(pamh, PAM_RHOST, host); (void)pam_set_item(pamh, PAM_RUSER, request->user); /* TTY is needed by eg. pam_access module */ (void)pam_set_item(pamh, PAM_TTY, "dovecot"); } static enum passdb_result pam_verify_plain_call(struct auth_request *request, const char *service, const char *password) { pam_handle_t *pamh; struct pam_conv_context ctx; struct pam_conv conv; enum passdb_result result; int status, status2; conv.conv = pam_userpass_conv; conv.appdata_ptr = &ctx; i_zero(&ctx); ctx.request = request; ctx.pass = password; status = pam_start(service, request->user, &conv, &pamh); if (status != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_start() failed: %s", pam_strerror(pamh, status)); return PASSDB_RESULT_INTERNAL_FAILURE; } set_pam_items(request, pamh); status = try_pam_auth(request, pamh, service); if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pam_end() failed: %s", pam_strerror(pamh, status2)); return PASSDB_RESULT_INTERNAL_FAILURE; } switch (status) { case PAM_SUCCESS: result = PASSDB_RESULT_OK; break; case PAM_USER_UNKNOWN: result = PASSDB_RESULT_USER_UNKNOWN; break; case PAM_NEW_AUTHTOK_REQD: case PAM_ACCT_EXPIRED: result = PASSDB_RESULT_PASS_EXPIRED; break; default: result = PASSDB_RESULT_PASSWORD_MISMATCH; break; } if (result != PASSDB_RESULT_OK && ctx.failure_msg != NULL) { auth_request_set_field(request, "reason", ctx.failure_msg, NULL); } return result; } static void pam_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct pam_passdb_module *module = (struct pam_passdb_module *)_module; enum passdb_result result; const char *service; if (module->requests_left > 0) { if (--module->requests_left == 0) worker_restart_request = TRUE; } service = t_auth_request_var_expand(module->service_name, request, NULL); auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup service=%s", service); result = pam_verify_plain_call(request, service, password); callback(result, request); } static struct passdb_module * pam_preinit(pool_t pool, const char *args) { struct pam_passdb_module *module; const char *const *t_args; int i; module = p_new(pool, struct pam_passdb_module, 1); module->service_name = "dovecot"; /* we're caching the password by using directly the plaintext password given by the auth mechanism */ module->module.default_pass_scheme = "PLAIN"; module->module.blocking = TRUE; module->requests_left = PASSDB_PAM_DEFAULT_MAX_REQUESTS; t_args = t_strsplit_spaces(args, " "); for(i = 0; t_args[i] != NULL; i++) { /* -session for backwards compatibility */ if (strcmp(t_args[i], "-session") == 0 || strcmp(t_args[i], "session=yes") == 0) module->pam_session = TRUE; else if (strcmp(t_args[i], "setcred=yes") == 0) module->pam_setcred = TRUE; else if (strncmp(t_args[i], "cache_key=", 10) == 0) { module->module.default_cache_key = auth_cache_parse_key(pool, t_args[i] + 10); } else if (strcmp(t_args[i], "blocking=yes") == 0) { /* ignore, for backwards compatibility */ } else if (strcmp(t_args[i], "failure_show_msg=yes") == 0) { module->failure_show_msg = TRUE; } else if (strcmp(t_args[i], "*") == 0) { /* for backwards compatibility */ module->service_name = "%Ls"; } else if (strncmp(t_args[i], "max_requests=", 13) == 0) { if (str_to_uint(t_args[i] + 13, &module->requests_left) < 0) { i_error("pam: Invalid requests_left value: %s", t_args[i] + 13); } } else if (t_args[i+1] == NULL) { module->service_name = p_strdup(pool, t_args[i]); } else { i_fatal("pam: Unknown setting: %s", t_args[i]); } } return &module->module; } struct passdb_module_interface passdb_pam = { "pam", pam_preinit, NULL, NULL, pam_verify_plain, NULL, NULL }; #else struct passdb_module_interface passdb_pam = { .name = "pam" }; #endif dovecot-2.2.33.2/src/auth/db-sql.c0000644000175000017500000001144413123174404013406 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #if defined(PASSDB_SQL) || defined(USERDB_SQL) #include "settings.h" #include "auth-request.h" #include "auth-worker-client.h" #include "db-sql.h" #include #define DEF_STR(name) DEF_STRUCT_STR(name, sql_settings) #define DEF_INT(name) DEF_STRUCT_INT(name, sql_settings) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, sql_settings) static struct setting_def setting_defs[] = { DEF_STR(driver), DEF_STR(connect), DEF_STR(password_query), DEF_STR(user_query), DEF_STR(update_query), DEF_STR(iterate_query), DEF_STR(default_pass_scheme), DEF_BOOL(userdb_warning_disable), { 0, NULL, 0 } }; static struct sql_settings default_sql_settings = { .driver = NULL, .connect = NULL, .password_query = "SELECT username, domain, password FROM users WHERE username = '%n' AND domain = '%d'", .user_query = "SELECT home, uid, gid FROM users WHERE username = '%n' AND domain = '%d'", .update_query = "UPDATE users SET password = '%w' WHERE username = '%n' AND domain = '%d'", .iterate_query = "SELECT username, domain FROM users", .default_pass_scheme = "MD5", .userdb_warning_disable = FALSE }; static struct sql_connection *connections = NULL; static struct sql_connection *sql_conn_find(const char *config_path) { struct sql_connection *conn; for (conn = connections; conn != NULL; conn = conn->next) { if (strcmp(conn->config_path, config_path) == 0) return conn; } return NULL; } static const char *parse_setting(const char *key, const char *value, struct sql_connection *conn) { return parse_setting_from_defs(conn->pool, setting_defs, &conn->set, key, value); } struct sql_connection *db_sql_init(const char *config_path, bool userdb) { struct sql_connection *conn; const char *error; pool_t pool; conn = sql_conn_find(config_path); if (conn != NULL) { if (userdb) conn->userdb_used = TRUE; conn->refcount++; return conn; } if (*config_path == '\0') i_fatal("sql: Configuration file path not given"); pool = pool_alloconly_create("sql_connection", 1024); conn = p_new(pool, struct sql_connection, 1); conn->pool = pool; conn->userdb_used = userdb; conn->refcount = 1; conn->config_path = p_strdup(pool, config_path); conn->set = default_sql_settings; if (!settings_read_nosection(config_path, parse_setting, conn, &error)) i_fatal("sql %s: %s", config_path, error); if (conn->set.password_query == default_sql_settings.password_query) conn->default_password_query = TRUE; if (conn->set.user_query == default_sql_settings.user_query) conn->default_user_query = TRUE; if (conn->set.update_query == default_sql_settings.update_query) conn->default_update_query = TRUE; if (conn->set.iterate_query == default_sql_settings.iterate_query) conn->default_iterate_query = TRUE; if (conn->set.driver == NULL) { i_fatal("sql: driver not set in configuration file %s", config_path); } if (conn->set.connect == NULL) { i_fatal("sql: connect string not set in configuration file %s", config_path); } conn->db = sql_init(conn->set.driver, conn->set.connect); conn->next = connections; connections = conn; return conn; } void db_sql_unref(struct sql_connection **_conn) { struct sql_connection *conn = *_conn; /* abort all pending auth requests before setting conn to NULL, so that callbacks can still access it */ sql_disconnect(conn->db); *_conn = NULL; if (--conn->refcount > 0) return; sql_deinit(&conn->db); pool_unref(&conn->pool); } void db_sql_connect(struct sql_connection *conn) { if (sql_connect(conn->db) < 0 && worker) { /* auth worker's sql connection failed. we can't do anything useful until the connection works. there's no point in having tons of worker processes all logging failures, so tell the auth master to stop creating new workers (and maybe close old ones). this handling is especially useful if we reach the max. number of connections for sql server. */ auth_worker_client_send_error(); } } void db_sql_success(struct sql_connection *conn ATTR_UNUSED) { if (worker) auth_worker_client_send_success(); } void db_sql_check_userdb_warning(struct sql_connection *conn) { if (worker || conn->userdb_used || conn->set.userdb_warning_disable) return; if (strcmp(conn->set.user_query, default_sql_settings.user_query) != 0) { i_warning("sql: Ignoring changed user_query in %s, " "because userdb sql not used. " "(If this is intentional, set userdb_warning_disable=yes)", conn->config_path); } else if (strcmp(conn->set.iterate_query, default_sql_settings.iterate_query) != 0) { i_warning("sql: Ignoring changed iterate_query in %s, " "because userdb sql not used. " "(If this is intentional, set userdb_warning_disable=yes)", conn->config_path); } } #endif dovecot-2.2.33.2/src/auth/Makefile.in0000644000175000017500000017125113172375572014145 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = auth$(EXEEXT) checkpassword-reply$(EXEEXT) noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/auth ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libpassword_a_AR = $(AR) $(ARFLAGS) libpassword_a_LIBADD = am_libpassword_a_OBJECTS = mycrypt.$(OBJEXT) password-scheme.$(OBJEXT) \ password-scheme-crypt.$(OBJEXT) \ password-scheme-md5crypt.$(OBJEXT) \ password-scheme-scram.$(OBJEXT) password-scheme-otp.$(OBJEXT) \ password-scheme-rpa.$(OBJEXT) password-scheme-pbkdf2.$(OBJEXT) libpassword_a_OBJECTS = $(am_libpassword_a_OBJECTS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(auth_moduledir)" \ "$(DESTDIR)$(stats_moduledir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(auth_module_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(stats_module_LTLIBRARIES) libauth_la_LIBADD = am__objects_1 = db-ldap.lo passdb-ldap.lo userdb-ldap.lo am_libauth_la_OBJECTS = auth.lo auth-cache.lo \ auth-client-connection.lo auth-master-connection.lo \ auth-policy.lo auth-postfix-connection.lo \ mech-otp-skey-common.lo mech-plain-common.lo auth-penalty.lo \ auth-request.lo auth-request-handler.lo auth-request-stats.lo \ auth-request-var-expand.lo auth-settings.lo auth-fields.lo \ auth-token.lo auth-worker-client.lo auth-worker-server.lo \ db-checkpassword.lo db-dict.lo db-dict-cache-key.lo \ db-oauth2.lo db-sql.lo db-passwd-file.lo mech.lo \ mech-anonymous.lo mech-plain.lo mech-login.lo mech-cram-md5.lo \ mech-digest-md5.lo mech-external.lo mech-gssapi.lo \ mech-ntlm.lo mech-otp.lo mech-scram-sha1.lo mech-skey.lo \ mech-rpa.lo mech-apop.lo mech-winbind.lo mech-dovecot-token.lo \ mech-oauth2.lo passdb.lo passdb-blocking.lo passdb-bsdauth.lo \ passdb-cache.lo passdb-checkpassword.lo passdb-dict.lo \ passdb-oauth2.lo passdb-passwd.lo passdb-passwd-file.lo \ passdb-pam.lo passdb-shadow.lo passdb-sia.lo \ passdb-vpopmail.lo passdb-sql.lo passdb-static.lo \ passdb-template.lo userdb.lo userdb-blocking.lo \ userdb-checkpassword.lo userdb-dict.lo userdb-nss.lo \ userdb-passwd.lo userdb-passwd-file.lo userdb-prefetch.lo \ userdb-static.lo userdb-vpopmail.lo userdb-sql.lo \ userdb-template.lo $(am__objects_1) libauth_la_OBJECTS = $(am_libauth_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__DEPENDENCIES_1 = libauthdb_imap_la_DEPENDENCIES = ../lib-imap-client/libimap_client.la \ $(am__DEPENDENCIES_1) am_libauthdb_imap_la_OBJECTS = libauthdb_imap_la-passdb-imap.lo libauthdb_imap_la_OBJECTS = $(am_libauthdb_imap_la_OBJECTS) libauthdb_imap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libauthdb_imap_la_LDFLAGS) $(LDFLAGS) \ -o $@ @LDAP_PLUGIN_TRUE@libauthdb_ldap_la_DEPENDENCIES = \ @LDAP_PLUGIN_TRUE@ $(am__DEPENDENCIES_1) am__libauthdb_ldap_la_SOURCES_DIST = db-ldap.c passdb-ldap.c \ userdb-ldap.c am__objects_2 = libauthdb_ldap_la-db-ldap.lo \ libauthdb_ldap_la-passdb-ldap.lo \ libauthdb_ldap_la-userdb-ldap.lo @LDAP_PLUGIN_TRUE@am_libauthdb_ldap_la_OBJECTS = $(am__objects_2) libauthdb_ldap_la_OBJECTS = $(am_libauthdb_ldap_la_OBJECTS) libauthdb_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libauthdb_ldap_la_LDFLAGS) $(LDFLAGS) \ -o $@ @LDAP_PLUGIN_TRUE@am_libauthdb_ldap_la_rpath = -rpath \ @LDAP_PLUGIN_TRUE@ $(auth_moduledir) @GSSAPI_PLUGIN_TRUE@libmech_gssapi_la_DEPENDENCIES = \ @GSSAPI_PLUGIN_TRUE@ $(am__DEPENDENCIES_1) am__libmech_gssapi_la_SOURCES_DIST = mech-gssapi.c @GSSAPI_PLUGIN_TRUE@am_libmech_gssapi_la_OBJECTS = \ @GSSAPI_PLUGIN_TRUE@ libmech_gssapi_la-mech-gssapi.lo libmech_gssapi_la_OBJECTS = $(am_libmech_gssapi_la_OBJECTS) libmech_gssapi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libmech_gssapi_la_LDFLAGS) $(LDFLAGS) \ -o $@ @GSSAPI_PLUGIN_TRUE@am_libmech_gssapi_la_rpath = -rpath \ @GSSAPI_PLUGIN_TRUE@ $(auth_moduledir) am_libstats_auth_la_OBJECTS = auth-stats.lo libstats_auth_la_OBJECTS = $(am_libstats_auth_la_OBJECTS) libstats_auth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libstats_auth_la_LDFLAGS) $(LDFLAGS) \ -o $@ am__EXEEXT_1 = test-auth-cache$(EXEEXT) \ test-auth-request-var-expand$(EXEEXT) \ test-username-filter$(EXEEXT) test-db-dict$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) am_auth_OBJECTS = auth-main.$(OBJEXT) auth_OBJECTS = $(am_auth_OBJECTS) am__DEPENDENCIES_2 = libauth.la libstats_auth.la libpassword.a \ ../lib-ntlm/libntlm.a ../lib-otp/libotp.a \ $(am__DEPENDENCIES_1) auth_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(auth_LDFLAGS) $(LDFLAGS) -o $@ checkpassword_reply_SOURCES = checkpassword-reply.c checkpassword_reply_OBJECTS = checkpassword-reply.$(OBJEXT) am_test_auth_cache_OBJECTS = test_auth_cache-auth-cache.$(OBJEXT) \ test_auth_cache-test-auth-cache.$(OBJEXT) test_auth_cache_OBJECTS = $(am_test_auth_cache_OBJECTS) am_test_auth_request_var_expand_OBJECTS = \ test-auth-request-var-expand.$(OBJEXT) test_auth_request_var_expand_OBJECTS = \ $(am_test_auth_request_var_expand_OBJECTS) am_test_db_dict_OBJECTS = test-db-dict.$(OBJEXT) test_db_dict_OBJECTS = $(am_test_db_dict_OBJECTS) am_test_username_filter_OBJECTS = test-username-filter.$(OBJEXT) test_username_filter_OBJECTS = $(am_test_username_filter_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libpassword_a_SOURCES) $(libauth_la_SOURCES) \ $(libauthdb_imap_la_SOURCES) $(libauthdb_ldap_la_SOURCES) \ $(libmech_gssapi_la_SOURCES) $(libstats_auth_la_SOURCES) \ $(auth_SOURCES) checkpassword-reply.c \ $(test_auth_cache_SOURCES) \ $(test_auth_request_var_expand_SOURCES) \ $(test_db_dict_SOURCES) $(test_username_filter_SOURCES) DIST_SOURCES = $(libpassword_a_SOURCES) $(libauth_la_SOURCES) \ $(libauthdb_imap_la_SOURCES) \ $(am__libauthdb_ldap_la_SOURCES_DIST) \ $(am__libmech_gssapi_la_SOURCES_DIST) \ $(libstats_auth_la_SOURCES) $(auth_SOURCES) \ checkpassword-reply.c $(test_auth_cache_SOURCES) \ $(test_auth_request_var_expand_SOURCES) \ $(test_db_dict_SOURCES) $(test_username_filter_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ # automake seems to force making this unconditional.. NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libpassword.a noinst_LTLIBRARIES = libauth.la auth_moduledir = $(moduledir)/auth @GSSAPI_PLUGIN_TRUE@GSSAPI_LIB = libmech_gssapi.la @LDAP_PLUGIN_TRUE@LDAP_LIB = libauthdb_ldap.la auth_module_LTLIBRARIES = \ $(GSSAPI_LIB) \ $(LDAP_LIB) \ libauthdb_imap.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-ntlm \ -I$(top_srcdir)/src/lib-otp \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-oauth2 \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ $(AUTH_CFLAGS) auth_LDFLAGS = -export-dynamic libpassword_a_SOURCES = \ mycrypt.c \ password-scheme.c \ password-scheme-crypt.c \ password-scheme-md5crypt.c \ password-scheme-scram.c \ password-scheme-otp.c \ password-scheme-rpa.c \ password-scheme-pbkdf2.c auth_libs = \ libauth.la \ libstats_auth.la \ libpassword.a \ ../lib-ntlm/libntlm.a \ ../lib-otp/libotp.a \ $(LIBDOVECOT_SQL) auth_LDADD = $(auth_libs) $(LIBDOVECOT) $(AUTH_LIBS) auth_CPPFLAGS = $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-ssl-iostream auth_DEPENDENCIES = $(auth_libs) $(LIBDOVECOT_DEPS) auth_SOURCES = main.c ldap_sources = db-ldap.c passdb-ldap.c userdb-ldap.c libauth_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libauth_la_SOURCES = \ auth.c \ auth-cache.c \ auth-client-connection.c \ auth-master-connection.c \ auth-policy.c \ auth-postfix-connection.c \ mech-otp-skey-common.c \ mech-plain-common.c \ auth-penalty.c \ auth-request.c \ auth-request-handler.c \ auth-request-stats.c \ auth-request-var-expand.c \ auth-settings.c \ auth-fields.c \ auth-token.c \ auth-worker-client.c \ auth-worker-server.c \ db-checkpassword.c \ db-dict.c \ db-dict-cache-key.c \ db-oauth2.c \ db-sql.c \ db-passwd-file.c \ mech.c \ mech-anonymous.c \ mech-plain.c \ mech-login.c \ mech-cram-md5.c \ mech-digest-md5.c \ mech-external.c \ mech-gssapi.c \ mech-ntlm.c \ mech-otp.c \ mech-scram-sha1.c \ mech-skey.c \ mech-rpa.c \ mech-apop.c \ mech-winbind.c \ mech-dovecot-token.c \ mech-oauth2.c \ passdb.c \ passdb-blocking.c \ passdb-bsdauth.c \ passdb-cache.c \ passdb-checkpassword.c \ passdb-dict.c \ passdb-oauth2.c \ passdb-passwd.c \ passdb-passwd-file.c \ passdb-pam.c \ passdb-shadow.c \ passdb-sia.c \ passdb-vpopmail.c \ passdb-sql.c \ passdb-static.c \ passdb-template.c \ userdb.c \ userdb-blocking.c \ userdb-checkpassword.c \ userdb-dict.c \ userdb-nss.c \ userdb-passwd.c \ userdb-passwd-file.c \ userdb-prefetch.c \ userdb-static.c \ userdb-vpopmail.c \ userdb-sql.c \ userdb-template.c \ $(ldap_sources) headers = \ auth.h \ auth-cache.h \ auth-client-connection.h \ auth-common.h \ auth-master-connection.h \ auth-postfix-connection.h \ mech-otp-skey-common.h \ mech-plain-common.h \ auth-penalty.h \ auth-policy.h \ auth-request.h \ auth-request-handler.h \ auth-request-stats.h \ auth-request-var-expand.h \ auth-settings.h \ auth-stats.h \ auth-fields.h \ auth-token.h \ auth-worker-client.h \ auth-worker-server.h \ db-dict.h \ db-ldap.h \ db-sql.h \ db-passwd-file.h \ db-checkpassword.h \ db-oauth2.h \ mech.h \ mycrypt.h \ passdb.h \ passdb-blocking.h \ passdb-cache.h \ passdb-template.h \ password-scheme.h \ userdb.h \ userdb-blocking.h \ userdb-template.h \ userdb-vpopmail.h @GSSAPI_PLUGIN_TRUE@libmech_gssapi_la_LDFLAGS = -module -avoid-version @GSSAPI_PLUGIN_TRUE@libmech_gssapi_la_LIBADD = $(KRB5_LIBS) @GSSAPI_PLUGIN_TRUE@libmech_gssapi_la_CPPFLAGS = $(AM_CPPFLAGS) $(KRB5_CFLAGS) -DPLUGIN_BUILD @GSSAPI_PLUGIN_TRUE@libmech_gssapi_la_SOURCES = mech-gssapi.c @LDAP_PLUGIN_TRUE@libauthdb_ldap_la_LDFLAGS = -module -avoid-version @LDAP_PLUGIN_TRUE@libauthdb_ldap_la_LIBADD = $(LDAP_LIBS) @LDAP_PLUGIN_TRUE@libauthdb_ldap_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD @LDAP_PLUGIN_TRUE@libauthdb_ldap_la_SOURCES = $(ldap_sources) libauthdb_imap_la_LDFLAGS = -module -avoid-version libauthdb_imap_la_LIBADD = \ ../lib-imap-client/libimap_client.la \ $(LIBDOVECOT) libauthdb_imap_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client libauthdb_imap_la_SOURCES = passdb-imap.c pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) checkpassword_reply_LDADD = $(LIBDOVECOT) checkpassword_reply_DEPENDENCIES = $(LIBDOVECOT_DEPS) checkpassword_reply_sources = \ checkpassword-reply.c stats_moduledir = $(moduledir)/stats stats_module_LTLIBRARIES = libstats_auth.la libstats_auth_la_LDFLAGS = -module -avoid-version libstats_auth_la_LIBADD = $(LIBDOVECOT) libstats_auth_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libstats_auth_la_SOURCES = auth-stats.c test_programs = \ test-auth-cache \ test-auth-request-var-expand \ test-username-filter \ test-db-dict test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_auth_cache_SOURCES = auth-cache.c test-auth-cache.c test_auth_cache_LDADD = $(test_libs) test_auth_cache_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) # this is needed to force auth-cache.c recompilation test_auth_cache_CPPFLAGS = $(AM_CPPFLAGS) test_auth_request_var_expand_SOURCES = test-auth-request-var-expand.c test_auth_request_var_expand_LDADD = $(test_libs) libauth.la test_auth_request_var_expand_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) test_username_filter_SOURCES = test-username-filter.c test_username_filter_LDADD = $(auth_libs) $(AUTH_LIBS) $(LIBDOVECOT) test_username_filter_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) $(LIBDOVECOT_DEPS) test_db_dict_SOURCES = test-db-dict.c test_db_dict_LDADD = $(test_libs) libauth.la test_db_dict_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/auth/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/auth/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libpassword.a: $(libpassword_a_OBJECTS) $(libpassword_a_DEPENDENCIES) $(EXTRA_libpassword_a_DEPENDENCIES) $(AM_V_at)-rm -f libpassword.a $(AM_V_AR)$(libpassword_a_AR) libpassword.a $(libpassword_a_OBJECTS) $(libpassword_a_LIBADD) $(AM_V_at)$(RANLIB) libpassword.a install-auth_moduleLTLIBRARIES: $(auth_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(auth_module_LTLIBRARIES)'; test -n "$(auth_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(auth_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(auth_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(auth_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(auth_moduledir)"; \ } uninstall-auth_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(auth_module_LTLIBRARIES)'; test -n "$(auth_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(auth_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(auth_moduledir)/$$f"; \ done clean-auth_moduleLTLIBRARIES: -test -z "$(auth_module_LTLIBRARIES)" || rm -f $(auth_module_LTLIBRARIES) @list='$(auth_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-stats_moduleLTLIBRARIES: $(stats_module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(stats_module_LTLIBRARIES)'; test -n "$(stats_moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(stats_moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(stats_moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(stats_moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(stats_moduledir)"; \ } uninstall-stats_moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(stats_module_LTLIBRARIES)'; test -n "$(stats_moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(stats_moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(stats_moduledir)/$$f"; \ done clean-stats_moduleLTLIBRARIES: -test -z "$(stats_module_LTLIBRARIES)" || rm -f $(stats_module_LTLIBRARIES) @list='$(stats_module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libauth.la: $(libauth_la_OBJECTS) $(libauth_la_DEPENDENCIES) $(EXTRA_libauth_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libauth_la_OBJECTS) $(libauth_la_LIBADD) $(LIBS) libauthdb_imap.la: $(libauthdb_imap_la_OBJECTS) $(libauthdb_imap_la_DEPENDENCIES) $(EXTRA_libauthdb_imap_la_DEPENDENCIES) $(AM_V_CCLD)$(libauthdb_imap_la_LINK) -rpath $(auth_moduledir) $(libauthdb_imap_la_OBJECTS) $(libauthdb_imap_la_LIBADD) $(LIBS) libauthdb_ldap.la: $(libauthdb_ldap_la_OBJECTS) $(libauthdb_ldap_la_DEPENDENCIES) $(EXTRA_libauthdb_ldap_la_DEPENDENCIES) $(AM_V_CCLD)$(libauthdb_ldap_la_LINK) $(am_libauthdb_ldap_la_rpath) $(libauthdb_ldap_la_OBJECTS) $(libauthdb_ldap_la_LIBADD) $(LIBS) libmech_gssapi.la: $(libmech_gssapi_la_OBJECTS) $(libmech_gssapi_la_DEPENDENCIES) $(EXTRA_libmech_gssapi_la_DEPENDENCIES) $(AM_V_CCLD)$(libmech_gssapi_la_LINK) $(am_libmech_gssapi_la_rpath) $(libmech_gssapi_la_OBJECTS) $(libmech_gssapi_la_LIBADD) $(LIBS) libstats_auth.la: $(libstats_auth_la_OBJECTS) $(libstats_auth_la_DEPENDENCIES) $(EXTRA_libstats_auth_la_DEPENDENCIES) $(AM_V_CCLD)$(libstats_auth_la_LINK) -rpath $(stats_moduledir) $(libstats_auth_la_OBJECTS) $(libstats_auth_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list auth$(EXEEXT): $(auth_OBJECTS) $(auth_DEPENDENCIES) $(EXTRA_auth_DEPENDENCIES) @rm -f auth$(EXEEXT) $(AM_V_CCLD)$(auth_LINK) $(auth_OBJECTS) $(auth_LDADD) $(LIBS) checkpassword-reply$(EXEEXT): $(checkpassword_reply_OBJECTS) $(checkpassword_reply_DEPENDENCIES) $(EXTRA_checkpassword_reply_DEPENDENCIES) @rm -f checkpassword-reply$(EXEEXT) $(AM_V_CCLD)$(LINK) $(checkpassword_reply_OBJECTS) $(checkpassword_reply_LDADD) $(LIBS) test-auth-cache$(EXEEXT): $(test_auth_cache_OBJECTS) $(test_auth_cache_DEPENDENCIES) $(EXTRA_test_auth_cache_DEPENDENCIES) @rm -f test-auth-cache$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_auth_cache_OBJECTS) $(test_auth_cache_LDADD) $(LIBS) test-auth-request-var-expand$(EXEEXT): $(test_auth_request_var_expand_OBJECTS) $(test_auth_request_var_expand_DEPENDENCIES) $(EXTRA_test_auth_request_var_expand_DEPENDENCIES) @rm -f test-auth-request-var-expand$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_auth_request_var_expand_OBJECTS) $(test_auth_request_var_expand_LDADD) $(LIBS) test-db-dict$(EXEEXT): $(test_db_dict_OBJECTS) $(test_db_dict_DEPENDENCIES) $(EXTRA_test_db_dict_DEPENDENCIES) @rm -f test-db-dict$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_db_dict_OBJECTS) $(test_db_dict_LDADD) $(LIBS) test-username-filter$(EXEEXT): $(test_username_filter_OBJECTS) $(test_username_filter_DEPENDENCIES) $(EXTRA_test_username_filter_DEPENDENCIES) @rm -f test-username-filter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_username_filter_OBJECTS) $(test_username_filter_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-client-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-fields.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-master-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-penalty.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-policy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-postfix-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-request-handler.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-request-stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-request-var-expand.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-request.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-token.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-worker-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-worker-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checkpassword-reply.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-checkpassword.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-dict-cache-key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-oauth2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-passwd-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-sql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libauthdb_imap_la-passdb-imap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libauthdb_ldap_la-db-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libauthdb_ldap_la-passdb-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libauthdb_ldap_la-userdb-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmech_gssapi_la-mech-gssapi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-anonymous.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-apop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-cram-md5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-digest-md5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-dovecot-token.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-external.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-gssapi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-login.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-ntlm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-oauth2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-otp-skey-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-otp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-plain-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-plain.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-rpa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-scram-sha1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-skey.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-winbind.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-blocking.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-bsdauth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-checkpassword.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-oauth2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-pam.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-passwd-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-passwd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-shadow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-sia.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-sql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-static.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-template.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb-vpopmail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passdb.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-crypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-md5crypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-otp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-pbkdf2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-rpa.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme-scram.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password-scheme.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-auth-request-var-expand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-db-dict.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-username-filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_auth_cache-auth-cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_auth_cache-test-auth-cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-blocking.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-checkpassword.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-ldap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-nss.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-passwd-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-passwd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-prefetch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-sql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-static.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-template.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb-vpopmail.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userdb.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libauthdb_imap_la-passdb-imap.lo: passdb-imap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_imap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libauthdb_imap_la-passdb-imap.lo -MD -MP -MF $(DEPDIR)/libauthdb_imap_la-passdb-imap.Tpo -c -o libauthdb_imap_la-passdb-imap.lo `test -f 'passdb-imap.c' || echo '$(srcdir)/'`passdb-imap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libauthdb_imap_la-passdb-imap.Tpo $(DEPDIR)/libauthdb_imap_la-passdb-imap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='passdb-imap.c' object='libauthdb_imap_la-passdb-imap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_imap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libauthdb_imap_la-passdb-imap.lo `test -f 'passdb-imap.c' || echo '$(srcdir)/'`passdb-imap.c libauthdb_ldap_la-db-ldap.lo: db-ldap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libauthdb_ldap_la-db-ldap.lo -MD -MP -MF $(DEPDIR)/libauthdb_ldap_la-db-ldap.Tpo -c -o libauthdb_ldap_la-db-ldap.lo `test -f 'db-ldap.c' || echo '$(srcdir)/'`db-ldap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libauthdb_ldap_la-db-ldap.Tpo $(DEPDIR)/libauthdb_ldap_la-db-ldap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='db-ldap.c' object='libauthdb_ldap_la-db-ldap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libauthdb_ldap_la-db-ldap.lo `test -f 'db-ldap.c' || echo '$(srcdir)/'`db-ldap.c libauthdb_ldap_la-passdb-ldap.lo: passdb-ldap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libauthdb_ldap_la-passdb-ldap.lo -MD -MP -MF $(DEPDIR)/libauthdb_ldap_la-passdb-ldap.Tpo -c -o libauthdb_ldap_la-passdb-ldap.lo `test -f 'passdb-ldap.c' || echo '$(srcdir)/'`passdb-ldap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libauthdb_ldap_la-passdb-ldap.Tpo $(DEPDIR)/libauthdb_ldap_la-passdb-ldap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='passdb-ldap.c' object='libauthdb_ldap_la-passdb-ldap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libauthdb_ldap_la-passdb-ldap.lo `test -f 'passdb-ldap.c' || echo '$(srcdir)/'`passdb-ldap.c libauthdb_ldap_la-userdb-ldap.lo: userdb-ldap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libauthdb_ldap_la-userdb-ldap.lo -MD -MP -MF $(DEPDIR)/libauthdb_ldap_la-userdb-ldap.Tpo -c -o libauthdb_ldap_la-userdb-ldap.lo `test -f 'userdb-ldap.c' || echo '$(srcdir)/'`userdb-ldap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libauthdb_ldap_la-userdb-ldap.Tpo $(DEPDIR)/libauthdb_ldap_la-userdb-ldap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userdb-ldap.c' object='libauthdb_ldap_la-userdb-ldap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libauthdb_ldap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libauthdb_ldap_la-userdb-ldap.lo `test -f 'userdb-ldap.c' || echo '$(srcdir)/'`userdb-ldap.c libmech_gssapi_la-mech-gssapi.lo: mech-gssapi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmech_gssapi_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmech_gssapi_la-mech-gssapi.lo -MD -MP -MF $(DEPDIR)/libmech_gssapi_la-mech-gssapi.Tpo -c -o libmech_gssapi_la-mech-gssapi.lo `test -f 'mech-gssapi.c' || echo '$(srcdir)/'`mech-gssapi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmech_gssapi_la-mech-gssapi.Tpo $(DEPDIR)/libmech_gssapi_la-mech-gssapi.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mech-gssapi.c' object='libmech_gssapi_la-mech-gssapi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmech_gssapi_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmech_gssapi_la-mech-gssapi.lo `test -f 'mech-gssapi.c' || echo '$(srcdir)/'`mech-gssapi.c auth-main.o: main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(auth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth-main.o -MD -MP -MF $(DEPDIR)/auth-main.Tpo -c -o auth-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth-main.Tpo $(DEPDIR)/auth-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='auth-main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(auth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c auth-main.obj: main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(auth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth-main.obj -MD -MP -MF $(DEPDIR)/auth-main.Tpo -c -o auth-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth-main.Tpo $(DEPDIR)/auth-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='auth-main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(auth_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` test_auth_cache-auth-cache.o: auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_auth_cache-auth-cache.o -MD -MP -MF $(DEPDIR)/test_auth_cache-auth-cache.Tpo -c -o test_auth_cache-auth-cache.o `test -f 'auth-cache.c' || echo '$(srcdir)/'`auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_auth_cache-auth-cache.Tpo $(DEPDIR)/test_auth_cache-auth-cache.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='auth-cache.c' object='test_auth_cache-auth-cache.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_auth_cache-auth-cache.o `test -f 'auth-cache.c' || echo '$(srcdir)/'`auth-cache.c test_auth_cache-auth-cache.obj: auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_auth_cache-auth-cache.obj -MD -MP -MF $(DEPDIR)/test_auth_cache-auth-cache.Tpo -c -o test_auth_cache-auth-cache.obj `if test -f 'auth-cache.c'; then $(CYGPATH_W) 'auth-cache.c'; else $(CYGPATH_W) '$(srcdir)/auth-cache.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_auth_cache-auth-cache.Tpo $(DEPDIR)/test_auth_cache-auth-cache.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='auth-cache.c' object='test_auth_cache-auth-cache.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_auth_cache-auth-cache.obj `if test -f 'auth-cache.c'; then $(CYGPATH_W) 'auth-cache.c'; else $(CYGPATH_W) '$(srcdir)/auth-cache.c'; fi` test_auth_cache-test-auth-cache.o: test-auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_auth_cache-test-auth-cache.o -MD -MP -MF $(DEPDIR)/test_auth_cache-test-auth-cache.Tpo -c -o test_auth_cache-test-auth-cache.o `test -f 'test-auth-cache.c' || echo '$(srcdir)/'`test-auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_auth_cache-test-auth-cache.Tpo $(DEPDIR)/test_auth_cache-test-auth-cache.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-auth-cache.c' object='test_auth_cache-test-auth-cache.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_auth_cache-test-auth-cache.o `test -f 'test-auth-cache.c' || echo '$(srcdir)/'`test-auth-cache.c test_auth_cache-test-auth-cache.obj: test-auth-cache.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_auth_cache-test-auth-cache.obj -MD -MP -MF $(DEPDIR)/test_auth_cache-test-auth-cache.Tpo -c -o test_auth_cache-test-auth-cache.obj `if test -f 'test-auth-cache.c'; then $(CYGPATH_W) 'test-auth-cache.c'; else $(CYGPATH_W) '$(srcdir)/test-auth-cache.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_auth_cache-test-auth-cache.Tpo $(DEPDIR)/test_auth_cache-test-auth-cache.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-auth-cache.c' object='test_auth_cache-test-auth-cache.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_auth_cache_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_auth_cache-test-auth-cache.obj `if test -f 'test-auth-cache.c'; then $(CYGPATH_W) 'test-auth-cache.c'; else $(CYGPATH_W) '$(srcdir)/test-auth-cache.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(auth_moduledir)" "$(DESTDIR)$(stats_moduledir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-auth_moduleLTLIBRARIES clean-generic clean-libtool \ clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibexecPROGRAMS \ clean-stats_moduleLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-auth_moduleLTLIBRARIES \ install-pkginc_libHEADERS install-stats_moduleLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-auth_moduleLTLIBRARIES \ uninstall-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS \ uninstall-stats_moduleLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-auth_moduleLTLIBRARIES clean-generic clean-libtool \ clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibexecPROGRAMS \ clean-stats_moduleLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am \ install-auth_moduleLTLIBRARIES install-data install-data-am \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-stats_moduleLTLIBRARIES \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-auth_moduleLTLIBRARIES uninstall-pkginc_libHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-stats_moduleLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/auth/mech-otp-skey-common.h0000644000175000017500000000073413165463624016217 00000000000000#ifndef MECH_OTP_SKEY_COMMON_H #define MECH_OTP_SKEY_COMMON_H struct otp_auth_request { struct auth_request auth_request; pool_t pool; int lock; struct otp_state state; }; void otp_lock_init(void); int otp_try_lock(struct auth_request *auth_request); void otp_unlock(struct auth_request *auth_request); void otp_set_credentials_callback(bool success, struct auth_request *auth_request); void mech_otp_skey_auth_free(struct auth_request *auth_request); #endif dovecot-2.2.33.2/src/auth/passdb-sql.c0000644000175000017500000002140013165463624014301 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_SQL #include "safe-memset.h" #include "password-scheme.h" #include "auth-cache.h" #include "db-sql.h" #include struct sql_passdb_module { struct passdb_module module; struct sql_connection *conn; }; struct passdb_sql_request { struct auth_request *auth_request; union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; set_credentials_callback_t *set_credentials; } callback; }; static void sql_query_save_results(struct sql_result *result, struct passdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; struct passdb_module *_module = auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; unsigned int i, fields_count; const char *name, *value; fields_count = sql_result_get_fields_count(result); for (i = 0; i < fields_count; i++) { name = sql_result_get_field_name(result, i); value = sql_result_get_field_value(result, i); if (*name == '\0') ; else if (value == NULL) auth_request_set_null_field(auth_request, name); else { auth_request_set_field(auth_request, name, value, module->conn->set.default_pass_scheme); } } } static void sql_query_callback(struct sql_result *result, struct passdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; struct passdb_module *_module = auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; enum passdb_result passdb_result; const char *password, *scheme; int ret; passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; password = NULL; ret = sql_result_next_row(result); if (ret >= 0) db_sql_success(module->conn); if (ret < 0) { if (!module->conn->default_password_query) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Password query failed: %s", sql_result_get_error(result)); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Password query failed: %s " "(using built-in default password_query: %s)", sql_result_get_error(result), module->conn->set.password_query); } } else if (ret == 0) { auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); passdb_result = PASSDB_RESULT_USER_UNKNOWN; } else { sql_query_save_results(result, sql_request); /* Note that we really want to check if the password field is found. Just checking if password is set isn't enough, because with proxies we might want to return NULL as password. */ if (sql_result_find_field(result, "password") < 0 && sql_result_find_field(result, "password_noscheme") < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Password query must return a field named " "'password'"); } else if (sql_result_next_row(result) > 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Password query returned multiple matches"); } else if (auth_request->passdb_password == NULL && !auth_fields_exists(auth_request->extra_fields, "nopassword")) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "Empty password returned without nopassword"); passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; } else { /* passdb_password may change on the way, so we'll need to strdup. */ password = t_strdup(auth_request->passdb_password); passdb_result = PASSDB_RESULT_OK; } } scheme = password_get_scheme(&password); /* auth_request_set_field() sets scheme */ i_assert(password == NULL || scheme != NULL); if (auth_request->credentials_scheme != NULL) { passdb_handle_credentials(passdb_result, password, scheme, sql_request->callback.lookup_credentials, auth_request); auth_request_unref(&auth_request); return; } /* verify plain */ if (password == NULL) { sql_request->callback.verify_plain(passdb_result, auth_request); auth_request_unref(&auth_request); return; } ret = auth_request_password_verify(auth_request, auth_request->mech_password, password, scheme, AUTH_SUBSYS_DB); sql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH, auth_request); auth_request_unref(&auth_request); } static const char * passdb_sql_escape(const char *str, const struct auth_request *auth_request) { struct passdb_module *_module = auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; return sql_escape_string(module->conn->db, str); } static void sql_lookup_pass(struct passdb_sql_request *sql_request) { struct passdb_module *_module = sql_request->auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; const char *query; query = t_auth_request_var_expand(module->conn->set.password_query, sql_request->auth_request, passdb_sql_escape); auth_request_log_debug(sql_request->auth_request, AUTH_SUBSYS_DB, "query: %s", query); auth_request_ref(sql_request->auth_request); sql_query(module->conn->db, query, sql_query_callback, sql_request); } static void sql_verify_plain(struct auth_request *request, const char *password ATTR_UNUSED, verify_plain_callback_t *callback) { struct passdb_sql_request *sql_request; sql_request = p_new(request->pool, struct passdb_sql_request, 1); sql_request->auth_request = request; sql_request->callback.verify_plain = callback; sql_lookup_pass(sql_request); } static void sql_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passdb_sql_request *sql_request; sql_request = p_new(request->pool, struct passdb_sql_request, 1); sql_request->auth_request = request; sql_request->callback.lookup_credentials = callback; sql_lookup_pass(sql_request); } static void sql_set_credentials_callback(const char *error, struct passdb_sql_request *sql_request) { struct passdb_module *_module = sql_request->auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; if (error != NULL) { if (!module->conn->default_update_query) { auth_request_log_error(sql_request->auth_request, AUTH_SUBSYS_DB, "Set credentials query failed: %s", error); } else { auth_request_log_error(sql_request->auth_request, AUTH_SUBSYS_DB, "Set credentials query failed: %s" "(using built-in default update_query: %s)", error, module->conn->set.update_query); } } sql_request->callback. set_credentials(error == NULL, sql_request->auth_request); i_free(sql_request); } static int sql_set_credentials(struct auth_request *request, const char *new_credentials, set_credentials_callback_t *callback) { struct sql_passdb_module *module = (struct sql_passdb_module *) request->passdb->passdb; struct sql_transaction_context *transaction; struct passdb_sql_request *sql_request; const char *query; request->mech_password = p_strdup(request->pool, new_credentials); query = t_auth_request_var_expand(module->conn->set.update_query, request, passdb_sql_escape); sql_request = i_new(struct passdb_sql_request, 1); sql_request->auth_request = request; sql_request->callback.set_credentials = callback; transaction = sql_transaction_begin(module->conn->db); sql_update(transaction, query); sql_transaction_commit(&transaction, sql_set_credentials_callback, sql_request); return 0; } static struct passdb_module * passdb_sql_preinit(pool_t pool, const char *args) { struct sql_passdb_module *module; struct sql_connection *conn; module = p_new(pool, struct sql_passdb_module, 1); module->conn = conn = db_sql_init(args, FALSE); module->module.default_cache_key = auth_cache_parse_key(pool, conn->set.password_query); module->module.default_pass_scheme = conn->set.default_pass_scheme; return &module->module; } static void passdb_sql_init(struct passdb_module *_module) { struct sql_passdb_module *module = (struct sql_passdb_module *)_module; enum sql_db_flags flags; flags = sql_get_flags(module->conn->db); module->module.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; if (!module->module.blocking || worker) db_sql_connect(module->conn); db_sql_check_userdb_warning(module->conn); } static void passdb_sql_deinit(struct passdb_module *_module) { struct sql_passdb_module *module = (struct sql_passdb_module *)_module; db_sql_unref(&module->conn); } struct passdb_module_interface passdb_sql = { "sql", passdb_sql_preinit, passdb_sql_init, passdb_sql_deinit, sql_verify_plain, sql_lookup_credentials, sql_set_credentials }; #else struct passdb_module_interface passdb_sql = { .name = "sql" }; #endif dovecot-2.2.33.2/src/auth/passdb-shadow.c0000644000175000017500000000573113123174404014765 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_SHADOW #include "safe-memset.h" #include #define SHADOW_CACHE_KEY "%u" #define SHADOW_PASS_SCHEME "CRYPT" static enum passdb_result shadow_lookup(struct auth_request *request, struct spwd **spw_r) { auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); *spw_r = getspnam(request->user); if (*spw_r == NULL) { auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); return PASSDB_RESULT_USER_UNKNOWN; } if (!IS_VALID_PASSWD((*spw_r)->sp_pwdp)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "invalid password field"); return PASSDB_RESULT_USER_DISABLED; } /* save the password so cache can use it */ auth_request_set_field(request, "password", (*spw_r)->sp_pwdp, SHADOW_PASS_SCHEME); return PASSDB_RESULT_OK; } static void shadow_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct spwd *spw; enum passdb_result res; int ret; res = shadow_lookup(request, &spw); if (res != PASSDB_RESULT_OK) { callback(res, request); return; } /* check if the password is valid */ ret = auth_request_password_verify(request, password, spw->sp_pwdp, SHADOW_PASS_SCHEME, AUTH_SUBSYS_DB); /* clear the passwords from memory */ safe_memset(spw->sp_pwdp, 0, strlen(spw->sp_pwdp)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", spw->sp_namp, NULL); callback(PASSDB_RESULT_OK, request); } static void shadow_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct spwd *spw; enum passdb_result res; res = shadow_lookup(request, &spw); if (res != PASSDB_RESULT_OK) { callback(res, NULL, 0, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", spw->sp_namp, NULL); passdb_handle_credentials(PASSDB_RESULT_OK, spw->sp_pwdp, SHADOW_PASS_SCHEME, callback, request); } static struct passdb_module * shadow_preinit(pool_t pool, const char *args) { struct passdb_module *module; module = p_new(pool, struct passdb_module, 1); module->blocking = TRUE; if (strcmp(args, "blocking=no") == 0) module->blocking = FALSE; else if (*args != '\0') i_fatal("passdb shadow: Unknown setting: %s", args); module->default_cache_key = SHADOW_CACHE_KEY; module->default_pass_scheme = SHADOW_PASS_SCHEME; return module; } static void shadow_deinit(struct passdb_module *module ATTR_UNUSED) { endspent(); } struct passdb_module_interface passdb_shadow = { "shadow", shadow_preinit, NULL, shadow_deinit, shadow_verify_plain, shadow_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_shadow = { .name = "shadow" }; #endif dovecot-2.2.33.2/src/auth/passdb-ldap.c0000644000175000017500000003557613165463624014445 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #if defined(PASSDB_LDAP) && (defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)) #include "ioloop.h" #include "array.h" #include "str.h" #include "password-scheme.h" #include "auth-cache.h" #include "db-ldap.h" #include struct ldap_passdb_module { struct passdb_module module; struct ldap_connection *conn; }; struct passdb_ldap_request { union { struct ldap_request ldap; struct ldap_request_search search; struct ldap_request_bind bind; } request; const char *dn; union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; } callback; unsigned int entries; bool require_password; }; static void ldap_query_save_result(struct ldap_connection *conn, struct auth_request *auth_request, struct ldap_request_search *ldap_request, LDAPMessage *res) { struct db_ldap_result_iterate_context *ldap_iter; const char *name, *const *values; ldap_iter = db_ldap_result_iterate_init(conn, ldap_request, res, FALSE); while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) { if (values[0] == NULL) { auth_request_set_null_field(auth_request, name); continue; } if (values[1] != NULL) { auth_request_log_warning(auth_request, AUTH_SUBSYS_DB, "Multiple values found for '%s', " "using value '%s'", name, values[0]); } auth_request_set_field(auth_request, name, values[0], conn->set.default_pass_scheme); } db_ldap_result_iterate_deinit(&ldap_iter); } static void ldap_lookup_finish(struct auth_request *auth_request, struct passdb_ldap_request *ldap_request, LDAPMessage *res) { enum passdb_result passdb_result; const char *password = NULL, *scheme; int ret; if (res == NULL) { passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } else if (ldap_request->entries == 0) { passdb_result = PASSDB_RESULT_USER_UNKNOWN; auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); } else if (ldap_request->entries > 1) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "pass_filter matched multiple objects, aborting"); passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } else if (auth_request->passdb_password == NULL && ldap_request->require_password && !auth_fields_exists(auth_request->extra_fields, "nopassword")) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "No password returned (and no nopassword)"); passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; } else { /* passdb_password may change on the way, so we'll need to strdup. */ password = t_strdup(auth_request->passdb_password); passdb_result = PASSDB_RESULT_OK; } scheme = password_get_scheme(&password); /* auth_request_set_field() sets scheme */ i_assert(password == NULL || scheme != NULL); if (auth_request->credentials_scheme != NULL) { passdb_handle_credentials(passdb_result, password, scheme, ldap_request->callback.lookup_credentials, auth_request); } else { if (password != NULL) { ret = auth_request_password_verify(auth_request, auth_request->mech_password, password, scheme, AUTH_SUBSYS_DB); passdb_result = ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH; } ldap_request->callback.verify_plain(passdb_result, auth_request); } } static void ldap_lookup_pass_callback(struct ldap_connection *conn, struct ldap_request *request, LDAPMessage *res) { struct passdb_ldap_request *ldap_request = (struct passdb_ldap_request *)request; struct auth_request *auth_request = request->auth_request; if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { ldap_lookup_finish(auth_request, ldap_request, res); auth_request_unref(&auth_request); return; } if (ldap_request->entries++ == 0) { /* first entry */ ldap_query_save_result(conn, auth_request, &ldap_request->request.search, res); } } static void ldap_auth_bind_callback(struct ldap_connection *conn, struct ldap_request *ldap_request, LDAPMessage *res) { struct passdb_ldap_request *passdb_ldap_request = (struct passdb_ldap_request *)ldap_request; struct auth_request *auth_request = ldap_request->auth_request; enum passdb_result passdb_result; const char *str; int ret; passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; if (res != NULL) { ret = ldap_result2error(conn->ld, res, 0); if (ret == LDAP_SUCCESS) passdb_result = PASSDB_RESULT_OK; else if (ret == LDAP_INVALID_CREDENTIALS) { str = "invalid credentials"; if (auth_request->set->debug_passwords) { str = t_strconcat(str, " (given password: ", auth_request->mech_password, ")", NULL); } auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "%s", str); passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; } else if (ret == LDAP_NO_SUCH_OBJECT) { passdb_result = PASSDB_RESULT_USER_UNKNOWN; auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "ldap_bind() failed: %s", ldap_err2string(ret)); } } passdb_ldap_request->callback. verify_plain(passdb_result, auth_request); auth_request_unref(&auth_request); } static void ldap_auth_bind(struct ldap_connection *conn, struct ldap_request_bind *brequest) { struct passdb_ldap_request *passdb_ldap_request = (struct passdb_ldap_request *)brequest; struct auth_request *auth_request = brequest->request.auth_request; if (*auth_request->mech_password == '\0') { /* Assume that empty password fails. This is especially important with Windows 2003 AD, which always returns success with empty passwords. */ auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "Login attempt with empty password"); passdb_ldap_request->callback. verify_plain(PASSDB_RESULT_PASSWORD_MISMATCH, auth_request); return; } brequest->request.callback = ldap_auth_bind_callback; db_ldap_request(conn, &brequest->request); } static void ldap_bind_lookup_dn_fail(struct auth_request *auth_request, struct passdb_ldap_request *request, LDAPMessage *res) { enum passdb_result passdb_result; if (res == NULL) passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; else if (request->entries == 0) { passdb_result = PASSDB_RESULT_USER_UNKNOWN; auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); } else { i_assert(request->entries > 1); auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "pass_filter matched multiple objects, aborting"); passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } if (auth_request->credentials_scheme != NULL) { request->callback.lookup_credentials(passdb_result, NULL, 0, auth_request); } else { request->callback.verify_plain(passdb_result, auth_request); } auth_request_unref(&auth_request); } static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn, struct ldap_request *ldap_request, LDAPMessage *res) { struct passdb_ldap_request *passdb_ldap_request = (struct passdb_ldap_request *)ldap_request; struct auth_request *auth_request = ldap_request->auth_request; struct passdb_ldap_request *brequest; char *dn; if (res != NULL && ldap_msgtype(res) == LDAP_RES_SEARCH_ENTRY) { if (passdb_ldap_request->entries++ > 0) { /* too many replies */ return; } /* first entry */ ldap_query_save_result(conn, auth_request, &passdb_ldap_request->request.search, res); /* save dn */ dn = ldap_get_dn(conn->ld, res); passdb_ldap_request->dn = p_strdup(auth_request->pool, dn); ldap_memfree(dn); } else if (res == NULL || passdb_ldap_request->entries != 1) { /* failure */ ldap_bind_lookup_dn_fail(auth_request, passdb_ldap_request, res); } else if (auth_request->skip_password_check) { /* we've already verified that the password matched - we just wanted to get any extra fields */ passdb_ldap_request->callback. verify_plain(PASSDB_RESULT_OK, auth_request); auth_request_unref(&auth_request); } else { /* create a new bind request */ brequest = p_new(auth_request->pool, struct passdb_ldap_request, 1); brequest->dn = passdb_ldap_request->dn; brequest->callback = passdb_ldap_request->callback; brequest->request.bind.dn = brequest->dn; brequest->request.bind.request.type = LDAP_REQUEST_TYPE_BIND; brequest->request.bind.request.auth_request = auth_request; ldap_auth_bind(conn, &brequest->request.bind); } } static void ldap_lookup_pass(struct auth_request *auth_request, struct passdb_ldap_request *request, bool require_password) { struct passdb_module *_module = auth_request->passdb->passdb; struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; struct ldap_connection *conn = module->conn; struct ldap_request_search *srequest = &request->request.search; const char **attr_names = (const char **)conn->pass_attr_names; string_t *str; request->require_password = require_password; srequest->request.type = LDAP_REQUEST_TYPE_SEARCH; str = t_str_new(512); auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); srequest->base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); auth_request_var_expand(str, conn->set.pass_filter, auth_request, ldap_escape); srequest->filter = p_strdup(auth_request->pool, str_c(str)); srequest->attr_map = &conn->pass_attr_map; srequest->attributes = conn->pass_attr_names; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "pass search: " "base=%s scope=%s filter=%s fields=%s", srequest->base, conn->set.scope, srequest->filter, attr_names == NULL ? "(all)" : t_strarray_join(attr_names, ",")); srequest->request.callback = ldap_lookup_pass_callback; db_ldap_request(conn, &srequest->request); } static void ldap_bind_lookup_dn(struct auth_request *auth_request, struct passdb_ldap_request *request) { struct passdb_module *_module = auth_request->passdb->passdb; struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; struct ldap_connection *conn = module->conn; struct ldap_request_search *srequest = &request->request.search; string_t *str; srequest->request.type = LDAP_REQUEST_TYPE_SEARCH; str = t_str_new(512); auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); srequest->base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); auth_request_var_expand(str, conn->set.pass_filter, auth_request, ldap_escape); srequest->filter = p_strdup(auth_request->pool, str_c(str)); /* we don't need the attributes to perform authentication, but they may contain some extra parameters. if a password is returned, it's just ignored. */ srequest->attr_map = &conn->pass_attr_map; srequest->attributes = conn->pass_attr_names; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "bind search: base=%s filter=%s", srequest->base, srequest->filter); srequest->request.callback = ldap_bind_lookup_dn_callback; db_ldap_request(conn, &srequest->request); } static void ldap_verify_plain_auth_bind_userdn(struct auth_request *auth_request, struct passdb_ldap_request *request) { struct passdb_module *_module = auth_request->passdb->passdb; struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; struct ldap_connection *conn = module->conn; struct ldap_request_bind *brequest = &request->request.bind; string_t *dn; brequest->request.type = LDAP_REQUEST_TYPE_BIND; dn = t_str_new(512); auth_request_var_expand(dn, conn->set.auth_bind_userdn, auth_request, ldap_escape); brequest->dn = p_strdup(auth_request->pool, str_c(dn)); ldap_auth_bind(conn, brequest); } static void ldap_verify_plain(struct auth_request *request, const char *password ATTR_UNUSED, verify_plain_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; struct ldap_connection *conn = module->conn; struct passdb_ldap_request *ldap_request; /* reconnect if needed. this is also done by db_ldap_search(), but with auth binds we'll have to do it ourself */ if (db_ldap_connect(conn)< 0) { callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; } ldap_request = p_new(request->pool, struct passdb_ldap_request, 1); ldap_request->callback.verify_plain = callback; auth_request_ref(request); ldap_request->request.ldap.auth_request = request; if (!conn->set.auth_bind) ldap_lookup_pass(request, ldap_request, TRUE); else if (conn->set.auth_bind_userdn == NULL) ldap_bind_lookup_dn(request, ldap_request); else ldap_verify_plain_auth_bind_userdn(request, ldap_request); } static void ldap_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; struct passdb_ldap_request *ldap_request; bool require_password; ldap_request = p_new(request->pool, struct passdb_ldap_request, 1); ldap_request->callback.lookup_credentials = callback; auth_request_ref(request); ldap_request->request.ldap.auth_request = request; /* with auth_bind=yes we don't necessarily have a password. this will fail actual password credentials lookups, but it's fine for passdb lookups done by lmtp/doveadm */ require_password = !module->conn->set.auth_bind; ldap_lookup_pass(request, ldap_request, require_password); } static struct passdb_module * passdb_ldap_preinit(pool_t pool, const char *args) { struct ldap_passdb_module *module; struct ldap_connection *conn; module = p_new(pool, struct ldap_passdb_module, 1); module->conn = conn = db_ldap_init(args, FALSE); p_array_init(&conn->pass_attr_map, pool, 16); db_ldap_set_attrs(conn, conn->set.pass_attrs, &conn->pass_attr_names, &conn->pass_attr_map, conn->set.auth_bind ? "password" : NULL); module->module.blocking = conn->set.blocking; module->module.default_cache_key = auth_cache_parse_key(pool, t_strconcat(conn->set.base, conn->set.pass_attrs, conn->set.pass_filter, NULL)); module->module.default_pass_scheme = conn->set.default_pass_scheme; return &module->module; } static void passdb_ldap_init(struct passdb_module *_module) { struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; if (!module->module.blocking || worker) db_ldap_connect_delayed(module->conn); } static void passdb_ldap_deinit(struct passdb_module *_module) { struct ldap_passdb_module *module = (struct ldap_passdb_module *)_module; db_ldap_unref(&module->conn); } #ifndef PLUGIN_BUILD struct passdb_module_interface passdb_ldap = #else struct passdb_module_interface passdb_ldap_plugin = #endif { "ldap", passdb_ldap_preinit, passdb_ldap_init, passdb_ldap_deinit, ldap_verify_plain, ldap_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_ldap = { .name = "ldap" }; #endif dovecot-2.2.33.2/src/auth/password-scheme-md5crypt.c0000644000175000017500000000733113165463624017110 00000000000000/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ /* * Ported from FreeBSD to Linux, only minimal changes. --marekm */ /* * Adapted from shadow-19990607 by Tudor Bosman, tudorb@jm.nu */ #include "lib.h" #include "safe-memset.h" #include "str.h" #include "md5.h" #include "password-scheme.h" static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static char magic[] = "$1$"; /* * This string is magic for * this algorithm. Having * it this way, we can get * get better later on */ static void to64(string_t *str, unsigned long v, int n) { while (--n >= 0) { str_append_c(str, itoa64[v&0x3f]); v >>= 6; } } /* * UNIX password * * Use MD5 for what it is best at... */ const char *password_generate_md5_crypt(const char *pw, const char *salt) { const char *sp,*ep; unsigned char final[MD5_RESULTLEN]; int sl,pl,i,j; struct md5_context ctx,ctx1; unsigned long l; string_t *passwd; size_t pw_len = strlen(pw); /* Refine the Salt first */ sp = salt; /* If it starts with the magic string, then skip that */ if (strncmp(sp, magic, sizeof(magic)-1) == 0) sp += sizeof(magic)-1; /* It stops at the first '$', max 8 chars */ for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) continue; /* get the length of the true salt */ sl = ep - sp; md5_init(&ctx); /* The password first, since that is what is most unknown */ md5_update(&ctx,pw,pw_len); /* Then our magic string */ md5_update(&ctx,magic,sizeof(magic)-1); /* Then the raw salt */ md5_update(&ctx,sp,sl); /* Then just as many characters of the MD5(pw,salt,pw) */ md5_init(&ctx1); md5_update(&ctx1,pw,pw_len); md5_update(&ctx1,sp,sl); md5_update(&ctx1,pw,pw_len); md5_final(&ctx1,final); for(pl = pw_len; pl > 0; pl -= MD5_RESULTLEN) md5_update(&ctx,final,pl>MD5_RESULTLEN ? MD5_RESULTLEN : pl); /* Don't leave anything around in vm they could use. */ safe_memset(final, 0, sizeof(final)); /* Then something really weird... */ for (j=0,i = pw_len; i ; i >>= 1) if(i&1) md5_update(&ctx, final+j, 1); else md5_update(&ctx, pw+j, 1); /* Now make the output string */ passwd = t_str_new(sl + 64); str_append(passwd, magic); str_append_n(passwd, sp, sl); str_append_c(passwd, '$'); md5_final(&ctx,final); /* * and now, just to make sure things don't run too fast * On a 60 Mhz Pentium this takes 34 msec, so you would * need 30 seconds to build a 1000 entry dictionary... */ for(i=0;i<1000;i++) { md5_init(&ctx1); if(i & 1) md5_update(&ctx1,pw,pw_len); else md5_update(&ctx1,final,MD5_RESULTLEN); if(i % 3) md5_update(&ctx1,sp,sl); if(i % 7) md5_update(&ctx1,pw,pw_len); if(i & 1) md5_update(&ctx1,final,MD5_RESULTLEN); else md5_update(&ctx1,pw,pw_len); md5_final(&ctx1,final); } l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(passwd,l,4); l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(passwd,l,4); l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(passwd,l,4); l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(passwd,l,4); l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(passwd,l,4); l = final[11] ; to64(passwd,l,2); /* Don't leave anything around in vm they could use. */ safe_memset(final, 0, sizeof(final)); return str_c(passwd); } dovecot-2.2.33.2/src/auth/mech-rpa.c0000644000175000017500000003577713165463624013752 00000000000000/* * Compuserve RPA authentication mechanism. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "mech.h" #include "passdb.h" #include "str.h" #include "strfuncs.h" #include "safe-memset.h" #include "randgen.h" #include "buffer.h" #include "hostpid.h" #include "hex-binary.h" #include "md5.h" struct rpa_auth_request { struct auth_request auth_request; pool_t pool; int phase; /* cached: */ unsigned char pwd_md5[MD5_RESULTLEN]; size_t service_len; const unsigned char *service_ucs2be; size_t username_len; const unsigned char *username_ucs2be; size_t realm_len; const unsigned char *realm_ucs2be; /* requested: */ unsigned char *service_challenge; unsigned char *service_timestamp; /* received: */ unsigned int user_challenge_len; unsigned char *user_challenge; unsigned char *user_response; unsigned char session_key[16]; }; #define RPA_SCHALLENGE_LEN 32 #define RPA_UCHALLENGE_LEN 16 #define RPA_TIMESTAMP_LEN 14 #define ASN1_APPLICATION 0x60 /* Object id encoded using ASN.1 DER */ static const unsigned char rpa_oid[] = { 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x73, 0x01, 0x01 }; void *ucs2be_str(pool_t pool, const char *str, size_t *size); /* * Compute client -> server authentication response. */ static void rpa_user_response(struct rpa_auth_request *request, unsigned char digest[STATIC_ARRAY MD5_RESULTLEN]) { struct md5_context ctx; unsigned char z[48]; memset(z, 0, sizeof(z)); md5_init(&ctx); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_update(&ctx, z, sizeof(z)); md5_update(&ctx, request->username_ucs2be, request->username_len); md5_update(&ctx, request->service_ucs2be, request->service_len); md5_update(&ctx, request->realm_ucs2be, request->realm_len); md5_update(&ctx, request->user_challenge, request->user_challenge_len); md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_final(&ctx, digest); } /* * Compute server -> client authentication response. */ static void rpa_server_response(struct rpa_auth_request *request, unsigned char digest[STATIC_ARRAY MD5_RESULTLEN]) { struct md5_context ctx; unsigned char tmp[MD5_RESULTLEN]; unsigned char z[48]; unsigned int i; memset(z, 0, sizeof(z)); md5_init(&ctx); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_update(&ctx, z, sizeof(z)); md5_update(&ctx, request->service_ucs2be, request->service_len); md5_update(&ctx, request->username_ucs2be, request->username_len); md5_update(&ctx, request->realm_ucs2be, request->realm_len); md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); md5_update(&ctx, request->user_challenge, request->user_challenge_len); md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_final(&ctx, tmp); for (i = 0; i < sizeof(tmp); i++) tmp[i] = request->session_key[i] ^ tmp[i]; md5_init(&ctx); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_update(&ctx, z, sizeof(z)); md5_update(&ctx, request->service_ucs2be, request->service_len); md5_update(&ctx, request->username_ucs2be, request->username_len); md5_update(&ctx, request->realm_ucs2be, request->realm_len); md5_update(&ctx, request->session_key, sizeof(request->session_key)); md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); md5_update(&ctx, request->user_challenge, request->user_challenge_len); md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); md5_update(&ctx, tmp, sizeof(tmp)); md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5)); md5_final(&ctx, digest); } static const unsigned char * rpa_check_message(const unsigned char *data, const unsigned char *end, const char **error) { const unsigned char *p = data; unsigned int len = 0; if (p + 2 > end) { *error = "message too short"; return NULL; } if (*p++ != ASN1_APPLICATION) { *error = "invalid data type"; return NULL; } if ((*p & 0x80) != 0) { unsigned int nbytes = *p++ & 0x7f; while (nbytes-- > 0) { if (p >= end) { *error = "invalid structure length"; return NULL; } len = (len << 8) | *p++; } } else len = *p++; if ((size_t)(end - p) != len) { *error = "structure length disagrees with data size"; return NULL; } if (p + sizeof(rpa_oid) > end) { *error = "not enough space for object id"; return NULL; } if (memcmp(p, rpa_oid, sizeof(rpa_oid)) != 0) { *error = "invalid object id"; return NULL; } return p + sizeof(rpa_oid); } static bool rpa_parse_token1(const void *data, size_t data_size, const char **error) { const unsigned char *end = ((const unsigned char *) data) + data_size; const unsigned char *p; unsigned int version_lo, version_hi; p = rpa_check_message(data, end, error); if (p == NULL) return FALSE; if (p + 6 > end) { *error = "message too short"; return FALSE; } version_lo = p[0] + (p[1] << 8); version_hi = p[2] + (p[3] << 8); if ((version_lo > 3) || (version_hi < 3)) { *error = "protocol version mismatch"; return FALSE; } p += 4; if ((p[0] != 0) || (p[1] != 1)) { *error = "invalid message flags"; return FALSE; } p += 2; if (p != end) { *error = "unneeded data found"; return FALSE; } return TRUE; } static unsigned int rpa_read_buffer(pool_t pool, const unsigned char **data, const unsigned char *end, unsigned char **buffer) { const unsigned char *p = *data; unsigned int len; if (p > end) return 0; len = *p++; if (p + len > end) return 0; *buffer = p_malloc(pool, len); memcpy(*buffer, p, len); *data += 1 + len; return len; } static bool rpa_parse_token3(struct rpa_auth_request *request, const void *data, size_t data_size, const char **error) { struct auth_request *auth_request = &request->auth_request; const unsigned char *end = ((const unsigned char *)data) + data_size; const unsigned char *p; unsigned int len; const char *user, *realm; p = rpa_check_message(data, end, error); if (p == NULL) return FALSE; /* Read username@realm */ if (p + 2 > end) { *error = "message too short"; return FALSE; } len = (p[0] << 8) + p[1]; if (p + 2 + len > end) { *error = "message too short"; return FALSE; } p += 2; user = t_strndup(p, len); realm = strrchr(user, '@'); if (realm == NULL) { *error = "missing realm"; return FALSE; } user = t_strdup_until(user, realm++); p += len; if (!auth_request_set_username(auth_request, user, error)) return FALSE; request->username_ucs2be = ucs2be_str(request->pool, auth_request->user, &request->username_len); request->realm_ucs2be = ucs2be_str(request->pool, realm, &request->realm_len); /* Read user challenge */ request->user_challenge_len = rpa_read_buffer(request->pool, &p, end, &request->user_challenge); if (request->user_challenge_len == 0) { *error = "invalid user challenge"; return FALSE; } /* Read user response */ len = rpa_read_buffer(request->pool, &p, end, &request->user_response); if (len != RPA_UCHALLENGE_LEN) { *error = "invalid user response"; return FALSE; } if (p != end) { *error = "unneeded data found"; return FALSE; } return TRUE; } static void buffer_append_asn1_length(buffer_t *buf, unsigned int length) { if (length < 0x80) { buffer_append_c(buf, length); } else if (length < 0x100) { buffer_append_c(buf, 0x81); buffer_append_c(buf, length); } else { buffer_append_c(buf, 0x82); buffer_append_c(buf, length >> 8); buffer_append_c(buf, length & 0xff); } } static void rpa_add_realm(string_t *realms, const char *realm, const char *service) { str_append(realms, service); str_append_c(realms, '@'); str_append(realms, realm); str_append_c(realms, ' '); } static const unsigned char * mech_rpa_build_token2(struct rpa_auth_request *request, size_t *size) { const struct auth_settings *set = request->auth_request.set; unsigned int realms_len, length; string_t *realms; buffer_t *buf; unsigned char timestamp[RPA_TIMESTAMP_LEN / 2]; const char *const *tmp; realms = t_str_new(64); for (tmp = set->realms_arr; *tmp != NULL; tmp++) { rpa_add_realm(realms, *tmp, request->auth_request.service); } if (str_len(realms) == 0) { rpa_add_realm(realms, *set->default_realm != '\0' ? set->default_realm : my_hostname, request->auth_request.service); } realms_len = str_len(realms) - 1; length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN + RPA_TIMESTAMP_LEN + 2 + realms_len; buf = buffer_create_dynamic(request->pool, length + 4); buffer_append_c(buf, ASN1_APPLICATION); buffer_append_asn1_length(buf, length); buffer_append(buf, rpa_oid, sizeof(rpa_oid)); /* Protocol version */ buffer_append_c(buf, 3); buffer_append_c(buf, 0); /* Service challenge */ request->service_challenge = p_malloc(request->pool, RPA_SCHALLENGE_LEN); random_fill(request->service_challenge, RPA_SCHALLENGE_LEN); buffer_append_c(buf, RPA_SCHALLENGE_LEN); buffer_append(buf, request->service_challenge, RPA_SCHALLENGE_LEN); /* Timestamp, looks like clients accept anything we send */ random_fill(timestamp, sizeof(timestamp)); request->service_timestamp = p_malloc(request->pool, RPA_TIMESTAMP_LEN); memcpy(request->service_timestamp, binary_to_hex(timestamp, sizeof(timestamp)), RPA_TIMESTAMP_LEN); buffer_append(buf, request->service_timestamp, RPA_TIMESTAMP_LEN); /* Realm list */ buffer_append_c(buf, realms_len >> 8); buffer_append_c(buf, realms_len & 0xff); buffer_append(buf, str_c(realms), realms_len); *size = buffer_get_used_size(buf); return buffer_free_without_data(&buf); } static const unsigned char * mech_rpa_build_token4(struct rpa_auth_request *request, size_t *size) { buffer_t *buf; unsigned char server_response[MD5_RESULTLEN]; unsigned int length = sizeof(rpa_oid) + sizeof(server_response) + 1 + sizeof(request->session_key) + 1 + 1; buf = buffer_create_dynamic(request->pool, length + 4); buffer_append_c(buf, ASN1_APPLICATION); buffer_append_asn1_length(buf, length); buffer_append(buf, rpa_oid, sizeof(rpa_oid)); /* Generate random session key */ random_fill(request->session_key, sizeof(request->session_key)); /* Server authentication response */ rpa_server_response(request, server_response); buffer_append_c(buf, sizeof(server_response)); buffer_append(buf, server_response, sizeof(server_response)); buffer_append_c(buf, sizeof(request->session_key)); buffer_append(buf, request->session_key, sizeof(request->session_key)); /* Status, 0 - success */ buffer_append_c(buf, 0); *size = buffer_get_used_size(buf); return buffer_free_without_data(&buf); } static bool verify_credentials(struct rpa_auth_request *request, const unsigned char *credentials, size_t size) { unsigned char response[MD5_RESULTLEN]; if (size != sizeof(request->pwd_md5)) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid credentials length"); return FALSE; } memcpy(request->pwd_md5, credentials, sizeof(request->pwd_md5)); rpa_user_response(request, response); return mem_equals_timing_safe(response, request->user_response, sizeof(response)); } static void rpa_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; const unsigned char *token4; size_t token4_size; switch (result) { case PASSDB_RESULT_OK: if (!verify_credentials(request, credentials, size)) auth_request_fail(auth_request); else { token4 = mech_rpa_build_token4(request, &token4_size); auth_request_handler_reply_continue(auth_request, token4, token4_size); request->phase = 2; } break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void mech_rpa_auth_phase1(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; const unsigned char *token2; size_t token2_size; const char *service, *error; if (!rpa_parse_token1(data, data_size, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid token 1: %s", error); auth_request_fail(auth_request); return; } service = t_str_lcase(auth_request->service); token2 = mech_rpa_build_token2(request, &token2_size); request->service_ucs2be = ucs2be_str(request->pool, service, &request->service_len); auth_request_handler_reply_continue(auth_request, token2, token2_size); request->phase = 1; } static void mech_rpa_auth_phase2(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; const char *error; if (!rpa_parse_token3(request, data, data_size, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid token 3: %s", error); auth_request_fail(auth_request); return; } auth_request_lookup_credentials(auth_request, "RPA", rpa_credentials_callback); } static void mech_rpa_auth_phase3(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 }; if ((data_size != sizeof(client_ack)) || (memcmp(data, client_ack, sizeof(client_ack)) != 0)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid token 5 or client rejects us"); auth_request_fail(auth_request); } else { auth_request_success(auth_request, "", 0); } } static void mech_rpa_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; switch (request->phase) { case 0: mech_rpa_auth_phase1(auth_request, data, data_size); break; case 1: mech_rpa_auth_phase2(auth_request, data, data_size); break; case 2: mech_rpa_auth_phase3(auth_request, data, data_size); break; default: auth_request_fail(auth_request); break; } } static void mech_rpa_auth_free(struct auth_request *auth_request) { struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; safe_memset(request->pwd_md5, 0, sizeof(request->pwd_md5)); pool_unref(&auth_request->pool); } static struct auth_request *mech_rpa_auth_new(void) { struct rpa_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"rpa_auth_request", 2048); request = p_new(pool, struct rpa_auth_request, 1); request->pool = pool; request->phase = 0; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_rpa = { "RPA", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE | MECH_SEC_MUTUAL_AUTH, .passdb_need = MECH_PASSDB_NEED_LOOKUP_CREDENTIALS, mech_rpa_auth_new, mech_generic_auth_initial, mech_rpa_auth_continue, mech_rpa_auth_free }; dovecot-2.2.33.2/src/auth/userdb-prefetch.c0000644000175000017500000000275513123174404015313 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_PREFETCH #include "str.h" #include "var-expand.h" static void prefetch_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { /* auth_request_set_field() should have already placed the userdb_* values to userdb_reply. */ if (!auth_request->userdb_prefetch_set) { if (auth_request_get_auth(auth_request)->userdbs->next == NULL) { /* no other userdbs */ if (auth_request->userdb_lookup) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "userdb lookup not possible with only userdb prefetch"); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "passdb didn't return userdb entries"); } callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } if (!auth_request->userdb_lookup || auth_request->debug) { /* more userdbs, they may know the user */ auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "passdb didn't return userdb entries, " "trying the next userdb"); } callback(USERDB_RESULT_USER_UNKNOWN, auth_request); return; } auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "success"); callback(USERDB_RESULT_OK, auth_request); } struct userdb_module_interface userdb_prefetch = { "prefetch", NULL, NULL, NULL, prefetch_lookup, NULL, NULL, NULL }; #else struct userdb_module_interface userdb_prefetch = { .name = "prefetch" }; #endif dovecot-2.2.33.2/src/auth/mech-otp.c0000644000175000017500000001542113123174404013737 00000000000000/* * One-Time-Password (RFC 2444) authentication mechanism. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "safe-memset.h" #include "hash.h" #include "mech.h" #include "passdb.h" #include "hex-binary.h" #include "otp.h" #include "mech-otp-skey-common.h" static void otp_send_challenge(struct auth_request *auth_request, const unsigned char *credentials, size_t size) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; const char *answer; if (otp_parse_dbentry(t_strndup(credentials, size), &request->state) != 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid OTP data in passdb"); auth_request_fail(auth_request); return; } if (--request->state.seq < 1) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "sequence number < 1"); auth_request_fail(auth_request); return; } request->lock = otp_try_lock(auth_request); if (!request->lock) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "user is locked, race attack?"); auth_request_fail(auth_request); return; } answer = p_strdup_printf(request->pool, "otp-%s %u %s ext", digest_name(request->state.algo), request->state.seq, request->state.seed); auth_request_handler_reply_continue(auth_request, answer, strlen(answer)); } static void skey_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { switch (result) { case PASSDB_RESULT_OK: otp_send_challenge(auth_request, credentials, size); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void otp_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { switch (result) { case PASSDB_RESULT_OK: otp_send_challenge(auth_request, credentials, size); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: /* OTP credentials not found, try S/KEY */ auth_request_lookup_credentials(auth_request, "OTP", skey_credentials_callback); break; } } static void mech_otp_auth_phase1(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; const char *authenid, *error; size_t i, count; /* authorization ID \0 authentication ID FIXME: we'll ignore authorization ID for now. */ authenid = NULL; count = 0; for (i = 0; i < data_size; i++) { if (data[i] == '\0') { if (++count == 1) authenid = (const char *) data + i + 1; } } if ((count < 1) || (count > 2)) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid input"); auth_request_fail(auth_request); return; } if (!auth_request_set_username(auth_request, authenid, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); return; } auth_request_lookup_credentials(auth_request, "OTP", otp_credentials_callback); } static void mech_otp_verify(struct auth_request *auth_request, const char *data, bool hex) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; struct otp_state *state = &request->state; unsigned char hash[OTP_HASH_SIZE], cur_hash[OTP_HASH_SIZE]; int ret; ret = otp_parse_response(data, hash, hex); if (ret < 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid response"); auth_request_fail(auth_request); otp_unlock(auth_request); return; } otp_next_hash(state->algo, hash, cur_hash); ret = memcmp(cur_hash, state->hash, OTP_HASH_SIZE); if (ret != 0) { auth_request_fail(auth_request); otp_unlock(auth_request); return; } memcpy(state->hash, hash, sizeof(state->hash)); auth_request_set_credentials(auth_request, "OTP", otp_print_dbentry(state), otp_set_credentials_callback); } static void mech_otp_verify_init(struct auth_request *auth_request, const char *data, bool hex) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; struct otp_state new_state; unsigned char hash[OTP_HASH_SIZE], cur_hash[OTP_HASH_SIZE]; const char *error; int ret; ret = otp_parse_init_response(data, &new_state, cur_hash, hex, &error); if (ret < 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid init response, %s", error); auth_request_fail(auth_request); otp_unlock(auth_request); return; } otp_next_hash(request->state.algo, cur_hash, hash); ret = memcmp(hash, request->state.hash, OTP_HASH_SIZE); if (ret != 0) { auth_request_fail(auth_request); otp_unlock(auth_request); return; } auth_request_set_credentials(auth_request, "OTP", otp_print_dbentry(&new_state), otp_set_credentials_callback); } static void mech_otp_auth_phase2(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { const char *str = t_strndup(data, data_size); if (strncmp(str, "hex:", 4) == 0) { mech_otp_verify(auth_request, str + 4, TRUE); } else if (strncmp(str, "word:", 5) == 0) { mech_otp_verify(auth_request, str + 5, FALSE); } else if (strncmp(str, "init-hex:", 9) == 0) { mech_otp_verify_init(auth_request, str + 9, TRUE); } else if (strncmp(str, "init-word:", 10) == 0) { mech_otp_verify_init(auth_request, str + 10, FALSE); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "unsupported response type"); auth_request_fail(auth_request); otp_unlock(auth_request); } } static void mech_otp_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { if (auth_request->user == NULL) { mech_otp_auth_phase1(auth_request, data, data_size); } else { mech_otp_auth_phase2(auth_request, data, data_size); } } static struct auth_request *mech_otp_auth_new(void) { struct otp_auth_request *request; pool_t pool; otp_lock_init(); pool = pool_alloconly_create(MEMPOOL_GROWING"otp_auth_request", 2048); request = p_new(pool, struct otp_auth_request, 1); request->pool = pool; request->lock = FALSE; request->auth_request.refcount = 1; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_otp = { "OTP", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_SET_CREDENTIALS, mech_otp_auth_new, mech_generic_auth_initial, mech_otp_auth_continue, mech_otp_skey_auth_free }; dovecot-2.2.33.2/src/auth/auth-worker-client.h0000644000175000017500000000113313123174404015747 00000000000000#ifndef AUTH_WORKER_CLIENT_H #define AUTH_WORKER_CLIENT_H #define AUTH_WORKER_PROTOCOL_MAJOR_VERSION 1 #define AUTH_WORKER_PROTOCOL_MINOR_VERSION 0 #define AUTH_WORKER_MAX_LINE_LENGTH 8192 extern struct auth_worker_client *auth_worker_client; struct auth_worker_client *auth_worker_client_create(struct auth *auth, int fd); void auth_worker_client_destroy(struct auth_worker_client **client); void auth_worker_client_unref(struct auth_worker_client **client); void auth_worker_client_send_error(void); void auth_worker_client_send_success(void); void auth_worker_client_send_shutdown(void); #endif dovecot-2.2.33.2/src/auth/db-oauth2.h0000644000175000017500000000240713123174404014015 00000000000000#ifndef DB_OAUTH2_H #define DB_OAUTH2_H 1 struct db_oauth2; struct oauth2_request; struct db_oauth2_request; typedef void db_oauth2_lookup_callback_t(struct db_oauth2_request *request, enum passdb_result result, const char *error, void *context); struct db_oauth2_request { pool_t pool; struct db_oauth2_request *prev,*next; struct db_oauth2 *db; struct oauth2_request *req; /* username to match */ const char *username; /* token to use */ const char *token; struct auth_request *auth_request; struct auth_fields *fields; db_oauth2_lookup_callback_t *callback; void *context; verify_plain_callback_t *verify_callback; }; struct db_oauth2 *db_oauth2_init(const char *config_path); void db_oauth2_ref(struct db_oauth2 *); void db_oauth2_unref(struct db_oauth2 **); void db_oauth2_lookup(struct db_oauth2 *db, struct db_oauth2_request *req, const char *token, struct auth_request *request, db_oauth2_lookup_callback_t *callback, void *context); #define db_oauth2_lookup(db, req, token, request, callback, context) \ db_oauth2_lookup(db, req, token + \ CALLBACK_TYPECHECK(callback, void(*)(struct db_oauth2_request *, enum passdb_result, const char*, typeof(context))), \ request, (db_oauth2_lookup_callback_t*)callback, (void*)context) #endif dovecot-2.2.33.2/src/auth/db-passwd-file.c0000644000175000017500000002610513165463624015040 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE) #include "userdb.h" #include "db-passwd-file.h" #include "array.h" #include "buffer.h" #include "istream.h" #include "hash.h" #include "str.h" #include "eacces-error.h" #include "ioloop.h" #include #include #include #include #define PARSE_TIME_STARTUP_WARN_SECS 60 #define PARSE_TIME_RELOAD_WARN_SECS 10 static struct db_passwd_file *passwd_files; static void ATTR_NULL(3) passwd_file_add(struct passwd_file *pw, const char *username, const char *pass, const char *const *args) { /* args = uid, gid, user info, home dir, shell, extra_fields */ struct passwd_user *pu; const char *extra_fields = NULL; char *user; size_t len; if (hash_table_lookup(pw->users, username) != NULL) { i_error("passwd-file %s: User %s exists more than once", pw->path, username); return; } pu = p_new(pw->pool, struct passwd_user, 1); user = p_strdup(pw->pool, username); len = pass == NULL ? 0 : strlen(pass); if (len > 4 && pass[0] != '{' && pass[0] != '$' && pass[len-1] == ']' && pass[len-4] == '[') { /* password[type] - we're being libpam-pwdfile compatible here. it uses 13 = DES and 34 = MD5. For backwards comaptibility with ourself, we have also 56 = Digest-MD5. */ int num = (pass[len-3] - '0') * 10 + (pass[len-2] - '0'); pass = t_strndup(pass, len-4); if (num == 34) { pu->password = p_strconcat(pw->pool, "{PLAIN-MD5}", pass, NULL); } else if (num == 56) { pu->password = p_strconcat(pw->pool, "{DIGEST-MD5}", pass, NULL); if (strlen(pu->password) != 32 + 12) { i_error("passwd-file %s: User %s " "has invalid password", pw->path, username); return; } } else { pu->password = p_strconcat(pw->pool, "{CRYPT}", pass, NULL); } } else { pu->password = p_strdup(pw->pool, pass); } pu->uid = (uid_t)-1; pu->gid = (gid_t)-1; if (*args == NULL) ; else if (!pw->db->userdb || **args == '\0') { args++; } else { pu->uid = userdb_parse_uid(NULL, *args); if (pu->uid == 0 || pu->uid == (uid_t)-1) { i_error("passwd-file %s: User %s has invalid UID '%s'", pw->path, username, *args); return; } args++; } if (*args == NULL) { if (pw->db->userdb_warn_missing) { i_error("passwd-file %s: User %s is missing " "userdb info", pw->path, username); } /* don't allow userdb lookups */ pu->uid = 0; pu->gid = 0; } else if (!pw->db->userdb || **args == '\0') args++; else { pu->gid = userdb_parse_gid(NULL, *args); if (pu->gid == 0 || pu->gid == (gid_t)-1) { i_error("passwd-file %s: User %s has invalid GID '%s'", pw->path, username, *args); return; } args++; } /* user info */ if (*args != NULL) args++; /* home */ if (*args != NULL) { if (pw->db->userdb) pu->home = p_strdup_empty(pw->pool, *args); args++; } /* shell */ if (*args != NULL) args++; if (*args != NULL && **args == '\0') { /* old format, this field is empty and next field may contain MAIL */ args++; if (*args != NULL && **args != '\0' && pw->db->userdb) { extra_fields = t_strconcat("userdb_mail=", t_strarray_join(args, ":"), NULL); } } else if (*args != NULL) { /* new format, contains a space separated list of extra fields */ extra_fields = t_strarray_join(args, ":"); } if (extra_fields != NULL) { pu->extra_fields = p_strsplit_spaces(pw->pool, extra_fields, " "); } hash_table_insert(pw->users, user, pu); } static struct passwd_file * passwd_file_new(struct db_passwd_file *db, const char *expanded_path) { struct passwd_file *pw; pw = i_new(struct passwd_file, 1); pw->db = db; pw->path = i_strdup(expanded_path); pw->fd = -1; if (hash_table_is_created(db->files)) hash_table_insert(db->files, pw->path, pw); return pw; } static int passwd_file_open(struct passwd_file *pw, bool startup, const char **error_r) { const char *no_args = NULL; struct istream *input; const char *line; struct stat st; time_t start_time, end_time; unsigned int time_secs; int fd; fd = open(pw->path, O_RDONLY); if (fd == -1) { if (errno == EACCES) *error_r = eacces_error_get("open", pw->path); else { *error_r = t_strdup_printf("open(%s) failed: %m", pw->path); } return -1; } if (fstat(fd, &st) != 0) { *error_r = t_strdup_printf("fstat(%s) failed: %m", pw->path); i_close_fd(&fd); return -1; } pw->fd = fd; pw->stamp = st.st_mtime; pw->size = st.st_size; pw->pool = pool_alloconly_create(MEMPOOL_GROWING"passwd_file", 10240); hash_table_create(&pw->users, pw->pool, 0, str_hash, strcmp); start_time = time(NULL); input = i_stream_create_fd(pw->fd, (size_t)-1, FALSE); i_stream_set_return_partial_line(input, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { if (*line == '\0' || *line == ':' || *line == '#') continue; /* no username or comment */ T_BEGIN { const char *const *args = t_strsplit(line, ":"); if (args[1] != NULL) { /* at least username+password */ passwd_file_add(pw, args[0], args[1], args+2); } else { /* only username */ passwd_file_add(pw, args[0], NULL, &no_args); } } T_END; } i_stream_destroy(&input); end_time = time(NULL); time_secs = end_time - start_time; if ((time_secs > PARSE_TIME_STARTUP_WARN_SECS && startup) || (time_secs > PARSE_TIME_RELOAD_WARN_SECS && !startup)) { i_warning("passwd-file %s: Reading %u users took %u secs", pw->path, hash_table_count(pw->users), time_secs); } else if (pw->db->debug) { i_debug("passwd-file %s: Read %u users in %u secs", pw->path, hash_table_count(pw->users), time_secs); } return 0; } static void passwd_file_close(struct passwd_file *pw) { if (pw->fd != -1) { if (close(pw->fd) < 0) i_error("passwd-file %s: close() failed: %m", pw->path); pw->fd = -1; } if (hash_table_is_created(pw->users)) hash_table_destroy(&pw->users); if (pw->pool != NULL) pool_unref(&pw->pool); } static void passwd_file_free(struct passwd_file *pw) { if (hash_table_is_created(pw->db->files)) hash_table_remove(pw->db->files, pw->path); passwd_file_close(pw); i_free(pw->path); i_free(pw); } static int passwd_file_sync(struct auth_request *request, struct passwd_file *pw) { struct stat st; const char *error; if (pw->last_sync_time == ioloop_time) return hash_table_is_created(pw->users) ? 0 : -1; pw->last_sync_time = ioloop_time; if (stat(pw->path, &st) < 0) { /* with variables don't give hard errors, or errors about nonexistent files */ if (errno == EACCES) { auth_request_log_error(request, AUTH_SUBSYS_DB, "%s", eacces_error_get("stat", pw->path)); } else { auth_request_log_error(request, AUTH_SUBSYS_DB, "stat(%s) failed: %m", pw->path); } if (pw->db->default_file != pw) passwd_file_free(pw); return -1; } if (st.st_mtime != pw->stamp || st.st_size != pw->size) { passwd_file_close(pw); if (passwd_file_open(pw, FALSE, &error) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "%s", error); return -1; } } return 0; } static struct db_passwd_file *db_passwd_file_find(const char *path) { struct db_passwd_file *f; for (f = passwd_files; f != NULL; f = f->next) { if (strcmp(f->path, path) == 0) return f; } return NULL; } static void db_passwd_file_set_userdb(struct db_passwd_file *db) { db->userdb = TRUE; /* warn about missing userdb fields only when there aren't any other userdbs. */ db->userdb_warn_missing = array_is_created(&global_auth_settings->userdbs) && array_count(&global_auth_settings->userdbs) == 1; } struct db_passwd_file * db_passwd_file_init(const char *path, bool userdb, bool debug) { struct db_passwd_file *db; const char *p; bool percents = FALSE; db = db_passwd_file_find(path); if (db != NULL) { db->refcount++; if (userdb) db_passwd_file_set_userdb(db); return db; } db = i_new(struct db_passwd_file, 1); db->refcount = 1; if (userdb) db_passwd_file_set_userdb(db); db->debug = debug; for (p = path; *p != '\0'; p++) { if (*p == '%' && p[1] != '\0') { if (var_get_key(++p) == '%') percents = TRUE; else db->vars = TRUE; } } if (percents && !db->vars) { /* just extra escaped % chars. remove them. */ struct var_expand_table empty_table[1]; string_t *dest; empty_table[0].key = '\0'; dest = t_str_new(256); var_expand(dest, path, empty_table); path = str_c(dest); } db->path = i_strdup(path); if (db->vars) { hash_table_create(&db->files, default_pool, 0, str_hash, strcmp); } else { db->default_file = passwd_file_new(db, path); } db->next = passwd_files; passwd_files = db; return db; } void db_passwd_file_parse(struct db_passwd_file *db) { const char *error; if (db->default_file != NULL && db->default_file->stamp == 0) { /* no variables, open the file immediately */ if (passwd_file_open(db->default_file, TRUE, &error) < 0) i_error("passwd-file: %s", error); } } void db_passwd_file_unref(struct db_passwd_file **_db) { struct db_passwd_file *db = *_db; struct db_passwd_file **p; struct hash_iterate_context *iter; char *path; struct passwd_file *file; *_db = NULL; i_assert(db->refcount >= 0); if (--db->refcount > 0) return; for (p = &passwd_files; *p != NULL; p = &(*p)->next) { if (*p == db) { *p = db->next; break; } } if (db->default_file != NULL) passwd_file_free(db->default_file); else { iter = hash_table_iterate_init(db->files); while (hash_table_iterate(iter, db->files, &path, &file)) passwd_file_free(file); hash_table_iterate_deinit(&iter); hash_table_destroy(&db->files); } i_free(db->path); i_free(db); } static const char * path_fix(const char *path, const struct auth_request *auth_request ATTR_UNUSED) { const char *p; p = strchr(path, '/'); if (p == NULL) return path; /* most likely this is an invalid request. just cut off the '/' and everything after it. */ return t_strdup_until(path, p); } int db_passwd_file_lookup(struct db_passwd_file *db, struct auth_request *request, const char *username_format, struct passwd_user **user_r) { struct passwd_file *pw; string_t *username, *dest; if (!db->vars) pw = db->default_file; else { dest = t_str_new(256); auth_request_var_expand(dest, db->path, request, path_fix); pw = hash_table_lookup(db->files, str_c(dest)); if (pw == NULL) { /* doesn't exist yet. create lookup for it. */ pw = passwd_file_new(db, str_c(dest)); } } if (passwd_file_sync(request, pw) < 0) { /* pw may be freed now */ return -1; } username = t_str_new(256); auth_request_var_expand(username, username_format, request, auth_request_str_escape); auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup: user=%s file=%s", str_c(username), pw->path); *user_r = hash_table_lookup(pw->users, str_c(username)); if (*user_r == NULL) { auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); return 0; } return 1; } #endif dovecot-2.2.33.2/src/auth/passdb-checkpassword.c0000644000175000017500000001017313123174404016334 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_CHECKPASSWORD #include "password-scheme.h" #include "db-checkpassword.h" struct checkpassword_passdb_module { struct passdb_module module; struct db_checkpassword *db; }; static void auth_checkpassword_callback(struct auth_request *request, enum db_checkpassword_status status, const char *const *extra_fields, verify_plain_callback_t *callback) { const char *scheme, *crypted_pass = NULL; unsigned int i; switch (status) { case DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE: callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; case DB_CHECKPASSWORD_STATUS_FAILURE: callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; case DB_CHECKPASSWORD_STATUS_OK: break; } for (i = 0; extra_fields[i] != NULL; i++) { if (strncmp(extra_fields[i], "password=", 9) == 0) crypted_pass = extra_fields[i]+9; else if (extra_fields[i][0] != '\0') { auth_request_set_field_keyvalue(request, extra_fields[i], NULL); } } if (crypted_pass != NULL) { /* for cache */ scheme = password_get_scheme(&crypted_pass); if (scheme != NULL) { auth_request_set_field(request, "password", crypted_pass, scheme); } else { auth_request_log_error(request, AUTH_SUBSYS_DB, "password field returned without {scheme} prefix"); } } callback(PASSDB_RESULT_OK, request); } static void checkpassword_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct checkpassword_passdb_module *module = (struct checkpassword_passdb_module *)_module; db_checkpassword_call(module->db, request, password, auth_checkpassword_callback, callback); } static void credentials_checkpassword_callback(struct auth_request *request, enum db_checkpassword_status status, const char *const *extra_fields, lookup_credentials_callback_t *callback) { const char *scheme, *crypted_pass = NULL; unsigned int i; switch (status) { case DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE: callback(PASSDB_RESULT_INTERNAL_FAILURE, NULL, 0, request); return; case DB_CHECKPASSWORD_STATUS_FAILURE: callback(PASSDB_RESULT_USER_UNKNOWN, NULL, 0, request); return; case DB_CHECKPASSWORD_STATUS_OK: break; } for (i = 0; extra_fields[i] != NULL; i++) { if (strncmp(extra_fields[i], "password=", 9) == 0) crypted_pass = extra_fields[i]+9; else if (extra_fields[i][0] != '\0') { auth_request_set_field_keyvalue(request, extra_fields[i], NULL); } } scheme = password_get_scheme(&crypted_pass); if (scheme == NULL) scheme = request->credentials_scheme; passdb_handle_credentials(PASSDB_RESULT_OK, crypted_pass, scheme, callback, request); } static void checkpassword_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct checkpassword_passdb_module *module = (struct checkpassword_passdb_module *)_module; db_checkpassword_call(module->db, request, NULL, credentials_checkpassword_callback, callback); } static struct passdb_module * checkpassword_preinit(pool_t pool, const char *args) { struct checkpassword_passdb_module *module; const char *checkpassword_path = args; const char *checkpassword_reply_path = PKG_LIBEXECDIR"/checkpassword-reply"; module = p_new(pool, struct checkpassword_passdb_module, 1); module->db = db_checkpassword_init(checkpassword_path, checkpassword_reply_path); return &module->module; } static void checkpassword_deinit(struct passdb_module *_module) { struct checkpassword_passdb_module *module = (struct checkpassword_passdb_module *)_module; db_checkpassword_deinit(&module->db); } struct passdb_module_interface passdb_checkpassword = { "checkpassword", checkpassword_preinit, NULL, checkpassword_deinit, checkpassword_verify_plain, checkpassword_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_checkpassword = { .name = "checkpassword" }; #endif dovecot-2.2.33.2/src/auth/userdb-template.c0000644000175000017500000000523513165463624015335 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "str.h" #include "userdb.h" #include "userdb-template.h" struct userdb_template { ARRAY(const char *) args; }; struct userdb_template * userdb_template_build(pool_t pool, const char *userdb_name, const char *args) { struct userdb_template *tmpl; const char *const *tmp, *key, *value, *nonull_value; uid_t uid; gid_t gid; tmpl = p_new(pool, struct userdb_template, 1); tmp = t_strsplit_spaces(args, " "); p_array_init(&tmpl->args, pool, str_array_length(tmp)); for (; *tmp != NULL; tmp++) { value = strchr(*tmp, '='); if (value == NULL) key = *tmp; else key = t_strdup_until(*tmp, value++); nonull_value = value == NULL ? "" : value; if (strcasecmp(key, "uid") == 0) { uid = userdb_parse_uid(NULL, nonull_value); if (uid == (uid_t)-1) { i_fatal("%s userdb: Invalid uid: %s", userdb_name, nonull_value); } value = dec2str(uid); } else if (strcasecmp(key, "gid") == 0) { gid = userdb_parse_gid(NULL, nonull_value); if (gid == (gid_t)-1) { i_fatal("%s userdb: Invalid gid: %s", userdb_name, nonull_value); } value = dec2str(gid); } else if (*key == '\0') { i_fatal("%s userdb: Empty key (=%s)", userdb_name, nonull_value); } key = p_strdup(pool, key); value = p_strdup(pool, value); array_append(&tmpl->args, &key, 1); array_append(&tmpl->args, &value, 1); } return tmpl; } void userdb_template_export(struct userdb_template *tmpl, struct auth_request *auth_request) { const struct var_expand_table *table; string_t *str; const char *const *args, *value; unsigned int i, count; if (userdb_template_is_empty(tmpl)) return; str = t_str_new(256); table = auth_request_get_var_expand_table(auth_request, NULL); args = array_get(&tmpl->args, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (args[i+1] == NULL) value = ""; else { str_truncate(str, 0); auth_request_var_expand_with_table(str, args[i+1], auth_request, table, NULL); value = str_c(str); } auth_request_set_userdb_field(auth_request, args[i], value); } } bool userdb_template_remove(struct userdb_template *tmpl, const char *key, const char **value_r) { const char *const *args; unsigned int i, count; args = array_get(&tmpl->args, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (strcmp(args[i], key) == 0) { *value_r = args[i+1]; array_delete(&tmpl->args, i, 2); return TRUE; } } return FALSE; } bool userdb_template_is_empty(struct userdb_template *tmpl) { return array_count(&tmpl->args) == 0; } dovecot-2.2.33.2/src/auth/auth-settings.h0000644000175000017500000000477313165463624015052 00000000000000#ifndef AUTH_SETTINGS_H #define AUTH_SETTINGS_H struct master_service; struct master_service_settings_output; struct auth_passdb_settings { const char *name; const char *driver; const char *args; const char *default_fields; const char *override_fields; const char *mechanisms; const char *username_filter; const char *skip; const char *result_success; const char *result_failure; const char *result_internalfail; bool deny; bool pass; /* deprecated, use result_success=continue instead */ bool master; const char *auth_verbose; }; struct auth_userdb_settings { const char *name; const char *driver; const char *args; const char *default_fields; const char *override_fields; const char *skip; const char *result_success; const char *result_failure; const char *result_internalfail; const char *auth_verbose; }; struct auth_settings { const char *mechanisms; const char *realms; const char *default_realm; uoff_t cache_size; unsigned int cache_ttl; unsigned int cache_negative_ttl; const char *username_chars; const char *username_translation; const char *username_format; const char *master_user_separator; const char *anonymous_username; const char *krb5_keytab; const char *gssapi_hostname; const char *winbind_helper_path; const char *proxy_self; unsigned int failure_delay; const char *policy_server_url; const char *policy_server_api_header; unsigned int policy_server_timeout_msecs; const char *policy_hash_mech; const char *policy_hash_nonce; const char *policy_request_attributes; bool policy_reject_on_fail; unsigned int policy_hash_truncate; bool stats; bool verbose, debug, debug_passwords; const char *verbose_passwords; bool ssl_require_client_cert; bool ssl_username_from_cert; bool use_winbind; unsigned int worker_max_count; /* settings that don't have auth_ prefix: */ ARRAY(struct auth_passdb_settings *) passdbs; ARRAY(struct auth_userdb_settings *) userdbs; const char *base_dir; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; bool verbose_proctitle; unsigned int first_valid_uid; unsigned int last_valid_uid; /* generated: */ char username_chars_map[256]; char username_translation_map[256]; const char *const *realms_arr; const struct ip_addr *proxy_self_ips; }; extern const struct setting_parser_info auth_setting_parser_info; extern struct auth_settings *global_auth_settings; struct auth_settings * auth_settings_read(const char *service, pool_t pool, struct master_service_settings_output *output_r) ATTR_NULL(1); #endif dovecot-2.2.33.2/src/auth/userdb-passwd.c0000644000175000017500000001450713165463624015025 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_PASSWD #include "ioloop.h" #include "ipwd.h" #include "time-util.h" #include "userdb-template.h" #define USER_CACHE_KEY "%u" #define PASSWD_SLOW_WARN_MSECS (10*1000) #define PASSWD_SLOW_MASTER_WARN_MSECS 50 #define PASSDB_SLOW_MASTER_WARN_COUNT_INTERVAL 100 #define PASSDB_SLOW_MASTER_WARN_MIN_PERCENTAGE 5 struct passwd_userdb_module { struct userdb_module module; struct userdb_template *tmpl; unsigned int fast_count, slow_count; unsigned int slow_warned:1; }; struct passwd_userdb_iterate_context { struct userdb_iterate_context ctx; struct passwd_userdb_iterate_context *next_waiting; }; static struct passwd_userdb_iterate_context *cur_userdb_iter = NULL; static struct timeout *cur_userdb_iter_to = NULL; static void passwd_check_warnings(struct auth_request *auth_request, struct passwd_userdb_module *module, const struct timeval *start_tv) { struct timeval end_tv; unsigned int msecs, percentage; if (gettimeofday(&end_tv, NULL) < 0) return; msecs = timeval_diff_msecs(&end_tv, start_tv); if (msecs >= PASSWD_SLOW_WARN_MSECS) { i_warning("passwd: Lookup for %s took %u secs", auth_request->user, msecs/1000); return; } if (worker || module->slow_warned) return; if (msecs < PASSWD_SLOW_MASTER_WARN_MSECS) { module->fast_count++; return; } module->slow_count++; if (module->fast_count + module->slow_count < PASSDB_SLOW_MASTER_WARN_COUNT_INTERVAL) return; percentage = module->slow_count * 100 / (module->slow_count + module->fast_count); if (percentage < PASSDB_SLOW_MASTER_WARN_MIN_PERCENTAGE) { /* start from beginning */ module->slow_count = module->fast_count = 0; } else { i_warning("passwd: %u%% of last %u lookups took over " "%u milliseconds, " "you may want to set blocking=yes for userdb", percentage, PASSDB_SLOW_MASTER_WARN_COUNT_INTERVAL, PASSWD_SLOW_MASTER_WARN_MSECS); module->slow_warned = TRUE; } } static void passwd_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct passwd_userdb_module *module = (struct passwd_userdb_module *)_module; struct passwd pw; struct timeval start_tv; int ret; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "lookup"); if (gettimeofday(&start_tv, NULL) < 0) start_tv.tv_sec = 0; ret = i_getpwnam(auth_request->user, &pw); if (start_tv.tv_sec != 0) passwd_check_warnings(auth_request, module, &start_tv); switch (ret) { case -1: auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "getpwnam() failed: %m"); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; case 0: auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); callback(USERDB_RESULT_USER_UNKNOWN, auth_request); return; } auth_request_set_field(auth_request, "user", pw.pw_name, NULL); auth_request_set_userdb_field(auth_request, "system_groups_user", pw.pw_name); auth_request_set_userdb_field(auth_request, "uid", dec2str(pw.pw_uid)); auth_request_set_userdb_field(auth_request, "gid", dec2str(pw.pw_gid)); auth_request_set_userdb_field(auth_request, "home", pw.pw_dir); userdb_template_export(module->tmpl, auth_request); callback(USERDB_RESULT_OK, auth_request); } static struct userdb_iterate_context * passwd_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct passwd_userdb_iterate_context *ctx; ctx = i_new(struct passwd_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; setpwent(); if (cur_userdb_iter == NULL) cur_userdb_iter = ctx; return &ctx->ctx; } static bool passwd_iterate_want_pw(struct passwd *pw, const struct auth_settings *set) { /* skip entries not in valid UID range. they're users for daemons and such. */ if (pw->pw_uid < (uid_t)set->first_valid_uid) return FALSE; if (pw->pw_uid > (uid_t)set->last_valid_uid && set->last_valid_uid != 0) return FALSE; return TRUE; } static void passwd_iterate_next(struct userdb_iterate_context *_ctx) { struct passwd_userdb_iterate_context *ctx = (struct passwd_userdb_iterate_context *)_ctx; const struct auth_settings *set = _ctx->auth_request->set; struct passwd *pw; if (cur_userdb_iter != NULL && cur_userdb_iter != ctx) { /* we can't support concurrent userdb iteration. wait until the previous one is done */ ctx->next_waiting = cur_userdb_iter->next_waiting; cur_userdb_iter->next_waiting = ctx; return; } errno = 0; while ((pw = getpwent()) != NULL) { if (passwd_iterate_want_pw(pw, set)) { _ctx->callback(pw->pw_name, _ctx->context); return; } } if (errno != 0) { i_error("getpwent() failed: %m"); _ctx->failed = TRUE; } _ctx->callback(NULL, _ctx->context); } static void ATTR_NULL(1) passwd_iterate_next_timeout(void *context ATTR_UNUSED) { timeout_remove(&cur_userdb_iter_to); passwd_iterate_next(&cur_userdb_iter->ctx); } static int passwd_iterate_deinit(struct userdb_iterate_context *_ctx) { struct passwd_userdb_iterate_context *ctx = (struct passwd_userdb_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; cur_userdb_iter = ctx->next_waiting; i_free(ctx); if (cur_userdb_iter != NULL) { cur_userdb_iter_to = timeout_add(0, passwd_iterate_next_timeout, (void *)NULL); } return ret; } static struct userdb_module * passwd_passwd_preinit(pool_t pool, const char *args) { struct passwd_userdb_module *module; const char *value; module = p_new(pool, struct passwd_userdb_module, 1); module->module.default_cache_key = USER_CACHE_KEY; module->tmpl = userdb_template_build(pool, "passwd", args); module->module.blocking = TRUE; if (userdb_template_remove(module->tmpl, "blocking", &value)) module->module.blocking = strcasecmp(value, "yes") == 0; /* FIXME: backwards compatibility */ if (!userdb_template_is_empty(module->tmpl)) i_warning("userdb passwd: Move templates args to override_fields setting"); return &module->module; } struct userdb_module_interface userdb_passwd = { "passwd", passwd_passwd_preinit, NULL, NULL, passwd_lookup, passwd_iterate_init, passwd_iterate_next, passwd_iterate_deinit }; #else struct userdb_module_interface userdb_passwd = { .name = "passwd" }; #endif dovecot-2.2.33.2/src/auth/auth-penalty.h0000644000175000017500000000163513123174404014645 00000000000000#ifndef AUTH_PENALTY_H #define AUTH_PENALTY_H struct auth_request; #define AUTH_PENALTY_INIT_SECS 2 #define AUTH_PENALTY_MAX_SECS 15 /* timeout specifies how long it takes for penalty to be irrelevant. */ #define AUTH_PENALTY_TIMEOUT \ (AUTH_PENALTY_INIT_SECS + 4 + 8 + AUTH_PENALTY_MAX_SECS) #define AUTH_PENALTY_MAX_PENALTY 4 /* If lookup failed, penalty and last_update are both zero */ typedef void auth_penalty_callback_t(unsigned int penalty, struct auth_request *request); struct auth_penalty *auth_penalty_init(const char *path); void auth_penalty_deinit(struct auth_penalty **penalty); unsigned int auth_penalty_to_secs(unsigned int penalty); void auth_penalty_lookup(struct auth_penalty *penalty, struct auth_request *auth_request, auth_penalty_callback_t *callback); void auth_penalty_update(struct auth_penalty *penalty, struct auth_request *auth_request, unsigned int value); #endif dovecot-2.2.33.2/src/auth/auth-postfix-connection.h0000644000175000017500000000033413123174404017015 00000000000000#ifndef AUTH_POSTFIX_CONNECTION_H #define AUTH_POSTFIX_CONNECTION_H struct auth_postfix_connection * auth_postfix_connection_create(struct auth *auth, int fd); void auth_postfix_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/auth/passdb-passwd.c0000644000175000017500000000613113123174404014774 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_PASSWD #include "safe-memset.h" #include "ipwd.h" #define PASSWD_CACHE_KEY "%u" #define PASSWD_PASS_SCHEME "CRYPT" static enum passdb_result passwd_lookup(struct auth_request *request, struct passwd *pw_r) { auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); switch (i_getpwnam(request->user, pw_r)) { case -1: auth_request_log_error(request, AUTH_SUBSYS_DB, "getpwnam() failed: %m"); return PASSDB_RESULT_INTERNAL_FAILURE; case 0: auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); return PASSDB_RESULT_USER_UNKNOWN; } if (!IS_VALID_PASSWD(pw_r->pw_passwd)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "invalid password field '%s'", pw_r->pw_passwd); return PASSDB_RESULT_USER_DISABLED; } /* save the password so cache can use it */ auth_request_set_field(request, "password", pw_r->pw_passwd, PASSWD_PASS_SCHEME); return PASSDB_RESULT_OK; } static void passwd_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passwd pw; enum passdb_result res; int ret; res = passwd_lookup(request, &pw); if (res != PASSDB_RESULT_OK) { callback(res, request); return; } /* check if the password is valid */ ret = auth_request_password_verify(request, password, pw.pw_passwd, PASSWD_PASS_SCHEME, AUTH_SUBSYS_DB); /* clear the passwords from memory */ safe_memset(pw.pw_passwd, 0, strlen(pw.pw_passwd)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); callback(PASSDB_RESULT_OK, request); } static void passwd_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passwd pw; enum passdb_result res; res = passwd_lookup(request, &pw); if (res != PASSDB_RESULT_OK) { callback(res, NULL, 0, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); passdb_handle_credentials(PASSDB_RESULT_OK, pw.pw_passwd, PASSWD_PASS_SCHEME, callback, request); } static struct passdb_module * passwd_preinit(pool_t pool, const char *args) { struct passdb_module *module; module = p_new(pool, struct passdb_module, 1); module->blocking = TRUE; if (strcmp(args, "blocking=no") == 0) module->blocking = FALSE; else if (*args != '\0') i_fatal("passdb passwd: Unknown setting: %s", args); module->default_cache_key = PASSWD_CACHE_KEY; module->default_pass_scheme = PASSWD_PASS_SCHEME; return module; } static void passwd_deinit(struct passdb_module *module ATTR_UNUSED) { endpwent(); } struct passdb_module_interface passdb_passwd = { "passwd", passwd_preinit, NULL, passwd_deinit, passwd_verify_plain, passwd_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_passwd = { .name = "passwd" }; #endif dovecot-2.2.33.2/src/auth/db-dict.c0000644000175000017500000004110513165463624013542 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "istream.h" #include "str.h" #include "json-parser.h" #include "settings.h" #include "dict.h" #include "auth-request.h" #include "auth-worker-client.h" #include "db-dict.h" #include enum dict_settings_section { DICT_SETTINGS_SECTION_ROOT = 0, DICT_SETTINGS_SECTION_KEY, DICT_SETTINGS_SECTION_PASSDB, DICT_SETTINGS_SECTION_USERDB }; struct dict_settings_parser_ctx { struct dict_connection *conn; enum dict_settings_section section; struct db_dict_key *cur_key; }; struct db_dict_iter_key { const struct db_dict_key *key; bool used; const char *value; }; struct db_dict_value_iter { pool_t pool; struct auth_request *auth_request; struct dict_connection *conn; const struct var_expand_table *var_expand_table; ARRAY(struct db_dict_iter_key) keys; const ARRAY_TYPE(db_dict_field) *fields; const ARRAY_TYPE(db_dict_key_p) *objects; unsigned int field_idx; unsigned int object_idx; struct json_parser *json_parser; string_t *tmpstr; const char *error; }; #define DEF_STR(name) DEF_STRUCT_STR(name, db_dict_settings) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, db_dict_settings) static struct setting_def setting_defs[] = { DEF_STR(uri), DEF_STR(default_pass_scheme), DEF_STR(iterate_prefix), DEF_BOOL(iterate_disable), DEF_STR(passdb_objects), DEF_STR(userdb_objects), { 0, NULL, 0 } }; static struct db_dict_settings default_dict_settings = { .uri = NULL, .default_pass_scheme = "MD5", .iterate_prefix = "", .iterate_disable = FALSE, .passdb_objects = "", .userdb_objects = "" }; #undef DEF_STR #define DEF_STR(name) DEF_STRUCT_STR(name, db_dict_key) static struct setting_def key_setting_defs[] = { DEF_STR(name), DEF_STR(key), DEF_STR(format), DEF_STR(default_value), { 0, NULL, 0 } }; static struct db_dict_key default_key_settings = { .name = NULL, .key = "", .format = "value", .default_value = NULL }; static struct dict_connection *connections = NULL; static struct dict_connection *dict_conn_find(const char *config_path) { struct dict_connection *conn; for (conn = connections; conn != NULL; conn = conn->next) { if (strcmp(conn->config_path, config_path) == 0) return conn; } return NULL; } static bool parse_obsolete_setting(const char *key, const char *value, struct dict_settings_parser_ctx *ctx, const char **error_r) { const struct db_dict_key *dbkey; if (strcmp(key, "password_key") == 0) { /* key passdb { key= format=json } passdb_objects = passdb */ ctx->cur_key = array_append_space(&ctx->conn->set.keys); *ctx->cur_key = default_key_settings; ctx->cur_key->name = "passdb"; ctx->cur_key->format = "json"; ctx->cur_key->parsed_format = DB_DICT_VALUE_FORMAT_JSON; ctx->cur_key->key = p_strdup(ctx->conn->pool, value); dbkey = ctx->cur_key; array_append(&ctx->conn->set.parsed_passdb_objects, &dbkey, 1); return TRUE; } if (strcmp(key, "user_key") == 0) { /* key userdb { key= format=json } userdb_objects = userdb */ ctx->cur_key = array_append_space(&ctx->conn->set.keys); *ctx->cur_key = default_key_settings; ctx->cur_key->name = "userdb"; ctx->cur_key->format = "json"; ctx->cur_key->parsed_format = DB_DICT_VALUE_FORMAT_JSON; ctx->cur_key->key = p_strdup(ctx->conn->pool, value); dbkey = ctx->cur_key; array_append(&ctx->conn->set.parsed_userdb_objects, &dbkey, 1); return TRUE; } if (strcmp(key, "value_format") == 0) { if (strcmp(value, "json") == 0) return TRUE; *error_r = "Deprecated value_format must be 'json'"; return FALSE; } return FALSE; } static const char *parse_setting(const char *key, const char *value, struct dict_settings_parser_ctx *ctx) { struct db_dict_field *field; const char *error = NULL; switch (ctx->section) { case DICT_SETTINGS_SECTION_ROOT: if (parse_obsolete_setting(key, value, ctx, &error)) return NULL; if (error != NULL) return error; return parse_setting_from_defs(ctx->conn->pool, setting_defs, &ctx->conn->set, key, value); case DICT_SETTINGS_SECTION_KEY: return parse_setting_from_defs(ctx->conn->pool, key_setting_defs, ctx->cur_key, key, value); case DICT_SETTINGS_SECTION_PASSDB: field = array_append_space(&ctx->conn->set.passdb_fields); field->name = p_strdup(ctx->conn->pool, key); field->value = p_strdup(ctx->conn->pool, value); return NULL; case DICT_SETTINGS_SECTION_USERDB: field = array_append_space(&ctx->conn->set.userdb_fields); field->name = p_strdup(ctx->conn->pool, key); field->value = p_strdup(ctx->conn->pool, value); return NULL; } return t_strconcat("Unknown setting: ", key, NULL); } static bool parse_section(const char *type, const char *name, struct dict_settings_parser_ctx *ctx, const char **errormsg) { if (type == NULL) { ctx->section = DICT_SETTINGS_SECTION_ROOT; if (ctx->cur_key != NULL) { if (strcmp(ctx->cur_key->format, "value") == 0) { ctx->cur_key->parsed_format = DB_DICT_VALUE_FORMAT_VALUE; } else if (strcmp(ctx->cur_key->format, "json") == 0) { ctx->cur_key->parsed_format = DB_DICT_VALUE_FORMAT_JSON; } else { *errormsg = t_strconcat("Unknown key format: ", ctx->cur_key->format, NULL); return FALSE; } } ctx->cur_key = NULL; return TRUE; } if (ctx->section != DICT_SETTINGS_SECTION_ROOT) { *errormsg = "Nested sections not supported"; return FALSE; } if (strcmp(type, "key") == 0) { if (name == NULL) { *errormsg = "Key section is missing name"; return FALSE; } if (strchr(name, '.') != NULL) { *errormsg = "Key section names must not contain '.'"; return FALSE; } ctx->section = DICT_SETTINGS_SECTION_KEY; ctx->cur_key = array_append_space(&ctx->conn->set.keys); *ctx->cur_key = default_key_settings; ctx->cur_key->name = p_strdup(ctx->conn->pool, name); return TRUE; } if (strcmp(type, "passdb_fields") == 0) { ctx->section = DICT_SETTINGS_SECTION_PASSDB; return TRUE; } if (strcmp(type, "userdb_fields") == 0) { ctx->section = DICT_SETTINGS_SECTION_USERDB; return TRUE; } *errormsg = "Unknown section"; return FALSE; } static void db_dict_settings_parse(struct db_dict_settings *set) { const struct db_dict_key *key; const char *const *tmp; tmp = t_strsplit_spaces(set->passdb_objects, " "); for (; *tmp != NULL; tmp++) { key = db_dict_set_key_find(&set->keys, *tmp); if (key == NULL) { i_fatal("dict: passdb_objects refers to key %s, " "which doesn't exist", *tmp); } if (key->parsed_format == DB_DICT_VALUE_FORMAT_VALUE) { i_fatal("dict: passdb_objects refers to key %s, " "but it's in value-only format", *tmp); } array_append(&set->parsed_passdb_objects, &key, 1); } tmp = t_strsplit_spaces(set->userdb_objects, " "); for (; *tmp != NULL; tmp++) { key = db_dict_set_key_find(&set->keys, *tmp); if (key == NULL) { i_fatal("dict: userdb_objects refers to key %s, " "which doesn't exist", *tmp); } if (key->parsed_format == DB_DICT_VALUE_FORMAT_VALUE) { i_fatal("dict: userdb_objects refers to key %s, " "but it's in value-only format", *tmp); } array_append(&set->parsed_userdb_objects, &key, 1); } } struct dict_connection *db_dict_init(const char *config_path) { struct dict_settings_parser_ctx ctx; struct dict_connection *conn; const char *error; pool_t pool; conn = dict_conn_find(config_path); if (conn != NULL) { conn->refcount++; return conn; } if (*config_path == '\0') i_fatal("dict: Configuration file path not given"); pool = pool_alloconly_create("dict_connection", 1024); conn = p_new(pool, struct dict_connection, 1); conn->pool = pool; conn->refcount = 1; conn->config_path = p_strdup(pool, config_path); conn->set = default_dict_settings; p_array_init(&conn->set.keys, pool, 8); p_array_init(&conn->set.passdb_fields, pool, 8); p_array_init(&conn->set.userdb_fields, pool, 8); p_array_init(&conn->set.parsed_passdb_objects, pool, 2); p_array_init(&conn->set.parsed_userdb_objects, pool, 2); i_zero(&ctx); ctx.conn = conn; if (!settings_read(config_path, NULL, parse_setting, parse_section, &ctx, &error)) i_fatal("dict %s: %s", config_path, error); db_dict_settings_parse(&conn->set); if (conn->set.uri == NULL) i_fatal("dict %s: Empty uri setting", config_path); if (dict_init(conn->set.uri, DICT_DATA_TYPE_STRING, "", global_auth_settings->base_dir, &conn->dict, &error) < 0) i_fatal("dict %s: Failed to init dict: %s", config_path, error); conn->next = connections; connections = conn; return conn; } void db_dict_unref(struct dict_connection **_conn) { struct dict_connection *conn = *_conn; *_conn = NULL; if (--conn->refcount > 0) return; dict_deinit(&conn->dict); pool_unref(&conn->pool); } static struct db_dict_iter_key * db_dict_iter_find_key(struct db_dict_value_iter *iter, const char *name) { struct db_dict_iter_key *key; array_foreach_modifiable(&iter->keys, key) { if (strcmp(key->key->name, name) == 0) return key; } return NULL; } static void db_dict_iter_find_used_keys(struct db_dict_value_iter *iter) { const struct db_dict_field *field; struct db_dict_iter_key *key; const char *p, *name; unsigned int idx, size; array_foreach(iter->fields, field) { for (p = field->value; *p != '\0'; ) { if (*p != '%') { p++; continue; } var_get_key_range(++p, &idx, &size); if (size == 0) { /* broken %variable ending too early */ break; } p += idx; if (size > 5 && memcmp(p, "dict:", 5) == 0) { name = t_strcut(t_strndup(p+5, size-5), ':'); key = db_dict_iter_find_key(iter, name); if (key != NULL) key->used = TRUE; } p += size; } } } static void db_dict_iter_find_used_objects(struct db_dict_value_iter *iter) { const struct db_dict_key *const *keyp; struct db_dict_iter_key *key; array_foreach(iter->objects, keyp) { key = db_dict_iter_find_key(iter, (*keyp)->name); i_assert(key != NULL); /* checked at init */ i_assert(key->key->parsed_format != DB_DICT_VALUE_FORMAT_VALUE); key->used = TRUE; } } static int db_dict_iter_key_cmp(const struct db_dict_iter_key *k1, const struct db_dict_iter_key *k2) { return null_strcmp(k1->key->default_value, k2->key->default_value); } static int db_dict_iter_lookup_key_values(struct db_dict_value_iter *iter) { struct db_dict_iter_key *key; string_t *path; int ret; /* sort the keys so that we'll first lookup the keys without default value. if their lookup fails, the user doesn't exist. */ array_sort(&iter->keys, db_dict_iter_key_cmp); path = t_str_new(128); str_append(path, DICT_PATH_SHARED); array_foreach_modifiable(&iter->keys, key) { if (!key->used) continue; str_truncate(path, strlen(DICT_PATH_SHARED)); str_append(path, key->key->key); ret = dict_lookup(iter->conn->dict, iter->pool, str_c(path), &key->value); if (ret > 0) { auth_request_log_debug(iter->auth_request, AUTH_SUBSYS_DB, "Lookup: %s = %s", str_c(path), key->value); } else if (ret < 0) { auth_request_log_error(iter->auth_request, AUTH_SUBSYS_DB, "Failed to lookup key %s", str_c(path)); return -1; } else if (key->key->default_value != NULL) { auth_request_log_debug(iter->auth_request, AUTH_SUBSYS_DB, "Lookup: %s not found, using default value %s", str_c(path), key->key->default_value); key->value = key->key->default_value; } else { return 0; } } return 1; } int db_dict_value_iter_init(struct dict_connection *conn, struct auth_request *auth_request, const ARRAY_TYPE(db_dict_field) *fields, const ARRAY_TYPE(db_dict_key_p) *objects, struct db_dict_value_iter **iter_r) { struct db_dict_value_iter *iter; struct db_dict_iter_key *iterkey; const struct db_dict_key *key; pool_t pool; int ret; pool = pool_alloconly_create(MEMPOOL_GROWING"auth dict lookup", 1024); iter = p_new(pool, struct db_dict_value_iter, 1); iter->pool = pool; iter->conn = conn; iter->fields = fields; iter->objects = objects; iter->tmpstr = str_new(pool, 128); iter->auth_request = auth_request; iter->var_expand_table = auth_request_get_var_expand_table(auth_request, NULL); /* figure out what keys we need to lookup, and lookup them */ p_array_init(&iter->keys, pool, array_count(&conn->set.keys)); array_foreach(&conn->set.keys, key) { iterkey = array_append_space(&iter->keys); struct db_dict_key *new_key = p_new(iter->pool, struct db_dict_key, 1); memcpy(new_key, key, sizeof(struct db_dict_key)); string_t *expanded_key = str_new(iter->pool, strlen(key->key)); auth_request_var_expand_with_table(expanded_key, key->key, auth_request, iter->var_expand_table, NULL); new_key->key = str_c(expanded_key); iterkey->key = new_key; } T_BEGIN { db_dict_iter_find_used_keys(iter); db_dict_iter_find_used_objects(iter); ret = db_dict_iter_lookup_key_values(iter); } T_END; if (ret <= 0) { pool_unref(&pool); return ret; } *iter_r = iter; return 1; } static bool db_dict_value_iter_json_next(struct db_dict_value_iter *iter, string_t *tmpstr, const char **key_r, const char **value_r) { enum json_type type; const char *value; if (json_parse_next(iter->json_parser, &type, &value) < 0) return FALSE; if (type != JSON_TYPE_OBJECT_KEY) { iter->error = "Object expected"; return FALSE; } if (*value == '\0') { iter->error = "Empty object key"; return FALSE; } str_truncate(tmpstr, 0); str_append(tmpstr, value); if (json_parse_next(iter->json_parser, &type, &value) < 0) { iter->error = "Missing value"; return FALSE; } if (type == JSON_TYPE_OBJECT) { iter->error = "Nested objects not supported"; return FALSE; } *key_r = str_c(tmpstr); *value_r = value; return TRUE; } static void db_dict_value_iter_json_init(struct db_dict_value_iter *iter, const char *data) { struct istream *input; i_assert(iter->json_parser == NULL); input = i_stream_create_from_data(data, strlen(data)); iter->json_parser = json_parser_init(input); i_stream_unref(&input); } static bool db_dict_value_iter_object_next(struct db_dict_value_iter *iter, const char **key_r, const char **value_r) { const struct db_dict_key *const *keyp; struct db_dict_iter_key *key; if (iter->json_parser != NULL) return db_dict_value_iter_json_next(iter, iter->tmpstr, key_r, value_r); if (iter->object_idx == array_count(iter->objects)) return FALSE; keyp = array_idx(iter->objects, iter->object_idx); key = db_dict_iter_find_key(iter, (*keyp)->name); i_assert(key != NULL); /* checked at init */ switch (key->key->parsed_format) { case DB_DICT_VALUE_FORMAT_VALUE: i_unreached(); case DB_DICT_VALUE_FORMAT_JSON: db_dict_value_iter_json_init(iter, key->value); return db_dict_value_iter_json_next(iter, iter->tmpstr, key_r, value_r); } i_unreached(); } static const char * db_dict_field_find(const char *data, void *context) { struct db_dict_value_iter *iter = context; struct db_dict_iter_key *key; const char *name, *value, *ret, *dotname = strchr(data, '.'); string_t *tmpstr; if (dotname != NULL) data = t_strdup_until(data, dotname++); key = db_dict_iter_find_key(iter, data); if (key == NULL) return NULL; switch (key->key->parsed_format) { case DB_DICT_VALUE_FORMAT_VALUE: return dotname != NULL ? NULL : (key->value == NULL ? "" : key->value); case DB_DICT_VALUE_FORMAT_JSON: if (dotname == NULL) return NULL; db_dict_value_iter_json_init(iter, key->value); ret = ""; tmpstr = t_str_new(64); while (db_dict_value_iter_json_next(iter, tmpstr, &name, &value)) { if (strcmp(name, dotname) == 0) { ret = t_strdup(value); break; } } (void)json_parser_deinit(&iter->json_parser, &iter->error); return ret; } i_unreached(); } bool db_dict_value_iter_next(struct db_dict_value_iter *iter, const char **key_r, const char **value_r) { static struct var_expand_func_table var_funcs_table[] = { { "dict", db_dict_field_find }, { NULL, NULL } }; const struct db_dict_field *field; if (iter->field_idx == array_count(iter->fields)) return db_dict_value_iter_object_next(iter, key_r, value_r); field = array_idx(iter->fields, iter->field_idx++); str_truncate(iter->tmpstr, 0); var_expand_with_funcs(iter->tmpstr, field->value, iter->var_expand_table, var_funcs_table, iter); *key_r = field->name; *value_r = str_c(iter->tmpstr); return TRUE; } int db_dict_value_iter_deinit(struct db_dict_value_iter **_iter, const char **error_r) { struct db_dict_value_iter *iter = *_iter; *_iter = NULL; *error_r = iter->error; if (iter->json_parser != NULL) { if (json_parser_deinit(&iter->json_parser, &iter->error) < 0 && *error_r == NULL) *error_r = iter->error; } pool_unref(&iter->pool); return *error_r != NULL ? -1 : 0; } dovecot-2.2.33.2/src/auth/mech-otp-skey-common.c0000644000175000017500000000256113165463624016212 00000000000000/* * Common code for OTP and SKEY authentication mechanisms. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "hash.h" #include "mech.h" #include "otp.h" #include "mech-otp-skey-common.h" static HASH_TABLE(char *, struct auth_request *) otp_lock_table; void otp_lock_init(void) { if (hash_table_is_created(otp_lock_table)) return; hash_table_create(&otp_lock_table, default_pool, 128, strcase_hash, strcasecmp); } int otp_try_lock(struct auth_request *auth_request) { if (hash_table_lookup(otp_lock_table, auth_request->user)) return FALSE; hash_table_insert(otp_lock_table, auth_request->user, auth_request); return TRUE; } void otp_unlock(struct auth_request *auth_request) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; if (!request->lock) return; hash_table_remove(otp_lock_table, auth_request->user); request->lock = FALSE; } void otp_set_credentials_callback(bool success, struct auth_request *auth_request) { if (success) auth_request_success(auth_request, "", 0); else { auth_request_internal_failure(auth_request); otp_unlock(auth_request); } otp_unlock(auth_request); } void mech_otp_skey_auth_free(struct auth_request *auth_request) { otp_unlock(auth_request); pool_unref(&auth_request->pool); } dovecot-2.2.33.2/src/auth/passdb-cache.c0000644000175000017500000001060313165463624014550 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "strescape.h" #include "restrict-process-size.h" #include "auth-request-stats.h" #include "password-scheme.h" #include "passdb.h" #include "passdb-cache.h" struct auth_cache *passdb_cache = NULL; static void passdb_cache_log_hit(struct auth_request *request, const char *value) { const char *p; if (!request->set->debug_passwords && *value != '\0' && *value != '\t') { /* hide the password */ p = strchr(value, '\t'); value = t_strconcat(PASSWORD_HIDDEN_STR, p, NULL); } auth_request_log_debug(request, AUTH_SUBSYS_DB, "cache hit: %s", value); } static bool passdb_cache_lookup(struct auth_request *request, const char *key, bool use_expired, struct auth_cache_node **node_r, const char **value_r, bool *neg_expired_r) { struct auth_stats *stats = auth_request_stats_get(request); const char *value; bool expired; /* value = password \t ... */ value = auth_cache_lookup(passdb_cache, request, key, node_r, &expired, neg_expired_r); if (value == NULL || (expired && !use_expired)) { stats->auth_cache_miss_count++; auth_request_log_debug(request, AUTH_SUBSYS_DB, value == NULL ? "cache miss" : "cache expired"); return FALSE; } stats->auth_cache_hit_count++; passdb_cache_log_hit(request, value); *value_r = value; return TRUE; } bool passdb_cache_verify_plain(struct auth_request *request, const char *key, const char *password, enum passdb_result *result_r, bool use_expired) { const char *value, *cached_pw, *scheme, *const *list; struct auth_cache_node *node; int ret; bool neg_expired; if (passdb_cache == NULL || key == NULL) return FALSE; if (!passdb_cache_lookup(request, key, use_expired, &node, &value, &neg_expired)) return FALSE; if (*value == '\0') { /* negative cache entry */ auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); *result_r = PASSDB_RESULT_USER_UNKNOWN; return TRUE; } list = t_strsplit_tabescaped(value); cached_pw = list[0]; if (*cached_pw == '\0') { /* NULL password */ auth_request_log_info(request, AUTH_SUBSYS_DB, "Cached NULL password access"); ret = 1; } else { scheme = password_get_scheme(&cached_pw); i_assert(scheme != NULL); ret = auth_request_password_verify(request, password, cached_pw, scheme, AUTH_SUBSYS_DB); if (ret == 0 && (node->last_success || neg_expired)) { /* a) the last authentication was successful. assume that the password was changed and cache is expired. b) negative TTL reached, use it for password mismatches too. */ node->last_success = FALSE; return FALSE; } } node->last_success = ret > 0; /* save the extra_fields only after we know we're using the cached data */ auth_request_set_fields(request, list + 1, NULL); *result_r = ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH; return TRUE; } bool passdb_cache_lookup_credentials(struct auth_request *request, const char *key, const char **password_r, const char **scheme_r, enum passdb_result *result_r, bool use_expired) { const char *value, *const *list; struct auth_cache_node *node; bool neg_expired; if (passdb_cache == NULL) return FALSE; if (!passdb_cache_lookup(request, key, use_expired, &node, &value, &neg_expired)) return FALSE; if (*value == '\0') { /* negative cache entry */ *result_r = PASSDB_RESULT_USER_UNKNOWN; *password_r = NULL; *scheme_r = NULL; return TRUE; } list = t_strsplit_tabescaped(value); auth_request_set_fields(request, list + 1, NULL); *result_r = PASSDB_RESULT_OK; *password_r = *list[0] == '\0' ? NULL : list[0]; *scheme_r = password_get_scheme(password_r); i_assert(*scheme_r != NULL || *password_r == NULL); return TRUE; } void passdb_cache_init(const struct auth_settings *set) { rlim_t limit; if (set->cache_size == 0 || set->cache_ttl == 0) return; if (restrict_get_process_size(&limit) == 0 && set->cache_size > limit) { i_warning("auth_cache_size (%luM) is higher than " "process VSZ limit (%luM)", (unsigned long)(set->cache_size/1024/1024), (unsigned long)(limit/1024/1024)); } passdb_cache = auth_cache_new(set->cache_size, set->cache_ttl, set->cache_negative_ttl); } void passdb_cache_deinit(void) { if (passdb_cache != NULL) auth_cache_free(&passdb_cache); } dovecot-2.2.33.2/src/auth/userdb-passwd-file.c0000644000175000017500000001333713165463624015742 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_PASSWD_FILE #include "istream.h" #include "str.h" #include "auth-cache.h" #include "db-passwd-file.h" #include #include struct passwd_file_userdb_iterate_context { struct userdb_iterate_context ctx; struct istream *input; char *path; bool skip_passdb_entries; }; struct passwd_file_userdb_module { struct userdb_module module; struct db_passwd_file *pwf; const char *username_format; }; static void passwd_file_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct passwd_file_userdb_module *module = (struct passwd_file_userdb_module *)_module; struct passwd_user *pu; const struct var_expand_table *table; string_t *str; const char *key, *value; char **p; int ret; ret = db_passwd_file_lookup(module->pwf, auth_request, module->username_format, &pu); if (ret <= 0 || pu->uid == 0) { callback(USERDB_RESULT_USER_UNKNOWN, auth_request); return; } if (pu->uid != (uid_t)-1) { auth_request_set_userdb_field(auth_request, "uid", dec2str(pu->uid)); } if (pu->gid != (gid_t)-1) { auth_request_set_userdb_field(auth_request, "gid", dec2str(pu->gid)); } if (pu->home != NULL) auth_request_set_userdb_field(auth_request, "home", pu->home); if (pu->extra_fields != NULL) { str = t_str_new(512); table = auth_request_get_var_expand_table(auth_request, NULL); for (p = pu->extra_fields; *p != NULL; p++) { if (strncmp(*p, "userdb_", 7) != 0) continue; key = *p + 7; value = strchr(key, '='); if (value != NULL) { key = t_strdup_until(key, value); str_truncate(str, 0); auth_request_var_expand_with_table(str, value + 1, auth_request, table, NULL); value = str_c(str); } else { value = ""; } auth_request_set_userdb_field(auth_request, key, value); } } callback(USERDB_RESULT_OK, auth_request); } static struct userdb_iterate_context * passwd_file_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct userdb_module *_module = auth_request->userdb->userdb; struct passwd_file_userdb_module *module = (struct passwd_file_userdb_module *)_module; struct passwd_file_userdb_iterate_context *ctx; int fd; ctx = i_new(struct passwd_file_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; ctx->skip_passdb_entries = !module->pwf->userdb_warn_missing; if (module->pwf->default_file == NULL) { i_error("passwd-file: User iteration isn't currently supported " "with %%variable paths"); ctx->ctx.failed = TRUE; return &ctx->ctx; } ctx->path = i_strdup(module->pwf->default_file->path); /* for now we support only a single passwd-file */ fd = open(ctx->path, O_RDONLY); if (fd == -1) { i_error("open(%s) failed: %m", ctx->path); ctx->ctx.failed = TRUE; } else { ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); } return &ctx->ctx; } static void passwd_file_iterate_next(struct userdb_iterate_context *_ctx) { struct passwd_file_userdb_iterate_context *ctx = (struct passwd_file_userdb_iterate_context *)_ctx; const char *line, *p; if (ctx->input == NULL) line = NULL; else { while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0' || *line == ':' || *line == '#') continue; /* no username or comment */ if (ctx->skip_passdb_entries && ((p = strchr(line, ':')) == NULL || strchr(p+1, ':') == NULL)) { /* only passdb info */ continue; } break; } if (line == NULL && ctx->input->stream_errno != 0) { i_error("read(%s) failed: %s", ctx->path, i_stream_get_error(ctx->input)); _ctx->failed = TRUE; } } if (line == NULL) _ctx->callback(NULL, _ctx->context); else T_BEGIN { _ctx->callback(t_strcut(line, ':'), _ctx->context); } T_END; } static int passwd_file_iterate_deinit(struct userdb_iterate_context *_ctx) { struct passwd_file_userdb_iterate_context *ctx = (struct passwd_file_userdb_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; if (ctx->input != NULL) i_stream_destroy(&ctx->input); i_free(ctx->path); i_free(ctx); return ret; } static struct userdb_module * passwd_file_preinit(pool_t pool, const char *args) { struct passwd_file_userdb_module *module; const char *format = PASSWD_FILE_DEFAULT_USERNAME_FORMAT; const char *p; if (strncmp(args, "username_format=", 16) == 0) { args += 16; p = strchr(args, ' '); if (p == NULL) { format = p_strdup(pool, args); args = ""; } else { format = p_strdup_until(pool, args, p); args = p + 1; } } if (*args == '\0') i_fatal("userdb passwd-file: Missing args"); module = p_new(pool, struct passwd_file_userdb_module, 1); module->pwf = db_passwd_file_init(args, TRUE, global_auth_settings->debug); module->username_format = format; return &module->module; } static void passwd_file_init(struct userdb_module *_module) { struct passwd_file_userdb_module *module = (struct passwd_file_userdb_module *)_module; db_passwd_file_parse(module->pwf); } static void passwd_file_deinit(struct userdb_module *_module) { struct passwd_file_userdb_module *module = (struct passwd_file_userdb_module *)_module; db_passwd_file_unref(&module->pwf); } struct userdb_module_interface userdb_passwd_file = { "passwd-file", passwd_file_preinit, passwd_file_init, passwd_file_deinit, passwd_file_lookup, passwd_file_iterate_init, passwd_file_iterate_next, passwd_file_iterate_deinit }; #else struct userdb_module_interface userdb_passwd_file = { .name = "passwd-file" }; #endif dovecot-2.2.33.2/src/auth/password-scheme-scram.c0000644000175000017500000001311613165463624016444 00000000000000/* * SCRAM-SHA-1 SASL authentication, see RFC-5802 * * Copyright (c) 2012 Florian Zeitz * * This software is released under the MIT license. */ #include "lib.h" #include "safe-memset.h" #include "base64.h" #include "buffer.h" #include "hmac.h" #include "randgen.h" #include "sha1.h" #include "str.h" #include "password-scheme.h" /* SCRAM allowed iteration count range. RFC says it SHOULD be at least 4096 */ #define SCRAM_MIN_ITERATE_COUNT 4096 #define SCRAM_MAX_ITERATE_COUNT INT_MAX #define SCRAM_DEFAULT_ITERATE_COUNT 4096 static void Hi(const unsigned char *str, size_t str_size, const unsigned char *salt, size_t salt_size, unsigned int i, unsigned char result[SHA1_RESULTLEN]) { struct hmac_context ctx; unsigned char U[SHA1_RESULTLEN]; unsigned int j, k; /* Calculate U1 */ hmac_init(&ctx, str, str_size, &hash_method_sha1); hmac_update(&ctx, salt, salt_size); hmac_update(&ctx, "\0\0\0\1", 4); hmac_final(&ctx, U); memcpy(result, U, SHA1_RESULTLEN); /* Calculate U2 to Ui and Hi */ for (j = 2; j <= i; j++) { hmac_init(&ctx, str, str_size, &hash_method_sha1); hmac_update(&ctx, U, sizeof(U)); hmac_final(&ctx, U); for (k = 0; k < SHA1_RESULTLEN; k++) result[k] ^= U[k]; } } int scram_sha1_scheme_parse(const unsigned char *credentials, size_t size, unsigned int *iter_count_r, const char **salt_r, unsigned char stored_key_r[], unsigned char server_key_r[], const char **error_r) { const char *const *fields; buffer_t *buf; /* password string format: iter,salt,stored_key,server_key */ fields = t_strsplit(t_strndup(credentials, size), ","); if (str_array_length(fields) != 4) { *error_r = "Invalid SCRAM-SHA-1 passdb entry format"; return -1; } if (str_to_uint(fields[0], iter_count_r) < 0 || *iter_count_r < SCRAM_MIN_ITERATE_COUNT || *iter_count_r > SCRAM_MAX_ITERATE_COUNT) { *error_r = "Invalid SCRAM-SHA-1 iteration count in passdb"; return -1; } *salt_r = fields[1]; buf = buffer_create_dynamic(pool_datastack_create(), SHA1_RESULTLEN); if (base64_decode(fields[2], strlen(fields[2]), NULL, buf) < 0 || buf->used != SHA1_RESULTLEN) { *error_r = "Invalid SCRAM-SHA-1 StoredKey in passdb"; return -1; } memcpy(stored_key_r, buf->data, SHA1_RESULTLEN); buffer_set_used_size(buf, 0); if (base64_decode(fields[3], strlen(fields[3]), NULL, buf) < 0 || buf->used != SHA1_RESULTLEN) { *error_r = "Invalid SCRAM-SHA-1 ServerKey in passdb"; return -1; } memcpy(server_key_r, buf->data, SHA1_RESULTLEN); return 0; } int scram_sha1_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { struct hmac_context ctx; const char *salt_base64; unsigned int iter_count; const unsigned char *salt; size_t salt_len; unsigned char salted_password[SHA1_RESULTLEN]; unsigned char client_key[SHA1_RESULTLEN]; unsigned char stored_key[SHA1_RESULTLEN]; unsigned char calculated_stored_key[SHA1_RESULTLEN]; unsigned char server_key[SHA1_RESULTLEN]; int ret; if (scram_sha1_scheme_parse(raw_password, size, &iter_count, &salt_base64, stored_key, server_key, error_r) < 0) return -1; salt = buffer_get_data(t_base64_decode_str(salt_base64), &salt_len); /* FIXME: credentials should be SASLprepped UTF8 data here */ Hi((const unsigned char *)plaintext, strlen(plaintext), salt, salt_len, iter_count, salted_password); /* Calculate ClientKey */ hmac_init(&ctx, salted_password, sizeof(salted_password), &hash_method_sha1); hmac_update(&ctx, "Client Key", 10); hmac_final(&ctx, client_key); /* Calculate StoredKey */ sha1_get_digest(client_key, sizeof(client_key), calculated_stored_key); ret = mem_equals_timing_safe(stored_key, calculated_stored_key, sizeof(stored_key)) ? 1 : 0; safe_memset(salted_password, 0, sizeof(salted_password)); safe_memset(client_key, 0, sizeof(client_key)); safe_memset(stored_key, 0, sizeof(stored_key)); return ret; } void scram_sha1_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { string_t *str; struct hmac_context ctx; unsigned char salt[16]; unsigned char salted_password[SHA1_RESULTLEN]; unsigned char client_key[SHA1_RESULTLEN]; unsigned char server_key[SHA1_RESULTLEN]; unsigned char stored_key[SHA1_RESULTLEN]; random_fill(salt, sizeof(salt)); str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(salt))); str_printfa(str, "%d,", SCRAM_DEFAULT_ITERATE_COUNT); base64_encode(salt, sizeof(salt), str); /* FIXME: credentials should be SASLprepped UTF8 data here */ Hi((const unsigned char *)plaintext, strlen(plaintext), salt, sizeof(salt), SCRAM_DEFAULT_ITERATE_COUNT, salted_password); /* Calculate ClientKey */ hmac_init(&ctx, salted_password, sizeof(salted_password), &hash_method_sha1); hmac_update(&ctx, "Client Key", 10); hmac_final(&ctx, client_key); /* Calculate StoredKey */ sha1_get_digest(client_key, sizeof(client_key), stored_key); str_append_c(str, ','); base64_encode(stored_key, sizeof(stored_key), str); /* Calculate ServerKey */ hmac_init(&ctx, salted_password, sizeof(salted_password), &hash_method_sha1); hmac_update(&ctx, "Server Key", 10); hmac_final(&ctx, server_key); str_append_c(str, ','); base64_encode(server_key, sizeof(server_key), str); safe_memset(salted_password, 0, sizeof(salted_password)); safe_memset(client_key, 0, sizeof(client_key)); safe_memset(server_key, 0, sizeof(server_key)); safe_memset(stored_key, 0, sizeof(stored_key)); *raw_password_r = (const unsigned char *)str_c(str); *size_r = str_len(str); } dovecot-2.2.33.2/src/auth/auth-penalty.c0000644000175000017500000001065113165463624014651 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "crc32.h" #include "master-service.h" #include "anvil-client.h" #include "auth-request.h" #include "auth-penalty.h" #include /* We don't want IPv6 hosts being able to flood our penalty tracking with tons of different IPs. */ #define PENALTY_IPV6_MASK_BITS 48 struct auth_penalty_request { struct auth_request *auth_request; struct anvil_client *client; auth_penalty_callback_t *callback; }; struct auth_penalty { struct anvil_client *client; unsigned int disabled:1; }; struct auth_penalty *auth_penalty_init(const char *path) { struct auth_penalty *penalty; penalty = i_new(struct auth_penalty, 1); penalty->client = anvil_client_init(path, NULL, ANVIL_CLIENT_FLAG_HIDE_ENOENT); if (anvil_client_connect(penalty->client, TRUE) < 0) penalty->disabled = TRUE; else { anvil_client_cmd(penalty->client, t_strdup_printf( "PENALTY-SET-EXPIRE-SECS\t%u", AUTH_PENALTY_TIMEOUT)); } return penalty; } void auth_penalty_deinit(struct auth_penalty **_penalty) { struct auth_penalty *penalty = *_penalty; *_penalty = NULL; anvil_client_deinit(&penalty->client); i_free(penalty); } unsigned int auth_penalty_to_secs(unsigned int penalty) { unsigned int i, secs = AUTH_PENALTY_INIT_SECS; for (i = 0; i < penalty; i++) secs *= 2; return secs < AUTH_PENALTY_MAX_SECS ? secs : AUTH_PENALTY_MAX_SECS; } static void auth_penalty_anvil_callback(const char *reply, void *context) { struct auth_penalty_request *request = context; unsigned int penalty = 0; unsigned long last_penalty = 0; unsigned int secs, drop_penalty; if (reply == NULL) { /* internal failure. */ if (!anvil_client_is_connected(request->client)) { /* we probably didn't have permissions to reconnect back to anvil. need to restart ourself. */ master_service_stop(master_service); } } else if (sscanf(reply, "%u %lu", &penalty, &last_penalty) != 2) { i_error("Invalid PENALTY-GET reply: %s", reply); } else { if ((time_t)last_penalty > ioloop_time) { /* time moved backwards? */ last_penalty = ioloop_time; } /* update penalty. */ drop_penalty = AUTH_PENALTY_MAX_PENALTY; while (penalty > 0) { secs = auth_penalty_to_secs(drop_penalty); if (ioloop_time - last_penalty < secs) break; drop_penalty--; penalty--; } } request->callback(penalty, request->auth_request); auth_request_unref(&request->auth_request); i_free(request); } static const char * auth_penalty_get_ident(struct auth_request *auth_request) { struct ip_addr ip; ip = auth_request->remote_ip; #ifdef HAVE_IPV6 if (IPADDR_IS_V6(&ip)) { memset(ip.u.ip6.s6_addr + PENALTY_IPV6_MASK_BITS/CHAR_BIT, 0, sizeof(ip.u.ip6.s6_addr) - PENALTY_IPV6_MASK_BITS/CHAR_BIT); } #endif return net_ip2addr(&ip); } void auth_penalty_lookup(struct auth_penalty *penalty, struct auth_request *auth_request, auth_penalty_callback_t *callback) { struct auth_penalty_request *request; const char *ident; ident = auth_penalty_get_ident(auth_request); if (penalty->disabled || ident == NULL || auth_request->no_penalty) { callback(0, auth_request); return; } request = i_new(struct auth_penalty_request, 1); request->auth_request = auth_request; request->client = penalty->client; request->callback = callback; auth_request_ref(auth_request); T_BEGIN { anvil_client_query(penalty->client, t_strdup_printf("PENALTY-GET\t%s", ident), auth_penalty_anvil_callback, request); } T_END; } static unsigned int get_userpass_checksum(struct auth_request *auth_request) { return auth_request->mech_password == NULL ? 0 : crc32_str_more(crc32_str(auth_request->mech_password), auth_request->user); } void auth_penalty_update(struct auth_penalty *penalty, struct auth_request *auth_request, unsigned int value) { const char *ident; ident = auth_penalty_get_ident(auth_request); if (penalty->disabled || ident == NULL || auth_request->no_penalty) return; if (value > AUTH_PENALTY_MAX_PENALTY) { /* even if the actual value doesn't change, the last_change timestamp does. */ value = AUTH_PENALTY_MAX_PENALTY; } T_BEGIN { const char *cmd; unsigned int checksum; checksum = value == 0 ? 0 : get_userpass_checksum(auth_request); cmd = t_strdup_printf("PENALTY-INC\t%s\t%u\t%u", ident, checksum, value); anvil_client_cmd(penalty->client, cmd); } T_END; } dovecot-2.2.33.2/src/auth/auth-request-stats.h0000644000175000017500000000060613123174404016012 00000000000000#ifndef AUTH_REQUEST_STATS_H #define AUTH_REQUEST_STATS_H #include "auth-stats.h" struct auth_request; struct auth_stats *auth_request_stats_get(struct auth_request *request); void auth_request_stats_add_tempfail(struct auth_request *request); void auth_request_stats_send(struct auth_request *request); void auth_request_stats_init(void); void auth_request_stats_deinit(void); #endif dovecot-2.2.33.2/src/auth/userdb-nss.c0000644000175000017500000001031013123174404014300 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ /* Currently supports only GLIBC-compatible NSS modules */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_NSS #include "module-dir.h" #include #include #include #include #define USER_CACHE_KEY "%u" struct nss_userdb_module { struct userdb_module module; char *buf; size_t bufsize; struct module nss_module; enum nss_status (*getpwnam_r)(const char *name, struct passwd *pwd, char *buffer, size_t buflen, int *errnop); }; static void userdb_nss_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct nss_userdb_module *module = (struct nss_userdb_module *)_module; struct passwd pw; enum nss_status status; enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; int err; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "lookup"); status = module->getpwnam_r(auth_request->user, &pw, module->buf, module->bufsize, &err); switch (status) { case NSS_STATUS_TRYAGAIN: auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "returned tryagain (err=%d)", err); break; case NSS_STATUS_UNAVAIL: auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "unavailable (err=%d)", err); break; case NSS_STATUS_NOTFOUND: auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); result = USERDB_RESULT_USER_UNKNOWN; break; case NSS_STATUS_SUCCESS: result = USERDB_RESULT_OK; break; default: auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "returned %d (err=%d)", status, err); break; } if (result != USERDB_RESULT_OK) { callback(result, auth_request); return; } auth_request_set_field(auth_request, "user", pw.pw_name, NULL); auth_request_set_userdb_field(auth_request, "system_groups_user", pw.pw_name); auth_request_set_userdb_field(auth_request, "uid", dec2str(pw.pw_uid)); auth_request_set_userdb_field(auth_request, "gid", dec2str(pw.pw_gid)); auth_request_set_userdb_field(auth_request, "home", pw.pw_dir); callback(USERDB_RESULT_OK, auth_request); } static void userdb_nss_load_module(struct nss_userdb_module *module, pool_t pool) { const char *name = module->nss_module.name; char *path; path = p_strdup_printf(pool, "/usr/lib/libnss_%s.so", name); module->nss_module.handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); if (module->nss_module.handle == NULL) i_fatal("dlopen(%s) failed: %s", path, dlerror()); module->nss_module.path = path; module->getpwnam_r = module_get_symbol(&module->nss_module, t_strdup_printf("_nss_%s_getpwnam_r", name)); if (module->getpwnam_r == NULL) i_fatal("userdb nss: Module %s missing getpwnam_r()", path); } static struct userdb_module * userdb_nss_preinit(pool_t pool, const char *args) { struct nss_userdb_module *module; const char *const *tmp; long bufsize; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); i_assert(bufsize > 0); module = p_new(pool, struct nss_userdb_module, 1); module->bufsize = bufsize; module->buf = p_malloc(pool, module->bufsize); module->module.blocking = TRUE; for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { if (strcmp(*tmp, "blocking=no") == 0) module->module.blocking = FALSE; else if (strncmp(*tmp, "service=", 8) == 0) module->nss_module.name = p_strdup(pool, *tmp + 8); else i_fatal("userdb nss: Unknown setting: %s", *tmp); } if (module->nss_module.name == NULL) i_fatal("userdb nss: Missing service"); userdb_nss_load_module(module, pool); module->module.default_cache_key = USER_CACHE_KEY; return &module->module; } static void userdb_nss_deinit(struct userdb_module *_module) { struct nss_userdb_module *module = (struct nss_userdb_module *)_module; void (*mod_endpwent)(void); const char *symbol; symbol = t_strdup_printf("_nss_%s_endpwent", module->nss_module.name); mod_endpwent = module_get_symbol(&module->nss_module, symbol); if (mod_endpwent != NULL) mod_endpwent(); } struct userdb_module_interface userdb_nss = { "nss", userdb_nss_preinit, NULL, userdb_nss_deinit, userdb_nss_lookup, NULL, NULL, NULL }; #else struct userdb_module_interface userdb_nss = { .name = "nss" }; #endif dovecot-2.2.33.2/src/auth/auth-worker-client.c0000644000175000017500000005503213165463624015764 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "base64.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "hex-binary.h" #include "str.h" #include "strescape.h" #include "process-title.h" #include "master-service.h" #include "auth-request.h" #include "auth-worker-client.h" #define AUTH_WORKER_WARN_DISCONNECTED_LONG_CMD_SECS 30 #define OUTBUF_THROTTLE_SIZE (1024*10) #define CLIENT_STATE_HANDSHAKE "handshaking" #define CLIENT_STATE_ITER "iterating users" #define CLIENT_STATE_IDLE "idling" #define CLIENT_STATE_STOP "waiting for shutdown" struct auth_worker_client { int refcount; struct auth *auth; int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to_idle; time_t cmd_start; unsigned int version_received:1; unsigned int dbhash_received:1; unsigned int error_sent:1; }; struct auth_worker_list_context { struct auth_worker_client *client; struct auth_request *auth_request; struct userdb_iterate_context *iter; bool sending, sent, done; }; struct auth_worker_client *auth_worker_client; static bool auth_worker_client_error = FALSE; static void auth_worker_input(struct auth_worker_client *client); static int auth_worker_output(struct auth_worker_client *client); void auth_worker_refresh_proctitle(const char *state) { if (!global_auth_settings->verbose_proctitle || !worker) return; if (auth_worker_client_error) state = "error"; else if (auth_worker_client == NULL) state = "waiting for connection"; process_title_set(t_strdup_printf("worker: %s", state)); } static void auth_worker_client_check_throttle(struct auth_worker_client *client) { if (o_stream_get_buffer_used_size(client->output) >= OUTBUF_THROTTLE_SIZE) { /* stop reading new requests until client has read the pending replies. */ if (client->io != NULL) io_remove(&client->io); } } static struct auth_request * worker_auth_request_new(struct auth_worker_client *client, unsigned int id, const char *const *args) { struct auth_request *auth_request; const char *key, *value; auth_request = auth_request_new_dummy(); client->refcount++; auth_request->context = client; auth_request->id = id; for (; *args != NULL; args++) { value = strchr(*args, '='); if (value == NULL) (void)auth_request_import(auth_request, *args, NULL); else { key = t_strdup_until(*args, value++); (void)auth_request_import(auth_request, key, value); } } /* reset changed-fields, so we'll export only the ones that were changed by this lookup. */ auth_fields_snapshot(auth_request->extra_fields); if (auth_request->userdb_reply != NULL) auth_fields_snapshot(auth_request->userdb_reply); auth_request_init(auth_request); return auth_request; } static void auth_worker_send_reply(struct auth_worker_client *client, struct auth_request *request, string_t *str) { time_t cmd_duration = time(NULL) - client->cmd_start; const char *p; if (worker_restart_request) o_stream_nsend_str(client->output, "RESTART\n"); o_stream_nsend(client->output, str_data(str), str_len(str)); if (o_stream_nfinish(client->output) < 0 && request != NULL && cmd_duration > AUTH_WORKER_WARN_DISCONNECTED_LONG_CMD_SECS) { p = strchr(str_c(str), '\t'); p = p == NULL ? "BUG" : t_strcut(p+1, '\t'); i_warning("Auth master disconnected us while handling " "request for %s for %ld secs (result=%s)", request->user, (long)cmd_duration, p); } } static void reply_append_extra_fields(string_t *str, struct auth_request *request) { if (!auth_fields_is_empty(request->extra_fields)) { str_append_c(str, '\t'); /* export only the fields changed by this lookup, so the changed-flag gets preserved correctly on the master side as well. */ auth_fields_append(request->extra_fields, str, AUTH_FIELD_FLAG_CHANGED, AUTH_FIELD_FLAG_CHANGED); } if (request->userdb_reply != NULL && auth_fields_is_empty(request->userdb_reply)) { /* all userdb_* fields had NULL values. we'll still need to tell this to the master */ str_append(str, "\tuserdb_"AUTH_REQUEST_USER_KEY_IGNORE); } } static void verify_plain_callback(enum passdb_result result, struct auth_request *request) { struct auth_worker_client *client = request->context; string_t *str; if (request->failed && result == PASSDB_RESULT_OK) result = PASSDB_RESULT_PASSWORD_MISMATCH; str = t_str_new(128); str_printfa(str, "%u\t", request->id); if (result == PASSDB_RESULT_OK) if (auth_fields_exists(request->extra_fields, "noauthenticate")) str_append(str, "NEXT"); else str_append(str, "OK"); else str_printfa(str, "FAIL\t%d", result); if (result != PASSDB_RESULT_INTERNAL_FAILURE) { str_append_c(str, '\t'); if (request->user_changed_by_lookup) str_append_tabescaped(str, request->user); str_append_c(str, '\t'); if (request->passdb_password != NULL) str_append_tabescaped(str, request->passdb_password); reply_append_extra_fields(str, request); } str_append_c(str, '\n'); auth_worker_send_reply(client, request, str); auth_request_unref(&request); auth_worker_client_check_throttle(client); auth_worker_client_unref(&client); } static bool auth_worker_handle_passv(struct auth_worker_client *client, unsigned int id, const char *const *args) { /* verify plaintext password */ struct auth_request *auth_request; struct auth_passdb *passdb; const char *password; unsigned int passdb_id; /* [] */ if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) { i_error("BUG: Auth worker server sent us invalid PASSV"); return FALSE; } password = args[1]; auth_request = worker_auth_request_new(client, id, args + 2); auth_request->mech_password = p_strdup(auth_request->pool, password); if (auth_request->user == NULL || auth_request->service == NULL) { i_error("BUG: PASSV had missing parameters"); auth_request_unref(&auth_request); return FALSE; } passdb = auth_request->passdb; while (passdb != NULL && passdb->passdb->id != passdb_id) passdb = passdb->next; if (passdb == NULL) { /* could be a masterdb */ passdb = auth_request_get_auth(auth_request)->masterdbs; while (passdb != NULL && passdb->passdb->id != passdb_id) passdb = passdb->next; if (passdb == NULL) { i_error("BUG: PASSV had invalid passdb ID"); auth_request_unref(&auth_request); return FALSE; } } auth_request->passdb = passdb; passdb->passdb->iface. verify_plain(auth_request, password, verify_plain_callback); return TRUE; } static void lookup_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *request) { struct auth_worker_client *client = request->context; string_t *str; if (request->failed && result == PASSDB_RESULT_OK) result = PASSDB_RESULT_PASSWORD_MISMATCH; str = t_str_new(128); str_printfa(str, "%u\t", request->id); if (result != PASSDB_RESULT_OK && result != PASSDB_RESULT_NEXT) str_printfa(str, "FAIL\t%d", result); else { if (result == PASSDB_RESULT_NEXT) str_append(str, "NEXT\t"); else str_append(str, "OK\t"); if (request->user_changed_by_lookup) str_append_tabescaped(str, request->user); str_append_c(str, '\t'); if (request->credentials_scheme[0] != '\0') { str_printfa(str, "{%s.b64}", request->credentials_scheme); base64_encode(credentials, size, str); } else { i_assert(size == 0); } reply_append_extra_fields(str, request); } str_append_c(str, '\n'); auth_worker_send_reply(client, request, str); auth_request_unref(&request); auth_worker_client_check_throttle(client); auth_worker_client_unref(&client); } static bool auth_worker_handle_passl(struct auth_worker_client *client, unsigned int id, const char *const *args) { /* lookup credentials */ struct auth_request *auth_request; const char *scheme; unsigned int passdb_id; /* [] */ if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) { i_error("BUG: Auth worker server sent us invalid PASSL"); return FALSE; } scheme = args[1]; auth_request = worker_auth_request_new(client, id, args + 2); auth_request->credentials_scheme = p_strdup(auth_request->pool, scheme); if (auth_request->user == NULL || auth_request->service == NULL) { i_error("BUG: PASSL had missing parameters"); auth_request_unref(&auth_request); return FALSE; } while (auth_request->passdb->passdb->id != passdb_id) { auth_request->passdb = auth_request->passdb->next; if (auth_request->passdb == NULL) { i_error("BUG: PASSL had invalid passdb ID"); auth_request_unref(&auth_request); return FALSE; } } if (auth_request->passdb->passdb->iface.lookup_credentials == NULL) { i_error("BUG: PASSL lookup not supported by given passdb"); auth_request_unref(&auth_request); return FALSE; } auth_request->prefer_plain_credentials = TRUE; auth_request->passdb->passdb->iface. lookup_credentials(auth_request, lookup_credentials_callback); return TRUE; } static void set_credentials_callback(bool success, struct auth_request *request) { struct auth_worker_client *client = request->context; string_t *str; str = t_str_new(64); str_printfa(str, "%u\t%s\n", request->id, success ? "OK" : "FAIL"); auth_worker_send_reply(client, request, str); auth_request_unref(&request); auth_worker_client_check_throttle(client); auth_worker_client_unref(&client); } static bool auth_worker_handle_setcred(struct auth_worker_client *client, unsigned int id, const char *const *args) { struct auth_request *auth_request; unsigned int passdb_id; const char *creds; /* [] */ if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) { i_error("BUG: Auth worker server sent us invalid SETCRED"); return FALSE; } creds = args[1]; auth_request = worker_auth_request_new(client, id, args + 2); if (auth_request->user == NULL || auth_request->service == NULL) { i_error("BUG: SETCRED had missing parameters"); auth_request_unref(&auth_request); return FALSE; } while (auth_request->passdb->passdb->id != passdb_id) { auth_request->passdb = auth_request->passdb->next; if (auth_request->passdb == NULL) { i_error("BUG: SETCRED had invalid passdb ID"); auth_request_unref(&auth_request); return FALSE; } } auth_request->passdb->passdb->iface. set_credentials(auth_request, creds, set_credentials_callback); return TRUE; } static void lookup_user_callback(enum userdb_result result, struct auth_request *auth_request) { struct auth_worker_client *client = auth_request->context; string_t *str; str = t_str_new(128); str_printfa(str, "%u\t", auth_request->id); switch (result) { case USERDB_RESULT_INTERNAL_FAILURE: str_append(str, "FAIL\t"); break; case USERDB_RESULT_USER_UNKNOWN: str_append(str, "NOTFOUND\t"); break; case USERDB_RESULT_OK: str_append(str, "OK\t"); if (auth_request->user_changed_by_lookup) str_append_tabescaped(str, auth_request->user); str_append_c(str, '\t'); /* export only the fields changed by this lookup */ auth_fields_append(auth_request->userdb_reply, str, AUTH_FIELD_FLAG_CHANGED, AUTH_FIELD_FLAG_CHANGED); if (auth_request->userdb_lookup_tempfailed) str_append(str, "\ttempfail"); break; } str_append_c(str, '\n'); auth_worker_send_reply(client, auth_request, str); auth_request_unref(&auth_request); auth_worker_client_check_throttle(client); auth_worker_client_unref(&client); } static struct auth_userdb * auth_userdb_find_by_id(struct auth_userdb *userdbs, unsigned int id) { struct auth_userdb *db; for (db = userdbs; db != NULL; db = db->next) { if (db->userdb->id == id) return db; } return NULL; } static bool auth_worker_handle_user(struct auth_worker_client *client, unsigned int id, const char *const *args) { /* lookup user */ struct auth_request *auth_request; unsigned int userdb_id; /* [] */ if (str_to_uint(args[0], &userdb_id) < 0) { i_error("BUG: Auth worker server sent us invalid USER"); return FALSE; } auth_request = worker_auth_request_new(client, id, args + 1); if (auth_request->user == NULL || auth_request->service == NULL) { i_error("BUG: USER had missing parameters"); auth_request_unref(&auth_request); return FALSE; } auth_request->userdb_lookup = TRUE; auth_request->userdb = auth_userdb_find_by_id(auth_request->userdb, userdb_id); if (auth_request->userdb == NULL) { i_error("BUG: USER had invalid userdb ID"); auth_request_unref(&auth_request); return FALSE; } if (auth_request->userdb_reply == NULL) auth_request_init_userdb_reply(auth_request); auth_request->userdb->userdb->iface-> lookup(auth_request, lookup_user_callback); return TRUE; } static void auth_worker_client_idle_kill(struct auth_worker_client *client ATTR_UNUSED) { auth_worker_client_send_shutdown(); } static void auth_worker_client_set_idle_timeout(struct auth_worker_client *client) { unsigned int idle_kill_secs; i_assert(client->to_idle == NULL); idle_kill_secs = master_service_get_idle_kill_secs(master_service); if (idle_kill_secs > 0) { client->to_idle = timeout_add(idle_kill_secs * 1000, auth_worker_client_idle_kill, client); } } static void list_iter_deinit(struct auth_worker_list_context *ctx) { struct auth_worker_client *client = ctx->client; string_t *str; i_assert(client->io == NULL); str = t_str_new(32); if (ctx->auth_request->userdb->userdb->iface-> iterate_deinit(ctx->iter) < 0) str_printfa(str, "%u\tFAIL\n", ctx->auth_request->id); else str_printfa(str, "%u\tOK\n", ctx->auth_request->id); auth_worker_send_reply(client, NULL, str); client->io = io_add(client->fd, IO_READ, auth_worker_input, client); auth_worker_client_set_idle_timeout(client); o_stream_set_flush_callback(client->output, auth_worker_output, client); auth_request_unref(&ctx->auth_request); auth_worker_client_unref(&client); i_free(ctx); auth_worker_refresh_proctitle(CLIENT_STATE_IDLE); } static void list_iter_callback(const char *user, void *context) { struct auth_worker_list_context *ctx = context; string_t *str; if (user == NULL) { if (ctx->sending) ctx->done = TRUE; else list_iter_deinit(ctx); return; } if (!ctx->sending) o_stream_cork(ctx->client->output); T_BEGIN { str = t_str_new(128); str_printfa(str, "%u\t*\t%s\n", ctx->auth_request->id, user); o_stream_nsend(ctx->client->output, str_data(str), str_len(str)); } T_END; if (ctx->sending) { /* avoid recursively looping to this same function */ ctx->sent = TRUE; return; } do { ctx->sending = TRUE; ctx->sent = FALSE; T_BEGIN { ctx->auth_request->userdb->userdb->iface-> iterate_next(ctx->iter); } T_END; if (o_stream_get_buffer_used_size(ctx->client->output) > OUTBUF_THROTTLE_SIZE) { if (o_stream_flush(ctx->client->output) < 0) { ctx->done = TRUE; break; } } } while (ctx->sent && o_stream_get_buffer_used_size(ctx->client->output) <= OUTBUF_THROTTLE_SIZE); o_stream_uncork(ctx->client->output); ctx->sending = FALSE; if (ctx->done) list_iter_deinit(ctx); else o_stream_set_flush_pending(ctx->client->output, TRUE); } static int auth_worker_list_output(struct auth_worker_list_context *ctx) { int ret; if ((ret = o_stream_flush(ctx->client->output)) < 0) { list_iter_deinit(ctx); return 1; } if (ret > 0) T_BEGIN { ctx->auth_request->userdb->userdb->iface-> iterate_next(ctx->iter); } T_END; return 1; } static bool auth_worker_handle_list(struct auth_worker_client *client, unsigned int id, const char *const *args) { struct auth_worker_list_context *ctx; struct auth_userdb *userdb; unsigned int userdb_id; if (str_to_uint(args[0], &userdb_id) < 0) { i_error("BUG: Auth worker server sent us invalid LIST"); return FALSE; } userdb = auth_userdb_find_by_id(client->auth->userdbs, userdb_id); if (userdb == NULL) { i_error("BUG: LIST had invalid userdb ID"); return FALSE; } ctx = i_new(struct auth_worker_list_context, 1); ctx->client = client; ctx->auth_request = worker_auth_request_new(client, id, args + 1); ctx->auth_request->userdb = userdb; if (ctx->auth_request->user == NULL || ctx->auth_request->service == NULL) { i_error("BUG: LIST had missing parameters"); auth_request_unref(&ctx->auth_request); i_free(ctx); return FALSE; } io_remove(&ctx->client->io); if (ctx->client->to_idle != NULL) timeout_remove(&ctx->client->to_idle); o_stream_set_flush_callback(ctx->client->output, auth_worker_list_output, ctx); ctx->iter = ctx->auth_request->userdb->userdb->iface-> iterate_init(ctx->auth_request, list_iter_callback, ctx); ctx->auth_request->userdb->userdb->iface->iterate_next(ctx->iter); return TRUE; } static bool auth_worker_handle_line(struct auth_worker_client *client, const char *line) { const char *const *args; unsigned int id; bool ret = FALSE; args = t_strsplit_tabescaped(line); if (args[0] == NULL || args[1] == NULL || args[2] == NULL || str_to_uint(args[0], &id) < 0) { i_error("BUG: Invalid input: %s", line); return FALSE; } io_loop_time_refresh(); client->cmd_start = ioloop_time; auth_worker_refresh_proctitle(args[1]); if (strcmp(args[1], "PASSV") == 0) ret = auth_worker_handle_passv(client, id, args + 2); else if (strcmp(args[1], "PASSL") == 0) ret = auth_worker_handle_passl(client, id, args + 2); else if (strcmp(args[1], "SETCRED") == 0) ret = auth_worker_handle_setcred(client, id, args + 2); else if (strcmp(args[1], "USER") == 0) ret = auth_worker_handle_user(client, id, args + 2); else if (strcmp(args[1], "LIST") == 0) ret = auth_worker_handle_list(client, id, args + 2); else { i_error("BUG: Auth-worker received unknown command: %s", args[1]); } if (client->io != NULL) auth_worker_refresh_proctitle(CLIENT_STATE_IDLE); return ret; } static bool auth_worker_verify_db_hash(const char *line) { string_t *str; unsigned char passdb_md5[MD5_RESULTLEN]; unsigned char userdb_md5[MD5_RESULTLEN]; passdbs_generate_md5(passdb_md5); userdbs_generate_md5(userdb_md5); str = t_str_new(128); str_append(str, "DBHASH\t"); binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5)); str_append_c(str, '\t'); binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5)); return strcmp(line, str_c(str)) == 0; } static void auth_worker_input(struct auth_worker_client *client) { char *line; bool ret; switch (i_stream_read(client->input)) { case 0: return; case -1: /* disconnected */ auth_worker_client_destroy(&client); return; case -2: /* buffer full */ i_error("BUG: Auth worker server sent us more than %d bytes", (int)AUTH_WORKER_MAX_LINE_LENGTH); auth_worker_client_destroy(&client); return; } if (!client->version_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!version_string_verify(line, "auth-worker", AUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("Auth worker not compatible with this server " "(mixed old and new binaries?)"); auth_worker_client_destroy(&client); return; } client->version_received = TRUE; } if (!client->dbhash_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!auth_worker_verify_db_hash(line)) { i_error("Auth worker sees different passdbs/userdbs " "than auth server. Maybe config just changed " "and this goes away automatically?"); auth_worker_client_destroy(&client); return; } client->dbhash_received = TRUE; auth_worker_refresh_proctitle(CLIENT_STATE_IDLE); } client->refcount++; while ((line = i_stream_next_line(client->input)) != NULL) { T_BEGIN { ret = auth_worker_handle_line(client, line); } T_END; if (!ret) { struct auth_worker_client *client2 = client; auth_worker_client_destroy(&client2); break; } } if (client->to_idle != NULL) timeout_reset(client->to_idle); auth_worker_client_unref(&client); } static int auth_worker_output(struct auth_worker_client *client) { if (o_stream_flush(client->output) < 0) { auth_worker_client_destroy(&client); return 1; } if (o_stream_get_buffer_used_size(client->output) <= OUTBUF_THROTTLE_SIZE/3 && client->io == NULL) { /* allow input again */ client->io = io_add(client->fd, IO_READ, auth_worker_input, client); } return 1; } struct auth_worker_client * auth_worker_client_create(struct auth *auth, int fd) { struct auth_worker_client *client; client = i_new(struct auth_worker_client, 1); client->refcount = 1; client->auth = auth; client->fd = fd; client->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH, FALSE); client->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(client->output, TRUE); o_stream_set_flush_callback(client->output, auth_worker_output, client); client->io = io_add(fd, IO_READ, auth_worker_input, client); auth_worker_client_set_idle_timeout(client); auth_worker_refresh_proctitle(CLIENT_STATE_HANDSHAKE); auth_worker_client = client; if (auth_worker_client_error) auth_worker_client_send_error(); return client; } void auth_worker_client_destroy(struct auth_worker_client **_client) { struct auth_worker_client *client = *_client; *_client = NULL; if (client->fd == -1) return; i_stream_close(client->input); o_stream_close(client->output); if (client->to_idle != NULL) timeout_remove(&client->to_idle); if (client->io != NULL) io_remove(&client->io); net_disconnect(client->fd); client->fd = -1; auth_worker_client_unref(&client); auth_worker_client = NULL; auth_worker_refresh_proctitle(""); master_service_client_connection_destroyed(master_service); } void auth_worker_client_unref(struct auth_worker_client **_client) { struct auth_worker_client *client = *_client; *_client = NULL; if (--client->refcount > 0) return; i_stream_unref(&client->input); o_stream_unref(&client->output); i_free(client); } void auth_worker_client_send_error(void) { auth_worker_client_error = TRUE; if (auth_worker_client != NULL && !auth_worker_client->error_sent) { o_stream_nsend_str(auth_worker_client->output, "ERROR\n"); auth_worker_client->error_sent = TRUE; } auth_worker_refresh_proctitle(""); } void auth_worker_client_send_success(void) { auth_worker_client_error = FALSE; if (auth_worker_client == NULL) return; if (auth_worker_client->error_sent) { o_stream_nsend_str(auth_worker_client->output, "SUCCESS\n"); auth_worker_client->error_sent = FALSE; } if (auth_worker_client->io != NULL) auth_worker_refresh_proctitle(CLIENT_STATE_IDLE); } void auth_worker_client_send_shutdown(void) { if (auth_worker_client != NULL) o_stream_nsend_str(auth_worker_client->output, "SHUTDOWN\n"); auth_worker_refresh_proctitle(CLIENT_STATE_STOP); } dovecot-2.2.33.2/src/auth/mech-external.c0000644000175000017500000000321513123174404014755 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #include "mech.h" #include "mech-plain-common.h" static void mech_external_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { const char *authzid, *error; authzid = t_strndup(data, data_size); if (request->user == NULL) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "username not known"); auth_request_fail(request); return; } /* this call is done simply to put the username through translation settings */ if (!auth_request_set_username(request, "", &error)) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid username"); auth_request_fail(request); return; } if (*authzid != '\0' && !auth_request_set_login_username(request, authzid, &error)) { /* invalid login username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "login user: %s", error); auth_request_fail(request); } else { auth_request_verify_plain(request, "", plain_verify_callback); } } static struct auth_request *mech_external_auth_new(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"external_auth_request", 2048); request = p_new(pool, struct auth_request, 1); request->pool = pool; return request; } const struct mech_module mech_external = { "EXTERNAL", .flags = 0, .passdb_need = MECH_PASSDB_NEED_VERIFY_PLAIN, mech_external_auth_new, mech_generic_auth_initial, mech_external_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/test-auth-request-var-expand.c0000644000175000017500000001675213165463624017715 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "auth.h" #include "passdb.h" #include "userdb.h" #include "auth-request.h" #include "test-common.h" static struct passdb_module test_passdb = { .id = 40 }; static struct userdb_module test_userdb = { .id = 41 }; static struct auth_passdb test_auth_passdb = { .passdb = &test_passdb }; static struct auth_userdb test_auth_userdb = { .userdb = &test_userdb }; static struct auth_request default_test_request = { .user = "-user@+domain1@+domain2", .service = "-service", .local_ip = { .family = AF_INET }, .remote_ip = { .family = AF_INET }, .client_pid = 54321, .mech_password = "-password", .mech_name = "-mech", .secured = TRUE, .local_port = 21, .remote_port = 210, .valid_client_cert = TRUE, .requested_login_user = "-loginuser@+logindomain1@+logindomain2", .session_id = "-session", .real_local_ip = { .family = AF_INET }, .real_remote_ip = { .family = AF_INET }, .real_local_port = 200, .real_remote_port = 201, .master_user = "-masteruser@-masterdomain1@-masterdomain2", .session_pid = 5000, .original_username = "-origuser@-origdomain1@-origdomain2", .passdb = &test_auth_passdb, .userdb = &test_auth_userdb }; static struct auth_request test_request; static struct auth_request empty_test_request = { .user = "" }; static const char * test_escape(const char *string, const struct auth_request *request) { char *dest; unsigned int i; test_assert(request == &test_request); dest = t_strdup_noconst(string); for (i = 0; dest[i] != '\0'; i++) { if (dest[i] == '-') dest[i] = '+'; } return dest; } static bool test_empty_request(string_t *str, const char *input) { str_truncate(str, 0); var_expand(str, input, auth_request_get_var_expand_table(&empty_test_request, NULL)); return strspn(str_c(str), "\n0") == str_len(str); } static void test_auth_request_var_expand_shortlong(void) { static const char *test_input_short = "%u\n%n\n%d\n%s\n%h\n%l\n%r\n%p\n%w\n%m\n%c\n%a\n%b\n%k\n"; static const char *test_input_long = "%{user}\n%{username}\n%{domain}\n%{service}\n%{home}\n" "%{lip}\n%{rip}\n%{pid}\n%{password}\n%{mech}\n%{secured}\n" "%{lport}\n%{rport}\n%{cert}\n"; static const char *test_output = /* %{home} is intentionally always expanding to empty */ "+user@+domain1@+domain2\n+user\n+domain1@+domain2\n+service\n\n" "7.91.205.21\n73.150.2.210\n54321\n+password\n+mech\nsecured\n" "21\n210\nvalid\n"; string_t *str = t_str_new(256); test_begin("auth request var expand short and long"); var_expand(str, test_input_short, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert(strcmp(str_c(str), test_output) == 0); str_truncate(str, 0); var_expand(str, test_input_long, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert(strcmp(str_c(str), test_output) == 0); /* test with empty input that it won't crash */ test_assert(test_empty_request(str, test_input_short)); test_assert(test_empty_request(str, test_input_long)); test_end(); } static void test_auth_request_var_expand_flags(void) { static const char *test_input = "%!\n%{secured}\n%{cert}\n"; string_t *str = t_str_new(10); test_begin("auth request var expand flags"); test_request.userdb_lookup = FALSE; test_request.secured = FALSE; test_request.valid_client_cert = FALSE; var_expand(str, test_input, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert(strcmp(str_c(str), "40\n\n\n") == 0); test_request.userdb_lookup = TRUE; test_request.secured = TRUE; test_request.valid_client_cert = TRUE; str_truncate(str, 0); var_expand(str, test_input, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert(strcmp(str_c(str), "41\nsecured\nvalid\n") == 0); test_assert(test_empty_request(str, test_input)); test_end(); } static void test_auth_request_var_expand_long(void) { static const char *test_input = "%{login_user}\n%{login_username}\n%{login_domain}\n%{session}\n" "%{real_lip}\n%{real_rip}\n%{real_lport}\n%{real_rport}\n" "%{master_user}\n%{session_pid}\n" "%{orig_user}\n%{orig_username}\n%{orig_domain}\n"; static const char *test_output = "+loginuser@+logindomain1@+logindomain2\n+loginuser\n+logindomain1@+logindomain2\n+session\n" "13.81.174.20\n13.81.174.21\n200\n201\n" "+masteruser@+masterdomain1@+masterdomain2\n5000\n" "+origuser@+origdomain1@+origdomain2\n+origuser\n+origdomain1@+origdomain2\n"; string_t *str = t_str_new(256); test_begin("auth request var expand long-only"); var_expand(str, test_input, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert(strcmp(str_c(str), test_output) == 0); test_assert(test_empty_request(str, test_input)); test_end(); } static void test_auth_request_var_expand_usernames(void) { static const struct { const char *username, *output; } tests[] = { { "-foo", "+foo\n\n\n\n+foo" }, { "-foo@-domain", "+foo\n+domain\n+domain\n+domain\n+foo@+domain" }, { "-foo@-domain1@-domain2", "+foo\n+domain1@+domain2\n+domain1\n+domain2\n+foo@+domain1@+domain2" } }; static const char *test_input = "%{username}\n%{domain}\n%{domain_first}\n%{domain_last}\n%{user}"; string_t *str = t_str_new(64); unsigned int i; test_begin("auth request var expand usernames"); for (i = 0; i < N_ELEMENTS(tests); i++) { test_request.user = t_strdup_noconst(tests[i].username); str_truncate(str, 0); var_expand(str, test_input, auth_request_get_var_expand_table(&test_request, test_escape)); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); } test_request.user = default_test_request.user; test_end(); } static void test_auth_request_var_expand_funcs(void) { pool_t pool; const char *value; test_begin("auth request var expand funcs"); pool = pool_alloconly_create("test var expand funcs", 1024); test_request.extra_fields = auth_fields_init(pool); test_request.userdb_reply = auth_fields_init(pool); auth_fields_add(test_request.extra_fields, "pkey1", "-pval1", 0); auth_fields_add(test_request.extra_fields, "pkey2", "", 0); auth_fields_add(test_request.userdb_reply, "ukey1", "-uval1", 0); auth_fields_add(test_request.userdb_reply, "ukey2", "", 0); value = t_auth_request_var_expand( "%{passdb:pkey1}\n%{passdb:pkey1:default1}\n" "%{passdb:pkey2}\n%{passdb:pkey2:default2}\n" "%{passdb:pkey3}\n%{passdb:pkey3:default3}\n" "%{passdb:ukey1}\n%{passdb:ukey1:default4}\n", &test_request, test_escape); test_assert(strcmp(value, "+pval1\n+pval1\n\n\n\ndefault3\n\ndefault4\n") == 0); value = t_auth_request_var_expand( "%{userdb:ukey1}\n%{userdb:ukey1:default1}\n" "%{userdb:ukey2}\n%{userdb:ukey2:default2}\n" "%{userdb:ukey3}\n%{userdb:ukey3:default3}\n" "%{userdb:pkey1}\n%{userdb:pkey1:default4}\n", &test_request, test_escape); test_assert(strcmp(value, "+uval1\n+uval1\n\n\n\ndefault3\n\ndefault4\n") == 0); pool_unref(&pool); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_auth_request_var_expand_shortlong, test_auth_request_var_expand_flags, test_auth_request_var_expand_long, test_auth_request_var_expand_usernames, test_auth_request_var_expand_funcs, NULL }; default_test_request.local_ip.u.ip4.s_addr = htonl(123456789); default_test_request.remote_ip.u.ip4.s_addr = htonl(1234567890); default_test_request.real_local_ip.u.ip4.s_addr = htonl(223456788); default_test_request.real_remote_ip.u.ip4.s_addr = htonl(223456789); test_request = default_test_request; return test_run(test_functions); } dovecot-2.2.33.2/src/auth/userdb-blocking.c0000644000175000017500000000670213123174404015277 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "str.h" #include "auth-worker-server.h" #include "userdb.h" #include "userdb-blocking.h" struct blocking_userdb_iterate_context { struct userdb_iterate_context ctx; struct auth_worker_connection *conn; bool next; bool destroyed; }; static bool user_callback(const char *reply, void *context) { struct auth_request *request = context; enum userdb_result result; const char *username, *args; if (strncmp(reply, "FAIL\t", 5) == 0) { result = USERDB_RESULT_INTERNAL_FAILURE; args = reply + 5; } else if (strncmp(reply, "NOTFOUND\t", 9) == 0) { result = USERDB_RESULT_USER_UNKNOWN; args = reply + 9; } else if (strncmp(reply, "OK\t", 3) == 0) { result = USERDB_RESULT_OK; username = reply + 3; args = strchr(username, '\t'); if (args == NULL) args = ""; else username = t_strdup_until(username, args++); if (username[0] != '\0' && strcmp(request->user, username) != 0) request->user = p_strdup(request->pool, username); } else { result = USERDB_RESULT_INTERNAL_FAILURE; i_error("BUG: auth-worker sent invalid user reply"); args = ""; } if (*args != '\0') { auth_fields_import(request->userdb_reply, args, 0); if (auth_fields_exists(request->userdb_reply, "tempfail")) request->userdb_lookup_tempfailed = TRUE; } auth_request_userdb_callback(result, request); auth_request_unref(&request); return TRUE; } void userdb_blocking_lookup(struct auth_request *request) { string_t *str; str = t_str_new(128); str_printfa(str, "USER\t%u\t", request->userdb->userdb->id); auth_request_export(request, str); auth_request_ref(request); auth_worker_call(request->pool, request->user, str_c(str), user_callback, request); } static bool iter_callback(const char *reply, void *context) { struct blocking_userdb_iterate_context *ctx = context; if (strncmp(reply, "*\t", 2) == 0) { if (ctx->destroyed) return TRUE; ctx->next = FALSE; ctx->ctx.callback(reply + 2, ctx->ctx.context); return ctx->next || ctx->destroyed; } if (strcmp(reply, "OK") != 0) ctx->ctx.failed = TRUE; if (!ctx->destroyed) ctx->ctx.callback(NULL, ctx->ctx.context); auth_request_unref(&ctx->ctx.auth_request); return TRUE; } struct userdb_iterate_context * userdb_blocking_iter_init(struct auth_request *request, userdb_iter_callback_t *callback, void *context) { struct blocking_userdb_iterate_context *ctx; string_t *str; str = t_str_new(128); str_printfa(str, "LIST\t%u\t", request->userdb->userdb->id); auth_request_export(request, str); ctx = p_new(request->pool, struct blocking_userdb_iterate_context, 1); ctx->ctx.auth_request = request; ctx->ctx.callback = callback; ctx->ctx.context = context; auth_request_ref(request); ctx->conn = auth_worker_call(request->pool, "*", str_c(str), iter_callback, ctx); return &ctx->ctx; } void userdb_blocking_iter_next(struct userdb_iterate_context *_ctx) { struct blocking_userdb_iterate_context *ctx = (struct blocking_userdb_iterate_context *)_ctx; ctx->next = TRUE; auth_worker_server_resume_input(ctx->conn); } int userdb_blocking_iter_deinit(struct userdb_iterate_context **_ctx) { struct blocking_userdb_iterate_context *ctx = (struct blocking_userdb_iterate_context *)*_ctx; int ret = ctx->ctx.failed ? -1 : 0; *_ctx = NULL; /* iter_callback() may still be called */ ctx->destroyed = TRUE; auth_worker_server_resume_input(ctx->conn); return ret; } dovecot-2.2.33.2/src/auth/db-dict-cache-key.c0000644000175000017500000000276413123174404015366 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "db-dict.h" const struct db_dict_key * db_dict_set_key_find(const ARRAY_TYPE(db_dict_key) *keys, const char *name) { const struct db_dict_key *key; array_foreach(keys, key) { if (strcmp(key->name, name) == 0) return key; } return NULL; } const char * db_dict_parse_cache_key(const ARRAY_TYPE(db_dict_key) *keys, const ARRAY_TYPE(db_dict_field) *fields, const ARRAY_TYPE(db_dict_key_p) *objects) { const struct db_dict_field *field; const struct db_dict_key *key; const struct db_dict_key *const *keyp; const char *p, *name; unsigned int idx, size; string_t *str = t_str_new(128); array_foreach(fields, field) { for (p = field->value; *p != '\0'; ) { if (*p != '%') { p++; continue; } var_get_key_range(++p, &idx, &size); if (size == 0) { /* broken %variable ending too early */ break; } p += idx; if (size > 5 && memcmp(p, "dict:", 5) == 0) { name = t_strcut(t_strndup(p+5, size-5), ':'); key = db_dict_set_key_find(keys, name); if (key != NULL) str_printfa(str, "\t%s", key->key); } else if (size == 1) { str_printfa(str, "\t%%%c", p[0]); } else { str_append(str, "\t%{"); str_append_n(str, p, size); str_append_c(str, '}'); } p += size; } } array_foreach(objects, keyp) str_printfa(str, "\t%s", (*keyp)->key); return str_c(str); } dovecot-2.2.33.2/src/auth/password-scheme-rpa.c0000644000175000017500000000151313165463624016117 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "md5.h" #include "hex-binary.h" #include "safe-memset.h" #include "password-scheme.h" void *ucs2be_str(pool_t pool, const char *str, size_t *size); /* * Convert string to big-endian ucs2. */ void *ucs2be_str(pool_t pool, const char *str, size_t *size) { buffer_t *buf = buffer_create_dynamic(pool, 32); while (*str) { buffer_append_c(buf, '\0'); buffer_append_c(buf, *str++); } *size = buffer_get_used_size(buf); return buffer_free_without_data(&buf); } void password_generate_rpa(const char *pw, unsigned char result[]) { unsigned char *ucs2be_pw; size_t size; ucs2be_pw = ucs2be_str(unsafe_data_stack_pool, pw, &size); md5_get_digest(ucs2be_pw, size, result); safe_memset(ucs2be_pw, 0, size); } dovecot-2.2.33.2/src/auth/test-auth-cache.c0000644000175000017500000000331313165463624015212 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "auth-request.h" #include "auth-cache.h" #include "test-common.h" const struct var_expand_table auth_request_var_expand_static_tab[] = { /* these 3 must be in this order */ { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 'a', NULL, NULL }, { '\0', NULL, "longb" }, { 'c', NULL, "longc" }, { '\0', NULL, NULL } }; const char * t_auth_request_var_expand(const char *str, const struct auth_request *auth_request ATTR_UNUSED, auth_request_escape_func_t *escape_func ATTR_UNUSED) { string_t *dest = t_str_new(128); var_expand(dest, str, auth_request_var_expand_static_tab); return str_c(dest); } static void test_auth_cache_parse_key(void) { struct { const char *in, *out; } tests[] = { { "%n@%d", "%u" }, { "%{username}@%{domain}", "%u" }, { "%n%d%u", "%u" }, { "%n", "%n" }, { "%d", "%d" }, { "%a%b%u", "%u\t%a\t%b" }, { "foo%5.5Mabar", "%a" }, { "foo%5.5M{longb}bar", "%{longb}" }, { "foo%5.5Mcbar", "%c" }, { "foo%5.5M{longc}bar", "%c" }, { "%a%b", "%a\t%b" }, { "%a%{longb}%a", "%a\t%{longb}" }, { "%{longc}%c", "%c" }, { "%c%a%{longc}%c", "%a\t%c" }, { "%a%{env:foo}%{env:foo}%a", "%a\t%{env:foo}\t%{env:foo}" } }; const char *cache_key; unsigned int i; test_begin("auth cache parse key"); for (i = 0; i < N_ELEMENTS(tests); i++) { cache_key = auth_cache_parse_key(pool_datastack_create(), tests[i].in); test_assert(strcmp(cache_key, tests[i].out) == 0); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_auth_cache_parse_key, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/auth/mech-ntlm.c0000644000175000017500000001573713165463624014134 00000000000000/* * NTLM and NTLMv2 authentication mechanism. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "mech.h" #include "passdb.h" #include "str.h" #include "buffer.h" #include "hex-binary.h" #include "safe-memset.h" #include "ntlm.h" struct ntlm_auth_request { struct auth_request auth_request; pool_t pool; /* requested: */ bool ntlm2_negotiated; bool unicode_negotiated; const unsigned char *challenge; /* received: */ struct ntlmssp_response *response; }; static bool lm_verify_credentials(struct ntlm_auth_request *request, const unsigned char *credentials, size_t size) { const unsigned char *client_response; unsigned char lm_response[LM_RESPONSE_SIZE]; unsigned int response_length; if (size != LM_HASH_SIZE) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid LM credentials length"); return FALSE; } response_length = ntlmssp_buffer_length(request->response, lm_response); client_response = ntlmssp_buffer_data(request->response, lm_response); if (response_length < LM_RESPONSE_SIZE) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "LM response length is too small"); return FALSE; } ntlmssp_v1_response(credentials, request->challenge, lm_response); return mem_equals_timing_safe(lm_response, client_response, LM_RESPONSE_SIZE); } static void lm_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; switch (result) { case PASSDB_RESULT_OK: if (lm_verify_credentials(request, credentials, size)) auth_request_success(auth_request, "", 0); else auth_request_fail(auth_request); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static int ntlm_verify_credentials(struct ntlm_auth_request *request, const unsigned char *credentials, size_t size) { struct auth_request *auth_request = &request->auth_request; const unsigned char *client_response; unsigned int response_length; response_length = ntlmssp_buffer_length(request->response, ntlm_response); client_response = ntlmssp_buffer_data(request->response, ntlm_response); if (response_length == 0) { /* try LM authentication unless NTLM2 was negotiated */ return request->ntlm2_negotiated ? -1 : 0; } if (size != NTLMSSP_HASH_SIZE) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid NTLM credentials length"); return -1; } if (response_length > NTLMSSP_RESPONSE_SIZE) { unsigned char ntlm_v2_response[NTLMSSP_V2_RESPONSE_SIZE]; const unsigned char *blob = client_response + NTLMSSP_V2_RESPONSE_SIZE; /* * Authentication target == NULL because we are acting * as a standalone server, not as NT domain member. */ ntlmssp_v2_response(auth_request->user, NULL, credentials, request->challenge, blob, response_length - NTLMSSP_V2_RESPONSE_SIZE, ntlm_v2_response); return mem_equals_timing_safe(ntlm_v2_response, client_response, NTLMSSP_V2_RESPONSE_SIZE) ? 1 : -1; } else { unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE]; const unsigned char *client_lm_response = ntlmssp_buffer_data(request->response, lm_response); if (request->ntlm2_negotiated) ntlmssp2_response(credentials, request->challenge, client_lm_response, ntlm_response); else ntlmssp_v1_response(credentials, request->challenge, ntlm_response); return mem_equals_timing_safe(ntlm_response, client_response, NTLMSSP_RESPONSE_SIZE) ? 1 : -1; } } static void ntlm_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; int ret; switch (result) { case PASSDB_RESULT_OK: ret = ntlm_verify_credentials(request, credentials, size); if (ret > 0) { auth_request_success(auth_request, "", 0); return; } if (ret < 0) { auth_request_fail(auth_request); return; } break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); return; default: break; } /* NTLM credentials not found or didn't want to use them, try with LM credentials */ auth_request_lookup_credentials(auth_request, "LANMAN", lm_credentials_callback); } static void mech_ntlm_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; const char *error; if (!request->challenge) { const struct ntlmssp_request *ntlm_request = (const struct ntlmssp_request *)data; const struct ntlmssp_challenge *message; size_t message_size; uint32_t flags; if (!ntlmssp_check_request(ntlm_request, data_size, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid NTLM request: %s", error); auth_request_fail(auth_request); return; } message = ntlmssp_create_challenge(request->pool, ntlm_request, &message_size); flags = read_le32(&message->flags); request->ntlm2_negotiated = flags & NTLMSSP_NEGOTIATE_NTLM2; request->unicode_negotiated = flags & NTLMSSP_NEGOTIATE_UNICODE; request->challenge = message->challenge; auth_request_handler_reply_continue(auth_request, message, message_size); } else { const struct ntlmssp_response *response = (const struct ntlmssp_response *)data; const char *username; if (!ntlmssp_check_response(response, data_size, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid NTLM response: %s", error); auth_request_fail(auth_request); return; } request->response = p_malloc(request->pool, data_size); memcpy(request->response, response, data_size); username = ntlmssp_t_str(request->response, user, request->unicode_negotiated); if (!auth_request_set_username(auth_request, username, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); return; } auth_request_lookup_credentials(auth_request, "NTLM", ntlm_credentials_callback); } } static struct auth_request *mech_ntlm_auth_new(void) { struct ntlm_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"ntlm_auth_request", 2048); request = p_new(pool, struct ntlm_auth_request, 1); request->pool = pool; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_ntlm = { "NTLM", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_LOOKUP_CREDENTIALS, mech_ntlm_auth_new, mech_generic_auth_initial, mech_ntlm_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/userdb-blocking.h0000644000175000017500000000062213123174404015277 00000000000000#ifndef USERDB_BLOCKING_H #define USERDB_BLOCKING_H void userdb_blocking_lookup(struct auth_request *request); struct userdb_iterate_context * userdb_blocking_iter_init(struct auth_request *request, userdb_iter_callback_t *callback, void *context); void userdb_blocking_iter_next(struct userdb_iterate_context *ctx); int userdb_blocking_iter_deinit(struct userdb_iterate_context **ctx); #endif dovecot-2.2.33.2/src/auth/auth-stats.h0000644000175000017500000000050413123174404014321 00000000000000#ifndef AUTH_STATS_H #define AUTH_STATS_H struct auth_stats { uint32_t auth_success_count; uint32_t auth_master_success_count; uint32_t auth_failure_count; uint32_t auth_db_tempfail_count; uint32_t auth_cache_hit_count; uint32_t auth_cache_miss_count; }; extern const struct stats_vfuncs auth_stats_vfuncs; #endif dovecot-2.2.33.2/src/auth/passdb-static.c0000644000175000017500000000535313165463624015002 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #include "passdb-template.h" #include "password-scheme.h" struct static_passdb_module { struct passdb_module module; struct passdb_template *tmpl; const char *static_password_tmpl; }; static enum passdb_result static_save_fields(struct auth_request *request, const char **password_r, const char **scheme_r) { struct static_passdb_module *module = (struct static_passdb_module *)request->passdb->passdb; *password_r = NULL; *scheme_r = NULL; auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); passdb_template_export(module->tmpl, request); if (module->static_password_tmpl != NULL) { *password_r = t_auth_request_var_expand( module->static_password_tmpl, request, NULL); } else if (auth_fields_exists(request->extra_fields, "nopassword")) { *password_r = ""; } else { auth_request_log_info(request, AUTH_SUBSYS_DB, "No password returned (and no nopassword)"); return PASSDB_RESULT_PASSWORD_MISMATCH; } *scheme_r = password_get_scheme(password_r); if (*scheme_r == NULL) *scheme_r = STATIC_PASS_SCHEME; auth_request_set_field(request, "password", *password_r, *scheme_r); return PASSDB_RESULT_OK; } static void static_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { enum passdb_result result; const char *static_password; const char *static_scheme; int ret; result = static_save_fields(request, &static_password, &static_scheme); if (result != PASSDB_RESULT_OK) { callback(result, request); return; } ret = auth_request_password_verify(request, password, static_password, static_scheme, AUTH_SUBSYS_DB); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } callback(PASSDB_RESULT_OK, request); } static void static_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { enum passdb_result result; const char *static_password; const char *static_scheme; result = static_save_fields(request, &static_password, &static_scheme); passdb_handle_credentials(result, static_password, static_scheme, callback, request); } static struct passdb_module * static_preinit(pool_t pool, const char *args) { struct static_passdb_module *module; const char *value; module = p_new(pool, struct static_passdb_module, 1); module->tmpl = passdb_template_build(pool, args); if (passdb_template_remove(module->tmpl, "password", &value)) module->static_password_tmpl = value; return &module->module; } struct passdb_module_interface passdb_static = { "static", static_preinit, NULL, NULL, static_verify_plain, static_lookup_credentials, NULL }; dovecot-2.2.33.2/src/auth/mech-scram-sha1.c0000644000175000017500000002546513165463624015120 00000000000000/* * SCRAM-SHA-1 SASL authentication, see RFC-5802 * * Copyright (c) 2011-2016 Florian Zeitz * * This software is released under the MIT license. */ #include #include "auth-common.h" #include "base64.h" #include "buffer.h" #include "hmac.h" #include "sha1.h" #include "randgen.h" #include "safe-memset.h" #include "str.h" #include "strfuncs.h" #include "strnum.h" #include "password-scheme.h" #include "mech.h" /* s-nonce length */ #define SCRAM_SERVER_NONCE_LEN 64 struct scram_auth_request { struct auth_request auth_request; pool_t pool; /* sent: */ const char *server_first_message; const char *snonce; /* received: */ const char *gs2_cbind_flag; const char *cnonce; const char *client_first_message_bare; const char *client_final_message_without_proof; buffer_t *proof; /* stored */ unsigned char stored_key[SHA1_RESULTLEN]; unsigned char server_key[SHA1_RESULTLEN]; }; static const char *get_scram_server_first(struct scram_auth_request *request, int iter, const char *salt) { unsigned char snonce[SCRAM_SERVER_NONCE_LEN+1]; string_t *str; size_t i; random_fill(snonce, sizeof(snonce)-1); /* make sure snonce is printable and does not contain ',' */ for (i = 0; i < sizeof(snonce)-1; i++) { snonce[i] = (snonce[i] % ('~' - '!')) + '!'; if (snonce[i] == ',') snonce[i] = '~'; } snonce[sizeof(snonce)-1] = '\0'; request->snonce = p_strndup(request->pool, snonce, sizeof(snonce)); str = t_str_new(sizeof(snonce)); str_printfa(str, "r=%s%s,s=%s,i=%d", request->cnonce, request->snonce, salt, iter); return str_c(str); } static const char *get_scram_server_final(struct scram_auth_request *request) { struct hmac_context ctx; const char *auth_message; unsigned char server_signature[SHA1_RESULTLEN]; string_t *str; auth_message = t_strconcat(request->client_first_message_bare, ",", request->server_first_message, ",", request->client_final_message_without_proof, NULL); hmac_init(&ctx, request->server_key, sizeof(request->server_key), &hash_method_sha1); hmac_update(&ctx, auth_message, strlen(auth_message)); hmac_final(&ctx, server_signature); str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(server_signature))); str_append(str, "v="); base64_encode(server_signature, sizeof(server_signature), str); return str_c(str); } static const char *scram_unescape_username(const char *in) { string_t *out; out = t_str_new(64); for (; *in != '\0'; in++) { i_assert(in[0] != ','); /* strsplit should have caught this */ if (in[0] == '=') { if (in[1] == '2' && in[2] == 'C') str_append_c(out, ','); else if (in[1] == '3' && in[2] == 'D') str_append_c(out, '='); else return NULL; in += 2; } else { str_append_c(out, *in); } } return str_c(out); } static bool parse_scram_client_first(struct scram_auth_request *request, const unsigned char *data, size_t size, const char **error_r) { const char *const *fields, *login_username = NULL; const char *gs2_cbind_flag, *authzid, *username, *nonce; fields = t_strsplit(t_strndup(data, size), ","); if (str_array_length(fields) < 4) { *error_r = "Invalid initial client message"; return FALSE; } gs2_cbind_flag = fields[0]; authzid = fields[1]; username = fields[2]; nonce = fields[3]; /* Order of fields is fixed: client-first-message = gs2-header client-first-message-bare gs2-header = gs2-cbind-flag "," [ authzid ] "," gs2-cbind-flag = ("p=" cb-name) / "n" / "y" client-first-message-bare = [reserved-mext ","] username "," nonce ["," extensions] reserved-mext = "m=" 1*(value-char) username = "n=" saslname nonce = "r=" c-nonce [s-nonce] extensions = attr-val *("," attr-val) ;; All extensions are optional, ;; i.e., unrecognized attributes ;; not defined in this document ;; MUST be ignored. attr-val = ALPHA "=" value */ switch (gs2_cbind_flag[0]) { case 'p': *error_r = "Channel binding not supported"; return FALSE; case 'y': case 'n': request->gs2_cbind_flag = p_strdup(request->pool, gs2_cbind_flag); break; default: *error_r = "Invalid GS2 header"; return FALSE; } if (authzid[0] == '\0') ; else if (authzid[0] == 'a' && authzid[1] == '=') { /* Unescape authzid */ login_username = scram_unescape_username(authzid + 2); if (login_username == NULL) { *error_r = "authzid escaping is invalid"; return FALSE; } } else { *error_r = "Invalid authzid field"; return FALSE; } if (username[0] == 'm') { *error_r = "Mandatory extension(s) not supported"; return FALSE; } if (username[0] == 'n' && username[1] == '=') { /* Unescape username */ username = scram_unescape_username(username + 2); if (username == NULL) { *error_r = "Username escaping is invalid"; return FALSE; } if (!auth_request_set_username(&request->auth_request, username, error_r)) return FALSE; } else { *error_r = "Invalid username field"; return FALSE; } if (login_username != NULL) { if (!auth_request_set_login_username(&request->auth_request, login_username, error_r)) return FALSE; } if (nonce[0] == 'r' && nonce[1] == '=') request->cnonce = p_strdup(request->pool, nonce+2); else { *error_r = "Invalid client nonce"; return FALSE; } /* This works only without channel binding support, otherwise the GS2 header doesn't have a fixed length */ request->client_first_message_bare = p_strndup(request->pool, data + 3, size - 3); return TRUE; } static bool verify_credentials(struct scram_auth_request *request) { struct hmac_context ctx; const char *auth_message; unsigned char client_key[SHA1_RESULTLEN]; unsigned char client_signature[SHA1_RESULTLEN]; unsigned char stored_key[SHA1_RESULTLEN]; size_t i; auth_message = t_strconcat(request->client_first_message_bare, ",", request->server_first_message, ",", request->client_final_message_without_proof, NULL); hmac_init(&ctx, request->stored_key, sizeof(request->stored_key), &hash_method_sha1); hmac_update(&ctx, auth_message, strlen(auth_message)); hmac_final(&ctx, client_signature); for (i = 0; i < sizeof(client_signature); i++) client_key[i] = ((char*)request->proof->data)[i] ^ client_signature[i]; sha1_get_digest(client_key, sizeof(client_key), stored_key); safe_memset(client_key, 0, sizeof(client_key)); safe_memset(client_signature, 0, sizeof(client_signature)); return mem_equals_timing_safe(stored_key, request->stored_key, sizeof(stored_key)); } static void credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct scram_auth_request *request = (struct scram_auth_request *)auth_request; const char *salt, *error; unsigned int iter_count; switch (result) { case PASSDB_RESULT_OK: if (scram_sha1_scheme_parse(credentials, size, &iter_count, &salt, request->stored_key, request->server_key, &error) < 0) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); break; } request->server_first_message = p_strdup(request->pool, get_scram_server_first(request, iter_count, salt)); auth_request_handler_reply_continue(auth_request, request->server_first_message, strlen(request->server_first_message)); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static bool parse_scram_client_final(struct scram_auth_request *request, const unsigned char *data, size_t size, const char **error_r) { const char **fields, *cbind_input, *nonce_str; unsigned int field_count; string_t *str; fields = t_strsplit(t_strndup(data, size), ","); field_count = str_array_length(fields); if (field_count < 3) { *error_r = "Invalid final client message"; return FALSE; } cbind_input = t_strconcat(request->gs2_cbind_flag, ",,", NULL); str = t_str_new(MAX_BASE64_ENCODED_SIZE(strlen(cbind_input))); str_append(str, "c="); base64_encode(cbind_input, strlen(cbind_input), str); if (strcmp(fields[0], str_c(str)) != 0) { *error_r = "Invalid channel binding data"; return FALSE; } nonce_str = t_strconcat("r=", request->cnonce, request->snonce, NULL); if (strcmp(fields[1], nonce_str) != 0) { *error_r = "Wrong nonce"; return FALSE; } if (fields[field_count-1][0] == 'p') { size_t len = strlen(&fields[field_count-1][2]); request->proof = buffer_create_dynamic(request->pool, MAX_BASE64_DECODED_SIZE(len)); if (base64_decode(&fields[field_count-1][2], len, NULL, request->proof) < 0) { *error_r = "Invalid base64 encoding"; return FALSE; } if (request->proof->used != SHA1_RESULTLEN) { *error_r = "Invalid ClientProof length"; return FALSE; } } else { *error_r = "Invalid ClientProof"; return FALSE; } (void)str_array_remove(fields, fields[field_count-1]); request->client_final_message_without_proof = p_strdup(request->pool, t_strarray_join(fields, ",")); return TRUE; } static void mech_scram_sha1_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct scram_auth_request *request = (struct scram_auth_request *)auth_request; const char *error = NULL; const char *server_final_message; size_t len; if (!request->client_first_message_bare) { /* Received client-first-message */ if (parse_scram_client_first(request, data, data_size, &error)) { auth_request_lookup_credentials(&request->auth_request, "SCRAM-SHA-1", credentials_callback); return; } } else { /* Received client-final-message */ if (parse_scram_client_final(request, data, data_size, &error)) { if (!verify_credentials(request)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "password mismatch"); } else { server_final_message = get_scram_server_final(request); len = strlen(server_final_message); auth_request_success(auth_request, server_final_message, len); return; } } } if (error != NULL) auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); } static struct auth_request *mech_scram_sha1_auth_new(void) { struct scram_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"scram_sha1_auth_request", 2048); request = p_new(pool, struct scram_auth_request, 1); request->pool = pool; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_scram_sha1 = { "SCRAM-SHA-1", .flags = MECH_SEC_MUTUAL_AUTH, .passdb_need = MECH_PASSDB_NEED_LOOKUP_CREDENTIALS, mech_scram_sha1_auth_new, mech_generic_auth_initial, mech_scram_sha1_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/auth-policy.c0000755000175000017500000003737713165463624014515 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "istream.h" #include "ioloop.h" #include "base64.h" #include "hex-binary.h" #include "hash-method.h" #include "http-url.h" #include "http-client.h" #include "json-parser.h" #include "auth-request.h" #include "auth-penalty.h" #include "auth-settings.h" #include "auth-policy.h" #include "iostream-ssl.h" #define AUTH_POLICY_DNS_SOCKET_PATH "dns-client" static struct http_client_settings http_client_set = { .dns_client_socket_path = AUTH_POLICY_DNS_SOCKET_PATH, .max_connect_attempts = 1, .max_idle_time_msecs = 10000, .max_parallel_connections = 100, .debug = 0, .user_agent = "dovecot/auth-policy-client" }; static char *auth_policy_json_template; static struct http_client *http_client; struct policy_lookup_ctx { pool_t pool; string_t *json; struct auth_request *request; struct http_client_request *http_request; struct json_parser *parser; const struct auth_settings *set; const char *url; bool expect_result; int result; const char *message; auth_policy_callback_t callback; void *callback_context; struct istream *payload; struct io *io; enum { POLICY_RESULT = 0, POLICY_RESULT_VALUE_STATUS, POLICY_RESULT_VALUE_MESSAGE } parse_state; bool parse_error; }; struct policy_template_keyvalue { const char *key; const char *value; }; static int auth_policy_attribute_comparator(const struct policy_template_keyvalue *a, const struct policy_template_keyvalue *b) { return strcmp(a->key, b->key); } static int auth_policy_strptrcmp(const char *a0, const char *a1, const char *b0, const char *b1) { i_assert(a0 <= a1 && b0 <= b1); return memcmp(a0, b0, I_MIN((a1-a0),(b1-b0))); } static void auth_policy_open_key(const char *key, string_t *template) { const char *ptr; while((ptr = strchr(key, '/')) != NULL) { str_append_c(template,'"'); json_append_escaped(template, t_strndup(key, (ptr-key))); str_append_c(template,'"'); str_append_c(template,':'); str_append_c(template,'{'); key = ptr+1; } } static void auth_policy_close_key(const char *key, string_t *template) { while((key = strchr(key, '/')) != NULL) { str_append_c(template,'}'); key++; } } static void auth_policy_open_and_close_to_key(const char *fromkey, const char *tokey, string_t *template) { const char *fptr,*tptr,*fdash,*tdash; fptr = strrchr(fromkey, '/'); tptr = strrchr(tokey, '/'); if (fptr == NULL && tptr == NULL) return; /* nothing to do */ if (fptr == NULL && tptr != NULL) { auth_policy_open_key(tokey, template); return; } if (fptr != NULL && tptr == NULL) { str_truncate(template, str_len(template)-1); auth_policy_close_key(fromkey, template); str_append_c(template, ','); return; } if (auth_policy_strptrcmp(fromkey, fptr, tokey, tptr) == 0) { /* nothing to do, again */ return; } fptr = fromkey; tptr = tokey; while (fptr != NULL && tptr != NULL) { fdash = strchr(fptr, '/'); tdash = strchr(tptr, '/'); if (fdash == NULL) { auth_policy_open_key(tptr, template); break; } if (tdash == NULL) { str_truncate(template, str_len(template)-1); auth_policy_close_key(fptr, template); str_append_c(template, ','); break; } if (auth_policy_strptrcmp(fptr, fdash, tptr, tdash) != 0) { str_truncate(template, str_len(template)-1); auth_policy_close_key(fptr, template); str_append_c(template, ','); auth_policy_open_key(tptr, template); break; } fptr = fdash+1; tptr = tdash+1; } } void auth_policy_init(void) { http_client_set.request_absolute_timeout_msecs = global_auth_settings->policy_server_timeout_msecs; if (global_auth_settings->debug) http_client_set.debug = 1; http_client_set.ssl_ca_dir = global_auth_settings->ssl_client_ca_dir; http_client_set.ssl_ca_file = global_auth_settings->ssl_client_ca_file; if (*http_client_set.ssl_ca_dir == '\0' && *http_client_set.ssl_ca_file == '\0') http_client_set.ssl_allow_invalid_cert = TRUE; http_client = http_client_init(&http_client_set); /* prepare template */ ARRAY(struct policy_template_keyvalue) attribute_pairs; const struct policy_template_keyvalue *kvptr; string_t *template = t_str_new(64); const char **ptr; const char *key = NULL; const char **list = t_strsplit_spaces(global_auth_settings->policy_request_attributes, "= "); t_array_init(&attribute_pairs, 8); for(ptr = list; *ptr != NULL; ptr++) { struct policy_template_keyvalue pair; if (key == NULL) { key = *ptr; } else { pair.key = key; pair.value = *ptr; key = NULL; array_append(&attribute_pairs, &pair, 1); } } if (key != NULL) { i_fatal("auth_policy_request_attributes contains invalid value"); } /* then we sort it */ array_sort(&attribute_pairs, auth_policy_attribute_comparator); /* and build a template string */ const char *prevkey = ""; array_foreach(&attribute_pairs, kvptr) { const char *kptr = strchr(kvptr->key, '/'); auth_policy_open_and_close_to_key(prevkey, kvptr->key, template); str_append_c(template,'"'); json_append_escaped(template, (kptr != NULL?kptr+1:kvptr->key)); str_append_c(template,'"'); str_append_c(template,':'); str_append_c(template,'"'); str_append(template,kvptr->value); str_append_c(template,'"'); str_append_c(template,','); prevkey = kvptr->key; } auth_policy_open_and_close_to_key(prevkey, "", template); str_truncate(template, str_len(template)-1); auth_policy_json_template = i_strdup(str_c(template)); } void auth_policy_deinit(void) { if (http_client != NULL) http_client_deinit(&http_client); i_free(auth_policy_json_template); } static void auth_policy_finish(void *ctx) { struct policy_lookup_ctx *context = ctx; if (context->parser != NULL) { const char *error ATTR_UNUSED; (void)json_parser_deinit(&(context->parser), &error); } if (context->http_request != NULL) http_client_request_abort(&(context->http_request)); if (context->request != NULL) auth_request_unref(&context->request); } static void auth_policy_parse_response(struct policy_lookup_ctx *context) { enum json_type type; const char *value; int ret; while((ret = json_parse_next(context->parser, &type, &value)) == 1) { if (context->parse_state == POLICY_RESULT) { if (type != JSON_TYPE_OBJECT_KEY) continue; else if (strcmp(value, "status") == 0) context->parse_state = POLICY_RESULT_VALUE_STATUS; else if (strcmp(value, "msg") == 0) context->parse_state = POLICY_RESULT_VALUE_MESSAGE; else continue; } else if (context->parse_state == POLICY_RESULT_VALUE_STATUS) { if (type != JSON_TYPE_NUMBER || str_to_int(value, &(context->result)) != 0) break; context->parse_state = POLICY_RESULT; } else if (context->parse_state == POLICY_RESULT_VALUE_MESSAGE) { if (type != JSON_TYPE_STRING) break; if (*value != '\0') context->message = p_strdup(context->pool, value); context->parse_state = POLICY_RESULT; } else { break; } } if (ret == 0 && !context->payload->eof) return; context->parse_error = TRUE; io_remove(&(context->io)); if (context->payload->stream_errno != 0) { auth_request_log_error(context->request, "policy", "Error reading policy server result: %s", i_stream_get_error(context->payload)); } else if (ret == 0 && context->payload->eof) { auth_request_log_error(context->request, "policy", "Policy server result was too short"); } else if (ret == 1) { auth_request_log_error(context->request, "policy", "Policy server response was malformed"); } else { const char *error = "unknown"; if (json_parser_deinit(&(context->parser), &error) != 0) auth_request_log_error(context->request, "policy", "Policy server response JSON parse error: %s", error); else if (context->parse_state == POLICY_RESULT) context->parse_error = FALSE; } i_stream_unref(&(context->payload)); if (context->parse_error) { context->result = (context->set->policy_reject_on_fail ? -1 : 0); } context->request->policy_refusal = FALSE; if (context->result < 0) { if (context->message != NULL) { /* set message here */ auth_request_log_debug(context->request, "policy", "Policy response %d with message: %s", context->result, context->message); auth_request_set_field(context->request, "reason", context->message, NULL); } context->request->policy_refusal = TRUE; } else { auth_request_log_debug(context->request, "policy", "Policy response %d", context->result); } if (context->request->policy_refusal == TRUE && context->set->verbose == TRUE) { auth_request_log_info(context->request, "policy", "Authentication failure due to policy server refusal%s%s", (context->message!=NULL?": ":""), (context->message!=NULL?context->message:"")); } if (context->callback != NULL) { context->callback(context->result, context->callback_context); } } static void auth_policy_process_response(const struct http_response *response, void *ctx) { struct policy_lookup_ctx *context = ctx; context->payload = response->payload; if ((response->status / 10) != 20) { auth_request_log_error(context->request, "policy", "Policy server HTTP error: %s", http_response_get_message(response)); if (context->callback != NULL) context->callback(context->result, context->callback_context); return; } if (response->payload == NULL) { if (context->expect_result) auth_request_log_error(context->request, "policy", "Policy server result was empty"); if (context->callback != NULL) context->callback(context->result, context->callback_context); return; } if (context->expect_result) { i_stream_ref(response->payload); context->io = io_add_istream(response->payload, auth_policy_parse_response, context); context->parser = json_parser_init(response->payload); auth_policy_parse_response(ctx); } else { auth_request_log_debug(context->request, "policy", "Policy response %d", context->result); if (context->callback != NULL) context->callback(context->result, context->callback_context); } } static void auth_policy_send_request(struct policy_lookup_ctx *context) { const char *error; struct http_url *url; if (http_url_parse(context->url, NULL, HTTP_URL_ALLOW_USERINFO_PART, context->pool, &url, &error) != 0) { auth_request_log_error(context->request, "policy", "Could not parse url %s: %s", context->url, error); auth_policy_finish(context); return; } context->http_request = http_client_request_url(http_client, "POST", url, auth_policy_process_response, (void*)context); http_client_request_set_destroy_callback(context->http_request, auth_policy_finish, context); http_client_request_add_header(context->http_request, "Content-Type", "application/json"); if (*context->set->policy_server_api_header != 0) { const char *ptr; if ((ptr = strstr(context->set->policy_server_api_header, ":")) != NULL) { const char *header = t_strcut(context->set->policy_server_api_header, ':'); http_client_request_add_header(context->http_request, header, ptr + 1); } else { http_client_request_add_header(context->http_request, "X-API-Key", context->set->policy_server_api_header); } } if (url->user != NULL) { /* allow empty password */ http_client_request_set_auth_simple(context->http_request, url->user, (url->password != NULL ? url->password : "")); } struct istream *is = i_stream_create_from_buffer(context->json); http_client_request_set_payload(context->http_request, is, FALSE); i_stream_unref(&is); http_client_request_submit(context->http_request); auth_request_ref(context->request); } static const char *auth_policy_escape_function(const char *string, const struct auth_request *auth_request ATTR_UNUSED) { string_t *tmp = t_str_new(64); json_append_escaped(tmp, string); return str_c(tmp); } static const struct var_expand_table *policy_get_var_expand_table(struct auth_request *auth_request, const char *hashed_password) { struct var_expand_table *table; unsigned int count = 1; table = auth_request_get_var_expand_table_full(auth_request, auth_policy_escape_function, &count); table[0].key = '\0'; table[0].long_key = "hashed_password"; table[0].value = hashed_password; if (table[0].value != NULL) table[0].value = auth_policy_escape_function(table[0].value, auth_request); return table; } static void auth_policy_create_json(struct policy_lookup_ctx *context, const char *password, bool include_success) { const struct var_expand_table *var_table; context->json = str_new(context->pool, 64); unsigned char *ptr; const struct hash_method *digest = hash_method_lookup(context->set->policy_hash_mech); i_assert(digest != NULL); void *ctx = t_malloc(digest->context_size); buffer_t *buffer = buffer_create_dynamic(pool_datastack_create(), 64); digest->init(ctx); digest->loop(ctx, context->set->policy_hash_nonce, strlen(context->set->policy_hash_nonce)); /* use +1 to make sure \0 gets included */ if (context->request->user == NULL) digest->loop(ctx, "\0", 1); else digest->loop(ctx, context->request->user, strlen(context->request->user) + 1); if (password != NULL) digest->loop(ctx, password, strlen(password)); ptr = buffer_get_modifiable_data(buffer, NULL); digest->result(ctx, ptr); buffer_set_used_size(buffer, digest->digest_size); if (context->set->policy_hash_truncate > 0) { buffer_truncate_rshift_bits(buffer, context->set->policy_hash_truncate); } const char *hashed_password = binary_to_hex(buffer->data, buffer->used); str_append_c(context->json, '{'); var_table = policy_get_var_expand_table(context->request, hashed_password); auth_request_var_expand_with_table(context->json, auth_policy_json_template, context->request, var_table, auth_policy_escape_function); if (include_success) { str_append(context->json, ",\"success\":"); if (!context->request->failed && context->request->successful && !context->request->internal_failure) str_append(context->json, "true"); else str_append(context->json, "false"); str_append(context->json, ",\"policy_reject\":"); str_append(context->json, context->request->policy_refusal ? "true" : "false"); } str_append_c(context->json, '}'); auth_request_log_debug(context->request, "policy", "Policy server request JSON: %s", str_c(context->json)); } static void auth_policy_url(struct policy_lookup_ctx *context, const char *command) { size_t len = strlen(context->set->policy_server_url); if (context->set->policy_server_url[len-1] == '&') context->url = p_strdup_printf(context->pool, "%scommand=%s", context->set->policy_server_url, command); else context->url = p_strdup_printf(context->pool, "%s?command=%s", context->set->policy_server_url, command); } void auth_policy_check(struct auth_request *request, const char *password, auth_policy_callback_t cb, void *context) { if (request->master != NULL || *(request->set->policy_server_url) == '\0') { cb(0, context); return; } struct policy_lookup_ctx *ctx = p_new(request->pool, struct policy_lookup_ctx, 1); ctx->pool = request->pool; ctx->request = request; ctx->expect_result = TRUE; ctx->callback = cb; ctx->callback_context = context; ctx->set = request->set; auth_policy_url(ctx, "allow"); ctx->result = (ctx->set->policy_reject_on_fail ? -1 : 0); auth_request_log_debug(request, "policy", "Policy request %s", ctx->url); T_BEGIN { auth_policy_create_json(ctx, password, FALSE); } T_END; auth_policy_send_request(ctx); } void auth_policy_report(struct auth_request *request) { if (request->master != NULL) return; if (*(request->set->policy_server_url) == '\0') return; struct policy_lookup_ctx *ctx = p_new(request->pool, struct policy_lookup_ctx, 1); ctx->pool = request->pool; ctx->request = request; ctx->expect_result = FALSE; ctx->set = request->set; auth_policy_url(ctx, "report"); auth_request_log_debug(request, "policy", "Policy request %s", ctx->url); T_BEGIN { auth_policy_create_json(ctx, request->mech_password, TRUE); } T_END; auth_policy_send_request(ctx); } dovecot-2.2.33.2/src/auth/main.c0000644000175000017500000002445113165463624013165 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "ioloop.h" #include "net.h" #include "lib-signals.h" #include "restrict-access.h" #include "child-wait.h" #include "sql-api.h" #include "module-dir.h" #include "hostpid.h" #include "randgen.h" #include "process-title.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "master-interface.h" #include "dict.h" #include "password-scheme.h" #include "passdb-cache.h" #include "mech.h" #include "auth.h" #include "auth-penalty.h" #include "auth-token.h" #include "auth-request-handler.h" #include "auth-request-stats.h" #include "auth-worker-server.h" #include "auth-worker-client.h" #include "auth-master-connection.h" #include "auth-client-connection.h" #include "auth-postfix-connection.h" #include "auth-policy.h" #include #include #define AUTH_PENALTY_ANVIL_PATH "anvil-auth-penalty" enum auth_socket_type { AUTH_SOCKET_UNKNOWN = 0, AUTH_SOCKET_CLIENT, AUTH_SOCKET_LOGIN_CLIENT, AUTH_SOCKET_MASTER, AUTH_SOCKET_USERDB, AUTH_SOCKET_POSTFIX, AUTH_SOCKET_TOKEN, AUTH_SOCKET_TOKEN_LOGIN }; struct auth_socket_listener { enum auth_socket_type type; struct stat st; char *path; }; bool worker = FALSE, worker_restart_request = FALSE; time_t process_start_time; struct auth_penalty *auth_penalty; static pool_t auth_set_pool; static struct module *modules = NULL; static struct mechanisms_register *mech_reg; static ARRAY(struct auth_socket_listener) listeners; void auth_refresh_proctitle(void) { if (!global_auth_settings->verbose_proctitle || worker) return; process_title_set(t_strdup_printf( "[%u wait, %u passdb, %u userdb]", auth_request_state_count[AUTH_REQUEST_STATE_NEW] + auth_request_state_count[AUTH_REQUEST_STATE_MECH_CONTINUE] + auth_request_state_count[AUTH_REQUEST_STATE_FINISHED], auth_request_state_count[AUTH_REQUEST_STATE_PASSDB], auth_request_state_count[AUTH_REQUEST_STATE_USERDB])); } static const char *const *read_global_settings(void) { struct master_service_settings_output set_output; const char **services; unsigned int i, count; auth_set_pool = pool_alloconly_create("auth settings", 8192); global_auth_settings = auth_settings_read(NULL, auth_set_pool, &set_output); /* strdup() the service names, because they're allocated from set parser pool, and we'll later clear it. */ count = str_array_length(set_output.specific_services); services = p_new(auth_set_pool, const char *, count + 1); for (i = 0; i < count; i++) { services[i] = p_strdup(auth_set_pool, set_output.specific_services[i]); } return services; } static enum auth_socket_type auth_socket_type_get(const char *path) { const char *name, *suffix; name = strrchr(path, '/'); if (name == NULL) name = path; else name++; suffix = strrchr(name, '-'); if (suffix == NULL) suffix = name; else suffix++; if (strcmp(suffix, "login") == 0) return AUTH_SOCKET_LOGIN_CLIENT; else if (strcmp(suffix, "master") == 0) return AUTH_SOCKET_MASTER; else if (strcmp(suffix, "userdb") == 0) return AUTH_SOCKET_USERDB; else if (strcmp(suffix, "postmap") == 0) return AUTH_SOCKET_POSTFIX; else if (strcmp(suffix, "token") == 0) return AUTH_SOCKET_TOKEN; else if (strcmp(suffix, "tokenlogin") == 0) return AUTH_SOCKET_TOKEN_LOGIN; else return AUTH_SOCKET_CLIENT; } static void listeners_init(void) { unsigned int i, n; const char *path; i_array_init(&listeners, 8); n = master_service_get_socket_count(master_service); for (i = 0; i < n; i++) { int fd = MASTER_LISTEN_FD_FIRST + i; struct auth_socket_listener *l; l = array_idx_modifiable(&listeners, fd); if (net_getunixname(fd, &path) < 0) { if (errno != ENOTSOCK) i_fatal("getunixname(%d) failed: %m", fd); /* not a unix socket, set its name and type lazily */ } else { l->type = auth_socket_type_get(path); l->path = i_strdup(path); if (l->type == AUTH_SOCKET_USERDB) { if (stat(path, &l->st) < 0) i_error("stat(%s) failed: %m", path); } } } } static bool auth_module_filter(const char *name, void *context ATTR_UNUSED) { if (strncmp(name, "authdb_", 7) == 0 || strncmp(name, "mech_", 5) == 0) { /* this is lazily loaded */ return FALSE; } return TRUE; } static void main_preinit(void) { struct module_dir_load_settings mod_set; const char *const *services; /* Open /dev/urandom before chrooting */ random_init(); /* Load built-in SQL drivers (if any) */ sql_drivers_init(); sql_drivers_register_all(); /* Initialize databases so their configuration files can be readable only by root. Also load all modules here. */ passdbs_init(); userdbs_init(); /* init schemes before plugins are loaded */ password_schemes_init(); services = read_global_settings(); i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = global_auth_settings->debug; mod_set.filter_callback = auth_module_filter; modules = module_dir_load(AUTH_MODULE_DIR, NULL, &mod_set); module_dir_init(modules); if (!worker) auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH); auth_request_stats_init(); mech_init(global_auth_settings); mech_reg = mech_register_init(global_auth_settings); dict_drivers_register_builtin(); auths_preinit(global_auth_settings, auth_set_pool, mech_reg, services); listeners_init(); if (!worker) auth_token_init(); /* Password lookups etc. may require roots, allow it. */ restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } void auth_module_load(const char *names) { struct module_dir_load_settings mod_set; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; mod_set.debug = global_auth_settings->debug; mod_set.ignore_missing = TRUE; modules = module_dir_load_missing(modules, AUTH_MODULE_DIR, names, &mod_set); module_dir_init(modules); } static void main_init(void) { process_start_time = ioloop_time; /* If auth caches aren't used, just ignore these signals */ lib_signals_ignore(SIGHUP, TRUE); lib_signals_ignore(SIGUSR2, TRUE); /* set proctitles before init()s, since they may set them to error */ auth_refresh_proctitle(); auth_worker_refresh_proctitle(""); child_wait_init(); auth_worker_server_init(); auths_init(); auth_request_handler_init(); auth_policy_init(); if (worker) { /* workers have only a single connection from the master auth process */ master_service_set_client_limit(master_service, 1); } else { /* caching is handled only by the main auth process */ passdb_cache_init(global_auth_settings); } } static void main_deinit(void) { struct auth_socket_listener *l; if (auth_penalty != NULL) { /* cancel all pending anvil penalty lookups */ auth_penalty_deinit(&auth_penalty); } auth_policy_deinit(); /* deinit auth workers, which aborts pending requests */ auth_worker_server_deinit(); /* deinit passdbs and userdbs. it aborts any pending async requests. */ auths_deinit(); /* flush pending requests */ auth_request_handler_deinit(); /* there are no more auth requests */ auths_free(); dict_drivers_unregister_builtin(); auth_token_deinit(); auth_client_connections_destroy_all(); auth_master_connections_destroy_all(); auth_postfix_connections_destroy_all(); if (auth_worker_client != NULL) auth_worker_client_destroy(&auth_worker_client); mech_register_deinit(&mech_reg); mech_deinit(global_auth_settings); /* allow modules to unregister their dbs/drivers/etc. before freeing the whole data structures containing them. */ module_dir_unload(&modules); userdbs_deinit(); passdbs_deinit(); passdb_cache_deinit(); password_schemes_deinit(); auth_request_stats_deinit(); sql_drivers_deinit(); random_deinit(); child_wait_deinit(); array_foreach_modifiable(&listeners, l) i_free(l->path); array_free(&listeners); pool_unref(&auth_set_pool); } static void worker_connected(struct master_service_connection *conn) { if (auth_worker_client != NULL) { i_error("Auth workers can handle only a single client"); return; } master_service_client_connection_accept(conn); (void)auth_worker_client_create(auth_default_service(), conn->fd); } static void client_connected(struct master_service_connection *conn) { struct auth_socket_listener *l; struct auth *auth; l = array_idx_modifiable(&listeners, conn->listen_fd); if (l->type == AUTH_SOCKET_UNKNOWN) { /* first connection from inet socket, figure out its type from the listener name */ l->type = auth_socket_type_get(conn->name); l->path = i_strdup(conn->name); } auth = auth_default_service(); switch (l->type) { case AUTH_SOCKET_MASTER: (void)auth_master_connection_create(auth, conn->fd, l->path, NULL, FALSE); break; case AUTH_SOCKET_USERDB: (void)auth_master_connection_create(auth, conn->fd, l->path, &l->st, TRUE); break; case AUTH_SOCKET_POSTFIX: (void)auth_postfix_connection_create(auth, conn->fd); break; case AUTH_SOCKET_LOGIN_CLIENT: auth_client_connection_create(auth, conn->fd, TRUE, FALSE); break; case AUTH_SOCKET_CLIENT: auth_client_connection_create(auth, conn->fd, FALSE, FALSE); break; case AUTH_SOCKET_TOKEN_LOGIN: auth_client_connection_create(auth, conn->fd, TRUE, TRUE); break; case AUTH_SOCKET_TOKEN: auth_client_connection_create(auth, conn->fd, FALSE, TRUE); break; default: i_unreached(); } master_service_client_connection_accept(conn); } static void auth_die(void) { if (!worker) { /* do nothing. auth clients should disconnect soon. */ } else { /* ask auth master to disconnect us */ auth_worker_client_send_shutdown(); } } int main(int argc, char *argv[]) { int c; master_service = master_service_init("auth", 0, &argc, &argv, "w"); master_service_init_log(master_service, "auth: "); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 'w': master_service_init_log(master_service, t_strdup_printf("auth-worker(%s): ", my_pid)); worker = TRUE; break; default: return FATAL_DEFAULT; } } main_preinit(); master_service_set_die_callback(master_service, auth_die); main_init(); master_service_init_finish(master_service); master_service_run(master_service, worker ? worker_connected : client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/auth/auth-request-handler.h0000644000175000017500000000406013123174404016267 00000000000000#ifndef AUTH_REQUEST_HANDLER_H #define AUTH_REQUEST_HANDLER_H struct auth_request; struct auth_client_connection; struct auth_master_connection; struct auth_stream_reply; enum auth_client_result { AUTH_CLIENT_RESULT_CONTINUE = 1, AUTH_CLIENT_RESULT_SUCCESS, AUTH_CLIENT_RESULT_FAILURE }; typedef void auth_client_request_callback_t(const char *reply, struct auth_client_connection *conn); typedef void auth_master_request_callback_t(const char *reply, struct auth_master_connection *conn); struct auth_request_handler * auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback, struct auth_client_connection *conn, auth_master_request_callback_t *master_callback); void auth_request_handler_destroy(struct auth_request_handler **handler); void auth_request_handler_unref(struct auth_request_handler **handler); void auth_request_handler_abort_requests(struct auth_request_handler *handler); void auth_request_handler_set(struct auth_request_handler *handler, unsigned int connect_uid, unsigned int client_pid); bool auth_request_handler_auth_begin(struct auth_request_handler *handler, const char *args); bool auth_request_handler_auth_continue(struct auth_request_handler *handler, const char *args); void auth_request_handler_reply(struct auth_request *request, enum auth_client_result result, const void *reply, size_t reply_size); void auth_request_handler_reply_continue(struct auth_request *request, const void *reply, size_t reply_size); unsigned int auth_request_handler_get_request_count(struct auth_request_handler *handler); bool auth_request_handler_master_request(struct auth_request_handler *handler, struct auth_master_connection *master, unsigned int id, unsigned int client_id, const char *const *params); void auth_request_handler_cancel_request(struct auth_request_handler *handler, unsigned int client_id); void auth_request_handler_flush_failures(bool flush_all); void auth_request_handler_init(void); void auth_request_handler_deinit(void); #endif dovecot-2.2.33.2/src/auth/mech-cram-md5.c0000644000175000017500000001124213123174404014537 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* CRAM-MD5 SASL authentication, see RFC-2195 Joshua Goodall */ #include "auth-common.h" #include "ioloop.h" #include "buffer.h" #include "hex-binary.h" #include "hmac-cram-md5.h" #include "hmac.h" #include "md5.h" #include "randgen.h" #include "mech.h" #include "passdb.h" #include "hostpid.h" #include struct cram_auth_request { struct auth_request auth_request; pool_t pool; /* requested: */ char *challenge; /* received: */ char *username; char *response; unsigned long maxbuf; }; static const char *get_cram_challenge(void) { unsigned char buf[17]; size_t i; random_fill(buf, sizeof(buf)-1); for (i = 0; i < sizeof(buf)-1; i++) buf[i] = (buf[i] % 10) + '0'; buf[sizeof(buf)-1] = '\0'; return t_strdup_printf("<%s.%s@%s>", (const char *)buf, dec2str(ioloop_time), my_hostname); } static bool verify_credentials(struct cram_auth_request *request, const unsigned char *credentials, size_t size) { unsigned char digest[MD5_RESULTLEN]; struct hmac_context ctx; const char *response_hex; if (size != CRAM_MD5_CONTEXTLEN) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid credentials length"); return FALSE; } hmac_init(&ctx, NULL, 0, &hash_method_md5); hmac_md5_set_cram_context(&ctx, credentials); hmac_update(&ctx, request->challenge, strlen(request->challenge)); hmac_final(&ctx, digest); response_hex = binary_to_hex(digest, sizeof(digest)); if (!mem_equals_timing_safe(response_hex, request->response, sizeof(digest)*2)) { auth_request_log_info(&request->auth_request, AUTH_SUBSYS_MECH, "password mismatch"); return FALSE; } return TRUE; } static bool parse_cram_response(struct cram_auth_request *request, const unsigned char *data, size_t size, const char **error_r) { size_t i, space; *error_r = NULL; /* SPACE . Username may contain spaces, so assume the rightmost space is the response separator. */ for (i = space = 0; i < size; i++) { if (data[i] == '\0') { *error_r = "NULs in response"; return FALSE; } if (data[i] == ' ') space = i; } if (space == 0) { *error_r = "missing digest"; return FALSE; } request->username = p_strndup(request->pool, data, space); space++; request->response = p_strndup(request->pool, data + space, size - space); return TRUE; } static void credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct cram_auth_request *request = (struct cram_auth_request *)auth_request; switch (result) { case PASSDB_RESULT_OK: if (verify_credentials(request, credentials, size)) auth_request_success(auth_request, "", 0); else auth_request_fail(auth_request); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void mech_cram_md5_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct cram_auth_request *request = (struct cram_auth_request *)auth_request; const char *error; if (parse_cram_response(request, data, data_size, &error)) { if (auth_request_set_username(auth_request, request->username, &error)) { auth_request_lookup_credentials(auth_request, "CRAM-MD5", credentials_callback); return; } } if (error == NULL) error = "authentication failed"; auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); } static void mech_cram_md5_auth_initial(struct auth_request *auth_request, const unsigned char *data ATTR_UNUSED, size_t data_size ATTR_UNUSED) { struct cram_auth_request *request = (struct cram_auth_request *)auth_request; request->challenge = p_strdup(request->pool, get_cram_challenge()); auth_request_handler_reply_continue(auth_request, request->challenge, strlen(request->challenge)); } static struct auth_request *mech_cram_md5_auth_new(void) { struct cram_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"cram_md5_auth_request", 2048); request = p_new(pool, struct cram_auth_request, 1); request->pool = pool; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_cram_md5 = { "CRAM-MD5", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_VERIFY_RESPONSE, mech_cram_md5_auth_new, mech_cram_md5_auth_initial, mech_cram_md5_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/password-scheme-pbkdf2.c0000644000175000017500000000453713165463624016516 00000000000000/* Copyright (c) 2015-2017 Dovecot Oy, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "password-scheme.h" #include "hex-binary.h" #include "hash-method.h" #include "pkcs5.h" #define PBKDF2_KEY_SIZE_SHA1 20 #define PBKDF2_GENERATE_SALT_LEN 16 #define PBKDF2_ROUNDS_DEFAULT 5000 static void pbkdf_run(const char *plaintext, const char *salt, unsigned int rounds, unsigned char key_r[PBKDF2_KEY_SIZE_SHA1]) { memset(key_r, 0, PBKDF2_KEY_SIZE_SHA1); buffer_t buf; buffer_create_from_data(&buf, key_r, PBKDF2_KEY_SIZE_SHA1); pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha1"), (const unsigned char *)plaintext, strlen(plaintext), (const unsigned char *)salt, strlen(salt), rounds, PBKDF2_KEY_SIZE_SHA1, &buf); } void pbkdf2_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { unsigned char key[PBKDF2_KEY_SIZE_SHA1]; const char *salt; string_t *str = t_str_new(64); unsigned int rounds = password_scheme_encryption_rounds; if (rounds == 0) rounds = PBKDF2_ROUNDS_DEFAULT; salt = password_generate_salt(PBKDF2_GENERATE_SALT_LEN); pbkdf_run(plaintext, salt, rounds, key); str_printfa(str, "$1$%s$%u$", salt, rounds); binary_to_hex_append(str, key, sizeof(key)); *raw_password_r = str_data(str); *size_r = str_len(str); } int pbkdf2_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r) { const char *const *fields; const char *salt; unsigned int rounds; unsigned char key1[PBKDF2_KEY_SIZE_SHA1], key2[PBKDF2_KEY_SIZE_SHA1]; buffer_t buf; /* $1$salt$rounds$hash */ if (size < 3 || memcmp(raw_password, "$1$", 3) != 0) { *error_r = "Invalid PBKDF2 passdb entry prefix"; return -1; } fields = t_strsplit(t_strndup(raw_password + 3, size - 3), "$"); salt = fields[0]; if (str_array_length(fields) != 3 || str_to_uint(fields[1], &rounds) < 0) { *error_r = "Invalid PBKDF2 passdb entry format"; return -1; } buffer_create_from_data(&buf, key1, sizeof(key1)); if (strlen(fields[2]) != sizeof(key1)*2 || hex_to_binary(fields[2], &buf) < 0) { *error_r = "PBKDF2 hash not 160bit hex-encoded"; return -1; } pbkdf_run(plaintext, salt, rounds, key2); return mem_equals_timing_safe(key1, key2, sizeof(key1)) ? 1 : 0; } dovecot-2.2.33.2/src/auth/passdb-template.h0000644000175000017500000000103513165463624015324 00000000000000#ifndef PASSDB_TEMPLATE_H #define PASSDB_TEMPLATE_H #define STATIC_PASS_SCHEME "PLAIN" struct passdb_template *passdb_template_build(pool_t pool, const char *args); void passdb_template_export(struct passdb_template *tmpl, struct auth_request *auth_request); bool passdb_template_remove(struct passdb_template *tmpl, const char *key, const char **value_r); bool passdb_template_is_empty(struct passdb_template *tmpl); const char *const *passdb_template_get_args(struct passdb_template *tmpl, unsigned int *count_r); #endif dovecot-2.2.33.2/src/auth/mech-winbind.c0000644000175000017500000002212013165463624014574 00000000000000/* * NTLM and Negotiate authentication mechanisms, * using Samba winbind daemon * * Copyright (c) 2007 Dmitry Butskoy * * This software is released under the MIT license. */ #include "auth-common.h" #include "lib-signals.h" #include "mech.h" #include "str.h" #include "buffer.h" #include "base64.h" #include "execv-const.h" #include "istream.h" #include "ostream.h" #include #include #define DEFAULT_WINBIND_HELPER_PATH "/usr/bin/ntlm_auth" enum helper_result { HR_OK = 0, /* OK or continue */ HR_FAIL = -1, /* authentication failed */ HR_RESTART = -2 /* FAIL + try to restart helper */ }; struct winbind_helper { const char *param; pid_t pid; struct istream *in_pipe; struct ostream *out_pipe; }; struct winbind_auth_request { struct auth_request auth_request; struct winbind_helper *winbind; bool continued; }; static struct winbind_helper winbind_ntlm_context = { "--helper-protocol=squid-2.5-ntlmssp", -1, NULL, NULL }; static struct winbind_helper winbind_spnego_context = { "--helper-protocol=gss-spnego", -1, NULL, NULL }; static bool sigchld_handler_set = FALSE; static void winbind_helper_disconnect(struct winbind_helper *winbind) { if (winbind->in_pipe != NULL) i_stream_destroy(&winbind->in_pipe); if (winbind->out_pipe != NULL) o_stream_destroy(&winbind->out_pipe); } static void winbind_wait_pid(struct winbind_helper *winbind) { int status, ret; if (winbind->pid == -1) return; /* FIXME: use child-wait.h API */ if ((ret = waitpid(winbind->pid, &status, WNOHANG)) <= 0) { if (ret < 0 && errno != ECHILD && errno != EINTR) i_error("waitpid() failed: %m"); return; } if (WIFSIGNALED(status)) { i_error("winbind: ntlm_auth died with signal %d", WTERMSIG(status)); } else if (WIFEXITED(status)) { i_error("winbind: ntlm_auth exited with exit code %d", WEXITSTATUS(status)); } else { /* shouldn't happen */ i_error("winbind: ntlm_auth exited with status %d", status); } winbind->pid = -1; } static void sigchld_handler(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { winbind_wait_pid(&winbind_ntlm_context); winbind_wait_pid(&winbind_spnego_context); } static void winbind_helper_connect(const struct auth_settings *set, struct winbind_helper *winbind) { int infd[2], outfd[2]; pid_t pid; if (winbind->in_pipe != NULL || winbind->pid != -1) return; if (pipe(infd) < 0) { i_error("pipe() failed: %m"); return; } if (pipe(outfd) < 0) { i_close_fd(&infd[0]); i_close_fd(&infd[1]); return; } pid = fork(); if (pid < 0) { i_error("fork() failed: %m"); i_close_fd(&infd[0]); i_close_fd(&infd[1]); i_close_fd(&outfd[0]); i_close_fd(&outfd[1]); return; } if (pid == 0) { /* child */ const char *args[3]; i_close_fd(&infd[0]); i_close_fd(&outfd[1]); if (dup2(outfd[0], STDIN_FILENO) < 0 || dup2(infd[1], STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); args[0] = set->winbind_helper_path; args[1] = winbind->param; args[2] = NULL; execv_const(args[0], args); } /* parent */ i_close_fd(&infd[1]); i_close_fd(&outfd[0]); winbind->pid = pid; winbind->in_pipe = i_stream_create_fd_autoclose(&infd[0], AUTH_CLIENT_MAX_LINE_LENGTH); winbind->out_pipe = o_stream_create_fd_autoclose(&outfd[1], (size_t)-1); if (!sigchld_handler_set) { sigchld_handler_set = TRUE; lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE, sigchld_handler, NULL); } } static enum helper_result do_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct winbind_auth_request *request = (struct winbind_auth_request *)auth_request; struct istream *in_pipe = request->winbind->in_pipe; string_t *str; char *answer; const char **token; bool gss_spnego = request->winbind == &winbind_spnego_context; if (request->winbind->in_pipe == NULL) return HR_RESTART; str = t_str_new(MAX_BASE64_ENCODED_SIZE(data_size + 1) + 4); str_printfa(str, "%s ", request->continued ? "KK" : "YR"); base64_encode(data, data_size, str); str_append_c(str, '\n'); if (o_stream_send(request->winbind->out_pipe, str_data(str), str_len(str)) < 0 || o_stream_flush(request->winbind->out_pipe) < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "write(out_pipe) failed: %s", o_stream_get_error(request->winbind->out_pipe)); return HR_RESTART; } request->continued = FALSE; while ((answer = i_stream_read_next_line(in_pipe)) == NULL) { if (in_pipe->stream_errno != 0 || in_pipe->eof) break; } if (answer == NULL) { if (in_pipe->stream_errno != 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "read(in_pipe) failed: %m"); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "read(in_pipe) failed: " "unexpected end of file"); } return HR_RESTART; } token = t_strsplit_spaces(answer, " "); if (token[0] == NULL || (token[1] == NULL && strcmp(token[0], "BH") != 0) || (gss_spnego && (token[1] == NULL || token[2] == NULL))) { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "Invalid input from helper: %s", answer); return HR_RESTART; } /* * NTLM: * The child's reply contains 2 parts: * - The code: TT, AF or NA * - The argument: * For TT it's the blob to send to the client, coded in base64 * For AF it's user or DOMAIN\user * For NA it's the NT error code * * GSS-SPNEGO: * The child's reply contains 3 parts: * - The code: TT, AF or NA * - The blob to send to the client, coded in base64 * - The argument: * For TT it's a dummy '*' * For AF it's DOMAIN\user * For NA it's the NT error code */ if (strcmp(token[0], "TT") == 0) { buffer_t *buf; buf = t_base64_decode_str(token[1]); auth_request_handler_reply_continue(auth_request, buf->data, buf->used); request->continued = TRUE; return HR_OK; } else if (strcmp(token[0], "NA") == 0) { const char *error = gss_spnego ? token[2] : token[1]; auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "user not authenticated: %s", error); return HR_FAIL; } else if (strcmp(token[0], "AF") == 0) { const char *user, *p, *error; user = t_strarray_join(gss_spnego ? token+2 : token+1, " "); i_assert(user != NULL); p = strchr(user, '\\'); if (p != NULL) { /* change "DOMAIN\user" to uniform style "user@DOMAIN" */ user = t_strconcat(p+1, "@", t_strdup_until(user, p), NULL); } if (!auth_request_set_username(auth_request, user, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); return HR_FAIL; } request->auth_request.passdb_success = TRUE; if (gss_spnego && strcmp(token[1], "*") != 0) { buffer_t *buf; buf = t_base64_decode_str(token[1]); auth_request_success(&request->auth_request, buf->data, buf->used); } else { auth_request_success(&request->auth_request, "", 0); } return HR_OK; } else if (strcmp(token[0], "BH") == 0) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "ntlm_auth reports broken helper: %s", token[1] != NULL ? token[1] : ""); return HR_RESTART; } else { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "Invalid input from helper: %s", answer); return HR_RESTART; } } static void mech_winbind_auth_initial(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct winbind_auth_request *request = (struct winbind_auth_request *)auth_request; winbind_helper_connect(auth_request->set, request->winbind); mech_generic_auth_initial(auth_request, data, data_size); } static void mech_winbind_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct winbind_auth_request *request = (struct winbind_auth_request *)auth_request; enum helper_result res; res = do_auth_continue(auth_request, data, data_size); if (res != HR_OK) { if (res == HR_RESTART) winbind_helper_disconnect(request->winbind); auth_request_fail(auth_request); } } static struct auth_request *do_auth_new(struct winbind_helper *winbind) { struct winbind_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"winbind_auth_request", 2048); request = p_new(pool, struct winbind_auth_request, 1); request->auth_request.pool = pool; request->winbind = winbind; return &request->auth_request; } static struct auth_request *mech_winbind_ntlm_auth_new(void) { return do_auth_new(&winbind_ntlm_context); } static struct auth_request *mech_winbind_spnego_auth_new(void) { return do_auth_new(&winbind_spnego_context); } const struct mech_module mech_winbind_ntlm = { "NTLM", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_winbind_ntlm_auth_new, mech_winbind_auth_initial, mech_winbind_auth_continue, mech_generic_auth_free }; const struct mech_module mech_winbind_spnego = { "GSS-SPNEGO", .flags = 0, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_winbind_spnego_auth_new, mech_winbind_auth_initial, mech_winbind_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/mech.h0000644000175000017500000000440013123174404013137 00000000000000#ifndef MECH_H #define MECH_H #include "auth-client-interface.h" struct auth_settings; struct auth_request; #include "auth-request.h" #include "auth-request-handler.h" /* Used only for string sanitization. */ #define MAX_MECH_NAME_LEN 64 enum mech_passdb_need { /* Mechanism doesn't need a passdb at all */ MECH_PASSDB_NEED_NOTHING = 0, /* Mechanism just needs to verify a given plaintext password */ MECH_PASSDB_NEED_VERIFY_PLAIN, /* Mechanism needs to verify a given challenge+response combination, i.e. there is only a single response from client. (Currently implemented the same as _LOOKUP_CREDENTIALS) */ MECH_PASSDB_NEED_VERIFY_RESPONSE, /* Mechanism needs to look up credentials with appropriate scheme */ MECH_PASSDB_NEED_LOOKUP_CREDENTIALS, /* Mechanism needs to look up credentials and also modify them */ MECH_PASSDB_NEED_SET_CREDENTIALS }; struct mech_module { const char *mech_name; enum mech_security_flags flags; enum mech_passdb_need passdb_need; struct auth_request *(*auth_new)(void); void (*auth_initial)(struct auth_request *request, const unsigned char *data, size_t data_size); void (*auth_continue)(struct auth_request *request, const unsigned char *data, size_t data_size); void (*auth_free)(struct auth_request *request); }; struct mech_module_list { struct mech_module_list *next; struct mech_module module; }; struct mechanisms_register { pool_t pool; const struct auth_settings *set; struct mech_module_list *modules; buffer_t *handshake; }; extern const struct mech_module mech_dovecot_token; void mech_register_module(const struct mech_module *module); void mech_unregister_module(const struct mech_module *module); const struct mech_module *mech_module_find(const char *name); void mech_generic_auth_initial(struct auth_request *request, const unsigned char *data, size_t data_size); void mech_generic_auth_free(struct auth_request *request); struct mechanisms_register * mech_register_init(const struct auth_settings *set); void mech_register_deinit(struct mechanisms_register **reg); const struct mech_module * mech_register_find(const struct mechanisms_register *reg, const char *name); void mech_init(const struct auth_settings *set); void mech_deinit(const struct auth_settings *set); #endif dovecot-2.2.33.2/src/auth/passdb-imap.c0000644000175000017500000001500013165463624014427 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "passdb.h" #include "str.h" #include "imap-resp-code.h" #include "imapc-client.h" #define IMAP_DEFAULT_PORT 143 #define IMAPS_DEFAULT_PORT 993 #define DNS_CLIENT_SOCKET_NAME "dns-client" struct imap_passdb_module { struct passdb_module module; struct imapc_client_settings set; bool set_have_vars; }; struct imap_auth_request { struct imapc_client *client; struct auth_request *auth_request; verify_plain_callback_t *verify_callback; struct timeout *to_free; }; static enum passdb_result passdb_imap_get_failure_result(const struct imapc_command_reply *reply) { const char *key = reply->resp_text_key; if (key == NULL) return PASSDB_RESULT_PASSWORD_MISMATCH; if (strcasecmp(key, IMAP_RESP_CODE_AUTHFAILED) == 0 || strcasecmp(key, IMAP_RESP_CODE_AUTHZFAILED) == 0) return PASSDB_RESULT_PASSWORD_MISMATCH; if (strcasecmp(key, IMAP_RESP_CODE_EXPIRED) == 0) return PASSDB_RESULT_PASS_EXPIRED; return PASSDB_RESULT_INTERNAL_FAILURE; } static void passdb_imap_login_free(struct imap_auth_request *request) { timeout_remove(&request->to_free); imapc_client_deinit(&request->client); auth_request_unref(&request->auth_request); } static void passdb_imap_login_callback(const struct imapc_command_reply *reply, void *context) { struct imap_auth_request *request = context; enum passdb_result result = PASSDB_RESULT_INTERNAL_FAILURE; switch (reply->state) { case IMAPC_COMMAND_STATE_OK: result = PASSDB_RESULT_OK; break; case IMAPC_COMMAND_STATE_NO: result = passdb_imap_get_failure_result(reply); auth_request_log_info(request->auth_request, AUTH_SUBSYS_DB, "%s", reply->text_full); break; case IMAPC_COMMAND_STATE_AUTH_FAILED: case IMAPC_COMMAND_STATE_BAD: case IMAPC_COMMAND_STATE_DISCONNECTED: auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "%s", reply->text_full); break; } request->verify_callback(result, request->auth_request); /* imapc_client can't be freed in this callback, so do it in a separate callback. FIXME: remove this once imapc supports proper refcounting. */ request->to_free = timeout_add_short(0, passdb_imap_login_free, request); } static void passdb_imap_verify_plain(struct auth_request *auth_request, const char *password, verify_plain_callback_t *callback) { struct passdb_module *_module = auth_request->passdb->passdb; struct imap_passdb_module *module = (struct imap_passdb_module *)_module; struct imap_auth_request *request; struct imapc_client_settings set; string_t *str; set = module->set; set.debug = auth_request->debug; set.dns_client_socket_path = t_strconcat(auth_request->set->base_dir, "/", DNS_CLIENT_SOCKET_NAME, NULL); set.password = password; set.max_idle_time = IMAPC_DEFAULT_MAX_IDLE_TIME; if (set.ssl_ca_dir == NULL) set.ssl_ca_dir = auth_request->set->ssl_client_ca_dir; if (set.ssl_ca_file == NULL) set.ssl_ca_file = auth_request->set->ssl_client_ca_file; if (module->set_have_vars) { str = t_str_new(128); auth_request_var_expand(str, set.username, auth_request, NULL); set.username = t_strdup(str_c(str)); str_truncate(str, 0); auth_request_var_expand(str, set.host, auth_request, NULL); set.host = t_strdup(str_c(str)); } auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "lookup host=%s port=%d", set.host, set.port); request = p_new(auth_request->pool, struct imap_auth_request, 1); request->client = imapc_client_init(&set); request->auth_request = auth_request; request->verify_callback = callback; auth_request_ref(auth_request); imapc_client_set_login_callback(request->client, passdb_imap_login_callback, request); imapc_client_login(request->client); } static struct passdb_module * passdb_imap_preinit(pool_t pool, const char *args) { struct imap_passdb_module *module; char **tmp; const char *key, *value; bool port_set = FALSE; module = p_new(pool, struct imap_passdb_module, 1); module->module.default_pass_scheme = "PLAIN"; module->set.port = IMAP_DEFAULT_PORT; module->set.ssl_mode = IMAPC_CLIENT_SSL_MODE_NONE; module->set.username = "%u"; module->set.rawlog_dir = ""; module->set.ssl_verify = TRUE; for (tmp = p_strsplit(pool, args, " "); *tmp != NULL; tmp++) { key = *tmp; value = strchr(key, '='); if (value == NULL) value = ""; else key = t_strdup_until(key, value++); if (strcmp(key, "host") == 0) module->set.host = value; else if (strcmp(key, "port") == 0) { if (net_str2port(value, &module->set.port) < 0) i_fatal("passdb imap: Invalid port: %s", value); port_set = TRUE; } else if (strcmp(key, "username") == 0) module->set.username = value; else if (strcmp(key, "ssl_ca_dir") == 0) module->set.ssl_ca_dir = value; else if (strcmp(key, "ssl_ca_file") == 0) module->set.ssl_ca_file = value; else if (strcmp(key, "rawlog_dir") == 0) module->set.rawlog_dir = value; else if (strcmp(key, "ssl") == 0) { if (strcmp(value, "imaps") == 0) { if (!port_set) module->set.port = IMAPS_DEFAULT_PORT; module->set.ssl_mode = IMAPC_CLIENT_SSL_MODE_IMMEDIATE; } else if (strcmp(value, "starttls") == 0) { module->set.ssl_mode = IMAPC_CLIENT_SSL_MODE_STARTTLS; } else { i_fatal("passdb imap: Invalid ssl mode: %s", value); } } else if (strcmp(key, "allow_invalid_cert") == 0) { if (strcmp(value, "yes") == 0) { module->set.ssl_verify = FALSE; } else if (strcmp(value, "no") == 0) { module->set.ssl_verify = TRUE; } else { i_fatal("passdb imap: Invalid allow_invalid_cert value: %s", value); } } else { i_fatal("passdb imap: Unknown parameter: %s", key); } } if (module->set.ssl_verify == TRUE && module->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE ) { if (module->set.ssl_ca_dir == NULL && module->set.ssl_ca_file == NULL) i_fatal("passdb imap: Cannot verify certificate without ssl_ca_dir or ssl_ca_file setting"); } if (module->set.host == NULL) i_fatal("passdb imap: Missing host parameter"); module->set_have_vars = strchr(module->set.username, '%') != NULL || strchr(module->set.host, '%') != NULL; return &module->module; } static struct passdb_module_interface passdb_imap_plugin = { "imap", passdb_imap_preinit, NULL, NULL, passdb_imap_verify_plain, NULL, NULL }; void authdb_imap_init(void); void authdb_imap_deinit(void); void authdb_imap_init(void) { passdb_register_module(&passdb_imap_plugin); } void authdb_imap_deinit(void) { passdb_unregister_module(&passdb_imap_plugin); } dovecot-2.2.33.2/src/auth/passdb-blocking.c0000644000175000017500000001106413144653606015275 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "str.h" #include "strescape.h" #include "auth-worker-server.h" #include "password-scheme.h" #include "passdb.h" #include "passdb-blocking.h" static void auth_worker_reply_parse_args(struct auth_request *request, const char *const *args) { if (**args != '\0') request->passdb_password = p_strdup(request->pool, *args); args++; if (*args != NULL) auth_request_set_fields(request, args, NULL); } static enum passdb_result auth_worker_reply_parse(struct auth_request *request, const char *reply) { enum passdb_result ret; const char *const *args; args = t_strsplit_tabescaped(reply); if (strcmp(*args, "OK") == 0 && args[1] != NULL && args[2] != NULL) { /* OK \t user \t password [\t extra] */ if (args[1][0] != '\0') auth_request_set_field(request, "user", args[1], NULL); auth_worker_reply_parse_args(request, args + 2); return PASSDB_RESULT_OK; } if (strcmp(*args, "NEXT") == 0 && args[1] != NULL) { /* NEXT \t user [\t extra] */ if (args[1][0] != '\0') auth_request_set_field(request, "user", args[1], NULL); auth_worker_reply_parse_args(request, args + 1); return PASSDB_RESULT_NEXT; } if (strcmp(*args, "FAIL") == 0 && args[1] != NULL) { int result; /* FAIL \t result [\t user \t password [\t extra]] */ if (str_to_int(args[1], &result) < 0) { /* shouldn't happen */ } else { ret = (enum passdb_result)result; if (ret == PASSDB_RESULT_OK) { /* shouldn't happen */ } else if (args[2] == NULL) { /* internal failure most likely */ return ret; } else if (args[3] != NULL) { if (*args[2] != '\0') { auth_request_set_field(request, "user", args[2], NULL); } auth_worker_reply_parse_args(request, args + 3); return ret; } } } auth_request_log_error(request, AUTH_SUBSYS_DB, "Received invalid reply from worker: %s", reply); return PASSDB_RESULT_INTERNAL_FAILURE; } static bool verify_plain_callback(const char *reply, void *context) { struct auth_request *request = context; enum passdb_result result; result = auth_worker_reply_parse(request, reply); auth_request_verify_plain_callback(result, request); auth_request_unref(&request); return TRUE; } void passdb_blocking_verify_plain(struct auth_request *request) { string_t *str; str = t_str_new(128); str_printfa(str, "PASSV\t%u\t", request->passdb->passdb->id); str_append_tabescaped(str, request->mech_password); str_append_c(str, '\t'); auth_request_export(request, str); auth_request_ref(request); auth_worker_call(request->pool, request->user, str_c(str), verify_plain_callback, request); } static bool lookup_credentials_callback(const char *reply, void *context) { struct auth_request *request = context; enum passdb_result result; const char *password = NULL, *scheme = NULL; result = auth_worker_reply_parse(request, reply); if (result == PASSDB_RESULT_OK && request->passdb_password != NULL) { password = request->passdb_password; scheme = password_get_scheme(&password); if (scheme == NULL) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Received reply from worker without " "password scheme"); result = PASSDB_RESULT_INTERNAL_FAILURE; } } passdb_handle_credentials(result, password, scheme, auth_request_lookup_credentials_callback, request); auth_request_unref(&request); return TRUE; } void passdb_blocking_lookup_credentials(struct auth_request *request) { string_t *str; str = t_str_new(128); str_printfa(str, "PASSL\t%u\t", request->passdb->passdb->id); str_append_tabescaped(str, request->credentials_scheme); str_append_c(str, '\t'); auth_request_export(request, str); auth_request_ref(request); auth_worker_call(request->pool, request->user, str_c(str), lookup_credentials_callback, request); } static bool set_credentials_callback(const char *reply, void *context) { struct auth_request *request = context; bool success; success = strcmp(reply, "OK") == 0 || strncmp(reply, "OK\t", 3) == 0; request->private_callback.set_credentials(success, request); auth_request_unref(&request); return TRUE; } void passdb_blocking_set_credentials(struct auth_request *request, const char *new_credentials) { string_t *str; str = t_str_new(128); str_printfa(str, "SETCRED\t%u\t", request->passdb->passdb->id); str_append_tabescaped(str, new_credentials); str_append_c(str, '\t'); auth_request_export(request, str); auth_request_ref(request); auth_worker_call(request->pool, request->user, str_c(str), set_credentials_callback, request); } dovecot-2.2.33.2/src/auth/passdb-oauth2.c0000644000175000017500000000336113123174404014677 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #include "db-oauth2.h" struct oauth2_passdb_module { struct passdb_module module; struct db_oauth2 *db; }; static void oauth2_verify_plain_continue(struct db_oauth2_request *req, enum passdb_result result, const char *error, struct auth_request *request) { if (result == PASSDB_RESULT_INTERNAL_FAILURE) auth_request_log_error(request, AUTH_SUBSYS_DB, "oauth2 failed: %s", error); else if (result != PASSDB_RESULT_OK) auth_request_log_info(request, AUTH_SUBSYS_DB, "oauth2 failed: %s", error); req->verify_callback(result, request); auth_request_unref(&request); } static void oauth2_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct oauth2_passdb_module *module = (struct oauth2_passdb_module *)request->passdb->passdb; struct db_oauth2_request *req = p_new(request->pool, struct db_oauth2_request, 1); req->pool = request->pool; req->verify_callback = callback; auth_request_ref(request); db_oauth2_lookup(module->db, req, password, request, oauth2_verify_plain_continue, request); } static struct passdb_module * oauth2_preinit(pool_t pool, const char *args) { struct oauth2_passdb_module *module; module = p_new(pool, struct oauth2_passdb_module, 1); module->db = db_oauth2_init(args); return &module->module; } static void oauth2_deinit(struct passdb_module *passdb) { struct oauth2_passdb_module *module = (struct oauth2_passdb_module *)passdb; db_oauth2_unref(&module->db); } struct passdb_module_interface passdb_oauth2 = { "oauth2", oauth2_preinit, NULL, oauth2_deinit, oauth2_verify_plain, NULL, NULL }; dovecot-2.2.33.2/src/auth/auth-policy.h0000644000175000017500000000052413123174404014464 00000000000000#ifndef AUTH_POLICY_H #define AUTH_POLICY_H typedef void (*auth_policy_callback_t)(int, void *); void auth_policy_check(struct auth_request *request, const char *password, auth_policy_callback_t cb, void *context); void auth_policy_report(struct auth_request *request); void auth_policy_init(void); void auth_policy_deinit(void); #endif dovecot-2.2.33.2/src/auth/auth-token.c0000644000175000017500000001114613123174404014302 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ /* Auth process maintains a random secret. Once a user authenticates the response to the REQUEST command from a master service is augmented with an auth_token value. This token is the SHA1 hash of the secret, the service name and the username of the user that just logged in. Using this token the service (e.g. imap) can login to another service (e.g. imap-urlauth) to gain access to resources that require additional privileges (e.g. another user's e-mail). */ #include "auth-common.h" #include "hex-binary.h" #include "hmac.h" #include "sha1.h" #include "randgen.h" #include "read-full.h" #include "write-full.h" #include "safe-memset.h" #include "auth-settings.h" #include "auth-token.h" #include #include #include #include #include #define AUTH_TOKEN_SECRET_LEN 32 #define AUTH_TOKEN_SECRET_FNAME "auth-token-secret.dat" static unsigned char auth_token_secret[AUTH_TOKEN_SECRET_LEN]; static int auth_token_read_secret(const char *path, unsigned char secret_r[AUTH_TOKEN_SECRET_LEN]) { struct stat st, lst; int fd, ret; fd = open(path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", path); return -1; } if (fstat(fd, &st) < 0) { i_error("fstat(%s) failed: %m", path); i_close_fd(&fd); return -1; } /* check secret len and file type */ if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) { i_error("Corrupted token secret file: %s", path); i_close_fd(&fd); i_unlink(path); return -1; } /* verify that we're not dealing with a symbolic link */ if (lstat(path, &lst) < 0) { i_error("lstat(%s) failed: %m", path); i_close_fd(&fd); return -1; } /* check security parameters for compromise */ if ((st.st_mode & 07777) != 0600 || st.st_uid != geteuid() || st.st_nlink > 1 || !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino || !CMP_DEV_T(st.st_dev, lst.st_dev)) { i_error("Compromised token secret file: %s", path); i_close_fd(&fd); i_unlink(path); return -1; } /* FIXME: fail here to generate new secret if stored one is too old */ ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN); if (ret < 0) i_error("read(%s) failed: %m", path); else if (ret == 0) { i_error("Token secret file unexpectedly shrank: %s", path); ret = -1; } if (close(fd) < 0) i_error("close(%s) failed: %m", path); if (global_auth_settings->debug) i_debug("Read auth token secret from %s", path); return ret; } static int auth_token_write_secret(const char *path, const unsigned char secret[AUTH_TOKEN_SECRET_LEN]) { const char *temp_path; mode_t old_mask; int fd, ret; temp_path = t_strconcat(path, ".tmp", NULL); old_mask = umask(0); fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); umask(old_mask); if (fd == -1) { i_error("open(%s) failed: %m", temp_path); return -1; } ret = write_full(fd, secret, AUTH_TOKEN_SECRET_LEN); if (ret < 0) i_error("write(%s) failed: %m", temp_path); if (close(fd) < 0) { i_error("close(%s) failed: %m", temp_path); ret = -1; } if (ret < 0) { i_unlink(temp_path); return -1; } if (rename(temp_path, path) < 0) { i_error("rename(%s, %s) failed: %m", temp_path, path); i_unlink(temp_path); return -1; } if (global_auth_settings->debug) i_debug("Wrote new auth token secret to %s", path); return 0; } void auth_token_init(void) { const char *secret_path = t_strconcat(global_auth_settings->base_dir, "/", AUTH_TOKEN_SECRET_FNAME, NULL); if (auth_token_read_secret(secret_path, auth_token_secret) < 0) { random_fill(auth_token_secret, sizeof(auth_token_secret)); if (auth_token_write_secret(secret_path, auth_token_secret) < 0) { i_error("Failed to write auth token secret file; " "returned tokens will be invalid once auth restarts"); } } } void auth_token_deinit(void) { /* not very useful, but we do it anyway */ safe_memset(auth_token_secret, 0, sizeof(auth_token_secret)); } const char *auth_token_get(const char *service, const char *session_pid, const char *username, const char *session_id) { struct hmac_context ctx; unsigned char result[SHA1_RESULTLEN]; hmac_init(&ctx, (const unsigned char*)username, strlen(username), &hash_method_sha1); hmac_update(&ctx, session_pid, strlen(session_pid)); if (session_id != NULL && *session_id != '\0') hmac_update(&ctx, session_id, strlen(session_id)); hmac_update(&ctx, service, strlen(service)); hmac_update(&ctx, auth_token_secret, sizeof(auth_token_secret)); hmac_final(&ctx, result); return binary_to_hex(result, sizeof(result)); } dovecot-2.2.33.2/src/auth/auth-request-var-expand.h0000644000175000017500000000266113165463624016737 00000000000000#ifndef AUTH_REQUEST_VAR_EXPAND_H #define AUTH_REQUEST_VAR_EXPAND_H typedef const char * auth_request_escape_func_t(const char *string, const struct auth_request *auth_request); #define AUTH_REQUEST_VAR_TAB_USER_IDX 0 #define AUTH_REQUEST_VAR_TAB_USERNAME_IDX 1 #define AUTH_REQUEST_VAR_TAB_DOMAIN_IDX 2 #define AUTH_REQUEST_VAR_TAB_COUNT 35 extern const struct var_expand_table auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT+1]; const struct var_expand_table * auth_request_get_var_expand_table(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func) ATTR_NULL(2); struct var_expand_table * auth_request_get_var_expand_table_full(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func, unsigned int *count) ATTR_NULL(2); void auth_request_var_expand(string_t *dest, const char *str, const struct auth_request *auth_request, auth_request_escape_func_t *escape_func); void auth_request_var_expand_with_table(string_t *dest, const char *str, const struct auth_request *auth_request, const struct var_expand_table *table, auth_request_escape_func_t *escape_func); const char * t_auth_request_var_expand(const char *str, const struct auth_request *auth_request, auth_request_escape_func_t *escape_func); const char *auth_request_str_escape(const char *string, const struct auth_request *request); #endif dovecot-2.2.33.2/src/auth/db-ldap.h0000644000175000017500000001304513123174404013533 00000000000000#ifndef DB_LDAP_H #define DB_LDAP_H /* Functions like ldap_bind() have been deprecated in OpenLDAP 2.3 This define enables them until the code here can be refactored */ #define LDAP_DEPRECATED 1 /* Maximum number of pending requests before delaying new requests. */ #define DB_LDAP_MAX_PENDING_REQUESTS 8 /* connect() timeout to LDAP */ #define DB_LDAP_CONNECT_TIMEOUT_SECS 5 /* If LDAP connection is down, fail requests after waiting for this long. */ #define DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS 4 /* If request is still in queue after this many seconds and other requests have been replied, assume the request was lost and abort it. */ #define DB_LDAP_REQUEST_LOST_TIMEOUT_SECS 60 /* If server disconnects us, don't reconnect if no requests have been sent for this many seconds. */ #define DB_LDAP_IDLE_RECONNECT_SECS 60 #include struct auth_request; struct ldap_connection; struct ldap_request; typedef void db_search_callback_t(struct ldap_connection *conn, struct ldap_request *request, LDAPMessage *res); struct ldap_settings { const char *hosts; const char *uris; const char *dn; const char *dnpass; bool auth_bind; const char *auth_bind_userdn; bool tls; bool sasl_bind; const char *sasl_mech; const char *sasl_realm; const char *sasl_authz_id; const char *tls_ca_cert_file; const char *tls_ca_cert_dir; const char *tls_cert_file; const char *tls_key_file; const char *tls_cipher_suite; const char *tls_require_cert; const char *deref; const char *scope; const char *base; unsigned int ldap_version; const char *ldaprc_path; const char *debug_level; const char *user_attrs; const char *user_filter; const char *pass_attrs; const char *pass_filter; const char *iterate_attrs; const char *iterate_filter; const char *default_pass_scheme; bool userdb_warning_disable; /* deprecated for now at least */ bool blocking; /* ... */ int ldap_deref, ldap_scope, ldap_tls_require_cert_parsed; uid_t uid; gid_t gid; }; enum ldap_request_type { LDAP_REQUEST_TYPE_SEARCH, LDAP_REQUEST_TYPE_BIND }; struct ldap_field { /* Dovecot field name. */ const char *name; /* Field value template with %vars. NULL = same as LDAP value. */ const char *value; /* LDAP attribute name, or "" if this is a static field. */ const char *ldap_attr_name; /* LDAP value contains a DN, which is looked up and used for @name attributes. */ bool value_is_dn; /* This attribute is used internally only via %{ldap_ptr}, it shouldn't be returned in iteration. */ bool skip; }; ARRAY_DEFINE_TYPE(ldap_field, struct ldap_field); struct ldap_request { enum ldap_request_type type; /* msgid for sent requests, -1 if not sent */ int msgid; /* timestamp when request was created */ time_t create_time; bool failed; db_search_callback_t *callback; struct auth_request *auth_request; }; struct ldap_request_named_result { const struct ldap_field *field; const char *dn; struct db_ldap_result *result; }; struct ldap_request_search { struct ldap_request request; const char *base; const char *filter; char **attributes; /* points to pass_attr_names / user_attr_names */ const ARRAY_TYPE(ldap_field) *attr_map; struct db_ldap_result *result; ARRAY(struct ldap_request_named_result) named_results; unsigned int name_idx; bool multi_entry; }; struct ldap_request_bind { struct ldap_request request; const char *dn; }; enum ldap_connection_state { /* Not connected */ LDAP_CONN_STATE_DISCONNECTED, /* Binding - either to default dn or doing auth bind */ LDAP_CONN_STATE_BINDING, /* Bound to auth dn */ LDAP_CONN_STATE_BOUND_AUTH, /* Bound to default dn */ LDAP_CONN_STATE_BOUND_DEFAULT }; struct ldap_connection { struct ldap_connection *next; pool_t pool; int refcount; char *config_path; struct ldap_settings set; LDAP *ld; enum ldap_connection_state conn_state; int default_bind_msgid; int fd; struct io *io; struct timeout *to; /* Request queue contains sent requests at tail (msgid != -1) and queued requests at head (msgid == -1). */ struct aqueue *request_queue; ARRAY(struct ldap_request *) request_array; /* Number of messages in queue with msgid != -1 */ unsigned int pending_count; /* Timestamp when we last received a reply */ time_t last_reply_stamp; char **pass_attr_names, **user_attr_names, **iterate_attr_names; ARRAY_TYPE(ldap_field) pass_attr_map, user_attr_map, iterate_attr_map; bool userdb_used; bool delayed_connect; }; /* Send/queue request */ void db_ldap_request(struct ldap_connection *conn, struct ldap_request *request); void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char ***attr_names_r, ARRAY_TYPE(ldap_field) *attr_map, const char *skip_attr) ATTR_NULL(5); struct ldap_connection *db_ldap_init(const char *config_path, bool userdb); void db_ldap_unref(struct ldap_connection **conn); int db_ldap_connect(struct ldap_connection *conn); void db_ldap_connect_delayed(struct ldap_connection *conn); void db_ldap_enable_input(struct ldap_connection *conn, bool enable); const char *ldap_escape(const char *str, const struct auth_request *auth_request); const char *ldap_get_error(struct ldap_connection *conn); struct db_ldap_result_iterate_context * db_ldap_result_iterate_init(struct ldap_connection *conn, struct ldap_request_search *ldap_request, LDAPMessage *res, bool skip_null_values); bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx, const char **name_r, const char *const **values_r); void db_ldap_result_iterate_deinit(struct db_ldap_result_iterate_context **ctx); #endif dovecot-2.2.33.2/src/auth/db-ldap.c0000644000175000017500000014634613172375226013552 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD) #include "net.h" #include "ioloop.h" #include "array.h" #include "hash.h" #include "aqueue.h" #include "str.h" #include "time-util.h" #include "env-util.h" #include "var-expand.h" #include "settings.h" #include "userdb.h" #include "db-ldap.h" #include #include #define HAVE_LDAP_SASL #ifdef HAVE_SASL_SASL_H # include #elif defined (HAVE_SASL_H) # include #else # undef HAVE_LDAP_SASL #endif #ifdef LDAP_OPT_X_TLS # define OPENLDAP_TLS_OPTIONS #endif #if SASL_VERSION_MAJOR < 2 # undef HAVE_LDAP_SASL #endif #ifndef LDAP_SASL_QUIET # define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */ #endif /* Older versions may require calling ldap_result() twice */ #if LDAP_VENDOR_VERSION <= 20112 # define OPENLDAP_ASYNC_WORKAROUND #endif /* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */ #ifndef LDAP_OPT_SUCCESS # define LDAP_OPT_SUCCESS LDAP_SUCCESS #endif static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= "; struct db_ldap_result { int refcount; LDAPMessage *msg; }; struct db_ldap_value { const char **values; bool used; }; struct db_ldap_result_iterate_context { pool_t pool; struct auth_request *auth_request; const ARRAY_TYPE(ldap_field) *attr_map; unsigned int attr_idx; /* attribute name => value */ HASH_TABLE(char *, struct db_ldap_value *) ldap_attrs; const char *val_1_arr[2]; string_t *var, *debug; bool skip_null_values; bool iter_dn_values; LDAPMessage *ldap_msg; LDAP *ld; }; struct db_ldap_sasl_bind_context { const char *authcid; const char *passwd; const char *realm; const char *authzid; }; #define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings) #define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings) static struct setting_def setting_defs[] = { DEF_STR(hosts), DEF_STR(uris), DEF_STR(dn), DEF_STR(dnpass), DEF_BOOL(auth_bind), DEF_STR(auth_bind_userdn), DEF_BOOL(tls), DEF_BOOL(sasl_bind), DEF_STR(sasl_mech), DEF_STR(sasl_realm), DEF_STR(sasl_authz_id), DEF_STR(tls_ca_cert_file), DEF_STR(tls_ca_cert_dir), DEF_STR(tls_cert_file), DEF_STR(tls_key_file), DEF_STR(tls_cipher_suite), DEF_STR(tls_require_cert), DEF_STR(deref), DEF_STR(scope), DEF_STR(base), DEF_INT(ldap_version), DEF_STR(debug_level), DEF_STR(ldaprc_path), DEF_STR(user_attrs), DEF_STR(user_filter), DEF_STR(pass_attrs), DEF_STR(pass_filter), DEF_STR(iterate_attrs), DEF_STR(iterate_filter), DEF_STR(default_pass_scheme), DEF_BOOL(userdb_warning_disable), DEF_BOOL(blocking), { 0, NULL, 0 } }; static struct ldap_settings default_ldap_settings = { .hosts = NULL, .uris = NULL, .dn = NULL, .dnpass = NULL, .auth_bind = FALSE, .auth_bind_userdn = NULL, .tls = FALSE, .sasl_bind = FALSE, .sasl_mech = NULL, .sasl_realm = NULL, .sasl_authz_id = NULL, .tls_ca_cert_file = NULL, .tls_ca_cert_dir = NULL, .tls_cert_file = NULL, .tls_key_file = NULL, .tls_cipher_suite = NULL, .tls_require_cert = NULL, .deref = "never", .scope = "subtree", .base = NULL, .ldap_version = 3, .debug_level = "0", .ldaprc_path = "", .user_attrs = "homeDirectory=home,uidNumber=uid,gidNumber=gid", .user_filter = "(&(objectClass=posixAccount)(uid=%u))", .pass_attrs = "uid=user,userPassword=password", .pass_filter = "(&(objectClass=posixAccount)(uid=%u))", .iterate_attrs = "uid=user", .iterate_filter = "(objectClass=posixAccount)", .default_pass_scheme = "crypt", .userdb_warning_disable = FALSE, .blocking = FALSE }; static struct ldap_connection *ldap_connections = NULL; static int db_ldap_bind(struct ldap_connection *conn); static void db_ldap_conn_close(struct ldap_connection *conn); struct db_ldap_result_iterate_context * db_ldap_result_iterate_init_full(struct ldap_connection *conn, struct ldap_request_search *ldap_request, LDAPMessage *res, bool skip_null_values, bool iter_dn_values); static int deref2str(const char *str, int *ref_r) { if (strcasecmp(str, "never") == 0) *ref_r = LDAP_DEREF_NEVER; else if (strcasecmp(str, "searching") == 0) *ref_r = LDAP_DEREF_SEARCHING; else if (strcasecmp(str, "finding") == 0) *ref_r = LDAP_DEREF_FINDING; else if (strcasecmp(str, "always") == 0) *ref_r = LDAP_DEREF_ALWAYS; else return -1; return 0; } static int scope2str(const char *str, int *scope_r) { if (strcasecmp(str, "base") == 0) *scope_r = LDAP_SCOPE_BASE; else if (strcasecmp(str, "onelevel") == 0) *scope_r = LDAP_SCOPE_ONELEVEL; else if (strcasecmp(str, "subtree") == 0) *scope_r = LDAP_SCOPE_SUBTREE; else return -1; return 0; } #ifdef OPENLDAP_TLS_OPTIONS static int tls_require_cert2str(const char *str, int *value_r) { if (strcasecmp(str, "never") == 0) *value_r = LDAP_OPT_X_TLS_NEVER; else if (strcasecmp(str, "hard") == 0) *value_r = LDAP_OPT_X_TLS_HARD; else if (strcasecmp(str, "demand") == 0) *value_r = LDAP_OPT_X_TLS_DEMAND; else if (strcasecmp(str, "allow") == 0) *value_r = LDAP_OPT_X_TLS_ALLOW; else if (strcasecmp(str, "try") == 0) *value_r = LDAP_OPT_X_TLS_TRY; else return -1; return 0; } #endif static int ldap_get_errno(struct ldap_connection *conn) { int ret, err; ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err); if (ret != LDAP_SUCCESS) { i_error("LDAP: Can't get error number: %s", ldap_err2string(ret)); return LDAP_UNAVAILABLE; } return err; } const char *ldap_get_error(struct ldap_connection *conn) { const char *ret; char *str = NULL; ret = ldap_err2string(ldap_get_errno(conn)); ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str); if (str != NULL) { ret = t_strconcat(ret, ", ", str, NULL); ldap_memfree(str); } ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL); return ret; } static void ldap_conn_reconnect(struct ldap_connection *conn) { db_ldap_conn_close(conn); if (db_ldap_connect(conn) < 0) db_ldap_conn_close(conn); } static int ldap_handle_error(struct ldap_connection *conn) { int err = ldap_get_errno(conn); switch (err) { case LDAP_SUCCESS: i_unreached(); case LDAP_SIZELIMIT_EXCEEDED: case LDAP_TIMELIMIT_EXCEEDED: case LDAP_NO_SUCH_ATTRIBUTE: case LDAP_UNDEFINED_TYPE: case LDAP_INAPPROPRIATE_MATCHING: case LDAP_CONSTRAINT_VIOLATION: case LDAP_TYPE_OR_VALUE_EXISTS: case LDAP_INVALID_SYNTAX: case LDAP_NO_SUCH_OBJECT: case LDAP_ALIAS_PROBLEM: case LDAP_INVALID_DN_SYNTAX: case LDAP_IS_LEAF: case LDAP_ALIAS_DEREF_PROBLEM: case LDAP_FILTER_ERROR: /* invalid input */ return -1; case LDAP_SERVER_DOWN: case LDAP_TIMEOUT: case LDAP_UNAVAILABLE: case LDAP_BUSY: #ifdef LDAP_CONNECT_ERROR case LDAP_CONNECT_ERROR: #endif case LDAP_LOCAL_ERROR: case LDAP_INVALID_CREDENTIALS: case LDAP_OPERATIONS_ERROR: default: /* connection problems */ ldap_conn_reconnect(conn); return 0; } } static int db_ldap_request_bind(struct ldap_connection *conn, struct ldap_request *request) { struct ldap_request_bind *brequest = (struct ldap_request_bind *)request; i_assert(request->type == LDAP_REQUEST_TYPE_BIND); i_assert(request->msgid == -1); i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH || conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT); i_assert(conn->pending_count == 0); request->msgid = ldap_bind(conn->ld, brequest->dn, request->auth_request->mech_password, LDAP_AUTH_SIMPLE); if (request->msgid == -1) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "ldap_bind(%s) failed: %s", brequest->dn, ldap_get_error(conn)); if (ldap_handle_error(conn) < 0) { /* broken request, remove it */ return 0; } return -1; } conn->conn_state = LDAP_CONN_STATE_BINDING; return 1; } static int db_ldap_request_search(struct ldap_connection *conn, struct ldap_request *request) { struct ldap_request_search *srequest = (struct ldap_request_search *)request; i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT); i_assert(request->msgid == -1); request->msgid = ldap_search(conn->ld, *srequest->base == '\0' ? NULL : srequest->base, conn->set.ldap_scope, srequest->filter, srequest->attributes, 0); if (request->msgid == -1) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "ldap_search(%s) parsing failed: %s", srequest->filter, ldap_get_error(conn)); if (ldap_handle_error(conn) < 0) { /* broken request, remove it */ return 0; } return -1; } return 1; } static bool db_ldap_request_queue_next(struct ldap_connection *conn) { struct ldap_request *const *requestp, *request; int ret = -1; /* connecting may call db_ldap_connect_finish(), which gets us back here. so do the connection before checking the request queue. */ if (db_ldap_connect(conn) < 0) return FALSE; if (conn->pending_count == aqueue_count(conn->request_queue)) { /* no non-pending requests */ return FALSE; } if (conn->pending_count > DB_LDAP_MAX_PENDING_REQUESTS) { /* wait until server has replied to some requests */ return FALSE; } requestp = array_idx(&conn->request_array, aqueue_idx(conn->request_queue, conn->pending_count)); request = *requestp; if (conn->pending_count > 0 && request->type == LDAP_REQUEST_TYPE_BIND) { /* we can't do binds until all existing requests are finished */ return FALSE; } switch (conn->conn_state) { case LDAP_CONN_STATE_DISCONNECTED: case LDAP_CONN_STATE_BINDING: /* wait until we're in bound state */ return FALSE; case LDAP_CONN_STATE_BOUND_AUTH: if (request->type == LDAP_REQUEST_TYPE_BIND) break; /* bind to default dn first */ i_assert(conn->pending_count == 0); (void)db_ldap_bind(conn); return FALSE; case LDAP_CONN_STATE_BOUND_DEFAULT: /* we can do anything in this state */ break; } switch (request->type) { case LDAP_REQUEST_TYPE_BIND: ret = db_ldap_request_bind(conn, request); break; case LDAP_REQUEST_TYPE_SEARCH: ret = db_ldap_request_search(conn, request); break; } if (ret > 0) { /* success */ i_assert(request->msgid != -1); conn->pending_count++; return TRUE; } else if (ret < 0) { /* disconnected */ return FALSE; } else { /* broken request, remove from queue */ aqueue_delete_tail(conn->request_queue); request->callback(conn, request, NULL); return TRUE; } } static bool db_ldap_check_limits(struct ldap_connection *conn, struct ldap_request *request) { struct ldap_request *const *first_requestp; unsigned int count; time_t secs_diff; count = aqueue_count(conn->request_queue); if (count == 0) return TRUE; first_requestp = array_idx(&conn->request_array, aqueue_idx(conn->request_queue, 0)); secs_diff = ioloop_time - (*first_requestp)->create_time; if (secs_diff > DB_LDAP_REQUEST_LOST_TIMEOUT_SECS) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "Connection appears to be hanging, reconnecting"); ldap_conn_reconnect(conn); return TRUE; } return TRUE; } void db_ldap_request(struct ldap_connection *conn, struct ldap_request *request) { i_assert(request->auth_request != NULL); request->msgid = -1; request->create_time = ioloop_time; if (!db_ldap_check_limits(conn, request)) { request->callback(conn, request, NULL); return; } aqueue_append(conn->request_queue, &request); (void)db_ldap_request_queue_next(conn); } static int db_ldap_connect_finish(struct ldap_connection *conn, int ret) { if (ret == LDAP_SERVER_DOWN) { i_error("LDAP: Can't connect to server: %s", conn->set.uris != NULL ? conn->set.uris : conn->set.hosts); return -1; } if (ret != LDAP_SUCCESS) { i_error("LDAP: binding failed (dn %s): %s", conn->set.dn == NULL ? "(none)" : conn->set.dn, ldap_get_error(conn)); return -1; } if (conn->to != NULL) timeout_remove(&conn->to); conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT; while (db_ldap_request_queue_next(conn)) ; return 0; } static void db_ldap_default_bind_finished(struct ldap_connection *conn, struct db_ldap_result *res) { int ret; i_assert(conn->pending_count == 0); conn->default_bind_msgid = -1; ret = ldap_result2error(conn->ld, res->msg, FALSE); if (db_ldap_connect_finish(conn, ret) < 0) { /* lost connection, close it */ db_ldap_conn_close(conn); } } static void db_ldap_abort_requests(struct ldap_connection *conn, unsigned int max_count, unsigned int timeout_secs, bool error, const char *reason) { struct ldap_request *const *requestp, *request; time_t diff; while (aqueue_count(conn->request_queue) > 0 && max_count > 0) { requestp = array_idx(&conn->request_array, aqueue_idx(conn->request_queue, 0)); request = *requestp; diff = ioloop_time - request->create_time; if (diff < (time_t)timeout_secs) break; /* timed out, abort */ aqueue_delete_tail(conn->request_queue); if (request->msgid != -1) { i_assert(conn->pending_count > 0); conn->pending_count--; } if (error) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "%s", reason); } else { auth_request_log_info(request->auth_request, AUTH_SUBSYS_DB, "%s", reason); } request->callback(conn, request, NULL); max_count--; } } static struct ldap_request * db_ldap_find_request(struct ldap_connection *conn, int msgid, unsigned int *idx_r) { struct ldap_request *const *requests, *request = NULL; unsigned int i, count; count = aqueue_count(conn->request_queue); if (count == 0) return NULL; requests = array_idx(&conn->request_array, 0); for (i = 0; i < count; i++) { request = requests[aqueue_idx(conn->request_queue, i)]; if (request->msgid == msgid) { *idx_r = i; return request; } if (request->msgid == -1) break; } return NULL; } static int db_ldap_fields_get_dn(struct ldap_connection *conn, struct ldap_request_search *request, struct db_ldap_result *res) { struct auth_request *auth_request = request->request.auth_request; struct ldap_request_named_result *named_res; struct db_ldap_result_iterate_context *ldap_iter; const char *name, *const *values; ldap_iter = db_ldap_result_iterate_init_full(conn, request, res->msg, TRUE, TRUE); while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) { if (values[1] != NULL) { auth_request_log_warning(auth_request, AUTH_SUBSYS_DB, "Multiple values found for '%s', " "using value '%s'", name, values[0]); } array_foreach_modifiable(&request->named_results, named_res) { if (strcmp(named_res->field->name, name) != 0) continue; /* In future we could also support LDAP URLs here */ named_res->dn = p_strdup(auth_request->pool, values[0]); } } db_ldap_result_iterate_deinit(&ldap_iter); return 0; } struct ldap_field_find_subquery_context { ARRAY_TYPE(string) attr_names; const char *name; }; static const char * db_ldap_field_subquery_find(const char *data, void *context) { struct ldap_field_find_subquery_context *ctx = context; char *ldap_attr; const char *p; if (*data != '\0') { data = t_strcut(data, ':'); p = strchr(data, '@'); if (p != NULL && strcmp(p+1, ctx->name) == 0) { ldap_attr = p_strdup_until(unsafe_data_stack_pool, data, p); array_append(&ctx->attr_names, &ldap_attr, 1); } } return NULL; } static int ldap_request_send_subquery(struct ldap_connection *conn, struct ldap_request_search *request, struct ldap_request_named_result *named_res) { static struct var_expand_func_table var_funcs_table[] = { { "ldap", db_ldap_field_subquery_find }, { "ldap_ptr", db_ldap_field_subquery_find }, { NULL, NULL } }; const struct ldap_field *field; const char *p; char *name; struct ldap_field_find_subquery_context ctx; string_t *tmp_str = t_str_new(64); i_zero(&ctx); t_array_init(&ctx.attr_names, 8); ctx.name = named_res->field->name; /* get the attributes names into array (ldapAttr@name -> ldapAttr) */ array_foreach(request->attr_map, field) { if (field->ldap_attr_name[0] == '\0') { str_truncate(tmp_str, 0); var_expand_with_funcs(tmp_str, field->value, NULL, var_funcs_table, &ctx); } else { p = strchr(field->ldap_attr_name, '@'); if (p != NULL && strcmp(p+1, named_res->field->name) == 0) { name = p_strdup_until(unsafe_data_stack_pool, field->ldap_attr_name, p); array_append(&ctx.attr_names, &name, 1); } } } array_append_zero(&ctx.attr_names); request->request.msgid = ldap_search(conn->ld, named_res->dn, LDAP_SCOPE_BASE, NULL, array_idx_modifiable(&ctx.attr_names, 0), 0); if (request->request.msgid == -1) { auth_request_log_error(request->request.auth_request, AUTH_SUBSYS_DB, "ldap_search(dn=%s) failed: %s", named_res->dn, ldap_get_error(conn)); return -1; } return 0; } static int db_ldap_search_save_result(struct ldap_request_search *request, struct db_ldap_result *res) { struct ldap_request_named_result *named_res; if (!array_is_created(&request->named_results)) { if (request->result != NULL) return -1; request->result = res; } else { named_res = array_idx_modifiable(&request->named_results, request->name_idx); if (named_res->result != NULL) return -1; named_res->result = res; } res->refcount++; return 0; } static int db_ldap_search_next_subsearch(struct ldap_connection *conn, struct ldap_request_search *request, struct db_ldap_result *res) { struct ldap_request_named_result *named_res; const struct ldap_field *field; if (request->result != NULL) res = request->result; if (!array_is_created(&request->named_results)) { /* see if we need to do more LDAP queries */ p_array_init(&request->named_results, request->request.auth_request->pool, 2); array_foreach(request->attr_map, field) { if (!field->value_is_dn) continue; named_res = array_append_space(&request->named_results); named_res->field = field; } if (db_ldap_fields_get_dn(conn, request, res) < 0) return -1; } else { request->name_idx++; } while (request->name_idx < array_count(&request->named_results)) { /* send the next LDAP query */ named_res = array_idx_modifiable(&request->named_results, request->name_idx); if (named_res->dn != NULL) { if (ldap_request_send_subquery(conn, request, named_res) < 0) return -1; return 1; } /* dn field wasn't returned, skip this */ request->name_idx++; } return 0; } static bool db_ldap_handle_request_result(struct ldap_connection *conn, struct ldap_request *request, unsigned int idx, struct db_ldap_result *res) { struct ldap_request_search *srequest = NULL; const struct ldap_request_named_result *named_res; int ret; bool final_result; i_assert(conn->pending_count > 0); if (request->type == LDAP_REQUEST_TYPE_BIND) { i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING); i_assert(conn->pending_count == 1); conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH; } else { srequest = (struct ldap_request_search *)request; switch (ldap_msgtype(res->msg)) { case LDAP_RES_SEARCH_ENTRY: case LDAP_RES_SEARCH_RESULT: break; case LDAP_RES_SEARCH_REFERENCE: /* we're going to ignore this */ return FALSE; default: i_error("LDAP: Reply with unexpected type %d", ldap_msgtype(res->msg)); return TRUE; } } if (ldap_msgtype(res->msg) == LDAP_RES_SEARCH_ENTRY) { ret = LDAP_SUCCESS; final_result = FALSE; } else { final_result = TRUE; ret = ldap_result2error(conn->ld, res->msg, 0); } /* LDAP_NO_SUCH_OBJECT is returned for nonexistent base */ if (ret != LDAP_SUCCESS && ret != LDAP_NO_SUCH_OBJECT && request->type == LDAP_REQUEST_TYPE_SEARCH) { /* handle search failures here */ struct ldap_request_search *srequest = (struct ldap_request_search *)request; if (!array_is_created(&srequest->named_results)) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "ldap_search(base=%s filter=%s) failed: %s", srequest->base, srequest->filter, ldap_err2string(ret)); } else { named_res = array_idx(&srequest->named_results, srequest->name_idx); auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "ldap_search(base=%s) failed: %s", named_res->dn, ldap_err2string(ret)); } res = NULL; } if (ret == LDAP_SUCCESS && srequest != NULL && !srequest->multi_entry) { /* expand any @results */ if (!final_result) { if (db_ldap_search_save_result(srequest, res) < 0) { auth_request_log_error(request->auth_request, AUTH_SUBSYS_DB, "LDAP search returned multiple entries"); res = NULL; } else { /* wait for finish */ return FALSE; } } else { ret = db_ldap_search_next_subsearch(conn, srequest, res); if (ret > 0) { /* more LDAP queries left */ return FALSE; } if (ret < 0) res = NULL; } } if (res == NULL && !final_result) { /* wait for the final reply */ request->failed = TRUE; return TRUE; } if (request->failed) res = NULL; if (final_result) { conn->pending_count--; aqueue_delete(conn->request_queue, idx); } T_BEGIN { if (res != NULL && srequest != NULL && srequest->result != NULL) request->callback(conn, request, srequest->result->msg); request->callback(conn, request, res == NULL ? NULL : res->msg); } T_END; if (idx > 0) { /* see if there are timed out requests */ db_ldap_abort_requests(conn, idx, DB_LDAP_REQUEST_LOST_TIMEOUT_SECS, TRUE, "Request lost"); } return TRUE; } static void db_ldap_result_unref(struct db_ldap_result **_res) { struct db_ldap_result *res = *_res; *_res = NULL; i_assert(res->refcount > 0); if (--res->refcount == 0) { ldap_msgfree(res->msg); i_free(res); } } static void db_ldap_request_free(struct ldap_request *request) { if (request->type == LDAP_REQUEST_TYPE_SEARCH) { struct ldap_request_search *srequest = (struct ldap_request_search *)request; struct ldap_request_named_result *named_res; if (srequest->result != NULL) db_ldap_result_unref(&srequest->result); if (array_is_created(&srequest->named_results)) { array_foreach_modifiable(&srequest->named_results, named_res) { if (named_res->result != NULL) db_ldap_result_unref(&named_res->result); } array_clear(&srequest->named_results); } } } static void db_ldap_handle_result(struct ldap_connection *conn, struct db_ldap_result *res) { struct auth_request *auth_request; struct ldap_request *request; unsigned int idx; int msgid; msgid = ldap_msgid(res->msg); if (msgid == conn->default_bind_msgid) { db_ldap_default_bind_finished(conn, res); return; } request = db_ldap_find_request(conn, msgid, &idx); if (request == NULL) { i_error("LDAP: Reply with unknown msgid %d", msgid); return; } /* request is allocated from auth_request's pool */ auth_request = request->auth_request; auth_request_ref(auth_request); if (db_ldap_handle_request_result(conn, request, idx, res)) db_ldap_request_free(request); auth_request_unref(&auth_request); } static void ldap_input(struct ldap_connection *conn) { struct timeval timeout; struct db_ldap_result *res; LDAPMessage *msg; time_t prev_reply_diff; int ret; do { if (conn->ld == NULL) return; i_zero(&timeout); ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &msg); #ifdef OPENLDAP_ASYNC_WORKAROUND if (ret == 0) { /* try again, there may be another in buffer */ ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &msg); } #endif if (ret <= 0) break; res = i_new(struct db_ldap_result, 1); res->refcount = 1; res->msg = msg; db_ldap_handle_result(conn, res); db_ldap_result_unref(&res); } while (conn->io != NULL); prev_reply_diff = ioloop_time - conn->last_reply_stamp; conn->last_reply_stamp = ioloop_time; if (ret > 0) { /* input disabled, continue once it's enabled */ i_assert(conn->io == NULL); } else if (ret == 0) { /* send more requests */ while (db_ldap_request_queue_next(conn)) ; } else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) { i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn)); ldap_conn_reconnect(conn); } else if (aqueue_count(conn->request_queue) > 0 || prev_reply_diff < DB_LDAP_IDLE_RECONNECT_SECS) { i_error("LDAP: Connection lost to LDAP server, reconnecting"); ldap_conn_reconnect(conn); } else { /* server probably disconnected an idle connection. don't reconnect until the next request comes. */ db_ldap_conn_close(conn); } } #ifdef HAVE_LDAP_SASL static int sasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED, void *defaults, void *interact) { struct db_ldap_sasl_bind_context *context = defaults; sasl_interact_t *in; const char *str; for (in = interact; in->id != SASL_CB_LIST_END; in++) { switch (in->id) { case SASL_CB_GETREALM: str = context->realm; break; case SASL_CB_AUTHNAME: str = context->authcid; break; case SASL_CB_USER: str = context->authzid; break; case SASL_CB_PASS: str = context->passwd; break; default: str = NULL; break; } if (str != NULL) { in->len = strlen(str); in->result = str; } } return LDAP_SUCCESS; } #endif static void ldap_connection_timeout(struct ldap_connection *conn) { i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING); i_error("LDAP %s: Initial binding to LDAP server timed out", conn->config_path); db_ldap_conn_close(conn); } #ifdef HAVE_LDAP_SASL static int db_ldap_bind_sasl(struct ldap_connection *conn) { struct db_ldap_sasl_bind_context context; int ret; i_zero(&context); context.authcid = conn->set.dn; context.passwd = conn->set.dnpass; context.realm = conn->set.sasl_realm; context.authzid = conn->set.sasl_authz_id; /* There doesn't seem to be a way to do SASL binding asynchronously.. */ ret = ldap_sasl_interactive_bind_s(conn->ld, NULL, conn->set.sasl_mech, NULL, NULL, LDAP_SASL_QUIET, sasl_interact, &context); if (db_ldap_connect_finish(conn, ret) < 0) return -1; conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT; return 0; } #else static int db_ldap_bind_sasl(struct ldap_connection *conn ATTR_UNUSED) { i_unreached(); /* already checked at init */ return -1; } #endif static int db_ldap_bind_simple(struct ldap_connection *conn) { int msgid; i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING); i_assert(conn->default_bind_msgid == -1); i_assert(conn->pending_count == 0); msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass, LDAP_AUTH_SIMPLE); if (msgid == -1) { i_assert(ldap_get_errno(conn) != LDAP_SUCCESS); if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) { /* lost connection, close it */ db_ldap_conn_close(conn); } return -1; } conn->conn_state = LDAP_CONN_STATE_BINDING; conn->default_bind_msgid = msgid; if (conn->to != NULL) timeout_remove(&conn->to); conn->to = timeout_add(DB_LDAP_REQUEST_LOST_TIMEOUT_SECS*1000, ldap_connection_timeout, conn); return 0; } static int db_ldap_bind(struct ldap_connection *conn) { if (conn->set.sasl_bind) { if (db_ldap_bind_sasl(conn) < 0) return -1; } else { if (db_ldap_bind_simple(conn) < 0) return -1; } return 0; } static void db_ldap_get_fd(struct ldap_connection *conn) { int ret; /* get the connection's fd */ ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *)&conn->fd); if (ret != LDAP_SUCCESS) { i_fatal("LDAP %s: Can't get connection fd: %s", conn->config_path, ldap_err2string(ret)); } if (conn->fd <= STDERR_FILENO) { /* Solaris LDAP library seems to be broken */ i_fatal("LDAP %s: Buggy LDAP library returned wrong fd: %d", conn->config_path, conn->fd); } i_assert(conn->fd != -1); net_set_nonblock(conn->fd, TRUE); } static void ATTR_NULL(1) db_ldap_set_opt(struct ldap_connection *conn, LDAP *ld, int opt, const void *value, const char *optname, const char *value_str) { int ret; ret = ldap_set_option(ld, opt, value); if (ret != LDAP_SUCCESS) { i_fatal("LDAP %s: Can't set option %s to %s: %s", conn->config_path, optname, value_str, ldap_err2string(ret)); } } static void ATTR_NULL(1) db_ldap_set_opt_str(struct ldap_connection *conn, LDAP *ld, int opt, const char *value, const char *optname) { if (value != NULL) db_ldap_set_opt(conn, ld, opt, value, optname, value); } static void db_ldap_set_tls_options(struct ldap_connection *conn) { #ifdef OPENLDAP_TLS_OPTIONS db_ldap_set_opt_str(conn, NULL, LDAP_OPT_X_TLS_CACERTFILE, conn->set.tls_ca_cert_file, "tls_ca_cert_file"); db_ldap_set_opt_str(conn, NULL, LDAP_OPT_X_TLS_CACERTDIR, conn->set.tls_ca_cert_dir, "tls_ca_cert_dir"); db_ldap_set_opt_str(conn, NULL, LDAP_OPT_X_TLS_CERTFILE, conn->set.tls_cert_file, "tls_cert_file"); db_ldap_set_opt_str(conn, NULL, LDAP_OPT_X_TLS_KEYFILE, conn->set.tls_key_file, "tls_key_file"); db_ldap_set_opt_str(conn, NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, conn->set.tls_cipher_suite, "tls_cipher_suite"); if (conn->set.tls_require_cert != NULL) { db_ldap_set_opt(conn, NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &conn->set.ldap_tls_require_cert_parsed, "tls_require_cert", conn->set.tls_require_cert); } #else if (conn->set.tls_ca_cert_file != NULL || conn->set.tls_ca_cert_dir != NULL || conn->set.tls_cert_file != NULL || conn->set.tls_key_file != NULL || conn->set.tls_cipher_suite != NULL) { i_fatal("LDAP %s: tls_* settings aren't supported by your LDAP library - they must not be set", conn->config_path); } #endif } static void db_ldap_set_options(struct ldap_connection *conn) { unsigned int ldap_version; int value; #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval tv; int ret; tv.tv_sec = DB_LDAP_CONNECT_TIMEOUT_SECS; tv.tv_usec = 0; ret = ldap_set_option(conn->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); if (ret != LDAP_SUCCESS) { i_fatal("LDAP %s: Can't set network-timeout: %s", conn->config_path, ldap_err2string(ret)); } #endif db_ldap_set_opt(conn, conn->ld, LDAP_OPT_DEREF, &conn->set.ldap_deref, "deref", conn->set.deref); #ifdef LDAP_OPT_DEBUG_LEVEL if (str_to_int(conn->set.debug_level, &value) >= 0 && value != 0) { db_ldap_set_opt(conn, NULL, LDAP_OPT_DEBUG_LEVEL, &value, "debug_level", conn->set.debug_level); } #endif ldap_version = conn->set.ldap_version; db_ldap_set_opt(conn, conn->ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version, "protocol_version", dec2str(ldap_version)); db_ldap_set_tls_options(conn); } static void db_ldap_init_ld(struct ldap_connection *conn) { int ret; if (conn->set.uris != NULL) { #ifdef LDAP_HAVE_INITIALIZE ret = ldap_initialize(&conn->ld, conn->set.uris); if (ret != LDAP_SUCCESS) { i_fatal("LDAP %s: ldap_initialize() failed with uris %s: %s", conn->config_path, conn->set.uris, ldap_err2string(ret)); } #else i_unreached(); /* already checked at init */ #endif } else { conn->ld = ldap_init(conn->set.hosts, LDAP_PORT); if (conn->ld == NULL) { i_fatal("LDAP %s: ldap_init() failed with hosts: %s", conn->config_path, conn->set.hosts); } } db_ldap_set_options(conn); } int db_ldap_connect(struct ldap_connection *conn) { int debug_level; bool debug; struct timeval start, end; int ret; debug = FALSE; if (str_to_int(conn->set.debug_level, &debug_level) >= 0) debug = debug_level > 0; if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED) return 0; if (debug) { if (gettimeofday(&start, NULL) < 0) i_zero(&start); } i_assert(conn->pending_count == 0); if (conn->delayed_connect) { conn->delayed_connect = FALSE; timeout_remove(&conn->to); } if (conn->ld == NULL) db_ldap_init_ld(conn); if (conn->set.tls) { #ifdef LDAP_HAVE_START_TLS_S ret = ldap_start_tls_s(conn->ld, NULL, NULL); if (ret != LDAP_SUCCESS) { if (ret == LDAP_OPERATIONS_ERROR && conn->set.uris != NULL && strncmp(conn->set.uris, "ldaps:", 6) == 0) { i_fatal("LDAP %s: Don't use both tls=yes " "and ldaps URI", conn->config_path); } i_error("LDAP %s: ldap_start_tls_s() failed: %s", conn->config_path, ldap_err2string(ret)); return -1; } #else i_unreached(); /* already checked at init */ #endif } if (db_ldap_bind(conn) < 0) return -1; if (debug) { if (gettimeofday(&end, NULL) == 0) { int msecs = timeval_diff_msecs(&end, &start); i_debug("LDAP initialization took %d msecs", msecs); } } db_ldap_get_fd(conn); conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); return 0; } static void db_ldap_connect_callback(struct ldap_connection *conn) { i_assert(conn->conn_state == LDAP_CONN_STATE_DISCONNECTED); (void)db_ldap_connect(conn); } void db_ldap_connect_delayed(struct ldap_connection *conn) { if (conn->delayed_connect) return; conn->delayed_connect = TRUE; i_assert(conn->to == NULL); conn->to = timeout_add_short(0, db_ldap_connect_callback, conn); } void db_ldap_enable_input(struct ldap_connection *conn, bool enable) { if (!enable) { if (conn->io != NULL) io_remove(&conn->io); } else { if (conn->io == NULL && conn->fd != -1) { conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); ldap_input(conn); } } } static void db_ldap_disconnect_timeout(struct ldap_connection *conn) { db_ldap_abort_requests(conn, UINT_MAX, DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS, FALSE, "Aborting (timeout), we're not connected to LDAP server"); if (aqueue_count(conn->request_queue) == 0) { /* no requests left, remove this timeout handler */ timeout_remove(&conn->to); } } static void db_ldap_conn_close(struct ldap_connection *conn) { struct ldap_request *const *requests, *request; unsigned int i; conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; conn->delayed_connect = FALSE; conn->default_bind_msgid = -1; if (conn->to != NULL) timeout_remove(&conn->to); if (conn->pending_count != 0) { requests = array_idx(&conn->request_array, 0); for (i = 0; i < conn->pending_count; i++) { request = requests[aqueue_idx(conn->request_queue, i)]; i_assert(request->msgid != -1); request->msgid = -1; } conn->pending_count = 0; } if (conn->ld != NULL) { ldap_unbind(conn->ld); conn->ld = NULL; } conn->fd = -1; if (conn->io != NULL) { /* the fd may have already been closed before ldap_unbind(), so we'll have to use io_remove_closed(). */ io_remove_closed(&conn->io); } if (aqueue_count(conn->request_queue) > 0) { conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS * 1000/2, db_ldap_disconnect_timeout, conn); } } struct ldap_field_find_context { ARRAY_TYPE(string) attr_names; pool_t pool; }; static const char * db_ldap_field_find(const char *data, void *context) { struct ldap_field_find_context *ctx = context; char *ldap_attr; if (*data != '\0') { ldap_attr = p_strdup(ctx->pool, t_strcut(data, ':')); if (strchr(ldap_attr, '@') == NULL) array_append(&ctx->attr_names, &ldap_attr, 1); } return NULL; } void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char ***attr_names_r, ARRAY_TYPE(ldap_field) *attr_map, const char *skip_attr) { static struct var_expand_func_table var_funcs_table[] = { { "ldap", db_ldap_field_find }, { "ldap_ptr", db_ldap_field_find }, { NULL, NULL } }; struct ldap_field_find_context ctx; struct ldap_field *field; string_t *tmp_str; const char *const *attr, *attr_data, *p; char *ldap_attr, *name, *templ; unsigned int i; if (*attrlist == '\0') return; attr = t_strsplit_spaces(attrlist, ","); tmp_str = t_str_new(128); ctx.pool = conn->pool; p_array_init(&ctx.attr_names, conn->pool, 16); for (i = 0; attr[i] != NULL; i++) { /* allow spaces here so "foo=1, bar=2" works */ attr_data = attr[i]; while (*attr_data == ' ') attr_data++; p = strchr(attr_data, '='); if (p == NULL) ldap_attr = name = p_strdup(conn->pool, attr_data); else if (attr_data[0] == '@') { ldap_attr = ""; name = p_strdup(conn->pool, attr_data); } else { ldap_attr = p_strdup_until(conn->pool, attr_data, p); name = p_strdup(conn->pool, p + 1); } templ = strchr(name, '='); if (templ == NULL) { if (*ldap_attr == '\0') { /* =foo static value */ templ = ""; } } else { *templ++ = '\0'; str_truncate(tmp_str, 0); var_expand_with_funcs(tmp_str, templ, NULL, var_funcs_table, &ctx); if (strchr(templ, '%') == NULL) { /* backwards compatibility: attr=name=prefix means same as attr=name=prefix%$ when %vars are missing */ templ = p_strconcat(conn->pool, templ, "%$", NULL); } } if (*name == '\0') i_error("LDAP %s: Invalid attrs entry: %s", conn->config_path, attr_data); else if (skip_attr == NULL || strcmp(skip_attr, name) != 0) { field = array_append_space(attr_map); if (name[0] == '@') { /* @name=ldapField */ name++; field->value_is_dn = TRUE; } else if (name[0] == '!' && name == ldap_attr) { /* !ldapAttr */ name = ""; i_assert(ldap_attr[0] == '!'); ldap_attr++; field->skip = TRUE; } field->name = name; field->value = templ; field->ldap_attr_name = ldap_attr; if (*ldap_attr != '\0' && strchr(ldap_attr, '@') == NULL) { /* root request's attribute */ array_append(&ctx.attr_names, &ldap_attr, 1); } } } array_append_zero(&ctx.attr_names); *attr_names_r = array_idx_modifiable(&ctx.attr_names, 0); } static const struct var_expand_table * db_ldap_value_get_var_expand_table(struct auth_request *auth_request, const char *ldap_value) { struct var_expand_table *table; unsigned int count = 1; table = auth_request_get_var_expand_table_full(auth_request, NULL, &count); table[0].key = '$'; table[0].value = ldap_value; return table; } #define IS_LDAP_ESCAPED_CHAR(c) \ ((((unsigned char)(c)) & 0x80) != 0 || strchr(LDAP_ESCAPE_CHARS, (c)) != NULL) const char *ldap_escape(const char *str, const struct auth_request *auth_request ATTR_UNUSED) { string_t *ret = NULL; for (const char *p = str; *p != '\0'; p++) { if (IS_LDAP_ESCAPED_CHAR(*p)) { if (ret == NULL) { ret = t_str_new((size_t) (p - str) + 64); str_append_n(ret, str, (size_t) (p - str)); } str_printfa(ret, "\\%02X", (unsigned char)*p); } else if (ret != NULL) str_append_c(ret, *p); } return ret == NULL ? str : str_c(ret); } static bool ldap_field_hide_password(struct db_ldap_result_iterate_context *ctx, const char *attr) { const struct ldap_field *field; if (ctx->auth_request->set->debug_passwords) return FALSE; array_foreach(ctx->attr_map, field) { if (strcmp(field->ldap_attr_name, attr) == 0) { if (strcmp(field->name, "password") == 0 || strcmp(field->name, "password_noscheme") == 0) return TRUE; } } return FALSE; } static void get_ldap_fields(struct db_ldap_result_iterate_context *ctx, struct ldap_connection *conn, LDAPMessage *entry, const char *suffix) { struct db_ldap_value *ldap_value; char *attr, **vals; unsigned int i, count; BerElement *ber; attr = ldap_first_attribute(conn->ld, entry, &ber); while (attr != NULL) { vals = ldap_get_values(conn->ld, entry, attr); ldap_value = p_new(ctx->pool, struct db_ldap_value, 1); if (vals == NULL) { ldap_value->values = p_new(ctx->pool, const char *, 1); count = 0; } else { for (count = 0; vals[count] != NULL; count++) ; } ldap_value->values = p_new(ctx->pool, const char *, count + 1); for (i = 0; i < count; i++) ldap_value->values[i] = p_strdup(ctx->pool, vals[i]); if (ctx->debug != NULL) { str_printfa(ctx->debug, " %s%s=", attr, suffix); if (count == 0) str_append(ctx->debug, ""); else if (ldap_field_hide_password(ctx, attr)) str_append(ctx->debug, PASSWORD_HIDDEN_STR); else { str_append(ctx->debug, ldap_value->values[0]); for (i = 1; i < count; i++) { str_printfa(ctx->debug, ",%s", ldap_value->values[0]); } } } hash_table_insert(ctx->ldap_attrs, p_strconcat(ctx->pool, attr, suffix, NULL), ldap_value); ldap_value_free(vals); ldap_memfree(attr); attr = ldap_next_attribute(conn->ld, entry, ber); } ber_free(ber, 0); } struct db_ldap_result_iterate_context * db_ldap_result_iterate_init_full(struct ldap_connection *conn, struct ldap_request_search *ldap_request, LDAPMessage *res, bool skip_null_values, bool iter_dn_values) { struct db_ldap_result_iterate_context *ctx; const struct ldap_request_named_result *named_res; const char *suffix; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"ldap result iter", 1024); ctx = p_new(pool, struct db_ldap_result_iterate_context, 1); ctx->pool = pool; ctx->auth_request = ldap_request->request.auth_request; ctx->attr_map = ldap_request->attr_map; ctx->skip_null_values = skip_null_values; ctx->iter_dn_values = iter_dn_values; hash_table_create(&ctx->ldap_attrs, pool, 0, strcase_hash, strcasecmp); ctx->var = str_new(ctx->pool, 256); if (ctx->auth_request->debug) ctx->debug = t_str_new(256); ctx->ldap_msg = res; ctx->ld = conn->ld; get_ldap_fields(ctx, conn, res, ""); if (array_is_created(&ldap_request->named_results)) { array_foreach(&ldap_request->named_results, named_res) { suffix = t_strdup_printf("@%s", named_res->field->name); if (named_res->result != NULL) { get_ldap_fields(ctx, conn, named_res->result->msg, suffix); } } } return ctx; } struct db_ldap_result_iterate_context * db_ldap_result_iterate_init(struct ldap_connection *conn, struct ldap_request_search *ldap_request, LDAPMessage *res, bool skip_null_values) { return db_ldap_result_iterate_init_full(conn, ldap_request, res, skip_null_values, FALSE); } static const char *db_ldap_field_get_default(const char *data) { const char *p; p = strchr(data, ':'); if (p == NULL) return ""; else { /* default value given */ return p+1; } } static const char *db_ldap_field_expand(const char *data, void *context) { struct db_ldap_result_iterate_context *ctx = context; struct db_ldap_value *ldap_value; const char *field_name = t_strcut(data, ':'); ldap_value = hash_table_lookup(ctx->ldap_attrs, field_name); if (ldap_value == NULL) { /* requested ldap attribute wasn't returned at all */ if (ctx->debug) str_printfa(ctx->debug, "; %s missing", field_name); return db_ldap_field_get_default(data); } ldap_value->used = TRUE; if (ldap_value->values[0] == NULL) { /* no value for ldap attribute */ return db_ldap_field_get_default(data); } if (ldap_value->values[1] != NULL) { auth_request_log_warning(ctx->auth_request, AUTH_SUBSYS_DB, "Multiple values found for '%s', using value '%s'", field_name, ldap_value->values[0]); } return ldap_value->values[0]; } static const char *db_ldap_field_ptr_expand(const char *data, void *context) { struct db_ldap_result_iterate_context *ctx = context; const char *field_name, *suffix; suffix = strchr(t_strcut(data, ':'), '@'); field_name = db_ldap_field_expand(data, ctx); if (field_name[0] == '\0') return ""; field_name = t_strconcat(field_name, suffix, NULL); return db_ldap_field_expand(field_name, ctx); } static const char * db_ldap_field_dn_expand(const char *data ATTR_UNUSED, void *context ATTR_UNUSED) { struct db_ldap_result_iterate_context *ctx = context; char *dn = ldap_get_dn(ctx->ld, ctx->ldap_msg); const char *dn_dup = t_strdup(dn); ldap_memfree(dn); return dn_dup; } static struct var_expand_func_table ldap_var_funcs_table[] = { { "ldap", db_ldap_field_expand }, { "ldap_ptr", db_ldap_field_ptr_expand }, { "ldap_dn", db_ldap_field_dn_expand }, { NULL, NULL } }; static const char *const * db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx, const struct ldap_field *field, struct db_ldap_value *ldap_value) { const struct var_expand_table *var_table; const char *const *values; if (ldap_value != NULL) values = ldap_value->values; else { /* LDAP attribute doesn't exist */ ctx->val_1_arr[0] = NULL; values = ctx->val_1_arr; } if (field->value == NULL) { /* use the LDAP attribute's value */ } else { /* template */ if (values[0] == NULL && *field->ldap_attr_name != '\0') { /* ldapAttr=key=template%$, but ldapAttr doesn't exist. */ return values; } if (values[0] != NULL && values[1] != NULL) { auth_request_log_warning(ctx->auth_request, AUTH_SUBSYS_DB, "Multiple values found for '%s', " "using value '%s'", field->name, values[0]); } /* do this lookup separately for each expansion, because: 1) the values are allocated from data stack 2) if "user" field is updated, we want %u/%n/%d updated (and less importantly the same for other variables) */ var_table = db_ldap_value_get_var_expand_table(ctx->auth_request, values[0]); var_expand_with_funcs(ctx->var, field->value, var_table, ldap_var_funcs_table, ctx); ctx->val_1_arr[0] = str_c(ctx->var); values = ctx->val_1_arr; } return values; } bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx, const char **name_r, const char *const **values_r) { const struct ldap_field *field; struct db_ldap_value *ldap_value; unsigned int pos; do { if (ctx->attr_idx == array_count(ctx->attr_map)) return FALSE; field = array_idx(ctx->attr_map, ctx->attr_idx++); } while (field->value_is_dn != ctx->iter_dn_values || field->skip); ldap_value = *field->ldap_attr_name == '\0' ? NULL : hash_table_lookup(ctx->ldap_attrs, field->ldap_attr_name); if (ldap_value != NULL) ldap_value->used = TRUE; else if (ctx->debug && *field->ldap_attr_name != '\0') str_printfa(ctx->debug, "; %s missing", field->ldap_attr_name); str_truncate(ctx->var, 0); *values_r = db_ldap_result_return_value(ctx, field, ldap_value); if (strchr(field->name, '%') == NULL) *name_r = field->name; else { /* expand %variables also for LDAP name fields. we'll use the same ctx->var, which may already contain the value. */ str_append_c(ctx->var, '\0'); pos = str_len(ctx->var); var_expand_with_funcs(ctx->var, field->name, auth_request_get_var_expand_table(ctx->auth_request, NULL), ldap_var_funcs_table, ctx); *name_r = str_c(ctx->var) + pos; } if (ctx->skip_null_values && (*values_r)[0] == NULL) { /* no values. don't confuse the caller with this reply. */ return db_ldap_result_iterate_next(ctx, name_r, values_r); } return TRUE; } static void db_ldap_result_finish_debug(struct db_ldap_result_iterate_context *ctx) { struct hash_iterate_context *iter; char *name; struct db_ldap_value *value; unsigned int unused_count = 0; size_t orig_len; orig_len = str_len(ctx->debug); if (orig_len == 0) { auth_request_log_debug(ctx->auth_request, AUTH_SUBSYS_DB, "no fields returned by the server"); return; } str_append(ctx->debug, "; "); iter = hash_table_iterate_init(ctx->ldap_attrs); while (hash_table_iterate(iter, ctx->ldap_attrs, &name, &value)) { if (!value->used) { str_printfa(ctx->debug, "%s,", name); unused_count++; } } hash_table_iterate_deinit(&iter); if (unused_count == 0) str_truncate(ctx->debug, orig_len); else { str_truncate(ctx->debug, str_len(ctx->debug)-1); str_append(ctx->debug, " unused"); } auth_request_log_debug(ctx->auth_request, AUTH_SUBSYS_DB, "result: %s", str_c(ctx->debug) + 1); } void db_ldap_result_iterate_deinit(struct db_ldap_result_iterate_context **_ctx) { struct db_ldap_result_iterate_context *ctx = *_ctx; *_ctx = NULL; if (ctx->debug != NULL) db_ldap_result_finish_debug(ctx); hash_table_destroy(&ctx->ldap_attrs); pool_unref(&ctx->pool); } static const char *parse_setting(const char *key, const char *value, struct ldap_connection *conn) { return parse_setting_from_defs(conn->pool, setting_defs, &conn->set, key, value); } static struct ldap_connection *ldap_conn_find(const char *config_path) { struct ldap_connection *conn; for (conn = ldap_connections; conn != NULL; conn = conn->next) { if (strcmp(conn->config_path, config_path) == 0) return conn; } return NULL; } struct ldap_connection *db_ldap_init(const char *config_path, bool userdb) { struct ldap_connection *conn; const char *str, *error; pool_t pool; /* see if it already exists */ conn = ldap_conn_find(config_path); if (conn != NULL) { if (userdb) conn->userdb_used = TRUE; conn->refcount++; return conn; } if (*config_path == '\0') i_fatal("LDAP: Configuration file path not given"); pool = pool_alloconly_create("ldap_connection", 1024); conn = p_new(pool, struct ldap_connection, 1); conn->pool = pool; conn->refcount = 1; conn->userdb_used = userdb; conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; conn->default_bind_msgid = -1; conn->fd = -1; conn->config_path = p_strdup(pool, config_path); conn->set = default_ldap_settings; if (!settings_read_nosection(config_path, parse_setting, conn, &error)) i_fatal("ldap %s: %s", config_path, error); if (conn->set.base == NULL) i_fatal("LDAP %s: No base given", config_path); if (conn->set.uris == NULL && conn->set.hosts == NULL) i_fatal("LDAP %s: No uris or hosts set", config_path); #ifndef LDAP_HAVE_INITIALIZE if (conn->set.uris != NULL) { i_fatal("LDAP %s: uris set, but Dovecot compiled without support for LDAP uris " "(ldap_initialize() not supported by LDAP library)", config_path); } #endif #ifndef LDAP_HAVE_START_TLS_S if (conn->set.tls) i_fatal("LDAP %s: tls=yes, but your LDAP library doesn't support TLS", config_path); #endif #ifndef HAVE_LDAP_SASL if (conn->set.sasl_bind) i_fatal("LDAP %s: sasl_bind=yes but no SASL support compiled in", conn->config_path); #endif if (conn->set.ldap_version < 3) { if (conn->set.sasl_bind) i_fatal("LDAP %s: sasl_bind=yes requires ldap_version=3", config_path); if (conn->set.tls) i_fatal("LDAP %s: tls=yes requires ldap_version=3", config_path); } #ifdef OPENLDAP_TLS_OPTIONS if (conn->set.tls_require_cert != NULL) { if (tls_require_cert2str(conn->set.tls_require_cert, &conn->set.ldap_tls_require_cert_parsed) < 0) i_fatal("LDAP %s: Unknown tls_require_cert value '%s'", config_path, conn->set.tls_require_cert); } #endif if (*conn->set.ldaprc_path != '\0') { str = getenv("LDAPRC"); if (str != NULL && strcmp(str, conn->set.ldaprc_path) != 0) { i_fatal("LDAP %s: Multiple different ldaprc_path " "settings not allowed (%s and %s)", config_path, str, conn->set.ldaprc_path); } env_put(t_strconcat("LDAPRC=", conn->set.ldaprc_path, NULL)); } if (deref2str(conn->set.deref, &conn->set.ldap_deref) < 0) i_fatal("LDAP %s: Unknown deref option '%s'", config_path, conn->set.deref); if (scope2str(conn->set.scope, &conn->set.ldap_scope) < 0) i_fatal("LDAP %s: Unknown scope option '%s'", config_path, conn->set.scope); i_array_init(&conn->request_array, 512); conn->request_queue = aqueue_init(&conn->request_array.arr); conn->next = ldap_connections; ldap_connections = conn; db_ldap_init_ld(conn); return conn; } void db_ldap_unref(struct ldap_connection **_conn) { struct ldap_connection *conn = *_conn; struct ldap_connection **p; *_conn = NULL; i_assert(conn->refcount >= 0); if (--conn->refcount > 0) return; for (p = &ldap_connections; *p != NULL; p = &(*p)->next) { if (*p == conn) { *p = conn->next; break; } } db_ldap_abort_requests(conn, UINT_MAX, 0, FALSE, "Shutting down"); i_assert(conn->pending_count == 0); db_ldap_conn_close(conn); i_assert(conn->to == NULL); array_free(&conn->request_array); aqueue_deinit(&conn->request_queue); pool_unref(&conn->pool); } #ifndef BUILTIN_LDAP /* Building a plugin */ extern struct passdb_module_interface passdb_ldap_plugin; extern struct userdb_module_interface userdb_ldap_plugin; void authdb_ldap_init(void); void authdb_ldap_deinit(void); void authdb_ldap_init(void) { passdb_register_module(&passdb_ldap_plugin); userdb_register_module(&userdb_ldap_plugin); } void authdb_ldap_deinit(void) { passdb_unregister_module(&passdb_ldap_plugin); userdb_unregister_module(&userdb_ldap_plugin); } #endif #endif dovecot-2.2.33.2/src/auth/userdb-vpopmail.c0000644000175000017500000001264613165463624015355 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* Thanks to Courier-IMAP for showing how the vpopmail API should be used */ #include "auth-common.h" #include "userdb.h" #if defined(PASSDB_VPOPMAIL) || defined(USERDB_VPOPMAIL) #include "str.h" #include "var-expand.h" #include "userdb-vpopmail.h" struct vpopmail_userdb_module { struct userdb_module module; const char *quota_template_key; const char *quota_template_value; }; struct vqpasswd *vpopmail_lookup_vqp(struct auth_request *request, char vpop_user[VPOPMAIL_LIMIT], char vpop_domain[VPOPMAIL_LIMIT]) { struct vqpasswd *vpw; /* vpop_user must be zero-filled or parse_email() leaves an extra character after the user name. we'll fill vpop_domain as well just to be sure... */ memset(vpop_user, '\0', VPOPMAIL_LIMIT); memset(vpop_domain, '\0', VPOPMAIL_LIMIT); if (parse_email(request->user, vpop_user, vpop_domain, VPOPMAIL_LIMIT-1) < 0) { auth_request_log_info(request, AUTH_SUBSYS_DB, "parse_email() failed"); return NULL; } auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup user=%s domain=%s", vpop_user, vpop_domain); vpw = vauth_getpw(vpop_user, vpop_domain); if (vpw == NULL) { auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); return NULL; } return vpw; } #endif #ifdef USERDB_VPOPMAIL static int userdb_vpopmail_get_quota(const char *template, const char *vpop_str, const char **quota_r, const char **error_r) { struct var_expand_table *tab; string_t *quota; if (template == NULL || *vpop_str == '\0' || strcmp(vpop_str, "NOQUOTA") == 0) { *quota_r = ""; return 0; } tab = t_new(struct var_expand_table, 2); tab[0].key = 'q'; tab[0].value = format_maildirquota(vpop_str); quota = t_str_new(128); var_expand(quota, template, tab); *quota_r = str_c(quota); return 0; } static void vpopmail_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct vpopmail_userdb_module *module = (struct vpopmail_userdb_module *)_module; char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT]; struct vqpasswd *vpw; const char *quota, *error; uid_t uid; gid_t gid; vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain); if (vpw == NULL) { callback(USERDB_RESULT_USER_UNKNOWN, auth_request); return; } /* we have to get uid/gid separately, because the gid field in struct vqpasswd isn't really gid at all but just some flags... */ if (vget_assign(vpop_domain, NULL, 0, &uid, &gid) == NULL) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "vget_assign(%s) failed", vpop_domain); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } if (auth_request->successful) { /* update the last login only when we're really */ vset_lastauth(vpop_user, vpop_domain, t_strdup_noconst(auth_request->service)); } if (vpw->pw_dir == NULL || vpw->pw_dir[0] == '\0') { /* user's homedir doesn't exist yet, create it */ auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "pw_dir isn't set, creating"); if (make_user_dir(vpop_user, vpop_domain, uid, gid) == NULL) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "make_user_dir(%s, %s) failed", vpop_user, vpop_domain); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } /* get the user again so pw_dir is visible */ vpw = vauth_getpw(vpop_user, vpop_domain); if (vpw == NULL) { callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } } if (userdb_vpopmail_get_quota(module->quota_template_value, vpw->pw_shell, "a, &error) < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "userdb_vpopmail_get_quota(%s, %s) failed: %s", module->quota_template_value, vpw->pw_shell, error); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } auth_request_set_userdb_field(auth_request, "uid", dec2str(uid)); auth_request_set_userdb_field(auth_request, "gid", dec2str(gid)); auth_request_set_userdb_field(auth_request, "home", vpw->pw_dir); if (*quota != '\0') { auth_request_set_userdb_field(auth_request, module->quota_template_key, quota); } callback(USERDB_RESULT_OK, auth_request); } static struct userdb_module * vpopmail_preinit(pool_t pool, const char *args) { struct vpopmail_userdb_module *module; const char *const *tmp, *p; module = p_new(pool, struct vpopmail_userdb_module, 1); module->module.blocking = TRUE; for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { if (strncmp(*tmp, "cache_key=", 10) == 0) module->module.default_cache_key = p_strdup(pool, *tmp + 10); else if (strncmp(*tmp, "quota_template=", 15) == 0) { p = strchr(*tmp + 15, '='); if (p == NULL) { i_fatal("vpopmail userdb: " "quota_template missing '='"); } module->quota_template_key = p_strdup_until(pool, *tmp + 15, p); module->quota_template_value = p_strdup(pool, p + 1); } else if (strcmp(*tmp, "blocking=no") == 0) { module->module.blocking = FALSE; } else i_fatal("userdb vpopmail: Unknown setting: %s", *tmp); } return &module->module; } struct userdb_module_interface userdb_vpopmail = { "vpopmail", vpopmail_preinit, NULL, NULL, vpopmail_lookup, NULL, NULL, NULL }; #else struct userdb_module_interface userdb_vpopmail = { .name = "vpopmail" }; #endif dovecot-2.2.33.2/src/auth/auth-client-connection.h0000644000175000017500000000150513165463624016613 00000000000000#ifndef AUTH_CLIENT_CONNECTION_H #define AUTH_CLIENT_CONNECTION_H #include "master-auth.h" struct auth_client_connection { struct auth_client_connection *prev, *next; struct auth *auth; int refcount; int fd; struct io *io; struct istream *input; struct ostream *output; unsigned int pid; unsigned int connect_uid; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; struct auth_request_handler *request_handler; unsigned int login_requests:1; unsigned int version_received:1; unsigned int token_auth:1; }; void auth_client_connection_create(struct auth *auth, int fd, bool login_requests, bool token_auth); void auth_client_connection_destroy(struct auth_client_connection **conn); struct auth_client_connection * auth_client_connection_lookup(unsigned int pid); void auth_client_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/auth/passdb.h0000644000175000017500000000755613165463624013531 00000000000000#ifndef PASSDB_H #define PASSDB_H #include "md5.h" #define IS_VALID_PASSWD(pass) \ ((pass)[0] != '\0' && (pass)[0] != '*' && (pass)[0] != '!') struct auth_request; struct auth_passdb_settings; enum passdb_result { PASSDB_RESULT_INTERNAL_FAILURE = -1, PASSDB_RESULT_SCHEME_NOT_AVAILABLE = -2, PASSDB_RESULT_USER_UNKNOWN = -3, PASSDB_RESULT_USER_DISABLED = -4, PASSDB_RESULT_PASS_EXPIRED = -5, PASSDB_RESULT_NEXT = -6, PASSDB_RESULT_PASSWORD_MISMATCH = 0, PASSDB_RESULT_OK = 1 }; typedef void verify_plain_callback_t(enum passdb_result result, struct auth_request *request); typedef void lookup_credentials_callback_t(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *request); typedef void set_credentials_callback_t(bool success, struct auth_request *request); struct passdb_module_interface { const char *name; struct passdb_module *(*preinit)(pool_t pool, const char *args); void (*init)(struct passdb_module *module); void (*deinit)(struct passdb_module *module); /* Check if plaintext password matches */ void (*verify_plain)(struct auth_request *request, const char *password, verify_plain_callback_t *callback); /* Return authentication credentials, set in auth_request->credentials. */ void (*lookup_credentials)(struct auth_request *request, lookup_credentials_callback_t *callback); /* Update credentials */ int (*set_credentials)(struct auth_request *request, const char *new_credentials, set_credentials_callback_t *callback); }; struct passdb_module { const char *args; /* The default caching key for this module, or NULL if caching isn't wanted. This is updated by settings in auth_passdb. */ #define default_cache_key cache_key /* FIXME: remove in v2.3 - for API backwards compatibility */ const char *default_cache_key; /* Default password scheme for this module. If cache_key is set, must not be NULL. */ const char *default_pass_scheme; /* Supported authentication mechanisms, NULL is all, [NULL] is none*/ const char *const *mechanisms; /* Username filter, NULL is no filter */ const char *const *username_filter; /* If blocking is set to TRUE, use child processes to access this passdb. */ bool blocking; /* id is used by blocking passdb to identify the passdb */ unsigned int id; /* number of time init() has been called */ int init_refcount; /* WARNING: avoid adding anything here that isn't based on args. if you do, you need to change passdb.c:passdb_find() also to avoid accidentally merging wrong passdbs. */ struct passdb_module_interface iface; }; /* Try to get credentials in wanted scheme (request->credentials_scheme) from given input. Returns FALSE if this wasn't possible (unknown scheme, conversion not possible or invalid credentials). If wanted scheme is "", the credentials are returned as-is without any checks. This is useful mostly just to see if there exist any credentials at all. */ bool passdb_get_credentials(struct auth_request *auth_request, const char *input, const char *input_scheme, const unsigned char **credentials_r, size_t *size_r); void passdb_handle_credentials(enum passdb_result result, const char *password, const char *scheme, lookup_credentials_callback_t *callback, struct auth_request *auth_request); struct passdb_module * passdb_preinit(pool_t pool, const struct auth_passdb_settings *set); void passdb_init(struct passdb_module *passdb); void passdb_deinit(struct passdb_module *passdb); void passdb_register_module(struct passdb_module_interface *iface); void passdb_unregister_module(struct passdb_module_interface *iface); void passdbs_generate_md5(unsigned char md5[STATIC_ARRAY MD5_RESULTLEN]); void passdbs_init(void); void passdbs_deinit(void); #include "auth-request.h" #endif dovecot-2.2.33.2/src/auth/mycrypt.h0000644000175000017500000000033113123174404013731 00000000000000#ifndef MYCRYPT_H #define MYCRYPT_H /* A simple wrapper to crypt(). Problem with it is that it requires _XOPEN_SOURCE define which breaks other things. */ char *mycrypt(const char *key, const char *salt); #endif dovecot-2.2.33.2/src/auth/passdb-bsdauth.c0000644000175000017500000000444013123174404015126 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_BSDAUTH #include "safe-memset.h" #include "auth-cache.h" #include "ipwd.h" #include "mycrypt.h" #include #include static void bsdauth_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passwd pw; const char *type; int result; auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); switch (i_getpwnam(request->user, &pw)) { case -1: auth_request_log_error(request, AUTH_SUBSYS_DB, "getpwnam() failed: %m"); callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; case 0: auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); callback(PASSDB_RESULT_USER_UNKNOWN, request); return; } /* check if the password is valid */ type = t_strdup_printf("auth-%s", request->service); result = auth_userokay(request->user, NULL, t_strdup_noconst(type), t_strdup_noconst(password)); /* clear the passwords from memory */ safe_memset(pw.pw_passwd, 0, strlen(pw.pw_passwd)); if (result == 0) { auth_request_log_password_mismatch(request, AUTH_SUBSYS_DB); callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); callback(PASSDB_RESULT_OK, request); } static struct passdb_module * bsdauth_preinit(pool_t pool, const char *args) { struct passdb_module *module; module = p_new(pool, struct passdb_module, 1); module->default_pass_scheme = "PLAIN"; /* same reason as PAM */ module->blocking = TRUE; if (strcmp(args, "blocking=no") == 0) module->blocking = FALSE; else if (strncmp(args, "cache_key=", 10) == 0) module->default_cache_key = auth_cache_parse_key(pool, args + 10); else if (*args != '\0') i_fatal("passdb bsdauth: Unknown setting: %s", args); return module; } static void bsdauth_deinit(struct passdb_module *module ATTR_UNUSED) { endpwent(); } struct passdb_module_interface passdb_bsdauth = { "bsdauth", bsdauth_preinit, NULL, bsdauth_deinit, bsdauth_verify_plain, NULL, NULL }; #else struct passdb_module_interface passdb_bsdauth = { .name = "bsdauth" }; #endif dovecot-2.2.33.2/src/auth/auth-fields.h0000644000175000017500000000352713123174404014441 00000000000000#ifndef AUTH_FIELDS_H #define AUTH_FIELDS_H struct auth_request; enum auth_field_flags { /* This field is internal to auth process and won't be sent to client */ AUTH_FIELD_FLAG_HIDDEN = 0x01, /* Changed since last snapshot. Set/cleared automatically. */ AUTH_FIELD_FLAG_CHANGED = 0x02 }; struct auth_field { const char *key, *value; enum auth_field_flags flags; }; ARRAY_DEFINE_TYPE(auth_field, struct auth_field); struct auth_fields *auth_fields_init(pool_t pool); void auth_fields_add(struct auth_fields *fields, const char *key, const char *value, enum auth_field_flags flags) ATTR_NULL(3); void auth_fields_reset(struct auth_fields *fields); void auth_fields_remove(struct auth_fields *fields, const char *key); const char *auth_fields_find(struct auth_fields *fields, const char *key); bool auth_fields_exists(struct auth_fields *fields, const char *key); void auth_fields_import(struct auth_fields *fields, const char *str, enum auth_field_flags flags); void auth_fields_import_prefixed(struct auth_fields *fields, const char *prefix, const char *str, enum auth_field_flags flags); const ARRAY_TYPE(auth_field) *auth_fields_export(struct auth_fields *fields); /* Append fields where (flag & flags_mask) == flags_result. */ void auth_fields_append(struct auth_fields *fields, string_t *dest, enum auth_field_flags flags_mask, enum auth_field_flags flags_result); bool auth_fields_is_empty(struct auth_fields *fields); /* If the field exists, clear its value (so the exported string will be "key" instead of e.g. "key=y"). */ void auth_fields_booleanize(struct auth_fields *fields, const char *key); /* Remember the current fields. */ void auth_fields_snapshot(struct auth_fields *fields); /* Rollback to previous snapshot, or clear the fields if there isn't any. */ void auth_fields_rollback(struct auth_fields *fields); #endif dovecot-2.2.33.2/src/auth/passdb.c0000644000175000017500000002337413165463624013520 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "password-scheme.h" #include "auth-worker-server.h" #include "passdb.h" static ARRAY(struct passdb_module_interface *) passdb_interfaces; static ARRAY(struct passdb_module *) passdb_modules; static const struct passdb_module_interface passdb_iface_deinit = { .name = "deinit" }; static struct passdb_module_interface *passdb_interface_find(const char *name) { struct passdb_module_interface *const *ifaces; array_foreach(&passdb_interfaces, ifaces) { struct passdb_module_interface *iface = *ifaces; if (strcmp(iface->name, name) == 0) return iface; } return NULL; } void passdb_register_module(struct passdb_module_interface *iface) { struct passdb_module_interface *old_iface; old_iface = passdb_interface_find(iface->name); if (old_iface != NULL && old_iface->verify_plain == NULL) { /* replacing a "support not compiled in" passdb */ passdb_unregister_module(old_iface); } else if (old_iface != NULL) { i_panic("passdb_register_module(%s): Already registered", iface->name); } array_append(&passdb_interfaces, &iface, 1); } void passdb_unregister_module(struct passdb_module_interface *iface) { struct passdb_module_interface *const *ifaces; unsigned int idx; array_foreach(&passdb_interfaces, ifaces) { if (*ifaces == iface) { idx = array_foreach_idx(&passdb_interfaces, ifaces); array_delete(&passdb_interfaces, idx, 1); return; } } i_panic("passdb_unregister_module(%s): Not registered", iface->name); } bool passdb_get_credentials(struct auth_request *auth_request, const char *input, const char *input_scheme, const unsigned char **credentials_r, size_t *size_r) { const char *wanted_scheme = auth_request->credentials_scheme; const char *plaintext, *username, *error; int ret; if (auth_request->prefer_plain_credentials && password_scheme_is_alias(input_scheme, "PLAIN")) { /* we've a plaintext scheme and we prefer to get it instead of converting it to the fallback scheme */ wanted_scheme = ""; } ret = password_decode(input, input_scheme, credentials_r, size_r, &error); if (ret <= 0) { if (ret < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Password data is not valid for scheme %s: %s", input_scheme, error); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Unknown scheme %s", input_scheme); } return FALSE; } if (*wanted_scheme == '\0') { /* anything goes. change the credentials_scheme to what we actually got, so blocking passdbs work. */ auth_request->credentials_scheme = p_strdup(auth_request->pool, t_strcut(input_scheme, '.')); return TRUE; } if (!password_scheme_is_alias(input_scheme, wanted_scheme)) { if (!password_scheme_is_alias(input_scheme, "PLAIN")) { const char *error = t_strdup_printf( "Requested %s scheme, but we have only %s", wanted_scheme, input_scheme); if (auth_request->set->debug_passwords) { error = t_strdup_printf("%s (input: %s)", error, input); } auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "%s", error); return FALSE; } /* we can generate anything out of plaintext passwords */ plaintext = t_strndup(*credentials_r, *size_r); username = auth_request->original_username; if (!auth_request->domain_is_realm && strchr(username, '@') != NULL) { /* domain must not be used as realm. add the @realm. */ username = t_strconcat(username, "@", auth_request->realm, NULL); } if (auth_request->set->debug_passwords) { auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "Generating %s from user '%s', password '%s'", wanted_scheme, username, plaintext); } if (!password_generate(plaintext, username, wanted_scheme, credentials_r, size_r)) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "Requested unknown scheme %s", wanted_scheme); return FALSE; } } return TRUE; } void passdb_handle_credentials(enum passdb_result result, const char *password, const char *scheme, lookup_credentials_callback_t *callback, struct auth_request *auth_request) { const unsigned char *credentials = NULL; size_t size = 0; if (result != PASSDB_RESULT_OK) { callback(result, NULL, 0, auth_request); return; } else if (auth_fields_exists(auth_request->extra_fields, "noauthenticate")) { callback(PASSDB_RESULT_NEXT, NULL, 0, auth_request); return; } if (password != NULL) { if (!passdb_get_credentials(auth_request, password, scheme, &credentials, &size)) result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; } else if (*auth_request->credentials_scheme == '\0') { /* We're doing a passdb lookup (not authenticating). Pass through a NULL password without an error. */ } else if (auth_request->delayed_credentials != NULL) { /* We already have valid credentials from an earlier passdb lookup. auth_request_lookup_credentials_finish() will use them. */ } else { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "Requested %s scheme, but we have a NULL password", auth_request->credentials_scheme); result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; } callback(result, credentials, size, auth_request); } static struct passdb_module * passdb_find(const char *driver, const char *args, unsigned int *idx_r) { struct passdb_module *const *passdbs; unsigned int i, count; passdbs = array_get(&passdb_modules, &count); for (i = 0; i < count; i++) { if (strcmp(passdbs[i]->iface.name, driver) == 0 && strcmp(passdbs[i]->args, args) == 0) { *idx_r = i; return passdbs[i]; } } return NULL; } struct passdb_module * passdb_preinit(pool_t pool, const struct auth_passdb_settings *set) { static unsigned int auth_passdb_id = 0; struct passdb_module_interface *iface; struct passdb_module *passdb; unsigned int idx; iface = passdb_interface_find(set->driver); if (iface == NULL || iface->verify_plain == NULL) { /* maybe it's a plugin. try to load it. */ auth_module_load(t_strconcat("authdb_", set->driver, NULL)); iface = passdb_interface_find(set->driver); } if (iface == NULL) i_fatal("Unknown passdb driver '%s'", set->driver); if (iface->verify_plain == NULL) { i_fatal("Support not compiled in for passdb driver '%s'", set->driver); } if (iface->preinit == NULL && iface->init == NULL && *set->args != '\0') { i_fatal("passdb %s: No args are supported: %s", set->driver, set->args); } passdb = passdb_find(set->driver, set->args, &idx); if (passdb != NULL) return passdb; if (iface->preinit == NULL) passdb = p_new(pool, struct passdb_module, 1); else passdb = iface->preinit(pool, set->args); passdb->id = ++auth_passdb_id; passdb->iface = *iface; passdb->args = p_strdup(pool, set->args); if (*set->mechanisms == '\0') { passdb->mechanisms = NULL; } else if (strcasecmp(set->mechanisms, "none") == 0) { passdb->mechanisms = (const char *const[]){NULL}; } else { passdb->mechanisms = (const char* const*)p_strsplit_spaces(pool, set->mechanisms, " ,"); } if (*set->username_filter == '\0') { passdb->username_filter = NULL; } else { passdb->username_filter = (const char* const*)p_strsplit_spaces(pool, set->username_filter, " ,"); } array_append(&passdb_modules, &passdb, 1); return passdb; } void passdb_init(struct passdb_module *passdb) { if (passdb->iface.init != NULL && passdb->init_refcount == 0) passdb->iface.init(passdb); passdb->init_refcount++; } void passdb_deinit(struct passdb_module *passdb) { unsigned int idx; i_assert(passdb->init_refcount > 0); if (--passdb->init_refcount > 0) return; if (passdb_find(passdb->iface.name, passdb->args, &idx) == NULL) i_unreached(); array_delete(&passdb_modules, idx, 1); if (passdb->iface.deinit != NULL) passdb->iface.deinit(passdb); /* make sure passdb isn't accessed again */ passdb->iface = passdb_iface_deinit; } void passdbs_generate_md5(unsigned char md5[STATIC_ARRAY MD5_RESULTLEN]) { struct md5_context ctx; struct passdb_module *const *passdbs; unsigned int i, count; md5_init(&ctx); passdbs = array_get(&passdb_modules, &count); for (i = 0; i < count; i++) { md5_update(&ctx, &passdbs[i]->id, sizeof(passdbs[i]->id)); md5_update(&ctx, passdbs[i]->iface.name, strlen(passdbs[i]->iface.name)); md5_update(&ctx, passdbs[i]->args, strlen(passdbs[i]->args)); } md5_final(&ctx, md5); } extern struct passdb_module_interface passdb_passwd; extern struct passdb_module_interface passdb_bsdauth; extern struct passdb_module_interface passdb_dict; extern struct passdb_module_interface passdb_shadow; extern struct passdb_module_interface passdb_passwd_file; extern struct passdb_module_interface passdb_pam; extern struct passdb_module_interface passdb_checkpassword; extern struct passdb_module_interface passdb_vpopmail; extern struct passdb_module_interface passdb_ldap; extern struct passdb_module_interface passdb_sql; extern struct passdb_module_interface passdb_sia; extern struct passdb_module_interface passdb_static; extern struct passdb_module_interface passdb_oauth2; void passdbs_init(void) { i_array_init(&passdb_interfaces, 16); i_array_init(&passdb_modules, 16); passdb_register_module(&passdb_passwd); passdb_register_module(&passdb_bsdauth); passdb_register_module(&passdb_dict); passdb_register_module(&passdb_passwd_file); passdb_register_module(&passdb_pam); passdb_register_module(&passdb_checkpassword); passdb_register_module(&passdb_shadow); passdb_register_module(&passdb_vpopmail); passdb_register_module(&passdb_ldap); passdb_register_module(&passdb_sql); passdb_register_module(&passdb_sia); passdb_register_module(&passdb_static); passdb_register_module(&passdb_oauth2); } void passdbs_deinit(void) { array_free(&passdb_modules); array_free(&passdb_interfaces); } dovecot-2.2.33.2/src/auth/db-passwd-file.h0000644000175000017500000000216613165463624015046 00000000000000#ifndef DB_PASSWD_FILE_H #define DB_PASSWD_FILE_H #include "hash.h" #define PASSWD_FILE_DEFAULT_USERNAME_FORMAT "%u" #define PASSWD_FILE_DEFAULT_SCHEME "CRYPT" struct passwd_user { uid_t uid; gid_t gid; char *home; char *password; char **extra_fields; }; struct passwd_file { struct db_passwd_file *db; pool_t pool; int refcount; time_t last_sync_time; char *path; time_t stamp; off_t size; int fd; HASH_TABLE(char *, struct passwd_user *) users; }; struct db_passwd_file { struct db_passwd_file *next; int refcount; char *path; HASH_TABLE(char *, struct passwd_file *) files; struct passwd_file *default_file; unsigned int vars:1; unsigned int userdb:1; unsigned int userdb_warn_missing:1; unsigned int debug:1; }; int db_passwd_file_lookup(struct db_passwd_file *db, struct auth_request *request, const char *username_format, struct passwd_user **user_r); struct db_passwd_file * db_passwd_file_init(const char *path, bool userdb, bool debug); void db_passwd_file_parse(struct db_passwd_file *db); void db_passwd_file_unref(struct db_passwd_file **db); #endif dovecot-2.2.33.2/src/auth/mech.c0000644000175000017500000001552013165463624013152 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "mech.h" #include "str.h" #include "passdb.h" #include static struct mech_module_list *mech_modules; void mech_register_module(const struct mech_module *module) { struct mech_module_list *list; list = i_new(struct mech_module_list, 1); list->module = *module; list->next = mech_modules; mech_modules = list; } void mech_unregister_module(const struct mech_module *module) { struct mech_module_list **pos, *list; for (pos = &mech_modules; *pos != NULL; pos = &(*pos)->next) { if (strcmp((*pos)->module.mech_name, module->mech_name) == 0) { list = *pos; *pos = (*pos)->next; i_free(list); break; } } } const struct mech_module *mech_module_find(const char *name) { struct mech_module_list *list; for (list = mech_modules; list != NULL; list = list->next) { if (strcasecmp(list->module.mech_name, name) == 0) return &list->module; } return NULL; } void mech_generic_auth_initial(struct auth_request *request, const unsigned char *data, size_t data_size) { if (data == NULL) { auth_request_handler_reply_continue(request, &uchar_nul, 0); } else { /* initial reply given, even if it was 0 bytes */ request->mech->auth_continue(request, data, data_size); } } void mech_generic_auth_free(struct auth_request *request) { pool_unref(&request->pool); } extern const struct mech_module mech_plain; extern const struct mech_module mech_login; extern const struct mech_module mech_apop; extern const struct mech_module mech_cram_md5; extern const struct mech_module mech_digest_md5; extern const struct mech_module mech_external; extern const struct mech_module mech_ntlm; extern const struct mech_module mech_otp; extern const struct mech_module mech_scram_sha1; extern const struct mech_module mech_skey; extern const struct mech_module mech_rpa; extern const struct mech_module mech_anonymous; #ifdef HAVE_GSSAPI extern const struct mech_module mech_gssapi; #endif #ifdef HAVE_GSSAPI_SPNEGO extern const struct mech_module mech_gssapi_spnego; #endif extern const struct mech_module mech_winbind_ntlm; extern const struct mech_module mech_winbind_spnego; extern const struct mech_module mech_oauthbearer; extern const struct mech_module mech_xoauth2; static void mech_register_add(struct mechanisms_register *reg, const struct mech_module *mech) { struct mech_module_list *list; list = p_new(reg->pool, struct mech_module_list, 1); list->module = *mech; str_printfa(reg->handshake, "MECH\t%s", mech->mech_name); if ((mech->flags & MECH_SEC_PRIVATE) != 0) str_append(reg->handshake, "\tprivate"); if ((mech->flags & MECH_SEC_ANONYMOUS) != 0) str_append(reg->handshake, "\tanonymous"); if ((mech->flags & MECH_SEC_PLAINTEXT) != 0) str_append(reg->handshake, "\tplaintext"); if ((mech->flags & MECH_SEC_DICTIONARY) != 0) str_append(reg->handshake, "\tdictionary"); if ((mech->flags & MECH_SEC_ACTIVE) != 0) str_append(reg->handshake, "\tactive"); if ((mech->flags & MECH_SEC_FORWARD_SECRECY) != 0) str_append(reg->handshake, "\tforward-secrecy"); if ((mech->flags & MECH_SEC_MUTUAL_AUTH) != 0) str_append(reg->handshake, "\tmutual-auth"); str_append_c(reg->handshake, '\n'); list->next = reg->modules; reg->modules = list; } static const char *mech_get_plugin_name(const char *name) { string_t *str = t_str_new(32); str_append(str, "mech_"); for (; *name != '\0'; name++) { if (*name == '-') str_append_c(str, '_'); else str_append_c(str, i_tolower(*name)); } return str_c(str); } struct mechanisms_register * mech_register_init(const struct auth_settings *set) { struct mechanisms_register *reg; const struct mech_module *mech; const char *const *mechanisms; pool_t pool; pool = pool_alloconly_create("mechanisms register", 1024); reg = p_new(pool, struct mechanisms_register, 1); reg->pool = pool; reg->set = set; reg->handshake = str_new(pool, 512); mechanisms = t_strsplit_spaces(set->mechanisms, " "); for (; *mechanisms != NULL; mechanisms++) { const char *name = *mechanisms; if (strcasecmp(name, "ANONYMOUS") == 0) { if (*set->anonymous_username == '\0') { i_fatal("ANONYMOUS listed in mechanisms, " "but anonymous_username not set"); } } mech = mech_module_find(name); if (mech == NULL) { /* maybe it's a plugin. try to load it. */ auth_module_load(mech_get_plugin_name(name)); mech = mech_module_find(name); } if (mech == NULL) i_fatal("Unknown authentication mechanism '%s'", name); mech_register_add(reg, mech); } if (reg->modules == NULL) i_fatal("No authentication mechanisms configured"); return reg; } void mech_register_deinit(struct mechanisms_register **_reg) { struct mechanisms_register *reg = *_reg; *_reg = NULL; pool_unref(®->pool); } const struct mech_module * mech_register_find(const struct mechanisms_register *reg, const char *name) { const struct mech_module_list *list; for (list = reg->modules; list != NULL; list = list->next) { if (strcasecmp(list->module.mech_name, name) == 0) return &list->module; } return NULL; } void mech_init(const struct auth_settings *set) { mech_register_module(&mech_plain); mech_register_module(&mech_login); mech_register_module(&mech_apop); mech_register_module(&mech_cram_md5); mech_register_module(&mech_digest_md5); mech_register_module(&mech_external); if (set->use_winbind) { mech_register_module(&mech_winbind_ntlm); mech_register_module(&mech_winbind_spnego); } else { mech_register_module(&mech_ntlm); #if defined(HAVE_GSSAPI_SPNEGO) && defined(BUILTIN_GSSAPI) mech_register_module(&mech_gssapi_spnego); #endif } mech_register_module(&mech_otp); mech_register_module(&mech_scram_sha1); mech_register_module(&mech_skey); mech_register_module(&mech_rpa); mech_register_module(&mech_anonymous); #ifdef BUILTIN_GSSAPI mech_register_module(&mech_gssapi); #endif mech_register_module(&mech_oauthbearer); mech_register_module(&mech_xoauth2); } void mech_deinit(const struct auth_settings *set) { mech_unregister_module(&mech_plain); mech_unregister_module(&mech_login); mech_unregister_module(&mech_apop); mech_unregister_module(&mech_cram_md5); mech_unregister_module(&mech_digest_md5); mech_unregister_module(&mech_external); if (set->use_winbind) { mech_unregister_module(&mech_winbind_ntlm); mech_unregister_module(&mech_winbind_spnego); } else { mech_unregister_module(&mech_ntlm); #if defined(HAVE_GSSAPI_SPNEGO) && defined(BUILTIN_GSSAPI) mech_unregister_module(&mech_gssapi_spnego); #endif } mech_unregister_module(&mech_otp); mech_unregister_module(&mech_scram_sha1); mech_unregister_module(&mech_skey); mech_unregister_module(&mech_rpa); mech_unregister_module(&mech_anonymous); #ifdef BUILTIN_GSSAPI mech_unregister_module(&mech_gssapi); #endif mech_unregister_module(&mech_oauthbearer); mech_unregister_module(&mech_xoauth2); } dovecot-2.2.33.2/src/auth/mech-dovecot-token.c0000644000175000017500000000450013123174404015712 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ /* Used internally by Dovecot processes to authenticate against each others (e.g. imap to imap-urlauth). See auth-token.c */ #include "auth-common.h" #include "mech.h" #include "safe-memset.h" #include "auth-token.h" static void mech_dovecot_token_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { const char *session_id, *username, *pid, *service, *error; char *auth_token; size_t i, len; int count; /* service \0 pid \0 username \0 session_id \0 auth_token */ service = (const char *) data; session_id = username = pid = auth_token = NULL; count = 0; for (i = 0; i < data_size; i++) { if (data[i] == '\0') { count++; i++; if (count == 1) pid = (const char *)data + i; else if (count == 2) username = (const char *)data + i; else if (count == 3) session_id = (const char *)data + i; else { len = data_size - i; auth_token = p_strndup(unsafe_data_stack_pool, data+i, len); break; } } } if (count != 4) { /* invalid input */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "invalid input"); auth_request_fail(request); } else if (!auth_request_set_username(request, username, &error)) { /* invalid username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); } else { const char *valid_token = auth_token_get(service, pid, request->user, session_id); if (auth_token != NULL && strcmp(auth_token, valid_token) == 0) { request->passdb_success = TRUE; auth_request_success(request, NULL, 0); } else { auth_request_fail(request); } } /* make sure it's cleared */ if (auth_token != NULL) safe_memset(auth_token, 0, strlen(auth_token)); } static struct auth_request *mech_dovecot_token_auth_new(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"dovecot_token_auth_request", 512); request = p_new(pool, struct auth_request, 1); request->pool = pool; return request; } const struct mech_module mech_dovecot_token = { "DOVECOT-TOKEN", .flags = MECH_SEC_PRIVATE, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_dovecot_token_auth_new, mech_generic_auth_initial, mech_dovecot_token_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/auth-request-handler.c0000644000175000017500000006225413165463624016306 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "array.h" #include "aqueue.h" #include "base64.h" #include "hash.h" #include "net.h" #include "str.h" #include "strescape.h" #include "str-sanitize.h" #include "master-interface.h" #include "auth-penalty.h" #include "auth-request.h" #include "auth-token.h" #include "auth-master-connection.h" #include "auth-request-handler.h" #include "auth-policy.h" #define AUTH_FAILURE_DELAY_CHECK_MSECS 500 struct auth_request_handler { int refcount; pool_t pool; HASH_TABLE(void *, struct auth_request *) requests; unsigned int connect_uid, client_pid; auth_client_request_callback_t *callback; struct auth_client_connection *conn; auth_master_request_callback_t *master_callback; unsigned int destroyed:1; unsigned int token_auth:1; }; static ARRAY(struct auth_request *) auth_failures_arr; static struct aqueue *auth_failures; static struct timeout *to_auth_failures; static void auth_failure_timeout(void *context) ATTR_NULL(1); struct auth_request_handler * auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback, struct auth_client_connection *conn, auth_master_request_callback_t *master_callback) { struct auth_request_handler *handler; pool_t pool; pool = pool_alloconly_create("auth request handler", 4096); handler = p_new(pool, struct auth_request_handler, 1); handler->refcount = 1; handler->pool = pool; hash_table_create_direct(&handler->requests, pool, 0); handler->callback = callback; handler->conn = conn; handler->master_callback = master_callback; handler->token_auth = token_auth; return handler; } unsigned int auth_request_handler_get_request_count(struct auth_request_handler *handler) { return hash_table_count(handler->requests); } void auth_request_handler_abort_requests(struct auth_request_handler *handler) { struct hash_iterate_context *iter; void *key; struct auth_request *auth_request; iter = hash_table_iterate_init(handler->requests); while (hash_table_iterate(iter, handler->requests, &key, &auth_request)) { switch (auth_request->state) { case AUTH_REQUEST_STATE_NEW: case AUTH_REQUEST_STATE_MECH_CONTINUE: case AUTH_REQUEST_STATE_FINISHED: auth_request->removed_from_handler = TRUE; auth_request_unref(&auth_request); hash_table_remove(handler->requests, key); break; case AUTH_REQUEST_STATE_PASSDB: case AUTH_REQUEST_STATE_USERDB: /* can't abort a pending passdb/userdb lookup */ break; case AUTH_REQUEST_STATE_MAX: i_unreached(); } } hash_table_iterate_deinit(&iter); } void auth_request_handler_unref(struct auth_request_handler **_handler) { struct auth_request_handler *handler = *_handler; *_handler = NULL; i_assert(handler->refcount > 0); if (--handler->refcount > 0) return; i_assert(hash_table_count(handler->requests) == 0); /* notify parent that we're done with all requests */ handler->callback(NULL, handler->conn); hash_table_destroy(&handler->requests); pool_unref(&handler->pool); } void auth_request_handler_destroy(struct auth_request_handler **_handler) { struct auth_request_handler *handler = *_handler; *_handler = NULL; i_assert(!handler->destroyed); handler->destroyed = TRUE; auth_request_handler_unref(&handler); } void auth_request_handler_set(struct auth_request_handler *handler, unsigned int connect_uid, unsigned int client_pid) { handler->connect_uid = connect_uid; handler->client_pid = client_pid; } static void auth_request_handler_remove(struct auth_request_handler *handler, struct auth_request *request) { i_assert(request->handler == handler); if (request->removed_from_handler) { /* already removed it */ return; } request->removed_from_handler = TRUE; /* if db lookup is stuck, this call doesn't actually free the auth request, so make sure we don't get back here. */ timeout_remove(&request->to_abort); hash_table_remove(handler->requests, POINTER_CAST(request->id)); auth_request_unref(&request); } static void auth_str_add_keyvalue(string_t *dest, const char *key, const char *value) { str_append_c(dest, '\t'); str_append(dest, key); str_append_c(dest, '='); str_append_tabescaped(dest, value); } static void auth_str_append_extra_fields(struct auth_request *request, string_t *dest) { if (!auth_fields_is_empty(request->extra_fields)) { str_append_c(dest, '\t'); auth_fields_append(request->extra_fields, dest, AUTH_FIELD_FLAG_HIDDEN, 0); } if (request->original_username != NULL && null_strcmp(request->original_username, request->user) != 0 && !auth_fields_exists(request->extra_fields, "original_user")) { auth_str_add_keyvalue(dest, "original_user", request->original_username); } if (request->master_user != NULL && !auth_fields_exists(request->extra_fields, "auth_user")) auth_str_add_keyvalue(dest, "auth_user", request->master_user); if (!request->auth_only && auth_fields_exists(request->extra_fields, "proxy")) { /* we're proxying */ if (!auth_fields_exists(request->extra_fields, "pass") && request->mech_password != NULL) { /* send back the password that was sent by user (not the password in passdb). */ auth_str_add_keyvalue(dest, "pass", request->mech_password); } if (request->master_user != NULL && !auth_fields_exists(request->extra_fields, "master")) { /* the master username needs to be forwarded */ auth_str_add_keyvalue(dest, "master", request->master_user); } } } static void auth_request_handle_failure(struct auth_request *request, const char *reply) { struct auth_request_handler *handler = request->handler; if (request->in_delayed_failure_queue) { /* we came here from flush_failures() */ handler->callback(reply, handler->conn); return; } /* remove the request from requests-list */ auth_request_ref(request); auth_request_handler_remove(handler, request); auth_policy_report(request); if (auth_fields_exists(request->extra_fields, "nodelay")) { /* passdb specifically requested not to delay the reply. */ handler->callback(reply, handler->conn); auth_request_unref(&request); return; } /* failure. don't announce it immediately to avoid a) timing attacks, b) flooding */ request->in_delayed_failure_queue = TRUE; handler->refcount++; if (auth_penalty != NULL) { auth_penalty_update(auth_penalty, request, request->last_penalty + 1); } auth_request_refresh_last_access(request); aqueue_append(auth_failures, &request); if (to_auth_failures == NULL) { to_auth_failures = timeout_add_short(AUTH_FAILURE_DELAY_CHECK_MSECS, auth_failure_timeout, (void *)NULL); } } static void auth_request_handler_reply_success_finish(struct auth_request *request) { struct auth_request_handler *handler = request->handler; string_t *str = t_str_new(128); if (request->last_penalty != 0 && auth_penalty != NULL) { /* reset penalty */ auth_penalty_update(auth_penalty, request, 0); } /* sanitize these fields, since the login code currently assumes they are exactly in this format. */ auth_fields_booleanize(request->extra_fields, "nologin"); auth_fields_booleanize(request->extra_fields, "proxy"); str_printfa(str, "OK\t%u\tuser=", request->id); str_append_tabescaped(str, request->user); auth_str_append_extra_fields(request, str); auth_policy_report(request); if (handler->master_callback == NULL || auth_fields_exists(request->extra_fields, "nologin") || auth_fields_exists(request->extra_fields, "proxy")) { /* this request doesn't have to wait for master process to pick it up. delete it */ auth_request_handler_remove(handler, request); } handler->callback(str_c(str), handler->conn); } static void auth_request_handler_reply_failure_finish(struct auth_request *request) { string_t *str = t_str_new(128); auth_fields_remove(request->extra_fields, "nologin"); str_printfa(str, "FAIL\t%u", request->id); if (request->user != NULL) auth_str_add_keyvalue(str, "user", request->user); else if (request->original_username != NULL) { auth_str_add_keyvalue(str, "user", request->original_username); } if (request->internal_failure) str_append(str, "\ttemp"); else if (request->master_user != NULL) { /* authentication succeeded, but we can't log in as the wanted user */ str_append(str, "\tauthz"); } if (auth_fields_exists(request->extra_fields, "nodelay")) { /* this is normally a hidden field, need to add it explicitly */ str_append(str, "\tnodelay"); } auth_str_append_extra_fields(request, str); switch (request->passdb_result) { case PASSDB_RESULT_NEXT: case PASSDB_RESULT_INTERNAL_FAILURE: case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_PASSWORD_MISMATCH: case PASSDB_RESULT_OK: break; case PASSDB_RESULT_USER_DISABLED: str_append(str, "\tuser_disabled"); break; case PASSDB_RESULT_PASS_EXPIRED: str_append(str, "\tpass_expired"); break; } auth_request_handle_failure(request, str_c(str)); } static void auth_request_handler_proxy_callback(bool success, struct auth_request *request) { struct auth_request_handler *handler = request->handler; if (success) auth_request_handler_reply_success_finish(request); else auth_request_handler_reply_failure_finish(request); auth_request_handler_unref(&handler); } void auth_request_handler_reply(struct auth_request *request, enum auth_client_result result, const void *auth_reply, size_t reply_size) { struct auth_request_handler *handler = request->handler; string_t *str; int ret; if (handler->destroyed) { /* the client connection was already closed. we can't do anything but abort this request */ request->internal_failure = TRUE; result = AUTH_CLIENT_RESULT_FAILURE; /* make sure this request is set to finished state (it's not with result=continue) */ auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); } switch (result) { case AUTH_CLIENT_RESULT_CONTINUE: str = t_str_new(16 + MAX_BASE64_ENCODED_SIZE(reply_size)); str_printfa(str, "CONT\t%u\t", request->id); base64_encode(auth_reply, reply_size, str); request->accept_cont_input = TRUE; handler->callback(str_c(str), handler->conn); break; case AUTH_CLIENT_RESULT_SUCCESS: if (reply_size > 0) { str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size)); base64_encode(auth_reply, reply_size, str); auth_fields_add(request->extra_fields, "resp", str_c(str), 0); } ret = auth_request_proxy_finish(request, auth_request_handler_proxy_callback); if (ret < 0) auth_request_handler_reply_failure_finish(request); else if (ret > 0) auth_request_handler_reply_success_finish(request); else return; break; case AUTH_CLIENT_RESULT_FAILURE: auth_request_proxy_finish_failure(request); auth_request_handler_reply_failure_finish(request); break; } /* NOTE: request may be destroyed now */ auth_request_handler_unref(&handler); } void auth_request_handler_reply_continue(struct auth_request *request, const void *reply, size_t reply_size) { auth_request_handler_reply(request, AUTH_CLIENT_RESULT_CONTINUE, reply, reply_size); } static void auth_request_handler_auth_fail(struct auth_request_handler *handler, struct auth_request *request, const char *reason) { string_t *str = t_str_new(128); auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", reason); str_printfa(str, "FAIL\t%u\treason=", request->id); str_append_tabescaped(str, reason); handler->callback(str_c(str), handler->conn); auth_request_handler_remove(handler, request); } static void auth_request_timeout(struct auth_request *request) { unsigned int secs = (unsigned int)(time(NULL) - request->last_access); if (request->state != AUTH_REQUEST_STATE_MECH_CONTINUE) { /* client's fault */ auth_request_log_error(request, AUTH_SUBSYS_MECH, "Request %u.%u timed out after %u secs, state=%d", request->handler->client_pid, request->id, secs, request->state); } else if (request->set->verbose) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Request timed out waiting for client to continue authentication " "(%u secs)", secs); } auth_request_handler_remove(request->handler, request); } static void auth_request_penalty_finish(struct auth_request *request) { timeout_remove(&request->to_penalty); auth_request_initial(request); } static void auth_penalty_callback(unsigned int penalty, struct auth_request *request) { unsigned int secs; request->last_penalty = penalty; if (penalty == 0) auth_request_initial(request); else { secs = auth_penalty_to_secs(penalty); request->to_penalty = timeout_add(secs * 1000, auth_request_penalty_finish, request); } } bool auth_request_handler_auth_begin(struct auth_request_handler *handler, const char *args) { const struct mech_module *mech; struct auth_request *request; const char *const *list, *name, *arg, *initial_resp; void *initial_resp_data; unsigned int id; buffer_t *buf; i_assert(!handler->destroyed); /* [...] */ list = t_strsplit_tabescaped(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("BUG: Authentication client %u " "sent broken AUTH request", handler->client_pid); return FALSE; } if (handler->token_auth) { mech = &mech_dovecot_token; if (strcmp(list[1], mech->mech_name) != 0) { /* unsupported mechanism */ i_error("BUG: Authentication client %u requested invalid " "authentication mechanism %s (DOVECOT-TOKEN required)", handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN)); return FALSE; } } else { struct auth *auth_default = auth_default_service(); mech = mech_register_find(auth_default->reg, list[1]); if (mech == NULL) { /* unsupported mechanism */ i_error("BUG: Authentication client %u requested unsupported " "authentication mechanism %s", handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN)); return FALSE; } } request = auth_request_new(mech); request->handler = handler; request->connect_uid = handler->connect_uid; request->client_pid = handler->client_pid; request->id = id; request->auth_only = handler->master_callback == NULL; /* parse optional parameters */ initial_resp = NULL; for (list += 2; *list != NULL; list++) { arg = strchr(*list, '='); if (arg == NULL) { name = *list; arg = ""; } else { name = t_strdup_until(*list, arg); arg++; } if (auth_request_import_auth(request, name, arg)) ; else if (strcmp(name, "resp") == 0) { initial_resp = arg; /* this must be the last parameter */ list++; break; } } if (*list != NULL) { i_error("BUG: Authentication client %u " "sent AUTH parameters after 'resp'", handler->client_pid); auth_request_unref(&request); return FALSE; } if (request->service == NULL) { i_error("BUG: Authentication client %u " "didn't specify service in request", handler->client_pid); auth_request_unref(&request); return FALSE; } if (hash_table_lookup(handler->requests, POINTER_CAST(id)) != NULL) { i_error("BUG: Authentication client %u " "sent a duplicate ID %u", handler->client_pid, id); auth_request_unref(&request); return FALSE; } auth_request_init(request); request->to_abort = timeout_add(MASTER_AUTH_SERVER_TIMEOUT_SECS * 1000, auth_request_timeout, request); hash_table_insert(handler->requests, POINTER_CAST(id), request); if (request->set->ssl_require_client_cert && !request->valid_client_cert) { /* we fail without valid certificate */ auth_request_handler_auth_fail(handler, request, "Client didn't present valid SSL certificate"); return TRUE; } /* Empty initial response is a "=" base64 string. Completely empty string shouldn't really be sent, but at least Exim does it, so just allow it for backwards compatibility.. */ if (initial_resp != NULL && *initial_resp != '\0') { size_t len = strlen(initial_resp); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(len)); if (base64_decode(initial_resp, len, NULL, buf) < 0) { auth_request_handler_auth_fail(handler, request, "Invalid base64 data in initial response"); return TRUE; } initial_resp_data = p_malloc(request->pool, I_MAX(buf->used, 1)); memcpy(initial_resp_data, buf->data, buf->used); request->initial_response = initial_resp_data; request->initial_response_len = buf->used; } /* handler is referenced until auth_request_handler_reply() is called. */ handler->refcount++; /* before we start authenticating, see if we need to wait first */ auth_penalty_lookup(auth_penalty, request, auth_penalty_callback); return TRUE; } bool auth_request_handler_auth_continue(struct auth_request_handler *handler, const char *args) { struct auth_request *request; const char *data; size_t data_len; buffer_t *buf; unsigned int id; data = strchr(args, '\t'); if (data == NULL || str_to_uint(t_strdup_until(args, data), &id) < 0) { i_error("BUG: Authentication client sent broken CONT request"); return FALSE; } data++; request = hash_table_lookup(handler->requests, POINTER_CAST(id)); if (request == NULL) { const char *reply = t_strdup_printf( "FAIL\t%u\treason=Authentication request timed out", id); handler->callback(reply, handler->conn); return TRUE; } /* accept input only once after mechanism has sent a CONT reply */ if (!request->accept_cont_input) { auth_request_handler_auth_fail(handler, request, "Unexpected continuation"); return TRUE; } request->accept_cont_input = FALSE; data_len = strlen(data); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(data_len)); if (base64_decode(data, data_len, NULL, buf) < 0) { auth_request_handler_auth_fail(handler, request, "Invalid base64 data in continued response"); return TRUE; } /* handler is referenced until auth_request_handler_reply() is called. */ handler->refcount++; auth_request_continue(request, buf->data, buf->used); return TRUE; } static void auth_str_append_userdb_extra_fields(struct auth_request *request, string_t *dest) { str_append_c(dest, '\t'); auth_fields_append(request->userdb_reply, dest, AUTH_FIELD_FLAG_HIDDEN, 0); if (request->master_user != NULL && !auth_fields_exists(request->userdb_reply, "master_user")) { auth_str_add_keyvalue(dest, "master_user", request->master_user); } if (*request->set->anonymous_username != '\0' && strcmp(request->user, request->set->anonymous_username) == 0) { /* this is an anonymous login, either via ANONYMOUS SASL mechanism or simply logging in as the anonymous user via another mechanism */ str_append(dest, "\tanonymous"); } /* generate auth_token when master service provided session_pid */ if (request->request_auth_token && request->session_pid != (pid_t)-1) { const char *auth_token = auth_token_get(request->service, dec2str(request->session_pid), request->user, request->session_id); auth_str_add_keyvalue(dest, "auth_token", auth_token); } if (request->master_user != NULL) { auth_str_add_keyvalue(dest, "auth_user", request->master_user); } else if (request->original_username != NULL && strcmp(request->original_username, request->user) != 0) { auth_str_add_keyvalue(dest, "auth_user", request->original_username); } } static void userdb_callback(enum userdb_result result, struct auth_request *request) { struct auth_request_handler *handler = request->handler; string_t *str; const char *value; i_assert(request->state == AUTH_REQUEST_STATE_USERDB); auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); if (request->userdb_lookup_tempfailed) result = USERDB_RESULT_INTERNAL_FAILURE; str = t_str_new(128); switch (result) { case USERDB_RESULT_INTERNAL_FAILURE: str_printfa(str, "FAIL\t%u", request->id); if (request->userdb_lookup_tempfailed) { value = auth_fields_find(request->userdb_reply, "reason"); if (value != NULL) auth_str_add_keyvalue(str, "reason", value); } break; case USERDB_RESULT_USER_UNKNOWN: str_printfa(str, "NOTFOUND\t%u", request->id); break; case USERDB_RESULT_OK: str_printfa(str, "USER\t%u\t", request->id); str_append_tabescaped(str, request->user); auth_str_append_userdb_extra_fields(request, str); break; } handler->master_callback(str_c(str), request->master); auth_master_connection_unref(&request->master); auth_request_unref(&request); auth_request_handler_unref(&handler); } static bool auth_master_request_failed(struct auth_request_handler *handler, struct auth_master_connection *master, unsigned int id) { if (handler->master_callback == NULL) return FALSE; handler->master_callback(t_strdup_printf("FAIL\t%u", id), master); return TRUE; } bool auth_request_handler_master_request(struct auth_request_handler *handler, struct auth_master_connection *master, unsigned int id, unsigned int client_id, const char *const *params) { struct auth_request *request; struct net_unix_cred cred; request = hash_table_lookup(handler->requests, POINTER_CAST(client_id)); if (request == NULL) { i_error("Master request %u.%u not found", handler->client_pid, client_id); return auth_master_request_failed(handler, master, id); } auth_request_ref(request); auth_request_handler_remove(handler, request); for (; *params != NULL; params++) { const char *name, *param = strchr(*params, '='); if (param == NULL) { name = *params; param = ""; } else { name = t_strdup_until(*params, param); param++; } (void)auth_request_import_master(request, name, param); } /* verify session pid if specified and possible */ if (request->session_pid != (pid_t)-1 && net_getunixcred(master->fd, &cred) == 0 && cred.pid != (pid_t)-1 && request->session_pid != cred.pid) { i_error("Session pid %ld provided by master for request %u.%u " "did not match peer credentials (pid=%ld, uid=%ld)", (long)request->session_pid, handler->client_pid, client_id, (long)cred.pid, (long)cred.uid); return auth_master_request_failed(handler, master, id); } if (request->state != AUTH_REQUEST_STATE_FINISHED || !request->successful) { i_error("Master requested unfinished authentication request " "%u.%u", handler->client_pid, client_id); handler->master_callback(t_strdup_printf("FAIL\t%u", id), master); auth_request_unref(&request); } else { /* the request isn't being referenced anywhere anymore, so we can do a bit of kludging.. replace the request's old client_id with master's id. */ auth_request_set_state(request, AUTH_REQUEST_STATE_USERDB); request->id = id; request->master = master; /* master and handler are referenced until userdb_callback i s called. */ auth_master_connection_ref(master); handler->refcount++; auth_request_lookup_user(request, userdb_callback); } return TRUE; } void auth_request_handler_cancel_request(struct auth_request_handler *handler, unsigned int client_id) { struct auth_request *request; request = hash_table_lookup(handler->requests, POINTER_CAST(client_id)); if (request != NULL) auth_request_handler_remove(handler, request); } void auth_request_handler_flush_failures(bool flush_all) { struct auth_request **auth_requests, *auth_request; unsigned int i, j, count; time_t diff; count = aqueue_count(auth_failures); if (count == 0) { if (to_auth_failures != NULL) timeout_remove(&to_auth_failures); return; } auth_requests = array_idx_modifiable(&auth_failures_arr, 0); /* count the number of requests that we need to flush */ for (i = 0; i < count; i++) { auth_request = auth_requests[aqueue_idx(auth_failures, i)]; /* FIXME: assumess that failure_delay is always the same. */ diff = ioloop_time - auth_request->last_access; if (diff < (time_t)auth_request->set->failure_delay && !flush_all) break; } /* shuffle these requests to try to prevent any kind of timing attacks where attacker performs multiple requests in parallel and attempts to figure out results based on the order of replies. */ count = i; for (i = 0; i < count; i++) { j = random() % (count - i) + i; auth_request = auth_requests[aqueue_idx(auth_failures, i)]; /* swap i & j */ auth_requests[aqueue_idx(auth_failures, i)] = auth_requests[aqueue_idx(auth_failures, j)]; auth_requests[aqueue_idx(auth_failures, j)] = auth_request; } /* flush the requests */ for (i = 0; i < count; i++) { auth_request = auth_requests[aqueue_idx(auth_failures, 0)]; aqueue_delete_tail(auth_failures); i_assert(auth_request != NULL); i_assert(auth_request->state == AUTH_REQUEST_STATE_FINISHED); auth_request_handler_reply(auth_request, AUTH_CLIENT_RESULT_FAILURE, &uchar_nul, 0); auth_request_unref(&auth_request); } } static void auth_failure_timeout(void *context ATTR_UNUSED) { auth_request_handler_flush_failures(FALSE); } void auth_request_handler_init(void) { i_array_init(&auth_failures_arr, 128); auth_failures = aqueue_init(&auth_failures_arr.arr); } void auth_request_handler_deinit(void) { auth_request_handler_flush_failures(TRUE); array_free(&auth_failures_arr); aqueue_deinit(&auth_failures); if (to_auth_failures != NULL) timeout_remove(&to_auth_failures); } dovecot-2.2.33.2/src/auth/test-username-filter.c0000644000175000017500000000422213165463624016312 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include "auth-common.h" #include "auth-request.h" struct auth_penalty *auth_penalty; time_t process_start_time; bool worker, worker_restart_request; void auth_module_load(const char *names ATTR_UNUSED) { } void auth_refresh_proctitle(void) { } static void test_username_filter(void) { const struct { const char *filter; const char *input; bool accepted; } cases[] = { { "", "", TRUE }, { "*", "", TRUE }, { "", "testuser1", TRUE }, { "*", "testuser1", TRUE }, { "!*", "testuser1", FALSE }, { "!*", "", FALSE }, { "*@*", "", FALSE }, { "*@*", "@", TRUE }, { "!*@*", "@", FALSE }, { "!*@*", "", TRUE }, { "*@*", "testuser1", FALSE }, { "!*@*", "testuser1", TRUE }, { "*@*", "testuser1@testdomain", TRUE }, { "!*@*", "testuser1@testdomain", FALSE }, { "*@testdomain *@testdomain2", "testuser1@testdomain", TRUE }, { "*@testdomain *@testdomain2", "testuser1@testdomain2", TRUE }, { "*@testdomain *@testdomain2", "testuser1@testdomain3", FALSE }, { "!testuser@testdomain *@testdomain", "testuser@testdomain", FALSE }, { "!testuser@testdomain *@testdomain", "testuser2@testdomain", TRUE }, { "*@testdomain !testuser@testdomain !testuser2@testdomain", "testuser@testdomain", FALSE }, { "*@testdomain !testuser@testdomain !testuser2@testdomain", "testuser3@testdomain", TRUE }, { "!testuser@testdomain !testuser2@testdomain", "testuser", TRUE }, { "!testuser@testdomain !testuser2@testdomain", "testuser@testdomain", FALSE }, { "!testuser@testdomain *@testdomain !testuser2@testdomain", "testuser3@testdomain", TRUE }, { "!testuser@testdomain *@testdomain !testuser2@testdomain", "testuser@testdomain", FALSE }, }; test_begin("test username_filter"); for(size_t i = 0; i < N_ELEMENTS(cases); i++) { const char *const *filter = t_strsplit_spaces(cases[i].filter, " ,"); test_assert_idx(auth_request_username_accepted(filter, cases[i].input) == cases[i].accepted, i); } test_end(); } int main(void) { static void (*test_functions[])(void) = { test_username_filter, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/auth/db-oauth2.c0000644000175000017500000004126313165463624014026 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "env-util.h" #include "var-expand.h" #include "settings.h" #include "oauth2.h" #include "http-client.h" #include "iostream-ssl.h" #include "auth-request.h" #include "passdb.h" #include "passdb-template.h" #include "llist.h" #include "db-oauth2.h" #include struct passdb_oauth2_settings { /* tokeninfo endpoint, format https://endpoint/somewhere?token= */ const char *tokeninfo_url; /* introspection endpoint, format https://endpoint/somewhere */ const char *introspection_url; /* expected scope, optional */ const char *scope; /* mode of introspection, one of get, get-auth, post - get: append token to url - get-auth: send token with header Authorization: Bearer token - post: send token= as POST request */ const char *introspection_mode; /* normalization var-expand template for username, defaults to %Lu */ const char *username_format; /* name of username attribute to lookup, mandatory */ const char *username_attribute; /* name of account is active attribute, optional */ const char *active_attribute; /* expected active value for active attribute, optional */ const char *active_value; /* template to expand into passdb */ const char *pass_attrs; /* TLS options */ const char *tls_ca_cert_file; const char *tls_ca_cert_dir; const char *tls_cert_file; const char *tls_key_file; const char *tls_cipher_suite; /* HTTP rawlog directory */ const char *rawlog_dir; /* HTTP client options */ unsigned int timeout_msecs; unsigned int max_idle_time_msecs; unsigned int max_parallel_connections; unsigned int max_pipelined_requests; bool tls_allow_invalid_cert; bool debug; /* Should introspection be done even if not necessary */ bool force_introspection; /* Should we send service and local/remote endpoints as X-Dovecot-Auth headers */ bool send_auth_headers; }; struct db_oauth2 { struct db_oauth2 *prev,*next; pool_t pool; const char *config_path; struct passdb_oauth2_settings set; struct http_client *client; struct passdb_template *tmpl; struct oauth2_settings oauth2_set; struct db_oauth2_request *head; unsigned int refcount; }; static struct db_oauth2 *db_oauth2_head = NULL; #undef DEF_STR #undef DEF_BOOL #undef DEF_INT #define DEF_STR(name) DEF_STRUCT_STR(name, passdb_oauth2_settings) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, passdb_oauth2_settings) #define DEF_INT(name) DEF_STRUCT_INT(name, passdb_oauth2_settings) static struct setting_def setting_defs[] = { DEF_STR(tokeninfo_url), DEF_STR(introspection_url), DEF_STR(scope), DEF_BOOL(force_introspection), DEF_STR(introspection_mode), DEF_STR(username_format), DEF_STR(username_attribute), DEF_STR(pass_attrs), DEF_STR(active_attribute), DEF_STR(active_value), DEF_INT(timeout_msecs), DEF_INT(max_idle_time_msecs), DEF_INT(max_parallel_connections), DEF_INT(max_pipelined_requests), DEF_BOOL(send_auth_headers), DEF_STR(tls_ca_cert_file), DEF_STR(tls_ca_cert_dir), DEF_STR(tls_cert_file), DEF_STR(tls_key_file), DEF_STR(tls_cipher_suite), DEF_BOOL(tls_allow_invalid_cert), DEF_STR(rawlog_dir), DEF_BOOL(debug), { 0, NULL, 0 } }; static struct passdb_oauth2_settings default_oauth2_settings = { .tokeninfo_url = "", .introspection_url = "", .scope = "", .force_introspection = FALSE, .introspection_mode = "", .username_format = "%Lu", .username_attribute = "email", .active_attribute = "", .active_value = "", .pass_attrs = "", .rawlog_dir = "", .timeout_msecs = 0, .max_idle_time_msecs = 60000, .max_parallel_connections = 1, .max_pipelined_requests = 1, .tls_ca_cert_file = NULL, .tls_ca_cert_dir = NULL, .tls_cert_file = NULL, .tls_key_file = NULL, .tls_cipher_suite = "HIGH:!SSLv2", .tls_allow_invalid_cert = FALSE, .send_auth_headers = FALSE, .debug = FALSE, }; static const char *parse_setting(const char *key, const char *value, struct db_oauth2 *db) { return parse_setting_from_defs(db->pool, setting_defs, &db->set, key, value); } struct db_oauth2 *db_oauth2_init(const char *config_path) { struct db_oauth2 *db; const char *error; struct http_client_settings http_set; for(db = db_oauth2_head; db != NULL; db = db->next) { if (strcmp(db->config_path, config_path) == 0) { db->refcount++; return db; } } pool_t pool = pool_alloconly_create("db_oauth2", 128); db = p_new(pool, struct db_oauth2, 1); db->pool = pool; db->refcount = 1; db->config_path = p_strdup(pool, config_path); db->set = default_oauth2_settings; if (!settings_read_nosection(config_path, parse_setting, db, &error)) i_fatal("oauth2 %s: %s", config_path, error); db->tmpl = passdb_template_build(pool, db->set.pass_attrs); i_zero(&http_set); http_set.ssl_ca_file = db->set.tls_ca_cert_file; http_set.ssl_ca_dir = db->set.tls_ca_cert_dir; if (db->set.tls_cert_file != NULL && *db->set.tls_cert_file != '\0') { http_set.ssl_cert = db->set.tls_cert_file; http_set.ssl_key = db->set.tls_key_file; } http_set.ssl_allow_invalid_cert = db->set.tls_allow_invalid_cert; http_set.dns_client_socket_path = "dns-client"; http_set.user_agent = "dovecot-oauth2-passdb/" DOVECOT_VERSION; if (*db->set.tokeninfo_url == '\0' && *db->set.introspection_url == '\0') i_fatal("oauth2: Tokeninfo or introspection URL must be given"); if (*db->set.rawlog_dir != '\0') http_set.rawlog_dir = db->set.rawlog_dir; http_set.max_idle_time_msecs = db->set.max_idle_time_msecs; http_set.max_parallel_connections = db->set.max_parallel_connections; http_set.max_pipelined_requests = db->set.max_pipelined_requests; http_set.no_auto_redirect = FALSE; http_set.no_auto_retry = TRUE; http_set.debug = db->set.debug; db->client = http_client_init(&http_set); i_zero(&db->oauth2_set); db->oauth2_set.client = db->client; db->oauth2_set.tokeninfo_url = db->set.tokeninfo_url, db->oauth2_set.introspection_url = db->set.introspection_url; db->oauth2_set.timeout_msecs = db->set.timeout_msecs; db->oauth2_set.send_auth_headers = db->set.send_auth_headers; if (*db->set.introspection_mode == '\0' || strcmp(db->set.introspection_mode, "auth") == 0) { db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET_AUTH; } else if (strcmp(db->set.introspection_mode, "get") == 0) { db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET; } else if (strcmp(db->set.introspection_mode, "post") == 0) { db->oauth2_set.introspection_mode = INTROSPECTION_MODE_POST; } else { i_fatal("Invalid value '%s' for introspection mode, must be on auth, get or post", db->set.introspection_mode); } DLLIST_PREPEND(&db_oauth2_head, db); return db; } void db_oauth2_ref(struct db_oauth2 *db) { i_assert(db->refcount > 0); db->refcount++; } void db_oauth2_unref(struct db_oauth2 **_db) { struct db_oauth2 *ptr, *db = *_db; i_assert(db->refcount > 0); if (--db->refcount > 0) return; for(ptr = db_oauth2_head; ptr != NULL; ptr = db->next) { if (ptr == db) { DLLIST_REMOVE(&db_oauth2_head, ptr); break; } } i_assert(ptr != NULL && ptr == db); /* make sure all requests are aborted */ while (db->head != NULL) oauth2_request_abort(&db->head->req); http_client_deinit(&db->client); pool_unref(&db->pool); } static bool db_oauth2_have_all_fields(struct db_oauth2_request *req) { unsigned int n,i; unsigned int size,idx; const char *const *args = passdb_template_get_args(req->db->tmpl, &n); if (req->fields == NULL) return FALSE; for(i=1;ifields, ptr+8)) return FALSE; ptr = ptr+size; } } } if (!auth_fields_exists(req->fields, req->db->set.username_attribute)) return FALSE; if (*req->db->set.active_attribute != '\0' && !auth_fields_exists(req->fields, req->db->set.active_attribute)) return FALSE; return TRUE; } static const char *field_get_default(const char *data) { const char *p; p = strchr(data, ':'); if (p == NULL) return ""; else { /* default value given */ return p+1; } } static const char * db_oauth2_var_expand_func_oauth2(const char *data, void *context) { struct db_oauth2_request *ctx = context; const char *field_name = t_strcut(data, ':'); const char *value = NULL; if (ctx->fields != NULL) value = auth_fields_find(ctx->fields, field_name); return value != NULL ? value : field_get_default(data); } static const char *escape_none(const char *value, const struct auth_request *req ATTR_UNUSED) { return value; } static const struct var_expand_table * db_oauth2_value_get_var_expand_table(struct auth_request *auth_request, const char *oauth2_value) { struct var_expand_table *table; unsigned int count = 1; table = auth_request_get_var_expand_table_full(auth_request, NULL, &count); table[0].key = '$'; table[0].value = oauth2_value; return table; } static bool db_oauth2_template_export(struct db_oauth2_request *req, enum passdb_result *result_r ATTR_UNUSED, const char **error_r ATTR_UNUSED) { /* var=$ expands into var=${oauth2:var} */ const struct var_expand_func_table funcs_table[] = { { "oauth2", db_oauth2_var_expand_func_oauth2 }, { NULL, NULL } }; string_t *dest; const char *const *args, *value; struct passdb_template *tmpl = req->db->tmpl; unsigned int i, count; if (passdb_template_is_empty(tmpl)) return TRUE; dest = t_str_new(256); args = passdb_template_get_args(tmpl, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (args[i+1] == NULL) value = ""; else { str_truncate(dest, 0); const struct var_expand_table * table = db_oauth2_value_get_var_expand_table(req->auth_request, auth_fields_find(req->fields, args[i])); var_expand_with_funcs(dest, args[i+1], table, funcs_table, req); value = str_c(dest); } auth_request_set_field(req->auth_request, args[i], value, STATIC_PASS_SCHEME); } return TRUE; } static void db_oauth2_fields_merge(struct db_oauth2_request *req, ARRAY_TYPE(oauth2_field) *fields) { const struct oauth2_field *field; if (req->fields == NULL) req->fields = auth_fields_init(req->pool); array_foreach(fields, field) { auth_fields_add(req->fields, field->name, field->value, 0); } } static void db_oauth2_callback(struct db_oauth2_request *req, enum passdb_result result, const char *error) { db_oauth2_lookup_callback_t *callback = req->callback; req->callback = NULL; i_assert(result == PASSDB_RESULT_OK || error != NULL); if (callback != NULL) { DLLIST_REMOVE(&req->db->head, req); callback(req, result, error, req->context); } } static bool db_oauth2_validate_username(struct db_oauth2_request *req, enum passdb_result *result_r, const char **error_r) { struct var_expand_table table[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { '\0', NULL, NULL } }; const char *username_value = auth_fields_find(req->fields, req->db->set.username_attribute); if (username_value == NULL) { *result_r = PASSDB_RESULT_INTERNAL_FAILURE; *error_r = "No username returned"; return FALSE; } table[0].value = username_value; table[1].value = t_strcut(username_value, '@'); table[2].value = strchr(username_value, '@'); if (table[2].value != NULL) table[2].value++; string_t *username_req = t_str_new(32); string_t *username_val = t_str_new(strlen(username_value)); auth_request_var_expand(username_req, req->db->set.username_format, req->auth_request, escape_none); var_expand(username_val, req->db->set.username_format, table); if (!str_equals(username_req, username_val)) { *error_r = t_strdup_printf("Username '%s' did not match '%s'", str_c(username_req), str_c(username_val)); *result_r = PASSDB_RESULT_USER_UNKNOWN; return FALSE; } else { return TRUE; } } static bool db_oauth2_user_is_enabled(struct db_oauth2_request *req, enum passdb_result *result_r, const char **error_r) { if (*req->db->set.active_attribute != '\0') { const char *active_value = auth_fields_find(req->fields, req->db->set.active_attribute); if (active_value == NULL || (*req->db->set.active_value != '\0' && strcmp(req->db->set.active_value, active_value) != 0)) { *error_r = "User account is not active"; *result_r = PASSDB_RESULT_USER_DISABLED; return FALSE; } } return TRUE; } static bool db_oauth2_token_in_scope(struct db_oauth2_request *req, enum passdb_result *result_r, const char **error_r) { if (*req->db->set.scope != '\0') { bool found = FALSE; const char *value = auth_fields_find(req->fields, "scope"); if (value != NULL) { const char **scopes = t_strsplit_spaces(value, " "); found = str_array_find(scopes, req->db->set.scope); } if (!found) { *error_r = t_strdup_printf("Token is not valid for scope '%s'", req->db->set.scope); *result_r = PASSDB_RESULT_USER_DISABLED; return FALSE; } } return TRUE; } static void db_oauth2_process_fields(struct db_oauth2_request *req, enum passdb_result *result_r, const char **error_r) { *error_r = NULL; if (db_oauth2_validate_username(req, result_r, error_r) && db_oauth2_user_is_enabled(req, result_r, error_r) && db_oauth2_token_in_scope(req, result_r, error_r) && db_oauth2_template_export(req, result_r, error_r)) { *result_r = PASSDB_RESULT_OK; } else { i_assert(*result_r != PASSDB_RESULT_OK && *error_r != NULL); } } static void db_oauth2_introspect_continue(struct oauth2_introspection_result *result, struct db_oauth2_request *req) { enum passdb_result passdb_result; const char *error; req->req = NULL; if (!result->success) { /* fail here */ passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; error = result->error; } else { db_oauth2_fields_merge(req, result->fields); db_oauth2_process_fields(req, &passdb_result, &error); } db_oauth2_callback(req, passdb_result, error); } static void db_oauth2_lookup_introspect(struct db_oauth2_request *req) { struct oauth2_request_input input; i_zero(&input); input.token = req->token; input.local_ip = req->auth_request->local_ip; input.local_port = req->auth_request->local_port; input.remote_ip = req->auth_request->remote_ip; input.remote_port = req->auth_request->remote_port; input.real_local_ip = req->auth_request->real_local_ip; input.real_local_port = req->auth_request->real_local_port; input.real_remote_ip = req->auth_request->real_remote_ip; input.real_remote_port = req->auth_request->real_remote_port; input.service = req->auth_request->service; req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, db_oauth2_introspect_continue, req); } static void db_oauth2_lookup_continue(struct oauth2_token_validation_result *result, struct db_oauth2_request *req) { enum passdb_result passdb_result; const char *error; req->req = NULL; if (!result->success) { passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; error = result->error; } else if (!result->valid) { passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; error = "Invalid token"; } else { db_oauth2_fields_merge(req, result->fields); if (*req->db->set.introspection_url != '\0' && (req->db->set.force_introspection || !db_oauth2_have_all_fields(req))) { db_oauth2_lookup_introspect(req); return; } db_oauth2_process_fields(req, &passdb_result, &error); } db_oauth2_callback(req, passdb_result, error); } #undef db_oauth2_lookup void db_oauth2_lookup(struct db_oauth2 *db, struct db_oauth2_request *req, const char *token, struct auth_request *request, db_oauth2_lookup_callback_t *callback, void *context) { struct oauth2_request_input input; i_zero(&input); req->db = db; req->token = p_strdup(req->pool, token); req->callback = callback; req->context = context; req->auth_request = request; input.token = token; input.local_ip = req->auth_request->local_ip; input.local_port = req->auth_request->local_port; input.remote_ip = req->auth_request->remote_ip; input.remote_port = req->auth_request->remote_port; input.real_local_ip = req->auth_request->real_local_ip; input.real_local_port = req->auth_request->real_local_port; input.real_remote_ip = req->auth_request->real_remote_ip; input.real_remote_port = req->auth_request->real_remote_port; input.service = req->auth_request->service; if (*db->oauth2_set.tokeninfo_url == '\0') { req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, db_oauth2_introspect_continue, req); } else { req->req = oauth2_token_validation_start(&db->oauth2_set, &input, db_oauth2_lookup_continue, req); } DLLIST_PREPEND(&db->head, req); } dovecot-2.2.33.2/src/auth/mycrypt.c0000644000175000017500000000100713123174404013725 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #define _XOPEN_SOURCE 4 #define _XOPEN_SOURCE_EXTENDED 1 /* 1 needed for AIX */ #ifndef _AIX # define _XOPEN_VERSION 4 /* breaks AIX */ #endif #define _XPG4_2 #ifdef CRYPT_USE_XPG6 # define _XPG6 /* Some Solaris versions require this, some break with this */ #endif #include #include "mycrypt.h" char *mycrypt(const char *key, const char *salt) { return crypt(key, salt); } dovecot-2.2.33.2/src/auth/mech-login.c0000644000175000017500000000346613123174404014253 00000000000000/* * LOGIN authentication mechanism. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "mech.h" #include "passdb.h" #include "safe-memset.h" #include "mech-plain-common.h" static void mech_login_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { static const char prompt2[] = "Password:"; const char *username, *error; if (request->user == NULL) { username = t_strndup(data, data_size); if (!auth_request_set_username(request, username, &error)) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); return; } auth_request_handler_reply_continue(request, prompt2, strlen(prompt2)); } else { char *pass = p_strndup(unsafe_data_stack_pool, data, data_size); auth_request_verify_plain(request, pass, plain_verify_callback); safe_memset(pass, 0, strlen(pass)); } } static void mech_login_auth_initial(struct auth_request *request, const unsigned char *data, size_t data_size) { static const char prompt1[] = "Username:"; if (data_size == 0) { auth_request_handler_reply_continue(request, prompt1, strlen(prompt1)); } else { mech_login_auth_continue(request, data, data_size); } } static struct auth_request *mech_login_auth_new(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"login_auth_request", 2048); request = p_new(pool, struct auth_request, 1); request->pool = pool; return request; } const struct mech_module mech_login = { "LOGIN", .flags = MECH_SEC_PLAINTEXT, .passdb_need = MECH_PASSDB_NEED_VERIFY_PLAIN, mech_login_auth_new, mech_login_auth_initial, mech_login_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/passdb-cache.h0000644000175000017500000000115113123174404014540 00000000000000#ifndef PASSDB_CACHE_H #define PASSDB_CACHE_H #include "auth-cache.h" #include "passdb.h" extern struct auth_cache *passdb_cache; bool passdb_cache_verify_plain(struct auth_request *request, const char *key, const char *password, enum passdb_result *result_r, bool use_expired); bool passdb_cache_lookup_credentials(struct auth_request *request, const char *key, const char **password_r, const char **scheme_r, enum passdb_result *result_r, bool use_expired); void passdb_cache_init(const struct auth_settings *set); void passdb_cache_deinit(void); #endif dovecot-2.2.33.2/src/auth/db-checkpassword.c0000644000175000017500000004201413165463624015457 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #if defined(PASSDB_CHECKPASSWORD) || defined(USERDB_CHECKPASSWORD) #include "lib-signals.h" #include "array.h" #include "buffer.h" #include "str.h" #include "ioloop.h" #include "hash.h" #include "execv-const.h" #include "env-util.h" #include "safe-memset.h" #include "strescape.h" #include "child-wait.h" #include "db-checkpassword.h" #include #include #define CHECKPASSWORD_MAX_REQUEST_LEN 512 struct chkpw_auth_request { struct db_checkpassword *db; struct auth_request *request; char *auth_password; db_checkpassword_callback_t *callback; void (*request_callback)(); pid_t pid; int fd_out, fd_in; struct io *io_out, *io_in; string_t *input_buf; size_t output_pos, output_len; int exit_status; unsigned int exited:1; }; struct db_checkpassword { char *checkpassword_path, *checkpassword_reply_path; HASH_TABLE(void *, struct chkpw_auth_request *) clients; struct child_wait *child_wait; }; static void env_put_extra_fields(const ARRAY_TYPE(auth_field) *extra_fields) { const struct auth_field *field; const char *key, *value; array_foreach(extra_fields, field) { key = t_str_ucase(field->key); value = field->value != NULL ? field->value : "1"; env_put(t_strconcat(key, "=", value, NULL)); } } static void checkpassword_request_close(struct chkpw_auth_request *request) { if (request->io_in != NULL) io_remove(&request->io_in); if (request->io_out != NULL) io_remove(&request->io_out); if (request->fd_in != -1) { if (close(request->fd_in) < 0) i_error("checkpassword: close() failed: %m"); request->fd_in = -1; } if (request->fd_out != -1) { if (close(request->fd_out) < 0) i_error("checkpassword: close() failed: %m"); } } static void checkpassword_request_free(struct chkpw_auth_request **_request) { struct chkpw_auth_request *request = *_request; *_request = NULL; if (!request->exited) { hash_table_remove(request->db->clients, POINTER_CAST(request->pid)); child_wait_remove_pid(request->db->child_wait, request->pid); } checkpassword_request_close(request); if (request->auth_password != NULL) { safe_memset(request->auth_password, 0, strlen(request->auth_password)); i_free(request->auth_password); } auth_request_unref(&request->request); str_free(&request->input_buf); i_free(request); } static void checkpassword_finish(struct chkpw_auth_request **_request, enum db_checkpassword_status status) { struct chkpw_auth_request *request = *_request; const char *const *extra_fields; *_request = NULL; extra_fields = t_strsplit_tabescaped(str_c(request->input_buf)); request->callback(request->request, status, extra_fields, request->request_callback); checkpassword_request_free(&request); } static void checkpassword_internal_failure(struct chkpw_auth_request **request) { checkpassword_finish(request, DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE); } static void checkpassword_request_finish_auth(struct chkpw_auth_request *request) { switch (request->exit_status) { /* vpopmail exit codes: */ case 3: /* password fail / vpopmail user not found */ case 12: /* null user name given */ case 13: /* null password given */ case 15: /* user has no password */ case 20: /* invalid user/domain characters */ case 21: /* system user not found */ case 22: /* system user shadow entry not found */ case 23: /* system password fail */ /* standard checkpassword exit codes: */ case 1: /* (1 is additionally defined in vpopmail for "pop/smtp/webmal/ imap/access denied") */ auth_request_log_info(request->request, AUTH_SUBSYS_DB, "Login failed (status=%d)", request->exit_status); checkpassword_finish(&request, DB_CHECKPASSWORD_STATUS_FAILURE); break; case 0: if (request->input_buf->used == 0) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Received no input"); checkpassword_internal_failure(&request); break; } checkpassword_finish(&request, DB_CHECKPASSWORD_STATUS_OK); break; case 2: /* checkpassword is called with wrong parameters? unlikely */ auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Child %s exited with status 2 (tried to use " "userdb-only checkpassword program for passdb?)", dec2str(request->pid)); checkpassword_internal_failure(&request); break; case 111: /* temporary problem, treat as internal error */ default: /* whatever error.. */ auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Child %s exited with status %d", dec2str(request->pid), request->exit_status); checkpassword_internal_failure(&request); break; } } static void checkpassword_request_finish_lookup(struct chkpw_auth_request *request) { switch (request->exit_status) { case 3: /* User does not exist. */ auth_request_log_info(request->request, AUTH_SUBSYS_DB, "User unknown"); checkpassword_finish(&request, DB_CHECKPASSWORD_STATUS_FAILURE); break; case 2: /* This is intentionally not 0. checkpassword-reply exits with 2 on success when AUTHORIZED is set. */ if (request->input_buf->used == 0) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Received no input"); checkpassword_internal_failure(&request); break; } checkpassword_finish(&request, DB_CHECKPASSWORD_STATUS_OK); break; default: /* whatever error... */ auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Child %s exited with status %d", dec2str(request->pid), request->exit_status); checkpassword_internal_failure(&request); break; } } static void checkpassword_request_half_finish(struct chkpw_auth_request *request) { /* the process must have exited, and the input fd must have closed */ if (!request->exited || request->fd_in != -1) return; if (request->auth_password != NULL) checkpassword_request_finish_auth(request); else checkpassword_request_finish_lookup(request); } static void env_put_auth_vars(struct auth_request *request) { const struct var_expand_table *tab; unsigned int i; tab = auth_request_get_var_expand_table(request, NULL); for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) { /* avoid keeping passwords in environment .. just in case an attacker might find it from there. environment is no longer world-readable in modern OSes, but maybe the attacker could be running with the same UID. of course then the attacker could usually ptrace() the process, except that is disabled on some secured systems. so, although I find it highly unlikely anyone could actually attack Dovecot this way in a real system, be safe just in case. besides, lets try to keep at least minimally compatible with the checkpassword API. */ if (tab[i].long_key != NULL && tab[i].value != NULL && strcasecmp(tab[i].long_key, "password") != 0) { env_put(t_strdup_printf("AUTH_%s=%s", t_str_ucase(tab[i].long_key), tab[i].value)); } } } static void checkpassword_setup_env(struct auth_request *request) { /* Besides passing the standard username and password in a pipe, also pass some other possibly interesting information via environment. Use UCSPI names for local/remote IPs. */ env_put("PROTO=TCP"); /* UCSPI */ env_put(t_strdup_printf("ORIG_UID=%s", dec2str(getuid()))); env_put(t_strconcat("SERVICE=", request->service, NULL)); if (request->local_ip.family != 0) { env_put(t_strconcat("TCPLOCALIP=", net_ip2addr(&request->local_ip), NULL)); /* FIXME: for backwards compatibility only, remove some day */ env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL)); } if (request->remote_ip.family != 0) { env_put(t_strconcat("TCPREMOTEIP=", net_ip2addr(&request->remote_ip), NULL)); /* FIXME: for backwards compatibility only, remove some day */ env_put(t_strconcat("REMOTE_IP=", net_ip2addr(&request->remote_ip), NULL)); } if (request->local_port != 0) { env_put(t_strdup_printf("TCPLOCALPORT=%u", request->local_port)); } if (request->remote_port != 0) { env_put(t_strdup_printf("TCPREMOTEPORT=%u", request->remote_port)); } if (request->master_user != NULL) { env_put(t_strconcat("MASTER_USER=", request->master_user, NULL)); } if (!auth_fields_is_empty(request->extra_fields)) { const ARRAY_TYPE(auth_field) *fields = auth_fields_export(request->extra_fields); /* extra fields could come from master db */ env_put_extra_fields(fields); } env_put_auth_vars(request); } static const char * checkpassword_get_cmd(struct auth_request *request, const char *args, const char *checkpassword_reply_path) { string_t *str; str = t_str_new(256); auth_request_var_expand(str, args, request, NULL); return t_strconcat(str_c(str), " ", checkpassword_reply_path, NULL); } static void checkpassword_child_input(struct chkpw_auth_request *request) { unsigned char buf[1024]; ssize_t ret; ret = read(request->fd_in, buf, sizeof(buf)); if (ret > 0) { str_append_n(request->input_buf, buf, ret); return; } if (ret < 0) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "read() failed: %m"); checkpassword_internal_failure(&request); } else if (strchr(str_c(request->input_buf), '\n') != NULL) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "LF characters in checkpassword reply"); checkpassword_internal_failure(&request); } else { auth_request_log_debug(request->request, AUTH_SUBSYS_DB, "Received input: %s", str_c(request->input_buf)); checkpassword_request_close(request); checkpassword_request_half_finish(request); } } static void checkpassword_child_output(struct chkpw_auth_request *request) { /* Send: username \0 password \0 timestamp \0. Must be 512 bytes or less. The "timestamp" parameter is actually useful only for APOP authentication. We don't support it, so keep it empty */ struct auth_request *auth_request = request->request; buffer_t *buf; const unsigned char *data; size_t size; ssize_t ret; buf = buffer_create_dynamic(pool_datastack_create(), CHECKPASSWORD_MAX_REQUEST_LEN); buffer_append(buf, auth_request->user, strlen(auth_request->user)+1); if (request->auth_password != NULL) { buffer_append(buf, request->auth_password, strlen(request->auth_password)+1); } else { buffer_append_c(buf, '\0'); } buffer_append_c(buf, '\0'); data = buffer_get_data(buf, &size); i_assert(size == request->output_len); /* already checked this */ i_assert(size <= CHECKPASSWORD_MAX_REQUEST_LEN); ret = write(request->fd_out, data + request->output_pos, size - request->output_pos); if (ret <= 0) { if (ret < 0) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "write() failed: %m"); } else { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "write() returned 0"); } checkpassword_internal_failure(&request); return; } request->output_pos += ret; if (request->output_pos < size) return; /* finished sending the data */ io_remove(&request->io_out); if (close(request->fd_out) < 0) i_error("checkpassword: close() failed: %m"); request->fd_out = -1; } static void ATTR_NORETURN checkpassword_exec(struct db_checkpassword *db, struct auth_request *request, int fd_in, int fd_out, bool authenticate) { const char *cmd, *const *args; /* fd 3 is used to send the username+password for the script fd 4 is used to communicate with checkpassword-reply */ if (dup2(fd_out, 3) < 0 || dup2(fd_in, 4) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "dup2() failed: %m"); exit(111); } if (!authenticate) { /* We want to retrieve passdb/userdb data and don't do authorization, so we need to signalize the checkpassword program that the password shall be ignored by setting AUTHORIZED. This needs a special checkpassword program which knows how to handle this. */ env_put("AUTHORIZED=1"); if (request->credentials_scheme != NULL) { /* passdb credentials lookup */ env_put("CREDENTIALS_LOOKUP=1"); env_put(t_strdup_printf("SCHEME=%s", request->credentials_scheme)); } } checkpassword_setup_env(request); cmd = checkpassword_get_cmd(request, db->checkpassword_path, db->checkpassword_reply_path); auth_request_log_debug(request, AUTH_SUBSYS_DB, "execute: %s", cmd); /* very simple argument splitting. */ args = t_strsplit(cmd, " "); execv_const(args[0], args); } static void sigchld_handler(const struct child_wait_status *status, struct db_checkpassword *db) { struct chkpw_auth_request *request = hash_table_lookup(db->clients, POINTER_CAST(status->pid)); i_assert(request != NULL); hash_table_remove(db->clients, POINTER_CAST(status->pid)); request->exited = TRUE; if (WIFSIGNALED(status->status)) { auth_request_log_error(request->request, AUTH_SUBSYS_DB, "Child %s died with signal %d", dec2str(status->pid), WTERMSIG(status->status)); checkpassword_internal_failure(&request); } else if (WIFEXITED(status->status)) { request->exit_status = WEXITSTATUS(status->status); auth_request_log_debug(request->request, AUTH_SUBSYS_DB, "exit_status=%d", request->exit_status); checkpassword_request_half_finish(request); } else { /* shouldn't happen */ auth_request_log_debug(request->request, AUTH_SUBSYS_DB, "Child %s exited with status=%d", dec2str(status->pid), status->status); checkpassword_internal_failure(&request); } } void db_checkpassword_call(struct db_checkpassword *db, struct auth_request *request, const char *auth_password, db_checkpassword_callback_t *callback, void (*request_callback)()) { struct chkpw_auth_request *chkpw_auth_request; size_t output_len; int fd_in[2], fd_out[2]; pid_t pid; /* \0 \0 timestamp \0 */ output_len = strlen(request->user) + 3; if (auth_password != NULL) output_len += strlen(auth_password); if (output_len > CHECKPASSWORD_MAX_REQUEST_LEN) { auth_request_log_info(request, AUTH_SUBSYS_DB, "Username+password combination too long (%"PRIuSIZE_T" bytes)", output_len); callback(request, DB_CHECKPASSWORD_STATUS_FAILURE, NULL, request_callback); return; } fd_in[0] = -1; if (pipe(fd_in) < 0 || pipe(fd_out) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "pipe() failed: %m"); if (fd_in[0] != -1) { i_close_fd(&fd_in[0]); i_close_fd(&fd_in[1]); } callback(request, DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE, NULL, request_callback); return; } pid = fork(); if (pid == -1) { auth_request_log_error(request, AUTH_SUBSYS_DB, "fork() failed: %m"); i_close_fd(&fd_in[0]); i_close_fd(&fd_in[1]); i_close_fd(&fd_out[0]); i_close_fd(&fd_out[1]); callback(request, DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE, NULL, request_callback); return; } if (pid == 0) { /* child */ i_close_fd(&fd_in[0]); i_close_fd(&fd_out[1]); checkpassword_exec(db, request, fd_in[1], fd_out[0], auth_password != NULL); /* not reached */ } if (close(fd_in[1]) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "close(fd_in[1]) failed: %m"); } if (close(fd_out[0]) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "close(fd_out[0]) failed: %m"); } auth_request_ref(request); chkpw_auth_request = i_new(struct chkpw_auth_request, 1); chkpw_auth_request->db = db; chkpw_auth_request->pid = pid; chkpw_auth_request->fd_in = fd_in[0]; chkpw_auth_request->fd_out = fd_out[1]; chkpw_auth_request->auth_password = i_strdup(auth_password); chkpw_auth_request->request = request; chkpw_auth_request->output_len = output_len; chkpw_auth_request->input_buf = str_new(default_pool, 256); chkpw_auth_request->callback = callback; chkpw_auth_request->request_callback = request_callback; chkpw_auth_request->io_in = io_add(fd_in[0], IO_READ, checkpassword_child_input, chkpw_auth_request); chkpw_auth_request->io_out = io_add(fd_out[1], IO_WRITE, checkpassword_child_output, chkpw_auth_request); hash_table_insert(db->clients, POINTER_CAST(pid), chkpw_auth_request); child_wait_add_pid(db->child_wait, pid); } struct db_checkpassword * db_checkpassword_init(const char *checkpassword_path, const char *checkpassword_reply_path) { struct db_checkpassword *db; db = i_new(struct db_checkpassword, 1); db->checkpassword_path = i_strdup(checkpassword_path); db->checkpassword_reply_path = i_strdup(checkpassword_reply_path); hash_table_create_direct(&db->clients, default_pool, 0); db->child_wait = child_wait_new_with_pid((pid_t)-1, sigchld_handler, db); return db; } void db_checkpassword_deinit(struct db_checkpassword **_db) { struct db_checkpassword *db = *_db; struct hash_iterate_context *iter; void *key; struct chkpw_auth_request *request; *_db = NULL; iter = hash_table_iterate_init(db->clients); while (hash_table_iterate(iter, db->clients, &key, &request)) checkpassword_internal_failure(&request); hash_table_iterate_deinit(&iter); child_wait_free(&db->child_wait); hash_table_destroy(&db->clients); i_free(db->checkpassword_reply_path); i_free(db->checkpassword_path); i_free(db); } #endif dovecot-2.2.33.2/src/auth/auth-master-connection.h0000644000175000017500000000217013165463624016627 00000000000000#ifndef AUTH_MASTER_CONNECTION_H #define AUTH_MASTER_CONNECTION_H struct stat; struct auth_stream_reply; struct auth_master_connection { struct auth_master_connection *prev, *next; struct auth *auth; int refcount; int fd; char *path; struct istream *input; struct ostream *output; struct io *io; struct master_list_iter_ctx *iter_ctx; /* If non-zero, allow only USER lookups whose returned uid matches this uid. Don't allow LIST/PASS lookups. */ uid_t userdb_restricted_uid; unsigned int version_received:1; unsigned int destroyed:1; unsigned int userdb_only:1; }; struct auth_master_connection * auth_master_connection_create(struct auth *auth, int fd, const char *path, const struct stat *socket_st, bool userdb_only) ATTR_NULL(4); void auth_master_connection_destroy(struct auth_master_connection **conn); void auth_master_connection_ref(struct auth_master_connection *conn); void auth_master_connection_unref(struct auth_master_connection **conn); void auth_master_request_callback(const char *reply, struct auth_master_connection *conn); void auth_master_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/auth/auth.c0000644000175000017500000003000613165463624013173 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "settings-parser.h" #include "master-service-settings.h" #include "mech.h" #include "userdb.h" #include "passdb.h" #include "passdb-template.h" #include "userdb-template.h" #include "auth.h" static const struct auth_userdb_settings userdb_dummy_set = { .name = "", .driver = "static", .args = "", .default_fields = "", .override_fields = "", .skip = "never", .result_success = "return-ok", .result_failure = "continue", .result_internalfail = "continue" }; static ARRAY(struct auth *) auths; static enum auth_passdb_skip auth_passdb_skip_parse(const char *str) { if (strcmp(str, "never") == 0) return AUTH_PASSDB_SKIP_NEVER; if (strcmp(str, "authenticated") == 0) return AUTH_PASSDB_SKIP_AUTHENTICATED; if (strcmp(str, "unauthenticated") == 0) return AUTH_PASSDB_SKIP_UNAUTHENTICATED; i_unreached(); } static enum auth_userdb_skip auth_userdb_skip_parse(const char *str) { if (strcmp(str, "never") == 0) return AUTH_USERDB_SKIP_NEVER; if (strcmp(str, "found") == 0) return AUTH_USERDB_SKIP_FOUND; if (strcmp(str, "notfound") == 0) return AUTH_USERDB_SKIP_NOTFOUND; i_unreached(); } static enum auth_db_rule auth_db_rule_parse(const char *str) { if (strcmp(str, "return") == 0) return AUTH_DB_RULE_RETURN; if (strcmp(str, "return-ok") == 0) return AUTH_DB_RULE_RETURN_OK; if (strcmp(str, "return-fail") == 0) return AUTH_DB_RULE_RETURN_FAIL; if (strcmp(str, "continue") == 0) return AUTH_DB_RULE_CONTINUE; if (strcmp(str, "continue-ok") == 0) return AUTH_DB_RULE_CONTINUE_OK; if (strcmp(str, "continue-fail") == 0) return AUTH_DB_RULE_CONTINUE_FAIL; i_unreached(); } static void auth_passdb_preinit(struct auth *auth, const struct auth_passdb_settings *set, struct auth_passdb **passdbs) { struct auth_passdb *auth_passdb, **dest; auth_passdb = p_new(auth->pool, struct auth_passdb, 1); auth_passdb->set = set; auth_passdb->skip = auth_passdb_skip_parse(set->skip); auth_passdb->result_success = auth_db_rule_parse(set->result_success); auth_passdb->result_failure = auth_db_rule_parse(set->result_failure); auth_passdb->result_internalfail = auth_db_rule_parse(set->result_internalfail); auth_passdb->default_fields_tmpl = passdb_template_build(auth->pool, set->default_fields); auth_passdb->override_fields_tmpl = passdb_template_build(auth->pool, set->override_fields); /* for backwards compatibility: */ if (set->pass) auth_passdb->result_success = AUTH_DB_RULE_CONTINUE; for (dest = passdbs; *dest != NULL; dest = &(*dest)->next) ; *dest = auth_passdb; auth_passdb->passdb = passdb_preinit(auth->pool, set); /* make sure any %variables in default_fields exist in cache_key */ if (auth_passdb->passdb->default_cache_key != NULL) { auth_passdb->cache_key = p_strconcat(auth->pool, auth_passdb->passdb->default_cache_key, set->default_fields, NULL); } else { auth_passdb->cache_key = NULL; } } static void auth_userdb_preinit(struct auth *auth, const struct auth_userdb_settings *set) { struct auth_userdb *auth_userdb, **dest; auth_userdb = p_new(auth->pool, struct auth_userdb, 1); auth_userdb->set = set; auth_userdb->skip = auth_userdb_skip_parse(set->skip); auth_userdb->result_success = auth_db_rule_parse(set->result_success); auth_userdb->result_failure = auth_db_rule_parse(set->result_failure); auth_userdb->result_internalfail = auth_db_rule_parse(set->result_internalfail); auth_userdb->default_fields_tmpl = userdb_template_build(auth->pool, set->driver, set->default_fields); auth_userdb->override_fields_tmpl = userdb_template_build(auth->pool, set->driver, set->override_fields); for (dest = &auth->userdbs; *dest != NULL; dest = &(*dest)->next) ; *dest = auth_userdb; auth_userdb->userdb = userdb_preinit(auth->pool, set); /* make sure any %variables in default_fields exist in cache_key */ if (auth_userdb->userdb->default_cache_key != NULL) { auth_userdb->cache_key = p_strconcat(auth->pool, auth_userdb->userdb->default_cache_key, set->default_fields, NULL); } else { auth_userdb->cache_key = NULL; } } static bool auth_passdb_list_have_verify_plain(const struct auth *auth) { const struct auth_passdb *passdb; for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) { if (passdb->passdb->iface.verify_plain != NULL) return TRUE; } return FALSE; } static bool auth_passdb_list_have_lookup_credentials(const struct auth *auth) { const struct auth_passdb *passdb; for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) { if (passdb->passdb->iface.lookup_credentials != NULL) return TRUE; } return FALSE; } static int auth_passdb_list_have_set_credentials(const struct auth *auth) { const struct auth_passdb *passdb; for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) { if (passdb->passdb->iface.set_credentials != NULL) return TRUE; } for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) { if (passdb->passdb->iface.set_credentials != NULL) return TRUE; } return FALSE; } static bool auth_mech_verify_passdb(const struct auth *auth, const struct mech_module_list *list) { switch (list->module.passdb_need) { case MECH_PASSDB_NEED_NOTHING: break; case MECH_PASSDB_NEED_VERIFY_PLAIN: if (!auth_passdb_list_have_verify_plain(auth)) return FALSE; break; case MECH_PASSDB_NEED_VERIFY_RESPONSE: case MECH_PASSDB_NEED_LOOKUP_CREDENTIALS: if (!auth_passdb_list_have_lookup_credentials(auth)) return FALSE; break; case MECH_PASSDB_NEED_SET_CREDENTIALS: if (!auth_passdb_list_have_lookup_credentials(auth)) return FALSE; if (!auth_passdb_list_have_set_credentials(auth)) return FALSE; break; } return TRUE; } static void auth_mech_list_verify_passdb(const struct auth *auth) { const struct mech_module_list *list; for (list = auth->reg->modules; list != NULL; list = list->next) { if (!auth_mech_verify_passdb(auth, list)) break; } if (list != NULL) { if (auth->passdbs == NULL) { i_fatal("No passdbs specified in configuration file. " "%s mechanism needs one", list->module.mech_name); } i_fatal("%s mechanism can't be supported with given passdbs", list->module.mech_name); } } static struct auth * ATTR_NULL(2) auth_preinit(const struct auth_settings *set, const char *service, pool_t pool, const struct mechanisms_register *reg) { struct auth_passdb_settings *const *passdbs; struct auth_userdb_settings *const *userdbs; struct auth *auth; unsigned int i, count, db_count, passdb_count, last_passdb = 0; auth = p_new(pool, struct auth, 1); auth->pool = pool; auth->service = p_strdup(pool, service); auth->set = set; auth->reg = reg; if (array_is_created(&set->passdbs)) passdbs = array_get(&set->passdbs, &db_count); else { passdbs = NULL; db_count = 0; } /* initialize passdbs first and count them */ for (passdb_count = 0, i = 0; i < db_count; i++) { if (passdbs[i]->master) continue; /* passdb { skip=unauthenticated } as the first passdb doesn't make sense, since user is never authenticated at that point. skip over them silently. */ if (auth->passdbs == NULL && auth_passdb_skip_parse(passdbs[i]->skip) == AUTH_PASSDB_SKIP_UNAUTHENTICATED) continue; auth_passdb_preinit(auth, passdbs[i], &auth->passdbs); passdb_count++; last_passdb = i; } if (passdb_count != 0 && passdbs[last_passdb]->pass) i_fatal("Last passdb can't have pass=yes"); for (i = 0; i < db_count; i++) { if (!passdbs[i]->master) continue; /* skip skip=unauthenticated, as explained above */ if (auth->masterdbs == NULL && auth_passdb_skip_parse(passdbs[i]->skip) == AUTH_PASSDB_SKIP_UNAUTHENTICATED) continue; if (passdbs[i]->deny) i_fatal("Master passdb can't have deny=yes"); if (passdbs[i]->pass && passdb_count == 0) { i_fatal("Master passdb can't have pass=yes " "if there are no passdbs"); } auth_passdb_preinit(auth, passdbs[i], &auth->masterdbs); } if (array_is_created(&set->userdbs)) { userdbs = array_get(&set->userdbs, &count); for (i = 0; i < count; i++) auth_userdb_preinit(auth, userdbs[i]); } if (auth->userdbs == NULL) { /* use a dummy userdb static. */ auth_userdb_preinit(auth, &userdb_dummy_set); } return auth; } static void auth_passdb_init(struct auth_passdb *passdb) { passdb_init(passdb->passdb); i_assert(passdb->passdb->default_pass_scheme != NULL || passdb->cache_key == NULL); } static void auth_init(struct auth *auth) { struct auth_passdb *passdb; struct auth_userdb *userdb; for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) auth_passdb_init(passdb); for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) auth_passdb_init(passdb); for (userdb = auth->userdbs; userdb != NULL; userdb = userdb->next) userdb_init(userdb->userdb); } static void auth_deinit(struct auth *auth) { struct auth_passdb *passdb; struct auth_userdb *userdb; for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) passdb_deinit(passdb->passdb); for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) passdb_deinit(passdb->passdb); for (userdb = auth->userdbs; userdb != NULL; userdb = userdb->next) userdb_deinit(userdb->userdb); } struct auth *auth_find_service(const char *name) { struct auth *const *a; unsigned int i, count; a = array_get(&auths, &count); if (name != NULL) { for (i = 1; i < count; i++) { if (strcmp(a[i]->service, name) == 0) return a[i]; } /* not found. maybe we can instead find a !service */ for (i = 1; i < count; i++) { if (a[i]->service[0] == '!' && strcmp(a[i]->service + 1, name) != 0) return a[i]; } } return a[0]; } struct auth *auth_default_service(void) { struct auth *const *a; unsigned int count; a = array_get(&auths, &count); return a[0]; } void auths_preinit(const struct auth_settings *set, pool_t pool, const struct mechanisms_register *reg, const char *const *services) { struct master_service_settings_output set_output; const struct auth_settings *service_set; struct auth *auth, *const *authp; unsigned int i; const char *not_service = NULL; bool check_default = TRUE; i_array_init(&auths, 8); auth = auth_preinit(set, NULL, pool, reg); array_append(&auths, &auth, 1); for (i = 0; services[i] != NULL; i++) { if (services[i][0] == '!') { if (not_service != NULL) { i_fatal("Can't have multiple protocol " "!services (seen %s and %s)", not_service, services[i]); } not_service = services[i]; } service_set = auth_settings_read(services[i], pool, &set_output); auth = auth_preinit(service_set, services[i], pool, reg); array_append(&auths, &auth, 1); } if (not_service != NULL && str_array_find(services, not_service+1)) check_default = FALSE; array_foreach(&auths, authp) { if ((*authp)->service != NULL || check_default) auth_mech_list_verify_passdb(*authp); } } void auths_init(void) { struct auth *const *auth; /* sanity checks */ i_assert(auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_USER_IDX].key == 'u'); i_assert(auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_USERNAME_IDX].key == 'n'); i_assert(auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX].key == 'd'); i_assert(auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT].key == '\0' && auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT].long_key == NULL); i_assert(auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT-1].key != '\0' || auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT-1].long_key != NULL); array_foreach(&auths, auth) auth_init(*auth); } void auths_deinit(void) { struct auth *const *auth; array_foreach(&auths, auth) auth_deinit(*auth); } void auths_free(void) { struct auth **auth; unsigned int i, count; /* deinit in reverse order, because modules have been allocated by the first auth pool that used them */ auth = array_get_modifiable(&auths, &count); for (i = count; i > 0; i--) pool_unref(&auth[i-1]->pool); array_free(&auths); } dovecot-2.2.33.2/src/auth/password-scheme-crypt.c0000644000175000017500000001133713165463624016503 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mycrypt.h" #include "password-scheme.h" /* Lengths and limits for some crypt() algorithms. */ #define CRYPT_BLF_ROUNDS_DEFAULT 5 #define CRYPT_BLF_ROUNDS_MIN 4 #define CRYPT_BLF_ROUNDS_MAX 31 #define CRYPT_BLF_SALT_LEN 22 #define CRYPT_SHA2_ROUNDS_DEFAULT 5000 #define CRYPT_SHA2_ROUNDS_MIN 1000 #define CRYPT_SHA2_ROUNDS_MAX 999999999 #define CRYPT_SHA2_SALT_LEN 16 unsigned int password_scheme_encryption_rounds = 0; void password_set_encryption_rounds(unsigned int rounds) { /* just take the new value. crypt_generate_*() will enforce their limits. */ password_scheme_encryption_rounds = rounds; } static void crypt_generate_des(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { #define CRYPT_SALT_LEN 2 const char *password, *salt; salt = password_generate_salt(CRYPT_SALT_LEN); password = t_strdup(mycrypt(plaintext, salt)); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void crypt_generate_blowfisch(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password, *salt, *magic_salt; unsigned int rounds = password_scheme_encryption_rounds; if (rounds == 0) rounds = CRYPT_BLF_ROUNDS_DEFAULT; else if (rounds < CRYPT_BLF_ROUNDS_MIN) rounds = CRYPT_BLF_ROUNDS_MIN; else if (rounds > CRYPT_BLF_ROUNDS_MAX) rounds = CRYPT_BLF_ROUNDS_MAX; salt = password_generate_salt(CRYPT_BLF_SALT_LEN); magic_salt = t_strdup_printf("$2a$%02u$%s", rounds, salt); password = t_strdup(mycrypt(plaintext, magic_salt)); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void crypt_generate_sha256(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password, *salt, *magic_salt; unsigned int rounds = password_scheme_encryption_rounds; if (rounds == 0) rounds = CRYPT_SHA2_ROUNDS_DEFAULT; else if (rounds < CRYPT_SHA2_ROUNDS_MIN) rounds = CRYPT_SHA2_ROUNDS_MIN; else if (rounds > CRYPT_SHA2_ROUNDS_MAX) rounds = CRYPT_SHA2_ROUNDS_MAX; salt = password_generate_salt(CRYPT_SHA2_SALT_LEN); if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT) magic_salt = t_strdup_printf("$5$%s", salt); else magic_salt = t_strdup_printf("$5$rounds=%u$%s", rounds, salt); password = t_strdup(mycrypt(plaintext, magic_salt)); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } static void crypt_generate_sha512(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { const char *password, *salt, *magic_salt; unsigned int rounds = password_scheme_encryption_rounds; if (rounds == 0) rounds = CRYPT_SHA2_ROUNDS_DEFAULT; else if (rounds < CRYPT_SHA2_ROUNDS_MIN) rounds = CRYPT_SHA2_ROUNDS_MIN; else if (rounds > CRYPT_SHA2_ROUNDS_MAX) rounds = CRYPT_SHA2_ROUNDS_MAX; salt = password_generate_salt(CRYPT_SHA2_SALT_LEN); if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT) magic_salt = t_strdup_printf("$6$%s", salt); else magic_salt = t_strdup_printf("$6$rounds=%u$%s", rounds, salt); password = t_strdup(mycrypt(plaintext, magic_salt)); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); } /* keep in sync with the crypt_schemes struct below */ static const struct { const char *key; const char *salt; const char *expected; } sample[] = { { "08/15!test~4711", "JB", "JBOZ0DgmtucwE" }, { "08/15!test~4711", "$2a$04$0123456789abcdefABCDEF", "$2a$04$0123456789abcdefABCDE.N.drYX5yIAL1LkTaaZotW3yI0hQhZru" }, { "08/15!test~4711", "$5$rounds=1000$0123456789abcdef", "$5$rounds=1000$0123456789abcdef$K/DksR0DT01hGc8g/kt" "9McEgrbFMKi9qrb1jehe7hn4" }, { "08/15!test~4711", "$6$rounds=1000$0123456789abcdef", "$6$rounds=1000$0123456789abcdef$ZIAd5WqfyLkpvsVCVUU1GrvqaZTq" "vhJoouxdSqJO71l9Ld3tVrfOatEjarhghvEYADkq//LpDnTeO90tcbtHR1" } }; /* keep in sync with the sample struct above */ static const struct password_scheme crypt_schemes[] = { { "CRYPT", PW_ENCODING_NONE, 0, crypt_verify, crypt_generate_des }, { "BLF-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, crypt_generate_blowfisch }, { "SHA256-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, crypt_generate_sha256 }, { "SHA512-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, crypt_generate_sha512 } }; void password_scheme_register_crypt(void) { unsigned int i; const char *crypted; for (i = 0; i < N_ELEMENTS(crypt_schemes); i++) { crypted = mycrypt(sample[i].key, sample[i].salt); if (crypted != NULL && (strcmp(crypted, sample[i].expected) == 0)) password_scheme_register(&crypt_schemes[i]); } } dovecot-2.2.33.2/src/auth/mech-skey.c0000644000175000017500000001163613165463624014127 00000000000000/* * S/Key (RFC 1731) authentication mechanism. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "safe-memset.h" #include "hash.h" #include "mech.h" #include "passdb.h" #include "hex-binary.h" #include "otp.h" #include "mech-otp-skey-common.h" static void skey_send_challenge(struct auth_request *auth_request, const unsigned char *credentials, size_t size) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; const char *answer; if (otp_parse_dbentry(t_strndup(credentials, size), &request->state) != 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid OTP data in passdb"); auth_request_fail(auth_request); return; } if (request->state.algo != OTP_HASH_MD4) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "md4 hash is needed"); auth_request_fail(auth_request); return; } if (--request->state.seq < 1) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "sequence number < 1"); auth_request_fail(auth_request); return; } request->lock = otp_try_lock(auth_request); if (!request->lock) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "user is locked, race attack?"); auth_request_fail(auth_request); return; } answer = p_strdup_printf(request->pool, "%u %s", request->state.seq, request->state.seed); auth_request_handler_reply_continue(auth_request, answer, strlen(answer)); } static void otp_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { switch (result) { case PASSDB_RESULT_OK: skey_send_challenge(auth_request, credentials, size); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void skey_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { switch (result) { case PASSDB_RESULT_OK: skey_send_challenge(auth_request, credentials, size); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: /* S/KEY credentials not found, try OTP */ auth_request_lookup_credentials(auth_request, "OTP", otp_credentials_callback); break; } } static void mech_skey_auth_phase1(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { const char *username, *error; username = t_strndup(data, data_size); if (!auth_request_set_username(auth_request, username, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); return; } auth_request_lookup_credentials(auth_request, "SKEY", skey_credentials_callback); } static void mech_skey_auth_phase2(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct otp_auth_request *request = (struct otp_auth_request *)auth_request; struct otp_state *state = &request->state; unsigned char hash[OTP_HASH_SIZE], cur_hash[OTP_HASH_SIZE]; int ret; if (data_size == 8) { memcpy(hash, data, 8); } else { const char *words = t_strndup(data, data_size); ret = otp_parse_response(words, hash, 0); if (ret < 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid response"); auth_request_fail(auth_request); otp_unlock(auth_request); return; } } otp_next_hash(state->algo, hash, cur_hash); ret = memcmp(cur_hash, state->hash, OTP_HASH_SIZE); if (ret != 0) { auth_request_fail(auth_request); otp_unlock(auth_request); return; } memcpy(state->hash, hash, sizeof(state->hash)); auth_request_set_credentials(auth_request, "OTP", otp_print_dbentry(state), otp_set_credentials_callback); } static void mech_skey_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { if (auth_request->user == NULL) { mech_skey_auth_phase1(auth_request, data, data_size); } else { mech_skey_auth_phase2(auth_request, data, data_size); } } static struct auth_request *mech_skey_auth_new(void) { struct otp_auth_request *request; pool_t pool; otp_lock_init(); pool = pool_alloconly_create(MEMPOOL_GROWING"skey_auth_request", 2048); request = p_new(pool, struct otp_auth_request, 1); request->pool = pool; request->lock = FALSE; request->auth_request.refcount = 1; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_skey = { "SKEY", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_SET_CREDENTIALS, mech_skey_auth_new, mech_generic_auth_initial, mech_skey_auth_continue, mech_otp_skey_auth_free }; dovecot-2.2.33.2/src/auth/mech-gssapi.c0000644000175000017500000005166513165463624014450 00000000000000/* * GSSAPI Module * * Copyright (c) 2005 Jelmer Vernooij * * Related standards: * - draft-ietf-sasl-gssapi-03 * - RFC2222 * * Some parts inspired by an older patch from Colin Walters * * This software is released under the MIT license. */ #include "auth-common.h" #include "env-util.h" #include "str.h" #include "str-sanitize.h" #include "hex-binary.h" #include "safe-memset.h" #include "mech.h" #include "passdb.h" #if defined(BUILTIN_GSSAPI) || defined(PLUGIN_BUILD) #ifndef HAVE___GSS_USEROK # define USE_KRB5_USEROK # include #endif #ifdef HAVE_GSSAPI_GSSAPI_H # include #elif defined (HAVE_GSSAPI_H) # include #endif #ifdef HAVE_GSSAPI_GSSAPI_KRB5_H # include #elif defined (HAVE_GSSAPI_KRB5_H) # include #else # undef USE_KRB5_USEROK #endif #ifdef HAVE_GSSAPI_GSSAPI_EXT_H # include #endif /* Non-zero flags defined in RFC 2222 */ enum sasl_gssapi_qop { SASL_GSSAPI_QOP_UNSPECIFIED = 0x00, SASL_GSSAPI_QOP_AUTH_ONLY = 0x01, SASL_GSSAPI_QOP_AUTH_INT = 0x02, SASL_GSSAPI_QOP_AUTH_CONF = 0x04 }; struct gssapi_auth_request { struct auth_request auth_request; gss_ctx_id_t gss_ctx; gss_cred_id_t service_cred; enum { GSS_STATE_SEC_CONTEXT, GSS_STATE_WRAP, GSS_STATE_UNWRAP } sasl_gssapi_state; gss_name_t authn_name; gss_name_t authz_name; pool_t pool; }; static bool gssapi_initialized = FALSE; static gss_OID_desc mech_gssapi_krb5_oid = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; static int mech_gssapi_wrap(struct gssapi_auth_request *request, gss_buffer_desc inbuf); static void mech_gssapi_log_error(struct auth_request *request, OM_uint32 status_value, int status_type, const char *description) { OM_uint32 message_context = 0; OM_uint32 minor_status; gss_buffer_desc status_string; do { (void)gss_display_status(&minor_status, status_value, status_type, GSS_C_NO_OID, &message_context, &status_string); auth_request_log_info(request, AUTH_SUBSYS_MECH, "While %s: %s", description, str_sanitize(status_string.value, (size_t)-1)); (void)gss_release_buffer(&minor_status, &status_string); } while (message_context != 0); } static void mech_gssapi_initialize(const struct auth_settings *set) { const char *path = set->krb5_keytab; if (*path != '\0') { /* environment may be used by Kerberos 5 library directly */ env_put(t_strconcat("KRB5_KTNAME=", path, NULL)); #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY gsskrb5_register_acceptor_identity(path); #elif defined (HAVE_KRB5_GSS_REGISTER_ACCEPTOR_IDENTITY) krb5_gss_register_acceptor_identity(path); #endif } } static struct auth_request *mech_gssapi_auth_new(void) { struct gssapi_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"gssapi_auth_request", 2048); request = p_new(pool, struct gssapi_auth_request, 1); request->pool = pool; request->gss_ctx = GSS_C_NO_CONTEXT; request->auth_request.pool = pool; return &request->auth_request; } static OM_uint32 obtain_service_credentials(struct auth_request *request, gss_cred_id_t *ret_r) { OM_uint32 major_status, minor_status; string_t *principal_name; gss_buffer_desc inbuf; gss_name_t gss_principal; const char *service_name; if (!gssapi_initialized) { gssapi_initialized = TRUE; mech_gssapi_initialize(request->set); } if (strcmp(request->set->gssapi_hostname, "$ALL") == 0) { auth_request_log_debug(request, AUTH_SUBSYS_MECH, "Using all keytab entries"); *ret_r = GSS_C_NO_CREDENTIAL; return GSS_S_COMPLETE; } if (strcasecmp(request->service, "POP3") == 0) { /* The standard POP3 service name with GSSAPI is called just "pop". */ service_name = "pop"; } else { service_name = t_str_lcase(request->service); } principal_name = t_str_new(128); str_append(principal_name, service_name); str_append_c(principal_name, '@'); str_append(principal_name, request->set->gssapi_hostname); auth_request_log_debug(request, AUTH_SUBSYS_MECH, "Obtaining credentials for %s", str_c(principal_name)); inbuf.length = str_len(principal_name); inbuf.value = str_c_modifiable(principal_name); major_status = gss_import_name(&minor_status, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_principal); str_free(&principal_name); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(request, major_status, GSS_C_GSS_CODE, "importing principal name"); return major_status; } major_status = gss_acquire_cred(&minor_status, gss_principal, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, ret_r, NULL, NULL); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(request, major_status, GSS_C_GSS_CODE, "acquiring service credentials"); mech_gssapi_log_error(request, minor_status, GSS_C_MECH_CODE, "acquiring service credentials"); return major_status; } gss_release_name(&minor_status, &gss_principal); return major_status; } static gss_name_t import_name(struct auth_request *request, void *str, size_t len) { OM_uint32 major_status, minor_status; gss_buffer_desc name_buf; gss_name_t name; name_buf.value = str; name_buf.length = len; major_status = gss_import_name(&minor_status, &name_buf, GSS_C_NO_OID, &name); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(request, major_status, GSS_C_GSS_CODE, "gss_import_name"); return GSS_C_NO_NAME; } return name; } static gss_name_t duplicate_name(struct auth_request *request, gss_name_t old) { OM_uint32 major_status, minor_status; gss_name_t new; major_status = gss_duplicate_name(&minor_status, old, &new); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(request, major_status, GSS_C_GSS_CODE, "gss_duplicate_name"); return GSS_C_NO_NAME; } return new; } static bool data_has_nuls(const void *data, size_t len) { const unsigned char *c = data; size_t i; /* apparently all names end with NUL? */ if (len > 0 && c[len-1] == '\0') len--; for (i = 0; i < len; i++) { if (c[i] == '\0') return TRUE; } return FALSE; } static int get_display_name(struct auth_request *auth_request, gss_name_t name, gss_OID *name_type_r, const char **display_name_r) { OM_uint32 major_status, minor_status; gss_buffer_desc buf; major_status = gss_display_name(&minor_status, name, &buf, name_type_r); if (major_status != GSS_S_COMPLETE) { mech_gssapi_log_error(auth_request, major_status, GSS_C_GSS_CODE, "gss_display_name"); return -1; } if (data_has_nuls(buf.value, buf.length)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "authn_name has NULs"); return -1; } *display_name_r = t_strndup(buf.value, buf.length); (void)gss_release_buffer(&minor_status, &buf); return 0; } static bool mech_gssapi_oid_cmp(const gss_OID_desc *oid1, const gss_OID_desc *oid2) { return oid1->length == oid2->length && mem_equals_timing_safe(oid1->elements, oid2->elements, oid1->length); } static int mech_gssapi_sec_context(struct gssapi_auth_request *request, gss_buffer_desc inbuf) { struct auth_request *auth_request = &request->auth_request; OM_uint32 major_status, minor_status; gss_buffer_desc output_token; gss_OID name_type; gss_OID mech_type; const char *username, *error; int ret = 0; major_status = gss_accept_sec_context ( &minor_status, &request->gss_ctx, request->service_cred, &inbuf, GSS_C_NO_CHANNEL_BINDINGS, &request->authn_name, &mech_type, &output_token, NULL, /* ret_flags */ NULL, /* time_rec */ NULL /* delegated_cred_handle */ ); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(auth_request, major_status, GSS_C_GSS_CODE, "processing incoming data"); mech_gssapi_log_error(auth_request, minor_status, GSS_C_MECH_CODE, "processing incoming data"); return -1; } switch (major_status) { case GSS_S_COMPLETE: if (!mech_gssapi_oid_cmp(mech_type, &mech_gssapi_krb5_oid)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "GSSAPI mechanism not Kerberos5"); ret = -1; } else if (get_display_name(auth_request, request->authn_name, &name_type, &username) < 0) ret = -1; else if (!auth_request_set_username(auth_request, username, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "authn_name: %s", error); ret = -1; } else { request->sasl_gssapi_state = GSS_STATE_WRAP; auth_request_log_debug(auth_request, AUTH_SUBSYS_MECH, "security context state completed."); } break; case GSS_S_CONTINUE_NEEDED: auth_request_log_debug(auth_request, AUTH_SUBSYS_MECH, "Processed incoming packet correctly, " "waiting for another."); break; default: auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "Received unexpected major status %d", major_status); break; } if (ret == 0) { if (output_token.length > 0) { auth_request_handler_reply_continue(auth_request, output_token.value, output_token.length); } else { /* If there is no output token, go straight to wrap, which is expecting an empty input token. */ ret = mech_gssapi_wrap(request, output_token); } } (void)gss_release_buffer(&minor_status, &output_token); return ret; } static int mech_gssapi_wrap(struct gssapi_auth_request *request, gss_buffer_desc inbuf) { OM_uint32 major_status, minor_status; gss_buffer_desc outbuf; unsigned char ret[4]; /* The client's return data should be empty here */ /* Only authentication, no integrity or confidentiality protection (yet?) */ ret[0] = (SASL_GSSAPI_QOP_UNSPECIFIED | SASL_GSSAPI_QOP_AUTH_ONLY); ret[1] = 0xFF; ret[2] = 0xFF; ret[3] = 0xFF; inbuf.length = 4; inbuf.value = ret; major_status = gss_wrap(&minor_status, request->gss_ctx, 0, GSS_C_QOP_DEFAULT, &inbuf, NULL, &outbuf); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(&request->auth_request, major_status, GSS_C_GSS_CODE, "sending security layer negotiation"); mech_gssapi_log_error(&request->auth_request, minor_status, GSS_C_MECH_CODE, "sending security layer negotiation"); return -1; } auth_request_log_debug(&request->auth_request, AUTH_SUBSYS_MECH, "Negotiated security layer"); auth_request_handler_reply_continue(&request->auth_request, outbuf.value, outbuf.length); (void)gss_release_buffer(&minor_status, &outbuf); request->sasl_gssapi_state = GSS_STATE_UNWRAP; return 0; } #ifdef USE_KRB5_USEROK static bool k5_principal_is_authorized(struct auth_request *request, const char *name) { const char *value, *const *authorized_names, *const *tmp; value = auth_fields_find(request->extra_fields, "k5principals"); if (value == NULL) return FALSE; authorized_names = t_strsplit_spaces(value, ","); for (tmp = authorized_names; *tmp != NULL; tmp++) { if (strcmp(*tmp, name) == 0) { auth_request_log_debug(request, AUTH_SUBSYS_MECH, "authorized by k5principals field: %s", name); return TRUE; } } return FALSE; } static bool mech_gssapi_krb5_userok(struct gssapi_auth_request *request, gss_name_t name, const char *login_user, bool check_name_type) { krb5_context ctx; krb5_principal princ; krb5_error_code krb5_err; gss_OID name_type; const char *princ_display_name; bool authorized = FALSE; /* Parse out the principal's username */ if (get_display_name(&request->auth_request, name, &name_type, &princ_display_name) < 0) return FALSE; if (!mech_gssapi_oid_cmp(name_type, GSS_KRB5_NT_PRINCIPAL_NAME) && check_name_type) { auth_request_log_info(&request->auth_request, AUTH_SUBSYS_MECH, "OID not kerberos principal name"); return FALSE; } /* Init a krb5 context and parse the principal username */ krb5_err = krb5_init_context(&ctx); if (krb5_err != 0) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "krb5_init_context() failed: %d", (int)krb5_err); return FALSE; } krb5_err = krb5_parse_name(ctx, princ_display_name, &princ); if (krb5_err != 0) { /* writing the error string would be better, but we probably rarely get here and there doesn't seem to be a standard way of getting it */ auth_request_log_info(&request->auth_request, AUTH_SUBSYS_MECH, "krb5_parse_name() failed: %d", (int)krb5_err); } else { /* See if the principal is in the list of authorized * principals for the user */ authorized = k5_principal_is_authorized(&request->auth_request, princ_display_name); /* See if the principal is authorized to act as the specified (UNIX) user */ if (!authorized) authorized = krb5_kuserok(ctx, princ, login_user); krb5_free_principal(ctx, princ); } krb5_free_context(ctx); return authorized; } #endif static int mech_gssapi_userok(struct gssapi_auth_request *request, const char *login_user) { struct auth_request *auth_request = &request->auth_request; OM_uint32 major_status, minor_status; int equal_authn_authz; #ifdef HAVE___GSS_USEROK int login_ok; #endif /* if authn and authz names equal, don't bother checking further. */ major_status = gss_compare_name(&minor_status, request->authn_name, request->authz_name, &equal_authn_authz); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(auth_request, major_status, GSS_C_GSS_CODE, "gss_compare_name failed"); return -1; } if (equal_authn_authz != 0) return 0; /* handle cross-realm authentication */ #ifdef HAVE___GSS_USEROK /* Solaris */ major_status = __gss_userok(&minor_status, request->authn_name, login_user, &login_ok); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(auth_request, major_status, GSS_C_GSS_CODE, "__gss_userok failed"); return -1; } if (login_ok == 0) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "User not authorized to log in as %s", login_user); return -1; } return 0; #elif defined(USE_KRB5_USEROK) if (!mech_gssapi_krb5_userok(request, request->authn_name, login_user, TRUE)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "User not authorized to log in as %s", login_user); return -1; } return 0; #else auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "Cross-realm authentication not supported " "(authn_name=%s, authz_name=%s)", request->auth_request.original_username, login_user); return -1; #endif } static void gssapi_credentials_callback(enum passdb_result result, const unsigned char *credentials ATTR_UNUSED, size_t size ATTR_UNUSED, struct auth_request *request) { struct gssapi_auth_request *gssapi_request = (struct gssapi_auth_request *)request; /* We don't care much whether the lookup succeeded or not because GSSAPI * does not strictly require a passdb. But if a passdb is configured, * now the k5principals field will have been filled in. */ switch (result) { case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(request); return; case PASSDB_RESULT_USER_DISABLED: case PASSDB_RESULT_PASS_EXPIRED: /* user is explicitly disabled, don't allow it to log in */ auth_request_fail(request); return; case PASSDB_RESULT_NEXT: case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_PASSWORD_MISMATCH: case PASSDB_RESULT_OK: break; } if (mech_gssapi_userok(gssapi_request, request->user) == 0) auth_request_success(request, NULL, 0); else auth_request_fail(request); } static int mech_gssapi_unwrap(struct gssapi_auth_request *request, gss_buffer_desc inbuf) { struct auth_request *auth_request = &request->auth_request; OM_uint32 major_status, minor_status; gss_buffer_desc outbuf; const char *login_user, *error; unsigned char *name; size_t name_len; major_status = gss_unwrap(&minor_status, request->gss_ctx, &inbuf, &outbuf, NULL, NULL); if (GSS_ERROR(major_status)) { mech_gssapi_log_error(auth_request, major_status, GSS_C_GSS_CODE, "final negotiation: gss_unwrap"); return -1; } /* outbuf[0] contains bitmask for selected security layer, outbuf[1..3] contains maximum output_message size */ if (outbuf.length < 4) { auth_request_log_error(auth_request, AUTH_SUBSYS_MECH, "Invalid response length"); return -1; } if (outbuf.length > 4) { name = (unsigned char *)outbuf.value + 4; name_len = outbuf.length - 4; if (data_has_nuls(name, name_len)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "authz_name has NULs"); return -1; } login_user = p_strndup(auth_request->pool, name, name_len); request->authz_name = import_name(auth_request, name, name_len); } else { request->authz_name = duplicate_name(auth_request, request->authn_name); if (get_display_name(auth_request, request->authz_name, NULL, &login_user) < 0) return -1; } if (request->authz_name == GSS_C_NO_NAME) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "no authz_name"); return -1; } /* Set username early, so that the credential lookup is for the * authorizing user. This means the username in subsequent log * messagess will be the authorization name, not the authentication * name, which may mean that future log messages should be adjusted * to log the right thing. */ if (!auth_request_set_username(auth_request, login_user, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "authz_name: %s", error); return -1; } /* Continue in callback once auth_request is populated with passdb information. */ auth_request->passdb_success = TRUE; /* default to success */ auth_request_lookup_credentials(&request->auth_request, "", gssapi_credentials_callback); return 0; } static void mech_gssapi_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { struct gssapi_auth_request *gssapi_request = (struct gssapi_auth_request *)request; gss_buffer_desc inbuf; int ret = -1; inbuf.value = (void *)data; inbuf.length = data_size; switch (gssapi_request->sasl_gssapi_state) { case GSS_STATE_SEC_CONTEXT: ret = mech_gssapi_sec_context(gssapi_request, inbuf); break; case GSS_STATE_WRAP: ret = mech_gssapi_wrap(gssapi_request, inbuf); break; case GSS_STATE_UNWRAP: ret = mech_gssapi_unwrap(gssapi_request, inbuf); break; default: i_unreached(); } if (ret < 0) auth_request_fail(request); } static void mech_gssapi_auth_initial(struct auth_request *request, const unsigned char *data, size_t data_size) { struct gssapi_auth_request *gssapi_request = (struct gssapi_auth_request *)request; OM_uint32 major_status; major_status = obtain_service_credentials(request, &gssapi_request->service_cred); if (GSS_ERROR(major_status)) { auth_request_internal_failure(request); return; } gssapi_request->authn_name = GSS_C_NO_NAME; gssapi_request->authz_name = GSS_C_NO_NAME; gssapi_request->sasl_gssapi_state = GSS_STATE_SEC_CONTEXT; if (data_size == 0) { /* The client should go first */ auth_request_handler_reply_continue(request, NULL, 0); } else { mech_gssapi_auth_continue(request, data, data_size); } } static void mech_gssapi_auth_free(struct auth_request *request) { struct gssapi_auth_request *gssapi_request = (struct gssapi_auth_request *)request; OM_uint32 minor_status; if (gssapi_request->gss_ctx != GSS_C_NO_CONTEXT) { (void)gss_delete_sec_context(&minor_status, &gssapi_request->gss_ctx, GSS_C_NO_BUFFER); } (void)gss_release_cred(&minor_status, &gssapi_request->service_cred); if (gssapi_request->authn_name != GSS_C_NO_NAME) { (void)gss_release_name(&minor_status, &gssapi_request->authn_name); } if (gssapi_request->authz_name != GSS_C_NO_NAME) { (void)gss_release_name(&minor_status, &gssapi_request->authz_name); } pool_unref(&request->pool); } const struct mech_module mech_gssapi = { "GSSAPI", .flags = 0, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_gssapi_auth_new, mech_gssapi_auth_initial, mech_gssapi_auth_continue, mech_gssapi_auth_free }; /* MTI Kerberos v1.5+ and Heimdal v0.7+ supports SPNEGO for Kerberos tickets internally. Nothing else needs to be done here. Note however that this does not support SPNEGO when the only available credential is NTLM.. */ const struct mech_module mech_gssapi_spnego = { "GSS-SPNEGO", .flags = 0, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_gssapi_auth_new, mech_gssapi_auth_initial, mech_gssapi_auth_continue, mech_gssapi_auth_free }; #ifndef BUILTIN_GSSAPI void mech_gssapi_init(void); void mech_gssapi_deinit(void); void mech_gssapi_init(void) { mech_register_module(&mech_gssapi); #ifdef HAVE_GSSAPI_SPNEGO /* load if we already didn't load it using winbind */ if (mech_module_find(mech_gssapi_spnego.mech_name) == NULL) mech_register_module(&mech_gssapi_spnego); #endif } void mech_gssapi_deinit(void) { #ifdef HAVE_GSSAPI_SPNEGO const struct mech_module *mech; mech = mech_module_find(mech_gssapi_spnego.mech_name); if (mech != NULL && mech->auth_new == mech_gssapi_auth_new) mech_unregister_module(&mech_gssapi_spnego); #endif mech_unregister_module(&mech_gssapi); } #endif #endif dovecot-2.2.33.2/src/auth/mech-digest-md5.c0000644000175000017500000003455213165463624015120 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* Digest-MD5 SASL authentication, see RFC-2831 */ #include "auth-common.h" #include "base64.h" #include "buffer.h" #include "hex-binary.h" #include "md5.h" #include "randgen.h" #include "str.h" #include "str-sanitize.h" #include "mech.h" #include "passdb.h" #define MAX_REALM_LEN 64 /* Linear whitespace */ #define IS_LWS(c) ((c) == ' ' || (c) == '\t') enum qop_option { QOP_AUTH = 0x01, /* authenticate */ QOP_AUTH_INT = 0x02, /* + integrity protection, not supported yet */ QOP_AUTH_CONF = 0x04, /* + encryption, not supported yet */ QOP_COUNT = 3 }; static const char *qop_names[] = { "auth", "auth-int", "auth-conf" }; struct digest_auth_request { struct auth_request auth_request; pool_t pool; /* requested: */ char *nonce; enum qop_option qop; /* received: */ char *username; char *cnonce; char *nonce_count; char *qop_value; char *digest_uri; /* may be NULL */ char *authzid; /* may be NULL, authorization ID */ unsigned char response[32]; unsigned long maxbuf; unsigned int nonce_found:1; /* final reply: */ char *rspauth; }; static string_t *get_digest_challenge(struct digest_auth_request *request) { const struct auth_settings *set = request->auth_request.set; buffer_t buf; string_t *str; const char *const *tmp; unsigned char nonce[16]; unsigned char nonce_base64[MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1]; int i; bool first_qop; /* realm="hostname" (multiple allowed) nonce="randomized data, at least 64bit" qop="auth,auth-int,auth-conf" maxbuf=number (with auth-int, auth-conf, defaults to 64k) charset="utf-8" (iso-8859-1 if it doesn't exist) algorithm="md5-sess" cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf) */ /* get 128bit of random data as nonce */ random_fill(nonce, sizeof(nonce)); buffer_create_from_data(&buf, nonce_base64, sizeof(nonce_base64)); base64_encode(nonce, sizeof(nonce), &buf); buffer_append_c(&buf, '\0'); request->nonce = p_strdup(request->pool, buf.data); str = t_str_new(256); if (*set->realms_arr == NULL) { /* If no realms are given, at least Cyrus SASL client defaults to destination host name */ str_append(str, "realm=\"\","); } else { for (tmp = set->realms_arr; *tmp != NULL; tmp++) str_printfa(str, "realm=\"%s\",", *tmp); } str_printfa(str, "nonce=\"%s\",", request->nonce); str_append(str, "qop=\""); first_qop = TRUE; for (i = 0; i < QOP_COUNT; i++) { if (request->qop & (1 << i)) { if (first_qop) first_qop = FALSE; else str_append_c(str, ','); str_append(str, qop_names[i]); } } str_append(str, "\","); str_append(str, "charset=\"utf-8\"," "algorithm=\"md5-sess\""); return str; } static bool verify_credentials(struct digest_auth_request *request, const unsigned char *credentials, size_t size) { struct md5_context ctx; unsigned char digest[MD5_RESULTLEN]; const char *a1_hex, *a2_hex, *response_hex; int i; /* get the MD5 password */ if (size != MD5_RESULTLEN) { auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH, "invalid credentials length"); return FALSE; } /* response = HEX( KD ( HEX(H(A1)), { nonce-value, ":" nc-value, ":", cnonce-value, ":", qop-value, ":", HEX(H(A2)) })) and if authzid is not empty: A1 = { H( { username-value, ":", realm-value, ":", passwd } ), ":", nonce-value, ":", cnonce-value, ":", authzid } else: A1 = { H( { username-value, ":", realm-value, ":", passwd } ), ":", nonce-value, ":", cnonce-value } If the "qop" directive's value is "auth", then A2 is: A2 = { "AUTHENTICATE:", digest-uri-value } If the "qop" value is "auth-int" or "auth-conf" then A2 is: A2 = { "AUTHENTICATE:", digest-uri-value, ":00000000000000000000000000000000" } */ /* A1 */ md5_init(&ctx); md5_update(&ctx, credentials, size); md5_update(&ctx, ":", 1); md5_update(&ctx, request->nonce, strlen(request->nonce)); md5_update(&ctx, ":", 1); md5_update(&ctx, request->cnonce, strlen(request->cnonce)); if (request->authzid != NULL) { md5_update(&ctx, ":", 1); md5_update(&ctx, request->authzid, strlen(request->authzid)); } md5_final(&ctx, digest); a1_hex = binary_to_hex(digest, 16); /* do it twice, first verify the user's response, the second is sent for client as a reply */ for (i = 0; i < 2; i++) { /* A2 */ md5_init(&ctx); if (i == 0) md5_update(&ctx, "AUTHENTICATE:", 13); else md5_update(&ctx, ":", 1); if (request->digest_uri != NULL) { md5_update(&ctx, request->digest_uri, strlen(request->digest_uri)); } if (request->qop == QOP_AUTH_INT || request->qop == QOP_AUTH_CONF) { md5_update(&ctx, ":00000000000000000000000000000000", 33); } md5_final(&ctx, digest); a2_hex = binary_to_hex(digest, 16); /* response */ md5_init(&ctx); md5_update(&ctx, a1_hex, 32); md5_update(&ctx, ":", 1); md5_update(&ctx, request->nonce, strlen(request->nonce)); md5_update(&ctx, ":", 1); md5_update(&ctx, request->nonce_count, strlen(request->nonce_count)); md5_update(&ctx, ":", 1); md5_update(&ctx, request->cnonce, strlen(request->cnonce)); md5_update(&ctx, ":", 1); md5_update(&ctx, request->qop_value, strlen(request->qop_value)); md5_update(&ctx, ":", 1); md5_update(&ctx, a2_hex, 32); md5_final(&ctx, digest); response_hex = binary_to_hex(digest, 16); if (i == 0) { /* verify response */ if (!mem_equals_timing_safe(response_hex, request->response, 32)) { auth_request_log_info(&request->auth_request, AUTH_SUBSYS_MECH, "password mismatch"); return FALSE; } } else { request->rspauth = p_strconcat(request->pool, "rspauth=", response_hex, NULL); } } return TRUE; } static bool parse_next(char **data, char **key, char **value) { /* @UNSAFE */ char *p, *dest; p = *data; while (IS_LWS(*p)) p++; /* get key */ *key = p; while (*p != '\0' && *p != '=' && *p != ',') p++; if (*p != '=') { *data = p; return FALSE; } *value = p+1; /* skip trailing whitespace in key */ while (p > *data && IS_LWS(p[-1])) p--; *p = '\0'; /* get value */ p = *value; while (IS_LWS(*p)) p++; if (*p != '"') { while (*p != '\0' && *p != ',') p++; *data = p+1; while (IS_LWS(p[-1])) p--; *p = '\0'; } else { /* quoted string */ *value = dest = ++p; while (*p != '\0' && *p != '"') { if (*p == '\\' && p[1] != '\0') p++; *dest++ = *p++; } *data = *p == '"' ? p+1 : p; *dest = '\0'; } return TRUE; } static bool auth_handle_response(struct digest_auth_request *request, char *key, char *value, const char **error) { unsigned int i; (void)str_lcase(key); if (strcmp(key, "realm") == 0) { if (request->auth_request.realm == NULL && *value != '\0') request->auth_request.realm = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "username") == 0) { if (request->username != NULL) { *error = "username must not exist more than once"; return FALSE; } if (*value == '\0') { *error = "empty username"; return FALSE; } request->username = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "nonce") == 0) { /* nonce must be same */ if (strcmp(value, request->nonce) != 0) { *error = "Invalid nonce"; return FALSE; } request->nonce_found = TRUE; return TRUE; } if (strcmp(key, "cnonce") == 0) { if (request->cnonce != NULL) { *error = "cnonce must not exist more than once"; return FALSE; } if (*value == '\0') { *error = "cnonce can't contain empty value"; return FALSE; } request->cnonce = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "nc") == 0) { unsigned int nc; if (request->nonce_count != NULL) { *error = "nonce-count must not exist more than once"; return FALSE; } if (str_to_uint(value, &nc) < 0) { *error = "nonce-count value invalid"; return FALSE; } if (nc != 1) { *error = "re-auth not supported currently"; return FALSE; } request->nonce_count = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "qop") == 0) { for (i = 0; i < QOP_COUNT; i++) { if (strcasecmp(qop_names[i], value) == 0) break; } if (i == QOP_COUNT) { *error = t_strdup_printf("Unknown QoP value: %s", str_sanitize(value, 32)); return FALSE; } request->qop &= (1 << i); if (request->qop == 0) { *error = "Nonallowed QoP requested"; return FALSE; } request->qop_value = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "digest-uri") == 0) { /* type / host / serv-name */ const char *const *uri = t_strsplit(value, "/"); if (uri[0] == NULL || uri[1] == NULL) { *error = "Invalid digest-uri"; return FALSE; } /* FIXME: RFC recommends that we verify the host/serv-type. But isn't the realm enough already? That'd be just extra configuration.. Maybe optionally list valid hosts in config file? */ request->digest_uri = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "maxbuf") == 0) { if (request->maxbuf != 0) { *error = "maxbuf must not exist more than once"; return FALSE; } if (str_to_ulong(value, &request->maxbuf) < 0 || request->maxbuf == 0) { *error = "Invalid maxbuf value"; return FALSE; } return TRUE; } if (strcmp(key, "charset") == 0) { if (strcasecmp(value, "utf-8") != 0) { *error = "Only utf-8 charset is allowed"; return FALSE; } return TRUE; } if (strcmp(key, "response") == 0) { if (strlen(value) != 32) { *error = "Invalid response value"; return FALSE; } memcpy(request->response, value, 32); return TRUE; } if (strcmp(key, "cipher") == 0) { /* not supported, ignore */ return TRUE; } if (strcmp(key, "authzid") == 0) { if (request->authzid != NULL) { *error = "authzid must not exist more than once"; return FALSE; } if (*value == '\0') { *error = "empty authzid"; return FALSE; } request->authzid = p_strdup(request->pool, value); return TRUE; } /* unknown key, ignore */ return TRUE; } static bool parse_digest_response(struct digest_auth_request *request, const unsigned char *data, size_t size, const char **error) { char *copy, *key, *value; bool failed; /* realm="realm" username="username" nonce="randomized data" cnonce="??" nc=00000001 qop="auth|auth-int|auth-conf" digest-uri="serv-type/host[/serv-name]" response=32 HEX digits maxbuf=number (with auth-int, auth-conf, defaults to 64k) charset="utf-8" (iso-8859-1 if it doesn't exist) cipher="cipher-value" authzid="authzid-value" */ *error = NULL; failed = FALSE; if (size == 0) { *error = "Client sent no input"; return FALSE; } /* treating response as NUL-terminated string also gets rid of all potential problems with NUL characters in strings. */ copy = t_strdup_noconst(t_strndup(data, size)); while (*copy != '\0') { if (parse_next(©, &key, &value)) { if (!auth_handle_response(request, key, value, error)) { failed = TRUE; break; } } if (*copy == ',') copy++; } if (!failed) { if (!request->nonce_found) { *error = "Missing nonce parameter"; failed = TRUE; } else if (request->cnonce == NULL) { *error = "Missing cnonce parameter"; failed = TRUE; } else if (request->username == NULL) { *error = "Missing username parameter"; failed = TRUE; } } if (request->nonce_count == NULL) request->nonce_count = p_strdup(request->pool, "00000001"); if (request->qop_value == NULL) request->qop_value = p_strdup(request->pool, "auth"); return !failed; } static void credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct digest_auth_request *request = (struct digest_auth_request *)auth_request; switch (result) { case PASSDB_RESULT_OK: if (!verify_credentials(request, credentials, size)) { auth_request_fail(auth_request); return; } auth_request_success(auth_request, request->rspauth, strlen(request->rspauth)); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void mech_digest_md5_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct digest_auth_request *request = (struct digest_auth_request *)auth_request; const char *username, *error; if (parse_digest_response(request, data, data_size, &error)) { if (auth_request->realm != NULL && strchr(request->username, '@') == NULL) { username = t_strconcat(request->username, "@", auth_request->realm, NULL); auth_request->domain_is_realm = TRUE; } else { username = request->username; } if (auth_request_set_username(auth_request, username, &error) && (request->authzid == NULL || auth_request_set_login_username(auth_request, request->authzid, &error))) { auth_request_lookup_credentials(auth_request, "DIGEST-MD5", credentials_callback); return; } } if (error != NULL) auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); } static void mech_digest_md5_auth_initial(struct auth_request *auth_request, const unsigned char *data ATTR_UNUSED, size_t data_size ATTR_UNUSED) { struct digest_auth_request *request = (struct digest_auth_request *)auth_request; string_t *challenge; /* FIXME: there's no support for subsequent authentication */ challenge = get_digest_challenge(request); auth_request_handler_reply_continue(auth_request, str_data(challenge), str_len(challenge)); } static struct auth_request *mech_digest_md5_auth_new(void) { struct digest_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"digest_md5_auth_request", 2048); request = p_new(pool, struct digest_auth_request, 1); request->pool = pool; request->qop = QOP_AUTH; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_digest_md5 = { "DIGEST-MD5", .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE | MECH_SEC_MUTUAL_AUTH, .passdb_need = MECH_PASSDB_NEED_LOOKUP_CREDENTIALS, mech_digest_md5_auth_new, mech_digest_md5_auth_initial, mech_digest_md5_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/db-sql.h0000644000175000017500000000164513165463624013430 00000000000000#ifndef DB_SQL_H #define DB_SQL_H #include "sql-api.h" struct sql_settings { const char *driver; const char *connect; const char *password_query; const char *user_query; const char *update_query; const char *iterate_query; const char *default_pass_scheme; bool userdb_warning_disable; }; struct sql_connection { struct sql_connection *next; pool_t pool; int refcount; char *config_path; struct sql_settings set; struct sql_db *db; unsigned int default_password_query:1; unsigned int default_user_query:1; unsigned int default_update_query:1; unsigned int default_iterate_query:1; unsigned int userdb_used:1; }; struct sql_connection *db_sql_init(const char *config_path, bool userdb); void db_sql_unref(struct sql_connection **conn); void db_sql_connect(struct sql_connection *conn); void db_sql_success(struct sql_connection *conn); void db_sql_check_userdb_warning(struct sql_connection *conn); #endif dovecot-2.2.33.2/src/auth/userdb-ldap.c0000644000175000017500000002223213165463624014436 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #if defined(USERDB_LDAP) && (defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)) #include "ioloop.h" #include "array.h" #include "str.h" #include "auth-cache.h" #include "db-ldap.h" #include struct ldap_userdb_module { struct userdb_module module; struct ldap_connection *conn; }; struct userdb_ldap_request { struct ldap_request_search request; userdb_callback_t *userdb_callback; unsigned int entries; }; struct userdb_iter_ldap_request { struct ldap_request_search request; struct ldap_userdb_iterate_context *ctx; userdb_callback_t *userdb_callback; }; struct ldap_userdb_iterate_context { struct userdb_iterate_context ctx; struct userdb_iter_ldap_request request; pool_t pool; struct ldap_connection *conn; bool continued, in_callback; }; static void ldap_query_get_result(struct ldap_connection *conn, struct auth_request *auth_request, struct ldap_request_search *ldap_request, LDAPMessage *res) { struct db_ldap_result_iterate_context *ldap_iter; const char *name, *const *values; ldap_iter = db_ldap_result_iterate_init(conn, ldap_request, res, TRUE); while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) { auth_request_set_userdb_field_values(auth_request, name, values); } db_ldap_result_iterate_deinit(&ldap_iter); } static void userdb_ldap_lookup_finish(struct auth_request *auth_request, struct userdb_ldap_request *urequest, LDAPMessage *res) { enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; if (res == NULL) { result = USERDB_RESULT_INTERNAL_FAILURE; } else if (urequest->entries == 0) { result = USERDB_RESULT_USER_UNKNOWN; auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); } else if (urequest->entries > 1) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "user_filter matched multiple objects, aborting"); result = USERDB_RESULT_INTERNAL_FAILURE; } else { result = USERDB_RESULT_OK; } urequest->userdb_callback(result, auth_request); } static void userdb_ldap_lookup_callback(struct ldap_connection *conn, struct ldap_request *request, LDAPMessage *res) { struct userdb_ldap_request *urequest = (struct userdb_ldap_request *) request; struct auth_request *auth_request = urequest->request.request.auth_request; if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { userdb_ldap_lookup_finish(auth_request, urequest, res); auth_request_unref(&auth_request); return; } if (urequest->entries++ == 0) { /* first entry */ ldap_query_get_result(conn, auth_request, &urequest->request, res); } } static void userdb_ldap_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct ldap_userdb_module *module = (struct ldap_userdb_module *)_module; struct ldap_connection *conn = module->conn; const char **attr_names = (const char **)conn->user_attr_names; struct userdb_ldap_request *request; string_t *str; auth_request_ref(auth_request); request = p_new(auth_request->pool, struct userdb_ldap_request, 1); request->userdb_callback = callback; str = t_str_new(512); auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); request->request.base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); auth_request_var_expand(str, conn->set.user_filter, auth_request, ldap_escape); request->request.filter = p_strdup(auth_request->pool, str_c(str)); request->request.attr_map = &conn->user_attr_map; request->request.attributes = conn->user_attr_names; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "user search: " "base=%s scope=%s filter=%s fields=%s", request->request.base, conn->set.scope, request->request.filter, attr_names == NULL ? "(all)" : t_strarray_join(attr_names, ",")); request->request.request.auth_request = auth_request; request->request.request.callback = userdb_ldap_lookup_callback; db_ldap_request(conn, &request->request.request); } static void userdb_ldap_iterate_callback(struct ldap_connection *conn, struct ldap_request *request, LDAPMessage *res) { struct userdb_iter_ldap_request *urequest = (struct userdb_iter_ldap_request *)request; struct ldap_userdb_iterate_context *ctx = urequest->ctx; struct db_ldap_result_iterate_context *ldap_iter; const char *name, *const *values; if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { if (res == NULL) ctx->ctx.failed = TRUE; ctx->ctx.callback(NULL, ctx->ctx.context); return; } /* the iteration can take a while. reset the request's create time so it won't be aborted while it's still running */ request->create_time = ioloop_time; ctx->in_callback = TRUE; ldap_iter = db_ldap_result_iterate_init(conn, &urequest->request, res, TRUE); while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) { if (strcmp(name, "user") != 0) { i_warning("ldap: iterate: " "Ignoring field not named 'user': %s", name); continue; } for (; *values != NULL; values++) { ctx->continued = FALSE; ctx->ctx.callback(*values, ctx->ctx.context); } } db_ldap_result_iterate_deinit(&ldap_iter); if (!ctx->continued) db_ldap_enable_input(conn, FALSE); ctx->in_callback = FALSE; } static struct userdb_iterate_context * userdb_ldap_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct userdb_module *_module = auth_request->userdb->userdb; struct ldap_userdb_module *module = (struct ldap_userdb_module *)_module; struct ldap_connection *conn = module->conn; struct ldap_userdb_iterate_context *ctx; struct userdb_iter_ldap_request *request; const char **attr_names = (const char **)conn->iterate_attr_names; string_t *str; ctx = i_new(struct ldap_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; ctx->conn = conn; request = &ctx->request; request->ctx = ctx; auth_request_ref(auth_request); request->request.request.auth_request = auth_request; str = t_str_new(512); auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); request->request.base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); auth_request_var_expand(str, conn->set.iterate_filter, auth_request, ldap_escape); request->request.filter = p_strdup(auth_request->pool, str_c(str)); request->request.attr_map = &conn->iterate_attr_map; request->request.attributes = conn->iterate_attr_names; request->request.multi_entry = TRUE; if (global_auth_settings->debug) { i_debug("ldap: iterate: base=%s scope=%s filter=%s fields=%s", request->request.base, conn->set.scope, request->request.filter, attr_names == NULL ? "(all)" : t_strarray_join(attr_names, ",")); } request->request.request.callback = userdb_ldap_iterate_callback; db_ldap_request(conn, &request->request.request); return &ctx->ctx; } static void userdb_ldap_iterate_next(struct userdb_iterate_context *_ctx) { struct ldap_userdb_iterate_context *ctx = (struct ldap_userdb_iterate_context *)_ctx; ctx->continued = TRUE; if (!ctx->in_callback) db_ldap_enable_input(ctx->conn, TRUE); } static int userdb_ldap_iterate_deinit(struct userdb_iterate_context *_ctx) { struct ldap_userdb_iterate_context *ctx = (struct ldap_userdb_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; db_ldap_enable_input(ctx->conn, TRUE); auth_request_unref(&ctx->request.request.request.auth_request); i_free(ctx); return ret; } static struct userdb_module * userdb_ldap_preinit(pool_t pool, const char *args) { struct ldap_userdb_module *module; struct ldap_connection *conn; module = p_new(pool, struct ldap_userdb_module, 1); module->conn = conn = db_ldap_init(args, TRUE); p_array_init(&conn->user_attr_map, pool, 16); p_array_init(&conn->iterate_attr_map, pool, 16); db_ldap_set_attrs(conn, conn->set.user_attrs, &conn->user_attr_names, &conn->user_attr_map, NULL); db_ldap_set_attrs(conn, conn->set.iterate_attrs, &conn->iterate_attr_names, &conn->iterate_attr_map, NULL); module->module.blocking = conn->set.blocking; module->module.default_cache_key = auth_cache_parse_key(pool, t_strconcat(conn->set.base, conn->set.user_attrs, conn->set.user_filter, NULL)); return &module->module; } static void userdb_ldap_init(struct userdb_module *_module) { struct ldap_userdb_module *module = (struct ldap_userdb_module *)_module; if (!module->module.blocking || worker) db_ldap_connect_delayed(module->conn); } static void userdb_ldap_deinit(struct userdb_module *_module) { struct ldap_userdb_module *module = (struct ldap_userdb_module *)_module; db_ldap_unref(&module->conn); } #ifndef PLUGIN_BUILD struct userdb_module_interface userdb_ldap = #else struct userdb_module_interface userdb_ldap_plugin = #endif { "ldap", userdb_ldap_preinit, userdb_ldap_init, userdb_ldap_deinit, userdb_ldap_lookup, userdb_ldap_iterate_init, userdb_ldap_iterate_next, userdb_ldap_iterate_deinit }; #else struct userdb_module_interface userdb_ldap = { .name = "ldap" }; #endif dovecot-2.2.33.2/src/auth/mech-apop.c0000644000175000017500000001051313123174404014071 00000000000000/* * APOP (RFC-1460) authentication mechanism. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "auth-common.h" #include "mech.h" #include "passdb.h" #include "md5.h" #include "buffer.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #include #include struct apop_auth_request { struct auth_request auth_request; pool_t pool; /* requested: */ char *challenge; /* received: */ unsigned char response_digest[16]; }; static bool verify_credentials(struct apop_auth_request *request, const unsigned char *credentials, size_t size) { unsigned char digest[16]; struct md5_context ctx; md5_init(&ctx); md5_update(&ctx, request->challenge, strlen(request->challenge)); md5_update(&ctx, credentials, size); md5_final(&ctx, digest); return mem_equals_timing_safe(digest, request->response_digest, 16); } static void apop_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *auth_request) { struct apop_auth_request *request = (struct apop_auth_request *)auth_request; switch (result) { case PASSDB_RESULT_OK: if (verify_credentials(request, credentials, size)) auth_request_success(auth_request, "", 0); else auth_request_fail(auth_request); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); break; default: auth_request_fail(auth_request); break; } } static void mech_apop_auth_initial(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { struct apop_auth_request *request = (struct apop_auth_request *)auth_request; const unsigned char *tmp, *end, *username = NULL; unsigned long pid, connect_uid, timestamp; const char *error; /* pop3-login handles sending the challenge and getting the response. Our input here is: \0 \0 */ if (data_size == 0) { /* Should never happen */ auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "no initial respone"); auth_request_fail(auth_request); return; } tmp = data; end = data + data_size; /* get the challenge */ while (tmp != end && *tmp != '\0') tmp++; request->challenge = p_strdup_until(request->pool, data, tmp); if (tmp != end) { /* get the username */ username = ++tmp; while (tmp != end && *tmp != '\0') tmp++; } else { /* should never happen */ auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "malformed data"); auth_request_fail(auth_request); return; } if (tmp + 1 + 16 != end) { /* Should never happen */ auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "malformed data"); auth_request_fail(auth_request); return; } memcpy(request->response_digest, tmp + 1, sizeof(request->response_digest)); /* the challenge must begin with trusted unique ID. we trust only ourself, so make sure it matches our connection specific UID which we told to client in handshake. Also require a timestamp which is later than this process's start time. */ if (sscanf(request->challenge, "<%lx.%lx.%lx.", &pid, &connect_uid, ×tamp) != 3 || connect_uid != auth_request->connect_uid || pid != (unsigned long)getpid() || (time_t)timestamp < process_start_time) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "invalid challenge"); auth_request_fail(auth_request); return; } if (!auth_request_set_username(auth_request, (const char *)username, &error)) { auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(auth_request); return; } auth_request_lookup_credentials(auth_request, "PLAIN", apop_credentials_callback); } static struct auth_request *mech_apop_auth_new(void) { struct apop_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"apop_auth_request", 2048); request = p_new(pool, struct apop_auth_request, 1); request->pool = pool; request->auth_request.pool = pool; return &request->auth_request; } const struct mech_module mech_apop = { "APOP", .flags = MECH_SEC_PRIVATE | MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, .passdb_need = MECH_PASSDB_NEED_VERIFY_RESPONSE, mech_apop_auth_new, mech_apop_auth_initial, NULL, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/passdb-sia.c0000644000175000017500000000255213123174404014252 00000000000000/* Copyright (C) 2006 Simon L Jackson */ /* Tru64 SIA support */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_SIA #include "safe-memset.h" #include #include #include static int checkpw_collect(int timeout ATTR_UNUSED, int rendition, uchar_t *title ATTR_UNUSED, int nprompts ATTR_UNUSED, prompt_t *prompts ATTR_UNUSED) { switch (rendition) { case SIAONELINER: case SIAINFO: case SIAWARNING: return SIACOLSUCCESS; } /* everything else is bogus */ return SIACOLABORT; } static void local_sia_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { char *argutility = "dovecot"; auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); /* check if the password is valid */ if (sia_validate_user(checkpw_collect, 1, &argutility, NULL, (char *)request->user, NULL, NULL, NULL, (char *)password) != SIASUCCESS) { auth_request_log_password_mismatch(request, AUTH_SUBSYS_DB); callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); } else { callback(PASSDB_RESULT_OK, request); } } struct passdb_module_interface passdb_sia = { "sia", NULL, NULL, NULL, local_sia_verify_plain, NULL, NULL }; #else struct passdb_module_interface passdb_sia = { .name = "sia" }; #endif dovecot-2.2.33.2/src/auth/userdb-sql.c0000644000175000017500000001746713165463624014333 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #ifdef USERDB_SQL #include "auth-cache.h" #include "db-sql.h" #include struct sql_userdb_module { struct userdb_module module; struct sql_connection *conn; }; struct userdb_sql_request { struct auth_request *auth_request; userdb_callback_t *callback; }; struct sql_userdb_iterate_context { struct userdb_iterate_context ctx; struct sql_result *result; unsigned int freed:1; unsigned int call_iter:1; }; static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx); static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx); static void sql_query_get_result(struct sql_result *result, struct auth_request *auth_request) { const char *name, *value; unsigned int i, fields_count; fields_count = sql_result_get_fields_count(result); for (i = 0; i < fields_count; i++) { name = sql_result_get_field_name(result, i); value = sql_result_get_field_value(result, i); if (*name != '\0' && value != NULL) { auth_request_set_userdb_field(auth_request, name, value); } } } static void sql_query_callback(struct sql_result *sql_result, struct userdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = (struct sql_userdb_module *)_module; enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; int ret; ret = sql_result_next_row(sql_result); if (ret >= 0) db_sql_success(module->conn); if (ret < 0) { if (!module->conn->default_user_query) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "User query failed: %s", sql_result_get_error(sql_result)); } else { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "User query failed: %s " "(using built-in default user_query: %s)", sql_result_get_error(sql_result), module->conn->set.user_query); } } else if (ret == 0) { result = USERDB_RESULT_USER_UNKNOWN; auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); } else { sql_query_get_result(sql_result, auth_request); result = USERDB_RESULT_OK; } sql_request->callback(result, auth_request); auth_request_unref(&auth_request); i_free(sql_request); } static const char * userdb_sql_escape(const char *str, const struct auth_request *auth_request) { struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = (struct sql_userdb_module *)_module; return sql_escape_string(module->conn->db, str); } static void userdb_sql_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = (struct sql_userdb_module *)_module; struct userdb_sql_request *sql_request; const char *query; query = t_auth_request_var_expand(module->conn->set.user_query, auth_request, userdb_sql_escape); auth_request_ref(auth_request); sql_request = i_new(struct userdb_sql_request, 1); sql_request->callback = callback; sql_request->auth_request = auth_request; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "%s", query); sql_query(module->conn->db, query, sql_query_callback, sql_request); } static void sql_iter_query_callback(struct sql_result *sql_result, struct sql_userdb_iterate_context *ctx) { ctx->result = sql_result; sql_result_ref(sql_result); if (ctx->freed) (void)userdb_sql_iterate_deinit(&ctx->ctx); else if (ctx->call_iter) userdb_sql_iterate_next(&ctx->ctx); } static struct userdb_iterate_context * userdb_sql_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = (struct sql_userdb_module *)_module; struct sql_userdb_iterate_context *ctx; const char *query; query = t_auth_request_var_expand(module->conn->set.iterate_query, auth_request, userdb_sql_escape); ctx = i_new(struct sql_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; auth_request_ref(auth_request); sql_query(module->conn->db, query, sql_iter_query_callback, ctx); auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "%s", query); return &ctx->ctx; } static int userdb_sql_iterate_get_user(struct sql_userdb_iterate_context *ctx, const char **user_r) { const char *domain; int idx; /* try user first */ idx = sql_result_find_field(ctx->result, "user"); if (idx == 0) { *user_r = sql_result_get_field_value(ctx->result, idx); return 0; } /* username [+ domain]? */ idx = sql_result_find_field(ctx->result, "username"); if (idx < 0) { /* no user or username, fail */ return -1; } *user_r = sql_result_get_field_value(ctx->result, idx); if (*user_r == NULL) return 0; domain = sql_result_find_field_value(ctx->result, "domain"); if (domain != NULL) *user_r = t_strconcat(*user_r, "@", domain, NULL); return 0; } static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx) { struct sql_userdb_iterate_context *ctx = (struct sql_userdb_iterate_context *)_ctx; struct userdb_module *_module = _ctx->auth_request->userdb->userdb; struct sql_userdb_module *module = (struct sql_userdb_module *)_module; const char *user; int ret; if (ctx->result == NULL) { /* query not finished yet */ ctx->call_iter = TRUE; return; } ret = sql_result_next_row(ctx->result); if (ret >= 0) db_sql_success(module->conn); if (ret > 0) { if (userdb_sql_iterate_get_user(ctx, &user) < 0) i_error("sql: Iterate query didn't return 'user' field"); else if (user == NULL) i_error("sql: Iterate query returned NULL user"); else { _ctx->callback(user, _ctx->context); return; } _ctx->failed = TRUE; } else if (ret < 0) { if (!module->conn->default_iterate_query) { i_error("sql: Iterate query failed: %s", sql_result_get_error(ctx->result)); } else { i_error("sql: Iterate query failed: %s " "(using built-in default iterate_query: %s)", sql_result_get_error(ctx->result), module->conn->set.iterate_query); } _ctx->failed = TRUE; } _ctx->callback(NULL, _ctx->context); } static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx) { struct sql_userdb_iterate_context *ctx = (struct sql_userdb_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; auth_request_unref(&_ctx->auth_request); if (ctx->result == NULL) { /* sql query hasn't finished yet */ ctx->freed = TRUE; } else { if (ctx->result != NULL) sql_result_unref(ctx->result); i_free(ctx); } return ret; } static struct userdb_module * userdb_sql_preinit(pool_t pool, const char *args) { struct sql_userdb_module *module; module = p_new(pool, struct sql_userdb_module, 1); module->conn = db_sql_init(args, TRUE); module->module.default_cache_key = auth_cache_parse_key(pool, module->conn->set.user_query); return &module->module; } static void userdb_sql_init(struct userdb_module *_module) { struct sql_userdb_module *module = (struct sql_userdb_module *)_module; enum sql_db_flags flags; flags = sql_get_flags(module->conn->db); _module->blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; if (!_module->blocking || worker) db_sql_connect(module->conn); } static void userdb_sql_deinit(struct userdb_module *_module) { struct sql_userdb_module *module = (struct sql_userdb_module *)_module; db_sql_unref(&module->conn); } struct userdb_module_interface userdb_sql = { "sql", userdb_sql_preinit, userdb_sql_init, userdb_sql_deinit, userdb_sql_lookup, userdb_sql_iterate_init, userdb_sql_iterate_next, userdb_sql_iterate_deinit }; #else struct userdb_module_interface userdb_sql = { .name = "sql" }; #endif dovecot-2.2.33.2/src/auth/mech-oauth2.c0000644000175000017500000001604313123174404014340 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "safe-memset.h" #include "str.h" #include "mech.h" #include "passdb.h" #include "oauth2.h" #include struct oauth2_auth_request { struct auth_request auth; bool failed; }; /* RFC5801 based unescaping */ static bool oauth2_unescape_username(const char *in, const char **username_r) { string_t *out; out = t_str_new(64); for (; *in != '\0'; in++) { if (in[0] == ',') return FALSE; if (in[0] == '=') { if (in[1] == '2' && in[2] == 'C') str_append_c(out, ','); else if (in[1] == '3' && in[2] == 'D') str_append_c(out, '='); else return FALSE; in += 2; } else { str_append_c(out, *in); } } *username_r = str_c(out); return TRUE; } static void oauth2_verify_callback(enum passdb_result result, const char *error, struct auth_request *request) { struct oauth2_auth_request *oauth2_req = (struct oauth2_auth_request*)request; i_assert(result == PASSDB_RESULT_OK || error != NULL); switch (result) { case PASSDB_RESULT_OK: auth_request_success(request, "", 0); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(request); break; default: /* we could get new token after this */ if (request->mech_password != NULL) request->mech_password = NULL; auth_request_handler_reply_continue(request, error, strlen(error)); oauth2_req->failed = TRUE; break; } } static void xoauth2_verify_callback(enum passdb_result result, struct auth_request *request) { const char *error = "{\"status\":\"401\",\"schemes\":\"bearer\",\"scope\":\"mail\"}"; oauth2_verify_callback(result, error, request); } static void oauthbearer_verify_callback(enum passdb_result result, struct auth_request *request) { const char *error = "{\"status\":\"invalid_token\"}"; oauth2_verify_callback(result, error, request); } /* Input syntax: user=Username^Aauth=Bearer token^A^A */ static void mech_xoauth2_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { struct oauth2_auth_request *oauth2_req = (struct oauth2_auth_request*)request; /* Specification says that client is sent "invalid token" challenge which the client is supposed to ack with empty response */ if (oauth2_req->failed) { auth_request_fail(request); return; } /* split the data from ^A */ bool user_given = FALSE; const char *error; const char *token = NULL; const char *const *ptr; const char *const *fields = t_strsplit(t_strndup(data, data_size), "\x01"); for(ptr = fields; *ptr != NULL; ptr++) { if (strncmp(*ptr,"user=", 5) == 0) { /* xoauth2 does not require unescaping because the data format does not contain anything to escape */ const char *username = (*ptr)+5; if (!auth_request_set_username(request, username, &error)) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); return; } user_given = TRUE; } else if (strncmp(*ptr,"auth=", 5) == 0) { const char *value = (*ptr)+5; if (strncasecmp(value, "bearer ", 7) == 0 && oauth2_valid_token(value+7)) { token = value+7; } else { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid continued data"); auth_request_fail(request); return; } } /* do not fail on unexpected fields */ } if (user_given && token != NULL) auth_request_verify_plain(request, token, xoauth2_verify_callback); else { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Username or token missing"); auth_request_fail(request); } } /* Input syntax for data: gs2flag,a=username,^Afield=...^Afield=...^Aauth=Bearer token^A^A */ static void mech_oauthbearer_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { struct oauth2_auth_request *oauth2_req = (struct oauth2_auth_request*)request; if (oauth2_req->failed) { auth_request_fail(request); return; } bool user_given = FALSE; const char *error; const char *username; const char *const *ptr; /* split the data from ^A */ const char **fields = t_strsplit(t_strndup(data, data_size), "\x01"); const char *token = NULL; /* ensure initial field is OK */ if (*fields == NULL || *(fields[0]) == '\0') { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid continued data"); auth_request_fail(request); return; } /* the first field is specified by RFC5801 as gs2-header */ for(ptr = t_strsplit_spaces(fields[0], ","); *ptr != NULL; ptr++) { switch(*ptr[0]) { case 'f': auth_request_log_info(request, AUTH_SUBSYS_MECH, "Client requested non-standard mechanism"); auth_request_fail(request); return; case 'p': /* channel binding is not supported */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "Client requested and used channel-binding"); auth_request_fail(request); return; case 'n': case 'y': /* we don't need to use channel-binding */ continue; case 'a': /* authzid */ if ((*ptr)[1] != '=' || !oauth2_unescape_username((*ptr)+2, &username)) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid username escaping"); auth_request_fail(request); return; } else if (!auth_request_set_username(request, username, &error)) { auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); } else { user_given = TRUE; } break; default: auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid gs2-header in request"); auth_request_fail(request); return; } } for(ptr = fields; *ptr != NULL; ptr++) { if (strncmp(*ptr,"auth=", 5) == 0) { const char *value = (*ptr)+5; if (strncasecmp(value, "bearer ", 7) == 0 && oauth2_valid_token(value+7)) { token = value+7; } else { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Invalid continued data"); auth_request_fail(request); return; } } /* do not fail on unexpected fields */ } if (user_given && token != NULL) auth_request_verify_plain(request, token, oauthbearer_verify_callback); else { auth_request_log_info(request, AUTH_SUBSYS_MECH, "Missing username or token"); auth_request_fail(request); } } static struct auth_request *mech_oauth2_auth_new(void) { struct oauth2_auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"oauth2_auth_request", 2048); request = p_new(pool, struct oauth2_auth_request, 1); request->auth.pool = pool; return &request->auth; } const struct mech_module mech_oauthbearer = { "OAUTHBEARER", /* while this does not transfer plaintext password, the token is still considered as password */ .flags = MECH_SEC_PLAINTEXT, .passdb_need = 0, mech_oauth2_auth_new, mech_generic_auth_initial, mech_oauthbearer_auth_continue, mech_generic_auth_free }; const struct mech_module mech_xoauth2 = { "XOAUTH2", .flags = MECH_SEC_PLAINTEXT, .passdb_need = 0, mech_oauth2_auth_new, mech_generic_auth_initial, mech_xoauth2_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/userdb-vpopmail.h0000644000175000017500000000072413123174404015341 00000000000000#ifndef USERDB_VPOPMAIL_H #define USERDB_VPOPMAIL_H #include #include #include /* Limit user and domain to 80 chars each (+1 for \0). I wouldn't recommend raising this limit at least much, vpopmail is full of potential buffer overflows. */ #define VPOPMAIL_LIMIT 81 struct vqpasswd *vpopmail_lookup_vqp(struct auth_request *request, char vpop_user[VPOPMAIL_LIMIT], char vpop_domain[VPOPMAIL_LIMIT]); #endif dovecot-2.2.33.2/src/auth/auth-common.h0000644000175000017500000000052613123174404014457 00000000000000#ifndef AUTH_COMMON_H #define AUTH_COMMON_H #include "lib.h" #include "auth.h" extern bool worker, worker_restart_request; extern time_t process_start_time; extern struct auth_penalty *auth_penalty; void auth_refresh_proctitle(void); void auth_worker_refresh_proctitle(const char *state); void auth_module_load(const char *names); #endif dovecot-2.2.33.2/src/auth/auth-cache.c0000644000175000017500000002763013165463624014245 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "lib-signals.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "var-expand.h" #include "auth-request.h" #include "auth-cache.h" #include struct auth_cache { HASH_TABLE(char *, struct auth_cache_node *) hash; struct auth_cache_node *head, *tail; size_t max_size, size_left; unsigned int ttl_secs, neg_ttl_secs; unsigned int hit_count, miss_count; unsigned int pos_entries, neg_entries; unsigned long long pos_size, neg_size; }; static bool auth_request_var_expand_tab_find(const char *key, unsigned int size, unsigned int *idx_r) { const struct var_expand_table *tab = auth_request_var_expand_static_tab; unsigned int i; for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) { if (size == 1) { if (key[0] == tab[i].key) { *idx_r = i; return TRUE; } } else if (tab[i].long_key != NULL) { if (strncmp(key, tab[i].long_key, size) == 0 && tab[i].long_key[size] == '\0') { *idx_r = i; return TRUE; } } } return FALSE; } static void auth_cache_key_add_var(string_t *str, const char *data, unsigned int len) { if (str_len(str) > 0) str_append_c(str, '\t'); str_append_c(str, '%'); if (len == 1) str_append_c(str, data[0]); else { str_append_c(str, '{'); str_append_n(str, data, len); str_append_c(str, '}'); } } static void auth_cache_key_add_tab_idx(string_t *str, unsigned int i) { const struct var_expand_table *tab = &auth_request_var_expand_static_tab[i]; if (str_len(str) > 0) str_append_c(str, '\t'); str_append_c(str, '%'); if (tab->key != '\0') str_append_c(str, tab->key); else { str_append_c(str, '{'); str_append(str, tab->long_key); str_append_c(str, '}'); } } char *auth_cache_parse_key(pool_t pool, const char *query) { string_t *str; bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT]; const char *extra_vars; unsigned int i, idx, size, tab_idx; memset(key_seen, 0, sizeof(key_seen)); str = t_str_new(32); for (; *query != '\0'; ) { if (*query != '%') { query++; continue; } var_get_key_range(++query, &idx, &size); if (size == 0) { /* broken %variable ending too early */ break; } query += idx; if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) { /* just add the key. it would be nice to prevent duplicates here as well, but that's just too much trouble and probably very rare. */ auth_cache_key_add_var(str, query, size); } else { i_assert(tab_idx < N_ELEMENTS(key_seen)); key_seen[tab_idx] = TRUE; } query += size; } if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] && key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX]) { /* %n and %d both used -> replace with %u */ key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE; key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE; key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE; } /* we rely on these being at the beginning */ i_assert(AUTH_REQUEST_VAR_TAB_USER_IDX == 0); i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1); i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2); extra_vars = t_strdup(str_c(str)); str_truncate(str, 0); for (i = 0; i < N_ELEMENTS(key_seen); i++) { if (key_seen[i]) auth_cache_key_add_tab_idx(str, i); } if (*extra_vars != '\0') { if (str_len(str) > 0) str_append_c(str, '\t'); str_append(str, extra_vars); } return p_strdup(pool, str_c(str)); } static void auth_cache_node_unlink(struct auth_cache *cache, struct auth_cache_node *node) { if (node->prev != NULL) node->prev->next = node->next; else { /* unlinking tail */ cache->tail = node->next; } if (node->next != NULL) node->next->prev = node->prev; else { /* unlinking head */ cache->head = node->prev; } } static void auth_cache_node_link_head(struct auth_cache *cache, struct auth_cache_node *node) { node->prev = cache->head; node->next = NULL; cache->head = node; if (node->prev != NULL) node->prev->next = node; else cache->tail = node; } static void auth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node) { char *key = node->data; auth_cache_node_unlink(cache, node); cache->size_left += node->alloc_size; hash_table_remove(cache->hash, key); i_free(node); } static void sig_auth_cache_clear(const siginfo_t *si ATTR_UNUSED, void *context) { struct auth_cache *cache = context; i_info("SIGHUP received, %u cache entries flushed", auth_cache_clear(cache)); } static void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context) { struct auth_cache *cache = context; unsigned int total_count; size_t cache_used; total_count = cache->hit_count + cache->miss_count; i_info("Authentication cache hits %u/%u (%u%%)", cache->hit_count, total_count, total_count == 0 ? 100 : (cache->hit_count * 100 / total_count)); i_info("Authentication cache inserts: " "positive: %u entries %llu bytes, " "negative: %u entries %llu bytes", cache->pos_entries, cache->pos_size, cache->neg_entries, cache->neg_size); cache_used = cache->max_size - cache->size_left; i_info("Authentication cache current size: " "%"PRIuSIZE_T" bytes used of %"PRIuSIZE_T" bytes (%u%%)", cache_used, cache->max_size, (unsigned int)(cache_used * 100ULL / cache->max_size)); /* reset counters */ cache->hit_count = cache->miss_count = 0; cache->pos_entries = cache->neg_entries = 0; cache->pos_size = cache->neg_size = 0; } struct auth_cache *auth_cache_new(size_t max_size, unsigned int ttl_secs, unsigned int neg_ttl_secs ) { struct auth_cache *cache; cache = i_new(struct auth_cache, 1); hash_table_create(&cache->hash, default_pool, 0, str_hash, strcmp); cache->max_size = max_size; cache->size_left = max_size; cache->ttl_secs = ttl_secs; cache->neg_ttl_secs = neg_ttl_secs; lib_signals_set_handler(SIGHUP, LIBSIG_FLAGS_SAFE, sig_auth_cache_clear, cache); lib_signals_set_handler(SIGUSR2, LIBSIG_FLAGS_SAFE, sig_auth_cache_stats, cache); return cache; } void auth_cache_free(struct auth_cache **_cache) { struct auth_cache *cache = *_cache; *_cache = NULL; lib_signals_unset_handler(SIGHUP, sig_auth_cache_clear, cache); lib_signals_unset_handler(SIGUSR2, sig_auth_cache_stats, cache); auth_cache_clear(cache); hash_table_destroy(&cache->hash); i_free(cache); } unsigned int auth_cache_clear(struct auth_cache *cache) { unsigned int ret = hash_table_count(cache->hash); while (cache->tail != NULL) auth_cache_node_destroy(cache, cache->tail); hash_table_clear(cache->hash, FALSE); return ret; } static bool auth_cache_node_is_user(struct auth_cache_node *node, const char *username) { const char *data = node->data; size_t username_len; /* The cache nodes begin with "P"/"U", passdb/userdb ID, optional "+" master user, "\t" and then usually followed by the username. It's too much trouble to keep track of all the cache keys, so we'll just match it as if it was the username. If e.g. '%n' is used in the cache key instead of '%u', it means that cache entries can be removed only when @domain isn't in the username parameter. */ if (*data != 'P' && *data != 'U') return FALSE; data++; while (*data >= '0' && *data <= '9') data++; if (*data == '+') { /* skip over +master_user */ while (*data != '\t' && *data != '\0') data++; } if (*data != '\t') return FALSE; data++; username_len = strlen(username); return strncmp(data, username, username_len) == 0 && (data[username_len] == '\t' || data[username_len] == '\0'); } static bool auth_cache_node_is_one_of_users(struct auth_cache_node *node, const char *const *usernames) { unsigned int i; for (i = 0; usernames[i] != NULL; i++) { if (auth_cache_node_is_user(node, usernames[i])) return TRUE; } return FALSE; } unsigned int auth_cache_clear_users(struct auth_cache *cache, const char *const *usernames) { struct auth_cache_node *node, *next; unsigned int ret = 0; for (node = cache->tail; node != NULL; node = next) { next = node->next; if (auth_cache_node_is_one_of_users(node, usernames)) { auth_cache_node_destroy(cache, node); ret++; } } return ret; } static const char * auth_cache_escape(const char *string, const struct auth_request *auth_request ATTR_UNUSED) { /* cache key %variables are separated by tabs, make sure that there are no tabs in the string */ return str_tabescape(string); } static const char * auth_request_expand_cache_key(const struct auth_request *request, const char *key) { /* Uniquely identify the request's passdb/userdb with the P/U prefix and by "%!", which expands to the passdb/userdb ID number. */ key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!", request->master_user == NULL ? "" : "+%{master_user}", "\t", key, NULL); return t_auth_request_var_expand(key, request, auth_cache_escape); } const char * auth_cache_lookup(struct auth_cache *cache, const struct auth_request *request, const char *key, struct auth_cache_node **node_r, bool *expired_r, bool *neg_expired_r) { struct auth_cache_node *node; const char *value; unsigned int ttl_secs; time_t now; *expired_r = FALSE; *neg_expired_r = FALSE; key = auth_request_expand_cache_key(request, key); node = hash_table_lookup(cache->hash, key); if (node == NULL) { cache->miss_count++; return NULL; } value = node->data + strlen(node->data) + 1; ttl_secs = *value == '\0' ? cache->neg_ttl_secs : cache->ttl_secs; now = time(NULL); if (node->created < now - (time_t)ttl_secs) { /* TTL expired */ cache->miss_count++; *expired_r = TRUE; } else { /* move to head */ if (node != cache->head) { auth_cache_node_unlink(cache, node); auth_cache_node_link_head(cache, node); } cache->hit_count++; } if (node->created < now - (time_t)cache->neg_ttl_secs) *neg_expired_r = TRUE; if (node_r != NULL) *node_r = node; return value; } void auth_cache_insert(struct auth_cache *cache, struct auth_request *request, const char *key, const char *value, bool last_success) { struct auth_cache_node *node; size_t data_size, alloc_size, key_len, value_len = strlen(value); char *hash_key, *current_username; if (*value == '\0' && cache->neg_ttl_secs == 0) { /* we're not caching negative entries */ return; } /* store into cache using the translated username, except if we're doing a master user login */ current_username = request->user; if (request->translated_username != NULL && request->requested_login_user == NULL && request->master_user == NULL) request->user = t_strdup_noconst(request->translated_username); key = auth_request_expand_cache_key(request, key); key_len = strlen(key); request->user = current_username; data_size = key_len + 1 + value_len + 1; alloc_size = sizeof(struct auth_cache_node) - sizeof(node->data) + data_size; /* make sure we have enough space */ while (cache->size_left < alloc_size && cache->tail != NULL) auth_cache_node_destroy(cache, cache->tail); node = hash_table_lookup(cache->hash, key); if (node != NULL) { /* key is already in cache (probably expired), remove it */ auth_cache_node_destroy(cache, node); } /* @UNSAFE */ node = i_malloc(alloc_size); node->created = time(NULL); node->alloc_size = alloc_size; node->last_success = last_success; memcpy(node->data, key, key_len); memcpy(node->data + key_len + 1, value, value_len); auth_cache_node_link_head(cache, node); cache->size_left -= alloc_size; hash_key = node->data; hash_table_insert(cache->hash, hash_key, node); if (*value != '\0') { cache->pos_entries++; cache->pos_size += alloc_size; } else { cache->neg_entries++; cache->neg_size += alloc_size; } } void auth_cache_remove(struct auth_cache *cache, const struct auth_request *request, const char *key) { struct auth_cache_node *node; key = auth_request_expand_cache_key(request, key); node = hash_table_lookup(cache->hash, key); if (node == NULL) return; auth_cache_node_destroy(cache, node); } dovecot-2.2.33.2/src/auth/passdb-passwd-file.c0000644000175000017500000001144613165463624015731 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_PASSWD_FILE #include "str.h" #include "auth-cache.h" #include "password-scheme.h" #include "db-passwd-file.h" struct passwd_file_passdb_module { struct passdb_module module; struct db_passwd_file *pwf; const char *username_format; }; static void passwd_file_save_results(struct auth_request *request, const struct passwd_user *pu, const char **crypted_pass_r, const char **scheme_r) { const struct var_expand_table *table; const char *key, *value; string_t *str; char **p; *crypted_pass_r = pu->password != NULL ? pu->password : ""; *scheme_r = password_get_scheme(crypted_pass_r); if (*scheme_r == NULL) *scheme_r = request->passdb->passdb->default_pass_scheme; /* save the password so cache can use it */ auth_request_set_field(request, "password", *crypted_pass_r, *scheme_r); if (pu->extra_fields != NULL) { str = t_str_new(512); table = auth_request_get_var_expand_table(request, NULL); for (p = pu->extra_fields; *p != NULL; p++) { value = strchr(*p, '='); if (value != NULL) { key = t_strdup_until(*p, value); str_truncate(str, 0); auth_request_var_expand_with_table(str, value + 1, request, table, NULL); value = str_c(str); } else { key = *p; value = ""; } auth_request_set_field(request, key, value, NULL); } } } static void passwd_file_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct passwd_file_passdb_module *module = (struct passwd_file_passdb_module *)_module; struct passwd_user *pu; const char *scheme, *crypted_pass; int ret; ret = db_passwd_file_lookup(module->pwf, request, module->username_format, &pu); if (ret <= 0) { callback(ret < 0 ? PASSDB_RESULT_INTERNAL_FAILURE : PASSDB_RESULT_USER_UNKNOWN, request); return; } passwd_file_save_results(request, pu, &crypted_pass, &scheme); ret = auth_request_password_verify(request, password, crypted_pass, scheme, AUTH_SUBSYS_DB); callback(ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH, request); } static void passwd_file_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passdb_module *_module = request->passdb->passdb; struct passwd_file_passdb_module *module = (struct passwd_file_passdb_module *)_module; struct passwd_user *pu; const char *crypted_pass, *scheme; int ret; ret = db_passwd_file_lookup(module->pwf, request, module->username_format, &pu); if (ret <= 0) { callback(ret < 0 ? PASSDB_RESULT_INTERNAL_FAILURE : PASSDB_RESULT_USER_UNKNOWN, NULL, 0, request); return; } passwd_file_save_results(request, pu, &crypted_pass, &scheme); passdb_handle_credentials(PASSDB_RESULT_OK, crypted_pass, scheme, callback, request); } static struct passdb_module * passwd_file_preinit(pool_t pool, const char *args) { struct passwd_file_passdb_module *module; const char *scheme = PASSWD_FILE_DEFAULT_SCHEME; const char *format = PASSWD_FILE_DEFAULT_USERNAME_FORMAT; const char *key, *value; while (*args != '\0') { if (*args == '/') break; key = args; value = strchr(key, '='); if (value == NULL) { value = ""; args = strchr(key, ' '); } else { key = t_strdup_until(key, value); args = strchr(++value, ' '); if (args != NULL) value = t_strdup_until(value, args); } if (args == NULL) args = ""; else args++; if (strcmp(key, "scheme") == 0) scheme = p_strdup(pool, value); else if (strcmp(key, "username_format") == 0) format = p_strdup(pool, value); else i_fatal("passdb passwd-file: Unknown setting: %s", key); } if (*args == '\0') i_fatal("passdb passwd-file: Missing args"); module = p_new(pool, struct passwd_file_passdb_module, 1); module->pwf = db_passwd_file_init(args, FALSE, global_auth_settings->debug); module->username_format = format; module->module.default_pass_scheme = scheme; return &module->module; } static void passwd_file_init(struct passdb_module *_module) { struct passwd_file_passdb_module *module = (struct passwd_file_passdb_module *)_module; db_passwd_file_parse(module->pwf); } static void passwd_file_deinit(struct passdb_module *_module) { struct passwd_file_passdb_module *module = (struct passwd_file_passdb_module *)_module; db_passwd_file_unref(&module->pwf); } struct passdb_module_interface passdb_passwd_file = { "passwd-file", passwd_file_preinit, passwd_file_init, passwd_file_deinit, passwd_file_verify_plain, passwd_file_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_passwd_file = { .name = "passwd-file" }; #endif dovecot-2.2.33.2/src/auth/test-db-dict.c0000644000175000017500000000314613165463624014522 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "db-dict.h" #include "test-common.h" static void test_db_dict_parse_cache_key(void) { struct db_dict_key keys[] = { { "key0", "%d and %n", NULL, NULL, 0 }, { "key1", "%{foo}%r%{bar}", NULL, NULL, 0 }, { "key2", "%{test1}/path", NULL, NULL, 0 }, { "key3", "path2/%{test2}", NULL, NULL, 0 }, { "key4", "%{plop}", NULL, NULL, 0 }, { "key5", "%{unused}", NULL, NULL, 0 } }; struct db_dict_field fields[] = { { "name1", "hello %{dict:key0} %l and %{dict:key1}" }, { "name2", "%{dict:key2} also %{extra} plus" } }; const struct db_dict_key *objects[] = { &keys[3], &keys[4] }; buffer_t keybuf, fieldbuf, objectbuf; ARRAY_TYPE(db_dict_key) keyarr; ARRAY_TYPE(db_dict_field) fieldarr; ARRAY_TYPE(db_dict_key_p) objectarr; test_begin("db dict parse cache key"); buffer_create_from_const_data(&keybuf, keys, sizeof(keys)); buffer_create_from_const_data(&fieldbuf, fields, sizeof(fields)); buffer_create_from_const_data(&objectbuf, objects, sizeof(objects)); array_create_from_buffer(&keyarr, &keybuf, sizeof(keys[0])); array_create_from_buffer(&fieldarr, &fieldbuf, sizeof(fields[0])); array_create_from_buffer(&objectarr, &objectbuf, sizeof(objects[0])); test_assert(strcmp(db_dict_parse_cache_key(&keyarr, &fieldarr, &objectarr), "\t%d and %n\t%l\t%{foo}%r%{bar}\t%{test1}/path\t%{extra}\tpath2/%{test2}\t%{plop}") == 0); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_db_dict_parse_cache_key, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/auth/auth-fields.c0000644000175000017500000001274213144653606014444 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "auth-request.h" #include "auth-fields.h" struct auth_fields { pool_t pool; ARRAY_TYPE(auth_field) fields, snapshot_fields; unsigned int snapshot_idx; bool snapshotted; }; struct auth_fields *auth_fields_init(pool_t pool) { struct auth_fields *fields; fields = p_new(pool, struct auth_fields, 1); fields->pool = pool; return fields; } static void auth_fields_snapshot_preserve(struct auth_fields *fields) { if (!fields->snapshotted || array_is_created(&fields->snapshot_fields)) return; p_array_init(&fields->snapshot_fields, fields->pool, array_count(&fields->fields)); array_append_array(&fields->snapshot_fields, &fields->fields); } static bool auth_fields_find_idx(struct auth_fields *fields, const char *key, unsigned int *idx_r) { const struct auth_field *f; unsigned int i, count; if (!array_is_created(&fields->fields)) return FALSE; f = array_get(&fields->fields, &count); for (i = 0; i < count; i++) { if (strcmp(f[i].key, key) == 0) { *idx_r = i; return TRUE; } } return FALSE; } void auth_fields_add(struct auth_fields *fields, const char *key, const char *value, enum auth_field_flags flags) { struct auth_field *field; unsigned int idx; i_assert(*key != '\0'); i_assert(strchr(key, '\t') == NULL && strchr(key, '\n') == NULL); if (!auth_fields_find_idx(fields, key, &idx)) { if (!array_is_created(&fields->fields)) p_array_init(&fields->fields, fields->pool, 16); field = array_append_space(&fields->fields); field->key = p_strdup(fields->pool, key); } else { auth_fields_snapshot_preserve(fields); field = array_idx_modifiable(&fields->fields, idx); } field->value = p_strdup_empty(fields->pool, value); field->flags = flags | AUTH_FIELD_FLAG_CHANGED; } void auth_fields_remove(struct auth_fields *fields, const char *key) { unsigned int idx; if (auth_fields_find_idx(fields, key, &idx)) { auth_fields_snapshot_preserve(fields); array_delete(&fields->fields, idx, 1); } } const char *auth_fields_find(struct auth_fields *fields, const char *key) { const struct auth_field *field; unsigned int idx; if (!auth_fields_find_idx(fields, key, &idx)) return NULL; field = array_idx(&fields->fields, idx); return field->value == NULL ? "" : field->value; } bool auth_fields_exists(struct auth_fields *fields, const char *key) { return auth_fields_find(fields, key) != NULL; } void auth_fields_reset(struct auth_fields *fields) { if (array_is_created(&fields->fields)) { auth_fields_snapshot_preserve(fields); array_clear(&fields->fields); } } void auth_fields_import_prefixed(struct auth_fields *fields, const char *prefix, const char *str, enum auth_field_flags flags) { T_BEGIN { const char *const *arg = t_strsplit_tabescaped(str); const char *key, *value; for (; *arg != NULL; arg++) { value = strchr(*arg, '='); if (value == NULL) { key = *arg; value = NULL; } else { key = t_strdup_until(*arg, value++); if (*prefix != '\0') key = t_strconcat(prefix, key, NULL); } auth_fields_add(fields, key, value, flags); } } T_END; } void auth_fields_import(struct auth_fields *fields, const char *str, enum auth_field_flags flags) { auth_fields_import_prefixed(fields, "", str, flags); } const ARRAY_TYPE(auth_field) *auth_fields_export(struct auth_fields *fields) { if (!array_is_created(&fields->fields)) p_array_init(&fields->fields, fields->pool, 1); return &fields->fields; } void auth_fields_append(struct auth_fields *fields, string_t *dest, enum auth_field_flags flags_mask, enum auth_field_flags flags_result) { const struct auth_field *f; unsigned int i, count; bool first = TRUE; if (!array_is_created(&fields->fields)) return; f = array_get(&fields->fields, &count); for (i = 0; i < count; i++) { if ((f[i].flags & flags_mask) != flags_result) continue; if (first) first = FALSE; else str_append_c(dest, '\t'); str_append(dest, f[i].key); if (f[i].value != NULL) { str_append_c(dest, '='); str_append_tabescaped(dest, f[i].value); } } } bool auth_fields_is_empty(struct auth_fields *fields) { return fields == NULL || !array_is_created(&fields->fields) || array_count(&fields->fields) == 0; } void auth_fields_booleanize(struct auth_fields *fields, const char *key) { struct auth_field *field; unsigned int idx; if (auth_fields_find_idx(fields, key, &idx)) { field = array_idx_modifiable(&fields->fields, idx); field->value = NULL; } } void auth_fields_snapshot(struct auth_fields *fields) { struct auth_field *field; fields->snapshotted = TRUE; if (!array_is_created(&fields->fields)) return; if (!array_is_created(&fields->snapshot_fields)) { /* try to avoid creating this array */ fields->snapshot_idx = array_count(&fields->fields); } else { array_clear(&fields->snapshot_fields); array_append_array(&fields->snapshot_fields, &fields->fields); } array_foreach_modifiable(&fields->fields, field) field->flags &= ~AUTH_FIELD_FLAG_CHANGED; } void auth_fields_rollback(struct auth_fields *fields) { if (array_is_created(&fields->snapshot_fields)) { array_clear(&fields->fields); array_append_array(&fields->fields, &fields->snapshot_fields); } else if (array_is_created(&fields->fields)) { array_delete(&fields->fields, fields->snapshot_idx, array_count(&fields->fields) - fields->snapshot_idx); } } dovecot-2.2.33.2/src/auth/auth-token.h0000644000175000017500000000035513123174404014307 00000000000000#ifndef AUTH_TOKEN_H #define AUTH_TOKEN_H void auth_token_init(void); void auth_token_deinit(void); const char *auth_token_get(const char *service, const char *session_pid, const char *username, const char *session_id); #endif dovecot-2.2.33.2/src/auth/passdb-template.c0000644000175000017500000000426513165463624015327 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "str.h" #include "passdb.h" #include "passdb-template.h" struct passdb_template { ARRAY(const char *) args; }; struct passdb_template *passdb_template_build(pool_t pool, const char *args) { struct passdb_template *tmpl; const char *const *tmp, *key, *value; tmpl = p_new(pool, struct passdb_template, 1); tmp = t_strsplit_spaces(args, " "); p_array_init(&tmpl->args, pool, str_array_length(tmp)); for (; *tmp != NULL; tmp++) { value = strchr(*tmp, '='); if (value == NULL) key = *tmp; else key = t_strdup_until(*tmp, value++); key = p_strdup(pool, key); value = p_strdup(pool, value); array_append(&tmpl->args, &key, 1); array_append(&tmpl->args, &value, 1); } return tmpl; } void passdb_template_export(struct passdb_template *tmpl, struct auth_request *auth_request) { const struct var_expand_table *table; string_t *str; const char *const *args, *value; unsigned int i, count; if (passdb_template_is_empty(tmpl)) return; str = t_str_new(256); table = auth_request_get_var_expand_table(auth_request, NULL); args = array_get(&tmpl->args, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (args[i+1] == NULL) value = ""; else { str_truncate(str, 0); auth_request_var_expand_with_table(str, args[i+1], auth_request, table, NULL); value = str_c(str); } auth_request_set_field(auth_request, args[i], value, STATIC_PASS_SCHEME); } } bool passdb_template_remove(struct passdb_template *tmpl, const char *key, const char **value_r) { const char *const *args; unsigned int i, count; args = array_get(&tmpl->args, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (strcmp(args[i], key) == 0) { *value_r = args[i+1]; array_delete(&tmpl->args, i, 2); return TRUE; } } return FALSE; } bool passdb_template_is_empty(struct passdb_template *tmpl) { return array_count(&tmpl->args) == 0; } const char *const *passdb_template_get_args(struct passdb_template *tmpl, unsigned int *count_r) { return array_get(&tmpl->args, count_r); } dovecot-2.2.33.2/src/auth/userdb.h0000644000175000017500000000510613165463624013526 00000000000000#ifndef USERDB_H #define USERDB_H #include "md5.h" #include "auth-fields.h" struct auth; struct auth_request; struct auth_userdb_settings; enum userdb_result { USERDB_RESULT_INTERNAL_FAILURE = -1, USERDB_RESULT_USER_UNKNOWN = -2, USERDB_RESULT_OK = 1 }; typedef void userdb_callback_t(enum userdb_result result, struct auth_request *request); /* user=NULL when there are no more users */ typedef void userdb_iter_callback_t(const char *user, void *context); struct userdb_module { const char *args; /* The default caching key for this module, or NULL if caching isn't wanted. This is updated by settings in auth_userdb. */ #define default_cache_key cache_key /* FIXME: remove in v2.3 - for API backwards compatibility */ const char *default_cache_key; /* If blocking is set to TRUE, use child processes to access this userdb. */ bool blocking; /* id is used by blocking userdb to identify the userdb */ unsigned int id; /* number of time init() has been called */ int init_refcount; /* WARNING: avoid adding anything here that isn't based on args. if you do, you need to change userdb.c:userdb_find() also to avoid accidentally merging wrong userdbs. */ const struct userdb_module_interface *iface; }; struct userdb_iterate_context { struct auth_request *auth_request; userdb_iter_callback_t *callback; void *context; bool failed; }; struct userdb_module_interface { const char *name; struct userdb_module *(*preinit)(pool_t pool, const char *args); void (*init)(struct userdb_module *module); void (*deinit)(struct userdb_module *module); void (*lookup)(struct auth_request *auth_request, userdb_callback_t *callback); struct userdb_iterate_context * (*iterate_init)(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context); void (*iterate_next)(struct userdb_iterate_context *ctx); int (*iterate_deinit)(struct userdb_iterate_context *ctx); }; uid_t userdb_parse_uid(struct auth_request *request, const char *str) ATTR_NULL(1); gid_t userdb_parse_gid(struct auth_request *request, const char *str) ATTR_NULL(1); struct userdb_module * userdb_preinit(pool_t pool, const struct auth_userdb_settings *set); void userdb_init(struct userdb_module *userdb); void userdb_deinit(struct userdb_module *userdb); void userdb_register_module(struct userdb_module_interface *iface); void userdb_unregister_module(struct userdb_module_interface *iface); void userdbs_generate_md5(unsigned char md5[STATIC_ARRAY MD5_RESULTLEN]); void userdbs_init(void); void userdbs_deinit(void); #include "auth-request.h" #endif dovecot-2.2.33.2/src/auth/auth-request.c0000644000175000017500000023146213165463624014672 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "buffer.h" #include "hash.h" #include "sha1.h" #include "hex-binary.h" #include "str.h" #include "array.h" #include "safe-memset.h" #include "str-sanitize.h" #include "strescape.h" #include "var-expand.h" #include "dns-lookup.h" #include "auth-cache.h" #include "auth-request.h" #include "auth-request-handler.h" #include "auth-request-stats.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #include "auth-policy.h" #include "passdb.h" #include "passdb-blocking.h" #include "passdb-cache.h" #include "passdb-template.h" #include "userdb-blocking.h" #include "userdb-template.h" #include "password-scheme.h" #include "wildcard-match.h" #include #define AUTH_SUBSYS_PROXY "proxy" #define AUTH_DNS_SOCKET_PATH "dns-client" #define AUTH_DNS_DEFAULT_TIMEOUT_MSECS (1000*10) #define AUTH_DNS_WARN_MSECS 500 #define AUTH_REQUEST_MAX_DELAY_SECS (60*5) #define CACHED_PASSWORD_SCHEME "SHA1" struct auth_request_proxy_dns_lookup_ctx { struct auth_request *request; auth_request_proxy_cb_t *callback; struct dns_lookup *dns_lookup; }; struct auth_policy_check_ctx { enum { AUTH_POLICY_CHECK_TYPE_PLAIN, AUTH_POLICY_CHECK_TYPE_LOOKUP, AUTH_POLICY_CHECK_TYPE_SUCCESS, } type; struct auth_request *request; buffer_t *success_data; verify_plain_callback_t *callback_plain; lookup_credentials_callback_t *callback_lookup; }; const char auth_default_subsystems[2]; unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX]; static void get_log_prefix(string_t *str, struct auth_request *auth_request, const char *subsystem); static void auth_request_userdb_import(struct auth_request *request, const char *args); static void auth_request_verify_plain_continue(struct auth_request *request, verify_plain_callback_t *callback); static void auth_request_lookup_credentials_policy_continue(struct auth_request *request, lookup_credentials_callback_t *callback); static void auth_request_policy_check_callback(int result, void *context); struct auth_request * auth_request_new(const struct mech_module *mech) { struct auth_request *request; request = mech->auth_new(); request->state = AUTH_REQUEST_STATE_NEW; auth_request_state_count[AUTH_REQUEST_STATE_NEW]++; request->refcount = 1; request->last_access = ioloop_time; request->session_pid = (pid_t)-1; request->set = global_auth_settings; request->debug = request->set->debug; request->mech = mech; request->mech_name = mech->mech_name; request->extra_fields = auth_fields_init(request->pool); return request; } struct auth_request *auth_request_new_dummy(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"auth_request", 1024); request = p_new(pool, struct auth_request, 1); request->pool = pool; request->state = AUTH_REQUEST_STATE_NEW; auth_request_state_count[AUTH_REQUEST_STATE_NEW]++; request->refcount = 1; request->last_access = ioloop_time; request->session_pid = (pid_t)-1; request->set = global_auth_settings; request->debug = request->set->debug; request->extra_fields = auth_fields_init(request->pool); return request; } void auth_request_set_state(struct auth_request *request, enum auth_request_state state) { if (request->state == state) return; i_assert(request->to_penalty == NULL); i_assert(auth_request_state_count[request->state] > 0); auth_request_state_count[request->state]--; auth_request_state_count[state]++; request->state = state; auth_refresh_proctitle(); } void auth_request_init(struct auth_request *request) { struct auth *auth; auth = auth_request_get_auth(request); request->set = auth->set; /* NOTE: request->debug may already be TRUE here */ if (request->set->debug) request->debug = TRUE; request->passdb = auth->passdbs; request->userdb = auth->userdbs; } struct auth *auth_request_get_auth(struct auth_request *request) { return auth_find_service(request->service); } void auth_request_success(struct auth_request *request, const void *data, size_t data_size) { i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); /* perform second policy lookup here */ struct auth_policy_check_ctx *ctx = p_new(request->pool, struct auth_policy_check_ctx, 1); ctx->request = request; ctx->success_data = buffer_create_dynamic(request->pool, data_size); buffer_append(ctx->success_data, data, data_size); ctx->type = AUTH_POLICY_CHECK_TYPE_SUCCESS; auth_policy_check(request, request->mech_password, auth_request_policy_check_callback, ctx); } static void auth_request_success_continue(struct auth_policy_check_ctx *ctx) { struct auth_request *request = ctx->request; struct auth_stats *stats; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (request->to_penalty != NULL) timeout_remove(&request->to_penalty); if (request->failed || !request->passdb_success) { /* password was valid, but some other check failed. */ auth_request_fail(request); return; } if (request->delay_until > ioloop_time) { unsigned int delay_secs = request->delay_until - ioloop_time; request->to_penalty = timeout_add(delay_secs * 1000, auth_request_success_continue, ctx); return; } request->successful = TRUE; if (ctx->success_data->used > 0 && !request->final_resp_ok) { /* we'll need one more SASL round, since client doesn't support the final SASL response */ auth_request_handler_reply_continue(request, ctx->success_data->data, ctx->success_data->used); return; } stats = auth_request_stats_get(request); stats->auth_success_count++; if (request->master_user != NULL) stats->auth_master_success_count++; auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); auth_request_refresh_last_access(request); auth_request_handler_reply(request, AUTH_CLIENT_RESULT_SUCCESS, ctx->success_data->data, ctx->success_data->used); } void auth_request_fail(struct auth_request *request) { struct auth_stats *stats; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); stats = auth_request_stats_get(request); stats->auth_failure_count++; auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); auth_request_refresh_last_access(request); auth_request_handler_reply(request, AUTH_CLIENT_RESULT_FAILURE, "", 0); } void auth_request_internal_failure(struct auth_request *request) { request->internal_failure = TRUE; auth_request_fail(request); } void auth_request_ref(struct auth_request *request) { request->refcount++; } void auth_request_unref(struct auth_request **_request) { struct auth_request *request = *_request; *_request = NULL; i_assert(request->refcount > 0); if (--request->refcount > 0) return; auth_request_stats_send(request); auth_request_state_count[request->state]--; auth_refresh_proctitle(); if (request->mech_password != NULL) { safe_memset(request->mech_password, 0, strlen(request->mech_password)); } if (request->dns_lookup_ctx != NULL) dns_lookup_abort(&request->dns_lookup_ctx->dns_lookup); if (request->to_abort != NULL) timeout_remove(&request->to_abort); if (request->to_penalty != NULL) timeout_remove(&request->to_penalty); if (request->mech != NULL) request->mech->auth_free(request); else pool_unref(&request->pool); } static void auth_str_add_keyvalue(string_t *dest, const char *key, const char *value) { str_append_c(dest, '\t'); str_append(dest, key); if (value != NULL) { str_append_c(dest, '='); str_append_tabescaped(dest, value); } } static void auth_request_export_fields(string_t *dest, struct auth_fields *auth_fields, const char *prefix) { const ARRAY_TYPE(auth_field) *fields = auth_fields_export(auth_fields); const struct auth_field *field; array_foreach(fields, field) { str_printfa(dest, "\t%s%s", prefix, field->key); if (field->value != NULL) { str_append_c(dest, '='); str_append_tabescaped(dest, field->value); } } } void auth_request_export(struct auth_request *request, string_t *dest) { str_append(dest, "user="); str_append_tabescaped(dest, request->user); auth_str_add_keyvalue(dest, "service", request->service); if (request->master_user != NULL) { auth_str_add_keyvalue(dest, "master-user", request->master_user); } auth_str_add_keyvalue(dest, "original_username", request->original_username); if (request->requested_login_user != NULL) { auth_str_add_keyvalue(dest, "requested-login-user", request->requested_login_user); } if (request->local_ip.family != 0) { auth_str_add_keyvalue(dest, "lip", net_ip2addr(&request->local_ip)); } if (request->remote_ip.family != 0) { auth_str_add_keyvalue(dest, "rip", net_ip2addr(&request->remote_ip)); } if (request->local_port != 0) str_printfa(dest, "\tlport=%u", request->local_port); if (request->remote_port != 0) str_printfa(dest, "\trport=%u", request->remote_port); if (request->real_local_ip.family != 0) { auth_str_add_keyvalue(dest, "real_lip", net_ip2addr(&request->real_local_ip)); } if (request->real_remote_ip.family != 0) { auth_str_add_keyvalue(dest, "real_rip", net_ip2addr(&request->real_remote_ip)); } if (request->real_local_port != 0) str_printfa(dest, "\treal_lport=%u", request->real_local_port); if (request->real_remote_port != 0) str_printfa(dest, "\treal_rport=%u", request->real_remote_port); if (request->local_name != 0) { str_append(dest, "\tlocal_name="); str_append_tabescaped(dest, request->local_name); } if (request->session_id != NULL) str_printfa(dest, "\tsession=%s", request->session_id); if (request->debug) str_append(dest, "\tdebug"); if (request->secured) str_append(dest, "\tsecured"); if (request->skip_password_check) str_append(dest, "\tskip-password-check"); if (request->delayed_credentials != NULL) str_append(dest, "\tdelayed-credentials"); if (request->valid_client_cert) str_append(dest, "\tvalid-client-cert"); if (request->no_penalty) str_append(dest, "\tno-penalty"); if (request->successful) str_append(dest, "\tsuccessful"); if (request->mech_name != NULL) auth_str_add_keyvalue(dest, "mech", request->mech_name); if (request->client_id != NULL) auth_str_add_keyvalue(dest, "client_id", request->client_id); /* export passdb extra fields */ auth_request_export_fields(dest, request->extra_fields, "passdb_"); /* export any userdb fields */ if (request->userdb_reply != NULL) auth_request_export_fields(dest, request->userdb_reply, "userdb_"); } bool auth_request_import_info(struct auth_request *request, const char *key, const char *value) { /* authentication and user lookups may set these */ if (strcmp(key, "service") == 0) request->service = p_strdup(request->pool, value); else if (strcmp(key, "lip") == 0) { (void)net_addr2ip(value, &request->local_ip); if (request->real_local_ip.family == 0) request->real_local_ip = request->local_ip; } else if (strcmp(key, "rip") == 0) { (void)net_addr2ip(value, &request->remote_ip); if (request->real_remote_ip.family == 0) request->real_remote_ip = request->remote_ip; } else if (strcmp(key, "lport") == 0) { (void)net_str2port(value, &request->local_port); if (request->real_local_port == 0) request->real_local_port = request->local_port; } else if (strcmp(key, "rport") == 0) { (void)net_str2port(value, &request->remote_port); if (request->real_remote_port == 0) request->real_remote_port = request->remote_port; } else if (strcmp(key, "real_lip") == 0) (void)net_addr2ip(value, &request->real_local_ip); else if (strcmp(key, "real_rip") == 0) (void)net_addr2ip(value, &request->real_remote_ip); else if (strcmp(key, "real_lport") == 0) (void)net_str2port(value, &request->real_local_port); else if (strcmp(key, "real_rport") == 0) (void)net_str2port(value, &request->real_remote_port); else if (strcmp(key, "local_name") == 0) request->local_name = p_strdup(request->pool, value); else if (strcmp(key, "session") == 0) request->session_id = p_strdup(request->pool, value); else if (strcmp(key, "debug") == 0) request->debug = TRUE; else if (strcmp(key, "client_id") == 0) request->client_id = p_strdup(request->pool, value); else if (strcmp(key, "forward_fields") == 0) { auth_fields_import_prefixed(request->extra_fields, "forward_", value, 0); /* make sure the forward_ fields aren't deleted by auth_fields_rollback() if the first passdb lookup fails. */ auth_fields_snapshot(request->extra_fields); } else return FALSE; /* NOTE: keep in sync with auth_request_export() */ return TRUE; } bool auth_request_import_auth(struct auth_request *request, const char *key, const char *value) { if (auth_request_import_info(request, key, value)) return TRUE; /* auth client may set these */ if (strcmp(key, "secured") == 0) request->secured = TRUE; else if (strcmp(key, "final-resp-ok") == 0) request->final_resp_ok = TRUE; else if (strcmp(key, "no-penalty") == 0) request->no_penalty = TRUE; else if (strcmp(key, "valid-client-cert") == 0) request->valid_client_cert = TRUE; else if (strcmp(key, "cert_username") == 0) { if (request->set->ssl_username_from_cert) { /* get username from SSL certificate. it overrides the username given by the auth mechanism. */ request->user = p_strdup(request->pool, value); request->cert_username = TRUE; } } else { return FALSE; } return TRUE; } bool auth_request_import_master(struct auth_request *request, const char *key, const char *value) { pid_t pid; /* master request lookups may set these */ if (strcmp(key, "session_pid") == 0) { if (str_to_pid(value, &pid) == 0) request->session_pid = pid; } else if (strcmp(key, "request_auth_token") == 0) request->request_auth_token = TRUE; else return FALSE; return TRUE; } bool auth_request_import(struct auth_request *request, const char *key, const char *value) { if (auth_request_import_auth(request, key, value)) return TRUE; /* for communication between auth master and worker processes */ if (strcmp(key, "user") == 0) request->user = p_strdup(request->pool, value); else if (strcmp(key, "master-user") == 0) request->master_user = p_strdup(request->pool, value); else if (strcmp(key, "original-username") == 0) request->original_username = p_strdup(request->pool, value); else if (strcmp(key, "requested-login-user") == 0) request->requested_login_user = p_strdup(request->pool, value); else if (strcmp(key, "successful") == 0) request->successful = TRUE; else if (strcmp(key, "skip-password-check") == 0) request->skip_password_check = TRUE; else if (strcmp(key, "delayed-credentials") == 0) { /* just make passdb_handle_credentials() work identically in auth-worker as it does in auth-master. the worker shouldn't care about the actual contents of the credentials. */ request->delayed_credentials = &uchar_nul; request->delayed_credentials_size = 1; } else if (strcmp(key, "mech") == 0) request->mech_name = p_strdup(request->pool, value); else if (strncmp(key, "passdb_", 7) == 0) auth_fields_add(request->extra_fields, key+7, value, 0); else if (strncmp(key, "userdb_", 7) == 0) { if (request->userdb_reply == NULL) request->userdb_reply = auth_fields_init(request->pool); auth_fields_add(request->userdb_reply, key+7, value, 0); } else return FALSE; return TRUE; } void auth_request_initial(struct auth_request *request) { i_assert(request->state == AUTH_REQUEST_STATE_NEW); auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE); request->mech->auth_initial(request, request->initial_response, request->initial_response_len); } void auth_request_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (request->successful) { auth_request_success(request, "", 0); return; } auth_request_refresh_last_access(request); request->mech->auth_continue(request, data, data_size); } static void auth_request_save_cache(struct auth_request *request, enum passdb_result result) { struct auth_passdb *passdb = request->passdb; const char *encoded_password; string_t *str; switch (result) { case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_PASSWORD_MISMATCH: case PASSDB_RESULT_OK: case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: /* can be cached */ break; case PASSDB_RESULT_NEXT: case PASSDB_RESULT_USER_DISABLED: case PASSDB_RESULT_PASS_EXPIRED: /* FIXME: we can't cache this now, or cache lookup would return success. */ return; case PASSDB_RESULT_INTERNAL_FAILURE: i_unreached(); } if (passdb_cache == NULL || passdb->cache_key == NULL) return; if (result < 0) { /* lookup failed. */ if (result == PASSDB_RESULT_USER_UNKNOWN) { auth_cache_insert(passdb_cache, request, passdb->cache_key, "", FALSE); } return; } if (request->passdb_password == NULL && !auth_fields_exists(request->extra_fields, "nopassword")) { /* passdb didn't provide the correct password */ if (result != PASSDB_RESULT_OK || request->mech_password == NULL) return; /* we can still cache valid password lookups though. strdup() it so that mech_password doesn't get cleared too early. */ if (!password_generate_encoded(request->mech_password, request->user, CACHED_PASSWORD_SCHEME, &encoded_password)) i_unreached(); request->passdb_password = p_strconcat(request->pool, "{"CACHED_PASSWORD_SCHEME"}", encoded_password, NULL); } /* save all except the currently given password in cache */ str = t_str_new(256); if (request->passdb_password != NULL) { if (*request->passdb_password != '{') { /* cached passwords must have a known scheme */ str_append_c(str, '{'); str_append(str, passdb->passdb->default_pass_scheme); str_append_c(str, '}'); } str_append_tabescaped(str, request->passdb_password); } if (!auth_fields_is_empty(request->extra_fields)) { str_append_c(str, '\t'); /* add only those extra fields to cache that were set by this passdb lookup. the CHANGED flag does this, because we snapshotted the extra_fields before the current passdb lookup. */ auth_fields_append(request->extra_fields, str, AUTH_FIELD_FLAG_CHANGED, AUTH_FIELD_FLAG_CHANGED); } auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str), result == PASSDB_RESULT_OK); } static void auth_request_master_lookup_finish(struct auth_request *request) { if (request->failed) return; /* master login successful. update user and master_user variables. */ auth_request_log_info(request, AUTH_SUBSYS_DB, "Master user logging in as %s", request->requested_login_user); request->master_user = request->user; request->user = request->requested_login_user; request->requested_login_user = NULL; } static bool auth_request_mechanism_accepted(const char *const *mechs, const struct mech_module *mech) { /* no filter specified, anything goes */ if (mechs == NULL) return TRUE; /* request has no mechanism, see if none is accepted */ if (mech == NULL) return str_array_icase_find(mechs, "none"); /* check if request mechanism is accepted */ return str_array_icase_find(mechs, mech->mech_name); } /** Check if username is included in the filter. Logic is that if the username is not excluded by anything, and is included by something, it will be accepted. By default, all usernames are included, unless there is a inclusion item, when username will be excluded if there is no inclusion for it. Exclusions are denoted with a ! in front of the pattern. */ bool auth_request_username_accepted(const char *const *filter, const char *username) { bool have_includes = FALSE; bool matched_inc = FALSE; for(;*filter != NULL; filter++) { /* if filter has ! it means the pattern will be refused */ bool exclude = (**filter == '!'); if (!exclude) have_includes = TRUE; if (wildcard_match(username, (*filter)+(exclude?1:0))) { if (exclude) { return FALSE; } else { matched_inc = TRUE; } } } return matched_inc || !have_includes; } static bool auth_request_want_skip_passdb(struct auth_request *request, struct auth_passdb *passdb) { /* if mechanism is not supported, skip */ const char *const *mechs = passdb->passdb->mechanisms; const char *const *username_filter = passdb->passdb->username_filter; const char *username; username = request->user; if (!auth_request_mechanism_accepted(mechs, request->mech)) { auth_request_log_debug(request, request->mech != NULL ? AUTH_SUBSYS_MECH : "none", "skipping passdb: mechanism filtered"); return TRUE; } if (passdb->passdb->username_filter != NULL && !auth_request_username_accepted(username_filter, username)) { auth_request_log_debug(request, request->mech != NULL ? AUTH_SUBSYS_MECH : "none", "skipping passdb: username filtered"); return TRUE; } /* skip_password_check basically specifies if authentication is finished */ bool authenticated = request->skip_password_check; switch (passdb->skip) { case AUTH_PASSDB_SKIP_NEVER: return FALSE; case AUTH_PASSDB_SKIP_AUTHENTICATED: return authenticated; case AUTH_PASSDB_SKIP_UNAUTHENTICATED: return !authenticated; } i_unreached(); } static bool auth_request_want_skip_userdb(struct auth_request *request, struct auth_userdb *userdb) { switch (userdb->skip) { case AUTH_USERDB_SKIP_NEVER: return FALSE; case AUTH_USERDB_SKIP_FOUND: return request->userdb_success; case AUTH_USERDB_SKIP_NOTFOUND: return !request->userdb_success; } i_unreached(); } static bool auth_request_handle_passdb_callback(enum passdb_result *result, struct auth_request *request) { struct auth_passdb *next_passdb; enum auth_db_rule result_rule; bool passdb_continue = FALSE; if (request->passdb_password != NULL) { safe_memset(request->passdb_password, 0, strlen(request->passdb_password)); } if (request->passdb->set->deny && *result != PASSDB_RESULT_USER_UNKNOWN) { /* deny passdb. we can get through this step only if the lookup returned that user doesn't exist in it. internal errors are fatal here. */ if (*result != PASSDB_RESULT_INTERNAL_FAILURE) { auth_request_log_info(request, AUTH_SUBSYS_DB, "User found from deny passdb"); *result = PASSDB_RESULT_USER_DISABLED; } return TRUE; } if (request->failed) { /* The passdb didn't fail, but something inside it failed (e.g. allow_nets mismatch). Make sure we'll fail this lookup, but reset the failure so the next passdb can succeed. */ if (*result == PASSDB_RESULT_OK) *result = PASSDB_RESULT_USER_UNKNOWN; request->failed = FALSE; } /* users that exist but can't log in are special. we don't try to match any of the success/failure rules to them. they'll always fail. */ switch (*result) { case PASSDB_RESULT_USER_DISABLED: return TRUE; case PASSDB_RESULT_PASS_EXPIRED: auth_request_set_field(request, "reason", "Password expired", NULL); return TRUE; case PASSDB_RESULT_OK: result_rule = request->passdb->result_success; break; case PASSDB_RESULT_INTERNAL_FAILURE: result_rule = request->passdb->result_internalfail; break; case PASSDB_RESULT_NEXT: auth_request_log_debug(request, AUTH_SUBSYS_DB, "Not performing authentication (noauthenticate set)"); result_rule = AUTH_DB_RULE_CONTINUE; break; case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_PASSWORD_MISMATCH: default: result_rule = request->passdb->result_failure; break; } switch (result_rule) { case AUTH_DB_RULE_RETURN: break; case AUTH_DB_RULE_RETURN_OK: request->passdb_success = TRUE; break; case AUTH_DB_RULE_RETURN_FAIL: request->passdb_success = FALSE; break; case AUTH_DB_RULE_CONTINUE: passdb_continue = TRUE; if (*result == PASSDB_RESULT_OK) { /* password was successfully verified. don't bother checking it again. */ request->skip_password_check = TRUE; } break; case AUTH_DB_RULE_CONTINUE_OK: passdb_continue = TRUE; request->passdb_success = TRUE; /* password was successfully verified. don't bother checking it again. */ request->skip_password_check = TRUE; break; case AUTH_DB_RULE_CONTINUE_FAIL: passdb_continue = TRUE; request->passdb_success = FALSE; break; } /* nopassword check is specific to a single passdb and shouldn't leak to the next one. we already added it to cache. */ auth_fields_remove(request->extra_fields, "nopassword"); auth_fields_remove(request->extra_fields, "noauthenticate"); if (request->requested_login_user != NULL && *result == PASSDB_RESULT_OK) { auth_request_master_lookup_finish(request); /* if the passdb lookup continues, it continues with non-master passdbs for the requested_login_user. */ next_passdb = auth_request_get_auth(request)->passdbs; } else { next_passdb = request->passdb->next; } while (next_passdb != NULL && auth_request_want_skip_passdb(request, next_passdb)) next_passdb = next_passdb->next; if (*result == PASSDB_RESULT_OK || *result == PASSDB_RESULT_NEXT) { /* this passdb lookup succeeded, preserve its extra fields */ auth_fields_snapshot(request->extra_fields); request->snapshot_have_userdb_prefetch_set = request->userdb_prefetch_set; if (request->userdb_reply != NULL) auth_fields_snapshot(request->userdb_reply); } else { /* this passdb lookup failed, remove any extra fields it set */ auth_fields_rollback(request->extra_fields); if (request->userdb_reply != NULL) { auth_fields_rollback(request->userdb_reply); request->userdb_prefetch_set = request->snapshot_have_userdb_prefetch_set; } } if (passdb_continue && next_passdb != NULL) { /* try next passdb. */ request->passdb = next_passdb; request->passdb_password = NULL; if (*result == PASSDB_RESULT_USER_UNKNOWN) { /* remember that we did at least one successful passdb lookup */ request->passdbs_seen_user_unknown = TRUE; } else if (*result == PASSDB_RESULT_INTERNAL_FAILURE) { /* remember that we have had an internal failure. at the end return internal failure if we couldn't successfully login. */ request->passdbs_seen_internal_failure = TRUE; } return FALSE; } else if (*result == PASSDB_RESULT_NEXT) { /* admin forgot to put proper passdb last */ auth_request_log_error(request, AUTH_SUBSYS_DB, "Last passdb had noauthenticate field, cannot authenticate user"); *result = PASSDB_RESULT_INTERNAL_FAILURE; } else if (request->passdb_success) { /* either this or a previous passdb lookup succeeded. */ *result = PASSDB_RESULT_OK; } else if (request->passdbs_seen_internal_failure) { /* last passdb lookup returned internal failure. it may have had the correct password, so return internal failure instead of plain failure. */ *result = PASSDB_RESULT_INTERNAL_FAILURE; } return TRUE; } static void auth_request_verify_plain_callback_finish(enum passdb_result result, struct auth_request *request) { passdb_template_export(request->passdb->override_fields_tmpl, request); if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ auth_request_verify_plain(request, request->mech_password, request->private_callback.verify_plain); } else { auth_request_ref(request); request->passdb_result = result; request->private_callback.verify_plain(request->passdb_result, request); auth_request_unref(&request); } } void auth_request_verify_plain_callback(enum passdb_result result, struct auth_request *request) { struct auth_passdb *passdb = request->passdb; i_assert(request->state == AUTH_REQUEST_STATE_PASSDB); auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE); if (result == PASSDB_RESULT_OK && auth_fields_exists(request->extra_fields, "noauthenticate")) result = PASSDB_RESULT_NEXT; if (result != PASSDB_RESULT_INTERNAL_FAILURE) auth_request_save_cache(request, result); else { /* lookup failed. if we're looking here only because the request was expired in cache, fallback to using cached expired record. */ const char *cache_key = passdb->cache_key; auth_request_stats_add_tempfail(request); if (passdb_cache_verify_plain(request, cache_key, request->mech_password, &result, TRUE)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "Falling back to expired data from cache"); } } auth_request_verify_plain_callback_finish(result, request); } static bool password_has_illegal_chars(const char *password) { for (; *password != '\0'; password++) { switch (*password) { case '\001': case '\t': case '\r': case '\n': /* these characters have a special meaning in internal protocols, make sure the password doesn't accidentally get there unescaped. */ return TRUE; } } return FALSE; } static bool auth_request_is_disabled_master_user(struct auth_request *request) { if (request->passdb != NULL) return FALSE; /* no masterdbs, master logins not supported */ i_assert(request->requested_login_user != NULL); auth_request_log_info(request, AUTH_SUBSYS_MECH, "Attempted master login with no master passdbs " "(trying to log in as user: %s)", request->requested_login_user); return TRUE; } static void auth_request_policy_penalty_finish(void *context) { struct auth_policy_check_ctx *ctx = context; if (ctx->request->to_penalty != NULL) timeout_remove(&ctx->request->to_penalty); i_assert(ctx->request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); switch(ctx->type) { case AUTH_POLICY_CHECK_TYPE_PLAIN: auth_request_verify_plain_continue(ctx->request, ctx->callback_plain); return; case AUTH_POLICY_CHECK_TYPE_LOOKUP: auth_request_lookup_credentials_policy_continue(ctx->request, ctx->callback_lookup); return; case AUTH_POLICY_CHECK_TYPE_SUCCESS: auth_request_success_continue(ctx); return; default: i_unreached(); } } static void auth_request_policy_check_callback(int result, void *context) { struct auth_policy_check_ctx *ctx = context; ctx->request->policy_processed = TRUE; if (result == -1) { /* fail it right here and now */ auth_request_fail(ctx->request); } else if (ctx->type != AUTH_POLICY_CHECK_TYPE_SUCCESS && result > 0 && !ctx->request->no_penalty) { ctx->request->to_penalty = timeout_add(result * 1000, auth_request_policy_penalty_finish, context); } else { auth_request_policy_penalty_finish(context); } } void auth_request_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct auth_policy_check_ctx *ctx; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (request->mech_password == NULL) request->mech_password = p_strdup(request->pool, password); else i_assert(request->mech_password == password); request->user_changed_by_lookup = FALSE; if (request->policy_processed) { auth_request_verify_plain_continue(request, callback); } else { ctx = p_new(request->pool, struct auth_policy_check_ctx, 1); ctx->request = request; ctx->callback_plain = callback; ctx->type = AUTH_POLICY_CHECK_TYPE_PLAIN; auth_policy_check(request, request->mech_password, auth_request_policy_check_callback, ctx); } } static void auth_request_verify_plain_continue(struct auth_request *request, verify_plain_callback_t *callback) { struct auth_passdb *passdb; enum passdb_result result; const char *cache_key; const char *password = request->mech_password; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (auth_request_is_disabled_master_user(request)) { callback(PASSDB_RESULT_USER_UNKNOWN, request); return; } if (password_has_illegal_chars(password)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "Attempted login with password having illegal chars"); callback(PASSDB_RESULT_USER_UNKNOWN, request); return; } passdb = request->passdb; while (passdb != NULL && auth_request_want_skip_passdb(request, passdb)) passdb = passdb->next; request->passdb = passdb; if (passdb == NULL) { auth_request_log_error(request, request->mech != NULL ? AUTH_SUBSYS_MECH : "none", "All password databases were skipped"); callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; } request->private_callback.verify_plain = callback; cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; if (passdb_cache_verify_plain(request, cache_key, password, &result, FALSE)) { auth_request_verify_plain_callback_finish(result, request); return; } auth_request_set_state(request, AUTH_REQUEST_STATE_PASSDB); request->credentials_scheme = NULL; if (passdb->passdb->iface.verify_plain == NULL) { /* we're deinitializing and just want to get rid of this request */ auth_request_verify_plain_callback( PASSDB_RESULT_INTERNAL_FAILURE, request); } else if (passdb->passdb->blocking) { passdb_blocking_verify_plain(request); } else { passdb_template_export(passdb->default_fields_tmpl, request); passdb->passdb->iface.verify_plain(request, password, auth_request_verify_plain_callback); } } static void auth_request_lookup_credentials_finish(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *request) { passdb_template_export(request->passdb->override_fields_tmpl, request); if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ if (request->skip_password_check && request->delayed_credentials == NULL && size > 0) { /* passdb continue* rule after a successful lookup. remember these credentials and use them later on. */ unsigned char *dup; dup = p_malloc(request->pool, size); memcpy(dup, credentials, size); request->delayed_credentials = dup; request->delayed_credentials_size = size; } auth_request_lookup_credentials(request, request->credentials_scheme, request->private_callback.lookup_credentials); } else { if (request->delayed_credentials != NULL && size == 0) { /* we did multiple passdb lookups, but the last one didn't provide any credentials (e.g. just wanted to add some extra fields). so use the first passdb's credentials instead. */ credentials = request->delayed_credentials; size = request->delayed_credentials_size; } if (request->set->debug_passwords && result == PASSDB_RESULT_OK) { auth_request_log_debug(request, AUTH_SUBSYS_DB, "Credentials: %s", binary_to_hex(credentials, size)); } if (result == PASSDB_RESULT_SCHEME_NOT_AVAILABLE && request->passdbs_seen_user_unknown) { /* one of the passdbs accepted the scheme, but the user was unknown there */ result = PASSDB_RESULT_USER_UNKNOWN; } request->passdb_result = result; request->private_callback. lookup_credentials(result, credentials, size, request); } } void auth_request_lookup_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *request) { struct auth_passdb *passdb = request->passdb; const char *cache_cred, *cache_scheme; i_assert(request->state == AUTH_REQUEST_STATE_PASSDB); auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE); if (result == PASSDB_RESULT_OK && auth_fields_exists(request->extra_fields, "noauthenticate")) result = PASSDB_RESULT_NEXT; if (result != PASSDB_RESULT_INTERNAL_FAILURE) auth_request_save_cache(request, result); else { /* lookup failed. if we're looking here only because the request was expired in cache, fallback to using cached expired record. */ const char *cache_key = passdb->cache_key; auth_request_stats_add_tempfail(request); if (passdb_cache_lookup_credentials(request, cache_key, &cache_cred, &cache_scheme, &result, TRUE)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "Falling back to expired data from cache"); passdb_handle_credentials( result, cache_cred, cache_scheme, auth_request_lookup_credentials_finish, request); return; } } auth_request_lookup_credentials_finish(result, credentials, size, request); } void auth_request_lookup_credentials(struct auth_request *request, const char *scheme, lookup_credentials_callback_t *callback) { struct auth_policy_check_ctx *ctx; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (request->credentials_scheme == NULL) request->credentials_scheme = p_strdup(request->pool, scheme); request->user_changed_by_lookup = FALSE; if (request->policy_processed) auth_request_lookup_credentials_policy_continue(request, callback); else { ctx = p_new(request->pool, struct auth_policy_check_ctx, 1); ctx->request = request; ctx->callback_lookup = callback; ctx->type = AUTH_POLICY_CHECK_TYPE_LOOKUP; auth_policy_check(request, ctx->request->mech_password, auth_request_policy_check_callback, ctx); } } static void auth_request_lookup_credentials_policy_continue(struct auth_request *request, lookup_credentials_callback_t *callback) { struct auth_passdb *passdb; const char *cache_key, *cache_cred, *cache_scheme; enum passdb_result result; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); if (auth_request_is_disabled_master_user(request)) { callback(PASSDB_RESULT_USER_UNKNOWN, NULL, 0, request); return; } passdb = request->passdb; while (passdb != NULL && auth_request_want_skip_passdb(request, passdb)) passdb = passdb->next; request->passdb = passdb; if (passdb == NULL) { auth_request_log_error(request, request->mech != NULL ? AUTH_SUBSYS_MECH : "none", "All password databases were skipped"); callback(PASSDB_RESULT_INTERNAL_FAILURE, NULL, 0, request); return; } request->private_callback.lookup_credentials = callback; cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; if (cache_key != NULL) { if (passdb_cache_lookup_credentials(request, cache_key, &cache_cred, &cache_scheme, &result, FALSE)) { passdb_handle_credentials( result, cache_cred, cache_scheme, auth_request_lookup_credentials_finish, request); return; } } auth_request_set_state(request, AUTH_REQUEST_STATE_PASSDB); if (passdb->passdb->iface.lookup_credentials == NULL) { /* this passdb doesn't support credentials */ auth_request_log_debug(request, AUTH_SUBSYS_DB, "passdb doesn't support credential lookups"); auth_request_lookup_credentials_callback( PASSDB_RESULT_SCHEME_NOT_AVAILABLE, &uchar_nul, 0, request); } else if (passdb->passdb->blocking) { passdb_blocking_lookup_credentials(request); } else { passdb_template_export(passdb->default_fields_tmpl, request); passdb->passdb->iface.lookup_credentials(request, auth_request_lookup_credentials_callback); } } void auth_request_set_credentials(struct auth_request *request, const char *scheme, const char *data, set_credentials_callback_t *callback) { struct auth_passdb *passdb = request->passdb; const char *cache_key, *new_credentials; cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; if (cache_key != NULL) auth_cache_remove(passdb_cache, request, cache_key); request->private_callback.set_credentials = callback; new_credentials = t_strdup_printf("{%s}%s", scheme, data); if (passdb->passdb->blocking) passdb_blocking_set_credentials(request, new_credentials); else if (passdb->passdb->iface.set_credentials != NULL) { passdb->passdb->iface.set_credentials(request, new_credentials, callback); } else { /* this passdb doesn't support credentials update */ callback(FALSE, request); } } static void auth_request_userdb_save_cache(struct auth_request *request, enum userdb_result result) { struct auth_userdb *userdb = request->userdb; string_t *str; const char *cache_value; if (passdb_cache == NULL || userdb->cache_key == NULL) return; if (result == USERDB_RESULT_USER_UNKNOWN) cache_value = ""; else { str = t_str_new(128); auth_fields_append(request->userdb_reply, str, AUTH_FIELD_FLAG_CHANGED, AUTH_FIELD_FLAG_CHANGED); if (request->user_changed_by_lookup) { /* username was changed by passdb or userdb */ if (str_len(str) > 0) str_append_c(str, '\t'); str_append(str, "user="); str_append_tabescaped(str, request->user); } if (str_len(str) == 0) { /* no userdb fields. but we can't save an empty string, since that means "user unknown". */ str_append(str, AUTH_REQUEST_USER_KEY_IGNORE); } cache_value = str_c(str); } /* last_success has no meaning with userdb */ auth_cache_insert(passdb_cache, request, userdb->cache_key, cache_value, FALSE); } static bool auth_request_lookup_user_cache(struct auth_request *request, const char *key, enum userdb_result *result_r, bool use_expired) { struct auth_stats *stats = auth_request_stats_get(request); const char *value; struct auth_cache_node *node; bool expired, neg_expired; value = auth_cache_lookup(passdb_cache, request, key, &node, &expired, &neg_expired); if (value == NULL || (expired && !use_expired)) { stats->auth_cache_miss_count++; auth_request_log_debug(request, AUTH_SUBSYS_DB, value == NULL ? "userdb cache miss" : "userdb cache expired"); return FALSE; } stats->auth_cache_hit_count++; auth_request_log_debug(request, AUTH_SUBSYS_DB, "userdb cache hit: %s", value); if (*value == '\0') { /* negative cache entry */ *result_r = USERDB_RESULT_USER_UNKNOWN; request->userdb_reply = auth_fields_init(request->pool); return TRUE; } /* We want to preserve any userdb fields set by the earlier passdb lookup, so initialize userdb_reply only if it doesn't exist. Don't use auth_request_init_userdb_reply(), because the entire userdb part of the result comes from the cache so we don't want to initialize it with default_fields. */ if (request->userdb_reply == NULL) request->userdb_reply = auth_fields_init(request->pool); auth_request_userdb_import(request, value); *result_r = USERDB_RESULT_OK; return TRUE; } void auth_request_userdb_callback(enum userdb_result result, struct auth_request *request) { struct auth_userdb *userdb = request->userdb; struct auth_userdb *next_userdb; enum auth_db_rule result_rule; bool userdb_continue = FALSE; switch (result) { case USERDB_RESULT_OK: result_rule = userdb->result_success; break; case USERDB_RESULT_INTERNAL_FAILURE: auth_request_stats_add_tempfail(request); result_rule = userdb->result_internalfail; break; case USERDB_RESULT_USER_UNKNOWN: default: result_rule = userdb->result_failure; break; } switch (result_rule) { case AUTH_DB_RULE_RETURN: break; case AUTH_DB_RULE_RETURN_OK: request->userdb_success = TRUE; break; case AUTH_DB_RULE_RETURN_FAIL: request->userdb_success = FALSE; break; case AUTH_DB_RULE_CONTINUE: userdb_continue = TRUE; break; case AUTH_DB_RULE_CONTINUE_OK: userdb_continue = TRUE; request->userdb_success = TRUE; break; case AUTH_DB_RULE_CONTINUE_FAIL: userdb_continue = TRUE; request->userdb_success = FALSE; break; } next_userdb = userdb->next; while (next_userdb != NULL && auth_request_want_skip_userdb(request, next_userdb)) next_userdb = next_userdb->next; if (userdb_continue && next_userdb != NULL) { /* try next userdb. */ if (result == USERDB_RESULT_INTERNAL_FAILURE) request->userdbs_seen_internal_failure = TRUE; if (result == USERDB_RESULT_OK) { /* this userdb lookup succeeded, preserve its extra fields */ userdb_template_export(userdb->override_fields_tmpl, request); auth_fields_snapshot(request->userdb_reply); } else { /* this userdb lookup failed, remove any extra fields it set */ auth_fields_rollback(request->userdb_reply); } request->user_changed_by_lookup = FALSE; request->userdb = next_userdb; auth_request_lookup_user(request, request->private_callback.userdb); return; } if (request->userdb_success) { result = USERDB_RESULT_OK; userdb_template_export(userdb->override_fields_tmpl, request); } else if (request->userdbs_seen_internal_failure || result == USERDB_RESULT_INTERNAL_FAILURE) { /* one of the userdb lookups failed. the user might have been in there, so this is an internal failure */ result = USERDB_RESULT_INTERNAL_FAILURE; } else if (request->client_pid != 0) { /* this was an actual login attempt, the user should have been found. */ if (auth_request_get_auth(request)->userdbs->next == NULL) { auth_request_log_error(request, AUTH_SUBSYS_DB, "user not found from userdb"); } else { auth_request_log_error(request, AUTH_SUBSYS_MECH, "user not found from any userdbs"); } result = USERDB_RESULT_USER_UNKNOWN; } else { result = USERDB_RESULT_USER_UNKNOWN; } if (request->userdb_lookup_tempfailed) { /* no caching */ } else if (result != USERDB_RESULT_INTERNAL_FAILURE) { if (!request->userdb_result_from_cache) auth_request_userdb_save_cache(request, result); } else if (passdb_cache != NULL && userdb->cache_key != NULL) { /* lookup failed. if we're looking here only because the request was expired in cache, fallback to using cached expired record. */ const char *cache_key = userdb->cache_key; if (auth_request_lookup_user_cache(request, cache_key, &result, TRUE)) { auth_request_log_info(request, AUTH_SUBSYS_DB, "Falling back to expired data from cache"); } } request->private_callback.userdb(result, request); } void auth_request_lookup_user(struct auth_request *request, userdb_callback_t *callback) { struct auth_userdb *userdb = request->userdb; const char *cache_key; request->private_callback.userdb = callback; request->user_changed_by_lookup = FALSE; request->userdb_lookup = TRUE; request->userdb_result_from_cache = FALSE; if (request->userdb_reply == NULL) auth_request_init_userdb_reply(request); else { /* we still want to set default_fields. these override any existing fields set by previous userdbs (because if that is unwanted, ":protected" can be used). */ userdb_template_export(userdb->default_fields_tmpl, request); } /* (for now) auth_cache is shared between passdb and userdb */ cache_key = passdb_cache == NULL ? NULL : userdb->cache_key; if (cache_key != NULL) { enum userdb_result result; if (auth_request_lookup_user_cache(request, cache_key, &result, FALSE)) { request->userdb_result_from_cache = TRUE; auth_request_userdb_callback(result, request); return; } } if (userdb->userdb->iface->lookup == NULL) { /* we are deinitializing */ auth_request_userdb_callback(USERDB_RESULT_INTERNAL_FAILURE, request); } else if (userdb->userdb->blocking) userdb_blocking_lookup(request); else userdb->userdb->iface->lookup(request, auth_request_userdb_callback); } static char * auth_request_fix_username(struct auth_request *request, const char *username, const char **error_r) { const struct auth_settings *set = request->set; unsigned char *p; char *user; if (*set->default_realm != '\0' && strchr(username, '@') == NULL) { user = p_strconcat(request->pool, username, "@", set->default_realm, NULL); } else { user = p_strdup(request->pool, username); } for (p = (unsigned char *)user; *p != '\0'; p++) { if (set->username_translation_map[*p & 0xff] != 0) *p = set->username_translation_map[*p & 0xff]; if (set->username_chars_map[*p & 0xff] == 0) { *error_r = t_strdup_printf( "Username character disallowed by auth_username_chars: " "0x%02x (username: %s)", *p, str_sanitize(username, 128)); return NULL; } } if (*set->username_format != '\0') { /* username format given, put it through variable expansion. we'll have to temporarily replace request->user to get %u to be the wanted username */ char *old_username; string_t *dest; old_username = request->user; request->user = user; dest = t_str_new(256); auth_request_var_expand(dest, set->username_format, request, NULL); user = p_strdup(request->pool, str_c(dest)); request->user = old_username; } if (user[0] == '\0') { /* Some PAM plugins go nuts with empty usernames */ *error_r = "Empty username"; return NULL; } return user; } bool auth_request_set_username(struct auth_request *request, const char *username, const char **error_r) { const struct auth_settings *set = request->set; const char *p, *login_username = NULL; if (*set->master_user_separator != '\0' && !request->userdb_lookup) { /* check if the username contains a master user */ p = strchr(username, *set->master_user_separator); if (p != NULL) { /* it does, set it. */ login_username = t_strdup_until(username, p); /* username is the master user */ username = p + 1; } } if (request->original_username == NULL) { /* the username may change later, but we need to use this username when verifying at least DIGEST-MD5 password. */ request->original_username = p_strdup(request->pool, username); } if (request->cert_username) { /* cert_username overrides the username given by authentication mechanism. but still do checks and translations to it. */ username = request->user; } request->user = auth_request_fix_username(request, username, error_r); if (request->user == NULL) return FALSE; if (request->translated_username == NULL) { /* similar to original_username, but after translations */ request->translated_username = request->user; } request->user_changed_by_lookup = TRUE; if (login_username != NULL) { if (!auth_request_set_login_username(request, login_username, error_r)) return FALSE; } return TRUE; } bool auth_request_set_login_username(struct auth_request *request, const char *username, const char **error_r) { struct auth_passdb *master_passdb; if (username[0] == '\0') { *error_r = "Master user login attempted to use empty login username"; return FALSE; } if (strcmp(username, request->user) == 0) { /* The usernames are the same, we don't really wish to log in as someone else */ return TRUE; } /* lookup request->user from masterdb first */ master_passdb = auth_request_get_auth(request)->masterdbs; if (master_passdb == NULL) { *error_r = "Master user login attempted without master passdbs"; return FALSE; } request->passdb = master_passdb; request->requested_login_user = auth_request_fix_username(request, username, error_r); if (request->requested_login_user == NULL) return FALSE; auth_request_log_debug(request, AUTH_SUBSYS_DB, "Master user lookup for login: %s", request->requested_login_user); return TRUE; } static void auth_request_validate_networks(struct auth_request *request, const char *name, const char *networks, const struct ip_addr *remote_ip) { const char *const *net; struct ip_addr net_ip; unsigned int bits; bool found = FALSE; for (net = t_strsplit_spaces(networks, ", "); *net != NULL; net++) { auth_request_log_debug(request, AUTH_SUBSYS_DB, "%s: Matching for network %s", name, *net); if (strcmp(*net, "local") == 0) { if (remote_ip->family == 0) { found = TRUE; break; } } else if (net_parse_range(*net, &net_ip, &bits) < 0) { auth_request_log_info(request, AUTH_SUBSYS_DB, "%s: Invalid network '%s'", name, *net); } else if (remote_ip->family != 0 && net_is_in_network(remote_ip, &net_ip, bits)) { found = TRUE; break; } } if (found) ; else if (remote_ip->family == 0) { auth_request_log_info(request, AUTH_SUBSYS_DB, "%s check failed: Remote IP not known and 'local' missing", name); } else { auth_request_log_info(request, AUTH_SUBSYS_DB, "%s check failed: IP %s not in allowed networks", name, net_ip2addr(remote_ip)); } if (!found) request->failed = TRUE; } static void auth_request_set_password(struct auth_request *request, const char *value, const char *default_scheme, bool noscheme) { if (request->passdb_password != NULL) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Multiple password values not supported"); return; } /* if the password starts with '{' it most likely contains also '}'. check it anyway to make sure, because we assert-crash later if it doesn't exist. this could happen if plaintext passwords are used. */ if (*value == '{' && !noscheme && strchr(value, '}') != NULL) request->passdb_password = p_strdup(request->pool, value); else { i_assert(default_scheme != NULL); request->passdb_password = p_strdup_printf(request->pool, "{%s}%s", default_scheme, value); } } static const char * get_updated_username(const char *old_username, const char *name, const char *value) { const char *p; if (strcmp(name, "user") == 0) { /* replace the whole username */ return value; } p = strchr(old_username, '@'); if (strcmp(name, "username") == 0) { if (strchr(value, '@') != NULL) return value; /* preserve the current @domain */ return t_strconcat(value, p, NULL); } if (strcmp(name, "domain") == 0) { if (p == NULL) { /* add the domain */ return t_strconcat(old_username, "@", value, NULL); } else { /* replace the existing domain */ p = t_strdup_until(old_username, p + 1); return t_strconcat(p, value, NULL); } } return NULL; } static bool auth_request_try_update_username(struct auth_request *request, const char *name, const char *value) { const char *new_value; new_value = get_updated_username(request->user, name, value); if (new_value == NULL) return FALSE; if (new_value[0] == '\0') { auth_request_log_error(request, AUTH_SUBSYS_DB, "username attempted to be changed to empty"); request->failed = TRUE; return TRUE; } if (strcmp(request->user, new_value) != 0) { auth_request_log_debug(request, AUTH_SUBSYS_DB, "username changed %s -> %s", request->user, new_value); request->user = p_strdup(request->pool, new_value); request->user_changed_by_lookup = TRUE; } return TRUE; } static void auth_request_passdb_import(struct auth_request *request, const char *args, const char *key_prefix, const char *default_scheme) { const char *const *arg, *field; for (arg = t_strsplit(args, "\t"); *arg != NULL; arg++) { field = t_strconcat(key_prefix, *arg, NULL); auth_request_set_field_keyvalue(request, field, default_scheme); } } void auth_request_set_field(struct auth_request *request, const char *name, const char *value, const char *default_scheme) { size_t name_len = strlen(name); i_assert(*name != '\0'); i_assert(value != NULL); i_assert(request->passdb != NULL); if (name_len > 10 && strcmp(name+name_len-10, ":protected") == 0) { /* set this field only if it hasn't been set before */ name = t_strndup(name, name_len-10); if (auth_fields_exists(request->extra_fields, name)) return; } else if (name_len > 7 && strcmp(name+name_len-7, ":remove") == 0) { /* remove this field entirely */ name = t_strndup(name, name_len-7); auth_fields_remove(request->extra_fields, name); return; } if (strcmp(name, "password") == 0) { auth_request_set_password(request, value, default_scheme, FALSE); return; } if (strcmp(name, "password_noscheme") == 0) { auth_request_set_password(request, value, default_scheme, TRUE); return; } if (auth_request_try_update_username(request, name, value)) { /* don't change the original value so it gets saved correctly to cache. */ } else if (strcmp(name, "login_user") == 0) { request->requested_login_user = p_strdup(request->pool, value); } else if (strcmp(name, "allow_nets") == 0) { auth_request_validate_networks(request, name, value, &request->remote_ip); } else if (strcmp(name, "fail") == 0) { request->failed = TRUE; } else if (strcmp(name, "delay_until") == 0) { time_t timestamp; unsigned int extra_secs = 0; const char *p; p = strchr(value, '+'); if (p != NULL) { value = t_strdup_until(value, p++); if (str_to_uint(p, &extra_secs) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Invalid delay_until randomness number '%s'", p); request->failed = TRUE; } else { extra_secs = rand() % extra_secs; } } if (str_to_time(value, ×tamp) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Invalid delay_until timestamp '%s'", value); request->failed = TRUE; } else if (timestamp <= ioloop_time) { /* no more delays */ } else if (timestamp - ioloop_time > AUTH_REQUEST_MAX_DELAY_SECS) { auth_request_log_error(request, AUTH_SUBSYS_DB, "delay_until timestamp %s is too much in the future, failing", value); request->failed = TRUE; } else { /* add randomness, but not too much of it */ timestamp += extra_secs; if (timestamp - ioloop_time > AUTH_REQUEST_MAX_DELAY_SECS) timestamp = ioloop_time + AUTH_REQUEST_MAX_DELAY_SECS; request->delay_until = timestamp; } } else if (strcmp(name, "allow_real_nets") == 0) { auth_request_validate_networks(request, name, value, &request->real_remote_ip); } else if (strncmp(name, "userdb_", 7) == 0) { /* for prefetch userdb */ request->userdb_prefetch_set = TRUE; if (request->userdb_reply == NULL) auth_request_init_userdb_reply(request); if (strcmp(name, "userdb_userdb_import") == 0) { /* we can't put the whole userdb_userdb_import value to extra_cache_fields or it doesn't work properly. so handle this explicitly. */ auth_request_passdb_import(request, value, "userdb_", default_scheme); return; } auth_request_set_userdb_field(request, name + 7, value); } else if (strcmp(name, "noauthenticate") == 0) { /* add "nopassword" also so that passdbs won't try to verify the password. */ auth_fields_add(request->extra_fields, name, value, 0); auth_fields_add(request->extra_fields, "nopassword", NULL, 0); } else if (strcmp(name, "nopassword") == 0) { /* NULL password - anything goes */ const char *password = request->passdb_password; if (password != NULL && !auth_fields_exists(request->extra_fields, "noauthenticate")) { (void)password_get_scheme(&password); if (*password != '\0') { auth_request_log_error(request, AUTH_SUBSYS_DB, "nopassword set but password is " "non-empty"); return; } } request->passdb_password = NULL; auth_fields_add(request->extra_fields, name, value, 0); return; } else if (strcmp(name, "passdb_import") == 0) { auth_request_passdb_import(request, value, "", default_scheme); return; } else { /* these fields are returned to client */ auth_fields_add(request->extra_fields, name, value, 0); return; } /* add the field unconditionally to extra_fields. this is required if a) auth cache is used, b) if we're a worker and we'll need to send this to the main auth process that can store it in the cache, c) for easily checking :protected fields' existence. */ auth_fields_add(request->extra_fields, name, value, AUTH_FIELD_FLAG_HIDDEN); } void auth_request_set_null_field(struct auth_request *request, const char *name) { if (strncmp(name, "userdb_", 7) == 0) { /* make sure userdb prefetch is used even if all the fields were returned as NULL. */ request->userdb_prefetch_set = TRUE; } } void auth_request_set_field_keyvalue(struct auth_request *request, const char *field, const char *default_scheme) { const char *key, *value; value = strchr(field, '='); if (value == NULL) { key = field; value = ""; } else { key = t_strdup_until(field, value); value++; } auth_request_set_field(request, key, value, default_scheme); } void auth_request_set_fields(struct auth_request *request, const char *const *fields, const char *default_scheme) { for (; *fields != NULL; fields++) { if (**fields == '\0') continue; auth_request_set_field_keyvalue(request, *fields, default_scheme); } } void auth_request_init_userdb_reply(struct auth_request *request) { request->userdb_reply = auth_fields_init(request->pool); userdb_template_export(request->userdb->default_fields_tmpl, request); } static void auth_request_set_uidgid_file(struct auth_request *request, const char *path_template) { string_t *path; struct stat st; path = t_str_new(256); auth_request_var_expand(path, path_template, request, NULL); if (stat(str_c(path), &st) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "stat(%s) failed: %m", str_c(path)); request->userdb_lookup_tempfailed = TRUE; } else { auth_fields_add(request->userdb_reply, "uid", dec2str(st.st_uid), 0); auth_fields_add(request->userdb_reply, "gid", dec2str(st.st_gid), 0); } } static void auth_request_userdb_import(struct auth_request *request, const char *args) { const char *key, *value, *const *arg; for (arg = t_strsplit(args, "\t"); *arg != NULL; arg++) { value = strchr(*arg, '='); if (value == NULL) { key = *arg; value = ""; } else { key = t_strdup_until(*arg, value); value++; } auth_request_set_userdb_field(request, key, value); } } void auth_request_set_userdb_field(struct auth_request *request, const char *name, const char *value) { size_t name_len = strlen(name); uid_t uid; gid_t gid; i_assert(value != NULL); if (name_len > 10 && strcmp(name+name_len-10, ":protected") == 0) { /* set this field only if it hasn't been set before */ name = t_strndup(name, name_len-10); if (auth_fields_exists(request->userdb_reply, name)) return; } else if (name_len > 7 && strcmp(name+name_len-7, ":remove") == 0) { /* remove this field entirely */ name = t_strndup(name, name_len-7); auth_fields_remove(request->userdb_reply, name); return; } if (strcmp(name, "uid") == 0) { uid = userdb_parse_uid(request, value); if (uid == (uid_t)-1) { request->userdb_lookup_tempfailed = TRUE; return; } value = dec2str(uid); } else if (strcmp(name, "gid") == 0) { gid = userdb_parse_gid(request, value); if (gid == (gid_t)-1) { request->userdb_lookup_tempfailed = TRUE; return; } value = dec2str(gid); } else if (strcmp(name, "tempfail") == 0) { request->userdb_lookup_tempfailed = TRUE; return; } else if (auth_request_try_update_username(request, name, value)) { return; } else if (strcmp(name, "uidgid_file") == 0) { auth_request_set_uidgid_file(request, value); return; } else if (strcmp(name, "userdb_import") == 0) { auth_request_userdb_import(request, value); return; } else if (strcmp(name, "system_user") == 0) { /* FIXME: the system_user is for backwards compatibility */ static bool warned = FALSE; if (!warned) { i_warning("userdb: Replace system_user with system_groups_user"); warned = TRUE; } name = "system_groups_user"; } else if (strcmp(name, AUTH_REQUEST_USER_KEY_IGNORE) == 0) { return; } auth_fields_add(request->userdb_reply, name, value, 0); } void auth_request_set_userdb_field_values(struct auth_request *request, const char *name, const char *const *values) { if (*values == NULL) return; if (strcmp(name, "gid") == 0) { /* convert gids to comma separated list */ string_t *value; gid_t gid; value = t_str_new(128); for (; *values != NULL; values++) { gid = userdb_parse_gid(request, *values); if (gid == (gid_t)-1) { request->userdb_lookup_tempfailed = TRUE; return; } if (str_len(value) > 0) str_append_c(value, ','); str_append(value, dec2str(gid)); } auth_fields_add(request->userdb_reply, name, str_c(value), 0); } else { /* add only one */ if (values[1] != NULL) { auth_request_log_warning(request, AUTH_SUBSYS_DB, "Multiple values found for '%s', " "using value '%s'", name, *values); } auth_request_set_userdb_field(request, name, *values); } } static bool auth_request_proxy_is_self(struct auth_request *request) { const char *port = NULL; /* check if the port is the same */ port = auth_fields_find(request->extra_fields, "port"); if (port != NULL && !str_uint_equals(port, request->local_port)) return FALSE; /* don't check destuser. in some systems destuser is intentionally changed to proxied connections, but that shouldn't affect the proxying decision. it's unlikely any systems would actually want to proxy a connection to itself only to change the username, since it can already be done without proxying by changing the "user" field. */ return TRUE; } static bool auth_request_proxy_ip_is_self(struct auth_request *request, const struct ip_addr *ip) { unsigned int i; if (net_ip_compare(ip, &request->real_local_ip)) return TRUE; for (i = 0; request->set->proxy_self_ips[i].family != 0; i++) { if (net_ip_compare(ip, &request->set->proxy_self_ips[i])) return TRUE; } return FALSE; } static void auth_request_proxy_finish_ip(struct auth_request *request, bool proxy_host_is_self) { if (!auth_fields_exists(request->extra_fields, "proxy_maybe")) { /* proxying */ } else if (!proxy_host_is_self || !auth_request_proxy_is_self(request)) { /* proxy destination isn't ourself - proxy */ auth_fields_remove(request->extra_fields, "proxy_maybe"); auth_fields_add(request->extra_fields, "proxy", NULL, 0); } else { /* proxying to ourself - log in without proxying by dropping all the proxying fields. */ bool proxy_always = auth_fields_exists(request->extra_fields, "proxy_always"); auth_request_proxy_finish_failure(request); if (proxy_always) { /* setup where "self" refers to the local director cluster, while "non-self" refers to remote clusters. we've matched self here, so add proxy field and let director fill the host. */ auth_fields_add(request->extra_fields, "proxy", NULL, 0); } } } static void auth_request_proxy_dns_callback(const struct dns_lookup_result *result, struct auth_request_proxy_dns_lookup_ctx *ctx) { struct auth_request *request = ctx->request; const char *host; unsigned int i; bool proxy_host_is_self; request->dns_lookup_ctx = NULL; ctx->dns_lookup = NULL; host = auth_fields_find(request->extra_fields, "host"); i_assert(host != NULL); if (result->ret != 0) { auth_request_log_error(request, AUTH_SUBSYS_PROXY, "DNS lookup for %s failed: %s", host, result->error); request->internal_failure = TRUE; auth_request_proxy_finish_failure(request); } else { if (result->msecs > AUTH_DNS_WARN_MSECS) { auth_request_log_warning(request, AUTH_SUBSYS_PROXY, "DNS lookup for %s took %u.%03u s", host, result->msecs/1000, result->msecs % 1000); } auth_fields_add(request->extra_fields, "hostip", net_ip2addr(&result->ips[0]), 0); proxy_host_is_self = FALSE; for (i = 0; i < result->ips_count; i++) { if (auth_request_proxy_ip_is_self(request, &result->ips[i])) { proxy_host_is_self = TRUE; break; } } auth_request_proxy_finish_ip(request, proxy_host_is_self); } if (ctx->callback != NULL) ctx->callback(result->ret == 0, request); auth_request_unref(&request); } static int auth_request_proxy_host_lookup(struct auth_request *request, const char *host, auth_request_proxy_cb_t *callback) { struct auth_request_proxy_dns_lookup_ctx *ctx; struct dns_lookup_settings dns_set; const char *value; unsigned int secs; /* need to do dns lookup for the host */ i_zero(&dns_set); dns_set.dns_client_socket_path = AUTH_DNS_SOCKET_PATH; dns_set.timeout_msecs = AUTH_DNS_DEFAULT_TIMEOUT_MSECS; value = auth_fields_find(request->extra_fields, "proxy_timeout"); if (value != NULL) { if (str_to_uint(value, &secs) < 0) { auth_request_log_error(request, AUTH_SUBSYS_PROXY, "Invalid proxy_timeout value: %s", value); } else { dns_set.timeout_msecs = secs*1000; } } ctx = p_new(request->pool, struct auth_request_proxy_dns_lookup_ctx, 1); ctx->request = request; auth_request_ref(request); request->dns_lookup_ctx = ctx; if (dns_lookup(host, &dns_set, auth_request_proxy_dns_callback, ctx, &ctx->dns_lookup) < 0) { /* failed early */ return -1; } ctx->callback = callback; return 0; } int auth_request_proxy_finish(struct auth_request *request, auth_request_proxy_cb_t *callback) { const char *host, *hostip; struct ip_addr ip; bool proxy_host_is_self; if (request->auth_only) return 1; if (!auth_fields_exists(request->extra_fields, "proxy") && !auth_fields_exists(request->extra_fields, "proxy_maybe")) return 1; host = auth_fields_find(request->extra_fields, "host"); if (host == NULL) { /* director can set the host. give it access to lip and lport so it can also perform proxy_maybe internally */ proxy_host_is_self = FALSE; if (request->local_ip.family != 0) { auth_fields_add(request->extra_fields, "lip", net_ip2addr(&request->local_ip), 0); } if (request->local_port != 0) { auth_fields_add(request->extra_fields, "lport", dec2str(request->local_port), 0); } } else if (net_addr2ip(host, &ip) == 0) { proxy_host_is_self = auth_request_proxy_ip_is_self(request, &ip); } else { hostip = auth_fields_find(request->extra_fields, "hostip"); if (hostip != NULL && net_addr2ip(hostip, &ip) < 0) { auth_request_log_error(request, AUTH_SUBSYS_PROXY, "Invalid hostip in passdb: %s", hostip); return -1; } if (hostip == NULL) { /* asynchronous host lookup */ return auth_request_proxy_host_lookup(request, host, callback); } proxy_host_is_self = auth_request_proxy_ip_is_self(request, &ip); } auth_request_proxy_finish_ip(request, proxy_host_is_self); return 1; } void auth_request_proxy_finish_failure(struct auth_request *request) { /* drop all proxying fields */ auth_fields_remove(request->extra_fields, "proxy"); auth_fields_remove(request->extra_fields, "proxy_maybe"); auth_fields_remove(request->extra_fields, "proxy_always"); auth_fields_remove(request->extra_fields, "host"); auth_fields_remove(request->extra_fields, "port"); auth_fields_remove(request->extra_fields, "destuser"); } static void log_password_failure(struct auth_request *request, const char *plain_password, const char *crypted_password, const char *scheme, const char *user, const char *subsystem) { static bool scheme_ok = FALSE; string_t *str = t_str_new(256); const char *working_scheme; str_printfa(str, "%s(%s) != '%s'", scheme, plain_password, crypted_password); if (!scheme_ok) { /* perhaps the scheme is wrong - see if we can find a working one */ working_scheme = password_scheme_detect(plain_password, crypted_password, user); if (working_scheme != NULL) { str_printfa(str, ", try %s scheme instead", working_scheme); } } auth_request_log_debug(request, subsystem, "%s", str_c(str)); } static void auth_request_append_password(struct auth_request *request, string_t *str) { const char *p, *log_type = request->set->verbose_passwords; unsigned int max_len = 1024; if (request->mech_password == NULL) return; p = strchr(log_type, ':'); if (p != NULL) { if (str_to_uint(p+1, &max_len) < 0) i_unreached(); log_type = t_strdup_until(log_type, p); } if (strcmp(log_type, "plain") == 0) { str_printfa(str, "(given password: %s)", t_strndup(request->mech_password, max_len)); } else if (strcmp(log_type, "sha1") == 0) { unsigned char sha1[SHA1_RESULTLEN]; sha1_get_digest(request->mech_password, strlen(request->mech_password), sha1); str_printfa(str, "(SHA1 of given password: %s)", t_strndup(binary_to_hex(sha1, sizeof(sha1)), max_len)); } else { i_unreached(); } } void auth_request_log_password_mismatch(struct auth_request *request, const char *subsystem) { string_t *str; if (strcmp(request->set->verbose_passwords, "no") == 0) { auth_request_log_info(request, subsystem, "Password mismatch"); return; } str = t_str_new(128); get_log_prefix(str, request, subsystem); str_append(str, "Password mismatch "); auth_request_append_password(request, str); i_info("%s", str_c(str)); } void auth_request_log_unknown_user(struct auth_request *request, const char *subsystem) { string_t *str; if (strcmp(request->set->verbose_passwords, "no") == 0 || !request->set->verbose) { auth_request_log_info(request, subsystem, "unknown user"); return; } str = t_str_new(128); get_log_prefix(str, request, subsystem); str_append(str, "unknown user "); auth_request_append_password(request, str); if (request->userdb_lookup) { if (request->userdb->next != NULL) str_append(str, " - trying the next userdb"); } else { if (request->passdb->next != NULL) str_append(str, " - trying the next passdb"); } i_info("%s", str_c(str)); } int auth_request_password_verify(struct auth_request *request, const char *plain_password, const char *crypted_password, const char *scheme, const char *subsystem) { const unsigned char *raw_password; size_t raw_password_size; const char *error; int ret; if (request->skip_password_check) { /* passdb continue* rule after a successful authentication */ return 1; } if (request->passdb->set->deny) { /* this is a deny database, we don't care about the password */ return 0; } if (auth_fields_exists(request->extra_fields, "nopassword")) { auth_request_log_debug(request, subsystem, "Allowing any password"); return 1; } ret = password_decode(crypted_password, scheme, &raw_password, &raw_password_size, &error); if (ret <= 0) { if (ret < 0) { auth_request_log_error(request, subsystem, "Password data is not valid for scheme %s: %s", scheme, error); } else { auth_request_log_error(request, subsystem, "Unknown scheme %s", scheme); } return -1; } /* Use original_username since it may be important for some password schemes (eg. digest-md5). Otherwise the username is used only for logging purposes. */ ret = password_verify(plain_password, request->original_username, scheme, raw_password, raw_password_size, &error); if (ret < 0) { const char *password_str = request->set->debug_passwords ? t_strdup_printf(" '%s'", crypted_password) : ""; auth_request_log_error(request, subsystem, "Invalid password%s in passdb: %s", password_str, error); } else if (ret == 0) { auth_request_log_password_mismatch(request, subsystem); } if (ret <= 0 && request->set->debug_passwords) T_BEGIN { log_password_failure(request, plain_password, crypted_password, scheme, request->original_username, subsystem); } T_END; return ret; } static void get_log_prefix(string_t *str, struct auth_request *auth_request, const char *subsystem) { #define MAX_LOG_USERNAME_LEN 64 const char *ip, *name; if (subsystem == AUTH_SUBSYS_DB) { if (!auth_request->userdb_lookup) { i_assert(auth_request->passdb != NULL); name = auth_request->passdb->set->name[0] != '\0' ? auth_request->passdb->set->name : auth_request->passdb->passdb->iface.name; } else { i_assert(auth_request->userdb != NULL); name = auth_request->userdb->set->name[0] != '\0' ? auth_request->userdb->set->name : auth_request->userdb->userdb->iface->name; } } else if (subsystem == AUTH_SUBSYS_MECH) { i_assert(auth_request->mech != NULL); name = t_str_lcase(auth_request->mech->mech_name); } else { name = subsystem; } str_append(str, name); str_append_c(str, '('); if (auth_request->user == NULL) str_append(str, "?"); else { str_sanitize_append(str, auth_request->user, MAX_LOG_USERNAME_LEN); } ip = net_ip2addr(&auth_request->remote_ip); if (ip[0] != '\0') { str_append_c(str, ','); str_append(str, ip); } if (auth_request->requested_login_user != NULL) str_append(str, ",master"); if (auth_request->session_id != NULL) str_printfa(str, ",<%s>", auth_request->session_id); str_append(str, "): "); } static const char * ATTR_FORMAT(3, 0) get_log_str(struct auth_request *auth_request, const char *subsystem, const char *format, va_list va) { string_t *str; str = t_str_new(128); get_log_prefix(str, auth_request, subsystem); str_vprintfa(str, format, va); return str_c(str); } void auth_request_log_debug(struct auth_request *auth_request, const char *subsystem, const char *format, ...) { va_list va; if (!auth_request->debug) return; va_start(va, format); T_BEGIN { i_debug("%s", get_log_str(auth_request, subsystem, format, va)); } T_END; va_end(va); } void auth_request_log_info(struct auth_request *auth_request, const char *subsystem, const char *format, ...) { va_list va; if (auth_request->set->debug) { /* auth_debug=yes overrides auth_verbose settings */ } else { const char *db_auth_verbose; if (auth_request->userdb_lookup) db_auth_verbose = auth_request->userdb->set->auth_verbose; else if (auth_request->passdb != NULL) db_auth_verbose = auth_request->passdb->set->auth_verbose; else db_auth_verbose = "d"; switch (db_auth_verbose[0]) { case 'y': break; case 'n': return; case 'd': if (!auth_request->set->verbose) return; break; default: i_unreached(); } } va_start(va, format); T_BEGIN { i_info("%s", get_log_str(auth_request, subsystem, format, va)); } T_END; va_end(va); } void auth_request_log_warning(struct auth_request *auth_request, const char *subsystem, const char *format, ...) { va_list va; va_start(va, format); T_BEGIN { i_warning("%s", get_log_str(auth_request, subsystem, format, va)); } T_END; va_end(va); } void auth_request_log_error(struct auth_request *auth_request, const char *subsystem, const char *format, ...) { va_list va; va_start(va, format); T_BEGIN { i_error("%s", get_log_str(auth_request, subsystem, format, va)); } T_END; va_end(va); } void auth_request_refresh_last_access(struct auth_request *request) { request->last_access = ioloop_time; if (request->to_abort != NULL) timeout_reset(request->to_abort); } dovecot-2.2.33.2/src/auth/auth-stats.c0000644000175000017500000000557713123174404014333 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "stats.h" #include "stats-parser.h" #include "auth-stats.h" static struct stats_parser_field auth_stats_fields[] = { #define E(parsename, name, type) { parsename, offsetof(struct auth_stats, name), sizeof(((struct auth_stats *)0)->name), type } #define EN(parsename, name) E(parsename, name, STATS_PARSER_TYPE_UINT) EN("auth_successes", auth_success_count), EN("auth_master_successes", auth_master_success_count), EN("auth_failures", auth_failure_count), EN("auth_db_tempfails", auth_db_tempfail_count), EN("auth_cache_hits", auth_cache_hit_count), EN("auth_cache_misses", auth_cache_miss_count) }; static size_t auth_stats_alloc_size(void) { return sizeof(struct auth_stats); } static unsigned int auth_stats_field_count(void) { return N_ELEMENTS(auth_stats_fields); } static const char *auth_stats_field_name(unsigned int n) { i_assert(n < N_ELEMENTS(auth_stats_fields)); return auth_stats_fields[n].name; } static void auth_stats_field_value(string_t *str, const struct stats *stats, unsigned int n) { i_assert(n < N_ELEMENTS(auth_stats_fields)); stats_parser_value(str, &auth_stats_fields[n], stats); } static bool auth_stats_diff(const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r) { return stats_parser_diff(auth_stats_fields, N_ELEMENTS(auth_stats_fields), stats1, stats2, diff_stats_r, error_r); } static void auth_stats_add(struct stats *dest, const struct stats *src) { stats_parser_add(auth_stats_fields, N_ELEMENTS(auth_stats_fields), dest, src); } static bool auth_stats_have_changed(const struct stats *_prev, const struct stats *_cur) { return memcmp(_prev, _cur, sizeof(struct auth_stats)) != 0; } static void auth_stats_export(buffer_t *buf, const struct stats *_stats) { const struct auth_stats *stats = (const struct auth_stats *)_stats; buffer_append(buf, stats, sizeof(*stats)); } static bool auth_stats_import(const unsigned char *data, size_t size, size_t *pos_r, struct stats *_stats, const char **error_r) { struct auth_stats *stats = (struct auth_stats *)_stats; if (size < sizeof(*stats)) { *error_r = "auth_stats too small"; return FALSE; } memcpy(stats, data, sizeof(*stats)); *pos_r = sizeof(*stats); return TRUE; } const struct stats_vfuncs auth_stats_vfuncs = { "auth", auth_stats_alloc_size, auth_stats_field_count, auth_stats_field_name, auth_stats_field_value, auth_stats_diff, auth_stats_add, auth_stats_have_changed, auth_stats_export, auth_stats_import }; /* for the stats_auth plugin: */ void stats_auth_init(void); void stats_auth_deinit(void); static struct stats_item *auth_stats_item; void stats_auth_init(void) { auth_stats_item = stats_register(&auth_stats_vfuncs); } void stats_auth_deinit(void) { stats_unregister(&auth_stats_item); } dovecot-2.2.33.2/src/auth/password-scheme.h0000644000175000017500000001056313165463624015351 00000000000000#ifndef PASSWORD_SCHEME_H #define PASSWORD_SCHEME_H enum password_encoding { PW_ENCODING_NONE, PW_ENCODING_BASE64, PW_ENCODING_HEX }; struct password_scheme { const char *name; enum password_encoding default_encoding; /* If non-zero, this is the expected raw password length. It can be used to automatically detect encoding between hex and base64 encoded passwords. */ unsigned int raw_password_len; int (*password_verify)(const char *plaintext, const char *user, const unsigned char *raw_password, size_t size, const char **error_r); void (*password_generate)(const char *plaintext, const char *user, const unsigned char **raw_password_r, size_t *size_r); }; ARRAY_DEFINE_TYPE(password_scheme_p, const struct password_scheme *); extern ARRAY_TYPE(password_scheme_p) password_schemes; extern unsigned int password_scheme_encryption_rounds; /* Returns 1 = matched, 0 = didn't match, -1 = unknown scheme or invalid raw_password */ int password_verify(const char *plaintext, const char *user, const char *scheme, const unsigned char *raw_password, size_t size, const char **error_r); /* Extracts scheme from password, or returns NULL if it isn't found. If auth_request is given, it's used for debug logging. */ const char *password_get_scheme(const char **password); /* Decode encoded (base64/hex) password to raw form. Returns 1 if ok, 0 if scheme is unknown, -1 if password is invalid. */ int password_decode(const char *password, const char *scheme, const unsigned char **raw_password_r, size_t *size_r, const char **error_r); /* Create password with wanted scheme out of plaintext password and username. Potential base64/hex directives are ignored in scheme. Returns FALSE if the scheme is unknown. */ bool password_generate(const char *plaintext, const char *user, const char *scheme, const unsigned char **raw_password_r, size_t *size_r); /* Like above, but generate encoded passwords. If hex/base64 directive isn't specified in the scheme, the default encoding for the scheme is used. Returns FALSE if the scheme is unknown. */ bool password_generate_encoded(const char *plaintext, const char *user, const char *scheme, const char **password_r); /* Returns TRUE if schemes are equivalent. */ bool password_scheme_is_alias(const char *scheme1, const char *scheme2); /* Try to detect in which scheme crypted password is. Returns the scheme name or NULL if nothing was found. */ const char * password_scheme_detect(const char *plain_password, const char *crypted_password, const char *user); void password_scheme_register(const struct password_scheme *scheme); void password_scheme_unregister(const struct password_scheme *scheme); void password_schemes_init(void); void password_schemes_deinit(void); /* some password schemes/algorithms supports a variable number of encryption rounds. */ void password_set_encryption_rounds(unsigned int rounds); /* INTERNAL: */ const char *password_generate_salt(size_t len); const char *password_generate_md5_crypt(const char *pw, const char *salt); int password_generate_otp(const char *pw, const char *state_data, unsigned int algo, const char **result_r) ATTR_NULL(2); void password_generate_rpa(const char *pw, unsigned char result[]); int crypt_verify(const char *plaintext, const char *user, const unsigned char *raw_password, size_t size, const char **error_r); int scram_sha1_scheme_parse(const unsigned char *credentials, size_t size, unsigned int *iter_count_r, const char **salt_r, unsigned char stored_key_r[], unsigned char server_key_r[], const char **error_r); int scram_sha1_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r ATTR_UNUSED); void scram_sha1_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r); void pbkdf2_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r); int pbkdf2_verify(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char *raw_password, size_t size, const char **error_r); /* check wich of the algorithms Blowfisch, SHA-256 and SHA-512 are supported by the used libc's/glibc's crypt() */ void password_scheme_register_crypt(void); #endif dovecot-2.2.33.2/src/auth/auth-request-var-expand.c0000644000175000017500000002103513165463624016726 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "str.h" #include "strescape.h" #include "auth-request.h" struct auth_request_var_expand_ctx { const struct auth_request *auth_request; auth_request_escape_func_t *escape_func; }; const struct var_expand_table auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT+1] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { 's', NULL, "service" }, { 'h', NULL, "home" }, { 'l', NULL, "lip" }, { 'r', NULL, "rip" }, { 'p', NULL, "pid" }, { 'w', NULL, "password" }, { '!', NULL, NULL }, { 'm', NULL, "mech" }, { 'c', NULL, "secured" }, { 'a', NULL, "lport" }, { 'b', NULL, "rport" }, { 'k', NULL, "cert" }, { '\0', NULL, "login_user" }, { '\0', NULL, "login_username" }, { '\0', NULL, "login_domain" }, { '\0', NULL, "session" }, { '\0', NULL, "real_lip" }, { '\0', NULL, "real_rip" }, { '\0', NULL, "real_lport" }, { '\0', NULL, "real_rport" }, { '\0', NULL, "domain_first" }, { '\0', NULL, "domain_last" }, { '\0', NULL, "master_user" }, { '\0', NULL, "session_pid" }, { '\0', NULL, "orig_user" }, { '\0', NULL, "orig_username" }, { '\0', NULL, "orig_domain" }, { '\0', NULL, "auth_user" }, { '\0', NULL, "auth_username" }, { '\0', NULL, "auth_domain" }, { '\0', NULL, "local_name" }, { '\0', NULL, "client_id" }, /* be sure to update AUTH_REQUEST_VAR_TAB_COUNT */ { '\0', NULL, NULL } }; static const char * escape_none(const char *string, const struct auth_request *request ATTR_UNUSED) { return string; } const char * auth_request_str_escape(const char *string, const struct auth_request *request ATTR_UNUSED) { return str_escape(string); } struct var_expand_table * auth_request_get_var_expand_table_full(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func, unsigned int *count) { const unsigned int auth_count = N_ELEMENTS(auth_request_var_expand_static_tab); struct var_expand_table *tab, *ret_tab; const char *orig_user, *auth_user, *username; if (escape_func == NULL) escape_func = escape_none; /* keep the extra fields at the beginning. the last static_tab field contains the ending NULL-fields. */ tab = ret_tab = t_new(struct var_expand_table, MALLOC_ADD(*count, auth_count)); tab += *count; *count += auth_count; memcpy(tab, auth_request_var_expand_static_tab, auth_count * sizeof(*tab)); username = auth_request->user != NULL ? auth_request->user : ""; tab[0].value = escape_func(username, auth_request); tab[1].value = escape_func(t_strcut(username, '@'), auth_request); tab[2].value = strchr(username, '@'); if (tab[2].value != NULL) tab[2].value = escape_func(tab[2].value+1, auth_request); tab[3].value = escape_func(auth_request->service, auth_request); /* tab[4] = we have no home dir */ if (auth_request->local_ip.family != 0) tab[5].value = net_ip2addr(&auth_request->local_ip); if (auth_request->remote_ip.family != 0) tab[6].value = net_ip2addr(&auth_request->remote_ip); tab[7].value = dec2str(auth_request->client_pid); if (auth_request->mech_password != NULL) { tab[8].value = escape_func(auth_request->mech_password, auth_request); } if (auth_request->userdb_lookup) { tab[9].value = auth_request->userdb == NULL ? "" : dec2str(auth_request->userdb->userdb->id); } else { tab[9].value = auth_request->passdb == NULL ? "" : dec2str(auth_request->passdb->passdb->id); } tab[10].value = auth_request->mech_name == NULL ? "" : escape_func(auth_request->mech_name, auth_request); tab[11].value = auth_request->secured ? "secured" : ""; tab[12].value = dec2str(auth_request->local_port); tab[13].value = dec2str(auth_request->remote_port); tab[14].value = auth_request->valid_client_cert ? "valid" : ""; if (auth_request->requested_login_user != NULL) { const char *login_user = auth_request->requested_login_user; tab[15].value = escape_func(login_user, auth_request); tab[16].value = escape_func(t_strcut(login_user, '@'), auth_request); tab[17].value = strchr(login_user, '@'); if (tab[17].value != NULL) { tab[17].value = escape_func(tab[17].value+1, auth_request); } } tab[18].value = auth_request->session_id == NULL ? NULL : escape_func(auth_request->session_id, auth_request); if (auth_request->real_local_ip.family != 0) tab[19].value = net_ip2addr(&auth_request->real_local_ip); if (auth_request->real_remote_ip.family != 0) tab[20].value = net_ip2addr(&auth_request->real_remote_ip); tab[21].value = dec2str(auth_request->real_local_port); tab[22].value = dec2str(auth_request->real_remote_port); tab[23].value = strchr(username, '@'); if (tab[23].value != NULL) { tab[23].value = escape_func(t_strcut(tab[23].value+1, '@'), auth_request); } tab[24].value = strrchr(username, '@'); if (tab[24].value != NULL) tab[24].value = escape_func(tab[24].value+1, auth_request); tab[25].value = auth_request->master_user == NULL ? NULL : escape_func(auth_request->master_user, auth_request); tab[26].value = auth_request->session_pid == (pid_t)-1 ? NULL : dec2str(auth_request->session_pid); orig_user = auth_request->original_username != NULL ? auth_request->original_username : username; tab[27].value = escape_func(orig_user, auth_request); tab[28].value = escape_func(t_strcut(orig_user, '@'), auth_request); tab[29].value = strchr(orig_user, '@'); if (tab[29].value != NULL) tab[29].value = escape_func(tab[29].value+1, auth_request); if (auth_request->master_user != NULL) auth_user = auth_request->master_user; else auth_user = orig_user; tab[30].value = escape_func(auth_user, auth_request); tab[31].value = escape_func(t_strcut(auth_user, '@'), auth_request); tab[32].value = strchr(auth_user, '@'); if (tab[32].value != NULL) tab[32].value = escape_func(tab[32].value+1, auth_request); if (auth_request->local_name != NULL) tab[33].value = escape_func(auth_request->local_name, auth_request); else tab[33].value = ""; if (auth_request->client_id != NULL) tab[34].value = escape_func(auth_request->client_id, auth_request); return ret_tab; } const struct var_expand_table * auth_request_get_var_expand_table(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func) { unsigned int count = 0; return auth_request_get_var_expand_table_full(auth_request, escape_func, &count); } static const char *field_get_default(const char *data) { const char *p; p = strchr(data, ':'); if (p == NULL) return ""; else { /* default value given */ return p+1; } } static const char * auth_request_var_expand_func_passdb(const char *data, void *context) { struct auth_request_var_expand_ctx *ctx = context; const char *field_name = t_strcut(data, ':'); const char *value; value = auth_fields_find(ctx->auth_request->extra_fields, field_name); return ctx->escape_func(value != NULL ? value : field_get_default(data), ctx->auth_request); } static const char * auth_request_var_expand_func_userdb(const char *data, void *context) { struct auth_request_var_expand_ctx *ctx = context; const char *field_name = t_strcut(data, ':'); const char *value; value = ctx->auth_request->userdb_reply == NULL ? NULL : auth_fields_find(ctx->auth_request->userdb_reply, field_name); return ctx->escape_func(value != NULL ? value : field_get_default(data), ctx->auth_request); } const struct var_expand_func_table auth_request_var_funcs_table[] = { { "passdb", auth_request_var_expand_func_passdb }, { "userdb", auth_request_var_expand_func_userdb }, { NULL, NULL } }; void auth_request_var_expand(string_t *dest, const char *str, const struct auth_request *auth_request, auth_request_escape_func_t *escape_func) { auth_request_var_expand_with_table(dest, str, auth_request, auth_request_get_var_expand_table(auth_request, escape_func), escape_func); } void auth_request_var_expand_with_table(string_t *dest, const char *str, const struct auth_request *auth_request, const struct var_expand_table *table, auth_request_escape_func_t *escape_func) { struct auth_request_var_expand_ctx ctx; i_zero(&ctx); ctx.auth_request = auth_request; ctx.escape_func = escape_func == NULL ? escape_none : escape_func; var_expand_with_funcs(dest, str, table, auth_request_var_funcs_table, &ctx); } const char * t_auth_request_var_expand(const char *str, const struct auth_request *auth_request, auth_request_escape_func_t *escape_func) { string_t *dest = t_str_new(128); auth_request_var_expand(dest, str, auth_request, escape_func); return str_c(dest); } dovecot-2.2.33.2/src/auth/auth-master-connection.c0000644000175000017500000005343613165463624016635 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "buffer.h" #include "hash.h" #include "llist.h" #include "str.h" #include "strescape.h" #include "str-sanitize.h" #include "hostpid.h" #include "hex-binary.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "ipwd.h" #include "master-service.h" #include "userdb.h" #include "userdb-blocking.h" #include "master-interface.h" #include "passdb-cache.h" #include "auth-request-handler.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #include #define MAX_INBUF_SIZE 1024 #define MAX_OUTBUF_SIZE (1024*50) struct master_userdb_request { struct auth_master_connection *conn; struct auth_request *auth_request; }; struct master_list_iter_ctx { struct auth_master_connection *conn; struct userdb_iterate_context *iter; struct auth_request *auth_request; bool failed; }; static void master_input(struct auth_master_connection *conn); static struct auth_master_connection *auth_master_connections; static const char * auth_master_reply_hide_passwords(struct auth_master_connection *conn, const char *str) { char **args, *p, *p2; unsigned int i; if (conn->auth->set->debug_passwords) return str; /* hide all parameters that have "pass" in their key */ args = p_strsplit(pool_datastack_create(), str, "\t"); for (i = 0; args[i] != NULL; i++) { p = strstr(args[i], "pass"); p2 = strchr(args[i], '='); if (p != NULL && p < p2) { *p2 = '\0'; args[i] = p_strconcat(pool_datastack_create(), args[i], "=", NULL); } } return t_strarray_join((void *)args, "\t"); } void auth_master_request_callback(const char *reply, struct auth_master_connection *conn) { struct const_iovec iov[2]; if (conn->auth->set->debug) { i_debug("master userdb out: %s", auth_master_reply_hide_passwords(conn, reply)); } iov[0].iov_base = reply; iov[0].iov_len = strlen(reply); iov[1].iov_base = "\n"; iov[1].iov_len = 1; o_stream_nsendv(conn->output, iov, 2); } static bool master_input_request(struct auth_master_connection *conn, const char *args) { struct auth_client_connection *client_conn; const char *const *list, *const *params; unsigned int id, client_pid, client_id; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; buffer_t buf; /* [] */ list = t_strsplit_tabescaped(args); if (str_array_length(list) < 4 || str_to_uint(list[0], &id) < 0 || str_to_uint(list[1], &client_pid) < 0 || str_to_uint(list[2], &client_id) < 0) { i_error("BUG: Master sent broken REQUEST"); return FALSE; } buffer_create_from_data(&buf, cookie, sizeof(cookie)); if (hex_to_binary(list[3], &buf) < 0) { i_error("BUG: Master sent broken REQUEST cookie"); return FALSE; } params = list + 4; client_conn = auth_client_connection_lookup(client_pid); if (client_conn == NULL) { i_error("Master requested auth for nonexistent client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (!mem_equals_timing_safe(client_conn->cookie, cookie, sizeof(cookie))) { i_error("Master requested auth for client %u with invalid cookie", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (!auth_request_handler_master_request( client_conn->request_handler, conn, id, client_id, params)) { i_error("Master requested auth for non-login client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } return TRUE; } static int master_input_cache_flush(struct auth_master_connection *conn, const char *args) { const char *const *list; unsigned int count; /* [ [ [..]] */ list = t_strsplit_tabescaped(args); if (list[0] == NULL) { i_error("BUG: doveadm sent broken CACHE-FLUSH"); return FALSE; } if (passdb_cache == NULL) { /* cache disabled */ count = 0; } else if (list[1] == NULL) { /* flush the whole cache */ count = auth_cache_clear(passdb_cache); } else { count = auth_cache_clear_users(passdb_cache, list+1); } (void)o_stream_send_str(conn->output, t_strdup_printf("OK\t%s\t%u\n", list[0], count)); return TRUE; } static int master_input_auth_request(struct auth_master_connection *conn, const char *args, const char *cmd, struct auth_request **request_r, const char **error_r) { struct auth_request *auth_request; const char *const *list, *name, *arg, *username; unsigned int id; /* [] */ list = t_strsplit_tabescaped(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("BUG: Master sent broken %s", cmd); return -1; } auth_request = auth_request_new_dummy(); auth_request->id = id; auth_request->master = conn; auth_master_connection_ref(conn); username = list[1]; for (list += 2; *list != NULL; list++) { arg = strchr(*list, '='); if (arg == NULL) { name = *list; arg = ""; } else { name = t_strdup_until(*list, arg); arg++; } (void)auth_request_import_info(auth_request, name, arg); } if (auth_request->service == NULL) { i_error("BUG: Master sent %s request without service", cmd); auth_request_unref(&auth_request); auth_master_connection_unref(&conn); return -1; } auth_request_init(auth_request); if (!auth_request_set_username(auth_request, username, error_r)) { *request_r = auth_request; return 0; } *request_r = auth_request; return 1; } static int user_verify_restricted_uid(struct auth_request *auth_request) { struct auth_master_connection *conn = auth_request->master; struct auth_fields *reply = auth_request->userdb_reply; const char *value, *reason; uid_t uid; if (conn->userdb_restricted_uid == 0) return 0; value = auth_fields_find(reply, "uid"); if (value == NULL) reason = "userdb reply doesn't contain uid"; else if (str_to_uid(value, &uid) < 0) reason = "userdb reply contains invalid uid"; else if (uid != conn->userdb_restricted_uid) { reason = t_strdup_printf( "userdb uid (%s) doesn't match peer uid (%s)", dec2str(uid), dec2str(conn->userdb_restricted_uid)); } else { return 0; } auth_request_log_error(auth_request, "userdb", "client doesn't have lookup permissions for this user: %s " "(to bypass this check, set: service auth { unix_listener %s { mode=0777 } })", reason, conn->path); return -1; } static void user_callback(enum userdb_result result, struct auth_request *auth_request) { struct auth_master_connection *conn = auth_request->master; string_t *str; const char *value; if (auth_request->userdb_lookup_tempfailed) result = USERDB_RESULT_INTERNAL_FAILURE; if (result == USERDB_RESULT_OK) { if (user_verify_restricted_uid(auth_request) < 0) result = USERDB_RESULT_INTERNAL_FAILURE; } str = t_str_new(128); switch (result) { case USERDB_RESULT_INTERNAL_FAILURE: str_printfa(str, "FAIL\t%u", auth_request->id); if (auth_request->userdb_lookup_tempfailed) { value = auth_fields_find(auth_request->userdb_reply, "reason"); if (value != NULL) str_printfa(str, "\treason=%s", value); } break; case USERDB_RESULT_USER_UNKNOWN: str_printfa(str, "NOTFOUND\t%u", auth_request->id); break; case USERDB_RESULT_OK: str_printfa(str, "USER\t%u\t", auth_request->id); str_append_tabescaped(str, auth_request->user); str_append_c(str, '\t'); auth_fields_append(auth_request->userdb_reply, str, AUTH_FIELD_FLAG_HIDDEN, 0); break; } if (conn->auth->set->debug) { i_debug("userdb out: %s", auth_master_reply_hide_passwords(conn, str_c(str))); } str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); auth_request_unref(&auth_request); auth_master_connection_unref(&conn); } static bool master_input_user(struct auth_master_connection *conn, const char *args) { struct auth_request *auth_request; const char *error; int ret; ret = master_input_auth_request(conn, args, "USER", &auth_request, &error); if (ret <= 0) { if (ret < 0) return FALSE; auth_request_log_info(auth_request, "userdb", "%s", error); user_callback(USERDB_RESULT_USER_UNKNOWN, auth_request); } else { auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB); auth_request_lookup_user(auth_request, user_callback); } return TRUE; } static void pass_callback_finish(struct auth_request *auth_request, enum passdb_result result) { struct auth_master_connection *conn = auth_request->master; string_t *str; str = t_str_new(128); switch (result) { case PASSDB_RESULT_OK: if (auth_request->failed || !auth_request->passdb_success) { str_printfa(str, "FAIL\t%u", auth_request->id); break; } str_printfa(str, "PASS\t%u\tuser=", auth_request->id); str_append_tabescaped(str, auth_request->user); if (!auth_fields_is_empty(auth_request->extra_fields)) { str_append_c(str, '\t'); auth_fields_append(auth_request->extra_fields, str, AUTH_FIELD_FLAG_HIDDEN, 0); } break; case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_USER_DISABLED: case PASSDB_RESULT_PASS_EXPIRED: str_printfa(str, "NOTFOUND\t%u", auth_request->id); break; case PASSDB_RESULT_NEXT: case PASSDB_RESULT_PASSWORD_MISMATCH: case PASSDB_RESULT_INTERNAL_FAILURE: str_printfa(str, "FAIL\t%u", auth_request->id); break; case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: str_printfa(str, "FAIL\t%u\treason=Configured passdbs don't support credentials lookups", auth_request->id); break; } if (conn->auth->set->debug) i_debug("passdb out: %s", str_c(str)); str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); auth_request_unref(&auth_request); auth_master_connection_unref(&conn); } static void auth_master_pass_proxy_finish(bool success, struct auth_request *auth_request) { pass_callback_finish(auth_request, success ? PASSDB_RESULT_OK : PASSDB_RESULT_INTERNAL_FAILURE); } static void pass_callback(enum passdb_result result, const unsigned char *credentials ATTR_UNUSED, size_t size ATTR_UNUSED, struct auth_request *auth_request) { int ret; if (result != PASSDB_RESULT_OK) auth_request_proxy_finish_failure(auth_request); else { ret = auth_request_proxy_finish(auth_request, auth_master_pass_proxy_finish); if (ret == 0) return; if (ret < 0) result = PASSDB_RESULT_INTERNAL_FAILURE; } pass_callback_finish(auth_request, result); } static const char *auth_restricted_reason(struct auth_master_connection *conn) { struct passwd pw; const char *namestr; if (i_getpwuid(conn->userdb_restricted_uid, &pw) <= 0) namestr = ""; else namestr = t_strdup_printf("(%s)", pw.pw_name); return t_strdup_printf("%s mode=0666, but not owned by UID %lu%s", conn->path, (unsigned long)conn->userdb_restricted_uid, namestr); } static bool master_input_pass(struct auth_master_connection *conn, const char *args) { struct auth_request *auth_request; const char *error; int ret; ret = master_input_auth_request(conn, args, "PASS", &auth_request, &error); if (ret <= 0) { if (ret < 0) return FALSE; auth_request_log_info(auth_request, "passdb", "%s", error); pass_callback(PASSDB_RESULT_USER_UNKNOWN, &uchar_nul, 0, auth_request); } else if (conn->userdb_restricted_uid != 0) { /* no permissions to do this lookup */ auth_request_log_error(auth_request, "passdb", "Auth client doesn't have permissions to do " "a PASS lookup: %s", auth_restricted_reason(conn)); pass_callback(PASSDB_RESULT_INTERNAL_FAILURE, &uchar_nul, 0, auth_request); } else { auth_request_set_state(auth_request, AUTH_REQUEST_STATE_MECH_CONTINUE); auth_request_lookup_credentials(auth_request, "", pass_callback); } return TRUE; } static void master_input_list_finish(struct master_list_iter_ctx *ctx) { i_assert(ctx->conn->iter_ctx == ctx); ctx->conn->iter_ctx = NULL; ctx->conn->io = io_add(ctx->conn->fd, IO_READ, master_input, ctx->conn); if (ctx->iter != NULL) (void)userdb_blocking_iter_deinit(&ctx->iter); o_stream_uncork(ctx->conn->output); o_stream_unset_flush_callback(ctx->conn->output); auth_request_unref(&ctx->auth_request); auth_master_connection_unref(&ctx->conn); i_free(ctx); } static int master_output_list(struct master_list_iter_ctx *ctx) { int ret; if ((ret = o_stream_flush(ctx->conn->output)) < 0) { master_input_list_finish(ctx); return 1; } if (ret > 0) { o_stream_cork(ctx->conn->output); userdb_blocking_iter_next(ctx->iter); } return 1; } static void master_input_list_callback(const char *user, void *context) { struct master_list_iter_ctx *ctx = context; struct auth_userdb *userdb = ctx->auth_request->userdb; int ret; if (user == NULL) { if (userdb_blocking_iter_deinit(&ctx->iter) < 0) ctx->failed = TRUE; do { userdb = userdb->next; } while (userdb != NULL && userdb->userdb->iface->iterate_init == NULL); if (userdb == NULL) { /* iteration is finished */ const char *str; str = t_strdup_printf("DONE\t%u\t%s\n", ctx->auth_request->id, ctx->failed ? "fail" : ""); o_stream_nsend_str(ctx->conn->output, str); master_input_list_finish(ctx); return; } /* continue iterating next userdb */ ctx->auth_request->userdb = userdb; ctx->iter = userdb_blocking_iter_init(ctx->auth_request, master_input_list_callback, ctx); userdb_blocking_iter_next(ctx->iter); return; } T_BEGIN { const char *str; str = t_strdup_printf("LIST\t%u\t%s\n", ctx->auth_request->id, str_tabescape(user)); ret = o_stream_send_str(ctx->conn->output, str); } T_END; if (o_stream_get_buffer_used_size(ctx->conn->output) >= MAX_OUTBUF_SIZE) ret = o_stream_flush(ctx->conn->output); if (ret < 0) { /* disconnected, don't bother finishing */ master_input_list_finish(ctx); return; } if (o_stream_get_buffer_used_size(ctx->conn->output) < MAX_OUTBUF_SIZE) userdb_blocking_iter_next(ctx->iter); else o_stream_uncork(ctx->conn->output); } static bool master_input_list(struct auth_master_connection *conn, const char *args) { struct auth_userdb *userdb = conn->auth->userdbs; struct auth_request *auth_request; struct master_list_iter_ctx *ctx; const char *str, *name, *arg, *const *list; unsigned int id; /* [] */ list = t_strsplit_tabescaped(args); if (list[0] == NULL || str_to_uint(list[0], &id) < 0) { i_error("BUG: Master sent broken LIST"); return FALSE; } list++; if (conn->iter_ctx != NULL) { i_error("Auth client is already iterating users"); str = t_strdup_printf("DONE\t%u\tfail\n", id); o_stream_nsend_str(conn->output, str); return TRUE; } if (conn->userdb_restricted_uid != 0) { i_error("Auth client doesn't have permissions to list users: %s", auth_restricted_reason(conn)); str = t_strdup_printf("DONE\t%u\tfail\n", id); o_stream_nsend_str(conn->output, str); return TRUE; } while (userdb != NULL && userdb->userdb->iface->iterate_init == NULL) userdb = userdb->next; if (userdb == NULL) { i_error("Trying to iterate users, but userdbs don't support it"); str = t_strdup_printf("DONE\t%u\tfail\n", id); o_stream_nsend_str(conn->output, str); return TRUE; } auth_request = auth_request_new_dummy(); auth_request->id = id; auth_request->master = conn; auth_master_connection_ref(conn); for (; *list != NULL; list++) { arg = strchr(*list, '='); if (arg == NULL) { name = *list; arg = ""; } else { name = t_strdup_until(*list, arg); arg++; } if (!auth_request_import_info(auth_request, name, arg) && strcmp(name, "user") == 0) { /* username mask */ auth_request->user = p_strdup(auth_request->pool, arg); } } /* rest of the code doesn't like NULL user or service */ if (auth_request->user == NULL) auth_request->user = ""; if (auth_request->service == NULL) auth_request->service = ""; ctx = i_new(struct master_list_iter_ctx, 1); ctx->conn = conn; ctx->auth_request = auth_request; ctx->auth_request->userdb = userdb; io_remove(&conn->io); o_stream_cork(conn->output); o_stream_set_flush_callback(conn->output, master_output_list, ctx); ctx->iter = userdb_blocking_iter_init(auth_request, master_input_list_callback, ctx); conn->iter_ctx = ctx; return TRUE; } static bool auth_master_input_line(struct auth_master_connection *conn, const char *line) { if (conn->auth->set->debug) i_debug("master in: %s", line); if (strncmp(line, "USER\t", 5) == 0) return master_input_user(conn, line + 5); if (strncmp(line, "LIST\t", 5) == 0) return master_input_list(conn, line + 5); if (strncmp(line, "PASS\t", 5) == 0) return master_input_pass(conn, line + 5); if (!conn->userdb_only) { i_assert(conn->userdb_restricted_uid == 0); if (strncmp(line, "REQUEST\t", 8) == 0) return master_input_request(conn, line + 8); if (strncmp(line, "CACHE-FLUSH\t", 12) == 0) return master_input_cache_flush(conn, line + 12); if (strncmp(line, "CPID\t", 5) == 0) { i_error("Authentication client trying to connect to " "master socket"); return FALSE; } } i_error("BUG: Unknown command in %s socket: %s", conn->userdb_only ? "userdb" : "master", str_sanitize(line, 80)); return FALSE; } static void master_input(struct auth_master_connection *conn) { char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_master_connection_destroy(&conn); return; case -2: /* buffer full */ i_error("BUG: Master sent us more than %d bytes", (int)MAX_INBUF_SIZE); auth_master_connection_destroy(&conn); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_MASTER_PROTOCOL_MAJOR_VERSION)) { i_error("Master not compatible with this server " "(mixed old and new binaries?)"); auth_master_connection_destroy(&conn); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = auth_master_input_line(conn, line); } T_END; if (!ret) { auth_master_connection_destroy(&conn); return; } } } static int master_output(struct auth_master_connection *conn) { if (o_stream_flush(conn->output) < 0) { /* transmit error, probably master died */ auth_master_connection_destroy(&conn); return 1; } if (conn->io == NULL && o_stream_get_buffer_used_size(conn->output) <= MAX_OUTBUF_SIZE/2) { /* allow input again */ conn->io = io_add(conn->fd, IO_READ, master_input, conn); } return 1; } static int auth_master_connection_set_permissions(struct auth_master_connection *conn, const struct stat *st) { struct net_unix_cred cred; if (st == NULL) return 0; /* figure out what permissions we want to give to this client */ if ((st->st_mode & 0777) != 0666) { /* permissions were already restricted by the socket permissions. also +x bit indicates that we shouldn't do any permission checks. */ return 0; } if (net_getunixcred(conn->fd, &cred) < 0) { i_error("userdb connection: Failed to get peer's credentials"); return -1; } if (cred.uid == st->st_uid || cred.gid == st->st_gid) { /* full permissions */ return 0; } else { /* restrict permissions: return only lookups whose returned uid matches the peer's uid */ conn->userdb_restricted_uid = cred.uid; return 0; } } struct auth_master_connection * auth_master_connection_create(struct auth *auth, int fd, const char *path, const struct stat *socket_st, bool userdb_only) { struct auth_master_connection *conn; const char *line; i_assert(path != NULL); conn = i_new(struct auth_master_connection, 1); conn->refcount = 1; conn->fd = fd; conn->path = i_strdup(path); conn->auth = auth; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_set_flush_callback(conn->output, master_output, conn); conn->io = io_add(fd, IO_READ, master_input, conn); conn->userdb_only = userdb_only; line = t_strdup_printf("VERSION\t%u\t%u\nSPID\t%s\n", AUTH_MASTER_PROTOCOL_MAJOR_VERSION, AUTH_MASTER_PROTOCOL_MINOR_VERSION, my_pid); o_stream_nsend_str(conn->output, line); DLLIST_PREPEND(&auth_master_connections, conn); if (auth_master_connection_set_permissions(conn, socket_st) < 0) { auth_master_connection_destroy(&conn); return NULL; } return conn; } void auth_master_connection_destroy(struct auth_master_connection **_conn) { struct auth_master_connection *conn = *_conn; *_conn = NULL; if (conn->destroyed) return; conn->destroyed = TRUE; DLLIST_REMOVE(&auth_master_connections, conn); if (conn->iter_ctx != NULL) master_input_list_finish(conn->iter_ctx); if (conn->input != NULL) i_stream_close(conn->input); if (conn->output != NULL) o_stream_close(conn->output); if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { if (close(conn->fd) < 0) i_error("close(%s): %m", conn->path); conn->fd = -1; } master_service_client_connection_destroyed(master_service); auth_master_connection_unref(&conn); } void auth_master_connection_ref(struct auth_master_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } void auth_master_connection_unref(struct auth_master_connection **_conn) { struct auth_master_connection *conn = *_conn; *_conn = NULL; i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; if (conn->input != NULL) i_stream_unref(&conn->input); if (conn->output != NULL) o_stream_unref(&conn->output); i_free(conn->path); i_free(conn); } void auth_master_connections_destroy_all(void) { struct auth_master_connection *conn; while (auth_master_connections != NULL) { conn = auth_master_connections; auth_master_connection_destroy(&conn); } } dovecot-2.2.33.2/src/auth/mech-anonymous.c0000644000175000017500000000234713123174404015170 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "mech.h" static void mech_anonymous_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { i_assert(*request->set->anonymous_username != '\0'); if (request->set->verbose) { /* temporarily set the user to the one that was given, so that the log message goes right */ request->user = p_strndup(pool_datastack_create(), data, data_size); auth_request_log_info(request, AUTH_SUBSYS_MECH, "login"); } request->user = p_strdup(request->pool, request->set->anonymous_username); request->passdb_success = TRUE; auth_request_success(request, "", 0); } static struct auth_request *mech_anonymous_auth_new(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"anonymous_auth_request", 2048); request = p_new(pool, struct auth_request, 1); request->pool = pool; return request; } const struct mech_module mech_anonymous = { "ANONYMOUS", .flags = MECH_SEC_ANONYMOUS, .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_anonymous_auth_new, mech_generic_auth_initial, mech_anonymous_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/checkpassword-reply.c0000644000175000017500000000610513123174404016213 00000000000000/* simple checkpassword wrapper to send userdb data back to dovecot-auth */ #include "lib.h" #include "str.h" #include "strescape.h" #include "write-full.h" #include int main(void) { string_t *str; const char *user, *home, *authorized, *orig_uid_env; const char *extra_env, *key, *value, *const *tmp; bool uid_found = FALSE, gid_found = FALSE; uid_t orig_uid; lib_init(); str = t_str_new(1024); orig_uid_env = getenv("ORIG_UID"); if (orig_uid_env == NULL || str_to_uid(orig_uid_env, &orig_uid) < 0) orig_uid = (uid_t)-1; /* ORIG_UID should have the auth process's UID that forked us. if the checkpassword changed the UID, this could be a security hole because the UID's other processes can ptrace this process and write any kind of a reply to fd 4. so we can run only if: a) INSECURE_SETUID environment is set. b) process isn't ptraceable (this binary is setuid/setgid) c) checkpassword didn't actually change the UID (but used userdb_uid instead) */ if (getenv("INSECURE_SETUID") == NULL && (orig_uid == (uid_t)-1 || orig_uid != getuid()) && getuid() == geteuid() && getgid() == getegid()) { if (orig_uid_env == NULL) { i_error("checkpassword: ORIG_UID environment was dropped by checkpassword. " "Can't verify if we're safe to run. See " "http://wiki2.dovecot.org/AuthDatabase/CheckPassword#Security"); } else { i_error("checkpassword: The checkpassword couldn't be run securely. See " "http://wiki2.dovecot.org/AuthDatabase/CheckPassword#Security"); } return 111; } user = getenv("USER"); if (user != NULL) { if (strchr(user, '\t') != NULL) { i_error("checkpassword: USER contains TAB"); return 1; } str_printfa(str, "user="); str_append_tabescaped(str, user); str_append_c(str, '\t'); } home = getenv("HOME"); if (home != NULL) { if (strchr(home, '\t') != NULL) { i_error("checkpassword: HOME contains TAB"); return 1; } str_printfa(str, "userdb_home="); str_append_tabescaped(str, home); str_append_c(str, '\t'); } extra_env = getenv("EXTRA"); if (extra_env != NULL) { for (tmp = t_strsplit(extra_env, " "); *tmp != NULL; tmp++) { value = getenv(*tmp); if (value != NULL) { key = t_str_lcase(*tmp); if (strcmp(key, "userdb_uid") == 0) uid_found = TRUE; else if (strcmp(key, "userdb_gid") == 0) gid_found = TRUE; str_append_tabescaped(str, key); str_append_c(str, '='); str_append_tabescaped(str, value); str_append_c(str, '\t'); } } } if (!uid_found) str_printfa(str, "userdb_uid=%s\t", dec2str(getuid())); if (!gid_found) str_printfa(str, "userdb_gid=%s\t", dec2str(getgid())); i_assert(str_len(str) > 0); if (write_full(4, str_data(str), str_len(str)) < 0) { i_error("checkpassword: write_full() failed: %m"); exit(111); } authorized = getenv("AUTHORIZED"); if (authorized == NULL) { /* authentication */ return 0; } else if (strcmp(authorized, "2") == 0) { /* successful passdb/userdb lookup */ return 2; } else { i_error("checkpassword: Script doesn't support passdb/userdb lookup"); return 111; } } dovecot-2.2.33.2/src/auth/auth.h0000644000175000017500000000371213123174404013171 00000000000000#ifndef AUTH_H #define AUTH_H #include "auth-settings.h" #define PASSWORD_HIDDEN_STR "" enum auth_passdb_skip { AUTH_PASSDB_SKIP_NEVER, AUTH_PASSDB_SKIP_AUTHENTICATED, AUTH_PASSDB_SKIP_UNAUTHENTICATED }; enum auth_userdb_skip { AUTH_USERDB_SKIP_NEVER, AUTH_USERDB_SKIP_FOUND, AUTH_USERDB_SKIP_NOTFOUND }; enum auth_db_rule { AUTH_DB_RULE_RETURN, AUTH_DB_RULE_RETURN_OK, AUTH_DB_RULE_RETURN_FAIL, AUTH_DB_RULE_CONTINUE, AUTH_DB_RULE_CONTINUE_OK, AUTH_DB_RULE_CONTINUE_FAIL }; struct auth_passdb { struct auth_passdb *next; const struct auth_passdb_settings *set; struct passdb_module *passdb; /* The caching key for this passdb, or NULL if caching isn't wanted. */ const char *cache_key; struct passdb_template *default_fields_tmpl; struct passdb_template *override_fields_tmpl; enum auth_passdb_skip skip; enum auth_db_rule result_success; enum auth_db_rule result_failure; enum auth_db_rule result_internalfail; }; struct auth_userdb { struct auth_userdb *next; const struct auth_userdb_settings *set; struct userdb_module *userdb; /* The caching key for this userdb, or NULL if caching isn't wanted. */ const char *cache_key; struct userdb_template *default_fields_tmpl; struct userdb_template *override_fields_tmpl; enum auth_userdb_skip skip; enum auth_db_rule result_success; enum auth_db_rule result_failure; enum auth_db_rule result_internalfail; }; struct auth { pool_t pool; const char *service; const struct auth_settings *set; const struct mechanisms_register *reg; struct auth_passdb *masterdbs; struct auth_passdb *passdbs; struct auth_userdb *userdbs; }; extern struct auth_penalty *auth_penalty; struct auth *auth_find_service(const char *name); struct auth *auth_default_service(void); void auths_preinit(const struct auth_settings *set, pool_t pool, const struct mechanisms_register *reg, const char *const *services); void auths_init(void); void auths_deinit(void); void auths_free(void); #endif dovecot-2.2.33.2/src/auth/auth-worker-server.c0000644000175000017500000003337013165463624016015 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "array.h" #include "aqueue.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "hex-binary.h" #include "str.h" #include "eacces-error.h" #include "auth-request.h" #include "auth-worker-client.h" #include "auth-worker-server.h" #include /* Initial lookup timeout */ #define AUTH_WORKER_LOOKUP_TIMEOUT_SECS 60 /* Timeout for multi-line replies, e.g. listing users. This should be a much higher value, because e.g. doveadm could be doing some long-running commands for the users. And because of buffering this timeout is for handling multiple users, not just one. */ #define AUTH_WORKER_RESUME_TIMEOUT_SECS (30*60) #define AUTH_WORKER_MAX_IDLE_SECS (60*5) #define AUTH_WORKER_ABORT_SECS 60 #define AUTH_WORKER_DELAY_WARN_SECS 3 #define AUTH_WORKER_DELAY_WARN_MIN_INTERVAL_SECS 300 struct auth_worker_request { unsigned int id; time_t created; const char *username; const char *data; auth_worker_callback_t *callback; void *context; }; struct auth_worker_connection { int fd; struct io *io; struct istream *input; struct ostream *output; struct timeout *to; struct auth_worker_request *request; unsigned int id_counter; unsigned int received_error:1; unsigned int restart:1; unsigned int shutdown:1; unsigned int timeout_pending_resume:1; unsigned int resuming:1; }; static ARRAY(struct auth_worker_connection *) connections = ARRAY_INIT; static unsigned int idle_count = 0, auth_workers_with_errors = 0; static ARRAY(struct auth_worker_request *) worker_request_array; static struct aqueue *worker_request_queue; static time_t auth_worker_last_warn; static unsigned int auth_workers_throttle_count; static const char *worker_socket_path; static void worker_input(struct auth_worker_connection *conn); static void auth_worker_destroy(struct auth_worker_connection **conn, const char *reason, bool restart) ATTR_NULL(2); static void auth_worker_idle_timeout(struct auth_worker_connection *conn) { i_assert(conn->request == NULL); if (idle_count > 1) auth_worker_destroy(&conn, NULL, FALSE); else timeout_reset(conn->to); } static void auth_worker_call_timeout(struct auth_worker_connection *conn) { i_assert(conn->request != NULL); auth_worker_destroy(&conn, "Lookup timed out", TRUE); } static bool auth_worker_request_send(struct auth_worker_connection *conn, struct auth_worker_request *request) { struct const_iovec iov[3]; unsigned int age_secs = ioloop_time - request->created; i_assert(conn->to != NULL); if (age_secs >= AUTH_WORKER_ABORT_SECS) { i_error("Aborting auth request that was queued for %d secs, " "%d left in queue", age_secs, aqueue_count(worker_request_queue)); request->callback(t_strdup_printf( "FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE), request->context); return FALSE; } if (age_secs >= AUTH_WORKER_DELAY_WARN_SECS && ioloop_time - auth_worker_last_warn > AUTH_WORKER_DELAY_WARN_MIN_INTERVAL_SECS) { auth_worker_last_warn = ioloop_time; i_warning("auth workers: Auth request was queued for %d " "seconds, %d left in queue " "(see auth_worker_max_count)", age_secs, aqueue_count(worker_request_queue)); } request->id = ++conn->id_counter; iov[0].iov_base = t_strdup_printf("%d\t", request->id); iov[0].iov_len = strlen(iov[0].iov_base); iov[1].iov_base = request->data; iov[1].iov_len = strlen(request->data); iov[2].iov_base = "\n"; iov[2].iov_len = 1; o_stream_nsendv(conn->output, iov, 3); i_assert(conn->request == NULL); conn->request = request; timeout_remove(&conn->to); conn->to = timeout_add(AUTH_WORKER_LOOKUP_TIMEOUT_SECS * 1000, auth_worker_call_timeout, conn); idle_count--; return TRUE; } static void auth_worker_request_send_next(struct auth_worker_connection *conn) { struct auth_worker_request *request, *const *requestp; do { if (aqueue_count(worker_request_queue) == 0) return; requestp = array_idx(&worker_request_array, aqueue_idx(worker_request_queue, 0)); request = *requestp; aqueue_delete_tail(worker_request_queue); } while (!auth_worker_request_send(conn, request)); } static void auth_worker_send_handshake(struct auth_worker_connection *conn) { string_t *str; unsigned char passdb_md5[MD5_RESULTLEN]; unsigned char userdb_md5[MD5_RESULTLEN]; str = t_str_new(128); str_printfa(str, "VERSION\tauth-worker\t%u\t%u\n", AUTH_WORKER_PROTOCOL_MAJOR_VERSION, AUTH_WORKER_PROTOCOL_MINOR_VERSION); passdbs_generate_md5(passdb_md5); userdbs_generate_md5(userdb_md5); str_append(str, "DBHASH\t"); binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5)); str_append_c(str, '\t'); binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5)); str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); } static struct auth_worker_connection *auth_worker_create(void) { struct auth_worker_connection *conn; int fd; if (array_count(&connections) >= auth_workers_throttle_count) return NULL; fd = net_connect_unix_with_retries(worker_socket_path, 5000); if (fd == -1) { if (errno == EACCES) { i_error("%s", eacces_error_get("net_connect_unix", worker_socket_path)); } else { i_error("net_connect_unix(%s) failed: %m", worker_socket_path); } return NULL; } conn = i_new(struct auth_worker_connection, 1); conn->fd = fd; conn->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); conn->io = io_add(fd, IO_READ, worker_input, conn); conn->to = timeout_add(AUTH_WORKER_MAX_IDLE_SECS * 1000, auth_worker_idle_timeout, conn); auth_worker_send_handshake(conn); idle_count++; array_append(&connections, &conn, 1); return conn; } static void auth_worker_destroy(struct auth_worker_connection **_conn, const char *reason, bool restart) { struct auth_worker_connection *conn = *_conn; struct auth_worker_connection *const *conns; unsigned int idx; *_conn = NULL; if (conn->received_error) { i_assert(auth_workers_with_errors > 0); i_assert(auth_workers_with_errors <= array_count(&connections)); auth_workers_with_errors--; } array_foreach(&connections, conns) { if (*conns == conn) { idx = array_foreach_idx(&connections, conns); array_delete(&connections, idx, 1); break; } } if (conn->request == NULL) idle_count--; if (conn->request != NULL) { i_error("auth worker: Aborted %s request for %s: %s", t_strcut(conn->request->data, '\t'), conn->request->username, reason); conn->request->callback(t_strdup_printf( "FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE), conn->request->context); } if (conn->io != NULL) io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (conn->to != NULL) timeout_remove(&conn->to); if (close(conn->fd) < 0) i_error("close(auth worker) failed: %m"); i_free(conn); if (idle_count == 0 && restart) { conn = auth_worker_create(); if (conn != NULL) auth_worker_request_send_next(conn); } } static struct auth_worker_connection *auth_worker_find_free(void) { struct auth_worker_connection **conns; if (idle_count == 0) return NULL; array_foreach_modifiable(&connections, conns) { struct auth_worker_connection *conn = *conns; if (conn->request == NULL) return conn; } i_unreached(); return NULL; } static bool auth_worker_request_handle(struct auth_worker_connection *conn, struct auth_worker_request *request, const char *line) { if (strncmp(line, "*\t", 2) == 0) { /* multi-line reply, not finished yet */ if (conn->resuming) timeout_reset(conn->to); else { conn->resuming = TRUE; timeout_remove(&conn->to); conn->to = timeout_add(AUTH_WORKER_RESUME_TIMEOUT_SECS * 1000, auth_worker_call_timeout, conn); } } else { conn->resuming = FALSE; conn->request = NULL; conn->timeout_pending_resume = FALSE; timeout_remove(&conn->to); conn->to = timeout_add(AUTH_WORKER_MAX_IDLE_SECS * 1000, auth_worker_idle_timeout, conn); idle_count++; } if (!request->callback(line, request->context) && conn->io != NULL) { conn->timeout_pending_resume = FALSE; timeout_remove(&conn->to); io_remove(&conn->io); return FALSE; } return TRUE; } static bool auth_worker_error(struct auth_worker_connection *conn) { if (conn->received_error) return TRUE; conn->received_error = TRUE; auth_workers_with_errors++; i_assert(auth_workers_with_errors <= array_count(&connections)); if (auth_workers_with_errors == 1) { /* this is the only failing auth worker connection. don't create new ones until this one sends SUCCESS. */ auth_workers_throttle_count = array_count(&connections); return TRUE; } /* too many auth workers, reduce them */ i_assert(array_count(&connections) > 1); if (auth_workers_throttle_count >= array_count(&connections)) auth_workers_throttle_count = array_count(&connections)-1; else if (auth_workers_throttle_count > 1) auth_workers_throttle_count--; auth_worker_destroy(&conn, "Internal auth worker failure", FALSE); return FALSE; } static void auth_worker_success(struct auth_worker_connection *conn) { unsigned int max_count = global_auth_settings->worker_max_count; if (!conn->received_error) return; i_assert(auth_workers_with_errors > 0); i_assert(auth_workers_with_errors <= array_count(&connections)); auth_workers_with_errors--; if (auth_workers_with_errors == 0) { /* all workers are succeeding now, set the limit back to original. */ auth_workers_throttle_count = max_count; } else if (auth_workers_throttle_count < max_count) auth_workers_throttle_count++; conn->received_error = FALSE; } static void worker_input(struct auth_worker_connection *conn) { const char *line, *id_str; unsigned int id; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_worker_destroy(&conn, "Worker process died unexpectedly", TRUE); return; case -2: /* buffer full */ i_error("BUG: Auth worker sent us more than %d bytes", (int)AUTH_WORKER_MAX_LINE_LENGTH); auth_worker_destroy(&conn, "Worker is buggy", TRUE); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { if (strcmp(line, "RESTART") == 0) { conn->restart = TRUE; continue; } if (strcmp(line, "SHUTDOWN") == 0) { conn->shutdown = TRUE; continue; } if (strcmp(line, "ERROR") == 0) { if (!auth_worker_error(conn)) return; continue; } if (strcmp(line, "SUCCESS") == 0) { auth_worker_success(conn); continue; } id_str = line; line = strchr(line, '\t'); if (line == NULL || str_to_uint(t_strdup_until(id_str, line), &id) < 0) continue; if (conn->request != NULL && id == conn->request->id) { if (!auth_worker_request_handle(conn, conn->request, line + 1)) break; } else { if (conn->request != NULL) { i_error("BUG: Worker sent reply with id %u, " "expected %u", id, conn->request->id); } else { i_error("BUG: Worker sent reply with id %u, " "none was expected", id); } auth_worker_destroy(&conn, "Worker is buggy", TRUE); return; } } if (conn->request != NULL) { /* there's still a pending request */ } else if (conn->restart) auth_worker_destroy(&conn, "Max requests limit", TRUE); else if (conn->shutdown) auth_worker_destroy(&conn, "Idle kill", FALSE); else auth_worker_request_send_next(conn); } static void worker_input_resume(struct auth_worker_connection *conn) { conn->timeout_pending_resume = FALSE; timeout_remove(&conn->to); conn->to = timeout_add(AUTH_WORKER_RESUME_TIMEOUT_SECS * 1000, auth_worker_call_timeout, conn); worker_input(conn); } struct auth_worker_connection * auth_worker_call(pool_t pool, const char *username, const char *data, auth_worker_callback_t *callback, void *context) { struct auth_worker_connection *conn; struct auth_worker_request *request; request = p_new(pool, struct auth_worker_request, 1); request->created = ioloop_time; request->username = p_strdup(pool, username); request->data = p_strdup(pool, data); request->callback = callback; request->context = context; if (aqueue_count(worker_request_queue) > 0) { /* requests are already being queued, no chance of finding/creating a worker */ conn = NULL; } else { conn = auth_worker_find_free(); if (conn == NULL) { /* no free connections, create a new one */ conn = auth_worker_create(); } } if (conn != NULL) { if (!auth_worker_request_send(conn, request)) i_unreached(); } else { /* reached the limit, queue the request */ aqueue_append(worker_request_queue, &request); } return conn; } void auth_worker_server_resume_input(struct auth_worker_connection *conn) { if (conn->request == NULL) { /* request was just finished, don't try to resume it */ return; } if (conn->io == NULL) conn->io = io_add(conn->fd, IO_READ, worker_input, conn); if (!conn->timeout_pending_resume) { conn->timeout_pending_resume = TRUE; if (conn->to != NULL) timeout_remove(&conn->to); conn->to = timeout_add_short(0, worker_input_resume, conn); } } void auth_worker_server_init(void) { worker_socket_path = "auth-worker"; auth_workers_throttle_count = global_auth_settings->worker_max_count; i_assert(auth_workers_throttle_count > 0); i_array_init(&worker_request_array, 128); worker_request_queue = aqueue_init(&worker_request_array.arr); i_array_init(&connections, 16); } void auth_worker_server_deinit(void) { struct auth_worker_connection **connp, *conn; while (array_count(&connections) > 0) { connp = array_idx_modifiable(&connections, 0); conn = *connp; auth_worker_destroy(&conn, "Shutting down", FALSE); } array_free(&connections); aqueue_deinit(&worker_request_queue); array_free(&worker_request_array); } dovecot-2.2.33.2/src/auth/auth-client-connection.c0000644000175000017500000002626613165463624016621 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "net.h" #include "hex-binary.h" #include "hostpid.h" #include "llist.h" #include "str.h" #include "str-sanitize.h" #include "randgen.h" #include "safe-memset.h" #include "master-service.h" #include "mech.h" #include "auth-fields.h" #include "auth-request-handler.h" #include "auth-client-interface.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #define OUTBUF_THROTTLE_SIZE (1024*50) #define AUTH_DEBUG_SENSITIVE_SUFFIX \ " (previous base64 data may contain sensitive data)" static void auth_client_disconnected(struct auth_client_connection **_conn); static void auth_client_connection_unref(struct auth_client_connection **_conn); static void auth_client_input(struct auth_client_connection *conn); static struct auth_client_connection *auth_client_connections; static const char *reply_line_hide_pass(const char *line) { string_t *newline; const char *p, *p2; if (strstr(line, "pass") == NULL) return line; newline = t_str_new(strlen(line)); const char *const *fields = t_strsplit(line, "\t"); while(*fields != NULL) { p = strstr(*fields, "pass"); p2 = strchr(*fields, '='); if (p == NULL || p2 == NULL || p2 < p) { str_append(newline, *fields); } else { /* include = */ str_append_data(newline, *fields, (p2 - *fields)+1); str_append(newline, PASSWORD_HIDDEN_STR); } str_append_c(newline, '\t'); fields++; } return str_c(newline); } static void auth_client_send(struct auth_client_connection *conn, const char *cmd) { struct const_iovec iov[2]; iov[0].iov_base = cmd; iov[0].iov_len = strlen(cmd); iov[1].iov_base = "\n"; iov[1].iov_len = 1; o_stream_nsendv(conn->output, iov, 2); if (o_stream_get_buffer_used_size(conn->output) >= OUTBUF_THROTTLE_SIZE) { /* stop reading new requests until client has read the pending replies. */ if (conn->io != NULL) io_remove(&conn->io); } if (conn->auth->set->debug) { i_debug("client passdb out: %s", conn->auth->set->debug_passwords ? cmd : reply_line_hide_pass(cmd)); } } static void auth_callback(const char *reply, struct auth_client_connection *conn) { if (reply == NULL) { /* handler destroyed */ auth_client_connection_unref(&conn); } else { auth_client_send(conn, reply); } } static bool auth_client_input_cpid(struct auth_client_connection *conn, const char *args) { struct auth_client_connection *old; unsigned int pid; i_assert(conn->pid == 0); if (str_to_uint(args, &pid) < 0 || pid == 0) { i_error("BUG: Authentication client said it's PID 0"); return FALSE; } if (conn->login_requests) old = auth_client_connection_lookup(pid); else { /* the client is only authenticating, not logging in. the PID isn't necessary, and since we allow authentication via TCP sockets the PIDs may conflict, so ignore them. */ old = NULL; pid = 0; } if (old != NULL) { /* already exists. it's possible that it just reconnected, see if the old connection is still there. */ i_assert(old != conn); if (i_stream_read(old->input) == -1) { auth_client_disconnected(&old); old = NULL; } } if (old != NULL) { i_error("BUG: Authentication client gave a PID " "%u of existing connection", pid); return FALSE; } /* handshake complete, we can now actually start serving requests */ conn->refcount++; conn->request_handler = auth_request_handler_create(conn->token_auth, auth_callback, conn, !conn->login_requests ? NULL : auth_master_request_callback); auth_request_handler_set(conn->request_handler, conn->connect_uid, pid); conn->pid = pid; if (conn->auth->set->debug) i_debug("auth client connected (pid=%u)", conn->pid); return TRUE; } static int auth_client_output(struct auth_client_connection *conn) { if (o_stream_flush(conn->output) < 0) { auth_client_disconnected(&conn); return 1; } if (o_stream_get_buffer_used_size(conn->output) <= OUTBUF_THROTTLE_SIZE/3 && conn->io == NULL) { /* allow input again */ conn->io = io_add(conn->fd, IO_READ, auth_client_input, conn); } return 1; } static const char * auth_line_hide_pass(struct auth_client_connection *conn, const char *line) { const char *p, *p2; p = strstr(line, "\tresp="); if (p == NULL) return line; p += 6; if (conn->auth->set->debug_passwords) return t_strconcat(line, AUTH_DEBUG_SENSITIVE_SUFFIX, NULL); p2 = strchr(p, '\t'); return t_strconcat(t_strdup_until(line, p), PASSWORD_HIDDEN_STR, p2, NULL); } static const char * cont_line_hide_pass(struct auth_client_connection *conn, const char *line) { const char *p; if (conn->auth->set->debug_passwords) return t_strconcat(line, AUTH_DEBUG_SENSITIVE_SUFFIX, NULL); p = strchr(line, '\t'); if (p == NULL) return line; return t_strconcat(t_strdup_until(line, p), PASSWORD_HIDDEN_STR, NULL); } static bool auth_client_cancel(struct auth_client_connection *conn, const char *line) { unsigned int client_id; if (str_to_uint(line, &client_id) < 0) { i_error("BUG: Authentication client sent broken CANCEL"); return FALSE; } auth_request_handler_cancel_request(conn->request_handler, client_id); return TRUE; } static bool auth_client_handle_line(struct auth_client_connection *conn, const char *line) { if (strncmp(line, "AUTH\t", 5) == 0) { if (conn->auth->set->debug) { i_debug("client in: %s", auth_line_hide_pass(conn, line)); } return auth_request_handler_auth_begin(conn->request_handler, line + 5); } if (strncmp(line, "CONT\t", 5) == 0) { if (conn->auth->set->debug) { i_debug("client in: %s", cont_line_hide_pass(conn, line)); } return auth_request_handler_auth_continue(conn->request_handler, line + 5); } if (strncmp(line, "CANCEL\t", 7) == 0) { if (conn->auth->set->debug) i_debug("client in: %s", line); return auth_client_cancel(conn, line + 7); } i_error("BUG: Authentication client sent unknown command: %s", str_sanitize(line, 80)); return FALSE; } static void auth_client_input(struct auth_client_connection *conn) { char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_client_disconnected(&conn); return; case -2: /* buffer full */ i_error("BUG: Auth client %u sent us more than %d bytes", conn->pid, (int)AUTH_CLIENT_MAX_LINE_LENGTH); auth_client_connection_destroy(&conn); return; } while (conn->request_handler == NULL) { /* still handshaking */ line = i_stream_next_line(conn->input); if (line == NULL) return; if (!conn->version_received) { /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication client " "not compatible with this server " "(mixed old and new binaries?)"); auth_client_connection_destroy(&conn); return; } conn->version_received = TRUE; continue; } if (strncmp(line, "CPID\t", 5) == 0) { if (!auth_client_input_cpid(conn, line + 5)) { auth_client_connection_destroy(&conn); return; } } else { i_error("BUG: Authentication client sent " "unknown handshake command: %s", str_sanitize(line, 80)); auth_client_connection_destroy(&conn); return; } } conn->refcount++; while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = auth_client_handle_line(conn, line); safe_memset(line, 0, strlen(line)); } T_END; if (!ret) { struct auth_client_connection *tmp_conn = conn; auth_client_connection_destroy(&tmp_conn); break; } } auth_client_connection_unref(&conn); } void auth_client_connection_create(struct auth *auth, int fd, bool login_requests, bool token_auth) { static unsigned int connect_uid_counter = 0; struct auth_client_connection *conn; const char *mechanisms; string_t *str; conn = i_new(struct auth_client_connection, 1); conn->auth = auth; conn->refcount = 1; conn->connect_uid = ++connect_uid_counter; conn->login_requests = login_requests; conn->token_auth = token_auth; random_fill(conn->cookie, sizeof(conn->cookie)); conn->fd = fd; conn->input = i_stream_create_fd(fd, AUTH_CLIENT_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); o_stream_set_flush_callback(conn->output, auth_client_output, conn); conn->io = io_add(fd, IO_READ, auth_client_input, conn); DLLIST_PREPEND(&auth_client_connections, conn); if (token_auth) { mechanisms = t_strconcat("MECH\t", mech_dovecot_token.mech_name, "\n", NULL); } else { mechanisms = str_c(auth->reg->handshake); } str = t_str_new(128); str_printfa(str, "VERSION\t%u\t%u\n%sSPID\t%s\nCUID\t%u\nCOOKIE\t", AUTH_CLIENT_PROTOCOL_MAJOR_VERSION, AUTH_CLIENT_PROTOCOL_MINOR_VERSION, mechanisms, my_pid, conn->connect_uid); binary_to_hex_append(str, conn->cookie, sizeof(conn->cookie)); str_append(str, "\nDONE\n"); if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) auth_client_disconnected(&conn); } void auth_client_connection_destroy(struct auth_client_connection **_conn) { struct auth_client_connection *conn = *_conn; *_conn = NULL; if (conn->fd == -1) return; DLLIST_REMOVE(&auth_client_connections, conn); i_stream_close(conn->input); o_stream_close(conn->output); if (conn->io != NULL) io_remove(&conn->io); net_disconnect(conn->fd); conn->fd = -1; if (conn->request_handler != NULL) { auth_request_handler_abort_requests(conn->request_handler); auth_request_handler_destroy(&conn->request_handler); } master_service_client_connection_destroyed(master_service); auth_client_connection_unref(&conn); } static void auth_client_disconnected(struct auth_client_connection **_conn) { struct auth_client_connection *conn = *_conn; unsigned int request_count; int err; *_conn = NULL; if (conn->input->stream_errno != 0) err = conn->input->stream_errno; else if (conn->output->stream_errno != 0) err = conn->output->stream_errno; else err = 0; request_count = conn->request_handler == NULL ? 0 : auth_request_handler_get_request_count(conn->request_handler); if (request_count > 0) { i_warning("auth client %u disconnected with %u " "pending requests: %s", conn->pid, request_count, err == 0 ? "EOF" : strerror(err)); } auth_client_connection_destroy(&conn); } static void auth_client_connection_unref(struct auth_client_connection **_conn) { struct auth_client_connection *conn = *_conn; *_conn = NULL; if (--conn->refcount > 0) return; i_stream_unref(&conn->input); o_stream_unref(&conn->output); i_free(conn); } struct auth_client_connection * auth_client_connection_lookup(unsigned int pid) { struct auth_client_connection *conn; for (conn = auth_client_connections; conn != NULL; conn = conn->next) { if (conn->pid == pid) return conn; } return NULL; } void auth_client_connections_destroy_all(void) { struct auth_client_connection *conn; while (auth_client_connections != NULL) { conn = auth_client_connections; auth_client_connection_destroy(&conn); } } dovecot-2.2.33.2/src/auth/userdb-static.c0000644000175000017500000000731713165463624015014 00000000000000/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "userdb.h" #include "userdb-template.h" struct static_context { userdb_callback_t *callback, *old_callback; void *old_context; }; struct static_userdb_module { struct userdb_module module; struct userdb_template *tmpl; unsigned int allow_all_users:1; }; static void static_lookup_real(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct static_userdb_module *module = (struct static_userdb_module *)_module; userdb_template_export(module->tmpl, auth_request); callback(USERDB_RESULT_OK, auth_request); } static void static_credentials_callback(enum passdb_result result, const unsigned char *credentials ATTR_UNUSED, size_t size ATTR_UNUSED, struct auth_request *auth_request) { struct static_context *ctx = auth_request->context; auth_request->userdb_lookup = TRUE; auth_request->private_callback.userdb = ctx->old_callback; auth_request->context = ctx->old_context; auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB); switch (result) { case PASSDB_RESULT_OK: static_lookup_real(auth_request, ctx->callback); break; case PASSDB_RESULT_USER_UNKNOWN: case PASSDB_RESULT_USER_DISABLED: case PASSDB_RESULT_PASS_EXPIRED: ctx->callback(USERDB_RESULT_USER_UNKNOWN, auth_request); break; case PASSDB_RESULT_SCHEME_NOT_AVAILABLE: auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "passdb doesn't support lookups, " "can't verify user's existence"); /* fall through */ default: ctx->callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); break; } i_free(ctx); } static void static_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct static_userdb_module *module = (struct static_userdb_module *)_module; struct static_context *ctx; if (!auth_request->successful && !module->allow_all_users) { /* this is a userdb-only lookup. we need to know if this users exists or not. use a passdb lookup to do that. if the passdb doesn't support returning credentials, this will of course fail.. */ ctx = i_new(struct static_context, 1); ctx->old_callback = auth_request->private_callback.userdb; ctx->old_context = auth_request->context; ctx->callback = callback; i_assert(auth_request->state == AUTH_REQUEST_STATE_USERDB); auth_request_set_state(auth_request, AUTH_REQUEST_STATE_MECH_CONTINUE); auth_request->context = ctx; if (auth_request->passdb != NULL) { /* kludge: temporarily work as if we weren't doing a userdb lookup. this is to get auth cache to use passdb caching instead of userdb caching. */ auth_request->userdb_lookup = FALSE; auth_request_lookup_credentials(auth_request, "", static_credentials_callback); } else { static_credentials_callback( PASSDB_RESULT_SCHEME_NOT_AVAILABLE, &uchar_nul, 0, auth_request); } } else { static_lookup_real(auth_request, callback); } } static struct userdb_module * static_preinit(pool_t pool, const char *args) { struct static_userdb_module *module; const char *value; module = p_new(pool, struct static_userdb_module, 1); module->tmpl = userdb_template_build(pool, "static", args); if (userdb_template_remove(module->tmpl, "allow_all_users", &value)) { module->allow_all_users = value == NULL || strcasecmp(value, "yes") == 0; } return &module->module; } struct userdb_module_interface userdb_static = { "static", static_preinit, NULL, NULL, static_lookup, NULL, NULL, NULL }; dovecot-2.2.33.2/src/auth/password-scheme-otp.c0000644000175000017500000000172513123174404016131 00000000000000/* * OTP password scheme. * * Copyright (c) 2006 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "hex-binary.h" #include "password-scheme.h" #include "randgen.h" #include "otp.h" int password_generate_otp(const char *pw, const char *state_data, unsigned int algo, const char **result_r) { struct otp_state state; if (state_data != NULL) { if (otp_parse_dbentry(state_data, &state) != 0) return -1; } else { /* Generate new OTP credentials from plaintext */ unsigned char random_data[OTP_MAX_SEED_LEN / 2]; const char *random_hex; random_fill(random_data, sizeof(random_data)); random_hex = binary_to_hex(random_data, sizeof(random_data)); if (i_strocpy(state.seed, random_hex, sizeof(state.seed)) < 0) i_unreached(); state.seq = 1024; state.algo = algo; } otp_hash(state.algo, state.seed, pw, state.seq, state.hash); *result_r = otp_print_dbentry(&state); return 0; } dovecot-2.2.33.2/src/auth/db-checkpassword.h0000644000175000017500000000157013123174404015453 00000000000000#ifndef CHECKPASSWORD_COMMON_H #define CHECKPASSWORD_COMMON_H #include "auth-request.h" enum db_checkpassword_status { DB_CHECKPASSWORD_STATUS_INTERNAL_FAILURE = -1, /* auth unsuccessful / user not found */ DB_CHECKPASSWORD_STATUS_FAILURE = 0, DB_CHECKPASSWORD_STATUS_OK = 1 }; typedef void db_checkpassword_callback_t(struct auth_request *request, enum db_checkpassword_status status, const char *const *extra_fields, void (*request_callback)()); struct db_checkpassword * db_checkpassword_init(const char *checkpassword_path, const char *checkpassword_reply_path); void db_checkpassword_deinit(struct db_checkpassword **db); void db_checkpassword_call(struct db_checkpassword *db, struct auth_request *request, const char *auth_password, db_checkpassword_callback_t *callback, void (*request_callback)()) ATTR_NULL(3); #endif dovecot-2.2.33.2/src/auth/passdb-dict.c0000644000175000017500000001225013123174404014415 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "passdb.h" #include "array.h" #include "str.h" #include "var-expand.h" #include "dict.h" #include "password-scheme.h" #include "auth-cache.h" #include "db-dict.h" #include struct dict_passdb_module { struct passdb_module module; struct dict_connection *conn; }; struct passdb_dict_request { struct auth_request *auth_request; union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; } callback; }; static int dict_query_save_results(struct auth_request *auth_request, struct dict_connection *conn, struct db_dict_value_iter *iter) { const char *key, *value, *error; while (db_dict_value_iter_next(iter, &key, &value)) { if (value != NULL) { auth_request_set_field(auth_request, key, value, conn->set.default_pass_scheme); } } if (db_dict_value_iter_deinit(&iter, &error) < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "%s", error); return -1; } return 0; } static enum passdb_result passdb_dict_lookup_key(struct auth_request *auth_request, struct dict_passdb_module *module) { struct db_dict_value_iter *iter; int ret; ret = db_dict_value_iter_init(module->conn, auth_request, &module->conn->set.passdb_fields, &module->conn->set.parsed_passdb_objects, &iter); if (ret < 0) return PASSDB_RESULT_INTERNAL_FAILURE; else if (ret == 0) { auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); return PASSDB_RESULT_USER_UNKNOWN; } else { if (dict_query_save_results(auth_request, module->conn, iter) < 0) return PASSDB_RESULT_INTERNAL_FAILURE; if (auth_request->passdb_password == NULL && !auth_fields_exists(auth_request->extra_fields, "nopassword")) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "No password returned (and no nopassword)"); return PASSDB_RESULT_PASSWORD_MISMATCH; } else { return PASSDB_RESULT_OK; } } } static void passdb_dict_lookup_pass(struct passdb_dict_request *dict_request) { struct auth_request *auth_request = dict_request->auth_request; struct passdb_module *_module = auth_request->passdb->passdb; struct dict_passdb_module *module = (struct dict_passdb_module *)_module; const char *password = NULL, *scheme = NULL; enum passdb_result passdb_result; int ret; if (array_count(&module->conn->set.passdb_fields) == 0 && array_count(&module->conn->set.parsed_passdb_objects) == 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "No passdb_objects or passdb_fields specified"); passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } else { passdb_result = passdb_dict_lookup_key(auth_request, module); } if (passdb_result == PASSDB_RESULT_OK) { /* passdb_password may change on the way, so we'll need to strdup. */ password = t_strdup(auth_request->passdb_password); scheme = password_get_scheme(&password); /* auth_request_set_field() sets scheme */ i_assert(password == NULL || scheme != NULL); } if (auth_request->credentials_scheme != NULL) { passdb_handle_credentials(passdb_result, password, scheme, dict_request->callback.lookup_credentials, auth_request); } else { if (password != NULL) { ret = auth_request_password_verify(auth_request, auth_request->mech_password, password, scheme, AUTH_SUBSYS_DB); passdb_result = ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH; } dict_request->callback.verify_plain(passdb_result, auth_request); } } static void dict_verify_plain(struct auth_request *request, const char *password ATTR_UNUSED, verify_plain_callback_t *callback) { struct passdb_dict_request *dict_request; dict_request = p_new(request->pool, struct passdb_dict_request, 1); dict_request->auth_request = request; dict_request->callback.verify_plain = callback; passdb_dict_lookup_pass(dict_request); } static void dict_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { struct passdb_dict_request *dict_request; dict_request = p_new(request->pool, struct passdb_dict_request, 1); dict_request->auth_request = request; dict_request->callback.lookup_credentials = callback; passdb_dict_lookup_pass(dict_request); } static struct passdb_module * passdb_dict_preinit(pool_t pool, const char *args) { struct dict_passdb_module *module; struct dict_connection *conn; module = p_new(pool, struct dict_passdb_module, 1); module->conn = conn = db_dict_init(args); module->module.blocking = TRUE; module->module.default_cache_key = auth_cache_parse_key(pool, db_dict_parse_cache_key(&conn->set.keys, &conn->set.passdb_fields, &conn->set.parsed_passdb_objects)); module->module.default_pass_scheme = conn->set.default_pass_scheme; return &module->module; } static void passdb_dict_deinit(struct passdb_module *_module) { struct dict_passdb_module *module = (struct dict_passdb_module *)_module; db_dict_unref(&module->conn); } struct passdb_module_interface passdb_dict = { "dict", passdb_dict_preinit, NULL, passdb_dict_deinit, dict_verify_plain, dict_lookup_credentials, NULL }; dovecot-2.2.33.2/src/auth/db-dict.h0000644000175000017500000000414413123174404013536 00000000000000#ifndef DB_DICT_H #define DB_DICT_H #include "sql-api.h" struct auth_request; struct db_dict_value_iter; enum db_dict_value_format { DB_DICT_VALUE_FORMAT_VALUE = 0, DB_DICT_VALUE_FORMAT_JSON }; struct db_dict_key { const char *name; const char *key; const char *format; const char *default_value; enum db_dict_value_format parsed_format; }; ARRAY_DEFINE_TYPE(db_dict_key, struct db_dict_key); ARRAY_DEFINE_TYPE(db_dict_key_p, const struct db_dict_key *); struct db_dict_field { const char *name; const char *value; }; ARRAY_DEFINE_TYPE(db_dict_field, struct db_dict_field); struct db_dict_settings { const char *uri; const char *default_pass_scheme; const char *iterate_prefix; bool iterate_disable; ARRAY_TYPE(db_dict_key) keys; const char *passdb_objects; const char *userdb_objects; ARRAY_TYPE(db_dict_field) passdb_fields; ARRAY_TYPE(db_dict_field) userdb_fields; ARRAY_TYPE(db_dict_key_p) parsed_passdb_objects; ARRAY_TYPE(db_dict_key_p) parsed_userdb_objects; }; struct dict_connection { struct dict_connection *next; pool_t pool; int refcount; char *config_path; struct db_dict_settings set; struct dict *dict; }; struct dict_connection *db_dict_init(const char *config_path); void db_dict_unref(struct dict_connection **conn); /* Returns 1 if ok, 0 if a key without default_value wasn't returned ("user doesn't exist"), -1 if internal error */ int db_dict_value_iter_init(struct dict_connection *conn, struct auth_request *auth_request, const ARRAY_TYPE(db_dict_field) *fields, const ARRAY_TYPE(db_dict_key_p) *objects, struct db_dict_value_iter **iter_r); bool db_dict_value_iter_next(struct db_dict_value_iter *iter, const char **key_r, const char **value_r); int db_dict_value_iter_deinit(struct db_dict_value_iter **iter, const char **error_r); const char *db_dict_parse_cache_key(const ARRAY_TYPE(db_dict_key) *keys, const ARRAY_TYPE(db_dict_field) *fields, const ARRAY_TYPE(db_dict_key_p) *objects); /* private: */ const struct db_dict_key * db_dict_set_key_find(const ARRAY_TYPE(db_dict_key) *keys, const char *name); #endif dovecot-2.2.33.2/src/auth/passdb-vpopmail.c0000644000175000017500000001344313123174404015326 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ /* Thanks to Courier-IMAP for showing how the vpopmail API should be used */ #include "auth-common.h" #include "passdb.h" #ifdef PASSDB_VPOPMAIL #include "safe-memset.h" #include "password-scheme.h" #include "auth-cache.h" #include "userdb-vpopmail.h" #define VPOPMAIL_DEFAULT_PASS_SCHEME "CRYPT" /* pw_flags was added in vpopmail 5.4, olders use pw_gid field */ #ifndef VQPASSWD_HAS_PW_FLAGS # define pw_flags pw_gid #endif struct vpopmail_passdb_module { struct passdb_module module; struct ip_addr webmail_ip; }; static bool vpopmail_is_disabled(struct auth_request *request, const struct vqpasswd *vpw) { struct passdb_module *_module = request->passdb->passdb; struct vpopmail_passdb_module *module = (struct vpopmail_passdb_module *)_module; if (strcasecmp(request->service, "IMAP") == 0) { if ((vpw->pw_flags & NO_IMAP) != 0) { /* IMAP from webmail IP may still be allowed */ if (!net_ip_compare(&module->webmail_ip, &request->remote_ip)) return TRUE; } if ((vpw->pw_flags & NO_WEBMAIL) != 0) { if (net_ip_compare(&module->webmail_ip, &request->remote_ip)) return TRUE; } } if ((vpw->pw_flags & NO_POP) != 0 && strcasecmp(request->service, "POP3") == 0) return TRUE; return FALSE; } static char * vpopmail_password_lookup(struct auth_request *auth_request, bool *cleartext, enum passdb_result *result_r) { char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT]; struct vqpasswd *vpw; char *password; vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain); if (vpw == NULL) { *result_r = PASSDB_RESULT_USER_UNKNOWN; return NULL; } if (vpopmail_is_disabled(auth_request, vpw)) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "%s disabled in vpopmail for this user", auth_request->service); password = NULL; *result_r = PASSDB_RESULT_USER_DISABLED; } else { if (vpw->pw_clear_passwd != NULL && *vpw->pw_clear_passwd != '\0') { password = t_strdup_noconst(vpw->pw_clear_passwd); *cleartext = TRUE; } else if (!*cleartext) password = t_strdup_noconst(vpw->pw_passwd); else password = NULL; *result_r = password != NULL ? PASSDB_RESULT_OK : PASSDB_RESULT_SCHEME_NOT_AVAILABLE; } safe_memset(vpw->pw_passwd, 0, strlen(vpw->pw_passwd)); if (vpw->pw_clear_passwd != NULL) { safe_memset(vpw->pw_clear_passwd, 0, strlen(vpw->pw_clear_passwd)); } return password; } static void vpopmail_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { enum passdb_result result; char *password; bool cleartext = TRUE; password = vpopmail_password_lookup(request, &cleartext, &result); if (password == NULL) { callback(result, NULL, 0, request); return; } passdb_handle_credentials(PASSDB_RESULT_OK, password, "CLEARTEXT", callback, request); safe_memset(password, 0, strlen(password)); } static void vpopmail_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { enum passdb_result result; const char *scheme, *tmp_pass; char *crypted_pass; bool cleartext = FALSE; int ret; crypted_pass = vpopmail_password_lookup(request, &cleartext, &result); if (crypted_pass == NULL) { callback(result, request); return; } tmp_pass = crypted_pass; if (cleartext) scheme = "CLEARTEXT"; else { scheme = password_get_scheme(&tmp_pass); if (scheme == NULL) scheme = request->passdb->passdb->default_pass_scheme; } ret = auth_request_password_verify(request, password, tmp_pass, scheme, AUTH_SUBSYS_DB); safe_memset(crypted_pass, 0, strlen(crypted_pass)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } #ifdef POP_AUTH_OPEN_RELAY if (strcasecmp(request->service, "POP3") == 0 || strcasecmp(request->service, "IMAP") == 0) { const char *host = net_ip2addr(&request->remote_ip); /* vpopmail 5.4 does not understand IPv6 */ if (host[0] != '\0' && IPADDR_IS_V4(&request->remote_ip)) { /* use putenv() directly rather than env_put() which would leak memory every time we got here. use a static buffer for putenv() as SUSv2 requirements would otherwise corrupt our environment later. */ static char ip_env[256]; i_snprintf(ip_env, sizeof(ip_env), "TCPREMOTEIP=%s", host); putenv(ip_env); open_smtp_relay(); } } #endif callback(PASSDB_RESULT_OK, request); } static struct passdb_module * vpopmail_preinit(pool_t pool, const char *args) { static bool vauth_load_initialized = FALSE; struct vpopmail_passdb_module *module; const char *const *tmp; module = p_new(pool, struct vpopmail_passdb_module, 1); module->module.default_pass_scheme = VPOPMAIL_DEFAULT_PASS_SCHEME; module->module.blocking = TRUE; tmp = t_strsplit_spaces(args, " "); for (; *tmp != NULL; tmp++) { if (strncmp(*tmp, "cache_key=", 10) == 0) { module->module.default_cache_key = auth_cache_parse_key(pool, *tmp + 10); } else if (strncmp(*tmp, "webmail=", 8) == 0) { if (net_addr2ip(*tmp + 8, &module->webmail_ip) < 0) i_fatal("vpopmail: Invalid webmail IP address"); } else if (strcmp(*tmp, "blocking=no") == 0) { module->module.blocking = FALSE; } else { i_fatal("passdb vpopmail: Unknown setting: %s", *tmp); } } if (!vauth_load_initialized) { vauth_load_initialized = TRUE; if (vauth_open(0) != 0) i_fatal("vpopmail: vauth_open() failed"); } return &module->module; } static void vpopmail_deinit(struct passdb_module *module ATTR_UNUSED) { vclose(); } struct passdb_module_interface passdb_vpopmail = { "vpopmail", vpopmail_preinit, NULL, vpopmail_deinit, vpopmail_verify_plain, vpopmail_lookup_credentials, NULL }; #else struct passdb_module_interface passdb_vpopmail = { .name = "vpopmail" }; #endif dovecot-2.2.33.2/src/auth/userdb.c0000644000175000017500000001440313123174404013506 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "array.h" #include "ipwd.h" #include "auth-worker-server.h" #include "userdb.h" static ARRAY(struct userdb_module_interface *) userdb_interfaces; static ARRAY(struct userdb_module *) userdb_modules; static const struct userdb_module_interface userdb_iface_deinit = { .name = "deinit" }; static struct userdb_module_interface *userdb_interface_find(const char *name) { struct userdb_module_interface *const *ifaces; array_foreach(&userdb_interfaces, ifaces) { struct userdb_module_interface *iface = *ifaces; if (strcmp(iface->name, name) == 0) return iface; } return NULL; } void userdb_register_module(struct userdb_module_interface *iface) { struct userdb_module_interface *old_iface; old_iface = userdb_interface_find(iface->name); if (old_iface != NULL && old_iface->lookup == NULL) { /* replacing a "support not compiled in" userdb */ userdb_unregister_module(old_iface); } else if (old_iface != NULL) { i_panic("userdb_register_module(%s): Already registered", iface->name); } array_append(&userdb_interfaces, &iface, 1); } void userdb_unregister_module(struct userdb_module_interface *iface) { struct userdb_module_interface *const *ifaces; unsigned int idx; array_foreach(&userdb_interfaces, ifaces) { if (*ifaces == iface) { idx = array_foreach_idx(&userdb_interfaces, ifaces); array_delete(&userdb_interfaces, idx, 1); return; } } i_panic("userdb_unregister_module(%s): Not registered", iface->name); } uid_t userdb_parse_uid(struct auth_request *request, const char *str) { struct passwd pw; uid_t uid; if (str == NULL) return (uid_t)-1; if (str_to_uid(str, &uid) == 0) return uid; switch (i_getpwnam(str, &pw)) { case -1: i_error("getpwnam() failed: %m"); return (uid_t)-1; case 0: if (request != NULL) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Invalid UID value '%s'", str); } return (uid_t)-1; default: return pw.pw_uid; } } gid_t userdb_parse_gid(struct auth_request *request, const char *str) { struct group gr; gid_t gid; if (str == NULL) return (gid_t)-1; if (str_to_gid(str, &gid) == 0) return gid; switch (i_getgrnam(str, &gr)) { case -1: i_error("getgrnam() failed: %m"); return (gid_t)-1; case 0: if (request != NULL) { auth_request_log_error(request, AUTH_SUBSYS_DB, "Invalid GID value '%s'", str); } return (gid_t)-1; default: return gr.gr_gid; } } static struct userdb_module * userdb_find(const char *driver, const char *args, unsigned int *idx_r) { struct userdb_module *const *userdbs; unsigned int i, count; userdbs = array_get(&userdb_modules, &count); for (i = 0; i < count; i++) { if (strcmp(userdbs[i]->iface->name, driver) == 0 && strcmp(userdbs[i]->args, args) == 0) { *idx_r = i; return userdbs[i]; } } return NULL; } struct userdb_module * userdb_preinit(pool_t pool, const struct auth_userdb_settings *set) { static unsigned int auth_userdb_id = 0; struct userdb_module_interface *iface; struct userdb_module *userdb; unsigned int idx; iface = userdb_interface_find(set->driver); if (iface == NULL || iface->lookup == NULL) { /* maybe it's a plugin. try to load it. */ auth_module_load(t_strconcat("authdb_", set->driver, NULL)); iface = userdb_interface_find(set->driver); } if (iface == NULL) i_fatal("Unknown userdb driver '%s'", set->driver); if (iface->lookup == NULL) { i_fatal("Support not compiled in for userdb driver '%s'", set->driver); } if (iface->preinit == NULL && iface->init == NULL && *set->args != '\0') { i_fatal("userdb %s: No args are supported: %s", set->driver, set->args); } userdb = userdb_find(set->driver, set->args, &idx); if (userdb != NULL) return userdb; if (iface->preinit == NULL) userdb = p_new(pool, struct userdb_module, 1); else userdb = iface->preinit(pool, set->args); userdb->id = ++auth_userdb_id; userdb->iface = iface; userdb->args = p_strdup(pool, set->args); array_append(&userdb_modules, &userdb, 1); return userdb; } void userdb_init(struct userdb_module *userdb) { if (userdb->iface->init != NULL && userdb->init_refcount == 0) userdb->iface->init(userdb); userdb->init_refcount++; } void userdb_deinit(struct userdb_module *userdb) { unsigned int idx; i_assert(userdb->init_refcount > 0); if (--userdb->init_refcount > 0) return; if (userdb_find(userdb->iface->name, userdb->args, &idx) == NULL) i_unreached(); array_delete(&userdb_modules, idx, 1); if (userdb->iface->deinit != NULL) userdb->iface->deinit(userdb); /* make sure userdb isn't accessed again */ userdb->iface = &userdb_iface_deinit; } void userdbs_generate_md5(unsigned char md5[STATIC_ARRAY MD5_RESULTLEN]) { struct md5_context ctx; struct userdb_module *const *userdbs; unsigned int i, count; md5_init(&ctx); userdbs = array_get(&userdb_modules, &count); for (i = 0; i < count; i++) { md5_update(&ctx, &userdbs[i]->id, sizeof(userdbs[i]->id)); md5_update(&ctx, userdbs[i]->iface->name, strlen(userdbs[i]->iface->name)); md5_update(&ctx, userdbs[i]->args, strlen(userdbs[i]->args)); } md5_final(&ctx, md5); } extern struct userdb_module_interface userdb_prefetch; extern struct userdb_module_interface userdb_static; extern struct userdb_module_interface userdb_passwd; extern struct userdb_module_interface userdb_passwd_file; extern struct userdb_module_interface userdb_vpopmail; extern struct userdb_module_interface userdb_ldap; extern struct userdb_module_interface userdb_sql; extern struct userdb_module_interface userdb_nss; extern struct userdb_module_interface userdb_checkpassword; extern struct userdb_module_interface userdb_dict; void userdbs_init(void) { i_array_init(&userdb_interfaces, 16); i_array_init(&userdb_modules, 16); userdb_register_module(&userdb_passwd); userdb_register_module(&userdb_passwd_file); userdb_register_module(&userdb_prefetch); userdb_register_module(&userdb_static); userdb_register_module(&userdb_vpopmail); userdb_register_module(&userdb_ldap); userdb_register_module(&userdb_sql); userdb_register_module(&userdb_nss); userdb_register_module(&userdb_checkpassword); userdb_register_module(&userdb_dict); } void userdbs_deinit(void) { array_free(&userdb_modules); array_free(&userdb_interfaces); } dovecot-2.2.33.2/src/auth/mech-plain.c0000644000175000017500000000446713123174404014250 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "safe-memset.h" #include "mech.h" #include "passdb.h" #include "mech-plain-common.h" static void mech_plain_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { const char *authid, *authenid, *error; char *pass; size_t i, len; int count; /* authorization ID \0 authentication ID \0 pass. */ authid = (const char *) data; authenid = NULL; pass = NULL; count = 0; for (i = 0; i < data_size; i++) { if (data[i] == '\0') { if (++count == 1) authenid = (const char *) data + i+1; else { i++; len = data_size - i; pass = p_strndup(unsafe_data_stack_pool, data+i, len); break; } } } if (authenid != NULL && strcmp(authid, authenid) == 0) { /* the login username isn't different */ authid = ""; } if (count != 2) { /* invalid input */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "invalid input"); auth_request_fail(request); } else if (!auth_request_set_username(request, authenid, &error)) { /* invalid username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); } else if (*authid != '\0' && !auth_request_set_login_username(request, authid, &error)) { /* invalid login username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "login user: %s", error); auth_request_fail(request); } else { auth_request_verify_plain(request, pass, plain_verify_callback); } /* make sure it's cleared */ if (pass != NULL) safe_memset(pass, 0, strlen(pass)); } static struct auth_request *mech_plain_auth_new(void) { struct auth_request *request; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"plain_auth_request", 2048); request = p_new(pool, struct auth_request, 1); request->pool = pool; return request; } const struct mech_module mech_plain = { "PLAIN", .flags = MECH_SEC_PLAINTEXT, .passdb_need = MECH_PASSDB_NEED_VERIFY_PLAIN, mech_plain_auth_new, mech_generic_auth_initial, mech_plain_auth_continue, mech_generic_auth_free }; dovecot-2.2.33.2/src/auth/auth-cache.h0000644000175000017500000000366113165463624014250 00000000000000#ifndef AUTH_CACHE_H #define AUTH_CACHE_H struct auth_cache_node { struct auth_cache_node *prev, *next; time_t created; /* Total number of bytes used by this node */ uint32_t alloc_size:31; /* TRUE if the user gave the correct password the last time. */ uint32_t last_success:1; char data[4]; /* key \0 value \0 */ }; struct auth_cache; struct auth_request; /* Parses all %x variables from query and compresses them into tab-separated list, so it can be used as a cache key. */ char *auth_cache_parse_key(pool_t pool, const char *query); /* Create a new cache. max_size specifies the maximum amount of memory in bytes to use for cache (it's not fully exact). ttl_secs specifies time to live for cache record, requests older than that are not used. neg_ttl_secs specifies the TTL for negative entries. */ struct auth_cache *auth_cache_new(size_t max_size, unsigned int ttl_secs, unsigned int neg_ttl_secs); void auth_cache_free(struct auth_cache **cache); /* Clear the cache. Returns how many entries were removed. */ unsigned int ATTR_NOWARN_UNUSED_RESULT auth_cache_clear(struct auth_cache *cache); unsigned int auth_cache_clear_users(struct auth_cache *cache, const char *const *usernames); /* Look key from cache. key should be the same string as returned by auth_cache_parse_key(). Returned node can't be used after any other auth_cache_*() calls. */ const char * auth_cache_lookup(struct auth_cache *cache, const struct auth_request *request, const char *key, struct auth_cache_node **node_r, bool *expired_r, bool *neg_expired_r); /* Insert key => value into cache. "" value means negative cache entry. */ void auth_cache_insert(struct auth_cache *cache, struct auth_request *request, const char *key, const char *value, bool last_success); /* Remove key from cache */ void auth_cache_remove(struct auth_cache *cache, const struct auth_request *request, const char *key); #endif dovecot-2.2.33.2/src/auth/auth-postfix-connection.c0000644000175000017500000001375713165463624017040 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "ioloop.h" #include "net.h" #include "istream.h" #include "ostream.h" #include "llist.h" #include "str.h" #include "strescape.h" #include "str-sanitize.h" #include "master-service.h" #include "userdb.h" #include "auth-postfix-connection.h" #include #define MAX_INBUF_SIZE 1024 #define MAX_OUTBUF_SIZE (1024*50) struct auth_postfix_connection { struct auth_postfix_connection *prev, *next; struct auth *auth; int refcount; int fd; char *path; struct istream *input; struct ostream *output; struct io *io; unsigned int destroyed:1; }; static void postfix_input(struct auth_postfix_connection *conn); static void auth_postfix_connection_ref(struct auth_postfix_connection *conn); static void auth_postfix_connection_destroy(struct auth_postfix_connection **_conn); static void auth_postfix_connection_unref(struct auth_postfix_connection **_conn); static struct auth_postfix_connection *auth_postfix_connections; static int postfix_input_auth_request(struct auth_postfix_connection *conn, const char *username, struct auth_request **request_r, const char **error_r) { struct auth_request *auth_request; auth_request = auth_request_new_dummy(); auth_request->id = 1; auth_request->context = conn; auth_postfix_connection_ref(conn); (void)auth_request_import_info(auth_request, "service", "postfix"); auth_request_init(auth_request); if (!auth_request_set_username(auth_request, username, error_r)) { *request_r = auth_request; return FALSE; } *request_r = auth_request; return TRUE; } static void user_callback(enum userdb_result result, struct auth_request *auth_request) { struct auth_postfix_connection *conn = auth_request->context; string_t *str; const char *value; if (auth_request->userdb_lookup_tempfailed) result = USERDB_RESULT_INTERNAL_FAILURE; str = t_str_new(128); switch (result) { case USERDB_RESULT_INTERNAL_FAILURE: if (auth_request->userdb_lookup_tempfailed) value = auth_fields_find(auth_request->userdb_reply, "reason"); else value = NULL; str_printfa(str, "400 %s", value != NULL ? value: "Internal failure"); break; case USERDB_RESULT_USER_UNKNOWN: str_append(str, "500 User not found"); break; case USERDB_RESULT_OK: str_append(str, "200 1"); break; } if (conn->auth->set->debug) i_debug("postfix out: %s", str_c(str)); str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); i_assert(conn->io == NULL); if (!conn->destroyed) conn->io = io_add(conn->fd, IO_READ, postfix_input, conn); auth_request_unref(&auth_request); auth_postfix_connection_unref(&conn); } static bool postfix_input_user(struct auth_postfix_connection *conn, const char *username) { struct auth_request *auth_request; const char *error; io_remove(&conn->io); if (!postfix_input_auth_request(conn, username, &auth_request, &error)) { auth_request_log_info(auth_request, "postfix", "%s", error); user_callback(USERDB_RESULT_USER_UNKNOWN, auth_request); } else { auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB); auth_request_lookup_user(auth_request, user_callback); } return TRUE; } static bool auth_postfix_input_line(struct auth_postfix_connection *conn, const char *line) { if (conn->auth->set->debug) i_debug("postfix in: %s", line); if (strncasecmp(line, "get ", 4) == 0) return postfix_input_user(conn, line + 4); i_error("BUG: Unknown command in postfix socket: %s", str_sanitize(line, 80)); return FALSE; } static void postfix_input(struct auth_postfix_connection *conn) { char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_postfix_connection_destroy(&conn); return; case -2: /* buffer full */ i_error("BUG: Postfix sent us more than %d bytes", (int)MAX_INBUF_SIZE); auth_postfix_connection_destroy(&conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = auth_postfix_input_line(conn, line); } T_END; if (!ret) { auth_postfix_connection_destroy(&conn); return; } } } struct auth_postfix_connection * auth_postfix_connection_create(struct auth *auth, int fd) { struct auth_postfix_connection *conn; conn = i_new(struct auth_postfix_connection, 1); conn->refcount = 1; conn->fd = fd; conn->auth = auth; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); conn->io = io_add(fd, IO_READ, postfix_input, conn); DLLIST_PREPEND(&auth_postfix_connections, conn); return conn; } static void auth_postfix_connection_destroy(struct auth_postfix_connection **_conn) { struct auth_postfix_connection *conn = *_conn; *_conn = NULL; if (conn->destroyed) return; conn->destroyed = TRUE; DLLIST_REMOVE(&auth_postfix_connections, conn); if (conn->input != NULL) i_stream_close(conn->input); if (conn->output != NULL) o_stream_close(conn->output); if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { if (close(conn->fd) < 0) i_error("close(%s): %m", conn->path); conn->fd = -1; } master_service_client_connection_destroyed(master_service); auth_postfix_connection_unref(&conn); } static void auth_postfix_connection_ref(struct auth_postfix_connection *conn) { i_assert(conn->refcount > 0); conn->refcount++; } static void auth_postfix_connection_unref(struct auth_postfix_connection **_conn) { struct auth_postfix_connection *conn = *_conn; *_conn = NULL; i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; if (conn->input != NULL) i_stream_unref(&conn->input); if (conn->output != NULL) o_stream_unref(&conn->output); i_free(conn); } void auth_postfix_connections_destroy_all(void) { struct auth_postfix_connection *conn; while (auth_postfix_connections != NULL) { conn = auth_postfix_connections; auth_postfix_connection_destroy(&conn); } } dovecot-2.2.33.2/src/auth/auth-request.h0000644000175000017500000002466613165463624014705 00000000000000#ifndef AUTH_REQUEST_H #define AUTH_REQUEST_H #include "net.h" #include "var-expand.h" #include "mech.h" #include "userdb.h" #include "passdb.h" #include "auth-request-var-expand.h" #define AUTH_REQUEST_USER_KEY_IGNORE " " struct auth_client_connection; enum auth_request_state { AUTH_REQUEST_STATE_NEW, AUTH_REQUEST_STATE_PASSDB, AUTH_REQUEST_STATE_MECH_CONTINUE, AUTH_REQUEST_STATE_FINISHED, AUTH_REQUEST_STATE_USERDB, AUTH_REQUEST_STATE_MAX }; struct auth_request { int refcount; pool_t pool; enum auth_request_state state; /* user contains the user who is being authenticated. When master user is logging in as someone else, it gets more complicated. Initially user is set to master's username and the requested_login_user is set to destination username. After masterdb has validated user as a valid master user, master_user is set to user and user is set to requested_login_user. */ char *user, *requested_login_user, *master_user; /* original_username contains the username exactly as given by the client. this is needed at least with DIGEST-MD5 for password verification. however with master logins the master username has been dropped from it. */ const char *original_username; /* the username after doing all internal translations, but before being changed by a db lookup */ const char *translated_username; /* realm for the request, may be specified by some auth mechanisms */ const char *realm; char *mech_password; /* set if verify_plain() is called */ char *passdb_password; /* set after password lookup if successful */ /* extra_fields are returned in authentication reply. Fields prefixed with "userdb_" are automatically placed to userdb_reply instead. */ struct auth_fields *extra_fields; /* the whole userdb result reply */ struct auth_fields *userdb_reply; struct auth_request_proxy_dns_lookup_ctx *dns_lookup_ctx; /* The final result of passdb lookup (delayed due to asynchronous proxy DNS lookups) */ enum passdb_result passdb_result; const struct mech_module *mech; const struct auth_settings *set; struct auth_passdb *passdb; struct auth_userdb *userdb; struct stats *stats; /* passdb lookups have a handler, userdb lookups don't */ struct auth_request_handler *handler; struct auth_master_connection *master; unsigned int connect_uid; unsigned int client_pid; unsigned int id; time_t last_access; time_t delay_until; pid_t session_pid; const char *service, *mech_name, *session_id, *local_name, *client_id; struct ip_addr local_ip, remote_ip, real_local_ip, real_remote_ip; in_port_t local_port, remote_port, real_local_port, real_remote_port; struct timeout *to_abort, *to_penalty; unsigned int policy_penalty; unsigned int last_penalty; size_t initial_response_len; const unsigned char *initial_response; union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; set_credentials_callback_t *set_credentials; userdb_callback_t *userdb; } private_callback; const char *credentials_scheme; const unsigned char *delayed_credentials; size_t delayed_credentials_size; void *context; /* this is a lookup on auth socket (not login socket). skip any proxying stuff if enabled. */ unsigned int auth_only:1; /* we're doing a userdb lookup now (we may have done passdb lookup earlier) */ unsigned int userdb_lookup:1; /* DIGEST-MD5 kludge */ unsigned int domain_is_realm:1; /* auth_debug is enabled for this request */ unsigned int debug:1; /* flags received from auth client: */ unsigned int secured:1; unsigned int final_resp_ok:1; unsigned int no_penalty:1; unsigned int valid_client_cert:1; unsigned int cert_username:1; unsigned int request_auth_token:1; /* success/failure states: */ unsigned int successful:1; unsigned int failed:1; /* overrides any other success */ unsigned int internal_failure:1; unsigned int passdbs_seen_user_unknown:1; unsigned int passdbs_seen_internal_failure:1; unsigned int userdbs_seen_internal_failure:1; /* current state: */ unsigned int accept_cont_input:1; unsigned int skip_password_check:1; unsigned int prefer_plain_credentials:1; unsigned int in_delayed_failure_queue:1; unsigned int removed_from_handler:1; unsigned int snapshot_have_userdb_prefetch_set:1; /* username was changed by this passdb/userdb lookup. Used by auth-workers to determine whether to send back a changed username. */ unsigned int user_changed_by_lookup:1; /* each passdb lookup can update the current success-status using the result_* rules. the authentication succeeds only if this is TRUE at the end. mechanisms that don't require passdb, but do a passdb lookup anyway (e.g. GSSAPI) need to set this to TRUE by default. */ unsigned int passdb_success:1; /* userdb equivalent of passdb_success */ unsigned int userdb_success:1; /* the last userdb lookup failed either due to "tempfail" extra field or because one of the returned uid/gid fields couldn't be translated to a number */ unsigned int userdb_lookup_tempfailed:1; /* userdb_* fields have been set by the passdb lookup, userdb prefetch will work. */ unsigned int userdb_prefetch_set:1; /* userdb lookup's results are from cache */ unsigned int userdb_result_from_cache:1; unsigned int stats_sent:1; unsigned int policy_refusal:1; unsigned int policy_processed:1; /* ... mechanism specific data ... */ }; typedef void auth_request_proxy_cb_t(bool success, struct auth_request *); extern unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX]; extern const char auth_default_subsystems[2]; #define AUTH_SUBSYS_DB &auth_default_subsystems[0] #define AUTH_SUBSYS_MECH &auth_default_subsystems[1] struct auth_request * auth_request_new(const struct mech_module *mech); struct auth_request *auth_request_new_dummy(void); void auth_request_init(struct auth_request *request); struct auth *auth_request_get_auth(struct auth_request *request); void auth_request_set_state(struct auth_request *request, enum auth_request_state state); void auth_request_ref(struct auth_request *request); void auth_request_unref(struct auth_request **request); void auth_request_success(struct auth_request *request, const void *data, size_t data_size); void auth_request_fail(struct auth_request *request); void auth_request_internal_failure(struct auth_request *request); void auth_request_export(struct auth_request *request, string_t *dest); bool auth_request_import(struct auth_request *request, const char *key, const char *value); bool auth_request_import_info(struct auth_request *request, const char *key, const char *value); bool auth_request_import_auth(struct auth_request *request, const char *key, const char *value); bool auth_request_import_master(struct auth_request *request, const char *key, const char *value); void auth_request_initial(struct auth_request *request); void auth_request_continue(struct auth_request *request, const unsigned char *data, size_t data_size); void auth_request_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback); void auth_request_lookup_credentials(struct auth_request *request, const char *scheme, lookup_credentials_callback_t *callback); void auth_request_lookup_user(struct auth_request *request, userdb_callback_t *callback); bool auth_request_set_username(struct auth_request *request, const char *username, const char **error_r); bool auth_request_set_login_username(struct auth_request *request, const char *username, const char **error_r); void auth_request_set_field(struct auth_request *request, const char *name, const char *value, const char *default_scheme) ATTR_NULL(4); void auth_request_set_null_field(struct auth_request *request, const char *name); void auth_request_set_field_keyvalue(struct auth_request *request, const char *field, const char *default_scheme) ATTR_NULL(3); void auth_request_set_fields(struct auth_request *request, const char *const *fields, const char *default_scheme) ATTR_NULL(3); void auth_request_init_userdb_reply(struct auth_request *request); void auth_request_set_userdb_field(struct auth_request *request, const char *name, const char *value); void auth_request_set_userdb_field_values(struct auth_request *request, const char *name, const char *const *values); /* returns -1 = failed, 0 = callback is called later, 1 = finished */ int auth_request_proxy_finish(struct auth_request *request, auth_request_proxy_cb_t *callback); void auth_request_proxy_finish_failure(struct auth_request *request); void auth_request_log_password_mismatch(struct auth_request *request, const char *subsystem); int auth_request_password_verify(struct auth_request *request, const char *plain_password, const char *crypted_password, const char *scheme, const char *subsystem); void auth_request_log_debug(struct auth_request *auth_request, const char *subsystem, const char *format, ...) ATTR_FORMAT(3, 4); void auth_request_log_info(struct auth_request *auth_request, const char *subsystem, const char *format, ...) ATTR_FORMAT(3, 4); void auth_request_log_warning(struct auth_request *auth_request, const char *subsystem, const char *format, ...) ATTR_FORMAT(3, 4); void auth_request_log_error(struct auth_request *auth_request, const char *subsystem, const char *format, ...) ATTR_FORMAT(3, 4); void auth_request_log_unknown_user(struct auth_request *auth_request, const char *subsystem); void auth_request_verify_plain_callback(enum passdb_result result, struct auth_request *request); void auth_request_lookup_credentials_callback(enum passdb_result result, const unsigned char *credentials, size_t size, struct auth_request *request); void auth_request_set_credentials(struct auth_request *request, const char *scheme, const char *data, set_credentials_callback_t *callback); void auth_request_userdb_callback(enum userdb_result result, struct auth_request *request); void auth_request_refresh_last_access(struct auth_request *request); void auth_str_append(string_t *dest, const char *key, const char *value); bool auth_request_username_accepted(const char *const *filter, const char *username); #endif dovecot-2.2.33.2/src/auth/userdb-dict.c0000644000175000017500000001217513165463624014446 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "userdb.h" #include "ioloop.h" #include "array.h" #include "str.h" #include "auth-cache.h" #include "db-dict.h" #include struct dict_userdb_module { struct userdb_module module; struct dict_connection *conn; }; struct dict_userdb_iterate_context { struct userdb_iterate_context ctx; userdb_callback_t *userdb_callback; const char *key_prefix; size_t key_prefix_len; struct dict_iterate_context *iter; }; static int dict_query_save_results(struct auth_request *auth_request, struct db_dict_value_iter *iter) { const char *key, *value, *error; while (db_dict_value_iter_next(iter, &key, &value)) { if (value != NULL) auth_request_set_userdb_field(auth_request, key, value); } if (db_dict_value_iter_deinit(&iter, &error) < 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "%s", error); return -1; } return 0; } static void userdb_dict_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { struct userdb_module *_module = auth_request->userdb->userdb; struct dict_userdb_module *module = (struct dict_userdb_module *)_module; struct db_dict_value_iter *iter; enum userdb_result userdb_result; int ret; if (array_count(&module->conn->set.userdb_fields) == 0 && array_count(&module->conn->set.parsed_userdb_objects) == 0) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "No userdb_objects or userdb_fields specified"); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } ret = db_dict_value_iter_init(module->conn, auth_request, &module->conn->set.userdb_fields, &module->conn->set.parsed_userdb_objects, &iter); if (ret < 0) userdb_result = USERDB_RESULT_INTERNAL_FAILURE; else if (ret == 0) { auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); userdb_result = USERDB_RESULT_USER_UNKNOWN; } else { if (dict_query_save_results(auth_request, iter) < 0) userdb_result = USERDB_RESULT_INTERNAL_FAILURE; else userdb_result = USERDB_RESULT_OK; } callback(userdb_result, auth_request); } static struct userdb_iterate_context * userdb_dict_iterate_init(struct auth_request *auth_request, userdb_iter_callback_t *callback, void *context) { struct userdb_module *_module = auth_request->userdb->userdb; struct dict_userdb_module *module = (struct dict_userdb_module *)_module; struct dict_userdb_iterate_context *ctx; string_t *path; ctx = i_new(struct dict_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; ctx->ctx.callback = callback; ctx->ctx.context = context; auth_request_ref(auth_request); if (*module->conn->set.iterate_prefix == '\0') { if (!module->conn->set.iterate_disable) { auth_request_log_error(auth_request, AUTH_SUBSYS_DB, "iterate: iterate_prefix not set"); ctx->ctx.failed = TRUE; } return &ctx->ctx; } path = t_str_new(128); str_append(path, DICT_PATH_SHARED); auth_request_var_expand(path, module->conn->set.iterate_prefix, auth_request, NULL); ctx->key_prefix = p_strdup(auth_request->pool, str_c(path)); ctx->key_prefix_len = strlen(ctx->key_prefix); ctx->iter = dict_iterate_init(module->conn->dict, ctx->key_prefix, 0); auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "iterate: prefix=%s", ctx->key_prefix); return &ctx->ctx; } static const char * userdb_dict_get_user(struct dict_userdb_iterate_context *ctx, const char *key) { i_assert(strncmp(key, ctx->key_prefix, ctx->key_prefix_len) == 0); return key + ctx->key_prefix_len; } static void userdb_dict_iterate_next(struct userdb_iterate_context *_ctx) { struct dict_userdb_iterate_context *ctx = (struct dict_userdb_iterate_context *)_ctx; const char *key, *value; if (ctx->iter != NULL && dict_iterate(ctx->iter, &key, &value)) _ctx->callback(userdb_dict_get_user(ctx, key), _ctx->context); else _ctx->callback(NULL, _ctx->context); } static int userdb_dict_iterate_deinit(struct userdb_iterate_context *_ctx) { struct dict_userdb_iterate_context *ctx = (struct dict_userdb_iterate_context *)_ctx; int ret = _ctx->failed ? -1 : 0; if (ctx->iter != NULL) { if (dict_iterate_deinit(&ctx->iter) < 0) ret = -1; } auth_request_unref(&ctx->ctx.auth_request); i_free(ctx); return ret; } static struct userdb_module * userdb_dict_preinit(pool_t pool, const char *args) { struct dict_userdb_module *module; struct dict_connection *conn; module = p_new(pool, struct dict_userdb_module, 1); module->conn = conn = db_dict_init(args); module->module.blocking = TRUE; module->module.default_cache_key = auth_cache_parse_key(pool, db_dict_parse_cache_key(&conn->set.keys, &conn->set.userdb_fields, &conn->set.parsed_userdb_objects)); return &module->module; } static void userdb_dict_deinit(struct userdb_module *_module) { struct dict_userdb_module *module = (struct dict_userdb_module *)_module; db_dict_unref(&module->conn); } struct userdb_module_interface userdb_dict = { "dict", userdb_dict_preinit, NULL, userdb_dict_deinit, userdb_dict_lookup, userdb_dict_iterate_init, userdb_dict_iterate_next, userdb_dict_iterate_deinit }; dovecot-2.2.33.2/src/auth/userdb-template.h0000644000175000017500000000066013165463624015337 00000000000000#ifndef USERDB_TEMPLATE_H #define USERDB_TEMPLATE_H struct userdb_template * userdb_template_build(pool_t pool, const char *userdb_name, const char *args); void userdb_template_export(struct userdb_template *tmpl, struct auth_request *auth_request); bool userdb_template_remove(struct userdb_template *tmpl, const char *key, const char **value_r); bool userdb_template_is_empty(struct userdb_template *tmpl); #endif dovecot-2.2.33.2/src/auth/Makefile.am0000644000175000017500000001412013165463624014121 00000000000000noinst_LIBRARIES = libpassword.a noinst_LTLIBRARIES = libauth.la auth_moduledir = $(moduledir)/auth # automake seems to force making this unconditional.. NOPLUGIN_LDFLAGS = if GSSAPI_PLUGIN GSSAPI_LIB = libmech_gssapi.la endif if LDAP_PLUGIN LDAP_LIB = libauthdb_ldap.la endif auth_module_LTLIBRARIES = \ $(GSSAPI_LIB) \ $(LDAP_LIB) \ libauthdb_imap.la pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = auth checkpassword-reply AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-stats \ -I$(top_srcdir)/src/lib-ntlm \ -I$(top_srcdir)/src/lib-otp \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-oauth2 \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ $(AUTH_CFLAGS) auth_LDFLAGS = -export-dynamic libpassword_a_SOURCES = \ mycrypt.c \ password-scheme.c \ password-scheme-crypt.c \ password-scheme-md5crypt.c \ password-scheme-scram.c \ password-scheme-otp.c \ password-scheme-rpa.c \ password-scheme-pbkdf2.c auth_libs = \ libauth.la \ libstats_auth.la \ libpassword.a \ ../lib-ntlm/libntlm.a \ ../lib-otp/libotp.a \ $(LIBDOVECOT_SQL) auth_LDADD = $(auth_libs) $(LIBDOVECOT) $(AUTH_LIBS) auth_CPPFLAGS = $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-ssl-iostream auth_DEPENDENCIES = $(auth_libs) $(LIBDOVECOT_DEPS) auth_SOURCES = main.c ldap_sources = db-ldap.c passdb-ldap.c userdb-ldap.c libauth_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libauth_la_SOURCES = \ auth.c \ auth-cache.c \ auth-client-connection.c \ auth-master-connection.c \ auth-policy.c \ auth-postfix-connection.c \ mech-otp-skey-common.c \ mech-plain-common.c \ auth-penalty.c \ auth-request.c \ auth-request-handler.c \ auth-request-stats.c \ auth-request-var-expand.c \ auth-settings.c \ auth-fields.c \ auth-token.c \ auth-worker-client.c \ auth-worker-server.c \ db-checkpassword.c \ db-dict.c \ db-dict-cache-key.c \ db-oauth2.c \ db-sql.c \ db-passwd-file.c \ mech.c \ mech-anonymous.c \ mech-plain.c \ mech-login.c \ mech-cram-md5.c \ mech-digest-md5.c \ mech-external.c \ mech-gssapi.c \ mech-ntlm.c \ mech-otp.c \ mech-scram-sha1.c \ mech-skey.c \ mech-rpa.c \ mech-apop.c \ mech-winbind.c \ mech-dovecot-token.c \ mech-oauth2.c \ passdb.c \ passdb-blocking.c \ passdb-bsdauth.c \ passdb-cache.c \ passdb-checkpassword.c \ passdb-dict.c \ passdb-oauth2.c \ passdb-passwd.c \ passdb-passwd-file.c \ passdb-pam.c \ passdb-shadow.c \ passdb-sia.c \ passdb-vpopmail.c \ passdb-sql.c \ passdb-static.c \ passdb-template.c \ userdb.c \ userdb-blocking.c \ userdb-checkpassword.c \ userdb-dict.c \ userdb-nss.c \ userdb-passwd.c \ userdb-passwd-file.c \ userdb-prefetch.c \ userdb-static.c \ userdb-vpopmail.c \ userdb-sql.c \ userdb-template.c \ $(ldap_sources) headers = \ auth.h \ auth-cache.h \ auth-client-connection.h \ auth-common.h \ auth-master-connection.h \ auth-postfix-connection.h \ mech-otp-skey-common.h \ mech-plain-common.h \ auth-penalty.h \ auth-policy.h \ auth-request.h \ auth-request-handler.h \ auth-request-stats.h \ auth-request-var-expand.h \ auth-settings.h \ auth-stats.h \ auth-fields.h \ auth-token.h \ auth-worker-client.h \ auth-worker-server.h \ db-dict.h \ db-ldap.h \ db-sql.h \ db-passwd-file.h \ db-checkpassword.h \ db-oauth2.h \ mech.h \ mycrypt.h \ passdb.h \ passdb-blocking.h \ passdb-cache.h \ passdb-template.h \ password-scheme.h \ userdb.h \ userdb-blocking.h \ userdb-template.h \ userdb-vpopmail.h if GSSAPI_PLUGIN libmech_gssapi_la_LDFLAGS = -module -avoid-version libmech_gssapi_la_LIBADD = $(KRB5_LIBS) libmech_gssapi_la_CPPFLAGS = $(AM_CPPFLAGS) $(KRB5_CFLAGS) -DPLUGIN_BUILD libmech_gssapi_la_SOURCES = mech-gssapi.c endif if LDAP_PLUGIN libauthdb_ldap_la_LDFLAGS = -module -avoid-version libauthdb_ldap_la_LIBADD = $(LDAP_LIBS) libauthdb_ldap_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD libauthdb_ldap_la_SOURCES = $(ldap_sources) endif libauthdb_imap_la_LDFLAGS = -module -avoid-version libauthdb_imap_la_LIBADD = \ ../lib-imap-client/libimap_client.la \ $(LIBDOVECOT) libauthdb_imap_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-imap-client libauthdb_imap_la_SOURCES = passdb-imap.c pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) checkpassword_reply_LDADD = $(LIBDOVECOT) checkpassword_reply_DEPENDENCIES = $(LIBDOVECOT_DEPS) checkpassword_reply_sources = \ checkpassword-reply.c stats_moduledir = $(moduledir)/stats stats_module_LTLIBRARIES = libstats_auth.la libstats_auth_la_LDFLAGS = -module -avoid-version libstats_auth_la_LIBADD = $(LIBDOVECOT) libstats_auth_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) libstats_auth_la_SOURCES = auth-stats.c test_programs = \ test-auth-cache \ test-auth-request-var-expand \ test-username-filter \ test-db-dict noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_auth_cache_SOURCES = auth-cache.c test-auth-cache.c test_auth_cache_LDADD = $(test_libs) test_auth_cache_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) # this is needed to force auth-cache.c recompilation test_auth_cache_CPPFLAGS = $(AM_CPPFLAGS) test_auth_request_var_expand_SOURCES = test-auth-request-var-expand.c test_auth_request_var_expand_LDADD = $(test_libs) libauth.la test_auth_request_var_expand_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) test_username_filter_SOURCES = test-username-filter.c test_username_filter_LDADD = $(auth_libs) $(AUTH_LIBS) $(LIBDOVECOT) test_username_filter_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) $(LIBDOVECOT_DEPS) test_db_dict_SOURCES = test-db-dict.c test_db_dict_LDADD = $(test_libs) libauth.la test_db_dict_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/auth/passdb-blocking.h0000644000175000017500000000045713123174404015275 00000000000000#ifndef PASSDB_BLOCKING_H #define PASSDB_BLOCKING_H void passdb_blocking_verify_plain(struct auth_request *request); void passdb_blocking_lookup_credentials(struct auth_request *request); void passdb_blocking_set_credentials(struct auth_request *request, const char *new_credentials); #endif dovecot-2.2.33.2/src/auth/mech-plain-common.c0000644000175000017500000000076413123174404015532 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "auth-common.h" #include "mech.h" #include "passdb.h" #include "mech-plain-common.h" void plain_verify_callback(enum passdb_result result, struct auth_request *request) { switch (result) { case PASSDB_RESULT_OK: auth_request_success(request, "", 0); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(request); break; default: auth_request_fail(request); break; } } dovecot-2.2.33.2/src/lib-sasl/0002755000175000017500000000000013172375610012711 500000000000000dovecot-2.2.33.2/src/lib-sasl/dsasl-client.h0000644000175000017500000000355513165463624015377 00000000000000#ifndef DSASL_CLIENT_H #define DSASL_CLIENT_H struct dsasl_client_settings { /* authentication ID - must be set with most mechanisms */ const char *authid; /* authorization ID (who to log in as, if authentication ID is a master user) */ const char *authzid; /* password - must be set with most mechanisms */ const char *password; }; /* PLAIN mechanism always exists and can be accessed directly via this. */ extern const struct dsasl_client_mech dsasl_client_mech_plain; const struct dsasl_client_mech *dsasl_client_mech_find(const char *name); const char *dsasl_client_mech_get_name(const struct dsasl_client_mech *mech); struct dsasl_client *dsasl_client_new(const struct dsasl_client_mech *mech, const struct dsasl_client_settings *set); void dsasl_client_free(struct dsasl_client **client); /* Call for server input. */ int dsasl_client_input(struct dsasl_client *client, const unsigned char *input, unsigned int input_len, const char **error_r); /* Call for getting server output. Also used to get the initial SASL response if supported by the protocol. */ int dsasl_client_output(struct dsasl_client *client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r); /* Call for setting extra parameters for authentication, these are mechanism dependant. -1 = error, 0 = not found, 1 = ok value can be NULL. */ int dsasl_client_set_parameter(struct dsasl_client *client, const char *param, const char *value, const char **error_r) ATTR_NULL(3); /* Call for getting extra result information. -1 = error, 0 = not found, 1 = ok */ int dsasl_client_get_result(struct dsasl_client *client, const char *key, const char **value_r, const char **error_r); void dsasl_clients_init(void); void dsasl_clients_deinit(void); #endif dovecot-2.2.33.2/src/lib-sasl/Makefile.in0000644000175000017500000005450213172375573014712 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-sasl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libsasl_la_LIBADD = am_libsasl_la_OBJECTS = mech-external.lo mech-login.lo mech-plain.lo \ mech-oauthbearer.lo dsasl-client.lo libsasl_la_OBJECTS = $(am_libsasl_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsasl_la_SOURCES) DIST_SOURCES = $(libsasl_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libsasl.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libsasl_la_SOURCES = \ mech-external.c \ mech-login.c \ mech-plain.c \ mech-oauthbearer.c \ dsasl-client.c headers = \ dsasl-client.h \ dsasl-client-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sasl/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-sasl/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsasl.la: $(libsasl_la_OBJECTS) $(libsasl_la_DEPENDENCIES) $(EXTRA_libsasl_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libsasl_la_OBJECTS) $(libsasl_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsasl-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-external.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-login.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-oauthbearer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mech-plain.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-sasl/mech-external.c0000644000175000017500000000264113165463624015537 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dsasl-client-private.h" struct external_dsasl_client { struct dsasl_client client; bool output_sent; }; static int mech_external_input(struct dsasl_client *_client, const unsigned char *input ATTR_UNUSED, unsigned int input_len, const char **error_r) { struct external_dsasl_client *client = (struct external_dsasl_client *)_client; if (!client->output_sent) { if (input_len > 0) { *error_r = "Server sent non-empty initial response"; return -1; } } else { *error_r = "Server didn't finish authentication"; return -1; } return 0; } static int mech_external_output(struct dsasl_client *_client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r ATTR_UNUSED) { struct external_dsasl_client *client = (struct external_dsasl_client *)_client; const char *username; if (_client->set.authzid != NULL) username = _client->set.authzid; else if (_client->set.authid != NULL) username = _client->set.authid; else username = ""; *output_r = (const void *)username; *output_len_r = strlen(username); client->output_sent = TRUE; return 0; } const struct dsasl_client_mech dsasl_client_mech_external = { .name = "EXTERNAL", .struct_size = sizeof(struct external_dsasl_client), .input = mech_external_input, .output = mech_external_output }; dovecot-2.2.33.2/src/lib-sasl/dsasl-client.c0000644000175000017500000000746213165463624015373 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "safe-memset.h" #include "dsasl-client-private.h" static int init_refcount = 0; static ARRAY(const struct dsasl_client_mech *) dsasl_mechanisms = ARRAY_INIT; static const struct dsasl_client_mech * dsasl_client_mech_find_idx(const char *name, unsigned int *idx_r) { const struct dsasl_client_mech *const *mechp; array_foreach(&dsasl_mechanisms, mechp) { if (strcasecmp((*mechp)->name, name) == 0) { *idx_r = array_foreach_idx(&dsasl_mechanisms, mechp); return *mechp; } } return NULL; } const struct dsasl_client_mech *dsasl_client_mech_find(const char *name) { unsigned int idx; return dsasl_client_mech_find_idx(name, &idx); } const char *dsasl_client_mech_get_name(const struct dsasl_client_mech *mech) { return mech->name; } void dsasl_client_mech_register(const struct dsasl_client_mech *mech) { unsigned int idx; if (dsasl_client_mech_find_idx(mech->name, &idx) != NULL) { /* allow plugins to override the default mechanisms */ array_delete(&dsasl_mechanisms, idx, 1); } array_append(&dsasl_mechanisms, &mech, 1); } void dsasl_client_mech_unregister(const struct dsasl_client_mech *mech) { unsigned int idx; if (dsasl_client_mech_find_idx(mech->name, &idx) == NULL) i_panic("SASL mechanism not registered: %s", mech->name); array_delete(&dsasl_mechanisms, idx, 1); } struct dsasl_client *dsasl_client_new(const struct dsasl_client_mech *mech, const struct dsasl_client_settings *set) { struct dsasl_client *client; pool_t pool = pool_alloconly_create("sasl client", 512); client = p_malloc(pool, mech->struct_size); client->pool = pool; client->mech = mech; client->set.authid = p_strdup(pool, set->authid); client->set.authzid = p_strdup(pool, set->authzid); client->password = p_strdup(pool, set->password); client->set.password = client->password; return client; } void dsasl_client_free(struct dsasl_client **_client) { struct dsasl_client *client = *_client; *_client = NULL; if (client->mech->free != NULL) client->mech->free(client); safe_memset(client->password, 0, strlen(client->password)); pool_unref(&client->pool); } int dsasl_client_input(struct dsasl_client *client, const unsigned char *input, unsigned int input_len, const char **error_r) { return client->mech->input(client, input, input_len, error_r); } int dsasl_client_output(struct dsasl_client *client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r) { return client->mech->output(client, output_r, output_len_r, error_r); } int dsasl_client_set_parameter(struct dsasl_client *client, const char *param, const char *value, const char **error_r) { if (client->mech->set_parameter != NULL) { int ret = client->mech->set_parameter(client, param, value, error_r); i_assert(ret >= 0 || *error_r != NULL); return ret; } else return 0; } int dsasl_client_get_result(struct dsasl_client *client, const char *key, const char **value_r, const char **error_r) { if (client->mech->get_result != NULL) { int ret = client->mech->get_result(client, key, value_r, error_r); i_assert(ret <= 0 || *value_r != NULL); i_assert(ret >= 0 || *error_r != NULL); return ret; } else return 0; } void dsasl_clients_init(void) { if (init_refcount++ > 0) return; i_array_init(&dsasl_mechanisms, 8); dsasl_client_mech_register(&dsasl_client_mech_external); dsasl_client_mech_register(&dsasl_client_mech_plain); dsasl_client_mech_register(&dsasl_client_mech_login); dsasl_client_mech_register(&dsasl_client_mech_oauthbearer); dsasl_client_mech_register(&dsasl_client_mech_xoauth2); } void dsasl_clients_deinit(void) { if (--init_refcount > 0) return; array_free(&dsasl_mechanisms); } dovecot-2.2.33.2/src/lib-sasl/mech-oauthbearer.c0000644000175000017500000001226413165463624016220 00000000000000/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "net.h" #include "json-parser.h" #include "istream.h" #include "dsasl-client-private.h" struct oauthbearer_dsasl_client { struct dsasl_client client; const char *host; const char *status; in_port_t port; bool output_sent; }; static int mech_oauthbearer_input(struct dsasl_client *_client, const unsigned char *input, unsigned int input_len, const char **error_r) { struct oauthbearer_dsasl_client *client = (struct oauthbearer_dsasl_client *)_client; if (!client->output_sent) { if (input_len > 0) { *error_r = "Server sent non-empty initial response"; return -1; } } else { client->status = ""; /* if response is empty, authentication has *SUCCEEDED* */ if (input_len == 0) return 0; /* authentication has failed, try parse status. we are only interested in extracting status if possible so we don't really need to much error handling. */ struct istream *is = i_stream_create_from_data(input, input_len); const char *status = NULL, *value; const char *error = NULL; enum json_type jtype; bool found_status = FALSE; struct json_parser *parser = json_parser_init(is); while (json_parse_next(parser, &jtype, &value)>0) { if (found_status && status == NULL) { if (jtype == JSON_TYPE_STRING || jtype == JSON_TYPE_NUMBER) status = t_strdup(value); break; } else if (jtype == JSON_TYPE_OBJECT_KEY && strcmp(value, "status") == 0) { found_status = TRUE; } else json_parse_skip_next(parser); } /* deinitialize json parser */ int ret = json_parser_deinit(&parser, &error); if (status != NULL) client->status = p_strdup(_client->pool, status); else { ret = -1; if (error == NULL) error = "Status value missing"; } if (ret < 0) *error_r = t_strdup_printf("Error parsing JSON reply: %s", error); else *error_r = t_strdup_printf("Failed to authenticate: %s", client->status); return -1; } return 0; } static int mech_oauthbearer_output(struct dsasl_client *_client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r) { struct oauthbearer_dsasl_client *client = (struct oauthbearer_dsasl_client *)_client; string_t *str; if (_client->set.authid == NULL) { *error_r = "authid not set"; return -1; } if (_client->password == NULL) { *error_r = "password not set"; return -1; } str = str_new(_client->pool, 64); str_printfa(str, "n,a=%s,\x01", _client->set.authid); if (client->host != NULL && *client->host != '\0') str_printfa(str, "host=%s\x01", client->host); if (client->port > 0) str_printfa(str, "port=%u\x01", client->port); str_printfa(str, "auth=Bearer %s\x01", _client->password); str_append_c(str, '\x01'); *output_r = str_data(str); *output_len_r = str_len(str); client->output_sent = TRUE; return 0; } static int mech_xoauth2_output(struct dsasl_client *_client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r) { struct oauthbearer_dsasl_client *client = (struct oauthbearer_dsasl_client *)_client; string_t *str; if (_client->set.authid == NULL) { *error_r = "authid not set"; return -1; } if (_client->password == NULL) { *error_r = "password not set"; return -1; } str = str_new(_client->pool, 64); str_printfa(str, "user=%s\x01", _client->set.authid); str_printfa(str, "auth=Bearer %s\x01", _client->password); str_append_c(str, '\x01'); *output_r = str_data(str); *output_len_r = str_len(str); client->output_sent = TRUE; return 0; } static int mech_oauthbearer_set_parameter(struct dsasl_client *_client, const char *key, const char *value, const char **error_r) { struct oauthbearer_dsasl_client *client = (struct oauthbearer_dsasl_client *)_client; if (strcmp(key, "host") == 0) { if (value != NULL) client->host = p_strdup(_client->pool, value); else client->host = NULL; return 1; } else if (strcmp(key, "port") == 0) { if (value == NULL) { client->port = 0; } else if (net_str2port(key, &client->port) < 0) { *error_r = "Invalid port value"; return -1; } return 1; } return 0; } static int mech_oauthbearer_get_result(struct dsasl_client *_client, const char *key, const char **value_r, const char **error_r ATTR_UNUSED) { struct oauthbearer_dsasl_client *client = (struct oauthbearer_dsasl_client *)_client; if (strcmp(key, "status") == 0) { /* this is set to value after login attempt */ i_assert(client->status != NULL); *value_r = client->status; return 1; } return 0; } const struct dsasl_client_mech dsasl_client_mech_oauthbearer = { .name = "OAUTHBEARER", .struct_size = sizeof(struct oauthbearer_dsasl_client), .input = mech_oauthbearer_input, .output = mech_oauthbearer_output, .set_parameter = mech_oauthbearer_set_parameter, .get_result = mech_oauthbearer_get_result, }; const struct dsasl_client_mech dsasl_client_mech_xoauth2 = { .name = "XOAUTH2", .struct_size = sizeof(struct oauthbearer_dsasl_client), .input = mech_oauthbearer_input, .output = mech_xoauth2_output, .set_parameter = mech_oauthbearer_set_parameter, .get_result = mech_oauthbearer_get_result, }; dovecot-2.2.33.2/src/lib-sasl/mech-login.c0000644000175000017500000000317713165463624015032 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "dsasl-client-private.h" enum login_state { STATE_INIT = 0, STATE_USER, STATE_PASS }; struct login_dsasl_client { struct dsasl_client client; enum login_state state; }; static int mech_login_input(struct dsasl_client *_client, const unsigned char *input ATTR_UNUSED, unsigned int input_len ATTR_UNUSED, const char **error_r) { struct login_dsasl_client *client = (struct login_dsasl_client *)_client; if (client->state == STATE_PASS) { *error_r = "Server didn't finish authentication"; return -1; } client->state++; return 0; } static int mech_login_output(struct dsasl_client *_client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r) { struct login_dsasl_client *client = (struct login_dsasl_client *)_client; if (_client->set.authid == NULL) { *error_r = "authid not set"; return -1; } if (_client->password == NULL) { *error_r = "password not set"; return -1; } switch (client->state) { case STATE_INIT: *output_r = &uchar_nul; *output_len_r = 0; return 0; case STATE_USER: *output_r = (const unsigned char *)_client->set.authid; *output_len_r = strlen(_client->set.authid); return 0; case STATE_PASS: *output_r = (const unsigned char *)_client->set.password; *output_len_r = strlen(_client->set.password); return 0; } i_unreached(); } const struct dsasl_client_mech dsasl_client_mech_login = { .name = "LOGIN", .struct_size = sizeof(struct login_dsasl_client), .input = mech_login_input, .output = mech_login_output }; dovecot-2.2.33.2/src/lib-sasl/dsasl-client-private.h0000644000175000017500000000241113165463624017035 00000000000000#ifndef DSASL_CLIENT_PRIVATE_H #define DSASL_CLIENT_PRIVATE_H #include "dsasl-client.h" struct dsasl_client { pool_t pool; struct dsasl_client_settings set; char *password; const struct dsasl_client_mech *mech; }; struct dsasl_client_mech { const char *name; size_t struct_size; int (*input)(struct dsasl_client *client, const unsigned char *input, unsigned int input_len, const char **error_r); int (*output)(struct dsasl_client *client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r); int (*set_parameter)(struct dsasl_client *client, const char *key, const char *value, const char **error_r); int (*get_result)(struct dsasl_client *client, const char *key, const char **value_r, const char **error_r); void (*free)(struct dsasl_client *client); }; extern const struct dsasl_client_mech dsasl_client_mech_external; extern const struct dsasl_client_mech dsasl_client_mech_login; extern const struct dsasl_client_mech dsasl_client_mech_oauthbearer; extern const struct dsasl_client_mech dsasl_client_mech_xoauth2; void dsasl_client_mech_register(const struct dsasl_client_mech *mech); void dsasl_client_mech_unregister(const struct dsasl_client_mech *mech); #endif dovecot-2.2.33.2/src/lib-sasl/mech-plain.c0000644000175000017500000000312213165463624015013 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "dsasl-client-private.h" struct plain_dsasl_client { struct dsasl_client client; bool output_sent; }; static int mech_plain_input(struct dsasl_client *_client, const unsigned char *input ATTR_UNUSED, unsigned int input_len, const char **error_r) { struct plain_dsasl_client *client = (struct plain_dsasl_client *)_client; if (!client->output_sent) { if (input_len > 0) { *error_r = "Server sent non-empty initial response"; return -1; } } else { *error_r = "Server didn't finish authentication"; return -1; } return 0; } static int mech_plain_output(struct dsasl_client *_client, const unsigned char **output_r, unsigned int *output_len_r, const char **error_r) { struct plain_dsasl_client *client = (struct plain_dsasl_client *)_client; string_t *str; if (_client->set.authid == NULL) { *error_r = "authid not set"; return -1; } if (_client->password == NULL) { *error_r = "password not set"; return -1; } str = str_new(_client->pool, 64); if (_client->set.authzid != NULL) str_append(str, _client->set.authzid); str_append_c(str, '\0'); str_append(str, _client->set.authid); str_append_c(str, '\0'); str_append(str, _client->password); *output_r = str_data(str); *output_len_r = str_len(str); client->output_sent = TRUE; return 0; } const struct dsasl_client_mech dsasl_client_mech_plain = { .name = "PLAIN", .struct_size = sizeof(struct plain_dsasl_client), .input = mech_plain_input, .output = mech_plain_output }; dovecot-2.2.33.2/src/lib-sasl/Makefile.am0000644000175000017500000000046313123174404014660 00000000000000noinst_LTLIBRARIES = libsasl.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libsasl_la_SOURCES = \ mech-external.c \ mech-login.c \ mech-plain.c \ mech-oauthbearer.c \ dsasl-client.c headers = \ dsasl-client.h \ dsasl-client-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/pop3-login/0002755000175000017500000000000013172375612013174 500000000000000dovecot-2.2.33.2/src/pop3-login/Makefile.in0000644000175000017500000005441713172375575015202 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = pop3-login$(EXEEXT) subdir = src/pop3-login ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_pop3_login_OBJECTS = client.$(OBJEXT) client-authenticate.$(OBJEXT) \ pop3-login-settings.$(OBJEXT) pop3-proxy.$(OBJEXT) pop3_login_OBJECTS = $(am_pop3_login_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(pop3_login_SOURCES) DIST_SOURCES = $(pop3_login_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common pop3_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) pop3_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT_DEPS) pop3_login_SOURCES = \ client.c \ client-authenticate.c \ pop3-login-settings.c \ pop3-proxy.c noinst_HEADERS = \ client.h \ client-authenticate.h \ pop3-login-settings.h \ pop3-proxy.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/pop3-login/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/pop3-login/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list pop3-login$(EXEEXT): $(pop3_login_OBJECTS) $(pop3_login_DEPENDENCIES) $(EXTRA_pop3_login_DEPENDENCIES) @rm -f pop3-login$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pop3_login_OBJECTS) $(pop3_login_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-authenticate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-login-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop3-proxy.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/pop3-login/client-authenticate.h0000644000175000017500000000103513123174404017204 00000000000000#ifndef CLIENT_AUTHENTICATE_H #define CLIENT_AUTHENTICATE_H void pop3_client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text); bool cmd_capa(struct pop3_client *client, const char *args); bool cmd_user(struct pop3_client *client, const char *args); bool cmd_pass(struct pop3_client *client, const char *args); bool cmd_auth(struct pop3_client *client, const char *args); bool cmd_apop(struct pop3_client *client, const char *args); #endif dovecot-2.2.33.2/src/pop3-login/pop3-proxy.c0000644000175000017500000002073013165463624015323 00000000000000/* Copyright (c) 2004-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "base64.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" #include "strescape.h" #include "dsasl-client.h" #include "client.h" #include "pop3-proxy.h" static const char *pop3_proxy_state_names[POP3_PROXY_STATE_COUNT] = { "banner", "starttls", "xclient", "login1", "login2" }; static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } static int proxy_send_login(struct pop3_client *client, struct ostream *output) { struct dsasl_client_settings sasl_set; const unsigned char *sasl_output; unsigned int len; const char *mech_name, *error; string_t *str = t_str_new(128); i_assert(client->common.proxy_ttl > 1); if (client->proxy_xclient && !client->common.proxy_not_trusted) { string_t *fwd = t_str_new(128); for(const char *const *ptr = client->common.auth_passdb_args;*ptr != NULL; ptr++) { if (strncasecmp(*ptr, "forward_", 8) == 0) { if (str_len(fwd) > 0) str_append_c(fwd, '\t'); str_append_tabescaped(fwd, (*ptr)+8); } } str_printfa(str, "XCLIENT ADDR=%s PORT=%u SESSION=%s TTL=%u", net_ip2addr(&client->common.ip), client->common.remote_port, client_get_session_id(&client->common), client->common.proxy_ttl - 1); if (str_len(fwd) > 0) { str_append(str, " FORWARD="); base64_encode(str_data(fwd), str_len(fwd), str); } str_append(str, "\r\n"); /* remote supports XCLIENT, send it */ o_stream_nsend(output, str_data(str), str_len(str)); client->proxy_state = POP3_PROXY_XCLIENT; } else { client->proxy_state = POP3_PROXY_LOGIN1; } str_truncate(str, 0); if (client->common.proxy_mech == NULL) { /* send USER command */ str_append(str, "USER "); str_append(str, client->common.proxy_user); str_append(str, "\r\n"); o_stream_nsend(output, str_data(str), str_len(str)); return 0; } i_assert(client->common.proxy_sasl_client == NULL); i_zero(&sasl_set); sasl_set.authid = client->common.proxy_master_user != NULL ? client->common.proxy_master_user : client->common.proxy_user; sasl_set.authzid = client->common.proxy_user; sasl_set.password = client->common.proxy_password; client->common.proxy_sasl_client = dsasl_client_new(client->common.proxy_mech, &sasl_set); mech_name = dsasl_client_mech_get_name(client->common.proxy_mech); str_printfa(str, "AUTH %s ", mech_name); if (dsasl_client_output(client->common.proxy_sasl_client, &sasl_output, &len, &error) < 0) { client_log_err(&client->common, t_strdup_printf( "proxy: SASL mechanism %s init failed: %s", mech_name, error)); return -1; } if (len == 0) str_append_c(str, '='); else base64_encode(sasl_output, len, str); str_append(str, "\r\n"); o_stream_nsend(output, str_data(str), str_len(str)); proxy_free_password(&client->common); if (client->proxy_state != POP3_PROXY_XCLIENT) client->proxy_state = POP3_PROXY_LOGIN2; return 0; } static int pop3_proxy_continue_sasl_auth(struct client *client, struct ostream *output, const char *line) { string_t *str; const unsigned char *data; unsigned int data_len; const char *error; int ret; str = t_str_new(128); if (base64_decode(line, strlen(line), NULL, str) < 0) { client_log_err(client, "proxy: Server sent invalid base64 data in AUTH response"); return -1; } ret = dsasl_client_input(client->proxy_sasl_client, str_data(str), str_len(str), &error); if (ret == 0) { ret = dsasl_client_output(client->proxy_sasl_client, &data, &data_len, &error); } if (ret < 0) { client_log_err(client, t_strdup_printf( "proxy: Server sent invalid authentication data: %s", error)); return -1; } i_assert(ret == 0); str_truncate(str, 0); base64_encode(data, data_len, str); str_append(str, "\r\n"); o_stream_nsend(output, str_data(str), str_len(str)); return 0; } int pop3_proxy_parse_line(struct client *client, const char *line) { struct pop3_client *pop3_client = (struct pop3_client *)client; struct ostream *output; enum login_proxy_ssl_flags ssl_flags; i_assert(!client->destroyed); output = login_proxy_get_ostream(client->login_proxy); switch (pop3_client->proxy_state) { case POP3_PROXY_BANNER: /* this is a banner */ if (strncmp(line, "+OK", 3) != 0) { client_log_err(client, t_strdup_printf( "proxy: Remote returned invalid banner: %s", str_sanitize(line, 160))); client_proxy_failed(client, TRUE); return -1; } pop3_client->proxy_xclient = strncmp(line+3, " [XCLIENT]", 10) == 0; ssl_flags = login_proxy_get_ssl_flags(client->login_proxy); if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) { if (proxy_send_login(pop3_client, output) < 0) { client_proxy_failed(client, TRUE); return -1; } } else { o_stream_nsend_str(output, "STLS\r\n"); pop3_client->proxy_state = POP3_PROXY_STARTTLS; } return 0; case POP3_PROXY_STARTTLS: if (strncmp(line, "+OK", 3) != 0) { client_log_err(client, t_strdup_printf( "proxy: Remote STLS failed: %s", str_sanitize(line, 160))); client_proxy_failed(client, TRUE); return -1; } if (login_proxy_starttls(client->login_proxy) < 0) { client_proxy_failed(client, TRUE); return -1; } /* i/ostreams changed. */ output = login_proxy_get_ostream(client->login_proxy); if (proxy_send_login(pop3_client, output) < 0) { client_proxy_failed(client, TRUE); return -1; } return 1; case POP3_PROXY_XCLIENT: if (strncmp(line, "+OK", 3) != 0) { client_log_err(client, t_strdup_printf( "proxy: Remote XCLIENT failed: %s", str_sanitize(line, 160))); client_proxy_failed(client, TRUE); return -1; } pop3_client->proxy_state = client->proxy_sasl_client == NULL ? POP3_PROXY_LOGIN1 : POP3_PROXY_LOGIN2; return 0; case POP3_PROXY_LOGIN1: i_assert(client->proxy_sasl_client == NULL); if (strncmp(line, "+OK", 3) != 0) break; /* USER successful, send PASS */ o_stream_nsend_str(output, t_strdup_printf( "PASS %s\r\n", client->proxy_password)); proxy_free_password(client); pop3_client->proxy_state = POP3_PROXY_LOGIN2; return 0; case POP3_PROXY_LOGIN2: if (strncmp(line, "+ ", 2) == 0 && client->proxy_sasl_client != NULL) { /* continue SASL authentication */ if (pop3_proxy_continue_sasl_auth(client, output, line+2) < 0) { client_proxy_failed(client, TRUE); return -1; } return 0; } if (strncmp(line, "+OK", 3) != 0) break; /* Login successful. Send this line to client. */ line = t_strconcat(line, "\r\n", NULL); o_stream_nsend_str(client->output, line); client_proxy_finish_destroy_client(client); return 1; case POP3_PROXY_STATE_COUNT: i_unreached(); } /* Login failed. Pass through the error message to client. If the backend server isn't Dovecot, the error message may be different from Dovecot's "user doesn't exist" error. This would allow an attacker to find out what users exist in the system. The optimal way to handle this would be to replace the backend's "password failed" error message with Dovecot's AUTH_FAILED_MSG, but this would require a new setting and the sysadmin to actually bother setting it properly. So for now we'll just forward the error message. This shouldn't be a real problem since of course everyone will be using only Dovecot as their backend :) */ if (strncmp(line, "-ERR ", 5) != 0) { client_send_reply(client, POP3_CMD_REPLY_ERROR, AUTH_FAILED_MSG); } else { client_send_raw(client, t_strconcat(line, "\r\n", NULL)); } if (client->set->auth_verbose) { if (strncmp(line, "-ERR ", 5) == 0) line += 5; client_proxy_log_failure(client, line); } client->proxy_auth_failed = TRUE; client_proxy_failed(client, FALSE); return -1; } void pop3_proxy_reset(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; pop3_client->proxy_state = POP3_PROXY_BANNER; } void pop3_proxy_error(struct client *client, const char *text) { client_send_reply(client, POP3_CMD_REPLY_ERROR, text); } const char *pop3_proxy_get_state(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; return pop3_proxy_state_names[pop3_client->proxy_state]; } dovecot-2.2.33.2/src/pop3-login/pop3-proxy.h0000644000175000017500000000043713123174404015317 00000000000000#ifndef POP3_PROXY_H #define POP3_PROXY_H void pop3_proxy_reset(struct client *client); int pop3_proxy_parse_line(struct client *client, const char *line); void pop3_proxy_error(struct client *client, const char *text); const char *pop3_proxy_get_state(struct client *client); #endif dovecot-2.2.33.2/src/pop3-login/client-authenticate.c0000644000175000017500000001373013165463624017217 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "base64.h" #include "buffer.h" #include "hex-binary.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" #include "auth-client.h" #include "../pop3/pop3-capability.h" #include "ssl-proxy.h" #include "client.h" #include "client-authenticate.h" #include "pop3-proxy.h" static const char *capability_string = POP3_CAPABILITY_REPLY; bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED) { const struct auth_mech_desc *mech; unsigned int i, count; string_t *str; str = t_str_new(128); str_append(str, "+OK\r\n"); str_append(str, capability_string); if (client_is_tls_enabled(&client->common) && !client->common.tls) str_append(str, "STLS\r\n"); if (!client->common.set->disable_plaintext_auth || client->common.secured) str_append(str, "USER\r\n"); str_append(str, "SASL"); mech = sasl_server_get_advertised_mechs(&client->common, &count); for (i = 0; i < count; i++) { str_append_c(str, ' '); str_append(str, mech[i].name); } str_append(str, "\r\n.\r\n"); client_send_raw(&client->common, str_c(str)); return TRUE; } void pop3_client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply ATTR_UNUSED, const char *text) { switch (result) { case CLIENT_AUTH_RESULT_SUCCESS: /* nothing to be done for POP3 */ break; case CLIENT_AUTH_RESULT_TEMPFAIL: client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text); break; case CLIENT_AUTH_RESULT_AUTHFAILED: case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: case CLIENT_AUTH_RESULT_AUTHZFAILED: case CLIENT_AUTH_RESULT_SSL_REQUIRED: client_send_reply(client, POP3_CMD_REPLY_AUTH_ERROR, text); break; default: client_send_reply(client, POP3_CMD_REPLY_ERROR, text); break; } } bool cmd_auth(struct pop3_client *pop3_client, const char *args) { struct client *client = &pop3_client->common; const struct auth_mech_desc *mech; const char *mech_name, *p; if (*args == '\0') { /* Old-style SASL discovery, used by MS Outlook */ unsigned int i, count; client_send_raw(client, "+OK\r\n"); mech = sasl_server_get_advertised_mechs(client, &count); for (i = 0; i < count; i++) { client_send_raw(client, mech[i].name); client_send_raw(client, "\r\n"); } client_send_raw(client, ".\r\n"); return TRUE; } /* */ p = strchr(args, ' '); if (p == NULL) { mech_name = args; args = NULL; } else { mech_name = t_strdup_until(args, p); args = p+1; } (void)client_auth_begin(client, mech_name, args); return TRUE; } bool cmd_user(struct pop3_client *pop3_client, const char *args) { if (!client_check_plaintext_auth(&pop3_client->common, FALSE)) { if (pop3_client->common.virtual_user == NULL) pop3_client->common.virtual_user = i_strdup(args); return TRUE; } i_free(pop3_client->last_user); pop3_client->last_user = i_strdup(args); client_send_raw(&pop3_client->common, "+OK\r\n"); return TRUE; } bool cmd_pass(struct pop3_client *pop3_client, const char *args) { struct client *client = &pop3_client->common; string_t *plain_login, *base64; if (pop3_client->last_user == NULL) { /* client may ignore the USER reply and only display the error message from PASS */ if (!client_check_plaintext_auth(client, TRUE)) return TRUE; client_send_reply(client, POP3_CMD_REPLY_ERROR, "No username given."); return TRUE; } /* authorization ID \0 authentication ID \0 pass */ plain_login = t_str_new(128); str_append_c(plain_login, '\0'); str_append(plain_login, pop3_client->last_user); str_append_c(plain_login, '\0'); str_append(plain_login, args); i_free_and_null(pop3_client->last_user); base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(plain_login->used)); base64_encode(plain_login->data, plain_login->used, base64); (void)client_auth_begin(client, "PLAIN", str_c(base64)); return TRUE; } bool cmd_apop(struct pop3_client *pop3_client, const char *args) { struct client *client = &pop3_client->common; buffer_t *apop_data, *base64; const char *p; unsigned int server_pid, connect_uid; if (pop3_client->apop_challenge == NULL) { if (client->set->auth_verbose) client_log(client, "APOP failed: APOP not enabled"); client_send_reply(client, POP3_CMD_REPLY_ERROR, "APOP not enabled."); return TRUE; } /* */ p = strchr(args, ' '); if (p == NULL || strlen(p+1) != 32) { if (client->set->auth_verbose) client_log(client, "APOP failed: Invalid parameters"); client_send_reply(client, POP3_CMD_REPLY_ERROR, "Invalid parameters."); return TRUE; } /* APOP challenge \0 username \0 APOP response */ apop_data = buffer_create_dynamic(pool_datastack_create(), 128); buffer_append(apop_data, pop3_client->apop_challenge, strlen(pop3_client->apop_challenge)+1); buffer_append(apop_data, args, (size_t)(p-args)); buffer_append_c(apop_data, '\0'); if (hex_to_binary(p+1, apop_data) < 0) { if (client->set->auth_verbose) { client_log(client, "APOP failed: " "Invalid characters in MD5 response"); } client_send_reply(client, POP3_CMD_REPLY_ERROR, "Invalid characters in MD5 response."); return TRUE; } base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(apop_data->used)); base64_encode(apop_data->data, apop_data->used, base64); auth_client_get_connect_id(auth_client, &server_pid, &connect_uid); if (pop3_client->apop_server_pid != server_pid || pop3_client->apop_connect_uid != connect_uid) { /* we reconnected to auth server and can't authenticate with APOP in this session anymore. disconnecting the user is probably the best solution now. */ client_destroy(client, "Reconnected to auth server, can't do APOP"); return TRUE; } (void)client_auth_begin(client, "APOP", str_c(base64)); return TRUE; } dovecot-2.2.33.2/src/pop3-login/client.c0000644000175000017500000002201213123174404014521 00000000000000/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ #include "login-common.h" #include "base64.h" #include "buffer.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "randgen.h" #include "hostpid.h" #include "safe-memset.h" #include "str.h" #include "strescape.h" #include "master-service.h" #include "client.h" #include "client-authenticate.h" #include "auth-client.h" #include "ssl-proxy.h" #include "pop3-proxy.h" #include "pop3-login-settings.h" /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 3 static bool cmd_stls(struct pop3_client *client) { client_cmd_starttls(&client->common); return TRUE; } static bool cmd_quit(struct pop3_client *client) { client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Logging out"); client_destroy(&client->common, "Aborted login"); return TRUE; } static bool cmd_xclient(struct pop3_client *client, const char *args) { const char *const *tmp; in_port_t remote_port; bool args_ok = TRUE; if (!client->common.trusted) { client_send_reply(&client->common, POP3_CMD_REPLY_OK, "You are not from trusted IP - ignoring"); return TRUE; } for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { if (strncasecmp(*tmp, "ADDR=", 5) == 0) { if (net_addr2ip(*tmp + 5, &client->common.ip) < 0) args_ok = FALSE; } else if (strncasecmp(*tmp, "PORT=", 5) == 0) { if (net_str2port(*tmp + 5, &remote_port) < 0) args_ok = FALSE; else client->common.remote_port = remote_port; } else if (strncasecmp(*tmp, "SESSION=", 8) == 0) { const char *value = *tmp + 8; if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { client->common.session_id = p_strdup(client->common.pool, value); } } else if (strncasecmp(*tmp, "TTL=", 4) == 0) { if (str_to_uint(*tmp + 4, &client->common.proxy_ttl) < 0) args_ok = FALSE; } else if (strncasecmp(*tmp, "FORWARD=", 8) == 0) { size_t value_len = strlen((*tmp)+8); client->common.forward_fields = str_new(client->common.preproxy_pool, MAX_BASE64_DECODED_SIZE(value_len)); if (base64_decode((*tmp)+8, value_len, NULL, client->common.forward_fields) < 0) args_ok = FALSE; } } if (!args_ok) { client_send_reply(&client->common, POP3_CMD_REPLY_ERROR, "Invalid parameters"); return TRUE; } /* args ok, set them and reset the state */ client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Updated"); return TRUE; } static bool client_command_execute(struct pop3_client *client, const char *cmd, const char *args) { cmd = t_str_ucase(cmd); if (strcmp(cmd, "CAPA") == 0) return cmd_capa(client, args); if (strcmp(cmd, "USER") == 0) return cmd_user(client, args); if (strcmp(cmd, "PASS") == 0) return cmd_pass(client, args); if (strcmp(cmd, "AUTH") == 0) return cmd_auth(client, args); if (strcmp(cmd, "APOP") == 0) return cmd_apop(client, args); if (strcmp(cmd, "STLS") == 0) return cmd_stls(client); if (strcmp(cmd, "QUIT") == 0) return cmd_quit(client); if (strcmp(cmd, "XCLIENT") == 0) return cmd_xclient(client, args); if (strcmp(cmd, "XOIP") == 0) { /* Compatibility with Zimbra's patched nginx */ return cmd_xclient(client, t_strconcat("ADDR=", args, NULL)); } client_send_reply(&client->common, POP3_CMD_REPLY_ERROR, "Unknown command."); return FALSE; } static void pop3_client_input(struct client *client) { i_assert(!client->authenticating); if (!client_read(client)) return; client_ref(client); o_stream_cork(client->output); /* if a command starts an authentication, stop processing further commands until the authentication is finished. */ while (!client->output->closed && !client->authenticating && auth_client_is_connected(auth_client)) { if (!client->v.input_next_cmd(client)) break; } if (auth_client != NULL && !auth_client_is_connected(auth_client)) client->input_blocked = TRUE; if (client_unref(&client)) o_stream_uncork(client->output); } static bool pop3_client_input_next_cmd(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; char *line, *args; if ((line = i_stream_next_line(client->input)) == NULL) return FALSE; args = strchr(line, ' '); if (args != NULL) *args++ = '\0'; if (client_command_execute(pop3_client, line, args != NULL ? args : "")) client->bad_counter = 0; else if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_send_reply(client, POP3_CMD_REPLY_ERROR, "Too many invalid bad commands."); client_destroy(client, "Disconnected: Too many bad commands"); return FALSE; } return TRUE; } static struct client *pop3_client_alloc(pool_t pool) { struct pop3_client *pop3_client; pop3_client = p_new(pool, struct pop3_client, 1); return &pop3_client->common; } static void pop3_client_create(struct client *client ATTR_UNUSED, void **other_sets ATTR_UNUSED) { } static void pop3_client_destroy(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; i_free_and_null(pop3_client->last_user); i_free_and_null(pop3_client->apop_challenge); } static char *get_apop_challenge(struct pop3_client *client) { unsigned char buffer[16]; unsigned char buffer_base64[MAX_BASE64_ENCODED_SIZE(sizeof(buffer)) + 1]; buffer_t buf; if (auth_client_find_mech(auth_client, "APOP") == NULL) { /* disabled, no need to present the challenge */ return NULL; } auth_client_get_connect_id(auth_client, &client->apop_server_pid, &client->apop_connect_uid); random_fill(buffer, sizeof(buffer)); buffer_create_from_data(&buf, buffer_base64, sizeof(buffer_base64)); base64_encode(buffer, sizeof(buffer), &buf); buffer_append_c(&buf, '\0'); return i_strdup_printf("<%x.%x.%lx.%s@%s>", client->apop_server_pid, client->apop_connect_uid, (unsigned long)ioloop_time, (const char *)buf.data, my_hostname); } static void pop3_client_notify_auth_ready(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; string_t *str; client->io = io_add(client->fd, IO_READ, client_input, client); str = t_str_new(128); if (client->trusted) { /* Dovecot extension to avoid extra roundtrip for CAPA */ str_append(str, "[XCLIENT] "); } str_append(str, client->set->login_greeting); pop3_client->apop_challenge = get_apop_challenge(pop3_client); if (pop3_client->apop_challenge != NULL) str_printfa(str, " %s", pop3_client->apop_challenge); client_send_reply(client, POP3_CMD_REPLY_OK, str_c(str)); client->banner_sent = TRUE; } static void pop3_client_notify_starttls(struct client *client, bool success, const char *text) { if (success) client_send_reply(client, POP3_CMD_REPLY_OK, text); else client_send_reply(client, POP3_CMD_REPLY_ERROR, text); } static void pop3_client_starttls(struct client *client ATTR_UNUSED) { } void client_send_reply(struct client *client, enum pop3_cmd_reply reply, const char *text) { const char *prefix = "-ERR"; switch (reply) { case POP3_CMD_REPLY_OK: prefix = "+OK"; break; case POP3_CMD_REPLY_TEMPFAIL: prefix = "-ERR [SYS/TEMP]"; break; case POP3_CMD_REPLY_AUTH_ERROR: if (text[0] == '[') prefix = "-ERR"; else prefix = "-ERR [AUTH]"; break; case POP3_CMD_REPLY_ERROR: break; } T_BEGIN { string_t *line = t_str_new(256); str_append(line, prefix); str_append_c(line, ' '); str_append(line, text); str_append(line, "\r\n"); client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } static void pop3_client_notify_disconnect(struct client *client, enum client_disconnect_reason reason, const char *text) { if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text); else client_send_reply(client, POP3_CMD_REPLY_ERROR, text); } static void pop3_login_die(void) { /* do nothing. pop3 connections typically die pretty quick anyway. */ } static void pop3_login_preinit(void) { login_set_roots = pop3_login_setting_roots; } static void pop3_login_init(void) { /* override the default login_die() */ master_service_set_die_callback(master_service, pop3_login_die); } static void pop3_login_deinit(void) { clients_destroy_all(); } static struct client_vfuncs pop3_client_vfuncs = { pop3_client_alloc, pop3_client_create, pop3_client_destroy, pop3_client_notify_auth_ready, pop3_client_notify_disconnect, NULL, pop3_client_notify_starttls, pop3_client_starttls, pop3_client_input, NULL, NULL, pop3_client_auth_result, pop3_proxy_reset, pop3_proxy_parse_line, pop3_proxy_error, pop3_proxy_get_state, client_common_send_raw_data, pop3_client_input_next_cmd, client_common_default_free, }; static const struct login_binary pop3_login_binary = { .protocol = "pop3", .process_name = "pop3-login", .default_port = 110, .default_ssl_port = 995, .client_vfuncs = &pop3_client_vfuncs, .preinit = pop3_login_preinit, .init = pop3_login_init, .deinit = pop3_login_deinit, .sasl_support_final_reply = FALSE }; int main(int argc, char *argv[]) { return login_binary_run(&pop3_login_binary, argc, argv); } dovecot-2.2.33.2/src/pop3-login/client.h0000644000175000017500000000126413123174404014534 00000000000000#ifndef CLIENT_H #define CLIENT_H #include "net.h" #include "client-common.h" #include "auth-client.h" enum pop3_proxy_state { POP3_PROXY_BANNER = 0, POP3_PROXY_STARTTLS, POP3_PROXY_XCLIENT, POP3_PROXY_LOGIN1, POP3_PROXY_LOGIN2, POP3_PROXY_STATE_COUNT }; struct pop3_client { struct client common; char *last_user; char *apop_challenge; unsigned int apop_server_pid, apop_connect_uid; enum pop3_proxy_state proxy_state; bool proxy_xclient; }; enum pop3_cmd_reply { POP3_CMD_REPLY_OK, POP3_CMD_REPLY_ERROR, POP3_CMD_REPLY_AUTH_ERROR, POP3_CMD_REPLY_TEMPFAIL }; void client_send_reply(struct client *client, enum pop3_cmd_reply reply, const char *text); #endif dovecot-2.2.33.2/src/pop3-login/pop3-login-settings.h0000644000175000017500000000021213123174404017073 00000000000000#ifndef POP3_LOGIN_SETTINGS_H #define POP3_LOGIN_SETTINGS_H extern const struct setting_parser_info *pop3_login_setting_roots[]; #endif dovecot-2.2.33.2/src/pop3-login/pop3-login-settings.c0000644000175000017500000000366113123174404017101 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "login-settings.h" #include "pop3-login-settings.h" #include /* */ static struct inet_listener_settings pop3_login_inet_listeners_array[] = { { .name = "pop3", .address = "", .port = 110 }, { .name = "pop3s", .address = "", .port = 995, .ssl = TRUE } }; static struct inet_listener_settings *pop3_login_inet_listeners[] = { &pop3_login_inet_listeners_array[0], &pop3_login_inet_listeners_array[1] }; static buffer_t pop3_login_inet_listeners_buf = { pop3_login_inet_listeners, sizeof(pop3_login_inet_listeners), { NULL, } }; /* */ struct service_settings pop3_login_service_settings = { .name = "pop3-login", .protocol = "pop3", .type = "login", .executable = "pop3-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = { { &pop3_login_inet_listeners_buf, sizeof(pop3_login_inet_listeners[0]) } } }; static const struct setting_define pop3_login_setting_defines[] = { SETTING_DEFINE_LIST_END }; static const struct setting_parser_info *pop3_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; const struct setting_parser_info pop3_login_setting_parser_info = { .module_name = "pop3-login", .defines = pop3_login_setting_defines, .type_offset = (size_t)-1, .parent_offset = (size_t)-1, .dependencies = pop3_login_setting_dependencies }; const struct setting_parser_info *pop3_login_setting_roots[] = { &login_setting_parser_info, &pop3_login_setting_parser_info, NULL }; dovecot-2.2.33.2/src/pop3-login/Makefile.am0000644000175000017500000000116213165463624015151 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = pop3-login AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-sasl \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/login-common pop3_login_LDADD = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT) \ $(SSL_LIBS) pop3_login_DEPENDENCIES = \ $(LIBDOVECOT_LOGIN) \ $(LIBDOVECOT_DEPS) pop3_login_SOURCES = \ client.c \ client-authenticate.c \ pop3-login-settings.c \ pop3-proxy.c noinst_HEADERS = \ client.h \ client-authenticate.h \ pop3-login-settings.h \ pop3-proxy.h dovecot-2.2.33.2/src/anvil/0002755000175000017500000000000013172375612012316 500000000000000dovecot-2.2.33.2/src/anvil/Makefile.in0000644000175000017500000005651713172375572014324 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = anvil$(EXEEXT) noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/anvil ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = test-penalty$(EXEEXT) am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) am_anvil_OBJECTS = main.$(OBJEXT) anvil-connection.$(OBJEXT) \ anvil-settings.$(OBJEXT) connect-limit.$(OBJEXT) \ penalty.$(OBJEXT) anvil_OBJECTS = $(am_anvil_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_test_penalty_OBJECTS = test-penalty.$(OBJEXT) test_penalty_OBJECTS = $(am_test_penalty_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(anvil_SOURCES) $(test_penalty_SOURCES) DIST_SOURCES = $(anvil_SOURCES) $(test_penalty_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master anvil_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) anvil_DEPENDENCIES = $(LIBDOVECOT_DEPS) anvil_SOURCES = \ main.c \ anvil-connection.c \ anvil-settings.c \ connect-limit.c \ penalty.c noinst_HEADERS = \ anvil-connection.h \ common.h \ connect-limit.h \ penalty.h test_programs = \ test-penalty test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_penalty_SOURCES = test-penalty.c test_penalty_LDADD = penalty.o $(test_libs) test_penalty_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/anvil/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/anvil/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list anvil$(EXEEXT): $(anvil_OBJECTS) $(anvil_DEPENDENCIES) $(EXTRA_anvil_DEPENDENCIES) @rm -f anvil$(EXEEXT) $(AM_V_CCLD)$(LINK) $(anvil_OBJECTS) $(anvil_LDADD) $(LIBS) test-penalty$(EXEEXT): $(test_penalty_OBJECTS) $(test_penalty_DEPENDENCIES) $(EXTRA_test_penalty_DEPENDENCIES) @rm -f test-penalty$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_penalty_OBJECTS) $(test_penalty_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/anvil-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/anvil-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connect-limit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/penalty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-penalty.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ clean-pkglibexecPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstPROGRAMS clean-pkglibexecPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkglibexecPROGRAMS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/anvil/penalty.c0000644000175000017500000001502513165463624014062 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ /* The idea behind checksums is that the same username+password doesn't increase the penalty, because it's most likely a user with a misconfigured account. */ #include "lib.h" #include "ioloop.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "llist.h" #include "ostream.h" #include "penalty.h" #include #define PENALTY_DEFAULT_EXPIRE_SECS (60*60) #define PENALTY_CHECKSUM_SAVE_COUNT #define CHECKSUM_VALUE_COUNT 2 #define CHECKSUM_VALUE_PTR_COUNT 10 #define LAST_UPDATE_BITS 15 struct penalty_rec { /* ordered by last_update */ struct penalty_rec *prev, *next; char *ident; unsigned int last_penalty; unsigned int penalty:16; unsigned int last_update:LAST_UPDATE_BITS; /* last_penalty + n */ unsigned int checksum_is_pointer:1; /* we use value up to two different checksums. after that switch to pointer. */ union { unsigned int value[CHECKSUM_VALUE_COUNT]; unsigned int *value_ptr; } checksum; }; struct penalty { /* ident => penalty_rec */ HASH_TABLE(char *, struct penalty_rec *) hash; struct penalty_rec *oldest, *newest; unsigned int expire_secs; struct timeout *to; }; struct penalty *penalty_init(void) { struct penalty *penalty; penalty = i_new(struct penalty, 1); hash_table_create(&penalty->hash, default_pool, 0, str_hash, strcmp); penalty->expire_secs = PENALTY_DEFAULT_EXPIRE_SECS; return penalty; } static void penalty_rec_free(struct penalty *penalty, struct penalty_rec *rec) { DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); if (rec->checksum_is_pointer) i_free(rec->checksum.value_ptr); i_free(rec->ident); i_free(rec); } void penalty_deinit(struct penalty **_penalty) { struct penalty *penalty = *_penalty; *_penalty = NULL; while (penalty->oldest != NULL) penalty_rec_free(penalty, penalty->oldest); hash_table_destroy(&penalty->hash); if (penalty->to != NULL) timeout_remove(&penalty->to); i_free(penalty); } void penalty_set_expire_secs(struct penalty *penalty, unsigned int expire_secs) { penalty->expire_secs = expire_secs; } static bool penalty_bump_checksum(struct penalty_rec *rec, unsigned int checksum) { unsigned int *checksums; unsigned int i, count; if (!rec->checksum_is_pointer) { checksums = rec->checksum.value; count = CHECKSUM_VALUE_COUNT; } else { checksums = rec->checksum.value_ptr; count = CHECKSUM_VALUE_PTR_COUNT; } for (i = 0; i < count; i++) { if (checksums[i] == checksum) { if (i > 0) { memmove(checksums + 1, checksums, sizeof(checksums[0]) * i); checksums[0] = checksum; } return TRUE; } } return FALSE; } static void penalty_add_checksum(struct penalty_rec *rec, unsigned int checksum) { unsigned int *checksums; i_assert(checksum != 0); if (!rec->checksum_is_pointer) { if (rec->checksum.value[CHECKSUM_VALUE_COUNT-1] == 0) { memcpy(rec->checksum.value + 1, rec->checksum.value, sizeof(rec->checksum.value[0]) * (CHECKSUM_VALUE_COUNT-1)); rec->checksum.value[0] = checksum; return; } /* switch to using a pointer */ checksums = i_new(unsigned int, CHECKSUM_VALUE_PTR_COUNT); memcpy(checksums, rec->checksum.value, sizeof(checksums[0]) * CHECKSUM_VALUE_COUNT); rec->checksum.value_ptr = checksums; rec->checksum_is_pointer = TRUE; } memmove(rec->checksum.value_ptr + 1, rec->checksum.value_ptr, sizeof(rec->checksum.value_ptr[0]) * (CHECKSUM_VALUE_PTR_COUNT-1)); rec->checksum.value_ptr[0] = checksum; } unsigned int penalty_get(struct penalty *penalty, const char *ident, time_t *last_penalty_r) { struct penalty_rec *rec; rec = hash_table_lookup(penalty->hash, ident); if (rec == NULL) { *last_penalty_r = 0; return 0; } *last_penalty_r = rec->last_penalty; return rec->penalty; } static void penalty_timeout(struct penalty *penalty) { struct penalty_rec *rec; time_t rec_last_update, expire_time; unsigned int diff; timeout_remove(&penalty->to); expire_time = ioloop_time - penalty->expire_secs; while (penalty->oldest != NULL) { rec = penalty->oldest; rec_last_update = rec->last_penalty + rec->last_update; if (rec_last_update > expire_time) { diff = rec_last_update - expire_time; penalty->to = timeout_add(diff * 1000, penalty_timeout, penalty); break; } hash_table_remove(penalty->hash, rec->ident); penalty_rec_free(penalty, rec); } } void penalty_inc(struct penalty *penalty, const char *ident, unsigned int checksum, unsigned int value) { struct penalty_rec *rec; time_t diff; i_assert(value > 0 || checksum == 0); i_assert(value <= INT_MAX); rec = hash_table_lookup(penalty->hash, ident); if (rec == NULL) { rec = i_new(struct penalty_rec, 1); rec->ident = i_strdup(ident); hash_table_insert(penalty->hash, rec->ident, rec); } else { DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); } if (checksum == 0) { rec->penalty = value; rec->last_penalty = ioloop_time; } else { if (penalty_bump_checksum(rec, checksum)) rec->penalty = value - 1; else { penalty_add_checksum(rec, checksum); rec->penalty = value; rec->last_penalty = ioloop_time; } } diff = ioloop_time - rec->last_penalty; if (diff >= (1 << LAST_UPDATE_BITS)) { rec->last_update = (1 << LAST_UPDATE_BITS) - 1; rec->last_penalty = ioloop_time - rec->last_update; } else { rec->last_update = diff; } DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec); if (penalty->to == NULL) { penalty->to = timeout_add(penalty->expire_secs * 1000, penalty_timeout, penalty); } } bool penalty_has_checksum(struct penalty *penalty, const char *ident, unsigned int checksum) { struct penalty_rec *rec; const unsigned int *checksums; unsigned int i, count; rec = hash_table_lookup(penalty->hash, ident); if (rec == NULL) return FALSE; if (!rec->checksum_is_pointer) { checksums = rec->checksum.value; count = CHECKSUM_VALUE_COUNT; } else { checksums = rec->checksum.value_ptr; count = CHECKSUM_VALUE_PTR_COUNT; } for (i = 0; i < count; i++) { if (checksums[i] == checksum) return TRUE; } return FALSE; } void penalty_dump(struct penalty *penalty, struct ostream *output) { const struct penalty_rec *rec; string_t *str = t_str_new(256); for (rec = penalty->oldest; rec != NULL; rec = rec->next) { str_truncate(str, 0); str_append_tabescaped(str, rec->ident); str_printfa(str, "\t%u\t%u\t%u\n", rec->penalty, rec->last_penalty, rec->last_penalty + rec->last_update); if (o_stream_send(output, str_data(str), str_len(str)) < 0) break; } o_stream_nsend(output, "\n", 1); } dovecot-2.2.33.2/src/anvil/connect-limit.c0000644000175000017500000001072413123174404015141 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "hash.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "connect-limit.h" struct ident_pid { /* ident string points to ident_hash keys */ const char *ident; pid_t pid; unsigned int refcount; }; struct connect_limit { /* ident => unsigned int refcount */ HASH_TABLE(char *, void *) ident_hash; /* struct ident_pid => struct ident_pid */ HASH_TABLE(struct ident_pid *, struct ident_pid *) ident_pid_hash; }; static unsigned int ident_pid_hash(const struct ident_pid *i) { return str_hash(i->ident) ^ i->pid; } static int ident_pid_cmp(const struct ident_pid *i1, const struct ident_pid *i2) { if (i1->pid < i2->pid) return -1; else if (i1->pid > i2->pid) return 1; else return strcmp(i1->ident, i2->ident); } struct connect_limit *connect_limit_init(void) { struct connect_limit *limit; limit = i_new(struct connect_limit, 1); hash_table_create(&limit->ident_hash, default_pool, 0, str_hash, strcmp); hash_table_create(&limit->ident_pid_hash, default_pool, 0, ident_pid_hash, ident_pid_cmp); return limit; } void connect_limit_deinit(struct connect_limit **_limit) { struct connect_limit *limit = *_limit; *_limit = NULL; hash_table_destroy(&limit->ident_hash); hash_table_destroy(&limit->ident_pid_hash); i_free(limit); } unsigned int connect_limit_lookup(struct connect_limit *limit, const char *ident) { void *value; value = hash_table_lookup(limit->ident_hash, ident); return POINTER_CAST_TO(value, unsigned int); } void connect_limit_connect(struct connect_limit *limit, pid_t pid, const char *ident) { struct ident_pid *i, lookup_i; char *key; void *value; if (!hash_table_lookup_full(limit->ident_hash, ident, &key, &value)) { key = i_strdup(ident); value = POINTER_CAST(1); hash_table_insert(limit->ident_hash, key, value); } else { value = POINTER_CAST(POINTER_CAST_TO(value, unsigned int) + 1); hash_table_update(limit->ident_hash, key, value); } lookup_i.ident = ident; lookup_i.pid = pid; i = hash_table_lookup(limit->ident_pid_hash, &lookup_i); if (i == NULL) { i = i_new(struct ident_pid, 1); i->ident = key; i->pid = pid; i->refcount = 1; hash_table_insert(limit->ident_pid_hash, i, i); } else { i->refcount++; } } static void connect_limit_ident_hash_unref(struct connect_limit *limit, const char *ident) { char *key; void *value; unsigned int new_refcount; if (!hash_table_lookup_full(limit->ident_hash, ident, &key, &value)) i_panic("connect limit hash tables are inconsistent"); new_refcount = POINTER_CAST_TO(value, unsigned int) - 1; if (new_refcount > 0) { value = POINTER_CAST(new_refcount); hash_table_update(limit->ident_hash, key, value); } else { hash_table_remove(limit->ident_hash, key); i_free(key); } } void connect_limit_disconnect(struct connect_limit *limit, pid_t pid, const char *ident) { struct ident_pid *i, lookup_i; lookup_i.ident = ident; lookup_i.pid = pid; i = hash_table_lookup(limit->ident_pid_hash, &lookup_i); if (i == NULL) { i_error("connect limit: disconnection for unknown " "pid %s + ident %s", dec2str(pid), ident); return; } if (--i->refcount == 0) { hash_table_remove(limit->ident_pid_hash, i); i_free(i); } connect_limit_ident_hash_unref(limit, ident); } void connect_limit_disconnect_pid(struct connect_limit *limit, pid_t pid) { struct hash_iterate_context *iter; struct ident_pid *i, *value; /* this should happen rarely (or never), so this slow implementation should be fine. */ iter = hash_table_iterate_init(limit->ident_pid_hash); while (hash_table_iterate(iter, limit->ident_pid_hash, &i, &value)) { if (i->pid == pid) { hash_table_remove(limit->ident_pid_hash, i); for (; i->refcount > 0; i->refcount--) connect_limit_ident_hash_unref(limit, i->ident); i_free(i); } } hash_table_iterate_deinit(&iter); } void connect_limit_dump(struct connect_limit *limit, struct ostream *output) { struct hash_iterate_context *iter; struct ident_pid *i, *value; string_t *str = t_str_new(256); iter = hash_table_iterate_init(limit->ident_pid_hash); while (hash_table_iterate(iter, limit->ident_pid_hash, &i, &value)) { str_truncate(str, 0); str_append_tabescaped(str, i->ident); str_printfa(str, "\t%ld\t%u\n", (long)i->pid, i->refcount); if (o_stream_send(output, str_data(str), str_len(str)) < 0) break; } hash_table_iterate_deinit(&iter); o_stream_nsend(output, "\n", 1); } dovecot-2.2.33.2/src/anvil/common.h0000644000175000017500000000024613123174404013667 00000000000000#ifndef COMMON_H #define COMMON_H #include "lib.h" extern struct connect_limit *connect_limit; extern struct penalty *penalty; extern bool anvil_restarted; #endif dovecot-2.2.33.2/src/anvil/main.c0000644000175000017500000000474413165463624013340 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.h" #include "env-util.h" #include "fdpass.h" #include "ioloop.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "master-interface.h" #include "connect-limit.h" #include "penalty.h" #include "anvil-connection.h" #include struct connect_limit *connect_limit; struct penalty *penalty; bool anvil_restarted; static struct io *log_fdpass_io; static void client_connected(struct master_service_connection *conn) { bool master = conn->listen_fd == MASTER_LISTEN_FD_FIRST; master_service_client_connection_accept(conn); (void)anvil_connection_create(conn->fd, master, conn->fifo); } static void ATTR_NULL(1) log_fdpass_input(void *context ATTR_UNUSED) { int fd; char c; ssize_t ret; /* master wants us to replace the log fd */ ret = fd_read(MASTER_ANVIL_LOG_FDPASS_FD, &c, 1, &fd); if (ret < 0) i_error("fd_read(log fd) failed: %m"); else if (ret == 0) { /* master died. lib-master should notice it soon. */ io_remove(&log_fdpass_io); } else { if (dup2(fd, STDERR_FILENO) < 0) i_fatal("dup2(fd_read log fd, stderr) failed: %m"); if (close(fd) < 0) i_error("close(fd_read log fd) failed: %m"); } } int main(int argc, char *argv[]) { const enum master_service_flags service_flags = MASTER_SERVICE_FLAG_UPDATE_PROCTITLE; const char *error; master_service = master_service_init("anvil", service_flags, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, NULL, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "anvil: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); anvil_restarted = getenv("ANVIL_RESTARTED") != NULL; /* delay dying until all of our clients are gone */ master_service_set_die_with_master(master_service, FALSE); connect_limit = connect_limit_init(); penalty = penalty_init(); log_fdpass_io = io_add(MASTER_ANVIL_LOG_FDPASS_FD, IO_READ, log_fdpass_input, (void *)NULL); master_service_init_finish(master_service); master_service_run(master_service, client_connected); if (log_fdpass_io != NULL) io_remove(&log_fdpass_io); penalty_deinit(&penalty); connect_limit_deinit(&connect_limit); anvil_connections_destroy_all(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/anvil/anvil-connection.h0000644000175000017500000000037313123174404015646 00000000000000#ifndef ANVIL_CONNECTION_H #define ANVIL_CONNECTION_H struct anvil_connection * anvil_connection_create(int fd, bool master, bool fifo); void anvil_connection_destroy(struct anvil_connection *conn); void anvil_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/anvil/anvil-connection.c0000644000175000017500000001362213165463624015655 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "common.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "master-service.h" #include "master-interface.h" #include "connect-limit.h" #include "penalty.h" #include "anvil-connection.h" #include #define MAX_INBUF_SIZE 1024 #define ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION 1 #define ANVIL_CLIENT_PROTOCOL_MINOR_VERSION 0 struct anvil_connection { struct anvil_connection *prev, *next; int fd; struct istream *input; struct ostream *output; struct io *io; unsigned int version_received:1; unsigned int handshaked:1; unsigned int master:1; unsigned int fifo:1; }; static struct anvil_connection *anvil_connections = NULL; static const char *const * anvil_connection_next_line(struct anvil_connection *conn) { const char *line; line = i_stream_next_line(conn->input); return line == NULL ? NULL : t_strsplit_tabescaped(line); } static int anvil_connection_request(struct anvil_connection *conn, const char *const *args, const char **error_r) { const char *cmd = args[0]; unsigned int value, checksum; time_t stamp; pid_t pid; args++; if (strcmp(cmd, "CONNECT") == 0) { if (args[0] == NULL || args[1] == NULL) { *error_r = "CONNECT: Not enough parameters"; return -1; } if (str_to_pid(args[0], &pid) < 0) { *error_r = "CONNECT: Invalid pid"; return -1; } connect_limit_connect(connect_limit, pid, args[1]); } else if (strcmp(cmd, "DISCONNECT") == 0) { if (args[0] == NULL || args[1] == NULL) { *error_r = "DISCONNECT: Not enough parameters"; return -1; } if (str_to_pid(args[0], &pid) < 0) { *error_r = "DISCONNECT: Invalid pid"; return -1; } connect_limit_disconnect(connect_limit, pid, args[1]); } else if (strcmp(cmd, "CONNECT-DUMP") == 0) { connect_limit_dump(connect_limit, conn->output); } else if (strcmp(cmd, "KILL") == 0) { if (args[0] == NULL) { *error_r = "KILL: Not enough parameters"; return -1; } if (!conn->master) { *error_r = "KILL sent by a non-master connection"; return -1; } if (str_to_pid(args[0], &pid) < 0) { *error_r = "KILL: Invalid pid"; return -1; } connect_limit_disconnect_pid(connect_limit, pid); } else if (strcmp(cmd, "LOOKUP") == 0) { if (args[0] == NULL) { *error_r = "LOOKUP: Not enough parameters"; return -1; } if (conn->output == NULL) { *error_r = "LOOKUP on a FIFO, can't send reply"; return -1; } value = connect_limit_lookup(connect_limit, args[0]); o_stream_nsend_str(conn->output, t_strdup_printf("%u\n", value)); } else if (strcmp(cmd, "PENALTY-GET") == 0) { if (args[0] == NULL) { *error_r = "PENALTY-GET: Not enough parameters"; return -1; } value = penalty_get(penalty, args[0], &stamp); o_stream_nsend_str(conn->output, t_strdup_printf("%u %s\n", value, dec2str(stamp))); } else if (strcmp(cmd, "PENALTY-INC") == 0) { if (args[0] == NULL || args[1] == NULL || args[2] == NULL) { *error_r = "PENALTY-INC: Not enough parameters"; return -1; } if (str_to_uint(args[1], &checksum) < 0 || str_to_uint(args[2], &value) < 0 || value > PENALTY_MAX_VALUE || (value == 0 && checksum != 0)) { *error_r = "PENALTY-INC: Invalid parameters"; return -1; } penalty_inc(penalty, args[0], checksum, value); } else if (strcmp(cmd, "PENALTY-SET-EXPIRE-SECS") == 0) { if (args[0] == NULL || str_to_uint(args[0], &value) < 0) { *error_r = "PENALTY-SET-EXPIRE-SECS: " "Invalid parameters"; return -1; } penalty_set_expire_secs(penalty, value); } else if (strcmp(cmd, "PENALTY-DUMP") == 0) { penalty_dump(penalty, conn->output); } else { *error_r = t_strconcat("Unknown command: ", cmd, NULL); return -1; } return 0; } static void anvil_connection_input(struct anvil_connection *conn) { const char *line, *const *args, *error; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Anvil client connection sent too much data"); anvil_connection_destroy(conn); return; case -1: anvil_connection_destroy(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, "anvil", ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION)) { if (anvil_restarted && (conn->master || conn->fifo)) { /* old pending data. ignore input until we get the handshake. */ anvil_connection_input(conn); return; } i_error("Anvil client not compatible with this server " "(mixed old and new binaries?) %s", line); anvil_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((args = anvil_connection_next_line(conn)) != NULL) { if (args[0] != NULL) { if (anvil_connection_request(conn, args, &error) < 0) { i_error("Anvil client input error: %s", error); anvil_connection_destroy(conn); break; } } } } struct anvil_connection * anvil_connection_create(int fd, bool master, bool fifo) { struct anvil_connection *conn; conn = i_new(struct anvil_connection, 1); conn->fd = fd; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); if (!fifo) { conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); } conn->io = io_add(fd, IO_READ, anvil_connection_input, conn); conn->master = master; conn->fifo = fifo; DLLIST_PREPEND(&anvil_connections, conn); return conn; } void anvil_connection_destroy(struct anvil_connection *conn) { bool fifo = conn->fifo; DLLIST_REMOVE(&anvil_connections, conn); io_remove(&conn->io); i_stream_destroy(&conn->input); if (conn->output != NULL) o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(anvil conn) failed: %m"); i_free(conn); if (!fifo) master_service_client_connection_destroyed(master_service); } void anvil_connections_destroy_all(void) { while (anvil_connections != NULL) anvil_connection_destroy(anvil_connections); } dovecot-2.2.33.2/src/anvil/test-penalty.c0000644000175000017500000000317313165463624015040 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "penalty.h" #include "test-common.h" static void test_penalty_checksum(void) { struct penalty *penalty; struct ioloop *ioloop; time_t t; unsigned int i, j; test_begin("penalty"); ioloop = io_loop_create(); penalty = penalty_init(); test_assert(penalty_get(penalty, "foo", &t) == 0); for (i = 1; i <= 10; i++) { ioloop_time = 12345678 + i; penalty_inc(penalty, "foo", i, 5+i); for (j = I_MIN(1, i-1); j <= i; j++) { test_assert(penalty_get(penalty, "foo", &t) == 5+i); test_assert(t == (time_t)(12345678 + i)); test_assert(penalty_has_checksum(penalty, "foo", i)); } test_assert(penalty_get(penalty, "foo", &t) == 5+i); test_assert(t == (time_t)(12345678 + i)); test_assert(!penalty_has_checksum(penalty, "foo", j)); } test_assert(penalty_get(penalty, "foo2", &t) == 0); /* overflows checksum array */ ioloop_time = 12345678 + i; penalty_inc(penalty, "foo", i, 5 + i); penalty_inc(penalty, "foo", i, 5 + i); penalty_inc(penalty, "foo", 0, 5 + i); test_assert(penalty_get(penalty, "foo", &t) == 5+i); test_assert(t == (time_t)(12345678 + i)); test_assert(!penalty_has_checksum(penalty, "foo", 1)); for (j = 2; j <= i; j++) { test_assert(penalty_get(penalty, "foo", &t) == 5+i); test_assert(t == (time_t)(12345678 + i)); test_assert(penalty_has_checksum(penalty, "foo", i)); } penalty_deinit(&penalty); io_loop_destroy(&ioloop); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_penalty_checksum, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/anvil/anvil-settings.c0000644000175000017500000000232213123174404015336 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings anvil_unix_listeners_array[] = { { "anvil", 0600, "", "" }, { "anvil-auth-penalty", 0600, "", "" } }; static struct file_listener_settings *anvil_unix_listeners[] = { &anvil_unix_listeners_array[0], &anvil_unix_listeners_array[1] }; static buffer_t anvil_unix_listeners_buf = { anvil_unix_listeners, sizeof(anvil_unix_listeners), { NULL, } }; /* */ struct service_settings anvil_service_settings = { .name = "anvil", .protocol = "", .type = "anvil", .executable = "anvil", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 1, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &anvil_unix_listeners_buf, sizeof(anvil_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; dovecot-2.2.33.2/src/anvil/connect-limit.h0000644000175000017500000000112513123174404015141 00000000000000#ifndef CONNECT_LIMIT_H #define CONNECT_LIMIT_H struct connect_limit *connect_limit_init(void); void connect_limit_deinit(struct connect_limit **limit); unsigned int connect_limit_lookup(struct connect_limit *limit, const char *ident); void connect_limit_connect(struct connect_limit *limit, pid_t pid, const char *ident); void connect_limit_disconnect(struct connect_limit *limit, pid_t pid, const char *ident); void connect_limit_disconnect_pid(struct connect_limit *limit, pid_t pid); void connect_limit_dump(struct connect_limit *limit, struct ostream *output); #endif dovecot-2.2.33.2/src/anvil/penalty.h0000644000175000017500000000136213123174404014053 00000000000000#ifndef PENALTY_H #define PENALTY_H #define PENALTY_MAX_VALUE ((1 << 16)-1) struct penalty *penalty_init(void); void penalty_deinit(struct penalty **penalty); void penalty_set_expire_secs(struct penalty *penalty, unsigned int expire_secs); unsigned int penalty_get(struct penalty *penalty, const char *ident, time_t *last_penalty_r); /* if checksum is non-zero and it already exists for ident, the value is set to "value-1", otherwise it's set to "value". */ void penalty_inc(struct penalty *penalty, const char *ident, unsigned int checksum, unsigned int value); bool penalty_has_checksum(struct penalty *penalty, const char *ident, unsigned int checksum); void penalty_dump(struct penalty *penalty, struct ostream *output); #endif dovecot-2.2.33.2/src/anvil/Makefile.am0000644000175000017500000000157713165463624014305 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = anvil AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master anvil_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) anvil_DEPENDENCIES = $(LIBDOVECOT_DEPS) anvil_SOURCES = \ main.c \ anvil-connection.c \ anvil-settings.c \ connect-limit.c \ penalty.c noinst_HEADERS = \ anvil-connection.h \ common.h \ connect-limit.h \ penalty.h test_programs = \ test-penalty noinst_PROGRAMS = $(test_programs) test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la test_penalty_SOURCES = test-penalty.c test_penalty_LDADD = penalty.o $(test_libs) test_penalty_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done dovecot-2.2.33.2/src/lib-stats/0002755000175000017500000000000013172375610013105 500000000000000dovecot-2.2.33.2/src/lib-stats/stats-parser.c0000644000175000017500000001005713123174404015614 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "time-util.h" #include "stats-parser.h" #define USECS_PER_SEC 1000000 static bool stats_diff_timeval(struct timeval *dest, const struct timeval *src1, const struct timeval *src2) { long long diff_usecs; diff_usecs = timeval_diff_usecs(src2, src1); if (diff_usecs < 0) return FALSE; dest->tv_sec = diff_usecs / USECS_PER_SEC; dest->tv_usec = diff_usecs % USECS_PER_SEC; return TRUE; } static bool stats_diff_uint32(uint32_t *dest, const uint32_t *src1, const uint32_t *src2) { if (*src1 > *src2) return FALSE; *dest = *src2 - *src1; return TRUE; } static bool stats_diff_uint64(uint64_t *dest, const uint64_t *src1, const uint64_t *src2) { if (*src1 > *src2) return FALSE; *dest = *src2 - *src1; return TRUE; } bool stats_parser_diff(const struct stats_parser_field *fields, unsigned int fields_count, const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r) { unsigned int i; for (i = 0; i < fields_count; i++) { unsigned int offset = fields[i].offset; void *dest = PTR_OFFSET(diff_stats_r, offset); const void *src1 = CONST_PTR_OFFSET(stats1, offset); const void *src2 = CONST_PTR_OFFSET(stats2, offset); switch (fields[i].type) { case STATS_PARSER_TYPE_UINT: switch (fields[i].size) { case sizeof(uint32_t): if (!stats_diff_uint32(dest, src1, src2)) { *error_r = t_strdup_printf("%s %u < %u", fields[i].name, *(const uint32_t *)src2, *(const uint32_t *)src1); return FALSE; } break; case sizeof(uint64_t): if (!stats_diff_uint64(dest, src1, src2)) { const uint64_t *n1 = src1, *n2 = src2; *error_r = t_strdup_printf("%s %llu < %llu", fields[i].name, (unsigned long long)*n2, (unsigned long long)*n1); return FALSE; } break; default: i_unreached(); } break; case STATS_PARSER_TYPE_TIMEVAL: if (!stats_diff_timeval(dest, src1, src2)) { const struct timeval *tv1 = src1, *tv2 = src2; *error_r = t_strdup_printf("%s %ld.%d < %ld.%d", fields[i].name, (long)tv2->tv_sec, (int)tv2->tv_usec, (long)tv1->tv_sec, (int)tv1->tv_usec); return FALSE; } break; } } return TRUE; } static void stats_timeval_add(struct timeval *dest, const struct timeval *src) { dest->tv_sec += src->tv_sec; dest->tv_usec += src->tv_usec; if (dest->tv_usec > USECS_PER_SEC) { dest->tv_usec -= USECS_PER_SEC; dest->tv_sec++; } } void stats_parser_add(const struct stats_parser_field *fields, unsigned int fields_count, struct stats *dest, const struct stats *src) { unsigned int i; for (i = 0; i < fields_count; i++) { unsigned int offset = fields[i].offset; void *f_dest = PTR_OFFSET(dest, offset); const void *f_src = CONST_PTR_OFFSET(src, offset); switch (fields[i].type) { case STATS_PARSER_TYPE_UINT: switch (fields[i].size) { case sizeof(uint32_t): { uint32_t *n_dest = f_dest; const uint32_t *n_src = f_src; *n_dest += *n_src; break; } case sizeof(uint64_t): { uint64_t *n_dest = f_dest; const uint64_t *n_src = f_src; *n_dest += *n_src; break; } default: i_unreached(); } break; case STATS_PARSER_TYPE_TIMEVAL: stats_timeval_add(f_dest, f_src); break; } } } void stats_parser_value(string_t *str, const struct stats_parser_field *field, const void *data) { const void *ptr = CONST_PTR_OFFSET(data, field->offset); switch (field->type) { case STATS_PARSER_TYPE_UINT: switch (field->size) { case sizeof(uint32_t): { const uint32_t *n = ptr; str_printfa(str, "%u", *n); break; } case sizeof(uint64_t): { const uint64_t *n = ptr; str_printfa(str, "%llu", (unsigned long long)*n); break; } default: i_unreached(); } break; case STATS_PARSER_TYPE_TIMEVAL: { const struct timeval *tv = ptr; str_printfa(str, "%lu.%u", (unsigned long)tv->tv_sec, (unsigned int)tv->tv_usec); break; } } } dovecot-2.2.33.2/src/lib-stats/Makefile.in0000644000175000017500000005421613172375573015110 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-stats ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libstats_la_LIBADD = am_libstats_la_OBJECTS = stats.lo stats-connection.lo stats-parser.lo libstats_la_OBJECTS = $(am_libstats_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstats_la_SOURCES) DIST_SOURCES = $(libstats_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libstats.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master libstats_la_SOURCES = \ stats.c \ stats-connection.c \ stats-parser.c headers = \ stats.h \ stats-connection.h \ stats-parser.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-stats/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-stats/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libstats.la: $(libstats_la_OBJECTS) $(libstats_la_DEPENDENCIES) $(EXTRA_libstats_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libstats_la_OBJECTS) $(libstats_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-stats/stats-parser.h0000644000175000017500000000136313123174404015621 00000000000000#ifndef STATS_PARSER_H #define STATS_PARSER_H struct stats; enum stats_parser_type { STATS_PARSER_TYPE_UINT, STATS_PARSER_TYPE_TIMEVAL }; struct stats_parser_field { const char *name; unsigned int offset; unsigned int size; enum stats_parser_type type; }; bool stats_parser_diff(const struct stats_parser_field *fields, unsigned int fields_count, const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r); void stats_parser_add(const struct stats_parser_field *fields, unsigned int fields_count, struct stats *dest, const struct stats *src); void stats_parser_value(string_t *str, const struct stats_parser_field *field, const void *data); #endif dovecot-2.2.33.2/src/lib-stats/stats-connection.h0000644000175000017500000000056513123174404016467 00000000000000#ifndef STATS_CONNECTION_H #define STATS_CONNECTION_H struct stats_connection *stats_connection_create(const char *path); void stats_connection_ref(struct stats_connection *conn); void stats_connection_unref(struct stats_connection **conn); /* Returns 0 on success, -1 on failure. */ int stats_connection_send(struct stats_connection *conn, const string_t *str); #endif dovecot-2.2.33.2/src/lib-stats/stats.h0000644000175000017500000000560413123174404014331 00000000000000#ifndef STATS_H #define STATS_H struct stats; struct stats_item; struct stats_vfuncs { const char *short_name; size_t (*alloc_size)(void); unsigned int (*field_count)(void); const char *(*field_name)(unsigned int n); void (*field_value)(string_t *str, const struct stats *stats, unsigned int n); bool (*diff)(const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r); void (*add)(struct stats *dest, const struct stats *src); bool (*have_changed)(const struct stats *prev, const struct stats *cur); void (*export)(buffer_t *buf, const struct stats *stats); bool (*import)(const unsigned char *data, size_t size, size_t *pos_r, struct stats *stats, const char **error_r); }; struct stats_item *stats_register(const struct stats_vfuncs *vfuncs); void stats_unregister(struct stats_item **item); /* Allocate struct stats from a given pool. */ struct stats *stats_alloc(pool_t pool); /* Returns the number of bytes allocated to stats. */ size_t stats_alloc_size(void); /* Copy all stats from src to dest. */ void stats_copy(struct stats *dest, const struct stats *src); /* Returns the number of stats fields. */ unsigned int stats_field_count(void); /* Returns the name of a stats field (exported to doveadm). */ const char *stats_field_name(unsigned int n); /* Returns the value of a stats field as a string (exported to doveadm). */ void stats_field_value(string_t *str, const struct stats *stats, unsigned int n); /* Return diff_stats_r->field = stats2->field - stats1->field. diff1 is supposed to have smaller values than diff2. Returns TRUE if this is so, FALSE if not */ bool stats_diff(const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r); /* dest->field += src->field */ void stats_add(struct stats *dest, const struct stats *src); /* Returns TRUE if any fields have changed in cur since prev in a way that a plugin should send the updated statistics to the stats process. Not all fields necessarily require sending an update. */ bool stats_have_changed(const struct stats *prev, const struct stats *cur); /* Export stats into a buffer in binary format. */ void stats_export(buffer_t *buf, const struct stats *stats); /* Import stats from a buffer. The buffer doesn't need to contain an update to all the stats items - old_stats are used for that item in such case. Currently it's not allowed to have unknown items in the buffer. */ bool stats_import(const unsigned char *data, size_t size, const struct stats *old_stats, struct stats *stats, const char **error_r); /* Return a pointer to stats where the specified item starts. The returned pointer can be used to fill up the item-specific stats (up to its alloc_size() number of bytes). */ void *stats_fill_ptr(struct stats *stats, struct stats_item *item); void stats_reset(struct stats *stats); #endif dovecot-2.2.33.2/src/lib-stats/stats-connection.c0000644000175000017500000000551213165463624016472 00000000000000/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "master-service.h" #include "stats-connection.h" #include #include #define STATS_EAGAIN_WARN_INTERVAL_SECS 30 struct stats_connection { int refcount; int fd; char *path; bool open_failed; time_t next_warning_timestamp; }; static bool stats_connection_open(struct stats_connection *conn) { if (conn->open_failed) return FALSE; conn->fd = open(conn->path, O_WRONLY | O_NONBLOCK); if (conn->fd == -1) { i_error("stats: open(%s) failed: %m", conn->path); conn->open_failed = TRUE; return FALSE; } return TRUE; } struct stats_connection * stats_connection_create(const char *path) { struct stats_connection *conn; conn = i_new(struct stats_connection, 1); conn->refcount = 1; conn->path = i_strdup(path); (void)stats_connection_open(conn); return conn; } void stats_connection_ref(struct stats_connection *conn) { conn->refcount++; } void stats_connection_unref(struct stats_connection **_conn) { struct stats_connection *conn = *_conn; i_assert(conn->refcount > 0); if (--conn->refcount > 0) return; *_conn = NULL; if (conn->fd != -1) { if (close(conn->fd) < 0) i_error("close(%s) failed: %m", conn->path); } i_free(conn->path); i_free(conn); } int stats_connection_send(struct stats_connection *conn, const string_t *str) { static bool pipe_warned = FALSE; ssize_t ret; /* if master process has been stopped (and restarted), don't even try to notify the stats process anymore. even if one exists, it doesn't know about us. */ if (master_service_is_master_stopped(master_service)) return -1; if (conn->fd == -1) { if (!stats_connection_open(conn)) return -1; i_assert(conn->fd != -1); } if (str_len(str) > PIPE_BUF && !pipe_warned) { i_warning("stats update sent more bytes that PIPE_BUF " "(%"PRIuSIZE_T" > %u), this may break statistics", str_len(str), (unsigned int)PIPE_BUF); pipe_warned = TRUE; } ret = write(conn->fd, str_data(str), str_len(str)); if (ret == (ssize_t)str_len(str)) { /* success */ return 0; } else if (ret < 0 && errno == EAGAIN) { /* stats process is busy */ if (ioloop_time > conn->next_warning_timestamp) { i_warning("write(%s) failed: %m (stats process is busy)", conn->path); conn->next_warning_timestamp = ioloop_time + STATS_EAGAIN_WARN_INTERVAL_SECS; } return -1; } else { /* error - reconnect */ if (ret < 0) { /* don't log EPIPE errors. they can happen when Dovecot is stopped. */ if (errno != EPIPE) i_error("write(%s) failed: %m", conn->path); } else if ((size_t)ret != str_len(str)) i_error("write(%s) wrote partial update", conn->path); if (close(conn->fd) < 0) i_error("close(%s) failed: %m", conn->path); conn->fd = -1; return -1; } } dovecot-2.2.33.2/src/lib-stats/stats.c0000644000175000017500000001214113123174404014316 00000000000000/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "stats.h" struct stats_item { struct stats_vfuncs v; size_t pos; }; static ARRAY(struct stats_item *) stats_items = ARRAY_INIT; static unsigned int stats_total_size = 0; static bool stats_allocated = FALSE; struct stats_item *stats_register(const struct stats_vfuncs *vfuncs) { struct stats_item *item; if (stats_allocated) i_panic("stats_register() called after stats_alloc_size() was already called - this will break existing allocations"); if (!array_is_created(&stats_items)) i_array_init(&stats_items, 8); item = i_new(struct stats_item, 1); item->v = *vfuncs; item->pos = stats_total_size; array_append(&stats_items, &item, 1); stats_total_size += vfuncs->alloc_size(); return item; } static bool stats_item_find(struct stats_item *item, unsigned int *idx_r) { struct stats_item *const *itemp; array_foreach(&stats_items, itemp) { if (*itemp == item) { *idx_r = array_foreach_idx(&stats_items, itemp); return TRUE; } } return FALSE; } static struct stats_item *stats_item_find_by_name(const char *name) { struct stats_item *const *itemp; array_foreach(&stats_items, itemp) { if (strcmp((*itemp)->v.short_name, name) == 0) return *itemp; } return NULL; } void stats_unregister(struct stats_item **_item) { struct stats_item *item = *_item; unsigned int idx; *_item = NULL; if (!stats_item_find(item, &idx)) i_unreached(); array_delete(&stats_items, idx, 1); i_free(item); if (array_count(&stats_items) == 0) { array_free(&stats_items); /* all stats should have been freed by now. allow re-registering and using stats. */ stats_allocated = FALSE; } } struct stats *stats_alloc(pool_t pool) { return p_malloc(pool, stats_alloc_size()); } size_t stats_alloc_size(void) { stats_allocated = TRUE; return stats_total_size; } void stats_copy(struct stats *dest, const struct stats *src) { memcpy(dest, src, stats_total_size); } unsigned int stats_field_count(void) { struct stats_item *const *itemp; unsigned int count = 0; array_foreach(&stats_items, itemp) count += (*itemp)->v.field_count(); return count; } const char *stats_field_name(unsigned int n) { struct stats_item *const *itemp; unsigned int i = 0, count; array_foreach(&stats_items, itemp) { count = (*itemp)->v.field_count(); if (i + count > n) return (*itemp)->v.field_name(n - i); i += count; } i_unreached(); } void stats_field_value(string_t *str, const struct stats *stats, unsigned int n) { struct stats_item *const *itemp; unsigned int i = 0, count; array_foreach(&stats_items, itemp) { count = (*itemp)->v.field_count(); if (i + count > n) { const void *item_stats = CONST_PTR_OFFSET(stats, (*itemp)->pos); (*itemp)->v.field_value(str, item_stats, n - i); return; } i += count; } i_unreached(); } bool stats_diff(const struct stats *stats1, const struct stats *stats2, struct stats *diff_stats_r, const char **error_r) { struct stats_item *const *itemp; bool ret = TRUE; array_foreach(&stats_items, itemp) { if (!(*itemp)->v.diff(CONST_PTR_OFFSET(stats1, (*itemp)->pos), CONST_PTR_OFFSET(stats2, (*itemp)->pos), PTR_OFFSET(diff_stats_r, (*itemp)->pos), error_r)) ret = FALSE; } return ret; } void stats_add(struct stats *dest, const struct stats *src) { struct stats_item *const *itemp; array_foreach(&stats_items, itemp) { (*itemp)->v.add(PTR_OFFSET(dest, (*itemp)->pos), CONST_PTR_OFFSET(src, (*itemp)->pos)); } } bool stats_have_changed(const struct stats *prev, const struct stats *cur) { struct stats_item *const *itemp; array_foreach(&stats_items, itemp) { if ((*itemp)->v.have_changed(CONST_PTR_OFFSET(prev, (*itemp)->pos), CONST_PTR_OFFSET(cur, (*itemp)->pos))) return TRUE; } return FALSE; } void stats_export(buffer_t *buf, const struct stats *stats) { struct stats_item *const *itemp; array_foreach(&stats_items, itemp) { buffer_append(buf, (*itemp)->v.short_name, strlen((*itemp)->v.short_name)+1); (*itemp)->v.export(buf, CONST_PTR_OFFSET(stats, (*itemp)->pos)); } } bool stats_import(const unsigned char *data, size_t size, const struct stats *old_stats, struct stats *stats, const char **error_r) { struct stats_item *item; const unsigned char *p; size_t pos; memcpy(stats, old_stats, stats_total_size); while (size > 0) { const char *next_name = (const void *)data; p = memchr(data, '\0', size); if (p == NULL) { *error_r = "Expected name, but NUL is missing"; return FALSE; } item = stats_item_find_by_name(next_name); if (item == NULL) { *error_r = t_strdup_printf("Unknown stats name: '%s'", next_name); return FALSE; } size -= (p+1) - data; data = p+1; if (!item->v.import(data, size, &pos, PTR_OFFSET(stats, item->pos), error_r)) return FALSE; i_assert(pos <= size); data += pos; size -= pos; } return TRUE; } void *stats_fill_ptr(struct stats *stats, struct stats_item *item) { return PTR_OFFSET(stats, item->pos); } void stats_reset(struct stats *stats) { memset(stats, 0, stats_total_size); } dovecot-2.2.33.2/src/lib-stats/Makefile.am0000644000175000017500000000046713123174404015060 00000000000000noinst_LTLIBRARIES = libstats.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master libstats_la_SOURCES = \ stats.c \ stats-connection.c \ stats-parser.c headers = \ stats.h \ stats-connection.h \ stats-parser.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-dns/0002755000175000017500000000000013172375610012533 500000000000000dovecot-2.2.33.2/src/lib-dns/dns-lookup.c0000644000175000017500000002313713165463624014723 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "net.h" #include "llist.h" #include "istream.h" #include "write-full.h" #include "time-util.h" #include "dns-lookup.h" #include #include #define MAX_INBUF_SIZE 512 struct dns_lookup { struct dns_lookup *prev, *next; struct dns_client *client; bool ptr_lookup; struct timeout *to; struct timeval start_time; unsigned int warn_msecs; struct dns_lookup_result result; struct ip_addr *ips; unsigned int ip_idx; char *name; dns_lookup_callback_t *callback; void *context; }; struct dns_client { int fd; char *path; unsigned int timeout_msecs, idle_timeout_msecs; struct istream *input; struct io *io; struct timeout *to_idle; struct dns_lookup *head, *tail; bool deinit_client_at_free; }; #undef dns_lookup #undef dns_lookup_ptr #undef dns_client_lookup #undef dns_client_lookup_ptr static void dns_lookup_free(struct dns_lookup **_lookup); static void dns_client_disconnect(struct dns_client *client, const char *error) { struct dns_lookup *lookup, *next; struct dns_lookup_result result; if (client->to_idle != NULL) timeout_remove(&client->to_idle); if (client->io != NULL) io_remove(&client->io); if (client->input != NULL) i_stream_destroy(&client->input); if (client->fd != -1) { if (close(client->fd) < 0) i_error("close(%s) failed: %m", client->path); client->fd = -1; } i_zero(&result); result.ret = EAI_FAIL; result.error = error; lookup = client->head; client->head = NULL; while (lookup != NULL) { next = lookup->next; lookup->callback(&result, lookup->context); dns_lookup_free(&lookup); lookup = next; } } static int dns_lookup_input_line(struct dns_lookup *lookup, const char *line) { struct dns_lookup_result *result = &lookup->result; if (result->ips_count == 0) { if (lookup->ptr_lookup) { /* [] */ if (strncmp(line, "0 ", 2) == 0) { result->name = lookup->name = i_strdup(line + 2); result->ret = 0; } else { if (str_to_int(line, &result->ret) < 0) { return -1; } result->error = net_gethosterror(result->ret); } return 1; } /* first line: [] */ if (sscanf(line, "%d %u", &result->ret, &result->ips_count) < 1) return -1; if (result->ret != 0) { result->error = net_gethosterror(result->ret); return 1; } if (result->ips_count == 0) return -1; result->ips = lookup->ips = i_new(struct ip_addr, result->ips_count); } else { if (net_addr2ip(line, &lookup->ips[lookup->ip_idx]) < 0) return -1; if (++lookup->ip_idx == result->ips_count) { result->ret = 0; return 1; } } return 0; } static void dns_lookup_save_msecs(struct dns_lookup *lookup) { struct timeval now; int diff; if (gettimeofday(&now, NULL) < 0) i_fatal("gettimeofday() failed: %m"); diff = timeval_diff_msecs(&now, &lookup->start_time); if (diff > 0) lookup->result.msecs = diff; } static void dns_client_input(struct dns_client *client) { const char *line; struct dns_lookup *lookup = client->head; bool retry = FALSE; int ret = 0; while ((line = i_stream_read_next_line(client->input)) != NULL) { if (lookup == NULL) { dns_client_disconnect(client, t_strdup_printf( "Unexpected input from %s", client->path)); return; } ret = dns_lookup_input_line(lookup, line); if (ret > 0) break; if (ret < 0) { dns_client_disconnect(client, t_strdup_printf( "Invalid input from %s", client->path)); return; } } if (ret != 0 && lookup->result.error != NULL) { /* already got the error */ } else if (client->input->stream_errno != 0) { dns_client_disconnect(client, t_strdup_printf( "read(%s) failed: %s", client->path, i_stream_get_error(client->input))); return; } else if (client->input->eof) { dns_client_disconnect(client, t_strdup_printf( "Unexpected EOF from %s", client->path)); return; } if (ret > 0) { dns_lookup_save_msecs(lookup); lookup->callback(&lookup->result, lookup->context); retry = !lookup->client->deinit_client_at_free; dns_lookup_free(&lookup); } if (retry) dns_client_input(client); } static void dns_lookup_timeout(struct dns_lookup *lookup) { lookup->result.error = "DNS lookup timed out"; lookup->callback(&lookup->result, lookup->context); dns_lookup_free(&lookup); } int dns_lookup(const char *host, const struct dns_lookup_settings *set, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) { struct dns_client *client; client = dns_client_init(set); client->deinit_client_at_free = TRUE; if (dns_client_lookup(client, host, callback, context, lookup_r) < 0) { dns_client_deinit(&client); return -1; } return 0; } int dns_lookup_ptr(const struct ip_addr *ip, const struct dns_lookup_settings *set, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) { struct dns_client *client; client = dns_client_init(set); client->deinit_client_at_free = TRUE; if (dns_client_lookup_ptr(client, ip, callback, context, lookup_r) < 0) { dns_client_deinit(&client); return -1; } return 0; } static void dns_client_idle_timeout(struct dns_client *client) { i_assert(client->head == NULL); dns_client_disconnect(client, "Idle timeout"); } static void dns_lookup_free(struct dns_lookup **_lookup) { struct dns_lookup *lookup = *_lookup; struct dns_client *client = lookup->client; *_lookup = NULL; DLLIST2_REMOVE(&client->head, &client->tail, lookup); if (lookup->to != NULL) timeout_remove(&lookup->to); i_free(lookup->name); i_free(lookup->ips); if (client->deinit_client_at_free) dns_client_deinit(&client); else if (client->head == NULL && client->fd != -1) { client->to_idle = timeout_add(client->idle_timeout_msecs, dns_client_idle_timeout, client); } i_free(lookup); } void dns_lookup_abort(struct dns_lookup **lookup) { dns_lookup_free(lookup); } void dns_lookup_switch_ioloop(struct dns_lookup *lookup) { if (lookup->to != NULL) lookup->to = io_loop_move_timeout(&lookup->to); if (lookup->client->deinit_client_at_free) lookup->client->io = io_loop_move_io(&lookup->client->io); } struct dns_client *dns_client_init(const struct dns_lookup_settings *set) { struct dns_client *client; client = i_new(struct dns_client, 1); client->path = i_strdup(set->dns_client_socket_path); client->timeout_msecs = set->timeout_msecs; client->idle_timeout_msecs = set->idle_timeout_msecs; client->fd = -1; return client; } void dns_client_deinit(struct dns_client **_client) { struct dns_client *client = *_client; *_client = NULL; i_assert(client->head == NULL); dns_client_disconnect(client, "deinit"); i_free(client->path); i_free(client); } int dns_client_connect(struct dns_client *client, const char **error_r) { if (client->fd != -1) return 0; client->fd = net_connect_unix(client->path); if (client->fd == -1) { *error_r = t_strdup_printf("connect(%s) failed: %m", client->path); return -1; } client->input = i_stream_create_fd(client->fd, MAX_INBUF_SIZE, FALSE); client->io = io_add(client->fd, IO_READ, dns_client_input, client); return 0; } static int dns_client_send_request(struct dns_client *client, const char *cmd, const char **error_r) { int ret; if (client->fd == -1) { if (dns_client_connect(client, error_r) < 0) return -1; ret = -1; } else { /* already connected. if write() fails, retry connecting */ ret = 0; } if (write_full(client->fd, cmd, strlen(cmd)) < 0) { *error_r = t_strdup_printf("write(%s) failed: %m", client->path); return ret; } return 1; } static int dns_client_lookup_common(struct dns_client *client, const char *cmd, bool ptr_lookup, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) { struct dns_lookup *lookup; struct dns_lookup_result result; int ret; i_zero(&result); result.ret = EAI_FAIL; if ((ret = dns_client_send_request(client, cmd, &result.error)) <= 0) { if (ret == 0) { /* retry once */ ret = dns_client_send_request(client, cmd, &result.error); } if (ret <= 0) { callback(&result, context); return -1; } } lookup = i_new(struct dns_lookup, 1); lookup->client = client; lookup->ptr_lookup = ptr_lookup; if (client->timeout_msecs != 0) { lookup->to = timeout_add(client->timeout_msecs, dns_lookup_timeout, lookup); } lookup->result.ret = EAI_FAIL; lookup->callback = callback; lookup->context = context; if (gettimeofday(&lookup->start_time, NULL) < 0) i_fatal("gettimeofday() failed: %m"); if (client->to_idle != NULL) timeout_remove(&client->to_idle); DLLIST2_APPEND(&client->head, &client->tail, lookup); *lookup_r = lookup; return 0; } int dns_client_lookup(struct dns_client *client, const char *host, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) { const char *cmd = t_strconcat("IP\t", host, "\n", NULL); return dns_client_lookup_common(client, cmd, FALSE, callback, context, lookup_r); } int dns_client_lookup_ptr(struct dns_client *client, const struct ip_addr *ip, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) { const char *cmd = t_strconcat("NAME\t", net_ip2addr(ip), "\n", NULL); return dns_client_lookup_common(client, cmd, TRUE, callback, context, lookup_r); } void dns_client_switch_ioloop(struct dns_client *client) { struct dns_lookup *lookup; if (client->io != NULL) client->io = io_loop_move_io(&client->io); if (client->to_idle != NULL) client->to_idle = io_loop_move_timeout(&client->to_idle); for (lookup = client->head; lookup != NULL; lookup = lookup->next) dns_lookup_switch_ioloop(lookup); } dovecot-2.2.33.2/src/lib-dns/Makefile.in0000644000175000017500000006311513172375572014533 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-dns ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libdns_la_LIBADD = am_libdns_la_OBJECTS = dns-lookup.lo dns-util.lo libdns_la_OBJECTS = $(am_libdns_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-dns-util$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) test_dns_util_SOURCES = test-dns-util.c test_dns_util_OBJECTS = test_dns_util-test-dns-util.$(OBJEXT) test_dns_util_DEPENDENCIES = $(test_libs) test_dns_util_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_dns_util_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdns_la_SOURCES) test-dns-util.c DIST_SOURCES = $(libdns_la_SOURCES) test-dns-util.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libdns.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libdns_la_SOURCES = \ dns-lookup.c \ dns-util.c headers = \ dns-lookup.h \ dns-util.h test_programs = \ test-dns-util test_libs = \ libdns.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_dns_util_SOURCE = test-dns-util.c test_dns_util_LDADD = $(test_libs) test_dns_util_CFLAGS = $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-test pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-dns/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-dns/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdns.la: $(libdns_la_OBJECTS) $(libdns_la_DEPENDENCIES) $(EXTRA_libdns_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libdns_la_OBJECTS) $(libdns_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-dns-util$(EXEEXT): $(test_dns_util_OBJECTS) $(test_dns_util_DEPENDENCIES) $(EXTRA_test_dns_util_DEPENDENCIES) @rm -f test-dns-util$(EXEEXT) $(AM_V_CCLD)$(test_dns_util_LINK) $(test_dns_util_OBJECTS) $(test_dns_util_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns-lookup.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dns_util-test-dns-util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< test_dns_util-test-dns-util.o: test-dns-util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_dns_util_CFLAGS) $(CFLAGS) -MT test_dns_util-test-dns-util.o -MD -MP -MF $(DEPDIR)/test_dns_util-test-dns-util.Tpo -c -o test_dns_util-test-dns-util.o `test -f 'test-dns-util.c' || echo '$(srcdir)/'`test-dns-util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_dns_util-test-dns-util.Tpo $(DEPDIR)/test_dns_util-test-dns-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-dns-util.c' object='test_dns_util-test-dns-util.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_dns_util_CFLAGS) $(CFLAGS) -c -o test_dns_util-test-dns-util.o `test -f 'test-dns-util.c' || echo '$(srcdir)/'`test-dns-util.c test_dns_util-test-dns-util.obj: test-dns-util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_dns_util_CFLAGS) $(CFLAGS) -MT test_dns_util-test-dns-util.obj -MD -MP -MF $(DEPDIR)/test_dns_util-test-dns-util.Tpo -c -o test_dns_util-test-dns-util.obj `if test -f 'test-dns-util.c'; then $(CYGPATH_W) 'test-dns-util.c'; else $(CYGPATH_W) '$(srcdir)/test-dns-util.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_dns_util-test-dns-util.Tpo $(DEPDIR)/test_dns_util-test-dns-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-dns-util.c' object='test_dns_util-test-dns-util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_dns_util_CFLAGS) $(CFLAGS) -c -o test_dns_util-test-dns-util.obj `if test -f 'test-dns-util.c'; then $(CYGPATH_W) 'test-dns-util.c'; else $(CYGPATH_W) '$(srcdir)/test-dns-util.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkginc_libHEADERS install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkginc_libHEADERS .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if test "$$bin" = "test-program-client-local"; then \ if ! env NOVALGRIND=yes $(RUN_TEST) ./$$bin; then exit 1; fi; \ else \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ fi \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-dns/dns-lookup.h0000644000175000017500000000646513123174404014722 00000000000000#ifndef DNS_LOOKUP_H #define DNS_LOOKUP_H #define DNS_CLIENT_SOCKET_NAME "dns-client" struct dns_lookup; struct dns_lookup_settings { const char *dns_client_socket_path; unsigned int timeout_msecs; /* the idle_timeout_msecs works only with the dns_client_* API. 0 = disconnect immediately */ unsigned int idle_timeout_msecs; }; struct dns_lookup_result { /* all is ok if ret=0, otherwise it contains net_gethosterror() compatible error code. error string is always set if ret != 0. */ int ret; const char *error; /* how many milliseconds the lookup took. */ unsigned int msecs; /* for IP lookup: */ unsigned int ips_count; const struct ip_addr *ips; /* for PTR lookup: */ const char *name; }; typedef void dns_lookup_callback_t(const struct dns_lookup_result *result, void *context); /* Do asynchronous DNS lookup via dns-client UNIX socket. Returns 0 if lookup started, -1 if there was an error communicating with the UNIX socket. When failing with -1, the callback is called before returning from the function. */ int dns_lookup(const char *host, const struct dns_lookup_settings *set, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) ATTR_NULL(4); #define dns_lookup(host, set, callback, context, lookup_r) \ dns_lookup(host + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct dns_lookup_result *, typeof(context))), \ set, (dns_lookup_callback_t *)callback, context, lookup_r) int dns_lookup_ptr(const struct ip_addr *ip, const struct dns_lookup_settings *set, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) ATTR_NULL(4); #define dns_lookup_ptr(host, set, callback, context, lookup_r) \ dns_lookup_ptr(host + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct dns_lookup_result *, typeof(context))), \ set, (dns_lookup_callback_t *)callback, context, lookup_r) /* Abort the DNS lookup without calling the callback. */ void dns_lookup_abort(struct dns_lookup **lookup); void dns_lookup_switch_ioloop(struct dns_lookup *lookup); /* Alternative API for clients that need to do multiple DNS lookups. */ struct dns_client *dns_client_init(const struct dns_lookup_settings *set); void dns_client_deinit(struct dns_client **client); /* Connect immediately to the dns-lookup socket. */ int dns_client_connect(struct dns_client *client, const char **error_r); int dns_client_lookup(struct dns_client *client, const char *host, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) ATTR_NULL(4); #define dns_client_lookup(client, host, callback, context, lookup_r) \ dns_client_lookup(client, host + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct dns_lookup_result *, typeof(context))), \ (dns_lookup_callback_t *)callback, context, lookup_r) int dns_client_lookup_ptr(struct dns_client *client, const struct ip_addr *ip, dns_lookup_callback_t *callback, void *context, struct dns_lookup **lookup_r) ATTR_NULL(4); #define dns_client_lookup_ptr(client, host, callback, context, lookup_r) \ dns_client_lookup_ptr(client, host + \ CALLBACK_TYPECHECK(callback, void (*)( \ const struct dns_lookup_result *, typeof(context))), \ (dns_lookup_callback_t *)callback, context, lookup_r) void dns_client_switch_ioloop(struct dns_client *client); #endif dovecot-2.2.33.2/src/lib-dns/dns-util.c0000644000175000017500000000414213123174404014347 00000000000000/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dns-util.h" /** return first position from b->a of c or a if not found */ static inline const char *strchr_ba(const char *a, const char *b, char c) { for(;b>a && *b != c; b--); return b; } int dns_ncompare(const char *a, const char *b, size_t n) { if (a == NULL && b == NULL) return 0; if (a == NULL && b != NULL) return 1; if (a != NULL && b == NULL) return -1; for(size_t i = 0; i < n && *a != '\0' && *b != '\0' && dns_tolower(*a) == dns_tolower(*b); i++, a++, b++); return dns_tolower(*a) - dns_tolower(*b); } int dns_compare(const char *a, const char *b) { return dns_ncompare(a, b, (size_t)-1); } int dns_compare_labels(const char *a, const char *b) { if (a == NULL && b == NULL) return 0; if (a == NULL && b != NULL) return 1; if (a != NULL && b == NULL) return -1; const char *ptr_a = a + strlen(a); const char *ptr_b = b + strlen(b); const char *label_a = ptr_a, *label_b = ptr_b; int comp = 0; while(comp == 0 && ptr_a > a && ptr_b > b) { /* look for start of label, including dot */ label_a = strchr_ba(a, ptr_a, '.'); label_b = strchr_ba(b, ptr_b, '.'); if (ptr_a - label_a != ptr_b - label_b) /* compare labels up to minimum length but include \0 to make sure that we don't consider alpha and alphabet equal */ return dns_ncompare(label_a, label_b, I_MIN(ptr_a - label_a, ptr_b - label_b)+1); comp = dns_ncompare(label_a, label_b, ptr_a -label_a); ptr_a = label_a - 1; ptr_b = label_b - 1; } return dns_tolower(*label_a) - dns_tolower(*label_b); } int dns_match_wildcard(const char *name, const char *mask) { i_assert(name != NULL && mask != NULL); for(;*name != '\0' && *mask != '\0'; name++, mask++) { switch(*mask) { case '*': name = strchr(name, '.'); if (name == NULL || mask[1] != '.') return -1; mask++; break; case '?': break; default: if (dns_tolower(*name) != dns_tolower(*mask)) return -1; } } if (*mask == '*') mask++; return dns_tolower(*name) == dns_tolower(*mask) ? 0 : -1; } dovecot-2.2.33.2/src/lib-dns/test-dns-util.c0000644000175000017500000000500013165463624015331 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-lib.h" #include "dns-util.h" #include "array.h" static void test_dns_compare(void) { struct { const char *a; const char *b; int res; } tests[] = { { NULL, NULL, 0 }, { NULL, "", 1 }, { "", NULL, -1 }, { "", "", 0 }, { "a", "a", 0 }, { "a", "b", -1 }, { "b", "a", 1 }, { "A", "A", 0 }, { "A", "B", -1 }, { "B", "A", 1 }, { "A", "a", 0 }, { "a", "B", -1 }, { "B", "a", 1 }, { "test.invalid", "TeSt.InVaLid", 0 }, { "alphabet.com", "alpha.com", 52 }, { "com.alphabet", "com.alpha", 98 }, { "com.com", "com.comcom", -99 }, }; test_begin("test_dns_compare"); for(size_t i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(dns_compare(tests[i].a, tests[i].b) == tests[i].res, i); test_assert_idx(dns_compare_labels(tests[i].a, tests[i].b) == tests[i].res, i); } test_end(); } static void test_dns_match(void) { struct { const char *name; const char *mask; int res; } tests[] = { { "", "", 0 }, { "", "*", 0 }, { "*", "", -1 }, { "TeSt.InVaLid", "test.invalid", 0 }, { "contoso.com", "test.invalid", -1 }, { "test.invalid", "test.unvalid", -1 }, { "name.test.invalid", "*.test.invalid", 0 }, { "real.name.test.invalid", "*.test.invalid", -1 }, { "real.name.test.invalid", "*.*.test.invalid", 0 }, { "name.test.invalid", "*name*.test.invalid", -1 }, { "name.invalid", "name.*", -1 }, }; test_begin("test_dns_match"); for(size_t i = 0; i < N_ELEMENTS(tests); i++) { test_assert_idx(dns_match_wildcard(tests[i].name, tests[i].mask) == tests[i].res, i); } test_end(); } static int arr_dns_compare(const char *const *a, const char *const *b) { return dns_compare_labels(*a,*b); } static void test_dns_sort(void) { const char *input[] = { "test.invalid", "test.com", "test.contoso.com", "test.alphabet.com", "test.xxx", }; const char *output[] = { "test.alphabet.com", "test.contoso.com", "test.com", "test.invalid", "test.xxx", }; test_begin("test_dns_sort"); ARRAY_TYPE(const_string) arr; t_array_init(&arr, 8); array_append(&arr, input, N_ELEMENTS(input)); array_sort(&arr, arr_dns_compare); for(size_t i = 0; i < N_ELEMENTS(output); i++) { const char *const *strp = array_idx(&arr, i); test_assert_idx(dns_compare(*strp, output[i]) == 0, i); } test_end(); } int main(void) { void (*test_functions[])(void) = { test_dns_compare, test_dns_match, test_dns_sort, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-dns/dns-util.h0000644000175000017500000000144513123174404014357 00000000000000#ifndef DNS_UTIL_H #define DNS_UTIL_H 1 static inline char dns_tolower(char c) { if (c >= 'A' && c <= 'Z') c+='a'-'A'; return c; } /** * Will compare names in accordance with RFC4343 */ int dns_compare(const char *a, const char *b) ATTR_PURE; int dns_ncompare(const char *a, const char *b, size_t n) ATTR_PURE; /** * Same as above but done by labels from right to left * * www.example.org and www.example.net would be compared as * org = net (return first difference) * example = example * www = www */ int dns_compare_labels(const char *a, const char *b) ATTR_PURE; /** * Will match names in RFC4592 style * * this means *.foo.bar will match name.foo.bar * but *DOES NOT* match something.name.foo.bar */ int dns_match_wildcard(const char *name, const char *mask) ATTR_PURE; #endif dovecot-2.2.33.2/src/lib-dns/Makefile.am0000644000175000017500000000144013123174404014476 00000000000000noinst_LTLIBRARIES = libdns.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libdns_la_SOURCES = \ dns-lookup.c \ dns-util.c headers = \ dns-lookup.h \ dns-util.h test_programs = \ test-dns-util noinst_PROGRAMS = $(test_programs) test_libs = \ libdns.la \ ../lib-test/libtest.la \ ../lib/liblib.la test_dns_util_SOURCE = test-dns-util.c test_dns_util_LDADD = $(test_libs) test_dns_util_CFLAGS = $(AM_CPPFLAGS) \ -I$(top_srcdir)/src/lib-test check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if test "$$bin" = "test-program-client-local"; then \ if ! env NOVALGRIND=yes $(RUN_TEST) ./$$bin; then exit 1; fi; \ else \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ fi \ done pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/config/0002755000175000017500000000000013172375612012452 500000000000000dovecot-2.2.33.2/src/config/sysinfo-get.h0000644000175000017500000000015113123174404014775 00000000000000#ifndef SYSINFO_GET_H #define SYSINFO_GET_H const char *sysinfo_get(const char *mail_location); #endif dovecot-2.2.33.2/src/config/config-parser.c0000644000175000017500000007646413165463624015317 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "strescape.h" #include "istream.h" #include "module-dir.h" #include "settings-parser.h" #include "service-settings.h" #include "master-service.h" #include "master-service-settings.h" #include "master-service-ssl-settings.h" #include "all-settings.h" #include "old-set-parser.h" #include "config-request.h" #include "config-parser-private.h" #include #include #include #ifdef HAVE_GLOB_H # include #endif #ifndef GLOB_BRACE # define GLOB_BRACE 0 #endif #define DNS_LOOKUP_TIMEOUT_SECS 30 #define DNS_LOOKUP_WARN_SECS 5 ARRAY_DEFINE_TYPE(setting_parser_info_p, const struct setting_parser_info *); static const enum settings_parser_flags settings_parser_flags = SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS | SETTINGS_PARSER_FLAG_TRACK_CHANGES; struct config_module_parser *config_module_parsers; struct config_filter_context *config_filter; struct module *modules; void (*hook_config_parser_begin)(struct config_parser_context *ctx); int (*hook_config_parser_end)(struct config_parser_context *ctx, const char **error_r); static ARRAY_TYPE(service_settings) services_free_at_deinit = ARRAY_INIT; static ARRAY_TYPE(setting_parser_info_p) roots_free_at_deinit = ARRAY_INIT; static const char *info_type_name_find(const struct setting_parser_info *info) { unsigned int i; for (i = 0; info->defines[i].key != NULL; i++) { if (info->defines[i].offset == info->type_offset) return info->defines[i].key; } i_panic("setting parser: Invalid type_offset value"); return NULL; } static int config_add_type(struct setting_parser_context *parser, const char *line, const char *section_name) { const struct setting_parser_info *info; const char *p; string_t *str; int ret; info = settings_parse_get_prev_info(parser); if (info == NULL) { /* section inside strlist */ return -1; } if (info->type_offset == (size_t)-1) return 0; str = t_str_new(256); p = strchr(line, '='); str_append_n(str, line, p-line); str_append_c(str, SETTINGS_SEPARATOR); str_append(str, p+1); if (info != NULL) { str_append_c(str, SETTINGS_SEPARATOR); str_append(str, info_type_name_find(info)); } ret = settings_parse_keyvalue(parser, str_c(str), section_name); i_assert(ret > 0); return 0; } static bool config_parser_is_in_localremote(struct config_section_stack *section) { const struct config_filter *filter = §ion->filter; return filter->local_name != NULL || filter->local_bits > 0 || filter->remote_bits > 0; } static void section_stack_write(string_t *str, struct config_section_stack *section) { if (section == NULL) return; section_stack_write(str, section->prev); if (!section->is_filter && section->key != NULL) str_printfa(str, "%s { ", section->key); } static const char * get_setting_full_path(struct config_parser_context *ctx, const char *key) { string_t *str = t_str_new(128); section_stack_write(str, ctx->cur_section); str_append(str, key); return str_c(str); } int config_apply_line(struct config_parser_context *ctx, const char *key, const char *line, const char *section_name) { struct config_module_parser *l; bool found = FALSE; int ret; for (l = ctx->cur_section->parsers; l->root != NULL; l++) { ret = settings_parse_line(l->parser, line); if (ret > 0) { found = TRUE; /* FIXME: remove once auth does support these. */ if (strcmp(l->root->module_name, "auth") == 0 && config_parser_is_in_localremote(ctx->cur_section)) { ctx->error = p_strconcat(ctx->pool, "Auth settings not supported inside local/remote blocks: ", key, NULL); return -1; } if (section_name != NULL) { if (config_add_type(l->parser, line, section_name) < 0) { ctx->error = "Section not allowed here"; return -1; } } } else if (ret < 0) { ctx->error = settings_parser_get_error(l->parser); return -1; } } if (!found) { ctx->error = p_strconcat(ctx->pool, "Unknown setting: ", get_setting_full_path(ctx, key), NULL); return -1; } return 0; } static const char * fix_relative_path(const char *path, struct input_stack *input) { const char *p; if (*path == '/') return path; p = strrchr(input->path, '/'); if (p == NULL) return path; return t_strconcat(t_strdup_until(input->path, p+1), path, NULL); } static struct config_module_parser * config_module_parsers_init(pool_t pool) { struct config_module_parser *dest; unsigned int i, count; for (count = 0; all_roots[count] != NULL; count++) ; dest = p_new(pool, struct config_module_parser, count + 1); for (i = 0; i < count; i++) { dest[i].root = all_roots[i]; dest[i].parser = settings_parser_init(pool, all_roots[i], settings_parser_flags); } return dest; } static void config_add_new_parser(struct config_parser_context *ctx) { struct config_section_stack *cur_section = ctx->cur_section; struct config_filter_parser *parser; parser = p_new(ctx->pool, struct config_filter_parser, 1); parser->filter = cur_section->filter; if (ctx->cur_input->linenum == 0) { parser->file_and_line = p_strdup(ctx->pool, ctx->cur_input->path); } else { parser->file_and_line = p_strdup_printf(ctx->pool, "%s:%d", ctx->cur_input->path, ctx->cur_input->linenum); } parser->parsers = cur_section->prev == NULL ? ctx->root_parsers : config_module_parsers_init(ctx->pool); array_append(&ctx->all_parsers, &parser, 1); cur_section->parsers = parser->parsers; } static struct config_section_stack * config_add_new_section(struct config_parser_context *ctx) { struct config_section_stack *section; section = p_new(ctx->pool, struct config_section_stack, 1); section->prev = ctx->cur_section; section->filter = ctx->cur_section->filter; section->parsers = ctx->cur_section->parsers; section->open_path = p_strdup(ctx->pool, ctx->cur_input->path); section->open_linenum = ctx->cur_input->linenum; return section; } static struct config_filter_parser * config_filter_parser_find(struct config_parser_context *ctx, const struct config_filter *filter) { struct config_filter_parser *const *parsers; array_foreach(&ctx->all_parsers, parsers) { struct config_filter_parser *parser = *parsers; if (config_filters_equal(&parser->filter, filter)) return parser; } return NULL; } int config_parse_net(const char *value, struct ip_addr *ip_r, unsigned int *bits_r, const char **error_r) { struct ip_addr *ips; const char *p; unsigned int ip_count, bits, max_bits; time_t t1, t2; int ret; if (net_parse_range(value, ip_r, bits_r) == 0) return 0; p = strchr(value, '/'); if (p != NULL) { value = t_strdup_until(value, p); p++; } t1 = time(NULL); alarm(DNS_LOOKUP_TIMEOUT_SECS); ret = net_gethostbyname(value, &ips, &ip_count); alarm(0); t2 = time(NULL); if (ret != 0) { *error_r = t_strdup_printf("gethostbyname(%s) failed: %s", value, net_gethosterror(ret)); return -1; } *ip_r = ips[0]; if (t2 - t1 >= DNS_LOOKUP_WARN_SECS) { i_warning("gethostbyname(%s) took %d seconds", value, (int)(t2-t1)); } max_bits = IPADDR_IS_V4(&ips[0]) ? 32 : 128; if (p == NULL) *bits_r = max_bits; else if (str_to_uint(p, &bits) == 0 && bits <= max_bits) *bits_r = bits; else { *error_r = t_strdup_printf("Invalid network mask: %s", p); return -1; } return 0; } static bool config_filter_add_new_filter(struct config_parser_context *ctx, const char *key, const char *value) { struct config_filter *filter = &ctx->cur_section->filter; struct config_filter *parent = &ctx->cur_section->prev->filter; struct config_filter_parser *parser; const char *error; if (strcmp(key, "protocol") == 0) { if (parent->service != NULL) ctx->error = "Nested protocol { protocol { .. } } block not allowed"; else filter->service = p_strdup(ctx->pool, value); } else if (strcmp(key, "local") == 0) { if (parent->remote_bits > 0) ctx->error = "remote { local { .. } } not allowed (use local { remote { .. } } instead)"; else if (parent->service != NULL) ctx->error = "protocol { local { .. } } not allowed (use local { protocol { .. } } instead)"; else if (parent->local_name != NULL) ctx->error = "local_name { local { .. } } not allowed (use local { local_name { .. } } instead)"; else if (config_parse_net(value, &filter->local_net, &filter->local_bits, &error) < 0) ctx->error = p_strdup(ctx->pool, error); else if (parent->local_bits > filter->local_bits || (parent->local_bits > 0 && !net_is_in_network(&filter->local_net, &parent->local_net, parent->local_bits))) ctx->error = "local net1 { local net2 { .. } } requires net2 to be inside net1"; else filter->local_host = p_strdup(ctx->pool, value); } else if (strcmp(key, "local_name") == 0) { if (parent->remote_bits > 0) ctx->error = "remote { local_name { .. } } not allowed (use local_name { remote { .. } } instead)"; else if (parent->service != NULL) ctx->error = "protocol { local_name { .. } } not allowed (use local_name { protocol { .. } } instead)"; else filter->local_name = p_strdup(ctx->pool, value); } else if (strcmp(key, "remote") == 0) { if (parent->service != NULL) ctx->error = "protocol { remote { .. } } not allowed (use remote { protocol { .. } } instead)"; else if (config_parse_net(value, &filter->remote_net, &filter->remote_bits, &error) < 0) ctx->error = p_strdup(ctx->pool, error); else if (parent->remote_bits > filter->remote_bits || (parent->remote_bits > 0 && !net_is_in_network(&filter->remote_net, &parent->remote_net, parent->remote_bits))) ctx->error = "remote net1 { remote net2 { .. } } requires net2 to be inside net1"; else filter->remote_host = p_strdup(ctx->pool, value); } else { return FALSE; } parser = config_filter_parser_find(ctx, filter); if (parser != NULL) ctx->cur_section->parsers = parser->parsers; else config_add_new_parser(ctx); ctx->cur_section->is_filter = TRUE; return TRUE; } static int config_filter_parser_check(struct config_parser_context *ctx, const struct config_module_parser *p, const char **error_r) { const char *error; char *error_dup = NULL; bool ok; for (; p->root != NULL; p++) { /* skip checking settings we don't care about */ if (!config_module_want_parser(ctx->root_parsers, ctx->modules, p->root)) continue; settings_parse_var_skip(p->parser); T_BEGIN { ok = settings_parser_check(p->parser, ctx->pool, &error); if (!ok) error_dup = i_strdup(error); } T_END; if (!ok) { i_assert(error_dup != NULL); *error_r = t_strdup(error_dup); i_free(error_dup); return -1; } } return 0; } static const char * get_str_setting(struct config_filter_parser *parser, const char *key, const char *default_value) { struct config_module_parser *module_parser; const char *const *set_value; enum setting_type set_type; module_parser = parser->parsers; for (; module_parser->parser != NULL; module_parser++) { set_value = settings_parse_get_value(module_parser->parser, key, &set_type); if (set_value != NULL && settings_parse_is_changed(module_parser->parser, key)) { i_assert(set_type == SET_STR || set_type == SET_ENUM); return *set_value; } } return default_value; } static int config_all_parsers_check(struct config_parser_context *ctx, struct config_filter_context *new_filter, const char **error_r) { struct config_filter_parser *const *parsers; struct config_module_parser *tmp_parsers; struct master_service_settings_output output; unsigned int i, count; const char *ssl_set, *global_ssl_set; pool_t tmp_pool; bool ssl_warned = FALSE; int ret = 0; if (ctx->cur_section->prev != NULL) { *error_r = t_strdup_printf( "Missing '}' (section started at %s:%u)", ctx->cur_section->open_path, ctx->cur_section->open_linenum); return -1; } tmp_pool = pool_alloconly_create(MEMPOOL_GROWING"config parsers check", 1024*64); parsers = array_get(&ctx->all_parsers, &count); i_assert(count > 0 && parsers[count-1] == NULL); count--; global_ssl_set = get_str_setting(parsers[0], "ssl", ""); for (i = 0; i < count && ret == 0; i++) { if (config_filter_parsers_get(new_filter, tmp_pool, NULL, &parsers[i]->filter, &tmp_parsers, &output, error_r) < 0) { ret = -1; break; } ssl_set = get_str_setting(parsers[i], "ssl", global_ssl_set); if (strcmp(ssl_set, "no") != 0 && strcmp(global_ssl_set, "no") == 0 && !ssl_warned) { i_warning("SSL is disabled because global ssl=no, " "ignoring ssl=%s for subsection", ssl_set); ssl_warned = TRUE; } ret = config_filter_parser_check(ctx, tmp_parsers, error_r); config_filter_parsers_free(tmp_parsers); p_clear(tmp_pool); } pool_unref(&tmp_pool); return ret; } static int str_append_file(string_t *str, const char *key, const char *path, const char **error_r) { unsigned char buf[1024]; int fd; ssize_t ret; *error_r = NULL; fd = open(path, O_RDONLY); if (fd == -1) { *error_r = t_strdup_printf("%s: Can't open file %s: %m", key, path); return -1; } while ((ret = read(fd, buf, sizeof(buf))) > 0) str_append_n(str, buf, ret); if (ret < 0) { *error_r = t_strdup_printf("%s: read(%s) failed: %m", key, path); } i_close_fd(&fd); return ret < 0 ? -1 : 0; } static int settings_add_include(struct config_parser_context *ctx, const char *path, bool ignore_errors, const char **error_r) { struct input_stack *tmp, *new_input; int fd; for (tmp = ctx->cur_input; tmp != NULL; tmp = tmp->prev) { if (strcmp(tmp->path, path) == 0) break; } if (tmp != NULL) { *error_r = t_strdup_printf("Recursive include file: %s", path); return -1; } if ((fd = open(path, O_RDONLY)) == -1) { if (ignore_errors) return 0; *error_r = t_strdup_printf("Couldn't open include file %s: %m", path); return -1; } new_input = p_new(ctx->pool, struct input_stack, 1); new_input->prev = ctx->cur_input; new_input->path = p_strdup(ctx->pool, path); new_input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(new_input->input, TRUE); ctx->cur_input = new_input; return 0; } static int settings_include(struct config_parser_context *ctx, const char *pattern, bool ignore_errors) { const char *error; #ifdef HAVE_GLOB glob_t globbers; unsigned int i; switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) { case 0: break; case GLOB_NOSPACE: ctx->error = "glob() failed: Not enough memory"; return -1; case GLOB_ABORTED: ctx->error = "glob() failed: Read error"; return -1; case GLOB_NOMATCH: if (ignore_errors) return 0; ctx->error = "No matches"; return -1; default: ctx->error = "glob() failed: Unknown error"; return -1; } /* iterate throuth the different files matching the globbing */ for (i = globbers.gl_pathc; i > 0; i--) { if (settings_add_include(ctx, globbers.gl_pathv[i-1], ignore_errors, &error) < 0) { ctx->error = p_strdup(ctx->pool, error); return -1; } } globfree(&globbers); return 0; #else if (settings_add_include(ctx, pattern, ignore_errors, &error) < 0) { ctx->error = p_strdup(ctx->pool, error); return -1; } return 0; #endif } static enum config_line_type config_parse_line(struct config_parser_context *ctx, char *line, string_t *full_line, const char **key_r, const char **value_r) { const char *key; size_t len; char *p; *key_r = NULL; *value_r = NULL; /* @UNSAFE: line is modified */ /* skip whitespace */ while (IS_WHITE(*line)) line++; /* ignore comments or empty lines */ if (*line == '#' || *line == '\0') return CONFIG_LINE_TYPE_SKIP; /* strip away comments. pretty kludgy way really.. */ for (p = line; *p != '\0'; p++) { if (*p == '\'' || *p == '"') { char quote = *p; for (p++; *p != quote && *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') p++; } if (*p == '\0') break; } else if (*p == '#') { if (!IS_WHITE(p[-1])) { i_warning("Configuration file %s line %u: " "Ambiguous '#' character in line, treating it as comment. " "Add a space before it to remove this warning.", ctx->cur_input->path, ctx->cur_input->linenum); } *p = '\0'; break; } } /* remove whitespace from end of line */ len = strlen(line); while (len >= 1) { if(!IS_WHITE(line[len-1])) break; len--; } line[len] = '\0'; if (len >= 1 && line[len-1] == '\\') { /* continues in next line */ len--; while (len >= 1) { if(!IS_WHITE(line[len-1])) break; len--; } if(len >= 1) { str_append_n(full_line, line, len); str_append_c(full_line, ' '); } return CONFIG_LINE_TYPE_CONTINUE; } if (str_len(full_line) > 0) { str_append(full_line, line); line = str_c_modifiable(full_line); } /* a) key = value b) section_type [section_name] { c) } */ key = line; while (!IS_WHITE(*line) && *line != '\0' && *line != '=') line++; if (IS_WHITE(*line)) { *line++ = '\0'; while (IS_WHITE(*line)) line++; } *key_r = key; *value_r = line; if (strcmp(key, "!include") == 0) return CONFIG_LINE_TYPE_INCLUDE; if (strcmp(key, "!include_try") == 0) return CONFIG_LINE_TYPE_INCLUDE_TRY; if (*line == '=') { /* a) */ *line++ = '\0'; while (IS_WHITE(*line)) line++; if (*line == '<') { while (IS_WHITE(line[1])) line++; *value_r = line + 1; return CONFIG_LINE_TYPE_KEYFILE; } if (*line == '$') { *value_r = line + 1; return CONFIG_LINE_TYPE_KEYVARIABLE; } len = strlen(line); if (len > 0 && ((*line == '"' && line[len-1] == '"') || (*line == '\'' && line[len-1] == '\''))) { line[len-1] = '\0'; *value_r = str_unescape(line+1); return CONFIG_LINE_TYPE_KEYVALUE_QUOTED; } else { *value_r = line; return CONFIG_LINE_TYPE_KEYVALUE; } } if (strcmp(key, "}") == 0 && *line == '\0') return CONFIG_LINE_TYPE_SECTION_END; /* b) + errors */ line[-1] = '\0'; if (*line == '{') *value_r = ""; else { /* get section name */ if (*line != '"') { *value_r = line; while (!IS_WHITE(*line) && *line != '\0') line++; if (*line != '\0') { *line++ = '\0'; while (IS_WHITE(*line)) line++; } } else { char *value = ++line; while (*line != '"' && *line != '\0') line++; if (*line == '"') { *line++ = '\0'; while (IS_WHITE(*line)) line++; *value_r = str_unescape(value); } } if (*line != '{') { *value_r = "Expecting '{'"; return CONFIG_LINE_TYPE_ERROR; } } if (line[1] != '\0') { *value_r = "Garbage after '{'"; return CONFIG_LINE_TYPE_ERROR; } return CONFIG_LINE_TYPE_SECTION_BEGIN; } static int config_parse_finish(struct config_parser_context *ctx, const char **error_r) { struct config_filter_context *new_filter; const char *error; int ret = 0; if (hook_config_parser_end != NULL) ret = hook_config_parser_end(ctx, error_r); new_filter = config_filter_init(ctx->pool); array_append_zero(&ctx->all_parsers); config_filter_add_all(new_filter, array_idx(&ctx->all_parsers, 0)); if (ret < 0) ; else if (ctx->hide_errors) ret = 0; else if ((ret = config_all_parsers_check(ctx, new_filter, &error)) < 0) { *error_r = t_strdup_printf("Error in configuration file %s: %s", ctx->path, error); } if (config_filter != NULL) config_filter_deinit(&config_filter); config_module_parsers = ctx->root_parsers; config_filter = new_filter; return ret; } static const void * config_get_value(struct config_section_stack *section, const char *key, bool expand_parent, enum setting_type *type_r) { struct config_module_parser *l; const void *value; for (l = section->parsers; l->root != NULL; l++) { value = settings_parse_get_value(l->parser, key, type_r); if (value != NULL) { if (!expand_parent || section->prev == NULL || settings_parse_is_changed(l->parser, key)) return value; /* not changed by this parser. maybe parent has. */ return config_get_value(section->prev, key, TRUE, type_r); } } return NULL; } static bool config_require_key(struct config_parser_context *ctx, const char *key) { struct config_module_parser *l; if (ctx->modules == NULL) return TRUE; for (l = ctx->cur_section->parsers; l->root != NULL; l++) { if (config_module_want_parser(ctx->root_parsers, ctx->modules, l->root) && settings_parse_is_valid_key(l->parser, key)) return TRUE; } return FALSE; } static int config_write_value(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value) { string_t *str = ctx->str; const void *var_name, *var_value, *p; enum setting_type var_type; const char *error, *path, *full_key; bool dump, expand_parent; switch (type) { case CONFIG_LINE_TYPE_KEYVALUE: case CONFIG_LINE_TYPE_KEYVALUE_QUOTED: str_append(str, value); break; case CONFIG_LINE_TYPE_KEYFILE: full_key = t_strndup(str_data(ctx->str), str_len(str)-1); if (!ctx->expand_values) { str_append_c(str, '<'); str_append(str, value); } else { if (!config_require_key(ctx, full_key)) { /* don't even try to open the file */ } else { path = fix_relative_path(value, ctx->cur_input); if (str_append_file(str, full_key, path, &error) < 0) { /* file reading failed */ ctx->error = p_strdup(ctx->pool, error); return -1; } } } break; case CONFIG_LINE_TYPE_KEYVARIABLE: /* expand_parent=TRUE for "key = $key stuff". we'll always expand it so that doveconf -n can give usable output */ p = strchr(value, ' '); if (p == NULL) var_name = value; else var_name = t_strdup_until(value, p); expand_parent = strcmp(key, var_name) == 0; if (!ctx->expand_values && !expand_parent) { str_append_c(str, '$'); str_append(str, value); } else { var_value = config_get_value(ctx->cur_section, var_name, expand_parent, &var_type); if (var_value == NULL) { ctx->error = p_strconcat(ctx->pool, "Unknown variable: $", var_name, NULL); return -1; } if (!config_export_type(str, var_value, NULL, var_type, TRUE, &dump)) { ctx->error = p_strconcat(ctx->pool, "Invalid variable: $", var_name, NULL); return -1; } if (p != NULL) str_append(str, p); } break; default: i_unreached(); } return 0; } static void config_parser_check_warnings(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value) { const char *path, *first_pos; if (strncmp(str_c(ctx->str), "plugin/", 7) == 0 && strcasecmp(value, "no") == 0 && type == CONFIG_LINE_TYPE_KEYVALUE) { i_warning("%s line %u: plugin { %s=%s } is most likely handled as 'yes' - " "remove the setting completely to disable it. " "If this is intentional, add quotes around the value: %s=\"%s\"", ctx->cur_input->path, ctx->cur_input->linenum, key, value, key, value); } first_pos = hash_table_lookup(ctx->seen_settings, str_c(ctx->str)); if (ctx->cur_section->prev == NULL) { /* changing a root setting. if we've already seen it inside filters, log a warning. */ if (first_pos == NULL) return; i_warning("%s line %u: Global setting %s won't change the setting inside an earlier filter at %s " "(if this is intentional, avoid this warning by moving the global setting before %s)", ctx->cur_input->path, ctx->cur_input->linenum, key, first_pos, first_pos); return; } if (first_pos != NULL) return; first_pos = p_strdup_printf(ctx->pool, "%s line %u", ctx->cur_input->path, ctx->cur_input->linenum); path = p_strdup(ctx->pool, str_c(ctx->str)); hash_table_insert(ctx->seen_settings, path, first_pos); } void config_parser_apply_line(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value) { const char *section_name; str_truncate(ctx->str, ctx->pathlen); switch (type) { case CONFIG_LINE_TYPE_SKIP: break; case CONFIG_LINE_TYPE_CONTINUE: i_unreached(); case CONFIG_LINE_TYPE_ERROR: ctx->error = p_strdup(ctx->pool, value); break; case CONFIG_LINE_TYPE_KEYVALUE: case CONFIG_LINE_TYPE_KEYVALUE_QUOTED: case CONFIG_LINE_TYPE_KEYFILE: case CONFIG_LINE_TYPE_KEYVARIABLE: str_append(ctx->str, key); config_parser_check_warnings(ctx, type, key, value); str_append_c(ctx->str, '='); if (config_write_value(ctx, type, key, value) < 0) break; (void)config_apply_line(ctx, key, str_c(ctx->str), NULL); break; case CONFIG_LINE_TYPE_SECTION_BEGIN: ctx->cur_section = config_add_new_section(ctx); ctx->cur_section->pathlen = ctx->pathlen; ctx->cur_section->key = p_strdup(ctx->pool, key); if (config_filter_add_new_filter(ctx, key, value)) { /* new filter */ break; } /* new config section */ if (*value == '\0') { /* no section name, use a counter */ section_name = dec2str(ctx->section_counter++); } else { section_name = settings_section_escape(value); } str_append(ctx->str, key); ctx->pathlen = str_len(ctx->str); str_append_c(ctx->str, '='); str_append(ctx->str, section_name); if (config_apply_line(ctx, key, str_c(ctx->str), value) < 0) break; str_truncate(ctx->str, ctx->pathlen); str_append_c(ctx->str, SETTINGS_SEPARATOR); str_append(ctx->str, section_name); str_append_c(ctx->str, SETTINGS_SEPARATOR); ctx->pathlen = str_len(ctx->str); break; case CONFIG_LINE_TYPE_SECTION_END: if (ctx->cur_section->prev == NULL) ctx->error = "Unexpected '}'"; else { ctx->pathlen = ctx->cur_section->pathlen; ctx->cur_section = ctx->cur_section->prev; } break; case CONFIG_LINE_TYPE_INCLUDE: case CONFIG_LINE_TYPE_INCLUDE_TRY: (void)settings_include(ctx, fix_relative_path(value, ctx->cur_input), type == CONFIG_LINE_TYPE_INCLUDE_TRY); break; } } int config_parse_file(const char *path, bool expand_values, const char *const *modules, const char **error_r) { struct input_stack root; struct config_parser_context ctx; unsigned int i, count; const char *key, *value; string_t *full_line; enum config_line_type type; char *line; int fd, ret = 0; bool handled; if (path == NULL) { path = ""; fd = -1; } else { fd = open(path, O_RDONLY); if (fd < 0) { *error_r = t_strdup_printf("open(%s) failed: %m", path); return 0; } } i_zero(&ctx); ctx.pool = pool_alloconly_create(MEMPOOL_GROWING"config file parser", 1024*256); ctx.path = path; ctx.hide_errors = fd == -1; for (count = 0; all_roots[count] != NULL; count++) ; ctx.root_parsers = p_new(ctx.pool, struct config_module_parser, count+1); for (i = 0; i < count; i++) { ctx.root_parsers[i].root = all_roots[i]; ctx.root_parsers[i].parser = settings_parser_init(ctx.pool, all_roots[i], settings_parser_flags); } i_zero(&root); root.path = path; ctx.cur_input = &root; ctx.expand_values = expand_values; ctx.modules = modules; hash_table_create(&ctx.seen_settings, ctx.pool, 0, str_hash, strcmp); p_array_init(&ctx.all_parsers, ctx.pool, 128); ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1); config_add_new_parser(&ctx); ctx.str = str_new(ctx.pool, 256); full_line = str_new(default_pool, 512); ctx.cur_input->input = fd != -1 ? i_stream_create_fd_autoclose(&fd, (size_t)-1) : i_stream_create_from_data("", 0); i_stream_set_return_partial_line(ctx.cur_input->input, TRUE); old_settings_init(&ctx); if (hook_config_parser_begin != NULL) T_BEGIN { hook_config_parser_begin(&ctx); } T_END; prevfile: while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) { ctx.cur_input->linenum++; type = config_parse_line(&ctx, line, full_line, &key, &value); str_truncate(ctx.str, ctx.pathlen); if (type == CONFIG_LINE_TYPE_CONTINUE) continue; T_BEGIN { handled = old_settings_handle(&ctx, type, key, value); if (!handled) config_parser_apply_line(&ctx, type, key, value); } T_END; if (ctx.error != NULL) { *error_r = t_strdup_printf( "Error in configuration file %s line %d: %s", ctx.cur_input->path, ctx.cur_input->linenum, ctx.error); ret = -2; break; } str_truncate(full_line, 0); } i_stream_destroy(&ctx.cur_input->input); ctx.cur_input = ctx.cur_input->prev; if (line == NULL && ctx.cur_input != NULL) goto prevfile; hash_table_destroy(&ctx.seen_settings); str_free(&full_line); if (ret == 0) ret = config_parse_finish(&ctx, error_r); return ret < 0 ? ret : 1; } void config_parse_load_modules(void) { struct module_dir_load_settings mod_set; struct module *m; const struct setting_parser_info **roots; ARRAY_TYPE(setting_parser_info_p) new_roots; ARRAY_TYPE(service_settings) new_services; struct service_settings *const *services, *service_set; unsigned int i, count; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; modules = module_dir_load(CONFIG_MODULE_DIR, NULL, &mod_set); module_dir_init(modules); i_array_init(&new_roots, 64); i_array_init(&new_services, 64); for (m = modules; m != NULL; m = m->next) { roots = module_get_symbol_quiet(m, t_strdup_printf("%s_set_roots", m->name)); if (roots != NULL) { for (i = 0; roots[i] != NULL; i++) array_append(&new_roots, &roots[i], 1); } services = module_get_symbol_quiet(m, t_strdup_printf("%s_service_settings_array", m->name)); if (services != NULL) { for (count = 0; services[count] != NULL; count++) ; array_append(&new_services, services, count); } else { service_set = module_get_symbol_quiet(m, t_strdup_printf("%s_service_settings", m->name)); if (service_set != NULL) array_append(&new_services, &service_set, 1); } } if (array_count(&new_roots) > 0) { /* modules added new settings. add the defaults and start using the new list. */ for (i = 0; all_roots[i] != NULL; i++) array_append(&new_roots, &all_roots[i], 1); array_append_zero(&new_roots); all_roots = array_idx(&new_roots, 0); roots_free_at_deinit = new_roots; } else { array_free(&new_roots); } if (array_count(&new_services) > 0) { /* module added new services. update the defaults. */ services = array_get(default_services, &count); for (i = 0; i < count; i++) array_append(&new_services, &services[i], 1); *default_services = new_services; services_free_at_deinit = new_services; } else { array_free(&new_services); } } static bool parsers_are_connected(const struct setting_parser_info *root, const struct setting_parser_info *info) { const struct setting_parser_info *p; const struct setting_parser_info *const *dep; /* we're trying to find info or its parents from root's dependencies. */ for (p = info; p != NULL; p = p->parent) { if (p == root) return TRUE; } if (root->dependencies != NULL) { for (dep = root->dependencies; *dep != NULL; dep++) { if (parsers_are_connected(*dep, info)) return TRUE; } } return FALSE; } bool config_module_want_parser(struct config_module_parser *parsers, const char *const *modules, const struct setting_parser_info *root) { struct config_module_parser *l; if (modules == NULL) return TRUE; if (root == &master_service_setting_parser_info) { /* everyone wants master service settings */ return TRUE; } for (l = parsers; l->root != NULL; l++) { if (!str_array_find(modules, l->root->module_name)) continue; /* see if we can find a way to get from the original parser to this parser */ if (parsers_are_connected(l->root, root)) return TRUE; } return FALSE; } void config_parser_deinit(void) { if (array_is_created(&services_free_at_deinit)) array_free(&services_free_at_deinit); if (array_is_created(&roots_free_at_deinit)) array_free(&roots_free_at_deinit); } dovecot-2.2.33.2/src/config/Makefile.in0000644000175000017500000007001713172375572014447 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = doveconf$(EXEEXT) pkglibexec_PROGRAMS = config$(EXEEXT) subdir = src/config ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginclude_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(pkgincludedir)" PROGRAMS = $(bin_PROGRAMS) $(pkglibexec_PROGRAMS) am__objects_1 = all-settings.$(OBJEXT) config-connection.$(OBJEXT) \ config-filter.$(OBJEXT) config-parser.$(OBJEXT) \ config-request.$(OBJEXT) old-set-parser.$(OBJEXT) \ sysinfo-get.$(OBJEXT) am_config_OBJECTS = main.$(OBJEXT) $(am__objects_1) config_OBJECTS = $(am_config_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_doveconf_OBJECTS = doveconf.$(OBJEXT) $(am__objects_1) doveconf_OBJECTS = $(am_doveconf_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(config_SOURCES) $(doveconf_SOURCES) DIST_SOURCES = $(config_SOURCES) $(doveconf_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ exampledir = $(docdir)/example-config AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DEXAMPLE_CONFIG_DIR=\""$(exampledir)"\" \ -DMODULEDIR=\""$(moduledir)"\" \ -DSSLDIR=\""$(ssldir)\"" config_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) config_DEPENDENCIES = $(LIBDOVECOT_DEPS) doveconf_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) doveconf_DEPENDENCIES = $(LIBDOVECOT_DEPS) common = \ all-settings.c \ config-connection.c \ config-filter.c \ config-parser.c \ config-request.c \ old-set-parser.c \ sysinfo-get.c config_SOURCES = \ main.c \ $(common) doveconf_SOURCES = \ doveconf.c \ $(common) noinst_HEADERS = \ all-settings.h \ config-connection.h \ old-set-parser.h \ sysinfo-get.h pkginclude_HEADERS = \ config-filter.h \ config-parser.h \ config-parser-private.h \ config-request.h EXTRA_DIST = \ config-settings.c \ settings-get.pl all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/config/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/config/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list config$(EXEEXT): $(config_OBJECTS) $(config_DEPENDENCIES) $(EXTRA_config_DEPENDENCIES) @rm -f config$(EXEEXT) $(AM_V_CCLD)$(LINK) $(config_OBJECTS) $(config_LDADD) $(LIBS) doveconf$(EXEEXT): $(doveconf_OBJECTS) $(doveconf_DEPENDENCIES) $(EXTRA_doveconf_DEPENDENCIES) @rm -f doveconf$(EXEEXT) $(AM_V_CCLD)$(LINK) $(doveconf_OBJECTS) $(doveconf_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/all-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-request.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveconf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/old-set-parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysinfo-get.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkgincludeHEADERS: $(pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkgincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libtool \ clean-pkglibexecPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkgincludeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-pkgincludeHEADERS \ uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic clean-libtool \ clean-pkglibexecPROGRAMS cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkgincludeHEADERS \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-pkgincludeHEADERS \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl $(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/config/config-connection.c0000644000175000017500000001351013165463624016141 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "istream.h" #include "ostream.h" #include "strescape.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "config-request.h" #include "config-parser.h" #include "config-connection.h" #include #define MAX_INBUF_SIZE 1024 #define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 2 #define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0 struct config_connection { struct config_connection *prev, *next; int fd; struct istream *input; struct ostream *output; struct io *io; unsigned int version_received:1; unsigned int handshaked:1; }; static struct config_connection *config_connections = NULL; static const char *const * config_connection_next_line(struct config_connection *conn) { const char *line; line = i_stream_next_line(conn->input); if (line == NULL) return NULL; return t_strsplit_tabescaped(line); } static void config_request_output(const char *key, const char *value, enum config_key_type type ATTR_UNUSED, void *context) { struct ostream *output = context; const char *p; o_stream_nsend_str(output, key); o_stream_nsend_str(output, "="); while ((p = strchr(value, '\n')) != NULL) { o_stream_nsend(output, value, p-value); o_stream_nsend(output, SETTING_STREAM_LF_CHAR, 1); value = p+1; } o_stream_nsend_str(output, value); o_stream_nsend_str(output, "\n"); } static int config_connection_request(struct config_connection *conn, const char *const *args) { struct config_export_context *ctx; struct master_service_settings_output output; struct config_filter filter; const char *path, *error, *module, *const *wanted_modules; ARRAY(const char *) modules; bool is_master = FALSE; /* [] */ t_array_init(&modules, 4); i_zero(&filter); for (; *args != NULL; args++) { if (strncmp(*args, "service=", 8) == 0) filter.service = *args + 8; else if (strncmp(*args, "module=", 7) == 0) { module = *args + 7; if (strcmp(module, "master") == 0) is_master = TRUE; array_append(&modules, &module, 1); } else if (strncmp(*args, "lname=", 6) == 0) filter.local_name = *args + 6; else if (strncmp(*args, "lip=", 4) == 0) { if (net_addr2ip(*args + 4, &filter.local_net) == 0) { filter.local_bits = IPADDR_IS_V4(&filter.local_net) ? 32 : 128; } } else if (strncmp(*args, "rip=", 4) == 0) { if (net_addr2ip(*args + 4, &filter.remote_net) == 0) { filter.remote_bits = IPADDR_IS_V4(&filter.remote_net) ? 32 : 128; } } } array_append_zero(&modules); wanted_modules = array_count(&modules) == 1 ? NULL : array_idx(&modules, 0); if (is_master) { /* master reads configuration only when reloading settings */ path = master_service_get_config_path(master_service); if (config_parse_file(path, TRUE, NULL, &error) <= 0) { o_stream_nsend_str(conn->output, t_strconcat("\nERROR ", error, "\n", NULL)); config_connection_destroy(conn); return -1; } } o_stream_cork(conn->output); ctx = config_export_init(wanted_modules, CONFIG_DUMP_SCOPE_SET, 0, config_request_output, conn->output); config_export_by_filter(ctx, &filter); config_export_get_output(ctx, &output); if (output.specific_services != NULL) { const char *const *s; for (s = output.specific_services; *s != NULL; s++) { o_stream_nsend_str(conn->output, t_strdup_printf("service=%s\t", *s)); } } if (output.service_uses_local) o_stream_nsend_str(conn->output, "service-uses-local\t"); if (output.service_uses_remote) o_stream_nsend_str(conn->output, "service-uses-remote\t"); if (output.used_local) o_stream_nsend_str(conn->output, "used-local\t"); if (output.used_remote) o_stream_nsend_str(conn->output, "used-remote\t"); o_stream_nsend_str(conn->output, "\n"); if (config_export_finish(&ctx) < 0) { config_connection_destroy(conn); return -1; } o_stream_nsend_str(conn->output, "\n"); o_stream_uncork(conn->output); return 0; } static void config_connection_input(struct config_connection *conn) { const char *const *args, *line; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Config client connection sent too much data"); config_connection_destroy(conn); return; case -1: config_connection_destroy(conn); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; if (!version_string_verify(line, "config", CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Config client not compatible with this server " "(mixed old and new binaries?)"); config_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((args = config_connection_next_line(conn)) != NULL) { if (args[0] == NULL) continue; if (strcmp(args[0], "REQ") == 0) { if (config_connection_request(conn, args + 1) < 0) break; } } } struct config_connection *config_connection_create(int fd) { struct config_connection *conn; conn = i_new(struct config_connection, 1); conn->fd = fd; conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); o_stream_set_no_error_handling(conn->output, TRUE); conn->io = io_add(fd, IO_READ, config_connection_input, conn); DLLIST_PREPEND(&config_connections, conn); return conn; } void config_connection_destroy(struct config_connection *conn) { DLLIST_REMOVE(&config_connections, conn); io_remove(&conn->io); i_stream_destroy(&conn->input); o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(config conn) failed: %m"); i_free(conn); master_service_client_connection_destroyed(master_service); } void config_connections_destroy_all(void) { while (config_connections != NULL) config_connection_destroy(config_connections); } dovecot-2.2.33.2/src/config/config-parser-private.h0000644000175000017500000000354213165463624016757 00000000000000#ifndef CONFIG_PARSER_PRIVATE_H #define CONFIG_PARSER_PRIVATE_H #include "config-parser.h" #include "config-filter.h" enum config_line_type { CONFIG_LINE_TYPE_SKIP, CONFIG_LINE_TYPE_CONTINUE, CONFIG_LINE_TYPE_ERROR, CONFIG_LINE_TYPE_KEYVALUE, CONFIG_LINE_TYPE_KEYVALUE_QUOTED, CONFIG_LINE_TYPE_KEYFILE, CONFIG_LINE_TYPE_KEYVARIABLE, CONFIG_LINE_TYPE_SECTION_BEGIN, CONFIG_LINE_TYPE_SECTION_END, CONFIG_LINE_TYPE_INCLUDE, CONFIG_LINE_TYPE_INCLUDE_TRY }; struct config_section_stack { struct config_section_stack *prev; const char *key; struct config_filter filter; /* root=NULL-terminated list of parsers */ struct config_module_parser *parsers; size_t pathlen; const char *open_path; unsigned int open_linenum; bool is_filter; }; struct input_stack { struct input_stack *prev; struct istream *input; const char *path; unsigned int linenum; }; struct config_parser_context { pool_t pool; const char *path; const char *const *modules; ARRAY(struct config_filter_parser *) all_parsers; struct config_module_parser *root_parsers; struct config_section_stack *cur_section; struct input_stack *cur_input; string_t *str; size_t pathlen; unsigned int section_counter; const char *error; struct old_set_parser *old; HASH_TABLE(const char *, const char *) seen_settings; struct config_filter_context *filter; unsigned int expand_values:1; unsigned int hide_errors:1; }; extern void (*hook_config_parser_begin)(struct config_parser_context *ctx); extern int (*hook_config_parser_end)(struct config_parser_context *ctx, const char **error_r); int config_apply_line(struct config_parser_context *ctx, const char *key, const char *line, const char *section_name) ATTR_NULL(4); void config_parser_apply_line(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value); #endif dovecot-2.2.33.2/src/config/config-filter.h0000644000175000017500000000365613123174404015273 00000000000000#ifndef CONFIG_FILTER_H #define CONFIG_FILTER_H #include "net.h" struct master_service_settings_output; struct config_filter { const char *service; /* local_name is for TLS SNI requests. both local_name and local_bits can't be set at the same time. */ const char *local_name; /* the hosts are used only in doveconf output */ const char *local_host, *remote_host; struct ip_addr local_net, remote_net; unsigned int local_bits, remote_bits; }; struct config_filter_parser { struct config_filter filter; const char *file_and_line; /* NULL-terminated array of parsers */ struct config_module_parser *parsers; }; ARRAY_DEFINE_TYPE(config_filter_parsers, struct config_filter_parser *); struct config_filter_context *config_filter_init(pool_t pool); void config_filter_deinit(struct config_filter_context **ctx); /* Replace filter's parsers with given parser list. */ void config_filter_add_all(struct config_filter_context *ctx, struct config_filter_parser *const *parsers); /* Build new parsers from all existing ones matching the given filter. */ int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool, const char *const *modules, const struct config_filter *filter, struct config_module_parser **parsers_r, struct master_service_settings_output *output_r, const char **error_r) ATTR_NULL(3); void config_filter_parsers_free(struct config_module_parser *parsers); /* Return a list of filters that are a subset of the given filter. */ struct config_filter_parser *const * config_filter_find_subset(struct config_filter_context *ctx, const struct config_filter *filter); /* Returns TRUE if filter matches mask. */ bool config_filter_match(const struct config_filter *mask, const struct config_filter *filter); /* Returns TRUE if two filters are fully equal. */ bool config_filters_equal(const struct config_filter *f1, const struct config_filter *f2); #endif dovecot-2.2.33.2/src/config/all-settings.c0000644000175000017500000041011113165464056015143 00000000000000#include "lib.h" #include "array.h" #include "ipwd.h" #include "var-expand.h" #include "file-lock.h" #include "fsync-mode.h" #include "hash-format.h" #include "net.h" #include "unichar.h" #include "hash-method.h" #include "settings-parser.h" #include "all-settings.h" #include #include #define CONFIG_BINARY extern buffer_t config_all_services_buf;/* ../../src/lib-storage/mail-storage-settings.h */ extern const struct setting_parser_info mail_user_setting_parser_info; extern const struct setting_parser_info mail_namespace_setting_parser_info; extern const struct setting_parser_info mail_storage_setting_parser_info; /* */ #define MAILBOX_SET_AUTO_NO "no" #define MAILBOX_SET_AUTO_CREATE "create" #define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe" /* */ struct mail_storage_settings { const char *mail_location; const char *mail_attachment_fs; const char *mail_attachment_dir; const char *mail_attachment_hash; uoff_t mail_attachment_min_size; const char *mail_attribute_dict; unsigned int mail_prefetch_count; const char *mail_cache_fields; const char *mail_always_cache_fields; const char *mail_never_cache_fields; const char *mail_server_comment; const char *mail_server_admin; unsigned int mail_cache_min_mail_count; unsigned int mailbox_idle_check_interval; unsigned int mail_max_keyword_length; unsigned int mail_max_lock_timeout; unsigned int mail_temp_scan_interval; unsigned int mail_vsize_bg_after_count; unsigned int mail_sort_max_read_count; bool mail_save_crlf; const char *mail_fsync; bool mmap_disable; bool dotlock_use_excl; bool mail_nfs_storage; bool mail_nfs_index; bool mailbox_list_index; bool mailbox_list_index_very_dirty_syncs; bool mailbox_list_index_include_inbox; bool mail_debug; bool mail_full_filesystem_access; bool maildir_stat_dirs; bool mail_shared_explicit_inbox; const char *lock_method; const char *pop3_uidl_format; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; const char *ssl_crypto_device; enum file_lock_method parsed_lock_method; enum fsync_mode parsed_fsync_mode; }; struct mail_namespace_settings { const char *name; const char *type; const char *separator; const char *prefix; const char *location; const char *alias_for; bool inbox; bool hidden; const char *list; bool subscriptions; bool ignore_on_failure; bool disabled; unsigned int order; ARRAY(struct mailbox_settings *) mailboxes; struct mail_user_settings *user_set; }; struct mailbox_settings { const char *name; const char *autocreate; const char *special_use; const char *driver; const char *comment; unsigned int autoexpunge; unsigned int autoexpunge_max_mails; }; struct mail_user_settings { const char *base_dir; const char *auth_socket_path; const char *mail_temp_dir; const char *mail_uid; const char *mail_gid; const char *mail_home; const char *mail_chroot; const char *mail_access_groups; const char *mail_privileged_group; const char *valid_chroot_dirs; unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; const char *mail_plugins; const char *mail_plugin_dir; const char *mail_log_prefix; ARRAY(struct mail_namespace_settings *) namespaces; ARRAY(const char *) plugin_envs; }; /* ../../src/lib-storage/index/pop3c/pop3c-settings.h */ /* */ enum pop3c_features { POP3C_FEATURE_NO_PIPELINING = 0x1, }; /* */ struct pop3c_settings { const char *pop3c_host; in_port_t pop3c_port; const char *pop3c_user; const char *pop3c_master_user; const char *pop3c_password; const char *pop3c_ssl; bool pop3c_ssl_verify; const char *pop3c_rawlog_dir; bool pop3c_quick_received_date; const char *pop3c_features; enum pop3c_features parsed_features; }; /* ../../src/lib-storage/index/mbox/mbox-settings.h */ struct mbox_settings { const char *mbox_read_locks; const char *mbox_write_locks; unsigned int mbox_lock_timeout; unsigned int mbox_dotlock_change_timeout; uoff_t mbox_min_index_size; bool mbox_dirty_syncs; bool mbox_very_dirty_syncs; bool mbox_lazy_writes; const char *mbox_md5; }; /* ../../src/lib-storage/index/maildir/maildir-settings.h */ struct maildir_settings { bool maildir_copy_with_hardlinks; bool maildir_very_dirty_syncs; bool maildir_broken_filename_sizes; bool maildir_empty_new; }; /* ../../src/lib-storage/index/imapc/imapc-settings.h */ /* */ enum imapc_features { IMAPC_FEATURE_RFC822_SIZE = 0x01, IMAPC_FEATURE_GUID_FORCED = 0x02, IMAPC_FEATURE_FETCH_HEADERS = 0x04, IMAPC_FEATURE_GMAIL_MIGRATION = 0x08, IMAPC_FEATURE_SEARCH = 0x10, IMAPC_FEATURE_ZIMBRA_WORKAROUNDS = 0x20, IMAPC_FEATURE_NO_EXAMINE = 0x40, IMAPC_FEATURE_PROXYAUTH = 0x80, IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS = 0x100, IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS = 0x200, IMAPC_FEATURE_MODSEQ = 0x400, IMAPC_FEATURE_DELAY_LOGIN = 0x800, IMAPC_FEATURE_FETCH_BODYSTRUCTURE = 0x1000, }; /* */ struct imapc_settings { const char *imapc_host; in_port_t imapc_port; const char *imapc_user; const char *imapc_master_user; const char *imapc_password; const char *imapc_sasl_mechanisms; const char *imapc_ssl; bool imapc_ssl_verify; const char *imapc_features; const char *imapc_rawlog_dir; const char *imapc_list_prefix; unsigned int imapc_cmd_timeout; unsigned int imapc_max_idle_time; unsigned int imapc_connection_retry_count; unsigned int imapc_connection_retry_interval; uoff_t imapc_max_line_length; const char *pop3_deleted_flag; enum imapc_features parsed_features; unsigned int throttle_init_msecs; unsigned int throttle_max_msecs; unsigned int throttle_shrink_min_msecs; }; /* ../../src/lib-storage/index/dbox-multi/mdbox-settings.h */ struct mdbox_settings { bool mdbox_preallocate_space; bool mdbox_purge_preserve_alt; uoff_t mdbox_rotate_size; unsigned int mdbox_rotate_interval; }; /* ../../src/lib-settings/settings.h */ #define DEF_STRUCT_STR(name, struct_name) \ { SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, const char *), \ #name, offsetof(struct struct_name, name) } #define DEF_STRUCT_INT(name, struct_name) \ { SET_INT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, unsigned int), \ #name, offsetof(struct struct_name, name) } #define DEF_STRUCT_BOOL(name, struct_name) \ { SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ ((struct struct_name *)0)->name, bool), \ #name, offsetof(struct struct_name, name) } /* ../../src/lib-master/service-settings.h */ /* */ enum service_user_default { SERVICE_USER_DEFAULT_NONE = 0, SERVICE_USER_DEFAULT_INTERNAL, SERVICE_USER_DEFAULT_LOGIN }; enum service_type { SERVICE_TYPE_UNKNOWN, SERVICE_TYPE_LOG, SERVICE_TYPE_ANVIL, SERVICE_TYPE_CONFIG, SERVICE_TYPE_LOGIN, SERVICE_TYPE_STARTUP }; /* */ struct file_listener_settings { const char *path; unsigned int mode; const char *user; const char *group; }; ARRAY_DEFINE_TYPE(file_listener_settings, struct file_listener_settings *); struct inet_listener_settings { const char *name; const char *address; in_port_t port; bool ssl; bool reuse_port; bool haproxy; }; ARRAY_DEFINE_TYPE(inet_listener_settings, struct inet_listener_settings *); struct service_settings { const char *name; const char *protocol; const char *type; const char *executable; const char *user; const char *group; const char *privileged_group; const char *extra_groups; const char *chroot; bool drop_priv_before_exec; unsigned int process_min_avail; unsigned int process_limit; unsigned int client_limit; unsigned int service_count; unsigned int idle_kill; uoff_t vsz_limit; ARRAY_TYPE(file_listener_settings) unix_listeners; ARRAY_TYPE(file_listener_settings) fifo_listeners; ARRAY_TYPE(inet_listener_settings) inet_listeners; /* internal to master: */ struct master_settings *master_set; enum service_type parsed_type; enum service_user_default user_default; unsigned int login_dump_core:1; /* -- flags that can be set internally -- */ /* process_limit must not be higher than 1 */ unsigned int process_limit_1:1; }; ARRAY_DEFINE_TYPE(service_settings, struct service_settings *); /* ../../src/lib-master/master-service-ssl-settings.h */ extern const struct setting_parser_info master_service_ssl_setting_parser_info; struct master_service_ssl_settings { const char *ssl; const char *ssl_ca; const char *ssl_cert; const char *ssl_alt_cert; const char *ssl_key; const char *ssl_alt_key; const char *ssl_key_password; const char *ssl_cipher_list; const char *ssl_protocols; const char *ssl_cert_username_field; const char *ssl_crypto_device; const char *ssl_options; bool ssl_verify_client_cert; bool ssl_require_crl; bool verbose_ssl; bool ssl_prefer_server_ciphers; /* These are derived from ssl_options, not set directly */ struct { bool compression; bool tickets; } parsed_opts; }; /* ../../src/lib-master/master-service-settings.h */ extern const struct setting_parser_info master_service_setting_parser_info; struct master_service_settings { const char *base_dir; const char *state_dir; const char *log_path; const char *info_log_path; const char *debug_log_path; const char *log_timestamp; const char *syslog_facility; const char *import_environment; uoff_t config_cache_size; bool version_ignore; bool shutdown_clients; bool verbose_proctitle; const char *haproxy_trusted_networks; unsigned int haproxy_timeout; }; /* ../../src/lib-lda/lda-settings.h */ extern const struct setting_parser_info lda_setting_parser_info; struct lda_settings { const char *postmaster_address; const char *hostname; const char *submission_host; const char *sendmail_path; const char *rejection_subject; const char *rejection_reason; const char *deliver_log_format; const char *recipient_delimiter; const char *lda_original_recipient_header; bool quota_full_tempfail; bool lda_mailbox_autocreate; bool lda_mailbox_autosubscribe; }; /* ../../src/lib-dict/dict-sql-settings.h */ struct dict_sql_settings { const char *connect; unsigned int max_field_count; ARRAY(struct dict_sql_map) maps; }; /* ../../src/lib-storage/mail-storage-settings.c */ extern const struct setting_parser_info mailbox_setting_parser_info; extern const struct setting_parser_info mail_namespace_setting_parser_info; /* */ static bool mail_storage_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct mail_storage_settings *set = _set; struct hash_format *format; const char *p, *error; bool uidl_format_ok; char c; if (set->mailbox_idle_check_interval == 0) { *error_r = "mailbox_idle_check_interval must not be 0"; return FALSE; } if (strcmp(set->mail_fsync, "optimized") == 0) set->parsed_fsync_mode = FSYNC_MODE_OPTIMIZED; else if (strcmp(set->mail_fsync, "never") == 0) set->parsed_fsync_mode = FSYNC_MODE_NEVER; else if (strcmp(set->mail_fsync, "always") == 0) set->parsed_fsync_mode = FSYNC_MODE_ALWAYS; else { *error_r = t_strdup_printf("Unknown mail_fsync: %s", set->mail_fsync); return FALSE; } if (set->mail_nfs_index && !set->mmap_disable) { *error_r = "mail_nfs_index=yes requires mmap_disable=yes"; return FALSE; } if (set->mail_nfs_index && set->parsed_fsync_mode != FSYNC_MODE_ALWAYS) { *error_r = "mail_nfs_index=yes requires mail_fsync=always"; return FALSE; } if (!file_lock_method_parse(set->lock_method, &set->parsed_lock_method)) { *error_r = t_strdup_printf("Unknown lock_method: %s", set->lock_method); return FALSE; } uidl_format_ok = FALSE; for (p = set->pop3_uidl_format; *p != '\0'; p++) { if (p[0] != '%' || p[1] == '\0') continue; c = var_get_key(++p); switch (c) { case 'v': case 'u': case 'm': case 'f': case 'g': uidl_format_ok = TRUE; break; case '%': break; default: *error_r = t_strdup_printf( "Unknown pop3_uidl_format variable: %%%c", c); return FALSE; } } if (!uidl_format_ok) { *error_r = "pop3_uidl_format setting doesn't contain any " "%% variables."; return FALSE; } if (strchr(set->mail_attachment_hash, '/') != NULL) { *error_r = "mail_attachment_hash setting " "must not contain '/' characters"; return FALSE; } if (hash_format_init(set->mail_attachment_hash, &format, &error) < 0) { *error_r = t_strconcat("Invalid mail_attachment_hash setting: ", error, NULL); return FALSE; } if (strchr(set->mail_attachment_hash, '-') != NULL) { *error_r = "mail_attachment_hash setting " "must not contain '-' characters"; return FALSE; } hash_format_deinit_free(&format); #ifndef CONFIG_BINARY if (*set->ssl_client_ca_dir != '\0' && access(set->ssl_client_ca_dir, X_OK) < 0) { *error_r = t_strdup_printf( "ssl_client_ca_dir: access(%s) failed: %m", set->ssl_client_ca_dir); return FALSE; } #endif // FIXME: check set->mail_server_admin syntax (RFC 5464, Section 6.2.2) return TRUE; } static bool namespace_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct mail_namespace_settings *ns = _set; struct mail_namespace_settings *const *namespaces; const char *name; unsigned int i, count; name = ns->prefix != NULL ? ns->prefix : ""; if (ns->separator[0] != '\0' && ns->separator[1] != '\0') { *error_r = t_strdup_printf("Namespace '%s': " "Hierarchy separator must be only one character long", name); return FALSE; } if (!uni_utf8_str_is_valid(name)) { *error_r = t_strdup_printf("Namespace prefix not valid UTF8: %s", name); return FALSE; } if (ns->alias_for != NULL && !ns->disabled) { if (array_is_created(&ns->user_set->namespaces)) { namespaces = array_get(&ns->user_set->namespaces, &count); } else { namespaces = NULL; count = 0; } for (i = 0; i < count; i++) { if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0) break; } if (i == count) { *error_r = t_strdup_printf( "Namespace '%s': alias_for points to " "unknown namespace: %s", name, ns->alias_for); return FALSE; } if (namespaces[i]->alias_for != NULL) { *error_r = t_strdup_printf( "Namespace '%s': alias_for chaining isn't " "allowed: %s -> %s", name, ns->alias_for, namespaces[i]->alias_for); return FALSE; } } return TRUE; } static bool mailbox_special_use_exists(const char *name) { if (name[0] != '\\') return FALSE; name++; if (strcasecmp(name, "All") == 0) return TRUE; if (strcasecmp(name, "Archive") == 0) return TRUE; if (strcasecmp(name, "Drafts") == 0) return TRUE; if (strcasecmp(name, "Flagged") == 0) return TRUE; if (strcasecmp(name, "Junk") == 0) return TRUE; if (strcasecmp(name, "Sent") == 0) return TRUE; if (strcasecmp(name, "Trash") == 0) return TRUE; return FALSE; } static bool mailbox_special_use_check(struct mailbox_settings *set, pool_t pool, const char **error_r) { const char *const *uses, *str; unsigned int i; uses = t_strsplit_spaces(set->special_use, " "); for (i = 0; uses[i] != NULL; i++) { if (!mailbox_special_use_exists(uses[i])) { *error_r = t_strdup_printf( "mailbox %s: unknown special_use: %s", set->name, uses[i]); return FALSE; } } /* make sure there are no extra spaces */ str = t_strarray_join(uses, " "); if (strcmp(str, set->special_use) != 0) set->special_use = p_strdup(pool, str); return TRUE; } static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r) { struct mailbox_settings *set = _set; if (!uni_utf8_str_is_valid(set->name)) { *error_r = t_strdup_printf("mailbox %s: name isn't valid UTF-8", set->name); return FALSE; } if (*set->special_use != '\0') { if (!mailbox_special_use_check(set, pool, error_r)) return FALSE; } return TRUE; } static bool mail_user_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r ATTR_UNUSED) { struct mail_user_settings *set = _set; #ifndef CONFIG_BINARY fix_base_path(set, pool, &set->auth_socket_path); #else if (*set->mail_plugins != '\0' && access(set->mail_plugin_dir, R_OK | X_OK) < 0) { *error_r = t_strdup_printf( "mail_plugin_dir: access(%s) failed: %m", set->mail_plugin_dir); return FALSE; } #endif return TRUE; } /* */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mail_storage_settings, name), NULL } static const struct setting_define mail_storage_setting_defines[] = { DEF(SET_STR_VARS, mail_location), { SET_ALIAS, "mail", 0, NULL }, DEF(SET_STR_VARS, mail_attachment_fs), DEF(SET_STR_VARS, mail_attachment_dir), DEF(SET_STR, mail_attachment_hash), DEF(SET_SIZE, mail_attachment_min_size), DEF(SET_STR_VARS, mail_attribute_dict), DEF(SET_UINT, mail_prefetch_count), DEF(SET_STR, mail_cache_fields), DEF(SET_STR, mail_always_cache_fields), DEF(SET_STR, mail_never_cache_fields), DEF(SET_STR, mail_server_comment), DEF(SET_STR, mail_server_admin), DEF(SET_UINT, mail_cache_min_mail_count), DEF(SET_TIME, mailbox_idle_check_interval), DEF(SET_UINT, mail_max_keyword_length), DEF(SET_TIME, mail_max_lock_timeout), DEF(SET_TIME, mail_temp_scan_interval), DEF(SET_UINT, mail_vsize_bg_after_count), DEF(SET_UINT, mail_sort_max_read_count), DEF(SET_BOOL, mail_save_crlf), DEF(SET_ENUM, mail_fsync), DEF(SET_BOOL, mmap_disable), DEF(SET_BOOL, dotlock_use_excl), DEF(SET_BOOL, mail_nfs_storage), DEF(SET_BOOL, mail_nfs_index), DEF(SET_BOOL, mailbox_list_index), DEF(SET_BOOL, mailbox_list_index_very_dirty_syncs), DEF(SET_BOOL, mailbox_list_index_include_inbox), DEF(SET_BOOL, mail_debug), DEF(SET_BOOL, mail_full_filesystem_access), DEF(SET_BOOL, maildir_stat_dirs), DEF(SET_BOOL, mail_shared_explicit_inbox), DEF(SET_ENUM, lock_method), DEF(SET_STR, pop3_uidl_format), DEF(SET_STR, ssl_client_ca_dir), DEF(SET_STR, ssl_client_ca_file), DEF(SET_STR, ssl_crypto_device), SETTING_DEFINE_LIST_END }; const struct mail_storage_settings mail_storage_default_settings = { .mail_location = "", .mail_attachment_fs = "sis posix", .mail_attachment_dir = "", .mail_attachment_hash = "%{sha1}", .mail_attachment_min_size = 1024*128, .mail_attribute_dict = "", .mail_prefetch_count = 0, .mail_cache_fields = "flags", .mail_always_cache_fields = "", .mail_never_cache_fields = "imap.envelope", .mail_server_comment = "", .mail_server_admin = "", .mail_cache_min_mail_count = 0, .mailbox_idle_check_interval = 30, .mail_max_keyword_length = 50, .mail_max_lock_timeout = 0, .mail_temp_scan_interval = 7*24*60*60, .mail_vsize_bg_after_count = 0, .mail_sort_max_read_count = 0, .mail_save_crlf = FALSE, .mail_fsync = "optimized:never:always", .mmap_disable = FALSE, .dotlock_use_excl = TRUE, .mail_nfs_storage = FALSE, .mail_nfs_index = FALSE, .mailbox_list_index = FALSE, .mailbox_list_index_very_dirty_syncs = FALSE, .mailbox_list_index_include_inbox = FALSE, .mail_debug = FALSE, .mail_full_filesystem_access = FALSE, .maildir_stat_dirs = FALSE, .mail_shared_explicit_inbox = FALSE, .lock_method = "fcntl:flock:dotlock", .pop3_uidl_format = "%08Xu%08Xv", .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .ssl_crypto_device = "" }; const struct setting_parser_info mail_storage_setting_parser_info = { .module_name = "mail", .defines = mail_storage_setting_defines, .defaults = &mail_storage_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mail_storage_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = mail_storage_settings_check }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mailbox_settings, name), NULL } static const struct setting_define mailbox_setting_defines[] = { DEF(SET_STR, name), { SET_ENUM, "auto", offsetof(struct mailbox_settings, autocreate), NULL } , DEF(SET_STR, special_use), DEF(SET_STR, driver), DEF(SET_STR, comment), DEF(SET_TIME, autoexpunge), DEF(SET_UINT, autoexpunge_max_mails), SETTING_DEFINE_LIST_END }; const struct mailbox_settings mailbox_default_settings = { .name = "", .autocreate = MAILBOX_SET_AUTO_NO":" MAILBOX_SET_AUTO_CREATE":" MAILBOX_SET_AUTO_SUBSCRIBE, .special_use = "", .driver = "", .comment = "", .autoexpunge = 0, .autoexpunge_max_mails = 0 }; const struct setting_parser_info mailbox_setting_parser_info = { .defines = mailbox_setting_defines, .defaults = &mailbox_default_settings, .type_offset = offsetof(struct mailbox_settings, name), .struct_size = sizeof(struct mailbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = mailbox_settings_check }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct mail_namespace_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, \ offsetof(struct mail_namespace_settings, field), defines } static const struct setting_define mail_namespace_setting_defines[] = { DEF(SET_STR, name), DEF(SET_ENUM, type), DEF(SET_STR, separator), DEF(SET_STR_VARS, prefix), DEF(SET_STR_VARS, location), { SET_ALIAS, "mail", 0, NULL }, { SET_ALIAS, "mail_location", 0, NULL }, DEF(SET_STR_VARS, alias_for), DEF(SET_BOOL, inbox), DEF(SET_BOOL, hidden), DEF(SET_ENUM, list), DEF(SET_BOOL, subscriptions), DEF(SET_BOOL, ignore_on_failure), DEF(SET_BOOL, disabled), DEF(SET_UINT, order), DEFLIST_UNIQUE(mailboxes, "mailbox", &mailbox_setting_parser_info), SETTING_DEFINE_LIST_END }; const struct mail_namespace_settings mail_namespace_default_settings = { .name = "", .type = "private:shared:public", .separator = "", .prefix = "", .location = "", .alias_for = NULL, .inbox = FALSE, .hidden = FALSE, .list = "yes:no:children", .subscriptions = TRUE, .ignore_on_failure = FALSE, .disabled = FALSE, .order = 0, .mailboxes = ARRAY_INIT }; const struct setting_parser_info mail_namespace_setting_parser_info = { .defines = mail_namespace_setting_defines, .defaults = &mail_namespace_default_settings, .type_offset = offsetof(struct mail_namespace_settings, name), .struct_size = sizeof(struct mail_namespace_settings), .parent_offset = offsetof(struct mail_namespace_settings, user_set), .parent = &mail_user_setting_parser_info, .check_func = namespace_settings_check }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct mail_user_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, \ offsetof(struct mail_user_settings, field), defines } static const struct setting_define mail_user_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, auth_socket_path), DEF(SET_STR_VARS, mail_temp_dir), DEF(SET_STR, mail_uid), DEF(SET_STR, mail_gid), DEF(SET_STR_VARS, mail_home), DEF(SET_STR_VARS, mail_chroot), DEF(SET_STR, mail_access_groups), DEF(SET_STR, mail_privileged_group), DEF(SET_STR, valid_chroot_dirs), DEF(SET_UINT, first_valid_uid), DEF(SET_UINT, last_valid_uid), DEF(SET_UINT, first_valid_gid), DEF(SET_UINT, last_valid_gid), DEF(SET_STR, mail_plugins), DEF(SET_STR, mail_plugin_dir), DEF(SET_STR, mail_log_prefix), DEFLIST_UNIQUE(namespaces, "namespace", &mail_namespace_setting_parser_info), { SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; static const struct mail_user_settings mail_user_default_settings = { .base_dir = PKG_RUNDIR, .auth_socket_path = "auth-userdb", .mail_temp_dir = "/tmp", .mail_uid = "", .mail_gid = "", .mail_home = "", .mail_chroot = "", .mail_access_groups = "", .mail_privileged_group = "", .valid_chroot_dirs = "", .first_valid_uid = 500, .last_valid_uid = 0, .first_valid_gid = 1, .last_valid_gid = 0, .mail_plugins = "", .mail_plugin_dir = MODULEDIR, .mail_log_prefix = "%s(%u): ", .namespaces = ARRAY_INIT, .plugin_envs = ARRAY_INIT }; const struct setting_parser_info mail_user_setting_parser_info = { .module_name = "mail", .defines = mail_user_setting_defines, .defaults = &mail_user_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mail_user_settings), .parent_offset = (size_t)-1, .check_func = mail_user_settings_check }; /* ../../src/lib-storage/index/pop3c/pop3c-settings.c */ /* */ struct pop3c_feature_list { const char *name; enum pop3c_features num; }; static const struct pop3c_feature_list pop3c_feature_list[] = { { "no-pipelining", POP3C_FEATURE_NO_PIPELINING }, { NULL, 0 } }; static int pop3c_settings_parse_features(struct pop3c_settings *set, const char **error_r) { enum pop3c_features features = 0; const struct pop3c_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->pop3c_features, " ,"); for (; *str != NULL; str++) { list = pop3c_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("pop3c_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool pop3c_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct pop3c_settings *set = _set; if (pop3c_settings_parse_features(set, error_r) < 0) return FALSE; return TRUE; } /* */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct pop3c_settings, name), NULL } static const struct setting_define pop3c_setting_defines[] = { DEF(SET_STR, pop3c_host), DEF(SET_IN_PORT, pop3c_port), DEF(SET_STR_VARS, pop3c_user), DEF(SET_STR_VARS, pop3c_master_user), DEF(SET_STR, pop3c_password), DEF(SET_ENUM, pop3c_ssl), DEF(SET_BOOL, pop3c_ssl_verify), DEF(SET_STR, pop3c_rawlog_dir), DEF(SET_BOOL, pop3c_quick_received_date), DEF(SET_STR, pop3c_features), SETTING_DEFINE_LIST_END }; static const struct pop3c_settings pop3c_default_settings = { .pop3c_host = "", .pop3c_port = 110, .pop3c_user = "%u", .pop3c_master_user = "", .pop3c_password = "", .pop3c_ssl = "no:pop3s:starttls", .pop3c_ssl_verify = TRUE, .pop3c_rawlog_dir = "", .pop3c_quick_received_date = FALSE, .pop3c_features = "" }; static const struct setting_parser_info pop3c_setting_parser_info = { .module_name = "pop3c", .defines = pop3c_setting_defines, .defaults = &pop3c_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct pop3c_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = pop3c_settings_check }; /* ../../src/lib-storage/index/mbox/mbox-settings.c */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mbox_settings, name), NULL } static const struct setting_define mbox_setting_defines[] = { DEF(SET_STR, mbox_read_locks), DEF(SET_STR, mbox_write_locks), DEF(SET_TIME, mbox_lock_timeout), DEF(SET_TIME, mbox_dotlock_change_timeout), DEF(SET_SIZE, mbox_min_index_size), DEF(SET_BOOL, mbox_dirty_syncs), DEF(SET_BOOL, mbox_very_dirty_syncs), DEF(SET_BOOL, mbox_lazy_writes), DEF(SET_ENUM, mbox_md5), SETTING_DEFINE_LIST_END }; static const struct mbox_settings mbox_default_settings = { .mbox_read_locks = "fcntl", .mbox_write_locks = "dotlock fcntl", .mbox_lock_timeout = 5*60, .mbox_dotlock_change_timeout = 2*60, .mbox_min_index_size = 0, .mbox_dirty_syncs = TRUE, .mbox_very_dirty_syncs = FALSE, .mbox_lazy_writes = TRUE, .mbox_md5 = "apop3d:all" }; static const struct setting_parser_info mbox_setting_parser_info = { .module_name = "mbox", .defines = mbox_setting_defines, .defaults = &mbox_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; /* ../../src/lib-storage/index/maildir/maildir-settings.c */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct maildir_settings, name), NULL } static const struct setting_define maildir_setting_defines[] = { DEF(SET_BOOL, maildir_copy_with_hardlinks), DEF(SET_BOOL, maildir_very_dirty_syncs), DEF(SET_BOOL, maildir_broken_filename_sizes), DEF(SET_BOOL, maildir_empty_new), SETTING_DEFINE_LIST_END }; static const struct maildir_settings maildir_default_settings = { .maildir_copy_with_hardlinks = TRUE, .maildir_very_dirty_syncs = FALSE, .maildir_broken_filename_sizes = FALSE, .maildir_empty_new = FALSE }; static const struct setting_parser_info maildir_setting_parser_info = { .module_name = "maildir", .defines = maildir_setting_defines, .defaults = &maildir_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct maildir_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; /* ../../src/lib-storage/index/imapc/imapc-settings.c */ /* */ struct imapc_feature_list { const char *name; enum imapc_features num; }; static const struct imapc_feature_list imapc_feature_list[] = { { "rfc822.size", IMAPC_FEATURE_RFC822_SIZE }, { "guid-forced", IMAPC_FEATURE_GUID_FORCED }, { "fetch-headers", IMAPC_FEATURE_FETCH_HEADERS }, { "gmail-migration", IMAPC_FEATURE_GMAIL_MIGRATION }, { "search", IMAPC_FEATURE_SEARCH }, { "zimbra-workarounds", IMAPC_FEATURE_ZIMBRA_WORKAROUNDS }, { "no-examine", IMAPC_FEATURE_NO_EXAMINE }, { "proxyauth", IMAPC_FEATURE_PROXYAUTH }, { "fetch-msn-workarounds", IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS }, { "fetch-fix-broken-mails", IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS }, { "modseq", IMAPC_FEATURE_MODSEQ }, { "delay-login", IMAPC_FEATURE_DELAY_LOGIN }, { "fetch-bodystructure", IMAPC_FEATURE_FETCH_BODYSTRUCTURE }, { NULL, 0 } }; static int imapc_settings_parse_throttle(struct imapc_settings *set, const char *throttle_str, const char **error_r) { const char *const *tmp; tmp = t_strsplit(throttle_str, ":"); if (str_array_length(tmp) != 3 || str_to_uint(tmp[0], &set->throttle_init_msecs) < 0 || str_to_uint(tmp[1], &set->throttle_max_msecs) < 0 || str_to_uint(tmp[2], &set->throttle_shrink_min_msecs) < 0) { *error_r = "imapc_features: Invalid throttle settings"; return -1; } return 0; } static int imapc_settings_parse_features(struct imapc_settings *set, const char **error_r) { enum imapc_features features = 0; const struct imapc_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->imapc_features, " ,"); for (; *str != NULL; str++) { list = imapc_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (strncasecmp(*str, "throttle:", 9) == 0) { if (imapc_settings_parse_throttle(set, *str + 9, error_r) < 0) return -1; continue; } if (list->name == NULL) { *error_r = t_strdup_printf("imapc_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool imapc_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct imapc_settings *set = _set; if (set->imapc_max_idle_time == 0) { *error_r = "imapc_max_idle_time must not be 0"; return FALSE; } if (imapc_settings_parse_features(set, error_r) < 0) return FALSE; return TRUE; } #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imapc_settings, name), NULL } static const struct setting_define imapc_setting_defines[] = { DEF(SET_STR, imapc_host), DEF(SET_IN_PORT, imapc_port), DEF(SET_STR_VARS, imapc_user), DEF(SET_STR_VARS, imapc_master_user), DEF(SET_STR, imapc_password), DEF(SET_STR, imapc_sasl_mechanisms), DEF(SET_ENUM, imapc_ssl), DEF(SET_BOOL, imapc_ssl_verify), DEF(SET_STR, imapc_features), DEF(SET_STR, imapc_rawlog_dir), DEF(SET_STR, imapc_list_prefix), DEF(SET_TIME, imapc_cmd_timeout), DEF(SET_TIME, imapc_max_idle_time), DEF(SET_UINT, imapc_connection_retry_count), DEF(SET_TIME, imapc_connection_retry_interval), DEF(SET_SIZE, imapc_max_line_length), DEF(SET_STR, pop3_deleted_flag), SETTING_DEFINE_LIST_END }; static const struct imapc_settings imapc_default_settings = { .imapc_host = "", .imapc_port = 143, .imapc_user = "", .imapc_master_user = "", .imapc_password = "", .imapc_sasl_mechanisms = "", .imapc_ssl = "no:imaps:starttls", .imapc_ssl_verify = TRUE, .imapc_features = "", .imapc_rawlog_dir = "", .imapc_list_prefix = "", .imapc_cmd_timeout = 5*60, .imapc_max_idle_time = 60*29, .imapc_connection_retry_count = 1, .imapc_connection_retry_interval = 1, .imapc_max_line_length = 0, .pop3_deleted_flag = "" }; static const struct setting_parser_info imapc_setting_parser_info = { .module_name = "imapc", .defines = imapc_setting_defines, .defaults = &imapc_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imapc_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info, .check_func = imapc_settings_check }; /* ../../src/lib-storage/index/dbox-multi/mdbox-settings.c */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct mdbox_settings, name), NULL } static const struct setting_define mdbox_setting_defines[] = { DEF(SET_BOOL, mdbox_preallocate_space), DEF(SET_BOOL, mdbox_purge_preserve_alt), DEF(SET_SIZE, mdbox_rotate_size), DEF(SET_TIME, mdbox_rotate_interval), SETTING_DEFINE_LIST_END }; static const struct mdbox_settings mdbox_default_settings = { .mdbox_preallocate_space = FALSE, .mdbox_purge_preserve_alt = FALSE, .mdbox_rotate_size = 2*1024*1024, .mdbox_rotate_interval = 0 }; static const struct setting_parser_info mdbox_setting_parser_info = { .module_name = "mdbox", .defines = mdbox_setting_defines, .defaults = &mdbox_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct mdbox_settings), .parent_offset = (size_t)-1, .parent = &mail_user_setting_parser_info }; /* ../../src/lib-settings/settings.c */ /* ../../src/lib-lda/lda-settings.c */ #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct lda_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct lda_settings, field), defines } static const struct setting_define lda_setting_defines[] = { DEF(SET_STR_VARS, postmaster_address), DEF(SET_STR, hostname), DEF(SET_STR_VARS, submission_host), DEF(SET_STR_VARS, sendmail_path), DEF(SET_STR, rejection_subject), DEF(SET_STR, rejection_reason), DEF(SET_STR, deliver_log_format), DEF(SET_STR, recipient_delimiter), DEF(SET_STR, lda_original_recipient_header), DEF(SET_BOOL, quota_full_tempfail), DEF(SET_BOOL, lda_mailbox_autocreate), DEF(SET_BOOL, lda_mailbox_autosubscribe), SETTING_DEFINE_LIST_END }; static const struct lda_settings lda_default_settings = { .postmaster_address = "postmaster@%d", .hostname = "", .submission_host = "", .sendmail_path = "/usr/sbin/sendmail", .rejection_subject = "Rejected: %s", .rejection_reason = "Your message to <%t> was automatically rejected:%n%r", .deliver_log_format = "msgid=%m: %$", .recipient_delimiter = "+", .lda_original_recipient_header = "", .quota_full_tempfail = FALSE, .lda_mailbox_autocreate = FALSE, .lda_mailbox_autosubscribe = FALSE }; static const struct setting_parser_info *lda_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info lda_setting_parser_info = { .module_name = "lda", .defines = lda_setting_defines, .defaults = &lda_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct lda_settings), .parent_offset = (size_t)-1, #ifndef CONFIG_BINARY .check_func = lda_settings_check, #endif .dependencies = lda_setting_dependencies }; /* ../../src/lib-dict/dict-sql-settings.c */ #define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map) /* ../../src/stats/stats-settings.h */ extern const struct setting_parser_info stats_setting_parser_info; struct stats_settings { uoff_t memory_limit; unsigned int command_min_time; unsigned int session_min_time; unsigned int user_min_time; unsigned int domain_min_time; unsigned int ip_min_time; unsigned int carbon_interval; const char *carbon_server; const char *carbon_name; }; /* ../../src/ssl-params/ssl-params-settings.h */ struct ssl_params_settings { unsigned int ssl_parameters_regenerate; unsigned int ssl_dh_parameters_length; }; /* ../../src/replication/replicator/replicator-settings.h */ extern const struct setting_parser_info replicator_setting_parser_info; struct replicator_settings { const char *auth_socket_path; const char *doveadm_socket_path; const char *replication_dsync_parameters; unsigned int replication_full_sync_interval; unsigned int replication_max_conns; }; /* ../../src/replication/aggregator/aggregator-settings.h */ extern const struct setting_parser_info aggregator_setting_parser_info; struct aggregator_settings { const char *replicator_host; in_port_t replicator_port; }; /* ../../src/pop3/pop3-settings.h */ extern const struct setting_parser_info pop3_setting_parser_info; /* */ enum pop3_client_workarounds { WORKAROUND_OUTLOOK_NO_NULS = 0x01, WORKAROUND_OE_NS_EOH = 0x02 }; enum pop3_delete_type { POP3_DELETE_TYPE_EXPUNGE = 0, POP3_DELETE_TYPE_FLAG, }; /* */ struct pop3_settings { bool verbose_proctitle; const char *rawlog_dir; /* pop3: */ bool pop3_no_flag_updates; bool pop3_enable_last; bool pop3_reuse_xuidl; bool pop3_save_uidl; bool pop3_lock_session; bool pop3_fast_size_lookups; const char *pop3_client_workarounds; const char *pop3_logout_format; const char *pop3_uidl_duplicates; const char *pop3_deleted_flag; const char *pop3_delete_type; enum pop3_client_workarounds parsed_workarounds; enum pop3_delete_type parsed_delete_type; }; /* ../../src/pop3-login/pop3-login-settings.h */ extern const struct setting_parser_info *pop3_login_setting_roots[]; /* ../../src/plugins/quota/quota-status-settings.h */ extern const struct setting_parser_info quota_status_setting_parser_info; struct quota_status_settings { char *recipient_delimiter; }; /* ../../src/plugins/mail-crypt/fs-crypt-settings.h */ extern const struct setting_parser_info fs_crypt_setting_parser_info; struct fs_crypt_settings { ARRAY(const char *) plugin_envs; }; /* ../../src/plugins/dict-ldap/dict-ldap-settings.h */ struct dict_ldap_settings { const char *uri; const char *bind_dn; const char *password; unsigned int timeout; unsigned int max_idle_time; unsigned int debug; unsigned int max_attribute_count; bool require_ssl; bool start_tls; ARRAY(struct dict_ldap_map) maps; }; /* ../../src/master/master-settings.h */ extern const struct setting_parser_info master_setting_parser_info; struct master_settings { const char *base_dir; const char *state_dir; const char *libexec_dir; const char *instance_name; const char *protocols; const char *listen; const char *ssl; const char *default_internal_user; const char *default_login_user; unsigned int default_process_limit; unsigned int default_client_limit; unsigned int default_idle_kill; uoff_t default_vsz_limit; bool version_ignore; unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; ARRAY_TYPE(service_settings) services; char **protocols_split; }; /* ../../src/login-common/login-settings.h */ extern const struct setting_parser_info **login_set_roots; extern const struct setting_parser_info login_setting_parser_info; struct login_settings { const char *login_trusted_networks; const char *login_source_ips; const char *login_greeting; const char *login_log_format_elements, *login_log_format; const char *login_access_sockets; const char *login_plugin_dir; const char *login_plugins; unsigned int login_proxy_max_disconnect_delay; const char *director_username_hash; const char *ssl_client_cert; const char *ssl_client_key; bool ssl_require_crl; bool auth_ssl_require_client_cert; bool auth_ssl_username_from_cert; bool disable_plaintext_auth; bool auth_verbose; bool auth_debug; bool auth_debug_passwords; bool verbose_proctitle; unsigned int mail_max_userip_connections; /* generated: */ char *const *log_format_elements_split; }; /* ../../src/lmtp/lmtp-settings.h */ extern const struct setting_parser_info lmtp_setting_parser_info; /* */ enum lmtp_hdr_delivery_address { LMTP_HDR_DELIVERY_ADDRESS_NONE, LMTP_HDR_DELIVERY_ADDRESS_FINAL, LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL }; /* */ struct lmtp_settings { bool lmtp_proxy; bool lmtp_save_to_detail_mailbox; bool lmtp_rcpt_check_quota; unsigned int lmtp_user_concurrency_limit; const char *lmtp_address_translate; const char *lmtp_hdr_delivery_address; const char *login_greeting; const char *login_trusted_networks; enum lmtp_hdr_delivery_address parsed_lmtp_hdr_delivery_address; }; /* ../../src/imap/imap-settings.h */ extern const struct setting_parser_info imap_setting_parser_info; /* */ enum imap_client_workarounds { WORKAROUND_DELAY_NEWMAIL = 0x01, WORKAROUND_TB_EXTRA_MAILBOX_SEP = 0x08, WORKAROUND_TB_LSUB_FLAGS = 0x10 }; enum imap_client_fetch_failure { IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY, IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER, IMAP_CLIENT_FETCH_FAILURE_NO_AFTER, }; /* */ struct imap_settings { bool verbose_proctitle; const char *rawlog_dir; /* imap: */ uoff_t imap_max_line_length; unsigned int imap_idle_notify_interval; const char *imap_capability; const char *imap_client_workarounds; const char *imap_logout_format; const char *imap_id_send; const char *imap_id_log; const char *imap_fetch_failure; bool imap_metadata; unsigned int imap_hibernate_timeout; /* imap urlauth: */ const char *imap_urlauth_host; in_port_t imap_urlauth_port; enum imap_client_workarounds parsed_workarounds; enum imap_client_fetch_failure parsed_fetch_failure; }; /* ../../src/imap-urlauth/imap-urlauth-worker-settings.h */ extern const struct setting_parser_info imap_urlauth_worker_setting_parser_info; struct imap_urlauth_worker_settings { bool verbose_proctitle; /* imap_urlauth: */ const char *imap_urlauth_host; in_port_t imap_urlauth_port; }; /* ../../src/imap-urlauth/imap-urlauth-settings.h */ extern const struct setting_parser_info imap_urlauth_setting_parser_info; struct imap_urlauth_settings { const char *base_dir; bool mail_debug; bool verbose_proctitle; /* imap_urlauth: */ const char *imap_urlauth_logout_format; const char *imap_urlauth_submit_user; const char *imap_urlauth_stream_user; }; /* ../../src/imap-urlauth/imap-urlauth-login-settings.h */ extern const struct setting_parser_info *imap_urlauth_login_setting_roots[]; /* ../../src/imap-login/imap-login-settings.h */ extern const struct setting_parser_info *imap_login_setting_roots[]; struct imap_login_settings { const char *imap_capability; const char *imap_id_send; const char *imap_id_log; bool imap_id_retain; }; /* ../../src/doveadm/doveadm-settings.h */ extern const struct setting_parser_info doveadm_setting_parser_info; /* */ enum dsync_features { DSYNC_FEATURE_EMPTY_HDR_WORKAROUND = 0x1, }; /* */ struct doveadm_settings { const char *base_dir; const char *libexec_dir; const char *mail_plugins; const char *mail_plugin_dir; bool auth_debug; const char *auth_socket_path; const char *doveadm_socket_path; unsigned int doveadm_worker_count; in_port_t doveadm_port; const char *doveadm_username; const char *doveadm_password; const char *doveadm_allowed_commands; const char *dsync_alt_char; const char *dsync_remote_cmd; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; const char *director_username_hash; const char *doveadm_api_key; const char *dsync_features; const char *dsync_hashed_headers; unsigned int dsync_commit_msgs_interval; const char *doveadm_http_rawlog_dir; enum dsync_features parsed_features; ARRAY(const char *) plugin_envs; }; /* ../../src/director/director-settings.h */ extern const struct setting_parser_info director_setting_parser_info; struct director_settings { const char *master_user_separator; const char *director_servers; const char *director_mail_servers; const char *director_username_hash; const char *director_flush_socket; unsigned int director_user_expire; unsigned int director_user_kick_delay; in_port_t director_doveadm_port; bool director_consistent_hashing; }; /* ../../src/dict/dict-settings.h */ extern const struct setting_parser_info dict_setting_parser_info; struct dict_server_settings { const char *base_dir; bool verbose_proctitle; const char *dict_db_config; ARRAY(const char *) dicts; }; /* ../../src/auth/auth-settings.h */ extern const struct setting_parser_info auth_setting_parser_info; struct auth_passdb_settings { const char *name; const char *driver; const char *args; const char *default_fields; const char *override_fields; const char *mechanisms; const char *username_filter; const char *skip; const char *result_success; const char *result_failure; const char *result_internalfail; bool deny; bool pass; /* deprecated, use result_success=continue instead */ bool master; const char *auth_verbose; }; struct auth_userdb_settings { const char *name; const char *driver; const char *args; const char *default_fields; const char *override_fields; const char *skip; const char *result_success; const char *result_failure; const char *result_internalfail; const char *auth_verbose; }; struct auth_settings { const char *mechanisms; const char *realms; const char *default_realm; uoff_t cache_size; unsigned int cache_ttl; unsigned int cache_negative_ttl; const char *username_chars; const char *username_translation; const char *username_format; const char *master_user_separator; const char *anonymous_username; const char *krb5_keytab; const char *gssapi_hostname; const char *winbind_helper_path; const char *proxy_self; unsigned int failure_delay; const char *policy_server_url; const char *policy_server_api_header; unsigned int policy_server_timeout_msecs; const char *policy_hash_mech; const char *policy_hash_nonce; const char *policy_request_attributes; bool policy_reject_on_fail; unsigned int policy_hash_truncate; bool stats; bool verbose, debug, debug_passwords; const char *verbose_passwords; bool ssl_require_client_cert; bool ssl_username_from_cert; bool use_winbind; unsigned int worker_max_count; /* settings that don't have auth_ prefix: */ ARRAY(struct auth_passdb_settings *) passdbs; ARRAY(struct auth_userdb_settings *) userdbs; const char *base_dir; const char *ssl_client_ca_dir; const char *ssl_client_ca_file; bool verbose_proctitle; unsigned int first_valid_uid; unsigned int last_valid_uid; /* generated: */ char username_chars_map[256]; char username_translation_map[256]; const char *const *realms_arr; const struct ip_addr *proxy_self_ips; }; /* ../../src/util/tcpwrap-settings.c */ #ifdef HAVE_LIBWRAP struct service_settings tcpwrap_service_settings = { .name = "tcpwrap", .protocol = "", .type = "", .executable = "tcpwrap", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #endif /* ../../src/stats/stats-settings.c */ /* */ static struct file_listener_settings stats_unix_listeners_array[] = { { "stats", 0600, "", "" } }; static struct file_listener_settings *stats_unix_listeners[] = { &stats_unix_listeners_array[0] }; static buffer_t stats_unix_listeners_buf = { stats_unix_listeners, sizeof(stats_unix_listeners), { NULL, } }; static struct file_listener_settings stats_fifo_listeners_array[] = { { "stats-mail", 0600, "", "" }, { "stats-user", 0600, "", "" } }; static struct file_listener_settings *stats_fifo_listeners[] = { &stats_fifo_listeners_array[0], &stats_fifo_listeners_array[1] }; static buffer_t stats_fifo_listeners_buf = { stats_fifo_listeners, sizeof(stats_fifo_listeners), { NULL, } }; /* */ struct service_settings stats_service_settings = { .name = "stats", .protocol = "", .type = "", .executable = "stats", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &stats_unix_listeners_buf, sizeof(stats_unix_listeners[0]) } }, .fifo_listeners = { { &stats_fifo_listeners_buf, sizeof(stats_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; #undef DEF #define DEF(type, name) \ { type, "stats_"#name, offsetof(struct stats_settings, name), NULL } static const struct setting_define stats_setting_defines[] = { DEF(SET_SIZE, memory_limit), DEF(SET_TIME, command_min_time), DEF(SET_TIME, session_min_time), DEF(SET_TIME, user_min_time), DEF(SET_TIME, domain_min_time), DEF(SET_TIME, ip_min_time), DEF(SET_STR, carbon_server), DEF(SET_TIME, carbon_interval), DEF(SET_STR, carbon_name), SETTING_DEFINE_LIST_END }; const struct stats_settings stats_default_settings = { .memory_limit = 1024*1024*16, .command_min_time = 60, .session_min_time = 60*15, .user_min_time = 60*60, .domain_min_time = 60*60*12, .ip_min_time = 60*60*12, .carbon_interval = 30, .carbon_server = "", .carbon_name = "" }; const struct setting_parser_info stats_setting_parser_info = { .module_name = "stats", .defines = stats_setting_defines, .defaults = &stats_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct stats_settings), .parent_offset = (size_t)-1 }; /* ../../src/ssl-params/ssl-params-settings.c */ /* */ static struct file_listener_settings ssl_params_unix_listeners_array[] = { { "ssl-params", 0666, "", "" }, { "login/ssl-params", 0666, "", "" } }; static struct file_listener_settings *ssl_params_unix_listeners[] = { &ssl_params_unix_listeners_array[0], &ssl_params_unix_listeners_array[1] }; static buffer_t ssl_params_unix_listeners_buf = { ssl_params_unix_listeners, sizeof(ssl_params_unix_listeners), { NULL, } }; /* */ struct service_settings ssl_params_service_settings = { .name = "ssl-params", .protocol = "", #ifdef HAVE_SSL .type = "startup", #else .type = "", #endif .executable = "ssl-params", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &ssl_params_unix_listeners_buf, sizeof(ssl_params_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct ssl_params_settings, name), NULL } static const struct setting_define ssl_params_setting_defines[] = { DEF(SET_TIME, ssl_parameters_regenerate), DEF(SET_UINT, ssl_dh_parameters_length), SETTING_DEFINE_LIST_END }; static const struct ssl_params_settings ssl_params_default_settings = { .ssl_parameters_regenerate = 0, .ssl_dh_parameters_length = 1024 }; const struct setting_parser_info ssl_params_setting_parser_info = { .module_name = "ssl-params", .defines = ssl_params_setting_defines, .defaults = &ssl_params_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct ssl_params_settings), .parent_offset = (size_t)-1 }; /* ../../src/replication/replicator/replicator-settings.c */ /* */ static struct file_listener_settings replicator_unix_listeners_array[] = { { "replicator", 0600, "$default_internal_user", "" }, { "replicator-doveadm", 0, "$default_internal_user", "" } }; static struct file_listener_settings *replicator_unix_listeners[] = { &replicator_unix_listeners_array[0], &replicator_unix_listeners_array[1] }; static buffer_t replicator_unix_listeners_buf = { replicator_unix_listeners, sizeof(replicator_unix_listeners), { NULL, } }; /* */ struct service_settings replicator_service_settings = { .name = "replicator", .protocol = "", .type = "", .executable = "replicator", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &replicator_unix_listeners_buf, sizeof(replicator_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct replicator_settings, name), NULL } static const struct setting_define replicator_setting_defines[] = { DEF(SET_STR, auth_socket_path), DEF(SET_STR, doveadm_socket_path), DEF(SET_STR, replication_dsync_parameters), DEF(SET_TIME, replication_full_sync_interval), DEF(SET_UINT, replication_max_conns), SETTING_DEFINE_LIST_END }; const struct replicator_settings replicator_default_settings = { .auth_socket_path = "auth-userdb", .doveadm_socket_path = "doveadm-server", .replication_dsync_parameters = "-d -N -l 30 -U", .replication_full_sync_interval = 60*60*24, .replication_max_conns = 10 }; const struct setting_parser_info replicator_setting_parser_info = { .module_name = "replicator", .defines = replicator_setting_defines, .defaults = &replicator_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct replicator_settings), .parent_offset = (size_t)-1 }; /* ../../src/replication/aggregator/aggregator-settings.c */ /* */ static struct file_listener_settings aggregator_unix_listeners_array[] = { { "replication-notify", 0600, "", "" } }; static struct file_listener_settings *aggregator_unix_listeners[] = { &aggregator_unix_listeners_array[0] }; static buffer_t aggregator_unix_listeners_buf = { aggregator_unix_listeners, sizeof(aggregator_unix_listeners), { NULL, } }; static struct file_listener_settings aggregator_fifo_listeners_array[] = { { "replication-notify-fifo", 0600, "", "" } }; static struct file_listener_settings *aggregator_fifo_listeners[] = { &aggregator_fifo_listeners_array[0] }; static buffer_t aggregator_fifo_listeners_buf = { aggregator_fifo_listeners, sizeof(aggregator_fifo_listeners), { NULL, } }; /* */ struct service_settings aggregator_service_settings = { .name = "aggregator", .protocol = "", .type = "", .executable = "aggregator", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = ".", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &aggregator_unix_listeners_buf, sizeof(aggregator_unix_listeners[0]) } }, .fifo_listeners = { { &aggregator_fifo_listeners_buf, sizeof(aggregator_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct aggregator_settings, name), NULL } static const struct setting_define aggregator_setting_defines[] = { DEF(SET_STR, replicator_host), DEF(SET_IN_PORT, replicator_port), SETTING_DEFINE_LIST_END }; const struct aggregator_settings aggregator_default_settings = { .replicator_host = "replicator", .replicator_port = 0 }; const struct setting_parser_info aggregator_setting_parser_info = { .module_name = "aggregator", .defines = aggregator_setting_defines, .defaults = &aggregator_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct aggregator_settings), .parent_offset = (size_t)-1 }; /* ../../src/pop3/pop3-settings.c */ /* */ static struct file_listener_settings pop3_unix_listeners_array[] = { { "login/pop3", 0666, "", "" } }; static struct file_listener_settings *pop3_unix_listeners[] = { &pop3_unix_listeners_array[0] }; static buffer_t pop3_unix_listeners_buf = { pop3_unix_listeners, sizeof(pop3_unix_listeners), { NULL, } }; /* */ /* */ struct pop3_client_workaround_list { const char *name; enum pop3_client_workarounds num; }; static const struct pop3_client_workaround_list pop3_client_workaround_list[] = { { "outlook-no-nuls", WORKAROUND_OUTLOOK_NO_NULS }, { "oe-ns-eoh", WORKAROUND_OE_NS_EOH }, { NULL, 0 } }; static int pop3_settings_parse_workarounds(struct pop3_settings *set, const char **error_r) { enum pop3_client_workarounds client_workarounds = 0; const struct pop3_client_workaround_list *list; const char *const *str; str = t_strsplit_spaces(set->pop3_client_workarounds, " ,"); for (; *str != NULL; str++) { list = pop3_client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { client_workarounds |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("pop3_client_workarounds: " "Unknown workaround: %s", *str); return -1; } } set->parsed_workarounds = client_workarounds; return 0; } static bool pop3_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct pop3_settings *set = _set; if (pop3_settings_parse_workarounds(set, error_r) < 0) return FALSE; if (strcmp(set->pop3_delete_type, "default") == 0) { if (set->pop3_deleted_flag[0] == '\0') set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE; else set->parsed_delete_type = POP3_DELETE_TYPE_FLAG; } else if (strcmp(set->pop3_delete_type, "expunge") == 0) { set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE; } else if (strcmp(set->pop3_delete_type, "flag") == 0) { if (set->pop3_deleted_flag[0] == '\0') { *error_r = "pop3_delete_type=flag, but pop3_deleted_flag not set"; return FALSE; } set->parsed_delete_type = POP3_DELETE_TYPE_FLAG; } else { *error_r = t_strdup_printf("pop3_delete_type: Unknown value '%s'", set->pop3_delete_type); return FALSE; } return TRUE; } /* */ struct service_settings pop3_service_settings = { .name = "pop3", .protocol = "pop3", .type = "", .executable = "pop3", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &pop3_unix_listeners_buf, sizeof(pop3_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct pop3_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct pop3_settings, field), defines } static const struct setting_define pop3_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR_VARS, rawlog_dir), DEF(SET_BOOL, pop3_no_flag_updates), DEF(SET_BOOL, pop3_enable_last), DEF(SET_BOOL, pop3_reuse_xuidl), DEF(SET_BOOL, pop3_save_uidl), DEF(SET_BOOL, pop3_lock_session), DEF(SET_BOOL, pop3_fast_size_lookups), DEF(SET_STR, pop3_client_workarounds), DEF(SET_STR, pop3_logout_format), DEF(SET_ENUM, pop3_uidl_duplicates), DEF(SET_STR, pop3_deleted_flag), DEF(SET_ENUM, pop3_delete_type), SETTING_DEFINE_LIST_END }; static const struct pop3_settings pop3_default_settings = { .verbose_proctitle = FALSE, .rawlog_dir = "", .pop3_no_flag_updates = FALSE, .pop3_enable_last = FALSE, .pop3_reuse_xuidl = FALSE, .pop3_save_uidl = FALSE, .pop3_lock_session = FALSE, .pop3_fast_size_lookups = FALSE, .pop3_client_workarounds = "", .pop3_logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", .pop3_uidl_duplicates = "allow:rename", .pop3_deleted_flag = "", .pop3_delete_type = "default:expunge:flag" }; static const struct setting_parser_info *pop3_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info pop3_setting_parser_info = { .module_name = "pop3", .defines = pop3_setting_defines, .defaults = &pop3_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct pop3_settings), .parent_offset = (size_t)-1, .check_func = pop3_settings_verify, .dependencies = pop3_setting_dependencies }; /* ../../src/pop3-login/pop3-login-settings.c */ /* */ static struct inet_listener_settings pop3_login_inet_listeners_array[] = { { .name = "pop3", .address = "", .port = 110 }, { .name = "pop3s", .address = "", .port = 995, .ssl = TRUE } }; static struct inet_listener_settings *pop3_login_inet_listeners[] = { &pop3_login_inet_listeners_array[0], &pop3_login_inet_listeners_array[1] }; static buffer_t pop3_login_inet_listeners_buf = { pop3_login_inet_listeners, sizeof(pop3_login_inet_listeners), { NULL, } }; /* */ struct service_settings pop3_login_service_settings = { .name = "pop3-login", .protocol = "pop3", .type = "login", .executable = "pop3-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = { { &pop3_login_inet_listeners_buf, sizeof(pop3_login_inet_listeners[0]) } } }; static const struct setting_define pop3_login_setting_defines[] = { SETTING_DEFINE_LIST_END }; static const struct setting_parser_info *pop3_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; const struct setting_parser_info pop3_login_setting_parser_info = { .module_name = "pop3-login", .defines = pop3_login_setting_defines, .type_offset = (size_t)-1, .parent_offset = (size_t)-1, .dependencies = pop3_login_setting_dependencies }; const struct setting_parser_info *pop3_login_setting_roots[] = { &login_setting_parser_info, &pop3_login_setting_parser_info, NULL }; /* ../../src/plugins/quota/quota-status-settings.c */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct quota_status_settings, name), NULL } static const struct setting_define quota_status_setting_defines[] = { DEF(SET_STR, recipient_delimiter), SETTING_DEFINE_LIST_END }; static const struct quota_status_settings quota_status_default_settings = { .recipient_delimiter = "+", }; static const struct setting_parser_info *quota_status_setting_dependencies[] = { NULL }; const struct setting_parser_info quota_status_setting_parser_info = { .module_name = "mail", .defines = quota_status_setting_defines, .defaults = "a_status_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct quota_status_settings), .parent_offset = (size_t)-1, .dependencies = quota_status_setting_dependencies }; /* ../../src/plugins/mail-crypt/fs-crypt-settings.c */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct fs_crypt_settings, name), NULL } static const struct setting_define fs_crypt_setting_defines[] = { { SET_STRLIST, "plugin", offsetof(struct fs_crypt_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; const struct fs_crypt_settings fs_crypt_default_settings = { .plugin_envs = ARRAY_INIT }; static const struct setting_parser_info *fs_crypt_setting_dependencies[] = { NULL }; const struct setting_parser_info fs_crypt_setting_parser_info = { .module_name = "fs-crypt", .defines = fs_crypt_setting_defines, .defaults = &fs_crypt_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct fs_crypt_settings), .parent_offset = (size_t)-1, .dependencies = fs_crypt_setting_dependencies }; /* ../../src/plugins/dict-ldap/dict-ldap-settings.c */ #undef DEF_STR #undef DEF_BOOL #undef DEF_UINT #define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map) #define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map) /* ../../src/master/master-settings.c */ extern const struct setting_parser_info service_setting_parser_info; extern const struct setting_parser_info service_setting_parser_info; /* */ static void expand_user(const char **user, enum service_user_default *default_r, const struct master_settings *set) { /* $variable expansion is typically done by doveconf, but these variables can come from built-in settings, so we need to expand them here */ if (strcmp(*user, "$default_internal_user") == 0) { *user = set->default_internal_user; *default_r = SERVICE_USER_DEFAULT_INTERNAL; } else if (strcmp(*user, "$default_login_user") == 0) { *user = set->default_login_user; *default_r = SERVICE_USER_DEFAULT_LOGIN; } else { *default_r = SERVICE_USER_DEFAULT_NONE; } } static bool fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l, pool_t pool, const struct master_settings *master_set, ARRAY_TYPE(const_string) *all_listeners, const char **error_r) { struct file_listener_settings *const *sets; size_t base_dir_len = strlen(master_set->base_dir); enum service_user_default user_default; if (!array_is_created(l)) return TRUE; array_foreach(l, sets) { struct file_listener_settings *set = *sets; if (set->path[0] == '\0') { *error_r = "path must not be empty"; return FALSE; } expand_user(&set->user, &user_default, master_set); if (*set->path != '/') { set->path = p_strconcat(pool, master_set->base_dir, "/", set->path, NULL); } else if (strncmp(set->path, master_set->base_dir, base_dir_len) == 0 && set->path[base_dir_len] == '/') { i_warning("You should remove base_dir prefix from " "unix_listener: %s", set->path); } if (set->mode != 0) array_append(all_listeners, &set->path, 1); } return TRUE; } static void add_inet_listeners(ARRAY_TYPE(inet_listener_settings) *l, ARRAY_TYPE(const_string) *all_listeners) { struct inet_listener_settings *const *sets; const char *str; if (!array_is_created(l)) return; array_foreach(l, sets) { struct inet_listener_settings *set = *sets; if (set->port != 0) { str = t_strdup_printf("%u:%s", set->port, set->address); array_append(all_listeners, &str, 1); } } } static bool master_settings_parse_type(struct service_settings *set, const char **error_r) { if (*set->type == '\0') set->parsed_type = SERVICE_TYPE_UNKNOWN; else if (strcmp(set->type, "log") == 0) set->parsed_type = SERVICE_TYPE_LOG; else if (strcmp(set->type, "config") == 0) set->parsed_type = SERVICE_TYPE_CONFIG; else if (strcmp(set->type, "anvil") == 0) set->parsed_type = SERVICE_TYPE_ANVIL; else if (strcmp(set->type, "login") == 0) set->parsed_type = SERVICE_TYPE_LOGIN; else if (strcmp(set->type, "startup") == 0) set->parsed_type = SERVICE_TYPE_STARTUP; else { *error_r = t_strconcat("Unknown service type: ", set->type, NULL); return FALSE; } return TRUE; } static void service_set_login_dump_core(struct service_settings *set) { const char *p; if (set->parsed_type != SERVICE_TYPE_LOGIN) return; p = strstr(set->executable, " -D"); if (p != NULL && (p[3] == '\0' || p[3] == ' ')) set->login_dump_core = TRUE; } static bool services_have_protocol(struct master_settings *set, const char *name) { struct service_settings *const *services; array_foreach(&set->services, services) { struct service_settings *service = *services; if (strcmp(service->protocol, name) == 0) return TRUE; } return FALSE; } #ifdef CONFIG_BINARY static const struct service_settings * master_default_settings_get_service(const char *name) { extern struct master_settings master_default_settings; struct service_settings *const *setp; array_foreach(&master_default_settings.services, setp) { if (strcmp((*setp)->name, name) == 0) return *setp; } return NULL; } #endif static unsigned int service_get_client_limit(struct master_settings *set, const char *name) { struct service_settings *const *servicep; array_foreach(&set->services, servicep) { if (strcmp((*servicep)->name, name) == 0) { if ((*servicep)->client_limit != 0) return (*servicep)->client_limit; else return set->default_client_limit; } } return set->default_client_limit; } static bool master_settings_verify(void *_set, pool_t pool, const char **error_r) { static int warned_auth = FALSE, warned_anvil = FALSE; struct master_settings *set = _set; struct service_settings *const *services; const char *const *strings; ARRAY_TYPE(const_string) all_listeners; struct passwd pw; unsigned int i, j, count, client_limit, process_limit; unsigned int max_auth_client_processes, max_anvil_client_processes; size_t len; #ifdef CONFIG_BINARY const struct service_settings *default_service; #else rlim_t fd_limit; const char *max_client_limit_source = "default_client_limit"; unsigned int max_client_limit = set->default_client_limit; #endif if (*set->listen == '\0') { *error_r = "listen can't be set empty"; return FALSE; } len = strlen(set->base_dir); if (len > 0 && set->base_dir[len-1] == '/') { /* drop trailing '/' */ set->base_dir = p_strndup(pool, set->base_dir, len - 1); } if (set->last_valid_uid != 0 && set->first_valid_uid > set->last_valid_uid) { *error_r = "first_valid_uid can't be larger than last_valid_uid"; return FALSE; } if (set->last_valid_gid != 0 && set->first_valid_gid > set->last_valid_gid) { *error_r = "first_valid_gid can't be larger than last_valid_gid"; return FALSE; } if (i_getpwnam(set->default_login_user, &pw) == 0) { *error_r = t_strdup_printf("default_login_user doesn't exist: %s", set->default_login_user); return FALSE; } if (i_getpwnam(set->default_internal_user, &pw) == 0) { *error_r = t_strdup_printf("default_internal_user doesn't exist: %s", set->default_internal_user); return FALSE; } /* check that we have at least one service. the actual service structure validity is checked later while creating them. */ if (!array_is_created(&set->services) || array_count(&set->services) == 0) { *error_r = "No services defined"; return FALSE; } services = array_get(&set->services, &count); for (i = 0; i < count; i++) { struct service_settings *service = services[i]; if (*service->name == '\0') { *error_r = t_strdup_printf( "Service #%d is missing name", i); return FALSE; } if (!master_settings_parse_type(service, error_r)) return FALSE; for (j = 0; j < i; j++) { if (strcmp(service->name, services[j]->name) == 0) { *error_r = t_strdup_printf( "Duplicate service name: %s", service->name); return FALSE; } } expand_user(&service->user, &service->user_default, set); service_set_login_dump_core(service); } set->protocols_split = p_strsplit_spaces(pool, set->protocols, " "); if (set->protocols_split[0] != NULL && strcmp(set->protocols_split[0], "none") == 0 && set->protocols_split[1] == NULL) set->protocols_split[0] = NULL; for (i = 0; set->protocols_split[i] != NULL; i++) { if (!services_have_protocol(set, set->protocols_split[i])) { *error_r = t_strdup_printf("protocols: " "Unknown protocol: %s", set->protocols_split[i]); return FALSE; } } t_array_init(&all_listeners, 64); max_auth_client_processes = 0; max_anvil_client_processes = 2; /* blocking, nonblocking pipes */ for (i = 0; i < count; i++) { struct service_settings *service = services[i]; if (*service->protocol != '\0' && !str_array_find((const char **)set->protocols_split, service->protocol)) { /* protocol not enabled, ignore its settings */ continue; } if (*service->executable != '/' && *service->executable != '\0') { service->executable = p_strconcat(pool, set->libexec_dir, "/", service->executable, NULL); } if (*service->chroot != '/' && *service->chroot != '\0') { service->chroot = p_strconcat(pool, set->base_dir, "/", service->chroot, NULL); } if (service->drop_priv_before_exec && *service->chroot != '\0') { *error_r = t_strdup_printf("service(%s): " "drop_priv_before_exec=yes can't be " "used with chroot", service->name); return FALSE; } process_limit = service->process_limit; if (process_limit == 0) process_limit = set->default_process_limit; if (service->process_min_avail > process_limit) { *error_r = t_strdup_printf("service(%s): " "process_min_avail is higher than process_limit", service->name); return FALSE; } if (service->vsz_limit < 1024*1024 && service->vsz_limit != 0) { *error_r = t_strdup_printf("service(%s): " "vsz_limit is too low", service->name); return FALSE; } #ifdef CONFIG_BINARY default_service = master_default_settings_get_service(service->name); if (default_service != NULL && default_service->process_limit_1 && process_limit > 1) { *error_r = t_strdup_printf("service(%s): " "process_limit must be 1", service->name); return FALSE; } #else if (max_client_limit < service->client_limit) { max_client_limit = service->client_limit; max_client_limit_source = t_strdup_printf( "service %s { client_limit }", service->name); } #endif if (*service->protocol != '\0') { /* each imap/pop3/lmtp process can use up a connection, although if service_count=1 it's only temporary */ if (service->service_count != 1 || strcmp(service->type, "login") == 0) max_auth_client_processes += process_limit; } if (strcmp(service->type, "login") == 0 || strcmp(service->name, "auth") == 0) max_anvil_client_processes += process_limit; if (!fix_file_listener_paths(&service->unix_listeners, pool, set, &all_listeners, error_r)) { *error_r = t_strdup_printf("service(%s): unix_listener: %s", service->name, *error_r); return FALSE; } if (!fix_file_listener_paths(&service->fifo_listeners, pool, set, &all_listeners, error_r)) { *error_r = t_strdup_printf("service(%s): fifo_listener: %s", service->name, *error_r); return FALSE; } add_inet_listeners(&service->inet_listeners, &all_listeners); } client_limit = service_get_client_limit(set, "auth"); if (client_limit < max_auth_client_processes && !warned_auth) { warned_auth = TRUE; i_warning("service auth { client_limit=%u } is lower than " "required under max. load (%u)", client_limit, max_auth_client_processes); } client_limit = service_get_client_limit(set, "anvil"); if (client_limit < max_anvil_client_processes && !warned_anvil) { warned_anvil = TRUE; i_warning("service anvil { client_limit=%u } is lower than " "required under max. load (%u)", client_limit, max_anvil_client_processes); } #ifndef CONFIG_BINARY if (restrict_get_fd_limit(&fd_limit) == 0 && fd_limit < (rlim_t)max_client_limit) { i_warning("fd limit (ulimit -n) is lower than required " "under max. load (%u < %u), because of %s", (unsigned int)fd_limit, max_client_limit, max_client_limit_source); } #endif /* check for duplicate listeners */ array_sort(&all_listeners, i_strcmp_p); strings = array_get(&all_listeners, &count); for (i = 1; i < count; i++) { if (strcmp(strings[i-1], strings[i]) == 0) { *error_r = t_strdup_printf("duplicate listener: %s", strings[i]); return FALSE; } } return TRUE; } /* */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct file_listener_settings, name), NULL } static const struct setting_define file_listener_setting_defines[] = { DEF(SET_STR, path), DEF(SET_UINT_OCT, mode), DEF(SET_STR, user), DEF(SET_STR, group), SETTING_DEFINE_LIST_END }; static const struct file_listener_settings file_listener_default_settings = { .path = "", .mode = 0600, .user = "", .group = "", }; static const struct setting_parser_info file_listener_setting_parser_info = { .defines = file_listener_setting_defines, .defaults = &file_listener_default_settings, .type_offset = offsetof(struct file_listener_settings, path), .struct_size = sizeof(struct file_listener_settings), .parent_offset = (size_t)-1, .parent = &service_setting_parser_info }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct inet_listener_settings, name), NULL } static const struct setting_define inet_listener_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, address), DEF(SET_IN_PORT, port), DEF(SET_BOOL, ssl), DEF(SET_BOOL, reuse_port), DEF(SET_BOOL, haproxy), SETTING_DEFINE_LIST_END }; static const struct inet_listener_settings inet_listener_default_settings = { .name = "", .address = "", .port = 0, .ssl = FALSE, .reuse_port = FALSE, .haproxy = FALSE }; static const struct setting_parser_info inet_listener_setting_parser_info = { .defines = inet_listener_setting_defines, .defaults = &inet_listener_default_settings, .type_offset = offsetof(struct inet_listener_settings, name), .struct_size = sizeof(struct inet_listener_settings), .parent_offset = (size_t)-1, .parent = &service_setting_parser_info }; #undef DEF #undef DEFLIST #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct service_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct service_settings, field), defines } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, offsetof(struct service_settings, field), defines } static const struct setting_define service_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, protocol), DEF(SET_STR, type), DEF(SET_STR, executable), DEF(SET_STR, user), DEF(SET_STR, group), DEF(SET_STR, privileged_group), DEF(SET_STR, extra_groups), DEF(SET_STR, chroot), DEF(SET_BOOL, drop_priv_before_exec), DEF(SET_UINT, process_min_avail), DEF(SET_UINT, process_limit), DEF(SET_UINT, client_limit), DEF(SET_UINT, service_count), DEF(SET_TIME, idle_kill), DEF(SET_SIZE, vsz_limit), DEFLIST_UNIQUE(unix_listeners, "unix_listener", &file_listener_setting_parser_info), DEFLIST_UNIQUE(fifo_listeners, "fifo_listener", &file_listener_setting_parser_info), DEFLIST_UNIQUE(inet_listeners, "inet_listener", &inet_listener_setting_parser_info), SETTING_DEFINE_LIST_END }; static const struct service_settings service_default_settings = { .name = "", .protocol = "", .type = "", .executable = "", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; const struct setting_parser_info service_setting_parser_info = { .defines = service_setting_defines, .defaults = &service_default_settings, .type_offset = offsetof(struct service_settings, name), .struct_size = sizeof(struct service_settings), .parent_offset = offsetof(struct service_settings, master_set), .parent = &master_setting_parser_info }; #undef DEF #undef DEFLIST_UNIQUE #define DEF(type, name) \ { type, #name, offsetof(struct master_settings, name), NULL } #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, offsetof(struct master_settings, field), defines } static const struct setting_define master_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, state_dir), DEF(SET_STR, libexec_dir), DEF(SET_STR, instance_name), DEF(SET_STR, protocols), DEF(SET_STR, listen), DEF(SET_ENUM, ssl), DEF(SET_STR, default_internal_user), DEF(SET_STR, default_login_user), DEF(SET_UINT, default_process_limit), DEF(SET_UINT, default_client_limit), DEF(SET_TIME, default_idle_kill), DEF(SET_SIZE, default_vsz_limit), DEF(SET_BOOL, version_ignore), DEF(SET_UINT, first_valid_uid), DEF(SET_UINT, last_valid_uid), DEF(SET_UINT, first_valid_gid), DEF(SET_UINT, last_valid_gid), DEFLIST_UNIQUE(services, "service", &service_setting_parser_info), SETTING_DEFINE_LIST_END }; struct master_settings master_default_settings = { .base_dir = PKG_RUNDIR, .state_dir = PKG_STATEDIR, .libexec_dir = PKG_LIBEXECDIR, .instance_name = PACKAGE, .protocols = "imap pop3 lmtp", .listen = "*, ::", .ssl = "yes:no:required", .default_internal_user = "dovecot", .default_login_user = "dovenull", .default_process_limit = 100, .default_client_limit = 1000, .default_idle_kill = 60, .default_vsz_limit = 256*1024*1024, .version_ignore = FALSE, .first_valid_uid = 500, .last_valid_uid = 0, .first_valid_gid = 1, .last_valid_gid = 0, #ifndef CONFIG_BINARY .services = ARRAY_INIT #else .services = { { &config_all_services_buf, sizeof(struct service_settings *) } }, #endif }; const struct setting_parser_info master_setting_parser_info = { .module_name = "master", .defines = master_setting_defines, .defaults = &master_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct master_settings), .parent_offset = (size_t)-1, .check_func = master_settings_verify }; /* ../../src/login-common/login-settings.c */ /* */ static bool login_settings_check(void *_set, pool_t pool, const char **error_r ATTR_UNUSED) { struct login_settings *set = _set; set->log_format_elements_split = p_strsplit(pool, set->login_log_format_elements, " "); if (set->auth_debug_passwords) set->auth_debug = TRUE; if (set->auth_debug) set->auth_verbose = TRUE; return TRUE; } /* */ #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct login_settings, name), NULL } static const struct setting_define login_setting_defines[] = { DEF(SET_STR, login_trusted_networks), DEF(SET_STR, login_source_ips), DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, login_log_format_elements), DEF(SET_STR, login_log_format), DEF(SET_STR, login_access_sockets), DEF(SET_STR, login_plugin_dir), DEF(SET_STR, login_plugins), DEF(SET_TIME, login_proxy_max_disconnect_delay), DEF(SET_STR, director_username_hash), DEF(SET_STR, ssl_client_cert), DEF(SET_STR, ssl_client_key), DEF(SET_BOOL, ssl_require_crl), DEF(SET_BOOL, auth_ssl_require_client_cert), DEF(SET_BOOL, auth_ssl_username_from_cert), DEF(SET_BOOL, disable_plaintext_auth), DEF(SET_BOOL, auth_verbose), DEF(SET_BOOL, auth_debug), DEF(SET_BOOL, verbose_proctitle), DEF(SET_UINT, mail_max_userip_connections), SETTING_DEFINE_LIST_END }; static const struct login_settings login_default_settings = { .login_trusted_networks = "", .login_source_ips = "", .login_greeting = PACKAGE_NAME" ready.", .login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c session=<%{session}>", .login_log_format = "%$: %s", .login_access_sockets = "", .login_plugin_dir = MODULEDIR"/login", .login_plugins = "", .login_proxy_max_disconnect_delay = 0, .director_username_hash = "%u", .ssl_client_cert = "", .ssl_client_key = "", .ssl_require_crl = TRUE, .auth_ssl_require_client_cert = FALSE, .auth_ssl_username_from_cert = FALSE, .disable_plaintext_auth = TRUE, .auth_verbose = FALSE, .auth_debug = FALSE, .verbose_proctitle = FALSE, .mail_max_userip_connections = 10 }; const struct setting_parser_info login_setting_parser_info = { .module_name = "login", .defines = login_setting_defines, .defaults = &login_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct login_settings), .parent_offset = (size_t)-1, .check_func = login_settings_check }; /* ../../src/log/log-settings.c */ /* */ static struct file_listener_settings log_unix_listeners_array[] = { { "log-errors", 0600, "", "" } }; static struct file_listener_settings *log_unix_listeners[] = { &log_unix_listeners_array[0] }; static buffer_t log_unix_listeners_buf = { log_unix_listeners, sizeof(log_unix_listeners), { NULL, } }; /* */ struct service_settings log_service_settings = { .name = "log", .protocol = "", .type = "log", .executable = "log", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &log_unix_listeners_buf, sizeof(log_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; /* ../../src/lmtp/lmtp-settings.c */ /* */ static struct file_listener_settings lmtp_unix_listeners_array[] = { { "lmtp", 0666, "", "" } }; static struct file_listener_settings *lmtp_unix_listeners[] = { &lmtp_unix_listeners_array[0] }; static buffer_t lmtp_unix_listeners_buf = { lmtp_unix_listeners, sizeof(lmtp_unix_listeners), { NULL, } }; /* */ /* */ static bool lmtp_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct lmtp_settings *set = _set; if (strcmp(set->lmtp_hdr_delivery_address, "none") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_NONE; } else if (strcmp(set->lmtp_hdr_delivery_address, "final") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_FINAL; } else if (strcmp(set->lmtp_hdr_delivery_address, "original") == 0) { set->parsed_lmtp_hdr_delivery_address = LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL; } else { *error_r = t_strdup_printf("Unknown lmtp_hdr_delivery_address: %s", set->lmtp_hdr_delivery_address); return FALSE; } return TRUE; } /* */ struct service_settings lmtp_service_settings = { .name = "lmtp", .protocol = "lmtp", .type = "", .executable = "lmtp", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &lmtp_unix_listeners_buf, sizeof(lmtp_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct lmtp_settings, name), NULL } static const struct setting_define lmtp_setting_defines[] = { DEF(SET_BOOL, lmtp_proxy), DEF(SET_BOOL, lmtp_save_to_detail_mailbox), DEF(SET_BOOL, lmtp_rcpt_check_quota), DEF(SET_UINT, lmtp_user_concurrency_limit), DEF(SET_STR, lmtp_address_translate), DEF(SET_ENUM, lmtp_hdr_delivery_address), DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, login_trusted_networks), SETTING_DEFINE_LIST_END }; static const struct lmtp_settings lmtp_default_settings = { .lmtp_proxy = FALSE, .lmtp_save_to_detail_mailbox = FALSE, .lmtp_rcpt_check_quota = FALSE, .lmtp_user_concurrency_limit = 0, .lmtp_address_translate = "", .lmtp_hdr_delivery_address = "final:none:original", .login_greeting = PACKAGE_NAME" ready.", .login_trusted_networks = "" }; static const struct setting_parser_info *lmtp_setting_dependencies[] = { &lda_setting_parser_info, NULL }; const struct setting_parser_info lmtp_setting_parser_info = { .module_name = "lmtp", .defines = lmtp_setting_defines, .defaults = &lmtp_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct lmtp_settings), .parent_offset = (size_t)-1, .check_func = lmtp_settings_check, .dependencies = lmtp_setting_dependencies }; /* ../../src/ipc/ipc-settings.c */ /* */ static struct file_listener_settings ipc_unix_listeners_array[] = { { "ipc", 0600, "", "" }, { "login/ipc-proxy", 0600, "$default_login_user", "" } }; static struct file_listener_settings *ipc_unix_listeners[] = { &ipc_unix_listeners_array[0], &ipc_unix_listeners_array[1] }; static buffer_t ipc_unix_listeners_buf = { ipc_unix_listeners, sizeof(ipc_unix_listeners), { NULL, } }; /* */ struct service_settings ipc_service_settings = { .name = "ipc", .protocol = "", .type = "", .executable = "ipc", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &ipc_unix_listeners_buf, sizeof(ipc_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; /* ../../src/indexer/indexer-worker-settings.c */ /* */ static struct file_listener_settings indexer_worker_unix_listeners_array[] = { { "indexer-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *indexer_worker_unix_listeners[] = { &indexer_worker_unix_listeners_array[0] }; static buffer_t indexer_worker_unix_listeners_buf = { indexer_worker_unix_listeners, sizeof(indexer_worker_unix_listeners), { NULL, } }; /* */ struct service_settings indexer_worker_service_settings = { .name = "indexer-worker", .protocol = "", .type = "", .executable = "indexer-worker", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 10, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &indexer_worker_unix_listeners_buf, sizeof(indexer_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; /* ../../src/indexer/indexer-settings.c */ extern const struct setting_parser_info service_setting_parser_info; /* */ static struct file_listener_settings indexer_unix_listeners_array[] = { { "indexer", 0666, "", "" } }; static struct file_listener_settings *indexer_unix_listeners[] = { &indexer_unix_listeners_array[0] }; static buffer_t indexer_unix_listeners_buf = { indexer_unix_listeners, sizeof(indexer_unix_listeners), { NULL, } }; /* */ struct service_settings indexer_service_settings = { .name = "indexer", .protocol = "", .type = "", .executable = "indexer", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &indexer_unix_listeners_buf, sizeof(indexer_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; /* ../../src/imap/imap-settings.c */ /* */ static struct file_listener_settings imap_unix_listeners_array[] = { { "login/imap", 0666, "", "" }, { "imap-master", 0600, "", "" } }; static struct file_listener_settings *imap_unix_listeners[] = { &imap_unix_listeners_array[0], &imap_unix_listeners_array[1] }; static buffer_t imap_unix_listeners_buf = { imap_unix_listeners, sizeof(imap_unix_listeners), { NULL, } }; /* */ /* */ struct imap_client_workaround_list { const char *name; enum imap_client_workarounds num; }; static const struct imap_client_workaround_list imap_client_workaround_list[] = { { "delay-newmail", WORKAROUND_DELAY_NEWMAIL }, { "tb-extra-mailbox-sep", WORKAROUND_TB_EXTRA_MAILBOX_SEP }, { "tb-lsub-flags", WORKAROUND_TB_LSUB_FLAGS }, { NULL, 0 } }; static int imap_settings_parse_workarounds(struct imap_settings *set, const char **error_r) { enum imap_client_workarounds client_workarounds = 0; const struct imap_client_workaround_list *list; const char *const *str; str = t_strsplit_spaces(set->imap_client_workarounds, " ,"); for (; *str != NULL; str++) { list = imap_client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { client_workarounds |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("imap_client_workarounds: " "Unknown workaround: %s", *str); return -1; } } set->parsed_workarounds = client_workarounds; return 0; } static bool imap_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct imap_settings *set = _set; if (imap_settings_parse_workarounds(set, error_r) < 0) return FALSE; if (strcmp(set->imap_fetch_failure, "disconnect-immediately") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY; else if (strcmp(set->imap_fetch_failure, "disconnect-after") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER; else if (strcmp(set->imap_fetch_failure, "no-after") == 0) set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_NO_AFTER; else { *error_r = t_strdup_printf("Unknown imap_fetch_failure: %s", set->imap_fetch_failure); return FALSE; } return TRUE; } /* */ struct service_settings imap_service_settings = { .name = "imap", .protocol = "imap", .type = "", .executable = "imap", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_unix_listeners_buf, sizeof(imap_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #undef DEFLIST #define DEF(type, name) \ { type, #name, offsetof(struct imap_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct imap_settings, field), defines } static const struct setting_define imap_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR_VARS, rawlog_dir), DEF(SET_SIZE, imap_max_line_length), DEF(SET_TIME, imap_idle_notify_interval), DEF(SET_STR, imap_capability), DEF(SET_STR, imap_client_workarounds), DEF(SET_STR, imap_logout_format), DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), DEF(SET_ENUM, imap_fetch_failure), DEF(SET_BOOL, imap_metadata), DEF(SET_TIME, imap_hibernate_timeout), DEF(SET_STR, imap_urlauth_host), DEF(SET_IN_PORT, imap_urlauth_port), SETTING_DEFINE_LIST_END }; static const struct imap_settings imap_default_settings = { .verbose_proctitle = FALSE, .rawlog_dir = "", /* RFC-2683 recommends at least 8000 bytes. Some clients however don't break large message sets to multiple commands, so we're pretty liberal by default. */ .imap_max_line_length = 64*1024, .imap_idle_notify_interval = 2*60, .imap_capability = "", .imap_client_workarounds = "", .imap_logout_format = "in=%i out=%o", .imap_id_send = "name *", .imap_id_log = "", .imap_fetch_failure = "disconnect-immediately:disconnect-after:no-after", .imap_metadata = FALSE, .imap_hibernate_timeout = 0, .imap_urlauth_host = "", .imap_urlauth_port = 143 }; static const struct setting_parser_info *imap_setting_dependencies[] = { &mail_user_setting_parser_info, &lda_setting_parser_info, NULL }; const struct setting_parser_info imap_setting_parser_info = { .module_name = "imap", .defines = imap_setting_defines, .defaults = &imap_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_settings), .parent_offset = (size_t)-1, .check_func = imap_settings_verify, .dependencies = imap_setting_dependencies }; /* ../../src/imap-urlauth/imap-urlauth-worker-settings.c */ /* */ static struct file_listener_settings imap_urlauth_worker_unix_listeners_array[] = { { "imap-urlauth-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *imap_urlauth_worker_unix_listeners[] = { &imap_urlauth_worker_unix_listeners_array[0] }; static buffer_t imap_urlauth_worker_unix_listeners_buf = { imap_urlauth_worker_unix_listeners, sizeof(imap_urlauth_worker_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_worker_service_settings = { .name = "imap-urlauth-worker", .protocol = "imap", .type = "", .executable = "imap-urlauth-worker", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_worker_unix_listeners_buf, sizeof(imap_urlauth_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_urlauth_worker_settings, name), NULL } static const struct setting_define imap_urlauth_worker_setting_defines[] = { DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, imap_urlauth_host), DEF(SET_IN_PORT, imap_urlauth_port), SETTING_DEFINE_LIST_END }; const struct imap_urlauth_worker_settings imap_urlauth_worker_default_settings = { .verbose_proctitle = FALSE, .imap_urlauth_host = "", .imap_urlauth_port = 143 }; static const struct setting_parser_info *imap_urlauth_worker_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info imap_urlauth_worker_setting_parser_info = { .module_name = "imap-urlauth-worker", .defines = imap_urlauth_worker_setting_defines, .defaults = &imap_urlauth_worker_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_urlauth_worker_settings), .parent_offset = (size_t)-1, .dependencies = imap_urlauth_worker_setting_dependencies }; /* ../../src/imap-urlauth/imap-urlauth-settings.c */ /* */ static struct file_listener_settings imap_urlauth_unix_listeners_array[] = { { "token-login/imap-urlauth", 0666, "", "" } }; static struct file_listener_settings *imap_urlauth_unix_listeners[] = { &imap_urlauth_unix_listeners_array[0] }; static buffer_t imap_urlauth_unix_listeners_buf = { imap_urlauth_unix_listeners, sizeof(imap_urlauth_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_service_settings = { .name = "imap-urlauth", .protocol = "imap", .type = "", .executable = "imap-urlauth", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1024, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_unix_listeners_buf, sizeof(imap_urlauth_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_urlauth_settings, name), NULL } static const struct setting_define imap_urlauth_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_BOOL, mail_debug), DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, imap_urlauth_logout_format), DEF(SET_STR, imap_urlauth_submit_user), DEF(SET_STR, imap_urlauth_stream_user), SETTING_DEFINE_LIST_END }; const struct imap_urlauth_settings imap_urlauth_default_settings = { .base_dir = PKG_RUNDIR, .mail_debug = FALSE, .verbose_proctitle = FALSE, .imap_urlauth_logout_format = "in=%i out=%o", .imap_urlauth_submit_user = NULL, .imap_urlauth_stream_user = NULL }; static const struct setting_parser_info *imap_urlauth_setting_dependencies[] = { NULL }; const struct setting_parser_info imap_urlauth_setting_parser_info = { .module_name = "imap-urlauth", .defines = imap_urlauth_setting_defines, .defaults = &imap_urlauth_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_urlauth_settings), .parent_offset = (size_t)-1, .dependencies = imap_urlauth_setting_dependencies }; /* ../../src/imap-urlauth/imap-urlauth-login-settings.c */ /* */ static struct file_listener_settings imap_urlauth_login_unix_listeners_array[] = { { "imap-urlauth", 0666, "", "" } }; static struct file_listener_settings *imap_urlauth_login_unix_listeners[] = { &imap_urlauth_login_unix_listeners_array[0] }; static buffer_t imap_urlauth_login_unix_listeners_buf = { imap_urlauth_login_unix_listeners, sizeof(imap_urlauth_login_unix_listeners), { NULL, } }; /* */ struct service_settings imap_urlauth_login_service_settings = { .name = "imap-urlauth-login", .protocol = "imap", .type = "login", .executable = "imap-urlauth-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "token-login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_urlauth_login_unix_listeners_buf, sizeof(imap_urlauth_login_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; static const struct setting_define imap_urlauth_login_setting_defines[] = { SETTING_DEFINE_LIST_END }; static const struct setting_parser_info *imap_urlauth_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; const struct setting_parser_info imap_urlauth_login_setting_parser_info = { .module_name = "imap-urlauth-login", .defines = imap_urlauth_login_setting_defines, .type_offset = (size_t)-1, .parent_offset = (size_t)-1, .dependencies = imap_urlauth_login_setting_dependencies }; const struct setting_parser_info *imap_urlauth_login_setting_roots[] = { &login_setting_parser_info, &imap_urlauth_login_setting_parser_info, NULL }; /* ../../src/imap-login/imap-login-settings.c */ /* */ static struct inet_listener_settings imap_login_inet_listeners_array[] = { { .name = "imap", .address = "", .port = 143 }, { .name = "imaps", .address = "", .port = 993, .ssl = TRUE } }; static struct inet_listener_settings *imap_login_inet_listeners[] = { &imap_login_inet_listeners_array[0], &imap_login_inet_listeners_array[1] }; static buffer_t imap_login_inet_listeners_buf = { imap_login_inet_listeners, sizeof(imap_login_inet_listeners), { NULL, } }; /* */ struct service_settings imap_login_service_settings = { .name = "imap-login", .protocol = "imap", .type = "login", .executable = "imap-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = { { &imap_login_inet_listeners_buf, sizeof(imap_login_inet_listeners[0]) } } }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct imap_login_settings, name), NULL } static const struct setting_define imap_login_setting_defines[] = { DEF(SET_STR, imap_capability), DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), DEF(SET_BOOL, imap_id_retain), SETTING_DEFINE_LIST_END }; static const struct imap_login_settings imap_login_default_settings = { .imap_capability = "", .imap_id_send = "name *", .imap_id_log = "", .imap_id_retain = FALSE, }; static const struct setting_parser_info *imap_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; static const struct setting_parser_info imap_login_setting_parser_info = { .module_name = "imap-login", .defines = imap_login_setting_defines, .defaults = &imap_login_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct imap_login_settings), .parent_offset = (size_t)-1, .dependencies = imap_login_setting_dependencies }; const struct setting_parser_info *imap_login_setting_roots[] = { &login_setting_parser_info, &imap_login_setting_parser_info, NULL }; /* ../../src/imap-hibernate/imap-hibernate-settings.c */ /* */ static struct file_listener_settings imap_hibernate_unix_listeners_array[] = { { "imap-hibernate", 0600, "", "" } }; static struct file_listener_settings *imap_hibernate_unix_listeners[] = { &imap_hibernate_unix_listeners_array[0] }; static buffer_t imap_hibernate_unix_listeners_buf = { imap_hibernate_unix_listeners, sizeof(imap_hibernate_unix_listeners), { NULL, } }; /* */ struct service_settings imap_hibernate_service_settings = { .name = "imap-hibernate", .protocol = "imap", .type = "", .executable = "imap-hibernate", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &imap_hibernate_unix_listeners_buf, sizeof(imap_hibernate_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; /* ../../src/doveadm/doveadm-settings.c */ /* */ static struct file_listener_settings doveadm_unix_listeners_array[] = { { "doveadm-server", 0600, "", "" } }; static struct file_listener_settings *doveadm_unix_listeners[] = { &doveadm_unix_listeners_array[0] }; static buffer_t doveadm_unix_listeners_buf = { doveadm_unix_listeners, sizeof(doveadm_unix_listeners), { NULL, } }; /* */ /* */ struct dsync_feature_list { const char *name; enum dsync_features num; }; static const struct dsync_feature_list dsync_feature_list[] = { { "empty-header-workaround", DSYNC_FEATURE_EMPTY_HDR_WORKAROUND }, { NULL, 0 } }; static int dsync_settings_parse_features(struct doveadm_settings *set, const char **error_r) { enum dsync_features features = 0; const struct dsync_feature_list *list; const char *const *str; str = t_strsplit_spaces(set->dsync_features, " ,"); for (; *str != NULL; str++) { list = dsync_feature_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { features |= list->num; break; } } if (list->name == NULL) { *error_r = t_strdup_printf("dsync_features: " "Unknown feature: %s", *str); return -1; } } set->parsed_features = features; return 0; } static bool doveadm_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct doveadm_settings *set = _set; #ifndef CONFIG_BINARY fix_base_path(set, pool, &set->auth_socket_path); fix_base_path(set, pool, &set->doveadm_socket_path); #endif if (*set->dsync_hashed_headers == '\0') { *error_r = "dsync_hashed_headers must not be empty"; return FALSE; } if (*set->dsync_alt_char == '\0') { *error_r = "dsync_alt_char must not be empty"; return FALSE; } if (dsync_settings_parse_features(set, error_r) != 0) return FALSE; return TRUE; } /* */ struct service_settings doveadm_service_settings = { .name = "doveadm", .protocol = "", .type = "", .executable = "doveadm-server", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &doveadm_unix_listeners_buf, sizeof(doveadm_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct doveadm_settings, name), NULL } static const struct setting_define doveadm_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, libexec_dir), DEF(SET_STR, mail_plugins), DEF(SET_STR, mail_plugin_dir), DEF(SET_BOOL, auth_debug), DEF(SET_STR, auth_socket_path), DEF(SET_STR, doveadm_socket_path), DEF(SET_UINT, doveadm_worker_count), DEF(SET_IN_PORT, doveadm_port), { SET_ALIAS, "doveadm_proxy_port", 0, NULL }, DEF(SET_STR, doveadm_username), DEF(SET_STR, doveadm_password), DEF(SET_STR, doveadm_allowed_commands), DEF(SET_STR, dsync_alt_char), DEF(SET_STR, dsync_remote_cmd), DEF(SET_STR, ssl_client_ca_dir), DEF(SET_STR, ssl_client_ca_file), DEF(SET_STR, director_username_hash), DEF(SET_STR, doveadm_api_key), DEF(SET_STR, dsync_features), DEF(SET_UINT, dsync_commit_msgs_interval), DEF(SET_STR, doveadm_http_rawlog_dir), DEF(SET_STR, dsync_hashed_headers), { SET_STRLIST, "plugin", offsetof(struct doveadm_settings, plugin_envs), NULL }, SETTING_DEFINE_LIST_END }; const struct doveadm_settings doveadm_default_settings = { .base_dir = PKG_RUNDIR, .libexec_dir = PKG_LIBEXECDIR, .mail_plugins = "", .mail_plugin_dir = MODULEDIR, .auth_debug = FALSE, .auth_socket_path = "auth-userdb", .doveadm_socket_path = "doveadm-server", .doveadm_worker_count = 0, .doveadm_port = 0, .doveadm_username = "doveadm", .doveadm_password = "", .doveadm_allowed_commands = "", .dsync_alt_char = "_", .dsync_remote_cmd = "ssh -l%{login} %{host} doveadm dsync-server -u%u -U", .dsync_features = "", .dsync_hashed_headers = "Date Message-ID", .dsync_commit_msgs_interval = 100, .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .director_username_hash = "%Lu", .doveadm_api_key = "", .doveadm_http_rawlog_dir = "", .plugin_envs = ARRAY_INIT }; static const struct setting_parser_info *doveadm_setting_dependencies[] = { &mail_user_setting_parser_info, NULL }; const struct setting_parser_info doveadm_setting_parser_info = { .module_name = "doveadm", .defines = doveadm_setting_defines, .defaults = &doveadm_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct doveadm_settings), .parent_offset = (size_t)-1, .check_func = doveadm_settings_check, .dependencies = doveadm_setting_dependencies }; /* ../../src/dns/dns-client-settings.c */ /* */ static struct file_listener_settings dns_client_unix_listeners_array[] = { { "dns-client", 0666, "", "" } }; static struct file_listener_settings *dns_client_unix_listeners[] = { &dns_client_unix_listeners_array[0] }; static buffer_t dns_client_unix_listeners_buf = { dns_client_unix_listeners, sizeof(dns_client_unix_listeners), { NULL, } }; /* */ struct service_settings dns_client_service_settings = { .name = "dns_client", .protocol = "", .type = "", .executable = "dns-client", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dns_client_unix_listeners_buf, sizeof(dns_client_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; /* ../../src/director/director-settings.c */ /* */ static bool director_settings_verify(void *_set, pool_t pool, const char **error_r); static struct file_listener_settings director_unix_listeners_array[] = { { "login/director", 0, "", "" }, { "director-admin", 0600, "", "" } }; static struct file_listener_settings *director_unix_listeners[] = { &director_unix_listeners_array[0], &director_unix_listeners_array[1] }; static buffer_t director_unix_listeners_buf = { director_unix_listeners, sizeof(director_unix_listeners), { NULL, } }; static struct file_listener_settings director_fifo_listeners_array[] = { { "login/proxy-notify", 0, "", "" } }; static struct file_listener_settings *director_fifo_listeners[] = { &director_fifo_listeners_array[0] }; static buffer_t director_fifo_listeners_buf = { director_fifo_listeners, sizeof(director_fifo_listeners), { NULL, } }; /* */ /* */ static bool director_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct director_settings *set = _set; if (set->director_user_expire < 10) { *error_r = "director_user_expire is too low"; return FALSE; } return TRUE; } /* */ struct service_settings director_service_settings = { .name = "director", .protocol = "", .type = "", .executable = "director", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = ".", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &director_unix_listeners_buf, sizeof(director_unix_listeners[0]) } }, .fifo_listeners = { { &director_fifo_listeners_buf, sizeof(director_fifo_listeners[0]) } }, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct director_settings, name), NULL } static const struct setting_define director_setting_defines[] = { DEF(SET_STR, master_user_separator), DEF(SET_STR, director_servers), DEF(SET_STR, director_mail_servers), DEF(SET_STR, director_username_hash), DEF(SET_STR, director_flush_socket), DEF(SET_TIME, director_user_expire), DEF(SET_TIME, director_user_kick_delay), DEF(SET_IN_PORT, director_doveadm_port), DEF(SET_BOOL, director_consistent_hashing), SETTING_DEFINE_LIST_END }; const struct director_settings director_default_settings = { .master_user_separator = "", .director_servers = "", .director_mail_servers = "", .director_username_hash = "%Lu", .director_flush_socket = "", .director_user_expire = 60*15, .director_user_kick_delay = 2, .director_doveadm_port = 0 }; const struct setting_parser_info director_setting_parser_info = { .module_name = "director", .defines = director_setting_defines, .defaults = &director_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct director_settings), .parent_offset = (size_t)-1, .check_func = director_settings_verify }; /* ../../src/dict/dict-settings.c */ /* */ static struct file_listener_settings dict_unix_listeners_array[] = { { "dict", 0600, "", "" } }; static struct file_listener_settings *dict_unix_listeners[] = { &dict_unix_listeners_array[0] }; static buffer_t dict_unix_listeners_buf = { dict_unix_listeners, sizeof(dict_unix_listeners), { NULL, } }; static struct file_listener_settings dict_async_unix_listeners_array[] = { { "dict-async", 0600, "", "" } }; static struct file_listener_settings *dict_async_unix_listeners[] = { &dict_async_unix_listeners_array[0] }; static buffer_t dict_async_unix_listeners_buf = { dict_async_unix_listeners, sizeof(dict_async_unix_listeners), { NULL, } }; /* */ struct service_settings dict_service_settings = { .name = "dict", .protocol = "", .type = "", .executable = "dict", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dict_unix_listeners_buf, sizeof(dict_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; struct service_settings dict_async_service_settings = { .name = "dict-async", .protocol = "", .type = "", .executable = "dict", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &dict_async_unix_listeners_buf, sizeof(dict_async_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct dict_server_settings, name), NULL } static const struct setting_define dict_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_BOOL, verbose_proctitle), DEF(SET_STR, dict_db_config), { SET_STRLIST, "dict", offsetof(struct dict_server_settings, dicts), NULL }, SETTING_DEFINE_LIST_END }; const struct dict_server_settings dict_default_settings = { .base_dir = PKG_RUNDIR, .verbose_proctitle = FALSE, .dict_db_config = "", .dicts = ARRAY_INIT }; const struct setting_parser_info dict_setting_parser_info = { .module_name = "dict", .defines = dict_setting_defines, .defaults = &dict_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct dict_server_settings), .parent_offset = (size_t)-1 }; /* ../../src/config/config-settings.c */ /* */ static struct file_listener_settings config_unix_listeners_array[] = { { "config", 0600, "", "" } }; static struct file_listener_settings *config_unix_listeners[] = { &config_unix_listeners_array[0] }; static buffer_t config_unix_listeners_buf = { config_unix_listeners, sizeof(config_unix_listeners), { NULL, } }; /* */ struct service_settings config_service_settings = { .name = "config", .protocol = "", .type = "config", .executable = "config", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &config_unix_listeners_buf, sizeof(config_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; /* ../../src/auth/auth-settings.c */ extern const struct setting_parser_info auth_passdb_setting_parser_info; extern const struct setting_parser_info auth_userdb_setting_parser_info; /* */ static struct file_listener_settings auth_unix_listeners_array[] = { { "login/login", 0666, "", "" }, { "token-login/tokenlogin", 0666, "", "" }, { "auth-login", 0600, "$default_internal_user", "" }, { "auth-client", 0600, "$default_internal_user", "" }, { "auth-userdb", 0666, "$default_internal_user", "" }, { "auth-master", 0600, "", "" } }; static struct file_listener_settings *auth_unix_listeners[] = { &auth_unix_listeners_array[0], &auth_unix_listeners_array[1], &auth_unix_listeners_array[2], &auth_unix_listeners_array[3], &auth_unix_listeners_array[4], &auth_unix_listeners_array[5] }; static buffer_t auth_unix_listeners_buf = { auth_unix_listeners, sizeof(auth_unix_listeners), { NULL, } }; /* */ /* */ static struct file_listener_settings auth_worker_unix_listeners_array[] = { { "auth-worker", 0600, "$default_internal_user", "" } }; static struct file_listener_settings *auth_worker_unix_listeners[] = { &auth_worker_unix_listeners_array[0] }; static buffer_t auth_worker_unix_listeners_buf = { auth_worker_unix_listeners, sizeof(auth_worker_unix_listeners), { NULL, } }; /* */ /* */ static bool auth_settings_set_self_ips(struct auth_settings *set, pool_t pool, const char **error_r) { const char *const *tmp; ARRAY(struct ip_addr) ips_array; struct ip_addr *ips; unsigned int ips_count; int ret; if (*set->proxy_self == '\0') { set->proxy_self_ips = p_new(pool, struct ip_addr, 1); return TRUE; } p_array_init(&ips_array, pool, 4); tmp = t_strsplit_spaces(set->proxy_self, " "); for (; *tmp != NULL; tmp++) { ret = net_gethostbyname(*tmp, &ips, &ips_count); if (ret != 0) { *error_r = t_strdup_printf("auth_proxy_self_ips: " "gethostbyname(%s) failed: %s", *tmp, net_gethosterror(ret)); } array_append(&ips_array, ips, ips_count); } array_append_zero(&ips_array); set->proxy_self_ips = array_idx(&ips_array, 0); return TRUE; } static bool auth_verify_verbose_password(struct auth_settings *set, const char **error_r) { const char *p, *value = set->verbose_passwords; unsigned int num; p = strchr(value, ':'); if (p != NULL) { if (str_to_uint(p+1, &num) < 0 || num == 0) { *error_r = t_strdup_printf("auth_verbose_passwords: " "Invalid truncation number: '%s'", p+1); return FALSE; } value = t_strdup_until(value, p); } if (strcmp(value, "no") == 0) return TRUE; else if (strcmp(value, "plain") == 0) return TRUE; else if (strcmp(value, "sha1") == 0) return TRUE; else if (strcmp(value, "yes") == 0) { /* just use it as alias for "plain" */ set->verbose_passwords = "plain"; return TRUE; } else { *error_r = "auth_verbose_passwords: Invalid value"; return FALSE; } } static bool auth_settings_check(void *_set, pool_t pool, const char **error_r) { struct auth_settings *set = _set; const char *p; if (set->debug_passwords) set->debug = TRUE; if (set->debug) set->verbose = TRUE; if (set->worker_max_count == 0) { *error_r = "auth_worker_max_count must be above zero"; return FALSE; } if (set->cache_size > 0 && set->cache_size < 1024) { /* probably a configuration error. older versions used megabyte numbers */ *error_r = t_strdup_printf("auth_cache_size value is too small " "(%"PRIuUOFF_T" bytes)", set->cache_size); return FALSE; } if (!auth_verify_verbose_password(set, error_r)) return FALSE; if (*set->username_chars == '\0') { /* all chars are allowed */ memset(set->username_chars_map, 1, sizeof(set->username_chars_map)); } else { for (p = set->username_chars; *p != '\0'; p++) set->username_chars_map[(int)(uint8_t)*p] = 1; } if (*set->username_translation != '\0') { p = set->username_translation; for (; *p != '\0' && p[1] != '\0'; p += 2) set->username_translation_map[(int)(uint8_t)*p] = p[1]; } set->realms_arr = (const char *const *)p_strsplit_spaces(pool, set->realms, " "); if (*set->policy_server_url != '\0') { if (*set->policy_hash_nonce == '\0') { *error_r = "auth_policy_hash_nonce must be set when policy server is used"; return FALSE; } const struct hash_method *digest = hash_method_lookup(set->policy_hash_mech); if (digest == NULL) { *error_r = "invalid auth_policy_hash_mech given"; return FALSE; } if (set->policy_hash_truncate > 0 && set->policy_hash_truncate >= digest->digest_size*8) { *error_r = t_strdup_printf("policy_hash_truncate is not smaller than digest size (%u >= %u)", set->policy_hash_truncate, digest->digest_size*8); return FALSE; } } if (!auth_settings_set_self_ips(set, pool, error_r)) return FALSE; return TRUE; } static bool auth_passdb_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct auth_passdb_settings *set = _set; if (set->driver == NULL || *set->driver == '\0') { *error_r = "passdb is missing driver"; return FALSE; } if (set->pass && strcmp(set->result_success, "return-ok") != 0) { *error_r = "Obsolete pass=yes setting mixed with non-default result_success"; return FALSE; } return TRUE; } static bool auth_userdb_settings_check(void *_set, pool_t pool ATTR_UNUSED, const char **error_r) { struct auth_userdb_settings *set = _set; if (set->driver == NULL || *set->driver == '\0') { *error_r = "userdb is missing driver"; return FALSE; } return TRUE; } /* */ struct service_settings auth_service_settings = { .name = "auth", .protocol = "", .type = "", .executable = "auth", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &auth_unix_listeners_buf, sizeof(auth_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; struct service_settings auth_worker_service_settings = { .name = "auth-worker", .protocol = "", .type = "", .executable = "auth -w", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 1, .service_count = 1, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &auth_worker_unix_listeners_buf, sizeof(auth_worker_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct auth_passdb_settings, name), NULL } static const struct setting_define auth_passdb_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, driver), DEF(SET_STR, args), DEF(SET_STR, default_fields), DEF(SET_STR, override_fields), DEF(SET_STR, mechanisms), DEF(SET_STR, username_filter), DEF(SET_ENUM, skip), DEF(SET_ENUM, result_success), DEF(SET_ENUM, result_failure), DEF(SET_ENUM, result_internalfail), DEF(SET_BOOL, deny), DEF(SET_BOOL, pass), DEF(SET_BOOL, master), DEF(SET_ENUM, auth_verbose), SETTING_DEFINE_LIST_END }; static const struct auth_passdb_settings auth_passdb_default_settings = { .name = "", .driver = "", .args = "", .default_fields = "", .override_fields = "", .mechanisms = "", .username_filter = "", .skip = "never:authenticated:unauthenticated", .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail", .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .deny = FALSE, .pass = FALSE, .master = FALSE, .auth_verbose = "default:yes:no" }; const struct setting_parser_info auth_passdb_setting_parser_info = { .defines = auth_passdb_setting_defines, .defaults = &auth_passdb_default_settings, .type_offset = offsetof(struct auth_passdb_settings, name), .struct_size = sizeof(struct auth_passdb_settings), .parent_offset = (size_t)-1, .parent = &auth_setting_parser_info, .check_func = auth_passdb_settings_check }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct auth_userdb_settings, name), NULL } static const struct setting_define auth_userdb_setting_defines[] = { DEF(SET_STR, name), DEF(SET_STR, driver), DEF(SET_STR, args), DEF(SET_STR, default_fields), DEF(SET_STR, override_fields), DEF(SET_ENUM, skip), DEF(SET_ENUM, result_success), DEF(SET_ENUM, result_failure), DEF(SET_ENUM, result_internalfail), DEF(SET_ENUM, auth_verbose), SETTING_DEFINE_LIST_END }; static const struct auth_userdb_settings auth_userdb_default_settings = { /* NOTE: when adding fields, update also auth.c:userdb_dummy_set */ .name = "", .driver = "", .args = "", .default_fields = "", .override_fields = "", .skip = "never:found:notfound", .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail", .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail", .auth_verbose = "default:yes:no" }; const struct setting_parser_info auth_userdb_setting_parser_info = { .defines = auth_userdb_setting_defines, .defaults = &auth_userdb_default_settings, .type_offset = offsetof(struct auth_userdb_settings, name), .struct_size = sizeof(struct auth_userdb_settings), .parent_offset = (size_t)-1, .parent = &auth_setting_parser_info, .check_func = auth_userdb_settings_check }; #undef DEF #undef DEF_NOPREFIX #undef DEFLIST #define DEF(type, name) \ { type, "auth_"#name, offsetof(struct auth_settings, name), NULL } #define DEF_NOPREFIX(type, name) \ { type, #name, offsetof(struct auth_settings, name), NULL } #define DEFLIST(field, name, defines) \ { SET_DEFLIST, name, offsetof(struct auth_settings, field), defines } static const struct setting_define auth_setting_defines[] = { DEF(SET_STR, mechanisms), DEF(SET_STR, realms), DEF(SET_STR, default_realm), DEF(SET_SIZE, cache_size), DEF(SET_TIME, cache_ttl), DEF(SET_TIME, cache_negative_ttl), DEF(SET_STR, username_chars), DEF(SET_STR, username_translation), DEF(SET_STR, username_format), DEF(SET_STR, master_user_separator), DEF(SET_STR, anonymous_username), DEF(SET_STR, krb5_keytab), DEF(SET_STR, gssapi_hostname), DEF(SET_STR, winbind_helper_path), DEF(SET_STR, proxy_self), DEF(SET_TIME, failure_delay), DEF(SET_STR, policy_server_url), DEF(SET_STR, policy_server_api_header), DEF(SET_UINT, policy_server_timeout_msecs), DEF(SET_STR, policy_hash_mech), DEF(SET_STR, policy_hash_nonce), DEF(SET_STR, policy_request_attributes), DEF(SET_BOOL, policy_reject_on_fail), DEF(SET_UINT, policy_hash_truncate), DEF(SET_BOOL, stats), DEF(SET_BOOL, verbose), DEF(SET_BOOL, debug), DEF(SET_BOOL, debug_passwords), DEF(SET_STR, verbose_passwords), DEF(SET_BOOL, ssl_require_client_cert), DEF(SET_BOOL, ssl_username_from_cert), DEF(SET_BOOL, use_winbind), DEF(SET_UINT, worker_max_count), DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info), DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info), DEF_NOPREFIX(SET_STR, base_dir), DEF_NOPREFIX(SET_BOOL, verbose_proctitle), DEF_NOPREFIX(SET_UINT, first_valid_uid), DEF_NOPREFIX(SET_UINT, last_valid_uid), DEF_NOPREFIX(SET_STR, ssl_client_ca_dir), DEF_NOPREFIX(SET_STR, ssl_client_ca_file), SETTING_DEFINE_LIST_END }; static const struct auth_settings auth_default_settings = { .mechanisms = "plain", .realms = "", .default_realm = "", .cache_size = 0, .cache_ttl = 60*60, .cache_negative_ttl = 60*60, .username_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@", .username_translation = "", .username_format = "%Lu", .master_user_separator = "", .anonymous_username = "anonymous", .krb5_keytab = "", .gssapi_hostname = "", .winbind_helper_path = "/usr/bin/ntlm_auth", .proxy_self = "", .failure_delay = 2, .policy_server_url = "", .policy_server_api_header = "", .policy_server_timeout_msecs = 2000, .policy_hash_mech = "sha256", .policy_hash_nonce = "", .policy_request_attributes = "login=%{orig_username} pwhash=%{hashed_password} remote=%{real_rip} device_id=%{client_id} protocol=%s", .policy_reject_on_fail = FALSE, .policy_hash_truncate = 12, .stats = FALSE, .verbose = FALSE, .debug = FALSE, .debug_passwords = FALSE, .verbose_passwords = "no", .ssl_require_client_cert = FALSE, .ssl_username_from_cert = FALSE, .ssl_client_ca_dir = "", .ssl_client_ca_file = "", .use_winbind = FALSE, .worker_max_count = 30, .passdbs = ARRAY_INIT, .userdbs = ARRAY_INIT, .base_dir = PKG_RUNDIR, .verbose_proctitle = FALSE, .first_valid_uid = 500, .last_valid_uid = 0, }; const struct setting_parser_info auth_setting_parser_info = { .module_name = "auth", .defines = auth_setting_defines, .defaults = &auth_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct auth_settings), .parent_offset = (size_t)-1, .check_func = auth_settings_check }; /* ../../src/anvil/anvil-settings.c */ /* */ static struct file_listener_settings anvil_unix_listeners_array[] = { { "anvil", 0600, "", "" }, { "anvil-auth-penalty", 0600, "", "" } }; static struct file_listener_settings *anvil_unix_listeners[] = { &anvil_unix_listeners_array[0], &anvil_unix_listeners_array[1] }; static buffer_t anvil_unix_listeners_buf = { anvil_unix_listeners, sizeof(anvil_unix_listeners), { NULL, } }; /* */ struct service_settings anvil_service_settings = { .name = "anvil", .protocol = "", .type = "anvil", .executable = "anvil", .user = "$default_internal_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "empty", .drop_priv_before_exec = FALSE, .process_min_avail = 1, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &anvil_unix_listeners_buf, sizeof(anvil_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; static struct service_settings *config_all_services[] = { #ifdef HAVE_LIBWRAP &tcpwrap_service_settings, #endif &stats_service_settings, &ssl_params_service_settings, &replicator_service_settings, &aggregator_service_settings, &pop3_service_settings, &pop3_login_service_settings, &log_service_settings, &lmtp_service_settings, &ipc_service_settings, &indexer_worker_service_settings, &indexer_service_settings, &imap_service_settings, &imap_urlauth_worker_service_settings, &imap_urlauth_service_settings, &imap_urlauth_login_service_settings, &imap_login_service_settings, &imap_hibernate_service_settings, &doveadm_service_settings, &dns_client_service_settings, &director_service_settings, &dict_service_settings, &dict_async_service_settings, &config_service_settings, &auth_service_settings, &auth_worker_service_settings, &anvil_service_settings, }; buffer_t config_all_services_buf = { config_all_services, sizeof(config_all_services), { NULL, } }; const struct setting_parser_info *all_default_roots[] = { &master_service_setting_parser_info, &master_service_ssl_setting_parser_info, &aggregator_setting_parser_info, &imapc_setting_parser_info, &imap_urlauth_setting_parser_info, &imap_urlauth_worker_setting_parser_info, &imap_login_setting_parser_info, &stats_setting_parser_info, &pop3_setting_parser_info, &ssl_params_setting_parser_info, &director_setting_parser_info, &imap_setting_parser_info, &dict_setting_parser_info, &replicator_setting_parser_info, &login_setting_parser_info, &mdbox_setting_parser_info, &imap_urlauth_login_setting_parser_info, &mbox_setting_parser_info, &lda_setting_parser_info, &pop3c_setting_parser_info, &mail_storage_setting_parser_info, &doveadm_setting_parser_info, &maildir_setting_parser_info, &mail_user_setting_parser_info, "a_status_setting_parser_info, &fs_crypt_setting_parser_info, &auth_setting_parser_info, &master_setting_parser_info, &pop3_login_setting_parser_info, &lmtp_setting_parser_info, NULL }; const struct setting_parser_info *const *all_roots = all_default_roots; ARRAY_TYPE(service_settings) *default_services = &master_default_settings.services; dovecot-2.2.33.2/src/config/config-request.h0000644000175000017500000000333613165463543015504 00000000000000#ifndef CONFIG_REQUEST_H #define CONFIG_REQUEST_H #include "settings-parser.h" #include "config-filter.h" struct master_service_settings_output; enum config_dump_scope { /* Dump all settings */ CONFIG_DUMP_SCOPE_ALL, /* Dump all that have explicitly been set */ CONFIG_DUMP_SCOPE_SET, /* Dump only settings that differ from defaults */ CONFIG_DUMP_SCOPE_CHANGED }; enum config_dump_flags { CONFIG_DUMP_FLAG_CHECK_SETTINGS = 0x01, CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS = 0x02, /* Errors are reported using callback and they don't stop handling */ CONFIG_DUMP_FLAG_CALLBACK_ERRORS = 0x04 }; enum config_key_type { CONFIG_KEY_NORMAL, CONFIG_KEY_LIST, CONFIG_KEY_UNIQUE_KEY, /* error message is in value */ CONFIG_KEY_ERROR }; typedef void config_request_callback_t(const char *key, const char *value, enum config_key_type type, void *context); bool config_export_type(string_t *str, const void *value, const void *default_value, enum setting_type type, bool dump_default, bool *dump_r) ATTR_NULL(3); struct config_export_context * config_export_init(const char *const *modules, enum config_dump_scope scope, enum config_dump_flags flags, config_request_callback_t *callback, void *context) ATTR_NULL(1, 5); void config_export_by_filter(struct config_export_context *ctx, const struct config_filter *filter); void config_export_parsers(struct config_export_context *ctx, const struct config_module_parser *parsers); void config_export_get_output(struct config_export_context *ctx, struct master_service_settings_output *output_r); const char * config_export_get_import_environment(struct config_export_context *ctx); int config_export_finish(struct config_export_context **ctx); #endif dovecot-2.2.33.2/src/config/sysinfo-get.c0000644000175000017500000000542313123174404014777 00000000000000/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mountpoint.h" #include "strescape.h" #include "sysinfo-get.h" #include #include #include #ifdef HAVE_SYS_UTSNAME_H # include #endif static bool readfile(const char *path, const char **data_r) { char buf[1024]; int fd, ret; fd = open(path, O_RDONLY); if (fd == -1) return FALSE; ret = read(fd, buf, sizeof(buf)); i_close_fd(&fd); if (ret <= 0) return FALSE; *data_r = t_strndup(buf, ret); return TRUE; } static bool lsb_distro_get(const char *path, const char **name_r) { const char *data, *const *p, *str, *end; if (!readfile(path, &data)) return FALSE; for (p = t_strsplit(data, "\n"); *p != NULL; p++) { if (strncmp(*p, "DISTRIB_DESCRIPTION=", 20) == 0) break; } if (*p == NULL) return FALSE; str = t_strcut(*p + 20, '\n'); if (*str != '"') *name_r = str; else { end = strrchr(++str, '"'); *name_r = str_unescape(p_strdup_until(unsafe_data_stack_pool, str, end)); } return TRUE; } static const char *distro_get(void) { static const char *files[] = { "", "/etc/redhat-release", "", "/etc/SuSE-release", "", "/etc/mandriva-release", "", "/etc/fedora-release", "", "/etc/sourcemage-release", "", "/etc/slackware-version", "", "/etc/gentoo-release", "Debian ", "/etc/debian_version", NULL }; const char *name; unsigned int i; if (lsb_distro_get("/etc/lsb-release", &name)) return name; for (i = 0; files[i] != NULL; i += 2) { if (readfile(files[i+1], &name)) { return t_strconcat(files[i], t_strcut(name, '\n'), NULL); } } return ""; } static const char *filesystem_get(const char *mail_location) { struct mountpoint mp; const char *path; path = strchr(mail_location, ':'); if (path == NULL) path = mail_location; else path = t_strcut(path + 1, ':'); if (*path == '~') { /* we don't know where users' home dirs are */ return ""; } path = t_strcut(path, '%'); if (strlen(path) <= 1) return ""; /* all in all it seems we can support only //%u style location */ if (mountpoint_get(path, pool_datastack_create(), &mp) < 0) return ""; return mp.type == NULL ? "" : mp.type; } const char *sysinfo_get(const char *mail_location) { const char *distro = "", *fs, *uname_info = ""; #ifdef HAVE_SYS_UTSNAME_H struct utsname u; if (uname(&u) < 0) i_error("uname() failed: %m"); else { uname_info = t_strdup_printf("%s %s %s", u.sysname, u.release, u.machine); } if (strcmp(u.sysname, "Linux") == 0) distro = distro_get(); #endif fs = filesystem_get(mail_location); if (*uname_info == '\0' && *distro == '\0' && *fs == '\0') return ""; return t_strdup_printf("OS: %s %s %s %s %s", u.sysname, u.release, u.machine, distro, fs); } dovecot-2.2.33.2/src/config/config-parser.h0000644000175000017500000000174213123174404015274 00000000000000#ifndef CONFIG_PARSER_H #define CONFIG_PARSER_H #define CONFIG_MODULE_DIR MODULEDIR"/settings" #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') struct config_module_parser { const struct setting_parser_info *root; struct setting_parser_context *parser; void *settings; }; ARRAY_DEFINE_TYPE(config_module_parsers, struct config_module_parser *); extern struct config_module_parser *config_module_parsers; extern struct config_filter_context *config_filter; extern struct module *modules; int config_parse_net(const char *value, struct ip_addr *ip_r, unsigned int *bits_r, const char **error_r); int config_parse_file(const char *path, bool expand_values, const char *const *modules, const char **error_r) ATTR_NULL(3); void config_parse_load_modules(void); bool config_module_want_parser(struct config_module_parser *parsers, const char *const *modules, const struct setting_parser_info *root) ATTR_NULL(2); void config_parser_deinit(void); #endif dovecot-2.2.33.2/src/config/old-set-parser.c0000644000175000017500000004761313165463624015413 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "settings-parser.h" #include "config-parser-private.h" #include "old-set-parser.h" #define config_apply_line (void)config_apply_line struct socket_set { const char *path, *mode, *user, *group; bool master; }; struct old_set_parser { const char *base_dir; /* 1 when in auth {} section, >1 when inside auth { .. { .. } } */ unsigned int auth_section; /* 1 when in socket listen {}, >1 when inside more of its sections */ unsigned int socket_listen_section; bool seen_auth_section; struct socket_set socket_set; }; static const struct config_filter any_filter = { .service = NULL }; static const struct config_filter imap_filter = { .service = "imap" }; static const struct config_filter pop3_filter = { .service = "pop3" }; static const struct config_filter managesieve_filter = { .service = "sieve" }; static void ATTR_FORMAT(2, 3) obsolete(struct config_parser_context *ctx, const char *str, ...) { static bool seen_obsoletes = FALSE; va_list args; if (!seen_obsoletes) { i_warning("NOTE: You can get a new clean config file with: " "doveconf -n > dovecot-new.conf"); seen_obsoletes = TRUE; } va_start(args, str); i_warning("Obsolete setting in %s:%u: %s", ctx->cur_input->path, ctx->cur_input->linenum, t_strdup_vprintf(str, args)); va_end(args); } static void set_rename(struct config_parser_context *ctx, const char *old_key, const char *key, const char *value) { obsolete(ctx, "%s has been renamed to %s", old_key, key); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value); } static bool old_settings_handle_root(struct config_parser_context *ctx, const char *key, const char *value) { const char *p; size_t len; if (strcmp(key, "base_dir") == 0) { len = strlen(value); if (len > 0 && value[len-1] == '/') value = t_strndup(value, len-1); ctx->old->base_dir = p_strdup(ctx->pool, value); } if (strcmp(key, "protocols") == 0) { char **protos, **s; bool have_imap = FALSE, have_imaps = FALSE; bool have_pop3 = FALSE, have_pop3s = FALSE; protos = p_strsplit_spaces(pool_datastack_create(), value, " "); for (s = protos; *s != NULL; s++) { if (strcmp(*s, "imap") == 0) have_imap = TRUE; else if (strcmp(*s, "imaps") == 0) { *s = ""; have_imaps = TRUE; } else if (strcmp(*s, "pop3") == 0) have_pop3 = TRUE; else if (strcmp(*s, "pop3s") == 0) { *s = ""; have_pop3s = TRUE; } else if (strcmp(*s, "managesieve") == 0) { *s = "sieve"; obsolete(ctx, "protocols=managesieve has been renamed to protocols=sieve"); } } value = t_strarray_join((const char *const *)protos, " "); /* ugly way to drop extra spaces.. */ protos = p_strsplit_spaces(pool_datastack_create(), value, " "); value = t_strarray_join((const char *const *)protos, " "); if (have_imaps && !have_imap) { obsolete(ctx, "'imaps' protocol can no longer be specified (use protocols=imap). to disable non-ssl imap, use service imap-login { inet_listener imap { port=0 } }"); value = t_strconcat(value, " imap", NULL); config_apply_line(ctx, "port", "service/imap-login/inet_listener/imap/port=0", NULL); } else if (have_imaps) obsolete(ctx, "'imaps' protocol is no longer necessary, remove it"); if (have_pop3s && !have_pop3) { obsolete(ctx, "'pop3s' protocol can no longer be specified (use protocols=pop3). to disable non-ssl pop3, use service pop3-login { inet_listener pop3 { port=0 } }"); value = t_strconcat(value, " pop3", NULL); config_apply_line(ctx, "port", "service/pop3-login/inet_listener/pop3/port=0", NULL); } else if (have_pop3s) obsolete(ctx, "'pop3s' protocol is no longer necessary, remove it"); if (*value == ' ') value++; config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value); return TRUE; } if (strcmp(key, "ssl_cert_file") == 0 || strcmp(key, "ssl_key_file") == 0 || strcmp(key, "ssl_ca_file") == 0) { if (*value == '\0') return TRUE; p = t_strdup_until(key, strrchr(key, '_')); obsolete(ctx, "%s has been replaced by %s = old->auth_section == 1) { if (strncmp(key, "auth_", 5) != 0) key = t_strconcat("auth_", key, NULL); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value); return TRUE; } return FALSE; } static void config_apply_login_set(struct config_parser_context *ctx, struct config_section_stack *old_section, const char *old_key, const char *key, const char *value) { obsolete(ctx, "%s has been replaced by service { %s }", old_key, key); if (config_filter_match(&old_section->filter, &imap_filter)) { config_apply_line(ctx, key, t_strdup_printf("service/imap-login/%s=%s", key, value), NULL); } if (config_filter_match(&old_section->filter, &pop3_filter)) { config_apply_line(ctx, key, t_strdup_printf("service/pop3-login/%s=%s", key, value), NULL); } if (config_filter_match(&old_section->filter, &managesieve_filter)) { /* if pigeonhole isn't installed, this fails. just ignore it then.. */ config_apply_line(ctx, key, t_strdup_printf("service/managesieve-login/%s=%s", key, value), NULL); ctx->error = NULL; } } static void config_apply_mail_set(struct config_parser_context *ctx, struct config_section_stack *old_section, const char *old_key, const char *key, const char *value) { obsolete(ctx, "%s has been replaced by service { %s }", old_key, key); if (config_filter_match(&old_section->filter, &imap_filter)) { config_apply_line(ctx, key, t_strdup_printf("service/imap/%s=%s", key,value), NULL); } if (config_filter_match(&old_section->filter, &pop3_filter)) { config_apply_line(ctx, key, t_strdup_printf("service/pop3/%s=%s", key,value), NULL); } if (config_filter_match(&old_section->filter, &managesieve_filter)) { config_apply_line(ctx, key, t_strdup_printf("service/managesieve/%s=%s", key,value), NULL); ctx->error = NULL; } } static void config_apply_auth_set(struct config_parser_context *ctx, const char *old_key, const char *key, const char *value) { obsolete(ctx, "%s has been replaced by service auth { %s }", old_key, key); config_apply_line(ctx, key, t_strdup_printf("service/auth/%s=%s", key,value), NULL); } static bool listen_has_port(const char *str) { const char *const *addrs; if (strchr(str, ':') == NULL) return FALSE; addrs = t_strsplit_spaces(str, ", "); for (; *addrs != NULL; addrs++) { if (strcmp(*addrs, "*") != 0 && strcmp(*addrs, "::") != 0 && strcmp(*addrs, "[::]") != 0 && !is_ipv4_address(*addrs) && !is_ipv6_address(*addrs)) return TRUE; } return FALSE; } static bool old_settings_handle_proto(struct config_parser_context *ctx, const char *key, const char *value) { struct config_section_stack *old_section = ctx->cur_section; const char *p; uoff_t size; bool root; while (ctx->cur_section->prev != NULL) ctx->cur_section = ctx->cur_section->prev; root = config_filter_match(&old_section->filter, &any_filter); if (strcmp(key, "ssl_listen") == 0 || (strcmp(key, "listen") == 0 && (listen_has_port(value) || !root))) { const char *ssl = strcmp(key, "ssl_listen") == 0 ? "s" : ""; if (*value == '\0') { /* default */ return TRUE; } p = strrchr(value, ':'); if (p != NULL && listen_has_port(value)) { obsolete(ctx, "%s=..:port has been replaced by service { inet_listener { port } }", key); value = t_strdup_until(value, p++); if (config_filter_match(&old_section->filter, &imap_filter)) { config_apply_line(ctx, "port", t_strdup_printf("service/imap-login/inet_listener/imap%s/port=%s", ssl, p), NULL); } if (config_filter_match(&old_section->filter, &pop3_filter)) { config_apply_line(ctx, "port", t_strdup_printf("service/pop3-login/inet_listener/pop3%s/port=%s", ssl, p), NULL); } if (*ssl == '\0' && config_filter_match(&old_section->filter, &managesieve_filter)) { config_apply_line(ctx, "port", t_strdup_printf("service/managesieve-login/inet_listener/managesieve/port=%s", p), NULL); ctx->error = NULL; } } if (root && *ssl == '\0') { config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value); } else { obsolete(ctx, "protocol { %s } has been replaced by service { inet_listener { address } }", key); if (config_filter_match(&old_section->filter, &imap_filter)) { config_apply_line(ctx, "address", t_strdup_printf("service/imap-login/inet_listener/imap%s/address=%s", ssl, value), NULL); } if (config_filter_match(&old_section->filter, &pop3_filter)) { config_apply_line(ctx, "address", t_strdup_printf("service/pop3-login/inet_listener/pop3%s/address=%s", ssl, value), NULL); } if (*ssl == '\0' && config_filter_match(&old_section->filter, &managesieve_filter)) { config_apply_line(ctx, "address", t_strdup_printf("service/managesieve-login/inet_listener/managesieve/address=%s", value), NULL); ctx->error = NULL; } } return TRUE; } if (strcmp(key, "login_chroot") == 0) { if (strcmp(value, "no") == 0) value = ""; else value = "login"; config_apply_login_set(ctx, old_section, key, "chroot", value); return TRUE; } if (strcmp(key, "login_user") == 0) { config_apply_login_set(ctx, old_section, key, "user", value); return TRUE; } if (strcmp(key, "login_executable") == 0) { config_apply_login_set(ctx, old_section, key, "executable", value); return TRUE; } if (strcmp(key, "login_process_size") == 0) { config_apply_login_set(ctx, old_section, key, "vsz_limit", t_strconcat(value, " M", NULL)); return TRUE; } if (strcmp(key, "login_process_per_connection") == 0) { config_apply_login_set(ctx, old_section, key, "service_count", strcmp(value, "no") == 0 ? "0" : "1"); return TRUE; } if (strcmp(key, "login_processes_count") == 0) { config_apply_login_set(ctx, old_section, key, "process_min_avail", value); return TRUE; } if (strcmp(key, "login_max_processes_count") == 0) { config_apply_login_set(ctx, old_section, key, "process_limit", value); return TRUE; } if (strcmp(key, "login_max_connections") == 0) { config_apply_login_set(ctx, old_section, key, "client_limit", value); return TRUE; } if (strcmp(key, "login_process_size") == 0) { config_apply_login_set(ctx, old_section, key, "vsz_limit", t_strconcat(value, " M", NULL)); return TRUE; } if (strcmp(key, "max_mail_processes") == 0) { config_apply_mail_set(ctx, old_section, key, "process_limit", value); return TRUE; } if (strcmp(key, "mail_executable") == 0) { config_apply_mail_set(ctx, old_section, key, "executable", value); return TRUE; } if (strcmp(key, "mail_process_size") == 0) { config_apply_mail_set(ctx, old_section, key, "vsz_limit", t_strconcat(value, " M", NULL)); return TRUE; } if (strcmp(key, "mail_drop_priv_before_exec") == 0) { config_apply_mail_set(ctx, old_section, key, "drop_priv_before_exec", value); return TRUE; } if (ctx->old->auth_section == 1) { if (strncmp(key, "auth_", 5) != 0) key = t_strconcat("auth_", key, NULL); } if (strcmp(key, "auth_executable") == 0) { config_apply_auth_set(ctx, key, "executable", value); return TRUE; } if (strcmp(key, "auth_process_size") == 0) { config_apply_auth_set(ctx, key, "vsz_limit", t_strconcat(value, " M", NULL)); return TRUE; } if (strcmp(key, "auth_user") == 0) { config_apply_auth_set(ctx, key, "user", value); return TRUE; } if (strcmp(key, "auth_chroot") == 0) { config_apply_auth_set(ctx, key, "chroot", value); return TRUE; } if (strcmp(key, "auth_cache_size") == 0 && str_to_uoff(value, &size) == 0 && size > 0 && size < 1024) { obsolete(ctx, "auth_cache_size value no longer defaults to " "megabytes. Use %sM", value); config_apply_line(ctx, key, t_strdup_printf("%s=%sM", key, value), NULL); return TRUE; } if (strcmp(key, "auth_count") == 0) { if (strcmp(value, "count") == 0) obsolete(ctx, "auth_count has been removed"); else obsolete(ctx, "auth_count has been removed, and its value must be 1"); return TRUE; } if (ctx->old->socket_listen_section == 2) { const char **p = NULL; if (strcmp(key, "path") == 0) p = &ctx->old->socket_set.path; else if (strcmp(key, "mode") == 0) p = &ctx->old->socket_set.mode; else if (strcmp(key, "user") == 0) p = &ctx->old->socket_set.user; else if (strcmp(key, "group") == 0) p = &ctx->old->socket_set.group; if (p != NULL) { *p = p_strdup(ctx->pool, value); return TRUE; } } return FALSE; } static bool old_auth_section(struct config_parser_context *ctx, const char *key, const char *value) { if (ctx->old->auth_section == 0 && ctx->old->seen_auth_section) { obsolete(ctx, "Multiple auth sections are no longer supported"); return FALSE; } ctx->old->seen_auth_section = TRUE; i_zero(&ctx->old->socket_set); ctx->old->auth_section++; if ((strcmp(key, "passdb") == 0 || strcmp(key, "userdb") == 0) && ctx->old->auth_section == 2) { obsolete(ctx, "%s %s {} has been replaced by %s { driver=%s }", key, value, key, value); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, key, ""); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, "driver", value); return TRUE; } if (strcmp(key, "socket") == 0 && ctx->old->auth_section == 2) { if (strcmp(value, "connect") == 0) { obsolete(ctx, "socket connect {} is no longer supported, configure external auth server separately"); return FALSE; } if (strcmp(value, "listen") != 0) return FALSE; /* socket listen { .. } */ ctx->old->socket_listen_section++; return TRUE; } if (ctx->old->socket_listen_section > 0) ctx->old->socket_listen_section++; if ((strcmp(key, "master") == 0 || strcmp(key, "client") == 0) && ctx->old->socket_listen_section == 2) { ctx->old->socket_set.master = strcmp(key, "master") == 0; return TRUE; } return FALSE; } static bool old_namespace(struct config_parser_context *ctx, const char *value) { if (strcmp(value, "private") != 0 && strcmp(value, "shared") != 0 && strcmp(value, "public") != 0) return FALSE; obsolete(ctx, "namespace %s {} has been replaced by namespace { type=%s }", value, value); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, "namespace", ""); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, "type", value); return TRUE; } static void socket_apply(struct config_parser_context *ctx) { const struct socket_set *set = &ctx->old->socket_set; const char *path, *prefix; size_t len; bool master_suffix; if (set->path == NULL) { ctx->error = "socket listen {} is missing path"; return; } path = set->path; len = strlen(ctx->old->base_dir); if (strncmp(path, ctx->old->base_dir, len) == 0 && path[len] == '/') path += len + 1; len = strlen(path); master_suffix = len >= 7 && (strcmp(path + len - 7, "-master") == 0 || strcmp(path + len - 7, "-userdb") == 0); if (set->master && !master_suffix) { ctx->error = "socket listen { master { path=.. } } must end with -master (or -userdb) suffix"; return; } else if (!set->master && master_suffix) { ctx->error = "socket listen { client { path=.. } } must end not with -master or -userdb suffix"; return; } config_apply_line(ctx, "unix_listener", t_strdup_printf("service/auth/unix_listener=%s", settings_section_escape(path)), path); prefix = t_strdup_printf("service/auth/unix_listener/%s", settings_section_escape(path)); if (set->mode != NULL) { config_apply_line(ctx, "mode", t_strdup_printf("%s/mode=%s", prefix, set->mode), NULL); } if (set->user != NULL) { config_apply_line(ctx, "user", t_strdup_printf("%s/user=%s", prefix, set->user), NULL); } if (set->group != NULL) { config_apply_line(ctx, "group", t_strdup_printf("%s/group=%s", prefix, set->group), NULL); } i_zero(&ctx->old->socket_set); } bool old_settings_handle(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value) { switch (type) { case CONFIG_LINE_TYPE_SKIP: case CONFIG_LINE_TYPE_CONTINUE: case CONFIG_LINE_TYPE_ERROR: case CONFIG_LINE_TYPE_INCLUDE: case CONFIG_LINE_TYPE_INCLUDE_TRY: case CONFIG_LINE_TYPE_KEYFILE: case CONFIG_LINE_TYPE_KEYVARIABLE: break; case CONFIG_LINE_TYPE_KEYVALUE: case CONFIG_LINE_TYPE_KEYVALUE_QUOTED: if (ctx->pathlen == 0) { struct config_section_stack *old_section = ctx->cur_section; bool ret; ret = old_settings_handle_proto(ctx, key, value); ctx->cur_section = old_section; if (ret) return TRUE; return old_settings_handle_root(ctx, key, value); } break; case CONFIG_LINE_TYPE_SECTION_BEGIN: if (ctx->old->auth_section > 0) return old_auth_section(ctx, key, value); else if (ctx->pathlen == 0 && strcmp(key, "auth") == 0) { obsolete(ctx, "add auth_ prefix to all settings inside auth {} and remove the auth {} section completely"); ctx->old->auth_section = 1; return TRUE; } else if (ctx->pathlen == 0 && strcmp(key, "namespace") == 0) return old_namespace(ctx, value); else if (ctx->pathlen == 0 && strcmp(key, "protocol") == 0 && strcmp(value, "managesieve") == 0) { obsolete(ctx, "protocol managesieve {} has been replaced by protocol sieve { }"); config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, "protocol", "sieve"); return TRUE; } break; case CONFIG_LINE_TYPE_SECTION_END: if (ctx->old->auth_section > 0) { if (--ctx->old->auth_section == 0) return TRUE; } if (ctx->old->socket_listen_section > 0) { if (ctx->old->socket_listen_section == 2) socket_apply(ctx); ctx->old->socket_listen_section--; return TRUE; } break; } return FALSE; } void old_settings_init(struct config_parser_context *ctx) { ctx->old = p_new(ctx->pool, struct old_set_parser, 1); ctx->old->base_dir = PKG_RUNDIR; } dovecot-2.2.33.2/src/config/doveconf.c0000644000175000017500000006011213165463624014342 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "abspath.h" #include "module-dir.h" #include "env-util.h" #include "guid.h" #include "hash.h" #include "hostpid.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "settings-parser.h" #include "master-interface.h" #include "master-service.h" #include "all-settings.h" #include "sysinfo-get.h" #include "config-connection.h" #include "config-parser.h" #include "config-request.h" #include "dovecot-version.h" #include #include #include #include struct prefix_stack { unsigned int prefix_idx; unsigned int str_pos; }; ARRAY_DEFINE_TYPE(prefix_stack, struct prefix_stack); struct config_dump_human_context { pool_t pool; string_t *list_prefix; ARRAY_TYPE(const_string) strings; ARRAY_TYPE(const_string) errors; struct config_export_context *export_ctx; unsigned int list_prefix_sent:1; }; #define LIST_KEY_PREFIX "\001" #define UNIQUE_KEY_SUFFIX "\xff" static const char *indent_str = " !!!!"; static void config_request_get_strings(const char *key, const char *value, enum config_key_type type, void *context) { struct config_dump_human_context *ctx = context; const char *p; switch (type) { case CONFIG_KEY_NORMAL: value = p_strdup_printf(ctx->pool, "%s=%s", key, value); break; case CONFIG_KEY_LIST: value = p_strdup_printf(ctx->pool, LIST_KEY_PREFIX"%s=%s", key, value); break; case CONFIG_KEY_UNIQUE_KEY: p = strrchr(key, '/'); i_assert(p != NULL); value = p_strdup_printf(ctx->pool, "%s/"UNIQUE_KEY_SUFFIX"%s=%s", t_strdup_until(key, p), p + 1, value); break; case CONFIG_KEY_ERROR: value = p_strdup(ctx->pool, value); array_append(&ctx->errors, &value, 1); return; } array_append(&ctx->strings, &value, 1); } static int config_string_cmp(const char *const *p1, const char *const *p2) { const char *s1 = *p1, *s2 = *p2; unsigned int i = 0; while (s1[i] == s2[i]) { if (s1[i] == '\0' || s1[i] == '=') return 0; i++; } if (s1[i] == '=') return -1; if (s2[i] == '=') return 1; return s1[i] - s2[i]; } static struct prefix_stack prefix_stack_pop(ARRAY_TYPE(prefix_stack) *stack) { const struct prefix_stack *s; struct prefix_stack sc; unsigned int count; s = array_get(stack, &count); i_assert(count > 0); if (count == 1) { sc.prefix_idx = UINT_MAX; } else { sc.prefix_idx = s[count-2].prefix_idx; } sc.str_pos = s[count-1].str_pos; array_delete(stack, count-1, 1); return sc; } static void prefix_stack_reset_str(ARRAY_TYPE(prefix_stack) *stack) { struct prefix_stack *s; array_foreach_modifiable(stack, s) s->str_pos = UINT_MAX; } static struct config_dump_human_context * config_dump_human_init(const char *const *modules, enum config_dump_scope scope, bool check_settings) { struct config_dump_human_context *ctx; enum config_dump_flags flags; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"config human strings", 1024*32); ctx = p_new(pool, struct config_dump_human_context, 1); ctx->pool = pool; ctx->list_prefix = str_new(ctx->pool, 128); i_array_init(&ctx->strings, 256); i_array_init(&ctx->errors, 256); flags = CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS | CONFIG_DUMP_FLAG_CALLBACK_ERRORS; if (check_settings) flags |= CONFIG_DUMP_FLAG_CHECK_SETTINGS; ctx->export_ctx = config_export_init(modules, scope, flags, config_request_get_strings, ctx); return ctx; } static void config_dump_human_deinit(struct config_dump_human_context *ctx) { array_free(&ctx->strings); array_free(&ctx->errors); pool_unref(&ctx->pool); } static bool value_need_quote(const char *value) { size_t len = strlen(value); if (len == 0) return FALSE; if (strchr(value, '#') != NULL) return TRUE; if (IS_WHITE(value[0]) || IS_WHITE(value[len-1])) return TRUE; return FALSE; } static int ATTR_NULL(4) config_dump_human_output(struct config_dump_human_context *ctx, struct ostream *output, unsigned int indent, const char *setting_name_filter, bool hide_passwords) { ARRAY_TYPE(const_string) prefixes_arr; ARRAY_TYPE(prefix_stack) prefix_stack; struct prefix_stack prefix; const char *const *strings, *const *args, *p, *str, *const *prefixes; const char *key, *key2, *value; unsigned int i, j, count, prefix_count; unsigned int prefix_idx = UINT_MAX; size_t len, skip_len, setting_name_filter_len; bool unique_key; int ret = 0; setting_name_filter_len = setting_name_filter == NULL ? 0 : strlen(setting_name_filter); if (config_export_finish(&ctx->export_ctx) < 0) return -1; array_sort(&ctx->strings, config_string_cmp); strings = array_get(&ctx->strings, &count); /* strings are sorted so that all lists come first */ p_array_init(&prefixes_arr, ctx->pool, 32); for (i = 0; i < count && strings[i][0] == LIST_KEY_PREFIX[0]; i++) T_BEGIN { p = strchr(strings[i], '='); i_assert(p != NULL); if (p[1] == '\0') { /* "strlist=" */ str = p_strdup_printf(ctx->pool, "%s/", t_strcut(strings[i]+1, '=')); array_append(&prefixes_arr, &str, 1); } else { /* string is in format: "list=0 1 2" */ for (args = t_strsplit(p + 1, " "); *args != NULL; args++) { str = p_strdup_printf(ctx->pool, "%s/%s/", t_strcut(strings[i]+1, '='), *args); array_append(&prefixes_arr, &str, 1); } } } T_END; prefixes = array_get(&prefixes_arr, &prefix_count); p_array_init(&prefix_stack, ctx->pool, 8); for (; i < count; i++) T_BEGIN { value = strchr(strings[i], '='); i_assert(value != NULL); key = t_strdup_until(strings[i], value++); unique_key = FALSE; p = strrchr(key, '/'); if (p != NULL && p[1] == UNIQUE_KEY_SUFFIX[0]) { key = t_strconcat(t_strdup_until(key, p + 1), p + 2, NULL); unique_key = TRUE; } if (setting_name_filter_len > 0) { /* see if this setting matches the name filter */ if (!(strncmp(setting_name_filter, key, setting_name_filter_len) == 0 && (key[setting_name_filter_len] == '/' || key[setting_name_filter_len] == '\0'))) goto end; } again: j = 0; /* if there are open sections and this key isn't in it, close the sections */ while (prefix_idx != UINT_MAX) { len = strlen(prefixes[prefix_idx]); if (strncmp(prefixes[prefix_idx], key, len) != 0) { prefix = prefix_stack_pop(&prefix_stack); indent--; if (prefix.str_pos != UINT_MAX) str_truncate(ctx->list_prefix, prefix.str_pos); else { o_stream_nsend(output, indent_str, indent*2); o_stream_nsend_str(output, "}\n"); } prefix_idx = prefix.prefix_idx; } else { /* keep the prefix */ j = prefix_idx + 1; break; } } /* see if this key is in some section */ for (; j < prefix_count; j++) { len = strlen(prefixes[j]); if (strncmp(prefixes[j], key, len) == 0) { key2 = key + (prefix_idx == UINT_MAX ? 0 : strlen(prefixes[prefix_idx])); prefix.str_pos = !unique_key ? UINT_MAX : str_len(ctx->list_prefix); prefix_idx = j; prefix.prefix_idx = prefix_idx; array_append(&prefix_stack, &prefix, 1); str_append_n(ctx->list_prefix, indent_str, indent*2); p = strchr(key2, '/'); if (p != NULL) str_append_n(ctx->list_prefix, key2, p - key2); else str_append(ctx->list_prefix, key2); if (unique_key && *value != '\0') { if (strchr(value, ' ') == NULL) str_printfa(ctx->list_prefix, " %s", value); else str_printfa(ctx->list_prefix, " \"%s\"", str_escape(value)); } str_append(ctx->list_prefix, " {\n"); indent++; if (unique_key) goto end; else goto again; } } o_stream_nsend(output, str_data(ctx->list_prefix), str_len(ctx->list_prefix)); str_truncate(ctx->list_prefix, 0); prefix_stack_reset_str(&prefix_stack); ctx->list_prefix_sent = TRUE; skip_len = prefix_idx == UINT_MAX ? 0 : strlen(prefixes[prefix_idx]); i_assert(skip_len == 0 || strncmp(prefixes[prefix_idx], strings[i], skip_len) == 0); o_stream_nsend(output, indent_str, indent*2); key = strings[i] + skip_len; if (unique_key) key++; value = strchr(key, '='); i_assert(value != NULL); o_stream_nsend(output, key, value-key); o_stream_nsend_str(output, " = "); if (hide_passwords && value[1] != '\0' && ((value-key > 9 && strncmp(value-9, "_password", 9) == 0) || (value-key > 8 && strncmp(value-8, "_api_key", 8) == 0) || strncmp(key, "ssl_key",7) == 0)) { o_stream_nsend_str(output, " # hidden, use -P to show it"); } else if (!value_need_quote(value+1)) o_stream_nsend_str(output, value+1); else { o_stream_nsend(output, "\"", 1); o_stream_nsend_str(output, str_escape(value+1)); o_stream_nsend(output, "\"", 1); } o_stream_nsend(output, "\n", 1); end: ; } T_END; while (prefix_idx != UINT_MAX) { prefix = prefix_stack_pop(&prefix_stack); if (prefix.str_pos != UINT_MAX) break; prefix_idx = prefix.prefix_idx; indent--; o_stream_nsend(output, indent_str, indent*2); o_stream_nsend_str(output, "}\n"); } /* flush output before writing errors */ o_stream_uncork(output); array_foreach(&ctx->errors, strings) { i_error("%s", *strings); ret = -1; } return ret; } static unsigned int config_dump_filter_begin(string_t *str, const struct config_filter *filter) { unsigned int indent = 0; if (filter->local_bits > 0) { str_printfa(str, "local %s", net_ip2addr(&filter->local_net)); if (IPADDR_IS_V4(&filter->local_net)) { if (filter->local_bits != 32) str_printfa(str, "/%u", filter->local_bits); } else { if (filter->local_bits != 128) str_printfa(str, "/%u", filter->local_bits); } str_append(str, " {\n"); indent++; } if (filter->local_name != NULL) { str_append_n(str, indent_str, indent*2); str_printfa(str, "local_name %s {\n", filter->local_name); indent++; } if (filter->remote_bits > 0) { str_append_n(str, indent_str, indent*2); str_printfa(str, "remote %s", net_ip2addr(&filter->remote_net)); if (IPADDR_IS_V4(&filter->remote_net)) { if (filter->remote_bits != 32) str_printfa(str, "/%u", filter->remote_bits); } else { if (filter->remote_bits != 128) str_printfa(str, "/%u", filter->remote_bits); } str_append(str, " {\n"); indent++; } if (filter->service != NULL) { str_append_n(str, indent_str, indent*2); str_printfa(str, "protocol %s {\n", filter->service); indent++; } return indent; } static void config_dump_filter_end(struct ostream *output, unsigned int indent) { while (indent > 0) { indent--; o_stream_nsend(output, indent_str, indent*2); o_stream_nsend(output, "}\n", 2); } } static int config_dump_human_sections(struct ostream *output, const struct config_filter *filter, const char *const *modules, bool hide_passwords) { struct config_filter_parser *const *filters; static struct config_dump_human_context *ctx; unsigned int indent; int ret = 0; filters = config_filter_find_subset(config_filter, filter); /* first filter should be the global one */ i_assert(filters[0] != NULL && filters[0]->filter.service == NULL); filters++; for (; *filters != NULL; filters++) { ctx = config_dump_human_init(modules, CONFIG_DUMP_SCOPE_SET, FALSE); indent = config_dump_filter_begin(ctx->list_prefix, &(*filters)->filter); config_export_parsers(ctx->export_ctx, (*filters)->parsers); if (config_dump_human_output(ctx, output, indent, NULL, hide_passwords) < 0) ret = -1; if (ctx->list_prefix_sent) config_dump_filter_end(output, indent); config_dump_human_deinit(ctx); } return ret; } static int ATTR_NULL(4) config_dump_human(const struct config_filter *filter, const char *const *modules, enum config_dump_scope scope, const char *setting_name_filter, bool hide_passwords) { static struct config_dump_human_context *ctx; struct ostream *output; int ret; output = o_stream_create_fd(STDOUT_FILENO, 0, FALSE); o_stream_set_no_error_handling(output, TRUE); o_stream_cork(output); ctx = config_dump_human_init(modules, scope, TRUE); config_export_by_filter(ctx->export_ctx, filter); ret = config_dump_human_output(ctx, output, 0, setting_name_filter, hide_passwords); config_dump_human_deinit(ctx); if (setting_name_filter == NULL) ret = config_dump_human_sections(output, filter, modules, hide_passwords); o_stream_uncork(output); o_stream_destroy(&output); return ret; } static int config_dump_one(const struct config_filter *filter, bool hide_key, enum config_dump_scope scope, const char *setting_name_filter, bool hide_passwords) { static struct config_dump_human_context *ctx; const char *const *str; size_t len; bool dump_section = FALSE; ctx = config_dump_human_init(NULL, scope, FALSE); config_export_by_filter(ctx->export_ctx, filter); if (config_export_finish(&ctx->export_ctx) < 0) return -1; len = strlen(setting_name_filter); array_foreach(&ctx->strings, str) { if (strncmp(*str, setting_name_filter, len) != 0) continue; if ((*str)[len] == '=') { if (hide_key) printf("%s\n", *str + len+1); else { printf("%s = %s\n", setting_name_filter, *str + len+1); } dump_section = FALSE; break; } else if ((*str)[len] == '/') { dump_section = TRUE; } } config_dump_human_deinit(ctx); if (dump_section) (void)config_dump_human(filter, NULL, scope, setting_name_filter, hide_passwords); return 0; } static void config_request_simple_stdout(const char *key, const char *value, enum config_key_type type ATTR_UNUSED, void *context) { char **setting_name_filters = context; unsigned int i; size_t filter_len; if (setting_name_filters == NULL) { printf("%s=%s\n", key, value); return; } for (i = 0; setting_name_filters[i] != NULL; i++) { filter_len = strlen(setting_name_filters[i]); if (strncmp(setting_name_filters[i], key, filter_len) == 0 && (key[filter_len] == '\0' || key[filter_len] == '/')) printf("%s=%s\n", key, value); } } static void config_request_putenv(const char *key, const char *value, enum config_key_type type ATTR_UNUSED, void *context ATTR_UNUSED) { T_BEGIN { env_put(t_strconcat(t_str_ucase(key), "=", value, NULL)); } T_END; } static const char *get_setting(const char *module, const char *name) { struct config_module_parser *l; const struct setting_define *def; const char *const *value; const void *set; for (l = config_module_parsers; l->root != NULL; l++) { if (strcmp(l->root->module_name, module) != 0) continue; set = settings_parser_get(l->parser); for (def = l->root->defines; def->key != NULL; def++) { if (strcmp(def->key, name) == 0) { value = CONST_PTR_OFFSET(set, def->offset); return *value; } } } return ""; } static void filter_parse_arg(struct config_filter *filter, const char *arg) { const char *key, *value, *error; value = strchr(arg, '='); if (value != NULL) key = t_strdup_until(arg, value++); else { key = arg; value = ""; } if (strcmp(key, "service") == 0) filter->service = value; else if (strcmp(key, "protocol") == 0) filter->service = value; else if (strcmp(key, "lname") == 0) filter->local_name = value; else if (strcmp(key, "local") == 0) { if (config_parse_net(value, &filter->local_net, &filter->local_bits, &error) < 0) i_fatal("local filter: %s", error); } else if (strcmp(key, "remote") == 0) { if (config_parse_net(value, &filter->remote_net, &filter->remote_bits, &error) < 0) i_fatal("remote filter: %s", error); } else { i_fatal("Unknown filter argument: %s", arg); } } struct hostname_format { const char *prefix, *suffix; unsigned int numcount; bool zeropadding; }; static void hostname_format_write(string_t *str, const struct hostname_format *fmt, unsigned int num) { str_truncate(str, 0); str_append(str, fmt->prefix); if (!fmt->zeropadding) str_printfa(str, "%d", num); else str_printfa(str, "%0*d", fmt->numcount, num); str_append(str, fmt->suffix); } static void hostname_verify_format(const char *arg) { struct hostname_format fmt; const char *p; unsigned char hash[GUID_128_HOST_HASH_SIZE]; unsigned int n, limit; HASH_TABLE(void *, void *) hosts; void *key, *value; string_t *host; const char *host2; bool duplicates = FALSE; i_zero(&fmt); if (arg != NULL) { /* host%d, host%2d, host%02d */ p = strchr(arg, '%'); if (p == NULL) i_fatal("Host parameter missing %%d"); fmt.prefix = t_strdup_until(arg, p++); if (*p == '0') { fmt.zeropadding = TRUE; p++; } if (!i_isdigit(*p)) fmt.numcount = 1; else fmt.numcount = *p++ - '0'; if (*p++ != 'd') i_fatal("Host parameter missing %%d"); fmt.suffix = p; } else { /* detect host1[suffix] vs host01[suffix] */ size_t len = strlen(my_hostname); while (len > 0 && !i_isdigit(my_hostname[len-1])) len--; fmt.suffix = my_hostname + len; fmt.numcount = 0; while (len > 0 && i_isdigit(my_hostname[len-1])) { len--; fmt.numcount++; } if (my_hostname[len] == '0') fmt.zeropadding = TRUE; fmt.prefix = t_strndup(my_hostname, len); if (fmt.numcount == 0) { i_fatal("Hostname '%s' has no digits, can't verify", my_hostname); } } for (n = 0, limit = 1; n < fmt.numcount; n++) limit *= 10; host = t_str_new(128); hash_table_create_direct(&hosts, default_pool, limit); for (n = 0; n < limit; n++) { hostname_format_write(host, &fmt, n); guid_128_host_hash_get(str_c(host), hash); i_assert(sizeof(key) >= sizeof(hash)); key = NULL; memcpy(&key, hash, sizeof(hash)); value = hash_table_lookup(hosts, key); if (value != NULL) { host2 = t_strdup(str_c(host)); hostname_format_write(host, &fmt, POINTER_CAST_TO(value, unsigned int)-1); i_error("Duplicate host hashes: %s and %s", str_c(host), host2); duplicates = TRUE; } else { hash_table_insert(hosts, key, POINTER_CAST(n+1)); } } hash_table_destroy(&hosts); if (duplicates) exit(EX_CONFIG); else { host2 = t_strdup(str_c(host)); hostname_format_write(host, &fmt, 0); printf("No duplicate host hashes in %s .. %s\n", str_c(host), host2); exit(0); } } static void check_wrong_config(const char *config_path) { const char *base_dir, *symlink_path, *prev_path; base_dir = get_setting("master", "base_dir"); symlink_path = t_strconcat(base_dir, "/"PACKAGE".conf", NULL); if (t_readlink(symlink_path, &prev_path) < 0) return; if (strcmp(prev_path, config_path) != 0) { i_warning("Dovecot was last started using %s, " "but this config is %s", prev_path, config_path); } } static void failure_exit_callback(int *status) { /* don't use EX_CONFIG, because it often causes MTAs to bounce the mails back. */ *status = EX_TEMPFAIL; } int main(int argc, char *argv[]) { enum config_dump_scope scope = CONFIG_DUMP_SCOPE_ALL; const char *orig_config_path, *config_path, *module; ARRAY(const char *) module_names; struct config_filter filter; const char *const *wanted_modules, *error; char **exec_args = NULL, **setting_name_filters = NULL; unsigned int i; int c, ret, ret2; bool config_path_specified, expand_vars = FALSE, hide_key = FALSE; bool parse_full_config = FALSE, simple_output = FALSE; bool dump_defaults = FALSE, host_verify = FALSE; bool print_plugin_banner = FALSE, hide_passwords = TRUE; if (getenv("USE_SYSEXITS") != NULL) { /* we're coming from (e.g.) LDA */ i_set_failure_exit_callback(failure_exit_callback); } i_zero(&filter); master_service = master_service_init("config", MASTER_SERVICE_FLAG_STANDALONE, &argc, &argv, "adf:hHm:nNpPexS"); orig_config_path = t_strdup(master_service_get_config_path(master_service)); i_set_failure_prefix("doveconf: "); t_array_init(&module_names, 4); while ((c = master_getopt(master_service)) > 0) { if (c == 'e') { expand_vars = TRUE; break; } switch (c) { case 'a': break; case 'd': dump_defaults = TRUE; break; case 'f': filter_parse_arg(&filter, optarg); break; case 'h': hide_key = TRUE; break; case 'H': host_verify = TRUE; break; case 'm': module = t_strdup(optarg); array_append(&module_names, &module, 1); break; case 'n': scope = CONFIG_DUMP_SCOPE_CHANGED; break; case 'N': scope = CONFIG_DUMP_SCOPE_SET; break; case 'p': parse_full_config = TRUE; break; case 'P': hide_passwords = FALSE; break; case 'S': simple_output = TRUE; break; case 'x': expand_vars = TRUE; break; default: return FATAL_DEFAULT; } } array_append_zero(&module_names); wanted_modules = array_count(&module_names) == 1 ? NULL : array_idx(&module_names, 0); config_path = master_service_get_config_path(master_service); /* use strcmp() instead of !=, because dovecot -n always gives us -c parameter */ config_path_specified = strcmp(config_path, orig_config_path) != 0; if (host_verify) hostname_verify_format(argv[optind]); if (c == 'e') { if (argv[optind] == NULL) i_fatal("Missing command for -e"); exec_args = &argv[optind]; } else if (argv[optind] != NULL) { /* print only a single config setting */ setting_name_filters = argv+optind; } else if (!simple_output) { /* print the config file path before parsing it, so in case of errors it's still shown */ printf("# "DOVECOT_VERSION_FULL": %s\n", config_path); print_plugin_banner = TRUE; fflush(stdout); } master_service_init_finish(master_service); config_parse_load_modules(); if (print_plugin_banner) { struct module *m; for (m = modules; m != NULL; m = m->next) { const char **str = module_get_symbol_quiet(m, t_strdup_printf("%s_doveconf_banner", m->name)); if (str != NULL) printf("# %s\n", *str); } } if ((ret = config_parse_file(dump_defaults ? NULL : config_path, expand_vars, parse_full_config ? NULL : wanted_modules, &error)) == 0 && access(EXAMPLE_CONFIG_DIR, X_OK) == 0) { i_fatal("%s (copy example configs from "EXAMPLE_CONFIG_DIR"/)", error); } if ((ret == -1 && exec_args != NULL) || ret == 0 || ret == -2) i_fatal("%s", error); if (simple_output) { struct config_export_context *ctx; ctx = config_export_init(wanted_modules, scope, CONFIG_DUMP_FLAG_CHECK_SETTINGS, config_request_simple_stdout, setting_name_filters); config_export_by_filter(ctx, &filter); ret2 = config_export_finish(&ctx); } else if (setting_name_filters != NULL) { ret2 = 0; /* ignore settings-check failures in configuration. this allows using doveconf to lookup settings for things like install or uninstall scripts where the configuration might (temporarily) not be fully usable */ ret = 0; for (i = 0; setting_name_filters[i] != NULL; i++) { if (config_dump_one(&filter, hide_key, scope, setting_name_filters[i], hide_passwords) < 0) ret2 = -1; } } else if (exec_args == NULL) { const char *info; info = sysinfo_get(get_setting("mail", "mail_location")); if (*info != '\0') printf("# %s\n", info); if (!config_path_specified) check_wrong_config(config_path); if (scope == CONFIG_DUMP_SCOPE_ALL) printf("# NOTE: Send doveconf -n output instead when asking for help.\n"); fflush(stdout); ret2 = config_dump_human(&filter, wanted_modules, scope, NULL, hide_passwords); } else { struct config_export_context *ctx; ctx = config_export_init(wanted_modules, CONFIG_DUMP_SCOPE_SET, CONFIG_DUMP_FLAG_CHECK_SETTINGS, config_request_putenv, NULL); config_export_by_filter(ctx, &filter); if (getenv(DOVECOT_PRESERVE_ENVS_ENV) != NULL) { /* Standalone binary is getting its configuration via doveconf. Clean the environment before calling it. Do this only if the environment exists, because lib-master doesn't set it if it doesn't want the environment to be cleaned (e.g. -k parameter). */ const char *import_environment = config_export_get_import_environment(ctx); master_service_import_environment(import_environment); master_service_env_clean(); } env_put("DOVECONF_ENV=1"); if (config_export_finish(&ctx) < 0) i_fatal("Invalid configuration"); execvp(exec_args[0], exec_args); i_fatal("execvp(%s) failed: %m", exec_args[0]); } if (ret < 0) { /* delayed error */ i_fatal("%s", error); } if (ret2 < 0) i_fatal("Errors in configuration"); config_filter_deinit(&config_filter); module_dir_unload(&modules); config_parser_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/config/config-request.c0000644000175000017500000002712613165463624015502 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "ostream.h" #include "settings-parser.h" #include "master-service-settings.h" #include "all-settings.h" #include "config-parser.h" #include "config-request.h" struct config_export_context { pool_t pool; string_t *value; string_t *prefix; HASH_TABLE(char *, char *) keys; enum config_dump_scope scope; config_request_callback_t *callback; void *context; const char *const *modules; enum config_dump_flags flags; const struct config_module_parser *parsers; struct config_module_parser *dup_parsers; struct master_service_settings_output output; bool failed; }; static void config_export_size(string_t *str, uoff_t size) { static const char suffixes[] = { 'B', 'k', 'M', 'G', 'T' }; char suffix = suffixes[0]; unsigned int i; if (size == 0) { str_append_c(str, '0'); return; } for (i = 1; i < N_ELEMENTS(suffixes) && (size % 1024) == 0; i++) { suffix = suffixes[i]; size /= 1024; } str_printfa(str, "%llu %c", (unsigned long long)size, suffix); } static void config_export_time(string_t *str, unsigned int stamp) { const char *suffix = "secs"; if (stamp == 0) { str_append_c(str, '0'); return; } if (stamp % 60 == 0) { stamp /= 60; suffix = "mins"; if (stamp % 60 == 0) { stamp /= 60; suffix = "hours"; if (stamp % 24 == 0) { stamp /= 24; suffix = "days"; if (stamp % 7 == 0) { stamp /= 7; suffix = "weeks"; } } } } str_printfa(str, "%u %s", stamp, suffix); } bool config_export_type(string_t *str, const void *value, const void *default_value, enum setting_type type, bool dump_default, bool *dump_r) { switch (type) { case SET_BOOL: { const bool *val = value, *dval = default_value; if (dump_default || dval == NULL || *val != *dval) str_append(str, *val ? "yes" : "no"); break; } case SET_SIZE: { const uoff_t *val = value, *dval = default_value; if (dump_default || dval == NULL || *val != *dval) config_export_size(str, *val); break; } case SET_UINT: case SET_UINT_OCT: case SET_TIME: { const unsigned int *val = value, *dval = default_value; if (dump_default || dval == NULL || *val != *dval) { switch (type) { case SET_UINT_OCT: str_printfa(str, "0%o", *val); break; case SET_TIME: config_export_time(str, *val); break; default: str_printfa(str, "%u", *val); break; } } break; } case SET_IN_PORT: { const in_port_t *val = value, *dval = default_value; if (dump_default || dval == NULL || *val != *dval) str_printfa(str, "%u", *val); break; } case SET_STR_VARS: { const char *const *val = value, *sval; const char *const *_dval = default_value; const char *dval = _dval == NULL ? NULL : *_dval; i_assert(*val == NULL || **val == SETTING_STRVAR_UNEXPANDED[0]); sval = *val == NULL ? NULL : (*val + 1); if ((dump_default || null_strcmp(sval, dval) != 0) && sval != NULL) { str_append(str, sval); *dump_r = TRUE; } break; } case SET_STR: { const char *const *val = value; const char *const *_dval = default_value; const char *dval = _dval == NULL ? NULL : *_dval; if ((dump_default || null_strcmp(*val, dval) != 0) && *val != NULL) { str_append(str, *val); *dump_r = TRUE; } break; } case SET_ENUM: { const char *const *val = value; size_t len = strlen(*val); if (dump_default) str_append(str, *val); else { const char *const *_dval = default_value; const char *dval = _dval == NULL ? NULL : *_dval; i_assert(dval != NULL); if (strncmp(*val, dval, len) != 0 || ((*val)[len] != ':' && (*val)[len] != '\0')) str_append(str, *val); } break; } default: return FALSE; } return TRUE; } static void setting_export_section_name(string_t *str, const struct setting_define *def, const void *set, unsigned int idx) { const char *const *name; size_t name_offset; if (def->type != SET_DEFLIST_UNIQUE) { /* not unique, use the index */ str_printfa(str, "%u", idx); return; } name_offset = def->list_info->type_offset; i_assert(name_offset != (size_t)-1); name = CONST_PTR_OFFSET(set, name_offset); if (*name == NULL || **name == '\0') { /* no name, this one isn't unique. use the index. */ str_printfa(str, "%u", idx); } else { str_append(str, settings_section_escape(*name)); } } static void settings_export(struct config_export_context *ctx, const struct setting_parser_info *info, bool parent_unique_deflist, const void *set, const void *change_set) { const struct setting_define *def; const void *value, *default_value, *change_value; void *const *children, *const *change_children = NULL; unsigned int i, count, count2; size_t prefix_len; const char *str; char *key; bool dump, dump_default = FALSE; for (def = info->defines; def->key != NULL; def++) { value = CONST_PTR_OFFSET(set, def->offset); default_value = info->defaults == NULL ? NULL : CONST_PTR_OFFSET(info->defaults, def->offset); change_value = CONST_PTR_OFFSET(change_set, def->offset); switch (ctx->scope) { case CONFIG_DUMP_SCOPE_ALL: dump_default = TRUE; break; case CONFIG_DUMP_SCOPE_SET: dump_default = *((const char *)change_value) != 0; break; case CONFIG_DUMP_SCOPE_CHANGED: dump_default = FALSE; break; } if (!parent_unique_deflist || (ctx->flags & CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS) == 0) { /* .. */ } else if (*((const char *)change_value) == 0 && def->offset != info->type_offset) { /* this is mainly for service {} blocks. if value hasn't changed, it's the default. even if info->defaults has a different value. */ default_value = value; } else { /* value is set explicitly, but we don't know the default here. assume it's not the default. */ dump_default = TRUE; } dump = FALSE; count = 0; children = NULL; str_truncate(ctx->value, 0); switch (def->type) { case SET_BOOL: case SET_SIZE: case SET_UINT: case SET_UINT_OCT: case SET_TIME: case SET_IN_PORT: case SET_STR_VARS: case SET_STR: case SET_ENUM: if (!config_export_type(ctx->value, value, default_value, def->type, dump_default, &dump)) i_unreached(); break; case SET_DEFLIST: case SET_DEFLIST_UNIQUE: { const ARRAY_TYPE(void_array) *val = value; const ARRAY_TYPE(void_array) *change_val = change_value; if (!array_is_created(val)) break; children = array_get(val, &count); for (i = 0; i < count; i++) { if (i > 0) str_append_c(ctx->value, ' '); setting_export_section_name(ctx->value, def, children[i], i); } change_children = array_get(change_val, &count2); i_assert(count == count2); break; } case SET_STRLIST: { const ARRAY_TYPE(const_string) *val = value; const char *const *strings; if (!array_is_created(val)) break; key = p_strconcat(ctx->pool, str_c(ctx->prefix), def->key, NULL); if (hash_table_lookup(ctx->keys, key) != NULL) { /* already added all of these */ break; } hash_table_insert(ctx->keys, key, key); /* for doveconf -n to see this KEY_LIST */ ctx->callback(key, "", CONFIG_KEY_LIST, ctx->context); strings = array_get(val, &count); i_assert(count % 2 == 0); for (i = 0; i < count; i += 2) { str = p_strdup_printf(ctx->pool, "%s%s%c%s", str_c(ctx->prefix), def->key, SETTINGS_SEPARATOR, strings[i]); ctx->callback(str, strings[i+1], CONFIG_KEY_NORMAL, ctx->context); } count = 0; break; } case SET_ALIAS: break; } if (str_len(ctx->value) > 0 || dump) { key = p_strconcat(ctx->pool, str_c(ctx->prefix), def->key, NULL); if (hash_table_lookup(ctx->keys, key) == NULL) { enum config_key_type type; if (def->offset == info->type_offset && parent_unique_deflist) type = CONFIG_KEY_UNIQUE_KEY; else if (SETTING_TYPE_IS_DEFLIST(def->type)) type = CONFIG_KEY_LIST; else type = CONFIG_KEY_NORMAL; ctx->callback(key, str_c(ctx->value), type, ctx->context); hash_table_insert(ctx->keys, key, key); } } i_assert(count == 0 || children != NULL); prefix_len = str_len(ctx->prefix); for (i = 0; i < count; i++) { str_append(ctx->prefix, def->key); str_append_c(ctx->prefix, SETTINGS_SEPARATOR); setting_export_section_name(ctx->prefix, def, children[i], i); str_append_c(ctx->prefix, SETTINGS_SEPARATOR); settings_export(ctx, def->list_info, def->type == SET_DEFLIST_UNIQUE, children[i], change_children[i]); str_truncate(ctx->prefix, prefix_len); } } } struct config_export_context * config_export_init(const char *const *modules, enum config_dump_scope scope, enum config_dump_flags flags, config_request_callback_t *callback, void *context) { struct config_export_context *ctx; pool_t pool; pool = pool_alloconly_create(MEMPOOL_GROWING"config export", 1024*64); ctx = p_new(pool, struct config_export_context, 1); ctx->pool = pool; ctx->modules = modules == NULL ? NULL : p_strarray_dup(pool, modules); ctx->flags = flags; ctx->callback = callback; ctx->context = context; ctx->scope = scope; ctx->value = str_new(pool, 256); ctx->prefix = str_new(pool, 64); hash_table_create(&ctx->keys, ctx->pool, 0, str_hash, strcmp); return ctx; } void config_export_by_filter(struct config_export_context *ctx, const struct config_filter *filter) { const char *error; if (config_filter_parsers_get(config_filter, ctx->pool, ctx->modules, filter, &ctx->dup_parsers, &ctx->output, &error) < 0) { i_error("%s", error); ctx->failed = TRUE; } ctx->parsers = ctx->dup_parsers; } void config_export_parsers(struct config_export_context *ctx, const struct config_module_parser *parsers) { ctx->parsers = parsers; } void config_export_get_output(struct config_export_context *ctx, struct master_service_settings_output *output_r) { *output_r = ctx->output; } const char * config_export_get_import_environment(struct config_export_context *ctx) { enum setting_type stype; unsigned int i; for (i = 0; ctx->parsers[i].root != NULL; i++) { if (ctx->parsers[i].root == &master_service_setting_parser_info) { const char *const *value = settings_parse_get_value(ctx->parsers[i].parser, "import_environment", &stype); i_assert(value != NULL); return *value; } } i_unreached(); } static void config_export_free(struct config_export_context *ctx) { if (ctx->dup_parsers != NULL) config_filter_parsers_free(ctx->dup_parsers); hash_table_destroy(&ctx->keys); pool_unref(&ctx->pool); } int config_export_finish(struct config_export_context **_ctx) { struct config_export_context *ctx = *_ctx; const struct config_module_parser *parser; const char *error; unsigned int i; int ret = 0; *_ctx = NULL; if (ctx->failed) { config_export_free(ctx); return -1; } for (i = 0; ctx->parsers[i].root != NULL; i++) { parser = &ctx->parsers[i]; if (!config_module_want_parser(config_module_parsers, ctx->modules, parser->root)) continue; T_BEGIN { settings_export(ctx, parser->root, FALSE, settings_parser_get(parser->parser), settings_parser_get_changes(parser->parser)); } T_END; if ((ctx->flags & CONFIG_DUMP_FLAG_CHECK_SETTINGS) != 0) { settings_parse_var_skip(parser->parser); if (!settings_parser_check(parser->parser, ctx->pool, &error)) { if ((ctx->flags & CONFIG_DUMP_FLAG_CALLBACK_ERRORS) != 0) { ctx->callback(NULL, error, CONFIG_KEY_ERROR, ctx->context); } else { i_error("%s", error); ret = -1; break; } } } } config_export_free(ctx); return ret; } dovecot-2.2.33.2/src/config/main.c0000644000175000017500000000260413123174404013452 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "restrict-access.h" #include "master-service.h" #include "config-connection.h" #include "config-parser.h" #include "config-request.h" static void client_connected(struct master_service_connection *conn) { master_service_client_connection_accept(conn); (void)config_connection_create(conn->fd); } int main(int argc, char *argv[]) { const char *path, *error; master_service = master_service_init("config", 0, &argc, &argv, ""); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; master_service_init_log(master_service, "config: "); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); config_parse_load_modules(); path = master_service_get_config_path(master_service); if (config_parse_file(path, TRUE, NULL, &error) <= 0) i_fatal("%s", error); /* notify about our success only after successfully parsing the config file, so if the parsing fails, master won't immediately just recreate this process (and fail again and so on). */ master_service_init_finish(master_service); master_service_run(master_service, client_connected); config_connections_destroy_all(); config_filter_deinit(&config_filter); module_dir_unload(&modules); config_parser_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/config/settings-get.pl0000755000175000017500000000733513165463624015360 00000000000000#!/usr/bin/env perl use strict; print '#include "lib.h"'."\n"; print '#include "array.h"'."\n"; print '#include "ipwd.h"'."\n"; print '#include "var-expand.h"'."\n"; print '#include "file-lock.h"'."\n"; print '#include "fsync-mode.h"'."\n"; print '#include "hash-format.h"'."\n"; print '#include "net.h"'."\n"; print '#include "unichar.h"'."\n"; print '#include "hash-method.h"'."\n"; print '#include "settings-parser.h"'."\n"; print '#include "all-settings.h"'."\n"; print '#include '."\n"; print '#include '."\n"; print '#define CONFIG_BINARY'."\n"; print 'extern buffer_t config_all_services_buf;'; my @services = (); my @service_ifdefs = (); my %parsers = {}; foreach my $file (@ARGV) { my $f; open($f, $file) || die "Can't open $file: $@"; my $state = 0; my $file_contents = ""; my $externs = ""; my $code = ""; my %funcs; my $cur_name = ""; my $ifdef = ""; my $state_ifdef = 0; while (<$f>) { my $write = 0; if ($state == 0) { if (/struct .*_settings \{/ || /struct setting_define.*\{/ || /struct .*_default_settings = \{/) { $state++; } elsif (/^struct service_settings (.*) = \{/) { $state++; if ($ifdef eq "") { $state_ifdef = 0; } else { $_ = $ifdef."\n".$_; $state_ifdef = 1; } push @services, $1; push @service_ifdefs, $ifdef; } elsif (/^(static )?const struct setting_parser_info (.*) = \{/) { $cur_name = $2; $state++ if ($cur_name !~ /^\*default_/); } elsif (/^extern const struct setting_parser_info (.*);/) { $externs .= "extern const struct setting_parser_info $1;\n"; } elsif (/\/\* \*\//) { $state = 4; $code .= $_; } if (/(^#ifdef .*)$/ || /^(#if .*)$/) { $ifdef = $1; } else { $ifdef = ""; } if (/#define.*DEF/ || /^#undef.*DEF/ || /ARRAY_DEFINE_TYPE.*_settings/) { $write = 1; $state = 2 if (/\\$/); } } elsif ($state == 2) { $write = 1; $state = 0 if (!/\\$/); } elsif ($state == 4) { $code .= $_; $state = 0 if (/\/\* <\/settings checks> \*\//); } if ($state == 1 || $state == 3) { if ($state == 1) { if (/\.module_name = "(.*)"/) { $parsers{$cur_name} = $1; } if (/DEFLIST.*".*",(.*)$/) { my $value = $1; if ($value =~ /.*&(.*)\)/) { $parsers{$1} = 0; $externs .= "extern const struct setting_parser_info $1;\n"; } else { $state = 3; } } } elsif ($state == 3) { if (/.*&(.*)\)/) { $parsers{$1} = 0; } } s/^static const (struct master_settings master_default_settings)/$1/; $write = 1; if (/};/) { $state = 0; if ($state_ifdef) { $_ .= "#endif\n"; $state_ifdef = 0; } } } $file_contents .= $_ if ($write); } print "/* $file */\n"; print $externs; print $code; print $file_contents; close $f; } print "static struct service_settings *config_all_services[] = {\n"; for (my $i = 0; $i < scalar(@services); $i++) { my $ifdef = $service_ifdefs[$i]; print "$ifdef\n" if ($ifdef ne ""); print "\t&".$services[$i].",\n"; print "#endif\n" if ($ifdef ne ""); } print "};\n"; print "buffer_t config_all_services_buf = {\n"; print "\tconfig_all_services, sizeof(config_all_services), { NULL, }\n"; print "};\n"; print "const struct setting_parser_info *all_default_roots[] = {\n"; print "\t&master_service_setting_parser_info,\n"; print "\t&master_service_ssl_setting_parser_info,\n"; foreach my $name (keys %parsers) { my $module = $parsers{$name}; next if (!$module); print "\t&".$name.", \n"; } print "\tNULL\n"; print "};\n"; print "const struct setting_parser_info *const *all_roots = all_default_roots;\n"; print "ARRAY_TYPE(service_settings) *default_services = &master_default_settings.services;\n"; dovecot-2.2.33.2/src/config/config-filter.c0000644000175000017500000002434513165463624015277 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "settings-parser.h" #include "master-service-settings.h" #include "config-parser.h" #include "config-filter.h" #include "dns-util.h" struct config_filter_context { pool_t pool; struct config_filter_parser *const *parsers; }; static bool config_filter_match_service(const struct config_filter *mask, const struct config_filter *filter) { if (mask->service != NULL) { if (filter->service == NULL) return FALSE; if (mask->service[0] == '!') { /* not service */ if (strcmp(filter->service, mask->service + 1) == 0) return FALSE; } else { if (strcmp(filter->service, mask->service) != 0) return FALSE; } } return TRUE; } static bool config_filter_match_local_name(const struct config_filter *mask, const char *filter_local_name) { /* Handle multiple names seperated by spaces in local_name * Ex: local_name "mail.domain.tld domain.tld mx.domain.tld" { ... } */ const char *const *local_name = t_strsplit_spaces(mask->local_name, " "); for (; *local_name != NULL; local_name++) { if (dns_match_wildcard(filter_local_name, *local_name) == 0) return TRUE; } return FALSE; } static bool config_filter_match_rest(const struct config_filter *mask, const struct config_filter *filter) { bool matched; if (mask->local_name != NULL) { if (filter->local_name == NULL) return FALSE; T_BEGIN { matched = config_filter_match_local_name(mask, filter->local_name); } T_END; if (!matched) return FALSE; } /* FIXME: it's not comparing full masks */ if (mask->remote_bits != 0) { if (filter->remote_bits == 0) return FALSE; if (!net_is_in_network(&filter->remote_net, &mask->remote_net, mask->remote_bits)) return FALSE; } if (mask->local_bits != 0) { if (filter->local_bits == 0) return FALSE; if (!net_is_in_network(&filter->local_net, &mask->local_net, mask->local_bits)) return FALSE; } return TRUE; } bool config_filter_match(const struct config_filter *mask, const struct config_filter *filter) { if (!config_filter_match_service(mask, filter)) return FALSE; return config_filter_match_rest(mask, filter); } bool config_filters_equal(const struct config_filter *f1, const struct config_filter *f2) { if (null_strcmp(f1->service, f2->service) != 0) return FALSE; if (f1->remote_bits != f2->remote_bits) return FALSE; if (!net_ip_compare(&f1->remote_net, &f2->remote_net)) return FALSE; if (f1->local_bits != f2->local_bits) return FALSE; if (!net_ip_compare(&f1->local_net, &f2->local_net)) return FALSE; if (null_strcasecmp(f1->local_name, f2->local_name) != 0) return FALSE; return TRUE; } struct config_filter_context *config_filter_init(pool_t pool) { struct config_filter_context *ctx; ctx = p_new(pool, struct config_filter_context, 1); ctx->pool = pool; return ctx; } void config_filter_deinit(struct config_filter_context **_ctx) { struct config_filter_context *ctx = *_ctx; unsigned int i; *_ctx = NULL; for (i = 0; ctx->parsers[i] != NULL; i++) config_filter_parsers_free(ctx->parsers[i]->parsers); pool_unref(&ctx->pool); } void config_filter_add_all(struct config_filter_context *ctx, struct config_filter_parser *const *parsers) { ctx->parsers = parsers; } static int config_filter_parser_cmp(struct config_filter_parser *const *p1, struct config_filter_parser *const *p2) { const struct config_filter *f1 = &(*p1)->filter, *f2 = &(*p2)->filter; /* remote and locals are first, although it doesn't really matter which one comes first */ if (f1->local_name != NULL && f2->local_name == NULL) return -1; if (f1->local_name == NULL && f2->local_name != NULL) return 1; if (f1->local_bits > f2->local_bits) return -1; if (f1->local_bits < f2->local_bits) return 1; if (f1->remote_bits > f2->remote_bits) return -1; if (f1->remote_bits < f2->remote_bits) return 1; if (f1->service != NULL && f2->service == NULL) return -1; if (f1->service == NULL && f2->service != NULL) return 1; return 0; } static int config_filter_parser_cmp_rev(struct config_filter_parser *const *p1, struct config_filter_parser *const *p2) { return -config_filter_parser_cmp(p1, p2); } static bool str_array_contains(ARRAY_TYPE(const_string) *arr, const char *str) { const char *const *p; array_foreach(arr, p) { if (strcmp(*p, str) == 0) return TRUE; } return FALSE; } static bool have_changed_settings(const struct config_filter_parser *parser, const char *const *modules) { const unsigned char *changes; unsigned int i, j, size; for (i = 0; parser->parsers[i].root != NULL; i++) { if (!config_module_want_parser(config_module_parsers, modules, parser->parsers[i].root)) continue; changes = settings_parser_get_changes(parser->parsers[i].parser); size = parser->parsers[i].root->struct_size; for (j = 0; j < size; j++) { if (changes[j] != 0) return TRUE; } } return FALSE; } static struct config_filter_parser *const * config_filter_find_all(struct config_filter_context *ctx, pool_t pool, const char *const *modules, const struct config_filter *filter, struct master_service_settings_output *output_r) { ARRAY_TYPE(config_filter_parsers) matches; ARRAY_TYPE(const_string) service_names; unsigned int i; i_zero(output_r); p_array_init(&matches, pool, 8); p_array_init(&service_names, pool, 8); for (i = 0; ctx->parsers[i] != NULL; i++) { const struct config_filter *mask = &ctx->parsers[i]->filter; if (!config_filter_match_service(mask, filter)) { if (!str_array_contains(&service_names, mask->service) && have_changed_settings(ctx->parsers[i], modules)) array_append(&service_names, &mask->service, 1); continue; } if (mask->local_bits > 0 || mask->local_name != NULL) output_r->service_uses_local = TRUE; if (mask->remote_bits > 0) output_r->service_uses_remote = TRUE; if (config_filter_match_rest(mask, filter)) { if (mask->local_bits > 0 || mask->local_name != NULL) output_r->used_local = TRUE; if (mask->remote_bits > 0) output_r->used_remote = TRUE; array_append(&matches, &ctx->parsers[i], 1); } } if (filter->service == NULL) { array_append_zero(&service_names); output_r->specific_services = array_idx(&service_names, 0); } array_sort(&matches, config_filter_parser_cmp); array_append_zero(&matches); return array_idx(&matches, 0); } struct config_filter_parser *const * config_filter_find_subset(struct config_filter_context *ctx, const struct config_filter *filter) { ARRAY_TYPE(config_filter_parsers) matches; struct config_filter tmp_mask; unsigned int i; t_array_init(&matches, 8); for (i = 0; ctx->parsers[i] != NULL; i++) { const struct config_filter *mask = &ctx->parsers[i]->filter; if (filter->service != NULL) { if (!config_filter_match_service(mask, filter)) continue; } tmp_mask = *mask; if (filter->local_name == NULL) tmp_mask.local_name = NULL; if (filter->local_bits == 0) tmp_mask.local_bits = 0; if (filter->remote_bits == 0) tmp_mask.remote_bits = 0; if (config_filter_match_rest(&tmp_mask, filter)) array_append(&matches, &ctx->parsers[i], 1); } array_sort(&matches, config_filter_parser_cmp_rev); array_append_zero(&matches); return array_idx(&matches, 0); } static bool config_filter_is_superset(const struct config_filter *sup, const struct config_filter *filter) { /* assume that both of the filters match the same subset, so we don't need to compare IPs and service name. */ if (sup->local_bits > filter->local_bits) return FALSE; if (sup->remote_bits > filter->remote_bits) return FALSE; if (sup->local_name != NULL && filter->local_name == NULL) { i_warning("%s", sup->local_name); return FALSE; } if (sup->service != NULL && filter->service == NULL) return FALSE; return TRUE; } static int config_module_parser_apply_changes(struct config_module_parser *dest, const struct config_filter_parser *src, pool_t pool, const char **error_r) { const char *conflict_key; unsigned int i; for (i = 0; dest[i].root != NULL; i++) { if (settings_parser_apply_changes(dest[i].parser, src->parsers[i].parser, pool, error_r == NULL ? NULL : &conflict_key) < 0) { i_assert(error_r != NULL); *error_r = t_strdup_printf("Conflict in setting %s " "found from filter at %s", conflict_key, src->file_and_line); return -1; } } return 0; } int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool, const char *const *modules, const struct config_filter *filter, struct config_module_parser **parsers_r, struct master_service_settings_output *output_r, const char **error_r) { struct config_filter_parser *const *src; struct config_module_parser *dest; const char *error = NULL, **error_p; unsigned int i, count; /* get the matching filters. the most specific ones are handled first, so that if more generic filters try to override settings we'll fail with an error. Merging SET_STRLIST types requires settings_parser_apply_changes() to work a bit unintuitively by letting the destination settings override the source settings. */ src = config_filter_find_all(ctx, pool, modules, filter, output_r); /* all of them should have the same number of parsers. duplicate our initial parsers from the first match */ for (count = 0; src[0]->parsers[count].root != NULL; count++) ; dest = p_new(pool, struct config_module_parser, count + 1); for (i = 0; i < count; i++) { dest[i] = src[0]->parsers[i]; dest[i].parser = settings_parser_dup(src[0]->parsers[i].parser, pool); } /* apply the changes from rest of the matches */ for (i = 1; src[i] != NULL; i++) { if (config_filter_is_superset(&src[i]->filter, &src[i-1]->filter)) error_p = NULL; else error_p = &error; if (config_module_parser_apply_changes(dest, src[i], pool, error_p) < 0) { i_assert(error != NULL); config_filter_parsers_free(dest); *error_r = error; return -1; } } *parsers_r = dest; return 0; } void config_filter_parsers_free(struct config_module_parser *parsers) { unsigned int i; for (i = 0; parsers[i].root != NULL; i++) settings_parser_deinit(&parsers[i].parser); } dovecot-2.2.33.2/src/config/config-connection.h0000644000175000017500000000035113123174404016132 00000000000000#ifndef CONFIG_CONNECTION_H #define CONFIG_CONNECTION_H struct config_connection *config_connection_create(int fd); void config_connection_destroy(struct config_connection *conn); void config_connections_destroy_all(void); #endif dovecot-2.2.33.2/src/config/config-settings.c0000644000175000017500000000213713123174404015632 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings config_unix_listeners_array[] = { { "config", 0600, "", "" } }; static struct file_listener_settings *config_unix_listeners[] = { &config_unix_listeners_array[0] }; static buffer_t config_unix_listeners_buf = { config_unix_listeners, sizeof(config_unix_listeners), { NULL, } }; /* */ struct service_settings config_service_settings = { .name = "config", .protocol = "", .type = "config", .executable = "config", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &config_unix_listeners_buf, sizeof(config_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; dovecot-2.2.33.2/src/config/all-settings.h0000644000175000017500000000034713123174404015143 00000000000000#ifndef ALL_SETTINGS_H #define ALL_SETTINGS_H extern const struct setting_parser_info *const *all_roots; extern const struct setting_parser_info *all_default_roots[]; extern ARRAY_TYPE(service_settings) *default_services; #endif dovecot-2.2.33.2/src/config/old-set-parser.h0000644000175000017500000000047613165463624015414 00000000000000#ifndef OLD_SET_PARSER_H #define OLD_SET_PARSER_H #include "config-parser-private.h" struct config_parser_context; bool old_settings_handle(struct config_parser_context *ctx, enum config_line_type type, const char *key, const char *value); void old_settings_init(struct config_parser_context *ctx); #endif dovecot-2.2.33.2/src/config/Makefile.am0000644000175000017500000000247413165463624014436 00000000000000pkglibexecdir = $(libexecdir)/dovecot exampledir = $(docdir)/example-config bin_PROGRAMS = doveconf pkglibexec_PROGRAMS = config AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DEXAMPLE_CONFIG_DIR=\""$(exampledir)"\" \ -DMODULEDIR=\""$(moduledir)"\" \ -DSSLDIR=\""$(ssldir)\"" config_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) config_DEPENDENCIES = $(LIBDOVECOT_DEPS) doveconf_LDADD = \ $(LIBDOVECOT) \ $(RAND_LIBS) doveconf_DEPENDENCIES = $(LIBDOVECOT_DEPS) common = \ all-settings.c \ config-connection.c \ config-filter.c \ config-parser.c \ config-request.c \ old-set-parser.c \ sysinfo-get.c config_SOURCES = \ main.c \ $(common) doveconf_SOURCES = \ doveconf.c \ $(common) noinst_HEADERS = \ all-settings.h \ config-connection.h \ old-set-parser.h \ sysinfo-get.h pkginclude_HEADERS = \ config-filter.h \ config-parser.h \ config-parser-private.h \ config-request.h all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl $(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c EXTRA_DIST = \ config-settings.c \ settings-get.pl dovecot-2.2.33.2/src/ssl-params/0002755000175000017500000000000013172375613013270 500000000000000dovecot-2.2.33.2/src/ssl-params/Makefile.in0000644000175000017500000005377513172375575015303 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = ssl-params$(EXEEXT) subdir = src/ssl-params ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_ssl_params_OBJECTS = main.$(OBJEXT) ssl-params.$(OBJEXT) \ ssl-params-settings.$(OBJEXT) ssl_params_OBJECTS = $(am_ssl_params_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(ssl_params_SOURCES) DIST_SOURCES = $(ssl_params_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DPKG_STATEDIR=\""$(statedir)"\" ssl_params_LDADD = $(LIBDOVECOT) ssl_params_DEPENDENCIES = $(LIBDOVECOT_DEPS) ssl_params_SOURCES = \ main.c \ ssl-params.c \ ssl-params-settings.c noinst_HEADERS = \ ssl-params.h \ ssl-params-settings.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/ssl-params/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/ssl-params/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list ssl-params$(EXEEXT): $(ssl_params_OBJECTS) $(ssl_params_DEPENDENCIES) $(EXTRA_ssl_params_DEPENDENCIES) @rm -f ssl-params$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ssl_params_OBJECTS) $(ssl_params_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl-params-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl-params.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/ssl-params/ssl-params-settings.h0000644000175000017500000000044613165463624017305 00000000000000#ifndef SSL_PARAMS_SETTINGS_H #define SSL_PARAMS_SETTINGS_H struct master_service; struct ssl_params_settings { unsigned int ssl_parameters_regenerate; unsigned int ssl_dh_parameters_length; }; struct ssl_params_settings * ssl_params_settings_read(struct master_service *service); #endif dovecot-2.2.33.2/src/ssl-params/ssl-params.h0000644000175000017500000000062113165463624015442 00000000000000#ifndef SSL_BUILD_PARAMS_H #define SSL_BUILD_PARAMS_H struct ssl_params_settings; typedef void ssl_params_callback_t(const unsigned char *data, size_t size); struct ssl_params * ssl_params_init(const char *path, ssl_params_callback_t *callback, const struct ssl_params_settings *set); void ssl_params_deinit(struct ssl_params **param); void ssl_params_refresh(struct ssl_params *param); #endif dovecot-2.2.33.2/src/ssl-params/ssl-params.c0000644000175000017500000001455313165463624015446 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "buffer.h" #include "file-lock.h" #include "read-full.h" #include "write-full.h" #include "master-interface.h" #include "master-service.h" #include "master-service-settings.h" #include "iostream-ssl.h" #include "ssl-params-settings.h" #include "ssl-params.h" #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_RESOURCE_H # include #endif #define MAX_PARAM_FILE_SIZE 1024*1024 #define SSL_BUILD_PARAM_TIMEOUT_SECS (60*30) #define SSL_PARAMS_PRIORITY 15 struct ssl_params { char *path; struct ssl_params_settings set; time_t last_mtime; ssl_params_callback_t *callback; }; static void ssl_params_if_unchanged(const char *path, time_t mtime, unsigned int ssl_dh_parameters_length ATTR_UNUSED) { const char *temp_path, *error; struct file_lock *lock; struct stat st, st2; mode_t old_mask; int fd, ret; buffer_t *buf; #ifdef HAVE_SETPRIORITY if (setpriority(PRIO_PROCESS, 0, SSL_PARAMS_PRIORITY) < 0) i_error("setpriority(%d) failed: %m", SSL_PARAMS_PRIORITY); #endif temp_path = t_strconcat(path, ".tmp", NULL); old_mask = umask(0); fd = open(temp_path, O_WRONLY | O_CREAT, 0644); umask(old_mask); if (fd == -1) i_fatal("creat(%s) failed: %m", temp_path); /* If multiple dovecot instances are running, only one of them needs to regenerate this file. */ ret = file_wait_lock(fd, temp_path, F_WRLCK, FILE_LOCK_METHOD_FCNTL, SSL_BUILD_PARAM_TIMEOUT_SECS, &lock); if (ret < 0) i_fatal("file_try_lock(%s) failed: %m", temp_path); if (ret == 0) { /* someone else is writing this */ i_fatal("Timeout while waiting for %s generation to complete", path); } /* make sure the .tmp file is still the one we created */ if (fstat(fd, &st) < 0) i_fatal("fstat(%s) failed: %m", temp_path); if (stat(temp_path, &st2) < 0) { if (errno != ENOENT) i_fatal("stat(%s) failed: %m", temp_path); st2.st_ino = st.st_ino+1; } if (st.st_ino != st2.st_ino) { /* nope. so someone else just generated the file. */ i_close_fd(&fd); return; } /* check that the parameters file is still the same */ if (stat(path, &st) == 0) { if (st.st_mtime != mtime) { i_close_fd(&fd); return; } } else if (errno != ENOENT) i_fatal("stat(%s) failed: %m", path); /* ok, we really want to generate it. */ if (ftruncate(fd, 0) < 0) i_fatal("ftruncate(%s) failed: %m", temp_path); i_info("Generating SSL parameters"); buf = buffer_create_dynamic(pool_datastack_create(), 1024); if (ssl_iostream_generate_params(buf, ssl_dh_parameters_length, &error) < 0) { i_fatal("ssl_iostream_generate_params(%u) failed: %s", ssl_dh_parameters_length, error); } if (write_full(fd, buf->data, buf->used) < 0) i_fatal("write(%s) failed: %m", temp_path); if (rename(temp_path, path) < 0) i_fatal("rename(%s, %s) failed: %m", temp_path, path); if (close(fd) < 0) i_fatal("close(%s) failed: %m", temp_path); file_lock_free(&lock); i_info("SSL parameters regeneration completed"); } static void ssl_params_close_listeners(void) { unsigned int i; /* we have forked, but the fds are still shared. we can't go io_remove()ing the fds from ioloop, because with many ioloops (e.g. epoll) the fds get removed from the main process's ioloop as well. so we'll just do the closing here manually. */ for (i = 0; i < master_service_get_socket_count(master_service); i++) { int fd = MASTER_LISTEN_FD_FIRST + i; if (close(fd) < 0) i_error("close(listener %d) failed: %m", fd); } } static void ssl_params_rebuild(struct ssl_params *param) { switch (fork()) { case -1: i_fatal("fork() failed: %m"); case 0: /* child - close listener fds so a long-running ssl-params doesn't cause Dovecot restart to fail */ ssl_params_close_listeners(); ssl_params_if_unchanged(param->path, param->last_mtime, param->set.ssl_dh_parameters_length); exit(0); default: /* parent */ break; } } static bool ssl_params_verify(struct ssl_params *param, const unsigned char *data, size_t size) { unsigned int bitsize, len; bool found = FALSE; /* ... */ while (size >= sizeof(bitsize)) { memcpy(&bitsize, data, sizeof(bitsize)); if (bitsize == 0) { if (found) return TRUE; i_warning("Regenerating %s for ssl_dh_parameters_length=%u", param->path, param->set.ssl_dh_parameters_length); return FALSE; } data += sizeof(bitsize); size -= sizeof(bitsize); if (bitsize == param->set.ssl_dh_parameters_length) found = TRUE; if (size < sizeof(len)) break; memcpy(&len, data, sizeof(len)); if (len > size - sizeof(len)) break; data += sizeof(bitsize) + len; size -= sizeof(bitsize) + len; } i_error("Corrupted %s", param->path); return FALSE; } static int ssl_params_read(struct ssl_params *param) { unsigned char *buffer; struct stat st; int fd, ret; fd = open(param->path, O_RDONLY); if (fd == -1) { if (errno != ENOENT) i_error("open(%s) failed: %m", param->path); return -1; } if (fstat(fd, &st) < 0) { i_error("fstat(%s) failed: %m", param->path); i_close_fd(&fd); return -1; } param->last_mtime = st.st_mtime; if (st.st_size == 0 || st.st_size > MAX_PARAM_FILE_SIZE) { i_error("Corrupted file: %s", param->path); i_close_fd(&fd); i_unlink(param->path); return -1; } buffer = t_malloc(st.st_size); ret = read_full(fd, buffer, st.st_size); if (ret < 0) i_error("read(%s) failed: %m", param->path); else if (ret == 0) { i_error("File unexpectedly shrank: %s", param->path); ret = -1; } else if (!ssl_params_verify(param, buffer, st.st_size)) { ret = -1; } else { param->callback(buffer, st.st_size); } if (close(fd) < 0) i_error("close(%s) failed: %m", param->path); return ret; } struct ssl_params * ssl_params_init(const char *path, ssl_params_callback_t *callback, const struct ssl_params_settings *set) { struct ssl_params *param; param = i_new(struct ssl_params, 1); param->path = i_strdup(path); param->set = *set; param->callback = callback; ssl_params_refresh(param); return param; } void ssl_params_refresh(struct ssl_params *param) { if (ssl_params_read(param) < 0) ssl_params_rebuild(param); } void ssl_params_deinit(struct ssl_params **_param) { struct ssl_params *param = *_param; *_param = NULL; i_free(param->path); i_free(param); } dovecot-2.2.33.2/src/ssl-params/main.c0000644000175000017500000001002113165463624014272 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "array.h" #include "ostream.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.h" #include "ssl-params-settings.h" #include "ssl-params.h" #include #define SSL_BUILD_PARAM_FNAME "ssl-parameters.dat" #define STARTUP_IDLE_TIMEOUT_MSECS 1000 struct client { int fd; struct ostream *output; }; static ARRAY(int) delayed_fds; static struct ssl_params *param; static buffer_t *ssl_params; static struct timeout *to_startup; static void client_deinit(struct ostream *output) { o_stream_destroy(&output); master_service_client_connection_destroyed(master_service); } static int client_output_flush(struct ostream *output) { if (o_stream_flush(output) == 0) { /* more to come */ return 0; } /* finished / disconnected */ client_deinit(output); return -1; } static void client_handle(int fd) { struct ostream *output; output = o_stream_create_fd_autoclose(&fd, (size_t)-1); if (o_stream_send(output, ssl_params->data, ssl_params->used) < 0 || o_stream_get_buffer_used_size(output) == 0) client_deinit(output); else { o_stream_set_flush_callback(output, client_output_flush, output); } } static void client_connected(struct master_service_connection *conn) { if (to_startup != NULL) timeout_remove(&to_startup); master_service_client_connection_accept(conn); if (ssl_params->used == 0) { /* waiting for parameter building to finish */ if (!array_is_created(&delayed_fds)) i_array_init(&delayed_fds, 32); array_append(&delayed_fds, &conn->fd, 1); } else { client_handle(conn->fd); } } static void ssl_params_callback(const unsigned char *data, size_t size) { const int *fds; buffer_set_used_size(ssl_params, 0); buffer_append(ssl_params, data, size); if (!array_is_created(&delayed_fds)) { /* if we don't get client connections soon, it means master ran us at startup to make sure ssl parameters are generated asap. if we're here because of that, don't bother hanging around to see if we get any client connections. */ if (to_startup == NULL) { to_startup = timeout_add(STARTUP_IDLE_TIMEOUT_MSECS, master_service_stop, master_service); } return; } array_foreach(&delayed_fds, fds) client_handle(*fds); array_free(&delayed_fds); } static void sig_chld(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { int status; if (waitpid(-1, &status, WNOHANG) < 0) i_error("waitpid() failed: %m"); else if (status != 0) i_error("child process failed with status %d", status); else { /* params should have been created now. try refreshing. */ ssl_params_refresh(param); } } static void main_init(const struct ssl_params_settings *set) { const struct master_service_settings *service_set; const char *filename; lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE, sig_chld, NULL); ssl_params = buffer_create_dynamic(default_pool, 1024); service_set = master_service_settings_get(master_service); filename = t_strconcat(service_set->state_dir, "/"SSL_BUILD_PARAM_FNAME, NULL); param = ssl_params_init(filename, ssl_params_callback, set); } static void main_deinit(void) { ssl_params_deinit(¶m); if (to_startup != NULL) timeout_remove(&to_startup); if (array_is_created(&delayed_fds)) array_free(&delayed_fds); } int main(int argc, char *argv[]) { const struct ssl_params_settings *set; master_service = master_service_init("ssl-params", 0, &argc, &argv, ""); master_service_init_log(master_service, "ssl-params: "); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; set = ssl_params_settings_read(master_service); restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); #ifndef HAVE_SSL i_fatal("Dovecot built without SSL support"); #endif main_init(set); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/ssl-params/Makefile.am0000644000175000017500000000072613165463624015251 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = ssl-params AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DPKG_STATEDIR=\""$(statedir)"\" ssl_params_LDADD = $(LIBDOVECOT) ssl_params_DEPENDENCIES = $(LIBDOVECOT_DEPS) ssl_params_SOURCES = \ main.c \ ssl-params.c \ ssl-params-settings.c noinst_HEADERS = \ ssl-params.h \ ssl-params-settings.h dovecot-2.2.33.2/src/ssl-params/ssl-params-settings.c0000644000175000017500000000473513165463624017305 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include "master-service-settings.h" #include "ssl-params-settings.h" #include #include /* */ static struct file_listener_settings ssl_params_unix_listeners_array[] = { { "ssl-params", 0666, "", "" }, { "login/ssl-params", 0666, "", "" } }; static struct file_listener_settings *ssl_params_unix_listeners[] = { &ssl_params_unix_listeners_array[0], &ssl_params_unix_listeners_array[1] }; static buffer_t ssl_params_unix_listeners_buf = { ssl_params_unix_listeners, sizeof(ssl_params_unix_listeners), { NULL, } }; /* */ struct service_settings ssl_params_service_settings = { .name = "ssl-params", .protocol = "", #ifdef HAVE_SSL .type = "startup", #else .type = "", #endif .executable = "ssl-params", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 0, .idle_kill = 0, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &ssl_params_unix_listeners_buf, sizeof(ssl_params_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct ssl_params_settings, name), NULL } static const struct setting_define ssl_params_setting_defines[] = { DEF(SET_TIME, ssl_parameters_regenerate), DEF(SET_UINT, ssl_dh_parameters_length), SETTING_DEFINE_LIST_END }; static const struct ssl_params_settings ssl_params_default_settings = { .ssl_parameters_regenerate = 0, .ssl_dh_parameters_length = 1024 }; const struct setting_parser_info ssl_params_setting_parser_info = { .module_name = "ssl-params", .defines = ssl_params_setting_defines, .defaults = &ssl_params_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct ssl_params_settings), .parent_offset = (size_t)-1 }; struct ssl_params_settings * ssl_params_settings_read(struct master_service *service) { static const struct setting_parser_info *set_roots[] = { &ssl_params_setting_parser_info, NULL }; const char *error; void **sets; if (master_service_settings_read_simple(service, set_roots, &error) < 0) i_fatal("Error reading configuration: %s", error); sets = master_service_settings_get_others(service); return sets[0]; } dovecot-2.2.33.2/src/lib-ssl-iostream/0002755000175000017500000000000013172375610014371 500000000000000dovecot-2.2.33.2/src/lib-ssl-iostream/Makefile.in0000644000175000017500000006456713172375573016406 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-ssl-iostream ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(pkginc_lib_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(moduledir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(module_LTLIBRARIES) $(noinst_LTLIBRARIES) libssl_iostream_la_LIBADD = am_libssl_iostream_la_OBJECTS = iostream-ssl.lo libssl_iostream_la_OBJECTS = $(am_libssl_iostream_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__DEPENDENCIES_1 = @BUILD_OPENSSL_TRUE@libssl_iostream_openssl_la_DEPENDENCIES = \ @BUILD_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) am__libssl_iostream_openssl_la_SOURCES_DIST = \ dovecot-openssl-common.c iostream-openssl.c \ iostream-openssl-common.c iostream-openssl-context.c \ iostream-openssl-params.c istream-openssl.c ostream-openssl.c @BUILD_OPENSSL_TRUE@am_libssl_iostream_openssl_la_OBJECTS = \ @BUILD_OPENSSL_TRUE@ dovecot-openssl-common.lo \ @BUILD_OPENSSL_TRUE@ iostream-openssl.lo \ @BUILD_OPENSSL_TRUE@ iostream-openssl-common.lo \ @BUILD_OPENSSL_TRUE@ iostream-openssl-context.lo \ @BUILD_OPENSSL_TRUE@ iostream-openssl-params.lo \ @BUILD_OPENSSL_TRUE@ istream-openssl.lo ostream-openssl.lo libssl_iostream_openssl_la_OBJECTS = \ $(am_libssl_iostream_openssl_la_OBJECTS) libssl_iostream_openssl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libssl_iostream_openssl_la_LDFLAGS) \ $(LDFLAGS) -o $@ @BUILD_OPENSSL_TRUE@am_libssl_iostream_openssl_la_rpath = -rpath \ @BUILD_OPENSSL_TRUE@ $(moduledir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libssl_iostream_la_SOURCES) \ $(libssl_iostream_openssl_la_SOURCES) DIST_SOURCES = $(libssl_iostream_la_SOURCES) \ $(am__libssl_iostream_openssl_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libssl_iostream.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -DMODULE_DIR=\""$(moduledir)"\" @BUILD_OPENSSL_TRUE@module_LTLIBRARIES = libssl_iostream_openssl.la @BUILD_OPENSSL_TRUE@libssl_iostream_openssl_la_LDFLAGS = -module -avoid-version @BUILD_OPENSSL_TRUE@libssl_iostream_openssl_la_LIBADD = $(SSL_LIBS) @BUILD_OPENSSL_TRUE@libssl_iostream_openssl_la_SOURCES = \ @BUILD_OPENSSL_TRUE@ dovecot-openssl-common.c \ @BUILD_OPENSSL_TRUE@ iostream-openssl.c \ @BUILD_OPENSSL_TRUE@ iostream-openssl-common.c \ @BUILD_OPENSSL_TRUE@ iostream-openssl-context.c \ @BUILD_OPENSSL_TRUE@ iostream-openssl-params.c \ @BUILD_OPENSSL_TRUE@ istream-openssl.c \ @BUILD_OPENSSL_TRUE@ ostream-openssl.c libssl_iostream_la_SOURCES = \ iostream-ssl.c noinst_HEADERS = \ dovecot-openssl-common.h headers = \ iostream-openssl.h \ iostream-ssl.h \ iostream-ssl-private.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-ssl-iostream/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-ssl-iostream/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-moduleLTLIBRARIES: $(module_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ } uninstall-moduleLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ done clean-moduleLTLIBRARIES: -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) @list='$(module_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libssl_iostream.la: $(libssl_iostream_la_OBJECTS) $(libssl_iostream_la_DEPENDENCIES) $(EXTRA_libssl_iostream_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libssl_iostream_la_OBJECTS) $(libssl_iostream_la_LIBADD) $(LIBS) libssl_iostream_openssl.la: $(libssl_iostream_openssl_la_OBJECTS) $(libssl_iostream_openssl_la_DEPENDENCIES) $(EXTRA_libssl_iostream_openssl_la_DEPENDENCIES) $(AM_V_CCLD)$(libssl_iostream_openssl_la_LINK) $(am_libssl_iostream_openssl_la_rpath) $(libssl_iostream_openssl_la_OBJECTS) $(libssl_iostream_openssl_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dovecot-openssl-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-openssl-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-openssl-context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-openssl-params.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-openssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-ssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-openssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-openssl.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(moduledir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-moduleLTLIBRARIES install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-moduleLTLIBRARIES clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-moduleLTLIBRARIES install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-moduleLTLIBRARIES uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-openssl-context.c0000644000175000017500000003737713165463624021467 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "safe-memset.h" #include "iostream-openssl.h" #include "dovecot-openssl-common.h" #include #include #include #include #include #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L # define HAVE_ECDH #endif struct ssl_iostream_password_context { const char *password; const char *error; }; static bool ssl_global_initialized = FALSE; int dovecot_ssl_extdata_index; static int ssl_iostream_init_global(const struct ssl_iostream_settings *set, const char **error_r); static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED, int is_export ATTR_UNUSED, int keylength) { #ifdef HAVE_RSA_GENERATE_KEY_EX BIGNUM *bn = BN_new(); RSA *rsa = RSA_new(); if (bn != NULL && BN_set_word(bn, RSA_F4) != 0 && RSA_generate_key_ex(rsa, keylength, bn, NULL) != 0) { BN_free(bn); return rsa; } if (bn != NULL) BN_free(bn); if (rsa != NULL) RSA_free(rsa); return NULL; #else return RSA_generate_key(keylength, RSA_F4, NULL, NULL); #endif } static DH *ssl_tmp_dh_callback(SSL *ssl ATTR_UNUSED, int is_export, int keylength) { struct ssl_iostream *ssl_io; ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index); /* Well, I'm not exactly sure why the logic in here is this. It's the same as in Postfix, so it can't be too wrong. */ if (is_export && keylength == 512 && ssl_io->ctx->dh_512 != NULL) return ssl_io->ctx->dh_512; else return ssl_io->ctx->dh_default; } static int pem_password_callback(char *buf, int size, int rwflag ATTR_UNUSED, void *userdata) { struct ssl_iostream_password_context *ctx = userdata; if (ctx->password == NULL) { ctx->error = "SSL private key file is password protected, " "but password isn't given"; return 0; } if (i_strocpy(buf, userdata, size) < 0) { ctx->error = "SSL private key password is too long"; return 0; } return strlen(buf); } int openssl_iostream_load_key(const struct ssl_iostream_settings *set, EVP_PKEY **pkey_r, const char **error_r) { struct ssl_iostream_password_context ctx; EVP_PKEY *pkey; BIO *bio; char *key; key = t_strdup_noconst(set->key); bio = BIO_new_mem_buf(key, strlen(key)); if (bio == NULL) { *error_r = t_strdup_printf("BIO_new_mem_buf() failed: %s", openssl_iostream_error()); safe_memset(key, 0, strlen(key)); return -1; } ctx.password = set->key_password; ctx.error = NULL; pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback, &ctx); if (pkey == NULL && ctx.error == NULL) { ctx.error = t_strdup_printf("Couldn't parse private SSL key: %s", openssl_iostream_error()); } BIO_free(bio); safe_memset(key, 0, strlen(key)); *pkey_r = pkey; *error_r = ctx.error; return pkey == NULL ? -1 : 0; } static int ssl_iostream_ctx_use_key(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, const char **error_r) { EVP_PKEY *pkey; int ret = 0; if (openssl_iostream_load_key(set, &pkey, error_r) < 0) return -1; if (!SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey)) { *error_r = t_strdup_printf( "Can't load SSL private key: %s", openssl_iostream_key_load_error()); ret = -1; } EVP_PKEY_free(pkey); return ret; } static int ssl_ctx_use_certificate_chain(SSL_CTX *ctx, const char *cert) { /* mostly just copy&pasted from SSL_CTX_use_certificate_chain_file() */ BIO *in; X509 *x; int ret = 0; in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert)); if (in == NULL) i_fatal("BIO_new_mem_buf() failed"); x = PEM_read_bio_X509(in, NULL, NULL, NULL); if (x == NULL) goto end; ret = SSL_CTX_use_certificate(ctx, x); if (ERR_peek_error() != 0) ret = 0; if (ret != 0) { /* If we could set up our certificate, now proceed to * the CA certificates. */ X509 *ca; int r; unsigned long err; while ((ca = PEM_read_bio_X509(in,NULL,NULL,NULL)) != NULL) { r = SSL_CTX_add_extra_chain_cert(ctx, ca); if (!r) { X509_free(ca); ret = 0; goto end; } } /* When the while loop ends, it's usually just EOF. */ err = ERR_peek_last_error(); if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) ERR_clear_error(); else ret = 0; /* some real error */ } end: if (x != NULL) X509_free(x); BIO_free(in); return ret; } static int load_ca(X509_STORE *store, const char *ca, STACK_OF(X509_NAME) **xnames_r) { /* mostly just copy&pasted from X509_load_cert_crl_file() */ STACK_OF(X509_INFO) *inf; STACK_OF(X509_NAME) *xnames; X509_INFO *itmp; X509_NAME *xname; BIO *bio; int i; bio = BIO_new_mem_buf(t_strdup_noconst(ca), strlen(ca)); if (bio == NULL) i_fatal("BIO_new_mem_buf() failed"); inf = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); BIO_free(bio); if (inf == NULL) return -1; xnames = sk_X509_NAME_new_null(); if (xnames == NULL) i_fatal("sk_X509_NAME_new_null() failed"); for(i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { X509_STORE_add_cert(store, itmp->x509); xname = X509_get_subject_name(itmp->x509); if (xname != NULL) xname = X509_NAME_dup(xname); if (xname != NULL) sk_X509_NAME_push(xnames, xname); } if(itmp->crl) X509_STORE_add_crl(store, itmp->crl); } sk_X509_INFO_pop_free(inf, X509_INFO_free); *xnames_r = xnames; return 0; } static void ssl_iostream_ctx_verify_remote_cert(struct ssl_iostream_context *ctx, STACK_OF(X509_NAME) *ca_names) { #if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE *store; store = SSL_CTX_get_cert_store(ctx->ssl_ctx); X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #endif SSL_CTX_set_client_CA_list(ctx->ssl_ctx, ca_names); } static struct ssl_iostream_settings * ssl_iostream_settings_dup(pool_t pool, const struct ssl_iostream_settings *old_set) { struct ssl_iostream_settings *new_set; new_set = p_new(pool, struct ssl_iostream_settings, 1); new_set->protocols = p_strdup(pool, old_set->protocols); new_set->cipher_list = p_strdup(pool, old_set->cipher_list); new_set->cert = p_strdup(pool, old_set->cert); new_set->key = p_strdup(pool, old_set->key); new_set->key_password = p_strdup(pool, old_set->key_password); new_set->verbose = old_set->verbose; return new_set; } #ifdef HAVE_SSL_GET_SERVERNAME static int ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED, void *context ATTR_UNUSED) { struct ssl_iostream *ssl_io; const char *host; ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index); host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (SSL_get_servername_type(ssl) != -1) { i_free(ssl_io->host); ssl_io->host = i_strdup(host); } else if (ssl_io->verbose) { i_debug("SSL_get_servername() failed"); } return SSL_TLSEXT_ERR_OK; } #endif static int ssl_iostream_context_load_ca(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, const char **error_r) { X509_STORE *store; STACK_OF(X509_NAME) *xnames = NULL; const char *ca_file, *ca_dir; bool have_ca = FALSE; if (set->ca != NULL) { store = SSL_CTX_get_cert_store(ctx->ssl_ctx); if (load_ca(store, set->ca, &xnames) < 0) { *error_r = t_strdup_printf("Couldn't parse ssl_ca: %s", openssl_iostream_error()); return -1; } ssl_iostream_ctx_verify_remote_cert(ctx, xnames); have_ca = TRUE; } ca_file = set->ca_file == NULL || *set->ca_file == '\0' ? NULL : set->ca_file; ca_dir = set->ca_dir == NULL || *set->ca_dir == '\0' ? NULL : set->ca_dir; if (ca_file != NULL || ca_dir != NULL) { if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_dir)) { *error_r = t_strdup_printf( "Can't load CA certs from directory %s: %s", set->ca_dir, openssl_iostream_error()); return -1; } have_ca = TRUE; } if (!have_ca && set->require_valid_cert) { *error_r = !ctx->client_ctx ? "Can't verify remote client certs without CA (ssl_ca setting)" : "Can't verify remote server certs without trusted CAs (ssl_client_ca_* settings)"; return -1; } return 0; } static int ssl_iostream_context_set(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, const char **error_r) { ctx->set = ssl_iostream_settings_dup(ctx->pool, set); if (set->cipher_list != NULL && !SSL_CTX_set_cipher_list(ctx->ssl_ctx, set->cipher_list)) { *error_r = t_strdup_printf("Can't set cipher list to '%s': %s", set->cipher_list, openssl_iostream_error()); return -1; } if (set->prefer_server_ciphers) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } if (ctx->set->protocols != NULL) { SSL_CTX_set_options(ctx->ssl_ctx, openssl_get_protocol_options(ctx->set->protocols)); } if (set->cert != NULL && ssl_ctx_use_certificate_chain(ctx->ssl_ctx, set->cert) == 0) { *error_r = t_strdup_printf("Can't load SSL certificate: %s", openssl_iostream_use_certificate_error(set->cert, NULL)); return -1; } if (set->key != NULL) { if (ssl_iostream_ctx_use_key(ctx, set, error_r) < 0) return -1; } /* set trusted CA certs */ if (set->verify_remote_cert) { if (ssl_iostream_context_load_ca(ctx, set, error_r) < 0) return -1; } if (set->cert_username_field != NULL) { ctx->username_nid = OBJ_txt2nid(set->cert_username_field); if (ctx->username_nid == NID_undef) { *error_r = t_strdup_printf( "Invalid cert_username_field: %s", set->cert_username_field); return -1; } } #ifdef HAVE_SSL_GET_SERVERNAME if (!ctx->client_ctx) { if (SSL_CTX_set_tlsext_servername_callback(ctx->ssl_ctx, ssl_servername_callback) != 1) { if (set->verbose) i_debug("OpenSSL library doesn't support SNI"); } } #endif return 0; } #if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO) static int ssl_proxy_ctx_get_pkey_ec_curve_name(const struct ssl_iostream_settings *set, int *nid_r, const char **error_r) { int nid = 0; EVP_PKEY *pkey; EC_KEY *eckey; const EC_GROUP *ecgrp; if (set->key != NULL) { if (openssl_iostream_load_key(set, &pkey, error_r) < 0) return -1; if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL && (ecgrp = EC_KEY_get0_group(eckey)) != NULL) nid = EC_GROUP_get_curve_name(ecgrp); else { /* clear errors added by the above calls */ openssl_iostream_clear_errors(); } EVP_PKEY_free(pkey); } *nid_r = nid; return 0; } #endif static int ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx, const struct ssl_iostream_settings *set ATTR_UNUSED, const char **error_r ATTR_UNUSED) { #if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO) EC_KEY *ecdh; int nid; const char *curve_name; #endif if (SSL_CTX_need_tmp_RSA(ssl_ctx)) SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); #ifdef HAVE_ECDH /* In the non-recommended situation where ECDH cipher suites are being used instead of ECDHE, do not reuse the same ECDH key pair for different sessions. This option improves forward secrecy. */ SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_ECDH_USE); #ifdef SSL_CTRL_SET_ECDH_AUTO /* OpenSSL >= 1.0.2 automatically handles ECDH temporary key parameter selection. */ if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) { /* shouldn't happen */ *error_r = t_strdup_printf("SSL_CTX_set_ecdh_auto() failed: %s", openssl_iostream_error()); return -1; } #else /* For OpenSSL < 1.0.2, ECDH temporary key parameter selection must be performed manually. Attempt to select the same curve as that used in the server's private EC key file. Otherwise fall back to the NIST P-384 (secp384r1) curve to be compliant with RFC 6460 when AES-256 TLS cipher suites are in use. This fall back option does however make Dovecot non-compliant with RFC 6460 which requires curve NIST P-256 (prime256v1) be used when AES-128 TLS cipher suites are in use. At least the non-compliance is in the form of providing too much security rather than too little. */ if (ssl_proxy_ctx_get_pkey_ec_curve_name(set, &nid, error_r) < 0) return -1; ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { /* Fall back option */ nid = NID_secp384r1; ecdh = EC_KEY_new_by_curve_name(nid); } if ((curve_name = OBJ_nid2sn(nid)) != NULL && set->verbose) { i_debug("SSL: elliptic curve %s will be used for ECDH and" " ECDHE key exchanges", curve_name); } if (ecdh != NULL) { SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); EC_KEY_free(ecdh); } #endif #endif return 0; } static int ssl_iostream_context_init_common(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, const char **error_r) { long ssl_ops = SSL_OP_NO_SSLv2 | (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); ctx->pool = pool_alloconly_create("ssl iostream context", 4096); /* enable all SSL workarounds, except empty fragments as it makes SSL more vulnerable against attacks */ #ifdef SSL_OP_NO_COMPRESSION if (!set->compression) ssl_ops |= SSL_OP_NO_COMPRESSION; #endif #ifdef SSL_OP_NO_TICKET if (!set->tickets) ssl_ops |= SSL_OP_NO_TICKET; #endif SSL_CTX_set_options(ctx->ssl_ctx, ssl_ops); #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif if (ssl_proxy_ctx_set_crypto_params(ctx->ssl_ctx, set, error_r) < 0) return -1; return ssl_iostream_context_set(ctx, set, error_r); } int openssl_iostream_context_init_client(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { struct ssl_iostream_settings set_copy = *set; struct ssl_iostream_context *ctx; SSL_CTX *ssl_ctx; /* ensure this is set to TRUE */ set_copy.verify_remote_cert = TRUE; if (ssl_iostream_init_global(&set_copy, error_r) < 0) return -1; if ((ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { *error_r = t_strdup_printf("SSL_CTX_new() failed: %s", openssl_iostream_error()); return -1; } SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); ctx = i_new(struct ssl_iostream_context, 1); ctx->ssl_ctx = ssl_ctx; ctx->client_ctx = TRUE; if (ssl_iostream_context_init_common(ctx, &set_copy, error_r) < 0) { ssl_iostream_context_deinit(&ctx); return -1; } *ctx_r = ctx; return 0; } int openssl_iostream_context_init_server(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { struct ssl_iostream_context *ctx; SSL_CTX *ssl_ctx; if (ssl_iostream_init_global(set, error_r) < 0) return -1; if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { *error_r = t_strdup_printf("SSL_CTX_new() failed: %s", openssl_iostream_error()); return -1; } ctx = i_new(struct ssl_iostream_context, 1); ctx->ssl_ctx = ssl_ctx; if (ssl_iostream_context_init_common(ctx, set, error_r) < 0) { ssl_iostream_context_deinit(&ctx); return -1; } *ctx_r = ctx; return 0; } void openssl_iostream_context_deinit(struct ssl_iostream_context *ctx) { SSL_CTX_free(ctx->ssl_ctx); openssl_iostream_context_free_params(ctx); pool_unref(&ctx->pool); i_free(ctx); } void openssl_iostream_global_deinit(void) { dovecot_openssl_common_global_unref(); } static int ssl_iostream_init_global(const struct ssl_iostream_settings *set, const char **error_r) { static char dovecot[] = "dovecot"; const char *error; if (ssl_global_initialized) return 0; ssl_global_initialized = TRUE; dovecot_openssl_common_global_ref(); dovecot_ssl_extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL); if (set->crypto_device != NULL && *set->crypto_device != '\0') { switch (dovecot_openssl_common_global_set_engine(set->crypto_device, &error)) { case 0: error = t_strdup_printf( "Unknown ssl_crypto_device: %s", set->crypto_device); /* fall through */ case -1: *error_r = error; /* we'll deinit at exit in any case */ return -1; } } return 0; } dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-ssl.c0000644000175000017500000001355313165463624017111 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "iostream-ssl-private.h" static bool ssl_module_loaded = FALSE; #ifdef HAVE_SSL static struct module *ssl_module = NULL; #endif static const struct iostream_ssl_vfuncs *ssl_vfuncs = NULL; #ifdef HAVE_SSL static void ssl_module_unload(void) { module_dir_unload(&ssl_module); } #endif void iostream_ssl_module_init(const struct iostream_ssl_vfuncs *vfuncs) { ssl_vfuncs = vfuncs; ssl_module_loaded = TRUE; } static int ssl_module_load(const char **error_r) { #ifdef HAVE_SSL const char *plugin_name = "ssl_iostream_openssl"; struct module_dir_load_settings mod_set; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.setting_name = ""; mod_set.require_init_funcs = TRUE; ssl_module = module_dir_load(MODULE_DIR, plugin_name, &mod_set); if (module_dir_try_load_missing(&ssl_module, MODULE_DIR, plugin_name, &mod_set, error_r) < 0) return -1; module_dir_init(ssl_module); if (!ssl_module_loaded) { *error_r = t_strdup_printf( "%s didn't call iostream_ssl_module_init() - SSL not initialized", plugin_name); module_dir_unload(&ssl_module); return -1; } /* Destroy SSL module after (most of) the others. Especially lib-fs backends may still want to access SSL module in their own atexit-callbacks. */ lib_atexit_priority(ssl_module_unload, LIB_ATEXIT_PRIORITY_LOW); return 0; #else *error_r = "SSL support not compiled in"; return -1; #endif } int ssl_iostream_context_init_client(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { if (!ssl_module_loaded) { if (ssl_module_load(error_r) < 0) return -1; } return ssl_vfuncs->context_init_client(set, ctx_r, error_r); } int ssl_iostream_context_init_server(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { if (!ssl_module_loaded) { if (ssl_module_load(error_r) < 0) return -1; } return ssl_vfuncs->context_init_server(set, ctx_r, error_r); } void ssl_iostream_context_deinit(struct ssl_iostream_context **_ctx) { struct ssl_iostream_context *ctx = *_ctx; *_ctx = NULL; ssl_vfuncs->context_deinit(ctx); } int ssl_iostream_generate_params(buffer_t *output, unsigned int dh_length, const char **error_r) { if (!ssl_module_loaded) { if (ssl_module_load(error_r) < 0) return -1; } return ssl_vfuncs->generate_params(output, dh_length, error_r); } int ssl_iostream_context_import_params(struct ssl_iostream_context *ctx, const buffer_t *input) { return ssl_vfuncs->context_import_params(ctx, input); } int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r) { struct ssl_iostream_settings set_copy = *set; set_copy.verify_remote_cert = TRUE; return ssl_vfuncs->create(ctx, host, &set_copy, input, output, iostream_r, error_r); } int io_stream_create_ssl_server(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r) { return ssl_vfuncs->create(ctx, NULL, set, input, output, iostream_r, error_r); } void ssl_iostream_unref(struct ssl_iostream **_ssl_io) { struct ssl_iostream *ssl_io = *_ssl_io; *_ssl_io = NULL; ssl_vfuncs->unref(ssl_io); } void ssl_iostream_destroy(struct ssl_iostream **_ssl_io) { struct ssl_iostream *ssl_io = *_ssl_io; *_ssl_io = NULL; ssl_vfuncs->destroy(ssl_io); } void ssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io, const char *prefix) { ssl_vfuncs->set_log_prefix(ssl_io, prefix); } int ssl_iostream_handshake(struct ssl_iostream *ssl_io) { return ssl_vfuncs->handshake(ssl_io); } void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context) { ssl_vfuncs->set_handshake_callback(ssl_io, callback, context); } bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->is_handshaked(ssl_io); } bool ssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_handshake_failed(ssl_io); } bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_valid_client_cert(ssl_io); } bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_broken_client_cert(ssl_io); } int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *name) { return ssl_vfuncs->cert_match_name(ssl_io, name); } int ssl_iostream_check_cert_validity(struct ssl_iostream *ssl_io, const char *host, const char **error_r) { if (!ssl_iostream_has_valid_client_cert(ssl_io)) { if (!ssl_iostream_has_broken_client_cert(ssl_io)) *error_r = "SSL certificate not received"; else { *error_r = t_strdup(ssl_iostream_get_last_error(ssl_io)); if (*error_r == NULL) *error_r = "Received invalid SSL certificate"; } return -1; } else if (ssl_iostream_cert_match_name(ssl_io, host) < 0) { *error_r = t_strdup_printf( "SSL certificate doesn't match expected host name %s", host); return -1; } return 0; } const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_peer_name(ssl_io); } const char *ssl_iostream_get_server_name(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_server_name(ssl_io); } const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_security_string(ssl_io); } const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_last_error(ssl_io); } dovecot-2.2.33.2/src/lib-ssl-iostream/istream-openssl.c0000644000175000017500000000740013165463624017606 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "iostream-openssl.h" struct ssl_istream { struct istream_private istream; struct ssl_iostream *ssl_io; bool seen_eof; }; static void i_stream_ssl_close(struct iostream_private *stream, bool close_parent) { struct ssl_istream *sstream = (struct ssl_istream *)stream; if (close_parent) i_stream_close(sstream->ssl_io->plain_input); } static void i_stream_ssl_destroy(struct iostream_private *stream) { struct ssl_istream *sstream = (struct ssl_istream *)stream; i_free(sstream->istream.w_buffer); sstream->ssl_io->ssl_input = NULL; ssl_iostream_unref(&sstream->ssl_io); } static ssize_t i_stream_ssl_read_real(struct istream_private *stream) { struct ssl_istream *sstream = (struct ssl_istream *)stream; struct ssl_iostream *ssl_io = sstream->ssl_io; unsigned char buffer[IO_BLOCK_SIZE]; size_t max_buffer_size = i_stream_get_max_buffer_size(&stream->istream); size_t orig_max_buffer_size = stream->max_buffer_size; size_t size; ssize_t ret, total_ret; if (sstream->seen_eof) { stream->istream.eof = TRUE; return -1; } if (stream->pos >= max_buffer_size) { i_stream_compress(stream); if (stream->pos >= max_buffer_size) return -2; } ret = openssl_iostream_more(ssl_io); if (ret <= 0) { if (ret < 0) { /* handshake failed */ i_assert(errno != 0); io_stream_set_error(&stream->iostream, "%s", ssl_io->last_error); stream->istream.stream_errno = errno; } return ret; } if (!i_stream_try_alloc(stream, 1, &size)) i_unreached(); if (stream->pos + size > max_buffer_size) { i_assert(max_buffer_size > stream->pos); size = max_buffer_size - stream->pos; } while ((ret = SSL_read(ssl_io->ssl, stream->w_buffer + stream->pos, size)) <= 0) { /* failed to read anything */ ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_read"); if (ret <= 0) { if (ret == 0) return 0; if (ssl_io->last_error != NULL) { io_stream_set_error(&stream->iostream, "%s", ssl_io->last_error); } if (errno != EPIPE) stream->istream.stream_errno = errno; stream->istream.eof = TRUE; sstream->seen_eof = TRUE; return -1; } /* we did some BIO I/O, try reading again */ } stream->pos += ret; total_ret = ret; /* now make sure that we read everything already buffered in OpenSSL into the stream (without reading anything more). this makes I/O loop behave similary for ssl-istream as file-istream. */ sstream->ssl_io->input_handler = FALSE; stream->max_buffer_size = (size_t)-1; while ((ret = SSL_read(ssl_io->ssl, buffer, sizeof(buffer))) > 0) { memcpy(i_stream_alloc(stream, ret), buffer, ret); stream->pos += ret; total_ret += ret; } stream->max_buffer_size = orig_max_buffer_size; return total_ret; } static ssize_t i_stream_ssl_read(struct istream_private *stream) { struct ssl_istream *sstream = (struct ssl_istream *)stream; ssize_t ret; sstream->ssl_io->input_handler = TRUE; if ((ret = i_stream_ssl_read_real(stream)) >= 0) { i_assert(i_stream_get_data_size(sstream->ssl_io->plain_input) == 0); } sstream->ssl_io->input_handler = FALSE; return ret; } struct istream *openssl_i_stream_create_ssl(struct ssl_iostream *ssl_io) { struct ssl_istream *sstream; ssl_io->refcount++; sstream = i_new(struct ssl_istream, 1); sstream->ssl_io = ssl_io; sstream->istream.iostream.close = i_stream_ssl_close; sstream->istream.iostream.destroy = i_stream_ssl_destroy; sstream->istream.max_buffer_size = ssl_io->plain_input->real_stream->max_buffer_size; sstream->istream.read = i_stream_ssl_read; sstream->istream.istream.readable_fd = FALSE; return i_stream_create(&sstream->istream, NULL, i_stream_get_fd(ssl_io->plain_input)); } dovecot-2.2.33.2/src/lib-ssl-iostream/ostream-openssl.c0000644000175000017500000001573613165463624017627 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "ostream-private.h" #include "iostream-openssl.h" struct ssl_ostream { struct ostream_private ostream; struct ssl_iostream *ssl_io; buffer_t *buffer; }; static void o_stream_ssl_close(struct iostream_private *stream, bool close_parent) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; if (close_parent) o_stream_close(sstream->ssl_io->plain_output); } static void o_stream_ssl_destroy(struct iostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; sstream->ssl_io->ssl_output = NULL; ssl_iostream_unref(&sstream->ssl_io); if (sstream->buffer != NULL) buffer_free(&sstream->buffer); } static size_t o_stream_ssl_buffer(struct ssl_ostream *sstream, const struct const_iovec *iov, unsigned int iov_count, size_t bytes_sent) { size_t avail, skip_left, size; unsigned int i; if (sstream->buffer == NULL) sstream->buffer = buffer_create_dynamic(default_pool, 4096); skip_left = bytes_sent; for (i = 0; i < iov_count; i++) { if (skip_left < iov[i].iov_len) break; skip_left -= iov[i].iov_len; } if (sstream->ostream.max_buffer_size == 0) { /* we're requeted to use whatever space is available in the buffer */ avail = buffer_get_writable_size(sstream->buffer) - sstream->buffer->used; } else { avail = sstream->ostream.max_buffer_size > sstream->buffer->used ? sstream->ostream.max_buffer_size - sstream->buffer->used : 0; } if (i < iov_count && skip_left > 0) { size = I_MIN(iov[i].iov_len - skip_left, avail); buffer_append(sstream->buffer, CONST_PTR_OFFSET(iov[i].iov_base, skip_left), size); bytes_sent += size; avail -= size; if (size != iov[i].iov_len) i = iov_count; } if (avail > 0) o_stream_set_flush_pending(sstream->ssl_io->plain_output, TRUE); for (; i < iov_count; i++) { size = I_MIN(iov[i].iov_len, avail); buffer_append(sstream->buffer, iov[i].iov_base, size); bytes_sent += size; avail -= size; if (size != iov[i].iov_len) break; } sstream->ostream.ostream.offset += bytes_sent; return bytes_sent; } static int o_stream_ssl_flush_buffer(struct ssl_ostream *sstream) { size_t pos = 0; int ret = 1; while (pos < sstream->buffer->used) { /* we're writing plaintext data to OpenSSL, which it encrypts and writes to bio_int's buffer. ssl_iostream_bio_sync() reads it from there and adds to plain_output stream. */ ret = SSL_write(sstream->ssl_io->ssl, CONST_PTR_OFFSET(sstream->buffer->data, pos), sstream->buffer->used - pos); if (ret <= 0) { ret = openssl_iostream_handle_write_error(sstream->ssl_io, ret, "SSL_write"); if (ret < 0) { io_stream_set_error(&sstream->ostream.iostream, "%s", sstream->ssl_io->last_error); sstream->ostream.ostream.stream_errno = errno; break; } if (ret == 0) break; } else { pos += ret; (void)openssl_iostream_bio_sync(sstream->ssl_io); } } buffer_delete(sstream->buffer, 0, pos); return ret <= 0 ? ret : 1; } static int o_stream_ssl_flush(struct ostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; int ret; if ((ret = openssl_iostream_more(sstream->ssl_io)) < 0) { /* handshake failed */ io_stream_set_error(&stream->iostream, "%s", sstream->ssl_io->last_error); stream->ostream.stream_errno = errno; } else if (ret > 0 && sstream->buffer != NULL && sstream->buffer->used > 0) { /* we can try to send some of our buffered data */ ret = o_stream_ssl_flush_buffer(sstream); } if (ret == 0 && sstream->ssl_io->want_read) { /* we need to read more data until we can continue. */ o_stream_set_flush_pending(sstream->ssl_io->plain_output, FALSE); sstream->ssl_io->ostream_flush_waiting_input = TRUE; ret = 1; } return ret; } static ssize_t o_stream_ssl_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; size_t bytes_sent = 0; bytes_sent = o_stream_ssl_buffer(sstream, iov, iov_count, bytes_sent); if (sstream->ssl_io->handshaked && sstream->buffer->used == bytes_sent) { /* buffer was empty before calling this. try to write it immediately. */ if (o_stream_ssl_flush_buffer(sstream) < 0) return -1; } return bytes_sent; } static void o_stream_ssl_switch_ioloop(struct ostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; o_stream_switch_ioloop(sstream->ssl_io->plain_output); } static int plain_flush_callback(struct ssl_ostream *sstream) { struct ostream *ostream = &sstream->ostream.ostream; int ret, ret2; /* try to actually flush the pending data */ if ((ret = o_stream_flush(sstream->ssl_io->plain_output)) < 0) return -1; /* we may be able to copy more data, try it */ o_stream_ref(ostream); if (sstream->ostream.callback != NULL) ret2 = sstream->ostream.callback(sstream->ostream.context); else ret2 = o_stream_flush(&sstream->ostream.ostream); if (ret2 == 0) o_stream_set_flush_pending(sstream->ssl_io->plain_output, TRUE); o_stream_unref(&ostream); if (ret2 < 0) return -1; return ret > 0 && ret2 > 0 ? 1 : 0; } static size_t o_stream_ssl_get_used_size(const struct ostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; return o_stream_get_buffer_used_size(sstream->ssl_io->plain_output); } static void o_stream_ssl_flush_pending(struct ostream_private *_stream, bool set) { struct ssl_ostream *sstream = (struct ssl_ostream *)_stream; o_stream_set_flush_pending(sstream->ssl_io->plain_output, set); } static void o_stream_ssl_set_max_buffer_size(struct iostream_private *_stream, size_t max_size) { struct ssl_ostream *sstream = (struct ssl_ostream *)_stream; sstream->ostream.max_buffer_size = max_size; o_stream_set_max_buffer_size(sstream->ssl_io->plain_output, max_size); } struct ostream *openssl_o_stream_create_ssl(struct ssl_iostream *ssl_io) { struct ssl_ostream *sstream; ssl_io->refcount++; sstream = i_new(struct ssl_ostream, 1); sstream->ssl_io = ssl_io; sstream->ostream.max_buffer_size = ssl_io->plain_output->real_stream->max_buffer_size; sstream->ostream.iostream.close = o_stream_ssl_close; sstream->ostream.iostream.destroy = o_stream_ssl_destroy; sstream->ostream.sendv = o_stream_ssl_sendv; sstream->ostream.flush = o_stream_ssl_flush; sstream->ostream.switch_ioloop = o_stream_ssl_switch_ioloop; sstream->ostream.get_used_size = o_stream_ssl_get_used_size; sstream->ostream.flush_pending = o_stream_ssl_flush_pending; sstream->ostream.iostream.set_max_buffer_size = o_stream_ssl_set_max_buffer_size; sstream->ostream.callback = ssl_io->plain_output->real_stream->callback; sstream->ostream.context = ssl_io->plain_output->real_stream->context; o_stream_set_flush_callback(ssl_io->plain_output, plain_flush_callback, sstream); return o_stream_create(&sstream->ostream, NULL, o_stream_get_fd(ssl_io->plain_output)); } dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-openssl.h0000644000175000017500000000675013165463624020001 00000000000000#ifndef IOSTREAM_OPENSSL_H #define IOSTREAM_OPENSSL_H #include "iostream-ssl-private.h" #include #ifndef HAVE_ASN1_STRING_GET0_DATA # define ASN1_STRING_get0_data(str) ASN1_STRING_data(str) #endif struct ssl_iostream_context { SSL_CTX *ssl_ctx; pool_t pool; const struct ssl_iostream_settings *set; DH *dh_512, *dh_default; int username_nid; unsigned int client_ctx:1; }; struct ssl_iostream { int refcount; struct ssl_iostream_context *ctx; SSL *ssl; BIO *bio_ext; struct istream *plain_input; struct ostream *plain_output; struct istream *ssl_input; struct ostream *ssl_output; char *host; char *last_error; char *log_prefix; char *plain_stream_errstr; int plain_stream_errno; /* copied settings */ bool verbose, verbose_invalid_cert, require_valid_cert; int username_nid; ssl_iostream_handshake_callback_t *handshake_callback; void *handshake_context; unsigned int handshaked:1; unsigned int handshake_failed:1; unsigned int cert_received:1; unsigned int cert_broken:1; unsigned int want_read:1; unsigned int input_handler:1; unsigned int ostream_flush_waiting_input:1; unsigned int closed:1; }; extern int dovecot_ssl_extdata_index; struct istream *openssl_i_stream_create_ssl(struct ssl_iostream *ssl_io); struct ostream *openssl_o_stream_create_ssl(struct ssl_iostream *ssl_io); int openssl_iostream_context_init_client(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); int openssl_iostream_context_init_server(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); void openssl_iostream_context_deinit(struct ssl_iostream_context *ctx); void openssl_iostream_global_deinit(void); int openssl_iostream_load_key(const struct ssl_iostream_settings *set, EVP_PKEY **pkey_r, const char **error_r); int openssl_cert_match_name(SSL *ssl, const char *verify_name); int openssl_get_protocol_options(const char *protocols); #define OPENSSL_ALL_PROTOCOL_OPTIONS \ (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1) /* Sync plain_input/plain_output streams with BIOs. Returns TRUE if at least one byte was read/written. */ bool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io); /* Call when there's more data available in plain_input/plain_output. Returns 1 if it's ok to continue with SSL_read/SSL_write, 0 if not (still handshaking), -1 if error occurred. */ int openssl_iostream_more(struct ssl_iostream *ssl_io); /* Returns 1 if the operation should be retried (we read/wrote more data), 0 if the operation should retried later once more data has been read/written, -1 if a fatal error occurred (errno is set). */ int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret, const char *func_name); int openssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret, const char *func_name); const char *openssl_iostream_error(void); const char *openssl_iostream_key_load_error(void); const char * openssl_iostream_use_certificate_error(const char *cert, const char *set_name); void openssl_iostream_clear_errors(void); int openssl_iostream_generate_params(buffer_t *output, unsigned int dh_length, const char **error_r); int openssl_iostream_context_import_params(struct ssl_iostream_context *ctx, const buffer_t *input); void openssl_iostream_context_free_params(struct ssl_iostream_context *ctx); void ssl_iostream_openssl_init(void); void ssl_iostream_openssl_deinit(void); #endif dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-ssl.h0000644000175000017500000000642413165463624017115 00000000000000#ifndef IOSTREAM_SSL_H #define IOSTREAM_SSL_H struct ssl_iostream; struct ssl_iostream_context; struct ssl_iostream_settings { const char *protocols; const char *cipher_list; const char *ca, *ca_file, *ca_dir; /* context-only */ const char *cert; const char *key; const char *key_password; const char *cert_username_field; const char *crypto_device; /* context-only */ bool verbose, verbose_invalid_cert; /* stream-only */ bool verify_remote_cert; /* neither/both */ bool require_valid_cert; /* stream-only */ bool prefer_server_ciphers; bool compression; bool tickets; }; /* Returns 0 if ok, -1 and sets error_r if failed. The returned error string becomes available via ssl_iostream_get_last_error() */ typedef int ssl_iostream_handshake_callback_t(const char **error_r, void *context); int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r); int io_stream_create_ssl_server(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r); /* returned input and output streams must also be unreferenced */ void ssl_iostream_unref(struct ssl_iostream **ssl_io); /* shutdown SSL connection and unreference ssl iostream */ void ssl_iostream_destroy(struct ssl_iostream **ssl_io); /* If verbose logging is enabled, use the specified log prefix */ void ssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io, const char *prefix); int ssl_iostream_handshake(struct ssl_iostream *ssl_io); void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context); bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io); /* Returns TRUE if the remote cert is invalid, or handshake callback returned failure. */ bool ssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io); bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io); bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io); int ssl_iostream_check_cert_validity(struct ssl_iostream *ssl_io, const char *host, const char **error_r); int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *name); const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io); const char *ssl_iostream_get_server_name(struct ssl_iostream *ssl_io); const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io); const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io); int ssl_iostream_generate_params(buffer_t *output, unsigned int dh_length, const char **error_r); int ssl_iostream_context_import_params(struct ssl_iostream_context *ctx, const buffer_t *input); int ssl_iostream_context_init_client(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); int ssl_iostream_context_init_server(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); void ssl_iostream_context_deinit(struct ssl_iostream_context **ctx); #endif dovecot-2.2.33.2/src/lib-ssl-iostream/dovecot-openssl-common.h0000644000175000017500000000115113123174404021062 00000000000000#ifndef DOVECOT_OPENSSL_COMMON_H #define DOVECOT_OPENSSL_COMMON_H /* Initialize OpenSSL if this is the first instance. Increase initialization reference count. */ void dovecot_openssl_common_global_ref(void); /* Deinitialize OpenSSL if this is the last instance. Returns TRUE if there are more instances left. */ bool dovecot_openssl_common_global_unref(void); /* Set OpenSSL engine if it's not already set. Returns 1 on success, 0 if engine is unknown, -1 on other error. error_r is set on 0/-1. */ int dovecot_openssl_common_global_set_engine(const char *engine, const char **error_r); #endif dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-openssl-params.c0000644000175000017500000000557213165463624021256 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "iostream-openssl.h" /* 2 or 5. Haven't seen their difference explained anywhere, but 2 is the default.. */ #define DH_GENERATOR 2 static int generate_dh_parameters(int bitsize, buffer_t *output, const char **error_r) { DH *dh; unsigned char *p; int len, len2; dh = DH_generate_parameters(bitsize, DH_GENERATOR, NULL, NULL); if (dh == NULL) { *error_r = t_strdup_printf( "DH_generate_parameters(bits=%d, gen=%d) failed: %s", bitsize, DH_GENERATOR, openssl_iostream_error()); return -1; } len = i2d_DHparams(dh, NULL); if (len < 0) { *error_r = t_strdup_printf("i2d_DHparams() failed: %s", openssl_iostream_error()); DH_free(dh); return -1; } buffer_append(output, &bitsize, sizeof(bitsize)); buffer_append(output, &len, sizeof(len)); p = buffer_append_space_unsafe(output, len); len2 = i2d_DHparams(dh, &p); i_assert(len == len2); DH_free(dh); return 0; } int openssl_iostream_generate_params(buffer_t *output, unsigned int dh_length, const char **error_r) { if (generate_dh_parameters(512, output, error_r) < 0) return -1; if (dh_length != 512) { if (generate_dh_parameters(dh_length, output, error_r) < 0) return -1; } buffer_append_zero(output, sizeof(int)); return 0; } static int read_int(const unsigned char **data, const unsigned char *end) { unsigned int len = end - *data; int ret; if (len < sizeof(ret)) return -1; memcpy(&ret, *data, sizeof(ret)); *data += sizeof(ret); return ret; } static int read_dh_parameters_next(struct ssl_iostream_context *ctx, const unsigned char **data, const unsigned char *end) { const unsigned char *dbuf; DH *dh; int bits, len, ret = 1; /* get bit size. 0 ends the DH parameters list. */ if ((bits = read_int(data, end)) <= 0) return bits; /* get data size */ if ((len = read_int(data, end)) <= 0 || end - *data < len) return -1; dbuf = *data; dh = d2i_DHparams(NULL, &dbuf, len); *data += len; if (dh == NULL) return -1; switch (bits) { case 512: if (ctx->dh_512 != NULL) return -1; ctx->dh_512 = dh; break; default: if (ctx->dh_default != NULL) return -1; ctx->dh_default = dh; break; } return ret; } int openssl_iostream_context_import_params(struct ssl_iostream_context *ctx, const buffer_t *input) { const unsigned char *data, *end; int ret; openssl_iostream_context_free_params(ctx); data = input->data; end = data + input->used; while ((ret = read_dh_parameters_next(ctx, &data, end)) > 0) ; return ret < 0 || data != end ? -1 : 0; } void openssl_iostream_context_free_params(struct ssl_iostream_context *ctx) { if (ctx->dh_512 != NULL) { DH_free(ctx->dh_512); ctx->dh_512 = NULL; } if (ctx->dh_default != NULL) { DH_free(ctx->dh_default); ctx->dh_default = NULL; } } dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-openssl-common.c0000644000175000017500000001675213165463624021265 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "iostream-openssl.h" #include #include #include enum { DOVECOT_SSL_PROTO_SSLv2 = 0x01, DOVECOT_SSL_PROTO_SSLv3 = 0x02, DOVECOT_SSL_PROTO_TLSv1 = 0x04, DOVECOT_SSL_PROTO_TLSv1_1 = 0x08, DOVECOT_SSL_PROTO_TLSv1_2 = 0x10, DOVECOT_SSL_PROTO_ALL = 0x1f }; int openssl_get_protocol_options(const char *protocols) { const char *const *tmp; int proto, op = 0, include = 0, exclude = 0; bool neg; tmp = t_strsplit_spaces(protocols, ", "); for (; *tmp != NULL; tmp++) { const char *name = *tmp; if (*name != '!') neg = FALSE; else { name++; neg = TRUE; } #ifdef SSL_TXT_SSLV2 if (strcasecmp(name, SSL_TXT_SSLV2) == 0) proto = DOVECOT_SSL_PROTO_SSLv2; else #endif #ifdef SSL_TXT_SSLV3 if (strcasecmp(name, SSL_TXT_SSLV3) == 0) proto = DOVECOT_SSL_PROTO_SSLv3; else #endif if (strcasecmp(name, SSL_TXT_TLSV1) == 0) proto = DOVECOT_SSL_PROTO_TLSv1; #ifdef SSL_TXT_TLSV1_1 else if (strcasecmp(name, SSL_TXT_TLSV1_1) == 0) proto = DOVECOT_SSL_PROTO_TLSv1_1; #endif #ifdef SSL_TXT_TLSV1_2 else if (strcasecmp(name, SSL_TXT_TLSV1_2) == 0) proto = DOVECOT_SSL_PROTO_TLSv1_2; #endif else { i_fatal("Invalid ssl_protocols setting: " "Unknown protocol '%s'", name); } if (neg) exclude |= proto; else include |= proto; } if (include != 0) { /* exclude everything, except those that are included (and let excludes still override those) */ exclude |= DOVECOT_SSL_PROTO_ALL & ~include; } if ((exclude & DOVECOT_SSL_PROTO_SSLv2) != 0) op |= SSL_OP_NO_SSLv2; if ((exclude & DOVECOT_SSL_PROTO_SSLv3) != 0) op |= SSL_OP_NO_SSLv3; if ((exclude & DOVECOT_SSL_PROTO_TLSv1) != 0) op |= SSL_OP_NO_TLSv1; #ifdef SSL_OP_NO_TLSv1_1 if ((exclude & DOVECOT_SSL_PROTO_TLSv1_1) != 0) op |= SSL_OP_NO_TLSv1_1; #endif #ifdef SSL_OP_NO_TLSv1_2 if ((exclude & DOVECOT_SSL_PROTO_TLSv1_2) != 0) op |= SSL_OP_NO_TLSv1_2; #endif return op; } static const char *asn1_string_to_c(ASN1_STRING *asn_str) { const char *cstr; unsigned int len; len = ASN1_STRING_length(asn_str); cstr = t_strndup(ASN1_STRING_get0_data(asn_str), len); if (strlen(cstr) != len) { /* NULs in the name - could be some MITM attack. never allow. */ return ""; } return cstr; } static const char *get_general_dns_name(const GENERAL_NAME *name) { if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING) return ""; return asn1_string_to_c(name->d.ia5); } static int get_general_ip_addr(const GENERAL_NAME *name, struct ip_addr *ip_r) { if (ASN1_STRING_type(name->d.ip) != V_ASN1_OCTET_STRING) return 0; const unsigned char *data = ASN1_STRING_get0_data(name->d.ip); if (name->d.ip->length == sizeof(ip_r->u.ip4.s_addr)) { ip_r->family = AF_INET; memcpy(&ip_r->u.ip4.s_addr, data, sizeof(ip_r->u.ip4.s_addr)); } else if (name->d.ip->length == sizeof(ip_r->u.ip6.s6_addr)) { ip_r->family = AF_INET6; memcpy(ip_r->u.ip6.s6_addr, data, sizeof(ip_r->u.ip6.s6_addr)); } else return -1; return 0; } static const char *get_cname(X509 *cert) { X509_NAME *name; X509_NAME_ENTRY *entry; ASN1_STRING *str; int cn_idx; name = X509_get_subject_name(cert); if (name == NULL) return ""; cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if (cn_idx == -1) return ""; entry = X509_NAME_get_entry(name, cn_idx); i_assert(entry != NULL); str = X509_NAME_ENTRY_get_data(entry); i_assert(str != NULL); return asn1_string_to_c(str); } static bool openssl_hostname_equals(const char *ssl_name, const char *host) { const char *p; if (strcmp(ssl_name, host) == 0) return TRUE; /* check for *.example.com wildcard */ if (ssl_name[0] != '*' || ssl_name[1] != '.') return FALSE; p = strchr(host, '.'); return p != NULL && strcmp(ssl_name+2, p+1) == 0; } int openssl_cert_match_name(SSL *ssl, const char *verify_name) { X509 *cert; STACK_OF(GENERAL_NAME) *gnames; const GENERAL_NAME *gn; struct ip_addr ip; const char *dnsname; bool dns_names = FALSE; unsigned int i, count; int ret; cert = SSL_get_peer_certificate(ssl); i_assert(cert != NULL); /* verify against SubjectAltNames */ gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames); i_zero(&ip); /* try to convert verify_name to IP */ if (inet_pton(AF_INET6, verify_name, &ip.u.ip6) == 1) ip.family = AF_INET6; else if (inet_pton(AF_INET, verify_name, &ip.u.ip4) == 1) ip.family = AF_INET; else i_zero(&ip); for (i = 0; i < count; i++) { gn = sk_GENERAL_NAME_value(gnames, i); if (gn->type == GEN_DNS) { dns_names = TRUE; dnsname = get_general_dns_name(gn); if (openssl_hostname_equals(dnsname, verify_name)) break; } else if (gn->type == GEN_IPADD) { struct ip_addr ip_2; i_zero(&ip_2); dns_names = TRUE; if (get_general_ip_addr(gn, &ip_2) == 0 && net_ip_compare(&ip, &ip_2)) break; } } sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free); /* verify against CommonName only when there wasn't any DNS SubjectAltNames */ if (dns_names) ret = i < count ? 0 : -1; else if (openssl_hostname_equals(get_cname(cert), verify_name)) ret = 0; else ret = -1; X509_free(cert); return ret; } static const char *ssl_err2str(unsigned long err, const char *data, int flags) { const char *ret; char *buf; size_t err_size = 256; buf = t_malloc(err_size); buf[err_size-1] = '\0'; ERR_error_string_n(err, buf, err_size-1); ret = buf; if ((flags & ERR_TXT_STRING) != 0) ret = t_strdup_printf("%s: %s", buf, data); return ret; } const char *openssl_iostream_error(void) { string_t *errstr = NULL; unsigned long err; const char *data, *final_error; int flags; while ((err = ERR_get_error_line_data(NULL, NULL, &data, &flags)) != 0) { if (ERR_GET_REASON(err) == ERR_R_MALLOC_FAILURE) i_fatal_status(FATAL_OUTOFMEM, "OpenSSL malloc() failed"); if (ERR_peek_error() == 0) break; if (errstr == NULL) errstr = t_str_new(128); else str_append(errstr, ", "); str_append(errstr, ssl_err2str(err, data, flags)); } if (err == 0) { if (errno != 0) final_error = strerror(errno); else final_error = "Unknown error"; } else { final_error = ssl_err2str(err, data, flags); } if (errstr == NULL) return final_error; else { str_printfa(errstr, ", %s", final_error); return str_c(errstr); } } const char *openssl_iostream_key_load_error(void) { unsigned long err = ERR_peek_error(); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_KEY_VALUES_MISMATCH) return "Key is for a different cert than ssl_cert"; else return openssl_iostream_error(); } static bool is_pem_key(const char *cert) { return strstr(cert, "PRIVATE KEY---") != NULL; } const char * openssl_iostream_use_certificate_error(const char *cert, const char *set_name) { unsigned long err; err = ERR_peek_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM || ERR_GET_REASON(err) != PEM_R_NO_START_LINE) return openssl_iostream_error(); else if (is_pem_key(cert)) { return "The file contains a private key " "(you've mixed ssl_cert and ssl_key settings)"; } else if (set_name != NULL && strchr(cert, '\n') == NULL) { return t_strdup_printf("There is no valid PEM certificate. " "(You probably forgot '<' from %s=<%s)", set_name, cert); } else { return "There is no valid PEM certificate."; } } void openssl_iostream_clear_errors(void) { while (ERR_get_error() != 0) ; } dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-openssl.c0000644000175000017500000005045013165463624017770 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "ostream-private.h" #include "iostream-openssl.h" #include static void openssl_iostream_free(struct ssl_iostream *ssl_io); static void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str) { if (ssl_io->verbose) { /* This error should normally be logged by lib-ssl-iostream's caller. But if verbose=TRUE, log it here as well to make sure that the error is always logged. */ i_debug("%sSSL error: %s", ssl_io->log_prefix, str); } i_free(ssl_io->last_error); ssl_io->last_error = i_strdup(str); } static void openssl_info_callback(const SSL *ssl, int where, int ret) { struct ssl_iostream *ssl_io; ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index); if ((where & SSL_CB_ALERT) != 0) { switch (ret & 0xff) { case SSL_AD_CLOSE_NOTIFY: i_debug("%sSSL alert: %s", ssl_io->log_prefix, SSL_alert_desc_string_long(ret)); break; default: i_debug("%sSSL alert: where=0x%x, ret=%d: %s %s", ssl_io->log_prefix, where, ret, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); break; } } else if (ret == 0) { i_debug("%sSSL failed: where=0x%x: %s", ssl_io->log_prefix, where, SSL_state_string_long(ssl)); } else { i_debug("%sSSL: where=0x%x, ret=%d: %s", ssl_io->log_prefix, where, ret, SSL_state_string_long(ssl)); } } static int openssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert, const char **error_r) { BIO *in; X509 *x; int ret = 0; in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert)); if (in == NULL) { *error_r = t_strdup_printf("BIO_new_mem_buf() failed: %s", openssl_iostream_error()); return -1; } x = PEM_read_bio_X509(in, NULL, NULL, NULL); if (x != NULL) { ret = SSL_use_certificate(ssl_io->ssl, x); if (ERR_peek_error() != 0) ret = 0; X509_free(x); } BIO_free(in); if (ret == 0) { *error_r = t_strdup_printf("Can't load ssl_cert: %s", openssl_iostream_use_certificate_error(cert, NULL)); return -1; } return 0; } static int openssl_iostream_use_key(struct ssl_iostream *ssl_io, const struct ssl_iostream_settings *set, const char **error_r) { EVP_PKEY *pkey; int ret = 0; if (openssl_iostream_load_key(set, &pkey, error_r) < 0) return -1; if (SSL_use_PrivateKey(ssl_io->ssl, pkey) != 1) { *error_r = t_strdup_printf("Can't load SSL private key: %s", openssl_iostream_key_load_error()); ret = -1; } EVP_PKEY_free(pkey); return ret; } static int openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx) { int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx(); SSL *ssl; struct ssl_iostream *ssl_io; char certname[1024]; X509_NAME *subject; ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_extidx); ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index); ssl_io->cert_received = TRUE; subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx)); if (subject == NULL || X509_NAME_oneline(subject, certname, sizeof(certname)) == NULL) certname[0] = '\0'; else certname[sizeof(certname)-1] = '\0'; /* just in case.. */ if (!preverify_ok) { openssl_iostream_set_error(ssl_io, t_strdup_printf( "Received invalid SSL certificate: %s: %s", X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), certname)); if (ssl_io->verbose_invalid_cert) i_info("%s", ssl_io->last_error); } else if (ssl_io->verbose) { i_info("Received valid SSL certificate: %s", certname); } if (!preverify_ok) { ssl_io->cert_broken = TRUE; if (ssl_io->require_valid_cert) { ssl_io->handshake_failed = TRUE; return 0; } } return 1; } static int openssl_iostream_set(struct ssl_iostream *ssl_io, const struct ssl_iostream_settings *set, const char **error_r) { const struct ssl_iostream_settings *ctx_set = ssl_io->ctx->set; int verify_flags; if (set->verbose) SSL_set_info_callback(ssl_io->ssl, openssl_info_callback); if (set->cipher_list != NULL && strcmp(ctx_set->cipher_list, set->cipher_list) != 0) { if (!SSL_set_cipher_list(ssl_io->ssl, set->cipher_list)) { *error_r = t_strdup_printf( "Can't set cipher list to '%s': %s", set->cipher_list, openssl_iostream_error()); return -1; } } if (set->prefer_server_ciphers) SSL_set_options(ssl_io->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); if (set->protocols != NULL) { #if defined(HAVE_SSL_CLEAR_OPTIONS) SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS); #endif SSL_set_options(ssl_io->ssl, openssl_get_protocol_options(set->protocols)); } if (set->cert != NULL && strcmp(ctx_set->cert, set->cert) != 0) { if (openssl_iostream_use_certificate(ssl_io, set->cert, error_r) < 0) return -1; } if (set->key != NULL && strcmp(ctx_set->key, set->key) != 0) { if (openssl_iostream_use_key(ssl_io, set, error_r) < 0) return -1; } if (set->verify_remote_cert) { if (ssl_io->ctx->client_ctx) verify_flags = SSL_VERIFY_NONE; else verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; SSL_set_verify(ssl_io->ssl, verify_flags, openssl_iostream_verify_client_cert); } if (set->cert_username_field != NULL) { ssl_io->username_nid = OBJ_txt2nid(set->cert_username_field); if (ssl_io->username_nid == NID_undef) { *error_r = t_strdup_printf( "Invalid cert_username_field: %s", set->cert_username_field); return -1; } } else { ssl_io->username_nid = ssl_io->ctx->username_nid; } ssl_io->verbose = set->verbose; ssl_io->verbose_invalid_cert = set->verbose_invalid_cert || set->verbose; ssl_io->require_valid_cert = set->require_valid_cert; return 0; } static int openssl_iostream_create(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r) { struct ssl_iostream *ssl_io; SSL *ssl; BIO *bio_int, *bio_ext; ssl = SSL_new(ctx->ssl_ctx); if (ssl == NULL) { *error_r = t_strdup_printf("SSL_new() failed: %s", openssl_iostream_error()); return -1; } /* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e). Each of the BIOs have one "write buffer". BIO_write() copies data to them, while BIO_read() reads from the other BIO's write buffer into the given buffer. The bio_int is used by OpenSSL and bio_ext is used by this library. */ if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) { *error_r = t_strdup_printf("BIO_new_bio_pair() failed: %s", openssl_iostream_error()); SSL_free(ssl); return -1; } ssl_io = i_new(struct ssl_iostream, 1); ssl_io->refcount = 1; ssl_io->ctx = ctx; ssl_io->ssl = ssl; ssl_io->bio_ext = bio_ext; ssl_io->plain_input = *input; ssl_io->plain_output = *output; ssl_io->host = i_strdup(host); ssl_io->log_prefix = host == NULL ? i_strdup("") : i_strdup_printf("%s: ", host); /* bio_int will be freed by SSL_free() */ SSL_set_bio(ssl_io->ssl, bio_int, bio_int); SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io); #ifdef HAVE_SSL_GET_SERVERNAME SSL_set_tlsext_host_name(ssl_io->ssl, host); #endif if (openssl_iostream_set(ssl_io, set, error_r) < 0) { openssl_iostream_free(ssl_io); return -1; } o_stream_uncork(ssl_io->plain_output); *input = openssl_i_stream_create_ssl(ssl_io); *output = openssl_o_stream_create_ssl(ssl_io); i_stream_set_name(*input, t_strconcat("SSL ", i_stream_get_name(ssl_io->plain_input), NULL)); o_stream_set_name(*output, t_strconcat("SSL ", o_stream_get_name(ssl_io->plain_output), NULL)); if (ssl_io->plain_output->real_stream->error_handling_disabled) o_stream_set_no_error_handling(*output, TRUE); ssl_io->ssl_input = *input; ssl_io->ssl_output = *output; *iostream_r = ssl_io; return 0; } static void openssl_iostream_free(struct ssl_iostream *ssl_io) { i_stream_unref(&ssl_io->plain_input); o_stream_unref(&ssl_io->plain_output); BIO_free(ssl_io->bio_ext); SSL_free(ssl_io->ssl); i_free(ssl_io->plain_stream_errstr); i_free(ssl_io->last_error); i_free(ssl_io->host); i_free(ssl_io->log_prefix); i_free(ssl_io); } static void openssl_iostream_unref(struct ssl_iostream *ssl_io) { i_assert(ssl_io->refcount > 0); if (--ssl_io->refcount > 0) return; openssl_iostream_free(ssl_io); } static void openssl_iostream_destroy(struct ssl_iostream *ssl_io) { if (SSL_shutdown(ssl_io->ssl) != 1) { /* if bidirectional shutdown fails we need to clear the error queue */ openssl_iostream_clear_errors(); } (void)openssl_iostream_more(ssl_io); (void)o_stream_flush(ssl_io->plain_output); /* close the plain i/o streams, because their fd may be closed soon, but we may still keep this ssl-iostream referenced until later. */ i_stream_close(ssl_io->plain_input); o_stream_close(ssl_io->plain_output); ssl_iostream_unref(&ssl_io); } static bool openssl_iostream_bio_output(struct ssl_iostream *ssl_io) { size_t bytes, max_bytes; ssize_t sent; unsigned char buffer[IO_BLOCK_SIZE]; bool bytes_sent = FALSE; int ret; o_stream_cork(ssl_io->plain_output); while ((bytes = BIO_ctrl_pending(ssl_io->bio_ext)) > 0) { /* bytes contains how many SSL encrypted bytes we should be sending out */ max_bytes = o_stream_get_buffer_avail_size(ssl_io->plain_output); if (bytes > max_bytes) { if (max_bytes == 0) { /* wait until output buffer clears */ o_stream_set_flush_pending(ssl_io->plain_output, TRUE); break; } bytes = max_bytes; } if (bytes > sizeof(buffer)) bytes = sizeof(buffer); /* BIO_read() is guaranteed to return all the bytes that BIO_ctrl_pending() returned */ ret = BIO_read(ssl_io->bio_ext, buffer, bytes); i_assert(ret == (int)bytes); /* we limited number of read bytes to plain_output's available size. this send() is guaranteed to either fully succeed or completely fail due to some error. */ sent = o_stream_send(ssl_io->plain_output, buffer, bytes); if (sent < 0) { i_assert(ssl_io->plain_output->closed || ssl_io->plain_output->stream_errno != 0); i_free(ssl_io->plain_stream_errstr); ssl_io->plain_stream_errstr = i_strdup(o_stream_get_error(ssl_io->plain_output)); ssl_io->plain_stream_errno = ssl_io->plain_output->stream_errno; ssl_io->closed = TRUE; break; } i_assert(sent == (ssize_t)bytes); bytes_sent = TRUE; } o_stream_uncork(ssl_io->plain_output); return bytes_sent; } static ssize_t openssl_iostream_read_more(struct ssl_iostream *ssl_io, const unsigned char **data_r, size_t *size_r) { *data_r = i_stream_get_data(ssl_io->plain_input, size_r); if (*size_r > 0) return 0; if (!ssl_io->input_handler) { /* read plain_input only when we came here from input handler. this makes sure that we don't get stuck with some input unexpectedly buffered. */ return 0; } if (i_stream_read_data(ssl_io->plain_input, data_r, size_r, 0) < 0) return -1; return 0; } static bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io) { const unsigned char *data; size_t bytes, size; int ret; bool bytes_read = FALSE; while ((bytes = BIO_ctrl_get_write_guarantee(ssl_io->bio_ext)) > 0) { /* bytes contains how many bytes we can write to bio_ext */ ssl_io->plain_input->real_stream->try_alloc_limit = bytes; ret = openssl_iostream_read_more(ssl_io, &data, &size); ssl_io->plain_input->real_stream->try_alloc_limit = 0; if (ret == -1 && size == 0 && !bytes_read) { if (ssl_io->plain_input->stream_errno != 0) { i_free(ssl_io->plain_stream_errstr); ssl_io->plain_stream_errstr = i_strdup(i_stream_get_error(ssl_io->plain_input)); ssl_io->plain_stream_errno = ssl_io->plain_input->stream_errno; } ssl_io->closed = TRUE; return FALSE; } if (size == 0) { /* wait for more input */ break; } if (size > bytes) size = bytes; ret = BIO_write(ssl_io->bio_ext, data, size); i_assert(ret == (ssize_t)size); i_stream_skip(ssl_io->plain_input, size); bytes_read = TRUE; } if (bytes == 0 && !bytes_read && ssl_io->want_read) { /* shouldn't happen */ i_error("SSL BIO buffer size too small"); i_free(ssl_io->plain_stream_errstr); ssl_io->plain_stream_errstr = i_strdup("SSL BIO buffer size too small"); ssl_io->plain_stream_errno = EINVAL; ssl_io->closed = TRUE; return FALSE; } if (i_stream_get_data_size(ssl_io->plain_input) > 0) { i_error("SSL: Too much data in buffered plain input buffer"); i_free(ssl_io->plain_stream_errstr); ssl_io->plain_stream_errstr = i_strdup("SSL: Too much data in buffered plain input buffer"); ssl_io->plain_stream_errno = EINVAL; ssl_io->closed = TRUE; return FALSE; } if (bytes_read) { if (ssl_io->ostream_flush_waiting_input) { ssl_io->ostream_flush_waiting_input = FALSE; o_stream_set_flush_pending(ssl_io->plain_output, TRUE); } i_stream_set_input_pending(ssl_io->ssl_input, TRUE); ssl_io->want_read = FALSE; } return bytes_read; } bool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io) { bool ret; ret = openssl_iostream_bio_output(ssl_io); if (openssl_iostream_bio_input(ssl_io)) ret = TRUE; return ret; } int openssl_iostream_more(struct ssl_iostream *ssl_io) { int ret; if (!ssl_io->handshaked) { if ((ret = ssl_iostream_handshake(ssl_io)) <= 0) return ret; } (void)openssl_iostream_bio_sync(ssl_io); return 1; } static int openssl_iostream_handle_error_full(struct ssl_iostream *ssl_io, int ret, const char *func_name, bool write_error) { const char *errstr = NULL; int err; err = SSL_get_error(ssl_io->ssl, ret); switch (err) { case SSL_ERROR_WANT_WRITE: if (!openssl_iostream_bio_sync(ssl_io)) { if (!write_error) i_panic("SSL ostream buffer size not unlimited"); return 0; } if (ssl_io->closed) { if (ssl_io->plain_stream_errstr != NULL) openssl_iostream_set_error(ssl_io, ssl_io->plain_stream_errstr); errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return 1; case SSL_ERROR_WANT_READ: ssl_io->want_read = TRUE; (void)openssl_iostream_bio_sync(ssl_io); if (ssl_io->closed) { if (ssl_io->plain_stream_errstr != NULL) openssl_iostream_set_error(ssl_io, ssl_io->plain_stream_errstr); errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return ssl_io->want_read ? 0 : 1; case SSL_ERROR_SYSCALL: /* eat up the error queue */ if (ERR_peek_error() != 0) { errstr = openssl_iostream_error(); errno = EINVAL; } else if (ret != 0) { i_assert(errno != 0); errstr = strerror(errno); } else { /* EOF. */ errno = EPIPE; errstr = "Disconnected"; break; } errstr = t_strdup_printf("%s syscall failed: %s", func_name, errstr); break; case SSL_ERROR_ZERO_RETURN: /* clean connection closing */ errno = EPIPE; i_free_and_null(ssl_io->last_error); return -1; case SSL_ERROR_SSL: errstr = t_strdup_printf("%s failed: %s", func_name, openssl_iostream_error()); errno = EINVAL; break; default: errstr = t_strdup_printf("%s failed: unknown failure %d (%s)", func_name, err, openssl_iostream_error()); errno = EINVAL; break; } openssl_iostream_set_error(ssl_io, errstr); return -1; } int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret, const char *func_name) { return openssl_iostream_handle_error_full(ssl_io, ret, func_name, FALSE); } int openssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret, const char *func_name) { return openssl_iostream_handle_error_full(ssl_io, ret, func_name, TRUE); } static int openssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *verify_name) { if (!ssl_iostream_has_valid_client_cert(ssl_io)) return -1; return openssl_cert_match_name(ssl_io->ssl, verify_name); } static int openssl_iostream_handshake(struct ssl_iostream *ssl_io) { const char *error = NULL; int ret; i_assert(!ssl_io->handshaked); if (ssl_io->ctx->client_ctx) { while ((ret = SSL_connect(ssl_io->ssl)) <= 0) { ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_connect()"); if (ret <= 0) return ret; } } else { while ((ret = SSL_accept(ssl_io->ssl)) <= 0) { ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_accept()"); if (ret <= 0) return ret; } } /* handshake finished */ (void)openssl_iostream_bio_sync(ssl_io); if (ssl_io->handshake_callback != NULL) { if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) < 0) { i_assert(error != NULL); i_stream_close(ssl_io->plain_input); o_stream_close(ssl_io->plain_output); openssl_iostream_set_error(ssl_io, error); ssl_io->handshake_failed = TRUE; errno = EINVAL; return -1; } } i_free_and_null(ssl_io->last_error); ssl_io->handshaked = TRUE; if (ssl_io->ssl_output != NULL) (void)o_stream_flush(ssl_io->ssl_output); return 1; } static void openssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context) { ssl_io->handshake_callback = callback; ssl_io->handshake_context = context; } static void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io, const char *prefix) { i_free(ssl_io->log_prefix); ssl_io->log_prefix = i_strdup(prefix); } static bool openssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io) { return ssl_io->handshaked; } static bool openssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io) { return ssl_io->handshake_failed; } static bool openssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io) { return ssl_io->cert_received && !ssl_io->cert_broken; } static bool openssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io) { return ssl_io->cert_received && ssl_io->cert_broken; } static const char * openssl_iostream_get_peer_name(struct ssl_iostream *ssl_io) { X509 *x509; char *name; int len; if (!ssl_iostream_has_valid_client_cert(ssl_io)) return NULL; x509 = SSL_get_peer_certificate(ssl_io->ssl); i_assert(x509 != NULL); len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509), ssl_io->username_nid, NULL, 0); if (len < 0) name = ""; else { name = t_malloc(len + 1); if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509), ssl_io->username_nid, name, len + 1) < 0) name = ""; else if (strlen(name) != (size_t)len) { /* NUL characters in name. Someone's trying to fake being another user? Don't allow it. */ name = ""; } } X509_free(x509); return *name == '\0' ? NULL : name; } static const char *openssl_iostream_get_server_name(struct ssl_iostream *ssl_io) { return ssl_io->host; } static const char * openssl_iostream_get_security_string(struct ssl_iostream *ssl_io) { const SSL_CIPHER *cipher; #if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP) const COMP_METHOD *comp; #endif const char *comp_str; int bits, alg_bits; if (!ssl_io->handshaked) return ""; cipher = SSL_get_current_cipher(ssl_io->ssl); bits = SSL_CIPHER_get_bits(cipher, &alg_bits); #if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP) comp = SSL_get_current_compression(ssl_io->ssl); comp_str = comp == NULL ? "" : t_strconcat(" ", SSL_COMP_get_name(comp), NULL); #else comp_str = ""; #endif return t_strdup_printf("%s with cipher %s (%d/%d bits)%s", SSL_get_version(ssl_io->ssl), SSL_CIPHER_get_name(cipher), bits, alg_bits, comp_str); } static const char * openssl_iostream_get_last_error(struct ssl_iostream *ssl_io) { return ssl_io->last_error; } static const struct iostream_ssl_vfuncs ssl_vfuncs = { openssl_iostream_context_init_client, openssl_iostream_context_init_server, openssl_iostream_context_deinit, openssl_iostream_generate_params, openssl_iostream_context_import_params, openssl_iostream_create, openssl_iostream_unref, openssl_iostream_destroy, openssl_iostream_handshake, openssl_iostream_set_handshake_callback, openssl_iostream_set_log_prefix, openssl_iostream_is_handshaked, openssl_iostream_has_handshake_failed, openssl_iostream_has_valid_client_cert, openssl_iostream_has_broken_client_cert, openssl_iostream_cert_match_name, openssl_iostream_get_peer_name, openssl_iostream_get_server_name, openssl_iostream_get_security_string, openssl_iostream_get_last_error }; void ssl_iostream_openssl_init(void) { iostream_ssl_module_init(&ssl_vfuncs); } void ssl_iostream_openssl_deinit(void) { openssl_iostream_global_deinit(); } dovecot-2.2.33.2/src/lib-ssl-iostream/iostream-ssl-private.h0000644000175000017500000000360013165463624020556 00000000000000#ifndef IOSTREAM_SSL_PRIVATE_H #define IOSTREAM_SSL_PRIVATE_H #include "iostream-ssl.h" struct iostream_ssl_vfuncs { int (*context_init_client)(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); int (*context_init_server)(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r); void (*context_deinit)(struct ssl_iostream_context *ctx); int (*generate_params)(buffer_t *output, unsigned int dh_length, const char **error_r); int (*context_import_params)(struct ssl_iostream_context *ctx, const buffer_t *input); int (*create)(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r); void (*unref)(struct ssl_iostream *ssl_io); void (*destroy)(struct ssl_iostream *ssl_io); int (*handshake)(struct ssl_iostream *ssl_io); void (*set_handshake_callback)(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context); void (*set_log_prefix)(struct ssl_iostream *ssl_io, const char *prefix); bool (*is_handshaked)(const struct ssl_iostream *ssl_io); bool (*has_handshake_failed)(const struct ssl_iostream *ssl_io); bool (*has_valid_client_cert)(const struct ssl_iostream *ssl_io); bool (*has_broken_client_cert)(struct ssl_iostream *ssl_io); int (*cert_match_name)(struct ssl_iostream *ssl_io, const char *name); const char *(*get_peer_name)(struct ssl_iostream *ssl_io); const char *(*get_server_name)(struct ssl_iostream *ssl_io); const char *(*get_security_string)(struct ssl_iostream *ssl_io); const char *(*get_last_error)(struct ssl_iostream *ssl_io); }; void iostream_ssl_module_init(const struct iostream_ssl_vfuncs *vfuncs); #endif dovecot-2.2.33.2/src/lib-ssl-iostream/dovecot-openssl-common.c0000644000175000017500000000735013123174404021064 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "dovecot-openssl-common.h" #include #include #include static int openssl_init_refcount = 0; static ENGINE *dovecot_openssl_engine; #ifdef HAVE_SSL_NEW_MEM_FUNCS static void *dovecot_openssl_malloc(size_t size, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED) #else static void *dovecot_openssl_malloc(size_t size) #endif { /* this may be performance critical, so don't use i_malloc() or calloc() */ void *mem = malloc(size); if (mem == NULL) { i_fatal_status(FATAL_OUTOFMEM, "OpenSSL: malloc(%"PRIuSIZE_T"): Out of memory", size); } return mem; } #ifdef HAVE_SSL_NEW_MEM_FUNCS static void *dovecot_openssl_realloc(void *ptr, size_t size, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED) #else static void *dovecot_openssl_realloc(void *ptr, size_t size) #endif { void *mem = realloc(ptr, size); if (mem == NULL) { i_fatal_status(FATAL_OUTOFMEM, "OpenSSL: realloc(%"PRIuSIZE_T"): Out of memory", size); } return mem; } #ifdef HAVE_SSL_NEW_MEM_FUNCS static void dovecot_openssl_free(void *ptr, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED) #else static void dovecot_openssl_free(void *ptr) #endif { free(ptr); } void dovecot_openssl_common_global_ref(void) { unsigned char buf; if (openssl_init_refcount++ > 0) return; /* use our own memory allocation functions that will die instead of returning NULL. this avoids random failures on out-of-memory conditions. */ if (CRYPTO_set_mem_functions(dovecot_openssl_malloc, dovecot_openssl_realloc, dovecot_openssl_free) == 0) { /*i_warning("CRYPTO_set_mem_functions() was called too late");*/ } SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); /* PRNG initialization might want to use /dev/urandom, make sure it does it before chrooting. We might not have enough entropy at the first try, so this function may fail. It's still been initialized though. */ (void)RAND_bytes(&buf, 1); } bool dovecot_openssl_common_global_unref(void) { i_assert(openssl_init_refcount > 0); if (--openssl_init_refcount > 0) return TRUE; if (dovecot_openssl_engine != NULL) { ENGINE_finish(dovecot_openssl_engine); dovecot_openssl_engine = NULL; } /* OBJ_cleanup() is called automatically by EVP_cleanup() in newer versions. Doesn't hurt to call it anyway. */ OBJ_cleanup(); #ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS SSL_COMP_free_compression_methods(); #endif ENGINE_cleanup(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); #ifdef HAVE_OPENSSL_AUTO_THREAD_DEINIT /* no cleanup needed */ #elif defined(HAVE_OPENSSL_ERR_REMOVE_THREAD_STATE) /* This was marked as deprecated in v1.1. */ ERR_remove_thread_state(NULL); #else /* This was deprecated by ERR_remove_thread_state(NULL) in v1.0.0. */ ERR_remove_state(0); #endif ERR_free_strings(); #ifdef HAVE_OPENSSL_CLEANUP OPENSSL_cleanup(); #endif return FALSE; } int dovecot_openssl_common_global_set_engine(const char *engine, const char **error_r) { if (dovecot_openssl_engine != NULL) return 1; ENGINE_load_builtin_engines(); dovecot_openssl_engine = ENGINE_by_id(engine); if (dovecot_openssl_engine == NULL) { *error_r = t_strdup_printf("Unknown engine '%s'", engine); return 0; } if (ENGINE_init(dovecot_openssl_engine) == 0) { *error_r = t_strdup_printf("ENGINE_init(%s) failed", engine); ENGINE_free(dovecot_openssl_engine); dovecot_openssl_engine = NULL; return -1; } if (ENGINE_set_default(dovecot_openssl_engine, ENGINE_METHOD_ALL) == 0) { *error_r = t_strdup_printf("ENGINE_set_default(%s) failed", engine); ENGINE_free(dovecot_openssl_engine); dovecot_openssl_engine = NULL; return -1; } return 1; } dovecot-2.2.33.2/src/lib-ssl-iostream/Makefile.am0000644000175000017500000000144213165463624016351 00000000000000noinst_LTLIBRARIES = libssl_iostream.la NOPLUGIN_LDFLAGS = AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -DMODULE_DIR=\""$(moduledir)"\" if BUILD_OPENSSL module_LTLIBRARIES = libssl_iostream_openssl.la libssl_iostream_openssl_la_LDFLAGS = -module -avoid-version libssl_iostream_openssl_la_LIBADD = $(SSL_LIBS) libssl_iostream_openssl_la_SOURCES = \ dovecot-openssl-common.c \ iostream-openssl.c \ iostream-openssl-common.c \ iostream-openssl-context.c \ iostream-openssl-params.c \ istream-openssl.c \ ostream-openssl.c endif libssl_iostream_la_SOURCES = \ iostream-ssl.c noinst_HEADERS = \ dovecot-openssl-common.h headers = \ iostream-openssl.h \ iostream-ssl.h \ iostream-ssl-private.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-imap-storage/0002755000175000017500000000000013172375611014340 500000000000000dovecot-2.2.33.2/src/lib-imap-storage/imap-msgpart.h0000644000175000017500000000611113123174404017020 00000000000000#ifndef IMAP_MSGPART_H #define IMAP_MSGPART_H struct imap_msgpart; struct imap_msgpart_open_result { /* message contents with CRLF linefeeds */ struct istream *input; /* size of input */ uoff_t size; /* if size was looked up using cache and it ends up being wrong, this field can be used to log about cache corruption */ enum mail_fetch_field size_field; /* TRUE if BINARY decoded content contains NUL characters */ bool binary_decoded_input_has_nuls; }; struct imap_msgpart *imap_msgpart_full(void); struct imap_msgpart *imap_msgpart_header(void); struct imap_msgpart *imap_msgpart_body(void); /* Parse section into imap_msgpart. Returns 0 and msgpart_r on success, -1 if the section isn't valid. The same imap_msgpart can be used to open multiple messages. */ int imap_msgpart_parse(const char *section, struct imap_msgpart **msgpart_r); void imap_msgpart_free(struct imap_msgpart **msgpart); /* Returns TRUE if the msgpart might return at least part of the message body. Or alternatively: If FALSE is returned, the msgpart will never return anything except (part of) the message header. MIME headers are counted as part of the message body. */ bool imap_msgpart_contains_body(const struct imap_msgpart *msgpart); /* Decode MIME parts with Content-Transfer-Encoding: base64/quoted-printable to binary data (IMAP BINARY extension). If something can't be decoded, fails with storage error set to MAIL_ERROR_CONVERSION. */ void imap_msgpart_set_decode_to_binary(struct imap_msgpart *msgpart); /* Set the fetch to be partial. For unlimited size use (uoff_t)-1. */ void imap_msgpart_set_partial(struct imap_msgpart *msgpart, uoff_t offset, uoff_t size); uoff_t imap_msgpart_get_partial_offset(struct imap_msgpart *msgpart); uoff_t imap_msgpart_get_partial_size(struct imap_msgpart *msgpart); /* Return wanted_fields mask. */ enum mail_fetch_field imap_msgpart_get_fetch_data(struct imap_msgpart *msgpart); /* Append all the specifically requested headers to the headers array (no deduplication is done) */ void imap_msgpart_get_wanted_headers(struct imap_msgpart *msgpart, ARRAY_TYPE(const_string) *headers); /* Open message part refenced by IMAP section as istream. Returns 0 if successful, -1 if storage error. Returned istream is initially referenced, so i_stream_unref() must be called for it. */ int imap_msgpart_open(struct mail *mail, struct imap_msgpart *msgpart, struct imap_msgpart_open_result *result_r); /* Return msgpart's size without actually opening the stream (if possible). */ int imap_msgpart_size(struct mail *mail, struct imap_msgpart *msgpart, uoff_t *size_r); /* Return msgpart's IMAP BODYPARTSTRUCTURE */ int imap_msgpart_bodypartstructure(struct mail *mail, struct imap_msgpart *msgpart, const char **bpstruct_r); /* Header context is automatically created by imap_msgpart_open() and destroyed by imap_msgpart_free(), but if you want to use the same imap_msgpart across multiple mailboxes, you need to close the part before closing the mailbox. */ void imap_msgpart_close_mailbox(struct imap_msgpart *msgpart); #endif dovecot-2.2.33.2/src/lib-imap-storage/Makefile.in0000644000175000017500000005471513172375573016346 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-imap-storage ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libimap_storage_la_LIBADD = am_libimap_storage_la_OBJECTS = imap-msgpart.lo imap-msgpart-url.lo \ imap-metadata.lo libimap_storage_la_OBJECTS = $(am_libimap_storage_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libimap_storage_la_SOURCES) DIST_SOURCES = $(libimap_storage_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libimap-storage.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap libimap_storage_la_SOURCES = \ imap-msgpart.c \ imap-msgpart-url.c \ imap-metadata.c headers = \ imap-msgpart.h \ imap-msgpart-url.h \ imap-metadata.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-imap-storage/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-imap-storage/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libimap-storage.la: $(libimap_storage_la_OBJECTS) $(libimap_storage_la_DEPENDENCIES) $(EXTRA_libimap_storage_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libimap_storage_la_OBJECTS) $(libimap_storage_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-metadata.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-msgpart-url.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-msgpart.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-imap-storage/imap-metadata.c0000644000175000017500000001756013165463624017143 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "mail-storage.h" #include "imap-metadata.h" struct imap_metadata_transaction { struct mailbox *box; struct mailbox_transaction_context *trans; enum mail_error error; char *error_string; unsigned int server:1; }; bool imap_metadata_verify_entry_name(const char *name, const char **error_r) { unsigned int i; bool ok; if (name[0] != '/') { *error_r = "Entry name must begin with '/'"; return FALSE; } for (i = 0; name[i] != '\0'; i++) { switch (name[i]) { case '/': if (i > 0 && name[i-1] == '/') { *error_r = "Entry name can't contain consecutive '/'"; return FALSE; } if (name[i+1] == '\0') { *error_r = "Entry name can't end with '/'"; return FALSE; } break; case '*': *error_r = "Entry name can't contain '*'"; return FALSE; case '%': *error_r = "Entry name can't contain '%'"; return FALSE; default: if (name[i] <= 0x19) { *error_r = "Entry name can't contain control chars"; return FALSE; } break; } } T_BEGIN { const char *prefix, *p = strchr(name+1, '/'); prefix = p == NULL ? name : t_strdup_until(name, p); ok = strcasecmp(prefix, IMAP_METADATA_PRIVATE_PREFIX) == 0 || strcasecmp(prefix, IMAP_METADATA_SHARED_PREFIX) == 0; } T_END; if (!ok) { *error_r = "Entry name must begin with /private or /shared"; return FALSE; } return TRUE; } static void imap_metadata_transaction_set_error(struct imap_metadata_transaction *imtrans, enum mail_error error, const char *string) { i_free(imtrans->error_string); imtrans->error_string = i_strdup(string); imtrans->error = error; } static bool imap_metadata_entry2key(struct imap_metadata_transaction *imtrans, const char *entry, enum mail_attribute_type *type_r, const char **key_r) { const char *key_prefix = (imtrans->server ? MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER : NULL); /* names are case-insensitive so we'll always lowercase them */ entry = t_str_lcase(entry); if (strncmp(entry, IMAP_METADATA_PRIVATE_PREFIX, strlen(IMAP_METADATA_PRIVATE_PREFIX)) == 0) { *key_r = entry + strlen(IMAP_METADATA_PRIVATE_PREFIX); *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE; } else { i_assert(strncmp(entry, IMAP_METADATA_SHARED_PREFIX, strlen(IMAP_METADATA_SHARED_PREFIX)) == 0); *key_r = entry + strlen(IMAP_METADATA_SHARED_PREFIX); *type_r = MAIL_ATTRIBUTE_TYPE_SHARED; } if ((*key_r)[0] == '\0') { /* /private or /shared prefix has no value itself */ } else { i_assert((*key_r)[0] == '/'); *key_r += 1; } if (strncmp(*key_r, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT, strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) == 0) { /* Dovecot's internal attribute (mailbox or server). don't allow accessing this. */ return FALSE; } /* Add the server-prefix (after checking for the above internal attribute). */ if (key_prefix != NULL) *key_r = t_strconcat(key_prefix, *key_r, NULL); return TRUE; } static int imap_metadata_get_mailbox_transaction(struct imap_metadata_transaction *imtrans) { if (imtrans->trans != NULL) return 0; if (imtrans->box == NULL || mailbox_open(imtrans->box) < 0) return -1; imtrans->trans = mailbox_transaction_begin(imtrans->box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); return 0; } int imap_metadata_set(struct imap_metadata_transaction *imtrans, const char *entry, const struct mail_attribute_value *value) { enum mail_attribute_type type; const char *key; if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) { imap_metadata_transaction_set_error(imtrans, MAIL_ERROR_PARAMS, "Internal mailbox attributes cannot be accessed"); return -1; } if (imap_metadata_get_mailbox_transaction(imtrans) < 0) return -1; return (value->value == NULL ? mailbox_attribute_unset(imtrans->trans, type, key) : mailbox_attribute_set(imtrans->trans, type, key, value)); } int imap_metadata_unset(struct imap_metadata_transaction *imtrans, const char *entry) { struct mail_attribute_value value; i_zero(&value); return imap_metadata_set(imtrans, entry, &value); } int imap_metadata_get(struct imap_metadata_transaction *imtrans, const char *entry, struct mail_attribute_value *value_r) { enum mail_attribute_type type; const char *key; i_zero(value_r); if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) return 0; if (imap_metadata_get_mailbox_transaction(imtrans) < 0) return -1; return mailbox_attribute_get(imtrans->trans, type, key, value_r); } int imap_metadata_get_stream(struct imap_metadata_transaction *imtrans, const char *entry, struct mail_attribute_value *value_r) { enum mail_attribute_type type; const char *key; i_zero(value_r); if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) return 0; if (imap_metadata_get_mailbox_transaction(imtrans) < 0) return -1; return mailbox_attribute_get_stream(imtrans->trans, type, key, value_r); } struct imap_metadata_iter { struct mailbox_attribute_iter *iter; }; struct imap_metadata_iter * imap_metadata_iter_init(struct imap_metadata_transaction *imtrans, const char *entry) { struct imap_metadata_iter *iter; enum mail_attribute_type type; const char *key; iter = i_new(struct imap_metadata_iter, 1); if (imap_metadata_entry2key(imtrans, entry, &type, &key)) { const char *prefix = key[0] == '\0' ? "" : t_strconcat(key, "/", NULL); iter->iter = mailbox_attribute_iter_init(imtrans->box, type, prefix); } return iter; } const char *imap_metadata_iter_next(struct imap_metadata_iter *iter) { if (iter->iter == NULL) return NULL; return mailbox_attribute_iter_next(iter->iter); } int imap_metadata_iter_deinit(struct imap_metadata_iter **_iter) { struct imap_metadata_iter *iter = *_iter; int ret; *_iter = NULL; if (iter->iter == NULL) ret = 0; else ret = mailbox_attribute_iter_deinit(&iter->iter); i_free(iter); return ret; } struct imap_metadata_transaction * imap_metadata_transaction_begin(struct mailbox *box) { struct imap_metadata_transaction *imtrans; imtrans = i_new(struct imap_metadata_transaction, 1); imtrans->box = box; return imtrans; } struct imap_metadata_transaction * imap_metadata_transaction_begin_server(struct mail_user *user) { struct mail_namespace *ns; struct mailbox *box; struct imap_metadata_transaction *imtrans; ns = mail_namespace_find_inbox(user->namespaces); box = mailbox_alloc(ns->list, "INBOX", 0); mailbox_set_reason(box, "Server METADATA"); imtrans = imap_metadata_transaction_begin(box); imtrans->server = TRUE; return imtrans; } static void imap_metadata_transaction_finish(struct imap_metadata_transaction **_imtrans) { struct imap_metadata_transaction *imtrans = *_imtrans; if (imtrans->server) mailbox_free(&imtrans->box); i_free(imtrans->error_string); i_free(imtrans); *_imtrans = NULL; } int imap_metadata_transaction_commit( struct imap_metadata_transaction **_imtrans, enum mail_error *error_code_r, const char **error_r) { struct imap_metadata_transaction *imtrans = *_imtrans; int ret = 0; if (imtrans->trans != NULL) { const char *error = NULL; ret = mailbox_transaction_commit(&imtrans->trans); if (ret < 0) error = mailbox_get_last_error(imtrans->box, error_code_r); if (error_r != NULL) *error_r = error; } imap_metadata_transaction_finish(_imtrans); return ret; } void imap_metadata_transaction_rollback( struct imap_metadata_transaction **_imtrans) { struct imap_metadata_transaction *imtrans = *_imtrans; if (imtrans->trans != NULL) mailbox_transaction_rollback(&imtrans->trans); imap_metadata_transaction_finish(_imtrans); } const char * imap_metadata_transaction_get_last_error( struct imap_metadata_transaction *imtrans, enum mail_error *error_code_r) { if (imtrans->error != MAIL_ERROR_NONE) { if (error_code_r != NULL) *error_code_r = imtrans->error; return imtrans->error_string; } i_assert(imtrans->box != NULL); return mailbox_get_last_error(imtrans->box, error_code_r); } dovecot-2.2.33.2/src/lib-imap-storage/imap-metadata.h0000644000175000017500000000437213123174404017132 00000000000000#ifndef IMAP_METADATA_H #define IMAP_METADATA_H #define IMAP_METADATA_PRIVATE_PREFIX "/private" #define IMAP_METADATA_SHARED_PREFIX "/shared" struct imap_metadata_iter; struct imap_metadata_transaction; /* Checks whether IMAP metadata entry name is valid */ bool imap_metadata_verify_entry_name( const char *name, const char **error_r); /* Set IMAP metadata entry to value. */ int imap_metadata_set(struct imap_metadata_transaction *imtrans, const char *entry, const struct mail_attribute_value *value); /* Delete IMAP metadata entry. This is just a wrapper to imap_metadata_set() with value->value=NULL. */ int imap_metadata_unset(struct imap_metadata_transaction *imtrans, const char *entry); /* Returns value for IMAP metadata entry. Returns 1 if value was returned, 0 if value wasn't found (set to NULL), -1 if error */ int imap_metadata_get(struct imap_metadata_transaction *imtrans, const char *entry, struct mail_attribute_value *value_r); /* Same as imap_metadata_get(), but the returned value may be either an input stream or a string. */ int imap_metadata_get_stream(struct imap_metadata_transaction *imtrans, const char *entry, struct mail_attribute_value *value_r); /* Iterate through IMAP metadata entries names under the specified entry. */ struct imap_metadata_iter * imap_metadata_iter_init(struct imap_metadata_transaction *imtrans, const char *entry); /* Returns the next IMAP metadata entry name or NULL if there are no more entries. */ const char *imap_metadata_iter_next(struct imap_metadata_iter *iter); int imap_metadata_iter_deinit(struct imap_metadata_iter **_iter); struct imap_metadata_transaction * imap_metadata_transaction_begin(struct mailbox *box); struct imap_metadata_transaction * imap_metadata_transaction_begin_mailbox(struct mail_user *user, const char *mailbox); struct imap_metadata_transaction * imap_metadata_transaction_begin_server(struct mail_user *user); int imap_metadata_transaction_commit( struct imap_metadata_transaction **_imtrans, enum mail_error *error_code_r, const char **error_r); void imap_metadata_transaction_rollback( struct imap_metadata_transaction **_imtrans); const char * imap_metadata_transaction_get_last_error( struct imap_metadata_transaction *imtrans, enum mail_error *error_code_r); #endif dovecot-2.2.33.2/src/lib-imap-storage/imap-msgpart.c0000644000175000017500000005477613165463624017052 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "array.h" #include "istream.h" #include "istream-crlf.h" #include "istream-nonuls.h" #include "istream-base64.h" #include "istream-header-filter.h" #include "istream-qp.h" #include "ostream.h" #include "message-parser.h" #include "message-decoder.h" #include "mail-storage-private.h" #include "mail-namespace.h" #include "imap-bodystructure.h" #include "imap-parser.h" #include "imap-msgpart.h" enum fetch_type { FETCH_FULL, FETCH_MIME, FETCH_MIME_BODY, FETCH_HEADER, FETCH_HEADER_FIELDS, FETCH_HEADER_FIELDS_NOT, FETCH_BODY }; struct imap_msgpart { pool_t pool; /* "" for root, otherwise e.g. "1.2.3". the .MIME, .HEADER, etc. suffix not included */ const char *section_number; enum fetch_type fetch_type; enum mail_fetch_field wanted_fields; /* HEADER.FIELDS[.NOT] (list of headers) */ struct mailbox_header_lookup_ctx *header_ctx; const char *const *headers; /* which part of the message part to fetch (default: 0..(uoff_t)-1) */ uoff_t partial_offset, partial_size; unsigned int decode_cte_to_binary:1; }; struct imap_msgpart_open_ctx { /* from matching message_part, set after opening: */ uoff_t physical_pos; struct message_size mime_hdr_size; struct message_size mime_body_size; }; static struct imap_msgpart *imap_msgpart_type(enum fetch_type fetch_type) { struct imap_msgpart *msgpart; pool_t pool; pool = pool_alloconly_create("imap msgpart", sizeof(*msgpart)+32); msgpart = p_new(pool, struct imap_msgpart, 1); msgpart->pool = pool; msgpart->partial_size = (uoff_t)-1; msgpart->fetch_type = fetch_type; msgpart->section_number = ""; if (fetch_type == FETCH_HEADER || fetch_type == FETCH_FULL) msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER; if (fetch_type == FETCH_BODY || fetch_type == FETCH_FULL) msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY; return msgpart; } struct imap_msgpart *imap_msgpart_full(void) { return imap_msgpart_type(FETCH_FULL); } struct imap_msgpart *imap_msgpart_header(void) { return imap_msgpart_type(FETCH_HEADER); } struct imap_msgpart *imap_msgpart_body(void) { return imap_msgpart_type(FETCH_BODY); } static struct message_part * imap_msgpart_find(struct message_part *parts, const char *section) { struct message_part *part = parts; const char *path; unsigned int num; path = section; while (*path >= '0' && *path <= '9' && part != NULL) { /* get part number, we have already verified its validity */ num = 0; while (*path != '\0' && *path != '.') { i_assert(*path >= '0' && *path <= '9'); num = num*10 + (*path - '0'); path++; } if (*path == '.') path++; if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) { /* find the part */ part = part->children; for (; num > 1 && part != NULL; num--) part = part->next; } else { /* only 1 allowed with non-multipart messages. if the child isn't message/rfc822, the path must be finished after this. */ if (num != 1) part = NULL; else if (*path != '\0' && (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) part = NULL; } if (part != NULL && (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 && (*path >= '0' && *path <= '9')) { /* if we continue inside the message/rfc822, skip this body part */ part = part->children; } } i_assert(part == NULL || *path == '\0'); return part; } static int imap_msgpart_get_header_fields(pool_t pool, const char *header_list, ARRAY_TYPE(const_string) *fields) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args, *hdr_list; unsigned int list_count; unsigned int i; int result = 0; input = i_stream_create_from_data(header_list, strlen(header_list)); parser = imap_parser_create(input, NULL, (size_t)-1); if (imap_parser_finish_line(parser, 0, 0, &args) > 0 && imap_arg_get_list_full(args, &hdr_list, &list_count) && args[1].type == IMAP_ARG_EOL && list_count > 0) { const char *value; p_array_init(fields, pool, list_count); for (i = 0; i < list_count; i++) { if (!imap_arg_get_astring(&hdr_list[i], &value)) { result = -1; break; } value = p_strdup(pool, t_str_ucase(value)); array_append(fields, &value, 1); } /* istream-header-filter requires headers to be sorted */ array_sort(fields, i_strcasecmp_p); } else { result = -1; } imap_parser_unref(&parser); i_stream_unref(&input); return result; } static int imap_msgpart_parse_header_fields(struct imap_msgpart *msgpart, const char *header_list) { ARRAY_TYPE(const_string) fields; if (header_list[0] == ' ') header_list++; /* HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */ if (imap_msgpart_get_header_fields(msgpart->pool, header_list, &fields) < 0) return -1; array_append_zero(&fields); msgpart->headers = array_idx(&fields, 0); return 0; } int imap_msgpart_parse(const char *section, struct imap_msgpart **msgpart_r) { struct imap_msgpart *msgpart; pool_t pool; unsigned int i; bool next_digit; int ret; pool = pool_alloconly_create("imap msgpart", 1024); msgpart = *msgpart_r = p_new(pool, struct imap_msgpart, 1); msgpart->pool = pool; msgpart->partial_size = (uoff_t)-1; /* get the section number */ next_digit = TRUE; for (i = 0; section[i] != '\0'; i++) { if (section[i] >= '0' && section[i] <= '9') { next_digit = FALSE; } else if (!next_digit && section[i] == '.') { next_digit = TRUE; } else { break; } } if (i == 0) { /* [], [HEADER], etc. */ msgpart->section_number = ""; } else if (section[i] == '\0') { /* [1.2.3] */ if (i > 0 && section[i-1] == '.') { pool_unref(&pool); return -1; } msgpart->section_number = p_strdup(pool, section); section = ""; } else { /* [1.2.3.MIME], [1.2.3.HEADER], etc */ if (section[i-1] != '.') { pool_unref(&pool); return -1; } msgpart->section_number = p_strndup(pool, section, i-1); section += i; } if (*section == '\0') { msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY; if (*msgpart->section_number == '\0') { /* BODY[] - header+body */ msgpart->fetch_type = FETCH_FULL; msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER; } else { /* BODY[1] - body only */ msgpart->fetch_type = FETCH_MIME_BODY; } return 0; } section = t_str_ucase(section); if (strcmp(section, "MIME") == 0) { if (msgpart->section_number[0] == '\0') return -1; msgpart->fetch_type = FETCH_MIME; msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY; } else if (strcmp(section, "TEXT") == 0) { /* body (for root or for message/rfc822) */ msgpart->fetch_type = FETCH_BODY; msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY; } else if (strncmp(section, "HEADER", 6) == 0) { /* header (for root or for message/rfc822) */ if (section[6] == '\0') { msgpart->fetch_type = FETCH_HEADER; ret = 0; } else if (strncmp(section, "HEADER.FIELDS.NOT", 17) == 0) { msgpart->fetch_type = FETCH_HEADER_FIELDS_NOT; ret = imap_msgpart_parse_header_fields(msgpart, section+17); } else if (strncmp(section, "HEADER.FIELDS", 13) == 0) { msgpart->fetch_type = FETCH_HEADER_FIELDS; ret = imap_msgpart_parse_header_fields(msgpart, section+13); } else { ret = -1; } if (ret < 0) { imap_msgpart_free(&msgpart); return -1; } if (msgpart->fetch_type == FETCH_HEADER_FIELDS) { /* we may be able to get this from cache, don't give a wanted_fields hint */ } else if (*msgpart->section_number == '\0') msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER; else msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY; } else { imap_msgpart_free(&msgpart); return -1; } return 0; } void imap_msgpart_free(struct imap_msgpart **_msgpart) { struct imap_msgpart *msgpart = *_msgpart; *_msgpart = NULL; imap_msgpart_close_mailbox(msgpart); pool_unref(&msgpart->pool); } bool imap_msgpart_contains_body(const struct imap_msgpart *msgpart) { switch (msgpart->fetch_type) { case FETCH_HEADER: case FETCH_HEADER_FIELDS: case FETCH_HEADER_FIELDS_NOT: return FALSE; case FETCH_FULL: case FETCH_MIME: case FETCH_MIME_BODY: case FETCH_BODY: break; } return TRUE; } void imap_msgpart_set_decode_to_binary(struct imap_msgpart *msgpart) { msgpart->decode_cte_to_binary = TRUE; } void imap_msgpart_set_partial(struct imap_msgpart *msgpart, uoff_t offset, uoff_t size) { msgpart->partial_offset = offset; msgpart->partial_size = size; } uoff_t imap_msgpart_get_partial_offset(struct imap_msgpart *msgpart) { return msgpart->partial_offset; } uoff_t imap_msgpart_get_partial_size(struct imap_msgpart *msgpart) { return msgpart->partial_size; } enum mail_fetch_field imap_msgpart_get_fetch_data(struct imap_msgpart *msgpart) { return msgpart->wanted_fields; } void imap_msgpart_get_wanted_headers(struct imap_msgpart *msgpart, ARRAY_TYPE(const_string) *headers) { unsigned int i; if (msgpart->fetch_type != FETCH_HEADER_FIELDS) return; for (i = 0; msgpart->headers[i] != NULL; i++) array_append(headers, &msgpart->headers[i], 1); } static int imap_msgpart_get_partial_header(struct mail *mail, struct istream *mail_input, const struct imap_msgpart *msgpart, uoff_t *virtual_size_r, bool *have_crlfs_r, struct imap_msgpart_open_result *result_r) { const char *const *hdr_fields = msgpart->headers; unsigned int hdr_count = str_array_length(hdr_fields); struct message_size hdr_size; struct istream *input; bool has_nuls; if (msgpart->fetch_type == FETCH_HEADER_FIELDS) { input = i_stream_create_header_filter(mail_input, HEADER_FILTER_INCLUDE | HEADER_FILTER_HIDE_BODY, hdr_fields, hdr_count, *null_header_filter_callback, (void *)NULL); } else { i_assert(msgpart->fetch_type == FETCH_HEADER_FIELDS_NOT); input = i_stream_create_header_filter(mail_input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_HIDE_BODY, hdr_fields, hdr_count, *null_header_filter_callback, (void *)NULL); } if (message_get_header_size(input, &hdr_size, &has_nuls) < 0) { errno = input->stream_errno; mail_storage_set_critical(mail->box->storage, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); i_stream_unref(&input); return -1; } i_stream_seek(input, 0); result_r->input = input; result_r->size = hdr_size.virtual_size; result_r->size_field = 0; *virtual_size_r = hdr_size.virtual_size; *have_crlfs_r = hdr_size.physical_size == hdr_size.virtual_size; return 0; } static struct istream * imap_msgpart_crlf_seek(struct mail *mail, struct istream *input, const struct imap_msgpart *msgpart) { struct mail_msgpart_partial_cache *cache = &mail->box->partial_cache; struct istream *crlf_input, *errinput; uoff_t physical_start = input->v_offset; uoff_t virtual_skip = msgpart->partial_offset; bool cr_skipped; i_assert(msgpart->headers == NULL); /* HEADER.FIELDS returns CRLFs */ if (virtual_skip == 0) { /* no need to seek */ } else if (mail->uid > 0 && cache->uid == mail->uid && cache->physical_start == physical_start && cache->virtual_pos < virtual_skip) { /* use cache */ i_stream_seek(input, physical_start + cache->physical_pos); virtual_skip -= cache->virtual_pos; } if (message_skip_virtual(input, virtual_skip, &cr_skipped) < 0) { errinput = i_stream_create_error_str(errno, "%s", i_stream_get_error(input)); i_stream_set_name(errinput, i_stream_get_name(input)); i_stream_unref(&input); return errinput; } if (mail->uid > 0 && (msgpart->partial_offset != 0 || msgpart->partial_size != (uoff_t)-1) && !input->eof) { /* update cache */ cache->uid = mail->uid; cache->physical_start = physical_start; cache->physical_pos = input->v_offset - physical_start; cache->virtual_pos = msgpart->partial_offset; if (cr_skipped) { /* the physical_pos points to virtual CRLF, but virtual_pos already skipped CR. that can't work, so seek back the virtual CR */ cache->virtual_pos--; } } crlf_input = i_stream_create_crlf(input); if (cr_skipped) i_stream_skip(crlf_input, 1); i_stream_unref(&input); return crlf_input; } static void imap_msgpart_get_partial(struct mail *mail, const struct imap_msgpart *msgpart, bool convert_nuls, bool use_partial_cache, uoff_t virtual_size, bool have_crlfs, struct imap_msgpart_open_result *result) { struct istream *input2; uoff_t bytes_left; /* input is already seeked to the beginning of the wanted data */ if (msgpart->partial_offset >= virtual_size) { /* can't seek past the MIME part */ i_stream_unref(&result->input); result->input = i_stream_create_from_data("", 0); result->size = 0; return; } if (have_crlfs) { /* input has CRLF linefeeds, we can quickly seek to wanted position */ i_stream_skip(result->input, msgpart->partial_offset); } else { /* input has LF linefeeds. it can be slow to seek to wanted position, so try to do caching whenever possible */ i_assert(use_partial_cache); result->input = imap_msgpart_crlf_seek(mail, result->input, msgpart); } bytes_left = virtual_size - msgpart->partial_offset; if (msgpart->partial_size <= bytes_left) { /* limit output to specified number of bytes */ result->size = msgpart->partial_size; } else { /* send all bytes */ result->size = bytes_left; } if (!mail->has_no_nuls && convert_nuls) { /* IMAP literals must not contain NULs. change them to 0x80 characters. */ input2 = i_stream_create_nonuls(result->input, 0x80); i_stream_unref(&result->input); result->input = input2; } input2 = i_stream_create_limit(result->input, result->size); i_stream_unref(&result->input); result->input = input2; } static int imap_msgpart_find_part(struct mail *mail, const struct imap_msgpart *msgpart, struct message_part **part_r) { struct message_part *parts, *part = NULL; if (*msgpart->section_number == '\0') { *part_r = NULL; return 1; } if (mail_get_parts(mail, &parts) < 0) return -1; part = imap_msgpart_find(parts, msgpart->section_number); if (part == NULL) { /* MIME part not found. */ *part_r = NULL; return 0; } switch (msgpart->fetch_type) { case FETCH_MIME: /* What to do if this is a message/rfc822? Does it have MIME headers or not? Possibilities are: a) no, return empty string (UW-IMAP does this), b) return the same as HEADER. Dovecot has done b) for a long time and it's not very clear which one is correct, so we'll just continue with b) */ case FETCH_FULL: case FETCH_MIME_BODY: break; case FETCH_HEADER: case FETCH_HEADER_FIELDS: case FETCH_HEADER_FIELDS_NOT: case FETCH_BODY: /* fetching message/rfc822 part's header/body */ if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) { *part_r = NULL; return 0; } i_assert(part->children != NULL && part->children->next == NULL); part = part->children; break; } *part_r = part; return 1; } static int imap_msgpart_open_normal(struct mail *mail, struct imap_msgpart *msgpart, const struct message_part *part, uoff_t *virtual_size_r, bool *have_crlfs_r, struct imap_msgpart_open_result *result_r) { struct message_size hdr_size, body_size, part_size; struct istream *input = NULL; bool unknown_crlfs = FALSE; i_zero(&hdr_size); i_zero(&body_size); i_zero(&part_size); if (*msgpart->section_number != '\0') { /* find the MIME part */ if (mail_get_stream_because(mail, NULL, NULL, "MIME part", &input) < 0) return -1; i_stream_seek(input, part->physical_pos); hdr_size = part->header_size; body_size = part->body_size; } else switch (msgpart->fetch_type) { case FETCH_FULL: /* fetch the whole message */ if (mail_get_stream_because(mail, NULL, NULL, "full mail", &input) < 0 || mail_get_virtual_size(mail, &body_size.virtual_size) < 0) return -1; result_r->size_field = MAIL_FETCH_VIRTUAL_SIZE; i_assert(mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER); mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; if (mail_get_physical_size(mail, &body_size.physical_size) < 0) unknown_crlfs = TRUE; mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; break; case FETCH_MIME: case FETCH_MIME_BODY: i_unreached(); case FETCH_HEADER: case FETCH_HEADER_FIELDS_NOT: /* fetch the message's header */ if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) return -1; result_r->size_field = MAIL_FETCH_MESSAGE_PARTS; break; case FETCH_HEADER_FIELDS: /* try to lookup the headers from cache */ if (msgpart->header_ctx == NULL) { msgpart->header_ctx = mailbox_header_lookup_init(mail->box, msgpart->headers); } if (mail_get_header_stream(mail, msgpart->header_ctx, &input) < 0) return -1; result_r->size_field = 0; break; case FETCH_BODY: /* fetch the message's body */ if (mail_get_stream_because(mail, &hdr_size, &body_size, "mail body", &input) < 0) return -1; result_r->size_field = MAIL_FETCH_MESSAGE_PARTS; break; } if (msgpart->headers != NULL) { /* return specific headers */ return imap_msgpart_get_partial_header(mail, input, msgpart, virtual_size_r, have_crlfs_r, result_r); } switch (msgpart->fetch_type) { case FETCH_FULL: part_size.physical_size += body_size.physical_size; part_size.virtual_size += body_size.virtual_size; /* fall through */ case FETCH_MIME: case FETCH_HEADER: part_size.physical_size += hdr_size.physical_size; part_size.virtual_size += hdr_size.virtual_size; break; case FETCH_HEADER_FIELDS: case FETCH_HEADER_FIELDS_NOT: i_unreached(); case FETCH_BODY: case FETCH_MIME_BODY: i_stream_skip(input, hdr_size.physical_size); part_size.physical_size += body_size.physical_size; part_size.virtual_size += body_size.virtual_size; break; } result_r->input = input; i_stream_ref(input); *virtual_size_r = part_size.virtual_size; *have_crlfs_r = !unknown_crlfs && part_size.virtual_size == part_size.physical_size; return 0; } int imap_msgpart_open(struct mail *mail, struct imap_msgpart *msgpart, struct imap_msgpart_open_result *result_r) { struct message_part *part; uoff_t virtual_size; bool include_hdr, binary, use_partial_cache, have_crlfs; int ret; i_zero(result_r); if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0) return -1; if (ret == 0) { /* MIME part not found. return an empty part. */ result_r->input = i_stream_create_from_data("", 0); return 0; } if (msgpart->decode_cte_to_binary && (msgpart->fetch_type == FETCH_FULL || msgpart->fetch_type == FETCH_BODY || msgpart->fetch_type == FETCH_MIME_BODY)) { /* binary fetch */ include_hdr = msgpart->fetch_type == FETCH_FULL; if (part == NULL) { if (mail_get_parts(mail, &part) < 0) return -1; } if (mail_get_binary_stream(mail, part, include_hdr, &virtual_size, &binary, &result_r->input) < 0) return -1; have_crlfs = TRUE; use_partial_cache = FALSE; } else { if (imap_msgpart_open_normal(mail, msgpart, part, &virtual_size, &have_crlfs, result_r) < 0) return -1; binary = FALSE; use_partial_cache = TRUE; } if (binary && msgpart->decode_cte_to_binary) result_r->binary_decoded_input_has_nuls = TRUE; imap_msgpart_get_partial(mail, msgpart, !binary, use_partial_cache, virtual_size, have_crlfs, result_r); return 0; } int imap_msgpart_size(struct mail *mail, struct imap_msgpart *msgpart, uoff_t *size_r) { struct imap_msgpart_open_result result; struct message_part *part; bool include_hdr; unsigned int lines; int ret; if (!msgpart->decode_cte_to_binary || (msgpart->fetch_type != FETCH_FULL && msgpart->fetch_type != FETCH_BODY && msgpart->fetch_type != FETCH_MIME_BODY)) { /* generic implementation */ if (imap_msgpart_open(mail, msgpart, &result) < 0) return -1; i_stream_unref(&result.input); *size_r = result.size; return 0; } /* binary-optimized implementation: */ if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0) return -1; if (ret == 0) { /* MIME part not found. return an empty part. */ *size_r = 0; return 0; } if (part == NULL) { if (mail_get_parts(mail, &part) < 0) return -1; } include_hdr = msgpart->fetch_type == FETCH_FULL; return mail_get_binary_size(mail, part, include_hdr, size_r, &lines); } static int imap_msgpart_parse_bodystructure(struct mail *mail, struct message_part *all_parts) { struct mail_private *pmail = (struct mail_private *)mail; const char *bodystructure, *error; if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &bodystructure) < 0) return -1; if (all_parts->context != NULL) { /* we just parsed the bodystructure */ return 0; } if (imap_bodystructure_parse(bodystructure, pmail->data_pool, all_parts, &error) < 0) { mail_set_cache_corrupted_reason(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, t_strdup_printf( "Invalid message_part/BODYSTRUCTURE %s: %s", bodystructure, error)); return -1; } return 0; } static int imap_msgpart_vsizes_to_binary(struct mail *mail, const struct message_part *part, struct message_part **binpart_r) { struct message_part **pos; uoff_t size; unsigned int lines; if (mail_get_binary_size(mail, part, FALSE, &size, &lines) < 0) return -1; *binpart_r = t_new(struct message_part, 1); **binpart_r = *part; (*binpart_r)->body_size.virtual_size = size; (*binpart_r)->body_size.lines = lines; pos = &(*binpart_r)->children; for (part = part->children; part != NULL; part = part->next) { if (imap_msgpart_vsizes_to_binary(mail, part, pos) < 0) return -1; pos = &(*pos)->next; } return 0; } int imap_msgpart_bodypartstructure(struct mail *mail, struct imap_msgpart *msgpart, const char **bpstruct_r) { struct message_part *all_parts, *part; string_t *bpstruct; int ret; /* if we start parsing the body in here, make sure we also parse the BODYSTRUCTURE */ mail_add_temp_wanted_fields(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, NULL); if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0) return -1; if (ret == 0) { /* MIME part not found. */ *bpstruct_r = NULL; return 0; } if (mail_get_parts(mail, &all_parts) < 0) return -1; if (all_parts->context == NULL) { if (imap_msgpart_parse_bodystructure(mail, all_parts) < 0) return -1; } if (part == NULL) part = all_parts; if (msgpart->decode_cte_to_binary) ret = imap_msgpart_vsizes_to_binary(mail, part, &part); if (ret >= 0) { bpstruct = t_str_new(256); imap_bodystructure_write(part, bpstruct, TRUE); *bpstruct_r = str_c(bpstruct); } return ret < 0 ? -1 : 1; } void imap_msgpart_close_mailbox(struct imap_msgpart *msgpart) { if (msgpart->header_ctx != NULL) mailbox_header_lookup_unref(&msgpart->header_ctx); } dovecot-2.2.33.2/src/lib-imap-storage/imap-msgpart-url.c0000644000175000017500000001622513165463624017635 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "istream.h" #include "message-parser.h" #include "mail-storage.h" #include "mail-namespace.h" #include "imap-url.h" #include "imap-msgpart.h" #include "imap-msgpart-url.h" struct imap_msgpart_url { char *mailbox; uint32_t uidvalidity; uint32_t uid; char *section; uoff_t partial_offset, partial_size; struct imap_msgpart *part; struct mail_user *user; struct mailbox *selected_box; struct mailbox *box; struct mailbox_transaction_context *trans; struct mail *mail; struct imap_msgpart_open_result result; unsigned int decode_cte_to_binary:1; }; int imap_msgpart_url_create(struct mail_user *user, const struct imap_url *url, struct imap_msgpart_url **mpurl_r, const char **error_r) { const char *section = url->section == NULL ? "" : url->section; struct imap_msgpart_url *mpurl; struct imap_msgpart *msgpart; i_assert(url->mailbox != NULL && url->uid != 0 && url->search_program == NULL); if (imap_msgpart_parse(section, &msgpart) < 0) { *error_r = "Invalid section"; return -1; } mpurl = i_new(struct imap_msgpart_url, 1); mpurl->part = msgpart; mpurl->user = user; mpurl->mailbox = i_strdup(url->mailbox); mpurl->uidvalidity = url->uidvalidity; mpurl->uid = url->uid; if (url->section != NULL) mpurl->section = i_strdup(url->section); mpurl->partial_offset = url->partial_offset; mpurl->partial_size = url->partial_size; imap_msgpart_set_partial(msgpart, url->partial_offset, url->partial_size == 0 ? (uoff_t)-1 : url->partial_size); *mpurl_r = mpurl; return 0; } int imap_msgpart_url_parse(struct mail_user *user, struct mailbox *selected_box, const char *urlstr, struct imap_msgpart_url **mpurl_r, const char **error_r) { struct mailbox_status box_status; struct imap_url base_url, *url; const char *error; /* build base url */ i_zero(&base_url); if (selected_box != NULL) { mailbox_get_open_status(selected_box, STATUS_UIDVALIDITY, &box_status); base_url.mailbox = mailbox_get_vname(selected_box); base_url.uidvalidity = box_status.uidvalidity; } /* parse url */ if (imap_url_parse(urlstr, &base_url, IMAP_URL_PARSE_REQUIRE_RELATIVE, &url, &error) < 0) { *error_r = t_strconcat("Invalid IMAP URL: ", error, NULL); return 0; } if (url->mailbox == NULL) { *error_r = "Mailbox-relative IMAP URL, but no mailbox selected"; return 0; } if (url->uid == 0 || url->search_program != NULL) { *error_r = "Invalid messagepart IMAP URL"; return 0; } if (imap_msgpart_url_create(user, url, mpurl_r, error_r) < 0) return -1; (*mpurl_r)->selected_box = selected_box; return 1; } struct mailbox *imap_msgpart_url_get_mailbox(struct imap_msgpart_url *mpurl) { return mpurl->box; } int imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl, struct mailbox **box_r, enum mail_error *error_code_r, const char **error_r) { struct mailbox_status box_status; enum mailbox_flags flags = MAILBOX_FLAG_READONLY; struct mail_namespace *ns; struct mailbox *box; if (mpurl->box != NULL) { *box_r = mpurl->box; *error_code_r = MAIL_ERROR_NONE; return 1; } /* find mailbox namespace */ ns = mail_namespace_find(mpurl->user->namespaces, mpurl->mailbox); /* open mailbox */ if (mpurl->selected_box != NULL && mailbox_equals(mpurl->selected_box, ns, mpurl->mailbox)) box = mpurl->selected_box; else { box = mailbox_alloc(ns->list, mpurl->mailbox, flags); mailbox_set_reason(box, "Read IMAP URL"); } if (mailbox_open(box) < 0) { *error_r = mail_storage_get_last_error(mailbox_get_storage(box), error_code_r); if (box != mpurl->selected_box) mailbox_free(&box); return *error_code_r == MAIL_ERROR_TEMP ? -1 : 0; } /* verify UIDVALIDITY */ mailbox_get_open_status(box, STATUS_UIDVALIDITY, &box_status); if (mpurl->uidvalidity > 0 && box_status.uidvalidity != mpurl->uidvalidity) { *error_r = "Invalid UIDVALIDITY"; *error_code_r = MAIL_ERROR_EXPUNGED; if (box != mpurl->selected_box) mailbox_free(&box); return 0; } mpurl->box = box; *box_r = box; return 1; } int imap_msgpart_url_open_mail(struct imap_msgpart_url *mpurl, struct mail **mail_r, const char **error_r) { struct mailbox_transaction_context *t; struct mailbox *box; enum mail_error error_code; struct mail *mail; int ret; if (mpurl->mail != NULL) { *mail_r = mpurl->mail; return 1; } /* open mailbox if it is not yet open */ if ((ret = imap_msgpart_url_open_mailbox(mpurl, &box, &error_code, error_r)) <= 0) return ret; /* start transaction */ t = mailbox_transaction_begin(box, 0); mail = mail_alloc(t, MAIL_FETCH_MESSAGE_PARTS | MAIL_FETCH_IMAP_BODYSTRUCTURE, NULL); /* find the message */ if (!mail_set_uid(mail, mpurl->uid)) { *error_r = "Message not found"; mail_free(&mail); mailbox_transaction_rollback(&t); return 0; } mpurl->trans = t; mpurl->mail = mail; *mail_r = mail; return 1; } struct imap_msgpart * imap_msgpart_url_get_part(struct imap_msgpart_url *mpurl) { return mpurl->part; } void imap_msgpart_url_set_decode_to_binary(struct imap_msgpart_url *mpurl) { imap_msgpart_set_decode_to_binary(mpurl->part); } int imap_msgpart_url_read_part(struct imap_msgpart_url *mpurl, struct imap_msgpart_open_result *result_r, const char **error_r) { struct mail *mail; int ret; if (mpurl->result.input != NULL) { i_stream_seek(mpurl->result.input, 0); *result_r = mpurl->result; return 1; } /* open mail if it is not yet open */ ret = imap_msgpart_url_open_mail(mpurl, &mail, error_r); if (ret <= 0) return ret; /* open the referenced part as a stream */ ret = imap_msgpart_open(mail, mpurl->part, result_r); if (ret < 0) { *error_r = mailbox_get_last_error(mpurl->box, NULL); return ret; } mpurl->result = *result_r; return 1; } int imap_msgpart_url_verify(struct imap_msgpart_url *mpurl, const char **error_r) { struct mail *mail; int ret; if (mpurl->result.input != NULL) return 1; /* open mail if it is not yet open */ ret = imap_msgpart_url_open_mail(mpurl, &mail, error_r); return ret; } int imap_msgpart_url_get_bodypartstructure(struct imap_msgpart_url *mpurl, const char **bpstruct_r, const char **error_r) { struct mail *mail; int ret; /* open mail if it is not yet open */ ret = imap_msgpart_url_open_mail(mpurl, &mail, error_r); if (ret <= 0) return ret; ret = imap_msgpart_bodypartstructure(mail, mpurl->part, bpstruct_r); if (ret < 0) *error_r = mailbox_get_last_error(mpurl->box, NULL); else if (ret == 0) *error_r = "Message part not found"; return ret; } void imap_msgpart_url_free(struct imap_msgpart_url **_mpurl) { struct imap_msgpart_url *mpurl = *_mpurl; *_mpurl = NULL; if (mpurl->result.input != NULL) i_stream_unref(&mpurl->result.input); if (mpurl->part != NULL) imap_msgpart_free(&mpurl->part); if (mpurl->mail != NULL) mail_free(&mpurl->mail); if (mpurl->trans != NULL) mailbox_transaction_rollback(&mpurl->trans); if (mpurl->box != NULL && mpurl->box != mpurl->selected_box) mailbox_free(&mpurl->box); if (mpurl->section != NULL) i_free(mpurl->section); i_free(mpurl->mailbox); i_free(mpurl); } dovecot-2.2.33.2/src/lib-imap-storage/imap-msgpart-url.h0000644000175000017500000000353513123174404017627 00000000000000#ifndef IMAP_MSGPART_URL_H #define IMAP_MSGPART_URL_H #include "imap-msgpart.h" struct imap_url; struct imap_msgpart; struct imap_msgpart_url; /* Functions returning int return 1 on success, 0 if URL doesn't point to valid mail, -1 on storage error. */ int imap_msgpart_url_create(struct mail_user *user, const struct imap_url *url, struct imap_msgpart_url **url_r, const char **error_r); int imap_msgpart_url_parse(struct mail_user *user, struct mailbox *selected_box, const char *urlstr, struct imap_msgpart_url **url_r, const char **error_r); int imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl, struct mailbox **box_r, enum mail_error *error_code_r, const char **error_r); struct mailbox *imap_msgpart_url_get_mailbox(struct imap_msgpart_url *mpurl); int imap_msgpart_url_open_mail(struct imap_msgpart_url *mpurl, struct mail **mail_r, const char **error_r); struct imap_msgpart * imap_msgpart_url_get_part(struct imap_msgpart_url *mpurl); /* Decode MIME parts with Content-Transfer-Encoding: base64/quoted-printable to binary data (IMAP BINARY extension). If something can't be decoded, fails with storage error set to MAIL_ERROR_CONVERSION. */ void imap_msgpart_url_set_decode_to_binary(struct imap_msgpart_url *mpurl); /* stream_r is set to NULL when part has zero length, e.g. when partial offset is larger than the size of the referenced part */ int imap_msgpart_url_read_part(struct imap_msgpart_url *mpurl, struct imap_msgpart_open_result *result_r, const char **error_r); int imap_msgpart_url_get_bodypartstructure(struct imap_msgpart_url *mpurl, const char **bpstruct_r, const char **error_r); int imap_msgpart_url_verify(struct imap_msgpart_url *mpurl, const char **error_r); void imap_msgpart_url_free(struct imap_msgpart_url **mpurl); #endif dovecot-2.2.33.2/src/lib-imap-storage/Makefile.am0000644000175000017500000000077113123174404016310 00000000000000noinst_LTLIBRARIES = libimap-storage.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-imap libimap_storage_la_SOURCES = \ imap-msgpart.c \ imap-msgpart-url.c \ imap-metadata.c headers = \ imap-msgpart.h \ imap-msgpart-url.h \ imap-metadata.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-ntlm/0002755000175000017500000000000013172375611012722 500000000000000dovecot-2.2.33.2/src/lib-ntlm/ntlm-flags.h0000644000175000017500000000774013123174404015056 00000000000000/* * NTLM message flags. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #ifndef NTLM_FLAGS_H #define NTLM_FLAGS_H /* * Indicates that Unicode strings are supported for use in security * buffer data. */ #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* * Indicates that OEM strings are supported for use in security buffer data. */ #define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* * Requests that the server's authentication realm be included in the * Type 2 message. */ #define NTLMSSP_REQUEST_TARGET 0x00000004 /* * Specifies that authenticated communication between the client and server * should carry a digital signature (message integrity). */ #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* * Specifies that authenticated communication between the client and server * should be encrypted (message confidentiality). */ #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* * Indicates that datagram authentication is being used. */ #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* * Indicates that the LAN Manager session key should be * used for signing and sealing authenticated communications. */ #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* * Indicates that NTLM authentication is being used. */ #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* * Sent by the client in the Type 1 message to indicate that the name of the * domain in which the client workstation has membership is included in the * message. This is used by the server to determine whether the client is * eligible for local authentication. */ #define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 /* * Sent by the client in the Type 1 message to indicate that the client * workstation's name is included in the message. This is used by the server * to determine whether the client is eligible for local authentication. */ #define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 /* * Sent by the server to indicate that the server and client are on the same * machine. Implies that the client may use the established local credentials * for authentication instead of calculating a response to the challenge. */ #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 /* * Indicates that authenticated communication between the client and server * should be signed with a "dummy" signature. */ #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a domain. */ #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a server. */ #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a share. Presumably, this is for share-level * authentication. Usage is unclear. */ #define NTLMSSP_TARGET_TYPE_SHARE 0x00040000 /* * Indicates that the NTLM2 signing and sealing scheme should be used for * protecting authenticated communications. Note that this refers to a * particular session security scheme, and is not related to the use of * NTLMv2 authentication. */ #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 /* * Sent by the server in the Type 2 message to indicate that it is including * a Target Information block in the message. The Target Information block * is used in the calculation of the NTLMv2 response. */ #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* * Indicates that 128-bit encryption is supported. */ #define NTLMSSP_NEGOTIATE_128 0x20000000 /* * Indicates that the client will provide an encrypted master session key in * the "Session Key" field of the Type 3 message. This is used in signing and * sealing, and is RC4-encrypted using the previous session key as the * encryption key. */ #define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000 /* * Indicates that 56-bit encryption is supported. */ #define NTLMSSP_NEGOTIATE_56 0x80000000 #endif dovecot-2.2.33.2/src/lib-ntlm/Makefile.in0000644000175000017500000004750713172375573014731 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-ntlm ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libntlm_a_AR = $(AR) $(ARFLAGS) libntlm_a_LIBADD = am_libntlm_a_OBJECTS = ntlm-des.$(OBJEXT) ntlm-encrypt.$(OBJEXT) \ ntlm-message.$(OBJEXT) libntlm_a_OBJECTS = $(am_libntlm_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libntlm_a_SOURCES) DIST_SOURCES = $(libntlm_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libntlm.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libntlm_a_SOURCES = \ ntlm-des.c \ ntlm-encrypt.c \ ntlm-message.c noinst_HEADERS = \ ntlm.h \ ntlm-types.h \ ntlm-flags.h \ ntlm-byteorder.h \ ntlm-des.h \ ntlm-encrypt.h \ ntlm-message.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-ntlm/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-ntlm/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libntlm.a: $(libntlm_a_OBJECTS) $(libntlm_a_DEPENDENCIES) $(EXTRA_libntlm_a_DEPENDENCIES) $(AM_V_at)-rm -f libntlm.a $(AM_V_AR)$(libntlm_a_AR) libntlm.a $(libntlm_a_OBJECTS) $(libntlm_a_LIBADD) $(AM_V_at)$(RANLIB) libntlm.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlm-des.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlm-encrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlm-message.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(HEADERS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-ntlm/ntlm-message.h0000644000175000017500000000063313123174404015400 00000000000000#ifndef NTLM_MESSAGE_H #define NTLM_MESSAGE_H const struct ntlmssp_challenge * ntlmssp_create_challenge(pool_t pool, const struct ntlmssp_request *request, size_t *size); bool ntlmssp_check_request(const struct ntlmssp_request *request, size_t data_size, const char **error); bool ntlmssp_check_response(const struct ntlmssp_response *response, size_t data_size, const char **error); #endif dovecot-2.2.33.2/src/lib-ntlm/ntlm-byteorder.h0000644000175000017500000000473413123174404015761 00000000000000/* * Little-endian data access functions. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #ifndef NTLM_BYTEORDER_H #define NTLM_BYTEORDER_H #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) static inline uint16_t read_le16(const void *addr) { return *((const uint16_t *) addr); } static inline uint32_t read_le32(const void *addr) { return *((const uint32_t *) addr); } static inline uint64_t read_le64(const void *addr) { return *((const uint64_t *) addr); } static inline void write_le16(void *addr, const uint16_t value) { *((uint16_t *) addr) = value; } static inline void write_le32(void *addr, const uint32_t value) { *((uint32_t *) addr) = value; } static inline void write_le64(void *addr, const uint64_t value) { *((uint64_t *) addr) = value; } #else /* * Dumb and slow, but byteorder and alignment independent code. */ #define readb(addr, pos, type) ((type)(*(((uint8_t *) (addr)) + (pos)))) static inline uint16_t read_le16(const void *addr) { return readb(addr, 0, uint16_t) | (readb(addr, 1, uint16_t) << 8); } static inline uint32_t read_le32(const void *addr) { return readb(addr, 0, uint32_t) | (readb(addr, 1, uint32_t) << 8) | (readb(addr, 2, uint32_t) << 16) | (readb(addr, 3, uint32_t) << 24); } static inline uint64_t read_le64(const void *addr) { return readb(addr, 0, uint64_t) | (readb(addr, 1, uint64_t) << 8) | (readb(addr, 2, uint64_t) << 16) | (readb(addr, 3, uint64_t) << 24) | (readb(addr, 4, uint64_t) << 32) | (readb(addr, 5, uint64_t) << 40) | (readb(addr, 6, uint64_t) << 48) | (readb(addr, 7, uint64_t) << 56); } #define writeb(addr, pos, value) \ *(((uint8_t *)(addr)) + (pos)) = (uint8_t) (value) static inline void write_le16(void *addr, const uint16_t value) { writeb(addr, 0, value & 0xff); writeb(addr, 1, (value >> 8) & 0xff); } static inline void write_le32(void *addr, const uint32_t value) { writeb(addr, 0, value & 0xff); writeb(addr, 1, (value >> 8) & 0xff); writeb(addr, 2, (value >> 16) & 0xff); writeb(addr, 3, (value >> 24) & 0xff); } static inline void write_le64(void *addr, const uint64_t value) { writeb(addr, 0, value & 0xff); writeb(addr, 1, (value >> 8) & 0xff); writeb(addr, 2, (value >> 16) & 0xff); writeb(addr, 3, (value >> 24) & 0xff); writeb(addr, 4, (value >> 32) & 0xff); writeb(addr, 5, (value >> 40) & 0xff); writeb(addr, 6, (value >> 48) & 0xff); writeb(addr, 7, (value >> 56) & 0xff); } #endif #endif dovecot-2.2.33.2/src/lib-ntlm/ntlm-des.h0000644000175000017500000000021313123174404014521 00000000000000#ifndef NTLM_DES_H #define NTLM_DES_H void deshash(unsigned char *dst, const unsigned char *key, const unsigned char *src); #endif dovecot-2.2.33.2/src/lib-ntlm/ntlm-message.c0000644000175000017500000001372713165463624015416 00000000000000/* * NTLM message handling. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "str.h" #include "buffer.h" #include "hostpid.h" #include "randgen.h" #include "ntlm.h" #include "ntlm-message.h" #include #include const char *ntlmssp_t_str_i(const void *message, struct ntlmssp_buffer *buffer, bool unicode) { unsigned int len = read_le16(&buffer->length); const char *p = ((const char *) message) + read_le32(&buffer->offset); string_t *str; if (unicode) len /= sizeof(ucs2le_t); str = t_str_new(len); while (len-- > 0) { str_append_c(str, *p & 0x7f); p += unicode ? sizeof(ucs2le_t) : 1; } return str_c(str); } static unsigned int append_string(buffer_t *buf, const char *str, bool ucase, bool unicode) { unsigned int length = 0; for ( ; *str; str++) { buffer_append_c(buf, ucase ? i_toupper(*str) : *str); if (unicode) { buffer_append_c(buf, 0); length++; } length++; } return length; } static void ntlmssp_append_string(buffer_t *buf, size_t buffer_offset, const char *str, int unicode) { struct ntlmssp_buffer buffer; unsigned int length; write_le32(&buffer.offset, buffer_get_used_size(buf)); length = append_string(buf, str, FALSE, unicode); write_le16(&buffer.length, length); write_le16(&buffer.space, length); buffer_write(buf, buffer_offset, &buffer, sizeof(buffer)); } static void ntlmssp_append_target_info(buffer_t *buf, size_t buffer_offset, ...) { struct ntlmssp_v2_target_info info; struct ntlmssp_buffer buffer; va_list args; unsigned int length, total_length = 0; int type; write_le32(&buffer.offset, buffer_get_used_size(buf)); va_start(args, buffer_offset); do { const char *data; type = va_arg(args, int); i_zero(&info); write_le16(&info.type, type); switch (type) { case NTPLMSSP_V2_TARGET_END: buffer_append(buf, &info, sizeof(info)); length = sizeof(info); break; case NTPLMSSP_V2_TARGET_SERVER: case NTPLMSSP_V2_TARGET_DOMAIN: case NTPLMSSP_V2_TARGET_FQDN: case NTPLMSSP_V2_TARGET_DNS: data = va_arg(args, const char *); write_le16(&info.length, strlen(data) * sizeof(ucs2le_t)); buffer_append(buf, &info, sizeof(info)); length = append_string(buf, data, FALSE, TRUE) + sizeof(info); break; default: i_panic("Invalid NTLM target info block type " "%u", type); } total_length += length; } while (type != NTPLMSSP_V2_TARGET_END); va_end(args); write_le16(&buffer.length, total_length); write_le16(&buffer.space, total_length); buffer_write(buf, buffer_offset, &buffer, sizeof(buffer)); } static inline uint32_t ntlmssp_flags(uint32_t client_flags) { uint32_t flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO; if (client_flags & NTLMSSP_NEGOTIATE_UNICODE) flags |= NTLMSSP_NEGOTIATE_UNICODE; else flags |= NTLMSSP_NEGOTIATE_OEM; if (client_flags & NTLMSSP_NEGOTIATE_NTLM2) flags |= NTLMSSP_NEGOTIATE_NTLM2; if (client_flags & NTLMSSP_REQUEST_TARGET) flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_SERVER; return flags; } const struct ntlmssp_challenge * ntlmssp_create_challenge(pool_t pool, const struct ntlmssp_request *request, size_t *size) { buffer_t *buf; uint32_t flags = ntlmssp_flags(read_le32(&request->flags)); int unicode = flags & NTLMSSP_NEGOTIATE_UNICODE; struct ntlmssp_challenge c; buf = buffer_create_dynamic(pool, sizeof(struct ntlmssp_challenge)); i_zero(&c); write_le64(&c.magic, NTLMSSP_MAGIC); write_le32(&c.type, NTLMSSP_MSG_TYPE2); write_le32(&c.flags, flags); random_fill(c.challenge, sizeof(c.challenge)); buffer_write(buf, 0, &c, sizeof(c)); if (flags & NTLMSSP_TARGET_TYPE_SERVER) ntlmssp_append_string(buf, offsetof(struct ntlmssp_challenge, target_name), my_hostname, unicode); ntlmssp_append_target_info(buf, offsetof(struct ntlmssp_challenge, target_info), NTPLMSSP_V2_TARGET_FQDN, my_hostname, NTPLMSSP_V2_TARGET_END); *size = buffer_get_used_size(buf); return buffer_free_without_data(&buf); } static int ntlmssp_check_buffer(const struct ntlmssp_buffer *buffer, size_t data_size, const char **error) { uint32_t offset = read_le32(&buffer->offset); uint16_t length = read_le16(&buffer->length); uint16_t space = read_le16(&buffer->space); /* Empty buffer is ok */ if (length == 0 && space == 0) return 1; if (offset >= data_size) { *error = "buffer offset out of bounds"; return 0; } if (offset + space > data_size) { *error = "buffer end out of bounds"; return 0; } return 1; } bool ntlmssp_check_request(const struct ntlmssp_request *request, size_t data_size, const char **error) { uint32_t flags; if (data_size < sizeof(struct ntlmssp_request)) { *error = "request too short"; return FALSE; } if (read_le64(&request->magic) != NTLMSSP_MAGIC) { *error = "signature mismatch"; return FALSE; } if (read_le32(&request->type) != NTLMSSP_MSG_TYPE1) { *error = "message type mismatch"; return FALSE; } flags = read_le32(&request->flags); if ((flags & NTLMSSP_NEGOTIATE_NTLM) == 0) { *error = "client doesn't advertise NTLM support"; return FALSE; } return TRUE; } bool ntlmssp_check_response(const struct ntlmssp_response *response, size_t data_size, const char **error) { if (data_size < sizeof(struct ntlmssp_response)) { *error = "response too short"; return FALSE; } if (read_le64(&response->magic) != NTLMSSP_MAGIC) { *error = "signature mismatch"; return FALSE; } if (read_le32(&response->type) != NTLMSSP_MSG_TYPE3) { *error = "message type mismatch"; return FALSE; } if (!ntlmssp_check_buffer(&response->lm_response, data_size, error) || !ntlmssp_check_buffer(&response->ntlm_response, data_size, error) || !ntlmssp_check_buffer(&response->domain, data_size, error) || !ntlmssp_check_buffer(&response->user, data_size, error) || !ntlmssp_check_buffer(&response->workstation, data_size, error)) return FALSE; return TRUE; } dovecot-2.2.33.2/src/lib-ntlm/ntlm-encrypt.h0000644000175000017500000000141513123174404015437 00000000000000#ifndef NTLM_ENCRYPT_H #define NTLM_ENCRYPT_H void lm_hash(const char *passwd, unsigned char hash[LM_HASH_SIZE]); void ntlm_v1_hash(const char *passwd, unsigned char hash[NTLMSSP_HASH_SIZE]); void ntlmssp_v1_response(const unsigned char *hash, const unsigned char *challenge, unsigned char response[NTLMSSP_RESPONSE_SIZE]); void ntlmssp2_response( const unsigned char *hash, const unsigned char *server_challenge, const unsigned char *client_challenge, unsigned char response[NTLMSSP_RESPONSE_SIZE]); void ntlmssp_v2_response(const char *user, const char *target, const unsigned char *hash_v1, const unsigned char *challenge, const unsigned char *blob, size_t blob_size, unsigned char response[NTLMSSP_V2_RESPONSE_SIZE]) ATTR_NULL(2); #endif dovecot-2.2.33.2/src/lib-ntlm/ntlm-des.c0000644000175000017500000005622613165463624014546 00000000000000/* * Implementation of DES encryption for NTLM * * Copyright 1997-2005 Simon Tatham. * * This software is released under the MIT license. */ #include "lib.h" #include "ntlm-des.h" /* * Description of DES * ------------------ * * Unlike the description in FIPS 46, I'm going to use _sensible_ indices: * bits in an n-bit word are numbered from 0 at the LSB to n-1 at the MSB. * And S-boxes are indexed by six consecutive bits, not by the outer two * followed by the middle four. * * The DES encryption routine requires a 64-bit input, and a key schedule K * containing 16 48-bit elements. * * First the input is permuted by the initial permutation IP. * Then the input is split into 32-bit words L and R. (L is the MSW.) * Next, 16 rounds. In each round: * (L, R) <- (R, L xor f(R, K[i])) * Then the pre-output words L and R are swapped. * Then L and R are glued back together into a 64-bit word. (L is the MSW, * again, but since we just swapped them, the MSW is the R that came out * of the last round.) * The 64-bit output block is permuted by the inverse of IP and returned. * * Decryption is identical except that the elements of K are used in the * opposite order. (This wouldn't work if that word swap didn't happen.) * * The function f, used in each round, accepts a 32-bit word R and a * 48-bit key block K. It produces a 32-bit output. * * First R is expanded to 48 bits using the bit-selection function E. * The resulting 48-bit block is XORed with the key block K to produce * a 48-bit block X. * This block X is split into eight groups of 6 bits. Each group of 6 * bits is then looked up in one of the eight S-boxes to convert * it to 4 bits. These eight groups of 4 bits are glued back * together to produce a 32-bit preoutput block. * The preoutput block is permuted using the permutation P and returned. * * Key setup maps a 64-bit key word into a 16x48-bit key schedule. Although * the approved input format for the key is a 64-bit word, eight of the * bits are discarded, so the actual quantity of key used is 56 bits. * * First the input key is converted to two 28-bit words C and D using * the bit-selection function PC1. * Then 16 rounds of key setup occur. In each round, C and D are each * rotated left by either 1 or 2 bits (depending on which round), and * then converted into a key schedule element using the bit-selection * function PC2. * * That's the actual algorithm. Now for the tedious details: all those * painful permutations and lookup tables. * * IP is a 64-to-64 bit permutation. Its output contains the following * bits of its input (listed in order MSB to LSB of output). * * 6 14 22 30 38 46 54 62 4 12 20 28 36 44 52 60 * 2 10 18 26 34 42 50 58 0 8 16 24 32 40 48 56 * 7 15 23 31 39 47 55 63 5 13 21 29 37 45 53 61 * 3 11 19 27 35 43 51 59 1 9 17 25 33 41 49 57 * * E is a 32-to-48 bit selection function. Its output contains the following * bits of its input (listed in order MSB to LSB of output). * * 0 31 30 29 28 27 28 27 26 25 24 23 24 23 22 21 20 19 20 19 18 17 16 15 * 16 15 14 13 12 11 12 11 10 9 8 7 8 7 6 5 4 3 4 3 2 1 0 31 * * The S-boxes are arbitrary table-lookups each mapping a 6-bit input to a * 4-bit output. In other words, each S-box is an array[64] of 4-bit numbers. * The S-boxes are listed below. The first S-box listed is applied to the * most significant six bits of the block X; the last one is applied to the * least significant. * * 14 0 4 15 13 7 1 4 2 14 15 2 11 13 8 1 * 3 10 10 6 6 12 12 11 5 9 9 5 0 3 7 8 * 4 15 1 12 14 8 8 2 13 4 6 9 2 1 11 7 * 15 5 12 11 9 3 7 14 3 10 10 0 5 6 0 13 * * 15 3 1 13 8 4 14 7 6 15 11 2 3 8 4 14 * 9 12 7 0 2 1 13 10 12 6 0 9 5 11 10 5 * 0 13 14 8 7 10 11 1 10 3 4 15 13 4 1 2 * 5 11 8 6 12 7 6 12 9 0 3 5 2 14 15 9 * * 10 13 0 7 9 0 14 9 6 3 3 4 15 6 5 10 * 1 2 13 8 12 5 7 14 11 12 4 11 2 15 8 1 * 13 1 6 10 4 13 9 0 8 6 15 9 3 8 0 7 * 11 4 1 15 2 14 12 3 5 11 10 5 14 2 7 12 * * 7 13 13 8 14 11 3 5 0 6 6 15 9 0 10 3 * 1 4 2 7 8 2 5 12 11 1 12 10 4 14 15 9 * 10 3 6 15 9 0 0 6 12 10 11 1 7 13 13 8 * 15 9 1 4 3 5 14 11 5 12 2 7 8 2 4 14 * * 2 14 12 11 4 2 1 12 7 4 10 7 11 13 6 1 * 8 5 5 0 3 15 15 10 13 3 0 9 14 8 9 6 * 4 11 2 8 1 12 11 7 10 1 13 14 7 2 8 13 * 15 6 9 15 12 0 5 9 6 10 3 4 0 5 14 3 * * 12 10 1 15 10 4 15 2 9 7 2 12 6 9 8 5 * 0 6 13 1 3 13 4 14 14 0 7 11 5 3 11 8 * 9 4 14 3 15 2 5 12 2 9 8 5 12 15 3 10 * 7 11 0 14 4 1 10 7 1 6 13 0 11 8 6 13 * * 4 13 11 0 2 11 14 7 15 4 0 9 8 1 13 10 * 3 14 12 3 9 5 7 12 5 2 10 15 6 8 1 6 * 1 6 4 11 11 13 13 8 12 1 3 4 7 10 14 7 * 10 9 15 5 6 0 8 15 0 14 5 2 9 3 2 12 * * 13 1 2 15 8 13 4 8 6 10 15 3 11 7 1 4 * 10 12 9 5 3 6 14 11 5 0 0 14 12 9 7 2 * 7 2 11 1 4 14 1 7 9 4 12 10 14 8 2 13 * 0 15 6 12 10 9 13 0 15 3 3 5 5 6 8 11 * * P is a 32-to-32 bit permutation. Its output contains the following * bits of its input (listed in order MSB to LSB of output). * * 16 25 12 11 3 20 4 15 31 17 9 6 27 14 1 22 * 30 24 8 18 0 5 29 23 13 19 2 26 10 21 28 7 * * PC1 is a 64-to-56 bit selection function. Its output is in two words, * C and D. The word C contains the following bits of its input (listed * in order MSB to LSB of output). * * 7 15 23 31 39 47 55 63 6 14 22 30 38 46 * 54 62 5 13 21 29 37 45 53 61 4 12 20 28 * * And the word D contains these bits. * * 1 9 17 25 33 41 49 57 2 10 18 26 34 42 * 50 58 3 11 19 27 35 43 51 59 36 44 52 60 * * PC2 is a 56-to-48 bit selection function. Its input is in two words, * C and D. These are treated as one 56-bit word (with C more significant, * so that bits 55 to 28 of the word are bits 27 to 0 of C, and bits 27 to * 0 of the word are bits 27 to 0 of D). The output contains the following * bits of this 56-bit input word (listed in order MSB to LSB of output). * * 42 39 45 32 55 51 53 28 41 50 35 46 33 37 44 52 30 48 40 49 29 36 43 54 * 15 4 25 19 9 1 26 16 5 11 23 8 12 7 17 0 22 3 10 14 6 20 27 24 */ /* * Implementation details * ---------------------- * * If you look at the code in this module, you'll find it looks * nothing _like_ the above algorithm. Here I explain the * differences... * * Key setup has not been heavily optimised here. We are not * concerned with key agility: we aren't codebreakers. We don't * mind a little delay (and it really is a little one; it may be a * factor of five or so slower than it could be but it's still not * an appreciable length of time) while setting up. The only tweaks * in the key setup are ones which change the format of the key * schedule to speed up the actual encryption. I'll describe those * below. * * The first and most obvious optimisation is the S-boxes. Since * each S-box always targets the same four bits in the final 32-bit * word, so the output from (for example) S-box 0 must always be * shifted left 28 bits, we can store the already-shifted outputs * in the lookup tables. This reduces lookup-and-shift to lookup, * so the S-box step is now just a question of ORing together eight * table lookups. * * The permutation P is just a bit order change; it's invariant * with respect to OR, in that P(x)|P(y) = P(x|y). Therefore, we * can apply P to every entry of the S-box tables and then we don't * have to do it in the code of f(). This yields a set of tables * which might be called SP-boxes. * * The bit-selection function E is our next target. Note that E is * immediately followed by the operation of splitting into 6-bit * chunks. Examining the 6-bit chunks coming out of E we notice * they're all contiguous within the word (speaking cyclically - * the end two wrap round); so we can extract those bit strings * individually rather than explicitly running E. This would yield * code such as * * y |= SPboxes[0][ (rotl(R, 5) ^ top6bitsofK) & 0x3F ]; * t |= SPboxes[1][ (rotl(R,11) ^ next6bitsofK) & 0x3F ]; * * and so on; and the key schedule preparation would have to * provide each 6-bit chunk separately. * * Really we'd like to XOR in the key schedule element before * looking up bit strings in R. This we can't do, naively, because * the 6-bit strings we want overlap. But look at the strings: * * 3322222222221111111111 * bit 10987654321098765432109876543210 * * box0 XXXXX X * box1 XXXXXX * box2 XXXXXX * box3 XXXXXX * box4 XXXXXX * box5 XXXXXX * box6 XXXXXX * box7 X XXXXX * * The bit strings we need to XOR in for boxes 0, 2, 4 and 6 don't * overlap with each other. Neither do the ones for boxes 1, 3, 5 * and 7. So we could provide the key schedule in the form of two * words that we can separately XOR into R, and then every S-box * index is available as a (cyclically) contiguous 6-bit substring * of one or the other of the results. * * The comments in Eric Young's libdes implementation point out * that two of these bit strings require a rotation (rather than a * simple shift) to extract. It's unavoidable that at least _one_ * must do; but we can actually run the whole inner algorithm (all * 16 rounds) rotated one bit to the left, so that what the `real' * DES description sees as L=0x80000001 we see as L=0x00000003. * This requires rotating all our SP-box entries one bit to the * left, and rotating each word of the key schedule elements one to * the left, and rotating L and R one bit left just after IP and * one bit right again just before FP. And in each round we convert * a rotate into a shift, so we've saved a few per cent. * * That's about it for the inner loop; the SP-box tables as listed * below are what I've described here (the original S value, * shifted to its final place in the input to P, run through P, and * then rotated one bit left). All that remains is to optimise the * initial permutation IP. * * IP is not an arbitrary permutation. It has the nice property * that if you take any bit number, write it in binary (6 bits), * permute those 6 bits and invert some of them, you get the final * position of that bit. Specifically, the bit whose initial * position is given (in binary) as fedcba ends up in position * AcbFED (where a capital letter denotes the inverse of a bit). * * We have the 64-bit data in two 32-bit words L and R, where bits * in L are those with f=1 and bits in R are those with f=0. We * note that we can do a simple transformation: suppose we exchange * the bits with f=1,c=0 and the bits with f=0,c=1. This will cause * the bit fedcba to be in position cedfba - we've `swapped' bits c * and f in the position of each bit! * * Better still, this transformation is easy. In the example above, * bits in L with c=0 are bits 0x0F0F0F0F, and those in R with c=1 * are 0xF0F0F0F0. So we can do * * difference = ((R >> 4) ^ L) & 0x0F0F0F0F * R ^= (difference << 4) * L ^= difference * * to perform the swap. Let's denote this by bitswap(4,0x0F0F0F0F). * Also, we can invert the bit at the top just by exchanging L and * R. So in a few swaps and a few of these bit operations we can * do: * * Initially the position of bit fedcba is fedcba * Swap L with R to make it Fedcba * Perform bitswap( 4,0x0F0F0F0F) to make it cedFba * Perform bitswap(16,0x0000FFFF) to make it ecdFba * Swap L with R to make it EcdFba * Perform bitswap( 2,0x33333333) to make it bcdFEa * Perform bitswap( 8,0x00FF00FF) to make it dcbFEa * Swap L with R to make it DcbFEa * Perform bitswap( 1,0x55555555) to make it acbFED * Swap L with R to make it AcbFED * * (In the actual code the four swaps are implicit: R and L are * simply used the other way round in the first, second and last * bitswap operations.) * * The final permutation is just the inverse of IP, so it can be * performed by a similar set of operations. */ struct des_context { uint32_t k0246[16], k1357[16]; }; #define rotl(x, c) ( (x << c) | (x >> (32-c)) ) #define rotl28(x, c) ( ( (x << c) | (x >> (28-c)) ) & 0x0FFFFFFF) static uint32_t bitsel(uint32_t * input, const int *bitnums, int size) { uint32_t ret = 0; while (size--) { int bitpos = *bitnums++; ret <<= 1; if (bitpos >= 0) ret |= 1 & (input[bitpos / 32] >> (bitpos % 32)); } return ret; } static inline void des_key_setup(uint32_t key_msw, uint32_t key_lsw, struct des_context *sched) { /* Tables are modified to work with 56-bit key */ static const int PC1_Cbits[] = { 6, 13, 20, 27, 34, 41, 48, 55, 5, 12, 19, 26, 33, 40, 47, 54, 4, 11, 18, 25, 32, 39, 46, 53, 3, 10, 17, 24 }; static const int PC1_Dbits[] = { 0, 7, 14, 21, 28, 35, 42, 49, 1, 8, 15, 22, 29, 36, 43, 50, 2, 9, 16, 23, 30, 37, 44, 51, 31, 38, 45, 52 }; /* * The bit numbers in the two lists below don't correspond to * the ones in the above description of PC2, because in the * above description C and D are concatenated so `bit 28' means * bit 0 of C. In this implementation we're using the standard * `bitsel' function above and C is in the second word, so bit * 0 of C is addressed by writing `32' here. */ static const int PC2_0246[] = { 49, 36, 59, 55, -1, -1, 37, 41, 48, 56, 34, 52, -1, -1, 15, 4, 25, 19, 9, 1, -1, -1, 12, 7, 17, 0, 22, 3, -1, -1, 46, 43 }; static const int PC2_1357[] = { -1, -1, 57, 32, 45, 54, 39, 50, -1, -1, 44, 53, 33, 40, 47, 58, -1, -1, 26, 16, 5, 11, 23, 8, -1, -1, 10, 14, 6, 20, 27, 24 }; static const int leftshifts[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; uint32_t C, D; uint32_t buf[2]; int i; buf[0] = key_lsw; buf[1] = key_msw; C = bitsel(buf, PC1_Cbits, 28); D = bitsel(buf, PC1_Dbits, 28); for (i = 0; i < 16; i++) { C = rotl28(C, leftshifts[i]); D = rotl28(D, leftshifts[i]); buf[0] = D; buf[1] = C; sched->k0246[i] = bitsel(buf, PC2_0246, 32); sched->k1357[i] = bitsel(buf, PC2_1357, 32); } } static const uint32_t SPboxes[8][64] = { {0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004}, {0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000}, {0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200}, {0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080}, {0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100}, {0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010}, {0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002}, {0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000} }; #define f(R, K0246, K1357) (\ s0246 = R ^ K0246, \ s1357 = R ^ K1357, \ s0246 = rotl(s0246, 28), \ SPboxes[0] [(s0246 >> 24) & 0x3F] | \ SPboxes[1] [(s1357 >> 24) & 0x3F] | \ SPboxes[2] [(s0246 >> 16) & 0x3F] | \ SPboxes[3] [(s1357 >> 16) & 0x3F] | \ SPboxes[4] [(s0246 >> 8) & 0x3F] | \ SPboxes[5] [(s1357 >> 8) & 0x3F] | \ SPboxes[6] [(s0246 ) & 0x3F] | \ SPboxes[7] [(s1357 ) & 0x3F]) #define bitswap(L, R, n, mask) (\ swap = mask & ( (R >> n) ^ L ), \ R ^= swap << n, \ L ^= swap) /* Initial permutation */ #define IP(L, R) (\ bitswap(R, L, 4, 0x0F0F0F0F), \ bitswap(R, L, 16, 0x0000FFFF), \ bitswap(L, R, 2, 0x33333333), \ bitswap(L, R, 8, 0x00FF00FF), \ bitswap(R, L, 1, 0x55555555)) /* Final permutation */ #define FP(L, R) (\ bitswap(R, L, 1, 0x55555555), \ bitswap(L, R, 8, 0x00FF00FF), \ bitswap(L, R, 2, 0x33333333), \ bitswap(R, L, 16, 0x0000FFFF), \ bitswap(R, L, 4, 0x0F0F0F0F)) static void des_encipher(uint32_t *output, uint32_t L, uint32_t R, struct des_context *sched) { uint32_t swap, s0246, s1357; IP(L, R); L = rotl(L, 1); R = rotl(R, 1); L ^= f(R, sched->k0246[0], sched->k1357[0]); R ^= f(L, sched->k0246[1], sched->k1357[1]); L ^= f(R, sched->k0246[2], sched->k1357[2]); R ^= f(L, sched->k0246[3], sched->k1357[3]); L ^= f(R, sched->k0246[4], sched->k1357[4]); R ^= f(L, sched->k0246[5], sched->k1357[5]); L ^= f(R, sched->k0246[6], sched->k1357[6]); R ^= f(L, sched->k0246[7], sched->k1357[7]); L ^= f(R, sched->k0246[8], sched->k1357[8]); R ^= f(L, sched->k0246[9], sched->k1357[9]); L ^= f(R, sched->k0246[10], sched->k1357[10]); R ^= f(L, sched->k0246[11], sched->k1357[11]); L ^= f(R, sched->k0246[12], sched->k1357[12]); R ^= f(L, sched->k0246[13], sched->k1357[13]); L ^= f(R, sched->k0246[14], sched->k1357[14]); R ^= f(L, sched->k0246[15], sched->k1357[15]); L = rotl(L, 31); R = rotl(R, 31); swap = L; L = R; R = swap; FP(L, R); output[0] = L; output[1] = R; } #define GET_32BIT_MSB_FIRST(cp) \ ((unsigned long) be32_to_cpu_unaligned(cp)) #define PUT_32BIT_MSB_FIRST(cp, value) \ cpu32_to_be_unaligned((value), (cp)) static inline void des_cbc_encrypt(unsigned char *dest, const unsigned char *src, struct des_context *sched) { uint32_t out[2], L, R; L = GET_32BIT_MSB_FIRST(src); R = GET_32BIT_MSB_FIRST(src + 4); des_encipher(out, L, R, sched); PUT_32BIT_MSB_FIRST(dest, out[0]); PUT_32BIT_MSB_FIRST(dest + 4, out[1]); } void deshash(unsigned char *dst, const unsigned char *key, const unsigned char *src) { struct des_context ctx; des_key_setup(GET_32BIT_MSB_FIRST(key) >> 8, GET_32BIT_MSB_FIRST(key + 3), &ctx); des_cbc_encrypt(dst, src, &ctx); } dovecot-2.2.33.2/src/lib-ntlm/ntlm-encrypt.c0000644000175000017500000000665013165463624015453 00000000000000/* * NTLM and NTLMv2 hash generation. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #include "lib.h" #include "buffer.h" #include "compat.h" #include "safe-memset.h" #include "md4.h" #include "md5.h" #include "hmac.h" #include "ntlm.h" #include "ntlm-des.h" #include static unsigned char * t_unicode_str(const char *src, bool ucase, size_t *size) { buffer_t *wstr; wstr = buffer_create_dynamic(unsafe_data_stack_pool, 32); for ( ; *src; src++) { buffer_append_c(wstr, ucase ? i_toupper(*src) : *src); buffer_append_c(wstr, '\0'); } *size = buffer_get_used_size(wstr); return buffer_free_without_data(&wstr); } void lm_hash(const char *passwd, unsigned char hash[LM_HASH_SIZE]) { static const unsigned char lm_magic[8] = "KGS!@#$%"; unsigned char buffer[14]; unsigned int i; strncpy((char *)buffer, passwd, sizeof(buffer)); for (i = 0; i < sizeof(buffer); i++) buffer[i] = i_toupper(buffer[i]); deshash(hash, buffer, lm_magic); deshash(hash + 8, buffer + 7, lm_magic); safe_memset(buffer, 0, sizeof(buffer)); } void ntlm_v1_hash(const char *passwd, unsigned char hash[NTLMSSP_HASH_SIZE]) { size_t len; void *wpwd = t_unicode_str(passwd, 0, &len); md4_get_digest(wpwd, len, hash); safe_memset(wpwd, 0, len); } static void hmac_md5_ucs2le_string_ucase(struct hmac_context *ctx, const char *str) { size_t len; unsigned char *wstr = t_unicode_str(str, 1, &len); hmac_update(ctx, wstr, len); } static void ATTR_NULL(2) ntlm_v2_hash(const char *user, const char *target, const unsigned char *hash_v1, unsigned char hash[NTLMSSP_V2_HASH_SIZE]) { struct hmac_context ctx; hmac_init(&ctx, hash_v1, NTLMSSP_HASH_SIZE, &hash_method_md5); hmac_md5_ucs2le_string_ucase(&ctx, user); if (target != NULL) hmac_md5_ucs2le_string_ucase(&ctx, target); hmac_final(&ctx, hash); } void ntlmssp_v1_response(const unsigned char *hash, const unsigned char *challenge, unsigned char response[NTLMSSP_RESPONSE_SIZE]) { unsigned char des_hash[NTLMSSP_DES_KEY_LENGTH * 3]; memcpy(des_hash, hash, NTLMSSP_HASH_SIZE); memset(des_hash + NTLMSSP_HASH_SIZE, 0, sizeof(des_hash) - NTLMSSP_HASH_SIZE); deshash(response, des_hash, challenge); deshash(response + 8, des_hash + 7, challenge); deshash(response + 16, des_hash + 14, challenge); safe_memset(des_hash, 0, sizeof(des_hash)); } void ntlmssp2_response(const unsigned char *hash, const unsigned char *server_challenge, const unsigned char *client_challenge, unsigned char response[NTLMSSP_RESPONSE_SIZE]) { struct md5_context ctx; unsigned char session_hash[MD5_RESULTLEN]; md5_init(&ctx); md5_update(&ctx, server_challenge, NTLMSSP_CHALLENGE_SIZE); md5_update(&ctx, client_challenge, NTLMSSP_CHALLENGE_SIZE); md5_final(&ctx, session_hash); ntlmssp_v1_response(hash, session_hash, response); } void ntlmssp_v2_response(const char *user, const char *target, const unsigned char *hash_v1, const unsigned char *challenge, const unsigned char *blob, size_t blob_size, unsigned char response[NTLMSSP_V2_RESPONSE_SIZE]) { struct hmac_context ctx; unsigned char hash[NTLMSSP_V2_HASH_SIZE]; ntlm_v2_hash(user, target, hash_v1, hash); hmac_init(&ctx, hash, NTLMSSP_V2_HASH_SIZE, &hash_method_md5); hmac_update(&ctx, challenge, NTLMSSP_CHALLENGE_SIZE); hmac_update(&ctx, blob, blob_size); hmac_final(&ctx, response); safe_memset(hash, 0, sizeof(hash)); } dovecot-2.2.33.2/src/lib-ntlm/ntlm-types.h0000644000175000017500000000715513123174404015126 00000000000000/* * NTLM data structures. * * Copyright (c) 2004 Andrey Panin * * This software is released under the MIT license. */ #ifndef NTLM_TYPES_H #define NTLM_TYPES_H #define NTLMSSP_MAGIC 0x005053534d4c544eULL #define NTLMSSP_MSG_TYPE1 1 #define NTLMSSP_MSG_TYPE2 2 #define NTLMSSP_MSG_TYPE3 3 #define NTLMSSP_DES_KEY_LENGTH 7 #define NTLMSSP_CHALLENGE_SIZE 8 #define LM_HASH_SIZE 16 #define LM_RESPONSE_SIZE 24 #define NTLMSSP_HASH_SIZE 16 #define NTLMSSP_RESPONSE_SIZE 24 #define NTLMSSP_V2_HASH_SIZE 16 #define NTLMSSP_V2_RESPONSE_SIZE 16 typedef uint16_t ucs2le_t; struct ntlmssp_buffer { uint16_t length; /* length of the buffer */ uint16_t space; /* space allocated space for buffer */ uint32_t offset; /* data offset from the start of the message */ }; typedef struct ntlmssp_buffer ntlmssp_buffer_t; /* * */ struct ntlmssp_message { uint64_t magic; /* NTLMSSP\0 */ uint32_t type; /* Should be 1 */ }; /* * Type 1 message, client sends it to start NTLM authentication sequence. */ struct ntlmssp_request { uint64_t magic; /* NTLMSSP\0 */ uint32_t type; /* Should be 1 */ uint32_t flags; /* Flags */ ntlmssp_buffer_t domain; /* Domain name (optional) */ ntlmssp_buffer_t workstation; /* Workstation name (optional) */ /* Start of the data block */ }; /* * The Type 2 message is sent by the server to the client in response to * the client's Type 1 message. It serves to complete the negotiation of * options with the client, and also provides a challenge to the client. */ struct ntlmssp_challenge { uint64_t magic; /* NTLMSSP\0 */ uint32_t type; /* Should be 2 */ ntlmssp_buffer_t target_name; /* Name of authentication target */ uint32_t flags; /* Flags */ uint8_t challenge[NTLMSSP_CHALLENGE_SIZE]; /* Server challenge */ uint32_t context[2]; /* Local authentication context handle */ ntlmssp_buffer_t target_info; /* Target information block (for NTLMv2) */ /* Start of the data block */ }; /* * The Type 3 message is the final step in authentication. This message * contains the client's responses to the Type 2 challenge, which demonstrate * that the client has knowledge of the account password without sending the * password directly. The Type 3 message also indicates the domain and username * of the authenticating account, as well as the client workstation name. */ struct ntlmssp_response { uint64_t magic; /* NTLMSSP\0 */ uint32_t type; /* Should be 3 */ ntlmssp_buffer_t lm_response; /* LM/LMv2 recponse */ ntlmssp_buffer_t ntlm_response; /* NTLM/NTLMv2 recponse */ ntlmssp_buffer_t domain; /* Domain name */ ntlmssp_buffer_t user; /* User name */ ntlmssp_buffer_t workstation; /* Workstation name */ ntlmssp_buffer_t session_key; /* Session key (optional */ uint32_t flags; /* Flags (optional) */ /* Start of the data block */ }; /* * NTLMv2 Target Information Block item. */ struct ntlmssp_v2_target_info { uint16_t type; /* Data type (see below) */ uint16_t length; /* Length of content field */ /* Content (always in ucs2-le) */ }; /* * NTLMv2 Target Information Block item data type. */ enum { NTPLMSSP_V2_TARGET_END = 0, /* End of list */ NTPLMSSP_V2_TARGET_SERVER, /* NetBIOS server name */ NTPLMSSP_V2_TARGET_DOMAIN, /* NT Domain NetBIOS name */ NTPLMSSP_V2_TARGET_FQDN, /* Fully qualified host name */ NTPLMSSP_V2_TARGET_DNS /* DNS domain name */ }; /* * NTLMv2 Authentication data blob. */ struct ntlmssp_v2_blob { uint32_t magic; /* Should be 0x01010000 */ uint32_t reserved; /* Always 0 */ uint64_t timestamp; /* Timestamp */ uint32_t unknown; /* Unknown something */ /* Target Information Block */ }; #endif dovecot-2.2.33.2/src/lib-ntlm/ntlm.h0000644000175000017500000000155213123174404013757 00000000000000#ifndef NTLM_H #define NTLM_H #include #include "ntlm-types.h" #include "ntlm-flags.h" #include "ntlm-byteorder.h" #include "ntlm-encrypt.h" #include "ntlm-message.h" #define ntlmssp_buffer_data(message, buffer) \ ntlmssp_buffer_data_i((message), &message->buffer) static inline const void * ntlmssp_buffer_data_i(void *message, struct ntlmssp_buffer *buffer) { return ((char *) message) + read_le32(&buffer->offset); } #define ntlmssp_buffer_length(message, buffer) \ ntlmssp_buffer_length_i(&message->buffer) static inline unsigned int ntlmssp_buffer_length_i(struct ntlmssp_buffer *buffer) { return read_le16(&buffer->length); } #define ntlmssp_t_str(message, buffer, unicode) \ ntlmssp_t_str_i((message), &(message)->buffer, (unicode)) const char *ntlmssp_t_str_i(const void *message, struct ntlmssp_buffer *buffer, bool unicode); #endif dovecot-2.2.33.2/src/lib-ntlm/Makefile.am0000644000175000017500000000042013165463624014674 00000000000000noinst_LIBRARIES = libntlm.a AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib libntlm_a_SOURCES = \ ntlm-des.c \ ntlm-encrypt.c \ ntlm-message.c noinst_HEADERS = \ ntlm.h \ ntlm-types.h \ ntlm-flags.h \ ntlm-byteorder.h \ ntlm-des.h \ ntlm-encrypt.h \ ntlm-message.h dovecot-2.2.33.2/src/lib-dovecot/0002755000175000017500000000000013172375611013413 500000000000000dovecot-2.2.33.2/src/lib-dovecot/Makefile.in0000644000175000017500000004524713172375572015420 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-dovecot ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" LTLIBRARIES = $(pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = am_libdovecot_la_OBJECTS = libdovecot_la_OBJECTS = $(am_libdovecot_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdovecot_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libdovecot_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_la_SOURCES) DIST_SOURCES = $(libdovecot_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkglib_LTLIBRARIES = libdovecot.la libdovecot_la_SOURCES = libdovecot_la_LIBADD = \ $(LIBDOVECOT_LA_LIBS) \ $(MODULE_LIBS) libdovecot_la_DEPENDENCIES = $(LIBDOVECOT_LA_LIBS) libdovecot_la_LDFLAGS = -export-dynamic all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-dovecot/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-dovecot/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot.la: $(libdovecot_la_OBJECTS) $(libdovecot_la_DEPENDENCIES) $(EXTRA_libdovecot_la_DEPENDENCIES) $(AM_V_CCLD)$(libdovecot_la_LINK) -rpath $(pkglibdir) $(libdovecot_la_OBJECTS) $(libdovecot_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(pkglibdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-pkglibLTLIBRARIES cscopelist-am ctags-am distclean \ distclean-compile distclean-generic distclean-libtool distdir \ dvi dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkglibLTLIBRARIES install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-dovecot/Makefile.am0000644000175000017500000000033413165463624015371 00000000000000pkglib_LTLIBRARIES = libdovecot.la libdovecot_la_SOURCES = libdovecot_la_LIBADD = \ $(LIBDOVECOT_LA_LIBS) \ $(MODULE_LIBS) libdovecot_la_DEPENDENCIES = $(LIBDOVECOT_LA_LIBS) libdovecot_la_LDFLAGS = -export-dynamic dovecot-2.2.33.2/src/lib-dcrypt/0002755000175000017500000000000013172375610013254 500000000000000dovecot-2.2.33.2/src/lib-dcrypt/Makefile.in0000644000175000017500000014250113172375572015251 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_DCRYPT_OPENSSL_TRUE@am__append_1 = libdcrypt_openssl.la noinst_PROGRAMS = $(am__EXEEXT_1) subdir = src/lib-dcrypt ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) libdcrypt_la_LIBADD = am_libdcrypt_la_OBJECTS = libdcrypt_la-dcrypt.lo \ libdcrypt_la-istream-decrypt.lo \ libdcrypt_la-ostream-encrypt.lo libdcrypt_la_OBJECTS = $(am_libdcrypt_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdcrypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libdcrypt_la_CFLAGS) \ $(CFLAGS) $(libdcrypt_la_LDFLAGS) $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = am__libdcrypt_openssl_la_SOURCES_DIST = dcrypt-openssl.c @BUILD_DCRYPT_OPENSSL_TRUE@am_libdcrypt_openssl_la_OBJECTS = libdcrypt_openssl_la-dcrypt-openssl.lo libdcrypt_openssl_la_OBJECTS = $(am_libdcrypt_openssl_la_OBJECTS) libdcrypt_openssl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) \ $(libdcrypt_openssl_la_LDFLAGS) $(LDFLAGS) -o $@ @BUILD_DCRYPT_OPENSSL_TRUE@am_libdcrypt_openssl_la_rpath = -rpath \ @BUILD_DCRYPT_OPENSSL_TRUE@ $(pkglibdir) am__EXEEXT_1 = test-crypto$(EXEEXT) test-stream$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am__objects_1 = test_crypto-dcrypt.$(OBJEXT) \ test_crypto-istream-decrypt.$(OBJEXT) \ test_crypto-ostream-encrypt.$(OBJEXT) am_test_crypto_OBJECTS = $(am__objects_1) \ test_crypto-test-crypto.$(OBJEXT) test_crypto_OBJECTS = $(am_test_crypto_OBJECTS) am__DEPENDENCIES_2 = $(LIBDOVECOT_TEST_DEPS) $(am__DEPENDENCIES_1) test_crypto_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_crypto_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am__objects_2 = test_stream-dcrypt.$(OBJEXT) \ test_stream-istream-decrypt.$(OBJEXT) \ test_stream-ostream-encrypt.$(OBJEXT) am_test_stream_OBJECTS = $(am__objects_2) \ test_stream-test-stream.$(OBJEXT) test_stream_OBJECTS = $(am_test_stream_OBJECTS) test_stream_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_stream_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdcrypt_la_SOURCES) $(libdcrypt_openssl_la_SOURCES) \ $(test_crypto_SOURCES) $(test_stream_SOURCES) DIST_SOURCES = $(libdcrypt_la_SOURCES) \ $(am__libdcrypt_openssl_la_SOURCES_DIST) \ $(test_crypto_SOURCES) $(test_stream_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libdcrypt.la pkglib_LTLIBRARIES = $(am__append_1) AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" libdcrypt_la_SOURCES = \ dcrypt.c \ istream-decrypt.c \ ostream-encrypt.c libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" libdcrypt_la_LDFLAGS = -export-dynamic @BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c @BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_LDFLAGS = -module -avoid-version ../lib-ssl-iostream/libssl_iostream_openssl.la @BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_LIBADD = $(SSL_LIBS) @BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libssl_iostream_openssl.la @BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ @BUILD_DCRYPT_OPENSSL_TRUE@ $(SSL_CFLAGS) headers = \ dcrypt.h \ dcrypt-iostream.h \ dcrypt-private.h \ ostream-encrypt.h \ istream-decrypt.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) EXTRA_DIST = \ sample-v1.asc \ sample-v1_short.asc \ sample-v2.asc test_programs = test-crypto test-stream LIBDOVECOT_TEST_DEPS = \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-test/libtest.la \ ../lib/liblib.la LIBDOVECOT_TEST = \ $(LIBDOVECOT_TEST_DEPS) \ $(MODULE_LIBS) test_crypto_LDADD = $(LIBDOVECOT_TEST) test_crypto_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) test_crypto_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" test_crypto_SOURCES = $(libdcrypt_la_SOURCES) test-crypto.c test_stream_LDADD = $(LIBDOVECOT_TEST) test_stream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) test_stream_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" test_stream_SOURCES = $(libdcrypt_la_SOURCES) test-stream.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-dcrypt/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-dcrypt/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } uninstall-pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ done clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) @list='$(pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdcrypt.la: $(libdcrypt_la_OBJECTS) $(libdcrypt_la_DEPENDENCIES) $(EXTRA_libdcrypt_la_DEPENDENCIES) $(AM_V_CCLD)$(libdcrypt_la_LINK) $(libdcrypt_la_OBJECTS) $(libdcrypt_la_LIBADD) $(LIBS) libdcrypt_openssl.la: $(libdcrypt_openssl_la_OBJECTS) $(libdcrypt_openssl_la_DEPENDENCIES) $(EXTRA_libdcrypt_openssl_la_DEPENDENCIES) $(AM_V_CCLD)$(libdcrypt_openssl_la_LINK) $(am_libdcrypt_openssl_la_rpath) $(libdcrypt_openssl_la_OBJECTS) $(libdcrypt_openssl_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-crypto$(EXEEXT): $(test_crypto_OBJECTS) $(test_crypto_DEPENDENCIES) $(EXTRA_test_crypto_DEPENDENCIES) @rm -f test-crypto$(EXEEXT) $(AM_V_CCLD)$(test_crypto_LINK) $(test_crypto_OBJECTS) $(test_crypto_LDADD) $(LIBS) test-stream$(EXEEXT): $(test_stream_OBJECTS) $(test_stream_DEPENDENCIES) $(EXTRA_test_stream_DEPENDENCIES) @rm -f test-stream$(EXEEXT) $(AM_V_CCLD)$(test_stream_LINK) $(test_stream_OBJECTS) $(test_stream_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-dcrypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-istream-decrypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-dcrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-istream-decrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-ostream-encrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-test-crypto.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-dcrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-istream-decrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-ostream-encrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-test-stream.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libdcrypt_la-dcrypt.lo: dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-dcrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-dcrypt.Tpo -c -o libdcrypt_la-dcrypt.lo `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-dcrypt.Tpo $(DEPDIR)/libdcrypt_la-dcrypt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='libdcrypt_la-dcrypt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-dcrypt.lo `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c libdcrypt_la-istream-decrypt.lo: istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-istream-decrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-istream-decrypt.Tpo -c -o libdcrypt_la-istream-decrypt.lo `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-istream-decrypt.Tpo $(DEPDIR)/libdcrypt_la-istream-decrypt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='libdcrypt_la-istream-decrypt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-istream-decrypt.lo `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c libdcrypt_la-ostream-encrypt.lo: ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-ostream-encrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-ostream-encrypt.Tpo -c -o libdcrypt_la-ostream-encrypt.lo `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-ostream-encrypt.Tpo $(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='libdcrypt_la-ostream-encrypt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-ostream-encrypt.lo `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c libdcrypt_openssl_la-dcrypt-openssl.lo: dcrypt-openssl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) -MT libdcrypt_openssl_la-dcrypt-openssl.lo -MD -MP -MF $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Tpo -c -o libdcrypt_openssl_la-dcrypt-openssl.lo `test -f 'dcrypt-openssl.c' || echo '$(srcdir)/'`dcrypt-openssl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Tpo $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt-openssl.c' object='libdcrypt_openssl_la-dcrypt-openssl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_openssl_la-dcrypt-openssl.lo `test -f 'dcrypt-openssl.c' || echo '$(srcdir)/'`dcrypt-openssl.c test_crypto-dcrypt.o: dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-dcrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-dcrypt.Tpo -c -o test_crypto-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-dcrypt.Tpo $(DEPDIR)/test_crypto-dcrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_crypto-dcrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c test_crypto-dcrypt.obj: dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-dcrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-dcrypt.Tpo -c -o test_crypto-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-dcrypt.Tpo $(DEPDIR)/test_crypto-dcrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_crypto-dcrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` test_crypto-istream-decrypt.o: istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-istream-decrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-istream-decrypt.Tpo -c -o test_crypto-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-istream-decrypt.Tpo $(DEPDIR)/test_crypto-istream-decrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_crypto-istream-decrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c test_crypto-istream-decrypt.obj: istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-istream-decrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-istream-decrypt.Tpo -c -o test_crypto-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-istream-decrypt.Tpo $(DEPDIR)/test_crypto-istream-decrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_crypto-istream-decrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` test_crypto-ostream-encrypt.o: ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-ostream-encrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-ostream-encrypt.Tpo -c -o test_crypto-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-ostream-encrypt.Tpo $(DEPDIR)/test_crypto-ostream-encrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_crypto-ostream-encrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c test_crypto-ostream-encrypt.obj: ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-ostream-encrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-ostream-encrypt.Tpo -c -o test_crypto-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-ostream-encrypt.Tpo $(DEPDIR)/test_crypto-ostream-encrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_crypto-ostream-encrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` test_crypto-test-crypto.o: test-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.o -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crypto.c' object='test_crypto-test-crypto.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c test_crypto-test-crypto.obj: test-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.obj -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crypto.c' object='test_crypto-test-crypto.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi` test_stream-dcrypt.o: dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-dcrypt.o -MD -MP -MF $(DEPDIR)/test_stream-dcrypt.Tpo -c -o test_stream-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-dcrypt.Tpo $(DEPDIR)/test_stream-dcrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_stream-dcrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c test_stream-dcrypt.obj: dcrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-dcrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-dcrypt.Tpo -c -o test_stream-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-dcrypt.Tpo $(DEPDIR)/test_stream-dcrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_stream-dcrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` test_stream-istream-decrypt.o: istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-istream-decrypt.o -MD -MP -MF $(DEPDIR)/test_stream-istream-decrypt.Tpo -c -o test_stream-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-istream-decrypt.Tpo $(DEPDIR)/test_stream-istream-decrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_stream-istream-decrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c test_stream-istream-decrypt.obj: istream-decrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-istream-decrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-istream-decrypt.Tpo -c -o test_stream-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-istream-decrypt.Tpo $(DEPDIR)/test_stream-istream-decrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_stream-istream-decrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` test_stream-ostream-encrypt.o: ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-ostream-encrypt.o -MD -MP -MF $(DEPDIR)/test_stream-ostream-encrypt.Tpo -c -o test_stream-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-ostream-encrypt.Tpo $(DEPDIR)/test_stream-ostream-encrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_stream-ostream-encrypt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c test_stream-ostream-encrypt.obj: ostream-encrypt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-ostream-encrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-ostream-encrypt.Tpo -c -o test_stream-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-ostream-encrypt.Tpo $(DEPDIR)/test_stream-ostream-encrypt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_stream-ostream-encrypt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` test_stream-test-stream.o: test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-test-stream.o -MD -MP -MF $(DEPDIR)/test_stream-test-stream.Tpo -c -o test_stream-test-stream.o `test -f 'test-stream.c' || echo '$(srcdir)/'`test-stream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-test-stream.Tpo $(DEPDIR)/test_stream-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stream.c' object='test_stream-test-stream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-test-stream.o `test -f 'test-stream.c' || echo '$(srcdir)/'`test-stream.c test_stream-test-stream.obj: test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-test-stream.obj -MD -MP -MF $(DEPDIR)/test_stream-test-stream.Tpo -c -o test_stream-test-stream.obj `if test -f 'test-stream.c'; then $(CYGPATH_W) 'test-stream.c'; else $(CYGPATH_W) '$(srcdir)/test-stream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-test-stream.Tpo $(DEPDIR)/test_stream-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stream.c' object='test_stream-test-stream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-test-stream.obj `if test -f 'test-stream.c'; then $(CYGPATH_W) 'test-stream.c'; else $(CYGPATH_W) '$(srcdir)/test-stream.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkginc_libHEADERS \ install-pkglibLTLIBRARIES install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES .PRECIOUS: Makefile check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-dcrypt/test-stream.c0000644000175000017500000004105213165463624015615 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "dcrypt.h" #include "dcrypt-iostream.h" #include "ostream.h" #include "ostream-encrypt.h" #include "istream.h" #include "istream-decrypt.h" #include "istream-hash.h" #include "istream-base64.h" #include "randgen.h" #include "hash-method.h" #include "test-common.h" #include "hex-binary.h" #include #include #include static const char key_v1_priv[] = "-----BEGIN PRIVATE KEY-----\n" \ "MIGpAgEAMBAGByqGSM49AgEGBSuBBAAjBIGRMIGOAgEBBEGz2V2VMi/5s+Z+GJh7\n" \ "4WfqZjZUpqqm+NJWojm6BbrZMY+9ZComlTGVcUZ007acFxV93oMmrfmtRUb5ynrb\n" \ "MRFskKFGA0QAAwHrAJc8TvyPzspOoz6UH1C1YRmaUVm8tsLu2d0dYtZeOKJUl52J\n" \ "4o8MKIg+ce4q0mTNFrhj+glKj29ppWti6JGAQA==\n" \ "-----END PRIVATE KEY-----"; static const char key_v1_pub[] = "-----BEGIN PUBLIC KEY-----\n" \ "MFgwEAYHKoZIzj0CAQYFK4EEACMDRAADAesAlzxO/I/Oyk6jPpQfULVhGZpRWby2\n" \ "wu7Z3R1i1l44olSXnYnijwwoiD5x7irSZM0WuGP6CUqPb2mla2LokYBA\n" \ "-----END PUBLIC KEY-----"; static const char key_v2_priv[] = "-----BEGIN PRIVATE KEY-----\n" \ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtuQJA+uboZWVwgHc\n" \ "DciyVdrovAPwlMqshDK3s78IDDuhRANCAAQm0VEdzLB9PtD0HA8JK1zifWnj8M00\n" \ "FQzedfp9SQsWyA8dzs5/NFR5MTe6Xbh/ndKEs1zZH3vZ4FlNrilZc0st\n" \ "-----END PRIVATE KEY-----"; static const char key_v2_pub[] = "-----BEGIN PUBLIC KEY-----\n" \ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJtFRHcywfT7Q9BwPCStc4n1p4/DN\n" \ "NBUM3nX6fUkLFsgPHc7OfzRUeTE3ul24f53ShLNc2R972eBZTa4pWXNLLQ==\n" \ "-----END PUBLIC KEY-----"; static const char test_sample_v1_hash[] = "1d7cc2cc1f1983f76241cc42389911e88590ad58cf9d54cafeb5b198d3723dd1"; static const char test_sample_v1_short_hash[] = "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"; static const char test_sample_v2_hash[] = "2e31218656dd34db65b321688bf418dee4ee785e99eb9c21e0d29b4af27a863e"; static struct dcrypt_keypair test_v1_kp; static struct dcrypt_keypair test_v2_kp; static void test_static_v1_input(void) { ssize_t siz; const struct hash_method *hash = hash_method_lookup("sha256"); unsigned char hash_ctx[hash->context_size]; unsigned char hash_dgst[hash->digest_size]; hash->init(hash_ctx); test_begin("test_static_v1_input"); struct istream *is_1 = i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1.asc", IO_BLOCK_SIZE); struct istream *is_2 = i_stream_create_base64_decoder(is_1); i_stream_unref(&is_1); struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); i_stream_unref(&is_2); struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); i_stream_unref(&is_3); while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } if (is_4->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_4)); test_assert(is_4->stream_errno == 0); i_stream_unref(&is_4); hash->result(hash_ctx, hash_dgst); test_assert(strcmp(test_sample_v1_hash, binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); test_end(); } static void test_static_v1_input_short(void) { ssize_t siz; const struct hash_method *hash = hash_method_lookup("sha256"); unsigned char hash_ctx[hash->context_size]; unsigned char hash_dgst[hash->digest_size]; hash->init(hash_ctx); test_begin("test_static_v1_input_short"); struct istream *is_1 = i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1_short.asc", IO_BLOCK_SIZE); struct istream *is_2 = i_stream_create_base64_decoder(is_1); i_stream_unref(&is_1); struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); i_stream_unref(&is_2); struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); i_stream_unref(&is_3); while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } if (is_4->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_4)); test_assert(is_4->stream_errno == 0); i_stream_unref(&is_4); hash->result(hash_ctx, hash_dgst); test_assert(strcmp(test_sample_v1_short_hash, binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); test_end(); } static void test_static_v2_input(void) { test_begin("test_static_v2_input"); ssize_t amt; const struct hash_method *hash = hash_method_lookup("sha256"); unsigned char hash_ctx[hash->context_size]; unsigned char hash_dgst[hash->digest_size]; hash->init(hash_ctx); struct istream *is_1 = i_stream_create_file(DCRYPT_SRC_DIR"/sample-v2.asc", IO_BLOCK_SIZE); struct istream *is_2 = i_stream_create_base64_decoder(is_1); i_stream_unref(&is_1); struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); i_stream_unref(&is_2); struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); i_stream_unref(&is_3); while((amt = i_stream_read(is_4))>0) { i_stream_skip(is_4, amt); } if (is_4->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_4)); test_assert(is_4->stream_errno == 0); i_stream_unref(&is_4); hash->result(hash_ctx, hash_dgst); test_assert(strcmp(test_sample_v2_hash, binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); test_end(); /** this code is left here to show how the sample file is created struct istream *is = i_stream_create_file("../lib-fts/udhr_fra.txt", 8192); struct istream *is_2 = i_stream_create_hash(is, hash, hash_ctx); int fd = open("sample-v2.bin", O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); struct ostream *os = o_stream_create_fd_file(fd, 0, TRUE); struct ostream *os_2 = o_stream_create_encrypt(os, "aes-256-gcm-sha256", test_v2_kp.pub, IO_STREAM_ENC_INTEGRITY_AEAD); const unsigned char *ptr; size_t siz; while(i_stream_read_data(is_2, &ptr, &siz, 0)>0) { o_stream_nsend(os_2, ptr, siz); i_stream_skip(is_2, siz); } i_assert(o_stream_nfinish(os_2)==0); o_stream_close(os_2); i_stream_close(is_2); hash->result(hash_ctx, hash_dgst); printf("%s\n", binary_to_hex(hash_dgst, sizeof(hash_dgst))); */ } static void test_write_read_v1(void) { test_begin("test_write_read_v1"); unsigned char payload[IO_BLOCK_SIZE]; const unsigned char *ptr; size_t pos = 0, siz; random_fill_weak(payload, IO_BLOCK_SIZE); buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); o_stream_nsend(os_2, payload, sizeof(payload)); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); test_assert(os_2->stream_errno == 0); test_assert(o_stream_nfinish(os_2) == 0); test_assert(os_2->stream_errno == 0); o_stream_unref(&os); o_stream_unref(&os_2); struct istream *is = test_istream_create_data(buf->data, buf->used); struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); size_t offset = 0; test_istream_set_allow_eof(is, FALSE); test_istream_set_size(is, 0); while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); else test_istream_set_size(is, ++offset); test_assert_idx(pos + siz <= sizeof(payload), pos); if (pos + siz > sizeof(payload)) break; test_assert_idx(siz == 0 || memcmp(ptr, payload + pos, siz) == 0, pos); i_stream_skip(is_2, siz); pos += siz; } test_assert(is_2->stream_errno == 0); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static void test_write_read_v1_short(void) { test_begin("test_write_read_v1_short"); unsigned char payload[1]; const unsigned char *ptr; size_t pos = 0, siz; random_fill_weak(payload, 1); buffer_t *buf = buffer_create_dynamic(default_pool, 64); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); o_stream_nsend(os_2, payload, sizeof(payload)); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); test_assert(os_2->stream_errno == 0); test_assert(o_stream_nfinish(os_2) == 0); test_assert(os_2->stream_errno == 0); o_stream_unref(&os); o_stream_unref(&os_2); struct istream *is = test_istream_create_data(buf->data, buf->used); struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); size_t offset = 0; test_istream_set_allow_eof(is, FALSE); test_istream_set_size(is, 0); while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); else test_istream_set_size(is, ++offset); test_assert_idx(pos + siz <= sizeof(payload), pos); if (pos + siz > sizeof(payload)) break; test_assert_idx(siz == 0 || memcmp(ptr, payload + pos, siz) == 0, pos); i_stream_skip(is_2, siz); pos += siz; } test_assert(is_2->stream_errno == 0); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static void test_write_read_v1_empty(void) { const unsigned char *ptr; size_t siz; test_begin("test_write_read_v1_empty"); buffer_t *buf = buffer_create_dynamic(default_pool, 64); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "", test_v1_kp.pub, IO_STREAM_ENC_VERSION_1); test_assert(o_stream_nfinish(os_2) == 0); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); o_stream_unref(&os); o_stream_unref(&os_2); /* this should've been enough */ struct istream *is = test_istream_create_data(buf->data, buf->used); struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); /* read should not fail */ test_istream_set_allow_eof(is, FALSE); test_istream_set_size(is, 0); size_t offset = 0; ssize_t ret; while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { test_assert(ret == 0); if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); else test_istream_set_size(is, ++offset); }; test_assert(is_2->stream_errno == 0); if (is_2->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_2)); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static void test_write_read_v2(void) { test_begin("test_write_read_v2"); unsigned char payload[IO_BLOCK_SIZE*10]; const unsigned char *ptr; size_t pos = 0, siz; random_fill_weak(payload, IO_BLOCK_SIZE*10); buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "aes-256-gcm-sha256", test_v1_kp.pub, IO_STREAM_ENC_INTEGRITY_AEAD); o_stream_nsend(os_2, payload, sizeof(payload)); test_assert(o_stream_nfinish(os_2) == 0); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); o_stream_unref(&os); o_stream_unref(&os_2); struct istream *is = test_istream_create_data(buf->data, buf->used); /* test regression where read fails due to incorrect behaviour when buffer is full before going to decrypt code */ i_stream_set_max_buffer_size(is, 8192); i_stream_read(is); struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); size_t offset = 0; test_istream_set_size(is, 0); test_istream_set_allow_eof(is, FALSE); while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); else test_istream_set_size(is, ++offset); test_assert_idx(pos + siz <= sizeof(payload), pos); if (pos + siz > sizeof(payload)) break; test_assert_idx(siz == 0 || memcmp(ptr, payload + pos, siz) == 0, pos); i_stream_skip(is_2, siz); pos += siz; } test_assert(is_2->stream_errno == 0); if (is_2->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_2)); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static void test_write_read_v2_short(void) { test_begin("test_write_read_v2_short"); unsigned char payload[1]; const unsigned char *ptr; size_t pos = 0, siz; random_fill_weak(payload, 1); buffer_t *buf = buffer_create_dynamic(default_pool, 64); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "aes-256-gcm-sha256", test_v1_kp.pub, IO_STREAM_ENC_INTEGRITY_AEAD); o_stream_nsend(os_2, payload, sizeof(payload)); test_assert(o_stream_nfinish(os_2) == 0); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); o_stream_unref(&os); o_stream_unref(&os_2); struct istream *is = test_istream_create_data(buf->data, buf->used); struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); size_t offset = 0; test_istream_set_allow_eof(is, FALSE); test_istream_set_size(is, 0); while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); test_istream_set_size(is, ++offset); test_assert_idx(pos + siz <= sizeof(payload), pos); if (pos + siz > sizeof(payload)) break; test_assert_idx(siz == 0 || memcmp(ptr, payload + pos, siz) == 0, pos); i_stream_skip(is_2, siz); pos += siz; } test_assert(is_2->stream_errno == 0); if (is_2->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_2)); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static void test_write_read_v2_empty(void) { const unsigned char *ptr; size_t siz; test_begin("test_write_read_v2_empty"); buffer_t *buf = buffer_create_dynamic(default_pool, 64); struct ostream *os = o_stream_create_buffer(buf); struct ostream *os_2 = o_stream_create_encrypt(os, "aes-256-gcm-sha256", test_v1_kp.pub, IO_STREAM_ENC_INTEGRITY_AEAD); test_assert(o_stream_nfinish(os_2) == 0); if (os_2->stream_errno != 0) i_debug("error: %s", o_stream_get_error(os_2)); o_stream_unref(&os); o_stream_unref(&os_2); /* this should've been enough */ struct istream *is = test_istream_create_data(buf->data, buf->used); struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); /* read should not fail */ size_t offset = 0; test_istream_set_allow_eof(is, FALSE); test_istream_set_size(is, 0); ssize_t ret; while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { test_assert(ret == 0); if (offset == buf->used) test_istream_set_allow_eof(is, TRUE); test_istream_set_size(is, ++offset); }; test_assert(is_2->stream_errno == 0); if (is_2->stream_errno != 0) i_debug("error: %s", i_stream_get_error(is_2)); i_stream_unref(&is); i_stream_unref(&is_2); buffer_free(&buf); test_end(); } static int no_op_cb(const char *digest ATTR_UNUSED, struct dcrypt_private_key **priv_key_r ATTR_UNUSED, const char **error_r ATTR_UNUSED, void *context ATTR_UNUSED) { return 0; } static void test_read_0_to_400_byte_garbage(void) { test_begin("test_read_0_to_100_byte_garbage"); char data[512]; memset(data, 0, sizeof(data)); for (size_t s = 0; s <= 400; ++s) { struct istream *is = test_istream_create_data(data, s); struct istream *ds = i_stream_create_decrypt_callback(is, no_op_cb, NULL); test_istream_set_size(is, 0); test_istream_set_allow_eof(is, FALSE); ssize_t siz = 0; for (size_t offset = 0; offset <= s && siz == 0; offset++) { if (offset == s) test_istream_set_allow_eof(is, TRUE); test_istream_set_size(is, offset); siz = i_stream_read(ds); } test_assert_idx(siz < 0, s); i_stream_unref(&ds); i_stream_unref(&is); } test_end(); } static void test_read_large_header(void) { test_begin("test_read_large_header"); struct istream *is = test_istream_create_data(IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)); struct istream *ds = i_stream_create_decrypt_callback(is, no_op_cb, NULL); test_istream_set_allow_eof(is, FALSE); test_istream_set_max_buffer_size(is, sizeof(IOSTREAM_CRYPT_MAGIC)); test_assert(i_stream_read(ds) == -1); i_stream_unref(&ds); i_stream_unref(&is); test_end(); } static void test_free_keys() { dcrypt_key_unref_private(&test_v1_kp.priv); dcrypt_key_unref_public(&test_v1_kp.pub); dcrypt_key_unref_private(&test_v2_kp.priv); dcrypt_key_unref_public(&test_v2_kp.pub); } int main(void) { struct dcrypt_settings set = { .module_dir = ".libs" }; const char *error; if (!dcrypt_initialize(NULL, &set, &error)) { i_error("No functional dcrypt backend found - skipping tests: %s", error); return 0; } random_init(); test_assert(dcrypt_key_load_private(&test_v1_kp.priv, key_v1_priv, NULL, NULL, NULL)); test_assert(dcrypt_key_load_public(&test_v1_kp.pub, key_v1_pub, NULL)); test_assert(dcrypt_key_load_private(&test_v2_kp.priv, key_v2_priv, NULL, NULL, NULL)); test_assert(dcrypt_key_load_public(&test_v2_kp.pub, key_v2_pub, NULL)); static void (*test_functions[])(void) = { test_static_v1_input, test_static_v1_input_short, test_static_v2_input, test_write_read_v1, test_write_read_v1_short, test_write_read_v1_empty, test_write_read_v2, test_write_read_v2_short, test_write_read_v2_empty, test_free_keys, test_read_0_to_400_byte_garbage, test_read_large_header, NULL }; return test_run(test_functions); } dovecot-2.2.33.2/src/lib-dcrypt/sample-v1_short.asc0000644000175000017500000000040013123174404016672 00000000000000Q1JZUFRFRAMHAQCtAEMDAA+5INzRNr5OAicv3XI4jh//ufjZN9yYr7mElHKNGY+D D2ziqHhPKVra6JzBzZvfySntDnDvdLAomafpDVlESMlkACB8mhA56i5P7XPoHdP/ w/oi6kooNSk5rd57+OqFiwD6TwAgu4C6eZWV+Spojlk8eOAw784ySgMHK8gDrXhA Jwg34GcAIPX4RmqXh+7QTAQWtGNHQvjqSygBxSUYkQ3KfPLMymKvAACMLy6/ dovecot-2.2.33.2/src/lib-dcrypt/dcrypt-openssl.c0000644000175000017500000020636413165463624016344 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "hex-binary.h" #include "safe-memset.h" #include "randgen.h" #include "array.h" #include "module-dir.h" #include "dovecot-openssl-common.h" #include #include #include #include #include #include #include #include #include #include #include #include "dcrypt.h" #include "dcrypt-private.h" /** key format documentation: ========================= v1 key ------ algo id = openssl NID enctype = 0 = none, 1 = ecdhe, 2 = password key id = sha256(hex encoded public point) public key ---------- 1algo idpublic point private key ----------- - enctype none 1algo id0private pointkey id - enctype ecdh (algorithm AES-256-CTR, key = SHA256(shared secret), IV = \0\0\0...) 1algo id1private pointephemeral public keyencryption key idkey id - enctype password (algorithm AES-256-CTR, key = PBKDF2(SHA1, 16, password, salt), IV = \0\0\0...) 1algo id2private pointsaltkey id v2 key ------ algo oid = ASN1 OID of key algorithm (RSA or EC curve) enctype = 0 = none, 1 = ecdhe, 2 = password key id = SHA256(i2d_PUBKEY) public key ---------- 2HEX(i2d_PUBKEY)key id - enctype none 2key algo oid0(RSA = i2d_PrivateKey, EC=Private Point)key id - enctype ecdh, key,iv = PBKDF2(hash algo, rounds, shared secret, salt) 2key algo oid1symmetric algo namesalthash algoroundsE(RSA = i2d_PrivateKey, EC=Private Point)ephemeral public keyencryption key idkey id - enctype password, key,iv = PBKDF2(hash algo, rounds, password, salt) 2key algo oid1symmetric algo namesalthash algoroundsE(RSA = i2d_PrivateKey, EC=Private Point)key id **/ #ifndef HAVE_EVP_PKEY_get0 #define EVP_PKEY_get0_EC_KEY(x) x->pkey.ec #define EVP_PKEY_get0_RSA(x) x->pkey.rsa #endif #ifndef HAVE_OBJ_LENGTH #define OBJ_length(o) ((o)->length) #endif #ifndef HAVE_EVP_MD_CTX_NEW # define EVP_MD_CTX_new() EVP_MD_CTX_create() # define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) #endif #ifndef HAVE_HMAC_CTX_NEW # define HMAC_Init_ex(ctx, key, key_len, md, impl) \ HMAC_Init_ex(&(ctx), key, key_len, md, impl) # define HMAC_Update(ctx, data, len) HMAC_Update(&(ctx), data, len) # define HMAC_Final(ctx, md, len) HMAC_Final(&(ctx), md, len) # define HMAC_CTX_free(ctx) HMAC_cleanup(&(ctx)) #else # define HMAC_CTX_free(ctx) \ STMT_START { HMAC_CTX_free(ctx); (ctx) = NULL; } STMT_END #endif struct dcrypt_context_symmetric { pool_t pool; const EVP_CIPHER *cipher; EVP_CIPHER_CTX *ctx; unsigned char *key; unsigned char *iv; unsigned char *aad; size_t aad_len; unsigned char *tag; size_t tag_len; int padding; int mode; }; struct dcrypt_context_hmac { pool_t pool; const EVP_MD *md; #ifdef HAVE_HMAC_CTX_NEW HMAC_CTX *ctx; #else HMAC_CTX ctx; #endif unsigned char *key; size_t klen; }; struct dcrypt_public_key { EVP_PKEY *key; unsigned int ref; }; struct dcrypt_private_key { EVP_PKEY *key; unsigned int ref; }; static bool dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, const char *algorithm, buffer_t *result, const char **error_r); static bool dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, buffer_t *result, const char **error_r); static bool dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, const char *algorithm, buffer_t *result, const char **error_r); static bool dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, buffer_t *result, const char **error_r); static void dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, struct dcrypt_public_key **pub_key_r); static void dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key); static void dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key); static bool dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); static bool dcrypt_openssl_key_string_get_info(const char *key_data, enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, enum dcrypt_key_encryption_type *encryption_type_r, const char **encryption_key_hash_r, const char **key_hash_r, const char **error_r); static bool dcrypt_openssl_error(const char **error_r) { if(error_r == NULL) return FALSE; /* caller is not really interested */ unsigned long ec = ERR_get_error(); *error_r = t_strdup_printf("%s", ERR_error_string(ec, NULL)); return FALSE; } static bool dcrypt_openssl_initialize(const struct dcrypt_settings *set, const char **error_r) { if (set->crypto_device != NULL && set->crypto_device[0] != '\0') { if (dovecot_openssl_common_global_set_engine(set->crypto_device, error_r) <= 0) return FALSE; } return TRUE; } /* legacy function for old formats that generates hex encoded point from EC public key */ static char *ec_key_get_pub_point_hex(const EC_KEY *key) { const EC_POINT *p; const EC_GROUP *g; p = EC_KEY_get0_public_key(key); g = EC_KEY_get0_group(key); return EC_POINT_point2hex(g, p, POINT_CONVERSION_COMPRESSED, NULL); } static bool dcrypt_openssl_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, struct dcrypt_context_symmetric **ctx_r, const char **error_r) { struct dcrypt_context_symmetric *ctx; pool_t pool; const EVP_CIPHER *cipher; cipher = EVP_get_cipherbyname(algorithm); if (cipher == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Invalid cipher %s", algorithm); return FALSE; } /* allocate context */ pool = pool_alloconly_create("dcrypt openssl", 1024); ctx = p_new(pool, struct dcrypt_context_symmetric, 1); ctx->pool = pool; ctx->cipher = cipher; ctx->padding = 1; ctx->mode =( mode == DCRYPT_MODE_ENCRYPT ? 1 : 0 ); *ctx_r = ctx; return TRUE; } static void dcrypt_openssl_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) { pool_t pool = (*ctx)->pool; if ((*ctx)->ctx) EVP_CIPHER_CTX_free((*ctx)->ctx); pool_unref(&pool); *ctx = NULL; } static void dcrypt_openssl_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, const unsigned char *key, size_t key_len) { if(ctx->key != NULL) p_free(ctx->pool, ctx->key); ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); memcpy(ctx->key, key, I_MIN(key_len,(size_t)EVP_CIPHER_key_length(ctx->cipher))); } static void dcrypt_openssl_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, const unsigned char *iv, size_t iv_len) { if(ctx->iv != NULL) p_free(ctx->pool, ctx->iv); ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); memcpy(ctx->iv, iv, I_MIN(iv_len,(size_t)EVP_CIPHER_iv_length(ctx->cipher))); } static void dcrypt_openssl_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) { if(ctx->key != NULL) p_free(ctx->pool, ctx->key); if(ctx->iv != NULL) p_free(ctx->pool, ctx->iv); ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); random_fill(ctx->key, EVP_CIPHER_key_length(ctx->cipher)); ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); random_fill(ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); } static void dcrypt_openssl_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, bool padding) { ctx->padding = (padding?1:0); } static bool dcrypt_openssl_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key) { if(ctx->key == NULL) return FALSE; buffer_append(key, ctx->key, EVP_CIPHER_key_length(ctx->cipher)); return TRUE; } static bool dcrypt_openssl_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv) { if(ctx->iv == NULL) return FALSE; buffer_append(iv, ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); return TRUE; } static void dcrypt_openssl_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, const unsigned char *aad, size_t aad_len) { if (ctx->aad != NULL) p_free(ctx->pool, ctx->aad); /* allow empty aad */ ctx->aad = p_malloc(ctx->pool, I_MAX(1,aad_len)); memcpy(ctx->aad, aad, aad_len); ctx->aad_len = aad_len; } static bool dcrypt_openssl_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, buffer_t *aad) { if (ctx->aad == NULL) return FALSE; buffer_append(aad, ctx->aad, ctx->aad_len); return TRUE; } static void dcrypt_openssl_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, const unsigned char *tag, size_t tag_len) { if (ctx->tag != NULL) p_free(ctx->pool, ctx->tag); /* unlike aad, tag cannot be empty */ ctx->tag = p_malloc(ctx->pool, tag_len); memcpy(ctx->tag, tag, tag_len); ctx->tag_len = tag_len; } static bool dcrypt_openssl_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, buffer_t *tag) { if (ctx->tag == NULL) return FALSE; buffer_append(tag, ctx->tag, ctx->tag_len); return TRUE; } static unsigned int dcrypt_openssl_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) { return EVP_CIPHER_key_length(ctx->cipher); } static unsigned int dcrypt_openssl_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) { return EVP_CIPHER_iv_length(ctx->cipher); } static unsigned int dcrypt_openssl_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) { return EVP_CIPHER_block_size(ctx->cipher); } static bool dcrypt_openssl_ctx_sym_init(struct dcrypt_context_symmetric *ctx, const char **error_r) { int ec; int len; i_assert(ctx->key != NULL); i_assert(ctx->iv != NULL); i_assert(ctx->ctx == NULL); if((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) return dcrypt_openssl_error(error_r); ec = EVP_CipherInit_ex(ctx->ctx, ctx->cipher, NULL, ctx->key, ctx->iv, ctx->mode); if (ec != 1) return dcrypt_openssl_error(error_r); EVP_CIPHER_CTX_set_padding(ctx->ctx, ctx->padding); len = 0; if (ctx->aad != NULL) ec = EVP_CipherUpdate(ctx->ctx, NULL, &len, ctx->aad, ctx->aad_len); if (ec != 1) return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_ctx_sym_update(struct dcrypt_context_symmetric *ctx, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); size_t buf_used = result->used; unsigned char *buf; int outl; i_assert(ctx->ctx != NULL); /* From `man 3 evp_cipherupdate`: EVP_EncryptUpdate() encrypts inl bytes from the buffer in and writes the encrypted version to out. This function can be called multiple times to encrypt successive blocks of data. The amount of data written depends on the block alignment of the encrypted data: as a result the amount of data written may be anything from zero bytes to (inl + cipher_block_size - 1) so out should contain sufficient room. The actual number of bytes written is placed in outl. */ buf = buffer_append_space_unsafe(result, data_len + block_size); outl = 0; if (EVP_CipherUpdate (ctx->ctx, buf, &outl, data, data_len) != 1) return dcrypt_openssl_error(error_r); buffer_set_used_size(result, buf_used + outl); return TRUE; } static bool dcrypt_openssl_ctx_sym_final(struct dcrypt_context_symmetric *ctx, buffer_t *result, const char **error_r) { const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); size_t buf_used = result->used; unsigned char *buf; int outl; int ec; i_assert(ctx->ctx != NULL); /* From `man 3 evp_cipherupdate`: If padding is enabled (the default) then EVP_EncryptFinal_ex() encrypts the "final" data, that is any data that remains in a partial block. It uses standard block padding (aka PKCS padding). The encrypted final data is written to out which should have sufficient space for one cipher block. The number of bytes written is placed in outl. After this function is called the encryption operation is finished and no further calls to EVP_EncryptUpdate() should be made. */ buf = buffer_append_space_unsafe(result, block_size); outl = 0; /* when **DECRYPTING** set expected tag */ if (ctx->mode == 0 && ctx->tag != NULL) { ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_TAG, ctx->tag_len, ctx->tag); } else ec = 1; if (ec == 1) ec = EVP_CipherFinal_ex(ctx->ctx, buf, &outl); if (ec == 1) { buffer_set_used_size(result, buf_used + outl); /* when **ENCRYPTING** recover tag */ if (ctx->mode == 1 && ctx->aad != NULL) { /* tag should be NULL here */ i_assert(ctx->tag == NULL); /* openssl claims taglen is always 16, go figure .. */ ctx->tag = p_malloc(ctx->pool, EVP_GCM_TLS_TAG_LEN); ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_GET_TAG, EVP_GCM_TLS_TAG_LEN, ctx->tag); ctx->tag_len = EVP_GCM_TLS_TAG_LEN; } } if (ec == 0 && error_r != NULL) *error_r = "data authentication failed"; else if (ec < 0) dcrypt_openssl_error(error_r); EVP_CIPHER_CTX_free(ctx->ctx); ctx->ctx = NULL; return ec == 1; } static bool dcrypt_openssl_ctx_hmac_create(const char *algorithm, struct dcrypt_context_hmac **ctx_r, const char **error_r) { struct dcrypt_context_hmac *ctx; pool_t pool; const EVP_MD *md; md = EVP_get_digestbyname(algorithm); if(md == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Invalid digest %s", algorithm); return FALSE; } /* allocate context */ pool = pool_alloconly_create("dcrypt openssl", 1024); ctx = p_new(pool, struct dcrypt_context_hmac, 1); ctx->pool = pool; ctx->md = md; *ctx_r = ctx; return TRUE; } static void dcrypt_openssl_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) { pool_t pool = (*ctx)->pool; HMAC_CTX_free((*ctx)->ctx); pool_unref(&pool); *ctx = NULL; } static void dcrypt_openssl_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, const unsigned char *key, size_t key_len) { if(ctx->key != NULL) p_free(ctx->pool, ctx->key); ctx->klen = I_MIN(key_len, HMAC_MAX_MD_CBLOCK); ctx->key = p_malloc(ctx->pool, ctx->klen); memcpy(ctx->key, key, ctx->klen); } static bool dcrypt_openssl_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) { if(ctx->key == NULL) return FALSE; buffer_append(key, ctx->key, ctx->klen); return TRUE; } static void dcrypt_openssl_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) { ctx->klen = HMAC_MAX_MD_CBLOCK; ctx->key = p_malloc(ctx->pool, ctx->klen); random_fill(ctx->key, ctx->klen); } static unsigned int dcrypt_openssl_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) { return EVP_MD_size(ctx->md); } static bool dcrypt_openssl_ctx_hmac_init(struct dcrypt_context_hmac *ctx, const char **error_r) { int ec; i_assert(ctx->md != NULL); #ifdef HAVE_HMAC_CTX_NEW ctx->ctx = HMAC_CTX_new(); if (ctx->ctx == NULL) return dcrypt_openssl_error(error_r); #endif ec = HMAC_Init_ex(ctx->ctx, ctx->key, ctx->klen, ctx->md, NULL); if (ec != 1) return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_ctx_hmac_update(struct dcrypt_context_hmac *ctx, const unsigned char *data, size_t data_len, const char **error_r) { int ec; ec = HMAC_Update(ctx->ctx, data, data_len); if (ec != 1) return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, const char **error_r) { int ec; unsigned char buf[HMAC_MAX_MD_CBLOCK]; unsigned int outl; ec = HMAC_Final(ctx->ctx, buf, &outl); HMAC_CTX_free(ctx->ctx); if (ec == 1) { buffer_append(result, buf, outl); } else return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_generate_ec_key(int nid, EVP_PKEY **key, const char **error_r) { EVP_PKEY_CTX *pctx; EVP_PKEY_CTX *ctx; EVP_PKEY *params = NULL; /* generate parameters for EC */ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); if (pctx == NULL || EVP_PKEY_paramgen_init(pctx) < 1 || EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) < 1 || EVP_PKEY_paramgen(pctx, ¶ms) < 1) { dcrypt_openssl_error(error_r); EVP_PKEY_CTX_free(pctx); return FALSE; } /* generate key from parameters */ ctx = EVP_PKEY_CTX_new(params, NULL); if (ctx == NULL || EVP_PKEY_keygen_init(ctx) < 1 || EVP_PKEY_keygen(ctx, key) < 1) { dcrypt_openssl_error(error_r); EVP_PKEY_free(params); EVP_PKEY_CTX_free(pctx); EVP_PKEY_CTX_free(ctx); return FALSE; } EVP_PKEY_free(params); EVP_PKEY_CTX_free(pctx); EVP_PKEY_CTX_free(ctx); EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY((*key)), OPENSSL_EC_NAMED_CURVE); EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY((*key)), POINT_CONVERSION_COMPRESSED); return TRUE; } static bool dcrypt_openssl_generate_rsa_key(int bits, EVP_PKEY **key, const char **error_r) { i_assert(bits >= 256); int ec = 0; EVP_PKEY_CTX *ctx; ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (ctx == NULL || EVP_PKEY_keygen_init(ctx) < 1 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) < 1 || EVP_PKEY_keygen(ctx, key) < 1) { dcrypt_openssl_error(error_r); ec = -1; } EVP_PKEY_CTX_free(ctx); return ec == 0; } static bool dcrypt_openssl_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, buffer_t *R, buffer_t *S, const char **error_r) { i_assert(local_key != NULL && local_key->key != NULL); EVP_PKEY *local = local_key->key; BN_CTX *bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) return dcrypt_openssl_error(error_r); const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); EC_POINT *pub = EC_POINT_new(grp); /* convert ephemeral key data EC point */ if (pub == NULL || EC_POINT_oct2point(grp, pub, R->data, R->used, bn_ctx) != 1) { EC_POINT_free(pub); BN_CTX_free(bn_ctx); return dcrypt_openssl_error(error_r); } EC_KEY *ec_key = EC_KEY_new(); /* convert point to public key */ int ec = 0; if (ec_key == NULL || EC_KEY_set_group(ec_key, grp) != 1 || EC_KEY_set_public_key(ec_key, pub) != 1) ec = -1; else EC_KEY_set_conv_form(ec_key, POINT_CONVERSION_COMPRESSED); EC_POINT_free(pub); BN_CTX_free(bn_ctx); /* make sure it looks like a valid key */ if (ec == -1 || EC_KEY_check_key(ec_key) != 1) { EC_KEY_free(ec_key); return dcrypt_openssl_error(error_r); } EVP_PKEY *peer = EVP_PKEY_new(); if (peer == NULL) { EC_KEY_free(ec_key); return dcrypt_openssl_error(error_r); } EVP_PKEY_set1_EC_KEY(peer, ec_key); EC_KEY_free(ec_key); EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(local, NULL); /* initialize derivation */ if (pctx == NULL || EVP_PKEY_derive_init(pctx) != 1 || EVP_PKEY_derive_set_peer(pctx, peer) != 1) { EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(peer); return dcrypt_openssl_error(error_r); } /* have to do it twice to get the data length */ size_t len; if (EVP_PKEY_derive(pctx, NULL, &len) != 1) { EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(peer); return dcrypt_openssl_error(error_r); } unsigned char buf[len]; memset(buf,0,len); if (EVP_PKEY_derive(pctx, buf, &len) != 1) { EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(peer); return dcrypt_openssl_error(error_r); } EVP_PKEY_CTX_free(pctx); buffer_append(S, buf, len); EVP_PKEY_free(peer); return TRUE; } static bool dcrypt_openssl_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, buffer_t *R, buffer_t *S, const char **error_r) { i_assert(peer_key != NULL && peer_key->key != NULL); /* ensure peer_key is EC key */ EVP_PKEY *local = NULL; EVP_PKEY *peer = peer_key->key; if (EVP_PKEY_base_id(peer) != EVP_PKEY_EC) { if (error_r != NULL) *error_r = "Only ECC key can be used"; return FALSE; } /* generate another key from same group */ int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(peer))); if (!dcrypt_openssl_generate_ec_key(nid, &local, error_r)) return FALSE; /* initialize */ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(local, NULL); if (pctx == NULL || EVP_PKEY_derive_init(pctx) != 1 || EVP_PKEY_derive_set_peer(pctx, peer) != 1) { EVP_PKEY_CTX_free(pctx); return dcrypt_openssl_error(error_r); } /* derive */ size_t len; if (EVP_PKEY_derive(pctx, NULL, &len) != 1) { EVP_PKEY_CTX_free(pctx); return dcrypt_openssl_error(error_r); } unsigned char buf[len]; if (EVP_PKEY_derive(pctx, buf, &len) != 1) { EVP_PKEY_CTX_free(pctx); return dcrypt_openssl_error(error_r); } EVP_PKEY_CTX_free(pctx); buffer_append(S, buf, len); /* get ephemeral key (=R) */ BN_CTX *bn_ctx = BN_CTX_new(); const EC_POINT *pub = EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(local)); const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); len = EC_POINT_point2oct(grp, pub, POINT_CONVERSION_COMPRESSED, NULL, 0, bn_ctx); unsigned char R_buf[len]; EC_POINT_point2oct(grp, pub, POINT_CONVERSION_COMPRESSED, R_buf, len, bn_ctx); BN_CTX_free(bn_ctx); buffer_append(R, R_buf, len); EVP_PKEY_free(local); return TRUE; } static bool dcrypt_openssl_pbkdf2(const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, const char *hash, unsigned int rounds, buffer_t *result, unsigned int result_len, const char **error_r) { int ret; i_assert(rounds > 0); i_assert(result_len > 0); i_assert(result != NULL); /* determine MD */ const EVP_MD* md = EVP_get_digestbyname(hash); if (md == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Invalid digest %s", hash); return FALSE; } unsigned char buffer[result_len]; if ((ret = PKCS5_PBKDF2_HMAC((const char*)password, password_len, salt, salt_len, rounds, md, result_len, buffer)) == 1) { buffer_append(result, buffer, result_len); } if (ret != 1) return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_generate_keypair(struct dcrypt_keypair *pair_r, enum dcrypt_key_type kind, unsigned int bits, const char *curve, const char **error_r) { EVP_PKEY *pkey = NULL; i_assert(pair_r != NULL); memset(pair_r, 0, sizeof(struct dcrypt_keypair)); if (kind == DCRYPT_KEY_RSA) { if (dcrypt_openssl_generate_rsa_key(bits, &pkey, error_r)) { pair_r->priv = i_new(struct dcrypt_private_key, 1); pair_r->priv->key = pkey; pair_r->priv->ref++; pair_r->pub = NULL; dcrypt_openssl_private_to_public_key(pair_r->priv, &(pair_r->pub)); return TRUE; } else return dcrypt_openssl_error(error_r); } else if (kind == DCRYPT_KEY_EC) { int nid = OBJ_sn2nid(curve); if (nid == NID_undef) { if (error_r != NULL) *error_r = t_strdup_printf("Unknown EC curve %s", curve); return FALSE; } if (dcrypt_openssl_generate_ec_key(nid, &pkey, error_r)) { pair_r->priv = i_new(struct dcrypt_private_key, 1); pair_r->priv->key = pkey; pair_r->priv->ref++; pair_r->pub = NULL; dcrypt_openssl_private_to_public_key(pair_r->priv, &(pair_r->pub)); return TRUE; } else return dcrypt_openssl_error(error_r); } if (error_r != NULL) *error_r = "Key type not supported in this build"; return FALSE; } static bool dcrypt_openssl_decrypt_point_v1(buffer_t *data, buffer_t *key, BIGNUM **point_r, const char **error_r) { struct dcrypt_context_symmetric *dctx; buffer_t *tmp = buffer_create_dynamic(pool_datastack_create(), 64); if (!dcrypt_openssl_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &dctx, error_r)) { return FALSE; } /* v1 KEYS have all-zero IV - have to use it ourselves too */ dcrypt_openssl_ctx_sym_set_iv(dctx, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); dcrypt_openssl_ctx_sym_set_key(dctx, key->data, key->used); if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || !dcrypt_openssl_ctx_sym_update(dctx, data->data, data->used, tmp, error_r) || !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { dcrypt_openssl_ctx_sym_destroy(&dctx); return FALSE; } dcrypt_openssl_ctx_sym_destroy(&dctx); *point_r = BN_bin2bn(tmp->data, tmp->used, NULL); safe_memset(buffer_get_modifiable_data(tmp, NULL), 0,tmp->used); buffer_set_used_size(key, 0); if (*point_r == FALSE) return dcrypt_openssl_error(error_r); return TRUE; } static bool dcrypt_openssl_decrypt_point_ec_v1(struct dcrypt_private_key *dec_key, const char *data_hex, const char *peer_key_hex, BIGNUM **point_r, const char **error_r) { buffer_t *peer_key, *data, key, *secret; bool res; data = buffer_create_dynamic(pool_datastack_create(), 128); peer_key = buffer_create_dynamic(pool_datastack_create(), 64); hex_to_binary(data_hex, data); hex_to_binary(peer_key_hex, peer_key); secret = buffer_create_dynamic(pool_datastack_create(), 64); if (!dcrypt_openssl_ecdh_derive_secret_local(dec_key, peer_key, secret, error_r)) return FALSE; /* run it thru SHA256 once */ unsigned char digest[SHA256_DIGEST_LENGTH]; SHA256(secret->data, secret->used, digest); safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); buffer_set_used_size(secret, 0); buffer_create_from_const_data(&key, digest, SHA256_DIGEST_LENGTH); /* then use this as key */ res = dcrypt_openssl_decrypt_point_v1(data, &key, point_r, error_r); memset(digest, 0, sizeof(digest)); safe_memset(digest, 0, SHA256_DIGEST_LENGTH); return res; } static bool dcrypt_openssl_decrypt_point_password_v1(const char *data_hex, const char *password_hex, const char *salt_hex, BIGNUM **point_r, const char **error_r) { buffer_t *salt, *data, *password, *key; struct dcrypt_context_symmetric *dctx; data = buffer_create_dynamic(pool_datastack_create(), 128); salt = buffer_create_dynamic(pool_datastack_create(), 16); password = buffer_create_dynamic(pool_datastack_create(), 32); key = buffer_create_dynamic(pool_datastack_create(), 32); hex_to_binary(data_hex, data); hex_to_binary(salt_hex, salt); hex_to_binary(password_hex, password); /* aes-256-ctr uses 32 byte key, and v1 uses all-zero IV */ if (!dcrypt_openssl_pbkdf2(password->data, password->used, salt->data, salt->used, "sha256", 16, key, 32, error_r)) { dcrypt_ctx_sym_destroy(&dctx); return FALSE; } return dcrypt_openssl_decrypt_point_v1(data, key, point_r, error_r); } static bool dcrypt_openssl_load_private_key_dovecot_v1(struct dcrypt_private_key **key_r, int len, const char **input, const char *password, struct dcrypt_private_key *dec_key, const char **error_r) { int nid, ec, enctype; BIGNUM *point = NULL; if (str_to_int(input[1], &nid) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } if (str_to_int(input[2], &enctype) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } /* decode and optionally decipher private key value */ if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { point = BN_new(); if (point == NULL || BN_hex2bn(&point, input[3]) < 1) { BN_free(point); return dcrypt_openssl_error(error_r); } } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { /* by password */ if (password == NULL) { if (error_r != NULL) *error_r = "password missing"; return FALSE; } const char *enc_priv_pt = input[3]; const char *salt = input[4]; if (!dcrypt_openssl_decrypt_point_password_v1(enc_priv_pt, password, salt, &point, error_r)) { return FALSE; } } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { /* by key */ if (dec_key == NULL) { if (error_r != NULL) *error_r = "decrypt key missing"; return FALSE; } const char *enc_priv_pt = input[3]; const char *peer_key = input[4]; if (!dcrypt_openssl_decrypt_point_ec_v1(dec_key, enc_priv_pt, peer_key, &point, error_r)) { return FALSE; } } else { if (error_r != NULL) *error_r = "Invalid key data"; return FALSE; } EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); if (eckey == NULL) return dcrypt_openssl_error(error_r); /* assign private key */ BN_CTX *bnctx = BN_CTX_new(); if (bnctx == NULL) { EC_KEY_free(eckey); return dcrypt_openssl_error(error_r); } EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED); EC_KEY_set_private_key(eckey, point); EC_KEY_precompute_mult(eckey, bnctx); EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); if (pub == NULL) { EC_KEY_free(eckey); BN_CTX_free(bnctx); return dcrypt_openssl_error(error_r); } /* calculate public key */ ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, NULL, NULL, bnctx); EC_KEY_set_public_key(eckey, pub); BN_free(point); EC_POINT_free(pub); BN_CTX_free(bnctx); /* make sure it looks OK and is correct */ if (ec == 1 && EC_KEY_check_key(eckey) == 1) { unsigned char digest[SHA256_DIGEST_LENGTH]; /* validate that the key was loaded correctly */ char *id = ec_key_get_pub_point_hex(eckey); if (id == NULL) { EC_KEY_free(eckey); return dcrypt_openssl_error(error_r); } SHA256((unsigned char*)id, strlen(id), digest); OPENSSL_free(id); const char *digest_hex = binary_to_hex(digest, SHA256_DIGEST_LENGTH); if (strcmp(digest_hex, input[len-1]) != 0) { if (error_r != NULL) *error_r = "Key id mismatch after load"; EC_KEY_free(eckey); return FALSE; } EVP_PKEY *key = EVP_PKEY_new(); if (key == NULL) { EC_KEY_free(eckey); return dcrypt_openssl_error(error_r); } EVP_PKEY_set1_EC_KEY(key, eckey); EC_KEY_free(eckey); *key_r = i_new(struct dcrypt_private_key, 1); (*key_r)->key = key; (*key_r)->ref++; return TRUE; } EC_KEY_free(eckey); return dcrypt_openssl_error(error_r); } /* encrypt/decrypt private keys */ static bool dcrypt_openssl_cipher_key_dovecot_v2(const char *cipher, enum dcrypt_sym_mode mode, buffer_t *input, buffer_t *secret, buffer_t *salt, const char *digalgo, unsigned int rounds, buffer_t *result_r, const char **error_r) { struct dcrypt_context_symmetric *dctx; bool res; if (!dcrypt_openssl_ctx_sym_create(cipher, mode, &dctx, error_r)) { return FALSE; } /* generate encryption key/iv based on secret/salt */ buffer_t *key_data = buffer_create_dynamic(pool_datastack_create(), 128); res = dcrypt_openssl_pbkdf2(secret->data, secret->used, salt->data, salt->used, digalgo, rounds, key_data, dcrypt_openssl_ctx_sym_get_key_length(dctx)+dcrypt_openssl_ctx_sym_get_iv_length(dctx), error_r); if (!res) { dcrypt_openssl_ctx_sym_destroy(&dctx); return FALSE; } buffer_t *tmp = buffer_create_dynamic(pool_datastack_create(), 128); const unsigned char *kd = buffer_free_without_data(&key_data); /* perform ciphering */ dcrypt_openssl_ctx_sym_set_key(dctx, kd, dcrypt_openssl_ctx_sym_get_key_length(dctx)); dcrypt_openssl_ctx_sym_set_iv(dctx, kd+dcrypt_openssl_ctx_sym_get_key_length(dctx), dcrypt_openssl_ctx_sym_get_iv_length(dctx)); if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || !dcrypt_openssl_ctx_sym_update(dctx, input->data, input->used, tmp, error_r) || !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { res = FALSE; } else { /* provide result if succeeded */ buffer_append_buf(result_r, tmp, 0, (size_t)-1); res = TRUE; } /* and ensure no data leaks */ safe_memset(buffer_get_modifiable_data(tmp, NULL), 0, tmp->used); dcrypt_openssl_ctx_sym_destroy(&dctx); return res; } static bool dcrypt_openssl_load_private_key_dovecot_v2(struct dcrypt_private_key **key_r, int len, const char **input, const char *password, struct dcrypt_private_key *dec_key, const char **error_r) { int enctype; buffer_t *key_data = buffer_create_dynamic(pool_datastack_create(), 256); /* check for encryption type */ if (str_to_int(input[2], &enctype) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } if (enctype < 0 || enctype > 2) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } /* match encryption type to field counts */ if ((enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE && len != 5) || (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD && len != 9) || (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK && len != 11)) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } /* get key type */ int nid = OBJ_txt2nid(input[1]); if (nid == NID_undef) return dcrypt_openssl_error(error_r); /* decode and possibly decipher private key value */ if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { if (hex_to_binary(input[3], key_data) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; } } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { if (dec_key == NULL) { if (error_r != NULL) *error_r = "decrypt key missing"; return FALSE; } unsigned int rounds; struct dcrypt_public_key *pubkey = NULL; if (str_to_uint(input[6], &rounds) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } buffer_t *data = buffer_create_dynamic(pool_datastack_create(), 128); /* check that we have correct decryption key */ dcrypt_openssl_private_to_public_key(dec_key, &pubkey); if (!dcrypt_openssl_public_key_id(pubkey, "sha256", data, error_r)) { dcrypt_openssl_unref_public_key(&pubkey); return FALSE; } dcrypt_openssl_unref_public_key(&pubkey); if (strcmp(binary_to_hex(data->data, data->used), input[9]) != 0) { if (error_r != NULL) *error_r = "No private key available"; return FALSE; } buffer_t *salt, *peer_key, *secret; salt = buffer_create_dynamic(pool_datastack_create(), strlen(input[4])/2); peer_key = buffer_create_dynamic(pool_datastack_create(), strlen(input[8])/2); secret = buffer_create_dynamic(pool_datastack_create(), 128); buffer_set_used_size(data, 0); hex_to_binary(input[4], salt); hex_to_binary(input[8], peer_key); hex_to_binary(input[7], data); /* get us secret value to use for key/iv generation */ if (EVP_PKEY_base_id((EVP_PKEY*)dec_key) == EVP_PKEY_RSA) { if (!dcrypt_openssl_rsa_decrypt(dec_key, peer_key->data, peer_key->used, secret, error_r)) return FALSE; } else { /* perform ECDH */ if (!dcrypt_openssl_ecdh_derive_secret_local(dec_key, peer_key, secret, error_r)) return FALSE; } /* decrypt key */ if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], DCRYPT_MODE_DECRYPT, data, secret, salt, input[5], rounds, key_data, error_r)) { return FALSE; } } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { if (password == NULL) { if (error_r != NULL) *error_r = "password missing"; return FALSE; } unsigned int rounds; if (str_to_uint(input[6], &rounds) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } buffer_t *salt, secret, *data; salt = buffer_create_dynamic(pool_datastack_create(), strlen(input[4])/2); buffer_create_from_const_data(&secret, password, strlen(password)); data = buffer_create_dynamic(pool_datastack_create(), strlen(input[7])/2); if (hex_to_binary(input[4], salt) != 0 || hex_to_binary(input[7], data) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], DCRYPT_MODE_DECRYPT, data, &secret, salt, input[5], rounds, key_data, error_r)) { return FALSE; } } /* decode actual key */ if (EVP_PKEY_type(nid) == EVP_PKEY_RSA) { RSA *rsa = RSA_new(); const unsigned char *ptr = buffer_get_data(key_data, NULL); if (rsa == NULL || d2i_RSAPrivateKey(&rsa, &ptr, key_data->used) == NULL || RSA_check_key(rsa) != 1) { safe_memset(buffer_get_modifiable_data(key_data, NULL), 0, key_data->used); RSA_free(rsa); return dcrypt_openssl_error(error_r); } safe_memset(buffer_get_modifiable_data(key_data, NULL), 0, key_data->used); buffer_set_used_size(key_data, 0); EVP_PKEY *pkey = EVP_PKEY_new(); if (pkey == NULL) { RSA_free(rsa); return dcrypt_openssl_error(error_r); } EVP_PKEY_set1_RSA(pkey, rsa); RSA_free(rsa); *key_r = i_new(struct dcrypt_private_key, 1); (*key_r)->key = pkey; (*key_r)->ref++; } else { int ec; BIGNUM *point = BN_new(); if (point == NULL || BN_mpi2bn(key_data->data, key_data->used, point) == NULL) { safe_memset(buffer_get_modifiable_data(key_data, NULL), 0, key_data->used); BN_free(point); return dcrypt_openssl_error(error_r); } EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); safe_memset(buffer_get_modifiable_data(key_data, NULL), 0, key_data->used); buffer_set_used_size(key_data, 0); BN_CTX *bnctx = BN_CTX_new(); if (eckey == NULL || bnctx == NULL) { BN_free(point); EC_KEY_free(eckey); BN_CTX_free(bnctx); return dcrypt_openssl_error(error_r); } EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED); EC_KEY_set_private_key(eckey, point); EC_KEY_precompute_mult(eckey, bnctx); EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); if (pub == NULL) ec = -1; else { /* calculate public key */ ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, NULL, NULL, bnctx); EC_KEY_set_public_key(eckey, pub); EC_POINT_free(pub); } BN_free(point); BN_CTX_free(bnctx); /* make sure the EC key is valid */ EVP_PKEY *key = EVP_PKEY_new(); if (ec == 1 && key != NULL && EC_KEY_check_key(eckey) == 1) { EVP_PKEY_set1_EC_KEY(key, eckey); EC_KEY_free(eckey); *key_r = i_new(struct dcrypt_private_key, 1); (*key_r)->key = key; (*key_r)->ref++; } else { EVP_PKEY_free(key); EC_KEY_free(eckey); return dcrypt_openssl_error(error_r); } } /* finally compare key to key id */ dcrypt_openssl_private_key_id(*key_r, "sha256", key_data, NULL); if (strcmp(binary_to_hex(key_data->data, key_data->used), input[len-1]) != 0) { dcrypt_openssl_unref_private_key(key_r); if (error_r != NULL) *error_r = "Key id mismatch after load"; return FALSE; } return TRUE; } static bool dcrypt_openssl_load_private_key_dovecot(struct dcrypt_private_key **key_r, const char *data, const char *password, struct dcrypt_private_key *key, enum dcrypt_key_version version, const char **error_r) { const char **input = t_strsplit(data, ":\t"); size_t len = str_array_length(input); switch (version) { case DCRYPT_KEY_VERSION_1: return dcrypt_openssl_load_private_key_dovecot_v1(key_r, len, input, password, key, error_r); case DCRYPT_KEY_VERSION_2: return dcrypt_openssl_load_private_key_dovecot_v2(key_r, len, input, password, key, error_r); case DCRYPT_KEY_VERSION_NA: i_unreached(); } return FALSE; } static bool dcrypt_openssl_load_public_key_dovecot_v1(struct dcrypt_public_key **key_r, int len, const char **input, const char **error_r) { int nid; if (str_to_int(input[1], &nid) != 0) { if (error_r != NULL) *error_r = "Corrupted data"; return FALSE; } EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); if (eckey == NULL) { dcrypt_openssl_error(error_r); return FALSE; } EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); BN_CTX *bnctx = BN_CTX_new(); EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(eckey)); if (bnctx == NULL || point == NULL || EC_POINT_hex2point(EC_KEY_get0_group(eckey), input[2], point, bnctx) == NULL) { BN_CTX_free(bnctx); EC_KEY_free(eckey); EC_POINT_free(point); dcrypt_openssl_error(error_r); return FALSE; } BN_CTX_free(bnctx); EC_KEY_set_public_key(eckey, point); EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); EC_POINT_free(point); if (EC_KEY_check_key(eckey) == 1) { EVP_PKEY *key = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(key, eckey); EC_KEY_free(eckey); /* make sure digest matches */ buffer_t *dgst = buffer_create_dynamic(pool_datastack_create(), 32); struct dcrypt_public_key tmp = { key, 0 }; dcrypt_openssl_public_key_id_old(&tmp, dgst, NULL); if (strcmp(binary_to_hex(dgst->data, dgst->used), input[len-1]) != 0) { if (error_r != NULL) *error_r = "Key id mismatch after load"; EVP_PKEY_free(key); return FALSE; } *key_r = i_new(struct dcrypt_public_key, 1); (*key_r)->key = key; (*key_r)->ref++; return TRUE; } dcrypt_openssl_error(error_r); return FALSE; } static bool dcrypt_openssl_load_public_key_dovecot_v2(struct dcrypt_public_key **key_r, int len, const char **input, const char **error_r) { buffer_t tmp; size_t keylen = strlen(input[1])/2; unsigned char keybuf[keylen]; const unsigned char *ptr; buffer_create_from_data(&tmp, keybuf, keylen); hex_to_binary(input[1], &tmp); ptr = keybuf; EVP_PKEY *pkey = EVP_PKEY_new(); if (pkey == NULL || d2i_PUBKEY(&pkey, &ptr, keylen)==NULL) { EVP_PKEY_free(pkey); dcrypt_openssl_error(error_r); return FALSE; } /* make sure digest matches */ buffer_t *dgst = buffer_create_dynamic(pool_datastack_create(), 32); struct dcrypt_public_key tmpkey = {pkey, 0}; dcrypt_openssl_public_key_id(&tmpkey, "sha256", dgst, NULL); if (strcmp(binary_to_hex(dgst->data, dgst->used), input[len-1]) != 0) { if (error_r != NULL) *error_r = "Key id mismatch after load"; EVP_PKEY_free(pkey); return FALSE; } *key_r = i_new(struct dcrypt_public_key, 1); (*key_r)->key = pkey; (*key_r)->ref++; return TRUE; } static bool dcrypt_openssl_load_public_key_dovecot(struct dcrypt_public_key **key_r, const char *data, enum dcrypt_key_version version, const char **error_r) { const char **input = t_strsplit(data, ":\t"); size_t len = str_array_length(input); switch (version) { case DCRYPT_KEY_VERSION_1: return dcrypt_openssl_load_public_key_dovecot_v1(key_r, len, input, error_r); break; case DCRYPT_KEY_VERSION_2: return dcrypt_openssl_load_public_key_dovecot_v2(key_r, len, input, error_r); break; case DCRYPT_KEY_VERSION_NA: i_unreached(); } return FALSE; } static bool dcrypt_openssl_encrypt_private_key_dovecot(buffer_t *key, int enctype, const char *cipher, const char *password, struct dcrypt_public_key *enc_key, buffer_t *destination, const char **error_r) { bool res; unsigned char *ptr; unsigned char salt[8]; buffer_t *peer_key = buffer_create_dynamic(pool_datastack_create(), 128); buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 128); cipher = t_str_lcase(cipher); str_append(destination, cipher); str_append_c(destination, ':'); random_fill(salt, sizeof(salt)); binary_to_hex_append(destination, salt, sizeof(salt)); buffer_t saltbuf; buffer_create_from_const_data(&saltbuf, salt, sizeof(salt)); /* so we don't have to make new version if we ever upgrade these */ str_append(destination, t_strdup_printf(":%s:%d:", DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS)); if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_RSA) { size_t used = buffer_get_used_size(secret); /* peer key, in this case, is encrypted secret, which is 16 bytes of data */ ptr = buffer_append_space_unsafe(secret, 16); random_fill(ptr, 16); buffer_set_used_size(secret, used+16); if (!dcrypt_rsa_encrypt(enc_key, secret->data, secret->used, peer_key, error_r)) { return FALSE; } } else if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_EC) { /* generate secret by ECDHE */ if (!dcrypt_openssl_ecdh_derive_secret_peer(enc_key, peer_key, secret, error_r)) { return FALSE; } } else { /* Loading the key should have failed */ i_unreached(); } /* add encryption key id, reuse peer_key buffer */ } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { str_append(secret, password); } /* encrypt key using secret and salt */ buffer_t *tmp = buffer_create_dynamic(pool_datastack_create(), 128); res = dcrypt_openssl_cipher_key_dovecot_v2(cipher, DCRYPT_MODE_ENCRYPT, key, secret, &saltbuf, DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS, tmp, error_r); safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); binary_to_hex_append(destination, tmp->data, tmp->used); /* some additional fields or private key version */ if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { str_append_c(destination, ':'); /* for RSA, this is the actual encrypted secret */ binary_to_hex_append(destination, peer_key->data, peer_key->used); str_append_c(destination, ':'); buffer_set_used_size(peer_key, 0); if (!dcrypt_openssl_public_key_id(enc_key, "sha256", peer_key, error_r)) return FALSE; binary_to_hex_append(destination, peer_key->data, peer_key->used); } return res; } static bool dcrypt_openssl_store_private_key_dovecot(struct dcrypt_private_key *key, const char *cipher, buffer_t *destination, const char *password, struct dcrypt_public_key *enc_key, const char **error_r) { size_t dest_used = buffer_get_used_size(destination); const char *cipher2 = NULL; EVP_PKEY *pkey = key->key; char objtxt[80]; /* openssl manual says this is OK */ ASN1_OBJECT *obj; if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { /* because otherwise we get wrong nid */ obj = OBJ_nid2obj(EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))); } else { obj = OBJ_nid2obj(EVP_PKEY_id(pkey)); } int enctype = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; int ln = OBJ_obj2txt(objtxt, sizeof(objtxt), obj, 1); if (ln < 1) return dcrypt_openssl_error(error_r); if (ln > (int)sizeof(objtxt)) { if (error_r != NULL) *error_r = "Object identifier too long"; return FALSE; } buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 256); /* convert key to private key value */ if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) { unsigned char *ptr; RSA *rsa = EVP_PKEY_get0_RSA(pkey); int ln = i2d_RSAPrivateKey(rsa, &ptr); if (ln < 1) return dcrypt_openssl_error(error_r); buffer_append(buf, ptr, ln); } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { unsigned char *ptr; EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); const BIGNUM *pk = EC_KEY_get0_private_key(eckey); /* serialize to MPI which is portable */ int len = BN_bn2mpi(pk, NULL); ptr = buffer_append_space_unsafe(buf, len); BN_bn2mpi(pk, ptr); } else { /* Loading the key should have failed */ i_unreached(); } /* see if we want ECDH based or password based encryption */ if (cipher != NULL && strncasecmp(cipher, "ecdh-", 5) == 0) { i_assert(enc_key != NULL); i_assert(password == NULL); enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PK; cipher2 = cipher+5; } else if (cipher != NULL) { i_assert(enc_key == NULL); i_assert(password != NULL); enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD; cipher2 = cipher; } else if (enctype == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { i_assert(enc_key == NULL && password == NULL); } /* put in OID and encryption type */ str_append(destination, t_strdup_printf("2:%s:%d:", objtxt, enctype)); /* perform encryption if desired */ if (enctype != DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { if (!dcrypt_openssl_encrypt_private_key_dovecot(buf, enctype, cipher2, password, enc_key, destination, error_r)) { buffer_set_used_size(destination, dest_used); return FALSE; } } else { binary_to_hex_append(destination, buf->data, buf->used); } /* append public key id */ str_append_c(destination, ':'); buffer_set_used_size(buf, 0); bool res = dcrypt_openssl_private_key_id(key, "sha256", buf, error_r); binary_to_hex_append(destination, buf->data, buf->used); if (!res) { /* well, that didn't end well */ buffer_set_used_size(destination, dest_used); return FALSE; } return TRUE; } static bool dcrypt_openssl_store_public_key_dovecot(struct dcrypt_public_key *key, buffer_t *destination, const char **error_r) { EVP_PKEY *pubkey = key->key; unsigned char *tmp = NULL; size_t dest_used = buffer_get_used_size(destination); int rv = i2d_PUBKEY(pubkey, &tmp); if (tmp == NULL) return dcrypt_openssl_error(error_r); /* then store it */ str_append_c(destination, '2'); str_append_c(destination, ':'); binary_to_hex_append(destination, tmp, rv); OPENSSL_free(tmp); /* append public key ID */ str_append_c(destination, ':'); buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 32); bool res = dcrypt_openssl_public_key_id(key, "sha256", buf, error_r); if (!res) { buffer_set_used_size(destination, dest_used); return FALSE; } str_append(destination, binary_to_hex(buf->data, buf->used)); return TRUE; } static bool dcrypt_openssl_load_private_key(struct dcrypt_private_key **key_r, const char *data, const char *password, struct dcrypt_private_key *dec_key, const char **error_r) { i_assert(key_r != NULL); enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; if (!dcrypt_openssl_key_string_get_info(data, &format, &version, &kind, NULL, NULL, NULL, error_r)) { return FALSE; } if (kind != DCRYPT_KEY_KIND_PRIVATE) { if (error_r != NULL) *error_r = "key is not private"; return FALSE; } if (format == DCRYPT_FORMAT_DOVECOT) return dcrypt_openssl_load_private_key_dovecot(key_r, data, password, dec_key, version, error_r); EVP_PKEY *key = NULL, *key2; BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); key = EVP_PKEY_new(); key2 = PEM_read_bio_PrivateKey(key_in, &key, NULL, (void*)password); BIO_vfree(key_in); if (key2 == NULL) { EVP_PKEY_free(key); return dcrypt_openssl_error(error_r); } if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(key), POINT_CONVERSION_COMPRESSED); } *key_r = i_new(struct dcrypt_private_key, 1); (*key_r)->key = key; (*key_r)->ref++; return TRUE; } static bool dcrypt_openssl_load_public_key(struct dcrypt_public_key **key_r, const char *data, const char **error_r) { enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; i_assert(key_r != NULL); if (!dcrypt_openssl_key_string_get_info(data, &format, &version, &kind, NULL, NULL, NULL, error_r)) { return FALSE; } if (kind != DCRYPT_KEY_KIND_PUBLIC) { if (error_r != NULL) *error_r = "key is not public"; return FALSE; } if (format == DCRYPT_FORMAT_DOVECOT) return dcrypt_openssl_load_public_key_dovecot(key_r, data, version, error_r); EVP_PKEY *key = NULL; BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); if (key_in == NULL) return dcrypt_openssl_error(error_r); key = PEM_read_bio_PUBKEY(key_in, &key, NULL, NULL); if (BIO_reset(key_in) <= 0) i_unreached(); if (key == NULL) { /* ec keys are bother */ /* read the header */ char buf[27]; /* begin public key */ if (BIO_gets(key_in, buf, sizeof(buf)) != 1) { BIO_vfree(key_in); return dcrypt_openssl_error(error_r); } if (strcmp(buf, "-----BEGIN PUBLIC KEY-----") != 0) { if (error_r != NULL) *error_r = "Missing public key header"; return FALSE; } BIO *b64 = BIO_new(BIO_f_base64()); if (b64 == NULL) { BIO_vfree(key_in); return dcrypt_openssl_error(error_r); } EC_KEY *eckey = d2i_EC_PUBKEY_bio(b64, NULL); if (eckey != NULL) { EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED); EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); key = EVP_PKEY_new(); if (key != NULL) EVP_PKEY_set1_EC_KEY(key, eckey); EC_KEY_free(eckey); } } BIO_vfree(key_in); if (key == NULL) return dcrypt_openssl_error(error_r); *key_r = i_new(struct dcrypt_public_key, 1); (*key_r)->key = key; (*key_r)->ref++; return TRUE; } static bool dcrypt_openssl_store_private_key(struct dcrypt_private_key *key, enum dcrypt_key_format format, const char *cipher, buffer_t *destination, const char *password, struct dcrypt_public_key *enc_key, const char **error_r) { i_assert(key != NULL && key->key != NULL); int ec; if (format == DCRYPT_FORMAT_DOVECOT) { bool ret; ret = dcrypt_openssl_store_private_key_dovecot(key, cipher, destination, password, enc_key, error_r); return ret; } EVP_PKEY *pkey = key->key; BIO *key_out = BIO_new(BIO_s_mem()); if (key_out == NULL) return dcrypt_openssl_error(error_r); const EVP_CIPHER *algo = NULL; if (cipher != NULL) { algo = EVP_get_cipherbyname(cipher); if (algo == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Invalid cipher %s", cipher); return FALSE; } } ec = PEM_write_bio_PrivateKey(key_out, pkey, algo, NULL, 0, NULL, (void*)password); if (BIO_flush(key_out) <= 0) ec = -1; if (ec != 1) { BIO_vfree(key_out); return dcrypt_openssl_error(error_r); } long bs; char *buf; bs = BIO_get_mem_data(key_out, &buf); buffer_append(destination, buf, bs); BIO_vfree(key_out); return TRUE; } static bool dcrypt_openssl_store_public_key(struct dcrypt_public_key *key, enum dcrypt_key_format format, buffer_t *destination, const char **error_r) { int ec; i_assert(key != NULL && key->key != NULL); if (format == DCRYPT_FORMAT_DOVECOT) return dcrypt_openssl_store_public_key_dovecot(key, destination, error_r); EVP_PKEY *pkey = key->key; BIO *key_out = BIO_new(BIO_s_mem()); if (key_out == NULL) return dcrypt_openssl_error(error_r); BIO *b64; if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) ec = PEM_write_bio_PUBKEY(key_out, pkey); else if ((b64 = BIO_new(BIO_f_base64())) == NULL) ec = -1; else { (void)BIO_puts(key_out, "-----BEGIN PUBLIC KEY-----\n"); (void)BIO_push(b64, key_out); ec = i2d_EC_PUBKEY_bio(b64, EVP_PKEY_get0_EC_KEY(pkey)); if (BIO_flush(b64) <= 0) ec = -1; (void)BIO_pop(b64); BIO_vfree(b64); if (BIO_puts(key_out, "-----END PUBLIC KEY-----") <= 0) ec = -1; } if (ec != 1) { BIO_vfree(key_out); return dcrypt_openssl_error(error_r); } long bs; char *buf; bs = BIO_get_mem_data(key_out, &buf); buffer_append(destination, buf, bs); BIO_vfree(key_out); return TRUE; } static void dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, struct dcrypt_public_key **pub_key_r) { i_assert(priv_key != NULL && pub_key_r != NULL); EVP_PKEY *pkey = priv_key->key; EVP_PKEY *pk; pk = EVP_PKEY_new(); i_assert(pk != NULL); /* we shouldn't get malloc() failures */ if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) { RSA *rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); EVP_PKEY_set1_RSA(pk, rsa); RSA_free(rsa); } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { EC_KEY* eck = EVP_PKEY_get1_EC_KEY(pkey); EC_KEY_set_asn1_flag(eck, OPENSSL_EC_NAMED_CURVE); EVP_PKEY_set1_EC_KEY(pk, eck); EC_KEY_free(eck); } else { /* Loading the key should have failed */ i_unreached(); } *pub_key_r = i_new(struct dcrypt_public_key, 1); (*pub_key_r)->key = pk; (*pub_key_r)->ref++; } static bool dcrypt_openssl_key_string_get_info(const char *key_data, enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, enum dcrypt_key_encryption_type *encryption_type_r, const char **encryption_key_hash_r, const char **key_hash_r, const char **error_r) { enum dcrypt_key_format format = DCRYPT_FORMAT_PEM; enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; enum dcrypt_key_encryption_type encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; enum dcrypt_key_kind kind = DCRYPT_KEY_KIND_PUBLIC; char *encryption_key_hash = NULL; char *key_hash = NULL; i_assert(key_data != NULL); /* is it PEM key */ if (strncmp(key_data, "-----BEGIN ", 11) == 0) { format = DCRYPT_FORMAT_PEM; version = DCRYPT_KEY_VERSION_NA; key_data += 11; if (strncmp(key_data, "RSA ", 4) == 0) { if (error_r != NULL) *error_r = "RSA private key format not " "supported, convert it to PKEY format " "with openssl pkey"; return FALSE; } if (strncmp(key_data, "ENCRYPTED ", 10) == 0) { encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; key_data += 10; } if (strncmp(key_data, "PRIVATE KEY-----", 16) == 0) kind = DCRYPT_KEY_KIND_PRIVATE; else if (strncmp(key_data, "PUBLIC KEY-----", 15) == 0) kind = DCRYPT_KEY_KIND_PUBLIC; else { if (error_r != NULL) *error_r = "Unknown/invalid PEM key type"; return FALSE; } } else { if (strncmp(key_data, "1:", 2) == 0) { if (error_r != NULL) *error_r = "Dovecot v1 key format " "uses tab to separate fields"; return FALSE; } else if (strncmp(key_data, "2\t", 2) == 0) { if (error_r != NULL) *error_r = "Dovecot v2 key format uses " "colon to separate fields"; return FALSE; } const char **fields = t_strsplit(key_data, ":\t"); int nfields = str_array_length(fields); if (nfields < 2) { if (error_r != NULL) *error_r = "Unknown key format"; return FALSE; } format = DCRYPT_FORMAT_DOVECOT; /* field 1 - version */ if (strcmp(fields[0], "1") == 0) { version = DCRYPT_KEY_VERSION_1; if (nfields == 4) { kind = DCRYPT_KEY_KIND_PUBLIC; } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; } else if (nfields == 6 && strcmp(fields[2],"2") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; } else if (nfields == 7 && strcmp(fields[2],"1") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; if (encryption_key_hash_r != NULL) encryption_key_hash = i_strdup(fields[nfields-2]); } else { if (error_r != NULL) *error_r = "Invalid dovecot v1 encoding"; return FALSE; } } else if (strcmp(fields[0], "2") == 0) { version = DCRYPT_KEY_VERSION_2; if (nfields == 3) { kind = DCRYPT_KEY_KIND_PUBLIC; } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; } else if (nfields == 9 && strcmp(fields[2],"2") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; } else if (nfields == 11 && strcmp(fields[2],"1") == 0) { kind = DCRYPT_KEY_KIND_PRIVATE; encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; if (encryption_key_hash_r != NULL) encryption_key_hash = i_strdup(fields[nfields-2]); } else { if (error_r != NULL) *error_r = "Invalid dovecot v2 encoding"; return FALSE; } } else { if (error_r != NULL) *error_r = "Invalid dovecot key version"; return FALSE; } /* last field is always key hash */ if (key_hash_r != NULL) key_hash = i_strdup(fields[nfields-1]); } if (format_r != NULL) *format_r = format; if (version_r != NULL) *version_r = version; if (encryption_type_r != NULL) *encryption_type_r = encryption_type; if (encryption_key_hash_r != NULL) { *encryption_key_hash_r = t_strdup(encryption_key_hash); i_free(encryption_key_hash); } if (kind_r != NULL) *kind_r = kind; if (key_hash_r != NULL) { *key_hash_r = t_strdup(key_hash); i_free(key_hash); } return TRUE; } static void dcrypt_openssl_ref_public_key(struct dcrypt_public_key *key) { i_assert(key != NULL && key->ref > 0); key->ref++; } static void dcrypt_openssl_ref_private_key(struct dcrypt_private_key *key) { i_assert(key != NULL && key->ref > 0); key->ref++; } static void dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key) { i_assert(key != NULL && *key != NULL); struct dcrypt_public_key *_key = *key; i_assert(_key->ref > 0); *key = NULL; if (--_key->ref > 0) return; EVP_PKEY_free(_key->key); i_free(_key); } static void dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key) { i_assert(key != NULL && *key != NULL); struct dcrypt_private_key *_key = *key; i_assert(_key->ref > 0); *key = NULL; if (--_key->ref > 0) return; EVP_PKEY_free(_key->key); i_free(_key); } static void dcrypt_openssl_unref_keypair(struct dcrypt_keypair *keypair) { i_assert(keypair != NULL); dcrypt_openssl_unref_public_key(&(keypair->pub)); dcrypt_openssl_unref_private_key(&(keypair->priv)); } static bool dcrypt_openssl_rsa_encrypt(struct dcrypt_public_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { int ec; i_assert(key != NULL && key->key != NULL); EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); size_t outl = EVP_PKEY_size(key->key); unsigned char buf[outl]; if (ctx == NULL || EVP_PKEY_encrypt_init(ctx) < 1 || EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) < 1 || EVP_PKEY_encrypt(ctx, buf, &outl, data, data_len) < 1) { dcrypt_openssl_error(error_r); ec = -1; } else { buffer_append(result, buf, outl); ec = 0; } EVP_PKEY_CTX_free(ctx); return ec == 0; } static bool dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { int ec; i_assert(key != NULL && key->key != NULL); EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); size_t outl = EVP_PKEY_size(key->key); unsigned char buf[outl]; if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) < 1 || EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) < 1 || EVP_PKEY_decrypt(ctx, buf, &outl, data, data_len) < 1) { dcrypt_openssl_error(error_r); ec = -1; } else { buffer_append(result, buf, outl); ec = 0; } EVP_PKEY_CTX_free(ctx); return ec == 0; } static const char *dcrypt_openssl_oid2name(const unsigned char *oid, size_t oid_len, const char **error_r) { const char *name; i_assert(oid != NULL); ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, oid_len); if (obj == NULL) { dcrypt_openssl_error(error_r); return NULL; } name = OBJ_nid2sn(OBJ_obj2nid(obj)); ASN1_OBJECT_free(obj); return name; } static bool dcrypt_openssl_name2oid(const char *name, buffer_t *oid, const char **error_r) { i_assert(name != NULL); ASN1_OBJECT *obj = OBJ_txt2obj(name, 0); if (obj == NULL) return dcrypt_openssl_error(error_r); size_t len = OBJ_length(obj); if (len == 0) { if (error_r != NULL) *error_r = "Object has no OID assigned"; return FALSE; } unsigned char *bufptr = buffer_append_space_unsafe(oid, len + 2); i2d_ASN1_OBJECT(obj, &bufptr); ASN1_OBJECT_free(obj); if (bufptr != NULL) { return TRUE; } return dcrypt_openssl_error(error_r); } static enum dcrypt_key_type dcrypt_openssl_private_key_type(struct dcrypt_private_key *key) { i_assert(key != NULL && key->key != NULL); EVP_PKEY *priv = key->key; if (EVP_PKEY_base_id(priv) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; else if (EVP_PKEY_base_id(priv) == EVP_PKEY_EC) return DCRYPT_KEY_EC; else i_unreached(); } static enum dcrypt_key_type dcrypt_openssl_public_key_type(struct dcrypt_public_key *key) { i_assert(key != NULL && key->key != NULL); EVP_PKEY *pub = key->key; if (EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; else if (EVP_PKEY_base_id(pub) == EVP_PKEY_EC) return DCRYPT_KEY_EC; else i_unreached(); } /** this is the v1 old legacy way of doing key id's **/ static bool dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, buffer_t *result, const char **error_r) { unsigned char buf[SHA256_DIGEST_LENGTH]; i_assert(key != NULL && key->key != NULL); EVP_PKEY *pub = key->key; if (EVP_PKEY_base_id(pub) != EVP_PKEY_EC) { if (error_r != NULL) *error_r = "Only EC key supported"; return FALSE; } char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(pub)); if (pub_pt_hex == NULL) return dcrypt_openssl_error(error_r); /* digest this */ SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); buffer_append(result, buf, SHA256_DIGEST_LENGTH); OPENSSL_free(pub_pt_hex); return TRUE; } static bool dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, buffer_t *result, const char **error_r) { unsigned char buf[SHA256_DIGEST_LENGTH]; i_assert(key != NULL && key->key != NULL); EVP_PKEY *priv = key->key; if (EVP_PKEY_base_id(priv) != EVP_PKEY_EC) { if (error_r != NULL) *error_r = "Only EC key supported"; return FALSE; } char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(priv)); if (pub_pt_hex == NULL) return dcrypt_openssl_error(error_r); /* digest this */ SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); buffer_append(result, buf, SHA256_DIGEST_LENGTH); OPENSSL_free(pub_pt_hex); return TRUE; } /** this is the new which uses H(der formatted public key) **/ static bool dcrypt_openssl_public_key_id_evp(EVP_PKEY *key, const EVP_MD *md, buffer_t *result, const char **error_r) { bool res = FALSE; unsigned char buf[EVP_MD_size(md)], *ptr; if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(key), POINT_CONVERSION_COMPRESSED); } BIO *b = BIO_new(BIO_s_mem()); if (b == NULL || i2d_PUBKEY_bio(b, key) < 1) { BIO_vfree(b); return dcrypt_openssl_error(error_r); } long len = BIO_get_mem_data(b, &ptr); unsigned int hlen = sizeof(buf); /* then hash it */ EVP_MD_CTX *ctx = EVP_MD_CTX_new(); if (ctx == NULL || EVP_DigestInit_ex(ctx, md, NULL) < 1 || EVP_DigestUpdate(ctx, (const unsigned char*)ptr, len) < 1 || EVP_DigestFinal_ex(ctx, buf, &hlen) < 1) { res = dcrypt_openssl_error(error_r); } else { buffer_append(result, buf, hlen); res = TRUE; } EVP_MD_CTX_free(ctx); BIO_vfree(b); return res; } static bool dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, const char *algorithm, buffer_t *result, const char **error_r) { const EVP_MD *md = EVP_get_digestbyname(algorithm); i_assert(key != NULL && key->key != NULL); EVP_PKEY *pub = key->key; if (md == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Unknown cipher %s", algorithm); return FALSE; } return dcrypt_openssl_public_key_id_evp(pub, md, result, error_r); } static bool dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, const char *algorithm, buffer_t *result, const char **error_r) { const EVP_MD *md = EVP_get_digestbyname(algorithm); i_assert(key != NULL && key->key != NULL); EVP_PKEY *priv = key->key; if (md == NULL) { if (error_r != NULL) *error_r = t_strdup_printf("Unknown cipher %s", algorithm); return FALSE; } return dcrypt_openssl_public_key_id_evp(priv, md, result, error_r); } static struct dcrypt_vfs dcrypt_openssl_vfs = { .initialize = dcrypt_openssl_initialize, .ctx_sym_create = dcrypt_openssl_ctx_sym_create, .ctx_sym_destroy = dcrypt_openssl_ctx_sym_destroy, .ctx_sym_set_key = dcrypt_openssl_ctx_sym_set_key, .ctx_sym_set_iv = dcrypt_openssl_ctx_sym_set_iv, .ctx_sym_set_key_iv_random = dcrypt_openssl_ctx_sym_set_key_iv_random, .ctx_sym_set_padding = dcrypt_openssl_ctx_sym_set_padding, .ctx_sym_get_key = dcrypt_openssl_ctx_sym_get_key, .ctx_sym_get_iv = dcrypt_openssl_ctx_sym_get_iv, .ctx_sym_set_aad = dcrypt_openssl_ctx_sym_set_aad, .ctx_sym_get_aad = dcrypt_openssl_ctx_sym_get_aad, .ctx_sym_set_tag = dcrypt_openssl_ctx_sym_set_tag, .ctx_sym_get_tag = dcrypt_openssl_ctx_sym_get_tag, .ctx_sym_get_key_length = dcrypt_openssl_ctx_sym_get_key_length, .ctx_sym_get_iv_length = dcrypt_openssl_ctx_sym_get_iv_length, .ctx_sym_get_block_size = dcrypt_openssl_ctx_sym_get_block_size, .ctx_sym_init = dcrypt_openssl_ctx_sym_init, .ctx_sym_update = dcrypt_openssl_ctx_sym_update, .ctx_sym_final = dcrypt_openssl_ctx_sym_final, .ctx_hmac_create = dcrypt_openssl_ctx_hmac_create, .ctx_hmac_destroy = dcrypt_openssl_ctx_hmac_destroy, .ctx_hmac_set_key = dcrypt_openssl_ctx_hmac_set_key, .ctx_hmac_set_key_random = dcrypt_openssl_ctx_hmac_set_key_random, .ctx_hmac_get_digest_length = dcrypt_openssl_ctx_hmac_get_digest_length, .ctx_hmac_get_key = dcrypt_openssl_ctx_hmac_get_key, .ctx_hmac_init = dcrypt_openssl_ctx_hmac_init, .ctx_hmac_update = dcrypt_openssl_ctx_hmac_update, .ctx_hmac_final = dcrypt_openssl_ctx_hmac_final, .ecdh_derive_secret_local = dcrypt_openssl_ecdh_derive_secret_local, .ecdh_derive_secret_peer = dcrypt_openssl_ecdh_derive_secret_peer, .pbkdf2 = dcrypt_openssl_pbkdf2, .generate_keypair = dcrypt_openssl_generate_keypair, .load_private_key = dcrypt_openssl_load_private_key, .load_public_key = dcrypt_openssl_load_public_key, .store_private_key = dcrypt_openssl_store_private_key, .store_public_key = dcrypt_openssl_store_public_key, .private_to_public_key = dcrypt_openssl_private_to_public_key, .key_string_get_info = dcrypt_openssl_key_string_get_info, .unref_keypair = dcrypt_openssl_unref_keypair, .unref_public_key = dcrypt_openssl_unref_public_key, .unref_private_key = dcrypt_openssl_unref_private_key, .ref_public_key = dcrypt_openssl_ref_public_key, .ref_private_key = dcrypt_openssl_ref_private_key, .rsa_encrypt = dcrypt_openssl_rsa_encrypt, .rsa_decrypt = dcrypt_openssl_rsa_decrypt, .oid2name = dcrypt_openssl_oid2name, .name2oid = dcrypt_openssl_name2oid, .private_key_type = dcrypt_openssl_private_key_type, .public_key_type = dcrypt_openssl_public_key_type, .public_key_id = dcrypt_openssl_public_key_id, .public_key_id_old = dcrypt_openssl_public_key_id_old, .private_key_id = dcrypt_openssl_private_key_id, .private_key_id_old = dcrypt_openssl_private_key_id_old, }; void dcrypt_openssl_init(struct module *module ATTR_UNUSED) { dovecot_openssl_common_global_ref(); dcrypt_set_vfs(&dcrypt_openssl_vfs); } void dcrypt_openssl_deinit(void) { dovecot_openssl_common_global_unref(); } dovecot-2.2.33.2/src/lib-dcrypt/dcrypt.h0000644000175000017500000002031613123174404014644 00000000000000#ifndef DCRYPT_H #define DCRYPT_H 1 struct dcrypt_context_symmetric; struct dcrypt_context_hmac; struct dcrypt_public_key; struct dcrypt_private_key; struct dcrypt_keypair { struct dcrypt_public_key *pub; struct dcrypt_private_key *priv; }; enum dcrypt_sym_mode { DCRYPT_MODE_ENCRYPT, DCRYPT_MODE_DECRYPT }; enum dcrypt_key_type { DCRYPT_KEY_RSA = 0x1, DCRYPT_KEY_EC = 0x2 }; /** * dovecot key format: * version version-specific data * v1: version tab nid tab raw ec private key (in hex) * v2: version colon algorithm oid colon private-or-public-key-only (in hex) */ enum dcrypt_key_format { DCRYPT_FORMAT_PEM, DCRYPT_FORMAT_DOVECOT, }; enum dcrypt_key_encryption_type { DCRYPT_KEY_ENCRYPTION_TYPE_NONE, DCRYPT_KEY_ENCRYPTION_TYPE_KEY, DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD }; enum dcrypt_key_version { DCRYPT_KEY_VERSION_1, DCRYPT_KEY_VERSION_2, DCRYPT_KEY_VERSION_NA /* not applicable, PEM key */ }; enum dcrypt_key_kind { DCRYPT_KEY_KIND_PUBLIC, DCRYPT_KEY_KIND_PRIVATE }; struct dcrypt_settings { /* OpenSSL engine to use */ const char *crypto_device; /* Look for backends in this directory */ const char *module_dir; }; /** * load and initialize dcrypt backend, use either openssl or gnutls */ bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, const char **error_r); /** * deinitialize dcrypt */ void dcrypt_deinitialize(void); /** * create symmetric context */ bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, struct dcrypt_context_symmetric **ctx_r, const char **error_r); /** * destroy symmetric context and free memory */ void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx); /** * key and IV manipulation functions */ void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, const unsigned char *key, size_t key_len); void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, const unsigned char *iv, size_t iv_len); void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx); bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key); bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv); /** * turn padding on/off (default: on) */ void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, bool padding); /** * authentication data manipulation (use with GCM only) */ void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, const unsigned char *aad, size_t aad_len); bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, buffer_t *aad); /** * result tag from aead (use with GCM only) */ void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, const unsigned char *tag, size_t tag_len); bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, buffer_t *tag); /* get various lengths */ unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx); unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx); unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx); /** * initialize crypto */ bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, const char **error_r); /** * update with data */ bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); /** * perform final step (may or may not emit data) */ bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, buffer_t *result, const char **error_r); /** * create HMAC context, algorithm is digest algorithm */ bool dcrypt_ctx_hmac_create(const char *algorithm, struct dcrypt_context_hmac **ctx_r, const char **error_r); /** * destroy HMAC context and free memory */ void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx); /** * hmac key manipulation */ void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, const unsigned char *key, size_t key_len); bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key); void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx); /** * get digest length for HMAC */ unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx); /** * initialize hmac */ bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, const char **error_r); /** * update hmac context with data */ bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, const unsigned char *data, size_t data_len, const char **error_r); /** * perform final rounds and retrieve result */ bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, const char **error_r); /** * Elliptic Curve based Diffie-Heffman shared secret derivation */ bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, buffer_t *R, buffer_t *S, const char **error_r); bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, buffer_t *R, buffer_t *S, const char **error_r); /** * generate cryptographic data from password and salt. Use 1000-10000 for rounds. */ bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, const char *hash, unsigned int rounds, buffer_t *result, unsigned int result_len, const char **error_r); bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, enum dcrypt_key_type kind, unsigned int bits, const char *curve, const char **error_r); /** * load loads key structure from external format. * store stores key structure into external format. * * you can provide either PASSWORD or ENC_KEY, not both. */ bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, const char *data, const char *password, struct dcrypt_private_key *dec_key, const char **error_r); bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, const char *data, const char **error_r); /** * When encrypting with public key, the cipher parameter here must begin with * ecdh-, for example ecdh-aes-256-ctr. An example of a valid cipher for * encrypting with password would be aes-256-ctr. */ bool dcrypt_key_store_private(struct dcrypt_private_key *key, enum dcrypt_key_format format, const char *cipher, buffer_t *destination, const char *password, struct dcrypt_public_key *enc_key, const char **error_r); bool dcrypt_key_store_public(struct dcrypt_public_key *key, enum dcrypt_key_format format, buffer_t *destination, const char **error_r); void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, struct dcrypt_public_key **pub_key_r); void dcrypt_keypair_unref(struct dcrypt_keypair *keypair); void dcrypt_key_ref_public(struct dcrypt_public_key *key); void dcrypt_key_ref_private(struct dcrypt_private_key *key); void dcrypt_key_unref_public(struct dcrypt_public_key **key); void dcrypt_key_unref_private(struct dcrypt_private_key **key); enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key); enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key); bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, buffer_t *result, const char **error_r); /* return digest of key */ bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, const char **error_r); /* return SHA1 sum of key */ bool dcrypt_key_id_private(struct dcrypt_private_key *key, const char *algorithm, buffer_t *result, const char **error_r); /* return digest of key */ bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, buffer_t *result, const char **error_r); /* return SHA1 sum of key */ bool dcrypt_key_string_get_info(const char *key_data, enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, enum dcrypt_key_encryption_type *encryption_type_r, const char **encryption_key_hash_r, const char **key_hash_r, const char **error_r); /* RSA stuff */ bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); /* OID stuff */ const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, const char **error_r); bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r); #endif dovecot-2.2.33.2/src/lib-dcrypt/ostream-encrypt.h0000644000175000017500000000131413123174404016470 00000000000000#ifndef OSTREAM_ENCRYPT_H #define OSTREAM_ENCRYPT_H struct dcrypt_public_key; struct dcrypt_context_symmetric; /** * algorithm is in form AES-256-CBC-SHA1, recommended * AES-256-GCM-SHA256 * * Algorithms (both crypto and digest) *MUST* have OID to use it. * */ struct ostream * o_stream_create_encrypt(struct ostream *output, const char *algorithm, struct dcrypt_public_key *box_pub, enum io_stream_encrypt_flags flags); /* create context for performing encryption with preset crypto context. do not call ctx_sym_init. no header or mac is written, just plain crypto data. */ struct ostream * o_stream_create_sym_encrypt(struct ostream *output, struct dcrypt_context_symmetric *ctx); #endif dovecot-2.2.33.2/src/lib-dcrypt/dcrypt.c0000644000175000017500000002725413123174404014647 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "dcrypt.h" #include "dcrypt-private.h" static struct module *dcrypt_module = NULL; static struct dcrypt_vfs *dcrypt_vfs = NULL; static const struct dcrypt_settings dcrypt_default_set = { .module_dir = DCRYPT_MODULE_DIR, }; bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, const char **error_r) { struct module_dir_load_settings mod_set; const char *error; if (dcrypt_vfs != NULL) { return TRUE; } if (backend == NULL) backend = "openssl"; /* default for now */ if (set == NULL) set = &dcrypt_default_set; const char *implementation = t_strconcat("dcrypt_",backend,NULL); i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.require_init_funcs = TRUE; if (module_dir_try_load_missing(&dcrypt_module, set->module_dir, implementation, &mod_set, &error) < 0) { if (error_r != NULL) *error_r = error; return FALSE; } module_dir_init(dcrypt_module); i_assert(dcrypt_vfs != NULL); if (dcrypt_vfs->initialize != NULL) { if (!dcrypt_vfs->initialize(set, error_r)) { dcrypt_deinitialize(); return FALSE; } } /* Destroy SSL module after(most of) the others. Especially lib-fs backends may still want to access SSL module in their own atexit-callbacks. */ lib_atexit_priority(dcrypt_deinitialize, LIB_ATEXIT_PRIORITY_LOW); return TRUE; } void dcrypt_deinitialize(void) { module_dir_unload(&dcrypt_module); dcrypt_vfs = NULL; } void dcrypt_set_vfs(struct dcrypt_vfs *vfs) { dcrypt_vfs = vfs; } bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, struct dcrypt_context_symmetric **ctx_r, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_create(algorithm, mode, ctx_r, error_r); } void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_destroy(ctx); } void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, const unsigned char *key, size_t key_len) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_key(ctx, key, key_len); } void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, const unsigned char *iv, size_t iv_len) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_iv(ctx, iv, iv_len); } void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_key_iv_random(ctx); } bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_key(ctx, key); } bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_iv(ctx, iv); } unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_key_length(ctx); } unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_iv_length(ctx); } void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, const unsigned char *aad, size_t aad_len) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_aad(ctx, aad, aad_len); } bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, buffer_t *aad) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_aad(ctx, aad); } void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, const unsigned char *tag, size_t tag_len) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_tag(ctx, tag, tag_len); } bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, buffer_t *tag) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_tag(ctx, tag); } unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_get_block_size(ctx); } bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_init(ctx, error_r); } bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_update(ctx, data, data_len, result, error_r); } bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_sym_final(ctx, result, error_r); } void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, bool padding) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_sym_set_padding(ctx, padding); } bool dcrypt_ctx_hmac_create(const char *algorithm, struct dcrypt_context_hmac **ctx_r, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_create(algorithm, ctx_r, error_r); } void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_hmac_destroy(ctx); } void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, const unsigned char *key, size_t key_len) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_hmac_set_key(ctx, key, key_len); } bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_get_key(ctx, key); } void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ctx_hmac_set_key_random(ctx); } unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_get_digest_length(ctx); } bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_init(ctx, error_r); } bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, const unsigned char *data, size_t data_len, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_update(ctx, data, data_len, error_r); } bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ctx_hmac_final(ctx, result, error_r); } bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, buffer_t *R, buffer_t *S, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ecdh_derive_secret_local(local_key, R, S, error_r); } bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, buffer_t *R, buffer_t *S, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->ecdh_derive_secret_peer(peer_key, R, S, error_r); } bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, const char *hash, unsigned int rounds, buffer_t *result, unsigned int result_len, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->pbkdf2(password, password_len, salt, salt_len, hash, rounds, result, result_len, error_r); } bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, enum dcrypt_key_type kind, unsigned int bits, const char *curve, const char **error_r) { i_assert(dcrypt_vfs != NULL); i_zero(pair_r); return dcrypt_vfs->generate_keypair(pair_r, kind, bits, curve, error_r); } bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, const char *data, const char *password, struct dcrypt_private_key *dec_key, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->load_private_key(key_r, data, password, dec_key, error_r); } bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, const char *data, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->load_public_key(key_r, data, error_r); } bool dcrypt_key_store_private(struct dcrypt_private_key *key, enum dcrypt_key_format format, const char *cipher, buffer_t *destination, const char *password, struct dcrypt_public_key *enc_key, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->store_private_key(key, format, cipher, destination, password, enc_key, error_r); } bool dcrypt_key_store_public(struct dcrypt_public_key *key, enum dcrypt_key_format format, buffer_t *destination, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->store_public_key(key, format, destination, error_r); } void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, struct dcrypt_public_key **pub_key_r) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->private_to_public_key(priv_key, pub_key_r); } bool dcrypt_key_string_get_info(const char *key_data, enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, enum dcrypt_key_encryption_type *encryption_type_r, const char **encryption_key_hash_r, const char **key_hash_r, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->key_string_get_info(key_data, format_r, version_r, kind_r, encryption_type_r, encryption_key_hash_r, key_hash_r, error_r); } enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->private_key_type(key); } enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->public_key_type(key); } bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->public_key_id(key, algorithm, result, error_r); } bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->public_key_id_old(key, result, error_r); } bool dcrypt_key_id_private(struct dcrypt_private_key *key, const char *algorithm, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->private_key_id(key, algorithm, result, error_r); } bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->private_key_id_old(key, result, error_r); } void dcrypt_keypair_unref(struct dcrypt_keypair *keypair) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->unref_keypair(keypair); } void dcrypt_key_ref_public(struct dcrypt_public_key *key) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ref_public_key(key); } void dcrypt_key_ref_private(struct dcrypt_private_key *key) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->ref_private_key(key); } void dcrypt_key_unref_public(struct dcrypt_public_key **key) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->unref_public_key(key); } void dcrypt_key_unref_private(struct dcrypt_private_key **key) { i_assert(dcrypt_vfs != NULL); dcrypt_vfs->unref_private_key(key); } bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->rsa_encrypt(key, data, data_len, result, error_r); } bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->rsa_decrypt(key, data, data_len, result, error_r); } const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->oid2name(oid, oid_len, error_r); } bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r) { i_assert(dcrypt_vfs != NULL); return dcrypt_vfs->name2oid(name, oid, error_r); } dovecot-2.2.33.2/src/lib-dcrypt/sample-v2.asc0000644000175000017500000004433313123174404015471 00000000000000Q1JZUFRFRAMHAgAAAAIAAADfBglghkgBZQMEAS4GCWCGSAFlAwQCAQAACAAAAACv AQKrE9JRl23tq1RrZzVOdniCF0DdU0t0nChX9mv2K7rd/QAAACEDwvjlcRkV+Zrl JQIYgGVPawwFMOUC/ezeeEhcxKeS7MEAAABA2/Y3MaAjsrRPX6pwn2K3Cd8fUZcz S27nzQpUedF2Snt5neblmGVSSwJjZCbnmK91SHmb6TMxflQUntMvjfna0AAAACCD ICFyviOkFxwR4KhIlPV5v5CIINPw5PYmg7m1JcJoAHQSyJAiN7SnaDrV/DZws6zu /Pe6tI6yjY8bIZyVw9sOIhL1UHR8z+RVsn6wwuHqHJhl32isMG2u3I4Q7pY8o/or xcfJZj/HimYKmBc/1Wg6RSLQLIMEg496hFVHBJ4RC+rifhlf88j4s2Txyn+fO9al oqa4Z2hMuW/ad1NlieWNr54/6xbeZbpP17KayiYQPz9CFChAD+L2Rxg3DeJARWBY ywaPmy/NaoNU4+Cllbc6zfI0uvZySe+cYOYnKBi7G3ZuENdmHkSA/JWX1BjGnRdc TRG1vIdit8rL1MGBc3zvKqT10h+ZOOedCEVBi6KXOZAe9FM9DK80CnHjOr9BGNtq mM9ag1sWDAMPvlCK/1+/0A/CyBebjYqoeVF09Q/TrPbNSzdB/AF+wlS/uS+W3ope Ny5+vHz3iO6pKF0gnepFfyk0Eav+5efnOS8Mr3FhQCWIBm7jBCpmyIUlIZ+oE+D3 2qmNyjMpaYB6WcXplQXbK4yTL1kty5BNP++92yuOA22CTZgIy+k0ZK1N6yScnzG4 Bya1KBlbBGOT92/HYqfm4YZ8hjJ/aCBK36UfDnsTIOHx/dm4jUCaGo7MOAmOfTxJ mSnzNyXclbo2+MNK3YFLarkcT9XI848ojnAWOpZ1x/062oXIILH3Q/PSYXeRlzhQ yWNCXU3b0liyo7vl+l/jRSYlhPGvSO5ZUoDJD/KHk6xrZI68pZ0oogm8ywSgMlpQ VYjTTdaUAUEcueC/n46WTITSvIEf4RfdXjE/r7MStZfd0ypofNJDo9H/xQRWctzq Go9Ta2jtmOTFcy1JAuUbYGe6SyG7MlOAEprLnwl8gji4uXCb2yD4pYTmDbRwcvbi Xldcbzi7XIA3cbeDWJRiBsUh5OkbKKgK9UpbYLuZyya4H6mohVfC/bDFPlSezbzX z3WGNwNRY+mDIPCDXP4qgomiQ0SkK8gmPZY+q8YNePgB3CryNmea4Bw5waAJ6DHY 66D9jL37IFjG/mOXqwIinwDvxdoWI8SjwqwFzUsjRnUe9zkIBAV3+KKU7pkDrLe2 1K9ZvDhrVvHgYPOOkQ0UxEvkb2QamLlAV+1Py68kKKouVcwF3KTvq1bJRMlfLEFp f2d/agN92yMac1jCC42hUDYNZrGMBTtco+ZqOJJ5xZouYDTiLP9lgHfO9sHZ+9pk Nf39SlnDfsYeRhOTAlGCYVEkEGDxWU+5CTZRHDTZYXU26a0Pyd82dE47QHqGikqk sFIqtIaxuJEXDva5gmtBgDf9u1xSUe4r6CXtymT+zOjp2Zj9+lu9ggB+hpymgeRL CjzTk2lr6cx9n63k0U8QFRTz5N2yNvubD4mv6RkqTY8xf+q2WDD/aX61uImw/ltC EtYJfxKHRHAeXJDlSN9DLZ3+lhfVmEyw6MdeeQH2hpvAKTZeXcPuMB2PRwB8+vkK LgFNneRn1Tuztirs9sPtnErgaDEyNGF/o8xrDAQ1mnzKR9tWAPtXKh119snDps4N wwiYGkT3pAsxDoh4LiJWOyks4PRKX+1Ly1BGCPoRX3/t/QaPmN61vXSHnwUQVCJ8 kaxyqOl43eQbKCdw/+EfyPyyp7I7w4IShUe7g4RtHbxltNcNvQTyztxdkYDWiFQ0 C5aDmaqtaYFB/lqOFKJNC+SOCrleSqUYnJj/PWf+jZuZ11zjI0XAlXVY1mbxjEG8 RqlU4HAtcUa78AACc+vBJ6HD3KydpKB+R1kBCw9VTWiNx5o2TmS0uw3uimzBrkWE VM2QntF438DJl0d5fGWeG89KqOFM10sDyNDQ+tjserDyaVKHBuErDC/C2BI7YFA2 63mfRTn4Yt7HltFch3MRFo9x3iRLrkqRuww2kCvZTauC9YxqZgeuD5XYDrRwbx2A WkF8bL55jnODO/RWxOthSv5z2OTWwqCCw8/SE6IAIRdGnIH5nu6uoK90NfxsN/iG c6VjqY8HXY1mCQwdTFKmY1gsGLVgyKPbugF6LMOjEvy6mN3GiJvd+Ncs5/5QPVeh gakujGRmkwQ4PMGn56mU7MvKoJ1jq4GPV3DDjSgTwxxJn0GUKihTnslR+lI9mJXD +riKQAzT6POjfvWnQ9bfhVmPrGU/rPAAzi02jUXwXCXPfDUBqiD9yixGCGF2gHeW rXFqQvDYU0DuetOePprlC+IfqgMJHTM7Eepqi0Kx+vygH4wnu/JJKcSmCte/z5lH iVM3c4SZVMnBWhfhN+TWnuFoPuUGVgisLePoi1NsAzbsh5GbvImLgVVNbSAZmWCc nbnh1VXU0MNF5Ovyx6XAWUeHnhosIuLP/zQR/ne0QgOYUxE61dJ5iRnE1oA2hrJm UeISJ6QKzJtf690EtqnSq8qzjOLsOlvNgf6O030vc9Wgj2PqSK93Ai5kNb5r2iyX 0y/JIJaEPN5mjS4VVZDgcQoo6tRQWF9+iGYgPK3vGqeAOLrorw7UG0FsvkNqm2US Kqi6/65jEdmp975vuRwa4o0zCfuWc9OZ8UZTkREmouc3fEkvaHoOKJm/O68E6aHU WlpgYvLWLWychCWMqr4tOBAarTz+UZGI6wtORYTQWXPbfl4YtmJ7fxuykNtkLC1/ Jw+MHZ1WmA1csvalfYvcY/rvNDlkdpM7iPeKLpAMspyWueXiYl5cCEJ7R4eexs+p 71juVPhv1UvBtR6vcxjd9PryFCeMpJDvLUwtNq2taRXTFFa4pz3+zl7zWA5gqIag HSd0khr00aF8fUQhTfSVUg6qYo/FOAeinJMfSsyXnJ/R7WOtZmi7UkuC63zaA4nr HXXtrefd434tHUuWQP+76Fwmp62tlPFlfiMqNRipIRUi2JLgRXws7A7DH9msSvAz IHD9lxR7KECQWkJUhGhTOWCTUeNeVdyeAZdLD+bGkicvaQlFfyaqxm0XCqdJEviU PngsGLaqgEqfMGUkOObd4W4MlRw6X+77pkQf/l1FktSm4Ixo7qMxDz0En7irYvlj BDCsVyig0MM853I8cYoMHkd33HAnMITS2LkFfjlwUFh91rZlBQawFt5JWAbx0UaR /dJOHkxEhyX6yW5+PWMxazPyiVLAqxErsOspJXlUI77zucII5REDHnRL5qo0+9x8 sR4a9AyUch+oOgFoHMTKeNEMoFtVfNmpeyc3TCZmPQFNFb4KT+jrpQuH8ohREdGh /73Np9ufGBAeGqOhkIWNGD+VTZqFATANkbRJWzGSKoG53W4XFrYhXEx0/MmHy3Uo a9pvN/nspChjMncoe0x9ZwO4S/Zxr0JWuikFzvUfEqVck3R99Bcd5dD67qHx8Wih s7m/Q3RxLS+dU/1fPa2SA/tVGC0OciYmr7vDZArsUscpYehtMqbpgaQPDJI07x64 ilQiNg2ZtFmTh8VHQIQyf8zdE9jtFhKJwrT/M/MN1st5v0dfBbohz7Q7TGDct9Fu 4+JwdpeZK+dm0yxNT+gEhQOAuK5+rBX5wsr1kZ5nohg0nrGAQ3Kl2z+QV4u1Xnr+ d4W7TBpsy/ugq4ZV9/T/p3DiObPl/kjjjipsJCHN5wpRGr7YeDWUbKRq/GNqKNrW 6CpyyYnMFK6PfP85u0poDXlW2DX72/s+bf0pOJIHjRl3BoKhhQpQbQADb/3aV0Ov /3/RKN7zN8Z2ctmOmbCeFpp/Mren56kM/TjnTZs0WLwesShcAY0JNCQ2QcZRFHkr 8SQjlgVPOVVdP1835X2muQgnbyoQ7VNAplA3xsVlvtmOQRD10LOAMF/8s+EF0yZB keieB84j7eHZ9u2763pCMNAokhQ9FcGskEOqM/iNmTtiS0xL4NFfRlKXb7zZcpVT bqLg1mi5c2PLLJls2dw5gfgz6TNcnBSaDn2zhnFll4kuZqxhz5i/2RfPQgicMlV7 /3MDEhTzm3qVhf+S380qatNN94JK0QAn37oKZiK+GTHs69L82YbT9ILMKNXipT9t iViUHzZnYrffayW2+vLWFfjkvkJDrawxcVZ52RuXq0S5qjWmVrkK2prv97PTpNOx H1W4ap+AFQqGgQkoQjqZXTKEr2DIIhg4Ntt/kqeEMLUdS26lKFBSx81JOZivBmby NXIFpNw6FG0RdjAoTt17+ka2hQqk1tya2zIbL+ppQBwJblLx6tEKTfD11QDunsMS 8mM1K6zw1Js2BYiMwpwlN5knRXPzy4oC2iWG5dan2IxW2Fd82zIMLPbeCe6PkwMV FEhaR5l8GYrpYHIgK2JaZm183sOAsav4RDsmc8bio/crrGnSu1B/apMuf79xqXvx ygwRi4t0iborisRsVJveG7+IVf9I5ZHT+WqeEhkhh+lJ4QlRN0JfxuX/cy8I8Pd1 0oHztq+Eyx551a4zgrVl3nDuAZDlByMZlXZCQwwAGMIOCmBNp07WmZ58XZOfQ6Dy rQiHY2gYYI6wGwy/aiiy7BzR0/JGTfs2o1EfHwuI+95D8gyjbDX9VlngNmbpfisK 9mOx2cLBc2aOIu4mYFmpnL7G5gUOz6M2G8MDX0J/NraMcvFSlCT6KyuMzmBnSIHW mmI1k0xKZ+rPxmad8arpZ37KXsKQaRmhYo0WgDSABGnW5lE/9ZJxOj92fgywFLB0 a58uyxF8M/XagJAE6teU4A63xxkiWNj2ZCG9/tndCrzAIe8rMqNbWD15x7tw5LlD GZI+DWVstLyNvaXOhF+Nt+oMwEtMVqxZCO4/1I1zBIS9ginTaw8lE76oWGkwbnP7 5LZb4+BJqPyvaIrHijZR3l/qbK9r0cWdOHjNjKYFPEouz2vuYd4Mh5+wlHcoixw4 SpQ3ZQMBJCfanZXAhcLUYsSp0fmXPmmdPnQ7U6cuqlcurJhQt0Ks8LCKS/cytezK 89q1XTrJ75etdwGfuCMRCInU+lqIvzxZiOHrn8Nxcg48HOvgfo6r9dnmwa//7Lgz jfGJgI222B6/KXeTPyqDhbJP0/gc09tqsmTYMKw/w9wdu3ehbu1lSqALx76O9SgB cl+HDfHNgTt5Yd3BpGbEgG2G+Iin2v1DPXTu/cMYynXmb5X78CPcU9iVc7JbHFVa kepWqFdqRrUuZ2u92N7fm2DgqcBcTex5VRLnN+pAT8VZM8nJIRGgUl0YXY1C5ilN lcQm0jHI0StOrtr6TcFmw6Mru3v+xygWy185js+Y9KZaglmuF7iTsmBfAYfWW8hj 8Rx9jL8Gi54uxel9sL0y4lSA3veX8eo7k3euvGKKE0eSuHYq0XAKAMJgW4DeRmQU iilg6h1AEdbTDxAlkwwr0bKMcZtsidLX6dxIe+zZj6Hck224ms29Sbid/RLFGqxb h97dTz+YwK3dyC5LhD/2txVCEPhBZW3x5sBwbORzllI7WcqB+i/ILlqJwAJ5bd8q x0NP6+ZiepeTSUpeZAL/Z3N49bIO1QNDItvjYxQAixpliMoDlQZPPzPRWtZB/jkV BeUjeiAzD5gcQ8h1EsaRtoESEI/QX/e/kfU0YtW+jYrt0l3GS9rs5IBzod6Z/Igh 9wSZ8JIjnUhEwlibTTdr5xk+K07u6LlhFs3Ho8lzz9bS+YxqOfp/htfe3Mt2MWhl MCCMl1mLVGUyK63znvWeki6TATrqnqokxST1BMKA0rSRxRpzJIbMcAkn5cFLbyZt nP962O8+C8qgTvH+SIB/st+9YtqpluN/gOIzb5tcpI2bwo3N7nZos/uJwYOybh8Z CQgDJSJqBatkldu0ZG0i226k/APO5MkyKQflyxaOvIL/f79i3tSYDIECPKNf1Txx oXkL21hHPbflnpbbw1Bd8Qc9Cjr2Ku8H4Gy7xSE1k+KNkJQv4VZ7nZX/35X/hAS9 Wxk/BtVh8jJQ6BOsB/6nS1i9FIrQOAl1XmksvRzXBRdxUnRSiqa6ctGPJF2/Pvcz IkceEsdrFLrG9twoLP8XEYlJ06F/aLCROhIGGuy5MtMuF8kSWvDu6m5bZVARm2V0 VvtcuEb7OlOwKA/vsC771zEGqnojpIYuRywLu7W1fS0xbwWMFzrAHHadgRlW+MAA DaKbPtXMPjyx5euHkCk0Fx+gkqK7N7MJQzakbcrhojKIR/1lZdi/lq16hc0f4TJV OUZETZL1UqY2q/RU8kdDySa3sLc8RqqKB7mwNswQQL4lMX4PlF2FgbdOKCJusjFT 3BEA0/7YKwfV5x3h/OU/dUPSGykpb7zcZBVmJcWIzPy08xTevNb7L/Efgamfrn2q InzZVdp9pa5FhajpbZ+bCAQ2xnM56ZBV7o4Fw7YaXVP+yJ6EdJyFyfalHmWcIfFQ M/CtTNK2Z3y+01+YxrQJ9EcK+v6+kjyDMsWTfJgWgCyn2dzQ5STip0Dd33wXj8+h 2xep/nQ8ez1O3AUrUNi196dtmlEk5XjyIbGnCKvEvqu6z3GjSy+Rdf12B25h+XBm FdW00pdTkscmhm8gNbvVQBR0B/9GfNCE55zRPOrZPo/2GduZwr8ZfhgBDP/945LZ Vij8eiOftznUt9vxsxpCOu+JUA1vrbspnA1R4qw9+GLgotW6Jkh3tuHa543lfDmm BNFNssANVkwXWkqlSUNBEudfW+om9lQbb7hxxI2t8azNldYOt6vcRU1ZzGTj7BW3 usUXzcywoAyEXoPYK++64BUPIYcOhyps/OMuyFbu0zANrLsSwUjB58iYNPGSo09G VFCrhS+q5l7KEeyfUu07DB5rw+DNDiB/mhOvtysjbo49nBb+ccFtfP7FtlFuv/In itPR5p46km49q1RfnrrHFw3dy0fHWpw744ivLAVUhcLB0FZpwQ0rxe5Hi3VvWIiL ZEiZHHuw1os1sG+TirapFDjOefnYRDZSEJQTNjGHI8Js3jzgWZX41SU2bnf7eaNp nM5o3O/ZwdN6K92J4jvRD5W0UfmCWabQoLkJ/pkWb9lc7CipkkUUaZdXDwKk7vBd SMuvttpQJv4iCUkJNpCiRyYIfw/hUgGOHNpSZGuUQRtk3HzrEo6wdsQf2WY1tZfD 4q9607pzDGWzRFR0KDPKfrjk7/Q/2hpm2JGMHmg8dd4V5JQXljqyuffA/c8yrlPj KFQHEFrg9CI2oA3UUNgpDaKxLr/ucuA9bjMI5APs37Eyk5jO5fDvh5XKDTXZUzaW 9Nt9FMH+FAHXIGTf3URvYJZ39hUS+Gngv1BDPNpkH4XtBytlLDeETaxyEjPIMkJZ eT0v+g8jVpHk85qJTxbuqZG13u610ZDmyPoPRAqfdFsNAJub8OpJLj4YzRl36G4g un5TG5TE1v0T2fRkWW8fZxNiHzblirx45dXF+xaB+LfElQWr/V+AFaWiKfy3KpP8 G83FWASAMlnK5RQf+OVYeIAihKGoPGygbMsfpn6E2BUXK+E8Hz6sQMfFxqllGPgk /f87pzQAOpAbPgR+/XjGD3YGlA3zAFMwl3rCBo4TtJ9wH35vEJmeaRHx5YL1uvVA S+/krsYMKQ1hjQKDi0Hlyx66sl1rIYCSytjSS9Ae6lb2rPSqspRyShpJL1S+Q5al 3urZKhrurGiJmKmZrpZomjnKr6l045Fs6XR/nmaLXBf0j3y5LJ5OsWZ6Rwzfajk7 n4pdaotHgs1LdQdm06bHUIeFwgUBno5LqeC+DXZhbV5I+b1I78H8BUXDPv88dPiL 66wGx+Ha6M8sm335ydnJL02cDFOJGywdKc1XilXPQP/c6EYUrYVbDQXebtgDac78 aw+8vMFy0DeKtlKfEBtm44eG3BNQI+jnZbg6TQtkp3noD5OYFjjWPLtbKBPgYU8b JvTOE/MTblHC34Kn5rlanArdLgMH31FLbr1o18gRQNws6Jyxv3+eFIA6umvSXiFk PNNMDFqTvJBJtlMhhhH1kVItZvzol8bJDd7ed4ZQie7fH3ukt1E3iZMW3fRgqomq ZDb4GOWAKfpKrWwH+c6JXPO5+1URwjpTmrZj7wPubtJMMFDbtoTBNJFAaOMwlSMb z94AP8kd2gr/pREl7Ih+vJoyruVNJybKjZenidQlAjbhNn6hBIUSGDyC4mKLhnD6 EA1DTxYjZupajP68DC+S/bAO7k9veQ2IWTdbFqkjCC6pUkxFp6DuM+ADZdbR7Llm nWUjb5CLLwNuuE+CMFFYUrK8BdnC1uaqb6fQiEo42u9lAkX4pRIB6TVnTtLa25ub CUWpSWpu6bEP1nIf9YuNPIxZt8YAA97HX3fSKY6PcRp7QDP9Gu2kRenZRfeHpoYz bkruh6jWXIMhtZiWO4+qpFW2AQBro8KSrnNxjpruxy/DxbbexJt7LNv8ZnlaIKCc S5Bv9WHfxUk6tJ03+CqxDqsRHy3QkzwnzXLnNnRUiNKi/fID1YrbSbPvnYJwDWi9 Dc2eDOdWJck71nnJSWP6nRcskNxbNnNnJ4MNXKmbGlJ1G+2F5zpqnO8O9VbMb5S9 9vE8dW2LFQ/7QzCqakL4X1oKzjEyZ/LOaYgH/KtSwEZWpeI/v6w41AVOTRgvkQYY kP//kDFPr1WepWqZ6dkq0JrpudTkddFoIJmRvxRFYFkBwd7KthKOzI3kZlB6gQss /wO04e8Wv/iGoNJJN6SxapJpe7Hhx7GljesOTSM0Vd6t7NgELWUtwZ0SGpoMZAup HIKT1tAt1XsorC51jzksaEYCxUK/Ceb/B3y9xYpD+97BwZsHheAG3yq05FItHKZB njzqBM8fvKR4GyGRN2HGcybet+FEDyg1Tl3kpjgbqagqCmqwZdGuo6XwW65NpODg TIIqaXudEOCo6VUcA68S0ZfIF7KaBzAinQy8Qw4m7WlzQa9R7peterNTeMDj9fBk 6daJNJ//AUwcDSUa1pfL4OlkjmquE1JYal2crA++XuTzRKIQA6HmYt2QEZjqNyG1 S0j1xhFgebNEmwyqRnwrhnWIZNJpltZE9iGfeEXi0QtEZn4/A2fNaDF1R/WIUVfA M0tdSZz9rL+Q6UxsfFdlTszcTN7P0z8XM1IO9q5HMgZ7FgT/FozDhbYW6cNN8Haa +wYL3foW8CV5NWxUbYD+OZejHQTpbqTkYGvumSjPWtlqUhTWJ1NZRa5Y6tH2vwou Lh3IA43DSYVvhDcZeVwi3C9HMDRPkr0ZkLQYfpL4jqeE30Wodg0JWbihDnNscCLq Cl2BLU/o9PydlgRkq6vWNXtRIBoA3yaMytFu4+sHIrhGkxXwpmnP9bOgjSeNqmJe V07r3FTso7cnHqOQUHbRmsogEMALUegTpDUE0+mWyrdmKRvkbhm2Rj0MLrhpZjqv E2RngcJ3pWVX3Ph7oVPfdRWwMlL7lAgSTY2LHwVdUjKPL93wX+mY+ZnMWNgw619A ix/bNX68Ufeqhp0mUVrJg2gdhlYiRLDocaVZdoB3/M1ENLbbAT9NyQGp5uP2wCZE deE3ttI8CNRq4HQ+BzHo/bQiflG+GwcX+3rKn9m0JA6Jx3FlsMh+a+mKhILoj/VO sy/nzz9Mzf0nh0SFkwv+mH9qTc+paX33zV4TQeyR/zRDs7/vGHR5VQsKArHkUY9c D/c1HjXlbIlX5MHGRv126XdEVFd0FAxdZBRw/R+21MG3FZ9QMvslWcQxYiOovXmP DXyiNA10jzFZwGPSVfoh2FlfcIHnNUdaa/2oY0vVmUcSpU4CRS/cTC1+9X6CHLNv Nok1Hcy7MowgPEmqpIIOTI5vyXDr9PtKU9z65fvYEhj+dLlBvK1QCLl1TBPNg8PU MJLFcQlO9Owwe1GbWCoqJ4+3Z5O41jIckkjkrASGuwxiihVNuAMrmIocPlmsdOIW DikNtSNX+gLb3BPEjVrt7aZkotdvzqD/ZPZdcTN/g5/LG01ShUWBhMjMnw4eW+eF AZy8xMymZFeD3BrGEY7UNwpsYlYDJq00FRwz6pRw21oZkQwSxjLUnwi9M7pNJ00K cddZHf8XDUiTRvYxVvC+piVb5bPzilIHHhbG3RdVU4RPjeQdfvE4zffD81n81az1 mioDUSdGcPJM/fyBFnIkQmfF12m4bheGQuoXWr801LpBmy+x0W89tSWycvPY5gTi 6Ua/novyfnTrYTWaDG0I+tX/3HWS+FXDj/HSCMv3kalxYrAOBDQoYTasdJlARVwU aGeDcgel9yCmRd9PlPGUX1HQ/IA2fLVkkZYV8Lo0K3x2EYJ1cmInm+c91CztpIb3 2Nlejn1lg0CnvR4sVZRh+TYe65eMw4sFWWIJDt6Ad2fxdP/SBCJa9rUbbWV/K9xc zxU/GdO5uWOiGdYm7Zf/8gPBRWozxw0zi1DrZNGrRCkPNY95MYHSTD0GsFy29ySu ROy5KEaXXxNzrqWhG90sQu43A31OermYR2G0ERbl7gSFoOhI7WusYwW2Me19tySp 9O5rS1gJieFJkpAKO2mVMGw1p1b6U4snyWYW4+gVfVanYhJ1RLof6rzgKhazMfIs HRkwh4+Snjup+3Uu29+R3p/nHJ9/cgMAKgT64Ll/VZrf+krym2DirZr3EbnAb4HW r60Hm8cR64WjTBmSXSgy17CWPPtTuEqwqU1TLNDMuSeLT6EQujEcHqYwdjLju3JQ Fcfz205gLjy+MsfuiiZznyTIANaSBPvyC3ExJ5Kagvq2pq/W3rURNV79vJinjOeu qa/EPiRIddNSMF2yJ/7VFTsHvJaz03y4AGth/FQxQSDk5F94B0iGEGeRKbSOjaKq wucfsur3OHmeDGmAdRrB24grkSP3Pnj+AVj9aWQxlmWGLBCnuGK3iqy2jw7+2ekA PLSBTN/YuilrOF3Cgv1bwgYH4FIS389LMnYyzovDlrRFWjchzSmaGZqD0QKXyjmd evpyQsUBBlvZiRHjRgjhlML/J88hFCtFehLuxuzqw9JSY8Bhauevw7rwKhrw4ZHB 2GxF5IXrRuar9U3ofI+4vg+THR0WnHpFIjNpH8zamKK+vD8IkBy6v4RuNCtLzvM0 ulvJmYe+Ep5E2ZP5Y7pTkSiHUAtlH8PKqTwOu51W3GdQPDNR7q31BSpEJS4T/fTl CtyiYXd+onkhzutkyBeOSYBtH8GpLzYPc6tiontupT0trC6bJzhm6Hj7JtEhVx4u 80PRd7ZnhuLIQ8K5Vc3uDXjoJ7G5xCr7pOk8rQsXAzuVTn3djUZDj94AOEgXXo3M 0TdfgH/ZOsF1nCkxQ41cVqAyG3801mx7vxkKfeqA9Is4lIHVMLt7JuT9BYVxI2Wd O3f2oxGaYjJdA+GYxM2Xug+9F9ZfmsU+6/urCvvw8mw8oW27gEN+iMrwVrB057Fz s5O2mWG1xuFdoiOjLaGaKOYoi621w4mRJjZuTLv75QdsTOlXdcTCvqsTZEcU0xdJ 6DR2Gcz8OlA0jEbFc78W8746gjATcevEqtVr+XZEYlTGR5sJ/CRNtD44FCTewIrl /7Z30d8SKw3aTm0JFgBV3Ki2Pgct0Yz3JSpMd/ELMtPD9zNcLrWj+iKQ9ztEvRlg eMl+7LgtadAjQcJDXxIrceKvahh9auGI1kqO4f2o0klw5iGXXWMid1dTH6GqtFrh JME0R9oCwUIrcFoXPFq+9uvRPUCJQzS/wa+BUsenEF2i1Bos6gtxfI8EoglUpLPq IhwM/cAuuGJdPDd2762dBNMZNtcwI5/AfxLFv/dJ//E562YpSvcnis6hw+DJphGx 3NJ9EIeNIuv5K58mGXDaBgh07pk8flKtk5OAX9kT1MXzCYhjkI7V+m2O9OHoV1BN ylktKz7p6a4TjpFfci5dWUkcSqq5g0W28I5155iFe1LI+P7OU1Be2dpldSkl/OjJ Z5YKQg2hhj4Ik4fbahD+/nXCUWzoJzI1T/Fszx2h20OsnRrCDwucSz4MpyXd7afK DxOjRQJrK9NqZOXiGy2DFzvpsQrqvu1LXcRGDFI8FX69th5ea0nyInIrcwRcJcqV V4mgrTMMgaXjAbfH3OLARNnwwCv+CMlxk+Y+zmlOqbMgwJmfgG+H1TVDOnbuYNPA o7P7oYOKNuCwi6sXl5I6CUray0WNSxW/ySz7J86krQD8KVUsCOlyUI+tJ1WLO7yN zes699oPUNEXg2rxv60F7k7/FQzwvoNrTqpiMU95nrc4D1WIWtZNqHRhgcEF5g6L lZWVJsdj6SfA+yRzZ9KZ4CMcJt9bGg06qXVfIFbhlycM/83ickP0fC4uQkrkdu2S QwXkfsWFE/PNMAnUoW1ctgP6VD2Gha2gXXNzgD9WZOM6b30ZjrIxr518q9xDVWfX uYNjlUVZEhwL1yC8RSslS+AvMjFmFiHphQbtk8YO4/hq26uBtK5TTNKNhH/9Ep7e iQ3veRS/XsIpI3FaS9ZHJTdKA+ZiZpTgUW8HGWLVCswIP1IFfprbrpcsGpEWz9wm vVKtwVxh1i58Y6DqZv18lWgdsxJ+26cZOJT8aPjRBha+Wx/eqY60e4KHN1BYd4Rd 1mcGM5XPQht4d9LjpuoaolgZwWgqH0SpUpEgD+nbq85BQk4oBXO332JF/RofErJs mqUncAoo/VJx/FsqZVJo4XDo3SOueOKYIyrL26MJQ9H864L2gxq9hZmrzYW2iv9n l5w9t+f70hsk69i2Se9Sz32WudbHDcnR7SaGvCKHKogAvd/7jryVA/ZgcLlpgMcZ GB2CjurQZuBe4OXvwmJxeKwB1d98in2sM5t8LhwKEvi8JDvi5Xy+z84ZmWsULT+H qmQeszsimFbeWPRi7LT2rm+/AwUNUY0XoAVSPiludjR+f4ld3CZydPs5ccXm4tGy JcYffvDtyIyYMCQjBM/zPi+qKTwyeIEwLZcfOwXeDpKCdtSOxBNfKJgupEWPoj0c T+1C+AomgQNLd5kfBc3/Y5v3hHH5eAMiieejHt7yYYQcTbok5OfJEsl/ZsG0JBLO TKTKiUGLVgYekiSkpYZaxe8Yof22ZUDLYUszfuMt/Kul0EdXxkFT3EYWQSe+7db4 lj2+AAI9ExtqkaOLn/JVcoCDRvIB2iRhCA+u01eluYoQaRww5/qEWU31BsSC2cKe BimZyOX2pMULVZP9g0RSdj68J6415F2Q9nma0GdagvQKDDqWqqWwYhi/nZvVQVRd rtOIaKvjRXw2I699eKTmLOvzY9s0Sm2jMeCb+QZEAnnUlWgraez9PHy83eV4PBw3 9gyQdM2zipIWzf9j9hYRyfSt4myxSWKctgo8r7C/9DJYbRFVeG7zixnkm6yu5kmC +fTxuaybximy8znEW16Uz9toAOQBhYEuS48P2/whExCPUgCqns8OeCm272Evf+Ki ybgAOkZfRRXbU6YNqC3gM4sZMk2u0RBLj6nz+Quw/BMddiIJoIQjF5JJIL+yZEWd FDTlbmxKHVadU+4F5g2Z7XRw5Nh4lOY0iov2n5Q4S8f5ISyJUm+UsYFptHqKo73Q CGIGu6G1uvr4IZMUAN9T6M34oOV94q3pateKV0NbqpkAlt7iOSXgi4ueOTHnPKPN /4dp9YsbxQhYgVUJbWhxf+x/Z7iwgn2JPEEfkONrOmgepvR9APrcHCIv31rzMS3S YnVfaAGnqqrHBYsNHiraBeUKNZLgefTtQBxfuZkrtmqvrG6FeS9AX7l5IVYLq+jq +IEY3S9vsr26hABtCKgszVxtVXnQrCitYaN4F+ssfvmLuHPn2xHXkeMGrf1728mc 6pDHVR8CVi8u+3Og606F5xfcp2qkfuzCoS2AyvRg5uSUbRJdmkfA/WVerjOUwmPb ImWzb84L83McI8qMGrjSi1MNfYiWQFB9Qqs7gOm2ixdiYNqqS6X/7DPrXtdWoiLF M511CGQTPWdIr18snnupLdLzdX+iFzmD9uatc41hhZNyyTpqr8OmYHEwQq2WdyPK r2AWXk8nJBzBNNqIZURz0zousFA8sgCk7mma0CDNj+cM1v03ekXrxM+GAP1RUBqL Em7VrPpSkJtMpWV+LuaDhHyh9XesVedOHICSdazc0qe4ZeLdDDJ9F3kCOqslOFLs A9zFFt99hfWyR62iKVT/fcDWLO0Jof7lTF+z/G1GzWyMm8jAeR5mEeJVi8BpQqSN 7yI1SdHr5qFrJe9Bbd5wS5pQctxtg8zItYRdXOy9OAo4W7OrGOSYhXAHheSE1yIC yLgXl0yex4fl89UEOUUhK2Sk2H0VcoZBwY0gG8Ynt9h8VQqe5MQhMG/bJDHck+5E i9gpSN/J7WvFCWVdnFbgthdRih+mLJ9/NIeHy3FJFnQ47OxHevEmsYTIBqjj6AS/ Y+W9L7QKOCiAl7s6e8sNBL6HLR3gtIxQ4McSfe5fmmbzvRSRyF6NVHf2FI3qrPjq LFdwZUwCaMWLJzeUcxe2Vdvl69mfK/sAM8Cm1cElQ2YAWAAta4xyTYbz+M3DskeP rkLTJE4aakesoHYhFQ54Uz9JDLd0BzaQhYB2KpQfF+d6pVeGbOlRKbz49GRJ+Cc6 FYt1c4KZa3Vrb6XBqCirknJbd4S8jCIoc7DdSzaob5ahwULUrKOvghbEvADeIvd0 b6sjeSbH8RDt51pelrSLAoFe4B8AbPiMshNKVBmuStZRUQ3/sEnuLkKsgDHDp98Y H8CJrticAQ+qJd972qikimSminlUFkXwSrG9Zg7IOmm3WdU+3UaftiSMft5lIHhb fimhrDyXDkiGjHb7D7piPansmoyvnDngdhOTcrLXFXQfnlvrfa2j99v9+l+JfBwN waJ/c9xcfLyAbb9nkrVUjIS69OdZPhGVf87y5Ny32EhsqM5IE8EbpG1E22YvtQqq nuqeY4eLT/ngJDIL6zD3XVzhxUrko/QrHu71rccW+XX8NnA7lEfZRjtxkzPwqSiE iajmTrcCQJTDqyPxSTUpnESPQUegVfgF25yghBkWbdWKCf3t3LZxozV1IcSRu445 nU8wKXkmKYnmFQDAe6VzPipmkctgpW4zaHddP7Lm0qhiWkWlB1seRkd7bAG1uQaZ GKYpPZ8g50VEQhNP/lqU5FQ3q0bRibcIdkjXNnwFlPWBFa2LfTlXDmX/dE6HLbUB x+2l4KB/EIUfQPwtleC/7oscZoZbOSUi0pBfda2CXUDo5afB9xwtUaFV1Ncl6u6B ThPW4SFB6oaExfUCSlQ8dLYtqrluF5p/u9mTCdXNavxpz39bRsPk0zzWIdJ7e6Fg T/Slk1+pHqShrMUYacjon3jkVi3dhzMUwZBIuVfnA0ZaUwNEPrW4nj9HgqFAlSOT HWwrVrRaKIubV7v7ra9rCb+8kjQOwxDO2uma+CarAf6IvL+Qn1OhWa+Gw0lE6N6g zF6RXe+zoOk+wAjI9HR0LDYP2Wx76E9N0CUMvJS6gNS5Xyan17rzRWd2CJ4KrEGK ZSXbqtDX6HzZ9C0blC7d8IYhVq2Jog8AzljkTHb+Uy8PbWnLKtWIARgCa0IwPray v5BPDuXyXo3hZOVMDQvZ2oVXk4XRPuf8GkmWqlNSY5klyat+GxJWxZSAfQkEC2EJ JgVGHqoPOlJc0AZg/gYqweP3OzNqqS+4EAFMByw4VAAJOArzwRP98r26mO10iucQ 8jCQ+zGg1r49naP/orOAwPf0ovGe+nsy5ZAaz323mVVkJXcNFZFjMl7ZLg5RrmyS IKQmSET2KqA8EOJGb6G00wqGf2gp36bQktgI4byi2Oj/bKr3TLLLrB8AD8KxaPiv U4AHX1EM450LL6/lwoA2SbMy96Cg8yqT6heIDU/oP+mKlTYi9gxK+MkDy8TrU2vN ZC8o8Hrux5w3zIc6pzODQ62x35Uk6bRNGaAEDav74JK2sX6SXlsBLy2Ke0rOwfFk t9+/GoMcytmojQVn5TLSnwNRDRD/2rNGIPFvbG4XbmiQgoreyWTUp2EZXYp8Abkv bk9r23Mbx39NS+qL46l0vH0XGKFP5yXsGrON3h5Rdr2ASjKP8uJ4ztL+MRaVe7ly hM3t8FRAxct8R38glEBXYxEj6tmQVwRb7Y9sgNvA1HbiysqLRBrIRgl6QCYKwd30 ov/rWUgv5McPh6y8XXwiOermElFYCa2y3cR4rY4ERGRkV+HndqAMNSgvw5i8Y0Yj f1ysnL5Uiip9Olgd7q/syvAD1X/N+JpytgWkmN0fTQD9vL2wwBTsHODwrooatW/h aG2sKTjuFW/wJ73DY6eJ0DfJfDo3lo85jbrVMc75jCupTsfD7yPRhcdyIAOe8sRc DvjjOR33Wz4CxYoV9feVSbVuyucS7tDSGYWvorYViNPrcKRNVFSIUI+nfySWuhsF SetqFoWFZhQRTAJ4m7KqfWfaq2lDiB9LCwBCmqdMjAAn8Pwj1WQWJeQ55FbdWgCh e+OY2cc9G7YpAnYeDvrPFL25xiaGkZIGZZsBfGZgUBkSp0gEJIhsoP+ZGB/7xR4D pwMEpo13quAB5A0M/MQ1PEKwlw5T+wlBd3ndM99VYDAvBGoKGA2IHJgz5MXQ9kbV idc+3ECGAVKnIa/4kUs/pEMkX8nuU8lkrzi3RJ34iIoeOc0KoGkYRZsJ1woSAlRT JNtbwSlEvihbMgLYf9ChwvGkIeQYqujcnWv+eb3IShRxNqZqfso/Y1ng3JFsmb4m Xq2wimE8gsv1LPXhxZU36z+b3uyLSjwQgfdcpZ8OxTd1BQzFVP5NcLgYlcoPBWbM BY8Sw6J+2goXbVgkMPhqyvKPOfrs7ozTKZ5KEpiKhNnuOLEJaBWolUk0AFgGCseA O0kYu2cz0gq9iLH1rV5q324DitsTAcDaSnODuQ6A0VarVvJse60gsPNcIfMYseCQ FY4OEjlth+dGYt55ULEzOieaVan1U6rm0uczBo7usbLSDYpKkqiONxZchZaiL6b8 ltLfTAiQWo8aWloR1Q5enVkQhN0KexhIa2bdCpuCfdtDUAOteNka+6nspU7FH3YU BF/GdP+nLLnI/jYj9VqcLbN+XflvgakXHLzn57Ik3jtFsZYpfREL3XjOb0ZodIFm lAExZIy1bawdHSRahf0esxpGtswZrHlfyP8LwpAdazIQyYqxD2+R7oD1fsKXC7uG hvYairIOJOZ3mmEtIG0HkYNxTH8d1EuJHtXXcFWRboCnMqL5KaqQUdXRdisPLeJp P0dMiGVJAL8dN4A9BBz1q73e0HI/Y+OzNymNL2lwQUWKPrO2glgP9Cr/7NOOsbYU LWeeVz+jqItS8Kf6HXsiN3KVUsAwPk3g1I/IG+Z7ADHOiwayntk3lufWWROVo0Io 5A92tAlAXxwFJjkWAAPbCvjxJF98Z5KKt+8cP1VIaX25qHxsGfmX1FFgiCUDN0XI KyWpeuacpI8Qnyj9G3U3gqWhBAYkNne1EP4TwFA8Q9E8fKV9NgSA73kTMdqSnXyh 48OoSW7nWA9D188korO46qM8k54q2NTmoiEz5Q27U0t8/gnGzJ+h3jJjWI7kbUTr nUgJaChsce/xgCqI24c6y/Ypnjc2Is6l2ja8siIXhJehFS653J3TX6D3FwZZdw4r XID568OB5pb8xWiwoahLPsuHdclN7sZY6eb09TDXLK7hixezX0ntwJY9VVQUzWvP c8oUUuS4/F89+fcwNJl+1Yd5dEj8Z158ImSd8i3CxX8/ErnmJeaP5En+e/PJ+fYu nATo1VWUlyhD6lpjBbMs1HGELvFj/8TlBT/n5frmMvOx7TfkcCtYrA/EAuvV5DeO LE0iFX2UHVV4WaGLIXLE+woDMvsSckdtBsbxcSdYl+1tsAy0ok/OLdjYssseWj2e KJDL6XEC5IjTiO41UcwpJN0W5TU4aUcGLBbr8fO1wUkPrthezj2bGYlHeRIKbONb joQ1SXYqXzeokgJo1iEsS0xWJ+6TKSBgnn8QMLD/hY1SkcuadTdowUG2RTubhQts RfH4YgAHeiEXItKiCKLvmmuK3FFplRN9mtf4f+SJgS9I2tMfqokgMAUyVRdJ7NO5 k0gW2JRPg+qL3PXY5JQvsIfPATxSBHldSnYE+iLctZ+0hQRan0b93oQPpdED4xHR PqfcEOB0y1sfJfU1gYLU8+PgCcQdQhMGegMu8gM3cU00fa5nd7GCTtzD8ZtMAyR1 HW0MjzxbzleQuks41t5N4xyZBbC/nYU2dtkFgkOQ8OQVT/YOUlQwOWrWM4LozvXq g9p6Jfx+zW6C8i+uSRrniGNbD3PddWT1U/Myn8nbyvSvXjysL7hPxuHyjZmHmhad b9NmVcROFHOKXlLWVMbimXXZTbeqm9ouduKVTmWfZSq86YaIeIfQMJW7iER7 dovecot-2.2.33.2/src/lib-dcrypt/istream-decrypt.h0000644000175000017500000000244113123174404016452 00000000000000#ifndef ISTREAM_DECRYPT_H #define ISTREAM_DECRYPT_H struct dcrypt_private_key; struct dcrypt_context_symmetric; enum decrypt_istream_format { DECRYPT_FORMAT_V1, DECRYPT_FORMAT_V2 }; /* Look for a private key for a specified public key digest and set it to priv_key_r. Returns 1 if ok, 0 if key doesn't exist, -1 on internal error. */ typedef int i_stream_decrypt_get_key_callback_t(const char *pubkey_digest, struct dcrypt_private_key **priv_key_r, const char **error_r, void *context); struct istream * i_stream_create_decrypt(struct istream *input, struct dcrypt_private_key *priv_key); /* create stream for reading plain encrypted data with no header or MAC. do not call dcrypt_ctx_sym_init */ struct istream * i_stream_create_sym_decrypt(struct istream *input, struct dcrypt_context_symmetric *ctx); /* Decrypt the istream. When a private key is needed, the callback will be called. This allows using multiple private keys for different mails. */ struct istream * i_stream_create_decrypt_callback(struct istream *input, i_stream_decrypt_get_key_callback_t *callback, void *context); enum decrypt_istream_format i_stream_encrypt_get_format(const struct istream *input); enum io_stream_encrypt_flags i_stream_encrypt_get_flags(const struct istream *input); #endif dovecot-2.2.33.2/src/lib-dcrypt/ostream-encrypt.c0000644000175000017500000005206613165463624016510 00000000000000/* file truct dcrypt_public_keyyntax * magic (14 bytes) * version (1 bytes) * flags (4 bytes) * size of header (4 bytes) * sha1 of key id (20 bytes) * cipher oid * mac oid * rounds (4 bytes) * key data size (4 bytes) * key data * cipher data * mac data (mac specific bytes) */ #include "lib.h" #include "buffer.h" #include "randgen.h" #include "dcrypt-iostream.h" #include "ostream-encrypt.h" #include "ostream-private.h" #include "hash-method.h" #include "sha2.h" #include "safe-memset.h" #include "dcrypt.h" #include #define IO_STREAM_ENCRYPT_SEED_SIZE 32 #define IO_STREAM_ENCRYPT_ROUNDS 2048 struct encrypt_ostream { struct ostream_private ostream; struct dcrypt_context_symmetric *ctx_sym; struct dcrypt_context_hmac *ctx_mac; enum io_stream_encrypt_flags flags; struct dcrypt_public_key *pub; unsigned char *key_data; size_t key_data_len; buffer_t *cipher_oid; buffer_t *mac_oid; size_t block_size; bool finalized; bool failed; bool prefix_written; }; static int o_stream_encrypt_send(struct encrypt_ostream *stream, const unsigned char *data, size_t size) { ssize_t ec; ec = o_stream_send(stream->ostream.parent, data, size); if (ec == (ssize_t)size) return 0; else if (ec < 0) { o_stream_copy_error_from_parent(&stream->ostream); return -1; } else { io_stream_set_error(&stream->ostream.iostream, "ostream-encrypt: Unexpectedly short write to parent stream"); stream->ostream.ostream.stream_errno = EINVAL; return -1; } } static int o_stream_encrypt_send_header_v1(struct encrypt_ostream *stream) { unsigned char c; unsigned short s; i_assert(!stream->prefix_written); stream->prefix_written = TRUE; buffer_t *values = buffer_create_dynamic(pool_datastack_create(), 256); buffer_append(values, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)); /* version */ c = 1; buffer_append(values, &c, 1); /* key data length */ s = htons(stream->key_data_len); buffer_append(values, &s, 2); /* then write key data */ buffer_append(values, stream->key_data, stream->key_data_len); i_free_and_null(stream->key_data); /* then send it to stream */ return o_stream_encrypt_send(stream, values->data, values->used); } static int o_stream_encrypt_send_header_v2(struct encrypt_ostream *stream) { unsigned char c; unsigned int i; i_assert(!stream->prefix_written); stream->prefix_written = TRUE; buffer_t *values = buffer_create_dynamic(pool_datastack_create(), 256); buffer_append(values, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)); c = 2; buffer_append(values, &c, 1); i = htonl(stream->flags); buffer_append(values, &i, 4); /* store total length of header 9 = version + flags + length 8 = rounds + key data length */ i = htonl(sizeof(IOSTREAM_CRYPT_MAGIC) + 9 + stream->cipher_oid->used + stream->mac_oid->used + 8 + stream->key_data_len); buffer_append(values, &i, 4); buffer_append_buf(values, stream->cipher_oid, 0, (size_t)-1); buffer_append_buf(values, stream->mac_oid, 0, (size_t)-1); i = htonl(IO_STREAM_ENCRYPT_ROUNDS); buffer_append(values, &i, 4); i = htonl(stream->key_data_len); buffer_append(values, &i, 4); buffer_append(values, stream->key_data, stream->key_data_len); i_free_and_null(stream->key_data); return o_stream_encrypt_send(stream, values->data, values->used); } static int o_stream_encrypt_keydata_create_v1(struct encrypt_ostream *stream) { buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf; const char *error = NULL; const struct hash_method *hash = &hash_method_sha256; /* various temporary buffers */ unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE]; unsigned char pkhash[hash->digest_size]; unsigned char ekhash[hash->digest_size]; unsigned char hres[hash->digest_size]; unsigned char hctx[hash->context_size]; /* hash the public key first */ buffer_create_from_data(&buf, pkhash, sizeof(pkhash)); if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) { io_stream_set_error(&stream->ostream.iostream, "Key hash failed: %s", error); return -1; } /* hash the key base */ hash->init(hctx); hash->loop(hctx, seed, sizeof(seed)); hash->result(hctx, ekhash); ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256); encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256); secret = buffer_create_dynamic(pool_datastack_create(), 256); if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, secret, &error)) { io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error); return -1; } /* hash the secret data */ hash->init(hctx); hash->loop(hctx, secret->data, secret->used); hash->result(hctx, hres); safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); /* use it to encrypt the actual encryption key */ struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, &dctx, &error)) { io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error); return -1; } random_fill(seed, sizeof(seed)); hash->init(hctx); hash->loop(hctx, seed, sizeof(seed)); hash->result(hctx, ekhash); int ec = 0; /* NB! The old code was broken and used this kind of IV - it is not correct, but we need to stay compatible with old data */ dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres)); if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), encrypted_key, &error) || !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) { safe_memset(seed, 0, sizeof(seed)); io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error); return -1; } /* same as above */ dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed)); safe_memset(seed, 0, sizeof(seed)); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error); return -1; } res = buffer_create_dynamic(default_pool, 256); /* ephemeral key */ unsigned short s; s = htons(ephemeral_key->used); buffer_append(res, &s, 2); buffer_append(res, ephemeral_key->data, ephemeral_key->used); /* public key hash */ s = htons(sizeof(pkhash)); buffer_append(res, &s, 2); buffer_append(res, pkhash, sizeof(pkhash)); /* encrypted key hash */ s = htons(sizeof(ekhash)); buffer_append(res, &s, 2); buffer_append(res, ekhash, sizeof(ekhash)); /* encrypted key */ s = htons(encrypted_key->used); buffer_append(res, &s, 2); buffer_append(res, encrypted_key->data, encrypted_key->used); stream->key_data_len = res->used; stream->key_data = buffer_free_without_data(&res); return 0; } static int o_stream_encrypt_key_for_pubkey_v2(struct encrypt_ostream *stream, const char *malg, const unsigned char *key, size_t key_len, struct dcrypt_public_key *pubkey, buffer_t *res) { enum dcrypt_key_type ktype; const char *error; buffer_t *encrypted_key, *ephemeral_key, *temp_key; ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256); encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256); temp_key = buffer_create_dynamic(pool_datastack_create(), 48); ktype = dcrypt_key_type_public(pubkey); if (ktype == DCRYPT_KEY_RSA) { /* encrypt key as R (as we don't need DH with RSA)*/ if (!dcrypt_rsa_encrypt(pubkey, key, key_len, encrypted_key, &error)) { io_stream_set_error(&stream->ostream.iostream, "Cannot encrypt key data: %s", error); return -1; } } else if (ktype == DCRYPT_KEY_EC) { /* R = our ephemeral public key */ buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); /* derive ephemeral key and shared secret */ if (!dcrypt_ecdh_derive_secret_peer(pubkey, ephemeral_key, secret, &error)) { io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error); return -1; } /* use shared secret and ephemeral key to generate encryption key/iv */ if (!dcrypt_pbkdf2(secret->data, secret->used, ephemeral_key->data, ephemeral_key->used, malg, IO_STREAM_ENCRYPT_ROUNDS, temp_key, 48, &error)) { safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error); } safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); /* encrypt key with shared secret */ struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_ENCRYPT, &dctx, &error)) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error); return -1; } const unsigned char *ptr = temp_key->data; i_assert(temp_key->used == 48); dcrypt_ctx_sym_set_key(dctx, ptr, 32); dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); int ec = 0; if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, key, key_len, encrypted_key, &error) || !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) return ec; } else { io_stream_set_error(&stream->ostream.iostream, "Unsupported key type"); return -1; } /* store key type */ char kt = ktype; buffer_append(res, &kt, 1); /* store hash of public key as ID */ dcrypt_key_id_public(stream->pub, "sha256", res, NULL); /* store ephemeral key (if present) */ unsigned int val = htonl(ephemeral_key->used); buffer_append(res, &val, 4); buffer_append_buf(res, ephemeral_key, 0, (size_t)-1); /* store encrypted key */ val = htonl(encrypted_key->used); buffer_append(res, &val, 4); buffer_append_buf(res, encrypted_key, 0, (size_t)-1); return 0; } static int o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, const char *malg) { const struct hash_method *hash = hash_method_lookup(malg); const char *error; size_t tagsize; const unsigned char *ptr; size_t kl; unsigned int val; buffer_t *keydata, *res; if (hash == NULL) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: Hash algorithm '%s' not supported", malg); return -1; } /* key data length for internal use */ if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { tagsize = IOSTREAM_TAG_SIZE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { tagsize = IOSTREAM_TAG_SIZE; } else { /* do not include MAC */ tagsize = 0; } /* generate keydata length of random data for key/iv/mac */ kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; keydata = buffer_create_dynamic(pool_datastack_create(), kl); random_fill(buffer_append_space_unsafe(keydata, kl), kl); buffer_set_used_size(keydata, kl); ptr = keydata->data; res = buffer_create_dynamic(default_pool, 256); /* store number of public key(s) */ buffer_append(res, "\1", 1); /* one key for now */ /* we can do multiple keys at this point, but do it only once now */ if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, stream->pub, res) != 0) { buffer_free(&res); return -1; } /* create hash of the key data */ unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, ptr, kl); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* store key data hash */ val = htonl(sizeof(hres)); buffer_append(res, &val, 4); buffer_append(res, hres, sizeof(hres)); /* pick up key data that goes into stream */ stream->key_data_len = res->used; stream->key_data = buffer_free_without_data(&res); /* prime contexts */ dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); dcrypt_ctx_hmac_init(stream->ctx_mac, &error); } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); } /* clear out private key data */ safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error); return -1; } return 0; } static ssize_t o_stream_encrypt_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; const char *error; ssize_t ec,total = 0; /* not if finalized */ i_assert(!estream->finalized); /* write prefix */ if (!estream->prefix_written) { T_BEGIN { if ((estream->flags & IO_STREAM_ENC_VERSION_1) == IO_STREAM_ENC_VERSION_1) ec = o_stream_encrypt_send_header_v1(estream); else ec = o_stream_encrypt_send_header_v2(estream); } T_END; if (ec < 0) { return -1; } } /* buffer for encrypted data */ unsigned char ciphertext[IO_BLOCK_SIZE]; buffer_t buf; buffer_create_from_data(&buf, ciphertext, sizeof(ciphertext)); /* encrypt & send all blocks of data at max ciphertext buffer's length */ for(unsigned int i = 0; i < iov_count; i++) { size_t bl, off = 0, len = iov[i].iov_len; const unsigned char *ptr = iov[i].iov_base; while(len > 0) { buffer_set_used_size(&buf, 0); /* update can emite twice the size of input */ bl = I_MIN(sizeof(ciphertext)/2, len); if (!dcrypt_ctx_sym_update(estream->ctx_sym, ptr + off, bl, &buf, &error)) { io_stream_set_error(&stream->iostream, "Encryption failure: %s", error); return -1; } if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { /* update mac */ if (!dcrypt_ctx_hmac_update(estream->ctx_mac, buf.data, buf.used, &error)) { io_stream_set_error(&stream->iostream, "MAC failure: %s", error); return -1; } } /* hopefully upstream can accomondate */ if (o_stream_encrypt_send(estream, buf.data, buf.used) < 0) { return -1; } len -= bl; off += bl; total += bl; } } stream->ostream.offset += total; return total; } static int o_stream_encrypt_finalize(struct ostream_private *stream) { const char *error; struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; /* if nothing was written, we are done */ if (!estream->prefix_written) return o_stream_flush(stream->parent); if (estream->finalized) { /* we've already flushed the encrypted output. just flush the parent. */ return o_stream_flush(stream->parent); } estream->finalized = TRUE; /* acquire last block */ buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), dcrypt_ctx_sym_get_block_size(estream->ctx_sym)); if (!dcrypt_ctx_sym_final(estream->ctx_sym, buf, &error)) { io_stream_set_error(&estream->ostream.iostream, "Encryption failure: %s", error); return -1; } /* sometimes final does not emit anything */ if (buf->used > 0) { /* update mac */ if (((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC)) { if (!dcrypt_ctx_hmac_update(estream->ctx_mac, buf->data, buf->used, &error)) { io_stream_set_error(&estream->ostream.iostream, "MAC failure: %s", error); return -1; } } if (o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { return -1; } } /* write last mac bytes */ buffer_set_used_size(buf, 0); if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_final(estream->ctx_mac, buf, &error)) { io_stream_set_error(&estream->ostream.iostream, "MAC failure: %s", error); return -1; } } else if ((estream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_get_tag(estream->ctx_sym, buf); i_assert(buf->used > 0); } if (buf->used > 0 && o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { return -1; } /* flush parent */ return o_stream_flush(stream->parent); } static void o_stream_encrypt_close(struct iostream_private *stream, bool close_parent) { struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; if (estream->ctx_sym != NULL && !estream->finalized && estream->ostream.ostream.stream_errno == 0) o_stream_encrypt_finalize(&estream->ostream); if (close_parent) { o_stream_close(estream->ostream.parent); } } static void o_stream_encrypt_destroy(struct iostream_private *stream) { struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; /* release resources */ if (estream->ctx_sym != NULL) dcrypt_ctx_sym_destroy(&(estream->ctx_sym)); if (estream->ctx_mac != NULL) dcrypt_ctx_hmac_destroy(&(estream->ctx_mac)); if (estream->key_data != NULL) i_free(estream->key_data); if (estream->cipher_oid != NULL) buffer_free(&(estream->cipher_oid)); if (estream->mac_oid != NULL) buffer_free(&(estream->mac_oid)); if (estream->pub != NULL) dcrypt_key_unref_public(&(estream->pub)); o_stream_unref(&estream->ostream.parent); } static int o_stream_encrypt_init(struct encrypt_ostream *estream, const char *algorithm) { const char *error; char *calg, *malg; if ((estream->flags & IO_STREAM_ENC_VERSION_1) == IO_STREAM_ENC_VERSION_1) { if (!dcrypt_ctx_sym_create("AES-256-CTR", DCRYPT_MODE_ENCRYPT, &(estream->ctx_sym), &error)) { io_stream_set_error(&estream->ostream.iostream, "Cannot create ostream-encrypt: %s", error); return -1; } estream->flags |= IO_STREAM_ENC_INTEGRITY_NONE; /* disable MAC */ /* then do keying */ return o_stream_encrypt_keydata_create_v1(estream); } else { calg = t_strdup_noconst(algorithm); malg = strrchr(calg, '-'); if (malg == NULL) { io_stream_set_error(&estream->ostream.iostream, "Invalid algorithm (must be cipher-mac)"); return -1; } (*malg++) = '\0'; if (!dcrypt_ctx_sym_create(calg, DCRYPT_MODE_ENCRYPT, &(estream->ctx_sym), &error)) { io_stream_set_error(&estream->ostream.iostream, "Cannot create ostream-encrypt: %s", error); return -1; } /* create cipher and mac context, take note of OIDs */ estream->cipher_oid = buffer_create_dynamic(default_pool, 12); estream->block_size = dcrypt_ctx_sym_get_block_size(estream->ctx_sym); if (!dcrypt_name2oid(calg, estream->cipher_oid, &error)) { io_stream_set_error(&estream->ostream.iostream, "Cannot create ostream-encrypt: %s", error); return -1; } /* mac context is optional */ if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_create(malg, &(estream->ctx_mac), &error)) { io_stream_set_error(&estream->ostream.iostream, "Cannot create ostream-encrypt: %s", error); return -1; } } estream->mac_oid = buffer_create_dynamic(default_pool, 12); if (!dcrypt_name2oid(malg, estream->mac_oid, &error)) { io_stream_set_error(&estream->ostream.iostream, "Cannot create ostream-encrypt: %s", error); return -1; } /* MAC algoritm is used for PBKDF2 and keydata hashing */ return o_stream_encrypt_keydata_create_v2(estream, malg); } } static struct encrypt_ostream * o_stream_create_encrypt_common(enum io_stream_encrypt_flags flags) { struct encrypt_ostream *estream; estream = i_new(struct encrypt_ostream, 1); estream->ostream.sendv = o_stream_encrypt_sendv; estream->ostream.iostream.close = o_stream_encrypt_close; estream->ostream.iostream.destroy = o_stream_encrypt_destroy; estream->flags = flags; return estream; } struct ostream * o_stream_create_encrypt(struct ostream *output, const char *algorithm, struct dcrypt_public_key *box_pub, enum io_stream_encrypt_flags flags) { struct encrypt_ostream *estream = o_stream_create_encrypt_common(flags); int ec; dcrypt_key_ref_public(box_pub); estream->pub = box_pub; T_BEGIN { ec = o_stream_encrypt_init(estream, algorithm); } T_END; struct ostream *os = o_stream_create(&estream->ostream, output, o_stream_get_fd(output)); if (ec != 0) { os->stream_errno = EINVAL; } return os; } struct ostream * o_stream_create_sym_encrypt(struct ostream *output, struct dcrypt_context_symmetric *ctx) { struct encrypt_ostream *estream = o_stream_create_encrypt_common(IO_STREAM_ENC_INTEGRITY_NONE); const char *error; int ec; estream->prefix_written = TRUE; if (!dcrypt_ctx_sym_init(estream->ctx_sym, &error)) ec = -1; else ec = 0; estream->ctx_sym = ctx; struct ostream *os = o_stream_create(&estream->ostream, output, o_stream_get_fd(output)); if (ec != 0) { io_stream_set_error(&estream->ostream.iostream, "Could not initialize stream: %s", error); os->stream_errno = EINVAL; } return os; } dovecot-2.2.33.2/src/lib-dcrypt/test-crypto.c0000644000175000017500000007002513165463624015644 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "str.h" #include "dcrypt.h" #include "dcrypt-iostream.h" #include "ostream.h" #include "ostream-encrypt.h" #include "istream.h" #include "iostream-temp.h" #include "randgen.h" #include "test-common.h" #include "hex-binary.h" #include #include #include static void test_cipher_test_vectors(void) { static struct { const char *key; const char *iv; const char *pt; const char *ct; } vectors[] = { { "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090a0b0c0d0e0f", "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d" }, { "2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2" } }; test_begin("test_cipher_test_vectors"); buffer_t *key,*iv,*pt,*ct,*res_enc,*res_dec; key = buffer_create_dynamic(pool_datastack_create(), 16); iv = buffer_create_dynamic(pool_datastack_create(), 16); pt = buffer_create_dynamic(pool_datastack_create(), 16); ct = buffer_create_dynamic(pool_datastack_create(), 16); res_enc = buffer_create_dynamic(pool_datastack_create(), 32); res_dec = buffer_create_dynamic(pool_datastack_create(), 32); for(size_t i = 0; i < N_ELEMENTS(vectors); i++) { struct dcrypt_context_symmetric *ctx; buffer_set_used_size(key, 0); buffer_set_used_size(iv, 0); buffer_set_used_size(pt, 0); buffer_set_used_size(ct, 0); buffer_set_used_size(res_enc, 0); buffer_set_used_size(res_dec, 0); hex_to_binary(vectors[i].key, key); hex_to_binary(vectors[i].iv, iv); hex_to_binary(vectors[i].pt, pt); hex_to_binary(vectors[i].ct, ct); if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_ENCRYPT, &ctx, NULL)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); continue; } dcrypt_ctx_sym_set_padding(ctx, FALSE); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); test_assert_idx(dcrypt_ctx_sym_update(ctx, pt->data, pt->used, res_enc, NULL), i); test_assert_idx(dcrypt_ctx_sym_final(ctx, res_enc, NULL), i); test_assert_idx(buffer_cmp(ct, res_enc), i); dcrypt_ctx_sym_destroy(&ctx); if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_DECRYPT, &ctx, NULL)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); continue; } dcrypt_ctx_sym_set_padding(ctx, FALSE); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); test_assert_idx(dcrypt_ctx_sym_update(ctx, res_enc->data, res_enc->used, res_dec, NULL), i); test_assert_idx(dcrypt_ctx_sym_final(ctx, res_dec, NULL), i); test_assert_idx(buffer_cmp(pt, res_dec), i); dcrypt_ctx_sym_destroy(&ctx); } test_end(); } static void test_cipher_aead_test_vectors(void) { struct dcrypt_context_symmetric *ctx; const char *error = NULL; test_begin("test_cipher_aead_test_vectors"); if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_ENCRYPT, &ctx, &error)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); return; } buffer_t *key, *iv, *aad, *pt, *ct, *tag, *tag_res, *res; key = buffer_create_dynamic(pool_datastack_create(), 16); iv = buffer_create_dynamic(pool_datastack_create(), 16); aad = buffer_create_dynamic(pool_datastack_create(), 16); pt = buffer_create_dynamic(pool_datastack_create(), 16); ct = buffer_create_dynamic(pool_datastack_create(), 16); tag = buffer_create_dynamic(pool_datastack_create(), 16); res = buffer_create_dynamic(pool_datastack_create(), 16); tag_res = buffer_create_dynamic(pool_datastack_create(), 16); hex_to_binary("feffe9928665731c6d6a8f9467308308", key); hex_to_binary("cafebabefacedbaddecaf888", iv); hex_to_binary("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", pt); hex_to_binary("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985", ct); hex_to_binary("4d5c2af327cd64a62cf35abd2ba6fab4", tag); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); test_assert(dcrypt_ctx_sym_init(ctx, &error)); test_assert(dcrypt_ctx_sym_update(ctx, pt->data, pt->used, res, &error)); test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); test_assert(dcrypt_ctx_sym_get_tag(ctx, tag_res)); test_assert(buffer_cmp(ct, res) == TRUE); test_assert(buffer_cmp(tag, tag_res) == TRUE); dcrypt_ctx_sym_destroy(&ctx); if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_DECRYPT, &ctx, &error)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); } else { buffer_set_used_size(res, 0); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); dcrypt_ctx_sym_set_tag(ctx, tag->data, tag->used); test_assert(dcrypt_ctx_sym_init(ctx, &error)); test_assert(dcrypt_ctx_sym_update(ctx, ct->data, ct->used, res, &error)); test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); test_assert(buffer_cmp(pt, res) == TRUE); dcrypt_ctx_sym_destroy(&ctx); } test_end(); } static void test_hmac_test_vectors(void) { test_begin("test_hmac_test_vectors"); buffer_t *pt, *ct, *key, *res; pt = buffer_create_dynamic(pool_datastack_create(), 50); key = buffer_create_dynamic(pool_datastack_create(), 20); ct = buffer_create_dynamic(pool_datastack_create(), 32); res = buffer_create_dynamic(pool_datastack_create(), 32); hex_to_binary("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", key); hex_to_binary("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", pt); hex_to_binary("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", res); struct dcrypt_context_hmac *hctx; if (!dcrypt_ctx_hmac_create("sha256", &hctx, NULL)) { test_assert_failed("dcrypt_ctx_hmac_create", __FILE__, __LINE__-1); } else { dcrypt_ctx_hmac_set_key(hctx, key->data, key->used); test_assert(dcrypt_ctx_hmac_init(hctx, NULL)); test_assert(dcrypt_ctx_hmac_update(hctx, pt->data, pt->used, NULL)); test_assert(dcrypt_ctx_hmac_final(hctx, ct, NULL)); test_assert(buffer_cmp(ct, res)); dcrypt_ctx_hmac_destroy(&hctx); } test_end(); } static void test_load_v1_keys(void) { test_begin("test_load_v1_keys"); const char *error = NULL; const char *data1 = "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c940e978d00686cbb52bd5014bc318563375876255\t0300E46DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A34235A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type encryption_type; const char *encryption_key_hash = NULL; const char *key_hash = NULL; bool ret = dcrypt_key_string_get_info(data1, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(error == NULL); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_1); test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); test_assert(strcmp(encryption_key_hash, "d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0") == 0); test_assert(strcmp(key_hash, "7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f") == 0); const char* data2 = "1\t716\t0301EB00973C4EFC8FCECA4EA33E941F50B561199A5159BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71EE2AD264CD16B863FA094A8F6F69A56B62E8918040\t7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; error = NULL; encryption_key_hash = NULL; key_hash = NULL; ret = dcrypt_key_string_get_info(data2, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(error == NULL); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_1); test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(encryption_key_hash == NULL); test_assert(strcmp(key_hash, "7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f") == 0); /* This is the key that should be able to decrypt key1 */ const char *data3 = "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; error = NULL; encryption_key_hash = NULL; key_hash = NULL; ret = dcrypt_key_string_get_info(data3, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(error == NULL); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_1); test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(encryption_key_hash == NULL); test_assert(strcmp(key_hash, "d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0") == 0); /* key3's key_hash should and does match key1's encryption_key_hash */ struct dcrypt_private_key *pkey = NULL; struct dcrypt_private_key *pkey2 = NULL; pkey = NULL; error = NULL; ret = dcrypt_key_load_private(&pkey2, data3, NULL, NULL, &error); test_assert(ret == TRUE); test_assert(error == NULL); ret = dcrypt_key_load_private(&pkey, data1, NULL, pkey2, &error); test_assert(ret == TRUE); test_assert(error == NULL); dcrypt_key_unref_private(&pkey2); dcrypt_key_unref_private(&pkey); test_end(); } static void test_load_v1_key(void) { test_begin("test_load_v1_key"); buffer_t *key_1 = buffer_create_dynamic(pool_datastack_create(), 128); struct dcrypt_private_key *pkey = NULL, *pkey2 = NULL; const char *error = NULL; test_assert(dcrypt_key_load_private(&pkey, "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0", NULL, NULL, &error)); if (pkey != NULL) { buffer_set_used_size(key_1, 0); /* check that key_id matches */ struct dcrypt_public_key *pubkey = NULL; dcrypt_key_convert_private_to_public(pkey, &pubkey); test_assert(dcrypt_key_store_public(pubkey, DCRYPT_FORMAT_DOVECOT, key_1, NULL)); buffer_set_used_size(key_1, 0); dcrypt_key_id_public(pubkey, "sha256", key_1, &error); test_assert(strcmp("792caad4d38c9eb2134a0cbc844eae386116de096a0ccafc98479825fc99b6a1", binary_to_hex(key_1->data, key_1->used)) == 0); dcrypt_key_unref_public(&pubkey); pkey2 = NULL; test_assert(dcrypt_key_load_private(&pkey2, "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c940e978d00686cbb52bd5014bc318563375876255\t0300E46DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A34235A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f", NULL, pkey, &error)); if (pkey2 != NULL) { buffer_set_used_size(key_1, 0); /* check that key_id matches */ struct dcrypt_public_key *pubkey = NULL; dcrypt_key_convert_private_to_public(pkey2, &pubkey); test_assert(dcrypt_key_store_public(pubkey, DCRYPT_FORMAT_DOVECOT, key_1, NULL)); buffer_set_used_size(key_1, 0); test_assert(dcrypt_key_id_public_old(pubkey, key_1, &error)); test_assert(strcmp("7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f", binary_to_hex(key_1->data, key_1->used)) == 0); dcrypt_key_unref_public(&pubkey); dcrypt_key_unref_private(&pkey2); } dcrypt_key_unref_private(&pkey); } test_end(); } static void test_load_v1_public_key(void) { test_begin("test_load_v1_public_key"); const char* data1 = "1\t716\t030131D8A5FD5167947A0AE9CB112ADED6526654635AA5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BBBCB44BBFC0D662A4287A848BA570D4E5E45A11FE0F\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; const char* error = NULL; const char* key_hash = NULL; const char* encryption_key_hash = NULL; enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type encryption_type; bool ret = dcrypt_key_string_get_info(data1, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(error == NULL); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_1); test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(key_hash != NULL && strcmp(key_hash, "d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0") == 0); test_assert(encryption_key_hash == NULL); struct dcrypt_public_key *pub_key = NULL; ret = dcrypt_key_load_public(&pub_key, data1, &error); test_assert(ret == TRUE); test_assert(error == NULL); test_assert(dcrypt_key_type_public(pub_key) == DCRYPT_KEY_EC); dcrypt_key_unref_public(&pub_key); test_assert(pub_key == NULL); test_end(); } static void test_load_v2_key(void) { const char *keys[] = { "-----BEGIN PRIVATE KEY-----\n" \ "MGcCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcETTBLAgEBBCC25AkD65uhlZXCAdwN\n" \ "yLJV2ui8A/CUyqyEMrezvwgMO6EkAyIAAybRUR3MsH0+0PQcDwkrXOJ9aePwzTQV\n" \ "DN51+n1JCxbI\n" \ "-----END PRIVATE KEY-----\n", "2:1.2.840.10045.3.1.7:0:0000002100b6e40903eb9ba19595c201dc0dc8b255dae8bc03f094caac8432b7b3bf080c3b:ab13d251976dedab546b67354e7678821740dd534b749c2857f66bf62bbaddfd", "2:1.2.840.10045.3.1.7:2:aes-256-ctr:483bd74fd3d91763:sha256:2048:d44ae35d3af7a2febcb15cde0c3693e7ed98595665ed655a97fa918d346d5c661a6e2339f4:ab13d251976dedab546b67354e7678821740dd534b749c2857f66bf62bbaddfd", "2:1.2.840.10045.3.1.7:1:aes-256-ctr:2574c10be28a4c09:sha256:2048:a750ec9dea91999f108f943485a20f273f40f75c37fc9bcccdedda514c8243e550d69ce1bd:02237a199d7d945aa6492275a02881071eceec5749caf2485da8c64fb601229098:ab13d251976dedab546b67354e7678821740dd534b749c2857f66bf62bbaddfd:ab13d251976dedab546b67354e7678821740dd534b749c2857f66bf62bbaddfd" }; test_begin("test_load_v2_key"); const char *error = NULL; buffer_t *tmp = buffer_create_dynamic(default_pool, 256); struct dcrypt_private_key *priv,*priv2; test_assert_idx(dcrypt_key_load_private(&priv2, keys[0], NULL, NULL, &error), 0); test_assert_idx(dcrypt_key_store_private(priv2, DCRYPT_FORMAT_PEM, NULL, tmp, NULL, NULL, &error), 0); test_assert_idx(strcmp(str_c(tmp), keys[0])==0, 0); buffer_set_used_size(tmp, 0); test_assert_idx(dcrypt_key_load_private(&priv, keys[1], NULL, NULL, &error), 1); test_assert_idx(dcrypt_key_store_private(priv, DCRYPT_FORMAT_DOVECOT, NULL, tmp, NULL, NULL, &error), 1); test_assert_idx(strcmp(str_c(tmp), keys[1])==0, 1); buffer_set_used_size(tmp, 0); dcrypt_key_unref_private(&priv); test_assert_idx(dcrypt_key_load_private(&priv, keys[2], "This Is Sparta", NULL, &error), 2); test_assert_idx(dcrypt_key_store_private(priv, DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", tmp, "This Is Sparta", NULL, &error), 2); buffer_set_used_size(tmp, 0); dcrypt_key_unref_private(&priv); struct dcrypt_public_key *pub = NULL; dcrypt_key_convert_private_to_public(priv2, &pub); test_assert_idx(dcrypt_key_load_private(&priv, keys[3], NULL, priv2, &error), 3); test_assert_idx(dcrypt_key_store_private(priv, DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", tmp, NULL, pub, &error), 3); buffer_set_used_size(tmp, 0); dcrypt_key_unref_private(&priv2); dcrypt_key_unref_private(&priv); dcrypt_key_unref_public(&pub); buffer_free(&tmp); if (error != NULL) error = NULL; test_end(); } static void test_load_v2_public_key(void) { struct dcrypt_public_key *pub = NULL; const char *error; test_begin("test_load_v2_public_key"); const char *key = "2:3058301006072a8648ce3d020106052b810400230344000301c50954e734dd8b410a607764a7057065a45510da52f2c6e28e0cb353b9c389fa8cb786943ae991fce9befed78fb162fbbc615415f06af06c8cc80c37f4e94ff6c7:185a7212542782e239111f9c19d126ad55b18ddaf4883d66afe8d9627c3607d8"; test_assert(dcrypt_key_load_public(&pub, key, &error)); buffer_t *tmp = buffer_create_dynamic(default_pool, 256); if (pub != NULL) { test_assert(dcrypt_key_store_public(pub, DCRYPT_FORMAT_DOVECOT, tmp, &error)); test_assert(strcmp(key, str_c(tmp))==0); buffer_free(&tmp); dcrypt_key_unref_public(&pub); } test_end(); } static void test_get_info_v2_key(void) { test_begin("test_get_info_v2_key"); const char *key = "2:305e301006072a8648ce3d020106052b81040026034a000203fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d90966e84dc"; enum dcrypt_key_format format; enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type encryption_type; const char *encryption_key_hash = NULL; const char *key_hash = NULL; const char *error = NULL; test_assert(dcrypt_key_string_get_info(key, &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error)); test_assert(error == NULL); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_2); test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(encryption_key_hash == NULL); test_assert(key_hash != NULL && strcmp(key_hash, "86706b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d90966e84dc") == 0); test_end(); } static void test_gen_and_get_info_rsa_pem(void) { test_begin("test_gen_and_get_info_rsa_pem"); const char *error = NULL; bool ret = FALSE; struct dcrypt_keypair pair; string_t* buf = str_new(default_pool, 4096); ret = dcrypt_keypair_generate(&pair, DCRYPT_KEY_RSA, 1024, NULL, NULL); test_assert(ret == TRUE); /* test public key */ enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type encryption_type; const char *encryption_key_hash; const char *key_hash; ret = dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, buf, &error); test_assert(ret == TRUE); ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(format == DCRYPT_FORMAT_PEM); test_assert(version == DCRYPT_KEY_VERSION_NA); test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(encryption_key_hash == NULL); test_assert(key_hash == NULL); /* test private key */ buffer_set_used_size(buf, 0); ret = dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_PEM, NULL, buf, NULL, NULL, &error); test_assert(ret == TRUE); ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, &kind, &encryption_type, &encryption_key_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(format == DCRYPT_FORMAT_PEM); test_assert(version == DCRYPT_KEY_VERSION_NA); test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); test_assert(encryption_key_hash == NULL); test_assert(key_hash == NULL); dcrypt_keypair_unref(&pair); buffer_free(&buf); test_end(); } static void test_get_info_rsa_private_key(void) { test_begin("test_get_info_rsa_private_key"); const char *key = "-----BEGIN RSA PRIVATE KEY-----\n" "MIICXQIBAAKBgQC89q02I9NezBLQ+otn5XLYE7S+GsKUz59ogr45DA/6MI9jey0W\n" "56SeWQ1FJD1vDhAx/TRBMfOmhcIPsBjc5sakYOawPdoiqLjOIlO+iHwnbbmLuMsq\n" "ue09vgvZsKjuTr2F5DOFQY43Bq/Nd+4bjHJItdOM58+xwA2I/8vDbtI8jwIDAQAB\n" "AoGBAJCUrTMfdjqyKjN7f+6ewKBTc5eBIiB6O53ba3B6qj7jqNKVDIrZ8jq2KFEe\n" "yWKPgBS/h5vafHKNJU6bjmp2qMUJPB7PTA876eDo0cq9PplUqihiTlXJFwNQYtF+\n" "o27To5t25+5qdSAj657+lQfFT9Xn9fzYHDmotURxH10FgFkBAkEA+7Ny6lBTeb3W\n" "LnP0UPfPzQLilEr8u81PLWe69RGtsEaMQHGpHOl4e+bvvVYbG1cgxwxI1m01uR9r\n" "qpD3qLUdrQJBAMAw6UvN8R+opYTZzwqK7Nliil2QZMPmXM04SV1iFq26NM60w2Fm\n" "HqOOh0EbpSWsFtIgxJFWoZOtrguxqCJuUqsCQF3EoXf3StHczhDqM8eCOpD2lTCH\n" "qxXPy8JvlW+9EUbNUWykq0rRE4idJQ0VKe4KjHR6+Buh/dSkhvi5Hvpj1tUCQHRv\n" "LWeXZLVhXqWVrzEb6VHpuRnmGKX2MdLCfu/sNQEbBlMUgCnJzFYaSybOsMaZ81lq\n" "MKw8Z7coSYEcKFhzrfECQQD7l+4Bhy8Zuz6VoGGIZwIhxkJrImBFmaUwx8N6jg20\n" "sgDRYwCoGkGd7B8uIHZLJoWzSSutHiu5i5PYUy5VT1yT\n" "-----END RSA PRIVATE KEY-----\n"; const char *error = NULL; test_assert(!dcrypt_key_string_get_info(key, NULL, NULL, NULL, NULL, NULL, NULL, &error)); test_assert(error != NULL && strstr(error, "pkey") != NULL); test_end(); } static void test_get_info_invalid_keys(void) { test_begin("test_get_info_invalid_keys"); const char *key = "1:716:030131D8A5FD5167947A0AE9CB112ADED6526654635AA5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BBBCB44BBFC0D662A4287A848BA570D4E5E45A11FE0F:d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; const char *error = NULL; test_assert(dcrypt_key_string_get_info(key, NULL, NULL, NULL, NULL, NULL, NULL, &error) == FALSE); test_assert(error != NULL && strstr(error, "tab") != NULL); key = "2\t305e301006072a8648ce3d020106052b81040026034a000203fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945ed9d182f3156550e9ee30b237a0217dbf79d28975f31\t86706b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d90966e84dc"; error = NULL; test_assert(dcrypt_key_string_get_info(key, NULL, NULL, NULL, NULL, NULL, NULL, &error) == FALSE); test_assert(error != NULL && strstr(error, "colon") != NULL); key = "2"; error = NULL; test_assert(dcrypt_key_string_get_info(key, NULL, NULL, NULL, NULL, NULL, NULL, &error) == FALSE); test_assert(error != NULL && strstr(error, "Unknown") != NULL); test_end(); } static void test_get_info_key_encrypted(void) { test_begin("test_get_info_key_encrypted"); struct dcrypt_keypair p1, p2; const char *error = NULL; bool ret = dcrypt_keypair_generate(&p1, DCRYPT_KEY_EC, 0, "secp521r1", &error); test_assert(ret == TRUE); ret = dcrypt_keypair_generate(&p2, DCRYPT_KEY_EC, 0, "secp521r1", &error); test_assert(ret == TRUE); string_t* buf = t_str_new(4096); buffer_set_used_size(buf, 0); ret = dcrypt_key_store_private(p1.priv, DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf, NULL, p2.pub, &error); test_assert(ret == TRUE); enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type enc_type; const char *enc_hash; const char *key_hash; ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, &kind, &enc_type, &enc_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_2); test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); test_assert(enc_hash != NULL); test_assert(key_hash != NULL); dcrypt_keypair_unref(&p1); dcrypt_keypair_unref(&p2); test_end(); } static void test_get_info_pw_encrypted(void) { test_begin("test_get_info_pw_encrypted"); struct dcrypt_keypair p1; i_zero(&p1); const char *error; bool ret = dcrypt_keypair_generate(&p1, DCRYPT_KEY_EC, 0, "secp521r1", &error); test_assert(ret == TRUE); string_t* buf = t_str_new(4096); ret = dcrypt_key_store_private(p1.priv, DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, "pw", NULL, &error); test_assert(ret == TRUE); enum dcrypt_key_format format; enum dcrypt_key_version version; enum dcrypt_key_kind kind; enum dcrypt_key_encryption_type enc_type; const char *enc_hash; const char *key_hash; ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, &kind, &enc_type, &enc_hash, &key_hash, &error); test_assert(ret == TRUE); test_assert(format == DCRYPT_FORMAT_DOVECOT); test_assert(version == DCRYPT_KEY_VERSION_2); test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD); test_assert(enc_hash == NULL); test_assert(key_hash != NULL); dcrypt_keypair_unref(&p1); test_end(); } static void test_password_change(void) { test_begin("test_password_change"); const char *pw1 = "first password"; struct dcrypt_keypair orig; const char *error = NULL; bool ret = dcrypt_keypair_generate(&orig, DCRYPT_KEY_EC, 0, "secp521r1", &error); test_assert(ret == TRUE); string_t *buf = t_str_new(4096); ret = dcrypt_key_store_private(orig.priv, DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, pw1, NULL, &error); test_assert(ret == TRUE); /* load the pw-encrypted key */ struct dcrypt_private_key *k1_priv = NULL; ret = dcrypt_key_load_private(&k1_priv, str_c(buf), pw1, NULL, &error); test_assert(ret == TRUE); /* encrypt a key with the pw-encrypted key k1 */ struct dcrypt_keypair k2; ret = dcrypt_keypair_generate(&k2, DCRYPT_KEY_EC, 0, "secp521r1", &error); test_assert(ret == TRUE); string_t *buf2 = t_str_new(4096); struct dcrypt_public_key *k1_pub = NULL; dcrypt_key_convert_private_to_public(k1_priv, &k1_pub); ret = dcrypt_key_store_private(k2.priv, DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf2, NULL, k1_pub, &error); test_assert(ret == TRUE); /* change the password */ const char *pw2 = "second password"; string_t *buf3 = t_str_new(4096); /* encrypt k1 with pw2 */ ret = dcrypt_key_store_private(k1_priv, DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf3, pw2, NULL, &error); test_assert(ret == TRUE); /* load the pw2 encrypted key */ struct dcrypt_private_key *k2_priv = NULL; ret = dcrypt_key_load_private(&k2_priv, str_c(buf3), pw2, NULL, &error); test_assert(ret == TRUE); /* load the key that was encrypted with pw1 using the pw2 encrypted key */ struct dcrypt_private_key *k3_priv = NULL; ret = dcrypt_key_load_private(&k3_priv, str_c(buf2), NULL, k2_priv, &error); test_assert(ret == TRUE); dcrypt_key_unref_private(&k1_priv); dcrypt_key_unref_public(&k1_pub); dcrypt_key_unref_private(&k2_priv); dcrypt_key_unref_private(&k3_priv); dcrypt_keypair_unref(&orig); dcrypt_keypair_unref(&k2); test_end(); } static void test_load_invalid_keys(void) { test_begin("test_load_invalid_keys"); const char *error = NULL; const char *key = "1:716:0301EB00973C4EFC8FCECA4EA33E941F50B561199A5159BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71EE2AD264CD16B863FA094A8F6F69A56B62E8918040:7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; struct dcrypt_public_key *pub_key = NULL; bool ret = dcrypt_key_load_public(&pub_key, key, &error); test_assert(ret == FALSE); test_assert(error != NULL); error = NULL; key = "2:305e301006072a8648ce3d020106052b81040026034a000203fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d90966e84dc"; struct dcrypt_private_key *priv_key = NULL; ret = dcrypt_key_load_private(&priv_key, key, NULL, NULL, &error); test_assert(ret == FALSE); test_assert(error != NULL); test_end(); } int main(void) { struct dcrypt_settings set = { .module_dir = ".libs" }; const char *error; random_init(); if (!dcrypt_initialize(NULL, &set, &error)) { i_error("No functional dcrypt backend found - skipping tests: %s", error); return 0; } static void (*test_functions[])(void) = { test_cipher_test_vectors, test_cipher_aead_test_vectors, test_hmac_test_vectors, test_load_v1_keys, test_load_v1_key, test_load_v1_public_key, test_load_v2_key, test_load_v2_public_key, test_get_info_v2_key, test_gen_and_get_info_rsa_pem, test_get_info_rsa_private_key, test_get_info_invalid_keys, test_get_info_key_encrypted, test_get_info_pw_encrypted, test_password_change, test_load_invalid_keys, NULL }; int ret = test_run(test_functions); dcrypt_deinitialize(); random_deinit(); return ret; } dovecot-2.2.33.2/src/lib-dcrypt/sample-v1.asc0000644000175000017500000000602413123174404015463 00000000000000Q1JZUFRFRAMHAQCtAEMCAMyKuGO/j3TPoXzRJ39THa1oGDChmueqRVFlR3qnIWcd Mt15zp+juTJzwfxKDNsgdIfFleIbuuo1AX1TgimaVfb8ACB8mhA56i5P7XPoHdP/ w/oi6kooNSk5rd57+OqFiwD6TwAgWi/IHZ3tFmaohetUkFowgcrYwMh9HR9iOXg6 QdIDnqMAIIrC9OcLyuMzUp18LpVKZLg6QaJEsjrepBatgkqRDgBKAAD7tnI+Rjjg rNZ5UHYvjA1xsrEhfbyx8X6Vb+em++p+aE+I92pBrqV/XIeR1er1oNX3nxZwEnL4 UwhavZOMw7Qna0o4bop4PfK65HqnFTmgaiNDBMdE/CFaxSRlI0PLc0jhqxoEYU/d 0hrtPcpQMq0sCxBbHqcnDF1xAK2hAZU12BH+JoV4bI1k1MMn1xAcXxiVdtSO5NE8 E5fyaMfvTq2zIZtqQY09arrd0DaQ8o/L2dIV6jQVNZLxbRFWVayoWloT/YVSlHhi w5YHJetffXO02mllj3Mxr5aIfCmpZbrcWMfrF88ksI6HyQOrTHKS+Y95+VFLbxy4 +BWFGKV4zUGUwrfEDOpxIAZbBHsABjV82NS2TEZltu/ki3EhlwC8Hy+oyqn+LZN5 LCmbt/maMI0EJU6cNCUCQM8Rq2Xv7xP/DrC3A0y7gj3pT44dY0dMj76gBApKO4Qw rLcBgY9qycXHtUwvtg/QZJrb2n2AB7h0+B3LgVm8P12l1KAFS2ykugBWUVJSuUjK 5fjTk4EDIaCm0rhs2hNty9OtBkBuQBolkzxHtqp9/+QhIWJEtNtQEdnwN8DtdlNH /p69sZvkDhmyqSuByCodwVhkPZf5d/oGVUvE73btlAJcl8NZMXDqgKHfT4U5OF6Y eSXUIz9oiM2Wy1CVqrA2JdFGQ4Hcbf76IP462+gGefOd62atFfvjGGIb+Okyrmab jNxn/7wtUw42MXIzoAk+GQsOgo77rH075mILWqp0OuAyRTKmsZTP3FaDb4SxQk3K pw3N7HiNIiDW1wd5BLJE73qxKr+JC8GLs/s+JpfVb+lxzXAKXpEZTGFd/zphF1Kf J+aBCF+UB5Lq+QYoHfKSJzRb24PScAVs1VrV8nJlQvxMZW6Rd4ofNcsMjY+b1pBx bv74+oACEXC/F1jpp3tMTTLOLN8/hNq0J1mE3uOYRAaNK1jaQQAoa4rNxeY/2vBw mod8/Erc9M0M4B3VHVfRJ4F8MAx3b8if3oiicLu0OJ16snGHM7BGp2pOEPRVAg1Q +70zDctuSV6HwPYSAXaIw2LPrbsQmq1Lq/JivHYRwjUDBFAaXkPv2WFEfUsXEtmw UzUPLgWxZM8MsPDEI510SzRd3OlBUjuS96+/9n/Qv1D5DBnrDH9gUwUiPr9V+rMM kmNaAQpTKpCNpevCH/F18rQnUi/PJKY37q8pF/lO0OW7mzS+CxCvFr+aWxZ2kV84 GwUrUuSdHa1Z/0BY/UTZn1BAi6bOudiWHH5loJBldreSJ2K2/iwMpLUUEuQ+TJJ8 BvsWeOLMqEpDIxuuYERCUqHO8EmtvsSPKcIeS7ZZpvkRlRIWCuKgnaHSymT87Eq1 SJskTkAWd1iGvQI78tM8KwT2KDmf7Qs3RUCiSynT/H1OmVQqwVn/5c9wFjV+0PRA KFZZDEMvwy5tYpJ1Y0nYuUUMOlA8l11rpKqAIcRj0V256uXoj5cBnTsGZAOFDBqx pBLmGFgz1havj8RsgqtJfCkkh+Y8l9xszAC98/FGYOtmKpP4sXXM3LASkhi2FU4C OH/4tUsoh3tMCendx36s1UmliE94BiNuJMUAqSh9cLCnW/Uiw0bzqV8GOJLDk/A3 XMWrERuA2Jn7qQ9e9qmYarc8r6JjWUuxECZ04d12wNCcDF7hWxFYLbwTk/ZUIM5D 1ZsdOPUQDf7gjEW4gQoOK7pQWifJa56ZSSFurLoL5ae+3dPwUu8HNQLvwmx4paSe 01shQd36MXcRZ7BRVp0GqNrviuAtXacSxx1GIO9rh7RtyGGs1grsQ07P6Evpk0k/ 1WY48cE+xWU5SH4JwxMZ3vbDguMY/cnp2VhuzZguJ4iIFKg5RMVShrSkZQcWwH7e 7JVu7hOe3bWp1KeVG41IsOFpo0Jfpegtgf4r1hYih02Q54UNIFf5G3IRsbC1pjtj ALYteCLe9oa+7lIAVDWgmq/NqWLsi4dtlz97TG8XApIFZ6Prr7KG8N+RFTmouXYT QH6FuF5XvJ0TqIgIkdSzbuaNmN47E6PQoDAuRJ4X9ahYpF1xC4ecnAaI+rlaparF Z1OOtwYVQ1Hthc7wp+205aK6ujZyU6a9MrGXGZklRQdigCd+EOs0kWy8t+bcmk6K 9aXEJyGMBtcgQDGWZZJer5w8aUKj6SDp9y7X1kyAuhh8DFvNKMgR3vs4wnvsjr1W cYxH8RvAVXj76Xjvvgeg3jcJbcr0mPwB0SrcpQ+88mF64x+nrKsXmz9E8wBgOMY6 4qXXb27YUehAGN81jTZRN8lShl20fnPZKd5U1sqIhevXNUrTVjxVNff0fHRnOOb9 sVD3vdQmfbXvB3nyQLKQ+Wcw/WPqa0But5KEXFjnaXPORcEWEpYvWOgGPegmrTgh P4ZVscRdxozvd0sKvpLNMd/EiNLBaYft+4Yo38WZYkZzNJT7LhW41pbQyK1cmZiU COisSP2rrus6iKCNwaGPMZJNCCdQN862DSTaa0IZSEeSRpfBfA2UevSsfX0uTgyb B4Az1u4QJv9fQp8oAnnpH7YHhW43V/YTVuwLLF7pqSl2J2gNcLFfGuJVftRp6xJg mkfXOxoJ0y5CQU7pwQZ/F6WxuWBX1m7MNE/OfI9l9ODj0uAgKn1e+DzH1VXZi8ls lenQuepkt7zqXG5dRRhW+FSlJy1oo9oPcf2bZhAYMty3mh9F4Ils3SSCE6T6cU0A yYCNrq8iKyq5V1jdC9haN5NmF9yNiIxYWdbigcBTzR5+AZuNe7aJSXu0qvQJR9s0 d/l7J6LsH25I/zsIV/0OHcrvMUEf2WzU3Q== dovecot-2.2.33.2/src/lib-dcrypt/dcrypt-private.h0000644000175000017500000001365213123174404016321 00000000000000#ifndef DCRYPT_PRIVATE_H #define DCRYPT_PRIVATE_H #define DCRYPT_DOVECOT_KEY_ENCRYPT_HASH "sha256" #define DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS 2048 #define DCRYPT_DOVECOT_KEY_ENCRYPT_NONE 0 #define DCRYPT_DOVECOT_KEY_ENCRYPT_PK 1 #define DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD 2 struct dcrypt_vfs { bool (*initialize)(const struct dcrypt_settings *set, const char **error_r); bool (*ctx_sym_create)(const char *algorithm, enum dcrypt_sym_mode mode, struct dcrypt_context_symmetric **ctx_r, const char **error_r); void (*ctx_sym_destroy)(struct dcrypt_context_symmetric **ctx); void (*ctx_sym_set_key)(struct dcrypt_context_symmetric *ctx, const unsigned char *key, size_t key_len); void (*ctx_sym_set_iv)(struct dcrypt_context_symmetric *ctx, const unsigned char *iv, size_t iv_len); void (*ctx_sym_set_key_iv_random)(struct dcrypt_context_symmetric *ctx); void (*ctx_sym_set_padding)(struct dcrypt_context_symmetric *ctx, bool padding); bool (*ctx_sym_get_key)(struct dcrypt_context_symmetric *ctx, buffer_t *key); bool (*ctx_sym_get_iv)(struct dcrypt_context_symmetric *ctx, buffer_t *iv); void (*ctx_sym_set_aad)(struct dcrypt_context_symmetric *ctx, const unsigned char *aad, size_t aad_len); bool (*ctx_sym_get_aad)(struct dcrypt_context_symmetric *ctx, buffer_t *aad); void (*ctx_sym_set_tag)(struct dcrypt_context_symmetric *ctx, const unsigned char *tag, size_t tag_len); bool (*ctx_sym_get_tag)(struct dcrypt_context_symmetric *ctx, buffer_t *tag); unsigned int (*ctx_sym_get_key_length)(struct dcrypt_context_symmetric *ctx); unsigned int (*ctx_sym_get_iv_length)(struct dcrypt_context_symmetric *ctx); unsigned int (*ctx_sym_get_block_size)(struct dcrypt_context_symmetric *ctx); bool (*ctx_sym_init)(struct dcrypt_context_symmetric *ctx, const char **error_r); bool (*ctx_sym_update)(struct dcrypt_context_symmetric *ctx, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); bool (*ctx_sym_final)(struct dcrypt_context_symmetric *ctx, buffer_t *result, const char **error_r); bool (*ctx_hmac_create)(const char *algorithm, struct dcrypt_context_hmac **ctx_r, const char **error_r); void (*ctx_hmac_destroy)(struct dcrypt_context_hmac **ctx); void (*ctx_hmac_set_key)(struct dcrypt_context_hmac *ctx, const unsigned char *key, size_t key_len); bool (*ctx_hmac_get_key)(struct dcrypt_context_hmac *ctx, buffer_t *key); unsigned int (*ctx_hmac_get_digest_length)(struct dcrypt_context_hmac *ctx); void (*ctx_hmac_set_key_random)(struct dcrypt_context_hmac *ctx); bool (*ctx_hmac_init)(struct dcrypt_context_hmac *ctx, const char **error_r); bool (*ctx_hmac_update)(struct dcrypt_context_hmac *ctx, const unsigned char *data, size_t data_len, const char **error_r); bool (*ctx_hmac_final)(struct dcrypt_context_hmac *ctx, buffer_t *result, const char **error_r); bool (*ecdh_derive_secret_local)(struct dcrypt_private_key *local_key, buffer_t *R, buffer_t *S, const char **error_r); bool (*ecdh_derive_secret_peer)(struct dcrypt_public_key *peer_key, buffer_t *R, buffer_t *S, const char **error_r); bool (*pbkdf2)(const unsigned char *password, size_t password_len, const unsigned char *salt, size_t salt_len, const char *hash, unsigned int rounds, buffer_t *result, unsigned int result_len, const char **error_r); bool (*generate_keypair)(struct dcrypt_keypair *pair_r, enum dcrypt_key_type kind, unsigned int bits, const char *curve, const char **error_r); bool (*load_private_key)(struct dcrypt_private_key **key_r, const char *data, const char *password, struct dcrypt_private_key *dec_key, const char **error_r); bool (*load_public_key)(struct dcrypt_public_key **key_r, const char *data, const char **error_r); bool (*store_private_key)(struct dcrypt_private_key *key, enum dcrypt_key_format format, const char *cipher, buffer_t *destination, const char *password, struct dcrypt_public_key *enc_key, const char **error_r); bool (*store_public_key)(struct dcrypt_public_key *key, enum dcrypt_key_format format, buffer_t *destination, const char **error_r); void (*private_to_public_key)(struct dcrypt_private_key *priv_key, struct dcrypt_public_key **pub_key_r); bool (*key_string_get_info)(const char *key_data, enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, enum dcrypt_key_encryption_type *encryption_type_r, const char **encryption_key_hash_r, const char **key_hash_r, const char **error_r); void (*unref_keypair)(struct dcrypt_keypair *keypair); void (*unref_public_key)(struct dcrypt_public_key **key); void (*unref_private_key)(struct dcrypt_private_key **key); void (*ref_public_key)(struct dcrypt_public_key *key); void (*ref_private_key)(struct dcrypt_private_key *key); bool (*rsa_encrypt)(struct dcrypt_public_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); bool (*rsa_decrypt)(struct dcrypt_private_key *key, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r); const char *(*oid2name)(const unsigned char *oid, size_t oid_len, const char **error_r); bool (*name2oid)(const char *name, buffer_t *oid, const char **error_r); enum dcrypt_key_type (*private_key_type)(struct dcrypt_private_key *key); enum dcrypt_key_type (*public_key_type)(struct dcrypt_public_key *key); bool (*public_key_id)(struct dcrypt_public_key *key, const char *algorithm, buffer_t *result, const char **error_r); bool (*public_key_id_old)(struct dcrypt_public_key *key, buffer_t *result, const char **error_r); bool (*private_key_id)(struct dcrypt_private_key *key, const char *algorithm, buffer_t *result, const char **error_r); bool (*private_key_id_old)(struct dcrypt_private_key *key, buffer_t *result, const char **error_r); }; void dcrypt_set_vfs(struct dcrypt_vfs *vfs); void dcrypt_openssl_init(struct module *module ATTR_UNUSED); void dcrypt_gnutls_init(struct module *module ATTR_UNUSED); void dcrypt_openssl_deinit(void); void dcrypt_gnutls_deinit(void); #endif dovecot-2.2.33.2/src/lib-dcrypt/dcrypt-iostream.h0000644000175000017500000000062513123174404016466 00000000000000#ifndef DCRYPT_IOSTREAM_H #define DCRYPT_IOSTREAM_H 1 static const unsigned char IOSTREAM_CRYPT_MAGIC[] = {'C','R','Y','P','T','E','D','\x03','\x07'}; #define IOSTREAM_CRYPT_VERSION 2 #define IOSTREAM_TAG_SIZE 16 enum io_stream_encrypt_flags { IO_STREAM_ENC_INTEGRITY_HMAC = 0x1, IO_STREAM_ENC_INTEGRITY_AEAD = 0x2, IO_STREAM_ENC_INTEGRITY_NONE = 0x4, IO_STREAM_ENC_VERSION_1 = 0x8, }; #endif dovecot-2.2.33.2/src/lib-dcrypt/istream-decrypt.c0000644000175000017500000006512713165463624016472 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "randgen.h" #include "safe-memset.h" #include "hash-method.h" #include "sha2.h" #include "dcrypt.h" #include "istream.h" #include "istream-decrypt.h" #include "istream-private.h" #include "dcrypt-iostream.h" #include "hex-binary.h" #include #define ISTREAM_DECRYPT_READ_FIRST 15 struct decrypt_istream { struct istream_private istream; buffer_t *buf; i_stream_decrypt_get_key_callback_t *key_callback; void *key_context; struct dcrypt_private_key *priv_key; bool initialized; bool finalized; bool use_mac; uoff_t ftr, pos; enum io_stream_encrypt_flags flags; unsigned char *iv; /* original iv, in case seeking is done, future feature */ struct dcrypt_context_symmetric *ctx_sym; struct dcrypt_context_hmac *ctx_mac; enum decrypt_istream_format format; }; enum decrypt_istream_format i_stream_encrypt_get_format(const struct istream *input) { return ((const struct decrypt_istream*)input->real_stream)->format; } enum io_stream_encrypt_flags i_stream_encrypt_get_flags(const struct istream *input) { return ((const struct decrypt_istream*)input->real_stream)->flags; } static ssize_t i_stream_decrypt_read_header_v1(struct decrypt_istream *stream, const unsigned char *data, size_t mlen) { const char *error = NULL; size_t keydata_len = 0; uint16_t len; int ec, i = 0; const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, *key_ct_pos = NULL; size_t pos = sizeof(IOSTREAM_CRYPT_MAGIC); size_t digest_len = 0; size_t key_ct_len = 0; size_t key_digest_size = 0; buffer_t ephemeral_key; buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t *key = buffer_create_dynamic(pool_datastack_create(), 256); if (mlen < 2) return 0; keydata_len = be16_to_cpu_unaligned(data); if (mlen-2 < keydata_len) { /* try to read more */ return 0; } data+=2; mlen-=2; while (i < 4 && mlen > 2) { memcpy(&len, data, 2); len = ntohs(len); data += 2; mlen -= 2; pos += 2; if (len == 0 || len > mlen) break; switch(i++) { case 0: buffer_create_from_const_data(&ephemeral_key, data, len); break; case 1: /* public key id */ digest_pos = data; digest_len = len; break; case 2: /* encryption key digest */ key_digest_pos = data; key_digest_size = len; break; case 3: /* encrypted key data */ key_ct_pos = data; key_ct_len = len; break; } pos += len; data += len; mlen -= len; } if (i < 4) { io_stream_set_error(&stream->istream.iostream, "Invalid or corrupted header"); stream->istream.istream.stream_errno = EINVAL; return -1; } /* we don't have a private key */ if (stream->priv_key == NULL) { /* see if we can get one */ if (stream->key_callback != NULL) { const char *key_id = binary_to_hex(digest_pos, digest_len); int ret = stream->key_callback(key_id, &(stream->priv_key), &error, stream->key_context); if (ret < 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error); return -1; } if (ret == 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } dcrypt_key_ref_private(stream->priv_key); } else { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } } buffer_t *check = buffer_create_dynamic(pool_datastack_create(), 32); if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot get public key hash: %s", error); return -1; } else { if (memcmp(digest_pos, check->data, I_MIN(digest_len,check->used)) != 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } } /* derive shared secret */ if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &ephemeral_key, secret, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot perform ECDH: %s", error); return -1; } /* run it thru SHA256 once */ const struct hash_method *hash = &hash_method_sha256; unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, secret->data, secret->used); hash->result(hctx, hres); safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); /* NB! The old code was broken and used this kind of IV - it is not correct, but we need to stay compatible with old data */ /* use it to decrypt the actual encryption key */ struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &dctx, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } ec = 0; dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size); if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) || !dcrypt_ctx_sym_final(dctx, key, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } /* see if we got the correct key */ hash->init(hctx); hash->loop(hctx, key->data, key->used); hash->result(hctx, hres); if (key_digest_size != sizeof(hres)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: invalid digest length"); return -1; } if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: decrypted key is invalid"); return -1; } /* prime context with key */ if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption context create error: %s", error); return -1; } /* Again, old code used this IV, so we have to use it too */ dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used); safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption init error: %s", error); return -1; } stream->use_mac = FALSE; stream->initialized = TRUE; /* now we are ready to decrypt stream */ return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len; } static bool get_msb32(const unsigned char **_data, const unsigned char *end, uint32_t *num_r) { const unsigned char *data = *_data; if (end-data < 4) return FALSE; *num_r = be32_to_cpu_unaligned(data); *_data += 4; return TRUE; } static bool i_stream_decrypt_der(const unsigned char **_data, const unsigned char *end, const char **str_r) { const unsigned char *data = *_data; unsigned int len; if (end-data < 2) return FALSE; /* get us DER encoded length */ if ((data[1] & 0x80) != 0) { /* two byte length */ if (end-data < 3) return FALSE; len = ((data[1] & 0x7f) << 8) + data[2] + 3; } else { len = data[1] + 2; } if ((size_t)(end-data) < len) return FALSE; *str_r = dcrypt_oid2name(data, len, NULL); *_data += len; return TRUE; } static ssize_t i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, unsigned int rounds, const unsigned char *data, const unsigned char *end, buffer_t *key, size_t key_len) { const char *error; enum dcrypt_key_type ktype; int keys; bool have_key = FALSE; unsigned char dgst[32]; uint32_t val; buffer_t buf; if (data == end) return 0; keys = *data++; /* if we have a key, prefab the digest */ if (stream->key_callback == NULL) { if (stream->priv_key == NULL) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } buffer_create_from_data(&buf, dgst, sizeof(dgst)); if (!dcrypt_key_id_private(stream->priv_key, "sha256", &buf, &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption error: " "dcrypt_key_id_private failed: %s", error); return -1; } } /* for each key */ for(;keys>0;keys--) { if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst)) return 0; ktype = *data++; if (stream->key_callback != NULL) { const char *hexdgst = binary_to_hex(data, sizeof(dgst)); /* digest length */ /* hope you going to give us right key.. */ int ret = stream->key_callback(hexdgst, &(stream->priv_key), &error, stream->key_context); if (ret < 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error); return -1; } if (ret > 0) { dcrypt_key_ref_private(stream->priv_key); have_key = TRUE; break; } } else { /* see if key matches to the one we have */ if (memcmp(dgst, data, sizeof(dgst)) == 0) { have_key = TRUE; break; } } data += sizeof(dgst); /* wasn't correct key, skip over some data */ if (!get_msb32(&data, end, &val) || !get_msb32(&data, end, &val)) return 0; } /* didn't find matching key */ if (!have_key) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } data += sizeof(dgst); const unsigned char *ephemeral_key; uint32_t ep_key_len; const unsigned char *encrypted_key; uint32_t eklen; const unsigned char *ekhash; uint32_t ekhash_len; /* read ephemeral key (can be missing for RSA) */ if (!get_msb32(&data, end, &ep_key_len) || (size_t)(end-data) < ep_key_len) return 0; ephemeral_key = data; data += ep_key_len; /* read encrypted key */ if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen) return 0; encrypted_key = data; data += eklen; /* read key data hash */ if (!get_msb32(&data, end, &ekhash_len) || (size_t)(end-data) < ekhash_len) return 0; ekhash = data; data += ekhash_len; /* decrypt the seed */ if (ktype == DCRYPT_KEY_RSA) { if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, key, &error)) { io_stream_set_error(&stream->istream.iostream, "key decryption error: %s", error); return -1; } } else if (ktype == DCRYPT_KEY_EC) { /* perform ECDHE */ buffer_t *temp_key = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t peer_key; buffer_create_from_const_data(&peer_key, ephemeral_key, ep_key_len); if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &peer_key, secret, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: corrupted header"); return -1; } /* use shared secret and peer key to generate decryption key, AES-256-CBC has 32 byte key and 16 byte IV */ if (!dcrypt_pbkdf2(secret->data, secret->used, peer_key.data, peer_key.used, malg, rounds, temp_key, 32+16, &error)) { safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); if (temp_key->used != 32+16) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid temporary key"); return -1; } struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, &dctx, &error)) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } const unsigned char *ptr = temp_key->data; /* we use ephemeral_key for IV */ dcrypt_ctx_sym_set_key(dctx, ptr, 32); dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); int ec = 0; if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, key, &error) || !dcrypt_ctx_sym_final(dctx, key, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: %s", error); ec = -1; } if (key->used != key_len) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid key length"); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) return ec; } else { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported key type 0x%02x", ktype); return -1; } /* make sure we were able to decrypt the encrypted key correctly */ const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg)); if (hash == NULL) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported hash algorithm: %s", malg); return -1; } unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, key->data, key->used); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* do the comparison */ if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: corrupted header ekhash"); return -1; } return 1; } static int i_stream_decrypt_header_contents(struct decrypt_istream *stream, const unsigned char *data, size_t size) { const unsigned char *end = data + size; bool failed = FALSE; /* read cipher OID */ const char *calg; if (!i_stream_decrypt_der(&data, end, &calg)) return 0; if (calg == NULL || !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), NULL)) { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid cipher: %s", calg); return -1; } /* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */ const char *malg; if (!i_stream_decrypt_der(&data, end, &malg)) return 0; if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &(stream->ctx_mac), NULL)) { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid MAC algorithm: %s", malg); return -1; } /* read rounds (for PBKDF2) */ uint32_t rounds; if (!get_msb32(&data, end, &rounds)) return 0; /* read key data length */ uint32_t kdlen; if (!get_msb32(&data, end, &kdlen)) return 0; size_t tagsize; if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { tagsize = IOSTREAM_TAG_SIZE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { tagsize = IOSTREAM_TAG_SIZE; } else { tagsize = 0; } /* how much key data we should be getting */ size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; buffer_t *keydata = buffer_create_dynamic(pool_datastack_create(), kl); /* try to decrypt the keydata with a private key */ int ret; if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, end, keydata, kl)) <= 0) return ret; /* oh, it worked! */ const unsigned char *ptr = keydata->data; if (keydata->used != kl) { /* but returned wrong amount of data */ io_stream_set_error(&stream->istream.iostream, "Key decryption error: Key data length mismatch"); return -1; } /* prime contexts */ dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); /* based on the chosen MAC, initialize HMAC or AEAD */ if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { const char *error; dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) { io_stream_set_error(&stream->istream.iostream, "MAC error: %s", error); stream->istream.istream.stream_errno = EINVAL; failed = TRUE; } stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac); stream->use_mac = TRUE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); stream->ftr = tagsize; stream->use_mac = TRUE; } else { stream->use_mac = FALSE; } /* destroy private key data */ safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); buffer_set_used_size(keydata, 0); return failed ? -1 : 1; } static ssize_t i_stream_decrypt_read_header(struct decrypt_istream *stream, const unsigned char *data, size_t mlen) { const char *error; const unsigned char *end = data + mlen; /* check magic */ if (mlen < sizeof(IOSTREAM_CRYPT_MAGIC)) return 0; if (memcmp(data, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) { io_stream_set_error(&stream->istream.iostream, "Stream is not encrypted (invalid magic)"); stream->istream.istream.stream_errno = EINVAL; return -1; } data += sizeof(IOSTREAM_CRYPT_MAGIC); if (data >= end) return 0; /* read more? */ /* check version */ if (*data == '\x01') { stream->format = DECRYPT_FORMAT_V1; return i_stream_decrypt_read_header_v1(stream, data+1, end - (data+1)); } else if (*data != '\x02') { io_stream_set_error(&stream->istream.iostream, "Unsupported encrypted data 0x%02x", *data); return -1; } stream->format = DECRYPT_FORMAT_V2; data++; /* read flags */ uint32_t flags; if (!get_msb32(&data, end, &flags)) return 0; stream->flags = flags; /* get the total length of header */ uint32_t hdr_len; if (!get_msb32(&data, end, &hdr_len)) return 0; /* do not forget stream format */ if ((size_t)(end-data)+1 < hdr_len) return 0; int ret; if ((ret = i_stream_decrypt_header_contents(stream, data, hdr_len)) < 0) return -1; else if (ret == 0) { io_stream_set_error(&stream->istream.iostream, "Decryption error: truncate header length"); stream->istream.istream.stream_errno = EINVAL; return -1; } stream->initialized = TRUE; /* if it all went well, try to initialize decryption context */ if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption init error: %s", error); return -1; } return hdr_len; } static ssize_t i_stream_decrypt_read(struct istream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; const unsigned char *data; size_t size, decrypt_size; const char *error = NULL; int ret; bool check_mac = FALSE; /* not if it's broken */ if (stream->istream.stream_errno != 0) return -1; for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= dstream->buf->used); buffer_delete(dstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = dstream->buf->data; i_assert(stream->pos <= dstream->buf->used); if (stream->pos >= dstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decrypted, return as much of it as we can */ if (dstream->initialized && dstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (dstream->buf->used <= dstream->istream.max_buffer_size) { new_pos = dstream->buf->used; if (dstream->finalized) stream->istream.eof = TRUE; } else { new_pos = dstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } if (dstream->finalized) { /* all data decrypted */ stream->istream.eof = TRUE; return -1; } /* need to read more input */ ret = i_stream_read(stream->parent); if (ret == 0) return ret; data = i_stream_get_data(stream->parent, &size); if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) { stream->istream.stream_errno = stream->parent->stream_errno; /* file was empty */ if (!dstream->initialized && size == 0 && stream->parent->eof) { stream->istream.eof = TRUE; return -1; } if (stream->istream.stream_errno != 0) return -1; if (!dstream->initialized) { io_stream_set_error(&stream->iostream, "Decryption error: %s", "Input truncated in decryption header"); stream->istream.stream_errno = EINVAL; return -1; } /* final block */ if (dcrypt_ctx_sym_final(dstream->ctx_sym, dstream->buf, &error)) { dstream->finalized = TRUE; continue; } io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (!dstream->initialized) { ssize_t hret; if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) { if (hret < 0) { if (stream->istream.stream_errno == 0) /* assume temporary failure */ stream->istream.stream_errno = EIO; return -1; } if (hret == 0 && stream->parent->eof) { /* not encrypted by us */ stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Truncated header"); return -1; } } if (hret == 0) { /* see if we can get more data */ if (ret == -2) { stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Header too large (more than %"PRIuSIZE_T" bytes)", size); return -1; } continue; } else { /* clean up buffer */ safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used); buffer_set_used_size(dstream->buf, 0); i_stream_skip(stream->parent, hret); } data = i_stream_get_data(stream->parent, &size); } decrypt_size = size; if (dstream->use_mac) { if (stream->parent->eof) { if (decrypt_size < dstream->ftr) { io_stream_set_error(&stream->iostream, "Decryption error: footer is longer than data"); stream->istream.stream_errno = EINVAL; return -1; } check_mac = TRUE; } else { /* ignore footer's length of data until we reach EOF */ size -= dstream->ftr; } decrypt_size -= dstream->ftr; if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, data, decrypt_size, &error)) { io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } } } if (check_mac) { if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; buffer_t db; buffer_create_from_data(&db, dgst, sizeof(dgst)); if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: mismatch"); stream->istream.stream_errno = EINVAL; return -1; } } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr); } } if (!dcrypt_ctx_sym_update(dstream->ctx_sym, data, decrypt_size, dstream->buf, &error)) { io_stream_set_error(&stream->iostream, "Decryption error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } } static void i_stream_decrypt_close(struct iostream_private *stream, bool close_parent) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; if (close_parent) i_stream_close(dstream->istream.parent); } static void i_stream_decrypt_destroy(struct iostream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; if (dstream->buf != NULL) buffer_free(&dstream->buf); if (dstream->iv != NULL) i_free_and_null(dstream->iv); if (dstream->ctx_sym != NULL) dcrypt_ctx_sym_destroy(&(dstream->ctx_sym)); if (dstream->ctx_mac != NULL) dcrypt_ctx_hmac_destroy(&(dstream->ctx_mac)); if (dstream->priv_key != NULL) dcrypt_key_unref_private(&(dstream->priv_key)); i_stream_unref(&(dstream->istream.parent)); } static struct decrypt_istream *i_stream_create_decrypt_common(struct istream *input) { struct decrypt_istream *dstream; dstream = i_new(struct decrypt_istream, 1); dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; dstream->istream.read = i_stream_decrypt_read; dstream->istream.iostream.close = i_stream_decrypt_close; dstream->istream.iostream.destroy = i_stream_decrypt_destroy; dstream->istream.istream.readable_fd = FALSE; dstream->istream.istream.blocking = input->blocking; dstream->istream.istream.seekable = FALSE; dstream->buf = buffer_create_dynamic(default_pool, 512); (void)i_stream_create(&dstream->istream, input, i_stream_get_fd(input)); return dstream; } struct istream * i_stream_create_decrypt(struct istream *input, struct dcrypt_private_key *priv_key) { struct decrypt_istream *dstream; dstream = i_stream_create_decrypt_common(input); dcrypt_key_ref_private(priv_key); dstream->priv_key = priv_key; return &dstream->istream.istream; } struct istream * i_stream_create_sym_decrypt(struct istream *input, struct dcrypt_context_symmetric *ctx) { const char *error; int ec; struct decrypt_istream *dstream; dstream = i_stream_create_decrypt_common(input); dstream->use_mac = FALSE; dstream->initialized = TRUE; if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1; else ec = 0; dstream->ctx_sym = ctx; if (ec != 0) { io_stream_set_error(&dstream->istream.iostream, "Cannot initialize decryption: %s", error); dstream->istream.istream.stream_errno = EIO; }; return &dstream->istream.istream; } struct istream * i_stream_create_decrypt_callback(struct istream *input, i_stream_decrypt_get_key_callback_t *callback, void *context) { struct decrypt_istream *dstream; i_assert(callback != NULL); dstream = i_stream_create_decrypt_common(input); dstream->key_callback = callback; dstream->key_context = context; return &dstream->istream.istream; } dovecot-2.2.33.2/src/lib-dcrypt/Makefile.am0000644000175000017500000000362113165463624015235 00000000000000noinst_LTLIBRARIES = libdcrypt.la pkglib_LTLIBRARIES = NOPLUGIN_LDFLAGS= AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-ssl-iostream \ -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" libdcrypt_la_SOURCES = \ dcrypt.c \ istream-decrypt.c \ ostream-encrypt.c libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" libdcrypt_la_LDFLAGS = -export-dynamic if BUILD_DCRYPT_OPENSSL pkglib_LTLIBRARIES += libdcrypt_openssl.la libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c libdcrypt_openssl_la_LDFLAGS = -module -avoid-version ../lib-ssl-iostream/libssl_iostream_openssl.la libdcrypt_openssl_la_LIBADD = $(SSL_LIBS) libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libssl_iostream_openssl.la libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ $(SSL_CFLAGS) endif headers = \ dcrypt.h \ dcrypt-iostream.h \ dcrypt-private.h \ ostream-encrypt.h \ istream-decrypt.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) EXTRA_DIST = \ sample-v1.asc \ sample-v1_short.asc \ sample-v2.asc test_programs = test-crypto test-stream noinst_PROGRAMS = $(test_programs) check: check-am check-test check-test: all-am for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done LIBDOVECOT_TEST_DEPS = \ ../lib-ssl-iostream/libssl_iostream.la \ ../lib-test/libtest.la \ ../lib/liblib.la LIBDOVECOT_TEST = \ $(LIBDOVECOT_TEST_DEPS) \ $(MODULE_LIBS) test_crypto_LDADD = $(LIBDOVECOT_TEST) test_crypto_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) test_crypto_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" test_crypto_SOURCES = $(libdcrypt_la_SOURCES) test-crypto.c test_stream_LDADD = $(LIBDOVECOT_TEST) test_stream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) test_stream_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" test_stream_SOURCES = $(libdcrypt_la_SOURCES) test-stream.c dovecot-2.2.33.2/src/lib-test/0002755000175000017500000000000013172375610012726 500000000000000dovecot-2.2.33.2/src/lib-test/Makefile.in0000644000175000017500000005414213172375574014730 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-test ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libtest_la_LIBADD = am_libtest_la_OBJECTS = test-common.lo test-istream.lo test-ostream.lo libtest_la_OBJECTS = $(am_libtest_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libtest_la_SOURCES) DIST_SOURCES = $(libtest_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkginc_libdir)" HEADERS = $(pkginc_lib_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libtest.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-charset libtest_la_SOURCES = \ test-common.c \ test-istream.c \ test-ostream.c headers = \ test-common.h pkginc_libdir = $(pkgincludedir) pkginc_lib_HEADERS = $(headers) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-test/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-test/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES) $(EXTRA_libtest_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-istream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-ostream.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkginc_libHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkginc_libHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/lib-test/test-common.c0000644000175000017500000001751113165463624015267 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "test-common.h" #include #include /* _exit() */ #include /* for fatal tests */ static bool test_deinit_lib; /* To test the firing of i_assert, we need non-local jumps, i.e. setjmp */ static volatile bool expecting_fatal = FALSE; static jmp_buf fatal_jmpbuf; #define OUT_NAME_ALIGN 70 static char *test_prefix; static bool test_success; static unsigned int failure_count; static unsigned int total_count; static unsigned int expected_errors; static char *expected_error_str; void test_begin(const char *name) { test_success = TRUE; if (!expecting_fatal) i_assert(test_prefix == NULL); else test_assert((test_success = (test_prefix == NULL))); test_prefix = i_strdup(name); } bool test_has_failed(void) { return !test_success; } void test_assert_failed(const char *code, const char *file, unsigned int line) { printf("%s:%u: Assert failed: %s\n", file, line, code); fflush(stdout); test_success = FALSE; } void test_assert_failed_idx(const char *code, const char *file, unsigned int line, long long i) { printf("%s:%u: Assert(#%lld) failed: %s\n", file, line, i, code); fflush(stdout); test_success = FALSE; } void test_assert_failed_strcmp(const char *code, const char *file, unsigned int line, const char * src, const char * dst) { printf("%s: Assert(#%u) failed: %s\n", file, line, code); printf(" \"%s\" != \"%s\"\n", src, dst); fflush(stdout); test_success = FALSE; } static void test_dump_rand_state(void) { static int seen_count = -1; int count = rand_get_seed_count(); if (count == seen_count) return; seen_count = count; if (count > 0) printf("test: random seed #%i was %u\n", rand_get_seed_count(), rand_get_last_seed()); else printf("test: random seed unknown\n"); } void test_end(void) { if (!expecting_fatal) i_assert(test_prefix != NULL); else test_assert(test_prefix != NULL); test_out("", test_success); if (!test_success) test_dump_rand_state(); i_free_and_null(test_prefix); test_success = FALSE; } void test_out(const char *name, bool success) { test_out_reason(name, success, NULL); } void test_out_quiet(const char *name, bool success) { if (success) { total_count++; return; } test_out(name, success); } void test_out_reason(const char *name, bool success, const char *reason) { int i = 0; if (test_prefix != NULL) { fputs(test_prefix, stdout); i += strlen(test_prefix); if (*name != '\0') { putchar(':'); i++; } putchar(' '); i++; } if (*name != '\0') { fputs(name, stdout); putchar(' '); i += strlen(name) + 1; } for (; i < OUT_NAME_ALIGN; i++) putchar('.'); fputs(" : ", stdout); if (success) fputs("ok", stdout); else { fputs("FAILED", stdout); test_success = FALSE; failure_count++; } if (reason != NULL && *reason != '\0') printf(": %s", reason); putchar('\n'); fflush(stdout); total_count++; } void test_expect_error_string(const char *substr) { i_assert(expected_errors == 0); expected_errors = 1; expected_error_str = i_strdup(substr); } void test_expect_errors(unsigned int expected) { i_assert(expected_errors == 0); expected_errors = expected; } void test_expect_no_more_errors(void) { test_assert(expected_errors == 0 && expected_error_str == NULL); i_free_and_null(expected_error_str); expected_errors = 0; } static void ATTR_FORMAT(2, 0) test_error_handler(const struct failure_context *ctx, const char *format, va_list args) { bool suppress = FALSE; #ifdef DEBUG if (ctx->type == LOG_TYPE_WARNING && strstr(format, "Growing") != NULL) { /* ignore "Growing memory pool" and "Growing data stack" warnings */ return; } #endif if (expected_errors > 0) { if (expected_error_str != NULL) T_BEGIN { /* test_assert() will reset test_success if need be. */ va_list args2; VA_COPY(args2, args); const char *str = t_strdup_vprintf(format, args2); suppress = strstr(str, expected_error_str) != NULL; test_assert(suppress == TRUE); i_free_and_null(expected_error_str); va_end(args2); } T_END; expected_errors--; } else { test_success = FALSE; } if (!suppress) { test_dump_rand_state(); default_error_handler(ctx, format, args); } } static void ATTR_FORMAT(2, 0) ATTR_NORETURN test_fatal_handler(const struct failure_context *ctx, const char *format, va_list args) { /* Prevent recursion, we can't handle our own errors */ i_set_fatal_handler(default_fatal_handler); i_assert(expecting_fatal); /* if not at the right time, bail */ i_set_fatal_handler(test_fatal_handler); longjmp(fatal_jmpbuf, 1); /* we simply can't get here - will the compiler complain? */ default_fatal_handler(ctx, format, args); } static void test_init(void) { test_prefix = NULL; failure_count = 0; total_count = 0; if (!lib_is_initialized()) { lib_init(); test_deinit_lib = TRUE; } else test_deinit_lib = FALSE; i_set_error_handler(test_error_handler); /* Don't set fatal handler until actually needed for fatal testing */ } static int test_deinit(void) { i_assert(test_prefix == NULL); printf("%u / %u tests failed\n", failure_count, total_count); if (test_deinit_lib) lib_deinit(); return failure_count == 0 ? 0 : 1; } static void test_run_funcs(void (*test_functions[])(void)) { unsigned int i; for (i = 0; test_functions[i] != NULL; i++) { T_BEGIN { test_functions[i](); } T_END; } } static void test_run_named_funcs(struct named_test tests[], const char *match) { unsigned int i; for (i = 0; tests[i].func != NULL; i++) { if (strstr(tests[i].name, match) != NULL) T_BEGIN { tests[i].func(); } T_END; } } static void run_one_fatal(test_fatal_func_t *fatal_function) { static unsigned int index = 0; for (;;) { volatile int jumped = setjmp(fatal_jmpbuf); if (jumped == 0) { /* normal flow */ expecting_fatal = TRUE; enum fatal_test_state ret = fatal_function(index); expecting_fatal = FALSE; if (ret == FATAL_TEST_FINISHED) { /* ran out of tests - good */ index = 0; break; } else if (ret == FATAL_TEST_FAILURE) { /* failed to fire assert - bad, but can continue */ test_success = FALSE; i_error("Desired assert failed to fire at step %i", index); index++; } else { /* FATAL_TEST_ABORT or other value */ test_success = FALSE; test_end(); index = 0; break; } } else { /* assert fired, continue with next test */ index++; } } } static void test_run_fatals(test_fatal_func_t *const fatal_functions[]) { unsigned int i; for (i = 0; fatal_functions[i] != NULL; i++) { T_BEGIN { run_one_fatal(fatal_functions[i]); } T_END; } } static void test_run_named_fatals(const struct named_fatal fatals[], const char *match) { unsigned int i; for (i = 0; fatals[i].func != NULL; i++) { if (strstr(fatals[i].name, match) != NULL) T_BEGIN { run_one_fatal(fatals[i].func); } T_END; } } int test_run(void (*test_functions[])(void)) { test_init(); test_run_funcs(test_functions); return test_deinit(); } int test_run_named(struct named_test tests[], const char *match) { test_init(); test_run_named_funcs(tests, match); return test_deinit(); } int test_run_with_fatals(void (*test_functions[])(void), test_fatal_func_t *const fatal_functions[]) { test_init(); test_run_funcs(test_functions); i_set_fatal_handler(test_fatal_handler); test_run_fatals(fatal_functions); return test_deinit(); } int test_run_named_with_fatals(const char *match, struct named_test tests[], struct named_fatal fatals[]) { test_init(); test_run_named_funcs(tests, match); i_set_fatal_handler(test_fatal_handler); test_run_named_fatals(fatals, match); return test_deinit(); } void ATTR_NORETURN test_exit(int status) { i_free_and_null(expected_error_str); i_free_and_null(test_prefix); (void)t_pop(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */ lib_deinit(); _exit(status); } dovecot-2.2.33.2/src/lib-test/test-ostream.c0000644000175000017500000001336613165463624015455 00000000000000/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "ostream-private.h" #include "test-common.h" struct test_ostream { struct ostream_private ostream; buffer_t *internal_buf; buffer_t *output_buf; size_t max_output_size; struct timeout *to; bool flush_pending; }; static void o_stream_test_destroy(struct iostream_private *stream) { struct test_ostream *tstream = (struct test_ostream *)stream; if (tstream->to != NULL) timeout_remove(&tstream->to); if (tstream->internal_buf != NULL) buffer_free(&tstream->internal_buf); } static int o_stream_test_flush(struct ostream_private *stream) { struct test_ostream *tstream = (struct test_ostream *)stream; if (tstream->internal_buf == NULL || tstream->internal_buf->used == 0) return 1; if (tstream->output_buf->used >= tstream->max_output_size) return 0; size_t left = tstream->max_output_size - tstream->output_buf->used; size_t n = I_MIN(left, tstream->internal_buf->used); buffer_append(tstream->output_buf, tstream->internal_buf->data, n); buffer_delete(tstream->internal_buf, 0, n); return tstream->internal_buf->used == 0 ? 1 : 0; } static ssize_t o_stream_test_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct test_ostream *tstream = (struct test_ostream *)stream; struct const_iovec cur_iov = { NULL, 0 }; size_t left, n; ssize_t ret = 0; unsigned int i; /* first we need to try to flush the internal buffer */ if ((ret = o_stream_test_flush(stream)) <= 0) return ret; /* append to output_buf until max_output_size is reached */ ret = 0; for (i = 0; i < iov_count; i++) { left = tstream->max_output_size < tstream->output_buf->used ? 0 : tstream->max_output_size - tstream->output_buf->used; n = I_MIN(left, iov[i].iov_len); buffer_append(tstream->output_buf, iov[i].iov_base, n); stream->ostream.offset += n; ret += n; if (n != iov[i].iov_len) { cur_iov.iov_base = CONST_PTR_OFFSET(iov[i].iov_base, n); cur_iov.iov_len = iov[i].iov_len - n; break; } } /* if we've internal_buf, append to it until max_buffer_size is reached */ if (i == iov_count || tstream->internal_buf == NULL) return ret; do { left = tstream->ostream.max_buffer_size - tstream->internal_buf->used; n = I_MIN(left, cur_iov.iov_len); buffer_append(tstream->internal_buf, cur_iov.iov_base, n); stream->ostream.offset += n; ret += n; if (n != cur_iov.iov_len) break; if (++i < iov_count) cur_iov = iov[i]; } while (i < iov_count); tstream->flush_pending = TRUE; return ret; } static void test_ostream_send_more(struct test_ostream *tstream) { struct ostream *ostream = &tstream->ostream.ostream; int ret; o_stream_ref(ostream); tstream->flush_pending = FALSE; if (tstream->ostream.callback != NULL) ret = tstream->ostream.callback(tstream->ostream.context); else ret = o_stream_test_flush(&tstream->ostream); if (ret == 0 || (tstream->internal_buf != NULL && tstream->internal_buf->used > 0)) tstream->flush_pending = TRUE; if (!tstream->flush_pending || tstream->output_buf->used >= tstream->max_output_size) timeout_remove(&tstream->to); o_stream_unref(&ostream); } static void test_ostream_set_send_more_timeout(struct test_ostream *tstream) { if (tstream->to == NULL && tstream->flush_pending && tstream->output_buf->used < tstream->max_output_size) tstream->to = timeout_add_short(0, test_ostream_send_more, tstream); } static void o_stream_test_flush_pending(struct ostream_private *stream, bool set) { struct test_ostream *tstream = (struct test_ostream *)stream; if (tstream->internal_buf != NULL && tstream->internal_buf->used > 0) { /* we have internal data, won't reset flush_pending */ i_assert(tstream->flush_pending); } else { tstream->flush_pending = set; } if (set) test_ostream_set_send_more_timeout(tstream); } static size_t o_stream_test_get_used_size(const struct ostream_private *stream) { struct test_ostream *tstream = (struct test_ostream *)stream; return tstream->internal_buf == NULL ? 0 : tstream->internal_buf->used; } struct ostream *test_ostream_create(buffer_t *output) { struct test_ostream *tstream; struct ostream *ostream; tstream = i_new(struct test_ostream, 1); tstream->ostream.max_buffer_size = (size_t)-1; tstream->ostream.iostream.destroy = o_stream_test_destroy; tstream->ostream.sendv = o_stream_test_sendv; tstream->ostream.flush = o_stream_test_flush; tstream->ostream.flush_pending = o_stream_test_flush_pending; tstream->ostream.get_used_size = o_stream_test_get_used_size; tstream->ostream.ostream.blocking = TRUE; tstream->output_buf = output; tstream->max_output_size = (size_t)-1; ostream = o_stream_create(&tstream->ostream, NULL, -1); o_stream_set_name(ostream, "(test-ostream)"); return ostream; } struct ostream *test_ostream_create_nonblocking(buffer_t *output, size_t max_internal_buffer_size) { struct test_ostream *tstream; tstream = (struct test_ostream *)test_ostream_create(output)->real_stream; tstream->internal_buf = buffer_create_dynamic(default_pool, 128); tstream->ostream.ostream.blocking = FALSE; tstream->ostream.max_buffer_size = max_internal_buffer_size; return &tstream->ostream.ostream; } static struct test_ostream *test_ostream_find(struct ostream *output) { struct ostream *out; for (out = output; out != NULL; out = out->real_stream->parent) { if (out->real_stream->sendv == o_stream_test_sendv) return (struct test_ostream *)out->real_stream; } i_panic("%s isn't test-ostream", o_stream_get_name(output)); } void test_ostream_set_max_output_size(struct ostream *output, size_t max_size) { struct test_ostream *tstream = test_ostream_find(output); tstream->max_output_size = max_size; test_ostream_set_send_more_timeout(tstream); } dovecot-2.2.33.2/src/lib-test/test-common.h0000644000175000017500000001000413165463624015262 00000000000000#ifndef TEST_COMMON_H #define TEST_COMMON_H struct istream *test_istream_create(const char *data); struct istream *test_istream_create_data(const void *data, size_t size); void test_istream_set_size(struct istream *input, uoff_t size); void test_istream_set_allow_eof(struct istream *input, bool allow); void test_istream_set_max_buffer_size(struct istream *input, size_t size); struct ostream *test_ostream_create(buffer_t *output); struct ostream *test_ostream_create_nonblocking(buffer_t *output, size_t max_internal_buffer_size); /* When output->used reaches max_size, start buffering output internally. When internal buffer reaches max_internal_buffer_size, start returning 0 for o_stream_send*(). */ void test_ostream_set_max_output_size(struct ostream *output, size_t max_size); void test_begin(const char *name); #define test_assert(code) STMT_START { \ if (!(code)) test_assert_failed(#code, __FILE__, __LINE__); \ } STMT_END /* Additional parameter may be int or unsigned int, to indicate which of * a barrage of tests have failed (such as in a loop). */ #define test_assert_idx(code, i) STMT_START { \ if (!(code)) test_assert_failed_idx(#code, __FILE__, __LINE__, i); \ } STMT_END /* Additional parameters are s1 (source) and s2 (destination) string * in strcmp(). */ #define test_assert_strcmp(s1, s2) STMT_START { \ if ((strcmp(s1,s2) != 0)) test_assert_failed_strcmp("strcmp(" #s1 "," #s2 ")", __FILE__, __LINE__, s1, s2); \ } STMT_END void test_assert_failed(const char *code, const char *file, unsigned int line); void test_assert_failed_idx(const char *code, const char *file, unsigned int line, long long i); void test_assert_failed_strcmp(const char *code, const char *file, unsigned int line, const char * src, const char * dst); bool test_has_failed(void); /* If you're testing nasty cases which you want to warn, surround the noisy op with these */ void test_expect_errors(unsigned int expected); void test_expect_error_string(const char *substr); /* expect just 1 message matching the printf format */ void test_expect_no_more_errors(void); /* Note that test_expect_error{s,_string}() effectively begin with a check equivalent to test_expect_no_more_errors(), so you don't need the latter explicitly if following it with either of the former.*/ void test_end(void); void test_out(const char *name, bool success); void test_out_quiet(const char *name, bool success); /* only prints failures */ void test_out_reason(const char *name, bool success, const char *reason) ATTR_NULL(3); int test_run(void (*test_functions[])(void)); struct named_test { const char *name; void (*func)(void); }; int test_run_named(struct named_test tests[], const char *match); #define TEST_DECL(x) void x(void); #define TEST_NAMELESS(x) x, /* Were you to want to use the X trick but not name the tests */ #define TEST_NAMED(x) { .name = #x , .func = x }, enum fatal_test_state { FATAL_TEST_FINISHED, /* no more test stages, don't call again */ FATAL_TEST_FAILURE, /* single stage has failed, continue */ FATAL_TEST_ABORT, /* something's gone horrifically wrong */ }; /* The fatal function is called first with stage=0. After each call the stage is increased by 1. The idea is that each stage would be running an individual test that is supposed to crash. The function is called until FATAL_TEST_FINISHED or FATAL_TEST_ABORT is returned. */ typedef enum fatal_test_state test_fatal_func_t(unsigned int stage); struct named_fatal { const char *name; test_fatal_func_t *func; }; int test_run_with_fatals(void (*test_functions[])(void), test_fatal_func_t *const fatal_functions[]); int test_run_named_with_fatals(const char *match, struct named_test tests[], struct named_fatal fatals[]); #define FATAL_DECL(x) enum fatal_test_state x(unsigned int); #define FATAL_NAMELESS(x) x, /* Were you to want to use the X trick but not name the tests */ #define FATAL_NAMED(x) { .name = #x , .func = x }, /* If a fork() wants to exit(), then this will avoid valgrind leak errors */ void test_exit(int status) ATTR_NORETURN; #endif dovecot-2.2.33.2/src/lib-test/Makefile.am0000644000175000017500000000042413123174404014672 00000000000000noinst_LTLIBRARIES = libtest.la AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-charset libtest_la_SOURCES = \ test-common.c \ test-istream.c \ test-ostream.c headers = \ test-common.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) dovecot-2.2.33.2/src/lib-test/test-istream.c0000644000175000017500000000755113165463624015446 00000000000000/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "istream-private.h" #include "test-common.h" struct test_istream { struct istream_private istream; const void *orig_buffer; unsigned int skip_diff; size_t max_pos; bool allow_eof; }; static ssize_t test_read(struct istream_private *stream) { struct test_istream *tstream = (struct test_istream *)stream; unsigned int new_skip_diff; size_t cur_max; ssize_t ret; i_assert(stream->skip <= stream->pos); if (stream->pos - stream->skip >= tstream->istream.max_buffer_size) { i_assert(stream->skip != stream->pos); return -2; } if (tstream->max_pos < stream->pos) { /* we seeked past the end of file. */ ret = 0; } else { /* copy data to a buffer in somewhat random place. this could help catch bugs. */ new_skip_diff = rand() % 128; stream->skip = (stream->skip - tstream->skip_diff) + new_skip_diff; stream->pos = (stream->pos - tstream->skip_diff) + new_skip_diff; tstream->max_pos = (tstream->max_pos - tstream->skip_diff) + new_skip_diff; tstream->skip_diff = new_skip_diff; cur_max = tstream->max_pos; if (stream->max_buffer_size < (size_t)-1 - stream->skip && cur_max > stream->skip + stream->max_buffer_size) cur_max = stream->skip + stream->max_buffer_size; /* use exactly correct buffer size so valgrind can catch read overflows */ if (stream->buffer_size != cur_max && cur_max > 0) { stream->w_buffer = i_realloc(stream->w_buffer, stream->buffer_size, cur_max); stream->buffer = stream->w_buffer; stream->buffer_size = cur_max; } ssize_t size = cur_max - new_skip_diff; if (size > 0) memcpy(stream->w_buffer + new_skip_diff, tstream->orig_buffer, (size_t)size); ret = cur_max - stream->pos; stream->pos = cur_max; } if (ret > 0) return ret; else if (!tstream->allow_eof || stream->pos - tstream->skip_diff < (uoff_t)stream->statbuf.st_size) return 0; else { stream->istream.eof = TRUE; return -1; } } static void test_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct test_istream *tstream = (struct test_istream *)stream; stream->istream.v_offset = v_offset; stream->skip = v_offset + tstream->skip_diff; stream->pos = stream->skip; } struct istream *test_istream_create_data(const void *data, size_t size) { struct test_istream *tstream; tstream = i_new(struct test_istream, 1); tstream->orig_buffer = data; tstream->istream.read = test_read; tstream->istream.seek = test_seek; tstream->istream.istream.blocking = FALSE; tstream->istream.istream.seekable = TRUE; i_stream_create(&tstream->istream, NULL, -1); tstream->istream.statbuf.st_size = tstream->max_pos = size; tstream->allow_eof = TRUE; tstream->istream.max_buffer_size = (size_t)-1; return &tstream->istream.istream; } struct istream *test_istream_create(const char *data) { return test_istream_create_data(data, strlen(data)); } static struct test_istream *test_istream_find(struct istream *input) { struct istream *in; for (in = input; in != NULL; in = in->real_stream->parent) { if (in->real_stream->read == test_read) return (struct test_istream *)in->real_stream; } i_panic("%s isn't test-istream", i_stream_get_name(input)); } void test_istream_set_allow_eof(struct istream *input, bool allow) { struct test_istream *tstream = test_istream_find(input); tstream->allow_eof = allow; } void test_istream_set_max_buffer_size(struct istream *input, size_t size) { struct test_istream *tstream = test_istream_find(input); tstream->istream.max_buffer_size = size; } void test_istream_set_size(struct istream *input, uoff_t size) { struct test_istream *tstream = test_istream_find(input); if (size > (uoff_t)tstream->istream.statbuf.st_size) size = (uoff_t)tstream->istream.statbuf.st_size; tstream->max_pos = size + tstream->skip_diff; } dovecot-2.2.33.2/src/Makefile.am0000644000175000017500000000146713165463624013172 00000000000000if HAVE_LDAP LIB_LDAP=lib-ldap endif LIBDOVECOT_SUBDIRS = \ lib-test \ lib \ lib-settings \ lib-auth \ lib-master \ lib-charset \ lib-ssl-iostream \ lib-dcrypt \ lib-dns \ lib-dict \ lib-sasl \ lib-stats \ lib-http \ lib-fs \ lib-mail \ lib-smtp \ lib-imap \ lib-imap-storage \ lib-program-client \ lib-oauth2 SUBDIRS = \ $(LIBDOVECOT_SUBDIRS) \ lib-dict-extra \ lib-dovecot \ $(LIB_LDAP) \ lib-fts \ lib-imap-client \ lib-imap-urlauth \ lib-compression \ lib-index \ lib-storage \ lib-sql \ lib-ntlm \ lib-otp \ lib-lda \ anvil \ auth \ dict \ dns \ indexer \ ipc \ master \ login-common \ imap-hibernate \ imap-login \ imap \ imap-urlauth \ pop3-login \ pop3 \ lda \ lmtp \ log \ config \ director \ replication \ util \ doveadm \ ssl-params \ stats \ plugins dovecot-2.2.33.2/src/log/0002755000175000017500000000000013172375612011766 500000000000000dovecot-2.2.33.2/src/log/log-error-buffer.h0000644000175000017500000000120513123174404015222 00000000000000#ifndef LOG_ERROR_BUFFER_H #define LOG_ERROR_BUFFER_H struct log_error_buffer; struct log_error { enum log_type type; time_t timestamp; const char *prefix; const char *text; }; struct log_error_buffer *log_error_buffer_init(void); void log_error_buffer_add(struct log_error_buffer *buf, const struct log_error *error); void log_error_buffer_deinit(struct log_error_buffer **buf); struct log_error_buffer_iter * log_error_buffer_iter_init(struct log_error_buffer *buf); struct log_error * log_error_buffer_iter_next(struct log_error_buffer_iter *iter); void log_error_buffer_iter_deinit(struct log_error_buffer_iter **iter); #endif dovecot-2.2.33.2/src/log/Makefile.in0000644000175000017500000005410713172375574013767 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ pkglibexec_PROGRAMS = log$(EXEEXT) subdir = src/log ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(pkglibexecdir)" PROGRAMS = $(pkglibexec_PROGRAMS) am_log_OBJECTS = doveadm-connection.$(OBJEXT) log-connection.$(OBJEXT) \ log-error-buffer.$(OBJEXT) log-settings.$(OBJEXT) \ main.$(OBJEXT) log_OBJECTS = $(am_log_OBJECTS) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(log_SOURCES) DIST_SOURCES = $(log_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkglibexecdir = $(libexecdir)/dovecot ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master log_LDADD = $(LIBDOVECOT) log_DEPENDENCIES = $(LIBDOVECOT_DEPS) log_SOURCES = \ doveadm-connection.c \ log-connection.c \ log-error-buffer.c \ log-settings.c \ main.c noinst_HEADERS = \ doveadm-connection.h \ log-connection.h \ log-error-buffer.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/log/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/log/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list log$(EXEEXT): $(log_OBJECTS) $(log_DEPENDENCIES) $(EXTRA_log_DEPENDENCIES) @rm -f log$(EXEEXT) $(AM_V_CCLD)$(LINK) $(log_OBJECTS) $(log_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log-error-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkglibexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-pkglibexecPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkglibexecPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-pkglibexecPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkglibexecPROGRAMS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-pkglibexecPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/src/log/doveadm-connection.c0000644000175000017500000000422513165463624015632 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "master-service.h" #include "log-error-buffer.h" #include "doveadm-connection.h" #include struct doveadm_connection { struct log_error_buffer *errorbuf; int fd; struct ostream *output; }; static void doveadm_connection_destroy(struct doveadm_connection **_conn); static int doveadm_connection_send_errors(struct doveadm_connection *conn) { struct log_error_buffer_iter *iter; const struct log_error *error; string_t *str = t_str_new(256); int ret = 0; iter = log_error_buffer_iter_init(conn->errorbuf); while ((error = log_error_buffer_iter_next(iter)) != NULL) { str_truncate(str, 0); str_printfa(str, "%s\t%ld\t", failure_log_type_names[error->type], (long)error->timestamp); str_append_tabescaped(str, error->prefix); str_append_c(str, '\t'); str_append_tabescaped(str, error->text); str_append_c(str, '\n'); if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) { ret = -1; break; } } log_error_buffer_iter_deinit(&iter); return ret; } static int doveadm_output(struct doveadm_connection *conn) { int ret; if ((ret = o_stream_flush(conn->output)) != 0) { /* error / finished */ doveadm_connection_destroy(&conn); } return 1; } void doveadm_connection_create(struct log_error_buffer *errorbuf, int fd) { struct doveadm_connection *conn; conn = i_new(struct doveadm_connection, 1); conn->errorbuf = errorbuf; conn->fd = fd; conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); if (doveadm_connection_send_errors(conn) < 0) doveadm_connection_destroy(&conn); else { o_stream_set_flush_callback(conn->output, doveadm_output, conn); o_stream_set_flush_pending(conn->output, TRUE); } } static void doveadm_connection_destroy(struct doveadm_connection **_conn) { struct doveadm_connection *conn = *_conn; *_conn = NULL; o_stream_destroy(&conn->output); if (close(conn->fd) < 0) i_error("close(doveadm connection) failed: %m"); i_free(conn); master_service_client_connection_destroyed(master_service); } dovecot-2.2.33.2/src/log/log-settings.c0000644000175000017500000000213413123174404014457 00000000000000/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include /* */ static struct file_listener_settings log_unix_listeners_array[] = { { "log-errors", 0600, "", "" } }; static struct file_listener_settings *log_unix_listeners[] = { &log_unix_listeners_array[0] }; static buffer_t log_unix_listeners_buf = { log_unix_listeners, sizeof(log_unix_listeners), { NULL, } }; /* */ struct service_settings log_service_settings = { .name = "log", .protocol = "", .type = "log", .executable = "log", .user = "", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 1, .client_limit = 0, .service_count = 0, .idle_kill = UINT_MAX, .vsz_limit = (uoff_t)-1, .unix_listeners = { { &log_unix_listeners_buf, sizeof(log_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, .process_limit_1 = TRUE }; dovecot-2.2.33.2/src/log/main.c0000644000175000017500000000447713165463624013013 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "lib-signals.h" #include "restrict-access.h" #include "master-interface.h" #include "master-service.h" #include "master-service-settings.h" #include "log-error-buffer.h" #include "log-connection.h" #include "doveadm-connection.h" #include bool verbose_proctitle; static struct log_error_buffer *errorbuf; static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { master_service_init_log(master_service, "log: "); } static void main_init(void) { lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE, sig_reopen_logs, NULL); errorbuf = log_error_buffer_init(); log_connections_init(); } static void main_deinit(void) { log_connections_deinit(); log_error_buffer_deinit(&errorbuf); } static void client_connected(struct master_service_connection *conn) { if (conn->fifo) { log_connection_create(errorbuf, conn->fd, conn->listen_fd); /* kludge: normally FIFOs aren't counted as connections, but here we want log process to stay open until all writers have closed */ conn->fifo = FALSE; } else if (strcmp(conn->name, "log-errors") == 0) doveadm_connection_create(errorbuf, conn->fd); else { i_error("Unknown listener name: %s", conn->name); return; } master_service_client_connection_accept(conn); } int main(int argc, char *argv[]) { const char *error; master_service = master_service_init("log", 0, &argc, &argv, ""); /* use log prefix and log to stderr until we've configured the real logging */ i_set_failure_file("/dev/stderr", "log: "); if (master_getopt(master_service) > 0) return FATAL_DEFAULT; if (master_service_settings_read_simple(master_service, NULL, &error) < 0) i_fatal("Error reading configuration: %s", error); master_service_init_log(master_service, "log: "); verbose_proctitle = master_service_settings_get(master_service)->verbose_proctitle; restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); /* logging should never die if there are some clients */ master_service_set_die_with_master(master_service, FALSE); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; } dovecot-2.2.33.2/src/log/log-connection.c0000644000175000017500000002750213165463624014777 00000000000000/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "istream.h" #include "llist.h" #include "hash.h" #include "time-util.h" #include "process-title.h" #include "master-interface.h" #include "master-service.h" #include "log-error-buffer.h" #include "log-connection.h" #include #include #define FATAL_QUEUE_TIMEOUT_MSECS 500 #define MAX_MSECS_PER_CONNECTION 100 /* Log a warning after 1 secs when we've been all the time busy writing the log connection. */ #define LOG_WARN_PENDING_COUNT (1000 / MAX_MSECS_PER_CONNECTION) /* If we keep beeing busy, log a warning every 60 seconds. */ #define LOG_WARN_PENDING_INTERVAL (60 * LOG_WARN_PENDING_COUNT) struct log_client { struct ip_addr ip; char *prefix; unsigned int fatal_logged:1; }; struct log_connection { struct log_connection *prev, *next; struct log_error_buffer *errorbuf; int fd; int listen_fd; struct io *io; struct istream *input; char *default_prefix; HASH_TABLE(void *, struct log_client *) clients; unsigned int pending_count; unsigned int master:1; unsigned int handshaked:1; }; static struct log_connection *log_connections = NULL; static ARRAY(struct log_connection *) logs_by_fd; static unsigned int global_pending_count; static struct log_connection *last_pending_log; static void log_connection_destroy(struct log_connection *log); static void log_refresh_proctitle(void) { if (!verbose_proctitle) return; if (global_pending_count == 0) process_title_set(""); else if (last_pending_log == NULL) { process_title_set(t_strdup_printf( "[%u services too fast]", global_pending_count)); } else if (global_pending_count > 1) { process_title_set(t_strdup_printf( "[%u services too fast, last: %d/%d/%s]", global_pending_count, last_pending_log->fd, last_pending_log->listen_fd, last_pending_log->default_prefix)); } else { process_title_set(t_strdup_printf( "[service too fast: %d/%d/%s]", last_pending_log->fd, last_pending_log->listen_fd, last_pending_log->default_prefix)); } } static struct log_client *log_client_get(struct log_connection *log, pid_t pid) { struct log_client *client; client = hash_table_lookup(log->clients, POINTER_CAST(pid)); if (client == NULL) { client = i_new(struct log_client, 1); hash_table_insert(log->clients, POINTER_CAST(pid), client); } return client; } static void log_client_free(struct log_connection *log, struct log_client *client, pid_t pid) { hash_table_remove(log->clients, POINTER_CAST(pid)); i_free(client->prefix); i_free(client); } static void log_parse_option(struct log_connection *log, const struct failure_line *failure) { struct log_client *client; client = log_client_get(log, failure->pid); if (strncmp(failure->text, "ip=", 3) == 0) (void)net_addr2ip(failure->text + 3, &client->ip); else if (strncmp(failure->text, "prefix=", 7) == 0) { i_free(client->prefix); client->prefix = i_strdup(failure->text + 7); } } static void client_log_ctx(struct log_connection *log, const struct failure_context *ctx, const struct timeval *log_time, const char *prefix, const char *text) { struct log_error err; switch (ctx->type) { case LOG_TYPE_DEBUG: case LOG_TYPE_INFO: case LOG_TYPE_COUNT: case LOG_TYPE_OPTION: break; case LOG_TYPE_WARNING: case LOG_TYPE_ERROR: case LOG_TYPE_FATAL: case LOG_TYPE_PANIC: i_zero(&err); err.type = ctx->type; err.timestamp = log_time->tv_sec; err.prefix = prefix; err.text = text; log_error_buffer_add(log->errorbuf, &err); break; } i_set_failure_prefix("%s", prefix); i_log_type(ctx, "%s", text); i_set_failure_prefix("log: "); } static void client_log_fatal(struct log_connection *log, struct log_client *client, const char *line, const struct timeval *log_time, const struct tm *tm) { struct failure_context failure_ctx; const char *prefix = log->default_prefix; i_zero(&failure_ctx); failure_ctx.type = LOG_TYPE_FATAL; failure_ctx.timestamp = tm; failure_ctx.timestamp_usecs = log_time->tv_usec; if (client != NULL) { if (client->prefix != NULL) prefix = client->prefix; else if (client->ip.family != 0) { line = t_strdup_printf("%s [last ip=%s]", line, net_ip2addr(&client->ip)); } } client_log_ctx(log, &failure_ctx, log_time, prefix, t_strconcat("master: ", line, NULL)); } static void log_parse_master_line(const char *line, const struct timeval *log_time, const struct tm *tm) { struct log_connection *const *logs, *log; struct log_client *client; const char *p, *p2, *cmd, *pidstr; unsigned int count; unsigned int service_fd; pid_t pid; p = strchr(line, ' '); if (p == NULL || (p2 = strchr(++p, ' ')) == NULL || str_to_uint(t_strcut(line, ' '), &service_fd) < 0) { i_error("Received invalid input from master: %s", line); return; } pidstr = t_strcut(p, ' '); if (str_to_pid(pidstr, &pid) < 0) { i_error("Received invalid pid from master: %s", pidstr); return; } cmd = p2 + 1; logs = array_get(&logs_by_fd, &count); if (service_fd >= count || logs[service_fd] == NULL) { if (strcmp(cmd, "BYE") == 0 && service_fd < count) { /* master is probably shutting down and we already noticed the log fd closing */ return; } i_error("Received master input for invalid service_fd %u: %s", service_fd, line); return; } log = logs[service_fd]; client = hash_table_lookup(log->clients, POINTER_CAST(pid)); if (strcmp(cmd, "BYE") == 0) { if (client == NULL) { /* we haven't seen anything important from this client. it's not an error. */ return; } log_client_free(log, client, pid); } else if (strncmp(cmd, "FATAL ", 6) == 0) { client_log_fatal(log, client, cmd + 6, log_time, tm); } else if (strncmp(cmd, "DEFAULT-FATAL ", 14) == 0) { /* If the client has logged a fatal/panic, don't log this message. */ if (client == NULL || !client->fatal_logged) client_log_fatal(log, client, cmd + 14, log_time, tm); } else { i_error("Received unknown command from master: %s", cmd); } } static void log_it(struct log_connection *log, const char *line, const struct timeval *log_time, const struct tm *tm) { struct failure_line failure; struct failure_context failure_ctx; struct log_client *client = NULL; const char *prefix; if (log->master) { T_BEGIN { log_parse_master_line(line, log_time, tm); } T_END; return; } i_failure_parse_line(line, &failure); switch (failure.log_type) { case LOG_TYPE_FATAL: case LOG_TYPE_PANIC: if (failure.pid != 0) { client = log_client_get(log, failure.pid); client->fatal_logged = TRUE; } break; case LOG_TYPE_OPTION: log_parse_option(log, &failure); return; default: client = failure.pid == 0 ? NULL : hash_table_lookup(log->clients, POINTER_CAST(failure.pid)); break; } i_assert(failure.log_type < LOG_TYPE_COUNT); i_zero(&failure_ctx); failure_ctx.type = failure.log_type; failure_ctx.timestamp = tm; failure_ctx.timestamp_usecs = log_time->tv_usec; prefix = client != NULL && client->prefix != NULL ? client->prefix : log->default_prefix; client_log_ctx(log, &failure_ctx, log_time, prefix, failure.text); } static int log_connection_handshake(struct log_connection *log) { struct log_service_handshake handshake; const unsigned char *data; size_t size; ssize_t ret; /* we're reading from a FIFO, so we're assuming that we're getting a full handshake packet immediately. if not, treat it as an error message that we want to log. */ ret = i_stream_read(log->input); if (ret < 0) { i_error("read(log %s) failed: %s", log->default_prefix, i_stream_get_error(log->input)); return -1; } if ((size_t)ret < sizeof(handshake)) { /* this isn't a handshake */ return 0; } data = i_stream_get_data(log->input, &size); i_assert(size >= sizeof(handshake)); memcpy(&handshake, data, sizeof(handshake)); if (handshake.log_magic != MASTER_LOG_MAGIC) { /* this isn't a handshake */ return 0; } if (handshake.prefix_len > size - sizeof(handshake)) { i_error("Missing prefix data in handshake"); return -1; } i_free(log->default_prefix); log->default_prefix = i_strndup(data + sizeof(handshake), handshake.prefix_len); i_stream_skip(log->input, sizeof(handshake) + handshake.prefix_len); if (strcmp(log->default_prefix, MASTER_LOG_PREFIX_NAME) == 0) { if (log->listen_fd != MASTER_LISTEN_FD_FIRST) { i_error("Received master prefix in handshake " "from non-master fd %d", log->fd); return -1; } log->master = TRUE; } log->handshaked = TRUE; return 0; } static void log_connection_input(struct log_connection *log) { const char *line; ssize_t ret; struct timeval now, start_timeval; struct tm tm; bool too_much = FALSE; if (!log->handshaked) { if (log_connection_handshake(log) < 0) { log_connection_destroy(log); return; } /* come back here even if we read something else besides a handshake. the first few lines could be coming from e.g. libc before the proper handshake line is sent. */ } io_loop_time_refresh(); start_timeval = ioloop_timeval; while ((ret = i_stream_read(log->input)) > 0 || ret == -2) { /* get new timestamps for every read() */ now = ioloop_timeval; tm = *localtime(&now.tv_sec); while ((line = i_stream_next_line(log->input)) != NULL) log_it(log, line, &now, &tm); io_loop_time_refresh(); if (timeval_diff_msecs(&ioloop_timeval, &start_timeval) > MAX_MSECS_PER_CONNECTION) { too_much = TRUE; break; } } if (log->input->eof) { if (log->input->stream_errno != 0) i_error("read(log %s) failed: %m", log->default_prefix); log_connection_destroy(log); } else { i_assert(!log->input->closed); if (!too_much) { if (log->pending_count > 0) { log->pending_count = 0; i_assert(global_pending_count > 0); global_pending_count--; if (log == last_pending_log) last_pending_log = NULL; log_refresh_proctitle(); } return; } last_pending_log = log; if (log->pending_count++ == 0) { global_pending_count++; log_refresh_proctitle(); } if (log->pending_count == LOG_WARN_PENDING_COUNT || (log->pending_count % LOG_WARN_PENDING_INTERVAL) == 0) { i_warning("Log connection fd %d listen_fd %d prefix '%s' is sending input faster than we can write", log->fd, log->listen_fd, log->default_prefix); } } } void log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd) { struct log_connection *log; log = i_new(struct log_connection, 1); log->errorbuf = errorbuf; log->fd = fd; log->listen_fd = listen_fd; log->io = io_add(fd, IO_READ, log_connection_input, log); log->input = i_stream_create_fd(fd, PIPE_BUF, FALSE); log->default_prefix = i_strdup_printf("listen_fd %d", listen_fd); hash_table_create_direct(&log->clients, default_pool, 0); array_idx_set(&logs_by_fd, listen_fd, &log); DLLIST_PREPEND(&log_connections, log); log_connection_input(log); } static void log_connection_destroy(struct log_connection *log) { struct hash_iterate_context *iter; void *key; struct log_client *client; array_idx_clear(&logs_by_fd, log->listen_fd); DLLIST_REMOVE(&log_connections, log); iter = hash_table_iterate_init(log->clients); while (hash_table_iterate(iter, log->clients, &key, &client)) i_free(client); hash_table_iterate_deinit(&iter); hash_table_destroy(&log->clients); i_stream_unref(&log->input); if (log->io != NULL) io_remove(&log->io); if (close(log->fd) < 0) i_error("close(log connection fd) failed: %m"); i_free(log->default_prefix); i_free(log); master_service_client_connection_destroyed(master_service); } void log_connections_init(void) { i_array_init(&logs_by_fd, 64); } void log_connections_deinit(void) { /* normally we don't exit until all log connections are gone, but we could get here when we're being killed by a signal */ while (log_connections != NULL) log_connection_destroy(log_connections); array_free(&logs_by_fd); } dovecot-2.2.33.2/src/log/log-error-buffer.c0000644000175000017500000000516613123174404015227 00000000000000/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "log-error-buffer.h" #define LOG_ERROR_BUFFER_MAX_LINES 1000 struct log_error_data { struct log_error_data *next; enum log_type type; time_t timestamp; unsigned char prefix_text[FLEXIBLE_ARRAY_MEMBER]; }; struct log_error_buffer { struct log_error_data *head, *tail; unsigned int count; }; struct log_error_buffer_iter { struct log_error_buffer *buf; struct log_error_data *cur; struct log_error error; }; struct log_error_buffer *log_error_buffer_init(void) { struct log_error_buffer *buf; buf = i_new(struct log_error_buffer, 1); return buf; } static void log_error_buffer_delete_head(struct log_error_buffer *buf) { struct log_error_data *data; i_assert(buf->head != NULL); buf->count--; data = buf->head; buf->head = data->next; if (buf->tail == data) { /* last one */ buf->tail = NULL; } i_free(data); } void log_error_buffer_add(struct log_error_buffer *buf, const struct log_error *error) { size_t prefix_size = strlen(error->prefix)+1; size_t text_size = strlen(error->text)+1; struct log_error_data *data; if (buf->count == LOG_ERROR_BUFFER_MAX_LINES) log_error_buffer_delete_head(buf); /* @UNSAFE */ data = i_malloc(MALLOC_ADD(sizeof(*data), MALLOC_ADD(prefix_size, text_size))); data->type = error->type; data->timestamp = error->timestamp; memcpy(data->prefix_text, error->prefix, prefix_size); memcpy(data->prefix_text + prefix_size, error->text, text_size); if (buf->tail != NULL) buf->tail->next = data; else buf->head = data; buf->tail = data; buf->count++; } void log_error_buffer_deinit(struct log_error_buffer **_buf) { struct log_error_buffer *buf = *_buf; *_buf = NULL; while (buf->count > 0) log_error_buffer_delete_head(buf); i_free(buf); } struct log_error_buffer_iter * log_error_buffer_iter_init(struct log_error_buffer *buf) { struct log_error_buffer_iter *iter; iter = i_new(struct log_error_buffer_iter, 1); iter->buf = buf; iter->cur = buf->head; return iter; } struct log_error * log_error_buffer_iter_next(struct log_error_buffer_iter *iter) { struct log_error_data *data = iter->cur; if (data == NULL) return NULL; iter->cur = iter->cur->next; iter->error.type = data->type; iter->error.timestamp = data->timestamp; iter->error.prefix = (const void *)data->prefix_text; iter->error.text = (const void *)(data->prefix_text + strlen(iter->error.prefix) + 1); return &iter->error; } void log_error_buffer_iter_deinit(struct log_error_buffer_iter **_iter) { struct log_error_buffer_iter *iter = *_iter; *_iter = NULL; i_free(iter); } dovecot-2.2.33.2/src/log/doveadm-connection.h0000644000175000017500000000021613123174404015620 00000000000000#ifndef DOVEADM_CONNECTION_H #define DOVEADM_CONNECTION_H void doveadm_connection_create(struct log_error_buffer *errorbuf, int fd); #endif dovecot-2.2.33.2/src/log/log-connection.h0000644000175000017500000000042413165463624014776 00000000000000#ifndef LOG_CONNECTION_H #define LOG_CONNECTION_H struct log_connection; extern bool verbose_proctitle; void log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd); void log_connections_init(void); void log_connections_deinit(void); #endif dovecot-2.2.33.2/src/log/Makefile.am0000644000175000017500000000066313165463624013750 00000000000000pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = log AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-master log_LDADD = $(LIBDOVECOT) log_DEPENDENCIES = $(LIBDOVECOT_DEPS) log_SOURCES = \ doveadm-connection.c \ log-connection.c \ log-error-buffer.c \ log-settings.c \ main.c noinst_HEADERS = \ doveadm-connection.h \ log-connection.h \ log-error-buffer.h dovecot-2.2.33.2/config.h.in0000644000175000017500000004725113144653647012376 00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define if you have buggy CMSG macros */ #undef BUGGY_CMSG_MACROS /* Built-in Cassandra support */ #undef BUILD_CASSANDRA /* Build with CDB support */ #undef BUILD_CDB /* Build with Berkeley DB support */ #undef BUILD_DB /* Built-in MySQL support */ #undef BUILD_MYSQL /* Built-in PostgreSQL support */ #undef BUILD_PGSQL /* Built-in SQLite support */ #undef BUILD_SQLITE /* GSSAPI support is built in */ #undef BUILTIN_GSSAPI /* LDAP support is built in */ #undef BUILTIN_LDAP /* IMAP capabilities advertised in banner */ #undef CAPABILITY_BANNER_STRING /* IMAP capabilities */ #undef CAPABILITY_STRING /* Define if _XPG6 macro is needed for crypt() */ #undef CRYPT_USE_XPG6 /* Build with extra debugging checks */ #undef DEBUG /* Define if your dev_t is a structure instead of integer type */ #undef DEV_T_STRUCT /* Path to /dev/urandom */ #undef DEV_URANDOM_PATH /* Disable asserts */ #undef DISABLE_ASSERTS /* Dovecot ABI version */ #undef DOVECOT_ABI_VERSION /* Dovecot name */ #undef DOVECOT_NAME /* Dovecot string */ #undef DOVECOT_STRING /* Dovecot version */ #undef DOVECOT_VERSION /* Dovecot major version */ #undef DOVECOT_VERSION_MAJOR /* Dovecot minor version */ #undef DOVECOT_VERSION_MINOR /* How to define flexible array members in structs */ #undef FLEXIBLE_ARRAY_MEMBER /* Build with ASN1_STRING_get0_data() support */ #undef HAVE_ASN1_STRING_GET0_DATA /* Define to 1 if you have the `backtrace_symbols' function. */ #undef HAVE_BACKTRACE_SYMBOLS /* Define if you have bzlib library */ #undef HAVE_BZLIB /* Build with Cassandra support */ #undef HAVE_CASSANDRA /* Cassandra supports speculative execution policy */ #undef HAVE_CASSANDRA_SPECULATIVE_POLICY /* Define to 1 if you have the `clearenv' function. */ #undef HAVE_CLEARENV /* Define if you have the clock_gettime function */ #undef HAVE_CLOCK_GETTIME /* Define if you have struct dirent->d_type */ #undef HAVE_DIRENT_D_TYPE /* Define to 1 if you have the header file. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the `dirfd' function. */ #undef HAVE_DIRFD /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Build with EVP_MD_CTX_new() support */ #undef HAVE_EVP_MD_CTX_NEW /* Build with EVP_PKEY_get0_*() support */ #undef HAVE_EVP_PKEY_get0 /* Define to 1 if you have the header file. */ #undef HAVE_EXECINFO_H /* Define to 1 if you have the `fallocate' function. */ #undef HAVE_FALLOCATE /* Define to 1 if you have the `fcntl' function. */ #undef HAVE_FCNTL /* Define if you have fdatasync() */ #undef HAVE_FDATASYNC /* Define to 1 if you have the `flock' function. */ #undef HAVE_FLOCK /* Define if you have FreeBSD-compatible sendfile() */ #undef HAVE_FREEBSD_SENDFILE /* Define if you want exttextcat support for FTS */ #undef HAVE_FTS_EXTTEXTCAT /* Define if you want stemming support for FTS */ #undef HAVE_FTS_STEMMER /* Define if you want textcat support for FTS */ #undef HAVE_FTS_TEXTCAT /* Define to 1 if you have the header file. */ #undef HAVE_GC_GC_H /* Define to 1 if you have the header file. */ #undef HAVE_GC_H /* Define to 1 if you have the `getmntent' function. */ #undef HAVE_GETMNTENT /* Define to 1 if you have the `getmntinfo' function. */ #undef HAVE_GETMNTINFO /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the `getpeereid' function. */ #undef HAVE_GETPEEREID /* Define to 1 if you have the `getpeerucred' function. */ #undef HAVE_GETPEERUCRED /* Define to 1 if you have the `glob' function. */ #undef HAVE_GLOB /* Define to 1 if you have the header file. */ #undef HAVE_GLOB_H /* Build with GNUTLS support */ #undef HAVE_GNUTLS /* Build with GSSAPI support */ #undef HAVE_GSSAPI /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_EXT_H /* GSSAPI headers in gssapi/gssapi.h */ #undef HAVE_GSSAPI_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_KRB5_H /* GSSAPI headers in gssapi.h */ #undef HAVE_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_KRB5_H /* GSSAPI supports SPNEGO */ #undef HAVE_GSSAPI_SPNEGO /* Define to 1 if you have the `gsskrb5_register_acceptor_identity' function. */ #undef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY /* Build with HMAC_CTX_new() support */ #undef HAVE_HMAC_CTX_NEW /* Define if you have the iconv() function and it works. */ #undef HAVE_ICONV /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON /* Define to 1 if you have the `inotify_init' function. */ #undef HAVE_INOTIFY_INIT /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Build with IPv6 support */ #undef HAVE_IPV6 /* Define to 1 if you have the header file. */ #undef HAVE_JFS_QUOTA_H /* Define to 1 if you have the `kevent' function. */ #undef HAVE_KEVENT /* Define to 1 if you have the `kqueue' function. */ #undef HAVE_KQUEUE /* Define to 1 if you have the `krb5_gss_register_acceptor_identity' function. */ #undef HAVE_KRB5_GSS_REGISTER_ACCEPTOR_IDENTITY /* libcap is installed for cap_init() */ #undef HAVE_LIBCAP /* Define to 1 if you have the header file. */ #undef HAVE_LIBEXTTEXTCAT_TEXTCAT_H /* Define to 1 if you have the header file. */ #undef HAVE_LIBGEN_H /* Define if you want ICU normalization support for FTS */ #undef HAVE_LIBICU /* Define to 1 if you have the header file. */ #undef HAVE_LIBTEXTCAT_TEXTCAT_H /* Define if you have libwrap */ #undef HAVE_LIBWRAP /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_DQBLK_XFS_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_FALLOC_H /* Define if you have Linux-compatible mremap() */ #undef HAVE_LINUX_MREMAP /* Define if you have Linux-compatible sendfile() */ #undef HAVE_LINUX_SENDFILE /* Define to 1 if you have the `lockf' function. */ #undef HAVE_LOCKF /* Define if you have lz4 library */ #undef HAVE_LZ4 /* Define if you have LZ4_compress_default */ #undef HAVE_LZ4_COMPRESS_DEFAULT /* Define if you have lzma library */ #undef HAVE_LZMA /* Define to 1 if you have the `madvise' function. */ #undef HAVE_MADVISE /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_NP_H /* Define to 1 if you have the `malloc_usable_size' function. */ #undef HAVE_MALLOC_USABLE_SIZE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file. */ #undef HAVE_MNTENT_H /* Define if you have dynamic module support */ #undef HAVE_MODULES /* Build with MySQL support */ #undef HAVE_MYSQL /* Define if your MySQL library has SSL functions */ #undef HAVE_MYSQL_SSL /* Define if your MySQL library supports setting cipher */ #undef HAVE_MYSQL_SSL_CIPHER /* Define if your MySQL library supports verifying the name in the SSL certificate */ #undef HAVE_MYSQL_SSL_VERIFY_SERVER_CERT /* Build with OBJ_length() support */ #undef HAVE_OBJ_LENGTH /* Define if you don't have C99 compatible vsnprintf() call */ #undef HAVE_OLD_VSNPRINTF /* Build with OpenSSL support */ #undef HAVE_OPENSSL /* Define if OpenSSL performs thread cleanup automatically */ #undef HAVE_OPENSSL_AUTO_THREAD_DEINIT /* OpenSSL supports OPENSSL_cleanup() */ #undef HAVE_OPENSSL_CLEANUP /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_ERR_H /* Define if you have ERR_remove_thread_state */ #undef HAVE_OPENSSL_ERR_REMOVE_THREAD_STATE /* Define if you have openssl/rand.h */ #undef HAVE_OPENSSL_RAND_H /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_SSL_H /* Define if you have pam/pam_appl.h */ #undef HAVE_PAM_PAM_APPL_H /* Define if you have pam_setcred() */ #undef HAVE_PAM_SETCRED /* Build with PostgreSQL support */ #undef HAVE_PGSQL /* Define to 1 if you have the `posix_fadvise' function. */ #undef HAVE_POSIX_FADVISE /* Define if you have a working posix_fallocate() */ #undef HAVE_POSIX_FALLOCATE /* Define if libpq has PQescapeStringConn function */ #undef HAVE_PQESCAPE_STRING_CONN /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD /* Define if you have prctl(PR_SET_DUMPABLE) */ #undef HAVE_PR_SET_DUMPABLE /* Define to 1 if you have the `quotactl' function. */ #undef HAVE_QUOTACTL /* Define to 1 if you have the header file. */ #undef HAVE_QUOTA_H /* Define if you have quota_open() */ #undef HAVE_QUOTA_OPEN /* Define if Q_QUOTACTL exists */ #undef HAVE_Q_QUOTACTL /* Define if you have RLIMIT_AS for setrlimit() */ #undef HAVE_RLIMIT_AS /* Define if you have RLIMIT_CORE for getrlimit() */ #undef HAVE_RLIMIT_CORE /* Define if you have RLIMIT_NPROC for setrlimit() */ #undef HAVE_RLIMIT_NPROC /* Define if you wish to retrieve quota of NFS mounted mailboxes */ #undef HAVE_RQUOTA /* Build with RSA_generate_key_ex() support */ #undef HAVE_RSA_GENERATE_KEY_EX /* Define to 1 if you have the header file. */ #undef HAVE_SASL_H /* Define to 1 if you have the header file. */ #undef HAVE_SASL_SASL_H /* Define if you have security/pam_appl.h */ #undef HAVE_SECURITY_PAM_APPL_H /* Define to 1 if you have the `setegid' function. */ #undef HAVE_SETEGID /* Define to 1 if you have the `seteuid' function. */ #undef HAVE_SETEUID /* Define to 1 if you have the `setpriority' function. */ #undef HAVE_SETPRIORITY /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE /* Define to 1 if you have the `setresgid' function. */ #undef HAVE_SETRESGID /* Define to 1 if you have the `setreuid' function. */ #undef HAVE_SETREUID /* Define to 1 if you have the `setrlimit' function. */ #undef HAVE_SETRLIMIT /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 'int' if you don't have socklen_t */ #undef HAVE_SOCKLEN_T /* Define if you have Solaris-compatible sendfile() */ #undef HAVE_SOLARIS_SENDFILE /* Build with SQLite3 support */ #undef HAVE_SQLITE /* Build with SSL/TLS support */ #undef HAVE_SSL /* Define if you have SSL_clear_options */ #undef HAVE_SSL_CLEAR_OPTIONS /* Build with OpenSSL compression */ #undef HAVE_SSL_COMPRESSION /* Build with SSL_COMP_free_compression_methods() support */ #undef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS /* Build with TLS hostname support */ #undef HAVE_SSL_GET_SERVERNAME /* Define if CRYPTO_set_mem_functions has new style parameters */ #undef HAVE_SSL_NEW_MEM_FUNCS /* Define if you have statfs.f_mntfromname */ #undef HAVE_STATFS_MNTFROMNAME /* Define if you have statvfs.f_mntfromname */ #undef HAVE_STATVFS_MNTFROMNAME /* Define if you have st_?tim timespec fields in struct stat */ #undef HAVE_STAT_XTIM /* Define if you have st_?timespec fields in struct stat */ #undef HAVE_STAT_XTIMESPEC /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the `stricmp' function. */ #undef HAVE_STRICMP /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define if struct sqblk.dqb_curblocks exists */ #undef HAVE_STRUCT_DQBLK_CURBLOCKS /* Define if struct sqblk.dqb_curspace exists */ #undef HAVE_STRUCT_DQBLK_CURSPACE /* Define if you have struct iovec */ #undef HAVE_STRUCT_IOVEC /* Define to 1 if the system has the type `struct sockpeercred'. */ #undef HAVE_STRUCT_SOCKPEERCRED /* Define if you want to use systemd socket activation */ #undef HAVE_SYSTEMD /* Define to 1 if you have the header file. */ #undef HAVE_SYS_EVENT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FS_QUOTA_COMMON_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FS_UFS_QUOTA_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MKDEV_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MNTTAB_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_QUOTA_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_RESOURCE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSMACROS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UTSNAME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_VMOUNT_H /* Define to 1 if you have the `timegm' function. */ #undef HAVE_TIMEGM /* Define if you have struct tm->tm_gmtoff */ #undef HAVE_TM_GMTOFF /* Define if you have typeof() */ #undef HAVE_TYPEOF /* Define to 1 if you have the header file. */ #undef HAVE_UCONTEXT_H /* Define to 1 if you have the header file. */ #undef HAVE_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_UFS_UFS_QUOTA_H /* Define if you have uintmax_t (C99 type) */ #undef HAVE_UINTMAX_T /* Define if you have uint_fast32_t (C99 type) */ #undef HAVE_UINT_FAST32_T /* Define to 1 if you have the `uname' function. */ #undef HAVE_UNAME /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV /* Define if you have a native uoff_t type */ #undef HAVE_UOFF_T /* Define to 1 if you have the `vsyslog' function. */ #undef HAVE_VSYSLOG /* Define to 1 if you have the `walkcontext' function. */ #undef HAVE_WALKCONTEXT /* Define to 1 if you have the `writev' function. */ #undef HAVE_WRITEV /* Define to 1 if you have the header file. */ #undef HAVE_XFS_XQM_H /* Define if you have zlib library */ #undef HAVE_ZLIB /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL /* Define if you have __gss_userok() */ #undef HAVE___GSS_USEROK /* Define as const if the declaration of iconv() needs const. */ #undef ICONV_CONST /* Implement I/O loop with Linux 2.6 epoll() */ #undef IOLOOP_EPOLL /* Implement I/O loop with BSD kqueue() */ #undef IOLOOP_KQUEUE /* Use Linux inotify */ #undef IOLOOP_NOTIFY_INOTIFY /* Use BSD kqueue directory changes notificaton */ #undef IOLOOP_NOTIFY_KQUEUE /* No special notify support */ #undef IOLOOP_NOTIFY_NONE /* Implement I/O loop with poll() */ #undef IOLOOP_POLL /* Implement I/O loop with select() */ #undef IOLOOP_SELECT /* Define if you have ldap_initialize */ #undef LDAP_HAVE_INITIALIZE /* Define if you have ldap_start_tls_s */ #undef LDAP_HAVE_START_TLS_S /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* List of compiled in mail storages */ #undef MAIL_STORAGES /* Required memory alignment */ #undef MEM_ALIGN_SIZE /* Define if shared mmaps don't get updated by write()s */ #undef MMAP_CONFLICTS_WRITE /* Dynamic module suffix */ #undef MODULE_SUFFIX /* Maximum value of off_t */ #undef OFF_T_MAX /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Support URL */ #undef PACKAGE_WEBPAGE /* Build with BSD authentication support */ #undef PASSDB_BSDAUTH /* Build with checkpassword passdb support */ #undef PASSDB_CHECKPASSWORD /* Build with LDAP support */ #undef PASSDB_LDAP /* Build with PAM support */ #undef PASSDB_PAM /* Build with passwd support */ #undef PASSDB_PASSWD /* Build with passwd-file support */ #undef PASSDB_PASSWD_FILE /* Build with shadow support */ #undef PASSDB_SHADOW /* Build with Tru64 SIA support */ #undef PASSDB_SIA /* Build with SQL support */ #undef PASSDB_SQL /* Build with vpopmail support */ #undef PASSDB_VPOPMAIL /* Define if pread/pwrite implementation is broken */ #undef PREAD_BROKEN /* Define if pread/pwrite needs _XOPEN_SOURCE 500 */ #undef PREAD_WRAPPERS /* printf() format for size_t */ #undef PRIuSIZE_T /* printf() format for uoff_t */ #undef PRIuUOFF_T /* Define if process title can be changed by modifying argv */ #undef PROCTITLE_HACK /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long', as computed by sizeof. */ #undef SIZEOF_LONG /* The size of `long long', as computed by sizeof. */ #undef SIZEOF_LONG_LONG /* The size of `void *', as computed by sizeof. */ #undef SIZEOF_VOID_P /* Build SQL drivers as plugins */ #undef SQL_DRIVER_PLUGINS /* Maximum value of ssize_t */ #undef SSIZE_T_MAX /* C99 static array */ #undef STATIC_ARRAY /* reasonable mntctl buffer size */ #undef STATIC_MTAB_SIZE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Points to textcat pkgdatadir containing the language files */ #undef TEXTCAT_DATADIR /* max. time_t bits gmtime() can handle */ #undef TIME_T_MAX_BITS /* Define if your time_t is signed */ #undef TIME_T_SIGNED /* Define if unsetenv() returns int */ #undef UNSETENV_RET_INT /* Define if off_t is int */ #undef UOFF_T_INT /* Define if off_t is long */ #undef UOFF_T_LONG /* Define if off_t is long long */ #undef UOFF_T_LONG_LONG /* Build with checkpassword userdb support */ #undef USERDB_CHECKPASSWORD /* Build with LDAP support */ #undef USERDB_LDAP /* Build with NSS module support */ #undef USERDB_NSS /* Build with passwd support */ #undef USERDB_PASSWD /* Build with passwd-file support */ #undef USERDB_PASSWD_FILE /* Build with prefetch userdb support */ #undef USERDB_PREFETCH /* Build with SQL support */ #undef USERDB_SQL /* Build with vpopmail support */ #undef USERDB_VPOPMAIL /* Define if you want to use Boehm GC */ #undef USE_GC /* A 'va_copy' style function */ #undef VA_COPY /* 'va_lists' cannot be copied as values */ #undef VA_COPY_AS_ARRAY /* Version number of package */ #undef VERSION /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to 'unsigned int' if you don't have it */ #undef size_t /* Define to 'int' if you don't have it */ #undef ssize_t dovecot-2.2.33.2/doc/0002755000175000017500000000000013172375614011165 500000000000000dovecot-2.2.33.2/doc/thread-refs.txt0000644000175000017500000002114213123174404014036 00000000000000Optimistic incremental THREAD=REFERENCES Step (1) is the slowest stage for building a THREAD=REFERENCES tree. If its result tree is permanently saved, the following thread builds can be based on it by updating the tree incrementally. Adding new messages to the tree is simple: simply follow the normal rules as when building a new tree from scratch. Expunging messages gets more problematic though. Each node in the tree keeps a "link reference count" which specifies how many messages contain a "this message" -> "parent message" reference (number of links to parent node). The first reference is usually added by the message's own References: or In-Reply-To: header and the latter references are added by References: headers. This link refcount must be updated when adding and expunging messages. When the link refcount drops to zero, the message becomes a root. The link refcount doesn't tell much about the number of children the node has, because References: headers may reference any number of its ancestors. The optimistic approach assumes that usually there are no problematic links. In such case expunging a message simply updates the link refcounts and marks the message's node as expunged. Expunged messages may still act as dummy nodes. The node may be removed only after there are no nodes which point to it. Problematic links are handled by marking nodes affected by them. If such a node is expunged or its link is unreferenced, the thread tree must be rebuilt. This is assumed to be a rarely occurring event. The problematic cases are: 1) Duplicate Message-ID: headers. If the first message having it is expunged, the thread tree must be rebuilt. 2) Message-ID loops. If a message referencing the looping path gets expunged, the loop may break and the thread tree must be rebuilt. Because it can't be determined easily which loops get broken by which expunges, this case can be handled in a bit easier way: When detecting a loop between parent and child, rebuild the tree if any link between the parent and child gets unreferenced. 3) A message changes its parent because an earlier message's References: header contained a different link. If the message gets expunged, the thread tree must be rebuilt to get the original parent back. 4) A link in a message's References: header is ignored, because the referenced child already specified a different parent to itself. If the message gets expunged, the thread tree must be rebuilt to determine its new parent. 5) A link in a message's References: header is ignored, because an earlier message's References: header already specified a different link. If the earlier message gets expunged, the parent may change. The earlier message could be found out quickly by keeping some extra state (or with a slow scan), but since this is assumed to be a rare problem, there's an easier (but less specific) way to handle this: Rebuild the tree if a link to the child node is unreferenced (or alternatively if a link to the original parent node is unreferenced, but it probably happens more often). Pseudocode: node { parent: Pointer to parent node. Children pointers aren't required. uid: Message's UID (0 = expunged or never even existed) parent_link_refcount: Number of messages containing "this message" -> "parent message" link, i.e. "number of links to parent node". However since parents can change, not all of these references might be from our current child nodes. When this refcount reaches 0, it means we must detach from our parent. expunge_rebuilds: If this message gets expunged, rebuild the thread tree. child_unref_rebuilds: If a link between this node and its child gets unreferenced, rebuild the thread tree. } link_reference(parent_node, child_node) child_node.parent_link_refcount++ if is_ancestor(child_node, parent_node) // child_node is an ancestor of parent_node. Adding child_node -> // parent_node would introduce a loop. If any messages referencing the // path between parent_node's parent and child_node get expunged, we // have to rebuild the tree because the loop might break. For example: // #1: a -> b (a.ref=1, b.ref=1) // #2: b -> a (a.ref=2, b.ref=2) // #3: c -> a -> b (a.ref=3, b.ref=3, c.ref=1) // Expunging #3 wouldn't break the loop, but expunging #1 would. for node in nodes[parent_node.parent .. child_node] node.child_unref_rebuilds = true else if child_node.parent == parent_node // The same link already exists else // Set parent_node as child_node's parent if child_node.parent == NIL child_node.parent = parent_node else // Conflicting parent already exists, keep the original. // We get here only when handling References: header. if child_node.uid != 0 // We've already seen this message. It specifies its own parent and // it doesn't matter what any other reference says. The only way its // parent can change is if the message itself gets expunged. child_node.expunge_rebuilds = true else // Message doesn't exist, so it was one of the node's children // that created the original reference. If that reference gets // dropped, the parent is changed. We could catch this in one of // several ways: // a) Link to original parent node gets unreferenced // b) Link to this node gets unreferenced // c) Any of the child nodes gets expunged // b) is probably the least likely to happen, so use it child_node.child_unref_rebuilds = true thread_add_msg(uid) // get the Message-IDs as specified by the thread spec (msgid, parent_msgid, references) = message_get_thread_headers(uid) if msgid != NIL if nodes[msgid].uid == 0 nodes[msgid].uid = uid else // duplicate Message-ID. if the original ever gets expunged, // rebuild the thread tree nodes[msgid].expunge_rebuilds = true msgid = NIL if msgid == NIL msgid = get_unique_msg_id() node = nodes[msgid] if node.parent != NIL and (parent_msgid == NIL or node.parent.msgid != parent_msgid) // Conflicting parent, remove it. If this message gets expunged, we have // to revert back to the original parent. node.parent = NIL node.expunge_rebuilds = true if parent_msgid != NIL link_reference(nodes[parent_msgid], node) // go through References (skipping the last one) for (ref_parent, ref_child) in references link_reference(nodes[ref_parent], nodes[ref_child]) unref_link(parent, child) if parent.child_unref_rebuilds return false child.parent_link_refcount-- if child.parent_link_refcount == 0 child.parent = NIL return true // returns false if thread tree needs to be rebuilt thread_expunge_msg(uid) // get the Message-IDs as specified by the thread spec (msgid, in_reply_to_msgid, references) = message_get_thread_headers(uid) node = nodes[msgid] if node.uid != uid // Removing a duplicate Message-ID return false if node.expunge_rebuilds return false if parent_msgid != NIL and not unref_link(nodes[parent_msgid], nodes[child_msgid]) return false if references != NIL // go through References for (parent_msgid, child_msgid) in references if not unref_link(nodes[parent_msgid], nodes[child_msgid]) return false last_parent_msgid = references.last else if in_reply_to_msgid != NIL last_parent_msgid = in_reply_to_msgid if last_parent_msgid != NIL and not unref_link(nodes[last_parent_msgid], node) return false // mark this node as expunged node.uid = 0 return true thread_iterate() root_nodes = [] referenced = [] children = [][] // Steps (2) and (3) for node in nodes if node.parent != NIL root_nodes[] = node else referenced[node.parent] = true if node.uid != 0 // Find the node's first non-dummy parent and add the node as its child. // If there are no non-dummy parents, add it as the highest dummy's // child. nondummy_parent = node.parent while nondummy_parent.uid == 0 and nondummy_parent.parent != NIL nondummy_parent = nondummy_parent.parent children[nondummy_parent][] = node for node in root_nodes if node.uid == 0 if children[node] == NIL // remove dummy roots that have no children. delete(node) else if count(children[node]) == 1 // dummy root has a single child, replace the root with its child node = children[node] for node in nodes if node.uid == 0 and !referenced[node] free(node) // root_nodes and children now contain a tree equivalent to a tree built by // THREAD=REFERENCES specification steps (1)-(3). The rest of the steps // can be performed using them. Note that node.parent should not (and need // not) be used because it points its parent before steps (2) and (3). dovecot-2.2.33.2/doc/Makefile.in0000644000175000017500000005524513172375572013166 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = doc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(docdir)" DATA = $(doc_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = man wiki example-config am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @BUILD_DOCS_TRUE@DOCDIRS = wiki example-config SUBDIRS = man $(DOCDIRS) docfiles = \ documentation.txt \ securecoding.txt \ thread-refs.txt \ mkcert.sh \ dovecot-openssl.cnf \ solr-schema.xml @BUILD_DOCS_TRUE@doc_DATA = $(docfiles) EXTRA_DIST = \ dovecot-initd.sh \ $(docfiles) all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-docDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-docDATA .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-docDATA install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-docDATA .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/doc/example-config/0002755000175000017500000000000013172375614014063 500000000000000dovecot-2.2.33.2/doc/example-config/Makefile.in0000644000175000017500000006047713172375572016067 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = doc/example-config ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(exampledir)" \ "$(DESTDIR)$(pkgsysconfdir)" DATA = $(example_DATA) $(nodist_pkgsysconf_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = conf.d pkgsysconfdir = $(sysconfdir)/dovecot nodist_pkgsysconf_DATA = README exampledir = $(docdir)/example-config example_DATA = \ dovecot.conf \ dovecot-dict-auth.conf.ext \ dovecot-dict-sql.conf.ext \ dovecot-ldap.conf.ext \ dovecot-sql.conf.ext EXTRA_DIST = \ $(example_DATA) \ README.in CLEANFILES = README all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/example-config/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/example-config/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-exampleDATA: $(example_DATA) @$(NORMAL_INSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(exampledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(exampledir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(exampledir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(exampledir)" || exit $$?; \ done uninstall-exampleDATA: @$(NORMAL_UNINSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(exampledir)'; $(am__uninstall_files_from_dir) install-nodist_pkgsysconfDATA: $(nodist_pkgsysconf_DATA) @$(NORMAL_INSTALL) @list='$(nodist_pkgsysconf_DATA)'; test -n "$(pkgsysconfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgsysconfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgsysconfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgsysconfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgsysconfdir)" || exit $$?; \ done uninstall-nodist_pkgsysconfDATA: @$(NORMAL_UNINSTALL) @list='$(nodist_pkgsysconf_DATA)'; test -n "$(pkgsysconfdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgsysconfdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(exampledir)" "$(DESTDIR)$(pkgsysconfdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-data-local install-exampleDATA \ install-nodist_pkgsysconfDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-exampleDATA uninstall-nodist_pkgsysconfDATA .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am dist-hook distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-data-local install-dvi install-dvi-am \ install-exampleDATA install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-nodist_pkgsysconfDATA install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-exampleDATA \ uninstall-nodist_pkgsysconfDATA .PRECIOUS: Makefile # nodist_ prefix just doesn't seem to work, so rm it directly: dist-hook: rm -f $(distdir)/README README: README.in Makefile cat $(srcdir)/README.in | sed "s|@exampledir@|$(exampledir)|" > README install-data-local: $(mkdir_p) $(DESTDIR)/$(pkgsysconfdir) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/doc/example-config/dovecot-ldap.conf.ext0000644000175000017500000001314513123174404020022 00000000000000# This file is commonly accessed via passdb {} or userdb {} section in # conf.d/auth-ldap.conf.ext # This file is opened as root, so it should be owned by root and mode 0600. # # http://wiki2.dovecot.org/AuthDatabase/LDAP # # NOTE: If you're not using authentication binds, you'll need to give # dovecot-auth read access to userPassword field in the LDAP server. # With OpenLDAP this is done by modifying /etc/ldap/slapd.conf. There should # already be something like this: # access to attribute=userPassword # by dn="" read # add this # by anonymous auth # by self write # by * none # Space separated list of LDAP hosts to use. host:port is allowed too. #hosts = # LDAP URIs to use. You can use this instead of hosts list. Note that this # setting isn't supported by all LDAP libraries. #uris = # Distinguished Name - the username used to login to the LDAP server. # Leave it commented out to bind anonymously (useful with auth_bind=yes). #dn = # Password for LDAP server, if dn is specified. #dnpass = # Use SASL binding instead of the simple binding. Note that this changes # ldap_version automatically to be 3 if it's lower. #sasl_bind = no # SASL mechanism name to use. #sasl_mech = # SASL realm to use. #sasl_realm = # SASL authorization ID, ie. the dnpass is for this "master user", but the # dn is still the logged in user. Normally you want to keep this empty. #sasl_authz_id = # Use TLS to connect to the LDAP server. #tls = no # TLS options, currently supported only with OpenLDAP: #tls_ca_cert_file = #tls_ca_cert_dir = #tls_cipher_suite = # TLS cert/key is used only if LDAP server requires a client certificate. #tls_cert_file = #tls_key_file = # Valid values: never, hard, demand, allow, try #tls_require_cert = # Use the given ldaprc path. #ldaprc_path = # LDAP library debug level as specified by LDAP_DEBUG_* in ldap_log.h. # -1 = everything. You may need to recompile OpenLDAP with debugging enabled # to get enough output. #debug_level = 0 # Use authentication binding for verifying password's validity. This works by # logging into LDAP server using the username and password given by client. # The pass_filter is used to find the DN for the user. Note that the pass_attrs # is still used, only the password field is ignored in it. Before doing any # search, the binding is switched back to the default DN. #auth_bind = no # If authentication binding is used, you can save one LDAP request per login # if users' DN can be specified with a common template. The template can use # the standard %variables (see user_filter). Note that you can't # use any pass_attrs if you use this setting. # # If you use this setting, it's a good idea to use a different # dovecot-ldap.conf.ext for userdb (it can even be a symlink, just as long as # the filename is different in userdb's args). That way one connection is used # only for LDAP binds and another connection is used for user lookups. # Otherwise the binding is changed to the default DN before each user lookup. # # For example: # auth_bind_userdn = cn=%u,ou=people,o=org # #auth_bind_userdn = # LDAP protocol version to use. Likely 2 or 3. #ldap_version = 3 # LDAP base. %variables can be used here. # For example: dc=mail, dc=example, dc=org base = # Dereference: never, searching, finding, always #deref = never # Search scope: base, onelevel, subtree #scope = subtree # User attributes are given in LDAP-name=dovecot-internal-name list. The # internal names are: # uid - System UID # gid - System GID # home - Home directory # mail - Mail location # # There are also other special fields which can be returned, see # http://wiki2.dovecot.org/UserDatabase/ExtraFields #user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid # Filter for user lookup. Some variables can be used (see # http://wiki2.dovecot.org/Variables for full list): # %u - username # %n - user part in user@domain, same as %u if there's no domain # %d - domain part in user@domain, empty if user there's no domain #user_filter = (&(objectClass=posixAccount)(uid=%u)) # Password checking attributes: # user: Virtual user name (user@domain), if you wish to change the # user-given username to something else # password: Password, may optionally start with {type}, eg. {crypt} # There are also other special fields which can be returned, see # http://wiki2.dovecot.org/PasswordDatabase/ExtraFields #pass_attrs = uid=user,userPassword=password # If you wish to avoid two LDAP lookups (passdb + userdb), you can use # userdb prefetch instead of userdb ldap in dovecot.conf. In that case you'll # also have to include user_attrs in pass_attrs field prefixed with "userdb_" # string. For example: #pass_attrs = uid=user,userPassword=password,\ # homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid # Filter for password lookups #pass_filter = (&(objectClass=posixAccount)(uid=%u)) # Attributes and filter to get a list of all users #iterate_attrs = uid=user #iterate_filter = (objectClass=posixAccount) # Default password scheme. "{scheme}" before password overrides this. # List of supported schemes is in: http://wiki2.dovecot.org/Authentication #default_pass_scheme = CRYPT # By default all LDAP lookups are performed by the auth master process. # If blocking=yes, auth worker processes are used to perform the lookups. # Each auth worker process creates its own LDAP connection so this can # increase parallelism. With blocking=no the auth master process can # keep 8 requests pipelined for the LDAP connection, while with blocking=yes # each connection has a maximum of 1 request running. For small systems the # blocking=no is sufficient and uses less resources. #blocking = no dovecot-2.2.33.2/doc/example-config/dovecot-sql.conf.ext0000644000175000017500000001327613165463624017721 00000000000000# This file is commonly accessed via passdb {} or userdb {} section in # conf.d/auth-sql.conf.ext # This file is opened as root, so it should be owned by root and mode 0600. # # http://wiki2.dovecot.org/AuthDatabase/SQL # # For the sql passdb module, you'll need a database with a table that # contains fields for at least the username and password. If you want to # use the user@domain syntax, you might want to have a separate domain # field as well. # # If your users all have the same uig/gid, and have predictable home # directories, you can use the static userdb module to generate the home # dir based on the username and domain. In this case, you won't need fields # for home, uid, or gid in the database. # # If you prefer to use the sql userdb module, you'll want to add fields # for home, uid, and gid. Here is an example table: # # CREATE TABLE users ( # username VARCHAR(128) NOT NULL, # domain VARCHAR(128) NOT NULL, # password VARCHAR(64) NOT NULL, # home VARCHAR(255) NOT NULL, # uid INTEGER NOT NULL, # gid INTEGER NOT NULL, # active CHAR(1) DEFAULT 'Y' NOT NULL # ); # Database driver: mysql, pgsql, sqlite #driver = # Database connection string. This is driver-specific setting. # # HA / round-robin load-balancing is supported by giving multiple host # settings, like: host=sql1.host.org host=sql2.host.org # # pgsql: # For available options, see the PostgreSQL documention for the # PQconnectdb function of libpq. # Use maxconns=n (default 5) to change how many connections Dovecot can # create to pgsql. # # mysql: # Basic options emulate PostgreSQL option names: # host, port, user, password, dbname # # But also adds some new settings: # client_flags - See MySQL manual # connect_timeout - Connect timeout in seconds (default: 5) # read_timeout - Read timeout in seconds (default: 30) # write_timeout - Write timeout in seconds (default: 30) # ssl_ca, ssl_ca_path - Set either one or both to enable SSL # ssl_cert, ssl_key - For sending client-side certificates to server # ssl_cipher - Set minimum allowed cipher security (default: HIGH) # ssl_verify_server_cert - Verify that the name in the server SSL certificate # matches the host (default: no) # option_file - Read options from the given file instead of # the default my.cnf location # option_group - Read options from the given group (default: client) # # You can connect to UNIX sockets by using host: host=/var/run/mysql.sock # Note that currently you can't use spaces in parameters. # # sqlite: # The path to the database file. # # Examples: # connect = host=192.168.1.1 dbname=users # connect = host=sql.example.com dbname=virtual user=virtual password=blarg # connect = /etc/dovecot/authdb.sqlite # #connect = # Default password scheme. # # List of supported schemes is in # http://wiki2.dovecot.org/Authentication/PasswordSchemes # #default_pass_scheme = MD5 # passdb query to retrieve the password. It can return fields: # password - The user's password. This field must be returned. # user - user@domain from the database. Needed with case-insensitive lookups. # username and domain - An alternative way to represent the "user" field. # # The "user" field is often necessary with case-insensitive lookups to avoid # e.g. "name" and "nAme" logins creating two different mail directories. If # your user and domain names are in separate fields, you can return "username" # and "domain" fields instead of "user". # # The query can also return other fields which have a special meaning, see # http://wiki2.dovecot.org/PasswordDatabase/ExtraFields # # Commonly used available substitutions (see http://wiki2.dovecot.org/Variables # for full list): # %u = entire user@domain # %n = user part of user@domain # %d = domain part of user@domain # # Note that these can be used only as input to SQL query. If the query outputs # any of these substitutions, they're not touched. Otherwise it would be # difficult to have eg. usernames containing '%' characters. # # Example: # password_query = SELECT userid AS user, pw AS password \ # FROM users WHERE userid = '%u' AND active = 'Y' # #password_query = \ # SELECT username, domain, password \ # FROM users WHERE username = '%n' AND domain = '%d' # userdb query to retrieve the user information. It can return fields: # uid - System UID (overrides mail_uid setting) # gid - System GID (overrides mail_gid setting) # home - Home directory # mail - Mail location (overrides mail_location setting) # # None of these are strictly required. If you use a single UID and GID, and # home or mail directory fits to a template string, you could use userdb static # instead. For a list of all fields that can be returned, see # http://wiki2.dovecot.org/UserDatabase/ExtraFields # # Examples: # user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' # user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' # user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' # #user_query = \ # SELECT home, uid, gid \ # FROM users WHERE username = '%n' AND domain = '%d' # If you wish to avoid two SQL lookups (passdb + userdb), you can use # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll # also have to return userdb fields in password_query prefixed with "userdb_" # string. For example: #password_query = \ # SELECT userid AS user, password, \ # home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ # FROM users WHERE userid = '%u' # Query to get a list of all usernames. #iterate_query = SELECT username AS user FROM users dovecot-2.2.33.2/doc/example-config/conf.d/0002755000175000017500000000000013172375614015232 500000000000000dovecot-2.2.33.2/doc/example-config/conf.d/15-lda.conf0000644000175000017500000000320413123174404016767 00000000000000## ## LDA specific settings (also used by LMTP) ## # Address to use when sending rejection mails. # Default is postmaster@. %d expands to recipient domain. #postmaster_address = # Hostname to use in various parts of sent mails (e.g. in Message-Id) and # in LMTP replies. Default is the system's real hostname@domain. #hostname = # If user is over quota, return with temporary failure instead of # bouncing the mail. #quota_full_tempfail = no # Binary to use for sending mails. #sendmail_path = /usr/sbin/sendmail # If non-empty, send mails via this SMTP host[:port] instead of sendmail. #submission_host = # Subject: header to use for rejection mails. You can use the same variables # as for rejection_reason below. #rejection_subject = Rejected: %s # Human readable error message for rejection mails. You can use variables: # %n = CRLF, %r = reason, %s = original subject, %t = recipient #rejection_reason = Your message to <%t> was automatically rejected:%n%r # Delimiter character between local-part and detail in email address. #recipient_delimiter = + # Header where the original recipient address (SMTP's RCPT TO: address) is taken # from if not available elsewhere. With dovecot-lda -a parameter overrides this. # A commonly used header for this is X-Original-To. #lda_original_recipient_header = # Should saving a mail to a nonexistent mailbox automatically create it? #lda_mailbox_autocreate = no # Should automatically created mailboxes be also automatically subscribed? #lda_mailbox_autosubscribe = no protocol lda { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins } dovecot-2.2.33.2/doc/example-config/conf.d/10-logging.conf0000644000175000017500000000576613165463624017702 00000000000000## ## Log destination. ## # Log file to use for error messages. "syslog" logs to syslog, # /dev/stderr logs to stderr. #log_path = syslog # Log file to use for informational messages. Defaults to log_path. #info_log_path = # Log file to use for debug messages. Defaults to info_log_path. #debug_log_path = # Syslog facility to use if you're logging to syslog. Usually if you don't # want to use "mail", you'll use local0..local7. Also other standard # facilities are supported. #syslog_facility = mail ## ## Logging verbosity and debugging. ## # Log unsuccessful authentication attempts and the reasons why they failed. #auth_verbose = no # In case of password mismatches, log the attempted password. Valid values are # no, plain and sha1. sha1 can be useful for detecting brute force password # attempts vs. user simply trying the same password over and over again. # You can also truncate the value to n chars by appending ":n" (e.g. sha1:6). #auth_verbose_passwords = no # Even more verbose logging for debugging purposes. Shows for example SQL # queries. #auth_debug = no # In case of password mismatches, log the passwords and used scheme so the # problem can be debugged. Enabling this also enables auth_debug. #auth_debug_passwords = no # Enable mail process debugging. This can help you figure out why Dovecot # isn't finding your mails. #mail_debug = no # Show protocol level SSL errors. #verbose_ssl = no # mail_log plugin provides more event logging for mail processes. plugin { # Events to log. Also available: flag_change append #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename # Available fields: uid, box, msgid, from, subject, size, vsize, flags # size and vsize are available only for expunge and copy events. #mail_log_fields = uid box msgid size } ## ## Log formatting. ## # Prefix for each line written to log file. % codes are in strftime(3) # format. #log_timestamp = "%b %d %H:%M:%S " # Space-separated list of elements we want to log. The elements which have # a non-empty variable value are joined together to form a comma-separated # string. #login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c # Login log format. %s contains login_log_format_elements string, %$ contains # the data we want to log. #login_log_format = %$: %s # Log prefix for mail processes. See doc/wiki/Variables.txt for list of # possible variables you can use. #mail_log_prefix = "%s(%u): " # Format to use for logging mail deliveries: # %$ - Delivery status message (e.g. "saved to INBOX") # %m / %{msgid} - Message-ID # %s / %{subject} - Subject # %f / %{from} - From address # %p / %{size} - Physical size # %w / %{vsize} - Virtual size # %e / %{from_envelope} - MAIL FROM envelope # %{to_envelope} - RCPT TO envelope # %{delivery_time} - How many milliseconds it took to deliver the mail # %{session_time} - How long LMTP session took, not including delivery_time # %{storage_id} - Backend-specific ID for mail, e.g. Maildir filename #deliver_log_format = msgid=%m: %$ dovecot-2.2.33.2/doc/example-config/conf.d/10-master.conf0000644000175000017500000000646713123174404017533 00000000000000#default_process_limit = 100 #default_client_limit = 1000 # Default VSZ (virtual memory size) limit for service processes. This is mainly # intended to catch and kill processes that leak memory before they eat up # everything. #default_vsz_limit = 256M # Login user is internally used by login processes. This is the most untrusted # user in Dovecot system. It shouldn't have access to anything at all. #default_login_user = dovenull # Internal user is used by unprivileged processes. It should be separate from # login user, so that login processes can't disturb other processes. #default_internal_user = dovecot service imap-login { inet_listener imap { #port = 143 } inet_listener imaps { #port = 993 #ssl = yes } # Number of connections to handle before starting a new process. Typically # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0 # is faster. #service_count = 1 # Number of processes to always keep waiting for more connections. #process_min_avail = 0 # If you set service_count=0, you probably need to grow this. #vsz_limit = $default_vsz_limit } service pop3-login { inet_listener pop3 { #port = 110 } inet_listener pop3s { #port = 995 #ssl = yes } } service lmtp { unix_listener lmtp { #mode = 0666 } # Create inet listener only if you can't use the above UNIX socket #inet_listener lmtp { # Avoid making LMTP visible for the entire internet #address = #port = #} } service imap { # Most of the memory goes to mmap()ing files. You may need to increase this # limit if you have huge mailboxes. #vsz_limit = $default_vsz_limit # Max. number of IMAP processes (connections) #process_limit = 1024 } service pop3 { # Max. number of POP3 processes (connections) #process_limit = 1024 } service auth { # auth_socket_path points to this userdb socket by default. It's typically # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have # full permissions to this socket are able to get a list of all usernames and # get the results of everyone's userdb lookups. # # The default 0666 mode allows anyone to connect to the socket, but the # userdb lookups will succeed only if the userdb returns an "uid" field that # matches the caller process's UID. Also if caller's uid or gid matches the # socket's uid or gid the lookup succeeds. Anything else causes a failure. # # To give the caller full permissions to lookup all users, set the mode to # something else than 0666 and Dovecot lets the kernel enforce the # permissions (e.g. 0777 allows everyone full permissions). unix_listener auth-userdb { #mode = 0666 #user = #group = } # Postfix smtp-auth #unix_listener /var/spool/postfix/private/auth { # mode = 0666 #} # Auth process is run as this user. #user = $default_internal_user } service auth-worker { # Auth worker process is run as root by default, so that it can access # /etc/shadow. If this isn't necessary, the user should be changed to # $default_internal_user. #user = root } service dict { # If dict proxy is used, mail processes should have access to its socket. # For example: mode=0660, group=vmail and global mail_access_groups=vmail unix_listener dict { #mode = 0600 #user = #group = } } dovecot-2.2.33.2/doc/example-config/conf.d/Makefile.in0000644000175000017500000004157713172375572017236 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = doc/example-config/conf.d ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/want_apparmor.m4 \ $(top_srcdir)/dovecot.m4 $(top_srcdir)/libtool.m4 \ $(top_srcdir)/ltoptions.m4 $(top_srcdir)/ltsugar.m4 \ $(top_srcdir)/ltversion.m4 $(top_srcdir)/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(exampledir)" DATA = $(example_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTH_CFLAGS = @AUTH_CFLAGS@ AUTH_LIBS = @AUTH_LIBS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ CASSANDRA_LIBS = @CASSANDRA_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CDB_LIBS = @CDB_LIBS@ CFLAGS = @CFLAGS@ CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ CLUCENE_LIBS = @CLUCENE_LIBS@ COMPRESS_LIBS = @COMPRESS_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPT_LIBS = @CRYPT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DICT_LIBS = @DICT_LIBS@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KRB5CONFIG = @KRB5CONFIG@ KRB5_CFLAGS = @KRB5_CFLAGS@ KRB5_LIBS = @KRB5_LIBS@ LD = @LD@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBCAP = @LIBCAP@ LIBDOVECOT = @LIBDOVECOT@ LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ LIBICONV = @LIBICONV@ LIBICU_CFLAGS = @LIBICU_CFLAGS@ LIBICU_LIBS = @LIBICU_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ LINKED_STORAGE_LIBS = @LINKED_STORAGE_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODULE_LIBS = @MODULE_LIBS@ MODULE_SUFFIX = @MODULE_SUFFIX@ MYSQL_CFLAGS = @MYSQL_CFLAGS@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PGSQL_CFLAGS = @PGSQL_CFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PG_CONFIG = @PG_CONFIG@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QUOTA_LIBS = @QUOTA_LIBS@ RANLIB = @RANLIB@ RPCGEN = @RPCGEN@ RUN_TEST = @RUN_TEST@ SED = @SED@ SETTING_FILES = @SETTING_FILES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SQL_CFLAGS = @SQL_CFLAGS@ SQL_LIBS = @SQL_LIBS@ SSL_CFLAGS = @SSL_CFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ dict_drivers = @dict_drivers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mail_storages = @mail_storages@ mailbox_list_drivers = @mailbox_list_drivers@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ rundir = @rundir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sql_drivers = @sql_drivers@ srcdir = @srcdir@ ssldir = @ssldir@ statedir = @statedir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkgsysconfdir = $(sysconfdir)/dovecot exampledir = $(docdir)/example-config/conf.d example_DATA = \ auth-checkpassword.conf.ext \ auth-deny.conf.ext \ auth-dict.conf.ext \ auth-ldap.conf.ext \ auth-master.conf.ext \ auth-passwdfile.conf.ext \ auth-sql.conf.ext \ auth-static.conf.ext \ auth-system.conf.ext \ auth-vpopmail.conf.ext \ 10-auth.conf \ 10-director.conf \ 10-logging.conf \ 10-mail.conf \ 10-master.conf \ 10-ssl.conf \ 15-lda.conf \ 15-mailboxes.conf \ 20-imap.conf \ 20-lmtp.conf \ 20-pop3.conf \ 90-acl.conf \ 90-plugin.conf \ 90-quota.conf EXTRA_DIST = \ $(example_DATA) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/example-config/conf.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/example-config/conf.d/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-exampleDATA: $(example_DATA) @$(NORMAL_INSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(exampledir)'"; \ $(MKDIR_P) "$(DESTDIR)$(exampledir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(exampledir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(exampledir)" || exit $$?; \ done uninstall-exampleDATA: @$(NORMAL_UNINSTALL) @list='$(example_DATA)'; test -n "$(exampledir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(exampledir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(exampledir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-exampleDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-exampleDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exampleDATA install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-exampleDATA .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.2.33.2/doc/example-config/conf.d/10-mail.conf0000644000175000017500000004106213165463624017163 00000000000000## ## Mailbox locations and namespaces ## # Location for users' mailboxes. The default is empty, which means that Dovecot # tries to find the mailboxes automatically. This won't work if the user # doesn't yet have any mail, so you should explicitly tell Dovecot the full # location. # # If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u) # isn't enough. You'll also need to tell Dovecot where the other mailboxes are # kept. This is called the "root mail directory", and it must be the first # path given in the mail_location setting. # # There are a few special variables you can use, eg.: # # %u - username # %n - user part in user@domain, same as %u if there's no domain # %d - domain part in user@domain, empty if there's no domain # %h - home directory # # See doc/wiki/Variables.txt for full list. Some examples: # # mail_location = maildir:~/Maildir # mail_location = mbox:~/mail:INBOX=/var/mail/%u # mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n # # # #mail_location = # If you need to set multiple mailbox locations or want to change default # namespace settings, you can do it by defining namespace sections. # # You can have private, shared and public namespaces. Private namespaces # are for user's personal mails. Shared namespaces are for accessing other # users' mailboxes that have been shared. Public namespaces are for shared # mailboxes that are managed by sysadmin. If you create any shared or public # namespaces you'll typically want to enable ACL plugin also, otherwise all # users can access all the shared mailboxes, assuming they have permissions # on filesystem level to do so. namespace inbox { # Namespace type: private, shared or public #type = private # Hierarchy separator to use. You should use the same separator for all # namespaces or some clients get confused. '/' is usually a good one. # The default however depends on the underlying mail storage format. #separator = # Prefix required to access this namespace. This needs to be different for # all namespaces. For example "Public/". #prefix = # Physical location of the mailbox. This is in same format as # mail_location, which is also the default for it. #location = # There can be only one INBOX, and this setting defines which namespace # has it. inbox = yes # If namespace is hidden, it's not advertised to clients via NAMESPACE # extension. You'll most likely also want to set list=no. This is mostly # useful when converting from another server with different namespaces which # you want to deprecate but still keep working. For example you can create # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". #hidden = no # Show the mailboxes under this namespace with LIST command. This makes the # namespace visible for clients that don't support NAMESPACE extension. # "children" value lists child mailboxes, but hides the namespace prefix. #list = yes # Namespace handles its own subscriptions. If set to "no", the parent # namespace handles them (empty prefix should always have this as "yes") #subscriptions = yes # See 15-mailboxes.conf for definitions of special mailboxes. } # Example shared namespace configuration #namespace { #type = shared #separator = / # Mailboxes are visible under "shared/user@domain/" # %%n, %%d and %%u are expanded to the destination user. #prefix = shared/%%u/ # Mail location for other users' mailboxes. Note that %variables and ~/ # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the # destination user's data. #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u # Use the default namespace for saving subscriptions. #subscriptions = no # List the shared/ namespace only if there are visible shared mailboxes. #list = children #} # Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? #mail_shared_explicit_inbox = no # System user and group used to access mails. If you use multiple, userdb # can override these by returning uid or gid fields. You can use either numbers # or names. #mail_uid = #mail_gid = # Group to enable temporarily for privileged operations. Currently this is # used only with INBOX when either its initial creation or dotlocking fails. # Typically this is set to "mail" to give access to /var/mail. #mail_privileged_group = # Grant access to these supplementary groups for mail processes. Typically # these are used to set up access to shared mailboxes. Note that it may be # dangerous to set these if users can create symlinks (e.g. if "mail" group is # set here, ln -s /var/mail ~/mail/var could allow a user to delete others' # mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). #mail_access_groups = # Allow full filesystem access to clients. There's no access checks other than # what the operating system does for the active UID/GID. It works with both # maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ # or ~user/. #mail_full_filesystem_access = no # Dictionary for key=value mailbox attributes. This is used for example by # URLAUTH and METADATA extensions. #mail_attribute_dict = # A comment or note that is associated with the server. This value is # accessible for authenticated users through the IMAP METADATA server # entry "/shared/comment". #mail_server_comment = "" # Indicates a method for contacting the server administrator. According to # RFC 5464, this value MUST be a URI (e.g., a mailto: or tel: URL), but that # is currently not enforced. Use for example mailto:admin@example.com. This # value is accessible for authenticated users through the IMAP METADATA server # entry "/shared/admin". #mail_server_admin = ## ## Mail processes ## # Don't use mmap() at all. This is required if you store indexes to shared # filesystems (NFS or clustered filesystem). #mmap_disable = no # Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL # since version 3, so this should be safe to use nowadays by default. #dotlock_use_excl = yes # When to use fsync() or fdatasync() calls: # optimized (default): Whenever necessary to avoid losing important data # always: Useful with e.g. NFS when write()s are delayed # never: Never use it (best performance, but crashes can lose data) #mail_fsync = optimized # Locking method for index files. Alternatives are fcntl, flock and dotlock. # Dotlocking uses some tricks which may create more disk I/O than other locking # methods. NFS users: flock doesn't work, remember to change mmap_disable. #lock_method = fcntl # Directory in which LDA/LMTP temporarily stores incoming mails >128 kB. #mail_temp_dir = /tmp # Valid UID range for users, defaults to 500 and above. This is mostly # to make sure that users can't log in as daemons or other system users. # Note that denying root logins is hardcoded to dovecot binary and can't # be done even if first_valid_uid is set to 0. #first_valid_uid = 500 #last_valid_uid = 0 # Valid GID range for users, defaults to non-root/wheel. Users having # non-valid GID as primary group ID aren't allowed to log in. If user # belongs to supplementary groups with non-valid GIDs, those groups are # not set. #first_valid_gid = 1 #last_valid_gid = 0 # Maximum allowed length for mail keyword name. It's only forced when trying # to create new keywords. #mail_max_keyword_length = 50 # ':' separated list of directories under which chrooting is allowed for mail # processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). # This setting doesn't affect login_chroot, mail_chroot or auth chroot # settings. If this setting is empty, "/./" in home dirs are ignored. # WARNING: Never add directories here which local users can modify, that # may lead to root exploit. Usually this should be done only if you don't # allow shell access for users. #valid_chroot_dirs = # Default chroot directory for mail processes. This can be overridden for # specific users in user database by giving /./ in user's home directory # (eg. /home/./user chroots into /home). Note that usually there is no real # need to do chrooting, Dovecot doesn't allow users to access files outside # their mail directory anyway. If your home directories are prefixed with # the chroot directory, append "/." to mail_chroot. #mail_chroot = # UNIX socket path to master authentication server to find users. # This is used by imap (for shared users) and lda. #auth_socket_path = /var/run/dovecot/auth-userdb # Directory where to look up mail plugins. #mail_plugin_dir = /usr/lib/dovecot # Space separated list of plugins to load for all services. Plugins specific to # IMAP, LDA, etc. are added to this list in their own .conf files. #mail_plugins = ## ## Mailbox handling optimizations ## # Mailbox list indexes can be used to optimize IMAP STATUS commands. They are # also required for IMAP NOTIFY extension to be enabled. #mailbox_list_index = no # Trust mailbox list index to be up-to-date. This reduces disk I/O at the cost # of potentially returning out-of-date results after e.g. server crashes. # The results will be automatically fixed once the folders are opened. #mailbox_list_index_very_dirty_syncs = yes # Should INBOX be kept up-to-date in the mailbox list index? By default it's # not, because most of the mailbox accesses will open INBOX anyway. #mailbox_list_index_include_inbox = no # The minimum number of mails in a mailbox before updates are done to cache # file. This allows optimizing Dovecot's behavior to do less disk writes at # the cost of more disk reads. #mail_cache_min_mail_count = 0 # When IDLE command is running, mailbox is checked once in a while to see if # there are any new mails or other changes. This setting defines the minimum # time to wait between those checks. Dovecot can also use inotify and # kqueue to find out immediately when changes occur. #mailbox_idle_check_interval = 30 secs # Save mails with CR+LF instead of plain LF. This makes sending those mails # take less CPU, especially with sendfile() syscall with Linux and FreeBSD. # But it also creates a bit more disk I/O which may just make it slower. # Also note that if other software reads the mboxes/maildirs, they may handle # the extra CRs wrong and cause problems. #mail_save_crlf = no # Max number of mails to keep open and prefetch to memory. This only works with # some mailbox formats and/or operating systems. #mail_prefetch_count = 0 # How often to scan for stale temporary files and delete them (0 = never). # These should exist only after Dovecot dies in the middle of saving mails. #mail_temp_scan_interval = 1w # How many slow mail accesses sorting can perform before it returns failure. # With IMAP the reply is: NO [LIMIT] Requested sort would have taken too long. # The untagged SORT reply is still returned, but it's likely not correct. #mail_sort_max_read_count = 0 protocol !indexer-worker { # If folder vsize calculation requires opening more than this many mails from # disk (i.e. mail sizes aren't in cache already), return failure and finish # the calculation via indexer process. Disabled by default. This setting must # be 0 for indexer-worker processes. #mail_vsize_bg_after_count = 0 } ## ## Maildir-specific settings ## # By default LIST command returns all entries in maildir beginning with a dot. # Enabling this option makes Dovecot return only entries which are directories. # This is done by stat()ing each entry, so it causes more disk I/O. # (For systems setting struct dirent->d_type, this check is free and it's # done always regardless of this setting) #maildir_stat_dirs = no # When copying a message, do it with hard links whenever possible. This makes # the performance much better, and it's unlikely to have any side effects. #maildir_copy_with_hardlinks = yes # Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only # when its mtime changes unexpectedly or when we can't find the mail otherwise. #maildir_very_dirty_syncs = no # If enabled, Dovecot doesn't use the S= in the Maildir filenames for # getting the mail's physical size, except when recalculating Maildir++ quota. # This can be useful in systems where a lot of the Maildir filenames have a # broken size. The performance hit for enabling this is very small. #maildir_broken_filename_sizes = no # Always move mails from new/ directory to cur/, even when the \Recent flags # aren't being reset. #maildir_empty_new = no ## ## mbox-specific settings ## # Which locking methods to use for locking mbox. There are four available: # dotlock: Create .lock file. This is the oldest and most NFS-safe # solution. If you want to use /var/mail/ like directory, the users # will need write access to that directory. # dotlock_try: Same as dotlock, but if it fails because of permissions or # because there isn't enough disk space, just skip it. # fcntl : Use this if possible. Works with NFS too if lockd is used. # flock : May not exist in all systems. Doesn't work with NFS. # lockf : May not exist in all systems. Doesn't work with NFS. # # You can use multiple locking methods; if you do the order they're declared # in is important to avoid deadlocks if other MTAs/MUAs are using multiple # locking methods as well. Some operating systems don't allow using some of # them simultaneously. #mbox_read_locks = fcntl #mbox_write_locks = dotlock fcntl # Maximum time to wait for lock (all of them) before aborting. #mbox_lock_timeout = 5 mins # If dotlock exists but the mailbox isn't modified in any way, override the # lock file after this much time. #mbox_dotlock_change_timeout = 2 mins # When mbox changes unexpectedly we have to fully read it to find out what # changed. If the mbox is large this can take a long time. Since the change # is usually just a newly appended mail, it'd be faster to simply read the # new mails. If this setting is enabled, Dovecot does this but still safely # fallbacks to re-reading the whole mbox file whenever something in mbox isn't # how it's expected to be. The only real downside to this setting is that if # some other MUA changes message flags, Dovecot doesn't notice it immediately. # Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK # commands. #mbox_dirty_syncs = yes # Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, # EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. #mbox_very_dirty_syncs = no # Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK # commands and when closing the mailbox). This is especially useful for POP3 # where clients often delete all mails. The downside is that our changes # aren't immediately visible to other MUAs. #mbox_lazy_writes = yes # If mbox size is smaller than this (e.g. 100k), don't write index files. # If an index file already exists it's still read, just not updated. #mbox_min_index_size = 0 # Mail header selection algorithm to use for MD5 POP3 UIDLs when # pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired # algorithm, but it fails if the first Received: header isn't unique in all # mails. An alternative algorithm is "all" that selects all headers. #mbox_md5 = apop3d ## ## mdbox-specific settings ## # Maximum dbox file size until it's rotated. #mdbox_rotate_size = 2M # Maximum dbox file age until it's rotated. Typically in days. Day begins # from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. #mdbox_rotate_interval = 0 # When creating new mdbox files, immediately preallocate their size to # mdbox_rotate_size. This setting currently works only in Linux with some # filesystems (ext4, xfs). #mdbox_preallocate_space = no ## ## Mail attachments ## # sdbox and mdbox support saving mail attachments to external files, which # also allows single instance storage for them. Other backends don't support # this for now. # Directory root where to store mail attachments. Disabled, if empty. #mail_attachment_dir = # Attachments smaller than this aren't saved externally. It's also possible to # write a plugin to disable saving specific attachments externally. #mail_attachment_min_size = 128k # Filesystem backend to use for saving attachments: # posix : No SiS done by Dovecot (but this might help FS's own deduplication) # sis posix : SiS with immediate byte-by-byte comparison during saving # sis-queue posix : SiS with delayed comparison and deduplication #mail_attachment_fs = sis posix # Hash format to use in attachment filenames. You can add any text and # variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. # Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits #mail_attachment_hash = %{sha1} dovecot-2.2.33.2/doc/example-config/conf.d/auth-sql.conf.ext0000644000175000017500000000142413123174404020343 00000000000000# Authentication for SQL users. Included from 10-auth.conf. # # passdb { driver = sql # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext args = /etc/dovecot/dovecot-sql.conf.ext } # "prefetch" user database means that the passdb already provided the # needed information and there's no need to do a separate userdb lookup. # #userdb { # driver = prefetch #} userdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } # If you don't have any user-specific settings, you can avoid the user_query # by using userdb static instead of userdb sql, for example: # #userdb { #driver = static #args = uid=vmail gid=vmail home=/var/vmail/%u #} dovecot-2.2.33.2/doc/example-config/conf.d/auth-ldap.conf.ext0000644000175000017500000000163413123174404020467 00000000000000# Authentication for LDAP users. Included from 10-auth.conf. # # passdb { driver = ldap # Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext args = /etc/dovecot/dovecot-ldap.conf.ext } # "prefetch" user database means that the passdb already provided the # needed information and there's no need to do a separate userdb lookup. # #userdb { # driver = prefetch #} userdb { driver = ldap args = /etc/dovecot/dovecot-ldap.conf.ext # Default fields can be used to specify defaults that LDAP may override #default_fields = home=/home/virtual/%u } # If you don't have any user-specific settings, you can avoid the userdb LDAP # lookup by using userdb static instead of userdb ldap, for example: # #userdb { #driver = static #args = uid=vmail gid=vmail home=/var/vmail/%u #} dovecot-2.2.33.2/doc/example-config/conf.d/auth-system.conf.ext0000644000175000017500000000421113165463624021100 00000000000000# Authentication for system users. Included from 10-auth.conf. # # # # PAM authentication. Preferred nowadays by most systems. # PAM is typically used with either userdb passwd or userdb static. # REMEMBER: You'll need /etc/pam.d/dovecot file created for PAM # authentication to actually work. passdb { driver = pam # [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=] # [cache_key=] [] #args = dovecot } # System users (NSS, /etc/passwd, or similiar). # In many systems nowadays this uses Name Service Switch, which is # configured in /etc/nsswitch.conf. #passdb { #driver = passwd # [blocking=no] #args = #} # Shadow passwords for system users (NSS, /etc/shadow or similiar). # Deprecated by PAM nowadays. # #passdb { #driver = shadow # [blocking=no] #args = #} # PAM-like authentication for OpenBSD. # #passdb { #driver = bsdauth # [blocking=no] [cache_key=] #args = #} ## ## User databases ## # System users (NSS, /etc/passwd, or similiar). In many systems nowadays this # uses Name Service Switch, which is configured in /etc/nsswitch.conf. userdb { # driver = passwd # [blocking=no] #args = # Override fields from passwd #override_fields = home=/home/virtual/%u } # Static settings generated from template #userdb { #driver = static # Can return anything a userdb could normally return. For example: # # args = uid=500 gid=500 home=/var/mail/%u # # LDA and LMTP needs to look up users only from the userdb. This of course # doesn't work with static userdb because there is no list of users. # Normally static userdb handles this by doing a passdb lookup. This works # with most passdbs, with PAM being the most notable exception. If you do # the user verification another way, you can add allow_all_users=yes to # the args in which case the passdb lookup is skipped. # #args = #} dovecot-2.2.33.2/doc/example-config/conf.d/auth-checkpassword.conf.ext0000644000175000017500000000076313123174404022411 00000000000000# Authentication for checkpassword users. Included from 10-auth.conf. # # passdb { driver = checkpassword args = /usr/bin/checkpassword } # passdb lookup should return also userdb info userdb { driver = prefetch } # Standard checkpassword doesn't support direct userdb lookups. # If you need checkpassword userdb, the checkpassword must support # Dovecot-specific extensions. #userdb { # driver = checkpassword # args = /usr/bin/checkpassword #} dovecot-2.2.33.2/doc/example-config/conf.d/auth-dict.conf.ext0000644000175000017500000000052713123174404020472 00000000000000# Authentication via dict backend. Included from 10-auth.conf. # # passdb { driver = dict # Path for dict configuration file, see # example-config/dovecot-dict-auth.conf.ext args = /etc/dovecot/dovecot-dict-auth.conf.ext } userdb { driver = dict args = /etc/dovecot/dovecot-dict-auth.conf.ext } dovecot-2.2.33.2/doc/example-config/conf.d/90-acl.conf0000644000175000017500000000124413123174404016773 00000000000000## ## Mailbox access control lists. ## # vfile backend reads ACLs from "dovecot-acl" file from mail directory. # You can also optionally give a global ACL directory path where ACLs are # applied to all users' mailboxes. The global ACL directory contains # one file for each mailbox, eg. INBOX or sub.mailbox. cache_secs parameter # specifies how many seconds to wait between stat()ing dovecot-acl file # to see if it changed. plugin { #acl = vfile:/etc/dovecot/global-acls:cache_secs=300 } # To let users LIST mailboxes shared by other users, Dovecot needs a # shared mailbox dictionary. For example: plugin { #acl_shared_dict = file:/var/lib/dovecot/shared-mailboxes } dovecot-2.2.33.2/doc/example-config/conf.d/auth-deny.conf.ext0000644000175000017500000000075113123174404020505 00000000000000# Deny access for users. Included from 10-auth.conf. # Users can be (temporarily) disabled by adding a passdb with deny=yes. # If the user is found from that database, authentication will fail. # The deny passdb should always be specified before others, so it gets # checked first. # Example deny passdb using passwd-file. You can use any passdb though. passdb { driver = passwd-file deny = yes # File contains a list of usernames, one per line args = /etc/dovecot/deny-users } dovecot-2.2.33.2/doc/example-config/conf.d/90-plugin.conf0000644000175000017500000000044413123174404017533 00000000000000## ## Plugin settings ## # All wanted plugins must be listed in mail_plugins setting before any of the # settings take effect. See for list of plugins and # their configuration. Note that %variable expansion is done for all values. plugin { #setting_name = value } dovecot-2.2.33.2/doc/example-config/conf.d/auth-static.conf.ext0000644000175000017500000000114313123174404021031 00000000000000# Static passdb. Included from 10-auth.conf. # This can be used for situations where Dovecot doesn't need to verify the # username or the password, or if there is a single password for all users: # # - proxy frontend, where the backend verifies the password # - proxy backend, where the frontend already verified the password # - authentication with SSL certificates # - simple testing #passdb { # driver = static # args = proxy=y host=%1Mu.example.com nopassword=y #} #passdb { # driver = static # args = password=test #} #userdb { # driver = static # args = uid=vmail gid=vmail home=/home/%u #} dovecot-2.2.33.2/doc/example-config/conf.d/90-quota.conf0000644000175000017500000000504513123174404017370 00000000000000## ## Quota configuration. ## # Note that you also have to enable quota plugin in mail_plugins setting. # ## ## Quota limits ## # Quota limits are set using "quota_rule" parameters. To get per-user quota # limits, you can set/override them by returning "quota_rule" extra field # from userdb. It's also possible to give mailbox-specific limits, for example # to give additional 100 MB when saving to Trash: plugin { #quota_rule = *:storage=1G #quota_rule2 = Trash:storage=+100M # LDA/LMTP allows saving the last mail to bring user from under quota to # over quota, if the quota doesn't grow too high. Default is to allow as # long as quota will stay under 10% above the limit. Also allowed e.g. 10M. #quota_grace = 10%% # Quota plugin can also limit the maximum accepted mail size. #quota_max_mail_size = 100M } ## ## Quota warnings ## # You can execute a given command when user exceeds a specified quota limit. # Each quota root has separate limits. Only the command for the first # exceeded limit is excecuted, so put the highest limit first. # The commands are executed via script service by connecting to the named # UNIX socket (quota-warning below). # Note that % needs to be escaped as %%, otherwise "% " expands to empty. plugin { #quota_warning = storage=95%% quota-warning 95 %u #quota_warning2 = storage=80%% quota-warning 80 %u } # Example quota-warning service. The unix listener's permissions should be # set in a way that mail processes can connect to it. Below example assumes # that mail processes run as vmail user. If you use mode=0666, all system users # can generate quota warnings to anyone. #service quota-warning { # executable = script /usr/local/bin/quota-warning.sh # user = dovecot # unix_listener quota-warning { # user = vmail # } #} ## ## Quota backends ## # Multiple backends are supported: # dirsize: Find and sum all the files found from mail directory. # Extremely SLOW with Maildir. It'll eat your CPU and disk I/O. # dict: Keep quota stored in dictionary (eg. SQL) # maildir: Maildir++ quota # fs: Read-only support for filesystem quota plugin { #quota = dirsize:User quota #quota = maildir:User quota #quota = dict:User quota::proxy::quota #quota = fs:User quota } # Multiple quota roots are also possible, for example this gives each user # their own 100MB quota and one shared 1GB quota within the domain: plugin { #quota = dict:user::proxy::quota #quota2 = dict:domain:%d:proxy::quota_domain #quota_rule = *:storage=102400 #quota2_rule = *:storage=1048576 } dovecot-2.2.33.2/doc/example-config/conf.d/15-mailboxes.conf0000644000175000017500000000537013123174404020220 00000000000000## ## Mailbox definitions ## # Each mailbox is specified in a separate mailbox section. The section name # specifies the mailbox name. If it has spaces, you can put the name # "in quotes". These sections can contain the following mailbox settings: # # auto: # Indicates whether the mailbox with this name is automatically created # implicitly when it is first accessed. The user can also be automatically # subscribed to the mailbox after creation. The following values are # defined for this setting: # # no - Never created automatically. # create - Automatically created, but no automatic subscription. # subscribe - Automatically created and subscribed. # # special_use: # A space-separated list of SPECIAL-USE flags (RFC 6154) to use for the # mailbox. There are no validity checks, so you could specify anything # you want in here, but it's not a good idea to use flags other than the # standard ones specified in the RFC: # # \All - This (virtual) mailbox presents all messages in the # user's message store. # \Archive - This mailbox is used to archive messages. # \Drafts - This mailbox is used to hold draft messages. # \Flagged - This (virtual) mailbox presents all messages in the # user's message store marked with the IMAP \Flagged flag. # \Junk - This mailbox is where messages deemed to be junk mail # are held. # \Sent - This mailbox is used to hold copies of messages that # have been sent. # \Trash - This mailbox is used to hold messages that have been # deleted. # # comment: # Defines a default comment or note associated with the mailbox. This # value is accessible through the IMAP METADATA mailbox entries # "/shared/comment" and "/private/comment". Users with sufficient # privileges can override the default value for entries with a custom # value. # NOTE: Assumes "namespace inbox" has been defined in 10-mail.conf. namespace inbox { # These mailboxes are widely used and could perhaps be created automatically: mailbox Drafts { special_use = \Drafts } mailbox Junk { special_use = \Junk } mailbox Trash { special_use = \Trash } # For \Sent mailboxes there are two widely used names. We'll mark both of # them as \Sent. User typically deletes one of them if duplicates are created. mailbox Sent { special_use = \Sent } mailbox "Sent Messages" { special_use = \Sent } # If you have a virtual "All messages" mailbox: #mailbox virtual/All { # special_use = \All # comment = All my messages #} # If you have a virtual "Flagged" mailbox: #mailbox virtual/Flagged { # special_use = \Flagged # comment = All my flagged messages #} } dovecot-2.2.33.2/doc/example-config/conf.d/20-imap.conf0000644000175000017500000001021313165463624017162 00000000000000## ## IMAP specific settings ## # If nothing happens for this long while client is IDLEing, move the connection # to imap-hibernate process and close the old imap process. This saves memory, # because connections use very little memory in imap-hibernate process. The # downside is that recreating the imap process back uses some resources. #imap_hibernate_timeout = 0 # Maximum IMAP command line length. Some clients generate very long command # lines with huge mailboxes, so you may need to raise this if you get # "Too long argument" or "IMAP command line too large" errors often. #imap_max_line_length = 64k # IMAP logout format string: # %i - total number of bytes read from client # %o - total number of bytes sent to client # %{fetch_hdr_count} - Number of mails with mail header data sent to client # %{fetch_hdr_bytes} - Number of bytes with mail header data sent to client # %{fetch_body_count} - Number of mails with mail body data sent to client # %{fetch_body_bytes} - Number of bytes with mail body data sent to client # %{deleted} - Number of mails where client added \Deleted flag # %{expunged} - Number of mails that client expunged, which does not # include automatically expunged mails # %{autoexpunged} - Number of mails that were automatically expunged after # client disconnected # %{trashed} - Number of mails that client copied/moved to the # special_use=\Trash mailbox. # %{appended} - Number of mails saved during the session #imap_logout_format = in=%i out=%o # Override the IMAP CAPABILITY response. If the value begins with '+', # add the given capabilities on top of the defaults (e.g. +XFOO XBAR). #imap_capability = # How long to wait between "OK Still here" notifications when client is # IDLEing. #imap_idle_notify_interval = 2 mins # ID field names and values to send to clients. Using * as the value makes # Dovecot use the default value. The following fields have default values # currently: name, version, os, os-version, support-url, support-email. #imap_id_send = # ID fields sent by client to log. * means everything. #imap_id_log = # Workarounds for various client bugs: # delay-newmail: # Send EXISTS/RECENT new mail notifications only when replying to NOOP # and CHECK commands. Some clients ignore them otherwise, for example OSX # Mail ( mail server # mapping. As long as user has simultaneous connections, the user is always # redirected to the same server. Each proxy server is running its own director # process, and the directors are communicating the state to each others. # Directors are mainly useful with NFS-like setups. # List of IPs or hostnames to all director servers, including ourself. # Ports can be specified as ip:port. The default port is the same as # what director service's inet_listener is using. #director_servers = # List of IPs or hostnames to all backend mail servers. Ranges are allowed # too, like 10.0.0.10-10.0.0.30. #director_mail_servers = # How long to redirect users to a specific server after it no longer has # any connections. #director_user_expire = 15 min # TCP/IP port that accepts doveadm connections (instead of director connections) # If you enable this, you'll also need to add inet_listener for the port. #director_doveadm_port = 0 # How the username is translated before being hashed. Useful values include # %Ln if user can log in with or without @domain, %Ld if mailboxes are shared # within domain. #director_username_hash = %Lu # To enable director service, uncomment the modes and assign a port. service director { unix_listener login/director { #mode = 0666 } fifo_listener login/proxy-notify { #mode = 0666 } unix_listener director-userdb { #mode = 0600 } inet_listener { #port = } } # Enable director for the wanted login services by telling them to # connect to director socket instead of the default login socket: service imap-login { #executable = imap-login director } service pop3-login { #executable = pop3-login director } # Enable director for LMTP proxying: protocol lmtp { #auth_socket_path = director-userdb } dovecot-2.2.33.2/doc/example-config/conf.d/20-lmtp.conf0000644000175000017500000000165013123174404017202 00000000000000## ## LMTP specific settings ## # Support proxying to other LMTP/SMTP servers by performing passdb lookups. #lmtp_proxy = no # When recipient address includes the detail (e.g. user+detail), try to save # the mail to the detail mailbox. See also recipient_delimiter and # lda_mailbox_autocreate settings. #lmtp_save_to_detail_mailbox = no # Verify quota before replying to RCPT TO. This adds a small overhead. #lmtp_rcpt_check_quota = no # Which recipient address to use for Delivered-To: header and Received: # header. The default is "final", which is the same as the one given to # RCPT TO command. "original" uses the address given in RCPT TO's ORCPT # parameter, "none" uses nothing. Note that "none" is currently always used # when a mail has multiple recipients. #lmtp_hdr_delivery_address = final protocol lmtp { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins } dovecot-2.2.33.2/doc/example-config/conf.d/20-pop3.conf0000644000175000017500000000774113123174404017116 00000000000000## ## POP3 specific settings ## # Don't try to set mails non-recent or seen with POP3 sessions. This is # mostly intended to reduce disk I/O. With maildir it doesn't move files # from new/ to cur/, with mbox it doesn't write Status-header. #pop3_no_flag_updates = no # Support LAST command which exists in old POP3 specs, but has been removed # from new ones. Some clients still wish to use this though. Enabling this # makes RSET command clear all \Seen flags from messages. #pop3_enable_last = no # If mail has X-UIDL header, use it as the mail's UIDL. #pop3_reuse_xuidl = no # Allow only one POP3 session to run simultaneously for the same user. #pop3_lock_session = no # POP3 requires message sizes to be listed as if they had CR+LF linefeeds. # Many POP3 servers violate this by returning the sizes with LF linefeeds, # because it's faster to get. When this setting is enabled, Dovecot still # tries to do the right thing first, but if that requires opening the # message, it fallbacks to the easier (but incorrect) size. #pop3_fast_size_lookups = no # POP3 UIDL (unique mail identifier) format to use. You can use following # variables, along with the variable modifiers described in # doc/wiki/Variables.txt (e.g. %Uf for the filename in uppercase) # # %v - Mailbox's IMAP UIDVALIDITY # %u - Mail's IMAP UID # %m - MD5 sum of the mailbox headers in hex (mbox only) # %f - filename (maildir only) # %g - Mail's GUID # # If you want UIDL compatibility with other POP3 servers, use: # UW's ipop3d : %08Xv%08Xu # Courier : %f or %v-%u (both might be used simultaneosly) # Cyrus (<= 2.1.3) : %u # Cyrus (>= 2.1.4) : %v.%u # Dovecot v0.99.x : %v.%u # tpop3d : %Mf # # Note that Outlook 2003 seems to have problems with %v.%u format which was # Dovecot's default, so if you're building a new server it would be a good # idea to change this. %08Xu%08Xv should be pretty fail-safe. # #pop3_uidl_format = %08Xu%08Xv # Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes # won't change those UIDLs. Currently this works only with Maildir. #pop3_save_uidl = no # What to do about duplicate UIDLs if they exist? # allow: Show duplicates to clients. # rename: Append a temporary -2, -3, etc. counter after the UIDL. #pop3_uidl_duplicates = allow # This option changes POP3 behavior so that it's not possible to actually # delete mails via POP3, only hide them from future POP3 sessions. The mails # will still be counted towards user's quota until actually deleted via IMAP. # Use e.g. "$POP3Deleted" as the value (it will be visible as IMAP keyword). # Make sure you can legally archive mails before enabling this setting. #pop3_deleted_flag = # POP3 logout format string: # %i - total number of bytes read from client # %o - total number of bytes sent to client # %t - number of TOP commands # %p - number of bytes sent to client as a result of TOP command # %r - number of RETR commands # %b - number of bytes sent to client as a result of RETR command # %d - number of deleted messages # %{deleted_bytes} - number of bytes in deleted messages # %m - number of messages (before deletion) # %s - mailbox size in bytes (before deletion) # %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly #pop3_logout_format = top=%t/%p, retr=%r/%b, del=%d/%m, size=%s # Workarounds for various client bugs: # outlook-no-nuls: # Outlook and Outlook Express hang if mails contain NUL characters. # This setting replaces them with 0x80 character. # oe-ns-eoh: # Outlook Express and Netscape Mail breaks if end of headers-line is # missing. This option simply sends it if it's missing. # The list is space-separated. #pop3_client_workarounds = protocol pop3 { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins # Maximum number of POP3 connections allowed for a user from each IP address. # NOTE: The username is compared case-sensitively. #mail_max_userip_connections = 10 } dovecot-2.2.33.2/doc/example-config/conf.d/10-auth.conf0000644000175000017500000001226013123174404017165 00000000000000## ## Authentication processes ## # Disable LOGIN command and all other plaintext authentications unless # SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP # matches the local IP (ie. you're connecting from the same computer), the # connection is considered secure and plaintext authentication is allowed. # See also ssl=required setting. #disable_plaintext_auth = yes # Authentication cache size (e.g. 10M). 0 means it's disabled. Note that # bsdauth, PAM and vpopmail require cache_key to be set for caching to be used. #auth_cache_size = 0 # Time to live for cached data. After TTL expires the cached record is no # longer used, *except* if the main database lookup returns internal failure. # We also try to handle password changes automatically: If user's previous # authentication was successful, but this one wasn't, the cache isn't used. # For now this works only with plaintext authentication. #auth_cache_ttl = 1 hour # TTL for negative hits (user not found, password mismatch). # 0 disables caching them completely. #auth_cache_negative_ttl = 1 hour # Space separated list of realms for SASL authentication mechanisms that need # them. You can leave it empty if you don't want to support multiple realms. # Many clients simply use the first one listed here, so keep the default realm # first. #auth_realms = # Default realm/domain to use if none was specified. This is used for both # SASL realms and appending @domain to username in plaintext logins. #auth_default_realm = # List of allowed characters in username. If the user-given username contains # a character not listed in here, the login automatically fails. This is just # an extra check to make sure user can't exploit any potential quote escaping # vulnerabilities with SQL/LDAP databases. If you want to allow all characters, # set this value to empty. #auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@ # Username character translations before it's looked up from databases. The # value contains series of from -> to characters. For example "#@/@" means # that '#' and '/' characters are translated to '@'. #auth_username_translation = # Username formatting before it's looked up from databases. You can use # the standard variables here, eg. %Lu would lowercase the username, %n would # drop away the domain if it was given, or "%n-AT-%d" would change the '@' into # "-AT-". This translation is done after auth_username_translation changes. #auth_username_format = %Lu # If you want to allow master users to log in by specifying the master # username within the normal username string (ie. not using SASL mechanism's # support for it), you can specify the separator character here. The format # is then . UW-IMAP uses "*" as the # separator, so that could be a good choice. #auth_master_user_separator = # Username to use for users logging in with ANONYMOUS SASL mechanism #auth_anonymous_username = anonymous # Maximum number of dovecot-auth worker processes. They're used to execute # blocking passdb and userdb queries (eg. MySQL and PAM). They're # automatically created and destroyed as needed. #auth_worker_max_count = 30 # Host name to use in GSSAPI principal names. The default is to use the # name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab # entries. #auth_gssapi_hostname = # Kerberos keytab to use for the GSSAPI mechanism. Will use the system # default (usually /etc/krb5.keytab) if not specified. You may need to change # the auth service to run as root to be able to read this file. #auth_krb5_keytab = # Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and # ntlm_auth helper. #auth_use_winbind = no # Path for Samba's ntlm_auth helper binary. #auth_winbind_helper_path = /usr/bin/ntlm_auth # Time to delay before replying to failed authentications. #auth_failure_delay = 2 secs # Require a valid SSL client certificate or the authentication fails. #auth_ssl_require_client_cert = no # Take the username from client's SSL certificate, using # X509_NAME_get_text_by_NID() which returns the subject's DN's # CommonName. #auth_ssl_username_from_cert = no # Space separated list of wanted authentication mechanisms: # plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey # gss-spnego # NOTE: See also disable_plaintext_auth setting. auth_mechanisms = plain ## ## Password and user databases ## # # Password database is used to verify user's password (and nothing more). # You can have multiple passdbs and userdbs. This is useful if you want to # allow both system users (/etc/passwd) and virtual users to login without # duplicating the system users into virtual database. # # # # User database specifies where mails are located and what user/group IDs # own them. For single-UID configuration use "static" userdb. # # #!include auth-deny.conf.ext #!include auth-master.conf.ext !include auth-system.conf.ext #!include auth-sql.conf.ext #!include auth-ldap.conf.ext #!include auth-passwdfile.conf.ext #!include auth-checkpassword.conf.ext #!include auth-vpopmail.conf.ext #!include auth-static.conf.ext dovecot-2.2.33.2/doc/example-config/conf.d/auth-vpopmail.conf.ext0000644000175000017500000000051213123174404021370 00000000000000# Authentication for vpopmail users. Included from 10-auth.conf. # # passdb { driver = vpopmail # [cache_key=] [webmail=] args = } userdb { driver = vpopmail # [quota_template=